Skip to content
Draft
Show file tree
Hide file tree
Changes from 6 commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
5 changes: 5 additions & 0 deletions Zend/Optimizer/compact_literals.c
Original file line number Diff line number Diff line change
Expand Up @@ -734,12 +734,17 @@ void zend_optimizer_compact_literals(zend_op_array *op_array, zend_optimizer_ctx
case ZEND_SEND_VAR_NO_REF_EX:
case ZEND_SEND_REF:
case ZEND_SEND_FUNC_ARG:
case ZEND_SEND_PLACEHOLDER:
case ZEND_CHECK_FUNC_ARG:
if (opline->op2_type == IS_CONST) {
opline->result.num = cache_size;
cache_size += 2 * sizeof(void *);
}
break;
case ZEND_CALLABLE_CONVERT_PARTIAL:
opline->op1.num = cache_size;
cache_size += 2 * sizeof(void *);
break;
}
opline++;
}
Expand Down
6 changes: 4 additions & 2 deletions Zend/Optimizer/optimize_func_calls.c
Original file line number Diff line number Diff line change
Expand Up @@ -193,6 +193,7 @@ void zend_optimize_func_calls(zend_op_array *op_array, zend_optimizer_ctx *ctx)
case ZEND_DO_UCALL:
case ZEND_DO_FCALL_BY_NAME:
case ZEND_CALLABLE_CONVERT:
case ZEND_CALLABLE_CONVERT_PARTIAL:
call--;
if (call_stack[call].func && call_stack[call].opline) {
zend_op *fcall = call_stack[call].opline;
Expand Down Expand Up @@ -225,13 +226,14 @@ void zend_optimize_func_calls(zend_op_array *op_array, zend_optimizer_ctx *ctx)
* At this point we also know whether or not the result of
* the DO opcode is used, allowing to optimize calls to
* ZEND_ACC_NODISCARD functions. */
if (opline->opcode != ZEND_CALLABLE_CONVERT) {
if (opline->opcode != ZEND_CALLABLE_CONVERT && opline->opcode != ZEND_CALLABLE_CONVERT_PARTIAL) {
opline->opcode = zend_get_call_op(fcall, call_stack[call].func, !RESULT_UNUSED(opline));
}

if ((ZEND_OPTIMIZER_PASS_16 & ctx->optimization_level)
&& call_stack[call].try_inline
&& opline->opcode != ZEND_CALLABLE_CONVERT) {
&& opline->opcode != ZEND_CALLABLE_CONVERT
&& opline->opcode != ZEND_CALLABLE_CONVERT_PARTIAL) {
zend_try_inline_call(op_array, fcall, opline, call_stack[call].func);
}
}
Expand Down
2 changes: 2 additions & 0 deletions Zend/Optimizer/zend_call_graph.c
Original file line number Diff line number Diff line change
Expand Up @@ -128,6 +128,7 @@ ZEND_API void zend_analyze_calls(zend_arena **arena, zend_script *script, uint32
case ZEND_DO_UCALL:
case ZEND_DO_FCALL_BY_NAME:
case ZEND_CALLABLE_CONVERT:
case ZEND_CALLABLE_CONVERT_PARTIAL:
func_info->flags |= ZEND_FUNC_HAS_CALLS;
if (call_info) {
call_info->caller_call_opline = opline;
Expand All @@ -144,6 +145,7 @@ ZEND_API void zend_analyze_calls(zend_arena **arena, zend_script *script, uint32
case ZEND_SEND_VAR_NO_REF:
case ZEND_SEND_VAR_NO_REF_EX:
case ZEND_SEND_USER:
case ZEND_SEND_PLACEHOLDER:
if (call_info) {
if (opline->op2_type == IS_CONST) {
call_info->named_args = 1;
Expand Down
1 change: 1 addition & 0 deletions Zend/Optimizer/zend_inference.c
Original file line number Diff line number Diff line change
Expand Up @@ -3905,6 +3905,7 @@ static zend_always_inline zend_result _zend_update_type_info(
}
break;
case ZEND_CALLABLE_CONVERT:
case ZEND_CALLABLE_CONVERT_PARTIAL:
UPDATE_SSA_TYPE(MAY_BE_OBJECT | MAY_BE_RC1 | MAY_BE_RCN, ssa_op->result_def);
UPDATE_SSA_OBJ_TYPE(zend_ce_closure, /* is_instanceof */ false, ssa_op->result_def);
break;
Expand Down
92 changes: 92 additions & 0 deletions Zend/tests/partial_application/attributes_001.phpt
Original file line number Diff line number Diff line change
@@ -0,0 +1,92 @@
--TEST--
Closure application copies attributes
--XFAIL--
Only NoDiscard and SensitiveParameter are copied
--FILE--
<?php

#[Attribute]
class Test {}

#[NoDiscard]
function f($a, #[SensitiveParameter] $b, #[Test] ...$c) {
}

function dump_attributes($function) {
echo "Function attributes:\n";
$r = new ReflectionFunction($function);
var_dump($r->getAttributes());

foreach ($r->getParameters() as $i => $p) {
echo "Parameter $i:\n";
var_dump($p->getAttributes());
}
}

dump_attributes('f');

$f = f(1, ?, ?, ...);

dump_attributes($f);

?>
--EXPECTF--
Function attributes:
array(1) {
[0]=>
object(ReflectionAttribute)#%d (1) {
["name"]=>
string(9) "NoDiscard"
}
}
Parameter 0:
array(0) {
}
Parameter 1:
array(1) {
[0]=>
object(ReflectionAttribute)#%d (1) {
["name"]=>
string(18) "SensitiveParameter"
}
}
Parameter 2:
array(1) {
[0]=>
object(ReflectionAttribute)#%d (1) {
["name"]=>
string(4) "Test"
}
}
Function attributes:
array(1) {
[0]=>
object(ReflectionAttribute)#%d (1) {
["name"]=>
string(9) "NoDiscard"
}
}
Parameter 0:
array(1) {
[0]=>
object(ReflectionAttribute)#%d (1) {
["name"]=>
string(18) "SensitiveParameter"
}
}
Parameter 1:
array(1) {
[0]=>
object(ReflectionAttribute)#%d (1) {
["name"]=>
string(4) "Test"
}
}
Parameter 2:
array(1) {
[0]=>
object(ReflectionAttribute)#%d (1) {
["name"]=>
string(4) "Test"
}
}
53 changes: 53 additions & 0 deletions Zend/tests/partial_application/attributes_002.phpt
Original file line number Diff line number Diff line change
@@ -0,0 +1,53 @@
--TEST--
Closure application preserves #[SensitiveParameter]
--FILE--
<?php

