- Notifications
You must be signed in to change notification settings - Fork7.9k
Add generators support#177
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to ourterms of service andprivacy statement. We’ll occasionally send you account related emails.
Already on GitHub?Sign in to your account
Uh oh!
There was an error while loading.Please reload this page.
Conversation
Generator functions have to specify the * (asterix) modifier after thefunction keyword. If they do so the ZEND_ACC_GENERATOR flag is added tothe fn_flags.
The block for the foreach separator was nested unnecessary. This commitsimply removes that nesting.
The yield statement can only be used in generator functions, which aremarked with an asterix.
The execution of generator functions will be suspended right after thearguments were RECVed. This will be done in zend_do_suspend_if_generator.
If the function is a generator this opcode will be invoked right afterreceiving the function arguments.The current implementation is just a dummy.
The Generator class now uses a zend_generator struct, so it'll be able tostore additional info.This commit also ensures that Generator cannot be directly instantiatedand extended. The error tests are now in a separate folder from the(yet-to-come) functional tests.
Right now generator functions simply immediately return a new Generatorobject (no suspension yet).
Generators need to switch the execute_data very often. If the execute_datais allocated on the VM stack this operation would require to always copythe structure (which is quite large). That's why the execution context isallocated on the heap instead (only for generators obviously).
This is just some initial code, which is still quite broken (and needs to bemoved so it can be reused.)
This simply adds dummy rewind/valid/current/key/next methods to Generator.
Before one could only call it with cwd=Zend.
This adds another function execute_ex(), which accepts a zend_execute_datastruct to run (contrary to execute(), which accepts a zend_op_array fromwhich it initialized the execute_data).This needs a bit more cleanup.
The generator zval is put into the return_value_ptr_ptr.
For generators ZEND_RETURN directly calls ZEND_VM_RETURN(), thus passingexecution back to the caller (zend_generator_resume).This commit also adds a check that only return; is used in generators andnot return $value;.
The test implements an xrange() function (the generator version of range()).
If the generator is closed before it has finished running, it may happenthat some FREE or SWITCH_FREE opcodes haven't been executed and memory isleaked.This fixes it by walking the brk_cont_array and manually freeing thevariables.
This happens primarily when the generator is invoked from some internalplace like a dynamic function call.
This fixes several issues. In particular it makes method generators workproperly and also allows generators using a symbol table.
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)
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 isNULL.Unlike in Python ->send() can be run without priming the generator with a->next() call first.
If the generator is used as a coroutine it often doesn't make sense to yieldanything. In this case one can simply receive values using $value = yield;The yield here will simply yield NULL.
Calling $generator->close() is equivalent to executing a return statementat the current position in the generator.
This is just an intial merge. It does not yet make generators and finallywork together.Conflicts:Zend/zend_language_scanner.cZend/zend_language_scanner_defs.hZend/zend_vm_def.hZend/zend_vm_execute.hZend/zend_vm_execute.sklZend/zend_vm_opcodes.h
The finally clause is now properly run when an exception is thrown in thetry-block. It is not yet run on `return` and also not run when the generatoris claused within a try block.I'll add those two things as soon as laruence refactored the finally code.
Merging master to fix Windows buildConflicts:Zend/zend_language_scanner.cZend/zend_language_scanner_defs.hZend/zend_vm_def.h
Conflicts:Zend/zend_vm_def.hZend/zend_vm_execute.h
Generators don't have a return value, so it doesn't make sense to havea shared implementation here.
* 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
Now I get the diff, thanks |
I decided to leave out yield delegation for an initial proposal, so removethe stubs for it too.
Conflicts:Zend/zend_language_parser.yZend/zend_vm_execute.skl
@@ -1,12 +0,0 @@ | |||
--TEST-- |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others.Learn more.
why delete this test?
If you try to traverse an already closed generator an exception will now bethrown.Furthermore this changes the error for traversing a by-val generator by-reffrom an E_ERROR to an Exception.
travisbot commentedAug 29, 2012
KendallHopkins commentedSep 1, 2012
@nikic I'm getting segfaults with this codehttps://gist.github.com/3588986 |
@KendallHopkins please submit a bug report to bugs.php.net so it could be tracked properly. |
KendallHopkins commentedSep 2, 2012
@@ -1845,17 +1845,22 @@ | |||
zend_bool nested; | |||
zend_op_array *op_array = EX(op_array); | |||
/* Generators go throw a different cleanup process */ |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others.Learn more.
Through? 😄
PR for generators as outlined inhttps://wiki.php.net/rfc/generators.
(For the diff)