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