From c767fec2d068cebb114fafaf58c83dc70f1dcac7 Mon Sep 17 00:00:00 2001 From: Arnaud Le Blanc Date: Sat, 10 Aug 2024 15:46:31 +0200 Subject: [PATCH 1/5] Fix crash during GC of suspended generator delegate (#15275) --- Zend/tests/gh15275-001.phpt | 38 +++++++++++++++++++++++++ Zend/tests/gh15275-002.phpt | 57 +++++++++++++++++++++++++++++++++++++ Zend/tests/gh15275-003.phpt | 51 +++++++++++++++++++++++++++++++++ Zend/tests/gh15275-004.phpt | 44 ++++++++++++++++++++++++++++ Zend/tests/gh15275-005.phpt | 51 +++++++++++++++++++++++++++++++++ Zend/tests/gh15275-006.phpt | 51 +++++++++++++++++++++++++++++++++ Zend/zend_execute.c | 8 +++++- Zend/zend_generators.c | 19 +++++++++---- 8 files changed, 313 insertions(+), 6 deletions(-) create mode 100644 Zend/tests/gh15275-001.phpt create mode 100644 Zend/tests/gh15275-002.phpt create mode 100644 Zend/tests/gh15275-003.phpt create mode 100644 Zend/tests/gh15275-004.phpt create mode 100644 Zend/tests/gh15275-005.phpt create mode 100644 Zend/tests/gh15275-006.phpt diff --git a/Zend/tests/gh15275-001.phpt b/Zend/tests/gh15275-001.phpt new file mode 100644 index 0000000000000..bfea734c6ba9b --- /dev/null +++ b/Zend/tests/gh15275-001.phpt @@ -0,0 +1,38 @@ +--TEST-- +GH-15275 001: Crash during GC of suspended generator delegate +--FILE-- +current()); + $iterable->next(); + var_dump("not executed"); +}); + +$ref = $fiber; + +$fiber->start(); + +gc_collect_cycles(); + +?> +==DONE== +--EXPECT-- +string(3) "foo" +==DONE== diff --git a/Zend/tests/gh15275-002.phpt b/Zend/tests/gh15275-002.phpt new file mode 100644 index 0000000000000..7b1f88c9f2b26 --- /dev/null +++ b/Zend/tests/gh15275-002.phpt @@ -0,0 +1,57 @@ +--TEST-- +GH-15275 002: Crash during GC of suspended generator delegate +--FILE-- +current()); + $gen->next(); + var_dump("not executed"); +}); + +$ref = $fiber; + +$fiber->start(); + +gc_collect_cycles(); + +?> +==DONE== +--EXPECT-- +string(3) "foo" +==DONE== +string(15) "It::getIterator" +string(1) "f" +string(1) "g" diff --git a/Zend/tests/gh15275-003.phpt b/Zend/tests/gh15275-003.phpt new file mode 100644 index 0000000000000..d62187e5342b7 --- /dev/null +++ b/Zend/tests/gh15275-003.phpt @@ -0,0 +1,51 @@ +--TEST-- +GH-15275 003: Crash during GC of suspended generator delegate +--FILE-- +current()); +$gen->next(); + +?> +==DONE== +--EXPECTF-- +string(3) "foo" +string(1) "f" +string(1) "g" + +Fatal error: Uncaught Exception in %s:8 +Stack trace: +#0 %s(15): It->getIterator() +#1 %s(23): f() +#2 [internal function]: g() +#3 %s(32): Generator->next() +#4 {main} + thrown in %s on line 8 diff --git a/Zend/tests/gh15275-004.phpt b/Zend/tests/gh15275-004.phpt new file mode 100644 index 0000000000000..61939ad2a4557 --- /dev/null +++ b/Zend/tests/gh15275-004.phpt @@ -0,0 +1,44 @@ +--TEST-- +GH-15275 004: Crash during GC of suspended generator delegate +--FILE-- +current()); +$gen->next(); + +gc_collect_cycles(); + +?> +==DONE== +--EXPECTF-- +string(3) "foo" +baz + +Fatal error: Uncaught Exception in %s:9 +Stack trace: +#0 %s(19): It->getIterator() +#1 [internal function]: f() +#2 %s(25): Generator->next() +#3 {main} + thrown in %s on line 9 diff --git a/Zend/tests/gh15275-005.phpt b/Zend/tests/gh15275-005.phpt new file mode 100644 index 0000000000000..07a6f48e7a17e --- /dev/null +++ b/Zend/tests/gh15275-005.phpt @@ -0,0 +1,51 @@ +--TEST-- +GH-15275 005: Crash during GC of suspended generator delegate +--FILE-- +current()); +$gen->next(); + +?> +==DONE== +--EXPECTF-- +string(3) "foo" +string(1) "f" +string(1) "g" + +Fatal error: Uncaught Exception in %s:8 +Stack trace: +#0 %s(15): It->getIterator() +#1 %s(23): f() +#2 [internal function]: g() +#3 %s(32): Generator->next() +#4 {main} + thrown in %s on line 8 diff --git a/Zend/tests/gh15275-006.phpt b/Zend/tests/gh15275-006.phpt new file mode 100644 index 0000000000000..7f585acf0f40b --- /dev/null +++ b/Zend/tests/gh15275-006.phpt @@ -0,0 +1,51 @@ +--TEST-- +GH-15275 006: Crash during GC of suspended generator delegate +--FILE-- +current()); +$gen->next(); + +gc_collect_cycles(); + +?> +==DONE== +--EXPECTF-- +string(3) "foo" +baz + +Fatal error: Uncaught Exception in %s:9 +Stack trace: +#0 %s(19): It->getIterator() +#1 [internal function]: f() +#2 %s(25): Generator->next() +#3 {main} + +Next Exception in %s:14 +Stack trace: +#0 %s(19): It->__destruct() +#1 [internal function]: f() +#2 %s(25): Generator->next() +#3 {main} + thrown in %s on line 14 diff --git a/Zend/zend_execute.c b/Zend/zend_execute.c index 3de48fb1358c5..716756ad93ba2 100644 --- a/Zend/zend_execute.c +++ b/Zend/zend_execute.c @@ -4471,7 +4471,13 @@ ZEND_API HashTable *zend_unfinished_execution_gc_ex(zend_execute_data *execute_d } if (call) { - uint32_t op_num = execute_data->opline - op_array->opcodes; + uint32_t op_num; + if (UNEXPECTED(execute_data->opline->opcode == ZEND_HANDLE_EXCEPTION)) { + op_num = EG(opline_before_exception) - op_array->opcodes; + } else { + op_num = execute_data->opline - op_array->opcodes; + } + ZEND_ASSERT(op_num < op_array->last); if (suspended_by_yield) { /* When the execution was suspended by yield, EX(opline) points to * next opline to execute. Otherwise, it points to the opline that diff --git a/Zend/zend_generators.c b/Zend/zend_generators.c index f7475ad6fbb77..85127025679d6 100644 --- a/Zend/zend_generators.c +++ b/Zend/zend_generators.c @@ -26,6 +26,7 @@ #include "zend_closures.h" #include "zend_generators_arginfo.h" #include "zend_observer.h" +#include "zend_vm_opcodes.h" ZEND_API zend_class_entry *zend_ce_generator; ZEND_API zend_class_entry *zend_ce_ClosedGeneratorException; @@ -512,6 +513,8 @@ static void zend_generator_throw_exception(zend_generator *generator, zval *exce * to pretend the exception happened during the YIELD opcode. */ EG(current_execute_data) = generator->execute_data; generator->execute_data->opline--; + ZEND_ASSERT(generator->execute_data->opline->opcode == ZEND_YIELD + || generator->execute_data->opline->opcode == ZEND_YIELD_FROM); generator->execute_data->prev_execute_data = original_execute_data; if (exception) { @@ -520,13 +523,14 @@ static void zend_generator_throw_exception(zend_generator *generator, zval *exce zend_rethrow_exception(EG(current_execute_data)); } + generator->execute_data->opline++; + /* if we don't stop an array/iterator yield from, the exception will only reach the generator after the values were all iterated over */ if (UNEXPECTED(Z_TYPE(generator->values) != IS_UNDEF)) { zval_ptr_dtor(&generator->values); ZVAL_UNDEF(&generator->values); } - generator->execute_data->opline++; EG(current_execute_data) = original_execute_data; } @@ -656,8 +660,6 @@ ZEND_API zend_generator *zend_generator_update_current(zend_generator *generator static zend_result zend_generator_get_next_delegated_value(zend_generator *generator) /* {{{ */ { - --generator->execute_data->opline; - zval *value; if (Z_TYPE(generator->values) == IS_ARRAY) { HashTable *ht = Z_ARR(generator->values); @@ -739,14 +741,12 @@ static zend_result zend_generator_get_next_delegated_value(zend_generator *gener } } - ++generator->execute_data->opline; return SUCCESS; failure: zval_ptr_dtor(&generator->values); ZVAL_UNDEF(&generator->values); - ++generator->execute_data->opline; return FAILURE; } /* }}} */ @@ -811,6 +811,15 @@ ZEND_API void zend_generator_resume(zend_generator *orig_generator) /* {{{ */ generator->flags &= ~ZEND_GENERATOR_IN_FIBER; return; } + if (UNEXPECTED(EG(exception))) { + /* Decrementing opline_before_exception to pretend the exception + * happened during the YIELD_FROM opcode. */ + if (generator->execute_data) { + ZEND_ASSERT(generator->execute_data->opline == EG(exception_op)); + ZEND_ASSERT((EG(opline_before_exception)-1)->opcode == ZEND_YIELD_FROM); + EG(opline_before_exception)--; + } + } /* If there are no more delegated values, resume the generator * after the "yield from" expression. */ } From 39bacafeed6e1d7021eb910300f74cc72ac1f1d8 Mon Sep 17 00:00:00 2001 From: Arnaud Le Blanc Date: Sat, 10 Aug 2024 15:48:30 +0200 Subject: [PATCH 2/5] [ci skip] NEWS for GH-15275 --- NEWS | 2 ++ 1 file changed, 2 insertions(+) diff --git a/NEWS b/NEWS index 7de98d7bfb621..8d3550cfb80a7 100644 --- a/NEWS +++ b/NEWS @@ -11,6 +11,8 @@ PHP NEWS . Fix uninitialized memory in network.c. (nielsdos) . Fixed bug GH-15108 (Segfault when destroying generator during shutdown). (Arnaud) + . Fixed bug GH-15275 (Crash during GC of suspended generator delegate). + (Arnaud) - Curl: . Fixed case when curl_error returns an empty string. From 4606202b89b067ae20cc0498603872e6e4c2b1b4 Mon Sep 17 00:00:00 2001 From: Arnaud Le Blanc Date: Sat, 10 Aug 2024 15:50:21 +0200 Subject: [PATCH 3/5] [ci skip] NEWS for GH-15275 --- NEWS | 2 ++ 1 file changed, 2 insertions(+) diff --git a/NEWS b/NEWS index fcf0fe7268fa6..705a0b064106b 100644 --- a/NEWS +++ b/NEWS @@ -11,6 +11,8 @@ PHP NEWS . Fix uninitialized memory in network.c. (nielsdos) . Fixed bug GH-15108 (Segfault when destroying generator during shutdown). (Arnaud) + . Fixed bug GH-15275 (Crash during GC of suspended generator delegate). + (Arnaud) - Curl: . Fixed case when curl_error returns an empty string. From 22502026d7e8b554bf43b73b7e5bcd0699f1b5cc Mon Sep 17 00:00:00 2001 From: Arnaud Le Blanc Date: Sat, 10 Aug 2024 15:52:10 +0200 Subject: [PATCH 4/5] [ci skip] NEWS for GH-15275 --- NEWS | 2 ++ 1 file changed, 2 insertions(+) diff --git a/NEWS b/NEWS index 3562198d0e795..ea782aef3e411 100644 --- a/NEWS +++ b/NEWS @@ -83,6 +83,8 @@ PHP NEWS . Added missing cstddef include for C++ builds. (cmb) . Fixed bug GH-15108 (Segfault when destroying generator during shutdown). (Arnaud) + . Fixed bug GH-15275 (Crash during GC of suspended generator delegate). + (Arnaud) - BCMath: . Adjust bcround()'s $mode parameter to only accept the RoundingMode From 84ea7a603e0b2abdf15c4449d29f82def9862f65 Mon Sep 17 00:00:00 2001 From: Arnaud Le Blanc Date: Thu, 8 Aug 2024 12:34:59 +0200 Subject: [PATCH 5/5] Change YIELD/YIELD_FROM to do not increment opline --- Zend/zend_execute.c | 39 ++------ Zend/zend_generators.c | 40 ++++---- Zend/zend_vm_def.h | 10 +- Zend/zend_vm_execute.h | 94 +------------------ .../tests/ReflectionGenerator_isClosed.phpt | 10 +- .../ReflectionGenerator_isClosed_002.phpt | 4 +- 6 files changed, 35 insertions(+), 162 deletions(-) diff --git a/Zend/zend_execute.c b/Zend/zend_execute.c index bcba10b774336..602bb3b0e79f5 100644 --- a/Zend/zend_execute.c +++ b/Zend/zend_execute.c @@ -4711,19 +4711,7 @@ ZEND_API void zend_cleanup_unfinished_execution(zend_execute_data *execute_data, ZEND_API ZEND_ATTRIBUTE_DEPRECATED HashTable *zend_unfinished_execution_gc(zend_execute_data *execute_data, zend_execute_data *call, zend_get_gc_buffer *gc_buffer) { - bool suspended_by_yield = false; - - if (Z_TYPE_INFO(EX(This)) & ZEND_CALL_GENERATOR) { - ZEND_ASSERT(EX(return_value)); - - /* The generator object is stored in EX(return_value) */ - zend_generator *generator = (zend_generator*) EX(return_value); - ZEND_ASSERT(execute_data == generator->execute_data); - - suspended_by_yield = !(generator->flags & ZEND_GENERATOR_CURRENTLY_RUNNING); - } - - return zend_unfinished_execution_gc_ex(execute_data, call, gc_buffer, suspended_by_yield); + return zend_unfinished_execution_gc_ex(execute_data, call, gc_buffer, false); } ZEND_API HashTable *zend_unfinished_execution_gc_ex(zend_execute_data *execute_data, zend_execute_data *call, zend_get_gc_buffer *gc_buffer, bool suspended_by_yield) @@ -4761,27 +4749,20 @@ ZEND_API HashTable *zend_unfinished_execution_gc_ex(zend_execute_data *execute_d zend_get_gc_buffer_add_zval(gc_buffer, &extra_named_params); } + uint32_t op_num; + if (UNEXPECTED(execute_data->opline->opcode == ZEND_HANDLE_EXCEPTION)) { + op_num = EG(opline_before_exception) - op_array->opcodes; + } else { + op_num = execute_data->opline - op_array->opcodes; + } + ZEND_ASSERT(op_num < op_array->last); + if (call) { - uint32_t op_num; - if (UNEXPECTED(execute_data->opline->opcode == ZEND_HANDLE_EXCEPTION)) { - op_num = EG(opline_before_exception) - op_array->opcodes; - } else { - op_num = execute_data->opline - op_array->opcodes; - } - ZEND_ASSERT(op_num < op_array->last); - if (suspended_by_yield) { - /* When the execution was suspended by yield, EX(opline) points to - * next opline to execute. Otherwise, it points to the opline that - * suspended execution. */ - op_num--; - ZEND_ASSERT(EX(func)->op_array.opcodes[op_num].opcode == ZEND_YIELD - || EX(func)->op_array.opcodes[op_num].opcode == ZEND_YIELD_FROM); - } zend_unfinished_calls_gc(execute_data, call, op_num, gc_buffer); } if (execute_data->opline != op_array->opcodes) { - uint32_t i, op_num = execute_data->opline - op_array->opcodes - 1; + uint32_t i; for (i = 0; i < op_array->last_live_range; i++) { const zend_live_range *range = &op_array->live_range[i]; if (range->start > op_num) { diff --git a/Zend/zend_generators.c b/Zend/zend_generators.c index 4fa64a338474e..3ec308246c7c1 100644 --- a/Zend/zend_generators.c +++ b/Zend/zend_generators.c @@ -116,8 +116,7 @@ static void zend_generator_cleanup_unfinished_execution( { zend_op_array *op_array = &execute_data->func->op_array; if (execute_data->opline != op_array->opcodes) { - /* -1 required because we want the last run opcode, not the next to-be-run one. */ - uint32_t op_num = execute_data->opline - op_array->opcodes - 1; + uint32_t op_num = execute_data->opline - op_array->opcodes; if (UNEXPECTED(generator->frozen_call_stack)) { /* Temporarily restore generator->execute_data if it has been NULLed out already. */ @@ -299,9 +298,7 @@ static void zend_generator_dtor_storage(zend_object *object) /* {{{ */ return; } - /* -1 required because we want the last run opcode, not the - * next to-be-run one. */ - op_num = ex->opline - ex->func->op_array.opcodes - 1; + op_num = ex->opline - ex->func->op_array.opcodes; try_catch_offset = -1; /* Find the innermost try/catch that we are inside of. */ @@ -331,7 +328,8 @@ static void zend_generator_dtor_storage(zend_object *object) /* {{{ */ Z_OBJ_P(fast_call) = NULL; Z_OPLINE_NUM_P(fast_call) = (uint32_t)-1; - ex->opline = &ex->func->op_array.opcodes[try_catch->finally_op]; + /* -1 because zend_generator_resume() will increment it */ + ex->opline = &ex->func->op_array.opcodes[try_catch->finally_op] - 1; generator->flags |= ZEND_GENERATOR_FORCED_CLOSE; zend_generator_resume(generator); @@ -516,9 +514,6 @@ static void zend_generator_throw_exception(zend_generator *generator, zval *exce /* Throw the exception in the context of the generator. Decrementing the opline * to pretend the exception happened during the YIELD opcode. */ EG(current_execute_data) = generator->execute_data; - generator->execute_data->opline--; - ZEND_ASSERT(generator->execute_data->opline->opcode == ZEND_YIELD - || generator->execute_data->opline->opcode == ZEND_YIELD_FROM); generator->execute_data->prev_execute_data = original_execute_data; if (exception) { @@ -527,8 +522,6 @@ static void zend_generator_throw_exception(zend_generator *generator, zval *exce zend_rethrow_exception(EG(current_execute_data)); } - generator->execute_data->opline++; - /* if we don't stop an array/iterator yield from, the exception will only reach the generator after the values were all iterated over */ if (UNEXPECTED(Z_TYPE(generator->values) != IS_UNDEF)) { zval_ptr_dtor(&generator->values); @@ -621,7 +614,7 @@ ZEND_API zend_generator *zend_generator_update_current(zend_generator *generator zend_generator_remove_child(&new_root_parent->node, new_root); if (EXPECTED(EG(exception) == NULL) && EXPECTED((OBJ_FLAGS(&generator->std) & IS_OBJ_DESTRUCTOR_CALLED) == 0)) { - zend_op *yield_from = (zend_op *) new_root->execute_data->opline - 1; + zend_op *yield_from = (zend_op *) new_root->execute_data->opline; if (yield_from->opcode == ZEND_YIELD_FROM) { if (Z_ISUNDEF(new_root_parent->retval)) { @@ -636,8 +629,6 @@ ZEND_API zend_generator *zend_generator_update_current(zend_generator *generator generator->execute_fake.prev_execute_data = original_execute_data; } - /* ZEND_YIELD(_FROM) already advance, so decrement opline to throw from correct place */ - new_root->execute_data->opline--; zend_throw_exception(zend_ce_ClosedGeneratorException, "Generator yielded from aborted, no return value available", 0); EG(current_execute_data) = original_execute_data; @@ -815,15 +806,6 @@ ZEND_API void zend_generator_resume(zend_generator *orig_generator) /* {{{ */ generator->flags &= ~ZEND_GENERATOR_IN_FIBER; return; } - if (UNEXPECTED(EG(exception))) { - /* Decrementing opline_before_exception to pretend the exception - * happened during the YIELD_FROM opcode. */ - if (generator->execute_data) { - ZEND_ASSERT(generator->execute_data->opline == EG(exception_op)); - ZEND_ASSERT((EG(opline_before_exception)-1)->opcode == ZEND_YIELD_FROM); - EG(opline_before_exception)--; - } - } /* If there are no more delegated values, resume the generator * after the "yield from" expression. */ } @@ -834,6 +816,16 @@ ZEND_API void zend_generator_resume(zend_generator *orig_generator) /* {{{ */ } /* Resume execution */ + ZEND_ASSERT(generator->execute_data->opline->opcode == ZEND_GENERATOR_CREATE + || generator->execute_data->opline->opcode == ZEND_YIELD + || generator->execute_data->opline->opcode == ZEND_YIELD_FROM + /* opline points to EG(exception_op), which is a sequence of + * ZEND_HANDLE_EXCEPTION ops, so the following increment is safe */ + || generator->execute_data->opline->opcode == ZEND_HANDLE_EXCEPTION + /* opline points to the start of a finally block minus one op to + * account for the following increment */ + || (generator->flags & ZEND_GENERATOR_FORCED_CLOSE)); + generator->execute_data->opline++; generator->flags |= ZEND_GENERATOR_CURRENTLY_RUNNING; if (!ZEND_OBSERVER_ENABLED) { zend_execute_ex(generator->execute_data); @@ -880,7 +872,7 @@ ZEND_API void zend_generator_resume(zend_generator *orig_generator) /* {{{ */ } /* yield from was used, try another resume. */ - if (UNEXPECTED((generator != orig_generator && !Z_ISUNDEF(generator->retval)) || (generator->execute_data && (generator->execute_data->opline - 1)->opcode == ZEND_YIELD_FROM))) { + if (UNEXPECTED((generator != orig_generator && !Z_ISUNDEF(generator->retval)) || (generator->execute_data && generator->execute_data->opline->opcode == ZEND_YIELD_FROM))) { generator = zend_generator_get_current(orig_generator); goto try_again; } diff --git a/Zend/zend_vm_def.h b/Zend/zend_vm_def.h index 7c3a22ddb119e..c99cc9c1a2f65 100644 --- a/Zend/zend_vm_def.h +++ b/Zend/zend_vm_def.h @@ -4609,7 +4609,7 @@ ZEND_VM_HANDLER(139, ZEND_GENERATOR_CREATE, ANY, ANY) generator->execute_fake.prev_execute_data = NULL; ZVAL_OBJ(&generator->execute_fake.This, (zend_object *) generator); - gen_execute_data->opline = opline + 1; + gen_execute_data->opline = opline; /* EX(return_value) keeps pointer to zend_object (not a real zval) */ gen_execute_data->return_value = (zval*)generator; call_info = Z_TYPE_INFO(EX(This)); @@ -8416,10 +8416,6 @@ ZEND_VM_HANDLER(160, ZEND_YIELD, CONST|TMP|VAR|CV|UNUSED, CONST|TMPVAR|CV|UNUSED generator->send_target = NULL; } - /* We increment to the next op, so we are at the correct position when the - * generator is resumed. */ - ZEND_VM_INC_OPCODE(); - /* The GOTO VM uses a local opline variable. We need to set the opline * variable in execute_data so we don't resume at an old position. */ SAVE_OPLINE(); @@ -8522,10 +8518,6 @@ ZEND_VM_C_LABEL(yield_from_try_again): /* This generator has no send target (though the generator we delegate to might have one) */ generator->send_target = NULL; - /* We increment to the next op, so we are at the correct position when the - * generator is resumed. */ - ZEND_VM_INC_OPCODE(); - /* The GOTO VM uses a local opline variable. We need to set the opline * variable in execute_data so we don't resume at an old position. */ SAVE_OPLINE(); diff --git a/Zend/zend_vm_execute.h b/Zend/zend_vm_execute.h index f234bb149a280..75499f028e2cd 100644 --- a/Zend/zend_vm_execute.h +++ b/Zend/zend_vm_execute.h @@ -2222,7 +2222,7 @@ static ZEND_OPCODE_HANDLER_RET ZEND_FASTCALL ZEND_GENERATOR_CREATE_SPEC_HANDLER( generator->execute_fake.prev_execute_data = NULL; ZVAL_OBJ(&generator->execute_fake.This, (zend_object *) generator); - gen_execute_data->opline = opline + 1; + gen_execute_data->opline = opline; /* EX(return_value) keeps pointer to zend_object (not a real zval) */ gen_execute_data->return_value = (zval*)generator; call_info = Z_TYPE_INFO(EX(This)); @@ -5866,10 +5866,6 @@ static ZEND_OPCODE_HANDLER_RET ZEND_FASTCALL ZEND_YIELD_FROM_SPEC_CONST_HANDLER( /* This generator has no send target (though the generator we delegate to might have one) */ generator->send_target = NULL; - /* We increment to the next op, so we are at the correct position when the - * generator is resumed. */ - ZEND_VM_INC_OPCODE(); - /* The GOTO VM uses a local opline variable. We need to set the opline * variable in execute_data so we don't resume at an old position. */ SAVE_OPLINE(); @@ -8098,10 +8094,6 @@ static ZEND_OPCODE_HANDLER_RET ZEND_FASTCALL ZEND_YIELD_SPEC_CONST_CONST_HANDLER generator->send_target = NULL; } - /* We increment to the next op, so we are at the correct position when the - * generator is resumed. */ - ZEND_VM_INC_OPCODE(); - /* The GOTO VM uses a local opline variable. We need to set the opline * variable in execute_data so we don't resume at an old position. */ SAVE_OPLINE(); @@ -10429,10 +10421,6 @@ static ZEND_OPCODE_HANDLER_RET ZEND_FASTCALL ZEND_YIELD_SPEC_CONST_TMPVAR_HANDLE generator->send_target = NULL; } - /* We increment to the next op, so we are at the correct position when the - * generator is resumed. */ - ZEND_VM_INC_OPCODE(); - /* The GOTO VM uses a local opline variable. We need to set the opline * variable in execute_data so we don't resume at an old position. */ SAVE_OPLINE(); @@ -11277,10 +11265,6 @@ static ZEND_OPCODE_HANDLER_RET ZEND_FASTCALL ZEND_YIELD_SPEC_CONST_UNUSED_HANDLE generator->send_target = NULL; } - /* We increment to the next op, so we are at the correct position when the - * generator is resumed. */ - ZEND_VM_INC_OPCODE(); - /* The GOTO VM uses a local opline variable. We need to set the opline * variable in execute_data so we don't resume at an old position. */ SAVE_OPLINE(); @@ -12917,10 +12901,6 @@ static ZEND_OPCODE_HANDLER_RET ZEND_FASTCALL ZEND_YIELD_SPEC_CONST_CV_HANDLER(ZE generator->send_target = NULL; } - /* We increment to the next op, so we are at the correct position when the - * generator is resumed. */ - ZEND_VM_INC_OPCODE(); - /* The GOTO VM uses a local opline variable. We need to set the opline * variable in execute_data so we don't resume at an old position. */ SAVE_OPLINE(); @@ -15465,10 +15445,6 @@ static ZEND_OPCODE_HANDLER_RET ZEND_FASTCALL ZEND_YIELD_FROM_SPEC_TMPVAR_HANDLER /* This generator has no send target (though the generator we delegate to might have one) */ generator->send_target = NULL; - /* We increment to the next op, so we are at the correct position when the - * generator is resumed. */ - ZEND_VM_INC_OPCODE(); - /* The GOTO VM uses a local opline variable. We need to set the opline * variable in execute_data so we don't resume at an old position. */ SAVE_OPLINE(); @@ -20930,10 +20906,6 @@ static ZEND_OPCODE_HANDLER_RET ZEND_FASTCALL ZEND_YIELD_SPEC_TMP_CONST_HANDLER(Z generator->send_target = NULL; } - /* We increment to the next op, so we are at the correct position when the - * generator is resumed. */ - ZEND_VM_INC_OPCODE(); - /* The GOTO VM uses a local opline variable. We need to set the opline * variable in execute_data so we don't resume at an old position. */ SAVE_OPLINE(); @@ -21375,10 +21347,6 @@ static ZEND_OPCODE_HANDLER_RET ZEND_FASTCALL ZEND_YIELD_SPEC_TMP_TMPVAR_HANDLER( generator->send_target = NULL; } - /* We increment to the next op, so we are at the correct position when the - * generator is resumed. */ - ZEND_VM_INC_OPCODE(); - /* The GOTO VM uses a local opline variable. We need to set the opline * variable in execute_data so we don't resume at an old position. */ SAVE_OPLINE(); @@ -21835,10 +21803,6 @@ static ZEND_OPCODE_HANDLER_RET ZEND_FASTCALL ZEND_YIELD_SPEC_TMP_UNUSED_HANDLER( generator->send_target = NULL; } - /* We increment to the next op, so we are at the correct position when the - * generator is resumed. */ - ZEND_VM_INC_OPCODE(); - /* The GOTO VM uses a local opline variable. We need to set the opline * variable in execute_data so we don't resume at an old position. */ SAVE_OPLINE(); @@ -22239,10 +22203,6 @@ static ZEND_OPCODE_HANDLER_RET ZEND_FASTCALL ZEND_YIELD_SPEC_TMP_CV_HANDLER(ZEND generator->send_target = NULL; } - /* We increment to the next op, so we are at the correct position when the - * generator is resumed. */ - ZEND_VM_INC_OPCODE(); - /* The GOTO VM uses a local opline variable. We need to set the opline * variable in execute_data so we don't resume at an old position. */ SAVE_OPLINE(); @@ -26243,10 +26203,6 @@ static ZEND_OPCODE_HANDLER_RET ZEND_FASTCALL ZEND_YIELD_SPEC_VAR_CONST_HANDLER(Z generator->send_target = NULL; } - /* We increment to the next op, so we are at the correct position when the - * generator is resumed. */ - ZEND_VM_INC_OPCODE(); - /* The GOTO VM uses a local opline variable. We need to set the opline * variable in execute_data so we don't resume at an old position. */ SAVE_OPLINE(); @@ -28736,10 +28692,6 @@ static ZEND_OPCODE_HANDLER_RET ZEND_FASTCALL ZEND_YIELD_SPEC_VAR_TMPVAR_HANDLER( generator->send_target = NULL; } - /* We increment to the next op, so we are at the correct position when the - * generator is resumed. */ - ZEND_VM_INC_OPCODE(); - /* The GOTO VM uses a local opline variable. We need to set the opline * variable in execute_data so we don't resume at an old position. */ SAVE_OPLINE(); @@ -30691,10 +30643,6 @@ static ZEND_OPCODE_HANDLER_RET ZEND_FASTCALL ZEND_YIELD_SPEC_VAR_UNUSED_HANDLER( generator->send_target = NULL; } - /* We increment to the next op, so we are at the correct position when the - * generator is resumed. */ - ZEND_VM_INC_OPCODE(); - /* The GOTO VM uses a local opline variable. We need to set the opline * variable in execute_data so we don't resume at an old position. */ SAVE_OPLINE(); @@ -33161,10 +33109,6 @@ static ZEND_OPCODE_HANDLER_RET ZEND_FASTCALL ZEND_YIELD_SPEC_VAR_CV_HANDLER(ZEND generator->send_target = NULL; } - /* We increment to the next op, so we are at the correct position when the - * generator is resumed. */ - ZEND_VM_INC_OPCODE(); - /* The GOTO VM uses a local opline variable. We need to set the opline * variable in execute_data so we don't resume at an old position. */ SAVE_OPLINE(); @@ -35442,10 +35386,6 @@ static ZEND_OPCODE_HANDLER_RET ZEND_FASTCALL ZEND_YIELD_SPEC_UNUSED_CONST_HANDLE generator->send_target = NULL; } - /* We increment to the next op, so we are at the correct position when the - * generator is resumed. */ - ZEND_VM_INC_OPCODE(); - /* The GOTO VM uses a local opline variable. We need to set the opline * variable in execute_data so we don't resume at an old position. */ SAVE_OPLINE(); @@ -37415,10 +37355,6 @@ static ZEND_OPCODE_HANDLER_RET ZEND_FASTCALL ZEND_YIELD_SPEC_UNUSED_TMPVAR_HANDL generator->send_target = NULL; } - /* We increment to the next op, so we are at the correct position when the - * generator is resumed. */ - ZEND_VM_INC_OPCODE(); - /* The GOTO VM uses a local opline variable. We need to set the opline * variable in execute_data so we don't resume at an old position. */ SAVE_OPLINE(); @@ -37956,10 +37892,6 @@ static ZEND_OPCODE_HANDLER_RET ZEND_FASTCALL ZEND_YIELD_SPEC_UNUSED_UNUSED_HANDL generator->send_target = NULL; } - /* We increment to the next op, so we are at the correct position when the - * generator is resumed. */ - ZEND_VM_INC_OPCODE(); - /* The GOTO VM uses a local opline variable. We need to set the opline * variable in execute_data so we don't resume at an old position. */ SAVE_OPLINE(); @@ -40037,10 +39969,6 @@ static ZEND_OPCODE_HANDLER_RET ZEND_FASTCALL ZEND_YIELD_SPEC_UNUSED_CV_HANDLER(Z generator->send_target = NULL; } - /* We increment to the next op, so we are at the correct position when the - * generator is resumed. */ - ZEND_VM_INC_OPCODE(); - /* The GOTO VM uses a local opline variable. We need to set the opline * variable in execute_data so we don't resume at an old position. */ SAVE_OPLINE(); @@ -41396,10 +41324,6 @@ static ZEND_OPCODE_HANDLER_RET ZEND_FASTCALL ZEND_YIELD_FROM_SPEC_CV_HANDLER(ZEN /* This generator has no send target (though the generator we delegate to might have one) */ generator->send_target = NULL; - /* We increment to the next op, so we are at the correct position when the - * generator is resumed. */ - ZEND_VM_INC_OPCODE(); - /* The GOTO VM uses a local opline variable. We need to set the opline * variable in execute_data so we don't resume at an old position. */ SAVE_OPLINE(); @@ -45469,10 +45393,6 @@ static ZEND_OPCODE_HANDLER_RET ZEND_FASTCALL ZEND_YIELD_SPEC_CV_CONST_HANDLER(ZE generator->send_target = NULL; } - /* We increment to the next op, so we are at the correct position when the - * generator is resumed. */ - ZEND_VM_INC_OPCODE(); - /* The GOTO VM uses a local opline variable. We need to set the opline * variable in execute_data so we don't resume at an old position. */ SAVE_OPLINE(); @@ -49157,10 +49077,6 @@ static ZEND_OPCODE_HANDLER_RET ZEND_FASTCALL ZEND_YIELD_SPEC_CV_TMPVAR_HANDLER(Z generator->send_target = NULL; } - /* We increment to the next op, so we are at the correct position when the - * generator is resumed. */ - ZEND_VM_INC_OPCODE(); - /* The GOTO VM uses a local opline variable. We need to set the opline * variable in execute_data so we don't resume at an old position. */ SAVE_OPLINE(); @@ -51014,10 +50930,6 @@ static ZEND_OPCODE_HANDLER_RET ZEND_FASTCALL ZEND_YIELD_SPEC_CV_UNUSED_HANDLER(Z generator->send_target = NULL; } - /* We increment to the next op, so we are at the correct position when the - * generator is resumed. */ - ZEND_VM_INC_OPCODE(); - /* The GOTO VM uses a local opline variable. We need to set the opline * variable in execute_data so we don't resume at an old position. */ SAVE_OPLINE(); @@ -54728,10 +54640,6 @@ static ZEND_OPCODE_HANDLER_RET ZEND_FASTCALL ZEND_YIELD_SPEC_CV_CV_HANDLER(ZEND_ generator->send_target = NULL; } - /* We increment to the next op, so we are at the correct position when the - * generator is resumed. */ - ZEND_VM_INC_OPCODE(); - /* The GOTO VM uses a local opline variable. We need to set the opline * variable in execute_data so we don't resume at an old position. */ SAVE_OPLINE(); diff --git a/ext/reflection/tests/ReflectionGenerator_isClosed.phpt b/ext/reflection/tests/ReflectionGenerator_isClosed.phpt index 059a174657d6f..3fc893083ce91 100644 --- a/ext/reflection/tests/ReflectionGenerator_isClosed.phpt +++ b/ext/reflection/tests/ReflectionGenerator_isClosed.phpt @@ -32,15 +32,15 @@ foreach ($gens as $gen) { } ?> ---EXPECTF-- -int(10) +--EXPECT-- +int(9) bool(false) -int(10) +int(9) bool(true) Cannot fetch information from a closed Generator -int(4) +int(3) bool(false) -int(4) +int(3) bool(true) Cannot fetch information from a closed Generator diff --git a/ext/reflection/tests/ReflectionGenerator_isClosed_002.phpt b/ext/reflection/tests/ReflectionGenerator_isClosed_002.phpt index c3dda39a283a9..1676369c5038a 100644 --- a/ext/reflection/tests/ReflectionGenerator_isClosed_002.phpt +++ b/ext/reflection/tests/ReflectionGenerator_isClosed_002.phpt @@ -30,11 +30,11 @@ foreach ($gens as $gen) { } ?> ---EXPECTF-- +--EXPECT-- bool(false) bool(true) bool(false) -int(11) +int(10) bool(false) bool(false)