diff options
41 files changed, 1595 insertions, 1011 deletions
@@ -21,6 +21,9 @@ PHP NEWS . Fixed bug #80330 (Replace language in APIs and source code/docs). (Darek Ćlusarczyk) +- Opcache: + . Added inheritance cache. (Dmitry) + - OpenSSL: . Bump minimal OpenSSL version to 1.0.2. (Jakub Zelenka) 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; diff --git a/ext/opcache/ZendAccelerator.c b/ext/opcache/ZendAccelerator.c index 088bca6ebf..9819e7acdd 100644 --- a/ext/opcache/ZendAccelerator.c +++ b/ext/opcache/ZendAccelerator.c @@ -118,6 +118,8 @@ bool fallback_process = 0; /* process uses file cache fallback */ #endif static zend_op_array *(*accelerator_orig_compile_file)(zend_file_handle *file_handle, int type); +static zend_class_entry* (*accelerator_orig_inheritance_cache_get)(zend_class_entry *ce, zend_class_entry *parent, zend_class_entry **traits_and_interfaces); +static zend_class_entry* (*accelerator_orig_inheritance_cache_add)(zend_class_entry *ce, zend_class_entry *proto, zend_class_entry *parent, zend_class_entry **traits_and_interfaces, HashTable *dependencies); static zend_result (*accelerator_orig_zend_stream_open_function)(const char *filename, zend_file_handle *handle ); static zend_string *(*accelerator_orig_zend_resolve_path)(const char *filename, size_t filename_len); static void (*accelerator_orig_zend_error_cb)(int type, const char *error_filename, const uint32_t error_lineno, zend_string *message); @@ -2249,6 +2251,269 @@ zend_op_array *persistent_compile_file(zend_file_handle *file_handle, int type) return zend_accel_load_script(persistent_script, from_shared_memory); } +static zend_inheritance_cache_entry* zend_accel_inheritance_cache_find(zend_inheritance_cache_entry *entry, zend_class_entry *ce, zend_class_entry *parent, zend_class_entry **traits_and_interfaces, bool *needs_autoload_ptr) +{ + uint32_t i; + + ZEND_ASSERT(ce->ce_flags & ZEND_ACC_IMMUTABLE); + ZEND_ASSERT(!(ce->ce_flags & ZEND_ACC_LINKED)); + + while (entry) { + bool found = 1; + bool needs_autoload = 0; + + if (entry->parent != parent) { + found = 0; + } else { + for (i = 0; i < ce->num_traits + ce->num_interfaces; i++) { + if (entry->traits_and_interfaces[i] != traits_and_interfaces[i]) { + found = 0; + break; + } + } + if (found && entry->dependencies) { + for (i = 0; i < entry->dependencies_count; i++) { + zend_class_entry *ce = zend_lookup_class_ex(entry->dependencies[i].name, NULL, ZEND_FETCH_CLASS_NO_AUTOLOAD); + + if (ce != entry->dependencies[i].ce) { + if (!ce) { + needs_autoload = 1; + } else { + found = 0; + break; + } + } + } + } + } + if (found) { + *needs_autoload_ptr = needs_autoload; + return entry; + } + entry = entry->next; + } + + return NULL; +} + +static zend_class_entry* zend_accel_inheritance_cache_get(zend_class_entry *ce, zend_class_entry *parent, zend_class_entry **traits_and_interfaces) +{ + uint32_t i; + bool needs_autoload; + zend_inheritance_cache_entry *entry = ce->inheritance_cache; + + while (entry) { + entry = zend_accel_inheritance_cache_find(entry, ce, parent, traits_and_interfaces, &needs_autoload); + if (entry) { + if (!needs_autoload) { + zend_map_ptr_extend(ZCSG(map_ptr_last)); + return entry->ce; + } + + for (i = 0; i < entry->dependencies_count; i++) { + zend_class_entry *ce = zend_lookup_class_ex(entry->dependencies[i].name, NULL, 0); + + if (ce == NULL) { + return NULL; + } + } + } + } + + return NULL; +} + +static bool is_array_cacheable(zval *zv) +{ + zval *p; + + ZEND_HASH_FOREACH_VAL(Z_ARR_P(zv), p) { + if (Z_REFCOUNTED_P(p)) { + if (Z_TYPE_P(p) == IS_ARRAY) { + if (!is_array_cacheable(p)) { + /* Can't cache */ + return 0; + } + } else if (Z_TYPE_P(p) == IS_OBJECT || Z_TYPE_P(p) == IS_RESOURCE || Z_TYPE_P(p) == IS_REFERENCE) { + /* Can't cache */ + return 0; + } + } + } ZEND_HASH_FOREACH_END(); + + return 1; +} + +static zend_class_entry* zend_accel_inheritance_cache_add(zend_class_entry *ce, zend_class_entry *proto, zend_class_entry *parent, zend_class_entry **traits_and_interfaces, HashTable *dependencies) +{ + zend_persistent_script dummy; + size_t size; + uint32_t i; + bool needs_autoload; + zend_class_entry *new_ce; + zend_inheritance_cache_entry *entry; + + ZEND_ASSERT(!(ce->ce_flags & ZEND_ACC_IMMUTABLE)); + ZEND_ASSERT(ce->ce_flags & ZEND_ACC_LINKED); + + if (!ZCG(accelerator_enabled) || + (ZCSG(restart_in_progress) && accel_restart_is_active())) { + return NULL; + } + + if (traits_and_interfaces && dependencies) { + for (i = 0; i < proto->num_traits + proto->num_interfaces; i++) { + if (traits_and_interfaces[i]) { + zend_hash_del(dependencies, traits_and_interfaces[i]->name); + } + } + } + + if (ce->ce_flags & ZEND_HAS_STATIC_IN_METHODS) { + zend_op_array *op_array; + zval *zv; + + ZEND_HASH_FOREACH_PTR(&ce->function_table, op_array) { + if (op_array->type == ZEND_USER_FUNCTION + && op_array->static_variables + && !(GC_FLAGS(op_array->static_variables) & IS_ARRAY_IMMUTABLE)) { + if (UNEXPECTED(GC_REFCOUNT(op_array->static_variables) > 1)) { + GC_DELREF(op_array->static_variables); + op_array->static_variables = zend_array_dup(op_array->static_variables); + } + ZEND_HASH_FOREACH_VAL(op_array->static_variables, zv) { + if (Z_ISREF_P(zv)) { + zend_reference *ref = Z_REF_P(zv); + + ZVAL_COPY_VALUE(zv, &ref->val); + if (GC_DELREF(ref) == 0) { + efree_size(ref, sizeof(zend_reference)); + } + } + if (Z_REFCOUNTED_P(zv)) { + if (Z_TYPE_P(zv) == IS_ARRAY) { + if (!is_array_cacheable(zv)) { + /* Can't cache */ + return NULL; + } + SEPARATE_ARRAY(zv); + } else if (Z_TYPE_P(zv) == IS_OBJECT || Z_TYPE_P(zv) == IS_RESOURCE) { + /* Can't cache */ + return NULL; + } + } + } ZEND_HASH_FOREACH_END(); + } + } ZEND_HASH_FOREACH_END(); + } + + SHM_UNPROTECT(); + zend_shared_alloc_lock(); + + entry = ce->inheritance_cache; + while (entry) { + entry = zend_accel_inheritance_cache_find(entry, ce, parent, traits_and_interfaces, &needs_autoload); + if (entry) { + if (!needs_autoload) { + zend_shared_alloc_unlock(); + SHM_PROTECT(); + + zend_map_ptr_extend(ZCSG(map_ptr_last)); + return entry->ce; + } + ZEND_ASSERT(0); // entry = entry->next; // This shouldn't be posible ??? + } + } + + zend_shared_alloc_init_xlat_table(); + + memset(&dummy, 0, sizeof(dummy)); + dummy.size = ZEND_ALIGNED_SIZE( + sizeof(zend_inheritance_cache_entry) - + sizeof(void*) + + (sizeof(void*) * (proto->num_traits + proto->num_interfaces))); + if (dependencies) { + dummy.size += ZEND_ALIGNED_SIZE(zend_hash_num_elements(dependencies) * sizeof(zend_class_dependency)); + } + ZCG(current_persistent_script) = &dummy; + zend_persist_class_entry_calc(ce); + size = dummy.size; + + zend_shared_alloc_clear_xlat_table(); + +#if ZEND_MM_ALIGNMENT < 8 + /* Align to 8-byte boundary */ + ZCG(mem) = zend_shared_alloc(size + 8); +#else + ZCG(mem) = zend_shared_alloc(size); +#endif + + if (!ZCG(mem)) { + zend_shared_alloc_destroy_xlat_table(); + zend_shared_alloc_unlock(); + SHM_PROTECT(); + return NULL; + } + +#if ZEND_MM_ALIGNMENT < 8 + /* Align to 8-byte boundary */ + ZCG(mem) = (void*)(((zend_uintptr_t)ZCG(mem) + 7L) & ~7L); +#endif + + memset(ZCG(mem), 0, size); + entry = (zend_inheritance_cache_entry*)ZCG(mem); + ZCG(mem) = (char*)ZCG(mem) + + ZEND_ALIGNED_SIZE( + (sizeof(zend_inheritance_cache_entry) - + sizeof(void*) + + (sizeof(void*) * (proto->num_traits + proto->num_interfaces)))); + entry->parent = parent; + for (i = 0; i < proto->num_traits + proto->num_interfaces; i++) { + entry->traits_and_interfaces[i] = traits_and_interfaces[i]; + } + if (dependencies && zend_hash_num_elements(dependencies)) { + zend_string *dep_name; + zend_class_entry *dep_ce; + + i = 0; + entry->dependencies_count = zend_hash_num_elements(dependencies); + entry->dependencies = (zend_class_dependency*)ZCG(mem); + ZEND_HASH_FOREACH_STR_KEY_PTR(dependencies, dep_name, dep_ce) { +#if ZEND_DEBUG + ZEND_ASSERT(zend_accel_in_shm(dep_name)); +#endif + entry->dependencies[i].name = dep_name; + entry->dependencies[i].ce = dep_ce; + i++; + } ZEND_HASH_FOREACH_END(); + ZCG(mem) = (char*)ZCG(mem) + zend_hash_num_elements(dependencies) * sizeof(zend_class_dependency); + } + entry->ce = new_ce = zend_persist_class_entry(ce); + zend_update_parent_ce(new_ce); + entry->next = proto->inheritance_cache; + proto->inheritance_cache = entry; + + zend_shared_alloc_destroy_xlat_table(); + + zend_shared_alloc_unlock(); + SHM_PROTECT(); + + /* Consistency check */ + if ((char*)entry + size != (char*)ZCG(mem)) { + zend_accel_error( + ((char*)entry + size < (char*)ZCG(mem)) ? ACCEL_LOG_ERROR : ACCEL_LOG_WARNING, + "Internal error: wrong class size calculation: %s start=" ZEND_ADDR_FMT ", end=" ZEND_ADDR_FMT ", real=" ZEND_ADDR_FMT "\n", + ZSTR_VAL(ce->name), + (size_t)entry, + (size_t)((char *)entry + size), + (size_t)ZCG(mem)); + } + + zend_map_ptr_extend(ZCSG(map_ptr_last)); + + return new_ce; +} + #ifdef ZEND_WIN32 static int accel_gen_uname_id(void) { @@ -3123,7 +3388,19 @@ file_cache_fallback: accel_use_shm_interned_strings(); } - return accel_finish_startup(); + if (accel_finish_startup() != SUCCESS) { + return FAILURE; + } + + if (ZCG(enabled) && accel_startup_ok) { + /* Override inheritance cache callbaks */ + accelerator_orig_inheritance_cache_get = zend_inheritance_cache_get; + accelerator_orig_inheritance_cache_add = zend_inheritance_cache_add; + zend_inheritance_cache_get = zend_accel_inheritance_cache_get; + zend_inheritance_cache_add = zend_accel_inheritance_cache_add; + } + + return SUCCESS; } static void (*orig_post_shutdown_cb)(void); @@ -3170,6 +3447,8 @@ void accel_shutdown(void) } zend_compile_file = accelerator_orig_compile_file; + zend_inheritance_cache_get = accelerator_orig_inheritance_cache_get; + zend_inheritance_cache_add = accelerator_orig_inheritance_cache_add; if ((ini_entry = zend_hash_str_find_ptr(EG(ini_directives), "include_path", sizeof("include_path")-1)) != NULL) { ini_entry->on_modify = orig_include_path_on_modify; @@ -3472,34 +3751,6 @@ try_again: } } -static void get_unresolved_initializer(zend_class_entry *ce, const char **kind, const char **name) { - zend_string *key; - zend_class_constant *c; - zend_property_info *prop; - - *kind = "unknown"; - *name = ""; - - ZEND_HASH_FOREACH_STR_KEY_PTR(&ce->constants_table, key, c) { - if (Z_TYPE(c->value) == IS_CONSTANT_AST) { - *kind = "constant "; - *name = ZSTR_VAL(key); - } - } ZEND_HASH_FOREACH_END(); - ZEND_HASH_FOREACH_STR_KEY_PTR(&ce->properties_info, key, prop) { - zval *val; - if (prop->flags & ZEND_ACC_STATIC) { - val = &ce->default_static_members_table[prop->offset]; - } else { - val = &ce->default_properties_table[OBJ_PROP_TO_NUM(prop->offset)]; - } - if (Z_TYPE_P(val) == IS_CONSTANT_AST) { - *kind = (prop->flags & ZEND_ACC_STATIC) ? "static property $" : "property $"; - *name = ZSTR_VAL(key); - } - } ZEND_HASH_FOREACH_END(); -} - static bool preload_needed_types_known(zend_class_entry *ce); static void get_unlinked_dependency(zend_class_entry *ce, const char **kind, const char **name) { zend_class_entry *p; @@ -3515,16 +3766,6 @@ static void get_unlinked_dependency(zend_class_entry *ce, const char **kind, con *name = ZSTR_VAL(ce->parent_name); return; } - if (!(p->ce_flags & ZEND_ACC_CONSTANTS_UPDATED)) { - *kind = "Parent with unresolved initializers "; - *name = ZSTR_VAL(ce->parent_name); - return; - } - if (!(p->ce_flags & ZEND_ACC_PROPERTY_TYPES_RESOLVED)) { - *kind = "Parent with unresolved property types "; - *name = ZSTR_VAL(ce->parent_name); - return; - } } if (ce->num_interfaces) { @@ -3559,12 +3800,11 @@ static void get_unlinked_dependency(zend_class_entry *ce, const char **kind, con static bool preload_try_resolve_constants(zend_class_entry *ce) { - bool ok, changed; + bool ok, changed, was_changed = 0; zend_class_constant *c; zval *val; EG(exception) = (void*)(uintptr_t)-1; /* prevent error reporting */ - CG(in_compilation) = 1; /* prevent autoloading */ do { ok = 1; changed = 0; @@ -3572,62 +3812,65 @@ static bool preload_try_resolve_constants(zend_class_entry *ce) val = &c->value; if (Z_TYPE_P(val) == IS_CONSTANT_AST) { if (EXPECTED(zval_update_constant_ex(val, c->ce) == SUCCESS)) { - changed = 1; + was_changed = changed = 1; } else { ok = 0; } } } ZEND_HASH_FOREACH_END(); + if (ok) { + ce->ce_flags &= ~ZEND_ACC_HAS_AST_CONSTANTS; + } if (ce->default_properties_count) { uint32_t i; + bool resolved = 1; + for (i = 0; i < ce->default_properties_count; i++) { val = &ce->default_properties_table[i]; if (Z_TYPE_P(val) == IS_CONSTANT_AST) { zend_property_info *prop = ce->properties_info_table[i]; if (UNEXPECTED(zval_update_constant_ex(val, prop->ce) != SUCCESS)) { - ok = 0; + resolved = ok = 0; } } } + if (resolved) { + ce->ce_flags &= ~ZEND_ACC_HAS_AST_PROPERTIES; + } } if (ce->default_static_members_count) { uint32_t count = ce->parent ? ce->default_static_members_count - ce->parent->default_static_members_count : ce->default_static_members_count; + bool resolved = 1; val = ce->default_static_members_table + ce->default_static_members_count - 1; while (count) { if (Z_TYPE_P(val) == IS_CONSTANT_AST) { if (UNEXPECTED(zval_update_constant_ex(val, ce) != SUCCESS)) { - ok = 0; + resolved = ok = 0; } } val--; count--; } + if (resolved) { + ce->ce_flags &= ~ZEND_ACC_HAS_AST_STATICS; + } } } while (changed && !ok); EG(exception) = NULL; CG(in_compilation) = 0; - return ok; + if (ok) { + ce->ce_flags |= ZEND_ACC_CONSTANTS_UPDATED; + } + + return ok || was_changed; } -static zend_class_entry *preload_fetch_resolved_ce(zend_string *name, zend_class_entry *self_ce) { +static zend_class_entry *preload_fetch_resolved_ce(zend_string *name) { zend_string *lcname = zend_string_tolower(name); zend_class_entry *ce = zend_hash_find_ptr(EG(class_table), lcname); zend_string_release(lcname); - if (!ce) { - return NULL; - } - if (ce == self_ce) { - /* Ignore the following requirements if this is the class referring to itself */ - return ce; - } - if (!(ce->ce_flags & ZEND_ACC_CONSTANTS_UPDATED)) { - return NULL; - } - if (!(ce->ce_flags & ZEND_ACC_PROPERTY_TYPES_RESOLVED)) { - return NULL; - } return ce; } @@ -3641,7 +3884,7 @@ static bool preload_try_resolve_property_types(zend_class_entry *ce) ZEND_TYPE_FOREACH(prop->type, single_type) { if (ZEND_TYPE_HAS_NAME(*single_type)) { zend_class_entry *p = - preload_fetch_resolved_ce(ZEND_TYPE_NAME(*single_type), ce); + preload_fetch_resolved_ce(ZEND_TYPE_NAME(*single_type)); if (!p) { ok = 0; continue; @@ -3650,6 +3893,9 @@ static bool preload_try_resolve_property_types(zend_class_entry *ce) } } ZEND_TYPE_FOREACH_END(); } ZEND_HASH_FOREACH_END(); + if (ok) { + ce->ce_flags |= ZEND_ACC_PROPERTY_TYPES_RESOLVED; + } } return ok; @@ -3778,12 +4024,6 @@ static void preload_link(void) parent = zend_hash_find_ptr(EG(class_table), key); zend_string_release(key); if (!parent) continue; - if (!(parent->ce_flags & ZEND_ACC_CONSTANTS_UPDATED)) { - continue; - } - if (!(parent->ce_flags & ZEND_ACC_PROPERTY_TYPES_RESOLVED)) { - continue; - } } if (ce->num_interfaces) { @@ -3794,10 +4034,6 @@ static void preload_link(void) found = 0; break; } - if (!(p->ce_flags & ZEND_ACC_CONSTANTS_UPDATED)) { - found = 0; - break; - } } if (!found) continue; } @@ -3822,11 +4058,8 @@ static void preload_link(void) continue; } - { - zend_string *key = zend_string_tolower(ce->name); - zv = zend_hash_set_bucket_key(EG(class_table), (Bucket*)zv, key); - zend_string_release(key); - } + key = zend_string_tolower(ce->name); + zv = zend_hash_set_bucket_key(EG(class_table), (Bucket*)zv, key); if (EXPECTED(zv)) { /* Set filename & lineno information for inheritance errors */ @@ -3842,7 +4075,8 @@ static void preload_link(void) } else { CG(zend_lineno) = ce->info.user.line_start; } - if (zend_do_link_class(ce, NULL) == FAILURE) { + ce = zend_do_link_class(ce, NULL, key); + if (!ce) { ZEND_ASSERT(0 && "Class linking failed?"); } CG(in_compilation) = 0; @@ -3850,22 +4084,41 @@ static void preload_link(void) changed = 1; } + + zend_string_release(key); } - if (ce->ce_flags & ZEND_ACC_LINKED) { - if (!(ce->ce_flags & ZEND_ACC_CONSTANTS_UPDATED)) { - if ((ce->ce_flags & ZEND_ACC_TRAIT) /* don't update traits */ - || preload_try_resolve_constants(ce)) { - ce->ce_flags |= ZEND_ACC_CONSTANTS_UPDATED; - changed = 1; - } - } + } ZEND_HASH_FOREACH_END(); + } while (changed); + + /* Resolve property types */ + ZEND_HASH_REVERSE_FOREACH_VAL(EG(class_table), zv) { + ce = Z_PTR_P(zv); + if (ce->type == ZEND_INTERNAL_CLASS) { + break; + } + if (!(ce->ce_flags & ZEND_ACC_PROPERTY_TYPES_RESOLVED)) { + if (!(ce->ce_flags & ZEND_ACC_TRAIT)) { + preload_try_resolve_property_types(ce); + } + } + } ZEND_HASH_FOREACH_END(); + - if (!(ce->ce_flags & ZEND_ACC_PROPERTY_TYPES_RESOLVED)) { - if ((ce->ce_flags & ZEND_ACC_TRAIT) /* don't update traits */ - || preload_try_resolve_property_types(ce)) { - ce->ce_flags |= ZEND_ACC_PROPERTY_TYPES_RESOLVED; + do { + changed = 0; + + ZEND_HASH_REVERSE_FOREACH_VAL(EG(class_table), zv) { + ce = Z_PTR_P(zv); + if (ce->type == ZEND_INTERNAL_CLASS) { + break; + } + if (!(ce->ce_flags & ZEND_ACC_CONSTANTS_UPDATED)) { + if (!(ce->ce_flags & ZEND_ACC_TRAIT)) { /* don't update traits */ + CG(in_compilation) = 1; /* prevent autoloading */ + if (preload_try_resolve_constants(ce)) { changed = 1; } + CG(in_compilation) = 0; } } } ZEND_HASH_FOREACH_END(); @@ -3895,18 +4148,6 @@ static void preload_link(void) ZSTR_VAL(ce->name), kind, name); } zend_string_release(key); - } else if (!(ce->ce_flags & ZEND_ACC_CONSTANTS_UPDATED)) { - const char *kind, *name; - get_unresolved_initializer(ce, &kind, &name); - zend_error_at( - E_WARNING, ZSTR_VAL(ce->info.user.filename), ce->info.user.line_start, - "Can't preload class %s with unresolved initializer for %s%s", - ZSTR_VAL(ce->name), kind, name); - } else if (!(ce->ce_flags & ZEND_ACC_PROPERTY_TYPES_RESOLVED)) { - zend_error_at( - E_WARNING, ZSTR_VAL(ce->info.user.filename), ce->info.user.line_start, - "Can't preload class %s with unresolved property types", - ZSTR_VAL(ce->name)); } else { continue; } @@ -3960,7 +4201,7 @@ static inline int preload_update_class_constants(zend_class_entry *ce) { * maybe-uninitialized analysis. */ int result; zend_try { - result = zend_update_class_constants(ce); + result = preload_try_resolve_constants(ce) ? SUCCESS : FAILURE; } zend_catch { result = FAILURE; } zend_end_try(); @@ -3976,13 +4217,6 @@ static zend_class_entry *preload_load_prop_type(zend_property_info *prop, zend_s } else { ce = zend_lookup_class(name); } - if (ce) { - return ce; - } - - zend_error_noreturn(E_ERROR, - "Failed to load class %s used by typed property %s::$%s during preloading", - ZSTR_VAL(name), ZSTR_VAL(prop->ce->name), zend_get_unmangled_property_name(prop->name)); return ce; } @@ -4008,12 +4242,7 @@ static void preload_ensure_classes_loadable() { } if (!(ce->ce_flags & ZEND_ACC_CONSTANTS_UPDATED)) { - if (preload_update_class_constants(ce) == FAILURE) { - zend_error_noreturn(E_ERROR, - "Failed to resolve initializers of class %s during preloading", - ZSTR_VAL(ce->name)); - } - ZEND_ASSERT(ce->ce_flags & ZEND_ACC_CONSTANTS_UPDATED); + preload_update_class_constants(ce); } if (!(ce->ce_flags & ZEND_ACC_PROPERTY_TYPES_RESOLVED)) { @@ -4025,7 +4254,9 @@ static void preload_ensure_classes_loadable() { if (ZEND_TYPE_HAS_NAME(*single_type)) { zend_class_entry *ce = preload_load_prop_type( prop, ZEND_TYPE_NAME(*single_type)); - ZEND_TYPE_SET_CE(*single_type, ce); + if (ce) { + ZEND_TYPE_SET_CE(*single_type, ce); + } } } ZEND_TYPE_FOREACH_END(); } ZEND_HASH_FOREACH_END(); @@ -4558,15 +4789,8 @@ static int accel_preload(const char *config, bool in_child) ZEND_ASSERT(ce->ce_flags & ZEND_ACC_PRELOADED); if (ce->default_static_members_count) { zend_cleanup_internal_class_data(ce); - if (ce->ce_flags & ZEND_ACC_CONSTANTS_UPDATED) { - int i; - - for (i = 0; i < ce->default_static_members_count; i++) { - if (Z_TYPE(ce->default_static_members_table[i]) == IS_CONSTANT_AST) { - ce->ce_flags &= ~ZEND_ACC_CONSTANTS_UPDATED; - break; - } - } + if (ce->ce_flags & ZEND_ACC_HAS_AST_STATICS) { + ce->ce_flags &= ~ZEND_ACC_CONSTANTS_UPDATED; } } if (ce->ce_flags & ZEND_HAS_STATIC_IN_METHODS) { @@ -4701,8 +4925,6 @@ static int accel_preload(const char *config, bool in_child) SHM_PROTECT(); HANDLE_UNBLOCK_INTERRUPTIONS(); - ZEND_ASSERT(ZCSG(preload_script)->arena_size == 0); - preload_load(); /* Store individual scripts with unlinked classes */ diff --git a/ext/opcache/ZendAccelerator.h b/ext/opcache/ZendAccelerator.h index 1d151b982e..d8cf261ed2 100644 --- a/ext/opcache/ZendAccelerator.h +++ b/ext/opcache/ZendAccelerator.h @@ -129,8 +129,6 @@ typedef struct _zend_persistent_script { void *mem; /* shared memory area used by script structures */ size_t size; /* size of used shared memory */ - void *arena_mem; /* part that should be copied into process */ - size_t arena_size; /* All entries that shouldn't be counted in the ADLER32 * checksum must be declared in this struct @@ -227,9 +225,7 @@ typedef struct _zend_accel_globals { #endif /* preallocated shared-memory block to save current script */ void *mem; - void *arena_mem; zend_persistent_script *current_persistent_script; - bool is_immutable_class; /* Temporary storage for warnings before they are moved into persistent_script. */ bool record_warnings; uint32_t num_warnings; diff --git a/ext/opcache/jit/zend_jit_x86.dasc b/ext/opcache/jit/zend_jit_x86.dasc index 5700a92ff1..df496cc1a4 100644 --- a/ext/opcache/jit/zend_jit_x86.dasc +++ b/ext/opcache/jit/zend_jit_x86.dasc @@ -9878,7 +9878,10 @@ static int zend_jit_do_fcall(dasm_State **Dst, const zend_op *opline, const zend if (func && !(func->op_array.fn_flags & ZEND_ACC_CLOSURE)) { if (ZEND_MAP_PTR_IS_OFFSET(func->op_array.run_time_cache)) { | MEM_OP2_2_ZTS add, r2, aword, compiler_globals, map_ptr_base, r1 - } else if (!zend_accel_in_shm(func->op_array.opcodes)) { + } else if ((func->op_array.fn_flags & ZEND_ACC_IMMUTABLE) + && (!func->op_array.scope || (func->op_array.scope->ce_flags & ZEND_ACC_LINKED))) { + | MEM_OP2_2_ZTS add, r2, aword, compiler_globals, map_ptr_base, r1 + } else { /* the called op_array may be not persisted yet */ | test r2, 1 | jz >1 @@ -12611,11 +12614,12 @@ static int zend_jit_recv_init(dasm_State **Dst, const zend_op *opline, const zen return 1; } -static zend_property_info* zend_get_known_property_info(zend_class_entry *ce, zend_string *member, bool on_this, zend_string *filename) +static zend_property_info* zend_get_known_property_info(const zend_op_array *op_array, zend_class_entry *ce, zend_string *member, bool on_this, zend_string *filename) { zend_property_info *info = NULL; - if (!ce || + if ((on_this && (op_array->fn_flags & ZEND_ACC_TRAIT_CLONE)) || + !ce || !(ce->ce_flags & ZEND_ACC_LINKED) || (ce->ce_flags & ZEND_ACC_TRAIT) || ce->create_object) { @@ -12745,7 +12749,7 @@ static int zend_jit_fetch_obj(dasm_State **Dst, member = RT_CONSTANT(opline, opline->op2); ZEND_ASSERT(Z_TYPE_P(member) == IS_STRING && Z_STRVAL_P(member)[0] != '\0'); - prop_info = zend_get_known_property_info(ce, Z_STR_P(member), opline->op1_type == IS_UNUSED, op_array->filename); + prop_info = zend_get_known_property_info(op_array, ce, Z_STR_P(member), opline->op1_type == IS_UNUSED, op_array->filename); if (opline->op1_type == IS_UNUSED || use_this) { | GET_ZVAL_PTR FCARG1a, this_addr @@ -12784,7 +12788,7 @@ static int zend_jit_fetch_obj(dasm_State **Dst, } if (!prop_info && trace_ce && (trace_ce->ce_flags & ZEND_ACC_IMMUTABLE)) { - prop_info = zend_get_known_property_info(trace_ce, Z_STR_P(member), opline->op1_type == IS_UNUSED, op_array->filename); + prop_info = zend_get_known_property_info(op_array, trace_ce, Z_STR_P(member), opline->op1_type == IS_UNUSED, op_array->filename); if (prop_info) { ce = trace_ce; ce_is_instanceof = 0; @@ -13140,7 +13144,7 @@ static int zend_jit_incdec_obj(dasm_State **Dst, member = RT_CONSTANT(opline, opline->op2); ZEND_ASSERT(Z_TYPE_P(member) == IS_STRING && Z_STRVAL_P(member)[0] != '\0'); name = Z_STR_P(member); - prop_info = zend_get_known_property_info(ce, name, opline->op1_type == IS_UNUSED, op_array->filename); + prop_info = zend_get_known_property_info(op_array, ce, name, opline->op1_type == IS_UNUSED, op_array->filename); if (opline->op1_type == IS_UNUSED || use_this) { | GET_ZVAL_PTR FCARG1a, this_addr @@ -13188,7 +13192,7 @@ static int zend_jit_incdec_obj(dasm_State **Dst, } if (!prop_info && trace_ce && (trace_ce->ce_flags & ZEND_ACC_IMMUTABLE)) { - prop_info = zend_get_known_property_info(trace_ce, name, opline->op1_type == IS_UNUSED, op_array->filename); + prop_info = zend_get_known_property_info(op_array, trace_ce, name, opline->op1_type == IS_UNUSED, op_array->filename); if (prop_info) { ce = trace_ce; ce_is_instanceof = 0; @@ -13514,7 +13518,7 @@ static int zend_jit_assign_obj_op(dasm_State **Dst, member = RT_CONSTANT(opline, opline->op2); ZEND_ASSERT(Z_TYPE_P(member) == IS_STRING && Z_STRVAL_P(member)[0] != '\0'); name = Z_STR_P(member); - prop_info = zend_get_known_property_info(ce, name, opline->op1_type == IS_UNUSED, op_array->filename); + prop_info = zend_get_known_property_info(op_array, ce, name, opline->op1_type == IS_UNUSED, op_array->filename); if (opline->op1_type == IS_UNUSED || use_this) { | GET_ZVAL_PTR FCARG1a, this_addr @@ -13571,7 +13575,7 @@ static int zend_jit_assign_obj_op(dasm_State **Dst, } if (!prop_info && trace_ce && (trace_ce->ce_flags & ZEND_ACC_IMMUTABLE)) { - prop_info = zend_get_known_property_info(trace_ce, name, opline->op1_type == IS_UNUSED, op_array->filename); + prop_info = zend_get_known_property_info(op_array, trace_ce, name, opline->op1_type == IS_UNUSED, op_array->filename); if (prop_info) { ce = trace_ce; ce_is_instanceof = 0; @@ -13847,7 +13851,7 @@ static int zend_jit_assign_obj(dasm_State **Dst, member = RT_CONSTANT(opline, opline->op2); ZEND_ASSERT(Z_TYPE_P(member) == IS_STRING && Z_STRVAL_P(member)[0] != '\0'); name = Z_STR_P(member); - prop_info = zend_get_known_property_info(ce, name, opline->op1_type == IS_UNUSED, op_array->filename); + prop_info = zend_get_known_property_info(op_array, ce, name, opline->op1_type == IS_UNUSED, op_array->filename); if (opline->op1_type == IS_UNUSED || use_this) { | GET_ZVAL_PTR FCARG1a, this_addr @@ -13903,7 +13907,7 @@ static int zend_jit_assign_obj(dasm_State **Dst, } if (!prop_info && trace_ce && (trace_ce->ce_flags & ZEND_ACC_IMMUTABLE)) { - prop_info = zend_get_known_property_info(trace_ce, name, opline->op1_type == IS_UNUSED, op_array->filename); + prop_info = zend_get_known_property_info(op_array, trace_ce, name, opline->op1_type == IS_UNUSED, op_array->filename); if (prop_info) { ce = trace_ce; ce_is_instanceof = 0; diff --git a/ext/opcache/tests/bug78014.phpt b/ext/opcache/tests/bug78014.phpt index 3b4d332828..63fc1b6db1 100644 --- a/ext/opcache/tests/bug78014.phpt +++ b/ext/opcache/tests/bug78014.phpt @@ -12,18 +12,8 @@ if (PHP_OS_FAMILY == 'Windows') die('skip Preloading is not supported on Windows ?> --FILE-- <?php -class B extends A { - function foo(): int { return 24; } -} $c = new C; var_dump($c->foo()); ?> ---EXPECTF-- -Warning: Can't preload unlinked class C: Parent with unresolved initializers B in %s on line %d - -Warning: Can't preload class B with unresolved initializer for constant X in %s on line %d - -Fatal error: Uncaught Error: Class "C" not found in %sbug78014.php:5 -Stack trace: -#0 {main} - thrown in %sbug78014.php on line 5 +--EXPECT-- +int(42) diff --git a/ext/opcache/tests/preload_004.phpt b/ext/opcache/tests/preload_004.phpt index f4dd45a062..c8006dae1c 100644 --- a/ext/opcache/tests/preload_004.phpt +++ b/ext/opcache/tests/preload_004.phpt @@ -9,13 +9,16 @@ opcache.preload={PWD}/preload_undef_const.inc <?php require_once('skipif.inc'); if (PHP_OS_FAMILY == 'Windows') die('skip Preloading is not supported on Windows'); -if (getenv('SKIP_ASAN')) die('xfail Startup failure leak'); ?> --FILE-- <?php var_dump(class_exists('Foo')); +try { + new Foo(); +} catch (Throwable $ex) { + echo $ex->getMessage() . "\n"; +} ?> --EXPECT-- -Fatal error: Undefined constant self::DOES_NOT_EXIST in Unknown on line 0 - -Fatal error: Failed to resolve initializers of class Foo during preloading in Unknown on line 0 +bool(true) +Undefined constant self::DOES_NOT_EXIST diff --git a/ext/opcache/tests/preload_009.phpt b/ext/opcache/tests/preload_009.phpt index a13b504cd1..88d0df5fdd 100644 --- a/ext/opcache/tests/preload_009.phpt +++ b/ext/opcache/tests/preload_009.phpt @@ -9,14 +9,18 @@ opcache.preload={PWD}/preload_undef_const_2.inc <?php require_once('skipif.inc'); if (PHP_OS_FAMILY == 'Windows') die('skip Preloading is not supported on Windows'); -if (getenv('SKIP_ASAN')) die('xfail Startup failure leak'); ?> --FILE-- <?php var_dump(trait_exists('T')); var_dump(class_exists('Foo')); +try { + new Foo(); +} catch (Throwable $ex) { + echo $ex->getMessage() . "\n"; +} ?> --EXPECT-- -Fatal error: Undefined constant "UNDEF" in Unknown on line 0 - -Fatal error: Failed to resolve initializers of class Foo during preloading in Unknown on line 0 +bool(true) +bool(true) +Undefined constant "UNDEF" diff --git a/ext/opcache/tests/preload_012.phpt b/ext/opcache/tests/preload_012.phpt index 7c5ad9aa1a..0ca2ae15b9 100644 --- a/ext/opcache/tests/preload_012.phpt +++ b/ext/opcache/tests/preload_012.phpt @@ -11,5 +11,16 @@ require_once('skipif.inc'); if (PHP_OS_FAMILY == 'Windows') die('skip Preloading is not supported on Windows'); ?> --FILE-- ---EXPECTF-- -Warning: Can't preload class Test with unresolved initializer for constant C in %s on line %d +<?php +class Foo { +} +var_dump(class_exists('Test')); +try { + new Test(); +} catch (Throwable $ex) { + echo $ex->getMessage() . "\n"; +} +?> +--EXPECT-- +bool(true) +Undefined constant Foo::BAR diff --git a/ext/opcache/tests/preload_ind.phpt b/ext/opcache/tests/preload_ind.phpt index 158fc15ab6..4f0c625b08 100644 --- a/ext/opcache/tests/preload_ind.phpt +++ b/ext/opcache/tests/preload_ind.phpt @@ -12,6 +12,5 @@ if (PHP_OS_FAMILY == 'Windows') die('skip Preloading is not supported on Windows ?> --FILE-- OK ---EXPECTF-- -Warning: Can't preload class C with unresolved property types in %s on line %d +--EXPECT-- OK diff --git a/ext/opcache/tests/preload_loadable_classes_2.phpt b/ext/opcache/tests/preload_loadable_classes_2.phpt index 665f3157e2..8d4e8e3712 100644 --- a/ext/opcache/tests/preload_loadable_classes_2.phpt +++ b/ext/opcache/tests/preload_loadable_classes_2.phpt @@ -9,11 +9,17 @@ opcache.preload={PWD}/preload_loadable_classes_2.inc <?php require_once('skipif.inc'); if (PHP_OS_FAMILY == 'Windows') die('skip Preloading is not supported on Windows'); -if (getenv('SKIP_ASAN')) die('xfail Startup failure leak'); ?> --FILE-- -Unreachable +<?php +const UNDEF = 1; +class Foo { + const UNDEF = 2; +} +var_dump(class_exists("Test")); +var_dump(Test::X); +var_dump(Test::Y); --EXPECT-- -Fatal error: Undefined constant "UNDEF" in Unknown on line 0 - -Fatal error: Failed to resolve initializers of class Test during preloading in Unknown on line 0 +bool(true) +int(1) +int(2) diff --git a/ext/opcache/tests/preload_loadable_classes_3.phpt b/ext/opcache/tests/preload_loadable_classes_3.phpt index 97309a6fcc..b12cc9d3cd 100644 --- a/ext/opcache/tests/preload_loadable_classes_3.phpt +++ b/ext/opcache/tests/preload_loadable_classes_3.phpt @@ -9,9 +9,15 @@ opcache.preload={PWD}/preload_loadable_classes_3.inc <?php require_once('skipif.inc'); if (PHP_OS_FAMILY == 'Windows') die('skip Preloading is not supported on Windows'); -if (getenv('SKIP_ASAN')) die('xfail Startup failure leak'); ?> --FILE-- -Unreachable +<?php +class Foo { +} +var_dump(new Test); +?> --EXPECT-- -Fatal error: Failed to load class Foo used by typed property Test::$prop during preloading in Unknown on line 0 +object(Test)#1 (0) { + ["prop":protected]=> + uninitialized(Foo) +} diff --git a/ext/opcache/tests/preload_unresolved_prop_type.phpt b/ext/opcache/tests/preload_unresolved_prop_type.phpt index 3e2accd19e..9e0b3570ee 100644 --- a/ext/opcache/tests/preload_unresolved_prop_type.phpt +++ b/ext/opcache/tests/preload_unresolved_prop_type.phpt @@ -11,7 +11,12 @@ require_once('skipif.inc'); if (PHP_OS_FAMILY == 'Windows') die('skip Preloading is not supported on Windows'); ?> --FILE-- +<?php +class Unknown { +} +$x = new Test; +$x->prop = new Unknown; +?> ===DONE=== ---EXPECTF-- -Warning: Can't preload class Test with unresolved property types in %s on line %d +--EXPECT-- ===DONE=== diff --git a/ext/opcache/zend_accelerator_util_funcs.c b/ext/opcache/zend_accelerator_util_funcs.c index d3011da941..f17f7e8076 100644 --- a/ext/opcache/zend_accelerator_util_funcs.c +++ b/ext/opcache/zend_accelerator_util_funcs.c @@ -25,13 +25,6 @@ #include "zend_persist.h" #include "zend_shared_alloc.h" -#define IN_ARENA(ptr) \ - ((void*)(ptr) >= ZCG(current_persistent_script)->arena_mem && \ - (void*)(ptr) < (void*)((char*)ZCG(current_persistent_script)->arena_mem + ZCG(current_persistent_script)->arena_size)) - -#define ARENA_REALLOC(ptr) \ - (void*)(((char*)(ptr)) + ((char*)ZCG(arena_mem) - (char*)ZCG(current_persistent_script)->arena_mem)) - typedef int (*id_function_t)(void *, void *); typedef void (*unique_copy_ctor_func_t)(void *pElement); @@ -131,238 +124,6 @@ void zend_accel_move_user_classes(HashTable *src, uint32_t count, zend_script *s src->pDestructor = orig_dtor; } -static void zend_hash_clone_constants(HashTable *ht) -{ - Bucket *p, *end; - zend_class_constant *c; - - if (HT_FLAGS(ht) & HASH_FLAG_UNINITIALIZED) { - return; - } - - p = emalloc(HT_SIZE(ht)); - memcpy(p, HT_GET_DATA_ADDR(ht), HT_USED_SIZE(ht)); - HT_SET_DATA_ADDR(ht, p); - - p = ht->arData; - end = p + ht->nNumUsed; - for (; p != end; p++) { - ZEND_ASSERT(Z_TYPE(p->val) != IS_UNDEF); - c = Z_PTR(p->val); - if (IN_ARENA(c)) { - c = ARENA_REALLOC(c); - Z_PTR(p->val) = c; - - if (IN_ARENA(c->ce)) { - c->ce = ARENA_REALLOC(c->ce); - } - } - } -} - -static void zend_hash_clone_methods(HashTable *ht) -{ - Bucket *p, *end; - zend_op_array *new_entry; - - ht->pDestructor = ZEND_FUNCTION_DTOR; - - if (HT_FLAGS(ht) & HASH_FLAG_UNINITIALIZED) { - return; - } - - p = emalloc(HT_SIZE(ht)); - memcpy(p, HT_GET_DATA_ADDR(ht), HT_USED_SIZE(ht)); - HT_SET_DATA_ADDR(ht, p); - - p = ht->arData; - end = p + ht->nNumUsed; - for (; p != end; p++) { - ZEND_ASSERT(Z_TYPE(p->val) != IS_UNDEF); - new_entry = Z_PTR(p->val); - if (IN_ARENA(new_entry)) { - new_entry = ARENA_REALLOC(new_entry); - Z_PTR(p->val) = new_entry; - - if (IN_ARENA(new_entry->scope)) { - new_entry->scope = ARENA_REALLOC(new_entry->scope); - - /* update prototype */ - if (IN_ARENA(new_entry->prototype)) { - new_entry->prototype = ARENA_REALLOC(new_entry->prototype); - } - } - if (IN_ARENA(ZEND_MAP_PTR(new_entry->run_time_cache))) { - ZEND_MAP_PTR_INIT(new_entry->run_time_cache, ARENA_REALLOC(ZEND_MAP_PTR(new_entry->run_time_cache))); - } - ZEND_MAP_PTR_INIT(new_entry->static_variables_ptr, &new_entry->static_variables); - } - } -} - -static void zend_hash_clone_prop_info(HashTable *ht) -{ - Bucket *p, *end; - zend_property_info *prop_info; - - if (HT_FLAGS(ht) & HASH_FLAG_UNINITIALIZED) { - return; - } - - p = emalloc(HT_SIZE(ht)); - memcpy(p, HT_GET_DATA_ADDR(ht), HT_USED_SIZE(ht)); - HT_SET_DATA_ADDR(ht, p); - - p = ht->arData; - end = p + ht->nNumUsed; - for (; p != end; p++) { - ZEND_ASSERT(Z_TYPE(p->val) != IS_UNDEF); - prop_info = Z_PTR(p->val); - if (IN_ARENA(prop_info)) { - prop_info = ARENA_REALLOC(prop_info); - Z_PTR(p->val) = prop_info; - - if (IN_ARENA(prop_info->ce)) { - prop_info->ce = ARENA_REALLOC(prop_info->ce); - } - - if (ZEND_TYPE_HAS_LIST(prop_info->type)) { - zend_type_list *list = ZEND_TYPE_LIST(prop_info->type); - if (IN_ARENA(list)) { - list = ARENA_REALLOC(list); - ZEND_TYPE_SET_PTR(prop_info->type, list); - - zend_type *list_type; - ZEND_TYPE_LIST_FOREACH(ZEND_TYPE_LIST(prop_info->type), list_type) { - if (ZEND_TYPE_HAS_CE(*list_type)) { - zend_class_entry *ce = ZEND_TYPE_CE(*list_type); - if (IN_ARENA(ce)) { - ce = ARENA_REALLOC(ce); - ZEND_TYPE_SET_PTR(*list_type, ce); - } - } - } ZEND_TYPE_LIST_FOREACH_END(); - } - } else if (ZEND_TYPE_HAS_CE(prop_info->type)) { - zend_class_entry *ce = ZEND_TYPE_CE(prop_info->type); - if (IN_ARENA(ce)) { - ce = ARENA_REALLOC(ce); - ZEND_TYPE_SET_PTR(prop_info->type, ce); - } - } - } - } -} - -#define zend_update_inherited_handler(handler) \ -{ \ - if (ce->handler != NULL && IN_ARENA(ce->handler)) { \ - ce->handler = ARENA_REALLOC(ce->handler); \ - } \ -} - -/* Protects class' refcount, copies default properties, functions and class name */ -static void zend_class_copy_ctor(zend_class_entry **pce) -{ - zend_class_entry *ce = *pce; - zval *src, *dst, *end; - - *pce = ce = ARENA_REALLOC(ce); - ce->refcount = 1; - - if ((ce->ce_flags & ZEND_ACC_LINKED) && IN_ARENA(ce->parent)) { - ce->parent = ARENA_REALLOC(ce->parent); - } - - if (ce->default_properties_table) { - dst = emalloc(sizeof(zval) * ce->default_properties_count); - src = ce->default_properties_table; - end = src + ce->default_properties_count; - ce->default_properties_table = dst; - for (; src != end; src++, dst++) { - ZVAL_COPY_VALUE_PROP(dst, src); - } - } - - zend_hash_clone_methods(&ce->function_table); - - /* static members */ - if (ce->default_static_members_table) { - int i, end; - zend_class_entry *parent = !(ce->ce_flags & ZEND_ACC_LINKED) ? NULL : ce->parent; - - dst = emalloc(sizeof(zval) * ce->default_static_members_count); - src = ce->default_static_members_table; - ce->default_static_members_table = dst; - i = ce->default_static_members_count - 1; - - /* Copy static properties in this class */ - end = parent ? parent->default_static_members_count : 0; - for (; i >= end; i--) { - zval *p = &dst[i]; - ZVAL_COPY_VALUE(p, &src[i]); - } - - /* Create indirections to static properties from parent classes */ - while (parent && parent->default_static_members_table) { - end = parent->parent ? parent->parent->default_static_members_count : 0; - for (; i >= end; i--) { - zval *p = &dst[i]; - ZVAL_INDIRECT(p, &parent->default_static_members_table[i]); - } - - parent = parent->parent; - } - } - ZEND_MAP_PTR_INIT(ce->static_members_table, &ce->default_static_members_table); - - /* properties_info */ - zend_hash_clone_prop_info(&ce->properties_info); - - /* constants table */ - zend_hash_clone_constants(&ce->constants_table); - - if (ce->properties_info_table) { - int i; - ce->properties_info_table = ARENA_REALLOC(ce->properties_info_table); - for (i = 0; i < ce->default_properties_count; i++) { - if (IN_ARENA(ce->properties_info_table[i])) { - ce->properties_info_table[i] = ARENA_REALLOC(ce->properties_info_table[i]); - } - } - } - - if (ce->num_interfaces) { - if (ce->ce_flags & ZEND_ACC_LINKED) { - zend_class_entry **interfaces = emalloc(sizeof(zend_class_entry*) * ce->num_interfaces); - uint32_t i; - - for (i = 0; i < ce->num_interfaces; i++) { - if (IN_ARENA(ce->interfaces[i])) { - interfaces[i] = ARENA_REALLOC(ce->interfaces[i]); - } else { - interfaces[i] = ce->interfaces[i]; - } - } - ce->interfaces = interfaces; - } - } - - 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 void zend_accel_function_hash_copy(HashTable *target, HashTable *source) { zend_function *function1, *function2; @@ -452,196 +213,6 @@ static void zend_accel_class_hash_copy(HashTable *target, HashTable *source) return; } -static void zend_accel_class_hash_copy_from_shm(HashTable *target, HashTable *source) -{ - Bucket *p, *end; - zval *t; - - zend_hash_extend(target, target->nNumUsed + source->nNumUsed, 0); - p = source->arData; - end = p + source->nNumUsed; - for (; p != end; p++) { - ZEND_ASSERT(Z_TYPE(p->val) != IS_UNDEF); - ZEND_ASSERT(p->key); - t = zend_hash_find_ex(target, p->key, 1); - if (UNEXPECTED(t != NULL)) { - if (EXPECTED(ZSTR_LEN(p->key) > 0) && EXPECTED(ZSTR_VAL(p->key)[0] == 0)) { - /* See comment in zend_accel_function_hash_copy(). */ - continue; - } else if (UNEXPECTED(!ZCG(accel_directives).ignore_dups)) { - zend_class_entry *ce1 = Z_PTR(p->val); - if (!(ce1->ce_flags & ZEND_ACC_ANON_CLASS)) { - CG(in_compilation) = 1; - zend_set_compiled_filename(ce1->info.user.filename); - CG(zend_lineno) = ce1->info.user.line_start; - zend_error(E_ERROR, - "Cannot declare %s %s, because the name is already in use", - zend_get_object_type(ce1), ZSTR_VAL(ce1->name)); - return; - } - continue; - } - } else { - t = _zend_hash_append_ptr_ex(target, p->key, Z_PTR(p->val), 1); - if (!(((zend_class_entry*)Z_PTR_P(t))->ce_flags & ZEND_ACC_IMMUTABLE)) { - zend_class_copy_ctor((zend_class_entry**)&Z_PTR_P(t)); - } - } - } - target->nInternalPointer = 0; - return; -} - -#if __has_feature(memory_sanitizer) -# define fast_memcpy memcpy -#elif defined(__AVX__) -# include <nmmintrin.h> -# if defined(__GNUC__) && defined(__i386__) -static zend_always_inline void fast_memcpy(void *dest, const void *src, size_t size) -{ - size_t delta = (char*)dest - (char*)src; - - __asm__ volatile ( - ".align 16\n\t" - ".LL0%=:\n\t" - "prefetchnta 0x40(%1)\n\t" - "vmovaps (%1), %%ymm0\n\t" - "vmovaps 0x20(%1), %%ymm1\n\t" - "vmovaps %%ymm0, (%1,%2)\n\t" - "vmovaps %%ymm1, 0x20(%1,%2)\n\t" - "addl $0x40, %1\n\t" - "subl $0x40, %0\n\t" - "ja .LL0%=" - : "+r"(size), - "+r"(src) - : "r"(delta) - : "cc", "memory", "%ymm0", "%ymm1"); -} -# elif defined(__GNUC__) && defined(__x86_64__) -static zend_always_inline void fast_memcpy(void *dest, const void *src, size_t size) -{ - size_t delta = (char*)dest - (char*)src; - - __asm__ volatile ( - ".align 16\n\t" - ".LL0%=:\n\t" - "prefetchnta 0x40(%1)\n\t" - "vmovaps (%1), %%ymm0\n\t" - "vmovaps 0x20(%1), %%ymm1\n\t" - "vmovaps %%ymm0, (%1,%2)\n\t" - "vmovaps %%ymm1, 0x20(%1,%2)\n\t" - "addq $0x40, %1\n\t" - "subq $0x40, %0\n\t" - "ja .LL0%=" - : "+r"(size), - "+r"(src) - : "r"(delta) - : "cc", "memory", "%ymm0", "%ymm1"); -} -# else -static zend_always_inline void fast_memcpy(void *dest, const void *src, size_t size) -{ - __m256 *dqdest = (__m256*)dest; - const __m256 *dqsrc = (const __m256*)src; - const __m256 *end = (const __m256*)((const char*)src + size); - - do { -#ifdef PHP_WIN32 - _mm_prefetch((const char *)(dqsrc + 2), _MM_HINT_NTA); -#else - _mm_prefetch(dqsrc + 2, _MM_HINT_NTA); -#endif - - __m256 ymm0 = _mm256_load_ps((const float *)(dqsrc + 0)); - __m256 ymm1 = _mm256_load_ps((const float *)(dqsrc + 1)); - dqsrc += 2; - _mm256_store_ps((float *)(dqdest + 0), ymm0); - _mm256_store_ps((float *)(dqdest + 1), ymm1); - dqdest += 2; - } while (dqsrc != end); -} -# endif -#elif defined(__SSE2__) -# include <emmintrin.h> -# if defined(__GNUC__) && defined(__i386__) -static zend_always_inline void fast_memcpy(void *dest, const void *src, size_t size) -{ - size_t delta = (char*)dest - (char*)src; - - __asm__ volatile ( - ".align 16\n\t" - ".LL0%=:\n\t" - "prefetchnta 0x40(%1)\n\t" - "movdqa (%1), %%xmm0\n\t" - "movdqa 0x10(%1), %%xmm1\n\t" - "movdqa 0x20(%1), %%xmm2\n\t" - "movdqa 0x30(%1), %%xmm3\n\t" - "movdqa %%xmm0, (%1,%2)\n\t" - "movdqa %%xmm1, 0x10(%1,%2)\n\t" - "movdqa %%xmm2, 0x20(%1,%2)\n\t" - "movdqa %%xmm3, 0x30(%1,%2)\n\t" - "addl $0x40, %1\n\t" - "subl $0x40, %0\n\t" - "ja .LL0%=" - : "+r"(size), - "+r"(src) - : "r"(delta) - : "cc", "memory", "%xmm0", "%xmm1", "%xmm1", "%xmm2"); -} -# elif defined(__GNUC__) && defined(__x86_64__) && !defined(__ILP32__) -static zend_always_inline void fast_memcpy(void *dest, const void *src, size_t size) -{ - size_t delta = (char*)dest - (char*)src; - - __asm__ volatile ( - ".align 16\n\t" - ".LL0%=:\n\t" - "prefetchnta 0x40(%1)\n\t" - "movdqa (%1), %%xmm0\n\t" - "movdqa 0x10(%1), %%xmm1\n\t" - "movdqa 0x20(%1), %%xmm2\n\t" - "movdqa 0x30(%1), %%xmm3\n\t" - "movdqa %%xmm0, (%1,%2)\n\t" - "movdqa %%xmm1, 0x10(%1,%2)\n\t" - "movdqa %%xmm2, 0x20(%1,%2)\n\t" - "movdqa %%xmm3, 0x30(%1,%2)\n\t" - "addq $0x40, %1\n\t" - "subq $0x40, %0\n\t" - "ja .LL0%=" - : "+r"(size), - "+r"(src) - : "r"(delta) - : "cc", "memory", "%xmm0", "%xmm1", "%xmm1", "%xmm2"); -} -# else -static zend_always_inline void fast_memcpy(void *dest, const void *src, size_t size) -{ - __m128i *dqdest = (__m128i*)dest; - const __m128i *dqsrc = (const __m128i*)src; - const __m128i *end = (const __m128i*)((const char*)src + size); - - do { -#ifdef PHP_WIN32 - _mm_prefetch((const char *)(dqsrc + 4), _MM_HINT_NTA); -#else - _mm_prefetch(dqsrc + 4, _MM_HINT_NTA); -#endif - - __m128i xmm0 = _mm_load_si128(dqsrc + 0); - __m128i xmm1 = _mm_load_si128(dqsrc + 1); - __m128i xmm2 = _mm_load_si128(dqsrc + 2); - __m128i xmm3 = _mm_load_si128(dqsrc + 3); - dqsrc += 4; - _mm_store_si128(dqdest + 0, xmm0); - _mm_store_si128(dqdest + 1, xmm1); - _mm_store_si128(dqdest + 2, xmm2); - _mm_store_si128(dqdest + 3, xmm3); - dqdest += 4; - } while (dqsrc != end); -} -# endif -#endif - zend_op_array* zend_accel_load_script(zend_persistent_script *persistent_script, int from_shared_memory) { zend_op_array *op_array; @@ -654,27 +225,11 @@ zend_op_array* zend_accel_load_script(zend_persistent_script *persistent_script, zend_accel_function_hash_copy(CG(function_table), &persistent_script->script.function_table); } - if (EXPECTED(from_shared_memory)) { - ZCG(current_persistent_script) = persistent_script; - ZCG(arena_mem) = NULL; - if (EXPECTED(persistent_script->arena_size)) { -#if defined(__AVX__) || defined(__SSE2__) - /* Target address must be aligned to 64-byte boundary */ - _mm_prefetch(persistent_script->arena_mem, _MM_HINT_NTA); - ZCG(arena_mem) = zend_arena_alloc(&CG(arena), persistent_script->arena_size + 64); - ZCG(arena_mem) = (void*)(((zend_uintptr_t)ZCG(arena_mem) + 63L) & ~63L); - fast_memcpy(ZCG(arena_mem), persistent_script->arena_mem, persistent_script->arena_size); -#else - ZCG(arena_mem) = zend_arena_alloc(&CG(arena), persistent_script->arena_size); - memcpy(ZCG(arena_mem), persistent_script->arena_mem, persistent_script->arena_size); -#endif - } - - /* Copy all the necessary stuff from shared memory to regular memory, and protect the shared script */ - if (zend_hash_num_elements(&persistent_script->script.class_table) > 0) { - zend_accel_class_hash_copy_from_shm(CG(class_table), &persistent_script->script.class_table); - } + if (zend_hash_num_elements(&persistent_script->script.class_table) > 0) { + zend_accel_class_hash_copy(CG(class_table), &persistent_script->script.class_table); + } + if (EXPECTED(from_shared_memory)) { /* Register __COMPILER_HALT_OFFSET__ constant */ if (persistent_script->compiler_halt_offset != 0 && persistent_script->script.filename) { @@ -688,12 +243,7 @@ zend_op_array* zend_accel_load_script(zend_persistent_script *persistent_script, zend_string_release_ex(name, 0); } - ZCG(current_persistent_script) = NULL; zend_map_ptr_extend(ZCSG(map_ptr_last)); - } else /* if (!from_shared_memory) */ { - if (zend_hash_num_elements(&persistent_script->script.class_table) > 0) { - zend_accel_class_hash_copy(CG(class_table), &persistent_script->script.class_table); - } } if (persistent_script->script.first_early_binding_opline != (uint32_t)-1) { diff --git a/ext/opcache/zend_file_cache.c b/ext/opcache/zend_file_cache.c index 62a2d261ff..56a26eec5d 100644 --- a/ext/opcache/zend_file_cache.c +++ b/ext/opcache/zend_file_cache.c @@ -450,6 +450,9 @@ static void zend_file_cache_serialize_op_array(zend_op_array *op_arra zend_file_cache_metainfo *info, void *buf) { + ZEND_MAP_PTR_INIT(op_array->static_variables_ptr, NULL); + ZEND_MAP_PTR_INIT(op_array->run_time_cache, NULL); + /* Check whether this op_array has already been serialized. */ if (IS_SERIALIZED(op_array->opcodes)) { ZEND_ASSERT(op_array->scope && "Only method op_arrays should be shared"); @@ -465,13 +468,6 @@ static void zend_file_cache_serialize_op_array(zend_op_array *op_arra zend_file_cache_serialize_hash(ht, script, info, buf, zend_file_cache_serialize_zval); } - ZEND_MAP_PTR_INIT(op_array->static_variables_ptr, &op_array->static_variables); - if (op_array->fn_flags & ZEND_ACC_IMMUTABLE) { - ZEND_MAP_PTR_INIT(op_array->run_time_cache, NULL); - } else { - SERIALIZE_PTR(ZEND_MAP_PTR(op_array->run_time_cache)); - } - if (op_array->scope) { if (UNEXPECTED(zend_shared_alloc_get_xlat_entry(op_array->opcodes))) { op_array->refcount = (uint32_t*)(intptr_t)-1; @@ -855,7 +851,8 @@ static void zend_file_cache_serialize_class(zval *zv, SERIALIZE_PTR(ce->iterator_funcs_ptr); } - ZEND_MAP_PTR_INIT(ce->static_members_table, &ce->default_static_members_table); + ZEND_MAP_PTR_INIT(ce->static_members_table, NULL); + ZEND_MAP_PTR_INIT(ce->mutable_data, NULL); } static void zend_file_cache_serialize_warnings( @@ -901,7 +898,6 @@ static void zend_file_cache_serialize(zend_persistent_script *script, zend_file_cache_serialize_op_array(&new_script->script.main_op_array, script, info, buf); zend_file_cache_serialize_warnings(new_script, info, buf); - SERIALIZE_PTR(new_script->arena_mem); new_script->mem = NULL; } @@ -1209,6 +1205,13 @@ static void zend_file_cache_unserialize_type( zend_string *type_name = ZEND_TYPE_NAME(*type); UNSERIALIZE_STR(type_name); ZEND_TYPE_SET_PTR(*type, type_name); + if (!(script->corrupted)) { + // TODO: we may use single map_ptr slot for each class name ??? + type->type_mask |= _ZEND_TYPE_CACHE_BIT; + type->ce_cache__ptr = (uint32_t)(uintptr_t)zend_map_ptr_new(); + } else { + type->type_mask &= ~_ZEND_TYPE_CACHE_BIT; + } } else if (ZEND_TYPE_HAS_CE(*type)) { zend_class_entry *ce = ZEND_TYPE_CE(*type); UNSERIALIZE_PTR(ce); @@ -1220,6 +1223,26 @@ static void zend_file_cache_unserialize_op_array(zend_op_array *op_arr zend_persistent_script *script, void *buf) { + if (!(script->corrupted) + && op_array != &script->script.main_op_array) { + op_array->fn_flags |= ZEND_ACC_IMMUTABLE; + if (op_array->static_variables) { + ZEND_MAP_PTR_NEW(op_array->static_variables_ptr); + } else { + ZEND_MAP_PTR_INIT(op_array->static_variables_ptr, &op_array->static_variables); + } + ZEND_MAP_PTR_NEW(op_array->run_time_cache); + } else { + op_array->fn_flags &= ~ZEND_ACC_IMMUTABLE; + ZEND_MAP_PTR_INIT(op_array->static_variables_ptr, &op_array->static_variables); + if (op_array != &script->script.main_op_array) { + ZEND_MAP_PTR_INIT(op_array->run_time_cache, zend_arena_alloc(&CG(arena), sizeof(void*))); + ZEND_MAP_PTR_SET(op_array->run_time_cache, NULL); + } else { + ZEND_MAP_PTR_INIT(op_array->run_time_cache, NULL); + } + } + /* Check whether this op_array has already been unserialized. */ if (IS_UNSERIALIZED(op_array->opcodes)) { ZEND_ASSERT(op_array->scope && "Only method op_arrays should be shared"); @@ -1235,26 +1258,6 @@ static void zend_file_cache_unserialize_op_array(zend_op_array *op_arr script, buf, zend_file_cache_unserialize_zval, ZVAL_PTR_DTOR); } - if (op_array->fn_flags & ZEND_ACC_IMMUTABLE) { - if (op_array->static_variables) { - ZEND_MAP_PTR_NEW(op_array->static_variables_ptr); - } else { - ZEND_MAP_PTR_INIT(op_array->static_variables_ptr, &op_array->static_variables); - } - ZEND_MAP_PTR_NEW(op_array->run_time_cache); - } else { - ZEND_MAP_PTR_INIT(op_array->static_variables_ptr, &op_array->static_variables); - if (ZEND_MAP_PTR(op_array->run_time_cache)) { - if (script->corrupted) { - /* Not in SHM: Use serialized arena pointer. */ - UNSERIALIZE_PTR(ZEND_MAP_PTR(op_array->run_time_cache)); - } else { - /* In SHM: Allocate new pointer. */ - ZEND_MAP_PTR_NEW(op_array->run_time_cache); - } - } - } - if (op_array->refcount) { op_array->refcount = NULL; UNSERIALIZE_PTR(op_array->literals); @@ -1605,10 +1608,20 @@ static void zend_file_cache_unserialize_class(zval *zv, UNSERIALIZE_PTR(ce->iterator_funcs_ptr->zf_next); } - if (ce->ce_flags & ZEND_ACC_IMMUTABLE && ce->default_static_members_table) { - ZEND_MAP_PTR_NEW(ce->static_members_table); + if (!(script->corrupted)) { + ce->ce_flags |= ZEND_ACC_IMMUTABLE; + ce->ce_flags &= ~ZEND_ACC_FILE_CACHED; + if (ce->ce_flags & ZEND_ACC_IMMUTABLE && ce->default_static_members_table) { + ZEND_MAP_PTR_NEW(ce->static_members_table); + } else { + ZEND_MAP_PTR_INIT(ce->static_members_table, &ce->default_static_members_table); + } + ZEND_MAP_PTR_NEW(ce->mutable_data); } else { + ce->ce_flags &= ~ZEND_ACC_IMMUTABLE; + ce->ce_flags |= ZEND_ACC_FILE_CACHED; ZEND_MAP_PTR_INIT(ce->static_members_table, &ce->default_static_members_table); + ZEND_MAP_PTR_INIT(ce->mutable_data, NULL); } } @@ -1637,8 +1650,6 @@ static void zend_file_cache_unserialize(zend_persistent_script *script, script, buf, zend_file_cache_unserialize_func, ZEND_FUNCTION_DTOR); zend_file_cache_unserialize_op_array(&script->script.main_op_array, script, buf); zend_file_cache_unserialize_warnings(script, buf); - - UNSERIALIZE_PTR(script->arena_mem); } zend_persistent_script *zend_file_cache_script_load(zend_file_handle *file_handle) diff --git a/ext/opcache/zend_persist.c b/ext/opcache/zend_persist.c index 9bdba91639..a95c614f2a 100644 --- a/ext/opcache/zend_persist.c +++ b/ext/opcache/zend_persist.c @@ -115,7 +115,11 @@ static void zend_hash_persist(HashTable *ht) } if (HT_FLAGS(ht) & HASH_FLAG_PACKED) { void *data = HT_GET_DATA_ADDR(ht); - data = zend_shared_memdup_free(data, HT_USED_SIZE(ht)); + if (GC_FLAGS(ht) & IS_ARRAY_IMMUTABLE) { + data = zend_shared_memdup(data, HT_USED_SIZE(ht)); + } else { + data = zend_shared_memdup_free(data, HT_USED_SIZE(ht)); + } HT_SET_DATA_ADDR(ht, data); } else if (ht->nNumUsed > HT_MIN_SIZE && ht->nNumUsed < (uint32_t)(-(int32_t)ht->nTableMask) / 4) { /* compact table */ @@ -133,7 +137,9 @@ static void zend_hash_persist(HashTable *ht) ZCG(mem) = (void*)((char*)ZCG(mem) + ZEND_ALIGNED_SIZE((hash_size * sizeof(uint32_t)) + (ht->nNumUsed * sizeof(Bucket)))); HT_HASH_RESET(ht); memcpy(ht->arData, old_buckets, ht->nNumUsed * sizeof(Bucket)); - efree(old_data); + if (!(GC_FLAGS(ht) & IS_ARRAY_IMMUTABLE)) { + efree(old_data); + } /* rehash */ for (idx = 0; idx < ht->nNumUsed; idx++) { @@ -150,7 +156,9 @@ static void zend_hash_persist(HashTable *ht) ZEND_ASSERT(((zend_uintptr_t)ZCG(mem) & 0x7) == 0); /* should be 8 byte aligned */ ZCG(mem) = (void*)((char*)data + ZEND_ALIGNED_SIZE(HT_USED_SIZE(ht))); memcpy(data, old_data, HT_USED_SIZE(ht)); - efree(old_data); + if (!(GC_FLAGS(ht) & IS_ARRAY_IMMUTABLE)) { + efree(old_data); + } HT_SET_DATA_ADDR(ht, data); } } @@ -206,28 +214,21 @@ static void zend_persist_zval(zval *z) if (!Z_REFCOUNTED_P(z)) { Z_ARR_P(z) = zend_shared_memdup_put(Z_ARR_P(z), sizeof(zend_array)); - zend_hash_persist(Z_ARRVAL_P(z)); - ZEND_HASH_FOREACH_BUCKET(Z_ARRVAL_P(z), p) { - if (p->key) { - zend_accel_memdup_interned_string(p->key); - } - zend_persist_zval(&p->val); - } ZEND_HASH_FOREACH_END(); } else { GC_REMOVE_FROM_BUFFER(Z_ARR_P(z)); Z_ARR_P(z) = zend_shared_memdup_put_free(Z_ARR_P(z), sizeof(zend_array)); - zend_hash_persist(Z_ARRVAL_P(z)); - ZEND_HASH_FOREACH_BUCKET(Z_ARRVAL_P(z), p) { - if (p->key) { - zend_accel_store_interned_string(p->key); - } - zend_persist_zval(&p->val); - } ZEND_HASH_FOREACH_END(); - /* make immutable array */ - Z_TYPE_FLAGS_P(z) = 0; - GC_SET_REFCOUNT(Z_COUNTED_P(z), 2); - GC_ADD_FLAGS(Z_COUNTED_P(z), IS_ARRAY_IMMUTABLE); } + zend_hash_persist(Z_ARRVAL_P(z)); + ZEND_HASH_FOREACH_BUCKET(Z_ARRVAL_P(z), p) { + if (p->key) { + zend_accel_store_interned_string(p->key); + } + zend_persist_zval(&p->val); + } ZEND_HASH_FOREACH_END(); + /* make immutable array */ + Z_TYPE_FLAGS_P(z) = 0; + GC_SET_REFCOUNT(Z_COUNTED_P(z), 2); + GC_ADD_FLAGS(Z_COUNTED_P(z), IS_ARRAY_IMMUTABLE); } break; case IS_CONSTANT_AST: @@ -235,7 +236,7 @@ static void zend_persist_zval(zval *z) if (new_ptr) { Z_AST_P(z) = new_ptr; Z_TYPE_FLAGS_P(z) = 0; - } else { + } else if (!zend_accel_in_shm(Z_AST_P(z))) { zend_ast_ref *old_ref = Z_AST_P(z); Z_AST_P(z) = zend_shared_memdup_put(Z_AST_P(z), sizeof(zend_ast_ref)); zend_persist_ast(GC_AST(old_ref)); @@ -258,6 +259,10 @@ static HashTable *zend_persist_attributes(HashTable *attributes) uint32_t i; zval *v; + if (zend_accel_in_shm(attributes)) { + return attributes; + } + zend_hash_persist(attributes); ZEND_HASH_FOREACH_VAL(attributes, v) { @@ -285,17 +290,12 @@ static HashTable *zend_persist_attributes(HashTable *attributes) return ptr; } -static void zend_persist_type(zend_type *type, bool use_arena) { +static void zend_persist_type(zend_type *type) { if (ZEND_TYPE_HAS_LIST(*type)) { zend_type_list *list = ZEND_TYPE_LIST(*type); if (ZEND_TYPE_USES_ARENA(*type)) { - if (!ZCG(is_immutable_class) && use_arena) { - list = zend_shared_memdup_arena_put(list, ZEND_TYPE_LIST_SIZE(list->num_types)); - } else { - /* Moved from arena to SHM because type list was fully resolved. */ - list = zend_shared_memdup_put(list, ZEND_TYPE_LIST_SIZE(list->num_types)); - ZEND_TYPE_FULL_MASK(*type) &= ~_ZEND_TYPE_ARENA_BIT; - } + list = zend_shared_memdup_put(list, ZEND_TYPE_LIST_SIZE(list->num_types)); + ZEND_TYPE_FULL_MASK(*type) &= ~_ZEND_TYPE_ARENA_BIT; } else { list = zend_shared_memdup_put_free(list, ZEND_TYPE_LIST_SIZE(list->num_types)); } @@ -308,6 +308,11 @@ static void zend_persist_type(zend_type *type, bool use_arena) { zend_string *type_name = ZEND_TYPE_NAME(*single_type); zend_accel_store_interned_string(type_name); ZEND_TYPE_SET_PTR(*single_type, type_name); + if (!ZCG(current_persistent_script)->corrupted) { + // TODO: we may use single map_ptr slot for each class name ??? + single_type->type_mask |= _ZEND_TYPE_CACHE_BIT; + single_type->ce_cache__ptr = (uint32_t)(uintptr_t)zend_map_ptr_new(); + } } } ZEND_TYPE_FOREACH_END(); } @@ -352,6 +357,7 @@ static void zend_persist_op_array_ex(zend_op_array *op_array, zend_persistent_sc if (scope) { op_array->scope = scope; } + if (op_array->prototype) { zend_function *ptr = zend_shared_alloc_get_xlat_entry(op_array->prototype); @@ -367,7 +373,6 @@ static void zend_persist_op_array_ex(zend_op_array *op_array, zend_persistent_sc op_array->static_variables = zend_shared_alloc_get_xlat_entry(op_array->static_variables); ZEND_ASSERT(op_array->static_variables != NULL); } - ZEND_MAP_PTR_INIT(op_array->static_variables_ptr, &op_array->static_variables); if (op_array->literals) { op_array->literals = zend_shared_alloc_get_xlat_entry(op_array->literals); ZEND_ASSERT(op_array->literals != NULL); @@ -435,7 +440,7 @@ static void zend_persist_op_array_ex(zend_op_array *op_array, zend_persistent_sc op_array->prototype = NULL; } - if (op_array->static_variables) { + if (op_array->static_variables && !zend_accel_in_shm(op_array->static_variables)) { Bucket *p; zend_hash_persist(op_array->static_variables); @@ -449,7 +454,12 @@ static void zend_persist_op_array_ex(zend_op_array *op_array, zend_persistent_sc GC_SET_REFCOUNT(op_array->static_variables, 2); GC_TYPE_INFO(op_array->static_variables) = GC_ARRAY | ((IS_ARRAY_IMMUTABLE|GC_NOT_COLLECTABLE) << GC_FLAGS_SHIFT); } - ZEND_MAP_PTR_INIT(op_array->static_variables_ptr, &op_array->static_variables); + + if (op_array->scope + && !(op_array->fn_flags & ZEND_ACC_CLOSURE) + && (op_array->scope->ce_flags & ZEND_ACC_CACHED)) { + return; + } if (op_array->literals) { zval *p, *end; @@ -551,7 +561,6 @@ static void zend_persist_op_array_ex(zend_op_array *op_array, zend_persistent_sc efree(op_array->opcodes); op_array->opcodes = new_opcodes; - ZEND_MAP_PTR_INIT(op_array->run_time_cache, NULL); } if (op_array->filename) { @@ -575,7 +584,7 @@ static void zend_persist_op_array_ex(zend_op_array *op_array, zend_persistent_sc if (arg_info[i].name) { zend_accel_store_interned_string(arg_info[i].name); } - zend_persist_type(&arg_info[i].type, 0); + zend_persist_type(&arg_info[i].type); } if (op_array->fn_flags & ZEND_ACC_HAS_RETURN_TYPE) { arg_info++; @@ -636,13 +645,11 @@ static void zend_persist_op_array(zval *zv) ZEND_MAP_PTR_NEW(op_array->static_variables_ptr); } } else { - ZEND_MAP_PTR_INIT(op_array->run_time_cache, ZCG(arena_mem)); - ZCG(arena_mem) = (void*)(((char*)ZCG(arena_mem)) + ZEND_ALIGNED_SIZE(sizeof(void*))); - ZEND_MAP_PTR_SET(op_array->run_time_cache, NULL); + ZEND_MAP_PTR_INIT(op_array->static_variables_ptr, &op_array->static_variables); } } -static void zend_persist_class_method(zval *zv) +static void zend_persist_class_method(zval *zv, zend_class_entry *ce) { zend_op_array *op_array = Z_PTR_P(zv); zend_op_array *old_op_array; @@ -654,11 +661,7 @@ static void zend_persist_class_method(zval *zv) if (old_op_array) { Z_PTR_P(zv) = old_op_array; } else { - if (ZCG(is_immutable_class)) { - op_array = Z_PTR_P(zv) = zend_shared_memdup_put(op_array, sizeof(zend_internal_function)); - } else { - op_array = Z_PTR_P(zv) = zend_shared_memdup_arena_put(op_array, sizeof(zend_internal_function)); - } + op_array = Z_PTR_P(zv) = zend_shared_memdup_put(op_array, sizeof(zend_internal_function)); if (op_array->scope) { void *persist_ptr; @@ -676,6 +679,14 @@ static void zend_persist_class_method(zval *zv) return; } + if ((op_array->fn_flags & ZEND_ACC_IMMUTABLE) + && !op_array->static_variables + && !ZCG(current_persistent_script)->corrupted + && zend_accel_in_shm(op_array)) { + zend_shared_alloc_register_xlat_entry(op_array, op_array); + return; + } + old_op_array = zend_shared_alloc_get_xlat_entry(op_array); if (old_op_array) { Z_PTR_P(zv) = old_op_array; @@ -692,33 +703,27 @@ static void zend_persist_class_method(zval *zv) } return; } - if (ZCG(is_immutable_class)) { - op_array = Z_PTR_P(zv) = zend_shared_memdup_put(op_array, sizeof(zend_op_array)); - } else { - op_array = Z_PTR_P(zv) = zend_shared_memdup_arena_put(op_array, sizeof(zend_op_array)); - } + op_array = Z_PTR_P(zv) = zend_shared_memdup_put(op_array, sizeof(zend_op_array)); zend_persist_op_array_ex(op_array, NULL); - if (ZCG(is_immutable_class)) { + if ((ce->ce_flags & ZEND_ACC_LINKED) + && (ce->ce_flags & ZEND_ACC_IMMUTABLE)) { op_array->fn_flags |= ZEND_ACC_IMMUTABLE; ZEND_MAP_PTR_NEW(op_array->run_time_cache); if (op_array->static_variables) { ZEND_MAP_PTR_NEW(op_array->static_variables_ptr); } } else { - ZEND_MAP_PTR_INIT(op_array->run_time_cache, ZCG(arena_mem)); - ZCG(arena_mem) = (void*)(((char*)ZCG(arena_mem)) + ZEND_ALIGNED_SIZE(sizeof(void*))); - ZEND_MAP_PTR_SET(op_array->run_time_cache, NULL); + if (ce->ce_flags & ZEND_ACC_IMMUTABLE) { + ZEND_MAP_PTR_INIT(op_array->run_time_cache, NULL); + } + ZEND_MAP_PTR_INIT(op_array->static_variables_ptr, &op_array->static_variables); } } static zend_property_info *zend_persist_property_info(zend_property_info *prop) { zend_class_entry *ce; - if (ZCG(is_immutable_class)) { - prop = zend_shared_memdup_put(prop, sizeof(zend_property_info)); - } else { - prop = zend_shared_memdup_arena_put(prop, sizeof(zend_property_info)); - } + prop = zend_shared_memdup_put(prop, sizeof(zend_property_info)); ce = zend_shared_alloc_get_xlat_entry(prop->ce); if (ce) { prop->ce = ce; @@ -738,7 +743,7 @@ static zend_property_info *zend_persist_property_info(zend_property_info *prop) if (prop->attributes) { prop->attributes = zend_persist_attributes(prop->attributes); } - zend_persist_type(&prop->type, 1); + zend_persist_type(&prop->type); return prop; } @@ -751,11 +756,7 @@ static void zend_persist_class_constant(zval *zv) Z_PTR_P(zv) = c; return; } - if (ZCG(is_immutable_class)) { - c = Z_PTR_P(zv) = zend_shared_memdup_put(Z_PTR_P(zv), sizeof(zend_class_constant)); - } else { - c = Z_PTR_P(zv) = zend_shared_memdup_arena_put(Z_PTR_P(zv), sizeof(zend_class_constant)); - } + c = Z_PTR_P(zv) = zend_shared_memdup_put(Z_PTR_P(zv), sizeof(zend_class_constant)); zend_persist_zval(&c->value); ce = zend_shared_alloc_get_xlat_entry(c->ce); if (ce) { @@ -783,39 +784,28 @@ static void zend_persist_class_constant(zval *zv) } } -static void zend_persist_class_entry(zval *zv) +zend_class_entry *zend_persist_class_entry(zend_class_entry *orig_ce) { Bucket *p; - zend_class_entry *orig_ce = Z_PTR_P(zv), *ce = orig_ce; + zend_class_entry *ce = orig_ce; if (ce->type == ZEND_USER_CLASS) { /* The same zend_class_entry may be reused by class_alias */ zend_class_entry *new_ce = zend_shared_alloc_get_xlat_entry(ce); if (new_ce) { - Z_PTR_P(zv) = new_ce; - return; + return new_ce; } - if ((ce->ce_flags & ZEND_ACC_LINKED) - && (ce->ce_flags & ZEND_ACC_CONSTANTS_UPDATED) - && (ce->ce_flags & ZEND_ACC_PROPERTY_TYPES_RESOLVED) - && !ZCG(current_persistent_script)->corrupted) { - ZCG(is_immutable_class) = 1; - ce = Z_PTR_P(zv) = zend_shared_memdup_put(ce, sizeof(zend_class_entry)); + ce = zend_shared_memdup_put(ce, sizeof(zend_class_entry)); + if (EXPECTED(!ZCG(current_persistent_script)->corrupted)) { ce->ce_flags |= ZEND_ACC_IMMUTABLE; - } else { - ZCG(is_immutable_class) = 0; - ce = Z_PTR_P(zv) = zend_shared_memdup_arena_put(ce, sizeof(zend_class_entry)); - } - ce->ce_flags |= ZEND_ACC_CACHED; - zend_accel_store_interned_string(ce->name); - if (ce->parent_name && !(ce->ce_flags & ZEND_ACC_LINKED)) { - zend_accel_store_interned_string(ce->parent_name); } + ce->inheritance_cache = NULL; + zend_hash_persist(&ce->function_table); ZEND_HASH_FOREACH_BUCKET(&ce->function_table, p) { ZEND_ASSERT(p->key != NULL); zend_accel_store_interned_string(p->key); - zend_persist_class_method(&p->val); + zend_persist_class_method(&p->val, ce); } ZEND_HASH_FOREACH_END(); HT_FLAGS(&ce->function_table) &= (HASH_FLAG_UNINITIALIZED | HASH_FLAG_STATIC_KEYS); if (ce->default_properties_table) { @@ -826,6 +816,15 @@ static void zend_persist_class_entry(zval *zv) zend_persist_zval(&ce->default_properties_table[i]); } } + if ((ce->ce_flags & ZEND_ACC_IMMUTABLE) + && (ce->ce_flags & ZEND_ACC_LINKED) + && !(ce->ce_flags & ZEND_ACC_CONSTANTS_UPDATED)) { + ZEND_MAP_PTR_NEW(ce->mutable_data); + } else if (ce->ce_flags & ZEND_ACC_IMMUTABLE) { + ZEND_MAP_PTR_INIT(ce->mutable_data, NULL); + } else { + ce->ce_flags |= ZEND_ACC_FILE_CACHED; + } if (ce->default_static_members_table) { int i; ce->default_static_members_table = zend_shared_memdup_free(ce->default_static_members_table, sizeof(zval) * ce->default_static_members_count); @@ -837,7 +836,11 @@ static void zend_persist_class_entry(zval *zv) zend_persist_zval(&ce->default_static_members_table[i]); } if (ce->ce_flags & ZEND_ACC_IMMUTABLE) { - ZEND_MAP_PTR_NEW(ce->static_members_table); + if (ce->ce_flags & ZEND_ACC_LINKED) { + ZEND_MAP_PTR_NEW(ce->static_members_table); + } else { + ZEND_MAP_PTR_INIT(ce->static_members_table, NULL); + } } else { ZEND_MAP_PTR_INIT(ce->static_members_table, &ce->default_static_members_table); } @@ -853,23 +856,6 @@ static void zend_persist_class_entry(zval *zv) } ZEND_HASH_FOREACH_END(); HT_FLAGS(&ce->constants_table) &= (HASH_FLAG_UNINITIALIZED | HASH_FLAG_STATIC_KEYS); - if (ce->info.user.filename) { - zend_accel_store_string(ce->info.user.filename); - } - if (ce->info.user.doc_comment) { - if (ZCG(accel_directives).save_comments) { - zend_accel_store_interned_string(ce->info.user.doc_comment); - } else { - if (!zend_shared_alloc_get_xlat_entry(ce->info.user.doc_comment)) { - zend_shared_alloc_register_xlat_entry(ce->info.user.doc_comment, ce->info.user.doc_comment); - zend_string_release_ex(ce->info.user.doc_comment, 0); - } - ce->info.user.doc_comment = NULL; - } - } - if (ce->attributes) { - ce->attributes = zend_persist_attributes(ce->attributes); - } zend_hash_persist(&ce->properties_info); ZEND_HASH_FOREACH_BUCKET(&ce->properties_info, p) { zend_property_info *prop = Z_PTR(p->val); @@ -895,13 +881,8 @@ static void zend_persist_class_entry(zval *zv) size_t size = sizeof(zend_property_info *) * ce->default_properties_count; ZEND_ASSERT(ce->ce_flags & ZEND_ACC_LINKED); - if (ZCG(is_immutable_class)) { - ce->properties_info_table = zend_shared_memdup( - ce->properties_info_table, size); - } else { - ce->properties_info_table = zend_shared_memdup_arena( - ce->properties_info_table, size); - } + ce->properties_info_table = zend_shared_memdup( + ce->properties_info_table, size); for (i = 0; i < ce->default_properties_count; i++) { if (ce->properties_info_table[i]) { @@ -914,6 +895,41 @@ static void zend_persist_class_entry(zval *zv) } } + if (ce->iterator_funcs_ptr) { + ce->iterator_funcs_ptr = zend_shared_memdup(ce->iterator_funcs_ptr, sizeof(zend_class_iterator_funcs)); + } + + if (ce->ce_flags & ZEND_ACC_CACHED) { + return ce; + } + + ce->ce_flags |= ZEND_ACC_CACHED; + + zend_accel_store_interned_string(ce->name); + if (ce->parent_name && !(ce->ce_flags & ZEND_ACC_LINKED)) { + zend_accel_store_interned_string(ce->parent_name); + } + + if (ce->info.user.filename) { + zend_accel_store_string(ce->info.user.filename); + } + + if (ce->info.user.doc_comment) { + if (ZCG(accel_directives).save_comments) { + zend_accel_store_interned_string(ce->info.user.doc_comment); + } else { + if (!zend_shared_alloc_get_xlat_entry(ce->info.user.doc_comment)) { + zend_shared_alloc_register_xlat_entry(ce->info.user.doc_comment, ce->info.user.doc_comment); + zend_string_release_ex(ce->info.user.doc_comment, 0); + } + ce->info.user.doc_comment = NULL; + } + } + + if (ce->attributes) { + ce->attributes = zend_persist_attributes(ce->attributes); + } + if (ce->num_interfaces && !(ce->ce_flags & ZEND_ACC_LINKED)) { uint32_t i = 0; @@ -973,14 +989,12 @@ static void zend_persist_class_entry(zval *zv) ce->trait_precedences, sizeof(zend_trait_precedence*) * (i + 1)); } } - - if (ce->iterator_funcs_ptr) { - ce->iterator_funcs_ptr = zend_shared_memdup(ce->iterator_funcs_ptr, sizeof(zend_class_iterator_funcs)); - } } + + return ce; } -static void zend_update_parent_ce(zend_class_entry *ce) +void zend_update_parent_ce(zend_class_entry *ce) { if (ce->ce_flags & ZEND_ACC_LINKED) { if (ce->parent) { @@ -1145,7 +1159,7 @@ static void zend_accel_persist_class_table(HashTable *class_table) ZEND_HASH_FOREACH_BUCKET(class_table, p) { ZEND_ASSERT(p->key != NULL); zend_accel_store_interned_string(p->key); - zend_persist_class_entry(&p->val); + Z_CE(p->val) = zend_persist_class_entry(Z_CE(p->val)); } ZEND_HASH_FOREACH_END(); ZEND_HASH_FOREACH_BUCKET(class_table, p) { if (EXPECTED(Z_TYPE(p->val) != IS_ALIAS_PTR)) { @@ -1198,9 +1212,6 @@ zend_persistent_script *zend_accel_script_persist(zend_persistent_script *script ZEND_ASSERT(((zend_uintptr_t)ZCG(mem) & 0x7) == 0); /* should be 8 byte aligned */ #endif - script->arena_mem = ZCG(arena_mem) = ZCG(mem); - ZCG(mem) = (void*)((char*)ZCG(mem) + script->arena_size); - #ifdef HAVE_JIT if (JIT_G(on) && for_shm) { zend_jit_unprotect(); @@ -1217,6 +1228,11 @@ zend_persistent_script *zend_accel_script_persist(zend_persistent_script *script zend_persist_op_array(&p->val); } ZEND_HASH_FOREACH_END(); zend_persist_op_array_ex(&script->script.main_op_array, script); + if (!script->corrupted) { + ZEND_MAP_PTR_INIT(script->script.main_op_array.run_time_cache, NULL); + } + ZEND_MAP_PTR_INIT(script->script.main_op_array.static_variables_ptr, + &script->script.main_op_array.static_variables); zend_persist_warnings(script); if (for_shm) { diff --git a/ext/opcache/zend_persist.h b/ext/opcache/zend_persist.h index d5eca3f195..38afac39d9 100644 --- a/ext/opcache/zend_persist.h +++ b/ext/opcache/zend_persist.h @@ -25,4 +25,8 @@ uint32_t zend_accel_script_persist_calc(zend_persistent_script *script, const char *key, unsigned int key_length, int for_shm); zend_persistent_script *zend_accel_script_persist(zend_persistent_script *script, const char **key, unsigned int key_length, int for_shm); +void zend_persist_class_entry_calc(zend_class_entry *ce); +zend_class_entry *zend_persist_class_entry(zend_class_entry *ce); +void zend_update_parent_ce(zend_class_entry *ce); + #endif /* ZEND_PERSIST_H */ diff --git a/ext/opcache/zend_persist_calc.c b/ext/opcache/zend_persist_calc.c index 7731b9a970..5728414eec 100644 --- a/ext/opcache/zend_persist_calc.c +++ b/ext/opcache/zend_persist_calc.c @@ -29,15 +29,6 @@ #define ADD_DUP_SIZE(m,s) ZCG(current_persistent_script)->size += zend_shared_memdup_size((void*)m, s) #define ADD_SIZE(m) ZCG(current_persistent_script)->size += ZEND_ALIGNED_SIZE(m) -#define ADD_ARENA_SIZE(m) ZCG(current_persistent_script)->arena_size += ZEND_ALIGNED_SIZE(m) - -#define ADD_SIZE_EX(m) do { \ - if (ZCG(is_immutable_class)) { \ - ADD_SIZE(m); \ - } else { \ - ADD_ARENA_SIZE(m); \ - } \ - } while (0) # define ADD_STRING(str) ADD_DUP_SIZE((str), _ZSTR_STRUCT_SIZE(ZSTR_LEN(str))) @@ -129,10 +120,12 @@ static void zend_persist_zval_calc(zval *z) } break; case IS_CONSTANT_AST: - size = zend_shared_memdup_size(Z_AST_P(z), sizeof(zend_ast_ref)); - if (size) { - ADD_SIZE(size); - zend_persist_ast_calc(Z_ASTVAL_P(z)); + if (!zend_accel_in_shm(Z_AST_P(z))) { + size = zend_shared_memdup_size(Z_AST_P(z), sizeof(zend_ast_ref)); + if (size) { + ADD_SIZE(size); + zend_persist_ast_calc(Z_ASTVAL_P(z)); + } } break; default: @@ -143,7 +136,8 @@ static void zend_persist_zval_calc(zval *z) static void zend_persist_attributes_calc(HashTable *attributes) { - if (!zend_shared_alloc_get_xlat_entry(attributes)) { + if (!zend_shared_alloc_get_xlat_entry(attributes) + && !zend_accel_in_shm(attributes)) { zend_attribute *attr; uint32_t i; @@ -166,14 +160,10 @@ static void zend_persist_attributes_calc(HashTable *attributes) } } -static void zend_persist_type_calc(zend_type *type, bool use_arena) +static void zend_persist_type_calc(zend_type *type) { if (ZEND_TYPE_HAS_LIST(*type)) { - if (ZEND_TYPE_USES_ARENA(*type) && !ZCG(is_immutable_class) && use_arena) { - ADD_ARENA_SIZE(ZEND_TYPE_LIST_SIZE(ZEND_TYPE_LIST(*type)->num_types)); - } else { - ADD_SIZE(ZEND_TYPE_LIST_SIZE(ZEND_TYPE_LIST(*type)->num_types)); - } + ADD_SIZE(ZEND_TYPE_LIST_SIZE(ZEND_TYPE_LIST(*type)->num_types)); } zend_type *single_type; @@ -198,13 +188,15 @@ static void zend_persist_op_array_calc_ex(zend_op_array *op_array) } } - if (op_array->scope && zend_shared_alloc_get_xlat_entry(op_array->opcodes)) { - /* already stored */ - ADD_SIZE(ZEND_ALIGNED_SIZE(zend_extensions_op_array_persist_calc(op_array))); - return; + if (op_array->scope) { + if (zend_shared_alloc_get_xlat_entry(op_array->opcodes)) { + /* already stored */ + ADD_SIZE(ZEND_ALIGNED_SIZE(zend_extensions_op_array_persist_calc(op_array))); + return; + } } - if (op_array->static_variables) { + if (op_array->static_variables && !zend_accel_in_shm(op_array->static_variables)) { if (!zend_shared_alloc_get_xlat_entry(op_array->static_variables)) { Bucket *p; @@ -219,6 +211,12 @@ static void zend_persist_op_array_calc_ex(zend_op_array *op_array) } } + if (op_array->scope + && !(op_array->fn_flags & ZEND_ACC_CLOSURE) + && (op_array->scope->ce_flags & ZEND_ACC_CACHED)) { + return; + } + if (op_array->literals) { zval *p = op_array->literals; zval *end = p + op_array->last_literal; @@ -254,7 +252,7 @@ static void zend_persist_op_array_calc_ex(zend_op_array *op_array) if (arg_info[i].name) { ADD_INTERNED_STRING(arg_info[i].name); } - zend_persist_type_calc(&arg_info[i].type, 0); + zend_persist_type_calc(&arg_info[i].type); } } @@ -293,9 +291,6 @@ static void zend_persist_op_array_calc(zval *zv) ZEND_ASSERT(op_array->type == ZEND_USER_FUNCTION); ADD_SIZE(sizeof(zend_op_array)); zend_persist_op_array_calc_ex(Z_PTR_P(zv)); - if (ZCG(current_persistent_script)->corrupted) { - ADD_ARENA_SIZE(sizeof(void*)); - } } static void zend_persist_class_method_calc(zval *zv) @@ -308,21 +303,26 @@ static void zend_persist_class_method_calc(zval *zv) if (op_array->fn_flags & ZEND_ACC_ARENA_ALLOCATED) { old_op_array = zend_shared_alloc_get_xlat_entry(op_array); if (!old_op_array) { - ADD_SIZE_EX(sizeof(zend_internal_function)); + ADD_SIZE(sizeof(zend_internal_function)); zend_shared_alloc_register_xlat_entry(op_array, Z_PTR_P(zv)); } } return; } + if ((op_array->fn_flags & ZEND_ACC_IMMUTABLE) + && !op_array->static_variables + && !ZCG(current_persistent_script)->corrupted + && zend_accel_in_shm(op_array)) { + zend_shared_alloc_register_xlat_entry(op_array, op_array); + return; + } + old_op_array = zend_shared_alloc_get_xlat_entry(op_array); if (!old_op_array) { - ADD_SIZE_EX(sizeof(zend_op_array)); + ADD_SIZE(sizeof(zend_op_array)); zend_persist_op_array_calc_ex(Z_PTR_P(zv)); zend_shared_alloc_register_xlat_entry(op_array, Z_PTR_P(zv)); - if (!ZCG(is_immutable_class)) { - ADD_ARENA_SIZE(sizeof(void*)); - } } else { /* If op_array is shared, the function name refcount is still incremented for each use, * so we need to release it here. We remembered the original function name in xlat. */ @@ -336,9 +336,9 @@ static void zend_persist_class_method_calc(zval *zv) static void zend_persist_property_info_calc(zend_property_info *prop) { - ADD_SIZE_EX(sizeof(zend_property_info)); + ADD_SIZE(sizeof(zend_property_info)); ADD_INTERNED_STRING(prop->name); - zend_persist_type_calc(&prop->type, 1); + zend_persist_type_calc(&prop->type); if (ZCG(accel_directives).save_comments && prop->doc_comment) { ADD_STRING(prop->doc_comment); } @@ -353,7 +353,7 @@ static void zend_persist_class_constant_calc(zval *zv) if (!zend_shared_alloc_get_xlat_entry(c)) { zend_shared_alloc_register_xlat_entry(c, c); - ADD_SIZE_EX(sizeof(zend_class_constant)); + ADD_SIZE(sizeof(zend_class_constant)); zend_persist_zval_calc(&c->value); if (ZCG(accel_directives).save_comments && c->doc_comment) { ADD_STRING(c->doc_comment); @@ -364,29 +364,8 @@ static void zend_persist_class_constant_calc(zval *zv) } } -static void check_property_type_resolution(zend_class_entry *ce) { - zend_property_info *prop; - if (ce->ce_flags & ZEND_ACC_PROPERTY_TYPES_RESOLVED) { - /* Preloading might have computed this already. */ - return; - } - - if (ce->ce_flags & ZEND_ACC_HAS_TYPE_HINTS) { - ZEND_HASH_FOREACH_PTR(&ce->properties_info, prop) { - zend_type *single_type; - ZEND_TYPE_FOREACH(prop->type, single_type) { - if (ZEND_TYPE_HAS_NAME(*single_type)) { - return; - } - } ZEND_TYPE_FOREACH_END(); - } ZEND_HASH_FOREACH_END(); - } - ce->ce_flags |= ZEND_ACC_PROPERTY_TYPES_RESOLVED; -} - -static void zend_persist_class_entry_calc(zval *zv) +void zend_persist_class_entry_calc(zend_class_entry *ce) { - zend_class_entry *ce = Z_PTR_P(zv); Bucket *p; if (ce->type == ZEND_USER_CLASS) { @@ -396,19 +375,8 @@ static void zend_persist_class_entry_calc(zval *zv) } zend_shared_alloc_register_xlat_entry(ce, ce); - check_property_type_resolution(ce); - - ZCG(is_immutable_class) = - (ce->ce_flags & ZEND_ACC_LINKED) && - (ce->ce_flags & ZEND_ACC_CONSTANTS_UPDATED) && - (ce->ce_flags & ZEND_ACC_PROPERTY_TYPES_RESOLVED) && - !ZCG(current_persistent_script)->corrupted; + ADD_SIZE(sizeof(zend_class_entry)); - ADD_SIZE_EX(sizeof(zend_class_entry)); - ADD_INTERNED_STRING(ce->name); - if (ce->parent_name && !(ce->ce_flags & ZEND_ACC_LINKED)) { - ADD_INTERNED_STRING(ce->parent_name); - } zend_hash_persist_calc(&ce->function_table); ZEND_HASH_FOREACH_BUCKET(&ce->function_table, p) { ZEND_ASSERT(p->key != NULL); @@ -440,16 +408,6 @@ static void zend_persist_class_entry_calc(zval *zv) zend_persist_class_constant_calc(&p->val); } ZEND_HASH_FOREACH_END(); - if (ce->info.user.filename) { - ADD_STRING(ce->info.user.filename); - } - if (ZCG(accel_directives).save_comments && ce->info.user.doc_comment) { - ADD_STRING(ce->info.user.doc_comment); - } - if (ce->attributes) { - zend_persist_attributes_calc(ce->attributes); - } - zend_hash_persist_calc(&ce->properties_info); ZEND_HASH_FOREACH_BUCKET(&ce->properties_info, p) { zend_property_info *prop = Z_PTR(p->val); @@ -461,7 +419,36 @@ static void zend_persist_class_entry_calc(zval *zv) } ZEND_HASH_FOREACH_END(); if (ce->properties_info_table) { - ADD_SIZE_EX(sizeof(zend_property_info *) * ce->default_properties_count); + ADD_SIZE(sizeof(zend_property_info *) * ce->default_properties_count); + } + + if (ce->num_interfaces && (ce->ce_flags & ZEND_ACC_LINKED)) { + ADD_SIZE(sizeof(zend_class_entry*) * ce->num_interfaces); + } + + if (ce->iterator_funcs_ptr) { + ADD_SIZE(sizeof(zend_class_iterator_funcs)); + } + + if (ce->ce_flags & ZEND_ACC_CACHED) { + return; + } + + ADD_INTERNED_STRING(ce->name); + if (ce->parent_name && !(ce->ce_flags & ZEND_ACC_LINKED)) { + ADD_INTERNED_STRING(ce->parent_name); + } + + if (ce->info.user.filename) { + ADD_STRING(ce->info.user.filename); + } + + if (ZCG(accel_directives).save_comments && ce->info.user.doc_comment) { + ADD_STRING(ce->info.user.doc_comment); + } + + if (ce->attributes) { + zend_persist_attributes_calc(ce->attributes); } if (ce->num_interfaces) { @@ -473,8 +460,6 @@ static void zend_persist_class_entry_calc(zval *zv) ADD_INTERNED_STRING(ce->interface_names[i].lc_name); } ADD_SIZE(sizeof(zend_class_name) * ce->num_interfaces); - } else { - ADD_SIZE(sizeof(zend_class_entry*) * ce->num_interfaces); } } @@ -523,10 +508,6 @@ static void zend_persist_class_entry_calc(zval *zv) ADD_SIZE(sizeof(zend_trait_precedence*) * (i + 1)); } } - - if (ce->iterator_funcs_ptr) { - ADD_SIZE(sizeof(zend_class_iterator_funcs)); - } } } @@ -538,7 +519,7 @@ static void zend_accel_persist_class_table_calc(HashTable *class_table) ZEND_HASH_FOREACH_BUCKET(class_table, p) { ZEND_ASSERT(p->key != NULL); ADD_INTERNED_STRING(p->key); - zend_persist_class_entry_calc(&p->val); + zend_persist_class_entry_calc(Z_CE(p->val)); } ZEND_HASH_FOREACH_END(); } @@ -557,8 +538,6 @@ uint32_t zend_accel_script_persist_calc(zend_persistent_script *new_persistent_s new_persistent_script->mem = NULL; new_persistent_script->size = 0; - new_persistent_script->arena_mem = NULL; - new_persistent_script->arena_size = 0; new_persistent_script->corrupted = 0; ZCG(current_persistent_script) = new_persistent_script; @@ -595,12 +574,6 @@ uint32_t zend_accel_script_persist_calc(zend_persistent_script *new_persistent_s zend_persist_op_array_calc_ex(&new_persistent_script->script.main_op_array); zend_persist_warnings_calc(new_persistent_script); -#if defined(__AVX__) || defined(__SSE2__) - /* Align size to 64-byte boundary */ - new_persistent_script->arena_size = (new_persistent_script->arena_size + 63) & ~63; -#endif - - new_persistent_script->size += new_persistent_script->arena_size; new_persistent_script->corrupted = 0; ZCG(current_persistent_script) = NULL; diff --git a/ext/opcache/zend_shared_alloc.c b/ext/opcache/zend_shared_alloc.c index 698c2884ca..a078a31c0f 100644 --- a/ext/opcache/zend_shared_alloc.c +++ b/ext/opcache/zend_shared_alloc.c @@ -375,7 +375,7 @@ int zend_shared_memdup_size(void *source, size_t size) return ZEND_ALIGNED_SIZE(size); } -static zend_always_inline void *_zend_shared_memdup(void *source, size_t size, bool arena, bool get_xlat, bool set_xlat, bool free_source) +static zend_always_inline void *_zend_shared_memdup(void *source, size_t size, bool get_xlat, bool set_xlat, bool free_source) { void *old_p, *retval; zend_ulong key; @@ -388,13 +388,8 @@ static zend_always_inline void *_zend_shared_memdup(void *source, size_t size, b return old_p; } } - if (arena) { - retval = ZCG(arena_mem); - ZCG(arena_mem) = (void*)(((char*)ZCG(arena_mem)) + ZEND_ALIGNED_SIZE(size)); - } else { - retval = ZCG(mem); - ZCG(mem) = (void*)(((char*)ZCG(mem)) + ZEND_ALIGNED_SIZE(size)); - } + retval = ZCG(mem); + ZCG(mem) = (void*)(((char*)ZCG(mem)) + ZEND_ALIGNED_SIZE(size)); memcpy(retval, source, size); if (set_xlat) { if (!get_xlat) { @@ -411,42 +406,32 @@ static zend_always_inline void *_zend_shared_memdup(void *source, size_t size, b void *zend_shared_memdup_get_put_free(void *source, size_t size) { - return _zend_shared_memdup(source, size, 0, 1, 1, 1); + return _zend_shared_memdup(source, size, 1, 1, 1); } void *zend_shared_memdup_put_free(void *source, size_t size) { - return _zend_shared_memdup(source, size, 0, 0, 1, 1); + return _zend_shared_memdup(source, size, 0, 1, 1); } void *zend_shared_memdup_free(void *source, size_t size) { - return _zend_shared_memdup(source, size, 0, 0, 0, 1); + return _zend_shared_memdup(source, size, 0, 0, 1); } void *zend_shared_memdup_get_put(void *source, size_t size) { - return _zend_shared_memdup(source, size, 0, 1, 1, 0); + return _zend_shared_memdup(source, size, 1, 1, 0); } void *zend_shared_memdup_put(void *source, size_t size) { - return _zend_shared_memdup(source, size, 0, 0, 1, 0); + return _zend_shared_memdup(source, size, 0, 1, 0); } void *zend_shared_memdup(void *source, size_t size) { - return _zend_shared_memdup(source, size, 0, 0, 0, 0); -} - -void *zend_shared_memdup_arena_put(void *source, size_t size) -{ - return _zend_shared_memdup(source, size, 1, 0, 1, 0); -} - -void *zend_shared_memdup_arena(void *source, size_t size) -{ - return _zend_shared_memdup(source, size, 1, 0, 0, 0); + return _zend_shared_memdup(source, size, 0, 0, 0); } void zend_shared_alloc_safe_unlock(void) diff --git a/ext/opcache/zend_shared_alloc.h b/ext/opcache/zend_shared_alloc.h index 8d37376570..7b67c3c454 100644 --- a/ext/opcache/zend_shared_alloc.h +++ b/ext/opcache/zend_shared_alloc.h @@ -139,8 +139,6 @@ void *zend_shared_memdup_free(void *source, size_t size); void *zend_shared_memdup_get_put(void *source, size_t size); void *zend_shared_memdup_put(void *source, size_t size); void *zend_shared_memdup(void *source, size_t size); -void *zend_shared_memdup_arena_put(void *source, size_t size); -void *zend_shared_memdup_arena(void *source, size_t size); int zend_shared_memdup_size(void *p, size_t size); diff --git a/ext/reflection/php_reflection.c b/ext/reflection/php_reflection.c index b0c1067474..73c6926b0d 100644 --- a/ext/reflection/php_reflection.c +++ b/ext/reflection/php_reflection.c @@ -386,7 +386,7 @@ static void _class_string(smart_str *str, zend_class_entry *ce, zval *obj, char zend_string *key; zend_class_constant *c; - ZEND_HASH_FOREACH_STR_KEY_PTR(&ce->constants_table, key, c) { + ZEND_HASH_FOREACH_STR_KEY_PTR(CE_CONSTANTS_TABLE(ce), key, c) { _class_const_string(str, ZSTR_VAL(key), c, ZSTR_VAL(sub_indent)); if (UNEXPECTED(EG(exception))) { zend_string_release(sub_indent); @@ -2948,8 +2948,14 @@ ZEND_METHOD(ReflectionUnionType, getTypes) append_type(return_value, *list_type); } ZEND_TYPE_LIST_FOREACH_END(); } else if (ZEND_TYPE_HAS_NAME(param->type)) { - append_type(return_value, - (zend_type) ZEND_TYPE_INIT_CLASS(ZEND_TYPE_NAME(param->type), 0, 0)); + if (ZEND_TYPE_HAS_CE_CACHE(param->type) + && ZEND_TYPE_CE_CACHE(param->type)) { + append_type(return_value, + (zend_type) ZEND_TYPE_INIT_CE(ZEND_TYPE_CE_CACHE(param->type), 0, 0)); + } else { + append_type(return_value, + (zend_type) ZEND_TYPE_INIT_CLASS(ZEND_TYPE_NAME(param->type), 0, 0)); + } } else if (ZEND_TYPE_HAS_CE(param->type)) { append_type(return_value, (zend_type) ZEND_TYPE_INIT_CE(ZEND_TYPE_CE(param->type), 0, 0)); @@ -3548,7 +3554,7 @@ ZEND_METHOD(ReflectionClassConstant, __construct) object = ZEND_THIS; intern = Z_REFLECTION_P(object); - if ((constant = zend_hash_find_ptr(&ce->constants_table, constname)) == NULL) { + if ((constant = zend_hash_find_ptr(CE_CONSTANTS_TABLE(ce), constname)) == NULL) { zend_throw_exception_ex(reflection_exception_ptr, 0, "Constant %s::%s does not exist", ZSTR_VAL(ce->name), ZSTR_VAL(constname)); RETURN_THROWS(); } @@ -4444,7 +4450,7 @@ ZEND_METHOD(ReflectionClass, getConstants) GET_REFLECTION_OBJECT_PTR(ce); array_init(return_value); - ZEND_HASH_FOREACH_STR_KEY_PTR(&ce->constants_table, key, constant) { + ZEND_HASH_FOREACH_STR_KEY_PTR(CE_CONSTANTS_TABLE(ce), key, constant) { if (UNEXPECTED(zval_update_constant_ex(&constant->value, ce) != SUCCESS)) { RETURN_THROWS(); } @@ -4478,7 +4484,7 @@ ZEND_METHOD(ReflectionClass, getReflectionConstants) GET_REFLECTION_OBJECT_PTR(ce); array_init(return_value); - ZEND_HASH_FOREACH_STR_KEY_PTR(&ce->constants_table, name, constant) { + ZEND_HASH_FOREACH_STR_KEY_PTR(CE_CONSTANTS_TABLE(ce), name, constant) { if (Z_ACCESS_FLAGS(constant->value) & filter) { zval class_const; reflection_class_constant_factory(name, constant, &class_const); @@ -4493,6 +4499,7 @@ ZEND_METHOD(ReflectionClass, getConstant) { reflection_object *intern; zend_class_entry *ce; + HashTable *constants_table; zend_class_constant *c; zend_string *name; @@ -4501,12 +4508,13 @@ ZEND_METHOD(ReflectionClass, getConstant) } GET_REFLECTION_OBJECT_PTR(ce); - ZEND_HASH_FOREACH_PTR(&ce->constants_table, c) { + constants_table = CE_CONSTANTS_TABLE(ce); + ZEND_HASH_FOREACH_PTR(constants_table, c) { if (UNEXPECTED(zval_update_constant_ex(&c->value, ce) != SUCCESS)) { RETURN_THROWS(); } } ZEND_HASH_FOREACH_END(); - if ((c = zend_hash_find_ptr(&ce->constants_table, name)) == NULL) { + if ((c = zend_hash_find_ptr(constants_table, name)) == NULL) { RETURN_FALSE; } ZVAL_COPY_OR_DUP(return_value, &c->value); @@ -4526,7 +4534,7 @@ ZEND_METHOD(ReflectionClass, getReflectionConstant) RETURN_THROWS(); } - if ((constant = zend_hash_find_ptr(&ce->constants_table, name)) == NULL) { + if ((constant = zend_hash_find_ptr(CE_CONSTANTS_TABLE(ce), name)) == NULL) { RETURN_FALSE; } reflection_class_constant_factory(name, constant, return_value); diff --git a/main/main.c b/main/main.c index b5115663b8..ac7170c88a 100644 --- a/main/main.c +++ b/main/main.c @@ -1826,6 +1826,7 @@ void php_request_shutdown(void *dummy) } zend_end_try(); /* 15. Free Willy (here be crashes) */ + zend_arena_destroy(CG(arena)); zend_interned_strings_deactivate(); zend_try { shutdown_memory_manager(CG(unclean_shutdown) || !report_memleaks, 0); |