]> granicus.if.org Git - php/commitdiff
Preloading: Relax known type restrictions
authorNikita Popov <nikita.ppv@gmail.com>
Thu, 4 Jul 2019 10:19:15 +0000 (12:19 +0200)
committerNikita Popov <nikita.ppv@gmail.com>
Thu, 4 Jul 2019 10:20:32 +0000 (12:20 +0200)
Check whether there is a parent/interface/trait method with the same
name and only then require the type to be known. This reduces the
number of cases where this triggers in practice a lot.

ext/opcache/ZendAccelerator.c
ext/opcache/tests/preload_variance.inc

index 41ea02b2c0a031f693266f83fe7af7606d0314d1..78260b9fe00af21a921ace9db88d324d08c0ae6d 100644 (file)
@@ -3350,7 +3350,7 @@ static void get_unresolved_initializer(zend_class_entry *ce, const char **kind,
        } ZEND_HASH_FOREACH_END();
 }
 
-static zend_bool preload_all_types_known(zend_class_entry *ce);
+static zend_bool preload_needed_types_known(zend_class_entry *ce);
 static void get_unlinked_dependency(zend_class_entry *ce, const char **kind, const char **name) {
        zend_class_entry *p;
        *kind = "Unknown reason";
@@ -3401,7 +3401,7 @@ static void get_unlinked_dependency(zend_class_entry *ce, const char **kind, con
                }
        }
 
-       if (!preload_all_types_known(ce)) {
+       if (!preload_needed_types_known(ce)) {
                *kind = "Unknown type dependencies";
                return;
        }
@@ -3529,17 +3529,58 @@ static zend_bool preload_is_type_known(zend_class_entry *ce, zend_type type) {
        return known;
 }
 
-static zend_bool preload_all_types_known(zend_class_entry *ce) {
+static zend_bool preload_is_method_maybe_override(zend_class_entry *ce, zend_string *lcname) {
+       zend_class_entry *p;
+       if (ce->trait_aliases || ce->trait_precedences) {
+               return 1;
+       }
+
+       if (ce->parent_name) {
+               zend_string *key = zend_string_tolower(ce->parent_name);
+               p = zend_hash_find_ptr(EG(class_table), key);
+               zend_string_release(key);
+               if (zend_hash_exists(&p->function_table, lcname)) {
+                       return 1;
+               }
+       }
+
+       if (ce->num_interfaces) {
+               uint32_t i;
+               for (i = 0; i < ce->num_interfaces; i++) {
+                       zend_class_entry *p = zend_hash_find_ptr(EG(class_table), ce->interface_names[i].lc_name);
+                       if (zend_hash_exists(&p->function_table, lcname)) {
+                               return 1;
+                       }
+               }
+       }
+
+       if (ce->num_traits) {
+               uint32_t i;
+               for (i = 0; i < ce->num_traits; i++) {
+                       zend_class_entry *p = zend_hash_find_ptr(EG(class_table), ce->trait_names[i].lc_name);
+                       if (zend_hash_exists(&p->function_table, lcname)) {
+                               return 1;
+                       }
+               }
+       }
+
+       return 0;
+}
+
+static zend_bool preload_needed_types_known(zend_class_entry *ce) {
        zend_function *fptr;
-       ZEND_HASH_FOREACH_PTR(&ce->function_table, fptr) {
+       zend_string *lcname;
+       ZEND_HASH_FOREACH_STR_KEY_PTR(&ce->function_table, lcname, fptr) {
                uint32_t i;
                if (fptr->common.fn_flags & ZEND_ACC_HAS_RETURN_TYPE) {
-                       if (!preload_is_type_known(ce, fptr->common.arg_info[-1].type)) {
+                       if (!preload_is_type_known(ce, fptr->common.arg_info[-1].type) &&
+                               preload_is_method_maybe_override(ce, lcname)) {
                                return 0;
                        }
                }
                for (i = 0; i < fptr->common.num_args; i++) {
-                       if (!preload_is_type_known(ce, fptr->common.arg_info[i].type)) {
+                       if (!preload_is_type_known(ce, fptr->common.arg_info[i].type) &&
+                               preload_is_method_maybe_override(ce, lcname)) {
                                return 0;
                        }
                }
@@ -3645,7 +3686,7 @@ static void preload_link(void)
                                 * know the types for covariant checks, but don't need them if we can ensure
                                 * compatibility through a simple string comparison. We could improve this using
                                 * a more general version of zend_can_early_bind(). */
-                               if (!preload_all_types_known(ce)) {
+                               if (!preload_needed_types_known(ce)) {
                                        continue;
                                }
 
index 09f057442c61895608cb9e938125b3a698612269..7b2dd7b8ffa179a1270f92a1f1c840cff58d65f0 100644 (file)
@@ -38,4 +38,6 @@ class H extends G {
 }
 
 // Early-binding preventer.
-class Z {}
+class Z {
+    public function method(X $a) {}
+}