xref: /PHP-7.4/Zend/zend_generators.c (revision 99a8ec6e)
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