From 0e5c2575ae9977300a9d2808962b582565aaf198 Mon Sep 17 00:00:00 2001 From: Gina Peter Banyard Date: Sat, 25 Oct 2025 22:41:15 +0100 Subject: [PATCH 1/2] zend_object_handler.c: call zend_get_call_trampoline_func() directly Abstracting away the bool parameter is not that useful, and doesn't make the code more legible. So just get rid of them and call the function with the boolean parameter directly. --- Zend/zend_object_handlers.c | 22 +++++----------------- 1 file changed, 5 insertions(+), 17 deletions(-) diff --git a/Zend/zend_object_handlers.c b/Zend/zend_object_handlers.c index 68daa207ee6e4..2fcfe080b8dcc 100644 --- a/Zend/zend_object_handlers.c +++ b/Zend/zend_object_handlers.c @@ -1828,12 +1828,6 @@ ZEND_API zend_function *zend_get_property_hook_trampoline( return func; } -static zend_always_inline zend_function *zend_get_user_call_function(zend_class_entry *ce, zend_string *method_name) /* {{{ */ -{ - return zend_get_call_trampoline_func(ce, method_name, false); -} -/* }}} */ - ZEND_API ZEND_COLD zend_never_inline void zend_bad_method_call(const zend_function *fbc, const zend_string *method_name, const zend_class_entry *scope) /* {{{ */ { zend_throw_error(NULL, "Call to %s method %s::%s() from %s%s", @@ -1875,7 +1869,7 @@ ZEND_API zend_function *zend_std_get_method(zend_object **obj_ptr, zend_string * ZSTR_ALLOCA_FREE(lc_method_name, use_heap); } if (zobj->ce->__call) { - return zend_get_user_call_function(zobj->ce, method_name); + return zend_get_call_trampoline_func(zobj->ce, method_name, false); } else { return NULL; } @@ -1901,7 +1895,7 @@ ZEND_API zend_function *zend_std_get_method(zend_object **obj_ptr, zend_string * if (UNEXPECTED(fbc->op_array.fn_flags & ZEND_ACC_PRIVATE) || UNEXPECTED(!zend_check_protected(zend_get_function_root_class(fbc), scope))) { if (zobj->ce->__call) { - fbc = zend_get_user_call_function(zobj->ce, method_name); + fbc = zend_get_call_trampoline_func(zobj->ce, method_name, false); } else { zend_bad_method_call(fbc, method_name, scope); fbc = NULL; @@ -1922,14 +1916,8 @@ ZEND_API zend_function *zend_std_get_method(zend_object **obj_ptr, zend_string * } /* }}} */ -static zend_always_inline zend_function *zend_get_user_callstatic_function(zend_class_entry *ce, zend_string *method_name) /* {{{ */ -{ - return zend_get_call_trampoline_func(ce, method_name, true); -} -/* }}} */ - static zend_always_inline zend_function *get_static_method_fallback( - zend_class_entry *ce, zend_string *function_name) + const zend_class_entry *ce, zend_string *function_name) { zend_object *object; if (ce->__call && @@ -1939,9 +1927,9 @@ static zend_always_inline zend_function *get_static_method_fallback( * see: tests/classes/__call_004.phpt */ ZEND_ASSERT(object->ce->__call); - return zend_get_user_call_function(object->ce, function_name); + return zend_get_call_trampoline_func(object->ce, function_name, false); } else if (ce->__callstatic) { - return zend_get_user_callstatic_function(ce, function_name); + return zend_get_call_trampoline_func(ce, function_name, true); } else { return NULL; } From 3012e04c2a24a8ff487e629ae13c7e0271aa53fe Mon Sep 17 00:00:00 2001 From: Gina Peter Banyard Date: Sat, 25 Oct 2025 23:25:34 +0100 Subject: [PATCH 2/2] Pass zend_function* directly to zend_get_call_trampoline_func() Every single callsite already checks if it set to call this function, so no need to duplicate the work. We can also remove the is_static bool parameter as it can be derived from the fn_flags. --- UPGRADING.INTERNALS | 2 ++ Zend/zend_API.c | 2 +- Zend/zend_object_handlers.c | 19 +++++++------------ Zend/zend_object_handlers.h | 2 +- 4 files changed, 11 insertions(+), 14 deletions(-) diff --git a/UPGRADING.INTERNALS b/UPGRADING.INTERNALS index 3869677779716..385ad0445cc99 100644 --- a/UPGRADING.INTERNALS +++ b/UPGRADING.INTERNALS @@ -41,6 +41,8 @@ PHP 8.6 INTERNALS UPGRADE NOTES stored instead. . The zend_active_function{_ex}() functions now return a const zend_function pointer. + . The zend_get_call_trampoline_func() API now takes the __call or + __callStatic zend_function* instead of a CE and a boolean argument. ======================== 2. Build system changes diff --git a/Zend/zend_API.c b/Zend/zend_API.c index 7897ac1473601..06486eafdc90b 100644 --- a/Zend/zend_API.c +++ b/Zend/zend_API.c @@ -3951,7 +3951,7 @@ static zend_always_inline bool zend_is_callable_check_func(zval *callable, const get_function_via_handler: if (fcc->object && fcc->calling_scope == ce_org) { if (strict_class && ce_org->__call) { - fcc->function_handler = zend_get_call_trampoline_func(ce_org, mname, 0); + fcc->function_handler = zend_get_call_trampoline_func(ce_org->__call, mname); call_via_handler = 1; retval = true; } else { diff --git a/Zend/zend_object_handlers.c b/Zend/zend_object_handlers.c index 2fcfe080b8dcc..ec628dbfc9fe9 100644 --- a/Zend/zend_object_handlers.c +++ b/Zend/zend_object_handlers.c @@ -1674,19 +1674,17 @@ ZEND_API bool zend_check_protected(const zend_class_entry *ce, const zend_class_ } /* }}} */ -ZEND_API zend_function *zend_get_call_trampoline_func(const zend_class_entry *ce, zend_string *method_name, bool is_static) /* {{{ */ +ZEND_API ZEND_ATTRIBUTE_NONNULL zend_function *zend_get_call_trampoline_func( + const zend_function *fbc, zend_string *method_name) /* {{{ */ { size_t mname_len; zend_op_array *func; - zend_function *fbc = is_static ? ce->__callstatic : ce->__call; /* We use non-NULL value to avoid useless run_time_cache allocation. * The low bit must be zero, to not be interpreted as a MAP_PTR offset. */ static const void *dummy = (void*)(intptr_t)2; static const zend_arg_info arg_info[1] = {{0}}; - ZEND_ASSERT(fbc); - if (EXPECTED(EG(trampoline).common.function_name == NULL)) { func = &EG(trampoline).op_array; } else { @@ -1700,13 +1698,10 @@ ZEND_API zend_function *zend_get_call_trampoline_func(const zend_class_entry *ce func->fn_flags = ZEND_ACC_CALL_VIA_TRAMPOLINE | ZEND_ACC_PUBLIC | ZEND_ACC_VARIADIC - | (fbc->common.fn_flags & (ZEND_ACC_RETURN_REFERENCE|ZEND_ACC_ABSTRACT|ZEND_ACC_DEPRECATED|ZEND_ACC_NODISCARD)); + | (fbc->common.fn_flags & (ZEND_ACC_RETURN_REFERENCE|ZEND_ACC_ABSTRACT|ZEND_ACC_DEPRECATED|ZEND_ACC_NODISCARD|ZEND_ACC_STATIC)); func->fn_flags2 = 0; /* Attributes outlive the trampoline because they are created by the compiler. */ func->attributes = fbc->common.attributes; - if (is_static) { - func->fn_flags |= ZEND_ACC_STATIC; - } func->opcodes = &EG(call_trampoline_op); ZEND_MAP_PTR_INIT(func->run_time_cache, (void**)dummy); func->scope = fbc->common.scope; @@ -1869,7 +1864,7 @@ ZEND_API zend_function *zend_std_get_method(zend_object **obj_ptr, zend_string * ZSTR_ALLOCA_FREE(lc_method_name, use_heap); } if (zobj->ce->__call) { - return zend_get_call_trampoline_func(zobj->ce, method_name, false); + return zend_get_call_trampoline_func(zobj->ce->__call, method_name); } else { return NULL; } @@ -1895,7 +1890,7 @@ ZEND_API zend_function *zend_std_get_method(zend_object **obj_ptr, zend_string * if (UNEXPECTED(fbc->op_array.fn_flags & ZEND_ACC_PRIVATE) || UNEXPECTED(!zend_check_protected(zend_get_function_root_class(fbc), scope))) { if (zobj->ce->__call) { - fbc = zend_get_call_trampoline_func(zobj->ce, method_name, false); + fbc = zend_get_call_trampoline_func(zobj->ce->__call, method_name); } else { zend_bad_method_call(fbc, method_name, scope); fbc = NULL; @@ -1927,9 +1922,9 @@ static zend_always_inline zend_function *get_static_method_fallback( * see: tests/classes/__call_004.phpt */ ZEND_ASSERT(object->ce->__call); - return zend_get_call_trampoline_func(object->ce, function_name, false); + return zend_get_call_trampoline_func(object->ce->__call, function_name); } else if (ce->__callstatic) { - return zend_get_call_trampoline_func(ce, function_name, true); + return zend_get_call_trampoline_func(ce->__callstatic, function_name); } else { return NULL; } diff --git a/Zend/zend_object_handlers.h b/Zend/zend_object_handlers.h index 84d0b57d7aa28..0b255f4304280 100644 --- a/Zend/zend_object_handlers.h +++ b/Zend/zend_object_handlers.h @@ -312,7 +312,7 @@ ZEND_API bool zend_check_protected(const zend_class_entry *ce, const zend_class_ ZEND_API zend_result zend_check_property_access(const zend_object *zobj, zend_string *prop_info_name, bool is_dynamic); -ZEND_API zend_function *zend_get_call_trampoline_func(const zend_class_entry *ce, zend_string *method_name, bool is_static); +ZEND_API ZEND_ATTRIBUTE_NONNULL zend_function *zend_get_call_trampoline_func(const zend_function *fbc, zend_string *method_name); ZEND_API uint32_t *zend_get_property_guard(zend_object *zobj, zend_string *member);