summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorDmitry Stogov <dmitry@zend.com>2015-01-29 21:05:02 +0300
committerDmitry Stogov <dmitry@zend.com>2015-01-29 21:05:02 +0300
commit15a23b1218b3e38630d677751a975907daa2cd54 (patch)
tree0d15452a3c633f65c6529cc7088704e7a229d452
parent10a3260b1f16b6075fd8140f673dfef4d5efea91 (diff)
downloadphp-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.c10
-rw-r--r--Zend/zend_compile.h3
-rw-r--r--Zend/zend_execute.c8
-rw-r--r--Zend/zend_execute_API.c10
-rw-r--r--Zend/zend_generators.c6
-rw-r--r--Zend/zend_globals.h5
-rw-r--r--Zend/zend_hash.c147
-rw-r--r--Zend/zend_hash.h9
-rw-r--r--Zend/zend_types.h16
-rw-r--r--Zend/zend_vm_def.h76
-rw-r--r--Zend/zend_vm_execute.h123
-rw-r--r--Zend/zend_vm_opcodes.c2
-rw-r--r--Zend/zend_vm_opcodes.h1
-rw-r--r--ext/opcache/Optimizer/block_pass.c6
-rw-r--r--ext/opcache/Optimizer/optimize_temp_vars_5.c26
-rw-r--r--ext/opcache/Optimizer/pass2.c4
-rw-r--r--ext/standard/array.c8
-rw-r--r--tests/lang/foreachLoop.013.phpt59
-rw-r--r--tests/lang/foreachLoop.015.phpt59
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): )---