Refactoring readFromStorage util frunctions

Co-authored-by: Daniel Kirchner <daniel@ekpyron.org>
This commit is contained in:
Djordje Mijovic 2020-08-19 13:13:05 +02:00
parent 23f6369a46
commit 15163b2270
2 changed files with 103 additions and 71 deletions

View File

@ -1404,58 +1404,35 @@ string YulUtilFunctions::mappingIndexAccessFunction(MappingType const& _mappingT
string YulUtilFunctions::readFromStorage(Type const& _type, size_t _offset, bool _splitFunctionTypes) string YulUtilFunctions::readFromStorage(Type const& _type, size_t _offset, bool _splitFunctionTypes)
{ {
if (_type.isValueType())
return readFromStorageValueType(_type, _offset, _splitFunctionTypes);
else
{
solAssert(_offset == 0, "");
return readFromStorageReferenceType(_type);
}
}
string YulUtilFunctions::readFromStorageDynamic(Type const& _type, bool _splitFunctionTypes)
{
solAssert(_type.isValueType(), "");
return readFromStorageValueTypeDynamic(_type, _splitFunctionTypes);
}
string YulUtilFunctions::readFromStorageValueType(Type const& _type, size_t _offset, bool _splitFunctionTypes)
{
solAssert(_type.isValueType(), "");
if (_type.category() == Type::Category::Function) if (_type.category() == Type::Category::Function)
solUnimplementedAssert(!_splitFunctionTypes, ""); solUnimplementedAssert(!_splitFunctionTypes, "");
string functionName = string functionName =
"read_from_storage_" + "read_from_storage_" +
string(_splitFunctionTypes ? "split_" : "") + string(_splitFunctionTypes ? "split_" : "") +
"offset_" + "offset_" +
to_string(_offset) + to_string(_offset) +
"_" + "_" +
_type.identifier(); _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>)<?hasOffset>, <memberStorageOffset></hasOffset>))
)")
("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)
)
("hasOffset", !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, [&] { return m_functionCollector.createFunction(functionName, [&] {
solAssert(_type.sizeOnStack() == 1, ""); solAssert(_type.sizeOnStack() == 1, "");
return Whiskers(R"( return Whiskers(R"(
@ -1463,19 +1440,19 @@ string YulUtilFunctions::readFromStorage(Type const& _type, size_t _offset, bool
value := <extract>(sload(slot)) value := <extract>(sload(slot))
} }
)") )")
("functionName", functionName) ("functionName", functionName)
("extract", extractFromStorageValue(_type, _offset, false)) ("extract", extractFromStorageValue(_type, _offset, false))
.render(); .render();
}); });
} }
string YulUtilFunctions::readFromStorageValueTypeDynamic(Type const& _type, bool _splitFunctionTypes)
string YulUtilFunctions::readFromStorageDynamic(Type const& _type, bool _splitFunctionTypes)
{ {
solAssert(_type.isValueType(), "");
if (_type.category() == Type::Category::Function) if (_type.category() == Type::Category::Function)
solUnimplementedAssert(!_splitFunctionTypes, ""); solUnimplementedAssert(!_splitFunctionTypes, "");
string functionName = string functionName =
"read_from_storage_dynamic" + "read_from_storage_value_type_dynamic" +
string(_splitFunctionTypes ? "split_" : "") + string(_splitFunctionTypes ? "split_" : "") +
"_" + "_" +
_type.identifier(); _type.identifier();
@ -1491,6 +1468,55 @@ string YulUtilFunctions::readFromStorageDynamic(Type const& _type, bool _splitFu
.render(); .render();
}); });
} }
string YulUtilFunctions::readFromStorageReferenceType(Type const& _type)
{
solUnimplementedAssert(_type.category() == Type::Category::Struct, "");
string functionName = "read_from_storage_reference_type_" + _type.identifier();
auto const& structType = dynamic_cast<StructType const&>(_type);
solAssert(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);
memberSetValues[i]["setMember"] = Whiskers(R"(
{
let <memberValues> := <readFromStorage>(add(slot, <memberSlotDiff>)<?hasOffset>, <memberStorageOffset></hasOffset>)
<writeToMemory>(add(value, <memberMemoryOffset>), <memberValues>)
}
)")
("memberValues", suffixedVariableNameList("memberValue_", 0, structMembers[i].type->stackItems().size()))
("memberMemoryOffset", structType.memoryOffsetOfMember(structMembers[i].name).str())
("memberSlotDiff", memberSlotDiff.str())
("memberStorageOffset", to_string(memberStorageOffset))
("readFromStorage",
structMembers[i].type->isValueType() ?
readFromStorageDynamic(*structMembers[i].type, true) :
readFromStorage(*structMembers[i].type, memberStorageOffset, true)
)
("writeToMemory", writeToMemoryFunction(*structMembers[i].type))
("hasOffset", structMembers[i].type->isValueType())
.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();
});
}
string YulUtilFunctions::readFromMemory(Type const& _type) string YulUtilFunctions::readFromMemory(Type const& _type)
{ {
@ -1562,35 +1588,33 @@ string YulUtilFunctions::updateStorageValueFunction(
)"); )");
templ("functionName", functionName); templ("functionName", functionName);
MemberList::MemberMap toStructMembers = toStructType.nativeMembers(nullptr); MemberList::MemberMap structMembers = fromStructType.nativeMembers(nullptr);
MemberList::MemberMap fromStructMembers = fromStructType.nativeMembers(nullptr);
vector<map<string, string>> memberParams(toStructMembers.size()); vector<map<string, string>> memberParams(structMembers.size());
for (size_t i = 0; i < toStructMembers.size(); ++i) for (size_t i = 0; i < structMembers.size(); ++i)
{ {
solAssert(toStructMembers[i].type->memoryHeadSize() == 32, ""); solAssert(structMembers[i].type->memoryHeadSize() == 32, "");
bool isStruct = toStructMembers[i].type->category() == Type::Category::Struct;
bool fromCalldata = fromStructType.location() == DataLocation::CallData; bool fromCalldata = fromStructType.location() == DataLocation::CallData;
auto const& [slotDiff, offset] = toStructType.storageOffsetsOfMember(toStructMembers[i].name); auto const& [slotDiff, offset] = toStructType.storageOffsetsOfMember(structMembers[i].name);
memberParams[i]["updateMemberCall"] = Whiskers(R"( memberParams[i]["updateMemberCall"] = Whiskers(R"(
let memberValue := <loadFromMemoryOrCalldata>(add(value, <memberOffset>)) let memberValue := <loadFromMemoryOrCalldata>(add(value, <memberOffset>))
<updateMember>(add(slot, <memberStorageSlotDiff>), <?hasOffset><memberStorageOffset>,</hasOffset> memberValue) <updateMember>(add(slot, <memberStorageSlotDiff>), <?hasOffset><memberStorageOffset>,</hasOffset> memberValue)
)") )")
("hasOffset", !isStruct) ("hasOffset", structMembers[i].type->isValueType())
( (
"updateMember", "updateMember",
isStruct ? structMembers[i].type->isValueType() ?
updateStorageValueFunction(*toStructMembers[i].type, fromStructMembers[i].type, offset) : updateStorageValueFunction(*structMembers[i].type, structMembers[i].type) :
updateStorageValueFunction(*toStructMembers[i].type, fromStructMembers[i].type) updateStorageValueFunction(*structMembers[i].type, structMembers[i].type, offset)
) )
("memberStorageSlotDiff", slotDiff.str()) ("memberStorageSlotDiff", slotDiff.str())
("memberStorageOffset", to_string(offset)) ("memberStorageOffset", to_string(offset))
("memberOffset", ("memberOffset",
fromCalldata ? fromCalldata ?
to_string(fromStructType.calldataOffsetOfMember(fromStructMembers[i].name)) : to_string(fromStructType.calldataOffsetOfMember(structMembers[i].name)) :
fromStructType.memoryOffsetOfMember(fromStructMembers[i].name).str() fromStructType.memoryOffsetOfMember(structMembers[i].name).str()
) )
("loadFromMemoryOrCalldata", readFromMemoryOrCalldata(*fromStructMembers[i].type, fromCalldata)) ("loadFromMemoryOrCalldata", readFromMemoryOrCalldata(*structMembers[i].type, fromCalldata))
.render(); .render();
} }
templ("member", memberParams); templ("member", memberParams);

View File

@ -221,9 +221,7 @@ public:
/// @param _keyType the type of the value provided /// @param _keyType the type of the value provided
std::string mappingIndexAccessFunction(MappingType const& _mappingType, Type const& _keyType); std::string mappingIndexAccessFunction(MappingType const& _mappingType, Type const& _keyType);
/// @returns a function that reads a value type from storage. /// @returns a function that reads a 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 /// @param _splitFunctionTypes if false, returns the address and function signature in a
/// single variable. /// single variable.
std::string readFromStorage(Type const& _type, size_t _offset, bool _splitFunctionTypes); std::string readFromStorage(Type const& _type, size_t _offset, bool _splitFunctionTypes);
@ -406,6 +404,16 @@ private:
std::string readFromMemoryOrCalldata(Type const& _type, bool _fromCalldata); std::string readFromMemoryOrCalldata(Type const& _type, bool _fromCalldata);
/// @returns a function that reads a value type from storage.
/// 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.
std::string readFromStorageValueType(Type const& _type, size_t _offset, bool _splitFunctionTypes);
std::string readFromStorageValueTypeDynamic(Type const& _type, bool _splitFunctionTypes);
/// @returns a function that reads a reference type from storage to memory (performing a deep copy).
std::string readFromStorageReferenceType(Type const& _type);
langutil::EVMVersion m_evmVersion; langutil::EVMVersion m_evmVersion;
RevertStrings m_revertStrings; RevertStrings m_revertStrings;
MultiUseYulFunctionCollector& m_functionCollector; MultiUseYulFunctionCollector& m_functionCollector;