@@ -29,6 +29,73 @@ static zend_object_handlers zend_generator_handlers;
29
29
30
30
static zend_object_value zend_generator_create (zend_class_entry * class_type TSRMLS_DC );
31
31
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
+
32
99
ZEND_API void zend_generator_close (zend_generator * generator , zend_bool finished_execution TSRMLS_DC ) /* {{{ */
33
100
{
34
101
if (generator -> value ) {
@@ -61,65 +128,6 @@ ZEND_API void zend_generator_close(zend_generator *generator, zend_bool finished
61
128
return ;
62
129
}
63
130
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
-
123
131
/* We have added an additional stack frame in prev_execute_data, so we
124
132
* have to free it. It also contains the arguments passed to the
125
133
* 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
138
146
}
139
147
}
140
148
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
+
141
155
/* Free a clone of closure */
142
156
if (op_array -> fn_flags & ZEND_ACC_CLOSURE ) {
143
157
destroy_op_array (op_array TSRMLS_CC );
144
158
efree (op_array );
145
159
}
146
160
147
161
efree (generator -> stack );
148
- if (generator -> stack == EG (argument_stack )) {
149
- /* abnormal exit for running generator */
150
- EG (argument_stack ) = NULL ;
151
- }
152
162
generator -> execute_data = NULL ;
153
163
}
154
164
}
@@ -519,8 +529,12 @@ ZEND_METHOD(Generator, send)
519
529
return ;
520
530
}
521
531
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
+ }
524
538
525
539
zend_generator_resume (generator TSRMLS_CC );
526
540
0 commit comments