Skip to content

Commit 95237d5

Browse files
committed
Use the same zend_arg_info struct for internal and user functions
1 parent 0d4ff66 commit 95237d5

18 files changed

+239
-230
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: 104 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"
@@ -2952,6 +2953,80 @@ static zend_always_inline void zend_normalize_internal_type(zend_type *type) {
29522953
} ZEND_TYPE_FOREACH_END();
29532954
}
29542955

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

29673043
if (type==MODULE_PERSISTENT) {
29683044
error_type = E_CORE_WARNING;
@@ -3019,7 +3095,7 @@ ZEND_API zend_result zend_register_functions(zend_class_entry *scope, const zend
30193095

30203096
if (ptr->arg_info) {
30213097
zend_internal_function_info *info = (zend_internal_function_info*)ptr->arg_info;
3022-
internal_function->arg_info = (zend_internal_arg_info*)ptr->arg_info+1;
3098+
internal_arg_info = ptr->arg_info+1;
30233099
internal_function->num_args = ptr->num_args;
30243100
/* Currently you cannot denote that the function can accept less arguments than num_args */
30253101
if (info->required_num_args == (uintptr_t)-1) {
@@ -3049,7 +3125,7 @@ ZEND_API zend_result zend_register_functions(zend_class_entry *scope, const zend
30493125
zend_error(E_CORE_WARNING, "Missing arginfo for %s%s%s()",
30503126
scope ? ZSTR_VAL(scope->name) : "", scope ? "::" : "", ptr->fname);
30513127

3052-
internal_function->arg_info = NULL;
3128+
internal_arg_info = NULL;
30533129
internal_function->num_args = 0;
30543130
internal_function->required_num_args = 0;
30553131
}
@@ -3060,13 +3136,11 @@ ZEND_API zend_result zend_register_functions(zend_class_entry *scope, const zend
30603136
!(internal_function->fn_flags & ZEND_ACC_HAS_RETURN_TYPE)) {
30613137
zend_error(E_CORE_WARNING, "%s::__toString() implemented without string return type",
30623138
ZSTR_VAL(scope->name));
3063-
internal_function->arg_info = (zend_internal_arg_info *) arg_info_toString + 1;
3139+
internal_arg_info = (zend_internal_arg_info *) arg_info_toString + 1;
30643140
internal_function->fn_flags |= ZEND_ACC_HAS_RETURN_TYPE;
30653141
internal_function->num_args = internal_function->required_num_args = 0;
30663142
}
30673143

3068-
3069-
zend_set_function_arg_flags((zend_function*)internal_function);
30703144
if (ptr->flags & ZEND_ACC_ABSTRACT) {
30713145
if (scope) {
30723146
/* This is a class that must be abstract itself. Here we set the check info. */
@@ -3131,17 +3205,17 @@ ZEND_API zend_result zend_register_functions(zend_class_entry *scope, const zend
31313205
}
31323206

31333207
/* If types of arguments have to be checked */
3134-
if (reg_function->arg_info && num_args) {
3208+
if (internal_arg_info && num_args) {
31353209
uint32_t i;
31363210
for (i = 0; i < num_args; i++) {
3137-
zend_internal_arg_info *arg_info = &reg_function->arg_info[i];
3211+
const zend_internal_arg_info *arg_info = &internal_arg_info[i];
31383212
ZEND_ASSERT(arg_info->name && "Parameter must have a name");
31393213
if (ZEND_TYPE_IS_SET(arg_info->type)) {
31403214
reg_function->fn_flags |= ZEND_ACC_HAS_TYPE_HINTS;
31413215
}
31423216
#if ZEND_DEBUG
31433217
for (uint32_t j = 0; j < i; j++) {
3144-
if (!strcmp(arg_info->name, reg_function->arg_info[j].name)) {
3218+
if (!strcmp(arg_info->name, internal_arg_info[j].name)) {
31453219
zend_error_noreturn(E_CORE_ERROR,
31463220
"Duplicate parameter name $%s for function %s%s%s()", arg_info->name,
31473221
scope ? ZSTR_VAL(scope->name) : "", scope ? "::" : "", ptr->fname);
@@ -3151,78 +3225,23 @@ ZEND_API zend_result zend_register_functions(zend_class_entry *scope, const zend
31513225
}
31523226
}
31533227

3154-
/* Rebuild arginfos if parameter/property types and/or a return type are used */
3155-
if (reg_function->arg_info &&
3156-
(reg_function->fn_flags & (ZEND_ACC_HAS_RETURN_TYPE|ZEND_ACC_HAS_TYPE_HINTS))) {
3157-
/* convert "const char*" class type names into "zend_string*" */
3228+
/* Convert zend_internal_arg_info to zend_arg_info */
3229+
if (internal_arg_info) {
31583230
uint32_t i;
3159-
zend_internal_arg_info *arg_info = reg_function->arg_info - 1;
3160-
zend_internal_arg_info *new_arg_info;
3231+
const zend_internal_arg_info *arg_info = internal_arg_info - 1;
3232+
zend_arg_info *new_arg_info;
31613233

31623234
/* Treat return type as an extra argument */
31633235
num_args++;
3164-
new_arg_info = malloc(sizeof(zend_internal_arg_info) * num_args);
3165-
memcpy(new_arg_info, arg_info, sizeof(zend_internal_arg_info) * num_args);
3236+
new_arg_info = malloc(sizeof(zend_arg_info) * num_args);
31663237
reg_function->arg_info = new_arg_info + 1;
31673238
for (i = 0; i < num_args; i++) {
3168-
if (ZEND_TYPE_HAS_LITERAL_NAME(new_arg_info[i].type)) {
3169-
// gen_stubs.php does not support codegen for DNF types in arg infos.
3170-
// As a temporary workaround, we split the type name on `|` characters,
3171-
// converting it to an union type if necessary.
3172-
const char *class_name = ZEND_TYPE_LITERAL_NAME(new_arg_info[i].type);
3173-
new_arg_info[i].type.type_mask &= ~_ZEND_TYPE_LITERAL_NAME_BIT;
3174-
3175-
size_t num_types = 1;
3176-
const char *p = class_name;
3177-
while ((p = strchr(p, '|'))) {
3178-
num_types++;
3179-
p++;
3180-
}
3181-
3182-
if (num_types == 1) {
3183-
/* Simple class type */
3184-
zend_string *str = zend_string_init_interned(class_name, strlen(class_name), 1);
3185-
zend_alloc_ce_cache(str);
3186-
ZEND_TYPE_SET_PTR(new_arg_info[i].type, str);
3187-
new_arg_info[i].type.type_mask |= _ZEND_TYPE_NAME_BIT;
3188-
} else {
3189-
/* Union type */
3190-
zend_type_list *list = malloc(ZEND_TYPE_LIST_SIZE(num_types));
3191-
list->num_types = num_types;
3192-
ZEND_TYPE_SET_LIST(new_arg_info[i].type, list);
3193-
ZEND_TYPE_FULL_MASK(new_arg_info[i].type) |= _ZEND_TYPE_UNION_BIT;
3194-
3195-
const char *start = class_name;
3196-
uint32_t j = 0;
3197-
while (true) {
3198-
const char *end = strchr(start, '|');
3199-
zend_string *str = zend_string_init_interned(start, end ? end - start : strlen(start), 1);
3200-
zend_alloc_ce_cache(str);
3201-
list->types[j] = (zend_type) ZEND_TYPE_INIT_CLASS(str, 0, 0);
3202-
if (!end) {
3203-
break;
3204-
}
3205-
start = end + 1;
3206-
j++;
3207-
}
3208-
}
3209-
}
3210-
if (ZEND_TYPE_IS_ITERABLE_FALLBACK(new_arg_info[i].type)) {
3211-
/* Warning generated an extension load warning which is emitted for every test
3212-
zend_error(E_CORE_WARNING, "iterable type is now a compile time alias for array|Traversable,"
3213-
" regenerate the argument info via the php-src gen_stub build script");
3214-
*/
3215-
zend_type legacy_iterable = ZEND_TYPE_INIT_CLASS_MASK(
3216-
ZSTR_KNOWN(ZEND_STR_TRAVERSABLE),
3217-
(new_arg_info[i].type.type_mask | MAY_BE_ARRAY)
3218-
);
3219-
new_arg_info[i].type = legacy_iterable;
3220-
}
3221-
3222-
zend_normalize_internal_type(&new_arg_info[i].type);
3239+
zend_convert_internal_arg_info(&new_arg_info[i], &arg_info[i], i == 0);
32233240
}
32243241
}
32253242

3243+
zend_set_function_arg_flags((zend_function*)reg_function);
3244+
32263245
if (scope) {
32273246
zend_check_magic_method_implementation(
32283247
scope, (zend_function *)reg_function, lowercase_name, E_CORE_ERROR);
@@ -5313,49 +5332,44 @@ static zend_string *try_parse_string(const char *str, size_t len, char quote) {
53135332
return zend_string_init(str, len, 0);
53145333
}
53155334

5316-
ZEND_API zend_result zend_get_default_from_internal_arg_info(
5317-
zval *default_value_zval, zend_internal_arg_info *arg_info)
5335+
ZEND_API zend_result zend_get_default_from_arg_info(
5336+
zval *default_value_zval, const zend_arg_info *arg_info)
53185337
{
5319-
const char *default_value = arg_info->default_value;
5338+
zend_string *default_value = arg_info->default_value;
53205339
if (!default_value) {
53215340
return FAILURE;
53225341
}
53235342

53245343
/* Avoid going through the full AST machinery for some simple and common cases. */
5325-
size_t default_value_len = strlen(default_value);
53265344
zend_ulong lval;
5327-
if (default_value_len == sizeof("null")-1
5328-
&& !memcmp(default_value, "null", sizeof("null")-1)) {
5345+
if (zend_string_equals_literal(default_value, "null")) {
53295346
ZVAL_NULL(default_value_zval);
53305347
return SUCCESS;
5331-
} else if (default_value_len == sizeof("true")-1
5332-
&& !memcmp(default_value, "true", sizeof("true")-1)) {
5348+
} else if (zend_string_equals_literal(default_value, "true")) {
53335349
ZVAL_TRUE(default_value_zval);
53345350
return SUCCESS;
5335-
} else if (default_value_len == sizeof("false")-1
5336-
&& !memcmp(default_value, "false", sizeof("false")-1)) {
5351+
} else if (zend_string_equals_literal(default_value, "false")) {
53375352
ZVAL_FALSE(default_value_zval);
53385353
return SUCCESS;
5339-
} else if (default_value_len >= 2
5340-
&& (default_value[0] == '\'' || default_value[0] == '"')
5341-
&& default_value[default_value_len - 1] == default_value[0]) {
5354+
} else if (ZSTR_LEN(default_value) >= 2
5355+
&& (ZSTR_VAL(default_value)[0] == '\'' || ZSTR_VAL(default_value)[0] == '"')
5356+
&& ZSTR_VAL(default_value)[ZSTR_LEN(default_value) - 1] == ZSTR_VAL(default_value)[0]) {
53425357
zend_string *str = try_parse_string(
5343-
default_value + 1, default_value_len - 2, default_value[0]);
5358+
ZSTR_VAL(default_value) + 1, ZSTR_LEN(default_value) - 2, ZSTR_VAL(default_value)[0]);
53445359
if (str) {
53455360
ZVAL_STR(default_value_zval, str);
53465361
return SUCCESS;
53475362
}
5348-
} else if (default_value_len == sizeof("[]")-1
5349-
&& !memcmp(default_value, "[]", sizeof("[]")-1)) {
5363+
} else if (zend_string_equals_literal(default_value, "[]")) {
53505364
ZVAL_EMPTY_ARRAY(default_value_zval);
53515365
return SUCCESS;
5352-
} else if (ZEND_HANDLE_NUMERIC_STR(default_value, default_value_len, lval)) {
5366+
} else if (ZEND_HANDLE_NUMERIC(default_value, lval)) {
53535367
ZVAL_LONG(default_value_zval, lval);
53545368
return SUCCESS;
53555369
}
53565370

53575371
#if 0
5358-
fprintf(stderr, "Evaluating %s via AST\n", default_value);
5372+
fprintf(stderr, "Evaluating %s via AST\n", ZSTR_VAL(default_value));
53595373
#endif
5360-
return get_default_via_ast(default_value_zval, default_value);
5374+
return get_default_via_ast(default_value_zval, ZSTR_VAL(default_value));
53615375
}

Zend/zend_API.h

Lines changed: 5 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -930,8 +930,11 @@ ZEND_API bool zend_is_iterable(const zval *iterable);
930930

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

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

936939
END_EXTERN_C()
937940

Zend/zend_closures.c

Lines changed: 12 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -605,7 +605,6 @@ static HashTable *zend_closure_get_debug_info(zend_object *object, int *is_temp)
605605
zval val;
606606
struct _zend_arg_info *arg_info = closure->func.common.arg_info;
607607
HashTable *debug_info;
608-
bool zstr_args = (closure->func.type == ZEND_USER_FUNCTION) || (closure->func.common.fn_flags & ZEND_ACC_USER_ARG_INFO);
609608

610609
*is_temp = 1;
611610

@@ -681,15 +680,9 @@ static HashTable *zend_closure_get_debug_info(zend_object *object, int *is_temp)
681680
zend_string *name;
682681
zval info;
683682
ZEND_ASSERT(arg_info->name && "Argument should have name");
684-
if (zstr_args) {
685-
name = zend_strpprintf(0, "%s$%s",
686-
ZEND_ARG_SEND_MODE(arg_info) ? "&" : "",
687-
ZSTR_VAL(arg_info->name));
688-
} else {
689-
name = zend_strpprintf(0, "%s$%s",
690-
ZEND_ARG_SEND_MODE(arg_info) ? "&" : "",
691-
((zend_internal_arg_info*)arg_info)->name);
692-
}
683+
name = zend_strpprintf(0, "%s$%s",
684+
ZEND_ARG_SEND_MODE(arg_info) ? "&" : "",
685+
ZSTR_VAL(arg_info->name));
693686
ZVAL_NEW_STR(&info, zend_strpprintf(0, "%s", i >= required ? "<optional>" : "<required>"));
694687
zend_hash_update(Z_ARRVAL(val), name, &info);
695688
zend_string_release_ex(name, 0);
@@ -872,8 +865,7 @@ ZEND_API void zend_create_fake_closure(zval *res, zend_function *func, zend_clas
872865
}
873866
/* }}} */
874867

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

878870
void zend_closure_from_frame(zval *return_value, zend_execute_data *call) { /* {{{ */
879871
zval instance;
@@ -938,3 +930,11 @@ void zend_closure_bind_var_ex(zval *closure_zv, uint32_t offset, zval *val) /* {
938930
ZVAL_COPY_VALUE(var, val);
939931
}
940932
/* }}} */
933+
934+
void zend_closure_startup(void)
935+
{
936+
/* __call and __callStatic name the arguments "$arguments" in the docs. */
937+
trampoline_arg_info[0].name = zend_string_init_interned("arguments", strlen("arguments"), true);
938+
trampoline_arg_info[0].type = (zend_type)ZEND_TYPE_INIT_CODE(IS_MIXED, false, _ZEND_ARG_INFO_FLAGS(false, 1, 0));
939+
trampoline_arg_info[0].default_value = NULL;
940+
}

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)