Skip to content

Commit c150079

Browse files
committed
Add preloading support for typed properties
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.
1 parent a2e9534 commit c150079

File tree

5 files changed

+75
-32
lines changed

5 files changed

+75
-32
lines changed

Zend/zend_compile.h

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -212,7 +212,7 @@ typedef struct _zend_oparray_context {
212212
/* Final class or method | | | */
213213
#define ZEND_ACC_FINAL (1 << 5) /* X | X | | */
214214
/* | | | */
215-
/* Abstarct method | | | */
215+
/* Abstract method | | | */
216216
#define ZEND_ACC_ABSTRACT (1 << 6) /* X | X | | */
217217
#define ZEND_ACC_EXPLICIT_ABSTRACT_CLASS (1 << 6) /* X | | | */
218218
/* | | | */
@@ -260,6 +260,9 @@ typedef struct _zend_oparray_context {
260260
/* User class has methods with static variables | | | */
261261
#define ZEND_HAS_STATIC_IN_METHODS (1 << 15) /* X | | | */
262262
/* | | | */
263+
/* Whether all property types are resolved to CEs | | | */
264+
#define ZEND_ACC_PROPERTY_TYPES_RESOLVED (1 << 16) /* X | | | */
265+
/* | | | */
263266
/* Function Flags (unused: 28...30) | | | */
264267
/* ============== | | | */
265268
/* | | | */

ext/opcache/ZendAccelerator.c

Lines changed: 38 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -3408,6 +3408,42 @@ static void preload_link(void)
34083408
} while (changed);
34093409
EG(exception) = NULL;
34103410

3411+
/* Resolve property types */
3412+
ZEND_HASH_REVERSE_FOREACH_VAL(EG(class_table), zv) {
3413+
ce = Z_PTR_P(zv);
3414+
if (ce->type == ZEND_INTERNAL_CLASS) {
3415+
break;
3416+
}
3417+
if ((ce->ce_flags & ZEND_ACC_LINKED)
3418+
&& !(ce->ce_flags & ZEND_ACC_PROPERTY_TYPES_RESOLVED)) {
3419+
zend_bool ok = 1;
3420+
zend_property_info *prop;
3421+
if (ce->ce_flags & ZEND_ACC_HAS_TYPE_HINTS) {
3422+
ZEND_HASH_FOREACH_PTR(&ce->properties_info, prop) {
3423+
zend_string *name, *lcname;
3424+
if (!ZEND_TYPE_IS_NAME(prop->type)) {
3425+
continue;
3426+
}
3427+
3428+
name = ZEND_TYPE_NAME(prop->type);
3429+
lcname = zend_string_tolower(name);
3430+
p = zend_hash_find_ptr(EG(class_table), lcname);
3431+
zend_string_release(lcname);
3432+
if (!p) {
3433+
ok = 0;
3434+
continue;
3435+
}
3436+
3437+
zend_string_release(name);
3438+
prop->type = ZEND_TYPE_ENCODE_CE(p, ZEND_TYPE_ALLOW_NULL(prop->type));
3439+
} ZEND_HASH_FOREACH_END();
3440+
}
3441+
if (ok) {
3442+
ce->ce_flags |= ZEND_ACC_PROPERTY_TYPES_RESOLVED;
3443+
}
3444+
}
3445+
} ZEND_HASH_FOREACH_END();
3446+
34113447
/* Move unlinked clases (and with unresilved constants) back to scripts */
34123448
orig_dtor = EG(class_table)->pDestructor;
34133449
EG(class_table)->pDestructor = NULL;
@@ -3420,6 +3456,8 @@ static void preload_link(void)
34203456
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);
34213457
} else if (!(ce->ce_flags & ZEND_ACC_CONSTANTS_UPDATED)) {
34223458
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);
3459+
} else if (!(ce->ce_flags & ZEND_ACC_PROPERTY_TYPES_RESOLVED)) {
3460+
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);
34233461
} else {
34243462
continue;
34253463
}

ext/opcache/tests/preload.inc

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -40,4 +40,5 @@ class Y {
4040

4141
class Z {
4242
public $foo;
43+
public a $bar;
4344
}

ext/opcache/zend_persist.c

Lines changed: 15 additions & 23 deletions
Original file line numberDiff line numberDiff line change
@@ -745,10 +745,6 @@ static void zend_persist_property_info(zval *zv)
745745
zend_string *class_name = ZEND_TYPE_NAME(prop->type);
746746
zend_accel_store_interned_string(class_name);
747747
prop->type = ZEND_TYPE_ENCODE_CLASS(class_name, ZEND_TYPE_ALLOW_NULL(prop->type));
748-
} else if (ZEND_TYPE_IS_CE(prop->type)) {
749-
zend_class_entry *ce = ZEND_TYPE_CE(prop->type);
750-
ce = zend_shared_alloc_get_xlat_entry(ce);
751-
prop->type = ZEND_TYPE_ENCODE_CE(ce, ZEND_TYPE_ALLOW_NULL(prop->type));
752748
}
753749
}
754750

@@ -790,24 +786,14 @@ static void zend_persist_class_constant(zval *zv)
790786
}
791787
}
792788