function f($a, #[SensitiveParameter] $b, $c) {
throw new Exception();
}

echo "# During partial application:\n";

try {
$f = f(1, 'sensitive');
} catch (Error $e) {
echo $e, "\n\n";
}

echo "# In trampoline:\n";

try {
$f = f(1, ?, ?)('sensitive');
} catch (Error $e) {
echo $e, "\n\n";
}

echo "# In execution:\n";

try {
$f = f(1, ?, ?)('sensitive', 3);
} catch (Exception $e) {
echo $e, "\n";
}

?>
--EXPECTF--
# During partial application:
ArgumentCountError: Too few arguments to function f(), 2 passed in %s on line %d and exactly 3 expected in %s:%d
Stack trace:
#0 %s(%d): f(1, Object(SensitiveParameterValue))
#1 {main}

# In trampoline:
ArgumentCountError: Too few arguments to function {closure:%s:%d}(), 1 passed in %s on line %d and exactly 2 expected in %s:%d
Stack trace:
#0 %s(%d): {closure:%s}(Object(SensitiveParameterValue))
#1 {main}

# In execution:
Exception in %s:%d
Stack trace:
#0 %s(%d): f(1, Object(SensitiveParameterValue), 3)
#1 %s(%d): {closure:%s}(Object(SensitiveParameterValue), 3)
#2 {main}
16 changes: 16 additions & 0 deletions Zend/tests/partial_application/attributes_003.phpt
Original file line number Diff line number Diff line change
@@ -0,0 +1,16 @@
--TEST--
Closure application preserves #[NoDiscard]
--FILE--
<?php

#[NoDiscard] function f($a) {
}

$f = f(?);
$f(1);

(void) $f(1);

?>
--EXPECTF--
Warning: The return value of function {closure:%s}() should either be used or intentionally ignored by casting it as (void) in %s on line 7
48 changes: 48 additions & 0 deletions Zend/tests/partial_application/clone.phpt
Original file line number Diff line number Diff line change
@@ -0,0 +1,48 @@
--TEST--
Closure application clone
--FILE--
<?php

