From 0f6c00200b95231bef00b9dfd94f35db924a3cc6 Mon Sep 17 00:00:00 2001 From: Dmitry Stogov Date: Thu, 25 Feb 2021 12:16:22 +0300 Subject: Speed up __sleep() and __wakeup() calls --- Zend/zend_string.h | 2 ++ ext/standard/var.c | 63 +++++++++++++++++++++++----------------- ext/standard/var_unserializer.re | 28 ++++++++++++------ 3 files changed, 58 insertions(+), 35 deletions(-) diff --git a/Zend/zend_string.h b/Zend/zend_string.h index c7580d63a4..88a2fddfb3 100644 --- a/Zend/zend_string.h +++ b/Zend/zend_string.h @@ -551,6 +551,8 @@ EMPTY_SWITCH_DEFAULT_CASE() _(ZEND_STR_FALSE, "false") \ _(ZEND_STR_NULL_LOWERCASE, "null") \ _(ZEND_STR_MIXED, "mixed") \ + _(ZEND_STR_SLEEP, "__sleep") \ + _(ZEND_STR_WAKEUP, "__wakeup") \ typedef enum _zend_known_string_id { diff --git a/ext/standard/var.c b/ext/standard/var.c index 5c13846a82..11a0873e8f 100644 --- a/ext/standard/var.c +++ b/ext/standard/var.c @@ -722,16 +722,27 @@ static inline bool php_var_serialize_class_name(smart_str *buf, zval *struc) /* } /* }}} */ -static int php_var_serialize_call_sleep(zval *retval, zval *struc) /* {{{ */ +static zend_result php_var_serialize_call_sleep(zend_object *obj, zend_function *fn, zval *retval) /* {{{ */ { - zval fname; - int res; + zend_result res; + zend_fcall_info fci; + zend_fcall_info_cache fci_cache; + + fci.size = sizeof(fci); + fci.object = obj; + fci.retval = retval; + fci.param_count = 0; + fci.params = NULL; + fci.named_params = NULL; + ZVAL_UNDEF(&fci.function_name); + + fci_cache.function_handler = fn; + fci_cache.object = obj; + fci_cache.called_scope = obj->ce; - ZVAL_STRINGL(&fname, "__sleep", sizeof("__sleep") - 1); BG(serialize_lock)++; - res = call_user_function(NULL, struc, &fname, retval, 0, 0); + res = zend_call_function(&fci, &fci_cache); BG(serialize_lock)--; - zval_ptr_dtor_str(&fname); if (res == FAILURE || Z_ISUNDEF_P(retval)) { zval_ptr_dtor(retval); @@ -739,11 +750,8 @@ static int php_var_serialize_call_sleep(zval *retval, zval *struc) /* {{{ */ } if (!HASH_OF(retval)) { - zend_class_entry *ce; - ZEND_ASSERT(Z_TYPE_P(struc) == IS_OBJECT); - ce = Z_OBJCE_P(struc); zval_ptr_dtor(retval); - php_error_docref(NULL, E_WARNING, "%s::__sleep() should return an array only containing the names of instance-variables to serialize", ZSTR_VAL(ce->name)); + php_error_docref(NULL, E_WARNING, "%s::__sleep() should return an array only containing the names of instance-variables to serialize", ZSTR_VAL(obj->ce->name)); return FAILURE; } @@ -1067,25 +1075,28 @@ again: return; } - if (ce != PHP_IC_ENTRY && zend_hash_str_exists(&ce->function_table, "__sleep", sizeof("__sleep")-1)) { - zval retval, tmp; - - ZVAL_OBJ_COPY(&tmp, Z_OBJ_P(struc)); - - if (php_var_serialize_call_sleep(&retval, &tmp) == FAILURE) { - if (!EG(exception)) { - /* we should still add element even if it's not OK, - * since we already wrote the length of the array before */ - smart_str_appendl(buf, "N;", 2); + if (ce != PHP_IC_ENTRY) { + zval *zv = zend_hash_find_ex(&ce->function_table, ZSTR_KNOWN(ZEND_STR_SLEEP), 1); + + if (zv) { + zval retval, tmp; + + ZVAL_OBJ_COPY(&tmp, Z_OBJ_P(struc)); + if (php_var_serialize_call_sleep(Z_OBJ(tmp), Z_FUNC_P(zv), &retval) == FAILURE) { + if (!EG(exception)) { + /* we should still add element even if it's not OK, + * since we already wrote the length of the array before */ + smart_str_appendl(buf, "N;", 2); + } + OBJ_RELEASE(Z_OBJ(tmp)); + return; } - zval_ptr_dtor(&tmp); + + php_var_serialize_class(buf, &tmp, &retval, var_hash); + zval_ptr_dtor(&retval); + OBJ_RELEASE(Z_OBJ(tmp)); return; } - - php_var_serialize_class(buf, &tmp, &retval, var_hash); - zval_ptr_dtor(&retval); - zval_ptr_dtor(&tmp); - return; } incomplete_class = php_var_serialize_class_name(buf, struc); diff --git a/ext/standard/var_unserializer.re b/ext/standard/var_unserializer.re index fb1ab2f496..a14e34125d 100644 --- a/ext/standard/var_unserializer.re +++ b/ext/standard/var_unserializer.re @@ -222,8 +222,6 @@ PHPAPI void var_destroy(php_unserialize_data_t *var_hashx) var_entries *var_hash = (*var_hashx)->entries.next; var_dtor_entries *var_dtor_hash = (*var_hashx)->first_dtor; bool delayed_call_failed = 0; - zval wakeup_name; - ZVAL_UNDEF(&wakeup_name); #if VAR_ENTRIES_DBG fprintf(stderr, "var_destroy( " ZEND_LONG_FMT ")\n", var_hash?var_hash->used_slots:-1L); @@ -246,12 +244,26 @@ PHPAPI void var_destroy(php_unserialize_data_t *var_hashx) /* Perform delayed __wakeup calls */ if (!delayed_call_failed) { zval retval; - if (Z_ISUNDEF(wakeup_name)) { - ZVAL_STRINGL(&wakeup_name, "__wakeup", sizeof("__wakeup") - 1); - } + zend_fcall_info fci; + zend_fcall_info_cache fci_cache; + + ZEND_ASSERT(Z_TYPE_P(zv) == IS_OBJECT); + + fci.size = sizeof(fci); + fci.object = Z_OBJ_P(zv); + fci.retval = &retval; + fci.param_count = 0; + fci.params = NULL; + fci.named_params = NULL; + ZVAL_UNDEF(&fci.function_name); + + fci_cache.function_handler = zend_hash_find_ptr( + &fci.object->ce->function_table, ZSTR_KNOWN(ZEND_STR_WAKEUP)); + fci_cache.object = fci.object; + fci_cache.called_scope = fci.object->ce; BG(serialize_lock)++; - if (call_user_function(NULL, zv, &wakeup_name, &retval, 0, 0) == FAILURE || Z_ISUNDEF(retval)) { + if (zend_call_function(&fci, &fci_cache) == FAILURE || Z_ISUNDEF(retval)) { delayed_call_failed = 1; GC_ADD_FLAGS(Z_OBJ_P(zv), IS_OBJ_DESTRUCTOR_CALLED); } @@ -288,8 +300,6 @@ PHPAPI void var_destroy(php_unserialize_data_t *var_hashx) var_dtor_hash = next; } - zval_ptr_dtor_nogc(&wakeup_name); - if ((*var_hashx)->ref_props) { zend_hash_destroy((*var_hashx)->ref_props); FREE_HASHTABLE((*var_hashx)->ref_props); @@ -788,7 +798,7 @@ static inline int object_common(UNSERIALIZE_PARAMETER, zend_long elements, bool } has_wakeup = Z_OBJCE_P(rval) != PHP_IC_ENTRY - && zend_hash_str_exists(&Z_OBJCE_P(rval)->function_table, "__wakeup", sizeof("__wakeup")-1); + && zend_hash_exists(&Z_OBJCE_P(rval)->function_table, ZSTR_KNOWN(ZEND_STR_WAKEUP)); ht = Z_OBJPROP_P(rval); if (elements >= (zend_long)(HT_MAX_SIZE - zend_hash_num_elements(ht))) { -- cgit v1.2.1