mirror of
https://github.com/php/php-src.git
synced 2025-08-15 21:48:51 +02:00
Merge branch 'PHP-8.0' into PHP-8.1
This commit is contained in:
commit
16dcededa8
3 changed files with 112 additions and 81 deletions
2
NEWS
2
NEWS
|
@ -8,6 +8,8 @@ PHP NEWS
|
|||
. Fixed bug GH-8108 (Timezone doesn't work as intended). (Derick)
|
||||
. Fixed bug #81660 (DateTimeZone::getTransitions() returns invalid data).
|
||||
(Derick)
|
||||
. Fixed bug GH-8289 (Exceptions thrown within a yielded from iterator are
|
||||
not rethrown into the generator). (Bob)
|
||||
|
||||
31 Mar 2022, PHP 8.1.5
|
||||
|
||||
|
|
34
Zend/tests/generators/gh8289.phpt
Normal file
34
Zend/tests/generators/gh8289.phpt
Normal file
|
@ -0,0 +1,34 @@
|
|||
--TEST--
|
||||
GH-8289 (Exceptions thrown within a yielded from iterator are not rethrown into the generator)
|
||||
--FILE--
|
||||
<?php
|
||||
|
||||
function yieldFromIteratorGeneratorThrows() {
|
||||
try {
|
||||
yield from new class(new ArrayIterator([1, -2])) extends IteratorIterator {
|
||||
public function key() {
|
||||
if ($k = parent::key()) {
|
||||
throw new Exception;
|
||||
}
|
||||
return $k;
|
||||
}
|
||||
};
|
||||
} catch (Exception $e) {
|
||||
echo "$e\n";
|
||||
yield 2;
|
||||
}
|
||||
}
|
||||
|
||||
foreach (yieldFromIteratorGeneratorThrows() as $k => $v) {
|
||||
var_dump($v);
|
||||
}
|
||||
|
||||
?>
|
||||
--EXPECTF--
|
||||
int(1)
|
||||
Exception in %s:%d
|
||||
Stack trace:
|
||||
#0 %s(%d): IteratorIterator@anonymous->key()
|
||||
#1 %s(%d): yieldFromIteratorGeneratorThrows()
|
||||
#2 {main}
|
||||
int(2)
|
|
@ -637,22 +637,17 @@ static zend_result zend_generator_get_next_delegated_value(zend_generator *gener
|
|||
if (iter->index++ > 0) {
|
||||
iter->funcs->move_forward(iter);
|
||||
if (UNEXPECTED(EG(exception) != NULL)) {
|
||||
goto exception;
|
||||
goto failure;
|
||||
}
|
||||
}
|
||||
|
||||
if (iter->funcs->valid(iter) == FAILURE) {
|
||||
if (UNEXPECTED(EG(exception) != NULL)) {
|
||||
goto exception;
|
||||
}
|
||||
/* reached end of iteration */
|
||||
goto failure;
|
||||
}
|
||||
|
||||
value = iter->funcs->get_current_data(iter);
|
||||
if (UNEXPECTED(EG(exception) != NULL)) {
|
||||
goto exception;
|
||||
} else if (UNEXPECTED(!value)) {
|
||||
if (UNEXPECTED(EG(exception) != NULL) || UNEXPECTED(!value)) {
|
||||
goto failure;
|
||||
}
|
||||
|
||||
|
@ -664,7 +659,7 @@ static zend_result zend_generator_get_next_delegated_value(zend_generator *gener
|
|||
iter->funcs->get_current_key(iter, &generator->key);
|
||||
if (UNEXPECTED(EG(exception) != NULL)) {
|
||||
ZVAL_UNDEF(&generator->key);
|
||||
goto exception;
|
||||
goto failure;
|
||||
}
|
||||
} else {
|
||||
ZVAL_LONG(&generator->key, iter->index);
|
||||
|
@ -672,9 +667,6 @@ static zend_result zend_generator_get_next_delegated_value(zend_generator *gener
|
|||
}
|
||||
return SUCCESS;
|
||||
|
||||
exception:
|
||||
zend_generator_throw_exception(generator, NULL);
|
||||
|
||||
failure:
|
||||
zval_ptr_dtor(&generator->values);
|
||||
ZVAL_UNDEF(&generator->values);
|
||||
|
@ -706,8 +698,33 @@ try_again:
|
|||
/* Drop the AT_FIRST_YIELD flag */
|
||||
orig_generator->flags &= ~ZEND_GENERATOR_AT_FIRST_YIELD;
|
||||
|
||||
/* Backup executor globals */
|
||||
zend_execute_data *original_execute_data = EG(current_execute_data);
|
||||
uint32_t original_jit_trace_num = EG(jit_trace_num);
|
||||
|
||||
/* Set executor globals */
|
||||
EG(current_execute_data) = generator->execute_data;
|
||||
EG(jit_trace_num) = 0;
|
||||
|
||||
/* We want the backtrace to look as if the generator function was
|
||||
* called from whatever method we are current running (e.g. next()).
|
||||
* So we have to link generator call frame with caller call frame. */
|
||||
if (generator == orig_generator) {
|
||||
generator->execute_data->prev_execute_data = original_execute_data;
|
||||
} else {
|
||||
/* We need some execute_data placeholder in stacktrace to be replaced
|
||||
* by the real stack trace when needed */
|
||||
generator->execute_data->prev_execute_data = &orig_generator->execute_fake;
|
||||
orig_generator->execute_fake.prev_execute_data = original_execute_data;
|
||||
}
|
||||
|
||||
/* Ensure this is run after executor_data swap to have a proper stack trace */
|
||||
if (UNEXPECTED(!Z_ISUNDEF(generator->values))) {
|
||||
if (EXPECTED(zend_generator_get_next_delegated_value(generator) == SUCCESS)) {
|
||||
/* Restore executor globals */
|
||||
EG(current_execute_data) = original_execute_data;
|
||||
EG(jit_trace_num) = original_jit_trace_num;
|
||||
|
||||
orig_generator->flags &= ~ZEND_GENERATOR_DO_INIT;
|
||||
return;
|
||||
}
|
||||
|
@ -715,85 +732,63 @@ try_again:
|
|||
* after the "yield from" expression. */
|
||||
}
|
||||
|
||||
{
|
||||
/* Backup executor globals */
|
||||
zend_execute_data *original_execute_data = EG(current_execute_data);
|
||||
uint32_t original_jit_trace_num = EG(jit_trace_num);
|
||||
if (UNEXPECTED(generator->frozen_call_stack)) {
|
||||
/* Restore frozen call-stack */
|
||||
zend_generator_restore_call_stack(generator);
|
||||
}
|
||||
|
||||
/* Set executor globals */
|
||||
EG(current_execute_data) = generator->execute_data;
|
||||
EG(jit_trace_num) = 0;
|
||||
/* Resume execution */
|
||||
generator->flags |= ZEND_GENERATOR_CURRENTLY_RUNNING;
|
||||
if (!ZEND_OBSERVER_ENABLED) {
|
||||
zend_execute_ex(generator->execute_data);
|
||||
} else {
|
||||
zend_observer_generator_resume(generator->execute_data);
|
||||
zend_execute_ex(generator->execute_data);
|
||||
if (generator->execute_data) {
|
||||
/* On the final return, this will be called from ZEND_GENERATOR_RETURN */
|
||||
zend_observer_fcall_end(generator->execute_data, &generator->value);
|
||||
}
|
||||
}
|
||||
generator->flags &= ~ZEND_GENERATOR_CURRENTLY_RUNNING;
|
||||
|
||||
/* We want the backtrace to look as if the generator function was
|
||||
* called from whatever method we are current running (e.g. next()).
|
||||
* So we have to link generator call frame with caller call frame. */
|
||||
generator->frozen_call_stack = NULL;
|
||||
if (EXPECTED(generator->execute_data) &&
|
||||
UNEXPECTED(generator->execute_data->call)) {
|
||||
/* Frize call-stack */
|
||||
generator->frozen_call_stack = zend_generator_freeze_call_stack(generator->execute_data);
|
||||
}
|
||||
|
||||
/* Restore executor globals */
|
||||
EG(current_execute_data) = original_execute_data;
|
||||
EG(jit_trace_num) = original_jit_trace_num;
|
||||
|
||||
/* If an exception was thrown in the generator we have to internally
|
||||
* rethrow it in the parent scope.
|
||||
* In case we did yield from, the Exception must be rethrown into
|
||||
* its calling frame (see above in if (check_yield_from). */
|
||||
if (UNEXPECTED(EG(exception) != NULL)) {
|
||||
if (generator == orig_generator) {
|
||||
generator->execute_data->prev_execute_data = original_execute_data;
|
||||
} else {
|
||||
/* We need some execute_data placeholder in stacktrace to be replaced
|
||||
* by the real stack trace when needed */
|
||||
generator->execute_data->prev_execute_data = &orig_generator->execute_fake;
|
||||
orig_generator->execute_fake.prev_execute_data = original_execute_data;
|
||||
}
|
||||
|
||||
if (UNEXPECTED(generator->frozen_call_stack)) {
|
||||
/* Restore frozen call-stack */
|
||||
zend_generator_restore_call_stack(generator);
|
||||
}
|
||||
|
||||
/* Resume execution */
|
||||
generator->flags |= ZEND_GENERATOR_CURRENTLY_RUNNING;
|
||||
if (!ZEND_OBSERVER_ENABLED) {
|
||||
zend_execute_ex(generator->execute_data);
|
||||
} else {
|
||||
zend_observer_generator_resume(generator->execute_data);
|
||||
zend_execute_ex(generator->execute_data);
|
||||
if (generator->execute_data) {
|
||||
/* On the final return, this will be called from ZEND_GENERATOR_RETURN */
|
||||
zend_observer_fcall_end(generator->execute_data, &generator->value);
|
||||
zend_generator_close(generator, 0);
|
||||
if (!EG(current_execute_data)) {
|
||||
zend_throw_exception_internal(NULL);
|
||||
} else if (EG(current_execute_data)->func &&
|
||||
ZEND_USER_CODE(EG(current_execute_data)->func->common.type)) {
|
||||
zend_rethrow_exception(EG(current_execute_data));
|
||||
}
|
||||
}
|
||||
generator->flags &= ~ZEND_GENERATOR_CURRENTLY_RUNNING;
|
||||
|
||||
generator->frozen_call_stack = NULL;
|
||||
if (EXPECTED(generator->execute_data) &&
|
||||
UNEXPECTED(generator->execute_data->call)) {
|
||||
/* Frize call-stack */
|
||||
generator->frozen_call_stack = zend_generator_freeze_call_stack(generator->execute_data);
|
||||
}
|
||||
|
||||
/* Restore executor globals */
|
||||
EG(current_execute_data) = original_execute_data;
|
||||
EG(jit_trace_num) = original_jit_trace_num;
|
||||
|
||||
/* If an exception was thrown in the generator we have to internally
|
||||
* rethrow it in the parent scope.
|
||||
* In case we did yield from, the Exception must be rethrown into
|
||||
* its calling frame (see above in if (check_yield_from). */
|
||||
if (UNEXPECTED(EG(exception) != NULL)) {
|
||||
if (generator == orig_generator) {
|
||||
zend_generator_close(generator, 0);
|
||||
if (!EG(current_execute_data)) {
|
||||
zend_throw_exception_internal(NULL);
|
||||
} else if (EG(current_execute_data)->func &&
|
||||
ZEND_USER_CODE(EG(current_execute_data)->func->common.type)) {
|
||||
zend_rethrow_exception(EG(current_execute_data));
|
||||
}
|
||||
} else {
|
||||
generator = zend_generator_get_current(orig_generator);
|
||||
zend_generator_throw_exception(generator, NULL);
|
||||
orig_generator->flags &= ~ZEND_GENERATOR_DO_INIT;
|
||||
goto try_again;
|
||||
}
|
||||
}
|
||||
|
||||
/* 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))) {
|
||||
} else {
|
||||
generator = zend_generator_get_current(orig_generator);
|
||||
zend_generator_throw_exception(generator, NULL);
|
||||
orig_generator->flags &= ~ZEND_GENERATOR_DO_INIT;
|
||||
goto try_again;
|
||||
}
|
||||
}
|
||||
|
||||
/* 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))) {
|
||||
generator = zend_generator_get_current(orig_generator);
|
||||
goto try_again;
|
||||
}
|
||||
|
||||
orig_generator->flags &= ~ZEND_GENERATOR_DO_INIT;
|
||||
}
|
||||
/* }}} */
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue