Skip to content

Commit cb9000f

Browse files
committed
Merge branch 'master' of git.php.net:/php-src
2 parents 52611a2 + 5087382 commit cb9000f

File tree

6 files changed

+332
-197
lines changed

6 files changed

+332
-197
lines changed

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
@@ -2769,7 +2769,7 @@ void zend_do_yield(znode *result, znode *value, const znode *key, zend_bool is_v
27692769
SET_UNUSED(opline->op2);
27702770
}
27712771

2772-
opline->result_type = IS_TMP_VAR;
2772+
opline->result_type = IS_VAR;
27732773
opline->result.var = get_temporary_variable(CG(active_op_array));
27742774
GET_NODE(result, opline->result);
27752775
}

Zend/zend_generators.c

Lines changed: 79 additions & 65 deletions
Original file line numberDiff line numberDiff line change
@@ -29,6 +29,73 @@ static zend_object_handlers zend_generator_handlers;
2929

3030
static zend_object_value zend_generator_create(zend_class_entry *class_type TSRMLS_DC);
3131

32+
static void zend_generator_cleanup_unfinished_execution(zend_generator *generator TSRMLS_DC) /* {{{ */
33+
{
34+
zend_execute_data *execute_data = generator->execute_data;
35+
zend_op_array *op_array = execute_data->op_array;
36+
37+
if (generator->send_target) {
38+
Z_DELREF_PP(generator->send_target);
39+
generator->send_target = NULL;
40+
}
41+
42+
/* Manually free loop variables, as execution couldn't reach their
43+
* SWITCH_FREE / FREE opcodes. */
44+
{
45+
/* -1 required because we want the last run opcode, not the
46+
* next to-be-run one. */
47+
zend_uint op_num = execute_data->opline - op_array->opcodes - 1;
48+
49+
int i;
50+
for (i = 0; i < op_array->last_brk_cont; ++i) {
51+
zend_brk_cont_element *brk_cont = op_array->brk_cont_array + i;
52+
53+
if (brk_cont->start < 0) {
54+
continue;
55+
} else if (brk_cont->start > op_num) {
56+
break;
57+
} else if (brk_cont->brk > op_num) {
58+
zend_op *brk_opline = op_array->opcodes + brk_cont->brk;
59+
60+
switch (brk_opline->opcode) {
61+
case ZEND_SWITCH_FREE:
62+
{
63+
temp_variable *var = EX_TMP_VAR(execute_data, brk_opline->op1.var);
64+
zval_ptr_dtor(&var->var.ptr);
65+
}
66+
break;
67+
case ZEND_FREE:
68+
{
69+
temp_variable *var = EX_TMP_VAR(execute_data, brk_opline->op1.var);
70+
zval_dtor(&var->tmp_var);
71+
}
72+
break;
73+
}
74+
}
75+
}
76+
}
77+
78+
/* Clear any backed up stack arguments */
79+
{
80+
void **ptr = generator->stack->top - 1;
81+
void **end = zend_vm_stack_frame_base(execute_data);
82+
83+
for (; ptr >= end; --ptr) {
84+
zval_ptr_dtor((zval **) ptr);
85+
}
86+
}
87+
88+
/* If yield was used as a function argument there may be active
89+
* method calls those objects need to be freed */
90+
while (execute_data->call >= execute_data->call_slots) {
91+
if (execute_data->call->object) {
92+
zval_ptr_dtor(&execute_data->call->object);
93+
}
94+
execute_data->call--;
95+
}
96+
}
97+
/* }}} */
98+
3299
ZEND_API void zend_generator_close(zend_generator *generator, zend_bool finished_execution TSRMLS_DC) /* {{{ */
33100
{
34101
if (generator->value) {
@@ -61,65 +128,6 @@ ZEND_API void zend_generator_close(zend_generator *generator, zend_bool finished
61128
return;
62129
}
63130

64-
/* If the generator is closed before it can finish execution (reach
65-
* a return statement) we have to free loop variables manually, as
66-
* we don't know whether the SWITCH_FREE / FREE opcodes have run */
67-
if (!finished_execution) {
68-
/* -1 required because we want the last run opcode, not the
69-
* next to-be-run one. */
70-
zend_uint op_num = execute_data->opline - op_array->opcodes - 1;
71-
72-
int i;
73-
for (i = 0; i < op_array->last_brk_cont; ++i) {
74-
zend_brk_cont_element *brk_cont = op_array->brk_cont_array + i;
75-
76-
if (brk_cont->start < 0) {
77-
continue;
78-
} else if (brk_cont->start > op_num) {
79-
break;
80-
} else if (brk_cont->brk > op_num) {
81-
zend_op *brk_opline = op_array->opcodes + brk_cont->brk;
82-
83-
switch (brk_opline->opcode) {
84-
case ZEND_SWITCH_FREE:
85-
{
86-
temp_variable *var = EX_TMP_VAR(execute_data, brk_opline->op1.var);
87-
zval_ptr_dtor(&var->var.ptr);
88-
}
89-
break;
90-
case ZEND_FREE:
91-
{
92-
temp_variable *var = EX_TMP_VAR(execute_data, brk_opline->op1.var);
93-
zval_dtor(&var->tmp_var);
94-
}
95-
break;
96-
}
97-
}
98-
}
99-
}
100-
101-
/* Clear any backed up stack arguments */
102-
if (generator->stack != EG(argument_stack)) {
103-
void **ptr = generator->stack->top - 1;
104-
void **end = zend_vm_stack_frame_base(execute_data);
105-
106-
/* If the top stack element is the argument count, skip it */
107-
if (execute_data->function_state.arguments) {
108-
ptr--;
109-
}
110-
111-
for (; ptr >= end; --ptr) {
112-
zval_ptr_dtor((zval**) ptr);
113-
}
114-
}
115-
116-
while (execute_data->call >= execute_data->call_slots) {
117-
if (execute_data->call->object) {
118-
zval_ptr_dtor(&execute_data->call->object);
119-
}
120-
execute_data->call--;
121-
}
122-
123131
/* We have added an additional stack frame in prev_execute_data, so we
124132
* have to free it. It also contains the arguments passed to the
125133
* generator (for func_get_args) so those have to be freed too. */
@@ -138,17 +146,19 @@ ZEND_API void zend_generator_close(zend_generator *generator, zend_bool finished
138146
}
139147
}
140148

149+
/* Some cleanups are only necessary if the generator was closued
150+
* before it could finish execution (reach a return statement). */
151+
if (!finished_execution) {
152+
zend_generator_cleanup_unfinished_execution(generator TSRMLS_CC);
153+
}
154+
141155
/* Free a clone of closure */
142156
if (op_array->fn_flags & ZEND_ACC_CLOSURE) {
143157
destroy_op_array(op_array TSRMLS_CC);
144158
efree(op_array);
145159
}
146160

147161
efree(generator->stack);
148-
if (generator->stack == EG(argument_stack)) {
149-
/* abnormal exit for running generator */
150-
EG(argument_stack) = NULL;
151-
}
152162
generator->execute_data = NULL;
153163
}
154164
}
@@ -519,8 +529,12 @@ ZEND_METHOD(Generator, send)
519529
return;
520530
}
521531

