summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorNikita Popov <nikic@php.net>2016-02-12 18:50:19 +0100
committerNikita Popov <nikic@php.net>2016-02-12 18:52:53 +0100
commitc0c73f708394c0c7ee4a3731dc29c87b83f786a4 (patch)
tree050682bd9c1366f79d03a6a3a32761ece5a0d1ce
parentd1b777bd992525ced913a033bd7fff9490614f52 (diff)
downloadphp-git-c0c73f708394c0c7ee4a3731dc29c87b83f786a4.tar.gz
Fix bug #69989
This should cover all the basic cycles. Anything further would require scanning the call stack and live temporaries.
-rw-r--r--NEWS1
-rw-r--r--Zend/tests/bug69989_3.phpt44
-rw-r--r--Zend/zend_generators.c45
-rw-r--r--Zend/zend_generators.h1
4 files changed, 86 insertions, 5 deletions
diff --git a/NEWS b/NEWS
index 0a598cf168..0ae66b8f34 100644
--- a/NEWS
+++ b/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)
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;