Alternatively could throw some kind of uncatchable dummy exception
into the generator. Right now just checking for NULL in two places
seems simpler.
This commit is contained in:
Nikita Popov 2016-05-28 14:38:11 +02:00
parent 79de9fa4aa
commit 921b3251b3
6 changed files with 118 additions and 4 deletions

2
NEWS
View File

@ -14,6 +14,8 @@ PHP NEWS
. Fixed bug #62814 (It is possible to stiffen child class members visibility).
(Nikita)
. Fixed bug #69989 (Generators don't participate in cycle GC). (Nikita)
. Fixed bug #71604 (Aborted Generators continue after nested finally).
(Nikita)
. Fixed bug #71572 (String offset assignment from an empty string inserts
null byte). (Francois)
. Fixed bug #71897 (ASCII 0x7F Delete control character permitted in

View File

@ -0,0 +1,25 @@
--TEST--
Bug #71604: Aborted Generators continue after nested finally
--FILE--
<?php
function gen() {
try {
try {
yield;
} finally {
print "INNER\n";
}
} catch (Exception $e) {
print "EX\n";
} finally {
print "OUTER\n";
}
print "NOTREACHED\n";
}
gen()->current();
?>
--EXPECT--
INNER
OUTER

View File

@ -0,0 +1,39 @@
--TEST--
Bug #71604: Aborted Generators continue after nested finally (2)
--FILE--
<?php
function gen() {
try {
throw new Exception(1);
} finally {
try {
throw new Exception(2);
} finally {
try {
yield;
} finally {
}
}
}
}
try {
gen()->rewind();
} catch (Exception $e) {
echo $e, "\n";
}
?>
--EXPECTF--
Exception: 1 in %s:%d
Stack trace:
#0 [internal function]: gen()
#1 %s(%d): Generator->rewind()
#2 {main}
Next Exception: 2 in %s:%d
Stack trace:
#0 [internal function]: gen()
#1 %s(%d): Generator->rewind()
#2 {main}

View File

@ -0,0 +1,38 @@
--TEST--
Bug #71604: Aborted Generators continue after nested finally (3)
--FILE--
<?php
function gen() {
try {
throw new Exception(1);
} finally {
try {
yield;
} finally {
try {
throw new Exception(2);
} finally {
}
}
}
}
try {
gen()->rewind();
} catch (Exception $e) {
echo $e, "\n";
}
?>
--EXPECTF--
Exception: 1 in %s:%d
Stack trace:
#0 [internal function]: gen()
#1 %s(%d): Generator->rewind()
#2 {main}
Next Exception: 2 in %s:%d
Stack trace:
#0 %s(%d): gen()
#1 {main}

View File

@ -7107,6 +7107,7 @@ ZEND_VM_HANDLER(155, ZEND_BIND_TRAITS, ANY, ANY)
ZEND_VM_HELPER(zend_dispatch_try_catch_finally_helper, ANY, ANY, uint32_t try_catch_offset, uint32_t op_num)
{
/* May be NULL during generator closing (only finally blocks are executed) */
zend_object *ex = EG(exception);
/* Walk try/catch/finally structures upwards, performing the necessary actions */
@ -7114,7 +7115,7 @@ ZEND_VM_HELPER(zend_dispatch_try_catch_finally_helper, ANY, ANY, uint32_t try_ca
zend_try_catch_element *try_catch =
&EX(func)->op_array.try_catch_array[try_catch_offset];
if (op_num < try_catch->catch_op) {
if (op_num < try_catch->catch_op && ex) {
/* Go to catch block */
cleanup_live_vars(execute_data, op_num, try_catch->catch_op);
ZEND_VM_SET_OPCODE(&EX(func)->op_array.opcodes[try_catch->catch_op]);
@ -7134,7 +7135,11 @@ ZEND_VM_HELPER(zend_dispatch_try_catch_finally_helper, ANY, ANY, uint32_t try_ca
/* Chain potential exception from wrapping finally block */
zval *fast_call = EX_VAR(EX(func)->op_array.opcodes[try_catch->finally_end].op1.var);
if (Z_OBJ_P(fast_call)) {
zend_exception_set_previous(ex, Z_OBJ_P(fast_call));
if (ex) {
zend_exception_set_previous(ex, Z_OBJ_P(fast_call));
} else {
EG(exception) = Z_OBJ_P(fast_call);
}
ex = Z_OBJ_P(fast_call);
}
}

View File

@ -1692,6 +1692,7 @@ static ZEND_OPCODE_HANDLER_RET ZEND_FASTCALL ZEND_BIND_TRAITS_SPEC_HANDLER(ZEND_
static ZEND_OPCODE_HANDLER_RET ZEND_FASTCALL zend_dispatch_try_catch_finally_helper_SPEC(uint32_t try_catch_offset, uint32_t op_num ZEND_OPCODE_HANDLER_ARGS_DC)
{
/* May be NULL during generator closing (only finally blocks are executed) */
zend_object *ex = EG(exception);
/* Walk try/catch/finally structures upwards, performing the necessary actions */
@ -1699,7 +1700,7 @@ static ZEND_OPCODE_HANDLER_RET ZEND_FASTCALL zend_dispatch_try_catch_finally_hel
zend_try_catch_element *try_catch =
&EX(func)->op_array.try_catch_array[try_catch_offset];
if (op_num < try_catch->catch_op) {
if (op_num < try_catch->catch_op && ex) {
/* Go to catch block */
cleanup_live_vars(execute_data, op_num, try_catch->catch_op);
ZEND_VM_SET_OPCODE(&EX(func)->op_array.opcodes[try_catch->catch_op]);
@ -1719,7 +1720,11 @@ static ZEND_OPCODE_HANDLER_RET ZEND_FASTCALL zend_dispatch_try_catch_finally_hel
/* Chain potential exception from wrapping finally block */
zval *fast_call = EX_VAR(EX(func)->op_array.opcodes[try_catch->finally_end].op1.var);
if (Z_OBJ_P(fast_call)) {
zend_exception_set_previous(ex, Z_OBJ_P(fast_call));
if (ex) {
zend_exception_set_previous(ex, Z_OBJ_P(fast_call));
} else {
EG(exception) = Z_OBJ_P(fast_call);
}
ex = Z_OBJ_P(fast_call);
}
}