diff options
author | Rasmus Lerdorf <rasmus@lerdorf.com> | 2015-01-30 22:57:40 -0800 |
---|---|---|
committer | Rasmus Lerdorf <rasmus@lerdorf.com> | 2015-01-30 22:57:40 -0800 |
commit | 74b85316ea06c17256e102483daa472f4b638221 (patch) | |
tree | abe0618e15fcfc93158b4239889485ffc70c4c1c | |
parent | 130d7320b160443ca160ee6b3a19a034ee2c5ef1 (diff) | |
parent | 1e41295097576dbce6c197ddb7507c07ccae3cbe (diff) | |
download | php-git-dstogov-foreach.tar.gz |
Merge branch 'foreach' of https://github.com/dstogov/php-src into dstogov-foreachdstogov-foreach
48 files changed, 2828 insertions, 1457 deletions
diff --git a/Zend/tests/bug40509.phpt b/Zend/tests/bug40509.phpt index 21eaae9444..65e32533ef 100644 --- a/Zend/tests/bug40509.phpt +++ b/Zend/tests/bug40509.phpt @@ -23,4 +23,4 @@ var_dump(key($arr["v"])); int(0) int(0) int(0) -NULL +int(0) diff --git a/Zend/tests/bug40705.phpt b/Zend/tests/bug40705.phpt index 374f73b75e..8a679654d5 100644 --- a/Zend/tests/bug40705.phpt +++ b/Zend/tests/bug40705.phpt @@ -23,4 +23,4 @@ int(0) int(0) int(1) int(2) -NULL +int(0) diff --git a/Zend/tests/foreach_003.phpt b/Zend/tests/foreach_003.phpt new file mode 100644 index 0000000000..71b0f2a5a3 --- /dev/null +++ b/Zend/tests/foreach_003.phpt @@ -0,0 +1,71 @@ +--TEST-- +Iterator exceptions in foreach by value +--FILE-- +<?php +class IT implements Iterator { + private $n = 0; + private $count = 0; + private $trap = null; + + function __construct($count, $trap = null) { + $this->count = $count; + $this->trap = $trap; + } + + function trap($trap) { + if ($trap === $this->trap) { + throw new Exception($trap); + } + } + + function rewind() {$this->trap(__FUNCTION__); $this->n = 0;} + function valid() {$this->trap(__FUNCTION__); return $this->n < $this->count;} + function key() {$this->trap(__FUNCTION__); return $this->n;} + function current() {$this->trap(__FUNCTION__); return $this->n;} + function next() {$this->trap(__FUNCTION__); $this->n++;} +} + +foreach(['rewind', 'valid', 'key', 'current', 'next'] as $trap) { + $obj = new IT(3, $trap); + try { + // IS_CV + foreach ($obj as $key => $val) echo "$val\n"; + } catch (Exception $e) { + echo $e->getMessage() . "\n"; + } + unset($obj); + + try { + // IS_VAR + foreach (new IT(3, $trap) as $key => $val) echo "$val\n"; + } catch (Exception $e) { + echo $e->getMessage() . "\n"; + } + + try { + // IS_TMP_VAR + foreach ((object)new IT(2, $trap) as $key => $val) echo "$val\n"; + } catch (Exception $e) { + echo $e->getMessage() . "\n"; + } +} +?> +--EXPECT-- +rewind +rewind +rewind +valid +valid +valid +key +key +key +current +current +current +0 +next +0 +next +0 +next diff --git a/Zend/tests/foreach_004.phpt b/Zend/tests/foreach_004.phpt new file mode 100644 index 0000000000..1f754a77ed --- /dev/null +++ b/Zend/tests/foreach_004.phpt @@ -0,0 +1,65 @@ +--TEST-- +Iterator exceptions in foreach by reference +--FILE-- +<?php +class IT extends ArrayIterator { + private $n = 0; + + function __construct($trap = null) { + parent::__construct([0, 1]); + $this->trap = $trap; + } + + function trap($trap) { + if ($trap === $this->trap) { + throw new Exception($trap); + } + } + + function rewind() {$this->trap(__FUNCTION__); return parent::rewind();} + function valid() {$this->trap(__FUNCTION__); return parent::valid();} + function key() {$this->trap(__FUNCTION__); return parent::key();} + function next() {$this->trap(__FUNCTION__); return parent::next();} +} + +foreach(['rewind', 'valid', 'key', 'next'] as $trap) { + $obj = new IT($trap); + try { + // IS_CV + foreach ($obj as $key => &$val) echo "$val\n"; + } catch (Exception $e) { + echo $e->getMessage() . "\n"; + } + unset($obj); + + try { + // IS_VAR + foreach (new IT($trap) as $key => &$val) echo "$val\n"; + } catch (Exception $e) { + echo $e->getMessage() . "\n"; + } + + try { + // IS_TMP_VAR + foreach ((object)new IT($trap) as $key => &$val) echo "$val\n"; + } catch (Exception $e) { + echo $e->getMessage() . "\n"; + } +} +?> +--EXPECT-- +rewind +rewind +rewind +valid +valid +valid +key +key +key +0 +next +0 +next +0 +next diff --git a/Zend/tests/foreach_005.phpt b/Zend/tests/foreach_005.phpt new file mode 100644 index 0000000000..6ed9fe940b --- /dev/null +++ b/Zend/tests/foreach_005.phpt @@ -0,0 +1,22 @@ +--TEST-- +Nested foreach by reference on the same array +--FILE-- +<?php +$a = [1,2,3]; +foreach($a as &$x) { + foreach($a as &$y) { + echo "$x-$y\n"; + $y++; + } +} +?> +--EXPECT-- +1-1 +2-2 +2-3 +3-2 +3-3 +4-4 +5-3 +5-4 +5-5 diff --git a/Zend/tests/foreach_006.phpt b/Zend/tests/foreach_006.phpt new file mode 100644 index 0000000000..65d6fdc52c --- /dev/null +++ b/Zend/tests/foreach_006.phpt @@ -0,0 +1,20 @@ +--TEST-- +Foreach by reference on constant +--FILE-- +<?php +for ($i = 0; $i < 3; $i++) { + foreach ([1,2,3] as &$val) { + echo "$val\n"; + } +} +?> +--EXPECT-- +1 +2 +3 +1 +2 +3 +1 +2 +3 diff --git a/Zend/tests/foreach_007.phpt b/Zend/tests/foreach_007.phpt new file mode 100644 index 0000000000..b99bc73ebe --- /dev/null +++ b/Zend/tests/foreach_007.phpt @@ -0,0 +1,13 @@ +--TEST-- +Foreach by reference and inserting new element when we are already at the end +--FILE-- +<?php +$a = [1]; +foreach($a as &$v) { + echo "$v\n"; + $a[1]=2; +} +?> +--EXPECT-- +1 +2 diff --git a/Zend/tests/foreach_008.phpt b/Zend/tests/foreach_008.phpt new file mode 100644 index 0000000000..c68bcd89da --- /dev/null +++ b/Zend/tests/foreach_008.phpt @@ -0,0 +1,21 @@ +--TEST-- +Nested foreach by reference and array modification +--FILE-- +<?php +$a = [0, 1, 2, 3]; +foreach ($a as &$x) { + foreach ($a as &$y) { + echo "$x - $y\n"; + if ($x == 0 && $y == 1) { + unset($a[2]); + unset($a[1]); + } + } +} +?> +--EXPECT-- +0 - 0 +0 - 1 +0 - 3 +3 - 0 +3 - 3 diff --git a/Zend/tests/foreach_009.phpt b/Zend/tests/foreach_009.phpt new file mode 100644 index 0000000000..6ce8384642 --- /dev/null +++ b/Zend/tests/foreach_009.phpt @@ -0,0 +1,40 @@ +--TEST-- +Nested foreach by reference and array modification with resize +--FILE-- +<?php +$a = [0, 1, 2, 3, 4, 5, 6, 7]; +unset($a[0], $a[1], $a[2], $a[3]); +foreach ($a as &$ref) { + foreach ($a as &$ref2) { + echo "$ref-$ref2\n"; + if ($ref == 5 && $ref2 == 6) { + $a[42] = 8; + } + } +} +?> +--EXPECT-- +4-4 +4-5 +4-6 +4-7 +5-4 +5-5 +5-6 +5-7 +5-8 +6-4 +6-5 +6-6 +6-7 +6-8 +7-4 +7-5 +7-6 +7-7 +7-8 +8-4 +8-5 +8-6 +8-7 +8-8 diff --git a/Zend/tests/foreach_010.phpt b/Zend/tests/foreach_010.phpt new file mode 100644 index 0000000000..6ba7e7e9fd --- /dev/null +++ b/Zend/tests/foreach_010.phpt @@ -0,0 +1,40 @@ +--TEST-- +Nested foreach by value over object and object modification with resize +--FILE-- +<?php +$o = (object)['a'=>0, 'b'=>1, 'c'=>2, 'd'=>3, 'e'=>4, 'f'=>5, 'g'=>6, 'h'=>7]; +unset($o->a, $o->b, $o->c, $o->d); +foreach ($o as $v1) { + foreach ($o as $v2) { + echo "$v1-$v2\n"; + if ($v1 == 5 && $v2 == 6) { + $o->i = 8; + } + } +} +?> +--EXPECT-- +4-4 +4-5 +4-6 +4-7 +5-4 +5-5 +5-6 +5-7 +5-8 +6-4 +6-5 +6-6 +6-7 +6-8 +7-4 +7-5 +7-6 +7-7 +7-8 +8-4 +8-5 +8-6 +8-7 +8-8 diff --git a/Zend/tests/foreach_011.phpt b/Zend/tests/foreach_011.phpt new file mode 100644 index 0000000000..e91426fb27 --- /dev/null +++ b/Zend/tests/foreach_011.phpt @@ -0,0 +1,19 @@ +--TEST-- +sort() functions precerve foreach by reference iterator pointer +--FILE-- +<?php +$a = [1,2,3,4,5,0]; +foreach($a as &$v) { + echo "$v\n"; + if ($v == 3) { + rsort($a); + } +} +?> +--EXPECT-- +1 +2 +3 +2 +1 +0 diff --git a/Zend/tests/foreach_012.phpt b/Zend/tests/foreach_012.phpt new file mode 100644 index 0000000000..5e5538cd4d --- /dev/null +++ b/Zend/tests/foreach_012.phpt @@ -0,0 +1,18 @@ +--TEST-- +array_walk() function precerve foreach by reference iterator pointer +--FILE-- +<?php +$a = [1,2,3,4,5]; +foreach($a as &$v) { + echo "$v\n"; + if ($v == 3) { + array_walk($a, function (&$x) {$x+=10;}); + } +} +?> +--EXPECT-- +1 +2 +3 +14 +15
\ No newline at end of file diff --git a/Zend/tests/foreach_013.phpt b/Zend/tests/foreach_013.phpt new file mode 100644 index 0000000000..cfbb3d7f79 --- /dev/null +++ b/Zend/tests/foreach_013.phpt @@ -0,0 +1,17 @@ +--TEST-- +array_push() function precerve foreach by reference iterator pointer +--FILE-- +<?php +$a = [1,2,3]; +foreach($a as &$v) { + echo "$v\n"; + if ($v == 3) { + array_push($a, 4); + } +} +?> +--EXPECT-- +1 +2 +3 +4 diff --git a/Zend/tests/foreach_014.phpt b/Zend/tests/foreach_014.phpt new file mode 100644 index 0000000000..8d0ac582a9 --- /dev/null +++ b/Zend/tests/foreach_014.phpt @@ -0,0 +1,15 @@ +--TEST-- +array_pop() function precerve foreach by reference iterator pointer +--FILE-- +<?php +$a = [1,2,3]; +foreach($a as &$v) { + echo "$v\n"; + if ($v == 2) { + array_pop($a); + } +} +?> +--EXPECT-- +1 +2 diff --git a/Zend/tests/foreach_015.phpt b/Zend/tests/foreach_015.phpt new file mode 100644 index 0000000000..adc8085f34 --- /dev/null +++ b/Zend/tests/foreach_015.phpt @@ -0,0 +1,18 @@ +--TEST-- +array_shift() function precerve foreach by reference iterator pointer +--FILE-- +<?php +$a = [1,2,3,4]; +foreach($a as &$v) { + echo "$v\n"; + array_shift($a); +} +var_dump($a); +?> +--EXPECT-- +1 +2 +3 +4 +array(0) { +}
\ No newline at end of file diff --git a/Zend/tests/foreach_016.phpt b/Zend/tests/foreach_016.phpt new file mode 100644 index 0000000000..423c8dd0a6 --- /dev/null +++ b/Zend/tests/foreach_016.phpt @@ -0,0 +1,18 @@ +--TEST-- +array_unshift() function precerve foreach by reference iterator pointer +--FILE-- +<?php +$a = [1,2,3]; +foreach($a as &$v) { + echo "$v\n"; + if ($v == 2) { + array_unshift($a, 0, 0, 0, 0, 0, 0, 0, 0); + } +} +var_dump(count($a)); +?> +--EXPECT-- +1 +2 +3 +int(11) diff --git a/Zend/tests/foreach_017.phpt b/Zend/tests/foreach_017.phpt new file mode 100644 index 0000000000..55eeeb0891 --- /dev/null +++ b/Zend/tests/foreach_017.phpt @@ -0,0 +1,111 @@ +--TEST-- +array_splice() function precerve foreach by reference iterator pointer +--FILE-- +<?php +/* remove before */ +$done = 0; +$a = [0,1,2,3,4]; +foreach($a as &$v) { + echo "$v\n"; + if (!$done && $v == 3) { + $done = 1; + array_splice($a, 1, 2); + } +} +echo "\n"; + +/* remove after */ +$done = 0; +$a = [0,1,2,3,4]; +foreach($a as &$v) { + echo "$v\n"; + if (!$done && $v == 0) { + $done = 1; + array_splice($a, 2, 2); + } +} +echo "\n"; + +/* remove current */ +$done = 0; +$a = [0,1,2,3,4]; +foreach($a as &$v) { + echo "$v\n"; + if (!$done && $v == 2) { + $done = 1; + array_splice($a, 1, 3); + } +} +echo "\n"; + +$replacement = ['x', 'y', 'z']; + +/* replace before */ +$done = 0; +$a = [0,1,2,3,4]; +foreach($a as &$v) { + echo "$v\n"; + if ($done && $v == 3) { + $done = 1; + array_splice($a, 1, 2, $replacement); + } +} +echo "\n"; + +/* replace after */ +$done = 0; +$a = [0,1,2,3,4]; +foreach($a as &$v) { + echo "$v\n"; + if (!$done && $v == 0) { + $done = 1; + array_splice($a, 2, 2, $replacement); + } +} +echo "\n"; + +/* replace current */ +$done = 0; +$a = [0,1,2,3,4]; +foreach($a as &$v) { + echo "$v\n"; + if (!$done && $v == 2) { + $done = 1; + array_splice($a, 1, 3, $replacement); + } +} +echo "\n"; +?> +--EXPECT-- +0 +1 +2 +3 +4 + +0 +1 +4 + +0 +1 +2 +4 + +0 +1 +2 +3 +4 + +0 +1 +x +y +z +4 + +0 +1 +2 +4 diff --git a/Zend/zend_compile.c b/Zend/zend_compile.c index 7476d475de..79293971e8 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); } @@ -3396,32 +3396,19 @@ void zend_compile_foreach(zend_ast *ast) /* {{{ */ } opnum_reset = get_next_op_number(CG(active_op_array)); - opline = zend_emit_op(&reset_node, ZEND_FE_RESET, &expr_node, NULL); - if (by_ref && is_variable) { - opline->extended_value = ZEND_FE_FETCH_BYREF; - } + opline = zend_emit_op(&reset_node, by_ref ? ZEND_FE_RESET_RW : ZEND_FE_RESET_R, &expr_node, NULL); + reset_node.flag = 1; /* generate FE_FREE */ zend_stack_push(&CG(loop_var_stack), &reset_node); opnum_fetch = get_next_op_number(CG(active_op_array)); - opline = zend_emit_op(&value_node, ZEND_FE_FETCH, &reset_node, NULL); - if (by_ref) { - opline->extended_value |= ZEND_FE_FETCH_BYREF; - } + opline = zend_emit_op(&value_node, by_ref ? ZEND_FE_FETCH_RW : ZEND_FE_FETCH_R, &reset_node, NULL); if (key_ast) { - opline->extended_value |= ZEND_FE_FETCH_WITH_KEY; + opline->extended_value = 1; } opline = zend_emit_op(NULL, ZEND_OP_DATA, NULL, NULL); - /* Allocate enough space to keep HashPointer on VM stack */ - opline->op1_type = IS_TMP_VAR; - opline->op1.var = get_temporary_variable(CG(active_op_array)); - if (sizeof(HashPointer) > sizeof(zval)) { - /* Make sure 1 zval is enough for HashPointer (2 must be enough) */ - get_temporary_variable(CG(active_op_array)); - } - if (key_ast) { zend_make_tmp_result(&key_node, opline); } @@ -3512,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 7c82c67d6d..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 */ @@ -828,9 +829,6 @@ int zend_add_literal(zend_op_array *op_array, zval *zv); #define ZEND_FETCH_ARG_MASK 0x000fffff -#define ZEND_FE_FETCH_BYREF 1 -#define ZEND_FE_FETCH_WITH_KEY 2 - #define EXT_TYPE_FREE_ON_RETURN (1<<2) #define ZEND_MEMBER_FUNC_CALL 1<<0 diff --git a/Zend/zend_execute.c b/Zend/zend_execute.c index 7a79defb05..1a81c298a8 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_TYPE_P(var) != IS_ARRAY && 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..971d4e7bc8 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_TYPE_P(var) != IS_ARRAY && 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..195b5e48f0 100644 --- a/Zend/zend_hash.c +++ b/Zend/zend_hash.c @@ -193,6 +193,140 @@ ZEND_API void zend_hash_set_apply_protection(HashTable *ht, zend_bool bApplyProt } } +ZEND_API uint32_t zend_hash_iterator_add(HashTable *ht, HashPosition pos) +{ + 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 = pos; + 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 = pos; + 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 _zend_hash_iterators_remove(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 zend_hash_iterators_remove(HashTable *ht) +{ + if (UNEXPECTED(ht->u.v.nIteratorsCount)) { + _zend_hash_iterators_remove(ht); + } +} + +ZEND_API HashPosition zend_hash_iterators_lower_pos(HashTable *ht, HashPosition start) +{ + HashTableIterator *iter = EG(ht_iterators); + HashTableIterator *end = iter + EG(ht_iterators_used); + HashPosition res = INVALID_IDX; + uint32_t idx; + + while (iter != end) { + if (iter->ht == ht) { + if (iter->pos >= start && iter->pos < res) { + res = iter->pos; + } + } + iter++; + } + return res; +} + +ZEND_API void _zend_hash_iterators_update(HashTable *ht, HashPosition from, HashPosition to) +{ + HashTableIterator *iter = EG(ht_iterators); + HashTableIterator *end = iter + EG(ht_iterators_used); + + while (iter != end) { + if (iter->ht == ht && iter->pos == from) { + iter->pos = to; + } + iter++; + } +} + static zend_always_inline Bucket *zend_hash_find_bucket(const HashTable *ht, zend_string *key) { zend_ulong h; @@ -305,6 +439,7 @@ add_to_hash: if (ht->nInternalPointer == INVALID_IDX) { ht->nInternalPointer = idx; } + zend_hash_iterators_update(ht, INVALID_IDX, idx); p = ht->arData + idx; p->h = h = zend_string_hash_val(key); p->key = key; @@ -472,6 +607,7 @@ add_to_packed: if (ht->nInternalPointer == INVALID_IDX) { ht->nInternalPointer = h; } + zend_hash_iterators_update(ht, INVALID_IDX, h); if ((zend_long)h >= (zend_long)ht->nNextFreeElement) { ht->nNextFreeElement = h < ZEND_LONG_MAX ? h + 1 : ZEND_LONG_MAX; } @@ -514,6 +650,7 @@ add_to_hash: if (ht->nInternalPointer == INVALID_IDX) { ht->nInternalPointer = idx; } + zend_hash_iterators_update(ht, INVALID_IDX, idx); if ((zend_long)h >= (zend_long)ht->nNextFreeElement) { ht->nNextFreeElement = h < ZEND_LONG_MAX ? h + 1 : ZEND_LONG_MAX; } @@ -594,19 +731,42 @@ ZEND_API int zend_hash_rehash(HashTable *ht) } memset(ht->arHash, INVALID_IDX, ht->nTableSize * sizeof(uint32_t)); - for (i = 0, j = 0; i < ht->nNumUsed; i++) { - p = ht->arData + i; - if (Z_TYPE(p->val) == IS_UNDEF) continue; - if (i != j) { - ht->arData[j] = ht->arData[i]; - if (ht->nInternalPointer == i) { - ht->nInternalPointer = j; + if (EXPECTED(ht->u.v.nIteratorsCount == 0)) { + for (i = 0, j = 0; i < ht->nNumUsed; i++) { + p = ht->arData + i; + if (Z_TYPE(p->val) == IS_UNDEF) continue; + if (i != j) { + ht->arData[j] = ht->arData[i]; + if (ht->nInternalPointer == i) { + ht->nInternalPointer = j; + } + } + nIndex = ht->arData[j].h & ht->nTableMask; + Z_NEXT(ht->arData[j].val) = ht->arHash[nIndex]; + ht->arHash[nIndex] = j; + j++; + } + } else { + uint32_t iter_pos = zend_hash_iterators_lower_pos(ht, 0); + + for (i = 0, j = 0; i < ht->nNumUsed; i++) { + p = ht->arData + i; + if (Z_TYPE(p->val) == IS_UNDEF) continue; + if (i != j) { + ht->arData[j] = ht->arData[i]; + if (ht->nInternalPointer == i) { + ht->nInternalPointer = j; + } + if (i == iter_pos) { + zend_hash_iterators_update(ht, i, j); + iter_pos = zend_hash_iterators_lower_pos(ht, iter_pos + 1); + } } + nIndex = ht->arData[j].h & ht->nTableMask; + Z_NEXT(ht->arData[j].val) = ht->arHash[nIndex]; + ht->arHash[nIndex] = j; + j++; } - nIndex = ht->arData[j].h & ht->nTableMask; - Z_NEXT(ht->arData[j].val) = ht->arHash[nIndex]; - ht->arHash[nIndex] = j; - j++; } ht->nNumUsed = j; return SUCCESS; @@ -628,17 +788,22 @@ static zend_always_inline void _zend_hash_del_el_ex(HashTable *ht, uint32_t idx, } while (ht->nNumUsed > 0 && (Z_TYPE(ht->arData[ht->nNumUsed-1].val) == IS_UNDEF)); } ht->nNumOfElements--; - if (ht->nInternalPointer == idx) { + if (ht->nInternalPointer == idx || UNEXPECTED(ht->u.v.nIteratorsCount)) { + uint32_t new_idx = idx; + while (1) { - idx++; - if (idx >= ht->nNumUsed) { - ht->nInternalPointer = INVALID_IDX; + new_idx++; + if (new_idx >= ht->nNumUsed) { + new_idx = INVALID_IDX; break; - } else if (Z_TYPE(ht->arData[idx].val) != IS_UNDEF) { - ht->nInternalPointer = idx; + } else if (Z_TYPE(ht->arData[new_idx].val) != IS_UNDEF) { break; } } + if (ht->nInternalPointer == idx) { + ht->nInternalPointer = new_idx; + } + zend_hash_iterators_update(ht, idx, new_idx); } if (p->key) { zend_string_release(p->key); @@ -893,6 +1058,7 @@ ZEND_API void zend_hash_destroy(HashTable *ht) } while (++p != end); } } + zend_hash_iterators_remove(ht); } else if (EXPECTED(!(ht->u.flags & HASH_FLAG_INITIALIZED))) { return; } @@ -933,7 +1099,7 @@ ZEND_API void zend_array_destroy(HashTable *ht) } } while (++p != end); } - + zend_hash_iterators_remove(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 377f508658..602cd7a2e4 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 */ @@ -171,13 +169,6 @@ ZEND_API zval *zend_hash_get_current_data_ex(HashTable *ht, HashPosition *pos); ZEND_API void zend_hash_internal_pointer_reset_ex(HashTable *ht, HashPosition *pos); ZEND_API void zend_hash_internal_pointer_end_ex(HashTable *ht, HashPosition *pos); -typedef struct _HashPointer { - HashPosition pos; - HashTable *ht; - zend_ulong h; - zend_string *key; -} HashPointer; - #define zend_hash_has_more_elements(ht) \ zend_hash_has_more_elements_ex(ht, &(ht)->nInternalPointer) #define zend_hash_move_forward(ht) \ @@ -234,6 +225,21 @@ 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, HashPosition pos); +ZEND_API HashPosition zend_hash_iterator_pos(uint32_t idx, HashTable *ht); +ZEND_API void zend_hash_iterator_del(uint32_t idx); +ZEND_API HashPosition zend_hash_iterators_lower_pos(HashTable *ht, HashPosition start); +ZEND_API void _zend_hash_iterators_update(HashTable *ht, HashPosition from, HashPosition to); + +static zend_always_inline void zend_hash_iterators_update(HashTable *ht, HashPosition from, HashPosition to) +{ + if (UNEXPECTED(ht->u.v.nIteratorsCount)) { + _zend_hash_iterators_update(ht, from, to); + } +} + + END_EXTERN_C() #define ZEND_INIT_SYMTABLE(ht) \ diff --git a/Zend/zend_opcode.c b/Zend/zend_opcode.c index a6d041f131..c1e3adb3ec 100644 --- a/Zend/zend_opcode.c +++ b/Zend/zend_opcode.c @@ -766,8 +766,10 @@ ZEND_API int pass_two(zend_op_array *op_array) case ZEND_JMP_SET: case ZEND_COALESCE: case ZEND_NEW: - case ZEND_FE_RESET: - case ZEND_FE_FETCH: + case ZEND_FE_RESET_R: + case ZEND_FE_RESET_RW: + case ZEND_FE_FETCH_R: + case ZEND_FE_FETCH_RW: ZEND_PASS_TWO_UPDATE_JMP_TARGET(op_array, opline, opline->op2); break; case ZEND_VERIFY_RETURN_TYPE: diff --git a/Zend/zend_types.h b/Zend/zend_types.h index 5c00cb133f..dbe8c616df 100644 --- a/Zend/zend_types.h +++ b/Zend/zend_types.h @@ -128,6 +128,8 @@ struct _zval_struct { uint32_t cache_slot; /* literal cache slot */ 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; }; @@ -160,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; @@ -178,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; @@ -261,6 +271,12 @@ static zend_always_inline zend_uchar zval_get_type(const zval* pz) { #define Z_CACHE_SLOT(zval) (zval).u2.cache_slot #define Z_CACHE_SLOT_P(zval_p) Z_CACHE_SLOT(*(zval_p)) +#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 24bb3fd3d6..80f7ef3c14 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_TYPE_P(var) != IS_ARRAY && 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_TYPE_P(var) != IS_ARRAY && 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)); } @@ -4620,292 +4643,505 @@ ZEND_VM_HANDLER(76, ZEND_UNSET_OBJ, VAR|UNUSED|CV, CONST|TMPVAR|CV) ZEND_VM_NEXT_OPCODE(); } -ZEND_VM_HANDLER(77, ZEND_FE_RESET, CONST|TMP|VAR|CV, ANY) +ZEND_VM_HANDLER(77, ZEND_FE_RESET_R, CONST|TMP|VAR|CV, ANY) { USE_OPLINE zend_free_op free_op1; - zval *array_ptr, *array_ref, iterator, tmp; + zval *array_ptr, *result; HashTable *fe_ht; - zend_object_iterator *iter = NULL; - zend_class_entry *ce = NULL; - zend_bool is_empty = 0; SAVE_OPLINE(); - if ((OP1_TYPE == IS_CV || OP1_TYPE == IS_VAR) && - (opline->extended_value & ZEND_FE_FETCH_BYREF)) { - array_ptr = array_ref = GET_OP1_ZVAL_PTR_PTR(BP_VAR_R); - ZVAL_DEREF(array_ptr); - if (Z_TYPE_P(array_ptr) == IS_ARRAY) { - SEPARATE_ARRAY(array_ptr); - if (!Z_ISREF_P(array_ref)) { - ZVAL_NEW_REF(array_ref, array_ref); - array_ptr = Z_REFVAL_P(array_ref); - } - if (Z_REFCOUNTED_P(array_ref)) Z_ADDREF_P(array_ref); - } else if (Z_TYPE_P(array_ptr) == IS_OBJECT) { - ce = Z_OBJCE_P(array_ptr); - if (ce->get_iterator == NULL) { + array_ptr = GET_OP1_ZVAL_PTR_DEREF(BP_VAR_R); + if (EXPECTED(Z_TYPE_P(array_ptr) == IS_ARRAY)) { + result = EX_VAR(opline->result.var); + ZVAL_COPY_VALUE(result, array_ptr); + if (OP1_TYPE != IS_TMP_VAR && Z_OPT_REFCOUNTED_P(result)) { + Z_ADDREF_P(array_ptr); + } + Z_FE_POS_P(result) = 0; + + FREE_OP1_IF_VAR(); + CHECK_EXCEPTION(); + ZEND_VM_NEXT_OPCODE(); + } else if (OP1_TYPE != IS_CONST && EXPECTED(Z_TYPE_P(array_ptr) == IS_OBJECT)) { + if (!Z_OBJCE_P(array_ptr)->get_iterator) { + HashPosition pos = 0; + Bucket *p; + + result = EX_VAR(opline->result.var); + ZVAL_COPY_VALUE(result, array_ptr); + if (OP1_TYPE != IS_TMP_VAR) { Z_ADDREF_P(array_ptr); } - array_ref = array_ptr; + fe_ht = Z_OBJPROP_P(array_ptr); + pos = 0; + while (1) { + if (pos >= fe_ht->nNumUsed) { + FREE_OP1_IF_VAR(); + 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; + if ((Z_TYPE(p->val) != IS_UNDEF && + (Z_TYPE(p->val) != IS_INDIRECT || + Z_TYPE_P(Z_INDIRECT(p->val)) != IS_UNDEF)) && + (!p->key || + zend_check_property_access(Z_OBJ_P(array_ptr), p->key) == SUCCESS)) { + break; + } + pos++; + } + fe_ht->nInternalPointer = pos; + Z_FE_ITER_P(EX_VAR(opline->result.var)) = zend_hash_iterator_add(fe_ht, pos); + + FREE_OP1_IF_VAR(); + CHECK_EXCEPTION(); + ZEND_VM_NEXT_OPCODE(); } else { - if (Z_REFCOUNTED_P(array_ref)) Z_ADDREF_P(array_ref); - } - } else { - array_ptr = array_ref = GET_OP1_ZVAL_PTR(BP_VAR_R); - if (OP1_TYPE & (IS_VAR|IS_CV)) { - ZVAL_DEREF(array_ptr); - } - if (OP1_TYPE == IS_TMP_VAR) { - ZVAL_COPY_VALUE(&tmp, array_ptr); - if (Z_OPT_IMMUTABLE_P(&tmp)) { - zval_copy_ctor_func(&tmp); - } - array_ref = array_ptr = &tmp; - if (Z_TYPE_P(array_ptr) == IS_OBJECT) { - ce = Z_OBJCE_P(array_ptr); - if (ce && ce->get_iterator) { - Z_DELREF_P(array_ref); + zend_class_entry *ce = Z_OBJCE_P(array_ptr); + zend_object_iterator *iter = ce->get_iterator(ce, array_ptr, 0); + zend_bool is_empty; + + if (UNEXPECTED(!iter) || UNEXPECTED(EG(exception))) { + FREE_OP1(); + if (!EG(exception)) { + zend_throw_exception_ex(NULL, 0, "Object of type %s did not create an Iterator", ce->name->val); } + zend_throw_exception_internal(NULL); + HANDLE_EXCEPTION(); } - } else if (Z_TYPE_P(array_ptr) == IS_OBJECT) { - ce = Z_OBJCE_P(array_ptr); - if (!ce->get_iterator) { - if (OP1_TYPE == IS_CV) { - Z_ADDREF_P(array_ref); + + iter->index = 0; + if (iter->funcs->rewind) { + iter->funcs->rewind(iter); + if (UNEXPECTED(EG(exception) != NULL)) { + OBJ_RELEASE(&iter->std); + FREE_OP1(); + HANDLE_EXCEPTION(); } } - } else if (Z_IMMUTABLE_P(array_ref)) { - if (OP1_TYPE == IS_CV) { - zval_copy_ctor_func(array_ref); - Z_ADDREF_P(array_ref); + + is_empty = iter->funcs->valid(iter) != SUCCESS; + + if (UNEXPECTED(EG(exception) != NULL)) { + OBJ_RELEASE(&iter->std); + FREE_OP1(); + HANDLE_EXCEPTION(); + } + iter->index = -1; /* will be set to 0 before using next handler */ + + ZVAL_OBJ(EX_VAR(opline->result.var), &iter->std); + Z_FE_ITER_P(EX_VAR(opline->result.var)) = (uint32_t)-1; + + FREE_OP1(); + if (is_empty) { + ZEND_VM_JMP(OP_JMP_ADDR(opline, opline->op2)); } else { - ZVAL_COPY_VALUE(&tmp, array_ref); - zval_copy_ctor_func(&tmp); - array_ptr = array_ref = &tmp; - } - } else if (Z_REFCOUNTED_P(array_ref)) { - if (OP1_TYPE == IS_CONST || - (OP1_TYPE == IS_CV && - !Z_ISREF_P(array_ref) && - Z_REFCOUNT_P(array_ref) > 1) || - (OP1_TYPE == IS_VAR && - !Z_ISREF_P(array_ref) && - Z_REFCOUNT_P(array_ref) > 2)) { - if (OP1_TYPE == IS_VAR) { - Z_DELREF_P(array_ref); - } - ZVAL_DUP(&tmp, array_ref); - array_ptr = array_ref = &tmp; - } else if (OP1_TYPE == IS_CV || OP1_TYPE == IS_VAR) { - if (Z_ISREF_P(array_ref) && Z_REFCOUNT_P(array_ref) == 1) { - ZVAL_UNREF(array_ref); - array_ptr = array_ref; - } - if (Z_IMMUTABLE_P(array_ptr)) { - zval_copy_ctor_func(array_ptr); - } else if (Z_ISREF_P(array_ref) && - Z_COPYABLE_P(array_ptr) && - Z_REFCOUNT_P(array_ptr) > 1) { - Z_DELREF_P(array_ptr); - zval_copy_ctor_func(array_ptr); - } - if (OP1_TYPE == IS_CV) { - Z_ADDREF_P(array_ref); - } + 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; + FREE_OP1(); + ZEND_VM_JMP(OP_JMP_ADDR(opline, opline->op2)); } +} + +ZEND_VM_HANDLER(125, ZEND_FE_RESET_RW, CONST|TMP|VAR|CV, ANY) +{ + USE_OPLINE + zend_free_op free_op1; + zval *array_ptr, *array_ref; + HashTable *fe_ht; + HashPosition pos = 0; + Bucket *p; - if (ce && ce->get_iterator) { - iter = ce->get_iterator(ce, array_ptr, opline->extended_value & ZEND_FE_FETCH_BYREF); + SAVE_OPLINE(); - if (OP1_TYPE == IS_VAR && !(opline->extended_value & ZEND_FE_FETCH_BYREF)) { - FREE_OP1_IF_VAR(); + if (OP1_TYPE == IS_VAR || OP1_TYPE == IS_CV) { + array_ref = array_ptr = GET_OP1_ZVAL_PTR_PTR(BP_VAR_R); + if (Z_ISREF_P(array_ref)) { + array_ptr = Z_REFVAL_P(array_ref); + } + } else { + array_ref = array_ptr = GET_OP1_ZVAL_PTR(BP_VAR_R); + } + + if (EXPECTED(Z_TYPE_P(array_ptr) == IS_ARRAY)) { + if (OP1_TYPE == IS_VAR || OP1_TYPE == IS_CV) { + if (array_ptr == array_ref) { + ZVAL_NEW_REF(array_ref, array_ref); + array_ptr = Z_REFVAL_P(array_ref); + } + Z_ADDREF_P(array_ref); + ZVAL_COPY_VALUE(EX_VAR(opline->result.var), array_ref); + } else { + array_ptr = EX_VAR(opline->result.var); + ZVAL_COPY_VALUE(array_ptr, array_ref); } - if (iter && EXPECTED(EG(exception) == NULL)) { - ZVAL_OBJ(&iterator, &iter->std); - array_ptr = array_ref = &iterator; + if (OP1_TYPE == IS_CONST) { + zval_copy_ctor_func(array_ptr); } else { - if (OP1_TYPE == IS_VAR && opline->extended_value & ZEND_FE_FETCH_BYREF) { + SEPARATE_ARRAY(array_ptr); + } + fe_ht = Z_ARRVAL_P(array_ptr); + 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)); } - if (!EG(exception)) { - zend_throw_exception_ex(NULL, 0, "Object of type %s did not create an Iterator", ce->name->val); + 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)) { + break; } - zend_throw_exception_internal(NULL); - HANDLE_EXCEPTION(); + pos++; } - } + fe_ht->nInternalPointer = pos; + Z_FE_ITER_P(EX_VAR(opline->result.var)) = zend_hash_iterator_add(fe_ht, pos); + + FREE_OP1_VAR_PTR(); + CHECK_EXCEPTION(); + ZEND_VM_NEXT_OPCODE(); + } else if (OP1_TYPE != IS_CONST && EXPECTED(Z_TYPE_P(array_ptr) == IS_OBJECT)) { + if (!Z_OBJCE_P(array_ptr)->get_iterator) { + if (OP1_TYPE == IS_VAR || OP1_TYPE == IS_CV) { + if (array_ptr == array_ref) { + ZVAL_NEW_REF(array_ref, array_ref); + array_ptr = Z_REFVAL_P(array_ref); + } + Z_ADDREF_P(array_ref); + ZVAL_COPY_VALUE(EX_VAR(opline->result.var), array_ref); + } else { + array_ptr = EX_VAR(opline->result.var); + ZVAL_COPY_VALUE(array_ptr, array_ref); + } + fe_ht = Z_OBJPROP_P(array_ptr); + 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; + if ((Z_TYPE(p->val) != IS_UNDEF && + (Z_TYPE(p->val) != IS_INDIRECT || + Z_TYPE_P(Z_INDIRECT(p->val)) != IS_UNDEF)) && + (!p->key || + zend_check_property_access(Z_OBJ_P(array_ptr), p->key) == SUCCESS)) { + break; + } + pos++; + } + fe_ht->nInternalPointer = pos; + Z_FE_ITER_P(EX_VAR(opline->result.var)) = zend_hash_iterator_add(fe_ht, pos); + + FREE_OP1_VAR_PTR(); + CHECK_EXCEPTION(); + ZEND_VM_NEXT_OPCODE(); + } else { + zend_class_entry *ce = Z_OBJCE_P(array_ptr); + zend_object_iterator *iter = ce->get_iterator(ce, array_ptr, 1); + zend_bool is_empty; + + if (UNEXPECTED(!iter) || UNEXPECTED(EG(exception))) { + if (OP1_TYPE == IS_VAR) { + FREE_OP1_VAR_PTR(); + } else { + FREE_OP1(); + } + if (!EG(exception)) { + zend_throw_exception_ex(NULL, 0, "Object of type %s did not create an Iterator", ce->name->val); + } + zend_throw_exception_internal(NULL); + HANDLE_EXCEPTION(); + } - ZVAL_COPY_VALUE(EX_VAR(opline->result.var), array_ref); + iter->index = 0; + if (iter->funcs->rewind) { + iter->funcs->rewind(iter); + if (UNEXPECTED(EG(exception) != NULL)) { + OBJ_RELEASE(&iter->std); + if (OP1_TYPE == IS_VAR) { + FREE_OP1_VAR_PTR(); + } else { + FREE_OP1(); + } + HANDLE_EXCEPTION(); + } + } + + is_empty = iter->funcs->valid(iter) != SUCCESS; - if (iter) { - iter->index = 0; - if (iter->funcs->rewind) { - iter->funcs->rewind(iter); if (UNEXPECTED(EG(exception) != NULL)) { - zval_ptr_dtor(array_ref); - if (OP1_TYPE == IS_VAR && opline->extended_value & ZEND_FE_FETCH_BYREF) { + OBJ_RELEASE(&iter->std); + if (OP1_TYPE == IS_VAR) { FREE_OP1_VAR_PTR(); + } else { + FREE_OP1(); } HANDLE_EXCEPTION(); } - } - is_empty = iter->funcs->valid(iter) != SUCCESS; - if (UNEXPECTED(EG(exception) != NULL)) { - zval_ptr_dtor(array_ref); - if (OP1_TYPE == IS_VAR && opline->extended_value & ZEND_FE_FETCH_BYREF) { + iter->index = -1; /* will be set to 0 before using next handler */ + + ZVAL_OBJ(EX_VAR(opline->result.var), &iter->std); + Z_FE_ITER_P(EX_VAR(opline->result.var)) = (uint32_t)-1; + + if (OP1_TYPE == IS_VAR) { FREE_OP1_VAR_PTR(); + } else { + FREE_OP1(); } - HANDLE_EXCEPTION(); + if (is_empty) { + ZEND_VM_JMP(OP_JMP_ADDR(opline, opline->op2)); + } else { + 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 (OP1_TYPE == IS_VAR) { + FREE_OP1_VAR_PTR(); + } else { + FREE_OP1(); } - iter->index = -1; /* will be set to 0 before using next handler */ - } else if ((fe_ht = HASH_OF(array_ptr)) != NULL) { - HashPointer *ptr = (HashPointer*)EX_VAR((opline+2)->op1.var); - HashPosition pos = 0; - Bucket *p; + ZEND_VM_JMP(OP_JMP_ADDR(opline, opline->op2)); + } +} + +ZEND_VM_HANDLER(78, ZEND_FE_FETCH_R, VAR, ANY) +{ + USE_OPLINE + zend_free_op free_op1; + zval *array; + zval *value; + HashTable *fe_ht; + HashPosition pos; + Bucket *p; + array = EX_VAR(opline->op1.var); + SAVE_OPLINE(); + if (EXPECTED(Z_TYPE_P(array) == IS_ARRAY)) { + fe_ht = Z_ARRVAL_P(array); + pos = Z_FE_POS_P(EX_VAR(opline->op1.var)); while (1) { - if (pos >= fe_ht->nNumUsed) { - is_empty = 1; - if (OP1_TYPE == IS_VAR && opline->extended_value & ZEND_FE_FETCH_BYREF) { - FREE_OP1_VAR_PTR(); - } + if (UNEXPECTED(pos >= fe_ht->nNumUsed)) { + /* reached end of iteration */ ZEND_VM_JMP(OP_JMP_ADDR(opline, opline->op2)); } 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)) { + value = &p->val; + if (Z_TYPE_P(value) == IS_UNDEF) { pos++; continue; + } else if (Z_TYPE_P(value) == IS_INDIRECT) { + value = Z_INDIRECT_P(value); + if (Z_TYPE_P(value) == IS_UNDEF) { + pos++; + continue; + } } - if (!ce || - !p->key || - zend_check_property_access(Z_OBJ_P(array_ptr), p->key) == SUCCESS) { - break; + break; + } + if (opline->extended_value) { + if (!p->key) { + ZVAL_LONG(EX_VAR((opline+1)->result.var), p->h); + } else { + ZVAL_STR_COPY(EX_VAR((opline+1)->result.var), p->key); } - pos++; } - fe_ht->nInternalPointer = pos; - ptr->pos = pos; - ptr->ht = fe_ht; - ptr->h = fe_ht->arData[pos].h; - ptr->key = fe_ht->arData[pos].key; - is_empty = 0; + ZVAL_COPY(EX_VAR(opline->result.var), value); + Z_FE_POS_P(EX_VAR(opline->op1.var)) = pos + 1; + 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)) == NULL) { + /* plain object */ + zend_object *zobj = Z_OBJ_P(array); + + fe_ht = Z_OBJPROP_P(array); + 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 */ + ZEND_VM_JMP(OP_JMP_ADDR(opline, opline->op2)); + } + + p = fe_ht->arData + pos; + value = &p->val; + if (Z_TYPE_P(value) == IS_UNDEF) { + pos++; + continue; + } else if (Z_TYPE_P(value) == IS_INDIRECT) { + value = Z_INDIRECT_P(value); + if (Z_TYPE_P(value) == IS_UNDEF) { + pos++; + continue; + } + } + if (!p->key || + zend_check_property_access(Z_OBJ_P(array), p->key) == SUCCESS) { + break; + } + pos++; + } + if (opline->extended_value) { + if (UNEXPECTED(!p->key)) { + ZVAL_LONG(EX_VAR((opline+1)->result.var), p->h); + } else if (p->key->val[0]) { + ZVAL_STR_COPY(EX_VAR((opline+1)->result.var), p->key); + } else { + const char *class_name, *prop_name; + size_t prop_name_len; + zend_unmangle_property_name_ex( + p->key, &class_name, &prop_name, &prop_name_len); + ZVAL_STRINGL(EX_VAR((opline+1)->result.var), prop_name, prop_name_len); + } + } + ZVAL_COPY(EX_VAR(opline->result.var), value); + while (1) { + pos++; + if (pos >= fe_ht->nNumUsed) { + pos = INVALID_IDX; + break; + } + 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)) && + (Z_TYPE_P(array) != IS_OBJECT || + !p->key || + zend_check_property_access(Z_OBJ_P(array), p->key) == SUCCESS)) { + break; + } + } + 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 { + /* !iter happens from exception */ + if (iter && ++iter->index > 0) { + /* This could cause an endless loop if index becomes zero again. + * In case that ever happens we need an additional flag. */ + iter->funcs->move_forward(iter); + if (UNEXPECTED(EG(exception) != NULL)) { + zval_ptr_dtor(array); + HANDLE_EXCEPTION(); + } + } + /* If index is zero we come from FE_RESET and checked valid() already. */ + if (!iter || (iter->index > 0 && iter->funcs->valid(iter) == FAILURE)) { + /* reached end of iteration */ + if (UNEXPECTED(EG(exception) != NULL)) { + zval_ptr_dtor(array); + HANDLE_EXCEPTION(); + } + ZEND_VM_JMP(OP_JMP_ADDR(opline, opline->op2)); + } + value = iter->funcs->get_current_data(iter); + if (UNEXPECTED(EG(exception) != NULL)) { + zval_ptr_dtor(array); + HANDLE_EXCEPTION(); + } + if (!value) { + /* failure in get_current_data */ + ZEND_VM_JMP(OP_JMP_ADDR(opline, opline->op2)); + } + ZVAL_COPY(EX_VAR(opline->result.var), value); + if (opline->extended_value) { + if (iter->funcs->get_current_key) { + iter->funcs->get_current_key(iter, EX_VAR((opline+1)->result.var)); + if (UNEXPECTED(EG(exception) != NULL)) { + zval_ptr_dtor(array); + HANDLE_EXCEPTION(); + } + } else { + ZVAL_LONG(EX_VAR((opline+1)->result.var), iter->index); + } + } + ZEND_VM_INC_OPCODE(); + ZEND_VM_NEXT_OPCODE(); + } } else { zend_error(E_WARNING, "Invalid argument supplied for foreach()"); - is_empty = 1; - } - - if (OP1_TYPE == IS_VAR && opline->extended_value & ZEND_FE_FETCH_BYREF) { - FREE_OP1_VAR_PTR(); - } - if (is_empty) { ZEND_VM_JMP(OP_JMP_ADDR(opline, opline->op2)); - } else { - CHECK_EXCEPTION(); - ZEND_VM_NEXT_OPCODE(); } } -ZEND_VM_HANDLER(78, ZEND_FE_FETCH, VAR, ANY) +ZEND_VM_HANDLER(126, ZEND_FE_FETCH_RW, VAR, ANY) { USE_OPLINE zend_free_op free_op1; - zval *array, *array_ref; + zval *array; zval *value; HashTable *fe_ht; - HashPointer *ptr; HashPosition pos; Bucket *p; - array = array_ref = EX_VAR(opline->op1.var); - if (Z_ISREF_P(array)) { - array = Z_REFVAL_P(array); - // TODO: referenced value might be changed to different array ??? - if (Z_IMMUTABLE_P(array)) { - zval_copy_ctor_func(array); - } - } - + array = EX_VAR(opline->op1.var); SAVE_OPLINE(); + ZVAL_DEREF(array); 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(OP_JMP_ADDR(opline, opline->op2)); - } 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 (1) { - if (pos == INVALID_IDX) { - pos = fe_ht->nInternalPointer; - break; - } else if (fe_ht->arData[pos].h == ptr->h && fe_ht->arData[pos].key == ptr->key) { - break; - } - pos = Z_NEXT(fe_ht->arData[pos].val); - } - } - } + 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 */ + fe_ht->nInternalPointer = INVALID_IDX; ZEND_VM_JMP(OP_JMP_ADDR(opline, opline->op2)); } p = fe_ht->arData + pos; value = &p->val; - if (UNEXPECTED(Z_TYPE_P(value) == IS_UNDEF)) { + if (Z_TYPE_P(value) == IS_UNDEF) { pos++; continue; - } else if (UNEXPECTED(Z_TYPE_P(value) == IS_INDIRECT)) { + } else if (Z_TYPE_P(value) == IS_INDIRECT) { value = Z_INDIRECT_P(value); - if (UNEXPECTED(Z_TYPE_P(value) == IS_UNDEF)) { + if (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)); + break; + } + if (opline->extended_value) { + if (!p->key) { + ZVAL_LONG(EX_VAR((opline+1)->result.var), p->h); } 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 { - ZVAL_STR_COPY(EX_VAR((opline+1)->result.var), p->key); - } + ZVAL_STR_COPY(EX_VAR((opline+1)->result.var), p->key); } - break; } - do { + ZVAL_MAKE_REF(value); + Z_ADDREF_P(value); + ZVAL_REF(EX_VAR(opline->result.var), Z_REF_P(value)); + while (1) { pos++; if (pos >= fe_ht->nNumUsed) { - fe_ht->nInternalPointer = ptr->pos = INVALID_IDX; - ZEND_VM_INC_OPCODE(); - ZEND_VM_NEXT_OPCODE(); + pos = INVALID_IDX; + break; } 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; - ptr->key = fe_ht->arData[pos].key; + if (Z_TYPE(p->val) != IS_UNDEF && + (Z_TYPE(p->val) != IS_INDIRECT || + Z_TYPE_P(Z_INDIRECT(p->val)) != IS_UNDEF)) { + break; + } + } + 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)) { @@ -4916,93 +5152,66 @@ ZEND_VM_HANDLER(78, ZEND_FE_FETCH, VAR, ANY) 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(OP_JMP_ADDR(opline, opline->op2)); - } 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 (1) { - if (pos == INVALID_IDX) { - pos = fe_ht->nInternalPointer; - break; - } else if (fe_ht->arData[pos].h == ptr->h && fe_ht->arData[pos].key == ptr->key) { - break; - } - pos = Z_NEXT(fe_ht->arData[pos].val); - } - } - } + 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 */ + fe_ht->nInternalPointer = INVALID_IDX; ZEND_VM_JMP(OP_JMP_ADDR(opline, opline->op2)); } p = fe_ht->arData + pos; value = &p->val; - if (UNEXPECTED(Z_TYPE_P(value) == IS_UNDEF)) { + if (Z_TYPE_P(value) == IS_UNDEF) { pos++; continue; - } else if (UNEXPECTED(Z_TYPE_P(value) == IS_INDIRECT)) { + } else if (Z_TYPE_P(value) == IS_INDIRECT) { value = Z_INDIRECT_P(value); - if (UNEXPECTED(Z_TYPE_P(value) == IS_UNDEF)) { + if (Z_TYPE_P(value) == IS_UNDEF) { pos++; continue; } } - - 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) == SUCCESS) { - if (opline->extended_value & ZEND_FE_FETCH_WITH_KEY) { - if (p->key->val[0]) { - ZVAL_STR_COPY(EX_VAR((opline+1)->result.var), p->key); - } else { - const char *class_name, *prop_name; - size_t prop_name_len; - zend_unmangle_property_name_ex( - p->key, &class_name, &prop_name, &prop_name_len); - ZVAL_STRINGL(EX_VAR((opline+1)->result.var), prop_name, prop_name_len); - } - } + if (!p->key || + zend_check_property_access(Z_OBJ_P(array), p->key) == SUCCESS) { break; } pos++; } - 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) { + if (UNEXPECTED(!p->key)) { + ZVAL_LONG(EX_VAR((opline+1)->result.var), p->h); + } else if (p->key->val[0]) { + ZVAL_STR_COPY(EX_VAR((opline+1)->result.var), p->key); + } else { + const char *class_name, *prop_name; + size_t prop_name_len; + zend_unmangle_property_name_ex( + p->key, &class_name, &prop_name, &prop_name_len); + ZVAL_STRINGL(EX_VAR((opline+1)->result.var), prop_name, prop_name_len); + } } - do { + ZVAL_MAKE_REF(value); + Z_ADDREF_P(value); + ZVAL_REF(EX_VAR(opline->result.var), Z_REF_P(value)); + while (1) { pos++; if (pos >= fe_ht->nNumUsed) { - fe_ht->nInternalPointer = ptr->pos = INVALID_IDX; - ZEND_VM_INC_OPCODE(); - ZEND_VM_NEXT_OPCODE(); + pos = INVALID_IDX; + break; } 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) == FAILURE)); - fe_ht->nInternalPointer = ptr->pos = pos; - ptr->h = fe_ht->arData[pos].h; - ptr->key = fe_ht->arData[pos].key; + if ((Z_TYPE(p->val) != IS_UNDEF && + (Z_TYPE(p->val) != IS_INDIRECT || + Z_TYPE_P(Z_INDIRECT(p->val)) != IS_UNDEF)) && + (Z_TYPE_P(array) != IS_OBJECT || + !p->key || + zend_check_property_access(Z_OBJ_P(array), p->key) == SUCCESS)) { + break; + } + } + 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 { @@ -5012,7 +5221,7 @@ ZEND_VM_HANDLER(78, ZEND_FE_FETCH, VAR, ANY) * In case that ever happens we need an additional flag. */ iter->funcs->move_forward(iter); if (UNEXPECTED(EG(exception) != NULL)) { - zval_ptr_dtor(array_ref); + zval_ptr_dtor(array); HANDLE_EXCEPTION(); } } @@ -5020,38 +5229,34 @@ ZEND_VM_HANDLER(78, ZEND_FE_FETCH, VAR, ANY) if (!iter || (iter->index > 0 && iter->funcs->valid(iter) == FAILURE)) { /* reached end of iteration */ if (UNEXPECTED(EG(exception) != NULL)) { - zval_ptr_dtor(array_ref); + zval_ptr_dtor(array); HANDLE_EXCEPTION(); } ZEND_VM_JMP(OP_JMP_ADDR(opline, opline->op2)); } value = iter->funcs->get_current_data(iter); if (UNEXPECTED(EG(exception) != NULL)) { - zval_ptr_dtor(array_ref); + zval_ptr_dtor(array); HANDLE_EXCEPTION(); } if (!value) { /* failure in get_current_data */ ZEND_VM_JMP(OP_JMP_ADDR(opline, opline->op2)); } - 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 (opline->extended_value) { if (iter->funcs->get_current_key) { iter->funcs->get_current_key(iter, EX_VAR((opline+1)->result.var)); if (UNEXPECTED(EG(exception) != NULL)) { - zval_ptr_dtor(array_ref); + zval_ptr_dtor(array); HANDLE_EXCEPTION(); } } else { ZVAL_LONG(EX_VAR((opline+1)->result.var), iter->index); } } + 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(); } @@ -5840,6 +6045,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_TYPE_P(var) != IS_ARRAY && 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 9a7b23d87a..471f73aa32 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_TYPE_P(var) != IS_ARRAY && 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_TYPE_P(var) != IS_ARRAY && 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)); } @@ -3039,194 +3055,273 @@ static int ZEND_FASTCALL ZEND_INCLUDE_OR_EVAL_SPEC_CONST_HANDLER(ZEND_OPCODE_HA ZEND_VM_NEXT_OPCODE(); } -static int ZEND_FASTCALL ZEND_FE_RESET_SPEC_CONST_HANDLER(ZEND_OPCODE_HANDLER_ARGS) +static int ZEND_FASTCALL ZEND_FE_RESET_R_SPEC_CONST_HANDLER(ZEND_OPCODE_HANDLER_ARGS) { USE_OPLINE - zval *array_ptr, *array_ref, iterator, tmp; + zval *array_ptr, *result; HashTable *fe_ht; - zend_object_iterator *iter = NULL; - zend_class_entry *ce = NULL; - zend_bool is_empty = 0; SAVE_OPLINE(); - if ((IS_CONST == IS_CV || IS_CONST == IS_VAR) && - (opline->extended_value & ZEND_FE_FETCH_BYREF)) { - array_ptr = array_ref = NULL; - ZVAL_DEREF(array_ptr); - if (Z_TYPE_P(array_ptr) == IS_ARRAY) { - SEPARATE_ARRAY(array_ptr); - if (!Z_ISREF_P(array_ref)) { - ZVAL_NEW_REF(array_ref, array_ref); - array_ptr = Z_REFVAL_P(array_ref); - } - if (Z_REFCOUNTED_P(array_ref)) Z_ADDREF_P(array_ref); - } else if (Z_TYPE_P(array_ptr) == IS_OBJECT) { - ce = Z_OBJCE_P(array_ptr); - if (ce->get_iterator == NULL) { + array_ptr = EX_CONSTANT(opline->op1); + if (EXPECTED(Z_TYPE_P(array_ptr) == IS_ARRAY)) { + result = EX_VAR(opline->result.var); + ZVAL_COPY_VALUE(result, array_ptr); + if (IS_CONST != IS_TMP_VAR && Z_OPT_REFCOUNTED_P(result)) { + Z_ADDREF_P(array_ptr); + } + Z_FE_POS_P(result) = 0; + + CHECK_EXCEPTION(); + ZEND_VM_NEXT_OPCODE(); + } else if (IS_CONST != IS_CONST && EXPECTED(Z_TYPE_P(array_ptr) == IS_OBJECT)) { + if (!Z_OBJCE_P(array_ptr)->get_iterator) { + HashPosition pos = 0; + Bucket *p; + + result = EX_VAR(opline->result.var); + ZVAL_COPY_VALUE(result, array_ptr); + if (IS_CONST != IS_TMP_VAR) { Z_ADDREF_P(array_ptr); } - array_ref = array_ptr; - } else { - if (Z_REFCOUNTED_P(array_ref)) Z_ADDREF_P(array_ref); - } - } else { - array_ptr = array_ref = EX_CONSTANT(opline->op1); - if (IS_CONST & (IS_VAR|IS_CV)) { - ZVAL_DEREF(array_ptr); - } - if (IS_CONST == IS_TMP_VAR) { - ZVAL_COPY_VALUE(&tmp, array_ptr); - if (Z_OPT_IMMUTABLE_P(&tmp)) { - zval_copy_ctor_func(&tmp); + fe_ht = Z_OBJPROP_P(array_ptr); + pos = 0; + 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; + if ((Z_TYPE(p->val) != IS_UNDEF && + (Z_TYPE(p->val) != IS_INDIRECT || + Z_TYPE_P(Z_INDIRECT(p->val)) != IS_UNDEF)) && + (!p->key || + zend_check_property_access(Z_OBJ_P(array_ptr), p->key) == SUCCESS)) { + break; + } + pos++; } - array_ref = array_ptr = &tmp; - if (Z_TYPE_P(array_ptr) == IS_OBJECT) { - ce = Z_OBJCE_P(array_ptr); - if (ce && ce->get_iterator) { - Z_DELREF_P(array_ref); + fe_ht->nInternalPointer = pos; + Z_FE_ITER_P(EX_VAR(opline->result.var)) = zend_hash_iterator_add(fe_ht, pos); + + CHECK_EXCEPTION(); + ZEND_VM_NEXT_OPCODE(); + } else { + zend_class_entry *ce = Z_OBJCE_P(array_ptr); + zend_object_iterator *iter = ce->get_iterator(ce, array_ptr, 0); + zend_bool is_empty; + + if (UNEXPECTED(!iter) || UNEXPECTED(EG(exception))) { + + if (!EG(exception)) { + zend_throw_exception_ex(NULL, 0, "Object of type %s did not create an Iterator", ce->name->val); } + zend_throw_exception_internal(NULL); + HANDLE_EXCEPTION(); } - } else if (Z_TYPE_P(array_ptr) == IS_OBJECT) { - ce = Z_OBJCE_P(array_ptr); - if (!ce->get_iterator) { - if (IS_CONST == IS_CV) { - Z_ADDREF_P(array_ref); + + iter->index = 0; + if (iter->funcs->rewind) { + iter->funcs->rewind(iter); + if (UNEXPECTED(EG(exception) != NULL)) { + OBJ_RELEASE(&iter->std); + + HANDLE_EXCEPTION(); } } - } else if (Z_IMMUTABLE_P(array_ref)) { - if (IS_CONST == IS_CV) { - zval_copy_ctor_func(array_ref); - Z_ADDREF_P(array_ref); + + is_empty = iter->funcs->valid(iter) != SUCCESS; + + if (UNEXPECTED(EG(exception) != NULL)) { + OBJ_RELEASE(&iter->std); + + HANDLE_EXCEPTION(); + } + iter->index = -1; /* will be set to 0 before using next handler */ + + ZVAL_OBJ(EX_VAR(opline->result.var), &iter->std); + Z_FE_ITER_P(EX_VAR(opline->result.var)) = (uint32_t)-1; + + if (is_empty) { + ZEND_VM_JMP(OP_JMP_ADDR(opline, opline->op2)); } else { - ZVAL_COPY_VALUE(&tmp, array_ref); - zval_copy_ctor_func(&tmp); - array_ptr = array_ref = &tmp; - } - } else if (Z_REFCOUNTED_P(array_ref)) { - if (IS_CONST == IS_CONST || - (IS_CONST == IS_CV && - !Z_ISREF_P(array_ref) && - Z_REFCOUNT_P(array_ref) > 1) || - (IS_CONST == IS_VAR && - !Z_ISREF_P(array_ref) && - Z_REFCOUNT_P(array_ref) > 2)) { - if (IS_CONST == IS_VAR) { - Z_DELREF_P(array_ref); - } - ZVAL_DUP(&tmp, array_ref); - array_ptr = array_ref = &tmp; - } else if (IS_CONST == IS_CV || IS_CONST == IS_VAR) { - if (Z_ISREF_P(array_ref) && Z_REFCOUNT_P(array_ref) == 1) { - ZVAL_UNREF(array_ref); - array_ptr = array_ref; - } - if (Z_IMMUTABLE_P(array_ptr)) { - zval_copy_ctor_func(array_ptr); - } else if (Z_ISREF_P(array_ref) && - Z_COPYABLE_P(array_ptr) && - Z_REFCOUNT_P(array_ptr) > 1) { - Z_DELREF_P(array_ptr); - zval_copy_ctor_func(array_ptr); - } - if (IS_CONST == IS_CV) { - Z_ADDREF_P(array_ref); - } + 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; + + ZEND_VM_JMP(OP_JMP_ADDR(opline, opline->op2)); } +} + +static int ZEND_FASTCALL ZEND_FE_RESET_RW_SPEC_CONST_HANDLER(ZEND_OPCODE_HANDLER_ARGS) +{ + USE_OPLINE - if (ce && ce->get_iterator) { - iter = ce->get_iterator(ce, array_ptr, opline->extended_value & ZEND_FE_FETCH_BYREF); + zval *array_ptr, *array_ref; + HashTable *fe_ht; + HashPosition pos = 0; + Bucket *p; + + SAVE_OPLINE(); - if (IS_CONST == IS_VAR && !(opline->extended_value & ZEND_FE_FETCH_BYREF)) { + if (IS_CONST == IS_VAR || IS_CONST == IS_CV) { + array_ref = array_ptr = NULL; + if (Z_ISREF_P(array_ref)) { + array_ptr = Z_REFVAL_P(array_ref); + } + } else { + array_ref = array_ptr = EX_CONSTANT(opline->op1); + } + if (EXPECTED(Z_TYPE_P(array_ptr) == IS_ARRAY)) { + if (IS_CONST == IS_VAR || IS_CONST == IS_CV) { + if (array_ptr == array_ref) { + ZVAL_NEW_REF(array_ref, array_ref); + array_ptr = Z_REFVAL_P(array_ref); + } + Z_ADDREF_P(array_ref); + ZVAL_COPY_VALUE(EX_VAR(opline->result.var), array_ref); + } else { + array_ptr = EX_VAR(opline->result.var); + ZVAL_COPY_VALUE(array_ptr, array_ref); } - if (iter && EXPECTED(EG(exception) == NULL)) { - ZVAL_OBJ(&iterator, &iter->std); - array_ptr = array_ref = &iterator; + if (IS_CONST == IS_CONST) { + zval_copy_ctor_func(array_ptr); } else { - if (IS_CONST == IS_VAR && opline->extended_value & ZEND_FE_FETCH_BYREF) { + SEPARATE_ARRAY(array_ptr); + } + fe_ht = Z_ARRVAL_P(array_ptr); + 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)); } - if (!EG(exception)) { - zend_throw_exception_ex(NULL, 0, "Object of type %s did not create an Iterator", ce->name->val); + 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)) { + break; } - zend_throw_exception_internal(NULL); - HANDLE_EXCEPTION(); + pos++; } - } + fe_ht->nInternalPointer = pos; + Z_FE_ITER_P(EX_VAR(opline->result.var)) = zend_hash_iterator_add(fe_ht, pos); - ZVAL_COPY_VALUE(EX_VAR(opline->result.var), array_ref); + CHECK_EXCEPTION(); + ZEND_VM_NEXT_OPCODE(); + } else if (IS_CONST != IS_CONST && EXPECTED(Z_TYPE_P(array_ptr) == IS_OBJECT)) { + if (!Z_OBJCE_P(array_ptr)->get_iterator) { + if (IS_CONST == IS_VAR || IS_CONST == IS_CV) { + if (array_ptr == array_ref) { + ZVAL_NEW_REF(array_ref, array_ref); + array_ptr = Z_REFVAL_P(array_ref); + } + Z_ADDREF_P(array_ref); + ZVAL_COPY_VALUE(EX_VAR(opline->result.var), array_ref); + } else { + array_ptr = EX_VAR(opline->result.var); + ZVAL_COPY_VALUE(array_ptr, array_ref); + } + fe_ht = Z_OBJPROP_P(array_ptr); + while (1) { + if (pos >= fe_ht->nNumUsed) { - if (iter) { - iter->index = 0; - if (iter->funcs->rewind) { - iter->funcs->rewind(iter); - if (UNEXPECTED(EG(exception) != NULL)) { - zval_ptr_dtor(array_ref); - if (IS_CONST == IS_VAR && opline->extended_value & ZEND_FE_FETCH_BYREF) { + 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; + if ((Z_TYPE(p->val) != IS_UNDEF && + (Z_TYPE(p->val) != IS_INDIRECT || + Z_TYPE_P(Z_INDIRECT(p->val)) != IS_UNDEF)) && + (!p->key || + zend_check_property_access(Z_OBJ_P(array_ptr), p->key) == SUCCESS)) { + break; + } + pos++; + } + fe_ht->nInternalPointer = pos; + Z_FE_ITER_P(EX_VAR(opline->result.var)) = zend_hash_iterator_add(fe_ht, pos); + CHECK_EXCEPTION(); + ZEND_VM_NEXT_OPCODE(); + } else { + zend_class_entry *ce = Z_OBJCE_P(array_ptr); + zend_object_iterator *iter = ce->get_iterator(ce, array_ptr, 1); + zend_bool is_empty; + + if (UNEXPECTED(!iter) || UNEXPECTED(EG(exception))) { + if (IS_CONST == IS_VAR) { + + } else { + + } + if (!EG(exception)) { + zend_throw_exception_ex(NULL, 0, "Object of type %s did not create an Iterator", ce->name->val); } + zend_throw_exception_internal(NULL); HANDLE_EXCEPTION(); } - } - is_empty = iter->funcs->valid(iter) != SUCCESS; - if (UNEXPECTED(EG(exception) != NULL)) { - zval_ptr_dtor(array_ref); - if (IS_CONST == IS_VAR && opline->extended_value & ZEND_FE_FETCH_BYREF) { + iter->index = 0; + if (iter->funcs->rewind) { + iter->funcs->rewind(iter); + if (UNEXPECTED(EG(exception) != NULL)) { + OBJ_RELEASE(&iter->std); + if (IS_CONST == IS_VAR) { + + } else { + + } + HANDLE_EXCEPTION(); + } } - HANDLE_EXCEPTION(); - } - iter->index = -1; /* will be set to 0 before using next handler */ - } else if ((fe_ht = HASH_OF(array_ptr)) != NULL) { - 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_FETCH_BYREF) { + is_empty = iter->funcs->valid(iter) != SUCCESS; + + if (UNEXPECTED(EG(exception) != NULL)) { + OBJ_RELEASE(&iter->std); + if (IS_CONST == IS_VAR) { + + } else { } - ZEND_VM_JMP(OP_JMP_ADDR(opline, opline->op2)); + HANDLE_EXCEPTION(); } - 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; + iter->index = -1; /* will be set to 0 before using next handler */ + + ZVAL_OBJ(EX_VAR(opline->result.var), &iter->std); + Z_FE_ITER_P(EX_VAR(opline->result.var)) = (uint32_t)-1; + + if (IS_CONST == IS_VAR) { + + } else { + } - if (!ce || - !p->key || - zend_check_property_access(Z_OBJ_P(array_ptr), p->key) == SUCCESS) { - break; + if (is_empty) { + ZEND_VM_JMP(OP_JMP_ADDR(opline, opline->op2)); + } else { + CHECK_EXCEPTION(); + ZEND_VM_NEXT_OPCODE(); } - pos++; } - fe_ht->nInternalPointer = pos; - ptr->pos = pos; - ptr->ht = fe_ht; - ptr->h = fe_ht->arData[pos].h; - ptr->key = fe_ht->arData[pos].key; - is_empty = 0; } else { zend_error(E_WARNING, "Invalid argument supplied for foreach()"); - is_empty = 1; - } + ZVAL_UNDEF(EX_VAR(opline->result.var)); + Z_FE_ITER_P(EX_VAR(opline->result.var)) = (uint32_t)-1; + if (IS_CONST == IS_VAR) { - if (IS_CONST == IS_VAR && opline->extended_value & ZEND_FE_FETCH_BYREF) { + } else { - } - if (is_empty) { + } ZEND_VM_JMP(OP_JMP_ADDR(opline, opline->op2)); - } else { - CHECK_EXCEPTION(); - ZEND_VM_NEXT_OPCODE(); } } @@ -8957,194 +9052,274 @@ static int ZEND_FASTCALL ZEND_CAST_SPEC_TMP_HANDLER(ZEND_OPCODE_HANDLER_ARGS) ZEND_VM_NEXT_OPCODE(); } -static int ZEND_FASTCALL ZEND_FE_RESET_SPEC_TMP_HANDLER(ZEND_OPCODE_HANDLER_ARGS) +static int ZEND_FASTCALL ZEND_FE_RESET_R_SPEC_TMP_HANDLER(ZEND_OPCODE_HANDLER_ARGS) { USE_OPLINE zend_free_op free_op1; - zval *array_ptr, *array_ref, iterator, tmp; + zval *array_ptr, *result; HashTable *fe_ht; - zend_object_iterator *iter = NULL; - zend_class_entry *ce = NULL; - zend_bool is_empty = 0; SAVE_OPLINE(); - if ((IS_TMP_VAR == IS_CV || IS_TMP_VAR == IS_VAR) && - (opline->extended_value & ZEND_FE_FETCH_BYREF)) { - array_ptr = array_ref = NULL; - ZVAL_DEREF(array_ptr); - if (Z_TYPE_P(array_ptr) == IS_ARRAY) { - SEPARATE_ARRAY(array_ptr); - if (!Z_ISREF_P(array_ref)) { - ZVAL_NEW_REF(array_ref, array_ref); - array_ptr = Z_REFVAL_P(array_ref); - } - if (Z_REFCOUNTED_P(array_ref)) Z_ADDREF_P(array_ref); - } else if (Z_TYPE_P(array_ptr) == IS_OBJECT) { - ce = Z_OBJCE_P(array_ptr); - if (ce->get_iterator == NULL) { + array_ptr = _get_zval_ptr_tmp(opline->op1.var, execute_data, &free_op1); + if (EXPECTED(Z_TYPE_P(array_ptr) == IS_ARRAY)) { + result = EX_VAR(opline->result.var); + ZVAL_COPY_VALUE(result, array_ptr); + if (IS_TMP_VAR != IS_TMP_VAR && Z_OPT_REFCOUNTED_P(result)) { + Z_ADDREF_P(array_ptr); + } + Z_FE_POS_P(result) = 0; + + CHECK_EXCEPTION(); + ZEND_VM_NEXT_OPCODE(); + } else if (IS_TMP_VAR != IS_CONST && EXPECTED(Z_TYPE_P(array_ptr) == IS_OBJECT)) { + if (!Z_OBJCE_P(array_ptr)->get_iterator) { + HashPosition pos = 0; + Bucket *p; + + result = EX_VAR(opline->result.var); + ZVAL_COPY_VALUE(result, array_ptr); + if (IS_TMP_VAR != IS_TMP_VAR) { Z_ADDREF_P(array_ptr); } - array_ref = array_ptr; - } else { - if (Z_REFCOUNTED_P(array_ref)) Z_ADDREF_P(array_ref); - } - } else { - array_ptr = array_ref = _get_zval_ptr_tmp(opline->op1.var, execute_data, &free_op1); - if (IS_TMP_VAR & (IS_VAR|IS_CV)) { - ZVAL_DEREF(array_ptr); - } - if (IS_TMP_VAR == IS_TMP_VAR) { - ZVAL_COPY_VALUE(&tmp, array_ptr); - if (Z_OPT_IMMUTABLE_P(&tmp)) { - zval_copy_ctor_func(&tmp); + fe_ht = Z_OBJPROP_P(array_ptr); + pos = 0; + 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; + if ((Z_TYPE(p->val) != IS_UNDEF && + (Z_TYPE(p->val) != IS_INDIRECT || + Z_TYPE_P(Z_INDIRECT(p->val)) != IS_UNDEF)) && + (!p->key || + zend_check_property_access(Z_OBJ_P(array_ptr), p->key) == SUCCESS)) { + break; + } + pos++; } - array_ref = array_ptr = &tmp; - if (Z_TYPE_P(array_ptr) == IS_OBJECT) { - ce = Z_OBJCE_P(array_ptr); - if (ce && ce->get_iterator) { - Z_DELREF_P(array_ref); + fe_ht->nInternalPointer = pos; + Z_FE_ITER_P(EX_VAR(opline->result.var)) = zend_hash_iterator_add(fe_ht, pos); + + CHECK_EXCEPTION(); + ZEND_VM_NEXT_OPCODE(); + } else { + zend_class_entry *ce = Z_OBJCE_P(array_ptr); + zend_object_iterator *iter = ce->get_iterator(ce, array_ptr, 0); + zend_bool is_empty; + + if (UNEXPECTED(!iter) || UNEXPECTED(EG(exception))) { + zval_ptr_dtor_nogc(free_op1); + if (!EG(exception)) { + zend_throw_exception_ex(NULL, 0, "Object of type %s did not create an Iterator", ce->name->val); } + zend_throw_exception_internal(NULL); + HANDLE_EXCEPTION(); } - } else if (Z_TYPE_P(array_ptr) == IS_OBJECT) { - ce = Z_OBJCE_P(array_ptr); - if (!ce->get_iterator) { - if (IS_TMP_VAR == IS_CV) { - Z_ADDREF_P(array_ref); + + iter->index = 0; + if (iter->funcs->rewind) { + iter->funcs->rewind(iter); + if (UNEXPECTED(EG(exception) != NULL)) { + OBJ_RELEASE(&iter->std); + zval_ptr_dtor_nogc(free_op1); + HANDLE_EXCEPTION(); } } - } else if (Z_IMMUTABLE_P(array_ref)) { - if (IS_TMP_VAR == IS_CV) { - zval_copy_ctor_func(array_ref); - Z_ADDREF_P(array_ref); + + is_empty = iter->funcs->valid(iter) != SUCCESS; + + if (UNEXPECTED(EG(exception) != NULL)) { + OBJ_RELEASE(&iter->std); + zval_ptr_dtor_nogc(free_op1); + HANDLE_EXCEPTION(); + } + iter->index = -1; /* will be set to 0 before using next handler */ + + ZVAL_OBJ(EX_VAR(opline->result.var), &iter->std); + Z_FE_ITER_P(EX_VAR(opline->result.var)) = (uint32_t)-1; + + zval_ptr_dtor_nogc(free_op1); + if (is_empty) { + ZEND_VM_JMP(OP_JMP_ADDR(opline, opline->op2)); } else { - ZVAL_COPY_VALUE(&tmp, array_ref); - zval_copy_ctor_func(&tmp); - array_ptr = array_ref = &tmp; - } - } else if (Z_REFCOUNTED_P(array_ref)) { - if (IS_TMP_VAR == IS_CONST || - (IS_TMP_VAR == IS_CV && - !Z_ISREF_P(array_ref) && - Z_REFCOUNT_P(array_ref) > 1) || - (IS_TMP_VAR == IS_VAR && - !Z_ISREF_P(array_ref) && - Z_REFCOUNT_P(array_ref) > 2)) { - if (IS_TMP_VAR == IS_VAR) { - Z_DELREF_P(array_ref); - } - ZVAL_DUP(&tmp, array_ref); - array_ptr = array_ref = &tmp; - } else if (IS_TMP_VAR == IS_CV || IS_TMP_VAR == IS_VAR) { - if (Z_ISREF_P(array_ref) && Z_REFCOUNT_P(array_ref) == 1) { - ZVAL_UNREF(array_ref); - array_ptr = array_ref; - } - if (Z_IMMUTABLE_P(array_ptr)) { - zval_copy_ctor_func(array_ptr); - } else if (Z_ISREF_P(array_ref) && - Z_COPYABLE_P(array_ptr) && - Z_REFCOUNT_P(array_ptr) > 1) { - Z_DELREF_P(array_ptr); - zval_copy_ctor_func(array_ptr); - } - if (IS_TMP_VAR == IS_CV) { - Z_ADDREF_P(array_ref); - } + 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; + zval_ptr_dtor_nogc(free_op1); + ZEND_VM_JMP(OP_JMP_ADDR(opline, opline->op2)); } +} + +static int ZEND_FASTCALL ZEND_FE_RESET_RW_SPEC_TMP_HANDLER(ZEND_OPCODE_HANDLER_ARGS) +{ + USE_OPLINE + zend_free_op free_op1; + zval *array_ptr, *array_ref; + HashTable *fe_ht; + HashPosition pos = 0; + Bucket *p; - if (ce && ce->get_iterator) { - iter = ce->get_iterator(ce, array_ptr, opline->extended_value & ZEND_FE_FETCH_BYREF); + SAVE_OPLINE(); - if (IS_TMP_VAR == IS_VAR && !(opline->extended_value & ZEND_FE_FETCH_BYREF)) { + if (IS_TMP_VAR == IS_VAR || IS_TMP_VAR == IS_CV) { + array_ref = array_ptr = NULL; + if (Z_ISREF_P(array_ref)) { + array_ptr = Z_REFVAL_P(array_ref); + } + } else { + array_ref = array_ptr = _get_zval_ptr_tmp(opline->op1.var, execute_data, &free_op1); + } + if (EXPECTED(Z_TYPE_P(array_ptr) == IS_ARRAY)) { + if (IS_TMP_VAR == IS_VAR || IS_TMP_VAR == IS_CV) { + if (array_ptr == array_ref) { + ZVAL_NEW_REF(array_ref, array_ref); + array_ptr = Z_REFVAL_P(array_ref); + } + Z_ADDREF_P(array_ref); + ZVAL_COPY_VALUE(EX_VAR(opline->result.var), array_ref); + } else { + array_ptr = EX_VAR(opline->result.var); + ZVAL_COPY_VALUE(array_ptr, array_ref); } - if (iter && EXPECTED(EG(exception) == NULL)) { - ZVAL_OBJ(&iterator, &iter->std); - array_ptr = array_ref = &iterator; + if (IS_TMP_VAR == IS_CONST) { + zval_copy_ctor_func(array_ptr); } else { - if (IS_TMP_VAR == IS_VAR && opline->extended_value & ZEND_FE_FETCH_BYREF) { + SEPARATE_ARRAY(array_ptr); + } + fe_ht = Z_ARRVAL_P(array_ptr); + 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)); } - if (!EG(exception)) { - zend_throw_exception_ex(NULL, 0, "Object of type %s did not create an Iterator", ce->name->val); + 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)) { + break; } - zend_throw_exception_internal(NULL); - HANDLE_EXCEPTION(); + pos++; } - } + fe_ht->nInternalPointer = pos; + Z_FE_ITER_P(EX_VAR(opline->result.var)) = zend_hash_iterator_add(fe_ht, pos); - ZVAL_COPY_VALUE(EX_VAR(opline->result.var), array_ref); + CHECK_EXCEPTION(); + ZEND_VM_NEXT_OPCODE(); + } else if (IS_TMP_VAR != IS_CONST && EXPECTED(Z_TYPE_P(array_ptr) == IS_OBJECT)) { + if (!Z_OBJCE_P(array_ptr)->get_iterator) { + if (IS_TMP_VAR == IS_VAR || IS_TMP_VAR == IS_CV) { + if (array_ptr == array_ref) { + ZVAL_NEW_REF(array_ref, array_ref); + array_ptr = Z_REFVAL_P(array_ref); + } + Z_ADDREF_P(array_ref); + ZVAL_COPY_VALUE(EX_VAR(opline->result.var), array_ref); + } else { + array_ptr = EX_VAR(opline->result.var); + ZVAL_COPY_VALUE(array_ptr, array_ref); + } + fe_ht = Z_OBJPROP_P(array_ptr); + while (1) { + if (pos >= fe_ht->nNumUsed) { - if (iter) { - iter->index = 0; - if (iter->funcs->rewind) { - iter->funcs->rewind(iter); - if (UNEXPECTED(EG(exception) != NULL)) { - zval_ptr_dtor(array_ref); - if (IS_TMP_VAR == IS_VAR && opline->extended_value & ZEND_FE_FETCH_BYREF) { + 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; + if ((Z_TYPE(p->val) != IS_UNDEF && + (Z_TYPE(p->val) != IS_INDIRECT || + Z_TYPE_P(Z_INDIRECT(p->val)) != IS_UNDEF)) && + (!p->key || + zend_check_property_access(Z_OBJ_P(array_ptr), p->key) == SUCCESS)) { + break; + } + pos++; + } + fe_ht->nInternalPointer = pos; + Z_FE_ITER_P(EX_VAR(opline->result.var)) = zend_hash_iterator_add(fe_ht, pos); + + CHECK_EXCEPTION(); + ZEND_VM_NEXT_OPCODE(); + } else { + zend_class_entry *ce = Z_OBJCE_P(array_ptr); + zend_object_iterator *iter = ce->get_iterator(ce, array_ptr, 1); + zend_bool is_empty; + + if (UNEXPECTED(!iter) || UNEXPECTED(EG(exception))) { + if (IS_TMP_VAR == IS_VAR) { + } else { + zval_ptr_dtor_nogc(free_op1); + } + if (!EG(exception)) { + zend_throw_exception_ex(NULL, 0, "Object of type %s did not create an Iterator", ce->name->val); } + zend_throw_exception_internal(NULL); HANDLE_EXCEPTION(); } - } - is_empty = iter->funcs->valid(iter) != SUCCESS; - if (UNEXPECTED(EG(exception) != NULL)) { - zval_ptr_dtor(array_ref); - if (IS_TMP_VAR == IS_VAR && opline->extended_value & ZEND_FE_FETCH_BYREF) { + iter->index = 0; + if (iter->funcs->rewind) { + iter->funcs->rewind(iter); + if (UNEXPECTED(EG(exception) != NULL)) { + OBJ_RELEASE(&iter->std); + if (IS_TMP_VAR == IS_VAR) { + + } else { + zval_ptr_dtor_nogc(free_op1); + } + HANDLE_EXCEPTION(); + } } - HANDLE_EXCEPTION(); - } - iter->index = -1; /* will be set to 0 before using next handler */ - } else if ((fe_ht = HASH_OF(array_ptr)) != NULL) { - 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_FETCH_BYREF) { + is_empty = iter->funcs->valid(iter) != SUCCESS; + + if (UNEXPECTED(EG(exception) != NULL)) { + OBJ_RELEASE(&iter->std); + if (IS_TMP_VAR == IS_VAR) { + } else { + zval_ptr_dtor_nogc(free_op1); } - ZEND_VM_JMP(OP_JMP_ADDR(opline, opline->op2)); + HANDLE_EXCEPTION(); } - 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; + iter->index = -1; /* will be set to 0 before using next handler */ + + ZVAL_OBJ(EX_VAR(opline->result.var), &iter->std); + Z_FE_ITER_P(EX_VAR(opline->result.var)) = (uint32_t)-1; + + if (IS_TMP_VAR == IS_VAR) { + + } else { + zval_ptr_dtor_nogc(free_op1); } - if (!ce || - !p->key || - zend_check_property_access(Z_OBJ_P(array_ptr), p->key) == SUCCESS) { - break; + if (is_empty) { + ZEND_VM_JMP(OP_JMP_ADDR(opline, opline->op2)); + } else { + CHECK_EXCEPTION(); + ZEND_VM_NEXT_OPCODE(); } - pos++; } - fe_ht->nInternalPointer = pos; - ptr->pos = pos; - ptr->ht = fe_ht; - ptr->h = fe_ht->arData[pos].h; - ptr->key = fe_ht->arData[pos].key; - is_empty = 0; } else { zend_error(E_WARNING, "Invalid argument supplied for foreach()"); - is_empty = 1; - } - - if (IS_TMP_VAR == IS_VAR && opline->extended_value & ZEND_FE_FETCH_BYREF) { + 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) { - } - if (is_empty) { + } else { + zval_ptr_dtor_nogc(free_op1); + } ZEND_VM_JMP(OP_JMP_ADDR(opline, opline->op2)); - } else { - CHECK_EXCEPTION(); - ZEND_VM_NEXT_OPCODE(); } } @@ -11776,292 +11951,505 @@ static int ZEND_FASTCALL ZEND_CAST_SPEC_VAR_HANDLER(ZEND_OPCODE_HANDLER_ARGS) ZEND_VM_NEXT_OPCODE(); } -static int ZEND_FASTCALL ZEND_FE_RESET_SPEC_VAR_HANDLER(ZEND_OPCODE_HANDLER_ARGS) +static int ZEND_FASTCALL ZEND_FE_RESET_R_SPEC_VAR_HANDLER(ZEND_OPCODE_HANDLER_ARGS) { USE_OPLINE zend_free_op free_op1; - zval *array_ptr, *array_ref, iterator, tmp; + zval *array_ptr, *result; HashTable *fe_ht; - zend_object_iterator *iter = NULL; - zend_class_entry *ce = NULL; - zend_bool is_empty = 0; SAVE_OPLINE(); - if ((IS_VAR == IS_CV || IS_VAR == IS_VAR) && - (opline->extended_value & ZEND_FE_FETCH_BYREF)) { - array_ptr = array_ref = _get_zval_ptr_ptr_var(opline->op1.var, execute_data, &free_op1); - ZVAL_DEREF(array_ptr); - if (Z_TYPE_P(array_ptr) == IS_ARRAY) { - SEPARATE_ARRAY(array_ptr); - if (!Z_ISREF_P(array_ref)) { - ZVAL_NEW_REF(array_ref, array_ref); - array_ptr = Z_REFVAL_P(array_ref); - } - if (Z_REFCOUNTED_P(array_ref)) Z_ADDREF_P(array_ref); - } else if (Z_TYPE_P(array_ptr) == IS_OBJECT) { - ce = Z_OBJCE_P(array_ptr); - if (ce->get_iterator == NULL) { + array_ptr = _get_zval_ptr_var_deref(opline->op1.var, execute_data, &free_op1); + if (EXPECTED(Z_TYPE_P(array_ptr) == IS_ARRAY)) { + result = EX_VAR(opline->result.var); + ZVAL_COPY_VALUE(result, array_ptr); + if (IS_VAR != IS_TMP_VAR && Z_OPT_REFCOUNTED_P(result)) { + Z_ADDREF_P(array_ptr); + } + Z_FE_POS_P(result) = 0; + + zval_ptr_dtor_nogc(free_op1); + CHECK_EXCEPTION(); + ZEND_VM_NEXT_OPCODE(); + } else if (IS_VAR != IS_CONST && EXPECTED(Z_TYPE_P(array_ptr) == IS_OBJECT)) { + if (!Z_OBJCE_P(array_ptr)->get_iterator) { + HashPosition pos = 0; + Bucket *p; + + result = EX_VAR(opline->result.var); + ZVAL_COPY_VALUE(result, array_ptr); + if (IS_VAR != IS_TMP_VAR) { Z_ADDREF_P(array_ptr); } - array_ref = array_ptr; - } else { - if (Z_REFCOUNTED_P(array_ref)) Z_ADDREF_P(array_ref); - } - } else { - array_ptr = array_ref = _get_zval_ptr_var(opline->op1.var, execute_data, &free_op1); - if (IS_VAR & (IS_VAR|IS_CV)) { - ZVAL_DEREF(array_ptr); - } - if (IS_VAR == IS_TMP_VAR) { - ZVAL_COPY_VALUE(&tmp, array_ptr); - if (Z_OPT_IMMUTABLE_P(&tmp)) { - zval_copy_ctor_func(&tmp); + fe_ht = Z_OBJPROP_P(array_ptr); + pos = 0; + while (1) { + if (pos >= fe_ht->nNumUsed) { + 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; + if ((Z_TYPE(p->val) != IS_UNDEF && + (Z_TYPE(p->val) != IS_INDIRECT || + Z_TYPE_P(Z_INDIRECT(p->val)) != IS_UNDEF)) && + (!p->key || + zend_check_property_access(Z_OBJ_P(array_ptr), p->key) == SUCCESS)) { + break; + } + pos++; } - array_ref = array_ptr = &tmp; - if (Z_TYPE_P(array_ptr) == IS_OBJECT) { - ce = Z_OBJCE_P(array_ptr); - if (ce && ce->get_iterator) { - Z_DELREF_P(array_ref); + fe_ht->nInternalPointer = pos; + Z_FE_ITER_P(EX_VAR(opline->result.var)) = zend_hash_iterator_add(fe_ht, pos); + + zval_ptr_dtor_nogc(free_op1); + CHECK_EXCEPTION(); + ZEND_VM_NEXT_OPCODE(); + } else { + zend_class_entry *ce = Z_OBJCE_P(array_ptr); + zend_object_iterator *iter = ce->get_iterator(ce, array_ptr, 0); + zend_bool is_empty; + + if (UNEXPECTED(!iter) || UNEXPECTED(EG(exception))) { + zval_ptr_dtor_nogc(free_op1); + if (!EG(exception)) { + zend_throw_exception_ex(NULL, 0, "Object of type %s did not create an Iterator", ce->name->val); } + zend_throw_exception_internal(NULL); + HANDLE_EXCEPTION(); } - } else if (Z_TYPE_P(array_ptr) == IS_OBJECT) { - ce = Z_OBJCE_P(array_ptr); - if (!ce->get_iterator) { - if (IS_VAR == IS_CV) { - Z_ADDREF_P(array_ref); + + iter->index = 0; + if (iter->funcs->rewind) { + iter->funcs->rewind(iter); + if (UNEXPECTED(EG(exception) != NULL)) { + OBJ_RELEASE(&iter->std); + zval_ptr_dtor_nogc(free_op1); + HANDLE_EXCEPTION(); } } - } else if (Z_IMMUTABLE_P(array_ref)) { - if (IS_VAR == IS_CV) { - zval_copy_ctor_func(array_ref); - Z_ADDREF_P(array_ref); + + is_empty = iter->funcs->valid(iter) != SUCCESS; + + if (UNEXPECTED(EG(exception) != NULL)) { + OBJ_RELEASE(&iter->std); + zval_ptr_dtor_nogc(free_op1); + HANDLE_EXCEPTION(); + } + iter->index = -1; /* will be set to 0 before using next handler */ + + ZVAL_OBJ(EX_VAR(opline->result.var), &iter->std); + Z_FE_ITER_P(EX_VAR(opline->result.var)) = (uint32_t)-1; + + zval_ptr_dtor_nogc(free_op1); + if (is_empty) { + ZEND_VM_JMP(OP_JMP_ADDR(opline, opline->op2)); } else { - ZVAL_COPY_VALUE(&tmp, array_ref); - zval_copy_ctor_func(&tmp); - array_ptr = array_ref = &tmp; - } - } else if (Z_REFCOUNTED_P(array_ref)) { - if (IS_VAR == IS_CONST || - (IS_VAR == IS_CV && - !Z_ISREF_P(array_ref) && - Z_REFCOUNT_P(array_ref) > 1) || - (IS_VAR == IS_VAR && - !Z_ISREF_P(array_ref) && - Z_REFCOUNT_P(array_ref) > 2)) { - if (IS_VAR == IS_VAR) { - Z_DELREF_P(array_ref); - } - ZVAL_DUP(&tmp, array_ref); - array_ptr = array_ref = &tmp; - } else if (IS_VAR == IS_CV || IS_VAR == IS_VAR) { - if (Z_ISREF_P(array_ref) && Z_REFCOUNT_P(array_ref) == 1) { - ZVAL_UNREF(array_ref); - array_ptr = array_ref; - } - if (Z_IMMUTABLE_P(array_ptr)) { - zval_copy_ctor_func(array_ptr); - } else if (Z_ISREF_P(array_ref) && - Z_COPYABLE_P(array_ptr) && - Z_REFCOUNT_P(array_ptr) > 1) { - Z_DELREF_P(array_ptr); - zval_copy_ctor_func(array_ptr); - } - if (IS_VAR == IS_CV) { - Z_ADDREF_P(array_ref); - } + 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; + zval_ptr_dtor_nogc(free_op1); + ZEND_VM_JMP(OP_JMP_ADDR(opline, opline->op2)); } +} - if (ce && ce->get_iterator) { - iter = ce->get_iterator(ce, array_ptr, opline->extended_value & ZEND_FE_FETCH_BYREF); +static int ZEND_FASTCALL ZEND_FE_RESET_RW_SPEC_VAR_HANDLER(ZEND_OPCODE_HANDLER_ARGS) +{ + USE_OPLINE + zend_free_op free_op1; + zval *array_ptr, *array_ref; + HashTable *fe_ht; + HashPosition pos = 0; + Bucket *p; - if (IS_VAR == IS_VAR && !(opline->extended_value & ZEND_FE_FETCH_BYREF)) { - zval_ptr_dtor_nogc(free_op1); + SAVE_OPLINE(); + + if (IS_VAR == IS_VAR || IS_VAR == IS_CV) { + array_ref = array_ptr = _get_zval_ptr_ptr_var(opline->op1.var, execute_data, &free_op1); + if (Z_ISREF_P(array_ref)) { + array_ptr = Z_REFVAL_P(array_ref); + } + } else { + array_ref = array_ptr = _get_zval_ptr_var(opline->op1.var, execute_data, &free_op1); + } + + if (EXPECTED(Z_TYPE_P(array_ptr) == IS_ARRAY)) { + if (IS_VAR == IS_VAR || IS_VAR == IS_CV) { + if (array_ptr == array_ref) { + ZVAL_NEW_REF(array_ref, array_ref); + array_ptr = Z_REFVAL_P(array_ref); + } + Z_ADDREF_P(array_ref); + ZVAL_COPY_VALUE(EX_VAR(opline->result.var), array_ref); + } else { + array_ptr = EX_VAR(opline->result.var); + ZVAL_COPY_VALUE(array_ptr, array_ref); } - if (iter && EXPECTED(EG(exception) == NULL)) { - ZVAL_OBJ(&iterator, &iter->std); - array_ptr = array_ref = &iterator; + if (IS_VAR == IS_CONST) { + zval_copy_ctor_func(array_ptr); } else { - if (IS_VAR == IS_VAR && opline->extended_value & ZEND_FE_FETCH_BYREF) { + SEPARATE_ARRAY(array_ptr); + } + fe_ht = Z_ARRVAL_P(array_ptr); + 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)); } - if (!EG(exception)) { - zend_throw_exception_ex(NULL, 0, "Object of type %s did not create an Iterator", ce->name->val); + 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)) { + break; } - zend_throw_exception_internal(NULL); - HANDLE_EXCEPTION(); + pos++; } - } + fe_ht->nInternalPointer = pos; + Z_FE_ITER_P(EX_VAR(opline->result.var)) = zend_hash_iterator_add(fe_ht, pos); - ZVAL_COPY_VALUE(EX_VAR(opline->result.var), array_ref); + if (free_op1) {zval_ptr_dtor_nogc(free_op1);}; + CHECK_EXCEPTION(); + ZEND_VM_NEXT_OPCODE(); + } else if (IS_VAR != IS_CONST && EXPECTED(Z_TYPE_P(array_ptr) == IS_OBJECT)) { + if (!Z_OBJCE_P(array_ptr)->get_iterator) { + if (IS_VAR == IS_VAR || IS_VAR == IS_CV) { + if (array_ptr == array_ref) { + ZVAL_NEW_REF(array_ref, array_ref); + array_ptr = Z_REFVAL_P(array_ref); + } + Z_ADDREF_P(array_ref); + ZVAL_COPY_VALUE(EX_VAR(opline->result.var), array_ref); + } else { + array_ptr = EX_VAR(opline->result.var); + ZVAL_COPY_VALUE(array_ptr, array_ref); + } + fe_ht = Z_OBJPROP_P(array_ptr); + 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; + if ((Z_TYPE(p->val) != IS_UNDEF && + (Z_TYPE(p->val) != IS_INDIRECT || + Z_TYPE_P(Z_INDIRECT(p->val)) != IS_UNDEF)) && + (!p->key || + zend_check_property_access(Z_OBJ_P(array_ptr), p->key) == SUCCESS)) { + break; + } + pos++; + } + fe_ht->nInternalPointer = pos; + Z_FE_ITER_P(EX_VAR(opline->result.var)) = zend_hash_iterator_add(fe_ht, pos); + + if (free_op1) {zval_ptr_dtor_nogc(free_op1);}; + CHECK_EXCEPTION(); + ZEND_VM_NEXT_OPCODE(); + } else { + zend_class_entry *ce = Z_OBJCE_P(array_ptr); + zend_object_iterator *iter = ce->get_iterator(ce, array_ptr, 1); + zend_bool is_empty; + + if (UNEXPECTED(!iter) || UNEXPECTED(EG(exception))) { + if (IS_VAR == IS_VAR) { + if (free_op1) {zval_ptr_dtor_nogc(free_op1);}; + } else { + zval_ptr_dtor_nogc(free_op1); + } + if (!EG(exception)) { + zend_throw_exception_ex(NULL, 0, "Object of type %s did not create an Iterator", ce->name->val); + } + zend_throw_exception_internal(NULL); + HANDLE_EXCEPTION(); + } + + iter->index = 0; + if (iter->funcs->rewind) { + iter->funcs->rewind(iter); + if (UNEXPECTED(EG(exception) != NULL)) { + OBJ_RELEASE(&iter->std); + if (IS_VAR == IS_VAR) { + if (free_op1) {zval_ptr_dtor_nogc(free_op1);}; + } else { + zval_ptr_dtor_nogc(free_op1); + } + HANDLE_EXCEPTION(); + } + } + + is_empty = iter->funcs->valid(iter) != SUCCESS; - if (iter) { - iter->index = 0; - if (iter->funcs->rewind) { - iter->funcs->rewind(iter); if (UNEXPECTED(EG(exception) != NULL)) { - zval_ptr_dtor(array_ref); - if (IS_VAR == IS_VAR && opline->extended_value & ZEND_FE_FETCH_BYREF) { + OBJ_RELEASE(&iter->std); + if (IS_VAR == IS_VAR) { if (free_op1) {zval_ptr_dtor_nogc(free_op1);}; + } else { + zval_ptr_dtor_nogc(free_op1); } HANDLE_EXCEPTION(); } - } - is_empty = iter->funcs->valid(iter) != SUCCESS; - if (UNEXPECTED(EG(exception) != NULL)) { - zval_ptr_dtor(array_ref); - if (IS_VAR == IS_VAR && opline->extended_value & ZEND_FE_FETCH_BYREF) { + iter->index = -1; /* will be set to 0 before using next handler */ + + ZVAL_OBJ(EX_VAR(opline->result.var), &iter->std); + 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 { + zval_ptr_dtor_nogc(free_op1); + } + if (is_empty) { + ZEND_VM_JMP(OP_JMP_ADDR(opline, opline->op2)); + } else { + CHECK_EXCEPTION(); + ZEND_VM_NEXT_OPCODE(); } - HANDLE_EXCEPTION(); } - iter->index = -1; /* will be set to 0 before using next handler */ - } else if ((fe_ht = HASH_OF(array_ptr)) != NULL) { - HashPointer *ptr = (HashPointer*)EX_VAR((opline+2)->op1.var); - HashPosition pos = 0; - Bucket *p; + } 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 { + zval_ptr_dtor_nogc(free_op1); + } + ZEND_VM_JMP(OP_JMP_ADDR(opline, opline->op2)); + } +} + +static int ZEND_FASTCALL ZEND_FE_FETCH_R_SPEC_VAR_HANDLER(ZEND_OPCODE_HANDLER_ARGS) +{ + USE_OPLINE + + zval *array; + zval *value; + HashTable *fe_ht; + HashPosition pos; + Bucket *p; + array = EX_VAR(opline->op1.var); + SAVE_OPLINE(); + if (EXPECTED(Z_TYPE_P(array) == IS_ARRAY)) { + fe_ht = Z_ARRVAL_P(array); + pos = Z_FE_POS_P(EX_VAR(opline->op1.var)); while (1) { - if (pos >= fe_ht->nNumUsed) { - is_empty = 1; - if (IS_VAR == IS_VAR && opline->extended_value & ZEND_FE_FETCH_BYREF) { - if (free_op1) {zval_ptr_dtor_nogc(free_op1);}; - } + if (UNEXPECTED(pos >= fe_ht->nNumUsed)) { + /* reached end of iteration */ ZEND_VM_JMP(OP_JMP_ADDR(opline, opline->op2)); } 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)) { + value = &p->val; + if (Z_TYPE_P(value) == IS_UNDEF) { pos++; continue; + } else if (Z_TYPE_P(value) == IS_INDIRECT) { + value = Z_INDIRECT_P(value); + if (Z_TYPE_P(value) == IS_UNDEF) { + pos++; + continue; + } } - if (!ce || - !p->key || - zend_check_property_access(Z_OBJ_P(array_ptr), p->key) == SUCCESS) { - break; + break; + } + if (opline->extended_value) { + if (!p->key) { + ZVAL_LONG(EX_VAR((opline+1)->result.var), p->h); + } else { + ZVAL_STR_COPY(EX_VAR((opline+1)->result.var), p->key); } - pos++; } - fe_ht->nInternalPointer = pos; - ptr->pos = pos; - ptr->ht = fe_ht; - ptr->h = fe_ht->arData[pos].h; - ptr->key = fe_ht->arData[pos].key; - is_empty = 0; + ZVAL_COPY(EX_VAR(opline->result.var), value); + Z_FE_POS_P(EX_VAR(opline->op1.var)) = pos + 1; + 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)) == NULL) { + /* plain object */ + zend_object *zobj = Z_OBJ_P(array); + + fe_ht = Z_OBJPROP_P(array); + 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 */ + ZEND_VM_JMP(OP_JMP_ADDR(opline, opline->op2)); + } + + p = fe_ht->arData + pos; + value = &p->val; + if (Z_TYPE_P(value) == IS_UNDEF) { + pos++; + continue; + } else if (Z_TYPE_P(value) == IS_INDIRECT) { + value = Z_INDIRECT_P(value); + if (Z_TYPE_P(value) == IS_UNDEF) { + pos++; + continue; + } + } + if (!p->key || + zend_check_property_access(Z_OBJ_P(array), p->key) == SUCCESS) { + break; + } + pos++; + } + if (opline->extended_value) { + if (UNEXPECTED(!p->key)) { + ZVAL_LONG(EX_VAR((opline+1)->result.var), p->h); + } else if (p->key->val[0]) { + ZVAL_STR_COPY(EX_VAR((opline+1)->result.var), p->key); + } else { + const char *class_name, *prop_name; + size_t prop_name_len; + zend_unmangle_property_name_ex( + p->key, &class_name, &prop_name, &prop_name_len); + ZVAL_STRINGL(EX_VAR((opline+1)->result.var), prop_name, prop_name_len); + } + } + ZVAL_COPY(EX_VAR(opline->result.var), value); + while (1) { + pos++; + if (pos >= fe_ht->nNumUsed) { + pos = INVALID_IDX; + break; + } + 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)) && + (Z_TYPE_P(array) != IS_OBJECT || + !p->key || + zend_check_property_access(Z_OBJ_P(array), p->key) == SUCCESS)) { + break; + } + } + 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 { + /* !iter happens from exception */ + if (iter && ++iter->index > 0) { + /* This could cause an endless loop if index becomes zero again. + * In case that ever happens we need an additional flag. */ + iter->funcs->move_forward(iter); + if (UNEXPECTED(EG(exception) != NULL)) { + zval_ptr_dtor(array); + HANDLE_EXCEPTION(); + } + } + /* If index is zero we come from FE_RESET and checked valid() already. */ + if (!iter || (iter->index > 0 && iter->funcs->valid(iter) == FAILURE)) { + /* reached end of iteration */ + if (UNEXPECTED(EG(exception) != NULL)) { + zval_ptr_dtor(array); + HANDLE_EXCEPTION(); + } + ZEND_VM_JMP(OP_JMP_ADDR(opline, opline->op2)); + } + value = iter->funcs->get_current_data(iter); + if (UNEXPECTED(EG(exception) != NULL)) { + zval_ptr_dtor(array); + HANDLE_EXCEPTION(); + } + if (!value) { + /* failure in get_current_data */ + ZEND_VM_JMP(OP_JMP_ADDR(opline, opline->op2)); + } + ZVAL_COPY(EX_VAR(opline->result.var), value); + if (opline->extended_value) { + if (iter->funcs->get_current_key) { + iter->funcs->get_current_key(iter, EX_VAR((opline+1)->result.var)); + if (UNEXPECTED(EG(exception) != NULL)) { + zval_ptr_dtor(array); + HANDLE_EXCEPTION(); + } + } else { + ZVAL_LONG(EX_VAR((opline+1)->result.var), iter->index); + } + } + ZEND_VM_INC_OPCODE(); + ZEND_VM_NEXT_OPCODE(); + } } else { zend_error(E_WARNING, "Invalid argument supplied for foreach()"); - is_empty = 1; - } - - if (IS_VAR == IS_VAR && opline->extended_value & ZEND_FE_FETCH_BYREF) { - if (free_op1) {zval_ptr_dtor_nogc(free_op1);}; - } - if (is_empty) { ZEND_VM_JMP(OP_JMP_ADDR(opline, opline->op2)); - } else { - CHECK_EXCEPTION(); - ZEND_VM_NEXT_OPCODE(); } } -static int ZEND_FASTCALL ZEND_FE_FETCH_SPEC_VAR_HANDLER(ZEND_OPCODE_HANDLER_ARGS) +static int ZEND_FASTCALL ZEND_FE_FETCH_RW_SPEC_VAR_HANDLER(ZEND_OPCODE_HANDLER_ARGS) { USE_OPLINE - zval *array, *array_ref; + zval *array; zval *value; HashTable *fe_ht; - HashPointer *ptr; HashPosition pos; Bucket *p; - array = array_ref = EX_VAR(opline->op1.var); - if (Z_ISREF_P(array)) { - array = Z_REFVAL_P(array); - // TODO: referenced value might be changed to different array ??? - if (Z_IMMUTABLE_P(array)) { - zval_copy_ctor_func(array); - } - } - + array = EX_VAR(opline->op1.var); SAVE_OPLINE(); + ZVAL_DEREF(array); 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(OP_JMP_ADDR(opline, opline->op2)); - } 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 (1) { - if (pos == INVALID_IDX) { - pos = fe_ht->nInternalPointer; - break; - } else if (fe_ht->arData[pos].h == ptr->h && fe_ht->arData[pos].key == ptr->key) { - break; - } - pos = Z_NEXT(fe_ht->arData[pos].val); - } - } - } + 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 */ + fe_ht->nInternalPointer = INVALID_IDX; ZEND_VM_JMP(OP_JMP_ADDR(opline, opline->op2)); } p = fe_ht->arData + pos; value = &p->val; - if (UNEXPECTED(Z_TYPE_P(value) == IS_UNDEF)) { + if (Z_TYPE_P(value) == IS_UNDEF) { pos++; continue; - } else if (UNEXPECTED(Z_TYPE_P(value) == IS_INDIRECT)) { + } else if (Z_TYPE_P(value) == IS_INDIRECT) { value = Z_INDIRECT_P(value); - if (UNEXPECTED(Z_TYPE_P(value) == IS_UNDEF)) { + if (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)); + break; + } + if (opline->extended_value) { + if (!p->key) { + ZVAL_LONG(EX_VAR((opline+1)->result.var), p->h); } 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 { - ZVAL_STR_COPY(EX_VAR((opline+1)->result.var), p->key); - } + ZVAL_STR_COPY(EX_VAR((opline+1)->result.var), p->key); } - break; } - do { + ZVAL_MAKE_REF(value); + Z_ADDREF_P(value); + ZVAL_REF(EX_VAR(opline->result.var), Z_REF_P(value)); + while (1) { pos++; if (pos >= fe_ht->nNumUsed) { - fe_ht->nInternalPointer = ptr->pos = INVALID_IDX; - ZEND_VM_INC_OPCODE(); - ZEND_VM_NEXT_OPCODE(); + pos = INVALID_IDX; + break; } 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; - ptr->key = fe_ht->arData[pos].key; + if (Z_TYPE(p->val) != IS_UNDEF && + (Z_TYPE(p->val) != IS_INDIRECT || + Z_TYPE_P(Z_INDIRECT(p->val)) != IS_UNDEF)) { + break; + } + } + 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)) { @@ -12072,93 +12460,66 @@ static int ZEND_FASTCALL ZEND_FE_FETCH_SPEC_VAR_HANDLER(ZEND_OPCODE_HANDLER_ARG 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(OP_JMP_ADDR(opline, opline->op2)); - } 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 (1) { - if (pos == INVALID_IDX) { - pos = fe_ht->nInternalPointer; - break; - } else if (fe_ht->arData[pos].h == ptr->h && fe_ht->arData[pos].key == ptr->key) { - break; - } - pos = Z_NEXT(fe_ht->arData[pos].val); - } - } - } + 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 */ + fe_ht->nInternalPointer = INVALID_IDX; ZEND_VM_JMP(OP_JMP_ADDR(opline, opline->op2)); } p = fe_ht->arData + pos; value = &p->val; - if (UNEXPECTED(Z_TYPE_P(value) == IS_UNDEF)) { + if (Z_TYPE_P(value) == IS_UNDEF) { pos++; continue; - } else if (UNEXPECTED(Z_TYPE_P(value) == IS_INDIRECT)) { + } else if (Z_TYPE_P(value) == IS_INDIRECT) { value = Z_INDIRECT_P(value); - if (UNEXPECTED(Z_TYPE_P(value) == IS_UNDEF)) { + if (Z_TYPE_P(value) == IS_UNDEF) { pos++; continue; } } - - 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) == SUCCESS) { - if (opline->extended_value & ZEND_FE_FETCH_WITH_KEY) { - if (p->key->val[0]) { - ZVAL_STR_COPY(EX_VAR((opline+1)->result.var), p->key); - } else { - const char *class_name, *prop_name; - size_t prop_name_len; - zend_unmangle_property_name_ex( - p->key, &class_name, &prop_name, &prop_name_len); - ZVAL_STRINGL(EX_VAR((opline+1)->result.var), prop_name, prop_name_len); - } - } + if (!p->key || + zend_check_property_access(Z_OBJ_P(array), p->key) == SUCCESS) { break; } pos++; } - 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) { + if (UNEXPECTED(!p->key)) { + ZVAL_LONG(EX_VAR((opline+1)->result.var), p->h); + } else if (p->key->val[0]) { + ZVAL_STR_COPY(EX_VAR((opline+1)->result.var), p->key); + } else { + const char *class_name, *prop_name; + size_t prop_name_len; + zend_unmangle_property_name_ex( + p->key, &class_name, &prop_name, &prop_name_len); + ZVAL_STRINGL(EX_VAR((opline+1)->result.var), prop_name, prop_name_len); + } } - do { + ZVAL_MAKE_REF(value); + Z_ADDREF_P(value); + ZVAL_REF(EX_VAR(opline->result.var), Z_REF_P(value)); + while (1) { pos++; if (pos >= fe_ht->nNumUsed) { - fe_ht->nInternalPointer = ptr->pos = INVALID_IDX; - ZEND_VM_INC_OPCODE(); - ZEND_VM_NEXT_OPCODE(); + pos = INVALID_IDX; + break; } 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) == FAILURE)); - fe_ht->nInternalPointer = ptr->pos = pos; - ptr->h = fe_ht->arData[pos].h; - ptr->key = fe_ht->arData[pos].key; + if ((Z_TYPE(p->val) != IS_UNDEF && + (Z_TYPE(p->val) != IS_INDIRECT || + Z_TYPE_P(Z_INDIRECT(p->val)) != IS_UNDEF)) && + (Z_TYPE_P(array) != IS_OBJECT || + !p->key || + zend_check_property_access(Z_OBJ_P(array), p->key) == SUCCESS)) { + break; + } + } + 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 { @@ -12168,7 +12529,7 @@ static int ZEND_FASTCALL ZEND_FE_FETCH_SPEC_VAR_HANDLER(ZEND_OPCODE_HANDLER_ARG * In case that ever happens we need an additional flag. */ iter->funcs->move_forward(iter); if (UNEXPECTED(EG(exception) != NULL)) { - zval_ptr_dtor(array_ref); + zval_ptr_dtor(array); HANDLE_EXCEPTION(); } } @@ -12176,38 +12537,34 @@ static int ZEND_FASTCALL ZEND_FE_FETCH_SPEC_VAR_HANDLER(ZEND_OPCODE_HANDLER_ARG if (!iter || (iter->index > 0 && iter->funcs->valid(iter) == FAILURE)) { /* reached end of iteration */ if (UNEXPECTED(EG(exception) != NULL)) { - zval_ptr_dtor(array_ref); + zval_ptr_dtor(array); HANDLE_EXCEPTION(); } ZEND_VM_JMP(OP_JMP_ADDR(opline, opline->op2)); } value = iter->funcs->get_current_data(iter); if (UNEXPECTED(EG(exception) != NULL)) { - zval_ptr_dtor(array_ref); + zval_ptr_dtor(array); HANDLE_EXCEPTION(); } if (!value) { /* failure in get_current_data */ ZEND_VM_JMP(OP_JMP_ADDR(opline, opline->op2)); } - 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 (opline->extended_value) { if (iter->funcs->get_current_key) { iter->funcs->get_current_key(iter, EX_VAR((opline+1)->result.var)); if (UNEXPECTED(EG(exception) != NULL)) { - zval_ptr_dtor(array_ref); + zval_ptr_dtor(array); HANDLE_EXCEPTION(); } } else { ZVAL_LONG(EX_VAR((opline+1)->result.var), iter->index); } } + 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(); } @@ -24064,194 +24421,273 @@ static int ZEND_FASTCALL ZEND_INCLUDE_OR_EVAL_SPEC_CV_HANDLER(ZEND_OPCODE_HANDL ZEND_VM_NEXT_OPCODE(); } -static int ZEND_FASTCALL ZEND_FE_RESET_SPEC_CV_HANDLER(ZEND_OPCODE_HANDLER_ARGS) +static int ZEND_FASTCALL ZEND_FE_RESET_R_SPEC_CV_HANDLER(ZEND_OPCODE_HANDLER_ARGS) { USE_OPLINE - zval *array_ptr, *array_ref, iterator, tmp; + zval *array_ptr, *result; HashTable *fe_ht; - zend_object_iterator *iter = NULL; - zend_class_entry *ce = NULL; - zend_bool is_empty = 0; SAVE_OPLINE(); - if ((IS_CV == IS_CV || IS_CV == IS_VAR) && - (opline->extended_value & ZEND_FE_FETCH_BYREF)) { - array_ptr = array_ref = _get_zval_ptr_cv_BP_VAR_R(execute_data, opline->op1.var); - ZVAL_DEREF(array_ptr); - if (Z_TYPE_P(array_ptr) == IS_ARRAY) { - SEPARATE_ARRAY(array_ptr); - if (!Z_ISREF_P(array_ref)) { - ZVAL_NEW_REF(array_ref, array_ref); - array_ptr = Z_REFVAL_P(array_ref); - } - if (Z_REFCOUNTED_P(array_ref)) Z_ADDREF_P(array_ref); - } else if (Z_TYPE_P(array_ptr) == IS_OBJECT) { - ce = Z_OBJCE_P(array_ptr); - if (ce->get_iterator == NULL) { + array_ptr = _get_zval_ptr_cv_deref_BP_VAR_R(execute_data, opline->op1.var); + if (EXPECTED(Z_TYPE_P(array_ptr) == IS_ARRAY)) { + result = EX_VAR(opline->result.var); + ZVAL_COPY_VALUE(result, array_ptr); + if (IS_CV != IS_TMP_VAR && Z_OPT_REFCOUNTED_P(result)) { + Z_ADDREF_P(array_ptr); + } + Z_FE_POS_P(result) = 0; + + CHECK_EXCEPTION(); + ZEND_VM_NEXT_OPCODE(); + } else if (IS_CV != IS_CONST && EXPECTED(Z_TYPE_P(array_ptr) == IS_OBJECT)) { + if (!Z_OBJCE_P(array_ptr)->get_iterator) { + HashPosition pos = 0; + Bucket *p; + + result = EX_VAR(opline->result.var); + ZVAL_COPY_VALUE(result, array_ptr); + if (IS_CV != IS_TMP_VAR) { Z_ADDREF_P(array_ptr); } - array_ref = array_ptr; - } else { - if (Z_REFCOUNTED_P(array_ref)) Z_ADDREF_P(array_ref); - } - } else { - array_ptr = array_ref = _get_zval_ptr_cv_BP_VAR_R(execute_data, opline->op1.var); - if (IS_CV & (IS_VAR|IS_CV)) { - ZVAL_DEREF(array_ptr); - } - if (IS_CV == IS_TMP_VAR) { - ZVAL_COPY_VALUE(&tmp, array_ptr); - if (Z_OPT_IMMUTABLE_P(&tmp)) { - zval_copy_ctor_func(&tmp); + fe_ht = Z_OBJPROP_P(array_ptr); + pos = 0; + 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; + if ((Z_TYPE(p->val) != IS_UNDEF && + (Z_TYPE(p->val) != IS_INDIRECT || + Z_TYPE_P(Z_INDIRECT(p->val)) != IS_UNDEF)) && + (!p->key || + zend_check_property_access(Z_OBJ_P(array_ptr), p->key) == SUCCESS)) { + break; + } + pos++; } - array_ref = array_ptr = &tmp; - if (Z_TYPE_P(array_ptr) == IS_OBJECT) { - ce = Z_OBJCE_P(array_ptr); - if (ce && ce->get_iterator) { - Z_DELREF_P(array_ref); + fe_ht->nInternalPointer = pos; + Z_FE_ITER_P(EX_VAR(opline->result.var)) = zend_hash_iterator_add(fe_ht, pos); + + CHECK_EXCEPTION(); + ZEND_VM_NEXT_OPCODE(); + } else { + zend_class_entry *ce = Z_OBJCE_P(array_ptr); + zend_object_iterator *iter = ce->get_iterator(ce, array_ptr, 0); + zend_bool is_empty; + + if (UNEXPECTED(!iter) || UNEXPECTED(EG(exception))) { + + if (!EG(exception)) { + zend_throw_exception_ex(NULL, 0, "Object of type %s did not create an Iterator", ce->name->val); } + zend_throw_exception_internal(NULL); + HANDLE_EXCEPTION(); } - } else if (Z_TYPE_P(array_ptr) == IS_OBJECT) { - ce = Z_OBJCE_P(array_ptr); - if (!ce->get_iterator) { - if (IS_CV == IS_CV) { - Z_ADDREF_P(array_ref); + + iter->index = 0; + if (iter->funcs->rewind) { + iter->funcs->rewind(iter); + if (UNEXPECTED(EG(exception) != NULL)) { + OBJ_RELEASE(&iter->std); + + HANDLE_EXCEPTION(); } } - } else if (Z_IMMUTABLE_P(array_ref)) { - if (IS_CV == IS_CV) { - zval_copy_ctor_func(array_ref); - Z_ADDREF_P(array_ref); + + is_empty = iter->funcs->valid(iter) != SUCCESS; + + if (UNEXPECTED(EG(exception) != NULL)) { + OBJ_RELEASE(&iter->std); + + HANDLE_EXCEPTION(); + } + iter->index = -1; /* will be set to 0 before using next handler */ + + ZVAL_OBJ(EX_VAR(opline->result.var), &iter->std); + Z_FE_ITER_P(EX_VAR(opline->result.var)) = (uint32_t)-1; + + if (is_empty) { + ZEND_VM_JMP(OP_JMP_ADDR(opline, opline->op2)); } else { - ZVAL_COPY_VALUE(&tmp, array_ref); - zval_copy_ctor_func(&tmp); - array_ptr = array_ref = &tmp; - } - } else if (Z_REFCOUNTED_P(array_ref)) { - if (IS_CV == IS_CONST || - (IS_CV == IS_CV && - !Z_ISREF_P(array_ref) && - Z_REFCOUNT_P(array_ref) > 1) || - (IS_CV == IS_VAR && - !Z_ISREF_P(array_ref) && - Z_REFCOUNT_P(array_ref) > 2)) { - if (IS_CV == IS_VAR) { - Z_DELREF_P(array_ref); - } - ZVAL_DUP(&tmp, array_ref); - array_ptr = array_ref = &tmp; - } else if (IS_CV == IS_CV || IS_CV == IS_VAR) { - if (Z_ISREF_P(array_ref) && Z_REFCOUNT_P(array_ref) == 1) { - ZVAL_UNREF(array_ref); - array_ptr = array_ref; - } - if (Z_IMMUTABLE_P(array_ptr)) { - zval_copy_ctor_func(array_ptr); - } else if (Z_ISREF_P(array_ref) && - Z_COPYABLE_P(array_ptr) && - Z_REFCOUNT_P(array_ptr) > 1) { - Z_DELREF_P(array_ptr); - zval_copy_ctor_func(array_ptr); - } - if (IS_CV == IS_CV) { - Z_ADDREF_P(array_ref); - } + 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; + + ZEND_VM_JMP(OP_JMP_ADDR(opline, opline->op2)); } +} + +static int ZEND_FASTCALL ZEND_FE_RESET_RW_SPEC_CV_HANDLER(ZEND_OPCODE_HANDLER_ARGS) +{ + USE_OPLINE - if (ce && ce->get_iterator) { - iter = ce->get_iterator(ce, array_ptr, opline->extended_value & ZEND_FE_FETCH_BYREF); + zval *array_ptr, *array_ref; + HashTable *fe_ht; + HashPosition pos = 0; + Bucket *p; - if (IS_CV == IS_VAR && !(opline->extended_value & ZEND_FE_FETCH_BYREF)) { + SAVE_OPLINE(); + if (IS_CV == IS_VAR || IS_CV == IS_CV) { + array_ref = array_ptr = _get_zval_ptr_cv_BP_VAR_R(execute_data, opline->op1.var); + if (Z_ISREF_P(array_ref)) { + array_ptr = Z_REFVAL_P(array_ref); } - if (iter && EXPECTED(EG(exception) == NULL)) { - ZVAL_OBJ(&iterator, &iter->std); - array_ptr = array_ref = &iterator; + } else { + array_ref = array_ptr = _get_zval_ptr_cv_BP_VAR_R(execute_data, opline->op1.var); + } + + if (EXPECTED(Z_TYPE_P(array_ptr) == IS_ARRAY)) { + if (IS_CV == IS_VAR || IS_CV == IS_CV) { + if (array_ptr == array_ref) { + ZVAL_NEW_REF(array_ref, array_ref); + array_ptr = Z_REFVAL_P(array_ref); + } + Z_ADDREF_P(array_ref); + ZVAL_COPY_VALUE(EX_VAR(opline->result.var), array_ref); + } else { + array_ptr = EX_VAR(opline->result.var); + ZVAL_COPY_VALUE(array_ptr, array_ref); + } + if (IS_CV == IS_CONST) { + zval_copy_ctor_func(array_ptr); } else { - if (IS_CV == IS_VAR && opline->extended_value & ZEND_FE_FETCH_BYREF) { + SEPARATE_ARRAY(array_ptr); + } + fe_ht = Z_ARRVAL_P(array_ptr); + 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)); } - if (!EG(exception)) { - zend_throw_exception_ex(NULL, 0, "Object of type %s did not create an Iterator", ce->name->val); + 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)) { + break; } - zend_throw_exception_internal(NULL); - HANDLE_EXCEPTION(); + pos++; } - } + fe_ht->nInternalPointer = pos; + Z_FE_ITER_P(EX_VAR(opline->result.var)) = zend_hash_iterator_add(fe_ht, pos); + + CHECK_EXCEPTION(); + ZEND_VM_NEXT_OPCODE(); + } else if (IS_CV != IS_CONST && EXPECTED(Z_TYPE_P(array_ptr) == IS_OBJECT)) { + if (!Z_OBJCE_P(array_ptr)->get_iterator) { + if (IS_CV == IS_VAR || IS_CV == IS_CV) { + if (array_ptr == array_ref) { + ZVAL_NEW_REF(array_ref, array_ref); + array_ptr = Z_REFVAL_P(array_ref); + } + Z_ADDREF_P(array_ref); + ZVAL_COPY_VALUE(EX_VAR(opline->result.var), array_ref); + } else { + array_ptr = EX_VAR(opline->result.var); + ZVAL_COPY_VALUE(array_ptr, array_ref); + } + fe_ht = Z_OBJPROP_P(array_ptr); + 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; + if ((Z_TYPE(p->val) != IS_UNDEF && + (Z_TYPE(p->val) != IS_INDIRECT || + Z_TYPE_P(Z_INDIRECT(p->val)) != IS_UNDEF)) && + (!p->key || + zend_check_property_access(Z_OBJ_P(array_ptr), p->key) == SUCCESS)) { + break; + } + pos++; + } + fe_ht->nInternalPointer = pos; + Z_FE_ITER_P(EX_VAR(opline->result.var)) = zend_hash_iterator_add(fe_ht, pos); - ZVAL_COPY_VALUE(EX_VAR(opline->result.var), array_ref); + CHECK_EXCEPTION(); + ZEND_VM_NEXT_OPCODE(); + } else { + zend_class_entry *ce = Z_OBJCE_P(array_ptr); + zend_object_iterator *iter = ce->get_iterator(ce, array_ptr, 1); + zend_bool is_empty; - if (iter) { - iter->index = 0; - if (iter->funcs->rewind) { - iter->funcs->rewind(iter); - if (UNEXPECTED(EG(exception) != NULL)) { - zval_ptr_dtor(array_ref); - if (IS_CV == IS_VAR && opline->extended_value & ZEND_FE_FETCH_BYREF) { + if (UNEXPECTED(!iter) || UNEXPECTED(EG(exception))) { + if (IS_CV == IS_VAR) { + + } else { } + if (!EG(exception)) { + zend_throw_exception_ex(NULL, 0, "Object of type %s did not create an Iterator", ce->name->val); + } + zend_throw_exception_internal(NULL); HANDLE_EXCEPTION(); } - } - is_empty = iter->funcs->valid(iter) != SUCCESS; - if (UNEXPECTED(EG(exception) != NULL)) { - zval_ptr_dtor(array_ref); - if (IS_CV == IS_VAR && opline->extended_value & ZEND_FE_FETCH_BYREF) { + iter->index = 0; + if (iter->funcs->rewind) { + iter->funcs->rewind(iter); + if (UNEXPECTED(EG(exception) != NULL)) { + OBJ_RELEASE(&iter->std); + if (IS_CV == IS_VAR) { + + } else { + + } + HANDLE_EXCEPTION(); + } } - HANDLE_EXCEPTION(); - } - iter->index = -1; /* will be set to 0 before using next handler */ - } else if ((fe_ht = HASH_OF(array_ptr)) != NULL) { - 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_FETCH_BYREF) { + is_empty = iter->funcs->valid(iter) != SUCCESS; + + if (UNEXPECTED(EG(exception) != NULL)) { + OBJ_RELEASE(&iter->std); + if (IS_CV == IS_VAR) { + + } else { } - ZEND_VM_JMP(OP_JMP_ADDR(opline, opline->op2)); + HANDLE_EXCEPTION(); } - 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; + iter->index = -1; /* will be set to 0 before using next handler */ + + ZVAL_OBJ(EX_VAR(opline->result.var), &iter->std); + Z_FE_ITER_P(EX_VAR(opline->result.var)) = (uint32_t)-1; + + if (IS_CV == IS_VAR) { + + } else { + } - if (!ce || - !p->key || - zend_check_property_access(Z_OBJ_P(array_ptr), p->key) == SUCCESS) { - break; + if (is_empty) { + ZEND_VM_JMP(OP_JMP_ADDR(opline, opline->op2)); + } else { + CHECK_EXCEPTION(); + ZEND_VM_NEXT_OPCODE(); } - pos++; } - fe_ht->nInternalPointer = pos; - ptr->pos = pos; - ptr->ht = fe_ht; - ptr->h = fe_ht->arData[pos].h; - ptr->key = fe_ht->arData[pos].key; - is_empty = 0; } else { zend_error(E_WARNING, "Invalid argument supplied for foreach()"); - is_empty = 1; - } + ZVAL_UNDEF(EX_VAR(opline->result.var)); + Z_FE_ITER_P(EX_VAR(opline->result.var)) = (uint32_t)-1; + if (IS_CV == IS_VAR) { - if (IS_CV == IS_VAR && opline->extended_value & ZEND_FE_FETCH_BYREF) { + } else { - } - if (is_empty) { + } ZEND_VM_JMP(OP_JMP_ADDR(opline, opline->op2)); - } else { - CHECK_EXCEPTION(); - ZEND_VM_NEXT_OPCODE(); } } @@ -32975,6 +33411,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_TYPE_P(var) != IS_ARRAY && 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 @@ -38295,31 +38746,31 @@ void zend_init_opcodes_handlers(void) ZEND_UNSET_OBJ_SPEC_CV_TMPVAR_HANDLER, ZEND_NULL_HANDLER, ZEND_UNSET_OBJ_SPEC_CV_CV_HANDLER, - ZEND_FE_RESET_SPEC_CONST_HANDLER, - ZEND_FE_RESET_SPEC_CONST_HANDLER, - ZEND_FE_RESET_SPEC_CONST_HANDLER, - ZEND_FE_RESET_SPEC_CONST_HANDLER, - ZEND_FE_RESET_SPEC_CONST_HANDLER, - ZEND_FE_RESET_SPEC_TMP_HANDLER, - ZEND_FE_RESET_SPEC_TMP_HANDLER, - ZEND_FE_RESET_SPEC_TMP_HANDLER, - ZEND_FE_RESET_SPEC_TMP_HANDLER, - ZEND_FE_RESET_SPEC_TMP_HANDLER, - ZEND_FE_RESET_SPEC_VAR_HANDLER, - ZEND_FE_RESET_SPEC_VAR_HANDLER, - ZEND_FE_RESET_SPEC_VAR_HANDLER, - ZEND_FE_RESET_SPEC_VAR_HANDLER, - ZEND_FE_RESET_SPEC_VAR_HANDLER, + ZEND_FE_RESET_R_SPEC_CONST_HANDLER, + ZEND_FE_RESET_R_SPEC_CONST_HANDLER, + ZEND_FE_RESET_R_SPEC_CONST_HANDLER, + ZEND_FE_RESET_R_SPEC_CONST_HANDLER, + ZEND_FE_RESET_R_SPEC_CONST_HANDLER, + ZEND_FE_RESET_R_SPEC_TMP_HANDLER, + ZEND_FE_RESET_R_SPEC_TMP_HANDLER, + ZEND_FE_RESET_R_SPEC_TMP_HANDLER, + ZEND_FE_RESET_R_SPEC_TMP_HANDLER, + ZEND_FE_RESET_R_SPEC_TMP_HANDLER, + ZEND_FE_RESET_R_SPEC_VAR_HANDLER, + ZEND_FE_RESET_R_SPEC_VAR_HANDLER, + ZEND_FE_RESET_R_SPEC_VAR_HANDLER, + ZEND_FE_RESET_R_SPEC_VAR_HANDLER, + ZEND_FE_RESET_R_SPEC_VAR_HANDLER, ZEND_NULL_HANDLER, ZEND_NULL_HANDLER, ZEND_NULL_HANDLER, ZEND_NULL_HANDLER, ZEND_NULL_HANDLER, - ZEND_FE_RESET_SPEC_CV_HANDLER, - ZEND_FE_RESET_SPEC_CV_HANDLER, - ZEND_FE_RESET_SPEC_CV_HANDLER, - ZEND_FE_RESET_SPEC_CV_HANDLER, - ZEND_FE_RESET_SPEC_CV_HANDLER, + ZEND_FE_RESET_R_SPEC_CV_HANDLER, + ZEND_FE_RESET_R_SPEC_CV_HANDLER, + ZEND_FE_RESET_R_SPEC_CV_HANDLER, + ZEND_FE_RESET_R_SPEC_CV_HANDLER, + ZEND_FE_RESET_R_SPEC_CV_HANDLER, ZEND_NULL_HANDLER, ZEND_NULL_HANDLER, ZEND_NULL_HANDLER, @@ -38330,11 +38781,11 @@ void zend_init_opcodes_handlers(void) ZEND_NULL_HANDLER, ZEND_NULL_HANDLER, ZEND_NULL_HANDLER, - ZEND_FE_FETCH_SPEC_VAR_HANDLER, - ZEND_FE_FETCH_SPEC_VAR_HANDLER, - ZEND_FE_FETCH_SPEC_VAR_HANDLER, - ZEND_FE_FETCH_SPEC_VAR_HANDLER, - ZEND_FE_FETCH_SPEC_VAR_HANDLER, + ZEND_FE_FETCH_R_SPEC_VAR_HANDLER, + ZEND_FE_FETCH_R_SPEC_VAR_HANDLER, + ZEND_FE_FETCH_R_SPEC_VAR_HANDLER, + ZEND_FE_FETCH_R_SPEC_VAR_HANDLER, + ZEND_FE_FETCH_R_SPEC_VAR_HANDLER, ZEND_NULL_HANDLER, ZEND_NULL_HANDLER, ZEND_NULL_HANDLER, @@ -39495,11 +39946,31 @@ void zend_init_opcodes_handlers(void) ZEND_NULL_HANDLER, ZEND_VERIFY_RETURN_TYPE_SPEC_CV_UNUSED_HANDLER, ZEND_NULL_HANDLER, + ZEND_FE_RESET_RW_SPEC_CONST_HANDLER, + ZEND_FE_RESET_RW_SPEC_CONST_HANDLER, + ZEND_FE_RESET_RW_SPEC_CONST_HANDLER, + ZEND_FE_RESET_RW_SPEC_CONST_HANDLER, + ZEND_FE_RESET_RW_SPEC_CONST_HANDLER, + ZEND_FE_RESET_RW_SPEC_TMP_HANDLER, + ZEND_FE_RESET_RW_SPEC_TMP_HANDLER, + ZEND_FE_RESET_RW_SPEC_TMP_HANDLER, + ZEND_FE_RESET_RW_SPEC_TMP_HANDLER, + ZEND_FE_RESET_RW_SPEC_TMP_HANDLER, + ZEND_FE_RESET_RW_SPEC_VAR_HANDLER, + ZEND_FE_RESET_RW_SPEC_VAR_HANDLER, + ZEND_FE_RESET_RW_SPEC_VAR_HANDLER, + ZEND_FE_RESET_RW_SPEC_VAR_HANDLER, + ZEND_FE_RESET_RW_SPEC_VAR_HANDLER, ZEND_NULL_HANDLER, ZEND_NULL_HANDLER, ZEND_NULL_HANDLER, ZEND_NULL_HANDLER, ZEND_NULL_HANDLER, + ZEND_FE_RESET_RW_SPEC_CV_HANDLER, + ZEND_FE_RESET_RW_SPEC_CV_HANDLER, + ZEND_FE_RESET_RW_SPEC_CV_HANDLER, + ZEND_FE_RESET_RW_SPEC_CV_HANDLER, + ZEND_FE_RESET_RW_SPEC_CV_HANDLER, ZEND_NULL_HANDLER, ZEND_NULL_HANDLER, ZEND_NULL_HANDLER, @@ -39510,6 +39981,11 @@ void zend_init_opcodes_handlers(void) ZEND_NULL_HANDLER, ZEND_NULL_HANDLER, ZEND_NULL_HANDLER, + ZEND_FE_FETCH_RW_SPEC_VAR_HANDLER, + ZEND_FE_FETCH_RW_SPEC_VAR_HANDLER, + ZEND_FE_FETCH_RW_SPEC_VAR_HANDLER, + ZEND_FE_FETCH_RW_SPEC_VAR_HANDLER, + ZEND_FE_FETCH_RW_SPEC_VAR_HANDLER, ZEND_NULL_HANDLER, ZEND_NULL_HANDLER, ZEND_NULL_HANDLER, @@ -39525,41 +40001,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_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_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 c3006e6036..d7c5db6966 100644 --- a/Zend/zend_vm_opcodes.c +++ b/Zend/zend_vm_opcodes.c @@ -99,8 +99,8 @@ const char *zend_vm_opcodes_map[170] = { "ZEND_UNSET_VAR", "ZEND_UNSET_DIM", "ZEND_UNSET_OBJ", - "ZEND_FE_RESET", - "ZEND_FE_FETCH", + "ZEND_FE_RESET_R", + "ZEND_FE_FETCH_R", "ZEND_EXIT", "ZEND_FETCH_R", "ZEND_FETCH_DIM_R", @@ -147,9 +147,9 @@ const char *zend_vm_opcodes_map[170] = { "ZEND_DEFINED", "ZEND_TYPE_CHECK", "ZEND_VERIFY_RETURN_TYPE", - NULL, - NULL, - NULL, + "ZEND_FE_RESET_RW", + "ZEND_FE_FETCH_RW", + "ZEND_FE_FREE", NULL, NULL, NULL, diff --git a/Zend/zend_vm_opcodes.h b/Zend/zend_vm_opcodes.h index f0fc7c7182..aa4afbd984 100644 --- a/Zend/zend_vm_opcodes.h +++ b/Zend/zend_vm_opcodes.h @@ -101,8 +101,8 @@ END_EXTERN_C() #define ZEND_UNSET_VAR 74 #define ZEND_UNSET_DIM 75 #define ZEND_UNSET_OBJ 76 -#define ZEND_FE_RESET 77 -#define ZEND_FE_FETCH 78 +#define ZEND_FE_RESET_R 77 +#define ZEND_FE_FETCH_R 78 #define ZEND_EXIT 79 #define ZEND_FETCH_R 80 #define ZEND_FETCH_DIM_R 81 @@ -149,6 +149,9 @@ END_EXTERN_C() #define ZEND_DEFINED 122 #define ZEND_TYPE_CHECK 123 #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 7948990f81..4db48944e1 100644 --- a/ext/opcache/Optimizer/block_pass.c +++ b/ext/opcache/Optimizer/block_pass.c @@ -166,14 +166,16 @@ static int find_code_blocks(zend_op_array *op_array, zend_cfg *cfg, zend_optimiz case ZEND_JMPNZ: case ZEND_JMPZ_EX: case ZEND_JMPNZ_EX: - case ZEND_FE_RESET: + case ZEND_FE_RESET_R: + case ZEND_FE_RESET_RW: case ZEND_NEW: case ZEND_JMP_SET: case ZEND_COALESCE: START_BLOCK_OP(ZEND_OP2(opline).opline_num); START_BLOCK_OP(opno + 1); break; - case ZEND_FE_FETCH: + case ZEND_FE_FETCH_R: + case ZEND_FE_FETCH_RW: START_BLOCK_OP(ZEND_OP2(opline).opline_num); START_BLOCK_OP(opno + 2); break; @@ -205,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; @@ -225,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]; @@ -293,11 +299,13 @@ static int find_code_blocks(zend_op_array *op_array, zend_cfg *cfg, zend_optimiz case ZEND_JMPNZ: case ZEND_JMPZ_EX: case ZEND_JMPNZ_EX: - case ZEND_FE_RESET: + case ZEND_FE_RESET_R: + case ZEND_FE_RESET_RW: case ZEND_NEW: case ZEND_JMP_SET: case ZEND_COALESCE: - case ZEND_FE_FETCH: + case ZEND_FE_FETCH_R: + case ZEND_FE_FETCH_RW: cur_block->op2_to = &blocks[ZEND_OP2(opline).opline_num]; /* break missing intentionally */ default: @@ -619,7 +627,7 @@ static void zend_optimize_block(zend_code_block *block, zend_op_array *op_array, ZEND_OP1_TYPE(VAR_SOURCE(opline->op1)) == IS_CONST && opline->opcode != ZEND_CASE && /* CASE _always_ expects variable */ opline->opcode != ZEND_FETCH_LIST && - opline->opcode != ZEND_FE_RESET && + (opline->opcode != ZEND_FE_RESET_R || opline->opcode != ZEND_FE_RESET_RW) && opline->opcode != ZEND_FREE ) { zend_op *src = VAR_SOURCE(opline->op1); diff --git a/ext/opcache/Optimizer/nop_removal.c b/ext/opcache/Optimizer/nop_removal.c index 0bb8cce17f..dccd010ae4 100644 --- a/ext/opcache/Optimizer/nop_removal.c +++ b/ext/opcache/Optimizer/nop_removal.c @@ -93,8 +93,10 @@ void zend_optimizer_nop_removal(zend_op_array *op_array) case ZEND_JMPNZ: case ZEND_JMPZ_EX: case ZEND_JMPNZ_EX: - case ZEND_FE_FETCH: - case ZEND_FE_RESET: + case ZEND_FE_FETCH_R: + case ZEND_FE_FETCH_RW: + case ZEND_FE_RESET_R: + case ZEND_FE_RESET_RW: case ZEND_NEW: case ZEND_JMP_SET: case ZEND_COALESCE: diff --git a/ext/opcache/Optimizer/optimize_temp_vars_5.c b/ext/opcache/Optimizer/optimize_temp_vars_5.c index 27ba6bad8e..dc93ce2f4c 100644 --- a/ext/opcache/Optimizer/optimize_temp_vars_5.c +++ b/ext/opcache/Optimizer/optimize_temp_vars_5.c @@ -66,16 +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 HashPointer on VM stack */ - if (opline->opcode == ZEND_OP_DATA && - (opline-1)->opcode == ZEND_FE_FETCH && - opline->op1_type == IS_TMP_VAR) { - start_of_T[VAR_NUM(ZEND_OP1(opline).var) - offset] = opline; - if (sizeof(HashPointer) > sizeof(zval)) { - /* Make shure 1 zval is enough for HashPointer (2 must be enough) */ - start_of_T[VAR_NUM(ZEND_OP1(opline).var) + 1 - offset] = opline; - } - } opline--; } @@ -88,25 +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 && - opline->op1_type == IS_TMP_VAR) { - max++; - ZEND_OP1(opline).var = NUM_VAR(max + offset); - if (sizeof(HashPointer) > sizeof(zval)) { - /* Make shure 1 zval is enough for HashPointer (2 must be enough) */ - max++; - } - } 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/pass1_5.c b/ext/opcache/Optimizer/pass1_5.c index 348fcb8efb..341e80501b 100644 --- a/ext/opcache/Optimizer/pass1_5.c +++ b/ext/opcache/Optimizer/pass1_5.c @@ -602,8 +602,10 @@ void zend_optimizer_pass1(zend_op_array *op_array, zend_optimizer_ctx *ctx) case ZEND_JMPNZ: case ZEND_JMPZ_EX: case ZEND_JMPNZ_EX: - case ZEND_FE_RESET: - case ZEND_FE_FETCH: + case ZEND_FE_RESET_R: + case ZEND_FE_RESET_RW: + case ZEND_FE_FETCH_R: + case ZEND_FE_FETCH_RW: case ZEND_NEW: case ZEND_JMP_SET: case ZEND_COALESCE: 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/opcache/Optimizer/pass3.c b/ext/opcache/Optimizer/pass3.c index c3a55b319f..3019b274e9 100644 --- a/ext/opcache/Optimizer/pass3.c +++ b/ext/opcache/Optimizer/pass3.c @@ -328,7 +328,8 @@ continue_jmp_ex_optimization: op->opcode == ZEND_RETURN || op->opcode == ZEND_RETURN_BY_REF || op->opcode == ZEND_FAST_RET || - op->opcode == ZEND_FE_FETCH || + op->opcode == ZEND_FE_FETCH_R || + op->opcode == ZEND_FE_FETCH_RW || op->opcode == ZEND_EXIT) { break; } @@ -363,7 +364,8 @@ continue_jmp_ex_optimization: op->opcode == ZEND_RETURN || op->opcode == ZEND_RETURN_BY_REF || op->opcode == ZEND_FAST_RET || - op->opcode == ZEND_FE_FETCH || + op->opcode == ZEND_FE_FETCH_R || + op->opcode == ZEND_FE_FETCH_RW || op->opcode == ZEND_EXIT) { break; } diff --git a/ext/opcache/Optimizer/zend_optimizer.c b/ext/opcache/Optimizer/zend_optimizer.c index 54063d0ed3..a8506fb078 100644 --- a/ext/opcache/Optimizer/zend_optimizer.c +++ b/ext/opcache/Optimizer/zend_optimizer.c @@ -450,8 +450,10 @@ static void zend_accel_optimize(zend_op_array *op_array, case ZEND_JMP_SET: case ZEND_COALESCE: case ZEND_NEW: - case ZEND_FE_RESET: - case ZEND_FE_FETCH: + case ZEND_FE_RESET_R: + case ZEND_FE_RESET_RW: + case ZEND_FE_FETCH_R: + case ZEND_FE_FETCH_RW: ZEND_PASS_TWO_UNDO_JMP_TARGET(op_array, opline, ZEND_OP2(opline)); break; } @@ -488,8 +490,10 @@ static void zend_accel_optimize(zend_op_array *op_array, case ZEND_JMP_SET: case ZEND_COALESCE: case ZEND_NEW: - case ZEND_FE_RESET: - case ZEND_FE_FETCH: + case ZEND_FE_RESET_R: + case ZEND_FE_RESET_RW: + case ZEND_FE_FETCH_R: + case ZEND_FE_FETCH_RW: ZEND_PASS_TWO_UPDATE_JMP_TARGET(op_array, opline, ZEND_OP2(opline)); break; } diff --git a/ext/opcache/zend_persist.c b/ext/opcache/zend_persist.c index 3d63f96e34..5b32baa64b 100644 --- a/ext/opcache/zend_persist.c +++ b/ext/opcache/zend_persist.c @@ -378,8 +378,10 @@ static void zend_persist_op_array_ex(zend_op_array *op_array, zend_persistent_sc case ZEND_JMP_SET: case ZEND_COALESCE: case ZEND_NEW: - case ZEND_FE_RESET: - case ZEND_FE_FETCH: + case ZEND_FE_RESET_R: + case ZEND_FE_RESET_RW: + case ZEND_FE_FETCH_R: + case ZEND_FE_FETCH_RW: ZEND_OP2(opline).jmp_addr = &new_opcodes[ZEND_OP2(opline).jmp_addr - op_array->opcodes]; break; } diff --git a/ext/standard/array.c b/ext/standard/array.c index 966ea0f1f6..f923ad9e7b 100644 --- a/ext/standard/array.c +++ b/ext/standard/array.c @@ -1877,26 +1877,54 @@ static void php_array_data_shuffle(zval *array) /* {{{ */ hash = Z_ARRVAL_P(array); n_left = n_elems; - if (hash->nNumUsed != hash->nNumOfElements) { - for (j = 0, idx = 0; idx < hash->nNumUsed; idx++) { - p = hash->arData + idx; - if (Z_TYPE(p->val) == IS_UNDEF) continue; - if (j != idx) { - hash->arData[j] = *p; + if (EXPECTED(hash->u.v.nIteratorsCount == 0)) { + if (hash->nNumUsed != hash->nNumOfElements) { + for (j = 0, idx = 0; idx < hash->nNumUsed; idx++) { + p = hash->arData + idx; + if (Z_TYPE(p->val) == IS_UNDEF) continue; + if (j != idx) { + hash->arData[j] = *p; + } + j++; } - j++; } - } - while (--n_left) { - rnd_idx = php_rand(); - RAND_RANGE(rnd_idx, 0, n_left, PHP_RAND_MAX); - if (rnd_idx != n_left) { - temp = hash->arData[n_left]; - hash->arData[n_left] = hash->arData[rnd_idx]; - hash->arData[rnd_idx] = temp; + while (--n_left) { + rnd_idx = php_rand(); + RAND_RANGE(rnd_idx, 0, n_left, PHP_RAND_MAX); + if (rnd_idx != n_left) { + temp = hash->arData[n_left]; + hash->arData[n_left] = hash->arData[rnd_idx]; + hash->arData[rnd_idx] = temp; + } + } + } else { + uint32_t iter_pos = zend_hash_iterators_lower_pos(hash, 0); + + if (hash->nNumUsed != hash->nNumOfElements) { + for (j = 0, idx = 0; idx < hash->nNumUsed; idx++) { + p = hash->arData + idx; + if (Z_TYPE(p->val) == IS_UNDEF) continue; + if (j != idx) { + hash->arData[j] = *p; + if (idx == iter_pos) { + zend_hash_iterators_update(hash, idx, j); + iter_pos = zend_hash_iterators_lower_pos(hash, iter_pos + 1); + } + } + j++; + } + } + while (--n_left) { + rnd_idx = php_rand(); + RAND_RANGE(rnd_idx, 0, n_left, PHP_RAND_MAX); + if (rnd_idx != n_left) { + temp = hash->arData[n_left]; + hash->arData[n_left] = hash->arData[rnd_idx]; + hash->arData[rnd_idx] = temp; + zend_hash_iterators_update(hash, rnd_idx, n_left); + } } } - HANDLE_BLOCK_INTERRUPTIONS(); hash->nNumUsed = n_elems; hash->nInternalPointer = 0; @@ -1941,6 +1969,7 @@ static void php_splice(HashTable *in_hash, int offset, int length, HashTable *re uint idx; Bucket *p; /* Pointer to hash bucket */ zval *entry; /* Hash entry */ + uint32_t iter_pos = zend_hash_iterators_lower_pos(in_hash, 0); /* Get number of entries in the input hash */ num_in = zend_hash_num_elements(in_hash); @@ -1966,7 +1995,6 @@ static void php_splice(HashTable *in_hash, int offset, int length, HashTable *re for (pos = 0, idx = 0; pos < offset && idx < in_hash->nNumUsed; idx++) { p = in_hash->arData + idx; if (Z_TYPE(p->val) == IS_UNDEF) continue; - pos++; /* Get entry and increase reference count */ entry = &p->val; @@ -1976,6 +2004,13 @@ static void php_splice(HashTable *in_hash, int offset, int length, HashTable *re } else { zend_hash_add_new(&out_hash, p->key, entry); } + if (idx == iter_pos) { + if (idx != pos) { + zend_hash_iterators_update(in_hash, idx, pos); + } + iter_pos = zend_hash_iterators_lower_pos(in_hash, iter_pos + 1); + } + pos++; } /* If hash for removed entries exists, go until offset+length and copy the entries to it */ @@ -2001,10 +2036,12 @@ static void php_splice(HashTable *in_hash, int offset, int length, HashTable *re } } } else { /* otherwise just skip those entries */ - for ( ; pos < offset + length && idx < in_hash->nNumUsed; idx++) { + int pos2 = pos; + + for ( ; pos2 < offset + length && idx < in_hash->nNumUsed; idx++) { p = in_hash->arData + idx; if (Z_TYPE(p->val) == IS_UNDEF) continue; - pos++; + pos2++; if (p->key == NULL) { zend_hash_index_del(in_hash, p->h); } else { @@ -2016,12 +2053,14 @@ static void php_splice(HashTable *in_hash, int offset, int length, HashTable *re } } } + iter_pos = zend_hash_iterators_lower_pos(in_hash, iter_pos); /* If there are entries to insert.. */ if (replace) { ZEND_HASH_FOREACH_VAL_IND(replace, entry) { if (Z_REFCOUNTED_P(entry)) Z_ADDREF_P(entry); zend_hash_next_index_insert_new(&out_hash, entry); + pos++; } ZEND_HASH_FOREACH_END(); } @@ -2035,13 +2074,31 @@ static void php_splice(HashTable *in_hash, int offset, int length, HashTable *re } else { zend_hash_add_new(&out_hash, p->key, entry); } + if (idx == iter_pos) { + if (idx != pos) { + zend_hash_iterators_update(in_hash, idx, pos); + } + iter_pos = zend_hash_iterators_lower_pos(in_hash, iter_pos + 1); + } + pos++; } - zend_hash_internal_pointer_reset(&out_hash); - + /* replace HashTable data */ + in_hash->u.v.nIteratorsCount = 0; in_hash->pDestructor = NULL; zend_hash_destroy(in_hash); - *in_hash = out_hash; + + in_hash->u.v.flags = out_hash.u.v.flags; + in_hash->nTableSize = out_hash.nTableSize; + in_hash->nTableMask = out_hash.nTableMask; + in_hash->nNumUsed = out_hash.nNumUsed; + in_hash->nNumOfElements = out_hash.nNumOfElements; + in_hash->nNextFreeElement = out_hash.nNextFreeElement; + in_hash->arData = out_hash.arData; + in_hash->arHash = out_hash.arHash; + in_hash->pDestructor = out_hash.pDestructor; + + zend_hash_internal_pointer_reset(in_hash); } /* }}} */ @@ -2194,17 +2251,38 @@ PHP_FUNCTION(array_shift) if (Z_ARRVAL_P(stack)->u.flags & HASH_FLAG_PACKED) { uint32_t k = 0; - for (idx = 0; idx < Z_ARRVAL_P(stack)->nNumUsed; idx++) { - p = Z_ARRVAL_P(stack)->arData + idx; - if (Z_TYPE(p->val) == IS_UNDEF) continue; - if (idx != k) { - Bucket *q = Z_ARRVAL_P(stack)->arData + k; - q->h = k; - q->key = NULL; - ZVAL_COPY_VALUE(&q->val, &p->val); - ZVAL_UNDEF(&p->val); + if (EXPECTED(Z_ARRVAL_P(stack)->u.v.nIteratorsCount == 0)) { + for (idx = 0; idx < Z_ARRVAL_P(stack)->nNumUsed; idx++) { + p = Z_ARRVAL_P(stack)->arData + idx; + if (Z_TYPE(p->val) == IS_UNDEF) continue; + if (idx != k) { + Bucket *q = Z_ARRVAL_P(stack)->arData + k; + q->h = k; + q->key = NULL; + ZVAL_COPY_VALUE(&q->val, &p->val); + ZVAL_UNDEF(&p->val); + } + k++; + } + } else { + uint32_t iter_pos = zend_hash_iterators_lower_pos(Z_ARRVAL_P(stack), 0); + + for (idx = 0; idx < Z_ARRVAL_P(stack)->nNumUsed; idx++) { + p = Z_ARRVAL_P(stack)->arData + idx; + if (Z_TYPE(p->val) == IS_UNDEF) continue; + if (idx != k) { + Bucket *q = Z_ARRVAL_P(stack)->arData + k; + q->h = k; + q->key = NULL; + ZVAL_COPY_VALUE(&q->val, &p->val); + ZVAL_UNDEF(&p->val); + if (idx == iter_pos) { + zend_hash_iterators_update(Z_ARRVAL_P(stack), idx, k); + iter_pos = zend_hash_iterators_lower_pos(Z_ARRVAL_P(stack), iter_pos + 1); + } + } + k++; } - k++; } Z_ARRVAL_P(stack)->nNumUsed = k; Z_ARRVAL_P(stack)->nNextFreeElement = k; @@ -2257,17 +2335,50 @@ PHP_FUNCTION(array_unshift) } zend_hash_next_index_insert_new(&new_hash, &args[i]); } - ZEND_HASH_FOREACH_STR_KEY_VAL(Z_ARRVAL_P(stack), key, value) { - if (key) { - zend_hash_add_new(&new_hash, key, value); - } else { - zend_hash_next_index_insert_new(&new_hash, value); - } - } ZEND_HASH_FOREACH_END(); + if (EXPECTED(Z_ARRVAL_P(stack)->u.v.nIteratorsCount == 0)) { + ZEND_HASH_FOREACH_STR_KEY_VAL(Z_ARRVAL_P(stack), key, value) { + if (key) { + zend_hash_add_new(&new_hash, key, value); + } else { + zend_hash_next_index_insert_new(&new_hash, value); + } + } ZEND_HASH_FOREACH_END(); + } else { + uint32_t old_idx; + uint32_t new_idx = i; + uint32_t iter_pos = zend_hash_iterators_lower_pos(Z_ARRVAL_P(stack), 0); + ZEND_HASH_FOREACH_STR_KEY_VAL(Z_ARRVAL_P(stack), key, value) { + if (key) { + zend_hash_add_new(&new_hash, key, value); + } else { + zend_hash_next_index_insert_new(&new_hash, value); + } + old_idx = (Bucket*)value - Z_ARRVAL_P(stack)->arData; + if (old_idx == iter_pos) { + zend_hash_iterators_update(Z_ARRVAL_P(stack), old_idx, new_idx); + iter_pos = zend_hash_iterators_lower_pos(Z_ARRVAL_P(stack), iter_pos + 1); + } + new_idx++; + } ZEND_HASH_FOREACH_END(); + } + + /* replace HashTable data */ + 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; + + Z_ARRVAL_P(stack)->u.v.flags = new_hash.u.v.flags; + Z_ARRVAL_P(stack)->nTableSize = new_hash.nTableSize; + Z_ARRVAL_P(stack)->nTableMask = new_hash.nTableMask; + Z_ARRVAL_P(stack)->nNumUsed = new_hash.nNumUsed; + Z_ARRVAL_P(stack)->nNumOfElements = new_hash.nNumOfElements; + Z_ARRVAL_P(stack)->nNextFreeElement = new_hash.nNextFreeElement; + Z_ARRVAL_P(stack)->arData = new_hash.arData; + Z_ARRVAL_P(stack)->arHash = new_hash.arHash; + Z_ARRVAL_P(stack)->pDestructor = new_hash.pDestructor; + + 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/bug23624.phpt b/tests/lang/bug23624.phpt index 4ddb82e8c6..f8778fffe3 100644 --- a/tests/lang/bug23624.phpt +++ b/tests/lang/bug23624.phpt @@ -9,4 +9,4 @@ Bug #23624 (foreach leaves current array key as null) ?> --EXPECT-- string(3) "one" -bool(false) +string(3) "one" diff --git a/tests/lang/foreachLoop.001.phpt b/tests/lang/foreachLoop.001.phpt index b24f14e81d..c35b9b44e5 100644 --- a/tests/lang/foreachLoop.001.phpt +++ b/tests/lang/foreachLoop.001.phpt @@ -60,5 +60,5 @@ string(1) "f" int(2)
string(1) "f"
-bool(false)
+string(1) "a"
bool(false) diff --git a/tests/lang/foreachLoop.009.phpt b/tests/lang/foreachLoop.009.phpt index 4586a35a10..df51fd6be4 100644 --- a/tests/lang/foreachLoop.009.phpt +++ b/tests/lang/foreachLoop.009.phpt @@ -55,6 +55,7 @@ foreach ($refedArray as $k=>&$v4) { Remove elements from a referenced array during loop
key: 0; value: original.0
key: 1; value: original.1
+key: 2; value: original.2
Remove elements from a referenced array during loop, using &$value
key: 0; value: original.0
@@ -64,11 +65,6 @@ Add elements to a referenced array during loop key: 0; value: original.0
key: 1; value: original.1
key: 2; value: original.2
-key: 3; value: new.0
-key: 4; value: new.1
-key: 5; value: new.2
-key: 6; value: new.3
-Loop detected, as expected.
Add elements to a referenced array during loop, using &$value
key: 0; value: original.0
diff --git a/tests/lang/foreachLoop.011.phpt b/tests/lang/foreachLoop.011.phpt index 671cfaf354..8527a8833e 100644 --- a/tests/lang/foreachLoop.011.phpt +++ b/tests/lang/foreachLoop.011.phpt @@ -25,10 +25,9 @@ foreach ($a as $v) { Change from array to non iterable:
int(1)
-
-Warning: Invalid argument supplied for foreach() in %s on line 5
+int(2)
+int(3)
Change from object to non iterable:
int(1)
-
-Warning: Invalid argument supplied for foreach() in %s on line 15 +int(2)
diff --git a/tests/lang/foreachLoop.013.phpt b/tests/lang/foreachLoop.013.phpt index b0c5e8dcf5..ea728156f0 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): )---
@@ -446,30 +463,17 @@ array(2) { }
--> 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! **
+ iteration 1: $k=2; $v=v.1
--> State of array after loop:
-array(8) {
+array(4) {
[0]=>
- string(5) "new.5"
- [1]=>
- &string(5) "new.4"
- [2]=>
- string(5) "new.3"
- [3]=>
- string(5) "new.2"
- [4]=>
string(5) "new.1"
- [5]=>
+ [1]=>
string(5) "new.0"
- [6]=>
+ [2]=>
string(3) "v.0"
- [7]=>
- string(3) "v.1"
+ [3]=>
+ &string(3) "v.1"
}
---( Array with 3 element(s): )---
@@ -484,32 +488,19 @@ array(3) { }
--> 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! **
+ iteration 1: $k=3; $v=v.2
--> State of array after loop:
-array(9) {
+array(5) {
[0]=>
- string(5) "new.5"
- [1]=>
- &string(5) "new.4"
- [2]=>
- string(5) "new.3"
- [3]=>
- string(5) "new.2"
- [4]=>
string(5) "new.1"
- [5]=>
+ [1]=>
string(5) "new.0"
- [6]=>
+ [2]=>
string(3) "v.0"
- [7]=>
+ [3]=>
string(3) "v.1"
- [8]=>
- string(3) "v.2"
+ [4]=>
+ &string(3) "v.2"
}
---( Array with 4 element(s): )---
@@ -526,32 +517,19 @@ array(4) { }
--> 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! **
+ iteration 1: $k=4; $v=v.3
--> State of array after loop:
-array(10) {
+array(6) {
[0]=>
- string(5) "new.5"
- [1]=>
- &string(5) "new.4"
- [2]=>
- string(5) "new.3"
- [3]=>
- string(5) "new.2"
- [4]=>
string(5) "new.1"
- [5]=>
+ [1]=>
string(5) "new.0"
- [6]=>
+ [2]=>
string(3) "v.0"
- [7]=>
+ [3]=>
string(3) "v.1"
- [8]=>
+ [4]=>
string(3) "v.2"
- [9]=>
- string(3) "v.3"
+ [5]=>
+ &string(3) "v.3"
}
diff --git a/tests/lang/foreachLoop.014.phpt b/tests/lang/foreachLoop.014.phpt index d32ea635fd..9b179ab22a 100644 --- a/tests/lang/foreachLoop.014.phpt +++ b/tests/lang/foreachLoop.014.phpt @@ -1,7 +1,5 @@ --TEST--
Directly modifying a REFERENCED array when foreach'ing over it.
---XFAIL--
-Needs major foreach changes to get sane behavior
--FILE--
<?php
@@ -96,7 +94,7 @@ array(2) { }
--> Do loop:
iteration 0: $k=0; $v=v.0
- iteration 1: $k=0; $v=v.0
+ iteration 1: $k=1; $v=v.1
--> State of array after loop:
array(0) {
}
@@ -114,10 +112,9 @@ array(3) { --> Do loop:
iteration 0: $k=0; $v=v.0
iteration 1: $k=1; $v=v.1
+ iteration 2: $k=2; $v=v.2
--> State of array after loop:
-array(1) {
- [0]=>
- string(3) "v.0"
+array(0) {
}
---( Array with 4 element(s): )---
@@ -135,8 +132,8 @@ 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
+ iteration 2: $k=2; $v=v.2
+ iteration 3: $k=3; $v=v.3
--> State of array after loop:
array(0) {
}
@@ -166,7 +163,7 @@ array(2) { }
--> Do loop:
iteration 0: $k=0; $v=v.0
- iteration 1: $k=0; $v=v.1
+ iteration 1: $k=1; $v=v.1
--> State of array after loop:
array(0) {
}
@@ -183,8 +180,8 @@ array(3) { }
--> Do loop:
iteration 0: $k=0; $v=v.0
- iteration 1: $k=0; $v=v.1
- iteration 2: $k=0; $v=v.2
+ iteration 1: $k=1; $v=v.1
+ iteration 2: $k=2; $v=v.2
--> State of array after loop:
array(0) {
}
@@ -203,9 +200,9 @@ array(4) { }
--> Do loop:
iteration 0: $k=0; $v=v.0
- iteration 1: $k=0; $v=v.1
- iteration 2: $k=0; $v=v.2
- iteration 3: $k=0; $v=v.3
+ iteration 1: $k=1; $v=v.1
+ iteration 2: $k=2; $v=v.2
+ iteration 3: $k=3; $v=v.3
--> State of array after loop:
array(0) {
}
@@ -309,13 +306,8 @@ array(2) { --> Do loop:
iteration 0: $k=0; $v=v.0
iteration 1: $k=1; $v=v.1
- iteration 2: $k=2; $v=new.0
- iteration 3: $k=3; $v=new.1
- iteration 4: $k=4; $v=new.2
- iteration 5: $k=5; $v=new.3
- ** Stuck in a loop! **
--> State of array after loop:
-array(8) {
+array(4) {
[0]=>
string(3) "v.0"
[1]=>
@@ -324,14 +316,6 @@ array(8) { string(5) "new.0"
[3]=>
string(5) "new.1"
- [4]=>
- string(5) "new.2"
- [5]=>
- string(5) "new.3"
- [6]=>
- string(5) "new.4"
- [7]=>
- string(5) "new.5"
}
---( Array with 3 element(s): )---
@@ -348,12 +332,8 @@ array(3) { iteration 0: $k=0; $v=v.0
iteration 1: $k=1; $v=v.1
iteration 2: $k=2; $v=v.2
- iteration 3: $k=3; $v=new.0
- iteration 4: $k=4; $v=new.1
- iteration 5: $k=5; $v=new.2
- ** Stuck in a loop! **
--> State of array after loop:
-array(9) {
+array(6) {
[0]=>
string(3) "v.0"
[1]=>
@@ -366,12 +346,6 @@ array(9) { string(5) "new.1"
[5]=>
string(5) "new.2"
- [6]=>
- string(5) "new.3"
- [7]=>
- string(5) "new.4"
- [8]=>
- string(5) "new.5"
}
---( Array with 4 element(s): )---
@@ -391,11 +365,8 @@ array(4) { iteration 1: $k=1; $v=v.1
iteration 2: $k=2; $v=v.2
iteration 3: $k=3; $v=v.3
- iteration 4: $k=4; $v=new.0
- iteration 5: $k=5; $v=new.1
- ** Stuck in a loop! **
--> State of array after loop:
-array(10) {
+array(8) {
[0]=>
string(3) "v.0"
[1]=>
@@ -412,10 +383,6 @@ array(10) { string(5) "new.2"
[7]=>
string(5) "new.3"
- [8]=>
- string(5) "new.4"
- [9]=>
- string(5) "new.5"
}
@@ -447,29 +414,16 @@ array(2) { }
--> 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! **
+ iteration 1: $k=1; $v=v.1
--> State of array after loop:
-array(8) {
+array(4) {
[0]=>
- string(5) "new.5"
- [1]=>
- string(5) "new.4"
- [2]=>
- string(5) "new.3"
- [3]=>
- string(5) "new.2"
- [4]=>
string(5) "new.1"
- [5]=>
+ [1]=>
string(5) "new.0"
- [6]=>
+ [2]=>
string(3) "v.0"
- [7]=>
+ [3]=>
string(3) "v.1"
}
@@ -485,31 +439,21 @@ array(3) { }
--> 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! **
+ iteration 1: $k=1; $v=v.1
+ iteration 2: $k=2; $v=v.2
--> State of array after loop:
-array(9) {
+array(6) {
[0]=>
- string(5) "new.5"
- [1]=>
- string(5) "new.4"
- [2]=>
- string(5) "new.3"
- [3]=>
string(5) "new.2"
- [4]=>
+ [1]=>
string(5) "new.1"
- [5]=>
+ [2]=>
string(5) "new.0"
- [6]=>
+ [3]=>
string(3) "v.0"
- [7]=>
+ [4]=>
string(3) "v.1"
- [8]=>
+ [5]=>
string(3) "v.2"
}
@@ -527,32 +471,25 @@ array(4) { }
--> 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! **
+ iteration 1: $k=1; $v=v.1
+ iteration 2: $k=2; $v=v.2
+ iteration 3: $k=3; $v=v.3
--> State of array after loop:
-array(10) {
+array(8) {
[0]=>
- string(5) "new.5"
- [1]=>
- string(5) "new.4"
- [2]=>
string(5) "new.3"
- [3]=>
+ [1]=>
string(5) "new.2"
- [4]=>
+ [2]=>
string(5) "new.1"
- [5]=>
+ [3]=>
string(5) "new.0"
- [6]=>
+ [4]=>
string(3) "v.0"
- [7]=>
+ [5]=>
string(3) "v.1"
- [8]=>
+ [6]=>
string(3) "v.2"
- [9]=>
+ [7]=>
string(3) "v.3"
}
diff --git a/tests/lang/foreachLoop.015.phpt b/tests/lang/foreachLoop.015.phpt index 5b12a2b0c2..a4ee09d6a9 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): )---
@@ -448,30 +465,17 @@ array(2) { }
--> 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! **
+ iteration 1: $k=2; $v=v.1
--> State of array after loop:
-array(8) {
+array(4) {
[0]=>
- string(5) "new.5"
- [1]=>
- &string(5) "new.4"
- [2]=>
- string(5) "new.3"
- [3]=>
- string(5) "new.2"
- [4]=>
string(5) "new.1"
- [5]=>
+ [1]=>
string(5) "new.0"
- [6]=>
+ [2]=>
string(3) "v.0"
- [7]=>
- string(3) "v.1"
+ [3]=>
+ &string(3) "v.1"
}
---( Array with 3 element(s): )---
@@ -486,32 +490,19 @@ array(3) { }
--> 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! **
+ iteration 1: $k=3; $v=v.2
--> State of array after loop:
-array(9) {
+array(5) {
[0]=>
- string(5) "new.5"
- [1]=>
- &string(5) "new.4"
- [2]=>
- string(5) "new.3"
- [3]=>
- string(5) "new.2"
- [4]=>
string(5) "new.1"
- [5]=>
+ [1]=>
string(5) "new.0"
- [6]=>
+ [2]=>
string(3) "v.0"
- [7]=>
+ [3]=>
string(3) "v.1"
- [8]=>
- string(3) "v.2"
+ [4]=>
+ &string(3) "v.2"
}
---( Array with 4 element(s): )---
@@ -528,32 +519,19 @@ array(4) { }
--> 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! **
+ iteration 1: $k=4; $v=v.3
--> State of array after loop:
-array(10) {
+array(6) {
[0]=>
- string(5) "new.5"
- [1]=>
- &string(5) "new.4"
- [2]=>
- string(5) "new.3"
- [3]=>
- string(5) "new.2"
- [4]=>
string(5) "new.1"
- [5]=>
+ [1]=>
string(5) "new.0"
- [6]=>
+ [2]=>
string(3) "v.0"
- [7]=>
+ [3]=>
string(3) "v.1"
- [8]=>
+ [4]=>
string(3) "v.2"
- [9]=>
- string(3) "v.3"
+ [5]=>
+ &string(3) "v.3"
}
diff --git a/tests/lang/foreachLoopObjects.006.phpt b/tests/lang/foreachLoopObjects.006.phpt index 8218b44dab..5204aca9ca 100644 --- a/tests/lang/foreachLoopObjects.006.phpt +++ b/tests/lang/foreachLoopObjects.006.phpt @@ -70,16 +70,12 @@ var_dump($obj); ?> --EXPECTF-- - Substituting the iterated object for a different object. string(10) "Original a" string(10) "Original b" -string(5) "new a" -string(5) "new b" -string(5) "new c" -string(5) "new d" -string(5) "new e" -string(5) "new f" +string(10) "Original c" +string(10) "Original d" +string(10) "Original e" object(stdClass)#%d (6) { ["a"]=> string(5) "new a" @@ -98,14 +94,9 @@ object(stdClass)#%d (6) { Substituting the iterated object for an array. string(10) "Original a" string(10) "Original b" -int(1) -int(2) -int(3) -int(4) -int(5) -int(6) -int(7) -int(8) +string(10) "Original c" +string(10) "Original d" +string(10) "Original e" array(8) { [0]=> int(1) @@ -128,11 +119,12 @@ array(8) { Substituting the iterated array for an object. int(1) int(2) -string(10) "Original a" -string(10) "Original b" -string(10) "Original c" -string(10) "Original d" -string(10) "Original e" +int(3) +int(4) +int(5) +int(6) +int(7) +int(8) object(C)#%d (5) { ["a"]=> string(10) "Original a" |