From 5287c6bb665ea94df6e0667ec33cc490a38daf05 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Tim=20D=C3=BCsterhus?= Date: Wed, 11 Dec 2024 12:12:51 +0100 Subject: [PATCH 1/5] Fix handling of `zend_ast_decl` AST nodes when copying MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Previously `zend_ast_decl` AST nodes were handled as if they were regular `zend_ast` nodes. This was not observable, because copying an AST only happened when evaluating const-expressions, which could not include declarations until the “Support Closures in constant expressions” RFC. --- Zend/zend_ast.c | 40 ++++++++++++++++++++++++++++----- ext/opcache/zend_file_cache.c | 20 +++++++++++++---- ext/opcache/zend_persist.c | 9 ++++++-- ext/opcache/zend_persist_calc.c | 9 ++++++-- 4 files changed, 65 insertions(+), 13 deletions(-) diff --git a/Zend/zend_ast.c b/Zend/zend_ast.c index c1d973f67c85c..6091b17480697 100644 --- a/Zend/zend_ast.c +++ b/Zend/zend_ast.c @@ -1103,8 +1103,13 @@ static size_t ZEND_FASTCALL zend_ast_tree_size(zend_ast *ast) } } } else if (zend_ast_is_decl(ast)) { - /* Not implemented. */ - ZEND_UNREACHABLE(); + zend_ast_decl *decl = (zend_ast_decl*)ast; + size = sizeof(zend_ast_decl); + for (size_t i = 0; i < 5; i++) { + if (decl->child[i]) { + size += zend_ast_tree_size(decl->child[i]); + } + } } else { uint32_t i, children = zend_ast_get_num_children(ast); @@ -1160,8 +1165,33 @@ static void* ZEND_FASTCALL zend_ast_tree_copy(zend_ast *ast, void *buf) new->op_array = old->op_array; buf = (void*)((char*)buf + sizeof(zend_ast_op_array)); } else if (zend_ast_is_decl(ast)) { - /* Not implemented. */ - ZEND_UNREACHABLE(); + zend_ast_decl *old = (zend_ast_decl*)ast; + zend_ast_decl *new = (zend_ast_decl*)buf; + new->kind = old->kind; + new->attr = old->attr; + new->start_lineno = old->start_lineno; + new->end_lineno = old->end_lineno; + new->flags = old->flags; + if (old->doc_comment) { + new->doc_comment = zend_string_copy(old->doc_comment); + } else { + new->doc_comment = NULL; + } + if (old->name) { + new->name = zend_string_copy(old->name); + } else { + new->name = NULL; + } + + buf = (void*)((char*)buf + sizeof(zend_ast_decl)); + for (size_t i = 0; i < 5; i++) { + if (old->child[i]) { + new->child[i] = (zend_ast*)buf; + buf = zend_ast_tree_copy(old->child[i], buf); + } else { + new->child[i] = NULL; + } + } } else { uint32_t i, children = zend_ast_get_num_children(ast); zend_ast *new = (zend_ast*)buf; @@ -1227,7 +1257,7 @@ ZEND_API void ZEND_FASTCALL zend_ast_destroy(zend_ast *ast) zend_string_release_ex(zend_ast_get_constant_name(ast), 0); } else if (EXPECTED(ast->kind == ZEND_AST_OP_ARRAY)) { /* Nothing to do. */ - } else if (EXPECTED(zend_ast_is_decl(ast))) { + } else if (zend_ast_is_decl(ast)) { zend_ast_decl *decl = (zend_ast_decl *) ast; if (decl->name) { diff --git a/ext/opcache/zend_file_cache.c b/ext/opcache/zend_file_cache.c index 74e0d19d1b6de..5d156975cc884 100644 --- a/ext/opcache/zend_file_cache.c +++ b/ext/opcache/zend_file_cache.c @@ -367,8 +367,15 @@ static void zend_file_cache_serialize_ast(zend_ast *ast, /* The op_array itself will be serialized as part of the dynamic_func_defs. */ SERIALIZE_PTR(zend_ast_get_op_array(ast)->op_array); } else if (zend_ast_is_decl(ast)) { - /* Not implemented. */ - ZEND_UNREACHABLE(); + zend_ast_decl *decl = (zend_ast_decl*)ast; + for (i = 0; i < 5; i++) { + if (decl->child[i] && !IS_SERIALIZED(decl->child[i])) { + SERIALIZE_PTR(decl->child[i]); + tmp = decl->child[i]; + UNSERIALIZE_PTR(tmp); + zend_file_cache_serialize_ast(tmp, script, info, buf); + } + } } else { uint32_t children = zend_ast_get_num_children(ast); for (i = 0; i < children; i++) { @@ -1252,8 +1259,13 @@ static void zend_file_cache_unserialize_ast(zend_ast *ast, /* The op_array itself will be unserialized as part of the dynamic_func_defs. */ UNSERIALIZE_PTR(zend_ast_get_op_array(ast)->op_array); } else if (zend_ast_is_decl(ast)) { - /* Not implemented. */ - ZEND_UNREACHABLE(); + zend_ast_decl *decl = (zend_ast_decl*)ast; + for (i = 0; i < 5; i++) { + if (decl->child[i] && !IS_UNSERIALIZED(decl->child[i])) { + UNSERIALIZE_PTR(decl->child[i]); + zend_file_cache_unserialize_ast(decl->child[i], script, buf); + } + } } else { uint32_t children = zend_ast_get_num_children(ast); for (i = 0; i < children; i++) { diff --git a/ext/opcache/zend_persist.c b/ext/opcache/zend_persist.c index 9e89980b67e14..fb98372202377 100644 --- a/ext/opcache/zend_persist.c +++ b/ext/opcache/zend_persist.c @@ -196,8 +196,13 @@ static zend_ast *zend_persist_ast(zend_ast *ast) copy->op_array = Z_PTR(z); node = (zend_ast *) copy; } else if (zend_ast_is_decl(ast)) { - /* Not implemented. */ - ZEND_UNREACHABLE(); + zend_ast_decl *copy = zend_shared_memdup(ast, sizeof(zend_ast_decl)); + for (i = 0; i < 5; i++) { + if (copy->child[i]) { + copy->child[i] = zend_persist_ast(copy->child[i]); + } + } + node = (zend_ast *) copy; } else { uint32_t children = zend_ast_get_num_children(ast); node = zend_shared_memdup(ast, zend_ast_size(children)); diff --git a/ext/opcache/zend_persist_calc.c b/ext/opcache/zend_persist_calc.c index 0c53923354c42..0163f3840bb1b 100644 --- a/ext/opcache/zend_persist_calc.c +++ b/ext/opcache/zend_persist_calc.c @@ -92,8 +92,13 @@ static void zend_persist_ast_calc(zend_ast *ast) ZVAL_PTR(&z, zend_ast_get_op_array(ast)->op_array); zend_persist_op_array_calc(&z); } else if (zend_ast_is_decl(ast)) { - /* Not implemented. */ - ZEND_UNREACHABLE(); + zend_ast_decl *decl = (zend_ast_decl*)ast; + ADD_SIZE(sizeof(zend_ast_decl)); + for (size_t i = 0; i < 5; i++) { + if (decl->child[i]) { + zend_persist_ast_calc(decl->child[i]); + } + } } else { uint32_t children = zend_ast_get_num_children(ast); ADD_SIZE(zend_ast_size(children)); From 4e13f14bdf16c1fa99b274c95639656d4685488a Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Tim=20D=C3=BCsterhus?= Date: Wed, 11 Dec 2024 10:20:09 +0100 Subject: [PATCH 2/5] Preserve the original AST for closures in const-expressions This is necessary when stringifying a `ReflectionAttribute` that has a closure as a parameter. --- .../attributes_ast_printing_runtime.phpt | 24 +++++++++++++++++++ Zend/zend_ast.c | 17 ++++++++----- Zend/zend_ast.h | 6 +++-- Zend/zend_compile.c | 3 +-- ext/opcache/zend_file_cache.c | 8 +++++++ ext/opcache/zend_persist.c | 1 + ext/opcache/zend_persist_calc.c | 1 + 7 files changed, 50 insertions(+), 10 deletions(-) create mode 100644 Zend/tests/closure_const_expr/attributes_ast_printing_runtime.phpt diff --git a/Zend/tests/closure_const_expr/attributes_ast_printing_runtime.phpt b/Zend/tests/closure_const_expr/attributes_ast_printing_runtime.phpt new file mode 100644 index 0000000000000..85d5ae925d967 --- /dev/null +++ b/Zend/tests/closure_const_expr/attributes_ast_printing_runtime.phpt @@ -0,0 +1,24 @@ +--TEST-- +AST printing for closures in attributes at runtime +--FILE-- +getAttributes() as $attribute) { + echo $attribute; +} + +?> +--EXPECT-- +Attribute [ Attr ] { + - Arguments [1] { + Argument #0 [ static function ($foo) { + echo $foo; +} ] + } +} diff --git a/Zend/zend_ast.c b/Zend/zend_ast.c index 6091b17480697..ecba06d737260 100644 --- a/Zend/zend_ast.c +++ b/Zend/zend_ast.c @@ -100,14 +100,14 @@ ZEND_API zend_ast * ZEND_FASTCALL zend_ast_create_constant(zend_string *name, ze return (zend_ast *) ast; } -ZEND_API zend_ast * ZEND_FASTCALL zend_ast_create_op_array(zend_op_array *op_array) { +ZEND_API zend_ast * ZEND_FASTCALL zend_ast_create_op_array(zend_op_array *op_array, zend_ast *original_ast, zend_ast_attr attr) { zend_ast_op_array *ast; ast = zend_ast_alloc(sizeof(zend_ast_op_array)); ast->kind = ZEND_AST_OP_ARRAY; - ast->attr = 0; - ast->lineno = CG(zend_lineno); + ast->attr = attr; ast->op_array = op_array; + ast->ast = original_ast; return (zend_ast *) ast; } @@ -1091,7 +1091,7 @@ static size_t ZEND_FASTCALL zend_ast_tree_size(zend_ast *ast) if (ast->kind == ZEND_AST_ZVAL || ast->kind == ZEND_AST_CONSTANT) { size = sizeof(zend_ast_zval); } else if (ast->kind == ZEND_AST_OP_ARRAY) { - size = sizeof(zend_ast_op_array); + size = sizeof(zend_ast_op_array) + zend_ast_tree_size(zend_ast_get_op_array(ast)->ast); } else if (zend_ast_is_list(ast)) { uint32_t i; zend_ast_list *list = zend_ast_get_list(ast); @@ -1161,9 +1161,10 @@ static void* ZEND_FASTCALL zend_ast_tree_copy(zend_ast *ast, void *buf) zend_ast_op_array *new = (zend_ast_op_array*)buf; new->kind = old->kind; new->attr = old->attr; - new->lineno = old->lineno; new->op_array = old->op_array; buf = (void*)((char*)buf + sizeof(zend_ast_op_array)); + new->ast = (zend_ast*)buf; + buf = zend_ast_tree_copy(old->ast, buf); } else if (zend_ast_is_decl(ast)) { zend_ast_decl *old = (zend_ast_decl*)ast; zend_ast_decl *new = (zend_ast_decl*)buf; @@ -1256,7 +1257,7 @@ ZEND_API void ZEND_FASTCALL zend_ast_destroy(zend_ast *ast) } else if (EXPECTED(ast->kind == ZEND_AST_CONSTANT)) { zend_string_release_ex(zend_ast_get_constant_name(ast), 0); } else if (EXPECTED(ast->kind == ZEND_AST_OP_ARRAY)) { - /* Nothing to do. */ + zend_ast_destroy(zend_ast_get_op_array(ast)->ast); } else if (zend_ast_is_decl(ast)) { zend_ast_decl *decl = (zend_ast_decl *) ast; @@ -1915,6 +1916,10 @@ static ZEND_COLD void zend_ast_export_ex(smart_str *str, zend_ast *ast, int prio smart_str_appendl(str, ZSTR_VAL(name), ZSTR_LEN(name)); break; } + case ZEND_AST_OP_ARRAY: { + zend_ast_export_ex(str, zend_ast_get_op_array(ast)->ast, priority, indent); + break; + } case ZEND_AST_CONSTANT_CLASS: smart_str_appendl(str, "__CLASS__", sizeof("__CLASS__")-1); break; diff --git a/Zend/zend_ast.h b/Zend/zend_ast.h index db41802b16f16..db9c28fd474d5 100644 --- a/Zend/zend_ast.h +++ b/Zend/zend_ast.h @@ -212,8 +212,8 @@ typedef struct _zend_op_array zend_op_array; typedef struct _zend_ast_op_array { zend_ast_kind kind; zend_ast_attr attr; - uint32_t lineno; zend_op_array *op_array; + zend_ast *ast; } zend_ast_op_array; /* Separate structure for function and class declaration, as they need extra information. */ @@ -240,7 +240,7 @@ ZEND_API zend_ast * ZEND_FASTCALL zend_ast_create_zval_from_long(zend_long lval) ZEND_API zend_ast * ZEND_FASTCALL zend_ast_create_constant(zend_string *name, zend_ast_attr attr); ZEND_API zend_ast * ZEND_FASTCALL zend_ast_create_class_const_or_name(zend_ast *class_name, zend_ast *name); -ZEND_API zend_ast * ZEND_FASTCALL zend_ast_create_op_array(zend_op_array *op_array); +ZEND_API zend_ast * ZEND_FASTCALL zend_ast_create_op_array(zend_op_array *op_array, zend_ast *original_ast, zend_ast_attr attr); #if ZEND_AST_SPEC # define ZEND_AST_SPEC_CALL(name, ...) \ @@ -388,6 +388,8 @@ static zend_always_inline uint32_t zend_ast_get_lineno(zend_ast *ast) { } else if (ast->kind == ZEND_AST_CONSTANT) { zval *zv = &((zend_ast_zval *) ast)->val; return Z_LINENO_P(zv); + } else if (ast->kind == ZEND_AST_OP_ARRAY) { + return zend_ast_get_op_array(ast)->ast->lineno; } else { return ast->lineno; } diff --git a/Zend/zend_compile.c b/Zend/zend_compile.c index 23dfd4577812a..db3e43f403f19 100644 --- a/Zend/zend_compile.c +++ b/Zend/zend_compile.c @@ -11222,8 +11222,7 @@ static void zend_compile_const_expr_closure(zend_ast **ast_ptr) znode node; zend_op_array *op = zend_compile_func_decl(&node, (zend_ast*)closure_ast, FUNC_DECL_LEVEL_CONSTEXPR); - zend_ast_destroy(*ast_ptr); - *ast_ptr = zend_ast_create_op_array(op); + *ast_ptr = zend_ast_create_op_array(op, (zend_ast*)closure_ast, 0); } static void zend_compile_const_expr_args(zend_ast **ast_ptr) diff --git a/ext/opcache/zend_file_cache.c b/ext/opcache/zend_file_cache.c index 5d156975cc884..7ea946ececc0c 100644 --- a/ext/opcache/zend_file_cache.c +++ b/ext/opcache/zend_file_cache.c @@ -366,6 +366,11 @@ static void zend_file_cache_serialize_ast(zend_ast *ast, } else if (ast->kind == ZEND_AST_OP_ARRAY) { /* The op_array itself will be serialized as part of the dynamic_func_defs. */ SERIALIZE_PTR(zend_ast_get_op_array(ast)->op_array); + + SERIALIZE_PTR(zend_ast_get_op_array(ast)->ast); + tmp = zend_ast_get_op_array(ast)->ast; + UNSERIALIZE_PTR(tmp); + zend_file_cache_serialize_ast(tmp, script, info, buf); } else if (zend_ast_is_decl(ast)) { zend_ast_decl *decl = (zend_ast_decl*)ast; for (i = 0; i < 5; i++) { @@ -1258,6 +1263,9 @@ static void zend_file_cache_unserialize_ast(zend_ast *ast, } else if (ast->kind == ZEND_AST_OP_ARRAY) { /* The op_array itself will be unserialized as part of the dynamic_func_defs. */ UNSERIALIZE_PTR(zend_ast_get_op_array(ast)->op_array); + + UNSERIALIZE_PTR(zend_ast_get_op_array(ast)->ast); + zend_file_cache_unserialize_ast(zend_ast_get_op_array(ast)->ast, script, buf); } else if (zend_ast_is_decl(ast)) { zend_ast_decl *decl = (zend_ast_decl*)ast; for (i = 0; i < 5; i++) { diff --git a/ext/opcache/zend_persist.c b/ext/opcache/zend_persist.c index fb98372202377..57dc303068c98 100644 --- a/ext/opcache/zend_persist.c +++ b/ext/opcache/zend_persist.c @@ -194,6 +194,7 @@ static zend_ast *zend_persist_ast(zend_ast *ast) ZVAL_PTR(&z, copy->op_array); zend_persist_op_array(&z); copy->op_array = Z_PTR(z); + copy->ast = zend_persist_ast(copy->ast); node = (zend_ast *) copy; } else if (zend_ast_is_decl(ast)) { zend_ast_decl *copy = zend_shared_memdup(ast, sizeof(zend_ast_decl)); diff --git a/ext/opcache/zend_persist_calc.c b/ext/opcache/zend_persist_calc.c index 0163f3840bb1b..bf0ab2bdd7dbd 100644 --- a/ext/opcache/zend_persist_calc.c +++ b/ext/opcache/zend_persist_calc.c @@ -91,6 +91,7 @@ static void zend_persist_ast_calc(zend_ast *ast) zval z; ZVAL_PTR(&z, zend_ast_get_op_array(ast)->op_array); zend_persist_op_array_calc(&z); + zend_persist_ast_calc(zend_ast_get_op_array(ast)->ast); } else if (zend_ast_is_decl(ast)) { zend_ast_decl *decl = (zend_ast_decl*)ast; ADD_SIZE(sizeof(zend_ast_decl)); From f7e035afc75473af68f36d762b0d4b5e0d2285e3 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Tim=20D=C3=BCsterhus?= Date: Fri, 3 Jan 2025 09:34:38 +0100 Subject: [PATCH 3/5] Store original AST as string for `zend_ast_op_array` --- Zend/zend_ast.c | 20 ++++++++++---------- Zend/zend_ast.h | 7 +++---- Zend/zend_compile.c | 5 ++++- ext/opcache/zend_file_cache.c | 8 ++------ ext/opcache/zend_persist.c | 2 +- ext/opcache/zend_persist_calc.c | 2 +- 6 files changed, 21 insertions(+), 23 deletions(-) diff --git a/Zend/zend_ast.c b/Zend/zend_ast.c index ecba06d737260..bcd10302ebfe3 100644 --- a/Zend/zend_ast.c +++ b/Zend/zend_ast.c @@ -100,14 +100,15 @@ ZEND_API zend_ast * ZEND_FASTCALL zend_ast_create_constant(zend_string *name, ze return (zend_ast *) ast; } -ZEND_API zend_ast * ZEND_FASTCALL zend_ast_create_op_array(zend_op_array *op_array, zend_ast *original_ast, zend_ast_attr attr) { +ZEND_API zend_ast * ZEND_FASTCALL zend_ast_create_op_array(zend_op_array *op_array, zend_string *original_ast) { zend_ast_op_array *ast; ast = zend_ast_alloc(sizeof(zend_ast_op_array)); ast->kind = ZEND_AST_OP_ARRAY; - ast->attr = attr; + ast->attr = 0; + ast->lineno = CG(zend_lineno); ast->op_array = op_array; - ast->ast = original_ast; + ast->original_ast = original_ast; return (zend_ast *) ast; } @@ -1091,7 +1092,7 @@ static size_t ZEND_FASTCALL zend_ast_tree_size(zend_ast *ast) if (ast->kind == ZEND_AST_ZVAL || ast->kind == ZEND_AST_CONSTANT) { size = sizeof(zend_ast_zval); } else if (ast->kind == ZEND_AST_OP_ARRAY) { - size = sizeof(zend_ast_op_array) + zend_ast_tree_size(zend_ast_get_op_array(ast)->ast); + size = sizeof(zend_ast_op_array); } else if (zend_ast_is_list(ast)) { uint32_t i; zend_ast_list *list = zend_ast_get_list(ast); @@ -1161,10 +1162,10 @@ static void* ZEND_FASTCALL zend_ast_tree_copy(zend_ast *ast, void *buf) zend_ast_op_array *new = (zend_ast_op_array*)buf; new->kind = old->kind; new->attr = old->attr; + new->lineno = old->lineno; new->op_array = old->op_array; + new->original_ast = zend_string_copy(old->original_ast); buf = (void*)((char*)buf + sizeof(zend_ast_op_array)); - new->ast = (zend_ast*)buf; - buf = zend_ast_tree_copy(old->ast, buf); } else if (zend_ast_is_decl(ast)) { zend_ast_decl *old = (zend_ast_decl*)ast; zend_ast_decl *new = (zend_ast_decl*)buf; @@ -1257,7 +1258,7 @@ ZEND_API void ZEND_FASTCALL zend_ast_destroy(zend_ast *ast) } else if (EXPECTED(ast->kind == ZEND_AST_CONSTANT)) { zend_string_release_ex(zend_ast_get_constant_name(ast), 0); } else if (EXPECTED(ast->kind == ZEND_AST_OP_ARRAY)) { - zend_ast_destroy(zend_ast_get_op_array(ast)->ast); + zend_string_release_ex(zend_ast_get_op_array(ast)->original_ast, 0); } else if (zend_ast_is_decl(ast)) { zend_ast_decl *decl = (zend_ast_decl *) ast; @@ -1916,10 +1917,9 @@ static ZEND_COLD void zend_ast_export_ex(smart_str *str, zend_ast *ast, int prio smart_str_appendl(str, ZSTR_VAL(name), ZSTR_LEN(name)); break; } - case ZEND_AST_OP_ARRAY: { - zend_ast_export_ex(str, zend_ast_get_op_array(ast)->ast, priority, indent); + case ZEND_AST_OP_ARRAY: + smart_str_append(str, zend_ast_get_op_array(ast)->original_ast); break; - } case ZEND_AST_CONSTANT_CLASS: smart_str_appendl(str, "__CLASS__", sizeof("__CLASS__")-1); break; diff --git a/Zend/zend_ast.h b/Zend/zend_ast.h index db9c28fd474d5..722b85299d20f 100644 --- a/Zend/zend_ast.h +++ b/Zend/zend_ast.h @@ -212,8 +212,9 @@ typedef struct _zend_op_array zend_op_array; typedef struct _zend_ast_op_array { zend_ast_kind kind; zend_ast_attr attr; + uint32_t lineno; zend_op_array *op_array; - zend_ast *ast; + zend_string *original_ast; } zend_ast_op_array; /* Separate structure for function and class declaration, as they need extra information. */ @@ -240,7 +241,7 @@ ZEND_API zend_ast * ZEND_FASTCALL zend_ast_create_zval_from_long(zend_long lval) ZEND_API zend_ast * ZEND_FASTCALL zend_ast_create_constant(zend_string *name, zend_ast_attr attr); ZEND_API zend_ast * ZEND_FASTCALL zend_ast_create_class_const_or_name(zend_ast *class_name, zend_ast *name); -ZEND_API zend_ast * ZEND_FASTCALL zend_ast_create_op_array(zend_op_array *op_array, zend_ast *original_ast, zend_ast_attr attr); +ZEND_API zend_ast * ZEND_FASTCALL zend_ast_create_op_array(zend_op_array *op_array, zend_string *original_ast); #if ZEND_AST_SPEC # define ZEND_AST_SPEC_CALL(name, ...) \ @@ -388,8 +389,6 @@ static zend_always_inline uint32_t zend_ast_get_lineno(zend_ast *ast) { } else if (ast->kind == ZEND_AST_CONSTANT) { zval *zv = &((zend_ast_zval *) ast)->val; return Z_LINENO_P(zv); - } else if (ast->kind == ZEND_AST_OP_ARRAY) { - return zend_ast_get_op_array(ast)->ast->lineno; } else { return ast->lineno; } diff --git a/Zend/zend_compile.c b/Zend/zend_compile.c index db3e43f403f19..56b29d2c98a44 100644 --- a/Zend/zend_compile.c +++ b/Zend/zend_compile.c @@ -11222,7 +11222,10 @@ static void zend_compile_const_expr_closure(zend_ast **ast_ptr) znode node; zend_op_array *op = zend_compile_func_decl(&node, (zend_ast*)closure_ast, FUNC_DECL_LEVEL_CONSTEXPR); - *ast_ptr = zend_ast_create_op_array(op, (zend_ast*)closure_ast, 0); + zend_string *original_ast = zend_ast_export("", *ast_ptr, ""); + zend_ast_destroy(*ast_ptr); + *ast_ptr = zend_ast_create_op_array(op, original_ast); + } static void zend_compile_const_expr_args(zend_ast **ast_ptr) diff --git a/ext/opcache/zend_file_cache.c b/ext/opcache/zend_file_cache.c index 7ea946ececc0c..93281fdefb805 100644 --- a/ext/opcache/zend_file_cache.c +++ b/ext/opcache/zend_file_cache.c @@ -367,10 +367,7 @@ static void zend_file_cache_serialize_ast(zend_ast *ast, /* The op_array itself will be serialized as part of the dynamic_func_defs. */ SERIALIZE_PTR(zend_ast_get_op_array(ast)->op_array); - SERIALIZE_PTR(zend_ast_get_op_array(ast)->ast); - tmp = zend_ast_get_op_array(ast)->ast; - UNSERIALIZE_PTR(tmp); - zend_file_cache_serialize_ast(tmp, script, info, buf); + SERIALIZE_STR(zend_ast_get_op_array(ast)->original_ast); } else if (zend_ast_is_decl(ast)) { zend_ast_decl *decl = (zend_ast_decl*)ast; for (i = 0; i < 5; i++) { @@ -1264,8 +1261,7 @@ static void zend_file_cache_unserialize_ast(zend_ast *ast, /* The op_array itself will be unserialized as part of the dynamic_func_defs. */ UNSERIALIZE_PTR(zend_ast_get_op_array(ast)->op_array); - UNSERIALIZE_PTR(zend_ast_get_op_array(ast)->ast); - zend_file_cache_unserialize_ast(zend_ast_get_op_array(ast)->ast, script, buf); + UNSERIALIZE_STR(zend_ast_get_op_array(ast)->original_ast); } else if (zend_ast_is_decl(ast)) { zend_ast_decl *decl = (zend_ast_decl*)ast; for (i = 0; i < 5; i++) { diff --git a/ext/opcache/zend_persist.c b/ext/opcache/zend_persist.c index 57dc303068c98..78943c1a024b4 100644 --- a/ext/opcache/zend_persist.c +++ b/ext/opcache/zend_persist.c @@ -194,7 +194,7 @@ static zend_ast *zend_persist_ast(zend_ast *ast) ZVAL_PTR(&z, copy->op_array); zend_persist_op_array(&z); copy->op_array = Z_PTR(z); - copy->ast = zend_persist_ast(copy->ast); + zend_accel_store_interned_string(copy->original_ast); node = (zend_ast *) copy; } else if (zend_ast_is_decl(ast)) { zend_ast_decl *copy = zend_shared_memdup(ast, sizeof(zend_ast_decl)); diff --git a/ext/opcache/zend_persist_calc.c b/ext/opcache/zend_persist_calc.c index bf0ab2bdd7dbd..48695e897918a 100644 --- a/ext/opcache/zend_persist_calc.c +++ b/ext/opcache/zend_persist_calc.c @@ -91,7 +91,7 @@ static void zend_persist_ast_calc(zend_ast *ast) zval z; ZVAL_PTR(&z, zend_ast_get_op_array(ast)->op_array); zend_persist_op_array_calc(&z); - zend_persist_ast_calc(zend_ast_get_op_array(ast)->ast); + ADD_INTERNED_STRING(zend_ast_get_op_array(ast)->original_ast); } else if (zend_ast_is_decl(ast)) { zend_ast_decl *decl = (zend_ast_decl*)ast; ADD_SIZE(sizeof(zend_ast_decl)); From dd80c75e4e17f0a1a93df2d18d8ad17ce0e14c81 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Tim=20D=C3=BCsterhus?= Date: Fri, 3 Jan 2025 09:34:38 +0100 Subject: [PATCH 4/5] Remove AST copying support for zend_ast_decl This is unreachable / untestable now. --- Zend/zend_ast.c | 40 +++++---------------------------- ext/opcache/zend_file_cache.c | 20 ++++------------- ext/opcache/zend_persist.c | 9 ++------ ext/opcache/zend_persist_calc.c | 9 ++------ 4 files changed, 13 insertions(+), 65 deletions(-) diff --git a/Zend/zend_ast.c b/Zend/zend_ast.c index bcd10302ebfe3..08d2de8080808 100644 --- a/Zend/zend_ast.c +++ b/Zend/zend_ast.c @@ -1104,13 +1104,8 @@ static size_t ZEND_FASTCALL zend_ast_tree_size(zend_ast *ast) } } } else if (zend_ast_is_decl(ast)) { - zend_ast_decl *decl = (zend_ast_decl*)ast; - size = sizeof(zend_ast_decl); - for (size_t i = 0; i < 5; i++) { - if (decl->child[i]) { - size += zend_ast_tree_size(decl->child[i]); - } - } + /* Not implemented. */ + ZEND_UNREACHABLE(); } else { uint32_t i, children = zend_ast_get_num_children(ast); @@ -1167,33 +1162,8 @@ static void* ZEND_FASTCALL zend_ast_tree_copy(zend_ast *ast, void *buf) new->original_ast = zend_string_copy(old->original_ast); buf = (void*)((char*)buf + sizeof(zend_ast_op_array)); } else if (zend_ast_is_decl(ast)) { - zend_ast_decl *old = (zend_ast_decl*)ast; - zend_ast_decl *new = (zend_ast_decl*)buf; - new->kind = old->kind; - new->attr = old->attr; - new->start_lineno = old->start_lineno; - new->end_lineno = old->end_lineno; - new->flags = old->flags; - if (old->doc_comment) { - new->doc_comment = zend_string_copy(old->doc_comment); - } else { - new->doc_comment = NULL; - } - if (old->name) { - new->name = zend_string_copy(old->name); - } else { - new->name = NULL; - } - - buf = (void*)((char*)buf + sizeof(zend_ast_decl)); - for (size_t i = 0; i < 5; i++) { - if (old->child[i]) { - new->child[i] = (zend_ast*)buf; - buf = zend_ast_tree_copy(old->child[i], buf); - } else { - new->child[i] = NULL; - } - } + /* Not implemented. */ + ZEND_UNREACHABLE(); } else { uint32_t i, children = zend_ast_get_num_children(ast); zend_ast *new = (zend_ast*)buf; @@ -1259,7 +1229,7 @@ ZEND_API void ZEND_FASTCALL zend_ast_destroy(zend_ast *ast) zend_string_release_ex(zend_ast_get_constant_name(ast), 0); } else if (EXPECTED(ast->kind == ZEND_AST_OP_ARRAY)) { zend_string_release_ex(zend_ast_get_op_array(ast)->original_ast, 0); - } else if (zend_ast_is_decl(ast)) { + } else if (EXPECTED(zend_ast_is_decl(ast))) { zend_ast_decl *decl = (zend_ast_decl *) ast; if (decl->name) { diff --git a/ext/opcache/zend_file_cache.c b/ext/opcache/zend_file_cache.c index 93281fdefb805..e851c9711c37e 100644 --- a/ext/opcache/zend_file_cache.c +++ b/ext/opcache/zend_file_cache.c @@ -369,15 +369,8 @@ static void zend_file_cache_serialize_ast(zend_ast *ast, SERIALIZE_STR(zend_ast_get_op_array(ast)->original_ast); } else if (zend_ast_is_decl(ast)) { - zend_ast_decl *decl = (zend_ast_decl*)ast; - for (i = 0; i < 5; i++) { - if (decl->child[i] && !IS_SERIALIZED(decl->child[i])) { - SERIALIZE_PTR(decl->child[i]); - tmp = decl->child[i]; - UNSERIALIZE_PTR(tmp); - zend_file_cache_serialize_ast(tmp, script, info, buf); - } - } + /* Not implemented. */ + ZEND_UNREACHABLE(); } else { uint32_t children = zend_ast_get_num_children(ast); for (i = 0; i < children; i++) { @@ -1263,13 +1256,8 @@ static void zend_file_cache_unserialize_ast(zend_ast *ast, UNSERIALIZE_STR(zend_ast_get_op_array(ast)->original_ast); } else if (zend_ast_is_decl(ast)) { - zend_ast_decl *decl = (zend_ast_decl*)ast; - for (i = 0; i < 5; i++) { - if (decl->child[i] && !IS_UNSERIALIZED(decl->child[i])) { - UNSERIALIZE_PTR(decl->child[i]); - zend_file_cache_unserialize_ast(decl->child[i], script, buf); - } - } + /* Not implemented. */ + ZEND_UNREACHABLE(); } else { uint32_t children = zend_ast_get_num_children(ast); for (i = 0; i < children; i++) { diff --git a/ext/opcache/zend_persist.c b/ext/opcache/zend_persist.c index 78943c1a024b4..4151946ceada1 100644 --- a/ext/opcache/zend_persist.c +++ b/ext/opcache/zend_persist.c @@ -197,13 +197,8 @@ static zend_ast *zend_persist_ast(zend_ast *ast) zend_accel_store_interned_string(copy->original_ast); node = (zend_ast *) copy; } else if (zend_ast_is_decl(ast)) { - zend_ast_decl *copy = zend_shared_memdup(ast, sizeof(zend_ast_decl)); - for (i = 0; i < 5; i++) { - if (copy->child[i]) { - copy->child[i] = zend_persist_ast(copy->child[i]); - } - } - node = (zend_ast *) copy; + /* Not implemented. */ + ZEND_UNREACHABLE(); } else { uint32_t children = zend_ast_get_num_children(ast); node = zend_shared_memdup(ast, zend_ast_size(children)); diff --git a/ext/opcache/zend_persist_calc.c b/ext/opcache/zend_persist_calc.c index 48695e897918a..4b69533997c69 100644 --- a/ext/opcache/zend_persist_calc.c +++ b/ext/opcache/zend_persist_calc.c @@ -93,13 +93,8 @@ static void zend_persist_ast_calc(zend_ast *ast) zend_persist_op_array_calc(&z); ADD_INTERNED_STRING(zend_ast_get_op_array(ast)->original_ast); } else if (zend_ast_is_decl(ast)) { - zend_ast_decl *decl = (zend_ast_decl*)ast; - ADD_SIZE(sizeof(zend_ast_decl)); - for (size_t i = 0; i < 5; i++) { - if (decl->child[i]) { - zend_persist_ast_calc(decl->child[i]); - } - } + /* Not implemented. */ + ZEND_UNREACHABLE(); } else { uint32_t children = zend_ast_get_num_children(ast); ADD_SIZE(zend_ast_size(children)); From f029cc144977ac8816caded028b6eb8ebdfc08fe Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Tim=20D=C3=BCsterhus?= Date: Fri, 3 Jan 2025 14:47:24 +0100 Subject: [PATCH 5/5] Print placeholder value when printing `ZEND_AST_OP_ARRAY` --- .../attributes_ast_printing_runtime.phpt | 4 +--- Zend/zend_ast.c | 10 +++++----- Zend/zend_ast.h | 3 +-- Zend/zend_compile.c | 4 +--- ext/opcache/zend_file_cache.c | 4 ---- ext/opcache/zend_persist.c | 1 - ext/opcache/zend_persist_calc.c | 1 - 7 files changed, 8 insertions(+), 19 deletions(-) diff --git a/Zend/tests/closure_const_expr/attributes_ast_printing_runtime.phpt b/Zend/tests/closure_const_expr/attributes_ast_printing_runtime.phpt index 85d5ae925d967..dfdb32e492f0c 100644 --- a/Zend/tests/closure_const_expr/attributes_ast_printing_runtime.phpt +++ b/Zend/tests/closure_const_expr/attributes_ast_printing_runtime.phpt @@ -17,8 +17,6 @@ foreach ($r->getAttributes() as $attribute) { --EXPECT-- Attribute [ Attr ] { - Arguments [1] { - Argument #0 [ static function ($foo) { - echo $foo; -} ] + Argument #0 [ Closure({closure:foo():3}) ] } } diff --git a/Zend/zend_ast.c b/Zend/zend_ast.c index 08d2de8080808..9258f4f30f462 100644 --- a/Zend/zend_ast.c +++ b/Zend/zend_ast.c @@ -100,7 +100,7 @@ ZEND_API zend_ast * ZEND_FASTCALL zend_ast_create_constant(zend_string *name, ze return (zend_ast *) ast; } -ZEND_API zend_ast * ZEND_FASTCALL zend_ast_create_op_array(zend_op_array *op_array, zend_string *original_ast) { +ZEND_API zend_ast * ZEND_FASTCALL zend_ast_create_op_array(zend_op_array *op_array) { zend_ast_op_array *ast; ast = zend_ast_alloc(sizeof(zend_ast_op_array)); @@ -108,7 +108,6 @@ ZEND_API zend_ast * ZEND_FASTCALL zend_ast_create_op_array(zend_op_array *op_arr ast->attr = 0; ast->lineno = CG(zend_lineno); ast->op_array = op_array; - ast->original_ast = original_ast; return (zend_ast *) ast; } @@ -1159,7 +1158,6 @@ static void* ZEND_FASTCALL zend_ast_tree_copy(zend_ast *ast, void *buf) new->attr = old->attr; new->lineno = old->lineno; new->op_array = old->op_array; - new->original_ast = zend_string_copy(old->original_ast); buf = (void*)((char*)buf + sizeof(zend_ast_op_array)); } else if (zend_ast_is_decl(ast)) { /* Not implemented. */ @@ -1228,7 +1226,7 @@ ZEND_API void ZEND_FASTCALL zend_ast_destroy(zend_ast *ast) } else if (EXPECTED(ast->kind == ZEND_AST_CONSTANT)) { zend_string_release_ex(zend_ast_get_constant_name(ast), 0); } else if (EXPECTED(ast->kind == ZEND_AST_OP_ARRAY)) { - zend_string_release_ex(zend_ast_get_op_array(ast)->original_ast, 0); + /* Nothing to do. */ } else if (EXPECTED(zend_ast_is_decl(ast))) { zend_ast_decl *decl = (zend_ast_decl *) ast; @@ -1888,7 +1886,9 @@ static ZEND_COLD void zend_ast_export_ex(smart_str *str, zend_ast *ast, int prio break; } case ZEND_AST_OP_ARRAY: - smart_str_append(str, zend_ast_get_op_array(ast)->original_ast); + smart_str_appends(str, "Closure("); + smart_str_append(str, zend_ast_get_op_array(ast)->op_array->function_name); + smart_str_appends(str, ")"); break; case ZEND_AST_CONSTANT_CLASS: smart_str_appendl(str, "__CLASS__", sizeof("__CLASS__")-1); diff --git a/Zend/zend_ast.h b/Zend/zend_ast.h index 722b85299d20f..db41802b16f16 100644 --- a/Zend/zend_ast.h +++ b/Zend/zend_ast.h @@ -214,7 +214,6 @@ typedef struct _zend_ast_op_array { zend_ast_attr attr; uint32_t lineno; zend_op_array *op_array; - zend_string *original_ast; } zend_ast_op_array; /* Separate structure for function and class declaration, as they need extra information. */ @@ -241,7 +240,7 @@ ZEND_API zend_ast * ZEND_FASTCALL zend_ast_create_zval_from_long(zend_long lval) ZEND_API zend_ast * ZEND_FASTCALL zend_ast_create_constant(zend_string *name, zend_ast_attr attr); ZEND_API zend_ast * ZEND_FASTCALL zend_ast_create_class_const_or_name(zend_ast *class_name, zend_ast *name); -ZEND_API zend_ast * ZEND_FASTCALL zend_ast_create_op_array(zend_op_array *op_array, zend_string *original_ast); +ZEND_API zend_ast * ZEND_FASTCALL zend_ast_create_op_array(zend_op_array *op_array); #if ZEND_AST_SPEC # define ZEND_AST_SPEC_CALL(name, ...) \ diff --git a/Zend/zend_compile.c b/Zend/zend_compile.c index 56b29d2c98a44..23dfd4577812a 100644 --- a/Zend/zend_compile.c +++ b/Zend/zend_compile.c @@ -11222,10 +11222,8 @@ static void zend_compile_const_expr_closure(zend_ast **ast_ptr) znode node; zend_op_array *op = zend_compile_func_decl(&node, (zend_ast*)closure_ast, FUNC_DECL_LEVEL_CONSTEXPR); - zend_string *original_ast = zend_ast_export("", *ast_ptr, ""); zend_ast_destroy(*ast_ptr); - *ast_ptr = zend_ast_create_op_array(op, original_ast); - + *ast_ptr = zend_ast_create_op_array(op); } static void zend_compile_const_expr_args(zend_ast **ast_ptr) diff --git a/ext/opcache/zend_file_cache.c b/ext/opcache/zend_file_cache.c index e851c9711c37e..74e0d19d1b6de 100644 --- a/ext/opcache/zend_file_cache.c +++ b/ext/opcache/zend_file_cache.c @@ -366,8 +366,6 @@ static void zend_file_cache_serialize_ast(zend_ast *ast, } else if (ast->kind == ZEND_AST_OP_ARRAY) { /* The op_array itself will be serialized as part of the dynamic_func_defs. */ SERIALIZE_PTR(zend_ast_get_op_array(ast)->op_array); - - SERIALIZE_STR(zend_ast_get_op_array(ast)->original_ast); } else if (zend_ast_is_decl(ast)) { /* Not implemented. */ ZEND_UNREACHABLE(); @@ -1253,8 +1251,6 @@ static void zend_file_cache_unserialize_ast(zend_ast *ast, } else if (ast->kind == ZEND_AST_OP_ARRAY) { /* The op_array itself will be unserialized as part of the dynamic_func_defs. */ UNSERIALIZE_PTR(zend_ast_get_op_array(ast)->op_array); - - UNSERIALIZE_STR(zend_ast_get_op_array(ast)->original_ast); } else if (zend_ast_is_decl(ast)) { /* Not implemented. */ ZEND_UNREACHABLE(); diff --git a/ext/opcache/zend_persist.c b/ext/opcache/zend_persist.c index 4151946ceada1..9e89980b67e14 100644 --- a/ext/opcache/zend_persist.c +++ b/ext/opcache/zend_persist.c @@ -194,7 +194,6 @@ static zend_ast *zend_persist_ast(zend_ast *ast) ZVAL_PTR(&z, copy->op_array); zend_persist_op_array(&z); copy->op_array = Z_PTR(z); - zend_accel_store_interned_string(copy->original_ast); node = (zend_ast *) copy; } else if (zend_ast_is_decl(ast)) { /* Not implemented. */ diff --git a/ext/opcache/zend_persist_calc.c b/ext/opcache/zend_persist_calc.c index 4b69533997c69..0c53923354c42 100644 --- a/ext/opcache/zend_persist_calc.c +++ b/ext/opcache/zend_persist_calc.c @@ -91,7 +91,6 @@ static void zend_persist_ast_calc(zend_ast *ast) zval z; ZVAL_PTR(&z, zend_ast_get_op_array(ast)->op_array); zend_persist_op_array_calc(&z); - ADD_INTERNED_STRING(zend_ast_get_op_array(ast)->original_ast); } else if (zend_ast_is_decl(ast)) { /* Not implemented. */ ZEND_UNREACHABLE();