]> granicus.if.org Git - php/commitdiff
Optimized FE_RESET and FE_FETCH
authorDmitry Stogov <dmitry@zend.com>
Mon, 15 Sep 2014 21:34:27 +0000 (01:34 +0400)
committerDmitry Stogov <dmitry@zend.com>
Mon, 15 Sep 2014 21:34:27 +0000 (01:34 +0400)
Zend/zend_hash.c
Zend/zend_hash.h
Zend/zend_iterators.c
Zend/zend_iterators.h
Zend/zend_vm_def.h
Zend/zend_vm_execute.h

index 5354ce29f63fba5d405ec837dcf724a246907241..e651cd85e5bfc2d7e25c1d4b41bcc6484863922f 100644 (file)
@@ -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;
index cf91bac50325edc8ef5c2a61360765d2cf46364c..6752ccb0740204b4d84dcef507d04fac14ba2630 100644 (file)
@@ -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) \
index 7c08ac59b511612006c2ebed74a1478b1259eb39..c2f51c4dd691f0ff899d13570108afd8627b5aac 100644 (file)
@@ -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;
 }
 
 /*
index b0105d52e274c2280e7c97d2b1a4a9345baac230..a1148db214fad5d24fec154d26a29e01515f8af0 100644 (file)
@@ -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);
index 658e7ce7d6c032f6524823035425d01733f016db..97351ce5d9b9f0340a75325597fa527d813b780f 100644 (file)
@@ -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)
index b833d1d1a27147ba17748029555abf22f1207d59..646a4987601ec56dccdc30fee8c6b23a8023b9c3 100644 (file)
@@ -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;