[Sol->Yul] Implementing struct copying from storage to memory

This commit is contained in:
Djordje Mijovic 2020-07-30 11:33:34 +02:00
parent b89c863e11
commit 381784dd89
4 changed files with 94 additions and 6 deletions

View File

@ -1413,6 +1413,49 @@ string YulUtilFunctions::readFromStorage(Type const& _type, size_t _offset, bool
to_string(_offset) +
"_" +
_type.identifier();
if (_type.category() == Type::Category::Struct)
{
solAssert(_offset == 0, "");
auto const& structType = dynamic_cast<StructType const&>(_type);
solUnimplementedAssert(structType.location() == DataLocation::Memory, "");
MemberList::MemberMap structMembers = structType.nativeMembers(nullptr);
vector<map<string, string>> memberSetValues(structMembers.size());
for (size_t i = 0; i < structMembers.size(); ++i)
{
auto const& [memberSlotDiff, memberStorageOffset] = structType.storageOffsetsOfMember(structMembers[i].name);
bool isStruct = structMembers[i].type->category() == Type::Category::Struct;
memberSetValues[i]["setMember"] = Whiskers(R"(
mstore(add(value, <memberMemoryOffset>), <readFromStorage>(add(slot, <memberSlotDiff>)<?notStruct>, <memberStorageOffset></notStruct>))
)")
("memberMemoryOffset", structType.memoryOffsetOfMember(structMembers[i].name).str())
("memberSlotDiff", memberSlotDiff.str())
("memberStorageOffset", to_string(memberStorageOffset))
("readFromStorage",
isStruct ?
readFromStorage(*structMembers[i].type, memberStorageOffset, true) :
readFromStorageDynamic(*structMembers[i].type, true)
)
("notStruct", !isStruct)
.render();
}
return m_functionCollector.createFunction(functionName, [&] {
return Whiskers(R"(
function <functionName>(slot) -> value {
value := <allocStruct>()
<#member>
<setMember>
</member>
}
)")
("functionName", functionName)
("allocStruct", allocateMemoryStructFunction(structType))
("member", memberSetValues)
.render();
});
}
return m_functionCollector.createFunction(functionName, [&] {
solAssert(_type.sizeOnStack() == 1, "");
return Whiskers(R"(
@ -1430,6 +1473,7 @@ string YulUtilFunctions::readFromStorageDynamic(Type const& _type, bool _splitFu
{
if (_type.category() == Type::Category::Function)
solUnimplementedAssert(!_splitFunctionTypes, "");
string functionName =
"read_from_storage_dynamic" +
string(_splitFunctionTypes ? "split_" : "") +
@ -2107,13 +2151,27 @@ string YulUtilFunctions::conversionFunction(Type const& _from, Type const& _to)
solUnimplementedAssert(!fromStructType.isDynamicallyEncoded(), "");
solUnimplementedAssert(toStructType.location() == DataLocation::Memory, "");
solUnimplementedAssert(fromStructType.location() == DataLocation::CallData, "");
solUnimplementedAssert(fromStructType.location() != DataLocation::Memory, "");
if (fromStructType.location() == DataLocation::CallData)
{
body = Whiskers(R"(
converted := <abiDecode>(value, calldatasize())
)")("abiDecode", ABIFunctions(m_evmVersion, m_revertStrings, m_functionCollector).tupleDecoder(
{&toStructType}
)).render();
}
else
{
solAssert(fromStructType.location() == DataLocation::Storage, "");
body = Whiskers(R"(
converted := <readFromStorage>(value)
)")
("readFromStorage", readFromStorage(toStructType, 0, true))
.render();
}
body = Whiskers(R"(
converted := <abiDecode>(value, calldatasize())
)")("abiDecode", ABIFunctions(m_evmVersion, m_revertStrings, m_functionCollector).tupleDecoder(
{&toStructType}
)).render();
break;
}
case Type::Category::FixedBytes:

View File

@ -222,6 +222,7 @@ public:
std::string mappingIndexAccessFunction(MappingType const& _mappingType, Type const& _keyType);
/// @returns a function that reads a value type from storage.
/// Will allocate memory if return type is struct with location set to memory
/// Performs bit mask/sign extend cleanup and appropriate left / right shift, but not validation.
/// @param _splitFunctionTypes if false, returns the address and function signature in a
/// single variable.

View File

@ -3,6 +3,7 @@ contract c {
uint256 a;
uint256 b;
}
uint[75] r;
Struct data1;
Struct data2;
@ -15,5 +16,7 @@ contract c {
}
}
// ====
// compileViaYul: also
// ----
// test() -> true

View File

@ -0,0 +1,26 @@
pragma experimental ABIEncoderV2;
contract C {
struct S {
uint32 a;
uint128 b;
uint256 c;
}
struct X {
uint32 a;
S s;
}
uint[79] arr;
X x = X(12, S(42, 23, 34));
function f() external returns (uint32, uint128, uint256) {
X memory m = x;
return (m.s.a, m.s.b, m.s.c);
}
}
// ====
// compileViaYul: also
// ----
// f() -> 42, 23, 34