Browse Source

Fix bug #69989

This should cover all the basic cycles. Anything further would
require scanning the call stack and live temporaries.
pull/1761/merge
Nikita Popov 10 years ago
parent
commit
c0c73f7083
  1. 1
      NEWS
  2. 44
      Zend/tests/bug69989_3.phpt
  3. 45
      Zend/zend_generators.c
  4. 1
      Zend/zend_generators.h

1
NEWS

@ -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)

44
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===

45
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;
}
/* }}} */

1
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;

Loading…
Cancel
Save