]> granicus.if.org Git - php/commitdiff
Add preloading support for typed properties
authorNikita Popov <nikita.ppv@gmail.com>
Fri, 15 Feb 2019 11:30:36 +0000 (12:30 +0100)
committerNikita Popov <nikita.ppv@gmail.com>
Fri, 15 Feb 2019 11:41:45 +0000 (12:41 +0100)
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.

Zend/zend_compile.h
ext/opcache/ZendAccelerator.c
ext/opcache/tests/preload.inc
ext/opcache/zend_persist.c
ext/opcache/zend_persist_calc.c

index d12199a6d625ce1aaa87291a1806921db2cbedec..a80838666b4a7b3586172543c689ce32913410e2 100644 (file)
@@ -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  |     |     |     */
 /*                                                        |     |     |     */
@@ -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)                       |     |     |     */
 /* ==============                                         |     |     |     */
 /*                                                        |     |     |     */
index ad9e3aa5a9ddba42cbd6b73d014d48c365b6d8d7..303034a4ba9fd63c9f992061e82ecaee1a18435b 100644 (file)
@@ -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;
@@ -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;
                }
index 0e09ad366c3191206cddffd6da2965492f1d89e0..6a4df58c5f7e2681c1f1c83b6dd61c2b1c53c8a6 100644 (file)
@@ -40,4 +40,5 @@ class Y {
 
 class Z {
     public $foo;
+    public a $bar;
 }
index d22b855f5889ab4e5bc3cea80d06bed9dad1136c..1a22f12b4db61d927fa2ba5ec61e2e4b7519dc3e 100644 (file)
@@ -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));
        }
 }
 
@@ -790,16 +786,6 @@ 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);
@@ -807,7 +793,7 @@ static void zend_persist_class_entry(zval *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));
@@ -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) {
@@ -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);
index 7daac7b51cf0a7c5418e76664335204c7861f405..774f42185f1379667a8a2c0ef62fc3c4585b4575 100644 (file)
@@ -331,14 +331,21 @@ 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)
@@ -346,10 +353,12 @@ 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));