The code did not check whether the zend_hash_index_find calls succeded,
so PHP crashed when an array callback was called that contains two elements
which don't have the indices 0 and 1.
set_error_handler(null) and set_exception_handler(null) now return the
previous error/exception handler instead of just returning bool(true).
This is consistent with the behavior of these functions with non-null
values.
This allows the error handler to be reset using set_error_handler(null).
As the code suggests this behavior was already previously intended, but
the callback check was done too strictly.
This reverts the following two commits:
* 6ba2e662e4
* d8f8e98d8e
Laruence already did some partial changes to set_error_handler and
set_exception_handler. I'm reverting those modifications to apply the full
set of changes. (The modifications changed the code structure in a way that
would lead to more duplication with the new behavior.)
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
* generators: (70 commits)
Fix typos
Fix segfault when traversing a by-ref generator twice
Make sure that exception is thrown on rewind() after closing too
Remove implementation stubs for yield delegation
Fix several issues and allow rewind only at/before first yield
Run finally if generator is closed before finishing
Finally with return now works in generators too
Add dedicated opcode for returns from a generator
Disallow serialization and unserialization
Fix zts build (typo)
Drop Generator::close() method
Forgot to add test
Support trivial finally in generators (no yield, no return)
Fix implementation of Iterator interface
Add T_YIELD in tokenizer_data.c
Throw error also for return occuring before yield
Fix throwing of exceptions within a generator
Remove reference restrictions from foreach
Require parenthesis around yield expressions
Add some more tests
...
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
This is just an intial merge. It does not yet make generators and finally
work together.
Conflicts:
Zend/zend_language_scanner.c
Zend/zend_language_scanner_defs.h
Zend/zend_vm_def.h
Zend/zend_vm_execute.h
Zend/zend_vm_execute.skl
Zend/zend_vm_opcodes.h
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 ;)
the reason why jpauli and I can not reproduce is (it's silly):
I typo "USE_ZEND_ALLOC *&&* valgrind" at the first time, then I always ctrl+r
and jpauli copied my command from the pastbin :)
thanks
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.
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.
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.
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().
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).
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.
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 :)
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.
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.
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.
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.
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)
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.
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;.
* pull-request/54:
Allow arbitrary expressions for empty()
This change is as per RFC https://wiki.php.net/rfc/empty_isset_exprs.
The change allows passing the result of function calls and other
expressions to the empty() language construct. This is accomplished by
simply rewriting empty(expr) to !expr.
The change does not affect the suppression of errors when using empty()
on variables. empty($undefinedVar) will continue not to throw errors.
When an expression is used inside empty() on the other hand, errors will
not be suppressed. Thus empty($undefinedVar + $somethingElse) *will*
throw a notice.
The change also does not make empty() into a real function, so using
'empty' as a callback is still not possible.
In addition to the empty() changes the commit adds nicer error messages
when isset() is used on function call results or other expressions.
This fixes the fix for bug #54547 in 32-bit machines by accepting
float comparisons in 32-bit machines as long as the integer is
not larger than the mantissa.
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.
This change is as per RFC https://wiki.php.net/rfc/empty_isset_exprs.
The change allows passing the result of function calls and other
expressions to the empty() language construct. This is accomplished by
simply rewriting empty(expr) to !expr.
The change does not affect the suppression of errors when using empty()
on variables. empty($undefinedVar) will continue not to throw errors.
When an expression is used inside empty() on the other hand, errors will
not be suppressed. Thus empty($undefinedVar + $somethingElse) *will*
throw a notice.
The change also does not make empty() into a real function, so using
'empty' as a callback is still not possible.
In addition to the empty() changes the commit adds nicer error messages
when isset() is used on function call results or other expressions.
* PHP-5.4:
Fixed Bug #62005 (unexpected behavior when incrementally assigning to a member of a null object)
fix stack overflow in php_intlog10abs()
fix stack overflow in php_intlog10abs()
* PHP-5.3:
Fixed Bug #62005 (unexpected behavior when incrementally assigning to a member of a null object)
fix stack overflow in php_intlog10abs()
Conflicts:
Zend/zend_execute.c
Generate T_STRING_VARNAME only if it actually is one. This is only the case
for "${varname}" and "${varname[offset]}" so we can just add a check for
} or [ after the LABEL.
# The handling of private properties in classes is now consistent with private properties in traits.
# Perviously, privates could cause strict warnings, are were not properly merged into the class when
# the parent class had a private property of the same name. Now, we introduce it without notice,
# since it is a new and independent property, just like in normal classes.
# This problem was diagnosed while working on Bug #60536.
# The handling of private properties in classes is now consistent with private properties in traits.
# Perviously, privates could cause strict warnings, are were not properly merged into the class when
# the parent class had a private property of the same name. Now, we introduce it without notice,
# since it is a new and independent property, just like in normal classes.
# This problem was diagnosed while working on Bug #60536.
# this is a tough one, I think I should explain
# Zend use zend_object->properties_table both as zval ** and zval ***
# if a zend_object->properties is not initialized, the properties_table is zval **
# while in rebuild_object_properties, zend will store the zval ** to zend_object->properties
# then stash the zval ***(ie, zobj->properties_table[0] is zval ** now) to zobj->properties_table[0]
# so when a zend_object inherit form multi parent and these parent have a same property_info->offset
# properties, will result in a repeat zval **->zval ** transform, which will lead to a segmentfault
# *may be* this fix is not the best fix, we should not use this tricky way, and rewrite this mechanism.
# this is a tough one, I think I should explain
# Zend use zend_object->properties_table both as zval ** and zval ***
# if a zend_object->properties is not initialized, the properties_table is zval **
# while in rebuild_object_properties, zend will store the zval ** to zend_object->properties
# then stash the zval ***(ie, zobj->properties_table[0] is zval ** now) to zobj->properties_table[0]
# so when a zend_object inherit form multi parent and these parent have a same property_info->offset
# properties, will result in a repeat zval **->zval ** transform, which will lead to a segmentfault
# *may be* this fix is not the best fix, we should not use this tricky way, and rewrite this mechanism.
- aliases that are not actually matching anything are treated as errors now. This
will make sure that all methods that are expected to be in a class are actually
there, or in case a trait changed for instance, that the code breaks already
on composition
- Precedence declarations are also checked to ensure that the method
which is supposed to take precedence actually exists, however,
the other traits mentioned in the declaration are not regarded.
We are more lenient here, since this avoids unnecessary fragility.
- fixed another seamingly unrelated test which broke in the progress
but wasn't clear before either.
- aliases that are not actually matching anything are treated as errors now. This
will make sure that all methods that are expected to be in a class are actually
there, or in case a trait changed for instance, that the code breaks already
on composition
- Precedence declarations are also checked to ensure that the method
which is supposed to take precedence actually exists, however,
the other traits mentioned in the declaration are not regarded.
We are more lenient here, since this avoids unnecessary fragility.
- fixed another seamingly unrelated test which broke in the progress
but wasn't clear before either.
# Moved the freeing of overriden functions to a point after the check.
# The new check comes after the normal inheritance check to give the first check
# the opportunity to abort with a more detailed error.
# Also fixed a small type in an unrelated test.
# Moved the freeing of overriden functions to a point after the check.
# The new check comes after the normal inheritance check to give the first check
# the opportunity to abort with a more detailed error.
# Also fixed a small type in an unrelated test.
# The handling of legacy constructors defined by traits was corrected.
# They are now properly registered and used on instantiation.
# The situation for conflicting legacy and __construct constructors is
# mostly identical. If they are defined in the class, they override conflicts
# and do not collide. However, in case different styles are mixed, between
# class and trait definition, we assume a programmer's mistake and report
# a collision.
#
# BTW: +1 for all the fixed tests! `make test` is fun again.
# The handling of legacy constructors defined by traits was corrected.
# They are now properly registered and used on instantiation.
# The situation for conflicting legacy and __construct constructors is
# mostly identical. If they are defined in the class, they override conflicts
# and do not collide. However, in case different styles are mixed, between
# class and trait definition, we assume a programmer's mistake and report
# a collision.
#
# BTW: +1 for all the fixed tests! `make test` is fun again.
# The method got unconditionally deleted from the class, since it was assumed that we override it, but we did not in case of abstract methods coming from a trait. Thus, dont delete when we try to merge in an abstract method.
# The method got unconditionally deleted from the class, since it was assumed that we override it, but we did not in case of abstract methods coming from a trait. Thus, dont delete when we try to merge in an abstract method.
# __TRAIT__ behaves like __CLASS__ more or less but is constraint to traits.
# Since traits are not types, there are not many valid use cases, and trying
# to use __TRAIT__ to make traits more like classes is discouraged.
# __TRAIT__ behaves like __CLASS__ more or less but is constraint to traits.
# Since traits are not types, there are not many valid use cases, and trying
# to use __TRAIT__ to make traits more like classes is discouraged.