]> granicus.if.org Git - php/commitdiff
Move most "finally" related code-generation from pass_two() to compiler.
authorDmitry Stogov <dmitry@zend.com>
Tue, 4 Aug 2015 04:37:06 +0000 (07:37 +0300)
committerDmitry Stogov <dmitry@zend.com>
Tue, 4 Aug 2015 04:42:28 +0000 (07:42 +0300)
Zend/tests/break_error_001.phpt [new file with mode: 0644]
Zend/tests/break_error_002.phpt [new file with mode: 0644]
Zend/tests/break_error_003.phpt [new file with mode: 0644]
Zend/tests/break_error_004.phpt [new file with mode: 0644]
Zend/zend_compile.c
Zend/zend_compile.h
Zend/zend_execute.c
Zend/zend_opcode.c

diff --git a/Zend/tests/break_error_001.phpt b/Zend/tests/break_error_001.phpt
new file mode 100644 (file)
index 0000000..eb8ce2e
--- /dev/null
@@ -0,0 +1,10 @@
+--TEST--
+'break' error (non positive numbers)
+--FILE--
+<?php
+function foo () {
+       break 0;
+}
+?>
+--EXPECTF--
+Fatal error: 'break' operator accepts only positive numbers in %sbreak_error_001.php on line 3
diff --git a/Zend/tests/break_error_002.phpt b/Zend/tests/break_error_002.phpt
new file mode 100644 (file)
index 0000000..a1c172d
--- /dev/null
@@ -0,0 +1,10 @@
+--TEST--
+'break' error (operator with non-constant operand)
+--FILE--
+<?php
+function foo () {
+       break $x;
+}
+?>
+--EXPECTF--
+Fatal error: 'break' operator with non-constant operand is no longer supported in %sbreak_error_002.php on line 3
diff --git a/Zend/tests/break_error_003.phpt b/Zend/tests/break_error_003.phpt
new file mode 100644 (file)
index 0000000..fa78234
--- /dev/null
@@ -0,0 +1,10 @@
+--TEST--
+'break' error (not in the loop context)
+--FILE--
+<?php
+function foo () {
+       break;
+}
+?>
+--EXPECTF--
+Fatal error: 'break' not in the 'loop' or 'switch' context in %sbreak_error_003.php on line 3
diff --git a/Zend/tests/break_error_004.phpt b/Zend/tests/break_error_004.phpt
new file mode 100644 (file)
index 0000000..1b99a99
--- /dev/null
@@ -0,0 +1,12 @@
+--TEST--
+'break' error (wrong level)
+--FILE--
+<?php
+function foo () {
+       while (1) {
+               break 2;
+       }
+}
+?>
+--EXPECTF--
+Fatal error: Cannot 'break' 2 levels in %sbreak_error_004.php on line 4
index 63bbee717a5109bca4afe9324a3c9f57a8213065..6deea135d6bfb9a066cafae4fe0510891cbcb1e9 100644 (file)
 
 typedef struct _zend_loop_var {
        zend_uchar opcode;
-       uint32_t try_catch_offset;
-       uint32_t brk_cont_offset;
-       znode var;
+       zend_uchar var_type;
+       uint32_t   var_num;
+       union {
+               uint32_t try_catch_offset;
+               uint32_t brk_cont_offset;
+       } u;
 } zend_loop_var;
 
 static inline void zend_alloc_cache_slot(uint32_t literal) {
@@ -565,7 +568,7 @@ void zend_stop_lexing(void)
        LANG_SCNG(yy_cursor) = LANG_SCNG(yy_limit);
 }
 
