1 /*
2 +----------------------------------------------------------------------+
3 | Zend Engine |
4 +----------------------------------------------------------------------+
5 | Copyright (c) 1998-2018 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 /* $Id$ */
21
22 #include "zend.h"
23 #include "zend_API.h"
24 #include "zend_interfaces.h"
25 #include "zend_exceptions.h"
26 #include "zend_generators.h"
27
28 ZEND_API zend_class_entry *zend_ce_generator;
29 ZEND_API zend_class_entry *zend_ce_ClosedGeneratorException;
30 static zend_object_handlers zend_generator_handlers;
31
32 static zend_object *zend_generator_create(zend_class_entry *class_type);
33
zend_generator_restore_call_stack(zend_generator * generator)34 ZEND_API void zend_generator_restore_call_stack(zend_generator *generator) /* {{{ */
35 {
36 zend_execute_data *call, *new_call, *prev_call = NULL;
37
38 call = generator->frozen_call_stack;
39 do {
40 new_call = zend_vm_stack_push_call_frame(
41 (ZEND_CALL_INFO(call) & ~ZEND_CALL_ALLOCATED),
42 call->func,
43 ZEND_CALL_NUM_ARGS(call),
44 (Z_TYPE(call->This) == IS_UNDEF) ?
45 (zend_class_entry*)Z_OBJ(call->This) : NULL,
46 (Z_TYPE(call->This) != IS_UNDEF) ?
47 Z_OBJ(call->This) : NULL);
48 memcpy(((zval*)new_call) + ZEND_CALL_FRAME_SLOT, ((zval*)call) + ZEND_CALL_FRAME_SLOT, ZEND_CALL_NUM_ARGS(call) * sizeof(zval));
49 new_call->prev_execute_data = prev_call;
50 prev_call = new_call;
51
52 call = call->prev_execute_data;
53 } while (call);
54 generator->execute_data->call = prev_call;
55 efree(generator->frozen_call_stack);
56 generator->frozen_call_stack = NULL;
57 }
58 /* }}} */
59
zend_generator_freeze_call_stack(zend_execute_data * execute_data)60 ZEND_API zend_execute_data* zend_generator_freeze_call_stack(zend_execute_data *execute_data) /* {{{ */
61 {
62 size_t used_stack;
63 zend_execute_data *call, *new_call, *prev_call = NULL;
64 zval *stack;
65
66 /* calculate required stack size */
67 used_stack = 0;
68 call = EX(call);
69 do {
70 used_stack += ZEND_CALL_FRAME_SLOT + ZEND_CALL_NUM_ARGS(call);
71 call = call->prev_execute_data;
72 } while (call);
73
74 stack = emalloc(used_stack * sizeof(zval));
75
76 /* save stack, linking frames in reverse order */
77 call = EX(call);
78 do {
79 size_t frame_size = ZEND_CALL_FRAME_SLOT + ZEND_CALL_NUM_ARGS(call);
80
81 new_call = (zend_execute_data*)(stack + used_stack - frame_size);
82 memcpy(new_call, call, frame_size * sizeof(zval));
83 used_stack -= frame_size;
84 new_call->prev_execute_data = prev_call;
85 prev_call = new_call;
86
87 new_call = call->prev_execute_data;
88 zend_vm_stack_free_call_frame(call);
89 call = new_call;
90 } while (call);
91
92 execute_data->call = NULL;
93 ZEND_ASSERT(prev_call == (zend_execute_data*)stack);
94
95 return prev_call;
96 }
97 /* }}} */
98
zend_generator_cleanup_unfinished_execution(zend_generator * generator,uint32_t catch_op_num)99 static void zend_generator_cleanup_unfinished_execution(
100 zend_generator *generator, uint32_t catch_op_num) /* {{{ */
101 {
102 zend_execute_data *execute_data = generator->execute_data;
103
104 if (execute_data->opline != execute_data->func->op_array.opcodes) {
105 /* -1 required because we want the last run opcode, not the next to-be-run one. */
106 uint32_t op_num = execute_data->opline - execute_data->func->op_array.opcodes - 1;
107
108 if (UNEXPECTED(generator->frozen_call_stack)) {
109 zend_generator_restore_call_stack(generator);
110 }
111 zend_cleanup_unfinished_execution(execute_data, op_num, catch_op_num);
112 }
113 }
114 /* }}} */
115
zend_generator_close(zend_generator * generator,zend_bool finished_execution)116 ZEND_API void zend_generator_close(zend_generator *generator, zend_bool finished_execution) /* {{{ */
117 {
118 if (EXPECTED(generator->execute_data)) {
119 zend_execute_data *execute_data = generator->execute_data;
120
121 if (EX_CALL_INFO() & ZEND_CALL_HAS_SYMBOL_TABLE) {
122 zend_clean_and_cache_symbol_table(execute_data->symbol_table);
123 }
124 /* always free the CV's, in the symtable are only not-free'd IS_INDIRECT's */
125 zend_free_compiled_variables(execute_data);
126
127 if (EX_CALL_INFO() & ZEND_CALL_RELEASE_THIS) {
128 OBJ_RELEASE(Z_OBJ(execute_data->This));
129 }
130
131 /* A fatal error / die occurred during the generator execution.
132 * Trying to clean up the stack may not be safe in this case. */
133 if (UNEXPECTED(CG(unclean_shutdown))) {
134 generator->execute_data = NULL;
135 return;
136 }
137
138 zend_vm_stack_free_extra_args(generator->execute_data);
139
140 /* Some cleanups are only necessary if the generator was closed
141 * before it could finish execution (reach a return statement). */
142 if (UNEXPECTED(!finished_execution)) {
143 zend_generator_cleanup_unfinished_execution(generator, 0);
144 }
145
146 /* Free closure object */
147 if (EX_CALL_INFO() & ZEND_CALL_CLOSURE) {
148 OBJ_RELEASE((zend_object *) EX(func)->common.prototype);
149 }
150
151 /* Free GC buffer. GC for closed generators doesn't need an allocated buffer */
152 if (generator->gc_buffer) {
153 efree(generator->gc_buffer);
154 generator->gc_buffer = NULL;
155 }
156
157 efree(generator->execute_data);
158 generator->execute_data = NULL;
159 }
160 }
161 /* }}} */
162
163 static zend_generator *zend_generator_get_child(zend_generator_node *node, zend_generator *leaf);
164
zend_generator_dtor_storage(zend_object * object)165 static void zend_generator_dtor_storage(zend_object *object) /* {{{ */
166 {
167 zend_generator *generator = (zend_generator*) object;
168 zend_execute_data *ex = generator->execute_data;
169 uint32_t op_num, finally_op_num, finally_op_end;
170 int i;
171
172 /* leave yield from mode to properly allow finally execution */
173 if (UNEXPECTED(Z_TYPE(generator->values) != IS_UNDEF)) {
174 zval_ptr_dtor(&generator->values);
175 ZVAL_UNDEF(&generator->values);
176 }
177
178 if (EXPECTED(generator->node.children == 0)) {
179 zend_generator *root = generator->node.ptr.root, *next;
180 while (UNEXPECTED(root != generator)) {
181 next = zend_generator_get_child(&root->node, generator);
182 generator->node.ptr.root = next;
183 next->node.parent = NULL;
184 OBJ_RELEASE(&root->std);
185 root = next;
186 }
187 }
188
189 if (EXPECTED(!ex) || EXPECTED(!(ex->func->op_array.fn_flags & ZEND_ACC_HAS_FINALLY_BLOCK))
190 || CG(unclean_shutdown)) {
191 return;
192 }
193
194 /* -1 required because we want the last run opcode, not the
195 * next to-be-run one. */
196 op_num = ex->opline - ex->func->op_array.opcodes - 1;
197
198 /* Find next finally block */
199 finally_op_num = 0;
200 finally_op_end = 0;
201 for (i = 0; i < ex->func->op_array.last_try_catch; i++) {
202 zend_try_catch_element *try_catch = &ex->func->op_array.try_catch_array[i];
203
204 if (op_num < try_catch->try_op) {
205 break;
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;
211 }
212 }
213
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;
218
219 zend_generator_cleanup_unfinished_execution(generator, finally_op_num);
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 fast_call->u2.lineno = (uint32_t)-1;
225
226 ex->opline = &ex->func->op_array.opcodes[finally_op_num];
227 generator->flags |= ZEND_GENERATOR_FORCED_CLOSE;
228 zend_generator_resume(generator);
229 }
230 }
231 /* }}} */
232
zend_generator_free_storage(zend_object * object)233 static void zend_generator_free_storage(zend_object *object) /* {{{ */
234 {
235 zend_generator *generator = (zend_generator*) object;
236
237 zend_generator_close(generator, 0);
238
239 /* we can't immediately free them in zend_generator_close() else yield from won't be able to fetch it */
240 zval_ptr_dtor(&generator->value);
241 zval_ptr_dtor(&generator->key);
242
243 if (EXPECTED(!Z_ISUNDEF(generator->retval))) {
244 zval_ptr_dtor(&generator->retval);
245 }
246
247 if (UNEXPECTED(generator->node.children > 4)) {
248 zend_hash_destroy(&generator->node.child.ht);
249 }
250
251 zend_object_std_dtor(&generator->std);
252
253 if (generator->iterator) {
254 zend_iterator_dtor(generator->iterator);
255 }
256 }
257 /* }}} */
258
calc_gc_buffer_size(zend_generator * generator)259 static uint32_t calc_gc_buffer_size(zend_generator *generator) /* {{{ */
260 {
261 uint32_t size = 4; /* value, key, retval, values */
262 if (generator->execute_data) {
263 zend_execute_data *execute_data = generator->execute_data;
264 zend_op_array *op_array = &EX(func)->op_array;
265
266 /* Compiled variables */
267 if (!(EX_CALL_INFO() & ZEND_CALL_HAS_SYMBOL_TABLE)) {
268 size += op_array->last_var;
269 }
270 /* Extra args */
271 if (EX_CALL_INFO() & ZEND_CALL_FREE_EXTRA_ARGS) {
272 size += EX_NUM_ARGS() - op_array->num_args;
273 }
274 size += Z_TYPE(execute_data->This) == IS_OBJECT; /* $this */
275 size += (EX_CALL_INFO() & ZEND_CALL_CLOSURE) != 0; /* Closure object */
276
277 /* Live vars */
278 if (execute_data->opline != op_array->opcodes) {
279 /* -1 required because we want the last run opcode, not the next to-be-run one. */
280 uint32_t i, op_num = execute_data->opline - op_array->opcodes - 1;
281 for (i = 0; i < op_array->last_live_range; i++) {
282 const zend_live_range *range = &op_array->live_range[i];
283 if (range->start > op_num) {
284 /* Further ranges will not be relevant... */
285 break;
286 } else if (op_num < range->end) {
287 /* LIVE_ROPE and LIVE_SILENCE not relevant for GC */
288 uint32_t kind = range->var & ZEND_LIVE_MASK;
289 if (kind == ZEND_LIVE_TMPVAR || kind == ZEND_LIVE_LOOP) {
290 size++;
291 }
292 }
293 }
294 }
295
296 /* Yield from root references */
297 if (generator->node.children == 0) {
298 zend_generator *root = generator->node.ptr.root;
299 while (root != generator) {
300 root = zend_generator_get_child(&root->node, generator);
301 size++;
302 }
303 }
304 }
305 return size;
306 }
307 /* }}} */
308
zend_generator_get_gc(zval * object,zval ** table,int * n)309 static HashTable *zend_generator_get_gc(zval *object, zval **table, int *n) /* {{{ */
310 {
311 zend_generator *generator = (zend_generator*) Z_OBJ_P(object);
312 zend_execute_data *execute_data = generator->execute_data;
313 zend_op_array *op_array;
314 zval *gc_buffer;
315 uint32_t gc_buffer_size;
316
317 if (!execute_data) {
318 /* If the generator has been closed, it can only hold on to three values: The value, key
319 * and retval. These three zvals are stored sequentially starting at &generator->value. */
320 *table = &generator->value;
321 *n = 3;
322 return NULL;
323 }
324
325 op_array = &EX(func)->op_array;
326 gc_buffer_size = calc_gc_buffer_size(generator);
327 if (generator->gc_buffer_size < gc_buffer_size) {
328 generator->gc_buffer = safe_erealloc(generator->gc_buffer, sizeof(zval), gc_buffer_size, 0);
329 generator->gc_buffer_size = gc_buffer_size;
330 }
331
332 *n = gc_buffer_size;
333 *table = gc_buffer = generator->gc_buffer;
334
335 ZVAL_COPY_VALUE(gc_buffer++, &generator->value);
336 ZVAL_COPY_VALUE(gc_buffer++, &generator->key);
337 ZVAL_COPY_VALUE(gc_buffer++, &generator->retval);
338 ZVAL_COPY_VALUE(gc_buffer++, &generator->values);
339
340 if (!(EX_CALL_INFO() & ZEND_CALL_HAS_SYMBOL_TABLE)) {
341 uint32_t i, num_cvs = EX(func)->op_array.last_var;
342 for (i = 0; i < num_cvs; i++) {
343 ZVAL_COPY_VALUE(gc_buffer++, EX_VAR_NUM(i));
344 }
345 }
346
347 if (EX_CALL_INFO() & ZEND_CALL_FREE_EXTRA_ARGS) {
348 zval *zv = EX_VAR_NUM(op_array->last_var + op_array->T);
349 zval *end = zv + (EX_NUM_ARGS() - op_array->num_args);
350 while (zv != end) {
351 ZVAL_COPY_VALUE(gc_buffer++, zv++);
352 }
353 }
354
355 if (Z_TYPE(execute_data->This) == IS_OBJECT) {
356 ZVAL_OBJ(gc_buffer++, Z_OBJ(execute_data->This));
357 }
358 if (EX_CALL_INFO() & ZEND_CALL_CLOSURE) {
359 ZVAL_OBJ(gc_buffer++, (zend_object *) EX(func)->common.prototype);
360 }
361
362 if (execute_data->opline != op_array->opcodes) {
363 uint32_t i, op_num = execute_data->opline - op_array->opcodes - 1;
364 for (i = 0; i < op_array->last_live_range; i++) {
365 const zend_live_range *range = &op_array->live_range[i];
366 if (range->start > op_num) {
367 break;
368 } else if (op_num < range->end) {
369 uint32_t kind = range->var & ZEND_LIVE_MASK;
370 uint32_t var_num = range->var & ~ZEND_LIVE_MASK;
371 zval *var = EX_VAR(var_num);
372 if (kind == ZEND_LIVE_TMPVAR || kind == ZEND_LIVE_LOOP) {
373 ZVAL_COPY_VALUE(gc_buffer++, var);
374 }
375 }
376 }
377 }
378
379 if (generator->node.children == 0) {
380 zend_generator *root = generator->node.ptr.root;
381 while (root != generator) {
382 ZVAL_OBJ(gc_buffer++, &root->std);
383 root = zend_generator_get_child(&root->node, generator);
384 }
385 }
386
387 if (EX_CALL_INFO() & ZEND_CALL_HAS_SYMBOL_TABLE) {
388 return execute_data->symbol_table;
389 } else {
390 return NULL;
391 }
392 }
393 /* }}} */
394
zend_generator_create(zend_class_entry * class_type)395 static zend_object *zend_generator_create(zend_class_entry *class_type) /* {{{ */
396 {
397 zend_generator *generator;
398
399 generator = emalloc(sizeof(zend_generator));
400 memset(generator, 0, sizeof(zend_generator));
401
402 /* The key will be incremented on first use, so it'll start at 0 */
403 generator->largest_used_integer_key = -1;
404
405 ZVAL_UNDEF(&generator->retval);
406 ZVAL_UNDEF(&generator->values);
407
408 /* By default we have a tree of only one node */
409 generator->node.parent = NULL;
410 generator->node.children = 0;
411 generator->node.ptr.root = generator;
412
413 zend_object_std_init(&generator->std, class_type);
414 generator->std.handlers = &zend_generator_handlers;
415
416 return (zend_object*)generator;
417 }
418 /* }}} */
419
zend_generator_get_constructor(zend_object * object)420 static ZEND_COLD zend_function *zend_generator_get_constructor(zend_object *object) /* {{{ */
421 {
422 zend_throw_error(NULL, "The \"Generator\" class is reserved for internal use and cannot be manually instantiated");
423
424 return NULL;
425 }
426 /* }}} */
427
zend_generator_check_placeholder_frame(zend_execute_data * ptr)428 ZEND_API zend_execute_data *zend_generator_check_placeholder_frame(zend_execute_data *ptr)
429 {
430 if (!ptr->func && Z_TYPE(ptr->This) == IS_OBJECT) {
431 if (Z_OBJCE(ptr->This) == zend_ce_generator) {
432 zend_generator *generator = (zend_generator *) Z_OBJ(ptr->This);
433 zend_generator *root = (generator->node.children < 1 ? generator : generator->node.ptr.leaf)->node.ptr.root;
434 zend_execute_data *prev = ptr->prev_execute_data;
435 if (generator->node.parent != root) {
436 do {
437 generator->execute_data->prev_execute_data = prev;
438 prev = generator->execute_data;
439 generator = generator->node.parent;
440 } while (generator->node.parent != root);
441 }
442 generator->execute_data->prev_execute_data = prev;
443 ptr = generator->execute_data;
444 }
445 }
446 return ptr;
447 }
448
zend_generator_throw_exception(zend_generator * generator,zval * exception)449 static void zend_generator_throw_exception(zend_generator *generator, zval *exception)
450 {
451 zend_execute_data *original_execute_data = EG(current_execute_data);
452
453 /* if we don't stop an array/iterator yield from, the exception will only reach the generator after the values were all iterated over */
454 if (UNEXPECTED(Z_TYPE(generator->values) != IS_UNDEF)) {
455 zval_ptr_dtor(&generator->values);
456 ZVAL_UNDEF(&generator->values);
457 }
458
459 /* Throw the exception in the context of the generator. Decrementing the opline
460 * to pretend the exception happened during the YIELD opcode. */
461 EG(current_execute_data) = generator->execute_data;
462 generator->execute_data->opline--;
463 if (exception) {
464 zend_throw_exception_object(exception);
465 } else {
466 zend_throw_exception_internal(NULL);
467 }
468 generator->execute_data->opline++;
469 EG(current_execute_data) = original_execute_data;
470 }
471
zend_generator_get_child(zend_generator_node * node,zend_generator * leaf)472 static zend_generator *zend_generator_get_child(zend_generator_node *node, zend_generator *leaf)
473 {
474 switch (node->children) {
475 case 0:
476 return NULL;
477 case 1:
478 return node->child.array[0].child;
479
480 #define ZEND_GEN_GET_CHILD(x) \
481 if (node->child.array[x].leaf == leaf) { \
482 return node->child.array[x].child; \
483 }
484 case 4:
485 ZEND_GEN_GET_CHILD(3)
486 case 3:
487 ZEND_GEN_GET_CHILD(2)
488 case 2:
489 ZEND_GEN_GET_CHILD(1)
490 ZEND_GEN_GET_CHILD(0)
491 ZEND_ASSERT(0); // we never should have no matching child
492 }
493
494 return zend_hash_index_find_ptr(&node->child.ht, (zend_ulong) leaf);
495 }
496
zend_generator_search_multi_children_node(zend_generator_node * node)497 static zend_generator_node *zend_generator_search_multi_children_node(zend_generator_node *node)
498 {
499 while (node->children == 1) {
500 node = &node->child.array[0].child->node;
501 }
502 return node->children > 1 ? node : NULL;
503 }
504
zend_generator_add_single_child(zend_generator_node * node,zend_generator * child,zend_generator * leaf)505 static void zend_generator_add_single_child(zend_generator_node *node, zend_generator *child, zend_generator *leaf)
506 {
507 if (node->children < 4) {
508 node->child.array[node->children].leaf = leaf;
509 node->child.array[node->children].child = child;
510 } else if (node->children > 4) {
511 zend_hash_index_add_ptr(&node->child.ht, (zend_ulong) leaf, child);
512 } else {
513 struct {
514 zend_generator *leaf;
515 zend_generator *child;
516 } array[4];
517 int i;
518
519 memcpy(&array, &node->child.array, sizeof(array));
520 zend_hash_init(&node->child.ht, 5, sigh, NULL, 0);
521 for (i = 0; i < 4; i++) {
522 zend_hash_index_add_ptr(&node->child.ht, (zend_ulong) array[i].leaf, array[i].child);
523 }
524 zend_hash_index_add_ptr(&node->child.ht, (zend_ulong) leaf, child);
525 }
526
527 node->children++;
528 }
529
zend_generator_merge_child_nodes(zend_generator_node * dest,zend_generator_node * src,zend_generator * child)530 static void zend_generator_merge_child_nodes(zend_generator_node *dest, zend_generator_node *src, zend_generator *child)
531 {
532 if (src->children <= 4) {
533 int i = src->children;
534 while (i--) {
535 zend_generator_add_single_child(dest, child, src->child.array[i].leaf);
536 }
537 } else {
538 zend_ulong leaf;
539 ZEND_HASH_FOREACH_NUM_KEY(&src->child.ht, leaf) {
540 zend_generator_add_single_child(dest, child, (zend_generator *) leaf);
541 } ZEND_HASH_FOREACH_END();
542 }
543 }
544
545 /* Make attention so that the root of each subtree of the Generators tree is referenced once per leaf */
zend_generator_add_child(zend_generator * generator,zend_generator * child)546 static void zend_generator_add_child(zend_generator *generator, zend_generator *child)
547 {
548 zend_generator *leaf = child->node.children ? child->node.ptr.leaf : child;
549 zend_generator_node *multi_children_node;
550 zend_bool was_leaf = generator->node.children == 0;
551
552 if (was_leaf) {
553 zend_generator *next = generator->node.parent;
554 leaf->node.ptr.root = generator->node.ptr.root;
555 ++GC_REFCOUNT(&generator->std); /* we need to increment the generator refcount here as it became integrated into the tree (no leaf), but we must not increment the refcount of the *whole* path in tree */
556 generator->node.ptr.leaf = leaf;
557
558 while (next) {
559 if (next->node.children > 1) {
560 if (next->node.children > 4) {
561 zend_generator *child = zend_hash_index_find_ptr(&next->node.child.ht, (zend_ulong) generator);
562 zend_hash_index_del(&next->node.child.ht, (zend_ulong) generator);
563 zend_hash_index_add_ptr(&next->node.child.ht, (zend_ulong) leaf, child);
564 } else {
565 switch (next->node.children) {
566 #define ZEND_GEN_UPDATE_CHILD(x) \
567 if (next->node.child.array[x].leaf == generator) { \
568 next->node.child.array[x].leaf = leaf; \
569 break; \
570 }
571 case 4:
572 ZEND_GEN_UPDATE_CHILD(3)
573 case 3:
574 ZEND_GEN_UPDATE_CHILD(2)
575 case 2:
576 ZEND_GEN_UPDATE_CHILD(1)
577 ZEND_GEN_UPDATE_CHILD(0)
578 ZEND_ASSERT(0); // we never should have no matching child
579 }
580 }
581 }
582
583 next->node.ptr.leaf = leaf;
584 next = next->node.parent;
585 }
586
587 zend_generator_add_single_child(&generator->node, child, leaf);
588 } else if (generator->node.children == 1) {
589 multi_children_node = zend_generator_search_multi_children_node(&generator->node);
590 if (multi_children_node) {
591 generator->node.children = 0;
592 zend_generator_merge_child_nodes(&generator->node, multi_children_node, generator->node.child.array[0].child);
593 }
594 }
595
596 if (!was_leaf) {
597 multi_children_node = zend_generator_search_multi_children_node(&child->node);
598 } else {
599 multi_children_node = (zend_generator_node *) 0x1;
600 }
601
602 {
603 zend_generator *parent = generator->node.parent, *cur = generator;
604
605 if (multi_children_node > (zend_generator_node *) 0x1) {
606 zend_generator_merge_child_nodes(&generator->node, multi_children_node, child);
607 } else {
608 zend_generator_add_single_child(&generator->node, child, leaf);
609 }
610 while (parent) {
611 if (parent->node.children > 1) {
612 if (multi_children_node == (zend_generator_node *) 0x1) {
613 multi_children_node = zend_generator_search_multi_children_node(&child->node);
614 }
615 if (multi_children_node) {
616 zend_generator_merge_child_nodes(&parent->node, multi_children_node, cur);
617 } else {
618 zend_generator_add_single_child(&parent->node, cur, leaf);
619 }
620 }
621 cur = parent;
622 parent = parent->node.parent;
623 }
624 }
625 }
626
zend_generator_yield_from(zend_generator * generator,zend_generator * from)627 void zend_generator_yield_from(zend_generator *generator, zend_generator *from)
628 {
629 zend_generator_add_child(from, generator);
630
631 generator->node.parent = from;
632 zend_generator_get_current(generator);
633 --GC_REFCOUNT(&from->std);
634 }
635
zend_generator_update_current(zend_generator * generator,zend_generator * leaf)636 ZEND_API zend_generator *zend_generator_update_current(zend_generator *generator, zend_generator *leaf)
637 {
638 zend_generator *old_root, *root = leaf->node.ptr.root;
639
640 /* generator at the root had stopped */
641 if (root != generator) {
642 old_root = root;
643 root = zend_generator_get_child(&root->node, leaf);
644 } else {
645 old_root = NULL;
646 }
647
648 while (!root->execute_data && root != generator) {
649 OBJ_RELEASE(&old_root->std);
650 old_root = root;
651
652 root = zend_generator_get_child(&root->node, leaf);
653 }
654
655 if (root->node.parent) {
656 if (root->node.parent->execute_data == NULL) {
657 if (EXPECTED(EG(exception) == NULL)) {
658 zend_op *yield_from = (zend_op *) root->execute_data->opline - 1;
659
660 if (yield_from->opcode == ZEND_YIELD_FROM) {
661 if (Z_ISUNDEF(root->node.parent->retval)) {
662 /* Throw the exception in the context of the generator */
663 zend_execute_data *original_execute_data = EG(current_execute_data);
664 EG(current_execute_data) = root->execute_data;
665
666 if (root == generator) {
667 root->execute_data->prev_execute_data = original_execute_data;
668 } else {
669 root->execute_data->prev_execute_data = &generator->execute_fake;
670 generator->execute_fake.prev_execute_data = original_execute_data;
671 }
672
673 root->execute_data->opline--; /* ZEND_YIELD(_FROM) already advance, so decrement opline to throw from correct place */
674 zend_throw_exception(zend_ce_ClosedGeneratorException, "Generator yielded from aborted, no return value available", 0);
675
676 EG(current_execute_data) = original_execute_data;
677
678 if (!((old_root ? old_root : generator)->flags & ZEND_GENERATOR_CURRENTLY_RUNNING)) {
679 leaf->node.ptr.root = root;
680 root->node.parent = NULL;
681 if (old_root) {
682 OBJ_RELEASE(&old_root->std);
683 }
684 zend_generator_resume(leaf);
685 return leaf->node.ptr.root; /* this may be updated during zend_generator_resume! */
686 }
687 } else {
688 zval_ptr_dtor(&root->value);
689 ZVAL_COPY(&root->value, &root->node.parent->value);
690 ZVAL_COPY(ZEND_CALL_VAR(root->execute_data, yield_from->result.var), &root->node.parent->retval);
691 }
692 }
693 }
694
695 root->node.parent = NULL;
696 } else {
697 do {
698 root = root->node.parent;
699 ++GC_REFCOUNT(&root->std);
700 } while (root->node.parent);
701 }
702 }
703
704 leaf->node.ptr.root = root;
705 if (old_root) {
706 OBJ_RELEASE(&old_root->std);
707 }
708
709 return root;
710 }
711
zend_generator_get_next_delegated_value(zend_generator * generator)712 static int zend_generator_get_next_delegated_value(zend_generator *generator) /* {{{ */
713 {
714 zval *value;
715 if (Z_TYPE(generator->values) == IS_ARRAY) {
716 HashTable *ht = Z_ARR(generator->values);
717 HashPosition pos = Z_FE_POS(generator->values);
718
719 Bucket *p;
720 do {
721 if (UNEXPECTED(pos >= ht->nNumUsed)) {
722 /* Reached end of array */
723 goto failure;
724 }
725
726 p = &ht->arData[pos];
727 value = &p->val;
728 if (Z_TYPE_P(value) == IS_INDIRECT) {
729 value = Z_INDIRECT_P(value);
730 }
731 pos++;
732 } while (Z_ISUNDEF_P(value));
733
734 zval_ptr_dtor(&generator->value);
735 ZVAL_COPY(&generator->value, value);
736
737 zval_ptr_dtor(&generator->key);
738 if (p->key) {
739 ZVAL_STR_COPY(&generator->key, p->key);
740 } else {
741 ZVAL_LONG(&generator->key, p->h);
742 }
743
744 Z_FE_POS(generator->values) = pos;
745 } else {
746 zend_object_iterator *iter = (zend_object_iterator *) Z_OBJ(generator->values);
747
748 if (iter->index++ > 0) {
749 iter->funcs->move_forward(iter);
750 if (UNEXPECTED(EG(exception) != NULL)) {
751 goto exception;
752 }
753 }
754
755 if (iter->funcs->valid(iter) == FAILURE) {
756 /* reached end of iteration */
757 goto failure;
758 }
759
760 value = iter->funcs->get_current_data(iter);
761 if (UNEXPECTED(EG(exception) != NULL)) {
762 goto exception;
763 } else if (UNEXPECTED(!value)) {
764 goto failure;
765 }
766
767 zval_ptr_dtor(&generator->value);
768 ZVAL_COPY(&generator->value, value);
769
770 zval_ptr_dtor(&generator->key);
771 if (iter->funcs->get_current_key) {
772 iter->funcs->get_current_key(iter, &generator->key);
773 if (UNEXPECTED(EG(exception) != NULL)) {
774 ZVAL_UNDEF(&generator->key);
775 goto exception;
776 }
777 } else {
778 ZVAL_LONG(&generator->key, iter->index);
779 }
780 }
781 return SUCCESS;
782
783 exception: {
784 zend_execute_data *ex = EG(current_execute_data);
785 EG(current_execute_data) = generator->execute_data;
786 zend_throw_exception_internal(NULL);
787 EG(current_execute_data) = ex;
788 }
789
790 failure:
791 zval_ptr_dtor(&generator->values);
792 ZVAL_UNDEF(&generator->values);
793 return FAILURE;
794 }
795 /* }}} */
796
zend_generator_resume(zend_generator * orig_generator)797 ZEND_API void zend_generator_resume(zend_generator *orig_generator) /* {{{ */
798 {
799 zend_generator *generator = zend_generator_get_current(orig_generator);
800
801 /* The generator is already closed, thus can't resume */
802 if (UNEXPECTED(!generator->execute_data)) {
803 return;
804 }
805
806 try_again:
807 if (generator->flags & ZEND_GENERATOR_CURRENTLY_RUNNING) {
808 zend_throw_error(NULL, "Cannot resume an already running generator");
809 return;
810 }
811
812 if (UNEXPECTED((orig_generator->flags & ZEND_GENERATOR_DO_INIT) != 0 && !Z_ISUNDEF(generator->value))) {
813 /* We must not advance Generator if we yield from a Generator being currently run */
814 return;
815 }
816
817 if (UNEXPECTED(!Z_ISUNDEF(generator->values))) {
818 if (EXPECTED(zend_generator_get_next_delegated_value(generator) == SUCCESS)) {
819 return;
820 }
821 /* If there are no more deletegated values, resume the generator
822 * after the "yield from" expression. */
823 }
824
825 /* Drop the AT_FIRST_YIELD flag */
826 orig_generator->flags &= ~ZEND_GENERATOR_AT_FIRST_YIELD;
827
828 {
829 /* Backup executor globals */
830 zend_execute_data *original_execute_data = EG(current_execute_data);
831
832 /* Set executor globals */
833 EG(current_execute_data) = generator->execute_data;
834
835 /* We want the backtrace to look as if the generator function was
836 * called from whatever method we are current running (e.g. next()).
837 * So we have to link generator call frame with caller call frame. */
838 if (generator == orig_generator) {
839 generator->execute_data->prev_execute_data = original_execute_data;
840 } else {
841 /* We need some execute_data placeholder in stacktrace to be replaced
842 * by the real stack trace when needed */
843 generator->execute_data->prev_execute_data = &orig_generator->execute_fake;
844 orig_generator->execute_fake.prev_execute_data = original_execute_data;
845 }
846
847 if (UNEXPECTED(generator->frozen_call_stack)) {
848 /* Restore frozen call-stack */
849 zend_generator_restore_call_stack(generator);
850 }
851
852 /* Resume execution */
853 generator->flags |= ZEND_GENERATOR_CURRENTLY_RUNNING;
854 zend_execute_ex(generator->execute_data);
855 generator->flags &= ~ZEND_GENERATOR_CURRENTLY_RUNNING;
856
857 generator->frozen_call_stack = NULL;
858 if (EXPECTED(generator->execute_data) &&
859 UNEXPECTED(generator->execute_data->call)) {
860 /* Frize call-stack */
861 generator->frozen_call_stack = zend_generator_freeze_call_stack(generator->execute_data);
862 }
863
864 /* Restore executor globals */
865 EG(current_execute_data) = original_execute_data;
866
867 /* If an exception was thrown in the generator we have to internally
868 * rethrow it in the parent scope.
869 * In case we did yield from, the Exception must be rethrown into
870 * its calling frame (see above in if (check_yield_from). */
871 if (UNEXPECTED(EG(exception) != NULL)) {
872 if (generator == orig_generator) {
873 zend_generator_close(generator, 0);
874 zend_throw_exception_internal(NULL);
875 } else {
876 generator = zend_generator_get_current(orig_generator);
877 zend_generator_throw_exception(generator, NULL);
878 goto try_again;
879 }
880 }
881
882 /* yield from was used, try another resume. */
883 if (UNEXPECTED((generator != orig_generator && !Z_ISUNDEF(generator->retval)) || (generator->execute_data && (generator->execute_data->opline - 1)->opcode == ZEND_YIELD_FROM))) {
884 generator = zend_generator_get_current(orig_generator);
885 goto try_again;
886 }
887 }
888 }
889 /* }}} */
890
zend_generator_ensure_initialized(zend_generator * generator)891 static inline void zend_generator_ensure_initialized(zend_generator *generator) /* {{{ */
892 {
893 if (UNEXPECTED(Z_TYPE(generator->value) == IS_UNDEF) && EXPECTED(generator->execute_data) && EXPECTED(generator->node.parent == NULL)) {
894 generator->flags |= ZEND_GENERATOR_DO_INIT;
895 zend_generator_resume(generator);
896 generator->flags &= ~ZEND_GENERATOR_DO_INIT;
897 generator->flags |= ZEND_GENERATOR_AT_FIRST_YIELD;
898 }
899 }
900 /* }}} */
901
zend_generator_rewind(zend_generator * generator)902 static inline void zend_generator_rewind(zend_generator *generator) /* {{{ */
903 {
904 zend_generator_ensure_initialized(generator);
905
906 if (!(generator->flags & ZEND_GENERATOR_AT_FIRST_YIELD)) {
907 zend_throw_exception(NULL, "Cannot rewind a generator that was already run", 0);
908 }
909 }
910 /* }}} */
911
912 /* {{{ proto void Generator::rewind()
913 * Rewind the generator */
ZEND_METHOD(Generator,rewind)914 ZEND_METHOD(Generator, rewind)
915 {
916 zend_generator *generator;
917
918 if (zend_parse_parameters_none() == FAILURE) {
919 return;
920 }
921
922 generator = (zend_generator *) Z_OBJ_P(getThis());
923
924 zend_generator_rewind(generator);
925 }
926 /* }}} */
927
928 /* {{{ proto bool Generator::valid()
929 * Check whether the generator is valid */
ZEND_METHOD(Generator,valid)930 ZEND_METHOD(Generator, valid)
931 {
932 zend_generator *generator;
933
934 if (zend_parse_parameters_none() == FAILURE) {
935 return;
936 }
937
938 generator = (zend_generator *) Z_OBJ_P(getThis());
939
940 zend_generator_ensure_initialized(generator);
941
942 zend_generator_get_current(generator);
943
944 RETURN_BOOL(EXPECTED(generator->execute_data != NULL));
945 }
946 /* }}} */
947
948 /* {{{ proto mixed Generator::current()
949 * Get the current value */
ZEND_METHOD(Generator,current)950 ZEND_METHOD(Generator, current)
951 {
952 zend_generator *generator, *root;
953
954 if (zend_parse_parameters_none() == FAILURE) {
955 return;
956 }
957
958 generator = (zend_generator *) Z_OBJ_P(getThis());
959
960 zend_generator_ensure_initialized(generator);
961
962 root = zend_generator_get_current(generator);
963 if (EXPECTED(generator->execute_data != NULL && Z_TYPE(root->value) != IS_UNDEF)) {
964 zval *value = &root->value;
965
966 ZVAL_DEREF(value);
967 ZVAL_COPY(return_value, value);
968 }
969 }
970 /* }}} */
971
972 /* {{{ proto mixed Generator::key()
973 * Get the current key */
ZEND_METHOD(Generator,key)974 ZEND_METHOD(Generator, key)
975 {
976 zend_generator *generator, *root;
977
978 if (zend_parse_parameters_none() == FAILURE) {
979 return;
980 }
981
982 generator = (zend_generator *) Z_OBJ_P(getThis());
983
984 zend_generator_ensure_initialized(generator);
985
986 root = zend_generator_get_current(generator);
987 if (EXPECTED(generator->execute_data != NULL && Z_TYPE(root->key) != IS_UNDEF)) {
988 zval *key = &root->key;
989
990 ZVAL_DEREF(key);
991 ZVAL_COPY(return_value, key);
992 }
993 }
994 /* }}} */
995
996 /* {{{ proto void Generator::next()
997 * Advances the generator */
ZEND_METHOD(Generator,next)998 ZEND_METHOD(Generator, next)
999 {
1000 zend_generator *generator;
1001
1002 if (zend_parse_parameters_none() == FAILURE) {
1003 return;
1004 }
1005
1006 generator = (zend_generator *) Z_OBJ_P(getThis());
1007
1008 zend_generator_ensure_initialized(generator);
1009
1010 zend_generator_resume(generator);
1011 }
1012 /* }}} */
1013
1014 /* {{{ proto mixed Generator::send(mixed value)
1015 * Sends a value to the generator */
ZEND_METHOD(Generator,send)1016 ZEND_METHOD(Generator, send)
1017 {
1018 zval *value;
1019 zend_generator *generator, *root;
1020
1021 ZEND_PARSE_PARAMETERS_START(1, 1)
1022 Z_PARAM_ZVAL(value)
1023 ZEND_PARSE_PARAMETERS_END();
1024
1025 generator = (zend_generator *) Z_OBJ_P(getThis());
1026
1027 zend_generator_ensure_initialized(generator);
1028
1029 /* The generator is already closed, thus can't send anything */
1030 if (UNEXPECTED(!generator->execute_data)) {
1031 return;
1032 }
1033
1034 root = zend_generator_get_current(generator);
1035 /* Put sent value in the target VAR slot, if it is used */
1036 if (root->send_target) {
1037 ZVAL_COPY(root->send_target, value);
1038 }
1039
1040 zend_generator_resume(generator);
1041
1042 root = zend_generator_get_current(generator);
1043 if (EXPECTED(generator->execute_data)) {
1044 zval *value = &root->value;
1045
1046 ZVAL_DEREF(value);
1047 ZVAL_COPY(return_value, value);
1048 }
1049 }
1050 /* }}} */
1051
1052 /* {{{ proto mixed Generator::throw(Exception exception)
1053 * Throws an exception into the generator */
ZEND_METHOD(Generator,throw)1054 ZEND_METHOD(Generator, throw)
1055 {
1056 zval *exception, exception_copy;
1057 zend_generator *generator;
1058
1059 ZEND_PARSE_PARAMETERS_START(1, 1)
1060 Z_PARAM_ZVAL(exception)
1061 ZEND_PARSE_PARAMETERS_END();
1062
1063 ZVAL_DUP(&exception_copy, exception);
1064
1065 generator = (zend_generator *) Z_OBJ_P(getThis());
1066
1067 zend_generator_ensure_initialized(generator);
1068
1069 if (generator->execute_data) {
1070 zend_generator *root = zend_generator_get_current(generator);
1071
1072 zend_generator_throw_exception(root, &exception_copy);
1073
1074 zend_generator_resume(generator);
1075
1076 root = zend_generator_get_current(generator);
1077 if (generator->execute_data) {
1078 zval *value = &root->value;
1079
1080 ZVAL_DEREF(value);
1081 ZVAL_COPY(return_value, value);
1082 }
1083 } else {
1084 /* If the generator is already closed throw the exception in the
1085 * current context */
1086 zend_throw_exception_object(&exception_copy);
1087 }
1088 }
1089 /* }}} */
1090
1091 /* {{{ proto mixed Generator::getReturn()
1092 * Retrieves the return value of the generator */
ZEND_METHOD(Generator,getReturn)1093 ZEND_METHOD(Generator, getReturn)
1094 {
1095 zend_generator *generator;
1096
1097 if (zend_parse_parameters_none() == FAILURE) {
1098 return;
1099 }
1100
1101 generator = (zend_generator *) Z_OBJ_P(getThis());
1102
1103 zend_generator_ensure_initialized(generator);
1104 if (UNEXPECTED(EG(exception))) {
1105 return;
1106 }
1107
1108 if (Z_ISUNDEF(generator->retval)) {
1109 /* Generator hasn't returned yet -> error! */
1110 zend_throw_exception(NULL,
1111 "Cannot get return value of a generator that hasn't returned", 0);
1112 return;
1113 }
1114
1115 ZVAL_COPY(return_value, &generator->retval);
1116 }
1117 /* }}} */
1118
1119 /* {{{ proto void Generator::__wakeup()
1120 * Throws an Exception as generators can't be serialized */
ZEND_METHOD(Generator,__wakeup)1121 ZEND_METHOD(Generator, __wakeup)
1122 {
1123 /* Just specifying the zend_class_unserialize_deny handler is not enough,
1124 * because it is only invoked for C unserialization. For O the error has
1125 * to be thrown in __wakeup. */
1126
1127 if (zend_parse_parameters_none() == FAILURE) {
1128 return;
1129 }
1130
1131 zend_throw_exception(NULL, "Unserialization of 'Generator' is not allowed", 0);
1132 }
1133 /* }}} */
1134
1135 /* get_iterator implementation */
1136
zend_generator_iterator_dtor(zend_object_iterator * iterator)1137 static void zend_generator_iterator_dtor(zend_object_iterator *iterator) /* {{{ */
1138 {
1139 zend_generator *generator = (zend_generator*)Z_OBJ(iterator->data);
1140 generator->iterator = NULL;
1141 zval_ptr_dtor(&iterator->data);
1142 zend_iterator_dtor(iterator);
1143 }
1144 /* }}} */
1145
zend_generator_iterator_valid(zend_object_iterator * iterator)1146 static int zend_generator_iterator_valid(zend_object_iterator *iterator) /* {{{ */
1147 {
1148 zend_generator *generator = (zend_generator*)Z_OBJ(iterator->data);
1149
1150 zend_generator_ensure_initialized(generator);
1151
1152 zend_generator_get_current(generator);
1153
1154 return generator->execute_data ? SUCCESS : FAILURE;
1155 }
1156 /* }}} */
1157
zend_generator_iterator_get_data(zend_object_iterator * iterator)1158 static zval *zend_generator_iterator_get_data(zend_object_iterator *iterator) /* {{{ */
1159 {
1160 zend_generator *generator = (zend_generator*)Z_OBJ(iterator->data), *root;
1161
1162 zend_generator_ensure_initialized(generator);
1163
1164 root = zend_generator_get_current(generator);
1165
1166 return &root->value;
1167 }
1168 /* }}} */
1169
zend_generator_iterator_get_key(zend_object_iterator * iterator,zval * key)1170 static void zend_generator_iterator_get_key(zend_object_iterator *iterator, zval *key) /* {{{ */
1171 {
1172 zend_generator *generator = (zend_generator*)Z_OBJ(iterator->data), *root;
1173
1174 zend_generator_ensure_initialized(generator);
1175
1176 root = zend_generator_get_current(generator);
1177
1178 if (EXPECTED(Z_TYPE(root->key) != IS_UNDEF)) {
1179 zval *zv = &root->key;
1180
1181 ZVAL_DEREF(zv);
1182 ZVAL_COPY(key, zv);
1183 } else {
1184 ZVAL_NULL(key);
1185 }
1186 }
1187 /* }}} */
1188
zend_generator_iterator_move_forward(zend_object_iterator * iterator)1189 static void zend_generator_iterator_move_forward(zend_object_iterator *iterator) /* {{{ */
1190 {
1191 zend_generator *generator = (zend_generator*)Z_OBJ(iterator->data);
1192
1193 zend_generator_ensure_initialized(generator);
1194
1195 zend_generator_resume(generator);
1196 }
1197 /* }}} */
1198
zend_generator_iterator_rewind(zend_object_iterator * iterator)1199 static void zend_generator_iterator_rewind(zend_object_iterator *iterator) /* {{{ */
1200 {
1201 zend_generator *generator = (zend_generator*)Z_OBJ(iterator->data);
1202
1203 zend_generator_rewind(generator);
1204 }
1205 /* }}} */
1206
1207 static zend_object_iterator_funcs zend_generator_iterator_functions = {
1208 zend_generator_iterator_dtor,
1209 zend_generator_iterator_valid,
1210 zend_generator_iterator_get_data,
1211 zend_generator_iterator_get_key,
1212 zend_generator_iterator_move_forward,
1213 zend_generator_iterator_rewind,
1214 NULL
1215 };
1216
zend_generator_get_iterator(zend_class_entry * ce,zval * object,int by_ref)1217 zend_object_iterator *zend_generator_get_iterator(zend_class_entry *ce, zval *object, int by_ref) /* {{{ */
1218 {
1219 zend_object_iterator *iterator;
1220 zend_generator *generator = (zend_generator*)Z_OBJ_P(object);
1221
1222 if (!generator->execute_data) {
1223 zend_throw_exception(NULL, "Cannot traverse an already closed generator", 0);
1224 return NULL;
1225 }
1226
1227 if (UNEXPECTED(by_ref) && !(generator->execute_data->func->op_array.fn_flags & ZEND_ACC_RETURN_REFERENCE)) {
1228 zend_throw_exception(NULL, "You can only iterate a generator by-reference if it declared that it yields by-reference", 0);
1229 return NULL;
1230 }
1231
1232 iterator = generator->iterator = emalloc(sizeof(zend_object_iterator));
1233
1234 zend_iterator_init(iterator);
1235
1236 iterator->funcs = &zend_generator_iterator_functions;
1237 ZVAL_COPY(&iterator->data, object);
1238
1239 return iterator;
1240 }
1241 /* }}} */
1242
1243 ZEND_BEGIN_ARG_INFO(arginfo_generator_void, 0)
1244 ZEND_END_ARG_INFO()
1245
1246 ZEND_BEGIN_ARG_INFO_EX(arginfo_generator_send, 0, 0, 1)
1247 ZEND_ARG_INFO(0, value)
1248 ZEND_END_ARG_INFO()
1249
1250 ZEND_BEGIN_ARG_INFO_EX(arginfo_generator_throw, 0, 0, 1)
1251 ZEND_ARG_INFO(0, exception)
1252 ZEND_END_ARG_INFO()
1253
1254 static const zend_function_entry generator_functions[] = {
1255 ZEND_ME(Generator, rewind, arginfo_generator_void, ZEND_ACC_PUBLIC)
1256 ZEND_ME(Generator, valid, arginfo_generator_void, ZEND_ACC_PUBLIC)
1257 ZEND_ME(Generator, current, arginfo_generator_void, ZEND_ACC_PUBLIC)
1258 ZEND_ME(Generator, key, arginfo_generator_void, ZEND_ACC_PUBLIC)
1259 ZEND_ME(Generator, next, arginfo_generator_void, ZEND_ACC_PUBLIC)
1260 ZEND_ME(Generator, send, arginfo_generator_send, ZEND_ACC_PUBLIC)
1261 ZEND_ME(Generator, throw, arginfo_generator_throw, ZEND_ACC_PUBLIC)
1262 ZEND_ME(Generator, getReturn,arginfo_generator_void, ZEND_ACC_PUBLIC)
1263 ZEND_ME(Generator, __wakeup, arginfo_generator_void, ZEND_ACC_PUBLIC)
1264 ZEND_FE_END
1265 };
1266
zend_register_generator_ce(void)1267 void zend_register_generator_ce(void) /* {{{ */
1268 {
1269 zend_class_entry ce;
1270
1271 INIT_CLASS_ENTRY(ce, "Generator", generator_functions);
1272 zend_ce_generator = zend_register_internal_class(&ce);
1273 zend_ce_generator->ce_flags |= ZEND_ACC_FINAL;
1274 zend_ce_generator->create_object = zend_generator_create;
1275 zend_ce_generator->serialize = zend_class_serialize_deny;
1276 zend_ce_generator->unserialize = zend_class_unserialize_deny;
1277
1278 /* get_iterator has to be assigned *after* implementing the inferface */
1279 zend_class_implements(zend_ce_generator, 1, zend_ce_iterator);
1280 zend_ce_generator->get_iterator = zend_generator_get_iterator;
1281 zend_ce_generator->iterator_funcs.funcs = &zend_generator_iterator_functions;
1282
1283 memcpy(&zend_generator_handlers, zend_get_std_object_handlers(), sizeof(zend_object_handlers));
1284 zend_generator_handlers.free_obj = zend_generator_free_storage;
1285 zend_generator_handlers.dtor_obj = zend_generator_dtor_storage;
1286 zend_generator_handlers.get_gc = zend_generator_get_gc;
1287 zend_generator_handlers.clone_obj = NULL;
1288 zend_generator_handlers.get_constructor = zend_generator_get_constructor;
1289
1290 INIT_CLASS_ENTRY(ce, "ClosedGeneratorException", NULL);
1291 zend_ce_ClosedGeneratorException = zend_register_internal_class_ex(&ce, zend_ce_exception);
1292 }
1293 /* }}} */
1294
1295 /*
1296 * Local variables:
1297 * tab-width: 4
1298 * c-basic-offset: 4
1299 * indent-tabs-mode: t
1300 * End:
1301 */
1302