Created
June 26, 2019 07:36
-
-
Save dstogov/0bcb35ed093f99d325f2f44e78a3add6 to your computer and use it in GitHub Desktop.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
diff --git a/Zend/zend_compile.h b/Zend/zend_compile.h | |
index 6721612b4c..de0ac1fe13 100644 | |
--- a/Zend/zend_compile.h | |
+++ b/Zend/zend_compile.h | |
@@ -278,6 +278,9 @@ typedef struct _zend_oparray_context { | |
/* Class has unresolved variance obligations. | | | */ | |
#define ZEND_ACC_UNRESOLVED_VARIANCE (1 << 21) /* X | | | */ | |
/* | | | */ | |
+/* Method compatibility already checked. | | | */ | |
+#define ZEND_ACC_CHECKED (1 << 22) /* X | | | */ | |
+/* | | | */ | |
/* Function Flags (unused: 28...30) | | | */ | |
/* ============== | | | */ | |
/* | | | */ | |
diff --git a/Zend/zend_inheritance.c b/Zend/zend_inheritance.c | |
index e568e53514..697417d31a 100644 | |
--- a/Zend/zend_inheritance.c | |
+++ b/Zend/zend_inheritance.c | |
@@ -804,6 +804,65 @@ static void do_inheritance_check_on_method(zend_function *child, zend_function * | |
} | |
/* }}} */ | |
+static zend_function *do_inherit_checked_method(zend_string *key, zend_function *parent, zend_class_entry *ce) /* {{{ */ | |
+{ | |
+ zval *child = zend_hash_find_ex(&ce->function_table, key, 1); | |
+ | |
+ if (child) { | |
+ zend_function *func = (zend_function*)Z_PTR_P(child); | |
+ uint32_t parent_flags; | |
+ | |
+ if (UNEXPECTED(func == parent)) { | |
+ /* The same method in interface may be inherited few times */ | |
+ return NULL; | |
+ } | |
+ | |
+ parent_flags = parent->common.fn_flags; | |
+ | |
+ if (parent_flags & (ZEND_ACC_PRIVATE|ZEND_ACC_CHANGED)) { | |
+ func->common.fn_flags |= ZEND_ACC_CHANGED; | |
+ } | |
+ | |
+ if (!(parent_flags & ZEND_ACC_PRIVATE)) { | |
+ zend_function *proto = parent->common.prototype ? | |
+ parent->common.prototype : parent; | |
+ | |
+ if (parent_flags & ZEND_ACC_CTOR) { | |
+ /* ctors only have a prototype if is abstract (or comes from an interface) */ | |
+ /* and if that is the case, we want to check inheritance against it */ | |
+ if (!(proto->common.fn_flags & ZEND_ACC_ABSTRACT)) { | |
+ return NULL; | |
+ } | |
+ } | |
+ | |
+ if (func->common.prototype != proto) { | |
+ if (func->common.scope != ce | |
+ && func->type == ZEND_USER_FUNCTION | |
+ && !func->op_array.static_variables) { | |
+ if (ce->ce_flags & ZEND_ACC_INTERFACE) { | |
+ /* Few parent interfaces contain the same method */ | |
+ return NULL; | |
+ } else { | |
+ /* op_array wasn't duplicated yet */ | |
+ zend_function *new_function = zend_arena_alloc(&CG(arena), sizeof(zend_op_array)); | |
+ memcpy(new_function, func, sizeof(zend_op_array)); | |
+ Z_PTR_P(child) = func = new_function; | |
+ } | |
+ } | |
+ func->common.prototype = proto; | |
+ } | |
+ } | |
+ return NULL; | |
+ } | |
+ | |
+ if (parent->common.fn_flags & (ZEND_ACC_ABSTRACT)) { | |
+ ce->ce_flags |= ZEND_ACC_IMPLICIT_ABSTRACT_CLASS; | |
+ } | |
+ | |
+ return zend_duplicate_function(parent, ce); | |
+} | |
+/* }}} */ | |
+ | |
static zend_function *do_inherit_method(zend_string *key, zend_function *parent, zend_class_entry *ce) /* {{{ */ | |
{ | |
zval *child = zend_hash_find_ex(&ce->function_table, key, 1); | |
@@ -1258,13 +1317,23 @@ ZEND_API void zend_do_inheritance(zend_class_entry *ce, zend_class_entry *parent | |
zend_hash_num_elements(&ce->function_table) + | |
zend_hash_num_elements(&parent_ce->function_table), 0); | |
- ZEND_HASH_FOREACH_STR_KEY_PTR(&parent_ce->function_table, key, func) { | |
- zend_function *new_func = do_inherit_method(key, func, ce); | |
+ if (ce->ce_flags & ZEND_ACC_CHECKED) { | |
+ ZEND_HASH_FOREACH_STR_KEY_PTR(&parent_ce->function_table, key, func) { | |
+ zend_function *new_func = do_inherit_checked_method(key, func, ce); | |
- if (new_func) { | |
- _zend_hash_append_ptr(&ce->function_table, key, new_func); | |
- } | |
- } ZEND_HASH_FOREACH_END(); | |
+ if (new_func) { | |
+ _zend_hash_append_ptr(&ce->function_table, key, new_func); | |
+ } | |
+ } ZEND_HASH_FOREACH_END(); | |
+ } else { | |
+ ZEND_HASH_FOREACH_STR_KEY_PTR(&parent_ce->function_table, key, func) { | |
+ zend_function *new_func = do_inherit_method(key, func, ce); | |
+ | |
+ if (new_func) { | |
+ _zend_hash_append_ptr(&ce->function_table, key, new_func); | |
+ } | |
+ } ZEND_HASH_FOREACH_END(); | |
+ } | |
} | |
do_inherit_parent_constructor(ce); | |
@@ -2387,6 +2456,7 @@ ZEND_API void zend_do_link_class(zend_class_entry *ce, zend_string *lc_parent_na | |
/* Check whether early binding is prevented due to unresolved types in inheritance checks. */ | |
zend_bool zend_can_early_bind(zend_class_entry *ce, zend_class_entry *parent_ce) { | |
+ zend_bool has_errors = 0; | |
zend_string *key; | |
zend_function *parent_func; | |
ZEND_HASH_FOREACH_STR_KEY_PTR(&parent_ce->function_table, key, parent_func) { | |
@@ -2411,16 +2481,42 @@ zend_bool zend_can_early_bind(zend_class_entry *ce, zend_class_entry *parent_ce) | |
zv = zend_hash_find_ex(&ce->function_table, key, 1); | |
if (zv) { | |
zend_function *child_func = Z_FUNC_P(zv); | |
+ uint32_t child_flags = child_func->common.fn_flags; | |
inheritance_status status; | |
+ if (UNEXPECTED(parent_flags & ZEND_ACC_FINAL)) { | |
+ return 1; | |
+ } | |
+ | |
+ if (UNEXPECTED((child_flags & ZEND_ACC_STATIC) != (parent_flags & ZEND_ACC_STATIC))) { | |
+ return 1; | |
+ } | |
+ | |
+ if (UNEXPECTED((child_flags & ZEND_ACC_ABSTRACT) > (parent_flags & ZEND_ACC_ABSTRACT))) { | |
+ return 1; | |
+ } | |
+ | |
+ if ((child_flags & ZEND_ACC_PPP_MASK) > (parent_flags & ZEND_ACC_PPP_MASK)) { | |
+ return 1; | |
+ } | |
+ | |
status = zend_do_perform_implementation_check( | |
&unresolved_class, child_func, parent_func); | |
- if (UNEXPECTED(status == INHERITANCE_UNRESOLVED)) { | |
- return 0; | |
+ if (UNEXPECTED(status != INHERITANCE_SUCCESS)) { | |
+ if (EXPECTED(status == INHERITANCE_UNRESOLVED)) { | |
+ return 0; | |
+ } | |
+ ZEND_ASSERT(status == INHERITANCE_ERROR); | |
+ has_errors = 1; | |
} | |
} | |
} ZEND_HASH_FOREACH_END(); | |
+ | |
+ if (!has_errors) { | |
+ ce->ce_flags |= ZEND_ACC_CHECKED; | |
+ } | |
+ | |
return 1; | |
} | |
/* }}} */ |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment