]> granicus.if.org Git - php/commitdiff
Implement argument unpacking
authorNikita Popov <nikic@php.net>
Thu, 29 Aug 2013 09:35:11 +0000 (11:35 +0200)
committerNikita Popov <nikic@php.net>
Sat, 11 Jan 2014 11:42:08 +0000 (12:42 +0100)
RFC: https://wiki.php.net/rfc/argument_unpacking

18 files changed:
Zend/tests/arg_unpack/basic.phpt [new file with mode: 0644]
Zend/tests/arg_unpack/by_ref.phpt [new file with mode: 0644]
Zend/tests/arg_unpack/dynamic.phpt [new file with mode: 0644]
Zend/tests/arg_unpack/internal.phpt [new file with mode: 0644]
Zend/tests/arg_unpack/invalid_type.phpt [new file with mode: 0644]
Zend/tests/arg_unpack/method.phpt [new file with mode: 0644]
Zend/tests/arg_unpack/new.phpt [new file with mode: 0644]
Zend/tests/arg_unpack/string_keys.phpt [new file with mode: 0644]
Zend/tests/arg_unpack/traversable_throwing_exception.phpt [new file with mode: 0644]
Zend/tests/arg_unpack/traversable_with_by_ref_parameters.phpt [new file with mode: 0644]
Zend/zend_compile.c
Zend/zend_compile.h
Zend/zend_execute.c
Zend/zend_language_parser.y
Zend/zend_vm_def.h
Zend/zend_vm_execute.h
Zend/zend_vm_opcodes.c
Zend/zend_vm_opcodes.h

