Merge pull request #5832 from ethereum/introduceEncodingOptions

[REF] Provide ABI encoding options as single struct parameter.
This commit is contained in:
chriseth 2019-01-21 23:53:21 +01:00 committed by GitHub
commit 7b66eb273d
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
2 changed files with 87 additions and 49 deletions

View File

@ -39,14 +39,19 @@ string ABIFunctions::tupleEncoder(
bool _encodeAsLibraryTypes bool _encodeAsLibraryTypes
) )
{ {
EncodingOptions options;
options.encodeAsLibraryTypes = _encodeAsLibraryTypes;
options.encodeFunctionFromStack = true;
options.padded = true;
options.dynamicInplace = false;
string functionName = string("abi_encode_tuple_"); string functionName = string("abi_encode_tuple_");
for (auto const& t: _givenTypes) for (auto const& t: _givenTypes)
functionName += t->identifier() + "_"; functionName += t->identifier() + "_";
functionName += "_to_"; functionName += "_to_";
for (auto const& t: _targetTypes) for (auto const& t: _targetTypes)
functionName += t->identifier() + "_"; functionName += t->identifier() + "_";
if (_encodeAsLibraryTypes) functionName += options.toFunctionNameSuffix();
functionName += "_library";
return createExternallyUsedFunction(functionName, [&]() { return createExternallyUsedFunction(functionName, [&]() {
solAssert(!_givenTypes.empty(), ""); solAssert(!_givenTypes.empty(), "");
@ -90,7 +95,7 @@ string ABIFunctions::tupleEncoder(
); );
elementTempl("values", valueNames); elementTempl("values", valueNames);
elementTempl("pos", to_string(headPos)); elementTempl("pos", to_string(headPos));
elementTempl("abiEncode", abiEncodingFunction(*_givenTypes[i], *_targetTypes[i], _encodeAsLibraryTypes, true)); elementTempl("abiEncode", abiEncodingFunction(*_givenTypes[i], *_targetTypes[i], options));
encodeElements += elementTempl.render(); encodeElements += elementTempl.render();
headPos += dynamic ? 0x20 : _targetTypes[i]->calldataEncodedSize(); headPos += dynamic ? 0x20 : _targetTypes[i]->calldataEncodedSize();
} }
@ -184,6 +189,20 @@ pair<string, set<string>> ABIFunctions::requestedFunctions()
return make_pair(result, std::move(m_externallyUsedFunctions)); return make_pair(result, std::move(m_externallyUsedFunctions));
} }
string ABIFunctions::EncodingOptions::toFunctionNameSuffix() const
{
string suffix;
if (!padded)
suffix += "_nonPadded";
if (dynamicInplace)
suffix += "_inplace";
if (encodeFunctionFromStack)
suffix += "_fromStack";
if (encodeAsLibraryTypes)
suffix += "_library";
return suffix;
}
string ABIFunctions::cleanupFunction(Type const& _type, bool _revertOnFailure) string ABIFunctions::cleanupFunction(Type const& _type, bool _revertOnFailure)
{ {
string functionName = string("cleanup_") + (_revertOnFailure ? "revert_" : "assert_") + _type.identifier(); string functionName = string("cleanup_") + (_revertOnFailure ? "revert_" : "assert_") + _type.identifier();
@ -490,32 +509,31 @@ string ABIFunctions::splitExternalFunctionIdFunction()
string ABIFunctions::abiEncodingFunction( string ABIFunctions::abiEncodingFunction(
Type const& _from, Type const& _from,
Type const& _to, Type const& _to,
bool _encodeAsLibraryTypes, EncodingOptions const& _options
bool _fromStack
) )
{ {
TypePointer toInterface = _to.fullEncodingType(_encodeAsLibraryTypes, true, false); TypePointer toInterface = _to.fullEncodingType(_options.encodeAsLibraryTypes, true, false);
solUnimplementedAssert(toInterface, "Encoding type \"" + _to.toString() + "\" not yet implemented."); solUnimplementedAssert(toInterface, "Encoding type \"" + _to.toString() + "\" not yet implemented.");
Type const& to = *toInterface; Type const& to = *toInterface;
if (_from.category() == Type::Category::StringLiteral) if (_from.category() == Type::Category::StringLiteral)
return abiEncodingFunctionStringLiteral(_from, to, _encodeAsLibraryTypes); return abiEncodingFunctionStringLiteral(_from, to, _options);
else if (auto toArray = dynamic_cast<ArrayType const*>(&to)) else if (auto toArray = dynamic_cast<ArrayType const*>(&to))
{ {
solAssert(_from.category() == Type::Category::Array, ""); solAssert(_from.category() == Type::Category::Array, "");
solAssert(to.dataStoredIn(DataLocation::Memory), ""); solAssert(to.dataStoredIn(DataLocation::Memory), "");
ArrayType const& fromArray = dynamic_cast<ArrayType const&>(_from); ArrayType const& fromArray = dynamic_cast<ArrayType const&>(_from);
if (fromArray.location() == DataLocation::CallData) if (fromArray.location() == DataLocation::CallData)
return abiEncodingFunctionCalldataArray(fromArray, *toArray, _encodeAsLibraryTypes); return abiEncodingFunctionCalldataArray(fromArray, *toArray, _options);
else if (!fromArray.isByteArray() && ( else if (!fromArray.isByteArray() && (
fromArray.location() == DataLocation::Memory || fromArray.location() == DataLocation::Memory ||
fromArray.baseType()->storageBytes() > 16 fromArray.baseType()->storageBytes() > 16
)) ))
return abiEncodingFunctionSimpleArray(fromArray, *toArray, _encodeAsLibraryTypes); return abiEncodingFunctionSimpleArray(fromArray, *toArray, _options);
else if (fromArray.location() == DataLocation::Memory) else if (fromArray.location() == DataLocation::Memory)
return abiEncodingFunctionMemoryByteArray(fromArray, *toArray, _encodeAsLibraryTypes); return abiEncodingFunctionMemoryByteArray(fromArray, *toArray, _options);
else if (fromArray.location() == DataLocation::Storage) else if (fromArray.location() == DataLocation::Storage)
return abiEncodingFunctionCompactStorageArray(fromArray, *toArray, _encodeAsLibraryTypes); return abiEncodingFunctionCompactStorageArray(fromArray, *toArray, _options);
else else
solAssert(false, ""); solAssert(false, "");
} }
@ -523,14 +541,13 @@ string ABIFunctions::abiEncodingFunction(
{ {
StructType const* fromStruct = dynamic_cast<StructType const*>(&_from); StructType const* fromStruct = dynamic_cast<StructType const*>(&_from);
solAssert(fromStruct, ""); solAssert(fromStruct, "");
return abiEncodingFunctionStruct(*fromStruct, *toStruct, _encodeAsLibraryTypes); return abiEncodingFunctionStruct(*fromStruct, *toStruct, _options);
} }
else if (_from.category() == Type::Category::Function) else if (_from.category() == Type::Category::Function)
return abiEncodingFunctionFunctionType( return abiEncodingFunctionFunctionType(
dynamic_cast<FunctionType const&>(_from), dynamic_cast<FunctionType const&>(_from),
to, to,
_encodeAsLibraryTypes, _options
_fromStack
); );
solAssert(_from.sizeOnStack() == 1, ""); solAssert(_from.sizeOnStack() == 1, "");
@ -541,7 +558,7 @@ string ABIFunctions::abiEncodingFunction(
_from.identifier() + _from.identifier() +
"_to_" + "_to_" +
to.identifier() + to.identifier() +
(_encodeAsLibraryTypes ? "_library" : ""); _options.toFunctionNameSuffix();
return createFunction(functionName, [&]() { return createFunction(functionName, [&]() {
solAssert(!to.isDynamicallyEncoded(), ""); solAssert(!to.isDynamicallyEncoded(), "");
@ -556,7 +573,7 @@ string ABIFunctions::abiEncodingFunction(
{ {
// special case: convert storage reference type to value type - this is only // special case: convert storage reference type to value type - this is only
// possible for library calls where we just forward the storage reference // possible for library calls where we just forward the storage reference
solAssert(_encodeAsLibraryTypes, ""); solAssert(_options.encodeAsLibraryTypes, "");
solAssert(to == IntegerType::uint256(), ""); solAssert(to == IntegerType::uint256(), "");
templ("cleanupConvert", "value"); templ("cleanupConvert", "value");
} }
@ -574,7 +591,7 @@ string ABIFunctions::abiEncodingFunction(
string ABIFunctions::abiEncodingFunctionCalldataArray( string ABIFunctions::abiEncodingFunctionCalldataArray(
Type const& _from, Type const& _from,
Type const& _to, Type const& _to,
bool _encodeAsLibraryTypes EncodingOptions const& _options
) )
{ {
solAssert(_to.isDynamicallySized(), ""); solAssert(_to.isDynamicallySized(), "");
@ -596,7 +613,7 @@ string ABIFunctions::abiEncodingFunctionCalldataArray(
_from.identifier() + _from.identifier() +
"_to_" + "_to_" +
_to.identifier() + _to.identifier() +
(_encodeAsLibraryTypes ? "_library" : ""); _options.toFunctionNameSuffix();
return createFunction(functionName, [&]() { return createFunction(functionName, [&]() {
solUnimplementedAssert(fromArrayType.isByteArray(), "Only byte arrays can be encoded from calldata currently."); solUnimplementedAssert(fromArrayType.isByteArray(), "Only byte arrays can be encoded from calldata currently.");
// TODO if this is not a byte array, we might just copy byte-by-byte anyway, // TODO if this is not a byte array, we might just copy byte-by-byte anyway,
@ -622,7 +639,7 @@ string ABIFunctions::abiEncodingFunctionCalldataArray(
string ABIFunctions::abiEncodingFunctionSimpleArray( string ABIFunctions::abiEncodingFunctionSimpleArray(
ArrayType const& _from, ArrayType const& _from,
ArrayType const& _to, ArrayType const& _to,
bool _encodeAsLibraryTypes EncodingOptions const& _options
) )
{ {
string functionName = string functionName =
@ -630,7 +647,7 @@ string ABIFunctions::abiEncodingFunctionSimpleArray(
_from.identifier() + _from.identifier() +
"_to_" + "_to_" +
_to.identifier() + _to.identifier() +
(_encodeAsLibraryTypes ? "_library" : ""); _options.toFunctionNameSuffix();
solAssert(_from.isDynamicallySized() == _to.isDynamicallySized(), ""); solAssert(_from.isDynamicallySized() == _to.isDynamicallySized(), "");
solAssert(_from.length() == _to.length(), ""); solAssert(_from.length() == _to.length(), "");
@ -691,11 +708,13 @@ string ABIFunctions::abiEncodingFunctionSimpleArray(
templ("storeLength", ""); templ("storeLength", "");
templ("dataAreaFun", arrayDataAreaFunction(_from)); templ("dataAreaFun", arrayDataAreaFunction(_from));
templ("elementEncodedSize", toCompactHexWithPrefix(_to.baseType()->calldataEncodedSize())); templ("elementEncodedSize", toCompactHexWithPrefix(_to.baseType()->calldataEncodedSize()));
EncodingOptions subOptions(_options);
subOptions.encodeFunctionFromStack = false;
templ("encodeToMemoryFun", abiEncodingFunction( templ("encodeToMemoryFun", abiEncodingFunction(
*_from.baseType(), *_from.baseType(),
*_to.baseType(), *_to.baseType(),
_encodeAsLibraryTypes, subOptions
false
)); ));
templ("arrayElementAccess", inMemory ? "mload(srcPtr)" : _from.baseType()->isValueType() ? "sload(srcPtr)" : "srcPtr" ); templ("arrayElementAccess", inMemory ? "mload(srcPtr)" : _from.baseType()->isValueType() ? "sload(srcPtr)" : "srcPtr" );
templ("nextArrayElement", nextArrayElementFunction(_from)); templ("nextArrayElement", nextArrayElementFunction(_from));
@ -706,7 +725,7 @@ string ABIFunctions::abiEncodingFunctionSimpleArray(
string ABIFunctions::abiEncodingFunctionMemoryByteArray( string ABIFunctions::abiEncodingFunctionMemoryByteArray(
ArrayType const& _from, ArrayType const& _from,
ArrayType const& _to, ArrayType const& _to,
bool _encodeAsLibraryTypes EncodingOptions const& _options
) )
{ {
string functionName = string functionName =
@ -714,7 +733,7 @@ string ABIFunctions::abiEncodingFunctionMemoryByteArray(
_from.identifier() + _from.identifier() +
"_to_" + "_to_" +
_to.identifier() + _to.identifier() +
(_encodeAsLibraryTypes ? "_library" : ""); _options.toFunctionNameSuffix();
solAssert(_from.isDynamicallySized() == _to.isDynamicallySized(), ""); solAssert(_from.isDynamicallySized() == _to.isDynamicallySized(), "");
solAssert(_from.length() == _to.length(), ""); solAssert(_from.length() == _to.length(), "");
@ -742,7 +761,7 @@ string ABIFunctions::abiEncodingFunctionMemoryByteArray(
string ABIFunctions::abiEncodingFunctionCompactStorageArray( string ABIFunctions::abiEncodingFunctionCompactStorageArray(
ArrayType const& _from, ArrayType const& _from,
ArrayType const& _to, ArrayType const& _to,
bool _encodeAsLibraryTypes EncodingOptions const& _options
) )
{ {
string functionName = string functionName =
@ -750,7 +769,7 @@ string ABIFunctions::abiEncodingFunctionCompactStorageArray(
_from.identifier() + _from.identifier() +
"_to_" + "_to_" +
_to.identifier() + _to.identifier() +
(_encodeAsLibraryTypes ? "_library" : ""); _options.toFunctionNameSuffix();
solAssert(_from.isDynamicallySized() == _to.isDynamicallySized(), ""); solAssert(_from.isDynamicallySized() == _to.isDynamicallySized(), "");
solAssert(_from.length() == _to.length(), ""); solAssert(_from.length() == _to.length(), "");
@ -840,11 +859,13 @@ string ABIFunctions::abiEncodingFunctionCompactStorageArray(
templ("itemsPerSlot", to_string(itemsPerSlot)); templ("itemsPerSlot", to_string(itemsPerSlot));
string elementEncodedSize = toCompactHexWithPrefix(_to.baseType()->calldataEncodedSize()); string elementEncodedSize = toCompactHexWithPrefix(_to.baseType()->calldataEncodedSize());
templ("elementEncodedSize", elementEncodedSize); templ("elementEncodedSize", elementEncodedSize);
EncodingOptions subOptions(_options);
subOptions.encodeFunctionFromStack = false;
string encodeToMemoryFun = abiEncodingFunction( string encodeToMemoryFun = abiEncodingFunction(
*_from.baseType(), *_from.baseType(),
*_to.baseType(), *_to.baseType(),
_encodeAsLibraryTypes, subOptions
false
); );
templ("encodeToMemoryFun", encodeToMemoryFun); templ("encodeToMemoryFun", encodeToMemoryFun);
std::vector<std::map<std::string, std::string>> items(itemsPerSlot); std::vector<std::map<std::string, std::string>> items(itemsPerSlot);
@ -859,7 +880,7 @@ string ABIFunctions::abiEncodingFunctionCompactStorageArray(
string ABIFunctions::abiEncodingFunctionStruct( string ABIFunctions::abiEncodingFunctionStruct(
StructType const& _from, StructType const& _from,
StructType const& _to, StructType const& _to,
bool _encodeAsLibraryTypes EncodingOptions const& _options
) )
{ {
string functionName = string functionName =
@ -867,7 +888,7 @@ string ABIFunctions::abiEncodingFunctionStruct(
_from.identifier() + _from.identifier() +
"_to_" + "_to_" +
_to.identifier() + _to.identifier() +
(_encodeAsLibraryTypes ? "_library" : ""); _options.toFunctionNameSuffix();
solUnimplementedAssert(!_from.dataStoredIn(DataLocation::CallData), "Encoding struct from calldata is not yet supported."); solUnimplementedAssert(!_from.dataStoredIn(DataLocation::CallData), "Encoding struct from calldata is not yet supported.");
solAssert(&_from.structDefinition() == &_to.structDefinition(), ""); solAssert(&_from.structDefinition() == &_to.structDefinition(), "");
@ -904,7 +925,7 @@ string ABIFunctions::abiEncodingFunctionStruct(
solAssert(member.type, ""); solAssert(member.type, "");
if (!member.type->canLiveOutsideStorage()) if (!member.type->canLiveOutsideStorage())
continue; continue;
TypePointer memberTypeTo = member.type->fullEncodingType(_encodeAsLibraryTypes, true, false); TypePointer memberTypeTo = member.type->fullEncodingType(_options.encodeAsLibraryTypes, true, false);
solUnimplementedAssert(memberTypeTo, "Encoding type \"" + member.type->toString() + "\" not yet implemented."); solUnimplementedAssert(memberTypeTo, "Encoding type \"" + member.type->toString() + "\" not yet implemented.");
auto memberTypeFrom = _from.memberType(member.name); auto memberTypeFrom = _from.memberType(member.name);
solAssert(memberTypeFrom, ""); solAssert(memberTypeFrom, "");
@ -958,7 +979,10 @@ string ABIFunctions::abiEncodingFunctionStruct(
} }
memberTempl("encodingOffset", toCompactHexWithPrefix(encodingOffset)); memberTempl("encodingOffset", toCompactHexWithPrefix(encodingOffset));
encodingOffset += dynamicMember ? 0x20 : memberTypeTo->calldataEncodedSize(); encodingOffset += dynamicMember ? 0x20 : memberTypeTo->calldataEncodedSize();
memberTempl("abiEncode", abiEncodingFunction(*memberTypeFrom, *memberTypeTo, _encodeAsLibraryTypes, false));
EncodingOptions subOptions(_options);
subOptions.encodeFunctionFromStack = false;
memberTempl("abiEncode", abiEncodingFunction(*memberTypeFrom, *memberTypeTo, subOptions));
members.push_back({}); members.push_back({});
members.back()["encode"] = memberTempl.render(); members.back()["encode"] = memberTempl.render();
@ -973,7 +997,7 @@ string ABIFunctions::abiEncodingFunctionStruct(
string ABIFunctions::abiEncodingFunctionStringLiteral( string ABIFunctions::abiEncodingFunctionStringLiteral(
Type const& _from, Type const& _from,
Type const& _to, Type const& _to,
bool _encodeAsLibraryTypes EncodingOptions const& _options
) )
{ {
solAssert(_from.category() == Type::Category::StringLiteral, ""); solAssert(_from.category() == Type::Category::StringLiteral, "");
@ -983,7 +1007,7 @@ string ABIFunctions::abiEncodingFunctionStringLiteral(
_from.identifier() + _from.identifier() +
"_to_" + "_to_" +
_to.identifier() + _to.identifier() +
(_encodeAsLibraryTypes ? "_library" : ""); _options.toFunctionNameSuffix();
return createFunction(functionName, [&]() { return createFunction(functionName, [&]() {
auto const& strType = dynamic_cast<StringLiteralType const&>(_from); auto const& strType = dynamic_cast<StringLiteralType const&>(_from);
string const& value = strType.value(); string const& value = strType.value();
@ -1034,8 +1058,7 @@ string ABIFunctions::abiEncodingFunctionStringLiteral(
string ABIFunctions::abiEncodingFunctionFunctionType( string ABIFunctions::abiEncodingFunctionFunctionType(
FunctionType const& _from, FunctionType const& _from,
Type const& _to, Type const& _to,
bool _encodeAsLibraryTypes, EncodingOptions const& _options
bool _fromStack
) )
{ {
solAssert(_from.kind() == FunctionType::Kind::External, ""); solAssert(_from.kind() == FunctionType::Kind::External, "");
@ -1046,10 +1069,9 @@ string ABIFunctions::abiEncodingFunctionFunctionType(
_from.identifier() + _from.identifier() +
"_to_" + "_to_" +
_to.identifier() + _to.identifier() +
(_fromStack ? "_fromStack" : "") + _options.toFunctionNameSuffix();
(_encodeAsLibraryTypes ? "_library" : "");
if (_fromStack) if (_options.encodeFunctionFromStack)
return createFunction(functionName, [&]() { return createFunction(functionName, [&]() {
return Whiskers(R"( return Whiskers(R"(
function <functionName>(addr, function_id, pos) { function <functionName>(addr, function_id, pos) {
@ -1716,3 +1738,4 @@ size_t ABIFunctions::headSize(TypePointers const& _targetTypes)
return headSize; return headSize;
} }

View File

@ -87,6 +87,23 @@ public:
std::pair<std::string, std::set<std::string>> requestedFunctions(); std::pair<std::string, std::set<std::string>> requestedFunctions();
private: private:
struct EncodingOptions
{
/// Pad/signextend value types and bytes/string to multiples of 32 bytes.
bool padded = true;
/// Store arrays and structs in place without "data pointer" and do not store the length.
bool dynamicInplace = false;
/// Only for external function types: The value is a pair of address / function id instead
/// of a memory pointer to the compression representation.
bool encodeFunctionFromStack = false;
/// Encode storage pointers as storage pointers (we are targeting a library call).
bool encodeAsLibraryTypes = false;
/// @returns a string to uniquely identify the encoding options for the encoding
/// function name. Skips everything that has its default value.
std::string toFunctionNameSuffix() const;
};
/// @returns the name of the cleanup function for the given type and /// @returns the name of the cleanup function for the given type and
/// adds its implementation to the requested functions. /// adds its implementation to the requested functions.
/// @param _revertOnFailure if true, causes revert on invalid data, /// @param _revertOnFailure if true, causes revert on invalid data,
@ -115,40 +132,39 @@ private:
std::string abiEncodingFunction( std::string abiEncodingFunction(
Type const& _givenType, Type const& _givenType,
Type const& _targetType, Type const& _targetType,
bool _encodeAsLibraryTypes, EncodingOptions const& _options
bool _fromStack
); );
/// Part of @a abiEncodingFunction for array target type and given calldata array. /// Part of @a abiEncodingFunction for array target type and given calldata array.
std::string abiEncodingFunctionCalldataArray( std::string abiEncodingFunctionCalldataArray(
Type const& _givenType, Type const& _givenType,
Type const& _targetType, Type const& _targetType,
bool _encodeAsLibraryTypes EncodingOptions const& _options
); );
/// Part of @a abiEncodingFunction for array target type and given memory array or /// Part of @a abiEncodingFunction for array target type and given memory array or
/// a given storage array with one item per slot. /// a given storage array with one item per slot.
std::string abiEncodingFunctionSimpleArray( std::string abiEncodingFunctionSimpleArray(
ArrayType const& _givenType, ArrayType const& _givenType,
ArrayType const& _targetType, ArrayType const& _targetType,
bool _encodeAsLibraryTypes EncodingOptions const& _options
); );
std::string abiEncodingFunctionMemoryByteArray( std::string abiEncodingFunctionMemoryByteArray(
ArrayType const& _givenType, ArrayType const& _givenType,
ArrayType const& _targetType, ArrayType const& _targetType,
bool _encodeAsLibraryTypes EncodingOptions const& _options
); );
/// Part of @a abiEncodingFunction for array target type and given storage array /// Part of @a abiEncodingFunction for array target type and given storage array
/// where multiple items are packed into the same storage slot. /// where multiple items are packed into the same storage slot.
std::string abiEncodingFunctionCompactStorageArray( std::string abiEncodingFunctionCompactStorageArray(
ArrayType const& _givenType, ArrayType const& _givenType,
ArrayType const& _targetType, ArrayType const& _targetType,
bool _encodeAsLibraryTypes EncodingOptions const& _options
); );
/// Part of @a abiEncodingFunction for struct types. /// Part of @a abiEncodingFunction for struct types.
std::string abiEncodingFunctionStruct( std::string abiEncodingFunctionStruct(
StructType const& _givenType, StructType const& _givenType,
StructType const& _targetType, StructType const& _targetType,
bool _encodeAsLibraryTypes EncodingOptions const& _options
); );
// @returns the name of the ABI encoding function with the given type // @returns the name of the ABI encoding function with the given type
@ -157,14 +173,13 @@ private:
std::string abiEncodingFunctionStringLiteral( std::string abiEncodingFunctionStringLiteral(
Type const& _givenType, Type const& _givenType,
Type const& _targetType, Type const& _targetType,
bool _encodeAsLibraryTypes EncodingOptions const& _options
); );
std::string abiEncodingFunctionFunctionType( std::string abiEncodingFunctionFunctionType(
FunctionType const& _from, FunctionType const& _from,
Type const& _to, Type const& _to,
bool _encodeAsLibraryTypes, EncodingOptions const& _options
bool _fromStack
); );
/// @returns the name of the ABI decoding function for the given type /// @returns the name of the ABI decoding function for the given type