--- /dev/null
+--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
+
}
/* }}} */
-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++;
+ }
+ }
}
/* }}} */
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);
#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)
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();
}
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();
}
}
}
+ 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)) {
}
}
+ 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)) {
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();
}
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();
}