@@ -95,9 +95,10 @@ ZEND_API zend_execute_data* zend_generator_freeze_call_stack(zend_execute_data *
95
95
static void zend_generator_cleanup_unfinished_execution (
96
96
zend_generator * generator , zend_execute_data * execute_data , uint32_t catch_op_num ) /* {{{ */
97
97
{
98
- if (execute_data -> opline != execute_data -> func -> op_array .opcodes ) {
98
+ zend_op_array * op_array = & execute_data -> func -> op_array ;
99
+ if (execute_data -> opline != op_array -> opcodes ) {
99
100
/* -1 required because we want the last run opcode, not the next to-be-run one. */
100
- uint32_t op_num = execute_data -> opline - execute_data -> func -> op_array . opcodes - 1 ;
101
+ uint32_t op_num = execute_data -> opline - op_array -> opcodes - 1 ;
101
102
102
103
if (UNEXPECTED (generator -> frozen_call_stack )) {
103
104
/* Temporarily restore generator->execute_data if it has been NULLed out already. */
@@ -106,6 +107,7 @@ static void zend_generator_cleanup_unfinished_execution(
106
107
zend_generator_restore_call_stack (generator );
107
108
generator -> execute_data = save_ex ;
108
109
}
110
+
109
111
zend_cleanup_unfinished_execution (execute_data , op_num , catch_op_num );
110
112
}
111
113
}
@@ -166,7 +168,7 @@ static void zend_generator_dtor_storage(zend_object *object) /* {{{ */
166
168
{
167
169
zend_generator * generator = (zend_generator * ) object ;
168
170
zend_execute_data * ex = generator -> execute_data ;
169
- uint32_t op_num , finally_op_num , finally_op_end ;
171
+ uint32_t op_num , try_catch_offset ;
170
172
int i ;
171
173
172
174
/* leave yield from mode to properly allow finally execution */
@@ -194,38 +196,57 @@ static void zend_generator_dtor_storage(zend_object *object) /* {{{ */
194
196
/* -1 required because we want the last run opcode, not the
195
197
* next to-be-run one. */
196
198
op_num = ex -> opline - ex -> func -> op_array .opcodes - 1 ;
199
+ try_catch_offset = -1 ;
197
200
198
- /* Find next finally block */
199
- finally_op_num = 0 ;
200
- finally_op_end = 0 ;
201
+ /* Find the innermost try/catch that we are inside of. */
201
202
for (i = 0 ; i < ex -> func -> op_array .last_try_catch ; i ++ ) {
202
203
zend_try_catch_element * try_catch = & ex -> func -> op_array .try_catch_array [i ];
203
-
204
204
if (op_num < try_catch -> try_op ) {
205
205
break ;
206
206
}
207
-
208
- if (op_num < try_catch -> finally_op ) {
209
- finally_op_num = try_catch -> finally_op ;
210
- finally_op_end = try_catch -> finally_end ;
207
+ if (op_num < try_catch -> catch_op || op_num < try_catch -> finally_end ) {
208
+ try_catch_offset = i ;
211
209
}
212
210
}
213
211
214
- /* If a finally block was found we jump directly to it and
215
- * resume the generator. */
216
- if (finally_op_num ) {
217
- zval * fast_call ;
212
+ /* Walk try/catch/finally structures upwards, performing the necessary actions. */
213
+ while (try_catch_offset != (uint32_t ) -1 ) {
214
+ zend_try_catch_element * try_catch = & ex -> func -> op_array .try_catch_array [try_catch_offset ];
218
215
219
- zend_generator_cleanup_unfinished_execution (generator , ex , finally_op_num );
216
+ if (op_num < try_catch -> finally_op ) {
217
+ /* Go to finally block */
218
+ zval * fast_call =
219
+ ZEND_CALL_VAR (ex , ex -> func -> op_array .opcodes [try_catch -> finally_end ].op1 .var );
220
220
221
- fast_call = ZEND_CALL_VAR ( ex , ex -> func -> op_array . opcodes [ finally_op_end ]. op1 . var );
222
- Z_OBJ_P (fast_call ) = EG (exception );
223
- EG (exception ) = NULL ;
224
- Z_OPLINE_NUM_P (fast_call ) = (uint32_t )-1 ;
221
+ zend_generator_cleanup_unfinished_execution ( generator , ex , try_catch -> finally_op );
222
+ Z_OBJ_P (fast_call ) = EG (exception );
223
+ EG (exception ) = NULL ;
224
+ Z_OPLINE_NUM_P (fast_call ) = (uint32_t )-1 ;
225
225
226
- ex -> opline = & ex -> func -> op_array .opcodes [finally_op_num ];
227
- generator -> flags |= ZEND_GENERATOR_FORCED_CLOSE ;
228
- zend_generator_resume (generator );
226
+ ex -> opline = & ex -> func -> op_array .opcodes [try_catch -> finally_op ];
227
+ generator -> flags |= ZEND_GENERATOR_FORCED_CLOSE ;
228
+ zend_generator_resume (generator );
229
+
230
+ /* TODO: If we hit another yield inside try/finally,
231
+ * should we also jump to the next finally block? */
232
+ return ;
233
+ } else if (op_num < try_catch -> finally_end ) {
234
+ zval * fast_call =
235
+ ZEND_CALL_VAR (ex , ex -> func -> op_array .opcodes [try_catch -> finally_end ].op1 .var );
236
+ /* Clean up incomplete return statement */
237
+ if (Z_OPLINE_NUM_P (fast_call ) != (uint32_t ) -1 ) {
238
+ zend_op * retval_op = & ex -> func -> op_array .opcodes [Z_OPLINE_NUM_P (fast_call )];
239
+ if (retval_op -> op2_type & (IS_TMP_VAR | IS_VAR )) {
240
+ zval_ptr_dtor (ZEND_CALL_VAR (ex , retval_op -> op2 .var ));
241
+ }
242
+ }
243
+ /* Clean up backed-up exception */
244
+ if (Z_OBJ_P (fast_call )) {
245
+ OBJ_RELEASE (Z_OBJ_P (fast_call ));
246
+ }
247
+ }
248
+
249
+ try_catch_offset -- ;
229
250
}
230
251
}
231
252
/* }}} */
0 commit comments