Improve finally fix

This commit is contained in:
Nikita Popov 2016-05-24 14:55:27 +02:00
parent da5c727499
commit 4f21c06c2e
11 changed files with 329 additions and 191 deletions

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,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

@ -1021,6 +1021,7 @@ static uint32_t zend_add_try_element(uint32_t try_op) /* {{{ */
elem->catch_op = 0;
elem->finally_op = 0;
elem->finally_end = 0;
elem->parent = CG(context).try_catch_offset;
return try_catch_offset;
}
@ -4009,6 +4010,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;
@ -4058,13 +4065,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;
opline->op2.num = CG(context).try_catch_offset;
}
/* 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);
@ -4684,11 +4684,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);
@ -4714,9 +4721,12 @@ void zend_compile_try(zend_ast *ast) /* {{{ */
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 = try_catch_offset;
CG(context).try_catch_offset = orig_try_catch_offset;
efree(jmp_opnums);
}

View file

@ -169,6 +169,7 @@ typedef struct _zend_try_catch_element {
uint32_t catch_op; /* ketchup! */
uint32_t finally_op;
uint32_t finally_end;
uint32_t parent;
} zend_try_catch_element;
#define ZEND_LIVE_TMPVAR 0

View file

@ -7108,9 +7108,8 @@ ZEND_VM_HANDLER(155, ZEND_BIND_TRAITS, ANY, ANY)
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 i, try_catch_offset = (uint32_t) -1;
zend_object *ex = EG(exception);
{
const zend_op *exc_opline = EG(opline_before_exception);
@ -7124,70 +7123,63 @@ ZEND_VM_HANDLER(149, ZEND_HANDLE_EXCEPTION, ANY, ANY)
}
}
/* 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 > op_num) {
/* further blocks will not be relevant... */
break;
}
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 (op_num < try_catch->catch_op || op_num < try_catch->finally_end) {
try_catch_offset = i;
}
}
cleanup_unfinished_calls(execute_data, 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);
/* 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];
cleanup_live_vars(execute_data, op_num, finally_op_num);
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;
uint32_t try_catch_offset;
zend_object *obj = EG(exception);
do {
if (catch_op_num && catch_op_num < finally_op_end) {
break;
}
fast_call = EX_VAR(EX(func)->op_array.opcodes[finally_op_end].op1.var);
if (Z_OBJ_P(fast_call)) {
zend_exception_set_previous(obj, Z_OBJ_P(fast_call));
obj = Z_OBJ_P(fast_call);
}
try_catch_offset = EX(func)->op_array.opcodes[finally_op_end].op2.num;
if (try_catch_offset == (uint32_t)-1) {
break;
}
finally_op_end = EX(func)->op_array.try_catch_array[try_catch_offset].finally_end;
} while (finally_op_end);
}
if (catch_op_num) {
ZEND_VM_SET_OPCODE(&EX(func)->op_array.opcodes[catch_op_num]);
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 (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);
}
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();
}
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 = try_catch->parent;
}
/* 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);
}
}
@ -7537,11 +7529,10 @@ ZEND_VM_HANDLER(142, ZEND_YIELD_FROM, CONST|TMP|VAR|CV, ANY)
ZEND_VM_RETURN();
}
ZEND_VM_HANDLER(159, ZEND_DISCARD_EXCEPTION, ANY, TRY_CATCH)
ZEND_VM_HANDLER(159, ZEND_DISCARD_EXCEPTION, ANY, ANY)
{
USE_OPLINE
zval *fast_call = EX_VAR(opline->op1.var);
uint32_t try_catch_offset;
SAVE_OPLINE();
/* check for delayed exception */
@ -7550,21 +7541,6 @@ ZEND_VM_HANDLER(159, ZEND_DISCARD_EXCEPTION, ANY, TRY_CATCH)
OBJ_RELEASE(Z_OBJ_P(fast_call));
Z_OBJ_P(fast_call) = NULL;
}
try_catch_offset = EX(func)->op_array.opcodes[EX(func)->op_array.try_catch_array[opline->op2.num].finally_end].op2.num;
while (try_catch_offset != (uint32_t)-1) {
zend_try_catch_element *try_catch = EX(func)->op_array.try_catch_array + try_catch_offset;
zval *fast_call = EX_VAR(EX(func)->op_array.opcodes[try_catch->finally_end].op1.var);
if (try_catch->finally_op && try_catch->finally_op > opline - EX(func)->op_array.opcodes) {
break;
}
if (Z_OBJ_P(fast_call) != NULL) {
/* discard the previously thrown exception */
OBJ_RELEASE(Z_OBJ_P(fast_call));
Z_OBJ_P(fast_call) = NULL;
}
try_catch_offset = EX(func)->op_array.opcodes[try_catch->finally_end].op2.num;
}
ZEND_VM_NEXT_OPCODE();
}
@ -7589,28 +7565,25 @@ ZEND_VM_HANDLER(163, ZEND_FAST_RET, ANY, TRY_CATCH)
if (fast_call->u2.lineno != (uint32_t)-1) {
const zend_op *fast_ret = EX(func)->op_array.opcodes + fast_call->u2.lineno;
if (Z_OBJ_P(fast_call)) {
OBJ_RELEASE(Z_OBJ_P(fast_call));
Z_OBJ_P(fast_call) = NULL;
}
ZEND_VM_SET_OPCODE(fast_ret + 1);
ZEND_VM_CONTINUE();
} else {
/* special case for unhandled exceptions */
if (opline->op2.num != (uint32_t)-1) {
/* check upper finally block */
zend_try_catch_element *try_catch = EX(func)->op_array.try_catch_array + opline->op2.num;
uint32_t try_catch_offset = opline->op2.num;
while (try_catch_offset != (uint32_t)-1) {
/* check upper try block */
zend_try_catch_element *try_catch = EX(func)->op_array.try_catch_array + try_catch_offset;
if (try_catch->catch_op && opline < &EX(func)->op_array.opcodes[try_catch->catch_op]) {
EG(exception) = Z_OBJ_P(fast_call);
cleanup_live_vars(execute_data, opline - EX(func)->op_array.opcodes, try_catch->catch_op);
ZEND_VM_SET_OPCODE(&EX(func)->op_array.opcodes[try_catch->catch_op]);
ZEND_VM_CONTINUE();
} else {
} else if (try_catch->finally_op) {
uint32_t target;
zval *next_fast_call = EX_VAR(EX(func)->op_array.opcodes[try_catch->finally_end].op1.var);
if (try_catch->finally_op && opline < &EX(func)->op_array.opcodes[try_catch->finally_op]) {
if (opline < &EX(func)->op_array.opcodes[try_catch->finally_op]) {
target = try_catch->finally_op;
} else {
target = try_catch->finally_end;
@ -7624,17 +7597,18 @@ ZEND_VM_HANDLER(163, ZEND_FAST_RET, ANY, TRY_CATCH)
ZEND_VM_SET_OPCODE(&EX(func)->op_array.opcodes[target]);
ZEND_VM_CONTINUE();
}
try_catch_offset = try_catch->parent;
}
/* leave function with exception */
EG(exception) = Z_OBJ_P(fast_call);
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 {
/* leave function with exception */
EG(exception) = Z_OBJ_P(fast_call);
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);
}
ZEND_VM_DISPATCH_TO_HELPER(zend_leave_helper);
}
}
}

