mirror of
https://github.com/ethereum/solidity
synced 2023-10-03 13:03:40 +00:00
Respect memory model for revert.
This commit is contained in:
parent
fe4822a1d2
commit
62355aead3
@ -205,12 +205,12 @@ string ABIFunctions::tupleDecoder(TypePointers const& _types, bool _fromMemory)
|
|||||||
|
|
||||||
Whiskers templ(R"(
|
Whiskers templ(R"(
|
||||||
function <functionName>(headStart, dataEnd) <arrow> <valueReturnParams> {
|
function <functionName>(headStart, dataEnd) <arrow> <valueReturnParams> {
|
||||||
if slt(sub(dataEnd, headStart), <minimumSize>) { <revertString> }
|
if slt(sub(dataEnd, headStart), <minimumSize>) { <revertString>() }
|
||||||
<decodeElements>
|
<decodeElements>
|
||||||
}
|
}
|
||||||
)");
|
)");
|
||||||
templ("functionName", functionName);
|
templ("functionName", functionName);
|
||||||
templ("revertString", revertReasonIfDebug("ABI decoding: tuple data too short"));
|
templ("revertString", revertReasonIfDebugFunction("ABI decoding: tuple data too short"));
|
||||||
templ("minimumSize", to_string(headSize(decodingTypes)));
|
templ("minimumSize", to_string(headSize(decodingTypes)));
|
||||||
|
|
||||||
string decodeElements;
|
string decodeElements;
|
||||||
@ -235,7 +235,7 @@ string ABIFunctions::tupleDecoder(TypePointers const& _types, bool _fromMemory)
|
|||||||
{
|
{
|
||||||
<?dynamic>
|
<?dynamic>
|
||||||
let offset := <load>(add(headStart, <pos>))
|
let offset := <load>(add(headStart, <pos>))
|
||||||
if gt(offset, 0xffffffffffffffff) { <revertString> }
|
if gt(offset, 0xffffffffffffffff) { <revertString>() }
|
||||||
<!dynamic>
|
<!dynamic>
|
||||||
let offset := <pos>
|
let offset := <pos>
|
||||||
</dynamic>
|
</dynamic>
|
||||||
@ -244,7 +244,7 @@ string ABIFunctions::tupleDecoder(TypePointers const& _types, bool _fromMemory)
|
|||||||
)");
|
)");
|
||||||
elementTempl("dynamic", decodingTypes[i]->isDynamicallyEncoded());
|
elementTempl("dynamic", decodingTypes[i]->isDynamicallyEncoded());
|
||||||
// TODO add test
|
// TODO add test
|
||||||
elementTempl("revertString", revertReasonIfDebug("ABI decoding: invalid tuple offset"));
|
elementTempl("revertString", revertReasonIfDebugFunction("ABI decoding: invalid tuple offset"));
|
||||||
elementTempl("load", _fromMemory ? "mload" : "calldataload");
|
elementTempl("load", _fromMemory ? "mload" : "calldataload");
|
||||||
elementTempl("values", boost::algorithm::join(valueNamesLocal, ", "));
|
elementTempl("values", boost::algorithm::join(valueNamesLocal, ", "));
|
||||||
elementTempl("pos", to_string(headPos));
|
elementTempl("pos", to_string(headPos));
|
||||||
@ -487,12 +487,12 @@ string ABIFunctions::abiEncodingFunctionCalldataArrayWithoutCleanup(
|
|||||||
else
|
else
|
||||||
templ("scaleLengthByStride",
|
templ("scaleLengthByStride",
|
||||||
Whiskers(R"(
|
Whiskers(R"(
|
||||||
if gt(length, <maxLength>) { <revertString> }
|
if gt(length, <maxLength>) { <revertString>() }
|
||||||
length := mul(length, <stride>)
|
length := mul(length, <stride>)
|
||||||
)")
|
)")
|
||||||
("stride", toCompactHexWithPrefix(fromArrayType.calldataStride()))
|
("stride", toCompactHexWithPrefix(fromArrayType.calldataStride()))
|
||||||
("maxLength", toCompactHexWithPrefix(u256(-1) / fromArrayType.calldataStride()))
|
("maxLength", toCompactHexWithPrefix(u256(-1) / fromArrayType.calldataStride()))
|
||||||
("revertString", revertReasonIfDebug("ABI encoding: array data too long"))
|
("revertString", revertReasonIfDebugFunction("ABI encoding: array data too long"))
|
||||||
.render()
|
.render()
|
||||||
// TODO add revert test
|
// TODO add revert test
|
||||||
);
|
);
|
||||||
@ -1148,14 +1148,14 @@ string ABIFunctions::abiDecodingFunctionArray(ArrayType const& _type, bool _from
|
|||||||
R"(
|
R"(
|
||||||
// <readableTypeName>
|
// <readableTypeName>
|
||||||
function <functionName>(offset, end) -> array {
|
function <functionName>(offset, end) -> array {
|
||||||
if iszero(slt(add(offset, 0x1f), end)) { <revertString> }
|
if iszero(slt(add(offset, 0x1f), end)) { <revertString>() }
|
||||||
let length := <retrieveLength>
|
let length := <retrieveLength>
|
||||||
array := <abiDecodeAvailableLen>(<offset>, length, end)
|
array := <abiDecodeAvailableLen>(<offset>, length, end)
|
||||||
}
|
}
|
||||||
)"
|
)"
|
||||||
);
|
);
|
||||||
// TODO add test
|
// TODO add test
|
||||||
templ("revertString", revertReasonIfDebug("ABI decoding: invalid calldata array offset"));
|
templ("revertString", revertReasonIfDebugFunction("ABI decoding: invalid calldata array offset"));
|
||||||
templ("functionName", functionName);
|
templ("functionName", functionName);
|
||||||
templ("readableTypeName", _type.toString(true));
|
templ("readableTypeName", _type.toString(true));
|
||||||
templ("retrieveLength", _type.isDynamicallySized() ? (load + "(offset)") : toCompactHexWithPrefix(_type.length()));
|
templ("retrieveLength", _type.isDynamicallySized() ? (load + "(offset)") : toCompactHexWithPrefix(_type.length()));
|
||||||
@ -1188,13 +1188,13 @@ string ABIFunctions::abiDecodingFunctionArrayAvailableLength(ArrayType const& _t
|
|||||||
</dynamic>
|
</dynamic>
|
||||||
let src := offset
|
let src := offset
|
||||||
if gt(add(src, mul(length, <stride>)), end) {
|
if gt(add(src, mul(length, <stride>)), end) {
|
||||||
<revertInvalidStride>
|
<revertInvalidStride>()
|
||||||
}
|
}
|
||||||
for { let i := 0 } lt(i, length) { i := add(i, 1) }
|
for { let i := 0 } lt(i, length) { i := add(i, 1) }
|
||||||
{
|
{
|
||||||
<?dynamicBase>
|
<?dynamicBase>
|
||||||
let innerOffset := <load>(src)
|
let innerOffset := <load>(src)
|
||||||
if gt(innerOffset, 0xffffffffffffffff) { <revertStringOffset> }
|
if gt(innerOffset, 0xffffffffffffffff) { <revertStringOffset>() }
|
||||||
let elementPos := add(offset, innerOffset)
|
let elementPos := add(offset, innerOffset)
|
||||||
<!dynamicBase>
|
<!dynamicBase>
|
||||||
let elementPos := src
|
let elementPos := src
|
||||||
@ -1215,9 +1215,9 @@ string ABIFunctions::abiDecodingFunctionArrayAvailableLength(ArrayType const& _t
|
|||||||
templ("dynamicBase", _type.baseType()->isDynamicallyEncoded());
|
templ("dynamicBase", _type.baseType()->isDynamicallyEncoded());
|
||||||
templ(
|
templ(
|
||||||
"revertInvalidStride",
|
"revertInvalidStride",
|
||||||
revertReasonIfDebug("ABI decoding: invalid calldata array stride")
|
revertReasonIfDebugFunction("ABI decoding: invalid calldata array stride")
|
||||||
);
|
);
|
||||||
templ("revertStringOffset", revertReasonIfDebug("ABI decoding: invalid calldata array offset"));
|
templ("revertStringOffset", revertReasonIfDebugFunction("ABI decoding: invalid calldata array offset"));
|
||||||
templ("decodingFun", abiDecodingFunction(*_type.baseType(), _fromMemory, false));
|
templ("decodingFun", abiDecodingFunction(*_type.baseType(), _fromMemory, false));
|
||||||
return templ.render();
|
return templ.render();
|
||||||
});
|
});
|
||||||
@ -1241,15 +1241,15 @@ string ABIFunctions::abiDecodingFunctionCalldataArray(ArrayType const& _type)
|
|||||||
w = Whiskers(R"(
|
w = Whiskers(R"(
|
||||||
// <readableTypeName>
|
// <readableTypeName>
|
||||||
function <functionName>(offset, end) -> arrayPos, length {
|
function <functionName>(offset, end) -> arrayPos, length {
|
||||||
if iszero(slt(add(offset, 0x1f), end)) { <revertStringOffset> }
|
if iszero(slt(add(offset, 0x1f), end)) { <revertStringOffset>() }
|
||||||
length := calldataload(offset)
|
length := calldataload(offset)
|
||||||
if gt(length, 0xffffffffffffffff) { <revertStringLength> }
|
if gt(length, 0xffffffffffffffff) { <revertStringLength>() }
|
||||||
arrayPos := add(offset, 0x20)
|
arrayPos := add(offset, 0x20)
|
||||||
if gt(add(arrayPos, mul(length, <stride>)), end) { <revertStringPos> }
|
if gt(add(arrayPos, mul(length, <stride>)), end) { <revertStringPos>() }
|
||||||
}
|
}
|
||||||
)");
|
)");
|
||||||
w("revertStringOffset", revertReasonIfDebug("ABI decoding: invalid calldata array offset"));
|
w("revertStringOffset", revertReasonIfDebugFunction("ABI decoding: invalid calldata array offset"));
|
||||||
w("revertStringLength", revertReasonIfDebug("ABI decoding: invalid calldata array length"));
|
w("revertStringLength", revertReasonIfDebugFunction("ABI decoding: invalid calldata array length"));
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
@ -1257,12 +1257,12 @@ string ABIFunctions::abiDecodingFunctionCalldataArray(ArrayType const& _type)
|
|||||||
// <readableTypeName>
|
// <readableTypeName>
|
||||||
function <functionName>(offset, end) -> arrayPos {
|
function <functionName>(offset, end) -> arrayPos {
|
||||||
arrayPos := offset
|
arrayPos := offset
|
||||||
if gt(add(arrayPos, mul(<length>, <stride>)), end) { <revertStringPos> }
|
if gt(add(arrayPos, mul(<length>, <stride>)), end) { <revertStringPos>() }
|
||||||
}
|
}
|
||||||
)");
|
)");
|
||||||
w("length", toCompactHexWithPrefix(_type.length()));
|
w("length", toCompactHexWithPrefix(_type.length()));
|
||||||
}
|
}
|
||||||
w("revertStringPos", revertReasonIfDebug("ABI decoding: invalid calldata array stride"));
|
w("revertStringPos", revertReasonIfDebugFunction("ABI decoding: invalid calldata array stride"));
|
||||||
w("functionName", functionName);
|
w("functionName", functionName);
|
||||||
w("readableTypeName", _type.toString(true));
|
w("readableTypeName", _type.toString(true));
|
||||||
w("stride", toCompactHexWithPrefix(_type.calldataStride()));
|
w("stride", toCompactHexWithPrefix(_type.calldataStride()));
|
||||||
@ -1288,11 +1288,11 @@ string ABIFunctions::abiDecodingFunctionByteArrayAvailableLength(ArrayType const
|
|||||||
array := <allocate>(<allocationSize>(length))
|
array := <allocate>(<allocationSize>(length))
|
||||||
mstore(array, length)
|
mstore(array, length)
|
||||||
let dst := add(array, 0x20)
|
let dst := add(array, 0x20)
|
||||||
if gt(add(src, length), end) { <revertStringLength> }
|
if gt(add(src, length), end) { <revertStringLength>() }
|
||||||
<copyToMemFun>(src, dst, length)
|
<copyToMemFun>(src, dst, length)
|
||||||
}
|
}
|
||||||
)");
|
)");
|
||||||
templ("revertStringLength", revertReasonIfDebug("ABI decoding: invalid byte array length"));
|
templ("revertStringLength", revertReasonIfDebugFunction("ABI decoding: invalid byte array length"));
|
||||||
templ("functionName", functionName);
|
templ("functionName", functionName);
|
||||||
templ("allocate", m_utils.allocationFunction());
|
templ("allocate", m_utils.allocationFunction());
|
||||||
templ("allocationSize", m_utils.arrayAllocationSizeFunction(_type));
|
templ("allocationSize", m_utils.arrayAllocationSizeFunction(_type));
|
||||||
@ -1312,12 +1312,12 @@ string ABIFunctions::abiDecodingFunctionCalldataStruct(StructType const& _type)
|
|||||||
Whiskers w{R"(
|
Whiskers w{R"(
|
||||||
// <readableTypeName>
|
// <readableTypeName>
|
||||||
function <functionName>(offset, end) -> value {
|
function <functionName>(offset, end) -> value {
|
||||||
if slt(sub(end, offset), <minimumSize>) { <revertString> }
|
if slt(sub(end, offset), <minimumSize>) { <revertString>() }
|
||||||
value := offset
|
value := offset
|
||||||
}
|
}
|
||||||
)"};
|
)"};
|
||||||
// TODO add test
|
// TODO add test
|
||||||
w("revertString", revertReasonIfDebug("ABI decoding: struct calldata too short"));
|
w("revertString", revertReasonIfDebugFunction("ABI decoding: struct calldata too short"));
|
||||||
w("functionName", functionName);
|
w("functionName", functionName);
|
||||||
w("readableTypeName", _type.toString(true));
|
w("readableTypeName", _type.toString(true));
|
||||||
w("minimumSize", to_string(_type.isDynamicallyEncoded() ? _type.calldataEncodedTailSize() : _type.calldataEncodedSize(true)));
|
w("minimumSize", to_string(_type.isDynamicallyEncoded() ? _type.calldataEncodedTailSize() : _type.calldataEncodedSize(true)));
|
||||||
@ -1337,7 +1337,7 @@ string ABIFunctions::abiDecodingFunctionStruct(StructType const& _type, bool _fr
|
|||||||
Whiskers templ(R"(
|
Whiskers templ(R"(
|
||||||
// <readableTypeName>
|
// <readableTypeName>
|
||||||
function <functionName>(headStart, end) -> value {
|
function <functionName>(headStart, end) -> value {
|
||||||
if slt(sub(end, headStart), <minimumSize>) { <revertString> }
|
if slt(sub(end, headStart), <minimumSize>) { <revertString>() }
|
||||||
value := <allocate>(<memorySize>)
|
value := <allocate>(<memorySize>)
|
||||||
<#members>
|
<#members>
|
||||||
{
|
{
|
||||||
@ -1348,7 +1348,7 @@ string ABIFunctions::abiDecodingFunctionStruct(StructType const& _type, bool _fr
|
|||||||
}
|
}
|
||||||
)");
|
)");
|
||||||
// TODO add test
|
// TODO add test
|
||||||
templ("revertString", revertReasonIfDebug("ABI decoding: struct data too short"));
|
templ("revertString", revertReasonIfDebugFunction("ABI decoding: struct data too short"));
|
||||||
templ("functionName", functionName);
|
templ("functionName", functionName);
|
||||||
templ("readableTypeName", _type.toString(true));
|
templ("readableTypeName", _type.toString(true));
|
||||||
templ("allocate", m_utils.allocationFunction());
|
templ("allocate", m_utils.allocationFunction());
|
||||||
@ -1365,7 +1365,7 @@ string ABIFunctions::abiDecodingFunctionStruct(StructType const& _type, bool _fr
|
|||||||
Whiskers memberTempl(R"(
|
Whiskers memberTempl(R"(
|
||||||
<?dynamic>
|
<?dynamic>
|
||||||
let offset := <load>(add(headStart, <pos>))
|
let offset := <load>(add(headStart, <pos>))
|
||||||
if gt(offset, 0xffffffffffffffff) { <revertString> }
|
if gt(offset, 0xffffffffffffffff) { <revertString>() }
|
||||||
<!dynamic>
|
<!dynamic>
|
||||||
let offset := <pos>
|
let offset := <pos>
|
||||||
</dynamic>
|
</dynamic>
|
||||||
@ -1373,7 +1373,7 @@ string ABIFunctions::abiDecodingFunctionStruct(StructType const& _type, bool _fr
|
|||||||
)");
|
)");
|
||||||
memberTempl("dynamic", decodingType->isDynamicallyEncoded());
|
memberTempl("dynamic", decodingType->isDynamicallyEncoded());
|
||||||
// TODO add test
|
// TODO add test
|
||||||
memberTempl("revertString", revertReasonIfDebug("ABI decoding: invalid struct offset"));
|
memberTempl("revertString", revertReasonIfDebugFunction("ABI decoding: invalid struct offset"));
|
||||||
memberTempl("load", _fromMemory ? "mload" : "calldataload");
|
memberTempl("load", _fromMemory ? "mload" : "calldataload");
|
||||||
memberTempl("pos", to_string(headPos));
|
memberTempl("pos", to_string(headPos));
|
||||||
memberTempl("memoryOffset", toCompactHexWithPrefix(_type.memoryOffsetOfMember(member.name)));
|
memberTempl("memoryOffset", toCompactHexWithPrefix(_type.memoryOffsetOfMember(member.name)));
|
||||||
@ -1441,7 +1441,7 @@ string ABIFunctions::calldataAccessFunction(Type const& _type)
|
|||||||
Whiskers w(R"(
|
Whiskers w(R"(
|
||||||
function <functionName>(base_ref, ptr) -> <return> {
|
function <functionName>(base_ref, ptr) -> <return> {
|
||||||
let rel_offset_of_tail := calldataload(ptr)
|
let rel_offset_of_tail := calldataload(ptr)
|
||||||
if iszero(slt(rel_offset_of_tail, sub(sub(calldatasize(), base_ref), sub(<neededLength>, 1)))) { <revertStringOffset> }
|
if iszero(slt(rel_offset_of_tail, sub(sub(calldatasize(), base_ref), sub(<neededLength>, 1)))) { <revertStringOffset>() }
|
||||||
value := add(rel_offset_of_tail, base_ref)
|
value := add(rel_offset_of_tail, base_ref)
|
||||||
<handleLength>
|
<handleLength>
|
||||||
}
|
}
|
||||||
@ -1453,14 +1453,14 @@ string ABIFunctions::calldataAccessFunction(Type const& _type)
|
|||||||
w("handleLength", Whiskers(R"(
|
w("handleLength", Whiskers(R"(
|
||||||
length := calldataload(value)
|
length := calldataload(value)
|
||||||
value := add(value, 0x20)
|
value := add(value, 0x20)
|
||||||
if gt(length, 0xffffffffffffffff) { <revertStringLength> }
|
if gt(length, 0xffffffffffffffff) { <revertStringLength>() }
|
||||||
if sgt(base_ref, sub(calldatasize(), mul(length, <calldataStride>))) { <revertStringStride> }
|
if sgt(base_ref, sub(calldatasize(), mul(length, <calldataStride>))) { <revertStringStride>() }
|
||||||
)")
|
)")
|
||||||
("calldataStride", toCompactHexWithPrefix(arrayType->calldataStride()))
|
("calldataStride", toCompactHexWithPrefix(arrayType->calldataStride()))
|
||||||
// TODO add test
|
// TODO add test
|
||||||
("revertStringLength", revertReasonIfDebug("Invalid calldata access length"))
|
("revertStringLength", revertReasonIfDebugFunction("Invalid calldata access length"))
|
||||||
// TODO add test
|
// TODO add test
|
||||||
("revertStringStride", revertReasonIfDebug("Invalid calldata access stride"))
|
("revertStringStride", revertReasonIfDebugFunction("Invalid calldata access stride"))
|
||||||
.render());
|
.render());
|
||||||
w("return", "value, length");
|
w("return", "value, length");
|
||||||
}
|
}
|
||||||
@ -1471,7 +1471,7 @@ string ABIFunctions::calldataAccessFunction(Type const& _type)
|
|||||||
}
|
}
|
||||||
w("neededLength", toCompactHexWithPrefix(tailSize));
|
w("neededLength", toCompactHexWithPrefix(tailSize));
|
||||||
w("functionName", functionName);
|
w("functionName", functionName);
|
||||||
w("revertStringOffset", revertReasonIfDebug("Invalid calldata access offset"));
|
w("revertStringOffset", revertReasonIfDebugFunction("Invalid calldata access offset"));
|
||||||
return w.render();
|
return w.render();
|
||||||
}
|
}
|
||||||
else if (_type.isValueType())
|
else if (_type.isValueType())
|
||||||
@ -1555,7 +1555,7 @@ size_t ABIFunctions::numVariablesForType(Type const& _type, EncodingOptions cons
|
|||||||
return _type.sizeOnStack();
|
return _type.sizeOnStack();
|
||||||
}
|
}
|
||||||
|
|
||||||
std::string ABIFunctions::revertReasonIfDebug(std::string const& _message)
|
std::string ABIFunctions::revertReasonIfDebugFunction(std::string const& _message)
|
||||||
{
|
{
|
||||||
return YulUtilFunctions::revertReasonIfDebug(m_revertStrings, _message);
|
return m_utils.revertReasonIfDebugFunction(_message);
|
||||||
}
|
}
|
||||||
|
@ -273,9 +273,9 @@ private:
|
|||||||
/// is true), for which it is two.
|
/// is true), for which it is two.
|
||||||
static size_t numVariablesForType(Type const& _type, EncodingOptions const& _options);
|
static size_t numVariablesForType(Type const& _type, EncodingOptions const& _options);
|
||||||
|
|
||||||
/// @returns code that stores @param _message for revert reason
|
/// @returns the name of a function that uses @param _message for revert reason
|
||||||
/// if m_revertStrings is debug.
|
/// if m_revertStrings is debug.
|
||||||
std::string revertReasonIfDebug(std::string const& _message = "");
|
std::string revertReasonIfDebugFunction(std::string const& _message = "");
|
||||||
|
|
||||||
langutil::EVMVersion m_evmVersion;
|
langutil::EVMVersion m_evmVersion;
|
||||||
RevertStrings const m_revertStrings;
|
RevertStrings const m_revertStrings;
|
||||||
|
@ -560,7 +560,11 @@ void CompilerContext::optimizeYul(yul::Object& _object, yul::EVMDialect const& _
|
|||||||
|
|
||||||
string CompilerContext::revertReasonIfDebug(string const& _message)
|
string CompilerContext::revertReasonIfDebug(string const& _message)
|
||||||
{
|
{
|
||||||
return YulUtilFunctions::revertReasonIfDebug(m_revertStrings, _message);
|
return YulUtilFunctions::revertReasonIfDebugBody(
|
||||||
|
m_revertStrings,
|
||||||
|
"mload(" + to_string(CompilerUtils::freeMemoryPointer) + ")",
|
||||||
|
_message
|
||||||
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
void CompilerContext::updateSourceLocation()
|
void CompilerContext::updateSourceLocation()
|
||||||
|
@ -268,7 +268,7 @@ public:
|
|||||||
);
|
);
|
||||||
|
|
||||||
/// If m_revertStrings is debug, @returns inline assembly code that
|
/// If m_revertStrings is debug, @returns inline assembly code that
|
||||||
/// stores @param _message in memory position 0 and reverts.
|
/// stores @param _message at the free memory pointer and reverts.
|
||||||
/// Otherwise returns "revert(0, 0)".
|
/// Otherwise returns "revert(0, 0)".
|
||||||
std::string revertReasonIfDebug(std::string const& _message = "");
|
std::string revertReasonIfDebug(std::string const& _message = "");
|
||||||
|
|
||||||
|
@ -2220,16 +2220,16 @@ string YulUtilFunctions::calldataArrayIndexRangeAccess(ArrayType const& _type)
|
|||||||
return m_functionCollector.createFunction(functionName, [&]() {
|
return m_functionCollector.createFunction(functionName, [&]() {
|
||||||
return Whiskers(R"(
|
return Whiskers(R"(
|
||||||
function <functionName>(offset, length, startIndex, endIndex) -> offsetOut, lengthOut {
|
function <functionName>(offset, length, startIndex, endIndex) -> offsetOut, lengthOut {
|
||||||
if gt(startIndex, endIndex) { <revertSliceStartAfterEnd> }
|
if gt(startIndex, endIndex) { <revertSliceStartAfterEnd>() }
|
||||||
if gt(endIndex, length) { <revertSliceGreaterThanLength> }
|
if gt(endIndex, length) { <revertSliceGreaterThanLength>() }
|
||||||
offsetOut := add(offset, mul(startIndex, <stride>))
|
offsetOut := add(offset, mul(startIndex, <stride>))
|
||||||
lengthOut := sub(endIndex, startIndex)
|
lengthOut := sub(endIndex, startIndex)
|
||||||
}
|
}
|
||||||
)")
|
)")
|
||||||
("functionName", functionName)
|
("functionName", functionName)
|
||||||
("stride", to_string(_type.calldataStride()))
|
("stride", to_string(_type.calldataStride()))
|
||||||
("revertSliceStartAfterEnd", revertReasonIfDebug("Slice starts after end"))
|
("revertSliceStartAfterEnd", revertReasonIfDebugFunction("Slice starts after end"))
|
||||||
("revertSliceGreaterThanLength", revertReasonIfDebug("Slice is greater than length"))
|
("revertSliceGreaterThanLength", revertReasonIfDebugFunction("Slice is greater than length"))
|
||||||
.render();
|
.render();
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
@ -2243,13 +2243,13 @@ string YulUtilFunctions::accessCalldataTailFunction(Type const& _type)
|
|||||||
return Whiskers(R"(
|
return Whiskers(R"(
|
||||||
function <functionName>(base_ref, ptr_to_tail) -> addr<?dynamicallySized>, length</dynamicallySized> {
|
function <functionName>(base_ref, ptr_to_tail) -> addr<?dynamicallySized>, length</dynamicallySized> {
|
||||||
let rel_offset_of_tail := calldataload(ptr_to_tail)
|
let rel_offset_of_tail := calldataload(ptr_to_tail)
|
||||||
if iszero(slt(rel_offset_of_tail, sub(sub(calldatasize(), base_ref), sub(<neededLength>, 1)))) { <invalidCalldataTailOffset> }
|
if iszero(slt(rel_offset_of_tail, sub(sub(calldatasize(), base_ref), sub(<neededLength>, 1)))) { <invalidCalldataTailOffset>() }
|
||||||
addr := add(base_ref, rel_offset_of_tail)
|
addr := add(base_ref, rel_offset_of_tail)
|
||||||
<?dynamicallySized>
|
<?dynamicallySized>
|
||||||
length := calldataload(addr)
|
length := calldataload(addr)
|
||||||
if gt(length, 0xffffffffffffffff) { <invalidCalldataTailLength> }
|
if gt(length, 0xffffffffffffffff) { <invalidCalldataTailLength>() }
|
||||||
addr := add(addr, 32)
|
addr := add(addr, 32)
|
||||||
if sgt(addr, sub(calldatasize(), mul(length, <calldataStride>))) { <shortCalldataTail> }
|
if sgt(addr, sub(calldatasize(), mul(length, <calldataStride>))) { <shortCalldataTail>() }
|
||||||
</dynamicallySized>
|
</dynamicallySized>
|
||||||
}
|
}
|
||||||
)")
|
)")
|
||||||
@ -2257,9 +2257,9 @@ string YulUtilFunctions::accessCalldataTailFunction(Type const& _type)
|
|||||||
("dynamicallySized", _type.isDynamicallySized())
|
("dynamicallySized", _type.isDynamicallySized())
|
||||||
("neededLength", toCompactHexWithPrefix(_type.calldataEncodedTailSize()))
|
("neededLength", toCompactHexWithPrefix(_type.calldataEncodedTailSize()))
|
||||||
("calldataStride", toCompactHexWithPrefix(_type.isDynamicallySized() ? dynamic_cast<ArrayType const&>(_type).calldataStride() : 0))
|
("calldataStride", toCompactHexWithPrefix(_type.isDynamicallySized() ? dynamic_cast<ArrayType const&>(_type).calldataStride() : 0))
|
||||||
("invalidCalldataTailOffset", revertReasonIfDebug("Invalid calldata tail offset"))
|
("invalidCalldataTailOffset", revertReasonIfDebugFunction("Invalid calldata tail offset"))
|
||||||
("invalidCalldataTailLength", revertReasonIfDebug("Invalid calldata tail length"))
|
("invalidCalldataTailLength", revertReasonIfDebugFunction("Invalid calldata tail length"))
|
||||||
("shortCalldataTail", revertReasonIfDebug("Calldata tail too short"))
|
("shortCalldataTail", revertReasonIfDebugFunction("Calldata tail too short"))
|
||||||
.render();
|
.render();
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
@ -4212,42 +4212,52 @@ string YulUtilFunctions::readFromMemoryOrCalldata(Type const& _type, bool _fromC
|
|||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
string YulUtilFunctions::revertReasonIfDebug(RevertStrings revertStrings, string const& _message)
|
string YulUtilFunctions::revertReasonIfDebugFunction(string const& _message)
|
||||||
{
|
{
|
||||||
if (revertStrings >= RevertStrings::Debug && !_message.empty())
|
string functionName = "revert_error_" + util::toHex(util::keccak256(_message).asBytes());
|
||||||
{
|
return m_functionCollector.createFunction(functionName, [&](auto&, auto&) -> string {
|
||||||
Whiskers templ(R"({
|
return revertReasonIfDebugBody(m_revertStrings, allocateUnboundedFunction() + "()", _message);
|
||||||
mstore(0, <sig>)
|
});
|
||||||
mstore(4, 0x20)
|
|
||||||
mstore(add(4, 0x20), <length>)
|
|
||||||
let reasonPos := add(4, 0x40)
|
|
||||||
<#word>
|
|
||||||
mstore(add(reasonPos, <offset>), <wordValue>)
|
|
||||||
</word>
|
|
||||||
revert(0, add(reasonPos, <end>))
|
|
||||||
})");
|
|
||||||
templ("sig", util::selectorFromSignature("Error(string)").str());
|
|
||||||
templ("length", to_string(_message.length()));
|
|
||||||
|
|
||||||
size_t words = (_message.length() + 31) / 32;
|
|
||||||
vector<map<string, string>> wordParams(words);
|
|
||||||
for (size_t i = 0; i < words; ++i)
|
|
||||||
{
|
|
||||||
wordParams[i]["offset"] = to_string(i * 32);
|
|
||||||
wordParams[i]["wordValue"] = formatAsStringOrNumber(_message.substr(32 * i, 32));
|
|
||||||
}
|
|
||||||
templ("word", wordParams);
|
|
||||||
templ("end", to_string(words * 32));
|
|
||||||
|
|
||||||
return templ.render();
|
|
||||||
}
|
|
||||||
else
|
|
||||||
return "revert(0, 0)";
|
|
||||||
}
|
}
|
||||||
|
|
||||||
string YulUtilFunctions::revertReasonIfDebug(string const& _message)
|
string YulUtilFunctions::revertReasonIfDebugBody(
|
||||||
|
RevertStrings _revertStrings,
|
||||||
|
string const& _allocation,
|
||||||
|
string const& _message
|
||||||
|
)
|
||||||
{
|
{
|
||||||
return revertReasonIfDebug(m_revertStrings, _message);
|
if (_revertStrings < RevertStrings::Debug || _message.empty())
|
||||||
|
return "revert(0, 0)";
|
||||||
|
|
||||||
|
Whiskers templ(R"(
|
||||||
|
let start := <allocate>
|
||||||
|
let pos := start
|
||||||
|
mstore(pos, <sig>)
|
||||||
|
pos := add(pos, 4)
|
||||||
|
mstore(pos, 0x20)
|
||||||
|
pos := add(pos, 0x20)
|
||||||
|
mstore(pos, <length>)
|
||||||
|
pos := add(pos, 0x20)
|
||||||
|
<#word>
|
||||||
|
mstore(add(pos, <offset>), <wordValue>)
|
||||||
|
</word>
|
||||||
|
revert(start, <overallLength>)
|
||||||
|
)");
|
||||||
|
templ("allocate", _allocation);
|
||||||
|
templ("sig", util::selectorFromSignature("Error(string)").str());
|
||||||
|
templ("length", to_string(_message.length()));
|
||||||
|
|
||||||
|
size_t words = (_message.length() + 31) / 32;
|
||||||
|
vector<map<string, string>> wordParams(words);
|
||||||
|
for (size_t i = 0; i < words; ++i)
|
||||||
|
{
|
||||||
|
wordParams[i]["offset"] = to_string(i * 32);
|
||||||
|
wordParams[i]["wordValue"] = formatAsStringOrNumber(_message.substr(32 * i, 32));
|
||||||
|
}
|
||||||
|
templ("word", wordParams);
|
||||||
|
templ("overallLength", to_string(4 + 0x20 + 0x20 + words * 32));
|
||||||
|
|
||||||
|
return templ.render();
|
||||||
}
|
}
|
||||||
|
|
||||||
string YulUtilFunctions::panicFunction(util::PanicCode _code)
|
string YulUtilFunctions::panicFunction(util::PanicCode _code)
|
||||||
|
@ -453,12 +453,18 @@ public:
|
|||||||
/// signature: (slot, offset) ->
|
/// signature: (slot, offset) ->
|
||||||
std::string storageSetToZeroFunction(Type const& _type);
|
std::string storageSetToZeroFunction(Type const& _type);
|
||||||
|
|
||||||
/// If revertStrings is debug, @returns inline assembly code that
|
/// If revertStrings is debug, @returns the name of a function that
|
||||||
/// stores @param _message in memory position 0 and reverts.
|
/// stores @param _message in memory position 0 and reverts.
|
||||||
/// Otherwise returns "revert(0, 0)".
|
/// Otherwise returns the name of a function that uses "revert(0, 0)".
|
||||||
static std::string revertReasonIfDebug(RevertStrings revertStrings, std::string const& _message = "");
|
std::string revertReasonIfDebugFunction(std::string const& _message = "");
|
||||||
|
|
||||||
std::string revertReasonIfDebug(std::string const& _message = "");
|
/// @returns the function body of ``revertReasonIfDebug``.
|
||||||
|
/// Should only be used internally and by the old code generator.
|
||||||
|
static std::string revertReasonIfDebugBody(
|
||||||
|
RevertStrings _revertStrings,
|
||||||
|
std::string const& _allocation,
|
||||||
|
std::string const& _message
|
||||||
|
);
|
||||||
|
|
||||||
/// Reverts with ``Panic(uint256)`` and the given code.
|
/// Reverts with ``Panic(uint256)`` and the given code.
|
||||||
std::string panicFunction(util::PanicCode _code);
|
std::string panicFunction(util::PanicCode _code);
|
||||||
|
@ -177,8 +177,3 @@ ABIFunctions IRGenerationContext::abiFunctions()
|
|||||||
{
|
{
|
||||||
return ABIFunctions(m_evmVersion, m_revertStrings, m_functions);
|
return ABIFunctions(m_evmVersion, m_revertStrings, m_functions);
|
||||||
}
|
}
|
||||||
|
|
||||||
std::string IRGenerationContext::revertReasonIfDebug(std::string const& _message)
|
|
||||||
{
|
|
||||||
return YulUtilFunctions::revertReasonIfDebug(m_revertStrings, _message);
|
|
||||||
}
|
|
||||||
|
@ -143,10 +143,6 @@ public:
|
|||||||
|
|
||||||
ABIFunctions abiFunctions();
|
ABIFunctions abiFunctions();
|
||||||
|
|
||||||
/// @returns code that stores @param _message for revert reason
|
|
||||||
/// if m_revertStrings is debug.
|
|
||||||
std::string revertReasonIfDebug(std::string const& _message = "");
|
|
||||||
|
|
||||||
RevertStrings revertStrings() const { return m_revertStrings; }
|
RevertStrings revertStrings() const { return m_revertStrings; }
|
||||||
|
|
||||||
std::set<ContractDefinition const*, ASTNode::CompareByID>& subObjectsCreated() { return m_subObjects; }
|
std::set<ContractDefinition const*, ASTNode::CompareByID>& subObjectsCreated() { return m_subObjects; }
|
||||||
|
@ -839,7 +839,7 @@ string IRGenerator::deployCode(ContractDefinition const& _contract)
|
|||||||
|
|
||||||
string IRGenerator::callValueCheck()
|
string IRGenerator::callValueCheck()
|
||||||
{
|
{
|
||||||
return "if callvalue() { " + m_context.revertReasonIfDebug("Ether sent to non-payable function") + " }";
|
return "if callvalue() { " + m_utils.revertReasonIfDebugFunction("Ether sent to non-payable function") + "() }";
|
||||||
}
|
}
|
||||||
|
|
||||||
string IRGenerator::dispatchRoutine(ContractDefinition const& _contract)
|
string IRGenerator::dispatchRoutine(ContractDefinition const& _contract)
|
||||||
@ -885,8 +885,8 @@ string IRGenerator::dispatchRoutine(ContractDefinition const& _contract)
|
|||||||
// we revert.
|
// we revert.
|
||||||
delegatecallCheck =
|
delegatecallCheck =
|
||||||
"if iszero(called_via_delegatecall) { " +
|
"if iszero(called_via_delegatecall) { " +
|
||||||
m_context.revertReasonIfDebug("Non-view function of library called without DELEGATECALL") +
|
m_utils.revertReasonIfDebugFunction("Non-view function of library called without DELEGATECALL") +
|
||||||
" }";
|
"() }";
|
||||||
}
|
}
|
||||||
templ["delegatecallCheck"] = delegatecallCheck;
|
templ["delegatecallCheck"] = delegatecallCheck;
|
||||||
templ["callValueCheck"] = (type->isPayable() || _contract.isLibrary()) ? "" : callValueCheck();
|
templ["callValueCheck"] = (type->isPayable() || _contract.isLibrary()) ? "" : callValueCheck();
|
||||||
@ -937,12 +937,11 @@ string IRGenerator::dispatchRoutine(ContractDefinition const& _contract)
|
|||||||
t("fallback", fallbackCode);
|
t("fallback", fallbackCode);
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
t(
|
t("fallback", (
|
||||||
"fallback",
|
|
||||||
etherReceiver ?
|
etherReceiver ?
|
||||||
m_context.revertReasonIfDebug("Unknown signature and no fallback defined") :
|
m_utils.revertReasonIfDebugFunction("Unknown signature and no fallback defined") :
|
||||||
m_context.revertReasonIfDebug("Contract does not have fallback nor receive functions")
|
m_utils.revertReasonIfDebugFunction("Contract does not have fallback nor receive functions")
|
||||||
);
|
) + "()");
|
||||||
return t.render();
|
return t.render();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -2433,7 +2433,7 @@ void IRGeneratorForStatements::appendExternalFunctionCall(
|
|||||||
}
|
}
|
||||||
|
|
||||||
Whiskers templ(R"(
|
Whiskers templ(R"(
|
||||||
if iszero(extcodesize(<address>)) { <revertNoCode> }
|
if iszero(extcodesize(<address>)) { <revertNoCode>() }
|
||||||
|
|
||||||
// storage for arguments and returned data
|
// storage for arguments and returned data
|
||||||
let <pos> := <allocateUnbounded>()
|
let <pos> := <allocateUnbounded>()
|
||||||
@ -2458,7 +2458,7 @@ void IRGeneratorForStatements::appendExternalFunctionCall(
|
|||||||
<?+retVars> <retVars> := </+retVars> <abiDecode>(<pos>, add(<pos>, <returnSize>))
|
<?+retVars> <retVars> := </+retVars> <abiDecode>(<pos>, add(<pos>, <returnSize>))
|
||||||
}
|
}
|
||||||
)");
|
)");
|
||||||
templ("revertNoCode", m_context.revertReasonIfDebug("Target contract does not contain code"));
|
templ("revertNoCode", m_utils.revertReasonIfDebugFunction("Target contract does not contain code"));
|
||||||
templ("pos", m_context.newYulVariable());
|
templ("pos", m_context.newYulVariable());
|
||||||
templ("end", m_context.newYulVariable());
|
templ("end", m_context.newYulVariable());
|
||||||
if (_functionCall.annotation().tryCall)
|
if (_functionCall.annotation().tryCall)
|
||||||
|
Loading…
Reference in New Issue
Block a user