1
0
mirror of https://github.com/php/php-src.git synced 2026-03-24 00:02:20 +01:00

Support partial GC for unfinished generators

This doesn't cover everything yet, but should be a good start for
cycled in unfinished generators.
This commit is contained in:
Nikita Popov
2016-02-11 16:38:30 +01:00
parent 68516091c1
commit f75be35312
3 changed files with 105 additions and 3 deletions

View File

@@ -0,0 +1,42 @@
--TEST--
Collection of some cycles on unfinished generators
--FILE--
<?php
// CV
function gen1() {
$gen = yield;
yield;
}
$gen = gen1();
$gen->send($gen);
// This
class Test {
public $gen;
public function gen2() {
yield;
}
}
$test = new Test;
$test->gen = $test->gen2();
// Closure object
$gen3 = (function() use (&$gen3) {
yield;
})();
// Yield from array
function gen4() {
yield from [yield];
}
$gen = gen4();
$gen->send($gen);
?>
===DONE===
--EXPECT--
===DONE===

View File

@@ -98,6 +98,12 @@ ZEND_API void zend_generator_close(zend_generator *generator, zend_bool finished
OBJ_RELEASE((zend_object *) EX(func)->common.prototype);
}
/* Free GC buffer. GC for closed generators doesn't need an allocated buffer */
if (generator->gc_buffer) {
efree(generator->gc_buffer);
generator->gc_buffer = NULL;
}
efree(generator->stack);
generator->execute_data = NULL;
}
@@ -188,11 +194,63 @@ static void zend_generator_free_storage(zend_object *object) /* {{{ */
}
/* }}} */
static uint32_t calc_gc_buffer_size(zend_generator *generator) /* {{{ */
{
uint32_t size = 4; /* value, key, retval, values */
if (generator->execute_data) {
zend_execute_data *execute_data = generator->execute_data;
zend_op_array *op_array = &EX(func)->op_array;
size += op_array->last_var; /* CVs */
size += Z_OBJ(execute_data->This) != NULL; /* $this */
size += (EX_CALL_INFO() & ZEND_CALL_CLOSURE) != 0; /* Closure object */
}
return size;
}
/* }}} */
static HashTable *zend_generator_get_gc(zval *object, zval **table, int *n) /* {{{ */
{
zend_generator *generator = (zend_generator*) Z_OBJ_P(object);
*table = &generator->value;
*n = 3;
zend_execute_data *execute_data = generator->execute_data;
zval *gc_buffer;
uint32_t gc_buffer_size;
if (!execute_data) {
/* If the generator has been closed, it can only hold on to three values: The value, key
* and retval. These three zvals are stored sequentially starting at &generator->value. */
*table = &generator->value;
*n = 3;
return NULL;
}
gc_buffer_size = calc_gc_buffer_size(generator);
if (!generator->gc_buffer) {
generator->gc_buffer = safe_emalloc(sizeof(zval), gc_buffer_size, 0);
}
*n = gc_buffer_size;
*table = gc_buffer = generator->gc_buffer;
ZVAL_COPY_VALUE(gc_buffer++, &generator->value);
ZVAL_COPY_VALUE(gc_buffer++, &generator->key);
ZVAL_COPY_VALUE(gc_buffer++, &generator->retval);
ZVAL_COPY_VALUE(gc_buffer++, &generator->values);
{
uint32_t i, num_cvs = EX(func)->op_array.last_var;
for (i = 0; i < num_cvs; i++) {
ZVAL_COPY_VALUE(gc_buffer++, EX_VAR_NUM(i));
}
}
if (Z_OBJ(execute_data->This)) {
ZVAL_OBJ(gc_buffer++, Z_OBJ(execute_data->This));
}
if (EX_CALL_INFO() & ZEND_CALL_CLOSURE) {
ZVAL_OBJ(gc_buffer++, (zend_object *) EX(func)->common.prototype);
}
return NULL;
}
/* }}} */

View File

@@ -82,7 +82,7 @@ struct _zend_generator {
* by-value foreach. */
zval values;
/* Node of waiting generators when multiple "yield *" expressions
/* Node of waiting generators when multiple "yield from" expressions
* are nested. */
zend_generator_node node;
@@ -91,6 +91,8 @@ struct _zend_generator {
/* ZEND_GENERATOR_* flags */
zend_uchar flags;
zval *gc_buffer;
};
static const zend_uchar ZEND_GENERATOR_CURRENTLY_RUNNING = 0x1;