Skip to content

Commit 29aed2a

Browse files
committed
Clean up dependency resolution during preloading
Combine the code for checking whether all dependencies are available and reporting an error if they are not. Actually store the loaded deps and then use those when checking for type availability, instead of looking up the same classes again and again.
1 parent 58040f2 commit 29aed2a

File tree

1 file changed

+89
-90
lines changed

1 file changed

+89
-90
lines changed

ext/opcache/ZendAccelerator.c

Lines changed: 89 additions & 90 deletions
Original file line numberDiff line numberDiff line change
@@ -3664,51 +3664,95 @@ static void preload_sort_classes(void *base, size_t count, size_t siz, compare_f
36643664
}
36653665
}
36663666

3667-
static bool preload_needed_types_known(zend_class_entry *ce);
3668-
static void get_unlinked_dependency(zend_class_entry *ce, const char **kind, const char **name) {
3669-
zend_class_entry *p;
3670-
*kind = "Unknown reason";
3671-
*name = "";
3667+
typedef struct {
3668+
zend_class_entry *parent;
3669+
zend_class_entry **interfaces;
3670+
zend_class_entry **traits;
3671+
const char *error_kind;
3672+
const char *error_name;
3673+
void *checkpoint;
3674+
} preload_deps;
3675+
3676+
static bool preload_needed_types_known(const preload_deps *deps, zend_class_entry *ce);
3677+
static zend_result preload_resolve_deps(preload_deps *deps, zend_class_entry *ce)
3678+
{
3679+
memset(deps, 0, sizeof(preload_deps));
3680+
deps->checkpoint = zend_arena_checkpoint(CG(arena));
36723681

36733682
if (ce->parent_name) {
36743683
zend_string *key = zend_string_tolower(ce->parent_name);
3675-
p = zend_hash_find_ptr(EG(class_table), key);
3684+
deps->parent = zend_hash_find_ptr(EG(class_table), key);
36763685
zend_string_release(key);
3677-
if (!p) {
3678-
*kind = "Unknown parent ";
3679-
*name = ZSTR_VAL(ce->parent_name);
3680-
return;
3686+
if (!deps->parent) {
3687+
deps->error_kind = "Unknown parent ";
3688+
deps->error_name = ZSTR_VAL(ce->parent_name);
3689+
return FAILURE;
36813690
}
36823691
}
36833692

36843693
if (ce->num_interfaces) {
3685-
uint32_t i;
3686-
for (i = 0; i < ce->num_interfaces; i++) {
3687-
p = zend_hash_find_ptr(EG(class_table), ce->interface_names[i].lc_name);
3688-
if (!p) {
3689-
*kind = "Unknown interface ";
3690-
*name = ZSTR_VAL(ce->interface_names[i].name);
3691-
return;
3694+
deps->interfaces =
3695+
zend_arena_alloc(&CG(arena), ce->num_interfaces * sizeof(zend_class_entry));
3696+
for (uint32_t i = 0; i < ce->num_interfaces; i++) {
3697+
deps->interfaces[i] =
3698+
zend_hash_find_ptr(EG(class_table), ce->interface_names[i].lc_name);
3699+
if (!deps->interfaces[i]) {
3700+
deps->error_kind = "Unknown interface ";
3701+
deps->error_name = ZSTR_VAL(ce->interface_names[i].name);
3702+
return FAILURE;
36923703
}
36933704
}
36943705
}
36953706

36963707
if (ce->num_traits) {
3697-
uint32_t i;
3698-
for (i = 0; i < ce->num_traits; i++) {
3699-
p = zend_hash_find_ptr(EG(class_table), ce->trait_names[i].lc_name);
3700-
if (!p) {
3701-
*kind = "Unknown trait ";
3702-
*name = ZSTR_VAL(ce->trait_names[i].name);
3703-
return;
3708+
deps->traits =
3709+
zend_arena_alloc(&CG(arena), ce->num_traits * sizeof(zend_class_entry));
3710+
for (uint32_t i = 0; i < ce->num_traits; i++) {
3711+
deps->traits[i] = zend_hash_find_ptr(EG(class_table), ce->trait_names[i].lc_name);
3712+
if (!deps->traits[i]) {
3713+
deps->error_kind = "Unknown trait ";
3714+
deps->error_name = ZSTR_VAL(ce->trait_names[i].name);
3715+
return FAILURE;
37043716
}
37053717
}
37063718
}
37073719

3708-
if (!preload_needed_types_known(ce)) {
3709-
*kind = "Unknown type dependencies";
3710-
return;
3720+
/* TODO: This is much more restrictive than necessary. We only need to actually
3721+
* know the types for covariant checks, but don't need them if we can ensure
3722+
* compatibility through a simple string comparison. We could improve this using
3723+
* a more general version of zend_can_early_bind(). */
3724+
if (!preload_needed_types_known(deps, ce)) {
3725+
deps->error_kind = "Unknown type dependencies";
3726+
deps->error_name = "";
3727+
return FAILURE;
3728+
}
3729+
3730+
return SUCCESS;
3731+
}
3732+
3733+
static void preload_release_deps(preload_deps *deps)
3734+
{
3735+
zend_arena_release(&CG(arena), deps->checkpoint);
3736+
}
3737+
3738+
static bool preload_can_resolve_deps(zend_class_entry *ce)
3739+
{
3740+
preload_deps deps;
3741+
zend_result result = preload_resolve_deps(&deps, ce);
3742+
preload_release_deps(&deps);
3743+
return result == SUCCESS;
3744+
}
3745+
3746+
static void get_unlinked_dependency(zend_class_entry *ce, const char **kind, const char **name) {
3747+
preload_deps deps;
3748+
if (preload_resolve_deps(&deps, ce) == FAILURE) {
3749+
*kind = deps.error_kind;
3750+
*name = deps.error_name;
3751+
} else {
3752+
*kind = "Unknown reason";
3753+
*name = "";
37113754
}
3755+
preload_release_deps(&deps);
37123756
}
37133757

37143758
static zend_result preload_update_constant(zval *val, zend_class_entry *scope)
@@ -3844,58 +3888,51 @@ static bool preload_is_type_known(zend_class_entry *ce, zend_type *type) {
38443888
return 1;
38453889
}
38463890

3847-
static bool preload_is_method_maybe_override(zend_class_entry *ce, zend_string *lcname) {
3848-
zend_class_entry *p;
3891+
static bool preload_is_method_maybe_override(
3892+
const preload_deps *deps, zend_class_entry *ce, zend_string *lcname) {
38493893
if (ce->trait_aliases || ce->trait_precedences) {
3850-
return 1;
3894+
return true;
38513895
}
38523896

38533897
if (ce->parent_name) {
3854-
zend_string *key = zend_string_tolower(ce->parent_name);
3855-
p = zend_hash_find_ptr(EG(class_table), key);
3856-
zend_string_release(key);
3857-
if (zend_hash_exists(&p->function_table, lcname)) {
3858-
return 1;
3898+
if (zend_hash_exists(&deps->parent->function_table, lcname)) {
3899+
return true;
38593900
}
38603901
}
38613902

38623903
if (ce->num_interfaces) {
3863-
uint32_t i;
3864-
for (i = 0; i < ce->num_interfaces; i++) {
3865-
zend_class_entry *p = zend_hash_find_ptr(EG(class_table), ce->interface_names[i].lc_name);
3866-
if (zend_hash_exists(&p->function_table, lcname)) {
3867-
return 1;
3904+
for (uint32_t i = 0; i < ce->num_interfaces; i++) {
3905+
if (zend_hash_exists(&deps->interfaces[i]->function_table, lcname)) {
3906+
return true;
38683907
}
38693908
}
38703909
}
38713910

38723911
if (ce->num_traits) {
3873-
uint32_t i;
3874-
for (i = 0; i < ce->num_traits; i++) {
3875-
zend_class_entry *p = zend_hash_find_ptr(EG(class_table), ce->trait_names[i].lc_name);
3876-
if (zend_hash_exists(&p->function_table, lcname)) {
3877-
return 1;
3912+
for (uint32_t i = 0; i < ce->num_traits; i++) {
3913+
if (zend_hash_exists(&deps->traits[i]->function_table, lcname)) {
3914+
return true;
38783915
}
38793916
}
38803917
}
38813918

3882-
return 0;
3919+
return false;
38833920
}
38843921

3885-
static bool preload_needed_types_known(zend_class_entry *ce) {
3922+
static bool preload_needed_types_known(const preload_deps *deps, zend_class_entry *ce) {
38863923
zend_function *fptr;
38873924
zend_string *lcname;
38883925
ZEND_HASH_FOREACH_STR_KEY_PTR(&ce->function_table, lcname, fptr) {
38893926
uint32_t i;
38903927
if (fptr->common.fn_flags & ZEND_ACC_HAS_RETURN_TYPE) {
38913928
if (!preload_is_type_known(ce, &fptr->common.arg_info[-1].type) &&
3892-
preload_is_method_maybe_override(ce, lcname)) {
3929+
preload_is_method_maybe_override(deps, ce, lcname)) {
38933930
return 0;
38943931
}
38953932
}
38963933
for (i = 0; i < fptr->common.num_args; i++) {
38973934
if (!preload_is_type_known(ce, &fptr->common.arg_info[i].type) &&
3898-
preload_is_method_maybe_override(ce, lcname)) {
3935+
preload_is_method_maybe_override(deps, ce, lcname)) {
38993936
return 0;
39003937
}
39013938
}
@@ -3907,10 +3944,9 @@ static void preload_link(void)
39073944
{
39083945
zval *zv;
39093946
zend_persistent_script *script;
3910-
zend_class_entry *ce, *parent, *p;
3947+
zend_class_entry *ce;
39113948
zend_string *key;
3912-
bool found, changed;
3913-
uint32_t i;
3949+
bool changed;
39143950

39153951
/* Resolve class dependencies */
39163952
do {
@@ -3933,44 +3969,7 @@ static void preload_link(void)
39333969
zend_string_release(key);
39343970
}
39353971

3936-
parent = NULL;
3937-
3938-
if (ce->parent_name) {
3939-
key = zend_string_tolower(ce->parent_name);
3940-
parent = zend_hash_find_ptr(EG(class_table), key);
3941-
zend_string_release(key);
3942-
if (!parent) continue;
3943-
}
3944-
3945-
if (ce->num_interfaces) {
3946-
found = 1;
3947-
for (i = 0; i < ce->num_interfaces; i++) {
3948-
p = zend_hash_find_ptr(EG(class_table), ce->interface_names[i].lc_name);
3949-
if (!p) {
3950-
found = 0;
3951-
break;
3952-
}
3953-
}
3954-
if (!found) continue;
3955-
}
3956-
3957-
if (ce->num_traits) {
3958-
found = 1;
3959-
for (i = 0; i < ce->num_traits; i++) {
3960-
p = zend_hash_find_ptr(EG(class_table), ce->trait_names[i].lc_name);
3961-
if (!p) {
3962-
found = 0;
3963-
break;
3964-
}
3965-
}
3966-
if (!found) continue;
3967-
}
3968-
3969-
/* TODO: This is much more restrictive than necessary. We only need to actually
3970-
* know the types for covariant checks, but don't need them if we can ensure
3971-
* compatibility through a simple string comparison. We could improve this using
3972-
* a more general version of zend_can_early_bind(). */
3973-
if (!preload_needed_types_known(ce)) {
3972+
if (!preload_can_resolve_deps(ce)) {
39743973
continue;
39753974
}
39763975

0 commit comments

Comments
 (0)