Add Closure::fromCallable(). #1906
| + call.arg_info = (zend_internal_arg_info *) mptr->common.prototype; | ||
| + call.scope = mptr->common.scope; | ||
| + | ||
| + zend_free_trampoline(mptr); | ||
| + mptr = (zend_function *) &call; | ||
| + } | ||
| + | ||
| + ZVAL_OBJ(&instance, fcc.object); | ||
| + zend_create_closure(return_value, mptr, mptr->common.scope, fcc.object ? fcc.object->ce : NULL, fcc.object ? &instance : NULL); | ||
| + | ||
| + return SUCCESS; | ||
| +} | ||
| + | ||
| + | ||
| +/* {{{ proto Closure Closure::fromCallable(callable callable) | ||
| + Create a closure from a callabl using the current scope. */ |
|
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
|
| + | ||
| + if (!zend_is_callable_ex(callable, NULL, 0, NULL, &fcc, error)) { | ||
| + return FAILURE; | ||
| + } | ||
| + | ||
| + mptr = fcc.function_handler; | ||
| + if (mptr == NULL) { | ||
| + return FAILURE; | ||
| + } | ||
| + | ||
| + if (mptr->common.fn_flags & ZEND_ACC_CALL_VIA_TRAMPOLINE) { | ||
| + zend_internal_function call; | ||
| + memset(&call, 0, sizeof(zend_internal_function)); | ||
| + | ||
| + call.type = ZEND_INTERNAL_FUNCTION; | ||
| + call.handler = zend_closure_call_magic; |
|
Call through zend_call_function() is more expensive than through TRAMPOLINE. Dmitry - I don't really understand trampolines. So long as this PR is acceptable and the tests all pass, is it okay for you to do any performance improvements you think are good, after this PR is accepted and closed? Yeah. Commit this. I'll take a look, if it's possible to optimise this using trampolines later.
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
|
| @@ -6793,6 +6806,10 @@ static const zend_function_entry reflection_zend_extension_functions[] = { | ||
| }; | ||
| /* }}} */ | ||
| +ZEND_BEGIN_ARG_INFO_EX(arginfo_closure, 0, 0, 1) | ||
| + ZEND_ARG_INFO(0, callable) | ||
| +ZEND_END_ARG_INFO() | ||
| + |
|
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
|
| @@ -0,0 +1,48 @@ | ||
| +--TEST-- | ||
| +Imagick::readImage test |
|
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
|
| + | ||
| + | ||
| +/* {{{ 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 |
|
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
|
| + zval_ptr_dtor(&fci.params[1]); | ||
| + efree(fci.params); | ||
| +} | ||
| + | ||
| + | ||
| +static int zend_create_closure_from_callable(zval *return_value, zval *callable, char **error) { | ||
| + zend_fcall_info_cache fcc; | ||
| + zend_function *mptr; | ||
| + zval instance; | ||
| + | ||
| + if (!zend_is_callable_ex(callable, NULL, 0, NULL, &fcc, error)) { | ||
| + return FAILURE; | ||
| + } | ||
| + | ||
| + mptr = fcc.function_handler; | ||
| + if (mptr == NULL) { |
|
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
|
| + if (mptr->common.fn_flags & ZEND_ACC_CALL_VIA_TRAMPOLINE) { | ||
| + zend_internal_function call; | ||
| + 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; | ||
| + } | ||
| + | ||
| + ZVAL_OBJ(&instance, fcc.object); | ||
| + zend_create_closure(return_value, mptr, mptr->common.scope, fcc.object ? fcc.object->ce : NULL, fcc.object ? &instance : NULL); |
|
This should use I'm not sure that setting called_scope to NULL for static calls is what you want to do. Please check how something like this behaves: class Foo {
const BAR = 1;
public static function method() {
return static::BAR;
}
}
var_dump(Closure::fromCallable(['Foo', 'method'])());
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
|
| + if (mptr == NULL) { | ||
| + return FAILURE; | ||
| + } | ||
| + | ||
| + if (mptr->common.fn_flags & ZEND_ACC_CALL_VIA_TRAMPOLINE) { | ||
| + zend_internal_function call; | ||
| + 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; |
|
This retains a reference to a stack value outside the block it was declared in.
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
|
| + return FAILURE; | ||
| + } | ||
| + | ||
| + mptr = fcc.function_handler; | ||
| + if (mptr == NULL) { | ||
| + return FAILURE; | ||
| + } | ||
| + | ||
| + if (mptr->common.fn_flags & ZEND_ACC_CALL_VIA_TRAMPOLINE) { | ||
| + zend_internal_function call; | ||
| + 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; |
|
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
|
| @@ -235,6 +235,105 @@ ZEND_METHOD(Closure, bind) | ||
| } | ||
| /* }}} */ | ||
| +static void zend_closure_call_magic(INTERNAL_FUNCTION_PARAMETERS) { | ||
| + zend_fcall_info fci; | ||
| + zend_fcall_info_cache fcc; | ||
| + | ||
| + 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 = (zval*) emalloc(sizeof(zval) * 2); |
|
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
|
| + fci.params = (zval*) emalloc(sizeof(zval) * 2); | ||
| + 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]); | ||
| + efree(fci.params); | ||
| +} |
|
Please add a test for a Also generally, whatever issue this code is solving, does the same issue exist for ReflectionMethod::getClosure() and if not, how did they solve it?
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
|
Add the ability to create closures from callable as part of RFC: https://wiki.php.net/rfc/closurefromcallable