diff options
Diffstat (limited to 'Zend/zend_closures.c')
-rw-r--r-- | Zend/zend_closures.c | 161 |
1 files changed, 128 insertions, 33 deletions
diff --git a/Zend/zend_closures.c b/Zend/zend_closures.c index def114c4ec..bedf022a4b 100644 --- a/Zend/zend_closures.c +++ b/Zend/zend_closures.c @@ -52,17 +52,11 @@ static zend_object_handlers closure_handlers; ZEND_METHOD(Closure, __invoke) /* {{{ */ { zend_function *func = EX(func); - zval *arguments; + zval *arguments = ZEND_CALL_ARG(execute_data, 1); - arguments = emalloc(sizeof(zval) * ZEND_NUM_ARGS()); - if (zend_get_parameters_array_ex(ZEND_NUM_ARGS(), arguments) == FAILURE) { - efree(arguments); - zend_throw_error(NULL, "Cannot get arguments for calling closure"); - RETVAL_FALSE; - } else if (call_user_function_ex(CG(function_table), NULL, getThis(), return_value, ZEND_NUM_ARGS(), arguments, 1, NULL) == FAILURE) { + if (call_user_function_ex(CG(function_table), NULL, getThis(), return_value, ZEND_NUM_ARGS(), arguments, 1, NULL) == FAILURE) { RETVAL_FALSE; } - efree(arguments); /* destruct the function also, then - we have allocated it in get_method */ zend_string_release(func->internal_function.function_name); @@ -77,16 +71,17 @@ static zend_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; if (newthis) { if (func->common.fn_flags & ZEND_ACC_STATIC) { zend_error(E_WARNING, "Cannot bind an instance to a static closure"); return 0; } - if (func->type == ZEND_INTERNAL_FUNCTION && func->common.scope && + if (is_fake_closure && func->common.scope && !instanceof_function(Z_OBJCE_P(newthis), func->common.scope)) { /* Binding incompatible $this to an internal method is not supported. */ - zend_error(E_WARNING, "Cannot bind internal method %s::%s() to object of class %s", + zend_error(E_WARNING, "Cannot bind method %s::%s() to object of class %s", ZSTR_VAL(func->common.scope->name), ZSTR_VAL(func->common.function_name), ZSTR_VAL(Z_OBJCE_P(newthis)->name)); @@ -105,7 +100,7 @@ static zend_bool zend_valid_closure_binding( return 0; } - if ((func->common.fn_flags & ZEND_ACC_FAKE_CLOSURE) && scope != func->common.scope) { + if (is_fake_closure && scope != func->common.scope) { zend_error(E_WARNING, "Cannot rebind scope of closure created by ReflectionFunctionAbstract::getClosure()"); return 0; } @@ -240,6 +235,104 @@ ZEND_METHOD(Closure, bind) } /* }}} */ +static void zend_closure_call_magic(INTERNAL_FUNCTION_PARAMETERS) /* {{{ */ { + zend_fcall_info fci; + zend_fcall_info_cache fcc; + zval params[2]; + + memset(&fci, 0, sizeof(zend_fcall_info)); + memset(&fci, 0, sizeof(zend_fcall_info_cache)); + + fci.size = sizeof(zend_fcall_info); + fci.retval = return_value; + + fcc.initialized = 1; + fcc.function_handler = (zend_function *) EX(func)->common.arg_info; + fci.params = params; + fci.param_count = 2; + ZVAL_STR(&fci.params[0], EX(func)->common.function_name); + array_init(&fci.params[1]); + zend_copy_parameters_array(ZEND_NUM_ARGS(), &fci.params[1]); + + fci.object = Z_OBJ(EX(This)); + fcc.object = Z_OBJ(EX(This)); + fcc.calling_scope = zend_get_executed_scope(); + + zend_call_function(&fci, &fcc); + + zval_ptr_dtor(&fci.params[0]); + zval_ptr_dtor(&fci.params[1]); +} +/* }}} */ + +static int zend_create_closure_from_callable(zval *return_value, zval *callable, char **error) /* {{{ */ { + zend_fcall_info_cache fcc; + zend_function *mptr; + zval instance; + zend_internal_function call; + + if (!zend_is_callable_ex(callable, NULL, 0, NULL, &fcc, error)) { + return FAILURE; + } + + mptr = fcc.function_handler; + if (mptr->common.fn_flags & ZEND_ACC_CALL_VIA_TRAMPOLINE) { + memset(&call, 0, sizeof(zend_internal_function)); + + call.type = ZEND_INTERNAL_FUNCTION; + call.handler = zend_closure_call_magic; + call.function_name = mptr->common.function_name; + call.arg_info = (zend_internal_arg_info *) mptr->common.prototype; + call.scope = mptr->common.scope; + + zend_free_trampoline(mptr); + mptr = (zend_function *) &call; + } + + if (fcc.object) { + ZVAL_OBJ(&instance, fcc.object); + zend_create_fake_closure(return_value, mptr, mptr->common.scope, fcc.called_scope, &instance); + } else { + zend_create_fake_closure(return_value, mptr, mptr->common.scope, fcc.called_scope, NULL); + } + + return SUCCESS; +} +/* }}} */ + +/* {{{ proto Closure Closure::fromCallable(callable callable) + Create a closure from a callable using the current scope. */ +ZEND_METHOD(Closure, fromCallable) +{ + zval *callable; + int success; + char *error = NULL; + + if (zend_parse_parameters(ZEND_NUM_ARGS(), "z", &callable) == FAILURE) { + return; + } + + if (Z_TYPE_P(callable) == IS_OBJECT && instanceof_function(Z_OBJCE_P(callable), zend_ce_closure)) { + /* It's already a closure */ + RETURN_ZVAL(callable, 1, 0); + } + + /* create closure as if it were called from parent scope */ + EG(current_execute_data) = EX(prev_execute_data); + success = zend_create_closure_from_callable(return_value, callable, &error); + EG(current_execute_data) = execute_data; + + if (success == FAILURE || error) { + if (error) { + zend_throw_exception_ex(zend_ce_type_error, 0, "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"); + } + } +} +/* }}} */ + static ZEND_COLD zend_function *zend_closure_get_constructor(zend_object *object) /* {{{ */ { zend_throw_error(NULL, "Instantiation of 'Closure' is not allowed"); @@ -276,7 +369,7 @@ ZEND_API zend_function *zend_get_closure_invoke_method(zend_object *object) /* { invoke->internal_function.handler = ZEND_MN(Closure___invoke); invoke->internal_function.module = 0; invoke->internal_function.scope = zend_ce_closure; - invoke->internal_function.function_name = zend_string_init(ZEND_INVOKE_FUNC_NAME, sizeof(ZEND_INVOKE_FUNC_NAME)-1, 0); + invoke->internal_function.function_name = CG(known_strings)[ZEND_STR_MAGIC_INVOKE]; return invoke; } /* }}} */ @@ -387,23 +480,16 @@ static zend_object *zend_closure_clone(zval *zobject) /* {{{ */ int zend_closure_get_closure(zval *obj, zend_class_entry **ce_ptr, zend_function **fptr_ptr, zend_object **obj_ptr) /* {{{ */ { - zend_closure *closure; - - if (Z_TYPE_P(obj) != IS_OBJECT) { - return FAILURE; - } - - closure = (zend_closure *)Z_OBJ_P(obj); + zend_closure *closure = (zend_closure *)Z_OBJ_P(obj); *fptr_ptr = &closure->func; *ce_ptr = closure->called_scope; - if (obj_ptr) { - if (Z_TYPE(closure->this_ptr) != IS_UNDEF) { - *obj_ptr = Z_OBJ(closure->this_ptr); - } else { - *obj_ptr = NULL; - } + if (Z_TYPE(closure->this_ptr) != IS_UNDEF) { + *obj_ptr = Z_OBJ(closure->this_ptr); + } else { + *obj_ptr = NULL; } + return SUCCESS; } /* }}} */ @@ -423,12 +509,12 @@ 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) { HashTable *static_variables = closure->func.op_array.static_variables; ZVAL_ARR(&val, zend_array_dup(static_variables)); - zend_hash_str_update(debug_info, "static", sizeof("static")-1, &val); + zend_hash_update(debug_info, CG(known_strings)[ZEND_STR_STATIC], &val); } if (Z_TYPE(closure->this_ptr) != IS_UNDEF) { Z_ADDREF(closure->this_ptr); - zend_hash_str_update(debug_info, "this", sizeof("this")-1, &closure->this_ptr); + zend_hash_update(debug_info, CG(known_strings)[ZEND_STR_THIS], &closure->this_ptr); } if (arg_info && @@ -501,11 +587,16 @@ ZEND_BEGIN_ARG_INFO_EX(arginfo_closure_call, 0, 0, 1) ZEND_ARG_VARIADIC_INFO(0, parameters) ZEND_END_ARG_INFO() +ZEND_BEGIN_ARG_INFO_EX(arginfo_closure_fromcallable, 0, 0, 1) + ZEND_ARG_INFO(0, callable) +ZEND_END_ARG_INFO() + static const zend_function_entry closure_functions[] = { ZEND_ME(Closure, __construct, NULL, ZEND_ACC_PRIVATE) ZEND_ME(Closure, bind, arginfo_closure_bind, ZEND_ACC_PUBLIC|ZEND_ACC_STATIC) ZEND_MALIAS(Closure, bindTo, bind, arginfo_closure_bindto, ZEND_ACC_PUBLIC) ZEND_ME(Closure, call, arginfo_closure_call, ZEND_ACC_PUBLIC) + ZEND_ME(Closure, fromCallable, arginfo_closure_fromcallable, ZEND_ACC_PUBLIC|ZEND_ACC_STATIC) ZEND_FE_END }; @@ -522,7 +613,6 @@ void zend_register_closure_ce(void) /* {{{ */ memcpy(&closure_handlers, zend_get_std_object_handlers(), sizeof(zend_object_handlers)); closure_handlers.free_obj = zend_closure_free_storage; - closure_handlers.clone_obj = NULL; closure_handlers.get_constructor = zend_closure_get_constructor; closure_handlers.get_method = zend_closure_get_method; closure_handlers.write_property = zend_closure_write_property; @@ -566,11 +656,8 @@ ZEND_API void zend_create_closure(zval *res, zend_function *func, zend_class_ent closure->func.common.prototype = (zend_function*)closure; closure->func.common.fn_flags |= ZEND_ACC_CLOSURE; if (closure->func.op_array.static_variables) { - HashTable *static_variables = closure->func.op_array.static_variables; - - ALLOC_HASHTABLE(closure->func.op_array.static_variables); - zend_hash_init(closure->func.op_array.static_variables, zend_hash_num_elements(static_variables), NULL, ZVAL_PTR_DTOR, 0); - zend_hash_apply_with_arguments(static_variables, zval_copy_static_var, 1, closure->func.op_array.static_variables); + closure->func.op_array.static_variables = + zend_array_dup(closure->func.op_array.static_variables); } if (UNEXPECTED(!closure->func.op_array.run_time_cache)) { closure->func.op_array.run_time_cache = func->op_array.run_time_cache = zend_arena_alloc(&CG(arena), func->op_array.cache_size); @@ -625,6 +712,14 @@ 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; + zend_hash_update(static_variables, var_name, var); +} +/* }}} */ + /* * Local variables: * tab-width: 4 |