diff --git a/Zend/tests/generators/iterator_wrapper_leak.phpt b/Zend/tests/generators/iterator_wrapper_leak.phpt new file mode 100644 index 0000000000000..bc0f34e9c8d4b --- /dev/null +++ b/Zend/tests/generators/iterator_wrapper_leak.phpt @@ -0,0 +1,33 @@ +--TEST-- +A generator iterator wrapper involved in a cycle should not leak +--FILE-- +gen1 = (function () { + yield 1; + yield 2; + yield 3; + })(); + $gen2 = function() { + foreach ($this->gen1 as $x) { + echo "$x\n"; + yield $x; + } + }; + $this->gen2 = $gen2(); + foreach ($this->gen2 as $x) { + if ($x == 2) { + break; + } + } + } +} +(new Test)->method(); +gc_collect_cycles(); + +?> +--EXPECT-- +1 +2 diff --git a/Zend/zend_generators.c b/Zend/zend_generators.c index b8730d282013d..fe7066c0c835f 100644 --- a/Zend/zend_generators.c +++ b/Zend/zend_generators.c @@ -1087,6 +1087,14 @@ static void zend_generator_iterator_rewind(zend_object_iterator *iterator) /* {{ } /* }}} */ +static HashTable *zend_generator_iterator_get_gc( + zend_object_iterator *iterator, zval **table, int *n) +{ + *table = &iterator->data; + *n = 1; + return NULL; +} + static const zend_object_iterator_funcs zend_generator_iterator_functions = { zend_generator_iterator_dtor, zend_generator_iterator_valid, @@ -1094,7 +1102,8 @@ static const zend_object_iterator_funcs zend_generator_iterator_functions = { zend_generator_iterator_get_key, zend_generator_iterator_move_forward, zend_generator_iterator_rewind, - NULL + NULL, + zend_generator_iterator_get_gc, }; zend_object_iterator *zend_generator_get_iterator(zend_class_entry *ce, zval *object, int by_ref) /* {{{ */ diff --git a/Zend/zend_interfaces.c b/Zend/zend_interfaces.c index a100176c367ad..68d0077795367 100644 --- a/Zend/zend_interfaces.c +++ b/Zend/zend_interfaces.c @@ -198,7 +198,8 @@ static const zend_object_iterator_funcs zend_interface_iterator_funcs_iterator = zend_user_it_get_current_key, zend_user_it_move_forward, zend_user_it_rewind, - zend_user_it_invalidate_current + zend_user_it_invalidate_current, + NULL, /* get_gc */ }; /* {{{ zend_user_it_get_iterator */ diff --git a/Zend/zend_iterators.c b/Zend/zend_iterators.c index 9823390a981cf..aa4391f2caa99 100644 --- a/Zend/zend_iterators.c +++ b/Zend/zend_iterators.c @@ -70,7 +70,11 @@ static void iter_wrapper_dtor(zend_object *object) } static HashTable *iter_wrapper_get_gc(zend_object *object, zval **table, int *n) { - /* TODO: We need a get_gc iterator handler */ + zend_object_iterator *iter = (zend_object_iterator*)object; + if (iter->funcs->get_gc) { + return iter->funcs->get_gc(iter, table, n); + } + *table = NULL; *n = 0; return NULL; diff --git a/Zend/zend_iterators.h b/Zend/zend_iterators.h index 941b260885f8e..bce98a24ac93c 100644 --- a/Zend/zend_iterators.h +++ b/Zend/zend_iterators.h @@ -50,6 +50,10 @@ typedef struct _zend_object_iterator_funcs { /* invalidate current value/key (optional, may be NULL) */ void (*invalidate_current)(zend_object_iterator *iter); + + /* Expose owned values to GC. + * This has the same semantics as the corresponding object handler. */ + HashTable *(*get_gc)(zend_object_iterator *iter, zval **table, int *n); } zend_object_iterator_funcs; struct _zend_object_iterator { diff --git a/Zend/zend_weakrefs.c b/Zend/zend_weakrefs.c index 5ad84193fa5fd..1f6f921ab77be 100644 --- a/Zend/zend_weakrefs.c +++ b/Zend/zend_weakrefs.c @@ -501,7 +501,8 @@ static const zend_object_iterator_funcs zend_weakmap_iterator_funcs = { zend_weakmap_iterator_get_current_key, zend_weakmap_iterator_move_forward, zend_weakmap_iterator_rewind, - NULL + NULL, + NULL, /* get_gc */ }; static zend_object_iterator *zend_weakmap_get_iterator( diff --git a/ext/com_dotnet/com_iterator.c b/ext/com_dotnet/com_iterator.c index 0272d48cd36cf..6126e958d5a4d 100644 --- a/ext/com_dotnet/com_iterator.c +++ b/ext/com_dotnet/com_iterator.c @@ -127,7 +127,8 @@ static const zend_object_iterator_funcs com_iter_funcs = { com_iter_get_data, com_iter_get_key, com_iter_move_forwards, - NULL + NULL, + NULL, /* get_gc */ }; zend_object_iterator *php_com_iter_get(zend_class_entry *ce, zval *object, int by_ref) diff --git a/ext/com_dotnet/com_saproxy.c b/ext/com_dotnet/com_saproxy.c index 2b42315151bfe..d86fb75e3ede4 100644 --- a/ext/com_dotnet/com_saproxy.c +++ b/ext/com_dotnet/com_saproxy.c @@ -512,7 +512,8 @@ static const zend_object_iterator_funcs saproxy_iter_funcs = { saproxy_iter_get_data, saproxy_iter_get_key, saproxy_iter_move_forwards, - NULL + NULL, + NULL, /* get_gc */ }; diff --git a/ext/date/php_date.c b/ext/date/php_date.c index 6813b60e624c8..bff52f178d393 100644 --- a/ext/date/php_date.c +++ b/ext/date/php_date.c @@ -1526,7 +1526,8 @@ static const zend_object_iterator_funcs date_period_it_funcs = { date_period_it_current_key, date_period_it_move_forward, date_period_it_rewind, - date_period_it_invalidate_current + date_period_it_invalidate_current, + NULL, /* get_gc */ }; zend_object_iterator *date_object_period_get_iterator(zend_class_entry *ce, zval *object, int by_ref) /* {{{ */ diff --git a/ext/dom/dom_iterators.c b/ext/dom/dom_iterators.c index c21cc92154bbb..f56d2490341ce 100644 --- a/ext/dom/dom_iterators.c +++ b/ext/dom/dom_iterators.c @@ -250,7 +250,8 @@ static const zend_object_iterator_funcs php_dom_iterator_funcs = { php_dom_iterator_current_key, php_dom_iterator_move_forward, NULL, - NULL + NULL, + NULL, /* get_gc */ }; zend_object_iterator *php_dom_get_iterator(zend_class_entry *ce, zval *object, int by_ref) /* {{{ */ diff --git a/ext/ffi/ffi.c b/ext/ffi/ffi.c index 946f6c9845299..c06597283b668 100644 --- a/ext/ffi/ffi.c +++ b/ext/ffi/ffi.c @@ -1874,7 +1874,8 @@ static const zend_object_iterator_funcs zend_ffi_cdata_it_funcs = { zend_ffi_cdata_it_get_current_key, zend_ffi_cdata_it_move_forward, zend_ffi_cdata_it_rewind, - NULL + NULL, + NULL, /* get_gc */ }; static zend_object_iterator *zend_ffi_cdata_get_iterator(zend_class_entry *ce, zval *object, int by_ref) /* {{{ */ diff --git a/ext/intl/resourcebundle/resourcebundle_iterator.c b/ext/intl/resourcebundle/resourcebundle_iterator.c index ab10daf82dd2b..517f01b837d89 100644 --- a/ext/intl/resourcebundle/resourcebundle_iterator.c +++ b/ext/intl/resourcebundle/resourcebundle_iterator.c @@ -140,7 +140,8 @@ static const zend_object_iterator_funcs resourcebundle_iterator_funcs = { resourcebundle_iterator_key, resourcebundle_iterator_step, resourcebundle_iterator_reset, - resourcebundle_iterator_invalidate + resourcebundle_iterator_invalidate, + NULL, /* get_gc */ }; /* }}} */ diff --git a/ext/mysqli/mysqli_result_iterator.c b/ext/mysqli/mysqli_result_iterator.c index df36a840e41fb..1388937816173 100644 --- a/ext/mysqli/mysqli_result_iterator.c +++ b/ext/mysqli/mysqli_result_iterator.c @@ -150,6 +150,7 @@ const zend_object_iterator_funcs php_mysqli_result_iterator_funcs = { php_mysqli_result_iterator_current_key, php_mysqli_result_iterator_move_forward, php_mysqli_result_iterator_rewind, - NULL + NULL, + NULL, /* get_gc */ }; /* }}} */ diff --git a/ext/pdo/pdo_stmt.c b/ext/pdo/pdo_stmt.c index d36788414800f..5f0f4c74e7ed0 100644 --- a/ext/pdo/pdo_stmt.c +++ b/ext/pdo/pdo_stmt.c @@ -2304,7 +2304,8 @@ static const zend_object_iterator_funcs pdo_stmt_iter_funcs = { pdo_stmt_iter_get_key, pdo_stmt_iter_move_forwards, NULL, - NULL + NULL, + NULL, /* get_gc */ }; zend_object_iterator *pdo_stmt_iter_get(zend_class_entry *ce, zval *object, int by_ref) diff --git a/ext/simplexml/simplexml.c b/ext/simplexml/simplexml.c index d39286757561f..6a2e2b95c2252 100644 --- a/ext/simplexml/simplexml.c +++ b/ext/simplexml/simplexml.c @@ -2470,7 +2470,8 @@ static const zend_object_iterator_funcs php_sxe_iterator_funcs = { /* {{{ */ php_sxe_iterator_current_key, php_sxe_iterator_move_forward, php_sxe_iterator_rewind, - NULL + NULL, + NULL, /* get_gc */ }; /* }}} */ diff --git a/ext/spl/spl_array.c b/ext/spl/spl_array.c index 86a1ad501c288..676d04ab93f7e 100644 --- a/ext/spl/spl_array.c +++ b/ext/spl/spl_array.c @@ -1154,7 +1154,8 @@ static const zend_object_iterator_funcs spl_array_it_funcs = { spl_array_it_get_current_key, spl_array_it_move_forward, spl_array_it_rewind, - NULL + NULL, + NULL, /* get_gc */ }; zend_object_iterator *spl_array_get_iterator(zend_class_entry *ce, zval *object, int by_ref) /* {{{ */ diff --git a/ext/spl/spl_directory.c b/ext/spl/spl_directory.c index fa33c022b0429..28a755a372dea 100644 --- a/ext/spl/spl_directory.c +++ b/ext/spl/spl_directory.c @@ -1628,7 +1628,8 @@ static const zend_object_iterator_funcs spl_filesystem_dir_it_funcs = { spl_filesystem_dir_it_current_key, spl_filesystem_dir_it_move_forward, spl_filesystem_dir_it_rewind, - NULL + NULL, + NULL, /* get_gc */ }; /* }}} */ @@ -1828,7 +1829,8 @@ static const zend_object_iterator_funcs spl_filesystem_tree_it_funcs = { spl_filesystem_tree_it_current_key, spl_filesystem_tree_it_move_forward, spl_filesystem_tree_it_rewind, - NULL + NULL, + NULL, /* get_gc */ }; /* }}} */ diff --git a/ext/spl/spl_dllist.c b/ext/spl/spl_dllist.c index adf3d354cccd8..9479b9056acab 100644 --- a/ext/spl/spl_dllist.c +++ b/ext/spl/spl_dllist.c @@ -1334,7 +1334,8 @@ static const zend_object_iterator_funcs spl_dllist_it_funcs = { spl_dllist_it_get_current_key, spl_dllist_it_move_forward, spl_dllist_it_rewind, - NULL + NULL, + NULL, /* get_gc */ }; /* }}} */ zend_object_iterator *spl_dllist_get_iterator(zend_class_entry *ce, zval *object, int by_ref) /* {{{ */ diff --git a/ext/spl/spl_fixedarray.c b/ext/spl/spl_fixedarray.c index 63a3ef78fa286..46536274b90df 100644 --- a/ext/spl/spl_fixedarray.c +++ b/ext/spl/spl_fixedarray.c @@ -964,7 +964,8 @@ static const zend_object_iterator_funcs spl_fixedarray_it_funcs = { spl_fixedarray_it_get_current_key, spl_fixedarray_it_move_forward, spl_fixedarray_it_rewind, - NULL + NULL, + NULL, /* get_gc */ }; zend_object_iterator *spl_fixedarray_get_iterator(zend_class_entry *ce, zval *object, int by_ref) /* {{{ */ diff --git a/ext/spl/spl_heap.c b/ext/spl/spl_heap.c index 0a694f6e7882b..3e8a73d021f2d 100644 --- a/ext/spl/spl_heap.c +++ b/ext/spl/spl_heap.c @@ -1068,7 +1068,8 @@ static const zend_object_iterator_funcs spl_heap_it_funcs = { spl_heap_it_get_current_key, spl_heap_it_move_forward, spl_heap_it_rewind, - NULL + NULL, + NULL, /* get_gc */ }; static const zend_object_iterator_funcs spl_pqueue_it_funcs = { @@ -1078,7 +1079,8 @@ static const zend_object_iterator_funcs spl_pqueue_it_funcs = { spl_heap_it_get_current_key, spl_heap_it_move_forward, spl_heap_it_rewind, - NULL + NULL, + NULL, /* get_gc */ }; zend_object_iterator *spl_heap_get_iterator(zend_class_entry *ce, zval *object, int by_ref) /* {{{ */ diff --git a/ext/spl/spl_iterators.c b/ext/spl/spl_iterators.c index 99e130e5d2ff8..dfac8a48dfa9e 100644 --- a/ext/spl/spl_iterators.c +++ b/ext/spl/spl_iterators.c @@ -438,7 +438,8 @@ static const zend_object_iterator_funcs spl_recursive_it_iterator_funcs = { spl_recursive_it_get_current_key, spl_recursive_it_move_forward, spl_recursive_it_rewind, - NULL + NULL, + NULL, /* get_gc */ }; static zend_object_iterator *spl_recursive_it_get_iterator(zend_class_entry *ce, zval *zobject, int by_ref)