From 82acb21d0f0b7242eaed370e2fa8aa50e1efe136 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Tim=20D=C3=BCsterhus?= Date: Tue, 18 Jun 2024 13:44:56 +0200 Subject: [PATCH] gen_stub: Intern the parameter name string for named arguments in internal attributes This is necessary because `zend_get_attribute_object()` will use the persistent string with the parameter name as the index for a newly created non-persistent HashTable, which is not legal. As parameter names are expected to be short-ish, reasonably common terms and need to sit around in memory anyways, we might as well make them an interned string, circumstepping the issue without needing to duplicate the parameter name into a non-persistent string. --- build/gen_stub.php | 2 +- ext/zend_test/test.c | 22 ++++++++ ext/zend_test/test.stub.php | 10 ++++ ext/zend_test/test_arginfo.h | 53 ++++++++++++++++++- .../tests/attribute-named-parameter.phpt | 22 ++++++++ 5 files changed, 107 insertions(+), 2 deletions(-) create mode 100644 ext/zend_test/tests/attribute-named-parameter.phpt diff --git a/build/gen_stub.php b/build/gen_stub.php index b17fbda6bf12b..4efe0164c40e8 100755 --- a/build/gen_stub.php +++ b/build/gen_stub.php @@ -3124,7 +3124,7 @@ public function generateCode(string $invocation, string $nameSuffix, array $allC $code .= $value->initializeZval($zvalName); $code .= "\tZVAL_COPY_VALUE(&attribute_{$escapedAttributeName}_{$nameSuffix}->args[$i].value, &$zvalName);\n"; if ($arg->name) { - $code .= "\tattribute_{$escapedAttributeName}_{$nameSuffix}->args[$i].name = zend_string_init(\"{$arg->name->name}\", sizeof(\"{$arg->name->name}\") - 1, 1);\n"; + $code .= "\tattribute_{$escapedAttributeName}_{$nameSuffix}->args[$i].name = zend_string_init_interned(\"{$arg->name->name}\", sizeof(\"{$arg->name->name}\") - 1, 1);\n"; } } return $code; diff --git a/ext/zend_test/test.c b/ext/zend_test/test.c index 78adc05ccca5e..e1ffb36cbe4f2 100644 --- a/ext/zend_test/test.c +++ b/ext/zend_test/test.c @@ -58,6 +58,7 @@ static zend_class_entry *zend_test_attribute; static zend_class_entry *zend_test_repeatable_attribute; static zend_class_entry *zend_test_parameter_attribute; static zend_class_entry *zend_test_property_attribute; +static zend_class_entry *zend_test_attribute_with_arguments; static zend_class_entry *zend_test_class_with_method_with_parameter_attribute; static zend_class_entry *zend_test_child_class_with_method_with_parameter_attribute; static zend_class_entry *zend_test_class_with_property_attribute; @@ -575,6 +576,11 @@ static ZEND_FUNCTION(zend_test_parameter_with_attribute) RETURN_LONG(1); } +static ZEND_FUNCTION(zend_test_attribute_with_named_argument) +{ + ZEND_PARSE_PARAMETERS_NONE(); +} + #ifdef ZEND_CHECK_STACK_LIMIT static ZEND_FUNCTION(zend_test_zend_call_stack_get) { @@ -989,6 +995,19 @@ static ZEND_METHOD(ZendTestPropertyAttribute, __construct) ZVAL_STR_COPY(OBJ_PROP_NUM(Z_OBJ_P(ZEND_THIS), 0), parameter); } +static ZEND_METHOD(ZendTestAttributeWithArguments, __construct) +{ + zval *arg; + + ZEND_PARSE_PARAMETERS_START(1, 1) + Z_PARAM_ZVAL(arg) + ZEND_PARSE_PARAMETERS_END(); + + zend_string *property_name = zend_string_init("arg", strlen("arg"), 0); + zend_update_property_ex(zend_test_attribute_with_arguments, Z_OBJ_P(ZEND_THIS), property_name, arg); + zend_string_release(property_name); +} + static ZEND_METHOD(ZendTestClassWithMethodWithParameterAttribute, no_override) { zend_string *parameter; @@ -1217,6 +1236,9 @@ PHP_MINIT_FUNCTION(zend_test) zend_test_property_attribute = register_class_ZendTestPropertyAttribute(); zend_mark_internal_attribute(zend_test_property_attribute); + zend_test_attribute_with_arguments = register_class_ZendTestAttributeWithArguments(); + zend_mark_internal_attribute(zend_test_attribute_with_arguments); + zend_test_class_with_method_with_parameter_attribute = register_class_ZendTestClassWithMethodWithParameterAttribute(); zend_test_child_class_with_method_with_parameter_attribute = register_class_ZendTestChildClassWithMethodWithParameterAttribute(zend_test_class_with_method_with_parameter_attribute); diff --git a/ext/zend_test/test.stub.php b/ext/zend_test/test.stub.php index b7790a256e48e..3a888fef6897a 100644 --- a/ext/zend_test/test.stub.php +++ b/ext/zend_test/test.stub.php @@ -104,6 +104,13 @@ public function testMethod(): bool {} final class ZendTestAttribute { } + #[Attribute(Attribute::TARGET_ALL)] + final class ZendTestAttributeWithArguments { + public readonly mixed $arg; + + public function __construct(mixed $arg) {} + } + #[Attribute(Attribute::TARGET_ALL|Attribute::IS_REPEATABLE)] final class ZendTestRepeatableAttribute { } @@ -243,6 +250,9 @@ function zend_test_parameter_with_attribute( string $parameter ): int {} + #[ZendTestAttributeWithArguments(arg: "foo")] + function zend_test_attribute_with_named_argument(): void {} + function zend_get_current_func_name(): string {} function zend_call_method(object|string $obj_or_class, string $method, mixed $arg1 = UNKNOWN, mixed $arg2 = UNKNOWN): mixed {} diff --git a/ext/zend_test/test_arginfo.h b/ext/zend_test/test_arginfo.h index 0bd79a13cf7b5..d78e42a92e005 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: 810a94e09724a27d46c99495593da999cfc46243 */ + * Stub hash: b7e8a9c27def16e8691883bea7837def1f717ab8 */ ZEND_STATIC_ASSERT(PHP_VERSION_ID >= 80000, "test_arginfo.h only supports PHP version ID 80000 or newer, " "but it is included on an older PHP version"); @@ -90,6 +90,8 @@ ZEND_BEGIN_ARG_WITH_RETURN_TYPE_INFO_EX(arginfo_zend_test_parameter_with_attribu ZEND_ARG_TYPE_INFO(0, parameter, IS_STRING, 0) ZEND_END_ARG_INFO() +#define arginfo_zend_test_attribute_with_named_argument arginfo_zend_test_void_return + ZEND_BEGIN_ARG_WITH_RETURN_TYPE_INFO_EX(arginfo_zend_get_current_func_name, 0, 0, IS_STRING, 0) ZEND_END_ARG_INFO() @@ -208,6 +210,10 @@ ZEND_END_ARG_INFO() #define arginfo_class__ZendTestTrait_testMethod arginfo_zend_test_is_pcre_bundled +ZEND_BEGIN_ARG_INFO_EX(arginfo_class_ZendTestAttributeWithArguments___construct, 0, 0, 1) + ZEND_ARG_TYPE_INFO(0, arg, IS_MIXED, 0) +ZEND_END_ARG_INFO() + ZEND_BEGIN_ARG_INFO_EX(arginfo_class_ZendTestParameterAttribute___construct, 0, 0, 1) ZEND_ARG_TYPE_INFO(0, parameter, IS_STRING, 0) ZEND_END_ARG_INFO() @@ -262,6 +268,7 @@ static ZEND_FUNCTION(zend_weakmap_remove); static ZEND_FUNCTION(zend_weakmap_dump); static ZEND_FUNCTION(zend_get_unit_enum); static ZEND_FUNCTION(zend_test_parameter_with_attribute); +static ZEND_FUNCTION(zend_test_attribute_with_named_argument); static ZEND_FUNCTION(zend_get_current_func_name); static ZEND_FUNCTION(zend_call_method); static ZEND_FUNCTION(zend_object_init_with_constructor); @@ -302,6 +309,7 @@ static ZEND_METHOD(_ZendTestMagicCall, __call); static ZEND_METHOD(_ZendTestChildClass, returnsThrowable); static ZEND_METHOD(ZendAttributeTest, testMethod); static ZEND_METHOD(_ZendTestTrait, testMethod); +static ZEND_METHOD(ZendTestAttributeWithArguments, __construct); static ZEND_METHOD(ZendTestParameterAttribute, __construct); static ZEND_METHOD(ZendTestPropertyAttribute, __construct); static ZEND_METHOD(ZendTestClassWithMethodWithParameterAttribute, no_override); @@ -359,6 +367,7 @@ static const zend_function_entry ext_functions[] = { ZEND_FE(zend_weakmap_dump, arginfo_zend_weakmap_dump) ZEND_FE(zend_get_unit_enum, arginfo_zend_get_unit_enum) ZEND_FE(zend_test_parameter_with_attribute, arginfo_zend_test_parameter_with_attribute) + ZEND_FE(zend_test_attribute_with_named_argument, arginfo_zend_test_attribute_with_named_argument) ZEND_FE(zend_get_current_func_name, arginfo_zend_get_current_func_name) ZEND_FE(zend_call_method, arginfo_zend_call_method) ZEND_FE(zend_object_init_with_constructor, arginfo_zend_object_init_with_constructor) @@ -466,6 +475,11 @@ static const zend_function_entry class_ZendTestAttribute_methods[] = { ZEND_FE_END }; +static const zend_function_entry class_ZendTestAttributeWithArguments_methods[] = { + ZEND_ME(ZendTestAttributeWithArguments, __construct, arginfo_class_ZendTestAttributeWithArguments___construct, ZEND_ACC_PUBLIC) + ZEND_FE_END +}; + static const zend_function_entry class_ZendTestRepeatableAttribute_methods[] = { ZEND_FE_END }; @@ -553,6 +567,15 @@ static void register_test_symbols(int module_number) zend_string *attribute_ZendTestParameterAttribute_func_zend_test_parameter_with_attribute_arg0_0_arg0_str = zend_string_init("value1", strlen("value1"), 1); ZVAL_STR(&attribute_ZendTestParameterAttribute_func_zend_test_parameter_with_attribute_arg0_0_arg0, attribute_ZendTestParameterAttribute_func_zend_test_parameter_with_attribute_arg0_0_arg0_str); ZVAL_COPY_VALUE(&attribute_ZendTestParameterAttribute_func_zend_test_parameter_with_attribute_arg0_0->args[0].value, &attribute_ZendTestParameterAttribute_func_zend_test_parameter_with_attribute_arg0_0_arg0); + + zend_string *attribute_name_ZendTestAttributeWithArguments_func_zend_test_attribute_with_named_argument_0 = zend_string_init_interned("ZendTestAttributeWithArguments", sizeof("ZendTestAttributeWithArguments") - 1, 1); + zend_attribute *attribute_ZendTestAttributeWithArguments_func_zend_test_attribute_with_named_argument_0 = zend_add_function_attribute(zend_hash_str_find_ptr(CG(function_table), "zend_test_attribute_with_named_argument", sizeof("zend_test_attribute_with_named_argument") - 1), attribute_name_ZendTestAttributeWithArguments_func_zend_test_attribute_with_named_argument_0, 1); + zend_string_release(attribute_name_ZendTestAttributeWithArguments_func_zend_test_attribute_with_named_argument_0); + zval attribute_ZendTestAttributeWithArguments_func_zend_test_attribute_with_named_argument_0_arg0; + zend_string *attribute_ZendTestAttributeWithArguments_func_zend_test_attribute_with_named_argument_0_arg0_str = zend_string_init("foo", strlen("foo"), 1); + ZVAL_STR(&attribute_ZendTestAttributeWithArguments_func_zend_test_attribute_with_named_argument_0_arg0, attribute_ZendTestAttributeWithArguments_func_zend_test_attribute_with_named_argument_0_arg0_str); + ZVAL_COPY_VALUE(&attribute_ZendTestAttributeWithArguments_func_zend_test_attribute_with_named_argument_0->args[0].value, &attribute_ZendTestAttributeWithArguments_func_zend_test_attribute_with_named_argument_0_arg0); + attribute_ZendTestAttributeWithArguments_func_zend_test_attribute_with_named_argument_0->args[0].name = zend_string_init_interned("arg", sizeof("arg") - 1, 1); } static zend_class_entry *register_class__ZendTestInterface(void) @@ -802,6 +825,34 @@ static zend_class_entry *register_class_ZendTestAttribute(void) return class_entry; } +static zend_class_entry *register_class_ZendTestAttributeWithArguments(void) +{ + zend_class_entry ce, *class_entry; + + INIT_CLASS_ENTRY(ce, "ZendTestAttributeWithArguments", class_ZendTestAttributeWithArguments_methods); + class_entry = zend_register_internal_class_ex(&ce, NULL); + class_entry->ce_flags |= ZEND_ACC_FINAL; + + zval property_arg_default_value; + ZVAL_UNDEF(&property_arg_default_value); + zend_string *property_arg_name = zend_string_init("arg", sizeof("arg") - 1, 1); +#if (PHP_VERSION_ID >= 80100) + zend_declare_typed_property(class_entry, property_arg_name, &property_arg_default_value, ZEND_ACC_PUBLIC|ZEND_ACC_READONLY, NULL, (zend_type) ZEND_TYPE_INIT_MASK(MAY_BE_ANY)); +#elif (PHP_VERSION_ID >= 80000) + zend_declare_typed_property(class_entry, property_arg_name, &property_arg_default_value, ZEND_ACC_PUBLIC, NULL, (zend_type) ZEND_TYPE_INIT_MASK(MAY_BE_ANY)); +#endif + zend_string_release(property_arg_name); + + zend_string *attribute_name_Attribute_class_ZendTestAttributeWithArguments_0 = zend_string_init_interned("Attribute", sizeof("Attribute") - 1, 1); + zend_attribute *attribute_Attribute_class_ZendTestAttributeWithArguments_0 = zend_add_class_attribute(class_entry, attribute_name_Attribute_class_ZendTestAttributeWithArguments_0, 1); + zend_string_release(attribute_name_Attribute_class_ZendTestAttributeWithArguments_0); + zval attribute_Attribute_class_ZendTestAttributeWithArguments_0_arg0; + ZVAL_LONG(&attribute_Attribute_class_ZendTestAttributeWithArguments_0_arg0, ZEND_ATTRIBUTE_TARGET_ALL); + ZVAL_COPY_VALUE(&attribute_Attribute_class_ZendTestAttributeWithArguments_0->args[0].value, &attribute_Attribute_class_ZendTestAttributeWithArguments_0_arg0); + + return class_entry; +} + static zend_class_entry *register_class_ZendTestRepeatableAttribute(void) { zend_class_entry ce, *class_entry; diff --git a/ext/zend_test/tests/attribute-named-parameter.phpt b/ext/zend_test/tests/attribute-named-parameter.phpt new file mode 100644 index 0000000000000..5c970db26219c --- /dev/null +++ b/ext/zend_test/tests/attribute-named-parameter.phpt @@ -0,0 +1,22 @@ +--TEST-- +Verify that attributes for internal functions correctly support named arguments. +--EXTENSIONS-- +zend_test +--FILE-- +getAttributes()[0]; +var_dump($attribute->getArguments()); +var_dump($attribute->newInstance()); + +?> +--EXPECTF-- +array(1) { + ["arg"]=> + string(3) "foo" +} +object(ZendTestAttributeWithArguments)#3 (1) { + ["arg"]=> + string(3) "foo" +}