Skip to content

Commit ad7cce6

Browse files
authored
zend_portability: Add ZEND_CONTAINER_OF() (php#21903)
* zend_portability: Add `ZEND_CONTAINER_OF()` * tree-wide: Use `ZEND_CONTAINER_OF()` Changes made with Coccinelle: @@ type T_container; identifier member; expression e; @@ - (T_container *)(((char *)(e)) - offsetof(T_container, member)) + ZEND_CONTAINER_OF(e, T_container, member) @@ type T_container; identifier member; expression e; typedef uintptr_t; @@ - (T_container *)(((uintptr_t)(e)) - offsetof(T_container, member)) + ZEND_CONTAINER_OF(e, T_container, member) @@ type T_container; identifier member; expression e; @@ - (const T_container *)(((char *)(e)) - offsetof(T_container, member)) + ZEND_CONTAINER_OF(e, T_container, member) @@ type T_container; identifier member; expression e; typedef uintptr_t; @@ - (const T_container *)(((uintptr_t)(e)) - offsetof(T_container, member)) + ZEND_CONTAINER_OF(e, T_container, member) * bcmath: Fix `get_bcmath_number_from_obj()` const-correctness A follow-up change to the new `ZEND_CONTAINER_OF()` macro will preserve the `const`-ness of the input pointer. By wrapping this macro inside a macro rather an an inline function we can preserve the `const`-ness across all layers. * zend_portability: Keep `const`-ness in `ZEND_CONTAINER_OF()` * zend_portability: Add C++ version to `ZEND_CONTAINER_OF()`
1 parent d6b7bd0 commit ad7cce6

69 files changed

Lines changed: 123 additions & 97 deletions

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

UPGRADING.INTERNALS

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -107,6 +107,7 @@ PHP 8.6 INTERNALS UPGRADE NOTES
107107
zend_fci_consumed_arg(), which allows moving a selected callback argument
108108
instead of copying it in zend_call_function(). Currently only a single
109109
consumed argument is supported.
110+
. Added ZEND_CONTAINER_OF().
110111

111112
========================
112113
2. Build system changes

Zend/Optimizer/zend_optimizer.c

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -780,7 +780,7 @@ static bool zend_optimizer_ignore_class(zval *ce_zv, const zend_string *filename
780780
if (CG(compiler_options) & ZEND_COMPILE_WITH_FILE_CACHE) {
781781
return true;
782782
}
783-
const Bucket *ce_bucket = (const Bucket*)((uintptr_t)ce_zv - offsetof(Bucket, val));
783+
const Bucket *ce_bucket = ZEND_CONTAINER_OF(ce_zv, Bucket, val);
784784
size_t offset = ce_bucket - EG(class_table)->arData;
785785
if (offset < EG(persistent_classes_count)) {
786786
return false;
@@ -801,7 +801,7 @@ static bool zend_optimizer_ignore_function(zval *fbc_zv, const zend_string *file
801801
if (CG(compiler_options) & ZEND_COMPILE_WITH_FILE_CACHE) {
802802
return true;
803803
}
804-
const Bucket *fbc_bucket = (const Bucket*)((uintptr_t)fbc_zv - offsetof(Bucket, val));
804+
const Bucket *fbc_bucket = ZEND_CONTAINER_OF(fbc_zv, Bucket, val);
805805
size_t offset = fbc_bucket - EG(function_table)->arData;
806806
if (offset < EG(persistent_functions_count)) {
807807
return false;

Zend/zend_closures.c

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -830,7 +830,7 @@ static void zend_create_closure_ex(zval *res, zend_function *func, zend_class_en
830830
/* wrap internal function handler to avoid memory leak */
831831
if (UNEXPECTED(closure->func.internal_function.handler == zend_closure_internal_handler)) {
832832
/* avoid infinity recursion, by taking handler from nested closure */
833-
zend_closure *nested = (zend_closure*)((char*)func - offsetof(zend_closure, func));
833+
zend_closure *nested = ZEND_CONTAINER_OF(func, zend_closure, func);
834834
ZEND_ASSERT(nested->std.ce == zend_ce_closure);
835835
closure->orig_internal_handler = nested->orig_internal_handler;
836836
} else {

Zend/zend_compile.c

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -5175,7 +5175,7 @@ static zend_result zend_compile_func_array_map(znode *result, zend_ast_list *arg
51755175
opline->lineno = lineno;
51765176
opline->extended_value = (2 << 16) | IS_ARRAY;
51775177
const zval *fbc_zv = zend_hash_find(CG(function_table), lcname);
5178-
const Bucket *fbc_bucket = (const Bucket*)((uintptr_t)fbc_zv - offsetof(Bucket, val));
5178+
const Bucket *fbc_bucket = ZEND_CONTAINER_OF(fbc_zv, Bucket, val);
51795179
Z_EXTRA_P(CT_CONSTANT(opline->op1)) = fbc_bucket - CG(function_table)->arData;
51805180

51815181
/* Initialize the result array. */
@@ -5472,7 +5472,7 @@ static void zend_compile_call(znode *result, const zend_ast *ast, uint32_t type)
54725472

54735473
/* Store offset to function from symbol table in op2.extra. */
54745474
if (fbc->type == ZEND_INTERNAL_FUNCTION) {
5475-
const Bucket *fbc_bucket = (const Bucket*)((uintptr_t)fbc_zv - offsetof(Bucket, val));
5475+
const Bucket *fbc_bucket = ZEND_CONTAINER_OF(fbc_zv, Bucket, val);
54765476
Z_EXTRA_P(CT_CONSTANT(opline->op2)) = fbc_bucket - CG(function_table)->arData;
54775477
}
54785478

Zend/zend_enum.h

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -36,7 +36,7 @@ typedef struct zend_enum_obj {
3636

3737
static inline zend_enum_obj *zend_enum_obj_from_obj(zend_object *zobj) {
3838
ZEND_ASSERT(zobj->ce->ce_flags & ZEND_ACC_ENUM);
39-
return (zend_enum_obj*)((char*)(zobj) - offsetof(zend_enum_obj, std));
39+
return ZEND_CONTAINER_OF(zobj, zend_enum_obj, std);
4040
}
4141

4242
void zend_enum_startup(void);

Zend/zend_fibers.h

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -154,7 +154,7 @@ static zend_always_inline zend_fiber *zend_fiber_from_context(zend_fiber_context
154154
{
155155
ZEND_ASSERT(context->kind == zend_ce_fiber && "Fiber context does not belong to a Zend fiber");
156156

157-
return (zend_fiber *)(((char *) context) - offsetof(zend_fiber, context));
157+
return ZEND_CONTAINER_OF(context, zend_fiber, context);
158158
}
159159

160160
static zend_always_inline zend_fiber_context *zend_fiber_get_context(zend_fiber *fiber)

Zend/zend_portability.h

Lines changed: 28 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -387,6 +387,34 @@ char *alloca();
387387
#define ZEND_ELEMENT_COUNT(m)
388388
#endif
389389

390+
#if __cplusplus
391+
extern "C++" {
392+
# include <cstddef>
393+
template<typename T, typename M>
394+
const T* zend_container_of(const M *ptr, size_t offset) {
395+
return reinterpret_cast<const T*>(reinterpret_cast<const char*>(ptr) - offset);
396+
}
397+
template<typename T, typename M>
398+
T* zend_container_of(M *ptr, size_t offset) {
399+
return reinterpret_cast<T*>(reinterpret_cast<char*>(ptr) - offset);
400+
}
401+
402+
# define ZEND_CONTAINER_OF(ptr, Type, member) zend_container_of<Type, decltype(Type::member)>(ptr, offsetof(Type, member))
403+
}
404+
#elif __STDC_VERSION__ >= 202311L || ZEND_GCC_VERSION
405+
/* typeof is C23 or a GCC extension */
406+
# define ZEND_CONTAINER_OF(ptr, Type, member) \
407+
_Generic( \
408+
(ptr), \
409+
const typeof(((Type*)0)->member) *: ((const Type*)((char*)(ptr) - offsetof(Type, member))), \
410+
typeof(((Type*)0)->member) *: ((Type*)((char*)(ptr) - offsetof(Type, member))) \
411+
)
412+
#else
413+
/* Define a variant that does not keep const-ness for older compilers. Mismatches
414+
* are expected to be caught by CI running modern compilers. */
415+
# define ZEND_CONTAINER_OF(ptr, Type, member) ((Type*)((char*)(ptr) - offsetof(Type, member)))
416+
#endif
417+
390418
#ifdef HAVE_BUILTIN_CONSTANT_P
391419
# define ZEND_CONST_COND(_condition, _default) \
392420
(__builtin_constant_p(_condition) ? (_condition) : (_default))

Zend/zend_weakrefs.c

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -58,10 +58,10 @@ static zend_class_entry *zend_ce_weakmap;
5858
static zend_object_handlers zend_weakref_handlers;
5959
static zend_object_handlers zend_weakmap_handlers;
6060

61-
#define zend_weakref_from(o) ((zend_weakref*)(((char*) o) - offsetof(zend_weakref, std)))
61+
#define zend_weakref_from(o) (ZEND_CONTAINER_OF(o, zend_weakref, std))
6262
#define zend_weakref_fetch(z) zend_weakref_from(Z_OBJ_P(z))
6363

64-
#define zend_weakmap_from(o) ((zend_weakmap*)(((char*) o) - offsetof(zend_weakmap, std)))
64+
#define zend_weakmap_from(o) (ZEND_CONTAINER_OF(o, zend_weakmap, std))
6565
#define zend_weakmap_fetch(z) zend_weakmap_from(Z_OBJ_P(z))
6666

6767
static inline void zend_weakref_unref_single(

ext/bcmath/bcmath.c

Lines changed: 2 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -875,10 +875,7 @@ static int bcmath_number_compare(zval *op1, zval *op2);
875875
#endif
876876
#define CHECK_SCALE_OVERFLOW(scale) (scale > INT_MAX)
877877

878-
static zend_always_inline bcmath_number_obj_t *get_bcmath_number_from_obj(const zend_object *obj)
879-
{
880-
return (bcmath_number_obj_t*)((char*)(obj) - offsetof(bcmath_number_obj_t, std));
881-
}
878+
#define get_bcmath_number_from_obj(obj) ZEND_CONTAINER_OF(obj, bcmath_number_obj_t, std)
882879

883880
static zend_always_inline bcmath_number_obj_t *get_bcmath_number_from_zval(const zval *zv)
884881
{
@@ -1214,7 +1211,7 @@ static zend_result bc_num_from_obj_or_str_or_long(
12141211
bc_num *num, size_t *full_scale, const zend_object *obj, const zend_string *str, zend_long lval)
12151212
{
12161213
if (obj) {
1217-
bcmath_number_obj_t *intern = get_bcmath_number_from_obj(obj);
1214+
const bcmath_number_obj_t *intern = get_bcmath_number_from_obj(obj);
12181215
*num = intern->num;
12191216
if (full_scale) {
12201217
*full_scale = intern->scale;

ext/curl/curl_private.h

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -152,13 +152,13 @@ void _php_setup_easy_copy_handlers(php_curl *ch, php_curl *source);
152152
zend_long php_curl_get_long(zval *zv);
153153

154154
static inline php_curl *curl_from_obj(zend_object *obj) {
155-
return (php_curl *)((char *)(obj) - offsetof(php_curl, std));
155+
return ZEND_CONTAINER_OF(obj, php_curl, std);
156156
}
157157

158158
#define Z_CURL_P(zv) curl_from_obj(Z_OBJ_P(zv))
159159

160160
static inline php_curlsh *curl_share_from_obj(zend_object *obj) {
161-
return (php_curlsh *)((char *)(obj) - offsetof(php_curlsh, std));
161+
return ZEND_CONTAINER_OF(obj, php_curlsh, std);
162162
}
163163

164164
#define Z_CURL_SHARE_P(zv) curl_share_from_obj(Z_OBJ_P(zv))

0 commit comments

Comments
 (0)