diff options
Diffstat (limited to 'Zend/zend_closures.c')
-rw-r--r-- | Zend/zend_closures.c | 62 |
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; |