mirror of
https://github.com/php/php-src.git
synced 2024-09-21 09:57:23 +00:00
Implement named parameters
From an engine perspective, named parameters mainly add three concepts: * The SEND_* opcodes now accept a CONST op2, which is the argument name. For now, it is looked up by linear scan and runtime cached. * This may leave UNDEF arguments on the stack. To avoid having to deal with them in other places, a CHECK_UNDEF_ARGS opcode is used to either replace them with defaults, or error. * For variadic functions, EX(extra_named_params) are collected and need to be freed based on ZEND_CALL_HAS_EXTRA_NAMED_PARAMS. RFC: https://wiki.php.net/rfc/named_params Closes GH-5357.
This commit is contained in:
parent
9a71d47d73
commit
d92229d8c7
@ -640,6 +640,8 @@ PHP 8.0 UPGRADE NOTES
|
||||
RFC: https://wiki.php.net/rfc/inheritance_private_methods
|
||||
. Added support for nullsafe operator (`?->`).
|
||||
RFC: https://wiki.php.net/rfc/nullsafe_operator
|
||||
. Added support for named arguments.
|
||||
RFC: https://wiki.php.net/rfc/named_params
|
||||
|
||||
- Date:
|
||||
. Added DateTime::createFromInterface() and
|
||||
|
@ -18,4 +18,4 @@ try {
|
||||
|
||||
?>
|
||||
--EXPECT--
|
||||
Exception: Cannot unpack Traversable with non-integer keys
|
||||
Exception: Keys must be of type int|string during argument unpacking
|
||||
|
@ -7,11 +7,6 @@ set_error_handler(function($errno, $errstr) {
|
||||
var_dump($errstr);
|
||||
});
|
||||
|
||||
try {
|
||||
var_dump(...[1, 2, "foo" => 3, 4]);
|
||||
} catch (Error $ex) {
|
||||
var_dump($ex->getMessage());
|
||||
}
|
||||
try {
|
||||
var_dump(...new ArrayIterator([1, 2, "foo" => 3, 4]));
|
||||
} catch (Error $ex) {
|
||||
@ -20,5 +15,4 @@ try {
|
||||
|
||||
?>
|
||||
--EXPECT--
|
||||
string(36) "Cannot unpack array with string keys"
|
||||
string(42) "Cannot unpack Traversable with string keys"
|
||||
string(68) "Cannot use positional argument after named argument during unpacking"
|
||||
|
@ -60,6 +60,7 @@ assert(0 && ($a = function &(array &$a, ?X $b = null) use ($c,&$d) : ?X {
|
||||
$x = C::${$z . "_1"};
|
||||
$x?->y;
|
||||
$x?->y();
|
||||
foo(bar: $x);
|
||||
}
|
||||
}
|
||||
}));
|
||||
@ -202,6 +203,7 @@ Warning: assert(): assert(0 && ($a = function &(array &$a, ?X $b = null) use($c,
|
||||
$x = C::${$z . '_1'};
|
||||
$x?->y;
|
||||
$x?->y();
|
||||
foo(bar: $x);
|
||||
}
|
||||
|
||||
}
|
||||
|
@ -8,4 +8,4 @@ $foo = 'bar';
|
||||
var_dump(new namespace::$foo);
|
||||
?>
|
||||
--EXPECTF--
|
||||
Parse error: syntax error, unexpected token "namespace" in %s on line %d
|
||||
Parse error: syntax error, unexpected token "namespace", expecting ":" in %s on line %d
|
||||
|
37
Zend/tests/named_params/__call.phpt
Normal file
37
Zend/tests/named_params/__call.phpt
Normal file
@ -0,0 +1,37 @@
|
||||
--TEST--
|
||||
Check that __call() and __callStatic() work with named parameters
|
||||
--FILE--
|
||||
<?php
|
||||
|
||||
class Test {
|
||||
public function __call(string $method, array $args) {
|
||||
$this->{'_'.$method}(...$args);
|
||||
}
|
||||
|
||||
public static function __callStatic(string $method, array $args) {
|
||||
(new static)->{'_'.$method}(...$args);
|
||||
}
|
||||
|
||||
private function _method($a = 'a', $b = 'b') {
|
||||
echo "a: $a, b: $b\n";
|
||||
}
|
||||
}
|
||||
|
||||
$obj = new class { public function __toString() { return "STR"; } };
|
||||
|
||||
$test = new Test;
|
||||
$test->method(a: 'A', b: 'B');
|
||||
$test->method(b: 'B');
|
||||
$test->method(b: $obj);
|
||||
Test::method(a: 'A', b: 'B');
|
||||
Test::method(b: 'B');
|
||||
Test::method(b: $obj);
|
||||
|
||||
?>
|
||||
--EXPECT--
|
||||
a: A, b: B
|
||||
a: a, b: B
|
||||
a: a, b: STR
|
||||
a: A, b: B
|
||||
a: a, b: B
|
||||
a: a, b: STR
|
64
Zend/tests/named_params/__invoke.phpt
Normal file
64
Zend/tests/named_params/__invoke.phpt
Normal file
@ -0,0 +1,64 @@
|
||||
--TEST--
|
||||
Check that __invoke() works with named parameters
|
||||
--FILE--
|
||||
<?php
|
||||
|
||||
class Test {
|
||||
public function __invoke($a = 'a', $b = 'b') {
|
||||
echo "a: $a, b: $b\n";
|
||||
}
|
||||
}
|
||||
|
||||
class Test2 {
|
||||
public function __invoke($a = 'a', $b = 'b', ...$rest) {
|
||||
echo "a: $a, b: $b\n";
|
||||
var_dump($rest);
|
||||
}
|
||||
}
|
||||
|
||||
$test = new Test;
|
||||
$test(b: 'B', a: 'A');
|
||||
$test(b: 'B');
|
||||
try {
|
||||
$test(b: 'B', c: 'C');
|
||||
} catch (Error $e) {
|
||||
echo $e->getMessage(), "\n";
|
||||
}
|
||||
echo "\n";
|
||||
|
||||
$test2 = new Test2;
|
||||
$test2(b: 'B', a: 'A', c: 'C');
|
||||
$test2(b: 'B', c: 'C');
|
||||
echo "\n";
|
||||
|
||||
$test3 = function($a = 'a', $b = 'b') {
|
||||
echo "a: $a, b: $b\n";
|
||||
};
|
||||
$test3(b: 'B', a: 'A');
|
||||
$test3(b: 'B');
|
||||
try {
|
||||
$test3(b: 'B', c: 'C');
|
||||
} catch (Error $e) {
|
||||
echo $e->getMessage(), "\n";
|
||||
}
|
||||
|
||||
?>
|
||||
--EXPECT--
|
||||
a: A, b: B
|
||||
a: a, b: B
|
||||
Unknown named parameter $c
|
||||
|
||||
a: A, b: B
|
||||
array(1) {
|
||||
["c"]=>
|
||||
string(1) "C"
|
||||
}
|
||||
a: a, b: B
|
||||
array(1) {
|
||||
["c"]=>
|
||||
string(1) "C"
|
||||
}
|
||||
|
||||
a: A, b: B
|
||||
a: a, b: B
|
||||
Unknown named parameter $c
|
50
Zend/tests/named_params/attributes.phpt
Normal file
50
Zend/tests/named_params/attributes.phpt
Normal file
@ -0,0 +1,50 @@
|
||||
--TEST--
|
||||
Named params in attributes
|
||||
--FILE--
|
||||
<?php
|
||||
|
||||
@@Attribute
|
||||
class MyAttribute {
|
||||
public function __construct(
|
||||
public $a = 'a',
|
||||
public $b = 'b',
|
||||
public $c = 'c',
|
||||
) {}
|
||||
}
|
||||
|
||||
@@MyAttribute('A', c: 'C')
|
||||
class Test1 {}
|
||||
|
||||
@@MyAttribute('A', a: 'C')
|
||||
class Test2 {}
|
||||
|
||||
$attr = (new ReflectionClass(Test1::class))->getAttributes()[0];
|
||||
var_dump($attr->getName());
|
||||
var_dump($attr->getArguments());
|
||||
var_dump($attr->newInstance());
|
||||
|
||||
$attr = (new ReflectionClass(Test2::class))->getAttributes()[0];
|
||||
try {
|
||||
var_dump($attr->newInstance());
|
||||
} catch (Error $e) {
|
||||
echo $e->getMessage(), "\n";
|
||||
}
|
||||
|
||||
?>
|
||||
--EXPECT--
|
||||
string(11) "MyAttribute"
|
||||
array(2) {
|
||||
[0]=>
|
||||
string(1) "A"
|
||||
["c"]=>
|
||||
string(1) "C"
|
||||
}
|
||||
object(MyAttribute)#1 (3) {
|
||||
["a"]=>
|
||||
string(1) "A"
|
||||
["b"]=>
|
||||
string(1) "b"
|
||||
["c"]=>
|
||||
string(1) "C"
|
||||
}
|
||||
Named parameter $a overwrites previous argument
|
@ -0,0 +1,11 @@
|
||||
--TEST--
|
||||
Named params in attributes: Duplicate named parameter error
|
||||
--FILE--
|
||||
<?php
|
||||
|
||||
@@MyAttribute(a: 'A', a: 'A')
|
||||
class Test {}
|
||||
|
||||
?>
|
||||
--EXPECTF--
|
||||
Fatal error: Duplicate named parameter $a in %s on line %d
|
21
Zend/tests/named_params/attributes_named_flags.phpt
Normal file
21
Zend/tests/named_params/attributes_named_flags.phpt
Normal file
@ -0,0 +1,21 @@
|
||||
--TEST--
|
||||
Named flags parameter for Attribute attribute
|
||||
--FILE--
|
||||
<?php
|
||||
|
||||
@@Attribute(flags: Attribute::TARGET_CLASS)
|
||||
class MyAttribute {
|
||||
}
|
||||
|
||||
@@MyAttribute
|
||||
function test() {}
|
||||
|
||||
(new ReflectionFunction('test'))->getAttributes()[0]->newInstance();
|
||||
|
||||
?>
|
||||
--EXPECTF--
|
||||
Fatal error: Uncaught Error: Attribute "MyAttribute" cannot target function (allowed targets: class) in %s:%d
|
||||
Stack trace:
|
||||
#0 %s(%d): ReflectionAttribute->newInstance()
|
||||
#1 {main}
|
||||
thrown in %s on line %d
|
@ -0,0 +1,22 @@
|
||||
--TEST--
|
||||
Named flags parameter for Attribute attribute (incorrect parameter name)
|
||||
--FILE--
|
||||
<?php
|
||||
|
||||
// TODO: This should error at compile-time.
|
||||
@@Attribute(not_flags: Attribute::TARGET_CLASS)
|
||||
class MyAttribute {
|
||||
}
|
||||
|
||||
@@MyAttribute
|
||||
function test() {}
|
||||
|
||||
(new ReflectionFunction('test'))->getAttributes()[0]->newInstance();
|
||||
|
||||
?>
|
||||
--EXPECTF--
|
||||
Fatal error: Uncaught Error: Attribute "MyAttribute" cannot target function (allowed targets: class) in %s:%d
|
||||
Stack trace:
|
||||
#0 %s(%d): ReflectionAttribute->newInstance()
|
||||
#1 {main}
|
||||
thrown in %s on line %d
|
@ -0,0 +1,14 @@
|
||||
--TEST--
|
||||
Named params in attributes: Positional after named error
|
||||
--FILE--
|
||||
<?php
|
||||
|
||||
@@Attribute
|
||||
class MyAttribute { }
|
||||
|
||||
@@MyAttribute(a: 'A', 'B')
|
||||
class Test {}
|
||||
|
||||
?>
|
||||
--EXPECTF--
|
||||
Fatal error: Cannot use positional argument after named argument in %s on line %d
|
69
Zend/tests/named_params/backtrace.phpt
Normal file
69
Zend/tests/named_params/backtrace.phpt
Normal file
@ -0,0 +1,69 @@
|
||||
--TEST--
|
||||
Extra named params in backtraces
|
||||
--FILE--
|
||||
<?php
|
||||
|
||||
function test($a, ...$rest) {
|
||||
var_dump(debug_backtrace());
|
||||
debug_print_backtrace();
|
||||
throw new Exception("Test");
|
||||
}
|
||||
|
||||
try {
|
||||
test(1, 2, x: 3, y: 4);
|
||||
} catch (Exception $e) {
|
||||
var_dump($e->getTrace());
|
||||
echo $e, "\n";
|
||||
}
|
||||
|
||||
?>
|
||||
--EXPECTF--
|
||||
array(1) {
|
||||
[0]=>
|
||||
array(4) {
|
||||
["file"]=>
|
||||
string(%d) "%s"
|
||||
["line"]=>
|
||||
int(10)
|
||||
["function"]=>
|
||||
string(4) "test"
|
||||
["args"]=>
|
||||
array(4) {
|
||||
[0]=>
|
||||
int(1)
|
||||
[1]=>
|
||||
int(2)
|
||||
["x"]=>
|
||||
int(3)
|
||||
["y"]=>
|
||||
int(4)
|
||||
}
|
||||
}
|
||||
}
|
||||
#0 test(1, 2, x: 3, y: 4) called at [%s:10]
|
||||
array(1) {
|
||||
[0]=>
|
||||
array(4) {
|
||||
["file"]=>
|
||||
string(%d) "%s"
|
||||
["line"]=>
|
||||
int(10)
|
||||
["function"]=>
|
||||
string(4) "test"
|
||||
["args"]=>
|
||||
array(4) {
|
||||
[0]=>
|
||||
int(1)
|
||||
[1]=>
|
||||
int(2)
|
||||
["x"]=>
|
||||
int(3)
|
||||
["y"]=>
|
||||
int(4)
|
||||
}
|
||||
}
|
||||
}
|
||||
Exception: Test in %s:%d
|
||||
Stack trace:
|
||||
#0 %s(%d): test(1, 2, x: 3, y: 4)
|
||||
#1 {main}
|
106
Zend/tests/named_params/basic.phpt
Normal file
106
Zend/tests/named_params/basic.phpt
Normal file
@ -0,0 +1,106 @@
|
||||
--TEST--
|
||||
Basic test
|
||||
--FILE--
|
||||
<?php
|
||||
|
||||
function test($a, $b, $c = "c", $d = "d", $e = "e") {
|
||||
echo "a=$a, b=$b, c=$c, d=$d, e=$e\n";
|
||||
}
|
||||
|
||||
function test3(&$a, &$b, &$c = "c", &$d = "d", &$e = "e") {
|
||||
echo "a=$a, b=$b, c=$c, d=$d, e=$e\n";
|
||||
}
|
||||
|
||||
function &id($x) {
|
||||
return $x;
|
||||
}
|
||||
|
||||
$a = "A"; $b = "B"; $c = "C"; $d = "D"; $e = "E";
|
||||
|
||||
echo "SEND_VAL:\n";
|
||||
test("A", "B", "C", d: "D", e: "E");
|
||||
test("A", "B", "C", e: "E", d: "D");
|
||||
test(e: "E", a: "A", d: "D", b: "B", c: "C");
|
||||
test("A", "B", "C", e: "E");
|
||||
|
||||
echo "SEND_VAL_EX:\n";
|
||||
test2("A", "B", "C", d: "D", e: "E");
|
||||
test2("A", "B", "C", e: "E", d: "D");
|
||||
test2(e: "E", a: "A", d: "D", b: "B", c: "C");
|
||||
test2("A", "B", "C", e: "E");
|
||||
|
||||
echo "SEND_VAR:\n";
|
||||
test($a, $b, $c, d: $d, e: $e);
|
||||
test($a, $b, $c, e: $e, d: $d);
|
||||
test(e: $e, a: $a, d: $d, b: $b, c: $c);
|
||||
test(a: $a, b: $b, c: $c, e: $e);
|
||||
|
||||
echo "SEND_VAR_EX:\n";
|
||||
test2($a, $b, $c, d: $d, e: $e);
|
||||
test2($a, $b, $c, e: $e, d: $d);
|
||||
test2(e: $e, a: $a, d: $d, b: $b, c: $c);
|
||||
test2(a: $a, b: $b, c: $c, e: $e);
|
||||
|
||||
echo "SEND_VAR_NO_REF:\n";
|
||||
test3(id("A"), id("B"), id("C"), d: id("D"), e: id("E"));
|
||||
test3(id("A"), id("B"), id("C"), e: id("E"), d: id("D"));
|
||||
test3(e: id("E"), a: id("A"), d: id("D"), b: id("B"), c: id("C"));
|
||||
test3(id("A"), id("B"), id("C"), e: id("E"));
|
||||
|
||||
echo "SEND_VAR_NO_REF_EX:\n";
|
||||
test4(id("A"), id("B"), id("C"), d: id("D"), e: id("E"));
|
||||
test4(id("A"), id("B"), id("C"), e: id("E"), d: id("D"));
|
||||
test4(e: id("E"), a: id("A"), d: id("D"), b: id("B"), c: id("C"));
|
||||
test4(id("A"), id("B"), id("C"), e: id("E"));
|
||||
|
||||
echo "SEND_REF:\n";
|
||||
test3($a, $b, $c, d: $d, e: $e);
|
||||
test3($a, $b, $c, e: $e, d: $d);
|
||||
test3(e: $e, a: $a, d: $d, b: $b, c: $c);
|
||||
test3(a: $a, b: $b, c: $c, e: $e);
|
||||
|
||||
function test2($a, $b, $c = "c", $d = "d", $e = "e") {
|
||||
echo "a=$a, b=$b, c=$c, d=$d, e=$e\n";
|
||||
}
|
||||
|
||||
function test4(&$a, &$b, &$c = "c", &$d = "d", &$e = "e") {
|
||||
echo "a=$a, b=$b, c=$c, d=$d, e=$e\n";
|
||||
}
|
||||
|
||||
?>
|
||||
--EXPECT--
|
||||
SEND_VAL:
|
||||
a=A, b=B, c=C, d=D, e=E
|
||||
a=A, b=B, c=C, d=D, e=E
|
||||
a=A, b=B, c=C, d=D, e=E
|
||||
a=A, b=B, c=C, d=d, e=E
|
||||
SEND_VAL_EX:
|
||||
a=A, b=B, c=C, d=D, e=E
|
||||
a=A, b=B, c=C, d=D, e=E
|
||||
a=A, b=B, c=C, d=D, e=E
|
||||
a=A, b=B, c=C, d=d, e=E
|
||||
SEND_VAR:
|
||||
a=A, b=B, c=C, d=D, e=E
|
||||
a=A, b=B, c=C, d=D, e=E
|
||||
a=A, b=B, c=C, d=D, e=E
|
||||
a=A, b=B, c=C, d=d, e=E
|
||||
SEND_VAR_EX:
|
||||
a=A, b=B, c=C, d=D, e=E
|
||||
a=A, b=B, c=C, d=D, e=E
|
||||
a=A, b=B, c=C, d=D, e=E
|
||||
a=A, b=B, c=C, d=d, e=E
|
||||
SEND_VAR_NO_REF:
|
||||
a=A, b=B, c=C, d=D, e=E
|
||||
a=A, b=B, c=C, d=D, e=E
|
||||
a=A, b=B, c=C, d=D, e=E
|
||||
a=A, b=B, c=C, d=d, e=E
|
||||
SEND_VAR_NO_REF_EX:
|
||||
a=A, b=B, c=C, d=D, e=E
|
||||
a=A, b=B, c=C, d=D, e=E
|
||||
a=A, b=B, c=C, d=D, e=E
|
||||
a=A, b=B, c=C, d=d, e=E
|
||||
SEND_REF:
|
||||
a=A, b=B, c=C, d=D, e=E
|
||||
a=A, b=B, c=C, d=D, e=E
|
||||
a=A, b=B, c=C, d=D, e=E
|
||||
a=A, b=B, c=C, d=d, e=E
|
105
Zend/tests/named_params/call_user_func.phpt
Normal file
105
Zend/tests/named_params/call_user_func.phpt
Normal file
@ -0,0 +1,105 @@
|
||||
--TEST--
|
||||
call_user_func() and friends with named parameters
|
||||
--FILE--
|
||||
<?php
|
||||
|
||||
$test = function($a = 'a', $b = 'b', $c = 'c') {
|
||||
echo "a = $a, b = $b, c = $c\n";
|
||||
};
|
||||
$test_variadic = function(...$args) {
|
||||
var_dump($args);
|
||||
};
|
||||
$test_ref = function(&$ref) {
|
||||
$ref++;
|
||||
};
|
||||
|
||||
class Test {
|
||||
public function __construct($a = 'a', $b = 'b', $c = 'c') {
|
||||
if (func_num_args() != 0) {
|
||||
echo "a = $a, b = $b, c = $c\n";
|
||||
}
|
||||
}
|
||||
|
||||
public function method($a = 'a', $b = 'b', $c = 'c') {
|
||||
echo "a = $a, b = $b, c = $c\n";
|
||||
}
|
||||
}
|
||||
|
||||
call_user_func($test, 'A', c: 'C');
|
||||
call_user_func($test, c: 'C', a: 'A');
|
||||
call_user_func($test, c: 'C');
|
||||
call_user_func($test_variadic, 'A', c: 'C');
|
||||
call_user_func($test_ref, ref: null);
|
||||
var_dump(call_user_func('call_user_func', $test, c: 'D'));
|
||||
try {
|
||||
var_dump(call_user_func('array_slice', [1, 2, 3, 4, 5], length: 2));
|
||||
} catch (ArgumentCountError $e) {
|
||||
echo $e->getMessage(), "\n";
|
||||
}
|
||||
try {
|
||||
var_dump(call_user_func('array_slice', [1, 2, 3, 4, 'x' => 5], 3, preserve_keys: true));
|
||||
} catch (ArgumentCountError $e) {
|
||||
echo $e->getMessage(), "\n";
|
||||
}
|
||||
echo "\n";
|
||||
|
||||
$test->__invoke('A', c: 'C');
|
||||
$test_variadic->__invoke('A', c: 'C');
|
||||
$test->call(new class {}, 'A', c: 'C');
|
||||
$test_variadic->call(new class {}, 'A', c: 'C');
|
||||
echo "\n";
|
||||
|
||||
$rf = new ReflectionFunction($test);
|
||||
$rf->invoke('A', c: 'C');
|
||||
$rf->invokeArgs(['A', 'c' => 'C']);
|
||||
$rm = new ReflectionMethod(Test::class, 'method');
|
||||
$rm->invoke(new Test, 'A', c: 'C');
|
||||
$rm->invokeArgs(new Test, ['A', 'c' => 'C']);
|
||||
$rc = new ReflectionClass(Test::class);
|
||||
$rc->newInstance('A', c: 'C');
|
||||
$rc->newInstanceArgs(['A', 'c' => 'C']);
|
||||
|
||||
?>
|
||||
--EXPECTF--
|
||||
a = A, b = b, c = C
|
||||
a = A, b = b, c = C
|
||||
a = a, b = b, c = C
|
||||
array(2) {
|
||||
[0]=>
|
||||
string(1) "A"
|
||||
["c"]=>
|
||||
string(1) "C"
|
||||
}
|
||||
|
||||
Warning: {closure}(): Argument #1 ($ref) must be passed by reference, value given in %s on line %d
|
||||
a = a, b = b, c = D
|
||||
NULL
|
||||
array_slice(): Argument #2 ($offset) not passed
|
||||
array(2) {
|
||||
[3]=>
|
||||
int(4)
|
||||
["x"]=>
|
||||
int(5)
|
||||
}
|
||||
|
||||
a = A, b = b, c = C
|
||||
array(2) {
|
||||
[0]=>
|
||||
string(1) "A"
|
||||
["c"]=>
|
||||
string(1) "C"
|
||||
}
|
||||
a = A, b = b, c = C
|
||||
array(2) {
|
||||
[0]=>
|
||||
string(1) "A"
|
||||
["c"]=>
|
||||
string(1) "C"
|
||||
}
|
||||
|
||||
a = A, b = b, c = C
|
||||
a = A, b = b, c = C
|
||||
a = A, b = b, c = C
|
||||
a = A, b = b, c = C
|
||||
a = A, b = b, c = C
|
||||
a = A, b = b, c = C
|
72
Zend/tests/named_params/call_user_func_array.phpt
Normal file
72
Zend/tests/named_params/call_user_func_array.phpt
Normal file
@ -0,0 +1,72 @@
|
||||
--TEST--
|
||||
Behavior of call_user_func_array() with named parameters
|
||||
--FILE--
|
||||
<?php
|
||||
|
||||
namespace {
|
||||
$test = function($a = 'a', $b = 'b', $c = 'c') {
|
||||
echo "a = $a, b = $b, c = $c\n";
|
||||
};
|
||||
$test_variadic = function(...$args) {
|
||||
var_dump($args);
|
||||
};
|
||||
|
||||
call_user_func_array($test, ['A', 'B']);
|
||||
call_user_func_array($test, [1 => 'A', 0 => 'B']);
|
||||
call_user_func_array($test, ['A', 'c' => 'C']);
|
||||
call_user_func_array($test_variadic, ['A', 'c' => 'C']);
|
||||
try {
|
||||
call_user_func_array($test, ['d' => 'D']);
|
||||
} catch (\Error $e) {
|
||||
echo $e->getMessage(), "\n";
|
||||
}
|
||||
try {
|
||||
call_user_func_array($test, ['c' => 'C', 'A']);
|
||||
} catch (\Error $e) {
|
||||
echo $e->getMessage(), "\n";
|
||||
}
|
||||
echo "\n";
|
||||
}
|
||||
|
||||
namespace Foo {
|
||||
call_user_func_array($test, ['A', 'B']);
|
||||
call_user_func_array($test, [1 => 'A', 0 => 'B']);
|
||||
call_user_func_array($test, ['A', 'c' => 'C']);
|
||||
call_user_func_array($test_variadic, ['A', 'c' => 'C']);
|
||||
try {
|
||||
call_user_func_array($test, ['d' => 'D']);
|
||||
} catch (\Error $e) {
|
||||
echo $e->getMessage(), "\n";
|
||||
}
|
||||
try {
|
||||
call_user_func_array($test, ['c' => 'C', 'A']);
|
||||
} catch (\Error $e) {
|
||||
echo $e->getMessage(), "\n";
|
||||
}
|
||||
}
|
||||
|
||||
?>
|
||||
--EXPECT--
|
||||
a = A, b = B, c = c
|
||||
a = A, b = B, c = c
|
||||
a = A, b = b, c = C
|
||||
array(2) {
|
||||
[0]=>
|
||||
string(1) "A"
|
||||
["c"]=>
|
||||
string(1) "C"
|
||||
}
|
||||
Unknown named parameter $d
|
||||
Cannot use positional argument after named argument
|
||||
|
||||
a = A, b = B, c = c
|
||||
a = A, b = B, c = c
|
||||
a = A, b = b, c = C
|
||||
array(2) {
|
||||
[0]=>
|
||||
string(1) "A"
|
||||
["c"]=>
|
||||
string(1) "C"
|
||||
}
|
||||
Unknown named parameter $d
|
||||
Cannot use positional argument after named argument
|
35
Zend/tests/named_params/defaults.phpt
Normal file
35
Zend/tests/named_params/defaults.phpt
Normal file
@ -0,0 +1,35 @@
|
||||
--TEST--
|
||||
Skipping over default parameters
|
||||
--FILE--
|
||||
<?php
|
||||
|
||||
function test1($a = 'a', $b = 'b') {
|
||||
echo "a: $a, b: $b\n";
|
||||
}
|
||||
|
||||
function test2($a = SOME_CONST, $b = 'b') {
|
||||
echo "a: $a, b: $b\n";
|
||||
}
|
||||
|
||||
function test3($a = SOME_OTHER_CONST, $b = 'b') {
|
||||
echo "a: $a, b: $b\n";
|
||||
}
|
||||
|
||||
test1(b: 'B');
|
||||
|
||||
define('SOME_CONST', 'X');
|
||||
test2(b: 'B');
|
||||
|
||||
// NB: We also want to test the stack trace.
|
||||
test3(b: 'B');
|
||||
|
||||
?>
|
||||
--EXPECTF--
|
||||
a: a, b: B
|
||||
a: X, b: B
|
||||
|
||||
Fatal error: Uncaught Error: Undefined constant "SOME_OTHER_CONST" in %s:%d
|
||||
Stack trace:
|
||||
#0 %s(%d): test3(NULL, 'B')
|
||||
#1 {main}
|
||||
thrown in %s on line %d
|
23
Zend/tests/named_params/duplicate_param.phpt
Normal file
23
Zend/tests/named_params/duplicate_param.phpt
Normal file
@ -0,0 +1,23 @@
|
||||
--TEST--
|
||||
Duplicate param
|
||||
--FILE--
|
||||
<?php
|
||||
|
||||
function test($a) {}
|
||||
|
||||
try {
|
||||
test(a: 1, a: 2);
|
||||
} catch (Error $e) {
|
||||
echo $e->getMessage(), "\n";
|
||||
}
|
||||
|
||||
try {
|
||||
test(1, a: 2);
|
||||
} catch (Error $e) {
|
||||
echo $e->getMessage(), "\n";
|
||||
}
|
||||
|
||||
?>
|
||||
--EXPECT--
|
||||
Named parameter $a overwrites previous argument
|
||||
Named parameter $a overwrites previous argument
|
29
Zend/tests/named_params/func_get_args.phpt
Normal file
29
Zend/tests/named_params/func_get_args.phpt
Normal file
@ -0,0 +1,29 @@
|
||||
--TEST--
|
||||
Behavior of func_get_args() and friends with named parameters
|
||||
--FILE--
|
||||
<?php
|
||||
|
||||
function test($a, $b = 'b', $c = 'c') {
|
||||
var_dump(func_num_args());
|
||||
var_dump(func_get_args());
|
||||
var_dump(func_get_arg(0));
|
||||
var_dump(func_get_arg(1));
|
||||
var_dump(func_get_arg(2));
|
||||
}
|
||||
|
||||
test(c: 'C', a: 'A');
|
||||
|
||||
?>
|
||||
--EXPECT--
|
||||
int(3)
|
||||
array(3) {
|
||||
[0]=>
|
||||
string(1) "A"
|
||||
[1]=>
|
||||
string(1) "b"
|
||||
[2]=>
|
||||
string(1) "C"
|
||||
}
|
||||
string(1) "A"
|
||||
string(1) "b"
|
||||
string(1) "C"
|
39
Zend/tests/named_params/internal.phpt
Normal file
39
Zend/tests/named_params/internal.phpt
Normal file
@ -0,0 +1,39 @@
|
||||
--TEST--
|
||||
Named params on internal functions
|
||||
--FILE--
|
||||
<?php
|
||||
|
||||
var_dump(array_slice(arg: [1, 2, 3, 4, 5], offset: 2, length: 2));
|
||||
var_dump(array_slice(length: 2, offset: 2, arg: [1, 2, 3, 4, 5]));
|
||||
|
||||
var_dump(array_slice(arg: ['a' => 0, 'b' => 1], offset: 1, preserve_keys: true));
|
||||
var_dump(array_slice(['a' => 0, 'b' => 1], preserve_keys: true, offset: 1));
|
||||
var_dump(str_pad("foo", 6, pad_type: STR_PAD_LEFT));
|
||||
|
||||
// Named params work with specialized functions.
|
||||
var_dump(strlen(string: 'foo'));
|
||||
|
||||
?>
|
||||
--EXPECT--
|
||||
array(2) {
|
||||
[0]=>
|
||||
int(3)
|
||||
[1]=>
|
||||
int(4)
|
||||
}
|
||||
array(2) {
|
||||
[0]=>
|
||||
int(3)
|
||||
[1]=>
|
||||
int(4)
|
||||
}
|
||||
array(1) {
|
||||
["b"]=>
|
||||
int(1)
|
||||
}
|
||||
array(1) {
|
||||
["b"]=>
|
||||
int(1)
|
||||
}
|
||||
string(6) " foo"
|
||||
int(3)
|
21
Zend/tests/named_params/internal_variadics.phpt
Normal file
21
Zend/tests/named_params/internal_variadics.phpt
Normal file
@ -0,0 +1,21 @@
|
||||
--TEST--
|
||||
Named params on internal functions: Variadic functions that don't support extra named args
|
||||
--FILE--
|
||||
<?php
|
||||
|
||||
try {
|
||||
array_merge([1, 2], a: [3, 4]);
|
||||
} catch (ArgumentCountError $e) {
|
||||
echo $e->getMessage(), "\n";
|
||||
}
|
||||
|
||||
try {
|
||||
array_diff_key([1, 2], [3, 4], a: [5, 6]);
|
||||
} catch (ArgumentCountError $e) {
|
||||
echo $e->getMessage(), "\n";
|
||||
}
|
||||
|
||||
?>
|
||||
--EXPECT--
|
||||
array_merge() does not accept unknown named parameters
|
||||
array_diff_key() does not accept unknown named parameters
|
38
Zend/tests/named_params/missing_param.phpt
Normal file
38
Zend/tests/named_params/missing_param.phpt
Normal file
@ -0,0 +1,38 @@
|
||||
--TEST--
|
||||
Required parameter not passed
|
||||
--FILE--
|
||||
<?php
|
||||
|
||||
function test($a, $b, $c, $d) {
|
||||
}
|
||||
|
||||
try {
|
||||
test(a: 'a', d: 'd');
|
||||
} catch (ArgumentCountError $e) {
|
||||
echo $e->getMessage(), "\n";
|
||||
}
|
||||
|
||||
try {
|
||||
array_keys(strict: true);
|
||||
} catch (ArgumentCountError $e) {
|
||||
echo $e->getMessage(), "\n";
|
||||
}
|
||||
|
||||
try {
|
||||
array_keys([], strict: true);
|
||||
} catch (ArgumentCountError $e) {
|
||||
echo $e->getMessage(), "\n";
|
||||
}
|
||||
|
||||
// This works fine, as search_value is explicitly specified.
|
||||
var_dump(array_keys([41, 42], search_value: 42, strict: true));
|
||||
|
||||
?>
|
||||
--EXPECT--
|
||||
test(): Argument #2 ($b) not passed
|
||||
array_keys(): Argument #1 ($arg) not passed
|
||||
array_keys(): Argument #2 ($search_value) must be passed explicitly, because the default value is not known
|
||||
array(1) {
|
||||
[0]=>
|
||||
int(1)
|
||||
}
|
10
Zend/tests/named_params/positional_after_named.phpt
Normal file
10
Zend/tests/named_params/positional_after_named.phpt
Normal file
@ -0,0 +1,10 @@
|
||||
--TEST--
|
||||
Cannot used positional parameter after named parameter
|
||||
--FILE--
|
||||
<?php
|
||||
|
||||
test(a: 1, 2);
|
||||
|
||||
?>
|
||||
--EXPECTF--
|
||||
Fatal error: Cannot use positional argument after named argument in %s on line %d
|
39
Zend/tests/named_params/references.phpt
Normal file
39
Zend/tests/named_params/references.phpt
Normal file
@ -0,0 +1,39 @@
|
||||
--TEST--
|
||||
Check that reference detection works properly
|
||||
--FILE--
|
||||
<?php
|
||||
|
||||
$v00 = $v01 = $v32 = $v33 = 0;
|
||||
test(p32: $v32, p33: $v33, p00: $v00, p01: $v01);
|
||||
echo "$v00 $v01 $v32 $v33\n";
|
||||
|
||||
$v = [0 => 0, 1 => 0, 32 => 0, 33 => 0];
|
||||
test(p32: $v[32], p33: $v[33], p00: $v[0], p01: $v[1]);
|
||||
echo "$v[0] $v[1] $v[32] $v[33]\n";
|
||||
|
||||
function test(
|
||||
&$p00 = null, $p01 = null, &$p02 = null, $p03 = null, &$p04 = null, $p05 = null,
|
||||
&$p06 = null, $p07 = null, &$p08 = null, $p09 = null, &$p10 = null, $p11 = null,
|
||||
&$p12 = null, $p13 = null, &$p14 = null, $p15 = null, &$p16 = null, $p17 = null,
|
||||
&$p18 = null, $p19 = null, &$p20 = null, $p21 = null, &$p22 = null, $p23 = null,
|
||||
&$p24 = null, $p25 = null, &$p26 = null, $p27 = null, &$p28 = null, $p29 = null,
|
||||
&$p30 = null, $p31 = null, &$p32 = null, $p33 = null, &$p34 = null, $p35 = null
|
||||
) {
|
||||
$p00++;
|
||||
$p32++;
|
||||
}
|
||||
|
||||
$v00 = $v01 = $v32 = $v33 = 0;
|
||||
test(p32: $v32, p33: $v33, p00: $v00, p01: $v01);
|
||||
echo "$v00 $v01 $v32 $v33\n";
|
||||
|
||||
$v = [0 => 0, 1 => 0, 32 => 0, 33 => 0];
|
||||
test(p32: $v[32], p33: $v[33], p00: $v[0], p01: $v[1]);
|
||||
echo "$v[0] $v[1] $v[32] $v[33]\n";
|
||||
|
||||
?>
|
||||
--EXPECT--
|
||||
1 0 1 0
|
||||
1 0 1 0
|
||||
1 0 1 0
|
||||
1 0 1 0
|
15
Zend/tests/named_params/reserved.phpt
Normal file
15
Zend/tests/named_params/reserved.phpt
Normal file
@ -0,0 +1,15 @@
|
||||
--TEST--
|
||||
Reserved keywords can be used with named parameters
|
||||
--FILE--
|
||||
<?php
|
||||
|
||||
function test($array) {
|
||||
var_dump($array);
|
||||
}
|
||||
|
||||
test(array: []);
|
||||
|
||||
?>
|
||||
--EXPECT--
|
||||
array(0) {
|
||||
}
|
26
Zend/tests/named_params/unknown_named_param.phpt
Normal file
26
Zend/tests/named_params/unknown_named_param.phpt
Normal file
@ -0,0 +1,26 @@
|
||||
--TEST--
|
||||
Unknown named parameter
|
||||
--FILE--
|
||||
<?php
|
||||
|
||||
function test($a) {
|
||||
}
|
||||
|
||||
function test2(...$a) {
|
||||
}
|
||||
|
||||
try {
|
||||
test(b: 42);
|
||||
} catch (Error $e) {
|
||||
echo $e->getMessage(), "\n";
|
||||
}
|
||||
|
||||
try {
|
||||
test2(a: 42);
|
||||
} catch (Error $e) {
|
||||
echo $e->getMessage(), "\n";
|
||||
}
|
||||
|
||||
?>
|
||||
--EXPECT--
|
||||
Unknown named parameter $b
|
85
Zend/tests/named_params/unpack.phpt
Normal file
85
Zend/tests/named_params/unpack.phpt
Normal file
@ -0,0 +1,85 @@
|
||||
--TEST--
|
||||
Unpacking named parameters
|
||||
--FILE--
|
||||
<?php
|
||||
|
||||
function test($a, $b, $c) {
|
||||
echo "a = $a, b = $b, c = $c\n";
|
||||
}
|
||||
|
||||
function test2($a = null, &$b = null) {
|
||||
$b++;
|
||||
}
|
||||
|
||||
test(...['a' => 'a', 'b' => 'b', 'c' => 'c']);
|
||||
test(...['c' => 'c', 'b' => 'b', 'a' => 'a']);
|
||||
test(...['a', 'b' => 'b', 'c' => 'c']);
|
||||
|
||||
try {
|
||||
test(...['a', 'b' => 'b', 'c']);
|
||||
} catch (Error $e) {
|
||||
echo $e->getMessage(), "\n";
|
||||
}
|
||||
|
||||
try {
|
||||
test(...['a', 'a' => 'a']);
|
||||
} catch (Error $e) {
|
||||
echo $e->getMessage(), "\n";
|
||||
}
|
||||
|
||||
$ary = ['b' => 0];
|
||||
$ary2 = $ary;
|
||||
test2(...$ary);
|
||||
var_dump($ary, $ary2);
|
||||
|
||||
test(...new ArrayIterator(['a' => 'a', 'b' => 'b', 'c' => 'c']));
|
||||
test(...new ArrayIterator(['c' => 'c', 'b' => 'b', 'a' => 'a']));
|
||||
test(...new ArrayIterator(['a', 'b' => 'b', 'c' => 'c']));
|
||||
|
||||
try {
|
||||
test(...new ArrayIterator(['a', 'b' => 'b', 'c']));
|
||||
} catch (Error $e) {
|
||||
echo $e->getMessage(), "\n";
|
||||
}
|
||||
|
||||
try {
|
||||
test(...new ArrayIterator(['a', 'a' => 'a']));
|
||||
} catch (Error $e) {
|
||||
echo $e->getMessage(), "\n";
|
||||
}
|
||||
|
||||
$ary = ['b' => 0];
|
||||
$ary2 = $ary;
|
||||
test2(...new ArrayIterator($ary));
|
||||
var_dump($ary, $ary2);
|
||||
|
||||
?>
|
||||
--EXPECTF--
|
||||
a = a, b = b, c = c
|
||||
a = a, b = b, c = c
|
||||
a = a, b = b, c = c
|
||||
Cannot use positional argument after named argument during unpacking
|
||||
Named parameter $a overwrites previous argument
|
||||
array(1) {
|
||||
["b"]=>
|
||||
int(1)
|
||||
}
|
||||
array(1) {
|
||||
["b"]=>
|
||||
int(0)
|
||||
}
|
||||
a = a, b = b, c = c
|
||||
a = a, b = b, c = c
|
||||
a = a, b = b, c = c
|
||||
Cannot use positional argument after named argument during unpacking
|
||||
Named parameter $a overwrites previous argument
|
||||
|
||||
Warning: Cannot pass by-reference argument 2 of test2() by unpacking a Traversable, passing by-value instead in %s on line %d
|
||||
array(1) {
|
||||
["b"]=>
|
||||
int(0)
|
||||
}
|
||||
array(1) {
|
||||
["b"]=>
|
||||
int(0)
|
||||
}
|
10
Zend/tests/named_params/unpack_and_named_1.phpt
Normal file
10
Zend/tests/named_params/unpack_and_named_1.phpt
Normal file
@ -0,0 +1,10 @@
|
||||
--TEST--
|
||||
Mixing unpacking and named params (1)
|
||||
--FILE--
|
||||
<?php
|
||||
|
||||
test(...[], a: 42);
|
||||
|
||||
?>
|
||||
--EXPECTF--
|
||||
Fatal error: Cannot combine named arguments and argument unpacking in %s on line %d
|
10
Zend/tests/named_params/unpack_and_named_2.phpt
Normal file
10
Zend/tests/named_params/unpack_and_named_2.phpt
Normal file
@ -0,0 +1,10 @@
|
||||
--TEST--
|
||||
Mixing unpacking and named params (2)
|
||||
--FILE--
|
||||
<?php
|
||||
|
||||
test(a: 42, ...[]);
|
||||
|
||||
?>
|
||||
--EXPECTF--
|
||||
Fatal error: Cannot combine named arguments and argument unpacking in %s on line %d
|
66
Zend/tests/named_params/variadic.phpt
Normal file
66
Zend/tests/named_params/variadic.phpt
Normal file
@ -0,0 +1,66 @@
|
||||
--TEST--
|
||||
Additional named params are collect into variadics
|
||||
--FILE--
|
||||
<?php
|
||||
|
||||
function test($a, string ...$extra) {
|
||||
var_dump($a);
|
||||
var_dump($extra);
|
||||
// Extra named parameters do not contribute toward func_num_args() and func_get_args().
|
||||
var_dump(func_num_args());
|
||||
var_dump(func_get_args());
|
||||
}
|
||||
|
||||
function test2(&...$refs) {
|
||||
foreach ($refs as &$ref) $ref++;
|
||||
}
|
||||
|
||||
test(b: 'b', a: 'a', c: 'c', extra: 'extra');
|
||||
echo "\n";
|
||||
|
||||
test('a', 'b', 'c', d: 'd');
|
||||
echo "\n";
|
||||
|
||||
$x = 0;
|
||||
$y = 0;
|
||||
test2(x: $x, y: $y);
|
||||
var_dump($x, $y);
|
||||
|
||||
?>
|
||||
--EXPECT--
|
||||
string(1) "a"
|
||||
array(3) {
|
||||
["b"]=>
|
||||
string(1) "b"
|
||||
["c"]=>
|
||||
string(1) "c"
|
||||
["extra"]=>
|
||||
string(5) "extra"
|
||||
}
|
||||
int(1)
|
||||
array(1) {
|
||||
[0]=>
|
||||
string(1) "a"
|
||||
}
|
||||
|
||||
string(1) "a"
|
||||
array(3) {
|
||||
[0]=>
|
||||
string(1) "b"
|
||||
[1]=>
|
||||
string(1) "c"
|
||||
["d"]=>
|
||||
string(1) "d"
|
||||
}
|
||||
int(3)
|
||||
array(3) {
|
||||
[0]=>
|
||||
string(1) "a"
|
||||
[1]=>
|
||||
string(1) "b"
|
||||
[2]=>
|
||||
string(1) "c"
|
||||
}
|
||||
|
||||
int(1)
|
||||
int(1)
|
@ -234,6 +234,9 @@ ZEND_API ZEND_COLD void ZEND_FASTCALL zend_wrong_parameter_error(int error_code,
|
||||
case ZPP_ERROR_WRONG_STRING_OR_CLASS_OR_NULL:
|
||||
zend_wrong_parameter_string_or_class_or_null_error(num, name, arg);
|
||||
break;
|
||||
case ZPP_ERROR_UNEXPECTED_EXTRA_NAMED:
|
||||
zend_unexpected_extra_named_error();
|
||||
break;
|
||||
default:
|
||||
ZEND_ASSERT(error_code != ZPP_ERROR_OK);
|
||||
}
|
||||
@ -306,6 +309,14 @@ ZEND_API ZEND_COLD void ZEND_FASTCALL zend_wrong_callback_error(uint32_t num, ch
|
||||
}
|
||||
/* }}} */
|
||||
|
||||
ZEND_API ZEND_COLD void ZEND_FASTCALL zend_unexpected_extra_named_error(void)
|
||||
{
|
||||
const char *space;
|
||||
const char *class_name = get_active_class_name(&space);
|
||||
zend_argument_count_error("%s%s%s() does not accept unknown named parameters",
|
||||
class_name, space, get_active_function_name());
|
||||
}
|
||||
|
||||
static ZEND_COLD void ZEND_FASTCALL zend_argument_error_variadic(zend_class_entry *error_ce, uint32_t arg_num, const char *format, va_list va) /* {{{ */
|
||||
{
|
||||
const char *space;
|
||||
@ -950,6 +961,11 @@ static int zend_parse_va_args(uint32_t num_args, const char *type_spec, va_list
|
||||
}
|
||||
/* mark the beginning of varargs */
|
||||
post_varargs = max_num_args;
|
||||
|
||||
if (ZEND_CALL_INFO(EG(current_execute_data)) & ZEND_CALL_HAS_EXTRA_NAMED_PARAMS) {
|
||||
zend_unexpected_extra_named_error();
|
||||
return FAILURE;
|
||||
}
|
||||
break;
|
||||
|
||||
default:
|
||||
@ -2298,11 +2314,20 @@ ZEND_API int zend_register_functions(zend_class_entry *scope, const zend_functio
|
||||
if (reg_function->common.arg_info && reg_function->common.num_args) {
|
||||
uint32_t i;
|
||||
for (i = 0; i < reg_function->common.num_args; i++) {
|
||||
zend_arg_info *arg_info = ®_function->common.arg_info[i];
|
||||
zend_internal_arg_info *arg_info = ®_function->internal_function.arg_info[i];
|
||||
ZEND_ASSERT(arg_info->name && "Parameter must have a name");
|
||||
if (ZEND_TYPE_IS_SET(arg_info->type)) {
|
||||
reg_function->common.fn_flags |= ZEND_ACC_HAS_TYPE_HINTS;
|
||||
}
|
||||
#if ZEND_DEBUG
|
||||
for (uint32_t j = 0; j < i; j++) {
|
||||
if (!strcmp(arg_info->name, reg_function->internal_function.arg_info[j].name)) {
|
||||
zend_error_noreturn(E_CORE_ERROR,
|
||||
"Duplicate parameter name $%s for function %s%s%s()", arg_info->name,
|
||||
scope ? ZSTR_VAL(scope->name) : "", scope ? "::" : "", ptr->fname);
|
||||
}
|
||||
}
|
||||
#endif
|
||||
}
|
||||
}
|
||||
|
||||
@ -3305,6 +3330,7 @@ ZEND_API int zend_fcall_info_init(zval *callable, uint32_t check_flags, zend_fca
|
||||
fci->retval = NULL;
|
||||
fci->param_count = 0;
|
||||
fci->params = NULL;
|
||||
fci->named_params = NULL;
|
||||
|
||||
return SUCCESS;
|
||||
}
|
||||
|
@ -47,6 +47,11 @@ typedef struct _zend_fcall_info {
|
||||
zval *params;
|
||||
zend_object *object;
|
||||
uint32_t param_count;
|
||||
/* This hashtable can also contain positional arguments (with integer keys),
|
||||
* which will be appended to the normal params[]. This makes it easier to
|
||||
* integrate APIs like call_user_func_array(). The usual restriction that
|
||||
* there may not be position arguments after named arguments applies. */
|
||||
HashTable *named_params;
|
||||
} zend_fcall_info;
|
||||
|
||||
typedef struct _zend_fcall_info_cache {
|
||||
@ -500,10 +505,13 @@ ZEND_API void add_property_zval_ex(zval *arg, const char *key, size_t key_len, z
|
||||
#define add_property_zval(__arg, __key, __value) add_property_zval_ex(__arg, __key, strlen(__key), __value)
|
||||
|
||||
|
||||
ZEND_API int _call_user_function_ex(zval *object, zval *function_name, zval *retval_ptr, uint32_t param_count, zval params[]);
|
||||
ZEND_API int _call_user_function_impl(zval *object, zval *function_name, zval *retval_ptr, uint32_t param_count, zval params[], HashTable *named_params);
|
||||
|
||||
#define call_user_function(_unused, object, function_name, retval_ptr, param_count, params) \
|
||||
_call_user_function_ex(object, function_name, retval_ptr, param_count, params)
|
||||
#define call_user_function(function_table, object, function_name, retval_ptr, param_count, params) \
|
||||
_call_user_function_impl(object, function_name, retval_ptr, param_count, params, NULL)
|
||||
|
||||
#define call_user_function_named(function_table, object, function_name, retval_ptr, param_count, params, named_params) \
|
||||
_call_user_function_impl(object, function_name, retval_ptr, param_count, params, named_params)
|
||||
|
||||
ZEND_API extern const zend_fcall_info empty_fcall_info;
|
||||
ZEND_API extern const zend_fcall_info_cache empty_fcall_info_cache;
|
||||
@ -569,14 +577,14 @@ ZEND_API int zend_call_function(zend_fcall_info *fci, zend_fcall_info_cache *fci
|
||||
* called_scope must be provided for instance and static method calls. */
|
||||
ZEND_API void zend_call_known_function(
|
||||
zend_function *fn, zend_object *object, zend_class_entry *called_scope, zval *retval_ptr,
|
||||
uint32_t param_count, zval *params);
|
||||
uint32_t param_count, zval *params, HashTable *named_params);
|
||||
|
||||
/* Call the provided zend_function instance method on an object. */
|
||||
static zend_always_inline void zend_call_known_instance_method(
|
||||
zend_function *fn, zend_object *object, zval *retval_ptr,
|
||||
uint32_t param_count, zval *params)
|
||||
{
|
||||
zend_call_known_function(fn, object, object->ce, retval_ptr, param_count, params);
|
||||
zend_call_known_function(fn, object, object->ce, retval_ptr, param_count, params, NULL);
|
||||
}
|
||||
|
||||
static zend_always_inline void zend_call_known_instance_method_with_0_params(
|
||||
@ -1241,6 +1249,7 @@ ZEND_API ZEND_COLD void ZEND_FASTCALL zend_wrong_parameter_class_or_null_error(u
|
||||
ZEND_API ZEND_COLD void ZEND_FASTCALL zend_wrong_parameter_string_or_class_error(uint32_t num, const char *name, zval *arg);
|
||||
ZEND_API ZEND_COLD void ZEND_FASTCALL zend_wrong_parameter_string_or_class_or_null_error(uint32_t num, const char *name, zval *arg);
|
||||
ZEND_API ZEND_COLD void ZEND_FASTCALL zend_wrong_callback_error(uint32_t num, char *error);
|
||||
ZEND_API ZEND_COLD void ZEND_FASTCALL zend_unexpected_extra_named_error(void);
|
||||
ZEND_API ZEND_COLD void ZEND_FASTCALL zend_argument_error(zend_class_entry *error_ce, uint32_t arg_num, const char *format, ...);
|
||||
ZEND_API ZEND_COLD void ZEND_FASTCALL zend_argument_type_error(uint32_t arg_num, const char *format, ...);
|
||||
ZEND_API ZEND_COLD void ZEND_FASTCALL zend_argument_value_error(uint32_t arg_num, const char *format, ...);
|
||||
@ -1254,6 +1263,7 @@ ZEND_API ZEND_COLD void ZEND_FASTCALL zend_argument_value_error(uint32_t arg_num
|
||||
#define ZPP_ERROR_WRONG_COUNT 6
|
||||
#define ZPP_ERROR_WRONG_STRING_OR_CLASS 7
|
||||
#define ZPP_ERROR_WRONG_STRING_OR_CLASS_OR_NULL 8
|
||||
#define ZPP_ERROR_UNEXPECTED_EXTRA_NAMED 9
|
||||
|
||||
#define ZEND_PARSE_PARAMETERS_START_EX(flags, min_num_args, max_num_args) do { \
|
||||
const int _flags = (flags); \
|
||||
@ -1701,11 +1711,31 @@ ZEND_API ZEND_COLD void ZEND_FASTCALL zend_argument_value_error(uint32_t arg_num
|
||||
dest = NULL; \
|
||||
dest_num = 0; \
|
||||
} \
|
||||
if (UNEXPECTED(ZEND_CALL_INFO(execute_data) & ZEND_CALL_HAS_EXTRA_NAMED_PARAMS)) { \
|
||||
_error_code = ZPP_ERROR_UNEXPECTED_EXTRA_NAMED; \
|
||||
break; \
|
||||
} \
|
||||
} while (0);
|
||||
|
||||
#define Z_PARAM_VARIADIC(spec, dest, dest_num) \
|
||||
Z_PARAM_VARIADIC_EX(spec, dest, dest_num, 0)
|
||||
|
||||
#define Z_PARAM_VARIADIC_WITH_NAMED(dest, dest_num, dest_named) do { \
|
||||
int _num_varargs = _num_args - _i; \
|
||||
if (EXPECTED(_num_varargs > 0)) { \
|
||||
dest = _real_arg + 1; \
|
||||
dest_num = _num_varargs; \
|
||||
} else { \
|
||||
dest = NULL; \
|
||||
dest_num = 0; \
|
||||
} \
|
||||
if (ZEND_CALL_INFO(execute_data) & ZEND_CALL_HAS_EXTRA_NAMED_PARAMS) { \
|
||||
dest_named = execute_data->extra_named_params; \
|
||||
} else { \
|
||||
dest_named = NULL; \
|
||||
} \
|
||||
} while (0);
|
||||
|
||||
#define Z_PARAM_STR_OR_ARRAY_HT_EX(dest_str, dest_ht, allow_null) \
|
||||
Z_PARAM_PROLOGUE(0, 0); \
|
||||
if (UNEXPECTED(!zend_parse_arg_str_or_array_ht(_arg, &dest_str, &dest_ht, allow_null))) { \
|
||||
|
@ -2062,6 +2062,11 @@ simple_list:
|
||||
zend_ast_export_name(str, ast->child[1], 0, indent);
|
||||
}
|
||||
break;
|
||||
case ZEND_AST_NAMED_ARG:
|
||||
smart_str_append(str, zend_ast_get_str(ast->child[0]));
|
||||
smart_str_appends(str, ": ");
|
||||
ast = ast->child[1];
|
||||
goto tail_call;
|
||||
|
||||
/* 3 child nodes */
|
||||
case ZEND_AST_METHOD_CALL:
|
||||
|
@ -145,6 +145,7 @@ enum _zend_ast_kind {
|
||||
ZEND_AST_ATTRIBUTE,
|
||||
ZEND_AST_MATCH,
|
||||
ZEND_AST_MATCH_ARM,
|
||||
ZEND_AST_NAMED_ARG,
|
||||
|
||||
/* 3 child nodes */
|
||||
ZEND_AST_METHOD_CALL = 3 << ZEND_AST_NUM_CHILDREN_SHIFT,
|
||||
|
@ -29,6 +29,7 @@ static HashTable internal_attributes;
|
||||
|
||||
void validate_attribute(zend_attribute *attr, uint32_t target, zend_class_entry *scope)
|
||||
{
|
||||
// TODO: More proper signature validation: Too many args, incorrect arg names.
|
||||
if (attr->argc > 0) {
|
||||
zval flags;
|
||||
|
||||
@ -121,7 +122,7 @@ ZEND_API int zend_get_attribute_value(zval *ret, zend_attribute *attr, uint32_t
|
||||
return FAILURE;
|
||||
}
|
||||
|
||||
ZVAL_COPY_OR_DUP(ret, &attr->argv[i]);
|
||||
ZVAL_COPY_OR_DUP(ret, &attr->args[i].value);
|
||||
|
||||
if (Z_TYPE_P(ret) == IS_CONSTANT_AST) {
|
||||
if (SUCCESS != zval_update_constant_ex(ret, scope)) {
|
||||
@ -182,7 +183,10 @@ static zend_always_inline void free_attribute(zend_attribute *attr, int persiste
|
||||
zend_string_release(attr->lcname);
|
||||
|
||||
for (i = 0; i < attr->argc; i++) {
|
||||
zval_ptr_dtor(&attr->argv[i]);
|
||||
if (attr->args[i].name) {
|
||||
zend_string_release(attr->args[i].name);
|
||||
}
|
||||
zval_ptr_dtor(&attr->args[i].value);
|
||||
}
|
||||
|
||||
pefree(attr, persistent);
|
||||
@ -219,7 +223,8 @@ ZEND_API zend_attribute *zend_add_attribute(HashTable **attributes, zend_bool pe
|
||||
|
||||
/* Initialize arguments to avoid partial initialization in case of fatal errors. */
|
||||
for (uint32_t i = 0; i < argc; i++) {
|
||||
ZVAL_UNDEF(&attr->argv[i]);
|
||||
attr->args[i].name = NULL;
|
||||
ZVAL_UNDEF(&attr->args[i].value);
|
||||
}
|
||||
|
||||
zend_hash_next_index_insert_ptr(*attributes, attr);
|
||||
|
@ -30,19 +30,25 @@
|
||||
#define ZEND_ATTRIBUTE_IS_REPEATABLE (1<<6)
|
||||
#define ZEND_ATTRIBUTE_FLAGS ((1<<7) - 1)
|
||||
|
||||
#define ZEND_ATTRIBUTE_SIZE(argc) (sizeof(zend_attribute) + sizeof(zval) * (argc) - sizeof(zval))
|
||||
#define ZEND_ATTRIBUTE_SIZE(argc) \
|
||||
(sizeof(zend_attribute) + sizeof(zend_attribute_arg) * (argc) - sizeof(zend_attribute_arg))
|
||||
|
||||
BEGIN_EXTERN_C()
|
||||
|
||||
extern ZEND_API zend_class_entry *zend_ce_attribute;
|
||||
|
||||
typedef struct {
|
||||
zend_string *name;
|
||||
zval value;
|
||||
} zend_attribute_arg;
|
||||
|
||||
typedef struct _zend_attribute {
|
||||
zend_string *name;
|
||||
zend_string *lcname;
|
||||
/* Parameter offsets start at 1, everything else uses 0. */
|
||||
uint32_t offset;
|
||||
uint32_t argc;
|
||||
zval argv[1];
|
||||
zend_attribute_arg args[1];
|
||||
} zend_attribute;
|
||||
|
||||
typedef struct _zend_internal_attribute {
|
||||
|
@ -1640,18 +1640,34 @@ static void debug_backtrace_get_args(zend_execute_data *call, zval *arg_array) /
|
||||
} else {
|
||||
ZVAL_EMPTY_ARRAY(arg_array);
|
||||
}
|
||||
|
||||
if (ZEND_CALL_INFO(call) & ZEND_CALL_HAS_EXTRA_NAMED_PARAMS) {
|
||||
zend_string *name;
|
||||
zval *arg;
|
||||
SEPARATE_ARRAY(arg_array);
|
||||
ZEND_HASH_FOREACH_STR_KEY_VAL(call->extra_named_params, name, arg) {
|
||||
ZVAL_DEREF(arg);
|
||||
Z_TRY_ADDREF_P(arg);
|
||||
zend_hash_add_new(Z_ARRVAL_P(arg_array), name, arg);
|
||||
} ZEND_HASH_FOREACH_END();
|
||||
}
|
||||
}
|
||||
/* }}} */
|
||||
|
||||
void debug_print_backtrace_args(zval *arg_array) /* {{{ */
|
||||
{
|
||||
zend_string *name;
|
||||
zval *tmp;
|
||||
int i = 0;
|
||||
|
||||
ZEND_HASH_FOREACH_VAL(Z_ARRVAL_P(arg_array), tmp) {
|
||||
ZEND_HASH_FOREACH_STR_KEY_VAL(Z_ARRVAL_P(arg_array), name, tmp) {
|
||||
if (i++) {
|
||||
ZEND_PUTS(", ");
|
||||
}
|
||||
if (name) {
|
||||
ZEND_PUTS(ZSTR_VAL(name));
|
||||
ZEND_PUTS(": ");
|
||||
}
|
||||
zend_print_flat_zval_r(tmp);
|
||||
} ZEND_HASH_FOREACH_END();
|
||||
}
|
||||
|
@ -48,9 +48,15 @@ static zend_object_handlers closure_handlers;
|
||||
ZEND_METHOD(Closure, __invoke) /* {{{ */
|
||||
{
|
||||
zend_function *func = EX(func);
|
||||
zval *arguments = ZEND_CALL_ARG(execute_data, 1);
|
||||
zval *args;
|
||||
uint32_t num_args;
|
||||
HashTable *named_args;
|
||||
|
||||
if (call_user_function(CG(function_table), NULL, ZEND_THIS, return_value, ZEND_NUM_ARGS(), arguments) == FAILURE) {
|
||||
ZEND_PARSE_PARAMETERS_START(0, -1)
|
||||
Z_PARAM_VARIADIC_WITH_NAMED(args, num_args, named_args)
|
||||
ZEND_PARSE_PARAMETERS_END();
|
||||
|
||||
if (call_user_function_named(CG(function_table), NULL, ZEND_THIS, return_value, num_args, args, named_args) == FAILURE) {
|
||||
RETVAL_FALSE;
|
||||
}
|
||||
|
||||
@ -122,9 +128,10 @@ ZEND_METHOD(Closure, call)
|
||||
fci.param_count = 0;
|
||||
fci.params = NULL;
|
||||
|
||||
if (zend_parse_parameters(ZEND_NUM_ARGS(), "o*", &newthis, &fci.params, &fci.param_count) == FAILURE) {
|
||||
RETURN_THROWS();
|
||||
}
|
||||
ZEND_PARSE_PARAMETERS_START(1, -1)
|
||||
Z_PARAM_OBJECT(newthis)
|
||||
Z_PARAM_VARIADIC_WITH_NAMED(fci.params, fci.param_count, fci.named_params)
|
||||
ZEND_PARSE_PARAMETERS_END();
|
||||
|
||||
closure = (zend_closure *) Z_OBJ_P(ZEND_THIS);
|
||||
|
||||
@ -246,6 +253,7 @@ static ZEND_NAMED_FUNCTION(zend_closure_call_magic) /* {{{ */ {
|
||||
|
||||
fcc.function_handler = (EX(func)->internal_function.fn_flags & ZEND_ACC_STATIC) ?
|
||||
EX(func)->internal_function.scope->__callstatic : EX(func)->internal_function.scope->__call;
|
||||
fci.named_params = NULL;
|
||||
fci.params = params;
|
||||
fci.param_count = 2;
|
||||
ZVAL_STR(&fci.params[0], EX(func)->common.function_name);
|
||||
|
@ -3285,15 +3285,48 @@ void zend_compile_compound_assign(znode *result, zend_ast *ast) /* {{{ */
|
||||
}
|
||||
/* }}} */
|
||||
|
||||
uint32_t zend_compile_args(zend_ast *ast, zend_function *fbc) /* {{{ */
|
||||
static uint32_t zend_get_arg_num(zend_function *fn, zend_string *arg_name) {
|
||||
// TODO: Caching?
|
||||
if (fn->type == ZEND_USER_FUNCTION) {
|
||||
for (uint32_t i = 0; i < fn->common.num_args; i++) {
|
||||
zend_arg_info *arg_info = &fn->op_array.arg_info[i];
|
||||
if (zend_string_equals(arg_info->name, arg_name)) {
|
||||
return i + 1;
|
||||
}
|
||||
}
|
||||
} else {
|
||||
for (uint32_t i = 0; i < fn->common.num_args; i++) {
|
||||
zend_internal_arg_info *arg_info = &fn->internal_function.arg_info[i];
|
||||
size_t len = strlen(arg_info->name);
|
||||
if (len == ZSTR_LEN(arg_name) && !memcmp(arg_info->name, ZSTR_VAL(arg_name), len)) {
|
||||
return i + 1;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/* Either an invalid argument name, or collected into a variadic argument. */
|
||||
return (uint32_t) -1;
|
||||
}
|
||||
|
||||
uint32_t zend_compile_args(
|
||||
zend_ast *ast, zend_function *fbc, bool *may_have_extra_named_args) /* {{{ */
|
||||
{
|
||||
zend_ast_list *args = zend_ast_get_list(ast);
|
||||
uint32_t i;
|
||||
zend_bool uses_arg_unpack = 0;
|
||||
uint32_t arg_count = 0; /* number of arguments not including unpacks */
|
||||
|
||||
/* Whether named arguments are used syntactically, to enforce language level limitations.
|
||||
* May not actually use named argument passing. */
|
||||
zend_bool uses_named_args = 0;
|
||||
/* Whether there may be any undef arguments due to the use of named arguments. */
|
||||
zend_bool may_have_undef = 0;
|
||||
/* Whether there may be any extra named arguments collected into a variadic. */
|
||||
*may_have_extra_named_args = 0;
|
||||
|
||||
for (i = 0; i < args->children; ++i) {
|
||||
zend_ast *arg = args->child[i];
|
||||
zend_string *arg_name = NULL;
|
||||
uint32_t arg_num = i + 1;
|
||||
|
||||
znode arg_node;
|
||||
@ -3301,6 +3334,11 @@ uint32_t zend_compile_args(zend_ast *ast, zend_function *fbc) /* {{{ */
|
||||
zend_uchar opcode;
|
||||
|
||||
if (arg->kind == ZEND_AST_UNPACK) {
|
||||
if (uses_named_args) {
|
||||
zend_error_noreturn(E_COMPILE_ERROR,
|
||||
"Cannot combine named arguments and argument unpacking");
|
||||
}
|
||||
|
||||
uses_arg_unpack = 1;
|
||||
fbc = NULL;
|
||||
|
||||
@ -3308,15 +3346,57 @@ uint32_t zend_compile_args(zend_ast *ast, zend_function *fbc) /* {{{ */
|
||||
opline = zend_emit_op(NULL, ZEND_SEND_UNPACK, &arg_node, NULL);
|
||||
opline->op2.num = arg_count;
|
||||
opline->result.var = EX_NUM_TO_VAR(arg_count - 1);
|
||||
|
||||
/* Unpack may contain named arguments. */
|
||||
may_have_undef = 1;
|
||||
if (!fbc || (fbc->common.fn_flags & ZEND_ACC_VARIADIC)) {
|
||||
*may_have_extra_named_args = 1;
|
||||
}
|
||||
continue;
|
||||
}
|
||||
|
||||
if (uses_arg_unpack) {
|
||||
zend_error_noreturn(E_COMPILE_ERROR,
|
||||
"Cannot use positional argument after argument unpacking");
|
||||
if (arg->kind == ZEND_AST_NAMED_ARG) {
|
||||
if (uses_arg_unpack) {
|
||||
zend_error_noreturn(E_COMPILE_ERROR,
|
||||
"Cannot combine named arguments and argument unpacking");
|
||||
}
|
||||
|
||||
uses_named_args = 1;
|
||||
arg_name = zval_make_interned_string(zend_ast_get_zval(arg->child[0]));
|
||||
arg = arg->child[1];
|
||||
|
||||
if (fbc) {
|
||||
arg_num = zend_get_arg_num(fbc, arg_name);
|
||||
if (arg_num == arg_count + 1) {
|
||||
/* Using named arguments, but passing in order. */
|
||||
arg_name = NULL;
|
||||
arg_count++;
|
||||
} else {
|
||||
// TODO: We could track which arguments were passed, even if out of order.
|
||||
may_have_undef = 1;
|
||||
if (arg_num == (uint32_t) -1 && (fbc->common.fn_flags & ZEND_ACC_VARIADIC)) {
|
||||
*may_have_extra_named_args = 1;
|
||||
}
|
||||
}
|
||||
} else {
|
||||
arg_num = (uint32_t) -1;
|
||||
may_have_undef = 1;
|
||||
*may_have_extra_named_args = 1;
|
||||
}
|
||||
} else {
|
||||
if (uses_arg_unpack) {
|
||||
zend_error_noreturn(E_COMPILE_ERROR,
|
||||
"Cannot use positional argument after argument unpacking");
|
||||
}
|
||||
|
||||
if (uses_named_args) {
|
||||
zend_error_noreturn(E_COMPILE_ERROR,
|
||||
"Cannot use positional argument after named argument");
|
||||
}
|
||||
|
||||
arg_count++;
|
||||
}
|
||||
|
||||
arg_count++;
|
||||
if (zend_is_call(arg)) {
|
||||
zend_compile_var(&arg_node, arg, BP_VAR_R, 0);
|
||||
if (arg_node.op_type & (IS_CONST|IS_TMP_VAR)) {
|
||||
@ -3327,7 +3407,7 @@ uint32_t zend_compile_args(zend_ast *ast, zend_function *fbc) /* {{{ */
|
||||
opcode = ZEND_SEND_VAL;
|
||||
}
|
||||
} else {
|
||||
if (fbc) {
|
||||
if (fbc && arg_num != (uint32_t) -1) {
|
||||
if (ARG_MUST_BE_SENT_BY_REF(fbc, arg_num)) {
|
||||
opcode = ZEND_SEND_VAR_NO_REF;
|
||||
} else if (ARG_MAY_BE_SENT_BY_REF(fbc, arg_num)) {
|
||||
@ -3340,7 +3420,7 @@ uint32_t zend_compile_args(zend_ast *ast, zend_function *fbc) /* {{{ */
|
||||
}
|
||||
}
|
||||
} else if (zend_is_variable(arg) && !zend_ast_is_short_circuited(arg)) {
|
||||
if (fbc) {
|
||||
if (fbc && arg_num != (uint32_t) -1) {
|
||||
if (ARG_SHOULD_BE_SENT_BY_REF(fbc, arg_num)) {
|
||||
zend_compile_var(&arg_node, arg, BP_VAR_W, 1);
|
||||
opcode = ZEND_SEND_REF;
|
||||
@ -3363,7 +3443,14 @@ uint32_t zend_compile_args(zend_ast *ast, zend_function *fbc) /* {{{ */
|
||||
}
|
||||
}
|
||||
opline = zend_emit_op(NULL, ZEND_CHECK_FUNC_ARG, NULL, NULL);
|
||||
opline->op2.num = arg_num;
|
||||
if (arg_name) {
|
||||
opline->op2_type = IS_CONST;
|
||||
zend_string_addref(arg_name);
|
||||
opline->op2.constant = zend_add_literal_string(&arg_name);
|
||||
opline->result.num = zend_alloc_cache_slots(2);
|
||||
} else {
|
||||
opline->op2.num = arg_num;
|
||||
}
|
||||
zend_compile_var(&arg_node, arg, BP_VAR_FUNC_ARG, 1);
|
||||
opcode = ZEND_SEND_FUNC_ARG;
|
||||
} while (0);
|
||||
@ -3372,7 +3459,7 @@ uint32_t zend_compile_args(zend_ast *ast, zend_function *fbc) /* {{{ */
|
||||
zend_compile_expr(&arg_node, arg);
|
||||
if (arg_node.op_type == IS_VAR) {
|
||||
/* pass ++$a or something similar */
|
||||
if (fbc) {
|
||||
if (fbc && arg_num != (uint32_t) -1) {
|
||||
if (ARG_MUST_BE_SENT_BY_REF(fbc, arg_num)) {
|
||||
opcode = ZEND_SEND_VAR_NO_REF;
|
||||
} else if (ARG_MAY_BE_SENT_BY_REF(fbc, arg_num)) {
|
||||
@ -3384,7 +3471,7 @@ uint32_t zend_compile_args(zend_ast *ast, zend_function *fbc) /* {{{ */
|
||||
opcode = ZEND_SEND_VAR_NO_REF_EX;
|
||||
}
|
||||
} else if (arg_node.op_type == IS_CV) {
|
||||
if (fbc) {
|
||||
if (fbc && arg_num != (uint32_t) -1) {
|
||||
if (ARG_SHOULD_BE_SENT_BY_REF(fbc, arg_num)) {
|
||||
opcode = ZEND_SEND_REF;
|
||||
} else {
|
||||
@ -3395,7 +3482,7 @@ uint32_t zend_compile_args(zend_ast *ast, zend_function *fbc) /* {{{ */
|
||||
}
|
||||
} else {
|
||||
/* Delay "Only variables can be passed by reference" error to execution */
|
||||
if (fbc && !ARG_MUST_BE_SENT_BY_REF(fbc, arg_num)) {
|
||||
if (fbc && arg_num != (uint32_t) -1 && !ARG_MUST_BE_SENT_BY_REF(fbc, arg_num)) {
|
||||
opcode = ZEND_SEND_VAL;
|
||||
} else {
|
||||
opcode = ZEND_SEND_VAL_EX;
|
||||
@ -3404,8 +3491,19 @@ uint32_t zend_compile_args(zend_ast *ast, zend_function *fbc) /* {{{ */
|
||||
}
|
||||
|
||||
opline = zend_emit_op(NULL, opcode, &arg_node, NULL);
|
||||
opline->op2.opline_num = arg_num;
|
||||
opline->result.var = EX_NUM_TO_VAR(arg_num - 1);
|
||||
if (arg_name) {
|
||||
opline->op2_type = IS_CONST;
|
||||
zend_string_addref(arg_name);
|
||||
opline->op2.constant = zend_add_literal_string(&arg_name);
|
||||
opline->result.num = zend_alloc_cache_slots(2);
|
||||
} else {
|
||||
opline->op2.opline_num = arg_num;
|
||||
opline->result.var = EX_NUM_TO_VAR(arg_num - 1);
|
||||
}
|
||||
}
|
||||
|
||||
if (may_have_undef) {
|
||||
zend_emit_op(NULL, ZEND_CHECK_UNDEF_ARGS, NULL, NULL);
|
||||
}
|
||||
|
||||
return arg_count;
|
||||
@ -3443,8 +3541,9 @@ void zend_compile_call_common(znode *result, zend_ast *args_ast, zend_function *
|
||||
zend_op *opline;
|
||||
uint32_t opnum_init = get_next_op_number() - 1;
|
||||
uint32_t arg_count;
|
||||
bool may_have_extra_named_args;
|
||||
|
||||
arg_count = zend_compile_args(args_ast, fbc);
|
||||
arg_count = zend_compile_args(args_ast, fbc, &may_have_extra_named_args);
|
||||
|
||||
zend_do_extended_fcall_begin();
|
||||
|
||||
@ -3456,6 +3555,9 @@ void zend_compile_call_common(znode *result, zend_ast *args_ast, zend_function *
|
||||
}
|
||||
|
||||
opline = zend_emit_op(result, zend_get_call_op(opline, fbc), NULL, NULL);
|
||||
if (may_have_extra_named_args) {
|
||||
opline->extended_value = ZEND_FCALL_MAY_HAVE_EXTRA_NAMED_PARAMS;
|
||||
}
|
||||
zend_do_extended_fcall_end();
|
||||
}
|
||||
/* }}} */
|
||||
@ -3520,11 +3622,12 @@ void zend_compile_dynamic_call(znode *result, znode *name_node, zend_ast *args_a
|
||||
}
|
||||
/* }}} */
|
||||
|
||||
static inline zend_bool zend_args_contain_unpack(zend_ast_list *args) /* {{{ */
|
||||
static inline zend_bool zend_args_contain_unpack_or_named(zend_ast_list *args) /* {{{ */
|
||||
{
|
||||
uint32_t i;
|
||||
for (i = 0; i < args->children; ++i) {
|
||||
if (args->child[i]->kind == ZEND_AST_UNPACK) {
|
||||
zend_ast *arg = args->child[i];
|
||||
if (arg->kind == ZEND_AST_UNPACK || arg->kind == ZEND_AST_NAMED_ARG) {
|
||||
return 1;
|
||||
}
|
||||
}
|
||||
@ -3774,6 +3877,7 @@ int zend_compile_func_cufa(znode *result, zend_ast_list *args, zend_string *lcna
|
||||
}
|
||||
zend_compile_expr(&arg_node, args->child[1]);
|
||||
zend_emit_op(NULL, ZEND_SEND_ARRAY, &arg_node, NULL);
|
||||
zend_emit_op(NULL, ZEND_CHECK_UNDEF_ARGS, NULL, NULL);
|
||||
zend_emit_op(result, ZEND_DO_FCALL, NULL, NULL);
|
||||
|
||||
return SUCCESS;
|
||||
@ -4080,7 +4184,7 @@ int zend_try_compile_special_func(znode *result, zend_string *lcname, zend_ast_l
|
||||
return FAILURE;
|
||||
}
|
||||
|
||||
if (zend_args_contain_unpack(args)) {
|
||||
if (zend_args_contain_unpack_or_named(args)) {
|
||||
return FAILURE;
|
||||
}
|
||||
|
||||
@ -6111,13 +6215,33 @@ static void zend_compile_attributes(HashTable **attributes, zend_ast *ast, uint3
|
||||
if (args) {
|
||||
ZEND_ASSERT(args->kind == ZEND_AST_ARG_LIST);
|
||||
|
||||
zend_bool uses_named_args = 0;
|
||||
for (j = 0; j < args->children; j++) {
|
||||
if (args->child[j]->kind == ZEND_AST_UNPACK) {
|
||||
zend_ast *arg_ast = args->child[j];
|
||||
|
||||
if (arg_ast->kind == ZEND_AST_UNPACK) {
|
||||
zend_error_noreturn(E_COMPILE_ERROR,
|
||||
"Cannot use unpacking in attribute argument list");
|
||||
}
|
||||
|
||||
zend_const_expr_to_zval(&attr->argv[j], args->child[j]);
|
||||
if (arg_ast->kind == ZEND_AST_NAMED_ARG) {
|
||||
attr->args[j].name = zend_string_copy(zend_ast_get_str(arg_ast->child[0]));
|
||||
arg_ast = arg_ast->child[1];
|
||||
uses_named_args = 1;
|
||||
|
||||
for (uint32_t k = 0; k < j; k++) {
|
||||
if (attr->args[k].name &&
|
||||
zend_string_equals(attr->args[k].name, attr->args[j].name)) {
|
||||
zend_error_noreturn(E_COMPILE_ERROR, "Duplicate named parameter $%s",
|
||||
ZSTR_VAL(attr->args[j].name));
|
||||
}
|
||||
}
|
||||
} else if (uses_named_args) {
|
||||
zend_error_noreturn(E_COMPILE_ERROR,
|
||||
"Cannot use positional argument after named argument");
|
||||
}
|
||||
|
||||
zend_const_expr_to_zval(&attr->args[j].value, arg_ast);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -511,6 +511,7 @@ struct _zend_execute_data {
|
||||
zend_execute_data *prev_execute_data;
|
||||
zend_array *symbol_table;
|
||||
void **run_time_cache; /* cache op_array->run_time_cache */
|
||||
zend_array *extra_named_params;
|
||||
};
|
||||
|
||||
#define ZEND_CALL_HAS_THIS IS_OBJECT_EX
|
||||
@ -528,6 +529,8 @@ struct _zend_execute_data {
|
||||
#define ZEND_CALL_FAKE_CLOSURE (1 << 23)
|
||||
#define ZEND_CALL_GENERATOR (1 << 24)
|
||||
#define ZEND_CALL_DYNAMIC (1 << 25)
|
||||
#define ZEND_CALL_MAY_HAVE_UNDEF (1 << 26)
|
||||
#define ZEND_CALL_HAS_EXTRA_NAMED_PARAMS (1 << 27)
|
||||
#define ZEND_CALL_SEND_ARG_BY_REF (1u << 31)
|
||||
|
||||
#define ZEND_CALL_NESTED_FUNCTION (ZEND_CALL_FUNCTION | ZEND_CALL_NESTED)
|
||||
@ -951,6 +954,8 @@ ZEND_API zend_string *zend_type_to_string(zend_type type);
|
||||
|
||||
#define ZEND_THROW_IS_EXPR 1u
|
||||
|
||||
#define ZEND_FCALL_MAY_HAVE_EXTRA_NAMED_PARAMS 1
|
||||
|
||||
/* The send mode and is_variadic flag are stored as part of zend_type */
|
||||
#define _ZEND_SEND_MODE_SHIFT _ZEND_TYPE_EXTRA_FLAGS_SHIFT
|
||||
#define _ZEND_IS_VARIADIC_BIT (1 << (_ZEND_TYPE_EXTRA_FLAGS_SHIFT + 2))
|
||||
|
@ -566,9 +566,14 @@ static void _build_trace_string(smart_str *str, HashTable *ht, uint32_t num) /*
|
||||
if (tmp) {
|
||||
if (Z_TYPE_P(tmp) == IS_ARRAY) {
|
||||
size_t last_len = ZSTR_LEN(str->s);
|
||||
zend_string *name;
|
||||
zval *arg;
|
||||
|
||||
ZEND_HASH_FOREACH_VAL(Z_ARRVAL_P(tmp), arg) {
|
||||
ZEND_HASH_FOREACH_STR_KEY_VAL(Z_ARRVAL_P(tmp), name, arg) {
|
||||
if (name) {
|
||||
smart_str_append(str, name);
|
||||
smart_str_appends(str, ": ");
|
||||
}
|
||||
_build_trace_args(arg, str);
|
||||
} ZEND_HASH_FOREACH_END();
|
||||
|
||||
@ -662,6 +667,7 @@ ZEND_METHOD(Exception, __toString)
|
||||
fci.retval = &trace;
|
||||
fci.param_count = 0;
|
||||
fci.params = NULL;
|
||||
fci.named_params = NULL;
|
||||
|
||||
zend_call_function(&fci, NULL);
|
||||
|
||||
|
@ -3671,7 +3671,7 @@ ZEND_API void zend_init_execute_data(zend_execute_data *execute_data, zend_op_ar
|
||||
}
|
||||
/* }}} */
|
||||
|
||||
static zend_execute_data *zend_vm_stack_copy_call_frame(zend_execute_data *call, uint32_t passed_args, uint32_t additional_args) /* {{{ */
|
||||
zend_execute_data *zend_vm_stack_copy_call_frame(zend_execute_data *call, uint32_t passed_args, uint32_t additional_args) /* {{{ */
|
||||
{
|
||||
zend_execute_data *new_call;
|
||||
int used_stack = (EG(vm_stack_top) - (zval*)call) + additional_args;
|
||||
@ -3707,16 +3707,6 @@ static zend_execute_data *zend_vm_stack_copy_call_frame(zend_execute_data *call,
|
||||
}
|
||||
/* }}} */
|
||||
|
||||
static zend_always_inline void zend_vm_stack_extend_call_frame(zend_execute_data **call, uint32_t passed_args, uint32_t additional_args) /* {{{ */
|
||||
{
|
||||
if (EXPECTED((uint32_t)(EG(vm_stack_end) - EG(vm_stack_top)) > additional_args)) {
|
||||
EG(vm_stack_top) += additional_args;
|
||||
} else {
|
||||
*call = zend_vm_stack_copy_call_frame(*call, passed_args, additional_args);
|
||||
}
|
||||
}
|
||||
/* }}} */
|
||||
|
||||
static zend_always_inline zend_generator *zend_get_running_generator(EXECUTE_DATA_D) /* {{{ */
|
||||
{
|
||||
/* The generator object is stored in EX(return_value) */
|
||||
@ -3786,12 +3776,16 @@ static void cleanup_unfinished_calls(zend_execute_data *execute_data, uint32_t o
|
||||
case ZEND_SEND_VAR_NO_REF_EX:
|
||||
case ZEND_SEND_USER:
|
||||
if (level == 0) {
|
||||
ZEND_CALL_NUM_ARGS(call) = opline->op2.num;
|
||||
/* For named args, the number of arguments is up to date. */
|
||||
if (opline->op2_type != IS_CONST) {
|
||||
ZEND_CALL_NUM_ARGS(call) = opline->op2.num;
|
||||
}
|
||||
do_exit = 1;
|
||||
}
|
||||
break;
|
||||
case ZEND_SEND_ARRAY:
|
||||
case ZEND_SEND_UNPACK:
|
||||
case ZEND_CHECK_UNDEF_ARGS:
|
||||
if (level == 0) {
|
||||
do_exit = 1;
|
||||
}
|
||||
@ -3836,6 +3830,9 @@ static void cleanup_unfinished_calls(zend_execute_data *execute_data, uint32_t o
|
||||
if (ZEND_CALL_INFO(call) & ZEND_CALL_RELEASE_THIS) {
|
||||
OBJ_RELEASE(Z_OBJ(call->This));
|
||||
}
|
||||
if (ZEND_CALL_INFO(call) & ZEND_CALL_HAS_EXTRA_NAMED_PARAMS) {
|
||||
zend_free_extra_named_params(call->extra_named_params);
|
||||
}
|
||||
if (call->func->common.fn_flags & ZEND_ACC_CLOSURE) {
|
||||
zend_object_release(ZEND_CLOSURE_OBJECT(call->func));
|
||||
} else if (call->func->common.fn_flags & ZEND_ACC_CALL_VIA_TRAMPOLINE) {
|
||||
@ -4336,6 +4333,226 @@ static zend_never_inline int ZEND_FASTCALL zend_quick_check_constant(
|
||||
return _zend_quick_get_constant(key, 0, 1 OPLINE_CC EXECUTE_DATA_CC);
|
||||
} /* }}} */
|
||||
|
||||
static zend_always_inline uint32_t zend_get_arg_offset_by_name(
|
||||
zend_function *fbc, zend_string *arg_name, void **cache_slot) {
|
||||
if (EXPECTED(*cache_slot == fbc)) {
|
||||
return *(uintptr_t *)(cache_slot + 1);
|
||||
}
|
||||
|
||||
// TODO: Use a hash table?
|
||||
uint32_t num_args = fbc->common.num_args;
|
||||
if (EXPECTED(fbc->type == ZEND_USER_FUNCTION)
|
||||
|| EXPECTED(fbc->common.fn_flags & ZEND_ACC_USER_ARG_INFO)) {
|
||||
for (uint32_t i = 0; i < num_args; i++) {
|
||||
zend_arg_info *arg_info = &fbc->op_array.arg_info[i];
|
||||
if (zend_string_equals(arg_name, arg_info->name)) {
|
||||
*cache_slot = fbc;
|
||||
*(uintptr_t *)(cache_slot + 1) = i;
|
||||
return i;
|
||||
}
|
||||
}
|
||||
} else {
|
||||
for (uint32_t i = 0; i < num_args; i++) {
|
||||
zend_internal_arg_info *arg_info = &fbc->internal_function.arg_info[i];
|
||||
size_t len = strlen(arg_info->name);
|
||||
if (len == ZSTR_LEN(arg_name) && !memcmp(arg_info->name, ZSTR_VAL(arg_name), len)) {
|
||||
*cache_slot = fbc;
|
||||
*(uintptr_t *)(cache_slot + 1) = i;
|
||||
return i;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (fbc->common.fn_flags & ZEND_ACC_VARIADIC) {
|
||||
*cache_slot = fbc;
|
||||
*(uintptr_t *)(cache_slot + 1) = fbc->common.num_args;
|
||||
return fbc->common.num_args;
|
||||
}
|
||||
|
||||
return (uint32_t) -1;
|
||||
}
|
||||
|
||||
zval * ZEND_FASTCALL zend_handle_named_arg(
|
||||
zend_execute_data **call_ptr, zend_string *arg_name,
|
||||
uint32_t *arg_num_ptr, void **cache_slot) {
|
||||
zend_execute_data *call = *call_ptr;
|
||||
zend_function *fbc = call->func;
|
||||
uint32_t arg_offset = zend_get_arg_offset_by_name(fbc, arg_name, cache_slot);
|
||||
if (UNEXPECTED(arg_offset == (uint32_t) -1)) {
|
||||
zend_throw_error(NULL, "Unknown named parameter $%s", ZSTR_VAL(arg_name));
|
||||
return NULL;
|
||||
}
|
||||
|
||||
zval *arg;
|
||||
if (UNEXPECTED(arg_offset == fbc->common.num_args)) {
|
||||
/* Unknown named parameter that will be collected into a variadic. */
|
||||
if (!(ZEND_CALL_INFO(call) & ZEND_CALL_HAS_EXTRA_NAMED_PARAMS)) {
|
||||
ZEND_ADD_CALL_FLAG(call, ZEND_CALL_HAS_EXTRA_NAMED_PARAMS);
|
||||
call->extra_named_params = zend_new_array(0);
|
||||
}
|
||||
|
||||
arg = zend_hash_add_empty_element(call->extra_named_params, arg_name);
|
||||
if (!arg) {
|
||||
zend_throw_error(NULL, "Named parameter $%s overwrites previous argument",
|
||||
ZSTR_VAL(arg_name));
|
||||
return NULL;
|
||||
}
|
||||
*arg_num_ptr = arg_offset + 1;
|
||||
return arg;
|
||||
}
|
||||
|
||||
uint32_t current_num_args = ZEND_CALL_NUM_ARGS(call);
|
||||
// TODO: We may wish to optimize the arg_offset == current_num_args case,
|
||||
// which is probably common (if the named parameters are in order of declaration).
|
||||
if (arg_offset >= current_num_args) {
|
||||
uint32_t new_num_args = arg_offset + 1;
|
||||
ZEND_CALL_NUM_ARGS(call) = new_num_args;
|
||||
|
||||
uint32_t num_extra_args = new_num_args - current_num_args;
|
||||
zend_vm_stack_extend_call_frame(call_ptr, current_num_args, num_extra_args);
|
||||
call = *call_ptr;
|
||||
|
||||
arg = ZEND_CALL_VAR_NUM(call, arg_offset);
|
||||
if (num_extra_args > 1) {
|
||||
zval *zv = ZEND_CALL_VAR_NUM(call, current_num_args);
|
||||
do {
|
||||
ZVAL_UNDEF(zv);
|
||||
zv++;
|
||||
} while (zv != arg);
|
||||
ZEND_ADD_CALL_FLAG(call, ZEND_CALL_MAY_HAVE_UNDEF);
|
||||
}
|
||||
} else {
|
||||
arg = ZEND_CALL_VAR_NUM(call, arg_offset);
|
||||
if (UNEXPECTED(!Z_ISUNDEF_P(arg))) {
|
||||
zend_throw_error(NULL, "Named parameter $%s overwrites previous argument",
|
||||
ZSTR_VAL(arg_name));
|
||||
return NULL;
|
||||
}
|
||||
}
|
||||
|
||||
*arg_num_ptr = arg_offset + 1;
|
||||
return arg;
|
||||
}
|
||||
|
||||
static void start_fake_frame(zend_execute_data *call, const zend_op *opline) {
|
||||
zend_execute_data *prev_execute_data = EG(current_execute_data);
|
||||
call->prev_execute_data = prev_execute_data;
|
||||
call->opline = opline;
|
||||
EG(current_execute_data) = call;
|
||||
}
|
||||
|
||||
static void end_fake_frame(zend_execute_data *call) {
|
||||
zend_execute_data *prev_execute_data = call->prev_execute_data;
|
||||
EG(current_execute_data) = prev_execute_data;
|
||||
call->prev_execute_data = NULL;
|
||||
if (UNEXPECTED(EG(exception)) && ZEND_USER_CODE(prev_execute_data->func->common.type)) {
|
||||
zend_rethrow_exception(prev_execute_data);
|
||||
}
|
||||
}
|
||||
|
||||
ZEND_API int ZEND_FASTCALL zend_handle_undef_args(zend_execute_data *call) {
|
||||
zend_function *fbc = call->func;
|
||||
if (fbc->type == ZEND_USER_FUNCTION) {
|
||||
uint32_t num_args = ZEND_CALL_NUM_ARGS(call);
|
||||
for (uint32_t i = 0; i < num_args; i++) {
|
||||
zval *arg = ZEND_CALL_VAR_NUM(call, i);
|
||||
if (!Z_ISUNDEF_P(arg)) {
|
||||
continue;
|
||||
}
|
||||
|
||||
zend_op_array *op_array = &fbc->op_array;
|
||||
zend_op *opline = &op_array->opcodes[i];
|
||||
if (EXPECTED(opline->opcode == ZEND_RECV_INIT)) {
|
||||
zval *default_value = RT_CONSTANT(opline, opline->op2);
|
||||
if (Z_OPT_TYPE_P(default_value) == IS_CONSTANT_AST) {
|
||||
void *run_time_cache = RUN_TIME_CACHE(op_array);
|
||||
zval *cache_val =
|
||||
(zval *) ((char *) run_time_cache + Z_CACHE_SLOT_P(default_value));
|
||||
|
||||
if (Z_TYPE_P(cache_val) != IS_UNDEF) {
|
||||
/* We keep in cache only not refcounted values */
|
||||
ZVAL_COPY_VALUE(arg, cache_val);
|
||||
} else {
|
||||
/* Update constant inside a temporary zval, to make sure the CONSTANT_AST
|
||||
* value is not accessible through back traces. */
|
||||
zval tmp;
|
||||
ZVAL_COPY(&tmp, default_value);
|
||||
start_fake_frame(call, opline);
|
||||
int ret = zval_update_constant_ex(&tmp, fbc->op_array.scope);
|
||||
end_fake_frame(call);
|
||||
if (UNEXPECTED(ret == FAILURE)) {
|
||||
zval_ptr_dtor_nogc(&tmp);
|
||||
return FAILURE;
|
||||
}
|
||||
ZVAL_COPY_VALUE(arg, &tmp);
|
||||
if (!Z_REFCOUNTED(tmp)) {
|
||||
ZVAL_COPY_VALUE(cache_val, &tmp);
|
||||
}
|
||||
}
|
||||
} else {
|
||||
ZVAL_COPY(arg, default_value);
|
||||
}
|
||||
} else {
|
||||
ZEND_ASSERT(opline->opcode == ZEND_RECV);
|
||||
start_fake_frame(call, opline);
|
||||
zend_argument_error(zend_ce_argument_count_error, i + 1, "not passed");
|
||||
end_fake_frame(call);
|
||||
}
|
||||
}
|
||||
|
||||
return SUCCESS;
|
||||
} else {
|
||||
if (fbc->common.fn_flags & ZEND_ACC_USER_ARG_INFO) {
|
||||
/* Magic function, let it deal with it. */
|
||||
return SUCCESS;
|
||||
}
|
||||
|
||||
uint32_t num_args = ZEND_CALL_NUM_ARGS(call);
|
||||
for (uint32_t i = 0; i < num_args; i++) {
|
||||
zval *arg = ZEND_CALL_VAR_NUM(call, i);
|
||||
if (!Z_ISUNDEF_P(arg)) {
|
||||
continue;
|
||||
}
|
||||
|
||||
zend_internal_arg_info *arg_info = &fbc->internal_function.arg_info[i];
|
||||
if (i < fbc->common.required_num_args) {
|
||||
start_fake_frame(call, NULL);
|
||||
zend_argument_error(zend_ce_argument_count_error, i + 1, "not passed");
|
||||
end_fake_frame(call);
|
||||
return FAILURE;
|
||||
}
|
||||
|
||||
zval default_value;
|
||||
if (zend_get_default_from_internal_arg_info(&default_value, arg_info) == FAILURE) {
|
||||
start_fake_frame(call, NULL);
|
||||
zend_argument_error(zend_ce_argument_count_error, i + 1,
|
||||
"must be passed explicitly, because the default value is not known");
|
||||
end_fake_frame(call);
|
||||
return FAILURE;
|
||||
}
|
||||
|
||||
if (Z_TYPE(default_value) == IS_CONSTANT_AST) {
|
||||
start_fake_frame(call, NULL);
|
||||
int ret = zval_update_constant_ex(&default_value, fbc->common.scope);
|
||||
end_fake_frame(call);
|
||||
if (ret == FAILURE) {
|
||||
return FAILURE;
|
||||
}
|
||||
}
|
||||
|
||||
ZVAL_COPY_VALUE(arg, &default_value);
|
||||
}
|
||||
}
|
||||
|
||||
return SUCCESS;
|
||||
}
|
||||
|
||||
ZEND_API void ZEND_FASTCALL zend_free_extra_named_params(zend_array *extra_named_params)
|
||||
{
|
||||
/* Extra named params may be shared. */
|
||||
zend_array_release(extra_named_params);
|
||||
}
|
||||
|
||||
#if defined(ZEND_VM_IP_GLOBAL_REG) && ((ZEND_VM_KIND == ZEND_VM_KIND_CALL) || (ZEND_VM_KIND == ZEND_VM_KIND_HYBRID))
|
||||
/* Special versions of functions that sets EX(opline) before calling zend_vm_stack_extend() */
|
||||
static zend_always_inline zend_execute_data *_zend_vm_stack_push_call_frame_ex(uint32_t used_stack, uint32_t call_info, zend_function *func, uint32_t num_args, void *object_or_called_scope) /* {{{ */
|
||||
|
@ -291,6 +291,21 @@ static zend_always_inline void zend_vm_stack_free_call_frame(zend_execute_data *
|
||||
zend_vm_stack_free_call_frame_ex(ZEND_CALL_INFO(call), call);
|
||||
}
|
||||
|
||||
zend_execute_data *zend_vm_stack_copy_call_frame(
|
||||
zend_execute_data *call, uint32_t passed_args, uint32_t additional_args);
|
||||
|
||||
static zend_always_inline void zend_vm_stack_extend_call_frame(
|
||||
zend_execute_data **call, uint32_t passed_args, uint32_t additional_args)
|
||||
{
|
||||
if (EXPECTED((uint32_t)(EG(vm_stack_end) - EG(vm_stack_top)) > additional_args)) {
|
||||
EG(vm_stack_top) += additional_args;
|
||||
} else {
|
||||
*call = zend_vm_stack_copy_call_frame(*call, passed_args, additional_args);
|
||||
}
|
||||
}
|
||||
|
||||
ZEND_API void ZEND_FASTCALL zend_free_extra_named_params(zend_array *extra_named_params);
|
||||
|
||||
/* services */
|
||||
ZEND_API const char *get_active_class_name(const char **space);
|
||||
ZEND_API const char *get_active_function_name(void);
|
||||
@ -333,6 +348,11 @@ ZEND_API void zend_clean_and_cache_symbol_table(zend_array *symbol_table);
|
||||
ZEND_API void zend_free_compiled_variables(zend_execute_data *execute_data);
|
||||
ZEND_API void zend_cleanup_unfinished_execution(zend_execute_data *execute_data, uint32_t op_num, uint32_t catch_op_num);
|
||||
|
||||
zval * ZEND_FASTCALL zend_handle_named_arg(
|
||||
zend_execute_data **call_ptr, zend_string *arg_name,
|
||||
uint32_t *arg_num_ptr, void **cache_slot);
|
||||
ZEND_API int ZEND_FASTCALL zend_handle_undef_args(zend_execute_data *call);
|
||||
|
||||
#define CACHE_ADDR(num) \
|
||||
((void**)((char*)EX(run_time_cache) + (num)))
|
||||
|
||||
|
@ -619,7 +619,7 @@ ZEND_API int zval_update_constant(zval *pp) /* {{{ */
|
||||
}
|
||||
/* }}} */
|
||||
|
||||
int _call_user_function_ex(zval *object, zval *function_name, zval *retval_ptr, uint32_t param_count, zval params[]) /* {{{ */
|
||||
int _call_user_function_impl(zval *object, zval *function_name, zval *retval_ptr, uint32_t param_count, zval params[], HashTable *named_params) /* {{{ */
|
||||
{
|
||||
zend_fcall_info fci;
|
||||
|
||||
@ -629,6 +629,7 @@ int _call_user_function_ex(zval *object, zval *function_name, zval *retval_ptr,
|
||||
fci.retval = retval_ptr;
|
||||
fci.param_count = param_count;
|
||||
fci.params = params;
|
||||
fci.named_params = named_params;
|
||||
|
||||
return zend_call_function(&fci, NULL);
|
||||
}
|
||||
@ -731,8 +732,14 @@ int zend_call_function(zend_fcall_info *fci, zend_fcall_info_cache *fci_cache) /
|
||||
}
|
||||
|
||||
for (i=0; i<fci->param_count; i++) {
|
||||
zval *param;
|
||||
zval *param = ZEND_CALL_ARG(call, i+1);
|
||||
zval *arg = &fci->params[i];
|
||||
if (UNEXPECTED(Z_ISUNDEF_P(arg))) {
|
||||
/* Allow forwarding undef slots. This is only used by Closure::__invoke(). */
|
||||
ZVAL_UNDEF(param);
|
||||
ZEND_ADD_CALL_FLAG(call, ZEND_CALL_MAY_HAVE_UNDEF);
|
||||
continue;
|
||||
}
|
||||
|
||||
if (ARG_SHOULD_BE_SENT_BY_REF(func, i + 1)) {
|
||||
if (UNEXPECTED(!Z_ISREF_P(arg))) {
|
||||
@ -742,6 +749,7 @@ int zend_call_function(zend_fcall_info *fci, zend_fcall_info_cache *fci_cache) /
|
||||
zend_param_must_be_ref(func, i + 1);
|
||||
if (UNEXPECTED(EG(exception))) {
|
||||
ZEND_CALL_NUM_ARGS(call) = i;
|
||||
cleanup_args:
|
||||
zend_vm_stack_free_args(call);
|
||||
zend_vm_stack_free_call_frame(call);
|
||||
if (EG(current_execute_data) == &dummy_execute_data) {
|
||||
@ -759,10 +767,61 @@ int zend_call_function(zend_fcall_info *fci, zend_fcall_info_cache *fci_cache) /
|
||||
}
|
||||
}
|
||||
|
||||
param = ZEND_CALL_ARG(call, i+1);
|
||||
ZVAL_COPY(param, arg);
|
||||
}
|
||||
|
||||
if (fci->named_params) {
|
||||
zend_string *name;
|
||||
zval *arg;
|
||||
uint32_t arg_num = ZEND_CALL_NUM_ARGS(call) + 1;
|
||||
zend_bool have_named_params = 0;
|
||||
ZEND_HASH_FOREACH_STR_KEY_VAL(fci->named_params, name, arg) {
|
||||
zval *target;
|
||||
if (name) {
|
||||
void *cache_slot[2] = {NULL, NULL};
|
||||
have_named_params = 1;
|
||||
target = zend_handle_named_arg(&call, name, &arg_num, cache_slot);
|
||||
if (!target) {
|
||||
goto cleanup_args;
|
||||
}
|
||||
} else {
|
||||
if (have_named_params) {
|
||||
zend_throw_error(NULL,
|
||||
"Cannot use positional argument after named argument");
|
||||
goto cleanup_args;
|
||||
}
|
||||
|
||||
zend_vm_stack_extend_call_frame(&call, arg_num - 1, 1);
|
||||
target = ZEND_CALL_ARG(call, arg_num);
|
||||
}
|
||||
|
||||
if (ARG_SHOULD_BE_SENT_BY_REF(func, arg_num)) {
|
||||
if (UNEXPECTED(!Z_ISREF_P(arg))) {
|
||||
if (!ARG_MAY_BE_SENT_BY_REF(func, arg_num)) {
|
||||
/* By-value send is not allowed -- emit a warning,
|
||||
* but still perform the call with a by-value send. */
|
||||
zend_param_must_be_ref(func, arg_num);
|
||||
if (UNEXPECTED(EG(exception))) {
|
||||
goto cleanup_args;
|
||||
}
|
||||
}
|
||||
}
|
||||
} else {
|
||||
if (Z_ISREF_P(arg) &&
|
||||
!(func->common.fn_flags & ZEND_ACC_CALL_VIA_TRAMPOLINE)) {
|
||||
/* don't separate references for __call */
|
||||
arg = Z_REFVAL_P(arg);
|
||||
}
|
||||
}
|
||||
|
||||
ZVAL_COPY(target, arg);
|
||||
if (!name) {
|
||||
ZEND_CALL_NUM_ARGS(call)++;
|
||||
arg_num++;
|
||||
}
|
||||
} ZEND_HASH_FOREACH_END();
|
||||
}
|
||||
|
||||
if (UNEXPECTED(func->op_array.fn_flags & ZEND_ACC_CLOSURE)) {
|
||||
uint32_t call_info;
|
||||
|
||||
@ -774,6 +833,17 @@ int zend_call_function(zend_fcall_info *fci, zend_fcall_info_cache *fci_cache) /
|
||||
ZEND_ADD_CALL_FLAG(call, call_info);
|
||||
}
|
||||
|
||||
if (UNEXPECTED(ZEND_CALL_INFO(call) & ZEND_CALL_MAY_HAVE_UNDEF)) {
|
||||
if (zend_handle_undef_args(call) == FAILURE) {
|
||||
zend_vm_stack_free_args(call);
|
||||
zend_vm_stack_free_call_frame(call);
|
||||
if (EG(current_execute_data) == &dummy_execute_data) {
|
||||
EG(current_execute_data) = dummy_execute_data.prev_execute_data;
|
||||
}
|
||||
return SUCCESS;
|
||||
}
|
||||
}
|
||||
|
||||
orig_fake_scope = EG(fake_scope);
|
||||
EG(fake_scope) = NULL;
|
||||
if (func->type == ZEND_USER_FUNCTION) {
|
||||
@ -804,6 +874,9 @@ int zend_call_function(zend_fcall_info *fci, zend_fcall_info_cache *fci_cache) /
|
||||
}
|
||||
EG(current_execute_data) = call->prev_execute_data;
|
||||
zend_vm_stack_free_args(call);
|
||||
if (UNEXPECTED(ZEND_CALL_INFO(call) & ZEND_CALL_HAS_EXTRA_NAMED_PARAMS)) {
|
||||
zend_array_release(call->extra_named_params);
|
||||
}
|
||||
|
||||
if (EG(exception)) {
|
||||
zval_ptr_dtor(fci->retval);
|
||||
@ -849,7 +922,7 @@ int zend_call_function(zend_fcall_info *fci, zend_fcall_info_cache *fci_cache) /
|
||||
|
||||
ZEND_API void zend_call_known_function(
|
||||
zend_function *fn, zend_object *object, zend_class_entry *called_scope, zval *retval_ptr,
|
||||
uint32_t param_count, zval *params)
|
||||
uint32_t param_count, zval *params, HashTable *named_params)
|
||||
{
|
||||
zval retval;
|
||||
zend_fcall_info fci;
|
||||
@ -862,6 +935,7 @@ ZEND_API void zend_call_known_function(
|
||||
fci.retval = retval_ptr ? retval_ptr : &retval;
|
||||
fci.param_count = param_count;
|
||||
fci.params = params;
|
||||
fci.named_params = named_params;
|
||||
ZVAL_UNDEF(&fci.function_name); /* Unused */
|
||||
|
||||
fcic.function_handler = fn;
|
||||
|
@ -127,6 +127,9 @@ ZEND_API void zend_generator_close(zend_generator *generator, zend_bool finished
|
||||
}
|
||||
/* always free the CV's, in the symtable are only not-free'd IS_INDIRECT's */
|
||||
zend_free_compiled_variables(execute_data);
|
||||
if (EX_CALL_INFO() & ZEND_CALL_HAS_EXTRA_NAMED_PARAMS) {
|
||||
zend_free_extra_named_params(execute_data->extra_named_params);
|
||||
}
|
||||
|
||||
if (EX_CALL_INFO() & ZEND_CALL_RELEASE_THIS) {
|
||||
OBJ_RELEASE(Z_OBJ(execute_data->This));
|
||||
@ -312,6 +315,11 @@ static HashTable *zend_generator_get_gc(zend_object *object, zval **table, int *
|
||||
if (EX_CALL_INFO() & ZEND_CALL_CLOSURE) {
|
||||
zend_get_gc_buffer_add_obj(gc_buffer, ZEND_CLOSURE_OBJECT(EX(func)));
|
||||
}
|
||||
if (EX_CALL_INFO() & ZEND_CALL_HAS_EXTRA_NAMED_PARAMS) {
|
||||
zval extra_named_params;
|
||||
ZVAL_ARR(&extra_named_params, EX(extra_named_params));
|
||||
zend_get_gc_buffer_add_zval(gc_buffer, &extra_named_params);
|
||||
}
|
||||
|
||||
if (execute_data->opline != op_array->opcodes) {
|
||||
uint32_t i, op_num = execute_data->opline - op_array->opcodes - 1;
|
||||
|
@ -79,7 +79,7 @@ ZEND_API zval* zend_call_method(zend_object *object, zend_class_entry *obj_ce, z
|
||||
called_scope = obj_ce;
|
||||
}
|
||||
|
||||
zend_call_known_function(fn, object, called_scope, retval_ptr, param_count, params);
|
||||
zend_call_known_function(fn, object, called_scope, retval_ptr, param_count, params, NULL);
|
||||
return retval_ptr;
|
||||
}
|
||||
/* }}} */
|
||||
|
@ -806,7 +806,9 @@ non_empty_argument_list:
|
||||
;
|
||||
|
||||
argument:
|
||||
expr { $$ = $1; }
|
||||
expr { $$ = $1; }
|
||||
| identifier ':' expr
|
||||
{ $$ = zend_ast_create(ZEND_AST_NAMED_ARG, $1, $3); }
|
||||
| T_ELLIPSIS expr { $$ = zend_ast_create(ZEND_AST_UNPACK, $2); }
|
||||
;
|
||||
|
||||
|
@ -1110,6 +1110,7 @@ ZEND_API zend_function *zend_get_call_trampoline_func(zend_class_entry *ce, zend
|
||||
* The low bit must be zero, to not be interpreted as a MAP_PTR offset.
|
||||
*/
|
||||
static const void *dummy = (void*)(intptr_t)2;
|
||||
static const zend_arg_info arg_info[1] = {{0}};
|
||||
|
||||
ZEND_ASSERT(fbc);
|
||||
|
||||
@ -1123,7 +1124,7 @@ ZEND_API zend_function *zend_get_call_trampoline_func(zend_class_entry *ce, zend
|
||||
func->arg_flags[0] = 0;
|
||||
func->arg_flags[1] = 0;
|
||||
func->arg_flags[2] = 0;
|
||||
func->fn_flags = ZEND_ACC_CALL_VIA_TRAMPOLINE | ZEND_ACC_PUBLIC;
|
||||
func->fn_flags = ZEND_ACC_CALL_VIA_TRAMPOLINE | ZEND_ACC_PUBLIC | ZEND_ACC_VARIADIC;
|
||||
if (is_static) {
|
||||
func->fn_flags |= ZEND_ACC_STATIC;
|
||||
}
|
||||
@ -1147,7 +1148,7 @@ ZEND_API zend_function *zend_get_call_trampoline_func(zend_class_entry *ce, zend
|
||||
func->prototype = NULL;
|
||||
func->num_args = 0;
|
||||
func->required_num_args = 0;
|
||||
func->arg_info = 0;
|
||||
func->arg_info = (zend_arg_info *) arg_info;
|
||||
|
||||
return (zend_function*)func;
|
||||
}
|
||||
|
@ -2761,7 +2761,7 @@ ZEND_VM_HOT_HELPER(zend_leave_helper, ANY, ANY)
|
||||
uint32_t call_info = EX_CALL_INFO();
|
||||
SAVE_OPLINE();
|
||||
|
||||
if (EXPECTED((call_info & (ZEND_CALL_CODE|ZEND_CALL_TOP|ZEND_CALL_HAS_SYMBOL_TABLE|ZEND_CALL_FREE_EXTRA_ARGS|ZEND_CALL_ALLOCATED)) == 0)) {
|
||||
if (EXPECTED((call_info & (ZEND_CALL_CODE|ZEND_CALL_TOP|ZEND_CALL_HAS_SYMBOL_TABLE|ZEND_CALL_FREE_EXTRA_ARGS|ZEND_CALL_ALLOCATED|ZEND_CALL_HAS_EXTRA_NAMED_PARAMS)) == 0)) {
|
||||
EG(current_execute_data) = EX(prev_execute_data);
|
||||
i_free_compiled_variables(execute_data);
|
||||
|
||||
@ -2794,6 +2794,10 @@ ZEND_VM_HOT_HELPER(zend_leave_helper, ANY, ANY)
|
||||
zend_clean_and_cache_symbol_table(EX(symbol_table));
|
||||
}
|
||||
|
||||
if (UNEXPECTED(call_info & ZEND_CALL_HAS_EXTRA_NAMED_PARAMS)) {
|
||||
zend_free_extra_named_params(EX(extra_named_params));
|
||||
}
|
||||
|
||||
/* Free extra args before releasing the closure,
|
||||
* as that may free the op_array. */
|
||||
zend_vm_stack_free_extra_args_ex(call_info, execute_data);
|
||||
@ -2841,11 +2845,14 @@ ZEND_VM_HOT_HELPER(zend_leave_helper, ANY, ANY)
|
||||
#ifdef ZEND_PREFER_RELOAD
|
||||
call_info = EX_CALL_INFO();
|
||||
#endif
|
||||
if (UNEXPECTED(call_info & (ZEND_CALL_HAS_SYMBOL_TABLE|ZEND_CALL_FREE_EXTRA_ARGS))) {
|
||||
if (UNEXPECTED(call_info & (ZEND_CALL_HAS_SYMBOL_TABLE|ZEND_CALL_FREE_EXTRA_ARGS|ZEND_CALL_HAS_EXTRA_NAMED_PARAMS))) {
|
||||
if (UNEXPECTED(call_info & ZEND_CALL_HAS_SYMBOL_TABLE)) {
|
||||
zend_clean_and_cache_symbol_table(EX(symbol_table));
|
||||
}
|
||||
zend_vm_stack_free_extra_args_ex(call_info, execute_data);
|
||||
if (UNEXPECTED(call_info & ZEND_CALL_HAS_EXTRA_NAMED_PARAMS)) {
|
||||
zend_free_extra_named_params(EX(extra_named_params));
|
||||
}
|
||||
}
|
||||
if (UNEXPECTED(call_info & ZEND_CALL_CLOSURE)) {
|
||||
OBJ_RELEASE(ZEND_CLOSURE_OBJECT(EX(func)));
|
||||
@ -3896,7 +3903,16 @@ ZEND_VM_HOT_HANDLER(129, ZEND_DO_ICALL, ANY, ANY, SPEC(RETVAL))
|
||||
|
||||
EG(current_execute_data) = execute_data;
|
||||
zend_vm_stack_free_args(call);
|
||||
zend_vm_stack_free_call_frame(call);
|
||||
|
||||
uint32_t call_info = ZEND_CALL_INFO(call);
|
||||
if (UNEXPECTED(call_info & (ZEND_CALL_HAS_EXTRA_NAMED_PARAMS|ZEND_CALL_ALLOCATED))) {
|
||||
if (call_info & ZEND_CALL_HAS_EXTRA_NAMED_PARAMS) {
|
||||
zend_free_extra_named_params(call->extra_named_params);
|
||||
}
|
||||
zend_vm_stack_free_call_frame_ex(call_info, call);
|
||||
} else {
|
||||
EG(vm_stack_top) = (zval*)call;
|
||||
}
|
||||
|
||||
if (!RETURN_VALUE_USED(opline)) {
|
||||
i_zval_ptr_dtor(ret);
|
||||
@ -4000,7 +4016,16 @@ ZEND_VM_HOT_HANDLER(131, ZEND_DO_FCALL_BY_NAME, ANY, ANY, SPEC(RETVAL))
|
||||
|
||||
ZEND_VM_C_LABEL(fcall_by_name_end):
|
||||
zend_vm_stack_free_args(call);
|
||||
zend_vm_stack_free_call_frame(call);
|
||||
|
||||
uint32_t call_info = ZEND_CALL_INFO(call);
|
||||
if (UNEXPECTED(call_info & (ZEND_CALL_HAS_EXTRA_NAMED_PARAMS|ZEND_CALL_ALLOCATED))) {
|
||||
if (call_info & ZEND_CALL_HAS_EXTRA_NAMED_PARAMS) {
|
||||
zend_free_extra_named_params(call->extra_named_params);
|
||||
}
|
||||
zend_vm_stack_free_call_frame_ex(call_info, call);
|
||||
} else {
|
||||
EG(vm_stack_top) = (zval*)call;
|
||||
}
|
||||
|
||||
if (!RETURN_VALUE_USED(opline)) {
|
||||
i_zval_ptr_dtor(ret);
|
||||
@ -4094,6 +4119,10 @@ ZEND_VM_HOT_HANDLER(60, ZEND_DO_FCALL, ANY, ANY, SPEC(RETVAL))
|
||||
|
||||
ZEND_VM_C_LABEL(fcall_end):
|
||||
zend_vm_stack_free_args(call);
|
||||
if (UNEXPECTED(ZEND_CALL_INFO(call) & ZEND_CALL_HAS_EXTRA_NAMED_PARAMS)) {
|
||||
zend_free_extra_named_params(call->extra_named_params);
|
||||
}
|
||||
|
||||
if (!RETURN_VALUE_USED(opline)) {
|
||||
i_zval_ptr_dtor(ret);
|
||||
}
|
||||
@ -4517,13 +4546,24 @@ ZEND_VM_HANDLER(107, ZEND_CATCH, CONST, JMP_ADDR, LAST_CATCH|CACHE_SLOT)
|
||||
ZEND_VM_NEXT_OPCODE_CHECK_EXCEPTION();
|
||||
}
|
||||
|
||||
ZEND_VM_HOT_HANDLER(65, ZEND_SEND_VAL, CONST|TMPVAR, NUM)
|
||||
ZEND_VM_HOT_HANDLER(65, ZEND_SEND_VAL, CONST|TMPVAR, CONST|UNUSED|NUM)
|
||||
{
|
||||
USE_OPLINE
|
||||
zval *value, *arg;
|
||||
|
||||
if (OP2_TYPE == IS_CONST) {
|
||||
SAVE_OPLINE();
|
||||
zend_string *arg_name = Z_STR_P(RT_CONSTANT(opline, opline->op2));
|
||||
uint32_t arg_num;
|
||||
arg = zend_handle_named_arg(&EX(call), arg_name, &arg_num, CACHE_ADDR(opline->result.num));
|
||||
if (UNEXPECTED(!arg)) {
|
||||
HANDLE_EXCEPTION();
|
||||
}
|
||||
} else {
|
||||
arg = ZEND_CALL_VAR(EX(call), opline->result.var);
|
||||
}
|
||||
|
||||
value = GET_OP1_ZVAL_PTR(BP_VAR_R);
|
||||
arg = ZEND_CALL_VAR(EX(call), opline->result.var);
|
||||
ZVAL_COPY_VALUE(arg, value);
|
||||
if (OP1_TYPE == IS_CONST) {
|
||||
if (UNEXPECTED(Z_OPT_REFCOUNTED_P(arg))) {
|
||||
@ -4547,11 +4587,23 @@ ZEND_VM_COLD_HELPER(zend_cannot_pass_by_ref_helper, ANY, ANY)
|
||||
HANDLE_EXCEPTION();
|
||||
}
|
||||
|
||||
ZEND_VM_HOT_SEND_HANDLER(116, ZEND_SEND_VAL_EX, CONST|TMP, NUM, SPEC(QUICK_ARG))
|
||||
ZEND_VM_HOT_SEND_HANDLER(116, ZEND_SEND_VAL_EX, CONST|TMP, CONST|UNUSED|NUM, SPEC(QUICK_ARG))
|
||||
{
|
||||
USE_OPLINE
|
||||
zval *value, *arg;
|
||||
uint32_t arg_num = opline->op2.num;
|
||||
uint32_t arg_num;
|
||||
|
||||
if (OP2_TYPE == IS_CONST) {
|
||||
SAVE_OPLINE();
|
||||
zend_string *arg_name = Z_STR_P(RT_CONSTANT(opline, opline->op2));
|
||||
arg = zend_handle_named_arg(&EX(call), arg_name, &arg_num, CACHE_ADDR(opline->result.num));
|
||||
if (UNEXPECTED(!arg)) {
|
||||
HANDLE_EXCEPTION();
|
||||
}
|
||||
} else {
|
||||
arg = ZEND_CALL_VAR(EX(call), opline->result.var);
|
||||
arg_num = opline->op2.num;
|
||||
}
|
||||
|
||||
if (EXPECTED(arg_num <= MAX_ARG_FLAG_NUM)) {
|
||||
if (QUICK_ARG_MUST_BE_SENT_BY_REF(EX(call)->func, arg_num)) {
|
||||
@ -4562,7 +4614,6 @@ ZEND_VM_C_LABEL(send_val_by_ref):
|
||||
ZEND_VM_DISPATCH_TO_HELPER(zend_cannot_pass_by_ref_helper);
|
||||
}
|
||||
value = GET_OP1_ZVAL_PTR(BP_VAR_R);
|
||||
arg = ZEND_CALL_VAR(EX(call), opline->result.var);
|
||||
ZVAL_COPY_VALUE(arg, value);
|
||||
if (OP1_TYPE == IS_CONST) {
|
||||
if (UNEXPECTED(Z_OPT_REFCOUNTED_P(arg))) {
|
||||
@ -4572,22 +4623,31 @@ ZEND_VM_C_LABEL(send_val_by_ref):
|
||||
ZEND_VM_NEXT_OPCODE();
|
||||
}
|
||||
|
||||
ZEND_VM_HOT_HANDLER(117, ZEND_SEND_VAR, VAR|CV, NUM)
|
||||
ZEND_VM_HOT_HANDLER(117, ZEND_SEND_VAR, VAR|CV, CONST|UNUSED|NUM)
|
||||
{
|
||||
USE_OPLINE
|
||||
zval *varptr, *arg;
|
||||
|
||||
if (OP2_TYPE == IS_CONST) {
|
||||
SAVE_OPLINE();
|
||||
zend_string *arg_name = Z_STR_P(RT_CONSTANT(opline, opline->op2));
|
||||
uint32_t arg_num;
|
||||
arg = zend_handle_named_arg(&EX(call), arg_name, &arg_num, CACHE_ADDR(opline->result.num));
|
||||
if (UNEXPECTED(!arg)) {
|
||||
HANDLE_EXCEPTION();
|
||||
}
|
||||
} else {
|
||||
arg = ZEND_CALL_VAR(EX(call), opline->result.var);
|
||||
}
|
||||
|
||||
varptr = GET_OP1_ZVAL_PTR_UNDEF(BP_VAR_R);
|
||||
if (OP1_TYPE == IS_CV && UNEXPECTED(Z_TYPE_INFO_P(varptr) == IS_UNDEF)) {
|
||||
SAVE_OPLINE();
|
||||
ZVAL_UNDEFINED_OP1();
|
||||
arg = ZEND_CALL_VAR(EX(call), opline->result.var);
|
||||
ZVAL_NULL(arg);
|
||||
ZEND_VM_NEXT_OPCODE_CHECK_EXCEPTION();
|
||||
}
|
||||
|
||||
arg = ZEND_CALL_VAR(EX(call), opline->result.var);
|
||||
|
||||
if (OP1_TYPE == IS_CV) {
|
||||
ZVAL_COPY_DEREF(arg, varptr);
|
||||
} else /* if (OP1_TYPE == IS_VAR) */ {
|
||||
@ -4609,13 +4669,24 @@ ZEND_VM_HOT_HANDLER(117, ZEND_SEND_VAR, VAR|CV, NUM)
|
||||
ZEND_VM_NEXT_OPCODE();
|
||||
}
|
||||
|
||||
ZEND_VM_HANDLER(106, ZEND_SEND_VAR_NO_REF, VAR, NUM)
|
||||
ZEND_VM_HANDLER(106, ZEND_SEND_VAR_NO_REF, VAR, CONST|UNUSED|NUM)
|
||||
{
|
||||
USE_OPLINE
|
||||
zval *varptr, *arg;
|
||||
|
||||
if (OP2_TYPE == IS_CONST) {
|
||||
SAVE_OPLINE();
|
||||
zend_string *arg_name = Z_STR_P(RT_CONSTANT(opline, opline->op2));
|
||||
uint32_t arg_num;
|
||||
arg = zend_handle_named_arg(&EX(call), arg_name, &arg_num, CACHE_ADDR(opline->result.num));
|
||||
if (UNEXPECTED(!arg)) {
|
||||
HANDLE_EXCEPTION();
|
||||
}
|
||||
} else {
|
||||
arg = ZEND_CALL_VAR(EX(call), opline->result.var);
|
||||
}
|
||||
|
||||
varptr = GET_OP1_ZVAL_PTR(BP_VAR_R);
|
||||
arg = ZEND_CALL_VAR(EX(call), opline->result.var);
|
||||
ZVAL_COPY_VALUE(arg, varptr);
|
||||
|
||||
if (EXPECTED(Z_ISREF_P(varptr))) {
|
||||
@ -4628,19 +4699,30 @@ ZEND_VM_HANDLER(106, ZEND_SEND_VAR_NO_REF, VAR, NUM)
|
||||
ZEND_VM_NEXT_OPCODE_CHECK_EXCEPTION();
|
||||
}
|
||||
|
||||
ZEND_VM_HOT_SEND_HANDLER(50, ZEND_SEND_VAR_NO_REF_EX, VAR, NUM, SPEC(QUICK_ARG))
|
||||
ZEND_VM_HOT_SEND_HANDLER(50, ZEND_SEND_VAR_NO_REF_EX, VAR, CONST|UNUSED|NUM, SPEC(QUICK_ARG))
|
||||
{
|
||||
USE_OPLINE
|
||||
zval *varptr, *arg;
|
||||
uint32_t arg_num = opline->op2.num;
|
||||
uint32_t arg_num;
|
||||
|
||||
if (OP2_TYPE == IS_CONST) {
|
||||
SAVE_OPLINE();
|
||||
zend_string *arg_name = Z_STR_P(RT_CONSTANT(opline, opline->op2));
|
||||
arg = zend_handle_named_arg(&EX(call), arg_name, &arg_num, CACHE_ADDR(opline->result.num));
|
||||
if (UNEXPECTED(!arg)) {
|
||||
HANDLE_EXCEPTION();
|
||||
}
|
||||
} else {
|
||||
arg = ZEND_CALL_VAR(EX(call), opline->result.var);
|
||||
arg_num = opline->op2.num;
|
||||
}
|
||||
|
||||
if (EXPECTED(arg_num <= MAX_ARG_FLAG_NUM)) {
|
||||
if (!QUICK_ARG_SHOULD_BE_SENT_BY_REF(EX(call)->func, arg_num)) {
|
||||
ZEND_VM_DISPATCH_TO_HANDLER(ZEND_SEND_VAR);
|
||||
ZEND_VM_C_GOTO(send_var);
|
||||
}
|
||||
|
||||
varptr = GET_OP1_ZVAL_PTR(BP_VAR_R);
|
||||
arg = ZEND_CALL_VAR(EX(call), opline->result.var);
|
||||
ZVAL_COPY_VALUE(arg, varptr);
|
||||
|
||||
if (EXPECTED(Z_ISREF_P(varptr) ||
|
||||
@ -4649,11 +4731,10 @@ ZEND_VM_HOT_SEND_HANDLER(50, ZEND_SEND_VAR_NO_REF_EX, VAR, NUM, SPEC(QUICK_ARG))
|
||||
}
|
||||
} else {
|
||||
if (!ARG_SHOULD_BE_SENT_BY_REF(EX(call)->func, arg_num)) {
|
||||
ZEND_VM_DISPATCH_TO_HANDLER(ZEND_SEND_VAR);
|
||||
ZEND_VM_C_GOTO(send_var);
|
||||
}
|
||||
|
||||
varptr = GET_OP1_ZVAL_PTR(BP_VAR_R);
|
||||
arg = ZEND_CALL_VAR(EX(call), opline->result.var);
|
||||
ZVAL_COPY_VALUE(arg, varptr);
|
||||
|
||||
if (EXPECTED(Z_ISREF_P(varptr) ||
|
||||
@ -4666,17 +4747,43 @@ ZEND_VM_HOT_SEND_HANDLER(50, ZEND_SEND_VAR_NO_REF_EX, VAR, NUM, SPEC(QUICK_ARG))
|
||||
ZVAL_NEW_REF(arg, arg);
|
||||
zend_error(E_NOTICE, "Only variables should be passed by reference");
|
||||
ZEND_VM_NEXT_OPCODE_CHECK_EXCEPTION();
|
||||
|
||||
ZEND_VM_C_LABEL(send_var):
|
||||
varptr = GET_OP1_ZVAL_PTR(BP_VAR_R);
|
||||
if (UNEXPECTED(Z_ISREF_P(varptr))) {
|
||||
zend_refcounted *ref = Z_COUNTED_P(varptr);
|
||||
|
||||
varptr = Z_REFVAL_P(varptr);
|
||||
ZVAL_COPY_VALUE(arg, varptr);
|
||||
if (UNEXPECTED(GC_DELREF(ref) == 0)) {
|
||||
efree_size(ref, sizeof(zend_reference));
|
||||
} else if (Z_OPT_REFCOUNTED_P(arg)) {
|
||||
Z_ADDREF_P(arg);
|
||||
}
|
||||
} else {
|
||||
ZVAL_COPY_VALUE(arg, varptr);
|
||||
}
|
||||
ZEND_VM_NEXT_OPCODE();
|
||||
}
|
||||
|
||||
ZEND_VM_HANDLER(67, ZEND_SEND_REF, VAR|CV, NUM)
|
||||
ZEND_VM_HANDLER(67, ZEND_SEND_REF, VAR|CV, CONST|UNUSED|NUM)
|
||||
{
|
||||
USE_OPLINE
|
||||
zval *varptr, *arg;
|
||||
|
||||
SAVE_OPLINE();
|
||||
varptr = GET_OP1_ZVAL_PTR_PTR(BP_VAR_W);
|
||||
if (OP2_TYPE == IS_CONST) {
|
||||
zend_string *arg_name = Z_STR_P(RT_CONSTANT(opline, opline->op2));
|
||||
uint32_t arg_num;
|
||||
arg = zend_handle_named_arg(&EX(call), arg_name, &arg_num, CACHE_ADDR(opline->result.num));
|
||||
if (UNEXPECTED(!arg)) {
|
||||
HANDLE_EXCEPTION();
|
||||
}
|
||||
} else {
|
||||
arg = ZEND_CALL_VAR(EX(call), opline->result.var);
|
||||
}
|
||||
|
||||
arg = ZEND_CALL_VAR(EX(call), opline->result.var);
|
||||
varptr = GET_OP1_ZVAL_PTR_PTR(BP_VAR_W);
|
||||
if (Z_ISREF_P(varptr)) {
|
||||
Z_ADDREF_P(varptr);
|
||||
} else {
|
||||
@ -4688,11 +4795,23 @@ ZEND_VM_HANDLER(67, ZEND_SEND_REF, VAR|CV, NUM)
|
||||
ZEND_VM_NEXT_OPCODE();
|
||||
}
|
||||
|
||||
ZEND_VM_HOT_SEND_HANDLER(66, ZEND_SEND_VAR_EX, VAR|CV, NUM, SPEC(QUICK_ARG))
|
||||
ZEND_VM_HOT_SEND_HANDLER(66, ZEND_SEND_VAR_EX, VAR|CV, CONST|UNUSED|NUM, SPEC(QUICK_ARG))
|
||||
{
|
||||
USE_OPLINE
|
||||
zval *varptr, *arg;
|
||||
uint32_t arg_num = opline->op2.num;
|
||||
uint32_t arg_num;
|
||||
|
||||
if (OP2_TYPE == IS_CONST) {
|
||||
SAVE_OPLINE();
|
||||
zend_string *arg_name = Z_STR_P(RT_CONSTANT(opline, opline->op2));
|
||||
arg = zend_handle_named_arg(&EX(call), arg_name, &arg_num, CACHE_ADDR(opline->result.num));
|
||||
if (UNEXPECTED(!arg)) {
|
||||
HANDLE_EXCEPTION();
|
||||
}
|
||||
} else {
|
||||
arg = ZEND_CALL_VAR(EX(call), opline->result.var);
|
||||
arg_num = opline->op2.num;
|
||||
}
|
||||
|
||||
if (EXPECTED(arg_num <= MAX_ARG_FLAG_NUM)) {
|
||||
if (QUICK_ARG_SHOULD_BE_SENT_BY_REF(EX(call)->func, arg_num)) {
|
||||
@ -4700,7 +4819,16 @@ ZEND_VM_HOT_SEND_HANDLER(66, ZEND_SEND_VAR_EX, VAR|CV, NUM, SPEC(QUICK_ARG))
|
||||
}
|
||||
} else if (ARG_SHOULD_BE_SENT_BY_REF(EX(call)->func, arg_num)) {
|
||||
ZEND_VM_C_LABEL(send_var_by_ref):
|
||||
ZEND_VM_DISPATCH_TO_HANDLER(ZEND_SEND_REF);
|
||||
varptr = GET_OP1_ZVAL_PTR_PTR(BP_VAR_W);
|
||||
if (Z_ISREF_P(varptr)) {
|
||||
Z_ADDREF_P(varptr);
|
||||
} else {
|
||||
ZVAL_MAKE_REF_EX(varptr, 2);
|
||||
}
|
||||
ZVAL_REF(arg, Z_REF_P(varptr));
|
||||
|
||||
FREE_OP1_VAR_PTR();
|
||||
ZEND_VM_NEXT_OPCODE();
|
||||
}
|
||||
|
||||
varptr = GET_OP1_ZVAL_PTR_UNDEF(BP_VAR_R);
|
||||
@ -4712,8 +4840,6 @@ ZEND_VM_C_LABEL(send_var_by_ref):
|
||||
ZEND_VM_NEXT_OPCODE_CHECK_EXCEPTION();
|
||||
}
|
||||
|
||||
arg = ZEND_CALL_VAR(EX(call), opline->result.var);
|
||||
|
||||
if (OP1_TYPE == IS_CV) {
|
||||
ZVAL_COPY_DEREF(arg, varptr);
|
||||
} else /* if (OP1_TYPE == IS_VAR) */ {
|
||||
@ -4735,10 +4861,23 @@ ZEND_VM_C_LABEL(send_var_by_ref):
|
||||
ZEND_VM_NEXT_OPCODE();
|
||||
}
|
||||
|
||||
ZEND_VM_HOT_SEND_HANDLER(100, ZEND_CHECK_FUNC_ARG, UNUSED, NUM, SPEC(QUICK_ARG))
|
||||
ZEND_VM_HOT_SEND_HANDLER(100, ZEND_CHECK_FUNC_ARG, UNUSED, CONST|UNUSED|NUM, SPEC(QUICK_ARG))
|
||||
{
|
||||
USE_OPLINE
|
||||
uint32_t arg_num = opline->op2.num;
|
||||
uint32_t arg_num;
|
||||
|
||||
if (OP2_TYPE == IS_CONST) {
|
||||
zend_string *arg_name = Z_STR_P(RT_CONSTANT(opline, opline->op2));
|
||||
arg_num = zend_get_arg_offset_by_name(
|
||||
EX(call)->func, arg_name, CACHE_ADDR(opline->result.num)) + 1;
|
||||
if (UNEXPECTED(arg_num == 0)) {
|
||||
/* Treat this as a by-value argument, and throw an error during SEND. */
|
||||
ZEND_DEL_CALL_FLAG(EX(call), ZEND_CALL_SEND_ARG_BY_REF);
|
||||
ZEND_VM_NEXT_OPCODE();
|
||||
}
|
||||
} else {
|
||||
arg_num = opline->op2.num;
|
||||
}
|
||||
|
||||
if (EXPECTED(arg_num <= MAX_ARG_FLAG_NUM)) {
|
||||
if (QUICK_ARG_SHOULD_BE_SENT_BY_REF(EX(call)->func, arg_num)) {
|
||||
@ -4754,17 +4893,38 @@ ZEND_VM_HOT_SEND_HANDLER(100, ZEND_CHECK_FUNC_ARG, UNUSED, NUM, SPEC(QUICK_ARG))
|
||||
ZEND_VM_NEXT_OPCODE();
|
||||
}
|
||||
|
||||
ZEND_VM_HOT_HANDLER(185, ZEND_SEND_FUNC_ARG, VAR, NUM)
|
||||
ZEND_VM_HOT_HANDLER(185, ZEND_SEND_FUNC_ARG, VAR, CONST|UNUSED|NUM)
|
||||
{
|
||||
USE_OPLINE
|
||||
zval *varptr, *arg;
|
||||
|
||||
if (OP2_TYPE == IS_CONST) {
|
||||
// TODO: Would it make sense to share the cache slot with CHECK_FUNC_ARG?
|
||||
SAVE_OPLINE();
|
||||
zend_string *arg_name = Z_STR_P(RT_CONSTANT(opline, opline->op2));
|
||||
uint32_t arg_num;
|
||||
arg = zend_handle_named_arg(&EX(call), arg_name, &arg_num, CACHE_ADDR(opline->result.num));
|
||||
if (UNEXPECTED(!arg)) {
|
||||
HANDLE_EXCEPTION();
|
||||
}
|
||||
} else {
|
||||
arg = ZEND_CALL_VAR(EX(call), opline->result.var);
|
||||
}
|
||||
|
||||
if (UNEXPECTED(ZEND_CALL_INFO(EX(call)) & ZEND_CALL_SEND_ARG_BY_REF)) {
|
||||
ZEND_VM_DISPATCH_TO_HANDLER(ZEND_SEND_REF);
|
||||
varptr = GET_OP1_ZVAL_PTR_PTR(BP_VAR_W);
|
||||
if (Z_ISREF_P(varptr)) {
|
||||
Z_ADDREF_P(varptr);
|
||||
} else {
|
||||
ZVAL_MAKE_REF_EX(varptr, 2);
|
||||
}
|
||||
ZVAL_REF(arg, Z_REF_P(varptr));
|
||||
|
||||
FREE_OP1_VAR_PTR();
|
||||
ZEND_VM_NEXT_OPCODE();
|
||||
}
|
||||
|
||||
varptr = GET_OP1_ZVAL_PTR_UNDEF(BP_VAR_R);
|
||||
arg = ZEND_CALL_VAR(EX(call), opline->result.var);
|
||||
|
||||
if (UNEXPECTED(Z_ISREF_P(varptr))) {
|
||||
zend_refcounted *ref = Z_COUNTED_P(varptr);
|
||||
@ -4787,7 +4947,7 @@ ZEND_VM_HANDLER(165, ZEND_SEND_UNPACK, ANY, ANY)
|
||||
{
|
||||
USE_OPLINE
|
||||
zval *args;
|
||||
int arg_num;
|
||||
uint32_t arg_num;
|
||||
|
||||
SAVE_OPLINE();
|
||||
args = GET_OP1_ZVAL_PTR_UNDEF(BP_VAR_R);
|
||||
@ -4798,20 +4958,28 @@ ZEND_VM_C_LABEL(send_again):
|
||||
HashTable *ht = Z_ARRVAL_P(args);
|
||||
zval *arg, *top;
|
||||
zend_string *name;
|
||||
zend_bool have_named_params = 0;
|
||||
|
||||
zend_vm_stack_extend_call_frame(&EX(call), arg_num - 1, zend_hash_num_elements(ht));
|
||||
|
||||
// TODO: Speed this up using a flag that specifies whether there are any ref parameters.
|
||||
if ((OP1_TYPE & (IS_VAR|IS_CV)) && Z_REFCOUNT_P(args) > 1) {
|
||||
uint32_t i;
|
||||
uint32_t tmp_arg_num = arg_num;
|
||||
int separate = 0;
|
||||
|
||||
/* check if any of arguments are going to be passed by reference */
|
||||
for (i = 0; i < zend_hash_num_elements(ht); i++) {
|
||||
if (ARG_SHOULD_BE_SENT_BY_REF(EX(call)->func, arg_num + i)) {
|
||||
ZEND_HASH_FOREACH_STR_KEY_VAL(ht, name, arg) {
|
||||
if (UNEXPECTED(name)) {
|
||||
void *cache_slot[2] = {NULL, NULL};
|
||||
tmp_arg_num = zend_get_arg_offset_by_name(
|
||||
EX(call)->func, name, cache_slot) + 1;
|
||||
}
|
||||
if (ARG_SHOULD_BE_SENT_BY_REF(EX(call)->func, tmp_arg_num)) {
|
||||
separate = 1;
|
||||
break;
|
||||
}
|
||||
}
|
||||
tmp_arg_num++;
|
||||
} ZEND_HASH_FOREACH_END();
|
||||
if (separate) {
|
||||
SEPARATE_ARRAY(args);
|
||||
ht = Z_ARRVAL_P(args);
|
||||
@ -4819,13 +4987,26 @@ ZEND_VM_C_LABEL(send_again):
|
||||
}
|
||||
|
||||
ZEND_HASH_FOREACH_STR_KEY_VAL(ht, name, arg) {
|
||||
if (name) {
|
||||
zend_throw_error(NULL, "Cannot unpack array with string keys");
|
||||
FREE_OP1();
|
||||
HANDLE_EXCEPTION();
|
||||
if (UNEXPECTED(name)) {
|
||||
void *cache_slot[2] = {NULL, NULL};
|
||||
have_named_params = 1;
|
||||
top = zend_handle_named_arg(&EX(call), name, &arg_num, cache_slot);
|
||||
if (UNEXPECTED(!top)) {
|
||||
FREE_OP1();
|
||||
HANDLE_EXCEPTION();
|
||||
}
|
||||
} else {
|
||||
if (have_named_params) {
|
||||
zend_throw_error(NULL,
|
||||
"Cannot use positional argument after named argument during unpacking");
|
||||
FREE_OP1();
|
||||
HANDLE_EXCEPTION();
|
||||
}
|
||||
|
||||
top = ZEND_CALL_ARG(EX(call), arg_num);
|
||||
ZEND_CALL_NUM_ARGS(EX(call))++;
|
||||
}
|
||||
|
||||
top = ZEND_CALL_ARG(EX(call), arg_num);
|
||||
if (ARG_SHOULD_BE_SENT_BY_REF(EX(call)->func, arg_num)) {
|
||||
if (Z_ISREF_P(arg)) {
|
||||
Z_ADDREF_P(arg);
|
||||
@ -4842,13 +5023,13 @@ ZEND_VM_C_LABEL(send_again):
|
||||
ZVAL_COPY_DEREF(top, arg);
|
||||
}
|
||||
|
||||
ZEND_CALL_NUM_ARGS(EX(call))++;
|
||||
arg_num++;
|
||||
} ZEND_HASH_FOREACH_END();
|
||||
|
||||
} else if (EXPECTED(Z_TYPE_P(args) == IS_OBJECT)) {
|
||||
zend_class_entry *ce = Z_OBJCE_P(args);
|
||||
zend_object_iterator *iter;
|
||||
zend_bool have_named_params = 0;
|
||||
|
||||
if (!ce || !ce->get_iterator) {
|
||||
zend_type_error("Only arrays and Traversables can be unpacked");
|
||||
@ -4881,6 +5062,7 @@ ZEND_VM_C_LABEL(send_again):
|
||||
break;
|
||||
}
|
||||
|
||||
zend_string *name = NULL;
|
||||
if (iter->funcs->get_current_key) {
|
||||
zval key;
|
||||
iter->funcs->get_current_key(iter, &key);
|
||||
@ -4889,33 +5071,61 @@ ZEND_VM_C_LABEL(send_again):
|
||||
}
|
||||
|
||||
if (UNEXPECTED(Z_TYPE(key) != IS_LONG)) {
|
||||
zend_throw_error(NULL,
|
||||
(Z_TYPE(key) == IS_STRING) ?
|
||||
"Cannot unpack Traversable with string keys" :
|
||||
"Cannot unpack Traversable with non-integer keys");
|
||||
zval_ptr_dtor(&key);
|
||||
break;
|
||||
if (UNEXPECTED(Z_TYPE(key) != IS_STRING)) {
|
||||
zend_throw_error(NULL,
|
||||
"Keys must be of type int|string during argument unpacking");
|
||||
zval_ptr_dtor(&key);
|
||||
break;
|
||||
}
|
||||
|
||||
name = Z_STR_P(&key);
|
||||
}
|
||||
}
|
||||
|
||||
if (ARG_MUST_BE_SENT_BY_REF(EX(call)->func, 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)->func->common.scope ? ZSTR_VAL(EX(call)->func->common.scope->name) : "",
|
||||
EX(call)->func->common.scope ? "::" : "",
|
||||
ZSTR_VAL(EX(call)->func->common.function_name)
|
||||
);
|
||||
if (UNEXPECTED(name)) {
|
||||
void *cache_slot[2] = {NULL, NULL};
|
||||
have_named_params = 1;
|
||||
top = zend_handle_named_arg(&EX(call), name, &arg_num, cache_slot);
|
||||
if (UNEXPECTED(!top)) {
|
||||
break;
|
||||
}
|
||||
|
||||
if (ARG_MUST_BE_SENT_BY_REF(EX(call)->func, 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)->func->common.scope ? ZSTR_VAL(EX(call)->func->common.scope->name) : "",
|
||||
EX(call)->func->common.scope ? "::" : "",
|
||||
ZSTR_VAL(EX(call)->func->common.function_name)
|
||||
);
|
||||
}
|
||||
|
||||
ZVAL_COPY_DEREF(top, arg);
|
||||
zend_string_release(name);
|
||||
} else {
|
||||
if (have_named_params) {
|
||||
zend_throw_error(NULL,
|
||||
"Cannot use positional argument after named argument during unpacking");
|
||||
break;
|
||||
}
|
||||
|
||||
if (ARG_MUST_BE_SENT_BY_REF(EX(call)->func, 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)->func->common.scope ? ZSTR_VAL(EX(call)->func->common.scope->name) : "",
|
||||
EX(call)->func->common.scope ? "::" : "",
|
||||
ZSTR_VAL(EX(call)->func->common.function_name)
|
||||
);
|
||||
}
|
||||
|
||||
|
||||
zend_vm_stack_extend_call_frame(&EX(call), arg_num - 1, 1);
|
||||
top = ZEND_CALL_ARG(EX(call), arg_num);
|
||||
ZVAL_COPY_DEREF(top, arg);
|
||||
ZEND_CALL_NUM_ARGS(EX(call))++;
|
||||
}
|
||||
|
||||
ZVAL_DEREF(arg);
|
||||
Z_TRY_ADDREF_P(arg);
|
||||
|
||||
zend_vm_stack_extend_call_frame(&EX(call), arg_num - 1, 1);
|
||||
top = ZEND_CALL_ARG(EX(call), arg_num);
|
||||
ZVAL_COPY_VALUE(top, arg);
|
||||
ZEND_CALL_NUM_ARGS(EX(call))++;
|
||||
|
||||
iter->funcs->move_forward(iter);
|
||||
}
|
||||
|
||||
@ -4959,10 +5169,11 @@ ZEND_VM_HANDLER(119, ZEND_SEND_ARRAY, ANY, ANY, NUM)
|
||||
HashTable *ht;
|
||||
zval *arg, *param;
|
||||
|
||||
|
||||
ZEND_VM_C_LABEL(send_array):
|
||||
ht = Z_ARRVAL_P(args);
|
||||
if (OP2_TYPE != IS_UNUSED) {
|
||||
/* We don't need to handle named params in this case,
|
||||
* because array_slice() is called with $preserve_keys == false. */
|
||||
zval *op2 = GET_OP2_ZVAL_PTR(BP_VAR_R);
|
||||
uint32_t skip = opline->extended_value;
|
||||
uint32_t count = zend_hash_num_elements(ht);
|
||||
@ -5007,10 +5218,28 @@ ZEND_VM_C_LABEL(send_array):
|
||||
}
|
||||
FREE_OP2();
|
||||
} else {
|
||||
zend_string *name;
|
||||
zend_bool have_named_params;
|
||||
zend_vm_stack_extend_call_frame(&EX(call), 0, zend_hash_num_elements(ht));
|
||||
arg_num = 1;
|
||||
param = ZEND_CALL_ARG(EX(call), 1);
|
||||
ZEND_HASH_FOREACH_VAL(ht, arg) {
|
||||
have_named_params = 0;
|
||||
ZEND_HASH_FOREACH_STR_KEY_VAL(ht, name, arg) {
|
||||
if (name) {
|
||||
void *cache_slot[2] = {NULL, NULL};
|
||||
have_named_params = 1;
|
||||
param = zend_handle_named_arg(&EX(call), name, &arg_num, cache_slot);
|
||||
if (!param) {
|
||||
FREE_OP1();
|
||||
HANDLE_EXCEPTION();
|
||||
}
|
||||
} else if (have_named_params) {
|
||||
zend_throw_error(NULL,
|
||||
"Cannot use positional argument after named argument");
|
||||
FREE_OP1();
|
||||
HANDLE_EXCEPTION();
|
||||
}
|
||||
|
||||
if (ARG_SHOULD_BE_SENT_BY_REF(EX(call)->func, arg_num)) {
|
||||
if (UNEXPECTED(!Z_ISREF_P(arg))) {
|
||||
if (!ARG_MAY_BE_SENT_BY_REF(EX(call)->func, arg_num)) {
|
||||
@ -5026,10 +5255,13 @@ ZEND_VM_C_LABEL(send_array):
|
||||
arg = Z_REFVAL_P(arg);
|
||||
}
|
||||
}
|
||||
|
||||
ZVAL_COPY(param, arg);
|
||||
ZEND_CALL_NUM_ARGS(EX(call))++;
|
||||
arg_num++;
|
||||
param++;
|
||||
if (!name) {
|
||||
ZEND_CALL_NUM_ARGS(EX(call))++;
|
||||
arg_num++;
|
||||
param++;
|
||||
}
|
||||
} ZEND_HASH_FOREACH_END();
|
||||
}
|
||||
}
|
||||
@ -5055,6 +5287,20 @@ ZEND_VM_HANDLER(120, ZEND_SEND_USER, CONST|TMP|VAR|CV, NUM)
|
||||
ZEND_VM_NEXT_OPCODE_CHECK_EXCEPTION();
|
||||
}
|
||||
|
||||
ZEND_VM_HOT_HANDLER(199, ZEND_CHECK_UNDEF_ARGS, UNUSED, UNUSED)
|
||||
{
|
||||
USE_OPLINE
|
||||
|
||||
zend_execute_data *call = execute_data->call;
|
||||
if (EXPECTED(!(ZEND_CALL_INFO(call) & ZEND_CALL_MAY_HAVE_UNDEF))) {
|
||||
ZEND_VM_NEXT_OPCODE();
|
||||
}
|
||||
|
||||
SAVE_OPLINE();
|
||||
zend_handle_undef_args(call);
|
||||
ZEND_VM_NEXT_OPCODE_CHECK_EXCEPTION();
|
||||
}
|
||||
|
||||
ZEND_VM_COLD_HELPER(zend_missing_arg_helper, ANY, ANY)
|
||||
{
|
||||
#ifdef ZEND_VM_IP_GLOBAL_REG
|
||||
@ -5201,6 +5447,31 @@ ZEND_VM_HANDLER(164, ZEND_RECV_VARIADIC, NUM, UNUSED, CACHE_SLOT)
|
||||
ZVAL_EMPTY_ARRAY(params);
|
||||
}
|
||||
|
||||
if (EX_CALL_INFO() & ZEND_CALL_HAS_EXTRA_NAMED_PARAMS) {
|
||||
zend_string *name;
|
||||
zval *param;
|
||||
zend_arg_info *arg_info = &EX(func)->common.arg_info[EX(func)->common.num_args];
|
||||
if (ZEND_TYPE_IS_SET(arg_info->type)) {
|
||||
SEPARATE_ARRAY(params);
|
||||
ZEND_HASH_FOREACH_STR_KEY_VAL(EX(extra_named_params), name, param) {
|
||||
if (UNEXPECTED(!zend_verify_variadic_arg_type(EX(func), arg_info, arg_num, param, CACHE_ADDR(opline->extended_value)))) {
|
||||
HANDLE_EXCEPTION();
|
||||
}
|
||||
Z_TRY_ADDREF_P(param);
|
||||
zend_hash_add_new(Z_ARRVAL_P(params), name, param);
|
||||
} ZEND_HASH_FOREACH_END();
|
||||
} else if (zend_hash_num_elements(Z_ARRVAL_P(params)) == 0) {
|
||||
GC_ADDREF(EX(extra_named_params));
|
||||
ZVAL_ARR(params, EX(extra_named_params));
|
||||
} else {
|
||||
SEPARATE_ARRAY(params);
|
||||
ZEND_HASH_FOREACH_STR_KEY_VAL(EX(extra_named_params), name, param) {
|
||||
Z_TRY_ADDREF_P(param);
|
||||
zend_hash_add_new(Z_ARRVAL_P(params), name, param);
|
||||
} ZEND_HASH_FOREACH_END();
|
||||
}
|
||||
}
|
||||
|
||||
ZEND_VM_NEXT_OPCODE_CHECK_EXCEPTION();
|
||||
}
|
||||
|
||||
@ -8117,7 +8388,7 @@ ZEND_VM_HANDLER(158, ZEND_CALL_TRAMPOLINE, ANY, ANY)
|
||||
zend_array *args = NULL;
|
||||
zend_function *fbc = EX(func);
|
||||
zval *ret = EX(return_value);
|
||||
uint32_t call_info = EX_CALL_INFO() & (ZEND_CALL_NESTED | ZEND_CALL_TOP | ZEND_CALL_RELEASE_THIS);
|
||||
uint32_t call_info = EX_CALL_INFO() & (ZEND_CALL_NESTED | ZEND_CALL_TOP | ZEND_CALL_RELEASE_THIS | ZEND_CALL_HAS_EXTRA_NAMED_PARAMS);
|
||||
uint32_t num_args = EX_NUM_ARGS();
|
||||
zend_execute_data *call;
|
||||
|
||||
@ -8145,10 +8416,21 @@ ZEND_VM_HANDLER(158, ZEND_CALL_TRAMPOLINE, ANY, ANY)
|
||||
ZEND_CALL_NUM_ARGS(call) = 2;
|
||||
|
||||
ZVAL_STR(ZEND_CALL_ARG(call, 1), fbc->common.function_name);
|
||||
|
||||
zval *call_args = ZEND_CALL_ARG(call, 2);
|
||||
if (args) {
|
||||
ZVAL_ARR(ZEND_CALL_ARG(call, 2), args);
|
||||
ZVAL_ARR(call_args, args);
|
||||
} else {
|
||||
ZVAL_EMPTY_ARRAY(ZEND_CALL_ARG(call, 2));
|
||||
ZVAL_EMPTY_ARRAY(call_args);
|
||||
}
|
||||
if (UNEXPECTED(call_info & ZEND_CALL_HAS_EXTRA_NAMED_PARAMS)) {
|
||||
if (zend_hash_num_elements(Z_ARRVAL_P(call_args)) == 0) {
|
||||
GC_ADDREF(call->extra_named_params);
|
||||
ZVAL_ARR(call_args, call->extra_named_params);
|
||||
} else {
|
||||
SEPARATE_ARRAY(call_args);
|
||||
zend_hash_copy(Z_ARRVAL_P(call_args), call->extra_named_params, zval_add_ref);
|
||||
}
|
||||
}
|
||||
zend_free_trampoline(fbc);
|
||||
fbc = call->func;
|
||||
@ -9187,7 +9469,7 @@ ZEND_VM_C_LABEL(fetch_dim_r_index_undef):
|
||||
ZEND_VM_NEXT_OPCODE_CHECK_EXCEPTION();
|
||||
}
|
||||
|
||||
ZEND_VM_HOT_TYPE_SPEC_HANDLER(ZEND_SEND_VAR, (op1_info & (MAY_BE_UNDEF|MAY_BE_REF)) == 0, ZEND_SEND_VAR_SIMPLE, CV|VAR, NUM)
|
||||
ZEND_VM_HOT_TYPE_SPEC_HANDLER(ZEND_SEND_VAR, op->op2_type == IS_UNUSED && (op1_info & (MAY_BE_UNDEF|MAY_BE_REF)) == 0, ZEND_SEND_VAR_SIMPLE, CV|VAR, NUM)
|
||||
{
|
||||
USE_OPLINE
|
||||
zval *varptr, *arg;
|
||||
@ -9204,7 +9486,7 @@ ZEND_VM_HOT_TYPE_SPEC_HANDLER(ZEND_SEND_VAR, (op1_info & (MAY_BE_UNDEF|MAY_BE_RE
|
||||
ZEND_VM_NEXT_OPCODE();
|
||||
}
|
||||
|
||||
ZEND_VM_HOT_TYPE_SPEC_HANDLER(ZEND_SEND_VAR_EX, op->op2.num <= MAX_ARG_FLAG_NUM && (op1_info & (MAY_BE_UNDEF|MAY_BE_REF)) == 0, ZEND_SEND_VAR_EX_SIMPLE, CV|VAR, NUM)
|
||||
ZEND_VM_HOT_TYPE_SPEC_HANDLER(ZEND_SEND_VAR_EX, op->op2_type == IS_UNUSED && op->op2.num <= MAX_ARG_FLAG_NUM && (op1_info & (MAY_BE_UNDEF|MAY_BE_REF)) == 0, ZEND_SEND_VAR_EX_SIMPLE, CV|VAR, UNUSED|NUM)
|
||||
{
|
||||
USE_OPLINE
|
||||
zval *varptr, *arg;
|
||||
@ -9226,7 +9508,7 @@ ZEND_VM_HOT_TYPE_SPEC_HANDLER(ZEND_SEND_VAR_EX, op->op2.num <= MAX_ARG_FLAG_NUM
|
||||
ZEND_VM_NEXT_OPCODE();
|
||||
}
|
||||
|
||||
ZEND_VM_HOT_TYPE_SPEC_HANDLER(ZEND_SEND_VAL, op->op1_type == IS_CONST && !Z_REFCOUNTED_P(RT_CONSTANT(op, op->op1)), ZEND_SEND_VAL_SIMPLE, CONST, NUM)
|
||||
ZEND_VM_HOT_TYPE_SPEC_HANDLER(ZEND_SEND_VAL, op->op1_type == IS_CONST && op->op2_type == IS_UNUSED && !Z_REFCOUNTED_P(RT_CONSTANT(op, op->op1)), ZEND_SEND_VAL_SIMPLE, CONST, NUM)
|
||||
{
|
||||
USE_OPLINE
|
||||
zval *value, *arg;
|
||||
@ -9237,7 +9519,7 @@ ZEND_VM_HOT_TYPE_SPEC_HANDLER(ZEND_SEND_VAL, op->op1_type == IS_CONST && !Z_REFC
|
||||
ZEND_VM_NEXT_OPCODE();
|
||||
}
|
||||
|
||||
ZEND_VM_HOT_TYPE_SPEC_HANDLER(ZEND_SEND_VAL_EX, op->op2.num <= MAX_ARG_FLAG_NUM && op->op1_type == IS_CONST && !Z_REFCOUNTED_P(RT_CONSTANT(op, op->op1)), ZEND_SEND_VAL_EX_SIMPLE, CONST, NUM)
|
||||
ZEND_VM_HOT_TYPE_SPEC_HANDLER(ZEND_SEND_VAL_EX, op->op2_type == IS_UNUSED && op->op2.num <= MAX_ARG_FLAG_NUM && op->op1_type == IS_CONST && !Z_REFCOUNTED_P(RT_CONSTANT(op, op->op1)), ZEND_SEND_VAL_EX_SIMPLE, CONST, NUM)
|
||||
{
|
||||
USE_OPLINE
|
||||
zval *value, *arg;
|
||||
|
File diff suppressed because it is too large
Load Diff
@ -1015,6 +1015,17 @@ function gen_handler($f, $spec, $kind, $name, $op1, $op2, $use, $code, $lineno,
|
||||
}
|
||||
}
|
||||
|
||||
/* Skip QUICK_ARG specialization for named parameters */
|
||||
if (isset($extra_spec["QUICK_ARG"])) {
|
||||
if ($op2 === "CONST") {
|
||||
if ($extra_spec["QUICK_ARG"] == 0) {
|
||||
unset($extra_spec["QUICK_ARG"]);
|
||||
} else {
|
||||
return;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (ZEND_VM_LINES) {
|
||||
out($f, "#line $lineno \"$definition_file\"\n");
|
||||
}
|
||||
@ -1335,6 +1346,13 @@ function gen_labels($f, $spec, $kind, $prolog, &$specs, $switch_labels = array()
|
||||
}
|
||||
}
|
||||
|
||||
/* Skip QUICK_ARG specialization for named parameters */
|
||||
if (isset($extra_spec["QUICK_ARG"])) {
|
||||
if ($op2 === "CONST") {
|
||||
unset($extra_spec["QUICK_ARG"]);
|
||||
}
|
||||
}
|
||||
|
||||
// Emit pointer to specialized handler
|
||||
$spec_name = $dsc["op"]."_SPEC".$prefix[$op1].$prefix[$op2].extra_spec_name($extra_spec);
|
||||
switch ($kind) {
|
||||
|
File diff suppressed because it is too large
Load Diff
@ -22,7 +22,7 @@
|
||||
#include <zend.h>
|
||||
#include <zend_vm_opcodes.h>
|
||||
|
||||
static const char *zend_vm_opcodes_names[199] = {
|
||||
static const char *zend_vm_opcodes_names[200] = {
|
||||
"ZEND_NOP",
|
||||
"ZEND_ADD",
|
||||
"ZEND_SUB",
|
||||
@ -222,9 +222,10 @@ static const char *zend_vm_opcodes_names[199] = {
|
||||
"ZEND_CASE_STRICT",
|
||||
"ZEND_MATCH_ERROR",
|
||||
"ZEND_JMP_NULL",
|
||||
"ZEND_CHECK_UNDEF_ARGS",
|
||||
};
|
||||
|
||||
static uint32_t zend_vm_opcodes_flags[199] = {
|
||||
static uint32_t zend_vm_opcodes_flags[200] = {
|
||||
0x00000000,
|
||||
0x00000b0b,
|
||||
0x00000b0b,
|
||||
@ -275,7 +276,7 @@ static uint32_t zend_vm_opcodes_flags[199] = {
|
||||
0x00002007,
|
||||
0x00000705,
|
||||
0x00000101,
|
||||
0x00001001,
|
||||
0x00001301,
|
||||
0x07000003,
|
||||
0x00000007,
|
||||
0x00000707,
|
||||
@ -290,9 +291,9 @@ static uint32_t zend_vm_opcodes_flags[199] = {
|
||||
0x00000003,
|
||||
0x00040110,
|
||||
0x00040310,
|
||||
0x00001007,
|
||||
0x00001001,
|
||||
0x00001001,
|
||||
0x00001307,
|
||||
0x00001301,
|
||||
0x00001301,
|
||||
0x0100a173,
|
||||
0x01040300,
|
||||
0x00000005,
|
||||
@ -325,13 +326,13 @@ static uint32_t zend_vm_opcodes_flags[199] = {
|
||||
0x00040751,
|
||||
0x0000070b,
|
||||
0x00040391,
|
||||
0x00001001,
|
||||
0x00001301,
|
||||
0x00000000,
|
||||
0x00000000,
|
||||
0x00000000,
|
||||
0x00000000,
|
||||
0x01000000,
|
||||
0x00001001,
|
||||
0x00001301,
|
||||
0x02042003,
|
||||
0x00000007,
|
||||
0x00040771,
|
||||
@ -341,8 +342,8 @@ static uint32_t zend_vm_opcodes_flags[199] = {
|
||||
0x01048773,
|
||||
0x00030107,
|
||||
0x00020707,
|
||||
0x00001003,
|
||||
0x00001001,
|
||||
0x00001303,
|
||||
0x00001301,
|
||||
0x01000703,
|
||||
0x01000000,
|
||||
0x00001003,
|
||||
@ -410,7 +411,7 @@ static uint32_t zend_vm_opcodes_flags[199] = {
|
||||
0x00100101,
|
||||
0x00100101,
|
||||
0x00000101,
|
||||
0x00001001,
|
||||
0x00001301,
|
||||
0x00000101,
|
||||
0x0300030b,
|
||||
0x0300030b,
|
||||
@ -424,6 +425,7 @@ static uint32_t zend_vm_opcodes_flags[199] = {
|
||||
0x00000301,
|
||||
0x0000010b,
|
||||
0x0000200b,
|
||||
0x00000101,
|
||||
};
|
||||
|
||||
ZEND_API const char* ZEND_FASTCALL zend_get_opcode_name(zend_uchar opcode) {
|
||||
|
@ -275,7 +275,8 @@ END_EXTERN_C()
|
||||
#define ZEND_CASE_STRICT 196
|
||||
#define ZEND_MATCH_ERROR 197
|
||||
#define ZEND_JMP_NULL 198
|
||||
#define ZEND_CHECK_UNDEF_ARGS 199
|
||||
|
||||
#define ZEND_VM_LAST_OPCODE 198
|
||||
#define ZEND_VM_LAST_OPCODE 199
|
||||
|
||||
#endif
|
||||
|
@ -1385,6 +1385,7 @@ static size_t curl_write(char *data, size_t size, size_t nmemb, void *ctx)
|
||||
fci.retval = &retval;
|
||||
fci.param_count = 2;
|
||||
fci.params = argv;
|
||||
fci.named_params = NULL;
|
||||
|
||||
ch->in_callback = 1;
|
||||
error = zend_call_function(&fci, &t->fci_cache);
|
||||
@ -1431,6 +1432,7 @@ static int curl_fnmatch(void *ctx, const char *pattern, const char *string)
|
||||
fci.retval = &retval;
|
||||
fci.param_count = 3;
|
||||
fci.params = argv;
|
||||
fci.named_params = NULL;
|
||||
|
||||
ch->in_callback = 1;
|
||||
error = zend_call_function(&fci, &t->fci_cache);
|
||||
@ -1483,6 +1485,7 @@ static size_t curl_progress(void *clientp, double dltotal, double dlnow, double
|
||||
fci.retval = &retval;
|
||||
fci.param_count = 5;
|
||||
fci.params = argv;
|
||||
fci.named_params = NULL;
|
||||
|
||||
ch->in_callback = 1;
|
||||
error = zend_call_function(&fci, &t->fci_cache);
|
||||
@ -1538,6 +1541,7 @@ static size_t curl_read(char *data, size_t size, size_t nmemb, void *ctx)
|
||||
fci.retval = &retval;
|
||||
fci.param_count = 3;
|
||||
fci.params = argv;
|
||||
fci.named_params = NULL;
|
||||
|
||||
ch->in_callback = 1;
|
||||
error = zend_call_function(&fci, &t->fci_cache);
|
||||
@ -1599,6 +1603,7 @@ static size_t curl_write_header(char *data, size_t size, size_t nmemb, void *ctx
|
||||
fci.retval = &retval;
|
||||
fci.param_count = 2;
|
||||
fci.params = argv;
|
||||
fci.named_params = NULL;
|
||||
|
||||
ch->in_callback = 1;
|
||||
error = zend_call_function(&fci, &t->fci_cache);
|
||||
|
@ -150,6 +150,7 @@ static void dom_xpath_ext_function_php(xmlXPathParserContextPtr ctxt, int nargs,
|
||||
xmlXPathFreeObject(obj);
|
||||
|
||||
fci.object = NULL;
|
||||
fci.named_params = NULL;
|
||||
fci.retval = &retval;
|
||||
|
||||
if (!zend_make_callable(&fci.function_name, &callable)) {
|
||||
|
@ -858,6 +858,7 @@ static void zend_ffi_callback_trampoline(ffi_cif* cif, void* ret, void** args, v
|
||||
fci.params = do_alloca(sizeof(zval) *callback_data->arg_count, use_heap);
|
||||
fci.object = NULL;
|
||||
fci.param_count = callback_data->arg_count;
|
||||
fci.named_params = NULL;
|
||||
|
||||
if (callback_data->type->func.args) {
|
||||
int n = 0;
|
||||
|
@ -1227,6 +1227,7 @@ void php_mysqli_fetch_into_hash(INTERNAL_FUNCTION_PARAMETERS, int override_flags
|
||||
fci.retval = &retval;
|
||||
fci.params = NULL;
|
||||
fci.param_count = 0;
|
||||
fci.named_params = NULL;
|
||||
|
||||
if (ctor_params && Z_TYPE_P(ctor_params) != IS_NULL) {
|
||||
if (zend_fcall_info_args(&fci, ctor_params) == FAILURE) {
|
||||
|
@ -782,6 +782,20 @@ void zend_optimizer_compact_literals(zend_op_array *op_array, zend_optimizer_ctx
|
||||
opline->extended_value = cache_size;
|
||||
cache_size += sizeof(void *);
|
||||
break;
|
||||
case ZEND_SEND_VAL:
|
||||
case ZEND_SEND_VAL_EX:
|
||||
case ZEND_SEND_VAR:
|
||||
case ZEND_SEND_VAR_EX:
|
||||
case ZEND_SEND_VAR_NO_REF:
|
||||
case ZEND_SEND_VAR_NO_REF_EX:
|
||||
case ZEND_SEND_REF:
|
||||
case ZEND_SEND_FUNC_ARG:
|
||||
case ZEND_CHECK_FUNC_ARG:
|
||||
if (opline->op2_type == IS_CONST) {
|
||||
opline->result.num = cache_size;
|
||||
cache_size += 2 * sizeof(void *);
|
||||
}
|
||||
break;
|
||||
}
|
||||
opline++;
|
||||
}
|
||||
|
@ -230,8 +230,8 @@ void zend_optimize_func_calls(zend_op_array *op_array, zend_optimizer_ctx *ctx)
|
||||
case ZEND_FETCH_STATIC_PROP_FUNC_ARG:
|
||||
case ZEND_FETCH_OBJ_FUNC_ARG:
|
||||
case ZEND_FETCH_DIM_FUNC_ARG:
|
||||
if (call_stack[call - 1].func) {
|
||||
ZEND_ASSERT(call_stack[call - 1].func_arg_num != (uint32_t)-1);
|
||||
if (call_stack[call - 1].func
|
||||
&& call_stack[call - 1].func_arg_num != (uint32_t)-1) {
|
||||
if (ARG_SHOULD_BE_SENT_BY_REF(call_stack[call - 1].func, call_stack[call - 1].func_arg_num)) {
|
||||
if (opline->opcode != ZEND_FETCH_STATIC_PROP_FUNC_ARG) {
|
||||
opline->opcode -= 9;
|
||||
@ -257,6 +257,11 @@ void zend_optimize_func_calls(zend_op_array *op_array, zend_optimizer_ctx *ctx)
|
||||
break;
|
||||
case ZEND_SEND_VAL_EX:
|
||||
if (call_stack[call - 1].func) {
|
||||
if (opline->op2_type == IS_CONST) {
|
||||
call_stack[call - 1].try_inline = 0;
|
||||
break;
|
||||
}
|
||||
|
||||
if (ARG_MUST_BE_SENT_BY_REF(call_stack[call - 1].func, opline->op2.num)) {
|
||||
/* We won't convert it into_DO_FCALL to emit error at run-time */
|
||||
call_stack[call - 1].opline = NULL;
|
||||
@ -267,6 +272,12 @@ void zend_optimize_func_calls(zend_op_array *op_array, zend_optimizer_ctx *ctx)
|
||||
break;
|
||||
case ZEND_CHECK_FUNC_ARG:
|
||||
if (call_stack[call - 1].func) {
|
||||
if (opline->op2_type == IS_CONST) {
|
||||
call_stack[call - 1].try_inline = 0;
|
||||
call_stack[call - 1].func_arg_num = (uint32_t)-1;
|
||||
break;
|
||||
}
|
||||
|
||||
call_stack[call - 1].func_arg_num = opline->op2.num;
|
||||
MAKE_NOP(opline);
|
||||
}
|
||||
@ -274,6 +285,11 @@ void zend_optimize_func_calls(zend_op_array *op_array, zend_optimizer_ctx *ctx)
|
||||
case ZEND_SEND_VAR_EX:
|
||||
case ZEND_SEND_FUNC_ARG:
|
||||
if (call_stack[call - 1].func) {
|
||||
if (opline->op2_type == IS_CONST) {
|
||||
call_stack[call - 1].try_inline = 0;
|
||||
break;
|
||||
}
|
||||
|
||||
call_stack[call - 1].func_arg_num = (uint32_t)-1;
|
||||
if (ARG_SHOULD_BE_SENT_BY_REF(call_stack[call - 1].func, opline->op2.num)) {
|
||||
opline->opcode = ZEND_SEND_REF;
|
||||
@ -284,6 +300,11 @@ void zend_optimize_func_calls(zend_op_array *op_array, zend_optimizer_ctx *ctx)
|
||||
break;
|
||||
case ZEND_SEND_VAR_NO_REF_EX:
|
||||
if (call_stack[call - 1].func) {
|
||||
if (opline->op2_type == IS_CONST) {
|
||||
call_stack[call - 1].try_inline = 0;
|
||||
break;
|
||||
}
|
||||
|
||||
if (ARG_MUST_BE_SENT_BY_REF(call_stack[call - 1].func, opline->op2.num)) {
|
||||
opline->opcode = ZEND_SEND_VAR_NO_REF;
|
||||
} else if (ARG_MAY_BE_SENT_BY_REF(call_stack[call - 1].func, opline->op2.num)) {
|
||||
@ -293,6 +314,14 @@ void zend_optimize_func_calls(zend_op_array *op_array, zend_optimizer_ctx *ctx)
|
||||
}
|
||||
}
|
||||
break;
|
||||
case ZEND_SEND_VAL:
|
||||
case ZEND_SEND_VAR:
|
||||
case ZEND_SEND_REF:
|
||||
if (opline->op2_type == IS_CONST) {
|
||||
call_stack[call - 1].try_inline = 0;
|
||||
break;
|
||||
}
|
||||
break;
|
||||
case ZEND_SEND_UNPACK:
|
||||
case ZEND_SEND_USER:
|
||||
case ZEND_SEND_ARRAY:
|
||||
|
@ -124,8 +124,12 @@ int zend_analyze_calls(zend_arena **arena, zend_script *script, uint32_t build_f
|
||||
case ZEND_SEND_VAR_NO_REF_EX:
|
||||
case ZEND_SEND_USER:
|
||||
if (call_info) {
|
||||
uint32_t num = opline->op2.num;
|
||||
if (opline->op2_type == IS_CONST) {
|
||||
call_info->named_args = 1;
|
||||
break;
|
||||
}
|
||||
|
||||
uint32_t num = opline->op2.num;
|
||||
if (num > 0) {
|
||||
num--;
|
||||
}
|
||||
|
@ -36,6 +36,7 @@ struct _zend_call_info {
|
||||
zend_call_info *next_callee;
|
||||
zend_bool recursive;
|
||||
zend_bool send_unpack; /* Parameters passed by SEND_UNPACK or SEND_ARRAY */
|
||||
zend_bool named_args; /* Function has named arguments */
|
||||
int num_args;
|
||||
zend_send_arg_info arg_info[1];
|
||||
};
|
||||
|
@ -4354,13 +4354,8 @@ int zend_may_throw_ex(const zend_op *opline, const zend_ssa_op *ssa_op, const ze
|
||||
case ZEND_JMP:
|
||||
case ZEND_CHECK_VAR:
|
||||
case ZEND_MAKE_REF:
|
||||
case ZEND_SEND_VAR:
|
||||
case ZEND_BEGIN_SILENCE:
|
||||
case ZEND_END_SILENCE:
|
||||
case ZEND_SEND_VAL:
|
||||
case ZEND_SEND_REF:
|
||||
case ZEND_SEND_VAR_EX:
|
||||
case ZEND_SEND_FUNC_ARG:
|
||||
case ZEND_FREE:
|
||||
case ZEND_SEPARATE:
|
||||
case ZEND_TYPE_CHECK:
|
||||
@ -4375,10 +4370,17 @@ int zend_may_throw_ex(const zend_op *opline, const zend_ssa_op *ssa_op, const ze
|
||||
case ZEND_FUNC_NUM_ARGS:
|
||||
case ZEND_FUNC_GET_ARGS:
|
||||
case ZEND_COPY_TMP:
|
||||
case ZEND_CHECK_FUNC_ARG:
|
||||
case ZEND_CASE_STRICT:
|
||||
case ZEND_JMP_NULL:
|
||||
return 0;
|
||||
case ZEND_SEND_VAR:
|
||||
case ZEND_SEND_VAL:
|
||||
case ZEND_SEND_REF:
|
||||
case ZEND_SEND_VAR_EX:
|
||||
case ZEND_SEND_FUNC_ARG:
|
||||
case ZEND_CHECK_FUNC_ARG:
|
||||
/* May throw for named params. */
|
||||
return opline->op2_type == IS_CONST;
|
||||
case ZEND_INIT_FCALL:
|
||||
/* can't throw, because call is resolved at compile time */
|
||||
return 0;
|
||||
|
@ -2521,6 +2521,10 @@ static int zend_jit(const zend_op_array *op_array, zend_ssa *ssa, const zend_op
|
||||
goto done;
|
||||
case ZEND_SEND_VAL:
|
||||
case ZEND_SEND_VAL_EX:
|
||||
if (opline->op2_type == IS_CONST) {
|
||||
/* Named parameters not supported in JIT (yet) */
|
||||
break;
|
||||
}
|
||||
if (opline->opcode == ZEND_SEND_VAL_EX
|
||||
&& opline->op2.num > MAX_ARG_FLAG_NUM) {
|
||||
break;
|
||||
@ -2531,6 +2535,10 @@ static int zend_jit(const zend_op_array *op_array, zend_ssa *ssa, const zend_op
|
||||
}
|
||||
goto done;
|
||||
case ZEND_SEND_REF:
|
||||
if (opline->op2_type == IS_CONST) {
|
||||
/* Named parameters not supported in JIT (yet) */
|
||||
break;
|
||||
}
|
||||
if (!zend_jit_send_ref(&dasm_state, opline, op_array,
|
||||
OP1_INFO(), 0)) {
|
||||
goto jit_failure;
|
||||
@ -2541,6 +2549,10 @@ static int zend_jit(const zend_op_array *op_array, zend_ssa *ssa, const zend_op
|
||||
case ZEND_SEND_VAR_NO_REF:
|
||||
case ZEND_SEND_VAR_NO_REF_EX:
|
||||
case ZEND_SEND_FUNC_ARG:
|
||||
if (opline->op2_type == IS_CONST) {
|
||||
/* Named parameters not supported in JIT (yet) */
|
||||
break;
|
||||
}
|
||||
if ((opline->opcode == ZEND_SEND_VAR_EX
|
||||
|| opline->opcode == ZEND_SEND_VAR_NO_REF_EX)
|
||||
&& opline->op2.num > MAX_ARG_FLAG_NUM) {
|
||||
@ -2560,6 +2572,10 @@ static int zend_jit(const zend_op_array *op_array, zend_ssa *ssa, const zend_op
|
||||
}
|
||||
goto done;
|
||||
case ZEND_CHECK_FUNC_ARG:
|
||||
if (opline->op2_type == IS_CONST) {
|
||||
/* Named parameters not supported in JIT (yet) */
|
||||
break;
|
||||
}
|
||||
if (opline->op2.num > MAX_ARG_FLAG_NUM) {
|
||||
break;
|
||||
}
|
||||
@ -2567,6 +2583,11 @@ static int zend_jit(const zend_op_array *op_array, zend_ssa *ssa, const zend_op
|
||||
goto jit_failure;
|
||||
}
|
||||
goto done;
|
||||
case ZEND_CHECK_UNDEF_ARGS:
|
||||
if (!zend_jit_check_undef_args(&dasm_state, opline)) {
|
||||
goto jit_failure;
|
||||
}
|
||||
goto done;
|
||||
case ZEND_DO_UCALL:
|
||||
is_terminated = 1;
|
||||
/* break missing intentionally */
|
||||
|
@ -3538,6 +3538,10 @@ static const void *zend_jit_trace(zend_jit_trace_rec *trace_buffer, uint32_t par
|
||||
goto done;
|
||||
case ZEND_SEND_VAL:
|
||||
case ZEND_SEND_VAL_EX:
|
||||
if (opline->op2_type == IS_CONST) {
|
||||
/* Named parameters not supported in JIT */
|
||||
break;
|
||||
}
|
||||
if (opline->opcode == ZEND_SEND_VAL_EX
|
||||
&& opline->op2.num > MAX_ARG_FLAG_NUM) {
|
||||
break;
|
||||
@ -3562,6 +3566,10 @@ static const void *zend_jit_trace(zend_jit_trace_rec *trace_buffer, uint32_t par
|
||||
}
|
||||
goto done;
|
||||
case ZEND_SEND_REF:
|
||||
if (opline->op2_type == IS_CONST) {
|
||||
/* Named parameters not supported in JIT */
|
||||
break;
|
||||
}
|
||||
op1_info = OP1_INFO();
|
||||
if (!zend_jit_send_ref(&dasm_state, opline, op_array,
|
||||
op1_info, 0)) {
|
||||
@ -3573,6 +3581,10 @@ static const void *zend_jit_trace(zend_jit_trace_rec *trace_buffer, uint32_t par
|
||||
case ZEND_SEND_VAR_NO_REF:
|
||||
case ZEND_SEND_VAR_NO_REF_EX:
|
||||
case ZEND_SEND_FUNC_ARG:
|
||||
if (opline->op2_type == IS_CONST) {
|
||||
/* Named parameters not supported in JIT */
|
||||
break;
|
||||
}
|
||||
if ((opline->opcode == ZEND_SEND_VAR_EX
|
||||
|| opline->opcode == ZEND_SEND_VAR_NO_REF_EX)
|
||||
&& opline->op2.num > MAX_ARG_FLAG_NUM) {
|
||||
@ -3609,6 +3621,10 @@ static const void *zend_jit_trace(zend_jit_trace_rec *trace_buffer, uint32_t par
|
||||
}
|
||||
goto done;
|
||||
case ZEND_CHECK_FUNC_ARG:
|
||||
if (opline->op2_type == IS_CONST) {
|
||||
/* Named parameters not supported in JIT */
|
||||
break;
|
||||
}
|
||||
if (opline->op2.num > MAX_ARG_FLAG_NUM
|
||||
&& (!JIT_G(current_frame)
|
||||
|| !JIT_G(current_frame)->call
|
||||
@ -3619,6 +3635,11 @@ static const void *zend_jit_trace(zend_jit_trace_rec *trace_buffer, uint32_t par
|
||||
goto jit_failure;
|
||||
}
|
||||
goto done;
|
||||
case ZEND_CHECK_UNDEF_ARGS:
|
||||
if (!zend_jit_check_undef_args(&dasm_state, opline)) {
|
||||
goto jit_failure;
|
||||
}
|
||||
goto done;
|
||||
case ZEND_DO_UCALL:
|
||||
case ZEND_DO_ICALL:
|
||||
case ZEND_DO_FCALL_BY_NAME:
|
||||
|
@ -57,6 +57,9 @@ ZEND_OPCODE_HANDLER_RET ZEND_FASTCALL zend_jit_leave_nested_func_helper(uint32_t
|
||||
} else if (UNEXPECTED(call_info & ZEND_CALL_CLOSURE)) {
|
||||
OBJ_RELEASE(ZEND_CLOSURE_OBJECT(EX(func)));
|
||||
}
|
||||
if (UNEXPECTED(call_info & ZEND_CALL_HAS_EXTRA_NAMED_PARAMS)) {
|
||||
zend_free_extra_named_params(EX(extra_named_params));
|
||||
}
|
||||
|
||||
old_execute_data = execute_data;
|
||||
execute_data = EX(prev_execute_data);
|
||||
@ -89,6 +92,9 @@ ZEND_OPCODE_HANDLER_RET ZEND_FASTCALL zend_jit_leave_top_func_helper(uint32_t ca
|
||||
}
|
||||
zend_vm_stack_free_extra_args_ex(call_info, execute_data);
|
||||
}
|
||||
if (UNEXPECTED(call_info & ZEND_CALL_HAS_EXTRA_NAMED_PARAMS)) {
|
||||
zend_free_extra_named_params(EX(extra_named_params));
|
||||
}
|
||||
if (UNEXPECTED(call_info & ZEND_CALL_CLOSURE)) {
|
||||
OBJ_RELEASE(ZEND_CLOSURE_OBJECT(EX(func)));
|
||||
}
|
||||
|
@ -8567,7 +8567,8 @@ static int zend_jit_do_fcall(dasm_State **Dst, const zend_op *opline, const zend
|
||||
#endif
|
||||
}
|
||||
|
||||
if ((opline-1)->opcode == ZEND_SEND_UNPACK|| (opline-1)->opcode == ZEND_SEND_ARRAY) {
|
||||
if ((opline-1)->opcode == ZEND_SEND_UNPACK || (opline-1)->opcode == ZEND_SEND_ARRAY ||
|
||||
(opline-1)->opcode == ZEND_CHECK_UNDEF_ARGS) {
|
||||
unknown_num_args = 1;
|
||||
}
|
||||
|
||||
@ -8621,6 +8622,10 @@ static int zend_jit_do_fcall(dasm_State **Dst, const zend_op *opline, const zend
|
||||
}
|
||||
}
|
||||
|
||||
bool may_have_extra_named_params =
|
||||
opline->extended_value == ZEND_FCALL_MAY_HAVE_EXTRA_NAMED_PARAMS &&
|
||||
(!func || func->common.fn_flags & ZEND_ACC_VARIADIC);
|
||||
|
||||
if (!reuse_ip) {
|
||||
zend_jit_start_reuse_ip();
|
||||
| // call = EX(call);
|
||||
@ -9025,6 +9030,17 @@ static int zend_jit_do_fcall(dasm_State **Dst, const zend_op *opline, const zend
|
||||
| mov FCARG1a, RX
|
||||
| EXT_CALL zend_jit_vm_stack_free_args_helper, r0
|
||||
}
|
||||
if (may_have_extra_named_params) {
|
||||
| test byte [RX + offsetof(zend_execute_data, This.u1.type_info) + 3], (ZEND_CALL_HAS_EXTRA_NAMED_PARAMS >> 24)
|
||||
| jnz >1
|
||||
|.cold_code
|
||||
|1:
|
||||
| mov FCARG1a, aword [RX + offsetof(zend_execute_data, extra_named_params)]
|
||||
| EXT_CALL zend_free_extra_named_params, r0
|
||||
| jmp >2
|
||||
|.code
|
||||
|2:
|
||||
}
|
||||
|
||||
|8:
|
||||
if (opline->opcode == ZEND_DO_FCALL) {
|
||||
@ -9183,6 +9199,24 @@ static int zend_jit_send_val(dasm_State **Dst, const zend_op *opline, const zend
|
||||
return 1;
|
||||
}
|
||||
|
||||
static int zend_jit_check_undef_args(dasm_State **Dst, const zend_op *opline)
|
||||
{
|
||||
| mov FCARG1a, EX->call
|
||||
| test byte [FCARG1a + offsetof(zend_execute_data, This.u1.type_info) + 3], (ZEND_CALL_MAY_HAVE_UNDEF >> 24)
|
||||
| jnz >1
|
||||
|.cold_code
|
||||
|1:
|
||||
| SAVE_VALID_OPLINE opline, r0
|
||||
| EXT_CALL zend_handle_undef_args, r0
|
||||
| test r0, r0
|
||||
| jnz ->exception_handler
|
||||
| jmp >2
|
||||
|.code
|
||||
|2:
|
||||
|
||||
return 1;
|
||||
}
|
||||
|
||||
static int zend_jit_send_ref(dasm_State **Dst, const zend_op *opline, const zend_op_array *op_array, uint32_t op1_info, int cold)
|
||||
{
|
||||
zend_jit_addr op1_addr, arg_addr, ref_addr;
|
||||
@ -10002,7 +10036,7 @@ static int zend_jit_leave_func(dasm_State **Dst, const zend_op *opline, const ze
|
||||
{
|
||||
/* ZEND_CALL_FAKE_CLOSURE handled on slow path to eliminate check for ZEND_CALL_CLOSURE on fast path */
|
||||
| mov FCARG1d, dword [FP + offsetof(zend_execute_data, This.u1.type_info)]
|
||||
| test FCARG1d, (ZEND_CALL_TOP|ZEND_CALL_HAS_SYMBOL_TABLE|ZEND_CALL_FREE_EXTRA_ARGS|ZEND_CALL_ALLOCATED|ZEND_CALL_FAKE_CLOSURE)
|
||||
| test FCARG1d, (ZEND_CALL_TOP|ZEND_CALL_HAS_SYMBOL_TABLE|ZEND_CALL_FREE_EXTRA_ARGS|ZEND_CALL_ALLOCATED|ZEND_CALL_HAS_EXTRA_NAMED_PARAMS|ZEND_CALL_FAKE_CLOSURE)
|
||||
if (trace && trace->op != ZEND_JIT_TRACE_END) {
|
||||
| jnz >1
|
||||
|.cold_code
|
||||
|
@ -415,7 +415,8 @@ static void zend_file_cache_serialize_attribute(zval *zv,
|
||||
SERIALIZE_STR(attr->lcname);
|
||||
|
||||
for (i = 0; i < attr->argc; i++) {
|
||||
zend_file_cache_serialize_zval(&attr->argv[i], script, info, buf);
|
||||
SERIALIZE_STR(attr->args[i].name);
|
||||
zend_file_cache_serialize_zval(&attr->args[i].value, script, info, buf);
|
||||
}
|
||||
}
|
||||
|
||||
@ -1180,7 +1181,8 @@ static void zend_file_cache_unserialize_attribute(zval *zv, zend_persistent_scri
|
||||
UNSERIALIZE_STR(attr->lcname);
|
||||
|
||||
for (i = 0; i < attr->argc; i++) {
|
||||
zend_file_cache_unserialize_zval(&attr->argv[i], script, buf);
|
||||
UNSERIALIZE_STR(attr->args[i].name);
|
||||
zend_file_cache_unserialize_zval(&attr->args[i].value, script, buf);
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -278,7 +278,10 @@ static HashTable *zend_persist_attributes(HashTable *attributes)
|
||||
zend_accel_store_interned_string(copy->lcname);
|
||||
|
||||
for (i = 0; i < copy->argc; i++) {
|
||||
zend_persist_zval(©->argv[i]);
|
||||
if (copy->args[i].name) {
|
||||
zend_accel_store_interned_string(copy->args[i].name);
|
||||
}
|
||||
zend_persist_zval(©->args[i].value);
|
||||
}
|
||||
|
||||
ZVAL_PTR(v, copy);
|
||||
|
@ -165,7 +165,10 @@ static void zend_persist_attributes_calc(HashTable *attributes)
|
||||
ADD_INTERNED_STRING(attr->lcname);
|
||||
|
||||
for (i = 0; i < attr->argc; i++) {
|
||||
zend_persist_zval_calc(&attr->argv[i]);
|
||||
if (attr->args[i].name) {
|
||||
ADD_INTERNED_STRING(attr->args[i].name);
|
||||
}
|
||||
zend_persist_zval_calc(&attr->args[i].value);
|
||||
}
|
||||
} ZEND_HASH_FOREACH_END();
|
||||
}
|
||||
|
@ -2400,6 +2400,7 @@ PHP_FUNCTION(preg_replace_callback_array)
|
||||
|
||||
fci.size = sizeof(fci);
|
||||
fci.object = NULL;
|
||||
fci.named_params = NULL;
|
||||
|
||||
ZEND_HASH_FOREACH_STR_KEY_VAL(pattern, str_idx_regex, replace) {
|
||||
if (!str_idx_regex) {
|
||||
|
@ -437,6 +437,7 @@ static void pdo_stmt_construct(zend_execute_data *execute_data, pdo_stmt_t *stmt
|
||||
fci.retval = &retval;
|
||||
fci.param_count = 0;
|
||||
fci.params = NULL;
|
||||
fci.named_params = NULL;
|
||||
|
||||
zend_fcall_info_args(&fci, ctor_args);
|
||||
|
||||
|
@ -2589,6 +2589,7 @@ static void php_pgsql_fetch_hash(INTERNAL_FUNCTION_PARAMETERS, zend_long result_
|
||||
fci.retval = &retval;
|
||||
fci.params = NULL;
|
||||
fci.param_count = 0;
|
||||
fci.named_params = NULL;
|
||||
|
||||
if (ctor_params && Z_TYPE_P(ctor_params) != IS_NULL) {
|
||||
if (zend_fcall_info_args(&fci, ctor_params) == FAILURE) {
|
||||
|
@ -1796,18 +1796,19 @@ ZEND_METHOD(ReflectionFunctionAbstract, getStaticVariables)
|
||||
ZEND_METHOD(ReflectionFunction, invoke)
|
||||
{
|
||||
zval retval;
|
||||
zval *params = NULL;
|
||||
int result, num_args = 0;
|
||||
zval *params;
|
||||
int result, num_args;
|
||||
HashTable *named_params;
|
||||
zend_fcall_info fci;
|
||||
zend_fcall_info_cache fcc;
|
||||
reflection_object *intern;
|
||||
zend_function *fptr;
|
||||
|
||||
GET_REFLECTION_OBJECT_PTR(fptr);
|
||||
ZEND_PARSE_PARAMETERS_START(0, -1)
|
||||
Z_PARAM_VARIADIC_WITH_NAMED(params, num_args, named_params)
|
||||
ZEND_PARSE_PARAMETERS_END();
|
||||
|
||||
if (zend_parse_parameters(ZEND_NUM_ARGS(), "*", ¶ms, &num_args) == FAILURE) {
|
||||
RETURN_THROWS();
|
||||
}
|
||||
GET_REFLECTION_OBJECT_PTR(fptr);
|
||||
|
||||
fci.size = sizeof(fci);
|
||||
ZVAL_UNDEF(&fci.function_name);
|
||||
@ -1815,6 +1816,7 @@ ZEND_METHOD(ReflectionFunction, invoke)
|
||||
fci.retval = &retval;
|
||||
fci.param_count = num_args;
|
||||
fci.params = params;
|
||||
fci.named_params = named_params;
|
||||
|
||||
fcc.function_handler = fptr;
|
||||
fcc.called_scope = NULL;
|
||||
@ -1846,36 +1848,26 @@ ZEND_METHOD(ReflectionFunction, invoke)
|
||||
ZEND_METHOD(ReflectionFunction, invokeArgs)
|
||||
{
|
||||
zval retval;
|
||||
zval *params, *val;
|
||||
int result;
|
||||
int i, argc;
|
||||
zend_fcall_info fci;
|
||||
zend_fcall_info_cache fcc;
|
||||
reflection_object *intern;
|
||||
zend_function *fptr;
|
||||
zval *param_array;
|
||||
HashTable *params;
|
||||
|
||||
GET_REFLECTION_OBJECT_PTR(fptr);
|
||||
|
||||
if (zend_parse_parameters(ZEND_NUM_ARGS(), "a", ¶m_array) == FAILURE) {
|
||||
if (zend_parse_parameters(ZEND_NUM_ARGS(), "h", ¶ms) == FAILURE) {
|
||||
RETURN_THROWS();
|
||||
}
|
||||
|
||||
argc = zend_hash_num_elements(Z_ARRVAL_P(param_array));
|
||||
|
||||
params = safe_emalloc(sizeof(zval), argc, 0);
|
||||
argc = 0;
|
||||
ZEND_HASH_FOREACH_VAL(Z_ARRVAL_P(param_array), val) {
|
||||
ZVAL_COPY(¶ms[argc], val);
|
||||
argc++;
|
||||
} ZEND_HASH_FOREACH_END();
|
||||
|
||||
fci.size = sizeof(fci);
|
||||
ZVAL_UNDEF(&fci.function_name);
|
||||
fci.object = NULL;
|
||||
fci.retval = &retval;
|
||||
fci.param_count = argc;
|
||||
fci.params = params;
|
||||
fci.param_count = 0;
|
||||
fci.params = NULL;
|
||||
fci.named_params = params;
|
||||
|
||||
fcc.function_handler = fptr;
|
||||
fcc.called_scope = NULL;
|
||||
@ -1888,11 +1880,6 @@ ZEND_METHOD(ReflectionFunction, invokeArgs)
|
||||
|
||||
result = zend_call_function(&fci, &fcc);
|
||||
|
||||
for (i = 0; i < argc; i++) {
|
||||
zval_ptr_dtor(¶ms[i]);
|
||||
}
|
||||
efree(params);
|
||||
|
||||
if (result == FAILURE) {
|
||||
zend_throw_exception_ex(reflection_exception_ptr, 0,
|
||||
"Invocation of function %s() failed", ZSTR_VAL(fptr->common.function_name));
|
||||
@ -3127,14 +3114,14 @@ ZEND_METHOD(ReflectionMethod, getClosure)
|
||||
static void reflection_method_invoke(INTERNAL_FUNCTION_PARAMETERS, int variadic)
|
||||
{
|
||||
zval retval;
|
||||
zval *params = NULL, *val, *object;
|
||||
zval *params = NULL, *object;
|
||||
HashTable *named_params = NULL;
|
||||
reflection_object *intern;
|
||||
zend_function *mptr;
|
||||
int i, argc = 0, result;
|
||||
int argc = 0, result;
|
||||
zend_fcall_info fci;
|
||||
zend_fcall_info_cache fcc;
|
||||
zend_class_entry *obj_ce;
|
||||
zval *param_array;
|
||||
|
||||
GET_REFLECTION_OBJECT_PTR(mptr);
|
||||
|
||||
@ -3155,22 +3142,14 @@ static void reflection_method_invoke(INTERNAL_FUNCTION_PARAMETERS, int variadic)
|
||||
}
|
||||
|
||||
if (variadic) {
|
||||
if (zend_parse_parameters(ZEND_NUM_ARGS(), "o!*", &object, ¶ms, &argc) == FAILURE) {
|
||||
RETURN_THROWS();
|
||||
}
|
||||
ZEND_PARSE_PARAMETERS_START(1, -1)
|
||||
Z_PARAM_OBJECT_OR_NULL(object)
|
||||
Z_PARAM_VARIADIC_WITH_NAMED(params, argc, named_params)
|
||||
ZEND_PARSE_PARAMETERS_END();
|
||||
} else {
|
||||
if (zend_parse_parameters(ZEND_NUM_ARGS(), "o!a", &object, ¶m_array) == FAILURE) {
|
||||
if (zend_parse_parameters(ZEND_NUM_ARGS(), "o!h", &object, &named_params) == FAILURE) {
|
||||
RETURN_THROWS();
|
||||
}
|
||||
|
||||
argc = zend_hash_num_elements(Z_ARRVAL_P(param_array));
|
||||
|
||||
params = safe_emalloc(sizeof(zval), argc, 0);
|
||||
argc = 0;
|
||||
ZEND_HASH_FOREACH_VAL(Z_ARRVAL_P(param_array), val) {
|
||||
ZVAL_COPY(¶ms[argc], val);
|
||||
argc++;
|
||||
} ZEND_HASH_FOREACH_END();
|
||||
}
|
||||
|
||||
/* In case this is a static method, we shouldn't pass an object_ptr
|
||||
@ -3207,6 +3186,7 @@ static void reflection_method_invoke(INTERNAL_FUNCTION_PARAMETERS, int variadic)
|
||||
fci.retval = &retval;
|
||||
fci.param_count = argc;
|
||||
fci.params = params;
|
||||
fci.named_params = named_params;
|
||||
|
||||
fcc.function_handler = mptr;
|
||||
fcc.called_scope = intern->ce;
|
||||
@ -3221,13 +3201,6 @@ static void reflection_method_invoke(INTERNAL_FUNCTION_PARAMETERS, int variadic)
|
||||
|
||||
result = zend_call_function(&fci, &fcc);
|
||||
|
||||
if (!variadic) {
|
||||
for (i = 0; i < argc; i++) {
|
||||
zval_ptr_dtor(¶ms[i]);
|
||||
}
|
||||
efree(params);
|
||||
}
|
||||
|
||||
if (result == FAILURE) {
|
||||
zend_throw_exception_ex(reflection_exception_ptr, 0,
|
||||
"Invocation of method %s::%s() failed", ZSTR_VAL(mptr->common.scope->name), ZSTR_VAL(mptr->common.function_name));
|
||||
@ -4676,8 +4649,9 @@ ZEND_METHOD(ReflectionClass, newInstance)
|
||||
|
||||
/* Run the constructor if there is one */
|
||||
if (constructor) {
|
||||
zval *params = NULL;
|
||||
int i, num_args = 0;
|
||||
zval *params;
|
||||
int num_args;
|
||||
HashTable *named_params;
|
||||
|
||||
if (!(constructor->common.fn_flags & ZEND_ACC_PUBLIC)) {
|
||||
zend_throw_exception_ex(reflection_exception_ptr, 0, "Access to non-public constructor of class %s", ZSTR_VAL(ce->name));
|
||||
@ -4685,20 +4659,13 @@ ZEND_METHOD(ReflectionClass, newInstance)
|
||||
RETURN_NULL();
|
||||
}
|
||||
|
||||
if (zend_parse_parameters(ZEND_NUM_ARGS(), "*", ¶ms, &num_args) == FAILURE) {
|
||||
zval_ptr_dtor(return_value);
|
||||
RETURN_THROWS();
|
||||
}
|
||||
ZEND_PARSE_PARAMETERS_START(0, -1)
|
||||
Z_PARAM_VARIADIC_WITH_NAMED(params, num_args, named_params)
|
||||
ZEND_PARSE_PARAMETERS_END();
|
||||
|
||||
for (i = 0; i < num_args; i++) {
|
||||
Z_TRY_ADDREF(params[i]);
|
||||
}
|
||||
|
||||
zend_call_known_instance_method(constructor, Z_OBJ_P(return_value), NULL, num_args, params);
|
||||
|
||||
for (i = 0; i < num_args; i++) {
|
||||
zval_ptr_dtor(¶ms[i]);
|
||||
}
|
||||
zend_call_known_function(
|
||||
constructor, Z_OBJ_P(return_value), Z_OBJCE_P(return_value), NULL,
|
||||
num_args, params, named_params);
|
||||
|
||||
if (EG(exception)) {
|
||||
zend_object_store_ctor_failed(Z_OBJ_P(return_value));
|
||||
@ -4734,11 +4701,10 @@ ZEND_METHOD(ReflectionClass, newInstanceWithoutConstructor)
|
||||
/* {{{ Returns an instance of this class */
|
||||
ZEND_METHOD(ReflectionClass, newInstanceArgs)
|
||||
{
|
||||
zval *val;
|
||||
reflection_object *intern;
|
||||
zend_class_entry *ce, *old_scope;
|
||||
int i, argc = 0;
|
||||
HashTable *args;
|
||||
int argc = 0;
|
||||
HashTable *args = NULL;
|
||||
zend_function *constructor;
|
||||
|
||||
GET_REFLECTION_OBJECT_PTR(ce);
|
||||
@ -4747,8 +4713,8 @@ ZEND_METHOD(ReflectionClass, newInstanceArgs)
|
||||
RETURN_THROWS();
|
||||
}
|
||||
|
||||
if (ZEND_NUM_ARGS() > 0) {
|
||||
argc = args->nNumOfElements;
|
||||
if (args) {
|
||||
argc = zend_hash_num_elements(args);
|
||||
}
|
||||
|
||||
if (UNEXPECTED(object_init_ex(return_value, ce) != SUCCESS)) {
|
||||
@ -4762,31 +4728,14 @@ ZEND_METHOD(ReflectionClass, newInstanceArgs)
|
||||
|
||||
/* Run the constructor if there is one */
|
||||
if (constructor) {
|
||||
zval *params = NULL;
|
||||
|
||||
if (!(constructor->common.fn_flags & ZEND_ACC_PUBLIC)) {
|
||||
zend_throw_exception_ex(reflection_exception_ptr, 0, "Access to non-public constructor of class %s", ZSTR_VAL(ce->name));
|
||||
zval_ptr_dtor(return_value);
|
||||
RETURN_NULL();
|
||||
}
|
||||
|
||||
if (argc) {
|
||||
params = safe_emalloc(sizeof(zval), argc, 0);
|
||||
argc = 0;
|
||||
ZEND_HASH_FOREACH_VAL(args, val) {
|
||||
ZVAL_COPY(¶ms[argc], val);
|
||||
argc++;
|
||||
} ZEND_HASH_FOREACH_END();
|
||||
}
|
||||
|
||||
zend_call_known_instance_method(constructor, Z_OBJ_P(return_value), NULL, argc, params);
|
||||
|
||||
if (params) {
|
||||
for (i = 0; i < argc; i++) {
|
||||
zval_ptr_dtor(¶ms[i]);
|
||||
}
|
||||
efree(params);
|
||||
}
|
||||
zend_call_known_function(
|
||||
constructor, Z_OBJ_P(return_value), Z_OBJCE_P(return_value), NULL, 0, NULL, args);
|
||||
|
||||
if (EG(exception)) {
|
||||
zend_object_store_ctor_failed(Z_OBJ_P(return_value));
|
||||
@ -6273,12 +6222,17 @@ ZEND_METHOD(ReflectionAttribute, getArguments)
|
||||
RETURN_THROWS();
|
||||
}
|
||||
|
||||
add_next_index_zval(return_value, &tmp);
|
||||
if (attr->data->args[i].name) {
|
||||
/* We ensured at compile-time that there are no duplicate parameter names. */
|
||||
zend_hash_add_new(Z_ARRVAL_P(return_value), attr->data->args[i].name, &tmp);
|
||||
} else {
|
||||
add_next_index_zval(return_value, &tmp);
|
||||
}
|
||||
}
|
||||
}
|
||||
/* }}} */
|
||||
|
||||
static int call_attribute_constructor(zend_class_entry *ce, zend_object *obj, zval *args, uint32_t argc) /* {{{ */
|
||||
static int call_attribute_constructor(zend_class_entry *ce, zend_object *obj, zval *args, uint32_t argc, HashTable *named_params) /* {{{ */
|
||||
{
|
||||
zend_function *ctor = ce->constructor;
|
||||
ZEND_ASSERT(ctor != NULL);
|
||||
@ -6288,7 +6242,8 @@ static int call_attribute_constructor(zend_class_entry *ce, zend_object *obj, zv
|
||||
return FAILURE;
|
||||
}
|
||||
|
||||
zend_call_known_instance_method(ctor, obj, NULL, argc, args);
|
||||
zend_call_known_function(ctor, obj, obj->ce, NULL, argc, args, named_params);
|
||||
|
||||
if (EG(exception)) {
|
||||
zend_object_store_ctor_failed(obj);
|
||||
return FAILURE;
|
||||
@ -6298,7 +6253,8 @@ static int call_attribute_constructor(zend_class_entry *ce, zend_object *obj, zv
|
||||
}
|
||||
/* }}} */
|
||||
|
||||
static void attribute_ctor_cleanup(zval *obj, zval *args, uint32_t argc) /* {{{ */
|
||||
static void attribute_ctor_cleanup(
|
||||
zval *obj, zval *args, uint32_t argc, HashTable *named_params) /* {{{ */
|
||||
{
|
||||
if (obj) {
|
||||
zval_ptr_dtor(obj);
|
||||
@ -6313,6 +6269,10 @@ static void attribute_ctor_cleanup(zval *obj, zval *args, uint32_t argc) /* {{{
|
||||
|
||||
efree(args);
|
||||
}
|
||||
|
||||
if (named_params) {
|
||||
zend_array_destroy(named_params);
|
||||
}
|
||||
}
|
||||
/* }}} */
|
||||
|
||||
@ -6327,8 +6287,7 @@ ZEND_METHOD(ReflectionAttribute, newInstance)
|
||||
zval obj;
|
||||
|
||||
zval *args = NULL;
|
||||
uint32_t count;
|
||||
uint32_t argc = 0;
|
||||
HashTable *named_params = NULL;
|
||||
|
||||
if (zend_parse_parameters_none() == FAILURE) {
|
||||
RETURN_THROWS();
|
||||
@ -6385,31 +6344,40 @@ ZEND_METHOD(ReflectionAttribute, newInstance)
|
||||
RETURN_THROWS();
|
||||
}
|
||||
|
||||
count = attr->data->argc;
|
||||
uint32_t argc = 0;
|
||||
if (attr->data->argc) {
|
||||
args = emalloc(attr->data->argc * sizeof(zval));
|
||||
|
||||
if (count) {
|
||||
args = emalloc(count * sizeof(zval));
|
||||
|
||||
for (argc = 0; argc < attr->data->argc; argc++) {
|
||||
if (FAILURE == zend_get_attribute_value(&args[argc], attr->data, argc, attr->scope)) {
|
||||
attribute_ctor_cleanup(&obj, args, argc);
|
||||
for (uint32_t i = 0; i < attr->data->argc; i++) {
|
||||
zval val;
|
||||
if (FAILURE == zend_get_attribute_value(&val, attr->data, i, attr->scope)) {
|
||||
attribute_ctor_cleanup(&obj, args, i, named_params);
|
||||
RETURN_THROWS();
|
||||
}
|
||||
if (attr->data->args[i].name) {
|
||||
if (!named_params) {
|
||||
named_params = zend_new_array(0);
|
||||
}
|
||||
zend_hash_add_new(named_params, attr->data->args[i].name, &val);
|
||||
} else {
|
||||
ZVAL_COPY_VALUE(&args[i], &val);
|
||||
argc++;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (ce->constructor) {
|
||||
if (FAILURE == call_attribute_constructor(ce, Z_OBJ(obj), args, argc)) {
|
||||
attribute_ctor_cleanup(&obj, args, argc);
|
||||
if (FAILURE == call_attribute_constructor(ce, Z_OBJ(obj), args, argc, named_params)) {
|
||||
attribute_ctor_cleanup(&obj, args, argc, named_params);
|
||||
RETURN_THROWS();
|
||||
}
|
||||
} else if (argc) {
|
||||
attribute_ctor_cleanup(&obj, args, argc);
|
||||
} else if (argc || named_params) {
|
||||
attribute_ctor_cleanup(&obj, args, argc, named_params);
|
||||
zend_throw_error(NULL, "Attribute class %s does not have a constructor, cannot pass arguments", ZSTR_VAL(ce->name));
|
||||
RETURN_THROWS();
|
||||
}
|
||||
|
||||
attribute_ctor_cleanup(NULL, args, argc);
|
||||
attribute_ctor_cleanup(NULL, args, argc, named_params);
|
||||
|
||||
RETURN_COPY_VALUE(&obj);
|
||||
}
|
||||
|
@ -437,7 +437,7 @@ static zend_class_entry *spl_perform_autoload(zend_string *class_name, zend_stri
|
||||
|
||||
zval param;
|
||||
ZVAL_STR(¶m, class_name);
|
||||
zend_call_known_function(func, alfi->obj, alfi->ce, NULL, 1, ¶m);
|
||||
zend_call_known_function(func, alfi->obj, alfi->ce, NULL, 1, ¶m, NULL);
|
||||
if (EG(exception)) {
|
||||
break;
|
||||
}
|
||||
|
@ -1904,6 +1904,7 @@ static int spl_filesystem_file_call(spl_filesystem_object *intern, zend_function
|
||||
fci.retval = return_value;
|
||||
fci.param_count = num_args;
|
||||
fci.params = params;
|
||||
fci.named_params = NULL;
|
||||
ZVAL_STR(&fci.function_name, func_ptr->common.function_name);
|
||||
|
||||
fcic.function_handler = func_ptr;
|
||||
|
@ -1549,7 +1549,7 @@ PHP_FUNCTION(call_user_func)
|
||||
|
||||
ZEND_PARSE_PARAMETERS_START(1, -1)
|
||||
Z_PARAM_FUNC(fci, fci_cache)
|
||||
Z_PARAM_VARIADIC('*', fci.params, fci.param_count)
|
||||
Z_PARAM_VARIADIC_WITH_NAMED(fci.params, fci.param_count, fci.named_params)
|
||||
ZEND_PARSE_PARAMETERS_END();
|
||||
|
||||
fci.retval = &retval;
|
||||
@ -1567,16 +1567,17 @@ PHP_FUNCTION(call_user_func)
|
||||
Warning: This function is special-cased by zend_compile.c and so is usually bypassed */
|
||||
PHP_FUNCTION(call_user_func_array)
|
||||
{
|
||||
zval *params, retval;
|
||||
zval retval;
|
||||
HashTable *params;
|
||||
zend_fcall_info fci;
|
||||
zend_fcall_info_cache fci_cache;
|
||||
|
||||
ZEND_PARSE_PARAMETERS_START(2, 2)
|
||||
Z_PARAM_FUNC(fci, fci_cache)
|
||||
Z_PARAM_ARRAY(params)
|
||||
Z_PARAM_ARRAY_HT(params)
|
||||
ZEND_PARSE_PARAMETERS_END();
|
||||
|
||||
zend_fcall_info_args(&fci, params);
|
||||
fci.named_params = params;
|
||||
fci.retval = &retval;
|
||||
|
||||
if (zend_call_function(&fci, &fci_cache) == SUCCESS && Z_TYPE(retval) != IS_UNDEF) {
|
||||
@ -1585,8 +1586,6 @@ PHP_FUNCTION(call_user_func_array)
|
||||
}
|
||||
ZVAL_COPY_VALUE(return_value, &retval);
|
||||
}
|
||||
|
||||
zend_fcall_info_args_clear(&fci, 1);
|
||||
}
|
||||
/* }}} */
|
||||
|
||||
|
@ -464,6 +464,7 @@ static void xml_call_handler(xml_parser *parser, zval *handler, zend_function *f
|
||||
fci.retval = retval;
|
||||
fci.param_count = argc;
|
||||
fci.params = argv;
|
||||
fci.named_params = NULL;
|
||||
|
||||
result = zend_call_function(&fci, NULL);
|
||||
if (result == FAILURE) {
|
||||
|
@ -219,6 +219,7 @@ static void xsl_ext_function_php(xmlXPathParserContextPtr ctxt, int nargs, int t
|
||||
}
|
||||
|
||||
fci.size = sizeof(fci);
|
||||
fci.named_params = NULL;
|
||||
if (fci.param_count > 0) {
|
||||
fci.params = args;
|
||||
} else {
|
||||
|
Loading…
Reference in New Issue
Block a user