diff --git a/Zend/tests/arg_unpack/basic.phpt b/Zend/tests/arg_unpack/basic.phpt
new file mode 100644 (file)
index 0000000..9c03655
--- /dev/null
@@ -0,0 +1,114 @@
+--TEST--
+Basic argument unpacking
+--FILE--
+<?php
+
+function test(...$args) {
+    var_dump($args);
+}
+
+function test2($arg1, $arg2, $arg3 = null) {
+    var_dump($arg1, $arg2, $arg3);
+}
+
+function getArray($array) {
+    return $array;
+}
+
+function arrayGen($array) {
+    foreach ($array as $element) {
+        yield $element;
+    }
+}
+
+$array = [1, 2, 3];
+
+test(...[]);
+test(...[1, 2, 3]);
+test(...$array);
+test(...getArray([1, 2, 3]));
+test(...arrayGen([]));
+test(...arrayGen([1, 2, 3]));
+
+test(1, ...[2, 3], ...[4, 5], 6);
+test(1, ...getArray([2, 3]), ...arrayGen([4, 5]), 6);
+
+test2(...[1, 2]);
+test2(...[1, 2, 3]);
+test2(...[1], ...[], ...[], ...[2, 3], 4, ...[5, 6]);
+
+?>
+--EXPECT--
+array(0) {
+}
+array(3) {
+  [0]=>
+  int(1)
+  [1]=>
+  int(2)
+  [2]=>
+  int(3)
+}
+array(3) {
+  [0]=>
+  int(1)
+  [1]=>
+  int(2)
+  [2]=>
+  int(3)
+}
+array(3) {
+  [0]=>
+  int(1)
+  [1]=>
+  int(2)
+  [2]=>
+  int(3)
+}
+array(0) {
+}
+array(3) {
+  [0]=>
+  int(1)
+  [1]=>
+  int(2)
+  [2]=>
+  int(3)
+}
+array(6) {
+  [0]=>
+  int(1)
+  [1]=>
+  int(2)
+  [2]=>
+  int(3)
+  [3]=>
+  int(4)
+  [4]=>
+  int(5)
+  [5]=>
+  int(6)
+}
+array(6) {
+  [0]=>
+  int(1)
+  [1]=>
+  int(2)
+  [2]=>
+  int(3)
+  [3]=>
+  int(4)
+  [4]=>
+  int(5)
+  [5]=>
+  int(6)
+}
+int(1)
+int(2)
+NULL
+int(1)
+int(2)
+int(3)
+int(1)
+int(2)
+int(3)
diff --git a/Zend/tests/arg_unpack/by_ref.phpt b/Zend/tests/arg_unpack/by_ref.phpt
new file mode 100644 (file)
index 0000000..0619a3b
--- /dev/null
@@ -0,0 +1,149 @@
+--TEST--
+Argument unpacking with by-ref arguments
+--FILE--
+<?php
+
+error_reporting(E_ALL);
+
+function test1(&...$args) {
+    foreach ($args as &$arg) {
+        $arg++;
+    }
+}
+
+test1(...[1, 2, 3]);
+
+$array = [1, 2, 3];
+test1(...$array);
+var_dump($array);
+
+$array1 = [1, 2]; $val2 = 3; $array2 = [4, 5];
+test1(...$array1, $val2, ...$array2);
+var_dump($array1, $val2, $array2);
+
+function test2($val1, &$ref1, $val2, &$ref2) {
+    $ref1++;
+    $ref2++;
+}
+
+$array = [1, 2, 3, 4];
+test2(...$array);
+var_dump($array);
+
+$a = $b = $c = $d = 0;
+
+$array = [];
+test2(...$array, $a, $b, $c, $d);
+var_dump($array, $a, $b, $c, $d);
+
+$array = [1];
+test2(...$array, $a, $b, $c, $d);
+var_dump($array, $a, $b, $c, $d);
+
+$array = [1, 2];
+test2(...$array, $a, $b, $c, $d);
+var_dump($array, $a, $b, $c, $d);
+
+$array = [1, 2, 3];
+test2(...$array, $a, $b, $c, $d);
+var_dump($array, $a, $b, $c, $d);
+
+$vars = [];
+$array = [];
+test2(...$array, $vars['a'], $vars['b'], $vars['c'], $vars['d']);
+var_dump($vars);
+
+$vars = [];
+$array = [1];
+test2(...$array, $vars['a'], $vars['b'], $vars['c'], $vars['d']);
+var_dump($vars);
+
+?>
+--EXPECTF--
+array(3) {
+  [0]=>
+  int(2)
+  [1]=>
+  int(3)
+  [2]=>
+  int(4)
+}
+array(2) {
+  [0]=>
+  int(2)
+  [1]=>
+  int(3)
+}
+int(4)
+array(2) {
+  [0]=>
+  int(5)
+  [1]=>
+  int(6)
+}
+array(4) {
+  [0]=>
+  int(1)
+  [1]=>
+  int(3)
+  [2]=>
+  int(3)
+  [3]=>
+  int(5)
+}
+array(0) {
+}
+int(0)
+int(1)
+int(0)
+int(1)
+array(1) {
+  [0]=>
+  int(1)
+}
+int(1)
+int(1)
+int(1)
+int(1)
+array(2) {
+  [0]=>
+  int(1)
+  [1]=>
+  int(3)
+}
+int(1)
+int(2)
+int(1)
+int(1)
+array(3) {
+  [0]=>
+  int(1)
+  [1]=>
+  int(3)
+  [2]=>
+  int(3)
+}
+int(2)
+int(2)
+int(1)
+int(1)
+
+Notice: Undefined index: a in %s on line %d
+
+Notice: Undefined index: c in %s on line %d
+array(2) {
+  ["b"]=>
+  int(1)
+  ["d"]=>
+  int(1)
+}
+
+Notice: Undefined index: b in %s on line %d
+
+Notice: Undefined index: d in %s on line %d
+array(2) {
+  ["a"]=>
+  int(1)
+  ["c"]=>
+  int(1)
+}
diff --git a/Zend/tests/arg_unpack/dynamic.phpt b/Zend/tests/arg_unpack/dynamic.phpt
new file mode 100644 (file)
index 0000000..efed84d
--- /dev/null
@@ -0,0 +1,37 @@
+--TEST--
+Unpack arguments for dynamic call
+--FILE--
+<?php
+
+$fn = function(...$args) {
+    var_dump($args);
+};
+
+$fn(...[]);
+$fn(...[1, 2, 3]);
+$fn(1, ...[2, 3], ...[], 4, 5);
+
+?>
+--EXPECT--
+array(0) {
+}
+array(3) {
+  [0]=>
+  int(1)
+  [1]=>
+  int(2)
+  [2]=>
+  int(3)
+}
+array(5) {
+  [0]=>
+  int(1)
+  [1]=>
+  int(2)
+  [2]=>
+  int(3)
+  [3]=>
+  int(4)
+  [4]=>
+  int(5)
+}
diff --git a/Zend/tests/arg_unpack/internal.phpt b/Zend/tests/arg_unpack/internal.phpt
new file mode 100644 (file)
index 0000000..adc985d
--- /dev/null
@@ -0,0 +1,43 @@
+--TEST--
+Argument unpacking with internal functions
+--FILE--
+<?php
+
+$arrays = [
+    [1, 2, 3],
+    [4, 5, 6],
+    [7, 8, 9],
+];
+var_dump(array_map(null, ...$arrays));
+
+?>
+--EXPECT--
+array(3) {
+  [0]=>
+  array(3) {
+    [0]=>
+    int(1)
+    [1]=>
+    int(4)
+    [2]=>
+    int(7)
+  }
+  [1]=>
+  array(3) {
+    [0]=>
+    int(2)
+    [1]=>
+    int(5)
+    [2]=>
+    int(8)
+  }
+  [2]=>
+  array(3) {
+    [0]=>
+    int(3)
+    [1]=>
+    int(6)
+    [2]=>
+    int(9)
+  }
+}
diff --git a/Zend/tests/arg_unpack/invalid_type.phpt b/Zend/tests/arg_unpack/invalid_type.phpt
new file mode 100644 (file)
index 0000000..3efffeb
--- /dev/null
@@ -0,0 +1,59 @@
+--TEST--
+Only arrays and Traversables can be unpacked
+--FILE--
+<?php
+
+function test(...$args) {
+    var_dump($args);
+}
+
+test(...null);
+test(...42);
+test(...new stdClass);
+
+test(1, 2, 3, ..."foo", ...[4, 5]);
+test(1, 2, ...new StdClass, 3, ...3.14, ...[4, 5]);
+
+?>
+--EXPECTF--
+Warning: Only arrays and Traversables can be unpacked in %s on line %d
+array(0) {
+}
+
+Warning: Only arrays and Traversables can be unpacked in %s on line %d
+array(0) {
+}
+
+Warning: Only arrays and Traversables can be unpacked in %s on line %d
+array(0) {
+}
+
+Warning: Only arrays and Traversables can be unpacked in %s on line %d
+array(5) {
+  [0]=>
+  int(1)
+  [1]=>
+  int(2)
+  [2]=>
+  int(3)
+  [3]=>
+  int(4)
+  [4]=>
+  int(5)
+}
+
+Warning: Only arrays and Traversables can be unpacked in %s on line %d
+
+Warning: Only arrays and Traversables can be unpacked in %s on line %d
+array(5) {
+  [0]=>
+  int(1)
+  [1]=>
+  int(2)
+  [2]=>
+  int(3)
+  [3]=>
+  int(4)
+  [4]=>
+  int(5)
+}
diff --git a/Zend/tests/arg_unpack/method.phpt b/Zend/tests/arg_unpack/method.phpt
new file mode 100644 (file)
index 0000000..d6a6e47
--- /dev/null
@@ -0,0 +1,45 @@
+--TEST--
+Unpack arguments for method calls
+--FILE--
+<?php
+
+class Foo {
+    public function test(...$args) {
+        var_dump($args);
+    }
+
+    public static function test2(...$args) {
+        var_dump($args);
+    }
+}
+
+$foo = new Foo;
+$foo->test(...[1, 2], 3, 4, ...[], 5);
+Foo::test2(1, 2, ...[3, 4], ...[], 5);
+
+?>
+--EXPECT--
+array(5) {
+  [0]=>
+  int(1)
+  [1]=>
+  int(2)
+  [2]=>
+  int(3)
+  [3]=>
+  int(4)
+  [4]=>
+  int(5)
+}
+array(5) {
+  [0]=>
+  int(1)
+  [1]=>
+  int(2)
+  [2]=>
+  int(3)
+  [3]=>
+  int(4)
+  [4]=>
+  int(5)
+}
diff --git a/Zend/tests/arg_unpack/new.phpt b/Zend/tests/arg_unpack/new.phpt
new file mode 100644 (file)
index 0000000..3cf224f
--- /dev/null
@@ -0,0 +1,39 @@
+--TEST--
+Unpack arguments for new expression
+--FILE--
+<?php
+
+class Foo {
+    public function __construct(...$args) {
+        var_dump($args);
+    }
+}
+
+new Foo(...[]);
+new Foo(...[1, 2, 3]);
+new Foo(...[1], 2, ...[], ...[3, 4], 5);
+
+?>
+--EXPECT--
+array(0) {
+}
+array(3) {
+  [0]=>
+  int(1)
+  [1]=>
+  int(2)
+  [2]=>
+  int(3)
+}
+array(5) {
+  [0]=>
+  int(1)
+  [1]=>
+  int(2)
+  [2]=>
+  int(3)
+  [3]=>
+  int(4)
+  [4]=>
+  int(5)
+}
diff --git a/Zend/tests/arg_unpack/string_keys.phpt b/Zend/tests/arg_unpack/string_keys.phpt
new file mode 100644 (file)
index 0000000..443a882
--- /dev/null
@@ -0,0 +1,20 @@
+--TEST--
+Argument unpacking does not work with string keys (forward compatibility for named args)
+--FILE--
+<?php
+
+set_error_handler(function($errno, $errstr) {
+    var_dump($errstr);
+});
+
+var_dump(...[1, 2, "foo" => 3, 4]);
+var_dump(...new ArrayIterator([1, 2, "foo" => 3, 4]));
+
+?>
+--EXPECTF--
+string(36) "Cannot unpack array with string keys"
+int(1)
+int(2)
+string(42) "Cannot unpack Traversable with string keys"
+int(1)
+int(2)
diff --git a/Zend/tests/arg_unpack/traversable_throwing_exception.phpt b/Zend/tests/arg_unpack/traversable_throwing_exception.phpt
new file mode 100644 (file)
index 0000000..8ddc24d
--- /dev/null
@@ -0,0 +1,33 @@
+--TEST--
+Traversables that throw exceptions are properly handled during argument unpack
+--FILE--
+<?php
+
+function test(...$args) {
+    var_dump($args);
+}
+
+class Foo implements IteratorAggregate {
+    public function getIterator() {
+        throw new Exception('getIterator');
+    }
+}
+
+function gen() {
+    yield 1;
+    yield 2;
+    throw new Exception('gen');
+}
+
+try {
+    test(1, 2, ...new Foo, 3, 4);
+} catch (Exception $e) { var_dump($e->getMessage()); }
+
+try {
+    test(1, 2, ...gen(), 3, 4);
+} catch (Exception $e) { var_dump($e->getMessage()); }
+
+?>
+--EXPECT--
+string(11) "getIterator"
+string(3) "gen"
diff --git a/Zend/tests/arg_unpack/traversable_with_by_ref_parameters.phpt b/Zend/tests/arg_unpack/traversable_with_by_ref_parameters.phpt
new file mode 100644 (file)
index 0000000..e862341
--- /dev/null
@@ -0,0 +1,34 @@
+--TEST--
+Traversables cannot be unpacked into by-reference parameters
+--FILE--
+<?php
+
+function test($val1, $val2, $val3, &$ref) {
+    $ref = 42;
+}
+
+function gen($array) {
+    foreach ($array as $element) {
+        yield $element;
+    }
+}
+
+test(...gen([1, 2, 3]), $a);
+var_dump($a);
+test(1, 2, 3, $b, ...gen([4, 5, 6]));
+var_dump($b);
+
+test(...gen([1, 2, 3, 4]));
+test(1, 2, ...gen([3, 4]));
+test(...gen([1, 2]), ...gen([3, 4]));
+
+?>
+--EXPECTF--
+int(42)
+int(42)
+
+Warning: Cannot pass by-reference argument 4 of test() by unpacking a Traversable, passing by-value instead in %s on line %d
+
+Warning: Cannot pass by-reference argument 4 of test() by unpacking a Traversable, passing by-value instead in %s on line %d
+
+Warning: Cannot pass by-reference argument 4 of test() by unpacking a Traversable, passing by-value instead in %s on line %d
index 9b12810b0abd1454ac9cfab5e1b5ea5c01ebe2c9..f789e3397f25c5e5d79dc5d017209ad30c9e7abe 100644 (file)
@@ -2549,8 +2549,11 @@ void zend_do_end_function_call(znode *function_name, znode *result, const znode
                }
                opline = &CG(active_op_array)->opcodes[Z_LVAL(function_name->u.constant)];
        } else {
+               zend_function **function_ptr_ptr;
+               zend_stack_top(&CG(function_call_stack), (void **) &function_ptr_ptr);
+
                opline = get_next_op(CG(active_op_array) TSRMLS_CC);
-               if (!is_method && !is_dynamic_fcall && function_name->op_type==IS_CONST) {
+               if (*function_ptr_ptr) {
                        opline->opcode = ZEND_DO_FCALL;
                        SET_NODE(opline->op1, function_name);
                        SET_UNUSED(opline->op2);
@@ -2562,6 +2565,13 @@ void zend_do_end_function_call(znode *function_name, znode *result, const znode
                        SET_UNUSED(opline->op1);
                        SET_UNUSED(opline->op2);
                        opline->op2.num = --CG(context).nested_calls;
+
+                       /* This would normally be a ZEND_DO_FCALL, but was forced to use
+                        * ZEND_DO_FCALL_BY_NAME due to a ... argument. In this case we need to
+                        * free the function_name */
+                       if (!is_method && !is_dynamic_fcall && function_name->op_type==IS_CONST) {
+                               zval_dtor(&function_name->u.constant);
+                       }
                }
        }
 
@@ -2687,6 +2697,39 @@ void zend_do_pass_param(znode *param, zend_uchar op, int offset TSRMLS_DC) /* {{
 }
 /* }}} */
 
+void zend_do_unpack_params(znode *params, int offset TSRMLS_DC) /* {{{ */
+{
+       zend_op *opline;
+       zend_function **function_ptr_ptr;
+
+       zend_stack_top(&CG(function_call_stack), (void **) &function_ptr_ptr);
+       if (*function_ptr_ptr) {
+               /* If argument unpacking is used argument numbers and sending modes can no longer be
+                * computed at compile time, thus we need access to EX(call). In order to have it we
+                * retroactively emit a ZEND_INIT_FCALL_BY_NAME opcode. */
+               zval func_name;
+               ZVAL_STRING(&func_name, (*function_ptr_ptr)->common.function_name, 1);
+
+               opline = get_next_op(CG(active_op_array) TSRMLS_CC);
+               opline->opcode = ZEND_INIT_FCALL_BY_NAME;
+               opline->result.num = CG(context).nested_calls;
+               SET_UNUSED(opline->op1);
+               opline->op2_type = IS_CONST;
+               opline->op2.constant = zend_add_func_name_literal(CG(active_op_array), &func_name TSRMLS_CC);
+               GET_CACHE_SLOT(opline->op2.constant);
+
+               ++CG(context).nested_calls;
+               *function_ptr_ptr = NULL;
+       }
+
+       opline = get_next_op(CG(active_op_array) TSRMLS_CC);
+       opline->opcode = ZEND_SEND_UNPACK;
+       SET_NODE(opline->op1, params);
+       SET_UNUSED(opline->op2);
+       opline->op2.num = (zend_uint) offset;
+}
+/* }}} */
+
 static int generate_free_switch_expr(const zend_switch_entry *switch_entry TSRMLS_DC) /* {{{ */
 {
        zend_op *opline;
index a32a0c888b910ab9495895234f322f3155b1205a..f884df19466a522f7543e87dd29d280127e38585 100644 (file)
@@ -383,6 +383,7 @@ typedef struct _call_slot {
        zend_function     *fbc;
        zval              *object;
        zend_class_entry  *called_scope;
+       zend_uint          num_additional_args;
        zend_bool          is_ctor_call;
        zend_bool          is_ctor_result_used;
 } call_slot;
@@ -554,6 +555,7 @@ void zend_do_early_binding(TSRMLS_D);
 ZEND_API void zend_do_delayed_early_binding(const zend_op_array *op_array TSRMLS_DC);
 
 void zend_do_pass_param(znode *param, zend_uchar op, int offset TSRMLS_DC);
+void zend_do_unpack_params(znode *params, int offset TSRMLS_DC);
 
 
 void zend_do_boolean_or_begin(znode *expr1, znode *op_token TSRMLS_DC);
index 99bb5e02a9707ecba3b313791ef042c4ce863c48..172f1731d0dd0fd170cd5f32578b7b4d8cc496c6 100644 (file)
@@ -1684,6 +1684,13 @@ ZEND_API zend_execute_data *zend_create_execute_data_from_op_array(zend_op_array
 }
 /* }}} */
 
+zend_always_inline zend_bool zend_is_by_ref_func_arg_fetch(zend_op *opline, call_slot *call TSRMLS_DC) /* {{{ */
+{
+       zend_uint arg_num = (opline->extended_value & ZEND_FETCH_ARG_MASK) + call->num_additional_args;
+       return ARG_SHOULD_BE_SENT_BY_REF(call->fbc, arg_num);
+}
+/* }}} */
+
 #define ZEND_VM_NEXT_OPCODE() \
        CHECK_SYMBOL_TABLES() \
        ZEND_VM_INC_OPCODE(); \
index 752e27fff42e8b955e5ed7fc8414e937d40c17f4..a4bc612bcb1d11b64c6c2cae729e1ff6e8a235d3 100644 (file)
@@ -585,9 +585,11 @@ non_empty_function_call_parameter_list:
                expr_without_variable   { Z_LVAL($$.u.constant) = 1;  zend_do_pass_param(&$1, ZEND_SEND_VAL, Z_LVAL($$.u.constant) TSRMLS_CC); }
        |       variable                                { Z_LVAL($$.u.constant) = 1;  zend_do_pass_param(&$1, ZEND_SEND_VAR, Z_LVAL($$.u.constant) TSRMLS_CC); }
        |       '&' w_variable                          { Z_LVAL($$.u.constant) = 1;  zend_do_pass_param(&$2, ZEND_SEND_REF, Z_LVAL($$.u.constant) TSRMLS_CC); }
+       |       T_ELLIPSIS expr                 { Z_LVAL($$.u.constant) = 0;  zend_do_unpack_params(&$2, Z_LVAL($$.u.constant) TSRMLS_CC); }
        |       non_empty_function_call_parameter_list ',' expr_without_variable        { Z_LVAL($$.u.constant)=Z_LVAL($1.u.constant)+1;  zend_do_pass_param(&$3, ZEND_SEND_VAL, Z_LVAL($$.u.constant) TSRMLS_CC); }
        |       non_empty_function_call_parameter_list ',' variable                                     { Z_LVAL($$.u.constant)=Z_LVAL($1.u.constant)+1;  zend_do_pass_param(&$3, ZEND_SEND_VAR, Z_LVAL($$.u.constant) TSRMLS_CC); }
        |       non_empty_function_call_parameter_list ',' '&' w_variable                       { Z_LVAL($$.u.constant)=Z_LVAL($1.u.constant)+1;  zend_do_pass_param(&$4, ZEND_SEND_REF, Z_LVAL($$.u.constant) TSRMLS_CC); }
+       |       non_empty_function_call_parameter_list ',' T_ELLIPSIS expr                      { Z_LVAL($$.u.constant)=Z_LVAL($1.u.constant);  zend_do_unpack_params(&$4, Z_LVAL($$.u.constant) TSRMLS_CC); }
 ;
 
 global_var_list:
index 5969d8c1d12d5e5a8aea5deac25a23d5999d1ad4..37af82cef2096b3f09fdff6858b18b9d577e1885 100644 (file)
@@ -1143,7 +1143,7 @@ ZEND_VM_HANDLER(92, ZEND_FETCH_FUNC_ARG, CONST|TMP|VAR|CV, UNUSED|CONST|VAR)
        USE_OPLINE
 
        ZEND_VM_DISPATCH_TO_HELPER_EX(zend_fetch_var_address_helper, type,
-               ARG_SHOULD_BE_SENT_BY_REF(EX(call)->fbc, (opline->extended_value & ZEND_FETCH_ARG_MASK))?BP_VAR_W:BP_VAR_R);
+               zend_is_by_ref_func_arg_fetch(opline, EX(call) TSRMLS_CC) ? BP_VAR_W : BP_VAR_R);
 }
 
 ZEND_VM_HANDLER(95, ZEND_FETCH_UNSET, CONST|TMP|VAR|CV, UNUSED|CONST|VAR)
@@ -1251,9 +1251,8 @@ ZEND_VM_HANDLER(93, ZEND_FETCH_DIM_FUNC_ARG, VAR|CV, CONST|TMP|VAR|UNUSED|CV)
 
        SAVE_OPLINE();
 
-       if (ARG_SHOULD_BE_SENT_BY_REF(EX(call)->fbc, (opline->extended_value & ZEND_FETCH_ARG_MASK))) {
+       if (zend_is_by_ref_func_arg_fetch(opline, EX(call) TSRMLS_CC)) {
                zval **container = GET_OP1_ZVAL_PTR_PTR(BP_VAR_W);
-
                if (OP1_TYPE == IS_VAR && UNEXPECTED(container == NULL)) {
                        zend_error_noreturn(E_ERROR, "Cannot use string offset as an array");
                }
@@ -1488,7 +1487,7 @@ ZEND_VM_HANDLER(94, ZEND_FETCH_OBJ_FUNC_ARG, VAR|UNUSED|CV, CONST|TMP|VAR|CV)
 {
        USE_OPLINE
 
-       if (ARG_SHOULD_BE_SENT_BY_REF(EX(call)->fbc, (opline->extended_value & ZEND_FETCH_ARG_MASK))) {
+       if (zend_is_by_ref_func_arg_fetch(opline, EX(call) TSRMLS_CC)) {
                /* Behave like FETCH_OBJ_W */
                zend_free_op free_op1, free_op2;
                zval *property;
@@ -1902,6 +1901,7 @@ ZEND_VM_HELPER(zend_do_fcall_common_helper, ANY, ANY)
        USE_OPLINE
        zend_bool should_change_scope = 0;
        zend_function *fbc = EX(function_state).function;
+       zend_uint num_args;
 
        SAVE_OPLINE();
        EX(object) = EX(call)->object;
@@ -1946,19 +1946,18 @@ ZEND_VM_HELPER(zend_do_fcall_common_helper, ANY, ANY)
                EG(called_scope) = EX(call)->called_scope;
        }
 
+       num_args = opline->extended_value + EX(call)->num_additional_args;
        EX(function_state).arguments = zend_vm_stack_top(TSRMLS_C);
-       zend_vm_stack_push((void*)(zend_uintptr_t)opline->extended_value TSRMLS_CC);
+       zend_vm_stack_push((void*)(zend_uintptr_t) num_args TSRMLS_CC);
        LOAD_OPLINE();
 
        if (fbc->type == ZEND_INTERNAL_FUNCTION) {
                if (fbc->common.fn_flags & ZEND_ACC_HAS_TYPE_HINTS) {
-                       zend_uint i=0;
-                       zval **p = (zval**)EX(function_state).arguments;
-                       ulong arg_count = opline->extended_value;
+                       zend_uint i;
+                       void **p = EX(function_state).arguments - num_args;
 
-                       while (arg_count>0) {
-                               zend_verify_arg_type(fbc, ++i, *(p-arg_count), 0 TSRMLS_CC);
-                               arg_count--;
+                       for (i = 0; i < num_args; ++i, ++p) {
+                               zend_verify_arg_type(fbc, i + 1, (zval *) *p, 0 TSRMLS_CC);
                        }
                }
 
@@ -1972,7 +1971,7 @@ ZEND_VM_HELPER(zend_do_fcall_common_helper, ANY, ANY)
 
                        if (!zend_execute_internal) {
                                /* saves one function call if zend_execute_internal is not used */
-                               fbc->internal_function.handler(opline->extended_value, ret->var.ptr, &ret->var.ptr, EX(object), RETURN_VALUE_USED(opline) TSRMLS_CC);
+                               fbc->internal_function.handler(num_args, ret->var.ptr, &ret->var.ptr, EX(object), RETURN_VALUE_USED(opline) TSRMLS_CC);
                        } else {
                                zend_execute_internal(execute_data, NULL, RETURN_VALUE_USED(opline) TSRMLS_CC);
                        }
@@ -2022,7 +2021,7 @@ ZEND_VM_HELPER(zend_do_fcall_common_helper, ANY, ANY)
 
                /* Not sure what should be done here if it's a static method */
                if (EXPECTED(EX(object) != NULL)) {
-                       Z_OBJ_HT_P(EX(object))->call_method(fbc->common.function_name, opline->extended_value, EX_T(opline->result.var).var.ptr, &EX_T(opline->result.var).var.ptr, EX(object), RETURN_VALUE_USED(opline) TSRMLS_CC);
+                       Z_OBJ_HT_P(EX(object))->call_method(fbc->common.function_name, num_args, EX_T(opline->result.var).var.ptr, &EX_T(opline->result.var).var.ptr, EX(object), RETURN_VALUE_USED(opline) TSRMLS_CC);
                } else {
                        zend_error_noreturn(E_ERROR, "Cannot call overloaded function for non-object");
                }
@@ -2476,6 +2475,8 @@ ZEND_VM_HANDLER(112, ZEND_INIT_METHOD_CALL, TMP|VAR|UNUSED|CV, CONST|TMP|VAR|CV)
                        call->object = this_ptr;
                }
        }
+
+       call->num_additional_args = 0;
        call->is_ctor_call = 0;
        EX(call) = call;
 
@@ -2602,6 +2603,8 @@ ZEND_VM_HANDLER(113, ZEND_INIT_STATIC_METHOD_CALL, CONST|VAR, CONST|TMP|VAR|UNUS
                        call->called_scope = Z_OBJCE_P(call->object);
                }
        }
+
+       call->num_additional_args = 0;
        call->is_ctor_call = 0;
        EX(call) = call;
 
@@ -2625,10 +2628,13 @@ ZEND_VM_HANDLER(59, ZEND_INIT_FCALL_BY_NAME, ANY, CONST|TMP|VAR|CV)
                } else {
                        CACHE_PTR(opline->op2.literal->cache_slot, call->fbc);
                }
+
                call->object = NULL;
                call->called_scope = NULL;
+               call->num_additional_args = 0;
                call->is_ctor_call = 0;
                EX(call) = call;
+
                /*CHECK_EXCEPTION();*/
                ZEND_VM_NEXT_OPCODE();
        } else {
@@ -2653,10 +2659,13 @@ ZEND_VM_HANDLER(59, ZEND_INIT_FCALL_BY_NAME, ANY, CONST|TMP|VAR|CV)
                        }
                        efree(lcname);
                        FREE_OP2();
+
                        call->object = NULL;
                        call->called_scope = NULL;
+                       call->num_additional_args = 0;
                        call->is_ctor_call = 0;
                        EX(call) = call;
+
                        CHECK_EXCEPTION();
                        ZEND_VM_NEXT_OPCODE();
                } else if (OP2_TYPE != IS_CONST && OP2_TYPE != IS_TMP_VAR &&
@@ -2673,8 +2682,11 @@ ZEND_VM_HANDLER(59, ZEND_INIT_FCALL_BY_NAME, ANY, CONST|TMP|VAR|CV)
                        } else {
                                FREE_OP2();
                        }
+
+                       call->num_additional_args = 0;
                        call->is_ctor_call = 0;
                        EX(call) = call;
+
                        CHECK_EXCEPTION();
                        ZEND_VM_NEXT_OPCODE();
                } else if (OP2_TYPE != IS_CONST &&
@@ -2740,8 +2752,11 @@ ZEND_VM_HANDLER(59, ZEND_INIT_FCALL_BY_NAME, ANY, CONST|TMP|VAR|CV)
                        if (UNEXPECTED(call->fbc == NULL)) {
                                zend_error_noreturn(E_ERROR, "Call to undefined method %s::%s()", ce->name, Z_STRVAL_PP(method));
                        }
+
+                       call->num_additional_args = 0;
                        call->is_ctor_call = 0;
                        EX(call) = call;
+
                        FREE_OP2();
                        CHECK_EXCEPTION();
                        ZEND_VM_NEXT_OPCODE();
@@ -2779,7 +2794,9 @@ ZEND_VM_HANDLER(69, ZEND_INIT_NS_FCALL_BY_NAME, ANY, CONST)
 
        call->object = NULL;
        call->called_scope = NULL;
+       call->num_additional_args = 0;
        call->is_ctor_call = 0;
+
        EX(call) = call;
        ZEND_VM_NEXT_OPCODE();
 }
