Merge branch 'coroutineDelegation' of https://github.com/bwoebi/php-src

This commit is contained in:
Bob Weinand 2015-04-14 17:58:58 +02:00
commit f3e124d58d
24 changed files with 2949 additions and 1237 deletions

View File

@ -0,0 +1,59 @@
--TEST--
Exceptions in linear yield from setup
--FILE--
<?php
function from($off) {
try {
yield $off + 1;
} catch (Exception $e) { print "catch in from()\n$e\n"; }
yield $off + 2;
}
function gen() {
try {
yield "gen" => 0;
} catch (Exception $e) { print "catch in gen()\n$e\n"; }
try {
yield from from(0);
} catch (Exception $e) { print "catch in gen()\n$e\n"; }
yield from from(2);
}
$i = 0;
try {
for ($gen = gen(); $gen->valid(); $gen->throw(new Exception((string) $i++))) {
var_dump($gen->current());
}
} catch (Exception $e) { print "catch in {main}\n$e\n"; }
var_dump($gen->valid());
?>
--EXPECTF--
int(0)
catch in gen()
exception 'Exception' with message '0' in %s:%d
Stack trace:
#0 {main}
int(1)
catch in from()
exception 'Exception' with message '1' in %s:%d
Stack trace:
#0 {main}
int(2)
catch in gen()
exception 'Exception' with message '2' in %s:%d
Stack trace:
#0 {main}
int(3)
catch in from()
exception 'Exception' with message '3' in %s:%d
Stack trace:
#0 {main}
int(4)
catch in {main}
exception 'Exception' with message '4' in %s:%d
Stack trace:
#0 {main}
bool(false)

View File

@ -0,0 +1,42 @@
--TEST--
Basic test if yield from works
--FILE--
<?php
function from() {
yield "from" => 1;
yield 2;
}
function gen() {
yield "gen" => 0;
yield from from();
yield 3;
}
/* foreach API */
foreach (gen() as $k => $v) {
var_dump($k, $v);
}
/* iterator API */
for ($gen = gen(); $gen->valid(); $gen->next()) {
var_dump($gen->key(), $gen->current());
}
?>
--EXPECT--
string(3) "gen"
int(0)
string(4) "from"
int(1)
int(0)
int(2)
int(0)
int(3)
string(3) "gen"
int(0)
string(4) "from"
int(1)
int(0)
int(2)
int(0)
int(3)

View File

@ -0,0 +1,41 @@
--TEST--
Multiple yield from on a same Generator instance
--FILE--
<?php
function gen($a = 0) {
yield 1 + $a;
if ($a < 1) {
var_dump(yield from gen($a + 1));
}
yield 3 + $a;
return 5 + $a;
}
function bar($gen) {
var_dump(yield from $gen);
}
/* Twice a Generator from bar() using yield from on $gen */
$gen = gen();
$gens[] = bar($gen);
$gens[] = bar($gen);
do {
foreach ($gens as $g) {
var_dump($g->current());
$g->next();
}
} while($gens[0]->valid());
var_dump($gens[1]->valid());
?>
--EXPECT--
int(1)
int(2)
int(4)
int(6)
int(3)
int(5)
bool(false)

View File

@ -0,0 +1,50 @@
--TEST--
Multiple yield from on a same Generator throwing an Exception
--FILE--
<?php
function from() {
yield 1;
throw new Exception();
}
function gen($gen) {
try {
var_dump(yield from $gen);
} catch (Exception $e) { print "Caught exception!\n$e\n"; }
}
$gen = from();
$gens[] = gen($gen);
$gens[] = gen($gen);
foreach ($gens as $g) {
$g->current(); // init.
}
do {
foreach ($gens as $i => $g) {
print "Generator $i\n";
var_dump($g->current());
$g->next();
}
} while($gens[0]->valid());
?>
--EXPECTF--
Generator 0
int(1)
Caught exception!
exception 'Exception' in %s:%d
Stack trace:
#0 %s(%d): from()
#1 [internal function]: gen(Object(Generator))
#2 %s(%d): Generator->next()
#3 {main}
Generator 1
Fatal error: Uncaught exception 'ClosedGeneratorException' with message 'Generator yielded from aborted, no return value available' in %s:%d
Stack trace:
#0 [internal function]: gen(Object(Generator))
#1 %s(%d): Generator->current()
#2 {main}
thrown in %s on line %d

View File

@ -0,0 +1,34 @@
--TEST--
Check if recursion with yield from works
--FILE--
<?php
function from($a = 0) {
yield 1 + $a;
if ($a <= 3) {
yield from from($a + 3);
yield from from($a + 6);
}
yield 2 + $a;
}
function gen() {
yield from from();
}
foreach(gen() as $v) {
var_dump($v);
}
?>
--EXPECT--
int(1)
int(4)
int(7)
int(8)
int(10)
int(11)
int(5)
int(7)
int(8)
int(2)

View File

@ -0,0 +1,22 @@
--TEST--
yielding values from an array
--FILE--
<?php
function from() {
yield 0;
yield from []; // must not yield anything
yield from [1,2];
}
function gen() {
yield from from();
}
foreach(gen() as $v) {
var_dump($v);
}
?>
--EXPECT--
int(0)
int(1)
int(2)

View File

@ -0,0 +1,49 @@
--TEST--
Exceptions in linear yield from setup
--FILE--
<?php
function from($off) {
debug_print_backtrace();
yield $off + 1;
}
function gen() {
yield 1;
debug_print_backtrace();
yield 2;
yield from from(2);
debug_print_backtrace();
}
print "\nImplicit foreach:\n";
foreach (gen() as $v) {
var_dump($v);
}
print "\nExplicit iterator:\n";
for ($gen = gen(); $gen->valid(); $gen->next()) {
var_dump($gen->current());
}
?>
--EXPECTF--
Implicit foreach:
int(1)
#0 gen() called at [%s:%d]
int(2)
#0 from(2) called at [%s:%d]
#1 gen() called at [%s:%d]
int(3)
#0 gen() called at [%s:%d]
Explicit iterator:
int(1)
#0 gen()
#1 Generator->next() called at [%s:%d]
int(2)
#0 from(2) called at [%s:%d]
#1 gen()
#2 Generator->next() called at [%s:%d]
int(3)
#0 gen()
#1 Generator->next() called at [%s:%d]

View File

@ -0,0 +1,26 @@
--TEST--
Deep recursion with yield from
--FILE--
<?php
ini_set("memory_limit", "512M");
function from($i) {
yield $i;
}
function gen($i = 0) {
if ($i < 50000) {
yield from gen(++$i);
} else {
yield $i;
yield from from(++$i);
}
}
foreach (gen() as $v) {
var_dump($v);
}
?>
--EXPECT--
int(50000)
int(50001)

View File

@ -0,0 +1,332 @@
--TEST--
yield from on multiple trees needing merge
--FILE--
<?php
function from($levels) {
foreach (range(0, 2 << $levels) as $v) {
yield $v;
}
}
function gen($gen, $level) {
if ($level % 2) {
yield $gen->current();
}
yield from $gen;
}
foreach (range(0, 6) as $levels) {
print "$levels level".($levels == 1 ? "" : "s")."\n\n";
$all = array();
$all[] = $gens[0][0] = from($levels);
for ($level = 1; $level < $levels; $level++) {
for ($i = 0; $i < (1 << $level); $i++) {
$all[] = $gens[$level][$i] = gen($gens[$level-1][$i >> 1], $level);
}
}
while (1) {
foreach ($all as $gen) {
var_dump($gen->current());
$gen->next();
if (!$gen->valid()) {
break 2;
}
}
}
print "\n\n";
}
?>
--EXPECT--
0 levels
int(0)
int(1)
int(2)
1 level
int(0)
int(1)
int(2)
int(3)
int(4)
2 levels
int(0)
int(1)
int(2)
int(3)
int(4)
int(5)
int(6)
int(7)
int(8)
3 levels
int(0)
int(1)
int(2)
int(3)
int(4)
int(5)
int(6)
int(7)
int(8)
int(9)
int(10)
int(11)
int(12)
int(13)
int(14)
int(15)
int(16)
4 levels
int(0)
int(1)
int(2)
int(3)
int(4)
int(5)
int(6)
int(7)
int(8)
int(9)
int(10)
int(11)
int(12)
int(13)
int(14)
int(15)
int(16)
int(17)
int(18)
int(19)
int(20)
int(21)
int(22)
int(23)
int(24)
int(25)
int(26)
int(27)
int(28)
int(29)
int(30)
int(31)
int(32)
5 levels
int(0)
int(1)
int(2)
int(3)
int(4)
int(5)
int(6)
int(7)
int(8)
int(9)
int(10)
int(11)
int(12)
int(13)
int(14)
int(15)
int(16)
int(17)
int(18)
int(19)
int(20)
int(21)
int(22)
int(23)
int(24)
int(25)
int(26)
int(27)
int(28)
int(29)
int(30)
int(31)
int(32)
int(33)
int(34)
int(35)
int(36)
int(37)
int(38)
int(39)
int(40)
int(41)
int(42)
int(43)
int(44)
int(45)
int(46)
int(47)
int(48)
int(49)
int(50)
int(51)
int(52)
int(53)
int(54)
int(55)
int(56)
int(57)
int(58)
int(59)
int(60)
int(61)
int(62)
int(63)
int(64)
6 levels
int(0)
int(1)
int(2)
int(3)
int(4)
int(5)
int(6)
int(7)
int(8)
int(9)
int(10)
int(11)
int(12)
int(13)
int(14)
int(15)
int(16)
int(17)
int(18)
int(19)
int(20)
int(21)
int(22)
int(23)
int(24)
int(25)
int(26)
int(27)
int(28)
int(29)
int(30)
int(31)
int(32)
int(33)
int(34)
int(35)
int(36)
int(37)
int(38)
int(39)
int(40)
int(41)
int(42)
int(43)
int(44)
int(45)
int(46)
int(47)
int(48)
int(49)
int(50)
int(51)
int(52)
int(53)
int(54)
int(55)
int(56)
int(57)
int(58)
int(59)
int(60)
int(61)
int(62)
int(63)
int(64)
int(65)
int(66)
int(67)
int(68)
int(69)
int(70)
int(71)
int(72)
int(73)
int(74)
int(75)
int(76)
int(77)
int(78)
int(79)
int(80)
int(81)
int(82)
int(83)
int(84)
int(85)
int(86)
int(87)
int(88)
int(89)
int(90)
int(91)
int(92)
int(93)
int(94)
int(95)
int(96)
int(97)
int(98)
int(99)
int(100)
int(101)
int(102)
int(103)
int(104)
int(105)
int(106)
int(107)
int(108)
int(109)
int(110)
int(111)
int(112)
int(113)
int(114)
int(115)
int(116)
int(117)
int(118)
int(119)
int(120)
int(121)
int(122)
int(123)
int(124)
int(125)
int(126)
int(127)
int(128)

