@@ -163,6 +163,52 @@ ZEND_API void zend_generator_close(zend_generator *generator, zend_bool finished
163
163
164
164
static zend_generator * zend_generator_get_child (zend_generator_node * node , zend_generator * leaf );
165
165
166
+ static void zend_generator_update_leaf_of_child (zend_generator_node * node , zend_generator * from_leaf , zend_generator * to_leaf )
167
+ {
168
+ if (node -> ptr .leaf == from_leaf ) {
169
+ node -> ptr .leaf = to_leaf ;
170
+ }
171
+ if (node -> children <= 1 ) {
172
+ node -> child .single .leaf = to_leaf ;
173
+ } else {
174
+ HashTable * ht = node -> child .ht ;
175
+ zend_generator * child = zend_hash_index_find_ptr (ht , (zend_ulong ) from_leaf );
176
+ if (child ) {
177
+ zend_hash_index_del (ht , (zend_ulong ) from_leaf );
178
+ zend_hash_index_add_ptr (ht , (zend_ulong ) to_leaf , child );
179
+ }
180
+ }
181
+ }
182
+
183
+ static void zend_generator_remove_leaf_child (zend_generator_node * node , zend_generator * leaf , zend_generator * replace_leaf ) {
184
+ if (node -> children > 1 ) {
185
+ HashTable * ht = node -> child .ht ;
186
+ zend_ulong child_leaf ;
187
+ zend_generator * child_generator ;
188
+ zend_hash_index_del (ht , (zend_ulong ) leaf );
189
+ if (-- node -> children == 1 ) {
190
+ ZEND_HASH_FOREACH_NUM_KEY_PTR (ht , child_leaf , child_generator ) {
191
+ node -> child .single .leaf = (zend_generator * ) child_leaf ;
192
+ node -> child .single .child = child_generator ;
193
+ if (node -> ptr .leaf == leaf ) {
194
+ node -> ptr .leaf = (zend_generator * ) child_leaf ;
195
+ }
196
+ break ;
197
+ } ZEND_HASH_FOREACH_END ();
198
+ zend_hash_destroy (ht );
199
+ efree (ht );
200
+ } else if (node -> ptr .leaf == leaf ) {
201
+ ZEND_HASH_FOREACH_NUM_KEY_PTR (ht , child_leaf , child_generator ) {
202
+ node -> ptr .leaf = (zend_generator * ) child_leaf ;
203
+ break ;
204
+ } ZEND_HASH_FOREACH_END ();
205
+ }
206
+ } else if (node -> ptr .leaf == leaf ) {
207
+ ZEND_ASSERT (replace_leaf != leaf );
208
+ node -> ptr .leaf = replace_leaf ;
209
+ }
210
+ }
211
+
166
212
static void zend_generator_dtor_storage (zend_object * object ) /* {{{ */
167
213
{
168
214
zend_generator * generator = (zend_generator * ) object ;
@@ -177,14 +223,43 @@ static void zend_generator_dtor_storage(zend_object *object) /* {{{ */
177
223
}
178
224
179
225
if (EXPECTED (generator -> node .children == 0 )) {
180
- zend_generator * root = generator -> node .ptr .root , * next ;
181
- while (UNEXPECTED (root != generator )) {
182
- next = zend_generator_get_child (& root -> node , generator );
183
- generator -> node .ptr .root = next ;
184
- next -> node .parent = NULL ;
185
- OBJ_RELEASE (& root -> std );
186
- root = next ;
226
+ run_normal_destruction :
227
+ zend_generator_update_current (generator , generator ); /* ensure we remove it from a *live* root */
228
+ zend_generator * root = generator -> node .ptr .root , * parent = generator -> node .parent , * next , * toproot = root ;
229
+ if (parent ) {
230
+ zend_bool parent_becomes_leaf = parent -> node .children == 1 ;
231
+ while (UNEXPECTED (root != generator )) {
232
+ next = zend_generator_get_child (& root -> node , generator );
233
+ if (parent_becomes_leaf ) {
234
+ zend_generator_update_leaf_of_child (& root -> node , generator , parent );
235
+ } else {
236
+ zend_generator_remove_leaf_child (& root -> node , generator , parent -> node .ptr .leaf );
237
+ OBJ_RELEASE (& root -> std );
238
+ }
239
+ root = next ;
240
+ }
241
+ if (parent_becomes_leaf ) {
242
+ parent -> node .ptr .root = toproot ;
243
+ parent -> node .children = 0 ;
244
+ OBJ_RELEASE (& parent -> std );
245
+ }
246
+ /* Reset for resuming in finally */
247
+ generator -> node .parent = NULL ;
248
+ generator -> node .ptr .root = generator ;
187
249
}
250
+ } else if (generator -> node .parent ) {
251
+ /* we're called out of order - this must only happen during shutdown sequence: we call our (direct) child nodes destructors first, to clean it from the bottom up */
252
+ while (generator -> node .children != 0 ) {
253
+ zend_generator * child ;
254
+ if (generator -> node .children == 1 ) {
255
+ child = generator -> node .child .single .child ;
256
+ } else {
257
+ child = (zend_generator * ) Z_PTR_P (zend_hash_get_current_data (generator -> node .child .ht ));
258
+ }
259
+ GC_ADD_FLAGS (& child -> std , IS_OBJ_DESTRUCTOR_CALLED );
260
+ zend_generator_dtor_storage (& child -> std );
261
+ }
262
+ goto run_normal_destruction ;
188
263
}
189
264
190
265
if (EXPECTED (!ex ) || EXPECTED (!(ex -> func -> op_array .fn_flags & ZEND_ACC_HAS_FINALLY_BLOCK ))
@@ -465,10 +540,13 @@ static void zend_generator_add_single_child(zend_generator_node *node, zend_gene
465
540
node -> child .ht = ht ;
466
541
}
467
542
468
- zend_hash_index_add_ptr (node -> child .ht , (zend_ulong ) leaf , child );
543
+ if (zend_hash_index_add_ptr (node -> child .ht , (zend_ulong ) leaf , child ) == NULL ) {
544
+ ZEND_ASSERT (node -> children > 1 );
545
+ return ;
546
+ }
469
547
}
470
548
471
- node -> children ++ ;
549
+ ++ node -> children ;
472
550
}
473
551
474
552
static void zend_generator_merge_child_nodes (zend_generator_node * dest , zend_generator_node * src , zend_generator * child )
@@ -507,7 +585,6 @@ static void zend_generator_add_child(zend_generator *generator, zend_generator *
507
585
} else if (generator -> node .children == 1 ) {
508
586
multi_children_node = zend_generator_search_multi_children_node (& generator -> node );
509
587
if (multi_children_node ) {
510
- generator -> node .children = 0 ;
511
588
zend_generator_merge_child_nodes (& generator -> node , multi_children_node , generator -> node .child .single .child );
512
589
}
513
590
}
@@ -518,6 +595,7 @@ static void zend_generator_add_child(zend_generator *generator, zend_generator *
518
595
multi_children_node = (zend_generator_node * ) 0x1 ;
519
596
}
520
597
598
+ /* for allowing zend_generator_get_child() to work, we need every multi children node to have ALL its leaf descendents present, linking to their respective child */
521
599
{
522
600
zend_generator * parent = generator -> node .parent , * cur = generator ;
523
601
@@ -574,7 +652,7 @@ ZEND_API zend_generator *zend_generator_update_current(zend_generator *generator
574
652
575
653
if (root -> node .parent ) {
576
654
if (root -> node .parent -> execute_data == NULL ) {
577
- if (EXPECTED (EG (exception ) == NULL )) {
655
+ if (EXPECTED (EG (exception ) == NULL ) && EXPECTED (( OBJ_FLAGS ( & generator -> std ) & IS_OBJ_DESTRUCTOR_CALLED ) == 0 ) ) {
578
656
zend_op * yield_from = (zend_op * ) root -> execute_data -> opline - 1 ;
579
657
580
658
if (yield_from -> opcode == ZEND_YIELD_FROM ) {
0 commit comments