Skip to content

Commit

Permalink
Allow to use yield without value
Browse files Browse the repository at this point in the history
If the generator is used as a coroutine it often doesn't make sense to yield
anything. In this case one can simply receive values using

    $value = yield;

The yield here will simply yield NULL.
  • Loading branch information
nikic committed May 29, 2012
1 parent 3600914 commit ad525c2
Show file tree
Hide file tree
Showing 5 changed files with 127 additions and 18 deletions.
27 changes: 27 additions & 0 deletions Zend/tests/generators/yield_without_value.phpt
@@ -0,0 +1,27 @@
--TEST--
yield can be used without a value
--FILE--
<?php

function *recv() {
while (true) {
var_dump(yield);
}
}

$reciever = recv();
var_dump($reciever->current());
$reciever->send(1);
var_dump($reciever->current());
$reciever->send(2);
var_dump($reciever->current());
$reciever->send(3);

?>
--EXPECT--
NULL
int(1)
NULL
int(2)
NULL
int(3)
8 changes: 6 additions & 2 deletions Zend/zend_compile.c
Expand Up @@ -2673,8 +2673,12 @@ void zend_do_yield(znode *result, const znode *expr TSRMLS_DC) /* {{{ */
opline = get_next_op(CG(active_op_array) TSRMLS_CC);

opline->opcode = ZEND_YIELD;
SET_NODE(opline->op1, expr);
SET_UNUSED(opline->op2);

if (expr) {
SET_NODE(opline->op1, expr);
} else {
SET_UNUSED(opline->op2);
}

opline->result_type = IS_VAR;
opline->result.var = get_temporary_variable(CG(active_op_array));
Expand Down
1 change: 1 addition & 0 deletions Zend/zend_language_parser.y
Expand Up @@ -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 { zend_do_yield(&$$, NULL 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); }
Expand Down
10 changes: 7 additions & 3 deletions Zend/zend_vm_def.h
Expand Up @@ -5258,7 +5258,7 @@ ZEND_VM_HANDLER(159, ZEND_SUSPEND_AND_RETURN_GENERATOR, ANY, ANY)
ZEND_VM_RETURN();
}

ZEND_VM_HANDLER(160, ZEND_YIELD, CONST|TMP|VAR|CV, ANY)
ZEND_VM_HANDLER(160, ZEND_YIELD, CONST|TMP|VAR|CV|UNUSED, ANY)
{
USE_OPLINE

Expand All @@ -5271,7 +5271,7 @@ ZEND_VM_HANDLER(160, ZEND_YIELD, CONST|TMP|VAR|CV, ANY)
}

/* Set the new yielded value */
{
if (OP1_TYPE != IS_UNUSED) {
zend_free_op free_op1;
zval *value = GET_OP1_ZVAL_PTR(BP_VAR_R);

Expand All @@ -5291,11 +5291,15 @@ ZEND_VM_HANDLER(160, ZEND_YIELD, CONST|TMP|VAR|CV, ANY)

generator->value = copy;
} else {
generator->value = value;
Z_ADDREF_P(value);
generator->value = value;
}

FREE_OP1_IF_VAR();
} else {
/* If no value way specified yield null */
Z_ADDREF(EG(uninitialized_zval));
generator->value = &EG(uninitialized_zval);
}

/* If a value is sent it should go into the result var */
Expand Down
99 changes: 86 additions & 13 deletions Zend/zend_vm_execute.h
Expand Up @@ -3047,7 +3047,7 @@ static int ZEND_FASTCALL ZEND_YIELD_SPEC_CONST_HANDLER(ZEND_OPCODE_HANDLER_ARGS
}

/* Set the new yielded value */
{
if (IS_CONST != IS_UNUSED) {

zval *value = opline->op1.zv;

Expand All @@ -3067,10 +3067,14 @@ static int ZEND_FASTCALL ZEND_YIELD_SPEC_CONST_HANDLER(ZEND_OPCODE_HANDLER_ARGS

generator->value = copy;
} else {
generator->value = value;
Z_ADDREF_P(value);
generator->value = value;
}

} else {
/* If no value way specified yield null */
Z_ADDREF(EG(uninitialized_zval));
generator->value = &EG(uninitialized_zval);
}

/* If a value is sent it should go into the result var */
Expand Down Expand Up @@ -7697,7 +7701,7 @@ static int ZEND_FASTCALL ZEND_YIELD_SPEC_TMP_HANDLER(ZEND_OPCODE_HANDLER_ARGS)
}

/* Set the new yielded value */
{
if (IS_TMP_VAR != IS_UNUSED) {
zend_free_op free_op1;
zval *value = _get_zval_ptr_tmp(opline->op1.var, EX_Ts(), &free_op1 TSRMLS_CC);

Expand All @@ -7717,10 +7721,14 @@ static int ZEND_FASTCALL ZEND_YIELD_SPEC_TMP_HANDLER(ZEND_OPCODE_HANDLER_ARGS)

generator->value = copy;
} else {
generator->value = value;
Z_ADDREF_P(value);
generator->value = value;
}

} else {
/* If no value way specified yield null */
Z_ADDREF(EG(uninitialized_zval));
generator->value = &EG(uninitialized_zval);
}

