Skip to content

Commit

Permalink
Support catching exceptions without capturing them to variables
Browse files Browse the repository at this point in the history
  • Loading branch information
MaxSem authored and nikic committed May 26, 2020
1 parent 1203bbf commit 23ee4d4
Show file tree
Hide file tree
Showing 8 changed files with 87 additions and 20 deletions.
3 changes: 3 additions & 0 deletions UPGRADING
Original file line number Diff line number Diff line change
Expand Up @@ -509,6 +509,9 @@ PHP 8.0 UPGRADE NOTES
RFC: https://wiki.php.net/rfc/throw_expression
. An optional trailing comma is now allowed in parameter lists.
RFC: https://wiki.php.net/rfc/trailing_comma_in_parameter_list
. It is now possible to write `catch (Exception)` to catch an exception
without storing it in a variable.
RFC: https://wiki.php.net/rfc/non-capturing_catches

- Date:
. Added DateTime::createFromInterface() and
Expand Down
31 changes: 31 additions & 0 deletions Zend/tests/try/catch_novar_1.phpt
Original file line number Diff line number Diff line change
@@ -0,0 +1,31 @@
--TEST--
catch without capturing a variable
--FILE--
<?php

try {
throw new Exception();
} catch (Exception) {
echo "Exception\n";
}

try {
throw new Exception();
} catch (Exception) {
echo "Exception\n";
} catch (Error) {
echo "FAIL\n";
}

try {
throw new Exception();
} catch (Exception|Error) {
echo "Exception\n";
} catch (Throwable) {
echo "FAIL\n";
}

--EXPECT--
Exception
Exception
Exception
26 changes: 26 additions & 0 deletions Zend/tests/try/catch_novar_2.phpt
Original file line number Diff line number Diff line change
@@ -0,0 +1,26 @@
--TEST--
catch without capturing a variable - exception in destructor
--FILE--
<?php
class ThrowsOnDestruct extends Exception {
public function __destruct() {
echo "Throwing\n";
throw new RuntimeException(__METHOD__);
}
}
try {
throw new ThrowsOnDestruct();
} catch (Exception) {
echo "Unreachable catch\n";
}
echo "Unreachable fallthrough\n";

?>
--EXPECTF--
Throwing

Fatal error: Uncaught RuntimeException: ThrowsOnDestruct::__destruct in %s:%d
Stack trace:
#0 %s(%d): ThrowsOnDestruct->__destruct()
#1 {main}
thrown in %s on line %d
6 changes: 4 additions & 2 deletions Zend/zend_ast.c
Original file line number Diff line number Diff line change
Expand Up @@ -1993,8 +1993,10 @@ static ZEND_COLD void zend_ast_export_ex(smart_str *str, zend_ast *ast, int prio
case ZEND_AST_CATCH:
smart_str_appends(str, "} catch (");
zend_ast_export_catch_name_list(str, zend_ast_get_list(ast->child[0]), indent);
smart_str_appends(str, " $");
zend_ast_export_var(str, ast->child[1], 0, indent);
if (ast->child[1]) {
smart_str_appends(str, " $");
zend_ast_export_var(str, ast->child[1], 0, indent);
}
smart_str_appends(str, ") {\n");
zend_ast_export_stmt(str, ast->child[2], indent + 1);
zend_ast_export_indent(str, indent);
Expand Down
8 changes: 4 additions & 4 deletions Zend/zend_compile.c
Original file line number Diff line number Diff line change
Expand Up @@ -5213,7 +5213,7 @@ void zend_compile_try(zend_ast *ast) /* {{{ */
zend_ast_list *classes = zend_ast_get_list(catch_ast->child[0]);
zend_ast *var_ast = catch_ast->child[1];
zend_ast *stmt_ast = catch_ast->child[2];
zend_string *var_name = zval_make_interned_string(zend_ast_get_zval(var_ast));
zend_string *var_name = var_ast ? zval_make_interned_string(zend_ast_get_zval(var_ast)) : NULL;
zend_bool is_last_catch = (i + 1 == catches->children);

uint32_t *jmp_multicatch = safe_emalloc(sizeof(uint32_t), classes->children - 1, 0);
Expand Down Expand Up @@ -5241,12 +5241,12 @@ void zend_compile_try(zend_ast *ast) /* {{{ */
zend_resolve_class_name_ast(class_ast));
opline->extended_value = zend_alloc_cache_slot();

