summaryrefslogtreecommitdiff
path: root/Zend
diff options
context:
space:
mode:
authorNikita Popov <nikita.ppv@gmail.com>2020-05-18 15:46:06 +0200
committerNikita Popov <nikita.ppv@gmail.com>2021-03-01 11:35:54 +0100
commit47a2e5c785cdba71e003d9ad77cb799d4be88806 (patch)
treed03767c644d6d6652a882b2b2946ff55a7a036a9 /Zend
parentb86dfb0e747a2254f3de97347ac89d791572141e (diff)
downloadphp-git-47a2e5c785cdba71e003d9ad77cb799d4be88806.tar.gz
Reference dynamic functions through dynamic_defs
Currently, dynamically declared functions and closures are inserted into the function table under a runtime definition key, and then later possibly renamed. When opcache is not used and a file containing a closure is repeatedly included, this leads to a very large memory leak, as the no longer needed closure declarations will never be freed (https://bugs.php.net/bug.php?id=76982). With this patch, dynamic functions are instead stored in a dynamic_func_defs member on the op_array, which opcodes reference by index. When the parent op_array is destroyed, the dynamic_func_defs it contains are also destroyed (unless they are stilled used elsewhere, e.g. because they have been bound, or are used by a live closure). This resolves the fundamental part of the leak, though doesn't completely fix it yet due to some arena allocations. The main non-obvious change here is to static variable handling: We can't destroy static_variables_ptr in destroy_op_array, as e.g. that would clear the static variables in a dynamic function when the op_array containing it is destroyed. Static variable destruction is separated out for this reason (we already do static variable destruction separately for normal functions, so we only need to handle main scripts). Closes GH-5595.
Diffstat (limited to 'Zend')
-rw-r--r--Zend/Optimizer/compact_literals.c4
-rw-r--r--Zend/Optimizer/zend_optimizer.c14
-rw-r--r--Zend/tests/static_variable_in_dynamic_function.phpt21
-rw-r--r--Zend/tests/static_variable_in_dynamic_function_2.phpt21
-rw-r--r--Zend/zend.c1
-rw-r--r--Zend/zend_closures.c7
-rw-r--r--Zend/zend_compile.c52
-rw-r--r--Zend/zend_compile.h8
-rw-r--r--Zend/zend_execute_API.c1
-rw-r--r--Zend/zend_opcode.c26
-rw-r--r--Zend/zend_vm_def.h21
-rw-r--r--Zend/zend_vm_execute.h83
-rw-r--r--Zend/zend_vm_handlers.h2
-rw-r--r--Zend/zend_vm_opcodes.c4
14 files changed, 162 insertions, 103 deletions
diff --git a/Zend/Optimizer/compact_literals.c b/Zend/Optimizer/compact_literals.c
index 0e1529d2bd..55cc40afb9 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 ae548fc520..cc9971a9f8 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 0000000000..77b71a4883
--- /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--
+<?php
+
+$code = <<<'CODE'
+if (1) {
+ function test() {
+ static $x = 0;
+ var_dump(++$x);
+ }
+ test();
+}
+CODE;
+eval($code);
+test();
+
+?>
+--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 0000000000..f7c160325f
--- /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--
+<?php
+
+$code = <<<'CODE'
+if (1) {
+ function test() {
+ static $x = 0;
+ var_dump(++$x);
+ }
+}
+CODE;
+eval($code);
+test();
+test();
+
+?>
+--EXPECT--
+int(1)
+int(2)
diff --git a/Zend/zend.c b/Zend/zend.c
index ce6287e2d1..9e5c7e2328 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 452f99e6fb..f1ffe78c69 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 2b2dc9b249..deff0dbe57 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 ce4b50aaee..6f64624fd8 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 1d484934e3..4e8c1d5a1f 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 ac9cc5f704..4e30b9ff7a 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 b78f8ad61e..8b4a86b745 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 419de3fa26..26903f9403 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_leave_helper_SPEC_LABEL:
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_leave_helper_SPEC_LABEL:
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_leave_helper_SPEC_LABEL:
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 ed57433e17..2fbc04273d 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 b5cb9fd15f..f25dc2e12d 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,