Fix "forech" statemt behaviour according to https://wiki.php.net/rfc/php7_foreach

Squashed commit of the following:

commit 1e41295097
Author: Dmitry Stogov <dmitry@zend.com>
Date:   Sat Jan 31 07:28:58 2015 +0300

    Generalize HashTableIterator API to allows its usage without involvement of HashTable.nInternalPonter

commit 5406f21b11
Author: Dmitry Stogov <dmitry@zend.com>
Date:   Fri Jan 30 18:08:43 2015 +0300

    Reduced alghorithms complexity

commit b37f1d58d2
Author: Dmitry Stogov <dmitry@zend.com>
Date:   Fri Jan 30 18:08:30 2015 +0300

    Fixed test name

commit fb2d079645
Author: Dmitry Stogov <dmitry@zend.com>
Date:   Fri Jan 30 18:08:05 2015 +0300

    API cleanup

commit 08302c0d6d
Author: Dmitry Stogov <dmitry@zend.com>
Date:   Fri Jan 30 14:20:46 2015 +0300

    Make array_splice() to preserve foreach hash position

commit cc4b7be41e
Author: Dmitry Stogov <dmitry@zend.com>
Date:   Fri Jan 30 12:24:31 2015 +0300

    Make internal function, operation on array passed by reference, to preserve foreach hash position

commit 5aa9712b0a
Author: Dmitry Stogov <dmitry@zend.com>
Date:   Fri Jan 30 09:49:35 2015 +0300

    Implement consistent behavior for foreach by value over plain object

commit 4c5b385ff5
Author: Dmitry Stogov <dmitry@zend.com>
Date:   Fri Jan 30 07:56:37 2015 +0300

    More careful iterators update.

commit 721fc9e80d
Author: Dmitry Stogov <dmitry@zend.com>
Date:   Thu Jan 29 21:43:28 2015 +0300

    Added new test

commit 15a23b1218
Author: Dmitry Stogov <dmitry@zend.com>
Date:   Thu Jan 29 21:05:02 2015 +0300

    Reimplement iteration magic with HashTableIterators (see https://wiki.php.net/rfc/php7_foreach#implementation_details)

commit 10a3260b1f
Author: Dmitry Stogov <dmitry@zend.com>
Date:   Thu Jan 29 21:04:44 2015 +0300

    New test

commit eef80c5837
Author: Dmitry Stogov <dmitry@zend.com>
Date:   Wed Jan 28 16:52:21 2015 +0300

    Fixed foreach by reference iteration over constant array

commit 61e7391873
Author: Dmitry Stogov <dmitry@zend.com>
Date:   Wed Jan 28 14:59:54 2015 +0300

    Fixed temporary variable re-allocation pass

commit 92e90c09f0
Author: Dmitry Stogov <dmitry@zend.com>
Date:   Wed Jan 28 12:44:57 2015 +0300

    Fixed operand destruction in case of exceptions in iterator

commit dd2a36a207
Author: Dmitry Stogov <dmitry@zend.com>
Date:   Wed Jan 28 10:02:34 2015 +0300

    Use GET_OP1_ZVAL_PTR_DEREF() (IS_TMP_VAR and IS_CONST can't be IS_REFERENCE)

commit 4638f7b914
Author: Dmitry Stogov <dmitry@zend.com>
Date:   Wed Jan 28 07:43:28 2015 +0300

    Change "foreach" statement behavior (this is just a PoC yet)

    - "foreach by value" don't relay on internal array/object pointer and doesnt perform array duplication. It just locks it incrementing reference counter. If the original array is modified by some code, the copy on write is performed and "foreach" still work with the old copy.

    - it makes no difference if array given to "foreach by value" is reference itself

    - "foreach by reference" still use internal array/object pointer and should work similar to PHP-5. (This id not completely implemented)
This commit is contained in:
Dmitry Stogov 2015-02-12 13:57:12 +03:00
parent 741b5952c6
commit 97fe15db43
48 changed files with 2936 additions and 1565 deletions

View File

@ -23,4 +23,4 @@ var_dump(key($arr["v"]));
int(0)
int(0)
int(0)
NULL
int(0)

View File

@ -23,4 +23,4 @@ int(0)
int(0)
int(1)
int(2)
NULL
int(0)

View File

@ -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

View File

@ -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

View File

@ -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

View File

@ -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

View File

@ -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

View File

@ -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

View File

@ -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

View File

@ -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

View File

@ -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

View File

@ -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

View File

@ -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

View File

@ -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

View File

@ -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) {
}

View File

@ -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)

111
Zend/tests/foreach_017.phpt Normal file
View File

@ -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

View File

@ -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);
}
@ -3438,32 +3438,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);
}
@ -3554,6 +3541,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();

View File

@ -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 */
@ -831,9 +832,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

View File

@ -1628,6 +1628,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;

View File

@ -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;
}
/* }}} */

View File

@ -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);
}
}
}

View File

@ -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;

View File

@ -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;

View File

@ -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) \

View File

@ -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:

View File

@ -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;
@ -260,6 +270,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))

View File