-static inline void zend_begin_loop(const znode *loop_var) /* {{{ */
+static inline void zend_begin_loop(zend_uchar free_opcode, const znode *loop_var) /* {{{ */
 {
        zend_brk_cont_element *brk_cont_element;
        int parent = CG(context).current_brk_cont;
@@ -576,9 +579,10 @@ static inline void zend_begin_loop(const znode *loop_var) /* {{{ */
        brk_cont_element->parent = parent;
 
        if (loop_var && (loop_var->op_type & (IS_VAR|IS_TMP_VAR))) {
-               info.opcode = loop_var->flag ? ZEND_FE_FREE : ZEND_FREE;
-               info.var = *loop_var;
-               info.brk_cont_offset = CG(context).current_brk_cont;
+               info.opcode = free_opcode;
+               info.var_type = loop_var->op_type;
+               info.var_num = loop_var->u.op.var;
+               info.u.brk_cont_offset = CG(context).current_brk_cont;
                brk_cont_element->start = get_next_op_number(CG(active_op_array));
        } else {
                info.opcode = ZEND_NOP;
@@ -888,49 +892,6 @@ static void str_dtor(zval *zv)  /* {{{ */ {
 
 static zend_bool zend_is_call(zend_ast *ast);
 
-static zend_loop_var *generate_fast_calls(zend_loop_var *var) /* {{{ */
-{
-       zend_loop_var *base = zend_stack_base(&CG(loop_var_stack));
-       for (; var >= base && var->opcode == ZEND_FAST_CALL; var--) {
-               zend_op *opline = get_next_op(CG(active_op_array));
-               opline->opcode = ZEND_FAST_CALL;
-               SET_NODE(opline->result, &var->var);
-               SET_UNUSED(opline->op1);
-               SET_UNUSED(opline->op2);
-               opline->op1.num = var->try_catch_offset;
-               opline->extended_value = ZEND_FAST_CALL_UNBOUND;
-       }
-       return var;
-}
-/* }}} */
-
-static zend_loop_var *generate_free_loop_var(zend_loop_var *info) /* {{{ */
-{
-       zend_op *opline;
-       zend_loop_var *base = zend_stack_base(&CG(loop_var_stack));
-       ZEND_ASSERT(info->opcode != ZEND_FAST_CALL);
-
-       if (info < base || info->opcode == ZEND_RETURN) {
-               /* Stack separator */
-               return NULL;
-       }
-
-       if (info->opcode == ZEND_NOP) {
-               /* Loop doesn't have freeable variable */
-               return info - 1;
-       }
-
-       ZEND_ASSERT(info->var.op_type == IS_VAR || info->var.op_type == IS_TMP_VAR);
-       opline = get_next_op(CG(active_op_array));
-       opline->opcode = info->opcode;
-       SET_NODE(opline->op1, &info->var);
-       SET_UNUSED(opline->op2);
-       opline->op2.num = info->brk_cont_offset;
-       opline->extended_value = ZEND_FREE_ON_RETURN;
-       return info - 1;
-}
-/* }}} */
-
 static uint32_t zend_add_try_element(uint32_t try_op) /* {{{ */
 {
        zend_op_array *op_array = CG(active_op_array);
@@ -3497,13 +3458,55 @@ void zend_compile_unset(zend_ast *ast) /* {{{ */
 }
 /* }}} */
 
-static void zend_handle_loops_and_finally() /* {{{ */
+static int zend_handle_loops_and_finally_ex(zend_long depth) /* {{{ */
 {
+       zend_loop_var *base;
        zend_loop_var *loop_var = zend_stack_top(&CG(loop_var_stack));
-       while (loop_var) {
-               loop_var = generate_fast_calls(loop_var);
-               loop_var = generate_free_loop_var(loop_var);
+
+       if (!loop_var) {
+               return 1;
        }
+       base = zend_stack_base(&CG(loop_var_stack));
+       for (; loop_var >= base; loop_var--) {
+               if (loop_var->opcode == ZEND_FAST_CALL) {
+                       zend_op *opline = get_next_op(CG(active_op_array));
+
+                       opline->opcode = ZEND_FAST_CALL;
+                       opline->result_type = IS_TMP_VAR;
+                       opline->result.var = loop_var->var_num;
+                       SET_UNUSED(opline->op1);
+                       SET_UNUSED(opline->op2);
+                       opline->op1.num = loop_var->u.try_catch_offset;
+                       opline->extended_value = ZEND_FAST_CALL_UNBOUND;
+               } else if (loop_var->opcode == ZEND_RETURN) {
+                       /* Stack separator */
+                       break;
+               } else if (depth <= 1) {
+                       return 1;
+               } else if (loop_var->opcode == ZEND_NOP) {
+                       /* Loop doesn't have freeable variable */
+                       depth--;
+               } else {
+                       zend_op *opline;
+
+                       ZEND_ASSERT(loop_var->var_type == IS_VAR || loop_var->var_type == IS_TMP_VAR);
+                       opline = get_next_op(CG(active_op_array));
+                       opline->opcode = loop_var->opcode;
+                       opline->op1_type = loop_var->var_type;
+                       opline->op1.var = loop_var->var_num;
+                       SET_UNUSED(opline->op2);
+                       opline->op2.num = loop_var->u.brk_cont_offset;
+                       opline->extended_value = ZEND_FREE_ON_RETURN;
+                       depth--;
+           }
+       }
+       return (depth == 0);
+}
+/* }}} */
+
+static int zend_handle_loops_and_finally(void) /* {{{ */
+{
+       zend_handle_loops_and_finally_ex(zend_stack_count(&CG(loop_var_stack)) + 1);
 }
 /* }}} */
 
@@ -3602,24 +3605,11 @@ void zend_compile_break_continue(zend_ast *ast) /* {{{ */
                zend_error_noreturn(E_COMPILE_ERROR, "'%s' not in the 'loop' or 'switch' context",
                        ast->kind == ZEND_AST_BREAK ? "break" : "continue");
        } else {
-               int array_offset = CG(context).current_brk_cont;
-               zend_long nest_level = depth;
-               zend_loop_var *loop_var = zend_stack_top(&CG(loop_var_stack));
-
-               do {
-                       if (array_offset == -1) {
-                               zend_error_noreturn(E_COMPILE_ERROR, "Cannot '%s' %d level%s",
-                                       ast->kind == ZEND_AST_BREAK ? "break" : "continue",
-                                       depth, depth == 1 ? "" : "s");
-                       }
-
-                       loop_var = generate_fast_calls(loop_var);
-                       if (nest_level > 1) {
-                               loop_var = generate_free_loop_var(loop_var);
-                       }
-
-                       array_offset = CG(active_op_array)->brk_cont_array[array_offset].parent;
-               } while (--nest_level > 0);
+               if (!zend_handle_loops_and_finally_ex(depth)) {
+                       zend_error_noreturn(E_COMPILE_ERROR, "Cannot '%s' %d level%s",
+                               ast->kind == ZEND_AST_BREAK ? "break" : "continue",
+                               depth, depth == 1 ? "" : "s");
+               }
        }
        opline = zend_emit_op(NULL, ast->kind == ZEND_AST_BREAK ? ZEND_BRK : ZEND_CONT, NULL, NULL);
        opline->op1.num = CG(context).current_brk_cont;
@@ -3627,7 +3617,7 @@ void zend_compile_break_continue(zend_ast *ast) /* {{{ */
 }
 /* }}} */
 
-zend_op *zend_resolve_goto_label(zend_op_array *op_array, zend_op *opline) /* {{{ */
+void zend_resolve_goto_label(zend_op_array *op_array, zend_op *opline) /* {{{ */
 {
        zend_label *dest;
        int current, remove_oplines = opline->op1.num;
@@ -3672,12 +3662,6 @@ zend_op *zend_resolve_goto_label(zend_op_array *op_array, zend_op *opline) /* {{
                }
        }
 
-       ZEND_ASSERT(remove_oplines >= 0);
-       while (remove_oplines--) {
-               MAKE_NOP(opline);
-               opline--;
-       }
-
        opline->opcode = ZEND_JMP;
        opline->op1.opline_num = dest->opline_num;
        opline->extended_value = 0;
@@ -3685,7 +3669,12 @@ zend_op *zend_resolve_goto_label(zend_op_array *op_array, zend_op *opline) /* {{
        SET_UNUSED(opline->op2);
        SET_UNUSED(opline->result);
 
-       return opline;
+       ZEND_ASSERT(remove_oplines >= 0);
+       while (remove_oplines--) {
+               opline--;
+               MAKE_NOP(opline);
+               ZEND_VM_SET_OPCODE_HANDLER(opline);
+       }
 }
 /* }}} */
 
@@ -3734,7 +3723,7 @@ void zend_compile_while(zend_ast *ast) /* {{{ */
 
        opnum_jmp = zend_emit_jump(0);
 
-       zend_begin_loop(NULL);
+       zend_begin_loop(ZEND_NOP, NULL);
 
        opnum_start = get_next_op_number(CG(active_op_array));
        zend_compile_stmt(stmt_ast);
@@ -3757,7 +3746,7 @@ void zend_compile_do_while(zend_ast *ast) /* {{{ */
        znode cond_node;
        uint32_t opnum_start, opnum_cond;
 
-       zend_begin_loop(NULL);
+       zend_begin_loop(ZEND_NOP, NULL);
 
        opnum_start = get_next_op_number(CG(active_op_array));
        zend_compile_stmt(stmt_ast);
@@ -3808,7 +3797,7 @@ void zend_compile_for(zend_ast *ast) /* {{{ */
 
        opnum_jmp = zend_emit_jump(0);
 
-       zend_begin_loop(NULL);
+       zend_begin_loop(ZEND_NOP, NULL);
 
        opnum_start = get_next_op_number(CG(active_op_array));
        zend_compile_stmt(stmt_ast);
@@ -3890,8 +3879,7 @@ void zend_compile_foreach(zend_ast *ast) /* {{{ */
                zend_emit_assign_znode(key_ast, &key_node);
        }
 
-       reset_node.flag = ZEND_FE_FREE;
-       zend_begin_loop(&reset_node);
+       zend_begin_loop(ZEND_FE_FREE, &reset_node);
 
        zend_compile_stmt(stmt_ast);
 
@@ -3966,8 +3954,7 @@ void zend_compile_switch(zend_ast *ast) /* {{{ */
 
        zend_compile_expr(&expr_node, expr_ast);
 
-       expr_node.flag = 0; /* Generate normal FREE */
-       zend_begin_loop(&expr_node);
+       zend_begin_loop(ZEND_FREE, &expr_node);
 
        case_node.op_type = IS_TMP_VAR;
        case_node.u.op.var = get_temporary_variable(CG(active_op_array));
@@ -4075,9 +4062,9 @@ void zend_compile_try(zend_ast *ast) /* {{{ */
 
                /* Push FAST_CALL on unwind stack */
                fast_call.opcode = ZEND_FAST_CALL;
-               fast_call.var.op_type = IS_TMP_VAR;
-               fast_call.var.u.op.var = CG(context).fast_call_var;
-               fast_call.try_catch_offset = try_catch_offset;
+               fast_call.var_type = IS_TMP_VAR;
+               fast_call.var_num = CG(context).fast_call_var;
+               fast_call.u.try_catch_offset = try_catch_offset;
                zend_stack_push(&CG(loop_var_stack), &fast_call);
        }
 
index 265a97db079e31e94191cd078c52cb85076e776f..ae9a2806ff63da485887d0557ea4398add8e825c 100644 (file)
@@ -712,7 +712,7 @@ void zend_do_extended_fcall_end(void);
 
 void zend_verify_namespace(void);
 
-zend_op *zend_resolve_goto_label(zend_op_array *op_array, zend_op *pass2_opline);
+void zend_resolve_goto_label(zend_op_array *op_array, zend_op *opline);
 
 ZEND_API void function_add_ref(zend_function *function);
 
index 3009ddfcff3226f4663967b15418993e7c14a458..b9a7ecfe1b339583670a9f6bcbdf80ef930cf948 100644 (file)
@@ -1944,31 +1944,6 @@ static zend_always_inline void zend_fetch_property_address(zval *result, zval *c
        }
 }
 
