summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--Zend/tests/try_finally_012.phpt32
-rw-r--r--Zend/zend_compile.c15
-rw-r--r--Zend/zend_compile.h4
-rw-r--r--Zend/zend_vm_def.h19
-rw-r--r--Zend/zend_vm_execute.h19
5 files changed, 84 insertions, 5 deletions
diff --git a/Zend/tests/try_finally_012.phpt b/Zend/tests/try_finally_012.phpt
new file mode 100644
index 0000000000..32fec7ab29
--- /dev/null
+++ b/Zend/tests/try_finally_012.phpt
@@ -0,0 +1,32 @@
+--TEST--
+Try finally (exception in "return" statement)
+--FILE--
+<?php
+class A {
+ public $x = 1;
+ public $y = 2;
+ function __destruct() {
+ throw new Exception();
+ }
+}
+function foo() {
+ foreach(new A() as $a) {
+ try {
+ return $a;
+ } catch (Exception $e) {
+ echo "exception in foo\n";
+ } finally {
+ echo "finally\n";
+ }
+ }
+}
+try {
+ foo();
+} catch (Exception $e) {
+ echo "exception in main\n";
+}
+?>
+--EXPECT--
+finally
+exception in main
+
diff --git a/Zend/zend_compile.c b/Zend/zend_compile.c
index 0927f0cd23..44807d359b 100644
--- a/Zend/zend_compile.c
+++ b/Zend/zend_compile.c
@@ -3514,9 +3514,20 @@ void zend_compile_unset(zend_ast *ast) /* {{{ */
}
/* }}} */
-static void zend_free_foreach_and_switch_variables(void) /* {{{ */
+static void zend_free_foreach_and_switch_variables(uint32_t flags) /* {{{ */
{
+ uint32_t start_op_number = get_next_op_number(CG(active_op_array));
+
zend_stack_apply(&CG(loop_var_stack), ZEND_STACK_APPLY_TOPDOWN, (int (*)(void *element)) generate_free_loop_var);
+
+ if (flags) {
+ uint32_t end_op_number = get_next_op_number(CG(active_op_array));
+
+ while (start_op_number < end_op_number) {
+ CG(active_op_array)->opcodes[start_op_number].extended_value |= flags;
+ start_op_number++;
+ }
+ }
}
/* }}} */
@@ -3538,7 +3549,7 @@ void zend_compile_return(zend_ast *ast) /* {{{ */
zend_compile_expr(&expr_node, expr_ast);
}
- zend_free_foreach_and_switch_variables();
+ zend_free_foreach_and_switch_variables(ZEND_FREE_ON_RETURN);
if (CG(context).in_finally) {
opline = zend_emit_op(NULL, ZEND_DISCARD_EXCEPTION, NULL, NULL);
diff --git a/Zend/zend_compile.h b/Zend/zend_compile.h
index a3bc17f425..8e9cfe795b 100644
--- a/Zend/zend_compile.h
+++ b/Zend/zend_compile.h
@@ -875,7 +875,9 @@ ZEND_API void zend_assert_valid_class_name(const zend_string *const_name);
#define ZEND_FETCH_ARG_MASK 0x000fffff
-#define ZEND_MEMBER_FUNC_CALL 1<<0
+#define ZEND_FREE_ON_RETURN (1<<0)
+
+#define ZEND_MEMBER_FUNC_CALL (1<<0)
#define ZEND_ARG_SEND_BY_REF (1<<0)
#define ZEND_ARG_COMPILE_TIME_BOUND (1<<1)
diff --git a/Zend/zend_vm_def.h b/Zend/zend_vm_def.h
index b94614e659..5ea7b8a1fa 100644
--- a/Zend/zend_vm_def.h
+++ b/Zend/zend_vm_def.h
@@ -2610,10 +2610,13 @@ ZEND_VM_HANDLER(47, ZEND_JMPNZ_EX, CONST|TMPVAR|CV, ANY)
ZEND_VM_HANDLER(70, ZEND_FREE, TMPVAR, ANY)
{
+ zval *var;
USE_OPLINE
SAVE_OPLINE();
- zval_ptr_dtor_nogc(EX_VAR(opline->op1.var));
+ var = EX_VAR(opline->op1.var);
+ zval_ptr_dtor_nogc(var);
+ ZVAL_NULL(var);
ZEND_VM_NEXT_OPCODE_CHECK_EXCEPTION();
}
@@ -2626,8 +2629,10 @@ ZEND_VM_HANDLER(127, ZEND_FE_FREE, TMPVAR, ANY)
var = EX_VAR(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));
+ Z_FE_ITER_P(var) = (uint32_t)-1;
}
zval_ptr_dtor_nogc(var);
+ ZVAL_NULL(var);
ZEND_VM_NEXT_OPCODE_CHECK_EXCEPTION();
}
@@ -7099,6 +7104,18 @@ ZEND_VM_HANDLER(149, ZEND_HANDLE_EXCEPTION, ANY, ANY)
}
}
+ if (catch_op_num) {
+ if ((EX(func)->op_array.opcodes[op_num].opcode == ZEND_FREE && (EX(func)->op_array.opcodes[op_num].extended_value & ZEND_FREE_ON_RETURN))
+ || (EX(func)->op_array.opcodes[op_num].opcode == ZEND_FE_FREE && (EX(func)->op_array.opcodes[op_num].extended_value & ZEND_FREE_ON_RETURN))
+ ) {
+ /* exceptions thrown because of TMP variable destruction on "return"
+ * statement should't be caught in the same function.
+ * See: Zend/tests/try_finally_012.phpt
+ */
+ catch_op_num = 0;
+ }
+ }
+
i_cleanup_unfinished_execution(execute_data, op_num, catch_op_num);
if (finally_op_num && (!catch_op_num || catch_op_num >= finally_op_num)) {
diff --git a/Zend/zend_vm_execute.h b/Zend/zend_vm_execute.h
index 828babb98d..8a6918e7ef 100644
--- a/Zend/zend_vm_execute.h
+++ b/Zend/zend_vm_execute.h
@@ -1500,6 +1500,18 @@ static ZEND_OPCODE_HANDLER_RET ZEND_FASTCALL ZEND_HANDLE_EXCEPTION_SPEC_HANDLER(
}
}
+ if (catch_op_num) {
+ if ((EX(func)->op_array.opcodes[op_num].opcode == ZEND_FREE && (EX(func)->op_array.opcodes[op_num].extended_value & ZEND_FREE_ON_RETURN))
+ || (EX(func)->op_array.opcodes[op_num].opcode == ZEND_FE_FREE && (EX(func)->op_array.opcodes[op_num].extended_value & ZEND_FREE_ON_RETURN))
+ ) {
+ /* exceptions thrown because of TMP variable destruction on "return"
+ * statement should't be caught in the same function.
+ * See: Zend/tests/try_finally_012.phpt
+ */
+ catch_op_num = 0;
+ }
+ }
+
i_cleanup_unfinished_execution(execute_data, op_num, catch_op_num);
if (finally_op_num && (!catch_op_num || catch_op_num >= finally_op_num)) {
@@ -40189,10 +40201,13 @@ static ZEND_OPCODE_HANDLER_RET ZEND_FASTCALL ZEND_JMPNZ_EX_SPEC_TMPVAR_HANDLER(Z
static ZEND_OPCODE_HANDLER_RET ZEND_FASTCALL ZEND_FREE_SPEC_TMPVAR_HANDLER(ZEND_OPCODE_HANDLER_ARGS)
{
+ zval *var;
USE_OPLINE
SAVE_OPLINE();
- zval_ptr_dtor_nogc(EX_VAR(opline->op1.var));
+ var = EX_VAR(opline->op1.var);
+ zval_ptr_dtor_nogc(var);
+ ZVAL_NULL(var);
ZEND_VM_NEXT_OPCODE_CHECK_EXCEPTION();
}
@@ -40205,8 +40220,10 @@ static ZEND_OPCODE_HANDLER_RET ZEND_FASTCALL ZEND_FE_FREE_SPEC_TMPVAR_HANDLER(ZE
var = EX_VAR(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));
+ Z_FE_ITER_P(var) = (uint32_t)-1;
}
zval_ptr_dtor_nogc(var);
+ ZVAL_NULL(var);
ZEND_VM_NEXT_OPCODE_CHECK_EXCEPTION();
}