xref: /PHP-7.1/Zend/zend_generators.c (revision 83e2b9e2)
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