Is the addition of those two ZEND_API functions okay? If not, I'll inline them.

Nikita

On Sat, Sep 22, 2012 at 7:15 PM, Nikita Popov <ni...@php.net> wrote:
> Commit:    a31fa55b44bcb342c00e9ab2f4a851d054897a39
> Author:    Nikita Popov <ni...@php.net>         Sat, 22 Sep 2012 19:12:21 
> +0200
> Parents:   6c135dff975f111ec5a84af93c1b98e9ae84fcd1
> Branches:  master
>
> Link:       
> http://git.php.net/?p=php-src.git;a=commitdiff;h=a31fa55b44bcb342c00e9ab2f4a851d054897a39
>
> Log:
> Fixed bug #63132
>
> EG(arg_types_stack) is now also backed up when generators are used. This
> allows the use of yield in nested method calls.
>
> This commit adds two new functions to the zend_ptr_stack API:
>
>     zend_ptr_stack_push_from_memory
>     zend_ptr_stack_pop_into_memory
>
> both taking the following arguments:
>
>     zend_ptr_stack *stack, int count, void **pointers
>
> Bugs:
> https://bugs.php.net/63132
>
> Changed paths:
>   A  Zend/tests/generators/nested_method_calls.phpt
>   M  Zend/zend_generators.c
>   M  Zend/zend_generators.h
>   M  Zend/zend_ptr_stack.c
>   M  Zend/zend_ptr_stack.h
>
>
> Diff:
> diff --git a/Zend/tests/generators/nested_method_calls.phpt 
> b/Zend/tests/generators/nested_method_calls.phpt
> new file mode 100644
> index 0000000..98aee2e
> --- /dev/null
> +++ b/Zend/tests/generators/nested_method_calls.phpt
> @@ -0,0 +1,39 @@
> +--TEST--
> +Yield can be used in nested method calls
> +--FILE--
> +<?php
> +
> +class A {
> +    function foo() {
> +        echo "Called A::foo\n";
> +    }
> +}
> +
> +class B {
> +    function foo() {
> +        echo "Called B::foo\n";
> +    }
> +}
> +
> +function gen($obj) {
> +    $obj->foo($obj->foo(yield));
> +}
> +
> +$g1 = gen(new A);
> +$g1->current();
> +
> +$g2 = gen(new B);
> +$g2->current();
> +
> +$g1->next();
> +
> +$g3 = clone $g2;
> +unset($g2);
> +$g3->next();
> +
> +?>
> +--EXPECT--
> +Called A::foo
> +Called A::foo
> +Called B::foo
> +Called B::foo
> diff --git a/Zend/zend_generators.c b/Zend/zend_generators.c
> index 01b33a3..fba62dd 100644
> --- a/Zend/zend_generators.c
> +++ b/Zend/zend_generators.c
> @@ -132,6 +132,21 @@ void zend_generator_close(zend_generator *generator, 
> zend_bool finished_executio
>                         efree(generator->backed_up_stack);
>                 }
>
> +               if (generator->backed_up_arg_types_stack) {
> +                       /* The arg types stack contains three elements per 
> call: fbc, object
> +                        * and called_scope. Here we traverse the stack from 
> top to bottom
> +                        * and dtor the object. */
> +                       int i = generator->backed_up_arg_types_stack_count / 
> 3;
> +                       while (i--) {
> +                               zval *object = (zval *) 
> generator->backed_up_arg_types_stack[3*i + 1];
> +                               if (object) {
> +                                       zval_ptr_dtor(&object);
> +                               }
> +                       }
> +
> +                       efree(generator->backed_up_arg_types_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. */
> @@ -288,6 +303,25 @@ static void zend_generator_clone_storage(zend_generator 
> *orig, zend_generator **
>                         }
>                 }
>
> +               if (orig->backed_up_arg_types_stack) {
> +                       size_t stack_size = 
> orig->backed_up_arg_types_stack_count * sizeof(void *);
> +
> +                       clone->backed_up_arg_types_stack = 
> emalloc(stack_size);
> +                       memcpy(clone->backed_up_arg_types_stack, 
> orig->backed_up_arg_types_stack, stack_size);
> +
> +                       /* We have to add refs to the objects in the arg 
> types stack (the
> +                        * object is always the second element of a 
> three-pack. */
> +                       {
> +                               int i, stack_frames = 
> clone->backed_up_arg_types_stack_count / 3;
> +                               for (i = 0; i < stack_frames; i++) {
> +                                       zval *object = (zval *) 
> clone->backed_up_arg_types_stack[3*i + 1];
> +                                       if (object) {
> +                                               Z_ADDREF_P(object);
> +                                       }
> +                               }
> +                       }
> +               }
> +
>                 /* Update the send_target to use the temporary variable with 
> the same
>                  * offset as the original generator, but in our temporary 
> variable
>                  * memory segment. */
> @@ -449,6 +483,7 @@ void zend_generator_resume(zend_generator *generator 
> TSRMLS_DC) /* {{{ */
>                 zval *original_This = EG(This);
>                 zend_class_entry *original_scope = EG(scope);
>                 zend_class_entry *original_called_scope = EG(called_scope);
> +               int original_arg_types_stack_count = EG(arg_types_stack).top;
>
>                 /* Remember the current stack position so we can back up 
> pushed args */
>                 generator->original_stack_top = zend_vm_stack_top(TSRMLS_C);
> @@ -461,6 +496,16 @@ void zend_generator_resume(zend_generator *generator 
> TSRMLS_DC) /* {{{ */
>                         generator->backed_up_stack = NULL;
>                 }
>
> +               if (generator->backed_up_arg_types_stack) {
> +                       zend_ptr_stack_push_from_memory(
> +                               &EG(arg_types_stack),
> +                               generator->backed_up_arg_types_stack_count,
> +                               generator->backed_up_arg_types_stack
> +                       );
> +                       efree(generator->backed_up_arg_types_stack);
> +                       generator->backed_up_arg_types_stack = NULL;
> +               }
> +
>                 /* We (mis)use the return_value_ptr_ptr to provide the 
> generator object
>                  * to the executor, so YIELD will be able to set the yielded 
> value */
>                 EG(return_value_ptr_ptr) = (zval **) generator;
> @@ -506,6 +551,18 @@ void zend_generator_resume(zend_generator *generator 
> TSRMLS_DC) /* {{{ */
>                         zend_vm_stack_free(generator->original_stack_top 
> TSRMLS_CC);
>                 }
>
> +               if (original_arg_types_stack_count != 
> EG(arg_types_stack).top) {
> +                       generator->backed_up_arg_types_stack_count =
> +                               EG(arg_types_stack).top - 
> original_arg_types_stack_count;
> +
> +                       generator->backed_up_arg_types_stack = 
> emalloc(generator->backed_up_arg_types_stack_count * sizeof(void *));
> +                       zend_ptr_stack_pop_into_memory(
> +                               &EG(arg_types_stack),
> +                               generator->backed_up_arg_types_stack_count,
> +                               generator->backed_up_arg_types_stack
> +                       );
> +               }
> +
>                 /* If an exception was thrown in the generator we have to 
> internally
>                  * rethrow it in the parent scope. */
>                 if (UNEXPECTED(EG(exception) != NULL)) {
> diff --git a/Zend/zend_generators.h b/Zend/zend_generators.h
> index e47b7ad..3dc3e6f 100644
> --- a/Zend/zend_generators.h
> +++ b/Zend/zend_generators.h
> @@ -36,6 +36,11 @@ typedef struct _zend_generator {
>         void *backed_up_stack;
>         size_t backed_up_stack_size;
>
> +       /* For method calls PHP also pushes various type information on a 
> second
> +        * stack, which also needs to be backed up. */
> +       void **backed_up_arg_types_stack;
> +       int backed_up_arg_types_stack_count;
> +
>         /* The original stack top before resuming the generator. This is 
> required
>          * for proper cleanup during exception handling. */
>         void **original_stack_top;
> diff --git a/Zend/zend_ptr_stack.c b/Zend/zend_ptr_stack.c
> index aefa91f..d178cc0 100644
> --- a/Zend/zend_ptr_stack.c
> +++ b/Zend/zend_ptr_stack.c
> @@ -111,6 +111,22 @@ ZEND_API int zend_ptr_stack_num_elements(zend_ptr_stack 
> *stack)
>         return stack->top;
>  }
>
> +ZEND_API void zend_ptr_stack_push_from_memory(zend_ptr_stack *stack, int 
> count, void **pointers)
> +{
> +       ZEND_PTR_STACK_RESIZE_IF_NEEDED(stack, count);
> +
> +       memcpy(stack->top_element, pointers, count * sizeof(void *));
> +       stack->top_element += count;
> +       stack->top += count;
> +}
> +
> +ZEND_API void zend_ptr_stack_pop_into_memory(zend_ptr_stack *stack, int 
> count, void **pointers)
> +{
> +       memcpy(pointers, stack->top_element - count, count * sizeof(void *));
> +       stack->top_element -= count;
> +       stack->top -= count;
> +}
> +
>  /*
>   * Local variables:
>   * tab-width: 4
> diff --git a/Zend/zend_ptr_stack.h b/Zend/zend_ptr_stack.h
> index 9f6fc13..fe93e93 100644
> --- a/Zend/zend_ptr_stack.h
> +++ b/Zend/zend_ptr_stack.h
> @@ -41,6 +41,8 @@ ZEND_API void zend_ptr_stack_destroy(zend_ptr_stack *stack);
>  ZEND_API void zend_ptr_stack_apply(zend_ptr_stack *stack, void (*func)(void 
> *));
>  ZEND_API void zend_ptr_stack_clean(zend_ptr_stack *stack, void (*func)(void 
> *), zend_bool free_elements);
>  ZEND_API int zend_ptr_stack_num_elements(zend_ptr_stack *stack);
> +ZEND_API void zend_ptr_stack_push_from_memory(zend_ptr_stack *stack, int 
> count, void **pointers);
> +ZEND_API void zend_ptr_stack_pop_into_memory(zend_ptr_stack *stack, int 
> count, void **pointers);
>  END_EXTERN_C()
>
>  #define ZEND_PTR_STACK_RESIZE_IF_NEEDED(stack, count)          \
>
>
> --
> PHP CVS Mailing List (http://www.php.net/)
> To unsubscribe, visit: http://www.php.net/unsub.php
>

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

Reply via email to