if (zend_string_equals_literal(var_name, "this")) {
if (var_name && zend_string_equals_literal(var_name, "this")) {
zend_error_noreturn(E_COMPILE_ERROR, "Cannot re-assign $this");
}

opline->result_type = IS_CV;
opline->result.var = lookup_cv(var_name);
opline->result_type = var_name ? IS_CV : IS_UNUSED;
opline->result.var = var_name ? lookup_cv(var_name) : -1;

if (is_last_catch && is_last_class) {
opline->extended_value |= ZEND_LAST_CATCH;
Expand Down
9 changes: 7 additions & 2 deletions Zend/zend_language_parser.y
Original file line number Diff line number Diff line change
Expand Up @@ -248,7 +248,7 @@ static YYSIZE_T zend_yytnamerr(char*, const char*);
%type <ast> encaps_var encaps_var_offset isset_variables
%type <ast> top_statement_list use_declarations const_list inner_statement_list if_stmt
%type <ast> alt_if_stmt for_exprs switch_case_list global_var_list static_var_list
%type <ast> echo_expr_list unset_variables catch_name_list catch_list parameter_list class_statement_list
%type <ast> echo_expr_list unset_variables catch_name_list catch_list optional_variable parameter_list class_statement_list
%type <ast> implements_list case_list if_stmt_without_else
%type <ast> non_empty_parameter_list argument_list non_empty_argument_list property_list
%type <ast> class_const_list class_const_decl class_name_list trait_adaptations method_body non_empty_for_exprs
Expand Down Expand Up @@ -465,7 +465,7 @@ statement:
catch_list:
%empty
{ $$ = zend_ast_create_list(0, ZEND_AST_CATCH_LIST); }
| catch_list T_CATCH '(' catch_name_list T_VARIABLE ')' '{' inner_statement_list '}'
| catch_list T_CATCH '(' catch_name_list optional_variable ')' '{' inner_statement_list '}'
{ $$ = zend_ast_list_add($1, zend_ast_create(ZEND_AST_CATCH, $4, $5, $8)); }
;

Expand All @@ -474,6 +474,11 @@ catch_name_list:
| catch_name_list '|' class_name { $$ = zend_ast_list_add($1, $3); }
;

optional_variable:
%empty { $$ = NULL; }
| T_VARIABLE { $$ = $1; }
;

finally_statement:
%empty { $$ = NULL; }
| T_FINALLY '{' inner_statement_list '}' { $$ = $3; }
Expand Down
12 changes: 6 additions & 6 deletions Zend/zend_vm_def.h
Original file line number Diff line number Diff line change
Expand Up @@ -4453,7 +4453,6 @@ ZEND_VM_HANDLER(107, ZEND_CATCH, CONST, JMP_ADDR, LAST_CATCH|CACHE_SLOT)
USE_OPLINE
zend_class_entry *ce, *catch_ce;
zend_object *exception;
zval *ex;

SAVE_OPLINE();
/* Check whether an exception has been thrown, if not, jump over code */
Expand Down Expand Up @@ -4486,17 +4485,18 @@ ZEND_VM_HANDLER(107, ZEND_CATCH, CONST, JMP_ADDR, LAST_CATCH|CACHE_SLOT)
}

exception = EG(exception);
ex = EX_VAR(opline->result.var);
{
EG(exception) = NULL;
if (RETURN_VALUE_USED(opline)) {
/* Always perform a strict assignment. There is a reasonable expectation that if you
* write "catch (Exception $e)" then $e will actually be instanceof Exception. As such,
* we should not permit coercion to string here. */
zval tmp;
ZVAL_OBJ(&tmp, exception);
EG(exception) = NULL;
zend_assign_to_variable(ex, &tmp, IS_TMP_VAR, /* strict */ 1);
ZEND_VM_NEXT_OPCODE_CHECK_EXCEPTION();
zend_assign_to_variable(EX_VAR(opline->result.var), &tmp, IS_TMP_VAR, /* strict */ 1);
} else {
OBJ_RELEASE(exception);
}
ZEND_VM_NEXT_OPCODE_CHECK_EXCEPTION();
}

ZEND_VM_HOT_HANDLER(65, ZEND_SEND_VAL, CONST|TMPVAR, NUM)
Expand Down
12 changes: 6 additions & 6 deletions Zend/zend_vm_execute.h
Original file line number Diff line number Diff line change
Expand Up @@ -3669,7 +3669,6 @@ static ZEND_OPCODE_HANDLER_RET ZEND_FASTCALL ZEND_CATCH_SPEC_CONST_HANDLER(ZEND_
USE_OPLINE
zend_class_entry *ce, *catch_ce;
zend_object *exception;
zval *ex;

SAVE_OPLINE();
/* Check whether an exception has been thrown, if not, jump over code */
Expand Down Expand Up @@ -3702,17 +3701,18 @@ static ZEND_OPCODE_HANDLER_RET ZEND_FASTCALL ZEND_CATCH_SPEC_CONST_HANDLER(ZEND_
}

exception = EG(exception);
ex = EX_VAR(opline->result.var);
{
EG(exception) = NULL;
if (RETURN_VALUE_USED(opline)) {
/* Always perform a strict assignment. There is a reasonable expectation that if you
* write "catch (Exception $e)" then $e will actually be instanceof Exception. As such,
* we should not permit coercion to string here. */
zval tmp;
ZVAL_OBJ(&tmp, exception);
EG(exception) = NULL;
zend_assign_to_variable(ex, &tmp, IS_TMP_VAR, /* strict */ 1);
ZEND_VM_NEXT_OPCODE_CHECK_EXCEPTION();
zend_assign_to_variable(EX_VAR(opline->result.var), &tmp, IS_TMP_VAR, /* strict */ 1);
} else {
OBJ_RELEASE(exception);
}
ZEND_VM_NEXT_OPCODE_CHECK_EXCEPTION();
}

static ZEND_VM_HOT ZEND_OPCODE_HANDLER_RET ZEND_FASTCALL ZEND_SEND_VAL_SPEC_CONST_HANDLER(ZEND_OPCODE_HANDLER_ARGS)
Expand Down

0 comments on commit 23ee4d4

Please sign in to comment.