diff options
author | Nikita Popov <nikic@php.net> | 2012-08-25 17:40:08 +0200 |
---|---|---|
committer | Nikita Popov <nikic@php.net> | 2012-08-25 17:40:08 +0200 |
commit | f53225a99ebae56c7a20d6e3ad4efe6772dda3f9 (patch) | |
tree | 8b77248bca9fb041abb14947a77fb4eada624783 | |
parent | 4d8edda341efef1901365f10213c027e745ac7ab (diff) | |
download | php-git-f53225a99ebae56c7a20d6e3ad4efe6772dda3f9.tar.gz |
Fix several issues and allow rewind only at/before first yield
* Trying to resume a generator while it is already running now throws a
fatal error.
* Trying to use yield in finally while the generator is being force-closed
(by GC) throws a fatal error.
* Rewinding after the first yield now throws an Exception
-rw-r--r-- | Zend/tests/generators/errors/resume_running_generator_error.phpt | 17 | ||||
-rw-r--r-- | Zend/tests/generators/errors/yield_in_force_closed_finally_error.phpt | 29 | ||||
-rw-r--r-- | Zend/tests/generators/generator_rewind.phpt | 43 | ||||
-rw-r--r-- | Zend/tests/generators/yield_in_finally.phpt | 29 | ||||
-rw-r--r-- | Zend/zend_generators.c | 49 | ||||
-rw-r--r-- | Zend/zend_generators.h | 11 | ||||
-rw-r--r-- | Zend/zend_vm_def.h | 4 | ||||
-rw-r--r-- | Zend/zend_vm_execute.h | 100 |
8 files changed, 269 insertions, 13 deletions
diff --git a/Zend/tests/generators/errors/resume_running_generator_error.phpt b/Zend/tests/generators/errors/resume_running_generator_error.phpt new file mode 100644 index 0000000000..567d72f3f9 --- /dev/null +++ b/Zend/tests/generators/errors/resume_running_generator_error.phpt @@ -0,0 +1,17 @@ +--TEST-- +It is not possible to resume an already running generator +--FILE-- +<?php + +function gen() { + $gen = yield; + $gen->next(); +} + +$gen = gen(); +$gen->send($gen); +$gen->next(); + +?> +--EXPECTF-- +Fatal error: Cannot resume an already running generator in %s on line %d diff --git a/Zend/tests/generators/errors/yield_in_force_closed_finally_error.phpt b/Zend/tests/generators/errors/yield_in_force_closed_finally_error.phpt new file mode 100644 index 0000000000..aada676a68 --- /dev/null +++ b/Zend/tests/generators/errors/yield_in_force_closed_finally_error.phpt @@ -0,0 +1,29 @@ +--TEST-- +yield cannot be used in a finally block when the generator is force-closed +--FILE-- +<?php + +function gen() { + try { + echo "before yield\n"; + yield; + echo "after yield\n"; + } finally { + echo "before yield in finally\n"; + yield; + echo "after yield in finally\n"; + } + + echo "after finally\n"; +} + +$gen = gen(); +$gen->rewind(); +unset($gen); + +?> +--EXPECTF-- +before yield +before yield in finally + +Fatal error: Cannot yield from finally in a force-closed generator in %s on line %d diff --git a/Zend/tests/generators/generator_rewind.phpt b/Zend/tests/generators/generator_rewind.phpt new file mode 100644 index 0000000000..3224f6a9b6 --- /dev/null +++ b/Zend/tests/generators/generator_rewind.phpt @@ -0,0 +1,43 @@ +--TEST-- +A generator can only be rewinded before or at the first yield +--FILE-- +<?php + +function gen() { + echo "before yield\n"; + yield; + echo "after yield\n"; + yield; +} + +$gen = gen(); +$gen->rewind(); +$gen->rewind(); +$gen->next(); + +try { + $gen->rewind(); +} catch (Exception $e) { + echo "\n", $e, "\n\n"; +} + +function gen2() { + echo "in generator\n"; + + if (false) yield; +} + +$gen = gen2(); +$gen->rewind(); + +?> +--EXPECTF-- +before yield +after yield + +exception 'Exception' with message 'Cannot rewind a generator that was already run' in %s:%d +Stack trace: +#0 %s(%d): Generator->rewind() +#1 {main} + +in generator diff --git a/Zend/tests/generators/yield_in_finally.phpt b/Zend/tests/generators/yield_in_finally.phpt new file mode 100644 index 0000000000..805484ad1d --- /dev/null +++ b/Zend/tests/generators/yield_in_finally.phpt @@ -0,0 +1,29 @@ +--TEST-- +yield can be used in finally (apart from forced closes) +--FILE-- +<?php + +function gen() { + try { + echo "before return\n"; + return; + echo "after return\n"; + } finally { + echo "before yield\n"; + yield "yielded value"; + echo "after yield\n"; + } + + echo "after finally\n"; +} + +$gen = gen(); +var_dump($gen->current()); +$gen->next(); + +?> +--EXPECTF-- +before return +before yield +string(%d) "yielded value" +after yield diff --git a/Zend/zend_generators.c b/Zend/zend_generators.c index 3170ec9c33..03294f7f0e 100644 --- a/Zend/zend_generators.c +++ b/Zend/zend_generators.c @@ -31,11 +31,13 @@ void zend_generator_close(zend_generator *generator, zend_bool finished_executio { if (generator->execute_data) { zend_execute_data *execute_data = generator->execute_data; + zend_op_array *op_array = execute_data->op_array; if (!finished_execution) { - zend_op_array *op_array = execute_data->op_array; if (op_array->has_finally_block) { - zend_uint op_num = execute_data->opline - op_array->opcodes; + /* -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 */ @@ -59,6 +61,7 @@ void zend_generator_close(zend_generator *generator, zend_bool finished_executio if (finally_op_num) { execute_data->opline = &op_array->opcodes[finally_op_num]; execute_data->leaving = ZEND_RETURN; + generator->flags |= ZEND_GENERATOR_FORCED_CLOSE; zend_generator_resume(generator TSRMLS_CC); return; } @@ -66,7 +69,7 @@ void zend_generator_close(zend_generator *generator, zend_bool finished_executio } if (!execute_data->symbol_table) { - zend_free_compiled_variables(execute_data->CVs, execute_data->op_array->last_var); + zend_free_compiled_variables(execute_data->CVs, op_array->last_var); } else { zend_clean_and_cache_symbol_table(execute_data->symbol_table TSRMLS_CC); } @@ -83,8 +86,9 @@ void zend_generator_close(zend_generator *generator, zend_bool finished_executio * a return statement) we have to free loop variables manually, as * we don't know whether the SWITCH_FREE / FREE opcodes have run */ if (!finished_execution) { - zend_op_array *op_array = execute_data->op_array; - zend_uint op_num = execute_data->opline - op_array->opcodes; + /* -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; int i; for (i = 0; i < op_array->last_brk_cont; ++i) { @@ -411,6 +415,13 @@ void zend_generator_resume(zend_generator *generator TSRMLS_DC) /* {{{ */ return; } + if (generator->flags & ZEND_GENERATOR_CURRENTLY_RUNNING) { + zend_error(E_ERROR, "Cannot resume an already running generator"); + } + + /* Drop the AT_FIRST_YIELD flag */ + generator->flags &= ~ZEND_GENERATOR_AT_FIRST_YIELD; + { /* Backup executor globals */ zval **original_return_value_ptr_ptr = EG(return_value_ptr_ptr); @@ -455,7 +466,9 @@ void zend_generator_resume(zend_generator *generator TSRMLS_DC) /* {{{ */ generator->execute_data->prev_execute_data->prev_execute_data = original_execute_data; /* Resume execution */ + generator->flags |= ZEND_GENERATOR_CURRENTLY_RUNNING; execute_ex(generator->execute_data TSRMLS_CC); + generator->flags &= ~ZEND_GENERATOR_CURRENTLY_RUNNING; /* Restore executor globals */ EG(return_value_ptr_ptr) = original_return_value_ptr_ptr; @@ -489,6 +502,17 @@ static void zend_generator_ensure_initialized(zend_generator *generator TSRMLS_D { if (!generator->value) { zend_generator_resume(generator TSRMLS_CC); + generator->flags |= ZEND_GENERATOR_AT_FIRST_YIELD; + } +} +/* }}} */ + +static void zend_generator_rewind(zend_generator *generator TSRMLS_DC) /* {{{ */ +{ + zend_generator_ensure_initialized(generator TSRMLS_CC); + + if (!(generator->flags & ZEND_GENERATOR_AT_FIRST_YIELD)) { + zend_throw_exception(NULL, "Cannot rewind a generator that was already run", 0 TSRMLS_CC); } } /* }}} */ @@ -505,10 +529,7 @@ ZEND_METHOD(Generator, rewind) generator = (zend_generator *) zend_object_store_get_object(getThis() TSRMLS_CC); - zend_generator_ensure_initialized(generator TSRMLS_CC); - - /* Generators aren't rewindable, so rewind() only has to make sure that - * the generator is initialized, nothing more */ + zend_generator_rewind(generator TSRMLS_CC); } /* }}} */ @@ -721,13 +742,21 @@ static void zend_generator_iterator_move_forward(zend_object_iterator *iterator } /* }}} */ +static void zend_generator_iterator_rewind(zend_object_iterator *iterator TSRMLS_DC) /* {{{ */ +{ + zend_generator *generator = (zend_generator *) iterator->data; + + zend_generator_rewind(generator TSRMLS_CC); +} +/* }}} */ + static zend_object_iterator_funcs zend_generator_iterator_functions = { zend_generator_iterator_dtor, zend_generator_iterator_valid, zend_generator_iterator_get_data, zend_generator_iterator_get_key, zend_generator_iterator_move_forward, - NULL + zend_generator_iterator_rewind }; zend_object_iterator *zend_generator_get_iterator(zend_class_entry *ce, zval *object, int by_ref TSRMLS_DC) /* {{{ */ diff --git a/Zend/zend_generators.h b/Zend/zend_generators.h index 37ffbbd6cc..e47b7ad885 100644 --- a/Zend/zend_generators.h +++ b/Zend/zend_generators.h @@ -22,6 +22,8 @@ #define ZEND_GENERATORS_H BEGIN_EXTERN_C() +extern ZEND_API zend_class_entry *zend_ce_generator; +END_EXTERN_C() typedef struct _zend_generator { zend_object std; @@ -46,17 +48,20 @@ typedef struct _zend_generator { temp_variable *send_target; /* Largest used integer key for auto-incrementing keys */ long largest_used_integer_key; + + /* ZEND_GENERATOR_* flags */ + zend_uchar flags; } zend_generator; -extern ZEND_API zend_class_entry *zend_ce_generator; +static const zend_uchar ZEND_GENERATOR_CURRENTLY_RUNNING = 0x1; +static const zend_uchar ZEND_GENERATOR_FORCED_CLOSE = 0x2; +static const zend_uchar ZEND_GENERATOR_AT_FIRST_YIELD = 0x4; void zend_register_generator_ce(TSRMLS_D); zval *zend_generator_create_zval(zend_op_array *op_array TSRMLS_DC); void zend_generator_close(zend_generator *generator, zend_bool finished_execution TSRMLS_DC); void zend_generator_resume(zend_generator *generator TSRMLS_DC); -END_EXTERN_C() - #endif /* diff --git a/Zend/zend_vm_def.h b/Zend/zend_vm_def.h index 216cd59bdb..19031bc18d 100644 --- a/Zend/zend_vm_def.h +++ b/Zend/zend_vm_def.h @@ -5402,6 +5402,10 @@ ZEND_VM_HANDLER(160, ZEND_YIELD, CONST|TMP|VAR|CV|UNUSED, CONST|TMP|VAR|CV|UNUSE /* The generator object is stored in return_value_ptr_ptr */ zend_generator *generator = (zend_generator *) EG(return_value_ptr_ptr); + if (generator->flags & ZEND_GENERATOR_FORCED_CLOSE) { + zend_error_noreturn(E_ERROR, "Cannot yield from finally in a force-closed generator"); + } + /* Destroy the previously yielded value */ if (generator->value) { zval_ptr_dtor(&generator->value); diff --git a/Zend/zend_vm_execute.h b/Zend/zend_vm_execute.h index ebc0fb9c49..6a5e2fff12 100644 --- a/Zend/zend_vm_execute.h +++ b/Zend/zend_vm_execute.h @@ -4209,6 +4209,10 @@ static int ZEND_FASTCALL ZEND_YIELD_SPEC_CONST_CONST_HANDLER(ZEND_OPCODE_HANDLE /* The generator object is stored in return_value_ptr_ptr */ zend_generator *generator = (zend_generator *) EG(return_value_ptr_ptr); + if (generator->flags & ZEND_GENERATOR_FORCED_CLOSE) { + zend_error_noreturn(E_ERROR, "Cannot yield from finally in a force-closed generator"); + } + /* Destroy the previously yielded value */ if (generator->value) { zval_ptr_dtor(&generator->value); @@ -4899,6 +4903,10 @@ static int ZEND_FASTCALL ZEND_YIELD_SPEC_CONST_TMP_HANDLER(ZEND_OPCODE_HANDLER_ /* The generator object is stored in return_value_ptr_ptr */ zend_generator *generator = (zend_generator *) EG(return_value_ptr_ptr); + if (generator->flags & ZEND_GENERATOR_FORCED_CLOSE) { + zend_error_noreturn(E_ERROR, "Cannot yield from finally in a force-closed generator"); + } + /* Destroy the previously yielded value */ if (generator->value) { zval_ptr_dtor(&generator->value); @@ -5914,6 +5922,10 @@ static int ZEND_FASTCALL ZEND_YIELD_SPEC_CONST_VAR_HANDLER(ZEND_OPCODE_HANDLER_ /* The generator object is stored in return_value_ptr_ptr */ zend_generator *generator = (zend_generator *) EG(return_value_ptr_ptr); + if (generator->flags & ZEND_GENERATOR_FORCED_CLOSE) { + zend_error_noreturn(E_ERROR, "Cannot yield from finally in a force-closed generator"); + } + /* Destroy the previously yielded value */ if (generator->value) { zval_ptr_dtor(&generator->value); @@ -6624,6 +6636,10 @@ static int ZEND_FASTCALL ZEND_YIELD_SPEC_CONST_UNUSED_HANDLER(ZEND_OPCODE_HANDL /* The generator object is stored in return_value_ptr_ptr */ zend_generator *generator = (zend_generator *) EG(return_value_ptr_ptr); + if (generator->flags & ZEND_GENERATOR_FORCED_CLOSE) { + zend_error_noreturn(E_ERROR, "Cannot yield from finally in a force-closed generator"); + } + /* Destroy the previously yielded value */ if (generator->value) { zval_ptr_dtor(&generator->value); @@ -7373,6 +7389,10 @@ static int ZEND_FASTCALL ZEND_YIELD_SPEC_CONST_CV_HANDLER(ZEND_OPCODE_HANDLER_A /* The generator object is stored in return_value_ptr_ptr */ zend_generator *generator = (zend_generator *) EG(return_value_ptr_ptr); + if (generator->flags & ZEND_GENERATOR_FORCED_CLOSE) { + zend_error_noreturn(E_ERROR, "Cannot yield from finally in a force-closed generator"); + } + /* Destroy the previously yielded value */ if (generator->value) { zval_ptr_dtor(&generator->value); @@ -9436,6 +9456,10 @@ static int ZEND_FASTCALL ZEND_YIELD_SPEC_TMP_CONST_HANDLER(ZEND_OPCODE_HANDLER_ /* The generator object is stored in return_value_ptr_ptr */ zend_generator *generator = (zend_generator *) EG(return_value_ptr_ptr); + if (generator->flags & ZEND_GENERATOR_FORCED_CLOSE) { + zend_error_noreturn(E_ERROR, "Cannot yield from finally in a force-closed generator"); + } + /* Destroy the previously yielded value */ if (generator->value) { zval_ptr_dtor(&generator->value); @@ -10126,6 +10150,10 @@ static int ZEND_FASTCALL ZEND_YIELD_SPEC_TMP_TMP_HANDLER(ZEND_OPCODE_HANDLER_AR /* The generator object is stored in return_value_ptr_ptr */ zend_generator *generator = (zend_generator *) EG(return_value_ptr_ptr); + if (generator->flags & ZEND_GENERATOR_FORCED_CLOSE) { + zend_error_noreturn(E_ERROR, "Cannot yield from finally in a force-closed generator"); + } + /* Destroy the previously yielded value */ if (generator->value) { zval_ptr_dtor(&generator->value); @@ -11141,6 +11169,10 @@ static int ZEND_FASTCALL ZEND_YIELD_SPEC_TMP_VAR_HANDLER(ZEND_OPCODE_HANDLER_AR /* The generator object is stored in return_value_ptr_ptr */ zend_generator *generator = (zend_generator *) EG(return_value_ptr_ptr); + if (generator->flags & ZEND_GENERATOR_FORCED_CLOSE) { + zend_error_noreturn(E_ERROR, "Cannot yield from finally in a force-closed generator"); + } + /* Destroy the previously yielded value */ if (generator->value) { zval_ptr_dtor(&generator->value); @@ -11717,6 +11749,10 @@ static int ZEND_FASTCALL ZEND_YIELD_SPEC_TMP_UNUSED_HANDLER(ZEND_OPCODE_HANDLER /* The generator object is stored in return_value_ptr_ptr */ zend_generator *generator = (zend_generator *) EG(return_value_ptr_ptr); + if (generator->flags & ZEND_GENERATOR_FORCED_CLOSE) { + zend_error_noreturn(E_ERROR, "Cannot yield from finally in a force-closed generator"); + } + /* Destroy the previously yielded value */ if (generator->value) { zval_ptr_dtor(&generator->value); @@ -12404,6 +12440,10 @@ static int ZEND_FASTCALL ZEND_YIELD_SPEC_TMP_CV_HANDLER(ZEND_OPCODE_HANDLER_ARG /* The generator object is stored in return_value_ptr_ptr */ zend_generator *generator = (zend_generator *) EG(return_value_ptr_ptr); + if (generator->flags & ZEND_GENERATOR_FORCED_CLOSE) { + zend_error_noreturn(E_ERROR, "Cannot yield from finally in a force-closed generator"); + } + /* Destroy the previously yielded value */ if (generator->value) { zval_ptr_dtor(&generator->value); @@ -16326,6 +16366,10 @@ static int ZEND_FASTCALL ZEND_YIELD_SPEC_VAR_CONST_HANDLER(ZEND_OPCODE_HANDLER_ /* The generator object is stored in return_value_ptr_ptr */ zend_generator *generator = (zend_generator *) EG(return_value_ptr_ptr); + if (generator->flags & ZEND_GENERATOR_FORCED_CLOSE) { + zend_error_noreturn(E_ERROR, "Cannot yield from finally in a force-closed generator"); + } + /* Destroy the previously yielded value */ if (generator->value) { zval_ptr_dtor(&generator->value); @@ -18405,6 +18449,10 @@ static int ZEND_FASTCALL ZEND_YIELD_SPEC_VAR_TMP_HANDLER(ZEND_OPCODE_HANDLER_AR /* The generator object is stored in return_value_ptr_ptr */ zend_generator *generator = (zend_generator *) EG(return_value_ptr_ptr); + if (generator->flags & ZEND_GENERATOR_FORCED_CLOSE) { + zend_error_noreturn(E_ERROR, "Cannot yield from finally in a force-closed generator"); + } + /* Destroy the previously yielded value */ if (generator->value) { zval_ptr_dtor(&generator->value); @@ -20864,6 +20912,10 @@ static int ZEND_FASTCALL ZEND_YIELD_SPEC_VAR_VAR_HANDLER(ZEND_OPCODE_HANDLER_AR /* The generator object is stored in return_value_ptr_ptr */ zend_generator *generator = (zend_generator *) EG(return_value_ptr_ptr); + if (generator->flags & ZEND_GENERATOR_FORCED_CLOSE) { + zend_error_noreturn(E_ERROR, "Cannot yield from finally in a force-closed generator"); + } + /* Destroy the previously yielded value */ if (generator->value) { zval_ptr_dtor(&generator->value); @@ -22001,6 +22053,10 @@ static int ZEND_FASTCALL ZEND_YIELD_SPEC_VAR_UNUSED_HANDLER(ZEND_OPCODE_HANDLER /* The generator object is stored in return_value_ptr_ptr */ zend_generator *generator = (zend_generator *) EG(return_value_ptr_ptr); + if (generator->flags & ZEND_GENERATOR_FORCED_CLOSE) { + zend_error_noreturn(E_ERROR, "Cannot yield from finally in a force-closed generator"); + } + /* Destroy the previously yielded value */ if (generator->value) { zval_ptr_dtor(&generator->value); @@ -24129,6 +24185,10 @@ static int ZEND_FASTCALL ZEND_YIELD_SPEC_VAR_CV_HANDLER(ZEND_OPCODE_HANDLER_ARG /* The generator object is stored in return_value_ptr_ptr */ zend_generator *generator = (zend_generator *) EG(return_value_ptr_ptr); + if (generator->flags & ZEND_GENERATOR_FORCED_CLOSE) { + zend_error_noreturn(E_ERROR, "Cannot yield from finally in a force-closed generator"); + } + /* Destroy the previously yielded value */ if (generator->value) { zval_ptr_dtor(&generator->value); @@ -25616,6 +25676,10 @@ static int ZEND_FASTCALL ZEND_YIELD_SPEC_UNUSED_CONST_HANDLER(ZEND_OPCODE_HANDL /* The generator object is stored in return_value_ptr_ptr */ zend_generator *generator = (zend_generator *) EG(return_value_ptr_ptr); + if (generator->flags & ZEND_GENERATOR_FORCED_CLOSE) { + zend_error_noreturn(E_ERROR, "Cannot yield from finally in a force-closed generator"); + } + /* Destroy the previously yielded value */ if (generator->value) { zval_ptr_dtor(&generator->value); @@ -26925,6 +26989,10 @@ static int ZEND_FASTCALL ZEND_YIELD_SPEC_UNUSED_TMP_HANDLER(ZEND_OPCODE_HANDLER /* The generator object is stored in return_value_ptr_ptr */ zend_generator *generator = (zend_generator *) EG(return_value_ptr_ptr); + if (generator->flags & ZEND_GENERATOR_FORCED_CLOSE) { + zend_error_noreturn(E_ERROR, "Cannot yield from finally in a force-closed generator"); + } + /* Destroy the previously yielded value */ if (generator->value) { zval_ptr_dtor(&generator->value); @@ -28234,6 +28302,10 @@ static int ZEND_FASTCALL ZEND_YIELD_SPEC_UNUSED_VAR_HANDLER(ZEND_OPCODE_HANDLER /* The generator object is stored in return_value_ptr_ptr */ zend_generator *generator = (zend_generator *) EG(return_value_ptr_ptr); + if (generator->flags & ZEND_GENERATOR_FORCED_CLOSE) { + zend_error_noreturn(E_ERROR, "Cannot yield from finally in a force-closed generator"); + } + /* Destroy the previously yielded value */ if (generator->value) { zval_ptr_dtor(&generator->value); @@ -28654,6 +28726,10 @@ static int ZEND_FASTCALL ZEND_YIELD_SPEC_UNUSED_UNUSED_HANDLER(ZEND_OPCODE_HAND /* The generator object is stored in return_value_ptr_ptr */ zend_generator *generator = (zend_generator *) EG(return_value_ptr_ptr); + if (generator->flags & ZEND_GENERATOR_FORCED_CLOSE) { + zend_error_noreturn(E_ERROR, "Cannot yield from finally in a force-closed generator"); + } + /* Destroy the previously yielded value */ if (generator->value) { zval_ptr_dtor(&generator->value); @@ -29960,6 +30036,10 @@ static int ZEND_FASTCALL ZEND_YIELD_SPEC_UNUSED_CV_HANDLER(ZEND_OPCODE_HANDLER_ /* The generator object is stored in return_value_ptr_ptr */ zend_generator *generator = (zend_generator *) EG(return_value_ptr_ptr); + if (generator->flags & ZEND_GENERATOR_FORCED_CLOSE) { + zend_error_noreturn(E_ERROR, "Cannot yield from finally in a force-closed generator"); + } + /* Destroy the previously yielded value */ if (generator->value) { zval_ptr_dtor(&generator->value); @@ -33481,6 +33561,10 @@ static int ZEND_FASTCALL ZEND_YIELD_SPEC_CV_CONST_HANDLER(ZEND_OPCODE_HANDLER_A /* The generator object is stored in return_value_ptr_ptr */ zend_generator *generator = (zend_generator *) EG(return_value_ptr_ptr); + if (generator->flags & ZEND_GENERATOR_FORCED_CLOSE) { + zend_error_noreturn(E_ERROR, "Cannot yield from finally in a force-closed generator"); + } + /* Destroy the previously yielded value */ if (generator->value) { zval_ptr_dtor(&generator->value); @@ -35429,6 +35513,10 @@ static int ZEND_FASTCALL ZEND_YIELD_SPEC_CV_TMP_HANDLER(ZEND_OPCODE_HANDLER_ARG /* The generator object is stored in return_value_ptr_ptr */ zend_generator *generator = (zend_generator *) EG(return_value_ptr_ptr); + if (generator->flags & ZEND_GENERATOR_FORCED_CLOSE) { + zend_error_noreturn(E_ERROR, "Cannot yield from finally in a force-closed generator"); + } + /* Destroy the previously yielded value */ if (generator->value) { zval_ptr_dtor(&generator->value); @@ -37756,6 +37844,10 @@ static int ZEND_FASTCALL ZEND_YIELD_SPEC_CV_VAR_HANDLER(ZEND_OPCODE_HANDLER_ARG /* The generator object is stored in return_value_ptr_ptr */ zend_generator *generator = (zend_generator *) EG(return_value_ptr_ptr); + if (generator->flags & ZEND_GENERATOR_FORCED_CLOSE) { + zend_error_noreturn(E_ERROR, "Cannot yield from finally in a force-closed generator"); + } + /* Destroy the previously yielded value */ if (generator->value) { zval_ptr_dtor(&generator->value); @@ -38752,6 +38844,10 @@ static int ZEND_FASTCALL ZEND_YIELD_SPEC_CV_UNUSED_HANDLER(ZEND_OPCODE_HANDLER_ /* The generator object is stored in return_value_ptr_ptr */ zend_generator *generator = (zend_generator *) EG(return_value_ptr_ptr); + if (generator->flags & ZEND_GENERATOR_FORCED_CLOSE) { + zend_error_noreturn(E_ERROR, "Cannot yield from finally in a force-closed generator"); + } + /* Destroy the previously yielded value */ if (generator->value) { zval_ptr_dtor(&generator->value); @@ -40748,6 +40844,10 @@ static int ZEND_FASTCALL ZEND_YIELD_SPEC_CV_CV_HANDLER(ZEND_OPCODE_HANDLER_ARGS /* The generator object is stored in return_value_ptr_ptr */ zend_generator *generator = (zend_generator *) EG(return_value_ptr_ptr); + if (generator->flags & ZEND_GENERATOR_FORCED_CLOSE) { + zend_error_noreturn(E_ERROR, "Cannot yield from finally in a force-closed generator"); + } + /* Destroy the previously yielded value */ if (generator->value) { zval_ptr_dtor(&generator->value); |