2012-05-19 22:03:27 +00:00
/*
+ - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - +
| Zend Engine |
+ - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - +
2019-01-30 09:23:29 +00:00
| Copyright ( c ) Zend Technologies Ltd . ( http : //www.zend.com) |
2012-05-19 22:03:27 +00:00
+ - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - +
| This source file is subject to version 2.00 of the Zend license , |
| that is bundled with this package in the file LICENSE , and is |
| available through the world - wide - web at the following url : |
| http : //www.zend.com/license/2_00.txt. |
| If you did not receive a copy of the Zend license and are unable to |
| obtain it through the world - wide - web , please send a note to |
| license @ zend . com so we can mail you a copy immediately . |
+ - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - +
| Authors : Nikita Popov < nikic @ php . net > |
2015-03-06 23:28:12 +00:00
| Bob Weinand < bobwei9 @ hotmail . com > |
2012-05-19 22:03:27 +00:00
+ - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - +
*/
# include "zend.h"
# include "zend_API.h"
2012-05-23 14:07:15 +00:00
# include "zend_interfaces.h"
2012-07-22 15:46:46 +00:00
# include "zend_exceptions.h"
2012-05-19 22:03:27 +00:00
# include "zend_generators.h"
2018-01-11 19:15:45 +00:00
# include "zend_closures.h"
2019-10-15 12:30:23 +00:00
# include "zend_generators_arginfo.h"
2020-09-01 15:57:49 +00:00
# include "zend_observer.h"
2012-05-19 22:03:27 +00:00
ZEND_API zend_class_entry * zend_ce_generator ;
2015-03-06 23:28:12 +00:00
ZEND_API zend_class_entry * zend_ce_ClosedGeneratorException ;
2012-05-20 12:19:16 +00:00
static zend_object_handlers zend_generator_handlers ;
2014-12-13 22:06:14 +00:00
static zend_object * zend_generator_create ( zend_class_entry * class_type ) ;
2013-02-01 17:33:26 +00:00
2016-09-28 18:06:10 +00:00
ZEND_API void zend_generator_restore_call_stack ( zend_generator * generator ) /* { { { */
2016-05-11 21:44:18 +00:00
{
zend_execute_data * call , * new_call , * prev_call = NULL ;
call = generator - > frozen_call_stack ;
do {
new_call = zend_vm_stack_push_call_frame (
( ZEND_CALL_INFO ( call ) & ~ ZEND_CALL_ALLOCATED ) ,
call - > func ,
ZEND_CALL_NUM_ARGS ( call ) ,
2019-04-10 23:08:32 +00:00
Z_PTR ( call - > This ) ) ;
2016-05-11 21:44:18 +00:00
memcpy ( ( ( zval * ) new_call ) + ZEND_CALL_FRAME_SLOT , ( ( zval * ) call ) + ZEND_CALL_FRAME_SLOT , ZEND_CALL_NUM_ARGS ( call ) * sizeof ( zval ) ) ;
new_call - > prev_execute_data = prev_call ;
prev_call = new_call ;
call = call - > prev_execute_data ;
} while ( call ) ;
generator - > execute_data - > call = prev_call ;
efree ( generator - > frozen_call_stack ) ;
generator - > frozen_call_stack = NULL ;
}
/* }}} */
2016-09-28 18:06:10 +00:00
ZEND_API zend_execute_data * zend_generator_freeze_call_stack ( zend_execute_data * execute_data ) /* { { { */
2016-05-11 21:44:18 +00:00
{
size_t used_stack ;
zend_execute_data * call , * new_call , * prev_call = NULL ;
zval * stack ;
/* calculate required stack size */
used_stack = 0 ;
call = EX ( call ) ;
do {
used_stack + = ZEND_CALL_FRAME_SLOT + ZEND_CALL_NUM_ARGS ( call ) ;
call = call - > prev_execute_data ;
} while ( call ) ;
stack = emalloc ( used_stack * sizeof ( zval ) ) ;
/* save stack, linking frames in reverse order */
call = EX ( call ) ;
do {
size_t frame_size = ZEND_CALL_FRAME_SLOT + ZEND_CALL_NUM_ARGS ( call ) ;
new_call = ( zend_execute_data * ) ( stack + used_stack - frame_size ) ;
memcpy ( new_call , call , frame_size * sizeof ( zval ) ) ;
used_stack - = frame_size ;
new_call - > prev_execute_data = prev_call ;
prev_call = new_call ;
new_call = call - > prev_execute_data ;
zend_vm_stack_free_call_frame ( call ) ;
call = new_call ;
} while ( call ) ;
execute_data - > call = NULL ;
ZEND_ASSERT ( prev_call = = ( zend_execute_data * ) stack ) ;
return prev_call ;
}
/* }}} */
2016-04-07 10:25:42 +00:00
static void zend_generator_cleanup_unfinished_execution (
2019-10-28 09:23:20 +00:00
zend_generator * generator , zend_execute_data * execute_data , uint32_t catch_op_num ) /* {{{ */
2013-11-30 12:35:33 +00:00
{
2019-12-20 15:15:24 +00:00
zend_op_array * op_array = & execute_data - > func - > op_array ;
if ( execute_data - > opline ! = op_array - > opcodes ) {
2015-06-23 18:13:25 +00:00
/* -1 required because we want the last run opcode, not the next to-be-run one. */
2019-12-20 15:15:24 +00:00
uint32_t op_num = execute_data - > opline - op_array - > opcodes - 1 ;
2015-06-23 18:13:25 +00:00
2016-05-11 21:44:18 +00:00
if ( UNEXPECTED ( generator - > frozen_call_stack ) ) {
2019-10-28 09:23:20 +00:00
/* Temporarily restore generator->execute_data if it has been NULLed out already. */
zend_execute_data * save_ex = generator - > execute_data ;
generator - > execute_data = execute_data ;
2016-09-28 11:26:08 +00:00
zend_generator_restore_call_stack ( generator ) ;
2019-10-28 09:23:20 +00:00
generator - > execute_data = save_ex ;
2016-05-11 21:44:18 +00:00
}
2019-12-20 15:15:24 +00:00
2017-05-17 17:56:49 +00:00
zend_cleanup_unfinished_execution ( execute_data , op_num , catch_op_num ) ;
2015-06-20 17:09:07 +00:00
}
2013-11-30 12:35:33 +00:00
}
/* }}} */
2014-12-13 22:06:14 +00:00
ZEND_API void zend_generator_close ( zend_generator * generator , zend_bool finished_execution ) /* { { { */
2012-05-20 12:19:16 +00:00
{
2015-09-20 00:19:31 +00:00
if ( EXPECTED ( generator - > execute_data ) ) {
2012-05-23 12:20:25 +00:00
zend_execute_data * execute_data = generator - > execute_data ;
2019-10-28 09:23:20 +00:00
/* Null out execute_data early, to prevent double frees if GC runs while we're
* already cleaning up execute_data . */
generator - > execute_data = NULL ;
2012-12-12 13:47:55 +00:00
2016-04-28 12:17:24 +00:00
if ( EX_CALL_INFO ( ) & ZEND_CALL_HAS_SYMBOL_TABLE ) {
2014-12-13 22:06:14 +00:00
zend_clean_and_cache_symbol_table ( execute_data - > symbol_table ) ;
2012-05-23 12:20:25 +00:00
}
2015-05-14 15:17:39 +00:00
/* always free the CV's, in the symtable are only not-free'd IS_INDIRECT's */
zend_free_compiled_variables ( execute_data ) ;
2020-04-06 10:46:52 +00:00
if ( EX_CALL_INFO ( ) & ZEND_CALL_HAS_EXTRA_NAMED_PARAMS ) {
zend_free_extra_named_params ( execute_data - > extra_named_params ) ;
}
2012-05-23 12:20:25 +00:00
2019-03-01 13:32:11 +00:00
if ( EX_CALL_INFO ( ) & ZEND_CALL_RELEASE_THIS ) {
2014-10-03 15:32:46 +00:00
OBJ_RELEASE ( Z_OBJ ( execute_data - > This ) ) ;
2014-03-27 22:11:22 +00:00
}
2012-05-27 01:50:31 +00:00
2015-09-20 00:19:31 +00:00
/* A fatal error / die occurred during the generator execution.
* Trying to clean up the stack may not be safe in this case . */
if ( UNEXPECTED ( CG ( unclean_shutdown ) ) ) {
2014-07-02 15:33:31 +00:00
generator - > execute_data = NULL ;
2013-06-29 19:51:54 +00:00
return ;
}
2019-10-28 09:23:20 +00:00
zend_vm_stack_free_extra_args ( execute_data ) ;
2012-06-08 22:40:47 +00:00
2015-09-20 00:19:31 +00:00
/* Some cleanups are only necessary if the generator was closed
2013-11-30 12:35:33 +00:00
* before it could finish execution ( reach a return statement ) . */
2015-09-20 00:19:31 +00:00
if ( UNEXPECTED ( ! finished_execution ) ) {
2019-10-28 09:23:20 +00:00
zend_generator_cleanup_unfinished_execution ( generator , execute_data , 0 ) ;
2013-11-30 12:35:33 +00:00
}
2015-04-08 19:11:42 +00:00
/* Free closure object */
2019-03-01 13:32:11 +00:00
if ( EX_CALL_INFO ( ) & ZEND_CALL_CLOSURE ) {
2018-01-11 19:15:45 +00:00
OBJ_RELEASE ( ZEND_CLOSURE_OBJECT ( EX ( func ) ) ) ;
2012-09-05 05:50:55 +00:00
}
2019-10-28 09:23:20 +00:00
efree ( execute_data ) ;
2012-05-23 12:20:25 +00:00
}
2012-05-26 21:59:22 +00:00
}
/* }}} */
2016-02-15 21:37:21 +00:00
static zend_generator * zend_generator_get_child ( zend_generator_node * node , zend_generator * leaf ) ;
2020-09-13 22:04:45 +00:00
static void zend_generator_update_leaf_of_child ( zend_generator_node * node , zend_generator * from_leaf , zend_generator * to_leaf )
{
ZEND_ASSERT ( node - > children > = 1 ) ;
if ( node - > ptr . leaf = = from_leaf ) {
node - > ptr . leaf = to_leaf ;
}
if ( node - > children = = 1 ) {
node - > child . single . leaf = to_leaf ;
} else {
HashTable * ht = node - > child . ht ;
zend_generator * child = zend_hash_index_find_ptr ( ht , ( zend_ulong ) from_leaf ) ;
ZEND_ASSERT ( child ! = NULL ) ;
zend_hash_index_del ( ht , ( zend_ulong ) from_leaf ) ;
zend_hash_index_add_ptr ( ht , ( zend_ulong ) to_leaf , child ) ;
}
}
static void zend_generator_remove_leaf_child ( zend_generator_node * node , zend_generator * leaf , zend_generator * replace_leaf ) {
if ( node - > children > 1 ) {
HashTable * ht = node - > child . ht ;
zend_ulong child_leaf ;
zend_generator * child_generator ;
zend_hash_index_del ( ht , ( zend_ulong ) leaf ) ;
if ( - - node - > children = = 1 ) {
ZEND_HASH_FOREACH_NUM_KEY_PTR ( ht , child_leaf , child_generator ) {
node - > child . single . leaf = ( zend_generator * ) child_leaf ;
node - > child . single . child = child_generator ;
if ( node - > ptr . leaf = = leaf ) {
node - > ptr . leaf = ( zend_generator * ) child_leaf ;
}
break ;
} ZEND_HASH_FOREACH_END ( ) ;
zend_hash_destroy ( ht ) ;
efree ( ht ) ;
} else if ( node - > ptr . leaf = = leaf ) {
ZEND_HASH_FOREACH_NUM_KEY_PTR ( ht , child_leaf , child_generator ) {
node - > ptr . leaf = ( zend_generator * ) child_leaf ;
break ;
} ZEND_HASH_FOREACH_END ( ) ;
}
} else if ( node - > ptr . leaf = = leaf ) {
ZEND_ASSERT ( replace_leaf ! = leaf ) ;
node - > ptr . leaf = replace_leaf ;
}
}
2014-12-13 22:06:14 +00:00
static void zend_generator_dtor_storage ( zend_object * object ) /* { { { */
2013-01-30 22:52:02 +00:00
{
2014-02-10 06:04:30 +00:00
zend_generator * generator = ( zend_generator * ) object ;
2013-01-30 22:52:02 +00:00
zend_execute_data * ex = generator - > execute_data ;
2019-12-20 15:15:24 +00:00
uint32_t op_num , try_catch_offset ;
2013-01-30 22:52:02 +00:00
int i ;
2016-02-15 21:37:21 +00:00
/* leave yield from mode to properly allow finally execution */
if ( UNEXPECTED ( Z_TYPE ( generator - > values ) ! = IS_UNDEF ) ) {
zval_ptr_dtor ( & generator - > values ) ;
ZVAL_UNDEF ( & generator - > values ) ;
}
2020-09-13 22:04:45 +00:00
if ( UNEXPECTED ( generator - > node . children ! = 0 ) & & generator - > node . parent ) {
/* we're called out of order - this must only happen during shutdown sequence: we call our (direct) child nodes destructors first, to clean it from the bottom up */
while ( generator - > node . children ! = 0 ) {
zend_generator * child ;
if ( generator - > node . children = = 1 ) {
child = generator - > node . child . single . child ;
} else {
child = ( zend_generator * ) Z_PTR_P ( zend_hash_get_current_data ( generator - > node . child . ht ) ) ;
}
GC_ADD_FLAGS ( & child - > std , IS_OBJ_DESTRUCTOR_CALLED ) ;
2020-09-14 21:05:28 +00:00
GC_ADDREF ( & child - > std ) ; /* must not be released during destructor */
2020-09-13 22:04:45 +00:00
zend_generator_dtor_storage ( & child - > std ) ;
2020-09-14 21:05:28 +00:00
OBJ_RELEASE ( & child - > std ) ;
2020-09-13 22:04:45 +00:00
}
}
2016-02-15 21:37:21 +00:00
if ( EXPECTED ( generator - > node . children = = 0 ) ) {
2020-09-13 22:04:45 +00:00
zend_generator_update_current ( generator , generator ) ; /* ensure we remove it from a *live* root */
zend_generator * root = generator - > node . ptr . root , * parent = generator - > node . parent , * next , * toproot = root ;
if ( parent ) {
zend_bool parent_becomes_leaf = parent - > node . children = = 1 ;
if ( parent_becomes_leaf ) {
while ( UNEXPECTED ( root ! = generator ) ) {
next = zend_generator_get_child ( & root - > node , generator ) ;
zend_generator_update_leaf_of_child ( & root - > node , generator , parent ) ;
root = next ;
}
parent - > node . ptr . root = toproot ;
parent - > node . children = 0 ;
} else {
zend_generator_remove_leaf_child ( & parent - > node , generator , NULL ) ;
while ( UNEXPECTED ( root ! = parent ) ) {
next = zend_generator_get_child ( & root - > node , generator ) ;
zend_generator_remove_leaf_child ( & root - > node , generator , parent - > node . ptr . leaf ) ;
OBJ_RELEASE ( & root - > std ) ;
root = next ;
}
}
OBJ_RELEASE ( & parent - > std ) ;
/* Reset for resuming in finally */
generator - > node . parent = NULL ;
generator - > node . ptr . root = generator ;
2016-02-15 21:37:21 +00:00
}
}
2018-01-12 20:24:04 +00:00
if ( EXPECTED ( ! ex ) | | EXPECTED ( ! ( ex - > func - > op_array . fn_flags & ZEND_ACC_HAS_FINALLY_BLOCK ) )
| | CG ( unclean_shutdown ) ) {
2013-01-30 22:52:02 +00:00
return ;
}
/* -1 required because we want the last run opcode, not the
* next to - be - run one . */
2014-06-26 19:51:14 +00:00
op_num = ex - > opline - ex - > func - > op_array . opcodes - 1 ;
2019-12-20 15:15:24 +00:00
try_catch_offset = - 1 ;
2013-01-30 22:52:02 +00:00
2019-12-20 15:15:24 +00:00
/* Find the innermost try/catch that we are inside of. */
2014-06-26 19:51:14 +00:00
for ( i = 0 ; i < ex - > func - > op_array . last_try_catch ; i + + ) {
zend_try_catch_element * try_catch = & ex - > func - > op_array . try_catch_array [ i ] ;
2013-01-30 22:52:02 +00:00
if ( op_num < try_catch - > try_op ) {
break ;
}
2019-12-20 15:15:24 +00:00
if ( op_num < try_catch - > catch_op | | op_num < try_catch - > finally_end ) {
try_catch_offset = i ;
2013-01-30 22:52:02 +00:00
}
}
2019-12-20 15:15:24 +00:00
/* Walk try/catch/finally structures upwards, performing the necessary actions. */
while ( try_catch_offset ! = ( uint32_t ) - 1 ) {
zend_try_catch_element * try_catch = & ex - > func - > op_array . try_catch_array [ try_catch_offset ] ;
2014-11-27 06:56:43 +00:00
2019-12-20 15:15:24 +00:00
if ( op_num < try_catch - > finally_op ) {
/* Go to finally block */
zval * fast_call =
ZEND_CALL_VAR ( ex , ex - > func - > op_array . opcodes [ try_catch - > finally_end ] . op1 . var ) ;
2016-04-07 10:25:42 +00:00
2019-12-20 15:15:24 +00:00
zend_generator_cleanup_unfinished_execution ( generator , ex , try_catch - > finally_op ) ;
Z_OBJ_P ( fast_call ) = EG ( exception ) ;
EG ( exception ) = NULL ;
Z_OPLINE_NUM_P ( fast_call ) = ( uint32_t ) - 1 ;
ex - > opline = & ex - > func - > op_array . opcodes [ try_catch - > finally_op ] ;
generator - > flags | = ZEND_GENERATOR_FORCED_CLOSE ;
zend_generator_resume ( generator ) ;
/* TODO: If we hit another yield inside try/finally,
* should we also jump to the next finally block ? */
return ;
} else if ( op_num < try_catch - > finally_end ) {
zval * fast_call =
ZEND_CALL_VAR ( ex , ex - > func - > op_array . opcodes [ try_catch - > finally_end ] . op1 . var ) ;
/* Clean up incomplete return statement */
if ( Z_OPLINE_NUM_P ( fast_call ) ! = ( uint32_t ) - 1 ) {
zend_op * retval_op = & ex - > func - > op_array . opcodes [ Z_OPLINE_NUM_P ( fast_call ) ] ;
if ( retval_op - > op2_type & ( IS_TMP_VAR | IS_VAR ) ) {
zval_ptr_dtor ( ZEND_CALL_VAR ( ex , retval_op - > op2 . var ) ) ;
}
}
/* Clean up backed-up exception */
if ( Z_OBJ_P ( fast_call ) ) {
OBJ_RELEASE ( Z_OBJ_P ( fast_call ) ) ;
}
}
2015-06-11 15:40:10 +00:00
2019-12-20 15:15:24 +00:00
try_catch_offset - - ;
2013-01-30 22:52:02 +00:00
}
}
/* }}} */
2014-12-13 22:06:14 +00:00
static void zend_generator_free_storage ( zend_object * object ) /* { { { */
2012-05-26 21:59:22 +00:00
{
2014-02-10 06:04:30 +00:00
zend_generator * generator = ( zend_generator * ) object ;
2014-12-13 22:06:14 +00:00
zend_generator_close ( generator , 0 ) ;
2012-05-26 21:59:22 +00:00
2015-11-24 22:43:34 +00:00
/* we can't immediately free them in zend_generator_close() else yield from won't be able to fetch it */
zval_ptr_dtor ( & generator - > value ) ;
zval_ptr_dtor ( & generator - > key ) ;
2015-09-20 00:19:31 +00:00
if ( EXPECTED ( ! Z_ISUNDEF ( generator - > retval ) ) ) {
2015-02-19 19:17:37 +00:00
zval_ptr_dtor ( & generator - > retval ) ;
}
2018-01-09 20:49:16 +00:00
if ( UNEXPECTED ( generator - > node . children > 1 ) ) {
zend_hash_destroy ( generator - > node . child . ht ) ;
efree ( generator - > node . child . ht ) ;
2015-03-06 23:28:12 +00:00
}
2014-12-13 22:06:14 +00:00
zend_object_std_dtor ( & generator - > std ) ;
2012-05-20 12:19:16 +00:00
}
/* }}} */
2019-01-31 15:47:58 +00:00
static HashTable * zend_generator_get_gc ( zend_object * object , zval * * table , int * n ) /* { { { */
2016-02-10 18:03:02 +00:00
{
2019-01-31 15:47:58 +00:00
zend_generator * generator = ( zend_generator * ) object ;
2016-02-11 15:38:30 +00:00
zend_execute_data * execute_data = generator - > execute_data ;
2016-02-12 17:50:19 +00:00
zend_op_array * op_array ;
2016-02-11 15:38:30 +00:00
if ( ! execute_data ) {
/* If the generator has been closed, it can only hold on to three values: The value, key
* and retval . These three zvals are stored sequentially starting at & generator - > value . */
* table = & generator - > value ;
* n = 3 ;
return NULL ;
}
2016-02-12 17:50:19 +00:00
op_array = & EX ( func ) - > op_array ;
2016-02-11 15:38:30 +00:00
2020-04-24 16:59:13 +00:00
zend_get_gc_buffer * gc_buffer = zend_get_gc_buffer_create ( ) ;
zend_get_gc_buffer_add_zval ( gc_buffer , & generator - > value ) ;
zend_get_gc_buffer_add_zval ( gc_buffer , & generator - > key ) ;
zend_get_gc_buffer_add_zval ( gc_buffer , & generator - > retval ) ;
zend_get_gc_buffer_add_zval ( gc_buffer , & generator - > values ) ;
2016-02-11 15:38:30 +00:00
2016-04-28 12:17:24 +00:00
if ( ! ( EX_CALL_INFO ( ) & ZEND_CALL_HAS_SYMBOL_TABLE ) ) {
2016-02-11 15:38:30 +00:00
uint32_t i , num_cvs = EX ( func ) - > op_array . last_var ;
for ( i = 0 ; i < num_cvs ; i + + ) {
2020-04-24 16:59:13 +00:00
zend_get_gc_buffer_add_zval ( gc_buffer , EX_VAR_NUM ( i ) ) ;
2016-02-11 15:38:30 +00:00
}
}
2016-02-12 17:50:19 +00:00
if ( EX_CALL_INFO ( ) & ZEND_CALL_FREE_EXTRA_ARGS ) {
zval * zv = EX_VAR_NUM ( op_array - > last_var + op_array - > T ) ;
zval * end = zv + ( EX_NUM_ARGS ( ) - op_array - > num_args ) ;
while ( zv ! = end ) {
2020-04-24 16:59:13 +00:00
zend_get_gc_buffer_add_zval ( gc_buffer , zv + + ) ;
2016-02-12 17:50:19 +00:00
}
}
2019-08-14 15:48:57 +00:00
if ( EX_CALL_INFO ( ) & ZEND_CALL_RELEASE_THIS ) {
2020-04-24 16:59:13 +00:00
zend_get_gc_buffer_add_obj ( gc_buffer , Z_OBJ ( execute_data - > This ) ) ;
2016-02-11 15:38:30 +00:00
}
if ( EX_CALL_INFO ( ) & ZEND_CALL_CLOSURE ) {
2020-04-24 16:59:13 +00:00
zend_get_gc_buffer_add_obj ( gc_buffer , ZEND_CLOSURE_OBJECT ( EX ( func ) ) ) ;
2016-02-11 15:38:30 +00:00
}
2020-04-06 10:46:52 +00:00
if ( EX_CALL_INFO ( ) & ZEND_CALL_HAS_EXTRA_NAMED_PARAMS ) {
zval extra_named_params ;
ZVAL_ARR ( & extra_named_params , EX ( extra_named_params ) ) ;
zend_get_gc_buffer_add_zval ( gc_buffer , & extra_named_params ) ;
}
2016-02-11 15:38:30 +00:00
2018-09-28 17:14:47 +00:00
if ( execute_data - > opline ! = op_array - > opcodes ) {
uint32_t i , op_num = execute_data - > opline - op_array - > opcodes - 1 ;
for ( i = 0 ; i < op_array - > last_live_range ; i + + ) {
const zend_live_range * range = & op_array - > live_range [ i ] ;
if ( range - > start > op_num ) {
break ;
} else if ( op_num < range - > end ) {
uint32_t kind = range - > var & ZEND_LIVE_MASK ;
uint32_t var_num = range - > var & ~ ZEND_LIVE_MASK ;
zval * var = EX_VAR ( var_num ) ;
if ( kind = = ZEND_LIVE_TMPVAR | | kind = = ZEND_LIVE_LOOP ) {
2020-04-24 16:59:13 +00:00
zend_get_gc_buffer_add_zval ( gc_buffer , var ) ;
2018-09-28 17:14:47 +00:00
}
}
}
}
2016-02-12 17:50:19 +00:00
if ( generator - > node . children = = 0 ) {
2018-01-13 10:04:26 +00:00
zend_generator * root = generator - > node . ptr . root ;
while ( root ! = generator ) {
2020-04-24 16:59:13 +00:00
zend_get_gc_buffer_add_obj ( gc_buffer , & root - > std ) ;
2018-01-13 10:04:26 +00:00
root = zend_generator_get_child ( & root - > node , generator ) ;
2016-02-12 17:50:19 +00:00
}
}
2020-04-24 16:59:13 +00:00
zend_get_gc_buffer_use ( gc_buffer , table , n ) ;
2016-04-28 12:17:24 +00:00
if ( EX_CALL_INFO ( ) & ZEND_CALL_HAS_SYMBOL_TABLE ) {
return execute_data - > symbol_table ;
} else {
return NULL ;
}
2016-02-10 18:03:02 +00:00
}
/* }}} */
2014-12-13 22:06:14 +00:00
static zend_object * zend_generator_create ( zend_class_entry * class_type ) /* { { { */
2012-05-20 12:19:16 +00:00
{
zend_generator * generator ;
generator = emalloc ( sizeof ( zend_generator ) ) ;
memset ( generator , 0 , sizeof ( zend_generator ) ) ;
2012-05-30 03:05:49 +00:00
/* The key will be incremented on first use, so it'll start at 0 */
generator - > largest_used_integer_key = - 1 ;
2015-02-19 19:17:37 +00:00
ZVAL_UNDEF ( & generator - > retval ) ;
2015-02-20 11:59:56 +00:00
ZVAL_UNDEF ( & generator - > values ) ;
2015-03-06 23:28:12 +00:00
/* By default we have a tree of only one node */
generator - > node . parent = NULL ;
generator - > node . children = 0 ;
generator - > node . ptr . root = generator ;
2015-02-19 19:17:37 +00:00
2014-12-13 22:06:14 +00:00
zend_object_std_init ( & generator - > std , class_type ) ;
2014-02-10 06:04:30 +00:00
generator - > std . handlers = & zend_generator_handlers ;
2012-05-20 12:19:16 +00:00
2014-02-10 06:04:30 +00:00
return ( zend_object * ) generator ;
2012-05-20 12:19:16 +00:00
}
/* }}} */
2015-08-19 11:40:56 +00:00
static ZEND_COLD zend_function * zend_generator_get_constructor ( zend_object * object ) /* { { { */
2012-05-20 12:19:16 +00:00
{
2015-07-07 17:10:22 +00:00
zend_throw_error ( NULL , " The \" Generator \" class is reserved for internal use and cannot be manually instantiated " ) ;
2012-05-20 12:19:16 +00:00
return NULL ;
}
/* }}} */
2012-05-19 22:03:27 +00:00
2015-03-06 23:28:12 +00:00
ZEND_API zend_execute_data * zend_generator_check_placeholder_frame ( zend_execute_data * ptr )
{
2016-04-01 13:17:49 +00:00
if ( ! ptr - > func & & Z_TYPE ( ptr - > This ) = = IS_OBJECT ) {
2015-03-06 23:28:12 +00:00
if ( Z_OBJCE ( ptr - > This ) = = zend_ce_generator ) {
zend_generator * generator = ( zend_generator * ) Z_OBJ ( ptr - > This ) ;
zend_generator * root = ( generator - > node . children < 1 ? generator : generator - > node . ptr . leaf ) - > node . ptr . root ;
zend_execute_data * prev = ptr - > prev_execute_data ;
if ( generator - > node . parent ! = root ) {
do {
generator - > execute_data - > prev_execute_data = prev ;
prev = generator - > execute_data ;
generator = generator - > node . parent ;
} while ( generator - > node . parent ! = root ) ;
}
generator - > execute_data - > prev_execute_data = prev ;
ptr = generator - > execute_data ;
}
}
return ptr ;
}
static void zend_generator_throw_exception ( zend_generator * generator , zval * exception )
{
2016-03-17 02:41:36 +00:00
zend_execute_data * original_execute_data = EG ( current_execute_data ) ;
2015-11-25 22:09:44 +00:00
/* if we don't stop an array/iterator yield from, the exception will only reach the generator after the values were all iterated over */
if ( UNEXPECTED ( Z_TYPE ( generator - > values ) ! = IS_UNDEF ) ) {
zval_ptr_dtor ( & generator - > values ) ;
ZVAL_UNDEF ( & generator - > values ) ;
}
2015-04-17 20:19:41 +00:00
/* Throw the exception in the context of the generator. Decrementing the opline
* to pretend the exception happened during the YIELD opcode . */
2015-03-06 23:28:12 +00:00
EG ( current_execute_data ) = generator - > execute_data ;
2015-04-17 20:19:41 +00:00
generator - > execute_data - > opline - - ;
2015-03-06 23:28:12 +00:00
if ( exception ) {
zend_throw_exception_object ( exception ) ;
} else {
2016-12-12 07:55:32 +00:00
zend_rethrow_exception ( EG ( current_execute_data ) ) ;
2015-03-06 23:28:12 +00:00
}
2015-04-17 20:19:41 +00:00
generator - > execute_data - > opline + + ;
2015-03-06 23:28:12 +00:00
EG ( current_execute_data ) = original_execute_data ;
}
static zend_generator * zend_generator_get_child ( zend_generator_node * node , zend_generator * leaf )
{
2020-09-01 14:19:34 +00:00
ZEND_ASSERT ( node - > children ! = 0 ) ;
if ( node - > children = = 1 ) {
2018-01-09 20:49:16 +00:00
return node - > child . single . child ;
} else {
return zend_hash_index_find_ptr ( node - > child . ht , ( zend_ulong ) leaf ) ;
2015-03-06 23:28:12 +00:00
}
}
static zend_generator_node * zend_generator_search_multi_children_node ( zend_generator_node * node )
{
while ( node - > children = = 1 ) {
2018-01-09 20:49:16 +00:00
node = & node - > child . single . child - > node ;
2015-03-06 23:28:12 +00:00
}
return node - > children > 1 ? node : NULL ;
}
static void zend_generator_add_single_child ( zend_generator_node * node , zend_generator * child , zend_generator * leaf )
{
2018-01-09 20:49:16 +00:00
if ( node - > children = = 0 ) {
node - > child . single . leaf = leaf ;
node - > child . single . child = child ;
2015-03-06 23:28:12 +00:00
} else {
2018-01-09 20:49:16 +00:00
if ( node - > children = = 1 ) {
HashTable * ht = emalloc ( sizeof ( HashTable ) ) ;
zend_hash_init ( ht , 0 , NULL , NULL , 0 ) ;
zend_hash_index_add_ptr ( ht ,
( zend_ulong ) node - > child . single . leaf , node - > child . single . child ) ;
node - > child . ht = ht ;
2015-03-06 23:28:12 +00:00
}
2018-01-09 20:49:16 +00:00
2020-09-13 22:04:45 +00:00
if ( zend_hash_index_add_ptr ( node - > child . ht , ( zend_ulong ) leaf , child ) = = NULL ) {
ZEND_ASSERT ( node - > children > 1 ) ;
return ;
}
2015-03-06 23:28:12 +00:00
}
2020-09-13 22:04:45 +00:00
+ + node - > children ;
2015-03-06 23:28:12 +00:00
}
static void zend_generator_merge_child_nodes ( zend_generator_node * dest , zend_generator_node * src , zend_generator * child )
{
2018-01-09 20:49:16 +00:00
zend_ulong leaf ;
ZEND_ASSERT ( src - > children > 1 ) ;
ZEND_HASH_FOREACH_NUM_KEY ( src - > child . ht , leaf ) {
zend_generator_add_single_child ( dest , child , ( zend_generator * ) leaf ) ;
} ZEND_HASH_FOREACH_END ( ) ;
2015-03-06 23:28:12 +00:00
}
2018-01-09 20:49:16 +00:00
/* Pay attention so that the root of each subtree of the Generators tree is referenced
* once per leaf */
2015-03-06 23:28:12 +00:00
static void zend_generator_add_child ( zend_generator * generator , zend_generator * child )
{
zend_generator * leaf = child - > node . children ? child - > node . ptr . leaf : child ;
zend_generator_node * multi_children_node ;
zend_bool was_leaf = generator - > node . children = = 0 ;
if ( was_leaf ) {
zend_generator * next = generator - > node . parent ;
leaf - > node . ptr . root = generator - > node . ptr . root ;
2017-10-26 22:28:58 +00:00
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 */
2015-03-06 23:28:12 +00:00
generator - > node . ptr . leaf = leaf ;
while ( next ) {
if ( next - > node . children > 1 ) {
2018-01-09 20:49:16 +00:00
zend_generator * child = zend_hash_index_find_ptr ( next - > node . child . ht , ( zend_ulong ) generator ) ;
zend_hash_index_del ( next - > node . child . ht , ( zend_ulong ) generator ) ;
zend_hash_index_add_ptr ( next - > node . child . ht , ( zend_ulong ) leaf , child ) ;
2015-03-06 23:28:12 +00:00
}
next - > node . ptr . leaf = leaf ;
next = next - > node . parent ;
}
} else if ( generator - > node . children = = 1 ) {
multi_children_node = zend_generator_search_multi_children_node ( & generator - > node ) ;
if ( multi_children_node ) {
2018-01-09 20:49:16 +00:00
zend_generator_merge_child_nodes ( & generator - > node , multi_children_node , generator - > node . child . single . child ) ;
2015-03-06 23:28:12 +00:00
}
}
if ( ! was_leaf ) {
multi_children_node = zend_generator_search_multi_children_node ( & child - > node ) ;
} else {
multi_children_node = ( zend_generator_node * ) 0x1 ;
}
2020-09-13 22:04:45 +00:00
/* for allowing zend_generator_get_child() to work, we need every multi children node to have ALL its leaf descendents present, linking to their respective child */
2015-03-06 23:28:12 +00:00
{
zend_generator * parent = generator - > node . parent , * cur = generator ;
if ( multi_children_node > ( zend_generator_node * ) 0x1 ) {
zend_generator_merge_child_nodes ( & generator - > node , multi_children_node , child ) ;
} else {
zend_generator_add_single_child ( & generator - > node , child , leaf ) ;
}
while ( parent ) {
if ( parent - > node . children > 1 ) {
if ( multi_children_node = = ( zend_generator_node * ) 0x1 ) {
multi_children_node = zend_generator_search_multi_children_node ( & child - > node ) ;
}
if ( multi_children_node ) {
zend_generator_merge_child_nodes ( & parent - > node , multi_children_node , cur ) ;
} else {
zend_generator_add_single_child ( & parent - > node , cur , leaf ) ;
}
}
cur = parent ;
parent = parent - > node . parent ;
}
}
}
2015-07-30 03:30:34 +00:00
void zend_generator_yield_from ( zend_generator * generator , zend_generator * from )
2015-03-06 23:28:12 +00:00
{
2015-07-30 03:30:34 +00:00
zend_generator_add_child ( from , generator ) ;
2015-03-06 23:28:12 +00:00
2015-07-30 03:30:34 +00:00
generator - > node . parent = from ;
zend_generator_get_current ( generator ) ;
2017-10-26 22:28:58 +00:00
GC_DELREF ( & from - > std ) ;
2020-04-03 08:06:41 +00:00
generator - > flags | = ZEND_GENERATOR_DO_INIT ;
2015-03-06 23:28:12 +00:00
}
2015-09-20 00:19:31 +00:00
ZEND_API zend_generator * zend_generator_update_current ( zend_generator * generator , zend_generator * leaf )
2015-03-06 23:28:12 +00:00
{
2015-09-20 00:19:31 +00:00
zend_generator * old_root , * root = leaf - > node . ptr . root ;
2015-03-06 23:28:12 +00:00
2015-04-15 19:41:29 +00:00
/* generator at the root had stopped */
if ( root ! = generator ) {
old_root = root ;
root = zend_generator_get_child ( & root - > node , leaf ) ;
} else {
old_root = NULL ;
}
2015-03-06 23:28:12 +00:00
while ( ! root - > execute_data & & root ! = generator ) {
2015-04-15 19:41:29 +00:00
OBJ_RELEASE ( & old_root - > std ) ;
old_root = root ;
2015-03-06 23:28:12 +00:00
root = zend_generator_get_child ( & root - > node , leaf ) ;
}
if ( root - > node . parent ) {
if ( root - > node . parent - > execute_data = = NULL ) {
2020-09-13 22:04:45 +00:00
if ( EXPECTED ( EG ( exception ) = = NULL ) & & EXPECTED ( ( OBJ_FLAGS ( & generator - > std ) & IS_OBJ_DESTRUCTOR_CALLED ) = = 0 ) ) {
2015-03-06 23:28:12 +00:00
zend_op * yield_from = ( zend_op * ) root - > execute_data - > opline - 1 ;
2016-01-07 10:56:10 +00:00
if ( yield_from - > opcode = = ZEND_YIELD_FROM ) {
2015-03-06 23:28:12 +00:00
if ( Z_ISUNDEF ( root - > node . parent - > retval ) ) {
/* Throw the exception in the context of the generator */
zend_execute_data * original_execute_data = EG ( current_execute_data ) ;
EG ( current_execute_data ) = root - > execute_data ;
if ( root = = generator ) {
root - > execute_data - > prev_execute_data = original_execute_data ;
} else {
root - > execute_data - > prev_execute_data = & generator - > execute_fake ;
generator - > execute_fake . prev_execute_data = original_execute_data ;
}
2016-12-01 14:21:28 +00:00
root - > execute_data - > opline - - ; /* ZEND_YIELD(_FROM) already advance, so decrement opline to throw from correct place */
2015-03-06 23:28:12 +00:00
zend_throw_exception ( zend_ce_ClosedGeneratorException , " Generator yielded from aborted, no return value available " , 0 ) ;
EG ( current_execute_data ) = original_execute_data ;
2016-07-23 14:39:21 +00:00
if ( ! ( ( old_root ? old_root : generator ) - > flags & ZEND_GENERATOR_CURRENTLY_RUNNING ) ) {
leaf - > node . ptr . root = root ;
root - > node . parent = NULL ;
if ( old_root ) {
OBJ_RELEASE ( & old_root - > std ) ;
}
zend_generator_resume ( leaf ) ;
return leaf - > node . ptr . root ; /* this may be updated during zend_generator_resume! */
}
2015-03-06 23:28:12 +00:00
} else {
2016-01-07 14:12:35 +00:00
zval_ptr_dtor ( & root - > value ) ;
2015-11-24 22:43:34 +00:00
ZVAL_COPY ( & root - > value , & root - > node . parent - > value ) ;
2015-03-06 23:28:12 +00:00
ZVAL_COPY ( ZEND_CALL_VAR ( root - > execute_data , yield_from - > result . var ) , & root - > node . parent - > retval ) ;
}
}
}
root - > node . parent = NULL ;
} else {
do {
root = root - > node . parent ;
2017-10-26 22:28:58 +00:00
GC_ADDREF ( & root - > std ) ;
2015-03-06 23:28:12 +00:00
} while ( root - > node . parent ) ;
}
}
2018-01-12 21:48:29 +00:00
leaf - > node . ptr . root = root ;
2015-04-15 19:41:29 +00:00
if ( old_root ) {
OBJ_RELEASE ( & old_root - > std ) ;
}
2018-01-12 21:48:29 +00:00
return root ;
2015-03-06 23:28:12 +00:00
}
2020-08-28 13:41:27 +00:00
static zend_result zend_generator_get_next_delegated_value ( zend_generator * generator ) /* { { { */
2015-02-20 11:59:56 +00:00
{
zval * value ;
if ( Z_TYPE ( generator - > values ) = = IS_ARRAY ) {
HashTable * ht = Z_ARR ( generator - > values ) ;
HashPosition pos = Z_FE_POS ( generator - > values ) ;
Bucket * p ;
do {
if ( UNEXPECTED ( pos > = ht - > nNumUsed ) ) {
/* Reached end of array */
goto failure ;
}
p = & ht - > arData [ pos ] ;
value = & p - > val ;
if ( Z_TYPE_P ( value ) = = IS_INDIRECT ) {
value = Z_INDIRECT_P ( value ) ;
}
pos + + ;
} while ( Z_ISUNDEF_P ( value ) ) ;
zval_ptr_dtor ( & generator - > value ) ;
ZVAL_COPY ( & generator - > value , value ) ;
zval_ptr_dtor ( & generator - > key ) ;
if ( p - > key ) {
ZVAL_STR_COPY ( & generator - > key , p - > key ) ;
} else {
ZVAL_LONG ( & generator - > key , p - > h ) ;
}
Z_FE_POS ( generator - > values ) = pos ;
} else {
zend_object_iterator * iter = ( zend_object_iterator * ) Z_OBJ ( generator - > values ) ;
2015-04-21 17:41:51 +00:00
if ( iter - > index + + > 0 ) {
2015-02-20 11:59:56 +00:00
iter - > funcs - > move_forward ( iter ) ;
if ( UNEXPECTED ( EG ( exception ) ! = NULL ) ) {
2015-12-06 01:45:19 +00:00
goto exception ;
2015-02-20 11:59:56 +00:00
}
}
if ( iter - > funcs - > valid ( iter ) = = FAILURE ) {
2020-08-31 08:51:00 +00:00
if ( UNEXPECTED ( EG ( exception ) ! = NULL ) ) {
goto exception ;
}
2015-02-20 11:59:56 +00:00
/* reached end of iteration */
goto failure ;
}
value = iter - > funcs - > get_current_data ( iter ) ;
2015-12-06 01:45:19 +00:00
if ( UNEXPECTED ( EG ( exception ) ! = NULL ) ) {
goto exception ;
} else if ( UNEXPECTED ( ! value ) ) {
2015-02-20 11:59:56 +00:00
goto failure ;
}
zval_ptr_dtor ( & generator - > value ) ;
ZVAL_COPY ( & generator - > value , value ) ;
zval_ptr_dtor ( & generator - > key ) ;
if ( iter - > funcs - > get_current_key ) {
iter - > funcs - > get_current_key ( iter , & generator - > key ) ;
if ( UNEXPECTED ( EG ( exception ) ! = NULL ) ) {
ZVAL_UNDEF ( & generator - > key ) ;
2015-12-06 01:45:19 +00:00
goto exception ;
2015-02-20 11:59:56 +00:00
}
} else {
ZVAL_LONG ( & generator - > key , iter - > index ) ;
}
}
return SUCCESS ;
2016-12-12 07:55:32 +00:00
exception :
2020-09-02 08:50:14 +00:00
zend_generator_throw_exception ( generator , NULL ) ;
2015-12-06 01:45:19 +00:00
2015-02-20 11:59:56 +00:00
failure :
zval_ptr_dtor ( & generator - > values ) ;
ZVAL_UNDEF ( & generator - > values ) ;
return FAILURE ;
}
/* }}} */
2015-03-06 23:28:12 +00:00
ZEND_API void zend_generator_resume ( zend_generator * orig_generator ) /* { { { */
2012-05-26 15:53:13 +00:00
{
2017-07-22 09:14:00 +00:00
zend_generator * generator = zend_generator_get_current ( orig_generator ) ;
2015-03-06 23:28:12 +00:00
2012-05-26 21:59:22 +00:00
/* The generator is already closed, thus can't resume */
2017-07-22 09:14:00 +00:00
if ( UNEXPECTED ( ! generator - > execute_data ) ) {
2012-05-26 21:59:22 +00:00
return ;
}
2015-03-06 23:28:12 +00:00
try_again :
2012-08-25 15:40:08 +00:00
if ( generator - > flags & ZEND_GENERATOR_CURRENTLY_RUNNING ) {
2015-07-07 17:10:22 +00:00
zend_throw_error ( NULL , " Cannot resume an already running generator " ) ;
2015-04-01 10:49:11 +00:00
return ;
2012-08-25 15:40:08 +00:00
}
2015-11-25 08:49:39 +00:00
if ( UNEXPECTED ( ( orig_generator - > flags & ZEND_GENERATOR_DO_INIT ) ! = 0 & & ! Z_ISUNDEF ( generator - > value ) ) ) {
/* We must not advance Generator if we yield from a Generator being currently run */
2020-04-03 08:06:41 +00:00
orig_generator - > flags & = ~ ZEND_GENERATOR_DO_INIT ;
2015-11-25 08:49:39 +00:00
return ;
}
2020-08-11 13:48:40 +00:00
/* Drop the AT_FIRST_YIELD flag */
orig_generator - > flags & = ~ ZEND_GENERATOR_AT_FIRST_YIELD ;
2015-09-20 00:19:31 +00:00
if ( UNEXPECTED ( ! Z_ISUNDEF ( generator - > values ) ) ) {
if ( EXPECTED ( zend_generator_get_next_delegated_value ( generator ) = = SUCCESS ) ) {
2020-05-15 09:03:05 +00:00
orig_generator - > flags & = ~ ZEND_GENERATOR_DO_INIT ;
2015-02-20 11:59:56 +00:00
return ;
}
/* If there are no more deletegated values, resume the generator
2015-03-06 23:28:12 +00:00
* after the " yield from " expression . */
}
2012-05-27 01:50:31 +00:00
{
/* Backup executor globals */
2012-05-27 22:24:58 +00:00
zend_execute_data * original_execute_data = EG ( current_execute_data ) ;
2020-06-29 18:40:49 +00:00
uint32_t original_jit_trace_num = EG ( jit_trace_num ) ;
2015-01-09 16:58:41 +00:00
2012-05-27 01:50:31 +00:00
/* Set executor globals */
2012-05-27 22:24:58 +00:00
EG ( current_execute_data ) = generator - > execute_data ;
2020-06-29 18:40:49 +00:00
EG ( jit_trace_num ) = 0 ;
2012-05-27 01:50:31 +00:00
2012-06-08 22:40:47 +00:00
/* We want the backtrace to look as if the generator function was
* called from whatever method we are current running ( e . g . next ( ) ) .
2015-01-09 16:58:41 +00:00
* So we have to link generator call frame with caller call frame . */
2015-03-06 23:28:12 +00:00
if ( generator = = orig_generator ) {
generator - > execute_data - > prev_execute_data = original_execute_data ;
} else {
/* We need some execute_data placeholder in stacktrace to be replaced
* by the real stack trace when needed */
generator - > execute_data - > prev_execute_data = & orig_generator - > execute_fake ;
orig_generator - > execute_fake . prev_execute_data = original_execute_data ;
}
2012-06-03 00:00:11 +00:00
2016-05-11 21:44:18 +00:00
if ( UNEXPECTED ( generator - > frozen_call_stack ) ) {
/* Restore frozen call-stack */
2016-09-28 11:26:08 +00:00
zend_generator_restore_call_stack ( generator ) ;
2016-05-11 21:44:18 +00:00
}
2012-05-27 01:50:31 +00:00
/* Resume execution */
2012-08-25 15:40:08 +00:00
generator - > flags | = ZEND_GENERATOR_CURRENTLY_RUNNING ;
2020-09-01 15:57:49 +00:00
if ( ! ZEND_OBSERVER_ENABLED ) {
zend_execute_ex ( generator - > execute_data ) ;
} else {
zend_op_array * op_array = & generator - > execute_data - > func - > op_array ;
void * observer_handlers = ZEND_OBSERVER_HANDLERS ( op_array ) ;
if ( ! observer_handlers ) {
zend_observer_fcall_install ( ( zend_function * ) op_array ) ;
observer_handlers = ZEND_OBSERVER_HANDLERS ( op_array ) ;
}
ZEND_ASSERT ( observer_handlers ) ;
if ( observer_handlers ! = ZEND_OBSERVER_NOT_OBSERVED ) {
zend_observe_fcall_begin ( observer_handlers , generator - > execute_data ) ;
}
zend_execute_ex ( generator - > execute_data ) ;
if ( generator - > execute_data ) {
/* On the final return, this will be called from ZEND_GENERATOR_RETURN */
zend_observer_maybe_fcall_call_end ( generator - > execute_data , & generator - > value ) ;
}
}
2012-08-25 15:40:08 +00:00
generator - > flags & = ~ ZEND_GENERATOR_CURRENTLY_RUNNING ;
2012-05-27 01:50:31 +00:00
2016-05-11 21:44:18 +00:00
generator - > frozen_call_stack = NULL ;
if ( EXPECTED ( generator - > execute_data ) & &
UNEXPECTED ( generator - > execute_data - > call ) ) {
/* Frize call-stack */
2016-09-28 11:26:08 +00:00
generator - > frozen_call_stack = zend_generator_freeze_call_stack ( generator - > execute_data ) ;
2014-06-26 19:51:14 +00:00
}
2012-05-27 01:50:31 +00:00
/* Restore executor globals */
2012-05-27 22:24:58 +00:00
EG ( current_execute_data ) = original_execute_data ;
2020-06-29 18:40:49 +00:00
EG ( jit_trace_num ) = original_jit_trace_num ;
2012-09-22 17:12:21 +00:00
2012-07-22 15:46:46 +00:00
/* If an exception was thrown in the generator we have to internally
2015-03-06 23:28:12 +00:00
* rethrow it in the parent scope .
* In case we did yield from , the Exception must be rethrown into
* its calling frame ( see above in if ( check_yield_from ) . */
2012-07-22 15:46:46 +00:00
if ( UNEXPECTED ( EG ( exception ) ! = NULL ) ) {
2015-03-06 23:28:12 +00:00
if ( generator = = orig_generator ) {
2015-04-15 19:41:29 +00:00
zend_generator_close ( generator , 0 ) ;
2019-12-18 10:02:44 +00:00
if ( ! EG ( current_execute_data ) ) {
zend_throw_exception_internal ( NULL ) ;
} else if ( EG ( current_execute_data ) - > func & &
ZEND_USER_CODE ( EG ( current_execute_data ) - > func - > common . type ) ) {
2016-12-12 07:55:32 +00:00
zend_rethrow_exception ( EG ( current_execute_data ) ) ;
}
2015-03-06 23:28:12 +00:00
} else {
generator = zend_generator_get_current ( orig_generator ) ;
zend_generator_throw_exception ( generator , NULL ) ;
2020-06-08 09:31:28 +00:00
orig_generator - > flags & = ~ ZEND_GENERATOR_DO_INIT ;
2015-03-06 23:28:12 +00:00
goto try_again ;
}
2012-07-22 15:46:46 +00:00
}
2015-02-20 11:59:56 +00:00
2015-03-06 23:28:12 +00:00
/* yield from was used, try another resume. */
2015-09-20 00:19:31 +00:00
if ( UNEXPECTED ( ( generator ! = orig_generator & & ! Z_ISUNDEF ( generator - > retval ) ) | | ( generator - > execute_data & & ( generator - > execute_data - > opline - 1 ) - > opcode = = ZEND_YIELD_FROM ) ) ) {
2015-03-06 23:28:12 +00:00
generator = zend_generator_get_current ( orig_generator ) ;
2015-02-20 11:59:56 +00:00
goto try_again ;
2012-07-22 15:46:46 +00:00
}
2012-05-27 01:50:31 +00:00
}
2020-04-03 08:06:41 +00:00
orig_generator - > flags & = ~ ZEND_GENERATOR_DO_INIT ;
2012-05-26 15:53:13 +00:00
}
/* }}} */
2016-04-29 11:44:56 +00:00
static inline void zend_generator_ensure_initialized ( zend_generator * generator ) /* { { { */
2012-05-26 15:53:13 +00:00
{
2015-11-24 22:43:34 +00:00
if ( UNEXPECTED ( Z_TYPE ( generator - > value ) = = IS_UNDEF ) & & EXPECTED ( generator - > execute_data ) & & EXPECTED ( generator - > node . parent = = NULL ) ) {
2014-12-13 22:06:14 +00:00
zend_generator_resume ( generator ) ;
2012-08-25 15:40:08 +00:00
generator - > flags | = ZEND_GENERATOR_AT_FIRST_YIELD ;
}
}
/* }}} */
2016-04-29 11:44:56 +00:00
static inline void zend_generator_rewind ( zend_generator * generator ) /* { { { */
2012-08-25 15:40:08 +00:00
{
2014-12-13 22:06:14 +00:00
zend_generator_ensure_initialized ( generator ) ;
2012-08-25 15:40:08 +00:00
if ( ! ( generator - > flags & ZEND_GENERATOR_AT_FIRST_YIELD ) ) {
2014-12-13 22:06:14 +00:00
zend_throw_exception ( NULL , " Cannot rewind a generator that was already run " , 0 ) ;
2012-05-26 15:53:13 +00:00
}
}
/* }}} */
2020-07-01 13:32:55 +00:00
/* {{{ Rewind the generator */
2012-05-23 14:07:15 +00:00
ZEND_METHOD ( Generator , rewind )
{
2012-05-26 15:53:13 +00:00
zend_generator * generator ;
2020-01-03 11:11:45 +00:00
ZEND_PARSE_PARAMETERS_NONE ( ) ;
2012-05-26 15:53:13 +00:00
2018-11-15 16:54:19 +00:00
generator = ( zend_generator * ) Z_OBJ_P ( ZEND_THIS ) ;
2012-05-26 15:53:13 +00:00
2014-12-13 22:06:14 +00:00
zend_generator_rewind ( generator ) ;
2012-05-23 14:07:15 +00:00
}
/* }}} */
2020-07-01 13:32:55 +00:00
/* {{{ Check whether the generator is valid */
2012-05-23 14:07:15 +00:00
ZEND_METHOD ( Generator , valid )
{
2012-05-26 15:53:13 +00:00
zend_generator * generator ;
2020-01-03 11:11:45 +00:00
ZEND_PARSE_PARAMETERS_NONE ( ) ;
2012-05-26 15:53:13 +00:00
2018-11-15 16:54:19 +00:00
generator = ( zend_generator * ) Z_OBJ_P ( ZEND_THIS ) ;
2012-05-26 15:53:13 +00:00
2014-12-13 22:06:14 +00:00
zend_generator_ensure_initialized ( generator ) ;
2012-05-26 21:07:05 +00:00
2015-03-06 23:28:12 +00:00
zend_generator_get_current ( generator ) ;
2015-11-24 22:43:34 +00:00
RETURN_BOOL ( EXPECTED ( generator - > execute_data ! = NULL ) ) ;
2012-05-23 14:07:15 +00:00
}
/* }}} */
2020-07-01 13:32:55 +00:00
/* {{{ Get the current value */
2012-05-23 14:07:15 +00:00
ZEND_METHOD ( Generator , current )
{
2015-03-06 23:28:12 +00:00
zend_generator * generator , * root ;
2012-05-26 15:53:13 +00:00
2020-01-03 11:11:45 +00:00
ZEND_PARSE_PARAMETERS_NONE ( ) ;
2012-05-26 15:53:13 +00:00
2018-11-15 16:54:19 +00:00
generator = ( zend_generator * ) Z_OBJ_P ( ZEND_THIS ) ;
2012-05-26 15:53:13 +00:00
2014-12-13 22:06:14 +00:00
zend_generator_ensure_initialized ( generator ) ;
2012-05-26 21:07:05 +00:00
2015-03-06 23:28:12 +00:00
root = zend_generator_get_current ( generator ) ;
2015-11-24 22:43:34 +00:00
if ( EXPECTED ( generator - > execute_data ! = NULL & & Z_TYPE ( root - > value ) ! = IS_UNDEF ) ) {
2015-06-12 10:33:14 +00:00
zval * value = & root - > value ;
2018-07-09 09:46:46 +00:00
ZVAL_COPY_DEREF ( return_value , value ) ;
2012-05-26 21:07:05 +00:00
}
2012-05-23 14:07:15 +00:00
}
/* }}} */
2020-07-01 13:32:55 +00:00
/* {{{ Get the current key */
2012-05-23 14:07:15 +00:00
ZEND_METHOD ( Generator , key )
{
2015-03-06 23:28:12 +00:00
zend_generator * generator , * root ;
2012-05-26 15:53:13 +00:00
2020-01-03 11:11:45 +00:00
ZEND_PARSE_PARAMETERS_NONE ( ) ;
2012-05-26 15:53:13 +00:00
2018-11-15 16:54:19 +00:00
generator = ( zend_generator * ) Z_OBJ_P ( ZEND_THIS ) ;
2012-05-26 15:53:13 +00:00
2014-12-13 22:06:14 +00:00
zend_generator_ensure_initialized ( generator ) ;
2012-05-30 00:44:06 +00:00
2015-03-06 23:28:12 +00:00
root = zend_generator_get_current ( generator ) ;
2015-11-24 22:43:34 +00:00
if ( EXPECTED ( generator - > execute_data ! = NULL & & Z_TYPE ( root - > key ) ! = IS_UNDEF ) ) {
2015-06-12 10:33:14 +00:00
zval * key = & root - > key ;
2018-07-09 09:46:46 +00:00
ZVAL_COPY_DEREF ( return_value , key ) ;
2012-05-30 00:44:06 +00:00
}
2012-05-23 14:07:15 +00:00
}
/* }}} */
2020-07-01 13:32:55 +00:00
/* {{{ Advances the generator */
2012-05-23 14:07:15 +00:00
ZEND_METHOD ( Generator , next )
{
2012-05-26 15:53:13 +00:00
zend_generator * generator ;
2020-01-03 11:11:45 +00:00
ZEND_PARSE_PARAMETERS_NONE ( ) ;
2012-05-26 15:53:13 +00:00
2018-11-15 16:54:19 +00:00
generator = ( zend_generator * ) Z_OBJ_P ( ZEND_THIS ) ;
2012-05-26 15:53:13 +00:00
2014-12-13 22:06:14 +00:00
zend_generator_ensure_initialized ( generator ) ;
2012-05-26 21:07:05 +00:00
2014-12-13 22:06:14 +00:00
zend_generator_resume ( generator ) ;
2012-05-23 14:07:15 +00:00
}
/* }}} */
2020-07-01 13:32:55 +00:00
/* {{{ Sends a value to the generator */
2012-05-29 15:34:33 +00:00
ZEND_METHOD ( Generator , send )
{
2012-06-23 12:43:52 +00:00
zval * value ;
2015-03-06 23:28:12 +00:00
zend_generator * generator , * root ;
2012-05-29 15:34:33 +00:00
2015-09-20 00:19:31 +00:00
ZEND_PARSE_PARAMETERS_START ( 1 , 1 )
Z_PARAM_ZVAL ( value )
ZEND_PARSE_PARAMETERS_END ( ) ;
2012-05-29 15:34:33 +00:00
2018-11-15 16:54:19 +00:00
generator = ( zend_generator * ) Z_OBJ_P ( ZEND_THIS ) ;
2012-05-29 15:34:33 +00:00
2015-01-03 09:22:58 +00:00
zend_generator_ensure_initialized ( generator ) ;
2012-05-29 15:34:33 +00:00
2012-05-29 16:01:08 +00:00
/* The generator is already closed, thus can't send anything */
2015-09-20 00:19:31 +00:00
if ( UNEXPECTED ( ! generator - > execute_data ) ) {
2012-05-29 16:01:08 +00:00
return ;
}
2015-03-06 23:28:12 +00:00
root = zend_generator_get_current ( generator ) ;
2013-11-30 12:05:40 +00:00
/* Put sent value in the target VAR slot, if it is used */
2015-03-06 23:28:12 +00:00
if ( root - > send_target ) {
ZVAL_COPY ( root - > send_target , value ) ;
2013-11-30 12:05:40 +00:00
}
2012-05-29 15:34:33 +00:00
2014-12-13 22:06:14 +00:00
zend_generator_resume ( generator ) ;
2012-05-31 18:03:18 +00:00
2015-03-06 23:28:12 +00:00
root = zend_generator_get_current ( generator ) ;
2015-11-24 22:43:34 +00:00
if ( EXPECTED ( generator - > execute_data ) ) {
2015-06-12 10:33:14 +00:00
zval * value = & root - > value ;
2018-07-09 09:46:46 +00:00
ZVAL_COPY_DEREF ( return_value , value ) ;
2012-05-31 18:03:18 +00:00
}
2012-05-29 15:34:33 +00:00
}
2012-08-29 18:49:14 +00:00
/* }}} */
2012-05-29 15:34:33 +00:00
2020-07-01 13:32:55 +00:00
/* {{{ Throws an exception into the generator */
2012-12-17 21:02:32 +00:00
ZEND_METHOD ( Generator , throw )
{
2017-10-30 21:48:20 +00:00
zval * exception ;
2012-12-17 21:02:32 +00:00
zend_generator * generator ;
2015-09-20 00:19:31 +00:00
ZEND_PARSE_PARAMETERS_START ( 1 , 1 )
2019-10-15 12:30:23 +00:00
Z_PARAM_OBJECT_OF_CLASS ( exception , zend_ce_throwable ) ;
2015-09-20 00:19:31 +00:00
ZEND_PARSE_PARAMETERS_END ( ) ;
2012-12-17 21:02:32 +00:00
2017-10-30 21:48:20 +00:00
Z_TRY_ADDREF_P ( exception ) ;
2012-12-17 21:02:32 +00:00
2018-11-15 16:54:19 +00:00
generator = ( zend_generator * ) Z_OBJ_P ( ZEND_THIS ) ;
2012-12-17 21:02:32 +00:00
2015-01-03 09:22:58 +00:00
zend_generator_ensure_initialized ( generator ) ;
2013-12-01 12:37:56 +00:00
2012-12-17 21:02:32 +00:00
if ( generator - > execute_data ) {
2015-03-06 23:28:12 +00:00
zend_generator * root = zend_generator_get_current ( generator ) ;
2012-12-17 21:02:32 +00:00
2017-10-30 21:48:20 +00:00
zend_generator_throw_exception ( root , exception ) ;
2012-12-17 21:02:32 +00:00
2014-12-13 22:06:14 +00:00
zend_generator_resume ( generator ) ;
2012-12-17 21:02:32 +00:00
2015-03-06 23:28:12 +00:00
root = zend_generator_get_current ( generator ) ;
2015-11-24 22:43:34 +00:00
if ( generator - > execute_data ) {
2015-06-12 10:33:14 +00:00
zval * value = & root - > value ;
2018-07-09 09:46:46 +00:00
ZVAL_COPY_DEREF ( return_value , value ) ;
2012-12-17 21:02:32 +00:00
}
} else {
/* If the generator is already closed throw the exception in the
* current context */
2017-10-30 21:48:20 +00:00
zend_throw_exception_object ( exception ) ;
2012-12-17 21:02:32 +00:00
}
}
/* }}} */
2020-07-01 13:32:55 +00:00
/* {{{ Retrieves the return value of the generator */
2015-02-19 19:17:37 +00:00
ZEND_METHOD ( Generator , getReturn )
{
zend_generator * generator ;
2020-01-03 11:11:45 +00:00
ZEND_PARSE_PARAMETERS_NONE ( ) ;
2015-02-19 19:17:37 +00:00
2018-11-15 16:54:19 +00:00
generator = ( zend_generator * ) Z_OBJ_P ( ZEND_THIS ) ;
2015-02-19 19:17:37 +00:00
zend_generator_ensure_initialized ( generator ) ;
2015-09-20 00:19:31 +00:00
if ( UNEXPECTED ( EG ( exception ) ) ) {
2015-02-19 19:17:37 +00:00
return ;
}
if ( Z_ISUNDEF ( generator - > retval ) ) {
/* Generator hasn't returned yet -> error! */
zend_throw_exception ( NULL ,
" Cannot get return value of a generator that hasn't returned " , 0 ) ;
return ;
}
ZVAL_COPY ( return_value , & generator - > retval ) ;
}
/* }}} */
2012-06-22 23:28:16 +00:00
/* get_iterator implementation */
2014-12-13 22:06:14 +00:00
static void zend_generator_iterator_dtor ( zend_object_iterator * iterator ) /* { { { */
2012-06-22 23:28:16 +00:00
{
2014-04-09 06:47:03 +00:00
zval_ptr_dtor ( & iterator - > data ) ;
2012-06-22 23:28:16 +00:00
}
/* }}} */
2014-12-13 22:06:14 +00:00
static int zend_generator_iterator_valid ( zend_object_iterator * iterator ) /* { { { */
2012-06-22 23:28:16 +00:00
{
2014-02-27 11:19:02 +00:00
zend_generator * generator = ( zend_generator * ) Z_OBJ ( iterator - > data ) ;
2012-06-22 23:28:16 +00:00
2014-12-13 22:06:14 +00:00
zend_generator_ensure_initialized ( generator ) ;
2012-06-22 23:28:16 +00:00
2015-03-06 23:28:12 +00:00
zend_generator_get_current ( generator ) ;
2015-11-24 22:43:34 +00:00
return generator - > execute_data ? SUCCESS : FAILURE ;
2012-06-22 23:28:16 +00:00
}
/* }}} */
2014-12-13 22:06:14 +00:00
static zval * zend_generator_iterator_get_data ( zend_object_iterator * iterator ) /* { { { */
2012-06-22 23:28:16 +00:00
{
2015-03-06 23:28:12 +00:00
zend_generator * generator = ( zend_generator * ) Z_OBJ ( iterator - > data ) , * root ;
2012-06-22 23:28:16 +00:00
2014-12-13 22:06:14 +00:00
zend_generator_ensure_initialized ( generator ) ;
2012-06-22 23:28:16 +00:00
2015-03-06 23:28:12 +00:00
root = zend_generator_get_current ( generator ) ;
return & root - > value ;
2012-06-22 23:28:16 +00:00
}
/* }}} */
2014-12-13 22:06:14 +00:00
static void zend_generator_iterator_get_key ( zend_object_iterator * iterator , zval * key ) /* { { { */
2012-06-22 23:28:16 +00:00
{
2015-03-06 23:28:12 +00:00
zend_generator * generator = ( zend_generator * ) Z_OBJ ( iterator - > data ) , * root ;
2012-06-22 23:28:16 +00:00
2014-12-13 22:06:14 +00:00
zend_generator_ensure_initialized ( generator ) ;
2012-06-22 23:28:16 +00:00
2015-03-06 23:28:12 +00:00
root = zend_generator_get_current ( generator ) ;
2015-09-20 00:19:31 +00:00
if ( EXPECTED ( Z_TYPE ( root - > key ) ! = IS_UNDEF ) ) {
2015-06-12 10:33:14 +00:00
zval * zv = & root - > key ;
2018-07-09 09:46:46 +00:00
ZVAL_COPY_DEREF ( key , zv ) ;
2013-02-16 18:13:36 +00:00
} else {
ZVAL_NULL ( key ) ;
2012-06-22 23:28:16 +00:00
}
}
/* }}} */
2014-12-13 22:06:14 +00:00
static void zend_generator_iterator_move_forward ( zend_object_iterator * iterator ) /* { { { */
2012-06-22 23:28:16 +00:00
{
2014-02-27 11:19:02 +00:00
zend_generator * generator = ( zend_generator * ) Z_OBJ ( iterator - > data ) ;
2012-06-22 23:28:16 +00:00
2014-12-13 22:06:14 +00:00
zend_generator_ensure_initialized ( generator ) ;
2012-06-22 23:28:16 +00:00
2014-12-13 22:06:14 +00:00
zend_generator_resume ( generator ) ;
2012-06-22 23:28:16 +00:00
}
/* }}} */
2014-12-13 22:06:14 +00:00
static void zend_generator_iterator_rewind ( zend_object_iterator * iterator ) /* { { { */
2012-08-25 15:40:08 +00:00
{
2014-02-27 11:19:02 +00:00
zend_generator * generator = ( zend_generator * ) Z_OBJ ( iterator - > data ) ;
2012-08-25 15:40:08 +00:00
2014-12-13 22:06:14 +00:00
zend_generator_rewind ( generator ) ;
2012-08-25 15:40:08 +00:00
}
/* }}} */
2020-07-01 10:03:13 +00:00
static HashTable * zend_generator_iterator_get_gc (
zend_object_iterator * iterator , zval * * table , int * n )
{
* table = & iterator - > data ;
* n = 1 ;
return NULL ;
}
2017-12-14 11:21:22 +00:00
static const zend_object_iterator_funcs zend_generator_iterator_functions = {
2012-06-22 23:28:16 +00:00
zend_generator_iterator_dtor ,
zend_generator_iterator_valid ,
zend_generator_iterator_get_data ,
zend_generator_iterator_get_key ,
zend_generator_iterator_move_forward ,
2016-04-29 11:44:56 +00:00
zend_generator_iterator_rewind ,
2020-07-01 09:49:44 +00:00
NULL ,
2020-07-01 10:03:13 +00:00
zend_generator_iterator_get_gc ,
2012-06-22 23:28:16 +00:00
} ;
2020-08-28 13:41:27 +00:00
/* by_ref is int due to Iterator API */
2014-12-13 22:06:14 +00:00
zend_object_iterator * zend_generator_get_iterator ( zend_class_entry * ce , zval * object , int by_ref ) /* { { { */
2012-06-22 23:28:16 +00:00
{
2014-02-27 11:19:02 +00:00
zend_object_iterator * iterator ;
zend_generator * generator = ( zend_generator * ) Z_OBJ_P ( object ) ;
2012-06-23 12:43:52 +00:00
2012-08-29 18:46:56 +00:00
if ( ! generator - > execute_data ) {
2014-12-13 22:06:14 +00:00
zend_throw_exception ( NULL , " Cannot traverse an already closed generator " , 0 ) ;
2012-08-29 18:46:56 +00:00
return NULL ;
}
2015-09-20 00:19:31 +00:00
if ( UNEXPECTED ( by_ref ) & & ! ( generator - > execute_data - > func - > op_array . fn_flags & ZEND_ACC_RETURN_REFERENCE ) ) {
2014-12-13 22:06:14 +00:00
zend_throw_exception ( NULL , " You can only iterate a generator by-reference if it declared that it yields by-reference " , 0 ) ;
2012-08-29 18:46:56 +00:00
return NULL ;
2012-07-17 11:24:27 +00:00
}
2020-06-30 16:26:29 +00:00
iterator = emalloc ( sizeof ( zend_object_iterator ) ) ;
2014-12-13 22:06:14 +00:00
zend_iterator_init ( iterator ) ;
2012-06-22 23:28:16 +00:00
2014-02-27 11:19:02 +00:00
iterator - > funcs = & zend_generator_iterator_functions ;
2020-06-17 10:34:04 +00:00
ZVAL_OBJ_COPY ( & iterator - > data , Z_OBJ_P ( object ) ) ;
2012-06-22 23:28:16 +00:00
2014-02-27 11:19:02 +00:00
return iterator ;
2012-06-22 23:28:16 +00:00
}
/* }}} */
2014-12-13 22:06:14 +00:00
void zend_register_generator_ce ( void ) /* { { { */
2012-05-19 22:03:27 +00:00
{
zend_class_entry ce ;
2020-04-25 21:10:07 +00:00
INIT_CLASS_ENTRY ( ce , " Generator " , class_Generator_methods ) ;
2014-12-13 22:06:14 +00:00
zend_ce_generator = zend_register_internal_class ( & ce ) ;
2020-09-07 17:06:53 +00:00
zend_ce_generator - > ce_flags | = ZEND_ACC_FINAL | ZEND_ACC_NO_DYNAMIC_PROPERTIES ;
2012-05-20 12:19:16 +00:00
zend_ce_generator - > create_object = zend_generator_create ;
2012-08-20 14:01:16 +00:00
zend_ce_generator - > serialize = zend_class_serialize_deny ;
zend_ce_generator - > unserialize = zend_class_unserialize_deny ;
2012-05-20 12:19:16 +00:00
2019-02-18 16:14:22 +00:00
/* get_iterator has to be assigned *after* implementing the interface */
2014-12-13 22:06:14 +00:00
zend_class_implements ( zend_ce_generator , 1 , zend_ce_iterator ) ;
2012-07-26 15:07:24 +00:00
zend_ce_generator - > get_iterator = zend_generator_get_iterator ;
2012-05-23 14:07:15 +00:00
2018-05-31 08:57:22 +00:00
memcpy ( & zend_generator_handlers , & std_object_handlers , sizeof ( zend_object_handlers ) ) ;
2014-02-10 06:04:30 +00:00
zend_generator_handlers . free_obj = zend_generator_free_storage ;
zend_generator_handlers . dtor_obj = zend_generator_dtor_storage ;
2016-02-10 18:03:02 +00:00
zend_generator_handlers . get_gc = zend_generator_get_gc ;
2013-03-25 16:40:58 +00:00
zend_generator_handlers . clone_obj = NULL ;
2014-02-10 06:04:30 +00:00
zend_generator_handlers . get_constructor = zend_generator_get_constructor ;
2015-03-06 23:28:12 +00:00
INIT_CLASS_ENTRY ( ce , " ClosedGeneratorException " , NULL ) ;
2015-07-03 14:45:03 +00:00
zend_ce_ClosedGeneratorException = zend_register_internal_class_ex ( & ce , zend_ce_exception ) ;
2012-05-19 22:03:27 +00:00
}
/* }}} */