From: Dmitry Stogov Date: Thu, 17 Jan 2019 21:53:48 +0000 (+0300) Subject: Improved ZEND_TRY_ASSIGN... API to avoid unnecessary double copying and reduce code... X-Git-Tag: php-7.4.0alpha1~1203 X-Git-Url: https://granicus.if.org/sourcecode?a=commitdiff_plain;h=e6182bd4eae63529d9d4a24f67f3a8c1df0e207b;p=php Improved ZEND_TRY_ASSIGN... API to avoid unnecessary double copying and reduce code bloat. --- diff --git a/Zend/zend_API.c b/Zend/zend_API.c index 3154f4808f..234422ef65 100644 --- a/Zend/zend_API.c +++ b/Zend/zend_API.c @@ -3685,6 +3685,133 @@ ZEND_API int zend_declare_typed_property(zend_class_entry *ce, zend_string *name } /* }}} */ +ZEND_API int zend_try_assign_typed_ref_ex(zend_reference *ref, zval *val, zend_bool strict) /* {{{ */ +{ + if (UNEXPECTED(!zend_verify_ref_assignable_zval(ref, val, strict))) { + zval_ptr_dtor(val); + return FAILURE; + } else { + zval_ptr_dtor(&ref->val); + ZVAL_COPY_VALUE(&ref->val, val); + return SUCCESS; + } +} +/* }}} */ + +ZEND_API int zend_try_assign_typed_ref(zend_reference *ref, zval *val) /* {{{ */ +{ + return zend_try_assign_typed_ref_ex(ref, val, ZEND_ARG_USES_STRICT_TYPES()); +} +/* }}} */ + +ZEND_API int zend_try_assign_typed_ref_null(zend_reference *ref) /* {{{ */ +{ + zval tmp; + + ZVAL_NULL(&tmp); + return zend_try_assign_typed_ref(ref, &tmp); +} +/* }}} */ + +ZEND_API int zend_try_assign_typed_ref_bool(zend_reference *ref, zend_bool val) /* {{{ */ +{ + zval tmp; + + ZVAL_BOOL(&tmp, val); + return zend_try_assign_typed_ref(ref, &tmp); +} +/* }}} */ + +ZEND_API int zend_try_assign_typed_ref_long(zend_reference *ref, zend_long lval) /* {{{ */ +{ + zval tmp; + + ZVAL_LONG(&tmp, lval); + return zend_try_assign_typed_ref(ref, &tmp); +} +/* }}} */ + +ZEND_API int zend_try_assign_typed_ref_double(zend_reference *ref, double dval) /* {{{ */ +{ + zval tmp; + + ZVAL_DOUBLE(&tmp, dval); + return zend_try_assign_typed_ref(ref, &tmp); +} +/* }}} */ + +ZEND_API int zend_try_assign_typed_ref_empty_string(zend_reference *ref) /* {{{ */ +{ + zval tmp; + + ZVAL_EMPTY_STRING(&tmp); + return zend_try_assign_typed_ref(ref, &tmp); +} +/* }}} */ + +ZEND_API int zend_try_assign_typed_ref_str(zend_reference *ref, zend_string *str) /* {{{ */ +{ + zval tmp; + + ZVAL_STR(&tmp, str); + return zend_try_assign_typed_ref(ref, &tmp); +} +/* }}} */ + +ZEND_API int zend_try_assign_typed_ref_string(zend_reference *ref, const char *string) /* {{{ */ +{ + zval tmp; + + ZVAL_STRING(&tmp, string); + return zend_try_assign_typed_ref(ref, &tmp); +} +/* }}} */ + +ZEND_API int zend_try_assign_typed_ref_stringl(zend_reference *ref, const char *string, size_t len) /* {{{ */ +{ + zval tmp; + + ZVAL_STRINGL(&tmp, string, len); + return zend_try_assign_typed_ref(ref, &tmp); +} +/* }}} */ + +ZEND_API int zend_try_assign_typed_ref_arr(zend_reference *ref, zend_array *arr) /* {{{ */ +{ + zval tmp; + + ZVAL_ARR(&tmp, arr); + return zend_try_assign_typed_ref(ref, &tmp); +} +/* }}} */ + +ZEND_API int zend_try_assign_typed_ref_res(zend_reference *ref, zend_resource *res) /* {{{ */ +{ + zval tmp; + + ZVAL_RES(&tmp, res); + return zend_try_assign_typed_ref(ref, &tmp); +} +/* }}} */ + +ZEND_API int zend_try_assign_typed_ref_zval(zend_reference *ref, zval *zv) /* {{{ */ +{ + zval tmp; + + ZVAL_COPY_VALUE(&tmp, zv); + return zend_try_assign_typed_ref(ref, &tmp); +} +/* }}} */ + +ZEND_API int zend_try_assign_typed_ref_zval_ex(zend_reference *ref, zval *zv, zend_bool strict) /* {{{ */ +{ + zval tmp; + + ZVAL_COPY_VALUE(&tmp, zv); + return zend_try_assign_typed_ref_ex(ref, &tmp, strict); +} +/* }}} */ + ZEND_API int zend_declare_property_ex(zend_class_entry *ce, zend_string *name, zval *property, int access_type, zend_string *doc_comment) /* {{{ */ { return zend_declare_typed_property(ce, name, property, access_type, doc_comment, 0); diff --git a/Zend/zend_API.h b/Zend/zend_API.h index f434ff4e02..6bc7ed4ffb 100644 --- a/Zend/zend_API.h +++ b/Zend/zend_API.h @@ -658,130 +658,266 @@ END_EXTERN_C() /* May modify arg in-place. Will free arg in failure case (and take ownership in success case). * Prefer using the ZEND_TRY_ASSIGN_* macros over these APIs. */ -static zend_always_inline int zend_try_assign_ex(zval *zv, zval *arg, zend_bool strict) { - if (EXPECTED(Z_ISREF_P(zv))) { - zend_reference *ref = Z_REF_P(zv); - zv = Z_REFVAL_P(zv); - if (UNEXPECTED(ZEND_REF_HAS_TYPE_SOURCES(ref)) && - UNEXPECTED(!zend_verify_ref_assignable_zval(ref, arg, strict)) - ) { - zval_ptr_dtor(arg); - return FAILURE; - } - } - - zval_ptr_dtor(zv); - ZVAL_COPY_VALUE(zv, arg); - return SUCCESS; -} - -static zend_always_inline int zend_try_assign(zval *zv, zval *arg) { - return zend_try_assign_ex(zv, arg, ZEND_ARG_USES_STRICT_TYPES()); -} +ZEND_API int zend_try_assign_typed_ref_ex(zend_reference *ref, zval *zv, zend_bool strict); +ZEND_API int zend_try_assign_typed_ref(zend_reference *ref, zval *zv); + +ZEND_API int zend_try_assign_typed_ref_null(zend_reference *ref); +ZEND_API int zend_try_assign_typed_ref_bool(zend_reference *ref, zend_bool val); +ZEND_API int zend_try_assign_typed_ref_long(zend_reference *ref, zend_long lval); +ZEND_API int zend_try_assign_typed_ref_double(zend_reference *ref, double dval); +ZEND_API int zend_try_assign_typed_ref_empty_string(zend_reference *ref); +ZEND_API int zend_try_assign_typed_ref_str(zend_reference *ref, zend_string *str); +ZEND_API int zend_try_assign_typed_ref_string(zend_reference *ref, const char *string); +ZEND_API int zend_try_assign_typed_ref_stringl(zend_reference *ref, const char *string, size_t len); +ZEND_API int zend_try_assign_typed_ref_arr(zend_reference *ref, zend_array *arr); +ZEND_API int zend_try_assign_typed_ref_res(zend_reference *ref, zend_resource *res); +ZEND_API int zend_try_assign_typed_ref_zval(zend_reference *ref, zval *zv); +ZEND_API int zend_try_assign_typed_ref_zval_ex(zend_reference *ref, zval *zv, zend_bool strict); #define ZEND_TRY_ASSIGN_NULL(zv) do { \ - zval _zv; \ - ZVAL_NULL(&_zv); \ - zend_try_assign(zv, &_zv); \ + zval *_zv = zv; \ + if (EXPECTED(Z_ISREF_P(_zv))) { \ + zend_reference *ref = Z_REF_P(_zv); \ + if (UNEXPECTED(ZEND_REF_HAS_TYPE_SOURCES(ref))) { \ + zend_try_assign_typed_ref_null(ref); \ + break; \ + } \ + _zv = &ref->val; \ + } \ + zval_ptr_dtor(_zv); \ + ZVAL_NULL(_zv); \ } while (0) #define ZEND_TRY_ASSIGN_FALSE(zv) do { \ - zval _zv; \ - ZVAL_FALSE(&_zv); \ - zend_try_assign(zv, &_zv); \ + zval *_zv = zv; \ + if (EXPECTED(Z_ISREF_P(_zv))) { \ + zend_reference *ref = Z_REF_P(_zv); \ + if (UNEXPECTED(ZEND_REF_HAS_TYPE_SOURCES(ref))) { \ + zend_try_assign_typed_ref_bool(ref, 0); \ + break; \ + } \ + _zv = &ref->val; \ + } \ + zval_ptr_dtor(_zv); \ + ZVAL_FALSE(_zv); \ } while (0) #define ZEND_TRY_ASSIGN_TRUE(zv) do { \ - zval _zv; \ - ZVAL_TRUE(&_zv); \ - zend_try_assign(zv, &_zv); \ + zval *_zv = zv; \ + if (EXPECTED(Z_ISREF_P(_zv))) { \ + zend_reference *ref = Z_REF_P(_zv); \ + if (UNEXPECTED(ZEND_REF_HAS_TYPE_SOURCES(ref))) { \ + zend_try_assign_typed_ref_bool(ref, 1); \ + break; \ + } \ + _zv = &ref->val; \ + } \ + zval_ptr_dtor(_zv); \ + ZVAL_TRUE(_zv); \ } while (0) #define ZEND_TRY_ASSIGN_LONG(zv, lval) do { \ - zval _zv; \ - ZVAL_LONG(&_zv, lval); \ - zend_try_assign(zv, &_zv); \ + zval *_zv = zv; \ + if (EXPECTED(Z_ISREF_P(_zv))) { \ + zend_reference *ref = Z_REF_P(_zv); \ + if (UNEXPECTED(ZEND_REF_HAS_TYPE_SOURCES(ref))) { \ + zend_try_assign_typed_ref_long(ref, lval); \ + break; \ + } \ + _zv = &ref->val; \ + } \ + zval_ptr_dtor(_zv); \ + ZVAL_LONG(_zv, lval); \ } while (0) #define ZEND_TRY_ASSIGN_DOUBLE(zv, dval) do { \ - zval _zv; \ - ZVAL_DOUBLE(&_zv, dval); \ - zend_try_assign(zv, &_zv); \ + zval *_zv = zv; \ + if (EXPECTED(Z_ISREF_P(_zv))) { \ + zend_reference *ref = Z_REF_P(_zv); \ + if (UNEXPECTED(ZEND_REF_HAS_TYPE_SOURCES(ref))) { \ + zend_try_assign_typed_ref_double(ref, dval); \ + break; \ + } \ + _zv = &ref->val; \ + } \ + zval_ptr_dtor(_zv); \ + ZVAL_DOUBLE(_zv, dval); \ } while (0) #define ZEND_TRY_ASSIGN_EMPTY_STRING(zv) do { \ - zval _zv; \ - ZVAL_EMPTY_STRING(&_zv); \ - zend_try_assign(zv, &_zv); \ + zval *_zv = zv; \ + if (EXPECTED(Z_ISREF_P(_zv))) { \ + zend_reference *ref = Z_REF_P(_zv); \ + if (UNEXPECTED(ZEND_REF_HAS_TYPE_SOURCES(ref))) { \ + zend_try_assign_typed_ref_empty_string(ref); \ + break; \ + } \ + _zv = &ref->val; \ + } \ + zval_ptr_dtor(_zv); \ + ZVAL_EMPTY_STRING(_zv); \ } while (0) #define ZEND_TRY_ASSIGN_STR(zv, str) do { \ - zval _zv; \ - ZVAL_STR(&_zv, str); \ - zend_try_assign(zv, &_zv); \ + zval *_zv = zv; \ + if (EXPECTED(Z_ISREF_P(_zv))) { \ + zend_reference *ref = Z_REF_P(_zv); \ + if (UNEXPECTED(ZEND_REF_HAS_TYPE_SOURCES(ref))) { \ + zend_try_assign_typed_ref_str(ref, str); \ + break; \ + } \ + _zv = &ref->val; \ + } \ + zval_ptr_dtor(_zv); \ + ZVAL_STR(_zv, str); \ } while (0) #define ZEND_TRY_ASSIGN_NEW_STR(zv, str) do { \ - zval _zv; \ - ZVAL_NEW_STR(&_zv, str); \ - zend_try_assign(zv, &_zv); \ + zval *_zv = zv; \ + if (EXPECTED(Z_ISREF_P(_zv))) { \ + zend_reference *ref = Z_REF_P(_zv); \ + if (UNEXPECTED(ZEND_REF_HAS_TYPE_SOURCES(ref))) { \ + zend_try_assign_typed_ref_str(ref, str); \ + break; \ + } \ + _zv = &ref->val; \ + } \ + zval_ptr_dtor(_zv); \ + ZVAL_NEW_STR(_zv, str); \ } while (0) #define ZEND_TRY_ASSIGN_STRING(zv, string) do { \ - zval _zv; \ - ZVAL_STRING(&_zv, string); \ - zend_try_assign(zv, &_zv); \ + zval *_zv = zv; \ + if (EXPECTED(Z_ISREF_P(_zv))) { \ + zend_reference *ref = Z_REF_P(_zv); \ + if (UNEXPECTED(ZEND_REF_HAS_TYPE_SOURCES(ref))) { \ + zend_try_assign_typed_ref_string(ref, string); \ + break; \ + } \ + _zv = &ref->val; \ + } \ + zval_ptr_dtor(_zv); \ + ZVAL_STRING(_zv, string); \ } while (0) #define ZEND_TRY_ASSIGN_STRINGL(zv, string, len) do { \ - zval _zv; \ - ZVAL_STRINGL(&_zv, string, len); \ - zend_try_assign(zv, &_zv); \ + zval *_zv = zv; \ + if (EXPECTED(Z_ISREF_P(_zv))) { \ + zend_reference *ref = Z_REF_P(_zv); \ + if (UNEXPECTED(ZEND_REF_HAS_TYPE_SOURCES(ref))) { \ + zend_try_assign_typed_ref_stringl(ref, string, len); \ + break; \ + } \ + _zv = &ref->val; \ + } \ + zval_ptr_dtor(_zv); \ + ZVAL_STRINGL(_zv, string, len); \ +} while (0) + +#define ZEND_TRY_ASSIGN_ARR(zv, arr) do { \ + zval *_zv = zv; \ + if (EXPECTED(Z_ISREF_P(_zv))) { \ + zend_reference *ref = Z_REF_P(_zv); \ + if (UNEXPECTED(ZEND_REF_HAS_TYPE_SOURCES(ref))) { \ + zend_try_assign_typed_ref_arr(ref, arr); \ + break; \ + } \ + _zv = &ref->val; \ + } \ + zval_ptr_dtor(_zv); \ + ZVAL_ARR(_zv, arr); \ } while (0) #define ZEND_TRY_ASSIGN_RES(zv, res) do { \ - zval _zv; \ - ZVAL_RES(&_zv, res); \ - zend_try_assign(zv, &_zv); \ + zval *_zv = zv; \ + if (EXPECTED(Z_ISREF_P(_zv))) { \ + zend_reference *ref = Z_REF_P(_zv); \ + if (UNEXPECTED(ZEND_REF_HAS_TYPE_SOURCES(ref))) { \ + zend_try_assign_typed_ref_res(ref, res); \ + break; \ + } \ + _zv = &ref->val; \ + } \ + zval_ptr_dtor(_zv); \ + ZVAL_RES(_zv, res); \ +} while (0) + +#define ZEND_TRY_ASSIGN_TMP(zv, other_zv) do { \ + zval *_zv = zv; \ + if (EXPECTED(Z_ISREF_P(_zv))) { \ + zend_reference *ref = Z_REF_P(_zv); \ + if (UNEXPECTED(ZEND_REF_HAS_TYPE_SOURCES(ref))) { \ + zend_try_assign_typed_ref(ref, other_zv); \ + break; \ + } \ + _zv = &ref->val; \ + } \ + zval_ptr_dtor(_zv); \ + ZVAL_COPY_VALUE(_zv, other_zv); \ +} while (0) + +#define ZEND_TRY_ASSIGN_VALUE(zv, other_zv) do { \ + zval *_zv = zv; \ + if (EXPECTED(Z_ISREF_P(_zv))) { \ + zend_reference *ref = Z_REF_P(_zv); \ + if (UNEXPECTED(ZEND_REF_HAS_TYPE_SOURCES(ref))) { \ + zend_try_assign_typed_ref_zval(ref, other_zv); \ + break; \ + } \ + _zv = &ref->val; \ + } \ + zval_ptr_dtor(_zv); \ + ZVAL_COPY_VALUE(_zv, other_zv); \ +} while (0) + +#define ZEND_TRY_ASSIGN_COPY(zv, other_zv) do { \ + Z_TRY_ADDREF_P(other_zv); \ + ZEND_TRY_ASSIGN_VALUE(zv, other_zv); \ } while (0) #define ZEND_TRY_ASSIGN_VALUE_EX(zv, other_zv, strict) do { \ - zval _zv; \ - ZVAL_COPY_VALUE(&_zv, other_zv); \ - zend_try_assign_ex(zv, &_zv, strict); \ + zval *_zv = zv; \ + if (EXPECTED(Z_ISREF_P(_zv))) { \ + zend_reference *ref = Z_REF_P(_zv); \ + if (UNEXPECTED(ZEND_REF_HAS_TYPE_SOURCES(ref))) { \ + zend_try_assign_typed_ref_zval_ex(ref, other_zv, strict); \ + break; \ + } \ + _zv = &ref->val; \ + } \ + zval_ptr_dtor(_zv); \ + ZVAL_COPY_VALUE(_zv, other_zv); \ } while (0) #define ZEND_TRY_ASSIGN_COPY_EX(zv, other_zv, strict) do { \ - zval _zv; \ - ZVAL_COPY(&_zv, other_zv); \ - zend_try_assign_ex(zv, &_zv, strict); \ + Z_TRY_ADDREF_P(other_zv); \ + ZEND_TRY_ASSIGN_VALUE_EX(zv, other_zv, strict); \ } while (0) -#define ZEND_TRY_ASSIGN_VALUE(zv, other_zv) \ - ZEND_TRY_ASSIGN_VALUE_EX(zv, other_zv, ZEND_ARG_USES_STRICT_TYPES()) -#define ZEND_TRY_ASSIGN_COPY(zv, other_zv) \ - ZEND_TRY_ASSIGN_COPY_EX(zv, other_zv, ZEND_ARG_USES_STRICT_TYPES()) - /* Initializes a reference to an empty array and returns dereferenced zval, * or NULL if the initialization failed. */ -static zend_always_inline zval *zend_try_array_init(zval *zv) { - zval tmp; - ZVAL_ARR(&tmp, zend_new_array(0)); - if (UNEXPECTED(zend_try_assign(zv, &tmp) == FAILURE)) { - return NULL; +static zend_always_inline zval *zend_try_array_init_size(zval *zv, uint32_t size) +{ + zend_array *arr = zend_new_array(size); + + if (EXPECTED(Z_ISREF_P(zv))) { + zend_reference *ref = Z_REF_P(zv); + if (UNEXPECTED(ZEND_REF_HAS_TYPE_SOURCES(ref))) { + if (zend_try_assign_typed_ref_arr(ref, arr) != SUCCESS) { + return NULL; + } + return &ref->val; + } + zv = &ref->val; } - ZVAL_DEREF(zv); + zval_ptr_dtor(zv); + ZVAL_ARR(zv, arr); return zv; } -static zend_always_inline zval *zend_try_array_init_size(zval *zv, uint32_t size) { - zval tmp; - ZVAL_ARR(&tmp, zend_new_array(size)); - if (UNEXPECTED(zend_try_assign(zv, &tmp) == FAILURE)) { - return NULL; - } - ZVAL_DEREF(zv); - return zv; +static zend_always_inline zval *zend_try_array_init(zval *zv) +{ + return zend_try_array_init_size(zv, 0); } /* Fast parameter parsing API */ diff --git a/ext/standard/type.c b/ext/standard/type.c index bc1233e35d..e783a37b40 100644 --- a/ext/standard/type.c +++ b/ext/standard/type.c @@ -84,7 +84,7 @@ PHP_FUNCTION(settype) RETURN_FALSE; } - zend_try_assign(var, &tmp); + ZEND_TRY_ASSIGN_TMP(var, &tmp); RETVAL_TRUE; } /* }}} */