Skip to content
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
8 changes: 8 additions & 0 deletions UPGRADING.INTERNALS
Original file line number Diff line number Diff line change
Expand Up @@ -47,6 +47,11 @@ PHP 8.7 INTERNALS UPGRADE NOTES
- The INIT_CLASS_ENTRY_INIT_METHODS() macro, which was previously used as
part of handling disabled classes, was removed following the removal of
the `disable_classes` INI setting in PHP 8.5.
- Removed ZEND_ACC_PTR_OPS. Attributes of user constants are now stored in
the attributes table of the declaring file's main op_array.
zend_constant.attributes can therefore be shared.
Use zend_constant_attributes_offset() to get the offset of a constant's own attributes.
Added zend_get_attribute_str_ex() to look up an attribute at a raw offset.

========================
2. Build system changes
Expand Down Expand Up @@ -82,6 +87,9 @@ PHP 8.7 INTERNALS UPGRADE NOTES
4. OpCode changes
========================

- ZEND_DECLARE_ATTRIBUTED_CONST no longer has an OP_DATA opline.
The offset of the constant's attributes in the op_array's attributes table is stored in extended_value.

========================
5. SAPI changes
========================
Expand Down
5 changes: 5 additions & 0 deletions Zend/zend_attributes.c
Original file line number Diff line number Diff line change
Expand Up @@ -304,6 +304,11 @@ ZEND_API zend_attribute *zend_get_attribute_str(const HashTable *attributes, con
return get_attribute_str(attributes, str, len, 0);
}

ZEND_API zend_attribute *zend_get_attribute_str_ex(const HashTable *attributes, const char *str, size_t len, uint32_t offset)
{
return get_attribute_str(attributes, str, len, offset);
}

