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