mirror of
https://github.com/php/php-src.git
synced 2024-09-21 18:07:23 +00:00
Merge branch 'coroutineDelegation' of https://github.com/bwoebi/php-src
This commit is contained in:
commit
f3e124d58d
@ -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", "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)
|
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}
|
||||
|
@ -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,
|
||||
|
@ -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)) &&
|
||||
|
@ -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;
|
||||
|
@ -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"
|
||||
|
@ -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());
|
||||
}
|
||||
/* }}} */
|
||||
|
||||
|
@ -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
|
||||
|
@ -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;
|
||||
|
@ -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
@ -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;
|
||||
}
|
||||
|
@ -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 {
|
||||
|
@ -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,
|
||||
|
@ -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",
|
||||
|
@ -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
|
||||
|
Loading…
Reference in New Issue
Block a user