diff options
-rw-r--r-- | Zend/tests/try/exceptions.inc | 5 | ||||
-rw-r--r-- | Zend/tests/try/try_multicatch_001.phpt | 19 | ||||
-rw-r--r-- | Zend/tests/try/try_multicatch_002.phpt | 21 | ||||
-rw-r--r-- | Zend/tests/try/try_multicatch_003.phpt | 21 | ||||
-rw-r--r-- | Zend/tests/try/try_multicatch_004.phpt | 21 | ||||
-rw-r--r-- | Zend/tests/try/try_multicatch_005.phpt | 25 | ||||
-rw-r--r-- | Zend/zend_ast.c | 9 | ||||
-rw-r--r-- | Zend/zend_compile.c | 55 | ||||
-rw-r--r-- | Zend/zend_language_parser.y | 9 |
9 files changed, 162 insertions, 23 deletions
diff --git a/Zend/tests/try/exceptions.inc b/Zend/tests/try/exceptions.inc new file mode 100644 index 0000000000..8a8777914c --- /dev/null +++ b/Zend/tests/try/exceptions.inc @@ -0,0 +1,5 @@ +<?php + +class Exception1 extends Exception {} +class Exception2 extends Exception {} +class Exception3 extends Exception {} diff --git a/Zend/tests/try/try_multicatch_001.phpt b/Zend/tests/try/try_multicatch_001.phpt new file mode 100644 index 0000000000..0dffd32c72 --- /dev/null +++ b/Zend/tests/try/try_multicatch_001.phpt @@ -0,0 +1,19 @@ +--TEST-- +Parsing test +--FILE-- +<?php + +require_once __DIR__ . '/exceptions.inc'; + +try { + echo 'TRY' . PHP_EOL; +} catch(Exception1 | Exception2 $e) { + echo 'Exception'; +} finally { + echo 'FINALLY' . PHP_EOL; +} + +?> +--EXPECT-- +TRY +FINALLY diff --git a/Zend/tests/try/try_multicatch_002.phpt b/Zend/tests/try/try_multicatch_002.phpt new file mode 100644 index 0000000000..0e70fec7eb --- /dev/null +++ b/Zend/tests/try/try_multicatch_002.phpt @@ -0,0 +1,21 @@ +--TEST-- +Catch first exception in the multicatch +--FILE-- +<?php + +require_once __DIR__ . '/exceptions.inc'; + +try { + echo 'TRY' . PHP_EOL; + throw new Exception1; +} catch(Exception1 | Exception2 | Exception3 $e) { + echo get_class($e) . PHP_EOL; +} finally { + echo 'FINALLY' . PHP_EOL; +} + +?> +--EXPECT-- +TRY +Exception1 +FINALLY diff --git a/Zend/tests/try/try_multicatch_003.phpt b/Zend/tests/try/try_multicatch_003.phpt new file mode 100644 index 0000000000..6aed1a2b09 --- /dev/null +++ b/Zend/tests/try/try_multicatch_003.phpt @@ -0,0 +1,21 @@ +--TEST-- +Catch second exception in the multicatch +--FILE-- +<?php + +require_once __DIR__ . '/exceptions.inc'; + +try { + echo 'TRY' . PHP_EOL; + throw new Exception2; +} catch(Exception1 | Exception2 | Exception3 $e) { + echo get_class($e) . PHP_EOL; +} finally { + echo 'FINALLY' . PHP_EOL; +} + +?> +--EXPECT-- +TRY +Exception2 +FINALLY diff --git a/Zend/tests/try/try_multicatch_004.phpt b/Zend/tests/try/try_multicatch_004.phpt new file mode 100644 index 0000000000..d8b245a767 --- /dev/null +++ b/Zend/tests/try/try_multicatch_004.phpt @@ -0,0 +1,21 @@ +--TEST-- +Catch last exception in the multicatch +--FILE-- +<?php + +require_once __DIR__ . '/exceptions.inc'; + +try { + echo 'TRY' . PHP_EOL; + throw new Exception3; +} catch(Exception1 | Exception2 | Exception3 $e) { + echo get_class($e) . PHP_EOL; +} finally { + echo 'FINALLY' . PHP_EOL; +} + +?> +--EXPECT-- +TRY +Exception3 +FINALLY diff --git a/Zend/tests/try/try_multicatch_005.phpt b/Zend/tests/try/try_multicatch_005.phpt new file mode 100644 index 0000000000..cc3fc890fa --- /dev/null +++ b/Zend/tests/try/try_multicatch_005.phpt @@ -0,0 +1,25 @@ +--TEST-- +Catch exception in the nested multicatch +--FILE-- +<?php + +require_once __DIR__ . '/exceptions.inc'; + +try { + try { + echo 'TRY' . PHP_EOL; + throw new Exception3; + } catch (Exception1 | Exception3 $e) { + echo get_class($e) . PHP_EOL; + } +} catch(Exception2 | Exception3 $e) { + echo 'Should never be executed'; +} finally { + echo 'FINALLY' . PHP_EOL; +} + +?> +--EXPECT-- +TRY +Exception3 +FINALLY diff --git a/Zend/zend_ast.c b/Zend/zend_ast.c index eee4a44e86..e7c6111919 100644 --- a/Zend/zend_ast.c +++ b/Zend/zend_ast.c @@ -776,19 +776,22 @@ static void zend_ast_export_encaps_list(smart_str *str, char quote, zend_ast_lis } } -static void zend_ast_export_name_list(smart_str *str, zend_ast_list *list, int indent) +static void zend_ast_export_name_list_ex(smart_str *str, zend_ast_list *list, int indent, const char *separator) { uint32_t i = 0; while (i < list->children) { if (i != 0) { - smart_str_appends(str, ", "); + smart_str_appends(str, separator); } zend_ast_export_name(str, list->child[i], 0, indent); i++; } } +#define zend_ast_export_name_list(s, l, i) zend_ast_export_name_list_ex(s, l, i, ", ") +#define zend_ast_export_catch_name_list(s, l, i) zend_ast_export_name_list_ex(s, l, i, "|") + static void zend_ast_export_var_list(smart_str *str, zend_ast_list *list, int indent) { uint32_t i = 0; @@ -1584,7 +1587,7 @@ simple_list: break; case ZEND_AST_CATCH: smart_str_appends(str, "} catch ("); - zend_ast_export_ns_name(str, ast->child[0], 0, indent); + zend_ast_export_catch_name_list(str, ast->child[0], indent); smart_str_appends(str, " $"); zend_ast_export_var(str, ast->child[1], 0, indent); smart_str_appends(str, ") {\n"); diff --git a/Zend/zend_compile.c b/Zend/zend_compile.c index 23781ba423..3adcc55f4e 100644 --- a/Zend/zend_compile.c +++ b/Zend/zend_compile.c @@ -4542,7 +4542,7 @@ void zend_compile_try(zend_ast *ast) /* {{{ */ zend_ast_list *catches = zend_ast_get_list(ast->child[1]); zend_ast *finally_ast = ast->child[2]; - uint32_t i; + uint32_t i, j; zend_op *opline; uint32_t try_catch_offset; uint32_t *jmp_opnums = safe_emalloc(sizeof(uint32_t), catches->children, 0); @@ -4587,34 +4587,53 @@ void zend_compile_try(zend_ast *ast) /* {{{ */ for (i = 0; i < catches->children; ++i) { zend_ast *catch_ast = catches->child[i]; - zend_ast *class_ast = catch_ast->child[0]; + 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]; zval *var_name = zend_ast_get_zval(var_ast); zend_bool is_last_catch = (i + 1 == catches->children); + uint32_t *jmp_multicatch = safe_emalloc(sizeof(uint32_t), classes->children - 1, 0); uint32_t opnum_catch; - if (!zend_is_const_default_class_ref(class_ast)) { - zend_error_noreturn(E_COMPILE_ERROR, "Bad class name in the catch statement"); - } + CG(zend_lineno) = catch_ast->lineno; - opnum_catch = get_next_op_number(CG(active_op_array)); - if (i == 0) { - CG(active_op_array)->try_catch_array[try_catch_offset].catch_op = opnum_catch; - } + for (j = 0; j < classes->children; j++) { - CG(zend_lineno) = catch_ast->lineno; + zend_ast *class_ast = classes->child[j]; + zend_bool is_last_class = (j + 1 == classes->children); - opline = get_next_op(CG(active_op_array)); - opline->opcode = ZEND_CATCH; - opline->op1_type = IS_CONST; - opline->op1.constant = zend_add_class_name_literal(CG(active_op_array), - zend_resolve_class_name_ast(class_ast)); + if (!zend_is_const_default_class_ref(class_ast)) { + zend_error_noreturn(E_COMPILE_ERROR, "Bad class name in the catch statement"); + } - opline->op2_type = IS_CV; - opline->op2.var = lookup_cv(CG(active_op_array), zend_string_copy(Z_STR_P(var_name))); - opline->result.num = is_last_catch; + opnum_catch = get_next_op_number(CG(active_op_array)); + if (i == 0 && j == 0) { + CG(active_op_array)->try_catch_array[try_catch_offset].catch_op = opnum_catch; + } + + opline = get_next_op(CG(active_op_array)); + opline->opcode = ZEND_CATCH; + opline->op1_type = IS_CONST; + opline->op1.constant = zend_add_class_name_literal(CG(active_op_array), + zend_resolve_class_name_ast(class_ast)); + + opline->op2_type = IS_CV; + opline->op2.var = lookup_cv(CG(active_op_array), zend_string_copy(Z_STR_P(var_name))); + + opline->result.num = is_last_catch && is_last_class; + + if (!is_last_class) { + jmp_multicatch[j] = zend_emit_jump(0); + opline->extended_value = get_next_op_number(CG(active_op_array)); + } + } + + for (j = 0; j < classes->children - 1; j++) { + zend_update_jump_target_to_next(jmp_multicatch[j]); + } + + efree(jmp_multicatch); zend_compile_stmt(stmt_ast); diff --git a/Zend/zend_language_parser.y b/Zend/zend_language_parser.y index 53b2f3f50b..4722846ce7 100644 --- a/Zend/zend_language_parser.y +++ b/Zend/zend_language_parser.y @@ -245,7 +245,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_list parameter_list class_statement_list +%type <ast> echo_expr_list unset_variables catch_name_list catch_list 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 name_list trait_adaptations method_body non_empty_for_exprs @@ -456,10 +456,15 @@ statement: catch_list: /* empty */ { $$ = zend_ast_create_list(0, ZEND_AST_CATCH_LIST); } - | catch_list T_CATCH '(' name T_VARIABLE ')' '{' inner_statement_list '}' + | catch_list T_CATCH '(' catch_name_list T_VARIABLE ')' '{' inner_statement_list '}' { $$ = zend_ast_list_add($1, zend_ast_create(ZEND_AST_CATCH, $4, $5, $8)); } ; +catch_name_list: + name { $$ = zend_ast_create_list(1, ZEND_AST_NAME_LIST, $1); } + | catch_name_list '|' name { $$ = zend_ast_list_add($1, $3); } +; + finally_statement: /* empty */ { $$ = NULL; } | T_FINALLY '{' inner_statement_list '}' { $$ = $3; } |