diff options
author | Dmitry Stogov <dmitry@zend.com> | 2015-01-29 21:05:02 +0300 |
---|---|---|
committer | Dmitry Stogov <dmitry@zend.com> | 2015-01-29 21:05:02 +0300 |
commit | 15a23b1218b3e38630d677751a975907daa2cd54 (patch) | |
tree | 0d15452a3c633f65c6529cc7088704e7a229d452 | |
parent | 10a3260b1f16b6075fd8140f673dfef4d5efea91 (diff) | |
download | php-git-15a23b1218b3e38630d677751a975907daa2cd54.tar.gz |
Reimplement iteration magic with HashTableIterators (see https://wiki.php.net/rfc/php7_foreach#implementation_details)
-rw-r--r-- | Zend/zend_compile.c | 10 | ||||
-rw-r--r-- | Zend/zend_compile.h | 3 | ||||
-rw-r--r-- | Zend/zend_execute.c | 8 | ||||
-rw-r--r-- | Zend/zend_execute_API.c | 10 | ||||
-rw-r--r-- | Zend/zend_generators.c | 6 | ||||
-rw-r--r-- | Zend/zend_globals.h | 5 | ||||
-rw-r--r-- | Zend/zend_hash.c | 147 | ||||
-rw-r--r-- | Zend/zend_hash.h | 9 | ||||
-rw-r--r-- | Zend/zend_types.h | 16 | ||||
-rw-r--r-- | Zend/zend_vm_def.h | 76 | ||||
-rw-r--r-- | Zend/zend_vm_execute.h | 123 | ||||
-rw-r--r-- | Zend/zend_vm_opcodes.c | 2 | ||||
-rw-r--r-- | Zend/zend_vm_opcodes.h | 1 | ||||
-rw-r--r-- | ext/opcache/Optimizer/block_pass.c | 6 | ||||
-rw-r--r-- | ext/opcache/Optimizer/optimize_temp_vars_5.c | 26 | ||||
-rw-r--r-- | ext/opcache/Optimizer/pass2.c | 4 | ||||
-rw-r--r-- | ext/standard/array.c | 8 | ||||
-rw-r--r-- | tests/lang/foreachLoop.013.phpt | 59 | ||||
-rw-r--r-- | tests/lang/foreachLoop.015.phpt | 59 |
19 files changed, 426 insertions, 152 deletions
diff --git a/Zend/zend_compile.c b/Zend/zend_compile.c index e37fdd778c..080b31562a 100644 --- a/Zend/zend_compile.c +++ b/Zend/zend_compile.c @@ -829,7 +829,7 @@ static int generate_free_loop_var(znode *var) /* {{{ */ { zend_op *opline = get_next_op(CG(active_op_array)); - opline->opcode = ZEND_FREE; + opline->opcode = var->flag ? ZEND_FE_FREE : ZEND_FREE; SET_NODE(opline->op1, var); SET_UNUSED(opline->op2); } @@ -3398,6 +3398,7 @@ void zend_compile_foreach(zend_ast *ast) /* {{{ */ opnum_reset = get_next_op_number(CG(active_op_array)); opline = zend_emit_op(&reset_node, by_ref ? ZEND_FE_RESET_RW : ZEND_FE_RESET_R, &expr_node, NULL); + reset_node.flag = by_ref; zend_stack_push(&CG(loop_var_stack), &reset_node); opnum_fetch = get_next_op_number(CG(active_op_array)); @@ -3408,12 +3409,6 @@ void zend_compile_foreach(zend_ast *ast) /* {{{ */ opline = zend_emit_op(NULL, ZEND_OP_DATA, NULL, NULL); - if (by_ref) { - /* Allocate temporary variable to keep HashTable value */ - opline->op1_type = IS_TMP_VAR; - opline->op1.var = get_temporary_variable(CG(active_op_array)); - } - if (key_ast) { zend_make_tmp_result(&key_node, opline); } @@ -3504,6 +3499,7 @@ void zend_compile_switch(zend_ast *ast) /* {{{ */ zend_compile_expr(&expr_node, expr_ast); + expr_node.flag = 0; zend_stack_push(&CG(loop_var_stack), &expr_node); zend_begin_loop(); diff --git a/Zend/zend_compile.h b/Zend/zend_compile.h index 2d71ca2571..5239f565b3 100644 --- a/Zend/zend_compile.h +++ b/Zend/zend_compile.h @@ -95,7 +95,8 @@ typedef union _znode_op { } znode_op; typedef struct _znode { /* used only during compilation */ - int op_type; + zend_uchar op_type; + zend_uchar flag; union { znode_op op; zval constant; /* replaced by literal/zv */ diff --git a/Zend/zend_execute.c b/Zend/zend_execute.c index 7a79defb05..d59aef1d1b 100644 --- a/Zend/zend_execute.c +++ b/Zend/zend_execute.c @@ -1584,6 +1584,14 @@ static inline zend_brk_cont_element* zend_brk_cont(int nest_levels, int array_of if (!(brk_opline->extended_value & EXT_TYPE_FREE_ON_RETURN)) { zval_ptr_dtor_nogc(EX_VAR(brk_opline->op1.var)); } + } else if (brk_opline->opcode == ZEND_FE_FREE) { + if (!(brk_opline->extended_value & EXT_TYPE_FREE_ON_RETURN)) { + zval *var = EX_VAR(brk_opline->op1.var); + if (Z_FE_ITER_P(var) != (uint32_t)-1) { + zend_hash_iterator_del(Z_FE_ITER_P(var)); + } + zval_ptr_dtor_nogc(var); + } } } array_offset = jmp_to->parent; diff --git a/Zend/zend_execute_API.c b/Zend/zend_execute_API.c index c9da46d819..c6a0d8f0c9 100644 --- a/Zend/zend_execute_API.c +++ b/Zend/zend_execute_API.c @@ -183,6 +183,11 @@ void init_executor(void) /* {{{ */ EG(scope) = NULL; + EG(ht_iterators_count) = sizeof(EG(ht_iterators_slots)) / sizeof(HashTableIterator); + EG(ht_iterators_used) = 0; + EG(ht_iterators) = EG(ht_iterators_slots); + memset(EG(ht_iterators), 0, sizeof(EG(ht_iterators_slots))); + EG(active) = 1; } /* }}} */ @@ -373,6 +378,11 @@ void shutdown_executor(void) /* {{{ */ zend_shutdown_fpu(); + EG(ht_iterators_used) = 0; + if (EG(ht_iterators) != EG(ht_iterators_slots)) { + efree(EG(ht_iterators)); + } + EG(active) = 0; } /* }}} */ diff --git a/Zend/zend_generators.c b/Zend/zend_generators.c index f19bb85013..1f25d07ea9 100644 --- a/Zend/zend_generators.c +++ b/Zend/zend_generators.c @@ -60,6 +60,12 @@ static void zend_generator_cleanup_unfinished_execution(zend_generator *generato if (brk_opline->opcode == ZEND_FREE) { zval *var = EX_VAR(brk_opline->op1.var); zval_ptr_dtor_nogc(var); + } else if (brk_opline->opcode == ZEND_FE_FREE) { + zval *var = EX_VAR(brk_opline->op1.var); + if (Z_FE_ITER_P(var) != (uint32_t)-1) { + zend_hash_iterator_del(Z_FE_ITER_P(var)); + } + zval_ptr_dtor_nogc(var); } } } diff --git a/Zend/zend_globals.h b/Zend/zend_globals.h index beca5ad631..abebb19ae3 100644 --- a/Zend/zend_globals.h +++ b/Zend/zend_globals.h @@ -225,6 +225,11 @@ struct _zend_executor_globals { zend_bool active; zend_bool valid_symbol_table; + uint32_t ht_iterators_count; /* number of allocatd slots */ + uint32_t ht_iterators_used; /* number of used slots */ + HashTableIterator *ht_iterators; + HashTableIterator ht_iterators_slots[16]; + void *saved_fpu_cw_ptr; #if XPFPA_HAVE_CW XPFPA_CW_DATATYPE saved_fpu_cw; diff --git a/Zend/zend_hash.c b/Zend/zend_hash.c index 480ac64dd1..dd175c7c00 100644 --- a/Zend/zend_hash.c +++ b/Zend/zend_hash.c @@ -193,6 +193,144 @@ ZEND_API void zend_hash_set_apply_protection(HashTable *ht, zend_bool bApplyProt } } +ZEND_API uint32_t zend_hash_iterator_add(HashTable *ht) +{ + HashTableIterator *iter = EG(ht_iterators); + HashTableIterator *end = iter + EG(ht_iterators_count); + uint32_t idx; + + if (EXPECTED(ht->u.v.nIteratorsCount != 255)) { + ht->u.v.nIteratorsCount++; + } + while (iter != end) { + if (iter->ht == NULL) { + iter->ht = ht; + iter->pos = ht->nInternalPointer; + idx = iter - EG(ht_iterators); + if (idx + 1 > EG(ht_iterators_used)) { + EG(ht_iterators_used) = idx + 1; + } + return idx; + } + iter++; + } + if (EG(ht_iterators) == EG(ht_iterators_slots)) { + EG(ht_iterators) = emalloc(sizeof(HashTableIterator) * (EG(ht_iterators_count) + 8)); + memcpy(EG(ht_iterators), EG(ht_iterators_slots), sizeof(HashTableIterator) * EG(ht_iterators_count)); + } else { + EG(ht_iterators) = erealloc(EG(ht_iterators), sizeof(HashTableIterator) * (EG(ht_iterators_count) + 8)); + } + iter = EG(ht_iterators) + EG(ht_iterators_count); + EG(ht_iterators_count) += 8; + iter->ht = ht; + iter->pos = ht->nInternalPointer; + memset(iter + 1, 0, sizeof(HashTableIterator) * 7); + idx = iter - EG(ht_iterators); + EG(ht_iterators_used) = idx + 1; + return idx; +} + +ZEND_API HashPosition zend_hash_iterator_pos(uint32_t idx, HashTable *ht) +{ + HashTableIterator *iter = EG(ht_iterators) + idx; + + ZEND_ASSERT(idx != (uint32_t)-1); + if (iter->pos == INVALID_IDX) { + return INVALID_IDX; + } else if (UNEXPECTED(iter->ht != ht)) { + if (EXPECTED(iter->ht) && EXPECTED(iter->ht->u.v.nIteratorsCount != 255)) { + iter->ht->u.v.nIteratorsCount--; + } + if (EXPECTED(ht->u.v.nIteratorsCount != 255)) { + ht->u.v.nIteratorsCount++; + } + iter->ht = ht; + iter->pos = ht->nInternalPointer; + } + return iter->pos; +} + +ZEND_API void zend_hash_iterator_del(uint32_t idx) +{ + HashTableIterator *iter = EG(ht_iterators) + idx; + + ZEND_ASSERT(idx != (uint32_t)-1); + + if (EXPECTED(iter->ht) && EXPECTED(iter->ht->u.v.nIteratorsCount != 255)) { + iter->ht->u.v.nIteratorsCount--; + } + iter->ht = NULL; + + if (idx == EG(ht_iterators_used) - 1) { + while (idx > 0 && EG(ht_iterators)[idx - 1].ht == NULL) { + idx--; + } + EG(ht_iterators_used) = idx; + } +} + +static zend_never_inline void _iterators_del(HashTable *ht) +{ + HashTableIterator *iter = EG(ht_iterators); + HashTableIterator *end = iter + EG(ht_iterators_used); + uint32_t idx; + + while (iter != end) { + if (iter->ht == ht) { + iter->ht = NULL; + } + iter++; + } + + idx = EG(ht_iterators_used); + while (idx > 0 && EG(ht_iterators)[idx - 1].ht == NULL) { + idx--; + } + EG(ht_iterators_used) = idx; +} + +static zend_always_inline void iterators_del(HashTable *ht) +{ + if (UNEXPECTED(ht->u.v.nIteratorsCount)) { + _iterators_del(ht); + } +} + +static zend_never_inline void _iterators_update(HashTable *ht, HashPosition pos) +{ + HashTableIterator *iter = EG(ht_iterators); + HashTableIterator *end = iter + EG(ht_iterators_used); + + while (iter != end) { + if (iter->ht == ht && iter->pos == ht->nInternalPointer) { + iter->pos = pos; + } + iter++; + } +} + +static zend_always_inline void iterators_update(HashTable *ht, HashPosition pos) +{ + if (UNEXPECTED(ht->u.v.nIteratorsCount)) { + _iterators_update(ht, pos); + } +} + +ZEND_API void zend_hash_iterators_update(HashTable *ht, HashPosition pos) +{ + if (UNEXPECTED(ht->u.v.nIteratorsCount)) { + HashTableIterator *iter = EG(ht_iterators); + HashTableIterator *end = iter + EG(ht_iterators_used); + + while (iter != end) { + if (iter->ht == ht && iter->pos == ht->nInternalPointer) { + iter->pos = pos; + } + iter++; + } + } +} + static zend_always_inline Bucket *zend_hash_find_bucket(const HashTable *ht, zend_string *key) { zend_ulong h; @@ -303,6 +441,7 @@ add_to_hash: idx = ht->nNumUsed++; ht->nNumOfElements++; if (ht->nInternalPointer == INVALID_IDX) { + iterators_update(ht, idx); ht->nInternalPointer = idx; } p = ht->arData + idx; @@ -470,6 +609,7 @@ add_to_packed: } ht->nNumOfElements++; if (ht->nInternalPointer == INVALID_IDX) { + iterators_update(ht, h); ht->nInternalPointer = h; } if ((zend_long)h >= (zend_long)ht->nNextFreeElement) { @@ -512,6 +652,7 @@ add_to_hash: idx = ht->nNumUsed++; ht->nNumOfElements++; if (ht->nInternalPointer == INVALID_IDX) { + iterators_update(ht, idx); ht->nInternalPointer = idx; } if ((zend_long)h >= (zend_long)ht->nNextFreeElement) { @@ -600,6 +741,7 @@ ZEND_API int zend_hash_rehash(HashTable *ht) if (i != j) { ht->arData[j] = ht->arData[i]; if (ht->nInternalPointer == i) { + iterators_update(ht, j); ht->nInternalPointer = j; } } @@ -632,9 +774,11 @@ static zend_always_inline void _zend_hash_del_el_ex(HashTable *ht, uint32_t idx, while (1) { idx++; if (idx >= ht->nNumUsed) { + iterators_update(ht, INVALID_IDX); ht->nInternalPointer = INVALID_IDX; break; } else if (Z_TYPE(ht->arData[idx].val) != IS_UNDEF) { + iterators_update(ht, idx); ht->nInternalPointer = idx; break; } @@ -893,6 +1037,7 @@ ZEND_API void zend_hash_destroy(HashTable *ht) } while (++p != end); } } + iterators_del(ht); } else if (EXPECTED(!(ht->u.flags & HASH_FLAG_INITIALIZED))) { return; } @@ -933,7 +1078,7 @@ ZEND_API void zend_array_destroy(HashTable *ht) } } while (++p != end); } - + iterators_del(ht); SET_INCONSISTENT(HT_DESTROYED); } else if (EXPECTED(!(ht->u.flags & HASH_FLAG_INITIALIZED))) { return; diff --git a/Zend/zend_hash.h b/Zend/zend_hash.h index 85f1890302..c8218f1112 100644 --- a/Zend/zend_hash.h +++ b/Zend/zend_hash.h @@ -50,8 +50,6 @@ typedef struct _zend_hash_key { typedef zend_bool (*merge_checker_func_t)(HashTable *target_ht, zval *source_data, zend_hash_key *hash_key, void *pParam); -typedef uint32_t HashPosition; - BEGIN_EXTERN_C() /* startup/shutdown */ @@ -227,6 +225,13 @@ void zend_hash_display(const HashTable *ht); ZEND_API int _zend_handle_numeric_str_ex(const char *key, size_t length, zend_ulong *idx); + +ZEND_API uint32_t zend_hash_iterator_add(HashTable *ht); +ZEND_API HashPosition zend_hash_iterator_pos(uint32_t idx, HashTable *ht); +ZEND_API void zend_hash_iterator_del(uint32_t idx); +ZEND_API void zend_hash_iterators_update(HashTable *ht, HashPosition pos); + + END_EXTERN_C() #define ZEND_INIT_SYMTABLE(ht) \ diff --git a/Zend/zend_types.h b/Zend/zend_types.h index aa5404139b..dbe8c616df 100644 --- a/Zend/zend_types.h +++ b/Zend/zend_types.h @@ -129,6 +129,7 @@ struct _zval_struct { uint32_t lineno; /* line number (for ast nodes) */ uint32_t num_args; /* arguments number for EX(This) */ uint32_t fe_pos; /* foreach position */ + uint32_t fe_iter_idx; /* foreach iterator index */ } u2; }; @@ -161,10 +162,11 @@ typedef struct _Bucket { typedef struct _HashTable { union { struct { - ZEND_ENDIAN_LOHI_3( + ZEND_ENDIAN_LOHI_4( zend_uchar flags, zend_uchar nApplyCount, - uint16_t reserve) + zend_uchar nIteratorsCount, + zend_uchar reserve) } v; uint32_t flags; } u; @@ -179,6 +181,13 @@ typedef struct _HashTable { dtor_func_t pDestructor; } HashTable; +typedef uint32_t HashPosition; + +typedef struct _HashTableIterator { + HashTable *ht; + HashPosition pos; +} HashTableIterator; + struct _zend_array { zend_refcounted gc; HashTable ht; @@ -265,6 +274,9 @@ static zend_always_inline zend_uchar zval_get_type(const zval* pz) { #define Z_FE_POS(zval) (zval).u2.fe_pos #define Z_FE_POS_P(zval_p) Z_FE_POS(*(zval_p)) +#define Z_FE_ITER(zval) (zval).u2.fe_iter_idx +#define Z_FE_ITER_P(zval_p) Z_FE_ITER(*(zval_p)) + #define Z_COUNTED(zval) (zval).value.counted #define Z_COUNTED_P(zval_p) Z_COUNTED(*(zval_p)) diff --git a/Zend/zend_vm_def.h b/Zend/zend_vm_def.h index 5ddc903888..245bc0b68a 100644 --- a/Zend/zend_vm_def.h +++ b/Zend/zend_vm_def.h @@ -2138,6 +2138,21 @@ ZEND_VM_HANDLER(70, ZEND_FREE, TMPVAR, ANY) ZEND_VM_NEXT_OPCODE(); } +ZEND_VM_HANDLER(127, ZEND_FE_FREE, TMPVAR, ANY) +{ + zval *var; + USE_OPLINE + + SAVE_OPLINE(); + var = EX_VAR(opline->op1.var); + if (Z_FE_ITER_P(var) != (uint32_t)-1) { + zend_hash_iterator_del(Z_FE_ITER_P(var)); + } + zval_ptr_dtor_nogc(var); + CHECK_EXCEPTION(); + ZEND_VM_NEXT_OPCODE(); +} + ZEND_VM_HANDLER(54, ZEND_ADD_CHAR, TMP|UNUSED, CONST) { USE_OPLINE @@ -3821,6 +3836,14 @@ ZEND_VM_HANDLER(100, ZEND_GOTO, ANY, CONST) if (!(brk_opline->extended_value & EXT_TYPE_FREE_ON_RETURN)) { zval_ptr_dtor_nogc(EX_VAR(brk_opline->op1.var)); } + } else if (brk_opline->opcode == ZEND_FE_FREE) { + if (!(brk_opline->extended_value & EXT_TYPE_FREE_ON_RETURN)) { + zval *var = EX_VAR(brk_opline->op1.var); + if (Z_FE_ITER_P(var) != (uint32_t)-1) { + zend_hash_iterator_del(Z_FE_ITER_P(var)); + } + zval_ptr_dtor_nogc(var); + } } ZEND_VM_JMP(OP_JMP_ADDR(opline, opline->op1)); } @@ -4758,8 +4781,7 @@ ZEND_VM_HANDLER(125, ZEND_FE_RESET_RW, CONST|TMP|VAR|CV, ANY) iter->index = -1; /* will be set to 0 before using next handler */ ZVAL_OBJ(EX_VAR(opline->result.var), &iter->std); - Z_FE_POS_P(EX_VAR(opline->result.var)) = INVALID_IDX; - ZVAL_PTR(EX_VAR((opline+2)->op1.var), NULL); + Z_FE_ITER_P(EX_VAR(opline->result.var)) = (uint32_t)-1; if (OP1_TYPE == IS_VAR) { FREE_OP1_VAR_PTR(); @@ -4798,6 +4820,7 @@ ZEND_VM_HANDLER(125, ZEND_FE_RESET_RW, CONST|TMP|VAR|CV, ANY) while (1) { if (pos >= fe_ht->nNumUsed) { FREE_OP1_VAR_PTR(); + Z_FE_ITER_P(EX_VAR(opline->result.var)) = (uint32_t)-1; ZEND_VM_JMP(OP_JMP_ADDR(opline, opline->op2)); } p = fe_ht->arData + pos; @@ -4811,8 +4834,8 @@ ZEND_VM_HANDLER(125, ZEND_FE_RESET_RW, CONST|TMP|VAR|CV, ANY) } pos++; } - Z_FE_POS_P(EX_VAR(opline->result.var)) = fe_ht->nInternalPointer = pos; - ZVAL_PTR(EX_VAR((opline+2)->op1.var), fe_ht); + fe_ht->nInternalPointer = pos; + Z_FE_ITER_P(EX_VAR(opline->result.var)) = zend_hash_iterator_add(fe_ht); FREE_OP1_VAR_PTR(); CHECK_EXCEPTION(); @@ -4820,6 +4843,7 @@ ZEND_VM_HANDLER(125, ZEND_FE_RESET_RW, CONST|TMP|VAR|CV, ANY) } else { zend_error(E_WARNING, "Invalid argument supplied for foreach()"); ZVAL_UNDEF(EX_VAR(opline->result.var)); + Z_FE_ITER_P(EX_VAR(opline->result.var)) = (uint32_t)-1; if (OP1_TYPE == IS_VAR) { FREE_OP1_VAR_PTR(); } else { @@ -4990,21 +5014,7 @@ ZEND_VM_HANDLER(126, ZEND_FE_FETCH_RW, VAR, ANY) ZVAL_DEREF(array); if (EXPECTED(Z_TYPE_P(array) == IS_ARRAY)) { fe_ht = Z_ARRVAL_P(array); - pos = Z_FE_POS_P(EX_VAR(opline->op1.var)); - if (UNEXPECTED(pos == INVALID_IDX)) { - /* reached end of iteration */ - ZEND_VM_JMP(OP_JMP_ADDR(opline, opline->op2)); - } else { - if (Z_PTR_P(EX_VAR((opline+1)->op1.var)) != fe_ht) { - pos = fe_ht->nInternalPointer; - } - SEPARATE_ARRAY(array); - fe_ht = Z_ARRVAL_P(array); - Z_PTR_P(EX_VAR((opline+1)->op1.var)) = fe_ht; - } -//??? if (pos != fe_ht->nInternalPointer) { -//??? //... -//??? } + pos = zend_hash_iterator_pos(Z_FE_ITER_P(EX_VAR(opline->op1.var)), fe_ht); while (1) { if (UNEXPECTED(pos >= fe_ht->nNumUsed)) { /* reached end of iteration */ @@ -5048,7 +5058,8 @@ ZEND_VM_HANDLER(126, ZEND_FE_FETCH_RW, VAR, ANY) break; } } - Z_FE_POS_P(EX_VAR(opline->op1.var)) = fe_ht->nInternalPointer = pos; + EG(ht_iterators)[Z_FE_ITER_P(EX_VAR(opline->op1.var))].pos = + fe_ht->nInternalPointer = pos; ZEND_VM_INC_OPCODE(); ZEND_VM_NEXT_OPCODE(); } else if (EXPECTED(Z_TYPE_P(array) == IS_OBJECT)) { @@ -5059,19 +5070,7 @@ ZEND_VM_HANDLER(126, ZEND_FE_FETCH_RW, VAR, ANY) zend_object *zobj = Z_OBJ_P(array); fe_ht = Z_OBJPROP_P(array); - pos = Z_FE_POS_P(EX_VAR(opline->op1.var)); - if (UNEXPECTED(pos == INVALID_IDX)) { - /* reached end of iteration */ - ZEND_VM_JMP(OP_JMP_ADDR(opline, opline->op2)); - } else { - if (Z_PTR_P(EX_VAR((opline+1)->op1.var)) != fe_ht) { - pos = fe_ht->nInternalPointer; - } - Z_PTR_P(EX_VAR((opline+1)->op1.var)) = fe_ht; - } -//??? if (pos != fe_ht->nInternalPointer) { -//??? -//??? } + pos = zend_hash_iterator_pos(Z_FE_ITER_P(EX_VAR(opline->op1.var)), fe_ht); while (1) { if (UNEXPECTED(pos >= fe_ht->nNumUsed)) { /* reached end of iteration */ @@ -5129,7 +5128,8 @@ ZEND_VM_HANDLER(126, ZEND_FE_FETCH_RW, VAR, ANY) break; } } - Z_FE_POS_P(EX_VAR(opline->op1.var)) = fe_ht->nInternalPointer = pos; + EG(ht_iterators)[Z_FE_ITER_P(EX_VAR(opline->op1.var))].pos = + fe_ht->nInternalPointer = pos; ZEND_VM_INC_OPCODE(); ZEND_VM_NEXT_OPCODE(); } else { @@ -5963,6 +5963,14 @@ ZEND_VM_HANDLER(149, ZEND_HANDLE_EXCEPTION, ANY, ANY) if (!(brk_opline->extended_value & EXT_TYPE_FREE_ON_RETURN)) { zval_ptr_dtor_nogc(EX_VAR(brk_opline->op1.var)); } + } else if (brk_opline->opcode == ZEND_FE_FREE) { + if (!(brk_opline->extended_value & EXT_TYPE_FREE_ON_RETURN)) { + zval *var = EX_VAR(brk_opline->op1.var); + if (Z_FE_ITER_P(var) != (uint32_t)-1) { + zend_hash_iterator_del(Z_FE_ITER_P(var)); + } + zval_ptr_dtor_nogc(var); + } } else if (brk_opline->opcode == ZEND_END_SILENCE) { /* restore previous error_reporting value */ if (!EG(error_reporting) && Z_LVAL_P(EX_VAR(brk_opline->op1.var)) != 0) { diff --git a/Zend/zend_vm_execute.h b/Zend/zend_vm_execute.h index 362f450390..4244f5f750 100644 --- a/Zend/zend_vm_execute.h +++ b/Zend/zend_vm_execute.h @@ -1347,6 +1347,14 @@ static int ZEND_FASTCALL ZEND_HANDLE_EXCEPTION_SPEC_HANDLER(ZEND_OPCODE_HANDLER if (!(brk_opline->extended_value & EXT_TYPE_FREE_ON_RETURN)) { zval_ptr_dtor_nogc(EX_VAR(brk_opline->op1.var)); } + } else if (brk_opline->opcode == ZEND_FE_FREE) { + if (!(brk_opline->extended_value & EXT_TYPE_FREE_ON_RETURN)) { + zval *var = EX_VAR(brk_opline->op1.var); + if (Z_FE_ITER_P(var) != (uint32_t)-1) { + zend_hash_iterator_del(Z_FE_ITER_P(var)); + } + zval_ptr_dtor_nogc(var); + } } else if (brk_opline->opcode == ZEND_END_SILENCE) { /* restore previous error_reporting value */ if (!EG(error_reporting) && Z_LVAL_P(EX_VAR(brk_opline->op1.var)) != 0) { @@ -1805,6 +1813,14 @@ static int ZEND_FASTCALL ZEND_GOTO_SPEC_CONST_HANDLER(ZEND_OPCODE_HANDLER_ARGS) if (!(brk_opline->extended_value & EXT_TYPE_FREE_ON_RETURN)) { zval_ptr_dtor_nogc(EX_VAR(brk_opline->op1.var)); } + } else if (brk_opline->opcode == ZEND_FE_FREE) { + if (!(brk_opline->extended_value & EXT_TYPE_FREE_ON_RETURN)) { + zval *var = EX_VAR(brk_opline->op1.var); + if (Z_FE_ITER_P(var) != (uint32_t)-1) { + zend_hash_iterator_del(Z_FE_ITER_P(var)); + } + zval_ptr_dtor_nogc(var); + } } ZEND_VM_JMP(OP_JMP_ADDR(opline, opline->op1)); } @@ -3175,8 +3191,7 @@ static int ZEND_FASTCALL ZEND_FE_RESET_RW_SPEC_CONST_HANDLER(ZEND_OPCODE_HANDLE iter->index = -1; /* will be set to 0 before using next handler */ ZVAL_OBJ(EX_VAR(opline->result.var), &iter->std); - Z_FE_POS_P(EX_VAR(opline->result.var)) = INVALID_IDX; - ZVAL_PTR(EX_VAR((opline+2)->op1.var), NULL); + Z_FE_ITER_P(EX_VAR(opline->result.var)) = (uint32_t)-1; if (IS_CONST == IS_VAR) { @@ -3215,6 +3230,7 @@ static int ZEND_FASTCALL ZEND_FE_RESET_RW_SPEC_CONST_HANDLER(ZEND_OPCODE_HANDLE while (1) { if (pos >= fe_ht->nNumUsed) { + Z_FE_ITER_P(EX_VAR(opline->result.var)) = (uint32_t)-1; ZEND_VM_JMP(OP_JMP_ADDR(opline, opline->op2)); } p = fe_ht->arData + pos; @@ -3228,14 +3244,15 @@ static int ZEND_FASTCALL ZEND_FE_RESET_RW_SPEC_CONST_HANDLER(ZEND_OPCODE_HANDLE } pos++; } - Z_FE_POS_P(EX_VAR(opline->result.var)) = fe_ht->nInternalPointer = pos; - ZVAL_PTR(EX_VAR((opline+2)->op1.var), fe_ht); + fe_ht->nInternalPointer = pos; + Z_FE_ITER_P(EX_VAR(opline->result.var)) = zend_hash_iterator_add(fe_ht); CHECK_EXCEPTION(); ZEND_VM_NEXT_OPCODE(); } else { zend_error(E_WARNING, "Invalid argument supplied for foreach()"); ZVAL_UNDEF(EX_VAR(opline->result.var)); + Z_FE_ITER_P(EX_VAR(opline->result.var)) = (uint32_t)-1; if (IS_CONST == IS_VAR) { } else { @@ -9109,8 +9126,7 @@ static int ZEND_FASTCALL ZEND_FE_RESET_RW_SPEC_TMP_HANDLER(ZEND_OPCODE_HANDLER_ iter->index = -1; /* will be set to 0 before using next handler */ ZVAL_OBJ(EX_VAR(opline->result.var), &iter->std); - Z_FE_POS_P(EX_VAR(opline->result.var)) = INVALID_IDX; - ZVAL_PTR(EX_VAR((opline+2)->op1.var), NULL); + Z_FE_ITER_P(EX_VAR(opline->result.var)) = (uint32_t)-1; if (IS_TMP_VAR == IS_VAR) { @@ -9149,6 +9165,7 @@ static int ZEND_FASTCALL ZEND_FE_RESET_RW_SPEC_TMP_HANDLER(ZEND_OPCODE_HANDLER_ while (1) { if (pos >= fe_ht->nNumUsed) { + Z_FE_ITER_P(EX_VAR(opline->result.var)) = (uint32_t)-1; ZEND_VM_JMP(OP_JMP_ADDR(opline, opline->op2)); } p = fe_ht->arData + pos; @@ -9162,14 +9179,15 @@ static int ZEND_FASTCALL ZEND_FE_RESET_RW_SPEC_TMP_HANDLER(ZEND_OPCODE_HANDLER_ } pos++; } - Z_FE_POS_P(EX_VAR(opline->result.var)) = fe_ht->nInternalPointer = pos; - ZVAL_PTR(EX_VAR((opline+2)->op1.var), fe_ht); + fe_ht->nInternalPointer = pos; + Z_FE_ITER_P(EX_VAR(opline->result.var)) = zend_hash_iterator_add(fe_ht); CHECK_EXCEPTION(); ZEND_VM_NEXT_OPCODE(); } else { zend_error(E_WARNING, "Invalid argument supplied for foreach()"); ZVAL_UNDEF(EX_VAR(opline->result.var)); + Z_FE_ITER_P(EX_VAR(opline->result.var)) = (uint32_t)-1; if (IS_TMP_VAR == IS_VAR) { } else { @@ -11945,8 +11963,7 @@ static int ZEND_FASTCALL ZEND_FE_RESET_RW_SPEC_VAR_HANDLER(ZEND_OPCODE_HANDLER_ iter->index = -1; /* will be set to 0 before using next handler */ ZVAL_OBJ(EX_VAR(opline->result.var), &iter->std); - Z_FE_POS_P(EX_VAR(opline->result.var)) = INVALID_IDX; - ZVAL_PTR(EX_VAR((opline+2)->op1.var), NULL); + Z_FE_ITER_P(EX_VAR(opline->result.var)) = (uint32_t)-1; if (IS_VAR == IS_VAR) { if (free_op1) {zval_ptr_dtor_nogc(free_op1);}; @@ -11985,6 +12002,7 @@ static int ZEND_FASTCALL ZEND_FE_RESET_RW_SPEC_VAR_HANDLER(ZEND_OPCODE_HANDLER_ while (1) { if (pos >= fe_ht->nNumUsed) { if (free_op1) {zval_ptr_dtor_nogc(free_op1);}; + Z_FE_ITER_P(EX_VAR(opline->result.var)) = (uint32_t)-1; ZEND_VM_JMP(OP_JMP_ADDR(opline, opline->op2)); } p = fe_ht->arData + pos; @@ -11998,8 +12016,8 @@ static int ZEND_FASTCALL ZEND_FE_RESET_RW_SPEC_VAR_HANDLER(ZEND_OPCODE_HANDLER_ } pos++; } - Z_FE_POS_P(EX_VAR(opline->result.var)) = fe_ht->nInternalPointer = pos; - ZVAL_PTR(EX_VAR((opline+2)->op1.var), fe_ht); + fe_ht->nInternalPointer = pos; + Z_FE_ITER_P(EX_VAR(opline->result.var)) = zend_hash_iterator_add(fe_ht); if (free_op1) {zval_ptr_dtor_nogc(free_op1);}; CHECK_EXCEPTION(); @@ -12007,6 +12025,7 @@ static int ZEND_FASTCALL ZEND_FE_RESET_RW_SPEC_VAR_HANDLER(ZEND_OPCODE_HANDLER_ } else { zend_error(E_WARNING, "Invalid argument supplied for foreach()"); ZVAL_UNDEF(EX_VAR(opline->result.var)); + Z_FE_ITER_P(EX_VAR(opline->result.var)) = (uint32_t)-1; if (IS_VAR == IS_VAR) { if (free_op1) {zval_ptr_dtor_nogc(free_op1);}; } else { @@ -12177,21 +12196,7 @@ static int ZEND_FASTCALL ZEND_FE_FETCH_RW_SPEC_VAR_HANDLER(ZEND_OPCODE_HANDLER_ ZVAL_DEREF(array); if (EXPECTED(Z_TYPE_P(array) == IS_ARRAY)) { fe_ht = Z_ARRVAL_P(array); - pos = Z_FE_POS_P(EX_VAR(opline->op1.var)); - if (UNEXPECTED(pos == INVALID_IDX)) { - /* reached end of iteration */ - ZEND_VM_JMP(OP_JMP_ADDR(opline, opline->op2)); - } else { - if (Z_PTR_P(EX_VAR((opline+1)->op1.var)) != fe_ht) { - pos = fe_ht->nInternalPointer; - } - SEPARATE_ARRAY(array); - fe_ht = Z_ARRVAL_P(array); - Z_PTR_P(EX_VAR((opline+1)->op1.var)) = fe_ht; - } -//??? if (pos != fe_ht->nInternalPointer) { -//??? //... -//??? } + pos = zend_hash_iterator_pos(Z_FE_ITER_P(EX_VAR(opline->op1.var)), fe_ht); while (1) { if (UNEXPECTED(pos >= fe_ht->nNumUsed)) { /* reached end of iteration */ @@ -12235,7 +12240,8 @@ static int ZEND_FASTCALL ZEND_FE_FETCH_RW_SPEC_VAR_HANDLER(ZEND_OPCODE_HANDLER_ break; } } - Z_FE_POS_P(EX_VAR(opline->op1.var)) = fe_ht->nInternalPointer = pos; + EG(ht_iterators)[Z_FE_ITER_P(EX_VAR(opline->op1.var))].pos = + fe_ht->nInternalPointer = pos; ZEND_VM_INC_OPCODE(); ZEND_VM_NEXT_OPCODE(); } else if (EXPECTED(Z_TYPE_P(array) == IS_OBJECT)) { @@ -12246,19 +12252,7 @@ static int ZEND_FASTCALL ZEND_FE_FETCH_RW_SPEC_VAR_HANDLER(ZEND_OPCODE_HANDLER_ zend_object *zobj = Z_OBJ_P(array); fe_ht = Z_OBJPROP_P(array); - pos = Z_FE_POS_P(EX_VAR(opline->op1.var)); - if (UNEXPECTED(pos == INVALID_IDX)) { - /* reached end of iteration */ - ZEND_VM_JMP(OP_JMP_ADDR(opline, opline->op2)); - } else { - if (Z_PTR_P(EX_VAR((opline+1)->op1.var)) != fe_ht) { - pos = fe_ht->nInternalPointer; - } - Z_PTR_P(EX_VAR((opline+1)->op1.var)) = fe_ht; - } -//??? if (pos != fe_ht->nInternalPointer) { -//??? -//??? } + pos = zend_hash_iterator_pos(Z_FE_ITER_P(EX_VAR(opline->op1.var)), fe_ht); while (1) { if (UNEXPECTED(pos >= fe_ht->nNumUsed)) { /* reached end of iteration */ @@ -12316,7 +12310,8 @@ static int ZEND_FASTCALL ZEND_FE_FETCH_RW_SPEC_VAR_HANDLER(ZEND_OPCODE_HANDLER_ break; } } - Z_FE_POS_P(EX_VAR(opline->op1.var)) = fe_ht->nInternalPointer = pos; + EG(ht_iterators)[Z_FE_ITER_P(EX_VAR(opline->op1.var))].pos = + fe_ht->nInternalPointer = pos; ZEND_VM_INC_OPCODE(); ZEND_VM_NEXT_OPCODE(); } else { @@ -24354,8 +24349,7 @@ static int ZEND_FASTCALL ZEND_FE_RESET_RW_SPEC_CV_HANDLER(ZEND_OPCODE_HANDLER_A iter->index = -1; /* will be set to 0 before using next handler */ ZVAL_OBJ(EX_VAR(opline->result.var), &iter->std); - Z_FE_POS_P(EX_VAR(opline->result.var)) = INVALID_IDX; - ZVAL_PTR(EX_VAR((opline+2)->op1.var), NULL); + Z_FE_ITER_P(EX_VAR(opline->result.var)) = (uint32_t)-1; if (IS_CV == IS_VAR) { @@ -24394,6 +24388,7 @@ static int ZEND_FASTCALL ZEND_FE_RESET_RW_SPEC_CV_HANDLER(ZEND_OPCODE_HANDLER_A while (1) { if (pos >= fe_ht->nNumUsed) { + Z_FE_ITER_P(EX_VAR(opline->result.var)) = (uint32_t)-1; ZEND_VM_JMP(OP_JMP_ADDR(opline, opline->op2)); } p = fe_ht->arData + pos; @@ -24407,14 +24402,15 @@ static int ZEND_FASTCALL ZEND_FE_RESET_RW_SPEC_CV_HANDLER(ZEND_OPCODE_HANDLER_A } pos++; } - Z_FE_POS_P(EX_VAR(opline->result.var)) = fe_ht->nInternalPointer = pos; - ZVAL_PTR(EX_VAR((opline+2)->op1.var), fe_ht); + fe_ht->nInternalPointer = pos; + Z_FE_ITER_P(EX_VAR(opline->result.var)) = zend_hash_iterator_add(fe_ht); CHECK_EXCEPTION(); ZEND_VM_NEXT_OPCODE(); } else { zend_error(E_WARNING, "Invalid argument supplied for foreach()"); ZVAL_UNDEF(EX_VAR(opline->result.var)); + Z_FE_ITER_P(EX_VAR(opline->result.var)) = (uint32_t)-1; if (IS_CV == IS_VAR) { } else { @@ -33144,6 +33140,21 @@ static int ZEND_FASTCALL ZEND_FREE_SPEC_TMPVAR_HANDLER(ZEND_OPCODE_HANDLER_ARGS ZEND_VM_NEXT_OPCODE(); } +static int ZEND_FASTCALL ZEND_FE_FREE_SPEC_TMPVAR_HANDLER(ZEND_OPCODE_HANDLER_ARGS) +{ + zval *var; + USE_OPLINE + + SAVE_OPLINE(); + var = EX_VAR(opline->op1.var); + if (Z_FE_ITER_P(var) != (uint32_t)-1) { + zend_hash_iterator_del(Z_FE_ITER_P(var)); + } + zval_ptr_dtor_nogc(var); + CHECK_EXCEPTION(); + ZEND_VM_NEXT_OPCODE(); +} + static int ZEND_FASTCALL ZEND_BOOL_SPEC_TMPVAR_HANDLER(ZEND_OPCODE_HANDLER_ARGS) { USE_OPLINE @@ -39719,16 +39730,16 @@ void zend_init_opcodes_handlers(void) ZEND_NULL_HANDLER, ZEND_NULL_HANDLER, ZEND_NULL_HANDLER, - ZEND_NULL_HANDLER, - ZEND_NULL_HANDLER, - ZEND_NULL_HANDLER, - ZEND_NULL_HANDLER, - ZEND_NULL_HANDLER, - ZEND_NULL_HANDLER, - ZEND_NULL_HANDLER, - ZEND_NULL_HANDLER, - ZEND_NULL_HANDLER, - ZEND_NULL_HANDLER, + ZEND_FE_FREE_SPEC_TMPVAR_HANDLER, + ZEND_FE_FREE_SPEC_TMPVAR_HANDLER, + ZEND_FE_FREE_SPEC_TMPVAR_HANDLER, + ZEND_FE_FREE_SPEC_TMPVAR_HANDLER, + ZEND_FE_FREE_SPEC_TMPVAR_HANDLER, + ZEND_FE_FREE_SPEC_TMPVAR_HANDLER, + ZEND_FE_FREE_SPEC_TMPVAR_HANDLER, + ZEND_FE_FREE_SPEC_TMPVAR_HANDLER, + ZEND_FE_FREE_SPEC_TMPVAR_HANDLER, + ZEND_FE_FREE_SPEC_TMPVAR_HANDLER, ZEND_NULL_HANDLER, ZEND_NULL_HANDLER, ZEND_NULL_HANDLER, diff --git a/Zend/zend_vm_opcodes.c b/Zend/zend_vm_opcodes.c index 295600c6fd..d7c5db6966 100644 --- a/Zend/zend_vm_opcodes.c +++ b/Zend/zend_vm_opcodes.c @@ -149,7 +149,7 @@ const char *zend_vm_opcodes_map[170] = { "ZEND_VERIFY_RETURN_TYPE", "ZEND_FE_RESET_RW", "ZEND_FE_FETCH_RW", - NULL, + "ZEND_FE_FREE", NULL, NULL, NULL, diff --git a/Zend/zend_vm_opcodes.h b/Zend/zend_vm_opcodes.h index 9cb1679e98..aa4afbd984 100644 --- a/Zend/zend_vm_opcodes.h +++ b/Zend/zend_vm_opcodes.h @@ -151,6 +151,7 @@ END_EXTERN_C() #define ZEND_VERIFY_RETURN_TYPE 124 #define ZEND_FE_RESET_RW 125 #define ZEND_FE_FETCH_RW 126 +#define ZEND_FE_FREE 127 #define ZEND_PRE_INC_OBJ 132 #define ZEND_PRE_DEC_OBJ 133 #define ZEND_POST_INC_OBJ 134 diff --git a/ext/opcache/Optimizer/block_pass.c b/ext/opcache/Optimizer/block_pass.c index 3df0faa0ef..4db48944e1 100644 --- a/ext/opcache/Optimizer/block_pass.c +++ b/ext/opcache/Optimizer/block_pass.c @@ -207,12 +207,15 @@ static int find_code_blocks(zend_op_array *op_array, zend_cfg *cfg, zend_optimiz for (i = 0; i< op_array->last_brk_cont; i++) { if (op_array->brk_cont_array[i].start >= 0 && (op_array->opcodes[op_array->brk_cont_array[i].brk].opcode == ZEND_FREE || + op_array->opcodes[op_array->brk_cont_array[i].brk].opcode == ZEND_FE_FREE || op_array->opcodes[op_array->brk_cont_array[i].brk].opcode == ZEND_END_SILENCE)) { int parent = op_array->brk_cont_array[i].parent; while (parent >= 0 && op_array->brk_cont_array[parent].start < 0 && - op_array->opcodes[op_array->brk_cont_array[parent].brk].opcode != ZEND_FREE) { + (op_array->opcodes[op_array->brk_cont_array[parent].brk].opcode != ZEND_FREE || + op_array->opcodes[op_array->brk_cont_array[parent].brk].opcode != ZEND_FE_FREE || + op_array->opcodes[op_array->brk_cont_array[parent].brk].opcode != ZEND_END_SILENCE)) { parent = op_array->brk_cont_array[parent].parent; } op_array->brk_cont_array[i].parent = parent; @@ -227,6 +230,7 @@ static int find_code_blocks(zend_op_array *op_array, zend_cfg *cfg, zend_optimiz for (i = 0; i< op_array->last_brk_cont; i++) { if (op_array->brk_cont_array[i].start >= 0 && (op_array->opcodes[op_array->brk_cont_array[i].brk].opcode == ZEND_FREE || + op_array->opcodes[op_array->brk_cont_array[i].brk].opcode == ZEND_FE_FREE || op_array->opcodes[op_array->brk_cont_array[i].brk].opcode == ZEND_END_SILENCE)) { if (i != j) { op_array->brk_cont_array[j] = op_array->brk_cont_array[i]; diff --git a/ext/opcache/Optimizer/optimize_temp_vars_5.c b/ext/opcache/Optimizer/optimize_temp_vars_5.c index 7ff94ddae5..dc93ce2f4c 100644 --- a/ext/opcache/Optimizer/optimize_temp_vars_5.c +++ b/ext/opcache/Optimizer/optimize_temp_vars_5.c @@ -66,12 +66,6 @@ void optimize_temporary_variables(zend_op_array *op_array, zend_optimizer_ctx *c if (ZEND_RESULT_TYPE(opline) & (IS_VAR | IS_TMP_VAR)) { start_of_T[VAR_NUM(ZEND_RESULT(opline).var) - offset] = opline; } - /* special puprose variable to keep HashTable* on VM stack */ - if (opline->opcode == ZEND_OP_DATA && - (opline-1)->opcode == ZEND_FE_FETCH_RW && - opline->op1_type == IS_TMP_VAR) { - start_of_T[VAR_NUM(ZEND_OP1(opline).var) - offset] = opline; - } opline--; } @@ -84,21 +78,13 @@ void optimize_temporary_variables(zend_op_array *op_array, zend_optimizer_ctx *c while (opline >= end) { if ((ZEND_OP1_TYPE(opline) & (IS_VAR | IS_TMP_VAR))) { - /* special puprose variable to keep HashPointer on VM stack */ - if (opline->opcode == ZEND_OP_DATA && - (opline-1)->opcode == ZEND_FE_FETCH_RW && - opline->op1_type == IS_TMP_VAR) { - max++; - ZEND_OP1(opline).var = NUM_VAR(max + offset); - } else { - currT = VAR_NUM(ZEND_OP1(opline).var) - offset; - if (!valid_T[currT]) { - GET_AVAILABLE_T(); - map_T[currT] = i; - valid_T[currT] = 1; - } - ZEND_OP1(opline).var = NUM_VAR(map_T[currT] + offset); + currT = VAR_NUM(ZEND_OP1(opline).var) - offset; + if (!valid_T[currT]) { + GET_AVAILABLE_T(); + map_T[currT] = i; + valid_T[currT] = 1; } + ZEND_OP1(opline).var = NUM_VAR(map_T[currT] + offset); } /* Skip OP_DATA */ diff --git a/ext/opcache/Optimizer/pass2.c b/ext/opcache/Optimizer/pass2.c index 5be1e30c9f..b578592493 100644 --- a/ext/opcache/Optimizer/pass2.c +++ b/ext/opcache/Optimizer/pass2.c @@ -203,7 +203,9 @@ void zend_optimizer_pass2(zend_op_array *op_array) jmp_to = &op_array->brk_cont_array[array_offset]; array_offset = jmp_to->parent; if (--nest_levels > 0) { - if (op_array->opcodes[jmp_to->brk].opcode == ZEND_FREE) { + if (op_array->opcodes[jmp_to->brk].opcode == ZEND_FREE || + op_array->opcodes[jmp_to->brk].opcode == ZEND_FE_FREE || + op_array->opcodes[jmp_to->brk].opcode == ZEND_END_SILENCE) { dont_optimize = 1; break; } diff --git a/ext/standard/array.c b/ext/standard/array.c index 966ea0f1f6..397bb88f0b 100644 --- a/ext/standard/array.c +++ b/ext/standard/array.c @@ -2203,6 +2203,9 @@ PHP_FUNCTION(array_shift) q->key = NULL; ZVAL_COPY_VALUE(&q->val, &p->val); ZVAL_UNDEF(&p->val); + if (idx == Z_ARRVAL_P(stack)->nInternalPointer) { + zend_hash_iterators_update(Z_ARRVAL_P(stack), k); + } } k++; } @@ -2265,9 +2268,14 @@ PHP_FUNCTION(array_unshift) } } ZEND_HASH_FOREACH_END(); + new_hash.nInternalPointer = Z_ARRVAL_P(stack)->nInternalPointer; + new_hash.u.v.nIteratorsCount = Z_ARRVAL_P(stack)->u.v.nIteratorsCount; + Z_ARRVAL_P(stack)->u.v.nIteratorsCount = 0; Z_ARRVAL_P(stack)->pDestructor = NULL; zend_hash_destroy(Z_ARRVAL_P(stack)); *Z_ARRVAL_P(stack) = new_hash; + zend_hash_iterators_update(Z_ARRVAL_P(stack), 0); + zend_hash_internal_pointer_reset(Z_ARRVAL_P(stack)); /* Clean up and return the number of elements in the stack */ RETVAL_LONG(zend_hash_num_elements(Z_ARRVAL_P(stack))); diff --git a/tests/lang/foreachLoop.013.phpt b/tests/lang/foreachLoop.013.phpt index b0c5e8dcf5..10f43f3998 100644 --- a/tests/lang/foreachLoop.013.phpt +++ b/tests/lang/foreachLoop.013.phpt @@ -1,7 +1,5 @@ --TEST--
Directly modifying an unreferenced array when foreach'ing over it while using &$value syntax.
---XFAIL--
-Needs major foreach changes to get sane behavior
--FILE--
<?php
@@ -70,7 +68,7 @@ withRefValue(3, $transform); withRefValue(4, $transform);
?>
---EXPECTF--
+--EXPECT--
Popping elements off end of an unreferenced array, using &$value.
---( Array with 1 element(s): )---
@@ -95,9 +93,10 @@ array(2) { }
--> Do loop:
iteration 0: $k=0; $v=v.0
- iteration 1: $k=0; $v=v.0
--> State of array after loop:
-array(0) {
+array(1) {
+ [0]=>
+ &string(3) "v.0"
}
---( Array with 3 element(s): )---
@@ -134,10 +133,12 @@ array(4) { --> Do loop:
iteration 0: $k=0; $v=v.0
iteration 1: $k=1; $v=v.1
- iteration 2: $k=0; $v=v.0
- iteration 3: $k=0; $v=v.0
--> State of array after loop:
-array(0) {
+array(2) {
+ [0]=>
+ string(3) "v.0"
+ [1]=>
+ &string(3) "v.1"
}
@@ -289,12 +290,28 @@ array(1) { }
--> Do loop:
iteration 0: $k=0; $v=v.0
+ iteration 1: $k=1; $v=new.0
+ iteration 2: $k=2; $v=new.1
+ iteration 3: $k=3; $v=new.2
+ iteration 4: $k=4; $v=new.3
+ iteration 5: $k=5; $v=new.4
+ ** Stuck in a loop! **
--> State of array after loop:
-array(2) {
+array(7) {
[0]=>
- &string(3) "v.0"
+ string(3) "v.0"
[1]=>
string(5) "new.0"
+ [2]=>
+ string(5) "new.1"
+ [3]=>
+ string(5) "new.2"
+ [4]=>
+ string(5) "new.3"
+ [5]=>
+ &string(5) "new.4"
+ [6]=>
+ string(5) "new.5"
}
---( Array with 2 element(s): )---
@@ -428,12 +445,28 @@ array(1) { }
--> Do loop:
iteration 0: $k=0; $v=v.0
+ iteration 1: $k=0; $v=new.0
+ iteration 2: $k=0; $v=new.1
+ iteration 3: $k=0; $v=new.2
+ iteration 4: $k=0; $v=new.3
+ iteration 5: $k=0; $v=new.4
+ ** Stuck in a loop! **
--> State of array after loop:
-array(2) {
+array(7) {
[0]=>
- string(5) "new.0"
+ string(5) "new.5"
[1]=>
- &string(3) "v.0"
+ &string(5) "new.4"
+ [2]=>
+ string(5) "new.3"
+ [3]=>
+ string(5) "new.2"
+ [4]=>
+ string(5) "new.1"
+ [5]=>
+ string(5) "new.0"
+ [6]=>
+ string(3) "v.0"
}
---( Array with 2 element(s): )---
diff --git a/tests/lang/foreachLoop.015.phpt b/tests/lang/foreachLoop.015.phpt index 5b12a2b0c2..a56249ee0b 100644 --- a/tests/lang/foreachLoop.015.phpt +++ b/tests/lang/foreachLoop.015.phpt @@ -1,7 +1,5 @@ --TEST--
Directly modifying a REFERENCED array when foreach'ing over it while using &$value syntax.
---XFAIL--
-Needs major foreach changes to get sane behavior
--FILE--
<?php
@@ -72,7 +70,7 @@ withRefValue(3, $transform); withRefValue(4, $transform);
?>
---EXPECTF--
+--EXPECT--
Popping elements off end of a referenced array, using &$value
---( Array with 1 element(s): )---
@@ -97,9 +95,10 @@ array(2) { }
--> Do loop:
iteration 0: $k=0; $v=v.0
- iteration 1: $k=0; $v=v.0
--> State of array after loop:
-array(0) {
+array(1) {
+ [0]=>
+ &string(3) "v.0"
}
---( Array with 3 element(s): )---
@@ -136,10 +135,12 @@ array(4) { --> Do loop:
iteration 0: $k=0; $v=v.0
iteration 1: $k=1; $v=v.1
- iteration 2: $k=0; $v=v.0
- iteration 3: $k=0; $v=v.0
--> State of array after loop:
-array(0) {
+array(2) {
+ [0]=>
+ string(3) "v.0"
+ [1]=>
+ &string(3) "v.1"
}
@@ -291,12 +292,28 @@ array(1) { }
--> Do loop:
iteration 0: $k=0; $v=v.0
+ iteration 1: $k=1; $v=new.0
+ iteration 2: $k=2; $v=new.1
+ iteration 3: $k=3; $v=new.2
+ iteration 4: $k=4; $v=new.3
+ iteration 5: $k=5; $v=new.4
+ ** Stuck in a loop! **
--> State of array after loop:
-array(2) {
+array(7) {
[0]=>
- &string(3) "v.0"
+ string(3) "v.0"
[1]=>
string(5) "new.0"
+ [2]=>
+ string(5) "new.1"
+ [3]=>
+ string(5) "new.2"
+ [4]=>
+ string(5) "new.3"
+ [5]=>
+ &string(5) "new.4"
+ [6]=>
+ string(5) "new.5"
}
---( Array with 2 element(s): )---
@@ -430,12 +447,28 @@ array(1) { }
--> Do loop:
iteration 0: $k=0; $v=v.0
+ iteration 1: $k=0; $v=new.0
+ iteration 2: $k=0; $v=new.1
+ iteration 3: $k=0; $v=new.2
+ iteration 4: $k=0; $v=new.3
+ iteration 5: $k=0; $v=new.4
+ ** Stuck in a loop! **
--> State of array after loop:
-array(2) {
+array(7) {
[0]=>
- string(5) "new.0"
+ string(5) "new.5"
[1]=>
- &string(3) "v.0"
+ &string(5) "new.4"
+ [2]=>
+ string(5) "new.3"
+ [3]=>
+ string(5) "new.2"
+ [4]=>
+ string(5) "new.1"
+ [5]=>
+ string(5) "new.0"
+ [6]=>
+ string(3) "v.0"
}
---( Array with 2 element(s): )---
|