From dc189f5de87801e7047d65feea67a8d110a89aa6 Mon Sep 17 00:00:00 2001 From: Gina Peter Banyard Date: Sat, 5 Apr 2025 17:15:48 +0100 Subject: [PATCH] Zend: Add MUTABLE zend_type foreach macros and const qualifiers The motivation for this is that types should be considered immutable. The only times this is not valid is during compilation, optimizations (opcache), or destruction. Therefore the "normal" type foreach macros are marked to take const arguments and we add mutable version that say so in the name. Thus add various const qualifiers to communicate intent. --- Zend/Optimizer/compact_literals.c | 2 +- Zend/Optimizer/dfa_pass.c | 8 +-- Zend/zend_API.c | 4 +- Zend/zend_compile.c | 30 +++++----- Zend/zend_execute.c | 94 +++++++++++++++---------------- Zend/zend_execute.h | 18 +++--- Zend/zend_inheritance.c | 32 +++++------ Zend/zend_opcode.c | 2 +- Zend/zend_types.h | 25 +++++++- ext/opcache/ZendAccelerator.c | 2 +- ext/opcache/zend_file_cache.c | 4 +- ext/opcache/zend_persist.c | 2 +- ext/opcache/zend_persist_calc.c | 2 +- ext/reflection/php_reflection.c | 4 +- 14 files changed, 125 insertions(+), 104 deletions(-) diff --git a/Zend/Optimizer/compact_literals.c b/Zend/Optimizer/compact_literals.c index db973572aca3a..202cb7c2d6114 100644 --- a/Zend/Optimizer/compact_literals.c +++ b/Zend/Optimizer/compact_literals.c @@ -69,7 +69,7 @@ static size_t type_num_classes(const zend_op_array *op_array, uint32_t arg_num) } ZEND_ASSERT(ZEND_TYPE_IS_UNION(arg_info->type)); size_t count = 0; - zend_type *list_type; + const zend_type *list_type; ZEND_TYPE_LIST_FOREACH(ZEND_TYPE_LIST(arg_info->type), list_type) { if (ZEND_TYPE_IS_INTERSECTION(*list_type)) { diff --git a/Zend/Optimizer/dfa_pass.c b/Zend/Optimizer/dfa_pass.c index 2c3aaae065997..bf85764c93b49 100644 --- a/Zend/Optimizer/dfa_pass.c +++ b/Zend/Optimizer/dfa_pass.c @@ -254,7 +254,7 @@ static void zend_ssa_remove_nops(zend_op_array *op_array, zend_ssa *ssa, zend_op free_alloca(shiftlist, use_heap); } -static bool safe_instanceof(zend_class_entry *ce1, zend_class_entry *ce2) { +static bool safe_instanceof(const zend_class_entry *ce1, const zend_class_entry *ce2) { if (ce1 == ce2) { return 1; } @@ -267,9 +267,9 @@ static bool safe_instanceof(zend_class_entry *ce1, zend_class_entry *ce2) { static inline bool can_elide_list_type( const zend_script *script, const zend_op_array *op_array, - const zend_ssa_var_info *use_info, zend_type type) + const zend_ssa_var_info *use_info, const zend_type type) { - zend_type *single_type; + const zend_type *single_type; /* For intersection: result==false is failure, default is success. * For union: result==true is success, default is failure. */ bool is_intersection = ZEND_TYPE_IS_INTERSECTION(type); @@ -280,7 +280,7 @@ static inline bool can_elide_list_type( } if (ZEND_TYPE_HAS_NAME(*single_type)) { zend_string *lcname = zend_string_tolower(ZEND_TYPE_NAME(*single_type)); - zend_class_entry *ce = zend_optimizer_get_class_entry(script, op_array, lcname); + const zend_class_entry *ce = zend_optimizer_get_class_entry(script, op_array, lcname); zend_string_release(lcname); bool result = ce && safe_instanceof(use_info->ce, ce); if (result == !is_intersection) { diff --git a/Zend/zend_API.c b/Zend/zend_API.c index 4c31fba5e506e..e0006e7d7275f 100644 --- a/Zend/zend_API.c +++ b/Zend/zend_API.c @@ -2914,14 +2914,14 @@ static zend_always_inline void zend_normalize_internal_type(zend_type *type) { ZEND_ASSERT(!ZEND_TYPE_CONTAINS_CODE(*type, IS_RESOURCE) && "resource is not allowed in a zend_type"); } zend_type *current; - ZEND_TYPE_FOREACH(*type, current) { + ZEND_TYPE_FOREACH_MUTABLE(*type, current) { if (ZEND_TYPE_HAS_NAME(*current)) { zend_string *name = zend_new_interned_string(ZEND_TYPE_NAME(*current)); zend_alloc_ce_cache(name); ZEND_TYPE_SET_PTR(*current, name); } else if (ZEND_TYPE_HAS_LIST(*current)) { zend_type *inner; - ZEND_TYPE_FOREACH(*current, inner) { + ZEND_TYPE_FOREACH_MUTABLE(*current, inner) { ZEND_ASSERT(!ZEND_TYPE_HAS_LITERAL_NAME(*inner) && !ZEND_TYPE_HAS_LIST(*inner)); if (ZEND_TYPE_HAS_NAME(*inner)) { zend_string *name = zend_new_interned_string(ZEND_TYPE_NAME(*inner)); diff --git a/Zend/zend_compile.c b/Zend/zend_compile.c index 2e405de30ea62..3fa4c3959cb43 100644 --- a/Zend/zend_compile.c +++ b/Zend/zend_compile.c @@ -1407,10 +1407,10 @@ static zend_string *resolve_class_name(zend_string *name, zend_class_entry *scop } static zend_string *add_intersection_type(zend_string *str, - zend_type_list *intersection_type_list, zend_class_entry *scope, + const zend_type_list *intersection_type_list, zend_class_entry *scope, bool is_bracketed) { - zend_type *single_type; + const zend_type *single_type; zend_string *intersection_str = NULL; ZEND_TYPE_LIST_FOREACH(intersection_type_list, single_type) { @@ -1432,7 +1432,7 @@ static zend_string *add_intersection_type(zend_string *str, return str; } -zend_string *zend_type_to_string_resolved(zend_type type, zend_class_entry *scope) { +zend_string *zend_type_to_string_resolved(const zend_type type, zend_class_entry *scope) { zend_string *str = NULL; /* Pure intersection type */ @@ -1441,7 +1441,7 @@ zend_string *zend_type_to_string_resolved(zend_type type, zend_class_entry *scop str = add_intersection_type(str, ZEND_TYPE_LIST(type), scope, /* is_bracketed */ false); } else if (ZEND_TYPE_HAS_LIST(type)) { /* A union type might not be a list */ - zend_type *list_type; + const zend_type *list_type; ZEND_TYPE_LIST_FOREACH(ZEND_TYPE_LIST(type), list_type) { if (ZEND_TYPE_IS_INTERSECTION(*list_type)) { str = add_intersection_type(str, ZEND_TYPE_LIST(*list_type), scope, /* is_bracketed */ true); @@ -1527,7 +1527,7 @@ ZEND_API zend_string *zend_type_to_string(zend_type type) { return zend_type_to_string_resolved(type, NULL); } -static bool is_generator_compatible_class_type(zend_string *name) { +static bool is_generator_compatible_class_type(const zend_string *name) { return zend_string_equals_ci(name, ZSTR_KNOWN(ZEND_STR_TRAVERSABLE)) || zend_string_equals_literal_ci(name, "Iterator") || zend_string_equals_literal_ci(name, "Generator"); @@ -1541,10 +1541,10 @@ static void zend_mark_function_as_generator(void) /* {{{ */ } if (CG(active_op_array)->fn_flags & ZEND_ACC_HAS_RETURN_TYPE) { - zend_type return_type = CG(active_op_array)->arg_info[-1].type; + const zend_type return_type = CG(active_op_array)->arg_info[-1].type; bool valid_type = (ZEND_TYPE_FULL_MASK(return_type) & MAY_BE_OBJECT) != 0; if (!valid_type) { - zend_type *single_type; + const zend_type *single_type; ZEND_TYPE_FOREACH(return_type, single_type) { if (ZEND_TYPE_HAS_NAME(*single_type) && is_generator_compatible_class_type(ZEND_TYPE_NAME(*single_type))) { @@ -2621,7 +2621,7 @@ static void zend_compile_memoized_expr(znode *result, zend_ast *expr) /* {{{ */ /* }}} */ /* Remember to update type_num_classes() in compact_literals.c when changing this function */ -static size_t zend_type_get_num_classes(zend_type type) { +static size_t zend_type_get_num_classes(const zend_type type) { if (!ZEND_TYPE_IS_COMPLEX(type)) { return 0; } @@ -2632,7 +2632,7 @@ static size_t zend_type_get_num_classes(zend_type type) { } ZEND_ASSERT(ZEND_TYPE_IS_UNION(type)); size_t count = 0; - zend_type *list_type; + const zend_type *list_type; ZEND_TYPE_LIST_FOREACH(ZEND_TYPE_LIST(type), list_type) { if (ZEND_TYPE_IS_INTERSECTION(*list_type)) { @@ -7048,7 +7048,7 @@ static zend_type zend_compile_single_typename(zend_ast *ast) } } -static void zend_are_intersection_types_redundant(zend_type left_type, zend_type right_type) +static void zend_are_intersection_types_redundant(const zend_type left_type, const zend_type right_type) { ZEND_ASSERT(ZEND_TYPE_IS_INTERSECTION(left_type)); ZEND_ASSERT(ZEND_TYPE_IS_INTERSECTION(right_type)); @@ -7067,9 +7067,9 @@ static void zend_are_intersection_types_redundant(zend_type left_type, zend_type } unsigned int sum = 0; - zend_type *outer_type; + const zend_type *outer_type; ZEND_TYPE_LIST_FOREACH(smaller_type_list, outer_type) - zend_type *inner_type; + const zend_type *inner_type; ZEND_TYPE_LIST_FOREACH(larger_type_list, inner_type) if (zend_string_equals_ci(ZEND_TYPE_NAME(*inner_type), ZEND_TYPE_NAME(*outer_type))) { sum++; @@ -7098,12 +7098,12 @@ static void zend_are_intersection_types_redundant(zend_type left_type, zend_type } } -static void zend_is_intersection_type_redundant_by_single_type(zend_type intersection_type, zend_type single_type) +static void zend_is_intersection_type_redundant_by_single_type(const zend_type intersection_type, const zend_type single_type) { ZEND_ASSERT(ZEND_TYPE_IS_INTERSECTION(intersection_type)); ZEND_ASSERT(!ZEND_TYPE_IS_INTERSECTION(single_type)); - zend_type *single_intersection_type = NULL; + const zend_type *single_intersection_type = NULL; ZEND_TYPE_FOREACH(intersection_type, single_intersection_type) if (zend_string_equals_ci(ZEND_TYPE_NAME(*single_intersection_type), ZEND_TYPE_NAME(single_type))) { zend_string *single_type_str = zend_type_to_string(single_type); @@ -7115,7 +7115,7 @@ static void zend_is_intersection_type_redundant_by_single_type(zend_type interse } /* Used by both intersection and union types prior to transforming the type list to a full zend_type */ -static void zend_is_type_list_redundant_by_single_type(zend_type_list *type_list, zend_type type) +static void zend_is_type_list_redundant_by_single_type(const zend_type_list *type_list, const zend_type type) { ZEND_ASSERT(!ZEND_TYPE_IS_INTERSECTION(type)); for (size_t i = 0; i < type_list->num_types - 1; i++) { diff --git a/Zend/zend_execute.c b/Zend/zend_execute.c index 4fed2af3a92fd..786cd60ef1401 100644 --- a/Zend/zend_execute.c +++ b/Zend/zend_execute.c @@ -609,7 +609,7 @@ ZEND_API ZEND_COLD void ZEND_FASTCALL zend_cannot_pass_by_reference(uint32_t arg zend_string_release(func_name); } -static zend_never_inline ZEND_COLD void zend_throw_auto_init_in_prop_error(zend_property_info *prop) { +static zend_never_inline ZEND_COLD void zend_throw_auto_init_in_prop_error(const zend_property_info *prop) { zend_string *type_str = zend_type_to_string(prop->type); zend_type_error( "Cannot auto-initialize an array inside property %s::$%s of type %s", @@ -619,7 +619,7 @@ static zend_never_inline ZEND_COLD void zend_throw_auto_init_in_prop_error(zend_ zend_string_release(type_str); } -static zend_never_inline ZEND_COLD void zend_throw_auto_init_in_ref_error(zend_property_info *prop) { +static zend_never_inline ZEND_COLD void zend_throw_auto_init_in_ref_error(const zend_property_info *prop) { zend_string *type_str = zend_type_to_string(prop->type); zend_type_error( "Cannot auto-initialize an array inside a reference held by property %s::$%s of type %s", @@ -630,7 +630,7 @@ static zend_never_inline ZEND_COLD void zend_throw_auto_init_in_ref_error(zend_p } static zend_never_inline ZEND_COLD void zend_throw_access_uninit_prop_by_ref_error( - zend_property_info *prop) { + const zend_property_info *prop) { zend_throw_error(NULL, "Cannot access uninitialized non-nullable property %s::$%s by reference", ZSTR_VAL(prop->ce->name), @@ -638,7 +638,7 @@ static zend_never_inline ZEND_COLD void zend_throw_access_uninit_prop_by_ref_err } /* this should modify object only if it's empty */ -static zend_never_inline ZEND_COLD void ZEND_FASTCALL zend_throw_non_object_error(zval *object, zval *property OPLINE_DC EXECUTE_DATA_DC) +static zend_never_inline ZEND_COLD void ZEND_FASTCALL zend_throw_non_object_error(const zval *object, zval *property OPLINE_DC EXECUTE_DATA_DC) { zend_string *tmp_property_name; zend_string *property_name = zval_get_tmp_string(property, &tmp_property_name); @@ -673,7 +673,7 @@ static zend_never_inline ZEND_COLD void ZEND_FASTCALL zend_throw_non_object_erro } static ZEND_COLD void zend_verify_type_error_common( - const zend_function *zf, const zend_arg_info *arg_info, zval *value, + const zend_function *zf, const zend_arg_info *arg_info, const zval *value, const char **fname, const char **fsep, const char **fclass, zend_string **need_msg, const char **given_kind) { @@ -696,9 +696,9 @@ static ZEND_COLD void zend_verify_type_error_common( } ZEND_API ZEND_COLD void zend_verify_arg_error( - const zend_function *zf, const zend_arg_info *arg_info, uint32_t arg_num, zval *value) + const zend_function *zf, const zend_arg_info *arg_info, uint32_t arg_num, const zval *value) { - zend_execute_data *ptr = EG(current_execute_data)->prev_execute_data; + const zend_execute_data *ptr = EG(current_execute_data)->prev_execute_data; const char *fname, *fsep, *fclass; zend_string *need_msg; const char *given_msg; @@ -909,7 +909,7 @@ ZEND_API ZEND_COLD void ZEND_FASTCALL zend_readonly_property_indirect_modificati ZSTR_VAL(info->ce->name), zend_get_unmangled_property_name(info->name)); } -ZEND_API ZEND_COLD void ZEND_FASTCALL zend_invalid_class_constant_type_error(uint8_t type) +ZEND_API ZEND_COLD void ZEND_FASTCALL zend_invalid_class_constant_type_error(const uint8_t type) { zend_type_error("Cannot use value of type %s as class constant name", zend_get_type_by_const(type)); } @@ -975,9 +975,9 @@ static zend_always_inline const zend_class_entry *zend_ce_from_type( } static bool zend_check_intersection_for_property_or_class_constant_class_type( - const zend_class_entry *scope, zend_type_list *intersection_type_list, const zend_class_entry *value_ce) + const zend_class_entry *scope, const zend_type_list *intersection_type_list, const zend_class_entry *value_ce) { - zend_type *list_type; + const zend_type *list_type; ZEND_TYPE_LIST_FOREACH(intersection_type_list, list_type) { ZEND_ASSERT(!ZEND_TYPE_HAS_LIST(*list_type)); @@ -990,13 +990,13 @@ static bool zend_check_intersection_for_property_or_class_constant_class_type( } static bool zend_check_and_resolve_property_or_class_constant_class_type( - const zend_class_entry *scope, zend_type member_type, const zend_class_entry *value_ce) { + const zend_class_entry *scope, const zend_type member_type, const zend_class_entry *value_ce) { if (ZEND_TYPE_HAS_LIST(member_type)) { - zend_type *list_type; if (ZEND_TYPE_IS_INTERSECTION(member_type)) { return zend_check_intersection_for_property_or_class_constant_class_type( scope, ZEND_TYPE_LIST(member_type), value_ce); } else { + const zend_type *list_type; ZEND_TYPE_LIST_FOREACH(ZEND_TYPE_LIST(member_type), list_type) { if (ZEND_TYPE_IS_INTERSECTION(*list_type)) { if (zend_check_intersection_for_property_or_class_constant_class_type( @@ -1059,7 +1059,7 @@ ZEND_API bool zend_never_inline zend_verify_property_type(const zend_property_in return i_zend_verify_property_type(info, property, strict); } -static zend_never_inline zval* zend_assign_to_typed_prop(zend_property_info *info, zval *property_val, zval *value, zend_refcounted **garbage_ptr EXECUTE_DATA_DC) +static zend_never_inline zval* zend_assign_to_typed_prop(const zend_property_info *info, zval *property_val, zval *value, zend_refcounted **garbage_ptr EXECUTE_DATA_DC) { zval tmp; @@ -1087,7 +1087,7 @@ static zend_never_inline zval* zend_assign_to_typed_prop(zend_property_info *inf return zend_assign_to_variable_ex(property_val, &tmp, IS_TMP_VAR, EX_USES_STRICT_TYPES(), garbage_ptr); } -static zend_always_inline bool zend_value_instanceof_static(zval *zv) { +static zend_always_inline bool zend_value_instanceof_static(const zval *zv) { if (Z_TYPE_P(zv) != IS_OBJECT) { return 0; } @@ -1110,7 +1110,7 @@ static zend_always_inline bool zend_value_instanceof_static(zval *zv) { #define PROGRESS_CACHE_SLOT() if (HAVE_CACHE_SLOT) {cache_slot++;} static zend_always_inline zend_class_entry *zend_fetch_ce_from_cache_slot( - void **cache_slot, zend_type *type) + void **cache_slot, const zend_type *type) { if (EXPECTED(HAVE_CACHE_SLOT && *cache_slot)) { return (zend_class_entry *) *cache_slot; @@ -1140,17 +1140,18 @@ static zend_always_inline zend_class_entry *zend_fetch_ce_from_cache_slot( return ce; } -static bool zend_check_intersection_type_from_cache_slot(zend_type_list *intersection_type_list, - zend_class_entry *arg_ce, void ***cache_slot_ptr) +static bool zend_check_intersection_type_from_cache_slot( + const zend_type_list *intersection_type_list, + const zend_class_entry *arg_ce, + void ***cache_slot_ptr) { void **cache_slot = *cache_slot_ptr; - zend_class_entry *ce; - zend_type *list_type; + const zend_type *list_type; bool status = true; ZEND_TYPE_LIST_FOREACH(intersection_type_list, list_type) { /* Only check classes if the type might be valid */ if (status) { - ce = zend_fetch_ce_from_cache_slot(cache_slot, list_type); + zend_class_entry *ce = zend_fetch_ce_from_cache_slot(cache_slot, list_type); /* If type is not an instance of one of the types taking part in the * intersection it cannot be a valid instance of the whole intersection type. */ if (!ce || !instanceof_function(arg_ce, ce)) { @@ -1166,17 +1167,16 @@ static bool zend_check_intersection_type_from_cache_slot(zend_type_list *interse } static zend_always_inline bool zend_check_type_slow( - zend_type *type, zval *arg, zend_reference *ref, void **cache_slot, + const zend_type *type, zval *arg, const zend_reference *ref, void **cache_slot, bool is_return_type, bool is_internal) { - uint32_t type_mask; if (ZEND_TYPE_IS_COMPLEX(*type) && EXPECTED(Z_TYPE_P(arg) == IS_OBJECT)) { zend_class_entry *ce; if (UNEXPECTED(ZEND_TYPE_HAS_LIST(*type))) { - zend_type *list_type; if (ZEND_TYPE_IS_INTERSECTION(*type)) { return zend_check_intersection_type_from_cache_slot(ZEND_TYPE_LIST(*type), Z_OBJCE_P(arg), &cache_slot); } else { + const zend_type *list_type; ZEND_TYPE_LIST_FOREACH(ZEND_TYPE_LIST(*type), list_type) { if (ZEND_TYPE_IS_INTERSECTION(*list_type)) { if (zend_check_intersection_type_from_cache_slot(ZEND_TYPE_LIST(*list_type), Z_OBJCE_P(arg), &cache_slot)) { @@ -1204,7 +1204,7 @@ static zend_always_inline bool zend_check_type_slow( } } - type_mask = ZEND_TYPE_FULL_MASK(*type); + const uint32_t type_mask = ZEND_TYPE_FULL_MASK(*type); if ((type_mask & MAY_BE_CALLABLE) && zend_is_callable(arg, is_internal ? IS_CALLABLE_SUPPRESS_DEPRECATIONS : 0, NULL)) { return 1; @@ -1232,10 +1232,10 @@ static zend_always_inline bool zend_check_type_slow( } static zend_always_inline bool zend_check_type( - zend_type *type, zval *arg, void **cache_slot, zend_class_entry *scope, + const zend_type *type, zval *arg, void **cache_slot, zend_class_entry *scope, bool is_return_type, bool is_internal) { - zend_reference *ref = NULL; + const zend_reference *ref = NULL; ZEND_ASSERT(ZEND_TYPE_IS_SET(*type)); if (UNEXPECTED(Z_ISREF_P(arg))) { @@ -1251,15 +1251,15 @@ static zend_always_inline bool zend_check_type( } ZEND_API bool zend_check_user_type_slow( - zend_type *type, zval *arg, zend_reference *ref, void **cache_slot, bool is_return_type) + const zend_type *type, zval *arg, const zend_reference *ref, void **cache_slot, bool is_return_type) { return zend_check_type_slow( type, arg, ref, cache_slot, is_return_type, /* is_internal */ false); } -static zend_always_inline bool zend_verify_recv_arg_type(zend_function *zf, uint32_t arg_num, zval *arg, void **cache_slot) +static zend_always_inline bool zend_verify_recv_arg_type(const zend_function *zf, uint32_t arg_num, zval *arg, void **cache_slot) { - zend_arg_info *cur_arg_info; + const zend_arg_info *cur_arg_info; ZEND_ASSERT(arg_num <= zf->common.num_args); cur_arg_info = &zf->common.arg_info[arg_num-1]; @@ -1274,7 +1274,7 @@ static zend_always_inline bool zend_verify_recv_arg_type(zend_function *zf, uint } static zend_always_inline bool zend_verify_variadic_arg_type( - zend_function *zf, zend_arg_info *arg_info, uint32_t arg_num, zval *arg, void **cache_slot) + const zend_function *zf, const zend_arg_info *arg_info, uint32_t arg_num, zval *arg, void **cache_slot) { ZEND_ASSERT(ZEND_TYPE_IS_SET(arg_info->type)); if (UNEXPECTED(!zend_check_type(&arg_info->type, arg, cache_slot, zf->common.scope, 0, 0))) { @@ -1285,7 +1285,7 @@ static zend_always_inline bool zend_verify_variadic_arg_type( return 1; } -static zend_never_inline ZEND_ATTRIBUTE_UNUSED bool zend_verify_internal_arg_types(zend_function *fbc, zend_execute_data *call) +static zend_never_inline ZEND_ATTRIBUTE_UNUSED bool zend_verify_internal_arg_types(const zend_function *fbc, zend_execute_data *call) { uint32_t i; uint32_t num_args = ZEND_CALL_NUM_ARGS(call); @@ -1314,7 +1314,7 @@ static zend_never_inline ZEND_ATTRIBUTE_UNUSED bool zend_verify_internal_arg_typ /* Determine whether an internal call should throw, because the passed arguments violate * an arginfo constraint. This is only checked in debug builds. In release builds, we * trust that arginfo matches what is enforced by zend_parse_parameters. */ -ZEND_API bool zend_internal_call_should_throw(zend_function *fbc, zend_execute_data *call) +ZEND_API bool zend_internal_call_should_throw(const zend_function *fbc, zend_execute_data *call) { if (fbc->internal_function.handler == ZEND_FN(pass) || (fbc->internal_function.fn_flags & ZEND_ACC_FAKE_CLOSURE)) { /* Be lenient about the special pass function and about fake closures. */ @@ -1341,7 +1341,7 @@ ZEND_API bool zend_internal_call_should_throw(zend_function *fbc, zend_execute_d return 0; } -ZEND_API ZEND_COLD void zend_internal_call_arginfo_violation(zend_function *fbc) +ZEND_API ZEND_COLD void zend_internal_call_arginfo_violation(const zend_function *fbc) { zend_error_noreturn(E_ERROR, "Arginfo / zpp mismatch during call of %s%s%s()", fbc->common.scope ? ZSTR_VAL(fbc->common.scope->name) : "", @@ -1353,10 +1353,10 @@ ZEND_API ZEND_COLD void zend_internal_call_arginfo_violation(zend_function *fbc) # define ZEND_VERIFY_FUNC_INFO 0 #endif -static void zend_verify_internal_func_info(zend_function *fn, zval *retval) { +static void zend_verify_internal_func_info(const zend_function *fn, const zval *retval) { #if ZEND_VERIFY_FUNC_INFO zend_string *name = fn->common.function_name; - uint32_t type_mask = zend_get_internal_func_info(fn, NULL, NULL); + const uint32_t type_mask = zend_get_internal_func_info(fn, NULL, NULL); if (!type_mask) { return; } @@ -1371,14 +1371,14 @@ static void zend_verify_internal_func_info(zend_function *fn, zval *retval) { } } - uint32_t type = 1u << Z_TYPE_P(retval); + const uint32_t type = 1u << Z_TYPE_P(retval); if (!(type_mask & type)) { zend_error_noreturn(E_CORE_ERROR, "%s() missing type %s", ZSTR_VAL(name), zend_get_type_by_const(Z_TYPE_P(retval))); } if (Z_TYPE_P(retval) == IS_ARRAY) { - HashTable *ht = Z_ARRVAL_P(retval); + const HashTable *ht = Z_ARRVAL_P(retval); uint32_t num_checked = 0; zend_string *str; zval *val; @@ -1395,7 +1395,7 @@ static void zend_verify_internal_func_info(zend_function *fn, zval *retval) { } } - uint32_t array_type = 1u << (Z_TYPE_P(val) + MAY_BE_ARRAY_SHIFT); + const uint32_t array_type = 1u << (Z_TYPE_P(val) + MAY_BE_ARRAY_SHIFT); if (!(type_mask & array_type)) { zend_error_noreturn(E_CORE_ERROR, "%s() missing array element type %s", @@ -1412,9 +1412,9 @@ static void zend_verify_internal_func_info(zend_function *fn, zval *retval) { } #endif -ZEND_API ZEND_COLD void ZEND_FASTCALL zend_missing_arg_error(zend_execute_data *execute_data) +ZEND_API ZEND_COLD void ZEND_FASTCALL zend_missing_arg_error(const zend_execute_data *execute_data) { - zend_execute_data *ptr = EX(prev_execute_data); + const zend_execute_data *ptr = EX(prev_execute_data); if (ptr && ptr->func && ZEND_USER_CODE(ptr->func->common.type)) { zend_throw_error(zend_ce_argument_count_error, "Too few arguments to function %s%s%s(), %d passed in %s on line %d and %s %d expected", @@ -1437,7 +1437,7 @@ ZEND_API ZEND_COLD void ZEND_FASTCALL zend_missing_arg_error(zend_execute_data * } } -ZEND_API ZEND_COLD void zend_verify_return_error(const zend_function *zf, zval *value) +ZEND_API ZEND_COLD void zend_verify_return_error(const zend_function *zf, const zval *value) { const zend_arg_info *arg_info = &zf->common.arg_info[-1]; const char *fname, *fsep, *fclass; @@ -1464,7 +1464,7 @@ ZEND_API ZEND_COLD void zend_verify_never_error(const zend_function *zf) } #if ZEND_DEBUG -static ZEND_COLD void zend_verify_internal_return_error(const zend_function *zf, zval *value) +static ZEND_COLD void zend_verify_internal_return_error(const zend_function *zf, const zval *value) { const zend_arg_info *arg_info = &zf->common.arg_info[-1]; const char *fname, *fsep, *fclass; @@ -1496,9 +1496,9 @@ static ZEND_COLD void zend_verify_void_return_error(const zend_function *zf, con fclass, fsep, fname, returned_msg, returned_kind); } -ZEND_API bool zend_verify_internal_return_type(zend_function *zf, zval *ret) +ZEND_API bool zend_verify_internal_return_type(const zend_function *zf, zval *ret) { - zend_internal_arg_info *ret_info = zf->internal_function.arg_info - 1; + const zend_internal_arg_info *ret_info = zf->internal_function.arg_info - 1; if (ZEND_TYPE_FULL_MASK(ret_info->type) & MAY_BE_VOID) { if (UNEXPECTED(Z_TYPE_P(ret) != IS_NULL)) { @@ -1523,7 +1523,7 @@ static ZEND_COLD void zend_verify_missing_return_type(const zend_function *zf) zend_verify_return_error(zf, NULL); } -static zend_always_inline bool zend_check_class_constant_type(zend_class_constant *c, zval *constant) +static zend_always_inline bool zend_check_class_constant_type(const zend_class_constant *c, zval *constant) { ZEND_ASSERT(!Z_ISREF_P(constant)); if (EXPECTED(ZEND_TYPE_CONTAINS_CODE(c->type, Z_TYPE_P(constant)))) { @@ -1540,7 +1540,7 @@ static zend_always_inline bool zend_check_class_constant_type(zend_class_constan return zend_verify_scalar_type_hint(type_mask, constant, true, false); } -ZEND_API bool zend_never_inline zend_verify_class_constant_type(zend_class_constant *c, const zend_string *name, zval *constant) +ZEND_API bool zend_never_inline zend_verify_class_constant_type(const zend_class_constant *c, const zend_string *name, zval *constant) { if (!zend_check_class_constant_type(c, constant)) { zend_verify_class_constant_type_error(c, name, constant); @@ -1709,7 +1709,7 @@ static zend_never_inline void zend_binary_assign_op_typed_ref(zend_reference *re } } -static zend_never_inline void zend_binary_assign_op_typed_prop(zend_property_info *prop_info, zval *zptr, zval *value OPLINE_DC EXECUTE_DATA_DC) +static zend_never_inline void zend_binary_assign_op_typed_prop(const zend_property_info *prop_info, zval *zptr, zval *value OPLINE_DC EXECUTE_DATA_DC) { zval z_copy; diff --git a/Zend/zend_execute.h b/Zend/zend_execute.h index e96a217a2904f..75c2d07e5e5cb 100644 --- a/Zend/zend_execute.h +++ b/Zend/zend_execute.h @@ -60,7 +60,7 @@ ZEND_API zend_result zend_eval_stringl_ex(const char *str, size_t str_len, zval /* export zend_pass_function to allow comparisons against it */ extern ZEND_API const zend_internal_function zend_pass_function; -ZEND_API ZEND_COLD void ZEND_FASTCALL zend_missing_arg_error(zend_execute_data *execute_data); +ZEND_API ZEND_COLD void ZEND_FASTCALL zend_missing_arg_error(const zend_execute_data *execute_data); ZEND_API ZEND_COLD void ZEND_FASTCALL zend_deprecated_function(const zend_function *fbc); ZEND_API ZEND_COLD void ZEND_FASTCALL zend_nodiscard_function(const zend_function *fbc); ZEND_API ZEND_COLD void ZEND_FASTCALL zend_deprecated_class_constant(const zend_class_constant *c, const zend_string *constant_name); @@ -95,23 +95,23 @@ ZEND_API ZEND_COLD void ZEND_FASTCALL zend_object_released_while_assigning_to_pr ZEND_API ZEND_COLD void ZEND_FASTCALL zend_cannot_add_element(void); ZEND_API bool ZEND_FASTCALL zend_asymmetric_property_has_set_access(const zend_property_info *prop_info); -ZEND_API ZEND_COLD void ZEND_FASTCALL zend_asymmetric_visibility_property_modification_error(const zend_property_info *info, const char *operation); +ZEND_API ZEND_COLD void ZEND_FASTCALL zend_asymmetric_visibility_property_modification_error(const zend_property_info *prop_info, const char *operation); ZEND_API bool zend_verify_scalar_type_hint(uint32_t type_mask, zval *arg, bool strict, bool is_internal_arg); ZEND_API ZEND_COLD void zend_verify_arg_error( - const zend_function *zf, const zend_arg_info *arg_info, uint32_t arg_num, zval *value); + const zend_function *zf, const zend_arg_info *arg_info, uint32_t arg_num, const zval *value); ZEND_API ZEND_COLD void zend_verify_return_error( - const zend_function *zf, zval *value); + const zend_function *zf, const zval *value); ZEND_API ZEND_COLD void zend_verify_never_error( const zend_function *zf); ZEND_API bool zend_verify_ref_array_assignable(zend_reference *ref); ZEND_API bool zend_check_user_type_slow( - zend_type *type, zval *arg, zend_reference *ref, void **cache_slot, bool is_return_type); + const zend_type *type, zval *arg, const zend_reference *ref, void **cache_slot, bool is_return_type); #if ZEND_DEBUG -ZEND_API bool zend_internal_call_should_throw(zend_function *fbc, zend_execute_data *call); -ZEND_API ZEND_COLD void zend_internal_call_arginfo_violation(zend_function *fbc); -ZEND_API bool zend_verify_internal_return_type(zend_function *zf, zval *ret); +ZEND_API bool zend_internal_call_should_throw(const zend_function *fbc, zend_execute_data *call); +ZEND_API ZEND_COLD void zend_internal_call_arginfo_violation(const zend_function *fbc); +ZEND_API bool zend_verify_internal_return_type(const zend_function *zf, zval *ret); #endif #define ZEND_REF_TYPE_SOURCES(ref) \ @@ -522,7 +522,7 @@ ZEND_API zend_result ZEND_FASTCALL zend_handle_undef_args(zend_execute_data *cal #define ZEND_CLASS_HAS_READONLY_PROPS(ce) ((bool)(ce->ce_flags & ZEND_ACC_HAS_READONLY_PROPS)) -ZEND_API bool zend_verify_class_constant_type(zend_class_constant *c, const zend_string *name, zval *constant); +ZEND_API bool zend_verify_class_constant_type(const zend_class_constant *c, const zend_string *name, zval *constant); ZEND_COLD void zend_verify_class_constant_type_error(const zend_class_constant *c, const zend_string *name, const zval *constant); ZEND_API bool zend_verify_property_type(const zend_property_info *info, zval *property, bool strict); diff --git a/Zend/zend_inheritance.c b/Zend/zend_inheritance.c index 53cd8533fd3e1..090b1049418d2 100644 --- a/Zend/zend_inheritance.c +++ b/Zend/zend_inheritance.c @@ -83,7 +83,7 @@ static void zend_type_list_copy_ctor( } zend_type *list_type; - ZEND_TYPE_LIST_FOREACH(new_list, list_type) { + ZEND_TYPE_LIST_FOREACH_MUTABLE(new_list, list_type) { zend_type_copy_ctor(list_type, use_arena, persistent); } ZEND_TYPE_LIST_FOREACH_END(); } @@ -362,7 +362,7 @@ static bool unlinked_instanceof(const zend_class_entry *ce1, const zend_class_en } static bool zend_type_permits_self( - zend_type type, const zend_class_entry *scope, zend_class_entry *self) { + const zend_type type, const zend_class_entry *scope, zend_class_entry *self) { if (ZEND_TYPE_FULL_MASK(type) & MAY_BE_OBJECT) { return 1; } @@ -370,7 +370,7 @@ static bool zend_type_permits_self( /* Any types that may satisfy self must have already been loaded at this point * (as a parent or interface), so we never need to register delayed variance obligations * for this case. */ - zend_type *single_type; + const zend_type *single_type; ZEND_TYPE_FOREACH(type, single_type) { if (ZEND_TYPE_HAS_NAME(*single_type)) { zend_string *name = resolve_class_name(scope, ZEND_TYPE_NAME(*single_type)); @@ -428,12 +428,12 @@ static void track_class_dependency(zend_class_entry *ce, zend_string *class_name /* Check whether any type in the fe_type intersection type is a subtype of the proto class. */ static inheritance_status zend_is_intersection_subtype_of_class( - zend_class_entry *fe_scope, zend_type fe_type, + zend_class_entry *fe_scope, const zend_type fe_type, zend_class_entry *proto_scope, zend_string *proto_class_name, zend_class_entry *proto_ce) { ZEND_ASSERT(ZEND_TYPE_IS_INTERSECTION(fe_type)); bool have_unresolved = false; - zend_type *single_type; + const zend_type *single_type; /* Traverse the list of child types and check that at least one is * a subtype of the parent type being checked */ @@ -473,7 +473,7 @@ static inheritance_status zend_is_intersection_subtype_of_class( /* Check whether a single class proto type is a subtype of a potentially complex fe_type. */ static inheritance_status zend_is_class_subtype_of_type( zend_class_entry *fe_scope, zend_string *fe_class_name, - zend_class_entry *proto_scope, zend_type proto_type) { + zend_class_entry *proto_scope, const zend_type proto_type) { zend_class_entry *fe_ce = NULL; bool have_unresolved = 0; @@ -513,7 +513,7 @@ static inheritance_status zend_is_class_subtype_of_type( } } - zend_type *single_type; + const zend_type *single_type; /* Traverse the list of parent types and check if the current child (FE) * class is the subtype of at least one of them (union) or all of them (intersection). */ @@ -584,15 +584,15 @@ static inheritance_status zend_is_class_subtype_of_type( return is_intersection ? INHERITANCE_SUCCESS : INHERITANCE_ERROR; } -static zend_string *get_class_from_type(const zend_class_entry *scope, zend_type single_type) { +static zend_string *get_class_from_type(const zend_class_entry *scope, const zend_type single_type) { if (ZEND_TYPE_HAS_NAME(single_type)) { return resolve_class_name(scope, ZEND_TYPE_NAME(single_type)); } return NULL; } -static void register_unresolved_classes(zend_class_entry *scope, zend_type type) { - zend_type *single_type; +static void register_unresolved_classes(zend_class_entry *scope, const zend_type type) { + const zend_type *single_type; ZEND_TYPE_FOREACH(type, single_type) { if (ZEND_TYPE_HAS_LIST(*single_type)) { register_unresolved_classes(scope, *single_type); @@ -606,11 +606,11 @@ static void register_unresolved_classes(zend_class_entry *scope, zend_type type) } static inheritance_status zend_is_intersection_subtype_of_type( - zend_class_entry *fe_scope, zend_type fe_type, - zend_class_entry *proto_scope, zend_type proto_type) + zend_class_entry *fe_scope, const zend_type fe_type, + zend_class_entry *proto_scope, const zend_type proto_type) { bool have_unresolved = false; - zend_type *single_type; + const zend_type *single_type; uint32_t proto_type_mask = ZEND_TYPE_PURE_MASK(proto_type); /* Currently, for object type any class name would be allowed here. @@ -672,8 +672,8 @@ static inheritance_status zend_is_intersection_subtype_of_type( } ZEND_API inheritance_status zend_perform_covariant_type_check( - zend_class_entry *fe_scope, zend_type fe_type, - zend_class_entry *proto_scope, zend_type proto_type) + zend_class_entry *fe_scope, const zend_type fe_type, + zend_class_entry *proto_scope, const zend_type proto_type) { ZEND_ASSERT(ZEND_TYPE_IS_SET(fe_type) && ZEND_TYPE_IS_SET(proto_type)); @@ -727,7 +727,7 @@ ZEND_API inheritance_status zend_perform_covariant_type_check( * We need to iterate over fe_type (U_i) first and the logic is independent of * whether proto_type is a union or intersection (only the inner check differs). */ early_exit_status = INHERITANCE_ERROR; - zend_type *single_type; + const zend_type *single_type; ZEND_TYPE_FOREACH(fe_type, single_type) { inheritance_status status; /* Union has an intersection type as it's member */ diff --git a/Zend/zend_opcode.c b/Zend/zend_opcode.c index ce052024ae7a0..b25152ec1248b 100644 --- a/Zend/zend_opcode.c +++ b/Zend/zend_opcode.c @@ -112,7 +112,7 @@ ZEND_API void destroy_zend_function(zend_function *function) ZEND_API void zend_type_release(zend_type type, bool persistent) { if (ZEND_TYPE_HAS_LIST(type)) { zend_type *list_type; - ZEND_TYPE_LIST_FOREACH(ZEND_TYPE_LIST(type), list_type) { + ZEND_TYPE_LIST_FOREACH_MUTABLE(ZEND_TYPE_LIST(type), list_type) { zend_type_release(*list_type, persistent); } ZEND_TYPE_LIST_FOREACH_END(); if (!ZEND_TYPE_USES_ARENA(type)) { diff --git a/Zend/zend_types.h b/Zend/zend_types.h index f839cec3b3667..7676a1d42a5f4 100644 --- a/Zend/zend_types.h +++ b/Zend/zend_types.h @@ -209,8 +209,14 @@ typedef struct { /* This iterates over a zend_type_list. */ #define ZEND_TYPE_LIST_FOREACH(list, type_ptr) do { \ + const zend_type *_list = (list)->types; \ + const zend_type *_end = _list + (list)->num_types; \ + for (; _list < _end; _list++) { \ + type_ptr = _list; + +#define ZEND_TYPE_LIST_FOREACH_MUTABLE(list, type_ptr) do { \ zend_type *_list = (list)->types; \ - zend_type *_end = _list + (list)->num_types; \ + const zend_type *_end = _list + (list)->num_types; \ for (; _list < _end; _list++) { \ type_ptr = _list; @@ -221,7 +227,22 @@ typedef struct { /* This iterates over any zend_type. If it's a type list, all list elements will * be visited. If it's a single type, only the single type is visited. */ #define ZEND_TYPE_FOREACH(type, type_ptr) do { \ - zend_type *_cur, *_end; \ + const zend_type *_cur, *_end; \ + if (ZEND_TYPE_HAS_LIST(type)) { \ + zend_type_list *_list = ZEND_TYPE_LIST(type); \ + _cur = _list->types; \ + _end = _cur + _list->num_types; \ + } else { \ + _cur = &(type); \ + _end = _cur + 1; \ + } \ + do { \ + type_ptr = _cur; + + +#define ZEND_TYPE_FOREACH_MUTABLE(type, type_ptr) do { \ + zend_type *_cur; \ + const zend_type *_end; \ if (ZEND_TYPE_HAS_LIST(type)) { \ zend_type_list *_list = ZEND_TYPE_LIST(type); \ _cur = _list->types; \ diff --git a/ext/opcache/ZendAccelerator.c b/ext/opcache/ZendAccelerator.c index 8caa0cbd4398e..704846c4a860f 100644 --- a/ext/opcache/ZendAccelerator.c +++ b/ext/opcache/ZendAccelerator.c @@ -626,7 +626,7 @@ static inline void accel_copy_permanent_list_types( zend_new_interned_string_func_t new_interned_string, zend_type type) { zend_type *single_type; - ZEND_TYPE_FOREACH(type, single_type) { + ZEND_TYPE_FOREACH_MUTABLE(type, single_type) { if (ZEND_TYPE_HAS_LIST(*single_type)) { ZEND_ASSERT(ZEND_TYPE_IS_INTERSECTION(*single_type)); accel_copy_permanent_list_types(new_interned_string, *single_type); diff --git a/ext/opcache/zend_file_cache.c b/ext/opcache/zend_file_cache.c index a2346a56d458e..d2b714d937dac 100644 --- a/ext/opcache/zend_file_cache.c +++ b/ext/opcache/zend_file_cache.c @@ -468,7 +468,7 @@ static void zend_file_cache_serialize_type( UNSERIALIZE_PTR(list); zend_type *list_type; - ZEND_TYPE_LIST_FOREACH(list, list_type) { + ZEND_TYPE_LIST_FOREACH_MUTABLE(list, list_type) { zend_file_cache_serialize_type(list_type, script, info, buf); } ZEND_TYPE_LIST_FOREACH_END(); } else if (ZEND_TYPE_HAS_NAME(*type)) { @@ -1348,7 +1348,7 @@ static void zend_file_cache_unserialize_type( ZEND_TYPE_SET_PTR(*type, list); zend_type *list_type; - ZEND_TYPE_LIST_FOREACH(list, list_type) { + ZEND_TYPE_LIST_FOREACH_MUTABLE(list, list_type) { zend_file_cache_unserialize_type(list_type, scope, script, buf); } ZEND_TYPE_LIST_FOREACH_END(); } else if (ZEND_TYPE_HAS_NAME(*type)) { diff --git a/ext/opcache/zend_persist.c b/ext/opcache/zend_persist.c index 3d45c63a98781..a384cdd2b9a06 100644 --- a/ext/opcache/zend_persist.c +++ b/ext/opcache/zend_persist.c @@ -367,7 +367,7 @@ static void zend_persist_type(zend_type *type) { } zend_type *single_type; - ZEND_TYPE_FOREACH(*type, single_type) { + ZEND_TYPE_FOREACH_MUTABLE(*type, single_type) { if (ZEND_TYPE_HAS_LIST(*single_type)) { zend_persist_type(single_type); continue; diff --git a/ext/opcache/zend_persist_calc.c b/ext/opcache/zend_persist_calc.c index bb4f9c7170f28..42a6cf76d62da 100644 --- a/ext/opcache/zend_persist_calc.c +++ b/ext/opcache/zend_persist_calc.c @@ -197,7 +197,7 @@ static void zend_persist_type_calc(zend_type *type) } zend_type *single_type; - ZEND_TYPE_FOREACH(*type, single_type) { + ZEND_TYPE_FOREACH_MUTABLE(*type, single_type) { if (ZEND_TYPE_HAS_LIST(*single_type)) { zend_persist_type_calc(single_type); continue; diff --git a/ext/reflection/php_reflection.c b/ext/reflection/php_reflection.c index b53b7b97c9bc9..bff617cfb61de 100644 --- a/ext/reflection/php_reflection.c +++ b/ext/reflection/php_reflection.c @@ -3170,7 +3170,7 @@ ZEND_METHOD(ReflectionUnionType, getTypes) array_init(return_value); if (ZEND_TYPE_HAS_LIST(param->type)) { - zend_type *list_type; + const zend_type *list_type; ZEND_TYPE_LIST_FOREACH(ZEND_TYPE_LIST(param->type), list_type) { append_type(return_value, *list_type); } ZEND_TYPE_LIST_FOREACH_END(); @@ -3221,7 +3221,7 @@ ZEND_METHOD(ReflectionIntersectionType, getTypes) { reflection_object *intern; type_reference *param; - zend_type *list_type; + const zend_type *list_type; ZEND_PARSE_PARAMETERS_NONE(); GET_REFLECTION_OBJECT_PTR(param);