Fix bug #65764
authorNikita Popov <nikic@php.net>
Sun, 1 Dec 2013 12:37:56 +0000 (13:37 +0100)
committerNikita Popov <nikic@php.net>
Sun, 1 Dec 2013 12:37:56 +0000 (13:37 +0100)
I'm not exactly sure whether this is the right way to fix it. The
question is whether Generator::throw() on a newborn generator (i.e.
a generator that is not yet at yield expression) should first advance to
the first yield and throw the exception there or whether it should
instead throw the exception in the caller's context.

The old behavior was to throw it at the start of the function (i.e.
the very first opcode), which causes issues like the one in #65764.
Effectively it's impossible to properly handle the exceptions in this
case.

For now I choose the variant where the generator advances to the
first yield before throwing, as that's consistent with how all other
methods on the Generator object currently behave. This does not
necessarily match the behavior in other languages, e.g. Python would throw
the exception in the caller's context. But then our send() method already
has this kind of deviation, so it stays internally consistent at least.

NEWS
Zend/tests/generators/throw_caught.phpt
Zend/tests/generators/throw_rethrow.phpt
Zend/zend_generators.c

diff --git a/NEWS b/NEWS
index 6afb736c6d97f2918c3ae9e98ad5e040a720808a..f4b3bf8c0c1b947d0b251f7dbf71cc785adc5bbd 100644 (file)
--- a/NEWS
+++ b/NEWS
@@ -4,8 +4,10 @@ PHP                                                                        NEWS
 
 - Core:
   . Added validation of class names in the autoload process. (Dmitry)
-  . Fixed buf #66041 (list() fails to unpack yielded ArrayAccess object).
+  . Fixed bug #66041 (list() fails to unpack yielded ArrayAccess object).
     (Nikita)
+  . Fixed bug #65764 (generators/throw_rethrow FAIL with
+    ZEND_COMPILE_EXTENDED_INFO). (Nikita)
 
 - Date:
   . Fixed bug #66060 (Heap buffer over-read in DateInterval). (Remi)
index 0c3f8e9b2daaefbbe3141ddbbe53c5699d762a74..c5e9d81ebcc8070bed26107053fe4cebeebc4dce 100644 (file)
@@ -4,6 +4,7 @@ Generator::throw() where the exception is caught in the generator
 <?php
 
 function gen() {
+    echo "before yield\n";
     try {
         yield;
     } catch (RuntimeException $e) {
@@ -18,6 +19,7 @@ var_dump($gen->throw(new RuntimeException('Test')));
 
 ?>
 --EXPECTF--
+before yield
 exception 'RuntimeException' with message 'Test' in %s:%d
 Stack trace:
 #0 {main}
index 267f5f0db86ece544abd70066ced2df81aae598f..65044ee3f304962d6ddd8cfd0f5c9084ba58b4b2 100644 (file)
@@ -4,6 +4,7 @@ Generator::throw() where the generator throws a different exception
 <?php
 
 function gen() {
+    echo "before yield\n";
     try {
         yield;
     } catch (RuntimeException $e) {
@@ -18,6 +19,7 @@ var_dump($gen->throw(new RuntimeException('throw')));
 
 ?>
 --EXPECTF--
+before yield
 Caught: exception 'RuntimeException' with message 'throw' in %s:%d
 Stack trace:
 #0 {main}
index bc3d6d9a1866797ed1de77c36d506e9968bc6033..ec9e9b89fbe4d707c3da35c99a570db6507607bb 100644 (file)
@@ -560,6 +560,8 @@ ZEND_METHOD(Generator, throw)
 
        generator = (zend_generator *) zend_object_store_get_object(getThis() TSRMLS_CC);
 
+       zend_generator_ensure_initialized(generator TSRMLS_CC); 
+
        if (generator->execute_data) {
                /* Throw the exception in the context of the generator */
                zend_execute_data *current_execute_data = EG(current_execute_data);