mirror of
https://github.com/ethereum/solidity
synced 2023-10-03 13:03:40 +00:00
Implementing struct copying from calldata to storage
This commit is contained in:
parent
762e3f3cee
commit
23f6369a46
@ -2,7 +2,7 @@
|
||||
|
||||
Language Features:
|
||||
* Allow function definitions outside of contracts, behaving much like internal library functions.
|
||||
|
||||
* Code generator: Implementing copying structs from calldata to storage.
|
||||
|
||||
Compiler Features:
|
||||
* SMTChecker: Add underflow and overflow as verification conditions in the CHC engine.
|
||||
|
@ -357,38 +357,47 @@ void StorageItem::storeValue(Type const& _sourceType, SourceLocation const& _loc
|
||||
"Struct assignment with conversion."
|
||||
);
|
||||
solAssert(!structType.containsNestedMapping(), "");
|
||||
solAssert(sourceType.location() != DataLocation::CallData, "Structs in calldata not supported.");
|
||||
for (auto const& member: structType.members(nullptr))
|
||||
if (sourceType.location() == DataLocation::CallData)
|
||||
{
|
||||
// assign each member that can live outside of storage
|
||||
TypePointer const& memberType = member.type;
|
||||
solAssert(memberType->nameable(), "");
|
||||
TypePointer sourceMemberType = sourceType.memberType(member.name);
|
||||
if (sourceType.location() == DataLocation::Storage)
|
||||
solAssert(sourceType.sizeOnStack() == 1, "");
|
||||
solAssert(structType.sizeOnStack() == 1, "");
|
||||
m_context << Instruction::DUP2 << Instruction::DUP2;
|
||||
m_context.callYulFunction(m_context.utilFunctions().updateStorageValueFunction(structType, &sourceType, 0), 2, 0);
|
||||
}
|
||||
else
|
||||
{
|
||||
for (auto const& member: structType.members(nullptr))
|
||||
{
|
||||
// stack layout: source_ref target_ref
|
||||
pair<u256, unsigned> const& offsets = sourceType.storageOffsetsOfMember(member.name);
|
||||
m_context << offsets.first << Instruction::DUP3 << Instruction::ADD;
|
||||
// assign each member that can live outside of storage
|
||||
TypePointer const& memberType = member.type;
|
||||
solAssert(memberType->nameable(), "");
|
||||
TypePointer sourceMemberType = sourceType.memberType(member.name);
|
||||
if (sourceType.location() == DataLocation::Storage)
|
||||
{
|
||||
// stack layout: source_ref target_ref
|
||||
pair<u256, unsigned> const& offsets = sourceType.storageOffsetsOfMember(member.name);
|
||||
m_context << offsets.first << Instruction::DUP3 << Instruction::ADD;
|
||||
m_context << u256(offsets.second);
|
||||
// stack: source_ref target_ref source_member_ref source_member_off
|
||||
StorageItem(m_context, *sourceMemberType).retrieveValue(_location, true);
|
||||
// stack: source_ref target_ref source_value...
|
||||
}
|
||||
else
|
||||
{
|
||||
solAssert(sourceType.location() == DataLocation::Memory, "");
|
||||
// stack layout: source_ref target_ref
|
||||
m_context << sourceType.memoryOffsetOfMember(member.name);
|
||||
m_context << Instruction::DUP3 << Instruction::ADD;
|
||||
MemoryItem(m_context, *sourceMemberType).retrieveValue(_location, true);
|
||||
// stack layout: source_ref target_ref source_value...
|
||||
}
|
||||
unsigned stackSize = sourceMemberType->sizeOnStack();
|
||||
pair<u256, unsigned> const& offsets = structType.storageOffsetsOfMember(member.name);
|
||||
m_context << dupInstruction(1 + stackSize) << offsets.first << Instruction::ADD;
|
||||
m_context << u256(offsets.second);
|
||||
// stack: source_ref target_ref source_member_ref source_member_off
|
||||
StorageItem(m_context, *sourceMemberType).retrieveValue(_location, true);
|
||||
// stack: source_ref target_ref source_value...
|
||||
// stack: source_ref target_ref target_off source_value... target_member_ref target_member_byte_off
|
||||
StorageItem(m_context, *memberType).storeValue(*sourceMemberType, _location, true);
|
||||
}
|
||||
else
|
||||
{
|
||||
solAssert(sourceType.location() == DataLocation::Memory, "");
|
||||
// stack layout: source_ref target_ref
|
||||
m_context << sourceType.memoryOffsetOfMember(member.name);
|
||||
m_context << Instruction::DUP3 << Instruction::ADD;
|
||||
MemoryItem(m_context, *sourceMemberType).retrieveValue(_location, true);
|
||||
// stack layout: source_ref target_ref source_value...
|
||||
}
|
||||
unsigned stackSize = sourceMemberType->sizeOnStack();
|
||||
pair<u256, unsigned> const& offsets = structType.storageOffsetsOfMember(member.name);
|
||||
m_context << dupInstruction(1 + stackSize) << offsets.first << Instruction::ADD;
|
||||
m_context << u256(offsets.second);
|
||||
// stack: source_ref target_ref target_off source_value... target_member_ref target_member_byte_off
|
||||
StorageItem(m_context, *memberType).storeValue(*sourceMemberType, _location, true);
|
||||
}
|
||||
// stack layout: source_ref target_ref
|
||||
solAssert(sourceType.sizeOnStack() == 1, "Unexpected source size.");
|
||||
|
@ -1553,11 +1553,6 @@ string YulUtilFunctions::updateStorageValueFunction(
|
||||
|
||||
Whiskers templ(R"(
|
||||
function <functionName>(slot, value) {
|
||||
<?fromCalldata>
|
||||
let valueMem := <convertToMemory>(value)
|
||||
<!fromCalldata>
|
||||
let valueMem := value
|
||||
</fromCalldata>
|
||||
<#member>
|
||||
{
|
||||
<updateMemberCall>
|
||||
@ -1566,12 +1561,6 @@ string YulUtilFunctions::updateStorageValueFunction(
|
||||
}
|
||||
)");
|
||||
templ("functionName", functionName);
|
||||
templ("fromCalldata", fromStructType.location() == DataLocation::CallData);
|
||||
if (fromStructType.location() == DataLocation::CallData)
|
||||
templ("convertToMemory", conversionFunction(
|
||||
fromStructType,
|
||||
*TypeProvider::structType(toStructType.structDefinition(), DataLocation::Memory)
|
||||
));
|
||||
|
||||
MemberList::MemberMap toStructMembers = toStructType.nativeMembers(nullptr);
|
||||
MemberList::MemberMap fromStructMembers = fromStructType.nativeMembers(nullptr);
|
||||
@ -1581,9 +1570,10 @@ string YulUtilFunctions::updateStorageValueFunction(
|
||||
{
|
||||
solAssert(toStructMembers[i].type->memoryHeadSize() == 32, "");
|
||||
bool isStruct = toStructMembers[i].type->category() == Type::Category::Struct;
|
||||
bool fromCalldata = fromStructType.location() == DataLocation::CallData;
|
||||
auto const& [slotDiff, offset] = toStructType.storageOffsetsOfMember(toStructMembers[i].name);
|
||||
memberParams[i]["updateMemberCall"] = Whiskers(R"(
|
||||
let memberValue := <loadFromMemory>(add(valueMem, <memberMemoryOffset>))
|
||||
let memberValue := <loadFromMemoryOrCalldata>(add(value, <memberOffset>))
|
||||
<updateMember>(add(slot, <memberStorageSlotDiff>), <?hasOffset><memberStorageOffset>,</hasOffset> memberValue)
|
||||
)")
|
||||
("hasOffset", !isStruct)
|
||||
@ -1595,8 +1585,12 @@ string YulUtilFunctions::updateStorageValueFunction(
|
||||
)
|
||||
("memberStorageSlotDiff", slotDiff.str())
|
||||
("memberStorageOffset", to_string(offset))
|
||||
("memberMemoryOffset", fromStructType.memoryOffsetOfMember(fromStructMembers[i].name).str())
|
||||
("loadFromMemory", readFromMemory(*fromStructMembers[i].type))
|
||||
("memberOffset",
|
||||
fromCalldata ?
|
||||
to_string(fromStructType.calldataOffsetOfMember(fromStructMembers[i].name)) :
|
||||
fromStructType.memoryOffsetOfMember(fromStructMembers[i].name).str()
|
||||
)
|
||||
("loadFromMemoryOrCalldata", readFromMemoryOrCalldata(*fromStructMembers[i].type, fromCalldata))
|
||||
.render();
|
||||
}
|
||||
templ("member", memberParams);
|
||||
|
@ -3,10 +3,11 @@ pragma experimental ABIEncoderV2;
|
||||
contract C {
|
||||
struct S {
|
||||
uint256 a;
|
||||
uint256 b;
|
||||
uint64 b;
|
||||
bytes2 c;
|
||||
}
|
||||
|
||||
uint[153] r;
|
||||
S s;
|
||||
|
||||
function f(uint32 a, S calldata c, uint256 b) external returns (uint256, uint256, byte) {
|
||||
@ -16,6 +17,6 @@ contract C {
|
||||
}
|
||||
|
||||
// ====
|
||||
// compileViaYul: true
|
||||
// compileViaYul: also
|
||||
// ----
|
||||
// f(uint32, (uint256, uint256, bytes2), uint256): 1, 42, 23, "ab", 1 -> 42, 23, "b"
|
||||
// f(uint32, (uint256, uint64, bytes2), uint256): 1, 42, 23, "ab", 1 -> 42, 23, "b"
|
Loading…
Reference in New Issue
Block a user