Skip to content

Commit 0731ca6

Browse files
committed
Use the same zend_arg_info struct for internal and user functions
1 parent 7f91462 commit 0731ca6

File tree

17 files changed

+208
-211
lines changed

17 files changed

+208
-211
lines changed

Zend/zend.c

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -38,6 +38,8 @@
3838
#include "zend_call_stack.h"
3939
#include "zend_max_execution_timer.h"
4040
#include "zend_hrtime.h"
41+
#include "zend_enum.h"
42+
#include "zend_closures.h"
4143
#include "Optimizer/zend_optimizer.h"
4244
#include "php.h"
4345
#include "php_globals.h"
@@ -1077,6 +1079,9 @@ void zend_startup(zend_utility_functions *utility_functions) /* {{{ */
10771079
tsrm_set_new_thread_end_handler(zend_new_thread_end_handler);
10781080
tsrm_set_shutdown_handler(zend_interned_strings_dtor);
10791081
#endif
1082+
1083+
zend_enum_startup();
1084+
zend_closure_startup();
10801085
}
10811086
/* }}} */
10821087

Zend/zend_API.c

Lines changed: 105 additions & 90 deletions
Original file line numberDiff line numberDiff line change
@@ -20,6 +20,7 @@
2020
*/
2121

2222
#include "zend.h"
23+
#include "zend_compile.h"
2324
#include "zend_execute.h"
2425
#include "zend_API.h"
2526
#include "zend_hash.h"
@@ -2933,6 +2934,80 @@ static zend_always_inline void zend_normalize_internal_type(zend_type *type) {
29332934
} ZEND_TYPE_FOREACH_END();
29342935
}
29352936

