diff options
author | Nikita Popov <nikita.ppv@gmail.com> | 2019-09-19 12:11:29 +0200 |
---|---|---|
committer | Nikita Popov <nikita.ppv@gmail.com> | 2019-09-23 15:31:35 +0200 |
commit | 9e8ba7891e74a80265e09ff5b810ec947680c1ce (patch) | |
tree | 6550898580da2445d9d54370d2adef6e68a881b3 /Zend/zend_execute.c | |
parent | 9ec2480bd6d8cadd8ca2ecac07f00fd3d411bc7a (diff) | |
download | php-git-9e8ba7891e74a80265e09ff5b810ec947680c1ce.tar.gz |
Change representation of zend_type from type code to MAY_BE_* mask
This switches zend_type from storing a single IS_* type code to
storing a MAY_BE_* type mask. Right now most code still assumes
that there is only a single type in the mask (or two together
with MAY_BE_NULL). But this will make it a lot simpler to introduce
union types.
An additional advantage (and why I'm doing this separately), is
that a number of special cases no longer need to be handled
separately: We can do a single mask & (1 << type) check to handle
all simple types, booleans (true|false) and null.
Diffstat (limited to 'Zend/zend_execute.c')
-rw-r--r-- | Zend/zend_execute.c | 320 |
1 files changed, 150 insertions, 170 deletions
diff --git a/Zend/zend_execute.c b/Zend/zend_execute.c index 1d628a2352..cf32ba55ac 100644 --- a/Zend/zend_execute.c +++ b/Zend/zend_execute.c @@ -582,39 +582,26 @@ static zend_never_inline ZEND_COLD int zend_wrong_assign_to_variable_reference(z return 1; } -static void zend_format_type(zend_type type, const char **part1, const char **part2) { - *part1 = ZEND_TYPE_ALLOW_NULL(type) ? "?" : ""; - if (ZEND_TYPE_IS_CLASS(type)) { - if (ZEND_TYPE_IS_CE(type)) { - *part2 = ZSTR_VAL(ZEND_TYPE_CE(type)->name); - } else { - *part2 = ZSTR_VAL(ZEND_TYPE_NAME(type)); - } - } else { - *part2 = zend_get_type_by_const(ZEND_TYPE_CODE(type)); - } -} - static zend_never_inline ZEND_COLD void zend_throw_auto_init_in_prop_error(zend_property_info *prop, const char *type) { - const char *prop_type1, *prop_type2; - zend_format_type(prop->type, &prop_type1, &prop_type2); + zend_string *type_str = zend_type_to_string(prop->type); zend_type_error( - "Cannot auto-initialize an %s inside property %s::$%s of type %s%s", + "Cannot auto-initialize an %s inside property %s::$%s of type %s", type, ZSTR_VAL(prop->ce->name), zend_get_unmangled_property_name(prop->name), - prop_type1, prop_type2 + ZSTR_VAL(type_str) ); + zend_string_release(type_str); } static zend_never_inline ZEND_COLD void zend_throw_auto_init_in_ref_error(zend_property_info *prop, const char *type) { - const char *prop_type1, *prop_type2; - zend_format_type(prop->type, &prop_type1, &prop_type2); + zend_string *type_str = zend_type_to_string(prop->type); zend_type_error( - "Cannot auto-initialize an %s inside a reference held by property %s::$%s of type %s%s", + "Cannot auto-initialize an %s inside a reference held by property %s::$%s of type %s", type, ZSTR_VAL(prop->ce->name), zend_get_unmangled_property_name(prop->name), - prop_type1, prop_type2 + ZSTR_VAL(type_str) ); + zend_string_release(type_str); } static zend_never_inline ZEND_COLD void zend_throw_access_uninit_prop_by_ref_error( @@ -724,22 +711,25 @@ static ZEND_COLD void zend_verify_type_error_common( *need_kind = ZSTR_VAL(ZEND_TYPE_NAME(arg_info->type)); } } else { - switch (ZEND_TYPE_CODE(arg_info->type)) { - case IS_OBJECT: + zend_type type = ZEND_TYPE_WITHOUT_NULL(arg_info->type); + switch (ZEND_TYPE_MASK(type)) { + case MAY_BE_OBJECT: *need_msg = "be an "; *need_kind = "object"; break; - case IS_CALLABLE: + case MAY_BE_CALLABLE: *need_msg = "be callable"; *need_kind = ""; break; - case IS_ITERABLE: + case MAY_BE_ITERABLE: *need_msg = "be iterable"; *need_kind = ""; break; default: + /* TODO: The zend_type_to_string() result is guaranteed interned here. + * It would be beter to switch all this code to use zend_string though. */ *need_msg = "be of the type "; - *need_kind = zend_get_type_by_const(ZEND_TYPE_CODE(arg_info->type)); + *need_kind = ZSTR_VAL(zend_type_to_string(type)); break; } } @@ -764,7 +754,7 @@ static ZEND_COLD void zend_verify_type_error_common( } } -static ZEND_COLD void zend_verify_arg_error( +ZEND_API ZEND_COLD void zend_verify_arg_error( const zend_function *zf, const zend_arg_info *arg_info, int arg_num, const zend_class_entry *ce, zval *value) { @@ -793,105 +783,100 @@ static ZEND_COLD void zend_verify_arg_error( } } -static zend_bool zend_verify_weak_scalar_type_hint(zend_uchar type_hint, zval *arg) +static zend_bool zend_verify_weak_scalar_type_hint(uint32_t type_mask, zval *arg) { - switch (type_hint) { - case _IS_BOOL: { - zend_bool dest; + if (type_mask & (MAY_BE_TRUE|MAY_BE_FALSE)) { + zend_bool dest; - if (!zend_parse_arg_bool_weak(arg, &dest)) { - return 0; - } - zval_ptr_dtor(arg); - ZVAL_BOOL(arg, dest); - return 1; + if (!zend_parse_arg_bool_weak(arg, &dest)) { + return 0; } - case IS_LONG: { - zend_long dest; + zval_ptr_dtor(arg); + ZVAL_BOOL(arg, dest); + return 1; + } + if (type_mask & MAY_BE_LONG) { + zend_long dest; - if (!zend_parse_arg_long_weak(arg, &dest)) { - return 0; - } - zval_ptr_dtor(arg); - ZVAL_LONG(arg, dest); - return 1; + if (!zend_parse_arg_long_weak(arg, &dest)) { + return 0; } - case IS_DOUBLE: { - double dest; + zval_ptr_dtor(arg); + ZVAL_LONG(arg, dest); + return 1; + } + if (type_mask & MAY_BE_DOUBLE) { + double dest; - if (!zend_parse_arg_double_weak(arg, &dest)) { - return 0; - } - zval_ptr_dtor(arg); - ZVAL_DOUBLE(arg, dest); - return 1; + if (!zend_parse_arg_double_weak(arg, &dest)) { + return 0; } - case IS_STRING: { - zend_string *dest; + zval_ptr_dtor(arg); + ZVAL_DOUBLE(arg, dest); + return 1; + } + if (type_mask & MAY_BE_STRING) { + zend_string *dest; - /* on success "arg" is converted to IS_STRING */ - return zend_parse_arg_str_weak(arg, &dest); - } - default: - return 0; + /* on success "arg" is converted to IS_STRING */ + return zend_parse_arg_str_weak(arg, &dest); } + return 0; } #if ZEND_DEBUG /* Used to sanity-check internal arginfo types without performing any actual type conversions. */ -static zend_bool zend_verify_weak_scalar_type_hint_no_sideeffect(zend_uchar type_hint, zval *arg) -{ - switch (type_hint) { - case _IS_BOOL: { - zend_bool dest; - return zend_parse_arg_bool_weak(arg, &dest); - } - case IS_LONG: { - zend_long dest; - if (Z_TYPE_P(arg) == IS_STRING) { - /* Handle this case separately to avoid the "non well-formed" warning */ - double dval; - zend_uchar type = is_numeric_string(Z_STRVAL_P(arg), Z_STRLEN_P(arg), NULL, &dval, 1); - if (type == IS_LONG) { - return 1; - } - if (type == IS_DOUBLE) { - return !zend_isnan(dval) && ZEND_DOUBLE_FITS_LONG(dval); +static zend_bool zend_verify_weak_scalar_type_hint_no_sideeffect(uint32_t type_mask, zval *arg) +{ + if (type_mask & (MAY_BE_TRUE|MAY_BE_FALSE)) { + zend_bool dest; + return zend_parse_arg_bool_weak(arg, &dest); + } + if (type_mask & MAY_BE_LONG) { + zend_long dest; + if (Z_TYPE_P(arg) == IS_STRING) { + /* Handle this case separately to avoid the "non well-formed" warning */ + double dval; + zend_uchar type = is_numeric_string(Z_STRVAL_P(arg), Z_STRLEN_P(arg), NULL, &dval, 1); + if (type == IS_LONG) { + return 1; + } + if (type == IS_DOUBLE) { + return !zend_isnan(dval) && ZEND_DOUBLE_FITS_LONG(dval); - } - return 0; } - return zend_parse_arg_long_weak(arg, &dest); + return 0; } - case IS_DOUBLE: { - double dest; - if (Z_TYPE_P(arg) == IS_STRING) { - /* Handle this case separately to avoid the "non well-formed" warning */ - return is_numeric_string(Z_STRVAL_P(arg), Z_STRLEN_P(arg), NULL, NULL, 1) != 0; - } - return zend_parse_arg_double_weak(arg, &dest); + return zend_parse_arg_long_weak(arg, &dest); + } + if (type_mask & MAY_BE_DOUBLE) { + double dest; + if (Z_TYPE_P(arg) == IS_STRING) { + /* Handle this case separately to avoid the "non well-formed" warning */ + return is_numeric_string(Z_STRVAL_P(arg), Z_STRLEN_P(arg), NULL, NULL, 1) != 0; } - case IS_STRING: - /* We don't call cast_object here, because this check must be side-effect free. As this - * is only used for a sanity check of arginfo/zpp consistency, it's okay if we accept - * more than actually allowed here. */ - return Z_TYPE_P(arg) < IS_STRING || Z_TYPE_P(arg) == IS_OBJECT; - default: - return 0; + return zend_parse_arg_double_weak(arg, &dest); + } + if (type_mask & MAY_BE_STRING) { + /* We don't call cast_object here, because this check must be side-effect free. As this + * is only used for a sanity check of arginfo/zpp consistency, it's okay if we accept + * more than actually allowed here. */ + return Z_TYPE_P(arg) < IS_STRING || Z_TYPE_P(arg) == IS_OBJECT; } + return 0; } #endif -static zend_bool zend_verify_scalar_type_hint(zend_uchar type_hint, zval *arg, zend_bool strict, zend_bool is_internal_arg) +ZEND_API zend_bool zend_verify_scalar_type_hint(uint32_t type_mask, zval *arg, zend_bool strict, zend_bool is_internal_arg) { if (UNEXPECTED(strict)) { /* SSTH Exception: IS_LONG may be accepted as IS_DOUBLE (converted) */ - if (type_hint != IS_DOUBLE || Z_TYPE_P(arg) != IS_LONG) { + if (!(type_mask & MAY_BE_DOUBLE) || Z_TYPE_P(arg) != IS_LONG) { return 0; } } else if (UNEXPECTED(Z_TYPE_P(arg) == IS_NULL)) { /* NULL may be accepted only by nullable hints (this is already checked) */ - if (is_internal_arg && (type_hint <= IS_STRING || type_hint == _IS_BOOL)) { + if (is_internal_arg && (type_mask & (MAY_BE_TRUE|MAY_BE_FALSE|MAY_BE_LONG|MAY_BE_DOUBLE|MAY_BE_STRING))) { /* As an exception, null is allowed for scalar types in weak mode. */ return 1; } @@ -899,15 +884,15 @@ static zend_bool zend_verify_scalar_type_hint(zend_uchar type_hint, zval *arg, z } #if ZEND_DEBUG if (is_internal_arg) { - return zend_verify_weak_scalar_type_hint_no_sideeffect(type_hint, arg); + return zend_verify_weak_scalar_type_hint_no_sideeffect(type_mask, arg); } #endif - return zend_verify_weak_scalar_type_hint(type_hint, arg); + return zend_verify_weak_scalar_type_hint(type_mask, arg); } ZEND_COLD zend_never_inline void zend_verify_property_type_error(zend_property_info *info, zval *property) { - const char *prop_type1, *prop_type2; + zend_string *type_str; /* we _may_ land here in case reading already errored and runtime cache thus has not been updated (i.e. it contains a valid but unrelated info) */ if (EG(exception)) { @@ -915,23 +900,23 @@ ZEND_COLD zend_never_inline void zend_verify_property_type_error(zend_property_i } // TODO Switch to a more standard error message? - zend_format_type(info->type, &prop_type1, &prop_type2); - (void) prop_type1; + type_str = zend_type_to_string(ZEND_TYPE_WITHOUT_NULL(info->type)); if (ZEND_TYPE_IS_CLASS(info->type)) { zend_type_error("Typed property %s::$%s must be an instance of %s%s, %s used", ZSTR_VAL(info->ce->name), zend_get_unmangled_property_name(info->name), - prop_type2, + ZSTR_VAL(type_str), ZEND_TYPE_ALLOW_NULL(info->type) ? " or null" : "", Z_TYPE_P(property) == IS_OBJECT ? ZSTR_VAL(Z_OBJCE_P(property)->name) : zend_get_type_by_const(Z_TYPE_P(property))); } else { zend_type_error("Typed property %s::$%s must be %s%s, %s used", ZSTR_VAL(info->ce->name), zend_get_unmangled_property_name(info->name), - prop_type2, + ZSTR_VAL(type_str), ZEND_TYPE_ALLOW_NULL(info->type) ? " or null" : "", Z_TYPE_P(property) == IS_OBJECT ? ZSTR_VAL(Z_OBJCE_P(property)->name) : zend_get_type_by_const(Z_TYPE_P(property))); } + zend_string_release(type_str); } static zend_bool zend_resolve_class_type(zend_type *type, zend_class_entry *self_ce) { @@ -979,17 +964,13 @@ static zend_always_inline zend_bool i_zend_check_property_type(zend_property_inf return instanceof_function(Z_OBJCE_P(property), ZEND_TYPE_CE(info->type)); } - ZEND_ASSERT(ZEND_TYPE_CODE(info->type) != IS_CALLABLE); - if (EXPECTED(ZEND_TYPE_CODE(info->type) == Z_TYPE_P(property))) { - return 1; - } else if (EXPECTED(Z_TYPE_P(property) == IS_NULL)) { - return ZEND_TYPE_ALLOW_NULL(info->type); - } else if (ZEND_TYPE_CODE(info->type) == _IS_BOOL && EXPECTED(Z_TYPE_P(property) == IS_FALSE || Z_TYPE_P(property) == IS_TRUE)) { + ZEND_ASSERT(!(ZEND_TYPE_MASK(info->type) & MAY_BE_CALLABLE)); + if (EXPECTED(ZEND_TYPE_CONTAINS_CODE(info->type, Z_TYPE_P(property)))) { return 1; - } else if (ZEND_TYPE_CODE(info->type) == IS_ITERABLE) { + } else if (ZEND_TYPE_MASK(info->type) & MAY_BE_ITERABLE) { return zend_is_iterable(property); } else { - return zend_verify_scalar_type_hint(ZEND_TYPE_CODE(info->type), property, strict, 0); + return zend_verify_scalar_type_hint(ZEND_TYPE_MASK(info->type), property, strict, 0); } } @@ -1030,6 +1011,7 @@ static zend_always_inline zend_bool zend_check_type( zend_bool is_return_type, zend_bool is_internal) { zend_reference *ref = NULL; + uint32_t type_mask; if (!ZEND_TYPE_IS_SET(type)) { return 1; @@ -1054,22 +1036,15 @@ static zend_always_inline zend_bool zend_check_type( return instanceof_function(Z_OBJCE_P(arg), *ce); } return Z_TYPE_P(arg) == IS_NULL && ZEND_TYPE_ALLOW_NULL(type); - } else if (EXPECTED(ZEND_TYPE_CODE(type) == Z_TYPE_P(arg))) { - return 1; - } - - if (Z_TYPE_P(arg) == IS_NULL && ZEND_TYPE_ALLOW_NULL(type)) { - /* Null passed to nullable type */ + } else if (EXPECTED(ZEND_TYPE_CONTAINS_CODE(type, Z_TYPE_P(arg)))) { return 1; } - if (ZEND_TYPE_CODE(type) == IS_CALLABLE) { + type_mask = ZEND_TYPE_MASK(type); + if (type_mask & MAY_BE_CALLABLE) { return zend_is_callable(arg, IS_CALLABLE_CHECK_SILENT, NULL); - } else if (ZEND_TYPE_CODE(type) == IS_ITERABLE) { + } else if (type_mask & MAY_BE_ITERABLE) { return zend_is_iterable(arg); - } else if (ZEND_TYPE_CODE(type) == _IS_BOOL && - EXPECTED(Z_TYPE_P(arg) == IS_FALSE || Z_TYPE_P(arg) == IS_TRUE)) { - return 1; } else if (ref && ZEND_REF_HAS_TYPE_SOURCES(ref)) { return 0; /* we cannot have conversions for typed refs */ } else if (is_internal && is_return_type) { @@ -1078,7 +1053,7 @@ static zend_always_inline zend_bool zend_check_type( * apply coercions. */ return 0; } else { - return zend_verify_scalar_type_hint(ZEND_TYPE_CODE(type), arg, + return zend_verify_scalar_type_hint(type_mask, arg, is_return_type ? ZEND_RET_USES_STRICT_TYPES() : ZEND_ARG_USES_STRICT_TYPES(), is_internal); } @@ -1229,7 +1204,7 @@ static int zend_verify_internal_return_type(zend_function *zf, zval *ret) zend_class_entry *ce = NULL; void *dummy_cache_slot = NULL; - if (ZEND_TYPE_CODE(ret_info->type) == IS_VOID) { + if (ZEND_TYPE_IS_MASK(ret_info->type) && (ZEND_TYPE_MASK(ret_info->type) & MAY_BE_VOID)) { if (UNEXPECTED(Z_TYPE_P(ret) != IS_NULL)) { zend_verify_void_return_error(zf, zend_zval_type_name(ret), ""); return 0; @@ -1260,7 +1235,9 @@ static ZEND_COLD int zend_verify_missing_return_type(const zend_function *zf, vo { zend_arg_info *ret_info = zf->common.arg_info - 1; - if (ZEND_TYPE_IS_SET(ret_info->type) && UNEXPECTED(ZEND_TYPE_CODE(ret_info->type) != IS_VOID)) { + if (ZEND_TYPE_IS_SET(ret_info->type) + && (!ZEND_TYPE_IS_MASK(ret_info->type) + || !(ZEND_TYPE_MASK(ret_info->type) & MAY_BE_VOID))) { zend_class_entry *ce = NULL; if (ZEND_TYPE_IS_CLASS(ret_info->type)) { if (EXPECTED(*cache_slot)) { @@ -1610,7 +1587,7 @@ static zend_property_info *zend_get_prop_not_accepting_double(zend_reference *re { zend_property_info *prop; ZEND_REF_FOREACH_TYPE_SOURCES(ref, prop) { - if (ZEND_TYPE_CODE(prop->type) != IS_DOUBLE) { + if (!ZEND_TYPE_IS_MASK(prop->type) || !(ZEND_TYPE_MASK(prop->type) & MAY_BE_DOUBLE)) { return prop; } } ZEND_REF_FOREACH_TYPE_SOURCES_END(); @@ -1641,19 +1618,20 @@ static ZEND_COLD zend_long zend_throw_incdec_ref_error(zend_reference *ref OPLIN } static ZEND_COLD zend_long zend_throw_incdec_prop_error(zend_property_info *prop OPLINE_DC) { - const char *prop_type1, *prop_type2; - zend_format_type(prop->type, &prop_type1, &prop_type2); + zend_string *type_str = zend_type_to_string(prop->type); if (ZEND_IS_INCREMENT(opline->opcode)) { - zend_type_error("Cannot increment property %s::$%s of type %s%s past its maximal value", + zend_type_error("Cannot increment property %s::$%s of type %s past its maximal value", ZSTR_VAL(prop->ce->name), zend_get_unmangled_property_name(prop->name), - prop_type1, prop_type2); + ZSTR_VAL(type_str)); + zend_string_release(type_str); return ZEND_LONG_MAX; } else { - zend_type_error("Cannot decrement property %s::$%s of type %s%s past its minimal value", + zend_type_error("Cannot decrement property %s::$%s of type %s past its minimal value", ZSTR_VAL(prop->ce->name), zend_get_unmangled_property_name(prop->name), - prop_type1, prop_type2); + ZSTR_VAL(type_str)); + zend_string_release(type_str); return ZEND_LONG_MIN; } } @@ -2578,8 +2556,7 @@ static zend_always_inline zend_bool check_type_array_assignable(zend_type type) if (!type) { return 1; } - return ZEND_TYPE_IS_CODE(type) - && (ZEND_TYPE_CODE(type) == IS_ARRAY || ZEND_TYPE_CODE(type) == IS_ITERABLE); + return ZEND_TYPE_IS_MASK(type) && (ZEND_TYPE_MASK(type) & (MAY_BE_ITERABLE|MAY_BE_ARRAY)); } static zend_always_inline zend_bool check_type_stdClass_assignable(zend_type type) { @@ -2593,7 +2570,7 @@ static zend_always_inline zend_bool check_type_stdClass_assignable(zend_type typ return zend_string_equals_literal_ci(ZEND_TYPE_NAME(type), "stdclass"); } } else { - return ZEND_TYPE_CODE(type) == IS_OBJECT; + return (ZEND_TYPE_MASK(type) & MAY_BE_OBJECT) != 0; } } @@ -2987,58 +2964,59 @@ static zend_always_inline int zend_fetch_static_property_address(zval **retval, } ZEND_API ZEND_COLD void zend_throw_ref_type_error_type(zend_property_info *prop1, zend_property_info *prop2, zval *zv) { - const char *prop1_type1, *prop1_type2, *prop2_type1, *prop2_type2; - zend_format_type(prop1->type, &prop1_type1, &prop1_type2); - zend_format_type(prop2->type, &prop2_type1, &prop2_type2); - zend_type_error("Reference with value of type %s held by property %s::$%s of type %s%s is not compatible with property %s::$%s of type %s%s", + zend_string *type1_str = zend_type_to_string(prop1->type); + zend_string *type2_str = zend_type_to_string(prop2->type); + zend_type_error("Reference with value of type %s held by property %s::$%s of type %s is not compatible with property %s::$%s of type %s", Z_TYPE_P(zv) == IS_OBJECT ? ZSTR_VAL(Z_OBJCE_P(zv)->name) : zend_get_type_by_const(Z_TYPE_P(zv)), ZSTR_VAL(prop1->ce->name), zend_get_unmangled_property_name(prop1->name), - prop1_type1, prop1_type2, + ZSTR_VAL(type1_str), ZSTR_VAL(prop2->ce->name), zend_get_unmangled_property_name(prop2->name), - prop2_type1, prop2_type2 + ZSTR_VAL(type2_str) ); + zend_string_release(type1_str); + zend_string_release(type2_str); } ZEND_API ZEND_COLD void zend_throw_ref_type_error_zval(zend_property_info *prop, zval *zv) { - const char *prop_type1, *prop_type2; - zend_format_type(prop->type, &prop_type1, &prop_type2); - zend_type_error("Cannot assign %s to reference held by property %s::$%s of type %s%s", + zend_string *type_str = zend_type_to_string(prop->type); + zend_type_error("Cannot assign %s to reference held by property %s::$%s of type %s", Z_TYPE_P(zv) == IS_OBJECT ? ZSTR_VAL(Z_OBJCE_P(zv)->name) : zend_get_type_by_const(Z_TYPE_P(zv)), ZSTR_VAL(prop->ce->name), zend_get_unmangled_property_name(prop->name), - prop_type1, prop_type2 + ZSTR_VAL(type_str) ); + zend_string_release(type_str); } ZEND_API ZEND_COLD void zend_throw_conflicting_coercion_error(zend_property_info *prop1, zend_property_info *prop2, zval *zv) { - const char *prop1_type1, *prop1_type2, *prop2_type1, *prop2_type2; - zend_format_type(prop1->type, &prop1_type1, &prop1_type2); - zend_format_type(prop2->type, &prop2_type1, &prop2_type2); - zend_type_error("Cannot assign %s to reference held by property %s::$%s of type %s%s and property %s::$%s of type %s%s, as this would result in an inconsistent type conversion", + zend_string *type1_str = zend_type_to_string(prop1->type); + zend_string *type2_str = zend_type_to_string(prop2->type); + zend_type_error("Cannot assign %s to reference held by property %s::$%s of type %s and property %s::$%s of type %s, as this would result in an inconsistent type conversion", Z_TYPE_P(zv) == IS_OBJECT ? ZSTR_VAL(Z_OBJCE_P(zv)->name) : zend_get_type_by_const(Z_TYPE_P(zv)), ZSTR_VAL(prop1->ce->name), zend_get_unmangled_property_name(prop1->name), - prop1_type1, prop1_type2, + ZSTR_VAL(type1_str), ZSTR_VAL(prop2->ce->name), zend_get_unmangled_property_name(prop2->name), - prop2_type1, prop2_type2 + ZSTR_VAL(type2_str) ); + zend_string_release(type1_str); + zend_string_release(type2_str); } /* 1: valid, 0: invalid, -1: may be valid after type coercion */ static zend_always_inline int i_zend_verify_type_assignable_zval( zend_type *type_ptr, zend_class_entry *self_ce, zval *zv, zend_bool strict) { zend_type type = *type_ptr; - zend_uchar type_code; + uint32_t type_mask; zend_uchar zv_type = Z_TYPE_P(zv); - if (ZEND_TYPE_ALLOW_NULL(type) && zv_type == IS_NULL) { - return 1; - } - if (ZEND_TYPE_IS_CLASS(type)) { + if (ZEND_TYPE_ALLOW_NULL(type) && zv_type == IS_NULL) { + return 1; + } if (!ZEND_TYPE_IS_CE(type)) { if (!zend_resolve_class_type(type_ptr, self_ce)) { return 0; @@ -3048,26 +3026,25 @@ static zend_always_inline int i_zend_verify_type_assignable_zval( return zv_type == IS_OBJECT && instanceof_function(Z_OBJCE_P(zv), ZEND_TYPE_CE(type)); } - type_code = ZEND_TYPE_CODE(type); - if (type_code == zv_type || - (type_code == _IS_BOOL && (zv_type == IS_FALSE || zv_type == IS_TRUE))) { + if (ZEND_TYPE_CONTAINS_CODE(type, zv_type)) { return 1; } - if (type_code == IS_ITERABLE) { + type_mask = ZEND_TYPE_MASK(type); + if (type_mask & MAY_BE_ITERABLE) { return zend_is_iterable(zv); } /* SSTH Exception: IS_LONG may be accepted as IS_DOUBLE (converted) */ if (strict) { - if (type_code == IS_DOUBLE && zv_type == IS_LONG) { + if ((type_mask & MAY_BE_DOUBLE) && zv_type == IS_LONG) { return -1; } return 0; } /* No weak conversions for arrays and objects */ - if (type_code == IS_ARRAY || type_code == IS_OBJECT) { + if (type_mask & (MAY_BE_ARRAY|MAY_BE_OBJECT)) { return 0; } @@ -3089,7 +3066,7 @@ ZEND_API zend_bool ZEND_FASTCALL zend_verify_ref_assignable_zval(zend_reference * must be the same (modulo nullability). To handle this, remember the first type we see and * compare against it when coercion becomes necessary. */ zend_property_info *seen_prop = NULL; - zend_uchar seen_type; + uint32_t seen_type_mask; zend_bool needs_coercion = 0; ZEND_ASSERT(Z_TYPE_P(zv) != IS_REFERENCE); @@ -3106,14 +3083,16 @@ ZEND_API zend_bool ZEND_FASTCALL zend_verify_ref_assignable_zval(zend_reference if (!seen_prop) { seen_prop = prop; - seen_type = ZEND_TYPE_IS_CLASS(prop->type) ? IS_OBJECT : ZEND_TYPE_CODE(prop->type); - } else if (needs_coercion && seen_type != ZEND_TYPE_CODE(prop->type)) { + seen_type_mask = ZEND_TYPE_IS_CLASS(prop->type) + ? MAY_BE_OBJECT : ZEND_TYPE_MASK(ZEND_TYPE_WITHOUT_NULL(prop->type)); + } else if (needs_coercion + && seen_type_mask != ZEND_TYPE_MASK(ZEND_TYPE_WITHOUT_NULL(prop->type))) { zend_throw_conflicting_coercion_error(seen_prop, prop, zv); return 0; } } ZEND_REF_FOREACH_TYPE_SOURCES_END(); - if (UNEXPECTED(needs_coercion && !zend_verify_weak_scalar_type_hint(seen_type, zv))) { + if (UNEXPECTED(needs_coercion && !zend_verify_weak_scalar_type_hint(seen_type_mask, zv))) { zend_throw_ref_type_error_zval(seen_prop, zv); return 0; } @@ -3174,12 +3153,13 @@ ZEND_API zend_bool ZEND_FASTCALL zend_verify_prop_assignable_by_ref(zend_propert if (result < 0) { zend_property_info *ref_prop = ZEND_REF_FIRST_SOURCE(Z_REF_P(orig_val)); - if (ZEND_TYPE_CODE(prop_info->type) != ZEND_TYPE_CODE(ref_prop->type)) { + if (ZEND_TYPE_MASK(ZEND_TYPE_WITHOUT_NULL(prop_info->type)) + != ZEND_TYPE_MASK(ZEND_TYPE_WITHOUT_NULL(ref_prop->type))) { /* Invalid due to conflicting coercion */ zend_throw_ref_type_error_type(ref_prop, prop_info, val); return 0; } - if (zend_verify_weak_scalar_type_hint(ZEND_TYPE_CODE(prop_info->type), val)) { + if (zend_verify_weak_scalar_type_hint(ZEND_TYPE_MASK(prop_info->type), val)) { return 1; } } |