Fixed bug #72213 (Finally leaks on nested exceptions).

Squashed commit of the following:

commit 8461b0407f
Author: Dmitry Stogov <dmitry@zend.com>
Date:   Wed May 25 00:34:42 2016 +0300

    Rmoved zend_try_catch_element.parent and walk through op_array.try_catch_array backward from the current try_cacth_offset.

commit 0c71e24964
Author: Dmitry Stogov <dmitry@zend.com>
Date:   Wed May 25 00:04:53 2016 +0300

    Move SAVE_OPLINE() to its original place

commit 111432a4df
Author: Dmitry Stogov <dmitry@zend.com>
Date:   Wed May 25 00:01:10 2016 +0300

    Separate the common part of ZEND_HANDLE_EXCEPTION and FAST_RET into zend_dispatch_try_catch_finally_helper.

commit 4f21c06c2e
Author: Nikita Popov <nikic@php.net>
Date:   Tue May 24 14:55:27 2016 +0200

    Improve finally fix

commit da5c727499
Author: Dmitry Stogov <dmitry@zend.com>
Date:   Tue May 24 10:36:08 2016 +0300

    Fixed Zend/tests/try/bug70228_3.phpt and Zend/tests/try/bug70228_4.phpt

commit cfcedf2fb4
Author: Dmitry Stogov <dmitry@zend.com>
Date:   Tue May 24 02:59:27 2016 +0300

    Added test

commit 4c6aa93d43
Author: Dmitry Stogov <dmitry@zend.com>
Date:   Tue May 24 00:38:20 2016 +0300

    Added tests

commit 8a8f4704b0
Author: Dmitry Stogov <dmitry@zend.com>
Date:   Mon May 23 23:27:34 2016 +0300

    Fixed bug #72213 (Finally leaks on nested exceptions)
This commit is contained in:
Dmitry Stogov 2016-05-25 01:25:12 +03:00
parent 91f5940329
commit 2ae21abdf7
24 changed files with 451 additions and 303 deletions

1
NEWS
View File

@ -19,6 +19,7 @@ PHP NEWS
. Fixed bug #71897 (ASCII 0x7F Delete control character permitted in
identifiers). (Andrea)
. Fixed bug #72188 (Nested try/finally blocks losing return value). (Dmitry)
. Fixed bug #72213 (Finally leaks on nested exceptions). (Dmitry, Nikita)
. Implemented the RFC `Support Class Constant Visibility`. (Sean DuBois,
Reeze Xia, Dmitry)
. Added void return type. (Andrea)

View File

@ -57,8 +57,13 @@ $bar = foo3();
string(9) "not catch"
NULL
Fatal error: Uncaught Error: Class 'NotExists' not found in %sbug65784.php:%d
Fatal error: Uncaught Exception: not catched in %sbug65784.php:42
Stack trace:
#0 %s(%d): foo3()
#0 %sbug65784.php(52): foo3()
#1 {main}
thrown in %sbug65784.php on line %d
Next Error: Class 'NotExists' not found in %s/bug65784.php:46
Stack trace:
#0 %sbug65784.php(52): foo3()
#1 {main}
thrown in %sbug65784.php on line 46

View File

