php-internal-docs 8.4.8
Unofficial docs for php/php-src
Loading...
Searching...
No Matches
zend_generators.c
Go to the documentation of this file.
1/*
2 +----------------------------------------------------------------------+
3 | Zend Engine |
4 +----------------------------------------------------------------------+
5 | Copyright (c) Zend Technologies Ltd. (http://www.zend.com) |
6 +----------------------------------------------------------------------+
7 | This source file is subject to version 2.00 of the Zend license, |
8 | that is bundled with this package in the file LICENSE, and is |
9 | available through the world-wide-web at the following url: |
10 | http://www.zend.com/license/2_00.txt. |
11 | If you did not receive a copy of the Zend license and are unable to |
12 | obtain it through the world-wide-web, please send a note to |
13 | license@zend.com so we can mail you a copy immediately. |
14 +----------------------------------------------------------------------+
15 | Authors: Nikita Popov <nikic@php.net> |
16 | Bob Weinand <bobwei9@hotmail.com> |
17 +----------------------------------------------------------------------+
18*/
19
20#include "zend.h"
21#include "zend_API.h"
22#include "zend_hash.h"
23#include "zend_interfaces.h"
24#include "zend_exceptions.h"
25#include "zend_generators.h"
26#include "zend_closures.h"
28#include "zend_observer.h"
29#include "zend_vm_opcodes.h"
30
33static zend_object_handlers zend_generator_handlers;
34
35static zend_object *zend_generator_create(zend_class_entry *class_type);
36
38{
39 zend_execute_data *call, *new_call, *prev_call = NULL;
40
41 call = generator->frozen_call_stack;
42 do {
43 new_call = zend_vm_stack_push_call_frame(
45 call->func,
47 Z_PTR(call->This));
49 new_call->extra_named_params = call->extra_named_params;
50 new_call->prev_execute_data = prev_call;
51 prev_call = new_call;
52
53 call = call->prev_execute_data;
54 } while (call);
55 generator->execute_data->call = prev_call;
56 efree(generator->frozen_call_stack);
57 generator->frozen_call_stack = NULL;
58}
59/* }}} */
60
62{
63 size_t used_stack;
64 zend_execute_data *call, *new_call, *prev_call = NULL;
65 zval *stack;
66
67 /* calculate required stack size */
68 used_stack = 0;
69 call = EX(call);
70 do {
72 call = call->prev_execute_data;
73 } while (call);
74
75 stack = emalloc(used_stack * sizeof(zval));
76
77 /* save stack, linking frames in reverse order */
78 call = EX(call);
79 do {
80 size_t frame_size = ZEND_CALL_FRAME_SLOT + ZEND_CALL_NUM_ARGS(call);
81
82 new_call = (zend_execute_data*)(stack + used_stack - frame_size);
83 memcpy(new_call, call, frame_size * sizeof(zval));
84 used_stack -= frame_size;
85 new_call->prev_execute_data = prev_call;
86 prev_call = new_call;
87
88 new_call = call->prev_execute_data;
89 zend_vm_stack_free_call_frame(call);
90 call = new_call;
91 } while (call);
92
93 execute_data->call = NULL;
94 ZEND_ASSERT(prev_call == (zend_execute_data*)stack);
95
96 return prev_call;
97}
98/* }}} */
99
100static zend_execute_data* zend_generator_revert_call_stack(zend_execute_data *call)
101{
103
104 do {
105 zend_execute_data *next = call->prev_execute_data;
106 call->prev_execute_data = prev;
107 prev = call;
108 call = next;
109 } while (call);
110
111 return prev;
112}
113
114static void zend_generator_cleanup_unfinished_execution(
115 zend_generator *generator, zend_execute_data *execute_data, uint32_t catch_op_num) /* {{{ */
116{
117 zend_op_array *op_array = &execute_data->func->op_array;
118 if (execute_data->opline != op_array->opcodes) {
119 uint32_t op_num = execute_data->opline - op_array->opcodes;
120
121 if (UNEXPECTED(generator->frozen_call_stack)) {
122 /* Temporarily restore generator->execute_data if it has been NULLed out already. */
123 zend_execute_data *save_ex = generator->execute_data;
124 generator->execute_data = execute_data;
126 generator->execute_data = save_ex;
127 }
128
129 zend_cleanup_unfinished_execution(execute_data, op_num, catch_op_num);
130 }
131}
132/* }}} */
133
134ZEND_API void zend_generator_close(zend_generator *generator, bool finished_execution) /* {{{ */
135{
136 if (EXPECTED(generator->execute_data)) {
138 /* Null out execute_data early, to prevent double frees if GC runs while we're
139 * already cleaning up execute_data. */
140 generator->execute_data = NULL;
141
144 }
145 /* always free the CV's, in the symtable are only not-free'd IS_INDIRECT's */
148 zend_free_extra_named_params(execute_data->extra_named_params);
149 }
150
153 }
154
155 /* A fatal error / die occurred during the generator execution.
156 * Trying to clean up the stack may not be safe in this case. */
157 if (UNEXPECTED(CG(unclean_shutdown))) {
158 generator->execute_data = NULL;
159 return;
160 }
161
162 zend_vm_stack_free_extra_args(execute_data);
163
164 /* Some cleanups are only necessary if the generator was closed
165 * before it could finish execution (reach a return statement). */
166 if (UNEXPECTED(!finished_execution)) {
167 zend_generator_cleanup_unfinished_execution(generator, execute_data, 0);
168 }
169
171 }
172}
173/* }}} */
174
175static void zend_generator_remove_child(zend_generator_node *node, zend_generator *child)
176{
177 ZEND_ASSERT(node->children >= 1);
178 if (node->children == 1) {
179 node->child.single = NULL;
180 } else {
181 HashTable *ht = node->child.ht;
183 if (node->children == 2) {
184 zend_generator *other_child;
185 ZEND_HASH_FOREACH_PTR(ht, other_child) {
186 node->child.single = other_child;
187 break;
190 efree(ht);
191 }
192 }
193 node->children--;
194}
195
196static zend_always_inline zend_generator *clear_link_to_leaf(zend_generator *generator) {
197 ZEND_ASSERT(!generator->node.parent);
198 zend_generator *leaf = generator->node.ptr.leaf;
199 if (leaf) {
200 leaf->node.ptr.root = NULL;
201 generator->node.ptr.leaf = NULL;
202 return leaf;
203 }
204 return NULL;
205}
206
207static zend_always_inline void clear_link_to_root(zend_generator *generator) {
208 ZEND_ASSERT(generator->node.parent);
209 if (generator->node.ptr.root) {
210 generator->node.ptr.root->node.ptr.leaf = NULL;
211 generator->node.ptr.root = NULL;
212 }
213}
214
215/* Check if the node 'generator' is running in a fiber */
216static inline bool check_node_running_in_fiber(zend_generator *generator) {
217 ZEND_ASSERT(generator->execute_data);
218
219 if (EXPECTED(generator->flags & ZEND_GENERATOR_IN_FIBER)) {
220 return true;
221 }
222
223 if (EXPECTED(generator->node.children == 0)) {
224 return false;
225 }
226
227 if (generator->node.children == 1) {
228 return check_node_running_in_fiber(generator->node.child.single);
229 }
230
231 zend_generator *child;
232 ZEND_HASH_FOREACH_PTR(generator->node.child.ht, child) {
233 if (check_node_running_in_fiber(child)) {
234 return true;
235 }
237
238 return false;
239}
240
241static void zend_generator_dtor_storage(zend_object *object) /* {{{ */
242{
244 zend_generator *current_generator = zend_generator_get_current(generator);
245 zend_execute_data *ex = generator->execute_data;
246 uint32_t op_num, try_catch_offset;
247 int i;
248
249 /* If current_generator is running in a fiber, there are 2 cases to consider:
250 * - If generator is also marked with ZEND_GENERATOR_IN_FIBER, then the
251 * entire path from current_generator to generator is executing in a
252 * fiber. Do not dtor now: These will be dtor when terminating the fiber.
253 * - If generator is not marked with ZEND_GENERATOR_IN_FIBER, and has a
254 * child marked with ZEND_GENERATOR_IN_FIBER, then this an intermediate
255 * node of case 1. Otherwise generator is not executing in a fiber and we
256 * can dtor.
257 */
258 if (current_generator->flags & ZEND_GENERATOR_IN_FIBER) {
259 if (check_node_running_in_fiber(generator)) {
260 /* Prevent finally blocks from yielding */
261 generator->flags |= ZEND_GENERATOR_FORCED_CLOSE;
262 return;
263 }
264 }
265
266 /* leave yield from mode to properly allow finally execution */
267 if (UNEXPECTED(Z_TYPE(generator->values) != IS_UNDEF)) {
268 zval_ptr_dtor(&generator->values);
269 ZVAL_UNDEF(&generator->values);
270 }
271
272 zend_generator *parent = generator->node.parent;
273 if (parent) {
274 zend_generator_remove_child(&parent->node, generator);
275 clear_link_to_root(generator);
276 generator->node.parent = NULL;
277 OBJ_RELEASE(&parent->std);
278 } else {
279 clear_link_to_leaf(generator);
280 }
281
282 if (EXPECTED(!ex) || EXPECTED(!(ex->func->op_array.fn_flags & ZEND_ACC_HAS_FINALLY_BLOCK))
283 || CG(unclean_shutdown)) {
285 return;
286 }
287
288 op_num = ex->opline - ex->func->op_array.opcodes;
289 try_catch_offset = -1;
290
291 /* Find the innermost try/catch that we are inside of. */
292 for (i = 0; i < ex->func->op_array.last_try_catch; i++) {
293 zend_try_catch_element *try_catch = &ex->func->op_array.try_catch_array[i];
294 if (op_num < try_catch->try_op) {
295 break;
296 }
297 if (op_num < try_catch->catch_op || op_num < try_catch->finally_end) {
298 try_catch_offset = i;
299 }
300 }
301
302 /* Walk try/catch/finally structures upwards, performing the necessary actions. */
303 while (try_catch_offset != (uint32_t) -1) {
304 zend_try_catch_element *try_catch = &ex->func->op_array.try_catch_array[try_catch_offset];
305
306 if (op_num < try_catch->finally_op) {
307 /* Go to finally block */
308 zval *fast_call =
309 ZEND_CALL_VAR(ex, ex->func->op_array.opcodes[try_catch->finally_end].op1.var);
310
311 zend_generator_cleanup_unfinished_execution(generator, ex, try_catch->finally_op);
312 zend_object *old_exception = EG(exception);
313 const zend_op *old_opline_before_exception = EG(opline_before_exception);
314 EG(exception) = NULL;
315 Z_OBJ_P(fast_call) = NULL;
316 Z_OPLINE_NUM_P(fast_call) = (uint32_t)-1;
317
318 /* -1 because zend_generator_resume() will increment it */
319 ex->opline = &ex->func->op_array.opcodes[try_catch->finally_op] - 1;
320 generator->flags |= ZEND_GENERATOR_FORCED_CLOSE;
322
323 if (old_exception) {
324 EG(opline_before_exception) = old_opline_before_exception;
325 if (EG(exception)) {
327 } else {
328 EG(exception) = old_exception;
329 }
330 }
331
332 /* TODO: If we hit another yield inside try/finally,
333 * should we also jump to the next finally block? */
334 break;
335 } else if (op_num < try_catch->finally_end) {
336 zval *fast_call =
337 ZEND_CALL_VAR(ex, ex->func->op_array.opcodes[try_catch->finally_end].op1.var);
338 /* Clean up incomplete return statement */
339 if (Z_OPLINE_NUM_P(fast_call) != (uint32_t) -1) {
340 zend_op *retval_op = &ex->func->op_array.opcodes[Z_OPLINE_NUM_P(fast_call)];
341 if (retval_op->op2_type & (IS_TMP_VAR | IS_VAR)) {
342 zval_ptr_dtor(ZEND_CALL_VAR(ex, retval_op->op2.var));
343 }
344 }
345 /* Clean up backed-up exception */
346 if (Z_OBJ_P(fast_call)) {
347 OBJ_RELEASE(Z_OBJ_P(fast_call));
348 }
349 }
350
351 try_catch_offset--;
352 }
353
355}
356/* }}} */
357
358static void zend_generator_free_storage(zend_object *object) /* {{{ */
359{
361
363
364 if (generator->func && (generator->func->common.fn_flags & ZEND_ACC_CLOSURE)) {
366 }
367
368 /* we can't immediately free them in zend_generator_close() else yield from won't be able to fetch it */
369 zval_ptr_dtor(&generator->value);
371
372 if (EXPECTED(!Z_ISUNDEF(generator->retval))) {
373 zval_ptr_dtor(&generator->retval);
374 }
375
376 if (UNEXPECTED(generator->node.children > 1)) {
377 zend_hash_destroy(generator->node.child.ht);
378 efree(generator->node.child.ht);
379 }
380
382}
383/* }}} */
384
386{
389
390 zend_get_gc_buffer_add_zval(gc_buffer, &generator->value);
391 zend_get_gc_buffer_add_zval(gc_buffer, &generator->key);
392 zend_get_gc_buffer_add_zval(gc_buffer, &generator->retval);
393 zend_get_gc_buffer_add_zval(gc_buffer, &generator->values);
394
395 if (UNEXPECTED(generator->frozen_call_stack)) {
396 /* The frozen stack is linked in reverse order */
397 call = zend_generator_revert_call_stack(generator->frozen_call_stack);
398 }
399
401
402 if (UNEXPECTED(generator->frozen_call_stack)) {
403 zend_generator_revert_call_stack(call);
404 }
405
406 if (generator->node.parent) {
407 zend_get_gc_buffer_add_obj(gc_buffer, &generator->node.parent->std);
408 }
409
410 return ht;
411}
412
413static HashTable *zend_generator_get_gc(zend_object *object, zval **table, int *n) /* {{{ */
414{
417
418 if (!execute_data) {
419 if (UNEXPECTED(generator->func->common.fn_flags & ZEND_ACC_CLOSURE)) {
421 zend_get_gc_buffer_add_zval(gc_buffer, &generator->value);
422 zend_get_gc_buffer_add_zval(gc_buffer, &generator->key);
423 zend_get_gc_buffer_add_zval(gc_buffer, &generator->retval);
424 zend_get_gc_buffer_add_obj(gc_buffer, ZEND_CLOSURE_OBJECT(generator->func));
425 zend_get_gc_buffer_use(gc_buffer, table, n);
426 } else {
427 /* If the non-closure generator has been closed, it can only hold on to three values: The value, key
428 * and retval. These three zvals are stored sequentially starting at &generator->value. */
429 *table = &generator->value;
430 *n = 3;
431 }
432 return NULL;
433 }
434
435 if (generator->flags & ZEND_GENERATOR_CURRENTLY_RUNNING) {
436 /* If the generator is currently running, we certainly won't be able to GC any values it
437 * holds on to. The execute_data state might be inconsistent during execution (e.g. because
438 * GC has been triggered in the middle of a variable reassignment), so we should not try
439 * to inspect it here. */
440 *table = NULL;
441 *n = 0;
442 return NULL;
443 }
444
447 zend_get_gc_buffer_use(gc_buffer, table, n);
448
449 return ht;
450}
451/* }}} */
452
453static zend_object *zend_generator_create(zend_class_entry *class_type) /* {{{ */
454{
456 memset(generator, 0, sizeof(zend_generator));
457
458 /* The key will be incremented on first use, so it'll start at 0 */
459 generator->largest_used_integer_key = -1;
460
461 ZVAL_UNDEF(&generator->retval);
462 ZVAL_UNDEF(&generator->values);
463
464 /* By default we have a tree of only one node */
465 generator->node.parent = NULL;
466 generator->node.children = 0;
467 generator->node.ptr.root = NULL;
468
469 zend_object_std_init(&generator->std, class_type);
470 return (zend_object*)generator;
471}
472/* }}} */
473
474static ZEND_COLD zend_function *zend_generator_get_constructor(zend_object *object) /* {{{ */
475{
476 zend_throw_error(NULL, "The \"Generator\" class is reserved for internal use and cannot be manually instantiated");
477
478 return NULL;
479}
480/* }}} */
481
483{
484 if (!ptr->func && Z_TYPE(ptr->This) == IS_OBJECT) {
485 if (Z_OBJCE(ptr->This) == zend_ce_generator) {
487 zend_execute_data *prev = ptr->prev_execute_data;
488 ZEND_ASSERT(generator->node.parent && "Placeholder only used with delegation");
489 while (generator->node.parent->node.parent) {
490 generator->execute_data->prev_execute_data = prev;
491 prev = generator->execute_data;
492 generator = generator->node.parent;
493 }
494 generator->execute_data->prev_execute_data = prev;
495 ptr = generator->execute_data;
496 }
497 }
498 return ptr;
499}
500
501static void zend_generator_throw_exception(zend_generator *generator, zval *exception)
502{
503 zend_execute_data *original_execute_data = EG(current_execute_data);
504
505 /* Throw the exception in the context of the generator. Decrementing the opline
506 * to pretend the exception happened during the YIELD opcode. */
507 EG(current_execute_data) = generator->execute_data;
508 generator->execute_data->prev_execute_data = original_execute_data;
509
510 if (exception) {
512 } else {
513 zend_rethrow_exception(EG(current_execute_data));
514 }
515
516 /* if we don't stop an array/iterator yield from, the exception will only reach the generator after the values were all iterated over */
517 if (UNEXPECTED(Z_TYPE(generator->values) != IS_UNDEF)) {
518 zval_ptr_dtor(&generator->values);
519 ZVAL_UNDEF(&generator->values);
520 }
521
522 EG(current_execute_data) = original_execute_data;
523}
524
525static void zend_generator_add_child(zend_generator *generator, zend_generator *child)
526{
527 zend_generator_node *node = &generator->node;
528
529 if (node->children == 0) {
530 node->child.single = child;
531 } else {
532 if (node->children == 1) {
533 HashTable *ht = emalloc(sizeof(HashTable));
534 zend_hash_init(ht, 0, NULL, NULL, 0);
535 zend_hash_index_add_new_ptr(ht,
536 (zend_ulong) node->child.single, node->child.single);
537 node->child.ht = ht;
538 }
539
540 zend_hash_index_add_new_ptr(node->child.ht, (zend_ulong) child, child);
541 }
542
543 ++node->children;
544}
545
547{
548 ZEND_ASSERT(!generator->node.parent && "Already has parent?");
549 zend_generator *leaf = clear_link_to_leaf(generator);
550 if (leaf && !from->node.parent && !from->node.ptr.leaf) {
551 from->node.ptr.leaf = leaf;
552 leaf->node.ptr.root = from;
553 }
554 generator->node.parent = from;
555 zend_generator_add_child(from, generator);
556 generator->flags |= ZEND_GENERATOR_DO_INIT;
557}
558
560{
561 zend_generator *root = generator->node.parent;
562 while (root->node.parent) {
563 root = root->node.parent;
564 }
565
566 clear_link_to_leaf(root);
567 root->node.ptr.leaf = generator;
568 generator->node.ptr.root = root;
569 return root;
570}
571
572static zend_generator *get_new_root(zend_generator *generator, zend_generator *root)
573{
574 while (!root->execute_data && root->node.children == 1) {
575 root = root->node.child.single;
576 }
577
578 if (root->execute_data) {
579 return root;
580 }
581
582 /* We have reached a multi-child node haven't found the root yet. We don't know which
583 * child to follow, so perform the search from the other direction instead. */
584 while (generator->node.parent->execute_data) {
585 generator = generator->node.parent;
586 }
587
588 return generator;
589}
590
592{
593 zend_generator *old_root = generator->node.ptr.root;
594 ZEND_ASSERT(!old_root->execute_data && "Nothing to update?");
595
596 zend_generator *new_root = get_new_root(generator, old_root);
597
598 ZEND_ASSERT(old_root->node.ptr.leaf == generator);
599 generator->node.ptr.root = new_root;
600 new_root->node.ptr.leaf = generator;
601 old_root->node.ptr.leaf = NULL;
602
603 zend_generator *new_root_parent = new_root->node.parent;
604 ZEND_ASSERT(new_root_parent);
605 zend_generator_remove_child(&new_root_parent->node, new_root);
606
608 zend_op *yield_from = (zend_op *) new_root->execute_data->opline;
609
610 if (yield_from->opcode == ZEND_YIELD_FROM) {
611 if (Z_ISUNDEF(new_root_parent->retval)) {
612 /* Throw the exception in the context of the generator */
613 zend_execute_data *original_execute_data = EG(current_execute_data);
614 EG(current_execute_data) = new_root->execute_data;
615
616 if (new_root == generator) {
618 } else {
619 new_root->execute_data->prev_execute_data = &generator->execute_fake;
620 generator->execute_fake.prev_execute_data = original_execute_data;
621 }
622
623 zend_throw_exception(zend_ce_ClosedGeneratorException, "Generator yielded from aborted, no return value available", 0);
624
625 EG(current_execute_data) = original_execute_data;
626
627 if (!(old_root->flags & ZEND_GENERATOR_CURRENTLY_RUNNING)) {
628 new_root->node.parent = NULL;
629 OBJ_RELEASE(&new_root_parent->std);
631 return zend_generator_get_current(generator);
632 }
633 } else {
634 zval_ptr_dtor(&new_root->value);
635 ZVAL_COPY(&new_root->value, &new_root_parent->value);
636 ZVAL_COPY(ZEND_CALL_VAR(new_root->execute_data, yield_from->result.var), &new_root_parent->retval);
637 }
638 }
639 }
640
641 new_root->node.parent = NULL;
642 OBJ_RELEASE(&new_root_parent->std);
643
644 return new_root;
645}
646
647static zend_result zend_generator_get_next_delegated_value(zend_generator *generator) /* {{{ */
648{
649 zval *value;
650 if (Z_TYPE(generator->values) == IS_ARRAY) {
651 HashTable *ht = Z_ARR(generator->values);
653
654 if (HT_IS_PACKED(ht)) {
655 do {
656 if (UNEXPECTED(pos >= ht->nNumUsed)) {
657 /* Reached end of array */
658 goto failure;
659 }
660
661 value = &ht->arPacked[pos];
662 pos++;
663 } while (Z_ISUNDEF_P(value));
664
665 zval_ptr_dtor(&generator->value);
666 ZVAL_COPY(&generator->value, value);
667
669 ZVAL_LONG(&generator->key, pos - 1);
670 } else {
671 Bucket *p;
672
673 do {
674 if (UNEXPECTED(pos >= ht->nNumUsed)) {
675 /* Reached end of array */
676 goto failure;
677 }
678
679 p = &ht->arData[pos];
680 value = &p->val;
681 pos++;
682 } while (Z_ISUNDEF_P(value));
683
684 zval_ptr_dtor(&generator->value);
685 ZVAL_COPY(&generator->value, value);
686
688 if (p->key) {
689 ZVAL_STR_COPY(&generator->key, p->key);
690 } else {
691 ZVAL_LONG(&generator->key, p->h);
692 }
693 }
694 Z_FE_POS(generator->values) = pos;
695 } else {
697
698 if (iter->index++ > 0) {
699 iter->funcs->move_forward(iter);
700 if (UNEXPECTED(EG(exception) != NULL)) {
701 goto failure;
702 }
703 }
704
705 if (iter->funcs->valid(iter) == FAILURE) {
706 /* reached end of iteration */
707 goto failure;
708 }
709
710 value = iter->funcs->get_current_data(iter);
711 if (UNEXPECTED(EG(exception) != NULL) || UNEXPECTED(!value)) {
712 goto failure;
713 }
714
715 zval_ptr_dtor(&generator->value);
716 ZVAL_COPY(&generator->value, value);
717
719 if (iter->funcs->get_current_key) {
720 iter->funcs->get_current_key(iter, &generator->key);
721 if (UNEXPECTED(EG(exception) != NULL)) {
722 ZVAL_UNDEF(&generator->key);
723 goto failure;
724 }
725 } else {
726 ZVAL_LONG(&generator->key, iter->index);
727 }
728 }
729
730 return SUCCESS;
731
732failure:
733 zval_ptr_dtor(&generator->values);
734 ZVAL_UNDEF(&generator->values);
735
736 return FAILURE;
737}
738/* }}} */
739
740ZEND_API void zend_generator_resume(zend_generator *orig_generator) /* {{{ */
741{
742 zend_generator *generator = zend_generator_get_current(orig_generator);
743
744 /* The generator is already closed, thus can't resume */
745 if (UNEXPECTED(!generator->execute_data)) {
746 return;
747 }
748
749try_again:
750 if (generator->flags & ZEND_GENERATOR_CURRENTLY_RUNNING) {
751 zend_throw_error(NULL, "Cannot resume an already running generator");
752 return;
753 }
754
755 if (UNEXPECTED((orig_generator->flags & ZEND_GENERATOR_DO_INIT) != 0 && !Z_ISUNDEF(generator->value))) {
756 /* We must not advance Generator if we yield from a Generator being currently run */
757 orig_generator->flags &= ~ZEND_GENERATOR_DO_INIT;
758 return;
759 }
760
761 if (EG(active_fiber)) {
762 orig_generator->flags |= ZEND_GENERATOR_IN_FIBER;
763 generator->flags |= ZEND_GENERATOR_IN_FIBER;
764 }
765
766 /* Drop the AT_FIRST_YIELD flag */
767 orig_generator->flags &= ~ZEND_GENERATOR_AT_FIRST_YIELD;
768
769 /* Backup executor globals */
770 zend_execute_data *original_execute_data = EG(current_execute_data);
771 uint32_t original_jit_trace_num = EG(jit_trace_num);
772
773 /* Set executor globals */
774 EG(current_execute_data) = generator->execute_data;
775 EG(jit_trace_num) = 0;
776
777 /* We want the backtrace to look as if the generator function was
778 * called from whatever method we are current running (e.g. next()).
779 * So we have to link generator call frame with caller call frame. */
780 if (generator == orig_generator) {
781 generator->execute_data->prev_execute_data = original_execute_data;
782 } else {
783 /* We need some execute_data placeholder in stacktrace to be replaced
784 * by the real stack trace when needed */
785 generator->execute_data->prev_execute_data = &orig_generator->execute_fake;
787 }
788
789 /* Ensure this is run after executor_data swap to have a proper stack trace */
790 if (UNEXPECTED(!Z_ISUNDEF(generator->values))) {
791 if (EXPECTED(zend_generator_get_next_delegated_value(generator) == SUCCESS)) {
792 /* Restore executor globals */
793 EG(current_execute_data) = original_execute_data;
794 EG(jit_trace_num) = original_jit_trace_num;
795
796 orig_generator->flags &= ~(ZEND_GENERATOR_DO_INIT | ZEND_GENERATOR_IN_FIBER);
797 generator->flags &= ~ZEND_GENERATOR_IN_FIBER;
798 return;
799 }
800 /* If there are no more delegated values, resume the generator
801 * after the "yield from" expression. */
802 }
803
804 if (UNEXPECTED(generator->frozen_call_stack)) {
805 /* Restore frozen call-stack */
807 }
808
809 /* Resume execution */
810 ZEND_ASSERT(generator->execute_data->opline->opcode == ZEND_GENERATOR_CREATE
811 || generator->execute_data->opline->opcode == ZEND_YIELD
812 || generator->execute_data->opline->opcode == ZEND_YIELD_FROM
813 /* opline points to EG(exception_op), which is a sequence of
814 * ZEND_HANDLE_EXCEPTION ops, so the following increment is safe */
815 || generator->execute_data->opline->opcode == ZEND_HANDLE_EXCEPTION
816 /* opline points to the start of a finally block minus one op to
817 * account for the following increment */
818 || (generator->flags & ZEND_GENERATOR_FORCED_CLOSE));
819 generator->execute_data->opline++;
820 generator->flags |= ZEND_GENERATOR_CURRENTLY_RUNNING;
822 zend_execute_ex(generator->execute_data);
823 } else {
825 zend_execute_ex(generator->execute_data);
826 if (generator->execute_data) {
827 /* On the final return, this will be called from ZEND_GENERATOR_RETURN */
828 zend_observer_fcall_end(generator->execute_data, &generator->value);
829 }
830 }
831 generator->flags &= ~(ZEND_GENERATOR_CURRENTLY_RUNNING | ZEND_GENERATOR_IN_FIBER);
832
833 generator->frozen_call_stack = NULL;
834 if (EXPECTED(generator->execute_data) &&
835 UNEXPECTED(generator->execute_data->call)) {
836 /* Frize call-stack */
837 generator->frozen_call_stack = zend_generator_freeze_call_stack(generator->execute_data);
838 }
839
840 /* Restore executor globals */
841 EG(current_execute_data) = original_execute_data;
842 EG(jit_trace_num) = original_jit_trace_num;
843
844 /* If an exception was thrown in the generator we have to internally
845 * rethrow it in the parent scope.
846 * In case we did yield from, the Exception must be rethrown into
847 * its calling frame (see above in if (check_yield_from). */
848 if (UNEXPECTED(EG(exception) != NULL)) {
849 if (generator == orig_generator) {
851 if (!EG(current_execute_data)) {
853 } else if (EG(current_execute_data)->func &&
854 ZEND_USER_CODE(EG(current_execute_data)->func->common.type)) {
855 zend_rethrow_exception(EG(current_execute_data));
856 }
857 } else {
858 generator = zend_generator_get_current(orig_generator);
859 zend_generator_throw_exception(generator, NULL);
860 orig_generator->flags &= ~ZEND_GENERATOR_DO_INIT;
861 goto try_again;
862 }
863 }
864
865 /* yield from was used, try another resume. */
866 if (UNEXPECTED((generator != orig_generator && !Z_ISUNDEF(generator->retval)) || (generator->execute_data && generator->execute_data->opline->opcode == ZEND_YIELD_FROM))) {
867 generator = zend_generator_get_current(orig_generator);
868 goto try_again;
869 }
870
871 orig_generator->flags &= ~(ZEND_GENERATOR_DO_INIT | ZEND_GENERATOR_IN_FIBER);
872}
873/* }}} */
874
875static inline void zend_generator_ensure_initialized(zend_generator *generator) /* {{{ */
876{
877 if (UNEXPECTED(Z_TYPE(generator->value) == IS_UNDEF) && EXPECTED(generator->execute_data) && EXPECTED(generator->node.parent == NULL)) {
879 generator->flags |= ZEND_GENERATOR_AT_FIRST_YIELD;
880 }
881}
882/* }}} */
883
884static inline void zend_generator_rewind(zend_generator *generator) /* {{{ */
885{
886 zend_generator_ensure_initialized(generator);
887
888 if (!(generator->flags & ZEND_GENERATOR_AT_FIRST_YIELD)) {
889 zend_throw_exception(NULL, "Cannot rewind a generator that was already run", 0);
890 }
891}
892/* }}} */
893
894/* {{{ Rewind the generator */
896{
898
900
902
903 zend_generator_rewind(generator);
904}
905/* }}} */
906
907/* {{{ Check whether the generator is valid */
909{
911
913
915
916 zend_generator_ensure_initialized(generator);
917
918 zend_generator_get_current(generator);
919
920 RETURN_BOOL(EXPECTED(generator->execute_data != NULL));
921}
922/* }}} */
923
924/* {{{ Get the current value */
926{
928
930
932
933 zend_generator_ensure_initialized(generator);
934
935 root = zend_generator_get_current(generator);
936 if (EXPECTED(generator->execute_data != NULL && Z_TYPE(root->value) != IS_UNDEF)) {
937 RETURN_COPY_DEREF(&root->value);
938 }
939}
940/* }}} */
941
942/* {{{ Get the current key */
944{
946
948
950
951 zend_generator_ensure_initialized(generator);
952
953 root = zend_generator_get_current(generator);
954 if (EXPECTED(generator->execute_data != NULL && Z_TYPE(root->key) != IS_UNDEF)) {
955 RETURN_COPY_DEREF(&root->key);
956 }
957}
958/* }}} */
959
960/* {{{ Advances the generator */
962{
964
966
968
969 zend_generator_ensure_initialized(generator);
970
972}
973/* }}} */
974
975/* {{{ Sends a value to the generator */
977{
978 zval *value;
980
984
986
987 zend_generator_ensure_initialized(generator);
988
989 /* The generator is already closed, thus can't send anything */
990 if (UNEXPECTED(!generator->execute_data)) {
991 return;
992 }
993
994 root = zend_generator_get_current(generator);
995 /* Put sent value in the target VAR slot, if it is used */
996 if (root->send_target && !(root->flags & ZEND_GENERATOR_CURRENTLY_RUNNING)) {
998 }
999
1001
1002 root = zend_generator_get_current(generator);
1003 if (EXPECTED(generator->execute_data)) {
1004 RETURN_COPY_DEREF(&root->value);
1005 }
1006}
1007/* }}} */
1008
1009/* {{{ Throws an exception into the generator */
1011{
1012 zval *exception;
1014
1018
1020
1022
1023 zend_generator_ensure_initialized(generator);
1024
1025 if (generator->execute_data) {
1026 zend_generator *root = zend_generator_get_current(generator);
1027
1028 zend_generator_throw_exception(root, exception);
1029
1031
1032 root = zend_generator_get_current(generator);
1033 if (generator->execute_data) {
1034 RETURN_COPY_DEREF(&root->value);
1035 }
1036 } else {
1037 /* If the generator is already closed throw the exception in the
1038 * current context */
1040 }
1041}
1042/* }}} */
1043
1044/* {{{ Retrieves the return value of the generator */
1046{
1048
1050
1052
1053 zend_generator_ensure_initialized(generator);
1054 if (UNEXPECTED(EG(exception))) {
1055 return;
1056 }
1057
1058 if (Z_ISUNDEF(generator->retval)) {
1059 /* Generator hasn't returned yet -> error! */
1061 "Cannot get return value of a generator that hasn't returned", 0);
1062 return;
1063 }
1064
1065 ZVAL_COPY(return_value, &generator->retval);
1066}
1067/* }}} */
1068
1070{
1072
1074
1076
1078
1079 zend_function *func = generator->func;
1080
1081 zval val;
1082 if (func->common.scope) {
1083 zend_string *class_name = func->common.scope->name;
1084 zend_string *func_name = func->common.function_name;
1085 zend_string *combined = zend_string_concat3(
1086 ZSTR_VAL(class_name), ZSTR_LEN(class_name),
1087 "::", strlen("::"),
1089 );
1090 ZVAL_NEW_STR(&val, combined);
1091 } else {
1092 ZVAL_STR_COPY(&val, func->common.function_name);
1093 }
1094
1095 zend_hash_update(Z_ARR_P(return_value), ZSTR_KNOWN(ZEND_STR_FUNCTION), &val);
1096}
1097
1098/* get_iterator implementation */
1099
1100static void zend_generator_iterator_dtor(zend_object_iterator *iterator) /* {{{ */
1101{
1102 zval_ptr_dtor(&iterator->data);
1103}
1104/* }}} */
1105
1106static zend_result zend_generator_iterator_valid(zend_object_iterator *iterator) /* {{{ */
1107{
1109
1110 zend_generator_ensure_initialized(generator);
1111
1112 zend_generator_get_current(generator);
1113
1114 return generator->execute_data ? SUCCESS : FAILURE;
1115}
1116/* }}} */
1117
1118static zval *zend_generator_iterator_get_data(zend_object_iterator *iterator) /* {{{ */
1119{
1120 zend_generator *generator = (zend_generator*)Z_OBJ(iterator->data), *root;
1121
1122 zend_generator_ensure_initialized(generator);
1123
1124 root = zend_generator_get_current(generator);
1125
1126 return &root->value;
1127}
1128/* }}} */
1129
1130static void zend_generator_iterator_get_key(zend_object_iterator *iterator, zval *key) /* {{{ */
1131{
1132 zend_generator *generator = (zend_generator*)Z_OBJ(iterator->data), *root;
1133
1134 zend_generator_ensure_initialized(generator);
1135
1136 root = zend_generator_get_current(generator);
1137
1138 if (EXPECTED(Z_TYPE(root->key) != IS_UNDEF)) {
1139 zval *zv = &root->key;
1140
1142 } else {
1143 ZVAL_NULL(key);
1144 }
1145}
1146/* }}} */
1147
1148static void zend_generator_iterator_move_forward(zend_object_iterator *iterator) /* {{{ */
1149{
1151
1152 zend_generator_ensure_initialized(generator);
1153
1155}
1156/* }}} */
1157
1158static void zend_generator_iterator_rewind(zend_object_iterator *iterator) /* {{{ */
1159{
1161
1162 zend_generator_rewind(generator);
1163}
1164/* }}} */
1165
1166static HashTable *zend_generator_iterator_get_gc(
1167 zend_object_iterator *iterator, zval **table, int *n)
1168{
1169 *table = &iterator->data;
1170 *n = 1;
1171 return NULL;
1172}
1173
1174static const zend_object_iterator_funcs zend_generator_iterator_functions = {
1175 zend_generator_iterator_dtor,
1176 zend_generator_iterator_valid,
1177 zend_generator_iterator_get_data,
1178 zend_generator_iterator_get_key,
1179 zend_generator_iterator_move_forward,
1180 zend_generator_iterator_rewind,
1181 NULL,
1182 zend_generator_iterator_get_gc,
1183};
1184
1185/* by_ref is int due to Iterator API */
1186static zend_object_iterator *zend_generator_get_iterator(zend_class_entry *ce, zval *object, int by_ref) /* {{{ */
1187{
1188 zend_object_iterator *iterator;
1190
1191 if (!generator->execute_data) {
1192 zend_throw_exception(NULL, "Cannot traverse an already closed generator", 0);
1193 return NULL;
1194 }
1195
1196 if (UNEXPECTED(by_ref) && !(generator->execute_data->func->op_array.fn_flags & ZEND_ACC_RETURN_REFERENCE)) {
1197 zend_throw_exception(NULL, "You can only iterate a generator by-reference if it declared that it yields by-reference", 0);
1198 return NULL;
1199 }
1200
1201 iterator = emalloc(sizeof(zend_object_iterator));
1202 zend_iterator_init(iterator);
1203
1204 iterator->funcs = &zend_generator_iterator_functions;
1205 ZVAL_OBJ_COPY(&iterator->data, Z_OBJ_P(object));
1206
1207 return iterator;
1208}
1209/* }}} */
1210
1211void zend_register_generator_ce(void) /* {{{ */
1212{
1213 zend_ce_generator = register_class_Generator(zend_ce_iterator);
1214 zend_ce_generator->create_object = zend_generator_create;
1215 /* get_iterator has to be assigned *after* implementing the interface */
1216 zend_ce_generator->get_iterator = zend_generator_get_iterator;
1217 zend_ce_generator->default_object_handlers = &zend_generator_handlers;
1218
1219 memcpy(&zend_generator_handlers, &std_object_handlers, sizeof(zend_object_handlers));
1220 zend_generator_handlers.free_obj = zend_generator_free_storage;
1221 zend_generator_handlers.dtor_obj = zend_generator_dtor_storage;
1222 zend_generator_handlers.get_gc = zend_generator_get_gc;
1223 zend_generator_handlers.clone_obj = NULL;
1224 zend_generator_handlers.get_constructor = zend_generator_get_constructor;
1225
1226 zend_ce_ClosedGeneratorException = register_class_ClosedGeneratorException(zend_ce_exception);
1227}
1228/* }}} */
bool exception
Definition assert.c:30
rewind($stream)
prev(array|object &$array)
zval * zv
Definition ffi.c:3975
zend_long n
Definition ffi.c:4979
void * ptr
Definition ffi.c:3814
memcpy(ptr1, ptr2, size)
memset(ptr, 0, type->size)
zval * val
Definition ffi.c:4262
HashTable * ht
Definition ffi.c:4838
#define NULL
Definition gdcache.h:45
#define SUCCESS
Definition hash_sha3.c:261
#define next(ls)
Definition minilua.c:2661
unsigned const char * pos
Definition php_ffi.h:52
unsigned char key[REFLECTION_KEY_LEN]
zend_test_fiber * active_fiber
Definition php_test.h:61
const char * func_name
zend_object * ex
zend_execute_data * original_execute_data
zval * current
Definition session.c:1024
p
Definition session.c:1105
zend_execute_data * prev_execute_data
const zend_op * opline
zend_generator * root
zend_generator * single
zend_generator * leaf
union _zend_generator_node::@240302224373203254241357377177253207307263372347 child
zend_generator * parent
union _zend_generator_node::@232312244065232141030257143346267306240210334326 ptr
zend_generator_node node
zend_execute_data execute_fake
zend_execute_data * execute_data
void(* get_current_key)(zend_object_iterator *iter, zval *key)
void(* move_forward)(zend_object_iterator *iter)
zend_result(* valid)(zend_object_iterator *iter)
zval *(* get_current_data)(zend_object_iterator *iter)
const zend_object_iterator_funcs * funcs
zend_op * opcodes
znode_op op2
znode_op result
uint8_t opcode
uint8_t op2_type
uint32_t var
ZEND_API ZEND_COLD void zend_throw_error(zend_class_entry *exception_ce, const char *format,...)
Definition zend.c:1772
#define RETURN_COPY_DEREF(zv)
Definition zend_API.h:1056
#define ZEND_PARSE_PARAMETERS_END()
Definition zend_API.h:1641
#define ZEND_PARSE_PARAMETERS_NONE()
Definition zend_API.h:1623
#define ZEND_PARSE_PARAMETERS_START(min_num_args, max_num_args)
Definition zend_API.h:1620
#define RETURN_BOOL(b)
Definition zend_API.h:1035
#define ZEND_METHOD(classname, name)
Definition zend_API.h:76
#define ZEND_THIS
Definition zend_API.h:523
#define Z_PARAM_OBJECT_OF_CLASS(dest, _ce)
Definition zend_API.h:1976
#define Z_PARAM_ZVAL(dest)
Definition zend_API.h:2100
#define array_init(arg)
Definition zend_API.h:537
#define efree(ptr)
Definition zend_alloc.h:155
#define emalloc(size)
Definition zend_alloc.h:151
struct _zval_struct zval
strlen(string $string)
execute_data func
#define ZEND_CLOSURE_OBJECT(op_array)
struct _zend_op zend_op
#define ZEND_USER_CODE(type)
#define ZEND_CALL_ALLOCATED
#define IS_VAR
#define ZEND_CALL_FRAME_SLOT
#define EX(element)
#define ZEND_CALL_VAR(call, n)
#define EX_CALL_INFO()
#define ZEND_CALL_HAS_SYMBOL_TABLE
struct _zend_op_array zend_op_array
#define ZEND_CALL_HAS_EXTRA_NAMED_PARAMS
#define ZEND_CALL_NUM_ARGS(call)
#define ZEND_CALL_INFO(call)
#define IS_TMP_VAR
#define ZEND_ACC_HAS_FINALLY_BLOCK
struct _zend_try_catch_element zend_try_catch_element
#define ZEND_CALL_RELEASE_THIS
#define ZEND_ACC_RETURN_REFERENCE
#define ZEND_ACC_CLOSURE
#define ZEND_API
ZEND_API ZEND_COLD void zend_throw_exception_internal(zend_object *exception)
ZEND_API zend_class_entry * zend_ce_exception
ZEND_API ZEND_COLD void zend_throw_exception_object(zval *exception)
ZEND_API ZEND_COLD zend_object * zend_throw_exception(zend_class_entry *exception_ce, const char *message, zend_long code)
ZEND_API zend_class_entry * zend_ce_throwable
void zend_exception_set_previous(zend_object *exception, zend_object *add_previous)
ZEND_API void ZEND_FASTCALL zend_free_extra_named_params(zend_array *extra_named_params)
ZEND_API HashTable * zend_unfinished_execution_gc_ex(zend_execute_data *execute_data, zend_execute_data *call, zend_get_gc_buffer *gc_buffer, bool suspended_by_yield)
ZEND_API void zend_clean_and_cache_symbol_table(zend_array *symbol_table)
ZEND_API void zend_cleanup_unfinished_execution(zend_execute_data *execute_data, uint32_t op_num, uint32_t catch_op_num)
ZEND_API void ZEND_FASTCALL zend_free_compiled_variables(zend_execute_data *execute_data)
ZEND_API void(* zend_execute_ex)(zend_execute_data *execute_data)
union _zend_function zend_function
ZEND_API zend_get_gc_buffer * zend_get_gc_buffer_create(void)
Definition zend_gc.c:2130
void zend_register_generator_ce(void)
ZEND_API void zend_generator_resume(zend_generator *orig_generator)
ZEND_API zend_generator * zend_generator_update_current(zend_generator *generator)
ZEND_API zend_generator * zend_generator_update_root(zend_generator *generator)
HashTable * zend_generator_frame_gc(zend_get_gc_buffer *gc_buffer, zend_generator *generator)
ZEND_API void zend_generator_close(zend_generator *generator, bool finished_execution)
ZEND_API void zend_generator_restore_call_stack(zend_generator *generator)
ZEND_API zend_class_entry * zend_ce_ClosedGeneratorException
ZEND_API zend_execute_data * zend_generator_freeze_call_stack(zend_execute_data *execute_data)
void zend_generator_yield_from(zend_generator *generator, zend_generator *from)
ZEND_API zend_execute_data * zend_generator_check_placeholder_frame(zend_execute_data *ptr)
ZEND_API zend_class_entry * zend_ce_generator
struct _zend_generator zend_generator
struct _zend_generator_node zend_generator_node
#define CG(v)
#define EG(v)
ZEND_API void ZEND_FASTCALL zend_hash_destroy(HashTable *ht)
Definition zend_hash.c:1727
ZEND_API zend_result ZEND_FASTCALL zend_hash_index_del(HashTable *ht, zend_ulong h)
Definition zend_hash.c:1692
ZEND_API zval *ZEND_FASTCALL zend_hash_update(HashTable *ht, zend_string *key, zval *pData)
Definition zend_hash.c:997
#define zend_hash_init(ht, nSize, pHashFunction, pDestructor, persistent)
Definition zend_hash.h:108
#define HT_IS_PACKED(ht)
Definition zend_hash.h:59
#define ZEND_HASH_FOREACH_PTR(ht, _ptr)
Definition zend_hash.h:1118
#define ZEND_HASH_FOREACH_END()
Definition zend_hash.h:1086
ZEND_API zend_class_entry * zend_ce_iterator
ZEND_API void zend_iterator_init(zend_object_iterator *iter)
struct _zend_object_iterator zend_object_iterator
struct _zend_object_iterator_funcs zend_object_iterator_funcs
uint32_t zend_ulong
Definition zend_long.h:43
struct _zend_string zend_string
ZEND_API const zend_object_handlers std_object_handlers
ZEND_API void ZEND_FASTCALL zend_object_std_init(zend_object *object, zend_class_entry *ce)
ZEND_API void zend_object_std_dtor(zend_object *object)
#define OBJ_RELEASE(obj)
ZEND_API void ZEND_FASTCALL zend_observer_generator_resume(zend_execute_data *execute_data)
#define ZEND_OBSERVER_ENABLED
#define EXPECTED(condition)
#define zend_always_inline
#define ZEND_ASSERT(c)
#define ZEND_COLD
#define UNEXPECTED(condition)
struct _zend_class_entry zend_class_entry
struct _zend_object zend_object
ZEND_API zend_string * zend_string_concat3(const char *str1, size_t str1_len, const char *str2, size_t str2_len, const char *str3, size_t str3_len)
#define ZSTR_VAL(zstr)
Definition zend_string.h:68
#define ZSTR_KNOWN(idx)
#define ZSTR_LEN(zstr)
Definition zend_string.h:69
#define Z_TRY_ADDREF_P(pz)
#define ZVAL_UNDEF(z)
#define IS_UNDEF
Definition zend_types.h:600
#define Z_ISUNDEF_P(zval_p)
Definition zend_types.h:957
#define ZVAL_NULL(z)
#define ZVAL_LONG(z, l)
#define ZVAL_STR_COPY(z, s)
struct _zend_array HashTable
Definition zend_types.h:386
#define Z_OBJ_P(zval_p)
Definition zend_types.h:990
#define IS_ARRAY
Definition zend_types.h:607
#define ZVAL_COPY_DEREF(z, v)
#define Z_ISUNDEF(zval)
Definition zend_types.h:956
#define IS_OBJ_DESTRUCTOR_CALLED
Definition zend_types.h:828
#define Z_PTR(zval)
#define Z_OPLINE_NUM_P(zval_p)
Definition zend_types.h:681
@ FAILURE
Definition zend_types.h:61
#define Z_FE_POS(zval)
Definition zend_types.h:683
#define IS_OBJECT
Definition zend_types.h:608
#define ZVAL_NEW_STR(z, s)
#define ZVAL_COPY(z, v)
uint32_t HashPosition
Definition zend_types.h:548
#define Z_ARR(zval)
Definition zend_types.h:983
struct _Bucket Bucket
#define ZVAL_OBJ_COPY(z, o)
ZEND_RESULT_CODE zend_result
Definition zend_types.h:64
struct _zend_object_handlers zend_object_handlers
Definition zend_types.h:88
#define Z_TYPE(zval)
Definition zend_types.h:659
struct _zend_execute_data zend_execute_data
Definition zend_types.h:91
#define Z_ARR_P(zval_p)
Definition zend_types.h:984
#define OBJ_FLAGS(obj)
Definition zend_types.h:831
#define Z_OBJCE(zval)
#define Z_OBJ(zval)
Definition zend_types.h:989
ZEND_API void zval_ptr_dtor(zval *zval_ptr)
zval * return_value
zend_generator * generator
execute_data
value
zend_execute_data * call
#define ZEND_YIELD
#define ZEND_GENERATOR_CREATE
#define ZEND_HANDLE_EXCEPTION
#define ZEND_YIELD_FROM