}
/* }}} */
+static zend_generator *zend_generator_get_child(zend_generator_node *node, zend_generator *leaf);
+
static void zend_generator_free_storage(zend_object *object) /* {{{ */
{
zend_generator *generator = (zend_generator*) object;
if (generator->iterator) {
zend_iterator_dtor(generator->iterator);
}
+
+ if (generator->node.children == 0) {
+ zend_generator *root = generator->node.ptr.root, *next;
+ while (root != generator) {
+ next = zend_generator_get_child(&root->node, generator);
+ OBJ_RELEASE(&root->std);
+ root = next;
+ }
+ }
}
/* }}} */
}
}
+/* Make attention so that the root of each subtree of the Generators tree is referenced once per leaf */
static void zend_generator_add_child(zend_generator *generator, zend_generator *child)
{
zend_generator *leaf = child->node.children ? child->node.ptr.leaf : child;
if (was_leaf) {
zend_generator *next = generator->node.parent;
leaf->node.ptr.root = generator->node.ptr.root;
+ ++GC_REFCOUNT(&generator->std); /* we need to increment the generator refcount here as it became integrated into the tree (no leaf), but we must not increment the refcount of the *whole* path in tree */
generator->node.ptr.leaf = leaf;
while (next) {
zend_generator_add_child(from, this);
this->node.parent = from;
+ zend_generator_get_current(this);
+ --GC_REFCOUNT(from);
}
ZEND_API zend_generator *zend_generator_get_current(zend_generator *generator)
{
zend_generator *leaf;
- zend_generator *root;
+ zend_generator *root, *old_root;
if (generator->node.parent == NULL) {
/* we're not in yield from mode */
return root;
}
+ /* generator at the root had stopped */
+ if (root != generator) {
+ old_root = root;
+ root = zend_generator_get_child(&root->node, leaf);
+ } else {
+ old_root = NULL;
+ }
+
while (!root->execute_data && root != generator) {
- /* generator at the root had stopped */
+ OBJ_RELEASE(&old_root->std);
+ old_root = root;
+
root = zend_generator_get_child(&root->node, leaf);
}
} else {
do {
root = root->node.parent;
+ ++GC_REFCOUNT(&root->std);
} while (root->node.parent);
}
}
+ if (old_root) {
+ OBJ_RELEASE(&old_root->std);
+ }
+
return leaf->node.ptr.root = root;
}
* 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)) {
- zend_generator_close(generator, 0);
-
if (generator == orig_generator) {
+ zend_generator_close(generator, 0);
zend_throw_exception_internal(NULL);
} else {
generator = zend_generator_get_current(orig_generator);