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