From: Nikita Popov Date: Thu, 4 Jul 2019 10:19:15 +0000 (+0200) Subject: Preloading: Relax known type restrictions X-Git-Tag: php-7.4.0alpha3~46 X-Git-Url: https://granicus.if.org/sourcecode?a=commitdiff_plain;h=b250f89b92541e7c60310152cda6a50f0fa30225;p=php Preloading: Relax known type restrictions 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. --- diff --git a/ext/opcache/ZendAccelerator.c b/ext/opcache/ZendAccelerator.c index 41ea02b2c0..78260b9fe0 100644 --- a/ext/opcache/ZendAccelerator.c +++ b/ext/opcache/ZendAccelerator.c @@ -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; } diff --git a/ext/opcache/tests/preload_variance.inc b/ext/opcache/tests/preload_variance.inc index 09f057442c..7b2dd7b8ff 100644 --- a/ext/opcache/tests/preload_variance.inc +++ b/ext/opcache/tests/preload_variance.inc @@ -38,4 +38,6 @@ class H extends G { } // Early-binding preventer. -class Z {} +class Z { + public function method(X $a) {} +}