From 23f6369a46dce987f856fcaa58683ea6c4f7d9cd Mon Sep 17 00:00:00 2001 From: Djordje Mijovic Date: Mon, 17 Aug 2020 14:47:54 +0200 Subject: [PATCH] Implementing struct copying from calldata to storage --- Changelog.md | 2 +- libsolidity/codegen/LValue.cpp | 65 +++++++++++-------- libsolidity/codegen/YulUtilFunctions.cpp | 22 +++---- .../calldata/calldata_struct_to_storage.sol | 7 +- 4 files changed, 50 insertions(+), 46 deletions(-) diff --git a/Changelog.md b/Changelog.md index 9c54eabe5..8b93fcfc1 100644 --- a/Changelog.md +++ b/Changelog.md @@ -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. diff --git a/libsolidity/codegen/LValue.cpp b/libsolidity/codegen/LValue.cpp index f4fdd0cbb..40555b658 100644 --- a/libsolidity/codegen/LValue.cpp +++ b/libsolidity/codegen/LValue.cpp @@ -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 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 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 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 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."); diff --git a/libsolidity/codegen/YulUtilFunctions.cpp b/libsolidity/codegen/YulUtilFunctions.cpp index 459620909..ad801d9f0 100644 --- a/libsolidity/codegen/YulUtilFunctions.cpp +++ b/libsolidity/codegen/YulUtilFunctions.cpp @@ -1553,11 +1553,6 @@ string YulUtilFunctions::updateStorageValueFunction( Whiskers templ(R"( function (slot, value) { - - let valueMem := (value) - - let valueMem := value - <#member> { @@ -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 := (add(valueMem, )) + let memberValue := (add(value, )) (add(slot, ), , 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); diff --git a/test/libsolidity/semanticTests/structs/calldata/calldata_struct_to_storage.sol b/test/libsolidity/semanticTests/structs/calldata/calldata_struct_to_storage.sol index 9a443d97d..b97ff80c4 100644 --- a/test/libsolidity/semanticTests/structs/calldata/calldata_struct_to_storage.sol +++ b/test/libsolidity/semanticTests/structs/calldata/calldata_struct_to_storage.sol @@ -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" \ No newline at end of file +// f(uint32, (uint256, uint64, bytes2), uint256): 1, 42, 23, "ab", 1 -> 42, 23, "b" \ No newline at end of file