Refactor abi encoding functions to prepare implementing calldata arrays and structs.

This commit is contained in:
Daniel Kirchner 2019-03-15 18:26:17 +01:00
parent b0cb330397
commit d82157d46a
3 changed files with 120 additions and 94 deletions

View File

@ -1106,11 +1106,9 @@ string ABIFunctions::abiEncodingFunctionStruct(
_to.identifier() +
_options.toFunctionNameSuffix();
solUnimplementedAssert(!_from.dataStoredIn(DataLocation::CallData), "Encoding struct from calldata is not yet supported.");
solAssert(&_from.structDefinition() == &_to.structDefinition(), "");
return createFunction(functionName, [&]() {
bool fromStorage = _from.location() == DataLocation::Storage;
bool dynamic = _to.isDynamicallyEncoded();
Whiskers templ(R"(
// <readableTypeNameFrom> -> <readableTypeNameTo>
@ -1121,7 +1119,7 @@ string ABIFunctions::abiEncodingFunctionStruct(
{
// <memberName>
<preprocess>
let memberValue := <retrieveValue>
let <memberValues> := <retrieveValue>
<encode>
}
</members>
@ -1139,7 +1137,7 @@ string ABIFunctions::abiEncodingFunctionStruct(
else
templ("assignEnd", "");
// to avoid multiple loads from the same slot for subsequent members
templ("init", fromStorage ? "let slotValue := 0" : "");
templ("init", _from.dataStoredIn(DataLocation::Storage) ? "let slotValue := 0" : "");
u256 previousSlotOffset(-1);
u256 encodingOffset = 0;
vector<map<string, string>> members;
@ -1159,7 +1157,9 @@ string ABIFunctions::abiEncodingFunctionStruct(
members.push_back({});
members.back()["preprocess"] = "";
if (fromStorage)
switch (_from.location())
{
case DataLocation::Storage:
{
solAssert(memberTypeFrom->isValueType() == memberTypeTo->isValueType(), "");
u256 storageSlotOffset;
@ -1180,11 +1180,18 @@ string ABIFunctions::abiEncodingFunctionStruct(
solAssert(intraSlotOffset == 0, "");
members.back()["retrieveValue"] = "add(value, " + toCompactHexWithPrefix(storageSlotOffset) + ")";
}
break;
}
else
case DataLocation::Memory:
{
string sourceOffset = toCompactHexWithPrefix(_from.memoryOffsetOfMember(member.name));
members.back()["retrieveValue"] = "mload(add(value, " + sourceOffset + "))";
break;
}
case DataLocation::CallData:
solUnimplementedAssert(false, "Encoding struct from calldata is not yet supported.");
default:
solAssert(false, "");
}
EncodingOptions subOptions(_options);
@ -1192,10 +1199,14 @@ string ABIFunctions::abiEncodingFunctionStruct(
// Like with arrays, struct members are always padded.
subOptions.padded = true;
string memberValues = m_utils.suffixedVariableNameList("memberValue", 0, numVariablesForType(*memberTypeFrom, subOptions));
members.back()["memberValues"] = memberValues;
string encode;
if (_options.dynamicInplace)
encode = Whiskers{"pos := <encode>(memberValue, pos)"}
encode = Whiskers{"pos := <encode>(<memberValues>, pos)"}
("encode", abiEncodeAndReturnUpdatedPosFunction(*memberTypeFrom, *memberTypeTo, subOptions))
("memberValues", memberValues)
.render();
else
{
@ -1203,10 +1214,11 @@ string ABIFunctions::abiEncodingFunctionStruct(
dynamicMember ?
string(R"(
mstore(add(pos, <encodingOffset>), sub(tail, pos))
tail := <abiEncode>(memberValue, tail)
tail := <abiEncode>(<memberValues>, tail)
)") :
"<abiEncode>(memberValue, add(pos, <encodingOffset>))"
"<abiEncode>(<memberValues>, add(pos, <encodingOffset>))"
);
encodeTempl("memberValues", memberValues);
encodeTempl("encodingOffset", toCompactHexWithPrefix(encodingOffset));
encodingOffset += dynamicMember ? 0x20 : memberTypeTo->calldataEncodedSize();
encodeTempl("abiEncode", abiEncodingFunction(*memberTypeFrom, *memberTypeTo, subOptions));

View File

@ -330,8 +330,9 @@ string YulUtilFunctions::arrayDataAreaFunction(ArrayType const& _type)
{
string functionName = "array_dataslot_" + _type.identifier();
return m_functionCollector->createFunction(functionName, [&]() {
if (_type.dataStoredIn(DataLocation::Memory))
switch (_type.location())
{
case DataLocation::Memory:
if (_type.isDynamicallySized())
return Whiskers(R"(
function <functionName>(memPtr) -> dataPtr {
@ -348,9 +349,7 @@ string YulUtilFunctions::arrayDataAreaFunction(ArrayType const& _type)
)")
("functionName", functionName)
.render();
}
else if (_type.dataStoredIn(DataLocation::Storage))
{
case DataLocation::Storage:
if (_type.isDynamicallySized())
{
Whiskers w(R"(
@ -372,10 +371,21 @@ string YulUtilFunctions::arrayDataAreaFunction(ArrayType const& _type)
w("functionName", functionName);
return w.render();
}
}
else
case DataLocation::CallData:
{
// Not used for calldata
// Calldata arrays are stored as offset of the data area and length
// on the stack, so the offset already points to the data area.
// This might change, if calldata arrays are stored in a single
// stack slot at some point.
Whiskers w(R"(
function <functionName>(slot) -> dataSlot {
dataSlot := slot
}
)");
w("functionName", functionName);
return w.render();
}
default:
solAssert(false, "");
}
});
@ -384,19 +394,13 @@ string YulUtilFunctions::arrayDataAreaFunction(ArrayType const& _type)
string YulUtilFunctions::nextArrayElementFunction(ArrayType const& _type)
{
solAssert(!_type.isByteArray(), "");
solAssert(
_type.location() == DataLocation::Memory ||
_type.location() == DataLocation::Storage,
""
);
solAssert(
_type.location() == DataLocation::Memory ||
_type.baseType()->storageBytes() > 16,
""
);
if (_type.dataStoredIn(DataLocation::Storage))
solAssert(_type.baseType()->storageBytes() > 16, "");
string functionName = "array_nextElement_" + _type.identifier();
return m_functionCollector->createFunction(functionName, [&]() {
if (_type.location() == DataLocation::Memory)
switch (_type.location())
{
case DataLocation::Memory:
return Whiskers(R"(
function <functionName>(memPtr) -> nextPtr {
nextPtr := add(memPtr, 0x20)
@ -404,7 +408,7 @@ string YulUtilFunctions::nextArrayElementFunction(ArrayType const& _type)
)")
("functionName", functionName)
.render();
else if (_type.location() == DataLocation::Storage)
case DataLocation::Storage:
return Whiskers(R"(
function <functionName>(slot) -> nextSlot {
nextSlot := add(slot, 1)
@ -412,8 +416,18 @@ string YulUtilFunctions::nextArrayElementFunction(ArrayType const& _type)
)")
("functionName", functionName)
.render();
else
case DataLocation::CallData:
return Whiskers(R"(
function <functionName>(calldataPtr) -> nextPtr {
nextPtr := add(calldataPtr, <stride>)
}
)")
("stride", toCompactHexWithPrefix(_type.baseType()->isDynamicallyEncoded() ? 32 : _type.baseType()->calldataEncodedSize()))
("functionName", functionName)
.render();
default:
solAssert(false, "");
}
});
}

View File

@ -79,11 +79,11 @@ public:
/// The function reverts for too large lengths.
std::string arrayAllocationSizeFunction(ArrayType const& _type);
/// @returns the name of a function that converts a storage slot number
/// or a memory pointer to the slot number / memory pointer for the data position of an array
/// which is stored in that slot / memory area.
/// a memory pointer or a calldata pointer to the slot number / memory pointer / calldata pointer
/// for the data position of an array which is stored in that slot / memory area / calldata area.
std::string arrayDataAreaFunction(ArrayType const& _type);
/// @returns the name of a function that advances an array data pointer to the next element.
/// Only works for memory arrays and storage arrays that store one item per slot.
/// Only works for memory arrays, calldata arrays and storage arrays that store one item per slot.
std::string nextArrayElementFunction(ArrayType const& _type);
/// @returns the name of a function that allocates memory.