Skip to content

Commit 2b5f8b1

Browse files
committed
fixup! Support first-class callables in const-expressions
1 parent 25db00e commit 2b5f8b1

File tree

3 files changed

+107
-60
lines changed

3 files changed

+107
-60
lines changed

Zend/zend_ast.c

Lines changed: 92 additions & 56 deletions
Original file line numberDiff line numberDiff line change
@@ -54,6 +54,18 @@ ZEND_API zend_ast * ZEND_FASTCALL zend_ast_create_znode(znode *node) {
5454
return (zend_ast *) ast;
5555
}
5656

57+
ZEND_API zend_ast * ZEND_FASTCALL zend_ast_create_fcc(void) {
58+
zend_ast_fcc *ast;
59+
60+
ast = zend_ast_alloc(sizeof(zend_ast_fcc));
61+
ast->kind = ZEND_AST_CALLABLE_CONVERT;
62+
ast->attr = 0;
63+
ast->lineno = ZEND_MAP_PTR_NEW_OFFSET();
64+
ZEND_MAP_PTR_NEW(ast->fptr);
65+
66+
return (zend_ast *) ast;
67+
}
68+
5769
static zend_always_inline zend_ast * zend_ast_create_zval_int(zval *zv, uint32_t attr, uint32_t lineno) {
5870
zend_ast_zval *ast;
5971

@@ -1009,81 +1021,96 @@ ZEND_API zend_result ZEND_FASTCALL zend_ast_evaluate_inner(
10091021
switch (ast->kind) {
10101022
case ZEND_AST_CALL: {
10111023
ZEND_ASSERT(ast->child[1]->kind == ZEND_AST_CALLABLE_CONVERT);
1024+
zend_ast_fcc *fcc_ast = (zend_ast_fcc*)ast->child[1];
1025+
fptr = ZEND_MAP_PTR_GET(fcc_ast->fptr);
10121026

1013-
zend_string *function_name = zend_ast_get_str(ast->child[0]);
1014-
zend_string *function_name_lc = zend_string_tolower(function_name);
1015-
fptr = zend_fetch_function(function_name_lc);
1016-
if (!fptr && ast->child[0]->attr != ZEND_NAME_FQ) {
1017-
const char *backslash = zend_memrchr(ZSTR_VAL(function_name_lc), '\\', ZSTR_LEN(function_name_lc));
1018-
if (backslash) {
1019-
fptr = zend_fetch_function_str(backslash + 1, ZSTR_LEN(function_name_lc) - (backslash - ZSTR_VAL(function_name_lc) + 1));
1020-
}
1021-
}
1022-
zend_string_release(function_name_lc);
10231027
if (!fptr) {
1024-
zend_throw_error(NULL, "Call to undefined function %s()", ZSTR_VAL(function_name));
1025-
return FAILURE;
1028+
zend_string *function_name = zend_ast_get_str(ast->child[0]);
1029+
zend_string *function_name_lc = zend_string_tolower(function_name);
1030+
fptr = zend_fetch_function(function_name_lc);
1031+
if (!fptr && ast->child[0]->attr != ZEND_NAME_FQ) {
1032+
const char *backslash = zend_memrchr(ZSTR_VAL(function_name_lc), '\\', ZSTR_LEN(function_name_lc));
1033+
if (backslash) {
1034+
fptr = zend_fetch_function_str(backslash + 1, ZSTR_LEN(function_name_lc) - (backslash - ZSTR_VAL(function_name_lc) + 1));
1035+
}
1036+
}
1037+
zend_string_release(function_name_lc);
1038+
if (!fptr) {
1039+
zend_throw_error(NULL, "Call to undefined function %s()", ZSTR_VAL(function_name));
1040+
return FAILURE;
1041+
}
1042+
1043+
ZEND_MAP_PTR_SET(fcc_ast->fptr, fptr);
10261044
}
1045+
10271046
break;
10281047
}
10291048
case ZEND_AST_STATIC_CALL: {
10301049
ZEND_ASSERT(ast->child[2]->kind == ZEND_AST_CALLABLE_CONVERT);
1050+
zend_ast_fcc *fcc_ast = (zend_ast_fcc*)ast->child[2];
10311051

1032-
zend_class_entry *ce = zend_ast_fetch_class(ast->child[0], scope);
1033-
if (!ce) {
1034-
return FAILURE;
1035-
}
1036-
zend_string *method_name = zend_ast_get_str(ast->child[1]);
1037-
if (ce->get_static_method) {
1038-
fptr = ce->get_static_method(ce, method_name);
1039-
} else {
1040-
fptr = zend_hash_find_ptr_lc(&ce->function_table, method_name);
1041-
if (fptr) {
1042-
if (!(fptr->common.fn_flags & ZEND_ACC_PUBLIC)) {
1043-
if (UNEXPECTED(fptr->common.scope != scope)) {
1044-
if (
1045-
UNEXPECTED(fptr->op_array.fn_flags & ZEND_ACC_PRIVATE)
1046-
|| UNEXPECTED(!zend_check_protected(zend_get_function_root_class(fptr), scope))
1047-
) {
1048-
if (ce->__callstatic) {
1049-
zend_throw_error(NULL, "Creating a callable for the magic __callStatic() method is not supported in constant expressions");
1050-
} else {
1051-
zend_bad_method_call(fptr, method_name, scope);
1052-
}
1052+
fptr = ZEND_MAP_PTR_GET(fcc_ast->fptr);
10531053

1054-
return FAILURE;
1054+
if (!fptr) {
1055+
zend_class_entry *ce = zend_ast_fetch_class(ast->child[0], scope);
1056+
if (!ce) {
1057+
return FAILURE;
1058+
}
1059+
zend_string *method_name = zend_ast_get_str(ast->child[1]);
1060+
if (ce->get_static_method) {
1061+
fptr = ce->get_static_method(ce, method_name);
1062+
} else {
1063+
fptr = zend_hash_find_ptr_lc(&ce->function_table, method_name);
1064+
if (fptr) {
1065+
if (!(fptr->common.fn_flags & ZEND_ACC_PUBLIC)) {
1066+
if (UNEXPECTED(fptr->common.scope != scope)) {
1067+
if (
1068+
UNEXPECTED(fptr->op_array.fn_flags & ZEND_ACC_PRIVATE)
1069+
|| UNEXPECTED(!zend_check_protected(zend_get_function_root_class(fptr), scope))
1070+
) {
1071+
if (ce->__callstatic) {
1072+
zend_throw_error(NULL, "Creating a callable for the magic __callStatic() method is not supported in constant expressions");
1073+
} else {
1074+
zend_bad_method_call(fptr, method_name, scope);
1075+
}
1076+
1077+
return FAILURE;
1078+
}
10551079
}
10561080
}
1057-
}
1058-
} else {
1059-
if (ce->__callstatic) {
1060-
zend_throw_error(NULL, "Creating a callable for the magic __callStatic() method is not supported in constant expressions");
10611081
} else {
1062-
zend_undefined_method(ce, method_name);
1082+
if (ce->__callstatic) {
1083+
zend_throw_error(NULL, "Creating a callable for the magic __callStatic() method is not supported in constant expressions");
1084+
} else {
1085+
zend_undefined_method(ce, method_name);
1086+
}
1087+
1088+
return FAILURE;
10631089
}
1090+
}
10641091

1092+
if (!(fptr->common.fn_flags & ZEND_ACC_STATIC)) {
1093+
zend_non_static_method_call(fptr);
1094+
10651095
return FAILURE;
10661096
}
1067-
}
1068-
1069-
if (!(fptr->common.fn_flags & ZEND_ACC_STATIC)) {
1070-
zend_non_static_method_call(fptr);
1071-
1072-
return FAILURE;
1073-
}
1074-
if ((fptr->common.fn_flags & ZEND_ACC_ABSTRACT)) {
1075-
zend_abstract_method_call(fptr);
1076-
1077-
return FAILURE;
1078-
} else if (fptr->common.scope->ce_flags & ZEND_ACC_TRAIT) {
1079-
zend_error(E_DEPRECATED,
1080-
"Calling static trait method %s::%s is deprecated, "
1081-
"it should only be called on a class using the trait",
1082-
ZSTR_VAL(fptr->common.scope->name), ZSTR_VAL(fptr->common.function_name));
1083-
if (EG(exception)) {
1097+
if ((fptr->common.fn_flags & ZEND_ACC_ABSTRACT)) {
1098+
zend_abstract_method_call(fptr);
1099+
10841100
return FAILURE;
1101+
} else if (fptr->common.scope->ce_flags & ZEND_ACC_TRAIT) {
1102+
zend_error(E_DEPRECATED,
1103+
"Calling static trait method %s::%s is deprecated, "
1104+
"it should only be called on a class using the trait",
1105+
ZSTR_VAL(fptr->common.scope->name), ZSTR_VAL(fptr->common.function_name));
1106+
if (EG(exception)) {
1107+
return FAILURE;
1108+
}
10851109
}
1110+
1111+
ZEND_MAP_PTR_SET(fcc_ast->fptr, fptr);
10861112
}
1113+
10871114
break;
10881115
}
10891116
}
@@ -1182,6 +1209,8 @@ static size_t ZEND_FASTCALL zend_ast_tree_size(zend_ast *ast)
11821209
size = sizeof(zend_ast_zval);
11831210
} else if (ast->kind == ZEND_AST_OP_ARRAY) {
11841211
size = sizeof(zend_ast_op_array);
1212+
} else if (ast->kind == ZEND_AST_CALLABLE_CONVERT) {
1213+
size = sizeof(zend_ast_fcc);
11851214
} else if (zend_ast_is_list(ast)) {
11861215
uint32_t i;
11871216
zend_ast_list *list = zend_ast_get_list(ast);
@@ -1249,6 +1278,13 @@ static void* ZEND_FASTCALL zend_ast_tree_copy(zend_ast *ast, void *buf)
12491278
new->lineno = old->lineno;
12501279
new->op_array = old->op_array;
12511280
buf = (void*)((char*)buf + sizeof(zend_ast_op_array));
1281+
} else if (ast->kind == ZEND_AST_CALLABLE_CONVERT) {
1282+
zend_ast_fcc *new = (zend_ast_fcc*)buf;
1283+
new->kind = ZEND_AST_CALLABLE_CONVERT;
1284+
new->attr = ast->attr;
1285+
new->lineno = ast->lineno;
1286+
new->fptr__ptr = ((zend_ast_fcc *) ast)->fptr__ptr;
1287+
buf = (void*)((char*)buf + sizeof(zend_ast_fcc));
12521288
} else if (zend_ast_is_decl(ast)) {
12531289
/* Not implemented. */
12541290
ZEND_UNREACHABLE();

Zend/zend_ast.h

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -22,6 +22,7 @@
2222
#define ZEND_AST_H
2323

2424
#include "zend_types.h"
25+
#include "zend_map_ptr.h"
2526

2627
#ifndef ZEND_AST_SPEC
2728
# define ZEND_AST_SPEC 1
@@ -228,6 +229,13 @@ typedef struct _zend_ast_decl {
228229
zend_ast *child[5];
229230
} zend_ast_decl;
230231

232+
typedef struct _zend_ast_fcc {
233+
zend_ast_kind kind; /* Type of the node (ZEND_AST_* enum constant) */
234+
zend_ast_attr attr; /* Additional attribute, use depending on node type */
235+
uint32_t lineno; /* Line number */
236+
ZEND_MAP_PTR_DEF(zend_function *, fptr);
237+
} zend_ast_fcc;
238+
231239
typedef void (*zend_ast_process_t)(zend_ast *ast);
232240
extern ZEND_API zend_ast_process_t zend_ast_process;
233241

@@ -319,6 +327,8 @@ ZEND_API zend_ast *zend_ast_create_decl(
319327
zend_string *name, zend_ast *child0, zend_ast *child1, zend_ast *child2, zend_ast *child3, zend_ast *child4
320328
);
321329

330+
ZEND_API zend_ast * ZEND_FASTCALL zend_ast_create_fcc(void);
331+
322332
typedef struct {
323333
bool had_side_effects;
324334
} zend_ast_evaluate_ctx;

Zend/zend_compile.c

Lines changed: 5 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -11234,19 +11234,20 @@ static void zend_compile_const_expr_closure(zend_ast **ast_ptr)
1123411234

1123511235
static void zend_compile_const_expr_fcc(zend_ast **ast_ptr)
1123611236
{
11237-
zend_ast *args_ast;
11237+
zend_ast **args_ast;
1123811238
switch ((*ast_ptr)->kind) {
1123911239
case ZEND_AST_CALL:
11240-
args_ast = (*ast_ptr)->child[1];
11240+
args_ast = &(*ast_ptr)->child[1];
1124111241
break;
1124211242
case ZEND_AST_STATIC_CALL:
11243-
args_ast = (*ast_ptr)->child[2];
11243+
args_ast = &(*ast_ptr)->child[2];
1124411244
break;
1124511245
EMPTY_SWITCH_DEFAULT_CASE();
1124611246
}
11247-
if (args_ast->kind != ZEND_AST_CALLABLE_CONVERT) {
11247+
if ((*args_ast)->kind != ZEND_AST_CALLABLE_CONVERT) {
1124811248
zend_error_noreturn(E_COMPILE_ERROR, "Constant expression contains invalid operations");
1124911249
}
11250+
*args_ast = zend_ast_create_fcc();
1125011251

1125111252
switch ((*ast_ptr)->kind) {
1125211253
case ZEND_AST_CALL: {

0 commit comments

Comments
 (0)