[PFA 3/n] Support PFA in const exprs (#22785)

* Use a pointer to the declaring opline->lineno as unique addr, instead of the declaring opline itself

zend_partial_create() takes a 'zend_op* declaring_opline' parameter for the
purpose of building a cache key: the opline address is unique to this PFA as
long as it's in SHM.

However the declaring opline is not available when evaluating a PFA AST. Replace
the parameter by 'uint32_t* declaring_lineno'. Callers can pass
'&zend_op->lineno' or '&ast->lineno': Both are unique to the related PFA.

* zend_partial_create(): Remove the dependency on the declaring op_array

The declaring op_array is used for multiple purposes:
 * Generating a PFA name
 * Finding the filename
 * Finding whether op_array is cached, and therefore whether the PFA should be
   cached too

However it's not available when evaluating a PFA AST.

Remove the declaring_op_array argument, and pass the relevant information
directly:
 * Generate the PFA name at compile time, pass it as argument to zend_partial_create()
 * Pass the filename and a cacheable flag as argument to zend_partial_create()

* Support for PFA in constant expressions
This commit is contained in:
Arnaud Le Blanc authored and GitHub committed 2026-07-27 17:59:25 +02:00
1 parent 99d2e3c166
commit 2d359a40b7
36 files changed
+901 -308

No files matched your search

+2 -1
View File
@@ -26,6 +26,7 @@
#include "zend_execute.h"
#include "zend_vm.h"
#include "zend_extensions.h"
#include "zend_partial.h"
#define DEBUG_COMPACT_LITERALS 0
@@ -747,7 +748,7 @@ void zend_optimizer_compact_literals(zend_op_array *op_array, zend_optimizer_ctx
}
break;
case ZEND_CALLABLE_CONVERT_PARTIAL:
opline->op1.num = cache_size;
opline->extended_value = cache_size | (opline->extended_value & ZEND_PARTIAL_FLAGS);
cache_size += 2 * sizeof(void *);
break;
}
@@ -0,0 +1,54 @@
--TEST--
PFA in constexpr 001
--FILE--
<?php
function f($a = g("foo", ?)) {
return $a(1);
}
function g(string $a, int $b) {
return [$a, $b];
}
class C {
static function f($a = self::g("foo", ?)) {
return $a(1);
}
static function g(string $a, int $b) {
return [$a, $b];
}
}
var_dump(f());
var_dump(f());
var_dump(C::f());
var_dump(C::f());
?>
--EXPECT--
array(2) {
[0]=>
string(3) "foo"
[1]=>
int(1)
}
array(2) {
[0]=>
string(3) "foo"
[1]=>
int(1)
}
array(2) {
[0]=>
string(3) "foo"
[1]=>
int(1)
}
array(2) {
[0]=>
string(3) "foo"
[1]=>
int(1)
}
@@ -0,0 +1,22 @@
--TEST--
PFA in constexpr: non-constexpr arg
--FILE--
<?php
function f($a = g(new stdClass, $x, ?)) {
return $a(1);
}
function g(string $a, int $b) {
return [$a, $b];
}
try {
var_dump(f());
} catch (Error $e) {
echo $e::class, ": ", $e->getMessage(), " on line ", $e->getLine();
}
?>
--EXPECTF--
Fatal error: Constant expression contains invalid operations in %s on line %d
@@ -0,0 +1,25 @@
--TEST--
PFA in constexpr: named args
--FILE--
<?php
function f($a = g(c: ?, ...)) {
return $a(1.5, b: 3);
}
function g(string $a = 'hello', int $b = 2, float $c = 0) {
return [$a, $b, $c];
}
var_dump(f());
?>
--EXPECT--
array(3) {
[0]=>
string(5) "hello"
[1]=>
int(3)
[2]=>
float(1.5)
}
@@ -0,0 +1,28 @@
--TEST--
PFA in constexpr: extra named args
--FILE--
<?php
function f($a = g(?, foo: 'bar', ...)) {
return $a(1, bar: 'baz');
}
function g(...$args) {
return [$args];
}
var_dump(f());
?>
--EXPECT--
array(1) {
[0]=>
array(3) {
[0]=>
int(1)
["foo"]=>
string(3) "bar"
["bar"]=>
string(3) "baz"
}
}
@@ -0,0 +1,13 @@
--TEST--
PFA in constexpr: variadic placeholder must be after positional params
--FILE--
<?php
function f($a = g(..., 1)) {
}
f();
?>
--EXPECTF--
Fatal error: Variadic placeholder must be last in %s on line %d
@@ -0,0 +1,13 @@
--TEST--
PFA in constexpr: variadic placeholder must be after named params
--FILE--
<?php
function f($a = g(..., foo: 1)) {
}
f();
?>
--EXPECTF--
Fatal error: Variadic placeholder must be last in %s on line %d
@@ -0,0 +1,13 @@
--TEST--
PFA in constexpr: variadic placeholder may only appear once
--FILE--
<?php
function f($a = g(..., ...)) {
}
f();
?>
--EXPECTF--
Fatal error: Variadic placeholder may only appear once in %s on line %d
@@ -0,0 +1,13 @@
--TEST--
PFA in constexpr: variadic placeholder not allowed to be named
--FILE--
<?php
function f($a = g(foo: ...)) {
}
f();
?>
--EXPECTF--
Parse error: syntax error, unexpected token "..." in %s on line %d
@@ -0,0 +1,20 @@
--TEST--
PFA in constexpr: error during partial creation
--FILE--
<?php
function f($a = g(?)) {
}
function g($a, $b) {
}
try {
f();
} catch (Error $e) {
echo $e::class, ": ", $e->getMessage(), "\n";
}
?>
--EXPECT--
ArgumentCountError: Partial application of g() expects exactly 2 arguments, 1 given
@@ -0,0 +1,43 @@
--TEST--
PFA in constexpr: error during arg list evaluation
--FILE--
<?php
function f($a = g(strlen(?), e: strlen(?), a: strlen(?), ...)) {
}
function g($a, $b, $c, $d, $e) {
}
function h($a = i(strlen(?), a: strlen(?), b: invalid(?), ...)) {
}
function i(...$args) {
}
function j($a = g(c: new stdClass, ...), $b = g(c: MISSING_CONST + 1, ...)) {
}
try {
f();
} catch (Error $e) {
echo $e::class, ": ", $e->getMessage(), "\n";
}
try {
h();
} catch (Error $e) {
echo $e::class, ": ", $e->getMessage(), "\n";
}
try {
j();
} catch (Error $e) {
echo $e::class, ": ", $e->getMessage(), "\n";
}
?>
--EXPECT--
Error: Named parameter $a overwrites previous argument
Error: Call to undefined function invalid()
Error: Undefined constant "MISSING_CONST"
@@ -0,0 +1,38 @@
--TEST--
PFA in constexpr: nested const expr
--FILE--
<?php
function f($a = g(new stdClass, g(...), ...)) {
return $a(1);
}
function g($a, $b, $c) {
return [$a, $b, $c];
}
var_dump(f());
?>
--EXPECTF--
array(3) {
[0]=>
object(stdClass)#%d (0) {
}
[1]=>
object(Closure)#%d (2) {
["function"]=>
string(1) "g"
["parameter"]=>
array(3) {
["$a"]=>
string(10) "<required>"
["$b"]=>
string(10) "<required>"
["$c"]=>
string(10) "<required>"
}
}
[2]=>
int(1)
}
@@ -0,0 +1,45 @@
--TEST--
PFA in constexpr: sites
--FILE--
<?php
#[Attribute]
class A {
function __construct(public mixed $fn) {}
}
#[A(strlen(?))]
class C {
const CC = strlen(?);
#[A(strlen(?))]
function f($arg = strlen(?)) {
return $arg;
}
}
const CC = strlen(?);
echo "# Class const\n";
var_dump((C::CC)('hello'));
echo "# Const\n";
var_dump((CC)('hello'));
echo "# Class attr\n";
var_dump((new ReflectionClass(C::class)->getAttributes(A::class)[0]->newInstance()->fn)('hello'));
echo "# Method attr\n";
var_dump((new ReflectionMethod(C::class, 'f')->getAttributes(A::class)[0]->newInstance()->fn)('hello'));
echo "# Method default value\n";
var_dump((new C()->f())('hello'));
?>
--EXPECT--
# Class const
int(5)
# Const
int(5)
# Class attr
int(5)
# Method attr
int(5)
# Method default value
int(5)
@@ -0,0 +1,20 @@
<?php
#[Attribute]
class A {
function __construct(public mixed $fn) {}
}
#[A(strlen(?))]
class C {
const CC = strlen(?);
#[A(strlen(?))]
function f($arg = strlen(?)) {
return $arg;
}
}
const CC = strlen(?);
?>
@@ -0,0 +1,33 @@
--TEST--
PFA in constexpr: preloading
--INI--
opcache.enable=1
opcache.enable_cli=1
opcache.optimization_level=-1
opcache.preload={PWD}/constexpr_015.inc
--SKIPIF--
<?php
if (PHP_OS_FAMILY == 'Windows') die('skip Preloading is not supported on Windows');
?>
--FILE--
<?php
echo "# Class const\n";
var_dump((C::CC)('hello'));
echo "# Class attr\n";
var_dump((new ReflectionClass(C::class)->getAttributes(A::class)[0]->newInstance()->fn)('hello'));
echo "# Method attr\n";
var_dump((new ReflectionMethod(C::class, 'f')->getAttributes(A::class)[0]->newInstance()->fn)('hello'));
echo "# Method default value\n";
var_dump((new C()->f())('hello'));
?>
--EXPECT--
# Class const
int(5)
# Class attr
int(5)
# Method attr
int(5)
# Method default value
int(5)
@@ -37,7 +37,7 @@ $_main:
0005 INIT_FCALL_BY_NAME 2 string("foo")
0006 SEND_PLACEHOLDER 1
0007 SEND_PLACEHOLDER 2
0008 T1 = CALLABLE_CONVERT_PARTIAL %d
0008 T1 = CALLABLE_CONVERT_PARTIAL %d string("{closure:%s}")
0009 INIT_DYNAMIC_CALL 1 T1
0010 SEND_VAL_EX int(2) 1
0011 DO_FCALL
@@ -37,7 +37,7 @@ $_main:
0005 INIT_FCALL_BY_NAME 0 string("foo")
0006 SEND_PLACEHOLDER string("a")
0007 SEND_PLACEHOLDER string("b")
0008 T1 = CALLABLE_CONVERT_PARTIAL %d array(...)
0008 T1 = CALLABLE_CONVERT_PARTIAL %d string("{closure:%s}") array(...)
0009 INIT_DYNAMIC_CALL 1 T1
0010 SEND_VAL_EX int(2) 1
0011 DO_FCALL
@@ -36,7 +36,7 @@ $_main:
0004 DECLARE_FUNCTION string("foo") 0
0005 INIT_FCALL_BY_NAME 0 string("foo")
0006 SEND_VAL_EX int(1) string("a")
0007 T1 = CALLABLE_CONVERT_PARTIAL %d
0007 T1 = CALLABLE_CONVERT_PARTIAL %d string("{closure:%s}")
0008 INIT_DYNAMIC_CALL 1 T1
0009 SEND_VAL_EX int(2) 1
0010 DO_FCALL
@@ -32,7 +32,7 @@ $_main:
0004 DECLARE_FUNCTION string("foo") 0
0005 INIT_FCALL_BY_NAME 0 string("foo")
0006 SEND_VAL_EX int(2) string("b")
0007 T0 = CALLABLE_CONVERT_PARTIAL 3
0007 T0 = CALLABLE_CONVERT_PARTIAL %d string("{closure:%s}")
0008 INIT_DYNAMIC_CALL 1 T0
0009 SEND_VAL_EX int(1) 1
0010 DO_FCALL
@@ -1,7 +1,5 @@
--TEST--
PFA RFC examples: "Constant expressions" section
--XFAIL--
PFA in constant expressions not implemented yet
--FILE--
<?php
+137 -6
View File
@@ -25,6 +25,7 @@
#include "zend_closures.h"
#include "zend_constants.h"
#include "zend_enum.h"
#include "zend_partial.h"
ZEND_API zend_ast_process_t zend_ast_process = NULL;
@@ -60,6 +61,8 @@ ZEND_API zend_ast * ZEND_FASTCALL zend_ast_create_fcc(zend_ast *args) {
ast->kind = ZEND_AST_CALLABLE_CONVERT;
ast->attr = 0;
ast->lineno = CG(zend_lineno);
ast->filename = NULL;
ast->name = NULL;
ast->args = args;
ZEND_MAP_PTR_INIT(ast->fptr, NULL);
@@ -671,6 +674,82 @@ ZEND_API zend_result ZEND_FASTCALL zend_ast_evaluate_ex(
return r;
}
static zend_execute_data *zend_ast_evaluate_arg_list(
zend_function *func,
zend_ast_list *args_ast,
zend_class_entry *scope,
void *object_or_called_scope,
bool *short_circuited_ptr,
zend_ast_evaluate_ctx *ctx,
zend_array **named_positions_ptr,
bool *uses_variadic_placeholder
) {
zend_execute_data *frame = zend_vm_stack_push_call_frame_ex(
zend_vm_calc_used_stack(args_ast->children, func),
0, func, 0, object_or_called_scope);
for (uint32_t i = 0; i < args_ast->children; i++) {
zend_ast *arg_ast = args_ast->child[i];
uint32_t arg_num = i + 1;
zval *arg;
zend_string *arg_name = NULL;
if (arg_ast->kind == ZEND_AST_NAMED_ARG) {
void *cache_slot[2] = {0};
arg_name = zend_ast_get_str(arg_ast->child[0]);
arg = zend_handle_named_arg(&frame, arg_name, &arg_num, (void**)&cache_slot);
if (!arg) {
goto fail;
}
if (named_positions_ptr) {
if (!*named_positions_ptr) {
*named_positions_ptr = zend_new_array(0);
}
zval tmp;
ZVAL_LONG(&tmp, zend_hash_num_elements(*named_positions_ptr));
zend_hash_add(*named_positions_ptr, arg_name, &tmp);
}
arg_ast = arg_ast->child[1];
} else {
arg = ZEND_CALL_VAR_NUM(frame, ZEND_CALL_NUM_ARGS(frame));
}
if (arg_ast->kind == ZEND_AST_PLACEHOLDER_ARG) {
if (arg_ast->attr == ZEND_PLACEHOLDER_VARIADIC) {
if (uses_variadic_placeholder) {
*uses_variadic_placeholder = true;
}
continue;
} else {
Z_TYPE_INFO_P(arg) = _IS_PLACEHOLDER;
}
} else {
if (zend_ast_evaluate_ex(arg, arg_ast, scope, short_circuited_ptr, ctx) == FAILURE) {
ZVAL_UNDEF(arg);
goto fail;
}
}
if (!arg_name) {
ZEND_CALL_NUM_ARGS(frame)++;
}
}
return frame;
fail:
for (uint32_t i = 0, num_args = ZEND_CALL_NUM_ARGS(frame); i < num_args; i++) {
zval_ptr_dtor(ZEND_CALL_VAR_NUM(frame, i));
}
if (ZEND_CALL_INFO(frame) & ZEND_CALL_HAS_EXTRA_NAMED_PARAMS) {
zend_array_destroy(frame->extra_named_params);
}
zend_vm_stack_free_call_frame(frame);
if (named_positions_ptr && *named_positions_ptr) {
zend_array_destroy(*named_positions_ptr);
}
return NULL;
}
static zend_result ZEND_FASTCALL zend_ast_evaluate_inner(
zval *result,
zend_ast *ast,
@@ -1155,10 +1234,6 @@ static zend_result ZEND_FASTCALL zend_ast_evaluate_inner(
zend_ast_list *args = zend_ast_get_list(fcc_ast->args);
ZEND_ASSERT(args->children > 0);
if (args->children != 1 || args->child[0]->attr != ZEND_PLACEHOLDER_VARIADIC) {
/* TODO: PFAs */
zend_error_noreturn(E_COMPILE_ERROR, "Constant expression contains invalid operations");
}
switch (ast->kind) {
case ZEND_AST_CALL: {
@@ -1248,9 +1323,46 @@ static zend_result ZEND_FASTCALL zend_ast_evaluate_inner(
default: ZEND_UNREACHABLE();
}
zend_create_fake_closure(result, fptr, fptr->common.scope, called_scope, NULL);
bool is_fcc = args->children == 1 && args->child[0]->attr == ZEND_PLACEHOLDER_VARIADIC;
return SUCCESS;
if (is_fcc) {
zend_create_fake_closure(result, fptr, fptr->common.scope, called_scope, NULL);
return SUCCESS;
}
zend_array *named_positions = NULL;
bool uses_variadic_placeholder = false;
zend_execute_data *frame = zend_ast_evaluate_arg_list(
fptr, args, scope, called_scope, short_circuited_ptr, ctx,
&named_positions, &uses_variadic_placeholder);
if (!frame) {
ZEND_ASSERT(EG(exception));
return FAILURE;
}
void *cache_slot[2] = {0};
zend_array *extra_named_params = ZEND_CALL_INFO(frame) & ZEND_CALL_HAS_EXTRA_NAMED_PARAMS
? frame->extra_named_params
: NULL;
uint32_t flags = (fcc_ast->attr & ZEND_PARTIAL_CACHEABLE_IN_SHM);
if (uses_variadic_placeholder) {
flags |= ZEND_PARTIAL_USES_VARIADIC_PLACEHOLDER;
}
zend_partial_create(result, &frame->This, fptr,
ZEND_CALL_NUM_ARGS(frame), ZEND_CALL_ARG(frame, 1),
extra_named_params, named_positions,
fcc_ast->filename, &ast->lineno,
(void**)cache_slot, fcc_ast->name, flags);
if (named_positions) {
zend_array_release(named_positions);
}
if (extra_named_params) {
zend_array_release(extra_named_params);
}
zend_vm_stack_free_call_frame(frame);
return EG(exception) ? FAILURE : SUCCESS;
}
case ZEND_AST_OP_ARRAY:
{
@@ -1425,6 +1537,16 @@ static void* ZEND_FASTCALL zend_ast_tree_copy(zend_ast *ast, void *buf)
new->kind = old->kind;
new->attr = old->attr;
new->lineno = old->lineno;
if (old->filename) {
new->filename = zend_string_copy(old->filename);
} else {
new->filename = NULL;
}
if (old->name) {
new->name = zend_string_copy(old->name);
} else {
new->name = NULL;
}
ZEND_MAP_PTR_INIT(new->fptr, ZEND_MAP_PTR(old->fptr));
buf = (void*)((char*)buf + sizeof(zend_ast_fcc));
new->args = buf;
@@ -1525,6 +1647,12 @@ tail_call:
} else if (EXPECTED(ast->kind == ZEND_AST_CALLABLE_CONVERT)) {
zend_ast_fcc *fcc_ast = (zend_ast_fcc*) ast;
if (fcc_ast->filename) {
zend_string_release_ex(fcc_ast->filename, 0);
}
if (fcc_ast->name) {
zend_string_release_ex(fcc_ast->name, 0);
}
ast = fcc_ast->args;
goto tail_call;
}
@@ -1543,6 +1671,9 @@ ZEND_API void zend_ast_apply(zend_ast *ast, zend_ast_apply_func fn, void *contex
for (i = 0; i < list->children; ++i) {
fn(&list->child[i], context);
}
} else if (ast->kind == ZEND_AST_CALLABLE_CONVERT) {
zend_ast_fcc *fcc_ast = (zend_ast_fcc*)ast;
fn(&fcc_ast->args, context);
} else if (zend_ast_is_decl(ast)) {
/* Not implemented. */
ZEND_UNREACHABLE();
+2
View File
@@ -233,6 +233,8 @@ typedef struct _zend_ast_fcc {
zend_ast_kind kind; /* Type of the node (ZEND_AST_* enum constant) */
zend_ast_attr attr; /* Additional attribute, use depending on node type */
uint32_t lineno; /* Line number */
zend_string *filename;
zend_string *name;
zend_ast *args;
ZEND_MAP_PTR_DEF(zend_function *, fptr);
} zend_ast_fcc;
+91 -27
View File
@@ -40,6 +40,7 @@
#include "zend_call_stack.h"
#include "zend_frameless_function.h"
#include "zend_property_hooks.h"
#include "zend_partial.h"
#define SET_NODE(target, src) do { \
target ## _type = (src)->op_type; \
@@ -4088,7 +4089,50 @@ ZEND_API uint8_t zend_get_call_op(const zend_op *init_op, const zend_function *f
}
/* }}} */
static void zend_compile_call_partial(znode *result, uint32_t arg_count,
static zend_string *zend_compile_partial_name(const zend_op_array *declaring_op_array,
const uint32_t declaring_lineno)
{
/* We attempt to generate a name that hints at where the PFA was created,
* similarly to Closures (GH-13550).
* We do not attempt to make the name unique. */
zend_string *filename = declaring_op_array->filename;
uint32_t start_lineno = declaring_lineno;
zend_string *class = zend_empty_string;
zend_string *separator = zend_empty_string;
zend_string *function = filename;
const char *parens = "";
if (declaring_op_array->function_name) {
function = declaring_op_array->function_name;
if (declaring_op_array->fn_flags & ZEND_ACC_CLOSURE) {
/* If the parent function is a closure, don't redundantly
* add the classname and parentheses. */
} else {
parens = "()";
if (declaring_op_array->scope && declaring_op_array->scope->name) {
class = declaring_op_array->scope->name;
separator = ZSTR_KNOWN(ZEND_STR_PAAMAYIM_NEKUDOTAYIM);
}
}
}
zend_string *name = zend_strpprintf_unchecked(
0,
"{closure:pfa:%S%S%S%s:%" PRIu32 "}",
class,
separator,
function,
parens,
start_lineno
);
return name;
}
static void zend_compile_call_partial(znode *result, zend_ast_fcc *fcc_ast, uint32_t arg_count,
bool may_have_extra_named_args, bool uses_variadic_placeholder,
zval *named_positions, uint32_t opnum_init, const zend_function *fbc) {
@@ -4105,15 +4149,16 @@ static void zend_compile_call_partial(znode *result, uint32_t arg_count,
zend_op *opline = zend_emit_op_tmp(result, ZEND_CALLABLE_CONVERT_PARTIAL,
NULL, NULL);
opline->op1.num = zend_alloc_cache_slots(2);
opline->extended_value = zend_alloc_cache_slots(2);
if (may_have_extra_named_args) {
opline->extended_value = ZEND_FCALL_MAY_HAVE_EXTRA_NAMED_PARAMS;
}
if (uses_variadic_placeholder) {
opline->extended_value |= ZEND_FCALL_USES_VARIADIC_PLACEHOLDER;
opline->extended_value |= ZEND_PARTIAL_USES_VARIADIC_PLACEHOLDER;
}
zend_string *name = zend_compile_partial_name(CG(active_op_array), opline->lineno);
opline->op1.constant = zend_add_literal_string(&name);
opline->op1_type = IS_CONST;
if (!Z_ISUNDEF_P(named_positions)) {
opline->op2.constant = zend_add_literal(named_positions);
opline->op2_type = IS_CONST;
@@ -4156,7 +4201,8 @@ static bool zend_compile_call_common(znode *result, zend_ast *args_ast, const ze
return true;
}
args_ast = ((zend_ast_fcc*)args_ast)->args;
zend_ast_fcc *fcc_ast = (zend_ast_fcc*)args_ast;
args_ast = fcc_ast->args;
bool may_have_extra_named_args;
bool uses_variadic_placeholder;
@@ -4168,7 +4214,7 @@ static bool zend_compile_call_common(znode *result, zend_ast *args_ast, const ze
&may_have_extra_named_args, true, &uses_variadic_placeholder,
&named_positions);
zend_compile_call_partial(result, arg_count,
zend_compile_call_partial(result, fcc_ast, arg_count,
may_have_extra_named_args, uses_variadic_placeholder,
&named_positions, opnum_init, fbc);
@@ -11856,7 +11902,8 @@ static bool zend_is_allowed_in_const_expr(zend_ast_kind kind) /* {{{ */
|| kind == ZEND_AST_NAMED_ARG
|| kind == ZEND_AST_PROP || kind == ZEND_AST_NULLSAFE_PROP
|| kind == ZEND_AST_CLOSURE
|| kind == ZEND_AST_CALL || kind == ZEND_AST_STATIC_CALL || kind == ZEND_AST_CALLABLE_CONVERT;
|| kind == ZEND_AST_CALL || kind == ZEND_AST_STATIC_CALL || kind == ZEND_AST_CALLABLE_CONVERT
|| kind == ZEND_AST_PLACEHOLDER_ARG;
}
/* }}} */
@@ -12021,24 +12068,20 @@ static void zend_compile_const_expr_closure(zend_ast **ast_ptr)
static void zend_compile_const_expr_fcc(zend_ast **ast_ptr)
{
zend_ast **args_ast;
switch ((*ast_ptr)->kind) {
case ZEND_AST_CALL:
args_ast = &(*ast_ptr)->child[1];
break;
case ZEND_AST_STATIC_CALL:
args_ast = &(*ast_ptr)->child[2];
break;
default: ZEND_UNREACHABLE();
}
if ((*args_ast)->kind != ZEND_AST_CALLABLE_CONVERT) {
zend_ast *args_ast = zend_ast_call_get_args(*ast_ptr);
if (args_ast->kind != ZEND_AST_CALLABLE_CONVERT) {
zend_error_noreturn(E_COMPILE_ERROR, "Constant expression contains invalid operations");
}
zend_ast_list *args = zend_ast_get_list(((zend_ast_fcc*)*args_ast)->args);
if (args->children != 1 || args->child[0]->attr != ZEND_PLACEHOLDER_VARIADIC) {
// TODO: PFAs
zend_error_noreturn(E_COMPILE_ERROR, "Constant expression contains invalid operations");
zend_ast_fcc *fcc = (zend_ast_fcc*)args_ast;
zend_ast_list *args = zend_ast_get_list(fcc->args);
bool is_fcc = args->children == 1 && args->child[0]->attr == ZEND_PLACEHOLDER_VARIADIC;
if (!is_fcc) {
fcc->filename = zend_string_copy(CG(active_op_array)->filename);
fcc->name = zend_compile_partial_name(CG(active_op_array), fcc->lineno);
}
switch ((*ast_ptr)->kind) {
@@ -12080,6 +12123,7 @@ static void zend_compile_const_expr_args(zend_ast **ast_ptr)
{
zend_ast_list *list = zend_ast_get_list(*ast_ptr);
bool uses_named_args = false;
bool uses_variadic_placeholder = false;
for (uint32_t i = 0; i < list->children; i++) {
const zend_ast *arg = list->child[i];
if (arg->kind == ZEND_AST_UNPACK) {
@@ -12088,11 +12132,31 @@ static void zend_compile_const_expr_args(zend_ast **ast_ptr)
}
if (arg->kind == ZEND_AST_NAMED_ARG) {
uses_named_args = true;
} else if (uses_named_args) {
zend_error_noreturn(E_COMPILE_ERROR,
"Cannot use positional argument after named argument");
if (uses_variadic_placeholder) {
zend_error_noreturn(E_COMPILE_ERROR, "Variadic placeholder must be last");
}
} else {
bool is_variadic_placeholder = arg->kind == ZEND_AST_PLACEHOLDER_ARG
&& arg->attr == ZEND_PLACEHOLDER_VARIADIC;
if (uses_named_args && !is_variadic_placeholder) {
zend_error_noreturn(E_COMPILE_ERROR, "Cannot use positional argument after named argument");
}
if (uses_variadic_placeholder) {
if (is_variadic_placeholder) {
zend_error_noreturn(E_COMPILE_ERROR, "Variadic placeholder may only appear once");
} else {
zend_error_noreturn(E_COMPILE_ERROR, "Variadic placeholder must be last");
}
}
if (is_variadic_placeholder) {
uses_variadic_placeholder = true;
}
}
}
if (uses_named_args) {
list->attr = 1;
}
-1
View File
@@ -1121,7 +1121,6 @@ ZEND_API zend_string *zend_type_to_string(zend_type type);
#define ZEND_THROW_IS_EXPR 1u
#define ZEND_FCALL_MAY_HAVE_EXTRA_NAMED_PARAMS (1<<0)
#define ZEND_FCALL_USES_VARIADIC_PLACEHOLDER (1<<1)
/* The send mode, the is_variadic, the is_promoted, and the is_tentative flags are stored as part of zend_type */
#define _ZEND_SEND_MODE_SHIFT _ZEND_TYPE_EXTRA_FLAGS_SHIFT
+22 -62
View File
@@ -51,6 +51,7 @@
#include "zend_closures.h"
#include "zend_attributes.h"
#include "zend_exceptions.h"
#include "zend_partial.h"
#include "ext/opcache/ZendAccelerator.h"
static zend_always_inline bool Z_IS_PLACEHOLDER_P(const zval *p) {
@@ -502,49 +503,6 @@ static zend_ast *zp_param_attributes_to_ast(zend_function *function,
return attributes_ast;
}
static zend_string *zp_pfa_name(const zend_op_array *declaring_op_array,
const zend_op *declaring_opline)
{
/* We attempt to generate a name that hints at where the PFA was created,
* similarly to Closures (GH-13550).
* We do not attempt to make the name unique. */
zend_string *filename = declaring_op_array->filename;
uint32_t start_lineno = declaring_opline->lineno;
zend_string *class = zend_empty_string;
zend_string *separator = zend_empty_string;
zend_string *function = filename;
const char *parens = "";
if (declaring_op_array->function_name) {
function = declaring_op_array->function_name;
if (declaring_op_array->fn_flags & ZEND_ACC_CLOSURE) {
/* If the parent function is a closure, don't redundantly
* add the classname and parentheses. */
} else {
parens = "()";
if (declaring_op_array->scope && declaring_op_array->scope->name) {
class = declaring_op_array->scope->name;
separator = ZSTR_KNOWN(ZEND_STR_PAAMAYIM_NEKUDOTAYIM);
}
}
}
zend_string *name = zend_strpprintf_unchecked(
0,
"{closure:pfa:%S%S%S%s:%" PRIu32 "}",
class,
separator,
function,
parens,
start_lineno
);
return name;
}
/* Generate the AST for calling the actual function */
static zend_ast *zp_compile_forwarding_call(
zval *this_ptr, zend_function *function,
@@ -701,9 +659,9 @@ static uint32_t zp_compute_num_required(const zend_function *function,
static zend_op_array *zp_compile(zval *this_ptr, zend_function *function,
uint32_t argc, zval *argv, zend_array *extra_named_params,
const zend_array *named_positions,
const zend_op_array *declaring_op_array,
const zend_op *declaring_opline, void **cache_slot,
bool uses_variadic_placeholder) {
zend_string *declaring_filename,
const uint32_t *declaring_lineno_ptr, void **cache_slot,
zend_string *pfa_name, uint32_t flags) {
zend_op_array *op_array = NULL;
@@ -713,6 +671,8 @@ static zend_op_array *zp_compile(zval *this_ptr, zend_function *function,
return NULL;
}
bool uses_variadic_placeholder = flags & ZEND_PARTIAL_USES_VARIADIC_PLACEHOLDER;
if (UNEXPECTED(zp_args_check(function, argc, argv, extra_named_params, uses_variadic_placeholder) != SUCCESS)) {
ZEND_ASSERT(EG(exception));
return NULL;
@@ -1012,10 +972,8 @@ static zend_op_array *zp_compile(zval *this_ptr, zend_function *function,
}
#endif
zend_string *pfa_name = zp_pfa_name(declaring_op_array, declaring_opline);
op_array = zend_accel_compile_pfa(closure_ast, declaring_op_array,
declaring_opline, function, pfa_name);
op_array = zend_accel_compile_pfa(closure_ast, declaring_filename,
declaring_lineno_ptr, function, pfa_name, flags & ZEND_PARTIAL_CACHEABLE_IN_SHM);
zend_ast_destroy(closure_ast);
@@ -1040,9 +998,9 @@ error:
static const zend_op_array *zp_get_op_array(zval *this_ptr, zend_function *function,
uint32_t argc, zval *argv, zend_array *extra_named_params,
const zend_array *named_positions,
const zend_op_array *declaring_op_array,
const zend_op *declaring_opline, void **cache_slot,
bool uses_variadic_placeholder) {
zend_string *declaring_filename,
const uint32_t *declaring_lineno_ptr, void **cache_slot,
zend_string *pfa_name, uint32_t flags) {
if (EXPECTED(function->type == ZEND_INTERNAL_FUNCTION
? cache_slot[0] == function
@@ -1051,13 +1009,13 @@ static const zend_op_array *zp_get_op_array(zval *this_ptr, zend_function *funct
return cache_slot[1];
}
const zend_op_array *op_array = zend_accel_pfa_cache_get(declaring_op_array,
declaring_opline, function);
const zend_op_array *op_array = zend_accel_pfa_cache_get(declaring_lineno_ptr, function,
flags & ZEND_PARTIAL_CACHEABLE_IN_SHM);
if (UNEXPECTED(!op_array)) {
op_array = zp_compile(this_ptr, function, argc, argv,
extra_named_params, named_positions, declaring_op_array, declaring_opline,
cache_slot, uses_variadic_placeholder);
extra_named_params, named_positions, declaring_filename, declaring_lineno_ptr,
cache_slot, pfa_name, flags);
}
if (EXPECTED(op_array) && !(function->common.fn_flags & ZEND_ACC_NEVER_CACHE)) {
@@ -1135,14 +1093,16 @@ static void zp_bind(zval *result, zend_function *function, uint32_t argc, zval *
void zend_partial_create(zval *result, zval *this_ptr, zend_function *function,
uint32_t argc, zval *argv, zend_array *extra_named_params,
const zend_array *named_positions,
const zend_op_array *declaring_op_array,
const zend_op *declaring_opline, void **cache_slot,
bool uses_variadic_placeholder) {
zend_string *declaring_filename,
const uint32_t *declaring_lineno_ptr, void **cache_slot,
zend_string *pfa_name, uint32_t flags) {
ZEND_ASSERT(pfa_name);
const zend_op_array *op_array = zp_get_op_array(this_ptr, function, argc, argv,
extra_named_params, named_positions,
declaring_op_array, declaring_opline,
cache_slot, uses_variadic_placeholder);
declaring_filename, declaring_lineno_ptr,
cache_slot, pfa_name, flags);
if (UNEXPECTED(!op_array)) {
ZEND_ASSERT(EG(exception));
+13 -3
View File
@@ -21,12 +21,22 @@
BEGIN_EXTERN_C()
#define ZEND_PARTIAL_USES_VARIADIC_PLACEHOLDER (1<<0)
/* Whether the PFA is cacheable in SHM. This flag is set only when the op_array that declares the PFA is itself in SHM,
* for two reasons: Avoids SHM churn, and ensures that the PFA cache key is unique. */
#define ZEND_PARTIAL_CACHEABLE_IN_SHM (1<<1)
#define ZEND_PARTIAL_FLAGS (ZEND_PARTIAL_USES_VARIADIC_PLACEHOLDER|ZEND_PARTIAL_CACHEABLE_IN_SHM)
/* Create a partial application of 'function'
* 'declaring_lineno_ptr' should be a pointer the zend_op.lineno or
* zend_ast.lineno that declares the PFA. The address is used to build a cache
* key. */
void zend_partial_create(zval *result, zval *this_ptr, zend_function *function,
uint32_t argc, zval *argv, zend_array *extra_named_params,
const zend_array *named_positions,
const zend_op_array *declaring_op_array,
const zend_op *declaring_opline, void **cache_slot,
bool uses_variadic_placeholder);
zend_string *declaring_filename,
const uint32_t *declaring_lineno_ptr, void **cache_slot,
zend_string *pfa_name, uint32_t flags);
void zend_partial_op_array_dtor(zval *pDest);
+5 -4
View File
@@ -9866,14 +9866,15 @@ ZEND_VM_HANDLER(202, ZEND_CALLABLE_CONVERT, UNUSED, UNUSED, NUM|CACHE_SLOT)
ZEND_VM_NEXT_OPCODE();
}
ZEND_VM_HANDLER(212, ZEND_CALLABLE_CONVERT_PARTIAL, CACHE_SLOT, CONST|UNUSED, NUM)
ZEND_VM_HANDLER(212, ZEND_CALLABLE_CONVERT_PARTIAL, CONST, CONST|UNUSED, NUM)
{
USE_OPLINE
SAVE_OPLINE();
zend_execute_data *call = EX(call);
void **cache_slot = CACHE_ADDR(opline->op1.num);
void **cache_slot = CACHE_ADDR(opline->extended_value & ~ZEND_PARTIAL_FLAGS);
zval *named_positions = GET_OP2_ZVAL_PTR();
zend_string *pfa_name = Z_STR_P(GET_OP1_ZVAL_PTR());
zend_partial_create(EX_VAR(opline->result.var),
&call->This, call->func,
@@ -9881,8 +9882,8 @@ ZEND_VM_HANDLER(212, ZEND_CALLABLE_CONVERT_PARTIAL, CACHE_SLOT, CONST|UNUSED, NU
(ZEND_CALL_INFO(call) & ZEND_CALL_HAS_EXTRA_NAMED_PARAMS) ?
call->extra_named_params : NULL,
OP2_TYPE == IS_CONST ? Z_ARRVAL_P(named_positions) : NULL,
&EX(func)->op_array, opline, cache_slot,
opline->extended_value & ZEND_FCALL_USES_VARIADIC_PLACEHOLDER);
EX(func)->op_array.filename, &opline->lineno, cache_slot,
pfa_name, opline->extended_value & ZEND_PARTIAL_FLAGS);
if (ZEND_CALL_INFO(call) & ZEND_CALL_HAS_EXTRA_NAMED_PARAMS) {
zend_array_release(call->extra_named_params);
+176 -172
View File
@@ -4273,45 +4273,6 @@ recv_init_check_type:
ZEND_VM_NEXT_OPCODE();
}
static ZEND_OPCODE_HANDLER_RET ZEND_OPCODE_HANDLER_FUNC_CCONV ZEND_CALLABLE_CONVERT_PARTIAL_SPEC_CONST_HANDLER(ZEND_OPCODE_HANDLER_ARGS)
{
USE_OPLINE
SAVE_OPLINE();
zend_execute_data *call = EX(call);
void **cache_slot = CACHE_ADDR(opline->op1.num);
zval *named_positions = RT_CONSTANT(opline, opline->op2);
zend_partial_create(EX_VAR(opline->result.var),
&call->This, call->func,
ZEND_CALL_NUM_ARGS(call), ZEND_CALL_ARG(call, 1),
(ZEND_CALL_INFO(call) & ZEND_CALL_HAS_EXTRA_NAMED_PARAMS) ?
call->extra_named_params : NULL,
IS_CONST == IS_CONST ? Z_ARRVAL_P(named_positions) : NULL,
&EX(func)->op_array, opline, cache_slot,
opline->extended_value & ZEND_FCALL_USES_VARIADIC_PLACEHOLDER);
if (ZEND_CALL_INFO(call) & ZEND_CALL_HAS_EXTRA_NAMED_PARAMS) {
zend_array_release(call->extra_named_params);
}
if ((call->func->common.fn_flags & ZEND_ACC_CALL_VIA_TRAMPOLINE)) {
zend_free_trampoline(call->func);
}
if (ZEND_CALL_INFO(call) & ZEND_CALL_RELEASE_THIS) {
OBJ_RELEASE(Z_OBJ(call->This));
} else if (ZEND_CALL_INFO(call) & ZEND_CALL_CLOSURE) {
OBJ_RELEASE(ZEND_CLOSURE_OBJECT(call->func));
}
EX(call) = call->prev_execute_data;
zend_vm_stack_free_call_frame(call);
ZEND_VM_NEXT_OPCODE_CHECK_EXCEPTION();
}
static ZEND_OPCODE_HANDLER_RET ZEND_OPCODE_HANDLER_FUNC_CCONV ZEND_INIT_DYNAMIC_CALL_SPEC_TMP_HANDLER(ZEND_OPCODE_HANDLER_ARGS)
{
USE_OPLINE
@@ -4456,45 +4417,6 @@ static ZEND_OPCODE_HANDLER_RET ZEND_OPCODE_HANDLER_FUNC_CCONV ZEND_RECV_VARIADIC
ZEND_VM_NEXT_OPCODE_CHECK_EXCEPTION();
}
static ZEND_OPCODE_HANDLER_RET ZEND_OPCODE_HANDLER_FUNC_CCONV ZEND_CALLABLE_CONVERT_PARTIAL_SPEC_UNUSED_HANDLER(ZEND_OPCODE_HANDLER_ARGS)
{
USE_OPLINE
SAVE_OPLINE();
zend_execute_data *call = EX(call);
void **cache_slot = CACHE_ADDR(opline->op1.num);
zval *named_positions = NULL;
zend_partial_create(EX_VAR(opline->result.var),
&call->This, call->func,
ZEND_CALL_NUM_ARGS(call), ZEND_CALL_ARG(call, 1),
(ZEND_CALL_INFO(call) & ZEND_CALL_HAS_EXTRA_NAMED_PARAMS) ?
call->extra_named_params : NULL,
IS_UNUSED == IS_CONST ? Z_ARRVAL_P(named_positions) : NULL,
&EX(func)->op_array, opline, cache_slot,
opline->extended_value & ZEND_FCALL_USES_VARIADIC_PLACEHOLDER);
if (ZEND_CALL_INFO(call) & ZEND_CALL_HAS_EXTRA_NAMED_PARAMS) {
zend_array_release(call->extra_named_params);
}
if ((call->func->common.fn_flags & ZEND_ACC_CALL_VIA_TRAMPOLINE)) {
zend_free_trampoline(call->func);
}
if (ZEND_CALL_INFO(call) & ZEND_CALL_RELEASE_THIS) {
OBJ_RELEASE(Z_OBJ(call->This));
} else if (ZEND_CALL_INFO(call) & ZEND_CALL_CLOSURE) {
OBJ_RELEASE(ZEND_CLOSURE_OBJECT(call->func));
}
EX(call) = call->prev_execute_data;
zend_vm_stack_free_call_frame(call);
ZEND_VM_NEXT_OPCODE_CHECK_EXCEPTION();
}
static ZEND_OPCODE_HANDLER_RET ZEND_OPCODE_HANDLER_FUNC_CCONV ZEND_FRAMELESS_ICALL_1_SPEC_UNUSED_HANDLER(ZEND_OPCODE_HANDLER_ARGS)
{
USE_OPLINE
@@ -8741,6 +8663,46 @@ static ZEND_VM_COLD ZEND_OPCODE_HANDLER_RET ZEND_OPCODE_HANDLER_FUNC_CCONV ZEND_
ZEND_VM_SMART_BRANCH(0, 1);
}
static ZEND_OPCODE_HANDLER_RET ZEND_OPCODE_HANDLER_FUNC_CCONV ZEND_CALLABLE_CONVERT_PARTIAL_SPEC_CONST_CONST_HANDLER(ZEND_OPCODE_HANDLER_ARGS)
{
USE_OPLINE
SAVE_OPLINE();
zend_execute_data *call = EX(call);
void **cache_slot = CACHE_ADDR(opline->extended_value & ~ZEND_PARTIAL_FLAGS);
zval *named_positions = RT_CONSTANT(opline, opline->op2);
zend_string *pfa_name = Z_STR_P(RT_CONSTANT(opline, opline->op1));
zend_partial_create(EX_VAR(opline->result.var),
&call->This, call->func,
ZEND_CALL_NUM_ARGS(call), ZEND_CALL_ARG(call, 1),
(ZEND_CALL_INFO(call) & ZEND_CALL_HAS_EXTRA_NAMED_PARAMS) ?
call->extra_named_params : NULL,
IS_CONST == IS_CONST ? Z_ARRVAL_P(named_positions) : NULL,
EX(func)->op_array.filename, &opline->lineno, cache_slot,
pfa_name, opline->extended_value & ZEND_PARTIAL_FLAGS);
if (ZEND_CALL_INFO(call) & ZEND_CALL_HAS_EXTRA_NAMED_PARAMS) {
zend_array_release(call->extra_named_params);
}
if ((call->func->common.fn_flags & ZEND_ACC_CALL_VIA_TRAMPOLINE)) {
zend_free_trampoline(call->func);
}
if (ZEND_CALL_INFO(call) & ZEND_CALL_RELEASE_THIS) {
OBJ_RELEASE(Z_OBJ(call->This));
} else if (ZEND_CALL_INFO(call) & ZEND_CALL_CLOSURE) {
OBJ_RELEASE(ZEND_CLOSURE_OBJECT(call->func));
}
EX(call) = call->prev_execute_data;
zend_vm_stack_free_call_frame(call);
ZEND_VM_NEXT_OPCODE_CHECK_EXCEPTION();
}
static ZEND_VM_HOT ZEND_OPCODE_HANDLER_RET ZEND_OPCODE_HANDLER_FUNC_CCONV ZEND_ADD_SPEC_CONST_TMPVARCV_HANDLER(ZEND_OPCODE_HANDLER_ARGS)
{
USE_OPLINE
@@ -12067,6 +12029,46 @@ static ZEND_OPCODE_HANDLER_RET ZEND_OPCODE_HANDLER_FUNC_CCONV ZEND_FUNC_GET_ARGS
}
/* Contrary to what its name indicates, ZEND_COPY_TMP may receive and define references. */
static ZEND_OPCODE_HANDLER_RET ZEND_OPCODE_HANDLER_FUNC_CCONV ZEND_CALLABLE_CONVERT_PARTIAL_SPEC_CONST_UNUSED_HANDLER(ZEND_OPCODE_HANDLER_ARGS)
{
USE_OPLINE
SAVE_OPLINE();
zend_execute_data *call = EX(call);
void **cache_slot = CACHE_ADDR(opline->extended_value & ~ZEND_PARTIAL_FLAGS);
zval *named_positions = NULL;
zend_string *pfa_name = Z_STR_P(RT_CONSTANT(opline, opline->op1));
zend_partial_create(EX_VAR(opline->result.var),
&call->This, call->func,
ZEND_CALL_NUM_ARGS(call), ZEND_CALL_ARG(call, 1),
(ZEND_CALL_INFO(call) & ZEND_CALL_HAS_EXTRA_NAMED_PARAMS) ?
call->extra_named_params : NULL,
IS_UNUSED == IS_CONST ? Z_ARRVAL_P(named_positions) : NULL,
EX(func)->op_array.filename, &opline->lineno, cache_slot,
pfa_name, opline->extended_value & ZEND_PARTIAL_FLAGS);
if (ZEND_CALL_INFO(call) & ZEND_CALL_HAS_EXTRA_NAMED_PARAMS) {
zend_array_release(call->extra_named_params);
}
if ((call->func->common.fn_flags & ZEND_ACC_CALL_VIA_TRAMPOLINE)) {
zend_free_trampoline(call->func);
}
if (ZEND_CALL_INFO(call) & ZEND_CALL_RELEASE_THIS) {
OBJ_RELEASE(Z_OBJ(call->This));
} else if (ZEND_CALL_INFO(call) & ZEND_CALL_CLOSURE) {
OBJ_RELEASE(ZEND_CLOSURE_OBJECT(call->func));
}
EX(call) = call->prev_execute_data;
zend_vm_stack_free_call_frame(call);
ZEND_VM_NEXT_OPCODE_CHECK_EXCEPTION();
}
static ZEND_OPCODE_HANDLER_RET ZEND_OPCODE_HANDLER_FUNC_CCONV ZEND_INIT_PARENT_PROPERTY_HOOK_CALL_SPEC_CONST_UNUSED_HANDLER(ZEND_OPCODE_HANDLER_ARGS)
{
USE_OPLINE
@@ -57089,45 +57091,6 @@ recv_init_check_type:
ZEND_VM_NEXT_OPCODE();
}
static ZEND_OPCODE_HANDLER_RET ZEND_OPCODE_HANDLER_CCONV ZEND_CALLABLE_CONVERT_PARTIAL_SPEC_CONST_TAILCALL_HANDLER(ZEND_OPCODE_HANDLER_ARGS)
{
USE_OPLINE
SAVE_OPLINE();
zend_execute_data *call = EX(call);
void **cache_slot = CACHE_ADDR(opline->op1.num);
zval *named_positions = RT_CONSTANT(opline, opline->op2);
zend_partial_create(EX_VAR(opline->result.var),
&call->This, call->func,
ZEND_CALL_NUM_ARGS(call), ZEND_CALL_ARG(call, 1),
(ZEND_CALL_INFO(call) & ZEND_CALL_HAS_EXTRA_NAMED_PARAMS) ?
call->extra_named_params : NULL,
IS_CONST == IS_CONST ? Z_ARRVAL_P(named_positions) : NULL,
&EX(func)->op_array, opline, cache_slot,
opline->extended_value & ZEND_FCALL_USES_VARIADIC_PLACEHOLDER);
if (ZEND_CALL_INFO(call) & ZEND_CALL_HAS_EXTRA_NAMED_PARAMS) {
zend_array_release(call->extra_named_params);
}
if ((call->func->common.fn_flags & ZEND_ACC_CALL_VIA_TRAMPOLINE)) {
zend_free_trampoline(call->func);
}
if (ZEND_CALL_INFO(call) & ZEND_CALL_RELEASE_THIS) {
OBJ_RELEASE(Z_OBJ(call->This));
} else if (ZEND_CALL_INFO(call) & ZEND_CALL_CLOSURE) {
OBJ_RELEASE(ZEND_CLOSURE_OBJECT(call->func));
}
EX(call) = call->prev_execute_data;
zend_vm_stack_free_call_frame(call);
ZEND_VM_NEXT_OPCODE_CHECK_EXCEPTION();
}
static ZEND_OPCODE_HANDLER_RET ZEND_OPCODE_HANDLER_CCONV ZEND_INIT_DYNAMIC_CALL_SPEC_TMP_TAILCALL_HANDLER(ZEND_OPCODE_HANDLER_ARGS)
{
USE_OPLINE
@@ -57272,45 +57235,6 @@ static ZEND_OPCODE_HANDLER_RET ZEND_OPCODE_HANDLER_CCONV ZEND_RECV_VARIADIC_SPEC
ZEND_VM_NEXT_OPCODE_CHECK_EXCEPTION();
}
static ZEND_OPCODE_HANDLER_RET ZEND_OPCODE_HANDLER_CCONV ZEND_CALLABLE_CONVERT_PARTIAL_SPEC_UNUSED_TAILCALL_HANDLER(ZEND_OPCODE_HANDLER_ARGS)
{
USE_OPLINE
SAVE_OPLINE();
zend_execute_data *call = EX(call);
void **cache_slot = CACHE_ADDR(opline->op1.num);
zval *named_positions = NULL;
zend_partial_create(EX_VAR(opline->result.var),
&call->This, call->func,
ZEND_CALL_NUM_ARGS(call), ZEND_CALL_ARG(call, 1),
(ZEND_CALL_INFO(call) & ZEND_CALL_HAS_EXTRA_NAMED_PARAMS) ?
call->extra_named_params : NULL,
IS_UNUSED == IS_CONST ? Z_ARRVAL_P(named_positions) : NULL,
&EX(func)->op_array, opline, cache_slot,
opline->extended_value & ZEND_FCALL_USES_VARIADIC_PLACEHOLDER);
if (ZEND_CALL_INFO(call) & ZEND_CALL_HAS_EXTRA_NAMED_PARAMS) {
zend_array_release(call->extra_named_params);
}
if ((call->func->common.fn_flags & ZEND_ACC_CALL_VIA_TRAMPOLINE)) {
zend_free_trampoline(call->func);
}
if (ZEND_CALL_INFO(call) & ZEND_CALL_RELEASE_THIS) {
OBJ_RELEASE(Z_OBJ(call->This));
} else if (ZEND_CALL_INFO(call) & ZEND_CALL_CLOSURE) {
OBJ_RELEASE(ZEND_CLOSURE_OBJECT(call->func));
}
EX(call) = call->prev_execute_data;
zend_vm_stack_free_call_frame(call);
ZEND_VM_NEXT_OPCODE_CHECK_EXCEPTION();
}
static ZEND_OPCODE_HANDLER_RET ZEND_OPCODE_HANDLER_CCONV ZEND_FRAMELESS_ICALL_1_SPEC_UNUSED_TAILCALL_HANDLER(ZEND_OPCODE_HANDLER_ARGS)
{
USE_OPLINE
@@ -61557,6 +61481,46 @@ static ZEND_VM_COLD ZEND_OPCODE_HANDLER_RET ZEND_OPCODE_HANDLER_CCONV ZEND_IN_AR
ZEND_VM_SMART_BRANCH(0, 1);
}
static ZEND_OPCODE_HANDLER_RET ZEND_OPCODE_HANDLER_CCONV ZEND_CALLABLE_CONVERT_PARTIAL_SPEC_CONST_CONST_TAILCALL_HANDLER(ZEND_OPCODE_HANDLER_ARGS)
{
USE_OPLINE
SAVE_OPLINE();
zend_execute_data *call = EX(call);
void **cache_slot = CACHE_ADDR(opline->extended_value & ~ZEND_PARTIAL_FLAGS);
zval *named_positions = RT_CONSTANT(opline, opline->op2);
zend_string *pfa_name = Z_STR_P(RT_CONSTANT(opline, opline->op1));
zend_partial_create(EX_VAR(opline->result.var),
&call->This, call->func,
ZEND_CALL_NUM_ARGS(call), ZEND_CALL_ARG(call, 1),
(ZEND_CALL_INFO(call) & ZEND_CALL_HAS_EXTRA_NAMED_PARAMS) ?
call->extra_named_params : NULL,
IS_CONST == IS_CONST ? Z_ARRVAL_P(named_positions) : NULL,
EX(func)->op_array.filename, &opline->lineno, cache_slot,
pfa_name, opline->extended_value & ZEND_PARTIAL_FLAGS);
if (ZEND_CALL_INFO(call) & ZEND_CALL_HAS_EXTRA_NAMED_PARAMS) {
zend_array_release(call->extra_named_params);
}
if ((call->func->common.fn_flags & ZEND_ACC_CALL_VIA_TRAMPOLINE)) {
zend_free_trampoline(call->func);
}
if (ZEND_CALL_INFO(call) & ZEND_CALL_RELEASE_THIS) {
OBJ_RELEASE(Z_OBJ(call->This));
} else if (ZEND_CALL_INFO(call) & ZEND_CALL_CLOSURE) {
OBJ_RELEASE(ZEND_CLOSURE_OBJECT(call->func));
}
EX(call) = call->prev_execute_data;
zend_vm_stack_free_call_frame(call);
ZEND_VM_NEXT_OPCODE_CHECK_EXCEPTION();
}
static ZEND_VM_HOT ZEND_OPCODE_HANDLER_RET ZEND_OPCODE_HANDLER_CCONV ZEND_ADD_SPEC_CONST_TMPVARCV_TAILCALL_HANDLER(ZEND_OPCODE_HANDLER_ARGS)
{
USE_OPLINE
@@ -64781,6 +64745,46 @@ static ZEND_OPCODE_HANDLER_RET ZEND_OPCODE_HANDLER_CCONV ZEND_FUNC_GET_ARGS_SPEC
}
/* Contrary to what its name indicates, ZEND_COPY_TMP may receive and define references. */
static ZEND_OPCODE_HANDLER_RET ZEND_OPCODE_HANDLER_CCONV ZEND_CALLABLE_CONVERT_PARTIAL_SPEC_CONST_UNUSED_TAILCALL_HANDLER(ZEND_OPCODE_HANDLER_ARGS)
{
USE_OPLINE
SAVE_OPLINE();
zend_execute_data *call = EX(call);
void **cache_slot = CACHE_ADDR(opline->extended_value & ~ZEND_PARTIAL_FLAGS);
zval *named_positions = NULL;
zend_string *pfa_name = Z_STR_P(RT_CONSTANT(opline, opline->op1));
zend_partial_create(EX_VAR(opline->result.var),
&call->This, call->func,
ZEND_CALL_NUM_ARGS(call), ZEND_CALL_ARG(call, 1),
(ZEND_CALL_INFO(call) & ZEND_CALL_HAS_EXTRA_NAMED_PARAMS) ?
call->extra_named_params : NULL,
IS_UNUSED == IS_CONST ? Z_ARRVAL_P(named_positions) : NULL,
EX(func)->op_array.filename, &opline->lineno, cache_slot,
pfa_name, opline->extended_value & ZEND_PARTIAL_FLAGS);
if (ZEND_CALL_INFO(call) & ZEND_CALL_HAS_EXTRA_NAMED_PARAMS) {
zend_array_release(call->extra_named_params);
}
if ((call->func->common.fn_flags & ZEND_ACC_CALL_VIA_TRAMPOLINE)) {
zend_free_trampoline(call->func);
}
if (ZEND_CALL_INFO(call) & ZEND_CALL_RELEASE_THIS) {
OBJ_RELEASE(Z_OBJ(call->This));
} else if (ZEND_CALL_INFO(call) & ZEND_CALL_CLOSURE) {
OBJ_RELEASE(ZEND_CLOSURE_OBJECT(call->func));
}
EX(call) = call->prev_execute_data;
zend_vm_stack_free_call_frame(call);
ZEND_VM_NEXT_OPCODE_CHECK_EXCEPTION();
}
static ZEND_OPCODE_HANDLER_RET ZEND_OPCODE_HANDLER_CCONV ZEND_INIT_PARENT_PROPERTY_HOOK_CALL_SPEC_CONST_UNUSED_TAILCALL_HANDLER(ZEND_OPCODE_HANDLER_ARGS)
{
USE_OPLINE
@@ -109528,10 +109532,10 @@ ZEND_API void execute_ex(zend_execute_data *ex)
(void*)&&ZEND_INIT_PARENT_PROPERTY_HOOK_CALL_SPEC_CONST_UNUSED_LABEL,
(void*)&&ZEND_DECLARE_ATTRIBUTED_CONST_SPEC_CONST_CONST_LABEL,
(void*)&&ZEND_TYPE_ASSERT_SPEC_CONST_LABEL,
(void*)&&ZEND_CALLABLE_CONVERT_PARTIAL_SPEC_CONST_LABEL,
(void*)&&ZEND_CALLABLE_CONVERT_PARTIAL_SPEC_CONST_CONST_LABEL,
(void*)&&ZEND_NULL_LABEL,
(void*)&&ZEND_NULL_LABEL,
(void*)&&ZEND_CALLABLE_CONVERT_PARTIAL_SPEC_UNUSED_LABEL,
(void*)&&ZEND_CALLABLE_CONVERT_PARTIAL_SPEC_CONST_UNUSED_LABEL,
(void*)&&ZEND_NULL_LABEL,
(void*)&&ZEND_SEND_PLACEHOLDER_SPEC_UNUSED_CONST_LABEL,
(void*)&&ZEND_NULL_LABEL,
@@ -110942,11 +110946,6 @@ zend_leave_helper_SPEC_LABEL:
ZEND_RECV_INIT_SPEC_CONST_HANDLER(ZEND_OPCODE_HANDLER_ARGS_PASSTHRU);
VM_TRACE_OP_END(ZEND_RECV_INIT_SPEC_CONST)
HYBRID_BREAK();
HYBRID_CASE(ZEND_CALLABLE_CONVERT_PARTIAL_SPEC_CONST):
VM_TRACE(ZEND_CALLABLE_CONVERT_PARTIAL_SPEC_CONST)
ZEND_CALLABLE_CONVERT_PARTIAL_SPEC_CONST_HANDLER(ZEND_OPCODE_HANDLER_ARGS_PASSTHRU);
VM_TRACE_OP_END(ZEND_CALLABLE_CONVERT_PARTIAL_SPEC_CONST)
HYBRID_BREAK();
HYBRID_CASE(ZEND_INIT_DYNAMIC_CALL_SPEC_TMP):
VM_TRACE(ZEND_INIT_DYNAMIC_CALL_SPEC_TMP)
ZEND_INIT_DYNAMIC_CALL_SPEC_TMP_HANDLER(ZEND_OPCODE_HANDLER_ARGS_PASSTHRU);
@@ -110962,11 +110961,6 @@ zend_leave_helper_SPEC_LABEL:
ZEND_RECV_VARIADIC_SPEC_UNUSED_HANDLER(ZEND_OPCODE_HANDLER_ARGS_PASSTHRU);
VM_TRACE_OP_END(ZEND_RECV_VARIADIC_SPEC_UNUSED)
HYBRID_BREAK();
HYBRID_CASE(ZEND_CALLABLE_CONVERT_PARTIAL_SPEC_UNUSED):
VM_TRACE(ZEND_CALLABLE_CONVERT_PARTIAL_SPEC_UNUSED)
ZEND_CALLABLE_CONVERT_PARTIAL_SPEC_UNUSED_HANDLER(ZEND_OPCODE_HANDLER_ARGS_PASSTHRU);
VM_TRACE_OP_END(ZEND_CALLABLE_CONVERT_PARTIAL_SPEC_UNUSED)
HYBRID_BREAK();
HYBRID_CASE(ZEND_FRAMELESS_ICALL_1_SPEC_UNUSED):
VM_TRACE(ZEND_FRAMELESS_ICALL_1_SPEC_UNUSED)
ZEND_FRAMELESS_ICALL_1_SPEC_UNUSED_HANDLER(ZEND_OPCODE_HANDLER_ARGS_PASSTHRU);
@@ -111566,6 +111560,11 @@ zend_leave_helper_SPEC_LABEL:
ZEND_IN_ARRAY_SPEC_CONST_CONST_HANDLER(ZEND_OPCODE_HANDLER_ARGS_PASSTHRU);
VM_TRACE_OP_END(ZEND_IN_ARRAY_SPEC_CONST_CONST)
HYBRID_BREAK();
HYBRID_CASE(ZEND_CALLABLE_CONVERT_PARTIAL_SPEC_CONST_CONST):
VM_TRACE(ZEND_CALLABLE_CONVERT_PARTIAL_SPEC_CONST_CONST)
ZEND_CALLABLE_CONVERT_PARTIAL_SPEC_CONST_CONST_HANDLER(ZEND_OPCODE_HANDLER_ARGS_PASSTHRU);
VM_TRACE_OP_END(ZEND_CALLABLE_CONVERT_PARTIAL_SPEC_CONST_CONST)
HYBRID_BREAK();
HYBRID_CASE(ZEND_ADD_SPEC_CONST_TMPVARCV):
VM_TRACE(ZEND_ADD_SPEC_CONST_TMPVARCV)
ZEND_ADD_SPEC_CONST_TMPVARCV_HANDLER(ZEND_OPCODE_HANDLER_ARGS_PASSTHRU);
@@ -111926,6 +111925,11 @@ zend_leave_helper_SPEC_LABEL:
ZEND_FUNC_GET_ARGS_SPEC_CONST_UNUSED_HANDLER(ZEND_OPCODE_HANDLER_ARGS_PASSTHRU);
VM_TRACE_OP_END(ZEND_FUNC_GET_ARGS_SPEC_CONST_UNUSED)
HYBRID_BREAK();
HYBRID_CASE(ZEND_CALLABLE_CONVERT_PARTIAL_SPEC_CONST_UNUSED):
VM_TRACE(ZEND_CALLABLE_CONVERT_PARTIAL_SPEC_CONST_UNUSED)
ZEND_CALLABLE_CONVERT_PARTIAL_SPEC_CONST_UNUSED_HANDLER(ZEND_OPCODE_HANDLER_ARGS_PASSTHRU);
VM_TRACE_OP_END(ZEND_CALLABLE_CONVERT_PARTIAL_SPEC_CONST_UNUSED)
HYBRID_BREAK();
HYBRID_CASE(ZEND_INIT_PARENT_PROPERTY_HOOK_CALL_SPEC_CONST_UNUSED):
VM_TRACE(ZEND_INIT_PARENT_PROPERTY_HOOK_CALL_SPEC_CONST_UNUSED)
ZEND_INIT_PARENT_PROPERTY_HOOK_CALL_SPEC_CONST_UNUSED_HANDLER(ZEND_OPCODE_HANDLER_ARGS_PASSTHRU);
@@ -118496,10 +118500,10 @@ void zend_vm_init(void)
ZEND_INIT_PARENT_PROPERTY_HOOK_CALL_SPEC_CONST_UNUSED_HANDLER,
ZEND_DECLARE_ATTRIBUTED_CONST_SPEC_CONST_CONST_HANDLER,
ZEND_TYPE_ASSERT_SPEC_CONST_HANDLER,
ZEND_CALLABLE_CONVERT_PARTIAL_SPEC_CONST_HANDLER,
ZEND_CALLABLE_CONVERT_PARTIAL_SPEC_CONST_CONST_HANDLER,
ZEND_NULL_HANDLER,
ZEND_NULL_HANDLER,
ZEND_CALLABLE_CONVERT_PARTIAL_SPEC_UNUSED_HANDLER,
ZEND_CALLABLE_CONVERT_PARTIAL_SPEC_CONST_UNUSED_HANDLER,
ZEND_NULL_HANDLER,
ZEND_SEND_PLACEHOLDER_SPEC_UNUSED_CONST_HANDLER,
ZEND_NULL_HANDLER,
@@ -121984,10 +121988,10 @@ void zend_vm_init(void)
ZEND_INIT_PARENT_PROPERTY_HOOK_CALL_SPEC_CONST_UNUSED_TAILCALL_HANDLER,
ZEND_DECLARE_ATTRIBUTED_CONST_SPEC_CONST_CONST_TAILCALL_HANDLER,
ZEND_TYPE_ASSERT_SPEC_CONST_TAILCALL_HANDLER,
ZEND_CALLABLE_CONVERT_PARTIAL_SPEC_CONST_TAILCALL_HANDLER,
ZEND_CALLABLE_CONVERT_PARTIAL_SPEC_CONST_CONST_TAILCALL_HANDLER,
ZEND_NULL_TAILCALL_HANDLER,
ZEND_NULL_TAILCALL_HANDLER,
ZEND_CALLABLE_CONVERT_PARTIAL_SPEC_UNUSED_TAILCALL_HANDLER,
ZEND_CALLABLE_CONVERT_PARTIAL_SPEC_CONST_UNUSED_TAILCALL_HANDLER,
ZEND_NULL_TAILCALL_HANDLER,
ZEND_SEND_PLACEHOLDER_SPEC_UNUSED_CONST_TAILCALL_HANDLER,
ZEND_NULL_TAILCALL_HANDLER,
+2 -2
View File
@@ -1087,8 +1087,8 @@
_(2556, ZEND_INIT_PARENT_PROPERTY_HOOK_CALL_SPEC_CONST_UNUSED) \
_(2557, ZEND_DECLARE_ATTRIBUTED_CONST_SPEC_CONST_CONST) \
_(2558, ZEND_TYPE_ASSERT_SPEC_CONST) \
_(2559, ZEND_CALLABLE_CONVERT_PARTIAL_SPEC_CONST) \
_(2562, ZEND_CALLABLE_CONVERT_PARTIAL_SPEC_UNUSED) \
_(2559, ZEND_CALLABLE_CONVERT_PARTIAL_SPEC_CONST_CONST) \
_(2562, ZEND_CALLABLE_CONVERT_PARTIAL_SPEC_CONST_UNUSED) \
_(2564, ZEND_SEND_PLACEHOLDER_SPEC_UNUSED_CONST) \
_(2567, ZEND_SEND_PLACEHOLDER_SPEC_UNUSED_UNUSED) \
_(2569, ZEND_INIT_FCALL_OFFSET_SPEC_CONST) \
+1 -1
View File
@@ -451,7 +451,7 @@ static uint32_t zend_vm_opcodes_flags[214] = {
0x01001103,
0x00000303,
0x01000003,
0x010003a0,
0x01000303,
0x00001301,
};
+17 -15
View File
@@ -2024,7 +2024,7 @@ static char *zend_accel_uintptr_hex(char *dest, uintptr_t n)
* a scheme, except file:// and phar://. */
#define PFA_KEY_PREFIX "pfa://"
static zend_string *zend_accel_pfa_key(const zend_op *declaring_opline,
static zend_string *zend_accel_pfa_key(const uint32_t *declaring_lineno_ptr,
const zend_function *called_function)
{
const size_t max_key_len = strlen(PFA_KEY_PREFIX) + (sizeof(uintptr_t)*2) + strlen(":") + (sizeof(uintptr_t)*2);
@@ -2032,7 +2032,7 @@ static zend_string *zend_accel_pfa_key(const zend_op *declaring_opline,
char *dest = ZSTR_VAL(key);
dest = zend_mempcpy(dest, PFA_KEY_PREFIX, strlen(PFA_KEY_PREFIX));
dest = zend_accel_uintptr_hex(dest, (uintptr_t)declaring_opline);
dest = zend_accel_uintptr_hex(dest, (uintptr_t)declaring_lineno_ptr);
*dest++ = ':';
const void *ptr;
@@ -2054,17 +2054,17 @@ static zend_string *zend_accel_pfa_key(const zend_op *declaring_opline,
return key;
}
const zend_op_array *zend_accel_pfa_cache_get(const zend_op_array *declaring_op_array,
const zend_op *declaring_opline, const zend_function *called_function)
const zend_op_array *zend_accel_pfa_cache_get(
const uint32_t *declaring_lineno_ptr, const zend_function *called_function, bool cacheable_in_shm)
{
zend_string *key = zend_accel_pfa_key(declaring_opline, called_function);
zend_string *key = zend_accel_pfa_key(declaring_lineno_ptr, called_function);
zend_op_array *op_array = NULL;
/* A PFA is SHM-cacheable if the declaring_op_array and called_function are
/* A PFA is SHM-cacheable if the declaring op_array and called_function are
* cached. */
if (ZCG(accelerator_enabled)
&& !file_cache_only
&& !declaring_op_array->refcount
&& cacheable_in_shm /* declaring op_array is cached */
&& (called_function->type != ZEND_USER_FUNCTION || !called_function->op_array.refcount)) {
zend_persistent_script *persistent_script = zend_accel_hash_find(&ZCSG(hash), key);
if (persistent_script) {
@@ -2084,11 +2084,13 @@ const zend_op_array *zend_accel_pfa_cache_get(const zend_op_array *declaring_op_
}
zend_op_array *zend_accel_compile_pfa(zend_ast *ast,
const zend_op_array *declaring_op_array,
const zend_op *declaring_opline,
zend_string *declaring_filename,
const uint32_t *declaring_lineno_ptr,
const zend_function *called_function,
zend_string *pfa_func_name)
zend_string *pfa_func_name, bool cacheable_in_shm)
{
ZEND_ASSERT(zend_accel_in_shm((void*)declaring_lineno_ptr) || !cacheable_in_shm);
zend_begin_record_errors();
zend_op_array *op_array;
@@ -2106,7 +2108,7 @@ zend_op_array *zend_accel_compile_pfa(zend_ast *ast,
CG(compiler_options) |= ZEND_COMPILE_IGNORE_INTERNAL_CLASSES;
#endif
op_array = zend_compile_ast(ast, ZEND_USER_FUNCTION, declaring_op_array->filename);
op_array = zend_compile_ast(ast, ZEND_USER_FUNCTION, declaring_filename);
CG(compiler_options) = orig_compiler_options;
} zend_catch {
@@ -2119,21 +2121,21 @@ zend_op_array *zend_accel_compile_pfa(zend_ast *ast,
ZEND_ASSERT(op_array->num_dynamic_func_defs == 1);
zend_string_release(op_array->dynamic_func_defs[0]->function_name);
op_array->dynamic_func_defs[0]->function_name = pfa_func_name;
op_array->dynamic_func_defs[0]->function_name = zend_string_copy(pfa_func_name);
zend_string *key = zend_accel_pfa_key(declaring_opline, called_function);
zend_string *key = zend_accel_pfa_key(declaring_lineno_ptr, called_function);
/* Cache op_array only if the declaring op_array and the called function
* are cached */
if (!ZCG(accelerator_enabled)
|| file_cache_only
|| declaring_op_array->refcount
|| !cacheable_in_shm /* declaring op_array is not in SHM */
|| (called_function->type == ZEND_USER_FUNCTION && called_function->op_array.refcount)
|| (ZCSG(restart_in_progress) && accel_restart_is_active())
|| (!ZCG(counted) && accel_activate_add() == FAILURE)) {
zend_op_array *script_op_array = op_array;
zend_op_array *op_array = script_op_array->dynamic_func_defs[0];
GC_ADDREF(op_array->function_name);
GC_TRY_ADDREF(op_array->function_name);
(*op_array->refcount)++;
destroy_op_array(script_op_array);
efree(script_op_array);
+5 -6
View File
@@ -334,15 +334,14 @@ zend_string* ZEND_FASTCALL accel_new_interned_string(zend_string *str);
uint32_t zend_accel_get_class_name_map_ptr(zend_string *type_name);
const zend_op_array *zend_accel_pfa_cache_get(const zend_op_array *declaring_op_array,
const zend_op *declaring_opline,
const zend_function *called_function);
const zend_op_array *zend_accel_pfa_cache_get(
const uint32_t *declaring_lineno_ptr, const zend_function *called_function, bool cacheable_in_shm);
zend_op_array *zend_accel_compile_pfa(zend_ast *ast,
const zend_op_array *declaring_op_array,
const zend_op *declaring_opline,
zend_string *declaring_filename,
const uint32_t *declaring_lineno_ptr,
const zend_function *called_function,
zend_string *pfa_func_name);
zend_string *pfa_func_name, bool cacheable_in_shm);
END_EXTERN_C()
@@ -33,7 +33,7 @@ $_main:
0005 INIT_FCALL 2 %d string("array_map")
0006 INIT_FCALL 0 %d string("plusn")
0007 SEND_VAL int(2) string("n")
0008 T2 = CALLABLE_CONVERT_PARTIAL 2
0008 T2 = CALLABLE_CONVERT_PARTIAL %d string("{closure:%s}")
0009 SEND_VAL T2 1
0010 SEND_VAR CV0($array) 2
0011 T2 = DO_ICALL
+23 -1
View File
@@ -22,6 +22,7 @@
#include "zend_attributes.h"
#include "zend_system_id.h"
#include "zend_enum.h"
#include "zend_partial.h"
#include "php.h"
#ifdef ZEND_WIN32
@@ -382,6 +383,10 @@ static void zend_file_cache_serialize_ast(zend_ast *ast,
} else if (ast->kind == ZEND_AST_CALLABLE_CONVERT) {
zend_ast_fcc *fcc = (zend_ast_fcc*)ast;
ZEND_MAP_PTR_INIT(fcc->fptr, NULL);
/* Will be reset if needed during unserialize */
fcc->attr |= ZEND_PARTIAL_CACHEABLE_IN_SHM;
SERIALIZE_STR(fcc->filename);
SERIALIZE_STR(fcc->name);
if (!IS_SERIALIZED(fcc->args)) {
SERIALIZE_PTR(fcc->args);
tmp = fcc->args;
@@ -636,6 +641,12 @@ static void zend_file_cache_serialize_op_array(zend_op_array *op_arra
break;
}
#endif
if (opline->opcode == ZEND_CALLABLE_CONVERT_PARTIAL) {
/* Will be reset if needed during unserialize */
opline->extended_value |= ZEND_PARTIAL_CACHEABLE_IN_SHM;
}
zend_serialize_opcode_handler(opline);
opline++;
}
@@ -1308,9 +1319,13 @@ static void zend_file_cache_unserialize_ast(zend_ast *ast,
zend_ast_get_op_array(ast)->op_array = Z_PTR(z);
} else if (ast->kind == ZEND_AST_CALLABLE_CONVERT) {
zend_ast_fcc *fcc = (zend_ast_fcc*)ast;
if (!script->corrupted) {
if (script->corrupted) {
fcc->attr &= ~ZEND_PARTIAL_CACHEABLE_IN_SHM;
} else {
ZEND_MAP_PTR_NEW(fcc->fptr);
}
UNSERIALIZE_STR(fcc->filename);
UNSERIALIZE_STR(fcc->name);
if (!IS_UNSERIALIZED(fcc->args)) {
UNSERIALIZE_PTR(fcc->args);
zend_file_cache_unserialize_ast(fcc->args, script, buf);
@@ -1541,6 +1556,13 @@ static void zend_file_cache_unserialize_op_array(zend_op_array *op_arr
zval *literal = RT_CONSTANT(opline, opline->op1);
UNSERIALIZE_ATTRIBUTES(Z_PTR_P(literal));
}
if (opline->opcode == ZEND_CALLABLE_CONVERT_PARTIAL) {
if (script->corrupted) {
opline->extended_value &= ~ZEND_PARTIAL_CACHEABLE_IN_SHM;
}
}
zend_deserialize_opcode_handler(opline);
opline++;
}
+14
View File
@@ -27,6 +27,7 @@
#include "zend_operators.h"
#include "zend_interfaces.h"
#include "zend_attributes.h"
#include "zend_partial.h"
#ifdef HAVE_JIT
# include "Optimizer/zend_func_info.h"
@@ -197,6 +198,13 @@ static zend_ast *zend_persist_ast(zend_ast *ast)
zend_ast_fcc *copy = zend_shared_memdup(ast, sizeof(zend_ast_fcc));
if (!ZCG(current_persistent_script)->corrupted) {
ZEND_MAP_PTR_NEW(copy->fptr);
copy->attr |= ZEND_PARTIAL_CACHEABLE_IN_SHM;
}
if (copy->filename) {
zend_accel_store_interned_string(copy->filename);
}
if (copy->name) {
zend_accel_store_interned_string(copy->name);
}
copy->args = zend_persist_ast(copy->args);
node = (zend_ast *) copy;
@@ -624,6 +632,12 @@ static void zend_persist_op_array_ex(zend_op_array *op_array, zend_persistent_sc
attributes = zend_persist_attributes(attributes);
ZVAL_PTR(literal, attributes);
}
if (opline->opcode == ZEND_CALLABLE_CONVERT_PARTIAL) {
if (!ZCG(current_persistent_script)->corrupted) {
opline->extended_value |= ZEND_PARTIAL_CACHEABLE_IN_SHM;
}
}
}
efree(op_array->opcodes);
+6
View File
@@ -92,6 +92,12 @@ static void zend_persist_ast_calc(zend_ast *ast)
} else if (ast->kind == ZEND_AST_CALLABLE_CONVERT) {
zend_ast_fcc *fcc_ast = (zend_ast_fcc*)ast;
ADD_SIZE(sizeof(zend_ast_fcc));
if (fcc_ast->filename) {
ADD_INTERNED_STRING(fcc_ast->filename);
}
if (fcc_ast->name) {
ADD_INTERNED_STRING(fcc_ast->name);
}
zend_persist_ast_calc(fcc_ast->args);
} else if (zend_ast_is_decl(ast)) {
/* Not implemented. */