522-
/* Put sent value into the TMP_VAR slot */
523-
MAKE_COPY_ZVAL(&value, &generator->send_target->tmp_var);
532+
/* Put sent value in the target VAR slot, if it is used */
533+
if (generator->send_target) {
534+
Z_DELREF_PP(generator->send_target);
535+
Z_ADDREF_P(value);
536+
*generator->send_target = value;
537+
}
524538

525539
zend_generator_resume(generator TSRMLS_CC);
526540

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
@@ -5382,11 +5382,15 @@ ZEND_VM_HANDLER(160, ZEND_YIELD, CONST|TMP|VAR|CV|UNUSED, CONST|TMP|VAR|CV|UNUSE
53825382
ZVAL_LONG(generator->key, generator->largest_used_integer_key);
53835383
}
53845384

5385-
/* If a value is sent it should go into the result var */
5386-
generator->send_target = &EX_T(opline->result.var);
5387-
5388-
/* Initialize the sent value to NULL */
5389-
EX_T(opline->result.var).tmp_var = EG(uninitialized_zval);
5385+
if (RETURN_VALUE_USED(opline)) {
5386+
/* If the return value of yield is used set the send
5387+
* target and initialize it to NULL */
5388+
generator->send_target = &EX_T(opline->result.var).var.ptr;
5389+
Z_ADDREF(EG(uninitialized_zval));
5390+
EX_T(opline->result.var).var.ptr = &EG(uninitialized_zval);
5391+
} else {
5392+
generator->send_target = NULL;
5393+
}
53905394

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

0 commit comments

Comments
 (0)