Skip to content

Commit 9800bc9

Browse files
committed
fixup! Support first-class callables in const-expressions
1 parent 0b05dfd commit 9800bc9

File tree

4 files changed

+109
-62
lines changed

4 files changed

+109
-62
lines changed

Zend/tests/closure_const_expr/fcc/namespace_004.phpt

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -48,14 +48,14 @@ object(Closure)#2 (2) {
4848
string(3) "cba"
4949
object(Closure)#2 (2) {
5050
["function"]=>
51-
string(10) "strrev"
51+
string(6) "strrev"
5252
["parameter"]=>
5353
array(1) {
5454
["$string"]=>
5555
string(10) "<required>"
5656
}
5757
}
58-
string(18) "cba"
58+
string(3) "cba"
5959
object(Closure)#1 (2) {
6060
["function"]=>
6161
string(6) "strrev"

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

@@ -997,81 +1009,96 @@ ZEND_API zend_result ZEND_FASTCALL zend_ast_evaluate_inner(
9971009
switch (ast->kind) {
9981010
case ZEND_AST_CALL: {
9991011
ZEND_ASSERT(ast->child[1]->kind == ZEND_AST_CALLABLE_CONVERT);
1012+
zend_ast_fcc *fcc_ast = (zend_ast_fcc*)ast->child[1];
1013+
fptr = ZEND_MAP_PTR_GET(fcc_ast->fptr);
10001014

1001-
zend_string *function_name = zend_ast_get_str(ast->child[0]);
1002-
zend_string *function_name_lc = zend_string_tolower(function_name);
1003-
fptr = zend_fetch_function(function_name_lc);
1004-
if (!fptr && ast->child[0]->attr != ZEND_NAME_FQ) {
1005-
const char *backslash = zend_memrchr(ZSTR_VAL(function_name_lc), '\\', ZSTR_LEN(function_name_lc));
1006-
if (backslash) {
1007-
fptr = zend_fetch_function_str(backslash + 1, ZSTR_LEN(function_name_lc) - (backslash - ZSTR_VAL(function_name_lc) + 1));
1008-
}
1009-
}
1010-
zend_string_release(function_name_lc);
10111015
if (!fptr) {
1012-
zend_throw_error(NULL, "Call to undefined function %s()", ZSTR_VAL(function_name));
1013-
return FAILURE;
1016+
zend_string *function_name = zend_ast_get_str(ast->child[0]);
1017+
zend_string *function_name_lc = zend_string_tolower(function_name);
1018+
fptr = zend_fetch_function(function_name_lc);
1019+
if (!fptr && ast->child[0]->attr != ZEND_NAME_FQ) {
1020+
const char *backslash = zend_memrchr(ZSTR_VAL(function_name_lc), '\\', ZSTR_LEN(function_name_lc));
1021+
if (backslash) {
1022+
fptr = zend_fetch_function_str(backslash + 1, ZSTR_LEN(function_name_lc) - (backslash - ZSTR_VAL(function_name_lc) + 1));
1023+
}
1024+
}
1025+
zend_string_release(function_name_lc);
1026+
if (!fptr) {
1027+
zend_throw_error(NULL, "Call to undefined function %s()", ZSTR_VAL(function_name));
1028+
return FAILURE;
1029+
}
1030+
1031+
ZEND_MAP_PTR_SET(fcc_ast->fptr, fptr);
10141032
}
1033+
10151034
break;
10161035
}
10171036
case ZEND_AST_STATIC_CALL: {
10181037
ZEND_ASSERT(ast->child[2]->kind == ZEND_AST_CALLABLE_CONVERT);
1038+
zend_ast_fcc *fcc_ast = (zend_ast_fcc*)ast->child[2];
10191039

1020-
zend_class_entry *ce = zend_ast_fetch_class(ast->child[0], scope);
1021-
if (!ce) {
1022-
return FAILURE;
1023-
}
1024-
zend_string *method_name = zend_ast_get_str(ast->child[1]);
1025-
if (ce->get_static_method) {
1026-
fptr = ce->get_static_method(ce, method_name);
1027-
} else {
1028-
fptr = zend_hash_find_ptr_lc(&ce->function_table, method_name);
1029-
if (fptr) {
1030-
if (!(fptr->common.fn_flags & ZEND_ACC_PUBLIC)) {
1031-
if (UNEXPECTED(fptr->common.scope != scope)) {
1032-
if (
1033-
UNEXPECTED(fptr->op_array.fn_flags & ZEND_ACC_PRIVATE)
1034-
|| UNEXPECTED(!zend_check_protected(zend_get_function_root_class(fptr), scope))
1035-
) {
1036-
if (ce->__callstatic) {
1037-
zend_throw_error(NULL, "Creating a callable for the magic __callStatic() method is not supported in constant expressions");
1038-
} else {
1039-
zend_bad_method_call(fptr, method_name, scope);
1040-
}
1040+
fptr = ZEND_MAP_PTR_GET(fcc_ast->fptr);
10411041

1042-
return FAILURE;
1042+
if (!fptr) {
1043+
zend_class_entry *ce = zend_ast_fetch_class(ast->child[0], scope);
1044+
if (!ce) {
1045+
return FAILURE;
1046+
}
1047+
zend_string *method_name = zend_ast_get_str(ast->child[1]);
1048+
if (ce->get_static_method) {
1049+
fptr = ce->get_static_method(ce, method_name);
1050+
} else {
1051+
fptr = zend_hash_find_ptr_lc(&ce->function_table, method_name);
1052+
if (fptr) {
1053+
if (!(fptr->common.fn_flags & ZEND_ACC_PUBLIC)) {
1054+
if (UNEXPECTED(fptr->common.scope != scope)) {
1055+
if (
1056+
UNEXPECTED(fptr->op_array.fn_flags & ZEND_ACC_PRIVATE)
1057+
|| UNEXPECTED(!zend_check_protected(zend_get_function_root_class(fptr), scope))
1058+
) {
1059+
if (ce->__callstatic) {
1060+
zend_throw_error(NULL, "Creating a callable for the magic __callStatic() method is not supported in constant expressions");
1061+
} else {
1062+
zend_bad_method_call(fptr, method_name, scope);
1063+
}
1064+
1065+
return FAILURE;
1066+
}
10431067
}
10441068
}
1045-
}
1046-
} else {
1047-
if (ce->__callstatic) {
1048-
zend_throw_error(NULL, "Creating a callable for the magic __callStatic() method is not supported in constant expressions");
10491069
} else {
1050-
zend_undefined_method(ce, method_name);
1070+
if (ce->__callstatic) {
1071+
zend_throw_error(NULL, "Creating a callable for the magic __callStatic() method is not supported in constant expressions");
1072+
} else {
1073+
zend_undefined_method(ce, method_name);
1074+
}
1075+
1076+
return FAILURE;
10511077
}
1078+
}
10521079

1080+
if (!(fptr->common.fn_flags & ZEND_ACC_STATIC)) {
1081+
zend_non_static_method_call(fptr);
1082+
10531083
return FAILURE;
10541084
}
1055-
}
1056-
1057-
if (!(fptr->common.fn_flags & ZEND_ACC_STATIC)) {
1058-
zend_non_static_method_call(fptr);
1059-
1060-
return FAILURE;
1061-
}
1062-
if ((fptr->common.fn_flags & ZEND_ACC_ABSTRACT)) {
1063-
zend_abstract_method_call(fptr);
1064-
1065-
return FAILURE;
1066-
} else if (fptr->common.scope->ce_flags & ZEND_ACC_TRAIT) {
1067-
zend_error(E_DEPRECATED,
1068-
"Calling static trait method %s::%s is deprecated, "
1069-
"it should only be called on a class using the trait",
1070-
ZSTR_VAL(fptr->common.scope->name), ZSTR_VAL(fptr->common.function_name));
1071-
if (EG(exception)) {
1085+
if ((fptr->common.fn_flags & ZEND_ACC_ABSTRACT)) {
1086+
zend_abstract_method_call(fptr);
1087+
10721088
return FAILURE;
1089+
} else if (fptr->common.scope->ce_flags & ZEND_ACC_TRAIT) {
1090+
zend_error(E_DEPRECATED,
1091+
"Calling static trait method %s::%s is deprecated, "
1092+
"it should only be called on a class using the trait",
1093+
ZSTR_VAL(fptr->common.scope->name), ZSTR_VAL(fptr->common.function_name));
1094+
if (EG(exception)) {
1095+
return FAILURE;
1096+
}
10731097
}
1098+
1099+
ZEND_MAP_PTR_SET(fcc_ast->fptr, fptr);
10741100
}
1101+
10751102
break;
10761103
}
10771104
}
@@ -1168,6 +1195,8 @@ static size_t ZEND_FASTCALL zend_ast_tree_size(zend_ast *ast)
11681195

11691196
if (ast->kind == ZEND_AST_ZVAL || ast->kind == ZEND_AST_CONSTANT || ast->kind == ZEND_AST_OP_ARRAY) {
11701197
size = sizeof(zend_ast_zval);
1198+
} else if (ast->kind == ZEND_AST_CALLABLE_CONVERT) {
1199+
size = sizeof(zend_ast_fcc);
11711200
} else if (zend_ast_is_list(ast)) {
11721201
uint32_t i;
11731202
zend_ast_list *list = zend_ast_get_list(ast);
@@ -1231,6 +1260,13 @@ static void* ZEND_FASTCALL zend_ast_tree_copy(zend_ast *ast, void *buf)
12311260
ZVAL_COPY(&new->val, &((zend_ast_zval *) ast)->val);
12321261
Z_LINENO(new->val) = zend_ast_get_lineno(ast);
12331262
buf = (void*)((char*)buf + sizeof(zend_ast_zval));
1263+
} else if (ast->kind == ZEND_AST_CALLABLE_CONVERT) {
1264+
zend_ast_fcc *new = (zend_ast_fcc*)buf;
1265+
new->kind = ZEND_AST_CALLABLE_CONVERT;
1266+
new->attr = ast->attr;
1267+
new->lineno = ast->lineno;
1268+
new->fptr__ptr = ((zend_ast_fcc *) ast)->fptr__ptr;
1269+
buf = (void*)((char*)buf + sizeof(zend_ast_fcc));
12341270
} else {
12351271
uint32_t i, children = zend_ast_get_num_children(ast);
12361272
zend_ast *new = (zend_ast*)buf;

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
@@ -219,6 +220,13 @@ typedef struct _zend_ast_decl {
219220
zend_ast *child[5];
220221
} zend_ast_decl;
221222

223+
typedef struct _zend_ast_fcc {
224+
zend_ast_kind kind; /* Type of the node (ZEND_AST_* enum constant) */
225+
zend_ast_attr attr; /* Additional attribute, use depending on node type */
226+
uint32_t lineno; /* Line number */
227+
ZEND_MAP_PTR_DEF(zend_function *, fptr);
228+
} zend_ast_fcc;
229+
222230
typedef void (*zend_ast_process_t)(zend_ast *ast);
223231
extern ZEND_API zend_ast_process_t zend_ast_process;
224232

@@ -308,6 +316,8 @@ ZEND_API zend_ast *zend_ast_create_decl(
308316
zend_string *name, zend_ast *child0, zend_ast *child1, zend_ast *child2, zend_ast *child3, zend_ast *child4
309317
);
310318

319+
ZEND_API zend_ast * ZEND_FASTCALL zend_ast_create_fcc(void);
320+
311321
typedef struct {
312322
bool had_side_effects;
313323
} zend_ast_evaluate_ctx;

Zend/zend_compile.c

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

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

1125411255
switch ((*ast_ptr)->kind) {
1125511256
case ZEND_AST_CALL: {

0 commit comments

Comments
 (0)