Skip to content

Commit 9589cae

Browse files
committed
Fixed bug #66041: list() fails to unpack yielded ArrayAccess object
Yield return values now use IS_VAR rather than IS_TMP_VAR. This fixes the issue with list() and should also be faster as it avoids doing a zval copy.
1 parent 5f09944 commit 9589cae

File tree

7 files changed

+266
-134
lines changed

7 files changed

+266
-134
lines changed

NEWS

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,8 @@ PHP NEWS
44

55
- Core:
66
. Added validation of class names in the autoload process. (Dmitry)
7+
. Fixed buf #66041 (list() fails to unpack yielded ArrayAccess object).
8+
(Nikita)
79

810
- Date:
911
. Fixed bug #66060 (Heap buffer over-read in DateInterval). (Remi)

Zend/tests/generators/bug66041.phpt

Lines changed: 17 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,17 @@
1+
--TEST--
2+
Bug #66041: list() fails to unpack yielded ArrayAccess object
3+
--FILE--
4+
<?php
5+
function dumpElement() {
6+
list($value) = yield;
7+
var_dump($value);
8+
};
9+
10+
$fixedArray = new SplFixedArray(1);
11+
$fixedArray[0] = 'the element';
12+
13+
$generator = dumpElement();
14+
$generator->send($fixedArray);
15+
?>
16+
--EXPECT--
17+
string(11) "the element"

Zend/zend_compile.c

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -2782,7 +2782,7 @@ void zend_do_yield(znode *result, znode *value, const znode *key, zend_bool is_v
27822782
SET_UNUSED(opline->op2);
27832783
}
27842784

2785-
opline->result_type = IS_TMP_VAR;
2785+
opline->result_type = IS_VAR;
27862786
opline->result.var = get_temporary_variable(CG(active_op_array));
27872787
GET_NODE(result, opline->result);
27882788
}

Zend/zend_generators.c

Lines changed: 11 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -55,6 +55,11 @@ ZEND_API void zend_generator_close(zend_generator *generator, zend_bool finished
5555
zval_ptr_dtor(&execute_data->current_this);
5656
}
5757

58+
if (!finished_execution && generator->send_target) {
59+
Z_DELREF_PP(generator->send_target);
60+
generator->send_target = NULL;
61+
}
62+
5863
/* A fatal error / die occurred during the generator execution. Trying to clean
5964
* up the stack may not be safe in this case. */
6065
if (CG(unclean_shutdown)) {
@@ -519,8 +524,12 @@ ZEND_METHOD(Generator, send)
519524
return;
520525
}
521526

522-
/* Put sent value into the TMP_VAR slot */
523-
MAKE_COPY_ZVAL(&value, &generator->send_target->tmp_var);
527+
/* Put sent value in the target VAR slot, if it is used */
528+
if (generator->send_target) {
529+
Z_DELREF_PP(generator->send_target);
530+
Z_ADDREF_P(value);
531+
*generator->send_target = value;
532+
}
524533

525534
zend_generator_resume(generator TSRMLS_CC);
526535

Zend/zend_generators.h

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -49,7 +49,7 @@ typedef struct _zend_generator {
4949
/* Current key */
5050
zval *key;
5151
/* Variable to put sent value into */
52-
temp_variable *send_target;
52+
zval **send_target;
5353
/* Largest used integer key for auto-incrementing keys */
5454
long largest_used_integer_key;
5555

Zend/zend_vm_def.h

Lines changed: 9 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -5353,11 +5353,15 @@ ZEND_VM_HANDLER(160, ZEND_YIELD, CONST|TMP|VAR|CV|UNUSED, CONST|TMP|VAR|CV|UNUSE
53535353
ZVAL_LONG(generator->key, generator->largest_used_integer_key);
53545354
}
53555355

5356-
/* If a value is sent it should go into the result var */
5357-
generator->send_target = &EX_T(opline->result.var);
5358-
5359-
/* Initialize the sent value to NULL */
5360-
EX_T(opline->result.var).tmp_var = EG(uninitialized_zval);
5356+
if (RETURN_VALUE_USED(opline)) {
5357+
/* If the return value of yield is used set the send
5358+
* target and initialize it to NULL */
5359+
generator->send_target = &EX_T(opline->result.var).var.ptr;
5360+
Z_ADDREF(EG(uninitialized_zval));
5361+
EX_T(opline->result.var).var.ptr = &EG(uninitialized_zval);
5362+
} else {
5363+
generator->send_target = NULL;
5364+
}
53615365

53625366
/* We increment to the next op, so we are at the correct position when the
53635367
* generator is resumed. */

0 commit comments

Comments
 (0)