diff options
Diffstat (limited to 'Zend')
-rw-r--r-- | Zend/tests/bug65784.phpt | 11 | ||||
-rw-r--r-- | Zend/tests/return_types/029.phpt | 7 | ||||
-rw-r--r-- | Zend/tests/try/bug70228_2.phpt | 20 | ||||
-rw-r--r-- | Zend/tests/try/bug70228_3.phpt | 31 | ||||
-rw-r--r-- | Zend/tests/try/bug70228_4.phpt | 32 | ||||
-rw-r--r-- | Zend/tests/try/bug72213.phpt | 2 | ||||
-rw-r--r-- | Zend/tests/try/try_finally_023.phpt | 37 | ||||
-rw-r--r-- | Zend/tests/try/try_finally_024.phpt | 38 | ||||
-rw-r--r-- | Zend/tests/try/try_finally_025.phpt | 28 | ||||
-rw-r--r-- | Zend/tests/try/try_finally_026.phpt | 37 | ||||
-rw-r--r-- | Zend/tests/try/try_finally_027.phpt | 34 | ||||
-rw-r--r-- | Zend/zend_compile.c | 29 | ||||
-rw-r--r-- | Zend/zend_compile.h | 6 | ||||
-rw-r--r-- | Zend/zend_opcode.c | 55 | ||||
-rw-r--r-- | Zend/zend_vm_def.h | 171 | ||||
-rw-r--r-- | Zend/zend_vm_execute.h | 167 | ||||
-rw-r--r-- | Zend/zend_vm_gen.php | 6 | ||||
-rw-r--r-- | Zend/zend_vm_opcodes.c | 4 | ||||
-rw-r--r-- | Zend/zend_vm_opcodes.h | 2 |
19 files changed, 445 insertions, 272 deletions
diff --git a/Zend/tests/bug65784.phpt b/Zend/tests/bug65784.phpt index c079b3d282..397c52aef9 100644 --- a/Zend/tests/bug65784.phpt +++ b/Zend/tests/bug65784.phpt @@ -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 diff --git a/Zend/tests/return_types/029.phpt b/Zend/tests/return_types/029.phpt index 011182df37..041b354130 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/bug70228_2.phpt b/Zend/tests/try/bug70228_2.phpt new file mode 100644 index 0000000000..c988e706ce --- /dev/null +++ b/Zend/tests/try/bug70228_2.phpt @@ -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) diff --git a/Zend/tests/try/bug70228_3.phpt b/Zend/tests/try/bug70228_3.phpt new file mode 100644 index 0000000000..55dbe4f891 --- /dev/null +++ b/Zend/tests/try/bug70228_3.phpt @@ -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 diff --git a/Zend/tests/try/bug70228_4.phpt b/Zend/tests/try/bug70228_4.phpt new file mode 100644 index 0000000000..f0ab3b0c2c --- /dev/null +++ b/Zend/tests/try/bug70228_4.phpt @@ -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 diff --git a/Zend/tests/try/bug72213.phpt b/Zend/tests/try/bug72213.phpt index aee4c95b20..624050295e 100644 --- a/Zend/tests/try/bug72213.phpt +++ b/Zend/tests/try/bug72213.phpt @@ -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() { diff --git a/Zend/tests/try/try_finally_023.phpt b/Zend/tests/try/try_finally_023.phpt new file mode 100644 index 0000000000..e88eddb3b2 --- /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-- +<?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} diff --git a/Zend/tests/try/try_finally_024.phpt b/Zend/tests/try/try_finally_024.phpt new file mode 100644 index 0000000000..eb0b26acbf --- /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-- +<?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} diff --git a/Zend/tests/try/try_finally_025.phpt b/Zend/tests/try/try_finally_025.phpt new file mode 100644 index 0000000000..7ca535bcf6 --- /dev/null +++ b/Zend/tests/try/try_finally_025.phpt @@ -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} diff --git a/Zend/tests/try/try_finally_026.phpt b/Zend/tests/try/try_finally_026.phpt new file mode 100644 index 0000000000..29a5de3b3a --- /dev/null +++ b/Zend/tests/try/try_finally_026.phpt @@ -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} diff --git a/Zend/tests/try/try_finally_027.phpt b/Zend/tests/try/try_finally_027.phpt new file mode 100644 index 0000000000..1e66479eb0 --- /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-- +<?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} diff --git a/Zend/zend_compile.c b/Zend/zend_compile.c index 0994681c20..c6da34732a 100644 --- a/Zend/zend_compile.c +++ b/Zend/zend_compile.c @@ -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); } /* }}} */ diff --git a/Zend/zend_compile.h b/Zend/zend_compile.h index 72215a20d1..5ab0d0375b 100644 --- a/Zend/zend_compile.h +++ b/Zend/zend_compile.h @@ -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 diff --git a/Zend/zend_opcode.c b/Zend/zend_opcode.c index 34e060fa4a..0b5fe75ff8 100644 --- a/Zend/zend_opcode.c +++ b/Zend/zend_opcode.c @@ -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: { diff --git a/Zend/zend_vm_def.h b/Zend/zend_vm_def.h index 4ac2057d27..7c584f38a1 100644 --- a/Zend/zend_vm_def.h +++ b/Zend/zend_vm_def.h @@ -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); - - 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); + cleanup_unfinished_calls(execute_data, throw_op_num); - 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) diff --git a/Zend/zend_vm_execute.h b/Zend/zend_vm_execute.h index 9f6130c059..878d280c17 100644 --- a/Zend/zend_vm_execute.h +++ b/Zend/zend_vm_execute.h @@ -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); - - 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); + cleanup_unfinished_calls(execute_data, throw_op_num); - 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) diff --git a/Zend/zend_vm_gen.php b/Zend/zend_vm_gen.php index 88fe287b55..03d9817c6b 100644 --- a/Zend/zend_vm_gen.php +++ b/Zend/zend_vm_gen.php @@ -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, diff --git a/Zend/zend_vm_opcodes.c b/Zend/zend_vm_opcodes.c index 180b745fb2..5463f9c498 100644 --- a/Zend/zend_vm_opcodes.c +++ b/Zend/zend_vm_opcodes.c @@ -371,8 +371,8 @@ static uint32_t zend_vm_opcodes_flags[184] = { 0x00000000, 0x0b000303, 0x00000003, - 0x09000020, - 0x0a003000, + 0x00000020, + 0x00003000, 0x00000010, 0x00000000, 0x00000707, diff --git a/Zend/zend_vm_opcodes.h b/Zend/zend_vm_opcodes.h index 00aa20ea37..4f5a0d615a 100644 --- a/Zend/zend_vm_opcodes.h +++ b/Zend/zend_vm_opcodes.h @@ -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 |