1
0
mirror of https://github.com/php/php-src.git synced 2026-03-24 00:02:20 +01:00

Unify arg info representation for internal and user functions

The arg_info member of zend_function is now always a zend_arg_info*. Before,
it was a zend_internal_arg_info* on internal functions, unless the
ZEND_ACC_USER_ARG_INFO flag was set.

Closes GH-19022
This commit is contained in:
Arnaud Le Blanc
2025-06-18 18:01:57 +02:00
parent 37d677d594
commit 626f3c3c7c
26 changed files with 410 additions and 239 deletions

View File

@@ -49,6 +49,9 @@ PHP 8.6 INTERNALS UPGRADE NOTES
removed. Call zend_wrong_param_count(); followed by RETURN_THROWS();
instead.
. PHP_HAVE_STREAMS macro removed from <php.h>.
. zend_function.arg_info is now always a zend_arg_info*. Before, it was a
zend_internal_arg_info on internal functions, unless the
ZEND_ACC_USER_ARG_INFO flag was set.
========================
2. Build system changes

View File

@@ -0,0 +1,21 @@
--TEST--
GH-19653 (Closure named argument unpacking between temporary closures can cause a crash) - temporary method variation
--EXTENSIONS--
zend_test
--FILE--
<?php
function usage1($f) {
$f(tmpMethodParamName: null);
}
usage1([new _ZendTestClass(), 'testTmpMethodWithArgInfo']);
usage1(eval('return function (string $a, string $b): string { return $a.$b; };'));
?>
--EXPECTF--
Fatal error: Uncaught Error: Unknown named parameter $tmpMethodParamName in %s:%d
Stack trace:
#0 %s(%d): usage1(Object(Closure))
#1 {main}
thrown in %s on line %d

View File

@@ -0,0 +1,21 @@
--TEST--
Declaring non persistent method with arg info
--EXTENSIONS--
zend_test
--FILE--
<?php
$o = new _ZendTestClass();
$o->testTmpMethodWithArgInfo(null);
echo new ReflectionFunction($o->testTmpMethodWithArgInfo(...));
?>
--EXPECT--
Closure [ <internal> public method testTmpMethodWithArgInfo ] {
- Parameters [2] {
Parameter #0 [ <optional> Foo|Bar|null $tmpMethodParamName = null ]
Parameter #1 [ <optional> string $tmpMethodParamWithStringDefaultValue = "tmpMethodParamWithStringDefaultValue" ]
}
}

View File

@@ -38,6 +38,8 @@
#include "zend_call_stack.h"
#include "zend_max_execution_timer.h"
#include "zend_hrtime.h"
#include "zend_enum.h"
#include "zend_closures.h"
#include "Optimizer/zend_optimizer.h"
#include "php.h"
#include "php_globals.h"
@@ -1054,6 +1056,7 @@ void zend_startup(zend_utility_functions *utility_functions) /* {{{ */
ZVAL_UNDEF(&EG(last_fatal_error_backtrace));
zend_interned_strings_init();
zend_object_handlers_startup();
zend_startup_builtin_functions();
zend_register_standard_constants();
zend_register_auto_global(zend_string_init_interned("GLOBALS", sizeof("GLOBALS") - 1, 1), 1, php_auto_globals_create_globals);
@@ -1077,6 +1080,9 @@ void zend_startup(zend_utility_functions *utility_functions) /* {{{ */
tsrm_set_new_thread_end_handler(zend_new_thread_end_handler);
tsrm_set_shutdown_handler(zend_interned_strings_dtor);
#endif
zend_enum_startup();
zend_closure_startup();
}
/* }}} */

View File

