Skip to content
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view

This file was deleted.

13 changes: 0 additions & 13 deletions Zend/tests/generators/errors/generator_cannot_return_error.phpt

This file was deleted.

81 changes: 81 additions & 0 deletions Zend/tests/generators/get_return.phpt
Original file line number Diff line number Diff line change
@@ -0,0 +1,81 @@
--TEST--
Generator::getReturn() success cases
--FILE--
<?php

function gen1() {
return 42;
yield 24;
}

$gen = gen1();
// Calling getReturn() directly here is okay due to auto-priming
var_dump($gen->getReturn());

function gen2() {
yield 24;
return 42;
}

$gen = gen2();
var_dump($gen->current());
$gen->next();
var_dump($gen->getReturn());

// & for generators specifies by-reference yield, not return
// so it's okay to return a literal
function &gen3() {
$var = 24;
yield $var;
return 42;
}

$gen = gen3();
var_dump($gen->current());
$gen->next();
var_dump($gen->getReturn());

// Return types for generators specify the return of the function,
// not of the generator return value, so this code is okay
function gen4() : Generator {
yield 24;
return 42;
}

$gen = gen4();
var_dump($gen->current());
$gen->next();
var_dump($gen->getReturn());

// Has no explicit return, but implicitly return NULL at the end
function gen5() {
yield 24;
}

$gen = gen5();
var_dump($gen->current());
$gen->next();
var_dump($gen->getReturn());

// Explicit value-less return also results in a NULL generator
// return value and there is no interference with type hints
function gen6() : Generator {
return;
yield 24;
}

$gen = gen6();
var_dump($gen->getReturn());

?>
--EXPECTF--
int(42)
int(24)
int(42)
int(24)
int(42)
int(24)
int(42)
int(24)
NULL
NULL
47 changes: 47 additions & 0 deletions Zend/tests/generators/get_return_and_finally.phpt
Original file line number Diff line number Diff line change
@@ -0,0 +1,47 @@
--TEST--
Test interaction of Generator::getReturn() and finally
--FILE--
<?php

function gen1() {
try {
throw new Exception("gen1() throw");
} finally {
return 42;
}
yield;
}

// The exception was discarded, so this works
$gen = gen1();
var_dump($gen->getReturn());

function gen2() {
try {
return 42;
} finally {
throw new Exception("gen2() throw");
}
yield;
}

$gen = gen2();
try {
// This will throw an exception (from the finally)
// during auto-priming, so fails
var_dump($gen->getReturn());
} catch (Exception $e) {
echo $e->getMessage(), "\n";
}
try {
// This fails, because the return value was discarded
var_dump($gen->getReturn());
} catch (Exception $e) {
echo $e->getMessage(), "\n";
}

?>
--EXPECT--
int(42)
gen2() throw
Cannot get return value of a generator that hasn't returned
79 changes: 79 additions & 0 deletions Zend/tests/generators/get_return_errors.phpt
Original file line number Diff line number Diff line change
@@ -0,0 +1,79 @@
--TEST--
Generator::getReturn() error cases
--FILE--
<?php

function gen1() {
yield 1;
yield 2;
return 3;
}

$gen = gen1();
try {
// Generator hasn't reached the "return" yet
$gen->getReturn();
} catch (Exception $e) {
echo $e->getMessage(), "\n";
}

function gen2() {
throw new Exception("gen2() throw");
yield 1;
return 2;
}

$gen = gen2();
try {
$gen->next();
} catch (Exception $e) {
echo $e->getMessage(), "\n";
}
try {
// Generator has been aborted as a result of an exception
$gen->getReturn();
} catch (Exception $e) {
echo $e->getMessage(), "\n";
}

function gen3() {
throw new Exception("gen3() throw");
return 1;
yield 2;
}

$gen = gen3();
try {
// Generator throws during auto-priming of getReturn() call
$gen->getReturn();
} catch (Exception $e) {
echo $e->getMessage(), "\n";
}

function gen4() {
yield;
return 1;
}

$gen = gen4();
try {
$gen->throw(new Exception("gen4() throw"));
} catch (Exception $e) {
echo $e->getMessage(), "\n";
}
try {
// Generator has been aborted as a result of an exception
// that was injected using throw()
$gen->getReturn();
} catch (Exception $e) {
echo $e->getMessage(), "\n";
}

