1
0
mirror of https://github.com/php/php-src.git synced 2026-03-26 01:02:25 +01:00
Commit Graph

50 Commits

Author SHA1 Message Date
Nikita Popov
be7b0bc3ec Implement Generator::throw() method
Generator::throw($exception) throws an exception into the generator. The
exception is thrown at the current point of suspension within the generator.
It basically behaves as if the current yield statement were replaced with
a throw statement and the generator subsequently resumed.
2012-12-24 00:27:55 +01:00
Nikita Popov
14f133036c Fix crash when last yielded value is a closure
If zend_generator_close is called from within zend_generator_resume (e.g.
due to a return statement) then all the EGs will still be using the values
from the generator. That's why the stack frame has to be the last thing
that is dtored, otherwise some other dtor that is using
EG(current_execute_data) might access the already freed memory segment.
This was the case with the closure dtor.

The fix is to move the dtors for key and value to the start of the handler.
This way the stack frame is the last thing that is freed.
2012-12-21 17:28:20 +01:00
Nikita Popov
3e78c6ad25 Do not add a ref to EX(object) on generator clone
If a ref has to be added it will be already added while walking the call
slots.
2012-12-20 20:33:18 +01:00
Nikita Popov
d53f1bf8ab Fix leak when generator ignores sent value
When the return value of yield wasn't used it was leaked.

This is fixed by using a TMP_VAR return value instead of VAR. TMP_VARs are
automatically freed when they aren't used.
2012-12-18 21:39:02 +01:00
Dmitry Stogov
9c96fe52d9 Restored proper generators behaviour in conjunction with "finally". (Nikita) 2012-12-12 17:47:55 +04:00
Dmitry Stogov
66b8ece02a Fixed bug #63066 (Calling an undefined method in a generator results in a seg fault) (fixed with previous commit). 2012-11-30 14:14:36 +04:00
Nikita Popov
60871e51ad Fix bug #63596: finally in generators segfaults
EX(fast_ret) wasn't initialized in this case so the code ended up
dereferencing an invalid pointer after the jump.
2012-11-24 19:24:09 +01:00
Nikita Popov
a31fa55b44 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
2012-09-22 19:15:53 +02:00
Nikita Popov
dffffdeb3d Fix leak when yielding array as key
The code was copy-pasted and I forgot to change OP1 to OP2 in that one
place.
2012-09-01 20:31:40 +02:00
Nikita Popov
bef79588d5 Fix segfault when traversing a by-ref generator twice
If you try to traverse an already closed generator an exception will now be
thrown.

Furthermore this changes the error for traversing a by-val generator by-ref
from an E_ERROR to an Exception.
2012-08-29 20:46:56 +02:00
Nikita Popov
cc07038fa9 Make sure that exception is thrown on rewind() after closing too 2012-08-29 20:31:34 +02:00
Nikita Popov
f53225a99e Fix several issues and allow rewind only at/before first yield
* Trying to resume a generator while it is already running now throws a
   fatal error.
 * Trying to use yield in finally while the generator is being force-closed
   (by GC) throws a fatal error.
 * Rewinding after the first yield now throws an Exception
2012-08-25 17:40:08 +02:00
Nikita Popov
4d8edda341 Run finally if generator is closed before finishing 2012-08-24 19:10:09 +02:00
Nikita Popov
7cdf6367a5 Finally with return now works in generators too 2012-08-24 13:52:16 +02:00
Nikita Popov
f45a0f31c8 Disallow serialization and unserialization 2012-08-20 16:01:16 +02:00
Nikita Popov
05f10480c5 Drop Generator::close() method 2012-08-20 12:53:18 +02:00
Nikita Popov
7195a5b376 Forgot to add test 2012-08-13 19:24:44 +02:00
Nikita Popov
268740d984 Fix implementation of Iterator interface
It looks like you have to implement the Iterator interface *before*
assigning get_iterator. Otherwise the structure for user iterators isn't
correctly zeroed out.

Additionaly I'm setting class_entry->iterator_funcs.funcs now. Not sure if
this is strictly necessary, but better safe than sorry ;)
2012-07-26 17:13:25 +02:00
Nikita Popov
134089372b Throw error also for return occuring before yield
Previously only an error was thrown when return occured after yield. Also
returns before the first yield would fail for by-ref generators.

