Skip to content

Commit 45057db

Browse files
committed
Preserve the original AST for closures in const-expressions
This is necessary when stringifying a `ReflectionAttribute` that has a closure as a parameter.
1 parent 9c829d9 commit 45057db

File tree

9 files changed

+99
-22
lines changed

9 files changed

+99
-22
lines changed
Lines changed: 24 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,24 @@
1+
--TEST--
2+
AST printing for closures in attributes at runtime
3+
--FILE--
4+
<?php
5+
6+
#[Attr(static function ($foo) {
7+
echo $foo;
8+
})]
9+
function foo() { }
10+
11+
$r = new ReflectionFunction('foo');
12+
foreach ($r->getAttributes() as $attribute) {
13+
echo $attribute;
14+
}
15+
16+
?>
17+
--EXPECT--
18+
Attribute [ Attr ] {
19+
- Arguments [1] {
20+
Argument #0 [ static function ($foo) {
21+
echo $foo;
22+
} ]
23+
}
24+
}

Zend/zend_ast.c

Lines changed: 29 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -100,6 +100,18 @@ ZEND_API zend_ast * ZEND_FASTCALL zend_ast_create_constant(zend_string *name, ze
100100
return (zend_ast *) ast;
101101
}
102102

103+
ZEND_API zend_ast * ZEND_FASTCALL zend_ast_create_op_array(zend_op_array *op_array, zend_ast *original_ast, zend_ast_attr attr) {
104+
zend_ast_op_array *ast;
105+
106+
ast = zend_ast_alloc(sizeof(zend_ast_op_array));
107+
ast->kind = ZEND_AST_OP_ARRAY;
108+
ast->attr = attr;
109+
ast->op_array = op_array;
110+
ast->ast = original_ast;
111+
112+
return (zend_ast *) ast;
113+
}
114+
103115
ZEND_API zend_ast * ZEND_FASTCALL zend_ast_create_class_const_or_name(zend_ast *class_name, zend_ast *name) {
104116
zend_string *name_str = zend_ast_get_str(name);
105117
if (zend_string_equals_ci(name_str, ZSTR_KNOWN(ZEND_STR_CLASS))) {
@@ -992,7 +1004,7 @@ ZEND_API zend_result ZEND_FASTCALL zend_ast_evaluate_inner(
9921004
}
9931005
case ZEND_AST_OP_ARRAY:
9941006
{
995-
zend_function *func = Z_PTR_P(&((zend_ast_zval*)(ast))->val);
1007+
zend_function *func = (zend_function *)zend_ast_get_op_array(ast)->op_array;
9961008

9971009
zend_create_closure(result, func, scope, scope, NULL);
9981010
return SUCCESS;
@@ -1076,8 +1088,10 @@ static size_t ZEND_FASTCALL zend_ast_tree_size(zend_ast *ast)
10761088
{
10771089
size_t size;
10781090

1079-
if (ast->kind == ZEND_AST_ZVAL || ast->kind == ZEND_AST_CONSTANT || ast->kind == ZEND_AST_OP_ARRAY) {
1091+
if (ast->kind == ZEND_AST_ZVAL || ast->kind == ZEND_AST_CONSTANT) {
10801092
size = sizeof(zend_ast_zval);
1093+
} else if (ast->kind == ZEND_AST_OP_ARRAY) {
1094+
size = sizeof(zend_ast_op_array) + zend_ast_tree_size(zend_ast_get_op_array(ast)->ast);
10811095
} else if (zend_ast_is_list(ast)) {
10821096
uint32_t i;
10831097
zend_ast_list *list = zend_ast_get_list(ast);
@@ -1143,12 +1157,14 @@ static void* ZEND_FASTCALL zend_ast_tree_copy(zend_ast *ast, void *buf)
11431157
}
11441158
}
11451159
} else if (ast->kind == ZEND_AST_OP_ARRAY) {
1146-
zend_ast_zval *new = (zend_ast_zval*)buf;
1147-
new->kind = ZEND_AST_OP_ARRAY;
1148-
new->attr = ast->attr;
1149-
ZVAL_COPY(&new->val, &((zend_ast_zval *) ast)->val);
1150-
Z_LINENO(new->val) = zend_ast_get_lineno(ast);
1151-
buf = (void*)((char*)buf + sizeof(zend_ast_zval));
1160+
zend_ast_op_array *old = zend_ast_get_op_array(ast);
1161+
zend_ast_op_array *new = (zend_ast_op_array*)buf;
1162+
new->kind = old->kind;
1163+
new->attr = old->attr;
1164+
new->op_array = old->op_array;
1165+
buf = (void*)((char*)buf + sizeof(zend_ast_op_array));
1166+
new->ast = (zend_ast*)buf;
1167+
buf = zend_ast_tree_copy(old->ast, buf);
11521168
} else if (zend_ast_is_decl(ast)) {
11531169
zend_ast_decl *old = (zend_ast_decl*)ast;
11541170
zend_ast_decl *new = (zend_ast_decl*)buf;
@@ -1241,7 +1257,7 @@ ZEND_API void ZEND_FASTCALL zend_ast_destroy(zend_ast *ast)
12411257
} else if (EXPECTED(ast->kind == ZEND_AST_CONSTANT)) {
12421258
zend_string_release_ex(zend_ast_get_constant_name(ast), 0);
12431259
} else if (EXPECTED(ast->kind == ZEND_AST_OP_ARRAY)) {
1244-
ZEND_ASSERT(!Z_REFCOUNTED(((zend_ast_zval*)(ast))->val));
1260+
zend_ast_destroy(zend_ast_get_op_array(ast)->ast);
12451261
} else if (zend_ast_is_decl(ast)) {
12461262
zend_ast_decl *decl = (zend_ast_decl *) ast;
12471263

@@ -1859,6 +1875,10 @@ static ZEND_COLD void zend_ast_export_ex(smart_str *str, zend_ast *ast, int prio
18591875
smart_str_appendl(str, ZSTR_VAL(name), ZSTR_LEN(name));
18601876
break;
18611877
}
1878+
case ZEND_AST_OP_ARRAY: {
1879+
zend_ast_export_ex(str, zend_ast_get_op_array(ast)->ast, priority, indent);
1880+
break;
1881+
}
18621882
case ZEND_AST_CONSTANT_CLASS:
18631883
smart_str_appendl(str, "__CLASS__", sizeof("__CLASS__")-1);
18641884
break;

Zend/zend_ast.h

Lines changed: 19 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -207,6 +207,15 @@ typedef struct _zend_ast_zval {
207207
zval val;
208208
} zend_ast_zval;
209209

210+
typedef struct _zend_op_array zend_op_array;
211+
212+
typedef struct _zend_ast_op_array {
213+
zend_ast_kind kind;
214+
zend_ast_attr attr;
215+
zend_op_array *op_array;
216+
zend_ast *ast;
217+
} zend_ast_op_array;
218+
210219
/* Separate structure for function and class declaration, as they need extra information. */
211220
typedef struct _zend_ast_decl {
212221
zend_ast_kind kind;
@@ -231,6 +240,8 @@ ZEND_API zend_ast * ZEND_FASTCALL zend_ast_create_zval_from_long(zend_long lval)
231240
ZEND_API zend_ast * ZEND_FASTCALL zend_ast_create_constant(zend_string *name, zend_ast_attr attr);
232241
ZEND_API zend_ast * ZEND_FASTCALL zend_ast_create_class_const_or_name(zend_ast *class_name, zend_ast *name);
233242

243+
ZEND_API zend_ast * ZEND_FASTCALL zend_ast_create_op_array(zend_op_array *op_array, zend_ast *original_ast, zend_ast_attr attr);
244+
234245
#if ZEND_AST_SPEC
235246
# define ZEND_AST_SPEC_CALL(name, ...) \
236247
ZEND_EXPAND_VA(ZEND_AST_SPEC_CALL_(name, __VA_ARGS__, _n, _5, _4, _3, _2, _1, _0)(__VA_ARGS__))
@@ -353,6 +364,11 @@ static zend_always_inline zend_string *zend_ast_get_str(zend_ast *ast) {
353364
return Z_STR_P(zv);
354365
}
355366

367+
static zend_always_inline zend_ast_op_array *zend_ast_get_op_array(zend_ast *ast) {
368+
ZEND_ASSERT(ast->kind == ZEND_AST_OP_ARRAY);
369+
return (zend_ast_op_array *) ast;
370+
}
371+
356372
static zend_always_inline zend_string *zend_ast_get_constant_name(zend_ast *ast) {
357373
ZEND_ASSERT(ast->kind == ZEND_AST_CONSTANT);
358374
ZEND_ASSERT(Z_TYPE(((zend_ast_zval *) ast)->val) == IS_STRING);
@@ -367,9 +383,11 @@ static zend_always_inline uint32_t zend_ast_get_lineno(zend_ast *ast) {
367383
if (ast->kind == ZEND_AST_ZVAL) {
368384
zval *zv = zend_ast_get_zval(ast);
369385
return Z_LINENO_P(zv);
370-
} else if (ast->kind == ZEND_AST_CONSTANT || ast->kind == ZEND_AST_OP_ARRAY) {
386+
} else if (ast->kind == ZEND_AST_CONSTANT) {
371387
zval *zv = &((zend_ast_zval *) ast)->val;
372388
return Z_LINENO_P(zv);
389+
} else if (ast->kind == ZEND_AST_OP_ARRAY) {
390+
return zend_ast_get_op_array(ast)->ast->lineno;
373391
} else {
374392
return ast->lineno;
375393
}

Zend/zend_compile.c

Lines changed: 2 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -11220,13 +11220,9 @@ static void zend_compile_const_expr_closure(zend_ast **ast_ptr)
1122011220
}
1122111221

1122211222
znode node;
11223-
zend_op_array *op = zend_compile_func_decl(&node, *ast_ptr, FUNC_DECL_LEVEL_CONSTEXPR);
11223+
zend_op_array *op = zend_compile_func_decl(&node, (zend_ast*)closure_ast, FUNC_DECL_LEVEL_CONSTEXPR);
1122411224

11225-
zend_ast_destroy(*ast_ptr);
11226-
zval z;
11227-
ZVAL_PTR(&z, op);
11228-
*ast_ptr = zend_ast_create_zval(&z);
11229-
(*ast_ptr)->kind = ZEND_AST_OP_ARRAY;
11225+
*ast_ptr = zend_ast_create_op_array(op, (zend_ast*)closure_ast, 0);
1123011226
}
1123111227

1123211228
static void zend_compile_const_expr_args(zend_ast **ast_ptr)

ext/opcache/zend_file_cache.c

Lines changed: 10 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -365,7 +365,12 @@ static void zend_file_cache_serialize_ast(zend_ast *ast,
365365
}
366366
} else if (ast->kind == ZEND_AST_OP_ARRAY) {
367367
/* The op_array itself will be serialized as part of the dynamic_func_defs. */
368-
SERIALIZE_PTR(Z_PTR(((zend_ast_zval*)ast)->val));
368+
SERIALIZE_PTR(((zend_ast_op_array*)ast)->op_array);
369+
370+
SERIALIZE_PTR(((zend_ast_op_array*)ast)->ast);
371+
tmp = ((zend_ast_op_array*)ast)->ast;
372+
UNSERIALIZE_PTR(tmp);
373+
zend_file_cache_serialize_ast(tmp, script, info, buf);
369374
} else if (zend_ast_is_decl(ast)) {
370375
zend_ast_decl *decl = (zend_ast_decl*)ast;
371376
for (i = 0; i < 5; i++) {
@@ -1257,7 +1262,10 @@ static void zend_file_cache_unserialize_ast(zend_ast *ast,
12571262
}
12581263
} else if (ast->kind == ZEND_AST_OP_ARRAY) {
12591264
/* The op_array itself will be unserialized as part of the dynamic_func_defs. */
1260-
UNSERIALIZE_PTR(Z_PTR(((zend_ast_zval*)ast)->val));
1265+
UNSERIALIZE_PTR(((zend_ast_op_array*)ast)->op_array);
1266+
1267+
UNSERIALIZE_PTR(((zend_ast_op_array*)ast)->ast);
1268+
zend_file_cache_unserialize_ast(((zend_ast_op_array*)ast)->ast, script, buf);
12611269
} else if (zend_ast_is_decl(ast)) {
12621270
zend_ast_decl *decl = (zend_ast_decl*)ast;
12631271
for (i = 0; i < 5; i++) {

ext/opcache/zend_persist.c

Lines changed: 6 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -189,8 +189,12 @@ static zend_ast *zend_persist_ast(zend_ast *ast)
189189
}
190190
node = (zend_ast *) copy;
191191
} else if (ast->kind == ZEND_AST_OP_ARRAY) {
192-
zend_ast_zval *copy = zend_shared_memdup(ast, sizeof(zend_ast_zval));
193-
zend_persist_op_array(&copy->val);
192+
zend_ast_op_array *copy = zend_shared_memdup(ast, sizeof(zend_ast_op_array));
193+
zval z;
194+
ZVAL_PTR(&z, copy->op_array);
195+
zend_persist_op_array(&z);
196+
copy->op_array = Z_PTR(z);
197+
copy->ast = zend_persist_ast(copy->ast);
194198
node = (zend_ast *) copy;
195199
} else if (zend_ast_is_decl(ast)) {
196200
zend_ast_decl *copy = zend_shared_memdup(ast, sizeof(zend_ast_decl));

ext/opcache/zend_persist_calc.c

Lines changed: 5 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -87,8 +87,11 @@ static void zend_persist_ast_calc(zend_ast *ast)
8787
}
8888
}
8989
} else if (ast->kind == ZEND_AST_OP_ARRAY) {
90-
ADD_SIZE(sizeof(zend_ast_zval));
91-
zend_persist_op_array_calc(&((zend_ast_zval*)(ast))->val);
90+
ADD_SIZE(sizeof(zend_ast_op_array));
91+
zval z;
92+
ZVAL_PTR(&z, zend_ast_get_op_array(ast)->op_array);
93+
zend_persist_op_array_calc(&z);
94+
zend_persist_ast_calc(zend_ast_get_op_array(ast)->ast);
9295
} else if (zend_ast_is_decl(ast)) {
9396
zend_ast_decl *decl = (zend_ast_decl*)ast;
9497
ADD_SIZE(sizeof(zend_ast_decl));

main/debug_gdb_scripts.c

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -860,6 +860,8 @@ asm(
860860
".ascii \"\\n\"\n"
861861
".ascii \" if kind == enum_value('ZEND_AST_ZVAL') or kind == enum_value('ZEND_AST_CONSTANT'):\\n\"\n"
862862
".ascii \" return self.val.cast(gdb.lookup_type('zend_ast_zval'))\\n\"\n"
863+
".ascii \" if kind == enum_value('ZEND_AST_OP_ARRAY'):\\n\"\n"
864+
".ascii \" return self.val.cast(gdb.lookup_type('zend_ast_op_array'))\\n\"\n"
863865
".ascii \" if kind == enum_value('ZEND_AST_ZNODE'):\\n\"\n"
864866
".ascii \" return self.val.cast(gdb.lookup_type('zend_ast_znode'))\\n\"\n"
865867
".ascii \" if self.is_decl():\\n\"\n"

scripts/gdb/php_gdb.py

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -190,6 +190,8 @@ def cast(self):
190190

191191
if kind == enum_value('ZEND_AST_ZVAL') or kind == enum_value('ZEND_AST_CONSTANT'):
192192
return self.val.cast(gdb.lookup_type('zend_ast_zval'))
193+
if kind == enum_value('ZEND_AST_OP_ARRAY'):
194+
return self.val.cast(gdb.lookup_type('zend_ast_op_array'))
193195
if kind == enum_value('ZEND_AST_ZNODE'):
194196
return self.val.cast(gdb.lookup_type('zend_ast_znode'))
195197
if self.is_decl():

0 commit comments

Comments
 (0)