diff options
author | Dmitry Stogov <dmitry@zend.com> | 2015-02-12 13:57:12 +0300 |
---|---|---|
committer | Dmitry Stogov <dmitry@zend.com> | 2015-02-12 13:57:12 +0300 |
commit | 97fe15db4356f8fa1b3b8eb9bb1baa8141376077 (patch) | |
tree | b0c6dcbe1d5444e758e71cf4802794d56550c940 /Zend | |
parent | 741b5952c6d8b0a75c9b421e10b9bace5b7a89f4 (diff) | |
download | php-git-97fe15db4356f8fa1b3b8eb9bb1baa8141376077.tar.gz |
Fix "forech" statemt behaviour according to https://wiki.php.net/rfc/php7_foreach
Squashed commit of the following:
commit 1e41295097576dbce6c197ddb7507c07ccae3cbe
Author: Dmitry Stogov <dmitry@zend.com>
Date: Sat Jan 31 07:28:58 2015 +0300
Generalize HashTableIterator API to allows its usage without involvement of HashTable.nInternalPonter
commit 5406f21b11e563069d64045e599693b51c444b63
Author: Dmitry Stogov <dmitry@zend.com>
Date: Fri Jan 30 18:08:43 2015 +0300
Reduced alghorithms complexity
commit b37f1d58d2a141b6e1d980a461ccb588d4317d2e
Author: Dmitry Stogov <dmitry@zend.com>
Date: Fri Jan 30 18:08:30 2015 +0300
Fixed test name
commit fb2d079645829b12ed4e55a461034df6400bc430
Author: Dmitry Stogov <dmitry@zend.com>
Date: Fri Jan 30 18:08:05 2015 +0300
API cleanup
commit 08302c0d6d1cab279b9f2129df03a057baddf2ff
Author: Dmitry Stogov <dmitry@zend.com>
Date: Fri Jan 30 14:20:46 2015 +0300
Make array_splice() to preserve foreach hash position
commit cc4b7be41e2e2b9b0d7a3c8e98466b8886692e6e
Author: Dmitry Stogov <dmitry@zend.com>
Date: Fri Jan 30 12:24:31 2015 +0300
Make internal function, operation on array passed by reference, to preserve foreach hash position
commit 5aa9712b0a30303aadfe3bdd8ae1f072ca3e6ba1
Author: Dmitry Stogov <dmitry@zend.com>
Date: Fri Jan 30 09:49:35 2015 +0300
Implement consistent behavior for foreach by value over plain object
commit 4c5b385ff53ae9f0b52572e98c4db801f56603b0
Author: Dmitry Stogov <dmitry@zend.com>
Date: Fri Jan 30 07:56:37 2015 +0300
More careful iterators update.
commit 721fc9e80d2ee8f2cd79c8c3cdceffae2c72de92
Author: Dmitry Stogov <dmitry@zend.com>
Date: Thu Jan 29 21:43:28 2015 +0300
Added new test
commit 15a23b1218b3e38630d677751a975907daa2cd54
Author: Dmitry Stogov <dmitry@zend.com>
Date: Thu Jan 29 21:05:02 2015 +0300
Reimplement iteration magic with HashTableIterators (see https://wiki.php.net/rfc/php7_foreach#implementation_details)
commit 10a3260b1f16b6075fd8140f673dfef4d5efea91
Author: Dmitry Stogov <dmitry@zend.com>
Date: Thu Jan 29 21:04:44 2015 +0300
New test
commit eef80c583762d1e98d177cdbb27e3a8a6b0c4539
Author: Dmitry Stogov <dmitry@zend.com>
Date: Wed Jan 28 16:52:21 2015 +0300
Fixed foreach by reference iteration over constant array
commit 61e739187391661e2d541947bec25d7dcc4479f3
Author: Dmitry Stogov <dmitry@zend.com>
Date: Wed Jan 28 14:59:54 2015 +0300
Fixed temporary variable re-allocation pass
commit 92e90c09f085c22707ff4a59201f016f56e0ef8b
Author: Dmitry Stogov <dmitry@zend.com>
Date: Wed Jan 28 12:44:57 2015 +0300
Fixed operand destruction in case of exceptions in iterator
commit dd2a36a2074bbb0cb31de00b66dcf2812d6d753f
Author: Dmitry Stogov <dmitry@zend.com>
Date: Wed Jan 28 10:02:34 2015 +0300
Use GET_OP1_ZVAL_PTR_DEREF() (IS_TMP_VAR and IS_CONST can't be IS_REFERENCE)
commit 4638f7b91407c48710007af82a68da0007c820f2
Author: Dmitry Stogov <dmitry@zend.com>
Date: Wed Jan 28 07:43:28 2015 +0300
Change "foreach" statement behavior (this is just a PoC yet)
- "foreach by value" don't relay on internal array/object pointer and doesnt perform array duplication. It just locks it incrementing reference counter. If the original array is modified by some code, the copy on write is performed and "foreach" still work with the old copy.
- it makes no difference if array given to "foreach by value" is reference itself
- "foreach by reference" still use internal array/object pointer and should work similar to PHP-5. (This id not completely implemented)
Diffstat (limited to 'Zend')
31 files changed, 2478 insertions, 1098 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 8ce35c08d5..1fcc7e1b31 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); } @@ -3438,32 +3438,19 @@ void zend_compile_foreach(zend_ast *ast) /* {{{ */ } opnum_reset = get_next_op_number(CG(active_op_array)); - opline = zend_emit_op(&reset_node, ZEND_FE_RESET, &expr_node, NULL); - if (by_ref && is_variable) { - opline->extended_value = ZEND_FE_FETCH_BYREF; - } + opline = zend_emit_op(&reset_node, by_ref ? ZEND_FE_RESET_RW : ZEND_FE_RESET_R, &expr_node, NULL); + reset_node.flag = 1; /* generate FE_FREE */ zend_stack_push(&CG(loop_var_stack), &reset_node); opnum_fetch = get_next_op_number(CG(active_op_array)); - opline = zend_emit_op(&value_node, ZEND_FE_FETCH, &reset_node, NULL); - if (by_ref) { - opline->extended_value |= ZEND_FE_FETCH_BYREF; - } + opline = zend_emit_op(&value_node, by_ref ? ZEND_FE_FETCH_RW : ZEND_FE_FETCH_R, &reset_node, NULL); if (key_ast) { - opline->extended_value |= ZEND_FE_FETCH_WITH_KEY; + opline->extended_value = 1; } opline = zend_emit_op(NULL, ZEND_OP_DATA, NULL, NULL); - /* Allocate enough space to keep HashPointer on VM stack */ - opline->op1_type = IS_TMP_VAR; - opline->op1.var = get_temporary_variable(CG(active_op_array)); - if (sizeof(HashPointer) > sizeof(zval)) { - /* Make sure 1 zval is enough for HashPointer (2 must be enough) */ - get_temporary_variable(CG(active_op_array)); - } - if (key_ast) { zend_make_tmp_result(&key_node, opline); } @@ -3554,6 +3541,7 @@ void zend_compile_switch(zend_ast *ast) /* {{{ */ zend_compile_expr(&expr_node, expr_ast); + expr_node.flag = 0; zend_stack_push(&CG(loop_var_stack), &expr_node); zend_begin_loop(); diff --git a/Zend/zend_compile.h b/Zend/zend_compile.h index 171fdcfbf2..d39f1285f4 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 */ @@ -831,9 +832,6 @@ int zend_add_literal(zend_op_array *op_array, zval *zv); #define ZEND_FETCH_ARG_MASK 0x000fffff -#define ZEND_FE_FETCH_BYREF 1 -#define ZEND_FE_FETCH_WITH_KEY 2 - #define EXT_TYPE_FREE_ON_RETURN (1<<2) #define ZEND_MEMBER_FUNC_CALL 1<<0 diff --git a/Zend/zend_execute.c b/Zend/zend_execute.c index 62064dcd5f..73c1d4d17c 100644 --- a/Zend/zend_execute.c +++ b/Zend/zend_execute.c @@ -1628,6 +1628,14 @@ static inline zend_brk_cont_element* zend_brk_cont(int nest_levels, int array_of if (!(brk_opline->extended_value & EXT_TYPE_FREE_ON_RETURN)) { zval_ptr_dtor_nogc(EX_VAR(brk_opline->op1.var)); } + } else if (brk_opline->opcode == ZEND_FE_FREE) { + if (!(brk_opline->extended_value & EXT_TYPE_FREE_ON_RETURN)) { + zval *var = EX_VAR(brk_opline->op1.var); + if (Z_TYPE_P(var) != IS_ARRAY && Z_FE_ITER_P(var) != (uint32_t)-1) { + zend_hash_iterator_del(Z_FE_ITER_P(var)); + } + zval_ptr_dtor_nogc(var); + } } } array_offset = jmp_to->parent; 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 80e1700373..d05a6adcb9 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; @@ -260,6 +270,12 @@ static zend_always_inline zend_uchar zval_get_type(const zval* pz) { #define Z_CACHE_SLOT(zval) (zval).u2.cache_slot #define Z_CACHE_SLOT_P(zval_p) Z_CACHE_SLOT(*(zval_p)) +#define Z_FE_POS(zval) (zval).u2.fe_pos +#define Z_FE_POS_P(zval_p) Z_FE_POS(*(zval_p)) + +#define Z_FE_ITER(zval) (zval).u2.fe_iter_idx +#define Z_FE_ITER_P(zval_p) Z_FE_ITER(*(zval_p)) + #define Z_COUNTED(zval) (zval).value.counted #define Z_COUNTED_P(zval_p) Z_COUNTED(*(zval_p)) diff --git a/Zend/zend_vm_def.h b/Zend/zend_vm_def.h index f437f430d5..81a9bbb658 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 @@ -3828,6 +3843,14 @@ ZEND_VM_HANDLER(100, ZEND_GOTO, ANY, CONST) if (!(brk_opline->extended_value & EXT_TYPE_FREE_ON_RETURN)) { zval_ptr_dtor_nogc(EX_VAR(brk_opline->op1.var)); } + } else if (brk_opline->opcode == ZEND_FE_FREE) { + if (!(brk_opline->extended_value & EXT_TYPE_FREE_ON_RETURN)) { + zval *var = EX_VAR(brk_opline->op1.var); + if (Z_TYPE_P(var) != IS_ARRAY && Z_FE_ITER_P(var) != (uint32_t)-1) { + zend_hash_iterator_del(Z_FE_ITER_P(var)); + } + zval_ptr_dtor_nogc(var); + } } ZEND_VM_JMP(OP_JMP_ADDR(opline, opline->op1)); } @@ -4627,292 +4650,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)) { @@ -4923,93 +5159,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 { @@ -5019,7 +5228,7 @@ ZEND_VM_HANDLER(78, ZEND_FE_FETCH, VAR, ANY) * In case that ever happens we need an additional flag. */ iter->funcs->move_forward(iter); if (UNEXPECTED(EG(exception) != NULL)) { - zval_ptr_dtor(array_ref); + zval_ptr_dtor(array); HANDLE_EXCEPTION(); } } @@ -5027,38 +5236,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(); } @@ -5847,6 +6052,14 @@ ZEND_VM_HANDLER(149, ZEND_HANDLE_EXCEPTION, ANY, ANY) if (!(brk_opline->extended_value & EXT_TYPE_FREE_ON_RETURN)) { zval_ptr_dtor_nogc(EX_VAR(brk_opline->op1.var)); } + } else if (brk_opline->opcode == ZEND_FE_FREE) { + if (!(brk_opline->extended_value & EXT_TYPE_FREE_ON_RETURN)) { + zval *var = EX_VAR(brk_opline->op1.var); + if (Z_TYPE_P(var) != IS_ARRAY && Z_FE_ITER_P(var) != (uint32_t)-1) { + zend_hash_iterator_del(Z_FE_ITER_P(var)); + } + zval_ptr_dtor_nogc(var); + } } else if (brk_opline->opcode == ZEND_END_SILENCE) { /* restore previous error_reporting value */ if (!EG(error_reporting) && Z_LVAL_P(EX_VAR(brk_opline->op1.var)) != 0) { diff --git a/Zend/zend_vm_execute.h b/Zend/zend_vm_execute.h index 3e32191a03..0121d65faf 100644 --- a/Zend/zend_vm_execute.h +++ b/Zend/zend_vm_execute.h @@ -1353,6 +1353,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) { @@ -1811,6 +1819,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)); } @@ -3045,194 +3061,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(); } } @@ -8964,194 +9059,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(); } } @@ -11784,292 +11959,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)) { @@ -12080,93 +12468,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 { @@ -12176,7 +12537,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(); } } @@ -12184,38 +12545,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(); } @@ -24074,194 +24431,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(); } } @@ -32986,6 +33422,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 @@ -38306,31 +38757,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, @@ -38341,11 +38792,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, @@ -39506,11 +39957,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, @@ -39521,6 +39992,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, @@ -39536,41 +40012,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 |