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:
@@ -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
|
||||
|
||||
21
Zend/tests/closures/gh19653_3.phpt
Normal file
21
Zend/tests/closures/gh19653_3.phpt
Normal 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
|
||||
21
Zend/tests/unified_arg_infos_001.phpt
Normal file
21
Zend/tests/unified_arg_infos_001.phpt
Normal 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" ]
|
||||
}
|
||||
}
|
||||
@@ -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();
|
||||
}
|
||||
/* }}} */
|
||||
|
||||
|
||||
193
Zend/zend_API.c
193
Zend/zend_API.c
@@ -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 = ®_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));
|
||||
}
|
||||
|
||||
@@ -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()
|
||||
|
||||
|
||||
@@ -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;
|
||||
}
|
||||
|
||||
@@ -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);
|
||||
|
||||
@@ -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(
|
||||
|
||||
@@ -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);
|
||||
|
||||
@@ -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);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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);
|
||||
|
||||
@@ -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");
|
||||
|
||||
@@ -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);
|
||||
}
|
||||
/* }}} */
|
||||
|
||||
|
||||
@@ -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>");
|
||||
}
|
||||
|
||||
@@ -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);
|
||||
}
|
||||
|
||||
@@ -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); \
|
||||
|
||||
@@ -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);
|
||||
|
||||
@@ -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) {
|
||||
|
||||
@@ -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)
|
||||
|
||||
@@ -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;
|
||||
|
||||
@@ -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);
|
||||
|
||||
@@ -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();
|
||||
|
||||
|
||||
11
ext/zend_test/tmp_methods.stub.php
Normal file
11
ext/zend_test/tmp_methods.stub.php
Normal 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
14
ext/zend_test/tmp_methods_arginfo.h
generated
Normal 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
|
||||
};
|
||||
@@ -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) {
|
||||
|
||||
Reference in New Issue
Block a user