@@ -2805,9 +2822,11 @@ ZEND_VM_HANDLER(60, ZEND_DO_FCALL, CONST, ANY)
        } else {
                CACHE_PTR(opline->op1.literal->cache_slot, EX(function_state).function);
        }
+
        call->fbc = EX(function_state).function;
        call->object = NULL;
        call->called_scope = NULL;
+       call->num_additional_args = 0;
        call->is_ctor_call = 0;
        EX(call) = call;
 
@@ -3039,10 +3058,13 @@ ZEND_VM_HANDLER(65, ZEND_SEND_VAL, CONST|TMP, ANY)
        USE_OPLINE
 
        SAVE_OPLINE();
-       if (opline->extended_value==ZEND_DO_FCALL_BY_NAME
-               && ARG_MUST_BE_SENT_BY_REF(EX(call)->fbc, opline->op2.opline_num)) {
-                       zend_error_noreturn(E_ERROR, "Cannot pass parameter %d by reference", opline->op2.opline_num);
+       if (opline->extended_value == ZEND_DO_FCALL_BY_NAME) {
+               int arg_num = opline->op2.num + EX(call)->num_additional_args;
+               if (ARG_MUST_BE_SENT_BY_REF(EX(call)->fbc, arg_num)) {
+                       zend_error_noreturn(E_ERROR, "Cannot pass parameter %d by reference", arg_num);
+               }
        }
+
        {
                zval *valptr;
                zval *value;
@@ -3100,14 +3122,18 @@ ZEND_VM_HANDLER(106, ZEND_SEND_VAR_NO_REF, VAR|CV, ANY)
        USE_OPLINE
        zend_free_op free_op1;
        zval *varptr;
+       int arg_num;
 
        SAVE_OPLINE();
        if (opline->extended_value & ZEND_ARG_COMPILE_TIME_BOUND) { /* Had function_ptr at compile_time */
                if (!(opline->extended_value & ZEND_ARG_SEND_BY_REF)) {
                        ZEND_VM_DISPATCH_TO_HELPER(zend_send_by_var_helper);
                }
-       } else if (!ARG_SHOULD_BE_SENT_BY_REF(EX(call)->fbc, opline->op2.opline_num)) {
-               ZEND_VM_DISPATCH_TO_HELPER(zend_send_by_var_helper);
+       } else {
+               arg_num = opline->op2.num + EX(call)->num_additional_args;
+               if (!ARG_SHOULD_BE_SENT_BY_REF(EX(call)->fbc, arg_num)) {
+                       ZEND_VM_DISPATCH_TO_HELPER(zend_send_by_var_helper);
+               }
        }
 
        varptr = GET_OP1_ZVAL_PTR(BP_VAR_R);
@@ -3125,7 +3151,7 @@ ZEND_VM_HANDLER(106, ZEND_SEND_VAR_NO_REF, VAR|CV, ANY)
 
                if ((opline->extended_value & ZEND_ARG_COMPILE_TIME_BOUND) ?
                        !(opline->extended_value & ZEND_ARG_SEND_SILENT) :
-                       !ARG_MAY_BE_SENT_BY_REF(EX(call)->fbc, opline->op2.opline_num)) {
+                       !ARG_MAY_BE_SENT_BY_REF(EX(call)->fbc, arg_num)) {
                        zend_error(E_STRICT, "Only variables should be passed by reference");
                }
                ALLOC_ZVAL(valptr);
@@ -3162,9 +3188,11 @@ ZEND_VM_HANDLER(67, ZEND_SEND_REF, VAR|CV, ANY)
        }
 
        if (opline->extended_value == ZEND_DO_FCALL_BY_NAME &&
-           EX(function_state).function->type == ZEND_INTERNAL_FUNCTION &&
-           !ARG_SHOULD_BE_SENT_BY_REF(EX(call)->fbc, opline->op2.opline_num)) {
-               ZEND_VM_DISPATCH_TO_HELPER(zend_send_by_var_helper);
+           EX(function_state).function->type == ZEND_INTERNAL_FUNCTION) { 
+               int arg_num = opline->op2.num + EX(call)->num_additional_args;
+               if (!ARG_SHOULD_BE_SENT_BY_REF(EX(call)->fbc, arg_num)) {
+                       ZEND_VM_DISPATCH_TO_HELPER(zend_send_by_var_helper);
+               }
        }
 
        SEPARATE_ZVAL_TO_MAKE_IS_REF(varptr_ptr);
@@ -3181,14 +3209,164 @@ ZEND_VM_HANDLER(66, ZEND_SEND_VAR, VAR|CV, ANY)
 {
        USE_OPLINE
 
-       if ((opline->extended_value == ZEND_DO_FCALL_BY_NAME)
-               && ARG_SHOULD_BE_SENT_BY_REF(EX(call)->fbc, opline->op2.opline_num)) {
-               ZEND_VM_DISPATCH_TO_HANDLER(ZEND_SEND_REF);
+       if (opline->extended_value == ZEND_DO_FCALL_BY_NAME) {
+               int arg_num = opline->op2.num + EX(call)->num_additional_args;
+               if (ARG_SHOULD_BE_SENT_BY_REF(EX(call)->fbc, arg_num)) {
+                       ZEND_VM_DISPATCH_TO_HANDLER(ZEND_SEND_REF);
+               }
        }
        SAVE_OPLINE();
        ZEND_VM_DISPATCH_TO_HELPER(zend_send_by_var_helper);
 }
 
+ZEND_VM_HANDLER(165, ZEND_SEND_UNPACK, ANY, ANY)
+{
+       USE_OPLINE
+       zend_free_op free_op1;
+       zval *args;
+       int arg_num;
+       SAVE_OPLINE();
+
+       args = GET_OP1_ZVAL_PTR(BP_VAR_R);
+       arg_num = opline->op2.num + EX(call)->num_additional_args + 1;
+
+       switch (Z_TYPE_P(args)) {
+               case IS_ARRAY: {
+                       HashTable *ht = Z_ARRVAL_P(args);
+                       HashPosition pos;
+                       zval **arg_ptr, *arg;
+
+                       ZEND_VM_STACK_GROW_IF_NEEDED(zend_hash_num_elements(ht));
+
+                       for (zend_hash_internal_pointer_reset_ex(ht, &pos);
+                            zend_hash_get_current_data_ex(ht, (void **) &arg_ptr, &pos) == SUCCESS;
+                            zend_hash_move_forward_ex(ht, &pos), ++arg_num
+                       ) {
+                               char *name;
+                               zend_uint name_len;
+                               zend_ulong index;
+
+                               if (zend_hash_get_current_key_ex(ht, &name, &name_len, &index, 0, &pos) == HASH_KEY_IS_STRING) {
+                                       zend_error(E_RECOVERABLE_ERROR, "Cannot unpack array with string keys");
+                                       FREE_OP1();
+                                       CHECK_EXCEPTION();
+                                       ZEND_VM_NEXT_OPCODE();
+                               }
+
+                               if (ARG_SHOULD_BE_SENT_BY_REF(EX(call)->fbc, arg_num)) {
+                                       SEPARATE_ZVAL_TO_MAKE_IS_REF(arg_ptr);
+                                       arg = *arg_ptr;
+                                       Z_ADDREF_P(arg);
+                               } else if (Z_ISREF_PP(arg_ptr)) {
+                                       ALLOC_ZVAL(arg);
+                                       MAKE_COPY_ZVAL(arg_ptr, arg);
+                               } else {
+                                       arg = *arg_ptr;
+                                       Z_ADDREF_P(arg);
+                               }
+
+                               zend_vm_stack_push(arg TSRMLS_CC);
+                               EX(call)->num_additional_args++;
+                       }
+                       break;
+               }
+               case IS_OBJECT: {
+                       zend_class_entry *ce = Z_OBJCE_P(args);
+                       zend_object_iterator *iter;
+
+                       if (!ce || !ce->get_iterator) {
+                               zend_error(E_WARNING, "Only arrays and Traversables can be unpacked");
+                               break;
+                       }
+
+                       iter = ce->get_iterator(ce, args, 0 TSRMLS_CC);
+                       if (UNEXPECTED(!iter)) {
+                               FREE_OP1();
+                               if (!EG(exception)) {
+                                       zend_throw_exception_ex(
+                                               NULL, 0 TSRMLS_CC, "Object of type %s did not create an Iterator", ce->name
+                                       );
+                               }
+                               HANDLE_EXCEPTION();
+                       }
+
+                       if (iter->funcs->rewind) {
+                               iter->funcs->rewind(iter TSRMLS_CC);
+                               if (UNEXPECTED(EG(exception) != NULL)) {
+                                       ZEND_VM_C_GOTO(unpack_iter_dtor);
+                               }
+                       }
+
+                       for (; iter->funcs->valid(iter TSRMLS_CC) == SUCCESS; ++arg_num) {
+                               zval **arg_ptr, *arg;
+
+                               if (UNEXPECTED(EG(exception) != NULL)) {
+                                       ZEND_VM_C_GOTO(unpack_iter_dtor);
+                               }
+
+                               iter->funcs->get_current_data(iter, &arg_ptr TSRMLS_CC);
+                               if (UNEXPECTED(EG(exception) != NULL)) {
+                                       ZEND_VM_C_GOTO(unpack_iter_dtor);
+                               }
+
+                               if (iter->funcs->get_current_key) {
+                                       zval key;
+                                       iter->funcs->get_current_key(iter, &key TSRMLS_CC);
+                                       if (UNEXPECTED(EG(exception) != NULL)) {
+                                               ZEND_VM_C_GOTO(unpack_iter_dtor);
+                                       }
+
+                                       if (Z_TYPE(key) == IS_STRING) {
+                                               zend_error(E_RECOVERABLE_ERROR,
+                                                       "Cannot unpack Traversable with string keys");
+                                               zval_dtor(&key);
+                                               ZEND_VM_C_GOTO(unpack_iter_dtor);
+                                       }
+
+                                       zval_dtor(&key);
+                               }
+
+                               if (ARG_MUST_BE_SENT_BY_REF(EX(call)->fbc, arg_num)) {
+                                       zend_error(
+                                               E_WARNING, "Cannot pass by-reference argument %d of %s%s%s()"
+                                               " by unpacking a Traversable, passing by-value instead", arg_num,
+                                               EX(call)->fbc->common.scope ? EX(call)->fbc->common.scope->name : "",
+                                               EX(call)->fbc->common.scope ? "::" : "",
+                                               EX(call)->fbc->common.function_name
+                                       );
+                               }
+                               
+                               if (Z_ISREF_PP(arg_ptr)) {
+                                       ALLOC_ZVAL(arg);
+                                       MAKE_COPY_ZVAL(arg_ptr, arg);
+                               } else {
+                                       arg = *arg_ptr;
+                                       Z_ADDREF_P(arg);
+                               }
+
+                               ZEND_VM_STACK_GROW_IF_NEEDED(1);
+                               zend_vm_stack_push(arg TSRMLS_CC);
+                               EX(call)->num_additional_args++;
+
+                               iter->funcs->move_forward(iter TSRMLS_CC);
+                               if (UNEXPECTED(EG(exception) != NULL)) {
+                                       ZEND_VM_C_GOTO(unpack_iter_dtor);
+                               }
+                       }
+
+ZEND_VM_C_LABEL(unpack_iter_dtor):
+                       iter->funcs->dtor(iter TSRMLS_CC);
+                       break;
+               }
+               default:
+                       zend_error(E_WARNING, "Only arrays and Traversables can be unpacked");
+       }
+
+       FREE_OP1();
+       CHECK_EXCEPTION();
+       ZEND_VM_NEXT_OPCODE();
+}
+
 ZEND_VM_HANDLER(63, ZEND_RECV, ANY, ANY)
 {
        USE_OPLINE
@@ -3424,6 +3602,7 @@ ZEND_VM_HANDLER(68, ZEND_NEW, ANY, ANY)
                call->fbc = constructor;
                call->object = object_zval;
                call->called_scope = EX_T(opline->op1.var).class_entry;
+               call->num_additional_args = 0;
                call->is_ctor_call = 1;
                call->is_ctor_result_used = RETURN_VALUE_USED(opline);
                EX(call) = call;
index e9eda3e0a2b121ac943d4eaff81911206dc7bce8..0651389f74131b96583f89464a95ca5874f2086b 100644 (file)
@@ -481,6 +481,7 @@ static int ZEND_FASTCALL zend_do_fcall_common_helper_SPEC(ZEND_OPCODE_HANDLER_AR
        USE_OPLINE
        zend_bool should_change_scope = 0;
        zend_function *fbc = EX(function_state).function;
+       zend_uint num_args;
 
        SAVE_OPLINE();
        EX(object) = EX(call)->object;
@@ -525,19 +526,18 @@ static int ZEND_FASTCALL zend_do_fcall_common_helper_SPEC(ZEND_OPCODE_HANDLER_AR
                EG(called_scope) = EX(call)->called_scope;
        }
 
+       num_args = opline->extended_value + EX(call)->num_additional_args;
        EX(function_state).arguments = zend_vm_stack_top(TSRMLS_C);
-       zend_vm_stack_push((void*)(zend_uintptr_t)opline->extended_value TSRMLS_CC);
+       zend_vm_stack_push((void*)(zend_uintptr_t) num_args TSRMLS_CC);
        LOAD_OPLINE();
 
        if (fbc->type == ZEND_INTERNAL_FUNCTION) {
                if (fbc->common.fn_flags & ZEND_ACC_HAS_TYPE_HINTS) {
-                       zend_uint i=0;
-                       zval **p = (zval**)EX(function_state).arguments;
-                       ulong arg_count = opline->extended_value;
+                       zend_uint i;
+                       void **p = EX(function_state).arguments - num_args;
 
-                       while (arg_count>0) {
-                               zend_verify_arg_type(fbc, ++i, *(p-arg_count), 0 TSRMLS_CC);
-                               arg_count--;
+                       for (i = 0; i < num_args; ++i, ++p) {
+                               zend_verify_arg_type(fbc, i + 1, (zval *) *p, 0 TSRMLS_CC);
                        }
                }
 
@@ -551,7 +551,7 @@ static int ZEND_FASTCALL zend_do_fcall_common_helper_SPEC(ZEND_OPCODE_HANDLER_AR
 
                        if (!zend_execute_internal) {
                                /* saves one function call if zend_execute_internal is not used */
-                               fbc->internal_function.handler(opline->extended_value, ret->var.ptr, &ret->var.ptr, EX(object), RETURN_VALUE_USED(opline) TSRMLS_CC);
+                               fbc->internal_function.handler(num_args, ret->var.ptr, &ret->var.ptr, EX(object), RETURN_VALUE_USED(opline) TSRMLS_CC);
                        } else {
                                zend_execute_internal(execute_data, NULL, RETURN_VALUE_USED(opline) TSRMLS_CC);
                        }
@@ -601,7 +601,7 @@ static int ZEND_FASTCALL zend_do_fcall_common_helper_SPEC(ZEND_OPCODE_HANDLER_AR
 
                /* Not sure what should be done here if it's a static method */
                if (EXPECTED(EX(object) != NULL)) {
-                       Z_OBJ_HT_P(EX(object))->call_method(fbc->common.function_name, opline->extended_value, EX_T(opline->result.var).var.ptr, &EX_T(opline->result.var).var.ptr, EX(object), RETURN_VALUE_USED(opline) TSRMLS_CC);
+                       Z_OBJ_HT_P(EX(object))->call_method(fbc->common.function_name, num_args, EX_T(opline->result.var).var.ptr, &EX_T(opline->result.var).var.ptr, EX(object), RETURN_VALUE_USED(opline) TSRMLS_CC);
                } else {
                        zend_error_noreturn(E_ERROR, "Cannot call overloaded function for non-object");
                }
@@ -701,6 +701,154 @@ static int ZEND_FASTCALL  ZEND_GENERATOR_RETURN_SPEC_HANDLER(ZEND_OPCODE_HANDLER
        ZEND_VM_RETURN();
 }
 
+static int ZEND_FASTCALL  ZEND_SEND_UNPACK_SPEC_HANDLER(ZEND_OPCODE_HANDLER_ARGS)
+{
+       USE_OPLINE
+       zend_free_op free_op1;
+       zval *args;
+       int arg_num;
+       SAVE_OPLINE();
+
+       args = get_zval_ptr(opline->op1_type, &opline->op1, execute_data, &free_op1, BP_VAR_R);
+       arg_num = opline->op2.num + EX(call)->num_additional_args + 1;
+
+       switch (Z_TYPE_P(args)) {
+               case IS_ARRAY: {
+                       HashTable *ht = Z_ARRVAL_P(args);
+                       HashPosition pos;
+                       zval **arg_ptr, *arg;
+
+                       ZEND_VM_STACK_GROW_IF_NEEDED(zend_hash_num_elements(ht));
+
+                       for (zend_hash_internal_pointer_reset_ex(ht, &pos);
+                            zend_hash_get_current_data_ex(ht, (void **) &arg_ptr, &pos) == SUCCESS;
+                            zend_hash_move_forward_ex(ht, &pos), ++arg_num
+                       ) {
+                               char *name;
+                               zend_uint name_len;
+                               zend_ulong index;
+
+                               if (zend_hash_get_current_key_ex(ht, &name, &name_len, &index, 0, &pos) == HASH_KEY_IS_STRING) {
+                                       zend_error(E_RECOVERABLE_ERROR, "Cannot unpack array with string keys");
+                                       FREE_OP(free_op1);
+                                       CHECK_EXCEPTION();
+                                       ZEND_VM_NEXT_OPCODE();
+                               }
+
+                               if (ARG_SHOULD_BE_SENT_BY_REF(EX(call)->fbc, arg_num)) {
+                                       SEPARATE_ZVAL_TO_MAKE_IS_REF(arg_ptr);
+                                       arg = *arg_ptr;
+                                       Z_ADDREF_P(arg);
+                               } else if (Z_ISREF_PP(arg_ptr)) {
+                                       ALLOC_ZVAL(arg);
+                                       MAKE_COPY_ZVAL(arg_ptr, arg);
+                               } else {
+                                       arg = *arg_ptr;
+                                       Z_ADDREF_P(arg);
+                               }
+
+                               zend_vm_stack_push(arg TSRMLS_CC);
+                               EX(call)->num_additional_args++;
+                       }
+                       break;
+               }
+               case IS_OBJECT: {
+                       zend_class_entry *ce = Z_OBJCE_P(args);
+                       zend_object_iterator *iter;
+
+                       if (!ce || !ce->get_iterator) {
+                               zend_error(E_WARNING, "Only arrays and Traversables can be unpacked");
+                               break;
+                       }
+
+                       iter = ce->get_iterator(ce, args, 0 TSRMLS_CC);
+                       if (UNEXPECTED(!iter)) {
+                               FREE_OP(free_op1);
+                               if (!EG(exception)) {
+                                       zend_throw_exception_ex(
+                                               NULL, 0 TSRMLS_CC, "Object of type %s did not create an Iterator", ce->name
+                                       );
+                               }
+                               HANDLE_EXCEPTION();
+                       }
+
+                       if (iter->funcs->rewind) {
+                               iter->funcs->rewind(iter TSRMLS_CC);
+                               if (UNEXPECTED(EG(exception) != NULL)) {
+                                       goto unpack_iter_dtor;
+                               }
+                       }
+
+                       for (; iter->funcs->valid(iter TSRMLS_CC) == SUCCESS; ++arg_num) {
+                               zval **arg_ptr, *arg;
+
+                               if (UNEXPECTED(EG(exception) != NULL)) {
+                                       goto unpack_iter_dtor;
+                               }
+
+                               iter->funcs->get_current_data(iter, &arg_ptr TSRMLS_CC);
+                               if (UNEXPECTED(EG(exception) != NULL)) {
+                                       goto unpack_iter_dtor;
+                               }
+
+                               if (iter->funcs->get_current_key) {
+                                       zval key;
+                                       iter->funcs->get_current_key(iter, &key TSRMLS_CC);
+                                       if (UNEXPECTED(EG(exception) != NULL)) {
+                                               goto unpack_iter_dtor;
+                                       }
+
+                                       if (Z_TYPE(key) == IS_STRING) {
+                                               zend_error(E_RECOVERABLE_ERROR,
+                                                       "Cannot unpack Traversable with string keys");
+                                               zval_dtor(&key);
+                                               goto unpack_iter_dtor;
+                                       }
+
+                                       zval_dtor(&key);
+                               }
+
+                               if (ARG_MUST_BE_SENT_BY_REF(EX(call)->fbc, arg_num)) {
+                                       zend_error(
+                                               E_WARNING, "Cannot pass by-reference argument %d of %s%s%s()"
+                                               " by unpacking a Traversable, passing by-value instead", arg_num,
+                                               EX(call)->fbc->common.scope ? EX(call)->fbc->common.scope->name : "",
+                                               EX(call)->fbc->common.scope ? "::" : "",
+                                               EX(call)->fbc->common.function_name
+                                       );
+                               }
+
+                               if (Z_ISREF_PP(arg_ptr)) {
+                                       ALLOC_ZVAL(arg);
+                                       MAKE_COPY_ZVAL(arg_ptr, arg);
+                               } else {
+                                       arg = *arg_ptr;
+                                       Z_ADDREF_P(arg);
+                               }
+
+                               ZEND_VM_STACK_GROW_IF_NEEDED(1);
+                               zend_vm_stack_push(arg TSRMLS_CC);
+                               EX(call)->num_additional_args++;
+
+                               iter->funcs->move_forward(iter TSRMLS_CC);
+                               if (UNEXPECTED(EG(exception) != NULL)) {
+                                       goto unpack_iter_dtor;
+                               }
+                       }
+
+unpack_iter_dtor:
+                       iter->funcs->dtor(iter TSRMLS_CC);
+                       break;
+               }
+               default:
+                       zend_error(E_WARNING, "Only arrays and Traversables can be unpacked");
+       }
+
+       FREE_OP(free_op1);
+       CHECK_EXCEPTION();
+       ZEND_VM_NEXT_OPCODE();
+}
+
 static int ZEND_FASTCALL  ZEND_RECV_SPEC_HANDLER(ZEND_OPCODE_HANDLER_ARGS)
 {
        USE_OPLINE
@@ -814,6 +962,7 @@ static int ZEND_FASTCALL  ZEND_NEW_SPEC_HANDLER(ZEND_OPCODE_HANDLER_ARGS)
                call->fbc = constructor;
                call->object = object_zval;
                call->called_scope = EX_T(opline->op1.var).class_entry;
+               call->num_additional_args = 0;
                call->is_ctor_call = 1;
                call->is_ctor_result_used = RETURN_VALUE_USED(opline);
                EX(call) = call;
@@ -1285,10 +1434,13 @@ static int ZEND_FASTCALL  ZEND_INIT_FCALL_BY_NAME_SPEC_CONST_HANDLER(ZEND_OPCODE
                } else {
                        CACHE_PTR(opline->op2.literal->cache_slot, call->fbc);
                }
+
                call->object = NULL;
                call->called_scope = NULL;
+               call->num_additional_args = 0;
                call->is_ctor_call = 0;
                EX(call) = call;
+
                /*CHECK_EXCEPTION();*/
                ZEND_VM_NEXT_OPCODE();
        } else {
@@ -1315,8 +1467,10 @@ static int ZEND_FASTCALL  ZEND_INIT_FCALL_BY_NAME_SPEC_CONST_HANDLER(ZEND_OPCODE
 
                        call->object = NULL;
                        call->called_scope = NULL;
+                       call->num_additional_args = 0;
                        call->is_ctor_call = 0;
                        EX(call) = call;
+
                        CHECK_EXCEPTION();
                        ZEND_VM_NEXT_OPCODE();
                } else if (IS_CONST != IS_CONST && IS_CONST != IS_TMP_VAR &&
@@ -1333,8 +1487,11 @@ static int ZEND_FASTCALL  ZEND_INIT_FCALL_BY_NAME_SPEC_CONST_HANDLER(ZEND_OPCODE
                        } else {
 
                        }
+
+                       call->num_additional_args = 0;
                        call->is_ctor_call = 0;
                        EX(call) = call;
+
                        CHECK_EXCEPTION();
                        ZEND_VM_NEXT_OPCODE();
                } else if (IS_CONST != IS_CONST &&
@@ -1400,6 +1557,8 @@ static int ZEND_FASTCALL  ZEND_INIT_FCALL_BY_NAME_SPEC_CONST_HANDLER(ZEND_OPCODE
                        if (UNEXPECTED(call->fbc == NULL)) {
                                zend_error_noreturn(E_ERROR, "Call to undefined method %s::%s()", ce->name, Z_STRVAL_PP(method));
                        }
+
+                       call->num_additional_args = 0;
                        call->is_ctor_call = 0;
                        EX(call) = call;
 
@@ -1439,7 +1598,9 @@ static int ZEND_FASTCALL  ZEND_INIT_NS_FCALL_BY_NAME_SPEC_CONST_HANDLER(ZEND_OPC
 
        call->object = NULL;
        call->called_scope = NULL;
+       call->num_additional_args = 0;
        call->is_ctor_call = 0;
+
        EX(call) = call;
        ZEND_VM_NEXT_OPCODE();
 }
@@ -1609,10 +1770,13 @@ static int ZEND_FASTCALL  ZEND_INIT_FCALL_BY_NAME_SPEC_TMP_HANDLER(ZEND_OPCODE_H
                } else {
                        CACHE_PTR(opline->op2.literal->cache_slot, call->fbc);
                }
+
                call->object = NULL;
                call->called_scope = NULL;
+               call->num_additional_args = 0;
                call->is_ctor_call = 0;
                EX(call) = call;
+
                /*CHECK_EXCEPTION();*/
                ZEND_VM_NEXT_OPCODE();
        } else {
@@ -1637,10 +1801,13 @@ static int ZEND_FASTCALL  ZEND_INIT_FCALL_BY_NAME_SPEC_TMP_HANDLER(ZEND_OPCODE_H
                        }
                        efree(lcname);
                        zval_dtor(free_op2.var);
+
                        call->object = NULL;
                        call->called_scope = NULL;
+                       call->num_additional_args = 0;
                        call->is_ctor_call = 0;
                        EX(call) = call;
+
                        CHECK_EXCEPTION();
                        ZEND_VM_NEXT_OPCODE();
                } else if (IS_TMP_VAR != IS_CONST && IS_TMP_VAR != IS_TMP_VAR &&
@@ -1657,8 +1824,11 @@ static int ZEND_FASTCALL  ZEND_INIT_FCALL_BY_NAME_SPEC_TMP_HANDLER(ZEND_OPCODE_H
                        } else {
                                zval_dtor(free_op2.var);
                        }
+
+                       call->num_additional_args = 0;
                        call->is_ctor_call = 0;
                        EX(call) = call;
+
                        CHECK_EXCEPTION();
                        ZEND_VM_NEXT_OPCODE();
                } else if (IS_TMP_VAR != IS_CONST &&
@@ -1724,8 +1894,11 @@ static int ZEND_FASTCALL  ZEND_INIT_FCALL_BY_NAME_SPEC_TMP_HANDLER(ZEND_OPCODE_H
                        if (UNEXPECTED(call->fbc == NULL)) {
                                zend_error_noreturn(E_ERROR, "Call to undefined method %s::%s()", ce->name, Z_STRVAL_PP(method));
                        }
+
+                       call->num_additional_args = 0;
                        call->is_ctor_call = 0;
                        EX(call) = call;
+
                        zval_dtor(free_op2.var);
                        CHECK_EXCEPTION();
                        ZEND_VM_NEXT_OPCODE();
@@ -1796,10 +1969,13 @@ static int ZEND_FASTCALL  ZEND_INIT_FCALL_BY_NAME_SPEC_VAR_HANDLER(ZEND_OPCODE_H
                } else {
                        CACHE_PTR(opline->op2.literal->cache_slot, call->fbc);
                }
+
                call->object = NULL;
                call->called_scope = NULL;
+               call->num_additional_args = 0;
                call->is_ctor_call = 0;
                EX(call) = call;
+
                /*CHECK_EXCEPTION();*/
                ZEND_VM_NEXT_OPCODE();
        } else {
@@ -1824,10 +2000,13 @@ static int ZEND_FASTCALL  ZEND_INIT_FCALL_BY_NAME_SPEC_VAR_HANDLER(ZEND_OPCODE_H
                        }
                        efree(lcname);
                        zval_ptr_dtor_nogc(&free_op2.var);
+
                        call->object = NULL;
                        call->called_scope = NULL;
+                       call->num_additional_args = 0;
                        call->is_ctor_call = 0;
                        EX(call) = call;
+
                        CHECK_EXCEPTION();
                        ZEND_VM_NEXT_OPCODE();
                } else if (IS_VAR != IS_CONST && IS_VAR != IS_TMP_VAR &&
@@ -1844,8 +2023,11 @@ static int ZEND_FASTCALL  ZEND_INIT_FCALL_BY_NAME_SPEC_VAR_HANDLER(ZEND_OPCODE_H
                        } else {
                                zval_ptr_dtor_nogc(&free_op2.var);
                        }
+
+                       call->num_additional_args = 0;
                        call->is_ctor_call = 0;
                        EX(call) = call;
+
                        CHECK_EXCEPTION();
                        ZEND_VM_NEXT_OPCODE();
                } else if (IS_VAR != IS_CONST &&
@@ -1911,8 +2093,11 @@ static int ZEND_FASTCALL  ZEND_INIT_FCALL_BY_NAME_SPEC_VAR_HANDLER(ZEND_OPCODE_H
                        if (UNEXPECTED(call->fbc == NULL)) {
                                zend_error_noreturn(E_ERROR, "Call to undefined method %s::%s()", ce->name, Z_STRVAL_PP(method));
                        }
+
+                       call->num_additional_args = 0;
                        call->is_ctor_call = 0;
                        EX(call) = call;
+
                        zval_ptr_dtor_nogc(&free_op2.var);
                        CHECK_EXCEPTION();
                        ZEND_VM_NEXT_OPCODE();
@@ -2021,10 +2206,13 @@ static int ZEND_FASTCALL  ZEND_INIT_FCALL_BY_NAME_SPEC_CV_HANDLER(ZEND_OPCODE_HA
                } else {
                        CACHE_PTR(opline->op2.literal->cache_slot, call->fbc);
                }
+
                call->object = NULL;
                call->called_scope = NULL;
+               call->num_additional_args = 0;
                call->is_ctor_call = 0;
                EX(call) = call;
+
                /*CHECK_EXCEPTION();*/
                ZEND_VM_NEXT_OPCODE();
        } else {
@@ -2051,8 +2239,10 @@ static int ZEND_FASTCALL  ZEND_INIT_FCALL_BY_NAME_SPEC_CV_HANDLER(ZEND_OPCODE_HA
 
                        call->object = NULL;
                        call->called_scope = NULL;
+                       call->num_additional_args = 0;
                        call->is_ctor_call = 0;
                        EX(call) = call;
+
                        CHECK_EXCEPTION();
                        ZEND_VM_NEXT_OPCODE();
                } else if (IS_CV != IS_CONST && IS_CV != IS_TMP_VAR &&
@@ -2069,8 +2259,11 @@ static int ZEND_FASTCALL  ZEND_INIT_FCALL_BY_NAME_SPEC_CV_HANDLER(ZEND_OPCODE_HA
                        } else {
 
                        }
+
+                       call->num_additional_args = 0;
                        call->is_ctor_call = 0;
                        EX(call) = call;
+
                        CHECK_EXCEPTION();
                        ZEND_VM_NEXT_OPCODE();
                } else if (IS_CV != IS_CONST &&
@@ -2136,6 +2329,8 @@ static int ZEND_FASTCALL  ZEND_INIT_FCALL_BY_NAME_SPEC_CV_HANDLER(ZEND_OPCODE_HA
                        if (UNEXPECTED(call->fbc == NULL)) {
                                zend_error_noreturn(E_ERROR, "Call to undefined method %s::%s()", ce->name, Z_STRVAL_PP(method));
                        }
+
+                       call->num_additional_args = 0;
                        call->is_ctor_call = 0;
                        EX(call) = call;
 
@@ -2375,9 +2570,11 @@ static int ZEND_FASTCALL  ZEND_DO_FCALL_SPEC_CONST_HANDLER(ZEND_OPCODE_HANDLER_A
        } else {
                CACHE_PTR(opline->op1.literal->cache_slot, EX(function_state).function);
        }
+
        call->fbc = EX(function_state).function;
        call->object = NULL;
        call->called_scope = NULL;
+       call->num_additional_args = 0;
        call->is_ctor_call = 0;
        EX(call) = call;
 
@@ -2534,10 +2731,13 @@ static int ZEND_FASTCALL  ZEND_SEND_VAL_SPEC_CONST_HANDLER(ZEND_OPCODE_HANDLER_A
        USE_OPLINE
 
        SAVE_OPLINE();
-       if (opline->extended_value==ZEND_DO_FCALL_BY_NAME
-               && ARG_MUST_BE_SENT_BY_REF(EX(call)->fbc, opline->op2.opline_num)) {
-                       zend_error_noreturn(E_ERROR, "Cannot pass parameter %d by reference", opline->op2.opline_num);
+       if (opline->extended_value == ZEND_DO_FCALL_BY_NAME) {
+               int arg_num = opline->op2.num + EX(call)->num_additional_args;
+               if (ARG_MUST_BE_SENT_BY_REF(EX(call)->fbc, arg_num)) {
+                       zend_error_noreturn(E_ERROR, "Cannot pass parameter %d by reference", arg_num);
+               }
        }
+
        {
                zval *valptr;
                zval *value;
@@ -3542,7 +3742,7 @@ static int ZEND_FASTCALL  ZEND_FETCH_FUNC_ARG_SPEC_CONST_CONST_HANDLER(ZEND_OPCO
 {
        USE_OPLINE
 
-       return zend_fetch_var_address_helper_SPEC_CONST_CONST(ARG_SHOULD_BE_SENT_BY_REF(EX(call)->fbc, (opline->extended_value & ZEND_FETCH_ARG_MASK))?BP_VAR_W:BP_VAR_R, ZEND_OPCODE_HANDLER_ARGS_PASSTHRU);
+       return zend_fetch_var_address_helper_SPEC_CONST_CONST(zend_is_by_ref_func_arg_fetch(opline, EX(call) TSRMLS_CC) ? BP_VAR_W : BP_VAR_R, ZEND_OPCODE_HANDLER_ARGS_PASSTHRU);
 }
 
 static int ZEND_FASTCALL  ZEND_FETCH_UNSET_SPEC_CONST_CONST_HANDLER(ZEND_OPCODE_HANDLER_ARGS)
@@ -3712,6 +3912,8 @@ static int ZEND_FASTCALL  ZEND_INIT_STATIC_METHOD_CALL_SPEC_CONST_CONST_HANDLER(
                        call->called_scope = Z_OBJCE_P(call->object);
                }
        }
+
+       call->num_additional_args = 0;
        call->is_ctor_call = 0;
        EX(call) = call;
 
@@ -4695,6 +4897,8 @@ static int ZEND_FASTCALL  ZEND_INIT_STATIC_METHOD_CALL_SPEC_CONST_TMP_HANDLER(ZE
                        call->called_scope = Z_OBJCE_P(call->object);
                }
        }
+
+       call->num_additional_args = 0;
        call->is_ctor_call = 0;
        EX(call) = call;
 
@@ -5396,7 +5600,7 @@ static int ZEND_FASTCALL  ZEND_FETCH_FUNC_ARG_SPEC_CONST_VAR_HANDLER(ZEND_OPCODE
 {
        USE_OPLINE
 
-       return zend_fetch_var_address_helper_SPEC_CONST_VAR(ARG_SHOULD_BE_SENT_BY_REF(EX(call)->fbc, (opline->extended_value & ZEND_FETCH_ARG_MASK))?BP_VAR_W:BP_VAR_R, ZEND_OPCODE_HANDLER_ARGS_PASSTHRU);
+       return zend_fetch_var_address_helper_SPEC_CONST_VAR(zend_is_by_ref_func_arg_fetch(opline, EX(call) TSRMLS_CC) ? BP_VAR_W : BP_VAR_R, ZEND_OPCODE_HANDLER_ARGS_PASSTHRU);
 }
 
 static int ZEND_FASTCALL  ZEND_FETCH_UNSET_SPEC_CONST_VAR_HANDLER(ZEND_OPCODE_HANDLER_ARGS)
@@ -5542,6 +5746,8 @@ static int ZEND_FASTCALL  ZEND_INIT_STATIC_METHOD_CALL_SPEC_CONST_VAR_HANDLER(ZE
                        call->called_scope = Z_OBJCE_P(call->object);
                }
        }
+
+       call->num_additional_args = 0;
        call->is_ctor_call = 0;
        EX(call) = call;
 
@@ -6131,7 +6337,7 @@ static int ZEND_FASTCALL  ZEND_FETCH_FUNC_ARG_SPEC_CONST_UNUSED_HANDLER(ZEND_OPC
 {
        USE_OPLINE
 
-       return zend_fetch_var_address_helper_SPEC_CONST_UNUSED(ARG_SHOULD_BE_SENT_BY_REF(EX(call)->fbc, (opline->extended_value & ZEND_FETCH_ARG_MASK))?BP_VAR_W:BP_VAR_R, ZEND_OPCODE_HANDLER_ARGS_PASSTHRU);
+       return zend_fetch_var_address_helper_SPEC_CONST_UNUSED(zend_is_by_ref_func_arg_fetch(opline, EX(call) TSRMLS_CC) ? BP_VAR_W : BP_VAR_R, ZEND_OPCODE_HANDLER_ARGS_PASSTHRU);
 }
 
 static int ZEND_FASTCALL  ZEND_FETCH_UNSET_SPEC_CONST_UNUSED_HANDLER(ZEND_OPCODE_HANDLER_ARGS)
@@ -6260,6 +6466,8 @@ static int ZEND_FASTCALL  ZEND_INIT_STATIC_METHOD_CALL_SPEC_CONST_UNUSED_HANDLER
                        call->called_scope = Z_OBJCE_P(call->object);
                }
        }
+
+       call->num_additional_args = 0;
        call->is_ctor_call = 0;
        EX(call) = call;
 
@@ -7111,6 +7319,8 @@ static int ZEND_FASTCALL  ZEND_INIT_STATIC_METHOD_CALL_SPEC_CONST_CV_HANDLER(ZEN
                        call->called_scope = Z_OBJCE_P(call->object);
                }
        }
+
+       call->num_additional_args = 0;
        call->is_ctor_call = 0;
        EX(call) = call;
 
@@ -7819,10 +8029,13 @@ static int ZEND_FASTCALL  ZEND_SEND_VAL_SPEC_TMP_HANDLER(ZEND_OPCODE_HANDLER_ARG
        USE_OPLINE
 
        SAVE_OPLINE();
-       if (opline->extended_value==ZEND_DO_FCALL_BY_NAME
-               && ARG_MUST_BE_SENT_BY_REF(EX(call)->fbc, opline->op2.opline_num)) {
-                       zend_error_noreturn(E_ERROR, "Cannot pass parameter %d by reference", opline->op2.opline_num);
+       if (opline->extended_value == ZEND_DO_FCALL_BY_NAME) {
+               int arg_num = opline->op2.num + EX(call)->num_additional_args;
+               if (ARG_MUST_BE_SENT_BY_REF(EX(call)->fbc, arg_num)) {
+                       zend_error_noreturn(E_ERROR, "Cannot pass parameter %d by reference", arg_num);
+               }
        }
+
        {
                zval *valptr;
                zval *value;
@@ -8880,7 +9093,7 @@ static int ZEND_FASTCALL  ZEND_FETCH_FUNC_ARG_SPEC_TMP_CONST_HANDLER(ZEND_OPCODE
 {
        USE_OPLINE
 
-       return zend_fetch_var_address_helper_SPEC_TMP_CONST(ARG_SHOULD_BE_SENT_BY_REF(EX(call)->fbc, (opline->extended_value & ZEND_FETCH_ARG_MASK))?BP_VAR_W:BP_VAR_R, ZEND_OPCODE_HANDLER_ARGS_PASSTHRU);
+       return zend_fetch_var_address_helper_SPEC_TMP_CONST(zend_is_by_ref_func_arg_fetch(opline, EX(call) TSRMLS_CC) ? BP_VAR_W : BP_VAR_R, ZEND_OPCODE_HANDLER_ARGS_PASSTHRU);
 }
 
 static int ZEND_FASTCALL  ZEND_FETCH_UNSET_SPEC_TMP_CONST_HANDLER(ZEND_OPCODE_HANDLER_ARGS)
@@ -9051,6 +9264,8 @@ static int ZEND_FASTCALL  ZEND_INIT_METHOD_CALL_SPEC_TMP_CONST_HANDLER(ZEND_OPCO
                        call->object = this_ptr;
                }
        }
+
+       call->num_additional_args = 0;
        call->is_ctor_call = 0;
        EX(call) = call;
 
@@ -9899,6 +10114,8 @@ static int ZEND_FASTCALL  ZEND_INIT_METHOD_CALL_SPEC_TMP_TMP_HANDLER(ZEND_OPCODE
                        call->object = this_ptr;
                }
        }
+
+       call->num_additional_args = 0;
        call->is_ctor_call = 0;
        EX(call) = call;
 
@@ -10602,7 +10819,7 @@ static int ZEND_FASTCALL  ZEND_FETCH_FUNC_ARG_SPEC_TMP_VAR_HANDLER(ZEND_OPCODE_H
 {
        USE_OPLINE
 
-       return zend_fetch_var_address_helper_SPEC_TMP_VAR(ARG_SHOULD_BE_SENT_BY_REF(EX(call)->fbc, (opline->extended_value & ZEND_FETCH_ARG_MASK))?BP_VAR_W:BP_VAR_R, ZEND_OPCODE_HANDLER_ARGS_PASSTHRU);
+       return zend_fetch_var_address_helper_SPEC_TMP_VAR(zend_is_by_ref_func_arg_fetch(opline, EX(call) TSRMLS_CC) ? BP_VAR_W : BP_VAR_R, ZEND_OPCODE_HANDLER_ARGS_PASSTHRU);
 }
 
 static int ZEND_FASTCALL  ZEND_FETCH_UNSET_SPEC_TMP_VAR_HANDLER(ZEND_OPCODE_HANDLER_ARGS)
@@ -10748,6 +10965,8 @@ static int ZEND_FASTCALL  ZEND_INIT_METHOD_CALL_SPEC_TMP_VAR_HANDLER(ZEND_OPCODE
                        call->object = this_ptr;
                }
        }
+
+       call->num_additional_args = 0;
        call->is_ctor_call = 0;
        EX(call) = call;
 
@@ -11339,7 +11558,7 @@ static int ZEND_FASTCALL  ZEND_FETCH_FUNC_ARG_SPEC_TMP_UNUSED_HANDLER(ZEND_OPCOD
 {
        USE_OPLINE
 
-       return zend_fetch_var_address_helper_SPEC_TMP_UNUSED(ARG_SHOULD_BE_SENT_BY_REF(EX(call)->fbc, (opline->extended_value & ZEND_FETCH_ARG_MASK))?BP_VAR_W:BP_VAR_R, ZEND_OPCODE_HANDLER_ARGS_PASSTHRU);
+       return zend_fetch_var_address_helper_SPEC_TMP_UNUSED(zend_is_by_ref_func_arg_fetch(opline, EX(call) TSRMLS_CC) ? BP_VAR_W : BP_VAR_R, ZEND_OPCODE_HANDLER_ARGS_PASSTHRU);
 }
 
 static int ZEND_FASTCALL  ZEND_FETCH_UNSET_SPEC_TMP_UNUSED_HANDLER(ZEND_OPCODE_HANDLER_ARGS)
@@ -12177,6 +12396,8 @@ static int ZEND_FASTCALL  ZEND_INIT_METHOD_CALL_SPEC_TMP_CV_HANDLER(ZEND_OPCODE_
                        call->object = this_ptr;
                }
        }
+
+       call->num_additional_args = 0;
        call->is_ctor_call = 0;
        EX(call) = call;
 
@@ -13040,14 +13261,18 @@ static int ZEND_FASTCALL  ZEND_SEND_VAR_NO_REF_SPEC_VAR_HANDLER(ZEND_OPCODE_HAND
        USE_OPLINE
        zend_free_op free_op1;
        zval *varptr;
+       int arg_num;
 
        SAVE_OPLINE();
        if (opline->extended_value & ZEND_ARG_COMPILE_TIME_BOUND) { /* Had function_ptr at compile_time */
                if (!(opline->extended_value & ZEND_ARG_SEND_BY_REF)) {
                        return zend_send_by_var_helper_SPEC_VAR(ZEND_OPCODE_HANDLER_ARGS_PASSTHRU);
                }
-       } else if (!ARG_SHOULD_BE_SENT_BY_REF(EX(call)->fbc, opline->op2.opline_num)) {
-               return zend_send_by_var_helper_SPEC_VAR(ZEND_OPCODE_HANDLER_ARGS_PASSTHRU);
+       } else {
+               arg_num = opline->op2.num + EX(call)->num_additional_args;
+               if (!ARG_SHOULD_BE_SENT_BY_REF(EX(call)->fbc, arg_num)) {
+                       return zend_send_by_var_helper_SPEC_VAR(ZEND_OPCODE_HANDLER_ARGS_PASSTHRU);
+               }
        }
 
        varptr = _get_zval_ptr_var(opline->op1.var, execute_data, &free_op1 TSRMLS_CC);
@@ -13065,7 +13290,7 @@ static int ZEND_FASTCALL  ZEND_SEND_VAR_NO_REF_SPEC_VAR_HANDLER(ZEND_OPCODE_HAND
 
                if ((opline->extended_value & ZEND_ARG_COMPILE_TIME_BOUND) ?
                        !(opline->extended_value & ZEND_ARG_SEND_SILENT) :
-                       !ARG_MAY_BE_SENT_BY_REF(EX(call)->fbc, opline->op2.opline_num)) {
+                       !ARG_MAY_BE_SENT_BY_REF(EX(call)->fbc, arg_num)) {
                        zend_error(E_STRICT, "Only variables should be passed by reference");
                }
                ALLOC_ZVAL(valptr);
@@ -13102,9 +13327,11 @@ static int ZEND_FASTCALL  ZEND_SEND_REF_SPEC_VAR_HANDLER(ZEND_OPCODE_HANDLER_ARG
        }
 
        if (opline->extended_value == ZEND_DO_FCALL_BY_NAME &&
-           EX(function_state).function->type == ZEND_INTERNAL_FUNCTION &&
-           !ARG_SHOULD_BE_SENT_BY_REF(EX(call)->fbc, opline->op2.opline_num)) {
-               return zend_send_by_var_helper_SPEC_VAR(ZEND_OPCODE_HANDLER_ARGS_PASSTHRU);
+           EX(function_state).function->type == ZEND_INTERNAL_FUNCTION) {
+               int arg_num = opline->op2.num + EX(call)->num_additional_args;
+               if (!ARG_SHOULD_BE_SENT_BY_REF(EX(call)->fbc, arg_num)) {
+                       return zend_send_by_var_helper_SPEC_VAR(ZEND_OPCODE_HANDLER_ARGS_PASSTHRU);
+               }
        }
 
        SEPARATE_ZVAL_TO_MAKE_IS_REF(varptr_ptr);
@@ -13121,9 +13348,11 @@ static int ZEND_FASTCALL  ZEND_SEND_VAR_SPEC_VAR_HANDLER(ZEND_OPCODE_HANDLER_ARG
 {
        USE_OPLINE
 
-       if ((opline->extended_value == ZEND_DO_FCALL_BY_NAME)
-               && ARG_SHOULD_BE_SENT_BY_REF(EX(call)->fbc, opline->op2.opline_num)) {
-               return ZEND_SEND_REF_SPEC_VAR_HANDLER(ZEND_OPCODE_HANDLER_ARGS_PASSTHRU);
+       if (opline->extended_value == ZEND_DO_FCALL_BY_NAME) {
+               int arg_num = opline->op2.num + EX(call)->num_additional_args;
+               if (ARG_SHOULD_BE_SENT_BY_REF(EX(call)->fbc, arg_num)) {
+                       return ZEND_SEND_REF_SPEC_VAR_HANDLER(ZEND_OPCODE_HANDLER_ARGS_PASSTHRU);
+               }
        }
        SAVE_OPLINE();
        return zend_send_by_var_helper_SPEC_VAR(ZEND_OPCODE_HANDLER_ARGS_PASSTHRU);
@@ -14738,7 +14967,7 @@ static int ZEND_FASTCALL  ZEND_FETCH_FUNC_ARG_SPEC_VAR_CONST_HANDLER(ZEND_OPCODE
 {
        USE_OPLINE
 
-       return zend_fetch_var_address_helper_SPEC_VAR_CONST(ARG_SHOULD_BE_SENT_BY_REF(EX(call)->fbc, (opline->extended_value & ZEND_FETCH_ARG_MASK))?BP_VAR_W:BP_VAR_R, ZEND_OPCODE_HANDLER_ARGS_PASSTHRU);
+       return zend_fetch_var_address_helper_SPEC_VAR_CONST(zend_is_by_ref_func_arg_fetch(opline, EX(call) TSRMLS_CC) ? BP_VAR_W : BP_VAR_R, ZEND_OPCODE_HANDLER_ARGS_PASSTHRU);
 }
 
 static int ZEND_FASTCALL  ZEND_FETCH_UNSET_SPEC_VAR_CONST_HANDLER(ZEND_OPCODE_HANDLER_ARGS)
@@ -14846,9 +15075,8 @@ static int ZEND_FASTCALL  ZEND_FETCH_DIM_FUNC_ARG_SPEC_VAR_CONST_HANDLER(ZEND_OP
 
        SAVE_OPLINE();
 
-       if (ARG_SHOULD_BE_SENT_BY_REF(EX(call)->fbc, (opline->extended_value & ZEND_FETCH_ARG_MASK))) {
+       if (zend_is_by_ref_func_arg_fetch(opline, EX(call) TSRMLS_CC)) {
                zval **container = _get_zval_ptr_ptr_var(opline->op1.var, execute_data, &free_op1 TSRMLS_CC);
-
                if (IS_VAR == IS_VAR && UNEXPECTED(container == NULL)) {
                        zend_error_noreturn(E_ERROR, "Cannot use string offset as an array");
                }
@@ -15083,7 +15311,7 @@ static int ZEND_FASTCALL  ZEND_FETCH_OBJ_FUNC_ARG_SPEC_VAR_CONST_HANDLER(ZEND_OP
 {
        USE_OPLINE
 
-       if (ARG_SHOULD_BE_SENT_BY_REF(EX(call)->fbc, (opline->extended_value & ZEND_FETCH_ARG_MASK))) {
+       if (zend_is_by_ref_func_arg_fetch(opline, EX(call) TSRMLS_CC)) {
                /* Behave like FETCH_OBJ_W */
                zend_free_op free_op1;
                zval *property;
@@ -15395,6 +15623,8 @@ static int ZEND_FASTCALL  ZEND_INIT_METHOD_CALL_SPEC_VAR_CONST_HANDLER(ZEND_OPCO
                        call->object = this_ptr;
                }
        }
+
+       call->num_additional_args = 0;
        call->is_ctor_call = 0;
        EX(call) = call;
 
@@ -15520,6 +15750,8 @@ static int ZEND_FASTCALL  ZEND_INIT_STATIC_METHOD_CALL_SPEC_VAR_CONST_HANDLER(ZE
                        call->called_scope = Z_OBJCE_P(call->object);
                }
        }
+
+       call->num_additional_args = 0;
        call->is_ctor_call = 0;
        EX(call) = call;
 
@@ -17169,9 +17401,8 @@ static int ZEND_FASTCALL  ZEND_FETCH_DIM_FUNC_ARG_SPEC_VAR_TMP_HANDLER(ZEND_OPCO
 
        SAVE_OPLINE();
 
-       if (ARG_SHOULD_BE_SENT_BY_REF(EX(call)->fbc, (opline->extended_value & ZEND_FETCH_ARG_MASK))) {
+       if (zend_is_by_ref_func_arg_fetch(opline, EX(call) TSRMLS_CC)) {
                zval **container = _get_zval_ptr_ptr_var(opline->op1.var, execute_data, &free_op1 TSRMLS_CC);
-
                if (IS_VAR == IS_VAR && UNEXPECTED(container == NULL)) {
                        zend_error_noreturn(E_ERROR, "Cannot use string offset as an array");
                }
@@ -17406,7 +17637,7 @@ static int ZEND_FASTCALL  ZEND_FETCH_OBJ_FUNC_ARG_SPEC_VAR_TMP_HANDLER(ZEND_OPCO
 {
        USE_OPLINE
 
-       if (ARG_SHOULD_BE_SENT_BY_REF(EX(call)->fbc, (opline->extended_value & ZEND_FETCH_ARG_MASK))) {
+       if (zend_is_by_ref_func_arg_fetch(opline, EX(call) TSRMLS_CC)) {
                /* Behave like FETCH_OBJ_W */
                zend_free_op free_op1, free_op2;
                zval *property;
@@ -17719,6 +17950,8 @@ static int ZEND_FASTCALL  ZEND_INIT_METHOD_CALL_SPEC_VAR_TMP_HANDLER(ZEND_OPCODE
                        call->object = this_ptr;
                }
        }
+
+       call->num_additional_args = 0;
        call->is_ctor_call = 0;
        EX(call) = call;
 
@@ -17845,6 +18078,8 @@ static int ZEND_FASTCALL  ZEND_INIT_STATIC_METHOD_CALL_SPEC_VAR_TMP_HANDLER(ZEND
                        call->called_scope = Z_OBJCE_P(call->object);
                }
        }
+
+       call->num_additional_args = 0;
        call->is_ctor_call = 0;
        EX(call) = call;
 
@@ -19290,7 +19525,7 @@ static int ZEND_FASTCALL  ZEND_FETCH_FUNC_ARG_SPEC_VAR_VAR_HANDLER(ZEND_OPCODE_H
 {
        USE_OPLINE
 
-       return zend_fetch_var_address_helper_SPEC_VAR_VAR(ARG_SHOULD_BE_SENT_BY_REF(EX(call)->fbc, (opline->extended_value & ZEND_FETCH_ARG_MASK))?BP_VAR_W:BP_VAR_R, ZEND_OPCODE_HANDLER_ARGS_PASSTHRU);
+       return zend_fetch_var_address_helper_SPEC_VAR_VAR(zend_is_by_ref_func_arg_fetch(opline, EX(call) TSRMLS_CC) ? BP_VAR_W : BP_VAR_R, ZEND_OPCODE_HANDLER_ARGS_PASSTHRU);
 }
 
 static int ZEND_FASTCALL  ZEND_FETCH_UNSET_SPEC_VAR_VAR_HANDLER(ZEND_OPCODE_HANDLER_ARGS)
@@ -19398,9 +19633,8 @@ static int ZEND_FASTCALL  ZEND_FETCH_DIM_FUNC_ARG_SPEC_VAR_VAR_HANDLER(ZEND_OPCO
 
        SAVE_OPLINE();
 
-       if (ARG_SHOULD_BE_SENT_BY_REF(EX(call)->fbc, (opline->extended_value & ZEND_FETCH_ARG_MASK))) {
+       if (zend_is_by_ref_func_arg_fetch(opline, EX(call) TSRMLS_CC)) {
                zval **container = _get_zval_ptr_ptr_var(opline->op1.var, execute_data, &free_op1 TSRMLS_CC);
-
                if (IS_VAR == IS_VAR && UNEXPECTED(container == NULL)) {
                        zend_error_noreturn(E_ERROR, "Cannot use string offset as an array");
                }
@@ -19635,7 +19869,7 @@ static int ZEND_FASTCALL  ZEND_FETCH_OBJ_FUNC_ARG_SPEC_VAR_VAR_HANDLER(ZEND_OPCO
 {
        USE_OPLINE
 
-       if (ARG_SHOULD_BE_SENT_BY_REF(EX(call)->fbc, (opline->extended_value & ZEND_FETCH_ARG_MASK))) {
+       if (zend_is_by_ref_func_arg_fetch(opline, EX(call) TSRMLS_CC)) {
                /* Behave like FETCH_OBJ_W */
                zend_free_op free_op1, free_op2;
                zval *property;
@@ -20003,6 +20237,8 @@ static int ZEND_FASTCALL  ZEND_INIT_METHOD_CALL_SPEC_VAR_VAR_HANDLER(ZEND_OPCODE
                        call->object = this_ptr;
                }
        }
+
+       call->num_additional_args = 0;
        call->is_ctor_call = 0;
        EX(call) = call;
 
@@ -20129,6 +20365,8 @@ static int ZEND_FASTCALL  ZEND_INIT_STATIC_METHOD_CALL_SPEC_VAR_VAR_HANDLER(ZEND
                        call->called_scope = Z_OBJCE_P(call->object);
                }
        }
+
+       call->num_additional_args = 0;
        call->is_ctor_call = 0;
        EX(call) = call;
 
@@ -21255,7 +21493,7 @@ static int ZEND_FASTCALL  ZEND_FETCH_FUNC_ARG_SPEC_VAR_UNUSED_HANDLER(ZEND_OPCOD
 {
        USE_OPLINE
 
-       return zend_fetch_var_address_helper_SPEC_VAR_UNUSED(ARG_SHOULD_BE_SENT_BY_REF(EX(call)->fbc, (opline->extended_value & ZEND_FETCH_ARG_MASK))?BP_VAR_W:BP_VAR_R, ZEND_OPCODE_HANDLER_ARGS_PASSTHRU);
+       return zend_fetch_var_address_helper_SPEC_VAR_UNUSED(zend_is_by_ref_func_arg_fetch(opline, EX(call) TSRMLS_CC) ? BP_VAR_W : BP_VAR_R, ZEND_OPCODE_HANDLER_ARGS_PASSTHRU);
 }
 
 static int ZEND_FASTCALL  ZEND_FETCH_UNSET_SPEC_VAR_UNUSED_HANDLER(ZEND_OPCODE_HANDLER_ARGS)
@@ -21331,9 +21569,8 @@ static int ZEND_FASTCALL  ZEND_FETCH_DIM_FUNC_ARG_SPEC_VAR_UNUSED_HANDLER(ZEND_O
 
        SAVE_OPLINE();
 
-       if (ARG_SHOULD_BE_SENT_BY_REF(EX(call)->fbc, (opline->extended_value & ZEND_FETCH_ARG_MASK))) {
+       if (zend_is_by_ref_func_arg_fetch(opline, EX(call) TSRMLS_CC)) {
                zval **container = _get_zval_ptr_ptr_var(opline->op1.var, execute_data, &free_op1 TSRMLS_CC);
-
                if (IS_VAR == IS_VAR && UNEXPECTED(container == NULL)) {
                        zend_error_noreturn(E_ERROR, "Cannot use string offset as an array");
                }
@@ -21554,6 +21791,8 @@ static int ZEND_FASTCALL  ZEND_INIT_STATIC_METHOD_CALL_SPEC_VAR_UNUSED_HANDLER(Z
                        call->called_scope = Z_OBJCE_P(call->object);
                }
        }
+
+       call->num_additional_args = 0;
        call->is_ctor_call = 0;
        EX(call) = call;
 
@@ -22829,9 +23068,8 @@ static int ZEND_FASTCALL  ZEND_FETCH_DIM_FUNC_ARG_SPEC_VAR_CV_HANDLER(ZEND_OPCOD
 
        SAVE_OPLINE();
 
-       if (ARG_SHOULD_BE_SENT_BY_REF(EX(call)->fbc, (opline->extended_value & ZEND_FETCH_ARG_MASK))) {
+       if (zend_is_by_ref_func_arg_fetch(opline, EX(call) TSRMLS_CC)) {
                zval **container = _get_zval_ptr_ptr_var(opline->op1.var, execute_data, &free_op1 TSRMLS_CC);
-
                if (IS_VAR == IS_VAR && UNEXPECTED(container == NULL)) {
                        zend_error_noreturn(E_ERROR, "Cannot use string offset as an array");
                }
@@ -23066,7 +23304,7 @@ static int ZEND_FASTCALL  ZEND_FETCH_OBJ_FUNC_ARG_SPEC_VAR_CV_HANDLER(ZEND_OPCOD
 {
        USE_OPLINE
 
-       if (ARG_SHOULD_BE_SENT_BY_REF(EX(call)->fbc, (opline->extended_value & ZEND_FETCH_ARG_MASK))) {
+       if (zend_is_by_ref_func_arg_fetch(opline, EX(call) TSRMLS_CC)) {
                /* Behave like FETCH_OBJ_W */
                zend_free_op free_op1;
                zval *property;
@@ -23431,6 +23669,8 @@ static int ZEND_FASTCALL  ZEND_INIT_METHOD_CALL_SPEC_VAR_CV_HANDLER(ZEND_OPCODE_
                        call->object = this_ptr;
                }
        }
+
+       call->num_additional_args = 0;
        call->is_ctor_call = 0;
        EX(call) = call;
 
@@ -23556,6 +23796,8 @@ static int ZEND_FASTCALL  ZEND_INIT_STATIC_METHOD_CALL_SPEC_VAR_CV_HANDLER(ZEND_
                        call->called_scope = Z_OBJCE_P(call->object);
                }
        }
+
+       call->num_additional_args = 0;
        call->is_ctor_call = 0;
        EX(call) = call;
 
@@ -24827,7 +25069,7 @@ static int ZEND_FASTCALL  ZEND_FETCH_OBJ_FUNC_ARG_SPEC_UNUSED_CONST_HANDLER(ZEND
 {
        USE_OPLINE
 
-       if (ARG_SHOULD_BE_SENT_BY_REF(EX(call)->fbc, (opline->extended_value & ZEND_FETCH_ARG_MASK))) {
+       if (zend_is_by_ref_func_arg_fetch(opline, EX(call) TSRMLS_CC)) {
                /* Behave like FETCH_OBJ_W */
                zend_free_op free_op1;
                zval *property;
@@ -25049,6 +25291,8 @@ static int ZEND_FASTCALL  ZEND_INIT_METHOD_CALL_SPEC_UNUSED_CONST_HANDLER(ZEND_O
                        call->object = this_ptr;
                }
        }
+
+       call->num_additional_args = 0;
        call->is_ctor_call = 0;
        EX(call) = call;
 
@@ -26230,7 +26474,7 @@ static int ZEND_FASTCALL  ZEND_FETCH_OBJ_FUNC_ARG_SPEC_UNUSED_TMP_HANDLER(ZEND_O
 {
        USE_OPLINE
 
-       if (ARG_SHOULD_BE_SENT_BY_REF(EX(call)->fbc, (opline->extended_value & ZEND_FETCH_ARG_MASK))) {
+       if (zend_is_by_ref_func_arg_fetch(opline, EX(call) TSRMLS_CC)) {
                /* Behave like FETCH_OBJ_W */
                zend_free_op free_op1, free_op2;
                zval *property;
@@ -26451,6 +26695,8 @@ static int ZEND_FASTCALL  ZEND_INIT_METHOD_CALL_SPEC_UNUSED_TMP_HANDLER(ZEND_OPC
                        call->object = this_ptr;
                }
        }
+
+       call->num_additional_args = 0;
        call->is_ctor_call = 0;
        EX(call) = call;
 
@@ -27537,7 +27783,7 @@ static int ZEND_FASTCALL  ZEND_FETCH_OBJ_FUNC_ARG_SPEC_UNUSED_VAR_HANDLER(ZEND_O
 {
        USE_OPLINE
 
-       if (ARG_SHOULD_BE_SENT_BY_REF(EX(call)->fbc, (opline->extended_value & ZEND_FETCH_ARG_MASK))) {
+       if (zend_is_by_ref_func_arg_fetch(opline, EX(call) TSRMLS_CC)) {
                /* Behave like FETCH_OBJ_W */
                zend_free_op free_op1, free_op2;
                zval *property;
@@ -27758,6 +28004,8 @@ static int ZEND_FASTCALL  ZEND_INIT_METHOD_CALL_SPEC_UNUSED_VAR_HANDLER(ZEND_OPC
                        call->object = this_ptr;
                }
        }
+
+       call->num_additional_args = 0;
        call->is_ctor_call = 0;
        EX(call) = call;
 
@@ -29268,7 +29516,7 @@ static int ZEND_FASTCALL  ZEND_FETCH_OBJ_FUNC_ARG_SPEC_UNUSED_CV_HANDLER(ZEND_OP
 {
        USE_OPLINE
 
-       if (ARG_SHOULD_BE_SENT_BY_REF(EX(call)->fbc, (opline->extended_value & ZEND_FETCH_ARG_MASK))) {
+       if (zend_is_by_ref_func_arg_fetch(opline, EX(call) TSRMLS_CC)) {
                /* Behave like FETCH_OBJ_W */
                zend_free_op free_op1;
                zval *property;
@@ -29488,6 +29736,8 @@ static int ZEND_FASTCALL  ZEND_INIT_METHOD_CALL_SPEC_UNUSED_CV_HANDLER(ZEND_OPCO
                        call->object = this_ptr;
                }
        }
+
+       call->num_additional_args = 0;
        call->is_ctor_call = 0;
        EX(call) = call;
 
@@ -30516,14 +30766,18 @@ static int ZEND_FASTCALL  ZEND_SEND_VAR_NO_REF_SPEC_CV_HANDLER(ZEND_OPCODE_HANDL
        USE_OPLINE
 
        zval *varptr;
+       int arg_num;
 
        SAVE_OPLINE();
        if (opline->extended_value & ZEND_ARG_COMPILE_TIME_BOUND) { /* Had function_ptr at compile_time */
                if (!(opline->extended_value & ZEND_ARG_SEND_BY_REF)) {
                        return zend_send_by_var_helper_SPEC_CV(ZEND_OPCODE_HANDLER_ARGS_PASSTHRU);
                }
-       } else if (!ARG_SHOULD_BE_SENT_BY_REF(EX(call)->fbc, opline->op2.opline_num)) {
-               return zend_send_by_var_helper_SPEC_CV(ZEND_OPCODE_HANDLER_ARGS_PASSTHRU);
+       } else {
+               arg_num = opline->op2.num + EX(call)->num_additional_args;
+               if (!ARG_SHOULD_BE_SENT_BY_REF(EX(call)->fbc, arg_num)) {
+                       return zend_send_by_var_helper_SPEC_CV(ZEND_OPCODE_HANDLER_ARGS_PASSTHRU);
+               }
        }
 
        varptr = _get_zval_ptr_cv_BP_VAR_R(execute_data, opline->op1.var TSRMLS_CC);
@@ -30541,7 +30795,7 @@ static int ZEND_FASTCALL  ZEND_SEND_VAR_NO_REF_SPEC_CV_HANDLER(ZEND_OPCODE_HANDL
 
                if ((opline->extended_value & ZEND_ARG_COMPILE_TIME_BOUND) ?
                        !(opline->extended_value & ZEND_ARG_SEND_SILENT) :
-                       !ARG_MAY_BE_SENT_BY_REF(EX(call)->fbc, opline->op2.opline_num)) {
+                       !ARG_MAY_BE_SENT_BY_REF(EX(call)->fbc, arg_num)) {
                        zend_error(E_STRICT, "Only variables should be passed by reference");
                }
                ALLOC_ZVAL(valptr);
@@ -30578,9 +30832,11 @@ static int ZEND_FASTCALL  ZEND_SEND_REF_SPEC_CV_HANDLER(ZEND_OPCODE_HANDLER_ARGS
        }
 
        if (opline->extended_value == ZEND_DO_FCALL_BY_NAME &&
-           EX(function_state).function->type == ZEND_INTERNAL_FUNCTION &&
-           !ARG_SHOULD_BE_SENT_BY_REF(EX(call)->fbc, opline->op2.opline_num)) {
-               return zend_send_by_var_helper_SPEC_CV(ZEND_OPCODE_HANDLER_ARGS_PASSTHRU);
+           EX(function_state).function->type == ZEND_INTERNAL_FUNCTION) {
+               int arg_num = opline->op2.num + EX(call)->num_additional_args;
+               if (!ARG_SHOULD_BE_SENT_BY_REF(EX(call)->fbc, arg_num)) {
+                       return zend_send_by_var_helper_SPEC_CV(ZEND_OPCODE_HANDLER_ARGS_PASSTHRU);
+               }
        }
 
        SEPARATE_ZVAL_TO_MAKE_IS_REF(varptr_ptr);
@@ -30596,9 +30852,11 @@ static int ZEND_FASTCALL  ZEND_SEND_VAR_SPEC_CV_HANDLER(ZEND_OPCODE_HANDLER_ARGS
 {
        USE_OPLINE
 
-       if ((opline->extended_value == ZEND_DO_FCALL_BY_NAME)
-               && ARG_SHOULD_BE_SENT_BY_REF(EX(call)->fbc, opline->op2.opline_num)) {
-               return ZEND_SEND_REF_SPEC_CV_HANDLER(ZEND_OPCODE_HANDLER_ARGS_PASSTHRU);
+       if (opline->extended_value == ZEND_DO_FCALL_BY_NAME) {
+               int arg_num = opline->op2.num + EX(call)->num_additional_args;
+               if (ARG_SHOULD_BE_SENT_BY_REF(EX(call)->fbc, arg_num)) {
+                       return ZEND_SEND_REF_SPEC_CV_HANDLER(ZEND_OPCODE_HANDLER_ARGS_PASSTHRU);
+               }
        }
        SAVE_OPLINE();
        return zend_send_by_var_helper_SPEC_CV(ZEND_OPCODE_HANDLER_ARGS_PASSTHRU);
@@ -32067,7 +32325,7 @@ static int ZEND_FASTCALL  ZEND_FETCH_FUNC_ARG_SPEC_CV_CONST_HANDLER(ZEND_OPCODE_
 {
        USE_OPLINE
 
-       return zend_fetch_var_address_helper_SPEC_CV_CONST(ARG_SHOULD_BE_SENT_BY_REF(EX(call)->fbc, (opline->extended_value & ZEND_FETCH_ARG_MASK))?BP_VAR_W:BP_VAR_R, ZEND_OPCODE_HANDLER_ARGS_PASSTHRU);
+       return zend_fetch_var_address_helper_SPEC_CV_CONST(zend_is_by_ref_func_arg_fetch(opline, EX(call) TSRMLS_CC) ? BP_VAR_W : BP_VAR_R, ZEND_OPCODE_HANDLER_ARGS_PASSTHRU);
 }
 
 static int ZEND_FASTCALL  ZEND_FETCH_UNSET_SPEC_CV_CONST_HANDLER(ZEND_OPCODE_HANDLER_ARGS)
@@ -32174,9 +32432,8 @@ static int ZEND_FASTCALL  ZEND_FETCH_DIM_FUNC_ARG_SPEC_CV_CONST_HANDLER(ZEND_OPC
 
        SAVE_OPLINE();
 
-       if (ARG_SHOULD_BE_SENT_BY_REF(EX(call)->fbc, (opline->extended_value & ZEND_FETCH_ARG_MASK))) {
+       if (zend_is_by_ref_func_arg_fetch(opline, EX(call) TSRMLS_CC)) {
                zval **container = _get_zval_ptr_ptr_cv_BP_VAR_W(execute_data, opline->op1.var TSRMLS_CC);
-
                if (IS_CV == IS_VAR && UNEXPECTED(container == NULL)) {
                        zend_error_noreturn(E_ERROR, "Cannot use string offset as an array");
                }
@@ -32408,7 +32665,7 @@ static int ZEND_FASTCALL  ZEND_FETCH_OBJ_FUNC_ARG_SPEC_CV_CONST_HANDLER(ZEND_OPC
 {
        USE_OPLINE
 
-       if (ARG_SHOULD_BE_SENT_BY_REF(EX(call)->fbc, (opline->extended_value & ZEND_FETCH_ARG_MASK))) {
+       if (zend_is_by_ref_func_arg_fetch(opline, EX(call) TSRMLS_CC)) {
                /* Behave like FETCH_OBJ_W */
                zend_free_op free_op1;
                zval *property;
@@ -32717,6 +32974,8 @@ static int ZEND_FASTCALL  ZEND_INIT_METHOD_CALL_SPEC_CV_CONST_HANDLER(ZEND_OPCOD
                        call->object = this_ptr;
                }
        }
+
+       call->num_additional_args = 0;
        call->is_ctor_call = 0;
        EX(call) = call;
 
@@ -34264,9 +34523,8 @@ static int ZEND_FASTCALL  ZEND_FETCH_DIM_FUNC_ARG_SPEC_CV_TMP_HANDLER(ZEND_OPCOD
 
        SAVE_OPLINE();
 
-       if (ARG_SHOULD_BE_SENT_BY_REF(EX(call)->fbc, (opline->extended_value & ZEND_FETCH_ARG_MASK))) {
+       if (zend_is_by_ref_func_arg_fetch(opline, EX(call) TSRMLS_CC)) {
                zval **container = _get_zval_ptr_ptr_cv_BP_VAR_W(execute_data, opline->op1.var TSRMLS_CC);
-
                if (IS_CV == IS_VAR && UNEXPECTED(container == NULL)) {
                        zend_error_noreturn(E_ERROR, "Cannot use string offset as an array");
                }
@@ -34498,7 +34756,7 @@ static int ZEND_FASTCALL  ZEND_FETCH_OBJ_FUNC_ARG_SPEC_CV_TMP_HANDLER(ZEND_OPCOD
 {
        USE_OPLINE
 
-       if (ARG_SHOULD_BE_SENT_BY_REF(EX(call)->fbc, (opline->extended_value & ZEND_FETCH_ARG_MASK))) {
+       if (zend_is_by_ref_func_arg_fetch(opline, EX(call) TSRMLS_CC)) {
                /* Behave like FETCH_OBJ_W */
                zend_free_op free_op1, free_op2;
                zval *property;
@@ -34808,6 +35066,8 @@ static int ZEND_FASTCALL  ZEND_INIT_METHOD_CALL_SPEC_CV_TMP_HANDLER(ZEND_OPCODE_
                        call->object = this_ptr;
                }
        }
+
+       call->num_additional_args = 0;
        call->is_ctor_call = 0;
        EX(call) = call;
 
@@ -36249,7 +36509,7 @@ static int ZEND_FASTCALL  ZEND_FETCH_FUNC_ARG_SPEC_CV_VAR_HANDLER(ZEND_OPCODE_HA
 {
        USE_OPLINE
 
-       return zend_fetch_var_address_helper_SPEC_CV_VAR(ARG_SHOULD_BE_SENT_BY_REF(EX(call)->fbc, (opline->extended_value & ZEND_FETCH_ARG_MASK))?BP_VAR_W:BP_VAR_R, ZEND_OPCODE_HANDLER_ARGS_PASSTHRU);
+       return zend_fetch_var_address_helper_SPEC_CV_VAR(zend_is_by_ref_func_arg_fetch(opline, EX(call) TSRMLS_CC) ? BP_VAR_W : BP_VAR_R, ZEND_OPCODE_HANDLER_ARGS_PASSTHRU);
 }
 
 static int ZEND_FASTCALL  ZEND_FETCH_UNSET_SPEC_CV_VAR_HANDLER(ZEND_OPCODE_HANDLER_ARGS)
@@ -36356,9 +36616,8 @@ static int ZEND_FASTCALL  ZEND_FETCH_DIM_FUNC_ARG_SPEC_CV_VAR_HANDLER(ZEND_OPCOD
 
        SAVE_OPLINE();
 
-       if (ARG_SHOULD_BE_SENT_BY_REF(EX(call)->fbc, (opline->extended_value & ZEND_FETCH_ARG_MASK))) {
+       if (zend_is_by_ref_func_arg_fetch(opline, EX(call) TSRMLS_CC)) {
                zval **container = _get_zval_ptr_ptr_cv_BP_VAR_W(execute_data, opline->op1.var TSRMLS_CC);
-
                if (IS_CV == IS_VAR && UNEXPECTED(container == NULL)) {
                        zend_error_noreturn(E_ERROR, "Cannot use string offset as an array");
                }
@@ -36590,7 +36849,7 @@ static int ZEND_FASTCALL  ZEND_FETCH_OBJ_FUNC_ARG_SPEC_CV_VAR_HANDLER(ZEND_OPCOD
 {
        USE_OPLINE
 
-       if (ARG_SHOULD_BE_SENT_BY_REF(EX(call)->fbc, (opline->extended_value & ZEND_FETCH_ARG_MASK))) {
+       if (zend_is_by_ref_func_arg_fetch(opline, EX(call) TSRMLS_CC)) {
                /* Behave like FETCH_OBJ_W */
                zend_free_op free_op1, free_op2;
                zval *property;
@@ -36954,6 +37213,8 @@ static int ZEND_FASTCALL  ZEND_INIT_METHOD_CALL_SPEC_CV_VAR_HANDLER(ZEND_OPCODE_
                        call->object = this_ptr;
                }
        }
+
+       call->num_additional_args = 0;
        call->is_ctor_call = 0;
        EX(call) = call;
 
@@ -38076,7 +38337,7 @@ static int ZEND_FASTCALL  ZEND_FETCH_FUNC_ARG_SPEC_CV_UNUSED_HANDLER(ZEND_OPCODE
 {
        USE_OPLINE
 
-       return zend_fetch_var_address_helper_SPEC_CV_UNUSED(ARG_SHOULD_BE_SENT_BY_REF(EX(call)->fbc, (opline->extended_value & ZEND_FETCH_ARG_MASK))?BP_VAR_W:BP_VAR_R, ZEND_OPCODE_HANDLER_ARGS_PASSTHRU);
+       return zend_fetch_var_address_helper_SPEC_CV_UNUSED(zend_is_by_ref_func_arg_fetch(opline, EX(call) TSRMLS_CC) ? BP_VAR_W : BP_VAR_R, ZEND_OPCODE_HANDLER_ARGS_PASSTHRU);
 }
 
 static int ZEND_FASTCALL  ZEND_FETCH_UNSET_SPEC_CV_UNUSED_HANDLER(ZEND_OPCODE_HANDLER_ARGS)
@@ -38151,9 +38412,8 @@ static int ZEND_FASTCALL  ZEND_FETCH_DIM_FUNC_ARG_SPEC_CV_UNUSED_HANDLER(ZEND_OP
 
        SAVE_OPLINE();
 
-       if (ARG_SHOULD_BE_SENT_BY_REF(EX(call)->fbc, (opline->extended_value & ZEND_FETCH_ARG_MASK))) {
+       if (zend_is_by_ref_func_arg_fetch(opline, EX(call) TSRMLS_CC)) {
                zval **container = _get_zval_ptr_ptr_cv_BP_VAR_W(execute_data, opline->op1.var TSRMLS_CC);
-
                if (IS_CV == IS_VAR && UNEXPECTED(container == NULL)) {
                        zend_error_noreturn(E_ERROR, "Cannot use string offset as an array");
                }
@@ -39502,9 +39762,8 @@ static int ZEND_FASTCALL  ZEND_FETCH_DIM_FUNC_ARG_SPEC_CV_CV_HANDLER(ZEND_OPCODE
 
        SAVE_OPLINE();
 
-       if (ARG_SHOULD_BE_SENT_BY_REF(EX(call)->fbc, (opline->extended_value & ZEND_FETCH_ARG_MASK))) {
+       if (zend_is_by_ref_func_arg_fetch(opline, EX(call) TSRMLS_CC)) {
                zval **container = _get_zval_ptr_ptr_cv_BP_VAR_W(execute_data, opline->op1.var TSRMLS_CC);
-
                if (IS_CV == IS_VAR && UNEXPECTED(container == NULL)) {
                        zend_error_noreturn(E_ERROR, "Cannot use string offset as an array");
                }
@@ -39736,7 +39995,7 @@ static int ZEND_FASTCALL  ZEND_FETCH_OBJ_FUNC_ARG_SPEC_CV_CV_HANDLER(ZEND_OPCODE
 {
        USE_OPLINE
 
-       if (ARG_SHOULD_BE_SENT_BY_REF(EX(call)->fbc, (opline->extended_value & ZEND_FETCH_ARG_MASK))) {
+       if (zend_is_by_ref_func_arg_fetch(opline, EX(call) TSRMLS_CC)) {
                /* Behave like FETCH_OBJ_W */
                zend_free_op free_op1;
                zval *property;
@@ -40097,6 +40356,8 @@ static int ZEND_FASTCALL  ZEND_INIT_METHOD_CALL_SPEC_CV_CV_HANDLER(ZEND_OPCODE_H
                        call->object = this_ptr;
                }
        }
+
+       call->num_additional_args = 0;
        call->is_ctor_call = 0;
        EX(call) = call;
 
@@ -44788,6 +45049,31 @@ void zend_init_opcodes_handlers(void)
        ZEND_RECV_VARIADIC_SPEC_HANDLER,
        ZEND_RECV_VARIADIC_SPEC_HANDLER,
        ZEND_RECV_VARIADIC_SPEC_HANDLER,
+       ZEND_SEND_UNPACK_SPEC_HANDLER,
+       ZEND_SEND_UNPACK_SPEC_HANDLER,
+       ZEND_SEND_UNPACK_SPEC_HANDLER,
+       ZEND_SEND_UNPACK_SPEC_HANDLER,
+       ZEND_SEND_UNPACK_SPEC_HANDLER,
+       ZEND_SEND_UNPACK_SPEC_HANDLER,
+       ZEND_SEND_UNPACK_SPEC_HANDLER,
+       ZEND_SEND_UNPACK_SPEC_HANDLER,
+       ZEND_SEND_UNPACK_SPEC_HANDLER,
+       ZEND_SEND_UNPACK_SPEC_HANDLER,
+       ZEND_SEND_UNPACK_SPEC_HANDLER,
+       ZEND_SEND_UNPACK_SPEC_HANDLER,
+       ZEND_SEND_UNPACK_SPEC_HANDLER,
+       ZEND_SEND_UNPACK_SPEC_HANDLER,
+       ZEND_SEND_UNPACK_SPEC_HANDLER,
+       ZEND_SEND_UNPACK_SPEC_HANDLER,
+       ZEND_SEND_UNPACK_SPEC_HANDLER,
+       ZEND_SEND_UNPACK_SPEC_HANDLER,
+       ZEND_SEND_UNPACK_SPEC_HANDLER,
+       ZEND_SEND_UNPACK_SPEC_HANDLER,
+       ZEND_SEND_UNPACK_SPEC_HANDLER,
+       ZEND_SEND_UNPACK_SPEC_HANDLER,
+       ZEND_SEND_UNPACK_SPEC_HANDLER,
+       ZEND_SEND_UNPACK_SPEC_HANDLER,
+       ZEND_SEND_UNPACK_SPEC_HANDLER,
        ZEND_NULL_HANDLER
   };
   zend_opcode_handlers = (opcode_handler_t*)labels;
index 3e8df979c803e224a1560851475b5c6bbf541fe2..d6f51cce5c3f8370e45c17b6ea862bf7b5d1fc1a 100644 (file)
@@ -21,7 +21,7 @@
 #include <stdio.h>
 #include <zend.h>
 
-const char *zend_vm_opcodes_map[165] = {
+const char *zend_vm_opcodes_map[166] = {
        "ZEND_NOP",
        "ZEND_ADD",
        "ZEND_SUB",
@@ -187,6 +187,7 @@ const char *zend_vm_opcodes_map[165] = {
        "ZEND_FAST_CALL",
        "ZEND_FAST_RET",
        "ZEND_RECV_VARIADIC",
+       "ZEND_SEND_UNPACK",
 };
 
 ZEND_API const char* zend_get_opcode_name(zend_uchar opcode) {
index e3e4f590d8d7fe19263232faa624e5b06e542aa4..c4cab07aeb3a9845261ee7473bb311ddd941b95a 100644 (file)
@@ -170,5 +170,6 @@ ZEND_API const char *zend_get_opcode_name(zend_uchar opcode);
 #define ZEND_FAST_CALL                       162
 #define ZEND_FAST_RET                        163
 #define ZEND_RECV_VARIADIC                   164
+#define ZEND_SEND_UNPACK                     165
 
 #endif
\ No newline at end of file