From: Dmitry Stogov Date: Mon, 15 Sep 2014 21:34:27 +0000 (+0400) Subject: Optimized FE_RESET and FE_FETCH X-Git-Tag: PRE_NATIVE_TLS_MERGE~158^2~85^2~10 X-Git-Url: https://granicus.if.org/sourcecode?a=commitdiff_plain;h=69917807939170c5a5e0ec6da1c7f9dd9633c6ae;p=php Optimized FE_RESET and FE_FETCH --- diff --git a/Zend/zend_hash.c b/Zend/zend_hash.c index 5354ce29f6..e651cd85e5 100644 --- a/Zend/zend_hash.c +++ b/Zend/zend_hash.c @@ -1489,58 +1489,6 @@ ZEND_API zend_bool zend_hash_index_exists(const HashTable *ht, zend_ulong h) } -ZEND_API zend_bool zend_hash_get_pointer(const HashTable *ht, HashPointer *ptr) -{ - ptr->pos = ht->nInternalPointer; - ptr->ht = (HashTable*)ht; - if (ht->nInternalPointer != INVALID_IDX) { - ptr->h = ht->arData[ht->nInternalPointer].h; - return 1; - } else { - ptr->h = 0; - return 0; - } -} - -ZEND_API zend_bool zend_hash_set_pointer(HashTable *ht, const HashPointer *ptr) -{ - uint32_t idx; - - if (ptr->pos == INVALID_IDX) { - ht->nInternalPointer = INVALID_IDX; - } else if (ptr->ht != ht) { - IS_CONSISTENT(ht); - for (idx = 0; idx < ht->nNumUsed; idx++) { - if (Z_TYPE(ht->arData[idx].val) != IS_UNDEF) { - ht->nInternalPointer = idx; - return 0; - } - } - idx = INVALID_IDX; - return 0; - } else if (ht->nInternalPointer != ptr->pos) { - IS_CONSISTENT(ht); - if (ht->u.flags & HASH_FLAG_PACKED) { - if (ptr->h < ht->nNumUsed && - Z_TYPE(ht->arData[ptr->h].val) != IS_UNDEF) { - ht->nInternalPointer = ptr->h; - return 1; - } - } else { - idx = ht->arHash[ptr->h & ht->nTableMask]; - while (idx != INVALID_IDX) { - if (ht->arData[idx].h == ptr->h && idx == ptr->pos) { - ht->nInternalPointer = idx; - return 1; - } - idx = Z_NEXT(ht->arData[idx].val); - } - } - return 0; - } - return 1; -} - ZEND_API void zend_hash_internal_pointer_reset_ex(HashTable *ht, HashPosition *pos) { uint32_t idx; diff --git a/Zend/zend_hash.h b/Zend/zend_hash.h index cf91bac503..6752ccb074 100644 --- a/Zend/zend_hash.h +++ b/Zend/zend_hash.h @@ -177,9 +177,6 @@ typedef struct _HashPointer { zend_ulong h; } HashPointer; -ZEND_API zend_bool zend_hash_get_pointer(const HashTable *ht, HashPointer *ptr); -ZEND_API zend_bool zend_hash_set_pointer(HashTable *ht, const HashPointer *ptr); - #define zend_hash_has_more_elements(ht) \ zend_hash_has_more_elements_ex(ht, &(ht)->nInternalPointer) #define zend_hash_move_forward(ht) \ diff --git a/Zend/zend_iterators.c b/Zend/zend_iterators.c index 7c08ac59b5..c2f51c4dd6 100644 --- a/Zend/zend_iterators.c +++ b/Zend/zend_iterators.c @@ -84,29 +84,13 @@ ZEND_API void zend_iterator_dtor(zend_object_iterator *iter TSRMLS_DC) zend_objects_store_del(&iter->std TSRMLS_CC); } -ZEND_API enum zend_object_iterator_kind zend_iterator_unwrap( - zval *array_ptr, zend_object_iterator **iter TSRMLS_DC) +ZEND_API zend_object_iterator* zend_iterator_unwrap(zval *array_ptr TSRMLS_DC) { - switch (Z_TYPE_P(array_ptr)) { - case IS_OBJECT: - if (Z_OBJ_HT_P(array_ptr) == &iterator_object_handlers) { - *iter = (zend_object_iterator *)Z_OBJ_P(array_ptr); - return ZEND_ITER_OBJECT; - } - if (Z_OBJPROP_P(array_ptr)) { - return ZEND_ITER_PLAIN_OBJECT; - } - return ZEND_ITER_INVALID; - - case IS_ARRAY: - if (Z_ARRVAL_P(array_ptr)) { - return ZEND_ITER_PLAIN_ARRAY; - } - return ZEND_ITER_INVALID; - - default: - return ZEND_ITER_INVALID; + if (Z_TYPE_P(array_ptr) && + Z_OBJ_HT_P(array_ptr) == &iterator_object_handlers) { + return (zend_object_iterator *)Z_OBJ_P(array_ptr); } + return NULL; } /* diff --git a/Zend/zend_iterators.h b/Zend/zend_iterators.h index b0105d52e2..a1148db214 100644 --- a/Zend/zend_iterators.h +++ b/Zend/zend_iterators.h @@ -71,16 +71,9 @@ typedef struct _zend_class_iterator_funcs { union _zend_function *zf_rewind; } zend_class_iterator_funcs; -enum zend_object_iterator_kind { - ZEND_ITER_INVALID, - ZEND_ITER_PLAIN_ARRAY, - ZEND_ITER_PLAIN_OBJECT, - ZEND_ITER_OBJECT -}; - BEGIN_EXTERN_C() /* given a zval, returns stuff that can be used to iterate it. */ -ZEND_API enum zend_object_iterator_kind zend_iterator_unwrap(zval *array_ptr, zend_object_iterator **iter TSRMLS_DC); +ZEND_API zend_object_iterator* zend_iterator_unwrap(zval *array_ptr TSRMLS_DC); /* given an iterator, wrap it up as a zval for use by the engine opcodes */ ZEND_API void zend_iterator_init(zend_object_iterator *iter TSRMLS_DC); diff --git a/Zend/zend_vm_def.h b/Zend/zend_vm_def.h index 658e7ce7d6..97351ce5d9 100644 --- a/Zend/zend_vm_def.h +++ b/Zend/zend_vm_def.h @@ -4604,25 +4604,37 @@ ZEND_VM_HANDLER(77, ZEND_FE_RESET, CONST|TMP|VAR|CV, ANY) } iter->index = -1; /* will be set to 0 before using next handler */ } else if ((fe_ht = HASH_OF(array_ptr)) != NULL) { - zend_hash_internal_pointer_reset(fe_ht); - if (ce) { - zend_object *zobj = Z_OBJ_P(array_ptr); - while (zend_hash_has_more_elements(fe_ht) == SUCCESS) { - zend_string *str_key; - zend_ulong int_key; - zend_uchar key_type; - - key_type = zend_hash_get_current_key(fe_ht, &str_key, &int_key, 0); - if (key_type != HASH_KEY_NON_EXISTENT && - (key_type == HASH_KEY_IS_LONG || - zend_check_property_access(zobj, str_key TSRMLS_CC) == SUCCESS)) { - break; + HashPointer *ptr = (HashPointer*)EX_VAR((opline+2)->op1.var); + HashPosition pos = 0; + Bucket *p; + + while (1) { + if (pos >= fe_ht->nNumUsed) { + is_empty = 1; + if (OP1_TYPE == IS_VAR && opline->extended_value & ZEND_FE_RESET_VARIABLE) { + FREE_OP1_VAR_PTR(); } - zend_hash_move_forward(fe_ht); + ZEND_VM_JMP(opline->op2.jmp_addr); } + p = fe_ht->arData + pos; + if (Z_TYPE(p->val) == IS_UNDEF || + (Z_TYPE(p->val) == IS_INDIRECT && + Z_TYPE_P(Z_INDIRECT(p->val)) == IS_UNDEF)) { + pos++; + continue; + } + if (!ce || + !p->key || + zend_check_property_access(Z_OBJ_P(array_ptr), p->key TSRMLS_CC) == SUCCESS) { + break; + } + pos++; } - is_empty = zend_hash_has_more_elements(fe_ht) != SUCCESS; - zend_hash_get_pointer(fe_ht, (HashPointer*)EX_VAR((opline+2)->op1.var)); + fe_ht->nInternalPointer = pos; + ptr->pos = pos; + ptr->ht = fe_ht; + ptr->h = fe_ht->arData[pos].h; + is_empty = 0; } else { zend_error(E_WARNING, "Invalid argument supplied for foreach()"); is_empty = 1; @@ -4646,8 +4658,9 @@ ZEND_VM_HANDLER(78, ZEND_FE_FETCH, VAR, ANY) zval *array, *array_ref; zval *value; HashTable *fe_ht; - zend_object_iterator *iter = NULL; - zval *key = NULL; + HashPointer *ptr; + HashPosition pos; + Bucket *p; array = array_ref = EX_VAR(opline->op1.var); if (Z_ISREF_P(array)) { @@ -4657,81 +4670,182 @@ ZEND_VM_HANDLER(78, ZEND_FE_FETCH, VAR, ANY) zval_copy_ctor(array); } } - if (opline->extended_value & ZEND_FE_FETCH_WITH_KEY) { - key = EX_VAR((opline+1)->result.var); - } SAVE_OPLINE(); - switch (zend_iterator_unwrap(array, &iter TSRMLS_CC)) { - default: - case ZEND_ITER_INVALID: - zend_error(E_WARNING, "Invalid argument supplied for foreach()"); + if (EXPECTED(Z_TYPE_P(array) == IS_ARRAY)) { + fe_ht = Z_ARRVAL_P(array); + ptr = (HashPointer*)EX_VAR((opline+1)->op1.var); + pos = ptr->pos; + if (UNEXPECTED(pos == INVALID_IDX)) { + /* reached end of iteration */ ZEND_VM_JMP(opline->op2.jmp_addr); - - case ZEND_ITER_PLAIN_OBJECT: { - zend_object *zobj = Z_OBJ_P(array); - int key_type; - zend_string *str_key; - zend_ulong int_key; - - fe_ht = Z_OBJPROP_P(array); - zend_hash_set_pointer(fe_ht, (HashPointer*)EX_VAR((opline+1)->op1.var)); + } else if (UNEXPECTED(ptr->ht != fe_ht)) { + ptr->ht = fe_ht; + pos = 0; + } else if (UNEXPECTED(fe_ht->nInternalPointer != ptr->pos)) { + if (fe_ht->u.flags & HASH_FLAG_PACKED) { + pos = ptr->h; + } else { + pos = fe_ht->arHash[ptr->h & fe_ht->nTableMask]; + while (pos != INVALID_IDX) { + if (fe_ht->arData[pos].h == ptr->h && pos == ptr->pos) { + break; + } + pos = Z_NEXT(fe_ht->arData[pos].val); + } + } + } + while (1) { + if (UNEXPECTED(pos >= fe_ht->nNumUsed)) { + /* reached end of iteration */ + ZEND_VM_JMP(opline->op2.jmp_addr); + } + p = fe_ht->arData + pos; + value = &p->val; + if (UNEXPECTED(Z_TYPE_P(value) == IS_UNDEF)) { + pos++; + continue; + } else if (UNEXPECTED(Z_TYPE_P(value) == IS_INDIRECT)) { + value = Z_INDIRECT_P(value); + if (UNEXPECTED(Z_TYPE_P(value) == IS_UNDEF)) { + pos++; + continue; + } + } + if (opline->extended_value & ZEND_FE_FETCH_BYREF) { + ZVAL_MAKE_REF(value); + Z_ADDREF_P(value); + ZVAL_REF(EX_VAR(opline->result.var), Z_REF_P(value)); + } else { + ZVAL_COPY(EX_VAR(opline->result.var), value); + } + if (opline->extended_value & ZEND_FE_FETCH_WITH_KEY) { + if (!p->key) { + ZVAL_LONG(EX_VAR((opline+1)->result.var), p->h); + } else if (IS_INTERNED(p->key)) { + ZVAL_INTERNED_STR(EX_VAR((opline+1)->result.var), p->key); + } else { + ZVAL_NEW_STR(EX_VAR((opline+1)->result.var), p->key); + GC_REFCOUNT(p->key)++; + } + } + break; + } + do { + pos++; + if (pos >= fe_ht->nNumUsed) { + fe_ht->nInternalPointer = ptr->pos = INVALID_IDX; + ZEND_VM_INC_OPCODE(); + ZEND_VM_NEXT_OPCODE(); + } + p = fe_ht->arData + pos; + } while (Z_TYPE(p->val) == IS_UNDEF || + (Z_TYPE(p->val) == IS_INDIRECT && + Z_TYPE_P(Z_INDIRECT(p->val)) == IS_UNDEF)); + fe_ht->nInternalPointer = ptr->pos = pos; + ptr->h = fe_ht->arData[pos].h; + ZEND_VM_INC_OPCODE(); + ZEND_VM_NEXT_OPCODE(); + } else if (EXPECTED(Z_TYPE_P(array) == IS_OBJECT)) { + zend_object_iterator *iter; + + if ((iter = zend_iterator_unwrap(array TSRMLS_CC)) == NULL) { + /* plain object */ + zend_object *zobj = Z_OBJ_P(array); + + fe_ht = Z_OBJPROP_P(array); + ptr = (HashPointer*)EX_VAR((opline+1)->op1.var); + pos = ptr->pos; + if (pos == INVALID_IDX) { + /* reached end of iteration */ + ZEND_VM_JMP(opline->op2.jmp_addr); + } else if (UNEXPECTED(ptr->ht != fe_ht)) { + ptr->ht = fe_ht; + pos = 0; + } else if (UNEXPECTED(fe_ht->nInternalPointer != ptr->pos)) { + if (fe_ht->u.flags & HASH_FLAG_PACKED) { + pos = ptr->h; + } else { + pos = fe_ht->arHash[ptr->h & fe_ht->nTableMask]; + while (pos != INVALID_IDX) { + if (fe_ht->arData[pos].h == ptr->h && pos == ptr->pos) { + break; + } + pos = Z_NEXT(fe_ht->arData[pos].val); + } + } + } while (1) { - if ((value = zend_hash_get_current_data(fe_ht)) == NULL) { + if (UNEXPECTED(pos >= fe_ht->nNumUsed)) { /* reached end of iteration */ ZEND_VM_JMP(opline->op2.jmp_addr); } - if (Z_TYPE_P(value) == IS_INDIRECT) { + p = fe_ht->arData + pos; + value = &p->val; + if (UNEXPECTED(Z_TYPE_P(value) == IS_UNDEF)) { + pos++; + continue; + } else if (UNEXPECTED(Z_TYPE_P(value) == IS_INDIRECT)) { value = Z_INDIRECT_P(value); - if (Z_TYPE_P(value) == IS_UNDEF) { - zend_hash_move_forward(fe_ht); + if (UNEXPECTED(Z_TYPE_P(value) == IS_UNDEF)) { + pos++; continue; } } - key_type = zend_hash_get_current_key(fe_ht, &str_key, &int_key, 0); - - zend_hash_move_forward(fe_ht); - if (key_type == HASH_KEY_IS_LONG || - zend_check_property_access(zobj, str_key TSRMLS_CC) == SUCCESS) { + if (UNEXPECTED(!p->key)) { + if (opline->extended_value & ZEND_FE_FETCH_WITH_KEY) { + ZVAL_LONG(EX_VAR((opline+1)->result.var), p->h); + } + break; + } else if (zend_check_property_access(zobj, p->key TSRMLS_CC) == SUCCESS) { + if (opline->extended_value & ZEND_FE_FETCH_WITH_KEY) { + if (p->key->val[0]) { + if (IS_INTERNED(p->key)) { + ZVAL_INTERNED_STR(EX_VAR((opline+1)->result.var), p->key); + } else { + ZVAL_NEW_STR(EX_VAR((opline+1)->result.var), p->key); + GC_REFCOUNT(p->key)++; + } + } else { + const char *class_name, *prop_name; + int prop_name_len; + zend_unmangle_property_name_ex( + p->key->val, p->key->len, &class_name, &prop_name, &prop_name_len); + ZVAL_STRINGL(EX_VAR((opline+1)->result.var), prop_name, prop_name_len); + } + } break; } + pos++; } - - if (key) { - if (key_type == HASH_KEY_IS_LONG) { - ZVAL_LONG(key, int_key); - } else { - const char *class_name, *prop_name; - int prop_name_len; - zend_unmangle_property_name_ex( - str_key->val, (int)str_key->len, &class_name, &prop_name, &prop_name_len - ); - ZVAL_STRINGL(key, prop_name, prop_name_len); - } - } - - zend_hash_get_pointer(fe_ht, (HashPointer*)EX_VAR((opline+1)->op1.var)); - break; - } - - case ZEND_ITER_PLAIN_ARRAY: - fe_ht = Z_ARRVAL_P(array); - zend_hash_set_pointer(fe_ht, (HashPointer*)EX_VAR((opline+1)->op1.var)); - if ((value = zend_hash_get_current_data(fe_ht)) == NULL) { - /* reached end of iteration */ - ZEND_VM_JMP(opline->op2.jmp_addr); - } - if (key) { - zend_hash_get_current_key_zval(fe_ht, key); + if (opline->extended_value & ZEND_FE_FETCH_BYREF) { + ZVAL_MAKE_REF(value); + Z_ADDREF_P(value); + ZVAL_REF(EX_VAR(opline->result.var), Z_REF_P(value)); + } else { + ZVAL_COPY(EX_VAR(opline->result.var), value); } - zend_hash_move_forward(fe_ht); - zend_hash_get_pointer(fe_ht, (HashPointer*)EX_VAR((opline+1)->op1.var)); - break; - - case ZEND_ITER_OBJECT: + do { + pos++; + if (pos >= fe_ht->nNumUsed) { + fe_ht->nInternalPointer = ptr->pos = INVALID_IDX; + ZEND_VM_INC_OPCODE(); + ZEND_VM_NEXT_OPCODE(); + } + p = fe_ht->arData + pos; + } while (Z_TYPE(p->val) == IS_UNDEF || + (Z_TYPE(p->val) == IS_INDIRECT && + Z_TYPE_P(Z_INDIRECT(p->val)) == IS_UNDEF) || + (EXPECTED(p->key != NULL) && + zend_check_property_access(zobj, p->key TSRMLS_CC) == FAILURE)); + fe_ht->nInternalPointer = ptr->pos = pos; + ptr->h = fe_ht->arData[pos].h; + ZEND_VM_INC_OPCODE(); + ZEND_VM_NEXT_OPCODE(); + } else { /* !iter happens from exception */ if (iter && ++iter->index > 0) { /* This could cause an endless loop if index becomes zero again. @@ -4760,31 +4874,31 @@ ZEND_VM_HANDLER(78, ZEND_FE_FETCH, VAR, ANY) /* failure in get_current_data */ ZEND_VM_JMP(opline->op2.jmp_addr); } - if (key) { + if (opline->extended_value & ZEND_FE_FETCH_BYREF) { + ZVAL_MAKE_REF(value); + Z_ADDREF_P(value); + ZVAL_REF(EX_VAR(opline->result.var), Z_REF_P(value)); + } else { + ZVAL_COPY(EX_VAR(opline->result.var), value); + } + if (opline->extended_value & ZEND_FE_FETCH_WITH_KEY) { if (iter->funcs->get_current_key) { - iter->funcs->get_current_key(iter, key TSRMLS_CC); + iter->funcs->get_current_key(iter, EX_VAR((opline+1)->result.var) TSRMLS_CC); if (UNEXPECTED(EG(exception) != NULL)) { zval_ptr_dtor(array_ref); HANDLE_EXCEPTION(); } } else { - ZVAL_LONG(key, iter->index); + ZVAL_LONG(EX_VAR((opline+1)->result.var), iter->index); } } - break; - } - - if (opline->extended_value & ZEND_FE_FETCH_BYREF) { - ZVAL_MAKE_REF(value); - Z_ADDREF_P(value); - ZVAL_REF(EX_VAR(opline->result.var), Z_REF_P(value)); + ZEND_VM_INC_OPCODE(); + ZEND_VM_NEXT_OPCODE(); + } } else { - ZVAL_COPY(EX_VAR(opline->result.var), value); + zend_error(E_WARNING, "Invalid argument supplied for foreach()"); + ZEND_VM_JMP(opline->op2.jmp_addr); } - - CHECK_EXCEPTION(); - ZEND_VM_INC_OPCODE(); - ZEND_VM_NEXT_OPCODE(); } ZEND_VM_HANDLER(114, ZEND_ISSET_ISEMPTY_VAR, CONST|TMP|VAR|CV, UNUSED|CONST|VAR) diff --git a/Zend/zend_vm_execute.h b/Zend/zend_vm_execute.h index b833d1d1a2..646a498760 100644 --- a/Zend/zend_vm_execute.h +++ b/Zend/zend_vm_execute.h @@ -3206,25 +3206,37 @@ static int ZEND_FASTCALL ZEND_FE_RESET_SPEC_CONST_HANDLER(ZEND_OPCODE_HANDLER_A } iter->index = -1; /* will be set to 0 before using next handler */ } else if ((fe_ht = HASH_OF(array_ptr)) != NULL) { - zend_hash_internal_pointer_reset(fe_ht); - if (ce) { - zend_object *zobj = Z_OBJ_P(array_ptr); - while (zend_hash_has_more_elements(fe_ht) == SUCCESS) { - zend_string *str_key; - zend_ulong int_key; - zend_uchar key_type; - - key_type = zend_hash_get_current_key(fe_ht, &str_key, &int_key, 0); - if (key_type != HASH_KEY_NON_EXISTENT && - (key_type == HASH_KEY_IS_LONG || - zend_check_property_access(zobj, str_key TSRMLS_CC) == SUCCESS)) { - break; + HashPointer *ptr = (HashPointer*)EX_VAR((opline+2)->op1.var); + HashPosition pos = 0; + Bucket *p; + + while (1) { + if (pos >= fe_ht->nNumUsed) { + is_empty = 1; + if (IS_CONST == IS_VAR && opline->extended_value & ZEND_FE_RESET_VARIABLE) { + } - zend_hash_move_forward(fe_ht); + ZEND_VM_JMP(opline->op2.jmp_addr); + } + p = fe_ht->arData + pos; + if (Z_TYPE(p->val) == IS_UNDEF || + (Z_TYPE(p->val) == IS_INDIRECT && + Z_TYPE_P(Z_INDIRECT(p->val)) == IS_UNDEF)) { + pos++; + continue; } + if (!ce || + !p->key || + zend_check_property_access(Z_OBJ_P(array_ptr), p->key TSRMLS_CC) == SUCCESS) { + break; + } + pos++; } - is_empty = zend_hash_has_more_elements(fe_ht) != SUCCESS; - zend_hash_get_pointer(fe_ht, (HashPointer*)EX_VAR((opline+2)->op1.var)); + fe_ht->nInternalPointer = pos; + ptr->pos = pos; + ptr->ht = fe_ht; + ptr->h = fe_ht->arData[pos].h; + is_empty = 0; } else { zend_error(E_WARNING, "Invalid argument supplied for foreach()"); is_empty = 1; @@ -9926,25 +9938,37 @@ static int ZEND_FASTCALL ZEND_FE_RESET_SPEC_TMP_HANDLER(ZEND_OPCODE_HANDLER_ARG } iter->index = -1; /* will be set to 0 before using next handler */ } else if ((fe_ht = HASH_OF(array_ptr)) != NULL) { - zend_hash_internal_pointer_reset(fe_ht); - if (ce) { - zend_object *zobj = Z_OBJ_P(array_ptr); - while (zend_hash_has_more_elements(fe_ht) == SUCCESS) { - zend_string *str_key; - zend_ulong int_key; - zend_uchar key_type; - - key_type = zend_hash_get_current_key(fe_ht, &str_key, &int_key, 0); - if (key_type != HASH_KEY_NON_EXISTENT && - (key_type == HASH_KEY_IS_LONG || - zend_check_property_access(zobj, str_key TSRMLS_CC) == SUCCESS)) { - break; + HashPointer *ptr = (HashPointer*)EX_VAR((opline+2)->op1.var); + HashPosition pos = 0; + Bucket *p; + + while (1) { + if (pos >= fe_ht->nNumUsed) { + is_empty = 1; + if (IS_TMP_VAR == IS_VAR && opline->extended_value & ZEND_FE_RESET_VARIABLE) { + } - zend_hash_move_forward(fe_ht); + ZEND_VM_JMP(opline->op2.jmp_addr); + } + p = fe_ht->arData + pos; + if (Z_TYPE(p->val) == IS_UNDEF || + (Z_TYPE(p->val) == IS_INDIRECT && + Z_TYPE_P(Z_INDIRECT(p->val)) == IS_UNDEF)) { + pos++; + continue; + } + if (!ce || + !p->key || + zend_check_property_access(Z_OBJ_P(array_ptr), p->key TSRMLS_CC) == SUCCESS) { + break; } + pos++; } - is_empty = zend_hash_has_more_elements(fe_ht) != SUCCESS; - zend_hash_get_pointer(fe_ht, (HashPointer*)EX_VAR((opline+2)->op1.var)); + fe_ht->nInternalPointer = pos; + ptr->pos = pos; + ptr->ht = fe_ht; + ptr->h = fe_ht->arData[pos].h; + is_empty = 0; } else { zend_error(E_WARNING, "Invalid argument supplied for foreach()"); is_empty = 1; @@ -16560,25 +16584,37 @@ static int ZEND_FASTCALL ZEND_FE_RESET_SPEC_VAR_HANDLER(ZEND_OPCODE_HANDLER_ARG } iter->index = -1; /* will be set to 0 before using next handler */ } else if ((fe_ht = HASH_OF(array_ptr)) != NULL) { - zend_hash_internal_pointer_reset(fe_ht); - if (ce) { - zend_object *zobj = Z_OBJ_P(array_ptr); - while (zend_hash_has_more_elements(fe_ht) == SUCCESS) { - zend_string *str_key; - zend_ulong int_key; - zend_uchar key_type; - - key_type = zend_hash_get_current_key(fe_ht, &str_key, &int_key, 0); - if (key_type != HASH_KEY_NON_EXISTENT && - (key_type == HASH_KEY_IS_LONG || - zend_check_property_access(zobj, str_key TSRMLS_CC) == SUCCESS)) { - break; + HashPointer *ptr = (HashPointer*)EX_VAR((opline+2)->op1.var); + HashPosition pos = 0; + Bucket *p; + + while (1) { + if (pos >= fe_ht->nNumUsed) { + is_empty = 1; + if (IS_VAR == IS_VAR && opline->extended_value & ZEND_FE_RESET_VARIABLE) { + if (free_op1.var) {zval_ptr_dtor_nogc(free_op1.var);}; } - zend_hash_move_forward(fe_ht); + ZEND_VM_JMP(opline->op2.jmp_addr); + } + p = fe_ht->arData + pos; + if (Z_TYPE(p->val) == IS_UNDEF || + (Z_TYPE(p->val) == IS_INDIRECT && + Z_TYPE_P(Z_INDIRECT(p->val)) == IS_UNDEF)) { + pos++; + continue; + } + if (!ce || + !p->key || + zend_check_property_access(Z_OBJ_P(array_ptr), p->key TSRMLS_CC) == SUCCESS) { + break; } + pos++; } - is_empty = zend_hash_has_more_elements(fe_ht) != SUCCESS; - zend_hash_get_pointer(fe_ht, (HashPointer*)EX_VAR((opline+2)->op1.var)); + fe_ht->nInternalPointer = pos; + ptr->pos = pos; + ptr->ht = fe_ht; + ptr->h = fe_ht->arData[pos].h; + is_empty = 0; } else { zend_error(E_WARNING, "Invalid argument supplied for foreach()"); is_empty = 1; @@ -16602,8 +16638,9 @@ static int ZEND_FASTCALL ZEND_FE_FETCH_SPEC_VAR_HANDLER(ZEND_OPCODE_HANDLER_ARG zval *array, *array_ref; zval *value; HashTable *fe_ht; - zend_object_iterator *iter = NULL; - zval *key = NULL; + HashPointer *ptr; + HashPosition pos; + Bucket *p; array = array_ref = EX_VAR(opline->op1.var); if (Z_ISREF_P(array)) { @@ -16613,81 +16650,182 @@ static int ZEND_FASTCALL ZEND_FE_FETCH_SPEC_VAR_HANDLER(ZEND_OPCODE_HANDLER_ARG zval_copy_ctor(array); } } - if (opline->extended_value & ZEND_FE_FETCH_WITH_KEY) { - key = EX_VAR((opline+1)->result.var); - } SAVE_OPLINE(); - switch (zend_iterator_unwrap(array, &iter TSRMLS_CC)) { - default: - case ZEND_ITER_INVALID: - zend_error(E_WARNING, "Invalid argument supplied for foreach()"); + if (EXPECTED(Z_TYPE_P(array) == IS_ARRAY)) { + fe_ht = Z_ARRVAL_P(array); + ptr = (HashPointer*)EX_VAR((opline+1)->op1.var); + pos = ptr->pos; + if (UNEXPECTED(pos == INVALID_IDX)) { + /* reached end of iteration */ ZEND_VM_JMP(opline->op2.jmp_addr); + } else if (UNEXPECTED(ptr->ht != fe_ht)) { + ptr->ht = fe_ht; + pos = 0; + } else if (UNEXPECTED(fe_ht->nInternalPointer != ptr->pos)) { + if (fe_ht->u.flags & HASH_FLAG_PACKED) { + pos = ptr->h; + } else { + pos = fe_ht->arHash[ptr->h & fe_ht->nTableMask]; + while (pos != INVALID_IDX) { + if (fe_ht->arData[pos].h == ptr->h && pos == ptr->pos) { + break; + } + pos = Z_NEXT(fe_ht->arData[pos].val); + } + } + } + while (1) { + if (UNEXPECTED(pos >= fe_ht->nNumUsed)) { + /* reached end of iteration */ + ZEND_VM_JMP(opline->op2.jmp_addr); + } + p = fe_ht->arData + pos; + value = &p->val; + if (UNEXPECTED(Z_TYPE_P(value) == IS_UNDEF)) { + pos++; + continue; + } else if (UNEXPECTED(Z_TYPE_P(value) == IS_INDIRECT)) { + value = Z_INDIRECT_P(value); + if (UNEXPECTED(Z_TYPE_P(value) == IS_UNDEF)) { + pos++; + continue; + } + } + if (opline->extended_value & ZEND_FE_FETCH_BYREF) { + ZVAL_MAKE_REF(value); + Z_ADDREF_P(value); + ZVAL_REF(EX_VAR(opline->result.var), Z_REF_P(value)); + } else { + ZVAL_COPY(EX_VAR(opline->result.var), value); + } + if (opline->extended_value & ZEND_FE_FETCH_WITH_KEY) { + if (!p->key) { + ZVAL_LONG(EX_VAR((opline+1)->result.var), p->h); + } else if (IS_INTERNED(p->key)) { + ZVAL_INTERNED_STR(EX_VAR((opline+1)->result.var), p->key); + } else { + ZVAL_NEW_STR(EX_VAR((opline+1)->result.var), p->key); + GC_REFCOUNT(p->key)++; + } + } + break; + } + do { + pos++; + if (pos >= fe_ht->nNumUsed) { + fe_ht->nInternalPointer = ptr->pos = INVALID_IDX; + ZEND_VM_INC_OPCODE(); + ZEND_VM_NEXT_OPCODE(); + } + p = fe_ht->arData + pos; + } while (Z_TYPE(p->val) == IS_UNDEF || + (Z_TYPE(p->val) == IS_INDIRECT && + Z_TYPE_P(Z_INDIRECT(p->val)) == IS_UNDEF)); + fe_ht->nInternalPointer = ptr->pos = pos; + ptr->h = fe_ht->arData[pos].h; + ZEND_VM_INC_OPCODE(); + ZEND_VM_NEXT_OPCODE(); + } else if (EXPECTED(Z_TYPE_P(array) == IS_OBJECT)) { + zend_object_iterator *iter; - case ZEND_ITER_PLAIN_OBJECT: { - zend_object *zobj = Z_OBJ_P(array); - int key_type; - zend_string *str_key; - zend_ulong int_key; + if ((iter = zend_iterator_unwrap(array TSRMLS_CC)) == NULL) { + /* plain object */ + zend_object *zobj = Z_OBJ_P(array); - fe_ht = Z_OBJPROP_P(array); - zend_hash_set_pointer(fe_ht, (HashPointer*)EX_VAR((opline+1)->op1.var)); + fe_ht = Z_OBJPROP_P(array); + ptr = (HashPointer*)EX_VAR((opline+1)->op1.var); + pos = ptr->pos; + if (pos == INVALID_IDX) { + /* reached end of iteration */ + ZEND_VM_JMP(opline->op2.jmp_addr); + } else if (UNEXPECTED(ptr->ht != fe_ht)) { + ptr->ht = fe_ht; + pos = 0; + } else if (UNEXPECTED(fe_ht->nInternalPointer != ptr->pos)) { + if (fe_ht->u.flags & HASH_FLAG_PACKED) { + pos = ptr->h; + } else { + pos = fe_ht->arHash[ptr->h & fe_ht->nTableMask]; + while (pos != INVALID_IDX) { + if (fe_ht->arData[pos].h == ptr->h && pos == ptr->pos) { + break; + } + pos = Z_NEXT(fe_ht->arData[pos].val); + } + } + } while (1) { - if ((value = zend_hash_get_current_data(fe_ht)) == NULL) { + if (UNEXPECTED(pos >= fe_ht->nNumUsed)) { /* reached end of iteration */ ZEND_VM_JMP(opline->op2.jmp_addr); } - if (Z_TYPE_P(value) == IS_INDIRECT) { + p = fe_ht->arData + pos; + value = &p->val; + if (UNEXPECTED(Z_TYPE_P(value) == IS_UNDEF)) { + pos++; + continue; + } else if (UNEXPECTED(Z_TYPE_P(value) == IS_INDIRECT)) { value = Z_INDIRECT_P(value); - if (Z_TYPE_P(value) == IS_UNDEF) { - zend_hash_move_forward(fe_ht); + if (UNEXPECTED(Z_TYPE_P(value) == IS_UNDEF)) { + pos++; continue; } } - key_type = zend_hash_get_current_key(fe_ht, &str_key, &int_key, 0); - - zend_hash_move_forward(fe_ht); - if (key_type == HASH_KEY_IS_LONG || - zend_check_property_access(zobj, str_key TSRMLS_CC) == SUCCESS) { + if (UNEXPECTED(!p->key)) { + if (opline->extended_value & ZEND_FE_FETCH_WITH_KEY) { + ZVAL_LONG(EX_VAR((opline+1)->result.var), p->h); + } + break; + } else if (zend_check_property_access(zobj, p->key TSRMLS_CC) == SUCCESS) { + if (opline->extended_value & ZEND_FE_FETCH_WITH_KEY) { + if (p->key->val[0]) { + if (IS_INTERNED(p->key)) { + ZVAL_INTERNED_STR(EX_VAR((opline+1)->result.var), p->key); + } else { + ZVAL_NEW_STR(EX_VAR((opline+1)->result.var), p->key); + GC_REFCOUNT(p->key)++; + } + } else { + const char *class_name, *prop_name; + int prop_name_len; + zend_unmangle_property_name_ex( + p->key->val, p->key->len, &class_name, &prop_name, &prop_name_len); + ZVAL_STRINGL(EX_VAR((opline+1)->result.var), prop_name, prop_name_len); + } + } break; } + pos++; } - - if (key) { - if (key_type == HASH_KEY_IS_LONG) { - ZVAL_LONG(key, int_key); - } else { - const char *class_name, *prop_name; - int prop_name_len; - zend_unmangle_property_name_ex( - str_key->val, (int)str_key->len, &class_name, &prop_name, &prop_name_len - ); - ZVAL_STRINGL(key, prop_name, prop_name_len); - } - } - - zend_hash_get_pointer(fe_ht, (HashPointer*)EX_VAR((opline+1)->op1.var)); - break; - } - - case ZEND_ITER_PLAIN_ARRAY: - fe_ht = Z_ARRVAL_P(array); - zend_hash_set_pointer(fe_ht, (HashPointer*)EX_VAR((opline+1)->op1.var)); - if ((value = zend_hash_get_current_data(fe_ht)) == NULL) { - /* reached end of iteration */ - ZEND_VM_JMP(opline->op2.jmp_addr); - } - if (key) { - zend_hash_get_current_key_zval(fe_ht, key); + if (opline->extended_value & ZEND_FE_FETCH_BYREF) { + ZVAL_MAKE_REF(value); + Z_ADDREF_P(value); + ZVAL_REF(EX_VAR(opline->result.var), Z_REF_P(value)); + } else { + ZVAL_COPY(EX_VAR(opline->result.var), value); } - zend_hash_move_forward(fe_ht); - zend_hash_get_pointer(fe_ht, (HashPointer*)EX_VAR((opline+1)->op1.var)); - break; - - case ZEND_ITER_OBJECT: + do { + pos++; + if (pos >= fe_ht->nNumUsed) { + fe_ht->nInternalPointer = ptr->pos = INVALID_IDX; + ZEND_VM_INC_OPCODE(); + ZEND_VM_NEXT_OPCODE(); + } + p = fe_ht->arData + pos; + } while (Z_TYPE(p->val) == IS_UNDEF || + (Z_TYPE(p->val) == IS_INDIRECT && + Z_TYPE_P(Z_INDIRECT(p->val)) == IS_UNDEF) || + (EXPECTED(p->key != NULL) && + zend_check_property_access(zobj, p->key TSRMLS_CC) == FAILURE)); + fe_ht->nInternalPointer = ptr->pos = pos; + ptr->h = fe_ht->arData[pos].h; + ZEND_VM_INC_OPCODE(); + ZEND_VM_NEXT_OPCODE(); + } else { /* !iter happens from exception */ if (iter && ++iter->index > 0) { /* This could cause an endless loop if index becomes zero again. @@ -16716,31 +16854,31 @@ static int ZEND_FASTCALL ZEND_FE_FETCH_SPEC_VAR_HANDLER(ZEND_OPCODE_HANDLER_ARG /* failure in get_current_data */ ZEND_VM_JMP(opline->op2.jmp_addr); } - if (key) { + if (opline->extended_value & ZEND_FE_FETCH_BYREF) { + ZVAL_MAKE_REF(value); + Z_ADDREF_P(value); + ZVAL_REF(EX_VAR(opline->result.var), Z_REF_P(value)); + } else { + ZVAL_COPY(EX_VAR(opline->result.var), value); + } + if (opline->extended_value & ZEND_FE_FETCH_WITH_KEY) { if (iter->funcs->get_current_key) { - iter->funcs->get_current_key(iter, key TSRMLS_CC); + iter->funcs->get_current_key(iter, EX_VAR((opline+1)->result.var) TSRMLS_CC); if (UNEXPECTED(EG(exception) != NULL)) { zval_ptr_dtor(array_ref); HANDLE_EXCEPTION(); } } else { - ZVAL_LONG(key, iter->index); + ZVAL_LONG(EX_VAR((opline+1)->result.var), iter->index); } } - break; - } - - if (opline->extended_value & ZEND_FE_FETCH_BYREF) { - ZVAL_MAKE_REF(value); - Z_ADDREF_P(value); - ZVAL_REF(EX_VAR(opline->result.var), Z_REF_P(value)); + ZEND_VM_INC_OPCODE(); + ZEND_VM_NEXT_OPCODE(); + } } else { - ZVAL_COPY(EX_VAR(opline->result.var), value); + zend_error(E_WARNING, "Invalid argument supplied for foreach()"); + ZEND_VM_JMP(opline->op2.jmp_addr); } - - CHECK_EXCEPTION(); - ZEND_VM_INC_OPCODE(); - ZEND_VM_NEXT_OPCODE(); } static int ZEND_FASTCALL ZEND_EXIT_SPEC_VAR_HANDLER(ZEND_OPCODE_HANDLER_ARGS) @@ -33897,25 +34035,37 @@ static int ZEND_FASTCALL ZEND_FE_RESET_SPEC_CV_HANDLER(ZEND_OPCODE_HANDLER_ARGS } iter->index = -1; /* will be set to 0 before using next handler */ } else if ((fe_ht = HASH_OF(array_ptr)) != NULL) { - zend_hash_internal_pointer_reset(fe_ht); - if (ce) { - zend_object *zobj = Z_OBJ_P(array_ptr); - while (zend_hash_has_more_elements(fe_ht) == SUCCESS) { - zend_string *str_key; - zend_ulong int_key; - zend_uchar key_type; - - key_type = zend_hash_get_current_key(fe_ht, &str_key, &int_key, 0); - if (key_type != HASH_KEY_NON_EXISTENT && - (key_type == HASH_KEY_IS_LONG || - zend_check_property_access(zobj, str_key TSRMLS_CC) == SUCCESS)) { - break; + HashPointer *ptr = (HashPointer*)EX_VAR((opline+2)->op1.var); + HashPosition pos = 0; + Bucket *p; + + while (1) { + if (pos >= fe_ht->nNumUsed) { + is_empty = 1; + if (IS_CV == IS_VAR && opline->extended_value & ZEND_FE_RESET_VARIABLE) { + } - zend_hash_move_forward(fe_ht); + ZEND_VM_JMP(opline->op2.jmp_addr); + } + p = fe_ht->arData + pos; + if (Z_TYPE(p->val) == IS_UNDEF || + (Z_TYPE(p->val) == IS_INDIRECT && + Z_TYPE_P(Z_INDIRECT(p->val)) == IS_UNDEF)) { + pos++; + continue; + } + if (!ce || + !p->key || + zend_check_property_access(Z_OBJ_P(array_ptr), p->key TSRMLS_CC) == SUCCESS) { + break; } + pos++; } - is_empty = zend_hash_has_more_elements(fe_ht) != SUCCESS; - zend_hash_get_pointer(fe_ht, (HashPointer*)EX_VAR((opline+2)->op1.var)); + fe_ht->nInternalPointer = pos; + ptr->pos = pos; + ptr->ht = fe_ht; + ptr->h = fe_ht->arData[pos].h; + is_empty = 0; } else { zend_error(E_WARNING, "Invalid argument supplied for foreach()"); is_empty = 1;