mirror of
https://github.com/ethereum/solidity
synced 2023-10-03 13:03:40 +00:00
Merge pull request #6496 from ethereum/move-funcs-6479
Move convert functions to Yul module
This commit is contained in:
commit
bf653b004e
@ -255,143 +255,6 @@ string ABIFunctions::EncodingOptions::toFunctionNameSuffix() const
|
||||
return suffix;
|
||||
}
|
||||
|
||||
string ABIFunctions::cleanupFunction(Type const& _type)
|
||||
{
|
||||
string functionName = string("cleanup_") + _type.identifier();
|
||||
return createFunction(functionName, [&]() {
|
||||
Whiskers templ(R"(
|
||||
function <functionName>(value) -> cleaned {
|
||||
<body>
|
||||
}
|
||||
)");
|
||||
templ("functionName", functionName);
|
||||
switch (_type.category())
|
||||
{
|
||||
case Type::Category::Address:
|
||||
templ("body", "cleaned := " + cleanupFunction(IntegerType(160)) + "(value)");
|
||||
break;
|
||||
case Type::Category::Integer:
|
||||
{
|
||||
IntegerType const& type = dynamic_cast<IntegerType const&>(_type);
|
||||
if (type.numBits() == 256)
|
||||
templ("body", "cleaned := value");
|
||||
else if (type.isSigned())
|
||||
templ("body", "cleaned := signextend(" + to_string(type.numBits() / 8 - 1) + ", value)");
|
||||
else
|
||||
templ("body", "cleaned := and(value, " + toCompactHexWithPrefix((u256(1) << type.numBits()) - 1) + ")");
|
||||
break;
|
||||
}
|
||||
case Type::Category::RationalNumber:
|
||||
templ("body", "cleaned := value");
|
||||
break;
|
||||
case Type::Category::Bool:
|
||||
templ("body", "cleaned := iszero(iszero(value))");
|
||||
break;
|
||||
case Type::Category::FixedPoint:
|
||||
solUnimplemented("Fixed point types not implemented.");
|
||||
break;
|
||||
case Type::Category::Function:
|
||||
solAssert(dynamic_cast<FunctionType const&>(_type).kind() == FunctionType::Kind::External, "");
|
||||
templ("body", "cleaned := " + cleanupFunction(FixedBytesType(24)) + "(value)");
|
||||
break;
|
||||
case Type::Category::Array:
|
||||
case Type::Category::Struct:
|
||||
case Type::Category::Mapping:
|
||||
solAssert(_type.dataStoredIn(DataLocation::Storage), "Cleanup requested for non-storage reference type.");
|
||||
templ("body", "cleaned := value");
|
||||
break;
|
||||
case Type::Category::FixedBytes:
|
||||
{
|
||||
FixedBytesType const& type = dynamic_cast<FixedBytesType const&>(_type);
|
||||
if (type.numBytes() == 32)
|
||||
templ("body", "cleaned := value");
|
||||
else if (type.numBytes() == 0)
|
||||
// This is disallowed in the type system.
|
||||
solAssert(false, "");
|
||||
else
|
||||
{
|
||||
size_t numBits = type.numBytes() * 8;
|
||||
u256 mask = ((u256(1) << numBits) - 1) << (256 - numBits);
|
||||
templ("body", "cleaned := and(value, " + toCompactHexWithPrefix(mask) + ")");
|
||||
}
|
||||
break;
|
||||
}
|
||||
case Type::Category::Contract:
|
||||
{
|
||||
AddressType addressType(dynamic_cast<ContractType const&>(_type).isPayable() ?
|
||||
StateMutability::Payable :
|
||||
StateMutability::NonPayable
|
||||
);
|
||||
templ("body", "cleaned := " + cleanupFunction(addressType) + "(value)");
|
||||
break;
|
||||
}
|
||||
case Type::Category::Enum:
|
||||
{
|
||||
// Out of range enums cannot be truncated unambigiously and therefore it should be an error.
|
||||
templ("body", "cleaned := value " + validatorFunction(_type) + "(value)");
|
||||
break;
|
||||
}
|
||||
case Type::Category::InaccessibleDynamic:
|
||||
templ("body", "cleaned := 0");
|
||||
break;
|
||||
default:
|
||||
solAssert(false, "Cleanup of type " + _type.identifier() + " requested.");
|
||||
}
|
||||
|
||||
return templ.render();
|
||||
});
|
||||
}
|
||||
|
||||
string ABIFunctions::validatorFunction(Type const& _type, bool _revertOnFailure)
|
||||
{
|
||||
string functionName = string("validator_") + (_revertOnFailure ? "revert_" : "assert_") + _type.identifier();
|
||||
return createFunction(functionName, [&]() {
|
||||
Whiskers templ(R"(
|
||||
function <functionName>(value) {
|
||||
if iszero(<condition>) { <failure> }
|
||||
}
|
||||
)");
|
||||
templ("functionName", functionName);
|
||||
if (_revertOnFailure)
|
||||
templ("failure", "revert(0, 0)");
|
||||
else
|
||||
templ("failure", "invalid()");
|
||||
|
||||
switch (_type.category())
|
||||
{
|
||||
case Type::Category::Address:
|
||||
case Type::Category::Integer:
|
||||
case Type::Category::RationalNumber:
|
||||
case Type::Category::Bool:
|
||||
case Type::Category::FixedPoint:
|
||||
case Type::Category::Function:
|
||||
case Type::Category::Array:
|
||||
case Type::Category::Struct:
|
||||
case Type::Category::Mapping:
|
||||
case Type::Category::FixedBytes:
|
||||
case Type::Category::Contract:
|
||||
{
|
||||
templ("condition", "eq(value, " + cleanupFunction(_type) + "(value))");
|
||||
break;
|
||||
}
|
||||
case Type::Category::Enum:
|
||||
{
|
||||
size_t members = dynamic_cast<EnumType const&>(_type).numberOfMembers();
|
||||
solAssert(members > 0, "empty enum should have caused a parser error.");
|
||||
templ("condition", "lt(value, " + to_string(members) + ")");
|
||||
break;
|
||||
}
|
||||
case Type::Category::InaccessibleDynamic:
|
||||
templ("condition", "1");
|
||||
break;
|
||||
default:
|
||||
solAssert(false, "Validation of type " + _type.identifier() + " requested.");
|
||||
}
|
||||
|
||||
return templ.render();
|
||||
});
|
||||
}
|
||||
|
||||
string ABIFunctions::cleanupFromStorageFunction(Type const& _type, bool _splitFunctionTypes)
|
||||
{
|
||||
solAssert(_type.isValueType(), "");
|
||||
@ -425,171 +288,6 @@ string ABIFunctions::cleanupFromStorageFunction(Type const& _type, bool _splitFu
|
||||
});
|
||||
}
|
||||
|
||||
string ABIFunctions::conversionFunction(Type const& _from, Type const& _to)
|
||||
{
|
||||
string functionName =
|
||||
"convert_" +
|
||||
_from.identifier() +
|
||||
"_to_" +
|
||||
_to.identifier();
|
||||
return createFunction(functionName, [&]() {
|
||||
Whiskers templ(R"(
|
||||
function <functionName>(value) -> converted {
|
||||
<body>
|
||||
}
|
||||
)");
|
||||
templ("functionName", functionName);
|
||||
string body;
|
||||
auto toCategory = _to.category();
|
||||
auto fromCategory = _from.category();
|
||||
switch (fromCategory)
|
||||
{
|
||||
case Type::Category::Address:
|
||||
body =
|
||||
Whiskers("converted := <convert>(value)")
|
||||
("convert", conversionFunction(IntegerType(160), _to))
|
||||
.render();
|
||||
break;
|
||||
case Type::Category::Integer:
|
||||
case Type::Category::RationalNumber:
|
||||
case Type::Category::Contract:
|
||||
{
|
||||
if (RationalNumberType const* rational = dynamic_cast<RationalNumberType const*>(&_from))
|
||||
solUnimplementedAssert(!rational->isFractional(), "Not yet implemented - FixedPointType.");
|
||||
if (toCategory == Type::Category::FixedBytes)
|
||||
{
|
||||
solAssert(
|
||||
fromCategory == Type::Category::Integer || fromCategory == Type::Category::RationalNumber,
|
||||
"Invalid conversion to FixedBytesType requested."
|
||||
);
|
||||
FixedBytesType const& toBytesType = dynamic_cast<FixedBytesType const&>(_to);
|
||||
body =
|
||||
Whiskers("converted := <shiftLeft>(<clean>(value))")
|
||||
("shiftLeft", m_utils.shiftLeftFunction(256 - toBytesType.numBytes() * 8))
|
||||
("clean", cleanupFunction(_from))
|
||||
.render();
|
||||
}
|
||||
else if (toCategory == Type::Category::Enum)
|
||||
{
|
||||
solAssert(_from.mobileType(), "");
|
||||
body =
|
||||
Whiskers("converted := <cleanEnum>(<cleanInt>(value))")
|
||||
("cleanEnum", cleanupFunction(_to))
|
||||
// "mobileType()" returns integer type for rational
|
||||
("cleanInt", cleanupFunction(*_from.mobileType()))
|
||||
.render();
|
||||
}
|
||||
else if (toCategory == Type::Category::FixedPoint)
|
||||
solUnimplemented("Not yet implemented - FixedPointType.");
|
||||
else if (toCategory == Type::Category::Address)
|
||||
body =
|
||||
Whiskers("converted := <convert>(value)")
|
||||
("convert", conversionFunction(_from, IntegerType(160)))
|
||||
.render();
|
||||
else
|
||||
{
|
||||
solAssert(
|
||||
toCategory == Type::Category::Integer ||
|
||||
toCategory == Type::Category::Contract,
|
||||
"");
|
||||
IntegerType const addressType(160);
|
||||
IntegerType const& to =
|
||||
toCategory == Type::Category::Integer ?
|
||||
dynamic_cast<IntegerType const&>(_to) :
|
||||
addressType;
|
||||
|
||||
// Clean according to the "to" type, except if this is
|
||||
// a widening conversion.
|
||||
IntegerType const* cleanupType = &to;
|
||||
if (fromCategory != Type::Category::RationalNumber)
|
||||
{
|
||||
IntegerType const& from =
|
||||
fromCategory == Type::Category::Integer ?
|
||||
dynamic_cast<IntegerType const&>(_from) :
|
||||
addressType;
|
||||
if (to.numBits() > from.numBits())
|
||||
cleanupType = &from;
|
||||
}
|
||||
body =
|
||||
Whiskers("converted := <cleanInt>(value)")
|
||||
("cleanInt", cleanupFunction(*cleanupType))
|
||||
.render();
|
||||
}
|
||||
break;
|
||||
}
|
||||
case Type::Category::Bool:
|
||||
{
|
||||
solAssert(_from == _to, "Invalid conversion for bool.");
|
||||
body =
|
||||
Whiskers("converted := <clean>(value)")
|
||||
("clean", cleanupFunction(_from))
|
||||
.render();
|
||||
break;
|
||||
}
|
||||
case Type::Category::FixedPoint:
|
||||
solUnimplemented("Fixed point types not implemented.");
|
||||
break;
|
||||
case Type::Category::Array:
|
||||
solUnimplementedAssert(false, "Array conversion not implemented.");
|
||||
break;
|
||||
case Type::Category::Struct:
|
||||
solUnimplementedAssert(false, "Struct conversion not implemented.");
|
||||
break;
|
||||
case Type::Category::FixedBytes:
|
||||
{
|
||||
FixedBytesType const& from = dynamic_cast<FixedBytesType const&>(_from);
|
||||
if (toCategory == Type::Category::Integer)
|
||||
body =
|
||||
Whiskers("converted := <convert>(<shift>(value))")
|
||||
("shift", m_utils.shiftRightFunction(256 - from.numBytes() * 8))
|
||||
("convert", conversionFunction(IntegerType(from.numBytes() * 8), _to))
|
||||
.render();
|
||||
else if (toCategory == Type::Category::Address)
|
||||
body =
|
||||
Whiskers("converted := <convert>(value)")
|
||||
("convert", conversionFunction(_from, IntegerType(160)))
|
||||
.render();
|
||||
else
|
||||
{
|
||||
// clear for conversion to longer bytes
|
||||
solAssert(toCategory == Type::Category::FixedBytes, "Invalid type conversion requested.");
|
||||
body =
|
||||
Whiskers("converted := <clean>(value)")
|
||||
("clean", cleanupFunction(from))
|
||||
.render();
|
||||
}
|
||||
break;
|
||||
}
|
||||
case Type::Category::Function:
|
||||
{
|
||||
solAssert(false, "Conversion should not be called for function types.");
|
||||
break;
|
||||
}
|
||||
case Type::Category::Enum:
|
||||
{
|
||||
solAssert(toCategory == Type::Category::Integer || _from == _to, "");
|
||||
EnumType const& enumType = dynamic_cast<decltype(enumType)>(_from);
|
||||
body =
|
||||
Whiskers("converted := <clean>(value)")
|
||||
("clean", cleanupFunction(enumType))
|
||||
.render();
|
||||
break;
|
||||
}
|
||||
case Type::Category::Tuple:
|
||||
{
|
||||
solUnimplementedAssert(false, "Tuple conversion not implemented.");
|
||||
break;
|
||||
}
|
||||
default:
|
||||
solAssert(false, "");
|
||||
}
|
||||
|
||||
solAssert(!body.empty(), _from.canonicalName() + " to " + _to.canonicalName());
|
||||
templ("body", body);
|
||||
return templ.render();
|
||||
});
|
||||
}
|
||||
|
||||
string ABIFunctions::abiEncodingFunction(
|
||||
Type const& _from,
|
||||
Type const& _to,
|
||||
@ -678,9 +376,9 @@ string ABIFunctions::abiEncodingFunction(
|
||||
{
|
||||
string cleanupConvert;
|
||||
if (_from == to)
|
||||
cleanupConvert = cleanupFunction(_from) + "(value)";
|
||||
cleanupConvert = m_utils.cleanupFunction(_from) + "(value)";
|
||||
else
|
||||
cleanupConvert = conversionFunction(_from, to) + "(value)";
|
||||
cleanupConvert = m_utils.conversionFunction(_from, to) + "(value)";
|
||||
if (!_options.padded)
|
||||
cleanupConvert = m_utils.leftAlignFunction(to) + "(" + cleanupConvert + ")";
|
||||
templ("cleanupConvert", cleanupConvert);
|
||||
@ -1365,7 +1063,7 @@ string ABIFunctions::abiEncodingFunctionFunctionType(
|
||||
}
|
||||
)")
|
||||
("functionName", functionName)
|
||||
("cleanExtFun", cleanupFunction(_to))
|
||||
("cleanExtFun", m_utils.cleanupFunction(_to))
|
||||
.render();
|
||||
});
|
||||
}
|
||||
@ -1431,7 +1129,7 @@ string ABIFunctions::abiDecodingFunctionValueType(Type const& _type, bool _fromM
|
||||
templ("load", _fromMemory ? "mload" : "calldataload");
|
||||
// Validation should use the type and not decodingType, because e.g.
|
||||
// the decoding type of an enum is a plain int.
|
||||
templ("validator", validatorFunction(_type, true));
|
||||
templ("validator", m_utils.validatorFunction(_type, true));
|
||||
return templ.render();
|
||||
});
|
||||
|
||||
@ -1696,7 +1394,7 @@ string ABIFunctions::abiDecodingFunctionFunctionType(FunctionType const& _type,
|
||||
)")
|
||||
("functionName", functionName)
|
||||
("load", _fromMemory ? "mload" : "calldataload")
|
||||
("validateExtFun", validatorFunction(_type, true))
|
||||
("validateExtFun", m_utils.validatorFunction(_type, true))
|
||||
.render();
|
||||
}
|
||||
});
|
||||
|
@ -128,20 +128,6 @@ private:
|
||||
std::string toFunctionNameSuffix() const;
|
||||
};
|
||||
|
||||
/// @returns the name of the cleanup function for the given type and
|
||||
/// adds its implementation to the requested functions.
|
||||
/// The cleanup function defers to the validator function with "assert"
|
||||
/// if there is no reasonable way to clean a value.
|
||||
std::string cleanupFunction(Type const& _type);
|
||||
|
||||
/// @returns the name of the validator function for the given type and
|
||||
/// adds its implementation to the requested functions.
|
||||
/// @param _revertOnFailure if true, causes revert on invalid data,
|
||||
/// otherwise an assertion failure.
|
||||
///
|
||||
/// This is used for data decoded from external sources.
|
||||
std::string validatorFunction(Type const& _type, bool _revertOnFailure = false);
|
||||
|
||||
/// Performs cleanup after reading from a potentially compressed storage slot.
|
||||
/// The function does not perform any validation, it just masks or sign-extends
|
||||
/// higher order bytes or left-aligns (in case of bytesNN).
|
||||
@ -151,13 +137,6 @@ private:
|
||||
/// single variable.
|
||||
std::string cleanupFromStorageFunction(Type const& _type, bool _splitFunctionTypes);
|
||||
|
||||
/// @returns the name of the function that converts a value of type @a _from
|
||||
/// to a value of type @a _to. The resulting vale is guaranteed to be in range
|
||||
/// (i.e. "clean"). Asserts on failure.
|
||||
///
|
||||
/// This is used for data being encoded or general type conversions in the code.
|
||||
std::string conversionFunction(Type const& _from, Type const& _to);
|
||||
|
||||
/// @returns the name of the ABI encoding function with the given type
|
||||
/// and queues the generation of the function to the requested functions.
|
||||
/// @param _fromStack if false, the input value was just loaded from storage
|
||||
|
@ -450,6 +450,308 @@ string YulUtilFunctions::allocationFunction()
|
||||
});
|
||||
}
|
||||
|
||||
string YulUtilFunctions::conversionFunction(Type const& _from, Type const& _to)
|
||||
{
|
||||
string functionName =
|
||||
"convert_" +
|
||||
_from.identifier() +
|
||||
"_to_" +
|
||||
_to.identifier();
|
||||
return m_functionCollector->createFunction(functionName, [&]() {
|
||||
Whiskers templ(R"(
|
||||
function <functionName>(value) -> converted {
|
||||
<body>
|
||||
}
|
||||
)");
|
||||
templ("functionName", functionName);
|
||||
string body;
|
||||
auto toCategory = _to.category();
|
||||
auto fromCategory = _from.category();
|
||||
switch (fromCategory)
|
||||
{
|
||||
case Type::Category::Address:
|
||||
body =
|
||||
Whiskers("converted := <convert>(value)")
|
||||
("convert", conversionFunction(IntegerType(160), _to))
|
||||
.render();
|
||||
break;
|
||||
case Type::Category::Integer:
|
||||
case Type::Category::RationalNumber:
|
||||
case Type::Category::Contract:
|
||||
{
|
||||
if (RationalNumberType const* rational = dynamic_cast<RationalNumberType const*>(&_from))
|
||||
solUnimplementedAssert(!rational->isFractional(), "Not yet implemented - FixedPointType.");
|
||||
if (toCategory == Type::Category::FixedBytes)
|
||||
{
|
||||
solAssert(
|
||||
fromCategory == Type::Category::Integer || fromCategory == Type::Category::RationalNumber,
|
||||
"Invalid conversion to FixedBytesType requested."
|
||||
);
|
||||
FixedBytesType const& toBytesType = dynamic_cast<FixedBytesType const&>(_to);
|
||||
body =
|
||||
Whiskers("converted := <shiftLeft>(<clean>(value))")
|
||||
("shiftLeft", shiftLeftFunction(256 - toBytesType.numBytes() * 8))
|
||||
("clean", cleanupFunction(_from))
|
||||
.render();
|
||||
}
|
||||
else if (toCategory == Type::Category::Enum)
|
||||
{
|
||||
solAssert(_from.mobileType(), "");
|
||||
body =
|
||||
Whiskers("converted := <cleanEnum>(<cleanInt>(value))")
|
||||
("cleanEnum", cleanupFunction(_to))
|
||||
// "mobileType()" returns integer type for rational
|
||||
("cleanInt", cleanupFunction(*_from.mobileType()))
|
||||
.render();
|
||||
}
|
||||
else if (toCategory == Type::Category::FixedPoint)
|
||||
solUnimplemented("Not yet implemented - FixedPointType.");
|
||||
else if (toCategory == Type::Category::Address)
|
||||
body =
|
||||
Whiskers("converted := <convert>(value)")
|
||||
("convert", conversionFunction(_from, IntegerType(160)))
|
||||
.render();
|
||||
else
|
||||
{
|
||||
solAssert(
|
||||
toCategory == Type::Category::Integer ||
|
||||
toCategory == Type::Category::Contract,
|
||||
"");
|
||||
IntegerType const addressType(160);
|
||||
IntegerType const& to =
|
||||
toCategory == Type::Category::Integer ?
|
||||
dynamic_cast<IntegerType const&>(_to) :
|
||||
addressType;
|
||||
|
||||
// Clean according to the "to" type, except if this is
|
||||
// a widening conversion.
|
||||
IntegerType const* cleanupType = &to;
|
||||
if (fromCategory != Type::Category::RationalNumber)
|
||||
{
|
||||
IntegerType const& from =
|
||||
fromCategory == Type::Category::Integer ?
|
||||
dynamic_cast<IntegerType const&>(_from) :
|
||||
addressType;
|
||||
if (to.numBits() > from.numBits())
|
||||
cleanupType = &from;
|
||||
}
|
||||
body =
|
||||
Whiskers("converted := <cleanInt>(value)")
|
||||
("cleanInt", cleanupFunction(*cleanupType))
|
||||
.render();
|
||||
}
|
||||
break;
|
||||
}
|
||||
case Type::Category::Bool:
|
||||
{
|
||||
solAssert(_from == _to, "Invalid conversion for bool.");
|
||||
body =
|
||||
Whiskers("converted := <clean>(value)")
|
||||
("clean", cleanupFunction(_from))
|
||||
.render();
|
||||
break;
|
||||
}
|
||||
case Type::Category::FixedPoint:
|
||||
solUnimplemented("Fixed point types not implemented.");
|
||||
break;
|
||||
case Type::Category::Array:
|
||||
solUnimplementedAssert(false, "Array conversion not implemented.");
|
||||
break;
|
||||
case Type::Category::Struct:
|
||||
solUnimplementedAssert(false, "Struct conversion not implemented.");
|
||||
break;
|
||||
case Type::Category::FixedBytes:
|
||||
{
|
||||
FixedBytesType const& from = dynamic_cast<FixedBytesType const&>(_from);
|
||||
if (toCategory == Type::Category::Integer)
|
||||
body =
|
||||
Whiskers("converted := <convert>(<shift>(value))")
|
||||
("shift", shiftRightFunction(256 - from.numBytes() * 8))
|
||||
("convert", conversionFunction(IntegerType(from.numBytes() * 8), _to))
|
||||
.render();
|
||||
else if (toCategory == Type::Category::Address)
|
||||
body =
|
||||
Whiskers("converted := <convert>(value)")
|
||||
("convert", conversionFunction(_from, IntegerType(160)))
|
||||
.render();
|
||||
else
|
||||
{
|
||||
// clear for conversion to longer bytes
|
||||
solAssert(toCategory == Type::Category::FixedBytes, "Invalid type conversion requested.");
|
||||
body =
|
||||
Whiskers("converted := <clean>(value)")
|
||||
("clean", cleanupFunction(from))
|
||||
.render();
|
||||
}
|
||||
break;
|
||||
}
|
||||
case Type::Category::Function:
|
||||
{
|
||||
solAssert(false, "Conversion should not be called for function types.");
|
||||
break;
|
||||
}
|
||||
case Type::Category::Enum:
|
||||
{
|
||||
solAssert(toCategory == Type::Category::Integer || _from == _to, "");
|
||||
EnumType const& enumType = dynamic_cast<decltype(enumType)>(_from);
|
||||
body =
|
||||
Whiskers("converted := <clean>(value)")
|
||||
("clean", cleanupFunction(enumType))
|
||||
.render();
|
||||
break;
|
||||
}
|
||||
case Type::Category::Tuple:
|
||||
{
|
||||
solUnimplementedAssert(false, "Tuple conversion not implemented.");
|
||||
break;
|
||||
}
|
||||
default:
|
||||
solAssert(false, "");
|
||||
}
|
||||
|
||||
solAssert(!body.empty(), _from.canonicalName() + " to " + _to.canonicalName());
|
||||
templ("body", body);
|
||||
return templ.render();
|
||||
});
|
||||
}
|
||||
|
||||
string YulUtilFunctions::cleanupFunction(Type const& _type)
|
||||
{
|
||||
string functionName = string("cleanup_") + _type.identifier();
|
||||
return m_functionCollector->createFunction(functionName, [&]() {
|
||||
Whiskers templ(R"(
|
||||
function <functionName>(value) -> cleaned {
|
||||
<body>
|
||||
}
|
||||
)");
|
||||
templ("functionName", functionName);
|
||||
switch (_type.category())
|
||||
{
|
||||
case Type::Category::Address:
|
||||
templ("body", "cleaned := " + cleanupFunction(IntegerType(160)) + "(value)");
|
||||
break;
|
||||
case Type::Category::Integer:
|
||||
{
|
||||
IntegerType const& type = dynamic_cast<IntegerType const&>(_type);
|
||||
if (type.numBits() == 256)
|
||||
templ("body", "cleaned := value");
|
||||
else if (type.isSigned())
|
||||
templ("body", "cleaned := signextend(" + to_string(type.numBits() / 8 - 1) + ", value)");
|
||||
else
|
||||
templ("body", "cleaned := and(value, " + toCompactHexWithPrefix((u256(1) << type.numBits()) - 1) + ")");
|
||||
break;
|
||||
}
|
||||
case Type::Category::RationalNumber:
|
||||
templ("body", "cleaned := value");
|
||||
break;
|
||||
case Type::Category::Bool:
|
||||
templ("body", "cleaned := iszero(iszero(value))");
|
||||
break;
|
||||
case Type::Category::FixedPoint:
|
||||
solUnimplemented("Fixed point types not implemented.");
|
||||
break;
|
||||
case Type::Category::Function:
|
||||
solAssert(dynamic_cast<FunctionType const&>(_type).kind() == FunctionType::Kind::External, "");
|
||||
templ("body", "cleaned := " + cleanupFunction(FixedBytesType(24)) + "(value)");
|
||||
break;
|
||||
case Type::Category::Array:
|
||||
case Type::Category::Struct:
|
||||
case Type::Category::Mapping:
|
||||
solAssert(_type.dataStoredIn(DataLocation::Storage), "Cleanup requested for non-storage reference type.");
|
||||
templ("body", "cleaned := value");
|
||||
break;
|
||||
case Type::Category::FixedBytes:
|
||||
{
|
||||
FixedBytesType const& type = dynamic_cast<FixedBytesType const&>(_type);
|
||||
if (type.numBytes() == 32)
|
||||
templ("body", "cleaned := value");
|
||||
else if (type.numBytes() == 0)
|
||||
// This is disallowed in the type system.
|
||||
solAssert(false, "");
|
||||
else
|
||||
{
|
||||
size_t numBits = type.numBytes() * 8;
|
||||
u256 mask = ((u256(1) << numBits) - 1) << (256 - numBits);
|
||||
templ("body", "cleaned := and(value, " + toCompactHexWithPrefix(mask) + ")");
|
||||
}
|
||||
break;
|
||||
}
|
||||
case Type::Category::Contract:
|
||||
{
|
||||
AddressType addressType(dynamic_cast<ContractType const&>(_type).isPayable() ?
|
||||
StateMutability::Payable :
|
||||
StateMutability::NonPayable
|
||||
);
|
||||
templ("body", "cleaned := " + cleanupFunction(addressType) + "(value)");
|
||||
break;
|
||||
}
|
||||
case Type::Category::Enum:
|
||||
{
|
||||
// Out of range enums cannot be truncated unambigiously and therefore it should be an error.
|
||||
templ("body", "cleaned := value " + validatorFunction(_type) + "(value)");
|
||||
break;
|
||||
}
|
||||
case Type::Category::InaccessibleDynamic:
|
||||
templ("body", "cleaned := 0");
|
||||
break;
|
||||
default:
|
||||
solAssert(false, "Cleanup of type " + _type.identifier() + " requested.");
|
||||
}
|
||||
|
||||
return templ.render();
|
||||
});
|
||||
}
|
||||
|
||||
string YulUtilFunctions::validatorFunction(Type const& _type, bool _revertOnFailure)
|
||||
{
|
||||
string functionName = string("validator_") + (_revertOnFailure ? "revert_" : "assert_") + _type.identifier();
|
||||
return m_functionCollector->createFunction(functionName, [&]() {
|
||||
Whiskers templ(R"(
|
||||
function <functionName>(value) {
|
||||
if iszero(<condition>) { <failure> }
|
||||
}
|
||||
)");
|
||||
templ("functionName", functionName);
|
||||
if (_revertOnFailure)
|
||||
templ("failure", "revert(0, 0)");
|
||||
else
|
||||
templ("failure", "invalid()");
|
||||
|
||||
switch (_type.category())
|
||||
{
|
||||
case Type::Category::Address:
|
||||
case Type::Category::Integer:
|
||||
case Type::Category::RationalNumber:
|
||||
case Type::Category::Bool:
|
||||
case Type::Category::FixedPoint:
|
||||
case Type::Category::Function:
|
||||
case Type::Category::Array:
|
||||
case Type::Category::Struct:
|
||||
case Type::Category::Mapping:
|
||||
case Type::Category::FixedBytes:
|
||||
case Type::Category::Contract:
|
||||
{
|
||||
templ("condition", "eq(value, " + cleanupFunction(_type) + "(value))");
|
||||
break;
|
||||
}
|
||||
case Type::Category::Enum:
|
||||
{
|
||||
size_t members = dynamic_cast<EnumType const&>(_type).numberOfMembers();
|
||||
solAssert(members > 0, "empty enum should have caused a parser error.");
|
||||
templ("condition", "lt(value, " + to_string(members) + ")");
|
||||
break;
|
||||
}
|
||||
case Type::Category::InaccessibleDynamic:
|
||||
templ("condition", "1");
|
||||
break;
|
||||
default:
|
||||
solAssert(false, "Validation of type " + _type.identifier() + " requested.");
|
||||
}
|
||||
|
||||
return templ.render();
|
||||
});
|
||||
}
|
||||
|
||||
string YulUtilFunctions::suffixedVariableNameList(string const& _baseName, size_t _startSuffix, size_t _endSuffix)
|
||||
{
|
||||
string result;
|
||||
|
@ -92,6 +92,27 @@ public:
|
||||
/// Return value: pointer
|
||||
std::string allocationFunction();
|
||||
|
||||
/// @returns the name of the function that converts a value of type @a _from
|
||||
/// to a value of type @a _to. The resulting vale is guaranteed to be in range
|
||||
/// (i.e. "clean"). Asserts on failure.
|
||||
///
|
||||
/// This is used for data being encoded or general type conversions in the code.
|
||||
std::string conversionFunction(Type const& _from, Type const& _to);
|
||||
|
||||
/// @returns the name of the cleanup function for the given type and
|
||||
/// adds its implementation to the requested functions.
|
||||
/// The cleanup function defers to the validator function with "assert"
|
||||
/// if there is no reasonable way to clean a value.
|
||||
std::string cleanupFunction(Type const& _type);
|
||||
|
||||
/// @returns the name of the validator function for the given type and
|
||||
/// adds its implementation to the requested functions.
|
||||
/// @param _revertOnFailure if true, causes revert on invalid data,
|
||||
/// otherwise an assertion failure.
|
||||
///
|
||||
/// This is used for data decoded from external sources.
|
||||
std::string validatorFunction(Type const& _type, bool _revertOnFailure = false);
|
||||
|
||||
/// @returns a string containing a comma-separated list of variable names consisting of @a _baseName suffixed
|
||||
/// with increasing integers in the range [@a _startSuffix, @a _endSuffix), if @a _startSuffix < @a _endSuffix,
|
||||
/// and with decreasing integers in the range [@a _endSuffix, @a _startSuffix), if @a _endSuffix < @a _startSuffix.
|
||||
|
Loading…
Reference in New Issue
Block a user