View file

@ -1693,9 +1693,8 @@ static ZEND_OPCODE_HANDLER_RET ZEND_FASTCALL ZEND_BIND_TRAITS_SPEC_HANDLER(ZEND_
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 i, try_catch_offset = (uint32_t) -1;
zend_object *ex = EG(exception);
{
const zend_op *exc_opline = EG(opline_before_exception);
@ -1709,70 +1708,63 @@ static ZEND_OPCODE_HANDLER_RET ZEND_FASTCALL ZEND_HANDLE_EXCEPTION_SPEC_HANDLER(
}
}
/* 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 > op_num) {
/* further blocks will not be relevant... */
break;
}
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 (op_num < try_catch->catch_op || op_num < try_catch->finally_end) {
try_catch_offset = i;
}
}
cleanup_unfinished_calls(execute_data, 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);
/* 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];
cleanup_live_vars(execute_data, op_num, finally_op_num);
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;
uint32_t try_catch_offset;
zend_object *obj = EG(exception);
do {
if (catch_op_num && catch_op_num < finally_op_end) {
break;
}
fast_call = EX_VAR(EX(func)->op_array.opcodes[finally_op_end].op1.var);
if (Z_OBJ_P(fast_call)) {
zend_exception_set_previous(obj, Z_OBJ_P(fast_call));
obj = Z_OBJ_P(fast_call);
}
try_catch_offset = EX(func)->op_array.opcodes[finally_op_end].op2.num;
if (try_catch_offset == (uint32_t)-1) {
break;
}
finally_op_end = EX(func)->op_array.try_catch_array[try_catch_offset].finally_end;
} while (finally_op_end);
}
if (catch_op_num) {
ZEND_VM_SET_OPCODE(&EX(func)->op_array.opcodes[catch_op_num]);
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 (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));
}
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();
}
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 = try_catch->parent;
}
/* 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));
}
}
@ -1820,7 +1812,6 @@ static ZEND_OPCODE_HANDLER_RET ZEND_FASTCALL ZEND_DISCARD_EXCEPTION_SPEC_HANDLER
{
USE_OPLINE
zval *fast_call = EX_VAR(opline->op1.var);
uint32_t try_catch_offset;
SAVE_OPLINE();
/* check for delayed exception */
@ -1829,21 +1820,6 @@ static ZEND_OPCODE_HANDLER_RET ZEND_FASTCALL ZEND_DISCARD_EXCEPTION_SPEC_HANDLER
OBJ_RELEASE(Z_OBJ_P(fast_call));
Z_OBJ_P(fast_call) = NULL;
}
try_catch_offset = EX(func)->op_array.opcodes[EX(func)->op_array.try_catch_array[opline->op2.num].finally_end].op2.num;
while (try_catch_offset != (uint32_t)-1) {
zend_try_catch_element *try_catch = EX(func)->op_array.try_catch_array + try_catch_offset;
zval *fast_call = EX_VAR(EX(func)->op_array.opcodes[try_catch->finally_end].op1.var);
if (try_catch->finally_op && try_catch->finally_op > opline - EX(func)->op_array.opcodes) {
break;
}
if (Z_OBJ_P(fast_call) != NULL) {
/* discard the previously thrown exception */
OBJ_RELEASE(Z_OBJ_P(fast_call));
Z_OBJ_P(fast_call) = NULL;
}
try_catch_offset = EX(func)->op_array.opcodes[try_catch->finally_end].op2.num;
}
ZEND_VM_NEXT_OPCODE();
}
@ -1868,28 +1844,25 @@ static ZEND_OPCODE_HANDLER_RET ZEND_FASTCALL ZEND_FAST_RET_SPEC_HANDLER(ZEND_OPC
if (fast_call->u2.lineno != (uint32_t)-1) {
const zend_op *fast_ret = EX(func)->op_array.opcodes + fast_call->u2.lineno;
if (Z_OBJ_P(fast_call)) {
OBJ_RELEASE(Z_OBJ_P(fast_call));
Z_OBJ_P(fast_call) = NULL;
}
ZEND_VM_SET_OPCODE(fast_ret + 1);
ZEND_VM_CONTINUE();
} else {
/* special case for unhandled exceptions */
if (opline->op2.num != (uint32_t)-1) {
/* check upper finally block */
zend_try_catch_element *try_catch = EX(func)->op_array.try_catch_array + opline->op2.num;
uint32_t try_catch_offset = opline->op2.num;
while (try_catch_offset != (uint32_t)-1) {
/* check upper try block */
zend_try_catch_element *try_catch = EX(func)->op_array.try_catch_array + try_catch_offset;
if (try_catch->catch_op && opline < &EX(func)->op_array.opcodes[try_catch->catch_op]) {
EG(exception) = Z_OBJ_P(fast_call);
cleanup_live_vars(execute_data, opline - EX(func)->op_array.opcodes, try_catch->catch_op);
ZEND_VM_SET_OPCODE(&EX(func)->op_array.opcodes[try_catch->catch_op]);
ZEND_VM_CONTINUE();
} else {
} else if (try_catch->finally_op) {
uint32_t target;
zval *next_fast_call = EX_VAR(EX(func)->op_array.opcodes[try_catch->finally_end].op1.var);
if (try_catch->finally_op && opline < &EX(func)->op_array.opcodes[try_catch->finally_op]) {
if (opline < &EX(func)->op_array.opcodes[try_catch->finally_op]) {
target = try_catch->finally_op;
} else {
target = try_catch->finally_end;
@ -1903,17 +1876,18 @@ static ZEND_OPCODE_HANDLER_RET ZEND_FASTCALL ZEND_FAST_RET_SPEC_HANDLER(ZEND_OPC
ZEND_VM_SET_OPCODE(&EX(func)->op_array.opcodes[target]);
ZEND_VM_CONTINUE();
}
try_catch_offset = try_catch->parent;
}
/* leave function with exception */
EG(exception) = Z_OBJ_P(fast_call);
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 {
/* leave function with exception */
EG(exception) = Z_OBJ_P(fast_call);
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));
}
ZEND_VM_TAIL_CALL(zend_leave_helper_SPEC(ZEND_OPCODE_HANDLER_ARGS_PASSTHRU));
}
}
}

View file

@ -368,7 +368,7 @@ static uint32_t zend_vm_opcodes_flags[184] = {
0x00000101,
0x05000000,
0x00000000,
0x00003000,
0x00000000,
0x0b000303,
0x00000003,
0x00000020,