@@ -20,6 +20,7 @@
*/
#include "zend.h"
#include "zend_compile.h"
#include "zend_execute.h"
#include "zend_API.h"
#include "zend_hash.h"
@@ -2927,6 +2928,80 @@ static zend_always_inline void zend_normalize_internal_type(zend_type *type) {
} ZEND_TYPE_FOREACH_END();
}
static void zend_convert_internal_arg_info_type(zend_type *type, bool persistent)
{
if (ZEND_TYPE_HAS_LITERAL_NAME(*type)) {
// gen_stubs.php does not support codegen for compound types. As a
// temporary workaround, we support union types by splitting
// the type name on `|` characters if necessary.
const char *class_name = ZEND_TYPE_LITERAL_NAME(*type);
type->type_mask &= ~_ZEND_TYPE_LITERAL_NAME_BIT;
size_t num_types = 1;
const char *p = class_name;
while ((p = strchr(p, '|'))) {
num_types++;
p++;
}
if (num_types == 1) {
/* Simple class type */
zend_string *str = zend_string_init_interned(class_name, strlen(class_name), persistent);
zend_alloc_ce_cache(str);
ZEND_TYPE_SET_PTR(*type, str);
type->type_mask |= _ZEND_TYPE_NAME_BIT;
} else {
/* Union type */
zend_type_list *list = pemalloc(ZEND_TYPE_LIST_SIZE(num_types), persistent);
list->num_types = num_types;
ZEND_TYPE_SET_LIST(*type, list);
ZEND_TYPE_FULL_MASK(*type) |= _ZEND_TYPE_UNION_BIT;
const char *start = class_name;
uint32_t j = 0;
while (true) {
const char *end = strchr(start, '|');
zend_string *str = zend_string_init_interned(start, end ? end - start : strlen(start), persistent);
zend_alloc_ce_cache(str);
list->types[j] = (zend_type) ZEND_TYPE_INIT_CLASS(str, 0, 0);
if (!end) {
break;
}
start = end + 1;
j++;
}
}
}
if (ZEND_TYPE_IS_ITERABLE_FALLBACK(*type)) {
/* Warning generated an extension load warning which is emitted for every test
zend_error(E_CORE_WARNING, "iterable type is now a compile time alias for array|Traversable,"
" regenerate the argument info via the php-src gen_stub build script");
*/
zend_type legacy_iterable = ZEND_TYPE_INIT_CLASS_MASK(
ZSTR_KNOWN(ZEND_STR_TRAVERSABLE),
(type->type_mask | MAY_BE_ARRAY)
);
*type = legacy_iterable;
}
}
ZEND_API void zend_convert_internal_arg_info(zend_arg_info *new_arg_info, const zend_internal_arg_info *arg_info, bool is_return_info, bool persistent)
{
if (!is_return_info) {
new_arg_info->name = zend_string_init_interned(arg_info->name, strlen(arg_info->name), persistent);
if (arg_info->default_value) {
new_arg_info->default_value = zend_string_init_interned(arg_info->default_value, strlen(arg_info->default_value), persistent);
} else {
new_arg_info->default_value = NULL;
}
} else {
new_arg_info->name = NULL;
new_arg_info->default_value = NULL;
}
new_arg_info->type = arg_info->type;
zend_convert_internal_arg_info_type(&new_arg_info->type, persistent);
}
/* registers all functions in *library_functions in the function hash */
ZEND_API zend_result zend_register_functions(zend_class_entry *scope, const zend_function_entry *functions, HashTable *function_table, int type) /* {{{ */
{
@@ -2938,6 +3013,7 @@ ZEND_API zend_result zend_register_functions(zend_class_entry *scope, const zend
int error_type;
zend_string *lowercase_name;
size_t fname_len;
const zend_internal_arg_info *internal_arg_info;
if (type==MODULE_PERSISTENT) {
error_type = E_CORE_WARNING;
@@ -2994,7 +3070,7 @@ ZEND_API zend_result zend_register_functions(zend_class_entry *scope, const zend
if (ptr->arg_info) {
zend_internal_function_info *info = (zend_internal_function_info*)ptr->arg_info;
internal_function->arg_info = (zend_internal_arg_info*)ptr->arg_info+1;
internal_arg_info = ptr->arg_info+1;
internal_function->num_args = ptr->num_args;
/* Currently you cannot denote that the function can accept less arguments than num_args */
if (info->required_num_args == (uintptr_t)-1) {
@@ -3024,7 +3100,7 @@ ZEND_API zend_result zend_register_functions(zend_class_entry *scope, const zend
zend_error(E_CORE_WARNING, "Missing arginfo for %s%s%s()",
scope ? ZSTR_VAL(scope->name) : "", scope ? "::" : "", ptr->fname);
internal_function->arg_info = NULL;
internal_arg_info = NULL;
internal_function->num_args = 0;
internal_function->required_num_args = 0;
}
@@ -3035,13 +3111,11 @@ ZEND_API zend_result zend_register_functions(zend_class_entry *scope, const zend
!(internal_function->fn_flags & ZEND_ACC_HAS_RETURN_TYPE)) {
zend_error(E_CORE_WARNING, "%s::__toString() implemented without string return type",
ZSTR_VAL(scope->name));
internal_function->arg_info = (zend_internal_arg_info *) arg_info_toString + 1;
internal_arg_info = (zend_internal_arg_info *) arg_info_toString + 1;
internal_function->fn_flags |= ZEND_ACC_HAS_RETURN_TYPE;
internal_function->num_args = internal_function->required_num_args = 0;
}
zend_set_function_arg_flags((zend_function*)internal_function);
if (ptr->flags & ZEND_ACC_ABSTRACT) {
if (scope) {
/* This is a class that must be abstract itself. Here we set the check info. */
@@ -3106,17 +3180,17 @@ ZEND_API zend_result zend_register_functions(zend_class_entry *scope, const zend
}
/* If types of arguments have to be checked */
if (reg_function->arg_info && num_args) {
if (internal_arg_info && num_args) {
uint32_t i;
for (i = 0; i < num_args; i++) {
zend_internal_arg_info *arg_info = &reg_function->arg_info[i];
const zend_internal_arg_info *arg_info = &internal_arg_info[i];
ZEND_ASSERT(arg_info->name && "Parameter must have a name");
if (ZEND_TYPE_IS_SET(arg_info->type)) {
reg_function->fn_flags |= ZEND_ACC_HAS_TYPE_HINTS;
}
#if ZEND_DEBUG
for (uint32_t j = 0; j < i; j++) {
if (!strcmp(arg_info->name, reg_function->arg_info[j].name)) {
if (!strcmp(arg_info->name, internal_arg_info[j].name)) {
zend_error_noreturn(E_CORE_ERROR,
"Duplicate parameter name $%s for function %s%s%s()", arg_info->name,
scope ? ZSTR_VAL(scope->name) : "", scope ? "::" : "", ptr->fname);
@@ -3126,78 +3200,24 @@ ZEND_API zend_result zend_register_functions(zend_class_entry *scope, const zend
}
}
/* Rebuild arginfos if parameter/property types and/or a return type are used */
if (reg_function->arg_info &&
(reg_function->fn_flags & (ZEND_ACC_HAS_RETURN_TYPE|ZEND_ACC_HAS_TYPE_HINTS))) {
/* convert "const char*" class type names into "zend_string*" */
/* Convert zend_internal_arg_info to zend_arg_info */
if (internal_arg_info) {
uint32_t i;
zend_internal_arg_info *arg_info = reg_function->arg_info - 1;
zend_internal_arg_info *new_arg_info;
const zend_internal_arg_info *arg_info = internal_arg_info - 1;
zend_arg_info *new_arg_info;
/* Treat return type as an extra argument */
num_args++;
new_arg_info = malloc(sizeof(zend_internal_arg_info) * num_args);
memcpy(new_arg_info, arg_info, sizeof(zend_internal_arg_info) * num_args);
new_arg_info = malloc(sizeof(zend_arg_info) * num_args);
reg_function->arg_info = new_arg_info + 1;
for (i = 0; i < num_args; i++) {
if (ZEND_TYPE_HAS_LITERAL_NAME(new_arg_info[i].type)) {
// gen_stubs.php does not support codegen for DNF types in arg infos.
// As a temporary workaround, we split the type name on `|` characters,
// converting it to an union type if necessary.
const char *class_name = ZEND_TYPE_LITERAL_NAME(new_arg_info[i].type);
new_arg_info[i].type.type_mask &= ~_ZEND_TYPE_LITERAL_NAME_BIT;
size_t num_types = 1;
const char *p = class_name;
while ((p = strchr(p, '|'))) {
num_types++;
p++;
}
if (num_types == 1) {
/* Simple class type */
zend_string *str = zend_string_init_interned(class_name, strlen(class_name), 1);
zend_alloc_ce_cache(str);
ZEND_TYPE_SET_PTR(new_arg_info[i].type, str);
new_arg_info[i].type.type_mask |= _ZEND_TYPE_NAME_BIT;
} else {
/* Union type */
zend_type_list *list = malloc(ZEND_TYPE_LIST_SIZE(num_types));
list->num_types = num_types;
ZEND_TYPE_SET_LIST(new_arg_info[i].type, list);
ZEND_TYPE_FULL_MASK(new_arg_info[i].type) |= _ZEND_TYPE_UNION_BIT;
const char *start = class_name;
uint32_t j = 0;
while (true) {
const char *end = strchr(start, '|');
zend_string *str = zend_string_init_interned(start, end ? end - start : strlen(start), 1);
zend_alloc_ce_cache(str);
list->types[j] = (zend_type) ZEND_TYPE_INIT_CLASS(str, 0, 0);
if (!end) {
break;
}
start = end + 1;
j++;
}
}
}
if (ZEND_TYPE_IS_ITERABLE_FALLBACK(new_arg_info[i].type)) {
/* Warning generated an extension load warning which is emitted for every test
zend_error(E_CORE_WARNING, "iterable type is now a compile time alias for array|Traversable,"
" regenerate the argument info via the php-src gen_stub build script");
*/
zend_type legacy_iterable = ZEND_TYPE_INIT_CLASS_MASK(
ZSTR_KNOWN(ZEND_STR_TRAVERSABLE),
(new_arg_info[i].type.type_mask | MAY_BE_ARRAY)
);
new_arg_info[i].type = legacy_iterable;
}
zend_normalize_internal_type(&new_arg_info[i].type);
zend_convert_internal_arg_info(&new_arg_info[i], &arg_info[i],
i == 0, true);
}
}
zend_set_function_arg_flags((zend_function*)reg_function);
if (scope) {
zend_check_magic_method_implementation(
scope, (zend_function *)reg_function, lowercase_name, E_CORE_ERROR);
@@ -5242,48 +5262,43 @@ static zend_string *try_parse_string(const char *str, size_t len, char quote) {
}
ZEND_API zend_result zend_get_default_from_internal_arg_info(
zval *default_value_zval, const zend_internal_arg_info *arg_info)
zval *default_value_zval, const zend_arg_info *arg_info)
{
const char *default_value = arg_info->default_value;
const zend_string *default_value = arg_info->default_value;
if (!default_value) {
return FAILURE;
}
/* Avoid going through the full AST machinery for some simple and common cases. */
size_t default_value_len = strlen(default_value);
zend_ulong lval;
if (default_value_len == sizeof("null")-1
&& !memcmp(default_value, "null", sizeof("null")-1)) {
if (zend_string_equals_literal(default_value, "null")) {
ZVAL_NULL(default_value_zval);
return SUCCESS;
} else if (default_value_len == sizeof("true")-1
&& !memcmp(default_value, "true", sizeof("true")-1)) {
} else if (zend_string_equals_literal(default_value, "true")) {
ZVAL_TRUE(default_value_zval);
return SUCCESS;
} else if (default_value_len == sizeof("false")-1
&& !memcmp(default_value, "false", sizeof("false")-1)) {
} else if (zend_string_equals_literal(default_value, "false")) {
ZVAL_FALSE(default_value_zval);
return SUCCESS;
} else if (default_value_len >= 2
&& (default_value[0] == '\'' || default_value[0] == '"')
&& default_value[default_value_len - 1] == default_value[0]) {
} else if (ZSTR_LEN(default_value) >= 2
&& (ZSTR_VAL(default_value)[0] == '\'' || ZSTR_VAL(default_value)[0] == '"')
&& ZSTR_VAL(default_value)[ZSTR_LEN(default_value) - 1] == ZSTR_VAL(default_value)[0]) {
zend_string *str = try_parse_string(
default_value + 1, default_value_len - 2, default_value[0]);
ZSTR_VAL(default_value) + 1, ZSTR_LEN(default_value) - 2, ZSTR_VAL(default_value)[0]);
if (str) {
ZVAL_STR(default_value_zval, str);
return SUCCESS;
}
} else if (default_value_len == sizeof("[]")-1
&& !memcmp(default_value, "[]", sizeof("[]")-1)) {
} else if (zend_string_equals_literal(default_value, "[]")) {
ZVAL_EMPTY_ARRAY(default_value_zval);
return SUCCESS;
} else if (ZEND_HANDLE_NUMERIC_STR(default_value, default_value_len, lval)) {
} else if (ZEND_HANDLE_NUMERIC(default_value, lval)) {
ZVAL_LONG(default_value_zval, lval);
return SUCCESS;
}
#if 0
fprintf(stderr, "Evaluating %s via AST\n", default_value);
fprintf(stderr, "Evaluating %s via AST\n", ZSTR_VAL(default_value));
#endif
return get_default_via_ast(default_value_zval, default_value);
return get_default_via_ast(default_value_zval, ZSTR_VAL(default_value));
}

View File

@@ -922,8 +922,12 @@ ZEND_API bool zend_is_iterable(const zval *iterable);
ZEND_API bool zend_is_countable(const zval *countable);
ZEND_API void zend_convert_internal_arg_info(zend_arg_info *new_arg_info,
const zend_internal_arg_info *arg_info, bool is_return_info,
bool permanent);
ZEND_API zend_result zend_get_default_from_internal_arg_info(
zval *default_value_zval, const zend_internal_arg_info *arg_info);
zval *default_value_zval, const zend_arg_info *arg_info);
END_EXTERN_C()

View File

@@ -510,7 +510,7 @@ ZEND_API zend_function *zend_get_closure_invoke_method(zend_object *object) /* {
* ZEND_ACC_USER_ARG_INFO flag to prevent invalid usage by Reflection */
invoke->type = ZEND_INTERNAL_FUNCTION;
invoke->internal_function.fn_flags =
ZEND_ACC_PUBLIC | ZEND_ACC_CALL_VIA_HANDLER | (closure->func.common.fn_flags & keep_flags);
ZEND_ACC_PUBLIC | ZEND_ACC_CALL_VIA_HANDLER | ZEND_ACC_NEVER_CACHE | (closure->func.common.fn_flags & keep_flags);
if (closure->func.type != ZEND_INTERNAL_FUNCTION || (closure->func.common.fn_flags & ZEND_ACC_USER_ARG_INFO)) {
invoke->internal_function.fn_flags |=
ZEND_ACC_USER_ARG_INFO;
@@ -618,7 +618,6 @@ static HashTable *zend_closure_get_debug_info(zend_object *object, int *is_temp)
zval val;
struct _zend_arg_info *arg_info = closure->func.common.arg_info;
HashTable *debug_info;
bool zstr_args = (closure->func.type == ZEND_USER_FUNCTION) || (closure->func.common.fn_flags & ZEND_ACC_USER_ARG_INFO);
*is_temp = 1;
@@ -694,15 +693,9 @@ static HashTable *zend_closure_get_debug_info(zend_object *object, int *is_temp)
zend_string *name;
zval info;
ZEND_ASSERT(arg_info->name && "Argument should have name");
if (zstr_args) {
name = zend_strpprintf(0, "%s$%s",
ZEND_ARG_SEND_MODE(arg_info) ? "&" : "",
ZSTR_VAL(arg_info->name));
} else {
name = zend_strpprintf(0, "%s$%s",
ZEND_ARG_SEND_MODE(arg_info) ? "&" : "",
((zend_internal_arg_info*)arg_info)->name);
}
name = zend_strpprintf(0, "%s$%s",
ZEND_ARG_SEND_MODE(arg_info) ? "&" : "",
ZSTR_VAL(arg_info->name));
ZVAL_NEW_STR(&info, zend_strpprintf(0, "%s", i >= required ? "<optional>" : "<required>"));
zend_hash_update(Z_ARRVAL(val), name, &info);
zend_string_release_ex(name, 0);
@@ -885,8 +878,7 @@ ZEND_API void zend_create_fake_closure(zval *res, zend_function *func, zend_clas
}
/* }}} */
/* __call and __callStatic name the arguments "$arguments" in the docs. */
static zend_internal_arg_info trampoline_arg_info[] = {ZEND_ARG_VARIADIC_TYPE_INFO(false, arguments, IS_MIXED, false)};
static zend_arg_info trampoline_arg_info[1];
void zend_closure_from_frame(zval *return_value, const zend_execute_data *call) { /* {{{ */
zval instance;
@@ -951,3 +943,11 @@ void zend_closure_bind_var_ex(zval *closure_zv, uint32_t offset, zval *val) /* {
ZVAL_COPY_VALUE(var, val);
}
/* }}} */
void zend_closure_startup(void)
{
/* __call and __callStatic name the arguments "$arguments" in the docs. */
trampoline_arg_info[0].name = zend_string_init_interned("arguments", strlen("arguments"), true);
trampoline_arg_info[0].type = (zend_type)ZEND_TYPE_INIT_CODE(IS_MIXED, false, _ZEND_ARG_INFO_FLAGS(false, 1, 0));
trampoline_arg_info[0].default_value = NULL;
}

View File

@@ -28,6 +28,7 @@ BEGIN_EXTERN_C()
#define ZEND_CLOSURE_OBJECT(op_array) \
((zend_object*)((char*)(op_array) - sizeof(zend_object)))
void zend_closure_startup(void);
void zend_register_closure_ce(void);
void zend_closure_bind_var(zval *closure_zv, zend_string *var_name, zval *var);
void zend_closure_bind_var_ex(zval *closure_zv, uint32_t offset, zval *val);

View File

@@ -3688,21 +3688,10 @@ static void zend_compile_compound_assign(znode *result, zend_ast *ast) /* {{{ */
static uint32_t zend_get_arg_num(const zend_function *fn, const zend_string *arg_name) {
// TODO: Caching?
if (fn->type == ZEND_USER_FUNCTION) {
for (uint32_t i = 0; i < fn->common.num_args; i++) {
const zend_arg_info *arg_info = &fn->op_array.arg_info[i];
if (zend_string_equals(arg_info->name, arg_name)) {
return i + 1;
}
}
} else {
ZEND_ASSERT(fn->common.num_args == 0 || fn->internal_function.arg_info);
for (uint32_t i = 0; i < fn->common.num_args; i++) {
const zend_internal_arg_info *arg_info = &fn->internal_function.arg_info[i];
size_t len = strlen(arg_info->name);
if (zend_string_equals_cstr(arg_name, arg_info->name, len)) {
return i + 1;
}
for (uint32_t i = 0; i < fn->common.num_args; i++) {
zend_arg_info *arg_info = &fn->op_array.arg_info[i];
if (zend_string_equals(arg_info->name, arg_name)) {
return i + 1;
}
}
@@ -4702,7 +4691,7 @@ static uint32_t zend_compile_frameless_icall_ex(znode *result, const zend_ast_li
if (i < args->children) {
zend_compile_expr(&arg_zvs[i], args->child[i]);
} else {
const zend_internal_arg_info *arg_info = (const zend_internal_arg_info *)&fbc->common.arg_info[i];
const zend_arg_info *arg_info = &fbc->common.arg_info[i];
arg_zvs[i].op_type = IS_CONST;
if (zend_get_default_from_internal_arg_info(&arg_zvs[i].u.constant, arg_info) == FAILURE) {
ZEND_UNREACHABLE();
@@ -7934,6 +7923,7 @@ static void zend_compile_params(zend_ast *ast, zend_ast *return_type_ast, uint32
arg_info = &arg_infos[i];
arg_info->name = zend_string_copy(name);
arg_info->type = (zend_type) ZEND_TYPE_INIT_NONE(0);
arg_info->default_value = NULL;
if (attributes_ast) {
zend_compile_attributes(

View File

@@ -592,7 +592,7 @@ typedef struct _zend_internal_function {
zend_function *prototype;
uint32_t num_args;
uint32_t required_num_args;
zend_internal_arg_info *arg_info;
zend_arg_info *arg_info;
HashTable *attributes;
ZEND_MAP_PTR_DEF(void **, run_time_cache);
zend_string *doc_comment;
@@ -976,7 +976,8 @@ ZEND_API ZEND_COLD void zend_user_exception_handler(void);
} \
} while (0)
void zend_free_internal_arg_info(zend_internal_function *function);
ZEND_API void zend_free_internal_arg_info(zend_internal_function *function,
bool permanent);
ZEND_API void destroy_zend_function(zend_function *function);
ZEND_API void zend_function_dtor(zval *zv);
ZEND_API void destroy_zend_class(zval *zv);

View File

@@ -36,6 +36,10 @@ ZEND_API zend_class_entry *zend_ce_unit_enum;
ZEND_API zend_class_entry *zend_ce_backed_enum;
ZEND_API zend_object_handlers zend_enum_object_handlers;
static zend_arg_info zarginfo_class_UnitEnum_cases[sizeof(arginfo_class_UnitEnum_cases)/sizeof(zend_internal_arg_info)];
static zend_arg_info zarginfo_class_BackedEnum_from[sizeof(arginfo_class_BackedEnum_from)/sizeof(zend_internal_arg_info)];
static zend_arg_info zarginfo_class_BackedEnum_tryFrom[sizeof(arginfo_class_BackedEnum_tryFrom)/sizeof(zend_internal_arg_info)];
zend_object *zend_enum_new(zval *result, zend_class_entry *ce, zend_string *case_name, zval *backing_value_zv)
{
zend_object *zobj = zend_objects_new(ce);
@@ -446,7 +450,7 @@ void zend_enum_register_funcs(zend_class_entry *ce)
cases_function->function_name = ZSTR_KNOWN(ZEND_STR_CASES);
cases_function->fn_flags = fn_flags;
cases_function->doc_comment = NULL;
cases_function->arg_info = (zend_internal_arg_info *) (arginfo_class_UnitEnum_cases + 1);
cases_function->arg_info = zarginfo_class_UnitEnum_cases + 1;
zend_enum_register_func(ce, ZEND_STR_CASES, cases_function);
if (ce->enum_backing_type != IS_UNDEF) {
@@ -457,7 +461,7 @@ void zend_enum_register_funcs(zend_class_entry *ce)
from_function->doc_comment = NULL;
from_function->num_args = 1;
from_function->required_num_args = 1;
from_function->arg_info = (zend_internal_arg_info *) (arginfo_class_BackedEnum_from + 1);
from_function->arg_info = zarginfo_class_BackedEnum_from + 1;
zend_enum_register_func(ce, ZEND_STR_FROM, from_function);
zend_internal_function *try_from_function = zend_arena_calloc(&CG(arena), sizeof(zend_internal_function), 1);
@@ -467,7 +471,7 @@ void zend_enum_register_funcs(zend_class_entry *ce)
try_from_function->doc_comment = NULL;
try_from_function->num_args = 1;
try_from_function->required_num_args = 1;
try_from_function->arg_info = (zend_internal_arg_info *) (arginfo_class_BackedEnum_tryFrom + 1);
try_from_function->arg_info = zarginfo_class_BackedEnum_tryFrom + 1;
zend_enum_register_func(ce, ZEND_STR_TRYFROM_LOWERCASE, try_from_function);
}
}
@@ -633,3 +637,16 @@ ZEND_API zend_object *zend_enum_get_case_cstr(zend_class_entry *ce, const char *
zend_class_constant *c = zend_hash_str_find_ptr(CE_CONSTANTS_TABLE(ce), name, strlen(name));
return zend_enum_case_from_class_constant(c);
}
void zend_enum_startup(void)
{
for (size_t i = 0; i < sizeof(zarginfo_class_UnitEnum_cases)/sizeof(zend_arg_info); i++) {
zend_convert_internal_arg_info(&zarginfo_class_UnitEnum_cases[i], &arginfo_class_UnitEnum_cases[i], i == 0, true);
}
for (size_t i = 0; i < sizeof(zarginfo_class_BackedEnum_from)/sizeof(zend_arg_info); i++) {
zend_convert_internal_arg_info(&zarginfo_class_BackedEnum_from[i], &arginfo_class_BackedEnum_from[i], i == 0, true);
}
for (size_t i = 0; i < sizeof(zarginfo_class_BackedEnum_tryFrom)/sizeof(zend_arg_info); i++) {
zend_convert_internal_arg_info(&zarginfo_class_BackedEnum_tryFrom[i], &arginfo_class_BackedEnum_tryFrom[i], i == 0, true);
}
}

View File

@@ -30,6 +30,7 @@ extern ZEND_API zend_class_entry *zend_ce_unit_enum;
extern ZEND_API zend_class_entry *zend_ce_backed_enum;
extern ZEND_API zend_object_handlers zend_enum_object_handlers;
void zend_enum_startup(void);
void zend_register_enum_ce(void);
void zend_enum_add_interfaces(zend_class_entry *ce);
zend_result zend_enum_build_backed_enum_table(zend_class_entry *ce);

View File

@@ -136,8 +136,7 @@ static ZEND_FUNCTION(pass)
{
}
ZEND_BEGIN_ARG_INFO_EX(zend_pass_function_arg_info, 0, 0, 0)
ZEND_END_ARG_INFO()
static zend_arg_info zend_pass_function_arg_info[1] = {0};
ZEND_API const zend_internal_function zend_pass_function = {
ZEND_INTERNAL_FUNCTION, /* type */
@@ -148,7 +147,7 @@ ZEND_API const zend_internal_function zend_pass_function = {
NULL, /* prototype */
0, /* num_args */
0, /* required_num_args */
(zend_internal_arg_info *) zend_pass_function_arg_info + 1, /* arg_info */
zend_pass_function_arg_info + 1, /* arg_info */
NULL, /* attributes */
NULL, /* run_time_cache */
NULL, /* doc_comment */
@@ -1480,7 +1479,7 @@ static ZEND_COLD void zend_verify_void_return_error(const zend_function *zf, con
ZEND_API bool zend_verify_internal_return_type(const zend_function *zf, zval *ret)
{
const zend_internal_arg_info *ret_info = zf->internal_function.arg_info - 1;
const zend_arg_info *ret_info = zf->internal_function.arg_info - 1;
if (ZEND_TYPE_FULL_MASK(ret_info->type) & MAY_BE_VOID) {
if (UNEXPECTED(Z_TYPE_P(ret) != IS_NULL)) {
@@ -5474,28 +5473,17 @@ static zend_always_inline uint32_t zend_get_arg_offset_by_name(
// TODO: Use a hash table?
uint32_t num_args = fbc->common.num_args;
if (EXPECTED(fbc->type == ZEND_USER_FUNCTION)
|| EXPECTED(fbc->common.fn_flags & ZEND_ACC_USER_ARG_INFO)) {
for (uint32_t i = 0; i < num_args; i++) {
const zend_arg_info *arg_info = &fbc->common.arg_info[i];
if (zend_string_equals(arg_name, arg_info->name)) {
if (fbc->type == ZEND_USER_FUNCTION && (!fbc->op_array.refcount || !(fbc->op_array.fn_flags & ZEND_ACC_CLOSURE))) {
*cache_slot = unique_id;
*(uintptr_t *)(cache_slot + 1) = i;
}
return i;
}
}
} else {
ZEND_ASSERT(num_args == 0 || fbc->internal_function.arg_info);
for (uint32_t i = 0; i < num_args; i++) {
const zend_internal_arg_info *arg_info = &fbc->internal_function.arg_info[i];
size_t len = strlen(arg_info->name);
if (zend_string_equals_cstr(arg_name, arg_info->name, len)) {
for (uint32_t i = 0; i < num_args; i++) {
const zend_arg_info *arg_info = &fbc->common.arg_info[i];
if (zend_string_equals(arg_name, arg_info->name)) {
if ((fbc->type == ZEND_USER_FUNCTION
&& (!fbc->op_array.refcount || !(fbc->op_array.fn_flags & ZEND_ACC_CLOSURE)))
|| (fbc->type == ZEND_INTERNAL_FUNCTION
&& !(fbc->common.fn_flags & ZEND_ACC_NEVER_CACHE))) {
*cache_slot = unique_id;
*(uintptr_t *)(cache_slot + 1) = i;
return i;
}
return i;
}
}
@@ -5503,7 +5491,7 @@ static zend_always_inline uint32_t zend_get_arg_offset_by_name(
if ((fbc->type == ZEND_USER_FUNCTION
&& (!fbc->op_array.refcount || !(fbc->op_array.fn_flags & ZEND_ACC_CLOSURE)))
|| (fbc->type == ZEND_INTERNAL_FUNCTION
&& !(fbc->common.fn_flags & ZEND_ACC_USER_ARG_INFO))) {
&& !(fbc->common.fn_flags & ZEND_ACC_NEVER_CACHE))) {
*cache_slot = unique_id;
*(uintptr_t *)(cache_slot + 1) = fbc->common.num_args;
}
@@ -5661,7 +5649,7 @@ ZEND_API zend_result ZEND_FASTCALL zend_handle_undef_args(zend_execute_data *cal
continue;
}
zend_internal_arg_info *arg_info = &fbc->internal_function.arg_info[i];
zend_arg_info *arg_info = &fbc->internal_function.arg_info[i];
if (i < fbc->common.required_num_args) {
zend_execute_data *old = start_fake_frame(call, NULL);
zend_argument_error(zend_ce_argument_count_error, i + 1, "not passed");

View File

@@ -652,11 +652,7 @@ ZEND_API const char *get_function_arg_name(const zend_function *func, uint32_t a
return NULL;
}
if (func->type == ZEND_USER_FUNCTION || (func->common.fn_flags & ZEND_ACC_USER_ARG_INFO)) {
return ZSTR_VAL(func->common.arg_info[arg_num - 1].name);
} else {
return ((zend_internal_arg_info*) func->common.arg_info)[arg_num - 1].name;
}
return ZSTR_VAL(func->common.arg_info[arg_num - 1].name);
}
/* }}} */

View File

@@ -953,18 +953,14 @@ static ZEND_COLD zend_string *zend_get_function_declaration(
}
smart_str_appendc(&str, '$');
if (fptr->type == ZEND_INTERNAL_FUNCTION) {
smart_str_appends(&str, ((zend_internal_arg_info*)arg_info)->name);
} else {
smart_str_appendl(&str, ZSTR_VAL(arg_info->name), ZSTR_LEN(arg_info->name));
}
smart_str_append(&str, arg_info->name);
if (i >= required && !ZEND_ARG_IS_VARIADIC(arg_info)) {
smart_str_appends(&str, " = ");
if (fptr->type == ZEND_INTERNAL_FUNCTION) {
if (((zend_internal_arg_info*)arg_info)->default_value) {
smart_str_appends(&str, ((zend_internal_arg_info*)arg_info)->default_value);
if (arg_info->default_value) {
smart_str_append(&str, arg_info->default_value);
} else {
smart_str_appends(&str, "<default>");
}

View File

@@ -46,6 +46,9 @@
#define IN_ISSET ZEND_GUARD_PROPERTY_ISSET
#define IN_HOOK ZEND_GUARD_PROPERTY_HOOK
static zend_arg_info zend_call_trampoline_arginfo[1] = {{0}};
static zend_arg_info zend_property_hook_arginfo[1] = {{0}};
static zend_always_inline bool zend_objects_check_stack_limit(void)
{
#ifdef ZEND_CHECK_STACK_LIMIT
@@ -1782,9 +1785,6 @@ ZEND_API zend_function *zend_get_property_hook_trampoline(
const zend_property_info *prop_info,
zend_property_hook_kind kind, zend_string *prop_name)
{
static const zend_internal_arg_info arg_info[2] = {
{ .name = "value" }
};
zend_function *func;
if (EXPECTED(EG(trampoline).common.function_name == NULL)) {
func = &EG(trampoline);
@@ -1810,7 +1810,7 @@ ZEND_API zend_function *zend_get_property_hook_trampoline(
func->common.scope = prop_info->ce;
func->common.prototype = NULL;
func->common.prop_info = prop_info;
func->common.arg_info = (zend_arg_info *) arg_info;
func->common.arg_info = zend_property_hook_arginfo;
func->internal_function.handler = kind == ZEND_PROPERTY_HOOK_GET
? ZEND_FN(zend_parent_hook_get_trampoline)
: ZEND_FN(zend_parent_hook_set_trampoline);
@@ -2574,3 +2574,8 @@ ZEND_API const zend_object_handlers std_object_handlers = {
zend_std_compare_objects, /* compare */
NULL, /* get_properties_for */
};
void zend_object_handlers_startup(void) {
zend_call_trampoline_arginfo[0].name = ZSTR_KNOWN(ZEND_STR_ARGS);
zend_property_hook_arginfo[0].name = ZSTR_KNOWN(ZEND_STR_VALUE);
}

View File

@@ -334,6 +334,8 @@ ZEND_API zend_function *zend_get_property_hook_trampoline(
ZEND_API bool ZEND_FASTCALL zend_asymmetric_property_has_set_access(const zend_property_info *prop_info);
void zend_object_handlers_startup(void);
#define zend_release_properties(ht) do { \
if (ht) { \
zend_array_release(ht); \

View File

@@ -124,21 +124,32 @@ ZEND_API void zend_type_release(zend_type type, bool persistent) {
}
}
void zend_free_internal_arg_info(zend_internal_function *function) {
if ((function->fn_flags & (ZEND_ACC_HAS_RETURN_TYPE|ZEND_ACC_HAS_TYPE_HINTS)) &&
function->arg_info) {
ZEND_API void zend_free_internal_arg_info(zend_internal_function *function,
bool persistent) {
if (function->arg_info) {
ZEND_ASSERT((persistent || (function->fn_flags & ZEND_ACC_NEVER_CACHE))
&& "Functions with non-persistent arg_info must be flagged ZEND_ACC_NEVER_CACHE");
uint32_t i;
uint32_t num_args = function->num_args + 1;
zend_internal_arg_info *arg_info = function->arg_info - 1;
zend_arg_info *arg_info = function->arg_info - 1;
if (function->fn_flags & ZEND_ACC_VARIADIC) {
num_args++;
}
for (i = 0 ; i < num_args; i++) {
zend_type_release(arg_info[i].type, /* persistent */ true);
bool is_return_info = i == 0;
if (!is_return_info) {
zend_string_release_ex(arg_info[i].name, persistent);
if (arg_info[i].default_value) {
zend_string_release_ex(arg_info[i].default_value,
persistent);
}
}
zend_type_release(arg_info[i].type, persistent);
}
free(arg_info);
pefree(arg_info, persistent);
}
}
@@ -157,7 +168,7 @@ ZEND_API void zend_function_dtor(zval *zv)
/* For methods this will be called explicitly. */
if (!function->common.scope) {
zend_free_internal_arg_info(&function->internal_function);
zend_free_internal_arg_info(&function->internal_function, true);
if (function->common.attributes) {
zend_hash_release(function->common.attributes);
@@ -474,12 +485,9 @@ ZEND_API void destroy_zend_class(zval *zv)
zend_hash_destroy(&ce->properties_info);
zend_string_release_ex(ce->name, 1);
/* TODO: eliminate this loop for classes without functions with arg_info / attributes */
ZEND_HASH_MAP_FOREACH_PTR(&ce->function_table, fn) {
if (fn->common.scope == ce) {
if (fn->common.fn_flags & (ZEND_ACC_HAS_RETURN_TYPE|ZEND_ACC_HAS_TYPE_HINTS)) {
zend_free_internal_arg_info(&fn->internal_function);
}
zend_free_internal_arg_info(&fn->internal_function, true);
if (fn->common.attributes) {
zend_hash_release(fn->common.attributes);

View File

@@ -663,8 +663,7 @@ static void accel_copy_permanent_strings(zend_new_interned_string_func_t new_int
if (Z_FUNC(p->val)->common.function_name) {
Z_FUNC(p->val)->common.function_name = new_interned_string(Z_FUNC(p->val)->common.function_name);
}
if (Z_FUNC(p->val)->common.arg_info &&
(Z_FUNC(p->val)->common.fn_flags & (ZEND_ACC_HAS_RETURN_TYPE|ZEND_ACC_HAS_TYPE_HINTS))) {
if (Z_FUNC(p->val)->common.arg_info) {
uint32_t i;
uint32_t num_args = Z_FUNC(p->val)->common.num_args + 1;
zend_arg_info *arg_info = Z_FUNC(p->val)->common.arg_info - 1;
@@ -673,6 +672,12 @@ static void accel_copy_permanent_strings(zend_new_interned_string_func_t new_int
num_args++;
}
for (i = 0 ; i < num_args; i++) {
if (i > 0) {
arg_info[i].name = new_interned_string(arg_info[i].name);
if (arg_info[i].default_value) {
arg_info[i].default_value = new_interned_string(arg_info[i].default_value);
}
}
accel_copy_permanent_list_types(new_interned_string, arg_info[i].type);
}
}
@@ -714,6 +719,24 @@ static void accel_copy_permanent_strings(zend_new_interned_string_func_t new_int
if (Z_FUNC(q->val)->common.function_name) {
Z_FUNC(q->val)->common.function_name = new_interned_string(Z_FUNC(q->val)->common.function_name);
}
if (Z_FUNC(q->val)->common.scope == ce) {
uint32_t i;
uint32_t num_args = Z_FUNC(q->val)->common.num_args + 1;
zend_arg_info *arg_info = Z_FUNC(q->val)->common.arg_info - 1;
if (Z_FUNC(q->val)->common.fn_flags & ZEND_ACC_VARIADIC) {
num_args++;
}
for (i = 0 ; i < num_args; i++) {
if (i > 0) {
arg_info[i].name = new_interned_string(arg_info[i].name);
if (arg_info[i].default_value) {
arg_info[i].default_value = new_interned_string(arg_info[i].default_value);
}
}
accel_copy_permanent_list_types(new_interned_string, arg_info[i].type);
}
}
} ZEND_HASH_FOREACH_END();
ZEND_HASH_MAP_FOREACH_BUCKET(&ce->constants_table, q) {

View File

@@ -493,7 +493,7 @@ static bool zend_jit_needs_arg_dtor(const zend_function *func, uint32_t arg_num,
&& func->type == ZEND_INTERNAL_FUNCTION
&& (func->internal_function.fn_flags & ZEND_ACC_HAS_TYPE_HINTS) != 0
&& arg_num < func->internal_function.num_args) {
const zend_internal_arg_info *arg_info = &func->internal_function.arg_info[arg_num];
const zend_arg_info *arg_info = &func->internal_function.arg_info[arg_num];
if (ZEND_ARG_SEND_MODE(arg_info) == ZEND_SEND_BY_VAL
&& ZEND_TYPE_IS_SET(arg_info->type)

View File

@@ -1321,6 +1321,7 @@ static void cls_method_dtor(zval *el) /* {{{ */ {
if (func->common.attributes) {
zend_hash_release(func->common.attributes);
}
zend_free_internal_arg_info(&func->internal_function, false);
efree(func);
}
/* }}} */
@@ -1336,6 +1337,7 @@ static void cls_method_pdtor(zval *el) /* {{{ */ {
if (func->common.attributes) {
zend_hash_release(func->common.attributes);
}
zend_free_internal_arg_info(&func->internal_function, true);
pefree(func, 1);
}
/* }}} */
@@ -1408,7 +1410,19 @@ bool pdo_hash_methods(pdo_dbh_object_t *dbh_obj, int kind)
if (funcs->arg_info) {
zend_internal_function_info *info = (zend_internal_function_info*)funcs->arg_info;
func.arg_info = (zend_internal_arg_info*)funcs->arg_info + 1;
uint32_t num_arg_info = 1 + funcs->num_args;
if (func.fn_flags & ZEND_ACC_VARIADIC) {
num_arg_info++;
}
zend_arg_info *arg_info = safe_pemalloc(num_arg_info,
sizeof(zend_arg_info), 0, dbh->is_persistent);
for (uint32_t i = 0; i < num_arg_info; i++) {
zend_convert_internal_arg_info(&arg_info[i],
&funcs->arg_info[i], i == 0, dbh->is_persistent);
}
func.arg_info = arg_info + 1;
func.num_args = funcs->num_args;
if (info->required_num_args == (uint32_t)-1) {
func.required_num_args = funcs->num_args;

View File

@@ -763,11 +763,6 @@ static void format_default_value(smart_str *str, zval *value) {
}
}
static inline bool has_internal_arg_info(const zend_function *fptr) {
return fptr->type == ZEND_INTERNAL_FUNCTION
&& !(fptr->common.fn_flags & ZEND_ACC_USER_ARG_INFO);
}
/* {{{ _parameter_string */
static void _parameter_string(smart_str *str, zend_function *fptr, struct _zend_arg_info *arg_info, uint32_t offset, bool required, char* indent)
{
@@ -789,17 +784,15 @@ static void _parameter_string(smart_str *str, zend_function *fptr, struct _zend_
if (ZEND_ARG_IS_VARIADIC(arg_info)) {
smart_str_appends(str, "...");
}
smart_str_append_printf(str, "$%s", has_internal_arg_info(fptr)
? ((zend_internal_arg_info*)arg_info)->name : ZSTR_VAL(arg_info->name));
smart_str_append_printf(str, "$%s", ZSTR_VAL(arg_info->name));
if (!required && !ZEND_ARG_IS_VARIADIC(arg_info)) {
if (fptr->type == ZEND_INTERNAL_FUNCTION) {
smart_str_appends(str, " = ");
/* TODO: We don't have a way to fetch the default value for an internal function
* with userland arg info. */
if (has_internal_arg_info(fptr)
&& ((zend_internal_arg_info*)arg_info)->default_value) {
smart_str_appends(str, ((zend_internal_arg_info*)arg_info)->default_value);
if (arg_info->default_value) {
smart_str_append(str, arg_info->default_value);
} else {
smart_str_appends(str, "<default>");
}
@@ -1463,11 +1456,7 @@ static void reflection_parameter_factory(zend_function *fptr, zval *closure_obje
}
prop_name = reflection_prop_name(object);
if (has_internal_arg_info(fptr)) {
ZVAL_STRING(prop_name, ((zend_internal_arg_info*)arg_info)->name);
} else {
ZVAL_STR_COPY(prop_name, arg_info->name);
}
ZVAL_STR_COPY(prop_name, arg_info->name);
}
/* }}} */
@@ -1653,8 +1642,7 @@ static zend_result get_parameter_default(zval *result, parameter_reference *para
/* We don't have a way to determine the default value for this case right now. */
return FAILURE;
}
return zend_get_default_from_internal_arg_info(
result, (zend_internal_arg_info *) param->arg_info);
return zend_get_default_from_internal_arg_info(result, param->arg_info);
} else {
zval *default_value = get_default_from_recv((zend_op_array *) param->fptr, param->offset);
if (!default_value) {
@@ -2590,22 +2578,11 @@ ZEND_METHOD(ReflectionParameter, __construct)
uint32_t i;
position = -1;
if (has_internal_arg_info(fptr)) {
for (i = 0; i < num_args; i++) {
if (arg_info[i].name) {
if (strcmp(((zend_internal_arg_info*)arg_info)[i].name, ZSTR_VAL(arg_name)) == 0) {
position = i;
break;
}
}
}
} else {
for (i = 0; i < num_args; i++) {
if (arg_info[i].name) {
if (zend_string_equals(arg_name, arg_info[i].name)) {
position = i;
break;
}
for (i = 0; i < num_args; i++) {
if (arg_info[i].name) {
if (zend_string_equals(arg_name, arg_info[i].name)) {
position = i;
break;
}
}
}
@@ -2646,11 +2623,7 @@ ZEND_METHOD(ReflectionParameter, __construct)
prop_name = reflection_prop_name(object);
zval_ptr_dtor(prop_name);
if (has_internal_arg_info(fptr)) {
ZVAL_STRING(prop_name, ((zend_internal_arg_info*)arg_info)[position].name);
} else {
ZVAL_STR_COPY(prop_name, arg_info[position].name);
}
ZVAL_STR_COPY(prop_name, arg_info[position].name);
return;
failure:
@@ -2689,11 +2662,7 @@ ZEND_METHOD(ReflectionParameter, getName)
ZEND_PARSE_PARAMETERS_NONE();
GET_REFLECTION_OBJECT_PTR(param);
if (has_internal_arg_info(param->fptr)) {
RETURN_STRING(((zend_internal_arg_info *) param->arg_info)->name);
} else {
RETURN_STR_COPY(param->arg_info->name);
}
RETURN_STR_COPY(param->arg_info->name);
}
/* }}} */
@@ -2947,8 +2916,7 @@ ZEND_METHOD(ReflectionParameter, isDefaultValueAvailable)
GET_REFLECTION_OBJECT_PTR(param);
if (param->fptr->type == ZEND_INTERNAL_FUNCTION) {
RETURN_BOOL(!(param->fptr->common.fn_flags & ZEND_ACC_USER_ARG_INFO)
&& ((zend_internal_arg_info*) (param->arg_info))->default_value);
RETURN_BOOL(param->arg_info->default_value);
} else {
zval *default_value = get_default_from_recv((zend_op_array *)param->fptr, param->offset);
RETURN_BOOL(default_value != NULL);

View File

@@ -37,10 +37,12 @@
#include "Zend/Optimizer/zend_optimizer.h"
#include "Zend/zend_alloc.h"
#include "test_arginfo.h"
#include "tmp_methods_arginfo.h"
#include "zend_call_stack.h"
#include "zend_exceptions.h"
#include "zend_mm_custom_handlers.h"
#include "ext/uri/php_uri.h"
#include "zend_observer.h"
#if defined(HAVE_LIBXML) && !defined(PHP_WIN32)
# include <libxml/globals.h>
@@ -1030,16 +1032,38 @@ static ZEND_FUNCTION(zend_test_log_err_debug)
php_log_err_with_severity(ZSTR_VAL(str), LOG_DEBUG);
}
typedef struct _zend_test_object {
zend_internal_function *tmp_method;
zend_object std;
} zend_test_object;
static zend_object *zend_test_class_new(zend_class_entry *class_type)
{
zend_object *obj = zend_objects_new(class_type);
object_properties_init(obj, class_type);
obj->handlers = &zend_test_class_handlers;
return obj;
zend_test_object *intern = zend_object_alloc(sizeof(zend_test_object), class_type);
zend_object_std_init(&intern->std, class_type);
object_properties_init(&intern->std, class_type);
return &intern->std;
}
static void zend_test_class_free_obj(zend_object *object)
{
zend_test_object *intern = (zend_test_object*)((char*)object - XtOffsetOf(zend_test_object, std));
if (intern->tmp_method) {
zend_internal_function *func = intern->tmp_method;
intern->tmp_method = NULL;
zend_string_release_ex(func->function_name, 0);
zend_free_internal_arg_info(func, false);
efree(func);
}
zend_object_std_dtor(object);
}
static zend_function *zend_test_class_method_get(zend_object **object, zend_string *name, const zval *key)
{
zend_test_object *intern = (zend_test_object*)((char*)(*object) - XtOffsetOf(zend_test_object, std));
if (zend_string_equals_literal_ci(name, "test")) {
zend_internal_function *fptr;
@@ -1058,6 +1082,41 @@ static zend_function *zend_test_class_method_get(zend_object **object, zend_stri
fptr->handler = ZEND_FN(zend_test_func);
fptr->doc_comment = NULL;
return (zend_function*)fptr;
} else if (zend_string_equals_literal_ci(name, "testTmpMethodWithArgInfo")) {
if (intern->tmp_method) {
return (zend_function*)intern->tmp_method;
}
const zend_function_entry *entry = &class_ZendTestTmpMethods_methods[0];
zend_internal_function *fptr = emalloc(sizeof(zend_internal_function));
memset(fptr, 0, sizeof(zend_internal_function));
fptr->type = ZEND_INTERNAL_FUNCTION;
fptr->handler = entry->handler;
fptr->function_name = zend_string_init(entry->fname, strlen(entry->fname), false);
fptr->scope = intern->std.ce;
fptr->prototype = NULL;
fptr->T = ZEND_OBSERVER_ENABLED;
fptr->fn_flags = ZEND_ACC_PUBLIC | ZEND_ACC_NEVER_CACHE;
zend_internal_function_info *info = (zend_internal_function_info*)entry->arg_info;
uint32_t num_arg_info = 1 + entry->num_args;
zend_arg_info *arg_info = safe_emalloc(num_arg_info, sizeof(zend_arg_info), 0);
for (uint32_t i = 0; i < num_arg_info; i++) {
zend_convert_internal_arg_info(&arg_info[i], &entry->arg_info[i], i == 0, false);
}
fptr->arg_info = arg_info + 1;
fptr->num_args = entry->num_args;
if (info->required_num_args == (uint32_t)-1) {
fptr->required_num_args = entry->num_args;
} else {
fptr->required_num_args = info->required_num_args;
}
intern->tmp_method = fptr;
return (zend_function*)fptr;
}
return zend_std_get_method(object, name, key);
@@ -1145,6 +1204,18 @@ static ZEND_METHOD(_ZendTestClass, variadicTest) {
object_init_ex(return_value, zend_get_called_scope(execute_data));
}
ZEND_METHOD(ZendTestTmpMethods, testTmpMethodWithArgInfo)
{
zend_object *obj;
zend_string *str;
ZEND_PARSE_PARAMETERS_START(0, 2);
Z_PARAM_OPTIONAL;
Z_PARAM_OBJ_OR_NULL(obj);
Z_PARAM_STR(str);
ZEND_PARSE_PARAMETERS_END();
}
static ZEND_METHOD(_ZendTestChildClass, returnsThrowable)
{
ZEND_PARSE_PARAMETERS_NONE();
@@ -1450,11 +1521,14 @@ PHP_MINIT_FUNCTION(zend_test)
register_ZendTestClass_dnf_property(zend_test_class);
zend_test_class->create_object = zend_test_class_new;
zend_test_class->get_static_method = zend_test_class_static_method_get;
zend_test_class->default_object_handlers = &zend_test_class_handlers;
zend_test_child_class = register_class__ZendTestChildClass(zend_test_class);
memcpy(&zend_test_class_handlers, &std_object_handlers, sizeof(zend_object_handlers));
zend_test_class_handlers.get_method = zend_test_class_method_get;
zend_test_class_handlers.free_obj = zend_test_class_free_obj;
zend_test_class_handlers.offset = XtOffsetOf(zend_test_object, std);
zend_test_gen_stub_flag_compatibility_test = register_class_ZendTestGenStubFlagCompatibilityTest();

View File

@@ -0,0 +1,11 @@
<?php
/** @generate-function-entries */
/**
* These methods will be added to a class at runtime
* @undocumentable
*/
class ZendTestTmpMethods {
public function testTmpMethodWithArgInfo(Foo|Bar|null $tmpMethodParamName = null, string $tmpMethodParamWithStringDefaultValue = "tmpMethodParamWithStringDefaultValue"): void {}
}

14
ext/zend_test/tmp_methods_arginfo.h generated Normal file
View File

@@ -0,0 +1,14 @@
/* This is a generated file, edit the .stub.php file instead.
* Stub hash: 7fd99c0b5a1957cb3a8c08a74421a720475bb46d */
ZEND_BEGIN_ARG_WITH_RETURN_TYPE_INFO_EX(arginfo_class_ZendTestTmpMethods_testTmpMethodWithArgInfo, 0, 0, IS_VOID, 0)
ZEND_ARG_OBJ_TYPE_MASK(0, tmpMethodParamName, Foo|Bar, MAY_BE_NULL, "null")
ZEND_ARG_TYPE_INFO_WITH_DEFAULT_VALUE(0, tmpMethodParamWithStringDefaultValue, IS_STRING, 0, "\"tmpMethodParamWithStringDefaultValue\"")
ZEND_END_ARG_INFO()
ZEND_METHOD(ZendTestTmpMethods, testTmpMethodWithArgInfo);
static const zend_function_entry class_ZendTestTmpMethods_methods[] = {
ZEND_ME(ZendTestTmpMethods, testTmpMethodWithArgInfo, arginfo_class_ZendTestTmpMethods_testTmpMethodWithArgInfo, ZEND_ACC_PUBLIC)
ZEND_FE_END
};

View File

@@ -34,11 +34,7 @@ static inline void phpdbg_append_individual_arg(smart_str *s, uint32_t i, zend_f
}
if (i < func->common.num_args) {
if (arginfo) {
if (func->type == ZEND_INTERNAL_FUNCTION) {
arg_name = (char *) ((zend_internal_arg_info *) &arginfo[i])->name;
} else {
arg_name = ZSTR_VAL(arginfo[i].name);
}
arg_name = ZSTR_VAL(arginfo[i].name);
}
smart_str_appends(s, arg_name ? arg_name : "?");
smart_str_appendc(s, '=');
@@ -213,11 +209,7 @@ static void phpdbg_dump_prototype(zval *tmp) /* {{{ */
char *arg_name = NULL;
if (arginfo) {
if (func->type == ZEND_INTERNAL_FUNCTION) {
arg_name = (char *)((zend_internal_arg_info *)&arginfo[j])->name;
} else {
arg_name = ZSTR_VAL(arginfo[j].name);
}
arg_name = ZSTR_VAL(arginfo[j].name);
}
if (!is_variadic) {