Commit:    f169b26dd7a4047996ab1284e649fda0cfb1a10b
Author:    Nikita Popov <ni...@php.net>         Sat, 9 Jun 2012 00:40:47 +0200
Parents:   40760ecb90d1b024c76862e33d13d40137650af7
Branches:  master

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

Log:
Fix backtraces and func_get_args()

To make the generator function show up in backtraces one has to insert an
additional execute_data into the chain, as prev_execute_data->function_state
is used to determine the called function.

Adding the additional stack frame is also required for func_get_args(), as
the arguments are fetched from there too. The arguments have to be copied
in order to keep them around. Due to the way they are saved doing so is
quite ugly, so I added another function zend_copy_arguments to zend_execute.c
which handles this.

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

diff --git a/Zend/tests/generators/backtrace.phpt 
b/Zend/tests/generators/backtrace.phpt
index cbf8de1..77976f9 100644
--- a/Zend/tests/generators/backtrace.phpt
+++ b/Zend/tests/generators/backtrace.phpt
@@ -7,7 +7,7 @@ function f1() {
     debug_print_backtrace();
 }
 
-function *f2() {
+function *f2($arg1, $arg2) {
     f1();
 }
 
@@ -15,11 +15,12 @@ function f3($gen) {
     $gen->rewind(); // trigger run
 }
 
-$gen = f2();
+$gen = f2('foo', 'bar');
 f3($gen);
 
 ?>
 --EXPECTF--
 #0  f1() called at [%s:%d]
-#1  Generator->rewind() called at [%s:%d]
-#2  f3(Generator Object ()) called at [%s:%d]
+#1  f2(foo, bar) called at [%s:%d]
+#2  Generator->rewind() called at [%s:%d]
+#3  f3(Generator Object ()) called at [%s:%d]
diff --git a/Zend/zend_execute.c b/Zend/zend_execute.c
index 9031fb5..c7ef212 100644
--- a/Zend/zend_execute.c
+++ b/Zend/zend_execute.c
@@ -1563,6 +1563,25 @@ void zend_free_compiled_variables(zval ***CVs, int num) 
/* {{{ */
 }
 /* }}} */
 
+void** zend_copy_arguments(void **arguments_end) /* {{{ */
+{
+       int arguments_count = (int) (zend_uintptr_t) *arguments_end;
+       size_t arguments_size = (arguments_count + 1) * sizeof(void **);
+       void **arguments_start = arguments_end - arguments_count;
+       void **copied_arguments_start = emalloc(arguments_size);
+       void **copied_arguments_end = copied_arguments_start + arguments_count;
+       int i;
+
+       memcpy(copied_arguments_start, arguments_start, arguments_size);
+
+       for (i = 0; i < arguments_count; i++) {
+               Z_ADDREF_P((zval *) arguments_start[i]);
+       }
+
+       return copied_arguments_end;
+}
+/* }}} */
+
 /*
  * Local variables:
  * tab-width: 4
diff --git a/Zend/zend_execute.h b/Zend/zend_execute.h
index 6dfd607..48f46bb 100644
--- a/Zend/zend_execute.h
+++ b/Zend/zend_execute.h
@@ -434,6 +434,7 @@ ZEND_API int zend_do_fcall(ZEND_OPCODE_HANDLER_ARGS);
 
 void zend_clean_and_cache_symbol_table(HashTable *symbol_table);
 void zend_free_compiled_variables(zval ***CVs, int num);
+void **zend_copy_arguments(void **arguments_end);
 
 #define CACHED_PTR(num) \
        EG(active_op_array)->run_time_cache[(num)]
diff --git a/Zend/zend_generators.c b/Zend/zend_generators.c
index b5642dd..6efa710 100644
--- a/Zend/zend_generators.c
+++ b/Zend/zend_generators.c
@@ -94,6 +94,28 @@ void zend_generator_close(zend_generator *generator, 
zend_bool finished_executio
                        efree(generator->backed_up_stack);
                }
 
+               /* We have added an additional stack frame in 
prev_execute_data, so we
+                * have to free it. It also contains the arguments passed to the
+                * generator (for func_get_args) so those have to be freed too. 
*/
+               {
+                       zend_execute_data *prev_execute_data = 
execute_data->prev_execute_data;
+                       void **arguments = 
prev_execute_data->function_state.arguments;
+
+                       if (arguments) {
+                               int arguments_count = (int) (zend_uintptr_t) 
*arguments;
+                               zval **arguments_start = (zval **) (arguments - 
arguments_count);
+                               int i;
+
+                               for (i = 0; i < arguments_count; ++i) {
+                                       zval_ptr_dtor(arguments_start + i);
+                               }
+
+                               efree(arguments_start);
+                       }
+
+                       efree(prev_execute_data);
+               }
+
                efree(execute_data);
                generator->execute_data = NULL;
        }
