diff options
-rw-r--r-- | Zend/zend_execute.c | 40 | ||||
-rw-r--r-- | Zend/zend_execute.h | 73 |
2 files changed, 65 insertions, 48 deletions
diff --git a/Zend/zend_execute.c b/Zend/zend_execute.c index 10dd64a15d..bf4a8e224a 100644 --- a/Zend/zend_execute.c +++ b/Zend/zend_execute.c @@ -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))) { diff --git a/Zend/zend_execute.h b/Zend/zend_execute.h index e34c56def2..053d22afd0 100644 --- a/Zend/zend_execute.h +++ b/Zend/zend_execute.h @@ -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); @@ -117,22 +124,7 @@ static zend_always_inline zval* zend_assign_to_variable(zval *variable_ptr, zval } } 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 */ @@ -145,22 +137,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; } |