@ -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
@ -3828,6 +3843,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));
}
@ -4627,245 +4650,296 @@ 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)) {
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);
}
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 {
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();
}
iter->index = 0;
if (iter->funcs->rewind) {
iter->funcs->rewind(iter);
if (UNEXPECTED(EG(exception) != NULL)) {
OBJ_RELEASE(&iter->std);
FREE_OP1();
HANDLE_EXCEPTION();
}
}
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 {
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;
SAVE_OPLINE();
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);
}
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) {
Z_ADDREF_P(array_ptr);
}
array_ref = array_ptr;
Z_ADDREF_P(array_ref);
ZVAL_COPY_VALUE(EX_VAR(opline->result.var), array_ref);
} else {
if (Z_REFCOUNTED_P(array_ref)) Z_ADDREF_P(array_ref);
array_ptr = EX_VAR(opline->result.var);
ZVAL_COPY_VALUE(array_ptr, 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);
}
}
} 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);
}
}
} else if (Z_IMMUTABLE_P(array_ref)) {
if (OP1_TYPE == IS_CV) {
zval_copy_ctor_func(array_ref);
Z_ADDREF_P(array_ref);
} 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);
}
}
}
}
if (ce && ce->get_iterator) {
iter = ce->get_iterator(ce, array_ptr, opline->extended_value & ZEND_FE_FETCH_BYREF);
if (OP1_TYPE == IS_VAR && !(opline->extended_value & ZEND_FE_FETCH_BYREF)) {
FREE_OP1_IF_VAR();
}
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) {
FREE_OP1_VAR_PTR();
}
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();
SEPARATE_ARRAY(array_ptr);
}
}
ZVAL_COPY_VALUE(EX_VAR(opline->result.var), array_ref);
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) {
FREE_OP1_VAR_PTR();
}
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) {
FREE_OP1_VAR_PTR();
}
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;
fe_ht = Z_ARRVAL_P(array_ptr);
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();
}
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)) {
pos++;
continue;
}
if (!ce ||
!p->key ||
zend_check_property_access(Z_OBJ_P(array_ptr), p->key) == SUCCESS) {
if (Z_TYPE(p->val) != IS_UNDEF &&
(Z_TYPE(p->val) != IS_INDIRECT ||
Z_TYPE_P(Z_INDIRECT(p->val)) != IS_UNDEF)) {
break;
}
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;
}
Z_FE_ITER_P(EX_VAR(opline->result.var)) = zend_hash_iterator_add(fe_ht, pos);
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();
} 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();
}
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 (UNEXPECTED(EG(exception) != NULL)) {
OBJ_RELEASE(&iter->std);
if (OP1_TYPE == IS_VAR) {
FREE_OP1_VAR_PTR();
} else {
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;
if (OP1_TYPE == IS_VAR) {
FREE_OP1_VAR_PTR();
} else {
FREE_OP1();
}
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();
}
ZEND_VM_JMP(OP_JMP_ADDR(opline, opline->op2));
}
}
ZEND_VM_HANDLER(78, ZEND_FE_FETCH, VAR, ANY)
ZEND_VM_HANDLER(78, ZEND_FE_FETCH_R, 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();
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 = Z_FE_POS_P(EX_VAR(opline->op1.var));
while (1) {
if (UNEXPECTED(pos >= fe_ht->nNumUsed)) {
/* reached end of iteration */
@ -4873,46 +4947,27 @@ ZEND_VM_HANDLER(78, ZEND_FE_FETCH, VAR, ANY)
}
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));
} 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);
}
}
break;
}
do {
pos++;
if (pos >= fe_ht->nNumUsed) {
fe_ht->nInternalPointer = ptr->pos = INVALID_IDX;
ZEND_VM_INC_OPCODE();
ZEND_VM_NEXT_OPCODE();
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);
}
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;
}
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)) {
@ -4923,30 +4978,7 @@ 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 */
@ -4955,61 +4987,54 @@ ZEND_VM_HANDLER(78, ZEND_FE_FETCH, VAR, ANY)
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_COPY(EX_VAR(opline->result.var), 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 {
@ -5019,7 +5044,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();
}
}
@ -5027,32 +5052,26 @@ 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) {
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_ref);
zval_ptr_dtor(array);
HANDLE_EXCEPTION();
}
} else {
@ -5068,6 +5087,192 @@ 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;
zval *value;
HashTable *fe_ht;
HashPosition pos;
Bucket *p;
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);
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 (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;
}
}
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);
}
}
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) {
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)) {
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)) {
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 */
fe_ht->nInternalPointer = INVALID_IDX;
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_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) {
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));
}
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);
}
}
ZVAL_MAKE_REF(value);
Z_ADDREF_P(value);
ZVAL_REF(EX_VAR(opline->result.var), Z_REF_P(value));
ZEND_VM_INC_OPCODE();
ZEND_VM_NEXT_OPCODE();
}
} else {
zend_error(E_WARNING, "Invalid argument supplied for foreach()");
ZEND_VM_JMP(OP_JMP_ADDR(opline, opline->op2));
}
}
ZEND_VM_HANDLER(114, ZEND_ISSET_ISEMPTY_VAR, CONST|TMPVAR|CV, UNUSED|CONST|VAR)
{
USE_OPLINE
@ -5847,6 +6052,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) {

File diff suppressed because it is too large Load Diff

View File

@ -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,

View File

@ -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

View File

@ -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);

View File

@ -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:

View File

@ -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 */

View File

@ -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:

View File

@ -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;
}

View File

@ -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;
}

View File

@ -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;
}

View File

@ -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;
}

View File

@ -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)));

View File

@ -9,4 +9,4 @@ Bug #23624 (foreach leaves current array key as null)
?>
--EXPECT--
string(3) "one"
bool(false)
string(3) "one"

View File

@ -60,5 +60,5 @@ string(1) "f"
int(2)
string(1) "f"
bool(false)
string(1) "a"
bool(false)

View File

@ -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

View File

@ -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)

View File

@ -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"
}

View File

@ -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"
}

View File

@ -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"
}

View File

@ -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"