From fedfc7239b4efc41e32e1cc7a1aaed3b84f19eea Mon Sep 17 00:00:00 2001 From: ju1ius Date: Mon, 14 Aug 2023 15:17:25 +0200 Subject: [PATCH 01/14] adds failing test case for #10120 --- .../typed_properties_095.phpt | 4 + ext/zend_test/test.c | 81 +++++++++++++++++++ .../tests/internal_dnf_arguments.phpt | 34 ++++++++ 3 files changed, 119 insertions(+) create mode 100644 ext/zend_test/tests/internal_dnf_arguments.phpt diff --git a/Zend/tests/type_declarations/typed_properties_095.phpt b/Zend/tests/type_declarations/typed_properties_095.phpt index 5caf862e72da..321b07e34c73 100644 --- a/Zend/tests/type_declarations/typed_properties_095.phpt +++ b/Zend/tests/type_declarations/typed_properties_095.phpt @@ -75,6 +75,8 @@ object(_ZendTestClass)#1 (3) { uninitialized(Traversable&Countable) ["readonlyProp"]=> uninitialized(int) + ["dnfProperty"]=> + uninitialized(Iterator|(Traversable&Countable)) } int(123) Cannot assign string to property _ZendTestClass::$intProp of type int @@ -91,6 +93,8 @@ object(Test)#4 (3) { uninitialized(Traversable&Countable) ["readonlyProp"]=> uninitialized(int) + ["dnfProperty"]=> + uninitialized(Iterator|(Traversable&Countable)) } int(123) Cannot assign string to property _ZendTestClass::$staticIntProp of type int diff --git a/ext/zend_test/test.c b/ext/zend_test/test.c index b4ea2b27c9e9..9e4b81588e85 100644 --- a/ext/zend_test/test.c +++ b/ext/zend_test/test.c @@ -884,11 +884,92 @@ static void le_throwing_resource_dtor(zend_resource *rsrc) zend_throw_exception(NULL, "Throwing resource destructor called", 0); } +// Returns a newly allocated DNF type `Iterator|(Traversable&Countable)`. +// +// We need to generate it "manually" because gen_stubs.php does not support codegen for DNF types ATM. +static zend_type create_test_dnf_type(void) { + zend_string *class_Iterator = zend_string_init_interned("Iterator", sizeof("Iterator") - 1, true); + zend_alloc_ce_cache(class_Iterator); + zend_string *class_Traversable = zend_string_init_interned("Traversable", sizeof("Traversable") - 1, true); + zend_alloc_ce_cache(class_Traversable); + zend_string *class_Countable = zend_string_init_interned("Countable", sizeof("Countable") - 1, true); + zend_alloc_ce_cache(class_Countable); + // + zend_type_list *intersection_list = malloc(ZEND_TYPE_LIST_SIZE(2)); + intersection_list->num_types = 2; + intersection_list->types[0] = (zend_type) ZEND_TYPE_INIT_CLASS(class_Traversable, 0, 0); + intersection_list->types[1] = (zend_type) ZEND_TYPE_INIT_CLASS(class_Countable, 0, 0); + zend_type_list *union_list = malloc(ZEND_TYPE_LIST_SIZE(2)); + union_list->num_types = 2; + union_list->types[0] = (zend_type) ZEND_TYPE_INIT_CLASS(class_Iterator, 0, 0); + union_list->types[1] = (zend_type) ZEND_TYPE_INIT_INTERSECTION(intersection_list, 0); + return (zend_type) ZEND_TYPE_INIT_UNION(union_list, 0); +} + +static void register_ZendTestClass_dnf_property(zend_class_entry *ce) { + zend_string *prop_name = zend_string_init_interned("dnfProperty", sizeof("dnfProperty") - 1, true); + zval default_value; + ZVAL_UNDEF(&default_value); + zend_type type = create_test_dnf_type(); + zend_declare_typed_property(ce, prop_name, &default_value, ZEND_ACC_PUBLIC, NULL, type); +} + +// arg_info for `zend_test_internal_dnf_arguments` +// The types are upgraded to DNF types in `register_dynamic_function_entries()` +static zend_internal_arg_info arginfo_zend_test_internal_dnf_arguments[] = { + {(const char*)(uintptr_t)(1), {0}, NULL}, + {"arg", {0}, NULL} +}; + +static ZEND_NAMED_FUNCTION(zend_test_internal_dnf_arguments) +{ + zend_object *obj; + ZEND_PARSE_PARAMETERS_START(1, 1); + Z_PARAM_OBJ(obj) + ZEND_PARSE_PARAMETERS_END(); + // we have to perform type-checking to avoid arginfo/zpp mismatch error, + bool type_matches = ( + instanceof_function(obj->ce, zend_ce_iterator) + || ( + instanceof_function(obj->ce, zend_ce_traversable) + && instanceof_function(obj->ce, zend_ce_countable) + ) + ); + if (!type_matches) { + zend_string *ty = zend_type_to_string(arginfo_zend_test_internal_dnf_arguments[1].type); + zend_argument_type_error(1, "must be of type %s, %s given", ty->val, obj->ce->name->val); + zend_string_release(ty); + } + + RETURN_OBJ_COPY(obj); +} + +static const zend_function_entry dynamic_function_entries[] = { + { + .fname = "zend_test_internal_dnf_arguments", + .handler = zend_test_internal_dnf_arguments, + .arg_info = arginfo_zend_test_internal_dnf_arguments, + .num_args = 1, + .flags = 0, + }, + ZEND_FE_END, +}; + +static void register_dynamic_function_entries(void) { + arginfo_zend_test_internal_dnf_arguments[0].type = create_test_dnf_type(); + arginfo_zend_test_internal_dnf_arguments[1].type = create_test_dnf_type(); + // + zend_register_functions(NULL, dynamic_function_entries, NULL, MODULE_PERSISTENT); +} + PHP_MINIT_FUNCTION(zend_test) { + register_dynamic_function_entries(); + zend_test_interface = register_class__ZendTestInterface(); zend_test_class = register_class__ZendTestClass(zend_test_interface); + 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; diff --git a/ext/zend_test/tests/internal_dnf_arguments.phpt b/ext/zend_test/tests/internal_dnf_arguments.phpt new file mode 100644 index 000000000000..eb6f5fb514f0 --- /dev/null +++ b/ext/zend_test/tests/internal_dnf_arguments.phpt @@ -0,0 +1,34 @@ +--TEST-- +DNF types for internal functions +--EXTENSIONS-- +zend_test +spl +reflection +--FILE-- +getReturnType()); +$paramType = $rf->getParameters()[0]->getType(); +var_dump((string)$paramType); + +try { + zend_test_internal_dnf_arguments(new stdClass); +} catch (\Throwable $err) { + echo $err->getMessage(), "\n"; +} + +$obj = new \ArrayIterator([]); +$result = zend_test_internal_dnf_arguments($obj); +var_dump($result); + +?> +--EXPECT-- +string(32) "Iterator|(Traversable&Countable)" +string(32) "Iterator|(Traversable&Countable)" +zend_test_internal_dnf_arguments(): Argument #1 ($arg) must be of type Iterator|(Traversable&Countable), stdClass given +object(ArrayIterator)#5 (1) { + ["storage":"ArrayIterator":private]=> + array(0) { + } +} From 25db0dd22b9a42d9769d2d7a33c9bec57e651d0d Mon Sep 17 00:00:00 2001 From: ju1ius Date: Mon, 14 Aug 2023 15:21:31 +0200 Subject: [PATCH 02/14] adds support for DNF types in internal functions and properties Not that this does not add support for items generated by gen_stubs, only for items registered dynamically via the zend API. Closes #10120 --- Zend/zend_API.c | 58 +++++++++++++++++++++++++++++---------------- Zend/zend_compile.c | 2 +- Zend/zend_types.h | 18 ++++++++++---- 3 files changed, 53 insertions(+), 25 deletions(-) diff --git a/Zend/zend_API.c b/Zend/zend_API.c index 34ab803321aa..e83039af5d88 100644 --- a/Zend/zend_API.c +++ b/Zend/zend_API.c @@ -2756,6 +2756,27 @@ ZEND_API void zend_add_magic_method(zend_class_entry *ce, zend_function *fptr, z ZEND_BEGIN_ARG_WITH_RETURN_TYPE_INFO_EX(arg_info_toString, 0, 0, IS_STRING, 0) ZEND_END_ARG_INFO() +static zend_always_inline void upgrade_internal_type(zend_type *type) { + ZEND_ASSERT(!ZEND_TYPE_HAS_LITERAL_NAME(*type)); + zend_type *current; + ZEND_TYPE_FOREACH(*type, current) { + if (ZEND_TYPE_HAS_NAME(*current)) { + zend_string *name = zend_new_interned_string(ZEND_TYPE_NAME(*current)); + zend_alloc_ce_cache(name); + ZEND_TYPE_SET_PTR(*current, name); + } else if (ZEND_TYPE_HAS_LIST(*current)) { + zend_type *inner; + ZEND_TYPE_FOREACH(*current, inner) { + if (ZEND_TYPE_HAS_NAME(*inner)) { + zend_string *name = zend_new_interned_string(ZEND_TYPE_NAME(*inner)); + zend_alloc_ce_cache(name); + ZEND_TYPE_SET_PTR(*inner, name); + } + } ZEND_TYPE_FOREACH_END(); + } + } ZEND_TYPE_FOREACH_END(); +} + /* 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) /* {{{ */ { @@ -2934,11 +2955,11 @@ ZEND_API zend_result zend_register_functions(zend_class_entry *scope, const zend memcpy(new_arg_info, arg_info, sizeof(zend_internal_arg_info) * num_args); reg_function->arg_info = new_arg_info + 1; for (i = 0; i < num_args; i++) { - if (ZEND_TYPE_IS_COMPLEX(new_arg_info[i].type)) { - ZEND_ASSERT(ZEND_TYPE_HAS_NAME(new_arg_info[i].type) - && "Should be stored as simple name"); + 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); - size_t num_types = 1; const char *p = class_name; while ((p = strchr(p, '|'))) { @@ -2948,8 +2969,9 @@ ZEND_API zend_result zend_register_functions(zend_class_entry *scope, const zend if (num_types == 1) { /* Simple class type */ - ZEND_TYPE_SET_PTR(new_arg_info[i].type, - zend_string_init_interned(class_name, strlen(class_name), 1)); + 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); } else { /* Union type */ zend_type_list *list = malloc(ZEND_TYPE_LIST_SIZE(num_types)); @@ -2961,8 +2983,8 @@ ZEND_API zend_result zend_register_functions(zend_class_entry *scope, const zend 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_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; @@ -2971,16 +2993,21 @@ ZEND_API zend_result zend_register_functions(zend_class_entry *scope, const zend j++; } } + new_arg_info[i].type.type_mask &= ~_ZEND_TYPE_LITERAL_NAME_BIT; } 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_CONST_MASK(ZSTR_KNOWN(ZEND_STR_TRAVERSABLE), - (new_arg_info[i].type.type_mask|MAY_BE_ARRAY)); + 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; } + + upgrade_internal_type(&new_arg_info[i].type); } } @@ -4367,16 +4394,7 @@ ZEND_API zend_property_info *zend_declare_typed_property(zend_class_entry *ce, z property_info->type = type; if (is_persistent_class(ce)) { - zend_type *single_type; - ZEND_TYPE_FOREACH(property_info->type, single_type) { - // TODO Add support and test cases when gen_stub support added - ZEND_ASSERT(!ZEND_TYPE_HAS_LIST(*single_type)); - if (ZEND_TYPE_HAS_NAME(*single_type)) { - zend_string *name = zend_new_interned_string(ZEND_TYPE_NAME(*single_type)); - ZEND_TYPE_SET_PTR(*single_type, name); - zend_alloc_ce_cache(name); - } - } ZEND_TYPE_FOREACH_END(); + upgrade_internal_type(&property_info->type); } zend_hash_update_ptr(&ce->properties_info, name, property_info); diff --git a/Zend/zend_compile.c b/Zend/zend_compile.c index 5dff47798b01..5ba58aefe210 100644 --- a/Zend/zend_compile.c +++ b/Zend/zend_compile.c @@ -6402,7 +6402,7 @@ static zend_type zend_compile_single_typename(zend_ast *ast) /* Transform iterable into a type union alias */ if (type_code == IS_ITERABLE) { /* Set iterable bit for BC compat during Reflection and string representation of type */ - zend_type iterable = (zend_type) ZEND_TYPE_INIT_CLASS_CONST_MASK(ZSTR_KNOWN(ZEND_STR_TRAVERSABLE), + zend_type iterable = (zend_type) ZEND_TYPE_INIT_CLASS_MASK(ZSTR_KNOWN(ZEND_STR_TRAVERSABLE), (MAY_BE_ARRAY|_ZEND_TYPE_ITERABLE_BIT)); return iterable; } diff --git a/Zend/zend_types.h b/Zend/zend_types.h index df9bff9015ea..f13b4772631f 100644 --- a/Zend/zend_types.h +++ b/Zend/zend_types.h @@ -115,6 +115,7 @@ typedef void (*copy_ctor_func_t)(zval *pElement); * ZEND_TYPE_IS_ONLY_MASK() - checks if type-hint refer to standard type only * ZEND_TYPE_IS_COMPLEX() - checks if type is a type_list, or contains a class either as a CE or as a name * ZEND_TYPE_HAS_NAME() - checks if type-hint contains some class as zend_string * + * ZEND_TYPE_HAS_LITERAL_NAME() - checks if type-hint contains some class as const char * * ZEND_TYPE_IS_INTERSECTION() - checks if the type_list represents an intersection type list * ZEND_TYPE_IS_UNION() - checks if the type_list represents a union type list * @@ -141,8 +142,11 @@ typedef struct { zend_type types[1]; } zend_type_list; -#define _ZEND_TYPE_EXTRA_FLAGS_SHIFT 25 -#define _ZEND_TYPE_MASK ((1u << 25) - 1) +#define _ZEND_TYPE_EXTRA_FLAGS_SHIFT 26 +#define _ZEND_TYPE_MASK ((1u << 26) - 1) +// Used to signify that type.ptr is not a `zend_string*` but a `const char*`, +// in which case _ZEND_TYPE_NAME_BIT must also be set. +#define _ZEND_TYPE_LITERAL_NAME_BIT (1u << 25) /* Only one of these bits may be set. */ #define _ZEND_TYPE_NAME_BIT (1u << 24) #define _ZEND_TYPE_LIST_BIT (1u << 22) @@ -171,6 +175,9 @@ typedef struct { #define ZEND_TYPE_HAS_NAME(t) \ ((((t).type_mask) & _ZEND_TYPE_NAME_BIT) != 0) +#define ZEND_TYPE_HAS_LITERAL_NAME(t) \ + ((((t).type_mask) & (_ZEND_TYPE_NAME_BIT|_ZEND_TYPE_LITERAL_NAME_BIT)) == (_ZEND_TYPE_NAME_BIT|_ZEND_TYPE_LITERAL_NAME_BIT)) + #define ZEND_TYPE_HAS_LIST(t) \ ((((t).type_mask) & _ZEND_TYPE_LIST_BIT) != 0) @@ -289,11 +296,14 @@ typedef struct { #define ZEND_TYPE_INIT_CLASS(class_name, allow_null, extra_flags) \ ZEND_TYPE_INIT_PTR(class_name, _ZEND_TYPE_NAME_BIT, allow_null, extra_flags) +#define ZEND_TYPE_INIT_CLASS_MASK(class_name, type_mask) \ + ZEND_TYPE_INIT_PTR_MASK(class_name, _ZEND_TYPE_NAME_BIT | (type_mask)) + #define ZEND_TYPE_INIT_CLASS_CONST(class_name, allow_null, extra_flags) \ - ZEND_TYPE_INIT_PTR(class_name, _ZEND_TYPE_NAME_BIT, allow_null, extra_flags) + ZEND_TYPE_INIT_PTR(class_name, (_ZEND_TYPE_NAME_BIT|_ZEND_TYPE_LITERAL_NAME_BIT), allow_null, extra_flags) #define ZEND_TYPE_INIT_CLASS_CONST_MASK(class_name, type_mask) \ - ZEND_TYPE_INIT_PTR_MASK(class_name, _ZEND_TYPE_NAME_BIT | (type_mask)) + ZEND_TYPE_INIT_PTR_MASK(class_name, (_ZEND_TYPE_NAME_BIT|_ZEND_TYPE_LITERAL_NAME_BIT) | (type_mask)) typedef union _zend_value { zend_long lval; /* long value */ From e076543b6c4e1963ac60bb176c8e534b9c8e613e Mon Sep 17 00:00:00 2001 From: ju1ius Date: Mon, 14 Aug 2023 18:56:06 +0200 Subject: [PATCH 03/14] adds workaround for #11883 This allows tests to pass ASAN --- ext/zend_test/test.c | 43 +++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 43 insertions(+) diff --git a/ext/zend_test/test.c b/ext/zend_test/test.c index 9e4b81588e85..90109c83a0ad 100644 --- a/ext/zend_test/test.c +++ b/ext/zend_test/test.c @@ -962,6 +962,47 @@ static void register_dynamic_function_entries(void) { zend_register_functions(NULL, dynamic_function_entries, NULL, MODULE_PERSISTENT); } +// workaround for https://github.com/php/php-src/issues/11883 +static void release_internal_zend_type(zend_type *type) { + if (ZEND_TYPE_HAS_LIST(*type)) { + zend_type *list_type, *sublist_type; + ZEND_TYPE_LIST_FOREACH(ZEND_TYPE_LIST(*type), list_type) { + if (ZEND_TYPE_HAS_LIST(*list_type)) { + ZEND_TYPE_LIST_FOREACH(ZEND_TYPE_LIST(*list_type), sublist_type) { + if (ZEND_TYPE_HAS_NAME(*sublist_type)) { + zend_string_release(ZEND_TYPE_NAME(*sublist_type)); + } + } ZEND_TYPE_LIST_FOREACH_END(); + free(ZEND_TYPE_LIST(*list_type)); + } else if (ZEND_TYPE_HAS_NAME(*list_type)) { + zend_string_release(ZEND_TYPE_NAME(*list_type)); + } + } ZEND_TYPE_LIST_FOREACH_END(); + free(ZEND_TYPE_LIST(*type)); + } else if (ZEND_TYPE_HAS_NAME(*type)) { + zend_string_release(ZEND_TYPE_NAME(*type)); + } + // zero-out the type to avoid double-free on shutdown in zend_free_internal_arg_info + memset(type, 0, sizeof(zend_type)); +} + +// workaround for https://github.com/php/php-src/issues/11883 +static void release_dynamic_function_entries(void) { + const zend_function_entry *iter = dynamic_function_entries; + while (iter->fname) { + zend_function *func = zend_hash_str_find_ptr(CG(function_table), iter->fname, strlen(iter->fname)); + // add 1 for the return type + uint32_t num_args = func->internal_function.num_args + 1; + // return type is at offset -1 + zend_internal_arg_info *arg_info_start = func->internal_function.arg_info - 1; + for (int i = 0; i < num_args; i++) { + zend_internal_arg_info *arg_info = arg_info_start + i; + release_internal_zend_type(&arg_info->type); + } + iter++; + } +} + PHP_MINIT_FUNCTION(zend_test) { register_dynamic_function_entries(); @@ -1038,6 +1079,8 @@ PHP_MINIT_FUNCTION(zend_test) PHP_MSHUTDOWN_FUNCTION(zend_test) { + release_dynamic_function_entries(); + if (type != MODULE_TEMPORARY) { UNREGISTER_INI_ENTRIES(); } From 3c9c2921327d57204a103e93877c9b6818fe7e21 Mon Sep 17 00:00:00 2001 From: ju1ius Date: Mon, 14 Aug 2023 18:57:03 +0200 Subject: [PATCH 04/14] makes _ZEND_TYPE_NAME_BIT and _ZEND_TYPE_LITERAL_NAME_BIT exclusive --- Zend/zend_API.c | 4 +++- Zend/zend_types.h | 17 ++++++++--------- 2 files changed, 11 insertions(+), 10 deletions(-) diff --git a/Zend/zend_API.c b/Zend/zend_API.c index e83039af5d88..fceb16b1b83e 100644 --- a/Zend/zend_API.c +++ b/Zend/zend_API.c @@ -2960,6 +2960,9 @@ ZEND_API zend_result zend_register_functions(zend_class_entry *scope, const zend // 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; + new_arg_info[i].type.type_mask |= _ZEND_TYPE_NAME_BIT; + size_t num_types = 1; const char *p = class_name; while ((p = strchr(p, '|'))) { @@ -2993,7 +2996,6 @@ ZEND_API zend_result zend_register_functions(zend_class_entry *scope, const zend j++; } } - new_arg_info[i].type.type_mask &= ~_ZEND_TYPE_LITERAL_NAME_BIT; } if (ZEND_TYPE_IS_ITERABLE_FALLBACK(new_arg_info[i].type)) { /* Warning generated an extension load warning which is emitted for every test diff --git a/Zend/zend_types.h b/Zend/zend_types.h index f13b4772631f..bf4fd9d18a41 100644 --- a/Zend/zend_types.h +++ b/Zend/zend_types.h @@ -142,15 +142,14 @@ typedef struct { zend_type types[1]; } zend_type_list; -#define _ZEND_TYPE_EXTRA_FLAGS_SHIFT 26 -#define _ZEND_TYPE_MASK ((1u << 26) - 1) -// Used to signify that type.ptr is not a `zend_string*` but a `const char*`, -// in which case _ZEND_TYPE_NAME_BIT must also be set. -#define _ZEND_TYPE_LITERAL_NAME_BIT (1u << 25) +#define _ZEND_TYPE_EXTRA_FLAGS_SHIFT 25 +#define _ZEND_TYPE_MASK ((1u << 25) - 1) /* Only one of these bits may be set. */ #define _ZEND_TYPE_NAME_BIT (1u << 24) +// Used to signify that type.ptr is not a `zend_string*` but a `const char*`, +#define _ZEND_TYPE_LITERAL_NAME_BIT (1u << 23) #define _ZEND_TYPE_LIST_BIT (1u << 22) -#define _ZEND_TYPE_KIND_MASK (_ZEND_TYPE_LIST_BIT|_ZEND_TYPE_NAME_BIT) +#define _ZEND_TYPE_KIND_MASK (_ZEND_TYPE_LIST_BIT|_ZEND_TYPE_NAME_BIT|_ZEND_TYPE_LITERAL_NAME_BIT) /* For BC behaviour with iterable type */ #define _ZEND_TYPE_ITERABLE_BIT (1u << 21) /* Whether the type list is arena allocated */ @@ -176,7 +175,7 @@ typedef struct { ((((t).type_mask) & _ZEND_TYPE_NAME_BIT) != 0) #define ZEND_TYPE_HAS_LITERAL_NAME(t) \ - ((((t).type_mask) & (_ZEND_TYPE_NAME_BIT|_ZEND_TYPE_LITERAL_NAME_BIT)) == (_ZEND_TYPE_NAME_BIT|_ZEND_TYPE_LITERAL_NAME_BIT)) + ((((t).type_mask) & _ZEND_TYPE_LITERAL_NAME_BIT) != 0) #define ZEND_TYPE_HAS_LIST(t) \ ((((t).type_mask) & _ZEND_TYPE_LIST_BIT) != 0) @@ -300,10 +299,10 @@ typedef struct { ZEND_TYPE_INIT_PTR_MASK(class_name, _ZEND_TYPE_NAME_BIT | (type_mask)) #define ZEND_TYPE_INIT_CLASS_CONST(class_name, allow_null, extra_flags) \ - ZEND_TYPE_INIT_PTR(class_name, (_ZEND_TYPE_NAME_BIT|_ZEND_TYPE_LITERAL_NAME_BIT), allow_null, extra_flags) + ZEND_TYPE_INIT_PTR(class_name, _ZEND_TYPE_LITERAL_NAME_BIT, allow_null, extra_flags) #define ZEND_TYPE_INIT_CLASS_CONST_MASK(class_name, type_mask) \ - ZEND_TYPE_INIT_PTR_MASK(class_name, (_ZEND_TYPE_NAME_BIT|_ZEND_TYPE_LITERAL_NAME_BIT) | (type_mask)) + ZEND_TYPE_INIT_PTR_MASK(class_name, (_ZEND_TYPE_LITERAL_NAME_BIT | (type_mask))) typedef union _zend_value { zend_long lval; /* long value */ From 02ee5f799e236ad63631a56002353b01d6c40eef Mon Sep 17 00:00:00 2001 From: ju1ius Date: Mon, 14 Aug 2023 18:58:28 +0200 Subject: [PATCH 05/14] addresses PR comments --- ext/zend_test/test.c | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/ext/zend_test/test.c b/ext/zend_test/test.c index 90109c83a0ad..30f30b71d39c 100644 --- a/ext/zend_test/test.c +++ b/ext/zend_test/test.c @@ -890,8 +890,7 @@ static void le_throwing_resource_dtor(zend_resource *rsrc) static zend_type create_test_dnf_type(void) { zend_string *class_Iterator = zend_string_init_interned("Iterator", sizeof("Iterator") - 1, true); zend_alloc_ce_cache(class_Iterator); - zend_string *class_Traversable = zend_string_init_interned("Traversable", sizeof("Traversable") - 1, true); - zend_alloc_ce_cache(class_Traversable); + zend_string *class_Traversable = ZSTR_KNOWN(ZEND_STR_TRAVERSABLE); zend_string *class_Countable = zend_string_init_interned("Countable", sizeof("Countable") - 1, true); zend_alloc_ce_cache(class_Countable); // @@ -917,7 +916,7 @@ static void register_ZendTestClass_dnf_property(zend_class_entry *ce) { // arg_info for `zend_test_internal_dnf_arguments` // The types are upgraded to DNF types in `register_dynamic_function_entries()` static zend_internal_arg_info arginfo_zend_test_internal_dnf_arguments[] = { - {(const char*)(uintptr_t)(1), {0}, NULL}, + {(const char*)(uintptr_t)(1), {0}, NULL}, // return-type {"arg", {0}, NULL} }; @@ -956,6 +955,7 @@ static const zend_function_entry dynamic_function_entries[] = { }; static void register_dynamic_function_entries(void) { + // return-type is at index 0 arginfo_zend_test_internal_dnf_arguments[0].type = create_test_dnf_type(); arginfo_zend_test_internal_dnf_arguments[1].type = create_test_dnf_type(); // From b9cebe9604e4655c8e1a41e8b8f96225ea9fb9aa Mon Sep 17 00:00:00 2001 From: ju1ius Date: Mon, 14 Aug 2023 19:47:26 +0200 Subject: [PATCH 06/14] properly releases memory for dynamic property type --- ext/zend_test/test.c | 9 ++++++++- 1 file changed, 8 insertions(+), 1 deletion(-) diff --git a/ext/zend_test/test.c b/ext/zend_test/test.c index 30f30b71d39c..c5ffba643c46 100644 --- a/ext/zend_test/test.c +++ b/ext/zend_test/test.c @@ -982,7 +982,7 @@ static void release_internal_zend_type(zend_type *type) { } else if (ZEND_TYPE_HAS_NAME(*type)) { zend_string_release(ZEND_TYPE_NAME(*type)); } - // zero-out the type to avoid double-free on shutdown in zend_free_internal_arg_info + // zero-out the type to avoid double-free on shutdown memset(type, 0, sizeof(zend_type)); } @@ -1003,6 +1003,12 @@ static void release_dynamic_function_entries(void) { } } +// workaround for https://github.com/php/php-src/issues/11883 +static void release_ZendTestClass_dnf_property(void) { + zend_property_info *prop = zend_hash_str_find_ptr(&zend_test_class->properties_info, "dnfProperty", sizeof("dnfProperty") - 1); + release_internal_zend_type(&prop->type); +} + PHP_MINIT_FUNCTION(zend_test) { register_dynamic_function_entries(); @@ -1080,6 +1086,7 @@ PHP_MINIT_FUNCTION(zend_test) PHP_MSHUTDOWN_FUNCTION(zend_test) { release_dynamic_function_entries(); + release_ZendTestClass_dnf_property(); if (type != MODULE_TEMPORARY) { UNREGISTER_INI_ENTRIES(); From 39e62c2c894ec4bb946522aaa1c874d9c1bec9f8 Mon Sep 17 00:00:00 2001 From: ju1ius Date: Mon, 14 Aug 2023 20:31:18 +0200 Subject: [PATCH 07/14] don't zet _ZEND_TYPE_NAME_BIT for union types --- Zend/zend_API.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Zend/zend_API.c b/Zend/zend_API.c index fceb16b1b83e..782ec8d5524d 100644 --- a/Zend/zend_API.c +++ b/Zend/zend_API.c @@ -2961,7 +2961,6 @@ ZEND_API zend_result zend_register_functions(zend_class_entry *scope, const zend // 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; - new_arg_info[i].type.type_mask |= _ZEND_TYPE_NAME_BIT; size_t num_types = 1; const char *p = class_name; @@ -2975,6 +2974,7 @@ ZEND_API zend_result zend_register_functions(zend_class_entry *scope, const zend 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)); From 9748f30511ccf42073d2f4e65c128386863151aa Mon Sep 17 00:00:00 2001 From: ju1ius Date: Mon, 14 Aug 2023 20:36:44 +0200 Subject: [PATCH 08/14] addresses PR comments --- Zend/zend_API.c | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-) diff --git a/Zend/zend_API.c b/Zend/zend_API.c index 782ec8d5524d..931a5557bca9 100644 --- a/Zend/zend_API.c +++ b/Zend/zend_API.c @@ -2756,7 +2756,7 @@ ZEND_API void zend_add_magic_method(zend_class_entry *ce, zend_function *fptr, z ZEND_BEGIN_ARG_WITH_RETURN_TYPE_INFO_EX(arg_info_toString, 0, 0, IS_STRING, 0) ZEND_END_ARG_INFO() -static zend_always_inline void upgrade_internal_type(zend_type *type) { +static zend_always_inline void normalize_internal_type(zend_type *type) { ZEND_ASSERT(!ZEND_TYPE_HAS_LITERAL_NAME(*type)); zend_type *current; ZEND_TYPE_FOREACH(*type, current) { @@ -2767,6 +2767,7 @@ static zend_always_inline void upgrade_internal_type(zend_type *type) { } else if (ZEND_TYPE_HAS_LIST(*current)) { zend_type *inner; ZEND_TYPE_FOREACH(*current, inner) { + ZEND_ASSERT(!ZEND_TYPE_HAS_LITERAL_NAME(*inner) && !ZEND_TYPE_HAS_LIST(*inner)); if (ZEND_TYPE_HAS_NAME(*inner)) { zend_string *name = zend_new_interned_string(ZEND_TYPE_NAME(*inner)); zend_alloc_ce_cache(name); @@ -3009,7 +3010,7 @@ ZEND_API zend_result zend_register_functions(zend_class_entry *scope, const zend new_arg_info[i].type = legacy_iterable; } - upgrade_internal_type(&new_arg_info[i].type); + normalize_internal_type(&new_arg_info[i].type); } } @@ -4396,7 +4397,7 @@ ZEND_API zend_property_info *zend_declare_typed_property(zend_class_entry *ce, z property_info->type = type; if (is_persistent_class(ce)) { - upgrade_internal_type(&property_info->type); + normalize_internal_type(&property_info->type); } zend_hash_update_ptr(&ce->properties_info, name, property_info); From 92284c748680bad4aa53a9262bc5f3d93ea88d4d Mon Sep 17 00:00:00 2001 From: ju1ius Date: Mon, 14 Aug 2023 22:21:14 +0200 Subject: [PATCH 09/14] fixup! properly releases memory for dynamic property type --- ext/zend_test/test.c | 11 +++++++---- 1 file changed, 7 insertions(+), 4 deletions(-) diff --git a/ext/zend_test/test.c b/ext/zend_test/test.c index c5ffba643c46..d383768c90f2 100644 --- a/ext/zend_test/test.c +++ b/ext/zend_test/test.c @@ -1004,9 +1004,12 @@ static void release_dynamic_function_entries(void) { } // workaround for https://github.com/php/php-src/issues/11883 -static void release_ZendTestClass_dnf_property(void) { - zend_property_info *prop = zend_hash_str_find_ptr(&zend_test_class->properties_info, "dnfProperty", sizeof("dnfProperty") - 1); - release_internal_zend_type(&prop->type); +static void release_ZendTestClass_dnf_property(int module_type) { + // zend_hash_str_find_bucket fails when the module is loaded with dl(), no idea why... + if (module_type == MODULE_PERSISTENT) { + zend_property_info *prop = zend_hash_str_find_ptr(&zend_test_class->properties_info, "dnfProperty", sizeof("dnfProperty") - 1); + release_internal_zend_type(&prop->type); + } } PHP_MINIT_FUNCTION(zend_test) @@ -1086,7 +1089,7 @@ PHP_MINIT_FUNCTION(zend_test) PHP_MSHUTDOWN_FUNCTION(zend_test) { release_dynamic_function_entries(); - release_ZendTestClass_dnf_property(); + release_ZendTestClass_dnf_property(type); if (type != MODULE_TEMPORARY) { UNREGISTER_INI_ENTRIES(); From 36b86e79cab5d493943e96de2dc2c0b494b8411a Mon Sep 17 00:00:00 2001 From: ju1ius Date: Mon, 14 Aug 2023 23:01:16 +0200 Subject: [PATCH 10/14] adds a method to test union type arginfo registration --- ext/zend_test/test.c | 23 +++++++++++++++++++++++ ext/zend_test/test.stub.php | 2 ++ ext/zend_test/test_arginfo.h | 8 +++++++- 3 files changed, 32 insertions(+), 1 deletion(-) diff --git a/ext/zend_test/test.c b/ext/zend_test/test.c index d383768c90f2..f6936641be6d 100644 --- a/ext/zend_test/test.c +++ b/ext/zend_test/test.c @@ -884,6 +884,28 @@ static void le_throwing_resource_dtor(zend_resource *rsrc) zend_throw_exception(NULL, "Throwing resource destructor called", 0); } +static ZEND_METHOD(_ZendTestClass, takesUnionType) +{ + zend_object *obj; + ZEND_PARSE_PARAMETERS_START(1, 1); + Z_PARAM_OBJ(obj) + ZEND_PARSE_PARAMETERS_END(); + // we have to perform type-checking to avoid arginfo/zpp mismatch error, + bool type_matches = ( + instanceof_function(obj->ce, zend_standard_class_def) + || + instanceof_function(obj->ce, zend_ce_iterator) + ); + if (!type_matches) { + zend_string *ty = zend_type_to_string(execute_data->func->internal_function.arg_info->type); + zend_argument_type_error(1, "must be of type %s, %s given", ty->val, obj->ce->name->val); + zend_string_release(ty); + RETURN_THROWS(); + } + + RETURN_NULL(); +} + // Returns a newly allocated DNF type `Iterator|(Traversable&Countable)`. // // We need to generate it "manually" because gen_stubs.php does not support codegen for DNF types ATM. @@ -938,6 +960,7 @@ static ZEND_NAMED_FUNCTION(zend_test_internal_dnf_arguments) zend_string *ty = zend_type_to_string(arginfo_zend_test_internal_dnf_arguments[1].type); zend_argument_type_error(1, "must be of type %s, %s given", ty->val, obj->ce->name->val); zend_string_release(ty); + RETURN_THROWS(); } RETURN_OBJ_COPY(obj); diff --git a/ext/zend_test/test.stub.php b/ext/zend_test/test.stub.php index afaf79da2acd..312ad689b31d 100644 --- a/ext/zend_test/test.stub.php +++ b/ext/zend_test/test.stub.php @@ -53,6 +53,8 @@ public function returnsStatic(): static {} public function returnsThrowable(): Throwable {} static public function variadicTest(string|Iterator ...$elements) : static {} + + public function takesUnionType(stdclass|Iterator $arg): void {} } class _ZendTestChildClass extends _ZendTestClass diff --git a/ext/zend_test/test_arginfo.h b/ext/zend_test/test_arginfo.h index a56090fd565a..1274886e2c68 100644 --- a/ext/zend_test/test_arginfo.h +++ b/ext/zend_test/test_arginfo.h @@ -1,5 +1,5 @@ /* This is a generated file, edit the .stub.php file instead. - * Stub hash: 87c580bffe8794d7597572c0d8571c7459420df8 */ + * Stub hash: 7370ccc87cf1f3c1fe254e2b23e2e4a9f0c32178 */ ZEND_BEGIN_ARG_WITH_RETURN_TYPE_INFO_EX(arginfo_zend_test_array_return, 0, 0, IS_ARRAY, 0) ZEND_END_ARG_INFO() @@ -172,6 +172,10 @@ ZEND_BEGIN_ARG_WITH_RETURN_TYPE_INFO_EX(arginfo_class__ZendTestClass_variadicTes ZEND_ARG_VARIADIC_OBJ_TYPE_MASK(0, elements, Iterator, MAY_BE_STRING) ZEND_END_ARG_INFO() +ZEND_BEGIN_ARG_WITH_RETURN_TYPE_INFO_EX(arginfo_class__ZendTestClass_takesUnionType, 0, 1, IS_VOID, 0) + ZEND_ARG_OBJ_TYPE_MASK(0, arg, stdclass|Iterator, 0, NULL) +ZEND_END_ARG_INFO() + ZEND_BEGIN_ARG_WITH_RETURN_OBJ_INFO_EX(arginfo_class__ZendTestChildClass_returnsThrowable, 0, 0, Exception, 0) ZEND_END_ARG_INFO() @@ -258,6 +262,7 @@ static ZEND_METHOD(_ZendTestClass, __toString); static ZEND_METHOD(_ZendTestClass, returnsStatic); static ZEND_METHOD(_ZendTestClass, returnsThrowable); static ZEND_METHOD(_ZendTestClass, variadicTest); +static ZEND_METHOD(_ZendTestClass, takesUnionType); static ZEND_METHOD(_ZendTestChildClass, returnsThrowable); static ZEND_METHOD(_ZendTestTrait, testMethod); static ZEND_METHOD(ZendTestParameterAttribute, __construct); @@ -340,6 +345,7 @@ static const zend_function_entry class__ZendTestClass_methods[] = { ZEND_ME(_ZendTestClass, returnsStatic, arginfo_class__ZendTestClass_returnsStatic, ZEND_ACC_PUBLIC) ZEND_ME(_ZendTestClass, returnsThrowable, arginfo_class__ZendTestClass_returnsThrowable, ZEND_ACC_PUBLIC) ZEND_ME(_ZendTestClass, variadicTest, arginfo_class__ZendTestClass_variadicTest, ZEND_ACC_PUBLIC|ZEND_ACC_STATIC) + ZEND_ME(_ZendTestClass, takesUnionType, arginfo_class__ZendTestClass_takesUnionType, ZEND_ACC_PUBLIC) ZEND_FE_END }; From d948c74a625eaeb001e527adbc4ed9fd905fb391 Mon Sep 17 00:00:00 2001 From: ju1ius Date: Tue, 15 Aug 2023 18:55:17 +0200 Subject: [PATCH 11/14] removes workarounds for #11883 --- ext/zend_test/test.c | 53 -------------------------------------------- 1 file changed, 53 deletions(-) diff --git a/ext/zend_test/test.c b/ext/zend_test/test.c index f6936641be6d..46279a4ff8e2 100644 --- a/ext/zend_test/test.c +++ b/ext/zend_test/test.c @@ -985,56 +985,6 @@ static void register_dynamic_function_entries(void) { zend_register_functions(NULL, dynamic_function_entries, NULL, MODULE_PERSISTENT); } -// workaround for https://github.com/php/php-src/issues/11883 -static void release_internal_zend_type(zend_type *type) { - if (ZEND_TYPE_HAS_LIST(*type)) { - zend_type *list_type, *sublist_type; - ZEND_TYPE_LIST_FOREACH(ZEND_TYPE_LIST(*type), list_type) { - if (ZEND_TYPE_HAS_LIST(*list_type)) { - ZEND_TYPE_LIST_FOREACH(ZEND_TYPE_LIST(*list_type), sublist_type) { - if (ZEND_TYPE_HAS_NAME(*sublist_type)) { - zend_string_release(ZEND_TYPE_NAME(*sublist_type)); - } - } ZEND_TYPE_LIST_FOREACH_END(); - free(ZEND_TYPE_LIST(*list_type)); - } else if (ZEND_TYPE_HAS_NAME(*list_type)) { - zend_string_release(ZEND_TYPE_NAME(*list_type)); - } - } ZEND_TYPE_LIST_FOREACH_END(); - free(ZEND_TYPE_LIST(*type)); - } else if (ZEND_TYPE_HAS_NAME(*type)) { - zend_string_release(ZEND_TYPE_NAME(*type)); - } - // zero-out the type to avoid double-free on shutdown - memset(type, 0, sizeof(zend_type)); -} - -// workaround for https://github.com/php/php-src/issues/11883 -static void release_dynamic_function_entries(void) { - const zend_function_entry *iter = dynamic_function_entries; - while (iter->fname) { - zend_function *func = zend_hash_str_find_ptr(CG(function_table), iter->fname, strlen(iter->fname)); - // add 1 for the return type - uint32_t num_args = func->internal_function.num_args + 1; - // return type is at offset -1 - zend_internal_arg_info *arg_info_start = func->internal_function.arg_info - 1; - for (int i = 0; i < num_args; i++) { - zend_internal_arg_info *arg_info = arg_info_start + i; - release_internal_zend_type(&arg_info->type); - } - iter++; - } -} - -// workaround for https://github.com/php/php-src/issues/11883 -static void release_ZendTestClass_dnf_property(int module_type) { - // zend_hash_str_find_bucket fails when the module is loaded with dl(), no idea why... - if (module_type == MODULE_PERSISTENT) { - zend_property_info *prop = zend_hash_str_find_ptr(&zend_test_class->properties_info, "dnfProperty", sizeof("dnfProperty") - 1); - release_internal_zend_type(&prop->type); - } -} - PHP_MINIT_FUNCTION(zend_test) { register_dynamic_function_entries(); @@ -1111,9 +1061,6 @@ PHP_MINIT_FUNCTION(zend_test) PHP_MSHUTDOWN_FUNCTION(zend_test) { - release_dynamic_function_entries(); - release_ZendTestClass_dnf_property(type); - if (type != MODULE_TEMPORARY) { UNREGISTER_INI_ENTRIES(); } From 6866898cbf7b3761f869a45934c65a723d14ec66 Mon Sep 17 00:00:00 2001 From: ju1ius Date: Tue, 15 Aug 2023 19:51:40 +0200 Subject: [PATCH 12/14] fixes wrong arginfo.h hash after rebase --- ext/zend_test/test_arginfo.h | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/ext/zend_test/test_arginfo.h b/ext/zend_test/test_arginfo.h index 1274886e2c68..d5546638949b 100644 --- a/ext/zend_test/test_arginfo.h +++ b/ext/zend_test/test_arginfo.h @@ -1,5 +1,5 @@ /* This is a generated file, edit the .stub.php file instead. - * Stub hash: 7370ccc87cf1f3c1fe254e2b23e2e4a9f0c32178 */ + * Stub hash: b458993ee586284b1e33848313d9ddf61273604e */ ZEND_BEGIN_ARG_WITH_RETURN_TYPE_INFO_EX(arginfo_zend_test_array_return, 0, 0, IS_ARRAY, 0) ZEND_END_ARG_INFO() From 71f1b6670abb02577f9ae45375ffb6813a954f65 Mon Sep 17 00:00:00 2001 From: ju1ius Date: Wed, 16 Aug 2023 09:34:38 +0200 Subject: [PATCH 13/14] adds zend_ prefix to normalize_internal_type --- Zend/zend_API.c | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/Zend/zend_API.c b/Zend/zend_API.c index 931a5557bca9..d0b863335e29 100644 --- a/Zend/zend_API.c +++ b/Zend/zend_API.c @@ -2756,7 +2756,7 @@ ZEND_API void zend_add_magic_method(zend_class_entry *ce, zend_function *fptr, z ZEND_BEGIN_ARG_WITH_RETURN_TYPE_INFO_EX(arg_info_toString, 0, 0, IS_STRING, 0) ZEND_END_ARG_INFO() -static zend_always_inline void normalize_internal_type(zend_type *type) { +static zend_always_inline void zend_normalize_internal_type(zend_type *type) { ZEND_ASSERT(!ZEND_TYPE_HAS_LITERAL_NAME(*type)); zend_type *current; ZEND_TYPE_FOREACH(*type, current) { @@ -3010,7 +3010,7 @@ ZEND_API zend_result zend_register_functions(zend_class_entry *scope, const zend new_arg_info[i].type = legacy_iterable; } - normalize_internal_type(&new_arg_info[i].type); + zend_normalize_internal_type(&new_arg_info[i].type); } } @@ -4397,7 +4397,7 @@ ZEND_API zend_property_info *zend_declare_typed_property(zend_class_entry *ce, z property_info->type = type; if (is_persistent_class(ce)) { - normalize_internal_type(&property_info->type); + zend_normalize_internal_type(&property_info->type); } zend_hash_update_ptr(&ce->properties_info, name, property_info); From e8c5abf9e0299f9f08c6a7ea1bd9c9531603f683 Mon Sep 17 00:00:00 2001 From: ju1ius Date: Wed, 16 Aug 2023 16:21:00 +0200 Subject: [PATCH 14/14] passes the module type to register_dynamic_function_entries() Doesn't matter much in practice but we are good citizens. --- ext/zend_test/test.c | 13 +++++++------ 1 file changed, 7 insertions(+), 6 deletions(-) diff --git a/ext/zend_test/test.c b/ext/zend_test/test.c index 46279a4ff8e2..ec82c727650b 100644 --- a/ext/zend_test/test.c +++ b/ext/zend_test/test.c @@ -890,7 +890,7 @@ static ZEND_METHOD(_ZendTestClass, takesUnionType) ZEND_PARSE_PARAMETERS_START(1, 1); Z_PARAM_OBJ(obj) ZEND_PARSE_PARAMETERS_END(); - // we have to perform type-checking to avoid arginfo/zpp mismatch error, + // we have to perform type-checking to avoid arginfo/zpp mismatch error bool type_matches = ( instanceof_function(obj->ce, zend_standard_class_def) || @@ -938,7 +938,8 @@ static void register_ZendTestClass_dnf_property(zend_class_entry *ce) { // arg_info for `zend_test_internal_dnf_arguments` // The types are upgraded to DNF types in `register_dynamic_function_entries()` static zend_internal_arg_info arginfo_zend_test_internal_dnf_arguments[] = { - {(const char*)(uintptr_t)(1), {0}, NULL}, // return-type + // first entry is a zend_internal_function_info (see zend_compile.h): {argument_count, return_type, unused} + {(const char*)(uintptr_t)(1), {0}, NULL}, {"arg", {0}, NULL} }; @@ -948,7 +949,7 @@ static ZEND_NAMED_FUNCTION(zend_test_internal_dnf_arguments) ZEND_PARSE_PARAMETERS_START(1, 1); Z_PARAM_OBJ(obj) ZEND_PARSE_PARAMETERS_END(); - // we have to perform type-checking to avoid arginfo/zpp mismatch error, + // we have to perform type-checking to avoid arginfo/zpp mismatch error bool type_matches = ( instanceof_function(obj->ce, zend_ce_iterator) || ( @@ -977,17 +978,17 @@ static const zend_function_entry dynamic_function_entries[] = { ZEND_FE_END, }; -static void register_dynamic_function_entries(void) { +static void register_dynamic_function_entries(int module_type) { // return-type is at index 0 arginfo_zend_test_internal_dnf_arguments[0].type = create_test_dnf_type(); arginfo_zend_test_internal_dnf_arguments[1].type = create_test_dnf_type(); // - zend_register_functions(NULL, dynamic_function_entries, NULL, MODULE_PERSISTENT); + zend_register_functions(NULL, dynamic_function_entries, NULL, module_type); } PHP_MINIT_FUNCTION(zend_test) { - register_dynamic_function_entries(); + register_dynamic_function_entries(type); zend_test_interface = register_class__ZendTestInterface();