View File

@ -0,0 +1,78 @@
--TEST--
yield from on multiple trees needing merge
--FILE--
<?php
function from($levels) {
foreach (range(0, 2 << $levels) as $v) {
yield $v;
if ($v == (1 << ($levels - 1)) - 2) {
throw new Exception();
}
}
}
function gen($gen, $level) {
yield from $gen;
}
$levels = 5;
print "$levels levels\n\n";
$all = array();
$all[] = $gens[0][0] = from($levels);
for ($level = 1; $level < $levels; $level++) {
for ($i = 0; $i < (1 << $level); $i++) {
$all[] = $gens[$level][$i] = gen($gens[$level-1][$i >> 1], $level);
}
}
for ($i = 0; $i < 2; $i++) {
try {
foreach ($all as $gen) {
var_dump($gen->current());
$gen->next();
if (!$gen->valid()) {
break;
}
}
} catch(Exception $e) {
print "$e\n";
unset($all[array_search($gen, $all)]);
}
}
?>
--EXPECTF--
5 levels
int(0)
int(1)
int(2)
int(3)
int(4)
int(5)
int(6)
int(7)
int(8)
int(9)
int(10)
int(11)
int(12)
int(13)
int(14)
exception 'Exception' in %s:%d
Stack trace:
#0 %s(%d): from(5)
#1 %s(%d): gen(Object(Generator), 1)
#2 %s(%d): gen(Object(Generator), 2)
#3 [internal function]: gen(Object(Generator), 3)
#4 %s(%d): Generator->next()
#5 {main}
exception 'ClosedGeneratorException' with message 'Generator yielded from aborted, no return value available' in %s:%d
Stack trace:
#0 [internal function]: gen(Object(Generator), 1)
#1 %s(%d): Generator->current()
#2 {main}

View File

