diff options
-rw-r--r-- | NEWS | 1 | ||||
-rw-r--r-- | Zend/tests/bug69989_3.phpt | 44 | ||||
-rw-r--r-- | Zend/zend_generators.c | 45 | ||||
-rw-r--r-- | Zend/zend_generators.h | 1 |
4 files changed, 86 insertions, 5 deletions
@@ -4,6 +4,7 @@ PHP NEWS - Core: . Fixed bug #62210 (Exceptions can leak temporary variables). (Dmitry, Bob) + . Fixed bug #69989 (Generators don't participate in cycle GC). (Nikita) . Implemented the RFC `Support Class Constant Visibility`. (Sean DuBois, Reeze Xia, Dmitry) . Added void return type. (Andrea) diff --git a/Zend/tests/bug69989_3.phpt b/Zend/tests/bug69989_3.phpt new file mode 100644 index 0000000000..260819197b --- /dev/null +++ b/Zend/tests/bug69989_3.phpt @@ -0,0 +1,44 @@ +--TEST-- +Generator cycle collection edge cases +--FILE-- +<?php + +// Extra args +function gen1() { + yield; +} +$obj = new stdClass; +$obj->gen = gen1($obj); + +// Symtable +function gen2() { + $varName = 'a'; + $$varName = yield; + yield; +} +$gen = gen2(); +$gen->send($gen); + +// Symtable indirect +function gen3() { + $varName = 'a'; + $$varName = 42; + $var = yield; + yield; +} +$gen = gen3(); +$gen->send($gen); + +// Yield from root +function gen4() { + yield from yield; +} +$gen = gen4(); +$gen2 = gen4($gen); +$gen2->send([1, 2, 3]); +$gen->send($gen2); + +?> +===DONE=== +--EXPECT-- +===DONE=== diff --git a/Zend/zend_generators.c b/Zend/zend_generators.c index 17e58b9a7b..1c798cc9b4 100644 --- a/Zend/zend_generators.c +++ b/Zend/zend_generators.c @@ -201,9 +201,25 @@ static uint32_t calc_gc_buffer_size(zend_generator *generator) /* {{{ */ zend_execute_data *execute_data = generator->execute_data; zend_op_array *op_array = &EX(func)->op_array; - size += op_array->last_var; /* CVs */ + /* Compiled variables */ + if (!execute_data->symbol_table) { + size += op_array->last_var; + } + /* Extra args */ + if (EX_CALL_INFO() & ZEND_CALL_FREE_EXTRA_ARGS) { + size += EX_NUM_ARGS() - op_array->num_args; + } size += Z_OBJ(execute_data->This) != NULL; /* $this */ size += (EX_CALL_INFO() & ZEND_CALL_CLOSURE) != 0; /* Closure object */ + + /* Yield from root references */ + if (generator->node.children == 0) { + zend_generator *root = generator->node.ptr.root; + while (root != generator) { + size++; + root = zend_generator_get_child(&root->node, generator); + } + } } return size; } @@ -213,6 +229,7 @@ static HashTable *zend_generator_get_gc(zval *object, zval **table, int *n) /* { { zend_generator *generator = (zend_generator*) Z_OBJ_P(object); zend_execute_data *execute_data = generator->execute_data; + zend_op_array *op_array; zval *gc_buffer; uint32_t gc_buffer_size; @@ -224,9 +241,11 @@ static HashTable *zend_generator_get_gc(zval *object, zval **table, int *n) /* { return NULL; } + op_array = &EX(func)->op_array; gc_buffer_size = calc_gc_buffer_size(generator); - if (!generator->gc_buffer) { - generator->gc_buffer = safe_emalloc(sizeof(zval), gc_buffer_size, 0); + if (generator->gc_buffer_size < gc_buffer_size) { + generator->gc_buffer = safe_erealloc(generator->gc_buffer, sizeof(zval), gc_buffer_size, 0); + generator->gc_buffer_size = gc_buffer_size; } *n = gc_buffer_size; @@ -237,13 +256,21 @@ static HashTable *zend_generator_get_gc(zval *object, zval **table, int *n) /* { ZVAL_COPY_VALUE(gc_buffer++, &generator->retval); ZVAL_COPY_VALUE(gc_buffer++, &generator->values); - { + if (!execute_data->symbol_table) { 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 (EX_CALL_INFO() & ZEND_CALL_FREE_EXTRA_ARGS) { + zval *zv = EX_VAR_NUM(op_array->last_var + op_array->T); + zval *end = zv + (EX_NUM_ARGS() - op_array->num_args); + while (zv != end) { + ZVAL_COPY_VALUE(gc_buffer++, zv++); + } + } + if (Z_OBJ(execute_data->This)) { ZVAL_OBJ(gc_buffer++, Z_OBJ(execute_data->This)); } @@ -251,7 +278,15 @@ static HashTable *zend_generator_get_gc(zval *object, zval **table, int *n) /* { ZVAL_OBJ(gc_buffer++, (zend_object *) EX(func)->common.prototype); } - return NULL; + if (generator->node.children == 0) { + zend_generator *root = generator->node.ptr.root; + while (root != generator) { + ZVAL_OBJ(gc_buffer++, &root->std); + root = zend_generator_get_child(&root->node, generator); + } + } + + return execute_data->symbol_table; } /* }}} */ diff --git a/Zend/zend_generators.h b/Zend/zend_generators.h index 89f0e3c2fb..95c5147a93 100644 --- a/Zend/zend_generators.h +++ b/Zend/zend_generators.h @@ -93,6 +93,7 @@ struct _zend_generator { zend_uchar flags; zval *gc_buffer; + uint32_t gc_buffer_size; }; static const zend_uchar ZEND_GENERATOR_CURRENTLY_RUNNING = 0x1; |