diff options
Diffstat (limited to 'Zend/zend_closures.c')
-rw-r--r-- | Zend/zend_closures.c | 139 |
1 files changed, 86 insertions, 53 deletions
diff --git a/Zend/zend_closures.c b/Zend/zend_closures.c index b9d8bd1c75..70dc469a48 100644 --- a/Zend/zend_closures.c +++ b/Zend/zend_closures.c @@ -2,7 +2,7 @@ +----------------------------------------------------------------------+ | Zend Engine | +----------------------------------------------------------------------+ - | Copyright (c) 1998-2018 Zend Technologies Ltd. (http://www.zend.com) | + | Copyright (c) Zend Technologies Ltd. (http://www.zend.com) | +----------------------------------------------------------------------+ | This source file is subject to version 2.00 of the Zend license, | | that is bundled with this package in the file LICENSE, and is | @@ -49,7 +49,7 @@ ZEND_METHOD(Closure, __invoke) /* {{{ */ zend_function *func = EX(func); zval *arguments = ZEND_CALL_ARG(execute_data, 1); - if (call_user_function(CG(function_table), NULL, getThis(), return_value, ZEND_NUM_ARGS(), arguments) == FAILURE) { + if (call_user_function(CG(function_table), NULL, ZEND_THIS, return_value, ZEND_NUM_ARGS(), arguments) == FAILURE) { RETVAL_FALSE; } @@ -82,10 +82,18 @@ static zend_bool zend_valid_closure_binding( ZSTR_VAL(Z_OBJCE_P(newthis)->name)); return 0; } - } else if (!(func->common.fn_flags & ZEND_ACC_STATIC) && func->common.scope - && func->type == ZEND_INTERNAL_FUNCTION) { - zend_error(E_WARNING, "Cannot unbind $this of internal method"); - return 0; + } else if (is_fake_closure && func->common.scope + && !(func->common.fn_flags & ZEND_ACC_STATIC)) { + if (func->type == ZEND_INTERNAL_FUNCTION) { + zend_error(E_WARNING, "Cannot unbind $this of internal method"); + return 0; + } else { + zend_error(E_DEPRECATED, "Unbinding $this of a method is deprecated"); + } + } else if (!is_fake_closure && !Z_ISUNDEF(closure->this_ptr) + && (func->common.fn_flags & ZEND_ACC_USES_THIS)) { + // TODO: Only deprecate if it had $this *originally*? + zend_error(E_DEPRECATED, "Unbinding $this of closure is deprecated"); } if (scope && scope != func->common.scope && scope->type == ZEND_INTERNAL_CLASS) { @@ -108,7 +116,7 @@ static zend_bool zend_valid_closure_binding( Call closure, binding to a given object with its class as the scope */ ZEND_METHOD(Closure, call) { - zval *zclosure, *newthis, closure_result; + zval *newthis, closure_result; zend_closure *closure; zend_fcall_info fci; zend_fcall_info_cache fci_cache; @@ -122,8 +130,7 @@ ZEND_METHOD(Closure, call) return; } - zclosure = getThis(); - closure = (zend_closure *) Z_OBJ_P(zclosure); + closure = (zend_closure *) Z_OBJ_P(ZEND_THIS); newobj = Z_OBJ_P(newthis); @@ -144,9 +151,17 @@ ZEND_METHOD(Closure, call) 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)) { - my_function.op_array.run_time_cache = emalloc(my_function.op_array.cache_size); - memset(my_function.op_array.run_time_cache, 0, my_function.op_array.cache_size); + if (ZEND_USER_CODE(my_function.type) + && (closure->func.common.scope != Z_OBJCE_P(newthis) + || (closure->func.common.fn_flags & ZEND_ACC_HEAP_RT_CACHE))) { + void *ptr; + + my_function.op_array.fn_flags |= ZEND_ACC_HEAP_RT_CACHE; + ptr = emalloc(sizeof(void*) + my_function.op_array.cache_size); + ZEND_MAP_PTR_INIT(my_function.op_array.run_time_cache, ptr); + ptr = (char*)ptr + sizeof(void*); + ZEND_MAP_PTR_SET(my_function.op_array.run_time_cache, ptr); + memset(ptr, 0, my_function.op_array.cache_size); } } @@ -154,7 +169,7 @@ ZEND_METHOD(Closure, call) fci_cache.object = fci.object = newobj; fci.size = sizeof(fci); - ZVAL_COPY_VALUE(&fci.function_name, zclosure); + ZVAL_OBJ(&fci.function_name, &closure->std); fci.retval = &closure_result; fci.no_separation = 1; @@ -168,8 +183,9 @@ ZEND_METHOD(Closure, call) if (fci_cache.function_handler->common.fn_flags & ZEND_ACC_GENERATOR) { /* copied upon generator creation */ GC_DELREF(&closure->std); - } else if (ZEND_USER_CODE(my_function.type) && closure->func.common.scope != Z_OBJCE_P(newthis)) { - efree(my_function.op_array.run_time_cache); + } else if (ZEND_USER_CODE(my_function.type) + && fci_cache.function_handler->common.fn_flags & ZEND_ACC_HEAP_RT_CACHE) { + efree(ZEND_MAP_PTR(my_function.op_array.run_time_cache)); } } /* }}} */ @@ -198,7 +214,7 @@ ZEND_METHOD(Closure, bind) zend_string *class_name = zval_get_tmp_string(scope_arg, &tmp_class_name); if (zend_string_equals_literal(class_name, "static")) { ce = closure->func.common.scope; - } else if ((ce = zend_lookup_class_ex(class_name, NULL, 1)) == NULL) { + } else if ((ce = zend_lookup_class(class_name)) == NULL) { zend_error(E_WARNING, "Class '%s' not found", ZSTR_VAL(class_name)); zend_tmp_string_release(tmp_class_name); RETURN_NULL(); @@ -246,8 +262,7 @@ static ZEND_NAMED_FUNCTION(zend_closure_call_magic) /* {{{ */ { ZVAL_EMPTY_ARRAY(&fci.params[1]); } - fci.object = Z_OBJ(EX(This)); - fcc.object = Z_OBJ(EX(This)); + fcc.object = fci.object = Z_OBJ_P(ZEND_THIS); zend_call_function(&fci, &fcc); @@ -277,6 +292,19 @@ static int zend_create_closure_from_callable(zval *return_value, zval *callable, return SUCCESS; } + if (!mptr->common.scope) { + return FAILURE; + } + if (mptr->common.fn_flags & ZEND_ACC_STATIC) { + if (!mptr->common.scope->__callstatic) { + return FAILURE; + } + } else { + if (!mptr->common.scope->__call) { + return FAILURE; + } + } + memset(&call, 0, sizeof(zend_internal_function)); call.type = ZEND_INTERNAL_FUNCTION; call.fn_flags = mptr->common.fn_flags & ZEND_ACC_STATIC; @@ -323,10 +351,10 @@ ZEND_METHOD(Closure, fromCallable) if (success == FAILURE || error) { if (error) { - zend_throw_exception_ex(zend_ce_type_error, 0, "Failed to create closure from callable: %s", error); + zend_type_error("Failed to create closure from callable: %s", error); efree(error); } else { - zend_throw_exception_ex(zend_ce_type_error, 0, "Failed to create closure from callable"); + zend_type_error("Failed to create closure from callable"); } } } @@ -397,27 +425,28 @@ static zend_function *zend_closure_get_method(zend_object **object, zend_string } /* }}} */ -static zval *zend_closure_read_property(zval *object, zval *member, int type, void **cache_slot, zval *rv) /* {{{ */ +static ZEND_COLD zval *zend_closure_read_property(zval *object, zval *member, int type, void **cache_slot, zval *rv) /* {{{ */ { ZEND_CLOSURE_PROPERTY_ERROR(); return &EG(uninitialized_zval); } /* }}} */ -static void zend_closure_write_property(zval *object, zval *member, zval *value, void **cache_slot) /* {{{ */ +static ZEND_COLD zval *zend_closure_write_property(zval *object, zval *member, zval *value, void **cache_slot) /* {{{ */ { ZEND_CLOSURE_PROPERTY_ERROR(); + return &EG(error_zval); } /* }}} */ -static zval *zend_closure_get_property_ptr_ptr(zval *object, zval *member, int type, void **cache_slot) /* {{{ */ +static ZEND_COLD zval *zend_closure_get_property_ptr_ptr(zval *object, zval *member, int type, void **cache_slot) /* {{{ */ { ZEND_CLOSURE_PROPERTY_ERROR(); return NULL; } /* }}} */ -static int zend_closure_has_property(zval *object, zval *member, int has_set_exists, void **cache_slot) /* {{{ */ +static ZEND_COLD int zend_closure_has_property(zval *object, zval *member, int has_set_exists, void **cache_slot) /* {{{ */ { if (has_set_exists != ZEND_PROPERTY_EXISTS) { ZEND_CLOSURE_PROPERTY_ERROR(); @@ -426,7 +455,7 @@ static int zend_closure_has_property(zval *object, zval *member, int has_set_exi } /* }}} */ -static void zend_closure_unset_property(zval *object, zval *member, void **cache_slot) /* {{{ */ +static ZEND_COLD void zend_closure_unset_property(zval *object, zval *member, void **cache_slot) /* {{{ */ { ZEND_CLOSURE_PROPERTY_ERROR(); } @@ -439,10 +468,6 @@ static void zend_closure_free_storage(zend_object *object) /* {{{ */ zend_object_std_dtor(&closure->std); if (closure->func.type == ZEND_USER_FUNCTION) { - if (closure->func.op_array.fn_flags & ZEND_ACC_NO_RT_ARENA) { - efree(closure->func.op_array.run_time_cache); - closure->func.op_array.run_time_cache = NULL; - } destroy_op_array(&closure->func.op_array); } @@ -507,7 +532,8 @@ static HashTable *zend_closure_get_debug_info(zval *object, int *is_temp) /* {{{ if (closure->func.type == ZEND_USER_FUNCTION && closure->func.op_array.static_variables) { zval *var; - HashTable *static_variables = closure->func.op_array.static_variables; + HashTable *static_variables = + ZEND_MAP_PTR_GET(closure->func.op_array.static_variables_ptr); ZVAL_ARR(&val, zend_array_dup(static_variables)); zend_hash_update(debug_info, ZSTR_KNOWN(ZEND_STR_STATIC), &val); ZEND_HASH_FOREACH_VAL(Z_ARRVAL(val), var) { @@ -571,7 +597,7 @@ static HashTable *zend_closure_get_gc(zval *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) ? - closure->func.op_array.static_variables : NULL; + ZEND_MAP_PTR_GET(closure->func.op_array.static_variables_ptr) : NULL; } /* }}} */ @@ -666,28 +692,44 @@ ZEND_API void zend_create_closure(zval *res, zend_function *func, zend_class_ent if (func->type == ZEND_USER_FUNCTION) { memcpy(&closure->func, func, sizeof(zend_op_array)); 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); } + 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 (!closure->func.op_array.run_time_cache + if (!ZEND_MAP_PTR_GET(closure->func.op_array.run_time_cache) || func->common.scope != scope - || (func->common.fn_flags & ZEND_ACC_NO_RT_ARENA) + || (func->common.fn_flags & ZEND_ACC_HEAP_RT_CACHE) ) { - if (!func->op_array.run_time_cache && (func->common.fn_flags & ZEND_ACC_CLOSURE)) { + void *ptr; + + if (!ZEND_MAP_PTR_GET(func->op_array.run_time_cache) + && (func->common.fn_flags & ZEND_ACC_CLOSURE) + && (func->common.scope == scope || + !(func->common.fn_flags & ZEND_ACC_IMMUTABLE))) { /* If a real closure is used for the first time, we create a shared runtime cache * and remember which scope it is for. */ - func->common.scope = scope; - func->op_array.run_time_cache = zend_arena_alloc(&CG(arena), func->op_array.cache_size); - closure->func.op_array.run_time_cache = func->op_array.run_time_cache; + if (func->common.scope != scope) { + func->common.scope = scope; + } + closure->func.op_array.fn_flags &= ~ZEND_ACC_HEAP_RT_CACHE; + ptr = zend_arena_alloc(&CG(arena), func->op_array.cache_size); + ZEND_MAP_PTR_SET(func->op_array.run_time_cache, ptr); + ZEND_MAP_PTR_SET(closure->func.op_array.run_time_cache, ptr); } else { /* Otherwise, we use a non-shared runtime cache */ - closure->func.op_array.run_time_cache = emalloc(func->op_array.cache_size); - closure->func.op_array.fn_flags |= ZEND_ACC_NO_RT_ARENA; + closure->func.op_array.fn_flags |= ZEND_ACC_HEAP_RT_CACHE; + ptr = emalloc(sizeof(void*) + func->op_array.cache_size); + ZEND_MAP_PTR_INIT(closure->func.op_array.run_time_cache, ptr); + ptr = (char*)ptr + sizeof(void*); + ZEND_MAP_PTR_SET(closure->func.op_array.run_time_cache, ptr); } - memset(closure->func.op_array.run_time_cache, 0, func->op_array.cache_size); + memset(ptr, 0, func->op_array.cache_size); } if (closure->func.op_array.refcount) { (*closure->func.op_array.refcount)++; @@ -720,7 +762,8 @@ ZEND_API void zend_create_closure(zval *res, zend_function *func, zend_class_ent if (scope) { closure->func.common.fn_flags |= ZEND_ACC_PUBLIC; if (this_ptr && Z_TYPE_P(this_ptr) == IS_OBJECT && (closure->func.common.fn_flags & ZEND_ACC_STATIC) == 0) { - ZVAL_COPY(&closure->this_ptr, this_ptr); + Z_ADDREF_P(this_ptr); + ZVAL_OBJ(&closure->this_ptr, Z_OBJ_P(this_ptr)); } } } @@ -740,7 +783,7 @@ ZEND_API void zend_create_fake_closure(zval *res, zend_function *func, zend_clas void zend_closure_bind_var(zval *closure_zv, zend_string *var_name, zval *var) /* {{{ */ { zend_closure *closure = (zend_closure *) Z_OBJ_P(closure_zv); - HashTable *static_variables = closure->func.op_array.static_variables; + HashTable *static_variables = ZEND_MAP_PTR_GET(closure->func.op_array.static_variables_ptr); zend_hash_update(static_variables, var_name, var); } /* }}} */ @@ -748,19 +791,9 @@ void zend_closure_bind_var(zval *closure_zv, zend_string *var_name, zval *var) / void zend_closure_bind_var_ex(zval *closure_zv, uint32_t offset, zval *val) /* {{{ */ { zend_closure *closure = (zend_closure *) Z_OBJ_P(closure_zv); - HashTable *static_variables = closure->func.op_array.static_variables; + HashTable *static_variables = ZEND_MAP_PTR_GET(closure->func.op_array.static_variables_ptr); zval *var = (zval*)((char*)static_variables->arData + offset); zval_ptr_dtor(var); ZVAL_COPY_VALUE(var, val); } /* }}} */ - -/* - * Local variables: - * tab-width: 4 - * c-basic-offset: 4 - * indent-tabs-mode: t - * End: - * vim600: sw=4 ts=4 fdm=marker - * vim<600: sw=4 ts=4 - */ |