Browse Source

Restored proper generators behaviour in conjunction with "finally". (Nikita)

pull/242/merge
Dmitry Stogov 13 years ago
parent
commit
9c96fe52d9
  1. 0
      Zend/tests/generators/finally/finally_ran_on_close.phpt
  2. 2
      Zend/tests/generators/finally/return_return.phpt
  3. 18
      Zend/tests/generators/finally/return_yield.phpt
  4. 24
      Zend/tests/generators/finally/throw_yield.phpt
  5. 18
      Zend/tests/generators/finally/yield_return.phpt
  6. 24
      Zend/tests/generators/finally/yield_throw.phpt
  7. 22
      Zend/tests/generators/finally/yield_yield.phpt
  8. 28
      Zend/tests/generators/finally_uninterrupted.phpt
  9. 38
      Zend/zend_generators.c
  10. 22
      Zend/zend_opcode.c
  11. 13
      Zend/zend_vm_def.h
  12. 63
      Zend/zend_vm_execute.h
  13. 1
      Zend/zend_vm_opcodes.h

0
Zend/tests/generators/finally_ran_on_close.phpt → Zend/tests/generators/finally/finally_ran_on_close.phpt

2
Zend/tests/generators/finally_with_return.phpt → Zend/tests/generators/finally/return_return.phpt

@ -1,5 +1,5 @@
--TEST--
Use of finally in generator with return
try { return } finally { return } in generator
--FILE--
<?php

18
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

24
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

18
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

24
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

22
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

28
Zend/tests/generators/finally_uninterrupted.phpt

@ -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

38
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++;

22
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:

13
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

63
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,

1
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

Loading…
Cancel
Save