Skip to content

Use attribute validator for assigning flags #19465

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Open
wants to merge 2 commits into
base: master
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
54 changes: 51 additions & 3 deletions Zend/zend_attributes.c
Original file line number Diff line number Diff line change
Expand Up @@ -70,7 +70,7 @@ uint32_t zend_attribute_attribute_get_flags(zend_attribute *attr, zend_class_ent
}

static void validate_allow_dynamic_properties(
zend_attribute *attr, uint32_t target, zend_class_entry *scope)
zend_attribute *attr, uint32_t target_type, zend_class_entry *scope, void *target, uint32_t offset)
{
if (scope->ce_flags & ZEND_ACC_TRAIT) {
zend_error_noreturn(E_ERROR, "Cannot apply #[\\AllowDynamicProperties] to trait %s",
Expand All @@ -96,7 +96,7 @@ static void validate_allow_dynamic_properties(
}

static void validate_attribute(
zend_attribute *attr, uint32_t target, zend_class_entry *scope)
zend_attribute *attr, uint32_t target_type, zend_class_entry *scope, void *target, uint32_t offset)
{
const char *msg = NULL;
if (scope->ce_flags & ZEND_ACC_TRAIT) {
Expand All @@ -113,6 +113,51 @@ static void validate_attribute(
}
}

static void validate_override(
zend_attribute *attr, uint32_t target_type, zend_class_entry *scope, void *target, uint32_t offset)
{
if (target_type & ZEND_ATTRIBUTE_TARGET_METHOD) {
zend_op_array *op_array = target;
op_array->fn_flags |= ZEND_ACC_OVERRIDE;
} else {
ZEND_ASSERT(target_type & ZEND_ATTRIBUTE_TARGET_PROPERTY);
zend_property_info *prop_info = target;
prop_info->flags |= ZEND_ACC_OVERRIDE;
}
}

static void validate_deprecated(
zend_attribute *attr, uint32_t target_type, zend_class_entry *scope, void *target, uint32_t offset)
{
if (target_type & (ZEND_ATTRIBUTE_TARGET_FUNCTION|ZEND_ATTRIBUTE_TARGET_METHOD)) {
zend_op_array *op_array = target;
op_array->fn_flags |= ZEND_ACC_DEPRECATED;
} else if (target_type & (ZEND_ATTRIBUTE_TARGET_CLASS_CONST)) {
zend_class_constant *c = target;
ZEND_CLASS_CONST_FLAGS(c) |= ZEND_ACC_DEPRECATED;
/* For deprecated constants, we need to flag the zval for recursion
* detection. Make sure the zval is separated out of shm. */
scope->ce_flags |= ZEND_ACC_HAS_AST_CONSTANTS;
scope->ce_flags &= ~ZEND_ACC_CONSTANTS_UPDATED;
} else {
ZEND_ASSERT(target_type & ZEND_ATTRIBUTE_TARGET_CONST);
zend_constant *c = target;
ZEND_CONSTANT_SET_FLAGS(
c,
ZEND_CONSTANT_FLAGS(c) | CONST_DEPRECATED,
ZEND_CONSTANT_MODULE_NUMBER(c)
);
}
}

static void validate_no_discard(
zend_attribute *attr, uint32_t target_type, zend_class_entry *scope, void *target, uint32_t offset)
{
ZEND_ASSERT(target_type & (ZEND_ATTRIBUTE_TARGET_FUNCTION|ZEND_ATTRIBUTE_TARGET_METHOD));
zend_op_array *op_array = target;
op_array->fn_flags |= ZEND_ACC_NODISCARD;
}

ZEND_METHOD(Attribute, __construct)
{
zend_long flags = ZEND_ATTRIBUTE_TARGET_ALL;
Expand Down Expand Up @@ -560,13 +605,16 @@ void zend_register_attribute_ce(void)
zend_ce_sensitive_parameter_value->default_object_handlers = &attributes_object_handlers_sensitive_parameter_value;

zend_ce_override = register_class_Override();
zend_mark_internal_attribute(zend_ce_override);
attr = zend_mark_internal_attribute(zend_ce_override);
attr->validator = validate_override;

zend_ce_deprecated = register_class_Deprecated();
attr = zend_mark_internal_attribute(zend_ce_deprecated);
attr->validator = validate_deprecated;

zend_ce_nodiscard = register_class_NoDiscard();
attr = zend_mark_internal_attribute(zend_ce_nodiscard);
attr->validator = validate_no_discard;
}

void zend_attributes_shutdown(void)
Expand Down
3 changes: 2 additions & 1 deletion Zend/zend_attributes.h
Original file line number Diff line number Diff line change
Expand Up @@ -70,7 +70,8 @@ typedef struct _zend_attribute {
typedef struct _zend_internal_attribute {
zend_class_entry *ce;
uint32_t flags;
void (*validator)(zend_attribute *attr, uint32_t target, zend_class_entry *scope);
/* Parameter offsets start at 1, everything else uses 0. */
void (*validator)(zend_attribute *attr, uint32_t target_type, zend_class_entry *scope, void *target, uint32_t offset);
} zend_internal_attribute;

ZEND_API zend_attribute *zend_get_attribute(HashTable *attributes, zend_string *lcname);
Expand Down
97 changes: 21 additions & 76 deletions Zend/zend_compile.c
Original file line number Diff line number Diff line change
Expand Up @@ -7468,7 +7468,7 @@ static bool zend_is_valid_default_value(zend_type type, zval *value)
}

static void zend_compile_attributes(
HashTable **attributes, zend_ast *ast, uint32_t offset, uint32_t target, uint32_t promoted
HashTable **attributes, zend_ast *ast, uint32_t offset, uint32_t target_type, uint32_t promoted, void *target
) /* {{{ */ {
zend_attribute *attr;
zend_internal_attribute *config;
Expand Down Expand Up @@ -7502,7 +7502,7 @@ static void zend_compile_attributes(
zend_string_release(lcname);

/* Exclude internal attributes that do not match on promoted properties. */
if (config && !(target & (config->flags & ZEND_ATTRIBUTE_TARGET_ALL))) {
if (config && !(target_type & (config->flags & ZEND_ATTRIBUTE_TARGET_ALL))) {
if (promoted & (config->flags & ZEND_ATTRIBUTE_TARGET_ALL)) {
zend_string_release(name);
continue;
Expand Down Expand Up @@ -7560,8 +7560,8 @@ static void zend_compile_attributes(
continue;
}

if (!(target & (config->flags & ZEND_ATTRIBUTE_TARGET_ALL))) {
zend_string *location = zend_get_attribute_target_names(target);
if (!(target_type & (config->flags & ZEND_ATTRIBUTE_TARGET_ALL))) {
zend_string *location = zend_get_attribute_target_names(target_type);
zend_string *allowed = zend_get_attribute_target_names(config->flags);

zend_error_noreturn(E_ERROR, "Attribute \"%s\" cannot target %s (allowed targets: %s)",
Expand All @@ -7575,8 +7575,9 @@ static void zend_compile_attributes(
}
}

if (config->validator != NULL) {
config->validator(attr, target, CG(active_class_entry));
/* target is NULL for global constants at compile-time. Validator will be called at runtime. */
if (config->validator != NULL && target) {
config->validator(attr, target_type, CG(active_class_entry), target, offset);
}
} ZEND_HASH_FOREACH_END();
}
Expand Down Expand Up @@ -7773,13 +7774,6 @@ static void zend_compile_params(zend_ast *ast, zend_ast *return_type_ast, uint32
arg_info->name = zend_string_copy(name);
arg_info->type = (zend_type) ZEND_TYPE_INIT_NONE(0);

if (attributes_ast) {
zend_compile_attributes(
&op_array->attributes, attributes_ast, i + 1, ZEND_ATTRIBUTE_TARGET_PARAMETER,
is_promoted ? ZEND_ATTRIBUTE_TARGET_PROPERTY : 0
);
}

bool forced_allow_nullable = false;
if (type_ast) {
uint32_t default_type = *default_ast_ptr ? Z_TYPE(default_node.u.constant) : IS_UNDEF;
Expand Down Expand Up @@ -7835,6 +7829,13 @@ static void zend_compile_params(zend_ast *ast, zend_ast *return_type_ast, uint32
zval_ptr_dtor(&default_node.u.constant);
}

if (attributes_ast) {
zend_compile_attributes(
&op_array->attributes, attributes_ast, i + 1, ZEND_ATTRIBUTE_TARGET_PARAMETER,
is_promoted ? ZEND_ATTRIBUTE_TARGET_PROPERTY : 0, op_array
);
}

opline = zend_emit_op(NULL, opcode, NULL, &default_node);
SET_NODE(opline->result, &var_node);
opline->op1.num = i + 1;
Expand Down Expand Up @@ -7917,12 +7918,7 @@ static void zend_compile_params(zend_ast *ast, zend_ast *return_type_ast, uint32
}
if (attributes_ast) {
zend_compile_attributes(
&prop->attributes, attributes_ast, 0, ZEND_ATTRIBUTE_TARGET_PROPERTY, ZEND_ATTRIBUTE_TARGET_PARAMETER);

zend_attribute *override_attribute = zend_get_attribute_str(prop->attributes, "override", sizeof("override")-1);
if (override_attribute) {
prop->flags |= ZEND_ACC_OVERRIDE;
}
&prop->attributes, attributes_ast, 0, ZEND_ATTRIBUTE_TARGET_PROPERTY, ZEND_ATTRIBUTE_TARGET_PARAMETER, prop);
}
}
}
Expand Down Expand Up @@ -8429,37 +8425,7 @@ static zend_op_array *zend_compile_func_decl_ex(
target = ZEND_ATTRIBUTE_TARGET_METHOD;
}

zend_compile_attributes(&op_array->attributes, decl->child[4], 0, target, 0);

zend_attribute *override_attribute = zend_get_attribute_str(
op_array->attributes,
"override",
sizeof("override")-1
);

if (override_attribute) {
op_array->fn_flags |= ZEND_ACC_OVERRIDE;
}

zend_attribute *deprecated_attribute = zend_get_attribute_str(
op_array->attributes,
"deprecated",
sizeof("deprecated")-1
);

if (deprecated_attribute) {
op_array->fn_flags |= ZEND_ACC_DEPRECATED;
}

zend_attribute *nodiscard_attribute = zend_get_attribute_str(
op_array->attributes,
"nodiscard",
sizeof("nodiscard")-1
);

if (nodiscard_attribute) {
op_array->fn_flags |= ZEND_ACC_NODISCARD;
}
zend_compile_attributes(&op_array->attributes, decl->child[4], 0, target, 0, op_array);
}

/* Do not leak the class scope into free standing functions, even if they are dynamically
Expand Down Expand Up @@ -8901,12 +8867,7 @@ static void zend_compile_prop_decl(zend_ast *ast, zend_ast *type_ast, uint32_t f
}

if (attr_ast) {
zend_compile_attributes(&info->attributes, attr_ast, 0, ZEND_ATTRIBUTE_TARGET_PROPERTY, 0);

zend_attribute *override_attribute = zend_get_attribute_str(info->attributes, "override", sizeof("override")-1);
if (override_attribute) {
info->flags |= ZEND_ACC_OVERRIDE;
}
zend_compile_attributes(&info->attributes, attr_ast, 0, ZEND_ATTRIBUTE_TARGET_PROPERTY, 0, info);
}

CG(context).active_property_info_name = old_active_property_info_name;
Expand Down Expand Up @@ -8983,17 +8944,7 @@ static void zend_compile_class_const_decl(zend_ast *ast, uint32_t flags, zend_as
c = zend_declare_typed_class_constant(ce, name, &value_zv, flags, doc_comment, type);

if (attr_ast) {
zend_compile_attributes(&c->attributes, attr_ast, 0, ZEND_ATTRIBUTE_TARGET_CLASS_CONST, 0);

zend_attribute *deprecated = zend_get_attribute_str(c->attributes, "deprecated", sizeof("deprecated")-1);

if (deprecated) {
ZEND_CLASS_CONST_FLAGS(c) |= ZEND_ACC_DEPRECATED;
/* For deprecated constants, we need to flag the zval for recursion
* detection. Make sure the zval is separated out of shm. */
ce->ce_flags |= ZEND_ACC_HAS_AST_CONSTANTS;
ce->ce_flags &= ~ZEND_ACC_CONSTANTS_UPDATED;
}
zend_compile_attributes(&c->attributes, attr_ast, 0, ZEND_ATTRIBUTE_TARGET_CLASS_CONST, 0, c);
}
}
}
Expand Down Expand Up @@ -9263,7 +9214,7 @@ static void zend_compile_class_decl(znode *result, zend_ast *ast, bool toplevel)
CG(active_class_entry) = ce;

if (decl->child[3]) {
zend_compile_attributes(&ce->attributes, decl->child[3], 0, ZEND_ATTRIBUTE_TARGET_CLASS, 0);
zend_compile_attributes(&ce->attributes, decl->child[3], 0, ZEND_ATTRIBUTE_TARGET_CLASS, 0, ce);
}

if (implements_ast) {
Expand Down Expand Up @@ -9440,13 +9391,7 @@ static void zend_compile_enum_case(zend_ast *ast)

zend_ast *attr_ast = ast->child[3];
if (attr_ast) {
zend_compile_attributes(&c->attributes, attr_ast, 0, ZEND_ATTRIBUTE_TARGET_CLASS_CONST, 0);

zend_attribute *deprecated = zend_get_attribute_str(c->attributes, "deprecated", sizeof("deprecated")-1);

if (deprecated) {
ZEND_CLASS_CONST_FLAGS(c) |= ZEND_ACC_DEPRECATED;
}
zend_compile_attributes(&c->attributes, attr_ast, 0, ZEND_ATTRIBUTE_TARGET_CLASS_CONST, 0, c);
}
}

Expand Down Expand Up @@ -9660,7 +9605,7 @@ static void zend_compile_const_decl(zend_ast *ast) /* {{{ */
}

HashTable *attributes = NULL;
zend_compile_attributes(&attributes, list->child[1], 0, ZEND_ATTRIBUTE_TARGET_CONST, 0);
zend_compile_attributes(&attributes, list->child[1], 0, ZEND_ATTRIBUTE_TARGET_CONST, 0, NULL);

ZEND_ASSERT(last_op != NULL);
last_op->opcode = ZEND_DECLARE_ATTRIBUTED_CONST;
Expand Down
19 changes: 6 additions & 13 deletions Zend/zend_constants.c
Original file line number Diff line number Diff line change
Expand Up @@ -561,17 +561,10 @@ void zend_constant_add_attributes(zend_constant *c, HashTable *attributes) {
GC_TRY_ADDREF(attributes);
c->attributes = attributes;

zend_attribute *deprecated_attribute = zend_get_attribute_str(
c->attributes,
"deprecated",
strlen("deprecated")
);

if (deprecated_attribute) {
ZEND_CONSTANT_SET_FLAGS(
c,
ZEND_CONSTANT_FLAGS(c) | CONST_DEPRECATED,
ZEND_CONSTANT_MODULE_NUMBER(c)
);
}
ZEND_HASH_PACKED_FOREACH_PTR(attributes, zend_attribute *attr) {
zend_internal_attribute *config = zend_internal_attribute_get(attr->lcname);
if (config && config->validator != NULL) {
config->validator(attr, ZEND_ATTRIBUTE_TARGET_CONST, CG(active_class_entry), c, 0);
}
} ZEND_HASH_FOREACH_END();
}
4 changes: 2 additions & 2 deletions ext/zend_test/test.c
Original file line number Diff line number Diff line change
Expand Up @@ -951,9 +951,9 @@ static zend_function *zend_test_class_static_method_get(zend_class_entry *ce, ze
return zend_std_get_static_method(ce, name, NULL);
}

void zend_attribute_validate_zendtestattribute(zend_attribute *attr, uint32_t target, zend_class_entry *scope)
void zend_attribute_validate_zendtestattribute(zend_attribute *attr, uint32_t target_type, zend_class_entry *scope, void *target, uint32_t offset)
{
if (target != ZEND_ATTRIBUTE_TARGET_CLASS) {
if (target_type != ZEND_ATTRIBUTE_TARGET_CLASS) {
zend_error(E_COMPILE_ERROR, "Only classes can be marked with #[ZendTestAttribute]");
}
}
Expand Down