From f85764827064b8b9231bd9313695eafff370a7cd Mon Sep 17 00:00:00 2001 From: Nikita Popov Date: Wed, 3 Jul 2019 14:05:10 +0200 Subject: [PATCH] Improve unlinked class diagnostics during preloading --- ext/opcache/ZendAccelerator.c | 62 +++++++++++++++++++++++++++++- ext/opcache/tests/bug78014.phpt | 2 +- ext/opcache/tests/preload_011.phpt | 6 +-- 3 files changed, 65 insertions(+), 5 deletions(-) diff --git a/ext/opcache/ZendAccelerator.c b/ext/opcache/ZendAccelerator.c index ee9f45609b..e41ef6725d 100644 --- a/ext/opcache/ZendAccelerator.c +++ b/ext/opcache/ZendAccelerator.c @@ -3350,6 +3350,63 @@ 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 void get_unlinked_dependency(zend_class_entry *ce, const char **kind, const char **name) { + zend_class_entry *p; + *kind = "Unknown reason"; + *name = ""; + + 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 (!p) { + *kind = "Unknown parent "; + *name = ZSTR_VAL(ce->parent_name); + return; + } + if (!(p->ce_flags & ZEND_ACC_CONSTANTS_UPDATED)) { + *kind = "Parent with unresolved initializers "; + *name = ZSTR_VAL(ce->parent_name); + return; + } + if (!(p->ce_flags & ZEND_ACC_PROPERTY_TYPES_RESOLVED)) { + *kind = "Parent with unresolved property types "; + *name = ZSTR_VAL(ce->parent_name); + return; + } + } + + if (ce->num_interfaces) { + uint32_t i; + for (i = 0; i < ce->num_interfaces; i++) { + p = zend_hash_find_ptr(EG(class_table), ce->interface_names[i].lc_name); + if (!p) { + *kind = "Unknown interface "; + *name = ZSTR_VAL(ce->interface_names[i].name); + return; + } + } + } + + if (ce->num_traits) { + uint32_t i; + for (i = 0; i < ce->num_traits; i++) { + p = zend_hash_find_ptr(EG(class_table), ce->trait_names[i].lc_name); + if (!p) { + *kind = "Unknown trait "; + *name = ZSTR_VAL(ce->trait_names[i].name); + return; + } + } + } + + if (!preload_all_types_known(ce)) { + *kind = "Unknown type dependencies"; + return; + } +} + static zend_bool preload_try_resolve_constants(zend_class_entry *ce) { zend_bool ok, changed; @@ -3650,9 +3707,12 @@ static void preload_link(void) E_WARNING, ZSTR_VAL(ce->info.user.filename), ce->info.user.line_start, "Can't preload already declared class %s", ZSTR_VAL(ce->name)); } else { + const char *kind, *name; + get_unlinked_dependency(ce, &kind, &name); zend_error_at( E_WARNING, ZSTR_VAL(ce->info.user.filename), ce->info.user.line_start, - "Can't preload unlinked class %s", ZSTR_VAL(ce->name)); + "Can't preload unlinked class %s: %s%s", + ZSTR_VAL(ce->name), kind, name); } zend_string_release(key); } else if (!(ce->ce_flags & ZEND_ACC_CONSTANTS_UPDATED)) { diff --git a/ext/opcache/tests/bug78014.phpt b/ext/opcache/tests/bug78014.phpt index 234d33ae27..14f205e7b8 100644 --- a/ext/opcache/tests/bug78014.phpt +++ b/ext/opcache/tests/bug78014.phpt @@ -16,7 +16,7 @@ $c = new C; var_dump($c->foo()); ?> --EXPECTF-- -Warning: Can't preload unlinked class C in %s on line %d +Warning: Can't preload unlinked class C: Parent with unresolved initializers B in %s on line %d Warning: Can't preload class B with unresolved initializer for constant X in %s on line %d diff --git a/ext/opcache/tests/preload_011.phpt b/ext/opcache/tests/preload_011.phpt index 438cd80500..32863398a9 100644 --- a/ext/opcache/tests/preload_011.phpt +++ b/ext/opcache/tests/preload_011.phpt @@ -22,9 +22,9 @@ $g = new G; ?> ===DONE=== --EXPECTF-- -Warning: Can't preload unlinked class H in %s on line %d +Warning: Can't preload unlinked class H: Unknown type dependencies in %s on line %d -Warning: Can't preload unlinked class B in %s on line %d +Warning: Can't preload unlinked class B: Unknown type dependencies in %s on line %d -Warning: Can't preload unlinked class A in %s on line %d +Warning: Can't preload unlinked class A: Unknown type dependencies in %s on line %d ===DONE=== -- 2.40.0