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