ZEND_API zend_attribute *zend_get_parameter_attribute(const HashTable *attributes, const zend_string *lcname, uint32_t offset)
{
return get_attribute(attributes, lcname, offset + 1);
Expand Down
6 changes: 5 additions & 1 deletion Zend/zend_attributes.h
Original file line number Diff line number Diff line change
Expand Up @@ -64,7 +64,9 @@ typedef struct _zend_attribute {
zend_string *validation_error;
uint32_t flags;
uint32_t lineno;
/* Parameter offsets start at 1, everything else uses 0. */
/* Parameter offsets start at 1.
* User constants use their own offset in the shared table of the main op_array (see zend_attributed_constant).

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

a bit confused here - for parameters, this is saying that the current attribute applies to parameter #n in a function

for user constants, I'm reading this as saying that the current attribute applies to the constant at offset #n in the main op_array - is that correct? Can you improve the documentation a bit?

* Everything else uses 0. */
uint32_t offset;
uint32_t argc;
zend_attribute_arg args[1];
Expand All @@ -78,6 +80,8 @@ typedef struct _zend_internal_attribute {

ZEND_API zend_attribute *zend_get_attribute(const HashTable *attributes, const zend_string *lcname);
ZEND_API zend_attribute *zend_get_attribute_str(const HashTable *attributes, const char *str, size_t len);
/* Raw offset, i.e. not adjusted for parameters. */
ZEND_API zend_attribute *zend_get_attribute_str_ex(const HashTable *attributes, const char *str, size_t len, uint32_t offset);

ZEND_API zend_attribute *zend_get_parameter_attribute(const HashTable *attributes, const zend_string *lcname, uint32_t offset);
ZEND_API zend_attribute *zend_get_parameter_attribute_str(const HashTable *attributes, const char *str, size_t len, uint32_t offset);
Expand Down
37 changes: 13 additions & 24 deletions Zend/zend_compile.c
Original file line number Diff line number Diff line change
Expand Up @@ -8226,23 +8226,12 @@ static void zend_compile_attributes(

if (*attributes != NULL) {
/* Allow delaying target validation for forward compatibility. */
const zend_attribute *delayed_target_validation = NULL;
if (target == ZEND_ATTRIBUTE_TARGET_PARAMETER) {
ZEND_ASSERT(offset >= 1);
/* zend_get_parameter_attribute_str will add 1 too */
delayed_target_validation = zend_get_parameter_attribute_str(
*attributes,
"delayedtargetvalidation",
strlen("delayedtargetvalidation"),
offset - 1
);
} else {
delayed_target_validation = zend_get_attribute_str(
*attributes,
"delayedtargetvalidation",
strlen("delayedtargetvalidation")
);
}
const zend_attribute *delayed_target_validation = zend_get_attribute_str_ex(
*attributes,
"delayedtargetvalidation",
strlen("delayedtargetvalidation"),
offset
);
/* Validate attributes in a secondary loop (needed to detect repeated attributes). */
ZEND_HASH_PACKED_FOREACH_PTR(*attributes, attr) {
if (attr->offset != offset || NULL == (config = zend_internal_attribute_get(attr->lcname))) {
Expand Down Expand Up @@ -10447,16 +10436,16 @@ 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);
/* Constants can only be declared at the top level, so the attributes of
* all constants of a file are stored in the attributes table of the main op_array. */
HashTable **attributes = &CG(active_op_array)->attributes;
ZEND_ASSERT(!CG(active_op_array)->function_name);
uint32_t offset = *attributes ? zend_hash_num_elements(*attributes) : 0;
zend_compile_attributes(attributes, list->child[1], offset, ZEND_ATTRIBUTE_TARGET_CONST, 0);

ZEND_ASSERT(last_op != NULL);
last_op->opcode = ZEND_DECLARE_ATTRIBUTED_CONST;
znode attribs_node;
attribs_node.op_type = IS_CONST;
ZVAL_PTR(&attribs_node.u.constant, attributes);
zend_emit_op_data(&attribs_node);
CG(active_op_array)->fn_flags |= ZEND_ACC_PTR_OPS;
last_op->extended_value = offset;
}
/* }}}*/

Expand Down
5 changes: 0 additions & 5 deletions Zend/zend_compile.h
Original file line number Diff line number Diff line change
Expand Up @@ -402,11 +402,6 @@ typedef struct _zend_oparray_context {
/* supports opcache compile-time evaluation (funcs) | | | */
#define ZEND_ACC_COMPILE_TIME_EVAL (1 << 27) /* | X | | */
/* | | | */
/* Has IS_PTR operands that needs special cleaning; same | | | */
/* value as ZEND_ACC_OVERRIDE but override is for class | | | */
/* methods and this is for the top level op array | | | */
#define ZEND_ACC_PTR_OPS (1 << 28) /* | X | | */
Comment thread
Girgias marked this conversation as resolved.
/* | | | */
/* has #[\NoDiscard] attribute | | | */
#define ZEND_ACC_NODISCARD (1 << 29) /* | X | | */
/* | | | */
Expand Down
11 changes: 7 additions & 4 deletions Zend/zend_constants.c
Original file line number Diff line number Diff line change
Expand Up @@ -75,6 +75,7 @@ static void copy_zend_constant(zval *zv)
zend_constant *c = Z_PTR_P(zv);

ZEND_ASSERT(ZEND_CONSTANT_FLAGS(c) & CONST_PERSISTENT);
ZEND_ASSERT(!(ZEND_CONSTANT_FLAGS(c) & CONST_ATTRIBUTED));
Z_PTR_P(zv) = pemalloc(sizeof(zend_constant), 1);
memcpy(Z_PTR_P(zv), c, sizeof(zend_constant));

Expand Down Expand Up @@ -497,9 +498,10 @@ ZEND_API zval *zend_get_constant_ex(zend_string *cname, const zend_class_entry *
static void* zend_hash_add_constant(HashTable *ht, zend_string *key, const zend_constant *c)
{
void *ret;
zend_constant *copy = pemalloc(sizeof(zend_constant), ZEND_CONSTANT_FLAGS(c) & CONST_PERSISTENT);
size_t size = (ZEND_CONSTANT_FLAGS(c) & CONST_ATTRIBUTED) ? sizeof(zend_attributed_constant) : sizeof(zend_constant);
zend_constant *copy = pemalloc(size, ZEND_CONSTANT_FLAGS(c) & CONST_PERSISTENT);

memcpy(copy, c, sizeof(zend_constant));
memcpy(copy, c, size);
ret = zend_hash_add_ptr(ht, key, copy);
if (!ret) {
pefree(copy, ZEND_CONSTANT_FLAGS(c) & CONST_PERSISTENT);
Expand Down Expand Up @@ -571,10 +573,11 @@ 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(
zend_attribute *deprecated_attribute = zend_get_attribute_str_ex(
c->attributes,
"deprecated",
strlen("deprecated")
strlen("deprecated"),
zend_constant_attributes_offset(c)
);

if (deprecated_attribute) {
Expand Down
19 changes: 18 additions & 1 deletion Zend/zend_constants.h
Original file line number Diff line number Diff line change
Expand Up @@ -27,6 +27,7 @@
#define CONST_DEPRECATED (1<<2) /* Deprecated */
#define CONST_OWNED (1<<3) /* constant should be destroyed together with class */
#define CONST_RECURSIVE (1<<4) /* Recursion protection for constant evaluation */
#define CONST_ATTRIBUTED (1<<5) /* Allocated as zend_attributed_constant */

#define CONST_IS_RECURSIVE(c) (Z_CONSTANT_FLAGS((c)->value) & CONST_RECURSIVE)
#define CONST_PROTECT_RECURSION(c) \
Expand All @@ -44,6 +45,7 @@ typedef struct _zend_constant {
zval value;
zend_string *name;
zend_string *filename;
/* May be shared with other constants, see zend_attributed_constant. */
HashTable *attributes;
} zend_constant;

Expand All @@ -58,6 +60,21 @@ typedef struct _zend_constant {
((_flags) & 0xff) | ((_module_number) << 8); \
} while (0)

/* User constants declared with attributes.
* This is a type specialization of the zend_constant type. */
typedef struct zend_attributed_constant {
zend_constant constant;
/* Offset into the attributes able of the main op_array of the declaring file. */
uint32_t attributes_offset;
} zend_attributed_constant;

static zend_always_inline uint32_t zend_constant_attributes_offset(const zend_constant *c)
{
return (ZEND_CONSTANT_FLAGS(c) & CONST_ATTRIBUTED)
? ((const zend_attributed_constant *) c)->attributes_offset
: 0;
}

#define REGISTER_NULL_CONSTANT(name, flags) zend_register_null_constant((name), sizeof(name)-1, (flags), module_number)
#define REGISTER_BOOL_CONSTANT(name, bval, flags) zend_register_bool_constant((name), sizeof(name)-1, (bval), (flags), module_number)
#define REGISTER_LONG_CONSTANT(name, lval, flags) zend_register_long_constant((name), sizeof(name)-1, (lval), (flags), module_number)
Expand Down Expand Up @@ -97,7 +114,7 @@ ZEND_API zend_constant *zend_register_double_constant(const char *name, size_t n
ZEND_API zend_constant *zend_register_string_constant(const char *name, size_t name_len, const char *strval, int flags, int module_number);
ZEND_API zend_constant *zend_register_stringl_constant(const char *name, size_t name_len, const char *strval, size_t strlen, int flags, int module_number);
ZEND_API zend_constant *zend_register_constant(zend_constant *c);
void zend_constant_add_attributes(zend_constant *c, HashTable *attributes);
void ZEND_ATTRIBUTE_NONNULL zend_constant_add_attributes(zend_constant *c, HashTable *attributes);
#ifdef ZTS
void zend_copy_constants(HashTable *target, HashTable *source);
#endif
Expand Down
12 changes: 6 additions & 6 deletions Zend/zend_execute.c
Original file line number Diff line number Diff line change
Expand Up @@ -1831,15 +1831,15 @@ ZEND_API zend_never_inline ZEND_COLD void zend_wrong_string_offset_error(void)
zend_throw_error(NULL, "%s", msg);
}

ZEND_COLD static zend_result ZEND_FASTCALL get_deprecation_suffix_from_attribute(HashTable *attributes, zend_class_entry* scope, zend_string **message_suffix)
ZEND_COLD static zend_result ZEND_FASTCALL get_deprecation_suffix_from_attribute(HashTable *attributes, uint32_t offset, zend_class_entry* scope, zend_string **message_suffix)
{
*message_suffix = ZSTR_EMPTY_ALLOC();

if (!attributes) {
return SUCCESS;
}

zend_attribute *deprecated = zend_get_attribute_str(attributes, "deprecated", sizeof("deprecated")-1);
zend_attribute *deprecated = zend_get_attribute_str_ex(attributes, "deprecated", sizeof("deprecated")-1, offset);

if (!deprecated) {
return SUCCESS;
Expand Down Expand Up @@ -1900,7 +1900,7 @@ ZEND_API ZEND_COLD void ZEND_FASTCALL zend_deprecated_function(const zend_functi
{
zend_string *message_suffix = ZSTR_EMPTY_ALLOC();

if (get_deprecation_suffix_from_attribute(fbc->common.attributes, fbc->common.scope, &message_suffix) == FAILURE) {
if (get_deprecation_suffix_from_attribute(fbc->common.attributes, 0, fbc->common.scope, &message_suffix) == FAILURE) {
return;
}

Expand Down Expand Up @@ -2007,7 +2007,7 @@ ZEND_API ZEND_COLD void ZEND_FASTCALL zend_deprecated_class_constant(const zend_
{
zend_string *message_suffix = ZSTR_EMPTY_ALLOC();

if (get_deprecation_suffix_from_attribute(c->attributes, c->ce, &message_suffix) == FAILURE) {
if (get_deprecation_suffix_from_attribute(c->attributes, 0, c->ce, &message_suffix) == FAILURE) {
return;
}

Expand All @@ -2028,7 +2028,7 @@ ZEND_API ZEND_COLD void ZEND_FASTCALL zend_deprecated_constant(const zend_consta
{
zend_string *message_suffix = ZSTR_EMPTY_ALLOC();

if (get_deprecation_suffix_from_attribute(c->attributes, NULL, &message_suffix) == FAILURE) {
if (get_deprecation_suffix_from_attribute(c->attributes, zend_constant_attributes_offset(c), NULL, &message_suffix) == FAILURE) {
return;
}

Expand All @@ -2048,7 +2048,7 @@ ZEND_API ZEND_COLD void zend_use_of_deprecated_trait(
) {
zend_string *message_suffix = ZSTR_EMPTY_ALLOC();

if (get_deprecation_suffix_from_attribute(trait->attributes, trait, &message_suffix) == FAILURE) {
if (get_deprecation_suffix_from_attribute(trait->attributes, 0, trait, &message_suffix) == FAILURE) {
return;
}

Expand Down
12 changes: 0 additions & 12 deletions Zend/zend_opcode.c
Original file line number Diff line number Diff line change
Expand Up @@ -595,18 +595,6 @@ ZEND_API void destroy_op_array(zend_op_array *op_array)
efree(op_array->vars);
}

/* ZEND_ACC_PTR_OPS and ZEND_ACC_OVERRIDE use the same value */
if ((op_array->fn_flags & ZEND_ACC_PTR_OPS) && !op_array->function_name) {
zend_op *op = op_array->opcodes;
zend_op *end = op + op_array->last;
while (op < end) {
if (op->opcode == ZEND_DECLARE_ATTRIBUTED_CONST) {
HashTable *attributes = Z_PTR_P(RT_CONSTANT(op+1, (op+1)->op1));
zend_hash_release(attributes);
}
op++;
}
}
if (op_array->literals) {
zval *literal = op_array->literals;
zval *end = literal + op_array->last_literal;
Expand Down
35 changes: 15 additions & 20 deletions Zend/zend_vm_def.h
Original file line number Diff line number Diff line change
Expand Up @@ -8373,46 +8373,41 @@ ZEND_VM_HANDLER(143, ZEND_DECLARE_CONST, CONST, CONST)
ZEND_VM_NEXT_OPCODE_CHECK_EXCEPTION();
}

ZEND_VM_HANDLER(210, ZEND_DECLARE_ATTRIBUTED_CONST, CONST, CONST)
ZEND_VM_HANDLER(210, ZEND_DECLARE_ATTRIBUTED_CONST, CONST, CONST, NUM)
{
USE_OPLINE
zval *name;
zval *val;
zend_constant c, *registered;
zend_attributed_constant c;
zend_constant *registered;

SAVE_OPLINE();
name = GET_OP1_ZVAL_PTR(BP_VAR_R);
val = GET_OP2_ZVAL_PTR(BP_VAR_R);

ZVAL_COPY(&c.value, val);
if (Z_OPT_TYPE(c.value) == IS_CONSTANT_AST) {
if (UNEXPECTED(zval_update_constant_ex(&c.value, EX(func)->op_array.scope) != SUCCESS)) {
zval_ptr_dtor_nogc(&c.value);
ZVAL_COPY(&c.constant.value, val);
if (Z_OPT_TYPE(c.constant.value) == IS_CONSTANT_AST) {
if (UNEXPECTED(zval_update_constant_ex(&c.constant.value, EX(func)->op_array.scope) != SUCCESS)) {
zval_ptr_dtor_nogc(&c.constant.value);
FREE_OP1();
FREE_OP2();
HANDLE_EXCEPTION();
}
}
/* non persistent, case sensitive */
ZEND_CONSTANT_SET_FLAGS(&c, 0, PHP_USER_CONSTANT);
c.name = zend_string_copy(Z_STR_P(name));
ZEND_CONSTANT_SET_FLAGS(&c.constant, CONST_ATTRIBUTED, PHP_USER_CONSTANT);
c.constant.name = zend_string_copy(Z_STR_P(name));
c.attributes_offset = opline->extended_value;

registered = zend_register_constant(&c);
if (registered == NULL) {
FREE_OP1();
FREE_OP2();
/* two opcodes used, second one is the data with attributes */
ZEND_VM_NEXT_OPCODE_EX(1, 2);
registered = zend_register_constant(&c.constant);
if (registered != NULL) {
/* The attributes are stored in the main op_array, see zend_compile_const_decl() */
zend_constant_add_attributes(registered, EX(func)->op_array.attributes);
}

HashTable *attributes = Z_PTR_P(GET_OP_DATA_ZVAL_PTR(BP_VAR_R));
ZEND_ASSERT(attributes != NULL);
zend_constant_add_attributes(registered, attributes);

FREE_OP1();
FREE_OP2();
/* two opcodes used, second one is the data with attributes */
ZEND_VM_NEXT_OPCODE_EX(1, 2);
ZEND_VM_NEXT_OPCODE_CHECK_EXCEPTION();
}

ZEND_VM_HANDLER(142, ZEND_DECLARE_LAMBDA_FUNCTION, UNUSED, NUM, NUM|CACHE_SLOT)
Expand Down
Loading
Loading