diff options
author | Nikita Popov <nikic@php.net> | 2012-08-24 15:50:53 +0200 |
---|---|---|
committer | Nikita Popov <nikic@php.net> | 2012-08-24 19:10:09 +0200 |
commit | 4d8edda341efef1901365f10213c027e745ac7ab (patch) | |
tree | 2f69b955ebbdfa67aee4899d94a693cf7529e952 | |
parent | 7cdf6367a51a54fce8676aeb6fd32bf91b00f84b (diff) | |
download | php-git-4d8edda341efef1901365f10213c027e745ac7ab.tar.gz |
Run finally if generator is closed before finishing
-rw-r--r-- | Zend/tests/generators/finally_ran_on_close.phpt | 25 | ||||
-rw-r--r-- | Zend/zend_generators.c | 35 | ||||
-rw-r--r-- | Zend/zend_generators.h | 1 |
3 files changed, 60 insertions, 1 deletions
diff --git a/Zend/tests/generators/finally_ran_on_close.phpt b/Zend/tests/generators/finally_ran_on_close.phpt new file mode 100644 index 0000000000..44a84fae5c --- /dev/null +++ b/Zend/tests/generators/finally_ran_on_close.phpt @@ -0,0 +1,25 @@ +--TEST-- +finally is run even if a generator is closed mid-execution +--FILE-- +<?php + +function gen() { + try { + echo "before yield\n"; + yield; + echo "after yield\n"; + } finally { + echo "finally run\n"; + } + + echo "code after finally\n"; +} + +$gen = gen(); +$gen->rewind(); +unset($gen); + +?> +--EXPECT-- +before yield +finally run diff --git a/Zend/zend_generators.c b/Zend/zend_generators.c index b4d8932b6b..3170ec9c33 100644 --- a/Zend/zend_generators.c +++ b/Zend/zend_generators.c @@ -32,6 +32,39 @@ void zend_generator_close(zend_generator *generator, zend_bool finished_executio if (generator->execute_data) { zend_execute_data *execute_data = generator->execute_data; + 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; + 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->leaving = ZEND_RETURN; + zend_generator_resume(generator TSRMLS_CC); + return; + } + } + } + if (!execute_data->symbol_table) { zend_free_compiled_variables(execute_data->CVs, execute_data->op_array->last_var); } else { @@ -371,7 +404,7 @@ static zend_function *zend_generator_get_constructor(zval *object TSRMLS_DC) /* } /* }}} */ -static void zend_generator_resume(zend_generator *generator TSRMLS_DC) /* {{{ */ +void zend_generator_resume(zend_generator *generator TSRMLS_DC) /* {{{ */ { /* The generator is already closed, thus can't resume */ if (!generator->execute_data) { diff --git a/Zend/zend_generators.h b/Zend/zend_generators.h index f5f4926e35..37ffbbd6cc 100644 --- a/Zend/zend_generators.h +++ b/Zend/zend_generators.h @@ -53,6 +53,7 @@ extern ZEND_API zend_class_entry *zend_ce_generator; 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() |