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