Now the error message is handled in pass_two, so all returns are checked.
2012-07-22 20:11:09 +02:00
Nikita Popov
94b2ccae9c Fix throwing of exceptions within a generator
If a generator threw an exception and was iterated using foreach (i.e. not
manually) an infinite loop was triggered. The reason was that the exception
was not properly rethrown using zend_throw_exception_internal.
2012-07-22 17:46:46 +02:00
Nikita Popov
de80e3ce4b Remove reference restrictions from foreach
foreach only allowed variables to be traversed by reference. This never
really made sense because

    a) Expressions like array(&$a, &$b) can be meaningfully iterated by-ref
    b) Function calls can return by-ref (so they can also be meaningfully
       iterated)
    c) Iterators could at least in theory also be iterated by-ref (not
       sure if any iterator makes use of this)

With by-ref generators the restriction makes even less sense, so I removed
it altogether.
2012-07-22 14:33:25 +02:00
Nikita Popov
80748631aa Require parenthesis around yield expressions
If yield is used in an expression context parenthesis are now required.
This ensures that the code is unambiguos.

Yield statements can still be used without parenthesis (which should be
the most common case).

Also yield expressions without value can be used without parenthesis,
too (this should be the most common case for coroutines).

If the yield expression is used in a context where parenthesis are required
anyway, no additional parenthesis have to be inserted.

Examples:

    // Statements don't need parenthesis
    yield $foo;
    yield $foo => $bar;

    // Yield without value doesn't need parenthesis either
    $data = yield;

    // Parentheses don't have to be duplicated
    foo(yield $bar);
    if (yield $bar) { ... }

    // But we have to use parentheses here
    $foo = (yield $bar);

This commit also fixes an issue with by-ref passing of $foo[0] like
variables. They previously weren't properly fetched for write.

Additionally this fixes valgrind warnings which were caused by access to
uninitialized memory in zend_is_function_or_method_call().
2012-07-22 01:22:22 +02:00
Nikita Popov
1f70a4c5fe Add some more tests 2012-07-20 17:40:04 +02:00
Nikita Popov
c9709bfbd7 Remove asterix modifier (*) for generators
Generators are now automatically detected by the presence of a `yield`
expression in their body.

This removes the ZEND_SUSPEND_AND_RETURN_GENERATOR opcode. Instead
additional checks for ZEND_ACC_GENERATOR are added to the fcall_common
helper and zend_call_function.

This also adds a new function zend_generator_create_zval, which handles
the actual creation of the generator zval from an op array.

I feel like I should deglobalize the zend_create_execute_data_from_op_array
code a bit. It currently changes EG(current_execute_data) and
EG(opline_ptr) which is somewhat confusing (given the name).
2012-07-20 16:09:06 +02:00
Nikita Popov
85f077cea1 Add support by yielding by-reference 2012-07-17 13:29:56 +02:00
Nikita Popov
5a9bddba66 Forgot to git add two tests 2012-06-25 21:41:50 +02:00
Nikita Popov
d939d2dee5 Add sceleton for yield* expression
This does not yet actually implement any delegation.
2012-06-19 00:37:31 +02:00
Nikita Popov
f169b26dd7 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.
2012-06-09 00:40:47 +02:00
Nikita Popov
40760ecb90 Fix cloning of generator methods
Forgot to add a reference to the this variable
2012-06-05 14:00:31 +02:00
Nikita Popov
bf82f46ea9 Properly handle yield during method calls 2012-06-03 02:16:29 +02:00
Nikita Popov
7b3bfa5784 Improve backtraces from generators
The current situation is still not perfect, as the generator function itself
does not appear in the stack trace. This makes sense in some way, but it
would probably be more helpful if it would show up (with the bound arguments)
after the $generator->xyz() call. This could be misleading too though as the
function is not *really* called there.
2012-06-03 02:00:11 +02:00
Nikita Popov
6117f4c7c0 Add cloning support for generators
Generators can now be cloned. I'm pretty sure that my current code does not
yet cover all the edge cases of cloning the execution context, so there are
probably a few bugs in there :)
2012-06-02 20:40:58 +02:00
Nikita Popov
1477be9aa8 Make $generator->send() return the current value
This makes the API easier to use (and is consistent with Python and JS).
2012-05-31 20:03:18 +02:00
Nikita Popov
ee89e228f6 Allow yielding during function calls
During function calls arguments are pushed onto the stack. Now these are
backed up on yield and restored on resume. This requires memcpy'ing them,
but there doesn't seem to be any better way to do it.

