summaryrefslogtreecommitdiff
path: root/Zend/zend_inheritance.c
diff options
context:
space:
mode:
Diffstat (limited to 'Zend/zend_inheritance.c')
-rw-r--r--Zend/zend_inheritance.c170
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
*/