summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorRasmus Lerdorf <rasmus@lerdorf.com>2015-01-30 22:57:40 -0800
committerRasmus Lerdorf <rasmus@lerdorf.com>2015-01-30 22:57:40 -0800
commit74b85316ea06c17256e102483daa472f4b638221 (patch)
treeabe0618e15fcfc93158b4239889485ffc70c4c1c
parent130d7320b160443ca160ee6b3a19a034ee2c5ef1 (diff)
parent1e41295097576dbce6c197ddb7507c07ccae3cbe (diff)
downloadphp-git-dstogov-foreach.tar.gz
Merge branch 'foreach' of https://github.com/dstogov/php-src into dstogov-foreachdstogov-foreach
-rw-r--r--Zend/tests/bug40509.phpt2
-rw-r--r--Zend/tests/bug40705.phpt2
-rw-r--r--Zend/tests/foreach_003.phpt71
-rw-r--r--Zend/tests/foreach_004.phpt65
-rw-r--r--Zend/tests/foreach_005.phpt22
-rw-r--r--Zend/tests/foreach_006.phpt20
-rw-r--r--Zend/tests/foreach_007.phpt13
-rw-r--r--Zend/tests/foreach_008.phpt21
-rw-r--r--Zend/tests/foreach_009.phpt40
-rw-r--r--Zend/tests/foreach_010.phpt40
-rw-r--r--Zend/tests/foreach_011.phpt19
-rw-r--r--Zend/tests/foreach_012.phpt18
-rw-r--r--Zend/tests/foreach_013.phpt17
-rw-r--r--Zend/tests/foreach_014.phpt15
-rw-r--r--Zend/tests/foreach_015.phpt18
-rw-r--r--Zend/tests/foreach_016.phpt18
-rw-r--r--Zend/tests/foreach_017.phpt111
-rw-r--r--Zend/zend_compile.c24
-rw-r--r--Zend/zend_compile.h6
-rw-r--r--Zend/zend_execute.c8
-rw-r--r--Zend/zend_execute_API.c10
-rw-r--r--Zend/zend_generators.c6
-rw-r--r--Zend/zend_globals.h5
-rw-r--r--Zend/zend_hash.c202
-rw-r--r--Zend/zend_hash.h24
-rw-r--r--Zend/zend_opcode.c6
-rw-r--r--Zend/zend_types.h20
-rw-r--r--Zend/zend_vm_def.h765
-rw-r--r--Zend/zend_vm_execute.h1971
-rw-r--r--Zend/zend_vm_opcodes.c10
-rw-r--r--Zend/zend_vm_opcodes.h7
-rw-r--r--ext/opcache/Optimizer/block_pass.c20
-rw-r--r--ext/opcache/Optimizer/nop_removal.c6
-rw-r--r--ext/opcache/Optimizer/optimize_temp_vars_5.c34
-rw-r--r--ext/opcache/Optimizer/pass1_5.c6
-rw-r--r--ext/opcache/Optimizer/pass2.c4
-rw-r--r--ext/opcache/Optimizer/pass3.c6
-rw-r--r--ext/opcache/Optimizer/zend_optimizer.c12
-rw-r--r--ext/opcache/zend_persist.c6
-rw-r--r--ext/standard/array.c191
-rw-r--r--tests/lang/bug23624.phpt2
-rw-r--r--tests/lang/foreachLoop.001.phpt2
-rw-r--r--tests/lang/foreachLoop.009.phpt6
-rw-r--r--tests/lang/foreachLoop.011.phpt7
-rw-r--r--tests/lang/foreachLoop.013.phpt118
-rw-r--r--tests/lang/foreachLoop.014.phpt139
-rw-r--r--tests/lang/foreachLoop.015.phpt118
-rw-r--r--tests/lang/foreachLoopObjects.006.phpt32
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"