diff --git a/Zend/tests/return_types/029.phpt b/Zend/tests/return_types/029.phpt index 011182df377..041b354130f 100644 --- a/Zend/tests/return_types/029.phpt +++ b/Zend/tests/return_types/029.phpt @@ -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} diff --git a/Zend/tests/try/try_finally_023.phpt b/Zend/tests/try/try_finally_023.phpt new file mode 100644 index 00000000000..e88eddb3b23 --- /dev/null +++ b/Zend/tests/try/try_finally_023.phpt @@ -0,0 +1,37 @@ +--TEST-- +Loop var dtor throwing exception during return inside try/catch inside finally +--FILE-- + +--EXPECTF-- +Exception: 1 in %s:%d +Stack trace: +#0 %s(%d): test() +#1 {main} diff --git a/Zend/tests/try/try_finally_024.phpt b/Zend/tests/try/try_finally_024.phpt new file mode 100644 index 00000000000..eb0b26acbf9 --- /dev/null +++ b/Zend/tests/try/try_finally_024.phpt @@ -0,0 +1,38 @@ +--TEST-- +Exception in finally inside finally following try/catch containing throwing try/finally +--FILE-- + +--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} diff --git a/Zend/tests/try/try_finally_025.phpt b/Zend/tests/try/try_finally_025.phpt new file mode 100644 index 00000000000..7ca535bcf68 --- /dev/null +++ b/Zend/tests/try/try_finally_025.phpt @@ -0,0 +1,28 @@ +--TEST-- +Throw in try of try/finally inside catch +--FILE-- + +--EXPECTF-- +Exception: 2 in %s:%d +Stack trace: +#0 %s(%d): test() +#1 {main} diff --git a/Zend/tests/try/try_finally_026.phpt b/Zend/tests/try/try_finally_026.phpt new file mode 100644 index 00000000000..29a5de3b3a1 --- /dev/null +++ b/Zend/tests/try/try_finally_026.phpt @@ -0,0 +1,37 @@ +--TEST-- +Throw in finally inside catch inside finally +--FILE-- + +--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} diff --git a/Zend/tests/try/try_finally_027.phpt b/Zend/tests/try/try_finally_027.phpt new file mode 100644 index 00000000000..1e66479eb09 --- /dev/null +++ b/Zend/tests/try/try_finally_027.phpt @@ -0,0 +1,34 @@ +--TEST-- +Return in try with throw in finally, inside other finally +--FILE-- + +--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} diff --git a/Zend/zend_compile.c b/Zend/zend_compile.c index 2ace159911c..8cb9800a315 100644 --- a/Zend/zend_compile.c +++ b/Zend/zend_compile.c @@ -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); } diff --git a/Zend/zend_compile.h b/Zend/zend_compile.h index ef341c5000c..8378a3e1ddd 100644 --- a/Zend/zend_compile.h +++ b/Zend/zend_compile.h @@ -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 diff --git a/Zend/zend_vm_def.h b/Zend/zend_vm_def.h index acad9625c9e..d1a7ae163c1 100644 --- a/Zend/zend_vm_def.h +++ b/Zend/zend_vm_def.h @@ -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); } } } diff --git a/Zend/zend_vm_execute.h b/Zend/zend_vm_execute.h index a1146fc5852..b34724b58d6 100644 --- a/Zend/zend_vm_execute.h +++ b/Zend/zend_vm_execute.h @@ -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)); } } } diff --git a/Zend/zend_vm_opcodes.c b/Zend/zend_vm_opcodes.c index a1713ef249f..5463f9c4988 100644 --- a/Zend/zend_vm_opcodes.c +++ b/Zend/zend_vm_opcodes.c @@ -368,7 +368,7 @@ static uint32_t zend_vm_opcodes_flags[184] = { 0x00000101, 0x05000000, 0x00000000, - 0x00003000, + 0x00000000, 0x0b000303, 0x00000003, 0x00000020,