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

View File

@ -330,8 +330,9 @@ string YulUtilFunctions::arrayDataAreaFunction(ArrayType const& _type)
{ {
string functionName = "array_dataslot_" + _type.identifier(); string functionName = "array_dataslot_" + _type.identifier();
return m_functionCollector->createFunction(functionName, [&]() { return m_functionCollector->createFunction(functionName, [&]() {
if (_type.dataStoredIn(DataLocation::Memory)) switch (_type.location())
{ {
case DataLocation::Memory:
if (_type.isDynamicallySized()) if (_type.isDynamicallySized())
return Whiskers(R"( return Whiskers(R"(
function <functionName>(memPtr) -> dataPtr { function <functionName>(memPtr) -> dataPtr {
@ -348,9 +349,7 @@ string YulUtilFunctions::arrayDataAreaFunction(ArrayType const& _type)
)") )")
("functionName", functionName) ("functionName", functionName)
.render(); .render();
} case DataLocation::Storage:
else if (_type.dataStoredIn(DataLocation::Storage))
{
if (_type.isDynamicallySized()) if (_type.isDynamicallySized())
{ {
Whiskers w(R"( Whiskers w(R"(
@ -372,10 +371,21 @@ string YulUtilFunctions::arrayDataAreaFunction(ArrayType const& _type)
w("functionName", functionName); w("functionName", functionName);
return w.render(); return w.render();
} }
} case DataLocation::CallData:
else
{ {
// 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, ""); solAssert(false, "");
} }
}); });
@ -384,19 +394,13 @@ string YulUtilFunctions::arrayDataAreaFunction(ArrayType const& _type)
string YulUtilFunctions::nextArrayElementFunction(ArrayType const& _type) string YulUtilFunctions::nextArrayElementFunction(ArrayType const& _type)
{ {
solAssert(!_type.isByteArray(), ""); solAssert(!_type.isByteArray(), "");
solAssert( if (_type.dataStoredIn(DataLocation::Storage))
_type.location() == DataLocation::Memory || solAssert(_type.baseType()->storageBytes() > 16, "");
_type.location() == DataLocation::Storage,
""
);
solAssert(
_type.location() == DataLocation::Memory ||
_type.baseType()->storageBytes() > 16,
""
);
string functionName = "array_nextElement_" + _type.identifier(); string functionName = "array_nextElement_" + _type.identifier();
return m_functionCollector->createFunction(functionName, [&]() { return m_functionCollector->createFunction(functionName, [&]() {
if (_type.location() == DataLocation::Memory) switch (_type.location())
{
case DataLocation::Memory:
return Whiskers(R"( return Whiskers(R"(
function <functionName>(memPtr) -> nextPtr { function <functionName>(memPtr) -> nextPtr {
nextPtr := add(memPtr, 0x20) nextPtr := add(memPtr, 0x20)
@ -404,7 +408,7 @@ string YulUtilFunctions::nextArrayElementFunction(ArrayType const& _type)
)") )")
("functionName", functionName) ("functionName", functionName)
.render(); .render();
else if (_type.location() == DataLocation::Storage) case DataLocation::Storage:
return Whiskers(R"( return Whiskers(R"(
function <functionName>(slot) -> nextSlot { function <functionName>(slot) -> nextSlot {
nextSlot := add(slot, 1) nextSlot := add(slot, 1)
@ -412,8 +416,18 @@ string YulUtilFunctions::nextArrayElementFunction(ArrayType const& _type)
)") )")
("functionName", functionName) ("functionName", functionName)
.render(); .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, ""); solAssert(false, "");
}
}); });
} }

View File

@ -79,11 +79,11 @@ public:
/// The function reverts for too large lengths. /// The function reverts for too large lengths.
std::string arrayAllocationSizeFunction(ArrayType const& _type); std::string arrayAllocationSizeFunction(ArrayType const& _type);
/// @returns the name of a function that converts a storage slot number /// @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 /// a memory pointer or a calldata pointer to the slot number / memory pointer / calldata pointer
/// which is stored in that slot / memory area. /// for the data position of an array which is stored in that slot / memory area / calldata area.
std::string arrayDataAreaFunction(ArrayType const& _type); std::string arrayDataAreaFunction(ArrayType const& _type);
/// @returns the name of a function that advances an array data pointer to the next element. /// @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); std::string nextArrayElementFunction(ArrayType const& _type);
/// @returns the name of a function that allocates memory. /// @returns the name of a function that allocates memory.