class C {
public $a;
public $b;
}

$clone = clone(?);
var_dump($clone(new C));

$clone = clone(...);
var_dump($clone(new C));

$clone = clone(new C, ?);
var_dump($clone(['a' => 1]));

$clone = clone(?, ['a' => 1]);
var_dump($clone(new C));

?>
--EXPECTF--
object(C)#%d (2) {
["a"]=>
NULL
["b"]=>
NULL
}
object(C)#%d (2) {
["a"]=>
NULL
["b"]=>
NULL
}
object(C)#%d (2) {
["a"]=>
int(1)
["b"]=>
NULL
}
object(C)#%d (2) {
["a"]=>
int(1)
["b"]=>
NULL
}
9 changes: 9 additions & 0 deletions Zend/tests/partial_application/compile_errors_001.phpt
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
--TEST--
Closure application compile errors: multiple ...
--FILE--
<?php
foo(..., ...);
?>
--EXPECTF--
Fatal error: Variadic placeholder may only appear once in %s on line %d

9 changes: 9 additions & 0 deletions Zend/tests/partial_application/compile_errors_002.phpt
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
--TEST--
Closure application compile errors: only named arguments after ...
--FILE--
<?php
foo(..., ?);
?>
--EXPECTF--
Fatal error: Only named arguments may follow variadic placeholder in %s on line %d

9 changes: 9 additions & 0 deletions Zend/tests/partial_application/compile_errors_003.phpt
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
--TEST--
Closure application compile errors: named arguments must come after placeholder
--FILE--
<?php
foo(n: 5, ?);
?>
--EXPECTF--
Fatal error: Cannot use positional argument after named argument in %s on line %d

8 changes: 8 additions & 0 deletions Zend/tests/partial_application/compile_errors_004.phpt
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
--TEST--
Closure application compile errors: named arguments must come after variadic placeholder
--FILE--
<?php
foo(n: 5, ...);
?>
--EXPECTF--
Fatal error: Cannot use positional argument after named argument in %s on line %d
9 changes: 9 additions & 0 deletions Zend/tests/partial_application/compile_errors_005.phpt
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
--TEST--
Closure application compile errors: follow variadic with un-named arg
--FILE--
<?php
foo(..., $a);
?>
--EXPECTF--
Fatal error: Only named arguments may follow variadic placeholder in %s on line %d

9 changes: 9 additions & 0 deletions Zend/tests/partial_application/compile_errors_006.phpt
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
--TEST--
Closure application compile errors: mix application with unpack (placeholder after)
--FILE--
<?php
foo(...["foo" => "bar"], ...);
?>
--EXPECTF--
Fatal error: Cannot combine partial application and unpacking in %s on line %d

9 changes: 9 additions & 0 deletions Zend/tests/partial_application/compile_errors_007.phpt
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
--TEST--
Closure application compile errors: mix application with unpack (placeholder before)
--FILE--
<?php
foo(..., ...["foo" => "bar"]);
?>
--EXPECTF--
Fatal error: Cannot combine partial application and unpacking %s on line %d

37 changes: 37 additions & 0 deletions Zend/tests/partial_application/errors_001.phpt
Original file line number Diff line number Diff line change
@@ -0,0 +1,37 @@
--TEST--
Closure application errors: placeholder count errors
--FILE--
<?php
function foo($a, $b, $c) {

}

try {
foo(?);
} catch (Error $ex) {
printf("%s\n", $ex->getMessage());
}

try {
foo(?, ?, ?, ?);
} catch (Error $ex) {
printf("%s\n", $ex->getMessage());
}

try {
property_exists(?);
} catch (Error $ex) {
printf("%s\n", $ex->getMessage());
}

try {
usleep(?, ?);
} catch (Error $ex) {
printf("%s\n", $ex->getMessage());
}
?>
--EXPECT--
not enough arguments or placeholders for application of foo, 1 given and exactly 3 expected
too many arguments or placeholders for application of foo, 4 given and a maximum of 3 expected
not enough arguments or placeholders for application of property_exists, 1 given and exactly 2 expected
too many arguments or placeholders for application of usleep, 2 given and a maximum of 1 expected
Loading
Loading