]> granicus.if.org Git - php/commitdiff
zend_assign_to_variable() optimization
authorDmitry Stogov <dmitry@zend.com>
Wed, 29 May 2019 10:58:37 +0000 (13:58 +0300)
committerDmitry Stogov <dmitry@zend.com>
Wed, 29 May 2019 10:58:37 +0000 (13:58 +0300)
Zend/zend_execute.c
Zend/zend_execute.h

index 13c374a572e9da3e2a3b3339dd87ec74167e4270..db9ca2286016c0902d032636787a1bc00a387379 100644 (file)
@@ -3026,6 +3026,46 @@ ZEND_API zend_bool ZEND_FASTCALL zend_verify_ref_assignable_zval(zend_reference
        return i_zend_verify_ref_assignable_zval(ref, zv, strict);
 }
 
+ZEND_API zval* zend_assign_to_typed_ref(zval *variable_ptr, zval *value, zend_uchar value_type, zend_bool strict, zend_refcounted *ref)
+{
+       zend_bool need_copy = ZEND_CONST_COND(value_type & (IS_CONST|IS_CV), 1) ||
+                       ((value_type & IS_VAR) && UNEXPECTED(ref) && GC_REFCOUNT(ref) > 1);
+       zend_bool ret;
+       zval tmp;
+
+       if (need_copy) {
+               ZVAL_COPY(&tmp, value);
+               value = &tmp;
+       }
+       ret = zend_verify_ref_assignable_zval(Z_REF_P(variable_ptr), value, strict);
+       if (need_copy) {
+               Z_TRY_DELREF_P(value);
+       }
+       if (!ret) {
+               zval_ptr_dtor(value);
+               return Z_REFVAL_P(variable_ptr);
+       }
+
+       variable_ptr = Z_REFVAL_P(variable_ptr);
+       if (EXPECTED(Z_REFCOUNTED_P(variable_ptr))) {
+               zend_refcounted *garbage = Z_COUNTED_P(variable_ptr);
+
+               zend_copy_to_variable(variable_ptr, value, value_type, ref);
+               if (GC_DELREF(garbage) == 0) {
+                       rc_dtor_func(garbage);
+               } else { /* we need to split */
+                       /* optimized version of GC_ZVAL_CHECK_POSSIBLE_ROOT(variable_ptr) */
+                       if (UNEXPECTED(GC_MAY_LEAK(garbage))) {
+                               gc_possible_root(garbage);
+                       }
+               }
+               return variable_ptr;
+       }
+
+       zend_copy_to_variable(variable_ptr, value, value_type, ref);
+       return variable_ptr;
+}
+
 ZEND_API zend_bool ZEND_FASTCALL zend_verify_prop_assignable_by_ref(zend_property_info *prop_info, zval *orig_val, zend_bool strict) {
        zval *val = orig_val;
        if (Z_ISREF_P(val) && ZEND_REF_HAS_TYPE_SOURCES(Z_REF_P(val))) {
index 494a5c999da05fd14eb0d45394cdb3f6e0148a07..b75aa0a2ad3c73ab914ea8fe840323abda9657c5 100644 (file)
@@ -78,10 +78,31 @@ ZEND_API ZEND_COLD void zend_throw_ref_type_error_type(zend_property_info *prop1
 ZEND_API void ZEND_FASTCALL zend_ref_add_type_source(zend_property_info_source_list *source_list, zend_property_info *prop);
 ZEND_API void ZEND_FASTCALL zend_ref_del_type_source(zend_property_info_source_list *source_list, zend_property_info *prop);
 
+ZEND_API zval* zend_assign_to_typed_ref(zval *variable_ptr, zval *value, zend_uchar value_type, zend_bool strict, zend_refcounted *ref);
+
+static zend_always_inline void zend_copy_to_variable(zval *variable_ptr, zval *value, zend_uchar value_type, zend_refcounted *ref)
+{
+       ZVAL_COPY_VALUE(variable_ptr, value);
+       if (ZEND_CONST_COND(value_type  == IS_CONST, 0)) {
+               if (UNEXPECTED(Z_OPT_REFCOUNTED_P(variable_ptr))) {
+                       Z_ADDREF_P(variable_ptr);
+               }
+       } else if (value_type & (IS_CONST|IS_CV)) {
+               if (Z_OPT_REFCOUNTED_P(variable_ptr)) {
+                       Z_ADDREF_P(variable_ptr);
+               }
+       } else if (ZEND_CONST_COND(value_type == IS_VAR, 1) && UNEXPECTED(ref)) {
+               if (UNEXPECTED(GC_DELREF(ref) == 0)) {
+                       efree_size(ref, sizeof(zend_reference));
+               } else if (Z_OPT_REFCOUNTED_P(variable_ptr)) {
+                       Z_ADDREF_P(variable_ptr);
+               }
+       }
+}
+
 static zend_always_inline zval* zend_assign_to_variable(zval *variable_ptr, zval *value, zend_uchar value_type, zend_bool strict)
 {
        zend_refcounted *ref = NULL;
-       zval tmp;
 
        if (ZEND_CONST_COND(value_type & (IS_VAR|IS_CV), 1) && Z_ISREF_P(value)) {
                ref = Z_COUNTED_P(value);
@@ -94,21 +115,7 @@ static zend_always_inline zval* zend_assign_to_variable(zval *variable_ptr, zval
 
                        if (Z_ISREF_P(variable_ptr)) {
                                if (UNEXPECTED(ZEND_REF_HAS_TYPE_SOURCES(Z_REF_P(variable_ptr)))) {
-                                       zend_bool need_copy = ZEND_CONST_COND(value_type & (IS_CONST|IS_CV), 1) ||
-                                               ((value_type & IS_VAR) && UNEXPECTED(ref) && GC_REFCOUNT(ref) > 1);
-                                       zend_bool ret;
-                                       if (need_copy) {
-                                               ZVAL_COPY(&tmp, value);
-                                               value = &tmp;
-                                       }
-                                       ret = zend_verify_ref_assignable_zval(Z_REF_P(variable_ptr), value, strict);
-                                       if (need_copy) {
-                                               Z_TRY_DELREF_P(value);
-                                       }
-                                       if (!ret) {
-                                               zval_ptr_dtor(value);
-                                               return Z_REFVAL_P(variable_ptr);
-                                       }
+                                       return zend_assign_to_typed_ref(variable_ptr, value, value_type, strict, ref);
                                }
 
                                variable_ptr = Z_REFVAL_P(variable_ptr);
@@ -122,22 +129,7 @@ static zend_always_inline zval* zend_assign_to_variable(zval *variable_ptr, zval
                                return variable_ptr;
                        }
                        garbage = Z_COUNTED_P(variable_ptr);
-                       ZVAL_COPY_VALUE(variable_ptr, value);
-                       if (ZEND_CONST_COND(value_type  == IS_CONST, 0)) {
-                               if (UNEXPECTED(Z_OPT_REFCOUNTED_P(variable_ptr))) {
-                                       Z_ADDREF_P(variable_ptr);
-                               }
-                       } else if (value_type & (IS_CONST|IS_CV)) {
-                               if (Z_OPT_REFCOUNTED_P(variable_ptr)) {
-                                       Z_ADDREF_P(variable_ptr);
-                               }
-                       } else if (ZEND_CONST_COND(value_type == IS_VAR, 1) && UNEXPECTED(ref)) {
-                               if (UNEXPECTED(GC_DELREF(ref) == 0)) {
-                                       efree_size(ref, sizeof(zend_reference));
-                               } else if (Z_OPT_REFCOUNTED_P(variable_ptr)) {
-                                       Z_ADDREF_P(variable_ptr);
-                               }
-                       }
+                       zend_copy_to_variable(variable_ptr, value, value_type, ref);
                        if (GC_DELREF(garbage) == 0) {
                                rc_dtor_func(garbage);
                        } else { /* we need to split */
@@ -150,22 +142,7 @@ static zend_always_inline zval* zend_assign_to_variable(zval *variable_ptr, zval
                }
        } while (0);
 
-       ZVAL_COPY_VALUE(variable_ptr, value);
-       if (ZEND_CONST_COND(value_type == IS_CONST, 0)) {
-               if (UNEXPECTED(Z_OPT_REFCOUNTED_P(variable_ptr))) {
-                       Z_ADDREF_P(variable_ptr);
-               }
-       } else if (value_type & (IS_CONST|IS_CV)) {
-               if (Z_OPT_REFCOUNTED_P(variable_ptr)) {
-                       Z_ADDREF_P(variable_ptr);
-               }
-       } else if (ZEND_CONST_COND(value_type == IS_VAR, 1) && UNEXPECTED(ref)) {
-               if (UNEXPECTED(GC_DELREF(ref) == 0)) {
-                       efree_size(ref, sizeof(zend_reference));
-               } else if (Z_OPT_REFCOUNTED_P(variable_ptr)) {
-                       Z_ADDREF_P(variable_ptr);
-               }
-       }
+       zend_copy_to_variable(variable_ptr, value, value_type, ref);
        return variable_ptr;
 }