?>
--EXPECT--
Cannot get return value of a generator that hasn't returned
gen2() throw
Cannot get return value of a generator that hasn't returned
gen3() throw
gen4() throw
Cannot get return value of a generator that hasn't returned
59 changes: 59 additions & 0 deletions Zend/tests/generators/get_return_types.phpt
Original file line number Diff line number Diff line change
@@ -0,0 +1,59 @@
--TEST--
Test different types of generator return values (VM operands)
--FILE--
<?php

function gen1() {
return; // CONST
yield;
}

$gen = gen1();
var_dump($gen->getReturn());

function gen2() {
return "str"; // CONST
yield;
}

$gen = gen2();
var_dump($gen->getReturn());

function gen3($var) {
return $var; // CV
yield;
}

$gen = gen3([1, 2, 3]);
var_dump($gen->getReturn());

function gen4($obj) {
return $obj->prop; // VAR
yield;
}

$gen = gen4((object) ['prop' => 321]);
var_dump($gen->getReturn());

function gen5($val) {
return (int) $val; // TMP
yield;
}

$gen = gen5("42");
var_dump($gen->getReturn());

?>
--EXPECT--
NULL
string(3) "str"
array(3) {
[0]=>
int(1)
[1]=>
int(2)
[2]=>
int(3)
}
int(321)
int(42)
35 changes: 35 additions & 0 deletions Zend/zend_generators.c
Original file line number Diff line number Diff line change
Expand Up @@ -186,6 +186,10 @@ static void zend_generator_free_storage(zend_object *object) /* {{{ */

zend_generator_close(generator, 0);

if (!Z_ISUNDEF(generator->retval)) {
zval_ptr_dtor(&generator->retval);
}

zend_object_std_dtor(&generator->std);

if (generator->iterator) {
Expand All @@ -204,6 +208,8 @@ static zend_object *zend_generator_create(zend_class_entry *class_type) /* {{{ *
/* The key will be incremented on first use, so it'll start at 0 */
generator->largest_used_integer_key = -1;

ZVAL_UNDEF(&generator->retval);

zend_object_std_init(&generator->std, class_type);
generator->std.handlers = &zend_generator_handlers;

Expand Down Expand Up @@ -535,6 +541,34 @@ ZEND_METHOD(Generator, throw)
}
/* }}} */

/* {{{ proto mixed Generator::getReturn()
* Retrieves the return value of the generator */
ZEND_METHOD(Generator, getReturn)
{
zend_generator *generator;

if (zend_parse_parameters_none() == FAILURE) {
return;
}

generator = (zend_generator *) Z_OBJ_P(getThis());

zend_generator_ensure_initialized(generator);
if (EG(exception)) {
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);
}
/* }}} */

/* {{{ proto void Generator::__wakeup()
* Throws an Exception as generators can't be serialized */
ZEND_METHOD(Generator, __wakeup)
Expand Down Expand Up @@ -668,6 +702,7 @@ static const zend_function_entry generator_functions[] = {
ZEND_ME(Generator, next, arginfo_generator_void, ZEND_ACC_PUBLIC)
ZEND_ME(Generator, send, arginfo_generator_send, ZEND_ACC_PUBLIC)
ZEND_ME(Generator, throw, arginfo_generator_throw, ZEND_ACC_PUBLIC)
ZEND_ME(Generator, getReturn,arginfo_generator_void, ZEND_ACC_PUBLIC)
ZEND_ME(Generator, __wakeup, arginfo_generator_void, ZEND_ACC_PUBLIC)
ZEND_FE_END
};
Expand Down
2 changes: 2 additions & 0 deletions Zend/zend_generators.h
Original file line number Diff line number Diff line change
Expand Up @@ -40,6 +40,8 @@ typedef struct _zend_generator {
zval value;
/* Current key */
zval key;
/* Return value */
zval retval;
/* Variable to put sent value into */
zval *send_target;
/* Largest used integer key for auto-incrementing keys */
Expand Down
5 changes: 0 additions & 5 deletions Zend/zend_opcode.c
Original file line number Diff line number Diff line change
Expand Up @@ -780,11 +780,6 @@ ZEND_API int pass_two(zend_op_array *op_array)
case ZEND_RETURN:
case ZEND_RETURN_BY_REF:
if (op_array->fn_flags & ZEND_ACC_GENERATOR) {
if (opline->op1_type != IS_CONST || Z_TYPE_P(RT_CONSTANT(op_array, opline->op1)) != IS_NULL) {
CG(zend_lineno) = opline->lineno;
zend_error_noreturn(E_COMPILE_ERROR, "Generators cannot return values using \"return\"");
}

opline->opcode = ZEND_GENERATOR_RETURN;
}
break;
Expand Down
Loading