From 27e5b9fde1e7c32f2a97181eda3159dae85b817d Mon Sep 17 00:00:00 2001 From: Nikita Popov Date: Mon, 18 May 2020 12:06:20 +0200 Subject: [PATCH 1/2] Introduce zend_func_ref zend_functions for closures and dynamically declared functions are now referenced in the literals array through zend_func_ref. This avoids the need for collision-prone runtime-definition keys, and avoid memory leaks when the same file is included many times. --- Zend/zend_compile.c | 42 ++++++++++++------------------------------ Zend/zend_compile.h | 2 +- Zend/zend_types.h | 21 +++++++++++++++++++++ Zend/zend_variables.c | 12 ++++++++++-- Zend/zend_vm_def.h | 16 ++++------------ Zend/zend_vm_execute.h | 16 ++++------------ 6 files changed, 52 insertions(+), 57 deletions(-) diff --git a/Zend/zend_compile.c b/Zend/zend_compile.c index 58c74e1da08e9..e2670c7a7ee0c 100644 --- a/Zend/zend_compile.c +++ b/Zend/zend_compile.c @@ -1071,21 +1071,11 @@ static zend_never_inline ZEND_COLD ZEND_NORETURN void do_bind_function_error(zen } } -ZEND_API int do_bind_function(zval *lcname) /* {{{ */ +ZEND_API int 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); - return FAILURE; - } - function = (zend_function*)Z_PTR_P(zv); - zv = zend_hash_set_bucket_key(EG(function_table), (Bucket*)zv, Z_STR_P(lcname)); - if (UNEXPECTED(!zv)) { - do_bind_function_error(Z_STR_P(lcname), &function->op_array, 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; } return SUCCESS; @@ -6176,7 +6166,7 @@ zend_string *zend_begin_method_decl(zend_op_array *op_array, zend_string *name, static void zend_begin_func_decl(znode *result, zend_op_array *op_array, zend_ast_decl *decl, zend_bool toplevel) /* {{{ */ { - zend_string *unqualified_name, *name, *lcname, *key; + zend_string *unqualified_name, *name, *lcname; zend_op *opline; unqualified_name = decl->name; @@ -6212,24 +6202,16 @@ static void zend_begin_func_decl(znode *result, zend_op_array *op_array, zend_as return; } - key = zend_build_runtime_definition_key(lcname, decl->start_lineno); - if (!zend_hash_add_ptr(CG(function_table), key, op_array)) { - zend_error_noreturn(E_ERROR, - "Runtime definition key collision for function %s. This is a bug", ZSTR_VAL(name)); - } + znode func_node; + func_node.op_type = IS_CONST; + ZVAL_NEW_FUNC_REF(&func_node.u.constant, (zend_function *) 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); + zend_emit_op_tmp(result, ZEND_DECLARE_LAMBDA_FUNCTION, &func_node, NULL); } 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 = zend_emit_op(NULL, ZEND_DECLARE_FUNCTION, &func_node, NULL); + opline->op2_type = IS_CONST; + LITERAL_STR(opline->op2, zend_string_copy(lcname)); } zend_string_release_ex(lcname, 0); } diff --git a/Zend/zend_compile.h b/Zend/zend_compile.h index a90219b1b4453..7f8536d68b65f 100644 --- a/Zend/zend_compile.h +++ b/Zend/zend_compile.h @@ -746,7 +746,7 @@ zend_bool zend_handle_encoding_declaration(zend_ast *ast); /* parser-driven code generators */ void zend_do_free(znode *op1); -ZEND_API int do_bind_function(zval *lcname); +ZEND_API int do_bind_function(zend_function *func, zval *lcname); ZEND_API int 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); diff --git a/Zend/zend_types.h b/Zend/zend_types.h index 3543277b779bf..6f6971d141c48 100644 --- a/Zend/zend_types.h +++ b/Zend/zend_types.h @@ -93,6 +93,7 @@ typedef struct _zend_object zend_object; typedef struct _zend_resource zend_resource; typedef struct _zend_reference zend_reference; typedef struct _zend_ast_ref zend_ast_ref; +typedef struct _zend_func_ref zend_func_ref; typedef struct _zend_ast zend_ast; typedef int (*compare_func_t)(const void *, const void *); @@ -293,6 +294,7 @@ typedef union _zend_value { void *ptr; zend_class_entry *ce; zend_function *func; + zend_func_ref *func_ref; struct { uint32_t w1; uint32_t w2; @@ -514,6 +516,11 @@ struct _zend_ast_ref { /*zend_ast ast; zend_ast follows the zend_ast_ref structure */ }; +struct _zend_func_ref { + zend_refcounted_h gc; + zend_function *func; +}; + /* Regular data types: Must be in sync with zend_variables.c. */ #define IS_UNDEF 0 #define IS_NULL 1 @@ -527,6 +534,7 @@ struct _zend_ast_ref { #define IS_RESOURCE 9 #define IS_REFERENCE 10 #define IS_CONSTANT_AST 11 /* Constant expressions */ +#define IS_FUNC_REF 12 /* Fake types used only for type hinting. * These are allowed to overlap with the types below. */ @@ -682,6 +690,7 @@ static zend_always_inline uint32_t zval_gc_info(uint32_t gc_type_info) { #define IS_REFERENCE_EX (IS_REFERENCE | (IS_TYPE_REFCOUNTED << Z_TYPE_FLAGS_SHIFT)) #define IS_CONSTANT_AST_EX (IS_CONSTANT_AST | (IS_TYPE_REFCOUNTED << Z_TYPE_FLAGS_SHIFT)) +#define IS_FUNC_REF_EX (IS_FUNC_REF | (IS_TYPE_REFCOUNTED << Z_TYPE_FLAGS_SHIFT)) /* string flags (zval.value->gc.u.flags) */ #define IS_STR_INTERNED GC_IMMUTABLE /* interned string */ @@ -859,6 +868,9 @@ static zend_always_inline uint32_t zval_gc_info(uint32_t gc_type_info) { #define Z_FUNC(zval) (zval).value.func #define Z_FUNC_P(zval_p) Z_FUNC(*(zval_p)) +#define Z_FUNC_REF(zval) (zval).value.func_ref +#define Z_FUNC_REF_P(zval_p) Z_FUNC_REF(*(zval_p)) + #define Z_PTR(zval) (zval).value.ptr #define Z_PTR_P(zval_p) Z_PTR(*(zval_p)) @@ -1082,6 +1094,15 @@ static zend_always_inline uint32_t zval_gc_info(uint32_t gc_type_info) { Z_TYPE_INFO_P(z) = _IS_ERROR; \ } while (0) +#define ZVAL_NEW_FUNC_REF(z, _func) do { \ + zend_func_ref *_ref = emalloc(sizeof(zend_func_ref)); \ + GC_SET_REFCOUNT(_ref, 1); \ + GC_TYPE_INFO(_ref) = IS_FUNC_REF; \ + _ref->func = (_func); \ + Z_FUNC_REF_P(z) = _ref; \ + Z_TYPE_INFO_P(z) = IS_FUNC_REF_EX; \ + } while (0) + #define Z_REFCOUNT_P(pz) zval_refcount_p(pz) #define Z_SET_REFCOUNT_P(pz, rc) zval_set_refcount_p(pz, rc) #define Z_ADDREF_P(pz) zval_addref_p(pz) diff --git a/Zend/zend_variables.c b/Zend/zend_variables.c index 810866a1be23c..d740e4bc53171 100644 --- a/Zend/zend_variables.c +++ b/Zend/zend_variables.c @@ -33,6 +33,7 @@ static void ZEND_FASTCALL zend_string_destroy(zend_string *str); #endif static void ZEND_FASTCALL zend_reference_destroy(zend_reference *ref); static void ZEND_FASTCALL zend_empty_destroy(zend_reference *ref); +static void ZEND_FASTCALL zend_func_ref_destroy(zend_func_ref *ref); typedef void (ZEND_FASTCALL *zend_rc_dtor_func_t)(zend_refcounted *p); @@ -48,12 +49,13 @@ static const zend_rc_dtor_func_t zend_rc_dtor_func[] = { /* IS_OBJECT */ (zend_rc_dtor_func_t)zend_objects_store_del, /* IS_RESOURCE */ (zend_rc_dtor_func_t)zend_list_free, /* IS_REFERENCE */ (zend_rc_dtor_func_t)zend_reference_destroy, - /* IS_CONSTANT_AST */ (zend_rc_dtor_func_t)zend_ast_ref_destroy + /* IS_CONSTANT_AST */ (zend_rc_dtor_func_t)zend_ast_ref_destroy, + /* IS_FUNC_REF */ (zend_rc_dtor_func_t)zend_func_ref_destroy, }; ZEND_API void ZEND_FASTCALL rc_dtor_func(zend_refcounted *p) { - ZEND_ASSERT(GC_TYPE(p) <= IS_CONSTANT_AST); + ZEND_ASSERT(GC_TYPE(p) <= IS_FUNC_REF); zend_rc_dtor_func[GC_TYPE(p)](p); } @@ -79,6 +81,12 @@ static void ZEND_FASTCALL zend_empty_destroy(zend_reference *ref) { } +static void ZEND_FASTCALL zend_func_ref_destroy(zend_func_ref *ref) +{ + destroy_zend_function(ref->func); + efree_size(ref, sizeof(zend_func_ref)); +} + ZEND_API void zval_ptr_dtor(zval *zval_ptr) /* {{{ */ { i_zval_ptr_dtor(zval_ptr); diff --git a/Zend/zend_vm_def.h b/Zend/zend_vm_def.h index de0dfa2f357c3..768d9ea7cca3c 100644 --- a/Zend/zend_vm_def.h +++ b/Zend/zend_vm_def.h @@ -7188,7 +7188,9 @@ ZEND_VM_HANDLER(141, ZEND_DECLARE_FUNCTION, ANY, ANY) USE_OPLINE SAVE_OPLINE(); - do_bind_function(RT_CONSTANT(opline, opline->op1)); + do_bind_function( + Z_FUNC_REF_P(RT_CONSTANT(opline, opline->op1))->func, + RT_CONSTANT(opline, opline->op2)); ZEND_VM_NEXT_OPCODE_CHECK_EXCEPTION(); } @@ -7451,20 +7453,10 @@ ZEND_VM_HANDLER(143, ZEND_DECLARE_CONST, CONST, CONST) ZEND_VM_HANDLER(142, ZEND_DECLARE_LAMBDA_FUNCTION, CONST, UNUSED, CACHE_SLOT) { USE_OPLINE - zend_function *func; - zval *zfunc; + zend_function *func = Z_FUNC_REF_P(RT_CONSTANT(opline, opline->op1))->func; 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) || diff --git a/Zend/zend_vm_execute.h b/Zend/zend_vm_execute.h index 0bf16a6fee812..8361e1917d534 100644 --- a/Zend/zend_vm_execute.h +++ b/Zend/zend_vm_execute.h @@ -2439,7 +2439,9 @@ static ZEND_OPCODE_HANDLER_RET ZEND_FASTCALL ZEND_DECLARE_FUNCTION_SPEC_HANDLER( USE_OPLINE SAVE_OPLINE(); - do_bind_function(RT_CONSTANT(opline, opline->op1)); + do_bind_function( + Z_FUNC_REF_P(RT_CONSTANT(opline, opline->op1))->func, + RT_CONSTANT(opline, opline->op2)); ZEND_VM_NEXT_OPCODE_CHECK_EXCEPTION(); } @@ -9100,20 +9102,10 @@ static ZEND_OPCODE_HANDLER_RET ZEND_FASTCALL ZEND_ISSET_ISEMPTY_VAR_SPEC_CONST_U 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; + zend_function *func = Z_FUNC_REF_P(RT_CONSTANT(opline, opline->op1))->func; 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) || From d5ba72151612a9c2d24653166601c0d8cd439b8d Mon Sep 17 00:00:00 2001 From: Nikita Popov Date: Mon, 18 May 2020 12:16:16 +0200 Subject: [PATCH 2/2] Add opcache support --- Zend/zend_type_info.h | 3 +- ext/opcache/Optimizer/compact_literals.c | 4 --- ext/opcache/Optimizer/zend_dump.c | 3 ++ ext/opcache/Optimizer/zend_inference.h | 2 ++ ext/opcache/zend_accelerator_util_funcs.c | 34 ++++++++--------------- ext/opcache/zend_file_cache.c | 34 +++++++++++++++++++++++ ext/opcache/zend_persist.c | 18 ++++++++++++ ext/opcache/zend_persist_calc.c | 9 ++++++ 8 files changed, 80 insertions(+), 27 deletions(-) diff --git a/Zend/zend_type_info.h b/Zend/zend_type_info.h index bace8014bfc3f..d5bc52e556e2f 100644 --- a/Zend/zend_type_info.h +++ b/Zend/zend_type_info.h @@ -60,6 +60,7 @@ #define MAY_BE_ARRAY_KEY_STRING (1<<22) #define MAY_BE_ARRAY_KEY_ANY (MAY_BE_ARRAY_KEY_LONG | MAY_BE_ARRAY_KEY_STRING) -#define MAY_BE_CLASS (1<<23) +#define MAY_BE_SPECIAL (1<<23) +#define MAY_BE_CLASS MAY_BE_SPECIAL #endif /* ZEND_TYPE_INFO_H */ diff --git a/ext/opcache/Optimizer/compact_literals.c b/ext/opcache/Optimizer/compact_literals.c index f29262bd46b78..4c26b8c080913 100644 --- a/ext/opcache/Optimizer/compact_literals.c +++ b/ext/opcache/Optimizer/compact_literals.c @@ -238,9 +238,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); @@ -773,7 +770,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/ext/opcache/Optimizer/zend_dump.c b/ext/opcache/Optimizer/zend_dump.c index dfd939f332c5a..ebc93cbd4e09f 100644 --- a/ext/opcache/Optimizer/zend_dump.c +++ b/ext/opcache/Optimizer/zend_dump.c @@ -72,6 +72,9 @@ void zend_dump_const(const zval *zv) case IS_ARRAY: fprintf(stderr, " array(...)"); break; + case IS_FUNC_REF: + fprintf(stderr, " func(%p)", Z_FUNC_REF_P(zv)->func); + break; default: fprintf(stderr, " zval(type=%d)", Z_TYPE_P(zv)); break; diff --git a/ext/opcache/Optimizer/zend_inference.h b/ext/opcache/Optimizer/zend_inference.h index da103e4bb5280..1c53a999f6251 100644 --- a/ext/opcache/Optimizer/zend_inference.h +++ b/ext/opcache/Optimizer/zend_inference.h @@ -157,6 +157,8 @@ DEFINE_SSA_OP_RANGE_OVERFLOW(op2) static zend_always_inline uint32_t _const_op_type(const zval *zv) { if (Z_TYPE_P(zv) == IS_CONSTANT_AST) { return MAY_BE_RC1 | MAY_BE_RCN | MAY_BE_ANY | MAY_BE_ARRAY_KEY_ANY | MAY_BE_ARRAY_OF_ANY; + } else if (Z_TYPE_P(zv) == IS_FUNC_REF) { + return MAY_BE_RC1 | MAY_BE_RCN | MAY_BE_SPECIAL; } else if (Z_TYPE_P(zv) == IS_ARRAY) { HashTable *ht = Z_ARRVAL_P(zv); uint32_t tmp = MAY_BE_ARRAY; diff --git a/ext/opcache/zend_accelerator_util_funcs.c b/ext/opcache/zend_accelerator_util_funcs.c index 131fc8495b910..b4002888ac051 100644 --- a/ext/opcache/zend_accelerator_util_funcs.c +++ b/ext/opcache/zend_accelerator_util_funcs.c @@ -438,20 +438,7 @@ 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; - } + goto failure; } else { _zend_hash_append_ptr(target, p->key, Z_PTR(p->val)); } @@ -490,12 +477,7 @@ static void zend_accel_function_hash_copy_from_shm(HashTable *target, HashTable ZEND_ASSERT(p->key); t = zend_hash_find_ex(target, p->key, 1); if (UNEXPECTED(t != NULL)) { - if (EXPECTED(ZSTR_LEN(p->key) > 0) && EXPECTED(ZSTR_VAL(p->key)[0] == 0)) { - /* See comment in zend_accel_function_hash_copy(). */ - continue; - } else { - goto failure; - } + goto failure; } else { _zend_hash_append_ptr_ex(target, p->key, Z_PTR(p->val), 1); } @@ -534,7 +516,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); @@ -571,7 +561,7 @@ static void zend_accel_class_hash_copy_from_shm(HashTable *target, HashTable *so 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(). */ + /* See comment in zend_accel_class_hash_copy(). */ 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 eb74187e5d419..0409103bc5ff4 100644 --- a/ext/opcache/zend_file_cache.c +++ b/ext/opcache/zend_file_cache.c @@ -211,6 +211,14 @@ static void zend_file_cache_unserialize_zval(zval *zv, zend_persistent_script *script, void *buf); +static void zend_file_cache_serialize_op_array(zend_op_array *op_array, + zend_persistent_script *script, + zend_file_cache_metainfo *info, + void *buf); +static void zend_file_cache_unserialize_op_array(zend_op_array *op_array, + zend_persistent_script *script, + void *buf); + static void *zend_file_cache_serialize_interned(zend_string *str, zend_file_cache_metainfo *info) { @@ -368,6 +376,23 @@ static void zend_file_cache_serialize_zval(zval *zv, zend_file_cache_serialize_ast(GC_AST(ast), script, info, buf); } break; + case IS_FUNC_REF: + if (!IS_SERIALIZED(Z_FUNC_REF_P(zv))) { + zend_func_ref *ref; + zend_function *func; + + SERIALIZE_PTR(Z_FUNC_REF_P(zv)); + ref = Z_FUNC_REF_P(zv); + UNSERIALIZE_PTR(ref); + + SERIALIZE_PTR(ref->func); + func = ref->func; + UNSERIALIZE_PTR(func); + + ZEND_ASSERT(func->type == ZEND_USER_FUNCTION); + zend_file_cache_serialize_op_array((zend_op_array *) func, script, info, buf); + } + break; } } @@ -1089,6 +1114,15 @@ static void zend_file_cache_unserialize_zval(zval *zv, zend_file_cache_unserialize_ast(Z_ASTVAL_P(zv), script, buf); } break; + case IS_FUNC_REF: + if (!IS_UNSERIALIZED(Z_FUNC_REF_P(zv))) { + UNSERIALIZE_PTR(Z_FUNC_REF_P(zv)); + UNSERIALIZE_PTR(Z_FUNC_REF_P(zv)->func); + ZEND_ASSERT(Z_FUNC_REF_P(zv)->func->type == ZEND_USER_FUNCTION); + zend_file_cache_unserialize_op_array( + (zend_op_array *) Z_FUNC_REF_P(zv)->func, script, buf); + } + break; } } diff --git a/ext/opcache/zend_persist.c b/ext/opcache/zend_persist.c index 6a158e73f9e33..36d8a754bbfec 100644 --- a/ext/opcache/zend_persist.c +++ b/ext/opcache/zend_persist.c @@ -79,6 +79,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}; @@ -251,6 +252,23 @@ static void zend_persist_zval(zval *z) efree(old_ref); } break; + case IS_FUNC_REF: + new_ptr = zend_shared_alloc_get_xlat_entry(Z_FUNC_REF_P(z)); + if (new_ptr) { + Z_FUNC_REF_P(z) = new_ptr; + } else { + zend_func_ref *old_ref = Z_FUNC_REF_P(z); + Z_FUNC_REF_P(z) = zend_shared_memdup_put(Z_FUNC_REF_P(z), sizeof(zend_func_ref)); + GC_SET_REFCOUNT(Z_COUNTED_P(z), 1); + efree(old_ref); + + zval tmp; + ZVAL_PTR(&tmp, Z_FUNC_REF_P(z)->func); + zend_persist_op_array(&tmp); + Z_FUNC_REF_P(z)->func = Z_PTR(tmp); + } + Z_TYPE_FLAGS_P(z) = 0; + break; default: ZEND_ASSERT(Z_TYPE_P(z) != IS_OBJECT); ZEND_ASSERT(Z_TYPE_P(z) != IS_RESOURCE); diff --git a/ext/opcache/zend_persist_calc.c b/ext/opcache/zend_persist_calc.c index 5de27b9efb006..bb5850994404c 100644 --- a/ext/opcache/zend_persist_calc.c +++ b/ext/opcache/zend_persist_calc.c @@ -54,6 +54,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) { @@ -141,6 +142,14 @@ static void zend_persist_zval_calc(zval *z) zend_persist_ast_calc(Z_ASTVAL_P(z)); } break; + case IS_FUNC_REF: + size = zend_shared_memdup_size(Z_FUNC_REF_P(z), sizeof(zend_func_ref)); + if (size) { + zval tmp; + ZVAL_PTR(&tmp, Z_FUNC_REF_P(z)->func); + zend_persist_op_array_calc(&tmp); + ADD_SIZE(size); + } default: ZEND_ASSERT(Z_TYPE_P(z) != IS_OBJECT); ZEND_ASSERT(Z_TYPE_P(z) != IS_RESOURCE);