From: Dmitry Stogov Date: Tue, 28 Aug 2018 21:35:07 +0000 (+0300) Subject: Perform run-time binding reusing HashTable bucket (without new bucket insertion). X-Git-Tag: php-7.4.0alpha1~2038 X-Git-Url: https://granicus.if.org/sourcecode?a=commitdiff_plain;h=cd9f0a22fc0fd51e86043246bb4e12d39becdcd9;p=php Perform run-time binding reusing HashTable bucket (without new bucket insertion). --- diff --git a/Zend/zend_compile.c b/Zend/zend_compile.c index 13622fbe27..45e154cbe9 100644 --- a/Zend/zend_compile.c +++ b/Zend/zend_compile.c @@ -1042,33 +1042,30 @@ static zend_never_inline ZEND_COLD ZEND_NORETURN void do_bind_function_error(zen if (old_function->type == ZEND_USER_FUNCTION && old_function->op_array.last > 0) { zend_error_noreturn(error_level, "Cannot redeclare %s() (previously declared in %s:%d)", - ZSTR_VAL(op_array->function_name), + op_array ? ZSTR_VAL(op_array->function_name) : ZSTR_VAL(old_function->common.function_name), ZSTR_VAL(old_function->op_array.filename), old_function->op_array.opcodes[0].lineno); } else { - zend_error_noreturn(error_level, "Cannot redeclare %s()", ZSTR_VAL(op_array->function_name)); + zend_error_noreturn(error_level, "Cannot redeclare %s()", + op_array ? ZSTR_VAL(op_array->function_name) : ZSTR_VAL(old_function->common.function_name)); } } ZEND_API int do_bind_function(zval *lcname) /* {{{ */ { - zend_function *function, *new_function; + zend_function *function; zval *rtd_key, *zv; rtd_key = lcname + 1; zv = zend_hash_find_ex(EG(function_table), Z_STR_P(rtd_key), 1); - new_function = function = (zend_function*)Z_PTR_P(zv); - if (function->op_array.static_variables - && !(function->op_array.fn_flags & ZEND_ACC_IMMUTABLE)) { - new_function = zend_arena_alloc(&CG(arena), sizeof(zend_op_array)); - memcpy(new_function, function, sizeof(zend_op_array)); - function->op_array.static_variables = NULL; /* NULL out the unbound function */ - if (function->op_array.refcount) { - (*function->op_array.refcount)++; - } - } - if (UNEXPECTED(zend_hash_add_ptr(EG(function_table), Z_STR_P(lcname), new_function) == NULL)) { - do_bind_function_error(Z_STR_P(lcname), &new_function->op_array, 0); + if (UNEXPECTED(!zv)) { + do_bind_function_error(Z_STR_P(lcname), NULL, 0); + return FAILURE; + } + function = (zend_function*)Z_PTR_P(zv); + zv = zend_hash_set_bucket_key(EG(function_table), (Bucket*)zv, Z_STR_P(lcname)); + if (UNEXPECTED(!zv)) { + do_bind_function_error(Z_STR_P(lcname), &function->op_array, 0); return FAILURE; } return SUCCESS; @@ -1082,22 +1079,20 @@ ZEND_API int do_bind_class(zval *lcname) /* {{{ */ rtd_key = lcname + 1; zv = zend_hash_find_ex(EG(class_table), Z_STR_P(rtd_key), 1); - ZEND_ASSERT(zv); - ce = (zend_class_entry*)Z_PTR_P(zv); + if (UNEXPECTED(!zv)) { + zend_error_noreturn(E_COMPILE_ERROR, "Cannot declare %s, because the name is already in use", Z_STRVAL_P(lcname)); + return FAILURE; + } - if (UNEXPECTED(zend_hash_add_ptr(EG(class_table), Z_STR_P(lcname), ce) == NULL)) { - /* If we're in compile time, in practice, it's quite possible - * that we'll never reach this class declaration at runtime, - * so we shut up about it. This allows the if (!defined('FOO')) { return; } - * approach to work. - */ + ce = (zend_class_entry*)Z_PTR_P(zv); + zv = zend_hash_set_bucket_key(EG(class_table), (Bucket*)zv, Z_STR_P(lcname)); + if (UNEXPECTED(!zv)) { zend_error_noreturn(E_COMPILE_ERROR, "Cannot declare %s %s, because the name is already in use", zend_get_object_type(ce), ZSTR_VAL(ce->name)); return FAILURE; - } else { - ce->refcount++; - zend_do_link_class(ce, NULL); - return SUCCESS; } + + zend_do_link_class(ce, NULL); + return SUCCESS; } /* }}} */ @@ -1110,7 +1105,7 @@ ZEND_API int do_bind_inherited_class(zval *lcname, zend_class_entry *parent_ce) zv = zend_hash_find_ex(EG(class_table), Z_STR_P(rtd_key), 1); - if (!zv) { + if (UNEXPECTED(!zv)) { /* If we're in compile time, in practice, it's quite possible * that we'll never reach this class declaration at runtime, * so we shut up about it. This allows the if (!defined('FOO')) { return; } @@ -1120,17 +1115,15 @@ ZEND_API int do_bind_inherited_class(zval *lcname, zend_class_entry *parent_ce) return FAILURE; } - ce = (zend_class_entry*)Z_PTR_P(zv); - - ce->refcount++; - /* Register the derived class */ - if (zend_hash_add_ptr(EG(class_table), Z_STR_P(lcname), ce) == NULL) { + ce = (zend_class_entry*)Z_PTR_P(zv); + zv = zend_hash_set_bucket_key(EG(class_table), (Bucket*)zv, Z_STR_P(lcname)); + if (UNEXPECTED(!zv)) { zend_error_noreturn(E_COMPILE_ERROR, "Cannot declare %s %s, because the name is already in use", zend_get_object_type(ce), ZSTR_VAL(ce->name)); + return FAILURE; } zend_do_link_class(ce, parent_ce); - return SUCCESS; } /* }}} */ diff --git a/Zend/zend_hash.c b/Zend/zend_hash.c index 3a8815336d..0caf37d7aa 100644 --- a/Zend/zend_hash.c +++ b/Zend/zend_hash.c @@ -1026,6 +1026,55 @@ ZEND_API zval* ZEND_FASTCALL zend_hash_next_index_insert_new(HashTable *ht, zval return _zend_hash_index_add_or_update_i(ht, ht->nNextFreeElement, pData, HASH_ADD | HASH_ADD_NEW | HASH_ADD_NEXT); } +ZEND_API zval* ZEND_FASTCALL zend_hash_set_bucket_key(HashTable *ht, Bucket *b, zend_string *key) +{ + uint32_t nIndex; + uint32_t idx, i; + Bucket *p, *arData; + + IS_CONSISTENT(ht); + HT_ASSERT_RC1(ht); + ZEND_ASSERT(!(HT_FLAGS(ht) & HASH_FLAG_PACKED)); + + p = zend_hash_find_bucket(ht, key, 0); + if (UNEXPECTED(p)) { + return (p == b) ? &p->val : NULL; + } + + if (!ZSTR_IS_INTERNED(key)) { + zend_string_addref(key); + HT_FLAGS(ht) &= ~HASH_FLAG_STATIC_KEYS; + } + + arData = ht->arData; + + /* del from hash */ + idx = HT_IDX_TO_HASH(b - arData); + nIndex = b->h | ht->nTableMask; + i = HT_HASH_EX(arData, nIndex); + if (i == idx) { + HT_HASH_EX(arData, nIndex) = Z_NEXT(b->val); + } else { + p = HT_HASH_TO_BUCKET_EX(arData, i); + while (Z_NEXT(p->val) != idx) { + i = Z_NEXT(p->val); + p = HT_HASH_TO_BUCKET_EX(arData, i); + } + Z_NEXT(p->val) = Z_NEXT(b->val); + } + zend_string_release(b->key); + + /* add to hash */ + idx = b - arData; + b->key = key; + b->h = ZSTR_H(key); + nIndex = b->h | ht->nTableMask; + Z_NEXT(b->val) = HT_HASH_EX(arData, nIndex); + HT_HASH_EX(arData, nIndex) = HT_IDX_TO_HASH(idx); + + return &b->val; +} + static void ZEND_FASTCALL zend_hash_do_resize(HashTable *ht) { diff --git a/Zend/zend_hash.h b/Zend/zend_hash.h index 440c92160e..7f53003ae8 100644 --- a/Zend/zend_hash.h +++ b/Zend/zend_hash.h @@ -128,6 +128,8 @@ ZEND_API zval* ZEND_FASTCALL zend_hash_index_add_empty_element(HashTable *ht, ze ZEND_API zval* ZEND_FASTCALL zend_hash_add_empty_element(HashTable *ht, zend_string *key); ZEND_API zval* ZEND_FASTCALL zend_hash_str_add_empty_element(HashTable *ht, const char *key, size_t len); +ZEND_API zval* ZEND_FASTCALL zend_hash_set_bucket_key(HashTable *ht, Bucket *p, zend_string *key); + #define ZEND_HASH_APPLY_KEEP 0 #define ZEND_HASH_APPLY_REMOVE 1<<0 #define ZEND_HASH_APPLY_STOP 1<<1