From ab8dfed144ae9233351214e01bab032f5b1db012 Mon Sep 17 00:00:00 2001 From: Daniel Kirchner Date: Tue, 2 Apr 2019 13:29:55 +0200 Subject: [PATCH] Implement copying recursive structs from storage to memory. --- Changelog.md | 1 + libsolidity/codegen/CompilerUtils.cpp | 53 ++++++++++++------- .../conversion/recursive_storage_memory.sol | 20 +++++++ .../recursive_storage_memory_complex.sol | 46 ++++++++++++++++ 4 files changed, 102 insertions(+), 18 deletions(-) create mode 100644 test/libsolidity/semanticTests/structs/conversion/recursive_storage_memory.sol create mode 100644 test/libsolidity/semanticTests/structs/conversion/recursive_storage_memory_complex.sol diff --git a/Changelog.md b/Changelog.md index 64115b520..e11faaf4d 100644 --- a/Changelog.md +++ b/Changelog.md @@ -1,6 +1,7 @@ ### 0.5.8 (unreleased) Language Features: + * Code Generation: Implement copying recursive structs from storage to memory. Compiler Features: diff --git a/libsolidity/codegen/CompilerUtils.cpp b/libsolidity/codegen/CompilerUtils.cpp index 8c1d1a6bc..f7d3b5d54 100644 --- a/libsolidity/codegen/CompilerUtils.cpp +++ b/libsolidity/codegen/CompilerUtils.cpp @@ -987,25 +987,42 @@ void CompilerUtils::convertType( switch (typeOnStack.location()) { case DataLocation::Storage: - // stack: - allocateMemory(typeOnStack.memorySize()); - m_context << Instruction::SWAP1 << Instruction::DUP2; - // stack: - for (auto const& member: typeOnStack.members(nullptr)) - { - if (!member.type->canLiveOutsideStorage()) - continue; - pair const& offsets = typeOnStack.storageOffsetsOfMember(member.name); - m_context << offsets.first << Instruction::DUP3 << Instruction::ADD; - m_context << u256(offsets.second); - StorageItem(m_context, *member.type).retrieveValue(SourceLocation(), true); - TypePointer targetMemberType = targetType.memberType(member.name); - solAssert(!!targetMemberType, "Member not found in target type."); - convertType(*member.type, *targetMemberType, true); - storeInMemoryDynamic(*targetMemberType, true); - } - m_context << Instruction::POP << Instruction::POP; + { + auto conversionImpl = [ + typeOnStack = dynamic_pointer_cast(_typeOnStack.shared_from_this()), + targetType = dynamic_pointer_cast(targetType.shared_from_this()) + ](CompilerContext& _context) { + CompilerUtils utils(_context); + // stack: + utils.allocateMemory(typeOnStack->memorySize()); + _context << Instruction::SWAP1 << Instruction::DUP2; + // stack: + for (auto const& member: typeOnStack->members(nullptr)) + { + if (!member.type->canLiveOutsideStorage()) + continue; + pair const& offsets = typeOnStack->storageOffsetsOfMember(member.name); + _context << offsets.first << Instruction::DUP3 << Instruction::ADD; + _context << u256(offsets.second); + StorageItem(_context, *member.type).retrieveValue(SourceLocation(), true); + TypePointer targetMemberType = targetType->memberType(member.name); + solAssert(!!targetMemberType, "Member not found in target type."); + utils.convertType(*member.type, *targetMemberType, true); + utils.storeInMemoryDynamic(*targetMemberType, true); + } + _context << Instruction::POP << Instruction::POP; + }; + if (typeOnStack.recursive()) + m_context.callLowLevelFunction( + "$convertRecursiveArrayStorageToMemory_" + typeOnStack.identifier() + "_to_" + targetType.identifier(), + 1, + 1, + conversionImpl + ); + else + conversionImpl(m_context); break; + } case DataLocation::CallData: { solUnimplementedAssert(!typeOnStack.isDynamicallyEncoded(), ""); diff --git a/test/libsolidity/semanticTests/structs/conversion/recursive_storage_memory.sol b/test/libsolidity/semanticTests/structs/conversion/recursive_storage_memory.sol new file mode 100644 index 000000000..e941c9432 --- /dev/null +++ b/test/libsolidity/semanticTests/structs/conversion/recursive_storage_memory.sol @@ -0,0 +1,20 @@ +contract CopyTest { + struct Tree { + Tree[] children; + } + Tree storageTree; + + constructor() public { + storageTree.children.length = 2; + storageTree.children[0].children.length = 23; + storageTree.children[1].children.length = 42; + } + + function run() public returns (uint256, uint256, uint256) { + Tree memory memoryTree; + memoryTree = storageTree; + return (memoryTree.children.length, memoryTree.children[0].children.length, memoryTree.children[1].children.length); + } +} +// ---- +// run() -> 2, 23, 42 diff --git a/test/libsolidity/semanticTests/structs/conversion/recursive_storage_memory_complex.sol b/test/libsolidity/semanticTests/structs/conversion/recursive_storage_memory_complex.sol new file mode 100644 index 000000000..44a9fb51b --- /dev/null +++ b/test/libsolidity/semanticTests/structs/conversion/recursive_storage_memory_complex.sol @@ -0,0 +1,46 @@ +contract CopyTest { + struct Tree { + uint256 data; + Tree[] children; + } + Tree storageTree; + + constructor() public { + storageTree.data = 0x42; + storageTree.children.length = 2; + storageTree.children[0].data = 0x4200; + storageTree.children[1].data = 0x4201; + storageTree.children[0].children.length = 3; + for (uint i = 0; i < 3; i++) + storageTree.children[0].children[i].data = 0x420000 + i; + storageTree.children[1].children.length = 4; + for (uint i = 0; i < 4; i++) + storageTree.children[1].children[i].data = 0x420100 + i; + } + + function countData(Tree memory tree) internal returns (uint256 c) { + c = 1; + for (uint i = 0; i < tree.children.length; i++) { + c += countData(tree.children[i]); + } + } + + function copyFromTree(Tree memory tree, uint256[] memory data, uint256 offset) internal returns (uint256) { + data[offset++] = tree.data; + for (uint i = 0; i < tree.children.length; i++) { + offset = copyFromTree(tree.children[i], data, offset); + } + return offset; + } + + function run() public returns (uint256[] memory) { + Tree memory memoryTree; + memoryTree = storageTree; + uint256 length = countData(memoryTree); + uint256[] memory result = new uint256[](length); + copyFromTree(memoryTree, result, 0); + return result; + } +} +// ---- +// run() -> 0x20, 10, 0x42, 0x4200, 0x420000, 0x420001, 0x420002, 0x4201, 0x420100, 0x420101, 0x420102, 0x420103