Skip to content

Commit 3a0d845

Browse files
committed
Support PFA syntax
1 parent 7dd5f31 commit 3a0d845

File tree

7 files changed

+163
-20
lines changed

7 files changed

+163
-20
lines changed

Zend/zend_ast.c

Lines changed: 121 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -54,13 +54,14 @@ ZEND_API zend_ast * ZEND_FASTCALL zend_ast_create_znode(const znode *node) {
5454
return (zend_ast *) ast;
5555
}
5656

57-
ZEND_API zend_ast * ZEND_FASTCALL zend_ast_create_fcc(void) {
57+
ZEND_API zend_ast * ZEND_FASTCALL zend_ast_create_fcc(zend_ast *args) {
5858
zend_ast_fcc *ast;
5959

6060
ast = zend_ast_alloc(sizeof(zend_ast_fcc));
6161
ast->kind = ZEND_AST_CALLABLE_CONVERT;
6262
ast->attr = 0;
6363
ast->lineno = CG(zend_lineno);
64+
ast->args = args;
6465
ZEND_MAP_PTR_INIT(ast->fptr, NULL);
6566

6667
return (zend_ast *) ast;
@@ -157,6 +158,12 @@ ZEND_API zend_ast *zend_ast_create_decl(
157158
return (zend_ast *) ast;
158159
}
159160

161+
static bool zend_ast_is_placeholder_arg(zend_ast *arg) {
162+
return arg->kind == ZEND_AST_PLACEHOLDER_ARG
163+
|| (arg->kind == ZEND_AST_NAMED_ARG
164+
&& arg->child[1]->kind == ZEND_AST_PLACEHOLDER_ARG);
165+
}
166+
160167
#if ZEND_AST_SPEC
161168
ZEND_API zend_ast * ZEND_FASTCALL zend_ast_create_0(zend_ast_kind kind) {
162169
zend_ast *ast;
@@ -400,6 +407,30 @@ ZEND_API zend_ast * ZEND_FASTCALL zend_ast_create_list_2(zend_ast_kind kind, zen
400407

401408
return ast;
402409
}
410+
411+
ZEND_API zend_ast * ZEND_FASTCALL zend_ast_create_arg_list_0(zend_ast_kind kind) {
412+
return zend_ast_create_list(0, kind);
413+
}
414+
415+
ZEND_API zend_ast * ZEND_FASTCALL zend_ast_create_arg_list_1(zend_ast_kind kind, zend_ast *arg) {
416+
zend_ast *list = zend_ast_create_list(1, kind, arg);
417+
418+
if (zend_ast_is_placeholder_arg(arg)) {
419+
return zend_ast_create_fcc(list);
420+
}
421+
422+
return list;
423+
}
424+
425+
ZEND_API zend_ast * ZEND_FASTCALL zend_ast_create_arg_list_2(zend_ast_kind kind, zend_ast *arg1, zend_ast *arg2) {
426+
zend_ast *list = zend_ast_create_list(2, kind, arg1, arg2);
427+
428+
if (zend_ast_is_placeholder_arg(arg1) || zend_ast_is_placeholder_arg(arg2)) {
429+
return zend_ast_create_fcc(list);
430+
}
431+
432+
return list;
433+
}
403434
#else
404435
static zend_ast *zend_ast_create_from_va_list(zend_ast_kind kind, zend_ast_attr attr, va_list va) {
405436
uint32_t i, children = kind >> ZEND_AST_NUM_CHILDREN_SHIFT;
@@ -479,6 +510,41 @@ ZEND_API zend_ast *zend_ast_create_list(uint32_t init_children, zend_ast_kind ki
479510

480511
return ast;
481512
}
513+
514+
ZEND_API zend_ast *zend_ast_create_arg_list(uint32_t init_children, zend_ast_kind kind, ...) {
515+
zend_ast *ast;
516+
zend_ast_list *list;
517+
bool has_placeholders = false;
518+
519+
ast = zend_ast_alloc(zend_ast_list_size(4));
520+
list = (zend_ast_list *) ast;
521+
list->kind = kind;
522+
list->attr = 0;
523+
list->lineno = CG(zend_lineno);
524+
list->children = 0;
525+
526+
{
527+
va_list va;
528+
uint32_t i;
529+
va_start(va, kind);
530+
for (i = 0; i < init_children; ++i) {
531+
zend_ast *child = va_arg(va, zend_ast *);
532+
ast = zend_ast_list_add(ast, child);
533+
uint32_t lineno = zend_ast_get_lineno(child);
534+
if (lineno < ast->lineno) {
535+
ast->lineno = lineno;
536+
}
537+
has_placeholders = has_placeholders || zend_ast_is_placeholder_arg(child);
538+
}
539+
va_end(va);
540+
}
541+
542+
if (has_placeholders) {
543+
return zend_ast_create_fcc(list);
544+
}
545+
546+
return ast;
547+
}
482548
#endif
483549

484550
zend_ast *zend_ast_create_concat_op(zend_ast *op0, zend_ast *op1) {
@@ -508,6 +574,23 @@ ZEND_ATTRIBUTE_NODISCARD ZEND_API zend_ast * ZEND_FASTCALL zend_ast_list_add(zen
508574
return (zend_ast *) list;
509575
}
510576

577+
ZEND_API zend_ast * ZEND_FASTCALL zend_ast_arg_list_add(zend_ast *list, zend_ast *arg)
578+
{
579+
if (list->kind == ZEND_AST_CALLABLE_CONVERT) {
580+
zend_ast_fcc *fcc_ast = (zend_ast_fcc*)list;
581+
fcc_ast->args = zend_ast_list_add(fcc_ast->args, arg);
582+
return (zend_ast*)fcc_ast;
583+
}
584+
585+
ZEND_ASSERT(list->kind == ZEND_AST_ARG_LIST);
586+
587+
if (zend_ast_is_placeholder_arg(arg)) {
588+
return zend_ast_create_fcc(zend_ast_list_add(list, arg));
589+
}
590+
591+
return zend_ast_list_add(list, arg);
592+
}
593+
511594
static zend_result zend_ast_add_array_element(const zval *result, zval *offset, zval *expr)
512595
{
513596
if (Z_TYPE_P(offset) == IS_UNDEF) {
@@ -1060,6 +1143,14 @@ static zend_result ZEND_FASTCALL zend_ast_evaluate_inner(
10601143
case ZEND_AST_CALL: {
10611144
ZEND_ASSERT(ast->child[1]->kind == ZEND_AST_CALLABLE_CONVERT);
10621145
zend_ast_fcc *fcc_ast = (zend_ast_fcc*)ast->child[1];
1146+
1147+
zend_ast_list *args = zend_ast_get_list(fcc_ast->args);
1148+
ZEND_ASSERT(args->children > 0);
1149+
if (args->children != 1 || args->child[0]->attr != _IS_PLACEHOLDER_VARIADIC) {
1150+
/* TODO: PFAs */
1151+
return FAILURE;
1152+
}
1153+
10631154
fptr = ZEND_MAP_PTR_GET(fcc_ast->fptr);
10641155

10651156
if (!fptr) {
@@ -1087,6 +1178,13 @@ static zend_result ZEND_FASTCALL zend_ast_evaluate_inner(
10871178
ZEND_ASSERT(ast->child[2]->kind == ZEND_AST_CALLABLE_CONVERT);
10881179
zend_ast_fcc *fcc_ast = (zend_ast_fcc*)ast->child[2];
10891180

1181+
zend_ast_list *args = zend_ast_get_list(fcc_ast->args);
1182+
ZEND_ASSERT(args->children > 0);
1183+
if (args->children != 1 || args->child[0]->attr != _IS_PLACEHOLDER_VARIADIC) {
1184+
/* TODO: PFAs */
1185+
return FAILURE;
1186+
}
1187+
10901188
zend_class_entry *ce = zend_ast_fetch_class(ast->child[0], scope);
10911189
if (!ce) {
10921190
return FAILURE;
@@ -1124,12 +1222,12 @@ static zend_result ZEND_FASTCALL zend_ast_evaluate_inner(
11241222

11251223
if (!(fptr->common.fn_flags & ZEND_ACC_STATIC)) {
11261224
zend_non_static_method_call(fptr);
1127-
1225+
11281226
return FAILURE;
11291227
}
11301228
if ((fptr->common.fn_flags & ZEND_ACC_ABSTRACT)) {
11311229
zend_abstract_method_call(fptr);
1132-
1230+
11331231
return FAILURE;
11341232
} else if (fptr->common.scope->ce_flags & ZEND_ACC_TRAIT) {
11351233
zend_error(E_DEPRECATED,
@@ -1243,7 +1341,8 @@ static size_t ZEND_FASTCALL zend_ast_tree_size(zend_ast *ast)
12431341
} else if (ast->kind == ZEND_AST_OP_ARRAY) {
12441342
size = sizeof(zend_ast_op_array);
12451343
} else if (ast->kind == ZEND_AST_CALLABLE_CONVERT) {
1246-
size = sizeof(zend_ast_fcc);
1344+
zend_ast *args_ast = ((zend_ast_fcc*)ast)->args;
1345+
size = sizeof(zend_ast_fcc) + zend_ast_tree_size(args_ast);
12471346
} else if (zend_ast_is_list(ast)) {
12481347
uint32_t i;
12491348
const zend_ast_list *list = zend_ast_get_list(ast);
@@ -1320,6 +1419,8 @@ static void* ZEND_FASTCALL zend_ast_tree_copy(zend_ast *ast, void *buf)
13201419
new->lineno = old->lineno;
13211420
ZEND_MAP_PTR_INIT(new->fptr, ZEND_MAP_PTR(old->fptr));
13221421
buf = (void*)((char*)buf + sizeof(zend_ast_fcc));
1422+
new->args = buf;
1423+
buf = zend_ast_tree_copy(old->args, buf);
13231424
} else if (zend_ast_is_decl(ast)) {
13241425
/* Not implemented. */
13251426
ZEND_UNREACHABLE();
@@ -1403,6 +1504,10 @@ ZEND_API void ZEND_FASTCALL zend_ast_destroy(zend_ast *ast)
14031504
zend_ast_destroy(decl->child[3]);
14041505
ast = decl->child[4];
14051506
goto tail_call;
1507+
} else if (EXPECTED(ast->kind == ZEND_AST_CALLABLE_CONVERT)) {
1508+
zend_ast_fcc *fcc_ast = (zend_ast_fcc*) ast;
1509+
1510+
zend_ast_destroy(fcc_ast->args);
14061511
}
14071512
}
14081513

@@ -2299,6 +2404,13 @@ static ZEND_COLD void zend_ast_export_ex(smart_str *str, zend_ast *ast, int prio
22992404
EMPTY_SWITCH_DEFAULT_CASE();
23002405
}
23012406
break;
2407+
case ZEND_AST_PLACEHOLDER_ARG:
2408+
if (ast->attr == _IS_PLACEHOLDER_ARG) {
2409+
APPEND_STR("?");
2410+
} else if (ast->attr == _IS_PLACEHOLDER_VARIADIC) {
2411+
APPEND_STR("...");
2412+
}
2413+
break;
23022414

23032415
/* 1 child node */
23042416
case ZEND_AST_VAR:
@@ -2445,9 +2557,11 @@ static ZEND_COLD void zend_ast_export_ex(smart_str *str, zend_ast *ast, int prio
24452557
zend_ast_export_ex(str, ast->child[1], 0, indent);
24462558
smart_str_appendc(str, ')');
24472559
break;
2448-
case ZEND_AST_CALLABLE_CONVERT:
2449-
smart_str_appends(str, "...");
2450-
break;
2560+
case ZEND_AST_CALLABLE_CONVERT: {
2561+
zend_ast_fcc *fcc_ast = (zend_ast_fcc*)ast;
2562+
ast = fcc_ast->args;
2563+
goto simple_list;
2564+
}
24512565
case ZEND_AST_CLASS_CONST:
24522566
zend_ast_export_ns_name(str, ast->child[0], 0, indent);
24532567
smart_str_appends(str, "::");

Zend/zend_ast.h

Lines changed: 16 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -76,6 +76,7 @@ enum _zend_ast_kind {
7676
ZEND_AST_TYPE,
7777
ZEND_AST_CONSTANT_CLASS,
7878
ZEND_AST_CALLABLE_CONVERT,
79+
ZEND_AST_PLACEHOLDER_ARG,
7980

8081
/* 1 child node */
8182
ZEND_AST_VAR = 1 << ZEND_AST_NUM_CHILDREN_SHIFT,
@@ -229,10 +230,12 @@ typedef struct _zend_ast_decl {
229230
zend_ast *child[5];
230231
} zend_ast_decl;
231232

233+
// TODO: rename
232234
typedef struct _zend_ast_fcc {
233235
zend_ast_kind kind; /* Type of the node (ZEND_AST_* enum constant) */
234236
zend_ast_attr attr; /* Additional attribute, use depending on node type */
235237
uint32_t lineno; /* Line number */
238+
zend_ast *args;
236239
ZEND_MAP_PTR_DEF(zend_function *, fptr);
237240
} zend_ast_fcc;
238241

@@ -307,27 +310,39 @@ ZEND_API zend_ast * ZEND_FASTCALL zend_ast_create_list_0(zend_ast_kind kind);
307310
ZEND_API zend_ast * ZEND_FASTCALL zend_ast_create_list_1(zend_ast_kind kind, zend_ast *child);
308311
ZEND_API zend_ast * ZEND_FASTCALL zend_ast_create_list_2(zend_ast_kind kind, zend_ast *child1, zend_ast *child2);
309312

313+
ZEND_API zend_ast * ZEND_FASTCALL zend_ast_create_arg_list_0(zend_ast_kind kind);
314+
ZEND_API zend_ast * ZEND_FASTCALL zend_ast_create_arg_list_1(zend_ast_kind kind, zend_ast *child);
315+
ZEND_API zend_ast * ZEND_FASTCALL zend_ast_create_arg_list_2(zend_ast_kind kind, zend_ast *child1, zend_ast *child2);
316+
310317
# define zend_ast_create(...) \
311318
ZEND_AST_SPEC_CALL(zend_ast_create, __VA_ARGS__)
312319
# define zend_ast_create_ex(...) \
313320
ZEND_AST_SPEC_CALL_EX(zend_ast_create_ex, __VA_ARGS__)
314321
# define zend_ast_create_list(init_children, ...) \
315322
ZEND_AST_SPEC_CALL(zend_ast_create_list, __VA_ARGS__)
323+
# define zend_ast_create_arg_list(init_children, ...) \
324+
ZEND_AST_SPEC_CALL(zend_ast_create_arg_list, __VA_ARGS__)
316325

317326
#else
318327
ZEND_API zend_ast *zend_ast_create(zend_ast_kind kind, ...);
319328
ZEND_API zend_ast *zend_ast_create_ex(zend_ast_kind kind, zend_ast_attr attr, ...);
320329
ZEND_API zend_ast *zend_ast_create_list(uint32_t init_children, zend_ast_kind kind, ...);
330+
ZEND_API zend_ast *zend_ast_create_arg_list(uint32_t init_children, zend_ast_kind kind, ...);
321331
#endif
322332

323333
ZEND_ATTRIBUTE_NODISCARD ZEND_API zend_ast * ZEND_FASTCALL zend_ast_list_add(zend_ast *list, zend_ast *op);
324334

335+
/* Like zend_ast_list_add(), but wraps the list into a ZEND_AST_CALLABLE_CONVERT
336+
* if any arg is a ZEND_AST_PLACEHOLDER_ARG. list can be a zend_ast_list, or a
337+
* zend_ast_fcc. */
338+
ZEND_API zend_ast * ZEND_FASTCALL zend_ast_arg_list_add(zend_ast *list, zend_ast *arg);
339+
325340
ZEND_API zend_ast *zend_ast_create_decl(
326341
zend_ast_kind kind, uint32_t flags, uint32_t start_lineno, zend_string *doc_comment,
327342
zend_string *name, zend_ast *child0, zend_ast *child1, zend_ast *child2, zend_ast *child3, zend_ast *child4
328343
);
329344

330-
ZEND_API zend_ast * ZEND_FASTCALL zend_ast_create_fcc(void);
345+
ZEND_API zend_ast * ZEND_FASTCALL zend_ast_create_fcc(zend_ast *args);
331346

332347
typedef struct {
333348
bool had_side_effects;

Zend/zend_language_parser.y

Lines changed: 16 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -901,16 +901,15 @@ return_type:
901901
;
902902

903903
argument_list:
904-
'(' ')' { $$ = zend_ast_create_list(0, ZEND_AST_ARG_LIST); }
904+
'(' ')' { $$ = zend_ast_create_arg_list(0, ZEND_AST_ARG_LIST); }
905905
| '(' non_empty_argument_list possible_comma ')' { $$ = $2; }
906-
| '(' T_ELLIPSIS ')' { $$ = zend_ast_create_fcc(); }
907906
;
908907

909908
non_empty_argument_list:
910909
argument
911-
{ $$ = zend_ast_create_list(1, ZEND_AST_ARG_LIST, $1); }
910+
{ $$ = zend_ast_create_arg_list(1, ZEND_AST_ARG_LIST, $1); }
912911
| non_empty_argument_list ',' argument
913-
{ $$ = zend_ast_list_add($1, $3); }
912+
{ $$ = zend_ast_arg_list_add($1, $3); }
914913
;
915914

916915
/* `clone_argument_list` is necessary to resolve a parser ambiguity (shift-reduce conflict)
@@ -923,25 +922,31 @@ non_empty_argument_list:
923922
* syntax.
924923
*/
925924
clone_argument_list:
926-
'(' ')' { $$ = zend_ast_create_list(0, ZEND_AST_ARG_LIST); }
925+
'(' ')' { $$ = zend_ast_create_arg_list(0, ZEND_AST_ARG_LIST); }
927926
| '(' non_empty_clone_argument_list possible_comma ')' { $$ = $2; }
928-
| '(' expr ',' ')' { $$ = zend_ast_create_list(1, ZEND_AST_ARG_LIST, $2); }
929-
| '(' T_ELLIPSIS ')' { $$ = zend_ast_create_fcc(); }
927+
| '(' expr ',' ')' { $$ = zend_ast_create_arg_list(1, ZEND_AST_ARG_LIST, $2); }
930928
;
931929

932930
non_empty_clone_argument_list:
933931
expr ',' argument
934-
{ $$ = zend_ast_create_list(2, ZEND_AST_ARG_LIST, $1, $3); }
932+
{ $$ = zend_ast_create_arg_list(2, ZEND_AST_ARG_LIST, $1, $3); }
935933
| argument_no_expr
936-
{ $$ = zend_ast_create_list(1, ZEND_AST_ARG_LIST, $1); }
934+
{ $$ = zend_ast_create_arg_list(1, ZEND_AST_ARG_LIST, $1); }
937935
| non_empty_clone_argument_list ',' argument
938-
{ $$ = zend_ast_list_add($1, $3); }
936+
{ $$ = zend_ast_arg_list_add($1, $3); }
939937
;
940938

941939
argument_no_expr:
942940
identifier ':' expr
943941
{ $$ = zend_ast_create(ZEND_AST_NAMED_ARG, $1, $3); }
944-
| T_ELLIPSIS expr { $$ = zend_ast_create(ZEND_AST_UNPACK, $2); }
942+
| T_ELLIPSIS
943+
{ $$ = zend_ast_create_ex(ZEND_AST_PLACEHOLDER_ARG, _IS_PLACEHOLDER_VARIADIC); }
944+
| '?'
945+
{ $$ = zend_ast_create_ex(ZEND_AST_PLACEHOLDER_ARG, _IS_PLACEHOLDER_ARG); }
946+
| identifier ':' '?'
947+
{ $$ = zend_ast_create(ZEND_AST_NAMED_ARG, $1, zend_ast_create_ex(ZEND_AST_PLACEHOLDER_ARG, _IS_PLACEHOLDER_ARG)); }
948+
| T_ELLIPSIS expr
949+
{ $$ = zend_ast_create(ZEND_AST_UNPACK, $2); }
945950
;
946951

947952
argument:

Zend/zend_types.h

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -638,6 +638,10 @@ struct _zend_ast_ref {
638638
#define _IS_BOOL 18
639639
#define _IS_NUMBER 19
640640

641+
/* used for PFAs/FCCs */
642+
#define _IS_PLACEHOLDER_ARG 20
643+
#define _IS_PLACEHOLDER_VARIADIC 21
644+
641645
/* guard flags */
642646
#define ZEND_GUARD_PROPERTY_GET (1<<0)
643647
#define ZEND_GUARD_PROPERTY_SET (1<<1)

ext/opcache/zend_file_cache.c

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -384,6 +384,7 @@ static void zend_file_cache_serialize_ast(zend_ast *ast,
384384
} else if (ast->kind == ZEND_AST_CALLABLE_CONVERT) {
385385
zend_ast_fcc *fcc = (zend_ast_fcc*)ast;
386386
ZEND_MAP_PTR_INIT(fcc->fptr, NULL);
387+
zend_file_cache_serialize_ast(fcc->args, script, info, buf);
387388
} else if (zend_ast_is_decl(ast)) {
388389
/* Not implemented. */
389390
ZEND_UNREACHABLE();
@@ -1304,6 +1305,7 @@ static void zend_file_cache_unserialize_ast(zend_ast *ast,
13041305
} else if (ast->kind == ZEND_AST_CALLABLE_CONVERT) {
13051306
zend_ast_fcc *fcc = (zend_ast_fcc*)ast;
13061307
ZEND_MAP_PTR_NEW(fcc->fptr);
1308+
zend_file_cache_unserialize_ast(fcc->args, script, buf);
13071309
} else if (zend_ast_is_decl(ast)) {
13081310
/* Not implemented. */
13091311
ZEND_UNREACHABLE();
@@ -2109,7 +2111,7 @@ void zend_file_cache_invalidate(zend_string *full_path)
21092111
if (ZCG(accel_directives).file_cache_read_only) {
21102112
return;
21112113
}
2112-
2114+
21132115
char *filename;
21142116

21152117
filename = zend_file_cache_get_bin_file_path(full_path);

ext/opcache/zend_persist.c

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -197,6 +197,7 @@ static zend_ast *zend_persist_ast(zend_ast *ast)
197197
node = (zend_ast *) copy;
198198
} else if (ast->kind == ZEND_AST_CALLABLE_CONVERT) {
199199
zend_ast_fcc *copy = zend_shared_memdup(ast, sizeof(zend_ast_fcc));
200+
copy->args = zend_persist_ast(copy->args);
200201
node = (zend_ast *) copy;
201202
} else if (zend_ast_is_decl(ast)) {
202203
/* Not implemented. */

0 commit comments

Comments
 (0)