/* If a value is sent it should go into the result var */
Expand Down Expand Up @@ -12424,7 +12432,7 @@ static int ZEND_FASTCALL ZEND_YIELD_SPEC_VAR_HANDLER(ZEND_OPCODE_HANDLER_ARGS)
}

/* Set the new yielded value */
{
if (IS_VAR != IS_UNUSED) {
zend_free_op free_op1;
zval *value = _get_zval_ptr_var(opline->op1.var, EX_Ts(), &free_op1 TSRMLS_CC);

Expand All @@ -12444,11 +12452,15 @@ static int ZEND_FASTCALL ZEND_YIELD_SPEC_VAR_HANDLER(ZEND_OPCODE_HANDLER_ARGS)

generator->value = copy;
} else {
generator->value = value;
Z_ADDREF_P(value);
generator->value = value;
}

if (free_op1.var) {zval_ptr_dtor(&free_op1.var);};
} else {
/* If no value way specified yield null */
Z_ADDREF(EG(uninitialized_zval));
generator->value = &EG(uninitialized_zval);
}

/* If a value is sent it should go into the result var */
Expand Down Expand Up @@ -22080,6 +22092,63 @@ static int ZEND_FASTCALL ZEND_EXIT_SPEC_UNUSED_HANDLER(ZEND_OPCODE_HANDLER_ARGS
ZEND_VM_NEXT_OPCODE(); /* Never reached */
}

static int ZEND_FASTCALL ZEND_YIELD_SPEC_UNUSED_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);

/* Destroy the previously yielded value */
if (generator->value) {
zval_ptr_dtor(&generator->value);
}

/* Set the new yielded value */
if (IS_UNUSED != IS_UNUSED) {

zval *value = NULL;

/* Consts, temporary variables and references need copying */
if (IS_UNUSED == IS_CONST || IS_UNUSED == IS_TMP_VAR
|| (PZVAL_IS_REF(value) && Z_REFCOUNT_P(value) > 0)
) {
zval *copy;

ALLOC_ZVAL(copy);
INIT_PZVAL_COPY(copy, value);

/* Temporary variables don't need ctor copying */
if (!0) {
zval_copy_ctor(copy);
}

generator->value = copy;
} else {
Z_ADDREF_P(value);
generator->value = value;
}

} else {
/* If no value way specified yield null */
Z_ADDREF(EG(uninitialized_zval));
generator->value = &EG(uninitialized_zval);
}

/* 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();

ZEND_VM_RETURN();
}

static int ZEND_FASTCALL zend_binary_assign_op_obj_helper_SPEC_UNUSED_CONST(int (*binary_op)(zval *result, zval *op1, zval *op2 TSRMLS_DC), ZEND_OPCODE_HANDLER_ARGS)
{
USE_OPLINE
Expand Down Expand Up @@ -28323,7 +28392,7 @@ static int ZEND_FASTCALL ZEND_YIELD_SPEC_CV_HANDLER(ZEND_OPCODE_HANDLER_ARGS)
}

/* Set the new yielded value */
{
if (IS_CV != IS_UNUSED) {

zval *value = _get_zval_ptr_cv_BP_VAR_R(EX_CVs(), opline->op1.var TSRMLS_CC);

Expand All @@ -28343,10 +28412,14 @@ static int ZEND_FASTCALL ZEND_YIELD_SPEC_CV_HANDLER(ZEND_OPCODE_HANDLER_ARGS)

generator->value = copy;
} else {
generator->value = value;
Z_ADDREF_P(value);
generator->value = value;
}

} else {
/* If no value way specified yield null */
Z_ADDREF(EG(uninitialized_zval));
generator->value = &EG(uninitialized_zval);
}

/* If a value is sent it should go into the result var */
Expand Down Expand Up @@ -41168,11 +41241,11 @@ void zend_init_opcodes_handlers(void)
ZEND_YIELD_SPEC_VAR_HANDLER,
ZEND_YIELD_SPEC_VAR_HANDLER,
ZEND_YIELD_SPEC_VAR_HANDLER,
ZEND_NULL_HANDLER,
ZEND_NULL_HANDLER,
ZEND_NULL_HANDLER,
ZEND_NULL_HANDLER,
ZEND_NULL_HANDLER,
ZEND_YIELD_SPEC_UNUSED_HANDLER,
ZEND_YIELD_SPEC_UNUSED_HANDLER,
ZEND_YIELD_SPEC_UNUSED_HANDLER,
ZEND_YIELD_SPEC_UNUSED_HANDLER,
ZEND_YIELD_SPEC_UNUSED_HANDLER,
ZEND_YIELD_SPEC_CV_HANDLER,
ZEND_YIELD_SPEC_CV_HANDLER,
ZEND_YIELD_SPEC_CV_HANDLER,
Expand Down

0 comments on commit ad525c2

Please sign in to comment.