Panic codes.

This commit is contained in:
chriseth 2020-10-12 16:01:45 +02:00
parent b559de11ee
commit bfd267459c
14 changed files with 156 additions and 79 deletions

View File

@ -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

View File

@ -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:

View File

@ -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;
}

View File

@ -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 = "");

View File

@ -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;
}

View File

@ -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"(
{
// 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() }
mstore(0, address())
mstore8(codepos, 0x73)
return(codepos, subSize)
}
)", {"subSize", "subOffset"});
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)))) {
mstore(0, <panicSig>)
mstore(4, 0)
revert(0, 0x24)
}
mstore(0, address())
mstore8(codepos, 0x73)
return(codepos, subSize)
}
)")("panicSig", util::selectorFromSignature("Panic(uint256)").str()).render(),
{"subSize", "subOffset"}
);
return m_context.runtimeSub();
}

View File

@ -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);

View File

@ -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();
});
}

View File

@ -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

View File

@ -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));

View File

@ -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())

View File

@ -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
View 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
};
}

View File

@ -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);