diff --git a/Zend/zend_API.c b/Zend/zend_API.c index 43e21cafd564..825a0be975ab 100644 --- a/Zend/zend_API.c +++ b/Zend/zend_API.c @@ -4236,9 +4236,9 @@ ZEND_API bool zend_is_callable_at_frame( } case IS_OBJECT: - if (!Z_OBJ_HANDLER_P(callable, get_closure) || Z_OBJ_HANDLER_P(callable, get_closure)(Z_OBJ_P(callable), &fcc->calling_scope, &fcc->function_handler, &fcc->object, 1) == FAILURE) { + if (UNEXPECTED(!zend_get_closure(Z_OBJ_P(callable), &fcc->calling_scope, &fcc->function_handler, &fcc->object, true))) { if (error) *error = estrdup("no array or string given"); - return 0; + return false; } fcc->called_scope = fcc->calling_scope; fcc->closure = Z_OBJ_P(callable); diff --git a/Zend/zend_closures.c b/Zend/zend_closures.c index 4aa467315907..407b413531c5 100644 --- a/Zend/zend_closures.c +++ b/Zend/zend_closures.c @@ -44,7 +44,6 @@ typedef struct _zend_closure { ZEND_API zend_class_entry *zend_ce_closure; static zend_object_handlers closure_handlers; -static zend_result zend_closure_get_closure(zend_object *obj, zend_class_entry **ce_ptr, zend_function **fptr_ptr, zend_object **obj_ptr, bool check_only); static void zend_create_closure_ex(zval *res, zend_function *func, zend_class_entry *scope, zend_class_entry *called_scope, zend_object *this_ptr, bool is_fake, uint32_t flags); static inline uint32_t zend_closure_flags(const zend_closure *closure) @@ -68,11 +67,14 @@ ZEND_METHOD(Closure, __invoke) /* {{{ */ Z_PARAM_VARIADIC_WITH_NAMED(args, num_args, named_args) ZEND_PARSE_PARAMETERS_END(); - zend_fcall_info_cache fcc = { + zend_closure *closure = (zend_closure*)Z_OBJ_P(ZEND_THIS); + const zend_fcall_info_cache fcc = { + .function_handler = &closure->func, + .calling_scope = closure->called_scope, + .called_scope = closure->called_scope, + .object = closure->this_ptr, .closure = Z_OBJ_P(ZEND_THIS), }; - zend_closure_get_closure(Z_OBJ_P(ZEND_THIS), &fcc.calling_scope, &fcc.function_handler, &fcc.object, false); - fcc.called_scope = fcc.calling_scope; zend_call_known_fcc(&fcc, return_value, num_args, args, named_args); /* destruct the function also, then - we have allocated it in get_method */ @@ -647,6 +649,39 @@ static zend_result zend_closure_get_closure(zend_object *obj, zend_class_entry * } /* }}} */ +ZEND_API bool zend_get_closure(zend_object *obj, zend_class_entry **ce_ptr, zend_function **fptr_ptr, zend_object **this_ptr, bool check_only) { + /* Fast path for native closures */ + if (EXPECTED(obj->ce == zend_ce_closure)) { + zend_closure *closure = (zend_closure*)obj; + + *fptr_ptr = &closure->func; + *ce_ptr = closure->called_scope; + *this_ptr = closure->this_ptr; + return true; + } + + if (EXPECTED(obj->handlers->get_closure == zend_std_get_closure)) { + /* Inline zend_std_get_closure behaviour for fast path of __invoke() methods */ + zend_class_entry *ce = obj->ce; + zend_function *func = zend_hash_find_ex_ptr(&ce->function_table, ZSTR_KNOWN(ZEND_STR_MAGIC_INVOKE), /* known_hash */ true); + + if (UNEXPECTED(func == NULL)) { + return false; + } + *fptr_ptr = func; + *ce_ptr = ce; + *this_ptr = obj; + return true; + } + + if (UNEXPECTED(obj->handlers->get_closure == NULL)) { + return false; + } + + /* Currently only FFI implements a custom get_closure */ + return obj->handlers->get_closure(obj, ce_ptr, fptr_ptr, this_ptr, check_only) == SUCCESS; +} + /* *is_temp is int due to Object Handler API */ static HashTable *zend_closure_get_debug_info(zend_object *object, int *is_temp) /* {{{ */ { diff --git a/Zend/zend_closures.h b/Zend/zend_closures.h index c421c100833a..b5c00838e1ce 100644 --- a/Zend/zend_closures.h +++ b/Zend/zend_closures.h @@ -40,6 +40,7 @@ ZEND_API void zend_create_partial_closure(zval *res, zend_function *func, zend_c ZEND_API zend_function *zend_get_closure_invoke_method(zend_object *obj); ZEND_API const zend_function *zend_get_closure_method_def(zend_object *obj); ZEND_API zend_object* zend_get_closure_this_ptr(zval *obj); +ZEND_API bool zend_get_closure(zend_object *obj, zend_class_entry **ce_ptr, zend_function **fptr_ptr, zend_object **this_ptr, bool check_only); END_EXTERN_C() diff --git a/Zend/zend_execute.c b/Zend/zend_execute.c index 14a340ffee37..a9d2ce52ae55 100644 --- a/Zend/zend_execute.c +++ b/Zend/zend_execute.c @@ -5175,33 +5175,31 @@ static zend_never_inline zend_execute_data *zend_init_dynamic_call_object(zend_o zend_object *object; uint32_t call_info; - if (EXPECTED(function->handlers->get_closure) && - EXPECTED(function->handlers->get_closure(function, &called_scope, &fbc, &object, 0) == SUCCESS)) { - - object_or_called_scope = called_scope; - if (EXPECTED(fbc->common.fn_flags & ZEND_ACC_CLOSURE)) { - /* Delay closure destruction until its invocation */ - GC_ADDREF(ZEND_CLOSURE_OBJECT(fbc)); - ZEND_ASSERT(ZEND_ACC_FAKE_CLOSURE == ZEND_CALL_FAKE_CLOSURE); - call_info = ZEND_CALL_NESTED_FUNCTION | ZEND_CALL_DYNAMIC | ZEND_CALL_CLOSURE | - (fbc->common.fn_flags & ZEND_ACC_FAKE_CLOSURE); - if (object) { - call_info |= ZEND_CALL_HAS_THIS; - object_or_called_scope = object; - } - } else { - call_info = ZEND_CALL_NESTED_FUNCTION | ZEND_CALL_DYNAMIC; - if (object) { - call_info |= ZEND_CALL_RELEASE_THIS | ZEND_CALL_HAS_THIS; - GC_ADDREF(object); /* For $this pointer */ - object_or_called_scope = object; - } - } - } else { + if (UNEXPECTED(!zend_get_closure(function, &called_scope, &fbc, &object, false))) { zend_throw_error(NULL, "Object of type %s is not callable", ZSTR_VAL(function->ce->name)); return NULL; } + object_or_called_scope = called_scope; + if (EXPECTED(fbc->common.fn_flags & ZEND_ACC_CLOSURE)) { + /* Delay closure destruction until its invocation */ + GC_ADDREF(ZEND_CLOSURE_OBJECT(fbc)); + ZEND_ASSERT(ZEND_ACC_FAKE_CLOSURE == ZEND_CALL_FAKE_CLOSURE); + call_info = ZEND_CALL_NESTED_FUNCTION | ZEND_CALL_DYNAMIC | ZEND_CALL_CLOSURE | + (fbc->common.fn_flags & ZEND_ACC_FAKE_CLOSURE); + if (object) { + call_info |= ZEND_CALL_HAS_THIS; + object_or_called_scope = object; + } + } else { + call_info = ZEND_CALL_NESTED_FUNCTION | ZEND_CALL_DYNAMIC; + if (object) { + call_info |= ZEND_CALL_RELEASE_THIS | ZEND_CALL_HAS_THIS; + GC_ADDREF(object); /* For $this pointer */ + object_or_called_scope = object; + } + } + if (EXPECTED(fbc->type == ZEND_USER_FUNCTION) && UNEXPECTED(!RUN_TIME_CACHE(&fbc->op_array))) { init_func_run_time_cache(&fbc->op_array); } diff --git a/Zend/zend_object_handlers.c b/Zend/zend_object_handlers.c index 8ca6d212fd72..d707a7335f2b 100644 --- a/Zend/zend_object_handlers.c +++ b/Zend/zend_object_handlers.c @@ -2612,6 +2612,7 @@ ZEND_API zend_result zend_std_cast_object_tostring(zend_object *readobj, zval *w } /* }}} */ +/* Note: changes to zend_std_get_closure should be applied to zend_get_closure() in zend_closure.c */ ZEND_API zend_result zend_std_get_closure(zend_object *obj, zend_class_entry **ce_ptr, zend_function **fptr_ptr, zend_object **obj_ptr, bool check_only) /* {{{ */ { zend_class_entry *ce = obj->ce; diff --git a/ext/reflection/php_reflection.c b/ext/reflection/php_reflection.c index 370cfe86f952..57e313726647 100644 --- a/ext/reflection/php_reflection.c +++ b/ext/reflection/php_reflection.c @@ -1805,11 +1805,12 @@ ZEND_METHOD(ReflectionFunctionAbstract, getClosureCalledClass) if (!Z_ISUNDEF(intern->obj)) { zend_class_entry *called_scope; zend_function *closure_func; - zend_object *object; - if (Z_OBJ_HANDLER(intern->obj, get_closure) - && Z_OBJ_HANDLER(intern->obj, get_closure)(Z_OBJ(intern->obj), &called_scope, &closure_func, &object, true) == SUCCESS - && closure_func && (called_scope || closure_func->common.scope) - ) { + zend_object *this_ptr; + + if (zend_get_closure(Z_OBJ(intern->obj), &called_scope, &closure_func, &this_ptr, true) + /* if zend_get_closure succeeds we have a non-NULL closure_func */ + && (called_scope || closure_func->common.scope)) { + ZEND_ASSERT(closure_func); zend_reflection_class_factory(called_scope ? called_scope : closure_func->common.scope, return_value); } } @@ -2062,8 +2063,7 @@ ZEND_METHOD(ReflectionFunction, invoke) fcc.object = NULL; if (!Z_ISUNDEF(intern->obj)) { - Z_OBJ_HT(intern->obj)->get_closure( - Z_OBJ(intern->obj), &fcc.called_scope, &fcc.function_handler, &fcc.object, false); + zend_get_closure(Z_OBJ(intern->obj), &fcc.calling_scope, &fcc.function_handler, &fcc.object, false); } zval retval; @@ -2095,8 +2095,7 @@ ZEND_METHOD(ReflectionFunction, invokeArgs) fcc.object = NULL; if (!Z_ISUNDEF(intern->obj)) { - Z_OBJ_HT(intern->obj)->get_closure( - Z_OBJ(intern->obj), &fcc.called_scope, &fcc.function_handler, &fcc.object, false); + zend_get_closure(Z_OBJ(intern->obj), &fcc.called_scope, &fcc.function_handler, &fcc.object, false); } zval retval;