diff options
Diffstat (limited to 'Zend')
-rw-r--r-- | Zend/tests/anon/015.phpt | 33 | ||||
-rw-r--r-- | Zend/tests/anon/016.phpt | 35 | ||||
-rw-r--r-- | Zend/zend.c | 1 | ||||
-rw-r--r-- | Zend/zend.h | 25 | ||||
-rw-r--r-- | Zend/zend_API.c | 159 | ||||
-rw-r--r-- | Zend/zend_API.h | 33 | ||||
-rw-r--r-- | Zend/zend_builtin_functions.c | 5 | ||||
-rw-r--r-- | Zend/zend_compile.c | 34 | ||||
-rw-r--r-- | Zend/zend_compile.h | 13 | ||||
-rw-r--r-- | Zend/zend_constants.c | 2 | ||||
-rw-r--r-- | Zend/zend_execute.c | 24 | ||||
-rw-r--r-- | Zend/zend_execute_API.c | 16 | ||||
-rw-r--r-- | Zend/zend_globals.h | 2 | ||||
-rw-r--r-- | Zend/zend_inheritance.c | 474 | ||||
-rw-r--r-- | Zend/zend_inheritance.h | 7 | ||||
-rw-r--r-- | Zend/zend_map_ptr.h | 12 | ||||
-rw-r--r-- | Zend/zend_opcode.c | 62 | ||||
-rw-r--r-- | Zend/zend_types.h | 32 | ||||
-rw-r--r-- | Zend/zend_vm_def.h | 8 | ||||
-rw-r--r-- | Zend/zend_vm_execute.h | 12 |
20 files changed, 889 insertions, 100 deletions
diff --git a/Zend/tests/anon/015.phpt b/Zend/tests/anon/015.phpt new file mode 100644 index 0000000000..324ebe880a --- /dev/null +++ b/Zend/tests/anon/015.phpt @@ -0,0 +1,33 @@ +--TEST-- +static variables in methods inherited from parent class +--FILE-- +<?php +class C { + function foo ($y = null) { + static $x = null; + if (!is_null($y)) { + $x = [$y]; + } + return $x; + } +} +$c = new C(); +$c->foo(42); +$d = new class extends C {}; +var_dump($d->foo()); +var_dump($d->foo(24)); +var_dump($c->foo()); +?> +--EXPECT-- +array(1) { + [0]=> + int(42) +} +array(1) { + [0]=> + int(24) +} +array(1) { + [0]=> + int(42) +} diff --git a/Zend/tests/anon/016.phpt b/Zend/tests/anon/016.phpt new file mode 100644 index 0000000000..4cde6dfeab --- /dev/null +++ b/Zend/tests/anon/016.phpt @@ -0,0 +1,35 @@ +--TEST-- +static variables in methods inherited from parent class (can't cache objects) +--FILE-- +<?php +class C { + function foo ($y = null) { + static $x = null; + if (!is_null($y)) { + $x = [$y]; + } + return $x; + } +} +$c = new C(); +$c->foo(new stdClass); +$d = new class extends C {}; +var_dump($d->foo()); +var_dump($d->foo(24)); +var_dump($c->foo()); +?> +--EXPECT-- +array(1) { + [0]=> + object(stdClass)#2 (0) { + } +} +array(1) { + [0]=> + int(24) +} +array(1) { + [0]=> + object(stdClass)#2 (0) { + } +} diff --git a/Zend/zend.c b/Zend/zend.c index 4da7991db6..4518322622 100644 --- a/Zend/zend.c +++ b/Zend/zend.c @@ -660,6 +660,7 @@ static void compiler_globals_ctor(zend_compiler_globals *compiler_globals) /* {{ zend_hash_copy(compiler_globals->auto_globals, global_auto_globals_table, auto_global_copy_ctor); compiler_globals->script_encoding_list = NULL; + compiler_globals->current_linking_class = NULL; #if ZEND_MAP_PTR_KIND == ZEND_MAP_PTR_KIND_PTR_OR_OFFSET /* Map region is going to be created and resized at run-time. */ diff --git a/Zend/zend.h b/Zend/zend.h index 6a2a834d93..44f3baac46 100644 --- a/Zend/zend.h +++ b/Zend/zend.h @@ -107,6 +107,28 @@ typedef struct _zend_trait_alias { uint32_t modifiers; } zend_trait_alias; +typedef struct _zend_class_mutable_data { + zval *default_properties_table; + HashTable *constants_table; + uint32_t ce_flags; +} zend_class_mutable_data; + +typedef struct _zend_class_dependency { + zend_string *name; + zend_class_entry *ce; +} zend_class_dependency; + +typedef struct _zend_inheritance_cache_entry zend_inheritance_cache_entry; + +struct _zend_inheritance_cache_entry { + zend_inheritance_cache_entry *next; + zend_class_entry *ce; + zend_class_entry *parent; + zend_class_dependency *dependencies; + uint32_t dependencies_count; + zend_class_entry *traits_and_interfaces[1]; +}; + struct _zend_class_entry { char type; zend_string *name; @@ -127,6 +149,9 @@ struct _zend_class_entry { HashTable properties_info; HashTable constants_table; + ZEND_MAP_PTR_DEF(zend_class_mutable_data*, mutable_data); + zend_inheritance_cache_entry *inheritance_cache; + struct _zend_property_info **properties_info_table; zend_function *constructor; diff --git a/Zend/zend_API.c b/Zend/zend_API.c index 5f558c4930..abfbb411a5 100644 --- a/Zend/zend_API.c +++ b/Zend/zend_API.c @@ -1213,39 +1213,147 @@ ZEND_API void zend_merge_properties(zval *obj, HashTable *properties) /* {{{ */ } /* }}} */ +static zend_class_mutable_data *zend_allocate_mutable_data(zend_class_entry *class_type) /* {{{ */ +{ + zend_class_mutable_data *mutable_data; + + ZEND_ASSERT(class_type->ce_flags & ZEND_ACC_IMMUTABLE); + ZEND_ASSERT(ZEND_MAP_PTR(class_type->mutable_data) != NULL); + ZEND_ASSERT(ZEND_MAP_PTR_GET_IMM(class_type->mutable_data) == NULL); + + mutable_data = zend_arena_alloc(&CG(arena), sizeof(zend_class_mutable_data)); + memset(mutable_data, 0, sizeof(zend_class_mutable_data)); + mutable_data->ce_flags = class_type->ce_flags; + ZEND_MAP_PTR_SET_IMM(class_type->mutable_data, mutable_data); + + return mutable_data; +} +/* }}} */ + +ZEND_API HashTable *zend_separate_class_constants_table(zend_class_entry *class_type) /* {{{ */ +{ + zend_class_mutable_data *mutable_data; + HashTable *constants_table; + zend_string *key; + zend_class_constant *new_c, *c; + + constants_table = zend_arena_alloc(&CG(arena), sizeof(HashTable)); + zend_hash_init(constants_table, zend_hash_num_elements(&class_type->constants_table), NULL, NULL, 0); + zend_hash_extend(constants_table, zend_hash_num_elements(&class_type->constants_table), 0); + + ZEND_HASH_FOREACH_STR_KEY_PTR(&class_type->constants_table, key, c) { + if (Z_TYPE(c->value) == IS_CONSTANT_AST) { + new_c = zend_arena_alloc(&CG(arena), sizeof(zend_class_constant)); + memcpy(new_c, c, sizeof(zend_class_constant)); + c = new_c; + } + _zend_hash_append_ptr(constants_table, key, c); + } ZEND_HASH_FOREACH_END(); + + ZEND_ASSERT(class_type->ce_flags & ZEND_ACC_IMMUTABLE); + ZEND_ASSERT(ZEND_MAP_PTR(class_type->mutable_data) != NULL); + + mutable_data = ZEND_MAP_PTR_GET_IMM(class_type->mutable_data); + if (!mutable_data) { + mutable_data = zend_allocate_mutable_data(class_type); + } + + mutable_data->constants_table = constants_table; + + return constants_table; +} + ZEND_API zend_result zend_update_class_constants(zend_class_entry *class_type) /* {{{ */ { - if (!(class_type->ce_flags & ZEND_ACC_CONSTANTS_UPDATED)) { - zend_class_constant *c; - zval *val; - zend_property_info *prop_info; + zend_class_mutable_data *mutable_data = NULL; + zval *default_properties_table = NULL; + zval *static_members_table = NULL; + zend_class_constant *c; + zval *val; + zend_property_info *prop_info; + uint32_t ce_flags; - if (class_type->parent) { - if (UNEXPECTED(zend_update_class_constants(class_type->parent) != SUCCESS)) { - return FAILURE; + ce_flags = class_type->ce_flags; + + if (ce_flags & ZEND_ACC_CONSTANTS_UPDATED) { + return SUCCESS; + } + + if (ce_flags & ZEND_ACC_IMMUTABLE) { + mutable_data = ZEND_MAP_PTR_GET_IMM(class_type->mutable_data); + if (mutable_data) { + ce_flags = mutable_data->ce_flags; + if (ce_flags & ZEND_ACC_CONSTANTS_UPDATED) { + return SUCCESS; } + } else { + mutable_data = zend_allocate_mutable_data(class_type); } + } - ZEND_HASH_FOREACH_PTR(&class_type->constants_table, c) { - val = &c->value; - if (Z_TYPE_P(val) == IS_CONSTANT_AST) { + if (class_type->parent) { + if (UNEXPECTED(zend_update_class_constants(class_type->parent) != SUCCESS)) { + return FAILURE; + } + } + + if (ce_flags & ZEND_ACC_HAS_AST_CONSTANTS) { + HashTable *constants_table; + + if (ce_flags & ZEND_ACC_IMMUTABLE) { + constants_table = mutable_data->constants_table; + if (!constants_table) { + constants_table = zend_separate_class_constants_table(class_type); + } + } else { + constants_table = &class_type->constants_table; + } + ZEND_HASH_FOREACH_PTR(constants_table, c) { + if (Z_TYPE(c->value) == IS_CONSTANT_AST) { + val = &c->value; if (UNEXPECTED(zval_update_constant_ex(val, c->ce) != SUCCESS)) { return FAILURE; } } } ZEND_HASH_FOREACH_END(); + } - if (class_type->default_static_members_count && !CE_STATIC_MEMBERS(class_type)) { - if (class_type->type == ZEND_INTERNAL_CLASS || (class_type->ce_flags & (ZEND_ACC_IMMUTABLE|ZEND_ACC_PRELOADED))) { + if (class_type->default_static_members_count) { + static_members_table = CE_STATIC_MEMBERS(class_type); + if (!static_members_table) { + if (class_type->type == ZEND_INTERNAL_CLASS || (ce_flags & (ZEND_ACC_IMMUTABLE|ZEND_ACC_PRELOADED))) { zend_class_init_statics(class_type); + static_members_table = CE_STATIC_MEMBERS(class_type); } } + } + + default_properties_table = class_type->default_properties_table; + if ((ce_flags & ZEND_ACC_IMMUTABLE) + && (ce_flags & ZEND_ACC_HAS_AST_PROPERTIES)) { + zval *src, *dst, *end; + + default_properties_table = mutable_data->default_properties_table; + if (!default_properties_table) { + default_properties_table = zend_arena_alloc(&CG(arena), sizeof(zval) * class_type->default_properties_count); + src = class_type->default_properties_table; + dst = default_properties_table; + end = dst + class_type->default_properties_count; + do { + ZVAL_COPY_VALUE_PROP(dst, src); + src++; + dst++; + } while (dst != end); + mutable_data->default_properties_table = default_properties_table; + } + } + if (ce_flags & (ZEND_ACC_HAS_AST_PROPERTIES|ZEND_ACC_HAS_AST_STATICS)) { ZEND_HASH_FOREACH_PTR(&class_type->properties_info, prop_info) { if (prop_info->flags & ZEND_ACC_STATIC) { - val = CE_STATIC_MEMBERS(class_type) + prop_info->offset; + val = static_members_table + prop_info->offset; } else { - val = (zval*)((char*)class_type->default_properties_table + prop_info->offset - OBJ_PROP_TO_OFFSET(0)); + val = (zval*)((char*)default_properties_table + prop_info->offset - OBJ_PROP_TO_OFFSET(0)); } if (Z_TYPE_P(val) == IS_CONSTANT_AST) { if (ZEND_TYPE_IS_SET(prop_info->type)) { @@ -1268,8 +1376,21 @@ ZEND_API zend_result zend_update_class_constants(zend_class_entry *class_type) / } } } ZEND_HASH_FOREACH_END(); + } - class_type->ce_flags |= ZEND_ACC_CONSTANTS_UPDATED; + ce_flags |= ZEND_ACC_CONSTANTS_UPDATED; + ce_flags &= ~ZEND_ACC_HAS_AST_CONSTANTS; + ce_flags &= ~ZEND_ACC_HAS_AST_PROPERTIES; + if (class_type->ce_flags & ZEND_ACC_IMMUTABLE) { + ce_flags &= ~ZEND_ACC_HAS_AST_STATICS; + if (mutable_data) { + mutable_data->ce_flags = ce_flags; + } + } else { + if (!(ce_flags & ZEND_ACC_PRELOADED)) { + ce_flags &= ~ZEND_ACC_HAS_AST_STATICS; + } + class_type->ce_flags = ce_flags; } return SUCCESS; @@ -1279,7 +1400,7 @@ ZEND_API zend_result zend_update_class_constants(zend_class_entry *class_type) / static zend_always_inline void _object_properties_init(zend_object *object, zend_class_entry *class_type) /* {{{ */ { if (class_type->default_properties_count) { - zval *src = class_type->default_properties_table; + zval *src = CE_DEFAULT_PROPERTIES_TABLE(class_type); zval *dst = object->properties_table; zval *end = src + class_type->default_properties_count; @@ -3809,6 +3930,11 @@ ZEND_API zend_property_info *zend_declare_typed_property(zend_class_entry *ce, z property_info = zend_arena_alloc(&CG(arena), sizeof(zend_property_info)); if (Z_TYPE_P(property) == IS_CONSTANT_AST) { ce->ce_flags &= ~ZEND_ACC_CONSTANTS_UPDATED; + if (access_type & ZEND_ACC_STATIC) { + ce->ce_flags |= ZEND_ACC_HAS_AST_STATICS; + } else { + ce->ce_flags |= ZEND_ACC_HAS_AST_PROPERTIES; + } } } @@ -4124,6 +4250,7 @@ ZEND_API zend_class_constant *zend_declare_class_constant_ex(zend_class_entry *c c->ce = ce; if (Z_TYPE_P(value) == IS_CONSTANT_AST) { ce->ce_flags &= ~ZEND_ACC_CONSTANTS_UPDATED; + ce->ce_flags |= ZEND_ACC_HAS_AST_CONSTANTS; } if (!zend_hash_add_ptr(&ce->constants_table, name, c)) { diff --git a/Zend/zend_API.h b/Zend/zend_API.h index 89eb6312d6..6c867b2869 100644 --- a/Zend/zend_API.h +++ b/Zend/zend_API.h @@ -278,6 +278,12 @@ typedef struct _zend_fcall_info_cache { #define CE_STATIC_MEMBERS(ce) \ ((zval*)ZEND_MAP_PTR_GET((ce)->static_members_table)) +#define CE_CONSTANTS_TABLE(ce) \ + zend_class_constants_table(ce) + +#define CE_DEFAULT_PROPERTIES_TABLE(ce) \ + zend_class_default_properties_table(ce) + #define ZEND_FCI_INITIALIZED(fci) ((fci).size != 0) ZEND_API int zend_next_free_module(void); @@ -382,6 +388,33 @@ ZEND_API void zend_declare_class_constant_stringl(zend_class_entry *ce, const ch ZEND_API void zend_declare_class_constant_string(zend_class_entry *ce, const char *name, size_t name_length, const char *value); ZEND_API zend_result zend_update_class_constants(zend_class_entry *class_type); +ZEND_API HashTable *zend_separate_class_constants_table(zend_class_entry *class_type); + +static zend_always_inline HashTable *zend_class_constants_table(zend_class_entry *ce) { + if ((ce->ce_flags & ZEND_ACC_HAS_AST_CONSTANTS) + && (ce->ce_flags & ZEND_ACC_IMMUTABLE)) { + zend_class_mutable_data *mutable_data = + (zend_class_mutable_data*)ZEND_MAP_PTR_GET_IMM(ce->mutable_data); + if (mutable_data && mutable_data->constants_table) { + return mutable_data->constants_table; + } else { + return zend_separate_class_constants_table(ce); + } + } else { + return &ce->constants_table; + } +} + +static zend_always_inline zval *zend_class_default_properties_table(zend_class_entry *ce) { + if ((ce->ce_flags & ZEND_ACC_HAS_AST_PROPERTIES) + && (ce->ce_flags & ZEND_ACC_IMMUTABLE)) { + zend_class_mutable_data *mutable_data = + (zend_class_mutable_data*)ZEND_MAP_PTR_GET_IMM(ce->mutable_data); + return mutable_data->default_properties_table; + } else { + return ce->default_properties_table; + } +} ZEND_API void zend_update_property_ex(zend_class_entry *scope, zend_object *object, zend_string *name, zval *value); ZEND_API void zend_update_property(zend_class_entry *scope, zend_object *object, const char *name, size_t name_length, zval *value); diff --git a/Zend/zend_builtin_functions.c b/Zend/zend_builtin_functions.c index fa65d1ca07..afabfa97f3 100644 --- a/Zend/zend_builtin_functions.c +++ b/Zend/zend_builtin_functions.c @@ -703,6 +703,7 @@ static void add_class_vars(zend_class_entry *scope, zend_class_entry *ce, bool s zend_property_info *prop_info; zval *prop, prop_copy; zend_string *key; + zval *default_properties_table = CE_DEFAULT_PROPERTIES_TABLE(ce); ZEND_HASH_FOREACH_STR_KEY_PTR(&ce->properties_info, key, prop_info) { if (((prop_info->flags & ZEND_ACC_PROTECTED) && @@ -716,7 +717,7 @@ static void add_class_vars(zend_class_entry *scope, zend_class_entry *ce, bool s prop = &ce->default_static_members_table[prop_info->offset]; ZVAL_DEINDIRECT(prop); } else if (!statics && (prop_info->flags & ZEND_ACC_STATIC) == 0) { - prop = &ce->default_properties_table[OBJ_PROP_TO_NUM(prop_info->offset)]; + prop = &default_properties_table[OBJ_PROP_TO_NUM(prop_info->offset)]; } if (!prop) { continue; @@ -734,7 +735,7 @@ static void add_class_vars(zend_class_entry *scope, zend_class_entry *ce, bool s /* this is necessary to make it able to work with default array * properties, returned to user */ if (Z_OPT_TYPE_P(prop) == IS_CONSTANT_AST) { - if (UNEXPECTED(zval_update_constant_ex(prop, NULL) != SUCCESS)) { + if (UNEXPECTED(zval_update_constant_ex(prop, ce) != SUCCESS)) { return; } } diff --git a/Zend/zend_compile.c b/Zend/zend_compile.c index 65fa186234..d617547f81 100644 --- a/Zend/zend_compile.c +++ b/Zend/zend_compile.c @@ -417,6 +417,8 @@ void init_compiler(void) /* {{{ */ CG(delayed_variance_obligations) = NULL; CG(delayed_autoloads) = NULL; + CG(unlinked_uses) = NULL; + CG(current_linking_class) = NULL; } /* }}} */ @@ -428,7 +430,6 @@ void shutdown_compiler(void) /* {{{ */ zend_stack_destroy(&CG(loop_var_stack)); zend_stack_destroy(&CG(delayed_oplines_stack)); zend_stack_destroy(&CG(short_circuiting_opnums)); - zend_arena_destroy(CG(arena)); if (CG(delayed_variance_obligations)) { zend_hash_destroy(CG(delayed_variance_obligations)); @@ -440,6 +441,12 @@ void shutdown_compiler(void) /* {{{ */ FREE_HASHTABLE(CG(delayed_autoloads)); CG(delayed_autoloads) = NULL; } + if (CG(unlinked_uses)) { + zend_hash_destroy(CG(unlinked_uses)); + FREE_HASHTABLE(CG(unlinked_uses)); + CG(unlinked_uses) = NULL; + } + CG(current_linking_class) = NULL; } /* }}} */ @@ -1136,7 +1143,8 @@ ZEND_API zend_result do_bind_class(zval *lcname, zend_string *lc_parent_name) /* return FAILURE; } - if (zend_do_link_class(ce, lc_parent_name) == FAILURE) { + ce = zend_do_link_class(ce, lc_parent_name, Z_STR_P(lcname)); + if (!ce) { /* Reload bucket pointer, the hash table may have been reallocated */ zv = zend_hash_find(EG(class_table), Z_STR_P(lcname)); zend_hash_set_bucket_key(EG(class_table), (Bucket *) zv, Z_STR_P(rtd_key)); @@ -1187,13 +1195,23 @@ zend_string *zend_type_to_string_resolved(zend_type type, zend_class_entry *scop if (ZEND_TYPE_HAS_CE(*list_type)) { str = add_type_string(str, ZEND_TYPE_CE(*list_type)->name); } else { - zend_string *resolved = resolve_class_name(ZEND_TYPE_NAME(*list_type), scope); - str = add_type_string(str, resolved); - zend_string_release(resolved); + if (ZEND_TYPE_HAS_CE_CACHE(*list_type) + && ZEND_TYPE_CE_CACHE(*list_type)) { + str = add_type_string(str, ZEND_TYPE_CE_CACHE(*list_type)->name); + } else { + zend_string *resolved = resolve_class_name(ZEND_TYPE_NAME(*list_type), scope); + str = add_type_string(str, resolved); + zend_string_release(resolved); + } } } ZEND_TYPE_LIST_FOREACH_END(); } else if (ZEND_TYPE_HAS_NAME(type)) { - str = resolve_class_name(ZEND_TYPE_NAME(type), scope); + if (ZEND_TYPE_HAS_CE_CACHE(type) + && ZEND_TYPE_CE_CACHE(type)) { + str = zend_string_copy(ZEND_TYPE_CE_CACHE(type)->name); + } else { + str = resolve_class_name(ZEND_TYPE_NAME(type), scope); + } } else if (ZEND_TYPE_HAS_CE(type)) { str = zend_string_copy(ZEND_TYPE_CE(type)->name); } @@ -1354,7 +1372,8 @@ ZEND_API void zend_do_delayed_early_binding(zend_op_array *op_array, uint32_t fi zend_class_entry *parent_ce = zend_hash_find_ex_ptr(EG(class_table), lc_parent_name, 1); if (parent_ce) { - if (zend_try_early_bind(ce, parent_ce, Z_STR_P(lcname), zv)) { + ce = zend_try_early_bind(ce, parent_ce, Z_STR_P(lcname), zv); + if (ce) { /* Store in run-time cache */ ((void**)((char*)run_time_cache + opline->extended_value))[0] = ce; } @@ -1831,6 +1850,7 @@ ZEND_API void zend_initialize_class_data(zend_class_entry *ce, bool nullify_hand ZEND_MAP_PTR_INIT(ce->static_members_table, &ce->default_static_members_table); ce->info.user.doc_comment = NULL; } + ZEND_MAP_PTR_INIT(ce->mutable_data, NULL); ce->default_properties_count = 0; ce->default_static_members_count = 0; diff --git a/Zend/zend_compile.h b/Zend/zend_compile.h index ffc2f7baa4..fbab084b2b 100644 --- a/Zend/zend_compile.h +++ b/Zend/zend_compile.h @@ -233,7 +233,7 @@ typedef struct _zend_oparray_context { /* op_array or class is preloaded | | | */ #define ZEND_ACC_PRELOADED (1 << 10) /* X | X | | */ /* | | | */ -/* Class Flags (unused: 23...) | | | */ +/* Class Flags (unused: 28...) | | | */ /* =========== | | | */ /* | | | */ /* Special class types | | | */ @@ -285,6 +285,16 @@ typedef struct _zend_oparray_context { /* stored in opcache (may be partially) | | | */ #define ZEND_ACC_CACHED (1 << 22) /* X | | | */ /* | | | */ +/* temporary flag used during delayed variance checks | | | */ +#define ZEND_ACC_CACHEABLE (1 << 23) /* X | | | */ +/* | | | */ +#define ZEND_ACC_HAS_AST_CONSTANTS (1 << 24) /* X | | | */ +#define ZEND_ACC_HAS_AST_PROPERTIES (1 << 25) /* X | | | */ +#define ZEND_ACC_HAS_AST_STATICS (1 << 26) /* X | | | */ +/* | | | */ +/* loaded from file cache to process memory | | | */ +#define ZEND_ACC_FILE_CACHED (1 << 27) /* X | | | */ +/* | | | */ /* Function Flags (unused: 27-30) | | | */ /* ============== | | | */ /* | | | */ @@ -802,6 +812,7 @@ ZEND_API int open_file_for_scanning(zend_file_handle *file_handle); ZEND_API void init_op_array(zend_op_array *op_array, zend_uchar type, int initial_ops_size); ZEND_API void destroy_op_array(zend_op_array *op_array); ZEND_API void zend_destroy_file_handle(zend_file_handle *file_handle); +ZEND_API void zend_cleanup_mutable_class_data(zend_class_entry *ce); ZEND_API void zend_cleanup_internal_class_data(zend_class_entry *ce); ZEND_API void zend_cleanup_internal_classes(void); ZEND_API void zend_type_release(zend_type type, bool persistent); diff --git a/Zend/zend_constants.c b/Zend/zend_constants.c index 03ab10b523..9534190f8d 100644 --- a/Zend/zend_constants.c +++ b/Zend/zend_constants.c @@ -374,7 +374,7 @@ ZEND_API zval *zend_get_constant_ex(zend_string *cname, zend_class_entry *scope, ce = zend_fetch_class(class_name, flags); } if (ce) { - c = zend_hash_find_ptr(&ce->constants_table, constant_name); + c = zend_hash_find_ptr(CE_CONSTANTS_TABLE(ce), constant_name); if (c == NULL) { if ((flags & ZEND_FETCH_CLASS_SILENT) == 0) { zend_throw_error(NULL, "Undefined constant %s::%s", ZSTR_VAL(class_name), ZSTR_VAL(constant_name)); diff --git a/Zend/zend_execute.c b/Zend/zend_execute.c index 93cee93dbd..5338a0ae43 100644 --- a/Zend/zend_execute.c +++ b/Zend/zend_execute.c @@ -857,7 +857,17 @@ static bool zend_check_and_resolve_property_class_type( if (ZEND_TYPE_HAS_LIST(info->type)) { zend_type *list_type; ZEND_TYPE_LIST_FOREACH(ZEND_TYPE_LIST(info->type), list_type) { - if (ZEND_TYPE_HAS_NAME(*list_type)) { + if (ZEND_TYPE_HAS_CE_CACHE(*list_type)) { + ce = ZEND_TYPE_CE_CACHE(*list_type); + if (!ce) { + zend_string *name = ZEND_TYPE_NAME(*list_type); + ce = resolve_single_class_type(name, info->ce); + if (UNEXPECTED(!ce)) { + continue; + } + ZEND_TYPE_SET_CE_CACHE(*list_type, ce); + } + } else if (ZEND_TYPE_HAS_NAME(*list_type)) { zend_string *name = ZEND_TYPE_NAME(*list_type); ce = resolve_single_class_type(name, info->ce); if (!ce) { @@ -874,7 +884,17 @@ static bool zend_check_and_resolve_property_class_type( } ZEND_TYPE_LIST_FOREACH_END(); return 0; } else { - if (UNEXPECTED(ZEND_TYPE_HAS_NAME(info->type))) { + if (ZEND_TYPE_HAS_CE_CACHE(info->type)) { + ce = ZEND_TYPE_CE_CACHE(info->type); + if (!ce) { + zend_string *name = ZEND_TYPE_NAME(info->type); + ce = resolve_single_class_type(name, info->ce); + if (UNEXPECTED(!ce)) { + return 0; + } + ZEND_TYPE_SET_CE_CACHE(info->type, ce); + } + } else if (UNEXPECTED(ZEND_TYPE_HAS_NAME(info->type))) { zend_string *name = ZEND_TYPE_NAME(info->type); ce = resolve_single_class_type(name, info->ce); if (UNEXPECTED(!ce)) { diff --git a/Zend/zend_execute_API.c b/Zend/zend_execute_API.c index 4a6ff1bdbc..7c32f3a7fb 100644 --- a/Zend/zend_execute_API.c +++ b/Zend/zend_execute_API.c @@ -295,9 +295,15 @@ void shutdown_executor(void) /* {{{ */ } ZEND_HASH_FOREACH_END(); ZEND_HASH_REVERSE_FOREACH_VAL(EG(class_table), zv) { zend_class_entry *ce = Z_PTR_P(zv); + if (ce->default_static_members_count) { zend_cleanup_internal_class_data(ce); } + + if (ZEND_MAP_PTR(ce->mutable_data) && ZEND_MAP_PTR_GET_IMM(ce->mutable_data)) { + zend_cleanup_mutable_class_data(ce); + } + if (ce->ce_flags & ZEND_HAS_STATIC_IN_METHODS) { zend_op_array *op_array; ZEND_HASH_FOREACH_PTR(&ce->function_table, op_array) { @@ -1059,7 +1065,15 @@ ZEND_API zend_class_entry *zend_lookup_class_ex(zend_string *name, zend_string * if ((flags & ZEND_FETCH_CLASS_ALLOW_UNLINKED) || ((flags & ZEND_FETCH_CLASS_ALLOW_NEARLY_LINKED) && (ce->ce_flags & ZEND_ACC_NEARLY_LINKED))) { - ce->ce_flags |= ZEND_ACC_HAS_UNLINKED_USES; + if (ce->ce_flags & ZEND_ACC_IMMUTABLE) { + if (!CG(unlinked_uses)) { + ALLOC_HASHTABLE(CG(unlinked_uses)); + zend_hash_init(CG(unlinked_uses), 0, NULL, NULL, 0); + } + zend_hash_index_add_empty_element(CG(unlinked_uses), (zend_long)(zend_uintptr_t)ce); + } else { + ce->ce_flags |= ZEND_ACC_HAS_UNLINKED_USES; + } return ce; } return NULL; diff --git a/Zend/zend_globals.h b/Zend/zend_globals.h index 6d67325fe1..73dac228cc 100644 --- a/Zend/zend_globals.h +++ b/Zend/zend_globals.h @@ -126,6 +126,8 @@ struct _zend_compiler_globals { HashTable *delayed_variance_obligations; HashTable *delayed_autoloads; + HashTable *unlinked_uses; + zend_class_entry *current_linking_class; uint32_t rtd_key_counter; diff --git a/Zend/zend_inheritance.c b/Zend/zend_inheritance.c index 16e31a10d8..32d03acb4a 100644 --- a/Zend/zend_inheritance.c +++ b/Zend/zend_inheritance.c @@ -27,6 +27,9 @@ #include "zend_operators.h" #include "zend_exceptions.h" +ZEND_API zend_class_entry* (*zend_inheritance_cache_get)(zend_class_entry *ce, zend_class_entry *parent, zend_class_entry **traits_and_interfaces) = NULL; +ZEND_API zend_class_entry* (*zend_inheritance_cache_add)(zend_class_entry *ce, zend_class_entry *proto, zend_class_entry *parent, zend_class_entry **traits_and_interfaces, HashTable *dependencies) = NULL; + static void add_dependency_obligation(zend_class_entry *ce, zend_class_entry *dependency_ce); static void add_compatibility_obligation( zend_class_entry *ce, const zend_function *child_fn, zend_class_entry *child_scope, @@ -368,6 +371,47 @@ typedef enum { INHERITANCE_SUCCESS = 1, } inheritance_status; + +static void track_class_dependency(zend_class_entry *ce, zend_string *class_name) +{ + HashTable *ht; + + if (!CG(current_linking_class) || ce == CG(current_linking_class)) { + return; + } else if (!class_name) { + class_name = ce->name; + } else if (zend_string_equals_literal_ci(class_name, "self") + || zend_string_equals_literal_ci(class_name, "parent")) { + return; + } + + ht = (HashTable*)CG(current_linking_class)->inheritance_cache; + +#ifndef ZEND_WIN32 + if (ce->type == ZEND_USER_CLASS && !(ce->ce_flags & ZEND_ACC_IMMUTABLE)) { +#else + if (!(ce->ce_flags & ZEND_ACC_IMMUTABLE)) { +#endif + // TODO: dependency on not-immutable class ??? + if (ht) { + zend_hash_destroy(ht); + FREE_HASHTABLE(ht); + CG(current_linking_class)->inheritance_cache = NULL; + } + CG(current_linking_class)->ce_flags &= ~ZEND_ACC_CACHEABLE; + CG(current_linking_class) = NULL; + return; + } + + /* Record dependency */ + if (!ht) { + ALLOC_HASHTABLE(ht); + zend_hash_init(ht, 0, NULL, NULL, 0); + CG(current_linking_class)->inheritance_cache = (zend_inheritance_cache_entry*)ht; + } + zend_hash_add_ptr(ht, class_name, ce); +} + static inheritance_status zend_perform_covariant_class_type_check( zend_class_entry *fe_scope, zend_string *fe_class_name, zend_class_entry *fe_ce, zend_class_entry *proto_scope, zend_type proto_type, @@ -381,6 +425,7 @@ static inheritance_status zend_perform_covariant_class_type_check( if (!fe_ce) { have_unresolved = 1; } else { + track_class_dependency(fe_ce, fe_class_name); return INHERITANCE_SUCCESS; } } @@ -389,6 +434,7 @@ static inheritance_status zend_perform_covariant_class_type_check( if (!fe_ce) { have_unresolved = 1; } else if (unlinked_instanceof(fe_ce, zend_ce_traversable)) { + track_class_dependency(fe_ce, fe_class_name); return INHERITANCE_SUCCESS; } } @@ -396,8 +442,9 @@ static inheritance_status zend_perform_covariant_class_type_check( zend_type *single_type; ZEND_TYPE_FOREACH(proto_type, single_type) { zend_class_entry *proto_ce; + zend_string *proto_class_name = NULL; if (ZEND_TYPE_HAS_NAME(*single_type)) { - zend_string *proto_class_name = + proto_class_name = resolve_class_name(proto_scope, ZEND_TYPE_NAME(*single_type)); if (zend_string_equals_ci(fe_class_name, proto_class_name)) { return INHERITANCE_SUCCESS; @@ -416,6 +463,8 @@ static inheritance_status zend_perform_covariant_class_type_check( if (!fe_ce || !proto_ce) { have_unresolved = 1; } else if (unlinked_instanceof(fe_ce, proto_ce)) { + track_class_dependency(fe_ce, fe_class_name); + track_class_dependency(proto_ce, proto_class_name); return INHERITANCE_SUCCESS; } } ZEND_TYPE_FOREACH_END(); @@ -1139,6 +1188,12 @@ static void do_inherit_class_constant(zend_string *name, zend_class_constant *pa } else if (!(Z_ACCESS_FLAGS(parent_const->value) & ZEND_ACC_PRIVATE)) { if (Z_TYPE(parent_const->value) == IS_CONSTANT_AST) { ce->ce_flags &= ~ZEND_ACC_CONSTANTS_UPDATED; + ce->ce_flags |= ZEND_ACC_HAS_AST_CONSTANTS; + if (ce->parent->ce_flags & ZEND_ACC_IMMUTABLE) { + c = zend_arena_alloc(&CG(arena), sizeof(zend_class_constant)); + memcpy(c, parent_const, sizeof(zend_class_constant)); + parent_const = c; + } } if (ce->type & ZEND_INTERNAL_CLASS) { c = pemalloc(sizeof(zend_class_constant), 1); @@ -1251,6 +1306,7 @@ ZEND_API void zend_do_inheritance_ex(zend_class_entry *ce, zend_class_entry *par ZVAL_COPY_OR_DUP_PROP(dst, src); if (Z_OPT_TYPE_P(dst) == IS_CONSTANT_AST) { ce->ce_flags &= ~ZEND_ACC_CONSTANTS_UPDATED; + ce->ce_flags |= ZEND_ACC_HAS_AST_PROPERTIES; } continue; } while (dst != end); @@ -1261,6 +1317,7 @@ ZEND_API void zend_do_inheritance_ex(zend_class_entry *ce, zend_class_entry *par ZVAL_COPY_PROP(dst, src); if (Z_OPT_TYPE_P(dst) == IS_CONSTANT_AST) { ce->ce_flags &= ~ZEND_ACC_CONSTANTS_UPDATED; + ce->ce_flags |= ZEND_ACC_HAS_AST_PROPERTIES; } continue; } while (dst != end); @@ -1323,6 +1380,7 @@ ZEND_API void zend_do_inheritance_ex(zend_class_entry *ce, zend_class_entry *par } if (Z_TYPE_P(Z_INDIRECT_P(dst)) == IS_CONSTANT_AST) { ce->ce_flags &= ~ZEND_ACC_CONSTANTS_UPDATED; + ce->ce_flags |= ZEND_ACC_HAS_AST_STATICS; } } while (dst != end); } else { @@ -1434,6 +1492,12 @@ static void do_inherit_iface_constant(zend_string *name, zend_class_constant *c, zend_class_constant *ct; if (Z_TYPE(c->value) == IS_CONSTANT_AST) { ce->ce_flags &= ~ZEND_ACC_CONSTANTS_UPDATED; + ce->ce_flags |= ZEND_ACC_HAS_AST_CONSTANTS; + if (iface->ce_flags & ZEND_ACC_IMMUTABLE) { + ct = zend_arena_alloc(&CG(arena), sizeof(zend_class_constant)); + memcpy(ct, c, sizeof(zend_class_constant)); + c = ct; + } } if (ce->type & ZEND_INTERNAL_CLASS) { ct = pemalloc(sizeof(zend_class_constant), 1); @@ -2073,37 +2137,13 @@ static void zend_do_traits_property_binding(zend_class_entry *ce, zend_class_ent } /* }}} */ -static void zend_do_bind_traits(zend_class_entry *ce) /* {{{ */ +static void zend_do_bind_traits(zend_class_entry *ce, zend_class_entry **traits) /* {{{ */ { HashTable **exclude_tables; zend_class_entry **aliases; - zend_class_entry **traits, *trait; - uint32_t i, j; ZEND_ASSERT(ce->num_traits > 0); - traits = emalloc(sizeof(zend_class_entry*) * ce->num_traits); - - for (i = 0; i < ce->num_traits; i++) { - trait = zend_fetch_class_by_name(ce->trait_names[i].name, - ce->trait_names[i].lc_name, ZEND_FETCH_CLASS_TRAIT); - if (UNEXPECTED(trait == NULL)) { - return; - } - if (UNEXPECTED(!(trait->ce_flags & ZEND_ACC_TRAIT))) { - zend_error_noreturn(E_ERROR, "%s cannot use %s - it is not a trait", ZSTR_VAL(ce->name), ZSTR_VAL(trait->name)); - return; - } - for (j = 0; j < i; j++) { - if (traits[j] == trait) { - /* skip duplications */ - trait = NULL; - break; - } - } - traits[i] = trait; - } - /* complete initialization of trait strutures in ce */ zend_traits_init_trait_structures(ce, traits, &exclude_tables, &aliases); @@ -2120,8 +2160,6 @@ static void zend_do_bind_traits(zend_class_entry *ce) /* {{{ */ /* then flatten the properties into it, to, mostly to notfiy developer about problems */ zend_do_traits_property_binding(ce, traits); - - efree(traits); } /* }}} */ @@ -2293,7 +2331,12 @@ static int check_variance_obligation(zval *zv) { if (obligation->type == OBLIGATION_DEPENDENCY) { zend_class_entry *dependency_ce = obligation->dependency_ce; if (dependency_ce->ce_flags & ZEND_ACC_UNRESOLVED_VARIANCE) { + zend_class_entry *orig_linking_class = CG(current_linking_class); + + CG(current_linking_class) = + (dependency_ce->ce_flags & ZEND_ACC_CACHEABLE) ? dependency_ce : NULL; resolve_delayed_variance_obligations(dependency_ce); + CG(current_linking_class) = orig_linking_class; } if (!(dependency_ce->ce_flags & ZEND_ACC_LINKED)) { return ZEND_HASH_APPLY_KEEP; @@ -2401,7 +2444,10 @@ static void check_unrecoverable_load_failure(zend_class_entry *ce) { * to remove the class from the class table and throw an exception, because there is already * a dependence on the inheritance hierarchy of this specific class. Instead we fall back to * a fatal error, as would happen if we did not allow exceptions in the first place. */ - if (ce->ce_flags & ZEND_ACC_HAS_UNLINKED_USES) { + if ((ce->ce_flags & ZEND_ACC_HAS_UNLINKED_USES) + || ((ce->ce_flags & ZEND_ACC_IMMUTABLE) + && CG(unlinked_uses) + && zend_hash_index_del(CG(unlinked_uses), (zend_long)(zend_uintptr_t)ce) == SUCCESS)) { zend_string *exception_str; zval exception_zv; ZEND_ASSERT(EG(exception) && "Exception must have been thrown"); @@ -2413,13 +2459,167 @@ static void check_unrecoverable_load_failure(zend_class_entry *ce) { } } -ZEND_API zend_result zend_do_link_class(zend_class_entry *ce, zend_string *lc_parent_name) /* {{{ */ +#define zend_update_inherited_handler(handler) do { \ + if (ce->handler == (zend_function*)op_array) { \ + ce->handler = (zend_function*)new_op_array; \ + } \ + } while (0) + +static zend_class_entry *zend_lazy_class_load(zend_class_entry *pce) +{ + zend_class_entry *ce; + Bucket *p, *end; + + ce = zend_arena_alloc(&CG(arena), sizeof(zend_class_entry)); + memcpy(ce, pce, sizeof(zend_class_entry)); + ce->ce_flags &= ~ZEND_ACC_IMMUTABLE; + ce->refcount = 1; + ce->inheritance_cache = NULL; + ZEND_MAP_PTR_INIT(ce->mutable_data, NULL); + + /* properties */ + if (ce->default_properties_table) { + zval *dst = emalloc(sizeof(zval) * ce->default_properties_count); + zval *src = ce->default_properties_table; + zval *end = src + ce->default_properties_count; + + ce->default_properties_table = dst; + for (; src != end; src++, dst++) { + ZVAL_COPY_VALUE_PROP(dst, src); + } + } + + /* methods */ + ce->function_table.pDestructor = ZEND_FUNCTION_DTOR; + if (!(HT_FLAGS(&ce->function_table) & HASH_FLAG_UNINITIALIZED)) { + p = emalloc(HT_SIZE(&ce->function_table)); + memcpy(p, HT_GET_DATA_ADDR(&ce->function_table), HT_USED_SIZE(&ce->function_table)); + HT_SET_DATA_ADDR(&ce->function_table, p); + p = ce->function_table.arData; + end = p + ce->function_table.nNumUsed; + for (; p != end; p++) { + zend_op_array *op_array, *new_op_array; + void ***run_time_cache_ptr; + + op_array = Z_PTR(p->val); + ZEND_ASSERT(op_array->type == ZEND_USER_FUNCTION); + ZEND_ASSERT(op_array->scope == pce); + ZEND_ASSERT(op_array->prototype == NULL); + new_op_array = zend_arena_alloc(&CG(arena), sizeof(zend_op_array) + sizeof(void*)); + Z_PTR(p->val) = new_op_array; + memcpy(new_op_array, op_array, sizeof(zend_op_array)); + run_time_cache_ptr = (void***)(new_op_array + 1); + *run_time_cache_ptr = NULL; + new_op_array->fn_flags &= ~ZEND_ACC_IMMUTABLE; + new_op_array->scope = ce; + ZEND_MAP_PTR_INIT(new_op_array->run_time_cache, run_time_cache_ptr); + ZEND_MAP_PTR_INIT(new_op_array->static_variables_ptr, &new_op_array->static_variables); + + zend_update_inherited_handler(constructor); + zend_update_inherited_handler(destructor); + zend_update_inherited_handler(clone); + zend_update_inherited_handler(__get); + zend_update_inherited_handler(__set); + zend_update_inherited_handler(__call); + zend_update_inherited_handler(__isset); + zend_update_inherited_handler(__unset); + zend_update_inherited_handler(__tostring); + zend_update_inherited_handler(__callstatic); + zend_update_inherited_handler(__debugInfo); + zend_update_inherited_handler(__serialize); + zend_update_inherited_handler(__unserialize); + } + } + + /* static members */ + if (ce->default_static_members_table) { + zval *dst = emalloc(sizeof(zval) * ce->default_static_members_count); + zval *src = ce->default_static_members_table; + zval *end = src + ce->default_static_members_count; + + ce->default_static_members_table = dst; + for (; src != end; src++, dst++) { + ZVAL_COPY_VALUE(dst, src); + } + } + ZEND_MAP_PTR_INIT(ce->static_members_table, &ce->default_static_members_table); + + /* properties_info */ + if (!(HT_FLAGS(&ce->properties_info) & HASH_FLAG_UNINITIALIZED)) { + p = emalloc(HT_SIZE(&ce->properties_info)); + memcpy(p, HT_GET_DATA_ADDR(&ce->properties_info), HT_USED_SIZE(&ce->properties_info)); + HT_SET_DATA_ADDR(&ce->properties_info, p); + p = ce->properties_info.arData; + end = p + ce->properties_info.nNumUsed; + for (; p != end; p++) { + zend_property_info *prop_info, *new_prop_info; + + prop_info = Z_PTR(p->val); + ZEND_ASSERT(prop_info->ce == pce); + new_prop_info= zend_arena_alloc(&CG(arena), sizeof(zend_property_info)); + Z_PTR(p->val) = new_prop_info; + memcpy(new_prop_info, prop_info, sizeof(zend_property_info)); + new_prop_info->ce = ce; + if (ZEND_TYPE_HAS_LIST(new_prop_info->type)) { + zend_type_list *new_list; + zend_type_list *list = ZEND_TYPE_LIST(new_prop_info->type); + + new_list = zend_arena_alloc(&CG(arena), ZEND_TYPE_LIST_SIZE(list->num_types)); + memcpy(new_list, list, ZEND_TYPE_LIST_SIZE(list->num_types)); + ZEND_TYPE_SET_PTR(new_prop_info->type, list); + ZEND_TYPE_FULL_MASK(new_prop_info->type) |= _ZEND_TYPE_ARENA_BIT; + } + } + } + + /* constants table */ + if (!(HT_FLAGS(&ce->constants_table) & HASH_FLAG_UNINITIALIZED)) { + p = emalloc(HT_SIZE(&ce->constants_table)); + memcpy(p, HT_GET_DATA_ADDR(&ce->constants_table), HT_USED_SIZE(&ce->constants_table)); + HT_SET_DATA_ADDR(&ce->constants_table, p); + p = ce->constants_table.arData; + end = p + ce->constants_table.nNumUsed; + for (; p != end; p++) { + zend_class_constant *c, *new_c; + + c = Z_PTR(p->val); + ZEND_ASSERT(c->ce == pce); + new_c = zend_arena_alloc(&CG(arena), sizeof(zend_class_constant)); + Z_PTR(p->val) = new_c; + memcpy(new_c, c, sizeof(zend_class_constant)); + new_c->ce = ce; + } + } + + return ce; +} + +#ifndef ZEND_WIN32 +# define UPDATE_IS_CACHEABLE(ce) do { \ + if ((ce)->type == ZEND_USER_CLASS) { \ + is_cacheable &= (ce)->ce_flags; \ + } \ + } while (0) +#else +// TODO: ASLR may cause different addresses in different workers ??? +# define UPDATE_IS_CACHEABLE(ce) do { \ + is_cacheable &= (ce)->ce_flags; \ + } while (0) +#endif + +ZEND_API zend_class_entry *zend_do_link_class(zend_class_entry *ce, zend_string *lc_parent_name, zend_string *key) /* {{{ */ { /* Load parent/interface dependencies first, so we can still gracefully abort linking * with an exception and remove the class from the class table. This is only possible * if no variance obligations on the current class have been added during autoloading. */ zend_class_entry *parent = NULL; zend_class_entry **interfaces = NULL; + zend_class_entry **traits_and_interfaces = NULL; + zend_class_entry *proto = NULL; + zend_class_entry *orig_linking_class; + uint32_t is_cacheable = ce->ce_flags & ZEND_ACC_IMMUTABLE; + uint32_t i, j; + zval *zv; if (ce->parent_name) { parent = zend_fetch_class_by_name( @@ -2427,13 +2627,43 @@ ZEND_API zend_result zend_do_link_class(zend_class_entry *ce, zend_string *lc_pa ZEND_FETCH_CLASS_ALLOW_NEARLY_LINKED | ZEND_FETCH_CLASS_EXCEPTION); if (!parent) { check_unrecoverable_load_failure(ce); - return FAILURE; + return NULL; + } + UPDATE_IS_CACHEABLE(parent); + } + + if (ce->num_traits || ce->num_interfaces) { + traits_and_interfaces = emalloc(sizeof(zend_class_entry*) * (ce->num_traits + ce->num_interfaces)); + + for (i = 0; i < ce->num_traits; i++) { + zend_class_entry *trait = zend_fetch_class_by_name(ce->trait_names[i].name, + ce->trait_names[i].lc_name, ZEND_FETCH_CLASS_TRAIT); + if (UNEXPECTED(trait == NULL)) { + efree(traits_and_interfaces); + return NULL; + } + if (UNEXPECTED(!(trait->ce_flags & ZEND_ACC_TRAIT))) { + zend_error_noreturn(E_ERROR, "%s cannot use %s - it is not a trait", ZSTR_VAL(ce->name), ZSTR_VAL(trait->name)); + efree(traits_and_interfaces); + return NULL; + } + for (j = 0; j < i; j++) { + if (traits_and_interfaces[j] == trait) { + /* skip duplications */ + trait = NULL; + break; + } + } + traits_and_interfaces[i] = trait; + if (trait) { + UPDATE_IS_CACHEABLE(trait); + } } } if (ce->num_interfaces) { /* Also copy the parent interfaces here, so we don't need to reallocate later. */ - uint32_t i, num_parent_interfaces = parent ? parent->num_interfaces : 0; + uint32_t num_parent_interfaces = parent ? parent->num_interfaces : 0; interfaces = emalloc( sizeof(zend_class_entry *) * (ce->num_interfaces + num_parent_interfaces)); if (num_parent_interfaces) { @@ -2448,12 +2678,61 @@ ZEND_API zend_result zend_do_link_class(zend_class_entry *ce, zend_string *lc_pa if (!iface) { check_unrecoverable_load_failure(ce); efree(interfaces); - return FAILURE; + efree(traits_and_interfaces); + return NULL; } interfaces[num_parent_interfaces + i] = iface; + traits_and_interfaces[ce->num_traits + i] = iface; + if (iface) { + UPDATE_IS_CACHEABLE(iface); + } } } + + if (ce->ce_flags & ZEND_ACC_IMMUTABLE) { + if (is_cacheable) { + if (zend_inheritance_cache_get && zend_inheritance_cache_add) { + zend_class_entry *ret = zend_inheritance_cache_get(ce, parent, traits_and_interfaces); + if (ret) { + if (traits_and_interfaces) { + efree(traits_and_interfaces); + } + if (traits_and_interfaces) { + efree(interfaces); + } + zv = zend_hash_find_ex(CG(class_table), key, 1); + Z_CE_P(zv) = ret; + return ret; + } + } else { + is_cacheable = 0; + } + proto = ce; + } + /* Lazy class loading */ + ce = zend_lazy_class_load(ce); + zv = zend_hash_find_ex(CG(class_table), key, 1); + Z_CE_P(zv) = ce; + if (CG(unlinked_uses) + && zend_hash_index_del(CG(unlinked_uses), (zend_long)(zend_uintptr_t)proto) == SUCCESS) { + ce->ce_flags |= ZEND_ACC_HAS_UNLINKED_USES; + } + } else if (ce->ce_flags & ZEND_ACC_FILE_CACHED) { + /* Lazy class loading */ + ce = zend_lazy_class_load(ce); + ce->ce_flags &= ~ZEND_ACC_FILE_CACHED; + zv = zend_hash_find_ex(CG(class_table), key, 1); + Z_CE_P(zv) = ce; + if (CG(unlinked_uses) + && zend_hash_index_del(CG(unlinked_uses), (zend_long)(zend_uintptr_t)proto) == SUCCESS) { + ce->ce_flags |= ZEND_ACC_HAS_UNLINKED_USES; + } + } + + orig_linking_class = CG(current_linking_class); + CG(current_linking_class) = is_cacheable ? ce : NULL; + if (parent) { if (!(parent->ce_flags & ZEND_ACC_LINKED)) { add_dependency_obligation(ce, parent); @@ -2461,7 +2740,7 @@ ZEND_API zend_result zend_do_link_class(zend_class_entry *ce, zend_string *lc_pa zend_do_inheritance(ce, parent); } if (ce->num_traits) { - zend_do_bind_traits(ce); + zend_do_bind_traits(ce, traits_and_interfaces); } if (interfaces) { zend_do_implement_interfaces(ce, interfaces); @@ -2478,19 +2757,53 @@ ZEND_API zend_result zend_do_link_class(zend_class_entry *ce, zend_string *lc_pa if (!(ce->ce_flags & ZEND_ACC_UNRESOLVED_VARIANCE)) { ce->ce_flags |= ZEND_ACC_LINKED; - return SUCCESS; + } else { + ce->ce_flags |= ZEND_ACC_NEARLY_LINKED; + if (CG(current_linking_class)) { + ce->ce_flags |= ZEND_ACC_CACHEABLE; + } + load_delayed_classes(); + if (ce->ce_flags & ZEND_ACC_UNRESOLVED_VARIANCE) { + resolve_delayed_variance_obligations(ce); + if (!(ce->ce_flags & ZEND_ACC_LINKED)) { + CG(current_linking_class) = orig_linking_class; + report_variance_errors(ce); + } + } + if (ce->ce_flags & ZEND_ACC_CACHEABLE) { + ce->ce_flags &= ~ZEND_ACC_CACHEABLE; + } else { + CG(current_linking_class) = NULL; + } + } + + if (!CG(current_linking_class)) { + is_cacheable = 0; } + CG(current_linking_class) = orig_linking_class; - ce->ce_flags |= ZEND_ACC_NEARLY_LINKED; - load_delayed_classes(); - if (ce->ce_flags & ZEND_ACC_UNRESOLVED_VARIANCE) { - resolve_delayed_variance_obligations(ce); - if (!(ce->ce_flags & ZEND_ACC_LINKED)) { - report_variance_errors(ce); + if (is_cacheable) { + HashTable *ht = (HashTable*)ce->inheritance_cache; + zend_class_entry *new_ce; + + ce->inheritance_cache = NULL; + new_ce = zend_inheritance_cache_add(ce, proto, parent, traits_and_interfaces, ht); + if (new_ce) { + zv = zend_hash_find_ex(CG(class_table), key, 1); + ce = new_ce; + Z_CE_P(zv) = ce; + } + if (ht) { + zend_hash_destroy(ht); + FREE_HASHTABLE(ht); } } - return SUCCESS; + if (traits_and_interfaces) { + efree(traits_and_interfaces); + } + + return ce; } /* }}} */ @@ -2539,21 +2852,66 @@ static inheritance_status zend_can_early_bind(zend_class_entry *ce, zend_class_e } /* }}} */ -bool zend_try_early_bind(zend_class_entry *ce, zend_class_entry *parent_ce, zend_string *lcname, zval *delayed_early_binding) /* {{{ */ +zend_class_entry *zend_try_early_bind(zend_class_entry *ce, zend_class_entry *parent_ce, zend_string *lcname, zval *delayed_early_binding) /* {{{ */ { - inheritance_status status = zend_can_early_bind(ce, parent_ce); + inheritance_status status; + zend_class_entry *proto = NULL; + zend_class_entry *orig_linking_class; + uint32_t is_cacheable = ce->ce_flags & ZEND_ACC_IMMUTABLE; + + UPDATE_IS_CACHEABLE(parent_ce); + if (is_cacheable) { + if (zend_inheritance_cache_get && zend_inheritance_cache_add) { + zend_class_entry *ret = zend_inheritance_cache_get(ce, parent_ce, NULL); + if (ret) { + if (delayed_early_binding) { + if (UNEXPECTED(zend_hash_set_bucket_key(EG(class_table), (Bucket*)delayed_early_binding, lcname) == NULL)) { + zend_error_noreturn(E_COMPILE_ERROR, "Cannot declare %s %s, because the name is already in use", zend_get_object_type(ce), ZSTR_VAL(ce->name)); + return NULL; + } + Z_CE_P(delayed_early_binding) = ret; + } else { + if (UNEXPECTED(zend_hash_add_ptr(CG(class_table), lcname, ret) == NULL)) { + return NULL; + } + } + return ret; + } + } else { + is_cacheable = 0; + } + proto = ce; + } + orig_linking_class = CG(current_linking_class); + CG(current_linking_class) = NULL; + status = zend_can_early_bind(ce, parent_ce); + CG(current_linking_class) = orig_linking_class; if (EXPECTED(status != INHERITANCE_UNRESOLVED)) { + if (ce->ce_flags & ZEND_ACC_IMMUTABLE) { + /* Lazy class loading */ + ce = zend_lazy_class_load(ce); + } else if (ce->ce_flags & ZEND_ACC_FILE_CACHED) { + /* Lazy class loading */ + ce = zend_lazy_class_load(ce); + ce->ce_flags &= ~ZEND_ACC_FILE_CACHED; + } + if (delayed_early_binding) { if (UNEXPECTED(zend_hash_set_bucket_key(EG(class_table), (Bucket*)delayed_early_binding, lcname) == NULL)) { zend_error_noreturn(E_COMPILE_ERROR, "Cannot declare %s %s, because the name is already in use", zend_get_object_type(ce), ZSTR_VAL(ce->name)); - return 0; + return NULL; } + Z_CE_P(delayed_early_binding) = ce; } else { if (UNEXPECTED(zend_hash_add_ptr(CG(class_table), lcname, ce) == NULL)) { - return 0; + return NULL; } } + + orig_linking_class = CG(current_linking_class); + CG(current_linking_class) = is_cacheable ? ce : NULL; + zend_do_inheritance_ex(ce, parent_ce, status == INHERITANCE_SUCCESS); if (parent_ce && parent_ce->num_interfaces) { zend_do_inherit_interfaces(ce, parent_ce); @@ -2564,8 +2922,28 @@ bool zend_try_early_bind(zend_class_entry *ce, zend_class_entry *parent_ce, zend } ZEND_ASSERT(!(ce->ce_flags & ZEND_ACC_UNRESOLVED_VARIANCE)); ce->ce_flags |= ZEND_ACC_LINKED; - return 1; + + CG(current_linking_class) = orig_linking_class; + + if (is_cacheable) { + HashTable *ht = (HashTable*)ce->inheritance_cache; + zend_class_entry *new_ce; + + ce->inheritance_cache = NULL; + new_ce = zend_inheritance_cache_add(ce, proto, parent_ce, NULL, ht); + if (new_ce) { + zval *zv = zend_hash_find_ex(CG(class_table), lcname, 1); + ce = new_ce; + Z_CE_P(zv) = ce; + } + if (ht) { + zend_hash_destroy(ht); + FREE_HASHTABLE(ht); + } + } + + return ce; } - return 0; + return NULL; } /* }}} */ diff --git a/Zend/zend_inheritance.h b/Zend/zend_inheritance.h index e82910f052..c67032f129 100644 --- a/Zend/zend_inheritance.h +++ b/Zend/zend_inheritance.h @@ -30,11 +30,14 @@ ZEND_API void zend_do_inheritance_ex(zend_class_entry *ce, zend_class_entry *par #define zend_do_inheritance(ce, parent_ce) \ zend_do_inheritance_ex(ce, parent_ce, 0) -ZEND_API zend_result zend_do_link_class(zend_class_entry *ce, zend_string *lc_parent_name); +ZEND_API zend_class_entry *zend_do_link_class(zend_class_entry *ce, zend_string *lc_parent_name, zend_string *key); void zend_verify_abstract_class(zend_class_entry *ce); void zend_build_properties_info_table(zend_class_entry *ce); -bool zend_try_early_bind(zend_class_entry *ce, zend_class_entry *parent_ce, zend_string *lcname, zval *delayed_early_binding); +zend_class_entry *zend_try_early_bind(zend_class_entry *ce, zend_class_entry *parent_ce, zend_string *lcname, zval *delayed_early_binding); + +ZEND_API extern zend_class_entry* (*zend_inheritance_cache_get)(zend_class_entry *ce, zend_class_entry *parent, zend_class_entry **traits_and_interfaces); +ZEND_API extern zend_class_entry* (*zend_inheritance_cache_add)(zend_class_entry *ce, zend_class_entry *proto, zend_class_entry *parent, zend_class_entry **traits_and_interfaces, HashTable *dependencies); END_EXTERN_C() diff --git a/Zend/zend_map_ptr.h b/Zend/zend_map_ptr.h index c6930473cf..c014f225a3 100644 --- a/Zend/zend_map_ptr.h +++ b/Zend/zend_map_ptr.h @@ -37,9 +37,13 @@ type * ZEND_MAP_PTR(name) # define ZEND_MAP_PTR_GET(ptr) \ (*(ZEND_MAP_PTR(ptr))) +# define ZEND_MAP_PTR_GET_IMM(ptr) \ + ZEND_MAP_PTR_GET(ptr) # define ZEND_MAP_PTR_SET(ptr, val) do { \ (*(ZEND_MAP_PTR(ptr))) = (val); \ } while (0) +# define ZEND_MAP_PTR_SET_IMM(ptr, val) \ + ZEND_MAP_PTR_SET(ptr, val) # define ZEND_MAP_PTR_INIT(ptr, val) do { \ ZEND_MAP_PTR(ptr) = (val); \ } while (0) @@ -51,6 +55,8 @@ # define ZEND_MAP_PTR_SET_REAL_BASE(base, ptr) do { \ base = (ptr); \ } while (0) +# define ZEND_MAP_PTR_OFFSET2PTR(ptr) \ + ((void**)((char*)CG(map_ptr_base) + (uintptr_t)ZEND_MAP_PTR(ptr))) #elif ZEND_MAP_PTR_KIND == ZEND_MAP_PTR_KIND_PTR_OR_OFFSET # define ZEND_MAP_PTR(ptr) \ ptr ## __ptr @@ -66,6 +72,8 @@ (*(ZEND_MAP_PTR_IS_OFFSET(ptr) ? \ ZEND_MAP_PTR_OFFSET2PTR(ptr) : \ ((void**)(ZEND_MAP_PTR(ptr))))) +# define ZEND_MAP_PTR_GET_IMM(ptr) \ + (*ZEND_MAP_PTR_OFFSET2PTR(ptr)) # define ZEND_MAP_PTR_SET(ptr, val) do { \ void **__p = (void**)(ZEND_MAP_PTR(ptr)); \ if (ZEND_MAP_PTR_IS_OFFSET(ptr)) { \ @@ -73,6 +81,10 @@ } \ *__p = (val); \ } while (0) +# define ZEND_MAP_PTR_SET_IMM(ptr, val) do { \ + void **__p = ZEND_MAP_PTR_OFFSET2PTR(ptr); \ + *__p = (val); \ + } while (0) # define ZEND_MAP_PTR_INIT(ptr, val) do { \ ZEND_MAP_PTR(ptr) = (val); \ } while (0) diff --git a/Zend/zend_opcode.c b/Zend/zend_opcode.c index ae9bf2d74d..abb75b8026 100644 --- a/Zend/zend_opcode.c +++ b/Zend/zend_opcode.c @@ -163,7 +163,7 @@ ZEND_API void zend_function_dtor(zval *zv) ZEND_API void zend_cleanup_internal_class_data(zend_class_entry *ce) { - if (CE_STATIC_MEMBERS(ce)) { + if (ZEND_MAP_PTR(ce->static_members_table) && CE_STATIC_MEMBERS(ce)) { zval *static_members = CE_STATIC_MEMBERS(ce); zval *p = static_members; zval *end = p + ce->default_static_members_count; @@ -255,18 +255,76 @@ static void _destroy_zend_class_traits_info(zend_class_entry *ce) } } +ZEND_API void zend_cleanup_mutable_class_data(zend_class_entry *ce) +{ + zend_class_mutable_data *mutable_data = ZEND_MAP_PTR_GET_IMM(ce->mutable_data); + + if (mutable_data) { + HashTable *constants_table; + zval *p; + + constants_table = mutable_data->constants_table; + if (constants_table && constants_table != &ce->constants_table) { + zend_class_constant *c; + + ZEND_HASH_FOREACH_PTR(constants_table, c) { + zval_ptr_dtor_nogc(&c->value); + } ZEND_HASH_FOREACH_END(); + zend_hash_destroy(constants_table); + mutable_data->constants_table = NULL; + } + + p = mutable_data->default_properties_table; + if (p && p != ce->default_properties_table) { + zval *end = p + ce->default_properties_count; + + while (p < end) { + zval_ptr_dtor_nogc(p); + p++; + } + mutable_data->default_properties_table = NULL; + } + + ZEND_MAP_PTR_SET_IMM(ce->mutable_data, NULL); + } +} + ZEND_API void destroy_zend_class(zval *zv) { zend_property_info *prop_info; zend_class_entry *ce = Z_PTR_P(zv); zend_function *fn; - if (ce->ce_flags & (ZEND_ACC_IMMUTABLE|ZEND_ACC_PRELOADED)) { + if (ce->ce_flags & (ZEND_ACC_IMMUTABLE|ZEND_ACC_PRELOADED|ZEND_ACC_FILE_CACHED)) { zend_op_array *op_array; if (ce->default_static_members_count) { zend_cleanup_internal_class_data(ce); } + + if (!(ce->ce_flags & ZEND_ACC_FILE_CACHED)) { + if (ZEND_MAP_PTR(ce->mutable_data) && ZEND_MAP_PTR_GET_IMM(ce->mutable_data)) { + zend_cleanup_mutable_class_data(ce); + } + } else { + zend_class_constant *c; + zval *p, *end; + + ZEND_HASH_FOREACH_PTR(&ce->constants_table, c) { + if (c->ce == ce) { + zval_ptr_dtor_nogc(&c->value); + } + } ZEND_HASH_FOREACH_END(); + + p = ce->default_properties_table; + end = p + ce->default_properties_count; + + while (p < end) { + zval_ptr_dtor_nogc(p); + p++; + } + } + if (ce->ce_flags & ZEND_HAS_STATIC_IN_METHODS) { ZEND_HASH_FOREACH_PTR(&ce->function_table, op_array) { if (op_array->type == ZEND_USER_FUNCTION) { diff --git a/Zend/zend_types.h b/Zend/zend_types.h index da6792ba7b..bb8073e1d5 100644 --- a/Zend/zend_types.h +++ b/Zend/zend_types.h @@ -130,7 +130,7 @@ typedef struct { * are only supported since C++20). */ void *ptr; uint32_t type_mask; - /* TODO: We could use the extra 32-bit of padding on 64-bit systems. */ + uint32_t ce_cache__ptr; /* map_ptr offset */ } zend_type; typedef struct { @@ -138,13 +138,15 @@ typedef struct { zend_type types[1]; } zend_type_list; -#define _ZEND_TYPE_EXTRA_FLAGS_SHIFT 24 -#define _ZEND_TYPE_MASK ((1u << 24) - 1) +#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 << 23) -#define _ZEND_TYPE_CE_BIT (1u << 22) -#define _ZEND_TYPE_LIST_BIT (1u << 21) +#define _ZEND_TYPE_NAME_BIT (1u << 24) +#define _ZEND_TYPE_CE_BIT (1u << 23) +#define _ZEND_TYPE_LIST_BIT (1u << 22) #define _ZEND_TYPE_KIND_MASK (_ZEND_TYPE_LIST_BIT|_ZEND_TYPE_CE_BIT|_ZEND_TYPE_NAME_BIT) +/* CE cached in map_ptr area */ +#define _ZEND_TYPE_CACHE_BIT (1u << 21) /* Whether the type list is arena allocated */ #define _ZEND_TYPE_ARENA_BIT (1u << 20) /* Type mask excluding the flags above. */ @@ -167,6 +169,9 @@ typedef struct { #define ZEND_TYPE_HAS_LIST(t) \ ((((t).type_mask) & _ZEND_TYPE_LIST_BIT) != 0) +#define ZEND_TYPE_HAS_CE_CACHE(t) \ + ((((t).type_mask) & _ZEND_TYPE_CACHE_BIT) != 0) + #define ZEND_TYPE_USES_ARENA(t) \ ((((t).type_mask) & _ZEND_TYPE_ARENA_BIT) != 0) @@ -185,6 +190,13 @@ typedef struct { #define ZEND_TYPE_LIST(t) \ ((zend_type_list *) (t).ptr) +#define ZEND_TYPE_CE_CACHE(t) \ + (*(zend_class_entry **)ZEND_MAP_PTR_OFFSET2PTR((t).ce_cache)) + +#define ZEND_TYPE_SET_CE_CACHE(t, ce) do { \ + *((zend_class_entry **)ZEND_MAP_PTR_OFFSET2PTR((t).ce_cache)) = ce; \ + } while (0) + #define ZEND_TYPE_LIST_SIZE(num_types) \ (sizeof(zend_type_list) + ((num_types) - 1) * sizeof(zend_type)) @@ -254,10 +266,10 @@ typedef struct { (((t).type_mask & _ZEND_TYPE_NULLABLE_BIT) != 0) #define ZEND_TYPE_INIT_NONE(extra_flags) \ - { NULL, (extra_flags) } + { NULL, (extra_flags), 0 } #define ZEND_TYPE_INIT_MASK(_type_mask) \ - { NULL, (_type_mask) } + { NULL, (_type_mask), 0 } #define ZEND_TYPE_INIT_CODE(code, allow_null, extra_flags) \ ZEND_TYPE_INIT_MASK(((code) == _IS_BOOL ? MAY_BE_BOOL : ((code) == IS_MIXED ? MAY_BE_ANY : (1 << (code)))) \ @@ -265,10 +277,10 @@ typedef struct { #define ZEND_TYPE_INIT_PTR(ptr, type_kind, allow_null, extra_flags) \ { (void *) (ptr), \ - (type_kind) | ((allow_null) ? _ZEND_TYPE_NULLABLE_BIT : 0) | (extra_flags) } + (type_kind) | ((allow_null) ? _ZEND_TYPE_NULLABLE_BIT : 0) | (extra_flags), 0 } #define ZEND_TYPE_INIT_PTR_MASK(ptr, type_mask) \ - { (void *) (ptr), (type_mask) } + { (void *) (ptr), (type_mask), 0 } #define ZEND_TYPE_INIT_CE(_ce, allow_null, extra_flags) \ ZEND_TYPE_INIT_PTR(_ce, _ZEND_TYPE_CE_BIT, allow_null, extra_flags) diff --git a/Zend/zend_vm_def.h b/Zend/zend_vm_def.h index 4b927461a2..d09fcd39b8 100644 --- a/Zend/zend_vm_def.h +++ b/Zend/zend_vm_def.h @@ -5821,7 +5821,7 @@ ZEND_VM_HANDLER(181, ZEND_FETCH_CLASS_CONSTANT, VAR|CONST|UNUSED|CLASS_FETCH, CO } } - zv = zend_hash_find_ex(&ce->constants_table, Z_STR_P(RT_CONSTANT(opline, opline->op2)), 1); + zv = zend_hash_find_ex(CE_CONSTANTS_TABLE(ce), Z_STR_P(RT_CONSTANT(opline, opline->op2)), 1); if (EXPECTED(zv != NULL)) { c = Z_PTR_P(zv); scope = EX(func)->op_array.scope; @@ -7526,7 +7526,8 @@ ZEND_VM_HANDLER(145, ZEND_DECLARE_CLASS_DELAYED, CONST, CONST) if (UNEXPECTED(!zv)) { zend_error_noreturn(E_COMPILE_ERROR, "Cannot declare %s %s, because the name is already in use", zend_get_object_type(ce), ZSTR_VAL(ce->name)); } else { - if (zend_do_link_class(ce, Z_STR_P(RT_CONSTANT(opline, opline->op2))) == FAILURE) { + ce = zend_do_link_class(ce, Z_STR_P(RT_CONSTANT(opline, opline->op2)), Z_STR_P(lcname)); + if (!ce) { /* Reload bucket pointer, the hash table may have been reallocated */ zv = zend_hash_find(EG(class_table), Z_STR_P(lcname)); zend_hash_set_bucket_key(EG(class_table), (Bucket *) zv, Z_STR_P(lcname + 1)); @@ -7567,7 +7568,8 @@ ZEND_VM_HANDLER(146, ZEND_DECLARE_ANON_CLASS, ANY, ANY, CACHE_SLOT) ce = Z_CE_P(zv); if (!(ce->ce_flags & ZEND_ACC_LINKED)) { SAVE_OPLINE(); - if (zend_do_link_class(ce, (OP2_TYPE == IS_CONST) ? Z_STR_P(RT_CONSTANT(opline, opline->op2)) : NULL) == FAILURE) { + ce = zend_do_link_class(ce, (OP2_TYPE == IS_CONST) ? Z_STR_P(RT_CONSTANT(opline, opline->op2)) : NULL, rtd_key); + if (!ce) { HANDLE_EXCEPTION(); } } diff --git a/Zend/zend_vm_execute.h b/Zend/zend_vm_execute.h index 2f1f6fc298..f27f3fd5e1 100644 --- a/Zend/zend_vm_execute.h +++ b/Zend/zend_vm_execute.h @@ -2813,7 +2813,8 @@ static ZEND_OPCODE_HANDLER_RET ZEND_FASTCALL ZEND_DECLARE_ANON_CLASS_SPEC_HANDLE ce = Z_CE_P(zv); if (!(ce->ce_flags & ZEND_ACC_LINKED)) { SAVE_OPLINE(); - if (zend_do_link_class(ce, (opline->op2_type == IS_CONST) ? Z_STR_P(RT_CONSTANT(opline, opline->op2)) : NULL) == FAILURE) { + ce = zend_do_link_class(ce, (opline->op2_type == IS_CONST) ? Z_STR_P(RT_CONSTANT(opline, opline->op2)) : NULL, rtd_key); + if (!ce) { HANDLE_EXCEPTION(); } } @@ -6848,7 +6849,7 @@ static ZEND_OPCODE_HANDLER_RET ZEND_FASTCALL ZEND_FETCH_CLASS_CONSTANT_SPEC_CONS } } - zv = zend_hash_find_ex(&ce->constants_table, Z_STR_P(RT_CONSTANT(opline, opline->op2)), 1); + zv = zend_hash_find_ex(CE_CONSTANTS_TABLE(ce), Z_STR_P(RT_CONSTANT(opline, opline->op2)), 1); if (EXPECTED(zv != NULL)) { c = Z_PTR_P(zv); scope = EX(func)->op_array.scope; @@ -7175,7 +7176,8 @@ static ZEND_OPCODE_HANDLER_RET ZEND_FASTCALL ZEND_DECLARE_CLASS_DELAYED_SPEC_CON if (UNEXPECTED(!zv)) { zend_error_noreturn(E_COMPILE_ERROR, "Cannot declare %s %s, because the name is already in use", zend_get_object_type(ce), ZSTR_VAL(ce->name)); } else { - if (zend_do_link_class(ce, Z_STR_P(RT_CONSTANT(opline, opline->op2))) == FAILURE) { + ce = zend_do_link_class(ce, Z_STR_P(RT_CONSTANT(opline, opline->op2)), Z_STR_P(lcname)); + if (!ce) { /* Reload bucket pointer, the hash table may have been reallocated */ zv = zend_hash_find(EG(class_table), Z_STR_P(lcname)); zend_hash_set_bucket_key(EG(class_table), (Bucket *) zv, Z_STR_P(lcname + 1)); @@ -24194,7 +24196,7 @@ static ZEND_OPCODE_HANDLER_RET ZEND_FASTCALL ZEND_FETCH_CLASS_CONSTANT_SPEC_VAR_ } } - zv = zend_hash_find_ex(&ce->constants_table, Z_STR_P(RT_CONSTANT(opline, opline->op2)), 1); + zv = zend_hash_find_ex(CE_CONSTANTS_TABLE(ce), Z_STR_P(RT_CONSTANT(opline, opline->op2)), 1); if (EXPECTED(zv != NULL)) { c = Z_PTR_P(zv); scope = EX(func)->op_array.scope; @@ -32617,7 +32619,7 @@ static ZEND_OPCODE_HANDLER_RET ZEND_FASTCALL ZEND_FETCH_CLASS_CONSTANT_SPEC_UNUS } } - zv = zend_hash_find_ex(&ce->constants_table, Z_STR_P(RT_CONSTANT(opline, opline->op2)), 1); + zv = zend_hash_find_ex(CE_CONSTANTS_TABLE(ce), Z_STR_P(RT_CONSTANT(opline, opline->op2)), 1); if (EXPECTED(zv != NULL)) { c = Z_PTR_P(zv); scope = EX(func)->op_array.scope; |