From 6ce5c27bc7e1b56a652f9311c5b15b825f46fabf Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Martin=20Schr=C3=B6der?= Date: Sat, 2 May 2020 07:42:40 +0200 Subject: [PATCH 1/3] Enable external linkage. Fixed comments. --- Zend/zend_attributes.c | 21 ++++++++++----- Zend/zend_attributes.h | 45 ++++++++++++++++++++------------- Zend/zend_compile.c | 7 ++--- ext/reflection/php_reflection.c | 20 +++++++-------- 4 files changed, 56 insertions(+), 37 deletions(-) diff --git a/Zend/zend_attributes.c b/Zend/zend_attributes.c index 1b0453d3316f3..f882d00ec69d8 100644 --- a/Zend/zend_attributes.c +++ b/Zend/zend_attributes.c @@ -2,6 +2,11 @@ #include "zend_API.h" #include "zend_attributes.h" +ZEND_API zend_class_entry *zend_ce_php_attribute; +ZEND_API zend_class_entry *zend_ce_php_compiler_attribute; + +static HashTable internal_validators; + void zend_attribute_validate_phpattribute(zend_attribute *attr, int target) { if (target != ZEND_ATTRIBUTE_TARGET_CLASS) { @@ -14,9 +19,9 @@ void zend_attribute_validate_phpcompilerattribute(zend_attribute *attr, int targ zend_error(E_COMPILE_ERROR, "The PhpCompilerAttribute can only be used by internal classes, use PhpAttribute instead"); } -void zend_register_attribute_ce(void) +ZEND_API void zend_register_attribute_ce(void) { - zend_hash_init(&zend_attributes_internal_validators, 8, NULL, NULL, 1); + zend_hash_init(&internal_validators, 8, NULL, NULL, 1); zend_class_entry ce; @@ -33,11 +38,15 @@ void zend_register_attribute_ce(void) zend_compiler_attribute_register(zend_ce_php_compiler_attribute, zend_attribute_validate_phpcompilerattribute); } -void zend_compiler_attribute_register(zend_class_entry *ce, zend_attributes_internal_validator validator) +ZEND_API zend_attributes_internal_validator zend_attribute_get_validator(zend_string *lcname) { - zend_string *attribute_name = zend_string_tolower_ex(ce->name, 1); + return zend_hash_find_ptr(&internal_validators, lcname); +} - zend_hash_update_ptr(&zend_attributes_internal_validators, attribute_name, validator); +ZEND_API void zend_compiler_attribute_register(zend_class_entry *ce, zend_attributes_internal_validator validator) +{ + zend_string *lcname = zend_string_tolower_ex(ce->name, 1); - zend_string_release(attribute_name); + zend_hash_update_ptr(&internal_validators, lcname, validator); + zend_string_release(lcname); } diff --git a/Zend/zend_attributes.h b/Zend/zend_attributes.h index 102ab9317aa67..f55d9e9aa53d6 100644 --- a/Zend/zend_attributes.h +++ b/Zend/zend_attributes.h @@ -9,11 +9,13 @@ #define ZEND_ATTRIBUTE_TARGET_PARAMETER 32 #define ZEND_ATTRIBUTE_TARGET_ALL 63 -zend_class_entry *zend_ce_php_attribute; -zend_class_entry *zend_ce_php_compiler_attribute; - #define ZEND_ATTRIBUTE_SIZE(argc) (sizeof(zend_attribute) + sizeof(zval) * (argc) - sizeof(zval)) +BEGIN_EXTERN_C() + +extern ZEND_API zend_class_entry *zend_ce_php_attribute; +extern ZEND_API zend_class_entry *zend_ce_php_compiler_attribute; + typedef struct _zend_attribute { zend_string *name; zend_string *lcname; @@ -22,7 +24,9 @@ typedef struct _zend_attribute { zval argv[1]; } zend_attribute; -static zend_always_inline void zend_attribute_release(zend_attribute *attr) +typedef void (*zend_attributes_internal_validator)(zend_attribute *attr, int target); + +static zend_always_inline void zend_attribute_free(zend_attribute *attr) { uint32_t i; @@ -36,37 +40,42 @@ static zend_always_inline void zend_attribute_release(zend_attribute *attr) efree(attr); } -static zend_always_inline zend_bool zend_has_attribute(HashTable *attributes, zend_string *name, uint32_t offset) +static zend_always_inline zend_attribute *zend_get_attribute(HashTable *attributes, zend_string *name, uint32_t offset) { if (attributes) { zend_attribute *attr; ZEND_HASH_FOREACH_PTR(attributes, attr) { if (attr->offset == offset && zend_string_equals(attr->lcname, name)) { - return 1; + return attr; } } ZEND_HASH_FOREACH_END(); } - return 0; + return NULL; } -static zend_always_inline zend_bool zend_has_attribute_str(HashTable *attributes, const char *str, size_t len, uint32_t offset) +static zend_always_inline zend_attribute *zend_get_attribute_str(HashTable *attributes, const char *str, size_t len, uint32_t offset) { - zend_bool result = 0; - if (attributes) { - zend_string *name = zend_string_init(str, len, 0); - result = zend_has_attribute(attributes, name, offset); - zend_string_release(name); + zend_attribute *attr; + + ZEND_HASH_FOREACH_PTR(attributes, attr) { + if (attr->offset == offset && ZSTR_LEN(attr->lcname) == len) { + if (0 == memcmp(ZSTR_VAL(attr->lcname), str, len)) { + return attr; + } + } + } ZEND_HASH_FOREACH_END(); } - return result; + return NULL; } -typedef void (*zend_attributes_internal_validator)(zend_attribute *attr, int target); -HashTable zend_attributes_internal_validators; +ZEND_API void zend_compiler_attribute_register(zend_class_entry *ce, zend_attributes_internal_validator validator); +ZEND_API zend_attributes_internal_validator zend_attribute_get_validator(zend_string *lcname); +ZEND_API void zend_register_attribute_ce(void); + +END_EXTERN_C() -void zend_compiler_attribute_register(zend_class_entry *ce, zend_attributes_internal_validator validator); -void zend_register_attribute_ce(void); #endif diff --git a/Zend/zend_compile.c b/Zend/zend_compile.c index 66f6b313daa12..1501df9f929a1 100644 --- a/Zend/zend_compile.c +++ b/Zend/zend_compile.c @@ -5743,10 +5743,11 @@ static zend_attribute *zend_compile_attribute(zend_ast *ast, uint32_t offset) /* } /* }}} */ -static void attribute_ptr_dtor(zval *v) +static void attribute_ptr_dtor(zval *v) /* {{{ */ { - zend_attribute_release((zend_attribute *) Z_PTR_P(v)); + zend_attribute_free((zend_attribute *) Z_PTR_P(v)); } +/* }}} */ static zend_always_inline HashTable *create_attribute_array(uint32_t size) /* {{{ */ { @@ -5772,7 +5773,7 @@ static void zend_compile_attributes(HashTable *attributes, zend_ast *ast, uint32 zend_attribute *attr = zend_compile_attribute(list->child[i], offset); // Validate internal attribute - zend_attributes_internal_validator validator = zend_hash_find_ptr(&zend_attributes_internal_validators, attr->lcname); + zend_attributes_internal_validator validator = zend_attribute_get_validator(attr->lcname); if (validator != NULL) { validator(attr, target); diff --git a/ext/reflection/php_reflection.c b/ext/reflection/php_reflection.c index 3f2f4c2413011..6cd48ec9dec3b 100644 --- a/ext/reflection/php_reflection.c +++ b/ext/reflection/php_reflection.c @@ -1765,7 +1765,7 @@ ZEND_METHOD(ReflectionFunctionAbstract, getDocComment) } /* }}} */ -/* {{{ proto public array ReflectionFunction::getAttributes() +/* {{{ proto public array ReflectionFunction::getAttributes([ string name, int flags ]) Returns the attributes of this function */ ZEND_METHOD(ReflectionFunctionAbstract, getAttributes) { @@ -2711,7 +2711,7 @@ ZEND_METHOD(ReflectionParameter, canBePassedByValue) } /* }}} */ -/* {{{ proto public bool ReflectionParameter::getAttributes(?string $name = null) +/* {{{ proto public array ReflectionParameter::getAttributes([ string name, int flags ]) Get parameter attributes. */ ZEND_METHOD(ReflectionParameter, getAttributes) { @@ -2731,7 +2731,7 @@ ZEND_METHOD(ReflectionParameter, getAttributes) reflect_attributes(INTERNAL_FUNCTION_PARAM_PASSTHRU, attributes, param->offset + 1, scope); } -/* {{{ proto public bool ReflectionParameter::getPosition() +/* {{{ proto public int ReflectionParameter::getPosition() Returns whether this parameter is an optional parameter */ ZEND_METHOD(ReflectionParameter, getPosition) { @@ -3786,7 +3786,7 @@ ZEND_METHOD(ReflectionClassConstant, getDocComment) } /* }}} */ -/* {{{ proto public array ReflectionClassConstant::getAttributes() +/* {{{ proto public array ReflectionClassConstant::getAttributes([ string name, int flags ]) Returns the attributes of this constant */ ZEND_METHOD(ReflectionClassConstant, getAttributes) { @@ -4174,7 +4174,7 @@ ZEND_METHOD(ReflectionClass, getDocComment) } /* }}} */ -/* {{{ proto public array ReflectionClass::getAttributes() +/* {{{ proto public array ReflectionClass::getAttributes([ string name, int flags ]) Returns the attributes for this class */ ZEND_METHOD(ReflectionClass, getAttributes) { @@ -5695,7 +5695,7 @@ ZEND_METHOD(ReflectionProperty, getDocComment) } /* }}} */ -/* {{{ proto public array ReflectionProperty::getAttributes() +/* {{{ proto public array ReflectionProperty::getAttributes([ string name, int flags ]) Returns the attributes of this property */ ZEND_METHOD(ReflectionProperty, getAttributes) { @@ -6467,7 +6467,7 @@ static zend_always_inline int import_attribute_value(zval *ret, zval *val, zend_ } /* }}} */ -/* {{{ proto public string ReflectionAttribute::getArguments() +/* {{{ proto public array ReflectionAttribute::getArguments() * Returns the arguments passed to the attribute */ ZEND_METHOD(ReflectionAttribute, getArguments) { @@ -6562,7 +6562,7 @@ static void attribute_ctor_cleanup(zval *obj, zval *args, uint32_t argc) /* {{{ } /* }}} */ -/* {{{ proto public string ReflectionAttribute::newInstance() +/* {{{ proto public object ReflectionAttribute::newInstance() * Returns the attribute as an object */ ZEND_METHOD(ReflectionAttribute, newInstance) { @@ -6587,10 +6587,10 @@ ZEND_METHOD(ReflectionAttribute, newInstance) RETURN_THROWS(); } - if (ce->type == ZEND_USER_CLASS && !zend_has_attribute_str(ce->info.user.attributes, ZEND_STRL("phpattribute"), 0)) { + if (ce->type == ZEND_USER_CLASS && !zend_get_attribute_str(ce->info.user.attributes, ZEND_STRL("phpattribute"), 0)) { zend_throw_error(NULL, "Attempting to use class '%s' as attribute that does not have <>.", ZSTR_VAL(attr->data->name)); RETURN_THROWS(); - } else if (ce->type == ZEND_INTERNAL_CLASS && zend_hash_exists(&zend_attributes_internal_validators, attr->data->lcname) == 0) { + } else if (ce->type == ZEND_INTERNAL_CLASS && !zend_attribute_get_validator(attr->data->lcname)) { zend_throw_error(NULL, "Attempting to use internal class '%s' as attribute that does not have <>.", ZSTR_VAL(attr->data->name)); RETURN_THROWS(); } From bd4eed02f93349ec32ef69484e9925398ea86b28 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Martin=20Schr=C3=B6der?= Date: Sat, 2 May 2020 08:22:27 +0200 Subject: [PATCH 2/3] Implemented AST export of attribute nodes. --- Zend/tests/attributes/012-ast-export.phpt | 54 ++++++++++++++++++++++ Zend/zend_ast.c | 55 ++++++++++++++++++++++- 2 files changed, 108 insertions(+), 1 deletion(-) create mode 100644 Zend/tests/attributes/012-ast-export.phpt diff --git a/Zend/tests/attributes/012-ast-export.phpt b/Zend/tests/attributes/012-ast-export.phpt new file mode 100644 index 0000000000000..47176b1b0d02d --- /dev/null +++ b/Zend/tests/attributes/012-ast-export.phpt @@ -0,0 +1,54 @@ +--TEST-- +Attributes AST can be exported. +--FILE-- +><> function ($a, <> $b) { })); + +assert(0 && ($a = <> fn () => 1)); + +assert(0 && ($a = new <> class() { + <><> const FOO = 'foo'; + <> public $x; + <> function a() { } +})); + +assert(0 && ($a = function () { + <> class Test1 { } + <> interface Test2 { } + <> trait Test3 { } +})); + +?> +--EXPECTF-- +Warning: assert(): assert(0 && ($a = <> <> function ($a, <> $b) { +})) failed in %s on line %d + +Warning: assert(): assert(0 && ($a = <> fn() => 1)) failed in %s on line %d + +Warning: assert(): assert(0 && ($a = new <> class { + <> + <> + const FOO = 'foo'; + <> + public $x; + <> + public function a() { + } + +})) failed in %s on line %d + +Warning: assert(): assert(0 && ($a = function () { + <> + class Test1 { + } + + <> + interface Test2 { + } + + <> + trait Test3 { + } + +})) failed in %s on line %d diff --git a/Zend/zend_ast.c b/Zend/zend_ast.c index 6c3f9870207aa..7bd732ea68088 100644 --- a/Zend/zend_ast.c +++ b/Zend/zend_ast.c @@ -1317,6 +1317,41 @@ static ZEND_COLD void zend_ast_export_class_no_header(smart_str *str, zend_ast_d smart_str_appends(str, "}"); } +static ZEND_COLD void zend_ast_export_attributes(smart_str *str, zend_ast *ast, int indent, zend_bool newlines) { + zend_ast_list *list = zend_ast_get_list(ast); + uint32_t i; + + for (i = 0; i < list->children; i++) { + zend_ast *attr = list->child[i]; + + smart_str_appends(str, "<<"); + smart_str_append(str, zend_ast_get_str(attr->child[0])); + + if (attr->child[1]) { + zend_ast_list *args = zend_ast_get_list(attr->child[1]); + uint32_t j; + + smart_str_appendc(str, '('); + for (j = 0; j < args->children; j++) { + if (j) { + smart_str_appends(str, ", "); + } + zend_ast_export_ex(str, args->child[j], 0, indent); + } + smart_str_appendc(str, ')'); + } + + smart_str_appends(str, ">>"); + + if (newlines) { + smart_str_appendc(str, '\n'); + zend_ast_export_indent(str, indent); + } else { + smart_str_appendc(str, ' '); + } + } +} + static ZEND_COLD void zend_ast_export_type(smart_str *str, zend_ast *ast, int indent) { if (ast->kind == ZEND_AST_TYPE_UNION) { zend_ast_list *list = zend_ast_get_list(ast); @@ -1410,6 +1445,10 @@ static ZEND_COLD void zend_ast_export_ex(smart_str *str, zend_ast *ast, int prio case ZEND_AST_ARROW_FUNC: case ZEND_AST_METHOD: decl = (zend_ast_decl *) ast; + if (decl->attributes) { + zend_bool newlines = (ast->kind == ZEND_AST_CLOSURE || ast->kind == ZEND_AST_ARROW_FUNC) ? 0 : 1; + zend_ast_export_attributes(str, decl->attributes, indent, newlines); + } if (decl->flags & ZEND_ACC_PUBLIC) { smart_str_appends(str, "public "); } else if (decl->flags & ZEND_ACC_PROTECTED) { @@ -1466,6 +1505,9 @@ static ZEND_COLD void zend_ast_export_ex(smart_str *str, zend_ast *ast, int prio break; case ZEND_AST_CLASS: decl = (zend_ast_decl *) ast; + if (decl->attributes) { + zend_ast_export_attributes(str, decl->attributes, indent, 1); + } if (decl->flags & ZEND_ACC_INTERFACE) { smart_str_appends(str, "interface "); } else if (decl->flags & ZEND_ACC_TRAIT) { @@ -1521,6 +1563,9 @@ static ZEND_COLD void zend_ast_export_ex(smart_str *str, zend_ast *ast, int prio zend_ast *type_ast = ast->child[0]; zend_ast *prop_ast = ast->child[1]; + if (ast->child[2]) { + zend_ast_export_attributes(str, ast->child[2], indent, 1); + } if (ast->attr & ZEND_ACC_PUBLIC) { smart_str_appends(str, "public "); } else if (ast->attr & ZEND_ACC_PROTECTED) { @@ -1546,6 +1591,7 @@ static ZEND_COLD void zend_ast_export_ex(smart_str *str, zend_ast *ast, int prio smart_str_appends(str, "const "); goto simple_list; case ZEND_AST_CLASS_CONST_DECL_ATTRIBUTES: + zend_ast_export_attributes(str, ast->child[1], indent, 1); zend_ast_export_ex(str, ast->child[0], 0, indent); break; case ZEND_AST_NAME_LIST: @@ -1789,13 +1835,17 @@ static ZEND_COLD void zend_ast_export_ex(smart_str *str, zend_ast *ast, int prio case ZEND_AST_NEW: smart_str_appends(str, "new "); if (ast->child[0]->kind == ZEND_AST_CLASS) { + zend_ast_decl *decl = (zend_ast_decl *) ast->child[0]; + if (decl->attributes) { + zend_ast_export_attributes(str, decl->attributes, indent, 0); + } smart_str_appends(str, "class"); if (zend_ast_get_list(ast->child[1])->children) { smart_str_appendc(str, '('); zend_ast_export_ex(str, ast->child[1], 0, indent); smart_str_appendc(str, ')'); } - zend_ast_export_class_no_header(str, (zend_ast_decl *) ast->child[0], indent); + zend_ast_export_class_no_header(str, decl, indent); } else { zend_ast_export_ns_name(str, ast->child[0], 0, indent); smart_str_appendc(str, '('); @@ -2006,6 +2056,9 @@ static ZEND_COLD void zend_ast_export_ex(smart_str *str, zend_ast *ast, int prio zend_ast_export_indent(str, indent); break; case ZEND_AST_PARAM: + if (ast->child[3]) { + zend_ast_export_attributes(str, ast->child[3], indent, 0); + } if (ast->child[0]) { zend_ast_export_type(str, ast->child[0], indent); smart_str_appendc(str, ' '); From a9d94bb4600fde2d8b960f1957a4cfdd104cfb6f Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Martin=20Schr=C3=B6der?= Date: Sat, 2 May 2020 08:37:33 +0200 Subject: [PATCH 3/3] Removed ZEND_API from register ce function. --- Zend/zend_attributes.c | 28 ++++++++++++++-------------- Zend/zend_attributes.h | 3 ++- 2 files changed, 16 insertions(+), 15 deletions(-) diff --git a/Zend/zend_attributes.c b/Zend/zend_attributes.c index f882d00ec69d8..86ef74be1777b 100644 --- a/Zend/zend_attributes.c +++ b/Zend/zend_attributes.c @@ -19,7 +19,20 @@ void zend_attribute_validate_phpcompilerattribute(zend_attribute *attr, int targ zend_error(E_COMPILE_ERROR, "The PhpCompilerAttribute can only be used by internal classes, use PhpAttribute instead"); } -ZEND_API void zend_register_attribute_ce(void) +ZEND_API zend_attributes_internal_validator zend_attribute_get_validator(zend_string *lcname) +{ + return zend_hash_find_ptr(&internal_validators, lcname); +} + +ZEND_API void zend_compiler_attribute_register(zend_class_entry *ce, zend_attributes_internal_validator validator) +{ + zend_string *lcname = zend_string_tolower_ex(ce->name, 1); + + zend_hash_update_ptr(&internal_validators, lcname, validator); + zend_string_release(lcname); +} + +void zend_register_attribute_ce(void) { zend_hash_init(&internal_validators, 8, NULL, NULL, 1); @@ -37,16 +50,3 @@ ZEND_API void zend_register_attribute_ce(void) zend_compiler_attribute_register(zend_ce_php_compiler_attribute, zend_attribute_validate_phpcompilerattribute); } - -ZEND_API zend_attributes_internal_validator zend_attribute_get_validator(zend_string *lcname) -{ - return zend_hash_find_ptr(&internal_validators, lcname); -} - -ZEND_API void zend_compiler_attribute_register(zend_class_entry *ce, zend_attributes_internal_validator validator) -{ - zend_string *lcname = zend_string_tolower_ex(ce->name, 1); - - zend_hash_update_ptr(&internal_validators, lcname, validator); - zend_string_release(lcname); -} diff --git a/Zend/zend_attributes.h b/Zend/zend_attributes.h index f55d9e9aa53d6..19c32416acfaa 100644 --- a/Zend/zend_attributes.h +++ b/Zend/zend_attributes.h @@ -74,7 +74,8 @@ static zend_always_inline zend_attribute *zend_get_attribute_str(HashTable *attr ZEND_API void zend_compiler_attribute_register(zend_class_entry *ce, zend_attributes_internal_validator validator); ZEND_API zend_attributes_internal_validator zend_attribute_get_validator(zend_string *lcname); -ZEND_API void zend_register_attribute_ce(void); + +void zend_register_attribute_ce(void); END_EXTERN_C()