mirror of
https://github.com/ethereum/solidity
synced 2023-10-03 13:03:40 +00:00
Panic codes.
This commit is contained in:
parent
b559de11ee
commit
bfd267459c
@ -29,6 +29,9 @@
|
||||
#include <libsolidity/codegen/CompilerUtils.h>
|
||||
#include <libsolidity/codegen/LValue.h>
|
||||
|
||||
#include <libsolutil/FunctionSelector.h>
|
||||
#include <libsolutil/Whiskers.h>
|
||||
|
||||
#include <libevmasm/Instruction.h>
|
||||
#include <liblangutil/Exceptions.h>
|
||||
|
||||
@ -857,13 +860,17 @@ void ArrayUtils::popStorageArrayElement(ArrayType const& _type) const
|
||||
|
||||
if (_type.isByteArray())
|
||||
{
|
||||
m_context.appendInlineAssembly(R"({
|
||||
util::Whiskers code(R"({
|
||||
let slot_value := sload(ref)
|
||||
switch and(slot_value, 1)
|
||||
case 0 {
|
||||
// short byte array
|
||||
let length := and(div(slot_value, 2), 0x1f)
|
||||
if iszero(length) { invalid() }
|
||||
if iszero(length) {
|
||||
mstore(0, <panicSelector>)
|
||||
mstore(4, <emptyArrayPop>)
|
||||
revert(0, 0x24)
|
||||
}
|
||||
|
||||
// Zero-out the suffix including the least significant byte.
|
||||
let mask := sub(exp(0x100, sub(33, length)), 1)
|
||||
@ -901,7 +908,10 @@ void ArrayUtils::popStorageArrayElement(ArrayType const& _type) const
|
||||
sstore(ref, slot_value)
|
||||
}
|
||||
}
|
||||
})", {"ref"});
|
||||
})");
|
||||
code("panicSelector", util::selectorFromSignature("Panic(uint256)").str());
|
||||
code("emptyArrayPop", to_string(unsigned(util::PanicCode::EmptyArrayPop)));
|
||||
m_context.appendInlineAssembly(code.render(), {"ref"});
|
||||
m_context << Instruction::POP;
|
||||
}
|
||||
else
|
||||
@ -912,7 +922,7 @@ void ArrayUtils::popStorageArrayElement(ArrayType const& _type) const
|
||||
m_context << Instruction::DUP1;
|
||||
// stack: ArrayReference oldLength oldLength
|
||||
m_context << Instruction::ISZERO;
|
||||
m_context.appendConditionalInvalid();
|
||||
m_context.appendConditionalPanic(util::PanicCode::EmptyArrayPop);
|
||||
|
||||
// Stack: ArrayReference oldLength
|
||||
m_context << u256(1) << Instruction::SWAP1 << Instruction::SUB;
|
||||
@ -1058,7 +1068,7 @@ void ArrayUtils::accessIndex(ArrayType const& _arrayType, bool _doBoundsCheck, b
|
||||
// check out-of-bounds access
|
||||
m_context << Instruction::DUP2 << Instruction::LT << Instruction::ISZERO;
|
||||
// out-of-bounds access throws exception
|
||||
m_context.appendConditionalInvalid();
|
||||
m_context.appendConditionalPanic(util::PanicCode::ArrayOutOfBounds);
|
||||
}
|
||||
if (location == DataLocation::CallData && _arrayType.isDynamicallySized())
|
||||
// remove length if present
|
||||
|
@ -72,7 +72,7 @@ public:
|
||||
/// Stack pre: reference (excludes byte offset)
|
||||
/// Stack post: new_length
|
||||
void incrementDynamicArraySize(ArrayType const& _type) const;
|
||||
/// Decrements the size of a dynamic array by one if length is nonzero. Causes an invalid instruction otherwise.
|
||||
/// Decrements the size of a dynamic array by one if length is nonzero. Causes a Panic otherwise.
|
||||
/// Clears the removed data element. In case of a byte array, this might move the data.
|
||||
/// Stack pre: reference
|
||||
/// Stack post:
|
||||
|
@ -41,6 +41,7 @@
|
||||
#include <libyul/Utilities.h>
|
||||
|
||||
#include <libsolutil/Whiskers.h>
|
||||
#include <libsolutil/FunctionSelector.h>
|
||||
|
||||
#include <liblangutil/ErrorReporter.h>
|
||||
#include <liblangutil/Scanner.h>
|
||||
@ -330,16 +331,24 @@ CompilerContext& CompilerContext::appendJump(evmasm::AssemblyItem::JumpType _jum
|
||||
return *this << item;
|
||||
}
|
||||
|
||||
CompilerContext& CompilerContext::appendInvalid()
|
||||
CompilerContext& CompilerContext::appendPanic(util::PanicCode _code)
|
||||
{
|
||||
return *this << Instruction::INVALID;
|
||||
Whiskers templ(R"({
|
||||
mstore(0, <selector>)
|
||||
mstore(4, <code>)
|
||||
revert(0, 0x24)
|
||||
})");
|
||||
templ("selector", util::selectorFromSignature("Panic(uint256)").str());
|
||||
templ("code", u256(_code).str());
|
||||
appendInlineAssembly(templ.render());
|
||||
return *this;
|
||||
}
|
||||
|
||||
CompilerContext& CompilerContext::appendConditionalInvalid()
|
||||
CompilerContext& CompilerContext::appendConditionalPanic(util::PanicCode _code)
|
||||
{
|
||||
*this << Instruction::ISZERO;
|
||||
evmasm::AssemblyItem afterTag = appendConditionalJump();
|
||||
*this << Instruction::INVALID;
|
||||
appendPanic(_code);
|
||||
*this << afterTag;
|
||||
return *this;
|
||||
}
|
||||
|
@ -36,6 +36,7 @@
|
||||
#include <liblangutil/ErrorReporter.h>
|
||||
#include <liblangutil/EVMVersion.h>
|
||||
#include <libsolutil/Common.h>
|
||||
#include <libsolutil/ErrorCodes.h>
|
||||
|
||||
#include <libyul/AsmAnalysisInfo.h>
|
||||
#include <libyul/backends/evm/EVMDialect.h>
|
||||
@ -195,10 +196,10 @@ public:
|
||||
evmasm::AssemblyItem appendJumpToNew() { return m_asm->appendJump().tag(); }
|
||||
/// Appends a JUMP to a tag already on the stack
|
||||
CompilerContext& appendJump(evmasm::AssemblyItem::JumpType _jumpType = evmasm::AssemblyItem::JumpType::Ordinary);
|
||||
/// Appends an INVALID instruction
|
||||
CompilerContext& appendInvalid();
|
||||
/// Appends a conditional INVALID instruction
|
||||
CompilerContext& appendConditionalInvalid();
|
||||
/// Appends code to revert with a Panic(uint256) error.
|
||||
CompilerContext& appendPanic(util::PanicCode _code);
|
||||
/// Appends code to revert with a Panic(uint256) error if the topmost stack element is nonzero.
|
||||
CompilerContext& appendConditionalPanic(util::PanicCode _code);
|
||||
/// Appends a REVERT(0, 0) call
|
||||
/// @param _message is an optional revert message used in debug mode
|
||||
CompilerContext& appendRevert(std::string const& _message = "");
|
||||
|
@ -810,7 +810,7 @@ void CompilerUtils::convertType(
|
||||
if (_asPartOfArgumentDecoding)
|
||||
m_context.appendConditionalRevert(false, "Enum out of range");
|
||||
else
|
||||
m_context.appendConditionalInvalid();
|
||||
m_context.appendConditionalPanic(util::PanicCode::EnumConversionError);
|
||||
enumOverflowCheckPending = false;
|
||||
}
|
||||
break;
|
||||
@ -849,7 +849,7 @@ void CompilerUtils::convertType(
|
||||
EnumType const& enumType = dynamic_cast<decltype(enumType)>(_targetType);
|
||||
solAssert(enumType.numberOfMembers() > 0, "empty enum should have caused a parser error.");
|
||||
m_context << u256(enumType.numberOfMembers() - 1) << Instruction::DUP2 << Instruction::GT;
|
||||
m_context.appendConditionalInvalid();
|
||||
m_context.appendConditionalPanic(util::PanicCode::EnumConversionError);
|
||||
enumOverflowCheckPending = false;
|
||||
}
|
||||
else if (targetTypeCategory == Type::Category::FixedPoint)
|
||||
@ -1213,13 +1213,13 @@ void CompilerUtils::pushZeroValue(Type const& _type)
|
||||
if (funType->kind() == FunctionType::Kind::Internal)
|
||||
{
|
||||
m_context << m_context.lowLevelFunctionTag("$invalidFunction", 0, 0, [](CompilerContext& _context) {
|
||||
_context.appendInvalid();
|
||||
_context.appendPanic(util::PanicCode::InvalidInternalFunction);
|
||||
});
|
||||
if (CompilerContext* runCon = m_context.runtimeContext())
|
||||
{
|
||||
leftShiftNumberOnStack(32);
|
||||
m_context << runCon->lowLevelFunctionTag("$invalidFunction", 0, 0, [](CompilerContext& _context) {
|
||||
_context.appendInvalid();
|
||||
_context.appendPanic(util::PanicCode::InvalidInternalFunction);
|
||||
}).toSubAssemblyTag(m_context.runtimeSub());
|
||||
m_context << Instruction::OR;
|
||||
}
|
||||
|
@ -47,6 +47,7 @@
|
||||
#include <liblangutil/ErrorReporter.h>
|
||||
|
||||
#include <libsolutil/Whiskers.h>
|
||||
#include <libsolutil/FunctionSelector.h>
|
||||
|
||||
#include <boost/range/adaptor/reversed.hpp>
|
||||
|
||||
@ -229,19 +230,26 @@ size_t ContractCompiler::deployLibrary(ContractDefinition const& _contract)
|
||||
m_context.pushSubroutineSize(m_context.runtimeSub());
|
||||
m_context.pushSubroutineOffset(m_context.runtimeSub());
|
||||
// This code replaces the address added by appendDeployTimeAddress().
|
||||
m_context.appendInlineAssembly(R"(
|
||||
m_context.appendInlineAssembly(
|
||||
Whiskers(R"(
|
||||
{
|
||||
// If code starts at 11, an mstore(0) writes to the full PUSH20 plus data
|
||||
// without the need for a shift.
|
||||
let codepos := 11
|
||||
codecopy(codepos, subOffset, subSize)
|
||||
// Check that the first opcode is a PUSH20
|
||||
if iszero(eq(0x73, byte(0, mload(codepos)))) { invalid() }
|
||||
if iszero(eq(0x73, byte(0, mload(codepos)))) {
|
||||
mstore(0, <panicSig>)
|
||||
mstore(4, 0)
|
||||
revert(0, 0x24)
|
||||
}
|
||||
mstore(0, address())
|
||||
mstore8(codepos, 0x73)
|
||||
return(codepos, subSize)
|
||||
}
|
||||
)", {"subSize", "subOffset"});
|
||||
)")("panicSig", util::selectorFromSignature("Panic(uint256)").str()).render(),
|
||||
{"subSize", "subOffset"}
|
||||
);
|
||||
|
||||
return m_context.runtimeSub();
|
||||
}
|
||||
|
@ -933,7 +933,7 @@ bool ExpressionCompiler::visit(FunctionCall const& _functionCall)
|
||||
{
|
||||
acceptAndConvert(*arguments[2], *TypeProvider::uint256());
|
||||
m_context << Instruction::DUP1 << Instruction::ISZERO;
|
||||
m_context.appendConditionalInvalid();
|
||||
m_context.appendConditionalPanic(util::PanicCode::DivisionByZero);
|
||||
for (unsigned i = 1; i < 3; i ++)
|
||||
acceptAndConvert(*arguments[2 - i], *TypeProvider::uint256());
|
||||
if (function.kind() == FunctionType::Kind::AddMod)
|
||||
@ -1051,7 +1051,7 @@ bool ExpressionCompiler::visit(FunctionCall const& _functionCall)
|
||||
m_context << u256(0xffffffffffffffff);
|
||||
m_context << Instruction::DUP2;
|
||||
m_context << Instruction::GT;
|
||||
m_context.appendConditionalRevert();
|
||||
m_context.appendConditionalPanic(PanicCode::ResourceError);
|
||||
|
||||
// Stack: requested_length
|
||||
utils().fetchFreeMemoryPointer();
|
||||
@ -1121,7 +1121,7 @@ bool ExpressionCompiler::visit(FunctionCall const& _functionCall)
|
||||
auto success = m_context.appendConditionalJump();
|
||||
if (function.kind() == FunctionType::Kind::Assert)
|
||||
// condition was not met, flag an error
|
||||
m_context.appendInvalid();
|
||||
m_context.appendPanic(util::PanicCode::Assert);
|
||||
else if (haveReasonString)
|
||||
{
|
||||
utils().revertWithStringData(*arguments.at(1)->annotation().type);
|
||||
@ -1886,7 +1886,7 @@ bool ExpressionCompiler::visit(IndexAccess const& _indexAccess)
|
||||
m_context << u256(fixedBytesType.numBytes());
|
||||
m_context << Instruction::DUP2 << Instruction::LT << Instruction::ISZERO;
|
||||
// out-of-bounds access throws exception
|
||||
m_context.appendConditionalInvalid();
|
||||
m_context.appendConditionalPanic(util::PanicCode::ArrayOutOfBounds);
|
||||
|
||||
m_context << Instruction::BYTE;
|
||||
utils().leftShiftNumberOnStack(256 - 8);
|
||||
@ -2154,7 +2154,7 @@ void ExpressionCompiler::appendArithmeticOperatorCode(Token _operator, Type cons
|
||||
{
|
||||
// Test for division by zero
|
||||
m_context << Instruction::DUP2 << Instruction::ISZERO;
|
||||
m_context.appendConditionalInvalid();
|
||||
m_context.appendConditionalPanic(util::PanicCode::DivisionByZero);
|
||||
|
||||
if (_operator == Token::Div)
|
||||
m_context << (c_isSigned ? Instruction::SDIV : Instruction::DIV);
|
||||
|
@ -122,7 +122,7 @@ string YulUtilFunctions::requireOrAssertFunction(bool _assert, Type const* _mess
|
||||
if iszero(condition) { <error> }
|
||||
}
|
||||
)")
|
||||
("error", _assert ? panicFunction() + "()" : "revert(0, 0)")
|
||||
("error", _assert ? panicFunction(PanicCode::Assert) + "()" : "revert(0, 0)")
|
||||
("functionName", functionName)
|
||||
.render();
|
||||
|
||||
@ -478,7 +478,7 @@ string YulUtilFunctions::overflowCheckedIntAddFunction(IntegerType const& _type)
|
||||
("maxValue", toCompactHexWithPrefix(u256(_type.maxValue())))
|
||||
("minValue", toCompactHexWithPrefix(u256(_type.minValue())))
|
||||
("cleanupFunction", cleanupFunction(_type))
|
||||
("panic", panicFunction())
|
||||
("panic", panicFunction(PanicCode::UnderOverflow))
|
||||
.render();
|
||||
});
|
||||
}
|
||||
@ -530,7 +530,7 @@ string YulUtilFunctions::overflowCheckedIntMulFunction(IntegerType const& _type)
|
||||
("maxValue", toCompactHexWithPrefix(u256(_type.maxValue())))
|
||||
("minValue", toCompactHexWithPrefix(u256(_type.minValue())))
|
||||
("cleanupFunction", cleanupFunction(_type))
|
||||
("panic", panicFunction())
|
||||
("panic", panicFunction(PanicCode::UnderOverflow))
|
||||
.render();
|
||||
});
|
||||
}
|
||||
@ -560,13 +560,13 @@ string YulUtilFunctions::overflowCheckedIntDivFunction(IntegerType const& _type)
|
||||
function <functionName>(x, y) -> r {
|
||||
x := <cleanupFunction>(x)
|
||||
y := <cleanupFunction>(y)
|
||||
if iszero(y) { <panic>() }
|
||||
if iszero(y) { <panicDivZero>() }
|
||||
<?signed>
|
||||
// overflow for minVal / -1
|
||||
if and(
|
||||
eq(x, <minVal>),
|
||||
eq(y, sub(0, 1))
|
||||
) { <panic>() }
|
||||
) { <panicOverflow>() }
|
||||
</signed>
|
||||
r := <?signed>s</signed>div(x, y)
|
||||
}
|
||||
@ -575,7 +575,8 @@ string YulUtilFunctions::overflowCheckedIntDivFunction(IntegerType const& _type)
|
||||
("signed", _type.isSigned())
|
||||
("minVal", toCompactHexWithPrefix(u256(_type.minValue())))
|
||||
("cleanupFunction", cleanupFunction(_type))
|
||||
("panic", panicFunction())
|
||||
("panicDivZero", panicFunction(PanicCode::DivisionByZero))
|
||||
("panicOverflow", panicFunction(PanicCode::UnderOverflow))
|
||||
.render();
|
||||
});
|
||||
}
|
||||
@ -596,7 +597,7 @@ string YulUtilFunctions::wrappingIntDivFunction(IntegerType const& _type)
|
||||
("functionName", functionName)
|
||||
("cleanupFunction", cleanupFunction(_type))
|
||||
("signed", _type.isSigned())
|
||||
("error", panicFunction())
|
||||
("error", panicFunction(PanicCode::DivisionByZero))
|
||||
.render();
|
||||
});
|
||||
}
|
||||
@ -617,7 +618,7 @@ string YulUtilFunctions::intModFunction(IntegerType const& _type)
|
||||
("functionName", functionName)
|
||||
("signed", _type.isSigned())
|
||||
("cleanupFunction", cleanupFunction(_type))
|
||||
("panic", panicFunction())
|
||||
("panic", panicFunction(PanicCode::DivisionByZero))
|
||||
.render();
|
||||
});
|
||||
}
|
||||
@ -647,7 +648,7 @@ string YulUtilFunctions::overflowCheckedIntSubFunction(IntegerType const& _type)
|
||||
("maxValue", toCompactHexWithPrefix(u256(_type.maxValue())))
|
||||
("minValue", toCompactHexWithPrefix(u256(_type.minValue())))
|
||||
("cleanupFunction", cleanupFunction(_type))
|
||||
("panic", panicFunction())
|
||||
("panic", panicFunction(PanicCode::UnderOverflow))
|
||||
.render();
|
||||
});
|
||||
}
|
||||
@ -808,7 +809,7 @@ string YulUtilFunctions::overflowCheckedIntLiteralExpFunction(
|
||||
("exponentCleanupFunction", cleanupFunction(_exponentType))
|
||||
("needsOverflowCheck", needsOverflowCheck)
|
||||
("exponentUpperbound", to_string(exponentUpperbound))
|
||||
("panic", panicFunction())
|
||||
("panic", panicFunction(PanicCode::UnderOverflow))
|
||||
("base", bigint2u(baseValue).str())
|
||||
.render();
|
||||
});
|
||||
@ -866,7 +867,7 @@ string YulUtilFunctions::overflowCheckedUnsignedExpFunction()
|
||||
}
|
||||
)")
|
||||
("functionName", functionName)
|
||||
("panic", panicFunction())
|
||||
("panic", panicFunction(PanicCode::UnderOverflow))
|
||||
("expLoop", overflowCheckedExpLoopFunction())
|
||||
("shr_1", shiftRightFunction(1))
|
||||
.render();
|
||||
@ -916,7 +917,7 @@ string YulUtilFunctions::overflowCheckedSignedExpFunction()
|
||||
}
|
||||
)")
|
||||
("functionName", functionName)
|
||||
("panic", panicFunction())
|
||||
("panic", panicFunction(PanicCode::UnderOverflow))
|
||||
("expLoop", overflowCheckedExpLoopFunction())
|
||||
("shr_1", shiftRightFunction(1))
|
||||
.render();
|
||||
@ -957,7 +958,7 @@ string YulUtilFunctions::overflowCheckedExpLoopFunction()
|
||||
}
|
||||
)")
|
||||
("functionName", functionName)
|
||||
("panic", panicFunction())
|
||||
("panic", panicFunction(PanicCode::UnderOverflow))
|
||||
("shr_1", shiftRightFunction(1))
|
||||
.render();
|
||||
});
|
||||
@ -1087,7 +1088,7 @@ std::string YulUtilFunctions::resizeDynamicArrayFunction(ArrayType const& _type)
|
||||
}
|
||||
})")
|
||||
("functionName", functionName)
|
||||
("panic", panicFunction())
|
||||
("panic", panicFunction(PanicCode::ResourceError))
|
||||
("fetchLength", arrayLengthFunction(_type))
|
||||
("convertToSize", arrayConvertLengthToSize(_type))
|
||||
("dataPosition", arrayDataAreaFunction(_type))
|
||||
@ -1123,7 +1124,7 @@ string YulUtilFunctions::resizeDynamicByteArrayFunction(ArrayType const& _type)
|
||||
}
|
||||
})")
|
||||
("functionName", functionName)
|
||||
("panic", panicFunction())
|
||||
("panic", panicFunction(PanicCode::ResourceError))
|
||||
("extractLength", extractByteArrayLengthFunction())
|
||||
("maxArrayLength", (u256(1) << 64).str())
|
||||
("decreaseSize", decreaseByteArraySizeFunction(_type))
|
||||
@ -1266,7 +1267,7 @@ string YulUtilFunctions::storageArrayPopFunction(ArrayType const& _type)
|
||||
sstore(array, newLen)
|
||||
})")
|
||||
("functionName", functionName)
|
||||
("panic", panicFunction())
|
||||
("panic", panicFunction(PanicCode::EmptyArrayPop))
|
||||
("fetchLength", arrayLengthFunction(_type))
|
||||
("indexAccess", storageArrayIndexAccessFunction(_type))
|
||||
("setToZero", storageSetToZeroFunction(*_type.baseType()))
|
||||
@ -1308,7 +1309,7 @@ string YulUtilFunctions::storageByteArrayPopFunction(ArrayType const& _type)
|
||||
}
|
||||
})")
|
||||
("functionName", functionName)
|
||||
("panic", panicFunction())
|
||||
("panic", panicFunction(PanicCode::EmptyArrayPop))
|
||||
("extractByteArrayLength", extractByteArrayLengthFunction())
|
||||
("transitLongToShort", byteArrayTransitLongToShortFunction(_type))
|
||||
("encodeUsedSetLen", shortByteArrayEncodeUsedAreaSetLengthFunction())
|
||||
@ -1370,7 +1371,7 @@ string YulUtilFunctions::storageArrayPushFunction(ArrayType const& _type)
|
||||
</isByteArray>
|
||||
})")
|
||||
("functionName", functionName)
|
||||
("panic", panicFunction())
|
||||
("panic", panicFunction(PanicCode::ResourceError))
|
||||
("extractByteArrayLength", _type.isByteArray() ? extractByteArrayLengthFunction() : "")
|
||||
("dataAreaFunction", arrayDataAreaFunction(_type))
|
||||
("isByteArray", _type.isByteArray())
|
||||
@ -1400,7 +1401,7 @@ string YulUtilFunctions::storageArrayPushZeroFunction(ArrayType const& _type)
|
||||
slot, offset := <indexAccess>(array, oldLen)
|
||||
})")
|
||||
("functionName", functionName)
|
||||
("panic", panicFunction())
|
||||
("panic", panicFunction(PanicCode::ResourceError))
|
||||
("fetchLength", arrayLengthFunction(_type))
|
||||
("indexAccess", storageArrayIndexAccessFunction(_type))
|
||||
("maxArrayLength", (u256(1) << 64).str())
|
||||
@ -1715,7 +1716,7 @@ string YulUtilFunctions::arrayAllocationSizeFunction(ArrayType const& _type)
|
||||
}
|
||||
)");
|
||||
w("functionName", functionName);
|
||||
w("panic", panicFunction());
|
||||
w("panic", panicFunction(PanicCode::ResourceError));
|
||||
w("byteArray", _type.isByteArray());
|
||||
w("dynamic", _type.isDynamicallySized());
|
||||
return w.render();
|
||||
@ -1786,7 +1787,7 @@ string YulUtilFunctions::storageArrayIndexAccessFunction(ArrayType const& _type)
|
||||
}
|
||||
)")
|
||||
("functionName", functionName)
|
||||
("panic", panicFunction())
|
||||
("panic", panicFunction(PanicCode::ArrayOutOfBounds))
|
||||
("arrayLen", arrayLengthFunction(_type))
|
||||
("dataAreaFunc", arrayDataAreaFunction(_type))
|
||||
("multipleItemsPerSlot", _type.baseType()->storageBytes() <= 16)
|
||||
@ -1816,7 +1817,7 @@ string YulUtilFunctions::memoryArrayIndexAccessFunction(ArrayType const& _type)
|
||||
}
|
||||
)")
|
||||
("functionName", functionName)
|
||||
("panic", panicFunction())
|
||||
("panic", panicFunction(PanicCode::ArrayOutOfBounds))
|
||||
("arrayLen", arrayLengthFunction(_type))
|
||||
("stride", to_string(_type.memoryStride()))
|
||||
("dynamicallySized", _type.isDynamicallySized())
|
||||
@ -1839,7 +1840,7 @@ string YulUtilFunctions::calldataArrayIndexAccessFunction(ArrayType const& _type
|
||||
}
|
||||
)")
|
||||
("functionName", functionName)
|
||||
("panic", panicFunction())
|
||||
("panic", panicFunction(PanicCode::ArrayOutOfBounds))
|
||||
("stride", to_string(_type.calldataStride()))
|
||||
("dynamicallySized", _type.isDynamicallySized())
|
||||
("dynamicallyEncodedBase", _type.baseType()->isDynamicallyEncoded())
|
||||
@ -2509,7 +2510,7 @@ string YulUtilFunctions::allocationFunction()
|
||||
)")
|
||||
("functionName", functionName)
|
||||
("freeMemoryPointer", to_string(CompilerUtils::freeMemoryPointer))
|
||||
("panic", panicFunction())
|
||||
("panic", panicFunction(PanicCode::ResourceError))
|
||||
.render();
|
||||
});
|
||||
}
|
||||
@ -3085,10 +3086,7 @@ string YulUtilFunctions::validatorFunction(Type const& _type, bool _revertOnFail
|
||||
}
|
||||
)");
|
||||
templ("functionName", functionName);
|
||||
if (_revertOnFailure)
|
||||
templ("failure", "revert(0, 0)");
|
||||
else
|
||||
templ("failure", panicFunction() + "()");
|
||||
PanicCode panicCode = PanicCode::Generic;
|
||||
|
||||
switch (_type.category())
|
||||
{
|
||||
@ -3111,6 +3109,7 @@ string YulUtilFunctions::validatorFunction(Type const& _type, bool _revertOnFail
|
||||
{
|
||||
size_t members = dynamic_cast<EnumType const&>(_type).numberOfMembers();
|
||||
solAssert(members > 0, "empty enum should have caused a parser error.");
|
||||
panicCode = PanicCode::EnumConversionError;
|
||||
templ("condition", "lt(value, " + to_string(members) + ")");
|
||||
break;
|
||||
}
|
||||
@ -3121,6 +3120,11 @@ string YulUtilFunctions::validatorFunction(Type const& _type, bool _revertOnFail
|
||||
solAssert(false, "Validation of type " + _type.identifier() + " requested.");
|
||||
}
|
||||
|
||||
if (_revertOnFailure)
|
||||
templ("failure", "revert(0, 0)");
|
||||
else
|
||||
templ("failure", panicFunction(panicCode) + "()");
|
||||
|
||||
return templ.render();
|
||||
});
|
||||
}
|
||||
@ -3196,7 +3200,7 @@ std::string YulUtilFunctions::decrementCheckedFunction(Type const& _type)
|
||||
}
|
||||
)")
|
||||
("functionName", functionName)
|
||||
("panic", panicFunction())
|
||||
("panic", panicFunction(PanicCode::UnderOverflow))
|
||||
("minval", toCompactHexWithPrefix(type.min()))
|
||||
("cleanupFunction", cleanupFunction(_type))
|
||||
.render();
|
||||
@ -3237,7 +3241,7 @@ std::string YulUtilFunctions::incrementCheckedFunction(Type const& _type)
|
||||
)")
|
||||
("functionName", functionName)
|
||||
("maxval", toCompactHexWithPrefix(type.max()))
|
||||
("panic", panicFunction())
|
||||
("panic", panicFunction(PanicCode::UnderOverflow))
|
||||
("cleanupFunction", cleanupFunction(_type))
|
||||
.render();
|
||||
});
|
||||
@ -3278,7 +3282,7 @@ string YulUtilFunctions::negateNumberCheckedFunction(Type const& _type)
|
||||
("functionName", functionName)
|
||||
("minval", toCompactHexWithPrefix(type.min()))
|
||||
("cleanupFunction", cleanupFunction(_type))
|
||||
("panic", panicFunction())
|
||||
("panic", panicFunction(PanicCode::UnderOverflow))
|
||||
.render();
|
||||
});
|
||||
}
|
||||
@ -3400,7 +3404,7 @@ string YulUtilFunctions::storageSetToZeroFunction(Type const& _type)
|
||||
)")
|
||||
("functionName", functionName)
|
||||
("clearArray", clearStorageArrayFunction(dynamic_cast<ArrayType const&>(_type)))
|
||||
("panic", panicFunction())
|
||||
("panic", panicFunction(PanicCode::Generic))
|
||||
.render();
|
||||
else if (_type.category() == Type::Category::Struct)
|
||||
return Whiskers(R"(
|
||||
@ -3411,7 +3415,7 @@ string YulUtilFunctions::storageSetToZeroFunction(Type const& _type)
|
||||
)")
|
||||
("functionName", functionName)
|
||||
("clearStruct", clearStorageStructFunction(dynamic_cast<StructType const&>(_type)))
|
||||
("panic", panicFunction())
|
||||
("panic", panicFunction(PanicCode::Generic))
|
||||
.render();
|
||||
else
|
||||
solUnimplemented("setToZero for type " + _type.identifier() + " not yet implemented!");
|
||||
@ -3645,16 +3649,20 @@ string YulUtilFunctions::revertReasonIfDebug(string const& _message)
|
||||
return revertReasonIfDebug(m_revertStrings, _message);
|
||||
}
|
||||
|
||||
string YulUtilFunctions::panicFunction()
|
||||
string YulUtilFunctions::panicFunction(util::PanicCode _code)
|
||||
{
|
||||
string functionName = "panic_error";
|
||||
string functionName = "panic_error_" + toCompactHexWithPrefix(uint64_t(_code));
|
||||
return m_functionCollector.createFunction(functionName, [&]() {
|
||||
return Whiskers(R"(
|
||||
function <functionName>() {
|
||||
invalid()
|
||||
mstore(0, <selector>)
|
||||
mstore(4, <code>)
|
||||
revert(0, 0x24)
|
||||
}
|
||||
)")
|
||||
("functionName", functionName)
|
||||
("selector", util::selectorFromSignature("Panic(uint256)").str())
|
||||
("code", toCompactHexWithPrefix(_code))
|
||||
.render();
|
||||
});
|
||||
}
|
||||
|
@ -28,6 +28,8 @@
|
||||
|
||||
#include <libsolidity/interface/DebugSettings.h>
|
||||
|
||||
#include <libsolutil/ErrorCodes.h>
|
||||
|
||||
#include <memory>
|
||||
#include <string>
|
||||
#include <vector>
|
||||
@ -406,9 +408,8 @@ public:
|
||||
|
||||
std::string revertReasonIfDebug(std::string const& _message = "");
|
||||
|
||||
/// Executes the invalid opcode.
|
||||
/// Might use revert with special error code in the future.
|
||||
std::string panicFunction();
|
||||
/// Reverts with ``Panic(uint256)`` and the given code.
|
||||
std::string panicFunction(util::PanicCode _code);
|
||||
|
||||
/// Returns the name of a function that decodes an error message.
|
||||
/// signature: () -> arrayPtr
|
||||
|
@ -208,7 +208,7 @@ InternalDispatchMap IRGenerator::generateInternalDispatchFunctions()
|
||||
}
|
||||
)");
|
||||
templ("functionName", funName);
|
||||
templ("panic", m_utils.panicFunction());
|
||||
templ("panic", m_utils.panicFunction(PanicCode::InvalidInternalFunction));
|
||||
templ("in", suffixedVariableNameList("in_", 0, arity.in));
|
||||
templ("out", suffixedVariableNameList("out_", 0, arity.out));
|
||||
|
||||
|
@ -1302,7 +1302,7 @@ void IRGeneratorForStatements::endVisit(FunctionCall const& _functionCall)
|
||||
define(modulus, *arguments[2]);
|
||||
Whiskers templ("if iszero(<modulus>) { <panic>() }\n");
|
||||
templ("modulus", modulus.name());
|
||||
templ("panic", m_utils.panicFunction());
|
||||
templ("panic", m_utils.panicFunction(PanicCode::DivisionByZero));
|
||||
m_code << templ.render();
|
||||
|
||||
string args;
|
||||
@ -1367,7 +1367,7 @@ void IRGeneratorForStatements::endVisit(FunctionCall const& _functionCall)
|
||||
t("allocateTemporaryMemory", m_utils.allocationTemporaryMemoryFunction());
|
||||
t("releaseTemporaryMemory", m_utils.releaseTemporaryMemoryFunction());
|
||||
t("object", IRNames::creationObject(*contract));
|
||||
t("panic", m_utils.panicFunction());
|
||||
t("panic", m_utils.panicFunction(PanicCode::ResourceError));
|
||||
t("abiEncode",
|
||||
m_context.abiFunctions().tupleEncoder(argumentTypes, functionType->parameterTypes(), false)
|
||||
);
|
||||
@ -2023,7 +2023,7 @@ void IRGeneratorForStatements::endVisit(IndexAccess const& _indexAccess)
|
||||
)")
|
||||
("index", index.name())
|
||||
("length", to_string(fixedBytesType.numBytes()))
|
||||
("panic", m_utils.panicFunction())
|
||||
("panic", m_utils.panicFunction(PanicCode::ArrayOutOfBounds))
|
||||
("array", IRVariable(_indexAccess.baseExpression()).name())
|
||||
("shl248", m_utils.shiftLeftFunction(256 - 8))
|
||||
("result", IRVariable(_indexAccess).name())
|
||||
|
@ -9,6 +9,7 @@ set(sources
|
||||
CommonIO.h
|
||||
Exceptions.cpp
|
||||
Exceptions.h
|
||||
ErrorCodes.h
|
||||
FixedHash.h
|
||||
FunctionSelector.h
|
||||
IndentedWriter.cpp
|
||||
|
38
libsolutil/ErrorCodes.h
Normal file
38
libsolutil/ErrorCodes.h
Normal file
@ -0,0 +1,38 @@
|
||||
/*
|
||||
This file is part of solidity.
|
||||
|
||||
solidity is free software: you can redistribute it and/or modify
|
||||
it under the terms of the GNU General Public License as published by
|
||||
the Free Software Foundation, either version 3 of the License, or
|
||||
(at your option) any later version.
|
||||
|
||||
solidity is distributed in the hope that it will be useful,
|
||||
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
GNU General Public License for more details.
|
||||
|
||||
You should have received a copy of the GNU General Public License
|
||||
along with solidity. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
// SPDX-License-Identifier: GPL-3.0
|
||||
#pragma once
|
||||
|
||||
#include <string>
|
||||
|
||||
namespace solidity::util
|
||||
{
|
||||
|
||||
enum class PanicCode
|
||||
{
|
||||
Generic = 0x00, // generic / unspecified error.
|
||||
Assert = 0x01, // generic / unspecified error. Used by assert().
|
||||
UnderOverflow = 0x11, // arithmetic underflow or overflow
|
||||
DivisionByZero = 0x12, // division or modulo by zero
|
||||
EnumConversionError = 0x21, // enum conversion error
|
||||
EmptyArrayPop = 0x31, // empty array pop
|
||||
ArrayOutOfBounds = 0x32, // array out of bounds access
|
||||
ResourceError = 0x41, // resource error (too large allocation or too large array)
|
||||
InvalidInternalFunction = 0x51, // calling invalid internal function
|
||||
};
|
||||
|
||||
}
|
@ -378,6 +378,7 @@ EVMDialectTyped::EVMDialectTyped(langutil::EVMVersion _evmVersion, bool _objectA
|
||||
BuiltinContext&,
|
||||
std::function<void(Expression const&)> _visitExpression
|
||||
) {
|
||||
// TODO this should use a Panic.
|
||||
// A value larger than 1 causes an invalid instruction.
|
||||
visitArguments(_assembly, _call, _visitExpression);
|
||||
_assembly.appendConstant(2);
|
||||
|
Loading…
Reference in New Issue
Block a user