diff --git a/UPGRADING.INTERNALS b/UPGRADING.INTERNALS index 132a2d97a755d..8f4046032b799 100644 --- a/UPGRADING.INTERNALS +++ b/UPGRADING.INTERNALS @@ -8,6 +8,7 @@ PHP 7.4 INTERNALS UPGRADE NOTES e. php_win32_error_to_msg() memory management f. get_properties_for() handler / Z_OBJDEBUG_P g. Required object handlers + h. Immutable classes and op_arrays 2. Build system changes a. Abstract @@ -121,6 +122,20 @@ PHP 7.4 INTERNALS UPGRADE NOTES It is recommended to initialize object handler structures by copying the std object handlers and only overwriting those you want to change. + h. Opcache may make classes and op_arrays immutable. Such classes are marked + by ZEND_ACC_IMMUTABLE flag, they are not going to be copied from opcache + shared memory to process memory and must not be modified at all. + Few related data structures were changed to allow addressing mutable data + structures from immutable ones. This access is implemented through + ZEND_MAP_PTR... abstraction macros and, basically, uses additional level of + indirection. op_array->run_time_cache, op_array->static_variables_ptr and + class_entry->static_members_table now have to be accessed through + ZEND_MAP_PTR... macros. + It's also not allowed to change op_array->reserved[] handles of immutable + op_arrays. Instead, now you have to reserve op_array handle using + zend_get_op_array_extension_handle() during MINIT and access its value + using ZEND_OP_ARRAY_EXTENSION(op_array, handle). + ======================== 2. Build system changes ======================== diff --git a/Zend/zend.c b/Zend/zend.c index 6836e5af0c934..1e4d2a0f42196 100644 --- a/Zend/zend.c +++ b/Zend/zend.c @@ -33,6 +33,8 @@ #include "zend_smart_string.h" #include "zend_cpuinfo.h" +static size_t global_map_ptr_last = 0; + #ifdef ZTS ZEND_API int compiler_globals_id; ZEND_API int executor_globals_id; @@ -41,7 +43,6 @@ static HashTable *global_class_table = NULL; static HashTable *global_constants_table = NULL; static HashTable *global_auto_globals_table = NULL; static HashTable *global_persistent_list = NULL; -static zend_uintptr_t global_last_static_member = 0; ZEND_TSRMLS_CACHE_DEFINE() # define GLOBAL_FUNCTION_TABLE global_function_table # define GLOBAL_CLASS_TABLE global_class_table @@ -626,13 +627,22 @@ static void compiler_globals_ctor(zend_compiler_globals *compiler_globals) /* {{ zend_hash_init_ex(compiler_globals->auto_globals, 8, NULL, auto_global_dtor, 1, 0); zend_hash_copy(compiler_globals->auto_globals, global_auto_globals_table, auto_global_copy_ctor); - compiler_globals->last_static_member = global_last_static_member; - if (compiler_globals->last_static_member) { - compiler_globals->static_members_table = calloc(compiler_globals->last_static_member + 1, sizeof(zval*)); - } else { - compiler_globals->static_members_table = NULL; - } compiler_globals->script_encoding_list = 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. */ + compiler_globals->map_ptr_base = NULL; + compiler_globals->map_ptr_size = 0; + compiler_globals->map_ptr_last = global_map_ptr_last; + if (compiler_globals->map_ptr_last) { + /* Allocate map_ptr table */ + compiler_globals->map_ptr_size = ZEND_MM_ALIGNED_SIZE_EX(compiler_globals->map_ptr_last, 4096); + compiler_globals->map_ptr_base = pemalloc(compiler_globals->map_ptr_size * sizeof(void*), 1); + memset(compiler_globals->map_ptr_base, 0, compiler_globals->map_ptr_last * sizeof(void*)); + } +#else +# error "Unknown ZEND_MAP_PTR_KIND" +#endif } /* }}} */ @@ -650,13 +660,14 @@ static void compiler_globals_dtor(zend_compiler_globals *compiler_globals) /* {{ zend_hash_destroy(compiler_globals->auto_globals); free(compiler_globals->auto_globals); } - if (compiler_globals->static_members_table) { - free(compiler_globals->static_members_table); - } if (compiler_globals->script_encoding_list) { pefree((char*)compiler_globals->script_encoding_list, 1); } - compiler_globals->last_static_member = 0; + if (compiler_globals->map_ptr_base) { + free(compiler_globals->map_ptr_base); + compiler_globals->map_ptr_base = NULL; + compiler_globals->map_ptr_size = 0; + } } /* }}} */ @@ -879,6 +890,22 @@ int zend_startup(zend_utility_functions *utility_functions, char **extensions) / #ifdef ZEND_WIN32 zend_get_windows_version_info(&EG(windows_version_info)); #endif +# if ZEND_MAP_PTR_KIND == ZEND_MAP_PTR_KIND_PTR + /* Create a map region, used for indirect pointers from shared to + * process memory. It's allocatred once and never resized. + * All processes must map it into the same address space. + */ + CG(map_ptr_size) = 1024 * 1024; // TODO: initial size ??? + CG(map_ptr_last) = 0; + CG(map_ptr_base) = pemalloc(CG(map_ptr_size) * sizeof(void*), 1); +# elif ZEND_MAP_PTR_KIND == ZEND_MAP_PTR_KIND_PTR_OR_OFFSET + /* Map region is going to be created and resized at run-time. */ + CG(map_ptr_base) = NULL; + CG(map_ptr_size) = 0; + CG(map_ptr_last) = 0; +# else +# error "Unknown ZEND_MAP_PTR_KIND" +# endif #endif EG(error_reporting) = E_ALL & ~E_NOTICE; @@ -931,7 +958,7 @@ int zend_post_startup(void) /* {{{ */ *GLOBAL_FUNCTION_TABLE = *compiler_globals->function_table; *GLOBAL_CLASS_TABLE = *compiler_globals->class_table; *GLOBAL_CONSTANTS_TABLE = *executor_globals->zend_constants; - global_last_static_member = compiler_globals->last_static_member; + global_map_ptr_last = compiler_globals->map_ptr_last; short_tags_default = CG(short_tags); compiler_options_default = CG(compiler_options); @@ -950,6 +977,8 @@ int zend_post_startup(void) /* {{{ */ executor_globals_ctor(executor_globals); global_persistent_list = &EG(persistent_list); zend_copy_ini_directives(); +#else + global_map_ptr_last = CG(map_ptr_last); #endif if (zend_post_startup_cb) { @@ -996,6 +1025,12 @@ void zend_shutdown(void) /* {{{ */ GLOBAL_CLASS_TABLE = NULL; GLOBAL_AUTO_GLOBALS_TABLE = NULL; GLOBAL_CONSTANTS_TABLE = NULL; +#else + if (CG(map_ptr_base)) { + free(CG(map_ptr_base)); + CG(map_ptr_base) = NULL; + CG(map_ptr_size) = 0; + } #endif zend_destroy_rsrc_list_dtors(); } @@ -1077,17 +1112,12 @@ ZEND_API void zend_activate(void) /* {{{ */ init_compiler(); init_executor(); startup_scanner(); + if (CG(map_ptr_last)) { + memset(CG(map_ptr_base), 0, CG(map_ptr_last) * sizeof(void*)); + } } /* }}} */ -#ifdef ZTS -void zend_reset_internal_classes(void) /* {{{ */ -{ - CG(last_static_member) = global_last_static_member; -} -/* }}} */ -#endif - void zend_call_destructors(void) /* {{{ */ { zend_try { @@ -1619,6 +1649,62 @@ void free_estring(char **str_p) /* {{{ */ } /* }}} */ +ZEND_API void zend_map_ptr_reset(void) +{ + CG(map_ptr_last) = global_map_ptr_last; +} + +ZEND_API void *zend_map_ptr_new(void) +{ + void **ptr; + + if (CG(map_ptr_last) >= CG(map_ptr_size)) { +#if ZEND_MAP_PTR_KIND == ZEND_MAP_PTR_KIND_PTR + // TODO: error ??? + ZEND_ASSERT(0); +#elif ZEND_MAP_PTR_KIND == ZEND_MAP_PTR_KIND_PTR_OR_OFFSET + /* Grow map_ptr table */ + CG(map_ptr_size) = ZEND_MM_ALIGNED_SIZE_EX(CG(map_ptr_last) + 1, 4096); + CG(map_ptr_base) = perealloc(CG(map_ptr_base), CG(map_ptr_size) * sizeof(void*), 1); +#else +# error "Unknown ZEND_MAP_PTR_KIND" +#endif + } + ptr = (void**)CG(map_ptr_base) + CG(map_ptr_last); + *ptr = NULL; + CG(map_ptr_last)++; +#if ZEND_MAP_PTR_KIND == ZEND_MAP_PTR_KIND_PTR + return ptr; +#elif ZEND_MAP_PTR_KIND == ZEND_MAP_PTR_KIND_PTR_OR_OFFSET + return ZEND_MAP_PTR_PTR2OFFSET(ptr); +#else +# error "Unknown ZEND_MAP_PTR_KIND" +#endif +} + +ZEND_API void zend_map_ptr_extend(size_t last) +{ + if (last > CG(map_ptr_last)) { + void **ptr; + + if (last >= CG(map_ptr_size)) { +#if ZEND_MAP_PTR_KIND == ZEND_MAP_PTR_KIND_PTR + /* This may never happen */ + ZEND_ASSERT(0); +#elif ZEND_MAP_PTR_KIND == ZEND_MAP_PTR_KIND_PTR_OR_OFFSET + /* Grow map_ptr table */ + CG(map_ptr_size) = ZEND_MM_ALIGNED_SIZE_EX(last, 4096); + CG(map_ptr_base) = perealloc(CG(map_ptr_base), CG(map_ptr_size) * sizeof(void*), 1); +#else +# error "Unknown ZEND_MAP_PTR_KIND" +#endif + } + ptr = (void**)CG(map_ptr_base) + CG(map_ptr_last); + memset(ptr, 0, (last - CG(map_ptr_last)) * sizeof(void*)); + CG(map_ptr_last) = last; + } +} + /* * Local variables: * tab-width: 4 diff --git a/Zend/zend.h b/Zend/zend.h index d6d427ccdbed4..0d3ce9ffa8ead 100644 --- a/Zend/zend.h +++ b/Zend/zend.h @@ -25,6 +25,7 @@ #define ZEND_ENGINE_3 #include "zend_types.h" +#include "zend_map_ptr.h" #include "zend_errors.h" #include "zend_alloc.h" #include "zend_llist.h" @@ -127,10 +128,7 @@ struct _zend_class_entry { int default_static_members_count; zval *default_properties_table; zval *default_static_members_table; - union { - zval *static_members_table; - zend_uintptr_t static_members_table_idx; - }; + ZEND_MAP_PTR_DEF(zval *, static_members_table); HashTable function_table; HashTable properties_info; HashTable constants_table; @@ -271,9 +269,8 @@ ZEND_API void zend_activate_modules(void); ZEND_API void zend_deactivate_modules(void); ZEND_API void zend_post_deactivate_modules(void); -void zend_reset_internal_classes(void); - ZEND_API void free_estring(char **str_p); + END_EXTERN_C() /* output support */ diff --git a/Zend/zend_API.c b/Zend/zend_API.c index 47a674cc133a8..22ce397bd343f 100644 --- a/Zend/zend_API.c +++ b/Zend/zend_API.c @@ -2795,7 +2795,9 @@ ZEND_API int zend_register_class_alias_ex(const char *name, size_t name_len, zen ce = zend_hash_add_ptr(CG(class_table), lcname, ce); zend_string_release_ex(lcname, 0); if (ce) { - ce->refcount++; + if (!(ce->ce_flags & ZEND_ACC_IMMUTABLE)) { + ce->refcount++; + } return SUCCESS; } return FAILURE; @@ -3696,18 +3698,14 @@ ZEND_API int zend_declare_property_ex(zend_class_entry *ce, zend_string *name, z ce->default_static_members_table = perealloc(ce->default_static_members_table, sizeof(zval) * ce->default_static_members_count, ce->type == ZEND_INTERNAL_CLASS); } ZVAL_COPY_VALUE(&ce->default_static_members_table[property_info->offset], property); - if (ce->type == ZEND_USER_CLASS) { - ce->static_members_table = ce->default_static_members_table; -#ifdef ZTS - } else if (!ce->static_members_table_idx) { - CG(last_static_member)++; - ce->static_members_table_idx = CG(last_static_member); - if (CG(static_members_table)) { - /* Support for run-time declaration: dl() */ - CG(static_members_table) = realloc(CG(static_members_table), (CG(last_static_member) + 1) * sizeof(zval*)); - CG(static_members_table)[ce->static_members_table_idx] = NULL; + if (!ZEND_MAP_PTR(ce->static_members_table)) { + ZEND_ASSERT(ce->type == ZEND_INTERNAL_CLASS); + if (!EG(current_execute_data)) { + ZEND_MAP_PTR_NEW(ce->static_members_table); + } else { + /* internal class loaded by dl() */ + ZEND_MAP_PTR_INIT(ce->static_members_table, &ce->default_static_members_table); } -#endif } } else { if ((property_info_ptr = zend_hash_find_ptr(&ce->properties_info, name)) != NULL && diff --git a/Zend/zend_API.h b/Zend/zend_API.h index 54344fe0aae9c..d80f5d2f0d5c7 100644 --- a/Zend/zend_API.h +++ b/Zend/zend_API.h @@ -228,11 +228,8 @@ typedef struct _zend_fcall_info_cache { #define INIT_NS_CLASS_ENTRY(class_container, ns, class_name, functions) \ INIT_CLASS_ENTRY(class_container, ZEND_NS_NAME(ns, class_name), functions) -#ifdef ZTS -# define CE_STATIC_MEMBERS(ce) (((ce)->type==ZEND_USER_CLASS)?(ce)->static_members_table:CG(static_members_table)[(ce)->static_members_table_idx]) -#else -# define CE_STATIC_MEMBERS(ce) ((ce)->static_members_table) -#endif +#define CE_STATIC_MEMBERS(ce) \ + ((zval*)ZEND_MAP_PTR_GET((ce)->static_members_table)) #define ZEND_FCI_INITIALIZED(fci) ((fci).size != 0) diff --git a/Zend/zend_builtin_functions.c b/Zend/zend_builtin_functions.c index 1ff32cc355aad..07a314028e4fa 100644 --- a/Zend/zend_builtin_functions.c +++ b/Zend/zend_builtin_functions.c @@ -1769,7 +1769,7 @@ static int copy_class_or_interface_name(zval *el, int num_args, va_list args, ze if ((hash_key->key && ZSTR_VAL(hash_key->key)[0] != 0) && (comply_mask == (ce->ce_flags & mask))) { - if (ce->refcount > 1 && + if ((ce->refcount > 1 || (ce->ce_flags & ZEND_ACC_IMMUTABLE)) && !same_name(hash_key->key, ce->name)) { add_next_index_str(array, zend_string_copy(hash_key->key)); } else { diff --git a/Zend/zend_closures.c b/Zend/zend_closures.c index 7ad9327a3246a..a5e2123db1e06 100644 --- a/Zend/zend_closures.c +++ b/Zend/zend_closures.c @@ -151,9 +151,14 @@ ZEND_METHOD(Closure, call) if (ZEND_USER_CODE(my_function.type) && (closure->func.common.scope != Z_OBJCE_P(newthis) || (closure->func.common.fn_flags & ZEND_ACC_HEAP_RT_CACHE))) { - my_function.op_array.run_time_cache = emalloc(my_function.op_array.cache_size); + void *ptr; + my_function.op_array.fn_flags |= ZEND_ACC_HEAP_RT_CACHE; - memset(my_function.op_array.run_time_cache, 0, my_function.op_array.cache_size); + ptr = emalloc(sizeof(void*) + my_function.op_array.cache_size); + ZEND_MAP_PTR_INIT(my_function.op_array.run_time_cache, ptr); + ptr = (char*)ptr + sizeof(void*); + ZEND_MAP_PTR_SET(my_function.op_array.run_time_cache, ptr); + memset(ptr, 0, my_function.op_array.cache_size); } } @@ -176,7 +181,7 @@ ZEND_METHOD(Closure, call) /* copied upon generator creation */ GC_DELREF(&closure->std); } else if (fci_cache.function_handler->common.fn_flags & ZEND_ACC_HEAP_RT_CACHE) { - efree(my_function.op_array.run_time_cache); + efree(ZEND_MAP_PTR(my_function.op_array.run_time_cache)); } } /* }}} */ @@ -501,7 +506,8 @@ static HashTable *zend_closure_get_debug_info(zval *object, int *is_temp) /* {{{ debug_info = zend_new_array(8); if (closure->func.type == ZEND_USER_FUNCTION && closure->func.op_array.static_variables) { - HashTable *static_variables = closure->func.op_array.static_variables; + HashTable *static_variables = + ZEND_MAP_PTR_GET(closure->func.op_array.static_variables_ptr); ZVAL_ARR(&val, zend_array_dup(static_variables)); zend_hash_update(debug_info, ZSTR_KNOWN(ZEND_STR_STATIC), &val); } @@ -559,7 +565,7 @@ static HashTable *zend_closure_get_gc(zval *obj, zval **table, int *n) /* {{{ */ *table = Z_TYPE(closure->this_ptr) != IS_NULL ? &closure->this_ptr : NULL; *n = Z_TYPE(closure->this_ptr) != IS_NULL ? 1 : 0; return (closure->func.type == ZEND_USER_FUNCTION) ? - closure->func.op_array.static_variables : NULL; + ZEND_MAP_PTR_GET(closure->func.op_array.static_variables_ptr) : NULL; } /* }}} */ @@ -654,28 +660,44 @@ ZEND_API void zend_create_closure(zval *res, zend_function *func, zend_class_ent if (func->type == ZEND_USER_FUNCTION) { memcpy(&closure->func, func, sizeof(zend_op_array)); closure->func.common.fn_flags |= ZEND_ACC_CLOSURE; + closure->func.common.fn_flags &= ~ZEND_ACC_IMMUTABLE; + if (closure->func.op_array.static_variables) { closure->func.op_array.static_variables = zend_array_dup(closure->func.op_array.static_variables); } + ZEND_MAP_PTR_INIT(closure->func.op_array.static_variables_ptr, + &closure->func.op_array.static_variables); /* Runtime cache is scope-dependent, so we cannot reuse it if the scope changed */ - if (!closure->func.op_array.run_time_cache + if (!ZEND_MAP_PTR_GET(closure->func.op_array.run_time_cache) || func->common.scope != scope || (func->common.fn_flags & ZEND_ACC_HEAP_RT_CACHE) ) { - if (!func->op_array.run_time_cache && (func->common.fn_flags & ZEND_ACC_CLOSURE)) { + void *ptr; + + if (!ZEND_MAP_PTR_GET(func->op_array.run_time_cache) + && (func->common.fn_flags & ZEND_ACC_CLOSURE) + && (func->common.scope == scope || + !(func->common.fn_flags & ZEND_ACC_IMMUTABLE))) { /* If a real closure is used for the first time, we create a shared runtime cache * and remember which scope it is for. */ - func->common.scope = scope; - func->op_array.run_time_cache = zend_arena_alloc(&CG(arena), func->op_array.cache_size); - closure->func.op_array.run_time_cache = func->op_array.run_time_cache; + if (func->common.scope != scope) { + func->common.scope = scope; + } + closure->func.op_array.fn_flags &= ~ZEND_ACC_HEAP_RT_CACHE; + ptr = zend_arena_alloc(&CG(arena), func->op_array.cache_size); + ZEND_MAP_PTR_SET(func->op_array.run_time_cache, ptr); + ZEND_MAP_PTR_SET(closure->func.op_array.run_time_cache, ptr); } else { /* Otherwise, we use a non-shared runtime cache */ - closure->func.op_array.run_time_cache = emalloc(func->op_array.cache_size); closure->func.op_array.fn_flags |= ZEND_ACC_HEAP_RT_CACHE; + ptr = emalloc(sizeof(void*) + func->op_array.cache_size); + ZEND_MAP_PTR_INIT(closure->func.op_array.run_time_cache, ptr); + ptr = (char*)ptr + sizeof(void*); + ZEND_MAP_PTR_SET(closure->func.op_array.run_time_cache, ptr); } - memset(closure->func.op_array.run_time_cache, 0, func->op_array.cache_size); + memset(ptr, 0, func->op_array.cache_size); } if (closure->func.op_array.refcount) { (*closure->func.op_array.refcount)++; @@ -728,7 +750,7 @@ ZEND_API void zend_create_fake_closure(zval *res, zend_function *func, zend_clas void zend_closure_bind_var(zval *closure_zv, zend_string *var_name, zval *var) /* {{{ */ { zend_closure *closure = (zend_closure *) Z_OBJ_P(closure_zv); - HashTable *static_variables = closure->func.op_array.static_variables; + HashTable *static_variables = ZEND_MAP_PTR_GET(closure->func.op_array.static_variables_ptr); zend_hash_update(static_variables, var_name, var); } /* }}} */ @@ -736,7 +758,7 @@ void zend_closure_bind_var(zval *closure_zv, zend_string *var_name, zval *var) / void zend_closure_bind_var_ex(zval *closure_zv, uint32_t offset, zval *val) /* {{{ */ { zend_closure *closure = (zend_closure *) Z_OBJ_P(closure_zv); - HashTable *static_variables = closure->func.op_array.static_variables; + HashTable *static_variables = ZEND_MAP_PTR_GET(closure->func.op_array.static_variables_ptr); zval *var = (zval*)((char*)static_variables->arData + offset); zval_ptr_dtor(var); ZVAL_COPY_VALUE(var, val); diff --git a/Zend/zend_compile.c b/Zend/zend_compile.c index daaf00c9b1386..c907594fadb73 100644 --- a/Zend/zend_compile.c +++ b/Zend/zend_compile.c @@ -1021,7 +1021,9 @@ ZEND_API void function_add_ref(zend_function *function) /* {{{ */ GC_ADDREF(op_array->static_variables); } } - op_array->run_time_cache = NULL; + ZEND_MAP_PTR_INIT(op_array->static_variables_ptr, &op_array->static_variables); + 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 if (function->type == ZEND_INTERNAL_FUNCTION) { if (function->common.function_name) { zend_string_addref(function->common.function_name); @@ -1614,9 +1616,9 @@ ZEND_API void zend_initialize_class_data(zend_class_entry *ce, zend_bool nullify zend_hash_init_ex(&ce->function_table, 8, NULL, ZEND_FUNCTION_DTOR, persistent_hashes, 0); if (ce->type == ZEND_INTERNAL_CLASS) { - ce->static_members_table = NULL; + ZEND_MAP_PTR_INIT(ce->static_members_table, NULL); } else { - ce->static_members_table = ce->default_static_members_table; + ZEND_MAP_PTR_INIT(ce->static_members_table, &ce->default_static_members_table); ce->info.user.doc_comment = NULL; } @@ -5867,6 +5869,9 @@ void zend_compile_func_decl(znode *result, zend_ast *ast, zend_bool toplevel) /* init_op_array(op_array, ZEND_USER_FUNCTION, INITIAL_OP_ARRAY_SIZE); + 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); + op_array->fn_flags |= (orig_op_array->fn_flags & ZEND_ACC_STRICT_TYPES); op_array->fn_flags |= decl->flags; op_array->line_start = decl->start_lineno; diff --git a/Zend/zend_compile.h b/Zend/zend_compile.h index 0f44be816c1e5..4a597dd46766b 100644 --- a/Zend/zend_compile.h +++ b/Zend/zend_compile.h @@ -389,7 +389,8 @@ struct _zend_op_array { uint32_t last; /* number of opcodes */ zend_op *opcodes; - void **run_time_cache; + ZEND_MAP_PTR_DEF(void **, run_time_cache); + ZEND_MAP_PTR_DEF(HashTable *, static_variables_ptr); HashTable *static_variables; zend_string **vars; /* names of CV variables */ @@ -660,19 +661,25 @@ struct _zend_execute_data { (node).constant = RT_CONSTANT(opline, node) - (op_array)->literals; \ } while (0) +#define RUN_TIME_CACHE(op_array) \ + ZEND_MAP_PTR_GET((op_array)->run_time_cache) + +#define ZEND_OP_ARRAY_EXTENSION(op_array, handle) \ + ((void**)RUN_TIME_CACHE(op_array))[handle] + #if ZEND_EX_USE_RUN_TIME_CACHE # define EX_RUN_TIME_CACHE() \ EX(run_time_cache) # define EX_LOAD_RUN_TIME_CACHE(op_array) do { \ - EX(run_time_cache) = (op_array)->run_time_cache; \ + EX(run_time_cache) = RUN_TIME_CACHE(op_array); \ } while (0) #else # define EX_RUN_TIME_CACHE() \ - EX(func)->op_array.run_time_cache + RUN_TIME_CACHE(&EX(func)->op_array) # define EX_LOAD_RUN_TIME_CACHE(op_array) do { \ } while (0) diff --git a/Zend/zend_execute.c b/Zend/zend_execute.c index 66ff7abfcd3fa..6cf75d0926fde 100644 --- a/Zend/zend_execute.c +++ b/Zend/zend_execute.c @@ -2407,39 +2407,20 @@ static zend_always_inline void i_init_func_execute_data(zend_op_array *op_array, } /* }}} */ -static zend_never_inline void ZEND_FASTCALL init_func_run_time_cache(zend_op_array *op_array) /* {{{ */ +static zend_always_inline void init_func_run_time_cache_i(zend_op_array *op_array) /* {{{ */ { - ZEND_ASSERT(op_array->run_time_cache == NULL); - op_array->run_time_cache = zend_arena_alloc(&CG(arena), op_array->cache_size); - memset(op_array->run_time_cache, 0, op_array->cache_size); -} -/* }}} */ + void **run_time_cache; -static zend_always_inline zend_function* ZEND_FASTCALL init_func_run_time_cache_i(zval *zv) /* {{{ */ -{ - zend_op_array *op_array = Z_PTR_P(zv); - - ZEND_ASSERT(op_array->run_time_cache == NULL); - if (op_array->fn_flags & ZEND_ACC_IMMUTABLE) { - zend_op_array *new_op_array = zend_arena_alloc(&CG(arena), sizeof(zend_op_array) + op_array->cache_size); - - Z_PTR_P(zv) = new_op_array; - memcpy(new_op_array, op_array, sizeof(zend_op_array)); - new_op_array->fn_flags &= ~ZEND_ACC_IMMUTABLE; - new_op_array->run_time_cache = (void**)(new_op_array + 1); - memset(new_op_array->run_time_cache, 0, new_op_array->cache_size); - return (zend_function*)new_op_array; - } else { - op_array->run_time_cache = zend_arena_alloc(&CG(arena), op_array->cache_size); - memset(op_array->run_time_cache, 0, op_array->cache_size); - return (zend_function*)op_array; - } + ZEND_ASSERT(RUN_TIME_CACHE(op_array) == NULL); + run_time_cache = zend_arena_alloc(&CG(arena), op_array->cache_size); + memset(run_time_cache, 0, op_array->cache_size); + ZEND_MAP_PTR_SET(op_array->run_time_cache, run_time_cache); } /* }}} */ -static zend_never_inline zend_function* init_func_run_time_cache_ex(zval *zv) /* {{{ */ +static zend_never_inline void ZEND_FASTCALL init_func_run_time_cache(zend_op_array *op_array) /* {{{ */ { - return init_func_run_time_cache_i(zv); + init_func_run_time_cache_i(op_array); } /* }}} */ @@ -2450,8 +2431,8 @@ ZEND_API zend_function * ZEND_FASTCALL zend_fetch_function(zend_string *name) /* if (EXPECTED(zv != NULL)) { zend_function *fbc = Z_FUNC_P(zv); - if (EXPECTED(fbc->type == ZEND_USER_FUNCTION) && UNEXPECTED(!fbc->op_array.run_time_cache)) { - fbc = (zend_function*)init_func_run_time_cache_i(zv); + if (EXPECTED(fbc->type == ZEND_USER_FUNCTION) && UNEXPECTED(!RUN_TIME_CACHE(&fbc->op_array))) { + init_func_run_time_cache_i(&fbc->op_array); } return fbc; } @@ -2465,8 +2446,8 @@ ZEND_API zend_function * ZEND_FASTCALL zend_fetch_function_str(const char *name, if (EXPECTED(zv != NULL)) { zend_function *fbc = Z_FUNC_P(zv); - if (EXPECTED(fbc->type == ZEND_USER_FUNCTION) && UNEXPECTED(!fbc->op_array.run_time_cache)) { - fbc = (zend_function*)init_func_run_time_cache_i(zv); + if (EXPECTED(fbc->type == ZEND_USER_FUNCTION) && UNEXPECTED(!RUN_TIME_CACHE(&fbc->op_array))) { + init_func_run_time_cache_i(&fbc->op_array); } return fbc; } @@ -2483,10 +2464,15 @@ static zend_always_inline void i_init_code_execute_data(zend_execute_data *execu zend_attach_symbol_table(execute_data); - if (!op_array->run_time_cache) { + if (!ZEND_MAP_PTR(op_array->run_time_cache)) { + void *ptr; + ZEND_ASSERT(op_array->fn_flags & ZEND_ACC_HEAP_RT_CACHE); - op_array->run_time_cache = emalloc(op_array->cache_size); - memset(op_array->run_time_cache, 0, op_array->cache_size); + ptr = emalloc(op_array->cache_size + sizeof(void*)); + ZEND_MAP_PTR_INIT(op_array->run_time_cache, ptr); + ptr = (char*)ptr + sizeof(void*); + ZEND_MAP_PTR_SET(op_array->run_time_cache, ptr); + memset(ptr, 0, op_array->cache_size); } EX_LOAD_RUN_TIME_CACHE(op_array); @@ -2505,7 +2491,7 @@ ZEND_API void zend_init_func_execute_data(zend_execute_data *ex, zend_op_array * #endif EX(prev_execute_data) = EG(current_execute_data); - if (!op_array->run_time_cache) { + if (!RUN_TIME_CACHE(op_array)) { init_func_run_time_cache(op_array); } i_init_func_execute_data(op_array, return_value, 1 EXECUTE_DATA_CC); @@ -2521,11 +2507,6 @@ ZEND_API void zend_init_func_execute_data(zend_execute_data *ex, zend_op_array * ZEND_API void zend_init_code_execute_data(zend_execute_data *execute_data, zend_op_array *op_array, zval *return_value) /* {{{ */ { EX(prev_execute_data) = EG(current_execute_data); - if (!op_array->run_time_cache) { - ZEND_ASSERT(op_array->fn_flags & ZEND_ACC_HEAP_RT_CACHE); - op_array->run_time_cache = emalloc(op_array->cache_size); - memset(op_array->run_time_cache, 0, op_array->cache_size); - } i_init_code_execute_data(execute_data, op_array, return_value); } /* }}} */ @@ -2867,7 +2848,7 @@ static zend_never_inline zend_execute_data *zend_init_dynamic_call_string(zend_s return NULL; } } - if (EXPECTED(fbc->type == ZEND_USER_FUNCTION) && UNEXPECTED(!fbc->op_array.run_time_cache)) { + if (EXPECTED(fbc->type == ZEND_USER_FUNCTION) && UNEXPECTED(!RUN_TIME_CACHE(&fbc->op_array))) { init_func_run_time_cache(&fbc->op_array); } } else { @@ -2885,8 +2866,8 @@ static zend_never_inline zend_execute_data *zend_init_dynamic_call_string(zend_s zend_string_release_ex(lcname, 0); fbc = Z_FUNC_P(func); - if (EXPECTED(fbc->type == ZEND_USER_FUNCTION) && UNEXPECTED(!fbc->op_array.run_time_cache)) { - fbc = init_func_run_time_cache_ex(func); + if (EXPECTED(fbc->type == ZEND_USER_FUNCTION) && UNEXPECTED(!RUN_TIME_CACHE(&fbc->op_array))) { + init_func_run_time_cache(&fbc->op_array); } called_scope = NULL; } @@ -2922,7 +2903,7 @@ static zend_never_inline zend_execute_data *zend_init_dynamic_call_object(zval * return NULL; } - if (EXPECTED(fbc->type == ZEND_USER_FUNCTION) && UNEXPECTED(!fbc->op_array.run_time_cache)) { + if (EXPECTED(fbc->type == ZEND_USER_FUNCTION) && UNEXPECTED(!RUN_TIME_CACHE(&fbc->op_array))) { init_func_run_time_cache(&fbc->op_array); } @@ -3009,7 +2990,7 @@ static zend_never_inline zend_execute_data *zend_init_dynamic_call_array(zend_ar return NULL; } - if (EXPECTED(fbc->type == ZEND_USER_FUNCTION) && UNEXPECTED(!fbc->op_array.run_time_cache)) { + if (EXPECTED(fbc->type == ZEND_USER_FUNCTION) && UNEXPECTED(!RUN_TIME_CACHE(&fbc->op_array))) { init_func_run_time_cache(&fbc->op_array); } diff --git a/Zend/zend_execute_API.c b/Zend/zend_execute_API.c index 2ade3e381865b..d321485ad3acc 100644 --- a/Zend/zend_execute_API.c +++ b/Zend/zend_execute_API.c @@ -318,9 +318,6 @@ void shutdown_executor(void) /* {{{ */ zend_hash_reverse_apply(EG(zend_constants), clean_non_persistent_constant_full); zend_hash_reverse_apply(EG(function_table), clean_non_persistent_function_full); zend_hash_reverse_apply(EG(class_table), clean_non_persistent_class_full); -#ifdef ZTS - zend_reset_internal_classes(); -#endif } else { ZEND_HASH_REVERSE_FOREACH_STR_KEY_VAL(EG(zend_constants), key, zv) { zend_constant *c = Z_PTR_P(zv); @@ -334,6 +331,7 @@ void shutdown_executor(void) /* {{{ */ efree(c); zend_string_release_ex(key, 0); } ZEND_HASH_FOREACH_END_DEL(); + ZEND_HASH_REVERSE_FOREACH_STR_KEY_VAL(EG(function_table), key, zv) { zend_function *func = Z_PTR_P(zv); if (_idx == EG(persistent_functions_count)) { diff --git a/Zend/zend_extensions.c b/Zend/zend_extensions.c index a2db4f590f097..076dcee5b58db 100644 --- a/Zend/zend_extensions.c +++ b/Zend/zend_extensions.c @@ -21,6 +21,7 @@ ZEND_API zend_llist zend_extensions; ZEND_API uint32_t zend_extension_flags = 0; +ZEND_API int zend_op_array_extension_handles = 0; static int last_resource_number; int zend_load_extension(const char *path) @@ -198,6 +199,7 @@ int zend_startup_extensions_mechanism() { /* Startup extensions mechanism */ zend_llist_init(&zend_extensions, sizeof(zend_extension), (void (*)(void *)) zend_extension_dtor, 1); + zend_op_array_extension_handles = 0; last_resource_number = 0; return SUCCESS; } @@ -257,6 +259,10 @@ ZEND_API int zend_get_resource_handle(zend_extension *extension) } } +ZEND_API int zend_get_op_array_extension_handle(void) +{ + return zend_op_array_extension_handles++; +} ZEND_API zend_extension *zend_get_extension(const char *extension_name) { diff --git a/Zend/zend_extensions.h b/Zend/zend_extensions.h index 866f852141718..8258aee276d92 100644 --- a/Zend/zend_extensions.h +++ b/Zend/zend_extensions.h @@ -111,7 +111,10 @@ struct _zend_extension { }; BEGIN_EXTERN_C() +extern ZEND_API int zend_op_array_extension_handles; + ZEND_API int zend_get_resource_handle(zend_extension *extension); +ZEND_API int zend_get_op_array_extension_handle(void); ZEND_API void zend_extension_dispatch_message(int message, void *arg); END_EXTERN_C() diff --git a/Zend/zend_globals.h b/Zend/zend_globals.h index 9412801c1b02c..f3d775af2e696 100644 --- a/Zend/zend_globals.h +++ b/Zend/zend_globals.h @@ -117,10 +117,9 @@ struct _zend_compiler_globals { zend_stack delayed_oplines_stack; -#ifdef ZTS - zval **static_members_table; - zend_uintptr_t last_static_member; -#endif + void *map_ptr_base; + size_t map_ptr_size; + size_t map_ptr_last; }; diff --git a/Zend/zend_inheritance.c b/Zend/zend_inheritance.c index 9a4efed9b9e81..ed5a554fb67e5 100644 --- a/Zend/zend_inheritance.c +++ b/Zend/zend_inheritance.c @@ -64,11 +64,16 @@ static zend_function *zend_duplicate_function(zend_function *func, zend_class_en /* reuse the same op_array structure */ return func; } - if (!(GC_FLAGS(func->op_array.static_variables) & IS_ARRAY_IMMUTABLE)) { - GC_ADDREF(func->op_array.static_variables); - } new_function = zend_arena_alloc(&CG(arena), sizeof(zend_op_array)); memcpy(new_function, func, sizeof(zend_op_array)); + if (ZEND_MAP_PTR_GET(func->op_array.static_variables_ptr)) { + /* See: Zend/tests/method_static_var.phpt */ + new_function->op_array.static_variables = ZEND_MAP_PTR_GET(func->op_array.static_variables_ptr); + } + if (!(GC_FLAGS(new_function->op_array.static_variables) & IS_ARRAY_IMMUTABLE)) { + GC_ADDREF(new_function->op_array.static_variables); + } + ZEND_MAP_PTR_INIT(new_function->op_array.static_variables_ptr, &new_function->op_array.static_variables); } return new_function; } @@ -87,23 +92,9 @@ static void do_inherit_parent_constructor(zend_class_entry *ce) /* {{{ */ if (EXPECTED(!ce->get_iterator)) { ce->get_iterator = parent->get_iterator; } - if (EXPECTED(!ce->iterator_funcs_ptr) && UNEXPECTED(parent->iterator_funcs_ptr)) { - if (ce->type == ZEND_INTERNAL_CLASS) { - ce->iterator_funcs_ptr = calloc(1, sizeof(zend_class_iterator_funcs)); - if (parent->iterator_funcs_ptr->zf_new_iterator) { - ce->iterator_funcs_ptr->zf_new_iterator = zend_hash_str_find_ptr(&ce->function_table, "getiterator", sizeof("getiterator") - 1); - } - if (parent->iterator_funcs_ptr->zf_current) { - ce->iterator_funcs_ptr->zf_rewind = zend_hash_str_find_ptr(&ce->function_table, "rewind", sizeof("rewind") - 1); - ce->iterator_funcs_ptr->zf_valid = zend_hash_str_find_ptr(&ce->function_table, "valid", sizeof("valid") - 1); - ce->iterator_funcs_ptr->zf_key = zend_hash_str_find_ptr(&ce->function_table, "key", sizeof("key") - 1); - ce->iterator_funcs_ptr->zf_current = zend_hash_str_find_ptr(&ce->function_table, "current", sizeof("current") - 1); - ce->iterator_funcs_ptr->zf_next = zend_hash_str_find_ptr(&ce->function_table, "next", sizeof("next") - 1); - } - } else { - ce->iterator_funcs_ptr = zend_arena_alloc(&CG(arena), sizeof(zend_class_iterator_funcs)); - memset(ce->iterator_funcs_ptr, 0, sizeof(zend_class_iterator_funcs)); - } + if (parent->iterator_funcs_ptr) { + /* Must be initialized through iface->interface_gets_implemented() */ + ZEND_ASSERT(ce->iterator_funcs_ptr); } if (EXPECTED(!ce->__get)) { ce->__get = parent->__get; @@ -894,6 +885,10 @@ ZEND_API void zend_do_inheritance(zend_class_entry *ce, zend_class_entry *parent dst = end + parent_ce->default_static_members_count; ce->default_static_members_table = end; } + if (CE_STATIC_MEMBERS(parent_ce) == NULL) { + ZEND_ASSERT(parent_ce->type == ZEND_INTERNAL_CLASS || (parent_ce->ce_flags & ZEND_ACC_IMMUTABLE)); + zend_class_init_statics(parent_ce); + } if (UNEXPECTED(parent_ce->type != ce->type)) { /* User class extends internal */ if (UNEXPECTED(zend_update_class_constants(parent_ce) != SUCCESS)) { @@ -910,7 +905,7 @@ ZEND_API void zend_do_inheritance(zend_class_entry *ce, zend_class_entry *parent } } while (dst != end); } else if (ce->type == ZEND_USER_CLASS) { - src = parent_ce->default_static_members_table + parent_ce->default_static_members_count; + src = CE_STATIC_MEMBERS(parent_ce) + parent_ce->default_static_members_count; do { dst--; src--; @@ -936,18 +931,14 @@ ZEND_API void zend_do_inheritance(zend_class_entry *ce, zend_class_entry *parent } while (dst != end); } ce->default_static_members_count += parent_ce->default_static_members_count; - if (ce->type == ZEND_USER_CLASS) { - ce->static_members_table = ce->default_static_members_table; -#ifdef ZTS - } else if (!ce->static_members_table_idx) { - CG(last_static_member)++; - ce->static_members_table_idx = CG(last_static_member); - if (CG(static_members_table)) { - /* Support for run-time declaration: dl() */ - CG(static_members_table) = realloc(CG(static_members_table), (CG(last_static_member) + 1) * sizeof(zval*)); - CG(static_members_table)[ce->static_members_table_idx] = NULL; + if (!ZEND_MAP_PTR(ce->static_members_table)) { + ZEND_ASSERT(ce->type == ZEND_INTERNAL_CLASS); + if (!EG(current_execute_data)) { + ZEND_MAP_PTR_NEW(ce->static_members_table); + } else { + /* internal class loaded by dl() */ + ZEND_MAP_PTR_INIT(ce->static_members_table, &ce->default_static_members_table); } -#endif } } @@ -1315,6 +1306,10 @@ static void zend_add_trait_method(zend_class_entry *ce, const char *name, zend_s } else { new_fn = zend_arena_alloc(&CG(arena), sizeof(zend_op_array)); memcpy(new_fn, fn, sizeof(zend_op_array)); + new_fn->op_array.fn_flags &= ~ZEND_ACC_IMMUTABLE; + ZEND_MAP_PTR_INIT(new_fn->op_array.run_time_cache, zend_arena_alloc(&CG(arena), sizeof(void*))); + ZEND_MAP_PTR_SET(new_fn->op_array.run_time_cache, NULL); + ZEND_MAP_PTR_INIT(new_fn->op_array.static_variables_ptr, &new_fn->op_array.static_variables); } fn = zend_hash_update_ptr(&ce->function_table, key, new_fn); zend_add_magic_methods(ce, key, fn); diff --git a/Zend/zend_map_ptr.h b/Zend/zend_map_ptr.h new file mode 100644 index 0000000000000..88872ab1fe57e --- /dev/null +++ b/Zend/zend_map_ptr.h @@ -0,0 +1,95 @@ +/* + +----------------------------------------------------------------------+ + | Zend Engine | + +----------------------------------------------------------------------+ + | Copyright (c) 1998-2018 Zend Technologies Ltd. (http://www.zend.com) | + +----------------------------------------------------------------------+ + | This source file is subject to version 2.00 of the Zend license, | + | that is bundled with this package in the file LICENSE, and is | + | available through the world-wide-web at the following url: | + | http://www.zend.com/license/2_00.txt. | + | If you did not receive a copy of the Zend license and are unable to | + | obtain it through the world-wide-web, please send a note to | + | license@zend.com so we can mail you a copy immediately. | + +----------------------------------------------------------------------+ + | Authors: Dmitry Stogov | + +----------------------------------------------------------------------+ +*/ + +#ifndef ZEND_MAP_PTR_H +#define ZEND_MAP_PTR_H + +#include "zend_portability.h" + +#define ZEND_MAP_PTR_KIND_PTR 0 +#define ZEND_MAP_PTR_KIND_PTR_OR_OFFSET 1 + +//#if defined(ZTS) || defined(TSRM_WIN32) +# define ZEND_MAP_PTR_KIND ZEND_MAP_PTR_KIND_PTR_OR_OFFSET +//#else +//# define ZEND_MAP_PTR_KIND ZEND_MAP_PTR_KIND_PTR +//#endif + +#if ZEND_MAP_PTR_KIND == ZEND_MAP_PTR_KIND_PTR +# define ZEND_MAP_PTR(ptr) \ + ptr ## __ptr +# define ZEND_MAP_PTR_DEF(type, name) \ + type * ZEND_MAP_PTR(name) +# define ZEND_MAP_PTR_GET(ptr) \ + (*(ZEND_MAP_PTR(ptr))) +# define ZEND_MAP_PTR_SET(ptr, val) do { \ + (*(ZEND_MAP_PTR(ptr))) = (val); \ + } while (0) +# define ZEND_MAP_PTR_INIT(ptr, val) do { \ + ZEND_MAP_PTR(ptr) = (val); \ + } while (0) +# define ZEND_MAP_PTR_NEW(ptr) do { \ + ZEND_MAP_PTR(ptr) = zend_map_ptr_new(); \ + } while (0) +#elif ZEND_MAP_PTR_KIND == ZEND_MAP_PTR_KIND_PTR_OR_OFFSET +# define ZEND_MAP_PTR(ptr) \ + ptr ## __ptr +# define ZEND_MAP_PTR_DEF(type, name) \ + type * ZEND_MAP_PTR(name) +# define ZEND_MAP_PTR_IS_OFFSET(ptr) \ + (((uintptr_t)ZEND_MAP_PTR(ptr)) & 1L) +# define ZEND_MAP_PTR_OFFSET2PTR(ptr) \ + ((void**)((char*)CG(map_ptr_base) + (uintptr_t)ZEND_MAP_PTR(ptr) - 1)) +# define ZEND_MAP_PTR_PTR2OFFSET(ptr) \ + ((void*)((uintptr_t)(((char*)(ptr)) - ((char*)CG(map_ptr_base))) | 1L)) +# define ZEND_MAP_PTR_GET(ptr) \ + (ZEND_MAP_PTR_IS_OFFSET(ptr) ? \ + *(ZEND_MAP_PTR_OFFSET2PTR(ptr)) : \ + (void*)(*(ZEND_MAP_PTR(ptr)))) +# define ZEND_MAP_PTR_SET(ptr, val) do { \ + if (ZEND_MAP_PTR_IS_OFFSET(ptr)) { \ + *(ZEND_MAP_PTR_OFFSET2PTR(ptr)) = (val); \ + } else { \ + *(ZEND_MAP_PTR(ptr)) = (val); \ + } \ + } while (0) +# define ZEND_MAP_PTR_INIT(ptr, val) do { \ + ZEND_MAP_PTR(ptr) = (val); \ + } while (0) +# define ZEND_MAP_PTR_NEW(ptr) do { \ + ZEND_MAP_PTR(ptr) = zend_map_ptr_new(); \ + } while (0) +#else +# error "Unknown ZEND_MAP_PTR_KIND" +#endif + +ZEND_API void zend_map_ptr_reset(void); +ZEND_API void *zend_map_ptr_new(void); +ZEND_API void zend_map_ptr_extend(size_t last); + +#endif /* ZEND_MAP_PTR_H */ + +/* + * Local variables: + * tab-width: 4 + * c-basic-offset: 4 + * indent-tabs-mode: t + * End: + * vim600: sw=4 ts=4 fdm=marker + * vim<600: sw=4 ts=4 + */ diff --git a/Zend/zend_object_handlers.c b/Zend/zend_object_handlers.c index 21e47bc164fa4..48549dfb999f1 100644 --- a/Zend/zend_object_handlers.c +++ b/Zend/zend_object_handlers.c @@ -1139,6 +1139,10 @@ ZEND_API zend_function *zend_get_call_trampoline_func(zend_class_entry *ce, zend size_t mname_len; zend_op_array *func; zend_function *fbc = is_static ? ce->__callstatic : ce->__call; + /* We use non-NULL value to avoid useless run_time_cache allocation. + * The low bit must be zero, to not be interpreted as a MAP_PTR offset. + */ + static const void *dummy = (void*)(intptr_t)2; ZEND_ASSERT(fbc); @@ -1157,7 +1161,7 @@ ZEND_API zend_function *zend_get_call_trampoline_func(zend_class_entry *ce, zend func->fn_flags |= ZEND_ACC_STATIC; } func->opcodes = &EG(call_trampoline_op); - func->run_time_cache = (void*)(intptr_t)-1; + ZEND_MAP_PTR_INIT(func->run_time_cache, (void***)&dummy); func->scope = fbc->common.scope; /* reserve space for arguments, local and temorary variables */ func->T = (fbc->type == ZEND_USER_FUNCTION)? MAX(fbc->op_array.last_var + fbc->op_array.T, 2) : 2; @@ -1352,11 +1356,7 @@ static void zend_intenal_class_init_statics(zend_class_entry *class_type) /* {{{ zend_intenal_class_init_statics(class_type->parent); } -#if ZTS - CG(static_members_table)[class_type->static_members_table_idx] = emalloc(sizeof(zval) * class_type->default_static_members_count); -#else - class_type->static_members_table = emalloc(sizeof(zval) * class_type->default_static_members_count); -#endif + ZEND_MAP_PTR_SET(class_type->static_members_table, emalloc(sizeof(zval) * class_type->default_static_members_count)); for (i = 0; i < class_type->default_static_members_count; i++) { p = &class_type->default_static_members_table[i]; if (Z_TYPE_P(p) == IS_INDIRECT) { @@ -1370,6 +1370,11 @@ static void zend_intenal_class_init_statics(zend_class_entry *class_type) /* {{{ } } /* }}} */ +ZEND_API void zend_class_init_statics(zend_class_entry *class_type) /* {{{ */ +{ + zend_intenal_class_init_statics(class_type); +} /* }}} */ + ZEND_API zval *zend_std_get_static_property(zend_class_entry *ce, zend_string *property_name, zend_bool silent) /* {{{ */ { zend_property_info *property_info = zend_hash_find_ptr(&ce->properties_info, property_name); @@ -1409,7 +1414,7 @@ ZEND_API zval *zend_std_get_static_property(zend_class_entry *ce, zend_string *p /* check if static properties were destroyed */ if (UNEXPECTED(CE_STATIC_MEMBERS(ce) == NULL)) { - if (ce->type == ZEND_INTERNAL_CLASS) { + if (ce->type == ZEND_INTERNAL_CLASS || (ce->ce_flags & ZEND_ACC_IMMUTABLE)) { zend_intenal_class_init_statics(ce); } else { undeclared_property: diff --git a/Zend/zend_object_handlers.h b/Zend/zend_object_handlers.h index 2f94ba1cb6ffc..6d347f4d5f995 100644 --- a/Zend/zend_object_handlers.h +++ b/Zend/zend_object_handlers.h @@ -197,6 +197,7 @@ extern const ZEND_API zend_object_handlers std_object_handlers; #define ZEND_PROPERTY_NOT_EMPTY ZEND_ISEMPTY /* Property is not empty */ #define ZEND_PROPERTY_EXISTS 0x2 /* Property exists */ +ZEND_API void zend_class_init_statics(zend_class_entry *ce); ZEND_API zend_function *zend_std_get_static_method(zend_class_entry *ce, zend_string *function_name_strval, const zval *key); ZEND_API zval *zend_std_get_static_property(zend_class_entry *ce, zend_string *property_name, zend_bool silent); ZEND_API ZEND_COLD zend_bool zend_std_unset_static_property(zend_class_entry *ce, zend_string *property_name); diff --git a/Zend/zend_opcode.c b/Zend/zend_opcode.c index f348b0b5892e3..9d974620ac61d 100644 --- a/Zend/zend_opcode.c +++ b/Zend/zend_opcode.c @@ -76,6 +76,7 @@ void init_op_array(zend_op_array *op_array, zend_uchar type, int initial_ops_siz op_array->last_live_range = 0; op_array->static_variables = NULL; + ZEND_MAP_PTR_INIT(op_array->static_variables_ptr, &op_array->static_variables); op_array->last_try_catch = 0; op_array->fn_flags = 0; @@ -83,8 +84,8 @@ void init_op_array(zend_op_array *op_array, zend_uchar type, int initial_ops_siz op_array->last_literal = 0; op_array->literals = NULL; - op_array->run_time_cache = NULL; - op_array->cache_size = 0; + ZEND_MAP_PTR_INIT(op_array->run_time_cache, NULL); + op_array->cache_size = zend_op_array_extension_handles * sizeof(void*); memset(op_array->reserved, 0, ZEND_MAX_RESERVED_RESOURCES * sizeof(void*)); @@ -145,11 +146,7 @@ ZEND_API void zend_cleanup_internal_class_data(zend_class_entry *ce) zval *p = static_members; zval *end = p + ce->default_static_members_count; -#ifdef ZTS - CG(static_members_table)[ce->static_members_table_idx] = NULL; -#else - ce->static_members_table = NULL; -#endif + ZEND_MAP_PTR_SET(ce->static_members_table, NULL); while (p != end) { i_zval_ptr_dtor(p); p++; @@ -213,7 +210,21 @@ ZEND_API void destroy_zend_class(zval *zv) zend_class_entry *ce = Z_PTR_P(zv); zend_function *fn; - if (--ce->refcount > 0) { + if (ce->ce_flags & ZEND_ACC_IMMUTABLE) { + zend_op_array *op_array; + + if (ce->default_static_members_count) { + zend_cleanup_internal_class_data(ce); + } + 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) { + destroy_op_array(op_array); + } + } ZEND_HASH_FOREACH_END(); + } + return; + } else if (--ce->refcount > 0) { return; } switch (ce->type) { @@ -305,6 +316,9 @@ ZEND_API void destroy_zend_class(zval *zv) p++; } free(ce->default_static_members_table); + if (ZEND_MAP_PTR(ce->static_members_table) != &ce->default_static_members_table) { + zend_cleanup_internal_class_data(ce); + } } zend_hash_destroy(&ce->properties_info); zend_string_release_ex(ce->name, 1); @@ -355,17 +369,18 @@ ZEND_API void destroy_op_array(zend_op_array *op_array) { uint32_t i; - if (op_array->static_variables && - !(GC_FLAGS(op_array->static_variables) & IS_ARRAY_IMMUTABLE)) { - if (GC_DELREF(op_array->static_variables) == 0) { - zend_array_destroy(op_array->static_variables); + if (op_array->static_variables) { + HashTable *ht = ZEND_MAP_PTR_GET(op_array->static_variables_ptr); + if (ht && !(GC_FLAGS(ht) & IS_ARRAY_IMMUTABLE)) { + if (GC_DELREF(ht) == 0) { + zend_array_destroy(ht); + } } } - if (op_array->run_time_cache - && (op_array->fn_flags & ZEND_ACC_HEAP_RT_CACHE)) { - efree(op_array->run_time_cache); - op_array->run_time_cache = NULL; + if ((op_array->fn_flags & ZEND_ACC_HEAP_RT_CACHE) + && ZEND_MAP_PTR(op_array->run_time_cache)) { + efree(ZEND_MAP_PTR(op_array->run_time_cache)); } if (!op_array->refcount || --(*op_array->refcount) > 0) { diff --git a/Zend/zend_vm_def.h b/Zend/zend_vm_def.h index 23332f9415881..8c43cbaa101a6 100644 --- a/Zend/zend_vm_def.h +++ b/Zend/zend_vm_def.h @@ -3076,7 +3076,7 @@ ZEND_VM_HOT_OBJ_HANDLER(112, ZEND_INIT_METHOD_CALL, CONST|TMPVAR|UNUSED|THIS|CV, EXPECTED(obj == orig_obj)) { CACHE_POLYMORPHIC_PTR(opline->result.num, called_scope, fbc); } - if (EXPECTED(fbc->type == ZEND_USER_FUNCTION) && UNEXPECTED(!fbc->op_array.run_time_cache)) { + if (EXPECTED(fbc->type == ZEND_USER_FUNCTION) && UNEXPECTED(!RUN_TIME_CACHE(&fbc->op_array))) { init_func_run_time_cache(&fbc->op_array); } } @@ -3197,7 +3197,7 @@ ZEND_VM_HANDLER(113, ZEND_INIT_STATIC_METHOD_CALL, UNUSED|CLASS_FETCH|CONST|VAR, EXPECTED(!(fbc->common.fn_flags & (ZEND_ACC_CALL_VIA_TRAMPOLINE|ZEND_ACC_NEVER_CACHE)))) { CACHE_POLYMORPHIC_PTR(opline->result.num, ce, fbc); } - if (EXPECTED(fbc->type == ZEND_USER_FUNCTION) && UNEXPECTED(!fbc->op_array.run_time_cache)) { + if (EXPECTED(fbc->type == ZEND_USER_FUNCTION) && UNEXPECTED(!RUN_TIME_CACHE(&fbc->op_array))) { init_func_run_time_cache(&fbc->op_array); } if (OP2_TYPE != IS_CONST) { @@ -3213,7 +3213,7 @@ ZEND_VM_HANDLER(113, ZEND_INIT_STATIC_METHOD_CALL, UNUSED|CLASS_FETCH|CONST|VAR, HANDLE_EXCEPTION(); } fbc = ce->constructor; - if (EXPECTED(fbc->type == ZEND_USER_FUNCTION) && UNEXPECTED(!fbc->op_array.run_time_cache)) { + if (EXPECTED(fbc->type == ZEND_USER_FUNCTION) && UNEXPECTED(!RUN_TIME_CACHE(&fbc->op_array))) { init_func_run_time_cache(&fbc->op_array); } } @@ -3266,8 +3266,8 @@ ZEND_VM_HOT_HANDLER(59, ZEND_INIT_FCALL_BY_NAME, ANY, CONST, NUM|CACHE_SLOT) ZEND_VM_DISPATCH_TO_HELPER(zend_undefined_function_helper, function_name, function_name); } fbc = Z_FUNC_P(func); - if (EXPECTED(fbc->type == ZEND_USER_FUNCTION) && UNEXPECTED(!fbc->op_array.run_time_cache)) { - fbc = init_func_run_time_cache_ex(func); + if (EXPECTED(fbc->type == ZEND_USER_FUNCTION) && UNEXPECTED(!RUN_TIME_CACHE(&fbc->op_array))) { + init_func_run_time_cache(&fbc->op_array); } CACHE_PTR(opline->result.num, fbc); } @@ -3387,7 +3387,7 @@ ZEND_VM_HANDLER(118, ZEND_INIT_USER_CALL, CONST, CONST|TMPVAR|CV, NUM) HANDLE_EXCEPTION(); } - if (EXPECTED(func->type == ZEND_USER_FUNCTION) && UNEXPECTED(!func->op_array.run_time_cache)) { + if (EXPECTED(func->type == ZEND_USER_FUNCTION) && UNEXPECTED(!RUN_TIME_CACHE(&func->op_array))) { init_func_run_time_cache(&func->op_array); } } else { @@ -3430,8 +3430,8 @@ ZEND_VM_HOT_HANDLER(69, ZEND_INIT_NS_FCALL_BY_NAME, ANY, CONST, NUM|CACHE_SLOT) } } fbc = Z_FUNC_P(func); - if (EXPECTED(fbc->type == ZEND_USER_FUNCTION) && UNEXPECTED(!fbc->op_array.run_time_cache)) { - fbc = init_func_run_time_cache_ex(func); + if (EXPECTED(fbc->type == ZEND_USER_FUNCTION) && UNEXPECTED(!RUN_TIME_CACHE(&fbc->op_array))) { + init_func_run_time_cache(&fbc->op_array); } CACHE_PTR(opline->result.num, fbc); } @@ -3461,8 +3461,8 @@ ZEND_VM_HOT_HANDLER(61, ZEND_INIT_FCALL, NUM, CONST, NUM|CACHE_SLOT) ZEND_VM_DISPATCH_TO_HELPER(zend_undefined_function_helper, function_name, fname); } fbc = Z_FUNC_P(func); - if (EXPECTED(fbc->type == ZEND_USER_FUNCTION) && UNEXPECTED(!fbc->op_array.run_time_cache)) { - fbc = init_func_run_time_cache_ex(func); + if (EXPECTED(fbc->type == ZEND_USER_FUNCTION) && UNEXPECTED(!RUN_TIME_CACHE(&fbc->op_array))) { + init_func_run_time_cache(&fbc->op_array); } CACHE_PTR(opline->result.num, fbc); } @@ -4940,7 +4940,7 @@ ZEND_VM_HANDLER(68, ZEND_NEW, UNUSED|CLASS_FETCH|CONST|VAR, UNUSED|CACHE_SLOT, N ZEND_CALL_FUNCTION, (zend_function *) &zend_pass_function, opline->extended_value, NULL, NULL); } else { - if (EXPECTED(constructor->type == ZEND_USER_FUNCTION) && UNEXPECTED(!constructor->op_array.run_time_cache)) { + if (EXPECTED(constructor->type == ZEND_USER_FUNCTION) && UNEXPECTED(!RUN_TIME_CACHE(&constructor->op_array))) { init_func_run_time_cache(&constructor->op_array); } /* We are not handling overloaded classes right now */ @@ -6960,20 +6960,10 @@ ZEND_VM_HANDLER(153, ZEND_DECLARE_LAMBDA_FUNCTION, CONST, UNUSED) zval *zfunc; zval *object; zend_class_entry *called_scope; - zend_function *fbc; zfunc = zend_hash_find_ex(EG(function_table), Z_STR_P(RT_CONSTANT(opline, opline->op1)), 1); ZEND_ASSERT(zfunc != NULL && Z_FUNC_P(zfunc)->type == ZEND_USER_FUNCTION); - fbc = Z_PTR_P(zfunc); - if (fbc->common.fn_flags & ZEND_ACC_IMMUTABLE) { - zend_function *new_func = zend_arena_alloc(&CG(arena), sizeof(zend_op_array)); - - memcpy(new_func, fbc, sizeof(zend_op_array)); - new_func->common.fn_flags &= ~ZEND_ACC_IMMUTABLE; - Z_PTR_P(zfunc) = fbc = new_func; - } - if (Z_TYPE(EX(This)) == IS_OBJECT) { called_scope = Z_OBJCE(EX(This)); if (UNEXPECTED((Z_FUNC_P(zfunc)->common.fn_flags & ZEND_ACC_STATIC) || @@ -7622,7 +7612,7 @@ ZEND_VM_HANDLER(158, ZEND_CALL_TRAMPOLINE, ANY, ANY) fbc = call->func; if (EXPECTED(fbc->type == ZEND_USER_FUNCTION)) { - if (UNEXPECTED(!fbc->op_array.run_time_cache)) { + if (UNEXPECTED(!RUN_TIME_CACHE(&fbc->op_array))) { init_func_run_time_cache(&fbc->op_array); } execute_data = call; @@ -7748,13 +7738,17 @@ ZEND_VM_HANDLER(183, ZEND_BIND_STATIC, CV, UNUSED, REF) variable_ptr = GET_OP1_ZVAL_PTR_PTR_UNDEF(BP_VAR_W); zval_ptr_dtor(variable_ptr); - ht = EX(func)->op_array.static_variables; - ZEND_ASSERT(ht != NULL); - if (GC_REFCOUNT(ht) > 1) { + ht = ZEND_MAP_PTR_GET(EX(func)->op_array.static_variables_ptr); + if (!ht) { + ZEND_ASSERT(EX(func)->op_array.fn_flags & ZEND_ACC_IMMUTABLE); + ht = zend_array_dup(EX(func)->op_array.static_variables); + ZEND_MAP_PTR_SET(EX(func)->op_array.static_variables_ptr, ht); + } else if (GC_REFCOUNT(ht) > 1) { if (!(GC_FLAGS(ht) & IS_ARRAY_IMMUTABLE)) { GC_DELREF(ht); } - EX(func)->op_array.static_variables = ht = zend_array_dup(ht); + ht = zend_array_dup(ht); + ZEND_MAP_PTR_SET(EX(func)->op_array.static_variables_ptr, ht); } value = (zval*)((char*)ht->arData + (opline->extended_value & ~ZEND_BIND_REF)); diff --git a/Zend/zend_vm_execute.h b/Zend/zend_vm_execute.h index 69e15a9fd96b9..20d55eb1ba290 100644 --- a/Zend/zend_vm_execute.h +++ b/Zend/zend_vm_execute.h @@ -1908,7 +1908,7 @@ static ZEND_OPCODE_HANDLER_RET ZEND_FASTCALL ZEND_CALL_TRAMPOLINE_SPEC_HANDLER(Z fbc = call->func; if (EXPECTED(fbc->type == ZEND_USER_FUNCTION)) { - if (UNEXPECTED(!fbc->op_array.run_time_cache)) { + if (UNEXPECTED(!RUN_TIME_CACHE(&fbc->op_array))) { init_func_run_time_cache(&fbc->op_array); } execute_data = call; @@ -2026,8 +2026,8 @@ static ZEND_VM_HOT ZEND_OPCODE_HANDLER_RET ZEND_FASTCALL ZEND_INIT_FCALL_BY_NAME ZEND_VM_TAIL_CALL(zend_undefined_function_helper_SPEC(function_name ZEND_OPCODE_HANDLER_ARGS_PASSTHRU_CC)); } fbc = Z_FUNC_P(func); - if (EXPECTED(fbc->type == ZEND_USER_FUNCTION) && UNEXPECTED(!fbc->op_array.run_time_cache)) { - fbc = init_func_run_time_cache_ex(func); + if (EXPECTED(fbc->type == ZEND_USER_FUNCTION) && UNEXPECTED(!RUN_TIME_CACHE(&fbc->op_array))) { + init_func_run_time_cache(&fbc->op_array); } CACHE_PTR(opline->result.num, fbc); } @@ -2115,8 +2115,8 @@ static ZEND_VM_HOT ZEND_OPCODE_HANDLER_RET ZEND_FASTCALL ZEND_INIT_NS_FCALL_BY_N } } fbc = Z_FUNC_P(func); - if (EXPECTED(fbc->type == ZEND_USER_FUNCTION) && UNEXPECTED(!fbc->op_array.run_time_cache)) { - fbc = init_func_run_time_cache_ex(func); + if (EXPECTED(fbc->type == ZEND_USER_FUNCTION) && UNEXPECTED(!RUN_TIME_CACHE(&fbc->op_array))) { + init_func_run_time_cache(&fbc->op_array); } CACHE_PTR(opline->result.num, fbc); } @@ -2146,8 +2146,8 @@ static ZEND_VM_HOT ZEND_OPCODE_HANDLER_RET ZEND_FASTCALL ZEND_INIT_FCALL_SPEC_CO ZEND_VM_TAIL_CALL(zend_undefined_function_helper_SPEC(fname ZEND_OPCODE_HANDLER_ARGS_PASSTHRU_CC)); } fbc = Z_FUNC_P(func); - if (EXPECTED(fbc->type == ZEND_USER_FUNCTION) && UNEXPECTED(!fbc->op_array.run_time_cache)) { - fbc = init_func_run_time_cache_ex(func); + if (EXPECTED(fbc->type == ZEND_USER_FUNCTION) && UNEXPECTED(!RUN_TIME_CACHE(&fbc->op_array))) { + init_func_run_time_cache(&fbc->op_array); } CACHE_PTR(opline->result.num, fbc); } @@ -5054,7 +5054,7 @@ static ZEND_VM_COLD ZEND_OPCODE_HANDLER_RET ZEND_FASTCALL ZEND_INIT_METHOD_CALL_ EXPECTED(obj == orig_obj)) { CACHE_POLYMORPHIC_PTR(opline->result.num, called_scope, fbc); } - if (EXPECTED(fbc->type == ZEND_USER_FUNCTION) && UNEXPECTED(!fbc->op_array.run_time_cache)) { + if (EXPECTED(fbc->type == ZEND_USER_FUNCTION) && UNEXPECTED(!RUN_TIME_CACHE(&fbc->op_array))) { init_func_run_time_cache(&fbc->op_array); } } @@ -5174,7 +5174,7 @@ static ZEND_OPCODE_HANDLER_RET ZEND_FASTCALL ZEND_INIT_STATIC_METHOD_CALL_SPEC_C EXPECTED(!(fbc->common.fn_flags & (ZEND_ACC_CALL_VIA_TRAMPOLINE|ZEND_ACC_NEVER_CACHE)))) { CACHE_POLYMORPHIC_PTR(opline->result.num, ce, fbc); } - if (EXPECTED(fbc->type == ZEND_USER_FUNCTION) && UNEXPECTED(!fbc->op_array.run_time_cache)) { + if (EXPECTED(fbc->type == ZEND_USER_FUNCTION) && UNEXPECTED(!RUN_TIME_CACHE(&fbc->op_array))) { init_func_run_time_cache(&fbc->op_array); } if (IS_CONST != IS_CONST) { @@ -5190,7 +5190,7 @@ static ZEND_OPCODE_HANDLER_RET ZEND_FASTCALL ZEND_INIT_STATIC_METHOD_CALL_SPEC_C HANDLE_EXCEPTION(); } fbc = ce->constructor; - if (EXPECTED(fbc->type == ZEND_USER_FUNCTION) && UNEXPECTED(!fbc->op_array.run_time_cache)) { + if (EXPECTED(fbc->type == ZEND_USER_FUNCTION) && UNEXPECTED(!RUN_TIME_CACHE(&fbc->op_array))) { init_func_run_time_cache(&fbc->op_array); } } @@ -5278,7 +5278,7 @@ static ZEND_OPCODE_HANDLER_RET ZEND_FASTCALL ZEND_INIT_USER_CALL_SPEC_CONST_CONS HANDLE_EXCEPTION(); } - if (EXPECTED(func->type == ZEND_USER_FUNCTION) && UNEXPECTED(!func->op_array.run_time_cache)) { + if (EXPECTED(func->type == ZEND_USER_FUNCTION) && UNEXPECTED(!RUN_TIME_CACHE(&func->op_array))) { init_func_run_time_cache(&func->op_array); } } else { @@ -7215,7 +7215,7 @@ static ZEND_VM_COLD ZEND_OPCODE_HANDLER_RET ZEND_FASTCALL ZEND_INIT_METHOD_CALL_ EXPECTED(obj == orig_obj)) { CACHE_POLYMORPHIC_PTR(opline->result.num, called_scope, fbc); } - if (EXPECTED(fbc->type == ZEND_USER_FUNCTION) && UNEXPECTED(!fbc->op_array.run_time_cache)) { + if (EXPECTED(fbc->type == ZEND_USER_FUNCTION) && UNEXPECTED(!RUN_TIME_CACHE(&fbc->op_array))) { init_func_run_time_cache(&fbc->op_array); } } @@ -7335,7 +7335,7 @@ static ZEND_OPCODE_HANDLER_RET ZEND_FASTCALL ZEND_INIT_STATIC_METHOD_CALL_SPEC_C EXPECTED(!(fbc->common.fn_flags & (ZEND_ACC_CALL_VIA_TRAMPOLINE|ZEND_ACC_NEVER_CACHE)))) { CACHE_POLYMORPHIC_PTR(opline->result.num, ce, fbc); } - if (EXPECTED(fbc->type == ZEND_USER_FUNCTION) && UNEXPECTED(!fbc->op_array.run_time_cache)) { + if (EXPECTED(fbc->type == ZEND_USER_FUNCTION) && UNEXPECTED(!RUN_TIME_CACHE(&fbc->op_array))) { init_func_run_time_cache(&fbc->op_array); } if ((IS_TMP_VAR|IS_VAR) != IS_CONST) { @@ -7351,7 +7351,7 @@ static ZEND_OPCODE_HANDLER_RET ZEND_FASTCALL ZEND_INIT_STATIC_METHOD_CALL_SPEC_C HANDLE_EXCEPTION(); } fbc = ce->constructor; - if (EXPECTED(fbc->type == ZEND_USER_FUNCTION) && UNEXPECTED(!fbc->op_array.run_time_cache)) { + if (EXPECTED(fbc->type == ZEND_USER_FUNCTION) && UNEXPECTED(!RUN_TIME_CACHE(&fbc->op_array))) { init_func_run_time_cache(&fbc->op_array); } } @@ -7440,7 +7440,7 @@ static ZEND_OPCODE_HANDLER_RET ZEND_FASTCALL ZEND_INIT_USER_CALL_SPEC_CONST_TMPV HANDLE_EXCEPTION(); } - if (EXPECTED(func->type == ZEND_USER_FUNCTION) && UNEXPECTED(!func->op_array.run_time_cache)) { + if (EXPECTED(func->type == ZEND_USER_FUNCTION) && UNEXPECTED(!RUN_TIME_CACHE(&func->op_array))) { init_func_run_time_cache(&func->op_array); } } else { @@ -8566,7 +8566,7 @@ static ZEND_OPCODE_HANDLER_RET ZEND_FASTCALL ZEND_INIT_STATIC_METHOD_CALL_SPEC_C EXPECTED(!(fbc->common.fn_flags & (ZEND_ACC_CALL_VIA_TRAMPOLINE|ZEND_ACC_NEVER_CACHE)))) { CACHE_POLYMORPHIC_PTR(opline->result.num, ce, fbc); } - if (EXPECTED(fbc->type == ZEND_USER_FUNCTION) && UNEXPECTED(!fbc->op_array.run_time_cache)) { + if (EXPECTED(fbc->type == ZEND_USER_FUNCTION) && UNEXPECTED(!RUN_TIME_CACHE(&fbc->op_array))) { init_func_run_time_cache(&fbc->op_array); } if (IS_UNUSED != IS_CONST) { @@ -8582,7 +8582,7 @@ static ZEND_OPCODE_HANDLER_RET ZEND_FASTCALL ZEND_INIT_STATIC_METHOD_CALL_SPEC_C HANDLE_EXCEPTION(); } fbc = ce->constructor; - if (EXPECTED(fbc->type == ZEND_USER_FUNCTION) && UNEXPECTED(!fbc->op_array.run_time_cache)) { + if (EXPECTED(fbc->type == ZEND_USER_FUNCTION) && UNEXPECTED(!RUN_TIME_CACHE(&fbc->op_array))) { init_func_run_time_cache(&fbc->op_array); } } @@ -8724,7 +8724,7 @@ static ZEND_OPCODE_HANDLER_RET ZEND_FASTCALL ZEND_NEW_SPEC_CONST_UNUSED_HANDLER( ZEND_CALL_FUNCTION, (zend_function *) &zend_pass_function, opline->extended_value, NULL, NULL); } else { - if (EXPECTED(constructor->type == ZEND_USER_FUNCTION) && UNEXPECTED(!constructor->op_array.run_time_cache)) { + if (EXPECTED(constructor->type == ZEND_USER_FUNCTION) && UNEXPECTED(!RUN_TIME_CACHE(&constructor->op_array))) { init_func_run_time_cache(&constructor->op_array); } /* We are not handling overloaded classes right now */ @@ -9073,20 +9073,10 @@ static ZEND_OPCODE_HANDLER_RET ZEND_FASTCALL ZEND_DECLARE_LAMBDA_FUNCTION_SPEC_C zval *zfunc; zval *object; zend_class_entry *called_scope; - zend_function *fbc; zfunc = zend_hash_find_ex(EG(function_table), Z_STR_P(RT_CONSTANT(opline, opline->op1)), 1); ZEND_ASSERT(zfunc != NULL && Z_FUNC_P(zfunc)->type == ZEND_USER_FUNCTION); - fbc = Z_PTR_P(zfunc); - if (fbc->common.fn_flags & ZEND_ACC_IMMUTABLE) { - zend_function *new_func = zend_arena_alloc(&CG(arena), sizeof(zend_op_array)); - - memcpy(new_func, fbc, sizeof(zend_op_array)); - new_func->common.fn_flags &= ~ZEND_ACC_IMMUTABLE; - Z_PTR_P(zfunc) = fbc = new_func; - } - if (Z_TYPE(EX(This)) == IS_OBJECT) { called_scope = Z_OBJCE(EX(This)); if (UNEXPECTED((Z_FUNC_P(zfunc)->common.fn_flags & ZEND_ACC_STATIC) || @@ -10297,7 +10287,7 @@ static ZEND_VM_COLD ZEND_OPCODE_HANDLER_RET ZEND_FASTCALL ZEND_INIT_METHOD_CALL_ EXPECTED(obj == orig_obj)) { CACHE_POLYMORPHIC_PTR(opline->result.num, called_scope, fbc); } - if (EXPECTED(fbc->type == ZEND_USER_FUNCTION) && UNEXPECTED(!fbc->op_array.run_time_cache)) { + if (EXPECTED(fbc->type == ZEND_USER_FUNCTION) && UNEXPECTED(!RUN_TIME_CACHE(&fbc->op_array))) { init_func_run_time_cache(&fbc->op_array); } } @@ -10417,7 +10407,7 @@ static ZEND_OPCODE_HANDLER_RET ZEND_FASTCALL ZEND_INIT_STATIC_METHOD_CALL_SPEC_C EXPECTED(!(fbc->common.fn_flags & (ZEND_ACC_CALL_VIA_TRAMPOLINE|ZEND_ACC_NEVER_CACHE)))) { CACHE_POLYMORPHIC_PTR(opline->result.num, ce, fbc); } - if (EXPECTED(fbc->type == ZEND_USER_FUNCTION) && UNEXPECTED(!fbc->op_array.run_time_cache)) { + if (EXPECTED(fbc->type == ZEND_USER_FUNCTION) && UNEXPECTED(!RUN_TIME_CACHE(&fbc->op_array))) { init_func_run_time_cache(&fbc->op_array); } if (IS_CV != IS_CONST) { @@ -10433,7 +10423,7 @@ static ZEND_OPCODE_HANDLER_RET ZEND_FASTCALL ZEND_INIT_STATIC_METHOD_CALL_SPEC_C HANDLE_EXCEPTION(); } fbc = ce->constructor; - if (EXPECTED(fbc->type == ZEND_USER_FUNCTION) && UNEXPECTED(!fbc->op_array.run_time_cache)) { + if (EXPECTED(fbc->type == ZEND_USER_FUNCTION) && UNEXPECTED(!RUN_TIME_CACHE(&fbc->op_array))) { init_func_run_time_cache(&fbc->op_array); } } @@ -10521,7 +10511,7 @@ static ZEND_OPCODE_HANDLER_RET ZEND_FASTCALL ZEND_INIT_USER_CALL_SPEC_CONST_CV_H HANDLE_EXCEPTION(); } - if (EXPECTED(func->type == ZEND_USER_FUNCTION) && UNEXPECTED(!func->op_array.run_time_cache)) { + if (EXPECTED(func->type == ZEND_USER_FUNCTION) && UNEXPECTED(!RUN_TIME_CACHE(&func->op_array))) { init_func_run_time_cache(&func->op_array); } } else { @@ -14002,7 +13992,7 @@ static ZEND_OPCODE_HANDLER_RET ZEND_FASTCALL ZEND_INIT_METHOD_CALL_SPEC_TMPVAR_C EXPECTED(obj == orig_obj)) { CACHE_POLYMORPHIC_PTR(opline->result.num, called_scope, fbc); } - if (EXPECTED(fbc->type == ZEND_USER_FUNCTION) && UNEXPECTED(!fbc->op_array.run_time_cache)) { + if (EXPECTED(fbc->type == ZEND_USER_FUNCTION) && UNEXPECTED(!RUN_TIME_CACHE(&fbc->op_array))) { init_func_run_time_cache(&fbc->op_array); } } @@ -15597,7 +15587,7 @@ static ZEND_OPCODE_HANDLER_RET ZEND_FASTCALL ZEND_INIT_METHOD_CALL_SPEC_TMPVAR_T EXPECTED(obj == orig_obj)) { CACHE_POLYMORPHIC_PTR(opline->result.num, called_scope, fbc); } - if (EXPECTED(fbc->type == ZEND_USER_FUNCTION) && UNEXPECTED(!fbc->op_array.run_time_cache)) { + if (EXPECTED(fbc->type == ZEND_USER_FUNCTION) && UNEXPECTED(!RUN_TIME_CACHE(&fbc->op_array))) { init_func_run_time_cache(&fbc->op_array); } } @@ -17460,7 +17450,7 @@ static ZEND_OPCODE_HANDLER_RET ZEND_FASTCALL ZEND_INIT_METHOD_CALL_SPEC_TMPVAR_C EXPECTED(obj == orig_obj)) { CACHE_POLYMORPHIC_PTR(opline->result.num, called_scope, fbc); } - if (EXPECTED(fbc->type == ZEND_USER_FUNCTION) && UNEXPECTED(!fbc->op_array.run_time_cache)) { + if (EXPECTED(fbc->type == ZEND_USER_FUNCTION) && UNEXPECTED(!RUN_TIME_CACHE(&fbc->op_array))) { init_func_run_time_cache(&fbc->op_array); } } @@ -23580,7 +23570,7 @@ static ZEND_OPCODE_HANDLER_RET ZEND_FASTCALL ZEND_INIT_STATIC_METHOD_CALL_SPEC_V EXPECTED(!(fbc->common.fn_flags & (ZEND_ACC_CALL_VIA_TRAMPOLINE|ZEND_ACC_NEVER_CACHE)))) { CACHE_POLYMORPHIC_PTR(opline->result.num, ce, fbc); } - if (EXPECTED(fbc->type == ZEND_USER_FUNCTION) && UNEXPECTED(!fbc->op_array.run_time_cache)) { + if (EXPECTED(fbc->type == ZEND_USER_FUNCTION) && UNEXPECTED(!RUN_TIME_CACHE(&fbc->op_array))) { init_func_run_time_cache(&fbc->op_array); } if (IS_CONST != IS_CONST) { @@ -23596,7 +23586,7 @@ static ZEND_OPCODE_HANDLER_RET ZEND_FASTCALL ZEND_INIT_STATIC_METHOD_CALL_SPEC_V HANDLE_EXCEPTION(); } fbc = ce->constructor; - if (EXPECTED(fbc->type == ZEND_USER_FUNCTION) && UNEXPECTED(!fbc->op_array.run_time_cache)) { + if (EXPECTED(fbc->type == ZEND_USER_FUNCTION) && UNEXPECTED(!RUN_TIME_CACHE(&fbc->op_array))) { init_func_run_time_cache(&fbc->op_array); } } @@ -25859,7 +25849,7 @@ static ZEND_OPCODE_HANDLER_RET ZEND_FASTCALL ZEND_INIT_STATIC_METHOD_CALL_SPEC_V EXPECTED(!(fbc->common.fn_flags & (ZEND_ACC_CALL_VIA_TRAMPOLINE|ZEND_ACC_NEVER_CACHE)))) { CACHE_POLYMORPHIC_PTR(opline->result.num, ce, fbc); } - if (EXPECTED(fbc->type == ZEND_USER_FUNCTION) && UNEXPECTED(!fbc->op_array.run_time_cache)) { + if (EXPECTED(fbc->type == ZEND_USER_FUNCTION) && UNEXPECTED(!RUN_TIME_CACHE(&fbc->op_array))) { init_func_run_time_cache(&fbc->op_array); } if ((IS_TMP_VAR|IS_VAR) != IS_CONST) { @@ -25875,7 +25865,7 @@ static ZEND_OPCODE_HANDLER_RET ZEND_FASTCALL ZEND_INIT_STATIC_METHOD_CALL_SPEC_V HANDLE_EXCEPTION(); } fbc = ce->constructor; - if (EXPECTED(fbc->type == ZEND_USER_FUNCTION) && UNEXPECTED(!fbc->op_array.run_time_cache)) { + if (EXPECTED(fbc->type == ZEND_USER_FUNCTION) && UNEXPECTED(!RUN_TIME_CACHE(&fbc->op_array))) { init_func_run_time_cache(&fbc->op_array); } } @@ -27410,7 +27400,7 @@ static ZEND_OPCODE_HANDLER_RET ZEND_FASTCALL ZEND_INIT_STATIC_METHOD_CALL_SPEC_V EXPECTED(!(fbc->common.fn_flags & (ZEND_ACC_CALL_VIA_TRAMPOLINE|ZEND_ACC_NEVER_CACHE)))) { CACHE_POLYMORPHIC_PTR(opline->result.num, ce, fbc); } - if (EXPECTED(fbc->type == ZEND_USER_FUNCTION) && UNEXPECTED(!fbc->op_array.run_time_cache)) { + if (EXPECTED(fbc->type == ZEND_USER_FUNCTION) && UNEXPECTED(!RUN_TIME_CACHE(&fbc->op_array))) { init_func_run_time_cache(&fbc->op_array); } if (IS_UNUSED != IS_CONST) { @@ -27426,7 +27416,7 @@ static ZEND_OPCODE_HANDLER_RET ZEND_FASTCALL ZEND_INIT_STATIC_METHOD_CALL_SPEC_V HANDLE_EXCEPTION(); } fbc = ce->constructor; - if (EXPECTED(fbc->type == ZEND_USER_FUNCTION) && UNEXPECTED(!fbc->op_array.run_time_cache)) { + if (EXPECTED(fbc->type == ZEND_USER_FUNCTION) && UNEXPECTED(!RUN_TIME_CACHE(&fbc->op_array))) { init_func_run_time_cache(&fbc->op_array); } } @@ -27568,7 +27558,7 @@ static ZEND_OPCODE_HANDLER_RET ZEND_FASTCALL ZEND_NEW_SPEC_VAR_UNUSED_HANDLER(ZE ZEND_CALL_FUNCTION, (zend_function *) &zend_pass_function, opline->extended_value, NULL, NULL); } else { - if (EXPECTED(constructor->type == ZEND_USER_FUNCTION) && UNEXPECTED(!constructor->op_array.run_time_cache)) { + if (EXPECTED(constructor->type == ZEND_USER_FUNCTION) && UNEXPECTED(!RUN_TIME_CACHE(&constructor->op_array))) { init_func_run_time_cache(&constructor->op_array); } /* We are not handling overloaded classes right now */ @@ -29821,7 +29811,7 @@ static ZEND_OPCODE_HANDLER_RET ZEND_FASTCALL ZEND_INIT_STATIC_METHOD_CALL_SPEC_V EXPECTED(!(fbc->common.fn_flags & (ZEND_ACC_CALL_VIA_TRAMPOLINE|ZEND_ACC_NEVER_CACHE)))) { CACHE_POLYMORPHIC_PTR(opline->result.num, ce, fbc); } - if (EXPECTED(fbc->type == ZEND_USER_FUNCTION) && UNEXPECTED(!fbc->op_array.run_time_cache)) { + if (EXPECTED(fbc->type == ZEND_USER_FUNCTION) && UNEXPECTED(!RUN_TIME_CACHE(&fbc->op_array))) { init_func_run_time_cache(&fbc->op_array); } if (IS_CV != IS_CONST) { @@ -29837,7 +29827,7 @@ static ZEND_OPCODE_HANDLER_RET ZEND_FASTCALL ZEND_INIT_STATIC_METHOD_CALL_SPEC_V HANDLE_EXCEPTION(); } fbc = ce->constructor; - if (EXPECTED(fbc->type == ZEND_USER_FUNCTION) && UNEXPECTED(!fbc->op_array.run_time_cache)) { + if (EXPECTED(fbc->type == ZEND_USER_FUNCTION) && UNEXPECTED(!RUN_TIME_CACHE(&fbc->op_array))) { init_func_run_time_cache(&fbc->op_array); } } @@ -31741,7 +31731,7 @@ static ZEND_VM_HOT ZEND_OPCODE_HANDLER_RET ZEND_FASTCALL ZEND_INIT_METHOD_CALL_S EXPECTED(obj == orig_obj)) { CACHE_POLYMORPHIC_PTR(opline->result.num, called_scope, fbc); } - if (EXPECTED(fbc->type == ZEND_USER_FUNCTION) && UNEXPECTED(!fbc->op_array.run_time_cache)) { + if (EXPECTED(fbc->type == ZEND_USER_FUNCTION) && UNEXPECTED(!RUN_TIME_CACHE(&fbc->op_array))) { init_func_run_time_cache(&fbc->op_array); } } @@ -31861,7 +31851,7 @@ static ZEND_OPCODE_HANDLER_RET ZEND_FASTCALL ZEND_INIT_STATIC_METHOD_CALL_SPEC_U EXPECTED(!(fbc->common.fn_flags & (ZEND_ACC_CALL_VIA_TRAMPOLINE|ZEND_ACC_NEVER_CACHE)))) { CACHE_POLYMORPHIC_PTR(opline->result.num, ce, fbc); } - if (EXPECTED(fbc->type == ZEND_USER_FUNCTION) && UNEXPECTED(!fbc->op_array.run_time_cache)) { + if (EXPECTED(fbc->type == ZEND_USER_FUNCTION) && UNEXPECTED(!RUN_TIME_CACHE(&fbc->op_array))) { init_func_run_time_cache(&fbc->op_array); } if (IS_CONST != IS_CONST) { @@ -31877,7 +31867,7 @@ static ZEND_OPCODE_HANDLER_RET ZEND_FASTCALL ZEND_INIT_STATIC_METHOD_CALL_SPEC_U HANDLE_EXCEPTION(); } fbc = ce->constructor; - if (EXPECTED(fbc->type == ZEND_USER_FUNCTION) && UNEXPECTED(!fbc->op_array.run_time_cache)) { + if (EXPECTED(fbc->type == ZEND_USER_FUNCTION) && UNEXPECTED(!RUN_TIME_CACHE(&fbc->op_array))) { init_func_run_time_cache(&fbc->op_array); } } @@ -33426,7 +33416,7 @@ static ZEND_OPCODE_HANDLER_RET ZEND_FASTCALL ZEND_INIT_METHOD_CALL_SPEC_UNUSED_T EXPECTED(obj == orig_obj)) { CACHE_POLYMORPHIC_PTR(opline->result.num, called_scope, fbc); } - if (EXPECTED(fbc->type == ZEND_USER_FUNCTION) && UNEXPECTED(!fbc->op_array.run_time_cache)) { + if (EXPECTED(fbc->type == ZEND_USER_FUNCTION) && UNEXPECTED(!RUN_TIME_CACHE(&fbc->op_array))) { init_func_run_time_cache(&fbc->op_array); } } @@ -33546,7 +33536,7 @@ static ZEND_OPCODE_HANDLER_RET ZEND_FASTCALL ZEND_INIT_STATIC_METHOD_CALL_SPEC_U EXPECTED(!(fbc->common.fn_flags & (ZEND_ACC_CALL_VIA_TRAMPOLINE|ZEND_ACC_NEVER_CACHE)))) { CACHE_POLYMORPHIC_PTR(opline->result.num, ce, fbc); } - if (EXPECTED(fbc->type == ZEND_USER_FUNCTION) && UNEXPECTED(!fbc->op_array.run_time_cache)) { + if (EXPECTED(fbc->type == ZEND_USER_FUNCTION) && UNEXPECTED(!RUN_TIME_CACHE(&fbc->op_array))) { init_func_run_time_cache(&fbc->op_array); } if ((IS_TMP_VAR|IS_VAR) != IS_CONST) { @@ -33562,7 +33552,7 @@ static ZEND_OPCODE_HANDLER_RET ZEND_FASTCALL ZEND_INIT_STATIC_METHOD_CALL_SPEC_U HANDLE_EXCEPTION(); } fbc = ce->constructor; - if (EXPECTED(fbc->type == ZEND_USER_FUNCTION) && UNEXPECTED(!fbc->op_array.run_time_cache)) { + if (EXPECTED(fbc->type == ZEND_USER_FUNCTION) && UNEXPECTED(!RUN_TIME_CACHE(&fbc->op_array))) { init_func_run_time_cache(&fbc->op_array); } } @@ -34078,7 +34068,7 @@ static ZEND_OPCODE_HANDLER_RET ZEND_FASTCALL ZEND_INIT_STATIC_METHOD_CALL_SPEC_U EXPECTED(!(fbc->common.fn_flags & (ZEND_ACC_CALL_VIA_TRAMPOLINE|ZEND_ACC_NEVER_CACHE)))) { CACHE_POLYMORPHIC_PTR(opline->result.num, ce, fbc); } - if (EXPECTED(fbc->type == ZEND_USER_FUNCTION) && UNEXPECTED(!fbc->op_array.run_time_cache)) { + if (EXPECTED(fbc->type == ZEND_USER_FUNCTION) && UNEXPECTED(!RUN_TIME_CACHE(&fbc->op_array))) { init_func_run_time_cache(&fbc->op_array); } if (IS_UNUSED != IS_CONST) { @@ -34094,7 +34084,7 @@ static ZEND_OPCODE_HANDLER_RET ZEND_FASTCALL ZEND_INIT_STATIC_METHOD_CALL_SPEC_U HANDLE_EXCEPTION(); } fbc = ce->constructor; - if (EXPECTED(fbc->type == ZEND_USER_FUNCTION) && UNEXPECTED(!fbc->op_array.run_time_cache)) { + if (EXPECTED(fbc->type == ZEND_USER_FUNCTION) && UNEXPECTED(!RUN_TIME_CACHE(&fbc->op_array))) { init_func_run_time_cache(&fbc->op_array); } } @@ -34236,7 +34226,7 @@ static ZEND_OPCODE_HANDLER_RET ZEND_FASTCALL ZEND_NEW_SPEC_UNUSED_UNUSED_HANDLER ZEND_CALL_FUNCTION, (zend_function *) &zend_pass_function, opline->extended_value, NULL, NULL); } else { - if (EXPECTED(constructor->type == ZEND_USER_FUNCTION) && UNEXPECTED(!constructor->op_array.run_time_cache)) { + if (EXPECTED(constructor->type == ZEND_USER_FUNCTION) && UNEXPECTED(!RUN_TIME_CACHE(&constructor->op_array))) { init_func_run_time_cache(&constructor->op_array); } /* We are not handling overloaded classes right now */ @@ -35756,7 +35746,7 @@ static ZEND_OPCODE_HANDLER_RET ZEND_FASTCALL ZEND_INIT_METHOD_CALL_SPEC_UNUSED_C EXPECTED(obj == orig_obj)) { CACHE_POLYMORPHIC_PTR(opline->result.num, called_scope, fbc); } - if (EXPECTED(fbc->type == ZEND_USER_FUNCTION) && UNEXPECTED(!fbc->op_array.run_time_cache)) { + if (EXPECTED(fbc->type == ZEND_USER_FUNCTION) && UNEXPECTED(!RUN_TIME_CACHE(&fbc->op_array))) { init_func_run_time_cache(&fbc->op_array); } } @@ -35876,7 +35866,7 @@ static ZEND_OPCODE_HANDLER_RET ZEND_FASTCALL ZEND_INIT_STATIC_METHOD_CALL_SPEC_U EXPECTED(!(fbc->common.fn_flags & (ZEND_ACC_CALL_VIA_TRAMPOLINE|ZEND_ACC_NEVER_CACHE)))) { CACHE_POLYMORPHIC_PTR(opline->result.num, ce, fbc); } - if (EXPECTED(fbc->type == ZEND_USER_FUNCTION) && UNEXPECTED(!fbc->op_array.run_time_cache)) { + if (EXPECTED(fbc->type == ZEND_USER_FUNCTION) && UNEXPECTED(!RUN_TIME_CACHE(&fbc->op_array))) { init_func_run_time_cache(&fbc->op_array); } if (IS_CV != IS_CONST) { @@ -35892,7 +35882,7 @@ static ZEND_OPCODE_HANDLER_RET ZEND_FASTCALL ZEND_INIT_STATIC_METHOD_CALL_SPEC_U HANDLE_EXCEPTION(); } fbc = ce->constructor; - if (EXPECTED(fbc->type == ZEND_USER_FUNCTION) && UNEXPECTED(!fbc->op_array.run_time_cache)) { + if (EXPECTED(fbc->type == ZEND_USER_FUNCTION) && UNEXPECTED(!RUN_TIME_CACHE(&fbc->op_array))) { init_func_run_time_cache(&fbc->op_array); } } @@ -40719,7 +40709,7 @@ static ZEND_VM_HOT ZEND_OPCODE_HANDLER_RET ZEND_FASTCALL ZEND_INIT_METHOD_CALL_S EXPECTED(obj == orig_obj)) { CACHE_POLYMORPHIC_PTR(opline->result.num, called_scope, fbc); } - if (EXPECTED(fbc->type == ZEND_USER_FUNCTION) && UNEXPECTED(!fbc->op_array.run_time_cache)) { + if (EXPECTED(fbc->type == ZEND_USER_FUNCTION) && UNEXPECTED(!RUN_TIME_CACHE(&fbc->op_array))) { init_func_run_time_cache(&fbc->op_array); } } @@ -44383,7 +44373,7 @@ static ZEND_OPCODE_HANDLER_RET ZEND_FASTCALL ZEND_INIT_METHOD_CALL_SPEC_CV_TMPVA EXPECTED(obj == orig_obj)) { CACHE_POLYMORPHIC_PTR(opline->result.num, called_scope, fbc); } - if (EXPECTED(fbc->type == ZEND_USER_FUNCTION) && UNEXPECTED(!fbc->op_array.run_time_cache)) { + if (EXPECTED(fbc->type == ZEND_USER_FUNCTION) && UNEXPECTED(!RUN_TIME_CACHE(&fbc->op_array))) { init_func_run_time_cache(&fbc->op_array); } } @@ -47133,13 +47123,17 @@ static ZEND_OPCODE_HANDLER_RET ZEND_FASTCALL ZEND_BIND_STATIC_SPEC_CV_UNUSED_HAN variable_ptr = EX_VAR(opline->op1.var); zval_ptr_dtor(variable_ptr); - ht = EX(func)->op_array.static_variables; - ZEND_ASSERT(ht != NULL); - if (GC_REFCOUNT(ht) > 1) { + ht = ZEND_MAP_PTR_GET(EX(func)->op_array.static_variables_ptr); + if (!ht) { + ZEND_ASSERT(EX(func)->op_array.fn_flags & ZEND_ACC_IMMUTABLE); + ht = zend_array_dup(EX(func)->op_array.static_variables); + ZEND_MAP_PTR_SET(EX(func)->op_array.static_variables_ptr, ht); + } else if (GC_REFCOUNT(ht) > 1) { if (!(GC_FLAGS(ht) & IS_ARRAY_IMMUTABLE)) { GC_DELREF(ht); } - EX(func)->op_array.static_variables = ht = zend_array_dup(ht); + ht = zend_array_dup(ht); + ZEND_MAP_PTR_SET(EX(func)->op_array.static_variables_ptr, ht); } value = (zval*)((char*)ht->arData + (opline->extended_value & ~ZEND_BIND_REF)); @@ -50181,7 +50175,7 @@ static ZEND_OPCODE_HANDLER_RET ZEND_FASTCALL ZEND_INIT_METHOD_CALL_SPEC_CV_CV_HA EXPECTED(obj == orig_obj)) { CACHE_POLYMORPHIC_PTR(opline->result.num, called_scope, fbc); } - if (EXPECTED(fbc->type == ZEND_USER_FUNCTION) && UNEXPECTED(!fbc->op_array.run_time_cache)) { + if (EXPECTED(fbc->type == ZEND_USER_FUNCTION) && UNEXPECTED(!RUN_TIME_CACHE(&fbc->op_array))) { init_func_run_time_cache(&fbc->op_array); } } diff --git a/ext/opcache/Optimizer/compact_literals.c b/ext/opcache/Optimizer/compact_literals.c index f2cd7b4442fb7..7e24dbea31f49 100644 --- a/ext/opcache/Optimizer/compact_literals.c +++ b/ext/opcache/Optimizer/compact_literals.c @@ -28,6 +28,7 @@ #include "zend_constants.h" #include "zend_execute.h" #include "zend_vm.h" +#include "zend_extensions.h" #define DEBUG_COMPACT_LITERALS 0 @@ -505,7 +506,7 @@ void zend_optimizer_compact_literals(zend_op_array *op_array, zend_optimizer_ctx method_slot = property_slot + j; /* Update opcodes to use new literals table */ - cache_size = 0; + cache_size = zend_op_array_extension_handles * sizeof(void*); opline = op_array->opcodes; end = opline + op_array->last; while (opline < end) { diff --git a/ext/opcache/ZendAccelerator.c b/ext/opcache/ZendAccelerator.c index fdaa9aecc0adc..f00d15119fd66 100644 --- a/ext/opcache/ZendAccelerator.c +++ b/ext/opcache/ZendAccelerator.c @@ -1374,6 +1374,8 @@ static zend_persistent_script *store_script_in_file_cache(zend_persistent_script ZCG(mem) = zend_arena_alloc(&CG(arena), memory_used); #endif + zend_shared_alloc_clear_xlat_table(); + /* Copy into memory block */ new_persistent_script = zend_accel_script_persist(new_persistent_script, NULL, 0, 0); @@ -1539,6 +1541,8 @@ static zend_persistent_script *cache_script_in_shared_memory(zend_persistent_scr return new_persistent_script; } + zend_shared_alloc_clear_xlat_table(); + /* Copy into shared memory */ new_persistent_script = zend_accel_script_persist(new_persistent_script, &key, key_length, 1); @@ -2278,6 +2282,7 @@ static void zend_reset_cache_vars(void) ZSMMG(wasted_shared_memory) = 0; ZCSG(restart_pending) = 0; ZCSG(force_restart_time) = 0; + ZCSG(map_ptr_last) = CG(map_ptr_last); } static void accel_reset_pcre_cache(void) @@ -2374,6 +2379,7 @@ int accel_activate(INIT_FUNC_ARGS) } accel_restart_enter(); + zend_map_ptr_reset(); zend_reset_cache_vars(); zend_accel_hash_clean(&ZCSG(hash)); @@ -2561,7 +2567,6 @@ static int zend_accel_init_shm(void) ZCSG(last_restart_time) = 0; ZCSG(restart_in_progress) = 0; - for (i = 0; i < -HT_MIN_MASK; i++) { ZCSG(uninitialized_bucket)[i] = HT_INVALID_IDX; } diff --git a/ext/opcache/ZendAccelerator.h b/ext/opcache/ZendAccelerator.h index f48c3779f28f8..7e06047200e7b 100644 --- a/ext/opcache/ZendAccelerator.h +++ b/ext/opcache/ZendAccelerator.h @@ -240,6 +240,7 @@ typedef struct _zend_accel_globals { void *mem; void *arena_mem; zend_persistent_script *current_persistent_script; + zend_bool is_immutable_class; /* cache to save hash lookup on the same INCLUDE opcode */ const zend_op *cache_opline; zend_persistent_script *cache_persistent_script; @@ -267,6 +268,8 @@ typedef struct _zend_accel_shared_globals { zend_ulong manual_restarts; /* number of restarts scheduled by opcache_reset() */ zend_accel_hash hash; /* hash table for cached scripts */ + size_t map_ptr_last; + /* Directives & Maintenance */ time_t start_time; time_t last_restart_time; diff --git a/ext/opcache/zend_accelerator_util_funcs.c b/ext/opcache/zend_accelerator_util_funcs.c index 4db2c34fbcd12..acec571f4df80 100644 --- a/ext/opcache/zend_accelerator_util_funcs.c +++ b/ext/opcache/zend_accelerator_util_funcs.c @@ -126,11 +126,14 @@ static void zend_hash_clone_constants(HashTable *ht, HashTable *source) end = p + ht->nNumUsed; for (; p != end; p++) { ZEND_ASSERT(Z_TYPE(p->val) != IS_UNDEF); - c = ARENA_REALLOC(Z_PTR(p->val)); - Z_PTR(p->val) = c; + 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); + if (IN_ARENA(c->ce)) { + c->ce = ARENA_REALLOC(c->ce); + } } } } @@ -162,16 +165,21 @@ static void zend_hash_clone_methods(HashTable *ht, HashTable *source, zend_class end = p + ht->nNumUsed; for (; p != end; p++) { ZEND_ASSERT(Z_TYPE(p->val) != IS_UNDEF); - new_entry = ARENA_REALLOC(Z_PTR(p->val)); - Z_PTR(p->val) = new_entry; + 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); + if (IN_ARENA(new_entry->scope)) { + new_entry->scope = ARENA_REALLOC(new_entry->scope); - /* update prototype */ - if (new_entry->prototype) { - new_entry->prototype = ARENA_REALLOC(new_entry->prototype); + /* update prototype */ + if (IN_ARENA(new_entry->prototype)) { + new_entry->prototype = ARENA_REALLOC(new_entry->prototype); + } } + 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); } } } @@ -203,11 +211,14 @@ static void zend_hash_clone_prop_info(HashTable *ht, HashTable *source, zend_cla end = p + ht->nNumUsed; for (; p != end; p++) { ZEND_ASSERT(Z_TYPE(p->val) != IS_UNDEF); - prop_info = ARENA_REALLOC(Z_PTR(p->val)); - Z_PTR(p->val) = prop_info; + 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 (IN_ARENA(prop_info->ce)) { + prop_info->ce = ARENA_REALLOC(prop_info->ce); + } } } } @@ -229,7 +240,7 @@ static void zend_class_copy_ctor(zend_class_entry **pce) *pce = ce = ARENA_REALLOC(old_ce); ce->refcount = 1; - if (ce->parent && (ce->ce_flags & ZEND_ACC_LINKED)) { + if ((ce->ce_flags & ZEND_ACC_LINKED) && IN_ARENA(ce->parent)) { ce->parent = ARENA_REALLOC(ce->parent); } @@ -271,7 +282,7 @@ static void zend_class_copy_ctor(zend_class_entry **pce) parent = parent->parent; } } - ce->static_members_table = ce->default_static_members_table; + ZEND_MAP_PTR_INIT(ce->static_members_table, &ce->default_static_members_table); /* properties_info */ zend_hash_clone_prop_info(&ce->properties_info, &old_ce->properties_info, old_ce); @@ -282,10 +293,23 @@ static void zend_class_copy_ctor(zend_class_entry **pce) if (ce->num_interfaces) { zend_class_name *interface_names; - ZEND_ASSERT(!(ce->ce_flags & ZEND_ACC_LINKED)); - interface_names = emalloc(sizeof(zend_class_name) * ce->num_interfaces); - memcpy(interface_names, ce->interface_names, sizeof(zend_class_name) * ce->num_interfaces); - ce->interface_names = interface_names; + if (!(ce->ce_flags & ZEND_ACC_LINKED)) { + interface_names = emalloc(sizeof(zend_class_name) * ce->num_interfaces); + memcpy(interface_names, ce->interface_names, sizeof(zend_class_name) * ce->num_interfaces); + ce->interface_names = interface_names; + } else { + 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); @@ -507,7 +531,9 @@ static void zend_accel_class_hash_copy_from_shm(HashTable *target, HashTable *so } } else { t = _zend_hash_append_ptr_ex(target, p->key, Z_PTR(p->val), 1); - zend_class_copy_ctor((zend_class_entry**)&Z_PTR_P(t)); + 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; @@ -668,6 +694,7 @@ zend_op_array* zend_accel_load_script(zend_persistent_script *persistent_script, op_array = (zend_op_array *) emalloc(sizeof(zend_op_array)); *op_array = persistent_script->script.main_op_array; + ZEND_MAP_PTR_INIT(op_array->static_variables_ptr, &op_array->static_variables); if (EXPECTED(from_shared_memory)) { zend_hash_init(&ZCG(bind_hash), 10, NULL, NULL, 0); @@ -712,6 +739,7 @@ zend_op_array* zend_accel_load_script(zend_persistent_script *persistent_script, zend_hash_destroy(&ZCG(bind_hash)); 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.function_table) > 0) { zend_accel_function_hash_copy(CG(function_table), &persistent_script->script.function_table); diff --git a/ext/opcache/zend_file_cache.c b/ext/opcache/zend_file_cache.c index f6ed2fc91877e..fc477e662c1ae 100644 --- a/ext/opcache/zend_file_cache.c +++ b/ext/opcache/zend_file_cache.c @@ -534,6 +534,13 @@ static void zend_file_cache_serialize_op_array(zend_op_array *op_arra SERIALIZE_STR(op_array->doc_comment); SERIALIZE_PTR(op_array->try_catch_array); SERIALIZE_PTR(op_array->prototype); + + 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)); + } } } @@ -749,6 +756,18 @@ static void zend_file_cache_serialize_class(zval *zv, SERIALIZE_PTR(ce->__tostring); SERIALIZE_PTR(ce->__callstatic); SERIALIZE_PTR(ce->__debugInfo); + + if (ce->iterator_funcs_ptr) { + SERIALIZE_PTR(ce->iterator_funcs_ptr->zf_new_iterator); + SERIALIZE_PTR(ce->iterator_funcs_ptr->zf_rewind); + SERIALIZE_PTR(ce->iterator_funcs_ptr->zf_valid); + SERIALIZE_PTR(ce->iterator_funcs_ptr->zf_key); + SERIALIZE_PTR(ce->iterator_funcs_ptr->zf_current); + SERIALIZE_PTR(ce->iterator_funcs_ptr->zf_next); + SERIALIZE_PTR(ce->iterator_funcs_ptr); + } + + ZEND_MAP_PTR_INIT(ce->static_members_table, &ce->default_static_members_table); } static void zend_file_cache_serialize(zend_persistent_script *script, @@ -1183,6 +1202,18 @@ static void zend_file_cache_unserialize_op_array(zend_op_array *op_arr UNSERIALIZE_STR(op_array->doc_comment); UNSERIALIZE_PTR(op_array->try_catch_array); UNSERIALIZE_PTR(op_array->prototype); + + 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); + UNSERIALIZE_PTR(ZEND_MAP_PTR(op_array->run_time_cache)); + } } } @@ -1384,6 +1415,22 @@ static void zend_file_cache_unserialize_class(zval *zv, ce->serialize = zend_class_serialize_deny; ce->unserialize = zend_class_unserialize_deny; } + + if (ce->iterator_funcs_ptr) { + UNSERIALIZE_PTR(ce->iterator_funcs_ptr); + UNSERIALIZE_PTR(ce->iterator_funcs_ptr->zf_new_iterator); + UNSERIALIZE_PTR(ce->iterator_funcs_ptr->zf_rewind); + UNSERIALIZE_PTR(ce->iterator_funcs_ptr->zf_valid); + UNSERIALIZE_PTR(ce->iterator_funcs_ptr->zf_key); + UNSERIALIZE_PTR(ce->iterator_funcs_ptr->zf_current); + 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); + } else { + ZEND_MAP_PTR_INIT(ce->static_members_table, &ce->default_static_members_table); + } } static void zend_file_cache_unserialize(zend_persistent_script *script, @@ -1546,6 +1593,7 @@ zend_persistent_script *zend_file_cache_script_load(zend_file_handle *file_handl goto use_process_mem; } memcpy(buf, mem, info.mem_size); + zend_map_ptr_extend(ZCSG(map_ptr_last)); } else { use_process_mem: buf = mem; @@ -1576,6 +1624,7 @@ zend_persistent_script *zend_file_cache_script_load(zend_file_handle *file_handl script->corrupted = 0; if (cache_it) { + ZCSG(map_ptr_last) = CG(map_ptr_last); script->dynamic_members.checksum = zend_accel_script_checksum(script); script->dynamic_members.last_used = ZCG(request_time); diff --git a/ext/opcache/zend_persist.c b/ext/opcache/zend_persist.c index 6b3413324c62f..4f59655ffde32 100644 --- a/ext/opcache/zend_persist.c +++ b/ext/opcache/zend_persist.c @@ -27,6 +27,7 @@ #include "zend_vm.h" #include "zend_constants.h" #include "zend_operators.h" +#include "zend_interfaces.h" #ifdef HAVE_OPCACHE_FILE_CACHE #define zend_set_str_gc_flags(str) do { \ @@ -391,9 +392,14 @@ static void zend_persist_op_array_ex(zend_op_array *op_array, zend_persistent_sc GC_TYPE_INFO(op_array->static_variables) = IS_ARRAY | (IS_ARRAY_IMMUTABLE << GC_FLAGS_SHIFT); } } + ZEND_MAP_PTR_INIT(op_array->static_variables_ptr, &op_array->static_variables); if (op_array->scope) { - op_array->scope = zend_shared_alloc_get_xlat_entry(op_array->scope); + zend_class_entry *scope = zend_shared_alloc_get_xlat_entry(op_array->scope); + + if (scope) { + op_array->scope = scope; + } if (op_array->prototype) { zend_function *ptr = zend_shared_alloc_get_xlat_entry(op_array->prototype); @@ -557,7 +563,7 @@ static void zend_persist_op_array_ex(zend_op_array *op_array, zend_persistent_sc efree(op_array->opcodes); op_array->opcodes = new_opcodes; - op_array->run_time_cache = NULL; + ZEND_MAP_PTR_INIT(op_array->run_time_cache, NULL); } if (op_array->function_name && !IS_ACCEL_INTERNED(op_array->function_name)) { @@ -633,11 +639,19 @@ static void zend_persist_op_array(zval *zv) zend_op_array *op_array = Z_PTR_P(zv); ZEND_ASSERT(op_array->type == ZEND_USER_FUNCTION); - memcpy(ZCG(mem), Z_PTR_P(zv), sizeof(zend_op_array)); - Z_PTR_P(zv) = ZCG(mem); - ZCG(mem) = (void*)((char*)ZCG(mem) + ZEND_ALIGNED_SIZE(sizeof(zend_op_array))); - zend_persist_op_array_ex(Z_PTR_P(zv), NULL); - ((zend_op_array*)Z_PTR_P(zv))->fn_flags |= ZEND_ACC_IMMUTABLE; + op_array = Z_PTR_P(zv) = zend_shared_memdup(Z_PTR_P(zv), sizeof(zend_op_array)); + zend_persist_op_array_ex(op_array, NULL); + if (!ZCG(current_persistent_script)->corrupted) { + 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); + } } static void zend_persist_class_method(zval *zv) @@ -645,7 +659,35 @@ static void zend_persist_class_method(zval *zv) zend_op_array *op_array = Z_PTR_P(zv); zend_op_array *old_op_array; - ZEND_ASSERT(op_array->type == ZEND_USER_FUNCTION); + if (op_array->type != ZEND_USER_FUNCTION) { + ZEND_ASSERT(op_array->type == ZEND_INTERNAL_FUNCTION); + if (op_array->fn_flags & ZEND_ACC_ARENA_ALLOCATED) { + old_op_array = zend_shared_alloc_get_xlat_entry(op_array); + 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)); + } + if (op_array->scope) { + void *persist_ptr; + + if ((persist_ptr = zend_shared_alloc_get_xlat_entry(op_array->scope))) { + op_array->scope = (zend_class_entry*)persist_ptr; + } + if (op_array->prototype) { + if ((persist_ptr = zend_shared_alloc_get_xlat_entry(op_array->prototype))) { + op_array->prototype = (zend_function*)persist_ptr; + } + } + } + } + } + return; + } + old_op_array = zend_shared_alloc_get_xlat_entry(op_array); if (old_op_array) { Z_PTR_P(zv) = old_op_array; @@ -654,11 +696,23 @@ static void zend_persist_class_method(zval *zv) } return; } - memcpy(ZCG(arena_mem), Z_PTR_P(zv), sizeof(zend_op_array)); - zend_shared_alloc_register_xlat_entry(Z_PTR_P(zv), ZCG(arena_mem)); - Z_PTR_P(zv) = ZCG(arena_mem); - ZCG(arena_mem) = (void*)((char*)ZCG(arena_mem) + ZEND_ALIGNED_SIZE(sizeof(zend_op_array))); - zend_persist_op_array_ex(Z_PTR_P(zv), NULL); + 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)); + } + zend_persist_op_array_ex(op_array, NULL); + if (ZCG(is_immutable_class)) { + 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); + } } static void zend_persist_property_info(zval *zv) @@ -669,10 +723,11 @@ static void zend_persist_property_info(zval *zv) Z_PTR_P(zv) = prop; return; } - memcpy(ZCG(arena_mem), Z_PTR_P(zv), sizeof(zend_property_info)); - zend_shared_alloc_register_xlat_entry(Z_PTR_P(zv), ZCG(arena_mem)); - prop = Z_PTR_P(zv) = ZCG(arena_mem); - ZCG(arena_mem) = (void*)((char*)ZCG(arena_mem) + ZEND_ALIGNED_SIZE(sizeof(zend_property_info))); + if (ZCG(is_immutable_class)) { + prop = Z_PTR_P(zv) = zend_shared_memdup_put(Z_PTR_P(zv), sizeof(zend_property_info)); + } else { + prop = Z_PTR_P(zv) = zend_shared_memdup_arena_put(Z_PTR_P(zv), sizeof(zend_property_info)); + } prop->ce = zend_shared_alloc_get_xlat_entry(prop->ce); zend_accel_store_interned_string(prop->name); if (prop->doc_comment) { @@ -696,10 +751,11 @@ static void zend_persist_class_constant(zval *zv) Z_PTR_P(zv) = c; return; } - memcpy(ZCG(arena_mem), Z_PTR_P(zv), sizeof(zend_class_constant)); - zend_shared_alloc_register_xlat_entry(Z_PTR_P(zv), ZCG(arena_mem)); - c = Z_PTR_P(zv) = ZCG(arena_mem); - ZCG(arena_mem) = (void*)((char*)ZCG(arena_mem) + ZEND_ALIGNED_SIZE(sizeof(zend_class_constant))); + 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)); + } zend_persist_zval(&c->value); c->ce = zend_shared_alloc_get_xlat_entry(c->ce); if (c->doc_comment) { @@ -726,10 +782,16 @@ static void zend_persist_class_entry(zval *zv) zend_class_entry *ce = Z_PTR_P(zv); if (ce->type == ZEND_USER_CLASS) { - memcpy(ZCG(arena_mem), Z_PTR_P(zv), sizeof(zend_class_entry)); - zend_shared_alloc_register_xlat_entry(Z_PTR_P(zv), ZCG(arena_mem)); - ce = Z_PTR_P(zv) = ZCG(arena_mem); - ZCG(arena_mem) = (void*)((char*)ZCG(arena_mem) + ZEND_ALIGNED_SIZE(sizeof(zend_class_entry))); + if ((ce->ce_flags & ZEND_ACC_LINKED) + && (ce->ce_flags & ZEND_ACC_CONSTANTS_UPDATED) + && !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->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)); + } 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); @@ -754,8 +816,14 @@ static void zend_persist_class_entry(zval *zv) for (; i < ce->default_static_members_count; i++) { zend_persist_zval(&ce->default_static_members_table[i]); } + if (ce->ce_flags & ZEND_ACC_IMMUTABLE) { + ZEND_MAP_PTR_NEW(ce->static_members_table); + } else { + ZEND_MAP_PTR_INIT(ce->static_members_table, &ce->default_static_members_table); + } + } else { + ZEND_MAP_PTR_INIT(ce->static_members_table, &ce->default_static_members_table); } - ce->static_members_table = NULL; zend_hash_persist(&ce->constants_table, zend_persist_class_constant); HT_FLAGS(&ce->constants_table) &= (HASH_FLAG_INITIALIZED | HASH_FLAG_STATIC_KEYS); @@ -778,10 +846,9 @@ static void zend_persist_class_entry(zval *zv) zend_hash_persist(&ce->properties_info, zend_persist_property_info); HT_FLAGS(&ce->properties_info) &= (HASH_FLAG_INITIALIZED | HASH_FLAG_STATIC_KEYS); - if (ce->num_interfaces) { + if (ce->num_interfaces && !(ce->ce_flags & ZEND_ACC_LINKED)) { uint32_t i = 0; - ZEND_ASSERT(!(ce->ce_flags & ZEND_ACC_LINKED)); for (i = 0; i < ce->num_interfaces; i++) { zend_accel_store_interned_string(ce->interface_names[i].name); zend_accel_store_interned_string(ce->interface_names[i].lc_name); @@ -838,6 +905,10 @@ 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)); + } } } @@ -853,8 +924,59 @@ static int zend_update_parent_ce(zval *zv) { zend_class_entry *ce = Z_PTR_P(zv); - if (ce->parent && (ce->ce_flags & ZEND_ACC_LINKED)) { - ce->parent = zend_shared_alloc_get_xlat_entry(ce->parent); + if (ce->ce_flags & ZEND_ACC_LINKED) { + if (ce->parent) { + int i, end; + zend_class_entry *parent = ce->parent; + + if (parent->type == ZEND_USER_CLASS) { + zend_class_entry *p = zend_shared_alloc_get_xlat_entry(parent); + + if (p) { + ce->parent = parent = p; + } + } + + /* Create indirections to static properties from parent classes */ + i = parent->default_static_members_count - 1; + while (parent && parent->default_static_members_table) { + end = parent->parent ? parent->parent->default_static_members_count : 0; + for (; i >= end; i--) { + zval *p = &ce->default_static_members_table[i]; + ZVAL_INDIRECT(p, &parent->default_static_members_table[i]); + } + + parent = parent->parent; + } + } + + if (ce->num_interfaces) { + uint32_t i = 0; + + ce->interfaces = zend_shared_memdup_free(ce->interfaces, sizeof(zend_class_entry*) * ce->num_interfaces); + for (i = 0; i < ce->num_interfaces; i++) { + if (ce->interfaces[i]->type == ZEND_USER_CLASS) { + zend_class_entry *tmp = zend_shared_alloc_get_xlat_entry(ce->interfaces[i]); + if (tmp != NULL) { + ce->interfaces[i] = tmp; + } + } + } + } + + if (ce->iterator_funcs_ptr) { + memset(ce->iterator_funcs_ptr, 0, sizeof(zend_class_iterator_funcs)); + if (instanceof_function_ex(ce, zend_ce_aggregate, 1)) { + ce->iterator_funcs_ptr->zf_new_iterator = zend_hash_str_find_ptr(&ce->function_table, "getiterator", sizeof("getiterator") - 1); + } + if (instanceof_function_ex(ce, zend_ce_iterator, 1)) { + ce->iterator_funcs_ptr->zf_rewind = zend_hash_str_find_ptr(&ce->function_table, "rewind", sizeof("rewind") - 1); + ce->iterator_funcs_ptr->zf_valid = zend_hash_str_find_ptr(&ce->function_table, "valid", sizeof("valid") - 1); + ce->iterator_funcs_ptr->zf_key = zend_hash_str_find_ptr(&ce->function_table, "key", sizeof("key") - 1); + ce->iterator_funcs_ptr->zf_current = zend_hash_str_find_ptr(&ce->function_table, "current", sizeof("current") - 1); + ce->iterator_funcs_ptr->zf_next = zend_hash_str_find_ptr(&ce->function_table, "next", sizeof("next") - 1); + } + } } /* update methods */ @@ -912,7 +1034,6 @@ zend_persistent_script *zend_accel_script_persist(zend_persistent_script *script script->mem = ZCG(mem); ZEND_ASSERT(((zend_uintptr_t)ZCG(mem) & 0x7) == 0); /* should be 8 byte aligned */ - zend_shared_alloc_clear_xlat_table(); script = zend_shared_memdup_free(script, sizeof(zend_persistent_script)); if (key && *key) { @@ -939,10 +1060,14 @@ zend_persistent_script *zend_accel_script_persist(zend_persistent_script *script script->arena_mem = ZCG(arena_mem) = ZCG(mem); ZCG(mem) = (void*)((char*)ZCG(mem) + script->arena_size); + zend_map_ptr_extend(ZCSG(map_ptr_last)); + zend_accel_persist_class_table(&script->script.class_table); zend_hash_persist(&script->script.function_table, zend_persist_op_array); zend_persist_op_array_ex(&script->script.main_op_array, script); + ZCSG(map_ptr_last) = CG(map_ptr_last); + script->corrupted = 0; ZCG(current_persistent_script) = NULL; diff --git a/ext/opcache/zend_persist_calc.c b/ext/opcache/zend_persist_calc.c index fcd5db58a7e9d..86949ddca85b1 100644 --- a/ext/opcache/zend_persist_calc.c +++ b/ext/opcache/zend_persist_calc.c @@ -28,8 +28,15 @@ #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_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))) @@ -258,6 +265,9 @@ 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) @@ -265,12 +275,26 @@ static void zend_persist_class_method_calc(zval *zv) zend_op_array *op_array = Z_PTR_P(zv); zend_op_array *old_op_array; - ZEND_ASSERT(op_array->type == ZEND_USER_FUNCTION); + if (op_array->type != ZEND_USER_FUNCTION) { + ZEND_ASSERT(op_array->type == ZEND_INTERNAL_FUNCTION); + 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)); + zend_shared_alloc_register_xlat_entry(op_array, Z_PTR_P(zv)); + } + } + return; + } + old_op_array = zend_shared_alloc_get_xlat_entry(op_array); if (!old_op_array) { - ADD_ARENA_SIZE(sizeof(zend_op_array)); + ADD_SIZE_EX(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*)); + } } } @@ -280,7 +304,7 @@ static void zend_persist_property_info_calc(zval *zv) if (!zend_shared_alloc_get_xlat_entry(prop)) { zend_shared_alloc_register_xlat_entry(prop, prop); - ADD_ARENA_SIZE(sizeof(zend_property_info)); + ADD_SIZE_EX(sizeof(zend_property_info)); ADD_INTERNED_STRING(prop->name, 0); if (ZCG(accel_directives).save_comments && prop->doc_comment) { ADD_STRING(prop->doc_comment); @@ -294,7 +318,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_ARENA_SIZE(sizeof(zend_class_constant)); + ADD_SIZE_EX(sizeof(zend_class_constant)); zend_persist_zval_calc(&c->value); if (ZCG(accel_directives).save_comments && c->doc_comment) { ADD_STRING(c->doc_comment); @@ -302,13 +326,17 @@ static void zend_persist_class_constant_calc(zval *zv) } } - static void zend_persist_class_entry_calc(zval *zv) { zend_class_entry *ce = Z_PTR_P(zv); if (ce->type == ZEND_USER_CLASS) { - ADD_ARENA_SIZE(sizeof(zend_class_entry)); + ZCG(is_immutable_class) = + (ce->ce_flags & ZEND_ACC_LINKED) && + (ce->ce_flags & ZEND_ACC_CONSTANTS_UPDATED) && + !ZCG(current_persistent_script)->corrupted; + + ADD_SIZE_EX(sizeof(zend_class_entry)); ADD_INTERNED_STRING(ce->name, 0); if (ce->parent_name && !(ce->ce_flags & ZEND_ACC_LINKED)) { ADD_INTERNED_STRING(ce->parent_name, 0); @@ -346,12 +374,15 @@ static void zend_persist_class_entry_calc(zval *zv) if (ce->num_interfaces) { uint32_t i; - ZEND_ASSERT(!(ce->ce_flags & ZEND_ACC_LINKED)); - for (i = 0; i < ce->num_interfaces; i++) { - ADD_INTERNED_STRING(ce->interface_names[i].name, 0); - ADD_INTERNED_STRING(ce->interface_names[i].lc_name, 0); + if (!(ce->ce_flags & ZEND_ACC_LINKED)) { + for (i = 0; i < ce->num_interfaces; i++) { + ADD_INTERNED_STRING(ce->interface_names[i].name, 0); + ADD_INTERNED_STRING(ce->interface_names[i].lc_name, 0); + } + ADD_SIZE(sizeof(zend_class_name) * ce->num_interfaces); + } else { + ADD_SIZE(sizeof(zend_class_entry*) * ce->num_interfaces); } - ADD_SIZE(sizeof(zend_class_name) * ce->num_interfaces); } if (ce->num_traits) { @@ -399,6 +430,10 @@ 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)); + } } } diff --git a/ext/opcache/zend_shared_alloc.c b/ext/opcache/zend_shared_alloc.c index 8d4d792bb326e..fe8d470d58589 100644 --- a/ext/opcache/zend_shared_alloc.c +++ b/ext/opcache/zend_shared_alloc.c @@ -347,7 +347,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, zend_bool get_xlat, zend_bool set_xlat, zend_bool free_source) +static zend_always_inline void *_zend_shared_memdup(void *source, size_t size, zend_bool arena, zend_bool get_xlat, zend_bool set_xlat, zend_bool free_source) { void *old_p, *retval; zend_ulong key; @@ -360,8 +360,13 @@ static zend_always_inline void *_zend_shared_memdup(void *source, size_t size, z return old_p; } } - retval = ZCG(mem); - ZCG(mem) = (void*)(((char*)ZCG(mem)) + ZEND_ALIGNED_SIZE(size)); + 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)); + } memcpy(retval, source, size); if (set_xlat) { if (!get_xlat) { @@ -378,32 +383,42 @@ static zend_always_inline void *_zend_shared_memdup(void *source, size_t size, z void *zend_shared_memdup_get_put_free(void *source, size_t size) { - return _zend_shared_memdup(source, size, 1, 1, 1); + return _zend_shared_memdup(source, size, 0, 1, 1, 1); } void *zend_shared_memdup_put_free(void *source, size_t size) { - return _zend_shared_memdup(source, size, 0, 1, 1); + return _zend_shared_memdup(source, size, 0, 0, 1, 1); } void *zend_shared_memdup_free(void *source, size_t size) { - return _zend_shared_memdup(source, size, 0, 0, 1); + return _zend_shared_memdup(source, size, 0, 0, 0, 1); } void *zend_shared_memdup_get_put(void *source, size_t size) { - return _zend_shared_memdup(source, size, 1, 1, 0); + return _zend_shared_memdup(source, size, 0, 1, 1, 0); } void *zend_shared_memdup_put(void *source, size_t size) { - return _zend_shared_memdup(source, size, 0, 1, 0); + return _zend_shared_memdup(source, size, 0, 0, 1, 0); } void *zend_shared_memdup(void *source, size_t size) { - return _zend_shared_memdup(source, size, 0, 0, 0); + 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); } void zend_shared_alloc_safe_unlock(void) @@ -483,6 +498,16 @@ void zend_shared_alloc_clear_xlat_table(void) zend_hash_clean(&ZCG(xlat_table)); } +uint32_t zend_shared_alloc_checkpoint_xlat_table(void) +{ + return ZCG(xlat_table).nNumUsed; +} + +void zend_shared_alloc_restore_xlat_table(uint32_t checkpoint) +{ + zend_hash_discard(&ZCG(xlat_table), checkpoint); +} + void zend_shared_alloc_register_xlat_entry(const void *old, const void *new) { zend_ulong key = (zend_ulong)old; diff --git a/ext/opcache/zend_shared_alloc.h b/ext/opcache/zend_shared_alloc.h index fd71529b76c39..f784c5e48df75 100644 --- a/ext/opcache/zend_shared_alloc.h +++ b/ext/opcache/zend_shared_alloc.h @@ -131,6 +131,8 @@ 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); @@ -160,6 +162,8 @@ void zend_shared_alloc_safe_unlock(void); void zend_shared_alloc_init_xlat_table(void); void zend_shared_alloc_destroy_xlat_table(void); void zend_shared_alloc_clear_xlat_table(void); +uint32_t zend_shared_alloc_checkpoint_xlat_table(void); +void zend_shared_alloc_restore_xlat_table(uint32_t checkpoint); void zend_shared_alloc_register_xlat_entry(const void *old, const void *new); void *zend_shared_alloc_get_xlat_entry(const void *old); diff --git a/ext/reflection/php_reflection.c b/ext/reflection/php_reflection.c index 59ff6526a75b1..382e565cd8f0b 100644 --- a/ext/reflection/php_reflection.c +++ b/ext/reflection/php_reflection.c @@ -685,7 +685,7 @@ static void _function_closure_string(smart_str *str, zend_function *fptr, char* return; } - static_variables = fptr->op_array.static_variables; + static_variables = ZEND_MAP_PTR_GET(fptr->op_array.static_variables_ptr); count = zend_hash_num_elements(static_variables); if (!count) { @@ -1790,19 +1790,21 @@ ZEND_METHOD(reflection_function, getStaticVariables) /* Return an empty array in case no static variables exist */ if (fptr->type == ZEND_USER_FUNCTION && fptr->op_array.static_variables != NULL) { + HashTable *ht; + array_init(return_value); - if (GC_REFCOUNT(fptr->op_array.static_variables) > 1) { - if (!(GC_FLAGS(fptr->op_array.static_variables) & IS_ARRAY_IMMUTABLE)) { - GC_DELREF(fptr->op_array.static_variables); - } - fptr->op_array.static_variables = zend_array_dup(fptr->op_array.static_variables); + ht = ZEND_MAP_PTR_GET(fptr->op_array.static_variables_ptr); + if (!ht) { + ZEND_ASSERT(fptr->op_array.fn_flags & ZEND_ACC_IMMUTABLE); + ht = zend_array_dup(fptr->op_array.static_variables); + ZEND_MAP_PTR_SET(fptr->op_array.static_variables_ptr, ht); } - ZEND_HASH_FOREACH_VAL(fptr->op_array.static_variables, val) { + ZEND_HASH_FOREACH_VAL(ht, val) { if (UNEXPECTED(zval_update_constant_ex(val, fptr->common.scope) != SUCCESS)) { return; } } ZEND_HASH_FOREACH_END(); - zend_hash_copy(Z_ARRVAL_P(return_value), fptr->op_array.static_variables, zval_add_ref); + zend_hash_copy(Z_ARRVAL_P(return_value), ht, zval_add_ref); } else { ZVAL_EMPTY_ARRAY(return_value); }