diff options
Diffstat (limited to 'Zend/zend_inheritance.c')
-rw-r--r-- | Zend/zend_inheritance.c | 170 |
1 files changed, 76 insertions, 94 deletions
diff --git a/Zend/zend_inheritance.c b/Zend/zend_inheritance.c index fd1345f844..7689a7c963 100644 --- a/Zend/zend_inheritance.c +++ b/Zend/zend_inheritance.c @@ -23,7 +23,7 @@ #include "zend_execute.h" #include "zend_inheritance.h" #include "zend_smart_str.h" -#include "zend_inheritance.h" +#include "zend_operators.h" static void overriden_ptr_dtor(zval *zv) /* {{{ */ { @@ -169,64 +169,50 @@ char *zend_visibility_string(uint32_t fn_flags) /* {{{ */ static zend_always_inline zend_bool zend_iterable_compatibility_check(zend_arg_info *arg_info) /* {{{ */ { - if (arg_info->type_hint == IS_ARRAY) { + if (ZEND_TYPE_CODE(arg_info->type) == IS_ARRAY) { return 1; } - - if (arg_info->class_name && zend_string_equals_literal_ci(arg_info->class_name, "Traversable")) { + + if (ZEND_TYPE_IS_CLASS(arg_info->type) && zend_string_equals_literal_ci(ZEND_TYPE_NAME(arg_info->type), "Traversable")) { return 1; } - + return 0; } /* }}} */ static int zend_do_perform_type_hint_check(const zend_function *fe, zend_arg_info *fe_arg_info, const zend_function *proto, zend_arg_info *proto_arg_info) /* {{{ */ { - if (ZEND_LOG_XOR(fe_arg_info->class_name, proto_arg_info->class_name)) { - /* Only one has a type declaration and the other one doesn't */ - return 0; - } + ZEND_ASSERT(ZEND_TYPE_IS_SET(fe_arg_info->type) && ZEND_TYPE_IS_SET(proto_arg_info->type)); - if (fe_arg_info->class_name) { + if (ZEND_TYPE_IS_CLASS(fe_arg_info->type) && ZEND_TYPE_IS_CLASS(proto_arg_info->type)) { zend_string *fe_class_name, *proto_class_name; const char *class_name; + size_t class_name_len; - if (fe->type == ZEND_INTERNAL_FUNCTION) { - fe_class_name = NULL; - class_name = ((zend_internal_arg_info*)fe_arg_info)->class_name; - } else { - fe_class_name = fe_arg_info->class_name; - class_name = ZSTR_VAL(fe_arg_info->class_name); - } - if (!strcasecmp(class_name, "parent") && proto->common.scope) { + fe_class_name = ZEND_TYPE_NAME(fe_arg_info->type); + class_name = ZSTR_VAL(fe_class_name); + class_name_len = ZSTR_LEN(fe_class_name); + if (class_name_len == sizeof("parent")-1 && !strcasecmp(class_name, "parent") && proto->common.scope) { fe_class_name = zend_string_copy(proto->common.scope->name); - } else if (!strcasecmp(class_name, "self") && fe->common.scope) { + } else if (class_name_len == sizeof("self")-1 && !strcasecmp(class_name, "self") && fe->common.scope) { fe_class_name = zend_string_copy(fe->common.scope->name); - } else if (fe_class_name) { - zend_string_addref(fe_class_name); } else { - fe_class_name = zend_string_init(class_name, strlen(class_name), 0); + zend_string_addref(fe_class_name); } - if (proto->type == ZEND_INTERNAL_FUNCTION) { - proto_class_name = NULL; - class_name = ((zend_internal_arg_info*)proto_arg_info)->class_name; - } else { - proto_class_name = proto_arg_info->class_name; - class_name = ZSTR_VAL(proto_arg_info->class_name); - } - if (!strcasecmp(class_name, "parent") && proto->common.scope && proto->common.scope->parent) { + proto_class_name = ZEND_TYPE_NAME(proto_arg_info->type); + class_name = ZSTR_VAL(proto_class_name); + class_name_len = ZSTR_LEN(proto_class_name); + if (class_name_len == sizeof("parent")-1 && !strcasecmp(class_name, "parent") && proto->common.scope && proto->common.scope->parent) { proto_class_name = zend_string_copy(proto->common.scope->parent->name); - } else if (!strcasecmp(class_name, "self") && proto->common.scope) { + } else if (class_name_len == sizeof("self")-1 && !strcasecmp(class_name, "self") && proto->common.scope) { proto_class_name = zend_string_copy(proto->common.scope->name); - } else if (proto_class_name) { - zend_string_addref(proto_class_name); } else { - proto_class_name = zend_string_init(class_name, strlen(class_name), 0); + zend_string_addref(proto_class_name); } - if (strcasecmp(ZSTR_VAL(fe_class_name), ZSTR_VAL(proto_class_name)) != 0) { + if (fe_class_name != proto_class_name && strcasecmp(ZSTR_VAL(fe_class_name), ZSTR_VAL(proto_class_name)) != 0) { if (fe->common.type != ZEND_USER_FUNCTION) { zend_string_release(proto_class_name); zend_string_release(fe_class_name); @@ -250,14 +236,28 @@ static int zend_do_perform_type_hint_check(const zend_function *fe, zend_arg_inf } zend_string_release(proto_class_name); zend_string_release(fe_class_name); + } else if (ZEND_TYPE_CODE(fe_arg_info->type) != ZEND_TYPE_CODE(proto_arg_info->type)) { + /* Incompatible built-in types */ + return 0; } - if (fe_arg_info->type_hint != proto_arg_info->type_hint) { - /* Incompatible type */ + return 1; +} +/* }}} */ + +static int zend_do_perform_arg_type_hint_check(const zend_function *fe, zend_arg_info *fe_arg_info, const zend_function *proto, zend_arg_info *proto_arg_info) /* {{{ */ +{ + if (!ZEND_TYPE_IS_SET(fe_arg_info->type)) { + /* Child with no type is always compatible */ + return 1; + } + + if (!ZEND_TYPE_IS_SET(proto_arg_info->type)) { + /* Child defines a type, but parent doesn't, violates LSP */ return 0; } - return 1; + return zend_do_perform_type_hint_check(fe, fe_arg_info, proto, proto_arg_info); } /* }}} */ @@ -328,22 +328,22 @@ static zend_bool zend_do_perform_implementation_check(const zend_function *fe, c } else { proto_arg_info = &proto->common.arg_info[proto->common.num_args]; } - - if (!zend_do_perform_type_hint_check(fe, fe_arg_info, proto, proto_arg_info)) { - switch (fe_arg_info->type_hint) { + + if (!zend_do_perform_arg_type_hint_check(fe, fe_arg_info, proto, proto_arg_info)) { + switch (ZEND_TYPE_CODE(fe_arg_info->type)) { case IS_ITERABLE: if (!zend_iterable_compatibility_check(proto_arg_info)) { return 0; } break; - + default: return 0; } } // This introduces BC break described at https://bugs.php.net/bug.php?id=72119 - if (proto_arg_info->type_hint && proto_arg_info->allow_null && !fe_arg_info->allow_null) { + if (ZEND_TYPE_IS_SET(proto_arg_info->type) && ZEND_TYPE_ALLOW_NULL(proto_arg_info->type) && !ZEND_TYPE_ALLOW_NULL(fe_arg_info->type)) { /* incompatible nullability */ return 0; } @@ -361,21 +361,21 @@ static zend_bool zend_do_perform_implementation_check(const zend_function *fe, c if (!(fe->common.fn_flags & ZEND_ACC_HAS_RETURN_TYPE)) { return 0; } - + if (!zend_do_perform_type_hint_check(fe, fe->common.arg_info - 1, proto, proto->common.arg_info - 1)) { - switch (proto->common.arg_info[-1].type_hint) { + switch (ZEND_TYPE_CODE(proto->common.arg_info[-1].type)) { case IS_ITERABLE: if (!zend_iterable_compatibility_check(fe->common.arg_info - 1)) { return 0; } break; - + default: return 0; } } - if (fe->common.arg_info[-1].allow_null && !proto->common.arg_info[-1].allow_null) { + if (ZEND_TYPE_ALLOW_NULL(fe->common.arg_info[-1].type) && !ZEND_TYPE_ALLOW_NULL(proto->common.arg_info[-1].type)) { return 0; } } @@ -386,21 +386,16 @@ static zend_bool zend_do_perform_implementation_check(const zend_function *fe, c static ZEND_COLD void zend_append_type_hint(smart_str *str, const zend_function *fptr, zend_arg_info *arg_info, int return_hint) /* {{{ */ { - if (arg_info->type_hint != IS_UNDEF && arg_info->allow_null) { + if (ZEND_TYPE_IS_SET(arg_info->type) && ZEND_TYPE_ALLOW_NULL(arg_info->type)) { smart_str_appendc(str, '?'); } - if (arg_info->class_name) { + if (ZEND_TYPE_IS_CLASS(arg_info->type)) { const char *class_name; size_t class_name_len; - if (fptr->type == ZEND_INTERNAL_FUNCTION) { - class_name = ((zend_internal_arg_info*)arg_info)->class_name; - class_name_len = strlen(class_name); - } else { - class_name = ZSTR_VAL(arg_info->class_name); - class_name_len = ZSTR_LEN(arg_info->class_name); - } + class_name = ZSTR_VAL(ZEND_TYPE_NAME(arg_info->type)); + class_name_len = ZSTR_LEN(ZEND_TYPE_NAME(arg_info->type)); if (!strcasecmp(class_name, "self") && fptr->common.scope) { class_name = ZSTR_VAL(fptr->common.scope->name); @@ -414,13 +409,13 @@ static ZEND_COLD void zend_append_type_hint(smart_str *str, const zend_function if (!return_hint) { smart_str_appendc(str, ' '); } - } else if (arg_info->type_hint) { - if (arg_info->type_hint == IS_LONG) { + } else if (ZEND_TYPE_IS_CODE(arg_info->type)) { + if (ZEND_TYPE_CODE(arg_info->type) == IS_LONG) { smart_str_appendl(str, "int", 3); - } else if (arg_info->type_hint == _IS_BOOL) { + } else if (ZEND_TYPE_CODE(arg_info->type) == _IS_BOOL) { smart_str_appendl(str, "bool", 4); } else { - const char *type_name = zend_get_type_by_const(arg_info->type_hint); + const char *type_name = zend_get_type_by_const(ZEND_TYPE_CODE(arg_info->type)); smart_str_appends(str, type_name); } if (!return_hint) { @@ -556,16 +551,6 @@ static void do_inheritance_check_on_method(zend_function *child, zend_function * uint32_t child_flags; uint32_t parent_flags = parent->common.fn_flags; - if ((parent->common.scope->ce_flags & ZEND_ACC_INTERFACE) == 0 - && parent->common.fn_flags & ZEND_ACC_ABSTRACT - && parent->common.scope != (child->common.prototype ? child->common.prototype->common.scope : child->common.scope) - && child->common.fn_flags & (ZEND_ACC_ABSTRACT|ZEND_ACC_IMPLEMENTED_ABSTRACT)) { - zend_error_noreturn(E_COMPILE_ERROR, "Can't inherit abstract function %s::%s() (previously declared abstract in %s)", - ZSTR_VAL(parent->common.scope->name), - ZSTR_VAL(child->common.function_name), - child->common.prototype ? ZSTR_VAL(child->common.prototype->common.scope->name) : ZSTR_VAL(child->common.scope->name)); - } - if (UNEXPECTED(parent_flags & ZEND_ACC_FINAL)) { zend_error_noreturn(E_COMPILE_ERROR, "Cannot override final method %s::%s()", ZEND_FN_SCOPE_NAME(parent), ZSTR_VAL(child->common.function_name)); } @@ -586,8 +571,9 @@ static void do_inheritance_check_on_method(zend_function *child, zend_function * zend_error_noreturn(E_COMPILE_ERROR, "Cannot make non abstract method %s::%s() abstract in class %s", ZEND_FN_SCOPE_NAME(parent), ZSTR_VAL(child->common.function_name), ZEND_FN_SCOPE_NAME(child)); } - /* Prevent derived classes from restricting access that was available in parent classes */ - if (UNEXPECTED((child_flags & ZEND_ACC_PPP_MASK) > (parent_flags & ZEND_ACC_PPP_MASK))) { + /* Prevent derived classes from restricting access that was available in parent classes (except deriving from non-abstract ctors) */ + if (UNEXPECTED((!(child_flags & ZEND_ACC_CTOR) || (parent_flags & (ZEND_ACC_ABSTRACT | ZEND_ACC_IMPLEMENTED_ABSTRACT))) && + (child_flags & ZEND_ACC_PPP_MASK) > (parent_flags & ZEND_ACC_PPP_MASK))) { zend_error_noreturn(E_COMPILE_ERROR, "Access level to %s::%s() must be %s (as in class %s)%s", ZEND_FN_SCOPE_NAME(child), ZSTR_VAL(child->common.function_name), zend_visibility_string(parent_flags), ZEND_FN_SCOPE_NAME(parent), (parent_flags&ZEND_ACC_PUBLIC) ? "" : " or weaker"); } @@ -607,13 +593,12 @@ static void do_inheritance_check_on_method(zend_function *child, zend_function * } else if (!(parent->common.fn_flags & ZEND_ACC_CTOR) || (parent->common.prototype && (parent->common.prototype->common.scope->ce_flags & ZEND_ACC_INTERFACE))) { /* ctors only have a prototype if it comes from an interface */ child->common.prototype = parent->common.prototype ? parent->common.prototype : parent; + /* and if that is the case, we want to check inheritance against it */ + if (parent->common.fn_flags & ZEND_ACC_CTOR) { + parent = child->common.prototype; + } } - if (child->common.prototype && ( - child->common.prototype->common.fn_flags & ZEND_ACC_ABSTRACT - )) { - parent = child->common.prototype; - } if (UNEXPECTED(!zend_do_perform_implementation_check(child, parent))) { int error_level; const char *error_verb; @@ -628,7 +613,7 @@ static void do_inheritance_check_on_method(zend_function *child, zend_function * } else if ((parent->common.fn_flags & ZEND_ACC_HAS_RETURN_TYPE) && (!(child->common.fn_flags & ZEND_ACC_HAS_RETURN_TYPE) || !zend_do_perform_type_hint_check(child, child->common.arg_info - 1, parent, parent->common.arg_info - 1) || - (child->common.arg_info[-1].allow_null && !parent->common.arg_info[-1].allow_null))) { + (ZEND_TYPE_ALLOW_NULL(child->common.arg_info[-1].type) && !ZEND_TYPE_ALLOW_NULL(parent->common.arg_info[-1].type)))) { error_level = E_COMPILE_ERROR; error_verb = "must"; } else { @@ -1103,7 +1088,6 @@ static zend_bool zend_traits_method_compatibility_check(zend_function *fn, zend_ uint32_t other_flags = other_fn->common.scope->ce_flags; return zend_do_perform_implementation_check(fn, other_fn) - && ((other_fn->common.scope->ce_flags & ZEND_ACC_INTERFACE) || zend_do_perform_implementation_check(other_fn, fn)) && ((fn_flags & (ZEND_ACC_FINAL|ZEND_ACC_STATIC)) == (other_flags & (ZEND_ACC_FINAL|ZEND_ACC_STATIC))); /* equal final and static qualifier */ } @@ -1112,7 +1096,7 @@ static zend_bool zend_traits_method_compatibility_check(zend_function *fn, zend_ static void zend_add_magic_methods(zend_class_entry* ce, zend_string* mname, zend_function* fe) /* {{{ */ { if (zend_string_equals_literal(mname, ZEND_CLONE_FUNC_NAME)) { - ce->clone = fe; fe->common.fn_flags |= ZEND_ACC_CLONE; + ce->clone = fe; } else if (zend_string_equals_literal(mname, ZEND_CONSTRUCTOR_FUNC_NAME)) { if (ce->constructor && (!ce->parent || ce->constructor != ce->parent->constructor)) { zend_error_noreturn(E_COMPILE_ERROR, "%s has colliding constructor definitions coming from traits", ZSTR_VAL(ce->name)); @@ -1173,12 +1157,13 @@ static void zend_add_trait_method(zend_class_entry *ce, const char *name, zend_s ZSTR_VAL(zend_get_function_declaration(fn)), ZSTR_VAL(zend_get_function_declaration(existing_fn))); } - } else if (fn->common.fn_flags & ZEND_ACC_ABSTRACT) { + } + if (fn->common.fn_flags & ZEND_ACC_ABSTRACT) { /* Make sure the abstract declaration is compatible with previous declaration */ if (UNEXPECTED(!zend_traits_method_compatibility_check(existing_fn, fn))) { zend_error_noreturn(E_COMPILE_ERROR, "Declaration of %s must be compatible with %s", - ZSTR_VAL(zend_get_function_declaration(fn)), - ZSTR_VAL(zend_get_function_declaration(existing_fn))); + ZSTR_VAL(zend_get_function_declaration(existing_fn)), + ZSTR_VAL(zend_get_function_declaration(fn))); } return; } @@ -1201,8 +1186,8 @@ static void zend_add_trait_method(zend_class_entry *ce, const char *name, zend_s /* Make sure the abstract declaration is compatible with previous declaration */ if (UNEXPECTED(!zend_traits_method_compatibility_check(existing_fn, fn))) { zend_error_noreturn(E_COMPILE_ERROR, "Declaration of %s must be compatible with %s", - ZSTR_VAL(zend_get_function_declaration(fn)), - ZSTR_VAL(zend_get_function_declaration(existing_fn))); + ZSTR_VAL(zend_get_function_declaration(existing_fn)), + ZSTR_VAL(zend_get_function_declaration(fn))); } return; } else if (UNEXPECTED(existing_fn->common.scope->ce_flags & ZEND_ACC_TRAIT)) { @@ -1540,7 +1525,6 @@ static void zend_do_traits_property_binding(zend_class_entry *ce) /* {{{ */ size_t i; zend_property_info *property_info; zend_property_info *coliding_prop; - zval compare_result; zend_string* prop_name; const char* class_name_unused; zend_bool not_compatible; @@ -1585,15 +1569,11 @@ static void zend_do_traits_property_binding(zend_class_entry *ce) /* {{{ */ == (flags & (ZEND_ACC_PPP_MASK | ZEND_ACC_STATIC))) { /* flags are identical, now the value needs to be checked */ if (flags & ZEND_ACC_STATIC) { - not_compatible = (FAILURE == compare_function(&compare_result, - &ce->default_static_members_table[coliding_prop->offset], - &ce->traits[i]->default_static_members_table[property_info->offset])) - || (Z_LVAL(compare_result) != 0); + not_compatible = fast_is_not_identical_function(&ce->default_static_members_table[coliding_prop->offset], + &ce->traits[i]->default_static_members_table[property_info->offset]); } else { - not_compatible = (FAILURE == compare_function(&compare_result, - &ce->default_properties_table[OBJ_PROP_TO_NUM(coliding_prop->offset)], - &ce->traits[i]->default_properties_table[OBJ_PROP_TO_NUM(property_info->offset)])) - || (Z_LVAL(compare_result) != 0); + not_compatible = fast_is_not_identical_function(&ce->default_properties_table[OBJ_PROP_TO_NUM(coliding_prop->offset)], + &ce->traits[i]->default_properties_table[OBJ_PROP_TO_NUM(property_info->offset)]); } } else { /* the flags are not identical, thus, we assume properties are not compatible */ @@ -1743,4 +1723,6 @@ void zend_check_deprecated_constructor(const zend_class_entry *ce) /* {{{ */ * c-basic-offset: 4 * indent-tabs-mode: t * End: + * vim600: sw=4 ts=4 fdm=marker + * vim<600: sw=4 ts=4 */ |