mirror of
https://github.com/ethereum/solidity
synced 2023-10-03 13:03:40 +00:00
Implement struct encoder.
This commit is contained in:
committed by
Alex Beregszaszi
parent
6385641f6e
commit
70d70e7816
@@ -404,9 +404,11 @@ string ABIFunctions::abiEncodingFunction(
|
||||
else
|
||||
solAssert(false, "");
|
||||
}
|
||||
else if (dynamic_cast<StructType const*>(&to))
|
||||
else if (auto const* toStruct = dynamic_cast<StructType const*>(&to))
|
||||
{
|
||||
solUnimplementedAssert(false, "Structs not yet implemented.");
|
||||
StructType const* fromStruct = dynamic_cast<StructType const*>(&_from);
|
||||
solAssert(fromStruct, "");
|
||||
return abiEncodingFunctionStruct(*fromStruct, *toStruct, _encodeAsLibraryTypes);
|
||||
}
|
||||
else if (_from.category() == Type::Category::Function)
|
||||
return abiEncodingFunctionFunctionType(
|
||||
@@ -534,7 +536,7 @@ string ABIFunctions::abiEncodingFunctionSimpleArray(
|
||||
for { let i := 0 } lt(i, length) { i := add(i, 1) }
|
||||
{
|
||||
mstore(pos, sub(tail, headStart))
|
||||
tail := <encodeToMemoryFun>(<arrayElementAccess>(srcPtr), tail)
|
||||
tail := <encodeToMemoryFun>(<arrayElementAccess>, tail)
|
||||
srcPtr := <nextArrayElement>(srcPtr)
|
||||
pos := add(pos, <elementEncodedSize>)
|
||||
}
|
||||
@@ -549,7 +551,7 @@ string ABIFunctions::abiEncodingFunctionSimpleArray(
|
||||
let srcPtr := <dataAreaFun>(value)
|
||||
for { let i := 0 } lt(i, length) { i := add(i, 1) }
|
||||
{
|
||||
<encodeToMemoryFun>(<arrayElementAccess>(srcPtr), pos)
|
||||
<encodeToMemoryFun>(<arrayElementAccess>, pos)
|
||||
srcPtr := <nextArrayElement>(srcPtr)
|
||||
pos := add(pos, <elementEncodedSize>)
|
||||
}
|
||||
@@ -573,7 +575,7 @@ string ABIFunctions::abiEncodingFunctionSimpleArray(
|
||||
_encodeAsLibraryTypes,
|
||||
true
|
||||
));
|
||||
templ("arrayElementAccess", inMemory ? "mload" : "sload");
|
||||
templ("arrayElementAccess", inMemory ? "mload(srcPtr)" : _from.baseType()->isValueType() ? "sload(srcPtr)" : "srcPtr" );
|
||||
templ("nextArrayElement", nextArrayElementFunction(_from));
|
||||
return templ.render();
|
||||
});
|
||||
@@ -726,6 +728,122 @@ string ABIFunctions::abiEncodingFunctionCompactStorageArray(
|
||||
});
|
||||
}
|
||||
|
||||
string ABIFunctions::abiEncodingFunctionStruct(
|
||||
StructType const& _from,
|
||||
StructType const& _to,
|
||||
bool _encodeAsLibraryTypes
|
||||
)
|
||||
{
|
||||
string functionName =
|
||||
"abi_encode_" +
|
||||
_from.identifier() +
|
||||
"_to_" +
|
||||
_to.identifier() +
|
||||
(_encodeAsLibraryTypes ? "_library" : "");
|
||||
|
||||
solUnimplementedAssert(!_from.dataStoredIn(DataLocation::CallData), "");
|
||||
solAssert(&_from.structDefinition() == &_to.structDefinition(), "");
|
||||
|
||||
return createFunction(functionName, [&]() {
|
||||
bool fromStorage = _from.location() == DataLocation::Storage;
|
||||
bool dynamic = _to.isDynamicallyEncoded();
|
||||
Whiskers templ(R"(
|
||||
function <functionName>(value, pos) <return> {
|
||||
let tail := add(pos, <headSize>)
|
||||
<init>
|
||||
<#members>
|
||||
{
|
||||
// <memberName>
|
||||
<encode>
|
||||
}
|
||||
</members>
|
||||
<assignEnd>
|
||||
}
|
||||
)");
|
||||
templ("functionName", functionName);
|
||||
templ("return", dynamic ? " -> end " : "");
|
||||
templ("assignEnd", dynamic ? "end := tail" : "");
|
||||
// to avoid multiple loads from the same slot for subsequent members
|
||||
templ("init", fromStorage ? "let slotValue := 0" : "");
|
||||
u256 previousSlotOffset(-1);
|
||||
u256 encodingOffset = 0;
|
||||
vector<map<string, string>> members;
|
||||
for (auto const& member: _to.members(nullptr))
|
||||
{
|
||||
solAssert(member.type, "");
|
||||
if (!member.type->canLiveOutsideStorage())
|
||||
continue;
|
||||
solUnimplementedAssert(
|
||||
member.type->mobileType() &&
|
||||
member.type->mobileType()->interfaceType(_encodeAsLibraryTypes) &&
|
||||
member.type->mobileType()->interfaceType(_encodeAsLibraryTypes)->encodingType(),
|
||||
"Encoding type \"" + member.type->toString() + "\" not yet implemented."
|
||||
);
|
||||
auto memberTypeTo = member.type->mobileType()->interfaceType(_encodeAsLibraryTypes)->encodingType();
|
||||
auto memberTypeFrom = _from.memberType(member.name);
|
||||
solAssert(memberTypeFrom, "");
|
||||
bool dynamicMember = memberTypeTo->isDynamicallyEncoded();
|
||||
if (dynamicMember)
|
||||
solAssert(dynamic, "");
|
||||
Whiskers memberTempl(R"(
|
||||
<preprocess>
|
||||
let memberValue := <retrieveValue>
|
||||
)" + (
|
||||
dynamicMember ?
|
||||
string(R"(
|
||||
mstore(add(pos, <encodingOffset>), sub(tail, pos))
|
||||
tail := <abiEncode>(memberValue, tail)
|
||||
)") :
|
||||
string(R"(
|
||||
<abiEncode>(memberValue, add(pos, <encodingOffset>))
|
||||
)")
|
||||
)
|
||||
);
|
||||
if (fromStorage)
|
||||
{
|
||||
solAssert(memberTypeFrom->isValueType() == memberTypeTo->isValueType(), "");
|
||||
u256 storageSlotOffset;
|
||||
size_t intraSlotOffset;
|
||||
tie(storageSlotOffset, intraSlotOffset) = _from.storageOffsetsOfMember(member.name);
|
||||
if (memberTypeFrom->isValueType())
|
||||
{
|
||||
if (storageSlotOffset != previousSlotOffset)
|
||||
{
|
||||
memberTempl("preprocess", "slotValue := sload(add(value, " + toCompactHexWithPrefix(storageSlotOffset) + "))");
|
||||
previousSlotOffset = storageSlotOffset;
|
||||
}
|
||||
else
|
||||
memberTempl("preprocess", "");
|
||||
memberTempl("retrieveValue", shiftRightFunction(intraSlotOffset * 8, false) + "(slotValue)");
|
||||
}
|
||||
else
|
||||
{
|
||||
solAssert(memberTypeFrom->dataStoredIn(DataLocation::Storage), "");
|
||||
solAssert(intraSlotOffset == 0, "");
|
||||
memberTempl("preprocess", "");
|
||||
memberTempl("retrieveValue", "add(value, " + toCompactHexWithPrefix(storageSlotOffset) + ")");
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
memberTempl("preprocess", "");
|
||||
string sourceOffset = toCompactHexWithPrefix(_from.memoryOffsetOfMember(member.name));
|
||||
memberTempl("retrieveValue", "mload(add(value, " + sourceOffset + "))");
|
||||
}
|
||||
memberTempl("encodingOffset", toCompactHexWithPrefix(encodingOffset));
|
||||
encodingOffset += dynamicMember ? 0x20 : memberTypeTo->calldataEncodedSize();
|
||||
memberTempl("abiEncode", abiEncodingFunction(*memberTypeFrom, *memberTypeTo, _encodeAsLibraryTypes, false));
|
||||
|
||||
members.push_back({});
|
||||
members.back()["encode"] = memberTempl.render();
|
||||
members.back()["memberName"] = member.name;
|
||||
}
|
||||
templ("members", members);
|
||||
templ("headSize", toCompactHexWithPrefix(encodingOffset));
|
||||
return templ.render();
|
||||
});
|
||||
}
|
||||
|
||||
string ABIFunctions::abiEncodingFunctionStringLiteral(
|
||||
Type const& _from,
|
||||
Type const& _to,
|
||||
|
||||
@@ -123,6 +123,13 @@ private:
|
||||
bool _encodeAsLibraryTypes
|
||||
);
|
||||
|
||||
/// Part of @a abiEncodingFunction for struct types.
|
||||
std::string abiEncodingFunctionStruct(
|
||||
StructType const& _givenType,
|
||||
StructType const& _targetType,
|
||||
bool _encodeAsLibraryTypes
|
||||
);
|
||||
|
||||
// @returns the name of the ABI encoding function with the given type
|
||||
// and queues the generation of the function to the requested functions.
|
||||
// Case for _givenType being a string literal
|
||||
|
||||
@@ -121,7 +121,7 @@ void CompilerUtils::storeInMemoryDynamic(Type const& _type, bool _padToWordBound
|
||||
{
|
||||
if (auto ref = dynamic_cast<ReferenceType const*>(&_type))
|
||||
{
|
||||
solAssert(ref->location() == DataLocation::Memory, "");
|
||||
solUnimplementedAssert(ref->location() == DataLocation::Memory, "");
|
||||
storeInMemoryDynamic(IntegerType(256), _padToWordBoundaries);
|
||||
}
|
||||
else if (auto str = dynamic_cast<StringLiteralType const*>(&_type))
|
||||
|
||||
Reference in New Issue
Block a user