@@ -240,6 +262,18 @@ static void zend_generator_clone_storage(zend_generator 
*orig, zend_generator **
                if (execute_data->object) {
                        Z_ADDREF_P(execute_data->object);
                }
+
+               /* Prev execute data contains an additional stack frame (for 
proper)
+                * backtraces) which has to be copied. */
+               clone->execute_data->prev_execute_data = 
emalloc(sizeof(zend_execute_data));
+               memcpy(clone->execute_data->prev_execute_data, 
execute_data->prev_execute_data, sizeof(zend_execute_data));
+
+               /* It also contains the arguments passed to the generator, 
which also
+                * have to be copied */
+               if (execute_data->prev_execute_data->function_state.arguments) {
+                       
clone->execute_data->prev_execute_data->function_state.arguments
+                               = 
zend_copy_arguments(execute_data->prev_execute_data->function_state.arguments);
+               }
        }
 
        /* The value and key are known not to be references, so simply add refs 
*/
@@ -329,9 +363,13 @@ static void zend_generator_resume(zval *object, 
zend_generator *generator TSRMLS
                EG(scope) = generator->execute_data->current_scope;
                EG(called_scope) = 
generator->execute_data->current_called_scope;
 
-               /* Set prev_execute_data to the current execute_data to get 
halfways
-                * reasonable backtraces */
-               generator->execute_data->prev_execute_data = 
original_execute_data;
+               /* We want the backtrace to look as if the generator function 
was
+                * called from whatever method we are current running (e.g. 
next()).
+                * The first prev_execute_data contains an additional stack 
frame,
+                * which makes the generator function show up in the backtrace 
and
+                * makes the arguments available to func_get_args(). So we have 
to
+                * set the prev_execute_data of that prev_execute_data :) */
+               generator->execute_data->prev_execute_data->prev_execute_data = 
original_execute_data;
 
                /* Go to next opcode (we don't want to run the last one again) 
*/
                generator->execute_data->opline++;
diff --git a/Zend/zend_vm_def.h b/Zend/zend_vm_def.h
index de63d8b..27afc49 100644
--- a/Zend/zend_vm_def.h
+++ b/Zend/zend_vm_def.h
@@ -5220,7 +5220,8 @@ ZEND_VM_HANDLER(156, ZEND_SEPARATE, VAR, UNUSED)
 
 ZEND_VM_HANDLER(159, ZEND_SUSPEND_AND_RETURN_GENERATOR, ANY, ANY)
 {
-       zend_bool nested;
+       zend_bool nested = EX(nested);
+       zend_execute_data *prev_execute_data = EX(prev_execute_data);
 
        if (EG(return_value_ptr_ptr)) {
                zval *return_value;
@@ -5240,11 +5241,23 @@ ZEND_VM_HANDLER(159, ZEND_SUSPEND_AND_RETURN_GENERATOR, 
ANY, ANY)
                /* back up the execution context */
                generator = (zend_generator *) 
zend_object_store_get_object(return_value TSRMLS_CC);
                generator->execute_data = execute_data;
+
+               /* We have to add another stack frame so the generator function 
shows
+                * up in backtraces and func_get_all() can access the function
+                * arguments. */
+               EX(prev_execute_data) = emalloc(sizeof(zend_execute_data));
+               if (prev_execute_data) {
+                       memcpy(EX(prev_execute_data), prev_execute_data, 
sizeof(zend_execute_data));
+                       EX(prev_execute_data)->function_state.arguments = 
zend_copy_arguments(prev_execute_data->function_state.arguments);
+               } else {
+                       memset(EX(prev_execute_data), 0, 
sizeof(zend_execute_data));
+                       EX(prev_execute_data)->function_state.function = 
(zend_function *) EX(op_array);
+                       EX(prev_execute_data)->function_state.arguments = NULL;
+               }
        }
 
        /* restore the previous execution context */
-       EG(current_execute_data) = EX(prev_execute_data);
-       nested = EX(nested);
+       EG(current_execute_data) = prev_execute_data;
 
        /* if there is no return value pointer we are responsible for freeing 
the
      * execution data */
@@ -5257,34 +5270,37 @@ ZEND_VM_HANDLER(159, ZEND_SUSPEND_AND_RETURN_GENERATOR, 
ANY, ANY)
                efree(execute_data);
        }
 
-       EG(opline_ptr) = NULL;
-       if (nested) {
-               /* so we can use EX() again */
-               execute_data = EG(current_execute_data);
 
-               EG(opline_ptr)           = &EX(opline);
-               EG(active_op_array)      = EX(op_array);
-               EG(return_value_ptr_ptr) = EX(original_return_value);
-               EG(active_symbol_table)  = EX(symbol_table);
-               EG(This)                 = EX(current_this);
-               EG(scope)                = EX(current_scope);
-               EG(called_scope)         = EX(current_called_scope);
-               
-               EX(function_state).function  = (zend_function *) EX(op_array);
-               EX(function_state).arguments = NULL;
+       /* Happens whenever the function is invoked using call_user_function,
+        * e.g. when doing a dynamic function call using call_user_func(). */
+       if (!nested) {
+               EG(opline_ptr) = NULL;
+               ZEND_VM_RETURN();
+       }
 
-               EX(object) = EX(current_object);
-               EX(called_scope) = DECODE_CTOR(EX(called_scope));
+       /* Bring back the previous execution context */
+       execute_data = EG(current_execute_data);
 
-               zend_vm_stack_clear_multiple(TSRMLS_C);
+       EG(opline_ptr)           = &EX(opline);
+       EG(active_op_array)      = EX(op_array);
+       EG(return_value_ptr_ptr) = EX(original_return_value);
+       EG(active_symbol_table)  = EX(symbol_table);
+       EG(This)                 = EX(current_this);
+       EG(scope)                = EX(current_scope);
+       EG(called_scope)         = EX(current_called_scope);
+               
+       EX(function_state).function  = (zend_function *) EX(op_array);
+       EX(function_state).arguments = NULL;
 
-               LOAD_REGS();
-               LOAD_OPLINE();
-               ZEND_VM_INC_OPCODE();
-               ZEND_VM_LEAVE();
-       }
+       EX(object) = EX(current_object);
+       EX(called_scope) = DECODE_CTOR(EX(called_scope));
 
-       ZEND_VM_RETURN();
+       zend_vm_stack_clear_multiple(TSRMLS_C);
+
+       LOAD_REGS();
+       LOAD_OPLINE();
+       ZEND_VM_INC_OPCODE();
+       ZEND_VM_LEAVE();
 }
 
 ZEND_VM_HANDLER(160, ZEND_YIELD, CONST|TMP|VAR|CV|UNUSED, 
CONST|TMP|VAR|CV|UNUSED)
diff --git a/Zend/zend_vm_execute.h b/Zend/zend_vm_execute.h
index 98d849b..987e4ce 100644
--- a/Zend/zend_vm_execute.h
+++ b/Zend/zend_vm_execute.h
@@ -1203,7 +1203,8 @@ static int ZEND_FASTCALL  
ZEND_USER_OPCODE_SPEC_HANDLER(ZEND_OPCODE_HANDLER_ARGS
 
 static int ZEND_FASTCALL  
ZEND_SUSPEND_AND_RETURN_GENERATOR_SPEC_HANDLER(ZEND_OPCODE_HANDLER_ARGS)
 {
-       zend_bool nested;
+       zend_bool nested = EX(nested);
+       zend_execute_data *prev_execute_data = EX(prev_execute_data);
 
        if (EG(return_value_ptr_ptr)) {
                zval *return_value;
@@ -1223,11 +1224,23 @@ static int ZEND_FASTCALL  
ZEND_SUSPEND_AND_RETURN_GENERATOR_SPEC_HANDLER(ZEND_OP
                /* back up the execution context */
                generator = (zend_generator *) 
zend_object_store_get_object(return_value TSRMLS_CC);
                generator->execute_data = execute_data;
+
+               /* We have to add another stack frame so the generator function 
shows
+                * up in backtraces and func_get_all() can access the function
+                * arguments. */
+               EX(prev_execute_data) = emalloc(sizeof(zend_execute_data));
+               if (prev_execute_data) {
+                       memcpy(EX(prev_execute_data), prev_execute_data, 
sizeof(zend_execute_data));
+                       EX(prev_execute_data)->function_state.arguments = 
zend_copy_arguments(prev_execute_data->function_state.arguments);
+               } else {
+                       memset(EX(prev_execute_data), 0, 
sizeof(zend_execute_data));
+                       EX(prev_execute_data)->function_state.function = 
(zend_function *) EX(op_array);
+                       EX(prev_execute_data)->function_state.arguments = NULL;
+               }
        }
 
        /* restore the previous execution context */
-       EG(current_execute_data) = EX(prev_execute_data);
-       nested = EX(nested);
+       EG(current_execute_data) = prev_execute_data;
 
        /* if there is no return value pointer we are responsible for freeing 
the
      * execution data */
@@ -1240,34 +1253,37 @@ static int ZEND_FASTCALL  
ZEND_SUSPEND_AND_RETURN_GENERATOR_SPEC_HANDLER(ZEND_OP
                efree(execute_data);
        }
 
-       EG(opline_ptr) = NULL;
-       if (nested) {
-               /* so we can use EX() again */
-               execute_data = EG(current_execute_data);
 
-               EG(opline_ptr)           = &EX(opline);
-               EG(active_op_array)      = EX(op_array);
-               EG(return_value_ptr_ptr) = EX(original_return_value);
-               EG(active_symbol_table)  = EX(symbol_table);
-               EG(This)                 = EX(current_this);
-               EG(scope)                = EX(current_scope);
-               EG(called_scope)         = EX(current_called_scope);
+       /* Happens whenever the function is invoked using call_user_function,
+        * e.g. when doing a dynamic function call using call_user_func(). */
+       if (!nested) {
+               EG(opline_ptr) = NULL;
+               ZEND_VM_RETURN();
+       }
 
-               EX(function_state).function  = (zend_function *) EX(op_array);
-               EX(function_state).arguments = NULL;
+       /* Bring back the previous execution context */
+       execute_data = EG(current_execute_data);
 
-               EX(object) = EX(current_object);
-               EX(called_scope) = DECODE_CTOR(EX(called_scope));
+       EG(opline_ptr)           = &EX(opline);
+       EG(active_op_array)      = EX(op_array);
+       EG(return_value_ptr_ptr) = EX(original_return_value);
+       EG(active_symbol_table)  = EX(symbol_table);
+       EG(This)                 = EX(current_this);
+       EG(scope)                = EX(current_scope);
+       EG(called_scope)         = EX(current_called_scope);
 
-               zend_vm_stack_clear_multiple(TSRMLS_C);
+       EX(function_state).function  = (zend_function *) EX(op_array);
+       EX(function_state).arguments = NULL;
 
-               LOAD_REGS();
-               LOAD_OPLINE();
-               ZEND_VM_INC_OPCODE();
-               ZEND_VM_LEAVE();
-       }
+       EX(object) = EX(current_object);
+       EX(called_scope) = DECODE_CTOR(EX(called_scope));
 
-       ZEND_VM_RETURN();
+       zend_vm_stack_clear_multiple(TSRMLS_C);
+
+       LOAD_REGS();
+       LOAD_OPLINE();
+       ZEND_VM_INC_OPCODE();
+       ZEND_VM_LEAVE();
 }
 
 static int ZEND_FASTCALL  
ZEND_FETCH_CLASS_SPEC_CONST_HANDLER(ZEND_OPCODE_HANDLER_ARGS)
-- 
PHP CVS Mailing List (http://www.php.net/)
To unsubscribe, visit: http://www.php.net/unsub.php

Reply via email to