2012-05-20 06:03:27 +08:00
/*
+ - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - +
| Zend Engine |
+ - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - +
2019-01-30 17:23:29 +08:00
| Copyright ( c ) Zend Technologies Ltd . ( http : //www.zend.com) |
2012-05-20 06:03:27 +08: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-07 07:28:12 +08:00
| Bob Weinand < bobwei9 @ hotmail . com > |
2012-05-20 06:03:27 +08:00
+ - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - +
*/
# include "zend.h"
# include "zend_API.h"
2012-05-23 22:07:15 +08:00
# include "zend_interfaces.h"
2012-07-22 23:46:46 +08:00
# include "zend_exceptions.h"
2012-05-20 06:03:27 +08:00
# include "zend_generators.h"
2018-01-12 03:15:45 +08:00
# include "zend_closures.h"
2019-10-15 20:30:23 +08:00
# include "zend_generators_arginfo.h"
2012-05-20 06:03:27 +08:00
ZEND_API zend_class_entry * zend_ce_generator ;
2015-03-07 07:28:12 +08:00
ZEND_API zend_class_entry * zend_ce_ClosedGeneratorException ;
2012-05-20 20:19:16 +08:00
static zend_object_handlers zend_generator_handlers ;
2014-12-14 06:06:14 +08:00
static zend_object * zend_generator_create ( zend_class_entry * class_type ) ;
2013-02-02 01:33:26 +08:00
2016-09-29 02:06:10 +08:00
ZEND_API void zend_generator_restore_call_stack ( zend_generator * generator ) /* { { { */
2016-05-12 05:44:18 +08: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-11 07:08:32 +08:00
Z_PTR ( call - > This ) ) ;
2016-05-12 05:44:18 +08: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-29 02:06:10 +08:00
ZEND_API zend_execute_data * zend_generator_freeze_call_stack ( zend_execute_data * execute_data ) /* { { { */
2016-05-12 05:44:18 +08: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 18:25:42 +08:00
static void zend_generator_cleanup_unfinished_execution (
2019-10-28 17:23:20 +08:00
zend_generator * generator , zend_execute_data * execute_data , uint32_t catch_op_num ) /* {{{ */
2013-11-30 20:35:33 +08:00
{
2019-12-20 23:15:24 +08:00
zend_op_array * op_array = & execute_data - > func - > op_array ;
if ( execute_data - > opline ! = op_array - > opcodes ) {
2015-06-24 02:13:25 +08:00
/* -1 required because we want the last run opcode, not the next to-be-run one. */
2019-12-20 23:15:24 +08:00
uint32_t op_num = execute_data - > opline - op_array - > opcodes - 1 ;
2015-06-24 02:13:25 +08:00
2016-05-12 05:44:18 +08:00
if ( UNEXPECTED ( generator - > frozen_call_stack ) ) {
2019-10-28 17:23:20 +08: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 19:26:08 +08:00
zend_generator_restore_call_stack ( generator ) ;
2019-10-28 17:23:20 +08:00
generator - > execute_data = save_ex ;
2016-05-12 05:44:18 +08:00
}
2019-12-20 23:15:24 +08:00
2017-05-18 01:56:49 +08:00
zend_cleanup_unfinished_execution ( execute_data , op_num , catch_op_num ) ;
2015-06-21 01:09:07 +08:00
}
2013-11-30 20:35:33 +08:00
}
/* }}} */
2014-12-14 06:06:14 +08:00
ZEND_API void zend_generator_close ( zend_generator * generator , zend_bool finished_execution ) /* { { { */
2012-05-20 20:19:16 +08:00
{
2015-09-20 08:19:31 +08:00
if ( EXPECTED ( generator - > execute_data ) ) {
2012-05-23 20:20:25 +08:00
zend_execute_data * execute_data = generator - > execute_data ;
2019-10-28 17:23:20 +08: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 21:47:55 +08:00
2016-04-28 20:17:24 +08:00
if ( EX_CALL_INFO ( ) & ZEND_CALL_HAS_SYMBOL_TABLE ) {
2014-12-14 06:06:14 +08:00
zend_clean_and_cache_symbol_table ( execute_data - > symbol_table ) ;
2012-05-23 20:20:25 +08:00
}
2015-05-14 23:17:39 +08:00
/* always free the CV's, in the symtable are only not-free'd IS_INDIRECT's */
zend_free_compiled_variables ( execute_data ) ;
2012-05-23 20:20:25 +08:00
2019-03-01 21:32:11 +08:00
if ( EX_CALL_INFO ( ) & ZEND_CALL_RELEASE_THIS ) {
2014-10-03 23:32:46 +08:00
OBJ_RELEASE ( Z_OBJ ( execute_data - > This ) ) ;
2014-03-28 06:11:22 +08:00
}
2012-05-27 09:50:31 +08:00
2015-09-20 08:19:31 +08: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 23:33:31 +08:00
generator - > execute_data = NULL ;
2013-06-30 03:51:54 +08:00
return ;
}
2019-10-28 17:23:20 +08:00
zend_vm_stack_free_extra_args ( execute_data ) ;
2012-06-09 06:40:47 +08:00
2015-09-20 08:19:31 +08:00
/* Some cleanups are only necessary if the generator was closed
2013-11-30 20:35:33 +08:00
* before it could finish execution ( reach a return statement ) . */
2015-09-20 08:19:31 +08:00
if ( UNEXPECTED ( ! finished_execution ) ) {
2019-10-28 17:23:20 +08:00
zend_generator_cleanup_unfinished_execution ( generator , execute_data , 0 ) ;
2013-11-30 20:35:33 +08:00
}
2015-04-09 03:11:42 +08:00
/* Free closure object */
2019-03-01 21:32:11 +08:00
if ( EX_CALL_INFO ( ) & ZEND_CALL_CLOSURE ) {
2018-01-12 03:15:45 +08:00
OBJ_RELEASE ( ZEND_CLOSURE_OBJECT ( EX ( func ) ) ) ;
2012-09-05 13:50:55 +08:00
}
2016-02-11 23:38:30 +08:00
/* Free GC buffer. GC for closed generators doesn't need an allocated buffer */
if ( generator - > gc_buffer ) {
efree ( generator - > gc_buffer ) ;
generator - > gc_buffer = NULL ;
}
2019-10-28 17:23:20 +08:00
efree ( execute_data ) ;
2012-05-23 20:20:25 +08:00
}
2012-05-27 05:59:22 +08:00
}
/* }}} */
2016-02-16 05:37:21 +08:00
static zend_generator * zend_generator_get_child ( zend_generator_node * node , zend_generator * leaf ) ;
2014-12-14 06:06:14 +08:00
static void zend_generator_dtor_storage ( zend_object * object ) /* { { { */
2013-01-31 06:52:02 +08:00
{
2014-02-10 14:04:30 +08:00
zend_generator * generator = ( zend_generator * ) object ;
2013-01-31 06:52:02 +08:00
zend_execute_data * ex = generator - > execute_data ;
2019-12-20 23:15:24 +08:00
uint32_t op_num , try_catch_offset ;
2013-01-31 06:52:02 +08:00
int i ;
2016-02-16 05:37:21 +08: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 ) ;
}
if ( EXPECTED ( generator - > node . children = = 0 ) ) {
zend_generator * root = generator - > node . ptr . root , * next ;
while ( UNEXPECTED ( root ! = generator ) ) {
next = zend_generator_get_child ( & root - > node , generator ) ;
2018-01-13 05:48:29 +08:00
generator - > node . ptr . root = next ;
next - > node . parent = NULL ;
2016-02-16 05:37:21 +08:00
OBJ_RELEASE ( & root - > std ) ;
root = next ;
}
}
2018-01-13 04:24:04 +08:00
if ( EXPECTED ( ! ex ) | | EXPECTED ( ! ( ex - > func - > op_array . fn_flags & ZEND_ACC_HAS_FINALLY_BLOCK ) )
| | CG ( unclean_shutdown ) ) {
2013-01-31 06:52:02 +08:00
return ;
}
/* -1 required because we want the last run opcode, not the
* next to - be - run one . */
2014-06-27 03:51:14 +08:00
op_num = ex - > opline - ex - > func - > op_array . opcodes - 1 ;
2019-12-20 23:15:24 +08:00
try_catch_offset = - 1 ;
2013-01-31 06:52:02 +08:00
2019-12-20 23:15:24 +08:00
/* Find the innermost try/catch that we are inside of. */
2014-06-27 03:51:14 +08: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-31 06:52:02 +08:00
if ( op_num < try_catch - > try_op ) {
break ;
}
2019-12-20 23:15:24 +08:00
if ( op_num < try_catch - > catch_op | | op_num < try_catch - > finally_end ) {
try_catch_offset = i ;
2013-01-31 06:52:02 +08:00
}
}
2019-12-20 23:15:24 +08: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 14:56:43 +08:00
2019-12-20 23:15:24 +08: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 18:25:42 +08:00
2019-12-20 23:15:24 +08: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 23:40:10 +08:00
2019-12-20 23:15:24 +08:00
try_catch_offset - - ;
2013-01-31 06:52:02 +08:00
}
}
/* }}} */
2014-12-14 06:06:14 +08:00
static void zend_generator_free_storage ( zend_object * object ) /* { { { */
2012-05-27 05:59:22 +08:00
{
2014-02-10 14:04:30 +08:00
zend_generator * generator = ( zend_generator * ) object ;
2014-12-14 06:06:14 +08:00
zend_generator_close ( generator , 0 ) ;
2012-05-27 05:59:22 +08:00
2015-11-25 06:43:34 +08: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 08:19:31 +08:00
if ( EXPECTED ( ! Z_ISUNDEF ( generator - > retval ) ) ) {
2015-02-20 03:17:37 +08:00
zval_ptr_dtor ( & generator - > retval ) ;
}
2018-01-10 04:49:16 +08:00
if ( UNEXPECTED ( generator - > node . children > 1 ) ) {
zend_hash_destroy ( generator - > node . child . ht ) ;
efree ( generator - > node . child . ht ) ;
2015-03-07 07:28:12 +08:00
}
2014-12-14 06:06:14 +08:00
zend_object_std_dtor ( & generator - > std ) ;
2014-03-19 21:00:28 +08:00
2014-04-09 05:50:15 +08:00
if ( generator - > iterator ) {
2014-12-14 06:06:14 +08:00
zend_iterator_dtor ( generator - > iterator ) ;
2014-03-21 03:05:38 +08:00
}
2012-05-20 20:19:16 +08:00
}
/* }}} */
2016-02-11 23:38:30 +08:00
static uint32_t calc_gc_buffer_size ( zend_generator * generator ) /* { { { */
{
uint32_t size = 4 ; /* value, key, retval, values */
if ( generator - > execute_data ) {
zend_execute_data * execute_data = generator - > execute_data ;
zend_op_array * op_array = & EX ( func ) - > op_array ;
2016-02-13 01:50:19 +08:00
/* Compiled variables */
2016-04-28 20:17:24 +08:00
if ( ! ( EX_CALL_INFO ( ) & ZEND_CALL_HAS_SYMBOL_TABLE ) ) {
2016-02-13 01:50:19 +08:00
size + = op_array - > last_var ;
}
/* Extra args */
if ( EX_CALL_INFO ( ) & ZEND_CALL_FREE_EXTRA_ARGS ) {
size + = EX_NUM_ARGS ( ) - op_array - > num_args ;
}
2019-08-14 23:48:57 +08:00
size + = ( EX_CALL_INFO ( ) & ZEND_CALL_RELEASE_THIS ) ! = 0 ; /* $this */
2016-02-11 23:38:30 +08:00
size + = ( EX_CALL_INFO ( ) & ZEND_CALL_CLOSURE ) ! = 0 ; /* Closure object */
2016-02-13 01:50:19 +08:00
2018-09-29 01:14:47 +08:00
/* Live vars */
if ( execute_data - > opline ! = op_array - > opcodes ) {
/* -1 required because we want the last run opcode, not the next to-be-run one. */
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 ) {
/* Further ranges will not be relevant... */
break ;
} else if ( op_num < range - > end ) {
/* LIVE_ROPE and LIVE_SILENCE not relevant for GC */
uint32_t kind = range - > var & ZEND_LIVE_MASK ;
if ( kind = = ZEND_LIVE_TMPVAR | | kind = = ZEND_LIVE_LOOP ) {
size + + ;
}
}
}
}
2016-02-13 01:50:19 +08:00
/* Yield from root references */
if ( generator - > node . children = = 0 ) {
2018-01-13 18:04:26 +08:00
zend_generator * root = generator - > node . ptr . root ;
while ( root ! = generator ) {
root = zend_generator_get_child ( & root - > node , generator ) ;
2016-02-13 01:50:19 +08:00
size + + ;
}
}
2016-02-11 23:38:30 +08:00
}
return size ;
}
/* }}} */
2019-01-31 23:47:58 +08:00
static HashTable * zend_generator_get_gc ( zend_object * object , zval * * table , int * n ) /* { { { */
2016-02-11 02:03:02 +08:00
{
2019-01-31 23:47:58 +08:00
zend_generator * generator = ( zend_generator * ) object ;
2016-02-11 23:38:30 +08:00
zend_execute_data * execute_data = generator - > execute_data ;
2016-02-13 01:50:19 +08:00
zend_op_array * op_array ;
2016-02-11 23:38:30 +08:00
zval * gc_buffer ;
uint32_t gc_buffer_size ;
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-13 01:50:19 +08:00
op_array = & EX ( func ) - > op_array ;
2016-02-11 23:38:30 +08:00
gc_buffer_size = calc_gc_buffer_size ( generator ) ;
2016-02-13 01:50:19 +08:00
if ( generator - > gc_buffer_size < gc_buffer_size ) {
generator - > gc_buffer = safe_erealloc ( generator - > gc_buffer , sizeof ( zval ) , gc_buffer_size , 0 ) ;
generator - > gc_buffer_size = gc_buffer_size ;
2016-02-11 23:38:30 +08:00
}
* n = gc_buffer_size ;
* table = gc_buffer = generator - > gc_buffer ;
ZVAL_COPY_VALUE ( gc_buffer + + , & generator - > value ) ;
ZVAL_COPY_VALUE ( gc_buffer + + , & generator - > key ) ;
ZVAL_COPY_VALUE ( gc_buffer + + , & generator - > retval ) ;
ZVAL_COPY_VALUE ( gc_buffer + + , & generator - > values ) ;
2016-04-28 20:17:24 +08:00
if ( ! ( EX_CALL_INFO ( ) & ZEND_CALL_HAS_SYMBOL_TABLE ) ) {
2016-02-11 23:38:30 +08:00
uint32_t i , num_cvs = EX ( func ) - > op_array . last_var ;
for ( i = 0 ; i < num_cvs ; i + + ) {
ZVAL_COPY_VALUE ( gc_buffer + + , EX_VAR_NUM ( i ) ) ;
}
}
2016-02-13 01:50:19 +08: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 ) {
ZVAL_COPY_VALUE ( gc_buffer + + , zv + + ) ;
}
}
2019-08-14 23:48:57 +08:00
if ( EX_CALL_INFO ( ) & ZEND_CALL_RELEASE_THIS ) {
2016-02-11 23:38:30 +08:00
ZVAL_OBJ ( gc_buffer + + , Z_OBJ ( execute_data - > This ) ) ;
}
if ( EX_CALL_INFO ( ) & ZEND_CALL_CLOSURE ) {
2018-01-12 03:15:45 +08:00
ZVAL_OBJ ( gc_buffer + + , ZEND_CLOSURE_OBJECT ( EX ( func ) ) ) ;
2016-02-11 23:38:30 +08:00
}
2018-09-29 01:14:47 +08: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 ) {
ZVAL_COPY_VALUE ( gc_buffer + + , var ) ;
}
}
}
}
2016-02-13 01:50:19 +08:00
if ( generator - > node . children = = 0 ) {
2018-01-13 18:04:26 +08:00
zend_generator * root = generator - > node . ptr . root ;
while ( root ! = generator ) {
ZVAL_OBJ ( gc_buffer + + , & root - > std ) ;
root = zend_generator_get_child ( & root - > node , generator ) ;
2016-02-13 01:50:19 +08:00
}
}
2016-04-28 20:17:24 +08:00
if ( EX_CALL_INFO ( ) & ZEND_CALL_HAS_SYMBOL_TABLE ) {
return execute_data - > symbol_table ;
} else {
return NULL ;
}
2016-02-11 02:03:02 +08:00
}
/* }}} */
2014-12-14 06:06:14 +08:00
static zend_object * zend_generator_create ( zend_class_entry * class_type ) /* { { { */
2012-05-20 20:19:16 +08:00
{
zend_generator * generator ;
generator = emalloc ( sizeof ( zend_generator ) ) ;
memset ( generator , 0 , sizeof ( zend_generator ) ) ;
2012-05-30 11:05:49 +08:00
/* The key will be incremented on first use, so it'll start at 0 */
generator - > largest_used_integer_key = - 1 ;
2015-02-20 03:17:37 +08:00
ZVAL_UNDEF ( & generator - > retval ) ;
2015-02-20 19:59:56 +08:00
ZVAL_UNDEF ( & generator - > values ) ;
2015-03-07 07:28:12 +08: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-20 03:17:37 +08:00
2014-12-14 06:06:14 +08:00
zend_object_std_init ( & generator - > std , class_type ) ;
2014-02-10 14:04:30 +08:00
generator - > std . handlers = & zend_generator_handlers ;
2012-05-20 20:19:16 +08:00
2014-02-10 14:04:30 +08:00
return ( zend_object * ) generator ;
2012-05-20 20:19:16 +08:00
}
/* }}} */
2015-08-19 19:40:56 +08:00
static ZEND_COLD zend_function * zend_generator_get_constructor ( zend_object * object ) /* { { { */
2012-05-20 20:19:16 +08:00
{
2015-07-08 01:10:22 +08:00
zend_throw_error ( NULL , " The \" Generator \" class is reserved for internal use and cannot be manually instantiated " ) ;
2012-05-20 20:19:16 +08:00
return NULL ;
}
/* }}} */
2012-05-20 06:03:27 +08:00
2015-03-07 07:28:12 +08:00
ZEND_API zend_execute_data * zend_generator_check_placeholder_frame ( zend_execute_data * ptr )
{
2016-04-01 21:17:49 +08:00
if ( ! ptr - > func & & Z_TYPE ( ptr - > This ) = = IS_OBJECT ) {
2015-03-07 07:28:12 +08: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 10:41:36 +08:00
zend_execute_data * original_execute_data = EG ( current_execute_data ) ;
2015-11-26 06:09:44 +08: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-18 04:19:41 +08:00
/* Throw the exception in the context of the generator. Decrementing the opline
* to pretend the exception happened during the YIELD opcode . */
2015-03-07 07:28:12 +08:00
EG ( current_execute_data ) = generator - > execute_data ;
2015-04-18 04:19:41 +08:00
generator - > execute_data - > opline - - ;
2015-03-07 07:28:12 +08:00
if ( exception ) {
zend_throw_exception_object ( exception ) ;
} else {
2016-12-12 15:55:32 +08:00
zend_rethrow_exception ( EG ( current_execute_data ) ) ;
2015-03-07 07:28:12 +08:00
}
2015-04-18 04:19:41 +08:00
generator - > execute_data - > opline + + ;
2015-03-07 07:28:12 +08:00
EG ( current_execute_data ) = original_execute_data ;
}
static zend_generator * zend_generator_get_child ( zend_generator_node * node , zend_generator * leaf )
{
2018-01-10 04:49:16 +08:00
if ( node - > children = = 0 ) {
return NULL ;
} else if ( node - > children = = 1 ) {
return node - > child . single . child ;
} else {
return zend_hash_index_find_ptr ( node - > child . ht , ( zend_ulong ) leaf ) ;
2015-03-07 07:28:12 +08:00
}
}
static zend_generator_node * zend_generator_search_multi_children_node ( zend_generator_node * node )
{
while ( node - > children = = 1 ) {
2018-01-10 04:49:16 +08:00
node = & node - > child . single . child - > node ;
2015-03-07 07:28:12 +08: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-10 04:49:16 +08:00
if ( node - > children = = 0 ) {
node - > child . single . leaf = leaf ;
node - > child . single . child = child ;
2015-03-07 07:28:12 +08:00
} else {
2018-01-10 04:49:16 +08: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-07 07:28:12 +08:00
}
2018-01-10 04:49:16 +08:00
zend_hash_index_add_ptr ( node - > child . ht , ( zend_ulong ) leaf , child ) ;
2015-03-07 07:28:12 +08:00
}
node - > children + + ;
}
static void zend_generator_merge_child_nodes ( zend_generator_node * dest , zend_generator_node * src , zend_generator * child )
{
2018-01-10 04:49:16 +08: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-07 07:28:12 +08:00
}
2018-01-10 04:49:16 +08:00
/* Pay attention so that the root of each subtree of the Generators tree is referenced
* once per leaf */
2015-03-07 07:28:12 +08: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-27 06:28:58 +08: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-07 07:28:12 +08:00
generator - > node . ptr . leaf = leaf ;
while ( next ) {
if ( next - > node . children > 1 ) {
2018-01-10 04:49:16 +08: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-07 07:28:12 +08: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 ) {
generator - > node . children = 0 ;
2018-01-10 04:49:16 +08:00
zend_generator_merge_child_nodes ( & generator - > node , multi_children_node , generator - > node . child . single . child ) ;
2015-03-07 07:28:12 +08:00
}
}
if ( ! was_leaf ) {
multi_children_node = zend_generator_search_multi_children_node ( & child - > node ) ;
} else {
multi_children_node = ( zend_generator_node * ) 0x1 ;
}
{
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 11:30:34 +08:00
void zend_generator_yield_from ( zend_generator * generator , zend_generator * from )
2015-03-07 07:28:12 +08:00
{
2015-07-30 11:30:34 +08:00
zend_generator_add_child ( from , generator ) ;
2015-03-07 07:28:12 +08:00
2015-07-30 11:30:34 +08:00
generator - > node . parent = from ;
zend_generator_get_current ( generator ) ;
2017-10-27 06:28:58 +08:00
GC_DELREF ( & from - > std ) ;
2015-03-07 07:28:12 +08:00
}
2015-09-20 08:19:31 +08:00
ZEND_API zend_generator * zend_generator_update_current ( zend_generator * generator , zend_generator * leaf )
2015-03-07 07:28:12 +08:00
{
2015-09-20 08:19:31 +08:00
zend_generator * old_root , * root = leaf - > node . ptr . root ;
2015-03-07 07:28:12 +08:00
2015-04-16 03:41:29 +08: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-07 07:28:12 +08:00
while ( ! root - > execute_data & & root ! = generator ) {
2015-04-16 03:41:29 +08:00
OBJ_RELEASE ( & old_root - > std ) ;
old_root = root ;
2015-03-07 07:28:12 +08:00
root = zend_generator_get_child ( & root - > node , leaf ) ;
}
if ( root - > node . parent ) {
if ( root - > node . parent - > execute_data = = NULL ) {
if ( EXPECTED ( EG ( exception ) = = NULL ) ) {
zend_op * yield_from = ( zend_op * ) root - > execute_data - > opline - 1 ;
2016-01-07 18:56:10 +08:00
if ( yield_from - > opcode = = ZEND_YIELD_FROM ) {
2015-03-07 07:28:12 +08: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 22:21:28 +08:00
root - > execute_data - > opline - - ; /* ZEND_YIELD(_FROM) already advance, so decrement opline to throw from correct place */
2015-03-07 07:28:12 +08: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 22:39:21 +08: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-07 07:28:12 +08:00
} else {
2016-01-07 22:12:35 +08:00
zval_ptr_dtor ( & root - > value ) ;
2015-11-25 06:43:34 +08:00
ZVAL_COPY ( & root - > value , & root - > node . parent - > value ) ;
2015-03-07 07:28:12 +08: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-27 06:28:58 +08:00
GC_ADDREF ( & root - > std ) ;
2015-03-07 07:28:12 +08:00
} while ( root - > node . parent ) ;
}
}
2018-01-13 05:48:29 +08:00
leaf - > node . ptr . root = root ;
2015-04-16 03:41:29 +08:00
if ( old_root ) {
OBJ_RELEASE ( & old_root - > std ) ;
}
2018-01-13 05:48:29 +08:00
return root ;
2015-03-07 07:28:12 +08:00
}
2015-02-20 19:59:56 +08:00
static int zend_generator_get_next_delegated_value ( zend_generator * generator ) /* { { { */
{
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-22 01:41:51 +08:00
if ( iter - > index + + > 0 ) {
2015-02-20 19:59:56 +08:00
iter - > funcs - > move_forward ( iter ) ;
if ( UNEXPECTED ( EG ( exception ) ! = NULL ) ) {
2015-12-06 09:45:19 +08:00
goto exception ;
2015-02-20 19:59:56 +08:00
}
}
if ( iter - > funcs - > valid ( iter ) = = FAILURE ) {
/* reached end of iteration */
goto failure ;
}
value = iter - > funcs - > get_current_data ( iter ) ;
2015-12-06 09:45:19 +08:00
if ( UNEXPECTED ( EG ( exception ) ! = NULL ) ) {
goto exception ;
} else if ( UNEXPECTED ( ! value ) ) {
2015-02-20 19:59:56 +08: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 09:45:19 +08:00
goto exception ;
2015-02-20 19:59:56 +08:00
}
} else {
ZVAL_LONG ( & generator - > key , iter - > index ) ;
}
}
return SUCCESS ;
2016-12-12 15:55:32 +08:00
exception :
zend_rethrow_exception ( generator - > execute_data ) ;
2015-12-06 09:45:19 +08:00
2015-02-20 19:59:56 +08:00
failure :
zval_ptr_dtor ( & generator - > values ) ;
ZVAL_UNDEF ( & generator - > values ) ;
return FAILURE ;
}
/* }}} */
2015-03-07 07:28:12 +08:00
ZEND_API void zend_generator_resume ( zend_generator * orig_generator ) /* { { { */
2012-05-26 23:53:13 +08:00
{
2017-07-22 17:14:00 +08:00
zend_generator * generator = zend_generator_get_current ( orig_generator ) ;
2015-03-07 07:28:12 +08:00
2012-05-27 05:59:22 +08:00
/* The generator is already closed, thus can't resume */
2017-07-22 17:14:00 +08:00
if ( UNEXPECTED ( ! generator - > execute_data ) ) {
2012-05-27 05:59:22 +08:00
return ;
}
2015-03-07 07:28:12 +08:00
try_again :
2012-08-25 23:40:08 +08:00
if ( generator - > flags & ZEND_GENERATOR_CURRENTLY_RUNNING ) {
2015-07-08 01:10:22 +08:00
zend_throw_error ( NULL , " Cannot resume an already running generator " ) ;
2015-04-01 18:49:11 +08:00
return ;
2012-08-25 23:40:08 +08:00
}
2015-11-25 16:49:39 +08: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 */
return ;
}
2015-09-20 08:19:31 +08:00
if ( UNEXPECTED ( ! Z_ISUNDEF ( generator - > values ) ) ) {
if ( EXPECTED ( zend_generator_get_next_delegated_value ( generator ) = = SUCCESS ) ) {
2015-02-20 19:59:56 +08:00
return ;
}
/* If there are no more deletegated values, resume the generator
2015-03-07 07:28:12 +08:00
* after the " yield from " expression . */
}
2012-08-25 23:40:08 +08:00
/* Drop the AT_FIRST_YIELD flag */
2015-03-07 07:28:12 +08:00
orig_generator - > flags & = ~ ZEND_GENERATOR_AT_FIRST_YIELD ;
2012-08-25 23:40:08 +08:00
2012-05-27 09:50:31 +08:00
{
/* Backup executor globals */
2012-05-28 06:24:58 +08:00
zend_execute_data * original_execute_data = EG ( current_execute_data ) ;
2015-01-10 00:58:41 +08:00
2012-05-27 09:50:31 +08:00
/* Set executor globals */
2012-05-28 06:24:58 +08:00
EG ( current_execute_data ) = generator - > execute_data ;
2012-05-27 09:50:31 +08:00
2012-06-09 06:40:47 +08: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-10 00:58:41 +08:00
* So we have to link generator call frame with caller call frame . */
2015-03-07 07:28:12 +08: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 08:00:11 +08:00
2016-05-12 05:44:18 +08:00
if ( UNEXPECTED ( generator - > frozen_call_stack ) ) {
/* Restore frozen call-stack */
2016-09-28 19:26:08 +08:00
zend_generator_restore_call_stack ( generator ) ;
2016-05-12 05:44:18 +08:00
}
2012-05-27 09:50:31 +08:00
/* Resume execution */
2012-08-25 23:40:08 +08:00
generator - > flags | = ZEND_GENERATOR_CURRENTLY_RUNNING ;
2014-12-14 06:06:14 +08:00
zend_execute_ex ( generator - > execute_data ) ;
2012-08-25 23:40:08 +08:00
generator - > flags & = ~ ZEND_GENERATOR_CURRENTLY_RUNNING ;
2012-05-27 09:50:31 +08:00
2016-05-12 05:44:18 +08:00
generator - > frozen_call_stack = NULL ;
if ( EXPECTED ( generator - > execute_data ) & &
UNEXPECTED ( generator - > execute_data - > call ) ) {
/* Frize call-stack */
2016-09-28 19:26:08 +08:00
generator - > frozen_call_stack = zend_generator_freeze_call_stack ( generator - > execute_data ) ;
2014-06-27 03:51:14 +08:00
}
2012-05-27 09:50:31 +08:00
/* Restore executor globals */
2012-05-28 06:24:58 +08:00
EG ( current_execute_data ) = original_execute_data ;
2012-09-23 01:12:21 +08:00
2012-07-22 23:46:46 +08:00
/* If an exception was thrown in the generator we have to internally
2015-03-07 07:28:12 +08: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 23:46:46 +08:00
if ( UNEXPECTED ( EG ( exception ) ! = NULL ) ) {
2015-03-07 07:28:12 +08:00
if ( generator = = orig_generator ) {
2015-04-16 03:41:29 +08:00
zend_generator_close ( generator , 0 ) ;
2019-12-18 18:02:44 +08: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 15:55:32 +08:00
zend_rethrow_exception ( EG ( current_execute_data ) ) ;
}
2015-03-07 07:28:12 +08:00
} else {
generator = zend_generator_get_current ( orig_generator ) ;
zend_generator_throw_exception ( generator , NULL ) ;
goto try_again ;
}
2012-07-22 23:46:46 +08:00
}
2015-02-20 19:59:56 +08:00
2015-03-07 07:28:12 +08:00
/* yield from was used, try another resume. */
2015-09-20 08:19:31 +08:00
if ( UNEXPECTED ( ( generator ! = orig_generator & & ! Z_ISUNDEF ( generator - > retval ) ) | | ( generator - > execute_data & & ( generator - > execute_data - > opline - 1 ) - > opcode = = ZEND_YIELD_FROM ) ) ) {
2015-03-07 07:28:12 +08:00
generator = zend_generator_get_current ( orig_generator ) ;
2015-02-20 19:59:56 +08:00
goto try_again ;
2012-07-22 23:46:46 +08:00
}
2012-05-27 09:50:31 +08:00
}
2012-05-26 23:53:13 +08:00
}
/* }}} */
2016-04-29 19:44:56 +08:00
static inline void zend_generator_ensure_initialized ( zend_generator * generator ) /* { { { */
2012-05-26 23:53:13 +08:00
{
2015-11-25 06:43:34 +08:00
if ( UNEXPECTED ( Z_TYPE ( generator - > value ) = = IS_UNDEF ) & & EXPECTED ( generator - > execute_data ) & & EXPECTED ( generator - > node . parent = = NULL ) ) {
2015-03-07 07:28:12 +08:00
generator - > flags | = ZEND_GENERATOR_DO_INIT ;
2014-12-14 06:06:14 +08:00
zend_generator_resume ( generator ) ;
2015-03-07 07:28:12 +08:00
generator - > flags & = ~ ZEND_GENERATOR_DO_INIT ;
2012-08-25 23:40:08 +08:00
generator - > flags | = ZEND_GENERATOR_AT_FIRST_YIELD ;
}
}
/* }}} */
2016-04-29 19:44:56 +08:00
static inline void zend_generator_rewind ( zend_generator * generator ) /* { { { */
2012-08-25 23:40:08 +08:00
{
2014-12-14 06:06:14 +08:00
zend_generator_ensure_initialized ( generator ) ;
2012-08-25 23:40:08 +08:00
if ( ! ( generator - > flags & ZEND_GENERATOR_AT_FIRST_YIELD ) ) {
2014-12-14 06:06:14 +08:00
zend_throw_exception ( NULL , " Cannot rewind a generator that was already run " , 0 ) ;
2012-05-26 23:53:13 +08:00
}
}
/* }}} */
2012-05-23 22:07:15 +08:00
/* {{{ proto void Generator::rewind()
* Rewind the generator */
ZEND_METHOD ( Generator , rewind )
{
2012-05-26 23:53:13 +08:00
zend_generator * generator ;
2020-01-03 19:11:45 +08:00
ZEND_PARSE_PARAMETERS_NONE ( ) ;
2012-05-26 23:53:13 +08:00
2018-11-16 00:54:19 +08:00
generator = ( zend_generator * ) Z_OBJ_P ( ZEND_THIS ) ;
2012-05-26 23:53:13 +08:00
2014-12-14 06:06:14 +08:00
zend_generator_rewind ( generator ) ;
2012-05-23 22:07:15 +08:00
}
/* }}} */
/* {{{ proto bool Generator::valid()
* Check whether the generator is valid */
ZEND_METHOD ( Generator , valid )
{
2012-05-26 23:53:13 +08:00
zend_generator * generator ;
2020-01-03 19:11:45 +08:00
ZEND_PARSE_PARAMETERS_NONE ( ) ;
2012-05-26 23:53:13 +08:00
2018-11-16 00:54:19 +08:00
generator = ( zend_generator * ) Z_OBJ_P ( ZEND_THIS ) ;
2012-05-26 23:53:13 +08:00
2014-12-14 06:06:14 +08:00
zend_generator_ensure_initialized ( generator ) ;
2012-05-27 05:07:05 +08:00
2015-03-07 07:28:12 +08:00
zend_generator_get_current ( generator ) ;
2015-11-25 06:43:34 +08:00
RETURN_BOOL ( EXPECTED ( generator - > execute_data ! = NULL ) ) ;
2012-05-23 22:07:15 +08:00
}
/* }}} */
/* {{{ proto mixed Generator::current()
* Get the current value */
ZEND_METHOD ( Generator , current )
{
2015-03-07 07:28:12 +08:00
zend_generator * generator , * root ;
2012-05-26 23:53:13 +08:00
2020-01-03 19:11:45 +08:00
ZEND_PARSE_PARAMETERS_NONE ( ) ;
2012-05-26 23:53:13 +08:00
2018-11-16 00:54:19 +08:00
generator = ( zend_generator * ) Z_OBJ_P ( ZEND_THIS ) ;
2012-05-26 23:53:13 +08:00
2014-12-14 06:06:14 +08:00
zend_generator_ensure_initialized ( generator ) ;
2012-05-27 05:07:05 +08:00
2015-03-07 07:28:12 +08:00
root = zend_generator_get_current ( generator ) ;
2015-11-25 06:43:34 +08:00
if ( EXPECTED ( generator - > execute_data ! = NULL & & Z_TYPE ( root - > value ) ! = IS_UNDEF ) ) {
2015-06-12 18:33:14 +08:00
zval * value = & root - > value ;
2018-07-09 17:46:46 +08:00
ZVAL_COPY_DEREF ( return_value , value ) ;
2012-05-27 05:07:05 +08:00
}
2012-05-23 22:07:15 +08:00
}
/* }}} */
/* {{{ proto mixed Generator::key()
* Get the current key */
ZEND_METHOD ( Generator , key )
{
2015-03-07 07:28:12 +08:00
zend_generator * generator , * root ;
2012-05-26 23:53:13 +08:00
2020-01-03 19:11:45 +08:00
ZEND_PARSE_PARAMETERS_NONE ( ) ;
2012-05-26 23:53:13 +08:00
2018-11-16 00:54:19 +08:00
generator = ( zend_generator * ) Z_OBJ_P ( ZEND_THIS ) ;
2012-05-26 23:53:13 +08:00
2014-12-14 06:06:14 +08:00
zend_generator_ensure_initialized ( generator ) ;
2012-05-30 08:44:06 +08:00
2015-03-07 07:28:12 +08:00
root = zend_generator_get_current ( generator ) ;
2015-11-25 06:43:34 +08:00
if ( EXPECTED ( generator - > execute_data ! = NULL & & Z_TYPE ( root - > key ) ! = IS_UNDEF ) ) {
2015-06-12 18:33:14 +08:00
zval * key = & root - > key ;
2018-07-09 17:46:46 +08:00
ZVAL_COPY_DEREF ( return_value , key ) ;
2012-05-30 08:44:06 +08:00
}
2012-05-23 22:07:15 +08:00
}
/* }}} */
/* {{{ proto void Generator::next()
* Advances the generator */
ZEND_METHOD ( Generator , next )
{
2012-05-26 23:53:13 +08:00
zend_generator * generator ;
2020-01-03 19:11:45 +08:00
ZEND_PARSE_PARAMETERS_NONE ( ) ;
2012-05-26 23:53:13 +08:00
2018-11-16 00:54:19 +08:00
generator = ( zend_generator * ) Z_OBJ_P ( ZEND_THIS ) ;
2012-05-26 23:53:13 +08:00
2014-12-14 06:06:14 +08:00
zend_generator_ensure_initialized ( generator ) ;
2012-05-27 05:07:05 +08:00
2014-12-14 06:06:14 +08:00
zend_generator_resume ( generator ) ;
2012-05-23 22:07:15 +08:00
}
/* }}} */
2015-06-24 03:47:07 +08:00
/* {{{ proto mixed Generator::send(mixed value)
2012-05-29 23:34:33 +08:00
* Sends a value to the generator */
ZEND_METHOD ( Generator , send )
{
2012-06-23 20:43:52 +08:00
zval * value ;
2015-03-07 07:28:12 +08:00
zend_generator * generator , * root ;
2012-05-29 23:34:33 +08:00
2015-09-20 08:19:31 +08:00
ZEND_PARSE_PARAMETERS_START ( 1 , 1 )
Z_PARAM_ZVAL ( value )
ZEND_PARSE_PARAMETERS_END ( ) ;
2012-05-29 23:34:33 +08:00
2018-11-16 00:54:19 +08:00
generator = ( zend_generator * ) Z_OBJ_P ( ZEND_THIS ) ;
2012-05-29 23:34:33 +08:00
2015-01-03 17:22:58 +08:00
zend_generator_ensure_initialized ( generator ) ;
2012-05-29 23:34:33 +08:00
2012-05-30 00:01:08 +08:00
/* The generator is already closed, thus can't send anything */
2015-09-20 08:19:31 +08:00
if ( UNEXPECTED ( ! generator - > execute_data ) ) {
2012-05-30 00:01:08 +08:00
return ;
}
2015-03-07 07:28:12 +08:00
root = zend_generator_get_current ( generator ) ;
2013-11-30 20:05:40 +08:00
/* Put sent value in the target VAR slot, if it is used */
2015-03-07 07:28:12 +08:00
if ( root - > send_target ) {
ZVAL_COPY ( root - > send_target , value ) ;
2013-11-30 20:05:40 +08:00
}
2012-05-29 23:34:33 +08:00
2014-12-14 06:06:14 +08:00
zend_generator_resume ( generator ) ;
2012-06-01 02:03:18 +08:00
2015-03-07 07:28:12 +08:00
root = zend_generator_get_current ( generator ) ;
2015-11-25 06:43:34 +08:00
if ( EXPECTED ( generator - > execute_data ) ) {
2015-06-12 18:33:14 +08:00
zval * value = & root - > value ;
2018-07-09 17:46:46 +08:00
ZVAL_COPY_DEREF ( return_value , value ) ;
2012-06-01 02:03:18 +08:00
}
2012-05-29 23:34:33 +08:00
}
2012-08-30 02:49:14 +08:00
/* }}} */
2012-05-29 23:34:33 +08:00
2015-06-24 03:47:07 +08:00
/* {{{ proto mixed Generator::throw(Exception exception)
2012-12-18 05:02:32 +08:00
* Throws an exception into the generator */
ZEND_METHOD ( Generator , throw )
{
2017-10-31 05:48:20 +08:00
zval * exception ;
2012-12-18 05:02:32 +08:00
zend_generator * generator ;
2015-09-20 08:19:31 +08:00
ZEND_PARSE_PARAMETERS_START ( 1 , 1 )
2019-10-15 20:30:23 +08:00
Z_PARAM_OBJECT_OF_CLASS ( exception , zend_ce_throwable ) ;
2015-09-20 08:19:31 +08:00
ZEND_PARSE_PARAMETERS_END ( ) ;
2012-12-18 05:02:32 +08:00
2017-10-31 05:48:20 +08:00
Z_TRY_ADDREF_P ( exception ) ;
2012-12-18 05:02:32 +08:00
2018-11-16 00:54:19 +08:00
generator = ( zend_generator * ) Z_OBJ_P ( ZEND_THIS ) ;
2012-12-18 05:02:32 +08:00
2015-01-03 17:22:58 +08:00
zend_generator_ensure_initialized ( generator ) ;
2013-12-01 20:37:56 +08:00
2012-12-18 05:02:32 +08:00
if ( generator - > execute_data ) {
2015-03-07 07:28:12 +08:00
zend_generator * root = zend_generator_get_current ( generator ) ;
2012-12-18 05:02:32 +08:00
2017-10-31 05:48:20 +08:00
zend_generator_throw_exception ( root , exception ) ;
2012-12-18 05:02:32 +08:00
2014-12-14 06:06:14 +08:00
zend_generator_resume ( generator ) ;
2012-12-18 05:02:32 +08:00
2015-03-07 07:28:12 +08:00
root = zend_generator_get_current ( generator ) ;
2015-11-25 06:43:34 +08:00
if ( generator - > execute_data ) {
2015-06-12 18:33:14 +08:00
zval * value = & root - > value ;
2018-07-09 17:46:46 +08:00
ZVAL_COPY_DEREF ( return_value , value ) ;
2012-12-18 05:02:32 +08:00
}
} else {
/* If the generator is already closed throw the exception in the
* current context */
2017-10-31 05:48:20 +08:00
zend_throw_exception_object ( exception ) ;
2012-12-18 05:02:32 +08:00
}
}
/* }}} */
2015-02-20 03:17:37 +08:00
/* {{{ proto mixed Generator::getReturn()
* Retrieves the return value of the generator */
ZEND_METHOD ( Generator , getReturn )
{
zend_generator * generator ;
2020-01-03 19:11:45 +08:00
ZEND_PARSE_PARAMETERS_NONE ( ) ;
2015-02-20 03:17:37 +08:00
2018-11-16 00:54:19 +08:00
generator = ( zend_generator * ) Z_OBJ_P ( ZEND_THIS ) ;
2015-02-20 03:17:37 +08:00
zend_generator_ensure_initialized ( generator ) ;
2015-09-20 08:19:31 +08:00
if ( UNEXPECTED ( EG ( exception ) ) ) {
2015-02-20 03:17:37 +08: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-23 07:28:16 +08:00
/* get_iterator implementation */
2014-12-14 06:06:14 +08:00
static void zend_generator_iterator_dtor ( zend_object_iterator * iterator ) /* { { { */
2012-06-23 07:28:16 +08:00
{
2014-04-09 05:50:15 +08:00
zend_generator * generator = ( zend_generator * ) Z_OBJ ( iterator - > data ) ;
generator - > iterator = NULL ;
2014-04-09 14:47:03 +08:00
zval_ptr_dtor ( & iterator - > data ) ;
2012-06-23 07:28:16 +08:00
}
/* }}} */
2014-12-14 06:06:14 +08:00
static int zend_generator_iterator_valid ( zend_object_iterator * iterator ) /* { { { */
2012-06-23 07:28:16 +08:00
{
2014-02-27 19:19:02 +08:00
zend_generator * generator = ( zend_generator * ) Z_OBJ ( iterator - > data ) ;
2012-06-23 07:28:16 +08:00
2014-12-14 06:06:14 +08:00
zend_generator_ensure_initialized ( generator ) ;
2012-06-23 07:28:16 +08:00
2015-03-07 07:28:12 +08:00
zend_generator_get_current ( generator ) ;
2015-11-25 06:43:34 +08:00
return generator - > execute_data ? SUCCESS : FAILURE ;
2012-06-23 07:28:16 +08:00
}
/* }}} */
2014-12-14 06:06:14 +08:00
static zval * zend_generator_iterator_get_data ( zend_object_iterator * iterator ) /* { { { */
2012-06-23 07:28:16 +08:00
{
2015-03-07 07:28:12 +08:00
zend_generator * generator = ( zend_generator * ) Z_OBJ ( iterator - > data ) , * root ;
2012-06-23 07:28:16 +08:00
2014-12-14 06:06:14 +08:00
zend_generator_ensure_initialized ( generator ) ;
2012-06-23 07:28:16 +08:00
2015-03-07 07:28:12 +08:00
root = zend_generator_get_current ( generator ) ;
return & root - > value ;
2012-06-23 07:28:16 +08:00
}
/* }}} */
2014-12-14 06:06:14 +08:00
static void zend_generator_iterator_get_key ( zend_object_iterator * iterator , zval * key ) /* { { { */
2012-06-23 07:28:16 +08:00
{
2015-03-07 07:28:12 +08:00
zend_generator * generator = ( zend_generator * ) Z_OBJ ( iterator - > data ) , * root ;
2012-06-23 07:28:16 +08:00
2014-12-14 06:06:14 +08:00
zend_generator_ensure_initialized ( generator ) ;
2012-06-23 07:28:16 +08:00
2015-03-07 07:28:12 +08:00
root = zend_generator_get_current ( generator ) ;
2015-09-20 08:19:31 +08:00
if ( EXPECTED ( Z_TYPE ( root - > key ) ! = IS_UNDEF ) ) {
2015-06-12 18:33:14 +08:00
zval * zv = & root - > key ;
2018-07-09 17:46:46 +08:00
ZVAL_COPY_DEREF ( key , zv ) ;
2013-02-17 02:13:36 +08:00
} else {
ZVAL_NULL ( key ) ;
2012-06-23 07:28:16 +08:00
}
}
/* }}} */
2014-12-14 06:06:14 +08:00
static void zend_generator_iterator_move_forward ( zend_object_iterator * iterator ) /* { { { */
2012-06-23 07:28:16 +08:00
{
2014-02-27 19:19:02 +08:00
zend_generator * generator = ( zend_generator * ) Z_OBJ ( iterator - > data ) ;
2012-06-23 07:28:16 +08:00
2014-12-14 06:06:14 +08:00
zend_generator_ensure_initialized ( generator ) ;
2012-06-23 07:28:16 +08:00
2014-12-14 06:06:14 +08:00
zend_generator_resume ( generator ) ;
2012-06-23 07:28:16 +08:00
}
/* }}} */
2014-12-14 06:06:14 +08:00
static void zend_generator_iterator_rewind ( zend_object_iterator * iterator ) /* { { { */
2012-08-25 23:40:08 +08:00
{
2014-02-27 19:19:02 +08:00
zend_generator * generator = ( zend_generator * ) Z_OBJ ( iterator - > data ) ;
2012-08-25 23:40:08 +08:00
2014-12-14 06:06:14 +08:00
zend_generator_rewind ( generator ) ;
2012-08-25 23:40:08 +08:00
}
/* }}} */
2017-12-14 19:21:22 +08:00
static const zend_object_iterator_funcs zend_generator_iterator_functions = {
2012-06-23 07:28:16 +08: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 19:44:56 +08:00
zend_generator_iterator_rewind ,
NULL
2012-06-23 07:28:16 +08:00
} ;
2014-12-14 06:06:14 +08:00
zend_object_iterator * zend_generator_get_iterator ( zend_class_entry * ce , zval * object , int by_ref ) /* { { { */
2012-06-23 07:28:16 +08:00
{
2014-02-27 19:19:02 +08:00
zend_object_iterator * iterator ;
zend_generator * generator = ( zend_generator * ) Z_OBJ_P ( object ) ;
2012-06-23 20:43:52 +08:00
2012-08-30 02:46:56 +08:00
if ( ! generator - > execute_data ) {
2014-12-14 06:06:14 +08:00
zend_throw_exception ( NULL , " Cannot traverse an already closed generator " , 0 ) ;
2012-08-30 02:46:56 +08:00
return NULL ;
}
2015-09-20 08:19:31 +08:00
if ( UNEXPECTED ( by_ref ) & & ! ( generator - > execute_data - > func - > op_array . fn_flags & ZEND_ACC_RETURN_REFERENCE ) ) {
2014-12-14 06:06:14 +08:00
zend_throw_exception ( NULL , " You can only iterate a generator by-reference if it declared that it yields by-reference " , 0 ) ;
2012-08-30 02:46:56 +08:00
return NULL ;
2012-07-17 19:24:27 +08:00
}
2014-04-09 05:50:15 +08:00
iterator = generator - > iterator = emalloc ( sizeof ( zend_object_iterator ) ) ;
2015-01-03 17:22:58 +08:00
2014-12-14 06:06:14 +08:00
zend_iterator_init ( iterator ) ;
2012-06-23 07:28:16 +08:00
2014-02-27 19:19:02 +08:00
iterator - > funcs = & zend_generator_iterator_functions ;
2019-05-29 01:10:02 +08:00
Z_ADDREF_P ( object ) ;
ZVAL_OBJ ( & iterator - > data , Z_OBJ_P ( object ) ) ;
2012-06-23 07:28:16 +08:00
2014-02-27 19:19:02 +08:00
return iterator ;
2012-06-23 07:28:16 +08:00
}
/* }}} */
2012-05-20 06:03:27 +08:00
static const zend_function_entry generator_functions [ ] = {
2019-10-15 20:30:23 +08:00
ZEND_ME ( Generator , rewind , arginfo_class_Generator_rewind , ZEND_ACC_PUBLIC )
ZEND_ME ( Generator , valid , arginfo_class_Generator_valid , ZEND_ACC_PUBLIC )
ZEND_ME ( Generator , current , arginfo_class_Generator_current , ZEND_ACC_PUBLIC )
ZEND_ME ( Generator , key , arginfo_class_Generator_key , ZEND_ACC_PUBLIC )
ZEND_ME ( Generator , next , arginfo_class_Generator_next , ZEND_ACC_PUBLIC )
ZEND_ME ( Generator , send , arginfo_class_Generator_send , ZEND_ACC_PUBLIC )
ZEND_ME ( Generator , throw , arginfo_class_Generator_throw , ZEND_ACC_PUBLIC )
ZEND_ME ( Generator , getReturn , arginfo_class_Generator_getReturn , ZEND_ACC_PUBLIC )
2012-05-20 06:03:27 +08:00
ZEND_FE_END
} ;
2014-12-14 06:06:14 +08:00
void zend_register_generator_ce ( void ) /* { { { */
2012-05-20 06:03:27 +08:00
{
zend_class_entry ce ;
INIT_CLASS_ENTRY ( ce , " Generator " , generator_functions ) ;
2014-12-14 06:06:14 +08:00
zend_ce_generator = zend_register_internal_class ( & ce ) ;
2014-11-22 12:57:55 +08:00
zend_ce_generator - > ce_flags | = ZEND_ACC_FINAL ;
2012-05-20 20:19:16 +08:00
zend_ce_generator - > create_object = zend_generator_create ;
2012-08-20 22:01:16 +08:00
zend_ce_generator - > serialize = zend_class_serialize_deny ;
zend_ce_generator - > unserialize = zend_class_unserialize_deny ;
2012-05-20 20:19:16 +08:00
2012-07-26 23:07:24 +08:00
/* get_iterator has to be assigned *after* implementing the inferface */
2014-12-14 06:06:14 +08:00
zend_class_implements ( zend_ce_generator , 1 , zend_ce_iterator ) ;
2012-07-26 23:07:24 +08:00
zend_ce_generator - > get_iterator = zend_generator_get_iterator ;
2012-05-23 22:07:15 +08:00
2018-05-31 16:57:22 +08:00
memcpy ( & zend_generator_handlers , & std_object_handlers , sizeof ( zend_object_handlers ) ) ;
2014-02-10 14:04:30 +08:00
zend_generator_handlers . free_obj = zend_generator_free_storage ;
zend_generator_handlers . dtor_obj = zend_generator_dtor_storage ;
2016-02-11 02:03:02 +08:00
zend_generator_handlers . get_gc = zend_generator_get_gc ;
2013-03-26 00:40:58 +08:00
zend_generator_handlers . clone_obj = NULL ;
2014-02-10 14:04:30 +08:00
zend_generator_handlers . get_constructor = zend_generator_get_constructor ;
2015-03-07 07:28:12 +08:00
INIT_CLASS_ENTRY ( ce , " ClosedGeneratorException " , NULL ) ;
2015-07-03 22:45:03 +08:00
zend_ce_ClosedGeneratorException = zend_register_internal_class_ex ( & ce , zend_ce_exception ) ;
2012-05-20 06:03:27 +08:00
}
/* }}} */