@@ -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 ) {
@@ -55,76 +122,12 @@ ZEND_API void zend_generator_close(zend_generator *generator, zend_bool finished
55
122
zval_ptr_dtor (& execute_data -> current_this );
56
123
}
57
124
58
- if (!finished_execution && generator -> send_target ) {
59
- Z_DELREF_PP (generator -> send_target );
60
- generator -> send_target = NULL ;
61
- }
62
-
63
125
/* A fatal error / die occurred during the generator execution. Trying to clean
64
126
* up the stack may not be safe in this case. */
65
127
if (CG (unclean_shutdown )) {
66
128
return ;
67
129
}
68
130
69
- /* If the generator is closed before it can finish execution (reach
70
- * a return statement) we have to free loop variables manually, as
71
- * we don't know whether the SWITCH_FREE / FREE opcodes have run */
72
- if (!finished_execution ) {
73
- /* -1 required because we want the last run opcode, not the
74
- * next to-be-run one. */
75
- zend_uint op_num = execute_data -> opline - op_array -> opcodes - 1 ;
76
-
77
- int i ;
78
- for (i = 0 ; i < op_array -> last_brk_cont ; ++ i ) {
79
- zend_brk_cont_element * brk_cont = op_array -> brk_cont_array + i ;
80
-
81
- if (brk_cont -> start < 0 ) {
82
- continue ;
83
- } else if (brk_cont -> start > op_num ) {
84
- break ;
85
- } else if (brk_cont -> brk > op_num ) {
86
- zend_op * brk_opline = op_array -> opcodes + brk_cont -> brk ;
87
-
88
- switch (brk_opline -> opcode ) {
89
- case ZEND_SWITCH_FREE :
90
- {
91
- temp_variable * var = EX_TMP_VAR (execute_data , brk_opline -> op1 .var );
92
- zval_ptr_dtor (& var -> var .ptr );
93
- }
94
- break ;
95
- case ZEND_FREE :
96
- {
97
- temp_variable * var = EX_TMP_VAR (execute_data , brk_opline -> op1 .var );
98
- zval_dtor (& var -> tmp_var );
99
- }
100
- break ;
101
- }
102
- }
103
- }
104
- }
105
-
106
- /* Clear any backed up stack arguments */
107
- if (generator -> stack != EG (argument_stack )) {
108
- void * * ptr = generator -> stack -> top - 1 ;
109
- void * * end = zend_vm_stack_frame_base (execute_data );
110
-
111
- /* If the top stack element is the argument count, skip it */
112
- if (execute_data -> function_state .arguments ) {
113
- ptr -- ;
114
- }
115
-
116
- for (; ptr >= end ; -- ptr ) {
117
- zval_ptr_dtor ((zval * * ) ptr );
118
- }
119
- }
120
-
121
- while (execute_data -> call >= execute_data -> call_slots ) {
122
- if (execute_data -> call -> object ) {
123
- zval_ptr_dtor (& execute_data -> call -> object );
124
- }
125
- execute_data -> call -- ;
126
- }
127
-
128
131
/* We have added an additional stack frame in prev_execute_data, so we
129
132
* have to free it. It also contains the arguments passed to the
130
133
* generator (for func_get_args) so those have to be freed too. */
@@ -143,17 +146,19 @@ ZEND_API void zend_generator_close(zend_generator *generator, zend_bool finished
143
146
}
144
147
}
145
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
+
146
155
/* Free a clone of closure */
147
156
if (op_array -> fn_flags & ZEND_ACC_CLOSURE ) {
148
157
destroy_op_array (op_array TSRMLS_CC );
149
158
efree (op_array );
150
159
}
151
160
152
161
efree (generator -> stack );
153
- if (generator -> stack == EG (argument_stack )) {
154
- /* abnormal exit for running generator */
155
- EG (argument_stack ) = NULL ;
156
- }
157
162
generator -> execute_data = NULL ;
158
163
}
159
164
}
0 commit comments