Created
December 12, 2012 12:58
-
-
Save nikic/4267515 to your computer and use it in GitHub Desktop.
Patch to restore old behavior of finally in generators
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| 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..a4bccc7 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,6 @@ 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: | |
| 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 |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment