Skip to content

Commit

Permalink
Add preloading support for typed properties
Browse files Browse the repository at this point in the history
During preloading, try to resolve all property types to CEs. Add a
flag that tracks this. If not all property types can be resolved,
then the class is not eligible for preloading.
  • Loading branch information
nikic committed Feb 15, 2019
1 parent a2e9534 commit c150079
Show file tree
Hide file tree
Showing 5 changed files with 75 additions and 32 deletions.
5 changes: 4 additions & 1 deletion Zend/zend_compile.h
Expand Up @@ -212,7 +212,7 @@ typedef struct _zend_oparray_context {
/* Final class or method | | | */
#define ZEND_ACC_FINAL (1 << 5) /* X | X | | */
/* | | | */
/* Abstarct method | | | */
/* Abstract method | | | */
#define ZEND_ACC_ABSTRACT (1 << 6) /* X | X | | */
#define ZEND_ACC_EXPLICIT_ABSTRACT_CLASS (1 << 6) /* X | | | */
/* | | | */
Expand Down Expand Up @@ -260,6 +260,9 @@ typedef struct _zend_oparray_context {
/* User class has methods with static variables | | | */
#define ZEND_HAS_STATIC_IN_METHODS (1 << 15) /* X | | | */
/* | | | */
/* Whether all property types are resolved to CEs | | | */
#define ZEND_ACC_PROPERTY_TYPES_RESOLVED (1 << 16) /* X | | | */
/* | | | */
/* Function Flags (unused: 28...30) | | | */
/* ============== | | | */
/* | | | */
Expand Down
38 changes: 38 additions & 0 deletions ext/opcache/ZendAccelerator.c
Expand Up @@ -3408,6 +3408,42 @@ static void preload_link(void)
} while (changed);
EG(exception) = NULL;

/* Resolve property types */
ZEND_HASH_REVERSE_FOREACH_VAL(EG(class_table), zv) {
ce = Z_PTR_P(zv);
if (ce->type == ZEND_INTERNAL_CLASS) {
break;
}
if ((ce->ce_flags & ZEND_ACC_LINKED)
&& !(ce->ce_flags & ZEND_ACC_PROPERTY_TYPES_RESOLVED)) {
zend_bool ok = 1;
zend_property_info *prop;
if (ce->ce_flags & ZEND_ACC_HAS_TYPE_HINTS) {
ZEND_HASH_FOREACH_PTR(&ce->properties_info, prop) {
zend_string *name, *lcname;
if (!ZEND_TYPE_IS_NAME(prop->type)) {
continue;
}

name = ZEND_TYPE_NAME(prop->type);
lcname = zend_string_tolower(name);
p = zend_hash_find_ptr(EG(class_table), lcname);
zend_string_release(lcname);
if (!p) {
ok = 0;
continue;
}

zend_string_release(name);
prop->type = ZEND_TYPE_ENCODE_CE(p, ZEND_TYPE_ALLOW_NULL(prop->type));
} ZEND_HASH_FOREACH_END();
}
if (ok) {
ce->ce_flags |= ZEND_ACC_PROPERTY_TYPES_RESOLVED;
}
}
} ZEND_HASH_FOREACH_END();

/* Move unlinked clases (and with unresilved constants) back to scripts */
orig_dtor = EG(class_table)->pDestructor;
EG(class_table)->pDestructor = NULL;
Expand All @@ -3420,6 +3456,8 @@ static void preload_link(void)
zend_error(E_WARNING, "Can't preload unlinked class %s at %s:%d\n", ZSTR_VAL(ce->name), ZSTR_VAL(ce->info.user.filename), ce->info.user.line_start);
} else if (!(ce->ce_flags & ZEND_ACC_CONSTANTS_UPDATED)) {
zend_error(E_WARNING, "Can't preload class %s with unresolved constants at %s:%d\n", ZSTR_VAL(ce->name), ZSTR_VAL(ce->info.user.filename), ce->info.user.line_start);
} else if (!(ce->ce_flags & ZEND_ACC_PROPERTY_TYPES_RESOLVED)) {
zend_error(E_WARNING, "Can't preload class %s with unresolved property types at %s:%d\n", ZSTR_VAL(ce->name), ZSTR_VAL(ce->info.user.filename), ce->info.user.line_start);
} else {
continue;
}
Expand Down
1 change: 1 addition & 0 deletions ext/opcache/tests/preload.inc
Expand Up @@ -40,4 +40,5 @@ class Y {

class Z {
public $foo;
public a $bar;
}
38 changes: 15 additions & 23 deletions ext/opcache/zend_persist.c
Expand Up @@ -745,10 +745,6 @@ static void zend_persist_property_info(zval *zv)
zend_string *class_name = ZEND_TYPE_NAME(prop->type);
zend_accel_store_interned_string(class_name);
prop->type = ZEND_TYPE_ENCODE_CLASS(class_name, ZEND_TYPE_ALLOW_NULL(prop->type));
} else if (ZEND_TYPE_IS_CE(prop->type)) {
zend_class_entry *ce = ZEND_TYPE_CE(prop->type);
ce = zend_shared_alloc_get_xlat_entry(ce);
prop->type = ZEND_TYPE_ENCODE_CE(ce, ZEND_TYPE_ALLOW_NULL(prop->type));
}
}

Expand Down Expand Up @@ -790,24 +786,14 @@ static void zend_persist_class_constant(zval *zv)
}
}

static zend_bool has_unresolved_property_types(zend_class_entry *ce) {
zend_property_info *prop;
ZEND_HASH_FOREACH_PTR(&ce->properties_info, prop) {
if (ZEND_TYPE_IS_NAME(prop->type)) {
return 1;
}
} ZEND_HASH_FOREACH_END();
return 0;
}

static void zend_persist_class_entry(zval *zv)
{
zend_class_entry *ce = Z_PTR_P(zv);

if (ce->type == ZEND_USER_CLASS) {
if ((ce->ce_flags & ZEND_ACC_LINKED)
&& (ce->ce_flags & ZEND_ACC_CONSTANTS_UPDATED)
&& !has_unresolved_property_types(ce)
&& (ce->ce_flags & ZEND_ACC_PROPERTY_TYPES_RESOLVED)
&& !ZCG(current_persistent_script)->corrupted) {
ZCG(is_immutable_class) = 1;
ce = Z_PTR_P(zv) = zend_shared_memdup_put(ce, sizeof(zend_class_entry));
Expand Down Expand Up @@ -953,14 +939,6 @@ static void zend_persist_class_entry(zval *zv)
}
}

//static int zend_update_property_info_ce(zval *zv)
//{
// zend_property_info *prop = Z_PTR_P(zv);
//
// prop->ce = zend_shared_alloc_get_xlat_entry(prop->ce);
// return 0;
//}

static void zend_update_parent_ce(zend_class_entry *ce)
{
if (ce->ce_flags & ZEND_ACC_LINKED) {
Expand Down Expand Up @@ -1018,6 +996,20 @@ static void zend_update_parent_ce(zend_class_entry *ce)
}
}

if (ce->ce_flags & ZEND_ACC_HAS_TYPE_HINTS) {
zend_property_info *prop;
ZEND_HASH_FOREACH_PTR(&ce->properties_info, prop) {
if (ZEND_TYPE_IS_CE(prop->type)) {
zend_class_entry *ce = ZEND_TYPE_CE(prop->type);
if (ce->type == ZEND_USER_CLASS) {
ce = zend_shared_alloc_get_xlat_entry(ce);
ZEND_ASSERT(ce);
prop->type = ZEND_TYPE_ENCODE_CE(ce, ZEND_TYPE_ALLOW_NULL(prop->type));
}
}
} ZEND_HASH_FOREACH_END();
}

/* update methods */
if (ce->constructor) {
zend_function *tmp = zend_shared_alloc_get_xlat_entry(ce->constructor);
Expand Down
25 changes: 17 additions & 8 deletions ext/opcache/zend_persist_calc.c
Expand Up @@ -331,25 +331,34 @@ static void zend_persist_class_constant_calc(zval *zv)
}
}

static zend_bool has_unresolved_property_types(zend_class_entry *ce) {
static void check_property_type_resolution(zend_class_entry *ce) {
zend_property_info *prop;
ZEND_HASH_FOREACH_PTR(&ce->properties_info, prop) {
if (ZEND_TYPE_IS_NAME(prop->type)) {
return 1;
}
} ZEND_HASH_FOREACH_END();
return 0;
if (ce->ce_flags & ZEND_ACC_PROPERTY_TYPES_RESOLVED) {
/* Preloading might have computed this already. */
return;
}

if (ce->ce_flags & ZEND_ACC_HAS_TYPE_HINTS) {
ZEND_HASH_FOREACH_PTR(&ce->properties_info, prop) {
if (ZEND_TYPE_IS_NAME(prop->type)) {
return;
}
} ZEND_HASH_FOREACH_END();
}
ce->ce_flags |= ZEND_ACC_PROPERTY_TYPES_RESOLVED;
}

static void zend_persist_class_entry_calc(zval *zv)
{
zend_class_entry *ce = Z_PTR_P(zv);

if (ce->type == ZEND_USER_CLASS) {
check_property_type_resolution(ce);

ZCG(is_immutable_class) =
(ce->ce_flags & ZEND_ACC_LINKED) &&
(ce->ce_flags & ZEND_ACC_CONSTANTS_UPDATED) &&
!has_unresolved_property_types(ce) &&
(ce->ce_flags & ZEND_ACC_PROPERTY_TYPES_RESOLVED) &&
!ZCG(current_persistent_script)->corrupted;

ADD_SIZE_EX(sizeof(zend_class_entry));
Expand Down

0 comments on commit c150079

Please sign in to comment.