2937+
void zend_convert_internal_arg_info_type(zend_type *type)
2938+
{
2939+
if (ZEND_TYPE_HAS_LITERAL_NAME(*type)) {
2940+
// gen_stubs.php does not support codegen for DNF types in arg infos.
2941+
// As a temporary workaround, we split the type name on `|` characters,
2942+
// converting it to an union type if necessary.
2943+
const char *class_name = ZEND_TYPE_LITERAL_NAME(*type);
2944+
type->type_mask &= ~_ZEND_TYPE_LITERAL_NAME_BIT;
2945+
2946+
size_t num_types = 1;
2947+
const char *p = class_name;
2948+
while ((p = strchr(p, '|'))) {
2949+
num_types++;
2950+
p++;
2951+
}
2952+
2953+
if (num_types == 1) {
2954+
/* Simple class type */
2955+
zend_string *str = zend_string_init_interned(class_name, strlen(class_name), 1);
2956+
zend_alloc_ce_cache(str);
2957+
ZEND_TYPE_SET_PTR(*type, str);
2958+
type->type_mask |= _ZEND_TYPE_NAME_BIT;
2959+
} else {
2960+
/* Union type */
2961+
zend_type_list *list = malloc(ZEND_TYPE_LIST_SIZE(num_types));
2962+
list->num_types = num_types;
2963+
ZEND_TYPE_SET_LIST(*type, list);
2964+
ZEND_TYPE_FULL_MASK(*type) |= _ZEND_TYPE_UNION_BIT;
2965+
2966+
const char *start = class_name;
2967+
uint32_t j = 0;
2968+
while (true) {
2969+
const char *end = strchr(start, '|');
2970+
zend_string *str = zend_string_init_interned(start, end ? end - start : strlen(start), 1);
2971+
zend_alloc_ce_cache(str);
2972+
list->types[j] = (zend_type) ZEND_TYPE_INIT_CLASS(str, 0, 0);
2973+
if (!end) {
2974+
break;
2975+
}
2976+
start = end + 1;
2977+
j++;
2978+
}
2979+
}
2980+
}
2981+
if (ZEND_TYPE_IS_ITERABLE_FALLBACK(*type)) {
2982+
/* Warning generated an extension load warning which is emitted for every test
2983+
zend_error(E_CORE_WARNING, "iterable type is now a compile time alias for array|Traversable,"
2984+
" regenerate the argument info via the php-src gen_stub build script");
2985+
*/
2986+
zend_type legacy_iterable = ZEND_TYPE_INIT_CLASS_MASK(
2987+
ZSTR_KNOWN(ZEND_STR_TRAVERSABLE),
2988+
(type->type_mask | MAY_BE_ARRAY)
2989+
);
2990+
*type = legacy_iterable;
2991+
}
2992+
}
2993+
2994+
void zend_convert_internal_arg_info(zend_arg_info *new_arg_info, const zend_internal_arg_info *arg_info, bool is_return_info)
2995+
{
2996+
if (!is_return_info) {
2997+
new_arg_info->name = zend_string_init_interned(arg_info->name, strlen(arg_info->name), 1);
2998+
if (arg_info->default_value) {
2999+
new_arg_info->default_value = zend_string_init_interned(arg_info->default_value, strlen(arg_info->default_value), 1);
3000+
} else {
3001+
new_arg_info->default_value = NULL;
3002+
}
3003+
} else {
3004+
new_arg_info->name = NULL;
3005+
new_arg_info->default_value = NULL;
3006+
}
3007+
new_arg_info->type = arg_info->type;
3008+
zend_convert_internal_arg_info_type(&new_arg_info->type);
3009+
}
3010+
29363011
/* registers all functions in *library_functions in the function hash */
29373012
ZEND_API zend_result zend_register_functions(zend_class_entry *scope, const zend_function_entry *functions, HashTable *function_table, int type) /* {{{ */
29383013
{
@@ -2944,6 +3019,7 @@ ZEND_API zend_result zend_register_functions(zend_class_entry *scope, const zend
29443019
int error_type;
29453020
zend_string *lowercase_name;
29463021
size_t fname_len;
3022+
const zend_internal_arg_info *internal_arg_info;
29473023

29483024
if (type==MODULE_PERSISTENT) {
29493025
error_type = E_CORE_WARNING;
@@ -3000,7 +3076,7 @@ ZEND_API zend_result zend_register_functions(zend_class_entry *scope, const zend
30003076

30013077
if (ptr->arg_info) {
30023078
zend_internal_function_info *info = (zend_internal_function_info*)ptr->arg_info;
3003-
internal_function->arg_info = (zend_internal_arg_info*)ptr->arg_info+1;
3079+
internal_arg_info = ptr->arg_info+1;
30043080
internal_function->num_args = ptr->num_args;
30053081
/* Currently you cannot denote that the function can accept less arguments than num_args */
30063082
if (info->required_num_args == (uintptr_t)-1) {
@@ -3030,7 +3106,7 @@ ZEND_API zend_result zend_register_functions(zend_class_entry *scope, const zend
30303106
zend_error(E_CORE_WARNING, "Missing arginfo for %s%s%s()",
30313107
scope ? ZSTR_VAL(scope->name) : "", scope ? "::" : "", ptr->fname);
30323108

3033-
internal_function->arg_info = NULL;
3109+
internal_arg_info = NULL;
30343110
internal_function->num_args = 0;
30353111
internal_function->required_num_args = 0;
30363112
}
@@ -3041,13 +3117,11 @@ ZEND_API zend_result zend_register_functions(zend_class_entry *scope, const zend
30413117
!(internal_function->fn_flags & ZEND_ACC_HAS_RETURN_TYPE)) {
30423118
zend_error(E_CORE_WARNING, "%s::__toString() implemented without string return type",
30433119
ZSTR_VAL(scope->name));
3044-
internal_function->arg_info = (zend_internal_arg_info *) arg_info_toString + 1;
3120+
internal_arg_info = (zend_internal_arg_info*) arg_info_toString + 1;
30453121
internal_function->fn_flags |= ZEND_ACC_HAS_RETURN_TYPE;
30463122
internal_function->num_args = internal_function->required_num_args = 0;
30473123
}
30483124

3049-
3050-
zend_set_function_arg_flags((zend_function*)internal_function);
30513125
if (ptr->flags & ZEND_ACC_ABSTRACT) {
30523126
if (scope) {
30533127
/* This is a class that must be abstract itself. Here we set the check info. */
@@ -3112,17 +3186,17 @@ ZEND_API zend_result zend_register_functions(zend_class_entry *scope, const zend
31123186
}
31133187

31143188
/* If types of arguments have to be checked */
3115-
if (reg_function->arg_info && num_args) {
3189+
if (internal_arg_info && num_args) {
31163190
uint32_t i;
31173191
for (i = 0; i < num_args; i++) {
3118-
zend_internal_arg_info *arg_info = &reg_function->arg_info[i];
3192+
const zend_internal_arg_info *arg_info = &internal_arg_info[i];
31193193
ZEND_ASSERT(arg_info->name && "Parameter must have a name");
31203194
if (ZEND_TYPE_IS_SET(arg_info->type)) {
31213195
reg_function->fn_flags |= ZEND_ACC_HAS_TYPE_HINTS;
31223196
}
31233197
#if ZEND_DEBUG
31243198
for (uint32_t j = 0; j < i; j++) {
3125-
if (!strcmp(arg_info->name, reg_function->arg_info[j].name)) {
3199+
if (!strcmp(arg_info->name, internal_arg_info[j].name)) {
31263200
zend_error_noreturn(E_CORE_ERROR,
31273201
"Duplicate parameter name $%s for function %s%s%s()", arg_info->name,
31283202
scope ? ZSTR_VAL(scope->name) : "", scope ? "::" : "", ptr->fname);
@@ -3132,78 +3206,24 @@ ZEND_API zend_result zend_register_functions(zend_class_entry *scope, const zend
31323206
}
31333207
}
31343208

3135-
/* Rebuild arginfos if parameter/property types and/or a return type are used */
3136-
if (reg_function->arg_info &&
3137-
(reg_function->fn_flags & (ZEND_ACC_HAS_RETURN_TYPE|ZEND_ACC_HAS_TYPE_HINTS))) {
3138-
/* convert "const char*" class type names into "zend_string*" */
3209+
/* Convert zend_internal_arg_info to zend_arg_info */
3210+
if (internal_arg_info) {
31393211
uint32_t i;
3140-
zend_internal_arg_info *arg_info = reg_function->arg_info - 1;
3141-
zend_internal_arg_info *new_arg_info;
3212+
const zend_internal_arg_info *arg_info = internal_arg_info - 1;
3213+
zend_arg_info *new_arg_info;
31423214

31433215
/* Treat return type as an extra argument */
31443216
num_args++;
3145-
new_arg_info = malloc(sizeof(zend_internal_arg_info) * num_args);
3146-
memcpy(new_arg_info, arg_info, sizeof(zend_internal_arg_info) * num_args);
3217+
new_arg_info = malloc(sizeof(zend_arg_info) * num_args);
3218+
memcpy(new_arg_info, arg_info, sizeof(zend_arg_info) * num_args);
31473219
reg_function->arg_info = new_arg_info + 1;
31483220
for (i = 0; i < num_args; i++) {
3149-
if (ZEND_TYPE_HAS_LITERAL_NAME(new_arg_info[i].type)) {
3150-
// gen_stubs.php does not support codegen for DNF types in arg infos.
3151-
// As a temporary workaround, we split the type name on `|` characters,
3152-
// converting it to an union type if necessary.
3153-
const char *class_name = ZEND_TYPE_LITERAL_NAME(new_arg_info[i].type);
3154-
new_arg_info[i].type.type_mask &= ~_ZEND_TYPE_LITERAL_NAME_BIT;
3155-
3156-
size_t num_types = 1;
3157-
const char *p = class_name;
3158-
while ((p = strchr(p, '|'))) {
3159-
num_types++;
3160-
p++;
3161-
}
3162-
3163-
if (num_types == 1) {
3164-
/* Simple class type */
3165-
zend_string *str = zend_string_init_interned(class_name, strlen(class_name), 1);
3166-
zend_alloc_ce_cache(str);
3167-
ZEND_TYPE_SET_PTR(new_arg_info[i].type, str);
3168-
new_arg_info[i].type.type_mask |= _ZEND_TYPE_NAME_BIT;
3169-
} else {
3170-
/* Union type */
3171-
zend_type_list *list = malloc(ZEND_TYPE_LIST_SIZE(num_types));
3172-
list->num_types = num_types;
3173-
ZEND_TYPE_SET_LIST(new_arg_info[i].type, list);
3174-
ZEND_TYPE_FULL_MASK(new_arg_info[i].type) |= _ZEND_TYPE_UNION_BIT;
3175-
3176-
const char *start = class_name;
3177-
uint32_t j = 0;
3178-
while (true) {
3179-
const char *end = strchr(start, '|');
3180-
zend_string *str = zend_string_init_interned(start, end ? end - start : strlen(start), 1);
3181-
zend_alloc_ce_cache(str);
3182-
list->types[j] = (zend_type) ZEND_TYPE_INIT_CLASS(str, 0, 0);
3183-
if (!end) {
3184-
break;
3185-
}
3186-
start = end + 1;
3187-
j++;
3188-
}
3189-
}
3190-
}
3191-
if (ZEND_TYPE_IS_ITERABLE_FALLBACK(new_arg_info[i].type)) {
3192-
/* Warning generated an extension load warning which is emitted for every test
3193-
zend_error(E_CORE_WARNING, "iterable type is now a compile time alias for array|Traversable,"
3194-
" regenerate the argument info via the php-src gen_stub build script");
3195-
*/
3196-
zend_type legacy_iterable = ZEND_TYPE_INIT_CLASS_MASK(
3197-
ZSTR_KNOWN(ZEND_STR_TRAVERSABLE),
3198-
(new_arg_info[i].type.type_mask | MAY_BE_ARRAY)
3199-
);
3200-
new_arg_info[i].type = legacy_iterable;
3201-
}
3202-
3203-
zend_normalize_internal_type(&new_arg_info[i].type);
3221+
zend_convert_internal_arg_info(&new_arg_info[i], &arg_info[i], i == 0);
32043222
}
32053223
}
32063224

3225+
zend_set_function_arg_flags((zend_function*)reg_function);
3226+
32073227
if (scope) {
32083228
zend_check_magic_method_implementation(
32093229
scope, (zend_function *)reg_function, lowercase_name, E_CORE_ERROR);
@@ -5371,49 +5391,44 @@ static zend_string *try_parse_string(const char *str, size_t len, char quote) {
53715391
return zend_string_init(str, len, 0);
53725392
}
53735393

5374-
ZEND_API zend_result zend_get_default_from_internal_arg_info(
5375-
zval *default_value_zval, zend_internal_arg_info *arg_info)
5394+
ZEND_API zend_result zend_get_default_from_arg_info(
5395+
zval *default_value_zval, zend_arg_info *arg_info)
53765396
{
5377-
const char *default_value = arg_info->default_value;
5397+
zend_string *default_value = arg_info->default_value;
53785398
if (!default_value) {
53795399
return FAILURE;
53805400
}
53815401

53825402
/* Avoid going through the full AST machinery for some simple and common cases. */
5383-
size_t default_value_len = strlen(default_value);
53845403
zend_ulong lval;
5385-
if (default_value_len == sizeof("null")-1
5386-
&& !memcmp(default_value, "null", sizeof("null")-1)) {
5404+
if (zend_string_equals_cstr(default_value, "null", strlen("null"))) {
53875405
ZVAL_NULL(default_value_zval);
53885406
return SUCCESS;
5389-
} else if (default_value_len == sizeof("true")-1
5390-
&& !memcmp(default_value, "true", sizeof("true")-1)) {
5407+
} else if (zend_string_equals_cstr(default_value, "true", strlen("true"))) {
53915408
ZVAL_TRUE(default_value_zval);
53925409
return SUCCESS;
5393-
} else if (default_value_len == sizeof("false")-1
5394-
&& !memcmp(default_value, "false", sizeof("false")-1)) {
5410+
} else if (zend_string_equals_cstr(default_value, "false", strlen("false"))) {
53955411
ZVAL_FALSE(default_value_zval);
53965412
return SUCCESS;
5397-
} else if (default_value_len >= 2
5398-
&& (default_value[0] == '\'' || default_value[0] == '"')
5399-
&& default_value[default_value_len - 1] == default_value[0]) {
5413+
} else if (ZSTR_LEN(default_value) >= 2
5414+
&& (ZSTR_VAL(default_value)[0] == '\'' || ZSTR_VAL(default_value)[0] == '"')
5415+
&& ZSTR_VAL(default_value)[ZSTR_LEN(default_value) - 1] == ZSTR_VAL(default_value)[0]) {
54005416
zend_string *str = try_parse_string(
5401-
default_value + 1, default_value_len - 2, default_value[0]);
5417+
ZSTR_VAL(default_value) + 1, ZSTR_LEN(default_value) - 2, ZSTR_VAL(default_value)[0]);
54025418
if (str) {
54035419
ZVAL_STR(default_value_zval, str);
54045420
return SUCCESS;
54055421
}
5406-
} else if (default_value_len == sizeof("[]")-1
5407-
&& !memcmp(default_value, "[]", sizeof("[]")-1)) {
5422+
} else if (zend_string_equals_cstr(default_value, "[]", strlen("[]"))) {
54085423
ZVAL_EMPTY_ARRAY(default_value_zval);
54095424
return SUCCESS;
5410-
} else if (ZEND_HANDLE_NUMERIC_STR(default_value, default_value_len, lval)) {
5425+
} else if (ZEND_HANDLE_NUMERIC(default_value, lval)) {
54115426
ZVAL_LONG(default_value_zval, lval);
54125427
return SUCCESS;
54135428
}
54145429

54155430
#if 0
5416-
fprintf(stderr, "Evaluating %s via AST\n", default_value);
5431+
fprintf(stderr, "Evaluating %s via AST\n", ZSTR_VAL(default_value));
54175432
#endif
5418-
return get_default_via_ast(default_value_zval, default_value);
5433+
return get_default_via_ast(default_value_zval, ZSTR_VAL(default_value));
54195434
}

Zend/zend_API.h

Lines changed: 4 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -929,8 +929,10 @@ ZEND_API bool zend_is_iterable(const zval *iterable);
929929

930930
ZEND_API bool zend_is_countable(const zval *countable);
931931

932-
ZEND_API zend_result zend_get_default_from_internal_arg_info(
933-
zval *default_value_zval, zend_internal_arg_info *arg_info);
932+
void zend_convert_internal_arg_info(zend_arg_info *new_arg_info, const zend_internal_arg_info *arg_info, bool is_return_info);
933+
934+
ZEND_API zend_result zend_get_default_from_arg_info(
935+
zval *default_value_zval, zend_arg_info *arg_info);
934936

935937
END_EXTERN_C()
936938

Zend/zend_closures.c

Lines changed: 13 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -588,7 +588,6 @@ static HashTable *zend_closure_get_debug_info(zend_object *object, int *is_temp)
588588
zval val;
589589
struct _zend_arg_info *arg_info = closure->func.common.arg_info;
590590
HashTable *debug_info;
591-
bool zstr_args = (closure->func.type == ZEND_USER_FUNCTION) || (closure->func.common.fn_flags & ZEND_ACC_USER_ARG_INFO);
592591

593592
*is_temp = 1;
594593

@@ -664,15 +663,9 @@ static HashTable *zend_closure_get_debug_info(zend_object *object, int *is_temp)
664663
zend_string *name;
665664
zval info;
666665
ZEND_ASSERT(arg_info->name && "Argument should have name");
667-
if (zstr_args) {
668-
name = zend_strpprintf(0, "%s$%s",
669-
ZEND_ARG_SEND_MODE(arg_info) ? "&" : "",
670-
ZSTR_VAL(arg_info->name));
671-
} else {
672-
name = zend_strpprintf(0, "%s$%s",
673-
ZEND_ARG_SEND_MODE(arg_info) ? "&" : "",
674-
((zend_internal_arg_info*)arg_info)->name);
675-
}
666+
name = zend_strpprintf(0, "%s$%s",
667+
ZEND_ARG_SEND_MODE(arg_info) ? "&" : "",
668+
ZSTR_VAL(arg_info->name));
676669
ZVAL_NEW_STR(&info, zend_strpprintf(0, "%s", i >= required ? "<optional>" : "<required>"));
677670
zend_hash_update(Z_ARRVAL(val), name, &info);
678671
zend_string_release_ex(name, 0);
@@ -852,8 +845,7 @@ ZEND_API void zend_create_fake_closure(zval *res, zend_function *func, zend_clas
852845
}
853846
/* }}} */
854847

855-
/* __call and __callStatic name the arguments "$arguments" in the docs. */
856-
static zend_internal_arg_info trampoline_arg_info[] = {ZEND_ARG_VARIADIC_TYPE_INFO(false, arguments, IS_MIXED, false)};
848+
static zend_arg_info trampoline_arg_info[1];
857849

858850
void zend_closure_from_frame(zval *return_value, zend_execute_data *call) { /* {{{ */
859851
zval instance;
@@ -917,4 +909,13 @@ void zend_closure_bind_var_ex(zval *closure_zv, uint32_t offset, zval *val) /* {
917909
zval_ptr_dtor(var);
918910
ZVAL_COPY_VALUE(var, val);
919911
}
912+
920913
/* }}} */
914+
915+
void zend_closure_startup(void)
916+
{
917+
/* __call and __callStatic name the arguments "$arguments" in the docs. */
918+
trampoline_arg_info[0].name = zend_string_init_interned("arguments", strlen("arguments"), 1);
919+
trampoline_arg_info[0].type = (zend_type)ZEND_TYPE_INIT_CODE(IS_MIXED, false, _ZEND_ARG_INFO_FLAGS(false, 1, 0));
920+
trampoline_arg_info[0].default_value = NULL;
921+
}

Zend/zend_closures.h

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -28,6 +28,7 @@ BEGIN_EXTERN_C()
2828
#define ZEND_CLOSURE_OBJECT(op_array) \
2929
((zend_object*)((char*)(op_array) - sizeof(zend_object)))
3030

31+
void zend_closure_startup(void);
3132
void zend_register_closure_ce(void);
3233
void zend_closure_bind_var(zval *closure_zv, zend_string *var_name, zval *var);
3334
void zend_closure_bind_var_ex(zval *closure_zv, uint32_t offset, zval *val);

0 commit comments

Comments
 (0)