--TEST--
-Use of finally in generator with return
+try { return } finally { return } in generator
--FILE--
<?php
--- /dev/null
+--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
--- /dev/null
+--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
--- /dev/null
+--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
--- /dev/null
+--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
--- /dev/null
+--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
+++ /dev/null
---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
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);
/* 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++;
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);
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 &&
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);
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:
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
}
}
-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
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,
#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