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