com php-src: Restored proper generators behaviour in conjunction with "finally". (Nikita): Zend/tests/generators/finally/finally_ra n_on_close.phpt
Zend/tests/generators/finally/return_ return.phpt Zend/tests/generators/finally/return_yield. phpt Zend/tests/generators/finally/throw_yield.phpt Zen
d/tests/generators/finally/yield_return.phpt Zend/tests/g enerators/finally/yield_throw.phpt Zend/tests/generators/ finally/yield_yield.phpt
Zend/tests/genera
| From: | Dmitry Stogov | Date: | Wed, 12 Dec 2012 13:47:55 +0000 |
| Subject: | com php-src: Restored proper generators behaviour in conjunction with "finally". (Nikita): Zend/tests/generators/finally/finally_ra n_on_close.phpt Zend/tests/generators/finally/return_ return.phpt Zend/tests/generators/finally/return_yield. phpt Zend/tests/generators/finally/throw_yield.phpt Zen d/tests/generators/finally/yield_return.phpt Zend/tests/g enerators/finally/yield_throw.phpt Zend/tests/generators/ finally/yield_yield.phpt Zend/tests/genera |
||
| Groups: | php.cvs | ||
| Request: | Send a blank email to php-cvs+get-70699@lists.php.net to get a copy of this message | ||
Commit: 9c96fe52d95a6674f90dc8efd4e2c054818c96ad
Author: Dmitry Stogov <dmitry@zend.com> Wed, 12 Dec 2012 17:47:55 +0400
Parents: 4d8d5d83fce83c32a04cd8d8cf50d909ab101f38
Branches: PHP-5.5 master
Link: http://git.php.net/?p=php-src.git;a=commitdiff;h=9c96fe52d95a6674f90dc8efd4e2c054818c96ad
Log:
Restored proper generators behaviour in conjunction with "finally". (Nikita)
Changed paths:
A Zend/tests/generators/finally/finally_ran_on_close.phpt
A Zend/tests/generators/finally/return_return.phpt
A Zend/tests/generators/finally/return_yield.phpt
A Zend/tests/generators/finally/throw_yield.phpt
A Zend/tests/generators/finally/yield_return.phpt
A Zend/tests/generators/finally/yield_throw.phpt
A Zend/tests/generators/finally/yield_yield.phpt
D Zend/tests/generators/finally_ran_on_close.phpt
D Zend/tests/generators/finally_uninterrupted.phpt
D Zend/tests/generators/finally_with_return.phpt
M Zend/zend_generators.c
M Zend/zend_opcode..c
M Zend/zend_vm_def.h
M Zend/zend_vm_execute.h
M Zend/zend_vm_opcodes.h
diff --git a/Zend/tests/generators/finally/finally_ran_on_close.phpt b/Zend/tests/generators/finally/finally_ran_on_close.phpt new file mode 100644 index 0000000..04a0561 --- /dev/null +++ b/Zend/tests/generators/finally/finally_ran_on_close.phpt @@ -0,0 +1,30 @@ +--TEST-- +finally is run even if a generator is closed mid-execution +--FILE-- +<?php + +function gen() { + try { + try { + echo "before yield\n"; + yield; + echo "after yield\n"; + } finally { + echo "finally run\n"; + } + echo "code after finally\n"; + } finally { + echo "second finally run\n"; + } + echo "code after second finally\n"; +} + +$gen = gen(); +$gen->rewind(); +unset($gen); + +?> +--EXPECT-- +before yield +finally run +second finally run diff --git a/Zend/tests/generators/finally/return_return.phpt b/Zend/tests/generators/finally/return_return.phpt new file mode 100644 index 0000000..02e2739 --- /dev/null +++ b/Zend/tests/generators/finally/return_return.phpt @@ -0,0 +1,33 @@ +--TEST-- +try { return } finally { return } in generator +--FILE-- +<?php + +function gen() { + try { + try { + echo "before return\n"; + return; + echo "after return\n"; + } finally { + echo "before return in inner finally\n"; + return; + echo "after return in inner finally\n"; + } + } finally { + echo "outer finally run\n"; + } + + echo "code after finally\n"; + + yield; // force generator +} + +$gen = gen(); +$gen->rewind(); // force run + +?> +--EXPECTF-- +before return +before return in inner finally +outer finally run diff --git a/Zend/tests/generators/finally/return_yield.phpt b/Zend/tests/generators/finally/return_yield.phpt new file mode 100644 index 0000000..c4f3485 --- /dev/null +++ b/Zend/tests/generators/finally/return_yield.phpt @@ -0,0 +1,18 @@ +--TEST-- +try { return } finally { yield } +--FILE-- +<?php +function foo($f, $t) { + for ($i = $f; $i <= $t; $i++) { + try { + return; + } finally { + yield $i; + } + } +} +foreach (foo(1, 5) as $x) { + echo $x, "\n"; +} +--EXPECT-- +1 diff --git a/Zend/tests/generators/finally/throw_yield.phpt b/Zend/tests/generators/finally/throw_yield.phpt new file mode 100644 index 0000000..2013c3e --- /dev/null +++ b/Zend/tests/generators/finally/throw_yield.phpt @@ -0,0 +1,24 @@ +--TEST-- +try { throw } finally { yield } +--FILE-- +<?php +function foo($f, $t) { + for ($i = $f; $i <= $t; $i++) { + try { + throw new Exception; + } finally { + yield $i; + } + } +} +foreach (foo(1, 5) as $x) { + echo $x, "\n"; +} +--EXPECTF-- +1 + +Fatal error: Uncaught exception 'Exception' in %s:%d +Stack trace: +#0 %s(%d): foo(1, 5) +#1 {main} + thrown in %s on line %d diff --git a/Zend/tests/generators/finally/yield_return.phpt b/Zend/tests/generators/finally/yield_return.phpt new file mode 100644 index 0000000..e3e1bec --- /dev/null +++ b/Zend/tests/generators/finally/yield_return.phpt @@ -0,0 +1,18 @@ +--TEST-- +try { yield } finally { return } +--FILE-- +<?php +function foo($f, $t) { + for ($i = $f; $i <= $t; $i++) { + try { + yield $i; + } finally { + return; + } + } +} +foreach (foo(1, 5) as $x) { + echo $x, "\n"; +} +--EXPECT-- +1 diff --git a/Zend/tests/generators/finally/yield_throw.phpt b/Zend/tests/generators/finally/yield_throw.phpt new file mode 100644 index 0000000..0ead450 --- /dev/null +++ b/Zend/tests/generators/finally/yield_throw.phpt @@ -0,0 +1,24 @@ +--TEST-- +try { yield } finally { throw } +--FILE-- +<?php +function foo($f, $t) { + for ($i = $f; $i <= $t; $i++) { + try { + yield $i; + } finally { + throw new Exception; + } + } +} +foreach (foo(1, 5) as $x) { + echo $x, "\n"; +} +--EXPECTF-- +1 + +Fatal error: Uncaught exception 'Exception' in %s:%d +Stack trace: +#0 %s(%d): foo(1, 5) +#1 {main} + thrown in %s on line %d diff --git a/Zend/tests/generators/finally/yield_yield.phpt b/Zend/tests/generators/finally/yield_yield.phpt new file mode 100644 index 0000000..76610ef --- /dev/null +++ b/Zend/tests/generators/finally/yield_yield.phpt @@ -0,0 +1,22 @@ +--TEST-- +Try { yield } finally { yield } +--FILE-- +<?php + +function foo() { + try { + echo "1"; + yield "2"; + echo "3"; + } finally { + echo "4"; + yield "5"; + echo "6"; + } + echo "7"; +} +foreach (foo() as $x) { + echo $x; +} +--EXPECT-- +1234567 diff --git a/Zend/tests/generators/finally_ran_on_close.phpt b/Zend/tests/generators/finally_ran_on_close.phpt deleted file mode 100644 index 04a0561..0000000 --- a/Zend/tests/generators/finally_ran_on_close.phpt +++ /dev/null @@ -1,30 +0,0 @@ ---TEST-- -finally is run even if a generator is closed mid-execution ---FILE-- -<?php - -function gen() { - try { - try { - echo "before yield\n"; - yield; - echo "after yield\n"; - } finally { - echo "finally run\n"; - } - echo "code after finally\n"; - } finally { - echo "second finally run\n"; - } - echo "code after second finally\n"; -} - -$gen = gen(); -$gen->rewind(); -unset($gen); - -?> ---EXPECT-- -before yield -finally run -second finally run diff --git a/Zend/tests/generators/finally_uninterrupted.phpt b/Zend/tests/generators/finally_uninterrupted.phpt deleted file mode 100644 index 64c9438..0000000 --- a/Zend/tests/generators/finally_uninterrupted.phpt +++ /dev/null @@ -1,28 +0,0 @@ ---TEST-- -Use of finally in generator without interrupt ---FILE-- -<?php - -function gen() { - try { - throw new Exception; - } finally { - echo "finally run\n"; - } - - yield; // force generator -} - -$gen = gen(); -$gen->rewind(); // force run - -?> ---EXPECTF-- -finally run - -Fatal error: Uncaught exception 'Exception' in %s:%d -Stack trace: -#0 [internal function]: gen() -#1 %s(%d): Generator->rewind() -#2 {main} - thrown in %s on line %d diff --git a/Zend/tests/generators/finally_with_return.phpt b/Zend/tests/generators/finally_with_return.phpt deleted file mode 100644 index b26a49f..0000000 --- a/Zend/tests/generators/finally_with_return.phpt +++ /dev/null @@ -1,33 +0,0 @@ ---TEST-- -Use of finally in generator with return ---FILE-- -<?php - -function gen() { - try { - try { - echo "before return\n"; - return; - echo "after return\n"; - } finally { - echo "before return in inner finally\n"; - return; - echo "after return in inner finally\n"; - } - } finally { - echo "outer finally run\n"; - } - - echo "code after finally\n"; - - yield; // force generator -} - -$gen = gen(); -$gen->rewind(); // force run - -?> ---EXPECTF-- -before return -before return in inner finally -outer finally run diff --git a/Zend/zend_generators.c b/Zend/zend_generators.c index 1844be8..2ee2fb9 100644 --- a/Zend/zend_generators.c +++ b/Zend/zend_generators.c @@ -32,7 +32,41 @@ ZEND_API void zend_generator_close(zend_generator *generator, zend_bool finished if (generator->execute_data) { zend_execute_data *execute_data = generator->execute_data; zend_op_array *op_array = execute_data->op_array; - void **stack_frame; + + if (!finished_execution) { + if (op_array->has_finally_block) { + /* -1 required because we want the last run opcode, not the + * next to-be-run one. */ + zend_uint op_num = execute_data->opline - op_array->opcodes - 1; + zend_uint finally_op_num = 0; + + /* Find next finally block */ + int i; + for (i = 0; i < op_array->last_try_catch; i++) { + zend_try_catch_element *try_catch = &op_array->try_catch_array[i]; + + if (op_num < try_catch->try_op) { + break; + } + + if (op_num < try_catch->finally_op) { + finally_op_num = try_catch->finally_op; + } + } + + /* If a finally block was found we jump directly to it and + * resume the generator. Furthermore we abort this close call + * because the generator will already be closed somewhere in + * the resume. */ + if (finally_op_num) { + execute_data->opline = &op_array->opcodes[finally_op_num]; + execute_data->fast_ret = NULL; + generator->flags |= ZEND_GENERATOR_FORCED_CLOSE; + zend_generator_resume(generator TSRMLS_CC); + return; + } + } + } if (!execute_data->symbol_table) { zend_free_compiled_variables(execute_data); @@ -83,7 +117,7 @@ ZEND_API void zend_generator_close(zend_generator *generator, zend_bool finished /* Clear any backed up stack arguments */ if (generator->stack != EG(argument_stack)) { - stack_frame = zend_vm_stack_frame_base(execute_data); + void **stack_frame = zend_vm_stack_frame_base(execute_data); while (generator->stack->top != stack_frame) { zval_ptr_dtor((zval**)stack_frame); stack_frame++; diff --git a/Zend/zend_opcode.c b/Zend/zend_opcode.c index 2fd8f52..3fb9453 100644 --- a/Zend/zend_opcode.c +++ b/Zend/zend_opcode.c @@ -542,17 +542,9 @@ static void zend_resolve_finally_call(zend_op_array *op_array, zend_uint op_num, dst_num > op_array->try_catch_array[i].finally_end)) { /* we have a jump out of try block that needs executing finally */ - /* generate a FAST_CALL to finaly block */ + /* generate a FAST_CALL to finally block */ start_op = get_next_op_number(op_array); - if (op_array->opcodes[op_num].opcode == ZEND_YIELD) { - /* Disable yield in finally block */ - opline = get_next_op(op_array TSRMLS_CC); - opline->opcode = ZEND_GENERATOR_FLAG; - opline->extended_value = 1; - SET_UNUSED(opline->op1); - SET_UNUSED(opline->op2); - } opline = get_next_op(op_array TSRMLS_CC); opline->opcode = ZEND_FAST_CALL; SET_UNUSED(opline->op1); @@ -563,7 +555,7 @@ static void zend_resolve_finally_call(zend_op_array *op_array, zend_uint op_num, opline->op2.opline_num = op_array->try_catch_array[i].catch_op; } - /* generate a sequence of FAST_CALL to upward finaly block */ + /* generate a sequence of FAST_CALL to upward finally block */ while (i > 0) { i--; if (op_array->try_catch_array[i].finally_op && @@ -579,14 +571,6 @@ static void zend_resolve_finally_call(zend_op_array *op_array, zend_uint op_num, opline->op1.opline_num = op_array->try_catch_array[i].finally_op; } } - if (op_array->opcodes[op_num].opcode == ZEND_YIELD) { - /* Re-enable yield */ - opline = get_next_op(op_array TSRMLS_CC); - opline->opcode = ZEND_GENERATOR_FLAG; - opline->extended_value = 0; - SET_UNUSED(opline->op1); - SET_UNUSED(opline->op2); - } /* Finish the sequence with original opcode */ opline = get_next_op(op_array TSRMLS_CC); @@ -642,7 +626,7 @@ static void zend_resolve_finally_calls(zend_op_array *op_array TSRMLS_DC) switch (opline->opcode) { case ZEND_RETURN: case ZEND_RETURN_BY_REF: - case ZEND_YIELD: + case ZEND_GENERATOR_RETURN: zend_resolve_finally_call(op_array, i, (zend_uint)-1 TSRMLS_CC); break; case ZEND_BRK: diff --git a/Zend/zend_vm_def.h b/Zend/zend_vm_def.h index c5d1748..939dc1d 100644 --- a/Zend/zend_vm_def.h +++ b/Zend/zend_vm_def.h @@ -5252,19 +5252,6 @@ ZEND_VM_HANDLER(156, ZEND_SEPARATE, VAR, UNUSED) ZEND_VM_NEXT_OPCODE(); } -ZEND_VM_HANDLER(159, ZEND_GENERATOR_FLAG, ANY, ANY) -{ - USE_OPLINE - zend_generator *generator = (zend_generator *) EG(return_value_ptr_ptr); - - if (opline->extended_value) { - generator->flags |= ZEND_GENERATOR_FORCED_CLOSE; - } else { - generator->flags &= ~ZEND_GENERATOR_FORCED_CLOSE; - } - ZEND_VM_NEXT_OPCODE(); -} - ZEND_VM_HANDLER(160, ZEND_YIELD, CONST|TMP|VAR|CV|UNUSED, CONST|TMP|VAR|CV|UNUSED) { USE_OPLINE diff --git a/Zend/zend_vm_execute.h b/Zend/zend_vm_execute.h index 7c11016..dd77aa5 100644 --- a/Zend/zend_vm_execute.h +++ b/Zend/zend_vm_execute.h @@ -1134,19 +1134,6 @@ static int ZEND_FASTCALL ZEND_USER_OPCODE_SPEC_HANDLER(ZEND_OPCODE_HANDLER_ARGS } } -static int ZEND_FASTCALL ZEND_GENERATOR_FLAG_SPEC_HANDLER(ZEND_OPCODE_HANDLER_ARGS) -{ - USE_OPLINE - zend_generator *generator = (zend_generator *) EG(return_value_ptr_ptr); - - if (opline->extended_value) { - generator->flags |= ZEND_GENERATOR_FORCED_CLOSE; - } else { - generator->flags &= ~ZEND_GENERATOR_FORCED_CLOSE; - } - ZEND_VM_NEXT_OPCODE(); -} - static int ZEND_FASTCALL ZEND_FAST_CALL_SPEC_HANDLER(ZEND_OPCODE_HANDLER_ARGS) { USE_OPLINE @@ -44840,31 +44827,31 @@ void zend_init_opcodes_handlers(void) ZEND_JMP_SET_VAR_SPEC_CV_HANDLER, ZEND_JMP_SET_VAR_SPEC_CV_HANDLER, ZEND_JMP_SET_VAR_SPEC_CV_HANDLER, - ZEND_GENERATOR_FLAG_SPEC_HANDLER, - ZEND_GENERATOR_FLAG_SPEC_HANDLER, - ZEND_GENERATOR_FLAG_SPEC_HANDLER, - ZEND_GENERATOR_FLAG_SPEC_HANDLER, - ZEND_GENERATOR_FLAG_SPEC_HANDLER, - ZEND_GENERATOR_FLAG_SPEC_HANDLER, - ZEND_GENERATOR_FLAG_SPEC_HANDLER, - ZEND_GENERATOR_FLAG_SPEC_HANDLER, - ZEND_GENERATOR_FLAG_SPEC_HANDLER, - ZEND_GENERATOR_FLAG_SPEC_HANDLER, - ZEND_GENERATOR_FLAG_SPEC_HANDLER, - ZEND_GENERATOR_FLAG_SPEC_HANDLER, - ZEND_GENERATOR_FLAG_SPEC_HANDLER, - ZEND_GENERATOR_FLAG_SPEC_HANDLER, - ZEND_GENERATOR_FLAG_SPEC_HANDLER, - ZEND_GENERATOR_FLAG_SPEC_HANDLER, - ZEND_GENERATOR_FLAG_SPEC_HANDLER, - ZEND_GENERATOR_FLAG_SPEC_HANDLER, - ZEND_GENERATOR_FLAG_SPEC_HANDLER, - ZEND_GENERATOR_FLAG_SPEC_HANDLER, - ZEND_GENERATOR_FLAG_SPEC_HANDLER, - ZEND_GENERATOR_FLAG_SPEC_HANDLER, - ZEND_GENERATOR_FLAG_SPEC_HANDLER, - ZEND_GENERATOR_FLAG_SPEC_HANDLER, - ZEND_GENERATOR_FLAG_SPEC_HANDLER, + ZEND_NULL_HANDLER, + ZEND_NULL_HANDLER, + ZEND_NULL_HANDLER, + ZEND_NULL_HANDLER, + ZEND_NULL_HANDLER, + ZEND_NULL_HANDLER, + ZEND_NULL_HANDLER, + ZEND_NULL_HANDLER, + ZEND_NULL_HANDLER, + ZEND_NULL_HANDLER, + ZEND_NULL_HANDLER, + ZEND_NULL_HANDLER, + ZEND_NULL_HANDLER, + ZEND_NULL_HANDLER, + ZEND_NULL_HANDLER, + ZEND_NULL_HANDLER, + ZEND_NULL_HANDLER, + ZEND_NULL_HANDLER, + ZEND_NULL_HANDLER, + ZEND_NULL_HANDLER, + ZEND_NULL_HANDLER, + ZEND_NULL_HANDLER, + ZEND_NULL_HANDLER, + ZEND_NULL_HANDLER, + ZEND_NULL_HANDLER, ZEND_YIELD_SPEC_CONST_CONST_HANDLER, ZEND_YIELD_SPEC_CONST_TMP_HANDLER, ZEND_YIELD_SPEC_CONST_VAR_HANDLER, diff --git a/Zend/zend_vm_opcodes.h b/Zend/zend_vm_opcodes.h index fcf7960..52f6cde 100644 --- a/Zend/zend_vm_opcodes.h +++ b/Zend/zend_vm_opcodes.h @@ -159,7 +159,6 @@ #define ZEND_SEPARATE 156 #define ZEND_QM_ASSIGN_VAR 157 #define ZEND_JMP_SET_VAR 158 -#define ZEND_GENERATOR_FLAG 159 #define ZEND_YIELD 160 #define ZEND_GENERATOR_RETURN 161 #define ZEND_FAST_CALL 162
diff --git a/Zend/tests/generators/finally/finally_ran_on_close.phpt b/Zend/tests/generators/finally/finally_ran_on_close.phpt new file mode 100644 index 0000000..04a0561 --- /dev/null +++ b/Zend/tests/generators/finally/finally_ran_on_close.phpt @@ -0,0 +1,30 @@ +--TEST-- +finally is run even if a generator is closed mid-execution +--FILE-- +<?php + +function gen() { + try { + try { + echo "before yield\n"; + yield; + echo "after yield\n"; + } finally { + echo "finally run\n"; + } + echo "code after finally\n"; + } finally { + echo "second finally run\n"; + } + echo "code after second finally\n"; +} + +$gen = gen(); +$gen->rewind(); +unset($gen); + +?> +--EXPECT-- +before yield +finally run +second finally run diff --git a/Zend/tests/generators/finally/return_return.phpt b/Zend/tests/generators/finally/return_return.phpt new file mode 100644 index 0000000..02e2739 --- /dev/null +++ b/Zend/tests/generators/finally/return_return.phpt @@ -0,0 +1,33 @@ +--TEST-- +try { return } finally { return } in generator +--FILE-- +<?php + +function gen() { + try { + try { + echo "before return\n"; + return; + echo "after return\n"; + } finally { + echo "before return in inner finally\n"; + return; + echo "after return in inner finally\n"; + } + } finally { + echo "outer finally run\n"; + } + + echo "code after finally\n"; + + yield; // force generator +} + +$gen = gen(); +$gen->rewind(); // force run + +?> +--EXPECTF-- +before return +before return in inner finally +outer finally run diff --git a/Zend/tests/generators/finally/return_yield.phpt b/Zend/tests/generators/finally/return_yield.phpt new file mode 100644 index 0000000..c4f3485 --- /dev/null +++ b/Zend/tests/generators/finally/return_yield.phpt @@ -0,0 +1,18 @@ +--TEST-- +try { return } finally { yield } +--FILE-- +<?php +function foo($f, $t) { + for ($i = $f; $i <= $t; $i++) { + try { + return; + } finally { + yield $i; + } + } +} +foreach (foo(1, 5) as $x) { + echo $x, "\n"; +} +--EXPECT-- +1 diff --git a/Zend/tests/generators/finally/throw_yield.phpt b/Zend/tests/generators/finally/throw_yield.phpt new file mode 100644 index 0000000..2013c3e --- /dev/null +++ b/Zend/tests/generators/finally/throw_yield.phpt @@ -0,0 +1,24 @@ +--TEST-- +try { throw } finally { yield } +--FILE-- +<?php +function foo($f, $t) { + for ($i = $f; $i <= $t; $i++) { + try { + throw new Exception; + } finally { + yield $i; + } + } +} +foreach (foo(1, 5) as $x) { + echo $x, "\n"; +} +--EXPECTF-- +1 + +Fatal error: Uncaught exception 'Exception' in %s:%d +Stack trace: +#0 %s(%d): foo(1, 5) +#1 {main} + thrown in %s on line %d diff --git a/Zend/tests/generators/finally/yield_return.phpt b/Zend/tests/generators/finally/yield_return.phpt new file mode 100644 index 0000000..e3e1bec --- /dev/null +++ b/Zend/tests/generators/finally/yield_return.phpt @@ -0,0 +1,18 @@ +--TEST-- +try { yield } finally { return } +--FILE-- +<?php +function foo($f, $t) { + for ($i = $f; $i <= $t; $i++) { + try { + yield $i; + } finally { + return; + } + } +} +foreach (foo(1, 5) as $x) { + echo $x, "\n"; +} +--EXPECT-- +1 diff --git a/Zend/tests/generators/finally/yield_throw.phpt b/Zend/tests/generators/finally/yield_throw.phpt new file mode 100644 index 0000000..0ead450 --- /dev/null +++ b/Zend/tests/generators/finally/yield_throw.phpt @@ -0,0 +1,24 @@ +--TEST-- +try { yield } finally { throw } +--FILE-- +<?php +function foo($f, $t) { + for ($i = $f; $i <= $t; $i++) { + try { + yield $i; + } finally { + throw new Exception; + } + } +} +foreach (foo(1, 5) as $x) { + echo $x, "\n"; +} +--EXPECTF-- +1 + +Fatal error: Uncaught exception 'Exception' in %s:%d +Stack trace: +#0 %s(%d): foo(1, 5) +#1 {main} + thrown in %s on line %d diff --git a/Zend/tests/generators/finally/yield_yield.phpt b/Zend/tests/generators/finally/yield_yield.phpt new file mode 100644 index 0000000..76610ef --- /dev/null +++ b/Zend/tests/generators/finally/yield_yield.phpt @@ -0,0 +1,22 @@ +--TEST-- +Try { yield } finally { yield } +--FILE-- +<?php + +function foo() { + try { + echo "1"; + yield "2"; + echo "3"; + } finally { + echo "4"; + yield "5"; + echo "6"; + } + echo "7"; +} +foreach (foo() as $x) { + echo $x; +} +--EXPECT-- +1234567 diff --git a/Zend/tests/generators/finally_ran_on_close.phpt b/Zend/tests/generators/finally_ran_on_close.phpt deleted file mode 100644 index 04a0561..0000000 --- a/Zend/tests/generators/finally_ran_on_close.phpt +++ /dev/null @@ -1,30 +0,0 @@ ---TEST-- -finally is run even if a generator is closed mid-execution ---FILE-- -<?php - -function gen() { - try { - try { - echo "before yield\n"; - yield; - echo "after yield\n"; - } finally { - echo "finally run\n"; - } - echo "code after finally\n"; - } finally { - echo "second finally run\n"; - } - echo "code after second finally\n"; -} - -$gen = gen(); -$gen->rewind(); -unset($gen); - -?> ---EXPECT-- -before yield -finally run -second finally run diff --git a/Zend/tests/generators/finally_uninterrupted.phpt b/Zend/tests/generators/finally_uninterrupted.phpt deleted file mode 100644 index 64c9438..0000000 --- a/Zend/tests/generators/finally_uninterrupted.phpt +++ /dev/null @@ -1,28 +0,0 @@ ---TEST-- -Use of finally in generator without interrupt ---FILE-- -<?php - -function gen() { - try { - throw new Exception; - } finally { - echo "finally run\n"; - } - - yield; // force generator -} - -$gen = gen(); -$gen->rewind(); // force run - -?> ---EXPECTF-- -finally run - -Fatal error: Uncaught exception 'Exception' in %s:%d -Stack trace: -#0 [internal function]: gen() -#1 %s(%d): Generator->rewind() -#2 {main} - thrown in %s on line %d diff --git a/Zend/tests/generators/finally_with_return.phpt b/Zend/tests/generators/finally_with_return.phpt deleted file mode 100644 index b26a49f..0000000 --- a/Zend/tests/generators/finally_with_return.phpt +++ /dev/null @@ -1,33 +0,0 @@ ---TEST-- -Use of finally in generator with return ---FILE-- -<?php - -function gen() { - try { - try { - echo "before return\n"; - return; - echo "after return\n"; - } finally { - echo "before return in inner finally\n"; - return; - echo "after return in inner finally\n"; - } - } finally { - echo "outer finally run\n"; - } - - echo "code after finally\n"; - - yield; // force generator -} - -$gen = gen(); -$gen->rewind(); // force run - -?> ---EXPECTF-- -before return -before return in inner finally -outer finally run diff --git a/Zend/zend_generators.c b/Zend/zend_generators.c index 1844be8..2ee2fb9 100644 --- a/Zend/zend_generators.c +++ b/Zend/zend_generators.c @@ -32,7 +32,41 @@ ZEND_API void zend_generator_close(zend_generator *generator, zend_bool finished if (generator->execute_data) { zend_execute_data *execute_data = generator->execute_data; zend_op_array *op_array = execute_data->op_array; - void **stack_frame; + + if (!finished_execution) { + if (op_array->has_finally_block) { + /* -1 required because we want the last run opcode, not the + * next to-be-run one. */ + zend_uint op_num = execute_data->opline - op_array->opcodes - 1; + zend_uint finally_op_num = 0; + + /* Find next finally block */ + int i; + for (i = 0; i < op_array->last_try_catch; i++) { + zend_try_catch_element *try_catch = &op_array->try_catch_array[i]; + + if (op_num < try_catch->try_op) { + break; + } + + if (op_num < try_catch->finally_op) { + finally_op_num = try_catch->finally_op; + } + } + + /* If a finally block was found we jump directly to it and + * resume the generator. Furthermore we abort this close call + * because the generator will already be closed somewhere in + * the resume. */ + if (finally_op_num) { + execute_data->opline = &op_array->opcodes[finally_op_num]; + execute_data->fast_ret = NULL; + generator->flags |= ZEND_GENERATOR_FORCED_CLOSE; + zend_generator_resume(generator TSRMLS_CC); + return; + } + } + } if (!execute_data->symbol_table) { zend_free_compiled_variables(execute_data); @@ -83,7 +117,7 @@ ZEND_API void zend_generator_close(zend_generator *generator, zend_bool finished /* Clear any backed up stack arguments */ if (generator->stack != EG(argument_stack)) { - stack_frame = zend_vm_stack_frame_base(execute_data); + void **stack_frame = zend_vm_stack_frame_base(execute_data); while (generator->stack->top != stack_frame) { zval_ptr_dtor((zval**)stack_frame); stack_frame++; diff --git a/Zend/zend_opcode.c b/Zend/zend_opcode.c index 2fd8f52..3fb9453 100644 --- a/Zend/zend_opcode.c +++ b/Zend/zend_opcode.c @@ -542,17 +542,9 @@ static void zend_resolve_finally_call(zend_op_array *op_array, zend_uint op_num, dst_num > op_array->try_catch_array[i].finally_end)) { /* we have a jump out of try block that needs executing finally */ - /* generate a FAST_CALL to finaly block */ + /* generate a FAST_CALL to finally block */ start_op = get_next_op_number(op_array); - if (op_array->opcodes[op_num].opcode == ZEND_YIELD) { - /* Disable yield in finally block */ - opline = get_next_op(op_array TSRMLS_CC); - opline->opcode = ZEND_GENERATOR_FLAG; - opline->extended_value = 1; - SET_UNUSED(opline->op1); - SET_UNUSED(opline->op2); - } opline = get_next_op(op_array TSRMLS_CC); opline->opcode = ZEND_FAST_CALL; SET_UNUSED(opline->op1); @@ -563,7 +555,7 @@ static void zend_resolve_finally_call(zend_op_array *op_array, zend_uint op_num, opline->op2.opline_num = op_array->try_catch_array[i].catch_op; } - /* generate a sequence of FAST_CALL to upward finaly block */ + /* generate a sequence of FAST_CALL to upward finally block */ while (i > 0) { i--; if (op_array->try_catch_array[i].finally_op && @@ -579,14 +571,6 @@ static void zend_resolve_finally_call(zend_op_array *op_array, zend_uint op_num, opline->op1.opline_num = op_array->try_catch_array[i].finally_op; } } - if (op_array->opcodes[op_num].opcode == ZEND_YIELD) { - /* Re-enable yield */ - opline = get_next_op(op_array TSRMLS_CC); - opline->opcode = ZEND_GENERATOR_FLAG; - opline->extended_value = 0; - SET_UNUSED(opline->op1); - SET_UNUSED(opline->op2); - } /* Finish the sequence with original opcode */ opline = get_next_op(op_array TSRMLS_CC); @@ -642,7 +626,7 @@ static void zend_resolve_finally_calls(zend_op_array *op_array TSRMLS_DC) switch (opline->opcode) { case ZEND_RETURN: case ZEND_RETURN_BY_REF: - case ZEND_YIELD: + case ZEND_GENERATOR_RETURN: zend_resolve_finally_call(op_array, i, (zend_uint)-1 TSRMLS_CC); break; case ZEND_BRK: diff --git a/Zend/zend_vm_def.h b/Zend/zend_vm_def.h index c5d1748..939dc1d 100644 --- a/Zend/zend_vm_def.h +++ b/Zend/zend_vm_def.h @@ -5252,19 +5252,6 @@ ZEND_VM_HANDLER(156, ZEND_SEPARATE, VAR, UNUSED) ZEND_VM_NEXT_OPCODE(); } -ZEND_VM_HANDLER(159, ZEND_GENERATOR_FLAG, ANY, ANY) -{ - USE_OPLINE - zend_generator *generator = (zend_generator *) EG(return_value_ptr_ptr); - - if (opline->extended_value) { - generator->flags |= ZEND_GENERATOR_FORCED_CLOSE; - } else { - generator->flags &= ~ZEND_GENERATOR_FORCED_CLOSE; - } - ZEND_VM_NEXT_OPCODE(); -} - ZEND_VM_HANDLER(160, ZEND_YIELD, CONST|TMP|VAR|CV|UNUSED, CONST|TMP|VAR|CV|UNUSED) { USE_OPLINE diff --git a/Zend/zend_vm_execute.h b/Zend/zend_vm_execute.h index 7c11016..dd77aa5 100644 --- a/Zend/zend_vm_execute.h +++ b/Zend/zend_vm_execute.h @@ -1134,19 +1134,6 @@ static int ZEND_FASTCALL ZEND_USER_OPCODE_SPEC_HANDLER(ZEND_OPCODE_HANDLER_ARGS } } -static int ZEND_FASTCALL ZEND_GENERATOR_FLAG_SPEC_HANDLER(ZEND_OPCODE_HANDLER_ARGS) -{ - USE_OPLINE - zend_generator *generator = (zend_generator *) EG(return_value_ptr_ptr); - - if (opline->extended_value) { - generator->flags |= ZEND_GENERATOR_FORCED_CLOSE; - } else { - generator->flags &= ~ZEND_GENERATOR_FORCED_CLOSE; - } - ZEND_VM_NEXT_OPCODE(); -} - static int ZEND_FASTCALL ZEND_FAST_CALL_SPEC_HANDLER(ZEND_OPCODE_HANDLER_ARGS) { USE_OPLINE @@ -44840,31 +44827,31 @@ void zend_init_opcodes_handlers(void) ZEND_JMP_SET_VAR_SPEC_CV_HANDLER, ZEND_JMP_SET_VAR_SPEC_CV_HANDLER, ZEND_JMP_SET_VAR_SPEC_CV_HANDLER, - ZEND_GENERATOR_FLAG_SPEC_HANDLER, - ZEND_GENERATOR_FLAG_SPEC_HANDLER, - ZEND_GENERATOR_FLAG_SPEC_HANDLER, - ZEND_GENERATOR_FLAG_SPEC_HANDLER, - ZEND_GENERATOR_FLAG_SPEC_HANDLER, - ZEND_GENERATOR_FLAG_SPEC_HANDLER, - ZEND_GENERATOR_FLAG_SPEC_HANDLER, - ZEND_GENERATOR_FLAG_SPEC_HANDLER, - ZEND_GENERATOR_FLAG_SPEC_HANDLER, - ZEND_GENERATOR_FLAG_SPEC_HANDLER, - ZEND_GENERATOR_FLAG_SPEC_HANDLER, - ZEND_GENERATOR_FLAG_SPEC_HANDLER, - ZEND_GENERATOR_FLAG_SPEC_HANDLER, - ZEND_GENERATOR_FLAG_SPEC_HANDLER, - ZEND_GENERATOR_FLAG_SPEC_HANDLER, - ZEND_GENERATOR_FLAG_SPEC_HANDLER, - ZEND_GENERATOR_FLAG_SPEC_HANDLER, - ZEND_GENERATOR_FLAG_SPEC_HANDLER, - ZEND_GENERATOR_FLAG_SPEC_HANDLER, - ZEND_GENERATOR_FLAG_SPEC_HANDLER, - ZEND_GENERATOR_FLAG_SPEC_HANDLER, - ZEND_GENERATOR_FLAG_SPEC_HANDLER, - ZEND_GENERATOR_FLAG_SPEC_HANDLER, - ZEND_GENERATOR_FLAG_SPEC_HANDLER, - ZEND_GENERATOR_FLAG_SPEC_HANDLER, + ZEND_NULL_HANDLER, + ZEND_NULL_HANDLER, + ZEND_NULL_HANDLER, + ZEND_NULL_HANDLER, + ZEND_NULL_HANDLER, + ZEND_NULL_HANDLER, + ZEND_NULL_HANDLER, + ZEND_NULL_HANDLER, + ZEND_NULL_HANDLER, + ZEND_NULL_HANDLER, + ZEND_NULL_HANDLER, + ZEND_NULL_HANDLER, + ZEND_NULL_HANDLER, + ZEND_NULL_HANDLER, + ZEND_NULL_HANDLER, + ZEND_NULL_HANDLER, + ZEND_NULL_HANDLER, + ZEND_NULL_HANDLER, + ZEND_NULL_HANDLER, + ZEND_NULL_HANDLER, + ZEND_NULL_HANDLER, + ZEND_NULL_HANDLER, + ZEND_NULL_HANDLER, + ZEND_NULL_HANDLER, + ZEND_NULL_HANDLER, ZEND_YIELD_SPEC_CONST_CONST_HANDLER, ZEND_YIELD_SPEC_CONST_TMP_HANDLER, ZEND_YIELD_SPEC_CONST_VAR_HANDLER, diff --git a/Zend/zend_vm_opcodes.h b/Zend/zend_vm_opcodes.h index fcf7960..52f6cde 100644 --- a/Zend/zend_vm_opcodes.h +++ b/Zend/zend_vm_opcodes.h @@ -159,7 +159,6 @@ #define ZEND_SEPARATE 156 #define ZEND_QM_ASSIGN_VAR 157 #define ZEND_JMP_SET_VAR 158 -#define ZEND_GENERATOR_FLAG 159 #define ZEND_YIELD 160 #define ZEND_GENERATOR_RETURN 161 #define ZEND_FAST_CALL 162