793-
static zend_bool has_unresolved_property_types(zend_class_entry *ce) {
794-
zend_property_info *prop;
795-
ZEND_HASH_FOREACH_PTR(&ce->properties_info, prop) {
796-
if (ZEND_TYPE_IS_NAME(prop->type)) {
797-
return 1;
798-
}
799-
} ZEND_HASH_FOREACH_END();
800-
return 0;
801-
}
802-
803789
static void zend_persist_class_entry(zval *zv)
804790
{
805791
zend_class_entry *ce = Z_PTR_P(zv);
806792

807793
if (ce->type == ZEND_USER_CLASS) {
808794
if ((ce->ce_flags & ZEND_ACC_LINKED)
809795
&& (ce->ce_flags & ZEND_ACC_CONSTANTS_UPDATED)
810-
&& !has_unresolved_property_types(ce)
796+
&& (ce->ce_flags & ZEND_ACC_PROPERTY_TYPES_RESOLVED)
811797
&& !ZCG(current_persistent_script)->corrupted) {
812798
ZCG(is_immutable_class) = 1;
813799
ce = Z_PTR_P(zv) = zend_shared_memdup_put(ce, sizeof(zend_class_entry));
@@ -953,14 +939,6 @@ static void zend_persist_class_entry(zval *zv)
953939
}
954940
}
955941

956-
//static int zend_update_property_info_ce(zval *zv)
957-
//{
958-
// zend_property_info *prop = Z_PTR_P(zv);
959-
//
960-
// prop->ce = zend_shared_alloc_get_xlat_entry(prop->ce);
961-
// return 0;
962-
//}
963-
964942
static void zend_update_parent_ce(zend_class_entry *ce)
965943
{
966944
if (ce->ce_flags & ZEND_ACC_LINKED) {
@@ -1018,6 +996,20 @@ static void zend_update_parent_ce(zend_class_entry *ce)
1018996
}
1019997
}
1020998

999+
if (ce->ce_flags & ZEND_ACC_HAS_TYPE_HINTS) {
1000+
zend_property_info *prop;
1001+
ZEND_HASH_FOREACH_PTR(&ce->properties_info, prop) {
1002+
if (ZEND_TYPE_IS_CE(prop->type)) {
1003+
zend_class_entry *ce = ZEND_TYPE_CE(prop->type);
1004+
if (ce->type == ZEND_USER_CLASS) {
1005+
ce = zend_shared_alloc_get_xlat_entry(ce);
1006+
ZEND_ASSERT(ce);
1007+
prop->type = ZEND_TYPE_ENCODE_CE(ce, ZEND_TYPE_ALLOW_NULL(prop->type));
1008+
}
1009+
}
1010+
} ZEND_HASH_FOREACH_END();
1011+
}
1012+
10211013
/* update methods */
10221014
if (ce->constructor) {
10231015
zend_function *tmp = zend_shared_alloc_get_xlat_entry(ce->constructor);

ext/opcache/zend_persist_calc.c

Lines changed: 17 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -331,25 +331,34 @@ static void zend_persist_class_constant_calc(zval *zv)
331331
}
332332
}
333333

334-
static zend_bool has_unresolved_property_types(zend_class_entry *ce) {
334+
static void check_property_type_resolution(zend_class_entry *ce) {
335335
zend_property_info *prop;
336-
ZEND_HASH_FOREACH_PTR(&ce->properties_info, prop) {
337-
if (ZEND_TYPE_IS_NAME(prop->type)) {
338-
return 1;
339-
}
340-
} ZEND_HASH_FOREACH_END();
341-
return 0;
336+
if (ce->ce_flags & ZEND_ACC_PROPERTY_TYPES_RESOLVED) {
337+
/* Preloading might have computed this already. */
338+
return;
339+
}
340+
341+
if (ce->ce_flags & ZEND_ACC_HAS_TYPE_HINTS) {
342+
ZEND_HASH_FOREACH_PTR(&ce->properties_info, prop) {
343+
if (ZEND_TYPE_IS_NAME(prop->type)) {
344+
return;
345+
}
346+
} ZEND_HASH_FOREACH_END();
347+
}
348+
ce->ce_flags |= ZEND_ACC_PROPERTY_TYPES_RESOLVED;
342349
}
343350

344351
static void zend_persist_class_entry_calc(zval *zv)
345352
{
346353
zend_class_entry *ce = Z_PTR_P(zv);
347354

348355
if (ce->type == ZEND_USER_CLASS) {
356+
check_property_type_resolution(ce);
357+
349358
ZCG(is_immutable_class) =
350359
(ce->ce_flags & ZEND_ACC_LINKED) &&
351360
(ce->ce_flags & ZEND_ACC_CONSTANTS_UPDATED) &&
352-
!has_unresolved_property_types(ce) &&
361+
(ce->ce_flags & ZEND_ACC_PROPERTY_TYPES_RESOLVED) &&
353362
!ZCG(current_persistent_script)->corrupted;
354363

355364
ADD_SIZE_EX(sizeof(zend_class_entry));

0 commit comments

Comments
 (0)