Instead of setting the old AST type to zero, replace the AST with
the compiled constexpr AST zval. This requires passing in a
zend_ast** instead of a zend_ast*.
This allows compiling ASTs containing constexprs multiple times
-- the second time, the existing compiled representation will be
resused.
This means we no longer need to copy the attributes AST for
promoted properties.
Make ReflectionAttribute::newInstance() respect the strict_types=1
declaration at the attribute use-site. More generally, pretend that
we are calling the attribute constructor from the place where the
attribute is used, which also means that the attribute location will
show up properly in backtraces and inside "called in" error information.
This requires us to store the attributes strict_types scope (as flags),
as well as the attribute line number. The attribute filename can be
recovered from the symbol it is used on. We might want to expose the
attribute line number via reflection as well.
See also https://externals.io/message/111915.
Closes GH-6201.
Due to improvements to early binding, the opcode based check is
no longer accurate. Reuse the syntactic check we're already using
for declares instead.
This is an annoying edge case that regularly gets broken. As we're
not aware of significant users of this API, and there are other
ways to hook this, remove support for EXT_NOP.
This addresses two issues:
* ~ throws for a number of types, and we should not compile-time
evaluate in that case. Add a check similar to what we do for
binary ops.
* Unary +/- may produce a different error message due to
canonicalization of the constant operand to the RHS. To avoid
this, put the constant operand on the RHS right away.
Fixes oss-fuzz #25649.
See https://github.com/nikic/php-ast/pull/181
> Hm, I'm thinking it would make more sense to change the structure in php-src.
> All the function types have consistent AST structure, but there's no reason at
> all why classes should be consistent with functions.
It's unusual to have an unused child node between other child nodes that are
used (for name, extends, implements, and attributes of AST_CLASS)
> That gap is a leftover from a previous refactoring. An earlier version of
> attributes extended `zend_ast_decl` with a new member called `attributes` and
> therefore did not need to handle functions and classes in different ways.
Closes GH-6088
Otherwise the assignment will have the same number as the default arm
which will 1. mis-trigger a breakpoint and 2. mark the line as covered
even when it isn't.
Closes GH-6083
For php-ast interning the file name is an effective memory leak,
see php-ast#134.
I don't think there's any reason to do this. At some point this
was needed due to bugs in the interned string mechanism that
caused issues if the string was later interned, e.g. through a
__FILE__ reference. These issues have since been resolved.
In conjunction with the filenames_table removal in c4016ecd44
this means that filenames now need to be refcounted like normal
strings. In particular the filename reference in op_arrays and CEs
are refcounted.
Shift the responsibility for emitting MAKE_REF to the list assignment
code, to make sure that LIST_W and MAKE_REF are directly adjacent,
and there are no opcodes in between that could modify the LIST_W
result.
Additionally, adjust the zend_wrong_string_offset() code to not
perform a loop over opcodes and assert that the next opcode is
a relevant one. The VM write-safety model requires this.
This is a followup to a07c1f56aa
and the full fix for oss-fuzz #25352.
Voidification of Zend API which always succeeded
Use bool argument types instead of int for boolean arguments
Use bool return type for functions which return true/false (1/0)
Use zend_result return type for functions which return SUCCESS/FAILURE as they don't follow normal boolean semantics
Closes GH-6002
From an engine perspective, named parameters mainly add three
concepts:
* The SEND_* opcodes now accept a CONST op2, which is the
argument name. For now, it is looked up by linear scan and
runtime cached.
* This may leave UNDEF arguments on the stack. To avoid having
to deal with them in other places, a CHECK_UNDEF_ARGS opcode
is used to either replace them with defaults, or error.
* For variadic functions, EX(extra_named_params) are collected
and need to be freed based on ZEND_CALL_HAS_EXTRA_NAMED_PARAMS.
RFC: https://wiki.php.net/rfc/named_params
Closes GH-5357.
In line with usual rules, give untyped properties a null default
value. Otherwise constructor promotion would give you a property
declaration that cannot be achieved through any other means.
As part of my work on typed class constants, I wanted to make a
separate pull request for unrelated changes.
These include:
* Moving from ast->child[0]->attr to ast->attr
* Making zend_ast_export_ex() export class constants' visibility
* Extracting an additional zend_compile_class_const_group() function
Closes GH-5812.
Add ZVAL_CHAR/RETVAL_CHAR/RETURN_CHAR as a shortcut for using
ZVAL_INTERNED_STRING and ZSTR_CHAR.
Add zend_string_init_fast() as a helper for the empty string /
one char interned string / zend_string_init() pattern.
Also add corresponding ZVAL_STRINGL_FAST etc macros.
Closes GH-5684.
This is a bit tricky: In this cases we have "namespace as", which
means that we will only recognize "namespace" as an identifier when
the lookahead token is already at the "as". This means that
zend_lex_tstring picks up the wrong identifier.
We solve this by actually assigning the identifier as the semantic
value on the parser stack -- as in almost all cases we will not
actually need the identifier, this is just an (offset, size)
reference, not a copy of the string.
Additionally, we need to teach the lexer feedback mechanism used
by tokenizer TOKEN_PARSE mode to apply feedback to something
other than the very last token. To that purpose we pass through
the token text and check the tokens in reverse order to find the
right one.
Closes GH-5668.
Fixes an edge case overlooked in
https://github.com/php/php-src/pull/5313
Generators are objects, so `object` should have also been allowed in addition to
`iterable` and `Traversable`
Closes GH-5626
Currently, disabling a function only replaces the internal
function handler with one that throws a warning, and a few
places in the engine special-case such functions, such as
function_exists. This leaves us with a Schrödinger's function,
which both does not exist (function_exists returns false) and
does exist (you cannot define a function with the same name).
In particular, this prevents the implementation of robust
polyfills, as reported in https://bugs.php.net/bug.php?id=79382:
if (!function_exists('getallheaders')) {
function getallheaders(...) { ... }
}
If getallheaders() is a disabled function, this code will break.
This patch changes disable_functions to remove the functions from
the function table completely. For all intents and purposes, it
will look like the function does not exist.
This also renders two bits of PHP functionality obsolete and thus
deprecated:
* ReflectionFunction::isDisabled(), as it will no longer be
possible to construct the ReflectionFunction of a disabled
function in the first place.
* get_defined_functions() with $exclude_disabled=false, as
get_defined_functions() now never returns disabled functions.
Fixed bug #79382.
Closes GH-5473.
Mark "throw" used in expression context with a flag, and don't
treat it as a BB terminator in that case. This prevents us from
optimizing away the following opcodes as unreachable, which may
result in live ranges being dropped incorrectly.
Close GH-5450.
- zend_hash_find_ptr_lc(ht, zend_string *key)
- zend_hash_str_find_ptr_lc(ht, const char *str, size_t len)
Note that zend_hash_str_find_ptr_lc used to exist in zend_compile.c
as zend_hash_find_ptr_lc. When exporting this I figured it
was best to use the same conventions as the rest of zend_hash.h.
RFC: https://wiki.php.net/rfc/throw_expression
This has an open issue with temporaries that are live at the time
of the throw being leaked. Landing this now for easier testing and
will revert if we cannot resolve the issue.
Closes GH-5279.
This is only used in reflection, where doing a simple string check
is acceptable.
I'm also dropping the "dtor" printing in the reflection dump.
Dtors are just one of many magic methods, I don't think there's
a point in explicitly highlighting them, when the name is already
unambiguous.
For division (rather than modulus) we should check the double
value, otherwise the result might be zero after integer truncation,
but not zero as a floating point value.
This reverts commit bb43a3822e.
After thinking about this a bit more, this is now going to be
a complete solution for the "readonly properties" case, for example:
unset($foo->readOnly->bar);
should also be legal and
$foo->readOnly['bar'] = 42;
should also be legal if $foo->readOnly is not an array but an
ArrayAccess object.
I think it may be better to distinguish better on the BP_VAR flag
level. Reverting for now.
$a->b->c = 'd';
is now compiled the same way as
$b = $a->b;
$b->c = 'd';
That is, we perform a read fetch on $a->b, rather than a write
fetch.
This is possible, because PHP 8 removed auto-vivification support
for objects, so $a->b->c = 'd' may no longer modify $a->b proper
(i.e. not counting interior mutability of the object).
Closes GH-5250.
* PHP-7.4:
Check asserts early
identation fix
Call global code of preloaded script in global context
Avoid "Anonymous class wasn't preloaded" error by lazely loading of not preloaded part of a preloaded script
Currently, trait methods are aliased will continue to use the
original function name. In a few places in the codebase, we will
try to look up the actual method name instead. However, this does
not work if an aliased method is used indirectly
(https://bugs.php.net/bug.php?id=69180).
I think it would be better to instead actually change the method
name to the alias. This is in principle easy: We have to allow
function_name to be changed even if op array is otherwise shared
(similar to static_variables). This means we need to addref/release
the function_name separately, but I don't think there is a
performance concern here (especially as everything is usually
interned).
There is a bit of complication in opcache, where we need to make
sure that the function name is released the correct number of times
(interning may overwrite the name in the original op_array, but we
need to release it as many times as the op_array is shared).
Fixes bug #69180.
Fixes bug #74939.
Closes GH-5226.
As an exception, we allow "Type $foo = null" to occur before a
required parameter, because this pattern was used as a replacement
for nullable types in PHP versions older than 7.1.
Closes GH-5067.
In order of preference, the generated name will be:
new class extends ParentClass {};
// -> ParentClass@anonymous
new class implements FirstInterface, SecondInterface {};
// -> FirstInterface@anonymous
new class {};
// -> class@anonymous
This is intended to display a more useful class name in error messages
and stack traces, and thus make debugging easier.
Closes GH-5153.
RFC: https://wiki.php.net/rfc/static_return_type
The "static" type is represented as MAY_BE_STATIC, rather than
a class type like "self" and "parent", as it has special
resolution semantics, and cannot be cached in the runtime cache.
Closes GH-5062.
This helps to avoid unnecessary IS_REFERENCE checks.
This changes some notices "Only variables should be passed by reference" to exception "Cannot pass parameter %d by reference".
Also, for consistency, compile-time fatal error "Only variables can be passed by reference" was converted to exception "Cannot pass parameter %d by reference"
This is equivalent to checking ce->num_interfaces. The only subtle
moment is during inheritance, where num_interface may change when
parent interfaces are inherited. The check in zend_do_link_class
thus uses "interfaces", not "ce->num_interfaces".
Internal constants can be marked as CONST_DEPRECATED, in which
case accessing them will throw a deprecation warning.
For now this is only supported on global constants, not class
constants. Complain to me if you need to deprecate a class
constant...
Closes GH-5072.
Instead of having a completely independent encoding for type list
entries. This is going to use more memory, but I'm not particularly
concerned about that, as type unions that contain multiple classes
should be uncommon. On the other hand, this allows us to treat
top-level types and types inside lists mostly the same.
A new ZEND_TYPE_FOREACH macros allows to transparently treat list
and non-list types the same way. I'm not using it everywhere it could be
used for now, just the places that seemed most obvious.
Of course, this will make any future type system changes much simpler,
as it will not be necessary to duplicate all logic two times.
Throw a compile error for "static" references instead, where it
isn't already the case.
Also extract the code that does that -- we have quite a few places
where we get a const class ref and require it to be default.
Also generate a fatal error if a collision occurs in zend_compile.
This is not perfect, because collisions might still be introduced
via opcache, if one file is included multiple times during a request,
invalidate in the meantime and recompiled by different processes.
This still needs to be addressed, but this patch fixes the much
more common case of collisions occuring when opcache is not used.
Fixes bug #78903.
We need to make sure that trait methods with static variables
allocate a separate MAP slot for the static variables pointer,
rather than working in-place.
I wasn't able to create a simple reproducer for this. General approach
is the same as for anonymous classes: If the key is already used, reuse
the old definition.
During preloading, check that all classes that have been included
as part of the preload script itself (rather than through opcache_compile_file)
can actually be preloaded, i.e. satisfy Windows restrictions, have
resolved initializers and resolved property types. When resolving
initializers and property types, also autoload additional classes.
Because of this, the resolution runs in a loop.
When we change back the bucket key on a class linking failure,
make sure to reload the bucket pointer, as the class table may
have been reallocated in the meantime.
Also remove a bogus bucket key change in anon class registration:
We don't actually rename the class in this case anymore, the RTD
key is already the final name.
The "return" in the for loop should have been a break on the switch,
otherwise the result is just ignored... but because it prevents
evaluation of the other operand, it also violates the invariant that
everything has been constant evaluated, resulting in an assertion
failure.
The for loop isn't correct in any case though, because it's not legal
to determine the result based on just the second operand, as the
first one may have a side-effect that cannot be optimized away.
Optimizations such as specializations for is_resource were first added in
dfb4f6b38d9efedafab7d2d98b9333715561256
I don't see any mention of is_scalar (and optimizing it) in the commit history,
or in prior PRs on github, or searching for is_scalar in externals.io
According to RFC: https://wiki.php.net/rfc/union_types_v2
The type representation now makes use of both the pointer payload
and the type mask at the same time. Additionall, zend_type_list is
introduced as a new kind of pointer payload, which is used to store
multiple class types. Each of the class types is a tagged pointer,
which may be either a class name or class entry. The latter is only
used for typed properties, while arguments/returns will instead use
cache slots. A type list can contain a mix of both names and CEs at
the same time, as not all classes may be resolvable.
One thing this is missing is support for union types in arginfo
and stubs, which I want to handle separately.
I've also dropped the special object code from the JIT implementation
for now -- I plan to add this back in a different form at a later time.
For now I did not want to include non-trivial JIT changes together
with large functional changes.
Another possible piece of follow-up work is to implement "iterable"
as an internal alias for "array|Traversable". I believe this will
eliminate quite a few special-cases that had to be implemented.
Closes GH-4838.
We now store the pointer payload and the type mask separately. This
is in preparation for union types, where we will be using both at
the same time.
To avoid increasing the size of arginfo structures, the
pass_by_reference and is_variadic fields are now stored as part of
the type_mask (8-bit are reserved for custom use).
Different types of pointer payloads are distinguished based on bits
in the type_mask.
We have a number of "types" like integer which are not actually
supported as builtin types -- instead they are silently interpreted
as class types.
I've seen this cause confusion a few types already. This change adds
a warning in this case. In the unlikely case that someone legitimately
wants to type against an integer class, the warning can be suppressed
by writing \integer or "use integer", or using Integer (this warning
will only trigger for lowercase spellings).
Closes GH-4815.
We are now guaranteed that $this always exists inside methods, as
well as insides closures (if they use $this at all).
This removes checks for $this existence from the individual object
opcodes. Instead ZEND_FETCH_THIS is used in the cases where $this
is not guaranteed to exist, which is mainly the pseudo-main scope.
Closes GH-3822.
Avoid need of insertion NOP opcoes between unrelated SMART BRANCH instruction and following JMPZ/JMPNZ.
Now instead of checking the opcode of following instruction, the same information is encoded into SMART BRANH result_type.
After fixing the int->double coercion case, this is already verified
at compile-time, so there is no need to redo this type check on
every call.
Only perform the type check every time for the case of AST default
values.
This originally manifested as a leak in oss-fuzz #18000. The following
is a reduced test case:
<?php
[
5 => 1,
"foo" > 1,
" " => "" == 0
];
<<<BAR
$x
BAR;
Because this particular error condition did not return T_ERROR,
EG(exception) was set while performing binary operation constant
evaluation, which checks exceptions for cast failures.
Instead of adding this indirect test case, I'm adding an assertion
that the lexer has to return T_ERROR if EG(exception) is set.