-static inline zend_brk_cont_element* zend_brk_cont(int nest_levels, int array_offset, const zend_op_array *op_array, const zend_execute_data *execute_data)
-{
-       zend_brk_cont_element *jmp_to;
-
-       do {
-               ZEND_ASSERT(array_offset != -1);
-               jmp_to = &op_array->brk_cont_array[array_offset];
-               if (nest_levels > 1 && jmp_to->start >= 0) {
-                       zend_op *brk_opline = &op_array->opcodes[jmp_to->brk];
-
-                       if (brk_opline->opcode == ZEND_FREE) {
-                               zval_ptr_dtor_nogc(EX_VAR(brk_opline->op1.var));
-                       } else if (brk_opline->opcode == ZEND_FE_FREE) {
-                               zval *var = EX_VAR(brk_opline->op1.var);
-                               if (Z_TYPE_P(var) != IS_ARRAY && Z_FE_ITER_P(var) != (uint32_t)-1) {
-                                       zend_hash_iterator_del(Z_FE_ITER_P(var));
-                               }
-                               zval_ptr_dtor_nogc(var);
-                       }
-               }
-               array_offset = jmp_to->parent;
-       } while (--nest_levels > 0);
-       return jmp_to;
-}
-
 #if ZEND_INTENSIVE_DEBUGGING
 
 #define CHECK_SYMBOL_TABLES()                                                                                                  \
