Commit 951680712b4 for php.net
commit 951680712b460be92c89f56046ae68db61fad490
Author: Tim Düsterhus <tim@bastelstu.be>
Date: Sun Jun 14 16:18:01 2026 +0200
zend_object_handlers: Replace function-like macros by (inline) functions (#22297)
`zend_free_trampoline()` requires access to the globals and thus
cannot easily be made an inline function without moving it to a
different (less-appropriate) header. Since trampolines should be
comparatively rare, having an extra function call here should be okay.
diff --git a/Zend/zend_object_handlers.c b/Zend/zend_object_handlers.c
index e7ddd466a51..9e993627a74 100644
--- a/Zend/zend_object_handlers.c
+++ b/Zend/zend_object_handlers.c
@@ -1825,6 +1825,18 @@ ZEND_API ZEND_ATTRIBUTE_NONNULL zend_function *zend_get_call_trampoline_func(
}
/* }}} */
+ZEND_API void zend_free_trampoline(zend_function *func)
+{
+ ZEND_ASSERT(func->common.fn_flags & ZEND_ACC_CALL_VIA_TRAMPOLINE);
+
+ if (func == &EG(trampoline)) {
+ EG(trampoline).common.attributes = NULL;
+ EG(trampoline).common.function_name = NULL;
+ } else {
+ efree(func);
+ }
+}
+
static ZEND_FUNCTION(zend_parent_hook_get_trampoline)
{
zend_object *obj = Z_PTR_P(ZEND_THIS);
diff --git a/Zend/zend_object_handlers.h b/Zend/zend_object_handlers.h
index d0dd804e8a4..8ed4803c3f0 100644
--- a/Zend/zend_object_handlers.h
+++ b/Zend/zend_object_handlers.h
@@ -236,11 +236,10 @@ struct _zend_object_handlers {
BEGIN_EXTERN_C()
extern const ZEND_API zend_object_handlers std_object_handlers;
-#define zend_get_std_object_handlers() \
- (&std_object_handlers)
-
-#define zend_get_function_root_class(fbc) \
- ((fbc)->common.prototype ? (fbc)->common.prototype->common.scope : (fbc)->common.scope)
+static zend_always_inline const zend_object_handlers *zend_get_std_object_handlers(void)
+{
+ return &std_object_handlers;
+}
#define ZEND_PROPERTY_ISSET 0x0 /* Property exists and is not NULL */
#define ZEND_PROPERTY_NOT_EMPTY ZEND_ISEMPTY /* Property is not empty */
@@ -290,18 +289,20 @@ static zend_always_inline HashTable *zend_std_get_properties_ex(zend_object *obj
/* Implements the fast path for array cast */
ZEND_API HashTable *zend_std_build_object_properties_array(zend_object *zobj);
-#define ZEND_STD_BUILD_OBJECT_PROPERTIES_ARRAY_COMPATIBLE(object) ( \
- /* We can use zend_std_build_object_properties_array() for objects \
- * without properties ht and with standard handlers */ \
- Z_OBJ_P(object)->properties == NULL \
- && Z_OBJ_HT_P(object)->get_properties_for == NULL \
- && Z_OBJ_HT_P(object)->get_properties == zend_std_get_properties \
- /* For initialized proxies we need to forward to the real instance */ \
- && ( \
- !zend_object_is_lazy_proxy(Z_OBJ_P(object)) \
- || !zend_lazy_object_initialized(Z_OBJ_P(object)) \
- ) \
-)
+static zend_always_inline bool ZEND_STD_BUILD_OBJECT_PROPERTIES_ARRAY_COMPATIBLE(const zval *zv) {
+ /* We can use zend_std_build_object_properties_array() for objects
+ * without properties ht and with standard handlers */
+ const zend_object *obj = Z_OBJ_P(zv);
+
+ return obj->properties == NULL
+ && obj->handlers->get_properties_for == NULL
+ && obj->handlers->get_properties == zend_std_get_properties
+ /* For initialized proxies we need to forward to the real instance */
+ && (
+ !zend_object_is_lazy_proxy(obj)
+ || !zend_lazy_object_initialized(obj)
+ );
+}
/* Handler for objects that cannot be meaningfully compared.
* Only objects with the same identity will be considered equal. */
@@ -312,6 +313,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_ATTRIBUTE_NONNULL zend_function *zend_get_call_trampoline_func(const zend_function *fbc, zend_string *method_name);
+ZEND_API void zend_free_trampoline(zend_function *func);
ZEND_API uint32_t *zend_get_property_guard(zend_object *zobj, zend_string *member);
@@ -335,20 +337,12 @@ ZEND_API bool ZEND_FASTCALL zend_asymmetric_property_has_set_access(const zend_p
void zend_object_handlers_startup(void);
-#define zend_release_properties(ht) do { \
- if (ht) { \
- zend_array_release(ht); \
- } \
-} while (0)
-
-#define zend_free_trampoline(func) do { \
- if ((func) == &EG(trampoline)) { \
- EG(trampoline).common.attributes = NULL; \
- EG(trampoline).common.function_name = NULL; \
- } else { \
- efree(func); \
- } \
- } while (0)
+static zend_always_inline void zend_release_properties(HashTable *ht)
+{
+ if (ht) {
+ zend_array_release(ht);
+ }
+}
/* Fallback to default comparison implementation if the arguments aren't both objects
* and have the same compare() handler. You'll likely want to use this unless you
diff --git a/Zend/zend_objects_API.h b/Zend/zend_objects_API.h
index 694ef398e37..434ac4499e7 100644
--- a/Zend/zend_objects_API.h
+++ b/Zend/zend_objects_API.h
@@ -136,6 +136,11 @@ static inline zend_property_info *zend_get_typed_property_info_for_slot(zend_obj
return NULL;
}
+static zend_always_inline zend_class_entry *zend_get_function_root_class(const zend_function *fbc)
+{
+ return fbc->common.prototype ? fbc->common.prototype->common.scope : fbc->common.scope;
+}
+
static zend_always_inline bool zend_check_method_accessible(const zend_function *fn, const zend_class_entry *scope)
{
if (!(fn->common.fn_flags & ZEND_ACC_PUBLIC)