| Commit message (Collapse) | Author | Age | Files | Lines |
| |
|
|
|
|
|
|
|
| |
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.
|
|
|
|
|
|
|
|
|
|
|
|
| |
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.
|
|
|
|
|
| |
If a ref has to be added it will be already added while walking the call
slots.
|
|
|
|
|
|
|
| |
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.
|
| |
|
| |
|
|
|
|
| |
- improved RETURN sequence to avoid redundant check if op_array is a generator
|
| |
|
| |
|
|
|
|
|
|
| |
were merged into a single stack. The stack size needed for op_array execution is calculated at compile time and preallocated at once. As result all the stack push operatins don't require checks for stack overflow any more.
. Generators implementation was improved using the new VM stack. Now it's a bit more clear and faster.
|
|
|
|
|
| |
EX(fast_ret) wasn't initialized in this case so the code ended up
dereferencing an invalid pointer after the jump.
|
| |
|
| |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| |
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
|
| |
|
| |
|
| |
|
| |
|
|
|
|
|
|
|
|
| |
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.
|
| |
|
|
|
|
|
|
|
|
| |
* 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
|
| |
|
| |
|
| |
|
|
|
|
|
|
|
|
|
| |
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 ;)
|
|
|
|
|
|
| |
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.
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| |
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).
|
| |
|
|
|
|
|
| |
If a generator is closed while it is running an E_WARNING is thrown and the
call is ignored. Maybe a fatal error should be thrown instead?
|
|
|
|
|
|
| |
Previously the zval* of the generator was passed into the VM by misusing
EG(return_value_ptr_ptr). Now the zend_generator* itself is directly passed
in. This saves us from always having to pass the zval* around everywhere.
|
|
|
|
|
| |
This implements the get_iterator handler for Generator objects, thus making
direct foreach() iteration significantly faster.
|
| |
|
|
|
|
| |
This does not yet actually implement any delegation.
|
|
|
|
|
|
|
|
|
|
|
|
| |
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.
|
|
|
|
| |
Forgot to add a reference to the this variable
|
| |
|
|
|
|
|
|
|
|
| |
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.
|
|
|
|
|
|
| |
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 :)
|
|
|
|
| |
This makes the API easier to use (and is consistent with Python and JS).
|
|
|
|
|
|
|
|
| |
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.
|
|
|
|
|
|
| |
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.
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| |
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.
|
|
|
|
|
| |
Calling $generator->close() is equivalent to executing a return statement
at the current position in the generator.
|
| |
|
|
|
|
|
|
|
|
|
| |
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.
|
|
|
|
|
|
|
| |
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)
|
|
|
|
|
| |
This fixes several issues. In particular it makes method generators work
properly and also allows generators using a symbol table.
|
|
|
|
|
|
|
|
|
| |
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.
|
| |
|