mirror of
https://github.com/ethereum/solidity
synced 2023-10-03 13:03:40 +00:00
Support copying of nested calldata arrays to memory.
This commit is contained in:
parent
101260943a
commit
565d0cd4eb
@ -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:
|
||||||
|
@ -958,65 +958,77 @@ 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)
|
||||||
{
|
{
|
||||||
// stack: <source ref> (variably sized)
|
if (
|
||||||
unsigned stackSize = typeOnStack.sizeOnStack();
|
typeOnStack.dataStoredIn(DataLocation::CallData) &&
|
||||||
ArrayUtils(m_context).retrieveLength(typeOnStack);
|
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;
|
||||||
|
|
||||||
// allocate memory
|
m_context.callYulFunction(
|
||||||
// stack: <source ref> (variably sized) <length>
|
m_context.utilFunctions().conversionFunction(typeOnStack, targetType),
|
||||||
m_context << Instruction::DUP1;
|
typeOnStack.isDynamicallySized() ? 2 : 1,
|
||||||
ArrayUtils(m_context).convertLengthToSize(targetType, true);
|
1
|
||||||
// stack: <source ref> (variably sized) <length> <size>
|
);
|
||||||
if (targetType.isDynamicallySized())
|
|
||||||
m_context << u256(0x20) << Instruction::ADD;
|
|
||||||
allocateMemory();
|
|
||||||
// stack: <source ref> (variably sized) <length> <mem start>
|
|
||||||
m_context << Instruction::DUP1;
|
|
||||||
moveIntoStack(2 + stackSize);
|
|
||||||
if (targetType.isDynamicallySized())
|
|
||||||
{
|
|
||||||
m_context << Instruction::DUP2;
|
|
||||||
storeInMemoryDynamic(*TypeProvider::uint256());
|
|
||||||
}
|
|
||||||
// stack: <mem start> <source ref> (variably sized) <length> <mem data pos>
|
|
||||||
if (targetType.baseType()->isValueType())
|
|
||||||
{
|
|
||||||
copyToStackTop(2 + stackSize, stackSize);
|
|
||||||
ArrayUtils(m_context).copyArrayToMemory(typeOnStack);
|
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
if (auto baseType = dynamic_cast<ArrayType const*>(typeOnStack.baseType()))
|
// stack: <source ref> (variably sized)
|
||||||
solUnimplementedAssert(
|
unsigned stackSize = typeOnStack.sizeOnStack();
|
||||||
typeOnStack.location() != DataLocation::CallData ||
|
ArrayUtils(m_context).retrieveLength(typeOnStack);
|
||||||
!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;
|
// allocate memory
|
||||||
// stack: <mem start> <source ref> (variably sized) <length> <counter> <mem data pos>
|
// stack: <source ref> (variably sized) <length>
|
||||||
auto repeat = m_context.newTag();
|
m_context << Instruction::DUP1;
|
||||||
m_context << repeat;
|
ArrayUtils(m_context).convertLengthToSize(targetType, true);
|
||||||
m_context << Instruction::DUP3 << Instruction::DUP3;
|
// stack: <source ref> (variably sized) <length> <size>
|
||||||
m_context << Instruction::LT << Instruction::ISZERO;
|
if (targetType.isDynamicallySized())
|
||||||
auto loopEnd = m_context.appendConditionalJump();
|
m_context << u256(0x20) << Instruction::ADD;
|
||||||
copyToStackTop(3 + stackSize, stackSize);
|
allocateMemory();
|
||||||
copyToStackTop(2 + stackSize, 1);
|
// stack: <source ref> (variably sized) <length> <mem start>
|
||||||
ArrayUtils(m_context).accessIndex(typeOnStack, false);
|
m_context << Instruction::DUP1;
|
||||||
if (typeOnStack.location() == DataLocation::Storage)
|
moveIntoStack(2 + stackSize);
|
||||||
StorageItem(m_context, *typeOnStack.baseType()).retrieveValue(SourceLocation(), true);
|
if (targetType.isDynamicallySized())
|
||||||
convertType(*typeOnStack.baseType(), *targetType.baseType(), _cleanupNeeded);
|
{
|
||||||
storeInMemoryDynamic(*targetType.baseType(), true);
|
m_context << Instruction::DUP2;
|
||||||
m_context << Instruction::SWAP1 << u256(1) << Instruction::ADD;
|
storeInMemoryDynamic(*TypeProvider::uint256());
|
||||||
m_context << Instruction::SWAP1;
|
}
|
||||||
m_context.appendJumpTo(repeat);
|
// stack: <mem start> <source ref> (variably sized) <length> <mem data pos>
|
||||||
m_context << loopEnd;
|
if (targetType.baseType()->isValueType())
|
||||||
m_context << Instruction::POP;
|
{
|
||||||
|
copyToStackTop(2 + stackSize, stackSize);
|
||||||
|
ArrayUtils(m_context).copyArrayToMemory(typeOnStack);
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
m_context << u256(0) << Instruction::SWAP1;
|
||||||
|
// stack: <mem start> <source ref> (variably sized) <length> <counter> <mem data pos>
|
||||||
|
auto repeat = m_context.newTag();
|
||||||
|
m_context << repeat;
|
||||||
|
m_context << Instruction::DUP3 << Instruction::DUP3;
|
||||||
|
m_context << Instruction::LT << Instruction::ISZERO;
|
||||||
|
auto loopEnd = m_context.appendConditionalJump();
|
||||||
|
copyToStackTop(3 + stackSize, stackSize);
|
||||||
|
copyToStackTop(2 + stackSize, 1);
|
||||||
|
ArrayUtils(m_context).accessIndex(typeOnStack, false);
|
||||||
|
if (typeOnStack.location() == DataLocation::Storage)
|
||||||
|
StorageItem(m_context, *typeOnStack.baseType()).retrieveValue(SourceLocation(), true);
|
||||||
|
convertType(*typeOnStack.baseType(), *targetType.baseType(), _cleanupNeeded);
|
||||||
|
storeInMemoryDynamic(*targetType.baseType(), true);
|
||||||
|
m_context << Instruction::SWAP1 << u256(1) << Instruction::ADD;
|
||||||
|
m_context << Instruction::SWAP1;
|
||||||
|
m_context.appendJumpTo(repeat);
|
||||||
|
m_context << loopEnd;
|
||||||
|
m_context << Instruction::POP;
|
||||||
|
}
|
||||||
|
// stack: <mem start> <source ref> (variably sized) <length> <mem data pos updated>
|
||||||
|
popStackSlots(2 + stackSize);
|
||||||
|
// Stack: <mem start>
|
||||||
}
|
}
|
||||||
// stack: <mem start> <source ref> (variably sized) <length> <mem data pos updated>
|
|
||||||
popStackSlots(2 + stackSize);
|
|
||||||
// Stack: <mem start>
|
|
||||||
}
|
}
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
@ -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
|
||||||
|
@ -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
|
@ -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"
|
@ -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
|
@ -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
|
@ -10,4 +10,3 @@ contract Test {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// ----
|
// ----
|
||||||
// UnimplementedFeatureError: Copying nested dynamic calldata arrays to memory is not implemented in the old code generator.
|
|
||||||
|
@ -10,4 +10,3 @@ contract Test {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// ----
|
// ----
|
||||||
// UnimplementedFeatureError: Copying nested dynamic calldata arrays to memory is not implemented in the old code generator.
|
|
||||||
|
@ -10,4 +10,3 @@ contract Test {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// ----
|
// ----
|
||||||
// UnimplementedFeatureError: Copying nested dynamic calldata arrays to memory is not implemented in the old code generator.
|
|
||||||
|
Loading…
Reference in New Issue
Block a user