Skip to content

Commit 2772751

Browse files
committed
Make constexpr compilation robust against multiple compilation
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.
1 parent 5686c16 commit 2772751

File tree

4 files changed

+46
-51
lines changed

4 files changed

+46
-51
lines changed

Zend/tests/bug79897.phpt

Lines changed: 9 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -12,21 +12,27 @@ class B {
1212

1313
class A {
1414
public function __construct(
15-
#[B(12)] public $b
15+
#[B(12, X)] public $b
1616
)
1717
{
1818
}
1919
}
2020

21+
const X = 42;
22+
2123
var_dump((new ReflectionParameter(['A', '__construct'], 'b'))->getAttributes()[0]->getArguments());
2224
var_dump((new ReflectionProperty('A', 'b'))->getAttributes()[0]->getArguments());
2325
?>
2426
--EXPECT--
25-
array(1) {
27+
array(2) {
2628
[0]=>
2729
int(12)
30+
[1]=>
31+
int(42)
2832
}
29-
array(1) {
33+
array(2) {
3034
[0]=>
3135
int(12)
36+
[1]=>
37+
int(42)
3238
}

Zend/zend_API.c

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -4397,7 +4397,7 @@ static zend_result get_default_via_ast(zval *default_value_zval, const char *def
43974397
}
43984398

43994399
zend_ast_list *statement_list = zend_ast_get_list(ast);
4400-
zend_ast *const_expression_ast = statement_list->child[0];
4400+
zend_ast **const_expr_ast_ptr = &statement_list->child[0];
44014401

44024402
zend_arena *original_ast_arena = CG(ast_arena);
44034403
uint32_t original_compiler_options = CG(compiler_options);
@@ -4406,7 +4406,7 @@ static zend_result get_default_via_ast(zval *default_value_zval, const char *def
44064406
/* Disable constant substitution, to make getDefaultValueConstant() work. */
44074407
CG(compiler_options) |= ZEND_COMPILE_NO_CONSTANT_SUBSTITUTION | ZEND_COMPILE_NO_PERSISTENT_CONSTANT_SUBSTITUTION;
44084408
zend_file_context_begin(&original_file_context);
4409-
zend_const_expr_to_zval(default_value_zval, const_expression_ast);
4409+
zend_const_expr_to_zval(default_value_zval, const_expr_ast_ptr);
44104410
CG(ast_arena) = original_ast_arena;
44114411
CG(compiler_options) = original_compiler_options;
44124412
zend_file_context_end(&original_file_context);

Zend/zend_compile.c

Lines changed: 34 additions & 45 deletions
Original file line numberDiff line numberDiff line change
@@ -4596,11 +4596,11 @@ static void zend_compile_static_var_common(zend_string *var_name, zval *value, u
45964596
void zend_compile_static_var(zend_ast *ast) /* {{{ */
45974597
{
45984598
zend_ast *var_ast = ast->child[0];
4599-
zend_ast *value_ast = ast->child[1];
4599+
zend_ast **value_ast_ptr = &ast->child[1];
46004600
zval value_zv;
46014601

4602-
if (value_ast) {
4603-
zend_const_expr_to_zval(&value_zv, value_ast);
4602+
if (*value_ast_ptr) {
4603+
zend_const_expr_to_zval(&value_zv, value_ast_ptr);
46044604
} else {
46054605
ZVAL_NULL(&value_zv);
46064606
}
@@ -5895,16 +5895,16 @@ void zend_compile_declare(zend_ast *ast) /* {{{ */
58955895
for (i = 0; i < declares->children; ++i) {
58965896
zend_ast *declare_ast = declares->child[i];
58975897
zend_ast *name_ast = declare_ast->child[0];
5898-
zend_ast *value_ast = declare_ast->child[1];
5898+
zend_ast **value_ast_ptr = &declare_ast->child[1];
58995899
zend_string *name = zend_ast_get_str(name_ast);
59005900

5901-
if (value_ast->kind != ZEND_AST_ZVAL) {
5901+
if ((*value_ast_ptr)->kind != ZEND_AST_ZVAL) {
59025902
zend_error_noreturn(E_COMPILE_ERROR, "declare(%s) value must be a literal", ZSTR_VAL(name));
59035903
}
59045904

59055905
if (zend_string_equals_literal_ci(name, "ticks")) {
59065906
zval value_zv;
5907-
zend_const_expr_to_zval(&value_zv, value_ast);
5907+
zend_const_expr_to_zval(&value_zv, value_ast_ptr);
59085908
FC(declarables).ticks = zval_get_long(&value_zv);
59095909
zval_ptr_dtor_nogc(&value_zv);
59105910
} else if (zend_string_equals_literal_ci(name, "encoding")) {
@@ -5926,7 +5926,7 @@ void zend_compile_declare(zend_ast *ast) /* {{{ */
59265926
"use block mode");
59275927
}
59285928

5929-
zend_const_expr_to_zval(&value_zv, value_ast);
5929+
zend_const_expr_to_zval(&value_zv, value_ast_ptr);
59305930

59315931
if (Z_TYPE(value_zv) != IS_LONG || (Z_LVAL(value_zv) != 0 && Z_LVAL(value_zv) != 1)) {
59325932
zend_error_noreturn(E_COMPILE_ERROR, "strict_types declaration must have 0 or 1 as its value");
@@ -6233,7 +6233,8 @@ static void zend_compile_attributes(HashTable **attributes, zend_ast *ast, uint3
62336233

62346234
zend_bool uses_named_args = 0;
62356235
for (j = 0; j < args->children; j++) {
6236-
zend_ast *arg_ast = args->child[j];
6236+
zend_ast **arg_ast_ptr = &args->child[j];
6237+
zend_ast *arg_ast = *arg_ast_ptr;
62376238

62386239
if (arg_ast->kind == ZEND_AST_UNPACK) {
62396240
zend_error_noreturn(E_COMPILE_ERROR,
@@ -6242,7 +6243,7 @@ static void zend_compile_attributes(HashTable **attributes, zend_ast *ast, uint3
62426243

62436244
if (arg_ast->kind == ZEND_AST_NAMED_ARG) {
62446245
attr->args[j].name = zend_string_copy(zend_ast_get_str(arg_ast->child[0]));
6245-
arg_ast = arg_ast->child[1];
6246+
arg_ast_ptr = &arg_ast->child[1];
62466247
uses_named_args = 1;
62476248

62486249
for (uint32_t k = 0; k < j; k++) {
@@ -6257,7 +6258,7 @@ static void zend_compile_attributes(HashTable **attributes, zend_ast *ast, uint3
62576258
"Cannot use positional argument after named argument");
62586259
}
62596260

6260-
zend_const_expr_to_zval(&attr->args[j].value, arg_ast);
6261+
zend_const_expr_to_zval(&attr->args[j].value, arg_ast_ptr);
62616262
}
62626263
}
62636264
}
@@ -6324,7 +6325,7 @@ void zend_compile_params(zend_ast *ast, zend_ast *return_type_ast, uint32_t fall
63246325
zend_ast *param_ast = list->child[i];
63256326
zend_ast *type_ast = param_ast->child[0];
63266327
zend_ast *var_ast = param_ast->child[1];
6327-
zend_ast *default_ast = param_ast->child[2];
6328+
zend_ast **default_ast_ptr = &param_ast->child[2];
63286329
zend_ast *attributes_ast = param_ast->child[3];
63296330
zend_ast *doc_comment_ast = param_ast->child[4];
63306331
zend_string *name = zval_make_interned_string(zend_ast_get_zval(var_ast));
@@ -6338,12 +6339,6 @@ void zend_compile_params(zend_ast *ast, zend_ast *return_type_ast, uint32_t fall
63386339
zend_op *opline;
63396340
zend_arg_info *arg_info;
63406341

6341-
zend_ast_ref *attributes_copy = NULL;
6342-
6343-
if (visibility && attributes_ast) {
6344-
attributes_copy = zend_ast_copy(attributes_ast);
6345-
}
6346-
63476342
if (zend_is_auto_global(name)) {
63486343
zend_error_noreturn(E_COMPILE_ERROR, "Cannot re-assign auto-global variable %s",
63496344
ZSTR_VAL(name));
@@ -6368,17 +6363,17 @@ void zend_compile_params(zend_ast *ast, zend_ast *return_type_ast, uint32_t fall
63686363
default_node.op_type = IS_UNUSED;
63696364
op_array->fn_flags |= ZEND_ACC_VARIADIC;
63706365

6371-
if (default_ast) {
6366+
if (*default_ast_ptr) {
63726367
zend_error_noreturn(E_COMPILE_ERROR,
63736368
"Variadic parameter cannot have a default value");
63746369
}
6375-
} else if (default_ast) {
6370+
} else if (*default_ast_ptr) {
63766371
/* we cannot substitute constants here or it will break ReflectionParameter::getDefaultValueConstantName() and ReflectionParameter::isDefaultValueConstant() */
63776372
uint32_t cops = CG(compiler_options);
63786373
CG(compiler_options) |= ZEND_COMPILE_NO_CONSTANT_SUBSTITUTION | ZEND_COMPILE_NO_PERSISTENT_CONSTANT_SUBSTITUTION;
63796374
opcode = ZEND_RECV_INIT;
63806375
default_node.op_type = IS_CONST;
6381-
zend_const_expr_to_zval(&default_node.u.constant, default_ast);
6376+
zend_const_expr_to_zval(&default_node.u.constant, default_ast_ptr);
63826377
CG(compiler_options) = cops;
63836378

63846379
if (!optional_param) {
@@ -6409,7 +6404,7 @@ void zend_compile_params(zend_ast *ast, zend_ast *return_type_ast, uint32_t fall
64096404
}
64106405

64116406
if (type_ast) {
6412-
uint32_t default_type = default_ast ? Z_TYPE(default_node.u.constant) : IS_UNDEF;
6407+
uint32_t default_type = *default_ast_ptr ? Z_TYPE(default_node.u.constant) : IS_UNDEF;
64136408
zend_bool force_nullable = default_type == IS_NULL && !visibility;
64146409

64156410
op_array->fn_flags |= ZEND_ACC_HAS_TYPE_HINTS;
@@ -6497,8 +6492,7 @@ void zend_compile_params(zend_ast *ast, zend_ast *return_type_ast, uint32_t fall
64976492
scope, name, &default_value, visibility | ZEND_ACC_PROMOTED, doc_comment, type);
64986493
if (attributes_ast) {
64996494
zend_compile_attributes(
6500-
&prop->attributes, GC_AST(attributes_copy), 0, ZEND_ATTRIBUTE_TARGET_PROPERTY);
6501-
zend_ast_ref_destroy(attributes_copy);
6495+
&prop->attributes, attributes_ast, 0, ZEND_ATTRIBUTE_TARGET_PROPERTY);
65026496
}
65036497
}
65046498
}
@@ -7002,7 +6996,7 @@ void zend_compile_prop_decl(zend_ast *ast, zend_ast *type_ast, uint32_t flags, z
70026996
zend_property_info *info;
70036997
zend_ast *prop_ast = list->child[i];
70046998
zend_ast *name_ast = prop_ast->child[0];
7005-
zend_ast *value_ast = prop_ast->child[1];
6999+
zend_ast **value_ast_ptr = &prop_ast->child[1];
70067000
zend_ast *doc_comment_ast = prop_ast->child[2];
70077001
zend_string *name = zval_make_interned_string(zend_ast_get_zval(name_ast));
70087002
zend_string *doc_comment = NULL;
@@ -7036,8 +7030,8 @@ void zend_compile_prop_decl(zend_ast *ast, zend_ast *type_ast, uint32_t flags, z
70367030
ZSTR_VAL(ce->name), ZSTR_VAL(name));
70377031
}
70387032

7039-
if (value_ast) {
7040-
zend_const_expr_to_zval(&value_zv, value_ast);
7033+
if (*value_ast_ptr) {
7034+
zend_const_expr_to_zval(&value_zv, value_ast_ptr);
70417035

70427036
if (ZEND_TYPE_IS_SET(type) && !Z_CONSTANT(value_zv)
70437037
&& !zend_is_valid_default_value(type, &value_zv)) {
@@ -7111,7 +7105,7 @@ void zend_compile_class_const_decl(zend_ast *ast, uint32_t flags, zend_ast *attr
71117105
zend_class_constant *c;
71127106
zend_ast *const_ast = list->child[i];
71137107
zend_ast *name_ast = const_ast->child[0];
7114-
zend_ast *value_ast = const_ast->child[1];
7108+
zend_ast **value_ast_ptr = &const_ast->child[1];
71157109
zend_ast *doc_comment_ast = const_ast->child[2];
71167110
zend_string *name = zval_make_interned_string(zend_ast_get_zval(name_ast));
71177111
zend_string *doc_comment = doc_comment_ast ? zend_string_copy(zend_ast_get_str(doc_comment_ast)) : NULL;
@@ -7121,7 +7115,7 @@ void zend_compile_class_const_decl(zend_ast *ast, uint32_t flags, zend_ast *attr
71217115
zend_check_const_and_trait_alias_attr(flags, "constant");
71227116
}
71237117

7124-
zend_const_expr_to_zval(&value_zv, value_ast);
7118+
zend_const_expr_to_zval(&value_zv, value_ast_ptr);
71257119
c = zend_declare_class_constant_ex(ce, name, &value_zv, flags, doc_comment);
71267120

71277121
if (attr_ast) {
@@ -7622,15 +7616,15 @@ void zend_compile_const_decl(zend_ast *ast) /* {{{ */
76227616
for (i = 0; i < list->children; ++i) {
76237617
zend_ast *const_ast = list->child[i];
76247618
zend_ast *name_ast = const_ast->child[0];
7625-
zend_ast *value_ast = const_ast->child[1];
7619+
zend_ast **value_ast_ptr = &const_ast->child[1];
76267620
zend_string *unqualified_name = zend_ast_get_str(name_ast);
76277621

76287622
zend_string *name;
76297623
znode name_node, value_node;
76307624
zval *value_zv = &value_node.u.constant;
76317625

76327626
value_node.op_type = IS_CONST;
7633-
zend_const_expr_to_zval(value_zv, value_ast);
7627+
zend_const_expr_to_zval(value_zv, value_ast_ptr);
76347628

76357629
if (zend_get_special_const(ZSTR_VAL(unqualified_name), ZSTR_LEN(unqualified_name))) {
76367630
zend_error_noreturn(E_COMPILE_ERROR,
@@ -9297,23 +9291,18 @@ void zend_compile_const_expr(zend_ast **ast_ptr) /* {{{ */
92979291
}
92989292
/* }}} */
92999293

9300-
void zend_const_expr_to_zval(zval *result, zend_ast *ast) /* {{{ */
9294+
void zend_const_expr_to_zval(zval *result, zend_ast **ast_ptr) /* {{{ */
93019295
{
9302-
zend_ast *orig_ast = ast;
9303-
zend_eval_const_expr(&ast);
9304-
zend_compile_const_expr(&ast);
9305-
if (ast->kind == ZEND_AST_ZVAL) {
9306-
ZVAL_COPY_VALUE(result, zend_ast_get_zval(ast));
9307-
} else {
9308-
ZVAL_AST(result, zend_ast_copy(ast));
9309-
/* destroy the ast here, it might have been replaced */
9310-
zend_ast_destroy(ast);
9296+
zend_eval_const_expr(ast_ptr);
9297+
zend_compile_const_expr(ast_ptr);
9298+
if ((*ast_ptr)->kind != ZEND_AST_ZVAL) {
9299+
/* Replace with compiled AST zval representation. */
9300+
zval ast_zv;
9301+
ZVAL_AST(&ast_zv, zend_ast_copy(*ast_ptr));
9302+
zend_ast_destroy(*ast_ptr);
9303+
*ast_ptr = zend_ast_create_zval(&ast_zv);
93119304
}
9312-
9313-
/* Kill this branch of the original AST, as it was already destroyed.
9314-
* It would be nice to find a better solution to this problem in the
9315-
* future. */
9316-
orig_ast->kind = 0;
9305+
ZVAL_COPY(result, zend_ast_get_zval(*ast_ptr));
93179306
}
93189307
/* }}} */
93199308

Zend/zend_compile.h

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -135,7 +135,7 @@ void zend_compile_stmt(zend_ast *ast);
135135
void zend_compile_expr(znode *node, zend_ast *ast);
136136
zend_op *zend_compile_var(znode *node, zend_ast *ast, uint32_t type, bool by_ref);
137137
void zend_eval_const_expr(zend_ast **ast_ptr);
138-
void zend_const_expr_to_zval(zval *result, zend_ast *ast);
138+
void zend_const_expr_to_zval(zval *result, zend_ast **ast_ptr);
139139

140140
typedef int (*user_opcode_handler_t) (zend_execute_data *execute_data);
141141

0 commit comments

Comments
 (0)