Commit:    0033a525213e68e6ae74b3c3d75fd6ea4beea5d6
Author:    Nikita Popov <ni...@php.net>         Wed, 30 May 2012 16:28:33 +0200
Parents:   879016023566ce162be1d81e973bde9db76dd519
Branches:  master

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

Log:
Allow throwing exceptions from generators

The missing piece is how one can find the next stack frame, which is
required for dtor'ing arguments pushed to the stack. As the generator
execute_data does not live on the stack one can't use it to figure out the
start of the next stack frame. So there must be some other method.

Changed paths:
  A  Zend/tests/generators/generator_throwing_exception.phpt
  M  Zend/zend_vm_def.h
  M  Zend/zend_vm_execute.h

diff --git a/Zend/tests/generators/generator_throwing_exception.phpt 
b/Zend/tests/generators/generator_throwing_exception.phpt
new file mode 100644
index 0000000..5df4a81
--- /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 f1fd64c..cbdf701 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 09c3461..039504b 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();
        }
-- 
PHP CVS Mailing List (http://www.php.net/)
To unsubscribe, visit: http://www.php.net/unsub.php

Reply via email to