From: Dmitry Stogov Date: Tue, 9 Feb 2021 19:53:57 +0000 (+0300) Subject: Added Inheritance Cache. X-Git-Url: https://granicus.if.org/sourcecode?a=commitdiff_plain;h=4b79dba93202ed5640dff317046ce2fdd42e1d82;p=php Added Inheritance Cache. This is a new transparent technology that eliminates overhead of PHP class inheritance. PHP classes are compiled and cached (by opcahce) separately, however their "linking" was done at run-time - on each request. The process of "linking" may involve a number of compatibility checks and borrowing methods/properties/constants form parent and traits. This takes significant time, but the result is the same on each request. Inheritance Cache performs "linking" for unique set of all the depending classes (parent, interfaces, traits, property types, method types involved into compatibility checks) once and stores result in opcache shared memory. As a part of the this patch, I removed limitations for immutable classes (unresolved constants, typed properties and covariant type checks). So now all classes stored in opcache are "immutable". They may be lazily loaded into process memory, if necessary, but this usually occurs just once (on first linking). The patch shows 8% improvement on Symphony "Hello World" app. --- diff --git a/NEWS b/NEWS index 14b189c1a1..5d6db8eb52 100644 --- a/NEWS +++ b/NEWS @@ -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-- +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-- +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-- 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 --FILE-- 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 --FILE-- 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 +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 --FILE-- -Unreachable + --FILE-- -Unreachable + --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-- +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 -# 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 -# 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);