diff --git a/Zend/Optimizer/compact_literals.c b/Zend/Optimizer/compact_literals.c index 0e1529d2bd195..55cc40afb9b52 100644 --- a/Zend/Optimizer/compact_literals.c +++ b/Zend/Optimizer/compact_literals.c @@ -241,9 +241,6 @@ void zend_optimizer_compact_literals(zend_op_array *op_array, zend_optimizer_ctx case ZEND_RECV_INIT: LITERAL_INFO(opline->op2.constant, LITERAL_VALUE, 1); break; - case ZEND_DECLARE_FUNCTION: - LITERAL_INFO(opline->op1.constant, LITERAL_VALUE, 2); - break; case ZEND_DECLARE_CLASS: case ZEND_DECLARE_CLASS_DELAYED: LITERAL_INFO(opline->op1.constant, LITERAL_VALUE, 2); @@ -776,7 +773,6 @@ void zend_optimizer_compact_literals(zend_op_array *op_array, zend_optimizer_ctx bind_var_slot[opline->op2.constant] = opline->extended_value; } break; - case ZEND_DECLARE_LAMBDA_FUNCTION: case ZEND_DECLARE_ANON_CLASS: case ZEND_DECLARE_CLASS_DELAYED: opline->extended_value = cache_size; diff --git a/Zend/Optimizer/zend_optimizer.c b/Zend/Optimizer/zend_optimizer.c index ae548fc5200bd..cc9971a9f87bc 100644 --- a/Zend/Optimizer/zend_optimizer.c +++ b/Zend/Optimizer/zend_optimizer.c @@ -1367,16 +1367,24 @@ static bool needs_live_range(zend_op_array *op_array, zend_op *def_opline) { return (type & (MAY_BE_STRING|MAY_BE_ARRAY|MAY_BE_OBJECT|MAY_BE_RESOURCE|MAY_BE_REF)) != 0; } +static void zend_foreach_op_array_helper( + zend_op_array *op_array, zend_op_array_func_t func, void *context) { + func(op_array, context); + for (uint32_t i = 0; i < op_array->num_dynamic_func_defs; i++) { + func(op_array->dynamic_func_defs[i], context); + } +} + void zend_foreach_op_array(zend_script *script, zend_op_array_func_t func, void *context) { zend_class_entry *ce; zend_string *key; zend_op_array *op_array; - func(&script->main_op_array, context); + zend_foreach_op_array_helper(&script->main_op_array, func, context); ZEND_HASH_FOREACH_PTR(&script->function_table, op_array) { - func(op_array, context); + zend_foreach_op_array_helper(op_array, func, context); } ZEND_HASH_FOREACH_END(); ZEND_HASH_FOREACH_STR_KEY_PTR(&script->class_table, key, ce) { @@ -1387,7 +1395,7 @@ void zend_foreach_op_array(zend_script *script, zend_op_array_func_t func, void if (op_array->scope == ce && op_array->type == ZEND_USER_FUNCTION && !(op_array->fn_flags & ZEND_ACC_TRAIT_CLONE)) { - func(op_array, context); + zend_foreach_op_array_helper(op_array, func, context); } } ZEND_HASH_FOREACH_END(); } ZEND_HASH_FOREACH_END(); diff --git a/Zend/tests/static_variable_in_dynamic_function.phpt b/Zend/tests/static_variable_in_dynamic_function.phpt new file mode 100644 index 0000000000000..77b71a48831f0 --- /dev/null +++ b/Zend/tests/static_variable_in_dynamic_function.phpt @@ -0,0 +1,21 @@ +--TEST-- +Static variables in dynamically declared function (first use before dynamic def dtor) +--FILE-- + +--EXPECT-- +int(1) +int(2) diff --git a/Zend/tests/static_variable_in_dynamic_function_2.phpt b/Zend/tests/static_variable_in_dynamic_function_2.phpt new file mode 100644 index 0000000000000..f7c160325f5af --- /dev/null +++ b/Zend/tests/static_variable_in_dynamic_function_2.phpt @@ -0,0 +1,21 @@ +--TEST-- +Static variables in dynamically declared function (first use after dynamic def dtor) +--FILE-- + +--EXPECT-- +int(1) +int(2) diff --git a/Zend/zend.c b/Zend/zend.c index ce6287e2d1f6f..9e5c7e2328af6 100644 --- a/Zend/zend.c +++ b/Zend/zend.c @@ -1696,6 +1696,7 @@ ZEND_API zend_result zend_execute_scripts(int type, zval *retval, int file_count ret = zend_exception_error(EG(exception), E_ERROR); } } + zend_destroy_static_vars(op_array); destroy_op_array(op_array); efree_size(op_array, sizeof(zend_op_array)); } else if (type==ZEND_REQUIRE) { diff --git a/Zend/zend_closures.c b/Zend/zend_closures.c index 8e5b599e39a74..5e05da46c796c 100644 --- a/Zend/zend_closures.c +++ b/Zend/zend_closures.c @@ -486,10 +486,9 @@ static void zend_closure_free_storage(zend_object *object) /* {{{ */ zend_object_std_dtor(&closure->std); if (closure->func.type == ZEND_USER_FUNCTION) { - /* We shared static_variables with the original function. - * Unshare now so we don't try to destroy them. */ - if (closure->func.op_array.fn_flags & ZEND_ACC_FAKE_CLOSURE) { - ZEND_MAP_PTR_INIT(closure->func.op_array.static_variables_ptr, NULL); + /* We don't own the static variables of fake closures. */ + if (!(closure->func.op_array.fn_flags & ZEND_ACC_FAKE_CLOSURE)) { + zend_destroy_static_vars(&closure->func.op_array); } destroy_op_array(&closure->func.op_array); } diff --git a/Zend/zend_compile.c b/Zend/zend_compile.c index 2b2dc9b249061..deff0dbe57368 100644 --- a/Zend/zend_compile.c +++ b/Zend/zend_compile.c @@ -1091,27 +1091,19 @@ static zend_never_inline ZEND_COLD ZEND_NORETURN void do_bind_function_error(zen } } -ZEND_API zend_result do_bind_function(zval *lcname) /* {{{ */ +ZEND_API zend_result do_bind_function(zend_function *func, zval *lcname) /* {{{ */ { - zend_function *function; - zval *rtd_key, *zv; - - rtd_key = lcname + 1; - zv = zend_hash_find_ex(EG(function_table), Z_STR_P(rtd_key), 1); - if (UNEXPECTED(!zv)) { - do_bind_function_error(Z_STR_P(lcname), NULL, 0); + zend_function *added_func = zend_hash_add_ptr(EG(function_table), Z_STR_P(lcname), func); + if (UNEXPECTED(!added_func)) { + do_bind_function_error(Z_STR_P(lcname), &func->op_array, 0); return FAILURE; } - function = (zend_function*)Z_PTR_P(zv); - if (UNEXPECTED(function->common.fn_flags & ZEND_ACC_PRELOADED) - && !(CG(compiler_options) & ZEND_COMPILE_PRELOAD)) { - zv = zend_hash_add(EG(function_table), Z_STR_P(lcname), zv); - } else { - zv = zend_hash_set_bucket_key(EG(function_table), (Bucket*)zv, Z_STR_P(lcname)); + + if (func->op_array.refcount) { + ++*func->op_array.refcount; } - if (UNEXPECTED(!zv)) { - do_bind_function_error(Z_STR_P(lcname), &function->op_array, 0); - return FAILURE; + if (func->common.function_name) { + zend_string_addref(func->common.function_name); } return SUCCESS; } @@ -6954,9 +6946,18 @@ zend_string *zend_begin_method_decl(zend_op_array *op_array, zend_string *name, } /* }}} */ +static uint32_t zend_add_dynamic_func_def(zend_op_array *def) { + zend_op_array *op_array = CG(active_op_array); + uint32_t def_offset = op_array->num_dynamic_func_defs++; + op_array->dynamic_func_defs = erealloc( + op_array->dynamic_func_defs, op_array->num_dynamic_func_defs * sizeof(zend_op_array *)); + op_array->dynamic_func_defs[def_offset] = def; + return def_offset; +} + static void zend_begin_func_decl(znode *result, zend_op_array *op_array, zend_ast_decl *decl, bool toplevel) /* {{{ */ { - zend_string *unqualified_name, *name, *lcname, *key; + zend_string *unqualified_name, *name, *lcname; zend_op *opline; unqualified_name = decl->name; @@ -6992,25 +6993,16 @@ static void zend_begin_func_decl(znode *result, zend_op_array *op_array, zend_as return; } - /* Generate RTD keys until we find one that isn't in use yet. */ - key = NULL; - do { - zend_tmp_string_release(key); - key = zend_build_runtime_definition_key(lcname, decl->start_lineno); - } while (!zend_hash_add_ptr(CG(function_table), key, op_array)); - + uint32_t func_ref = zend_add_dynamic_func_def(op_array); if (op_array->fn_flags & ZEND_ACC_CLOSURE) { opline = zend_emit_op_tmp(result, ZEND_DECLARE_LAMBDA_FUNCTION, NULL, NULL); - opline->extended_value = zend_alloc_cache_slot(); - opline->op1_type = IS_CONST; - LITERAL_STR(opline->op1, key); + opline->op2.num = func_ref; } else { opline = get_next_op(); opline->opcode = ZEND_DECLARE_FUNCTION; opline->op1_type = IS_CONST; LITERAL_STR(opline->op1, zend_string_copy(lcname)); - /* RTD key is placed after lcname literal in op1 */ - zend_add_literal_string(&key); + opline->op2.num = func_ref; } zend_string_release_ex(lcname, 0); } diff --git a/Zend/zend_compile.h b/Zend/zend_compile.h index ce4b50aaee3c2..6f64624fd87a2 100644 --- a/Zend/zend_compile.h +++ b/Zend/zend_compile.h @@ -456,8 +456,13 @@ struct _zend_op_array { zend_string *doc_comment; int last_literal; + uint32_t num_dynamic_func_defs; zval *literals; + /* Functions that are declared dynamically are stored here and + * referenced by index from opcodes. */ + zend_op_array **dynamic_func_defs; + void *reserved[ZEND_MAX_RESERVED_RESOURCES]; }; @@ -781,7 +786,7 @@ bool zend_handle_encoding_declaration(zend_ast *ast); /* parser-driven code generators */ void zend_do_free(znode *op1); -ZEND_API zend_result do_bind_function(zval *lcname); +ZEND_API zend_result do_bind_function(zend_function *func, zval *lcname); ZEND_API zend_result do_bind_class(zval *lcname, zend_string *lc_parent_name); ZEND_API uint32_t zend_build_delayed_early_binding_list(const zend_op_array *op_array); ZEND_API void zend_do_delayed_early_binding(zend_op_array *op_array, uint32_t first_early_binding_opline); @@ -812,6 +817,7 @@ ZEND_API int zend_execute_scripts(int type, zval *retval, int file_count, ...); ZEND_API int open_file_for_scanning(zend_file_handle *file_handle); ZEND_API void init_op_array(zend_op_array *op_array, zend_uchar type, int initial_ops_size); ZEND_API void destroy_op_array(zend_op_array *op_array); +ZEND_API void zend_destroy_static_vars(zend_op_array *op_array); ZEND_API void zend_destroy_file_handle(zend_file_handle *file_handle); ZEND_API void zend_cleanup_mutable_class_data(zend_class_entry *ce); ZEND_API void zend_cleanup_internal_class_data(zend_class_entry *ce); diff --git a/Zend/zend_execute_API.c b/Zend/zend_execute_API.c index 1d484934e3e09..4e8c1d5a1f379 100644 --- a/Zend/zend_execute_API.c +++ b/Zend/zend_execute_API.c @@ -1224,6 +1224,7 @@ ZEND_API zend_result zend_eval_stringl(const char *str, size_t str_len, zval *re } EG(no_extensions)=0; + zend_destroy_static_vars(new_op_array); destroy_op_array(new_op_array); efree_size(new_op_array, sizeof(zend_op_array)); retval = SUCCESS; diff --git a/Zend/zend_opcode.c b/Zend/zend_opcode.c index ac9cc5f7041bc..4e30b9ff7ad9b 100644 --- a/Zend/zend_opcode.c +++ b/Zend/zend_opcode.c @@ -85,6 +85,9 @@ 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->num_dynamic_func_defs = 0; + op_array->dynamic_func_defs = NULL; + ZEND_MAP_PTR_INIT(op_array->run_time_cache, NULL); op_array->cache_size = zend_op_array_extension_handles * sizeof(void*); @@ -511,16 +514,20 @@ void zend_class_add_ref(zval *zv) } } -ZEND_API void destroy_op_array(zend_op_array *op_array) +ZEND_API void zend_destroy_static_vars(zend_op_array *op_array) { - uint32_t i; - if (ZEND_MAP_PTR(op_array->static_variables_ptr)) { HashTable *ht = ZEND_MAP_PTR_GET(op_array->static_variables_ptr); if (ht) { zend_array_destroy(ht); + ZEND_MAP_PTR_SET(op_array->static_variables_ptr, NULL); } } +} + +ZEND_API void destroy_op_array(zend_op_array *op_array) +{ + uint32_t i; if ((op_array->fn_flags & ZEND_ACC_HEAP_RT_CACHE) && ZEND_MAP_PTR(op_array->run_time_cache)) { @@ -600,6 +607,19 @@ ZEND_API void destroy_op_array(zend_op_array *op_array) if (op_array->static_variables) { zend_array_destroy(op_array->static_variables); } + if (op_array->num_dynamic_func_defs) { + for (i = 0; i < op_array->num_dynamic_func_defs; i++) { + /* Closures overwrite static_variables in their copy. + * Make sure to destroy them when the prototype function is destroyed. */ + if (op_array->dynamic_func_defs[i]->static_variables + && (op_array->dynamic_func_defs[i]->fn_flags & ZEND_ACC_CLOSURE)) { + zend_array_destroy(op_array->dynamic_func_defs[i]->static_variables); + op_array->dynamic_func_defs[i]->static_variables = NULL; + } + destroy_op_array(op_array->dynamic_func_defs[i]); + } + efree(op_array->dynamic_func_defs); + } } static void zend_update_extended_stmts(zend_op_array *op_array) diff --git a/Zend/zend_vm_def.h b/Zend/zend_vm_def.h index b78f8ad61e54e..8b4a86b7452f0 100644 --- a/Zend/zend_vm_def.h +++ b/Zend/zend_vm_def.h @@ -2824,6 +2824,7 @@ ZEND_VM_HOT_HELPER(zend_leave_helper, ANY, ANY) ZEND_VM_LEAVE(); } else if (EXPECTED((call_info & ZEND_CALL_TOP) == 0)) { zend_detach_symbol_table(execute_data); + zend_destroy_static_vars(&EX(func)->op_array); destroy_op_array(&EX(func)->op_array); efree_size(EX(func), sizeof(zend_op_array)); #ifdef ZEND_PREFER_RELOAD @@ -6231,6 +6232,7 @@ ZEND_VM_HANDLER(73, ZEND_INCLUDE_OR_EVAL, CONST|TMPVAR|CV, ANY, EVAL, SPEC(OBSER zend_vm_stack_free_call_frame(call); } + zend_destroy_static_vars(new_op_array); destroy_op_array(new_op_array); efree_size(new_op_array, sizeof(zend_op_array)); if (UNEXPECTED(EG(exception) != NULL)) { @@ -7583,12 +7585,14 @@ ZEND_VM_HANDLER(146, ZEND_DECLARE_ANON_CLASS, ANY, ANY, CACHE_SLOT) ZEND_VM_NEXT_OPCODE(); } -ZEND_VM_HANDLER(141, ZEND_DECLARE_FUNCTION, ANY, ANY) +ZEND_VM_HANDLER(141, ZEND_DECLARE_FUNCTION, ANY, NUM) { + zend_function *func; USE_OPLINE SAVE_OPLINE(); - do_bind_function(RT_CONSTANT(opline, opline->op1)); + func = (zend_function *) EX(func)->op_array.dynamic_func_defs[opline->op2.num]; + do_bind_function(func, RT_CONSTANT(opline, opline->op1)); ZEND_VM_NEXT_OPCODE_CHECK_EXCEPTION(); } @@ -7853,23 +7857,14 @@ ZEND_VM_HANDLER(143, ZEND_DECLARE_CONST, CONST, CONST) ZEND_VM_NEXT_OPCODE_CHECK_EXCEPTION(); } -ZEND_VM_HANDLER(142, ZEND_DECLARE_LAMBDA_FUNCTION, CONST, UNUSED, CACHE_SLOT) +ZEND_VM_HANDLER(142, ZEND_DECLARE_LAMBDA_FUNCTION, CONST, NUM) { USE_OPLINE zend_function *func; - zval *zfunc; zval *object; zend_class_entry *called_scope; - func = CACHED_PTR(opline->extended_value); - if (UNEXPECTED(func == NULL)) { - zfunc = zend_hash_find_ex(EG(function_table), Z_STR_P(RT_CONSTANT(opline, opline->op1)), 1); - ZEND_ASSERT(zfunc != NULL); - func = Z_FUNC_P(zfunc); - ZEND_ASSERT(func->type == ZEND_USER_FUNCTION); - CACHE_PTR(opline->extended_value, func); - } - + func = (zend_function *) EX(func)->op_array.dynamic_func_defs[opline->op2.num]; if (Z_TYPE(EX(This)) == IS_OBJECT) { called_scope = Z_OBJCE(EX(This)); if (UNEXPECTED((func->common.fn_flags & ZEND_ACC_STATIC) || diff --git a/Zend/zend_vm_execute.h b/Zend/zend_vm_execute.h index 419de3fa26463..26903f940334a 100644 --- a/Zend/zend_vm_execute.h +++ b/Zend/zend_vm_execute.h @@ -1146,6 +1146,7 @@ static zend_never_inline ZEND_OPCODE_HANDLER_RET ZEND_FASTCALL zend_leave_helper ZEND_VM_LEAVE(); } else if (EXPECTED((call_info & ZEND_CALL_TOP) == 0)) { zend_detach_symbol_table(execute_data); + zend_destroy_static_vars(&EX(func)->op_array); destroy_op_array(&EX(func)->op_array); efree_size(EX(func), sizeof(zend_op_array)); #ifdef ZEND_PREFER_RELOAD @@ -2828,10 +2829,12 @@ static ZEND_OPCODE_HANDLER_RET ZEND_FASTCALL ZEND_DECLARE_ANON_CLASS_SPEC_HANDLE static ZEND_OPCODE_HANDLER_RET ZEND_FASTCALL ZEND_DECLARE_FUNCTION_SPEC_HANDLER(ZEND_OPCODE_HANDLER_ARGS) { + zend_function *func; USE_OPLINE SAVE_OPLINE(); - do_bind_function(RT_CONSTANT(opline, opline->op1)); + func = (zend_function *) EX(func)->op_array.dynamic_func_defs[opline->op2.num]; + do_bind_function(func, RT_CONSTANT(opline, opline->op1)); ZEND_VM_NEXT_OPCODE_CHECK_EXCEPTION(); } @@ -4732,6 +4735,7 @@ static ZEND_OPCODE_HANDLER_RET ZEND_FASTCALL ZEND_INCLUDE_OR_EVAL_SPEC_CONST_HAN zend_vm_stack_free_call_frame(call); } + zend_destroy_static_vars(new_op_array); destroy_op_array(new_op_array); efree_size(new_op_array, sizeof(zend_op_array)); if (UNEXPECTED(EG(exception) != NULL)) { @@ -4801,6 +4805,7 @@ static ZEND_OPCODE_HANDLER_RET ZEND_FASTCALL ZEND_INCLUDE_OR_EVAL_SPEC_OBSERVER_ zend_vm_stack_free_call_frame(call); } + zend_destroy_static_vars(new_op_array); destroy_op_array(new_op_array); efree_size(new_op_array, sizeof(zend_op_array)); if (UNEXPECTED(EG(exception) != NULL)) { @@ -5152,6 +5157,32 @@ static ZEND_OPCODE_HANDLER_RET ZEND_FASTCALL ZEND_DECLARE_CLASS_SPEC_CONST_HANDL ZEND_VM_NEXT_OPCODE_CHECK_EXCEPTION(); } +static ZEND_OPCODE_HANDLER_RET ZEND_FASTCALL ZEND_DECLARE_LAMBDA_FUNCTION_SPEC_CONST_HANDLER(ZEND_OPCODE_HANDLER_ARGS) +{ + USE_OPLINE + zend_function *func; + zval *object; + zend_class_entry *called_scope; + + func = (zend_function *) EX(func)->op_array.dynamic_func_defs[opline->op2.num]; + if (Z_TYPE(EX(This)) == IS_OBJECT) { + called_scope = Z_OBJCE(EX(This)); + if (UNEXPECTED((func->common.fn_flags & ZEND_ACC_STATIC) || + (EX(func)->common.fn_flags & ZEND_ACC_STATIC))) { + object = NULL; + } else { + object = &EX(This); + } + } else { + called_scope = Z_CE(EX(This)); + object = NULL; + } + zend_create_closure(EX_VAR(opline->result.var), func, + EX(func)->op_array.scope, called_scope, object); + + ZEND_VM_NEXT_OPCODE(); +} + static ZEND_OPCODE_HANDLER_RET ZEND_FASTCALL ZEND_YIELD_FROM_SPEC_CONST_HANDLER(ZEND_OPCODE_HANDLER_ARGS) { USE_OPLINE @@ -10207,41 +10238,6 @@ static ZEND_OPCODE_HANDLER_RET ZEND_FASTCALL ZEND_ISSET_ISEMPTY_VAR_SPEC_CONST_U } /* No specialization for op_types (CONST|TMPVAR|CV, UNUSED|CLASS_FETCH|CONST|VAR) */ -static ZEND_OPCODE_HANDLER_RET ZEND_FASTCALL ZEND_DECLARE_LAMBDA_FUNCTION_SPEC_CONST_UNUSED_HANDLER(ZEND_OPCODE_HANDLER_ARGS) -{ - USE_OPLINE - zend_function *func; - zval *zfunc; - zval *object; - zend_class_entry *called_scope; - - func = CACHED_PTR(opline->extended_value); - if (UNEXPECTED(func == NULL)) { - zfunc = zend_hash_find_ex(EG(function_table), Z_STR_P(RT_CONSTANT(opline, opline->op1)), 1); - ZEND_ASSERT(zfunc != NULL); - func = Z_FUNC_P(zfunc); - ZEND_ASSERT(func->type == ZEND_USER_FUNCTION); - CACHE_PTR(opline->extended_value, func); - } - - if (Z_TYPE(EX(This)) == IS_OBJECT) { - called_scope = Z_OBJCE(EX(This)); - if (UNEXPECTED((func->common.fn_flags & ZEND_ACC_STATIC) || - (EX(func)->common.fn_flags & ZEND_ACC_STATIC))) { - object = NULL; - } else { - object = &EX(This); - } - } else { - called_scope = Z_CE(EX(This)); - object = NULL; - } - zend_create_closure(EX_VAR(opline->result.var), func, - EX(func)->op_array.scope, called_scope, object); - - ZEND_VM_NEXT_OPCODE(); -} - static ZEND_OPCODE_HANDLER_RET ZEND_FASTCALL ZEND_YIELD_SPEC_CONST_UNUSED_HANDLER(ZEND_OPCODE_HANDLER_ARGS) { USE_OPLINE @@ -14356,6 +14352,7 @@ static ZEND_OPCODE_HANDLER_RET ZEND_FASTCALL ZEND_INCLUDE_OR_EVAL_SPEC_TMPVAR_HA zend_vm_stack_free_call_frame(call); } + zend_destroy_static_vars(new_op_array); destroy_op_array(new_op_array); efree_size(new_op_array, sizeof(zend_op_array)); if (UNEXPECTED(EG(exception) != NULL)) { @@ -38239,6 +38236,7 @@ static ZEND_OPCODE_HANDLER_RET ZEND_FASTCALL ZEND_INCLUDE_OR_EVAL_SPEC_CV_HANDLE zend_vm_stack_free_call_frame(call); } + zend_destroy_static_vars(new_op_array); destroy_op_array(new_op_array); efree_size(new_op_array, sizeof(zend_op_array)); if (UNEXPECTED(EG(exception) != NULL)) { @@ -53314,7 +53312,7 @@ ZEND_API void execute_ex(zend_execute_data *ex) (void*)&&ZEND_NULL_LABEL, (void*)&&ZEND_MAKE_REF_SPEC_CV_UNUSED_LABEL, (void*)&&ZEND_DECLARE_FUNCTION_SPEC_LABEL, - (void*)&&ZEND_DECLARE_LAMBDA_FUNCTION_SPEC_CONST_UNUSED_LABEL, + (void*)&&ZEND_DECLARE_LAMBDA_FUNCTION_SPEC_CONST_LABEL, (void*)&&ZEND_DECLARE_CONST_SPEC_CONST_CONST_LABEL, (void*)&&ZEND_DECLARE_CLASS_SPEC_CONST_LABEL, (void*)&&ZEND_DECLARE_CLASS_DELAYED_SPEC_CONST_CONST_LABEL, @@ -54625,6 +54623,7 @@ ZEND_API void execute_ex(zend_execute_data *ex) ZEND_VM_LEAVE(); } else if (EXPECTED((call_info & ZEND_CALL_TOP) == 0)) { zend_detach_symbol_table(execute_data); + zend_destroy_static_vars(&EX(func)->op_array); destroy_op_array(&EX(func)->op_array); efree_size(EX(func), sizeof(zend_op_array)); #ifdef ZEND_PREFER_RELOAD @@ -55133,6 +55132,10 @@ ZEND_API void execute_ex(zend_execute_data *ex) VM_TRACE(ZEND_DECLARE_CLASS_SPEC_CONST) ZEND_DECLARE_CLASS_SPEC_CONST_HANDLER(ZEND_OPCODE_HANDLER_ARGS_PASSTHRU); HYBRID_BREAK(); + HYBRID_CASE(ZEND_DECLARE_LAMBDA_FUNCTION_SPEC_CONST): + VM_TRACE(ZEND_DECLARE_LAMBDA_FUNCTION_SPEC_CONST) + ZEND_DECLARE_LAMBDA_FUNCTION_SPEC_CONST_HANDLER(ZEND_OPCODE_HANDLER_ARGS_PASSTHRU); + HYBRID_BREAK(); HYBRID_CASE(ZEND_YIELD_FROM_SPEC_CONST): VM_TRACE(ZEND_YIELD_FROM_SPEC_CONST) ZEND_YIELD_FROM_SPEC_CONST_HANDLER(ZEND_OPCODE_HANDLER_ARGS_PASSTHRU); @@ -55609,10 +55612,6 @@ ZEND_API void execute_ex(zend_execute_data *ex) VM_TRACE(ZEND_ISSET_ISEMPTY_VAR_SPEC_CONST_UNUSED) ZEND_ISSET_ISEMPTY_VAR_SPEC_CONST_UNUSED_HANDLER(ZEND_OPCODE_HANDLER_ARGS_PASSTHRU); HYBRID_BREAK(); - HYBRID_CASE(ZEND_DECLARE_LAMBDA_FUNCTION_SPEC_CONST_UNUSED): - VM_TRACE(ZEND_DECLARE_LAMBDA_FUNCTION_SPEC_CONST_UNUSED) - ZEND_DECLARE_LAMBDA_FUNCTION_SPEC_CONST_UNUSED_HANDLER(ZEND_OPCODE_HANDLER_ARGS_PASSTHRU); - HYBRID_BREAK(); HYBRID_CASE(ZEND_YIELD_SPEC_CONST_UNUSED): VM_TRACE(ZEND_YIELD_SPEC_CONST_UNUSED) ZEND_YIELD_SPEC_CONST_UNUSED_HANDLER(ZEND_OPCODE_HANDLER_ARGS_PASSTHRU); @@ -61342,7 +61341,7 @@ void zend_vm_init(void) ZEND_NULL_HANDLER, ZEND_MAKE_REF_SPEC_CV_UNUSED_HANDLER, ZEND_DECLARE_FUNCTION_SPEC_HANDLER, - ZEND_DECLARE_LAMBDA_FUNCTION_SPEC_CONST_UNUSED_HANDLER, + ZEND_DECLARE_LAMBDA_FUNCTION_SPEC_CONST_HANDLER, ZEND_DECLARE_CONST_SPEC_CONST_CONST_HANDLER, ZEND_DECLARE_CLASS_SPEC_CONST_HANDLER, ZEND_DECLARE_CLASS_DELAYED_SPEC_CONST_CONST_HANDLER, diff --git a/Zend/zend_vm_handlers.h b/Zend/zend_vm_handlers.h index ed57433e178cc..2fbc04273d5ac 100644 --- a/Zend/zend_vm_handlers.h +++ b/Zend/zend_vm_handlers.h @@ -1158,7 +1158,7 @@ _(2286, ZEND_MAKE_REF_SPEC_VAR_UNUSED) \ _(2288, ZEND_MAKE_REF_SPEC_CV_UNUSED) \ _(2289, ZEND_DECLARE_FUNCTION_SPEC) \ - _(2290, ZEND_DECLARE_LAMBDA_FUNCTION_SPEC_CONST_UNUSED) \ + _(2290, ZEND_DECLARE_LAMBDA_FUNCTION_SPEC_CONST) \ _(2291, ZEND_DECLARE_CONST_SPEC_CONST_CONST) \ _(2292, ZEND_DECLARE_CLASS_SPEC_CONST) \ _(2293, ZEND_DECLARE_CLASS_DELAYED_SPEC_CONST_CONST) \ diff --git a/Zend/zend_vm_opcodes.c b/Zend/zend_vm_opcodes.c index b5cb9fd15ffb5..f25dc2e12ddd0 100644 --- a/Zend/zend_vm_opcodes.c +++ b/Zend/zend_vm_opcodes.c @@ -368,8 +368,8 @@ static uint32_t zend_vm_opcodes_flags[201] = { 0x00047305, 0x00000000, 0x00000101, - 0x00000000, - 0x00040103, + 0x00001000, + 0x00001003, 0x00000303, 0x00000003, 0x00000303, diff --git a/ext/opcache/zend_accelerator_util_funcs.c b/ext/opcache/zend_accelerator_util_funcs.c index 327e8a4d55467..ad456c1bcb23a 100644 --- a/ext/opcache/zend_accelerator_util_funcs.c +++ b/ext/opcache/zend_accelerator_util_funcs.c @@ -138,23 +138,9 @@ static void zend_accel_function_hash_copy(HashTable *target, HashTable *source) ZEND_ASSERT(p->key); t = zend_hash_find_ex(target, p->key, 1); if (UNEXPECTED(t != NULL)) { - if (EXPECTED(ZSTR_LEN(p->key) > 0) && EXPECTED(ZSTR_VAL(p->key)[0] == 0)) { - /* Runtime definition key. There are two circumstances under which the key can - * already be defined: - * 1. The file has been re-included without being changed in the meantime. In - * this case we can keep the old value, because we know that the definition - * hasn't changed. - * 2. The file has been changed in the meantime, but the RTD key ends up colliding. - * This would be a bug. - * As we can't distinguish these cases, we assume that it is 1. and keep the old - * value. */ - continue; - } else { - goto failure; - } - } else { - _zend_hash_append_ptr_ex(target, p->key, Z_PTR(p->val), 1); + goto failure; } + _zend_hash_append_ptr_ex(target, p->key, Z_PTR(p->val), 1); } target->nInternalPointer = 0; return; @@ -190,7 +176,15 @@ static void zend_accel_class_hash_copy(HashTable *target, HashTable *source) t = zend_hash_find_ex(target, p->key, 1); if (UNEXPECTED(t != NULL)) { if (EXPECTED(ZSTR_LEN(p->key) > 0) && EXPECTED(ZSTR_VAL(p->key)[0] == 0)) { - /* See comment in zend_accel_function_hash_copy(). */ + /* Runtime definition key. There are two circumstances under which the key can + * already be defined: + * 1. The file has been re-included without being changed in the meantime. In + * this case we can keep the old value, because we know that the definition + * hasn't changed. + * 2. The file has been changed in the meantime, but the RTD key ends up colliding. + * This would be a bug. + * As we can't distinguish these cases, we assume that it is 1. and keep the old + * value. */ continue; } else if (UNEXPECTED(!ZCG(accel_directives).ignore_dups)) { zend_class_entry *ce1 = Z_PTR(p->val); diff --git a/ext/opcache/zend_file_cache.c b/ext/opcache/zend_file_cache.c index d89c462df58cb..1021992b3acdb 100644 --- a/ext/opcache/zend_file_cache.c +++ b/ext/opcache/zend_file_cache.c @@ -606,6 +606,20 @@ static void zend_file_cache_serialize_op_array(zend_op_array *op_arra } } + if (op_array->num_dynamic_func_defs) { + zend_op_array **defs; + SERIALIZE_PTR(op_array->dynamic_func_defs); + defs = op_array->dynamic_func_defs; + UNSERIALIZE_PTR(defs); + for (uint32_t i = 0; i < op_array->num_dynamic_func_defs; i++) { + zend_op_array *def; + SERIALIZE_PTR(defs[i]); + def = defs[i]; + UNSERIALIZE_PTR(def); + zend_file_cache_serialize_op_array(def, script, info, buf); + } + } + SERIALIZE_STR(op_array->function_name); SERIALIZE_STR(op_array->filename); SERIALIZE_PTR(op_array->live_range); @@ -1394,6 +1408,14 @@ static void zend_file_cache_unserialize_op_array(zend_op_array *op_arr } } + if (op_array->num_dynamic_func_defs) { + UNSERIALIZE_PTR(op_array->dynamic_func_defs); + for (uint32_t i = 0; i < op_array->num_dynamic_func_defs; i++) { + UNSERIALIZE_PTR(op_array->dynamic_func_defs[i]); + zend_file_cache_unserialize_op_array(op_array->dynamic_func_defs[i], script, buf); + } + } + UNSERIALIZE_STR(op_array->function_name); UNSERIALIZE_STR(op_array->filename); UNSERIALIZE_PTR(op_array->live_range); diff --git a/ext/opcache/zend_persist.c b/ext/opcache/zend_persist.c index 5e406ec4b6b6d..6688e473cb70f 100644 --- a/ext/opcache/zend_persist.c +++ b/ext/opcache/zend_persist.c @@ -82,6 +82,7 @@ typedef void (*zend_persist_func_t)(zval*); static void zend_persist_zval(zval *z); +static void zend_persist_op_array(zval *zv); static const uint32_t uninitialized_bucket[-HT_MIN_MASK] = {HT_INVALID_IDX, HT_INVALID_IDX}; @@ -663,6 +664,17 @@ static void zend_persist_op_array_ex(zend_op_array *op_array, zend_persistent_sc } } + if (op_array->num_dynamic_func_defs) { + op_array->dynamic_func_defs = zend_shared_memdup_put_free( + op_array->dynamic_func_defs, sizeof(zend_function *) * op_array->num_dynamic_func_defs); + for (uint32_t i = 0; i < op_array->num_dynamic_func_defs; i++) { + zval tmp; + ZVAL_PTR(&tmp, op_array->dynamic_func_defs[i]); + zend_persist_op_array(&tmp); + op_array->dynamic_func_defs[i] = Z_PTR(tmp); + } + } + ZCG(mem) = (void*)((char*)ZCG(mem) + ZEND_ALIGNED_SIZE(zend_extensions_op_array_persist(op_array, ZCG(mem)))); #ifdef HAVE_JIT @@ -676,16 +688,22 @@ static void zend_persist_op_array_ex(zend_op_array *op_array, zend_persistent_sc static void zend_persist_op_array(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); - 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); + + old_op_array = zend_shared_alloc_get_xlat_entry(op_array); + if (!old_op_array) { + op_array = Z_PTR_P(zv) = zend_shared_memdup_put(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 { + /* This can happen during preloading, if a dynamic function definition is declared. */ } } diff --git a/ext/opcache/zend_persist_calc.c b/ext/opcache/zend_persist_calc.c index 015490b07a161..ad27d539be2c7 100644 --- a/ext/opcache/zend_persist_calc.c +++ b/ext/opcache/zend_persist_calc.c @@ -46,6 +46,7 @@ } while (0) static void zend_persist_zval_calc(zval *z); +static void zend_persist_op_array_calc(zval *zv); static void zend_hash_persist_calc(HashTable *ht) { @@ -287,16 +288,29 @@ static void zend_persist_op_array_calc_ex(zend_op_array *op_array) } } + if (op_array->num_dynamic_func_defs) { + ADD_SIZE(sizeof(void *) * op_array->num_dynamic_func_defs); + for (uint32_t i = 0; i < op_array->num_dynamic_func_defs; i++) { + zval tmp; + ZVAL_PTR(&tmp, op_array->dynamic_func_defs[i]); + zend_persist_op_array_calc(&tmp); + } + } + ADD_SIZE(ZEND_ALIGNED_SIZE(zend_extensions_op_array_persist_calc(op_array))); } static void zend_persist_op_array_calc(zval *zv) { zend_op_array *op_array = Z_PTR_P(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 (!zend_shared_alloc_get_xlat_entry(op_array)) { + zend_shared_alloc_register_xlat_entry(op_array, op_array); + ADD_SIZE(sizeof(zend_op_array)); + zend_persist_op_array_calc_ex(op_array); + } else { + /* This can happen during preloading, if a dynamic function definition is declared. */ + } } static void zend_persist_class_method_calc(zval *zv) diff --git a/sapi/phpdbg/phpdbg_print.c b/sapi/phpdbg/phpdbg_print.c index e3bbb9fed8a08..e009432a9e26e 100644 --- a/sapi/phpdbg/phpdbg_print.c +++ b/sapi/phpdbg/phpdbg_print.c @@ -87,6 +87,13 @@ static inline void phpdbg_print_function_helper(zend_function *method) /* {{{ */ efree(decode); opline++; } while (opcode++ < end); + + for (uint32_t i = 0; i < op_array->num_dynamic_func_defs; i++) { + zend_op_array *def = op_array->dynamic_func_defs[i]; + phpdbg_out("\ndynamic def: %i, function name: %.*s\n", + i, (int) ZSTR_LEN(def->function_name), ZSTR_VAL(def->function_name)); + phpdbg_print_function_helper((zend_function *) def); + } } } break;