mirror of
https://github.com/ethereum/solidity
synced 2023-10-03 13:03:40 +00:00
Codegen for catch.
This commit is contained in:
parent
6fd0cf547e
commit
7aa7e708f9
@ -190,13 +190,15 @@ string ABIFunctions::tupleEncoderPacked(
|
||||
return templ.render();
|
||||
});
|
||||
}
|
||||
string ABIFunctions::tupleDecoder(TypePointers const& _types, bool _fromMemory)
|
||||
string ABIFunctions::tupleDecoder(TypePointers const& _types, OnError _onError, bool _fromMemory)
|
||||
{
|
||||
string functionName = string("abi_decode_tuple_");
|
||||
for (auto const& t: _types)
|
||||
functionName += t->identifier();
|
||||
if (_fromMemory)
|
||||
functionName += "_fromMemory";
|
||||
if (_onError == OnError::ReturnFalse)
|
||||
functionName += "_try";
|
||||
|
||||
return createFunction(functionName, [&]() {
|
||||
TypePointers decodingTypes;
|
||||
@ -204,17 +206,25 @@ string ABIFunctions::tupleDecoder(TypePointers const& _types, bool _fromMemory)
|
||||
decodingTypes.emplace_back(t->decodingType());
|
||||
|
||||
Whiskers templ(R"(
|
||||
function <functionName>(headStart, dataEnd) <arrow> <valueReturnParams> {
|
||||
function <functionName>(headStart, dataEnd) <+returnParams> -> <returnParams> </+returnParams> {
|
||||
if slt(sub(dataEnd, headStart), <minimumSize>) { <revertString> }
|
||||
<?try> success := 1 </try>
|
||||
<decodeElements>
|
||||
}
|
||||
)");
|
||||
templ("functionName", functionName);
|
||||
templ("revertString", revertReasonIfDebug("ABI decoding: tuple data too short"));
|
||||
templ("try", _onError == OnError::ReturnFalse);
|
||||
if (_onError == OnError::ReturnFalse)
|
||||
// TODO rename
|
||||
templ("revertString", "success := 0 leave");
|
||||
else
|
||||
templ("revertString", revertReasonIfDebug("ABI decoding: tuple data too short"));
|
||||
templ("minimumSize", to_string(headSize(decodingTypes)));
|
||||
|
||||
string decodeElements;
|
||||
vector<string> valueReturnParams;
|
||||
if (_onError == OnError::ReturnFalse)
|
||||
valueReturnParams.emplace_back("success");
|
||||
size_t headPos = 0;
|
||||
size_t stackPos = 0;
|
||||
for (size_t i = 0; i < _types.size(); ++i)
|
||||
@ -225,6 +235,8 @@ string ABIFunctions::tupleDecoder(TypePointers const& _types, bool _fromMemory)
|
||||
solAssert(sizeOnStack == decodingTypes[i]->sizeOnStack(), "");
|
||||
solAssert(sizeOnStack > 0, "");
|
||||
vector<string> valueNamesLocal;
|
||||
if (_onError == OnError::ReturnFalse)
|
||||
valueNamesLocal.push_back("success");
|
||||
for (size_t j = 0; j < sizeOnStack; j++)
|
||||
{
|
||||
valueNamesLocal.emplace_back("value" + to_string(stackPos));
|
||||
@ -240,15 +252,22 @@ string ABIFunctions::tupleDecoder(TypePointers const& _types, bool _fromMemory)
|
||||
let offset := <pos>
|
||||
</dynamic>
|
||||
<values> := <abiDecode>(add(headStart, offset), dataEnd)
|
||||
<?try> if iszero(success) { leave } </try>
|
||||
}
|
||||
)");
|
||||
elementTempl("dynamic", decodingTypes[i]->isDynamicallyEncoded());
|
||||
// TODO add test
|
||||
elementTempl("revertString", revertReasonIfDebug("ABI decoding: invalid tuple offset"));
|
||||
if (_onError == OnError::ReturnFalse)
|
||||
// TODO rename
|
||||
elementTempl("revertString", "success := 0 leave");
|
||||
else
|
||||
elementTempl("revertString", revertReasonIfDebug("ABI decoding: invalid tuple offset"));
|
||||
elementTempl("load", _fromMemory ? "mload" : "calldataload");
|
||||
// TODO assign success
|
||||
elementTempl("values", boost::algorithm::join(valueNamesLocal, ", "));
|
||||
elementTempl("pos", to_string(headPos));
|
||||
elementTempl("abiDecode", abiDecodingFunction(*_types[i], _fromMemory, true));
|
||||
elementTempl("try", _onError == OnError::ReturnFalse);
|
||||
decodeElements += elementTempl.render();
|
||||
headPos += decodingTypes[i]->calldataHeadSize();
|
||||
}
|
||||
@ -1062,7 +1081,7 @@ string ABIFunctions::abiEncodingFunctionFunctionType(
|
||||
});
|
||||
}
|
||||
|
||||
string ABIFunctions::abiDecodingFunction(Type const& _type, bool _fromMemory, bool _forUseOnStack)
|
||||
string ABIFunctions::abiDecodingFunction(Type const& _type, OnError _onError, bool _fromMemory, bool _forUseOnStack)
|
||||
{
|
||||
// The decoding function has to perform bounds checks unless it decodes a value type.
|
||||
// Conversely, bounds checks have to be performed before the decoding function
|
||||
@ -1076,28 +1095,28 @@ string ABIFunctions::abiDecodingFunction(Type const& _type, bool _fromMemory, bo
|
||||
if (arrayType->dataStoredIn(DataLocation::CallData))
|
||||
{
|
||||
solAssert(!_fromMemory, "");
|
||||
return abiDecodingFunctionCalldataArray(*arrayType);
|
||||
return abiDecodingFunctionCalldataArray(*arrayType, _onError);
|
||||
}
|
||||
else
|
||||
return abiDecodingFunctionArray(*arrayType, _fromMemory);
|
||||
return abiDecodingFunctionArray(*arrayType, _onError, _fromMemory);
|
||||
}
|
||||
else if (auto const* structType = dynamic_cast<StructType const*>(decodingType))
|
||||
{
|
||||
if (structType->dataStoredIn(DataLocation::CallData))
|
||||
{
|
||||
solAssert(!_fromMemory, "");
|
||||
return abiDecodingFunctionCalldataStruct(*structType);
|
||||
return abiDecodingFunctionCalldataStruct(*structType, _onError);
|
||||
}
|
||||
else
|
||||
return abiDecodingFunctionStruct(*structType, _fromMemory);
|
||||
return abiDecodingFunctionStruct(*structType, _onError, _fromMemory);
|
||||
}
|
||||
else if (auto const* functionType = dynamic_cast<FunctionType const*>(decodingType))
|
||||
return abiDecodingFunctionFunctionType(*functionType, _fromMemory, _forUseOnStack);
|
||||
return abiDecodingFunctionFunctionType(*functionType, _onError, _fromMemory, _forUseOnStack);
|
||||
else
|
||||
return abiDecodingFunctionValueType(_type, _fromMemory);
|
||||
return abiDecodingFunctionValueType(_type, _onError, _fromMemory);
|
||||
}
|
||||
|
||||
string ABIFunctions::abiDecodingFunctionValueType(Type const& _type, bool _fromMemory)
|
||||
string ABIFunctions::abiDecodingFunctionValueType(Type const& _type, OnError _onError, bool _fromMemory)
|
||||
{
|
||||
TypePointer decodingType = _type.decodingType();
|
||||
solAssert(decodingType, "");
|
||||
@ -1109,12 +1128,13 @@ string ABIFunctions::abiDecodingFunctionValueType(Type const& _type, bool _fromM
|
||||
string functionName =
|
||||
"abi_decode_" +
|
||||
_type.identifier() +
|
||||
(_fromMemory ? "_fromMemory" : "");
|
||||
(_fromMemory ? "_fromMemory" : "") +
|
||||
(_onError == OnError::ReturnFalse ? "_try" : "");
|
||||
return createFunction(functionName, [&]() {
|
||||
Whiskers templ(R"(
|
||||
function <functionName>(offset, end) -> value {
|
||||
function <functionName>(offset, end) -> <?try> success, </try> value {
|
||||
value := <load>(offset)
|
||||
<validator>(value)
|
||||
<?try> success := </try> <validator>(value)
|
||||
}
|
||||
)");
|
||||
templ("functionName", functionName);
|
||||
@ -1122,58 +1142,65 @@ string ABIFunctions::abiDecodingFunctionValueType(Type const& _type, bool _fromM
|
||||
// Validation should use the type and not decodingType, because e.g.
|
||||
// the decoding type of an enum is a plain int.
|
||||
templ("validator", m_utils.validatorFunction(_type, true));
|
||||
// TODO extend the validator to retrun success
|
||||
return templ.render();
|
||||
});
|
||||
|
||||
}
|
||||
|
||||
string ABIFunctions::abiDecodingFunctionArray(ArrayType const& _type, bool _fromMemory)
|
||||
string ABIFunctions::abiDecodingFunctionArray(ArrayType const& _type, OnError _onError, bool _fromMemory)
|
||||
{
|
||||
solAssert(_type.dataStoredIn(DataLocation::Memory), "");
|
||||
|
||||
string functionName =
|
||||
"abi_decode_" +
|
||||
_type.identifier() +
|
||||
(_fromMemory ? "_fromMemory" : "");
|
||||
(_fromMemory ? "_fromMemory" : "") +
|
||||
(_onError == OnError::ReturnFalse ? "_try" : "");
|
||||
|
||||
return createFunction(functionName, [&]() {
|
||||
string load = _fromMemory ? "mload" : "calldataload";
|
||||
Whiskers templ(
|
||||
R"(
|
||||
// <readableTypeName>
|
||||
function <functionName>(offset, end) -> array {
|
||||
function <functionName>(offset, end) -> <?try> success, </try> array {
|
||||
if iszero(slt(add(offset, 0x1f), end)) { <revertString> }
|
||||
let length := <retrieveLength>
|
||||
array := <abiDecodeAvailableLen>(<offset>, length, end)
|
||||
<?try> success, </try> array := <abiDecodeAvailableLen>(<offset>, length, end)
|
||||
}
|
||||
)"
|
||||
);
|
||||
// TODO add test
|
||||
templ("revertString", revertReasonIfDebug("ABI decoding: invalid calldata array offset"));
|
||||
if (_onError == OnError::ReturnFalse)
|
||||
// TODO rename
|
||||
templ("revertString", "success := 0 leave");
|
||||
else
|
||||
templ("revertString", revertReasonIfDebug("ABI decoding: invalid calldata array offset"));
|
||||
templ("functionName", functionName);
|
||||
templ("readableTypeName", _type.toString(true));
|
||||
templ("retrieveLength", _type.isDynamicallySized() ? (load + "(offset)") : toCompactHexWithPrefix(_type.length()));
|
||||
templ("offset", _type.isDynamicallySized() ? "add(offset, 0x20)" : "offset");
|
||||
templ("abiDecodeAvailableLen", abiDecodingFunctionArrayAvailableLength(_type, _fromMemory));
|
||||
templ("abiDecodeAvailableLen", abiDecodingFunctionArrayAvailableLength(_type, _onError, _fromMemory));
|
||||
return templ.render();
|
||||
});
|
||||
}
|
||||
|
||||
string ABIFunctions::abiDecodingFunctionArrayAvailableLength(ArrayType const& _type, bool _fromMemory)
|
||||
string ABIFunctions::abiDecodingFunctionArrayAvailableLength(ArrayType const& _type, OnError _onError, bool _fromMemory)
|
||||
{
|
||||
solAssert(_type.dataStoredIn(DataLocation::Memory), "");
|
||||
if (_type.isByteArray())
|
||||
return abiDecodingFunctionByteArrayAvailableLength(_type, _fromMemory);
|
||||
return abiDecodingFunctionByteArrayAvailableLength(_type, _onError, _fromMemory);
|
||||
|
||||
string functionName =
|
||||
"abi_decode_available_length_" +
|
||||
_type.identifier() +
|
||||
(_fromMemory ? "_fromMemory" : "");
|
||||
(_fromMemory ? "_fromMemory" : "") +
|
||||
(_onError == OnError::ReturnFalse ? "_try" : "");
|
||||
|
||||
return createFunction(functionName, [&]() {
|
||||
Whiskers templ(R"(
|
||||
// <readableTypeName>
|
||||
function <functionName>(offset, length, end) -> array {
|
||||
function <functionName>(offset, length, end) -> <?try> success, </try> array {
|
||||
array := <allocate>(<allocationSize>(length))
|
||||
let dst := array
|
||||
<storeLength>
|
||||
@ -1209,17 +1236,20 @@ string ABIFunctions::abiDecodingFunctionArrayAvailableLength(ArrayType const& _t
|
||||
templ("staticBoundsCheck", "if gt(add(src, mul(length, " +
|
||||
calldataStride +
|
||||
")), end) { " +
|
||||
revertReasonIfDebug("ABI decoding: invalid calldata array stride") +
|
||||
(
|
||||
_onError == OnError::ReturnFalse ? "success := 0 leave" :
|
||||
revertReasonIfDebug("ABI decoding: invalid calldata array stride")
|
||||
) +
|
||||
" }"
|
||||
);
|
||||
templ("retrieveElementPos", "src");
|
||||
}
|
||||
templ("decodingFun", abiDecodingFunction(*_type.baseType(), _fromMemory, false));
|
||||
templ("decodingFun", abiDecodingFunction(*_type.baseType(), _onError, _fromMemory, false));
|
||||
return templ.render();
|
||||
});
|
||||
}
|
||||
|
||||
string ABIFunctions::abiDecodingFunctionCalldataArray(ArrayType const& _type)
|
||||
string ABIFunctions::abiDecodingFunctionCalldataArray(ArrayType const& _type, OnError _onError)
|
||||
{
|
||||
solAssert(_type.dataStoredIn(DataLocation::CallData), "");
|
||||
if (!_type.isDynamicallySized())
|
||||
@ -1229,14 +1259,16 @@ string ABIFunctions::abiDecodingFunctionCalldataArray(ArrayType const& _type)
|
||||
|
||||
string functionName =
|
||||
"abi_decode_" +
|
||||
_type.identifier();
|
||||
_type.identifier() +
|
||||
(_onError == OnError::ReturnFalse ? "_try" : "");
|
||||
|
||||
return createFunction(functionName, [&]() {
|
||||
Whiskers w;
|
||||
if (_type.isDynamicallySized())
|
||||
{
|
||||
w = Whiskers(R"(
|
||||
// <readableTypeName>
|
||||
function <functionName>(offset, end) -> arrayPos, length {
|
||||
function <functionName>(offset, end) -> <+try> success, </try> arrayPos, length {
|
||||
if iszero(slt(add(offset, 0x1f), end)) { <revertStringOffset> }
|
||||
length := calldataload(offset)
|
||||
if gt(length, 0xffffffffffffffff) { <revertStringLength> }
|
||||
@ -1244,6 +1276,7 @@ string ABIFunctions::abiDecodingFunctionCalldataArray(ArrayType const& _type)
|
||||
if gt(add(arrayPos, mul(length, <stride>)), end) { <revertStringPos> }
|
||||
}
|
||||
)");
|
||||
// TODO modify to return failure
|
||||
w("revertStringOffset", revertReasonIfDebug("ABI decoding: invalid calldata array offset"));
|
||||
w("revertStringLength", revertReasonIfDebug("ABI decoding: invalid calldata array length"));
|
||||
}
|
||||
@ -1251,13 +1284,14 @@ string ABIFunctions::abiDecodingFunctionCalldataArray(ArrayType const& _type)
|
||||
{
|
||||
w = Whiskers(R"(
|
||||
// <readableTypeName>
|
||||
function <functionName>(offset, end) -> arrayPos {
|
||||
function <functionName>(offset, end) -> <+try> success, </try> arrayPos {
|
||||
arrayPos := offset
|
||||
if gt(add(arrayPos, mul(<length>, <stride>)), end) { <revertStringPos> }
|
||||
}
|
||||
)");
|
||||
w("length", toCompactHexWithPrefix(_type.length()));
|
||||
}
|
||||
// TODO modify to return failure
|
||||
w("revertStringPos", revertReasonIfDebug("ABI decoding: invalid calldata array stride"));
|
||||
w("functionName", functionName);
|
||||
w("readableTypeName", _type.toString(true));
|
||||
@ -1268,7 +1302,7 @@ string ABIFunctions::abiDecodingFunctionCalldataArray(ArrayType const& _type)
|
||||
});
|
||||
}
|
||||
|
||||
string ABIFunctions::abiDecodingFunctionByteArrayAvailableLength(ArrayType const& _type, bool _fromMemory)
|
||||
string ABIFunctions::abiDecodingFunctionByteArrayAvailableLength(ArrayType const& _type, OnError _onError, bool _fromMemory)
|
||||
{
|
||||
solAssert(_type.dataStoredIn(DataLocation::Memory), "");
|
||||
solAssert(_type.isByteArray(), "");
|
||||
@ -1276,11 +1310,12 @@ string ABIFunctions::abiDecodingFunctionByteArrayAvailableLength(ArrayType const
|
||||
string functionName =
|
||||
"abi_decode_available_length_" +
|
||||
_type.identifier() +
|
||||
(_fromMemory ? "_fromMemory" : "");
|
||||
(_fromMemory ? "_fromMemory" : "") +
|
||||
(_onError == OnError::ReturnFalse ? "_try" : "");
|
||||
|
||||
return createFunction(functionName, [&]() {
|
||||
Whiskers templ(R"(
|
||||
function <functionName>(src, length, end) -> array {
|
||||
function <functionName>(src, length, end) -> <?try> success, </try> array {
|
||||
array := <allocate>(<allocationSize>(length))
|
||||
mstore(array, length)
|
||||
let dst := add(array, 0x20)
|
||||
@ -1288,6 +1323,7 @@ string ABIFunctions::abiDecodingFunctionByteArrayAvailableLength(ArrayType const
|
||||
<copyToMemFun>(src, dst, length)
|
||||
}
|
||||
)");
|
||||
// TODO modify
|
||||
templ("revertStringLength", revertReasonIfDebug("ABI decoding: invalid byte array length"));
|
||||
templ("functionName", functionName);
|
||||
templ("allocate", m_utils.allocationFunction());
|
||||
@ -1297,22 +1333,24 @@ string ABIFunctions::abiDecodingFunctionByteArrayAvailableLength(ArrayType const
|
||||
});
|
||||
}
|
||||
|
||||
string ABIFunctions::abiDecodingFunctionCalldataStruct(StructType const& _type)
|
||||
string ABIFunctions::abiDecodingFunctionCalldataStruct(StructType const& _type, OnError _onError)
|
||||
{
|
||||
solAssert(_type.dataStoredIn(DataLocation::CallData), "");
|
||||
string functionName =
|
||||
"abi_decode_" +
|
||||
_type.identifier();
|
||||
_type.identifier() +
|
||||
(_onError == OnError::ReturnFalse ? "_try" : "");
|
||||
|
||||
return createFunction(functionName, [&]() {
|
||||
Whiskers w{R"(
|
||||
// <readableTypeName>
|
||||
function <functionName>(offset, end) -> value {
|
||||
function <functionName>(offset, end) -> <+try> success, </try> value {
|
||||
if slt(sub(end, offset), <minimumSize>) { <revertString> }
|
||||
value := offset
|
||||
}
|
||||
)"};
|
||||
// TODO add test
|
||||
// TODO modfy
|
||||
w("revertString", revertReasonIfDebug("ABI decoding: struct calldata too short"));
|
||||
w("functionName", functionName);
|
||||
w("readableTypeName", _type.toString(true));
|
||||
@ -1321,18 +1359,19 @@ string ABIFunctions::abiDecodingFunctionCalldataStruct(StructType const& _type)
|
||||
});
|
||||
}
|
||||
|
||||
string ABIFunctions::abiDecodingFunctionStruct(StructType const& _type, bool _fromMemory)
|
||||
string ABIFunctions::abiDecodingFunctionStruct(StructType const& _type, OnError _onError, bool _fromMemory)
|
||||
{
|
||||
solAssert(!_type.dataStoredIn(DataLocation::CallData), "");
|
||||
string functionName =
|
||||
"abi_decode_" +
|
||||
_type.identifier() +
|
||||
(_fromMemory ? "_fromMemory" : "");
|
||||
(_fromMemory ? "_fromMemory" : "") +
|
||||
(_onError == OnError::ReturnFalse ? "_try" : "");
|
||||
|
||||
return createFunction(functionName, [&]() {
|
||||
Whiskers templ(R"(
|
||||
// <readableTypeName>
|
||||
function <functionName>(headStart, end) -> value {
|
||||
function <functionName>(headStart, end) -> <?try> success, </try> value {
|
||||
if slt(sub(end, headStart), <minimumSize>) { <revertString> }
|
||||
value := <allocate>(<memorySize>)
|
||||
<#members>
|
||||
@ -1344,6 +1383,7 @@ string ABIFunctions::abiDecodingFunctionStruct(StructType const& _type, bool _fr
|
||||
}
|
||||
)");
|
||||
// TODO add test
|
||||
// TODO modify
|
||||
templ("revertString", revertReasonIfDebug("ABI decoding: struct data too short"));
|
||||
templ("functionName", functionName);
|
||||
templ("readableTypeName", _type.toString(true));
|
||||
@ -1365,10 +1405,12 @@ string ABIFunctions::abiDecodingFunctionStruct(StructType const& _type, bool _fr
|
||||
<!dynamic>
|
||||
let offset := <pos>
|
||||
</dynamic>
|
||||
// TODO outline and leave on error
|
||||
mstore(add(value, <memoryOffset>), <abiDecode>(add(headStart, offset), end))
|
||||
)");
|
||||
memberTempl("dynamic", decodingType->isDynamicallyEncoded());
|
||||
// TODO add test
|
||||
// TODO modify
|
||||
memberTempl("revertString", revertReasonIfDebug("ABI decoding: invalid struct offset"));
|
||||
memberTempl("load", _fromMemory ? "mload" : "calldataload");
|
||||
memberTempl("pos", to_string(headPos));
|
||||
@ -1386,7 +1428,7 @@ string ABIFunctions::abiDecodingFunctionStruct(StructType const& _type, bool _fr
|
||||
});
|
||||
}
|
||||
|
||||
string ABIFunctions::abiDecodingFunctionFunctionType(FunctionType const& _type, bool _fromMemory, bool _forUseOnStack)
|
||||
string ABIFunctions::abiDecodingFunctionFunctionType(FunctionType const& _type, OnError _onError, bool _fromMemory, bool _forUseOnStack)
|
||||
{
|
||||
solAssert(_type.kind() == FunctionType::Kind::External, "");
|
||||
|
||||
@ -1394,13 +1436,15 @@ string ABIFunctions::abiDecodingFunctionFunctionType(FunctionType const& _type,
|
||||
"abi_decode_" +
|
||||
_type.identifier() +
|
||||
(_fromMemory ? "_fromMemory" : "") +
|
||||
(_forUseOnStack ? "_onStack" : "");
|
||||
(_forUseOnStack ? "_onStack" : "") +
|
||||
(_onError == OnError::ReturnFalse ? "_try" : "");
|
||||
|
||||
return createFunction(functionName, [&]() {
|
||||
if (_forUseOnStack)
|
||||
{
|
||||
return Whiskers(R"(
|
||||
function <functionName>(offset, end) -> addr, function_selector {
|
||||
function <functionName>(offset, end) -> <?try> failure, </try> addr, function_selector {
|
||||
// TODO split
|
||||
addr, function_selector := <splitExtFun>(<decodeFun>(offset, end))
|
||||
}
|
||||
)")
|
||||
@ -1412,7 +1456,7 @@ string ABIFunctions::abiDecodingFunctionFunctionType(FunctionType const& _type,
|
||||
else
|
||||
{
|
||||
return Whiskers(R"(
|
||||
function <functionName>(offset, end) -> fun {
|
||||
function <functionName>(offset, end) -> <?try> failure, </try> fun {
|
||||
fun := <load>(offset)
|
||||
<validateExtFun>(fun)
|
||||
}
|
||||
@ -1425,17 +1469,18 @@ string ABIFunctions::abiDecodingFunctionFunctionType(FunctionType const& _type,
|
||||
});
|
||||
}
|
||||
|
||||
string ABIFunctions::calldataAccessFunction(Type const& _type)
|
||||
string ABIFunctions::calldataAccessFunction(Type const& _type, OnError _onError)
|
||||
{
|
||||
solAssert(_type.isValueType() || _type.dataStoredIn(DataLocation::CallData), "");
|
||||
string functionName = "calldata_access_" + _type.identifier();
|
||||
string functionName = "calldata_access_" + _type.identifier() + (_onError == OnError::ReturnFalse ? "_try" : "");
|
||||
|
||||
return createFunction(functionName, [&]() {
|
||||
if (_type.isDynamicallyEncoded())
|
||||
{
|
||||
unsigned int tailSize = _type.calldataEncodedTailSize();
|
||||
solAssert(tailSize > 1, "");
|
||||
Whiskers w(R"(
|
||||
function <functionName>(base_ref, ptr) -> <return> {
|
||||
function <functionName>(base_ref, ptr) -> <?try> failure, </try> <return> {
|
||||
let rel_offset_of_tail := calldataload(ptr)
|
||||
if iszero(slt(rel_offset_of_tail, sub(sub(calldatasize(), base_ref), sub(<neededLength>, 1)))) { <revertStringOffset> }
|
||||
value := add(rel_offset_of_tail, base_ref)
|
||||
@ -1454,8 +1499,10 @@ string ABIFunctions::calldataAccessFunction(Type const& _type)
|
||||
)")
|
||||
("calldataStride", toCompactHexWithPrefix(arrayType->calldataStride()))
|
||||
// TODO add test
|
||||
// TODO modify
|
||||
("revertStringLength", revertReasonIfDebug("Invalid calldata access length"))
|
||||
// TODO add test
|
||||
// TODO modify
|
||||
("revertStringStride", revertReasonIfDebug("Invalid calldata access stride"))
|
||||
.render());
|
||||
w("return", "value, length");
|
||||
@ -1467,6 +1514,7 @@ string ABIFunctions::calldataAccessFunction(Type const& _type)
|
||||
}
|
||||
w("neededLength", toCompactHexWithPrefix(tailSize));
|
||||
w("functionName", functionName);
|
||||
// TODO modify
|
||||
w("revertStringOffset", revertReasonIfDebug("Invalid calldata access offset"));
|
||||
return w.render();
|
||||
}
|
||||
@ -1484,6 +1532,7 @@ string ABIFunctions::calldataAccessFunction(Type const& _type)
|
||||
}
|
||||
)")
|
||||
("functionName", functionName)
|
||||
// TODO modify
|
||||
("decodingFunction", decodingFunction)
|
||||
.render();
|
||||
}
|
||||
@ -1494,6 +1543,7 @@ string ABIFunctions::calldataAccessFunction(Type const& _type)
|
||||
_type.category() == Type::Category::Struct,
|
||||
""
|
||||
);
|
||||
// TODO modify
|
||||
return Whiskers(R"(
|
||||
function <functionName>(baseRef, ptr) -> value {
|
||||
value := ptr
|
||||
|
@ -116,6 +116,8 @@ public:
|
||||
return tupleEncoderPacked(_givenTypes, _targetTypes, true);
|
||||
}
|
||||
|
||||
enum OnError { Revert, ReturnFalse };
|
||||
|
||||
/// @returns name of an assembly function to ABI-decode values of @a _types
|
||||
/// into memory. If @a _fromMemory is true, decodes from memory instead of
|
||||
/// from calldata.
|
||||
@ -124,7 +126,7 @@ public:
|
||||
/// Outputs: <value0> <value1> ... <valuen>
|
||||
/// The values represent stack slots. If a type occupies more or less than one
|
||||
/// stack slot, it takes exactly that number of values.
|
||||
std::string tupleDecoder(TypePointers const& _types, bool _fromMemory = false);
|
||||
std::string tupleDecoder(TypePointers const& _types, OnError _onError, bool _fromMemory = false);
|
||||
|
||||
struct EncodingOptions
|
||||
{
|
||||
@ -168,12 +170,12 @@ public:
|
||||
/// Decodes array in case of dynamic arrays with offset pointing to
|
||||
/// data and length already on stack
|
||||
/// signature: (dataOffset, length, dataEnd) -> decodedArray
|
||||
std::string abiDecodingFunctionArrayAvailableLength(ArrayType const& _type, bool _fromMemory);
|
||||
std::string abiDecodingFunctionArrayAvailableLength(ArrayType const& _type, OnError _onError, bool _fromMemory);
|
||||
|
||||
/// Internal decoding function that is also used by some copying routines.
|
||||
/// @returns the name of a function that decodes structs.
|
||||
/// signature: (dataStart, dataEnd) -> decodedStruct
|
||||
std::string abiDecodingFunctionStruct(StructType const& _type, bool _fromMemory);
|
||||
std::string abiDecodingFunctionStruct(StructType const& _type, OnError _onError, bool _fromMemory);
|
||||
|
||||
private:
|
||||
/// Part of @a abiEncodingFunction for array target type and given calldata array.
|
||||
@ -239,17 +241,17 @@ private:
|
||||
);
|
||||
|
||||
/// Part of @a abiDecodingFunction for value types.
|
||||
std::string abiDecodingFunctionValueType(Type const& _type, bool _fromMemory);
|
||||
std::string abiDecodingFunctionValueType(Type const& _type, OnError _onError, bool _fromMemory);
|
||||
/// Part of @a abiDecodingFunction for "regular" array types.
|
||||
std::string abiDecodingFunctionArray(ArrayType const& _type, bool _fromMemory);
|
||||
std::string abiDecodingFunctionArray(ArrayType const& _type, OnError _onError, bool _fromMemory);
|
||||
/// Part of @a abiDecodingFunction for calldata array types.
|
||||
std::string abiDecodingFunctionCalldataArray(ArrayType const& _type);
|
||||
std::string abiDecodingFunctionCalldataArray(ArrayType const& _type, OnError _onError);
|
||||
/// Part of @a abiDecodingFunctionArrayWithAvailableLength
|
||||
std::string abiDecodingFunctionByteArrayAvailableLength(ArrayType const& _type, bool _fromMemory);
|
||||
std::string abiDecodingFunctionByteArrayAvailableLength(ArrayType const& _type, OnError _onError, bool _fromMemory);
|
||||
/// Part of @a abiDecodingFunction for calldata struct types.
|
||||
std::string abiDecodingFunctionCalldataStruct(StructType const& _type);
|
||||
std::string abiDecodingFunctionCalldataStruct(StructType const& _type, OnError _onError);
|
||||
/// Part of @a abiDecodingFunction for array types.
|
||||
std::string abiDecodingFunctionFunctionType(FunctionType const& _type, bool _fromMemory, bool _forUseOnStack);
|
||||
std::string abiDecodingFunctionFunctionType(FunctionType const& _type, OnError _onError, bool _fromMemory, bool _forUseOnStack);
|
||||
/// @returns the name of a function that retrieves an element from calldata.
|
||||
std::string calldataAccessFunction(Type const& _type);
|
||||
|
||||
|
@ -992,6 +992,7 @@ void ContractCompiler::handleCatch(vector<ASTPointer<TryCatchClause>> const& _ca
|
||||
if (error || panic)
|
||||
// Note that this function returns zero on failure, which is not a problem yet,
|
||||
// but will be a problem once we allow user-defined errors.
|
||||
// TODO
|
||||
m_context.callYulFunction(m_context.utilFunctions().returnDataSelectorFunction(), 0, 1);
|
||||
// stack: <selector>
|
||||
if (error)
|
||||
|
@ -4129,11 +4129,13 @@ string YulUtilFunctions::returnDataSelectorFunction()
|
||||
|
||||
return m_functionCollector.createFunction(functionName, [&]() {
|
||||
return util::Whiskers(R"(
|
||||
function <functionName>() -> sig {
|
||||
if gt(returndatasize(), 3) {
|
||||
function <functionName>() -> sig, error {
|
||||
switch gt(returndatasize(), 3)
|
||||
case 1 {
|
||||
returndatacopy(0, 0, 4)
|
||||
sig := <shr224>(mload(0))
|
||||
}
|
||||
default { error := 1 }
|
||||
}
|
||||
)")
|
||||
("functionName", functionName)
|
||||
@ -4201,6 +4203,30 @@ string YulUtilFunctions::tryDecodePanicDataFunction()
|
||||
});
|
||||
}
|
||||
|
||||
string YulUtilFunctions::tryDecodeReturndata(vector<Type const*> const& _types)
|
||||
{
|
||||
string const functionName = "try_decode_returndata_";
|
||||
for (Type const* type: _types)
|
||||
functionName += type->identifier();
|
||||
solAssert(m_evmVersion.supportsReturndata(), "");
|
||||
|
||||
return m_functionCollector.createFunction(functionName, [&]() {
|
||||
return util::Whiskers(R"(
|
||||
function <functionName>() -> success<+values>, <values></+values> {
|
||||
if lt(returndatasize(), 4) { leave }
|
||||
let data := <allocate>(sub(returndatasize(), 4))
|
||||
returndatacopy(data, 4, sub(returndatasize(), 4))
|
||||
|
||||
ret := msg
|
||||
}
|
||||
)")
|
||||
("functionName", functionName)
|
||||
("allocate", allocationFunction())
|
||||
("finalizeAllocation", finalizeAllocationFunction())
|
||||
.render();
|
||||
});
|
||||
}
|
||||
|
||||
string YulUtilFunctions::extractReturndataFunction()
|
||||
{
|
||||
string const functionName = "extract_returndata";
|
||||
|
@ -471,6 +471,12 @@ public:
|
||||
/// signature: () -> success, value
|
||||
std::string tryDecodePanicDataFunction();
|
||||
|
||||
/// @returns the name of a function that tries to abi-decode parameters in a catch clause
|
||||
/// from the return data.
|
||||
/// Does not check the return data signature.
|
||||
/// signature: () -> success, value...
|
||||
std::string tryDecodeReturndata(std::vector<Type const*> const& _types);
|
||||
|
||||
/// Returns a function name that returns a newly allocated `bytes` array that contains the return data.
|
||||
///
|
||||
/// If returndatacopy() is not supported by the underlying target, a empty array will be returned instead.
|
||||
|
@ -2999,49 +2999,57 @@ bool IRGeneratorForStatements::visit(TryStatement const& _tryStatement)
|
||||
void IRGeneratorForStatements::handleCatch(TryStatement const& _tryStatement)
|
||||
{
|
||||
string const runFallback = m_context.newYulVariable();
|
||||
string const selector = m_context.newYulVariable();
|
||||
string const shortCalldata = m_context.newYulVariable();
|
||||
m_code << "let " << runFallback << " := 1\n";
|
||||
m_code << "let " << selector << ", " << shortCalldata << ", " << " := " << m_utils.returnDataSelectorFunction() << "()\n";
|
||||
m_code << "if iszero(" << shortCalldata << ") {\n";
|
||||
|
||||
// This function returns zero on "short returndata". We have to add a success flag
|
||||
// once we implement custom error codes.
|
||||
if (_tryStatement.errorClause() || _tryStatement.panicClause())
|
||||
m_code << "switch " << m_utils.returnDataSelectorFunction() << "()\n";
|
||||
if (_tryStatement.clauses().size() - 1 - (_tryStatement.fallback() ? 1 : 0) > 0)
|
||||
m_code << "switch " << selector << "\n";
|
||||
|
||||
if (TryCatchClause const* errorClause = _tryStatement.errorClause())
|
||||
for (ASTPointer<TryCatchClause> const& clause: _tryStatement.clauses())
|
||||
{
|
||||
m_code << "case " << selectorFromSignature32("Error(string)") << " {\n";
|
||||
string const dataVariable = m_context.newYulVariable();
|
||||
m_code << "let " << dataVariable << " := " << m_utils.tryDecodeErrorMessageFunction() << "()\n";
|
||||
m_code << "if " << dataVariable << " {\n";
|
||||
m_code << runFallback << " := 0\n";
|
||||
if (errorClause->parameters())
|
||||
if (clause->kind() == TryCatchClause::Kind::Success || clause->kind() == TryCatchClause::Kind::Fallback)
|
||||
continue;
|
||||
|
||||
string signature;
|
||||
vector<Type const*> parameterTypes;
|
||||
if (clause->kind() == TryCatchClause::Kind::Error)
|
||||
{
|
||||
solAssert(errorClause->parameters()->parameters().size() == 1, "");
|
||||
IRVariable const& var = m_context.addLocalVariable(*errorClause->parameters()->parameters().front());
|
||||
define(var) << dataVariable << "\n";
|
||||
signature = "Error(string)";
|
||||
parameterTypes.push_back(TypeProvider::stringMemory);
|
||||
}
|
||||
else if (clause->kind() == TryCatchClause::Kind::Panic)
|
||||
{
|
||||
signature = "Panic(uint256)";
|
||||
parameterTypes.push_back(TypeProvider::uint256();
|
||||
}
|
||||
else
|
||||
{
|
||||
ErrorDefinition const& error = dynamic_cast<ErrorDefinition const&>(
|
||||
*clause->errorName().annotation().referecedDeclaration
|
||||
);
|
||||
signature = error->functionType(true)->externalSignature();
|
||||
parameterTypes = error->functionType(true)->parameterTypes();
|
||||
}
|
||||
solAssert(clause->parameters(), "");
|
||||
vector<string> variables;
|
||||
string const success = m_context.newYulVariable();
|
||||
variables.push_back(success);
|
||||
for (ASTPointer<VariableDeclaration> const& varDecl: error->parameters()->parameters())
|
||||
variables += m_context.addLocalVariable(*varDecl).stackSlots();
|
||||
|
||||
m_code << "case " << selectorFromSignature32(signature) << " {\n";
|
||||
m_code << "let " << joinHumanReadable(variables) << " := " << m_utils.tryDecodeReturndata(parameterTypes) << "()\n";
|
||||
m_code << "if " << success << " {\n";
|
||||
m_code << runFallback << " := 0\n";
|
||||
errorClause->accept(*this);
|
||||
m_code << "}\n";
|
||||
m_code << "}\n";
|
||||
}
|
||||
if (TryCatchClause const* panicClause = _tryStatement.panicClause())
|
||||
{
|
||||
m_code << "case " << selectorFromSignature32("Panic(uint256)") << " {\n";
|
||||
string const success = m_context.newYulVariable();
|
||||
string const code = m_context.newYulVariable();
|
||||
m_code << "let " << success << ", " << code << " := " << m_utils.tryDecodePanicDataFunction() << "()\n";
|
||||
m_code << "if " << success << " {\n";
|
||||
m_code << runFallback << " := 0\n";
|
||||
if (panicClause->parameters())
|
||||
{
|
||||
solAssert(panicClause->parameters()->parameters().size() == 1, "");
|
||||
IRVariable const& var = m_context.addLocalVariable(*panicClause->parameters()->parameters().front());
|
||||
define(var) << code << "\n";
|
||||
}
|
||||
panicClause->accept(*this);
|
||||
m_code << "}\n";
|
||||
m_code << "}\n";
|
||||
}
|
||||
|
||||
m_code << "}\n";
|
||||
m_code << "if " << runFallback << " {\n";
|
||||
if (_tryStatement.fallbackClause())
|
||||
handleCatchFallback(*_tryStatement.fallbackClause());
|
||||
|
Loading…
Reference in New Issue
Block a user