diff --git a/libsolidity/codegen/ArrayUtils.cpp b/libsolidity/codegen/ArrayUtils.cpp index 14e302bc9..97b9bb9e0 100644 --- a/libsolidity/codegen/ArrayUtils.cpp +++ b/libsolidity/codegen/ArrayUtils.cpp @@ -29,6 +29,9 @@ #include #include +#include +#include + #include #include @@ -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, ) + mstore(4, ) + 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 diff --git a/libsolidity/codegen/ArrayUtils.h b/libsolidity/codegen/ArrayUtils.h index 7cc01dd26..5ddc63a27 100644 --- a/libsolidity/codegen/ArrayUtils.h +++ b/libsolidity/codegen/ArrayUtils.h @@ -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: diff --git a/libsolidity/codegen/CompilerContext.cpp b/libsolidity/codegen/CompilerContext.cpp index ff52e626c..319ec5e80 100644 --- a/libsolidity/codegen/CompilerContext.cpp +++ b/libsolidity/codegen/CompilerContext.cpp @@ -41,6 +41,7 @@ #include #include +#include #include #include @@ -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, ) + mstore(4, ) + 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; } diff --git a/libsolidity/codegen/CompilerContext.h b/libsolidity/codegen/CompilerContext.h index b552bf6b1..5402d46ff 100644 --- a/libsolidity/codegen/CompilerContext.h +++ b/libsolidity/codegen/CompilerContext.h @@ -36,6 +36,7 @@ #include #include #include +#include #include #include @@ -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 = ""); diff --git a/libsolidity/codegen/CompilerUtils.cpp b/libsolidity/codegen/CompilerUtils.cpp index f9be04c6e..da6477b64 100644 --- a/libsolidity/codegen/CompilerUtils.cpp +++ b/libsolidity/codegen/CompilerUtils.cpp @@ -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(_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; } diff --git a/libsolidity/codegen/ContractCompiler.cpp b/libsolidity/codegen/ContractCompiler.cpp index eb3081e13..6b1d2a292 100644 --- a/libsolidity/codegen/ContractCompiler.cpp +++ b/libsolidity/codegen/ContractCompiler.cpp @@ -47,6 +47,7 @@ #include #include +#include #include @@ -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, ) + 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(); } diff --git a/libsolidity/codegen/ExpressionCompiler.cpp b/libsolidity/codegen/ExpressionCompiler.cpp index 781871a13..6b7a3b71a 100644 --- a/libsolidity/codegen/ExpressionCompiler.cpp +++ b/libsolidity/codegen/ExpressionCompiler.cpp @@ -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); diff --git a/libsolidity/codegen/YulUtilFunctions.cpp b/libsolidity/codegen/YulUtilFunctions.cpp index b0df5e4e1..c5996dc9a 100644 --- a/libsolidity/codegen/YulUtilFunctions.cpp +++ b/libsolidity/codegen/YulUtilFunctions.cpp @@ -122,7 +122,7 @@ string YulUtilFunctions::requireOrAssertFunction(bool _assert, Type const* _mess if iszero(condition) { } } )") - ("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 (x, y) -> r { x := (x) y := (y) - if iszero(y) { () } + if iszero(y) { () } // overflow for minVal / -1 if and( eq(x, ), eq(y, sub(0, 1)) - ) { () } + ) { () } r := sdiv(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) })") ("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 := (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(_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(_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(_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 () { - invalid() + mstore(0, ) + mstore(4, ) + revert(0, 0x24) } )") ("functionName", functionName) + ("selector", util::selectorFromSignature("Panic(uint256)").str()) + ("code", toCompactHexWithPrefix(_code)) .render(); }); } diff --git a/libsolidity/codegen/YulUtilFunctions.h b/libsolidity/codegen/YulUtilFunctions.h index daed3c4c2..e5ccfccd3 100644 --- a/libsolidity/codegen/YulUtilFunctions.h +++ b/libsolidity/codegen/YulUtilFunctions.h @@ -28,6 +28,8 @@ #include +#include + #include #include #include @@ -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 diff --git a/libsolidity/codegen/ir/IRGenerator.cpp b/libsolidity/codegen/ir/IRGenerator.cpp index 2e0d161dd..d64a34e65 100644 --- a/libsolidity/codegen/ir/IRGenerator.cpp +++ b/libsolidity/codegen/ir/IRGenerator.cpp @@ -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)); diff --git a/libsolidity/codegen/ir/IRGeneratorForStatements.cpp b/libsolidity/codegen/ir/IRGeneratorForStatements.cpp index cc95efd60..e7b8fe071 100644 --- a/libsolidity/codegen/ir/IRGeneratorForStatements.cpp +++ b/libsolidity/codegen/ir/IRGeneratorForStatements.cpp @@ -1302,7 +1302,7 @@ void IRGeneratorForStatements::endVisit(FunctionCall const& _functionCall) define(modulus, *arguments[2]); Whiskers templ("if iszero() { () }\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()) diff --git a/libsolutil/CMakeLists.txt b/libsolutil/CMakeLists.txt index a83eb734c..ea8bcdf86 100644 --- a/libsolutil/CMakeLists.txt +++ b/libsolutil/CMakeLists.txt @@ -9,6 +9,7 @@ set(sources CommonIO.h Exceptions.cpp Exceptions.h + ErrorCodes.h FixedHash.h FunctionSelector.h IndentedWriter.cpp diff --git a/libsolutil/ErrorCodes.h b/libsolutil/ErrorCodes.h new file mode 100644 index 000000000..d281b66b2 --- /dev/null +++ b/libsolutil/ErrorCodes.h @@ -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 . +*/ +// SPDX-License-Identifier: GPL-3.0 +#pragma once + +#include + +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 +}; + +} diff --git a/libyul/backends/evm/EVMDialect.cpp b/libyul/backends/evm/EVMDialect.cpp index 5287e5a6a..0f98776dd 100644 --- a/libyul/backends/evm/EVMDialect.cpp +++ b/libyul/backends/evm/EVMDialect.cpp @@ -378,6 +378,7 @@ EVMDialectTyped::EVMDialectTyped(langutil::EVMVersion _evmVersion, bool _objectA BuiltinContext&, std::function _visitExpression ) { + // TODO this should use a Panic. // A value larger than 1 causes an invalid instruction. visitArguments(_assembly, _call, _visitExpression); _assembly.appendConstant(2);