Also this fixes the issue with exceptions thrown during function calls.
2012-05-31 00:00:30 +02:00
Nikita Popov
0033a52521 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.
2012-05-30 16:28:33 +02:00
Nikita Popov
8790160235 Add auto-increment keys
When no key is explicitely yielded PHP will used auto-incrementing keys
as a fallback. They behave the same as with arrays, i.e. the key is the
successor of the largest previously used integer key.
2012-05-30 05:05:49 +02:00
Nikita Popov
bc08c2cf94 Add support for yielding keys
Keys are yielded using the

    yield $key => $value

syntax. Currently this is implemented as a statement only and not as an
expression, because conflicts arise considering nesting and use in arrays:

    yield yield $a => $b;
    // could be either
    yield (yield $a) => $b;
    // or
    yield (yield $a => $b);

Once I find some way to resolve these conflicts this should be available
as an expression too.

Also the key yielding code is rather copy-and-past-y for the value yielding
code, so that should be factored out.
2012-05-30 02:44:06 +02:00
Nikita Popov
72a91d08e7 Add $generator->close() method
Calling $generator->close() is equivalent to executing a return statement
at the current position in the generator.
2012-05-29 18:11:18 +02:00
Nikita Popov
12e928314f Fix segfault when send()ing to a closed generator 2012-05-29 18:01:08 +02:00
Nikita Popov
ad525c288a Allow to use yield without value
If the generator is used as a coroutine it often doesn't make sense to yield
anything. In this case one can simply receive values using

    $value = yield;

The yield here will simply yield NULL.
2012-05-29 17:53:11 +02:00
Nikita Popov
3600914ced Add support for $generator->send()
Yield now is an expression and the return value is the value passed to
$generator->send(). By default (i.e. if ->next() is called) the value is
NULL.

Unlike in Python ->send() can be run without priming the generator with a
->next() call first.
2012-05-29 17:34:33 +02:00
Nikita Popov
4aab08b64c Properly free resources when generator return value not used
To keep things clean two new functions are introduced:

zend_clean_and_cache_symbol_table(HashTable *symbol_table)
zend_free_compiled_variables(zval ***CVs, int num)
2012-05-28 06:43:18 +02:00
Nikita Popov
bcc7d976f3 Set EG(current_execute_data)
This fixes several issues. In particular it makes method generators work
properly and also allows generators using a symbol table.
2012-05-28 06:06:30 +02:00
Nikita Popov
9f52c5c226 Fix generator creation when execute_data is not nested
This happens primarily when the generator is invoked from some internal
place like a dynamic function call.
2012-05-27 22:48:21 +02:00
Nikita Popov
64a643a4e3 Free loop variables
If the generator is closed before it has finished running, it may happen
that some FREE or SWITCH_FREE opcodes haven't been executed and memory is
leaked.

This fixes it by walking the brk_cont_array and manually freeing the
variables.
2012-05-27 17:14:20 +02:00
Nikita Popov
39d3d5ec13 Add first real generator test
The test implements an xrange() function (the generator version of range()).
2012-05-27 00:50:27 +02:00
Nikita Popov
5bb3a995c2 Implement return for generators
For generators ZEND_RETURN directly calls ZEND_VM_RETURN(), thus passing
execution back to the caller (zend_generator_resume).

This commit also adds a check that only return; is used in generators and
not return $value;.
2012-05-26 23:07:40 +02:00
Nikita Popov
46fa26ab85 Make generator functions return a Generator object
Right now generator functions simply immediately return a new Generator
object (no suspension yet).
2012-05-20 14:45:01 +02:00
Nikita Popov
40b7533576 Add some boilerplate code for Generator class
The Generator class now uses a zend_generator struct, so it'll be able to
store additional info.

This commit also ensures that Generator cannot be directly instantiated
and extended. The error tests are now in a separate folder from the
(yet-to-come) functional tests.
2012-05-20 14:19:16 +02:00
Nikita Popov
fd2a109f86 Add error if yield is used outside a generator
The yield statement can only be used in generator functions, which are
marked with an asterix.
2012-05-19 18:49:27 +02:00