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