summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--Zend/tests/generators/generator_throwing_exception.phpt28
-rw-r--r--Zend/zend_vm_def.h38
-rw-r--r--Zend/zend_vm_execute.h50
3 files changed, 100 insertions, 16 deletions
diff --git a/Zend/tests/generators/generator_throwing_exception.phpt b/Zend/tests/generators/generator_throwing_exception.phpt
new file mode 100644
index 0000000000..5df4a81132
--- /dev/null
+++ b/Zend/tests/generators/generator_throwing_exception.phpt
@@ -0,0 +1,28 @@
+--TEST--
+Generators can throw exceptions
+--FILE--
+<?php
+
+function *gen() {
+ yield 'foo';
+ throw new Exception('test');
+ yield 'bar';
+}
+
+$gen = gen();
+
+var_dump($gen->current());
+
+try {
+ $gen->next();
+} catch (Exception $e) {
+ echo 'Caught exception with message "', $e->getMessage(), '"', "\n";
+}
+
+var_dump($gen->current());
+
+?>
+--EXPECT--
+string(3) "foo"
+Caught exception with message "test"
+NULL
diff --git a/Zend/zend_vm_def.h b/Zend/zend_vm_def.h
index f1fd64cf81..cbdf701adc 100644
--- a/Zend/zend_vm_def.h
+++ b/Zend/zend_vm_def.h
@@ -2821,10 +2821,10 @@ ZEND_VM_HANDLER(62, ZEND_RETURN, CONST|TMP|VAR|CV, ANY)
/* The generator object is stored in return_value_ptr_ptr */
zend_generator *generator = (zend_generator *) zend_object_store_get_object(*EG(return_value_ptr_ptr) TSRMLS_CC);
- /* Close the generator to free up resources. */
+ /* Close the generator to free up resources */
zend_generator_close(generator, 1 TSRMLS_CC);
- /* Pass execution back to generator handling code */
+ /* Pass execution back to handling code */
ZEND_VM_RETURN();
}
@@ -4992,11 +4992,25 @@ ZEND_VM_HANDLER(149, ZEND_HANDLE_EXCEPTION, ANY, ANY)
int i;
zend_uint catch_op_num = 0;
int catched = 0;
- zval restored_error_reporting;
+ void **stack_frame;
- void **stack_frame = (void**)(((char*)EX_Ts()) +
- (ZEND_MM_ALIGNED_SIZE(sizeof(temp_variable)) * EX(op_array)->T));
+ /* Figure out where the next stack frame (which maybe contains pushed
+ * arguments that have to be dtor'ed) starts */
+ if (EX(op_array)->fn_flags & ZEND_ACC_GENERATOR) {
+ /* For generators the execution context is not stored on the stack so
+ * I don't know yet how to figure out where the next stack frame
+ * starts. For now I'll just use the stack top to ignore argument
+ * dtor'ing altogether. */
+ stack_frame = zend_vm_stack_top(TSRMLS_C);
+ } else {
+ /* In all other cases the next stack frame starts after the temporary
+ * variables section of the current execution context */
+ stack_frame = (void **) ((char *) EX_Ts() +
+ ZEND_MM_ALIGNED_SIZE(sizeof(temp_variable)) * EX(op_array)->T);
+ }
+ /* If the exception was thrown during a function call there might be
+ * arguments pushed to the stack that have to be dtor'ed. */
while (zend_vm_stack_top(TSRMLS_C) != stack_frame) {
zval *stack_zval_p = zend_vm_stack_pop(TSRMLS_C);
zval_ptr_dtor(&stack_zval_p);
@@ -5058,6 +5072,8 @@ ZEND_VM_HANDLER(149, ZEND_HANDLE_EXCEPTION, ANY, ANY)
/* restore previous error_reporting value */
if (!EG(error_reporting) && EX(old_error_reporting) != NULL && Z_LVAL_P(EX(old_error_reporting)) != 0) {
+ zval restored_error_reporting;
+
Z_TYPE(restored_error_reporting) = IS_LONG;
Z_LVAL(restored_error_reporting) = Z_LVAL_P(EX(old_error_reporting));
convert_to_string(&restored_error_reporting);
@@ -5067,6 +5083,18 @@ ZEND_VM_HANDLER(149, ZEND_HANDLE_EXCEPTION, ANY, ANY)
EX(old_error_reporting) = NULL;
if (!catched) {
+ /* For generators skip the leave handler 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 *) zend_object_store_get_object(*EG(return_value_ptr_ptr) TSRMLS_CC);
+
+ /* Close the generator to free up resources */
+ zend_generator_close(generator, 1 TSRMLS_CC);
+
+ /* Pass execution back to handling code */
+ ZEND_VM_RETURN();
+ }
+
ZEND_VM_DISPATCH_TO_HELPER(zend_leave_helper);
} else {
ZEND_VM_SET_OPCODE(&EX(op_array)->opcodes[catch_op_num]);
diff --git a/Zend/zend_vm_execute.h b/Zend/zend_vm_execute.h
index 09c3461b7a..039504be45 100644
--- a/Zend/zend_vm_execute.h
+++ b/Zend/zend_vm_execute.h
@@ -1055,11 +1055,25 @@ static int ZEND_FASTCALL ZEND_HANDLE_EXCEPTION_SPEC_HANDLER(ZEND_OPCODE_HANDLER
int i;
zend_uint catch_op_num = 0;
int catched = 0;
- zval restored_error_reporting;
+ void **stack_frame;
- void **stack_frame = (void**)(((char*)EX_Ts()) +
- (ZEND_MM_ALIGNED_SIZE(sizeof(temp_variable)) * EX(op_array)->T));
+ /* Figure out where the next stack frame (which maybe contains pushed
+ * arguments that have to be dtor'ed) starts */
+ if (EX(op_array)->fn_flags & ZEND_ACC_GENERATOR) {
+ /* For generators the execution context is not stored on the stack so
+ * I don't know yet how to figure out where the next stack frame
+ * starts. For now I'll just use the stack top to ignore argument
+ * dtor'ing altogether. */
+ stack_frame = zend_vm_stack_top(TSRMLS_C);
+ } else {
+ /* In all other cases the next stack frame starts after the temporary
+ * variables section of the current execution context */
+ stack_frame = (void **) ((char *) EX_Ts() +
+ ZEND_MM_ALIGNED_SIZE(sizeof(temp_variable)) * EX(op_array)->T);
+ }
+ /* If the exception was thrown during a function call there might be
+ * arguments pushed to the stack that have to be dtor'ed. */
while (zend_vm_stack_top(TSRMLS_C) != stack_frame) {
zval *stack_zval_p = zend_vm_stack_pop(TSRMLS_C);
zval_ptr_dtor(&stack_zval_p);
@@ -1121,6 +1135,8 @@ static int ZEND_FASTCALL ZEND_HANDLE_EXCEPTION_SPEC_HANDLER(ZEND_OPCODE_HANDLER
/* restore previous error_reporting value */
if (!EG(error_reporting) && EX(old_error_reporting) != NULL && Z_LVAL_P(EX(old_error_reporting)) != 0) {
+ zval restored_error_reporting;
+
Z_TYPE(restored_error_reporting) = IS_LONG;
Z_LVAL(restored_error_reporting) = Z_LVAL_P(EX(old_error_reporting));
convert_to_string(&restored_error_reporting);
@@ -1130,6 +1146,18 @@ 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 */
+ if (EX(op_array)->fn_flags & ZEND_ACC_GENERATOR) {
+ /* The generator object is stored in return_value_ptr_ptr */
+ zend_generator *generator = (zend_generator *) zend_object_store_get_object(*EG(return_value_ptr_ptr) TSRMLS_CC);
+
+ /* Close the generator to free up resources */
+ zend_generator_close(generator, 1 TSRMLS_CC);
+
+ /* Pass execution back to handling code */
+ ZEND_VM_RETURN();
+ }
+
return zend_leave_helper_SPEC(ZEND_OPCODE_HANDLER_ARGS_PASSTHRU);
} else {
ZEND_VM_SET_OPCODE(&EX(op_array)->opcodes[catch_op_num]);
@@ -2318,10 +2346,10 @@ static int ZEND_FASTCALL ZEND_RETURN_SPEC_CONST_HANDLER(ZEND_OPCODE_HANDLER_ARG
/* The generator object is stored in return_value_ptr_ptr */
zend_generator *generator = (zend_generator *) zend_object_store_get_object(*EG(return_value_ptr_ptr) TSRMLS_CC);
- /* Close the generator to free up resources. */
+ /* Close the generator to free up resources */
zend_generator_close(generator, 1 TSRMLS_CC);
- /* Pass execution back to generator handling code */
+ /* Pass execution back to handling code */
ZEND_VM_RETURN();
}
@@ -7368,10 +7396,10 @@ static int ZEND_FASTCALL ZEND_RETURN_SPEC_TMP_HANDLER(ZEND_OPCODE_HANDLER_ARGS)
/* The generator object is stored in return_value_ptr_ptr */
zend_generator *generator = (zend_generator *) zend_object_store_get_object(*EG(return_value_ptr_ptr) TSRMLS_CC);
- /* Close the generator to free up resources. */
+ /* Close the generator to free up resources */
zend_generator_close(generator, 1 TSRMLS_CC);
- /* Pass execution back to generator handling code */
+ /* Pass execution back to handling code */
ZEND_VM_RETURN();
}
@@ -12323,10 +12351,10 @@ static int ZEND_FASTCALL ZEND_RETURN_SPEC_VAR_HANDLER(ZEND_OPCODE_HANDLER_ARGS)
/* The generator object is stored in return_value_ptr_ptr */
zend_generator *generator = (zend_generator *) zend_object_store_get_object(*EG(return_value_ptr_ptr) TSRMLS_CC);
- /* Close the generator to free up resources. */
+ /* Close the generator to free up resources */
zend_generator_close(generator, 1 TSRMLS_CC);
- /* Pass execution back to generator handling code */
+ /* Pass execution back to handling code */
ZEND_VM_RETURN();
}
@@ -29344,10 +29372,10 @@ static int ZEND_FASTCALL ZEND_RETURN_SPEC_CV_HANDLER(ZEND_OPCODE_HANDLER_ARGS)
/* The generator object is stored in return_value_ptr_ptr */
zend_generator *generator = (zend_generator *) zend_object_store_get_object(*EG(return_value_ptr_ptr) TSRMLS_CC);
- /* Close the generator to free up resources. */
+ /* Close the generator to free up resources */
zend_generator_close(generator, 1 TSRMLS_CC);
- /* Pass execution back to generator handling code */
+ /* Pass execution back to handling code */
ZEND_VM_RETURN();
}