@ -84,6 +84,7 @@ enum _zend_ast_kind {
ZEND_AST_PRE_DEC,
ZEND_AST_POST_INC,
ZEND_AST_POST_DEC,
ZEND_AST_YIELD_FROM,
ZEND_AST_GLOBAL,
ZEND_AST_UNSET,

View File

@ -27,6 +27,7 @@
#include "zend_exceptions.h"
#include "zend_extensions.h"
#include "zend_closures.h"
#include "zend_generators.h"
#undef ZEND_TEST_EXCEPTIONS
@ -2269,6 +2270,8 @@ ZEND_FUNCTION(debug_print_backtrace)
call_type = NULL;
ZVAL_UNDEF(&arg_array);
ptr = zend_generator_check_placeholder_frame(ptr);
skip = ptr;
/* skip internal handler */
if ((!skip->func || !ZEND_USER_CODE(skip->func->common.type)) &&
@ -2467,6 +2470,8 @@ ZEND_API void zend_fetch_debug_backtrace(zval *return_value, int skip_last, int
frameno++;
array_init(&stack_frame);
ptr = zend_generator_check_placeholder_frame(ptr);
skip = ptr;
/* skip internal handler */
if ((!skip->func || !ZEND_USER_CODE(skip->func->common.type)) &&

View File

@ -5861,16 +5861,8 @@ void zend_compile_exit(znode *result, zend_ast *ast) /* {{{ */
}
/* }}} */
void zend_compile_yield(znode *result, zend_ast *ast) /* {{{ */
static void zend_mark_function_as_generator() /* {{{ */
{
zend_ast *value_ast = ast->child[0];
zend_ast *key_ast = ast->child[1];
znode value_node, key_node;
znode *value_node_ptr = NULL, *key_node_ptr = NULL;
zend_op *opline;
zend_bool returns_by_ref = (CG(active_op_array)->fn_flags & ZEND_ACC_RETURN_REFERENCE) != 0;
if (!CG(active_op_array)->function_name) {
zend_error_noreturn(E_COMPILE_ERROR,
"The \"yield\" expression can only be used inside a function");
@ -5892,6 +5884,20 @@ void zend_compile_yield(znode *result, zend_ast *ast) /* {{{ */
}
CG(active_op_array)->fn_flags |= ZEND_ACC_GENERATOR;
}
/* }}} */
void zend_compile_yield(znode *result, zend_ast *ast) /* {{{ */
{
zend_ast *value_ast = ast->child[0];
zend_ast *key_ast = ast->child[1];
znode value_node, key_node;
znode *value_node_ptr = NULL, *key_node_ptr = NULL;
zend_op *opline;
zend_bool returns_by_ref = (CG(active_op_array)->fn_flags & ZEND_ACC_RETURN_REFERENCE) != 0;
zend_mark_function_as_generator();
if (key_ast) {
zend_compile_expr(&key_node, key_ast);
@ -5915,6 +5921,18 @@ void zend_compile_yield(znode *result, zend_ast *ast) /* {{{ */
}
/* }}} */
void zend_compile_yield_from(znode *result, zend_ast *ast) /* {{{ */
{
zend_ast *expr_ast = ast->child[0];
znode expr_node;
zend_mark_function_as_generator();
zend_compile_expr(&expr_node, expr_ast);
zend_emit_op_tmp(result, ZEND_YIELD_FROM, &expr_node, NULL);
}
/* }}} */
void zend_compile_instanceof(znode *result, zend_ast *ast) /* {{{ */
{
zend_ast *obj_ast = ast->child[0];
@ -6829,6 +6847,9 @@ void zend_compile_expr(znode *result, zend_ast *ast) /* {{{ */
case ZEND_AST_YIELD:
zend_compile_yield(result, ast);
return;
case ZEND_AST_YIELD_FROM:
zend_compile_yield_from(result, ast);
return;
case ZEND_AST_INSTANCEOF:
zend_compile_instanceof(result, ast);
return;

View File

@ -2137,6 +2137,16 @@ static zend_always_inline void zend_vm_stack_extend_call_frame(zend_execute_data
}
/* }}} */
static zend_always_inline zend_generator *zend_get_running_generator(zend_execute_data *execute_data) /* {{{ */
{
/* The generator object is stored in EX(return_value) */
zend_generator *generator = (zend_generator *) EX(return_value);
/* However control may currently be delegated to another generator.
* That's the one we're interested in. */
return generator;
}
/* }}} */
#ifdef HAVE_GCC_GLOBAL_REGS
# if defined(__GNUC__) && ZEND_GCC_VERSION >= 4008 && defined(i386)
# define ZEND_VM_FP_GLOBAL_REG "%esi"

View File

@ -13,6 +13,7 @@
| license@zend.com so we can mail you a copy immediately. |
+----------------------------------------------------------------------+
| Authors: Nikita Popov <nikic@php.net> |
| Bob Weinand <bobwei9@hotmail.com> |
+----------------------------------------------------------------------+
*/
@ -25,6 +26,7 @@
#include "zend_generators.h"
ZEND_API zend_class_entry *zend_ce_generator;
ZEND_API zend_class_entry *zend_ce_ClosedGeneratorException;
static zend_object_handlers zend_generator_handlers;
static zend_object *zend_generator_create(zend_class_entry *class_type);
@ -94,6 +96,11 @@ ZEND_API void zend_generator_close(zend_generator *generator, zend_bool finished
ZVAL_UNDEF(&generator->key);
}
if (Z_TYPE(generator->values) != IS_UNDEF) {
zval_ptr_dtor(&generator->values);
ZVAL_UNDEF(&generator->values);
}
if (generator->execute_data) {
zend_execute_data *execute_data = generator->execute_data;
zend_op_array *op_array = &execute_data->func->op_array;
@ -185,7 +192,14 @@ static void zend_generator_free_storage(zend_object *object) /* {{{ */
zend_generator_close(generator, 0);
zval_ptr_dtor(&generator->retval);
if (!Z_ISUNDEF(generator->retval)) {
zval_ptr_dtor(&generator->retval);
}
if (generator->node.children > 4) {
zend_hash_destroy(&generator->node.child.ht);
}
zend_object_std_dtor(&generator->std);
if (generator->iterator) {
@ -205,6 +219,12 @@ static zend_object *zend_generator_create(zend_class_entry *class_type) /* {{{ *
generator->largest_used_integer_key = -1;
ZVAL_UNDEF(&generator->retval);
ZVAL_UNDEF(&generator->values);
/* By default we have a tree of only one node */
generator->node.parent = NULL;
generator->node.children = 0;
generator->node.ptr.root = generator;
zend_object_std_init(&generator->std, class_type);
generator->std.handlers = &zend_generator_handlers;
@ -237,7 +257,6 @@ ZEND_API void zend_generator_create_zval(zend_execute_data *call, zend_op_array
/* Save execution context in generator object. */
generator = (zend_generator *) Z_OBJ_P(return_value);
execute_data->prev_execute_data = NULL;
generator->execute_data = execute_data;
generator->stack = EG(vm_stack);
generator->stack->top = EG(vm_stack_top);
@ -247,6 +266,9 @@ ZEND_API void zend_generator_create_zval(zend_execute_data *call, zend_op_array
/* EX(return_value) keeps pointer to zend_object (not a real zval) */
execute_data->return_value = (zval*)generator;
memset(&generator->execute_fake, 0, sizeof(zend_execute_data));
Z_OBJ(generator->execute_fake.This) = (zend_object *) generator;
}
/* }}} */
@ -258,20 +280,369 @@ static zend_function *zend_generator_get_constructor(zend_object *object) /* {{{
}
/* }}} */
ZEND_API void zend_generator_resume(zend_generator *generator) /* {{{ */
ZEND_API zend_execute_data *zend_generator_check_placeholder_frame(zend_execute_data *ptr)
{
if (!ptr->func && ptr->prev_execute_data && Z_OBJ(ptr->This)) {
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)
{
/* Throw the exception in the context of the generator */
zend_execute_data *original_execute_data = EG(current_execute_data);
EG(current_execute_data) = generator->execute_data;
if (exception) {
zend_throw_exception_object(exception);
} else {
zend_throw_exception_internal(NULL);
}
EG(current_execute_data) = original_execute_data;
}
static zend_generator *zend_generator_get_child(zend_generator_node *node, zend_generator *leaf)
{
switch (node->children) {
case 0:
return NULL;
case 1:
return node->child.array[0].child;
#define ZEND_GEN_GET_CHILD(x) \
if (node->child.array[x].leaf == leaf) { \
return node->child.array[x].child; \
}
case 4:
ZEND_GEN_GET_CHILD(3)
case 3:
ZEND_GEN_GET_CHILD(2)
case 2:
ZEND_GEN_GET_CHILD(1)
ZEND_GEN_GET_CHILD(0)
ZEND_ASSERT(0); // we never should have no matching child
}
return zend_hash_index_find_ptr(&node->child.ht, (zend_ulong) leaf);
}
static zend_generator_node *zend_generator_search_multi_children_node(zend_generator_node *node)
{
while (node->children == 1) {
node = &node->child.array[0].child->node;
}
return node->children > 1 ? node : NULL;
}
static void zend_generator_add_single_child(zend_generator_node *node, zend_generator *child, zend_generator *leaf)
{
if (node->children < 4) {
node->child.array[node->children].leaf = leaf;
node->child.array[node->children].child = child;
} else if (node->children > 4) {
zend_hash_index_add_ptr(&node->child.ht, (zend_ulong) leaf, child);
} else {
struct {
zend_generator *leaf;
zend_generator *child;
} array[4];
int i;
memcpy(&array, &node->child.array, sizeof(array));
zend_hash_init(&node->child.ht, 5, sigh, NULL, 0);
for (i = 0; i < 4; i++) {
zend_hash_index_add_ptr(&node->child.ht, (zend_ulong) array[i].leaf, array[i].child);
}
zend_hash_index_add_ptr(&node->child.ht, (zend_ulong) leaf, child);
}
node->children++;
}
static void zend_generator_merge_child_nodes(zend_generator_node *dest, zend_generator_node *src, zend_generator *child)
{
if (src->children <= 4) {
int i = src->children;
while (i--) {
zend_generator_add_single_child(dest, child, src->child.array[i].leaf);
}
} else {
zend_ulong leaf;
ZEND_HASH_FOREACH_NUM_KEY(&src->child.ht, leaf) {
zend_generator_add_single_child(dest, child, (zend_generator *) leaf);
} ZEND_HASH_FOREACH_END();
}
}
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;
generator->node.ptr.leaf = leaf;
while (next) {
if (next->node.children > 1) {
if (next->node.children > 4) {
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);
} else {
switch (next->node.children) {
#define ZEND_GEN_UPDATE_CHILD(x) \
if (next->node.child.array[x].leaf == generator) { \
next->node.child.array[x].leaf = leaf; \
break; \
}
case 4:
ZEND_GEN_UPDATE_CHILD(3)
case 3:
ZEND_GEN_UPDATE_CHILD(2)
case 2:
ZEND_GEN_UPDATE_CHILD(1)
ZEND_GEN_UPDATE_CHILD(0)
ZEND_ASSERT(0); // we never should have no matching child
}
}
}
next->node.ptr.leaf = leaf;
next = next->node.parent;
}
zend_generator_add_single_child(&generator->node, child, leaf);
} 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;
zend_generator_merge_child_nodes(&generator->node, multi_children_node, generator->node.child.array[0].child);
}
}
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;
}
}
}
void zend_generator_yield_from(zend_generator *this, zend_generator *from)
{
zend_generator_add_child(from, this);
this->node.parent = from;
}
ZEND_API zend_generator *zend_generator_get_current(zend_generator *generator)
{
zend_generator *leaf;
zend_generator *root;
if (generator->node.parent == NULL) {
/* we're not in yield from mode */
return generator;
}
leaf = generator->node.children ? generator->node.ptr.leaf : generator;
root = leaf->node.ptr.root;
if (root->execute_data && root->node.parent == NULL) {
/* generator still running */
return root;
}
while (!root->execute_data && root != generator) {
/* generator at the root had stopped */
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;
if (yield_from->opcode == ZEND_YIELD_FROM && !(yield_from->result_type & EXT_TYPE_UNUSED)) {
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;
}
zend_throw_exception(zend_ce_ClosedGeneratorException, "Generator yielded from aborted, no return value available", 0);
EG(current_execute_data) = original_execute_data;
} else {
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;
} while (root->node.parent);
}
}
return leaf->node.ptr.root = root;
}
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);
if (++iter->index > 0) {
iter->funcs->move_forward(iter);
if (UNEXPECTED(EG(exception) != NULL)) {
goto failure;
}
}
if (iter->funcs->valid(iter) == FAILURE) {
/* reached end of iteration */
goto failure;
}
value = iter->funcs->get_current_data(iter);
if (UNEXPECTED(EG(exception) != NULL || !value)) {
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);
goto failure;
}
} else {
ZVAL_LONG(&generator->key, iter->index);
}
}
return SUCCESS;
failure:
zval_ptr_dtor(&generator->values);
ZVAL_UNDEF(&generator->values);
return FAILURE;
}
/* }}} */
ZEND_API void zend_generator_resume(zend_generator *orig_generator) /* {{{ */
{
zend_generator *generator;
/* The generator is already closed, thus can't resume */
if (!generator->execute_data) {
if (!orig_generator->execute_data) {
return;
}
generator = zend_generator_get_current(orig_generator);
try_again:
if (generator->flags & ZEND_GENERATOR_CURRENTLY_RUNNING) {
zend_error(E_EXCEPTION | E_ERROR, "Cannot resume an already running generator");
return;
}
if (!Z_ISUNDEF(generator->values)) {
if (zend_generator_get_next_delegated_value(generator) == SUCCESS) {
return;
}
/* If there are no more deletegated values, resume the generator
* after the "yield from" expression. */
}
if ((orig_generator->flags & ZEND_GENERATOR_DO_INIT) && !Z_ISUNDEF(generator->value)) {
/* We must not advance Generator if we yield from a Generator being currently run */
return;
}
/* Drop the AT_FIRST_YIELD flag */
generator->flags &= ~ZEND_GENERATOR_AT_FIRST_YIELD;
orig_generator->flags &= ~ZEND_GENERATOR_AT_FIRST_YIELD;
{
/* Backup executor globals */
@ -290,7 +661,14 @@ ZEND_API void zend_generator_resume(zend_generator *generator) /* {{{ */
/* We want the backtrace to look as if the generator function was
* called from whatever method we are current running (e.g. next()).
* So we have to link generator call frame with caller call frame. */
generator->execute_data->prev_execute_data = original_execute_data;
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;
}
/* Resume execution */
generator->flags |= ZEND_GENERATOR_CURRENTLY_RUNNING;
@ -301,7 +679,6 @@ ZEND_API void zend_generator_resume(zend_generator *generator) /* {{{ */
if (generator->execute_data) {
generator->stack = EG(vm_stack);
generator->stack->top = EG(vm_stack_top);
generator->execute_data->prev_execute_data = NULL;
}
/* Restore executor globals */
@ -312,9 +689,25 @@ ZEND_API void zend_generator_resume(zend_generator *generator) /* {{{ */
EG(vm_stack) = original_stack;
/* If an exception was thrown in the generator we have to internally
* rethrow it in the parent scope. */
* 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). */
if (UNEXPECTED(EG(exception) != NULL)) {
zend_throw_exception_internal(NULL);
zend_generator_close(generator, 0);
if (generator == orig_generator) {
zend_throw_exception_internal(NULL);
} else {
generator = zend_generator_get_current(orig_generator);
zend_generator_throw_exception(generator, NULL);
goto try_again;
}
}
/* yield from was used, try another resume. */
if ((generator != orig_generator && !Z_ISUNDEF(generator->retval)) || (generator->execute_data && (generator->execute_data->opline - 1)->opcode == ZEND_YIELD_FROM)) {
generator = zend_generator_get_current(orig_generator);
goto try_again;
}
}
}
@ -322,8 +715,10 @@ ZEND_API void zend_generator_resume(zend_generator *generator) /* {{{ */
static void zend_generator_ensure_initialized(zend_generator *generator) /* {{{ */
{
if (generator->execute_data && Z_TYPE(generator->value) == IS_UNDEF) {
if (generator->execute_data && Z_TYPE(generator->value) == IS_UNDEF && generator->node.parent == NULL) {
generator->flags |= ZEND_GENERATOR_DO_INIT;
zend_generator_resume(generator);
generator->flags &= ~ZEND_GENERATOR_DO_INIT;
generator->flags |= ZEND_GENERATOR_AT_FIRST_YIELD;
}
}
@ -369,7 +764,9 @@ ZEND_METHOD(Generator, valid)
zend_generator_ensure_initialized(generator);
RETURN_BOOL(Z_TYPE(generator->value) != IS_UNDEF);
zend_generator_get_current(generator);
RETURN_BOOL(Z_TYPE(generator->value) != IS_UNDEF || generator->node.parent != NULL);
}
/* }}} */
@ -377,7 +774,7 @@ ZEND_METHOD(Generator, valid)
* Get the current value */
ZEND_METHOD(Generator, current)
{
zend_generator *generator;
zend_generator *generator, *root;
if (zend_parse_parameters_none() == FAILURE) {
return;
@ -387,8 +784,9 @@ ZEND_METHOD(Generator, current)
zend_generator_ensure_initialized(generator);
if (Z_TYPE(generator->value) != IS_UNDEF) {
RETURN_ZVAL_FAST(&generator->value);
root = zend_generator_get_current(generator);
if (Z_TYPE(root->value) != IS_UNDEF) {
RETURN_ZVAL_FAST(&root->value);
}
}
/* }}} */
@ -397,7 +795,7 @@ ZEND_METHOD(Generator, current)
* Get the current key */
ZEND_METHOD(Generator, key)
{
zend_generator *generator;
zend_generator *generator, *root;
if (zend_parse_parameters_none() == FAILURE) {
return;
@ -407,8 +805,9 @@ ZEND_METHOD(Generator, key)
zend_generator_ensure_initialized(generator);
if (Z_TYPE(generator->key) != IS_UNDEF) {
RETURN_ZVAL_FAST(&generator->key);
root = zend_generator_get_current(generator);
if (Z_TYPE(root->key) != IS_UNDEF) {
RETURN_ZVAL_FAST(&root->key);
}
}
/* }}} */
@ -436,7 +835,7 @@ ZEND_METHOD(Generator, next)
ZEND_METHOD(Generator, send)
{
zval *value;
zend_generator *generator;
zend_generator *generator, *root;
if (zend_parse_parameters(ZEND_NUM_ARGS(), "z", &value) == FAILURE) {
return;
@ -451,16 +850,18 @@ ZEND_METHOD(Generator, send)
return;
}
root = zend_generator_get_current(generator);
/* Put sent value in the target VAR slot, if it is used */
if (generator->send_target) {
if (Z_REFCOUNTED_P(generator->send_target)) Z_DELREF_P(generator->send_target);
ZVAL_COPY(generator->send_target, value);
if (root->send_target) {
if (Z_REFCOUNTED_P(root->send_target)) Z_DELREF_P(root->send_target);
ZVAL_COPY(root->send_target, value);
}
zend_generator_resume(generator);
if (Z_TYPE(generator->value) != IS_UNDEF) {
RETURN_ZVAL_FAST(&generator->value);
root = zend_generator_get_current(generator);
if (Z_TYPE(root->value) != IS_UNDEF) {
RETURN_ZVAL_FAST(&root->value);
}
}
/* }}} */
@ -483,18 +884,15 @@ ZEND_METHOD(Generator, throw)
zend_generator_ensure_initialized(generator);
if (generator->execute_data) {
/* Throw the exception in the context of the generator */
zend_execute_data *current_execute_data = EG(current_execute_data);
EG(current_execute_data) = generator->execute_data;
zend_generator *root = zend_generator_get_current(generator);
zend_throw_exception_object(&exception_copy);
EG(current_execute_data) = current_execute_data;
zend_generator_throw_exception(root, &exception_copy);
zend_generator_resume(generator);
if (Z_TYPE(generator->value) != IS_UNDEF) {
RETURN_ZVAL_FAST(&generator->value);
root = zend_generator_get_current(generator);
if (Z_TYPE(root->value) != IS_UNDEF) {
RETURN_ZVAL_FAST(&root->value);
}
} else {
/* If the generator is already closed throw the exception in the
@ -565,28 +963,34 @@ static int zend_generator_iterator_valid(zend_object_iterator *iterator) /* {{{
zend_generator_ensure_initialized(generator);
return Z_TYPE(generator->value) != IS_UNDEF ? SUCCESS : FAILURE;
zend_generator_get_current(generator);
return Z_TYPE(generator->value) != IS_UNDEF || generator->node.parent != NULL ? SUCCESS : FAILURE;
}
/* }}} */
static zval *zend_generator_iterator_get_data(zend_object_iterator *iterator) /* {{{ */
{
zend_generator *generator = (zend_generator*)Z_OBJ(iterator->data);
zend_generator *generator = (zend_generator*)Z_OBJ(iterator->data), *root;
zend_generator_ensure_initialized(generator);
return &generator->value;
root = zend_generator_get_current(generator);
return &root->value;
}
/* }}} */
static void zend_generator_iterator_get_key(zend_object_iterator *iterator, zval *key) /* {{{ */
{
zend_generator *generator = (zend_generator*)Z_OBJ(iterator->data);
zend_generator *generator = (zend_generator*)Z_OBJ(iterator->data), *root;
zend_generator_ensure_initialized(generator);
if (Z_TYPE(generator->key) != IS_UNDEF) {
ZVAL_ZVAL(key, &generator->key, 1, 0);
root = zend_generator_get_current(generator);
if (Z_TYPE(root->key) != IS_UNDEF) {
ZVAL_ZVAL(key, &root->key, 1, 0);
} else {
ZVAL_NULL(key);
}
@ -691,6 +1095,9 @@ void zend_register_generator_ce(void) /* {{{ */
zend_generator_handlers.dtor_obj = zend_generator_dtor_storage;
zend_generator_handlers.clone_obj = NULL;
zend_generator_handlers.get_constructor = zend_generator_get_constructor;
INIT_CLASS_ENTRY(ce, "ClosedGeneratorException", NULL);
zend_ce_ClosedGeneratorException = zend_register_internal_class_ex(&ce, zend_exception_get_default());
}
/* }}} */

View File

@ -13,6 +13,7 @@
| license@zend.com so we can mail you a copy immediately. |
+----------------------------------------------------------------------+
| Authors: Nikita Popov <nikic@php.net> |
| Bob Weinand <bobwei9@hotmail.com> |
+----------------------------------------------------------------------+
*/
@ -24,8 +25,28 @@
BEGIN_EXTERN_C()
extern ZEND_API zend_class_entry *zend_ce_generator;
extern ZEND_API zend_class_entry *zend_ce_ClosedGeneratorException;
typedef struct _zend_generator {
typedef struct _zend_generator_node zend_generator_node;
typedef struct _zend_generator zend_generator;
struct _zend_generator_node {
zend_generator *parent; /* NULL for root */
uint32_t children;
union {
HashTable ht; /* if > 4 children */
struct {
zend_generator *leaf;
zend_generator *child;
} array[4]; /* if <= 4 children */
} child;
union {
zend_generator *leaf; /* if > 0 children */
zend_generator *root; /* if 0 children */
} ptr;
};
struct _zend_generator {
zend_object std;
zend_object_iterator *iterator;
@ -47,19 +68,37 @@ typedef struct _zend_generator {
/* Largest used integer key for auto-incrementing keys */
zend_long largest_used_integer_key;
/* Values specified by "yield from" to yield from this generator.
* This is only used for arrays or non-generator Traversables.
* This zval also uses the u2 structure in the same way as
* by-value foreach. */
zval values;
/* Node of waiting generators when multiple "yield *" expressions
* are nested. */
zend_generator_node node;
/* Fake execute_data for stacktraces */
zend_execute_data execute_fake;
/* ZEND_GENERATOR_* flags */
zend_uchar flags;
} zend_generator;
};
static const zend_uchar ZEND_GENERATOR_CURRENTLY_RUNNING = 0x1;
static const zend_uchar ZEND_GENERATOR_FORCED_CLOSE = 0x2;
static const zend_uchar ZEND_GENERATOR_AT_FIRST_YIELD = 0x4;
static const zend_uchar ZEND_GENERATOR_DO_INIT = 0x8;
void zend_register_generator_ce(void);
ZEND_API void zend_generator_create_zval(zend_execute_data *call, zend_op_array *op_array, zval *return_value);
ZEND_API void zend_generator_close(zend_generator *generator, zend_bool finished_execution);
ZEND_API void zend_generator_resume(zend_generator *generator);
void zend_generator_yield_from(zend_generator *this, zend_generator *from);
ZEND_API zend_generator *zend_generator_get_current(zend_generator *generator);
ZEND_API zend_execute_data *zend_generator_check_placeholder_frame(zend_execute_data *ptr);
END_EXTERN_C()
#endif

View File

@ -578,6 +578,15 @@ static zend_always_inline void *zend_hash_str_update_mem(HashTable *ht, const ch
return zend_hash_str_update_ptr(ht, str, len, p);
}
static zend_always_inline void *zend_hash_index_add_ptr(HashTable *ht, zend_ulong h, void *pData)
{
zval tmp, *zv;
ZVAL_PTR(&tmp, pData);
zv = zend_hash_index_add(ht, h, &tmp);
return zv ? Z_PTR_P(zv) : NULL;
}
static zend_always_inline void *zend_hash_index_update_ptr(HashTable *ht, zend_ulong h, void *pData)
{
zval tmp, *zv;

View File

@ -67,6 +67,7 @@ static YYSIZE_T zend_yytnamerr(char*, const char*);
%right T_PRINT
%right T_YIELD
%right T_DOUBLE_ARROW
%right T_YIELD_FROM
%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 T_POW_EQUAL
%left '?' ':'
%right T_COALESCE
@ -112,6 +113,7 @@ static YYSIZE_T zend_yytnamerr(char*, const char*);
%token T_LOGICAL_AND "and (T_LOGICAL_AND)"
%token T_PRINT "print (T_PRINT)"
%token T_YIELD "yield (T_YIELD)"
%token T_YIELD_FROM "yield from (T_YIELD_FROM)"
%token T_PLUS_EQUAL "+= (T_PLUS_EQUAL)"
%token T_MINUS_EQUAL "-= (T_MINUS_EQUAL)"
%token T_MUL_EQUAL "*= (T_MUL_EQUAL)"
@ -907,6 +909,7 @@ expr_without_variable:
| T_YIELD { $$ = zend_ast_create(ZEND_AST_YIELD, NULL, NULL); }
| T_YIELD expr { $$ = zend_ast_create(ZEND_AST_YIELD, $2, NULL); }
| T_YIELD expr T_DOUBLE_ARROW expr { $$ = zend_ast_create(ZEND_AST_YIELD, $4, $2); }
| T_YIELD_FROM expr { $$ = zend_ast_create(ZEND_AST_YIELD_FROM, $2); }
| function returns_ref '(' parameter_list ')' lexical_vars return_type
backup_doc_comment '{' inner_statement_list '}'
{ $$ = zend_ast_create_decl(ZEND_AST_CLOSURE, $2, $1, $8,

File diff suppressed because it is too large Load Diff

View File

@ -1116,6 +1116,10 @@ NEWLINE ("\r"|"\n"|"\r\n")
return T_RETURN;
}
<ST_IN_SCRIPTING>"yield"{WHITESPACE}"from" {
return T_YIELD_FROM;
}
<ST_IN_SCRIPTING>"yield" {
return T_YIELD;
}

View File

@ -3892,8 +3892,7 @@ ZEND_VM_HANDLER(161, ZEND_GENERATOR_RETURN, CONST|TMP|VAR|CV, ANY)
zval *retval;
zend_free_op free_op1;
/* The generator object is stored in EX(return_value) */
zend_generator *generator = (zend_generator *) EX(return_value);
zend_generator *generator = zend_get_running_generator(execute_data);
SAVE_OPLINE();
retval = GET_OP1_ZVAL_PTR(BP_VAR_R);
@ -7027,8 +7026,7 @@ ZEND_VM_HANDLER(149, ZEND_HANDLE_EXCEPTION, ANY, ANY)
ZEND_VM_SET_OPCODE(&EX(func)->op_array.opcodes[catch_op_num]);
ZEND_VM_CONTINUE();
} else if (UNEXPECTED((EX(func)->op_array.fn_flags & ZEND_ACC_GENERATOR) != 0)) {
/* The generator object is stored in EX(return_value) */
zend_generator *generator = (zend_generator *) EX(return_value);
zend_generator *generator = zend_get_running_generator(execute_data);
zend_generator_close(generator, 1);
ZEND_VM_RETURN();
} else {
@ -7061,8 +7059,7 @@ ZEND_VM_HANDLER(150, ZEND_USER_OPCODE, ANY, ANY)
ZEND_VM_CONTINUE();
case ZEND_USER_OPCODE_RETURN:
if (UNEXPECTED((EX(func)->op_array.fn_flags & ZEND_ACC_GENERATOR) != 0)) {
/* The generator object is stored in EX(return_value) */
zend_generator *generator = (zend_generator *) EX(return_value);
zend_generator *generator = zend_get_running_generator(execute_data);
zend_generator_close(generator, 1);
ZEND_VM_RETURN();
} else {
@ -7162,8 +7159,7 @@ ZEND_VM_HANDLER(160, ZEND_YIELD, CONST|TMP|VAR|CV|UNUSED, CONST|TMP|VAR|CV|UNUSE
{
USE_OPLINE
/* The generator object is stored in EX(return_value) */
zend_generator *generator = (zend_generator *) EX(return_value);
zend_generator *generator = zend_get_running_generator(execute_data);
SAVE_OPLINE();
if (generator->flags & ZEND_GENERATOR_FORCED_CLOSE) {
@ -7297,6 +7293,99 @@ ZEND_VM_HANDLER(160, ZEND_YIELD, CONST|TMP|VAR|CV|UNUSED, CONST|TMP|VAR|CV|UNUSE
ZEND_VM_RETURN();
}
ZEND_VM_HANDLER(142, ZEND_YIELD_FROM, CONST|TMP|VAR|CV, ANY)
{
USE_OPLINE
/* The generator object is stored in EX(return_value) */
zend_generator *generator = (zend_generator *) EX(return_value);
zval *val;
zend_free_op free_op1;
SAVE_OPLINE();
val = GET_OP1_ZVAL_PTR_DEREF(BP_VAR_R);
if (Z_TYPE_P(val) == IS_ARRAY) {
ZVAL_COPY_VALUE(&generator->values, val);
if (OP1_TYPE != IS_TMP_VAR && Z_OPT_REFCOUNTED_P(val)) {
Z_ADDREF_P(val);
}
Z_FE_POS(generator->values) = 0;
FREE_OP1_IF_VAR();
} else if (OP1_TYPE != IS_CONST && Z_TYPE_P(val) == IS_OBJECT && Z_OBJCE_P(val)->get_iterator) {
zend_class_entry *ce = Z_OBJCE_P(val);
if (ce == zend_ce_generator) {
zend_generator *new_gen = (zend_generator *) Z_OBJ_P(val);
if (OP1_TYPE != IS_TMP_VAR) {
Z_ADDREF_P(val);
}
FREE_OP1_IF_VAR();
if (Z_ISUNDEF(new_gen->retval)) {
zend_generator_yield_from(generator, new_gen);
} else if (new_gen->execute_data == NULL) {
// TODO: Should be an engine exception
zend_error(E_RECOVERABLE_ERROR, "Generator passed to yield from was aborted without proper return and is unable to continue");
CHECK_EXCEPTION();
ZEND_VM_NEXT_OPCODE();
} else {
if (RETURN_VALUE_USED(opline)) {
ZVAL_COPY(EX_VAR(opline->result.var), &new_gen->retval);
}
CHECK_EXCEPTION();
ZEND_VM_NEXT_OPCODE();
}
} else {
zend_object_iterator *iter = ce->get_iterator(ce, val, 0);
FREE_OP1();
if (UNEXPECTED(!iter) || UNEXPECTED(EG(exception))) {
if (!EG(exception)) {
zend_throw_exception_ex(NULL, 0,
"Object of type %s did not create an Iterator", ce->name->val);
}
zend_throw_exception_internal(NULL);
HANDLE_EXCEPTION();
}
iter->index = 0;
if (iter->funcs->rewind) {
iter->funcs->rewind(iter);
if (UNEXPECTED(EG(exception) != NULL)) {
OBJ_RELEASE(&iter->std);
HANDLE_EXCEPTION();
}
}
ZVAL_OBJ(&generator->values, &iter->std);
}
} else {
// TODO: Should be an engine exception
zend_throw_exception(NULL, "Can use \"yield from\" only with arrays and Traversables", 0);
HANDLE_EXCEPTION();
}
/* This is the default return value
* when the expression is a Generator, it will be overwritten in zend_generator_resume() */
if (RETURN_VALUE_USED(opline)) {
ZVAL_NULL(EX_VAR(opline->result.var));
}
/* We increment to the next op, so we are at the correct position when the
* generator is resumed. */
ZEND_VM_INC_OPCODE();
/* 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();
}
ZEND_VM_HANDLER(159, ZEND_DISCARD_EXCEPTION, ANY, ANY)
{
USE_OPLINE
@ -7358,8 +7447,7 @@ ZEND_VM_HANDLER(163, ZEND_FAST_RET, ANY, ANY)
ZEND_VM_SET_OPCODE(&EX(func)->op_array.opcodes[opline->op2.opline_num]);
ZEND_VM_CONTINUE();
} else if (UNEXPECTED((EX(func)->op_array.fn_flags & ZEND_ACC_GENERATOR) != 0)) {
/* The generator object is stored in EX(return_value) */
zend_generator *generator = (zend_generator *) EX(return_value);
zend_generator *generator = zend_get_running_generator(execute_data);
zend_generator_close(generator, 1);
ZEND_VM_RETURN();
} else {

View File

@ -1620,8 +1620,7 @@ static ZEND_OPCODE_HANDLER_RET ZEND_FASTCALL ZEND_HANDLE_EXCEPTION_SPEC_HANDLER(
ZEND_VM_SET_OPCODE(&EX(func)->op_array.opcodes[catch_op_num]);
ZEND_VM_CONTINUE();
} else if (UNEXPECTED((EX(func)->op_array.fn_flags & ZEND_ACC_GENERATOR) != 0)) {
/* The generator object is stored in EX(return_value) */
zend_generator *generator = (zend_generator *) EX(return_value);
zend_generator *generator = zend_get_running_generator(execute_data);
zend_generator_close(generator, 1);
ZEND_VM_RETURN();
} else {
@ -1654,8 +1653,7 @@ static ZEND_OPCODE_HANDLER_RET ZEND_FASTCALL ZEND_USER_OPCODE_SPEC_HANDLER(ZEND_
ZEND_VM_CONTINUE();
case ZEND_USER_OPCODE_RETURN:
if (UNEXPECTED((EX(func)->op_array.fn_flags & ZEND_ACC_GENERATOR) != 0)) {
/* The generator object is stored in EX(return_value) */
zend_generator *generator = (zend_generator *) EX(return_value);
zend_generator *generator = zend_get_running_generator(execute_data);
zend_generator_close(generator, 1);
ZEND_VM_RETURN();
} else {
@ -1733,8 +1731,7 @@ static ZEND_OPCODE_HANDLER_RET ZEND_FASTCALL ZEND_FAST_RET_SPEC_HANDLER(ZEND_OPC
ZEND_VM_SET_OPCODE(&EX(func)->op_array.opcodes[opline->op2.opline_num]);
ZEND_VM_CONTINUE();
} else if (UNEXPECTED((EX(func)->op_array.fn_flags & ZEND_ACC_GENERATOR) != 0)) {
/* The generator object is stored in EX(return_value) */
zend_generator *generator = (zend_generator *) EX(return_value);
zend_generator *generator = zend_get_running_generator(execute_data);
zend_generator_close(generator, 1);
ZEND_VM_RETURN();
} else {
@ -3075,8 +3072,7 @@ static ZEND_OPCODE_HANDLER_RET ZEND_FASTCALL ZEND_GENERATOR_RETURN_SPEC_CONST_HA
zval *retval;
/* The generator object is stored in EX(return_value) */
zend_generator *generator = (zend_generator *) EX(return_value);
zend_generator *generator = zend_get_running_generator(execute_data);
SAVE_OPLINE();
retval = EX_CONSTANT(opline->op1);
@ -3966,6 +3962,96 @@ static ZEND_OPCODE_HANDLER_RET ZEND_FASTCALL ZEND_QM_ASSIGN_SPEC_CONST_HANDLER(Z
ZEND_VM_NEXT_OPCODE();
}
static ZEND_OPCODE_HANDLER_RET ZEND_FASTCALL ZEND_YIELD_FROM_SPEC_CONST_HANDLER(ZEND_OPCODE_HANDLER_ARGS)
{
USE_OPLINE
/* The generator object is stored in EX(return_value) */
zend_generator *generator = (zend_generator *) EX(return_value);
zval *val;
SAVE_OPLINE();
val = EX_CONSTANT(opline->op1);
if (Z_TYPE_P(val) == IS_ARRAY) {
ZVAL_COPY_VALUE(&generator->values, val);
if (IS_CONST != IS_TMP_VAR && Z_OPT_REFCOUNTED_P(val)) {
Z_ADDREF_P(val);
}
Z_FE_POS(generator->values) = 0;
} else if (IS_CONST != IS_CONST && Z_TYPE_P(val) == IS_OBJECT && Z_OBJCE_P(val)->get_iterator) {
zend_class_entry *ce = Z_OBJCE_P(val);
if (ce == zend_ce_generator) {
zend_generator *new_gen = (zend_generator *) Z_OBJ_P(val);
if (IS_CONST != IS_TMP_VAR) {
Z_ADDREF_P(val);
}
if (Z_ISUNDEF(new_gen->retval)) {
zend_generator_yield_from(generator, new_gen);
} else if (new_gen->execute_data == NULL) {
// TODO: Should be an engine exception
zend_error(E_RECOVERABLE_ERROR, "Generator passed to yield from was aborted without proper return and is unable to continue");
CHECK_EXCEPTION();
ZEND_VM_NEXT_OPCODE();
} else {
if (RETURN_VALUE_USED(opline)) {
ZVAL_COPY(EX_VAR(opline->result.var), &new_gen->retval);
}
CHECK_EXCEPTION();
ZEND_VM_NEXT_OPCODE();
}
} else {
zend_object_iterator *iter = ce->get_iterator(ce, val, 0);
if (UNEXPECTED(!iter) || UNEXPECTED(EG(exception))) {
if (!EG(exception)) {
zend_throw_exception_ex(NULL, 0,
"Object of type %s did not create an Iterator", ce->name->val);
}
zend_throw_exception_internal(NULL);
HANDLE_EXCEPTION();
}
iter->index = 0;
if (iter->funcs->rewind) {
iter->funcs->rewind(iter);
if (UNEXPECTED(EG(exception) != NULL)) {
OBJ_RELEASE(&iter->std);
HANDLE_EXCEPTION();
}
}
ZVAL_OBJ(&generator->values, &iter->std);
}
} else {
// TODO: Should be an engine exception
zend_throw_exception(NULL, "Can use \"yield from\" only with arrays and Traversables", 0);
HANDLE_EXCEPTION();
}
/* This is the default return value
* when the expression is a Generator, it will be overwritten in zend_generator_resume() */
if (RETURN_VALUE_USED(opline)) {
ZVAL_NULL(EX_VAR(opline->result.var));
}
/* We increment to the next op, so we are at the correct position when the
* generator is resumed. */
ZEND_VM_INC_OPCODE();
/* 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 ZEND_OPCODE_HANDLER_RET ZEND_FASTCALL ZEND_STRLEN_SPEC_CONST_HANDLER(ZEND_OPCODE_HANDLER_ARGS)
{
USE_OPLINE
@ -6024,8 +6110,7 @@ static ZEND_OPCODE_HANDLER_RET ZEND_FASTCALL ZEND_YIELD_SPEC_CONST_CONST_HANDLER
{
USE_OPLINE
/* The generator object is stored in EX(return_value) */
zend_generator *generator = (zend_generator *) EX(return_value);
zend_generator *generator = zend_get_running_generator(execute_data);
SAVE_OPLINE();
if (generator->flags & ZEND_GENERATOR_FORCED_CLOSE) {
@ -6208,8 +6293,7 @@ static ZEND_OPCODE_HANDLER_RET ZEND_FASTCALL ZEND_YIELD_SPEC_CONST_TMP_HANDLER(Z
{
USE_OPLINE
/* The generator object is stored in EX(return_value) */
zend_generator *generator = (zend_generator *) EX(return_value);
zend_generator *generator = zend_get_running_generator(execute_data);
SAVE_OPLINE();
if (generator->flags & ZEND_GENERATOR_FORCED_CLOSE) {
@ -6738,8 +6822,7 @@ static ZEND_OPCODE_HANDLER_RET ZEND_FASTCALL ZEND_YIELD_SPEC_CONST_VAR_HANDLER(Z
{
USE_OPLINE
/* The generator object is stored in EX(return_value) */
zend_generator *generator = (zend_generator *) EX(return_value);
zend_generator *generator = zend_get_running_generator(execute_data);
SAVE_OPLINE();
if (generator->flags & ZEND_GENERATOR_FORCED_CLOSE) {
@ -7615,8 +7698,7 @@ static ZEND_OPCODE_HANDLER_RET ZEND_FASTCALL ZEND_YIELD_SPEC_CONST_UNUSED_HANDLE
{
USE_OPLINE
/* The generator object is stored in EX(return_value) */
zend_generator *generator = (zend_generator *) EX(return_value);
zend_generator *generator = zend_get_running_generator(execute_data);
SAVE_OPLINE();
if (generator->flags & ZEND_GENERATOR_FORCED_CLOSE) {
@ -9218,8 +9300,7 @@ static ZEND_OPCODE_HANDLER_RET ZEND_FASTCALL ZEND_YIELD_SPEC_CONST_CV_HANDLER(ZE
{
USE_OPLINE
/* The generator object is stored in EX(return_value) */
zend_generator *generator = (zend_generator *) EX(return_value);
zend_generator *generator = zend_get_running_generator(execute_data);
SAVE_OPLINE();
if (generator->flags & ZEND_GENERATOR_FORCED_CLOSE) {
@ -10879,8 +10960,7 @@ static ZEND_OPCODE_HANDLER_RET ZEND_FASTCALL ZEND_GENERATOR_RETURN_SPEC_TMP_HAND
zval *retval;
zend_free_op free_op1;
/* The generator object is stored in EX(return_value) */
zend_generator *generator = (zend_generator *) EX(return_value);
zend_generator *generator = zend_get_running_generator(execute_data);
SAVE_OPLINE();
retval = _get_zval_ptr_tmp(opline->op1.var, execute_data, &free_op1);
@ -11476,6 +11556,97 @@ static ZEND_OPCODE_HANDLER_RET ZEND_FASTCALL ZEND_QM_ASSIGN_SPEC_TMP_HANDLER(ZEN
ZEND_VM_NEXT_OPCODE();
}
static ZEND_OPCODE_HANDLER_RET ZEND_FASTCALL ZEND_YIELD_FROM_SPEC_TMP_HANDLER(ZEND_OPCODE_HANDLER_ARGS)
{
USE_OPLINE
/* The generator object is stored in EX(return_value) */
zend_generator *generator = (zend_generator *) EX(return_value);
zval *val;
zend_free_op free_op1;
SAVE_OPLINE();
val = _get_zval_ptr_tmp(opline->op1.var, execute_data, &free_op1);
if (Z_TYPE_P(val) == IS_ARRAY) {
ZVAL_COPY_VALUE(&generator->values, val);
if (IS_TMP_VAR != IS_TMP_VAR && Z_OPT_REFCOUNTED_P(val)) {
Z_ADDREF_P(val);
}
Z_FE_POS(generator->values) = 0;
} else if (IS_TMP_VAR != IS_CONST && Z_TYPE_P(val) == IS_OBJECT && Z_OBJCE_P(val)->get_iterator) {
zend_class_entry *ce = Z_OBJCE_P(val);
if (ce == zend_ce_generator) {
zend_generator *new_gen = (zend_generator *) Z_OBJ_P(val);
if (IS_TMP_VAR != IS_TMP_VAR) {
Z_ADDREF_P(val);
}
if (Z_ISUNDEF(new_gen->retval)) {
zend_generator_yield_from(generator, new_gen);
} else if (new_gen->execute_data == NULL) {
// TODO: Should be an engine exception
zend_error(E_RECOVERABLE_ERROR, "Generator passed to yield from was aborted without proper return and is unable to continue");
CHECK_EXCEPTION();
ZEND_VM_NEXT_OPCODE();
} else {
if (RETURN_VALUE_USED(opline)) {
ZVAL_COPY(EX_VAR(opline->result.var), &new_gen->retval);
}
CHECK_EXCEPTION();
ZEND_VM_NEXT_OPCODE();
}
} else {
zend_object_iterator *iter = ce->get_iterator(ce, val, 0);
zval_ptr_dtor_nogc(free_op1);
if (UNEXPECTED(!iter) || UNEXPECTED(EG(exception))) {
if (!EG(exception)) {
zend_throw_exception_ex(NULL, 0,
"Object of type %s did not create an Iterator", ce->name->val);
}
zend_throw_exception_internal(NULL);
HANDLE_EXCEPTION();
}
iter->index = 0;
if (iter->funcs->rewind) {
iter->funcs->rewind(iter);
if (UNEXPECTED(EG(exception) != NULL)) {
OBJ_RELEASE(&iter->std);
HANDLE_EXCEPTION();
}
}
ZVAL_OBJ(&generator->values, &iter->std);
}
} else {
// TODO: Should be an engine exception
zend_throw_exception(NULL, "Can use \"yield from\" only with arrays and Traversables", 0);
HANDLE_EXCEPTION();
}
/* This is the default return value
* when the expression is a Generator, it will be overwritten in zend_generator_resume() */
if (RETURN_VALUE_USED(opline)) {
ZVAL_NULL(EX_VAR(opline->result.var));
}
/* We increment to the next op, so we are at the correct position when the
* generator is resumed. */
ZEND_VM_INC_OPCODE();
/* 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 ZEND_OPCODE_HANDLER_RET ZEND_FASTCALL ZEND_TYPE_CHECK_SPEC_TMP_HANDLER(ZEND_OPCODE_HANDLER_ARGS)
{
USE_OPLINE
@ -11911,8 +12082,7 @@ static ZEND_OPCODE_HANDLER_RET ZEND_FASTCALL ZEND_YIELD_SPEC_TMP_CONST_HANDLER(Z
{
USE_OPLINE
/* The generator object is stored in EX(return_value) */
zend_generator *generator = (zend_generator *) EX(return_value);
zend_generator *generator = zend_get_running_generator(execute_data);
SAVE_OPLINE();
if (generator->flags & ZEND_GENERATOR_FORCED_CLOSE) {
@ -12080,8 +12250,7 @@ static ZEND_OPCODE_HANDLER_RET ZEND_FASTCALL ZEND_YIELD_SPEC_TMP_TMP_HANDLER(ZEN
{
USE_OPLINE
/* The generator object is stored in EX(return_value) */
zend_generator *generator = (zend_generator *) EX(return_value);
zend_generator *generator = zend_get_running_generator(execute_data);
SAVE_OPLINE();
if (generator->flags & ZEND_GENERATOR_FORCED_CLOSE) {
@ -12249,8 +12418,7 @@ static ZEND_OPCODE_HANDLER_RET ZEND_FASTCALL ZEND_YIELD_SPEC_TMP_VAR_HANDLER(ZEN
{
USE_OPLINE
/* The generator object is stored in EX(return_value) */
zend_generator *generator = (zend_generator *) EX(return_value);
zend_generator *generator = zend_get_running_generator(execute_data);
SAVE_OPLINE();
if (generator->flags & ZEND_GENERATOR_FORCED_CLOSE) {
@ -12614,8 +12782,7 @@ static ZEND_OPCODE_HANDLER_RET ZEND_FASTCALL ZEND_YIELD_SPEC_TMP_UNUSED_HANDLER(
{
USE_OPLINE
/* The generator object is stored in EX(return_value) */
zend_generator *generator = (zend_generator *) EX(return_value);
zend_generator *generator = zend_get_running_generator(execute_data);
SAVE_OPLINE();
if (generator->flags & ZEND_GENERATOR_FORCED_CLOSE) {
@ -13136,8 +13303,7 @@ static ZEND_OPCODE_HANDLER_RET ZEND_FASTCALL ZEND_YIELD_SPEC_TMP_CV_HANDLER(ZEND
{
USE_OPLINE
/* The generator object is stored in EX(return_value) */
zend_generator *generator = (zend_generator *) EX(return_value);
zend_generator *generator = zend_get_running_generator(execute_data);
SAVE_OPLINE();
if (generator->flags & ZEND_GENERATOR_FORCED_CLOSE) {
@ -13912,8 +14078,7 @@ static ZEND_OPCODE_HANDLER_RET ZEND_FASTCALL ZEND_GENERATOR_RETURN_SPEC_VAR_HAND
zval *retval;
zend_free_op free_op1;
/* The generator object is stored in EX(return_value) */
zend_generator *generator = (zend_generator *) EX(return_value);
zend_generator *generator = zend_get_running_generator(execute_data);
SAVE_OPLINE();
retval = _get_zval_ptr_var(opline->op1.var, execute_data, &free_op1);
@ -15052,6 +15217,99 @@ static ZEND_OPCODE_HANDLER_RET ZEND_FASTCALL ZEND_QM_ASSIGN_SPEC_VAR_HANDLER(ZEN
ZEND_VM_NEXT_OPCODE();
}
static ZEND_OPCODE_HANDLER_RET ZEND_FASTCALL ZEND_YIELD_FROM_SPEC_VAR_HANDLER(ZEND_OPCODE_HANDLER_ARGS)
{
USE_OPLINE
/* The generator object is stored in EX(return_value) */
zend_generator *generator = (zend_generator *) EX(return_value);
zval *val;
zend_free_op free_op1;
SAVE_OPLINE();
val = _get_zval_ptr_var_deref(opline->op1.var, execute_data, &free_op1);
if (Z_TYPE_P(val) == IS_ARRAY) {
ZVAL_COPY_VALUE(&generator->values, val);
if (IS_VAR != IS_TMP_VAR && Z_OPT_REFCOUNTED_P(val)) {
Z_ADDREF_P(val);
}
Z_FE_POS(generator->values) = 0;
zval_ptr_dtor_nogc(free_op1);
} else if (IS_VAR != IS_CONST && Z_TYPE_P(val) == IS_OBJECT && Z_OBJCE_P(val)->get_iterator) {
zend_class_entry *ce = Z_OBJCE_P(val);
if (ce == zend_ce_generator) {
zend_generator *new_gen = (zend_generator *) Z_OBJ_P(val);
if (IS_VAR != IS_TMP_VAR) {
Z_ADDREF_P(val);
}
zval_ptr_dtor_nogc(free_op1);
if (Z_ISUNDEF(new_gen->retval)) {
zend_generator_yield_from(generator, new_gen);
} else if (new_gen->execute_data == NULL) {
// TODO: Should be an engine exception
zend_error(E_RECOVERABLE_ERROR, "Generator passed to yield from was aborted without proper return and is unable to continue");
CHECK_EXCEPTION();
ZEND_VM_NEXT_OPCODE();
} else {
if (RETURN_VALUE_USED(opline)) {
ZVAL_COPY(EX_VAR(opline->result.var), &new_gen->retval);
}
CHECK_EXCEPTION();
ZEND_VM_NEXT_OPCODE();
}
} else {
zend_object_iterator *iter = ce->get_iterator(ce, val, 0);
zval_ptr_dtor_nogc(free_op1);
if (UNEXPECTED(!iter) || UNEXPECTED(EG(exception))) {
if (!EG(exception)) {
zend_throw_exception_ex(NULL, 0,
"Object of type %s did not create an Iterator", ce->name->val);
}
zend_throw_exception_internal(NULL);
HANDLE_EXCEPTION();
}
iter->index = 0;
if (iter->funcs->rewind) {
iter->funcs->rewind(iter);
if (UNEXPECTED(EG(exception) != NULL)) {
OBJ_RELEASE(&iter->std);
HANDLE_EXCEPTION();
}
}
ZVAL_OBJ(&generator->values, &iter->std);
}
} else {
// TODO: Should be an engine exception
zend_throw_exception(NULL, "Can use \"yield from\" only with arrays and Traversables", 0);
HANDLE_EXCEPTION();
}
/* This is the default return value
* when the expression is a Generator, it will be overwritten in zend_generator_resume() */
if (RETURN_VALUE_USED(opline)) {
ZVAL_NULL(EX_VAR(opline->result.var));
}
/* We increment to the next op, so we are at the correct position when the
* generator is resumed. */
ZEND_VM_INC_OPCODE();
/* 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 ZEND_OPCODE_HANDLER_RET ZEND_FASTCALL ZEND_TYPE_CHECK_SPEC_VAR_HANDLER(ZEND_OPCODE_HANDLER_ARGS)
{
USE_OPLINE
@ -16756,8 +17014,7 @@ static ZEND_OPCODE_HANDLER_RET ZEND_FASTCALL ZEND_YIELD_SPEC_VAR_CONST_HANDLER(Z
{
USE_OPLINE
/* The generator object is stored in EX(return_value) */
zend_generator *generator = (zend_generator *) EX(return_value);
zend_generator *generator = zend_get_running_generator(execute_data);
SAVE_OPLINE();
if (generator->flags & ZEND_GENERATOR_FORCED_CLOSE) {
@ -16960,8 +17217,7 @@ static ZEND_OPCODE_HANDLER_RET ZEND_FASTCALL ZEND_YIELD_SPEC_VAR_TMP_HANDLER(ZEN
{
USE_OPLINE
/* The generator object is stored in EX(return_value) */
zend_generator *generator = (zend_generator *) EX(return_value);
zend_generator *generator = zend_get_running_generator(execute_data);
SAVE_OPLINE();
if (generator->flags & ZEND_GENERATOR_FORCED_CLOSE) {
@ -17223,8 +17479,7 @@ static ZEND_OPCODE_HANDLER_RET ZEND_FASTCALL ZEND_YIELD_SPEC_VAR_VAR_HANDLER(ZEN
{
USE_OPLINE
/* The generator object is stored in EX(return_value) */
zend_generator *generator = (zend_generator *) EX(return_value);
zend_generator *generator = zend_get_running_generator(execute_data);
SAVE_OPLINE();
if (generator->flags & ZEND_GENERATOR_FORCED_CLOSE) {
@ -18183,8 +18438,7 @@ static ZEND_OPCODE_HANDLER_RET ZEND_FASTCALL ZEND_YIELD_SPEC_VAR_UNUSED_HANDLER(
{
USE_OPLINE
/* The generator object is stored in EX(return_value) */
zend_generator *generator = (zend_generator *) EX(return_value);
zend_generator *generator = zend_get_running_generator(execute_data);
SAVE_OPLINE();
if (generator->flags & ZEND_GENERATOR_FORCED_CLOSE) {
@ -19938,8 +20192,7 @@ static ZEND_OPCODE_HANDLER_RET ZEND_FASTCALL ZEND_YIELD_SPEC_VAR_CV_HANDLER(ZEND
{
USE_OPLINE
/* The generator object is stored in EX(return_value) */
zend_generator *generator = (zend_generator *) EX(return_value);
zend_generator *generator = zend_get_running_generator(execute_data);
SAVE_OPLINE();
if (generator->flags & ZEND_GENERATOR_FORCED_CLOSE) {
@ -23162,8 +23415,7 @@ static ZEND_OPCODE_HANDLER_RET ZEND_FASTCALL ZEND_YIELD_SPEC_UNUSED_CONST_HANDLE
{
USE_OPLINE
/* The generator object is stored in EX(return_value) */
zend_generator *generator = (zend_generator *) EX(return_value);
zend_generator *generator = zend_get_running_generator(execute_data);
SAVE_OPLINE();
if (generator->flags & ZEND_GENERATOR_FORCED_CLOSE) {
@ -23300,8 +23552,7 @@ static ZEND_OPCODE_HANDLER_RET ZEND_FASTCALL ZEND_YIELD_SPEC_UNUSED_TMP_HANDLER(
{
USE_OPLINE
/* The generator object is stored in EX(return_value) */
zend_generator *generator = (zend_generator *) EX(return_value);
zend_generator *generator = zend_get_running_generator(execute_data);
SAVE_OPLINE();
if (generator->flags & ZEND_GENERATOR_FORCED_CLOSE) {
@ -23438,8 +23689,7 @@ static ZEND_OPCODE_HANDLER_RET ZEND_FASTCALL ZEND_YIELD_SPEC_UNUSED_VAR_HANDLER(
{
USE_OPLINE
/* The generator object is stored in EX(return_value) */
zend_generator *generator = (zend_generator *) EX(return_value);
zend_generator *generator = zend_get_running_generator(execute_data);
SAVE_OPLINE();
if (generator->flags & ZEND_GENERATOR_FORCED_CLOSE) {
@ -23951,8 +24201,7 @@ static ZEND_OPCODE_HANDLER_RET ZEND_FASTCALL ZEND_YIELD_SPEC_UNUSED_UNUSED_HANDL
{
USE_OPLINE
/* The generator object is stored in EX(return_value) */
zend_generator *generator = (zend_generator *) EX(return_value);
zend_generator *generator = zend_get_running_generator(execute_data);
SAVE_OPLINE();
if (generator->flags & ZEND_GENERATOR_FORCED_CLOSE) {
@ -25455,8 +25704,7 @@ static ZEND_OPCODE_HANDLER_RET ZEND_FASTCALL ZEND_YIELD_SPEC_UNUSED_CV_HANDLER(Z
{
USE_OPLINE
/* The generator object is stored in EX(return_value) */
zend_generator *generator = (zend_generator *) EX(return_value);
zend_generator *generator = zend_get_running_generator(execute_data);
SAVE_OPLINE();
if (generator->flags & ZEND_GENERATOR_FORCED_CLOSE) {
@ -27505,8 +27753,7 @@ static ZEND_OPCODE_HANDLER_RET ZEND_FASTCALL ZEND_GENERATOR_RETURN_SPEC_CV_HANDL
zval *retval;
/* The generator object is stored in EX(return_value) */
zend_generator *generator = (zend_generator *) EX(return_value);
zend_generator *generator = zend_get_running_generator(execute_data);
SAVE_OPLINE();
retval = _get_zval_ptr_cv_BP_VAR_R(execute_data, opline->op1.var);
@ -28495,6 +28742,96 @@ static ZEND_OPCODE_HANDLER_RET ZEND_FASTCALL ZEND_QM_ASSIGN_SPEC_CV_HANDLER(ZEND
ZEND_VM_NEXT_OPCODE();
}
static ZEND_OPCODE_HANDLER_RET ZEND_FASTCALL ZEND_YIELD_FROM_SPEC_CV_HANDLER(ZEND_OPCODE_HANDLER_ARGS)
{
USE_OPLINE
/* The generator object is stored in EX(return_value) */
zend_generator *generator = (zend_generator *) EX(return_value);
zval *val;
SAVE_OPLINE();
val = _get_zval_ptr_cv_deref_BP_VAR_R(execute_data, opline->op1.var);
if (Z_TYPE_P(val) == IS_ARRAY) {
ZVAL_COPY_VALUE(&generator->values, val);
if (IS_CV != IS_TMP_VAR && Z_OPT_REFCOUNTED_P(val)) {
Z_ADDREF_P(val);
}
Z_FE_POS(generator->values) = 0;
} else if (IS_CV != IS_CONST && Z_TYPE_P(val) == IS_OBJECT && Z_OBJCE_P(val)->get_iterator) {
zend_class_entry *ce = Z_OBJCE_P(val);
if (ce == zend_ce_generator) {
zend_generator *new_gen = (zend_generator *) Z_OBJ_P(val);
if (IS_CV != IS_TMP_VAR) {
Z_ADDREF_P(val);
}
if (Z_ISUNDEF(new_gen->retval)) {
zend_generator_yield_from(generator, new_gen);
} else if (new_gen->execute_data == NULL) {
// TODO: Should be an engine exception
zend_error(E_RECOVERABLE_ERROR, "Generator passed to yield from was aborted without proper return and is unable to continue");
CHECK_EXCEPTION();
ZEND_VM_NEXT_OPCODE();
} else {
if (RETURN_VALUE_USED(opline)) {
ZVAL_COPY(EX_VAR(opline->result.var), &new_gen->retval);
}
CHECK_EXCEPTION();
ZEND_VM_NEXT_OPCODE();
}
} else {
zend_object_iterator *iter = ce->get_iterator(ce, val, 0);
if (UNEXPECTED(!iter) || UNEXPECTED(EG(exception))) {
if (!EG(exception)) {
zend_throw_exception_ex(NULL, 0,
"Object of type %s did not create an Iterator", ce->name->val);
}
zend_throw_exception_internal(NULL);
HANDLE_EXCEPTION();
}
iter->index = 0;
if (iter->funcs->rewind) {
iter->funcs->rewind(iter);
if (UNEXPECTED(EG(exception) != NULL)) {
OBJ_RELEASE(&iter->std);
HANDLE_EXCEPTION();
}
}
ZVAL_OBJ(&generator->values, &iter->std);
}
} else {
// TODO: Should be an engine exception
zend_throw_exception(NULL, "Can use \"yield from\" only with arrays and Traversables", 0);
HANDLE_EXCEPTION();
}
/* This is the default return value
* when the expression is a Generator, it will be overwritten in zend_generator_resume() */
if (RETURN_VALUE_USED(opline)) {
ZVAL_NULL(EX_VAR(opline->result.var));
}
/* We increment to the next op, so we are at the correct position when the
* generator is resumed. */
ZEND_VM_INC_OPCODE();
/* 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 ZEND_OPCODE_HANDLER_RET ZEND_FASTCALL ZEND_STRLEN_SPEC_CV_HANDLER(ZEND_OPCODE_HANDLER_ARGS)
{
USE_OPLINE
@ -31453,8 +31790,7 @@ static ZEND_OPCODE_HANDLER_RET ZEND_FASTCALL ZEND_YIELD_SPEC_CV_CONST_HANDLER(ZE
{
USE_OPLINE
/* The generator object is stored in EX(return_value) */
zend_generator *generator = (zend_generator *) EX(return_value);
zend_generator *generator = zend_get_running_generator(execute_data);
SAVE_OPLINE();
if (generator->flags & ZEND_GENERATOR_FORCED_CLOSE) {
@ -31727,8 +32063,7 @@ static ZEND_OPCODE_HANDLER_RET ZEND_FASTCALL ZEND_YIELD_SPEC_CV_TMP_HANDLER(ZEND
{
USE_OPLINE
/* The generator object is stored in EX(return_value) */
zend_generator *generator = (zend_generator *) EX(return_value);
zend_generator *generator = zend_get_running_generator(execute_data);
SAVE_OPLINE();
if (generator->flags & ZEND_GENERATOR_FORCED_CLOSE) {
@ -32392,8 +32727,7 @@ static ZEND_OPCODE_HANDLER_RET ZEND_FASTCALL ZEND_YIELD_SPEC_CV_VAR_HANDLER(ZEND
{
USE_OPLINE
/* The generator object is stored in EX(return_value) */
zend_generator *generator = (zend_generator *) EX(return_value);
zend_generator *generator = zend_get_running_generator(execute_data);
SAVE_OPLINE();
if (generator->flags & ZEND_GENERATOR_FORCED_CLOSE) {
@ -33566,8 +33900,7 @@ static ZEND_OPCODE_HANDLER_RET ZEND_FASTCALL ZEND_YIELD_SPEC_CV_UNUSED_HANDLER(Z
{
USE_OPLINE
/* The generator object is stored in EX(return_value) */
zend_generator *generator = (zend_generator *) EX(return_value);
zend_generator *generator = zend_get_running_generator(execute_data);
SAVE_OPLINE();
if (generator->flags & ZEND_GENERATOR_FORCED_CLOSE) {
@ -36192,8 +36525,7 @@ static ZEND_OPCODE_HANDLER_RET ZEND_FASTCALL ZEND_YIELD_SPEC_CV_CV_HANDLER(ZEND_
{
USE_OPLINE
/* The generator object is stored in EX(return_value) */
zend_generator *generator = (zend_generator *) EX(return_value);
zend_generator *generator = zend_get_running_generator(execute_data);
SAVE_OPLINE();
if (generator->flags & ZEND_GENERATOR_FORCED_CLOSE) {
@ -47148,31 +47480,31 @@ void zend_init_opcodes_handlers(void)
ZEND_DECLARE_FUNCTION_SPEC_HANDLER,
ZEND_DECLARE_FUNCTION_SPEC_HANDLER,
ZEND_DECLARE_FUNCTION_SPEC_HANDLER,
ZEND_YIELD_FROM_SPEC_CONST_HANDLER,
ZEND_YIELD_FROM_SPEC_CONST_HANDLER,
ZEND_YIELD_FROM_SPEC_CONST_HANDLER,
ZEND_YIELD_FROM_SPEC_CONST_HANDLER,
ZEND_YIELD_FROM_SPEC_CONST_HANDLER,
ZEND_YIELD_FROM_SPEC_TMP_HANDLER,
ZEND_YIELD_FROM_SPEC_TMP_HANDLER,
ZEND_YIELD_FROM_SPEC_TMP_HANDLER,
ZEND_YIELD_FROM_SPEC_TMP_HANDLER,
ZEND_YIELD_FROM_SPEC_TMP_HANDLER,
ZEND_YIELD_FROM_SPEC_VAR_HANDLER,
ZEND_YIELD_FROM_SPEC_VAR_HANDLER,
ZEND_YIELD_FROM_SPEC_VAR_HANDLER,
ZEND_YIELD_FROM_SPEC_VAR_HANDLER,
ZEND_YIELD_FROM_SPEC_VAR_HANDLER,
ZEND_NULL_HANDLER,
ZEND_NULL_HANDLER,
ZEND_NULL_HANDLER,
ZEND_NULL_HANDLER,
ZEND_NULL_HANDLER,
ZEND_NULL_HANDLER,
ZEND_NULL_HANDLER,
ZEND_NULL_HANDLER,
ZEND_NULL_HANDLER,
ZEND_NULL_HANDLER,
ZEND_NULL_HANDLER,
ZEND_NULL_HANDLER,
ZEND_NULL_HANDLER,
ZEND_NULL_HANDLER,
ZEND_NULL_HANDLER,
ZEND_NULL_HANDLER,
ZEND_NULL_HANDLER,
ZEND_NULL_HANDLER,
ZEND_NULL_HANDLER,
ZEND_NULL_HANDLER,
ZEND_NULL_HANDLER,
ZEND_NULL_HANDLER,
ZEND_NULL_HANDLER,
ZEND_NULL_HANDLER,
ZEND_NULL_HANDLER,
ZEND_YIELD_FROM_SPEC_CV_HANDLER,
ZEND_YIELD_FROM_SPEC_CV_HANDLER,
ZEND_YIELD_FROM_SPEC_CV_HANDLER,
ZEND_YIELD_FROM_SPEC_CV_HANDLER,
ZEND_YIELD_FROM_SPEC_CV_HANDLER,
ZEND_DECLARE_CONST_SPEC_CONST_CONST_HANDLER,
ZEND_NULL_HANDLER,
ZEND_NULL_HANDLER,

View File

@ -164,7 +164,7 @@ const char *zend_vm_opcodes_map[171] = {
"ZEND_DECLARE_CLASS",
"ZEND_DECLARE_INHERITED_CLASS",
"ZEND_DECLARE_FUNCTION",
NULL,
"ZEND_YIELD_FROM",
"ZEND_DECLARE_CONST",
"ZEND_ADD_INTERFACE",
"ZEND_DECLARE_INHERITED_CLASS_DELAYED",

View File

@ -173,6 +173,7 @@ END_EXTERN_C()
#define ZEND_DECLARE_CLASS 139
#define ZEND_DECLARE_INHERITED_CLASS 140
#define ZEND_DECLARE_FUNCTION 141
#define ZEND_YIELD_FROM 142
#define ZEND_DECLARE_CONST 143
#define ZEND_ADD_INTERFACE 144
#define ZEND_DECLARE_INHERITED_CLASS_DELAYED 145