mirror of
https://github.com/ethereum/solidity
synced 2023-10-03 13:03:40 +00:00
Catch panic.
This commit is contained in:
parent
b78443ac75
commit
b965446182
@ -1,5 +1,8 @@
|
||||
### 0.8.1 (unreleased)
|
||||
|
||||
Language Features:
|
||||
* Possibility to use ``catch Panic(uint code)`` to catch a panic failure from an external call.
|
||||
|
||||
Compiler Features:
|
||||
* Parser: Report meaningful error if parsing a version pragma failed.
|
||||
* SMTChecker: Support ABI functions as uninterpreted functions.
|
||||
|
@ -997,6 +997,7 @@ void TypeChecker::endVisit(TryStatement const& _tryStatement)
|
||||
}
|
||||
}
|
||||
|
||||
TryCatchClause const* panicClause = nullptr;
|
||||
TryCatchClause const* errorClause = nullptr;
|
||||
TryCatchClause const* lowLevelClause = nullptr;
|
||||
for (size_t i = 1; i < _tryStatement.clauses().size(); ++i)
|
||||
@ -1029,7 +1030,7 @@ void TypeChecker::endVisit(TryStatement const& _tryStatement)
|
||||
);
|
||||
}
|
||||
}
|
||||
else if (clause.errorName() == "Error")
|
||||
else if (clause.errorName() == "Error" || clause.errorName() == "Panic")
|
||||
{
|
||||
if (!m_evmVersion.supportsReturndata())
|
||||
m_errorReporter.typeError(
|
||||
@ -1040,26 +1041,46 @@ void TypeChecker::endVisit(TryStatement const& _tryStatement)
|
||||
"). You need at least a Byzantium-compatible EVM or use `catch { ... }`."
|
||||
);
|
||||
|
||||
if (errorClause)
|
||||
m_errorReporter.typeError(
|
||||
1036_error,
|
||||
clause.location(),
|
||||
SecondarySourceLocation{}.append("The first clause is here:", errorClause->location()),
|
||||
"This try statement already has an \"Error\" catch clause."
|
||||
);
|
||||
errorClause = &clause;
|
||||
if (
|
||||
!clause.parameters() ||
|
||||
clause.parameters()->parameters().size() != 1 ||
|
||||
*clause.parameters()->parameters().front()->type() != *TypeProvider::stringMemory()
|
||||
)
|
||||
m_errorReporter.typeError(2943_error, clause.location(), "Expected `catch Error(string memory ...) { ... }`.");
|
||||
if (clause.errorName() == "Error")
|
||||
{
|
||||
if (errorClause)
|
||||
m_errorReporter.typeError(
|
||||
1036_error,
|
||||
clause.location(),
|
||||
SecondarySourceLocation{}.append("The first clause is here:", errorClause->location()),
|
||||
"This try statement already has an \"Error\" catch clause."
|
||||
);
|
||||
errorClause = &clause;
|
||||
if (
|
||||
!clause.parameters() ||
|
||||
clause.parameters()->parameters().size() != 1 ||
|
||||
*clause.parameters()->parameters().front()->type() != *TypeProvider::stringMemory()
|
||||
)
|
||||
m_errorReporter.typeError(2943_error, clause.location(), "Expected `catch Error(string memory ...) { ... }`.");
|
||||
}
|
||||
else
|
||||
{
|
||||
if (panicClause)
|
||||
m_errorReporter.typeError(
|
||||
6732_error,
|
||||
clause.location(),
|
||||
SecondarySourceLocation{}.append("The first clause is here:", panicClause->location()),
|
||||
"This try statement already has a \"Panic\" catch clause."
|
||||
);
|
||||
panicClause = &clause;
|
||||
if (
|
||||
!clause.parameters() ||
|
||||
clause.parameters()->parameters().size() != 1 ||
|
||||
*clause.parameters()->parameters().front()->type() != *TypeProvider::uint256()
|
||||
)
|
||||
m_errorReporter.typeError(1271_error, clause.location(), "Expected `catch Panic(uint ...) { ... }`.");
|
||||
}
|
||||
}
|
||||
else
|
||||
m_errorReporter.typeError(
|
||||
3542_error,
|
||||
clause.location(),
|
||||
"Invalid catch clause name. Expected either `catch (...)` or `catch Error(...)`."
|
||||
"Invalid catch clause name. Expected either `catch (...)`, `catch Error(...)`, or `catch Panic(...)`."
|
||||
);
|
||||
}
|
||||
}
|
||||
|
@ -841,7 +841,15 @@ TryCatchClause const* TryStatement::successClause() const
|
||||
return m_clauses[0].get();
|
||||
}
|
||||
|
||||
TryCatchClause const* TryStatement::structuredClause() const
|
||||
TryCatchClause const* TryStatement::panicClause() const
|
||||
{
|
||||
for (size_t i = 1; i < m_clauses.size(); ++i)
|
||||
if (m_clauses[i]->errorName() == "Panic")
|
||||
return m_clauses[i].get();
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
TryCatchClause const* TryStatement::errorClause() const
|
||||
{
|
||||
for (size_t i = 1; i < m_clauses.size(); ++i)
|
||||
if (m_clauses[i]->errorName() == "Error")
|
||||
|
@ -1494,6 +1494,8 @@ private:
|
||||
* Syntax:
|
||||
* try <call> returns (uint x, uint y) {
|
||||
* // success code
|
||||
* } catch Panic(uint errorCode) {
|
||||
* // panic
|
||||
* } catch Error(string memory cause) {
|
||||
* // error code, reason provided
|
||||
* } catch (bytes memory lowLevelData) {
|
||||
@ -1524,7 +1526,8 @@ public:
|
||||
std::vector<ASTPointer<TryCatchClause>> const& clauses() const { return m_clauses; }
|
||||
|
||||
TryCatchClause const* successClause() const;
|
||||
TryCatchClause const* structuredClause() const;
|
||||
TryCatchClause const* panicClause() const;
|
||||
TryCatchClause const* errorClause() const;
|
||||
TryCatchClause const* fallbackClause() const;
|
||||
|
||||
private:
|
||||
|
@ -971,31 +971,46 @@ bool ContractCompiler::visit(TryStatement const& _tryStatement)
|
||||
void ContractCompiler::handleCatch(vector<ASTPointer<TryCatchClause>> const& _catchClauses)
|
||||
{
|
||||
// Stack is empty.
|
||||
ASTPointer<TryCatchClause> structured{};
|
||||
ASTPointer<TryCatchClause> error{};
|
||||
ASTPointer<TryCatchClause> panic{};
|
||||
ASTPointer<TryCatchClause> fallback{};
|
||||
for (size_t i = 1; i < _catchClauses.size(); ++i)
|
||||
if (_catchClauses[i]->errorName() == "Error")
|
||||
structured = _catchClauses[i];
|
||||
error = _catchClauses[i];
|
||||
else if (_catchClauses[i]->errorName() == "Panic")
|
||||
panic = _catchClauses[i];
|
||||
else if (_catchClauses[i]->errorName().empty())
|
||||
fallback = _catchClauses[i];
|
||||
else
|
||||
solAssert(false, "");
|
||||
|
||||
solAssert(_catchClauses.size() == 1ul + (structured ? 1 : 0) + (fallback ? 1 : 0), "");
|
||||
solAssert(_catchClauses.size() == 1ul + (error ? 1 : 0) + (panic ? 1 : 0) + (fallback ? 1 : 0), "");
|
||||
|
||||
evmasm::AssemblyItem endTag = m_context.newTag();
|
||||
evmasm::AssemblyItem fallbackTag = m_context.newTag();
|
||||
if (structured)
|
||||
evmasm::AssemblyItem panicTag = m_context.newTag();
|
||||
if (error || panic)
|
||||
// Note that this function returns zero on failure, which is not a problem yet,
|
||||
// but will be a problem once we allow user-defined errors.
|
||||
m_context.callYulFunction(m_context.utilFunctions().returnDataSelectorFunction(), 0, 1);
|
||||
// stack: <selector>
|
||||
if (error)
|
||||
{
|
||||
solAssert(
|
||||
structured->parameters() &&
|
||||
structured->parameters()->parameters().size() == 1 &&
|
||||
structured->parameters()->parameters().front() &&
|
||||
*structured->parameters()->parameters().front()->annotation().type == *TypeProvider::stringMemory(),
|
||||
error->parameters() &&
|
||||
error->parameters()->parameters().size() == 1 &&
|
||||
error->parameters()->parameters().front() &&
|
||||
*error->parameters()->parameters().front()->annotation().type == *TypeProvider::stringMemory(),
|
||||
""
|
||||
);
|
||||
solAssert(m_context.evmVersion().supportsReturndata(), "");
|
||||
|
||||
// stack: <selector>
|
||||
m_context << Instruction::DUP1 << selectorFromSignature32("Error(string)") << Instruction::EQ;
|
||||
m_context << Instruction::ISZERO;
|
||||
m_context.appendConditionalJumpTo(panicTag);
|
||||
m_context << Instruction::POP; // remove selector
|
||||
|
||||
// Try to decode the error message.
|
||||
// If this fails, leaves 0 on the stack, otherwise the pointer to the data string.
|
||||
m_context.callYulFunction(m_context.utilFunctions().tryDecodeErrorMessageFunction(), 0, 1);
|
||||
@ -1006,9 +1021,42 @@ void ContractCompiler::handleCatch(vector<ASTPointer<TryCatchClause>> const& _ca
|
||||
m_context.adjustStackOffset(1);
|
||||
|
||||
m_context << decodeSuccessTag;
|
||||
structured->accept(*this);
|
||||
error->accept(*this);
|
||||
m_context.appendJumpTo(endTag);
|
||||
m_context.adjustStackOffset(1);
|
||||
}
|
||||
m_context << panicTag;
|
||||
if (panic)
|
||||
{
|
||||
solAssert(
|
||||
panic->parameters() &&
|
||||
panic->parameters()->parameters().size() == 1 &&
|
||||
panic->parameters()->parameters().front() &&
|
||||
*panic->parameters()->parameters().front()->annotation().type == *TypeProvider::uint256(),
|
||||
""
|
||||
);
|
||||
solAssert(m_context.evmVersion().supportsReturndata(), "");
|
||||
|
||||
// stack: <selector>
|
||||
m_context << selectorFromSignature32("Panic(uint256)") << Instruction::EQ;
|
||||
m_context << Instruction::ISZERO;
|
||||
m_context.appendConditionalJumpTo(fallbackTag);
|
||||
|
||||
m_context.callYulFunction(m_context.utilFunctions().tryDecodePanicDataFunction(), 0, 2);
|
||||
m_context << Instruction::SWAP1;
|
||||
// stack: <code> <success>
|
||||
AssemblyItem decodeSuccessTag = m_context.appendConditionalJump();
|
||||
m_context << Instruction::POP;
|
||||
m_context.appendJumpTo(fallbackTag);
|
||||
m_context.adjustStackOffset(1);
|
||||
|
||||
m_context << decodeSuccessTag;
|
||||
panic->accept(*this);
|
||||
m_context.appendJumpTo(endTag);
|
||||
m_context.adjustStackOffset(1);
|
||||
}
|
||||
if (error || panic)
|
||||
m_context << Instruction::POP; // selector
|
||||
m_context << fallbackTag;
|
||||
if (fallback)
|
||||
{
|
||||
|
@ -3985,19 +3985,36 @@ string YulUtilFunctions::panicFunction(util::PanicCode _code)
|
||||
});
|
||||
}
|
||||
|
||||
string YulUtilFunctions::returnDataSelectorFunction()
|
||||
{
|
||||
string const functionName = "return_data_selector";
|
||||
solAssert(m_evmVersion.supportsReturndata(), "");
|
||||
|
||||
return m_functionCollector.createFunction(functionName, [&]() {
|
||||
return util::Whiskers(R"(
|
||||
function <functionName>() -> sig {
|
||||
if gt(returndatasize(), 3) {
|
||||
returndatacopy(0, 0, 4)
|
||||
sig := <shr224>(mload(0))
|
||||
}
|
||||
}
|
||||
)")
|
||||
("functionName", functionName)
|
||||
("shr224", shiftRightFunction(224))
|
||||
.render();
|
||||
});
|
||||
}
|
||||
|
||||
string YulUtilFunctions::tryDecodeErrorMessageFunction()
|
||||
{
|
||||
string const functionName = "try_decode_error_message";
|
||||
solAssert(m_evmVersion.supportsReturndata(), "");
|
||||
|
||||
return m_functionCollector.createFunction(functionName, [&]() {
|
||||
return util::Whiskers(R"(
|
||||
function <functionName>() -> ret {
|
||||
if lt(returndatasize(), 0x44) { leave }
|
||||
|
||||
returndatacopy(0, 0, 4)
|
||||
let sig := <shr224>(mload(0))
|
||||
if iszero(eq(sig, 0x<ErrorSignature>)) { leave }
|
||||
|
||||
let data := mload(<freeMemoryPointer>)
|
||||
returndatacopy(data, 4, sub(returndatasize(), 4))
|
||||
|
||||
@ -4021,14 +4038,32 @@ string YulUtilFunctions::tryDecodeErrorMessageFunction()
|
||||
}
|
||||
)")
|
||||
("functionName", functionName)
|
||||
("shr224", shiftRightFunction(224))
|
||||
("ErrorSignature", FixedHash<4>(util::keccak256("Error(string)")).hex())
|
||||
("freeMemoryPointer", to_string(CompilerUtils::freeMemoryPointer))
|
||||
("roundUp", roundUpFunction())
|
||||
.render();
|
||||
});
|
||||
}
|
||||
|
||||
string YulUtilFunctions::tryDecodePanicDataFunction()
|
||||
{
|
||||
string const functionName = "try_decode_panic_data";
|
||||
solAssert(m_evmVersion.supportsReturndata(), "");
|
||||
|
||||
return m_functionCollector.createFunction(functionName, [&]() {
|
||||
return util::Whiskers(R"(
|
||||
function <functionName>() -> success, data {
|
||||
if gt(returndatasize(), 0x23) {
|
||||
returndatacopy(0, 4, 0x20)
|
||||
success := 1
|
||||
data := mload(0)
|
||||
}
|
||||
}
|
||||
)")
|
||||
("functionName", functionName)
|
||||
.render();
|
||||
});
|
||||
}
|
||||
|
||||
string YulUtilFunctions::extractReturndataFunction()
|
||||
{
|
||||
string const functionName = "extract_returndata";
|
||||
|
@ -440,13 +440,21 @@ public:
|
||||
/// 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
|
||||
///
|
||||
/// Returns a newly allocated `bytes memory` array containing the decoded error message
|
||||
/// or 0 on failure.
|
||||
/// @returns the name of a function that returns the return data selector.
|
||||
/// Returns zero if return data is too short.
|
||||
std::string returnDataSelectorFunction();
|
||||
|
||||
/// @returns the name of a function that tries to abi-decode a string from offset 4 in the
|
||||
/// return data. On failure, returns 0, otherwise a pointer to the newly allocated string.
|
||||
/// Does not check the return data signature.
|
||||
/// signature: () -> ptr
|
||||
std::string tryDecodeErrorMessageFunction();
|
||||
|
||||
/// @returns the name of a function that tries to abi-decode a uint256 value from offset 4 in the
|
||||
/// return data.
|
||||
/// Does not check the return data signature.
|
||||
/// signature: () -> success, value
|
||||
std::string tryDecodePanicDataFunction();
|
||||
|
||||
/// Returns a function name that returns a newly allocated `bytes` array that contains the return data.
|
||||
///
|
||||
|
@ -2969,46 +2969,53 @@ bool IRGeneratorForStatements::visit(TryStatement const& _tryStatement)
|
||||
|
||||
void IRGeneratorForStatements::handleCatch(TryStatement const& _tryStatement)
|
||||
{
|
||||
if (_tryStatement.structuredClause())
|
||||
handleCatchStructuredAndFallback(*_tryStatement.structuredClause(), _tryStatement.fallbackClause());
|
||||
else if (_tryStatement.fallbackClause())
|
||||
handleCatchFallback(*_tryStatement.fallbackClause());
|
||||
else
|
||||
rethrow();
|
||||
}
|
||||
string const runFallback = m_context.newYulVariable();
|
||||
m_code << "let " << runFallback << " := 1\n";
|
||||
|
||||
void IRGeneratorForStatements::handleCatchStructuredAndFallback(
|
||||
TryCatchClause const& _structured,
|
||||
TryCatchClause const* _fallback
|
||||
)
|
||||
{
|
||||
solAssert(
|
||||
_structured.parameters() &&
|
||||
_structured.parameters()->parameters().size() == 1 &&
|
||||
_structured.parameters()->parameters().front() &&
|
||||
*_structured.parameters()->parameters().front()->annotation().type == *TypeProvider::stringMemory(),
|
||||
""
|
||||
);
|
||||
solAssert(m_context.evmVersion().supportsReturndata(), "");
|
||||
// This function returns zero on "short returndata". We have to add a success flag
|
||||
// once we implement custom error codes.
|
||||
if (_tryStatement.errorClause() || _tryStatement.panicClause())
|
||||
m_code << "switch " << m_utils.returnDataSelectorFunction() << "()\n";
|
||||
|
||||
// Try to decode the error message.
|
||||
// If this fails, leaves 0 on the stack, otherwise the pointer to the data string.
|
||||
string const dataVariable = m_context.newYulVariable();
|
||||
|
||||
m_code << "let " << dataVariable << " := " << m_utils.tryDecodeErrorMessageFunction() << "()\n";
|
||||
m_code << "switch iszero(" << dataVariable << ") \n";
|
||||
m_code << "case 0 { // decoding success\n";
|
||||
if (_structured.parameters())
|
||||
if (TryCatchClause const* errorClause = _tryStatement.errorClause())
|
||||
{
|
||||
solAssert(_structured.parameters()->parameters().size() == 1, "");
|
||||
IRVariable const& var = m_context.addLocalVariable(*_structured.parameters()->parameters().front());
|
||||
define(var) << dataVariable << "\n";
|
||||
m_code << "case " << selectorFromSignature32("Error(string)") << " {\n";
|
||||
string const dataVariable = m_context.newYulVariable();
|
||||
m_code << "let " << dataVariable << " := " << m_utils.tryDecodeErrorMessageFunction() << "()\n";
|
||||
m_code << "if " << dataVariable << " {\n";
|
||||
m_code << runFallback << " := 0\n";
|
||||
if (errorClause->parameters())
|
||||
{
|
||||
solAssert(errorClause->parameters()->parameters().size() == 1, "");
|
||||
IRVariable const& var = m_context.addLocalVariable(*errorClause->parameters()->parameters().front());
|
||||
define(var) << dataVariable << "\n";
|
||||
}
|
||||
errorClause->accept(*this);
|
||||
m_code << "}\n";
|
||||
m_code << "}\n";
|
||||
}
|
||||
_structured.accept(*this);
|
||||
m_code << "}\n";
|
||||
m_code << "default { // decoding failure\n";
|
||||
if (_fallback)
|
||||
handleCatchFallback(*_fallback);
|
||||
if (TryCatchClause const* panicClause = _tryStatement.panicClause())
|
||||
{
|
||||
m_code << "case " << selectorFromSignature32("Panic(uint256)") << " {\n";
|
||||
string const success = m_context.newYulVariable();
|
||||
string const code = m_context.newYulVariable();
|
||||
m_code << "let " << success << ", " << code << " := " << m_utils.tryDecodePanicDataFunction() << "()\n";
|
||||
m_code << "if " << success << " {\n";
|
||||
m_code << runFallback << " := 0\n";
|
||||
if (panicClause->parameters())
|
||||
{
|
||||
solAssert(panicClause->parameters()->parameters().size() == 1, "");
|
||||
IRVariable const& var = m_context.addLocalVariable(*panicClause->parameters()->parameters().front());
|
||||
define(var) << code << "\n";
|
||||
}
|
||||
panicClause->accept(*this);
|
||||
m_code << "}\n";
|
||||
m_code << "}\n";
|
||||
}
|
||||
|
||||
m_code << "if " << runFallback << " {\n";
|
||||
if (_tryStatement.fallbackClause())
|
||||
handleCatchFallback(*_tryStatement.fallbackClause());
|
||||
else
|
||||
rethrow();
|
||||
m_code << "}\n";
|
||||
|
@ -100,10 +100,6 @@ public:
|
||||
private:
|
||||
/// Handles all catch cases of a try statement, except the success-case.
|
||||
void handleCatch(TryStatement const& _tryStatement);
|
||||
void handleCatchStructuredAndFallback(
|
||||
TryCatchClause const& _structured,
|
||||
TryCatchClause const* _fallback
|
||||
);
|
||||
void handleCatchFallback(TryCatchClause const& _fallback);
|
||||
|
||||
/// Generates code to rethrow an exception.
|
||||
|
89
test/libsolidity/semanticTests/tryCatch/malformed_error.sol
Normal file
89
test/libsolidity/semanticTests/tryCatch/malformed_error.sol
Normal file
@ -0,0 +1,89 @@
|
||||
contract C {
|
||||
function f(uint size) public pure {
|
||||
assembly {
|
||||
mstore(0, 0x08c379a000000000000000000000000000000000000000000000000000000000)
|
||||
mstore(4, 0x20)
|
||||
mstore(0x24, 7)
|
||||
mstore(0x44, "abcdefg")
|
||||
revert(0, size)
|
||||
}
|
||||
}
|
||||
function a() public returns (uint) {
|
||||
try this.f(3) {
|
||||
assert(false);
|
||||
} catch Panic(uint) {
|
||||
assert(false);
|
||||
} catch Error(string memory) {
|
||||
assert(false);
|
||||
} catch {
|
||||
assert(true);
|
||||
}
|
||||
}
|
||||
function b() public returns (uint) {
|
||||
try this.f(6) {
|
||||
assert(false);
|
||||
} catch Panic(uint) {
|
||||
assert(false);
|
||||
} catch Error(string memory) {
|
||||
assert(false);
|
||||
} catch {
|
||||
assert(true);
|
||||
}
|
||||
}
|
||||
function b2() public returns (uint) {
|
||||
try this.f(0x43) {
|
||||
assert(false);
|
||||
} catch Panic(uint) {
|
||||
assert(false);
|
||||
} catch Error(string memory) {
|
||||
assert(false);
|
||||
} catch {
|
||||
assert(true);
|
||||
}
|
||||
}
|
||||
function b3() public returns (string memory) {
|
||||
try this.f(0x4a) {
|
||||
assert(false);
|
||||
} catch Panic(uint) {
|
||||
assert(false);
|
||||
} catch Error(string memory) {
|
||||
assert(false);
|
||||
} catch {
|
||||
assert(true);
|
||||
}
|
||||
}
|
||||
function c() public returns (string memory) {
|
||||
try this.f(0x4b) {
|
||||
assert(false);
|
||||
} catch Panic(uint) {
|
||||
assert(false);
|
||||
} catch Error(string memory er) {
|
||||
assert(true);
|
||||
return er;
|
||||
} catch {
|
||||
assert(false);
|
||||
}
|
||||
}
|
||||
function d() public returns (string memory) {
|
||||
try this.f(0x100) {
|
||||
assert(false);
|
||||
} catch Panic(uint) {
|
||||
assert(false);
|
||||
} catch Error(string memory er) {
|
||||
assert(true);
|
||||
return er;
|
||||
} catch {
|
||||
assert(false);
|
||||
}
|
||||
}
|
||||
}
|
||||
// ====
|
||||
// EVMVersion: >=byzantium
|
||||
// compileViaYul: also
|
||||
// ----
|
||||
// a() -> 0x00
|
||||
// b() -> 0x00
|
||||
// b2() -> 0x00
|
||||
// b3() -> 0x20, 0x00
|
||||
// c() -> 0x20, 7, "abcdefg"
|
||||
// d() -> 0x20, 7, "abcdefg"
|
55
test/libsolidity/semanticTests/tryCatch/malformed_panic.sol
Normal file
55
test/libsolidity/semanticTests/tryCatch/malformed_panic.sol
Normal file
@ -0,0 +1,55 @@
|
||||
contract C {
|
||||
function f(uint size) public pure {
|
||||
assembly {
|
||||
mstore(0, 0x4e487b7100000000000000000000000000000000000000000000000000000000)
|
||||
mstore(4, 0x43)
|
||||
revert(0, size)
|
||||
}
|
||||
}
|
||||
function a() public returns (uint) {
|
||||
try this.f(3) {
|
||||
assert(false);
|
||||
} catch Panic(uint) {
|
||||
assert(false);
|
||||
} catch {
|
||||
assert(true);
|
||||
}
|
||||
}
|
||||
function b() public returns (uint) {
|
||||
try this.f(6) {
|
||||
assert(false);
|
||||
} catch Panic(uint) {
|
||||
assert(false);
|
||||
} catch {
|
||||
assert(true);
|
||||
}
|
||||
}
|
||||
function c() public returns (uint) {
|
||||
try this.f(0x24) {
|
||||
assert(false);
|
||||
} catch Panic(uint c) {
|
||||
assert(true);
|
||||
return c;
|
||||
} catch {
|
||||
assert(false);
|
||||
}
|
||||
}
|
||||
function d() public returns (uint) {
|
||||
try this.f(0x100) {
|
||||
assert(false);
|
||||
} catch Panic(uint c) {
|
||||
assert(true);
|
||||
return c;
|
||||
} catch {
|
||||
assert(false);
|
||||
}
|
||||
}
|
||||
}
|
||||
// ====
|
||||
// EVMVersion: >=byzantium
|
||||
// compileViaYul: also
|
||||
// ----
|
||||
// a() -> 0x00
|
||||
// b() -> 0x00
|
||||
// c() -> 0x43
|
||||
// d() -> 0x43
|
@ -0,0 +1,53 @@
|
||||
contract C {
|
||||
function f(uint size) public pure {
|
||||
assembly {
|
||||
mstore(0, 0x4e487b7100000000000000000000000000000000000000000000000000000000)
|
||||
mstore(4, 0x43)
|
||||
revert(0, size)
|
||||
}
|
||||
}
|
||||
function a() public returns (uint) {
|
||||
try this.f(3) {
|
||||
assert(false);
|
||||
} catch Panic(uint) {
|
||||
assert(false);
|
||||
}
|
||||
// Error will be re-thrown, since there is no low-level catch clause
|
||||
assert(false);
|
||||
}
|
||||
function b() public returns (uint) {
|
||||
try this.f(6) {
|
||||
assert(false);
|
||||
} catch Panic(uint) {
|
||||
assert(false);
|
||||
}
|
||||
// Error will be re-thrown, since there is no low-level catch clause
|
||||
assert(false);
|
||||
}
|
||||
function c() public returns (uint) {
|
||||
try this.f(0x24) {
|
||||
assert(false);
|
||||
} catch Panic(uint c) {
|
||||
assert(true);
|
||||
return c;
|
||||
}
|
||||
assert(false);
|
||||
}
|
||||
function d() public returns (uint) {
|
||||
try this.f(0x100) {
|
||||
assert(false);
|
||||
} catch Panic(uint c) {
|
||||
assert(true);
|
||||
return c;
|
||||
}
|
||||
assert(false);
|
||||
}
|
||||
}
|
||||
// ====
|
||||
// EVMVersion: >=byzantium
|
||||
// compileViaYul: also
|
||||
// ----
|
||||
// a() -> FAILURE, hex"4e487b"
|
||||
// b() -> FAILURE, hex"4e487b710000"
|
||||
// c() -> 0x43
|
||||
// d() -> 0x43
|
@ -0,0 +1,59 @@
|
||||
contract C {
|
||||
function f(uint size) public pure {
|
||||
assembly {
|
||||
mstore(0, 0x4e487b7100000000000000000000000000000000000000000000000000000000)
|
||||
mstore(4, 0x43)
|
||||
revert(0, size)
|
||||
}
|
||||
}
|
||||
function a() public returns (uint) {
|
||||
try this.f(3) {
|
||||
assert(false);
|
||||
} catch Panic(uint) {
|
||||
assert(false);
|
||||
} catch Error(string memory) {
|
||||
assert(false);
|
||||
}
|
||||
// Error will be re-thrown, since there is no low-level catch clause
|
||||
assert(false);
|
||||
}
|
||||
function b() public returns (uint) {
|
||||
try this.f(6) {
|
||||
assert(false);
|
||||
} catch Panic(uint) {
|
||||
assert(false);
|
||||
} catch Error(string memory) {
|
||||
assert(false);
|
||||
}
|
||||
// Error will be re-thrown, since there is no low-level catch clause
|
||||
assert(false);
|
||||
}
|
||||
function c() public returns (uint) {
|
||||
try this.f(0x24) {
|
||||
assert(false);
|
||||
} catch Panic(uint c) {
|
||||
assert(true);
|
||||
return c;
|
||||
} catch Error(string memory) {
|
||||
assert(false);
|
||||
}
|
||||
}
|
||||
function d() public returns (uint) {
|
||||
try this.f(0x100) {
|
||||
assert(false);
|
||||
} catch Panic(uint c) {
|
||||
assert(true);
|
||||
return c;
|
||||
} catch Error(string memory) {
|
||||
assert(false);
|
||||
}
|
||||
}
|
||||
}
|
||||
// ====
|
||||
// EVMVersion: >=byzantium
|
||||
// compileViaYul: also
|
||||
// ----
|
||||
// a() -> FAILURE, hex"4e487b"
|
||||
// b() -> FAILURE, hex"4e487b710000"
|
||||
// c() -> 0x43
|
||||
// d() -> 0x43
|
@ -0,0 +1,62 @@
|
||||
contract C {
|
||||
function f(uint size) public pure {
|
||||
assembly {
|
||||
mstore(0, 0x4e487b7100000000000000000000000000000000000000000000000000000000)
|
||||
mstore(4, 0x43)
|
||||
revert(0, size)
|
||||
}
|
||||
}
|
||||
function a() public returns (uint) {
|
||||
try this.f(3) {
|
||||
assert(false);
|
||||
} catch Panic(uint) {
|
||||
assert(false);
|
||||
} catch Error(string memory) {
|
||||
assert(false);
|
||||
} catch {
|
||||
assert(true);
|
||||
}
|
||||
}
|
||||
function b() public returns (uint) {
|
||||
try this.f(6) {
|
||||
assert(false);
|
||||
} catch Panic(uint) {
|
||||
assert(false);
|
||||
} catch Error(string memory) {
|
||||
assert(false);
|
||||
} catch {
|
||||
assert(true);
|
||||
}
|
||||
}
|
||||
function c() public returns (uint) {
|
||||
try this.f(0x24) {
|
||||
assert(false);
|
||||
} catch Panic(uint c) {
|
||||
assert(true);
|
||||
return c;
|
||||
} catch Error(string memory) {
|
||||
assert(false);
|
||||
} catch {
|
||||
assert(false);
|
||||
}
|
||||
}
|
||||
function d() public returns (uint) {
|
||||
try this.f(0x100) {
|
||||
assert(false);
|
||||
} catch Panic(uint c) {
|
||||
assert(true);
|
||||
return c;
|
||||
} catch Error(string memory) {
|
||||
assert(false);
|
||||
} catch {
|
||||
assert(false);
|
||||
}
|
||||
}
|
||||
}
|
||||
// ====
|
||||
// EVMVersion: >=byzantium
|
||||
// compileViaYul: also
|
||||
// ----
|
||||
// a() -> 0x00
|
||||
// b() -> 0x00
|
||||
// c() -> 0x43
|
34
test/libsolidity/semanticTests/tryCatch/panic.sol
Normal file
34
test/libsolidity/semanticTests/tryCatch/panic.sol
Normal file
@ -0,0 +1,34 @@
|
||||
contract C {
|
||||
function uf(bool b, uint x, uint y) public pure returns (uint) {
|
||||
require(b, "failure");
|
||||
return x - y;
|
||||
}
|
||||
function onlyPanic(bool b, uint x, uint y) public returns (uint r, uint code) {
|
||||
try this.uf(b, x, y) returns (uint b) {
|
||||
r = b;
|
||||
} catch Panic(uint c) {
|
||||
code = c;
|
||||
}
|
||||
}
|
||||
function panicAndError(bool b, uint x, uint y) public returns (uint r, uint code, string memory msg_) {
|
||||
try this.uf(b, x, y) returns (uint b) {
|
||||
r = b;
|
||||
} catch Panic(uint c) {
|
||||
code = c;
|
||||
} catch Error(string memory _errmsg) {
|
||||
msg_ = _errmsg;
|
||||
}
|
||||
}
|
||||
}
|
||||
// ====
|
||||
// EVMVersion: >=byzantium
|
||||
// compileViaYul: also
|
||||
// ----
|
||||
// onlyPanic(bool,uint256,uint256): true, 7, 6 -> 1, 0x00
|
||||
// onlyPanic(bool,uint256,uint256): true, 6, 7 -> 0x00, 0x11
|
||||
// onlyPanic(bool,uint256,uint256): false, 7, 6 -> FAILURE, hex"08c379a0", 0x20, 7, "failure"
|
||||
// onlyPanic(bool,uint256,uint256): false, 6, 7 -> FAILURE, hex"08c379a0", 0x20, 7, "failure"
|
||||
// panicAndError(bool,uint256,uint256): true, 7, 6 -> 1, 0x00, 0x60, 0x00
|
||||
// panicAndError(bool,uint256,uint256): true, 6, 7 -> 0x00, 0x11, 0x60, 0x00
|
||||
// panicAndError(bool,uint256,uint256): false, 7, 6 -> 0x00, 0x00, 0x60, 7, "failure"
|
||||
// panicAndError(bool,uint256,uint256): false, 6, 7 -> 0x00, 0x00, 0x60, 7, "failure"
|
13
test/libsolidity/syntaxTests/tryCatch/double_panic.sol
Normal file
13
test/libsolidity/syntaxTests/tryCatch/double_panic.sol
Normal file
@ -0,0 +1,13 @@
|
||||
contract C {
|
||||
function f() public {
|
||||
try this.f() {
|
||||
} catch Panic(bytes memory) {
|
||||
} catch Panic(uint) {
|
||||
}
|
||||
}
|
||||
}
|
||||
// ====
|
||||
// EVMVersion: >=byzantium
|
||||
// ----
|
||||
// TypeError 1271: (72-109): Expected `catch Panic(uint ...) { ... }`.
|
||||
// TypeError 6732: (110-139): This try statement already has a "Panic" catch clause.
|
@ -0,0 +1,12 @@
|
||||
contract C {
|
||||
function f() public {
|
||||
try this.f() {
|
||||
} catch Panic(uint) {
|
||||
} catch Panic(uint) {
|
||||
}
|
||||
}
|
||||
}
|
||||
// ====
|
||||
// EVMVersion: >=byzantium
|
||||
// ----
|
||||
// TypeError 6732: (102-131): This try statement already has a "Panic" catch clause.
|
11
test/libsolidity/syntaxTests/tryCatch/error_and_panic.sol
Normal file
11
test/libsolidity/syntaxTests/tryCatch/error_and_panic.sol
Normal file
@ -0,0 +1,11 @@
|
||||
contract C {
|
||||
function f() public {
|
||||
try this.f() {
|
||||
} catch Error(string memory) {
|
||||
} catch Panic(uint) {
|
||||
}
|
||||
}
|
||||
}
|
||||
// ====
|
||||
// EVMVersion: >=byzantium
|
||||
// ----
|
@ -7,5 +7,5 @@ contract C {
|
||||
}
|
||||
}
|
||||
// ----
|
||||
// TypeError 3542: (93-119): Invalid catch clause name. Expected either `catch (...)` or `catch Error(...)`.
|
||||
// TypeError 3542: (120-143): Invalid catch clause name. Expected either `catch (...)` or `catch Error(...)`.
|
||||
// TypeError 3542: (93-119): Invalid catch clause name. Expected either `catch (...)`, `catch Error(...)`, or `catch Panic(...)`.
|
||||
// TypeError 3542: (120-143): Invalid catch clause name. Expected either `catch (...)`, `catch Error(...)`, or `catch Panic(...)`.
|
||||
|
11
test/libsolidity/syntaxTests/tryCatch/wrong_panic.sol
Normal file
11
test/libsolidity/syntaxTests/tryCatch/wrong_panic.sol
Normal file
@ -0,0 +1,11 @@
|
||||
contract C {
|
||||
function f() public {
|
||||
try this.f() {
|
||||
} catch Panic() {
|
||||
}
|
||||
}
|
||||
}
|
||||
// ====
|
||||
// EVMVersion: >=byzantium
|
||||
// ----
|
||||
// TypeError 1271: (72-97): Expected `catch Panic(uint ...) { ... }`.
|
11
test/libsolidity/syntaxTests/tryCatch/wrong_panic_2.sol
Normal file
11
test/libsolidity/syntaxTests/tryCatch/wrong_panic_2.sol
Normal file
@ -0,0 +1,11 @@
|
||||
contract C {
|
||||
function f() public {
|
||||
try this.f() {
|
||||
} catch Panic(bytes memory) {
|
||||
}
|
||||
}
|
||||
}
|
||||
// ====
|
||||
// EVMVersion: >=byzantium
|
||||
// ----
|
||||
// TypeError 1271: (72-109): Expected `catch Panic(uint ...) { ... }`.
|
Loading…
Reference in New Issue
Block a user