@ -16,7 +16,12 @@ function foo() : array {
foo();
?>
--EXPECTF--
Fatal error: Uncaught TypeError: Return value of foo() must be of the type array, none returned in %s29.php:%d
Fatal error: Uncaught Exception: xxxx in %s:%d
Stack trace:
#0 %s(%d): foo()
#1 {main}
Next TypeError: Return value of foo() must be of the type array, none returned in %s29.php:%d
Stack trace:
#0 %s(%d): foo()
#1 {main}

View File

@ -0,0 +1,20 @@
--TEST--
Bug #70228 (memleak if return in finally block)
--FILE--
<?php
function test() {
try {
throw new Exception(1);
} finally {
try {
throw new Exception(2);
} finally {
return 42;
}
}
}
var_dump(test());
?>
--EXPECT--
int(42)

View File

@ -0,0 +1,31 @@
--TEST--
Bug #70228 (memleak if return in finally block)
--FILE--
<?php
function test() {
try {
throw new Exception(1);
} finally {
try {
try {
} finally {
return 42;
}
} finally {
throw new Exception(2);
}
}
}
try {
var_dump(test());
} catch (Exception $e) {
do {
echo $e->getMessage() . "\n";
$e = $e->getPrevious();
} while ($e);
}
?>
--EXPECT--
2
1

View File

@ -0,0 +1,32 @@
--TEST--
Bug #70228 (memleak if return in finally block)
--FILE--
<?php
function test() {
try {
throw new Exception(1);
} finally {
try {
try {
try {
} finally {
return 42;
}
} finally {
throw new Exception(3);
}
} catch (Exception $e) {}
}
}
try {
var_dump(test());
} catch (Exception $e) {
do {
echo $e->getMessage() . "\n";
$e = $e->getPrevious();
} while ($e);
}
?>
--EXPECT--
1

View File

@ -1,7 +1,5 @@
--TEST--
Bug #72213 (Finally leaks on nested exceptions)
--XFAIL--
See https://bugs.php.net/bug.php?id=72213
--FILE--
<?php
function test() {

View File

@ -0,0 +1,37 @@
--TEST--
Loop var dtor throwing exception during return inside try/catch inside finally
--FILE--
<?php
class Dtor {
public function __destruct() {
throw new Exception(2);
}
}
function test() {
try {
throw new Exception(1);
} finally {
try {
foreach ([new Dtor] as $v) {
unset($v);
return 42;
}
} catch (Exception $e) {
}
}
}
try {
test();
} catch (Exception $e) {
echo $e, "\n";
}
?>
--EXPECTF--
Exception: 1 in %s:%d
Stack trace:
#0 %s(%d): test()
#1 {main}

View File

@ -0,0 +1,38 @@
--TEST--
Exception in finally inside finally following try/catch containing throwing try/finally
--FILE--
<?php
function test() {
try {
throw new Exception(1);
} finally {
try {
try {
} finally {
throw new Exception(2);
}
} catch (Exception $e) {}
try {
} finally {
throw new Exception(3);
}
}
}
try {
test();
} catch (Exception $e) {
echo $e, "\n";
}
?>
--EXPECTF--
Exception: 1 in %s:%d
Stack trace:
#0 %s(%d): test()
#1 {main}
Next Exception: 3 in %s:%d
Stack trace:
#0 %s(%d): test()
#1 {main}

View File

@ -0,0 +1,28 @@
--TEST--
Throw in try of try/finally inside catch
--FILE--
<?php
function test() {
try {
throw new Exception(1);
} catch (Exception $e) {
try {
throw new Exception(2);
} finally {
}
}
}
try {
test();
} catch (Exception $e) {
echo $e, "\n";
}
?>
--EXPECTF--
Exception: 2 in %s:%d
Stack trace:
#0 %s(%d): test()
#1 {main}

View File

@ -0,0 +1,37 @@
--TEST--
Throw in finally inside catch inside finally
--FILE--
<?php
function test() {
try {
throw new Exception(1);
} finally {
try {
throw new Exception(2);
} catch (Exception $e) {
try {
} finally {
throw new Exception(3);
}
}
}
}
try {
test();
} catch (Exception $e) {
echo $e, "\n";
}
?>
--EXPECTF--
Exception: 1 in %s:%d
Stack trace:
#0 %s(%d): test()
#1 {main}
Next Exception: 3 in %s:%d
Stack trace:
#0 %s(%d): test()
#1 {main}

View File

@ -0,0 +1,34 @@
--TEST--
Return in try with throw in finally, inside other finally
--FILE--
<?php
function test() {
try {
throw new Exception(1);
} finally {
try {
return 42;
} finally {
throw new Exception(2);
}
}
}
try {
test();
} catch (Exception $e) {
echo $e, "\n";
}
?>
--EXPECTF--
Exception: 1 in %s:%d
Stack trace:
#0 %s(%d): test()
#1 {main}
Next Exception: 2 in %s:%d
Stack trace:
#0 %s(%d): test()
#1 {main}

View File

@ -234,6 +234,7 @@ void zend_oparray_context_begin(zend_oparray_context *prev_context) /* {{{ */
CG(context).backpatch_count = 0;
CG(context).in_finally = 0;
CG(context).fast_call_var = -1;
CG(context).try_catch_offset = -1;
CG(context).current_brk_cont = -1;
CG(context).last_brk_cont = 0;
CG(context).brk_cont_array = NULL;
@ -4008,6 +4009,12 @@ static int zend_handle_loops_and_finally_ex(zend_long depth) /* {{{ */
SET_UNUSED(opline->op1);
SET_UNUSED(opline->op2);
opline->op1.num = loop_var->u.try_catch_offset;
} else if (loop_var->opcode == ZEND_DISCARD_EXCEPTION) {
zend_op *opline = get_next_op(CG(active_op_array));
opline->opcode = ZEND_DISCARD_EXCEPTION;
opline->op1_type = IS_TMP_VAR;
opline->op1.var = loop_var->var_num;
SET_UNUSED(opline->op2);
} else if (loop_var->opcode == ZEND_RETURN) {
/* Stack separator */
break;
@ -4057,12 +4064,6 @@ void zend_compile_return(zend_ast *ast) /* {{{ */
zend_compile_expr(&expr_node, expr_ast);
}
if (CG(context).in_finally) {
opline = zend_emit_op(NULL, ZEND_DISCARD_EXCEPTION, NULL, NULL);
opline->op1_type = IS_TMP_VAR;
opline->op1.var = CG(context).fast_call_var;
}
/* Generator return types are handled separately */
if (!(CG(active_op_array)->fn_flags & ZEND_ACC_GENERATOR) && CG(active_op_array)->fn_flags & ZEND_ACC_HAS_RETURN_TYPE) {
zend_emit_return_type_check(expr_ast ? &expr_node : NULL, CG(active_op_array)->arg_info - 1);
@ -4573,6 +4574,7 @@ void zend_compile_try(zend_ast *ast) /* {{{ */
uint32_t try_catch_offset;
uint32_t *jmp_opnums = safe_emalloc(sizeof(uint32_t), catches->children, 0);
uint32_t orig_fast_call_var = CG(context).fast_call_var;
uint32_t orig_try_catch_offset = CG(context).try_catch_offset;
if (catches->children == 0 && !finally_ast) {
zend_error_noreturn(E_COMPILE_ERROR, "Cannot use try without catch or finally");
@ -4606,6 +4608,8 @@ void zend_compile_try(zend_ast *ast) /* {{{ */
zend_stack_push(&CG(loop_var_stack), &fast_call);
}
CG(context).try_catch_offset = try_catch_offset;
zend_compile_stmt(try_ast);
if (catches->children != 0) {
@ -4679,11 +4683,18 @@ void zend_compile_try(zend_ast *ast) /* {{{ */
}
if (finally_ast) {
zend_loop_var discard_exception;
uint32_t opnum_jmp = get_next_op_number(CG(active_op_array)) + 1;
/* Pop FAST_CALL from unwind stack */
zend_stack_del_top(&CG(loop_var_stack));
/* Push DISCARD_EXCEPTION on unwind stack */
discard_exception.opcode = ZEND_DISCARD_EXCEPTION;
discard_exception.var_type = IS_TMP_VAR;
discard_exception.var_num = CG(context).fast_call_var;
zend_stack_push(&CG(loop_var_stack), &discard_exception);
CG(zend_lineno) = finally_ast->lineno;
opline = zend_emit_op(NULL, ZEND_FAST_CALL, NULL, NULL);
@ -4704,12 +4715,18 @@ void zend_compile_try(zend_ast *ast) /* {{{ */
opline = zend_emit_op(NULL, ZEND_FAST_RET, NULL, NULL);
opline->op1_type = IS_TMP_VAR;
opline->op1.var = CG(context).fast_call_var;
opline->op2.num = orig_try_catch_offset;
zend_update_jump_target_to_next(opnum_jmp);
CG(context).fast_call_var = orig_fast_call_var;
/* Pop DISCARD_EXCEPTION from unwind stack */
zend_stack_del_top(&CG(loop_var_stack));
}
CG(context).try_catch_offset = orig_try_catch_offset;
efree(jmp_opnums);
}
/* }}} */

View File

@ -191,6 +191,7 @@ typedef struct _zend_oparray_context {
int backpatch_count;
int in_finally;
uint32_t fast_call_var;
uint32_t try_catch_offset;
int current_brk_cont;
int last_brk_cont;
zend_brk_cont_element *brk_cont_array;
@ -959,11 +960,6 @@ static zend_always_inline int zend_check_arg_send_type(const zend_function *zf,
#define ZEND_RETURNS_FUNCTION 1<<0
#define ZEND_RETURNS_VALUE 1<<1
#define ZEND_FAST_RET_TO_CATCH 1
#define ZEND_FAST_RET_TO_FINALLY 2
#define ZEND_FAST_CALL_FROM_FINALLY 1
#define ZEND_ARRAY_ELEMENT_REF (1<<0)
#define ZEND_ARRAY_NOT_PACKED (1<<1)
#define ZEND_ARRAY_SIZE_SHIFT 2

View File

@ -534,57 +534,6 @@ static void zend_check_finally_breakout(zend_op_array *op_array, uint32_t op_num
}
}
static void zend_resolve_fast_call(zend_op_array *op_array, uint32_t op_num)
{
int i;
uint32_t finally_num = (uint32_t)-1;
for (i = 0; i < op_array->last_try_catch; i++) {
if (op_num >= op_array->try_catch_array[i].finally_op
&& op_num < op_array->try_catch_array[i].finally_end) {
finally_num = i;
}
}
if (finally_num != (uint32_t)-1) {
/* Must be ZEND_FAST_CALL */
ZEND_ASSERT(op_array->opcodes[op_array->try_catch_array[finally_num].finally_op - 2].opcode == ZEND_FAST_CALL);
op_array->opcodes[op_num].extended_value = ZEND_FAST_CALL_FROM_FINALLY;
}
}
static void zend_resolve_finally_ret(zend_op_array *op_array, uint32_t op_num)
{
int i;
uint32_t finally_num = (uint32_t)-1;
uint32_t catch_num = (uint32_t)-1;
for (i = 0; i < op_array->last_try_catch; i++) {
if (op_array->try_catch_array[i].try_op > op_num) {
break;
}
if (op_num < op_array->try_catch_array[i].finally_op) {
finally_num = i;
}
if (op_num < op_array->try_catch_array[i].catch_op) {
catch_num = i;
}
}
if (finally_num != (uint32_t)-1 &&
(catch_num == (uint32_t)-1 ||
op_array->try_catch_array[catch_num].catch_op >=
op_array->try_catch_array[finally_num].finally_op)) {
/* in case of unhandled exception return to upward finally block */
op_array->opcodes[op_num].extended_value = ZEND_FAST_RET_TO_FINALLY;
op_array->opcodes[op_num].op2.num = finally_num;
} else if (catch_num != (uint32_t)-1) {
/* in case of unhandled exception return to upward catch block */
op_array->opcodes[op_num].extended_value = ZEND_FAST_RET_TO_CATCH;
op_array->opcodes[op_num].op2.num = catch_num;
}
}
static uint32_t zend_get_brk_cont_target(const zend_op_array *op_array, const zend_op *opline) {
int nest_levels = opline->op2.num;
int array_offset = opline->op1.num;
@ -633,12 +582,8 @@ ZEND_API int pass_two(zend_op_array *op_array)
switch (opline->opcode) {
case ZEND_FAST_CALL:
opline->op1.opline_num = op_array->try_catch_array[opline->op1.num].finally_op;
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_BRK:
case ZEND_CONT:
{

View File

@ -7105,12 +7105,58 @@ ZEND_VM_HANDLER(155, ZEND_BIND_TRAITS, ANY, ANY)
ZEND_VM_NEXT_OPCODE_CHECK_EXCEPTION();
}
ZEND_VM_HELPER(zend_dispatch_try_catch_finally_helper, ANY, ANY, uint32_t try_catch_offset, uint32_t op_num)
{
zend_object *ex = EG(exception);
/* Walk try/catch/finally structures upwards, performing the necessary actions */
while (try_catch_offset != (uint32_t) -1) {
zend_try_catch_element *try_catch =
&EX(func)->op_array.try_catch_array[try_catch_offset];
if (op_num < try_catch->catch_op) {
/* Go to catch block */
cleanup_live_vars(execute_data, op_num, try_catch->catch_op);
ZEND_VM_SET_OPCODE(&EX(func)->op_array.opcodes[try_catch->catch_op]);
ZEND_VM_CONTINUE();
} else if (op_num < try_catch->finally_op) {
/* Go to finally block */
zval *fast_call = EX_VAR(EX(func)->op_array.opcodes[try_catch->finally_end].op1.var);
cleanup_live_vars(execute_data, op_num, try_catch->finally_op);
Z_OBJ_P(fast_call) = EG(exception);
EG(exception) = NULL;
fast_call->u2.lineno = (uint32_t)-1;
ZEND_VM_SET_OPCODE(&EX(func)->op_array.opcodes[try_catch->finally_op]);
ZEND_VM_CONTINUE();
} else if (op_num < try_catch->finally_end) {
/* Chain potential exception from wrapping finally block */
zval *fast_call = EX_VAR(EX(func)->op_array.opcodes[try_catch->finally_end].op1.var);
if (Z_OBJ_P(fast_call)) {
zend_exception_set_previous(ex, Z_OBJ_P(fast_call));
ex = Z_OBJ_P(fast_call);
}
}
try_catch_offset--;
}
/* Uncaught exception */
cleanup_live_vars(execute_data, op_num, 0);
if (UNEXPECTED((EX_CALL_INFO() & ZEND_CALL_GENERATOR) != 0)) {
zend_generator *generator = zend_get_running_generator(execute_data);
zend_generator_close(generator, 1);
ZEND_VM_RETURN();
} else {
ZEND_VM_DISPATCH_TO_HELPER(zend_leave_helper);
}
}
ZEND_VM_HANDLER(149, ZEND_HANDLE_EXCEPTION, ANY, ANY)
{
uint32_t op_num = EG(opline_before_exception) - EX(func)->op_array.opcodes;
int i;
uint32_t catch_op_num = 0, finally_op_num = 0, finally_op_end = 0;
int in_finally = 0;
uint32_t throw_op_num = EG(opline_before_exception) - EX(func)->op_array.opcodes;
uint32_t i, current_try_catch_offset = (uint32_t) -1;
{
const zend_op *exc_opline = EG(opline_before_exception);
@ -7118,68 +7164,27 @@ ZEND_VM_HANDLER(149, ZEND_HANDLE_EXCEPTION, ANY, ANY)
&& exc_opline->extended_value & ZEND_FREE_ON_RETURN) {
/* exceptions thrown because of loop var destruction on return/break/...
* are logically thrown at the end of the foreach loop, so adjust the
* op_num.
* throw_op_num.
*/
op_num = EX(func)->op_array.live_range[exc_opline->op2.num].end;
throw_op_num = EX(func)->op_array.live_range[exc_opline->op2.num].end;
}
}
/* Find the innermost try/catch/finally the exception was thrown in */
for (i = 0; i < EX(func)->op_array.last_try_catch; i++) {
if (EX(func)->op_array.try_catch_array[i].try_op > op_num) {
zend_try_catch_element *try_catch = &EX(func)->op_array.try_catch_array[i];
if (try_catch->try_op > throw_op_num) {
/* further blocks will not be relevant... */
break;
}
in_finally = 0;
if (op_num < EX(func)->op_array.try_catch_array[i].catch_op) {
catch_op_num = EX(func)->op_array.try_catch_array[i].catch_op;
}
if (op_num < EX(func)->op_array.try_catch_array[i].finally_op) {
finally_op_num = EX(func)->op_array.try_catch_array[i].finally_op;
finally_op_end = EX(func)->op_array.try_catch_array[i].finally_end;
}
if (op_num >= EX(func)->op_array.try_catch_array[i].finally_op &&
op_num < EX(func)->op_array.try_catch_array[i].finally_end) {
finally_op_end = EX(func)->op_array.try_catch_array[i].finally_end;
in_finally = 1;
if (throw_op_num < try_catch->catch_op || throw_op_num < try_catch->finally_end) {
current_try_catch_offset = i;
}
}
cleanup_unfinished_calls(execute_data, op_num);
cleanup_unfinished_calls(execute_data, throw_op_num);
if (finally_op_num && (!catch_op_num || catch_op_num >= finally_op_num)) {
zval *fast_call = EX_VAR(EX(func)->op_array.opcodes[finally_op_end].op1.var);
cleanup_live_vars(execute_data, op_num, finally_op_num);
if (in_finally && Z_OBJ_P(fast_call)) {
zend_exception_set_previous(EG(exception), Z_OBJ_P(fast_call));
}
Z_OBJ_P(fast_call) = EG(exception);
EG(exception) = NULL;
fast_call->u2.lineno = (uint32_t)-1;
ZEND_VM_SET_OPCODE(&EX(func)->op_array.opcodes[finally_op_num]);
ZEND_VM_CONTINUE();
} else {
cleanup_live_vars(execute_data, op_num, catch_op_num);
if (in_finally) {
/* we are going out of current finally scope */
zval *fast_call = EX_VAR(EX(func)->op_array.opcodes[finally_op_end].op1.var);
if (Z_OBJ_P(fast_call)) {
zend_exception_set_previous(EG(exception), Z_OBJ_P(fast_call));
Z_OBJ_P(fast_call) = NULL;
}
}
if (catch_op_num) {
ZEND_VM_SET_OPCODE(&EX(func)->op_array.opcodes[catch_op_num]);
ZEND_VM_CONTINUE();
} else if (UNEXPECTED((EX_CALL_INFO() & ZEND_CALL_GENERATOR) != 0)) {
zend_generator *generator = zend_get_running_generator(execute_data);
zend_generator_close(generator, 1);
ZEND_VM_RETURN();
} else {
ZEND_VM_DISPATCH_TO_HELPER(zend_leave_helper);
}
}
ZEND_VM_DISPATCH_TO_HELPER(zend_dispatch_try_catch_finally_helper, try_catch_offset, current_try_catch_offset, op_num, throw_op_num);
}
ZEND_VM_HANDLER(146, ZEND_VERIFY_ABSTRACT_CLASS, ANY, ANY)
@ -7539,71 +7544,43 @@ ZEND_VM_HANDLER(159, ZEND_DISCARD_EXCEPTION, ANY, ANY)
/* discard the previously thrown exception */
OBJ_RELEASE(Z_OBJ_P(fast_call));
Z_OBJ_P(fast_call) = NULL;
ZEND_VM_NEXT_OPCODE_CHECK_EXCEPTION();
}
ZEND_VM_NEXT_OPCODE();
}
ZEND_VM_HANDLER(162, ZEND_FAST_CALL, JMP_ADDR, ANY, FAST_CALL)
ZEND_VM_HANDLER(162, ZEND_FAST_CALL, JMP_ADDR, ANY)
{
USE_OPLINE
zval *fast_call = EX_VAR(opline->result.var);
if (opline->extended_value == ZEND_FAST_CALL_FROM_FINALLY && UNEXPECTED(Z_OBJ_P(fast_call) != NULL)) {
fast_call->u2.lineno = (uint32_t)-1;
} else {
Z_OBJ_P(fast_call) = NULL;
/* set return address */
fast_call->u2.lineno = opline - EX(func)->op_array.opcodes;
}
Z_OBJ_P(fast_call) = NULL;
/* set return address */
fast_call->u2.lineno = opline - EX(func)->op_array.opcodes;
ZEND_VM_SET_OPCODE(OP_JMP_ADDR(opline, opline->op1));
ZEND_VM_CONTINUE();
}
ZEND_VM_HANDLER(163, ZEND_FAST_RET, ANY, TRY_CATCH, FAST_RET)
ZEND_VM_HANDLER(163, ZEND_FAST_RET, ANY, TRY_CATCH)
{
USE_OPLINE
zval *fast_call = EX_VAR(opline->op1.var);
uint32_t current_try_catch_offset, current_op_num;
if (fast_call->u2.lineno != (uint32_t)-1) {
const zend_op *fast_ret = EX(func)->op_array.opcodes + fast_call->u2.lineno;
ZEND_VM_SET_OPCODE(fast_ret + 1);
ZEND_VM_CONTINUE();
} else {
/* special case for unhandled exceptions */
USE_OPLINE
if (opline->extended_value == ZEND_FAST_RET_TO_FINALLY) {
uint32_t finally_op = EX(func)->op_array.try_catch_array[opline->op2.num].finally_op;
uint32_t finally_end = EX(func)->op_array.try_catch_array[opline->op2.num].finally_end;
zval *next_fast_call = EX_VAR(EX(func)->op_array.opcodes[finally_end].op1.var);
Z_OBJ_P(next_fast_call) = Z_OBJ_P(fast_call);
next_fast_call->u2.lineno = (uint32_t)-1;
cleanup_live_vars(execute_data, opline - EX(func)->op_array.opcodes, finally_op);
ZEND_VM_SET_OPCODE(&EX(func)->op_array.opcodes[finally_op]);
ZEND_VM_CONTINUE();
} else {
EG(exception) = Z_OBJ_P(fast_call);
Z_OBJ_P(fast_call) = NULL;
if (opline->extended_value == ZEND_FAST_RET_TO_CATCH) {
uint32_t catch_op = EX(func)->op_array.try_catch_array[opline->op2.num].catch_op;
cleanup_live_vars(execute_data, opline - EX(func)->op_array.opcodes, catch_op);
ZEND_VM_SET_OPCODE(&EX(func)->op_array.opcodes[catch_op]);
ZEND_VM_CONTINUE();
} else {
cleanup_live_vars(execute_data, opline - EX(func)->op_array.opcodes, 0);
if (UNEXPECTED((EX_CALL_INFO() & ZEND_CALL_GENERATOR) != 0)) {
zend_generator *generator = zend_get_running_generator(execute_data);
zend_generator_close(generator, 1);
ZEND_VM_RETURN();
} else {
ZEND_VM_DISPATCH_TO_HELPER(zend_leave_helper);
}
}
}
}
/* special case for unhandled exceptions */
EG(exception) = Z_OBJ_P(fast_call);
Z_OBJ_P(fast_call) = NULL;
current_try_catch_offset = opline->op2.num;
current_op_num = opline - EX(func)->op_array.opcodes;
ZEND_VM_DISPATCH_TO_HELPER(zend_dispatch_try_catch_finally_helper, try_catch_offset, current_try_catch_offset, op_num, current_op_num);
}
ZEND_VM_HANDLER(168, ZEND_BIND_GLOBAL, CV, CONST)

View File

@ -1690,12 +1690,58 @@ static ZEND_OPCODE_HANDLER_RET ZEND_FASTCALL ZEND_BIND_TRAITS_SPEC_HANDLER(ZEND_
ZEND_VM_NEXT_OPCODE_CHECK_EXCEPTION();
}
static ZEND_OPCODE_HANDLER_RET ZEND_FASTCALL zend_dispatch_try_catch_finally_helper_SPEC(uint32_t try_catch_offset, uint32_t op_num ZEND_OPCODE_HANDLER_ARGS_DC)
{
zend_object *ex = EG(exception);
/* Walk try/catch/finally structures upwards, performing the necessary actions */
while (try_catch_offset != (uint32_t) -1) {
zend_try_catch_element *try_catch =
&EX(func)->op_array.try_catch_array[try_catch_offset];
if (op_num < try_catch->catch_op) {
/* Go to catch block */
cleanup_live_vars(execute_data, op_num, try_catch->catch_op);
ZEND_VM_SET_OPCODE(&EX(func)->op_array.opcodes[try_catch->catch_op]);
ZEND_VM_CONTINUE();
} else if (op_num < try_catch->finally_op) {
/* Go to finally block */
zval *fast_call = EX_VAR(EX(func)->op_array.opcodes[try_catch->finally_end].op1.var);
cleanup_live_vars(execute_data, op_num, try_catch->finally_op);
Z_OBJ_P(fast_call) = EG(exception);
EG(exception) = NULL;
fast_call->u2.lineno = (uint32_t)-1;
ZEND_VM_SET_OPCODE(&EX(func)->op_array.opcodes[try_catch->finally_op]);
ZEND_VM_CONTINUE();
} else if (op_num < try_catch->finally_end) {
/* Chain potential exception from wrapping finally block */
zval *fast_call = EX_VAR(EX(func)->op_array.opcodes[try_catch->finally_end].op1.var);
if (Z_OBJ_P(fast_call)) {
zend_exception_set_previous(ex, Z_OBJ_P(fast_call));
ex = Z_OBJ_P(fast_call);
}
}
try_catch_offset--;
}
/* Uncaught exception */
cleanup_live_vars(execute_data, op_num, 0);
if (UNEXPECTED((EX_CALL_INFO() & ZEND_CALL_GENERATOR) != 0)) {
zend_generator *generator = zend_get_running_generator(execute_data);
zend_generator_close(generator, 1);
ZEND_VM_RETURN();
} else {
ZEND_VM_TAIL_CALL(zend_leave_helper_SPEC(ZEND_OPCODE_HANDLER_ARGS_PASSTHRU));
}
}
static ZEND_OPCODE_HANDLER_RET ZEND_FASTCALL ZEND_HANDLE_EXCEPTION_SPEC_HANDLER(ZEND_OPCODE_HANDLER_ARGS)
{
uint32_t op_num = EG(opline_before_exception) - EX(func)->op_array.opcodes;
int i;
uint32_t catch_op_num = 0, finally_op_num = 0, finally_op_end = 0;
int in_finally = 0;
uint32_t throw_op_num = EG(opline_before_exception) - EX(func)->op_array.opcodes;
uint32_t i, current_try_catch_offset = (uint32_t) -1;
{
const zend_op *exc_opline = EG(opline_before_exception);
@ -1703,68 +1749,27 @@ static ZEND_OPCODE_HANDLER_RET ZEND_FASTCALL ZEND_HANDLE_EXCEPTION_SPEC_HANDLER(
&& exc_opline->extended_value & ZEND_FREE_ON_RETURN) {
/* exceptions thrown because of loop var destruction on return/break/...
* are logically thrown at the end of the foreach loop, so adjust the
* op_num.
* throw_op_num.
*/
op_num = EX(func)->op_array.live_range[exc_opline->op2.num].end;
throw_op_num = EX(func)->op_array.live_range[exc_opline->op2.num].end;
}
}
/* Find the innermost try/catch/finally the exception was thrown in */
for (i = 0; i < EX(func)->op_array.last_try_catch; i++) {
if (EX(func)->op_array.try_catch_array[i].try_op > op_num) {
zend_try_catch_element *try_catch = &EX(func)->op_array.try_catch_array[i];
if (try_catch->try_op > throw_op_num) {
/* further blocks will not be relevant... */
break;
}
in_finally = 0;
if (op_num < EX(func)->op_array.try_catch_array[i].catch_op) {
catch_op_num = EX(func)->op_array.try_catch_array[i].catch_op;
}
if (op_num < EX(func)->op_array.try_catch_array[i].finally_op) {
finally_op_num = EX(func)->op_array.try_catch_array[i].finally_op;
finally_op_end = EX(func)->op_array.try_catch_array[i].finally_end;
}
if (op_num >= EX(func)->op_array.try_catch_array[i].finally_op &&
op_num < EX(func)->op_array.try_catch_array[i].finally_end) {
finally_op_end = EX(func)->op_array.try_catch_array[i].finally_end;
in_finally = 1;
if (throw_op_num < try_catch->catch_op || throw_op_num < try_catch->finally_end) {
current_try_catch_offset = i;
}
}
cleanup_unfinished_calls(execute_data, op_num);
cleanup_unfinished_calls(execute_data, throw_op_num);
if (finally_op_num && (!catch_op_num || catch_op_num >= finally_op_num)) {
zval *fast_call = EX_VAR(EX(func)->op_array.opcodes[finally_op_end].op1.var);
cleanup_live_vars(execute_data, op_num, finally_op_num);
if (in_finally && Z_OBJ_P(fast_call)) {
zend_exception_set_previous(EG(exception), Z_OBJ_P(fast_call));
}
Z_OBJ_P(fast_call) = EG(exception);
EG(exception) = NULL;
fast_call->u2.lineno = (uint32_t)-1;
ZEND_VM_SET_OPCODE(&EX(func)->op_array.opcodes[finally_op_num]);
ZEND_VM_CONTINUE();
} else {
cleanup_live_vars(execute_data, op_num, catch_op_num);
if (in_finally) {
/* we are going out of current finally scope */
zval *fast_call = EX_VAR(EX(func)->op_array.opcodes[finally_op_end].op1.var);
if (Z_OBJ_P(fast_call)) {
zend_exception_set_previous(EG(exception), Z_OBJ_P(fast_call));
Z_OBJ_P(fast_call) = NULL;
}
}
if (catch_op_num) {
ZEND_VM_SET_OPCODE(&EX(func)->op_array.opcodes[catch_op_num]);
ZEND_VM_CONTINUE();
} else if (UNEXPECTED((EX_CALL_INFO() & ZEND_CALL_GENERATOR) != 0)) {
zend_generator *generator = zend_get_running_generator(execute_data);
zend_generator_close(generator, 1);
ZEND_VM_RETURN();
} else {
ZEND_VM_TAIL_CALL(zend_leave_helper_SPEC(ZEND_OPCODE_HANDLER_ARGS_PASSTHRU));
}
}
ZEND_VM_TAIL_CALL(zend_dispatch_try_catch_finally_helper_SPEC(current_try_catch_offset, throw_op_num ZEND_OPCODE_HANDLER_ARGS_PASSTHRU_CC));
}
static ZEND_OPCODE_HANDLER_RET ZEND_FASTCALL ZEND_VERIFY_ABSTRACT_CLASS_SPEC_HANDLER(ZEND_OPCODE_HANDLER_ARGS)
@ -1818,6 +1823,7 @@ static ZEND_OPCODE_HANDLER_RET ZEND_FASTCALL ZEND_DISCARD_EXCEPTION_SPEC_HANDLER
/* discard the previously thrown exception */
OBJ_RELEASE(Z_OBJ_P(fast_call));
Z_OBJ_P(fast_call) = NULL;
ZEND_VM_NEXT_OPCODE_CHECK_EXCEPTION();
}
ZEND_VM_NEXT_OPCODE();
@ -1828,13 +1834,9 @@ static ZEND_OPCODE_HANDLER_RET ZEND_FASTCALL ZEND_FAST_CALL_SPEC_HANDLER(ZEND_OP
USE_OPLINE
zval *fast_call = EX_VAR(opline->result.var);
if (opline->extended_value == ZEND_FAST_CALL_FROM_FINALLY && UNEXPECTED(Z_OBJ_P(fast_call) != NULL)) {
fast_call->u2.lineno = (uint32_t)-1;
} else {
Z_OBJ_P(fast_call) = NULL;
/* set return address */
fast_call->u2.lineno = opline - EX(func)->op_array.opcodes;
}
Z_OBJ_P(fast_call) = NULL;
/* set return address */
fast_call->u2.lineno = opline - EX(func)->op_array.opcodes;
ZEND_VM_SET_OPCODE(OP_JMP_ADDR(opline, opline->op1));
ZEND_VM_CONTINUE();
}
@ -1843,46 +1845,21 @@ static ZEND_OPCODE_HANDLER_RET ZEND_FASTCALL ZEND_FAST_RET_SPEC_HANDLER(ZEND_OPC
{
USE_OPLINE
zval *fast_call = EX_VAR(opline->op1.var);
uint32_t current_try_catch_offset, current_op_num;
if (fast_call->u2.lineno != (uint32_t)-1) {
const zend_op *fast_ret = EX(func)->op_array.opcodes + fast_call->u2.lineno;
ZEND_VM_SET_OPCODE(fast_ret + 1);
ZEND_VM_CONTINUE();
} else {
/* special case for unhandled exceptions */
USE_OPLINE
if (opline->extended_value == ZEND_FAST_RET_TO_FINALLY) {
uint32_t finally_op = EX(func)->op_array.try_catch_array[opline->op2.num].finally_op;
uint32_t finally_end = EX(func)->op_array.try_catch_array[opline->op2.num].finally_end;
zval *next_fast_call = EX_VAR(EX(func)->op_array.opcodes[finally_end].op1.var);
Z_OBJ_P(next_fast_call) = Z_OBJ_P(fast_call);
next_fast_call->u2.lineno = (uint32_t)-1;
cleanup_live_vars(execute_data, opline - EX(func)->op_array.opcodes, finally_op);
ZEND_VM_SET_OPCODE(&EX(func)->op_array.opcodes[finally_op]);
ZEND_VM_CONTINUE();
} else {
EG(exception) = Z_OBJ_P(fast_call);
Z_OBJ_P(fast_call) = NULL;
if (opline->extended_value == ZEND_FAST_RET_TO_CATCH) {
uint32_t catch_op = EX(func)->op_array.try_catch_array[opline->op2.num].catch_op;
cleanup_live_vars(execute_data, opline - EX(func)->op_array.opcodes, catch_op);
ZEND_VM_SET_OPCODE(&EX(func)->op_array.opcodes[catch_op]);
ZEND_VM_CONTINUE();
} else {
cleanup_live_vars(execute_data, opline - EX(func)->op_array.opcodes, 0);
if (UNEXPECTED((EX_CALL_INFO() & ZEND_CALL_GENERATOR) != 0)) {
zend_generator *generator = zend_get_running_generator(execute_data);
zend_generator_close(generator, 1);
ZEND_VM_RETURN();
} else {
ZEND_VM_TAIL_CALL(zend_leave_helper_SPEC(ZEND_OPCODE_HANDLER_ARGS_PASSTHRU));
}
}
}
}
/* special case for unhandled exceptions */
EG(exception) = Z_OBJ_P(fast_call);
Z_OBJ_P(fast_call) = NULL;
current_try_catch_offset = opline->op2.num;
current_op_num = opline - EX(func)->op_array.opcodes;
ZEND_VM_TAIL_CALL(zend_dispatch_try_catch_finally_helper_SPEC(current_try_catch_offset, current_op_num ZEND_OPCODE_HANDLER_ARGS_PASSTHRU_CC));
}
static ZEND_OPCODE_HANDLER_RET ZEND_FASTCALL ZEND_ASSERT_CHECK_SPEC_HANDLER(ZEND_OPCODE_HANDLER_ARGS)

View File

@ -83,8 +83,8 @@ $vm_op_flags = array(
"ZEND_VM_EXT_CONST_FETCH" => 0x06000000,
"ZEND_VM_EXT_TYPE" => 0x07000000,
"ZEND_VM_EXT_EVAL" => 0x08000000,
"ZEND_VM_EXT_FAST_CALL" => 0x09000000,
"ZEND_VM_EXT_FAST_RET" => 0x0a000000,
// unused 0x09000000,
// unused 0x0a000000,
"ZEND_VM_EXT_SRC" => 0x0b000000,
"ZEND_VM_EXT_SEND" => 0x0c000000,
"ZEND_VM_NO_CONST_CONST" => 0x40000000,
@ -124,8 +124,6 @@ $vm_ext_decode = array(
"ARRAY_INIT" => ZEND_VM_EXT_ARRAY_INIT,
"TYPE" => ZEND_VM_EXT_TYPE,
"EVAL" => ZEND_VM_EXT_EVAL,
"FAST_CALL" => ZEND_VM_EXT_FAST_CALL,
"FAST_RET" => ZEND_VM_EXT_FAST_RET,
"ISSET" => ZEND_VM_EXT_ISSET,
"ARG_NUM" => ZEND_VM_EXT_ARG_NUM,
"REF" => ZEND_VM_EXT_REF,

View File

@ -371,8 +371,8 @@ static uint32_t zend_vm_opcodes_flags[184] = {
0x00000000,
0x0b000303,
0x00000003,
0x09000020,
0x0a003000,
0x00000020,
0x00003000,
0x00000010,
0x00000000,
0x00000707,

View File

@ -54,8 +54,6 @@
#define ZEND_VM_EXT_CONST_FETCH 0x06000000
#define ZEND_VM_EXT_TYPE 0x07000000
#define ZEND_VM_EXT_EVAL 0x08000000
#define ZEND_VM_EXT_FAST_CALL 0x09000000
#define ZEND_VM_EXT_FAST_RET 0x0a000000
#define ZEND_VM_EXT_SRC 0x0b000000
#define ZEND_VM_EXT_SEND 0x0c000000
#define ZEND_VM_NO_CONST_CONST 0x40000000

View File

@ -898,9 +898,8 @@ static void assemble_code_blocks(zend_cfg *cfg, zend_op_array *op_array)
zend_op *opline = new_opcodes;
zend_op *end = opline + len;
while (opline < end) {
if ((opline->opcode == ZEND_FAST_CALL ||
opline->opcode == ZEND_FAST_RET) &&
opline->extended_value &&
if (opline->opcode == ZEND_FAST_RET &&
opline->op2.num != (uint32_t)-1 &&
opline->op2.num < (uint32_t)j) {
opline->op2.num = map[opline->op2.num];
}

View File

@ -92,7 +92,7 @@ static void zend_dump_unused_op(const zend_op *opline, znode_op op, uint32_t fla
if (ZEND_VM_OP_NUM == (flags & ZEND_VM_OP_MASK)) {
fprintf(stderr, " %u", op.num);
} else if (ZEND_VM_OP_TRY_CATCH == (flags & ZEND_VM_OP_MASK)) {
if (opline->opcode != ZEND_FAST_RET || opline->extended_value) {
if (op.num != (uint32_t)-1) {
fprintf(stderr, " try-catch(%u)", op.num);
}
} else if (ZEND_VM_OP_LIVE_RANGE == (flags & ZEND_VM_OP_MASK)) {
@ -498,16 +498,6 @@ static void zend_dump_op(const zend_op_array *op_array, const zend_basic_block *
fprintf(stderr, " (\?\?\?)");
break;
}
} else if (ZEND_VM_EXT_FAST_CALL == (flags & ZEND_VM_EXT_MASK)) {
if (opline->extended_value == ZEND_FAST_CALL_FROM_FINALLY) {
fprintf(stderr, " (from-finally)");
}
} else if (ZEND_VM_EXT_FAST_RET == (flags & ZEND_VM_EXT_MASK)) {
if (opline->extended_value == ZEND_FAST_RET_TO_CATCH) {
fprintf(stderr, " (to-catch)");
} else if (opline->extended_value == ZEND_FAST_RET_TO_FINALLY) {
fprintf(stderr, " (to-finally)");
}
} else if (ZEND_VM_EXT_SRC == (flags & ZEND_VM_EXT_MASK)) {
if (opline->extended_value == ZEND_RETURNS_VALUE) {
fprintf(stderr, " (value)");

View File

@ -74,7 +74,7 @@ char *phpdbg_decode_input_op(
} else if (ZEND_VM_OP_NUM == (flags & ZEND_VM_OP_MASK)) {
spprintf(&result, 0, "%" PRIu32, op.num);
} else if (ZEND_VM_OP_TRY_CATCH == (flags & ZEND_VM_OP_MASK)) {
if (opline->opcode != ZEND_FAST_RET || opline->extended_value) {
if (op.num != (uint32_t)-1) {
spprintf(&result, 0, "try-catch(%" PRIu32 ")", op.num);
}
} else if (ZEND_VM_OP_LIVE_RANGE == (flags & ZEND_VM_OP_MASK)) {
@ -99,21 +99,6 @@ char *phpdbg_decode_opline(zend_op_array *ops, zend_op *opline) /*{{{ */
uint32_t flags = zend_get_opcode_flags(opline->opcode);
char *result, *decode[4] = {NULL, NULL, NULL, NULL};
/* EX */
switch (opline->opcode) {
case ZEND_FAST_CALL:
if (opline->extended_value == ZEND_FAST_CALL_FROM_FINALLY) {
decode[0] = estrdup("FAST_CALL<FROM_FINALLY>");
}
break;
case ZEND_FAST_RET:
if (opline->extended_value != 0) {
spprintf(&decode[0], 0, "FAST_RET<%s>",
opline->extended_value == ZEND_FAST_RET_TO_CATCH ? "TO_CATCH" : "TO_FINALLY");
}
break;
}
/* OP1 */
decode[1] = phpdbg_decode_input_op(
ops, opline, opline->op1, opline->op1_type, ZEND_VM_OP1_FLAGS(flags));

View File

@ -25,7 +25,7 @@ prompt> [L7 %s ECHO "ok "
00008: }
00009: } catch (Error $e) {
prompt> ok
[L7 %s FAST_RET<TO_CATCH> ~%d try-catch(0) %s]
[L7 %s FAST_RET ~%d try-catch(0) %s]
[L9 %s CATCH "Error" $e 1 %s]
>00005: x();
00006: } finally {