summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--Zend/tests/try/exceptions.inc5
-rw-r--r--Zend/tests/try/try_multicatch_001.phpt19
-rw-r--r--Zend/tests/try/try_multicatch_002.phpt21
-rw-r--r--Zend/tests/try/try_multicatch_003.phpt21
-rw-r--r--Zend/tests/try/try_multicatch_004.phpt21
-rw-r--r--Zend/tests/try/try_multicatch_005.phpt25
-rw-r--r--Zend/zend_ast.c9
-rw-r--r--Zend/zend_compile.c55
-rw-r--r--Zend/zend_language_parser.y9
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; }