diff options
| -rw-r--r-- | Zend/tests/generators/generator_send.phpt | 22 | ||||
| -rw-r--r-- | Zend/zend_compile.c | 6 | ||||
| -rw-r--r-- | Zend/zend_compile.h | 2 | ||||
| -rw-r--r-- | Zend/zend_generators.c | 32 | ||||
| -rw-r--r-- | Zend/zend_generators.h | 2 | ||||
| -rw-r--r-- | Zend/zend_language_parser.y | 5 | ||||
| -rw-r--r-- | Zend/zend_vm_def.h | 11 | ||||
| -rw-r--r-- | Zend/zend_vm_execute.h | 44 |
8 files changed, 115 insertions, 9 deletions
diff --git a/Zend/tests/generators/generator_send.phpt b/Zend/tests/generators/generator_send.phpt new file mode 100644 index 0000000000..11ac37f846 --- /dev/null +++ b/Zend/tests/generators/generator_send.phpt @@ -0,0 +1,22 @@ +--TEST-- +Values can be sent back to the generator +--FILE-- +<?php + +function *gen() { + var_dump(yield "yield foo"); + var_dump(yield "yield bar"); +} + +$gen = gen(); +var_dump($gen->current()); +$gen->send("send bar"); +var_dump($gen->current()); +$gen->send("send foo"); + +?> +--EXPECT-- +string(9) "yield foo" +string(8) "send bar" +string(9) "yield bar" +string(8) "send foo" diff --git a/Zend/zend_compile.c b/Zend/zend_compile.c index 30fecf1e2e..37e49014ec 100644 --- a/Zend/zend_compile.c +++ b/Zend/zend_compile.c @@ -2662,7 +2662,7 @@ void zend_do_return(znode *expr, int do_end_vparse TSRMLS_DC) /* {{{ */ } /* }}} */ -void zend_do_yield(znode *expr TSRMLS_DC) /* {{{ */ +void zend_do_yield(znode *result, const znode *expr TSRMLS_DC) /* {{{ */ { zend_op *opline; @@ -2675,6 +2675,10 @@ void zend_do_yield(znode *expr TSRMLS_DC) /* {{{ */ opline->opcode = ZEND_YIELD; SET_NODE(opline->op1, expr); SET_UNUSED(opline->op2); + + opline->result_type = IS_VAR; + opline->result.var = get_temporary_variable(CG(active_op_array)); + GET_NODE(result, opline->result); } /* }}} */ diff --git a/Zend/zend_compile.h b/Zend/zend_compile.h index 5365f9693d..953a9f1df0 100644 --- a/Zend/zend_compile.h +++ b/Zend/zend_compile.h @@ -490,7 +490,7 @@ void zend_do_build_full_name(znode *result, znode *prefix, znode *name, int is_c int zend_do_begin_class_member_function_call(znode *class_name, znode *method_name TSRMLS_DC); void zend_do_end_function_call(znode *function_name, znode *result, const znode *argument_list, int is_method, int is_dynamic_fcall TSRMLS_DC); void zend_do_return(znode *expr, int do_end_vparse TSRMLS_DC); -void zend_do_yield(znode *expr TSRMLS_DC); +void zend_do_yield(znode *result, const znode *expr TSRMLS_DC); void zend_do_suspend_if_generator(TSRMLS_D); void zend_do_handle_exception(TSRMLS_D); diff --git a/Zend/zend_generators.c b/Zend/zend_generators.c index e4b070498b..01c9aa3198 100644 --- a/Zend/zend_generators.c +++ b/Zend/zend_generators.c @@ -292,15 +292,47 @@ ZEND_METHOD(Generator, next) } /* }}} */ +/* {{{ proto void Generator::send() + * Sends a value to the generator */ +ZEND_METHOD(Generator, send) +{ + zval *object, *value; + zend_generator *generator; + + if (zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "z", &value) == FAILURE) { + return; + } + + object = getThis(); + generator = (zend_generator *) zend_object_store_get_object(object TSRMLS_CC); + + zend_generator_ensure_initialized(object, generator TSRMLS_CC); + + /* The sent value was initialized to NULL, so dtor that */ + zval_ptr_dtor(generator->send_target->var.ptr_ptr); + + /* Set new sent value */ + Z_ADDREF_P(value); + generator->send_target->var.ptr = value; + generator->send_target->var.ptr_ptr = &value; + + zend_generator_resume(object, generator TSRMLS_CC); +} + ZEND_BEGIN_ARG_INFO(arginfo_generator_void, 0) ZEND_END_ARG_INFO() +ZEND_BEGIN_ARG_INFO_EX(arginfo_generator_send, 0, 0, 1) + ZEND_ARG_INFO(0, value) +ZEND_END_ARG_INFO() + static const zend_function_entry generator_functions[] = { ZEND_ME(Generator, rewind, arginfo_generator_void, ZEND_ACC_PUBLIC) ZEND_ME(Generator, valid, arginfo_generator_void, ZEND_ACC_PUBLIC) ZEND_ME(Generator, current, arginfo_generator_void, ZEND_ACC_PUBLIC) ZEND_ME(Generator, key, arginfo_generator_void, ZEND_ACC_PUBLIC) ZEND_ME(Generator, next, arginfo_generator_void, ZEND_ACC_PUBLIC) + ZEND_ME(Generator, send, arginfo_generator_send, ZEND_ACC_PUBLIC) ZEND_FE_END }; diff --git a/Zend/zend_generators.h b/Zend/zend_generators.h index 192e9e75c9..c9b43b31b6 100644 --- a/Zend/zend_generators.h +++ b/Zend/zend_generators.h @@ -30,6 +30,8 @@ typedef struct _zend_generator { zend_execute_data *execute_data; /* Current value */ zval *value; + /* Variable to put sent value into */ + temp_variable *send_target; } zend_generator; extern ZEND_API zend_class_entry *zend_ce_generator; diff --git a/Zend/zend_language_parser.y b/Zend/zend_language_parser.y index 2045a5e465..ea8ac41795 100644 --- a/Zend/zend_language_parser.y +++ b/Zend/zend_language_parser.y @@ -68,6 +68,8 @@ static YYSIZE_T zend_yytnamerr(char*, const char*); %token T_LOGICAL_AND "and (T_LOGICAL_AND)" %right T_PRINT %token T_PRINT "print (T_PRINT)" +%right T_YIELD +%token T_YIELD "yield (T_YIELD)" %left '=' T_PLUS_EQUAL T_MINUS_EQUAL T_MUL_EQUAL T_DIV_EQUAL T_CONCAT_EQUAL T_MOD_EQUAL T_AND_EQUAL T_OR_EQUAL T_XOR_EQUAL T_SL_EQUAL T_SR_EQUAL %token T_PLUS_EQUAL "+= (T_PLUS_EQUAL)" %token T_MINUS_EQUAL "-= (T_MINUS_EQUAL)" @@ -158,7 +160,6 @@ static YYSIZE_T zend_yytnamerr(char*, const char*); %token T_FUNCTION "function (T_FUNCTION)" %token T_CONST "const (T_CONST)" %token T_RETURN "return (T_RETURN)" -%token T_YIELD "yield (T_YIELD)" %token T_TRY "try (T_TRY)" %token T_CATCH "catch (T_CATCH)" %token T_THROW "throw (T_THROW)" @@ -298,7 +299,6 @@ unticked_statement: | T_RETURN ';' { zend_do_return(NULL, 0 TSRMLS_CC); } | T_RETURN expr_without_variable ';' { zend_do_return(&$2, 0 TSRMLS_CC); } | T_RETURN variable ';' { zend_do_return(&$2, 1 TSRMLS_CC); } - | T_YIELD expr ';' { zend_do_yield(&$2 TSRMLS_CC); } | T_GLOBAL global_var_list ';' | T_STATIC static_var_list ';' | T_ECHO echo_expr_list ';' @@ -801,6 +801,7 @@ expr_without_variable: | combined_scalar { $$ = $1; } | '`' backticks_expr '`' { zend_do_shell_exec(&$$, &$2 TSRMLS_CC); } | T_PRINT expr { zend_do_print(&$$, &$2 TSRMLS_CC); } + | T_YIELD expr { zend_do_yield(&$$, &$2 TSRMLS_CC); } | function is_generator is_reference { zend_do_begin_lambda_function_declaration(&$$, &$1, $2.op_type, $3.op_type, 0 TSRMLS_CC); } '(' parameter_list ')' lexical_vars { zend_do_suspend_if_generator(TSRMLS_C); } '{' inner_statement_list '}' { zend_do_end_function_declaration(&$1 TSRMLS_CC); $$ = $4; } diff --git a/Zend/zend_vm_def.h b/Zend/zend_vm_def.h index 47d5ec4098..8c810cdee8 100644 --- a/Zend/zend_vm_def.h +++ b/Zend/zend_vm_def.h @@ -5260,6 +5260,8 @@ ZEND_VM_HANDLER(159, ZEND_SUSPEND_AND_RETURN_GENERATOR, ANY, ANY) ZEND_VM_HANDLER(160, ZEND_YIELD, CONST|TMP|VAR|CV, ANY) { + USE_OPLINE + /* The generator object is stored in return_value_ptr_ptr */ zend_generator *generator = (zend_generator *) zend_object_store_get_object(*EG(return_value_ptr_ptr) TSRMLS_CC); @@ -5268,8 +5270,8 @@ ZEND_VM_HANDLER(160, ZEND_YIELD, CONST|TMP|VAR|CV, ANY) zval_ptr_dtor(&generator->value); } + /* Set the new yielded value */ { - USE_OPLINE zend_free_op free_op1; zval *value = GET_OP1_ZVAL_PTR(BP_VAR_R); @@ -5296,6 +5298,13 @@ ZEND_VM_HANDLER(160, ZEND_YIELD, CONST|TMP|VAR|CV, ANY) FREE_OP1_IF_VAR(); } + /* If a value is sent it should go into the result var */ + generator->send_target = &EX_T(opline->result.var); + + /* Initialize the sent value to NULL */ + Z_ADDREF(EG(uninitialized_zval)); + AI_SET_PTR(&EX_T(opline->result.var), &EG(uninitialized_zval)); + /* The GOTO VM uses a local opline variable. We need to set the opline * variable in execute_data so we don't resume at an old position. */ SAVE_OPLINE(); diff --git a/Zend/zend_vm_execute.h b/Zend/zend_vm_execute.h index bd22768a3a..fa07733206 100644 --- a/Zend/zend_vm_execute.h +++ b/Zend/zend_vm_execute.h @@ -3036,6 +3036,8 @@ static int ZEND_FASTCALL ZEND_QM_ASSIGN_VAR_SPEC_CONST_HANDLER(ZEND_OPCODE_HAND static int ZEND_FASTCALL ZEND_YIELD_SPEC_CONST_HANDLER(ZEND_OPCODE_HANDLER_ARGS) { + USE_OPLINE + /* The generator object is stored in return_value_ptr_ptr */ zend_generator *generator = (zend_generator *) zend_object_store_get_object(*EG(return_value_ptr_ptr) TSRMLS_CC); @@ -3044,8 +3046,8 @@ static int ZEND_FASTCALL ZEND_YIELD_SPEC_CONST_HANDLER(ZEND_OPCODE_HANDLER_ARGS zval_ptr_dtor(&generator->value); } + /* Set the new yielded value */ { - USE_OPLINE zval *value = opline->op1.zv; @@ -3071,6 +3073,13 @@ static int ZEND_FASTCALL ZEND_YIELD_SPEC_CONST_HANDLER(ZEND_OPCODE_HANDLER_ARGS } + /* If a value is sent it should go into the result var */ + generator->send_target = &EX_T(opline->result.var); + + /* Initialize the sent value to NULL */ + Z_ADDREF(EG(uninitialized_zval)); + AI_SET_PTR(&EX_T(opline->result.var), &EG(uninitialized_zval)); + /* The GOTO VM uses a local opline variable. We need to set the opline * variable in execute_data so we don't resume at an old position. */ SAVE_OPLINE(); @@ -7677,6 +7686,8 @@ static int ZEND_FASTCALL ZEND_INSTANCEOF_SPEC_TMP_HANDLER(ZEND_OPCODE_HANDLER_A static int ZEND_FASTCALL ZEND_YIELD_SPEC_TMP_HANDLER(ZEND_OPCODE_HANDLER_ARGS) { + USE_OPLINE + /* The generator object is stored in return_value_ptr_ptr */ zend_generator *generator = (zend_generator *) zend_object_store_get_object(*EG(return_value_ptr_ptr) TSRMLS_CC); @@ -7685,8 +7696,8 @@ static int ZEND_FASTCALL ZEND_YIELD_SPEC_TMP_HANDLER(ZEND_OPCODE_HANDLER_ARGS) zval_ptr_dtor(&generator->value); } + /* Set the new yielded value */ { - USE_OPLINE zend_free_op free_op1; zval *value = _get_zval_ptr_tmp(opline->op1.var, EX_Ts(), &free_op1 TSRMLS_CC); @@ -7712,6 +7723,13 @@ static int ZEND_FASTCALL ZEND_YIELD_SPEC_TMP_HANDLER(ZEND_OPCODE_HANDLER_ARGS) } + /* If a value is sent it should go into the result var */ + generator->send_target = &EX_T(opline->result.var); + + /* Initialize the sent value to NULL */ + Z_ADDREF(EG(uninitialized_zval)); + AI_SET_PTR(&EX_T(opline->result.var), &EG(uninitialized_zval)); + /* The GOTO VM uses a local opline variable. We need to set the opline * variable in execute_data so we don't resume at an old position. */ SAVE_OPLINE(); @@ -12395,6 +12413,8 @@ static int ZEND_FASTCALL ZEND_INSTANCEOF_SPEC_VAR_HANDLER(ZEND_OPCODE_HANDLER_A static int ZEND_FASTCALL ZEND_YIELD_SPEC_VAR_HANDLER(ZEND_OPCODE_HANDLER_ARGS) { + USE_OPLINE + /* The generator object is stored in return_value_ptr_ptr */ zend_generator *generator = (zend_generator *) zend_object_store_get_object(*EG(return_value_ptr_ptr) TSRMLS_CC); @@ -12403,8 +12423,8 @@ static int ZEND_FASTCALL ZEND_YIELD_SPEC_VAR_HANDLER(ZEND_OPCODE_HANDLER_ARGS) zval_ptr_dtor(&generator->value); } + /* Set the new yielded value */ { - USE_OPLINE zend_free_op free_op1; zval *value = _get_zval_ptr_var(opline->op1.var, EX_Ts(), &free_op1 TSRMLS_CC); @@ -12431,6 +12451,13 @@ static int ZEND_FASTCALL ZEND_YIELD_SPEC_VAR_HANDLER(ZEND_OPCODE_HANDLER_ARGS) if (free_op1.var) {zval_ptr_dtor(&free_op1.var);}; } + /* If a value is sent it should go into the result var */ + generator->send_target = &EX_T(opline->result.var); + + /* Initialize the sent value to NULL */ + Z_ADDREF(EG(uninitialized_zval)); + AI_SET_PTR(&EX_T(opline->result.var), &EG(uninitialized_zval)); + /* The GOTO VM uses a local opline variable. We need to set the opline * variable in execute_data so we don't resume at an old position. */ SAVE_OPLINE(); @@ -28285,6 +28312,8 @@ static int ZEND_FASTCALL ZEND_INSTANCEOF_SPEC_CV_HANDLER(ZEND_OPCODE_HANDLER_AR static int ZEND_FASTCALL ZEND_YIELD_SPEC_CV_HANDLER(ZEND_OPCODE_HANDLER_ARGS) { + USE_OPLINE + /* The generator object is stored in return_value_ptr_ptr */ zend_generator *generator = (zend_generator *) zend_object_store_get_object(*EG(return_value_ptr_ptr) TSRMLS_CC); @@ -28293,8 +28322,8 @@ static int ZEND_FASTCALL ZEND_YIELD_SPEC_CV_HANDLER(ZEND_OPCODE_HANDLER_ARGS) zval_ptr_dtor(&generator->value); } + /* Set the new yielded value */ { - USE_OPLINE zval *value = _get_zval_ptr_cv_BP_VAR_R(EX_CVs(), opline->op1.var TSRMLS_CC); @@ -28320,6 +28349,13 @@ static int ZEND_FASTCALL ZEND_YIELD_SPEC_CV_HANDLER(ZEND_OPCODE_HANDLER_ARGS) } + /* If a value is sent it should go into the result var */ + generator->send_target = &EX_T(opline->result.var); + + /* Initialize the sent value to NULL */ + Z_ADDREF(EG(uninitialized_zval)); + AI_SET_PTR(&EX_T(opline->result.var), &EG(uninitialized_zval)); + /* The GOTO VM uses a local opline variable. We need to set the opline * variable in execute_data so we don't resume at an old position. */ SAVE_OPLINE(); |
