mirror of
https://github.com/php/php-src.git
synced 2024-11-27 03:44:07 +08:00
Added yield from operator
This commit is contained in:
parent
fcdb6e0811
commit
b4a142ab97
@ -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)
|
||||
|
42
Zend/tests/generators/basic_yield_from_proxying.phpt
Normal file
42
Zend/tests/generators/basic_yield_from_proxying.phpt
Normal 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)
|
@ -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)
|
||||
|
50
Zend/tests/generators/mutli_yield_from_with_exception.phpt
Normal file
50
Zend/tests/generators/mutli_yield_from_with_exception.phpt
Normal 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
|
||||
|
34
Zend/tests/generators/recursive_yield_from.phpt
Normal file
34
Zend/tests/generators/recursive_yield_from.phpt
Normal 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)
|
||||
|
22
Zend/tests/generators/yield_from_array.phpt
Normal file
22
Zend/tests/generators/yield_from_array.phpt
Normal 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)
|
49
Zend/tests/generators/yield_from_backtrace.phpt
Normal file
49
Zend/tests/generators/yield_from_backtrace.phpt
Normal 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]
|
||||
|
26
Zend/tests/generators/yield_from_deep_recursion.phpt
Normal file
26
Zend/tests/generators/yield_from_deep_recursion.phpt
Normal file
@ -0,0 +1,26 @@
|
||||
--TEST--
|
||||
Deep recursion with yield from
|
||||
--FILE--
|
||||
<?php
|
||||
ini_set("memory_limit", "60G");
|
||||
|
||||
function from($i) {
|
||||
yield $i;
|
||||
}
|
||||
|
||||
function gen($i = 0) {
|
||||
if ($i < 1000) {
|
||||
yield from gen(++$i);
|
||||
} else {
|
||||
yield $i;
|
||||
yield from from(++$i);
|
||||
}
|
||||
}
|
||||
|
||||
foreach (gen() as $v) {
|
||||
var_dump($v);
|
||||
}
|
||||
?>
|
||||
--EXPECT--
|
||||
int(1000)
|
||||
int(1001)
|
332
Zend/tests/generators/yield_from_multi_tree.phpt
Normal file
332
Zend/tests/generators/yield_from_multi_tree.phpt
Normal 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)
|
||||
|
78
Zend/tests/generators/yield_from_multi_tree_exception.phpt
Normal file
78
Zend/tests/generators/yield_from_multi_tree_exception.phpt
Normal 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}
|
||||
|
@ -27,6 +27,7 @@
|
||||
#include "zend_exceptions.h"
|
||||
#include "zend_extensions.h"
|
||||
#include "zend_closures.h"
|
||||
#include "zend_generators.h"
|
||||
|
||||
#undef ZEND_TEST_EXCEPTIONS
|
||||
|
||||
@ -2249,6 +2250,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)) &&
|
||||
@ -2444,6 +2447,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)) &&
|
||||
|
@ -2028,7 +2028,7 @@ static zend_always_inline zend_generator *zend_get_running_generator(zend_execut
|
||||
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->current_generator;
|
||||
return generator;
|
||||
}
|
||||
/* }}} */
|
||||
|
||||
|
@ -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);
|
||||
@ -195,6 +197,10 @@ static void zend_generator_free_storage(zend_object *object) /* {{{ */
|
||||
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) {
|
||||
@ -216,7 +222,10 @@ static zend_object *zend_generator_create(zend_class_entry *class_type) /* {{{ *
|
||||
ZVAL_UNDEF(&generator->retval);
|
||||
ZVAL_UNDEF(&generator->values);
|
||||
|
||||
zend_ptr_stack_init(&generator->generator_stack);
|
||||
/* 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;
|
||||
@ -283,7 +292,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);
|
||||
@ -291,11 +299,11 @@ ZEND_API void zend_generator_create_zval(zend_execute_data *call, zend_op_array
|
||||
EG(vm_stack_end) = current_stack->end;
|
||||
EG(vm_stack) = current_stack;
|
||||
|
||||
/* By default we obviously execute the generator itself. */
|
||||
generator->current_generator = generator;
|
||||
|
||||
/* 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;
|
||||
}
|
||||
/* }}} */
|
||||
|
||||
@ -307,6 +315,261 @@ static zend_function *zend_generator_get_constructor(zend_object *object) /* {{{
|
||||
}
|
||||
/* }}} */
|
||||
|
||||
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;
|
||||
@ -383,29 +646,37 @@ failure:
|
||||
}
|
||||
/* }}} */
|
||||
|
||||
ZEND_API void zend_generator_resume(zend_generator *generator) /* {{{ */
|
||||
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_ERROR, "Cannot resume an already running generator");
|
||||
}
|
||||
|
||||
try_again:
|
||||
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
|
||||
* at the "yield *" expression. */
|
||||
// TODO: Handle exceptions
|
||||
* 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 */
|
||||
@ -424,7 +695,14 @@ try_again:
|
||||
/* 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;
|
||||
@ -435,7 +713,6 @@ try_again:
|
||||
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 */
|
||||
@ -446,13 +723,24 @@ try_again:
|
||||
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 (!Z_ISUNDEF(generator->values)) {
|
||||
/* 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;
|
||||
}
|
||||
}
|
||||
@ -461,8 +749,10 @@ try_again:
|
||||
|
||||
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;
|
||||
}
|
||||
}
|
||||
@ -508,7 +798,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);
|
||||
}
|
||||
/* }}} */
|
||||
|
||||
@ -516,7 +808,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;
|
||||
@ -526,8 +818,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);
|
||||
}
|
||||
}
|
||||
/* }}} */
|
||||
@ -536,7 +829,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;
|
||||
@ -546,8 +839,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);
|
||||
}
|
||||
}
|
||||
/* }}} */
|
||||
@ -575,7 +869,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;
|
||||
@ -590,16 +884,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);
|
||||
}
|
||||
}
|
||||
/* }}} */
|
||||
@ -622,18 +918,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
|
||||
@ -704,28 +997,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);
|
||||
}
|
||||
@ -830,6 +1129,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());
|
||||
}
|
||||
/* }}} */
|
||||
|
||||
|
@ -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;
|
||||
@ -48,33 +69,37 @@ typedef struct _zend_generator {
|
||||
/* Largest used integer key for auto-incrementing keys */
|
||||
zend_long largest_used_integer_key;
|
||||
|
||||
/* Values specified by "yield *" to yield from this generator.
|
||||
/* 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;
|
||||
|
||||
/* Generator that is currently yielding values. This will differ
|
||||
* from the surrounding structure if "yield *" is used on a generator. */
|
||||
struct _zend_generator *current_generator;
|
||||
|
||||
/* Stack of waiting generators when multiple "yield *" expressions
|
||||
/* Node of waiting generators when multiple "yield *" expressions
|
||||
* are nested. */
|
||||
zend_ptr_stack generator_stack;
|
||||
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
|
||||
|
@ -545,6 +545,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;
|
||||
|
@ -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)"
|
||||
@ -872,7 +874,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 '*' expr { $$ = zend_ast_create(ZEND_AST_YIELD_FROM, $3); }
|
||||
| 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
@ -1119,6 +1119,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;
|
||||
}
|
||||
|
@ -6392,13 +6392,27 @@ ZEND_VM_HANDLER(170, ZEND_YIELD_FROM, CONST|TMP|VAR|CV, ANY)
|
||||
if (ce == zend_ce_generator) {
|
||||
zend_generator *new_gen = (zend_generator *) Z_OBJ_P(val);
|
||||
|
||||
zend_ptr_stack_push(&generator->generator_stack, generator->current_generator);
|
||||
generator->current_generator = new_gen;
|
||||
|
||||
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();
|
||||
@ -6425,12 +6439,13 @@ ZEND_VM_HANDLER(170, ZEND_YIELD_FROM, CONST|TMP|VAR|CV, ANY)
|
||||
}
|
||||
} else {
|
||||
// TODO: Should be an engine exception
|
||||
zend_throw_exception(NULL, "Can use \"yield *\" only with arrays and Traversables", 0);
|
||||
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)) {
|
||||
// TODO
|
||||
ZVAL_NULL(EX_VAR(opline->result.var));
|
||||
}
|
||||
|
||||
|
@ -1384,8 +1384,7 @@ static int ZEND_FASTCALL ZEND_HANDLE_EXCEPTION_SPEC_HANDLER(ZEND_OPCODE_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 {
|
||||
@ -1418,8 +1417,7 @@ static int ZEND_FASTCALL ZEND_USER_OPCODE_SPEC_HANDLER(ZEND_OPCODE_HANDLER_ARGS
|
||||
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 {
|
||||
@ -1496,8 +1494,7 @@ static int ZEND_FASTCALL ZEND_FAST_RET_SPEC_HANDLER(ZEND_OPCODE_HANDLER_ARGS)
|
||||
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 {
|
||||
@ -2611,8 +2608,7 @@ static int ZEND_FASTCALL ZEND_GENERATOR_RETURN_SPEC_CONST_HANDLER(ZEND_OPCODE_H
|
||||
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);
|
||||
@ -3507,7 +3503,28 @@ static int ZEND_FASTCALL ZEND_YIELD_FROM_SPEC_CONST_HANDLER(ZEND_OPCODE_HANDLER
|
||||
} 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);
|
||||
|
||||
@ -3533,10 +3550,12 @@ static int ZEND_FASTCALL ZEND_YIELD_FROM_SPEC_CONST_HANDLER(ZEND_OPCODE_HANDLER
|
||||
}
|
||||
} else {
|
||||
// TODO: Should be an engine exception
|
||||
zend_throw_exception(NULL, "Can use \"yield *\" only with arrays and Traversables", 0);
|
||||
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));
|
||||
}
|
||||
@ -5149,8 +5168,7 @@ static int ZEND_FASTCALL ZEND_YIELD_SPEC_CONST_CONST_HANDLER(ZEND_OPCODE_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);
|
||||
|
||||
if (generator->flags & ZEND_GENERATOR_FORCED_CLOSE) {
|
||||
zend_error_noreturn(E_ERROR, "Cannot yield from finally in a force-closed generator");
|
||||
@ -5326,8 +5344,7 @@ static int ZEND_FASTCALL ZEND_YIELD_SPEC_CONST_TMP_HANDLER(ZEND_OPCODE_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);
|
||||
|
||||
if (generator->flags & ZEND_GENERATOR_FORCED_CLOSE) {
|
||||
zend_error_noreturn(E_ERROR, "Cannot yield from finally in a force-closed generator");
|
||||
@ -5834,8 +5851,7 @@ static int ZEND_FASTCALL ZEND_YIELD_SPEC_CONST_VAR_HANDLER(ZEND_OPCODE_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);
|
||||
|
||||
if (generator->flags & ZEND_GENERATOR_FORCED_CLOSE) {
|
||||
zend_error_noreturn(E_ERROR, "Cannot yield from finally in a force-closed generator");
|
||||
@ -6633,8 +6649,7 @@ static int ZEND_FASTCALL ZEND_YIELD_SPEC_CONST_UNUSED_HANDLER(ZEND_OPCODE_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);
|
||||
|
||||
if (generator->flags & ZEND_GENERATOR_FORCED_CLOSE) {
|
||||
zend_error_noreturn(E_ERROR, "Cannot yield from finally in a force-closed generator");
|
||||
@ -7785,8 +7800,7 @@ static int ZEND_FASTCALL ZEND_YIELD_SPEC_CONST_CV_HANDLER(ZEND_OPCODE_HANDLER_A
|
||||
{
|
||||
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);
|
||||
|
||||
if (generator->flags & ZEND_GENERATOR_FORCED_CLOSE) {
|
||||
zend_error_noreturn(E_ERROR, "Cannot yield from finally in a force-closed generator");
|
||||
@ -8985,8 +8999,7 @@ static int ZEND_FASTCALL ZEND_GENERATOR_RETURN_SPEC_TMP_HANDLER(ZEND_OPCODE_HAN
|
||||
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);
|
||||
@ -9589,7 +9602,28 @@ static int ZEND_FASTCALL ZEND_YIELD_FROM_SPEC_TMP_HANDLER(ZEND_OPCODE_HANDLER_A
|
||||
} 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);
|
||||
@ -9616,10 +9650,12 @@ static int ZEND_FASTCALL ZEND_YIELD_FROM_SPEC_TMP_HANDLER(ZEND_OPCODE_HANDLER_A
|
||||
}
|
||||
} else {
|
||||
// TODO: Should be an engine exception
|
||||
zend_throw_exception(NULL, "Can use \"yield *\" only with arrays and Traversables", 0);
|
||||
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));
|
||||
}
|
||||
@ -10007,8 +10043,7 @@ static int ZEND_FASTCALL ZEND_YIELD_SPEC_TMP_CONST_HANDLER(ZEND_OPCODE_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);
|
||||
|
||||
if (generator->flags & ZEND_GENERATOR_FORCED_CLOSE) {
|
||||
zend_error_noreturn(E_ERROR, "Cannot yield from finally in a force-closed generator");
|
||||
@ -10169,8 +10204,7 @@ static int ZEND_FASTCALL ZEND_YIELD_SPEC_TMP_TMP_HANDLER(ZEND_OPCODE_HANDLER_AR
|
||||
{
|
||||
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);
|
||||
|
||||
if (generator->flags & ZEND_GENERATOR_FORCED_CLOSE) {
|
||||
zend_error_noreturn(E_ERROR, "Cannot yield from finally in a force-closed generator");
|
||||
@ -10331,8 +10365,7 @@ static int ZEND_FASTCALL ZEND_YIELD_SPEC_TMP_VAR_HANDLER(ZEND_OPCODE_HANDLER_AR
|
||||
{
|
||||
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);
|
||||
|
||||
if (generator->flags & ZEND_GENERATOR_FORCED_CLOSE) {
|
||||
zend_error_noreturn(E_ERROR, "Cannot yield from finally in a force-closed generator");
|
||||
@ -10635,8 +10668,7 @@ static int ZEND_FASTCALL ZEND_YIELD_SPEC_TMP_UNUSED_HANDLER(ZEND_OPCODE_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);
|
||||
|
||||
if (generator->flags & ZEND_GENERATOR_FORCED_CLOSE) {
|
||||
zend_error_noreturn(E_ERROR, "Cannot yield from finally in a force-closed generator");
|
||||
@ -11089,8 +11121,7 @@ static int ZEND_FASTCALL ZEND_YIELD_SPEC_TMP_CV_HANDLER(ZEND_OPCODE_HANDLER_ARG
|
||||
{
|
||||
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);
|
||||
|
||||
if (generator->flags & ZEND_GENERATOR_FORCED_CLOSE) {
|
||||
zend_error_noreturn(E_ERROR, "Cannot yield from finally in a force-closed generator");
|
||||
@ -11767,8 +11798,7 @@ static int ZEND_FASTCALL ZEND_GENERATOR_RETURN_SPEC_VAR_HANDLER(ZEND_OPCODE_HAN
|
||||
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);
|
||||
@ -12926,7 +12956,29 @@ static int ZEND_FASTCALL ZEND_YIELD_FROM_SPEC_VAR_HANDLER(ZEND_OPCODE_HANDLER_A
|
||||
} 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);
|
||||
@ -12953,10 +13005,12 @@ static int ZEND_FASTCALL ZEND_YIELD_FROM_SPEC_VAR_HANDLER(ZEND_OPCODE_HANDLER_A
|
||||
}
|
||||
} else {
|
||||
// TODO: Should be an engine exception
|
||||
zend_throw_exception(NULL, "Can use \"yield *\" only with arrays and Traversables", 0);
|
||||
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));
|
||||
}
|
||||
@ -14479,8 +14533,7 @@ static int ZEND_FASTCALL ZEND_YIELD_SPEC_VAR_CONST_HANDLER(ZEND_OPCODE_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);
|
||||
|
||||
if (generator->flags & ZEND_GENERATOR_FORCED_CLOSE) {
|
||||
zend_error_noreturn(E_ERROR, "Cannot yield from finally in a force-closed generator");
|
||||
@ -14679,8 +14732,7 @@ static int ZEND_FASTCALL ZEND_YIELD_SPEC_VAR_TMP_HANDLER(ZEND_OPCODE_HANDLER_AR
|
||||
{
|
||||
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);
|
||||
|
||||
if (generator->flags & ZEND_GENERATOR_FORCED_CLOSE) {
|
||||
zend_error_noreturn(E_ERROR, "Cannot yield from finally in a force-closed generator");
|
||||
@ -14930,8 +14982,7 @@ static int ZEND_FASTCALL ZEND_YIELD_SPEC_VAR_VAR_HANDLER(ZEND_OPCODE_HANDLER_AR
|
||||
{
|
||||
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);
|
||||
|
||||
if (generator->flags & ZEND_GENERATOR_FORCED_CLOSE) {
|
||||
zend_error_noreturn(E_ERROR, "Cannot yield from finally in a force-closed generator");
|
||||
@ -15772,8 +15823,7 @@ static int ZEND_FASTCALL ZEND_YIELD_SPEC_VAR_UNUSED_HANDLER(ZEND_OPCODE_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);
|
||||
|
||||
if (generator->flags & ZEND_GENERATOR_FORCED_CLOSE) {
|
||||
zend_error_noreturn(E_ERROR, "Cannot yield from finally in a force-closed generator");
|
||||
@ -17316,8 +17366,7 @@ static int ZEND_FASTCALL ZEND_YIELD_SPEC_VAR_CV_HANDLER(ZEND_OPCODE_HANDLER_ARG
|
||||
{
|
||||
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);
|
||||
|
||||
if (generator->flags & ZEND_GENERATOR_FORCED_CLOSE) {
|
||||
zend_error_noreturn(E_ERROR, "Cannot yield from finally in a force-closed generator");
|
||||
@ -20205,8 +20254,7 @@ static int ZEND_FASTCALL ZEND_YIELD_SPEC_UNUSED_CONST_HANDLER(ZEND_OPCODE_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);
|
||||
|
||||
if (generator->flags & ZEND_GENERATOR_FORCED_CLOSE) {
|
||||
zend_error_noreturn(E_ERROR, "Cannot yield from finally in a force-closed generator");
|
||||
@ -20336,8 +20384,7 @@ static int ZEND_FASTCALL ZEND_YIELD_SPEC_UNUSED_TMP_HANDLER(ZEND_OPCODE_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);
|
||||
|
||||
if (generator->flags & ZEND_GENERATOR_FORCED_CLOSE) {
|
||||
zend_error_noreturn(E_ERROR, "Cannot yield from finally in a force-closed generator");
|
||||
@ -20467,8 +20514,7 @@ static int ZEND_FASTCALL ZEND_YIELD_SPEC_UNUSED_VAR_HANDLER(ZEND_OPCODE_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);
|
||||
|
||||
if (generator->flags & ZEND_GENERATOR_FORCED_CLOSE) {
|
||||
zend_error_noreturn(E_ERROR, "Cannot yield from finally in a force-closed generator");
|
||||
@ -20929,8 +20975,7 @@ static int ZEND_FASTCALL ZEND_YIELD_SPEC_UNUSED_UNUSED_HANDLER(ZEND_OPCODE_HAND
|
||||
{
|
||||
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);
|
||||
|
||||
if (generator->flags & ZEND_GENERATOR_FORCED_CLOSE) {
|
||||
zend_error_noreturn(E_ERROR, "Cannot yield from finally in a force-closed generator");
|
||||
@ -22313,8 +22358,7 @@ static int ZEND_FASTCALL ZEND_YIELD_SPEC_UNUSED_CV_HANDLER(ZEND_OPCODE_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);
|
||||
|
||||
if (generator->flags & ZEND_GENERATOR_FORCED_CLOSE) {
|
||||
zend_error_noreturn(E_ERROR, "Cannot yield from finally in a force-closed generator");
|
||||
@ -24192,8 +24236,7 @@ static int ZEND_FASTCALL ZEND_GENERATOR_RETURN_SPEC_CV_HANDLER(ZEND_OPCODE_HAND
|
||||
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);
|
||||
@ -25176,7 +25219,28 @@ static int ZEND_FASTCALL ZEND_YIELD_FROM_SPEC_CV_HANDLER(ZEND_OPCODE_HANDLER_AR
|
||||
} 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);
|
||||
|
||||
@ -25202,10 +25266,12 @@ static int ZEND_FASTCALL ZEND_YIELD_FROM_SPEC_CV_HANDLER(ZEND_OPCODE_HANDLER_AR
|
||||
}
|
||||
} else {
|
||||
// TODO: Should be an engine exception
|
||||
zend_throw_exception(NULL, "Can use \"yield *\" only with arrays and Traversables", 0);
|
||||
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));
|
||||
}
|
||||
@ -27601,8 +27667,7 @@ static int ZEND_FASTCALL ZEND_YIELD_SPEC_CV_CONST_HANDLER(ZEND_OPCODE_HANDLER_A
|
||||
{
|
||||
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);
|
||||
|
||||
if (generator->flags & ZEND_GENERATOR_FORCED_CLOSE) {
|
||||
zend_error_noreturn(E_ERROR, "Cannot yield from finally in a force-closed generator");
|
||||
@ -27871,8 +27936,7 @@ static int ZEND_FASTCALL ZEND_YIELD_SPEC_CV_TMP_HANDLER(ZEND_OPCODE_HANDLER_ARG
|
||||
{
|
||||
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);
|
||||
|
||||
if (generator->flags & ZEND_GENERATOR_FORCED_CLOSE) {
|
||||
zend_error_noreturn(E_ERROR, "Cannot yield from finally in a force-closed generator");
|
||||
@ -28509,8 +28573,7 @@ static int ZEND_FASTCALL ZEND_YIELD_SPEC_CV_VAR_HANDLER(ZEND_OPCODE_HANDLER_ARG
|
||||
{
|
||||
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);
|
||||
|
||||
if (generator->flags & ZEND_GENERATOR_FORCED_CLOSE) {
|
||||
zend_error_noreturn(E_ERROR, "Cannot yield from finally in a force-closed generator");
|
||||
@ -29554,8 +29617,7 @@ static int ZEND_FASTCALL ZEND_YIELD_SPEC_CV_UNUSED_HANDLER(ZEND_OPCODE_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);
|
||||
|
||||
if (generator->flags & ZEND_GENERATOR_FORCED_CLOSE) {
|
||||
zend_error_noreturn(E_ERROR, "Cannot yield from finally in a force-closed generator");
|
||||
@ -31597,8 +31659,7 @@ static int ZEND_FASTCALL ZEND_YIELD_SPEC_CV_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);
|
||||
zend_generator *generator = zend_get_running_generator(execute_data);
|
||||
|
||||
if (generator->flags & ZEND_GENERATOR_FORCED_CLOSE) {
|
||||
zend_error_noreturn(E_ERROR, "Cannot yield from finally in a force-closed generator");
|
||||
|
Loading…
Reference in New Issue
Block a user