Skip to content

Commit 83e2b9e

Browse files
committed
Fixed bug #76946
1 parent 69c1b61 commit 83e2b9e

File tree

3 files changed

+65
-0
lines changed

3 files changed

+65
-0
lines changed

NEWS

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,8 @@ PHP NEWS
22
|||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
33
?? ??? 2018, PHP 7.1.24
44

5+
- Core:
6+
. Fixed bug #76946 (Cyclic reference in generator not detected). (Nikita)
57

68
11 Oct 2018, PHP 7.1.23
79

Zend/tests/bug76946.phpt

Lines changed: 27 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,27 @@
1+
--TEST--
2+
Bug #76946: Cyclic reference in generator not detected
3+
--FILE--
4+
<?php
5+
6+
function gen() {
7+
$gen = yield;
8+
foreach ([1, $gen] as $v) {
9+
yield $v;
10+
}
11+
}
12+
13+
function gen2() {
14+
$gen = yield;
15+
$gen + yield;
16+
}
17+
18+
$gen = gen();
19+
$gen->send($gen);
20+
21+
$gen2 = gen2();
22+
$gen2->send($gen2);
23+
24+
?>
25+
===DONE===
26+
--EXPECT--
27+
===DONE===

Zend/zend_generators.c

Lines changed: 36 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -274,6 +274,25 @@ static uint32_t calc_gc_buffer_size(zend_generator *generator) /* {{{ */
274274
size += Z_TYPE(execute_data->This) == IS_OBJECT; /* $this */
275275
size += (EX_CALL_INFO() & ZEND_CALL_CLOSURE) != 0; /* Closure object */
276276

277+
/* Live vars */
278+
if (execute_data->opline != op_array->opcodes) {
279+
/* -1 required because we want the last run opcode, not the next to-be-run one. */
280+
uint32_t i, op_num = execute_data->opline - op_array->opcodes - 1;
281+
for (i = 0; i < op_array->last_live_range; i++) {
282+
const zend_live_range *range = &op_array->live_range[i];
283+
if (range->start > op_num) {
284+
/* Further ranges will not be relevant... */
285+
break;
286+
} else if (op_num < range->end) {
287+
/* LIVE_ROPE and LIVE_SILENCE not relevant for GC */
288+
uint32_t kind = range->var & ZEND_LIVE_MASK;
289+
if (kind == ZEND_LIVE_TMPVAR || kind == ZEND_LIVE_LOOP) {
290+
size++;
291+
}
292+
}
293+
}
294+
}
295+
277296
/* Yield from root references */
278297
if (generator->node.children == 0) {
279298
zend_generator *root = generator->node.ptr.root;
@@ -340,6 +359,23 @@ static HashTable *zend_generator_get_gc(zval *object, zval **table, int *n) /* {
340359
ZVAL_OBJ(gc_buffer++, (zend_object *) EX(func)->common.prototype);
341360
}
342361

362+
if (execute_data->opline != op_array->opcodes) {
363+
uint32_t i, op_num = execute_data->opline - op_array->opcodes - 1;
364+
for (i = 0; i < op_array->last_live_range; i++) {
365+
const zend_live_range *range = &op_array->live_range[i];
366+
if (range->start > op_num) {
367+
break;
368+
} else if (op_num < range->end) {
369+
uint32_t kind = range->var & ZEND_LIVE_MASK;
370+
uint32_t var_num = range->var & ~ZEND_LIVE_MASK;
371+
zval *var = EX_VAR(var_num);
372+
if (kind == ZEND_LIVE_TMPVAR || kind == ZEND_LIVE_LOOP) {
373+
ZVAL_COPY_VALUE(gc_buffer++, var);
374+
}
375+
}
376+
}
377+
}
378+
343379
if (generator->node.children == 0) {
344380
zend_generator *root = generator->node.ptr.root;
345381
while (root != generator) {

0 commit comments

Comments
 (0)