index 9ead51465056b31094fdf324e9c18331d72e2652..237e7cd6f6600622d7f08b21856ca64dd7d2da5e 100644 (file)
@@ -508,28 +508,7 @@ static void zend_check_finally_breakout(zend_op_array *op_array, uint32_t op_num
        }
 }
 
-static void zend_adjust_fast_call(zend_op_array *op_array, uint32_t fast_call, uint32_t start, uint32_t end)
-{
-       int i;
-       uint32_t op_num = 0;
-
-       for (i = 0; i < op_array->last_try_catch; i++) {
-               if (op_array->try_catch_array[i].finally_op > start
-                               && op_array->try_catch_array[i].finally_end < end) {
-                       op_num = op_array->try_catch_array[i].finally_op;
-                       start = op_array->try_catch_array[i].finally_end;
-               }
-       }
-
-       if (op_num) {
-               /* Must be ZEND_FAST_CALL */
-               ZEND_ASSERT(op_array->opcodes[op_num - 2].opcode == ZEND_FAST_CALL);
-               op_array->opcodes[op_num - 2].extended_value = ZEND_FAST_CALL_FROM_FINALLY;
-               op_array->opcodes[op_num - 2].op2.opline_num = fast_call;
-       }
-}
-
-static void zend_resolve_fast_call(zend_op_array *op_array, uint32_t fast_call, uint32_t op_num)
+static void zend_resolve_fast_call(zend_op_array *op_array, uint32_t op_num)
 {
        int i;
        uint32_t finally_op_num = 0;
@@ -544,78 +523,9 @@ static void zend_resolve_fast_call(zend_op_array *op_array, uint32_t fast_call,
        if (finally_op_num) {
                /* Must be ZEND_FAST_CALL */
                ZEND_ASSERT(op_array->opcodes[finally_op_num - 2].opcode == ZEND_FAST_CALL);
-               if (op_array->opcodes[fast_call].extended_value == 0) {
-                       op_array->opcodes[fast_call].extended_value = ZEND_FAST_CALL_FROM_FINALLY;
-                       op_array->opcodes[fast_call].op2.opline_num = finally_op_num - 2;
-               }
-       }
-}
-
-static void zend_resolve_finally_call(zend_op_array *op_array, uint32_t op_num, uint32_t dst_num)
-{
-       uint32_t start_op;
-       zend_op *opline;
-       uint32_t i = op_array->last_try_catch;
-
-       if (dst_num != (uint32_t)-1) {
-               zend_check_finally_breakout(op_array, op_num, dst_num);
-       }
-
-       /* the backward order matters */
-       while (i > 0) {
-               i--;
-               if (op_array->try_catch_array[i].finally_op &&
-                   op_num >= op_array->try_catch_array[i].try_op &&
-                   op_num < op_array->try_catch_array[i].finally_op - 1 &&
-                   (dst_num < op_array->try_catch_array[i].try_op ||
-                    dst_num > op_array->try_catch_array[i].finally_end)) {
-                       /* we have a jump out of try block that needs executing finally */
-                       uint32_t fast_call_var;
-
-                       /* Must be ZEND_FAST_RET */
-                       ZEND_ASSERT(op_array->opcodes[op_array->try_catch_array[i].finally_end].opcode == ZEND_FAST_RET);
-                       fast_call_var = op_array->opcodes[op_array->try_catch_array[i].finally_end].op1.var;
-
-                       /* generate a FAST_CALL to finally block */
-                       start_op = get_next_op_number(op_array);
-
-                       opline = get_next_op(op_array);
-                       opline->opcode = ZEND_FAST_CALL;
-                       opline->result_type = IS_TMP_VAR;
-                       opline->result.var = fast_call_var;
-                       SET_UNUSED(opline->op1);
-                       SET_UNUSED(opline->op2);
-                       zend_adjust_fast_call(op_array, start_op,
-                                       op_array->try_catch_array[i].finally_op,
-                                       op_array->try_catch_array[i].finally_end);
-                       zend_resolve_fast_call(op_array, start_op, op_array->try_catch_array[i].finally_op - 2);
-                       opline->op1.opline_num = op_array->try_catch_array[i].finally_op;
-
-                       /* We should not generate JMPs that require handling multiple finally blocks */
-                       while (i > 0) {
-                               i--;
-                               if (op_array->try_catch_array[i].finally_op &&
-                                       op_num >= op_array->try_catch_array[i].try_op &&
-                                       op_num < op_array->try_catch_array[i].finally_op - 1 &&
-                                       (dst_num < op_array->try_catch_array[i].try_op ||
-                                        dst_num > op_array->try_catch_array[i].finally_end)
-                               ) {
-                                       ZEND_ASSERT(0);
-                               }
-                       }
-
-                       /* Finish the sequence with original opcode */
-                       opline = get_next_op(op_array);
-                       *opline = op_array->opcodes[op_num];
-
-                       /* Replace original opcode with jump to this sequence */
-                       opline = op_array->opcodes + op_num;
-                       opline->opcode = ZEND_JMP;
-                       SET_UNUSED(opline->op1);
-                       SET_UNUSED(opline->op2);
-                       opline->op1.opline_num = start_op;
-
-                       break;
+               if (op_array->opcodes[op_num].extended_value == 0) {
+                       op_array->opcodes[op_num].extended_value = ZEND_FAST_CALL_FROM_FINALLY;
+                       op_array->opcodes[op_num].op2.opline_num = finally_op_num - 2;
                }
        }
 }
@@ -662,39 +572,6 @@ static uint32_t zend_get_brk_cont_target(const zend_op_array *op_array, const ze
        return opline->opcode == ZEND_BRK ? jmp_to->brk : jmp_to->cont;
 }
 
-static void zend_resolve_finally_calls(zend_op_array *op_array)
-{
-       uint32_t i, j;
-       zend_op *opline;
-
-       for (i = 0, j = op_array->last; i < j; i++) {
-               opline = op_array->opcodes + i;
-               switch (opline->opcode) {
-                       case ZEND_BRK:
-                       case ZEND_CONT:
-                               zend_check_finally_breakout(op_array, i, zend_get_brk_cont_target(op_array, opline));
-                               break;
-                       case ZEND_GOTO:
-                               zend_check_finally_breakout(op_array, i,
-                                       zend_resolve_goto_label(op_array, opline)->op1.opline_num);
-                               break;
-                       case ZEND_JMP:
-                               zend_resolve_finally_call(op_array, i, opline->op1.opline_num);
-                               break;
-                       case ZEND_FAST_CALL:
-                               if (opline->extended_value == ZEND_FAST_CALL_UNBOUND) {
-                                       opline->op1.opline_num = op_array->try_catch_array[opline->op1.num].finally_op;
-                                       opline->extended_value = 0;
-                               }
-                               zend_resolve_fast_call(op_array, i, i);
-                               break;
-                       case ZEND_FAST_RET:
-                               zend_resolve_finally_ret(op_array, i);
-                               break;
-               }
-       }
-}
-
 ZEND_API int pass_two(zend_op_array *op_array)
 {
        zend_op *opline, *end;
@@ -702,9 +579,6 @@ ZEND_API int pass_two(zend_op_array *op_array)
        if (!ZEND_USER_CODE(op_array->type)) {
                return 0;
        }
-       if (op_array->fn_flags & ZEND_ACC_HAS_FINALLY_BLOCK) {
-               zend_resolve_finally_calls(op_array);
-       }
        if (CG(compiler_options) & ZEND_COMPILE_EXTENDED_INFO) {
                zend_update_extended_info(op_array);
        }
@@ -728,6 +602,17 @@ ZEND_API int pass_two(zend_op_array *op_array)
        end = opline + op_array->last;
        while (opline < end) {
                switch (opline->opcode) {
+                       case ZEND_FAST_CALL:
+                               if (opline->extended_value == ZEND_FAST_CALL_UNBOUND) {
+                                       opline->op1.opline_num = op_array->try_catch_array[opline->op1.num].finally_op;
+                                       opline->extended_value = 0;
+                               }
+                               zend_resolve_fast_call(op_array, opline - op_array->opcodes);
+                               ZEND_PASS_TWO_UPDATE_JMP_TARGET(op_array, opline, opline->op1);
+                               break;
+                       case ZEND_FAST_RET:
+                               zend_resolve_finally_ret(op_array, opline - op_array->opcodes);
+                               break;
                        case ZEND_DECLARE_ANON_INHERITED_CLASS:
                                ZEND_PASS_TWO_UPDATE_JMP_TARGET(op_array, opline, opline->op1);
                                /* break omitted intentionally */
@@ -739,6 +624,10 @@ ZEND_API int pass_two(zend_op_array *op_array)
                        case ZEND_CONT:
                                {
                                        uint32_t jmp_target = zend_get_brk_cont_target(op_array, opline);
+
+                                       if (op_array->fn_flags & ZEND_ACC_HAS_FINALLY_BLOCK) {
+                                               zend_check_finally_breakout(op_array, opline - op_array->opcodes, jmp_target);
+                                       }
                                        opline->opcode = ZEND_JMP;
                                        opline->op1.opline_num = jmp_target;
                                        opline->op2.num = 0;
@@ -746,10 +635,12 @@ ZEND_API int pass_two(zend_op_array *op_array)
                                }
                                break;
                        case ZEND_GOTO:
-                               opline = zend_resolve_goto_label(op_array, opline);
+                               zend_resolve_goto_label(op_array, opline);
+                               if (op_array->fn_flags & ZEND_ACC_HAS_FINALLY_BLOCK) {
+                                       zend_check_finally_breakout(op_array, opline - op_array->opcodes, opline->op1.opline_num);
+                               }
                                /* break omitted intentionally */
                        case ZEND_JMP:
-                       case ZEND_FAST_CALL:
                        case ZEND_DECLARE_ANON_CLASS:
                                ZEND_PASS_TWO_UPDATE_JMP_TARGET(op_array, opline, opline->op1);
                                break;