Commit:    94b2ccae9ce95c4c71bb8db8ce75dcdf26df7d7a
Author:    Nikita Popov <ni...@php.net>         Sun, 22 Jul 2012 17:46:46 +0200
Parents:   de80e3ce4b5b7a9ec0cfdd0778e77027a7ebfcc2
Branches:  master

Link:       
http://git.php.net/?p=php-src.git;a=commitdiff;h=94b2ccae9ce95c4c71bb8db8ce75dcdf26df7d7a

Log:
Fix throwing of exceptions within a generator

If a generator threw an exception and was iterated using foreach (i.e. not
manually) an infinite loop was triggered. The reason was that the exception
was not properly rethrown using zend_throw_exception_internal.

Changed paths:
  M  Zend/tests/generators/backtrace.phpt
  A  Zend/tests/generators/generator_throwing_in_foreach.phpt
  M  Zend/zend_generators.c
  M  Zend/zend_vm_def.h
  M  Zend/zend_vm_execute.h


Diff:
diff --git a/Zend/tests/generators/backtrace.phpt 
b/Zend/tests/generators/backtrace.phpt
index 5f665b7..5fed1d4 100644
--- a/Zend/tests/generators/backtrace.phpt
+++ b/Zend/tests/generators/backtrace.phpt
@@ -22,6 +22,6 @@ f3($gen);
 ?>
 --EXPECTF--
 #0  f1() called at [%s:%d]
-#1  f2(foo, bar) called at [%s:%d]
+#1  f2(foo, bar)
 #2  Generator->rewind() called at [%s:%d]
 #3  f3(Generator Object ()) called at [%s:%d]
diff --git a/Zend/tests/generators/generator_throwing_in_foreach.phpt 
b/Zend/tests/generators/generator_throwing_in_foreach.phpt
new file mode 100644
index 0000000..dbf20c2
--- /dev/null
+++ b/Zend/tests/generators/generator_throwing_in_foreach.phpt
@@ -0,0 +1,20 @@
+--TEST--
+Exceptions throwing by generators during foreach iteration are properly handled
+--FILE--
+<?php
+
+function gen() {
+    throw new Exception("foo");
+    yield; // force generator
+}
+
+foreach (gen() as $value) { }
+
+?>
+--EXPECTF--
+Fatal error: Uncaught exception 'Exception' with message 'foo' in %s:%d
+Stack trace:
+#0 %s(%d): gen()
+#1 {main}
+  thrown in %s on line %d
+
diff --git a/Zend/zend_generators.c b/Zend/zend_generators.c
index b164fb8..d7ffb30 100644
--- a/Zend/zend_generators.c
+++ b/Zend/zend_generators.c
@@ -21,6 +21,7 @@
 #include "zend.h"
 #include "zend_API.h"
 #include "zend_interfaces.h"
+#include "zend_exceptions.h"
 #include "zend_generators.h"
 
 ZEND_API zend_class_entry *zend_ce_generator;
@@ -352,12 +353,11 @@ zval *zend_generator_create_zval(zend_op_array *op_array 
TSRMLS_DC) /* {{{ */
         * up in backtraces and func_get_all() can access the function
         * arguments. */
        execute_data->prev_execute_data = emalloc(sizeof(zend_execute_data));
+       memset(execute_data->prev_execute_data, 0, sizeof(zend_execute_data));
+       execute_data->prev_execute_data->function_state.function = 
(zend_function *) op_array;
        if (EG(current_execute_data)) {
-               memcpy(execute_data->prev_execute_data, 
EG(current_execute_data), sizeof(zend_execute_data));
                execute_data->prev_execute_data->function_state.arguments = 
zend_copy_arguments(EG(current_execute_data)->function_state.arguments);
        } else {
-               memset(execute_data->prev_execute_data, 0, 
sizeof(zend_execute_data));
-               execute_data->prev_execute_data->function_state.function = 
(zend_function *) op_array;
                execute_data->prev_execute_data->function_state.arguments = 
NULL;
        }
 
@@ -450,6 +450,12 @@ static void zend_generator_resume(zend_generator 
*generator TSRMLS_DC) /* {{{ */
                        memcpy(generator->backed_up_stack, 
generator->original_stack_top, generator->backed_up_stack_size);
                        zend_vm_stack_free(generator->original_stack_top 
TSRMLS_CC);
                }
+
+               /* If an exception was thrown in the generator we have to 
internally
+                * rethrow it in the parent scope. */
+               if (UNEXPECTED(EG(exception) != NULL)) {
+                       zend_throw_exception_internal(NULL TSRMLS_CC);
+               }
        }
 }
 /* }}} */
diff --git a/Zend/zend_vm_def.h b/Zend/zend_vm_def.h
index 6b84255..03768e8 100644
--- a/Zend/zend_vm_def.h
+++ b/Zend/zend_vm_def.h
@@ -5088,7 +5088,7 @@ ZEND_VM_HANDLER(149, ZEND_HANDLE_EXCEPTION, ANY, ANY)
        EX(old_error_reporting) = NULL;
 
        if (!catched) {
-               /* For generators skip the leave handler return directly */
+               /* For generators skip the leave handler and return directly */
                if (EX(op_array)->fn_flags & ZEND_ACC_GENERATOR) {
                        /* The generator object is stored in 
return_value_ptr_ptr */
                        zend_generator *generator = (zend_generator *) 
EG(return_value_ptr_ptr);
diff --git a/Zend/zend_vm_execute.h b/Zend/zend_vm_execute.h
index 9a9917f..5ded3e3 100644
--- a/Zend/zend_vm_execute.h
+++ b/Zend/zend_vm_execute.h
@@ -1151,7 +1151,7 @@ static int ZEND_FASTCALL  
ZEND_HANDLE_EXCEPTION_SPEC_HANDLER(ZEND_OPCODE_HANDLER
        EX(old_error_reporting) = NULL;
 
        if (!catched) {
-               /* For generators skip the leave handler return directly */
+               /* For generators skip the leave handler and return directly */
                if (EX(op_array)->fn_flags & ZEND_ACC_GENERATOR) {
                        /* The generator object is stored in 
return_value_ptr_ptr */
                        zend_generator *generator = (zend_generator *) 
EG(return_value_ptr_ptr);


--
PHP CVS Mailing List (http://www.php.net/)
To unsubscribe, visit: http://www.php.net/unsub.php

Reply via email to