summaryrefslogtreecommitdiff
path: root/Zend/zend_closures.c
diff options
context:
space:
mode:
Diffstat (limited to 'Zend/zend_closures.c')
-rw-r--r--Zend/zend_closures.c62
1 files changed, 37 insertions, 25 deletions
diff --git a/Zend/zend_closures.c b/Zend/zend_closures.c
index e7777c58cf..f1ffe78c69 100644
--- a/Zend/zend_closures.c
+++ b/Zend/zend_closures.c
@@ -69,11 +69,11 @@ ZEND_METHOD(Closure, __invoke) /* {{{ */
}
/* }}} */
-static zend_bool zend_valid_closure_binding(
+static bool zend_valid_closure_binding(
zend_closure *closure, zval *newthis, zend_class_entry *scope) /* {{{ */
{
zend_function *func = &closure->func;
- zend_bool is_fake_closure = (func->common.fn_flags & ZEND_ACC_FAKE_CLOSURE) != 0;
+ bool is_fake_closure = (func->common.fn_flags & ZEND_ACC_FAKE_CLOSURE) != 0;
if (newthis) {
if (func->common.fn_flags & ZEND_ACC_STATIC) {
zend_error(E_WARNING, "Cannot bind an instance to a static closure");
@@ -128,6 +128,7 @@ ZEND_METHOD(Closure, call)
zend_fcall_info_cache fci_cache;
zend_function my_function;
zend_object *newobj;
+ zend_class_entry *newclass;
fci.param_count = 0;
fci.params = NULL;
@@ -140,26 +141,27 @@ ZEND_METHOD(Closure, call)
closure = (zend_closure *) Z_OBJ_P(ZEND_THIS);
newobj = Z_OBJ_P(newthis);
+ newclass = newobj->ce;
- if (!zend_valid_closure_binding(closure, newthis, Z_OBJCE_P(newthis))) {
+ if (!zend_valid_closure_binding(closure, newthis, newclass)) {
return;
}
if (closure->func.common.fn_flags & ZEND_ACC_GENERATOR) {
zval new_closure;
- zend_create_closure(&new_closure, &closure->func, Z_OBJCE_P(newthis), closure->called_scope, newthis);
+ zend_create_closure(&new_closure, &closure->func, newclass, closure->called_scope, newthis);
closure = (zend_closure *) Z_OBJ(new_closure);
fci_cache.function_handler = &closure->func;
} else {
memcpy(&my_function, &closure->func, closure->func.type == ZEND_USER_FUNCTION ? sizeof(zend_op_array) : sizeof(zend_internal_function));
my_function.common.fn_flags &= ~ZEND_ACC_CLOSURE;
/* use scope of passed object */
- my_function.common.scope = Z_OBJCE_P(newthis);
+ my_function.common.scope = newclass;
fci_cache.function_handler = &my_function;
/* Runtime cache relies on bound scope to be immutable, hence we need a separate rt cache in case scope changed */
if (ZEND_USER_CODE(my_function.type)
- && (closure->func.common.scope != Z_OBJCE_P(newthis)
+ && (closure->func.common.scope != newclass
|| (closure->func.common.fn_flags & ZEND_ACC_HEAP_RT_CACHE))) {
void *ptr;
@@ -172,7 +174,7 @@ ZEND_METHOD(Closure, call)
}
}
- fci_cache.called_scope = newobj->ce;
+ fci_cache.called_scope = newclass;
fci_cache.object = fci.object = newobj;
fci.size = sizeof(fci);
@@ -354,9 +356,9 @@ ZEND_METHOD(Closure, fromCallable)
zval *callable;
char *error = NULL;
- if (zend_parse_parameters(ZEND_NUM_ARGS(), "z", &callable) == FAILURE) {
- RETURN_THROWS();
- }
+ ZEND_PARSE_PARAMETERS_START(1, 1)
+ Z_PARAM_ZVAL(callable)
+ ZEND_PARSE_PARAMETERS_END();
if (Z_TYPE_P(callable) == IS_OBJECT && instanceof_function(Z_OBJCE_P(callable), zend_ce_closure)) {
/* It's already a closure */
@@ -484,6 +486,10 @@ static void zend_closure_free_storage(zend_object *object) /* {{{ */
zend_object_std_dtor(&closure->std);
if (closure->func.type == ZEND_USER_FUNCTION) {
+ /* 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);
}
@@ -518,7 +524,7 @@ static zend_object *zend_closure_clone(zend_object *zobject) /* {{{ */
}
/* }}} */
-int zend_closure_get_closure(zend_object *obj, zend_class_entry **ce_ptr, zend_function **fptr_ptr, zend_object **obj_ptr, zend_bool check_only) /* {{{ */
+int zend_closure_get_closure(zend_object *obj, zend_class_entry **ce_ptr, zend_function **fptr_ptr, zend_object **obj_ptr, bool check_only) /* {{{ */
{
zend_closure *closure = (zend_closure *)obj;
*fptr_ptr = &closure->func;
@@ -541,7 +547,7 @@ static HashTable *zend_closure_get_debug_info(zend_object *object, int *is_temp)
zval val;
struct _zend_arg_info *arg_info = closure->func.common.arg_info;
HashTable *debug_info;
- zend_bool zstr_args = (closure->func.type == ZEND_USER_FUNCTION) || (closure->func.common.fn_flags & ZEND_ACC_USER_ARG_INFO);
+ bool zstr_args = (closure->func.type == ZEND_USER_FUNCTION) || (closure->func.common.fn_flags & ZEND_ACC_USER_ARG_INFO);
*is_temp = 1;
@@ -613,7 +619,9 @@ static HashTable *zend_closure_get_gc(zend_object *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) ?
+ /* Fake closures don't own the static variables they reference. */
+ return (closure->func.type == ZEND_USER_FUNCTION
+ && !(closure->func.op_array.fn_flags & ZEND_ACC_FAKE_CLOSURE)) ?
ZEND_MAP_PTR_GET(closure->func.op_array.static_variables_ptr) : NULL;
}
/* }}} */
@@ -627,11 +635,7 @@ ZEND_COLD ZEND_METHOD(Closure, __construct)
void zend_register_closure_ce(void) /* {{{ */
{
- zend_class_entry ce;
-
- INIT_CLASS_ENTRY(ce, "Closure", class_Closure_methods);
- zend_ce_closure = zend_register_internal_class(&ce);
- zend_ce_closure->ce_flags |= ZEND_ACC_FINAL;
+ zend_ce_closure = register_class_Closure();
zend_ce_closure->create_object = zend_closure_new;
zend_ce_closure->serialize = zend_class_serialize_deny;
zend_ce_closure->unserialize = zend_class_unserialize_deny;
@@ -662,7 +666,7 @@ static ZEND_NAMED_FUNCTION(zend_closure_internal_handler) /* {{{ */
}
/* }}} */
-ZEND_API void zend_create_closure(zval *res, zend_function *func, zend_class_entry *scope, zend_class_entry *called_scope, zval *this_ptr) /* {{{ */
+static void zend_create_closure_ex(zval *res, zend_function *func, zend_class_entry *scope, zend_class_entry *called_scope, zval *this_ptr, bool is_fake) /* {{{ */
{
zend_closure *closure;
@@ -681,12 +685,15 @@ ZEND_API void zend_create_closure(zval *res, zend_function *func, zend_class_ent
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);
+ /* For fake closures, we want to reuse the static variables of the original function. */
+ if (!is_fake) {
+ 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);
}
- 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 (!ZEND_MAP_PTR_GET(closure->func.op_array.run_time_cache)
@@ -756,11 +763,16 @@ ZEND_API void zend_create_closure(zval *res, zend_function *func, zend_class_ent
}
/* }}} */
+ZEND_API void zend_create_closure(zval *res, zend_function *func, zend_class_entry *scope, zend_class_entry *called_scope, zval *this_ptr)
+{
+ zend_create_closure_ex(res, func, scope, called_scope, this_ptr, /* is_fake */ false);
+}
+
ZEND_API void zend_create_fake_closure(zval *res, zend_function *func, zend_class_entry *scope, zend_class_entry *called_scope, zval *this_ptr) /* {{{ */
{
zend_closure *closure;
- zend_create_closure(res, func, scope, called_scope, this_ptr);
+ zend_create_closure_ex(res, func, scope, called_scope, this_ptr, /* is_fake */ true);
closure = (zend_closure *)Z_OBJ_P(res);
closure->func.common.fn_flags |= ZEND_ACC_FAKE_CLOSURE;