diff --git a/Zend/tests/try/catch_novar_1.phpt b/Zend/tests/try/catch_novar_1.phpt new file mode 100644 index 0000000000000..80e93f3b959b7 --- /dev/null +++ b/Zend/tests/try/catch_novar_1.phpt @@ -0,0 +1,31 @@ +--TEST-- +catch without capturing a variable +--FILE-- + +--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 diff --git a/Zend/zend_ast.c b/Zend/zend_ast.c index 2b5d1ea3242e4..7451cf28e92be 100644 --- a/Zend/zend_ast.c +++ b/Zend/zend_ast.c @@ -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); diff --git a/Zend/zend_compile.c b/Zend/zend_compile.c index cc7eb6f1d59d3..7638bd16fc5eb 100644 --- a/Zend/zend_compile.c +++ b/Zend/zend_compile.c @@ -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); @@ -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; diff --git a/Zend/zend_language_parser.y b/Zend/zend_language_parser.y index 78b6e758ba9dc..18290acc446f5 100644 --- a/Zend/zend_language_parser.y +++ b/Zend/zend_language_parser.y @@ -248,7 +248,7 @@ static YYSIZE_T zend_yytnamerr(char*, const char*); %type encaps_var encaps_var_offset isset_variables %type top_statement_list use_declarations const_list inner_statement_list if_stmt %type alt_if_stmt for_exprs switch_case_list global_var_list static_var_list -%type echo_expr_list unset_variables catch_name_list catch_list parameter_list class_statement_list +%type echo_expr_list unset_variables catch_name_list catch_list optional_variable parameter_list class_statement_list %type implements_list case_list if_stmt_without_else %type non_empty_parameter_list argument_list non_empty_argument_list property_list %type class_const_list class_const_decl class_name_list trait_adaptations method_body non_empty_for_exprs @@ -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)); } ; @@ -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; } diff --git a/Zend/zend_vm_def.h b/Zend/zend_vm_def.h index ed160f18da338..f7831af1aa6dd 100644 --- a/Zend/zend_vm_def.h +++ b/Zend/zend_vm_def.h @@ -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 */ @@ -4486,18 +4485,24 @@ ZEND_VM_HANDLER(107, ZEND_CATCH, CONST, JMP_ADDR, LAST_CATCH|CACHE_SLOT) } exception = EG(exception); - ex = EX_VAR(opline->result.var); - if (UNEXPECTED(Z_ISREF_P(ex))) { - ex = Z_REFVAL_P(ex); - } - zval_ptr_dtor(ex); - ZVAL_OBJ(ex, EG(exception)); - if (UNEXPECTED(EG(exception) != exception)) { - ZVAL_UNDEF(ex); - HANDLE_EXCEPTION(); + if (RETURN_VALUE_USED(opline)) { + zval *ex = EX_VAR(opline->result.var); + if (UNEXPECTED(Z_ISREF_P(ex))) { + ex = Z_REFVAL_P(ex); + } + zval_ptr_dtor(ex); + ZVAL_OBJ(ex, EG(exception)); + if (UNEXPECTED(EG(exception) != exception)) { + ZVAL_UNDEF(ex); + HANDLE_EXCEPTION(); + } else { + EG(exception) = NULL; + ZEND_VM_NEXT_OPCODE(); + } } else { EG(exception) = NULL; - ZEND_VM_NEXT_OPCODE(); + OBJ_RELEASE(exception); + ZEND_VM_NEXT_OPCODE_CHECK_EXCEPTION(); } } diff --git a/Zend/zend_vm_execute.h b/Zend/zend_vm_execute.h index d4077f5605f50..861ce3e5402c9 100644 --- a/Zend/zend_vm_execute.h +++ b/Zend/zend_vm_execute.h @@ -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 */ @@ -3702,18 +3701,24 @@ static ZEND_OPCODE_HANDLER_RET ZEND_FASTCALL ZEND_CATCH_SPEC_CONST_HANDLER(ZEND_ } exception = EG(exception); - ex = EX_VAR(opline->result.var); - if (UNEXPECTED(Z_ISREF_P(ex))) { - ex = Z_REFVAL_P(ex); - } - zval_ptr_dtor(ex); - ZVAL_OBJ(ex, EG(exception)); - if (UNEXPECTED(EG(exception) != exception)) { - ZVAL_UNDEF(ex); - HANDLE_EXCEPTION(); + if (RETURN_VALUE_USED(opline)) { + zval *ex = EX_VAR(opline->result.var); + if (UNEXPECTED(Z_ISREF_P(ex))) { + ex = Z_REFVAL_P(ex); + } + zval_ptr_dtor(ex); + ZVAL_OBJ(ex, EG(exception)); + if (UNEXPECTED(EG(exception) != exception)) { + ZVAL_UNDEF(ex); + HANDLE_EXCEPTION(); + } else { + EG(exception) = NULL; + ZEND_VM_NEXT_OPCODE(); + } } else { EG(exception) = NULL; - ZEND_VM_NEXT_OPCODE(); + OBJ_RELEASE(exception); + ZEND_VM_NEXT_OPCODE_CHECK_EXCEPTION(); } }