From 54ad6ef0ea931959af2e18fac0bae470737c0e8b Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Tim=20D=C3=BCsterhus?= Date: Tue, 5 May 2026 19:36:37 +0200 Subject: [PATCH] zend_types: Move `zend_gc_*()` functions above the wrapper macros That allows reliable usage of the `GC_*()` macros in (inline) functions in zend_types.h. --- Zend/zend_types.h | 220 +++++++++++++++++++++++----------------------- 1 file changed, 110 insertions(+), 110 deletions(-) diff --git a/Zend/zend_types.h b/Zend/zend_types.h index cfff3b942c4b..f3039d202519 100644 --- a/Zend/zend_types.h +++ b/Zend/zend_types.h @@ -713,6 +713,116 @@ static zend_always_inline uint8_t zval_get_type(const zval* pz) { #define Z_TYPE_FLAGS_SHIFT 8 #define Z_TYPE_INFO_EXTRA_SHIFT 16 +/* zval_gc_flags(zval.value->gc.u.type_info) (common flags) */ +#define GC_NOT_COLLECTABLE (1<<4) +#define GC_PROTECTED (1<<5) /* used for recursion detection */ +#define GC_IMMUTABLE (1<<6) /* can't be changed in place */ +#define GC_PERSISTENT (1<<7) /* allocated using malloc */ +#define GC_PERSISTENT_LOCAL (1<<8) /* persistent, but thread-local */ + +#define GC_TYPE_MASK 0x0000000f +#define GC_FLAGS_MASK 0x000003f0 +#define GC_INFO_MASK 0xfffffc00 +#define GC_FLAGS_SHIFT 0 +#define GC_INFO_SHIFT 10 + +static zend_always_inline uint8_t zval_gc_type(uint32_t gc_type_info) { + return (gc_type_info & GC_TYPE_MASK); +} + +static zend_always_inline uint32_t zval_gc_flags(uint32_t gc_type_info) { + return (gc_type_info >> GC_FLAGS_SHIFT) & (GC_FLAGS_MASK >> GC_FLAGS_SHIFT); +} + +static zend_always_inline uint32_t zval_gc_info(uint32_t gc_type_info) { + return (gc_type_info >> GC_INFO_SHIFT); +} + +#define GC_TYPE_INFO(p) (p)->gc.u.type_info +#define GC_TYPE(p) zval_gc_type(GC_TYPE_INFO(p)) +#define GC_FLAGS(p) zval_gc_flags(GC_TYPE_INFO(p)) +#define GC_INFO(p) zval_gc_info(GC_TYPE_INFO(p)) + +#define GC_ADD_FLAGS(p, flags) do { \ + GC_TYPE_INFO(p) |= (flags) << GC_FLAGS_SHIFT; \ + } while (0) +#define GC_DEL_FLAGS(p, flags) do { \ + GC_TYPE_INFO(p) &= ~((flags) << GC_FLAGS_SHIFT); \ + } while (0) + +#ifndef ZEND_RC_DEBUG +# define ZEND_RC_DEBUG 0 +#endif + +#if ZEND_RC_DEBUG +extern ZEND_API bool zend_rc_debug; +/* The GC_PERSISTENT flag is reused for IS_OBJ_WEAKLY_REFERENCED on objects. + * Skip checks for OBJECT/NULL type to avoid interpreting the flag incorrectly. */ +# define ZEND_RC_MOD_CHECK(p) do { \ + if (zend_rc_debug) { \ + uint8_t type = zval_gc_type((p)->u.type_info); \ + if (type != IS_OBJECT && type != IS_NULL) { \ + ZEND_ASSERT(!(zval_gc_flags((p)->u.type_info) & GC_IMMUTABLE)); \ + ZEND_ASSERT((zval_gc_flags((p)->u.type_info) & (GC_PERSISTENT|GC_PERSISTENT_LOCAL)) != GC_PERSISTENT); \ + } \ + } \ + } while (0) +# define GC_MAKE_PERSISTENT_LOCAL(p) do { \ + GC_ADD_FLAGS(p, GC_PERSISTENT_LOCAL); \ + } while (0) +#else +# define ZEND_RC_MOD_CHECK(p) \ + do { } while (0) +# define GC_MAKE_PERSISTENT_LOCAL(p) \ + do { } while (0) +#endif + +static zend_always_inline uint32_t zend_gc_refcount(const zend_refcounted_h *p) { + return p->refcount; +} + +static zend_always_inline uint32_t zend_gc_set_refcount(zend_refcounted_h *p, uint32_t rc) { + p->refcount = rc; + return p->refcount; +} + +static zend_always_inline uint32_t zend_gc_addref(zend_refcounted_h *p) { + ZEND_RC_MOD_CHECK(p); + return ++(p->refcount); +} + +static zend_always_inline void zend_gc_try_addref(zend_refcounted_h *p) { + if (!(p->u.type_info & GC_IMMUTABLE)) { + ZEND_RC_MOD_CHECK(p); + ++p->refcount; + } +} + +static zend_always_inline void zend_gc_try_delref(zend_refcounted_h *p) { + if (!(p->u.type_info & GC_IMMUTABLE)) { + ZEND_RC_MOD_CHECK(p); + --p->refcount; + } +} + +static zend_always_inline uint32_t zend_gc_delref(zend_refcounted_h *p) { + ZEND_ASSERT(p->refcount > 0); + ZEND_RC_MOD_CHECK(p); + return --(p->refcount); +} + +static zend_always_inline uint32_t zend_gc_addref_ex(zend_refcounted_h *p, uint32_t rc) { + ZEND_RC_MOD_CHECK(p); + p->refcount += rc; + return p->refcount; +} + +static zend_always_inline uint32_t zend_gc_delref_ex(zend_refcounted_h *p, uint32_t rc) { + ZEND_RC_MOD_CHECK(p); + p->refcount -= rc; + return p->refcount; +} + #define GC_REFCOUNT(p) zend_gc_refcount(&(p)->gc) #define GC_SET_REFCOUNT(p, rc) zend_gc_set_refcount(&(p)->gc, rc) #define GC_ADDREF(p) zend_gc_addref(&(p)->gc) @@ -754,36 +864,6 @@ static zend_always_inline uint8_t zval_get_type(const zval* pz) { } \ } while (0) -#define GC_TYPE_MASK 0x0000000f -#define GC_FLAGS_MASK 0x000003f0 -#define GC_INFO_MASK 0xfffffc00 -#define GC_FLAGS_SHIFT 0 -#define GC_INFO_SHIFT 10 - -static zend_always_inline uint8_t zval_gc_type(uint32_t gc_type_info) { - return (gc_type_info & GC_TYPE_MASK); -} - -static zend_always_inline uint32_t zval_gc_flags(uint32_t gc_type_info) { - return (gc_type_info >> GC_FLAGS_SHIFT) & (GC_FLAGS_MASK >> GC_FLAGS_SHIFT); -} - -static zend_always_inline uint32_t zval_gc_info(uint32_t gc_type_info) { - return (gc_type_info >> GC_INFO_SHIFT); -} - -#define GC_TYPE_INFO(p) (p)->gc.u.type_info -#define GC_TYPE(p) zval_gc_type(GC_TYPE_INFO(p)) -#define GC_FLAGS(p) zval_gc_flags(GC_TYPE_INFO(p)) -#define GC_INFO(p) zval_gc_info(GC_TYPE_INFO(p)) - -#define GC_ADD_FLAGS(p, flags) do { \ - GC_TYPE_INFO(p) |= (flags) << GC_FLAGS_SHIFT; \ - } while (0) -#define GC_DEL_FLAGS(p, flags) do { \ - GC_TYPE_INFO(p) &= ~((flags) << GC_FLAGS_SHIFT); \ - } while (0) - #define Z_GC_TYPE(zval) GC_TYPE(Z_COUNTED(zval)) #define Z_GC_TYPE_P(zval_p) Z_GC_TYPE(*(zval_p)) @@ -795,13 +875,6 @@ static zend_always_inline uint32_t zval_gc_info(uint32_t gc_type_info) { #define Z_GC_TYPE_INFO(zval) GC_TYPE_INFO(Z_COUNTED(zval)) #define Z_GC_TYPE_INFO_P(zval_p) Z_GC_TYPE_INFO(*(zval_p)) -/* zval_gc_flags(zval.value->gc.u.type_info) (common flags) */ -#define GC_NOT_COLLECTABLE (1<<4) -#define GC_PROTECTED (1<<5) /* used for recursion detection */ -#define GC_IMMUTABLE (1<<6) /* can't be changed in place */ -#define GC_PERSISTENT (1<<7) /* allocated using malloc */ -#define GC_PERSISTENT_LOCAL (1<<8) /* persistent, but thread-local */ - #define GC_NULL (IS_NULL | (GC_NOT_COLLECTABLE << GC_FLAGS_SHIFT)) #define GC_STRING (IS_STRING | (GC_NOT_COLLECTABLE << GC_FLAGS_SHIFT)) #define GC_ARRAY IS_ARRAY @@ -1299,79 +1372,6 @@ static zend_always_inline uint32_t zval_gc_info(uint32_t gc_type_info) { #define Z_TRY_ADDREF(z) Z_TRY_ADDREF_P(&(z)) #define Z_TRY_DELREF(z) Z_TRY_DELREF_P(&(z)) -#ifndef ZEND_RC_DEBUG -# define ZEND_RC_DEBUG 0 -#endif - -#if ZEND_RC_DEBUG -extern ZEND_API bool zend_rc_debug; -/* The GC_PERSISTENT flag is reused for IS_OBJ_WEAKLY_REFERENCED on objects. - * Skip checks for OBJECT/NULL type to avoid interpreting the flag incorrectly. */ -# define ZEND_RC_MOD_CHECK(p) do { \ - if (zend_rc_debug) { \ - uint8_t type = zval_gc_type((p)->u.type_info); \ - if (type != IS_OBJECT && type != IS_NULL) { \ - ZEND_ASSERT(!(zval_gc_flags((p)->u.type_info) & GC_IMMUTABLE)); \ - ZEND_ASSERT((zval_gc_flags((p)->u.type_info) & (GC_PERSISTENT|GC_PERSISTENT_LOCAL)) != GC_PERSISTENT); \ - } \ - } \ - } while (0) -# define GC_MAKE_PERSISTENT_LOCAL(p) do { \ - GC_ADD_FLAGS(p, GC_PERSISTENT_LOCAL); \ - } while (0) -#else -# define ZEND_RC_MOD_CHECK(p) \ - do { } while (0) -# define GC_MAKE_PERSISTENT_LOCAL(p) \ - do { } while (0) -#endif - -static zend_always_inline uint32_t zend_gc_refcount(const zend_refcounted_h *p) { - return p->refcount; -} - -static zend_always_inline uint32_t zend_gc_set_refcount(zend_refcounted_h *p, uint32_t rc) { - p->refcount = rc; - return p->refcount; -} - -static zend_always_inline uint32_t zend_gc_addref(zend_refcounted_h *p) { - ZEND_RC_MOD_CHECK(p); - return ++(p->refcount); -} - -static zend_always_inline void zend_gc_try_addref(zend_refcounted_h *p) { - if (!(p->u.type_info & GC_IMMUTABLE)) { - ZEND_RC_MOD_CHECK(p); - ++p->refcount; - } -} - -static zend_always_inline void zend_gc_try_delref(zend_refcounted_h *p) { - if (!(p->u.type_info & GC_IMMUTABLE)) { - ZEND_RC_MOD_CHECK(p); - --p->refcount; - } -} - -static zend_always_inline uint32_t zend_gc_delref(zend_refcounted_h *p) { - ZEND_ASSERT(p->refcount > 0); - ZEND_RC_MOD_CHECK(p); - return --(p->refcount); -} - -static zend_always_inline uint32_t zend_gc_addref_ex(zend_refcounted_h *p, uint32_t rc) { - ZEND_RC_MOD_CHECK(p); - p->refcount += rc; - return p->refcount; -} - -static zend_always_inline uint32_t zend_gc_delref_ex(zend_refcounted_h *p, uint32_t rc) { - ZEND_RC_MOD_CHECK(p); - p->refcount -= rc; - return p->refcount; -} - static zend_always_inline uint32_t zval_refcount_p(const zval* pz) { #if ZEND_DEBUG ZEND_ASSERT(Z_REFCOUNTED_P(pz) || Z_TYPE_P(pz) == IS_ARRAY);