diff --git a/Zend/Optimizer/compact_literals.c b/Zend/Optimizer/compact_literals.c index d0aaccec7ce2c..28a506f689127 100644 --- a/Zend/Optimizer/compact_literals.c +++ b/Zend/Optimizer/compact_literals.c @@ -734,6 +734,7 @@ 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; diff --git a/Zend/Optimizer/optimize_func_calls.c b/Zend/Optimizer/optimize_func_calls.c index 8b29f47c94976..39b62dc4d7af5 100644 --- a/Zend/Optimizer/optimize_func_calls.c +++ b/Zend/Optimizer/optimize_func_calls.c @@ -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; @@ -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); } } diff --git a/Zend/Optimizer/zend_call_graph.c b/Zend/Optimizer/zend_call_graph.c index 8a2f8ea2a7e1a..68ede34d88d00 100644 --- a/Zend/Optimizer/zend_call_graph.c +++ b/Zend/Optimizer/zend_call_graph.c @@ -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; @@ -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; diff --git a/Zend/Optimizer/zend_inference.c b/Zend/Optimizer/zend_inference.c index fc6b9b421b628..68bc2d4ce2b66 100644 --- a/Zend/Optimizer/zend_inference.c +++ b/Zend/Optimizer/zend_inference.c @@ -3901,6 +3901,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; diff --git a/Zend/tests/partial_application/attributes_001.phpt b/Zend/tests/partial_application/attributes_001.phpt new file mode 100644 index 0000000000000..152a57c6aa2c4 --- /dev/null +++ b/Zend/tests/partial_application/attributes_001.phpt @@ -0,0 +1,90 @@ +--TEST-- +Partial application copies attributes +--FILE-- +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" + } +} diff --git a/Zend/tests/partial_application/attributes_002.phpt b/Zend/tests/partial_application/attributes_002.phpt new file mode 100644 index 0000000000000..189d002c63a95 --- /dev/null +++ b/Zend/tests/partial_application/attributes_002.phpt @@ -0,0 +1,52 @@ +--TEST-- +Partial application preserves #[SensitiveParameter] +--FILE-- + +--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: +Error: not enough arguments for application of f, 1 given and exactly 2 expected, declared in %s on line %d in %s:%d +Stack trace: +#0 %s(%d): Closure->f(Object(SensitiveParameterValue)) +#1 {main} + +# In execution: +Exception in %s:%d +Stack trace: +#0 %s(%d): f(1, Object(SensitiveParameterValue), 3) +#1 {main} diff --git a/Zend/tests/partial_application/attributes_003.phpt b/Zend/tests/partial_application/attributes_003.phpt new file mode 100644 index 0000000000000..e907cb65a3769 --- /dev/null +++ b/Zend/tests/partial_application/attributes_003.phpt @@ -0,0 +1,16 @@ +--TEST-- +Partial application preserves #[NoDiscard] +--FILE-- + +--EXPECTF-- +Warning: The return value of function f() should either be used or intentionally ignored by casting it as (void) in %s on line 7 diff --git a/Zend/tests/partial_application/attributes_004.phpt b/Zend/tests/partial_application/attributes_004.phpt new file mode 100644 index 0000000000000..b887849c22325 --- /dev/null +++ b/Zend/tests/partial_application/attributes_004.phpt @@ -0,0 +1,14 @@ +--TEST-- +Partial application preserves #[Deprecated] +--FILE-- + +--EXPECTF-- +Deprecated: Function f() is deprecated in %s on line 7 diff --git a/Zend/tests/partial_application/compile_errors_001.phpt b/Zend/tests/partial_application/compile_errors_001.phpt new file mode 100644 index 0000000000000..73eafa4e2cd45 --- /dev/null +++ b/Zend/tests/partial_application/compile_errors_001.phpt @@ -0,0 +1,9 @@ +--TEST-- +Partial application compile errors: multiple ... +--FILE-- + +--EXPECTF-- +Fatal error: Variadic placeholder may only appear once in %s on line %d + diff --git a/Zend/tests/partial_application/compile_errors_002.phpt b/Zend/tests/partial_application/compile_errors_002.phpt new file mode 100644 index 0000000000000..29a346ec88a5f --- /dev/null +++ b/Zend/tests/partial_application/compile_errors_002.phpt @@ -0,0 +1,9 @@ +--TEST-- +Partial application compile errors: only named arguments after ... +--FILE-- + +--EXPECTF-- +Fatal error: Only named arguments may follow variadic placeholder in %s on line %d + diff --git a/Zend/tests/partial_application/compile_errors_003.phpt b/Zend/tests/partial_application/compile_errors_003.phpt new file mode 100644 index 0000000000000..c6b856a7199b2 --- /dev/null +++ b/Zend/tests/partial_application/compile_errors_003.phpt @@ -0,0 +1,9 @@ +--TEST-- +Partial application compile errors: named arguments must come after placeholder +--FILE-- + +--EXPECTF-- +Fatal error: Cannot use positional argument after named argument in %s on line %d + diff --git a/Zend/tests/partial_application/compile_errors_004.phpt b/Zend/tests/partial_application/compile_errors_004.phpt new file mode 100644 index 0000000000000..ad819416f07a0 --- /dev/null +++ b/Zend/tests/partial_application/compile_errors_004.phpt @@ -0,0 +1,8 @@ +--TEST-- +Partial application compile errors: named arguments must come after variadic placeholder +--FILE-- + +--EXPECTF-- +Fatal error: Cannot use positional argument after named argument in %s on line %d diff --git a/Zend/tests/partial_application/compile_errors_005.phpt b/Zend/tests/partial_application/compile_errors_005.phpt new file mode 100644 index 0000000000000..b6a898008617b --- /dev/null +++ b/Zend/tests/partial_application/compile_errors_005.phpt @@ -0,0 +1,9 @@ +--TEST-- +Partial application compile errors: follow variadic with un-named arg +--FILE-- + +--EXPECTF-- +Fatal error: Only named arguments may follow variadic placeholder in %s on line %d + diff --git a/Zend/tests/partial_application/compile_errors_006.phpt b/Zend/tests/partial_application/compile_errors_006.phpt new file mode 100644 index 0000000000000..4657f56207c88 --- /dev/null +++ b/Zend/tests/partial_application/compile_errors_006.phpt @@ -0,0 +1,9 @@ +--TEST-- +Partial application compile errors: mix application with unpack (placeholder after) +--FILE-- + "bar"], ...); +?> +--EXPECTF-- +Fatal error: Cannot combine partial application and unpacking in %s on line %d + diff --git a/Zend/tests/partial_application/compile_errors_007.phpt b/Zend/tests/partial_application/compile_errors_007.phpt new file mode 100644 index 0000000000000..5cc881ebf60f5 --- /dev/null +++ b/Zend/tests/partial_application/compile_errors_007.phpt @@ -0,0 +1,9 @@ +--TEST-- +Partial application compile errors: mix application with unpack (placeholder before) +--FILE-- + "bar"]); +?> +--EXPECTF-- +Fatal error: Cannot combine partial application and unpacking %s on line %d + diff --git a/Zend/tests/partial_application/errors_001.phpt b/Zend/tests/partial_application/errors_001.phpt new file mode 100644 index 0000000000000..d6e37051c1eca --- /dev/null +++ b/Zend/tests/partial_application/errors_001.phpt @@ -0,0 +1,38 @@ +--TEST-- +Partial application errors: placeholder count errors +--FILE-- +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()); +} +?> +--EXPECTF-- +not enough arguments or placeholders for application of foo, 1 given and exactly 3 expected, declared in %s on line 2 +too many arguments or placeholders for application of foo, 4 given and a maximum of 3 expected, declared in %s on line 2 +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 + diff --git a/Zend/tests/partial_application/errors_002.phpt b/Zend/tests/partial_application/errors_002.phpt new file mode 100644 index 0000000000000..2f852348fb1fd --- /dev/null +++ b/Zend/tests/partial_application/errors_002.phpt @@ -0,0 +1,17 @@ +--TEST-- +Partial application errors: named parameter overwrites placeholder +--FILE-- +getMessage()); +} +?> +--EXPECT-- +Named parameter $a overwrites previous placeholder + diff --git a/Zend/tests/partial_application/errors_003.phpt b/Zend/tests/partial_application/errors_003.phpt new file mode 100644 index 0000000000000..dd5841ffcdc52 --- /dev/null +++ b/Zend/tests/partial_application/errors_003.phpt @@ -0,0 +1,78 @@ +--TEST-- +Partial application errors: missing parameters +--FILE-- +getMessage()); +} + +$foo = foo(?, ?); + +try { + $foo(1); +} catch (Error $ex) { + printf("%s\n", $ex->getMessage()); +} + +$bar = bar(?, ?, ...); + +try { + $bar(1); +} catch (Error $ex) { + printf("%s\n", $ex->getMessage()); +} + +class Foo { + public function bar($a, ...$b) {} +} + +$foo = new Foo; + +$bar = $foo->bar(?); + +try { + $bar(); +} catch (Error $ex) { + printf("%s\n", $ex->getMessage()); +} + +$repeat = str_repeat('a', ...); + +try { + $repeat(); +} catch (Error $ex) { + printf("%s\n", $ex->getMessage()); +} + +$usleep = usleep(?); + +try { + $usleep(); +} catch (Error $ex) { + printf("%s\n", $ex->getMessage()); +} + +try { + $usleep(1, 2); +} catch (Error $ex) { + printf("%s\n", $ex->getMessage()); +} +?> +--EXPECTF-- +not enough arguments for application of foo, 0 given and exactly 1 expected, declared in %s on line 8 +not enough arguments for application of foo, 1 given and exactly 2 expected, declared in %s on line 16 +not enough arguments for application of bar, 1 given and at least 3 expected, declared in %s on line 24 +not enough arguments for application of Foo::bar, 0 given and exactly 1 expected, declared in %s on line 38 +not enough arguments for application of str_repeat, 0 given and at least 1 expected, declared in %s on line 46 +not enough arguments for application of usleep, 0 given and exactly 1 expected, declared in %s on line 54 +too many arguments for application of usleep, 2 given and a maximum of 1 expected, declared in %s on line 54 diff --git a/Zend/tests/partial_application/export_001.phpt b/Zend/tests/partial_application/export_001.phpt new file mode 100644 index 0000000000000..b4a38de342a33 --- /dev/null +++ b/Zend/tests/partial_application/export_001.phpt @@ -0,0 +1,14 @@ +--TEST-- +Partial application ast export +--INI-- +assert.exception=1 +--FILE-- +getMessage()); +} +?> +--EXPECT-- +assert(0 && foo(?) && foo(new stdClass(), ...)) diff --git a/Zend/tests/partial_application/extra_collect_001.phpt b/Zend/tests/partial_application/extra_collect_001.phpt new file mode 100644 index 0000000000000..c5b44576f0e15 --- /dev/null +++ b/Zend/tests/partial_application/extra_collect_001.phpt @@ -0,0 +1,50 @@ +--TEST-- +Partial application named parameters: extra collection +--FILE-- + +--EXPECT-- +array(3) { + ["foo"]=> + string(3) "foo" + ["bar"]=> + string(3) "bar" + ["baz"]=> + string(3) "baz" +} +array(2) { + ["bar"]=> + string(3) "bar" + ["baz"]=> + string(3) "baz" +} +array(2) { + ["foo"]=> + string(3) "foo" + ["bar"]=> + string(3) "bar" +} + diff --git a/Zend/tests/partial_application/fuzz_001.phpt b/Zend/tests/partial_application/fuzz_001.phpt new file mode 100644 index 0000000000000..aa3502d066ad2 --- /dev/null +++ b/Zend/tests/partial_application/fuzz_001.phpt @@ -0,0 +1,16 @@ +--TEST-- +Partial application fuzz 001 +--FILE-- + +--EXPECTF-- +Partial [ function {closure:%s:%d} ] { + @@ %s 4 - 4 + + - Parameters [1] { + Parameter #0 [ $b ] + } +} diff --git a/Zend/tests/partial_application/fuzz_002.phpt b/Zend/tests/partial_application/fuzz_002.phpt new file mode 100644 index 0000000000000..46461b48bab9b --- /dev/null +++ b/Zend/tests/partial_application/fuzz_002.phpt @@ -0,0 +1,13 @@ +--TEST-- +Partial application fuzz 002 +--FILE-- + +--EXPECTF-- +OK diff --git a/Zend/tests/partial_application/fuzz_003.phpt b/Zend/tests/partial_application/fuzz_003.phpt new file mode 100644 index 0000000000000..32a11540be1b1 --- /dev/null +++ b/Zend/tests/partial_application/fuzz_003.phpt @@ -0,0 +1,20 @@ +--TEST-- +Partial application fuzz 003 +--FILE-- +method(1,...); +$bar(2); +?> +--EXPECT-- +array(2) { + [0]=> + int(1) + [1]=> + int(2) +} diff --git a/Zend/tests/partial_application/fuzz_004.phpt b/Zend/tests/partial_application/fuzz_004.phpt new file mode 100644 index 0000000000000..60e26ef120a70 --- /dev/null +++ b/Zend/tests/partial_application/fuzz_004.phpt @@ -0,0 +1,19 @@ +--TEST-- +Partial application fuzz 004 +--FILE-- +__invoke(UNDEFINED); +} catch (\Throwable $e) { + echo $e->getMessage(), "\n"; +} + +?> +--EXPECT-- +Undefined constant "UNDEFINED" diff --git a/Zend/tests/partial_application/fuzz_005.phpt b/Zend/tests/partial_application/fuzz_005.phpt new file mode 100644 index 0000000000000..a9927667078b4 --- /dev/null +++ b/Zend/tests/partial_application/fuzz_005.phpt @@ -0,0 +1,16 @@ +--TEST-- +Partial application fuzz 005 +--FILE-- + +==DONE== +--EXPECT-- +==DONE== diff --git a/Zend/tests/partial_application/fuzz_006.phpt b/Zend/tests/partial_application/fuzz_006.phpt new file mode 100644 index 0000000000000..a817c6b812857 --- /dev/null +++ b/Zend/tests/partial_application/fuzz_006.phpt @@ -0,0 +1,16 @@ +--TEST-- +Partial application fuzz 006 +--FILE-- + +--EXPECT-- +int(1) diff --git a/Zend/tests/partial_application/fuzz_007.phpt b/Zend/tests/partial_application/fuzz_007.phpt new file mode 100644 index 0000000000000..8e2532f31ccaa --- /dev/null +++ b/Zend/tests/partial_application/fuzz_007.phpt @@ -0,0 +1,19 @@ +--TEST-- +Partial application fuzz 007 +--FILE-- +getMessage(), "\n"; +} + +?> +--EXPECT-- +Undefined constant "UNDEFINED" diff --git a/Zend/tests/partial_application/invokable.phpt b/Zend/tests/partial_application/invokable.phpt new file mode 100644 index 0000000000000..c158b74d5c64f --- /dev/null +++ b/Zend/tests/partial_application/invokable.phpt @@ -0,0 +1,47 @@ +--TEST-- +Partial application invokable +--FILE-- + +--EXPECTF-- +Partial [ public method __invoke ] { + @@ %s.php 11 - 11 + + - Parameters [2] { + Parameter #0 [ int $a ] + Parameter #1 [ object $b ] + } + - Return [ C ] +} + +Partial [ public method __invoke ] { + @@ %s.php 15 - 15 + + - Parameters [1] { + Parameter #0 [ int $a ] + } + - Return [ C ] +} + +int(1) +object(stdClass)#%d (0) { +} diff --git a/Zend/tests/partial_application/jit_001.phpt b/Zend/tests/partial_application/jit_001.phpt new file mode 100644 index 0000000000000..47a5d380d8f94 --- /dev/null +++ b/Zend/tests/partial_application/jit_001.phpt @@ -0,0 +1,9 @@ +--TEST-- +Partial application JIT 001 +--FILE-- + +--EXPECT-- +int(1) +int(2) diff --git a/Zend/tests/partial_application/magic_001.phpt b/Zend/tests/partial_application/magic_001.phpt new file mode 100644 index 0000000000000..2b4fa5e497f00 --- /dev/null +++ b/Zend/tests/partial_application/magic_001.phpt @@ -0,0 +1,78 @@ +--TEST-- +Partial application magic: __call +--FILE-- +method(?); + +echo (string) new ReflectionFunction($bar); + +try { + $bar(); +} catch (Error $ex) { + printf("%s\n", $ex->getMessage()); +} + +try { + $bar(1, 2); +} catch (Error $ex) { + printf("%s\n", $ex->getMessage()); +} + +$bar(1); + +$bar = $foo->method(?, ...); + +echo (string) new ReflectionFunction($bar); + +$bar(10); + +$bar = $foo->method(new Foo, ...); + +echo (string) new ReflectionFunction($bar); + +$bar(100); +?> +--EXPECTF-- +Partial [ public method method ] { + @@ %s 12 - 12 + + - Parameters [1] { + Parameter #0 [ $ ] + } +} +not enough arguments for application of Foo::method, 0 given and exactly 1 expected, declared in %s on line 12 +too many arguments for application of Foo::method, 2 given and a maximum of 1 expected, declared in %s on line 12 +Foo::method +int(1) +Partial [ public method method ] { + @@ %s 30 - 30 + + - Parameters [2] { + Parameter #0 [ $ ] + Parameter #1 [ ...$args ] + } +} +Foo::method +int(10) +Partial [ public method method ] { + @@ %s 36 - 36 + + - Parameters [1] { + Parameter #0 [ ...$args ] + } +} +Foo::method +object(Foo)#%d (0) { +} +int(100) + diff --git a/Zend/tests/partial_application/magic_002.phpt b/Zend/tests/partial_application/magic_002.phpt new file mode 100644 index 0000000000000..2280d8da34dd8 --- /dev/null +++ b/Zend/tests/partial_application/magic_002.phpt @@ -0,0 +1,62 @@ +--TEST-- +Partial application magic: __callStatic +--FILE-- + +--EXPECTF-- +Partial [ public method method ] { + @@ %s 10 - 10 + + - Parameters [1] { + Parameter #0 [ $ ] + } +} +Foo::method +int(1) +Partial [ public method method ] { + @@ %s 16 - 16 + + - Parameters [2] { + Parameter #0 [ $ ] + Parameter #1 [ ...$args ] + } +} +Foo::method +int(10) +Partial [ public method method ] { + @@ %s 22 - 22 + + - Parameters [1] { + Parameter #0 [ ...$args ] + } +} +Foo::method +object(Foo)#%d (0) { +} +int(100) + diff --git a/Zend/tests/partial_application/magic_003.phpt b/Zend/tests/partial_application/magic_003.phpt new file mode 100644 index 0000000000000..a46fb5df226bf --- /dev/null +++ b/Zend/tests/partial_application/magic_003.phpt @@ -0,0 +1,13 @@ +--TEST-- +Partial application magic trampoline release unused +--FILE-- + +--EXPECT-- +OK diff --git a/Zend/tests/partial_application/magic_004.phpt b/Zend/tests/partial_application/magic_004.phpt new file mode 100644 index 0000000000000..4c6f47ef366fd --- /dev/null +++ b/Zend/tests/partial_application/magic_004.phpt @@ -0,0 +1,13 @@ +--TEST-- +Partial application magic trampoline release used +--FILE-- + +--EXPECT-- +OK diff --git a/Zend/tests/partial_application/magic_005.phpt b/Zend/tests/partial_application/magic_005.phpt new file mode 100644 index 0000000000000..4a5e1a7b069ab --- /dev/null +++ b/Zend/tests/partial_application/magic_005.phpt @@ -0,0 +1,38 @@ +--TEST-- +Partial application magic null ptr deref in arginfo +--FILE-- +method(?); +var_dump($bar); +?> +--EXPECTF-- +object(Closure)#%d (6) { + ["name"]=> + string(6) "method" + ["file"]=> + string(73) "%smagic_005.php" + ["line"]=> + int(8) + ["this"]=> + object(Foo)#%d (0) { + } + ["parameter"]=> + array(1) { + ["$"]=> + string(10) "" + } + ["args"]=> + array(1) { + ["args"]=> + array(1) { + [0]=> + NULL + } + } +} diff --git a/Zend/tests/partial_application/magic_006.phpt b/Zend/tests/partial_application/magic_006.phpt new file mode 100644 index 0000000000000..a5b4f1915c027 --- /dev/null +++ b/Zend/tests/partial_application/magic_006.phpt @@ -0,0 +1,20 @@ +--TEST-- +Partial application magic varargs +--FILE-- +method(1,...); +$bar(2); +?> +--EXPECT-- +array(2) { + [0]=> + int(1) + [1]=> + int(2) +} diff --git a/Zend/tests/partial_application/named_placeholder_001.phpt b/Zend/tests/partial_application/named_placeholder_001.phpt new file mode 100644 index 0000000000000..f6884997c746d --- /dev/null +++ b/Zend/tests/partial_application/named_placeholder_001.phpt @@ -0,0 +1,109 @@ +--TEST-- +Partial application named placeholder +--FILE-- +getMessage(), "\n"; +} + +try { + $bar = $bar(..., c: ?); +} catch (\Throwable $e) { + echo $e->getMessage(), "\n"; +} + +?> +--EXPECTF-- +Partial [ function foo ] { + @@ %snamed_placeholder_001.php 11 - 11 + + - Parameters [1] { + Parameter #0 [ $b = 2 ] + } +} +int(1) +object(B)#%d (0) { +} +int(3) +Partial [ function foo ] { + @@ %snamed_placeholder_001.php 17 - 17 + + - Parameters [1] { + Parameter #0 [ $b = 2 ] + } +} +int(1) +object(B)#%d (0) { +} +int(3) +Partial [ function foo ] { + @@ %snamed_placeholder_001.php 24 - 24 + + - Parameters [1] { + Parameter #0 [ $b = 2 ] + } +} +int(1) +object(B)#%d (0) { +} +int(3) +Partial [ function bar ] { + @@ %snamed_placeholder_001.php 34 - 34 + + - Parameters [3] { + Parameter #0 [ $a = 1 ] + Parameter #1 [ $b = 2 ] + Parameter #2 [ ...$c ] + } +} +object(A)#%d (0) { +} +object(B)#%d (0) { +} +array(1) { + [0]=> + object(C)#%d (0) { + } +} +Named parameter $a overwrites previous placeholder +Cannot use named placeholder for unknown or variadic parameter $c diff --git a/Zend/tests/partial_application/pipe_optimization_001.phpt b/Zend/tests/partial_application/pipe_optimization_001.phpt new file mode 100644 index 0000000000000..ce1956757756b --- /dev/null +++ b/Zend/tests/partial_application/pipe_optimization_001.phpt @@ -0,0 +1,43 @@ +--TEST-- +Partial application pipe optimization: PFA with single placeholder arg can be optimized +--EXTENSIONS-- +opcache +--INI-- +opcache.opt_debug_level=0x20000 +--FILE-- + 0) { + function foo($a) { + var_dump($a); + } +} + +1 |> foo(?); + +?> +--EXPECTF-- +$_main: + ; (lines=9, args=0, vars=0, tmps=2) + ; (after optimizer) + ; %spipe_optimization_001.php:1-12 +0000 INIT_FCALL 0 %d string("time") +0001 V1 = DO_ICALL +0002 T0 = IS_SMALLER int(0) V1 +0003 JMPZ T0 0005 +0004 DECLARE_FUNCTION string("foo") 0 +0005 INIT_FCALL_BY_NAME 1 string("foo") +0006 SEND_VAL_EX int(1) 1 +0007 DO_FCALL_BY_NAME +0008 RETURN int(1) + +foo: + ; (lines=5, args=1, vars=1, tmps=0) + ; (after optimizer) + ; %spipe_optimization_001.php:4-6 +0000 CV0($a) = RECV 1 +0001 INIT_FCALL 1 %d string("var_dump") +0002 SEND_VAR CV0($a) 1 +0003 DO_ICALL +0004 RETURN null +int(1) diff --git a/Zend/tests/partial_application/pipe_optimization_002.phpt b/Zend/tests/partial_application/pipe_optimization_002.phpt new file mode 100644 index 0000000000000..a94ebcc260f60 --- /dev/null +++ b/Zend/tests/partial_application/pipe_optimization_002.phpt @@ -0,0 +1,47 @@ +--TEST-- +Partial application pipe optimization: PFA with only one placeholder can be optimized +--EXTENSIONS-- +opcache +--INI-- +opcache.opt_debug_level=0x20000 +--FILE-- + 0) { + function foo($a, $b) { + var_dump($a, $b); + } +} + +2 |> foo(1, ?); + +?> +--EXPECTF-- +$_main: + ; (lines=10, args=0, vars=0, tmps=2) + ; (after optimizer) + ; %spipe_optimization_002.php:1-12 +0000 INIT_FCALL 0 %d string("time") +0001 V1 = DO_ICALL +0002 T0 = IS_SMALLER int(0) V1 +0003 JMPZ T0 0005 +0004 DECLARE_FUNCTION string("foo") 0 +0005 INIT_FCALL_BY_NAME 2 string("foo") +0006 SEND_VAL_EX int(1) 1 +0007 SEND_VAL_EX int(2) 2 +0008 DO_FCALL_BY_NAME +0009 RETURN int(1) + +foo: + ; (lines=7, args=2, vars=2, tmps=0) + ; (after optimizer) + ; %spipe_optimization_002.php:4-6 +0000 CV0($a) = RECV 1 +0001 CV1($b) = RECV 2 +0002 INIT_FCALL 2 %d string("var_dump") +0003 SEND_VAR CV0($a) 1 +0004 SEND_VAR CV1($b) 2 +0005 DO_ICALL +0006 RETURN null +int(1) +int(2) diff --git a/Zend/tests/partial_application/pipe_optimization_003.phpt b/Zend/tests/partial_application/pipe_optimization_003.phpt new file mode 100644 index 0000000000000..58cebf25ab5b1 --- /dev/null +++ b/Zend/tests/partial_application/pipe_optimization_003.phpt @@ -0,0 +1,47 @@ +--TEST-- +Partial application pipe optimization: PFA with only one placeholder can be optimized +--EXTENSIONS-- +opcache +--INI-- +opcache.opt_debug_level=0x20000 +--FILE-- + 0) { + function foo($a, $b) { + var_dump($a, $b); + } +} + +2 |> foo(?, 1); + +?> +--EXPECTF-- +$_main: + ; (lines=10, args=0, vars=0, tmps=2) + ; (after optimizer) + ; %spipe_optimization_003.php:1-12 +0000 INIT_FCALL 0 %d string("time") +0001 V1 = DO_ICALL +0002 T0 = IS_SMALLER int(0) V1 +0003 JMPZ T0 0005 +0004 DECLARE_FUNCTION string("foo") 0 +0005 INIT_FCALL_BY_NAME 2 string("foo") +0006 SEND_VAL_EX int(2) 1 +0007 SEND_VAL_EX int(1) 2 +0008 DO_FCALL_BY_NAME +0009 RETURN int(1) + +foo: + ; (lines=7, args=2, vars=2, tmps=0) + ; (after optimizer) + ; %spipe_optimization_003.php:4-6 +0000 CV0($a) = RECV 1 +0001 CV1($b) = RECV 2 +0002 INIT_FCALL 2 %d string("var_dump") +0003 SEND_VAR CV0($a) 1 +0004 SEND_VAR CV1($b) 2 +0005 DO_ICALL +0006 RETURN null +int(2) +int(1) diff --git a/Zend/tests/partial_application/pipe_optimization_004.phpt b/Zend/tests/partial_application/pipe_optimization_004.phpt new file mode 100644 index 0000000000000..c20d080824a0d --- /dev/null +++ b/Zend/tests/partial_application/pipe_optimization_004.phpt @@ -0,0 +1,62 @@ +--TEST-- +Partial application pipe optimization: PFA with multiple placeholders can not be optimized +--EXTENSIONS-- +opcache +--INI-- +opcache.opt_debug_level=0x20000 +--FILE-- + 0) { + function foo($a, $b) { + var_dump($a, $b); + } +} + +try { +2 |> foo(?, ?); +} catch (\Throwable $e) { + echo $e->getMessage(), "\n"; +} + +?> +--EXPECTF-- +$_main: + ; (lines=20, args=0, vars=1, tmps=2) + ; (after optimizer) + ; %spipe_optimization_004.php:1-16 +0000 INIT_FCALL 0 %d string("time") +0001 V2 = DO_ICALL +0002 T1 = IS_SMALLER int(0) V2 +0003 JMPZ T1 0005 +0004 DECLARE_FUNCTION string("foo") 0 +0005 INIT_FCALL_BY_NAME 2 string("foo") +0006 SEND_PLACEHOLDER +0007 SEND_PLACEHOLDER +0008 CHECK_PARTIAL_ARGS +0009 T1 = CALLABLE_CONVERT_PARTIAL +0010 INIT_DYNAMIC_CALL 1 T1 +0011 SEND_VAL_EX int(2) 1 +0012 DO_FCALL +0013 RETURN int(1) +0014 CV0($e) = CATCH string("Throwable") +0015 INIT_METHOD_CALL 0 CV0($e) string("getMessage") +0016 V1 = DO_FCALL +0017 ECHO V1 +0018 ECHO string("\n") +0019 RETURN int(1) +EXCEPTION TABLE: + 0005, 0014, -, - + +foo: + ; (lines=7, args=2, vars=2, tmps=0) + ; (after optimizer) + ; %spipe_optimization_004.php:4-6 +0000 CV0($a) = RECV 1 +0001 CV1($b) = RECV 2 +0002 INIT_FCALL 2 %d string("var_dump") +0003 SEND_VAR CV0($a) 1 +0004 SEND_VAR CV1($b) 2 +0005 DO_ICALL +0006 RETURN null +not enough arguments for application of foo, 1 given and exactly 2 expected, declared in %s on line %d diff --git a/Zend/tests/partial_application/pipe_optimization_005.phpt b/Zend/tests/partial_application/pipe_optimization_005.phpt new file mode 100644 index 0000000000000..1a84e6feaf0ad --- /dev/null +++ b/Zend/tests/partial_application/pipe_optimization_005.phpt @@ -0,0 +1,47 @@ +--TEST-- +Partial application pipe optimization: PFA with only one placeholder can be optimized +--EXTENSIONS-- +opcache +--INI-- +opcache.opt_debug_level=0x20000 +--FILE-- + 0) { + function foo($a, $b) { + var_dump($a, $b); + } +} + +2 |> foo(1, ...); + +?> +--EXPECTF-- +$_main: + ; (lines=10, args=0, vars=0, tmps=2) + ; (after optimizer) + ; %spipe_optimization_005.php:1-12 +0000 INIT_FCALL 0 %d string("time") +0001 V1 = DO_ICALL +0002 T0 = IS_SMALLER int(0) V1 +0003 JMPZ T0 0005 +0004 DECLARE_FUNCTION string("foo") 0 +0005 INIT_FCALL_BY_NAME 2 string("foo") +0006 SEND_VAL_EX int(1) 1 +0007 SEND_VAL_EX int(2) 2 +0008 DO_FCALL_BY_NAME +0009 RETURN int(1) + +foo: + ; (lines=7, args=2, vars=2, tmps=0) + ; (after optimizer) + ; %spipe_optimization_005.php:4-6 +0000 CV0($a) = RECV 1 +0001 CV1($b) = RECV 2 +0002 INIT_FCALL 2 %d string("var_dump") +0003 SEND_VAR CV0($a) 1 +0004 SEND_VAR CV1($b) 2 +0005 DO_ICALL +0006 RETURN null +int(1) +int(2) diff --git a/Zend/tests/partial_application/pipe_optimization_006.phpt b/Zend/tests/partial_application/pipe_optimization_006.phpt new file mode 100644 index 0000000000000..040033d770b0e --- /dev/null +++ b/Zend/tests/partial_application/pipe_optimization_006.phpt @@ -0,0 +1,51 @@ +--TEST-- +Partial application pipe optimization: PFA with only one placeholder can be optimized (named) +--EXTENSIONS-- +opcache +--INI-- +opcache.opt_debug_level=0x20000 +--FILE-- + 0) { + function foo($a, $b = null, $c = null) { + var_dump($a, $b, $c); + } +} + +2 |> foo(1, c: ?); + +?> +--EXPECTF-- +$_main: + ; (lines=11, args=0, vars=0, tmps=2) + ; (after optimizer) + ; %spipe_optimization_006.php:1-12 +0000 INIT_FCALL 0 %d string("time") +0001 V1 = DO_ICALL +0002 T0 = IS_SMALLER int(0) V1 +0003 JMPZ T0 0005 +0004 DECLARE_FUNCTION string("foo") 0 +0005 INIT_FCALL_BY_NAME 1 string("foo") +0006 SEND_VAL_EX int(1) 1 +0007 SEND_VAL_EX int(2) string("c") +0008 CHECK_UNDEF_ARGS +0009 DO_FCALL_BY_NAME +0010 RETURN int(1) + +foo: + ; (lines=9, args=3, vars=3, tmps=0) + ; (after optimizer) + ; %spipe_optimization_006.php:4-6 +0000 CV0($a) = RECV 1 +0001 CV1($b) = RECV_INIT 2 null +0002 CV2($c) = RECV_INIT 3 null +0003 INIT_FCALL 3 %d string("var_dump") +0004 SEND_VAR CV0($a) 1 +0005 SEND_VAR CV1($b) 2 +0006 SEND_VAR CV2($c) 3 +0007 DO_ICALL +0008 RETURN null +int(1) +NULL +int(2) diff --git a/Zend/tests/partial_application/pipe_optimization_007.phpt b/Zend/tests/partial_application/pipe_optimization_007.phpt new file mode 100644 index 0000000000000..b696375433b30 --- /dev/null +++ b/Zend/tests/partial_application/pipe_optimization_007.phpt @@ -0,0 +1,62 @@ +--TEST-- +Partial application pipe optimization: PFA with multiple placeholders can not be optimized +--EXTENSIONS-- +opcache +--INI-- +opcache.opt_debug_level=0x20000 +--FILE-- + 0) { + function foo($a, $b) { + var_dump($a, $b); + } +} + +try { +2 |> foo(a: ?, b: ?); +} catch (\Throwable $e) { + echo $e->getMessage(), "\n"; +} + +?> +--EXPECTF-- +$_main: + ; (lines=20, args=0, vars=1, tmps=2) + ; (after optimizer) + ; %spipe_optimization_007.php:1-16 +0000 INIT_FCALL 0 %d string("time") +0001 V2 = DO_ICALL +0002 T1 = IS_SMALLER int(0) V2 +0003 JMPZ T1 0005 +0004 DECLARE_FUNCTION string("foo") 0 +0005 INIT_FCALL_BY_NAME 0 string("foo") +0006 SEND_PLACEHOLDER string("a") +0007 SEND_PLACEHOLDER string("b") +0008 CHECK_PARTIAL_ARGS +0009 T1 = CALLABLE_CONVERT_PARTIAL +0010 INIT_DYNAMIC_CALL 1 T1 +0011 SEND_VAL_EX int(2) 1 +0012 DO_FCALL +0013 RETURN int(1) +0014 CV0($e) = CATCH string("Throwable") +0015 INIT_METHOD_CALL 0 CV0($e) string("getMessage") +0016 V1 = DO_FCALL +0017 ECHO V1 +0018 ECHO string("\n") +0019 RETURN int(1) +EXCEPTION TABLE: + 0005, 0014, -, - + +foo: + ; (lines=7, args=2, vars=2, tmps=0) + ; (after optimizer) + ; %spipe_optimization_007.php:4-6 +0000 CV0($a) = RECV 1 +0001 CV1($b) = RECV 2 +0002 INIT_FCALL 2 %d string("var_dump") +0003 SEND_VAR CV0($a) 1 +0004 SEND_VAR CV1($b) 2 +0005 DO_ICALL +0006 RETURN null +not enough arguments for application of foo, 1 given and exactly 2 expected, declared in %s on line %d diff --git a/Zend/tests/partial_application/pipe_optimization_008.phpt b/Zend/tests/partial_application/pipe_optimization_008.phpt new file mode 100644 index 0000000000000..3cf0a3e2db18c --- /dev/null +++ b/Zend/tests/partial_application/pipe_optimization_008.phpt @@ -0,0 +1,63 @@ +--TEST-- +Partial application pipe optimization: PFA with both a variadic placeholder and named arg can not be optimized +--EXTENSIONS-- +opcache +--INI-- +opcache.opt_debug_level=0x20000 +--FILE-- + 0) { + function foo($a, $b) { + var_dump($a, $b); + } +} + +try { + 2 |> foo(..., a: 1); +} catch (\Throwable $e) { + echo $e->getMessage(), "\n"; +} + +?> +--EXPECTF-- +$_main: + ; (lines=20, args=0, vars=1, tmps=2) + ; (after optimizer) + ; %spipe_optimization_008.php:1-16 +0000 INIT_FCALL 0 %d string("time") +0001 V2 = DO_ICALL +0002 T1 = IS_SMALLER int(0) V2 +0003 JMPZ T1 0005 +0004 DECLARE_FUNCTION string("foo") 0 +0005 INIT_FCALL_BY_NAME 1 string("foo") +0006 SEND_PLACEHOLDER +0007 SEND_VAL_EX int(1) string("a") +0008 CHECK_PARTIAL_ARGS +0009 T1 = CALLABLE_CONVERT_PARTIAL +0010 INIT_DYNAMIC_CALL 1 T1 +0011 SEND_VAL_EX int(2) 1 +0012 DO_FCALL +0013 RETURN int(1) +0014 CV0($e) = CATCH string("Throwable") +0015 INIT_METHOD_CALL 0 CV0($e) string("getMessage") +0016 V1 = DO_FCALL +0017 ECHO V1 +0018 ECHO string("\n") +0019 RETURN int(1) +EXCEPTION TABLE: + 0005, 0014, -, - + +foo: + ; (lines=7, args=2, vars=2, tmps=0) + ; (after optimizer) + ; %spipe_optimization_008.php:4-6 +0000 CV0($a) = RECV 1 +0001 CV1($b) = RECV 2 +0002 INIT_FCALL 2 %d string("var_dump") +0003 SEND_VAR CV0($a) 1 +0004 SEND_VAR CV1($b) 2 +0005 DO_ICALL +0006 RETURN null +int(1) +int(2) diff --git a/Zend/tests/partial_application/pipe_optimization_009.phpt b/Zend/tests/partial_application/pipe_optimization_009.phpt new file mode 100644 index 0000000000000..b2c0cfc963cf3 --- /dev/null +++ b/Zend/tests/partial_application/pipe_optimization_009.phpt @@ -0,0 +1,99 @@ +--TEST-- +Partial application pipe optimization: Evaluation order +--EXTENSIONS-- +opcache +--INI-- +opcache.opt_debug_level=0x20000 +--FILE-- + 0) { + function foo($a, $b, $c) { + var_dump($a, $b, $c); + } + function lhs() { + echo __FUNCTION__, "\n"; + return 0; + } + function arg1() { + echo __FUNCTION__, "\n"; + return 1; + } + function arg2() { + echo __FUNCTION__, "\n"; + return 2; + } +} + +lhs() |> foo(arg1(), ?, arg2()); + +?> +--EXPECTF-- +$_main: + ; (lines=21, args=0, vars=0, tmps=2) + ; (after optimizer) + ; %spipe_optimization_009.php:1-24 +0000 INIT_FCALL 0 %d string("time") +0001 V1 = DO_ICALL +0002 T0 = IS_SMALLER int(0) V1 +0003 JMPZ T0 0008 +0004 DECLARE_FUNCTION string("foo") 0 +0005 DECLARE_FUNCTION string("lhs") 1 +0006 DECLARE_FUNCTION string("arg1") 2 +0007 DECLARE_FUNCTION string("arg2") 3 +0008 INIT_FCALL_BY_NAME 0 string("lhs") +0009 V1 = DO_FCALL_BY_NAME +0010 T0 = QM_ASSIGN V1 +0011 INIT_FCALL_BY_NAME 3 string("foo") +0012 INIT_FCALL_BY_NAME 0 string("arg1") +0013 V1 = DO_FCALL_BY_NAME +0014 SEND_VAR_NO_REF_EX V1 1 +0015 SEND_VAL_EX T0 2 +0016 INIT_FCALL_BY_NAME 0 string("arg2") +0017 V0 = DO_FCALL_BY_NAME +0018 SEND_VAR_NO_REF_EX V0 3 +0019 DO_FCALL_BY_NAME +0020 RETURN int(1) +LIVE RANGES: + 0: 0011 - 0015 (tmp/var) + +foo: + ; (lines=9, args=3, vars=3, tmps=0) + ; (after optimizer) + ; %spipe_optimization_009.php:4-6 +0000 CV0($a) = RECV 1 +0001 CV1($b) = RECV 2 +0002 CV2($c) = RECV 3 +0003 INIT_FCALL 3 %d string("var_dump") +0004 SEND_VAR CV0($a) 1 +0005 SEND_VAR CV1($b) 2 +0006 SEND_VAR CV2($c) 3 +0007 DO_ICALL +0008 RETURN null + +lhs: + ; (lines=2, args=0, vars=0, tmps=0) + ; (after optimizer) + ; %spipe_optimization_009.php:7-10 +0000 ECHO string("lhs\n") +0001 RETURN int(0) + +arg1: + ; (lines=2, args=0, vars=0, tmps=0) + ; (after optimizer) + ; %spipe_optimization_009.php:11-14 +0000 ECHO string("arg1\n") +0001 RETURN int(1) + +arg2: + ; (lines=2, args=0, vars=0, tmps=0) + ; (after optimizer) + ; %spipe_optimization_009.php:15-18 +0000 ECHO string("arg2\n") +0001 RETURN int(2) +lhs +arg1 +arg2 +int(1) +int(0) +int(2) diff --git a/Zend/tests/partial_application/pipe_optimization_010.phpt b/Zend/tests/partial_application/pipe_optimization_010.phpt new file mode 100644 index 0000000000000..3836bc6332067 --- /dev/null +++ b/Zend/tests/partial_application/pipe_optimization_010.phpt @@ -0,0 +1,54 @@ +--TEST-- +Partial application pipe optimization: References +--EXTENSIONS-- +opcache +--INI-- +opcache.opt_debug_level=0x20000 +--FILE-- + 0) { + function foo(&$a, $b) { + var_dump($a, $b); + $a = 2; + } +} + +1 |> foo($a, ?); +var_dump($a); + +?> +--EXPECTF-- +$_main: + ; (lines=13, args=0, vars=1, tmps=2) + ; (after optimizer) + ; %spipe_optimization_010.php:1-14 +0000 INIT_FCALL 0 %d string("time") +0001 V2 = DO_ICALL +0002 T1 = IS_SMALLER int(0) V2 +0003 JMPZ T1 0005 +0004 DECLARE_FUNCTION string("foo") 0 +0005 INIT_FCALL_BY_NAME 2 string("foo") +0006 SEND_VAR_EX CV0($a) 1 +0007 SEND_VAL_EX int(1) 2 +0008 DO_FCALL_BY_NAME +0009 INIT_FCALL 1 %d string("var_dump") +0010 SEND_VAR CV0($a) 1 +0011 DO_ICALL +0012 RETURN int(1) + +foo: + ; (lines=8, args=2, vars=2, tmps=0) + ; (after optimizer) + ; %spipe_optimization_010.php:4-7 +0000 CV0($a) = RECV 1 +0001 CV1($b) = RECV 2 +0002 INIT_FCALL 2 %d string("var_dump") +0003 SEND_VAR CV0($a) 1 +0004 SEND_VAR CV1($b) 2 +0005 DO_ICALL +0006 ASSIGN CV0($a) int(2) +0007 RETURN null +NULL +int(1) +int(2) diff --git a/Zend/tests/partial_application/pipe_optimization_011.phpt b/Zend/tests/partial_application/pipe_optimization_011.phpt new file mode 100644 index 0000000000000..1e1e39f9998e1 --- /dev/null +++ b/Zend/tests/partial_application/pipe_optimization_011.phpt @@ -0,0 +1,106 @@ +--TEST-- +Partial application pipe optimization: Evaluation order +--EXTENSIONS-- +opcache +--INI-- +opcache.opt_debug_level=0x20000 +--FILE-- + 0) { + function foo($a, $b, $c) { + var_dump($a, $b, $c); + } + function lhs() { + echo __FUNCTION__, "\n"; + return 0; + } + function arg1() { + global $a; + $a = 2; + echo __FUNCTION__, "\n"; + return 1; + } + function arg2() { + global $a; + $a = 3; + echo __FUNCTION__, "\n"; + return 2; + } +} + +$a = 0; +$a |> foo(arg1(), ?, arg2()); + +?> +--EXPECTF-- +$_main: + ; (lines=20, args=0, vars=1, tmps=2) + ; (after optimizer) + ; %spipe_optimization_011.php:1-29 +0000 INIT_FCALL 0 %d string("time") +0001 V2 = DO_ICALL +0002 T1 = IS_SMALLER int(0) V2 +0003 JMPZ T1 0008 +0004 DECLARE_FUNCTION string("foo") 0 +0005 DECLARE_FUNCTION string("lhs") 1 +0006 DECLARE_FUNCTION string("arg1") 2 +0007 DECLARE_FUNCTION string("arg2") 3 +0008 ASSIGN CV0($a) int(0) +0009 T1 = QM_ASSIGN CV0($a) +0010 INIT_FCALL_BY_NAME 3 string("foo") +0011 INIT_FCALL_BY_NAME 0 string("arg1") +0012 V2 = DO_FCALL_BY_NAME +0013 SEND_VAR_NO_REF_EX V2 1 +0014 SEND_VAL_EX T1 2 +0015 INIT_FCALL_BY_NAME 0 string("arg2") +0016 V1 = DO_FCALL_BY_NAME +0017 SEND_VAR_NO_REF_EX V1 3 +0018 DO_FCALL_BY_NAME +0019 RETURN int(1) +LIVE RANGES: + 1: 0010 - 0014 (tmp/var) + +foo: + ; (lines=9, args=3, vars=3, tmps=0) + ; (after optimizer) + ; %spipe_optimization_011.php:4-6 +0000 CV0($a) = RECV 1 +0001 CV1($b) = RECV 2 +0002 CV2($c) = RECV 3 +0003 INIT_FCALL 3 %d string("var_dump") +0004 SEND_VAR CV0($a) 1 +0005 SEND_VAR CV1($b) 2 +0006 SEND_VAR CV2($c) 3 +0007 DO_ICALL +0008 RETURN null + +lhs: + ; (lines=2, args=0, vars=0, tmps=0) + ; (after optimizer) + ; %spipe_optimization_011.php:7-10 +0000 ECHO string("lhs\n") +0001 RETURN int(0) + +arg1: + ; (lines=4, args=0, vars=1, tmps=0) + ; (after optimizer) + ; %spipe_optimization_011.php:11-16 +0000 BIND_GLOBAL CV0($a) string("a") +0001 ASSIGN CV0($a) int(2) +0002 ECHO string("arg1\n") +0003 RETURN int(1) + +arg2: + ; (lines=4, args=0, vars=1, tmps=0) + ; (after optimizer) + ; %spipe_optimization_011.php:17-22 +0000 BIND_GLOBAL CV0($a) string("a") +0001 ASSIGN CV0($a) int(3) +0002 ECHO string("arg2\n") +0003 RETURN int(2) +arg1 +arg2 +int(1) +int(0) +int(2) diff --git a/Zend/tests/partial_application/rebinding_001.phpt b/Zend/tests/partial_application/rebinding_001.phpt new file mode 100644 index 0000000000000..594188fa405fa --- /dev/null +++ b/Zend/tests/partial_application/rebinding_001.phpt @@ -0,0 +1,65 @@ +--TEST-- +Partial application can only be rebound to an instanceof $this +--FILE-- +f(?); +$g = $c->g(?); + +echo "# Can be rebound to \$this of the same class:\n"; +$f->bindTo(new C)(1); + +echo "# Can be rebound to \$this of a sub-class:\n"; +$f->bindTo(new SubClass)(1); + +echo "# Cannot be rebound to an unrelated class:\n"; +try { + $f->bindTo(new Unrelated)(1); +} catch (Error $e) { + echo $e->getMessage(), "\n"; +} + +echo "# Cannot unbind \$this on instance method:\n"; +try { + $f->bindTo(null)(1); +} catch (Error $e) { + echo $e->getMessage(), "\n"; +} + +echo "# Can unbind \$this on static method:\n"; +try { + $g->bindTo(null)(1); +} catch (Error $e) { + echo $e->getMessage(), "\n"; +} + +?> +--EXPECTF-- +# Can be rebound to $this of the same class: +object(C)#%d (0) { +} +# Can be rebound to $this of a sub-class: +object(SubClass)#%d (0) { +} +# Cannot be rebound to an unrelated class: + +Warning: Cannot bind method C::f() to object of class Unrelated in %s on line %d +Value of type null is not callable +# Cannot unbind $this on instance method: + +Warning: Cannot unbind $this of method in %s on line %d +Value of type null is not callable +# Can unbind $this on static method: + +Warning: Cannot unbind $this of method in %s on line %d +Value of type null is not callable diff --git a/Zend/tests/partial_application/rebinding_002.phpt b/Zend/tests/partial_application/rebinding_002.phpt new file mode 100644 index 0000000000000..61793ffa1a3d5 --- /dev/null +++ b/Zend/tests/partial_application/rebinding_002.phpt @@ -0,0 +1,45 @@ +--TEST-- +Partial application scope cannot be rebound +--FILE-- +f(?); +$g = g(?); + +echo "# Can be rebound to the same scope:\n"; +$f->bindTo($c, C::class)(1); + +echo "# Method cannot be rebound to a different scope:\n"; +try { + $f->bindTo($c, SubClass::class)(1); +} catch (Error $e) { + echo $e->getMessage(), "\n"; +} + +echo "# Function cannot be refound to a different scope:\n"; +try { + $g->bindTo($c, SubClass::class)(1); +} catch (Error $e) { + echo $e->getMessage(), "\n"; +} +?> +--EXPECTF-- +# Can be rebound to the same scope: +string(1) "C" +# Method cannot be rebound to a different scope: + +Warning: Cannot rebind scope of closure created from method in %s on line %d +Value of type null is not callable +# Function cannot be refound to a different scope: + +Warning: Cannot rebind scope of closure created from function in %s on line %d +Value of type null is not callable diff --git a/Zend/tests/partial_application/references_001.phpt b/Zend/tests/partial_application/references_001.phpt new file mode 100644 index 0000000000000..9e06069af2182 --- /dev/null +++ b/Zend/tests/partial_application/references_001.phpt @@ -0,0 +1,24 @@ +--TEST-- +Partial application references 001 +--FILE-- + +--EXPECT-- +array(1) { + [0]=> + &NULL +} +NULL diff --git a/Zend/tests/partial_application/references_002.phpt b/Zend/tests/partial_application/references_002.phpt new file mode 100644 index 0000000000000..2dc0c79b11da4 --- /dev/null +++ b/Zend/tests/partial_application/references_002.phpt @@ -0,0 +1,24 @@ +--TEST-- +Partial application references 002 +--FILE-- + +--EXPECT-- +array(1) { + [0]=> + &int(2) +} +int(2) diff --git a/Zend/tests/partial_application/references_003.phpt b/Zend/tests/partial_application/references_003.phpt new file mode 100644 index 0000000000000..1a91fb79e655f --- /dev/null +++ b/Zend/tests/partial_application/references_003.phpt @@ -0,0 +1,20 @@ +--TEST-- +Partial application references 002 +--FILE-- +getMessage(), "\n"; +} + +?> +--EXPECT-- +foo(): Argument #1 ($b) could not be passed by reference diff --git a/Zend/tests/partial_application/references_004.phpt b/Zend/tests/partial_application/references_004.phpt new file mode 100644 index 0000000000000..208f81c2fedb4 --- /dev/null +++ b/Zend/tests/partial_application/references_004.phpt @@ -0,0 +1,17 @@ +--TEST-- +Partial application references 004 +--FILE-- +getMessage(), "\n"; +} + +?> +--EXPECT-- +foo(): Argument #1 ($a) could not be passed by reference diff --git a/Zend/tests/partial_application/reflection_001.phpt b/Zend/tests/partial_application/reflection_001.phpt new file mode 100644 index 0000000000000..f5dfcfadb330c --- /dev/null +++ b/Zend/tests/partial_application/reflection_001.phpt @@ -0,0 +1,48 @@ +--TEST-- +Partial application reflection: required parameters +--FILE-- + +--EXPECTF-- +Partial [ function foo ] { + @@ %sreflection_001.php 6 - 6 + + - Parameters [3] { + Parameter #0 [ $a = 1 ] + Parameter #1 [ $b = 5 ] + Parameter #2 [ $c = 10 ] + } +} +Partial [ function foo ] { + @@ %sreflection_001.php 10 - 10 + + - Parameters [3] { + Parameter #0 [ $a = 1 ] + Parameter #1 [ $b = 5 ] + Parameter #2 [ $c = 10 ] + } +} +Partial [ function foo ] { + @@ %sreflection_001.php 14 - 14 + + - Parameters [3] { + Parameter #0 [ $a = 1 ] + Parameter #1 [ $b = 5 ] + Parameter #2 [ $c = 10 ] + } +} diff --git a/Zend/tests/partial_application/reflection_002.phpt b/Zend/tests/partial_application/reflection_002.phpt new file mode 100644 index 0000000000000..cee99f0b7735c --- /dev/null +++ b/Zend/tests/partial_application/reflection_002.phpt @@ -0,0 +1,57 @@ +--TEST-- +Partial application reflection: variadics +--FILE-- + +--EXPECTF-- +Partial [ function foo ] { + @@ %s 6 - 6 + + - Parameters [1] { + Parameter #0 [ $a ] + } +} +Partial [ function foo ] { + @@ %s 10 - 10 + + - Parameters [2] { + Parameter #0 [ $a ] + Parameter #1 [ ...$b ] + } +} +Partial [ function foo ] { + @@ %s 14 - 14 + + - Parameters [2] { + Parameter #0 [ $a ] + Parameter #1 [ $ ] + } +} +Partial [ function foo ] { + @@ %s 18 - 18 + + - Parameters [3] { + Parameter #0 [ $a ] + Parameter #1 [ $ ] + Parameter #2 [ $ ] + } +} diff --git a/Zend/tests/partial_application/reflection_003.phpt b/Zend/tests/partial_application/reflection_003.phpt new file mode 100644 index 0000000000000..e3c2b487b6735 --- /dev/null +++ b/Zend/tests/partial_application/reflection_003.phpt @@ -0,0 +1,43 @@ +--TEST-- +Partial application reflection: internal with variadics +--FILE-- + +--EXPECTF-- +Partial [ function sprintf ] { + @@ %sreflection_003.php 2 - 2 + + - Parameters [1] { + Parameter #0 [ string $format ] + } + - Return [ string ] +} +Partial [ function sprintf ] { + @@ %sreflection_003.php 6 - 6 + + - Parameters [2] { + Parameter #0 [ string $format ] + Parameter #1 [ mixed ...$values ] + } + - Return [ string ] +} +Partial [ function sprintf ] { + @@ %sreflection_003.php 10 - 10 + + - Parameters [2] { + Parameter #0 [ string $format ] + Parameter #1 [ mixed $ ] + } + - Return [ string ] +} diff --git a/Zend/tests/partial_application/reflection_004.phpt b/Zend/tests/partial_application/reflection_004.phpt new file mode 100644 index 0000000000000..bbb810f16ae4f --- /dev/null +++ b/Zend/tests/partial_application/reflection_004.phpt @@ -0,0 +1,13 @@ +--TEST-- +Partial application reflection: ReflectionFunction::isClosure() is true for partials +--FILE-- +isClosure(), "\n"; + +echo (int)(new ReflectionFunction(sprintf(?)))->isClosure(), "\n"; + +?> +--EXPECT-- +0 +1 diff --git a/Zend/tests/partial_application/reflection_005.phpt b/Zend/tests/partial_application/reflection_005.phpt new file mode 100644 index 0000000000000..f380ffab722c9 --- /dev/null +++ b/Zend/tests/partial_application/reflection_005.phpt @@ -0,0 +1,16 @@ +--TEST-- +Partial application reflection: ReflectionFunction::isPartial() is true for partials +--FILE-- +isPartial(), "\n"; + +echo (int)(new ReflectionFunction(function () {}))->isPartial(), "\n"; + +echo (int)(new ReflectionFunction(sprintf(?)))->isPartial(), "\n"; + +?> +--EXPECTF-- +0 +0 +1 diff --git a/Zend/tests/partial_application/relative_return_types.phpt b/Zend/tests/partial_application/relative_return_types.phpt new file mode 100644 index 0000000000000..5d5dd49c76efc --- /dev/null +++ b/Zend/tests/partial_application/relative_return_types.phpt @@ -0,0 +1,133 @@ +--TEST-- +Partial application: relative return types +--FILE-- + 0) { + trait T { + public function getSelf(object $o): self { + return $o; + } + public function getStatic(object $o): static { + return $o; + } + } +} + +class C { + use T; +} + +class D extends C { +} + +$c = new C; + +$self = $c->getSelf(?); + +echo (string) new ReflectionFunction($self), "\n"; + +var_dump($self($c)); +var_dump($self(new D)); +try { + $self(new stdClass); +} catch (Error $e) { + echo $e->getMessage(), "\n"; +} + +$static = $c->getStatic(?); + +echo (string) new ReflectionFunction($static), "\n"; + +var_dump($static($c)); +var_dump($static(new D)); +try { + $static(new stdClass); +} catch (Error $e) { + echo $e->getMessage(), "\n"; +} + +$d = new D; + +$self = $d->getSelf(?); + +echo (string) new ReflectionFunction($self), "\n"; + +var_dump($self($d)); +var_dump($self(new D)); +try { + $self(new stdClass); +} catch (Error $e) { + echo $e->getMessage(), "\n"; +} + +$static = $d->getStatic(?); + +echo (string) new ReflectionFunction($static), "\n"; + +var_dump($static($d)); +var_dump($static(new D)); +try { + $static(new stdClass); +} catch (Error $e) { + echo $e->getMessage(), "\n"; +} + +?> +--EXPECTF-- +Partial [ public method getSelf ] { + @@ %s.php 23 - 23 + + - Parameters [1] { + Parameter #0 [ object $o ] + } + - Return [ self ] +} + +object(C)#%d (0) { +} +object(D)#%d (0) { +} +C::getSelf(): Return value must be of type C, stdClass returned +Partial [ public method getStatic ] { + @@ %s.php 35 - 35 + + - Parameters [1] { + Parameter #0 [ object $o ] + } + - Return [ static ] +} + +object(C)#%d (0) { +} +object(D)#%d (0) { +} +C::getStatic(): Return value must be of type C, stdClass returned +Partial [ public method getSelf ] { + @@ %s.php 49 - 49 + + - Parameters [1] { + Parameter #0 [ object $o ] + } + - Return [ self ] +} + +object(D)#%d (0) { +} +object(D)#%d (0) { +} +C::getSelf(): Return value must be of type C, stdClass returned +Partial [ public method getStatic ] { + @@ %s.php 61 - 61 + + - Parameters [1] { + Parameter #0 [ object $o ] + } + - Return [ static ] +} + +object(D)#%d (0) { +} +object(D)#%d (0) { +} +C::getStatic(): Return value must be of type D, stdClass returned diff --git a/Zend/tests/partial_application/return_type_001.phpt b/Zend/tests/partial_application/return_type_001.phpt new file mode 100644 index 0000000000000..571ac6b5a9d55 --- /dev/null +++ b/Zend/tests/partial_application/return_type_001.phpt @@ -0,0 +1,17 @@ +--TEST-- +Partial application return type +--FILE-- + +--EXPECTF-- +Partial [ function foo ] { + @@ %s 4 - 4 + + - Parameters [0] { + } + - Return [ array ] +} + diff --git a/Zend/tests/partial_application/rfc_examples_001.phpt b/Zend/tests/partial_application/rfc_examples_001.phpt new file mode 100644 index 0000000000000..4054af8de3868 --- /dev/null +++ b/Zend/tests/partial_application/rfc_examples_001.phpt @@ -0,0 +1,152 @@ +--TEST-- +Partial application RFC examples: equivalence +--FILE-- + [ + stuff(?, ?, ?, ?, ?), + fn(int $i, string $s, float $f, Point $p, int $m = 0): array => stuff($i, $s, $f, $p, $m), + ], + 'Manually specify the first two values, and pull the rest "as is" (2)' => [ + stuff(?, ?, ...), + fn(int $i, string $s, float $f, Point $p, int $m = 0): array => stuff($i, $s, $f, $p, $m), + ], + 'The degenerate "first class callables" case. (Supported since 8.1)' => [ + stuff(...), + fn(int $i, string $s, float $f, Point $p, int $m = 0): array => stuff($i, $s, $f, $p, $m), + ], + 'Provide some values, require the rest to be provided later' => [ + stuff(1, 'hi', ?, ?, ?), + fn(float $f, Point $p, int $m = 0): array => stuff(1, 'hi', $f, $p, $m), + ], + 'Provide some values, require the rest to be provided later (2)' => [ + stuff(1, 'hi', ...), + fn(float $f, Point $p, int $m = 0): array => stuff(1, 'hi', $f, $p, $m), + ], + 'Provided some values, but not just from the left' => [ + stuff(1, ?, 3.5, ?, ?), + fn(string $s, Point $p, int $m = 0): array => stuff(1, $s, 3.5, $p, $m), + ], + 'Provided some values, but not just from the left (2)' => [ + stuff(1, ?, 3.5, ...), + fn(string $s, Point $p, int $m = 0): array => stuff(1, $s, 3.5, $p, $m), + ], + 'Provide just the last value' => [ + stuff(?, ?, ?, ?, 5), + fn(int $i, string $s, float $f, Point $p): array => stuff($i, $s, $f, $p, 5), + ], + 'Not accounting for an optional argument means it will always get its default value' => [ + stuff(?, ?, ?, ?), + fn(int $i, string $s, float $f, Point $p): array => stuff($i, $s, $f, $p), + ], + 'Named arguments can be pulled "out of order", and still work' => [ + stuff(?, ?, f: 3.5, p: $point), + fn(int $i, string $s): array => stuff($i, $s, 3.5, $point), + ], + 'Named arguments can be pulled "out of order", and still work (2)' => [ + stuff(?, ?, p: $point, f: 3.5), + fn(int $i, string $s): array => stuff($i, $s, 3.5, $point), + ], + 'The ... "everything else" placeholder respects named arguments' => [ + stuff(?, ?, ..., f: 3.5, p: $point), + fn(int $i, string $s, int $m = 0): array => stuff($i, $s, 3.5, $point, $m), + ], + 'Prefill all parameters, making a "delayed call" or "thunk"' => [ + stuff(1, 'hi', 3.4, $point, 5, ...), + fn(): array => stuff(1, 'hi', 3.4, $point, 5), + ], + 'Placeholders may be named, too. Their order doesn\'t matter as long as they come after the ..., if any' => [ + stuff(?, p: $point, f: ?, s: ?, m: 4), + fn(int $i, string $s, float $f): array => stuff($i, $s, $f, $point, 4), + ], + 'Placeholders may be named, too. Their order doesn\'t matter as long as they come after the ..., if any (2)' => [ + stuff(..., m: 4, p: $point, i: ?), + fn(int $i, string $s, float $f): array => stuff($i, $s, $f, $point, 4), + ], +]; + +foreach ($tests as $test => [$pfa, $closure]) { + echo "# ", $test, "\n"; + $pfaReflector = new ReflectionFunction($pfa); + $closureReflector = new ReflectionFunction($closure); + + try { + if (count($pfaReflector->getParameters()) !== count($closureReflector->getParameters())) { + throw new Exception("Arity does not match"); + } + + $it = new MultipleIterator(); + $it->attachIterator(new ArrayIterator($pfaReflector->getParameters())); + $it->attachIterator(new ArrayIterator($closureReflector->getParameters())); + foreach ($it as $i => [$pfaParam, $closureParam]) { + [$i] = $i; + if ($pfaParam->getName() !== $closureParam->getName()) { + throw new Exception(sprintf("Name of param %d does not match: %s vs %s", + $i, + $pfaParam->getName(), + $closureParam->getName(), + )); + } + if ((string)$pfaParam->getType() !== (string)$closureParam->getType()) { + throw new Exception(sprintf("Type of param %d does not match: %s vs %s", + $i, + $pfaParam->getType(), + $closureParam->getType(), + )); + } + if ($pfaParam->isOptional() !== $closureParam->isOptional()) { + throw new Exception(sprintf("Optionalness of param %d does not match: %d vs %d", + $i, + $pfaParam->isOptional(), + $closureParam->isOptional(), + )); + } + } + } catch (Exception $e) { + echo $e->getMessage(), "\n"; + echo $pfaReflector; + echo $closureReflector; + } + + $args = []; + foreach ($pfaReflector->getParameters() as $i => $p) { + $args[] = match ((string) $p->getType()) { + 'int' => 100 + $i, + 'float' => 100.5 + $i, + 'string' => (string) (100 + $i), + 'Point' => new Point, + }; + } + + if ($pfa(...$args) !== $closure(...$args)) { + echo "PFA is not equivalent to closure\n"; + } +} + +?> +--EXPECT-- +# Manually specify the first two values, and pull the rest "as is" +# Manually specify the first two values, and pull the rest "as is" (2) +# The degenerate "first class callables" case. (Supported since 8.1) +# Provide some values, require the rest to be provided later +# Provide some values, require the rest to be provided later (2) +# Provided some values, but not just from the left +# Provided some values, but not just from the left (2) +# Provide just the last value +# Not accounting for an optional argument means it will always get its default value +# Named arguments can be pulled "out of order", and still work +# Named arguments can be pulled "out of order", and still work (2) +# The ... "everything else" placeholder respects named arguments +# Prefill all parameters, making a "delayed call" or "thunk" +# Placeholders may be named, too. Their order doesn't matter as long as they come after the ..., if any +# Placeholders may be named, too. Their order doesn't matter as long as they come after the ..., if any (2) diff --git a/Zend/tests/partial_application/rfc_examples_002.phpt b/Zend/tests/partial_application/rfc_examples_002.phpt new file mode 100644 index 0000000000000..d1071bcd84cfe --- /dev/null +++ b/Zend/tests/partial_application/rfc_examples_002.phpt @@ -0,0 +1,118 @@ +--TEST-- +Partial application RFC examples: variadics equivalence +--XFAIL-- +Name of params for positional placeholders that run into the variadic portion is wrong +--FILE-- + [ + things(...), + fn(int $i, ?float $f = null, Point ...$points): array => things(...[$i, $f, ...$points]), + ], + 'Provide some values, but allow the variadic to remain variadic' => [ + things(1, 3.14, ...), + fn(Point ...$points): array => things(...[1, 3.14, ...$points]), + ], + 'In this version, the partial requires precisely four arguments, the last two of which will get received by things() in the variadic parameter. Note too that $f becomes required in this case.' => [ + things(?, ?, ?, ?), + fn(int $i, ?float $f, Point $p1, Point $p2): array => things($i, $f, $p1, $p2), + ], +]; + +foreach ($tests as $test => [$pfa, $closure]) { + echo "# ", $test, "\n"; + $pfaReflector = new ReflectionFunction($pfa); + $closureReflector = new ReflectionFunction($closure); + + try { + if (count($pfaReflector->getParameters()) !== count($closureReflector->getParameters())) { + throw new Exception("Arity does not match"); + } + + $it = new MultipleIterator(); + $it->attachIterator(new ArrayIterator($pfaReflector->getParameters())); + $it->attachIterator(new ArrayIterator($closureReflector->getParameters())); + foreach ($it as $i => [$pfaParam, $closureParam]) { + [$i] = $i; + if ($pfaParam->getName() !== $closureParam->getName()) { + throw new Exception(sprintf("Name of param %d does not match: %s vs %s", + $i, + $pfaParam->getName(), + $closureParam->getName(), + )); + } + if ((string)$pfaParam->getType() !== (string)$closureParam->getType()) { + throw new Exception(sprintf("Type of param %d does not match: %s vs %s", + $i, + $pfaParam->getType(), + $closureParam->getType(), + )); + } + if ($pfaParam->isOptional() !== $closureParam->isOptional()) { + throw new Exception(sprintf("Optionalness of param %d does not match: %d vs %d", + $i, + $pfaParam->isOptional(), + $closureParam->isOptional(), + )); + } + } + } catch (Exception $e) { + echo $e->getMessage(), "\n"; + echo $pfaReflector; + echo $closureReflector; + } + + $args = []; + foreach ($pfaReflector->getParameters() as $i => $p) { + $args[] = match ((string) $p->getType()) { + 'int' => 100 + $i, + 'float' => 100.5 + $i, + '?float' => 100.5 + $i, + 'string' => (string) (100 + $i), + 'Point' => new Point, + }; + } + + if ($pfa(...$args) !== $closure(...$args)) { + echo "PFA is not equivalent to closure\n"; + } +} + +?> +--EXPECTF-- +# FCC equivalent. The signature is unchanged +# Provide some values, but allow the variadic to remain variadic +# In this version, the partial requires precisely four arguments, the last two of which will get received by things() in the variadic parameter. Note too that $f becomes required in this case. +Name of param 2 does not match: points vs p1 +Partial [ function things ] { + @@ %srfc_examples_002.php 22 - 22 + + - Parameters [4] { + Parameter #0 [ int $i ] + Parameter #1 [ ?float $f ] + Parameter #2 [ Point $points ] + Parameter #3 [ Point $points ] + } + - Return [ array ] +} +Closure [ function {closure:%s:%d} ] { + @@ %srfc_examples_002.php 23 - 23 + + - Parameters [4] { + Parameter #0 [ int $i ] + Parameter #1 [ ?float $f ] + Parameter #2 [ Point $p1 ] + Parameter #3 [ Point $p2 ] + } + - Return [ array ] +} diff --git a/Zend/tests/partial_application/rfc_examples_003.phpt b/Zend/tests/partial_application/rfc_examples_003.phpt new file mode 100644 index 0000000000000..4ac2a386d40bb --- /dev/null +++ b/Zend/tests/partial_application/rfc_examples_003.phpt @@ -0,0 +1,32 @@ +--TEST-- +Partial application RFC examples: errors +--FILE-- + 0) { + function stuff(int $i, string $s, float $f, Point $p, int $m = 0) {} +} + +try { + stuff(?); +} catch (Error $e) { + echo $e->getMessage(), "\n"; +} + +try { + stuff(?, ?, ?, ?, ?, ?); +} catch (Error $e) { + echo $e->getMessage(), "\n"; +} + +try { + stuff(?, ?, 3.5, null, i: 5); +} catch (Error $e) { + echo $e->getMessage(), "\n"; +} + +?> +--EXPECTF-- +not enough arguments or placeholders for application of stuff, 1 given and at least 4 expected, declared in %s on line %d +too many arguments or placeholders for application of stuff, 6 given and a maximum of 5 expected, declared in %s on line %d +Named parameter $i overwrites previous placeholder diff --git a/Zend/tests/partial_application/rfc_examples_004.phpt b/Zend/tests/partial_application/rfc_examples_004.phpt new file mode 100644 index 0000000000000..9b767a9f604c7 --- /dev/null +++ b/Zend/tests/partial_application/rfc_examples_004.phpt @@ -0,0 +1,14 @@ +--TEST-- +Partial application RFC examples: errors +--FILE-- + 0) { + function stuff(int $i, string $s, float $f, Point $p, int $m = 0) {} +} + +stuff(i:1, ?, ?, ?, ?); + +?> +--EXPECTF-- +Fatal error: Cannot use positional argument after named argument in %s on line %d diff --git a/Zend/tests/partial_application/rfc_examples_005.phpt b/Zend/tests/partial_application/rfc_examples_005.phpt new file mode 100644 index 0000000000000..17a2e30a69224 --- /dev/null +++ b/Zend/tests/partial_application/rfc_examples_005.phpt @@ -0,0 +1,14 @@ +--TEST-- +Partial application RFC examples: errors +--FILE-- + 0) { + function stuff(int $i, string $s, float $f, Point $p, int $m = 0) {} +} + +stuff(?, ?, ?, p: $point, ?); + +?> +--EXPECTF-- +Fatal error: Cannot use positional argument after named argument in %s on line %d diff --git a/Zend/tests/partial_application/rfc_examples_006.phpt b/Zend/tests/partial_application/rfc_examples_006.phpt new file mode 100644 index 0000000000000..dc20af37f5291 --- /dev/null +++ b/Zend/tests/partial_application/rfc_examples_006.phpt @@ -0,0 +1,29 @@ +--TEST-- +Partial application RFC examples: func_get_args() +--FILE-- + +--EXPECT-- +2 +int(1) +int(2) +int(3) +int(4) +2 +int(1) +int(2) +int(3) +int(4) diff --git a/Zend/tests/partial_application/rfc_examples_007.phpt b/Zend/tests/partial_application/rfc_examples_007.phpt new file mode 100644 index 0000000000000..9bef206caef85 --- /dev/null +++ b/Zend/tests/partial_application/rfc_examples_007.phpt @@ -0,0 +1,30 @@ +--TEST-- +Partial application RFC examples: evaluation order +--FILE-- + speak($who, getArg()); +print "Arnaud\n"; +$arrow('Larry'); + +$partial = speak(?, getArg()); +print "Arnaud\n"; +$partial('Larry'); + +?> +--EXPECT-- +Arnaud +getArg +Larry: hi +getArg +Arnaud +Larry: hi diff --git a/Zend/tests/partial_application/rfc_examples_008.phpt b/Zend/tests/partial_application/rfc_examples_008.phpt new file mode 100644 index 0000000000000..6fd4b587499d4 --- /dev/null +++ b/Zend/tests/partial_application/rfc_examples_008.phpt @@ -0,0 +1,25 @@ +--TEST-- +Partial application RFC examples: magic methods +--FILE-- +method(?, ?); + +$m(1, 2); + +?> +--EXPECTF-- +Foo::method +Array +( + [0] => 1 + [1] => 2 +) diff --git a/Zend/tests/partial_application/rfc_examples_009.phpt b/Zend/tests/partial_application/rfc_examples_009.phpt new file mode 100644 index 0000000000000..c66b0bd9d5633 --- /dev/null +++ b/Zend/tests/partial_application/rfc_examples_009.phpt @@ -0,0 +1,24 @@ +--TEST-- +Partial application RFC examples: unary function example +--FILE-- + +--EXPECT-- +array(4) { + [0]=> + bool(false) + [1]=> + bool(true) + [2]=> + bool(false) + [3]=> + bool(true) +} diff --git a/Zend/tests/partial_application/rfc_examples_010.phpt b/Zend/tests/partial_application/rfc_examples_010.phpt new file mode 100644 index 0000000000000..09e1647baa2aa --- /dev/null +++ b/Zend/tests/partial_application/rfc_examples_010.phpt @@ -0,0 +1,24 @@ +--TEST-- +Partial application RFC examples: delayed execution +--FILE-- + +--EXPECTF-- +object(Point)#%d (0) { +} diff --git a/Zend/tests/partial_application/static_method_001.phpt b/Zend/tests/partial_application/static_method_001.phpt new file mode 100644 index 0000000000000..d66bc4c451538 --- /dev/null +++ b/Zend/tests/partial_application/static_method_001.phpt @@ -0,0 +1,18 @@ +--TEST-- +Partial application static method +--FILE-- + +--EXPECTF-- +Foo::method diff --git a/Zend/tests/partial_application/statics_001.phpt b/Zend/tests/partial_application/statics_001.phpt new file mode 100644 index 0000000000000..3f8d80a5c3876 --- /dev/null +++ b/Zend/tests/partial_application/statics_001.phpt @@ -0,0 +1,22 @@ +--TEST-- +Partial application static variables shared +--FILE-- + +--EXPECTF-- +OK diff --git a/Zend/tests/partial_application/this_001.phpt b/Zend/tests/partial_application/this_001.phpt new file mode 100644 index 0000000000000..dc8726e77ec63 --- /dev/null +++ b/Zend/tests/partial_application/this_001.phpt @@ -0,0 +1,22 @@ +--TEST-- +Partial application this +--FILE-- +method(new stdClass, ...); + +$baz = $bar(new stdClass, ...); + +var_dump($baz()); +?> +--EXPECTF-- +object(Foo)#%d (0) { +} + diff --git a/Zend/tests/partial_application/variation_call_001.phpt b/Zend/tests/partial_application/variation_call_001.phpt new file mode 100644 index 0000000000000..744e0aed7fd11 --- /dev/null +++ b/Zend/tests/partial_application/variation_call_001.phpt @@ -0,0 +1,31 @@ +--TEST-- +Partial application variation call +--FILE-- +method(?, new Param); + +$closure(1); + +$closure->call(new Foo(), 10); +?> +--EXPECT-- +Bar: 1, Param +Foo: 10, Param diff --git a/Zend/tests/partial_application/variation_closure_001.phpt b/Zend/tests/partial_application/variation_closure_001.phpt new file mode 100644 index 0000000000000..fd6cfd00a1117 --- /dev/null +++ b/Zend/tests/partial_application/variation_closure_001.phpt @@ -0,0 +1,18 @@ +--TEST-- +Partial application variation closure +--FILE-- + +--EXPECTF-- +Partial [ function {closure:%s:%d} ] { + @@ %s 6 - 6 + + - Parameters [1] { + Parameter #0 [ $b ] + } +} diff --git a/Zend/tests/partial_application/variation_closure_002.phpt b/Zend/tests/partial_application/variation_closure_002.phpt new file mode 100644 index 0000000000000..b439a0337551e --- /dev/null +++ b/Zend/tests/partial_application/variation_closure_002.phpt @@ -0,0 +1,24 @@ +--TEST-- +Partial application variation closure __invoke +--FILE-- +__invoke(1, ?); + +echo (string) new ReflectionFunction($function); + +$function(10); +?> +--EXPECTF-- +Partial [ public method __invoke ] { + @@ %svariation_closure_002.php 6 - 6 + + - Parameters [1] { + Parameter #0 [ $b ] + } +} +int(1) +int(10) diff --git a/Zend/tests/partial_application/variation_closure_003.phpt b/Zend/tests/partial_application/variation_closure_003.phpt new file mode 100644 index 0000000000000..52d0080fd3909 --- /dev/null +++ b/Zend/tests/partial_application/variation_closure_003.phpt @@ -0,0 +1,43 @@ +--TEST-- +Partial application variation closure __invoke with this +--FILE-- +bar(); + +$function = $closure->__invoke(1, ?); + +echo (string) new ReflectionFunction($function); + +var_dump($function(10)); +?> +--EXPECTF-- +Partial [ public method __invoke ] { + @@ %svariation_closure_003.php 14 - 14 + + - Parameters [1] { + Parameter #0 [ $b ] + } +} +array(2) { + [0]=> + object(Foo)#1 (0) { + } + [1]=> + array(2) { + [0]=> + int(1) + [1]=> + int(10) + } +} + diff --git a/Zend/tests/partial_application/variation_debug_001.phpt b/Zend/tests/partial_application/variation_debug_001.phpt new file mode 100644 index 0000000000000..5a6ee48998f97 --- /dev/null +++ b/Zend/tests/partial_application/variation_debug_001.phpt @@ -0,0 +1,42 @@ +--TEST-- +Partial application variation debug user +--FILE-- + +--EXPECTF-- +object(Closure)#%d (5) { + ["name"]=> + string(3) "bar" + ["file"]=> + string(83) "%svariation_debug_001.php" + ["line"]=> + int(6) + ["parameter"]=> + array(1) { + ["$a"]=> + string(10) "" + } + ["args"]=> + array(3) { + ["a"]=> + NULL + ["b"]=> + object(stdClass)#%d (0) { + } + ["c"]=> + array(3) { + [0]=> + int(20) + [1]=> + object(stdClass)#%d (0) { + } + ["four"]=> + int(4) + } + } +} diff --git a/Zend/tests/partial_application/variation_debug_002.phpt b/Zend/tests/partial_application/variation_debug_002.phpt new file mode 100644 index 0000000000000..3d3fe6a81dbf7 --- /dev/null +++ b/Zend/tests/partial_application/variation_debug_002.phpt @@ -0,0 +1,51 @@ +--TEST-- +Partial application variation debug internal +--FILE-- + +--EXPECTF-- +object(Closure)#%d (5) { + ["name"]=> + string(9) "array_map" + ["file"]=> + string(83) "%svariation_debug_002.php" + ["line"]=> + int(2) + ["parameter"]=> + array(2) { + ["$callback"]=> + string(10) "" + ["$arrays"]=> + string(10) "" + } + ["args"]=> + array(3) { + ["callback"]=> + NULL + ["array"]=> + array(3) { + [0]=> + int(1) + [1]=> + int(2) + [2]=> + int(3) + } + ["arrays"]=> + array(2) { + [0]=> + array(3) { + [0]=> + int(4) + [1]=> + int(5) + [2]=> + int(6) + } + ["four"]=> + object(stdClass)#%d (0) { + } + } + } +} diff --git a/Zend/tests/partial_application/variation_ex_001.phpt b/Zend/tests/partial_application/variation_ex_001.phpt new file mode 100644 index 0000000000000..9965b6fa6e98c --- /dev/null +++ b/Zend/tests/partial_application/variation_ex_001.phpt @@ -0,0 +1,14 @@ +--TEST-- +Partial application variation uaf in cleanup unfinished calls +--FILE-- + +--EXPECT-- +OK diff --git a/Zend/tests/partial_application/variation_gc_001.phpt b/Zend/tests/partial_application/variation_gc_001.phpt new file mode 100644 index 0000000000000..cbb198632f823 --- /dev/null +++ b/Zend/tests/partial_application/variation_gc_001.phpt @@ -0,0 +1,19 @@ +--TEST-- +Partial application variation GC +--FILE-- +method = self::__construct(new stdClass, ...); + } +} + +$foo = new Foo(new stdClass); +$foo->bar = $foo; + +echo "OK"; +?> +--EXPECT-- +OK diff --git a/Zend/tests/partial_application/variation_gc_002.phpt b/Zend/tests/partial_application/variation_gc_002.phpt new file mode 100644 index 0000000000000..5f00705cf79b8 --- /dev/null +++ b/Zend/tests/partial_application/variation_gc_002.phpt @@ -0,0 +1,11 @@ +--TEST-- +Partial application variation GC +--FILE-- +prop = var_dump($obj, ?); + +echo "OK"; +?> +--EXPECT-- +OK diff --git a/Zend/tests/partial_application/variation_gc_003.phpt b/Zend/tests/partial_application/variation_gc_003.phpt new file mode 100644 index 0000000000000..c919d488eac14 --- /dev/null +++ b/Zend/tests/partial_application/variation_gc_003.phpt @@ -0,0 +1,15 @@ +--TEST-- +Partial application variation GC +--FILE-- +prop = test(?, x: $obj); + +echo "OK"; +?> +--EXPECT-- +OK diff --git a/Zend/tests/partial_application/variation_invoke_001.phpt b/Zend/tests/partial_application/variation_invoke_001.phpt new file mode 100644 index 0000000000000..eed2ad12b17b4 --- /dev/null +++ b/Zend/tests/partial_application/variation_invoke_001.phpt @@ -0,0 +1,23 @@ +--TEST-- +Partial application variation __invoke +--FILE-- +__invoke(32) == 42) { + echo "OK\n"; +} + +try { + $foo->nothing(); +} catch (Error $ex) { + echo "OK"; +} +?> +--EXPECT-- +OK +OK diff --git a/Zend/tests/partial_application/variation_nocall_001.phpt b/Zend/tests/partial_application/variation_nocall_001.phpt new file mode 100644 index 0000000000000..1f570bad2cfa5 --- /dev/null +++ b/Zend/tests/partial_application/variation_nocall_001.phpt @@ -0,0 +1,12 @@ +--TEST-- +Partial application variation no call args leak +--FILE-- + +--EXPECT-- +OK diff --git a/Zend/tests/partial_application/variation_nocall_002.phpt b/Zend/tests/partial_application/variation_nocall_002.phpt new file mode 100644 index 0000000000000..f6b5f3fbf3461 --- /dev/null +++ b/Zend/tests/partial_application/variation_nocall_002.phpt @@ -0,0 +1,14 @@ +--TEST-- +Partial application variation no call order of destruction +--FILE-- +method(new stdClass, ...)(new stdClass, ...); + +echo "OK"; +?> +--EXPECT-- +OK diff --git a/Zend/tests/partial_application/variation_parent_001.phpt b/Zend/tests/partial_application/variation_parent_001.phpt new file mode 100644 index 0000000000000..cf1b2f06565ba --- /dev/null +++ b/Zend/tests/partial_application/variation_parent_001.phpt @@ -0,0 +1,56 @@ +--TEST-- +Partial application variation parent +--FILE-- +method(10, ...); +$baz = $bar(20, ...); + +var_dump($baz, $baz()); +?> +--EXPECTF-- +object(Closure)#%d (6) { + ["name"]=> + string(6) "method" + ["file"]=> + string(84) "%svariation_parent_001.php" + ["line"]=> + int(12) + ["this"]=> + object(Foo)#%d (0) { + } + ["parameter"]=> + array(1) { + ["$c"]=> + string(10) "" + } + ["args"]=> + array(3) { + ["a"]=> + int(10) + ["b"]=> + int(20) + ["c"]=> + array(0) { + } + } +} +object(Closure)#%d (4) { + ["name"]=> + string(25) "{closure:Foo::method():4}" + ["file"]=> + string(84) "%s" + ["line"]=> + int(4) + ["this"]=> + object(Foo)#%d (0) { + } +} diff --git a/Zend/tests/partial_application/variation_scope_001.phpt b/Zend/tests/partial_application/variation_scope_001.phpt new file mode 100644 index 0000000000000..4cda24a7834ef --- /dev/null +++ b/Zend/tests/partial_application/variation_scope_001.phpt @@ -0,0 +1,18 @@ +--TEST-- +Partial application variation called scope +--FILE-- +method(new stdClass, ...); + +$bar(); +?> +--EXPECT-- +Foo::method diff --git a/Zend/tests/partial_application/variation_strict_001.phpt b/Zend/tests/partial_application/variation_strict_001.phpt new file mode 100644 index 0000000000000..0817a5f9ab170 --- /dev/null +++ b/Zend/tests/partial_application/variation_strict_001.phpt @@ -0,0 +1,21 @@ +--TEST-- +Partial application variation: strict_types declared +--FILE-- +getMessage()); +} +?> +--EXPECTF-- +foo(): Argument #1 ($int) must be of type int, string given, called in %s on line %d + diff --git a/Zend/tests/partial_application/variation_variadics_001.phpt b/Zend/tests/partial_application/variation_variadics_001.phpt new file mode 100644 index 0000000000000..d92287e853ee8 --- /dev/null +++ b/Zend/tests/partial_application/variation_variadics_001.phpt @@ -0,0 +1,26 @@ +--TEST-- +Partial application variation variadics user +--FILE-- + +--EXPECTF-- +Partial [ function foo ] { + @@ %s 6 - 6 + + - Parameters [1] { + Parameter #0 [ ...$b ] + } +} +int(10) +int(100) +int(1000) +int(10000) diff --git a/Zend/tests/partial_application/variation_variadics_002.phpt b/Zend/tests/partial_application/variation_variadics_002.phpt new file mode 100644 index 0000000000000..97173349fa8a6 --- /dev/null +++ b/Zend/tests/partial_application/variation_variadics_002.phpt @@ -0,0 +1,20 @@ +--TEST-- +Partial application variation variadics internal +--FILE-- + +--EXPECTF-- +Partial [ function sprintf ] { + @@ %svariation_variadics_002.php 2 - 2 + + - Parameters [1] { + Parameter #0 [ mixed ...$values ] + } + - Return [ string ] +} +100 1000 10000 diff --git a/Zend/tests/partial_application/variation_variadics_003.phpt b/Zend/tests/partial_application/variation_variadics_003.phpt new file mode 100644 index 0000000000000..c5e41ab1aeb50 --- /dev/null +++ b/Zend/tests/partial_application/variation_variadics_003.phpt @@ -0,0 +1,50 @@ +--TEST-- +Partial application variation variadics interactions +--FILE-- +getMessage()); +} + +try { + $foo = foo(?, ?, ?); // FAIL 2 expected, 3 given +} catch (Error $ex) { + printf("%s\n", $ex->getMessage()); +} + +function bar($a, $b, ...$c) { + var_dump(func_get_args()); +} + +$bar = bar(?, ?); + +try { + $bar(1, 2, 3); // FAIL 3 given, maximum 2 expected +} catch (Error $ex) { + printf("%s\n", $ex->getMessage()); +} + +$foo = foo(?, ?, ...); + +$foo(1, 2, 3); // OK +?> +--EXPECTF-- +too many arguments for application of foo, 3 given and a maximum of 2 expected, declared in %s on line %d +too many arguments or placeholders for application of foo, 3 given and a maximum of 2 expected, declared in %s on line %d +too many arguments for application of bar, 3 given and a maximum of 2 expected, declared in %s on line %d +array(3) { + [0]=> + int(1) + [1]=> + int(2) + [2]=> + int(3) +} diff --git a/Zend/tests/partial_application/variation_variadics_004.phpt b/Zend/tests/partial_application/variation_variadics_004.phpt new file mode 100644 index 0000000000000..e972f6c982ef9 --- /dev/null +++ b/Zend/tests/partial_application/variation_variadics_004.phpt @@ -0,0 +1,65 @@ +--TEST-- +Partial application variation variadics and optional args +--FILE-- + $day, "month" => $month, "year" => $year]; +} + +$foo = foo(..., year: 2006); + +var_dump($foo(2)); + +$foo = foo(..., month: 12); + +$bar = $foo(..., year: 2016); + +var_dump($foo(2)); + +var_dump($bar(2)); + +var_dump($foo()); + +var_dump($bar()); +?> +--EXPECTF-- +array(3) { + ["day"]=> + int(2) + ["month"]=> + int(1) + ["year"]=> + int(2006) +} +array(3) { + ["day"]=> + int(2) + ["month"]=> + int(12) + ["year"]=> + int(2005) +} +array(3) { + ["day"]=> + int(2) + ["month"]=> + int(12) + ["year"]=> + int(2016) +} +array(3) { + ["day"]=> + int(1) + ["month"]=> + int(12) + ["year"]=> + int(2005) +} +array(3) { + ["day"]=> + int(1) + ["month"]=> + int(12) + ["year"]=> + int(2016) +} diff --git a/Zend/tests/partial_application/variation_variadics_006.phpt b/Zend/tests/partial_application/variation_variadics_006.phpt new file mode 100644 index 0000000000000..923bddcbe18f6 --- /dev/null +++ b/Zend/tests/partial_application/variation_variadics_006.phpt @@ -0,0 +1,18 @@ +--TEST-- +Partial application variation named may overwrite variadic placeholder +--FILE-- + +--EXPECTF-- +array(1) { + [0]=> + string(1) "a" +} + diff --git a/Zend/tests/partial_application/variation_variadics_007.phpt b/Zend/tests/partial_application/variation_variadics_007.phpt new file mode 100644 index 0000000000000..1688112787ad8 --- /dev/null +++ b/Zend/tests/partial_application/variation_variadics_007.phpt @@ -0,0 +1,14 @@ +--TEST-- +Partial application variation extra through variadic +--FILE-- + $a + $b)); +?> +--EXPECT-- +int(3) diff --git a/Zend/tests/partial_application/variation_variadics_008.phpt b/Zend/tests/partial_application/variation_variadics_008.phpt new file mode 100644 index 0000000000000..d22a4e58fd703 --- /dev/null +++ b/Zend/tests/partial_application/variation_variadics_008.phpt @@ -0,0 +1,17 @@ +--TEST-- +Partial application variation variadics wrong signature checked +--FILE-- +getMessage() . PHP_EOL; +} +?> +--EXPECTF-- +too many arguments or placeholders for application of foo, 2 given and a maximum of 1 expected, declared in %s on line %d + diff --git a/Zend/zend.c b/Zend/zend.c index 2d8a0f455f8b4..7fb4423edfd1c 100644 --- a/Zend/zend.c +++ b/Zend/zend.c @@ -38,6 +38,7 @@ #include "zend_call_stack.h" #include "zend_max_execution_timer.h" #include "zend_hrtime.h" +#include "zend_partial.h" #include "Optimizer/zend_optimizer.h" #include "php.h" #include "php_globals.h" @@ -1077,6 +1078,8 @@ void zend_startup(zend_utility_functions *utility_functions) /* {{{ */ tsrm_set_new_thread_end_handler(zend_new_thread_end_handler); tsrm_set_shutdown_handler(zend_interned_strings_dtor); #endif + + zend_partial_startup(); } /* }}} */ diff --git a/Zend/zend_API.h b/Zend/zend_API.h index 02ec1b18a6b69..ba76b8771ceda 100644 --- a/Zend/zend_API.h +++ b/Zend/zend_API.h @@ -851,7 +851,8 @@ static zend_always_inline void zend_call_known_fcc( { zend_function *func = fcc->function_handler; /* Need to copy trampolines as they get released after they are called */ - if (UNEXPECTED(func->common.fn_flags & ZEND_ACC_CALL_VIA_TRAMPOLINE)) { + if (UNEXPECTED((func->common.fn_flags & ZEND_ACC_CALL_VIA_TRAMPOLINE) + && !(func->common.fn_flags & ZEND_ACC_TRAMPOLINE_PERMANENT))) { func = (zend_function*) emalloc(sizeof(zend_function)); memcpy(func, fcc->function_handler, sizeof(zend_function)); zend_string_addref(func->op_array.function_name); diff --git a/Zend/zend_ast.c b/Zend/zend_ast.c index 728695bd9e930..1917f3c87beb9 100644 --- a/Zend/zend_ast.c +++ b/Zend/zend_ast.c @@ -61,6 +61,7 @@ ZEND_API zend_ast * ZEND_FASTCALL zend_ast_create_fcc(void) { ast->kind = ZEND_AST_CALLABLE_CONVERT; ast->attr = 0; ast->lineno = CG(zend_lineno); + ast->args = NULL; ZEND_MAP_PTR_INIT(ast->fptr, NULL); return (zend_ast *) ast; @@ -157,6 +158,12 @@ ZEND_API zend_ast *zend_ast_create_decl( return (zend_ast *) ast; } +static bool zend_ast_is_placeholder_arg(zend_ast *arg) { + return arg->kind == ZEND_AST_PLACEHOLDER_ARG + || (arg->kind == ZEND_AST_NAMED_ARG + && arg->child[1]->kind == ZEND_AST_PLACEHOLDER_ARG); +} + #if ZEND_AST_SPEC ZEND_API zend_ast * ZEND_FASTCALL zend_ast_create_0(zend_ast_kind kind) { zend_ast *ast; @@ -400,6 +407,34 @@ ZEND_API zend_ast * ZEND_FASTCALL zend_ast_create_list_2(zend_ast_kind kind, zen return ast; } + +ZEND_API zend_ast * ZEND_FASTCALL zend_ast_create_arg_list_0(zend_ast_kind kind) { + return zend_ast_create_list(0, kind); +} + +ZEND_API zend_ast * ZEND_FASTCALL zend_ast_create_arg_list_1(zend_ast_kind kind, zend_ast *arg) { + zend_ast *list = zend_ast_create_list(1, kind, arg); + + if (zend_ast_is_placeholder_arg(arg)) { + zend_ast_fcc *fcc_ast = (zend_ast_fcc*)zend_ast_create_fcc(); + fcc_ast->args = list; + return (zend_ast*)fcc_ast; + } + + return list; +} + +ZEND_API zend_ast * ZEND_FASTCALL zend_ast_create_arg_list_2(zend_ast_kind kind, zend_ast *arg1, zend_ast *arg2) { + zend_ast *list = zend_ast_create_list(1, kind, arg1, arg2); + + if (zend_ast_is_placeholder_arg(arg1) || zend_ast_is_placeholder_arg(arg2)) { + zend_ast_fcc *fcc_ast = (zend_ast_fcc*)zend_ast_create_fcc(); + fcc_ast->args = list; + return (zend_ast*)fcc_ast; + } + + return list; +} #else static zend_ast *zend_ast_create_from_va_list(zend_ast_kind kind, zend_ast_attr attr, va_list va) { uint32_t i, children = kind >> ZEND_AST_NUM_CHILDREN_SHIFT; @@ -479,6 +514,43 @@ ZEND_API zend_ast *zend_ast_create_list(uint32_t init_children, zend_ast_kind ki return ast; } + +ZEND_API zend_ast *zend_ast_create_arg_list(uint32_t init_children, zend_ast_kind kind, ...) { + zend_ast *ast; + zend_ast_list *list; + bool has_placeholders = false; + + ast = zend_ast_alloc(zend_ast_list_size(4)); + list = (zend_ast_list *) ast; + list->kind = kind; + list->attr = 0; + list->lineno = CG(zend_lineno); + list->children = 0; + + { + va_list va; + uint32_t i; + va_start(va, kind); + for (i = 0; i < init_children; ++i) { + zend_ast *child = va_arg(va, zend_ast *); + ast = zend_ast_list_add(ast, child); + uint32_t lineno = zend_ast_get_lineno(child); + if (lineno < ast->lineno) { + ast->lineno = lineno; + } + has_placeholders = has_placeholders || zend_ast_is_placeholder_arg(child); + } + va_end(va); + } + + if (has_placeholders) { + zend_ast_fcc *fcc_ast = (zend_ast_fcc*)zend_ast_create_fcc(); + fcc_ast->args = ast; + return (zend_ast*)fcc_ast; + } + + return ast; +} #endif zend_ast *zend_ast_create_concat_op(zend_ast *op0, zend_ast *op1) { @@ -508,6 +580,25 @@ ZEND_API zend_ast * ZEND_FASTCALL zend_ast_list_add(zend_ast *ast, zend_ast *op) return (zend_ast *) list; } +ZEND_API zend_ast * ZEND_FASTCALL zend_ast_arg_list_add(zend_ast *list, zend_ast *arg) +{ + if (list->kind == ZEND_AST_CALLABLE_CONVERT) { + zend_ast_fcc *fcc_ast = (zend_ast_fcc*)list; + fcc_ast->args = zend_ast_list_add(fcc_ast->args, arg); + return (zend_ast*)fcc_ast; + } + + ZEND_ASSERT(list->kind == ZEND_AST_ARG_LIST); + + if (zend_ast_is_placeholder_arg(arg)) { + zend_ast_fcc *fcc_ast = (zend_ast_fcc*)zend_ast_create_fcc(); + fcc_ast->args = zend_ast_list_add(list, arg); + return (zend_ast*)fcc_ast; + } + + return zend_ast_list_add(list, arg); +} + static zend_result zend_ast_add_array_element(zval *result, zval *offset, zval *expr) { if (Z_TYPE_P(offset) == IS_UNDEF) { @@ -1058,6 +1149,14 @@ ZEND_API zend_result ZEND_FASTCALL zend_ast_evaluate_inner( case ZEND_AST_CALL: { ZEND_ASSERT(ast->child[1]->kind == ZEND_AST_CALLABLE_CONVERT); zend_ast_fcc *fcc_ast = (zend_ast_fcc*)ast->child[1]; + + zend_ast_list *args = zend_ast_get_list(fcc_ast->args); + ZEND_ASSERT(args->children > 0); + if (args->children != 1 || args->child[0]->attr != _IS_PLACEHOLDER_VARIADIC) { + /* TODO: PFAs */ + return FAILURE; + } + fptr = ZEND_MAP_PTR_GET(fcc_ast->fptr); if (!fptr) { @@ -1085,6 +1184,13 @@ ZEND_API zend_result ZEND_FASTCALL zend_ast_evaluate_inner( ZEND_ASSERT(ast->child[2]->kind == ZEND_AST_CALLABLE_CONVERT); zend_ast_fcc *fcc_ast = (zend_ast_fcc*)ast->child[2]; + zend_ast_list *args = zend_ast_get_list(fcc_ast->args); + ZEND_ASSERT(args->children > 0); + if (args->children != 1 || args->child[0]->attr != _IS_PLACEHOLDER_VARIADIC) { + /* TODO: PFAs */ + return FAILURE; + } + fptr = ZEND_MAP_PTR_GET(fcc_ast->fptr); if (!fptr) { @@ -1127,12 +1233,12 @@ ZEND_API zend_result ZEND_FASTCALL zend_ast_evaluate_inner( if (!(fptr->common.fn_flags & ZEND_ACC_STATIC)) { zend_non_static_method_call(fptr); - + return FAILURE; } if ((fptr->common.fn_flags & ZEND_ACC_ABSTRACT)) { zend_abstract_method_call(fptr); - + return FAILURE; } else if (fptr->common.scope->ce_flags & ZEND_ACC_TRAIT) { zend_error(E_DEPRECATED, @@ -1246,7 +1352,8 @@ static size_t ZEND_FASTCALL zend_ast_tree_size(zend_ast *ast) } else if (ast->kind == ZEND_AST_OP_ARRAY) { size = sizeof(zend_ast_op_array); } else if (ast->kind == ZEND_AST_CALLABLE_CONVERT) { - size = sizeof(zend_ast_fcc); + zend_ast *args_ast = ((zend_ast_fcc*)ast)->args; + size = sizeof(zend_ast_fcc) + zend_ast_tree_size(args_ast); } else if (zend_ast_is_list(ast)) { uint32_t i; zend_ast_list *list = zend_ast_get_list(ast); @@ -1323,6 +1430,8 @@ static void* ZEND_FASTCALL zend_ast_tree_copy(zend_ast *ast, void *buf) new->lineno = old->lineno; ZEND_MAP_PTR_INIT(new->fptr, ZEND_MAP_PTR(old->fptr)); buf = (void*)((char*)buf + sizeof(zend_ast_fcc)); + new->args = buf; + buf = zend_ast_tree_copy(old->args, buf); } else if (zend_ast_is_decl(ast)) { /* Not implemented. */ ZEND_UNREACHABLE(); @@ -1406,6 +1515,10 @@ ZEND_API void ZEND_FASTCALL zend_ast_destroy(zend_ast *ast) zend_ast_destroy(decl->child[3]); ast = decl->child[4]; goto tail_call; + } else if (EXPECTED(ast->kind == ZEND_AST_CALLABLE_CONVERT)) { + zend_ast_fcc *fcc_ast = (zend_ast_fcc*) ast; + + zend_ast_destroy(fcc_ast->args); } } @@ -2296,6 +2409,13 @@ static ZEND_COLD void zend_ast_export_ex(smart_str *str, zend_ast *ast, int prio EMPTY_SWITCH_DEFAULT_CASE(); } break; + case ZEND_AST_PLACEHOLDER_ARG: + if (ast->attr == _IS_PLACEHOLDER_ARG) { + APPEND_STR("?"); + } else if (ast->attr == _IS_PLACEHOLDER_VARIADIC) { + APPEND_STR("..."); + } + break; /* 1 child node */ case ZEND_AST_VAR: @@ -2442,9 +2562,10 @@ static ZEND_COLD void zend_ast_export_ex(smart_str *str, zend_ast *ast, int prio zend_ast_export_ex(str, ast->child[1], 0, indent); smart_str_appendc(str, ')'); break; - case ZEND_AST_CALLABLE_CONVERT: - smart_str_appends(str, "..."); - break; + case ZEND_AST_CALLABLE_CONVERT:; + zend_ast_fcc *fcc_ast = (zend_ast_fcc*)ast; + ast = fcc_ast->args; + goto simple_list; case ZEND_AST_CLASS_CONST: zend_ast_export_ns_name(str, ast->child[0], 0, indent); smart_str_appends(str, "::"); @@ -2963,3 +3084,14 @@ zend_ast * ZEND_FASTCALL zend_ast_with_attributes(zend_ast *ast, zend_ast *attr) return ast; } + +zend_ast * ZEND_FASTCALL zend_ast_call_get_args(zend_ast *ast) +{ + if (ast->kind == ZEND_AST_CALL) { + return ast->child[1]; + } else if (ast->kind == ZEND_AST_STATIC_CALL || ast->kind == ZEND_AST_METHOD_CALL) { + return ast->child[2]; + } + + return NULL; +} diff --git a/Zend/zend_ast.h b/Zend/zend_ast.h index 08400cff5dd8e..5483eb7b88a3f 100644 --- a/Zend/zend_ast.h +++ b/Zend/zend_ast.h @@ -76,6 +76,7 @@ enum _zend_ast_kind { ZEND_AST_TYPE, ZEND_AST_CONSTANT_CLASS, ZEND_AST_CALLABLE_CONVERT, + ZEND_AST_PLACEHOLDER_ARG, /* 1 child node */ ZEND_AST_VAR = 1 << ZEND_AST_NUM_CHILDREN_SHIFT, @@ -229,10 +230,12 @@ typedef struct _zend_ast_decl { zend_ast *child[5]; } zend_ast_decl; +// TODO: rename typedef struct _zend_ast_fcc { zend_ast_kind kind; /* Type of the node (ZEND_AST_* enum constant) */ zend_ast_attr attr; /* Additional attribute, use depending on node type */ uint32_t lineno; /* Line number */ + zend_ast *args; ZEND_MAP_PTR_DEF(zend_function *, fptr); } zend_ast_fcc; @@ -307,21 +310,33 @@ ZEND_API zend_ast * ZEND_FASTCALL zend_ast_create_list_0(zend_ast_kind kind); ZEND_API zend_ast * ZEND_FASTCALL zend_ast_create_list_1(zend_ast_kind kind, zend_ast *child); ZEND_API zend_ast * ZEND_FASTCALL zend_ast_create_list_2(zend_ast_kind kind, zend_ast *child1, zend_ast *child2); +ZEND_API zend_ast * ZEND_FASTCALL zend_ast_create_arg_list_0(zend_ast_kind kind); +ZEND_API zend_ast * ZEND_FASTCALL zend_ast_create_arg_list_1(zend_ast_kind kind, zend_ast *child); +ZEND_API zend_ast * ZEND_FASTCALL zend_ast_create_arg_list_2(zend_ast_kind kind, zend_ast *child1, zend_ast *child2); + # define zend_ast_create(...) \ ZEND_AST_SPEC_CALL(zend_ast_create, __VA_ARGS__) # define zend_ast_create_ex(...) \ ZEND_AST_SPEC_CALL_EX(zend_ast_create_ex, __VA_ARGS__) # define zend_ast_create_list(init_children, ...) \ ZEND_AST_SPEC_CALL(zend_ast_create_list, __VA_ARGS__) +# define zend_ast_create_arg_list(init_children, ...) \ + ZEND_AST_SPEC_CALL(zend_ast_create_arg_list, __VA_ARGS__) #else ZEND_API zend_ast *zend_ast_create(zend_ast_kind kind, ...); ZEND_API zend_ast *zend_ast_create_ex(zend_ast_kind kind, zend_ast_attr attr, ...); ZEND_API zend_ast *zend_ast_create_list(uint32_t init_children, zend_ast_kind kind, ...); +ZEND_API zend_ast *zend_ast_create_arg_list(uint32_t init_children, zend_ast_kind kind, ...); #endif ZEND_API zend_ast * ZEND_FASTCALL zend_ast_list_add(zend_ast *list, zend_ast *op); +/* Like zend_ast_list_add(), but wraps the list into a ZEND_AST_CALLABLE_CONVERT + * if any arg is a ZEND_AST_PLACEHOLDER_ARG. list can be a zend_ast_list, or a + * zend_ast_fcc. */ +ZEND_API zend_ast * ZEND_FASTCALL zend_ast_arg_list_add(zend_ast *list, zend_ast *arg); + ZEND_API zend_ast *zend_ast_create_decl( zend_ast_kind kind, uint32_t flags, uint32_t start_lineno, zend_string *doc_comment, zend_string *name, zend_ast *child0, zend_ast *child1, zend_ast *child2, zend_ast *child3, zend_ast *child4 @@ -425,4 +440,6 @@ static zend_always_inline zend_ast *zend_ast_list_rtrim(zend_ast *ast) { zend_ast * ZEND_FASTCALL zend_ast_with_attributes(zend_ast *ast, zend_ast *attr); +zend_ast * ZEND_FASTCALL zend_ast_call_get_args(zend_ast *ast); + #endif diff --git a/Zend/zend_builtin_functions.c b/Zend/zend_builtin_functions.c index 48e0e86d3b2f8..4c49ac62cd9cf 100644 --- a/Zend/zend_builtin_functions.c +++ b/Zend/zend_builtin_functions.c @@ -1734,6 +1734,10 @@ static void debug_backtrace_get_args(zend_execute_data *call, zval *arg_array) / ZEND_HASH_FILL_PACKED(Z_ARRVAL_P(arg_array)) { if (call->func->type == ZEND_USER_FUNCTION) { uint32_t first_extra_arg = MIN(num_args, call->func->op_array.num_args); + if (ZEND_CALL_INFO(call) & ZEND_CALL_TRAMPOLINE) { + /* Trampolines use a contiguous layout */ + first_extra_arg = num_args; + } if (UNEXPECTED(ZEND_CALL_INFO(call) & ZEND_CALL_HAS_SYMBOL_TABLE)) { /* In case of attached symbol_table, values on stack may be invalid diff --git a/Zend/zend_closures.c b/Zend/zend_closures.c index 5777e1a34a2b8..916fb9a393995 100644 --- a/Zend/zend_closures.c +++ b/Zend/zend_closures.c @@ -25,6 +25,7 @@ #include "zend_interfaces.h" #include "zend_objects.h" #include "zend_objects_API.h" +#include "zend_partial.h" #include "zend_globals.h" #include "zend_closures_arginfo.h" @@ -32,6 +33,7 @@ typedef struct _zend_closure { zend_object std; zend_function func; zval this_ptr; + uint32_t closure_flags; zend_class_entry *called_scope; zif_handler orig_internal_handler; } zend_closure; @@ -40,6 +42,8 @@ typedef struct _zend_closure { ZEND_API zend_class_entry *zend_ce_closure; static zend_object_handlers closure_handlers; +static zval zend_closure_no_this; + static zend_result zend_closure_get_closure(zend_object *obj, zend_class_entry **ce_ptr, zend_function **fptr_ptr, zend_object **obj_ptr, bool check_only); ZEND_METHOD(Closure, __invoke) /* {{{ */ @@ -56,13 +60,15 @@ ZEND_METHOD(Closure, __invoke) /* {{{ */ zend_fcall_info_cache fcc = { .closure = Z_OBJ_P(ZEND_THIS), }; - zend_closure_get_closure(Z_OBJ_P(ZEND_THIS), &fcc.calling_scope, &fcc.function_handler, &fcc.object, false); + Z_OBJ_P(ZEND_THIS)->handlers->get_closure(Z_OBJ_P(ZEND_THIS), &fcc.calling_scope, &fcc.function_handler, &fcc.object, false); fcc.called_scope = fcc.calling_scope; zend_call_known_fcc(&fcc, return_value, num_args, args, named_args); - /* destruct the function also, then - we have allocated it in get_method */ - zend_string_release_ex(func->internal_function.function_name, 0); - efree(func); + if (!(func->common.fn_flags & ZEND_ACC_TRAMPOLINE_PERMANENT)) { + /* destruct the function also, then - we have allocated it in get_method */ + zend_string_release_ex(func->internal_function.function_name, 0); + efree(func); + } /* Set the func pointer to NULL. Prior to PHP 8.3, this was only done for debug builds, * because debug builds check certain properties after the call and needed to know this @@ -78,7 +84,9 @@ static bool zend_valid_closure_binding( zend_closure *closure, zval *newthis, zend_class_entry *scope) /* {{{ */ { zend_function *func = &closure->func; - bool is_fake_closure = (func->common.fn_flags & ZEND_ACC_FAKE_CLOSURE) != 0; + bool is_fake_closure = (func->common.fn_flags & ZEND_ACC_FAKE_CLOSURE) != 0 + || (closure->closure_flags & ZEND_CLOSURE_PARTIAL); + if (newthis) { if (func->common.fn_flags & ZEND_ACC_STATIC) { zend_error(E_WARNING, "Cannot bind an instance to a static closure"); @@ -159,7 +167,19 @@ ZEND_METHOD(Closure, call) ZVAL_UNDEF(&closure_result); fci.retval = &closure_result; - if (closure->func.common.fn_flags & ZEND_ACC_GENERATOR) { + if (closure->closure_flags & ZEND_CLOSURE_PARTIAL) { + zval new_closure; + zend_partial_bind(&new_closure, ZEND_THIS, newthis, newclass); + closure = (zend_closure *) Z_OBJ(new_closure); + fci_cache.function_handler = zend_partial_get_trampoline(Z_OBJ(new_closure)); + fci_cache.object = fci.object = Z_OBJ(new_closure); + + zend_call_function(&fci, &fci_cache); + + if (!fci_cache.function_handler) { + OBJ_RELEASE(Z_OBJ(new_closure)); + } + } else if (closure->func.common.fn_flags & ZEND_ACC_GENERATOR) { zval new_closure; zend_create_closure(&new_closure, &closure->func, newclass, closure->called_scope, newthis); closure = (zend_closure *) Z_OBJ(new_closure); @@ -251,7 +271,11 @@ static void do_closure_bind(zval *return_value, zval *zclosure, zval *newthis, z called_scope = ce; } - zend_create_closure(return_value, &closure->func, ce, called_scope, newthis); + if (closure->closure_flags & ZEND_CLOSURE_PARTIAL) { + zend_partial_bind(return_value, zclosure, newthis, called_scope); + } else { + zend_create_closure(return_value, &closure->func, ce, called_scope, newthis); + } } /* {{{ Create a closure from another one and bind to another object and scope */ @@ -504,6 +528,12 @@ ZEND_API const zend_function *zend_get_closure_method_def(zend_object *obj) /* { ZEND_API zval* zend_get_closure_this_ptr(zval *obj) /* {{{ */ { zend_closure *closure = (zend_closure *)Z_OBJ_P(obj); + + if (UNEXPECTED(Z_TYPE(closure->this_ptr) != IS_OBJECT)) { + /* zend_partial This may refer to a type */ + return &zend_closure_no_this; + } + return &closure->this_ptr; } /* }}} */ @@ -582,7 +612,7 @@ static zend_result zend_closure_get_closure(zend_object *obj, zend_class_entry * /* }}} */ /* *is_temp is int due to Object Handler API */ -static HashTable *zend_closure_get_debug_info(zend_object *object, int *is_temp) /* {{{ */ +HashTable *zend_closure_get_debug_info(zend_object *object, int *is_temp) /* {{{ */ { zend_closure *closure = (zend_closure *)object; zval val; @@ -644,7 +674,7 @@ static HashTable *zend_closure_get_debug_info(zend_object *object, int *is_temp) } } - if (Z_TYPE(closure->this_ptr) != IS_UNDEF) { + if (Z_TYPE(closure->this_ptr) == IS_OBJECT) { Z_ADDREF(closure->this_ptr); zend_hash_update(debug_info, ZSTR_KNOWN(ZEND_STR_THIS), &closure->this_ptr); } @@ -720,6 +750,8 @@ void zend_register_closure_ce(void) /* {{{ */ closure_handlers.get_debug_info = zend_closure_get_debug_info; closure_handlers.get_closure = zend_closure_get_closure; closure_handlers.get_gc = zend_closure_get_gc; + + ZVAL_UNDEF(&zend_closure_no_this); } /* }}} */ diff --git a/Zend/zend_closures.h b/Zend/zend_closures.h index ced1b5ba48c15..d17451980ff47 100644 --- a/Zend/zend_closures.h +++ b/Zend/zend_closures.h @@ -24,6 +24,8 @@ BEGIN_EXTERN_C() +#define ZEND_CLOSURE_PARTIAL (1<<0) + /* This macro depends on zend_closure structure layout */ #define ZEND_CLOSURE_OBJECT(op_array) \ ((zend_object*)((char*)(op_array) - sizeof(zend_object))) @@ -32,6 +34,7 @@ void zend_register_closure_ce(void); void zend_closure_bind_var(zval *closure_zv, zend_string *var_name, zval *var); void zend_closure_bind_var_ex(zval *closure_zv, uint32_t offset, zval *val); void zend_closure_from_frame(zval *closure_zv, zend_execute_data *frame); +HashTable *zend_closure_get_debug_info(zend_object *object, int *is_temp); extern ZEND_API zend_class_entry *zend_ce_closure; diff --git a/Zend/zend_compile.c b/Zend/zend_compile.c index f3f6d1b75aec1..7292997cffbbb 100644 --- a/Zend/zend_compile.c +++ b/Zend/zend_compile.c @@ -3690,11 +3690,13 @@ static uint32_t zend_get_arg_num(zend_function *fn, zend_string *arg_name) { } static uint32_t zend_compile_args( - zend_ast *ast, zend_function *fbc, bool *may_have_extra_named_args) /* {{{ */ + zend_ast *ast, zend_function *fbc, bool is_call_partial, + bool *may_have_extra_named_args) /* {{{ */ { zend_ast_list *args = zend_ast_get_list(ast); uint32_t i; bool uses_arg_unpack = 0; + bool uses_variadic_placeholder = 0; uint32_t arg_count = 0; /* number of arguments not including unpacks */ /* Whether named arguments are used syntactically, to enforce language level limitations. @@ -3720,6 +3722,11 @@ static uint32_t zend_compile_args( "Cannot use argument unpacking after named arguments"); } + if (is_call_partial) { + zend_error_noreturn(E_COMPILE_ERROR, + "Cannot combine partial application and unpacking"); + } + /* Unpack may contain named arguments. */ may_have_undef = 1; if (!fbc || (fbc->common.fn_flags & ZEND_ACC_VARIADIC)) { @@ -3771,9 +3778,47 @@ static uint32_t zend_compile_args( "Cannot use positional argument after named argument"); } + if (uses_variadic_placeholder) { + if (arg->kind == ZEND_AST_PLACEHOLDER_ARG + && arg->attr == _IS_PLACEHOLDER_VARIADIC) { + zend_error_noreturn(E_COMPILE_ERROR, + "Variadic placeholder may only appear once"); + } else { + zend_error_noreturn(E_COMPILE_ERROR, + "Only named arguments may follow variadic placeholder"); + } + } + arg_count++; } + if (arg->kind == ZEND_AST_PLACEHOLDER_ARG) { + if (uses_arg_unpack) { + zend_error_noreturn(E_COMPILE_ERROR, + "Cannot combine partial application and unpacking"); + } + + if (arg->attr == _IS_PLACEHOLDER_VARIADIC) { + uses_variadic_placeholder = true; + } + + fbc = NULL; + + opline = zend_emit_op(NULL, ZEND_SEND_PLACEHOLDER, NULL, NULL); + opline->op1.num = arg->attr; + 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); + } + + continue; + } + /* Treat passing of $GLOBALS the same as passing a call. * This will error at runtime if the argument is by-ref. */ if (zend_is_call(arg) || is_globals_fetch(arg)) { @@ -3884,8 +3929,13 @@ static uint32_t zend_compile_args( } } - if (may_have_undef) { - zend_emit_op(NULL, ZEND_CHECK_UNDEF_ARGS, NULL, NULL); + if (!is_call_partial) { + if (may_have_undef) { + zend_emit_op(NULL, ZEND_CHECK_UNDEF_ARGS, NULL, NULL); + } + } else { + // TODO: merge this opcode with ZEND_CALLABLE_CONVERT_PARTIAL? + zend_emit_op(NULL, ZEND_CHECK_PARTIAL_ARGS, NULL, NULL); } return arg_count; @@ -3925,12 +3975,34 @@ ZEND_API uint8_t zend_get_call_op(const zend_op *init_op, zend_function *fbc, bo } /* }}} */ +void zend_compile_call_partial(znode *result, uint32_t arg_count, bool may_have_extra_named_args, uint32_t opnum_init, zend_function *fbc) { /* {{{ */ + zend_op *opline = &CG(active_op_array)->opcodes[opnum_init]; + + opline->extended_value = arg_count; + + ZEND_ASSERT(opline->opcode != ZEND_NEW); + + if (opline->opcode == ZEND_INIT_FCALL) { + opline->op1.num = zend_vm_calc_used_stack(arg_count, fbc); + } + + opline = zend_emit_op_tmp(result, ZEND_CALLABLE_CONVERT_PARTIAL, + NULL, NULL); + + if (may_have_extra_named_args) { + opline->extended_value = ZEND_FCALL_MAY_HAVE_EXTRA_NAMED_PARAMS; + } +} /* }}} */ + static bool zend_compile_call_common(znode *result, zend_ast *args_ast, zend_function *fbc, uint32_t lineno) /* {{{ */ { zend_op *opline; uint32_t opnum_init = get_next_op_number() - 1; + bool is_partial_call = false; if (args_ast->kind == ZEND_AST_CALLABLE_CONVERT) { + is_partial_call = true; + opline = &CG(active_op_array)->opcodes[opnum_init]; opline->extended_value = 0; @@ -3938,16 +4010,31 @@ static bool zend_compile_call_common(znode *result, zend_ast *args_ast, zend_fun zend_error_noreturn(E_COMPILE_ERROR, "Cannot create Closure for new expression"); } - if (opline->opcode == ZEND_INIT_FCALL) { - opline->op1.num = zend_vm_calc_used_stack(0, fbc); + zend_ast_list *fcc_args = zend_ast_get_list(((zend_ast_fcc*)args_ast)->args); + + /* FCCs are a special case of PFAs with a single variadic placeholder */ + if (fcc_args->children == 1 && fcc_args->child[0]->attr == _IS_PLACEHOLDER_VARIADIC) { + + if (opline->opcode == ZEND_INIT_FCALL) { + opline->op1.num = zend_vm_calc_used_stack(0, fbc); + } + + zend_emit_op_tmp(result, ZEND_CALLABLE_CONVERT, NULL, NULL); + + return true; } - zend_emit_op_tmp(result, ZEND_CALLABLE_CONVERT, NULL, NULL); - return true; + args_ast = ((zend_ast_fcc*)args_ast)->args; } bool may_have_extra_named_args; - uint32_t arg_count = zend_compile_args(args_ast, fbc, &may_have_extra_named_args); + uint32_t arg_count = zend_compile_args(args_ast, fbc, + is_partial_call, &may_have_extra_named_args); + + if (is_partial_call) { + zend_compile_call_partial(result, arg_count, may_have_extra_named_args, opnum_init, fbc); + return true; + } zend_do_extended_fcall_begin(); @@ -4029,7 +4116,8 @@ static inline bool zend_args_contain_unpack_or_named(zend_ast_list *args) /* {{{ uint32_t i; for (i = 0; i < args->children; ++i) { zend_ast *arg = args->child[i]; - if (arg->kind == ZEND_AST_UNPACK || arg->kind == ZEND_AST_NAMED_ARG) { + if (arg->kind == ZEND_AST_UNPACK || + arg->kind == ZEND_AST_NAMED_ARG) { return 1; } } @@ -6426,6 +6514,74 @@ static bool can_match_use_jumptable(zend_ast_list *arms) { return 1; } +static zend_ast *zend_partial_apply(zend_ast *callable_ast, zend_ast *pipe_arg) +{ + if (callable_ast->kind != ZEND_AST_CALL + && callable_ast->kind != ZEND_AST_STATIC_CALL + && callable_ast->kind != ZEND_AST_METHOD_CALL) { + return NULL; + } + + zend_ast *args_ast = zend_ast_call_get_args(callable_ast); + if (!args_ast || args_ast->kind != ZEND_AST_CALLABLE_CONVERT) { + return NULL; + } + + zend_ast_list *arg_list = zend_ast_get_list(((zend_ast_fcc*)args_ast)->args); + + zend_ast *first_placeholder = NULL; + bool uses_variadic_placeholder = false; + + for (uint32_t i = 0; i < arg_list->children; i++) { + zend_ast *arg = arg_list->child[i]; + if (arg->kind == ZEND_AST_NAMED_ARG) { + if (uses_variadic_placeholder) { + /* PFAs with both a variadic placeholder and named args can not + * be optimized because the named arg may resolve to the + * position of the placeholder: f(..., name: $v). + * Arg placeholders ('?') are safe, as named args are not + * allowed to override them. */ + return NULL; + } + + arg = arg->child[1]; + } + + if (arg->kind == ZEND_AST_PLACEHOLDER_ARG) { + if (first_placeholder == NULL) { + first_placeholder = arg; + } else { + /* A PFA with multiple placeholders is unexpected is this + * context, and will usually error due to a missing argument, + * so we don't optimize those. */ + return NULL; + } + if (arg->attr == _IS_PLACEHOLDER_VARIADIC) { + uses_variadic_placeholder = true; + } + } + } + + ZEND_ASSERT(first_placeholder); + + zend_ast *new_arg_list = zend_ast_create_list(0, arg_list->kind); + for (uint32_t i = 0; i < arg_list->children; i++) { + zend_ast *arg = arg_list->child[i]; + if (arg == first_placeholder) { + new_arg_list = zend_ast_list_add(new_arg_list, pipe_arg); + } else if (arg->kind == ZEND_AST_NAMED_ARG + && arg->child[1] == first_placeholder) { + zend_ast *name = arg->child[0]; + new_arg_list = zend_ast_list_add(new_arg_list, + zend_ast_create(ZEND_AST_NAMED_ARG, name, pipe_arg)); + } else { + new_arg_list = zend_ast_list_add(new_arg_list, arg); + } + } + + return new_arg_list; +} + static void zend_compile_pipe(znode *result, zend_ast *ast) { zend_ast *operand_ast = ast->child[0]; @@ -6446,28 +6602,35 @@ static void zend_compile_pipe(znode *result, zend_ast *ast) } /* Turn the operand into a function parameter list. */ - zend_ast *arg_list_ast = zend_ast_create_list(1, ZEND_AST_ARG_LIST, zend_ast_create_znode(&wrapped_operand_result)); + zend_ast *arg = zend_ast_create_znode(&wrapped_operand_result); zend_ast *fcall_ast; znode callable_result; - /* Turn $foo |> bar(...) into bar($foo). */ - if (callable_ast->kind == ZEND_AST_CALL - && callable_ast->child[1]->kind == ZEND_AST_CALLABLE_CONVERT) { - fcall_ast = zend_ast_create(ZEND_AST_CALL, - callable_ast->child[0], arg_list_ast); - /* Turn $foo |> bar::baz(...) into bar::baz($foo). */ - } else if (callable_ast->kind == ZEND_AST_STATIC_CALL - && callable_ast->child[2]->kind == ZEND_AST_CALLABLE_CONVERT) { - fcall_ast = zend_ast_create(ZEND_AST_STATIC_CALL, - callable_ast->child[0], callable_ast->child[1], arg_list_ast); - /* Turn $foo |> $bar->baz(...) into $bar->baz($foo). */ - } else if (callable_ast->kind == ZEND_AST_METHOD_CALL - && callable_ast->child[2]->kind == ZEND_AST_CALLABLE_CONVERT) { - fcall_ast = zend_ast_create(ZEND_AST_METHOD_CALL, - callable_ast->child[0], callable_ast->child[1], arg_list_ast); + zend_ast *pfa_arg_list_ast = NULL; + + /* Turn $foo |> PFA into plain function call if possible */ + if ((pfa_arg_list_ast = zend_partial_apply(callable_ast, arg))) { + switch (callable_ast->kind) { + case ZEND_AST_CALL: + fcall_ast = zend_ast_create(ZEND_AST_CALL, + callable_ast->child[0], pfa_arg_list_ast); + break; + case ZEND_AST_STATIC_CALL: + fcall_ast = zend_ast_create(ZEND_AST_STATIC_CALL, + callable_ast->child[0], callable_ast->child[1], + pfa_arg_list_ast); + break; + case ZEND_AST_METHOD_CALL: + fcall_ast = zend_ast_create(ZEND_AST_METHOD_CALL, + callable_ast->child[0], callable_ast->child[1], + pfa_arg_list_ast); + break; + EMPTY_SWITCH_DEFAULT_CASE() + } /* Turn $foo |> $expr into ($expr)($foo) */ } else { + zend_ast *arg_list_ast = zend_ast_create_list(1, ZEND_AST_ARG_LIST, arg); zend_compile_expr(&callable_result, callable_ast); callable_ast = zend_ast_create_znode(&callable_result); fcall_ast = zend_ast_create(ZEND_AST_CALL, @@ -11394,6 +11557,13 @@ static void zend_compile_const_expr_fcc(zend_ast **ast_ptr) if ((*args_ast)->kind != ZEND_AST_CALLABLE_CONVERT) { zend_error_noreturn(E_COMPILE_ERROR, "Constant expression contains invalid operations"); } + + zend_ast_list *args = zend_ast_get_list(((zend_ast_fcc*)*args_ast)->args); + if (args->children != 1 || args->child[0]->attr != _IS_PLACEHOLDER_VARIADIC) { + // TODO: PFAs + zend_error_noreturn(E_COMPILE_ERROR, "Constant expression contains invalid operations"); + } + ZEND_MAP_PTR_NEW(((zend_ast_fcc *)*args_ast)->fptr); switch ((*ast_ptr)->kind) { diff --git a/Zend/zend_compile.h b/Zend/zend_compile.h index 62d0fbcded2ee..e96ebfedf6bcc 100644 --- a/Zend/zend_compile.h +++ b/Zend/zend_compile.h @@ -333,7 +333,7 @@ typedef struct _zend_oparray_context { /* Class cannot be serialized or unserialized | | | */ #define ZEND_ACC_NOT_SERIALIZABLE (1 << 29) /* X | | | */ /* | | | */ -/* Function Flags (unused: 30) | | | */ +/* Function Flags (unused: none) | | | */ /* ============== | | | */ /* | | | */ /* deprecation flag | | | */ @@ -403,6 +403,9 @@ typedef struct _zend_oparray_context { /* has #[\NoDiscard] attribute | | | */ #define ZEND_ACC_NODISCARD (1 << 29) /* | X | | */ /* | | | */ +/* trampoline is permanent | | | */ +#define ZEND_ACC_TRAMPOLINE_PERMANENT (1 << 30) /* | X | | */ +/* | | | */ /* op_array uses strict mode types | | | */ #define ZEND_ACC_STRICT_TYPES (1U << 31) /* | X | | */ diff --git a/Zend/zend_execute.c b/Zend/zend_execute.c index e1593cadce7d0..3dd7c3514fba7 100644 --- a/Zend/zend_execute.c +++ b/Zend/zend_execute.c @@ -34,6 +34,7 @@ #include "zend_exceptions.h" #include "zend_interfaces.h" #include "zend_closures.h" +#include "zend_partial.h" #include "zend_generators.h" #include "zend_vm.h" #include "zend_dtrace.h" @@ -4308,7 +4309,7 @@ static zend_always_inline void zend_init_cvs(uint32_t first, uint32_t last EXECU } } -static zend_always_inline void i_init_func_execute_data(zend_op_array *op_array, zval *return_value, bool may_be_trampoline EXECUTE_DATA_DC) /* {{{ */ +static zend_always_inline void i_init_func_execute_data_ex(zend_op_array *op_array, zval *return_value, bool may_be_trampoline, bool skip_extra_args, bool skip_cvs EXECUTE_DATA_DC) { uint32_t first_extra_arg, num_args; ZEND_ASSERT(EX(func) == (zend_function*)op_array); @@ -4325,7 +4326,7 @@ static zend_always_inline void i_init_func_execute_data(zend_op_array *op_array, first_extra_arg = op_array->num_args; num_args = EX_NUM_ARGS(); if (UNEXPECTED(num_args > first_extra_arg)) { - if (!may_be_trampoline || EXPECTED(!(op_array->fn_flags & ZEND_ACC_CALL_VIA_TRAMPOLINE))) { + if ((!may_be_trampoline || EXPECTED(!(op_array->fn_flags & ZEND_ACC_CALL_VIA_TRAMPOLINE))) && !skip_extra_args) { zend_copy_extra_args(EXECUTE_DATA_C); } } else if (EXPECTED((op_array->fn_flags & ZEND_ACC_HAS_TYPE_HINTS) == 0)) { @@ -4337,13 +4338,20 @@ static zend_always_inline void i_init_func_execute_data(zend_op_array *op_array, #endif } - /* Initialize CV variables (skip arguments) */ - zend_init_cvs(num_args, op_array->last_var EXECUTE_DATA_CC); + if (!skip_cvs) { + /* Initialize CV variables (skip arguments) */ + zend_init_cvs(num_args, op_array->last_var EXECUTE_DATA_CC); + } EX(run_time_cache) = RUN_TIME_CACHE(op_array); EG(current_execute_data) = execute_data; } + +static zend_always_inline void i_init_func_execute_data(zend_op_array *op_array, zval *return_value, bool may_be_trampoline EXECUTE_DATA_DC) /* {{{ */ +{ + i_init_func_execute_data_ex(op_array, return_value, may_be_trampoline, false, false EXECUTE_DATA_CC); +} /* }}} */ static zend_always_inline void init_func_run_time_cache_i(zend_op_array *op_array) /* {{{ */ @@ -4480,6 +4488,9 @@ zend_execute_data *zend_vm_stack_copy_call_frame(zend_execute_data *call, uint32 /* copy call frame into new stack segment */ new_call = zend_vm_stack_extend(used_stack * sizeof(zval)); +#ifdef __SANITIZE_ADDRESS__ + __asan_unpoison_memory_region(new_call, used_stack * sizeof(zval)); +#endif *new_call = *call; ZEND_ADD_CALL_FLAG(new_call, ZEND_CALL_ALLOCATED); @@ -4686,6 +4697,7 @@ static void cleanup_unfinished_calls(zend_execute_data *execute_data, uint32_t o case ZEND_DO_UCALL: case ZEND_DO_FCALL_BY_NAME: case ZEND_CALLABLE_CONVERT: + case ZEND_CALLABLE_CONVERT_PARTIAL: level++; break; case ZEND_INIT_FCALL: @@ -4723,6 +4735,7 @@ static void cleanup_unfinished_calls(zend_execute_data *execute_data, uint32_t o case ZEND_SEND_ARRAY: case ZEND_SEND_UNPACK: case ZEND_CHECK_UNDEF_ARGS: + case ZEND_CHECK_PARTIAL_ARGS: if (level == 0) { do_exit = 1; } @@ -4743,6 +4756,7 @@ static void cleanup_unfinished_calls(zend_execute_data *execute_data, uint32_t o case ZEND_DO_UCALL: case ZEND_DO_FCALL_BY_NAME: case ZEND_CALLABLE_CONVERT: + case ZEND_CALLABLE_CONVERT_PARTIAL: level++; break; case ZEND_INIT_FCALL: @@ -5372,27 +5386,29 @@ static zend_always_inline uint32_t zend_get_arg_offset_by_name( 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; + if (EXPECTED(ZSTR_LEN(arg_name) > 0)) { + // 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 { - ZEND_ASSERT(num_args == 0 || fbc->internal_function.arg_info); - 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 (zend_string_equals_cstr(arg_name, arg_info->name, len)) { - *cache_slot = fbc; - *(uintptr_t *)(cache_slot + 1) = i; - return i; + } else { + ZEND_ASSERT(num_args == 0 || fbc->internal_function.arg_info); + 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 (zend_string_equals_cstr(arg_name, arg_info->name, len)) { + *cache_slot = fbc; + *(uintptr_t *)(cache_slot + 1) = i; + return i; + } } } } @@ -5427,8 +5443,8 @@ zval * ZEND_FASTCALL zend_handle_named_arg( 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)); + zend_throw_error(NULL, "Named parameter $%s overwrites previous %s", + ZSTR_VAL(arg_name), Z_TYPE_P(arg) == _IS_PLACEHOLDER_ARG ? "placeholder" : "argument"); return NULL; } *arg_num_ptr = arg_offset + 1; @@ -5457,9 +5473,14 @@ zval * ZEND_FASTCALL zend_handle_named_arg( } } else { arg = ZEND_CALL_VAR_NUM(call, arg_offset); + + if (UNEXPECTED(Z_TYPE_P(arg) == _IS_PLACEHOLDER_VARIADIC)) { + ZVAL_UNDEF(arg); + } + if (UNEXPECTED(!Z_ISUNDEF_P(arg))) { - zend_throw_error(NULL, "Named parameter $%s overwrites previous argument", - ZSTR_VAL(arg_name)); + zend_throw_error(NULL, "Named parameter $%s overwrites previous %s", + ZSTR_VAL(arg_name), Z_TYPE_P(arg) == _IS_PLACEHOLDER_ARG ? "placeholder" : "argument"); return NULL; } } @@ -5531,6 +5552,10 @@ ZEND_API zend_result ZEND_FASTCALL zend_handle_undef_args(zend_execute_data *cal } else { ZVAL_COPY(arg, default_value); } + } else if (UNEXPECTED(opline->opcode == ZEND_CALL_PARTIAL)) { + // TODO: more tests + /* Undef args will be handled when calling the actual function */ + return SUCCESS; } else { ZEND_ASSERT(opline->opcode == ZEND_RECV); zend_execute_data *old = start_fake_frame(call, opline); @@ -5607,11 +5632,17 @@ static zend_always_inline zend_execute_data *_zend_vm_stack_push_call_frame_ex(u if (UNEXPECTED(used_stack > (size_t)(((char*)EG(vm_stack_end)) - (char*)call))) { EX(opline) = opline; /* this is the only difference */ call = (zend_execute_data*)zend_vm_stack_extend(used_stack); +#ifdef __SANITIZE_ADDRESS__ + __asan_unpoison_memory_region(call, used_stack); +#endif ZEND_ASSERT_VM_STACK_GLOBAL; zend_vm_init_call_frame(call, call_info | ZEND_CALL_ALLOCATED, func, num_args, object_or_called_scope); return call; } else { EG(vm_stack_top) = (zval*)((char*)call + used_stack); +#ifdef __SANITIZE_ADDRESS__ + __asan_unpoison_memory_region(call, used_stack); +#endif zend_vm_init_call_frame(call, call_info, func, num_args, object_or_called_scope); return call; } diff --git a/Zend/zend_execute.h b/Zend/zend_execute.h index cf15c9e3b2db5..02e3d37bc65d0 100644 --- a/Zend/zend_execute.h +++ b/Zend/zend_execute.h @@ -26,6 +26,9 @@ #include "zend_operators.h" #include "zend_variables.h" #include "zend_constants.h" +#ifdef __SANITIZE_ADDRESS__ +# include +#endif #include @@ -322,6 +325,9 @@ static zend_always_inline zend_vm_stack zend_vm_stack_new_page(size_t size, zend page->top = ZEND_VM_STACK_ELEMENTS(page); page->end = (zval*)((char*)page + size); page->prev = prev; +#ifdef __SANITIZE_ADDRESS__ + __asan_poison_memory_region(page->top, page->end - page->top); +#endif return page; } @@ -342,10 +348,16 @@ static zend_always_inline zend_execute_data *zend_vm_stack_push_call_frame_ex(ui if (UNEXPECTED(used_stack > (size_t)(((char*)EG(vm_stack_end)) - (char*)call))) { call = (zend_execute_data*)zend_vm_stack_extend(used_stack); +#ifdef __SANITIZE_ADDRESS__ + __asan_unpoison_memory_region(call, used_stack); +#endif ZEND_ASSERT_VM_STACK_GLOBAL; zend_vm_init_call_frame(call, call_info | ZEND_CALL_ALLOCATED, func, num_args, object_or_called_scope); return call; } else { +#ifdef __SANITIZE_ADDRESS__ + __asan_unpoison_memory_region(call, used_stack); +#endif EG(vm_stack_top) = (zval*)((char*)call + used_stack); zend_vm_init_call_frame(call, call_info, func, num_args, object_or_called_scope); return call; @@ -415,6 +427,10 @@ static zend_always_inline void zend_vm_stack_free_call_frame_ex(uint32_t call_in EG(vm_stack) = prev; efree(p); } else { +#ifdef __SANITIZE_ADDRESS__ + size_t size = (size_t)EG(vm_stack_top) - (size_t)call; + __asan_poison_memory_region(call, size); +#endif EG(vm_stack_top) = (zval*)call; } @@ -433,6 +449,9 @@ 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)) { +#ifdef __SANITIZE_ADDRESS__ + __asan_unpoison_memory_region(EG(vm_stack_top), additional_args * sizeof(zval)); +#endif EG(vm_stack_top) += additional_args; } else { *call = zend_vm_stack_copy_call_frame(*call, passed_args, additional_args); diff --git a/Zend/zend_fibers.c b/Zend/zend_fibers.c index 97b7cdcc911b7..19cd7203b2710 100644 --- a/Zend/zend_fibers.c +++ b/Zend/zend_fibers.c @@ -583,6 +583,9 @@ static ZEND_STACK_ALIGNED void zend_fiber_execute(zend_fiber_transfer *transfer) EG(vm_stack_page_size) = ZEND_FIBER_VM_STACK_SIZE; fiber->execute_data = (zend_execute_data *) stack->top; +#ifdef __SANITIZE_ADDRESS__ + __asan_unpoison_memory_region(stack->top, sizeof(zend_execute_data)); +#endif fiber->stack_bottom = fiber->execute_data; memset(fiber->execute_data, 0, sizeof(zend_execute_data)); diff --git a/Zend/zend_language_parser.y b/Zend/zend_language_parser.y index 805f378cb983c..69d4450053502 100644 --- a/Zend/zend_language_parser.y +++ b/Zend/zend_language_parser.y @@ -902,16 +902,15 @@ return_type: ; argument_list: - '(' ')' { $$ = zend_ast_create_list(0, ZEND_AST_ARG_LIST); } + '(' ')' { $$ = zend_ast_create_arg_list(0, ZEND_AST_ARG_LIST); } | '(' non_empty_argument_list possible_comma ')' { $$ = $2; } - | '(' T_ELLIPSIS ')' { $$ = zend_ast_create_fcc(); } ; non_empty_argument_list: argument - { $$ = zend_ast_create_list(1, ZEND_AST_ARG_LIST, $1); } + { $$ = zend_ast_create_arg_list(1, ZEND_AST_ARG_LIST, $1); } | non_empty_argument_list ',' argument - { $$ = zend_ast_list_add($1, $3); } + { $$ = zend_ast_arg_list_add($1, $3); } ; /* `clone_argument_list` is necessary to resolve a parser ambiguity (shift-reduce conflict) @@ -924,25 +923,31 @@ non_empty_argument_list: * syntax. */ clone_argument_list: - '(' ')' { $$ = zend_ast_create_list(0, ZEND_AST_ARG_LIST); } + '(' ')' { $$ = zend_ast_create_arg_list(0, ZEND_AST_ARG_LIST); } | '(' non_empty_clone_argument_list possible_comma ')' { $$ = $2; } - | '(' expr ',' ')' { $$ = zend_ast_create_list(1, ZEND_AST_ARG_LIST, $2); } - | '(' T_ELLIPSIS ')' { $$ = zend_ast_create_fcc(); } + | '(' expr ',' ')' { $$ = zend_ast_create_arg_list(1, ZEND_AST_ARG_LIST, $2); } ; non_empty_clone_argument_list: expr ',' argument - { $$ = zend_ast_create_list(2, ZEND_AST_ARG_LIST, $1, $3); } + { $$ = zend_ast_create_arg_list(2, ZEND_AST_ARG_LIST, $1, $3); } | argument_no_expr - { $$ = zend_ast_create_list(1, ZEND_AST_ARG_LIST, $1); } + { $$ = zend_ast_create_arg_list(1, ZEND_AST_ARG_LIST, $1); } | non_empty_clone_argument_list ',' argument - { $$ = zend_ast_list_add($1, $3); } + { $$ = zend_ast_arg_list_add($1, $3); } ; argument_no_expr: identifier ':' expr { $$ = zend_ast_create(ZEND_AST_NAMED_ARG, $1, $3); } - | T_ELLIPSIS expr { $$ = zend_ast_create(ZEND_AST_UNPACK, $2); } + | T_ELLIPSIS + { $$ = zend_ast_create_ex(ZEND_AST_PLACEHOLDER_ARG, _IS_PLACEHOLDER_VARIADIC); } + | '?' + { $$ = zend_ast_create_ex(ZEND_AST_PLACEHOLDER_ARG, _IS_PLACEHOLDER_ARG); } + | identifier ':' '?' + { $$ = zend_ast_create(ZEND_AST_NAMED_ARG, $1, zend_ast_create_ex(ZEND_AST_PLACEHOLDER_ARG, _IS_PLACEHOLDER_ARG)); } + | T_ELLIPSIS expr + { $$ = zend_ast_create(ZEND_AST_UNPACK, $2); } ; argument: diff --git a/Zend/zend_object_handlers.c b/Zend/zend_object_handlers.c index 971df5a7f232a..23632694372ed 100644 --- a/Zend/zend_object_handlers.c +++ b/Zend/zend_object_handlers.c @@ -39,6 +39,8 @@ #define ZEND_WRONG_PROPERTY_OFFSET 0 #define ZEND_HOOKED_PROPERTY_OFFSET 1 +static zend_arg_info zend_call_trampoline_arginfo[1] = {{0}}; + /* guard flags */ #define IN_GET ZEND_GUARD_PROPERTY_GET #define IN_SET ZEND_GUARD_PROPERTY_SET @@ -1667,6 +1669,13 @@ ZEND_API bool zend_check_protected(const zend_class_entry *ce, const zend_class_ } /* }}} */ +static zend_always_inline zend_arg_info* zend_get_call_trampoline_arginfo(void) { + if (UNEXPECTED(zend_call_trampoline_arginfo[0].name == NULL)) { + zend_call_trampoline_arginfo[0].name = ZSTR_KNOWN(ZEND_STR_ARGS); + } + return zend_call_trampoline_arginfo; +} + ZEND_API zend_function *zend_get_call_trampoline_func(const zend_class_entry *ce, zend_string *method_name, bool is_static) /* {{{ */ { size_t mname_len; @@ -1676,7 +1685,6 @@ ZEND_API zend_function *zend_get_call_trampoline_func(const zend_class_entry *ce * 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); @@ -1727,7 +1735,7 @@ ZEND_API zend_function *zend_get_call_trampoline_func(const zend_class_entry *ce func->prop_info = NULL; func->num_args = 0; func->required_num_args = 0; - func->arg_info = (zend_arg_info *) arg_info; + func->arg_info = zend_get_call_trampoline_arginfo(); return (zend_function*)func; } diff --git a/Zend/zend_object_handlers.h b/Zend/zend_object_handlers.h index fb87695a2ed25..ba3594d7fbd94 100644 --- a/Zend/zend_object_handlers.h +++ b/Zend/zend_object_handlers.h @@ -344,7 +344,7 @@ ZEND_API bool ZEND_FASTCALL zend_asymmetric_property_has_set_access(const zend_p if ((func) == &EG(trampoline)) { \ EG(trampoline).common.attributes = NULL; \ EG(trampoline).common.function_name = NULL; \ - } else { \ + } else if (!(func->common.fn_flags & ZEND_ACC_TRAMPOLINE_PERMANENT)) { \ efree(func); \ } \ } while (0) diff --git a/Zend/zend_partial.c b/Zend/zend_partial.c new file mode 100644 index 0000000000000..eb9b07766b665 --- /dev/null +++ b/Zend/zend_partial.c @@ -0,0 +1,1237 @@ +/* + +----------------------------------------------------------------------+ + | Zend Engine | + +----------------------------------------------------------------------+ + | Copyright (c) Zend Technologies Ltd. (http://www.zend.com) | + +----------------------------------------------------------------------+ + | This source file is subject to version 2.00 of the Zend license, | + | that is bundled with this package in the file LICENSE, and is | + | available through the world-wide-web at the following url: | + | http://www.zend.com/license/2_00.txt. | + | If you did not receive a copy of the Zend license and are unable to | + | obtain it through the world-wide-web, please send a note to | + | license@zend.com so we can mail you a copy immediately. | + +----------------------------------------------------------------------+ + | Authors: krakjoe | + +----------------------------------------------------------------------+ +*/ +#include "zend.h" +#include "zend_API.h" +#include "zend_compile.h" +#include "zend_hash.h" +#include "zend_interfaces.h" +#include "zend_closures.h" +#include "zend_partial.h" +#include "zend_portability.h" +#include "zend_types.h" +#include "zend_vm.h" +#include "zend_observer.h" +#include "zend_attributes.h" + +typedef struct _zend_partial { + /* Common zend_closure fields */ + zend_object std; + /* this will be returned by get_closure, and will invoke func. Reflects the + * partial signature. */ + zend_function trampoline; + zval This; + uint32_t closure_flags; + /* End of common fields */ + /* this is the unmodified function that will be invoked at call time */ + zend_function func; + uint32_t argc; + zval *argv; + zend_array *named; +} zend_partial; + +static zend_object_handlers zend_partial_handlers; + +static zend_arg_info zend_call_magic_arginfo[1]; + +// TODO: Does this need to be in EG()? +static union { + zend_op opcodes[2]; + char * buf[sizeof(zend_op)*2 + sizeof(zval)]; +} partial_call_trampoline_op; + +#define Z_IS_PLACEHOLDER_ARG_P(p) \ + (Z_TYPE_P(p) == _IS_PLACEHOLDER_ARG) + +#define Z_IS_PLACEHOLDER_VARIADIC_P(p) \ + (Z_TYPE_P(p) == _IS_PLACEHOLDER_VARIADIC) + +#define Z_IS_PLACEHOLDER_P(p) \ + (Z_IS_PLACEHOLDER_ARG_P(p) || Z_IS_PLACEHOLDER_VARIADIC_P(p)) + +#define Z_IS_NOT_PLACEHOLDER_P(p) \ + (!Z_IS_PLACEHOLDER_ARG_P(p) && !Z_IS_PLACEHOLDER_VARIADIC_P(p)) + +#define ZEND_PARTIAL_IS_CALL_TRAMPOLINE(func) \ + UNEXPECTED(((func)->type == ZEND_USER_FUNCTION) && ((func)->op_array.opcodes == &EG(call_trampoline_op))) + +#define ZEND_PARTIAL_FUNC_SIZE(func) \ + (((func)->type == ZEND_INTERNAL_FUNCTION) ? sizeof(zend_internal_function) : sizeof(zend_op_array)) + +#define ZEND_PARTIAL_FUNC_FLAG(func, flags) \ + (((func)->common.fn_flags & flags) != 0) + +#define ZEND_PARTIAL_FUNC_DEL(func, flag) \ + ((func)->common.fn_flags &= ~flag) + +#define ZEND_PARTIAL_FUNC_ADD(func, flag) \ + ((func)->common.fn_flags |= flag) + +#define ZEND_PARTIAL_CALL_FLAG(partial, flag) \ + (ZEND_CALL_INFO(partial) & flag) + +static zend_always_inline uint32_t zend_partial_signature_size(zend_partial *partial) { + uint32_t count = partial->argc; + + if (ZEND_PARTIAL_FUNC_FLAG(&partial->func, ZEND_ACC_HAS_RETURN_TYPE)) { + count++; + } + + if (ZEND_PARTIAL_CALL_FLAG(partial, ZEND_APPLY_VARIADIC) + || ZEND_PARTIAL_FUNC_FLAG(&partial->func, ZEND_ACC_VARIADIC)) { + count++; + } + + return count * sizeof(zend_arg_info); +} + +static void zend_partial_signature_copy_attributes(zend_array *dest, zend_array *src, uint32_t dest_offset, uint32_t src_offset) { + zend_attribute *attr; + ZEND_HASH_PACKED_FOREACH_PTR(src, attr) { + if (attr->offset == src_offset) { + if (src_offset != 0) { + zend_attribute *copy = emalloc(sizeof(zend_attribute)); + memcpy(copy, attr, sizeof(zend_attribute)); + copy->offset = dest_offset; + attr = copy; + } + zend_hash_next_index_insert_ptr(dest, attr); + } else if (attr->offset > src_offset) { + break; + } + } ZEND_HASH_FOREACH_END(); +} + +static zend_always_inline void zend_partial_signature_create(zend_partial *partial) { + zend_arg_info *signature = emalloc(zend_partial_signature_size(partial)), *info = signature; + + if (ZEND_PARTIAL_FUNC_FLAG(&partial->trampoline, ZEND_ACC_HAS_RETURN_TYPE)) { + memcpy(info, + partial->trampoline.common.arg_info - 1, sizeof(zend_arg_info)); + info++; + } + + uint32_t offset = 0, num = 0, required = 0, limit = partial->argc; + bool byref = false; + + memset(partial->trampoline.op_array.arg_flags, 0, + sizeof(partial->trampoline.op_array.arg_flags)); + + zend_array *attributes; + if (partial->func.common.attributes) { + attributes = emalloc(sizeof(zend_array)); + zend_hash_init(attributes, 0, NULL, NULL, false); + zend_partial_signature_copy_attributes(attributes, partial->func.common.attributes, 0, 0); + partial->trampoline.common.attributes = attributes; + } else { + attributes = NULL; + } + + while (offset < limit) { + zval *arg = &partial->argv[offset]; + + if (Z_IS_PLACEHOLDER_P(arg)) { + if (offset < partial->func.common.num_args) { + num++; + if (offset < partial->func.common.required_num_args) { + required = num; + } + memcpy(info, + &partial->func.common.arg_info[offset], + sizeof(zend_arg_info)); + if (EXPECTED(num < MAX_ARG_FLAG_NUM)) { + uint32_t mode = ZEND_ARG_SEND_MODE(info); + if (mode) { + ZEND_SET_ARG_FLAG(&partial->trampoline, num, mode); + byref = true; + } + } + if (UNEXPECTED(attributes)) { + zend_partial_signature_copy_attributes(attributes, partial->func.common.attributes, num, offset + 1); + } + info++; + } else { + ZEND_ASSERT(ZEND_PARTIAL_FUNC_FLAG(&partial->func, ZEND_ACC_VARIADIC)); + num++; + /* Placeholders that run into the variadic portion become + * required and make all params before them required */ + required = num; + info->name = zend_empty_string; + info->default_value = NULL; + if (ZEND_PARTIAL_IS_CALL_TRAMPOLINE(&partial->func)) { + info->type = (zend_type){0}; + } else { + info->type = (partial->func.common.arg_info + partial->func.common.num_args)->type; + ZEND_TYPE_FULL_MASK(info->type) &= ~_ZEND_IS_VARIADIC_BIT; + } + if (EXPECTED(num < MAX_ARG_FLAG_NUM)) { + uint32_t mode = ZEND_ARG_SEND_MODE(info); + if (mode) { + ZEND_SET_ARG_FLAG(&partial->trampoline, num, mode); + byref = true; + } + } + if (UNEXPECTED(attributes)) { + zend_partial_signature_copy_attributes(attributes, partial->func.common.attributes, num, partial->func.common.num_args + 1); + } + info++; + } + } else if (Z_ISUNDEF_P(arg)) { + ZEND_ASSERT(!ZEND_PARTIAL_CALL_FLAG(partial, ZEND_APPLY_VARIADIC)); + ZEND_ADD_CALL_FLAG(partial, ZEND_APPLY_UNDEF); + } else { + if (num > required) { + ZEND_ADD_CALL_FLAG(partial, ZEND_APPLY_UNDEF); + } + } + offset++; + } + + /* If we have a variadic placeholder and the underlying function is + * variadic, add a variadic param. Otherwise, allow extra args to be passed + * (implied by the ZEND_APPLY_VARIADIC flag) but don't add an explicit + * variadic param. */ + if (ZEND_PARTIAL_CALL_FLAG(partial, ZEND_APPLY_VARIADIC) + && ZEND_PARTIAL_FUNC_FLAG(&partial->func, ZEND_ACC_VARIADIC)) { + if (ZEND_PARTIAL_IS_CALL_TRAMPOLINE(&partial->func)) { + memcpy(info, zend_call_magic_arginfo, sizeof(zend_arg_info)); + } else { + memcpy(info, + partial->func.common.arg_info + partial->func.common.num_args, + sizeof(zend_arg_info)); + } + ZEND_PARTIAL_FUNC_ADD(&partial->trampoline, ZEND_ACC_VARIADIC); + uint32_t mode = ZEND_ARG_SEND_MODE(info); + if (mode) { + for (uint32_t i = num + 1; i < MAX_ARG_FLAG_NUM; i++) { + ZEND_SET_ARG_FLAG(&partial->trampoline, i, mode); + } + byref = true; + } + if (UNEXPECTED(attributes)) { + zend_partial_signature_copy_attributes(attributes, partial->func.common.attributes, num + 1, partial->func.common.num_args + 1); + } + info++; + } + + if (byref) { + ZEND_ADD_CALL_FLAG(partial, ZEND_APPLY_BYREF); + } + + partial->trampoline.common.num_args = num; + partial->trampoline.common.required_num_args = required; + partial->trampoline.common.arg_info = signature; + + if (ZEND_PARTIAL_FUNC_FLAG(&partial->trampoline, ZEND_ACC_HAS_RETURN_TYPE)) { + partial->trampoline.common.arg_info++; + } + + if (partial->func.type == ZEND_INTERNAL_FUNCTION + && !ZEND_PARTIAL_FUNC_FLAG(&partial->func, ZEND_ACC_USER_ARG_INFO)) { + /* Internal functions use an incompatible arg_info struct + * TODO: unify zend_arg_info / zend_internal_arg_info? */ + zend_arg_info *end = info; + info = signature; + if (ZEND_PARTIAL_FUNC_FLAG(&partial->trampoline, ZEND_ACC_HAS_RETURN_TYPE)) { + info++; + } + for (; info < end; info++) { + zend_internal_arg_info *ii = (zend_internal_arg_info*) info; + if ((zend_string*)ii->name == zend_empty_string) { + continue; + } + info->name = zend_string_init(ii->name, strlen(ii->name), false); + if (ii->default_value) { + info->default_value = zend_string_init(ii->default_value, strlen(ii->default_value), false); + } + } + } + + partial->trampoline.common.prototype = &partial->func; +} + +bool zend_is_partial_trampoline(zend_function *function) { + if (!(function->common.fn_flags & ZEND_ACC_CLOSURE)) { + return false; + } + + zend_partial *ptr = (zend_partial*)((char*)function - XtOffsetOf(zend_partial, trampoline)); + + if (!(ptr->closure_flags & ZEND_CLOSURE_PARTIAL)) { + return false; + } + + return true; +} + +static zend_always_inline zend_partial* zend_partial_fetch(zval *This) { + if (!This || Z_TYPE_P(This) != IS_OBJECT) { + return NULL; + } + + if (UNEXPECTED(!instanceof_function(Z_OBJCE_P(This), zend_ce_closure))) { + return NULL; + } + + zend_partial *ptr = (zend_partial*) Z_OBJ_P(This); + + if (!(ptr->closure_flags & ZEND_CLOSURE_PARTIAL)) { + return NULL; + } + + return ptr; +} + +static zend_always_inline void zend_partial_trampoline_create(zend_partial *partial, zend_function *trampoline) +{ + /* We use non-NULL value to avoid useless run_time_cache allocation. + * The low bit must be zero, to not be interpreted as a MAP_PTR offset. + */ + static const void *dummy = (void*)(intptr_t)2; + + const uint32_t keep_flags = + ZEND_ACC_RETURN_REFERENCE | ZEND_ACC_HAS_RETURN_TYPE | ZEND_ACC_STRICT_TYPES | ZEND_ACC_NODISCARD | ZEND_ACC_DEPRECATED; + + trampoline->common = partial->func.common; + trampoline->type = ZEND_USER_FUNCTION; + + /* ZEND_ACC_CALL_VIA_TRAMPOLINE | ZEND_ACC_TRAMPOLINE_PERMANENT: Don't copy extra args + * ZEND_ACC_HAS_TYPE_HINTS: Don't try to skip RECV ops */ + trampoline->op_array.fn_flags = + ZEND_ACC_PUBLIC | ZEND_ACC_HAS_TYPE_HINTS | ZEND_ACC_CALL_VIA_TRAMPOLINE | ZEND_ACC_TRAMPOLINE_PERMANENT | ZEND_ACC_CLOSURE | (partial->func.common.fn_flags & keep_flags); + + zend_partial_signature_create(partial); + + trampoline->op_array.opcodes = partial_call_trampoline_op.opcodes; + trampoline->op_array.filename = zend_get_executed_filename_ex(); + if (trampoline->op_array.filename == NULL) { + trampoline->op_array.filename = ZSTR_EMPTY_ALLOC(); + } else { + zend_string_addref(trampoline->op_array.filename); + } + trampoline->op_array.line_start = zend_get_executed_lineno(); + trampoline->op_array.line_end = trampoline->op_array.line_start; + + trampoline->op_array.last = sizeof(partial_call_trampoline_op.opcodes) / sizeof(zend_op); + + /* Ensure that trampoline and func have the same stack size */ + trampoline->op_array.T = partial->func.common.T; + if (partial->func.type == ZEND_USER_FUNCTION) { + trampoline->op_array.last_var = partial->argc + partial->func.op_array.last_var - MIN(partial->func.op_array.num_args, partial->argc); + } else { + trampoline->op_array.last_var = MAX(partial->func.op_array.num_args, partial->argc); + } + ZEND_ASSERT(zend_vm_calc_used_stack(trampoline->common.num_args, trampoline) == zend_vm_calc_used_stack(partial->argc, &partial->func)); + + ZEND_MAP_PTR_INIT(trampoline->op_array.run_time_cache, (void**)dummy); +} + +static zend_always_inline zend_object* zend_partial_new(zend_class_entry *type, uint32_t info) { + zend_partial *partial = ecalloc(1, sizeof(zend_partial)); + + zend_object_std_init(&partial->std, type); + + partial->std.handlers = &zend_partial_handlers; + partial->closure_flags = ZEND_CLOSURE_PARTIAL; + + ZEND_ADD_CALL_FLAG(partial, info); + + return (zend_object*) partial; +} + +static zend_always_inline void zend_partial_debug_add(zend_function *function, HashTable *ht, zend_arg_info *info, zval *value) { + if (function->type == ZEND_USER_FUNCTION || ZEND_PARTIAL_FUNC_FLAG(function, ZEND_ACC_USER_ARG_INFO)) { + zend_hash_add_new(ht, info->name, value); + } else { + zend_internal_arg_info *internal = (zend_internal_arg_info*) info; + + zend_hash_str_add_new(ht, internal->name, strlen(internal->name), value); + } +} + +static zend_always_inline void zend_partial_debug_fill(zend_partial *partial, HashTable *ht) { + zval *arg = partial->argv; + zval *aend = arg + partial->argc; + + zend_arg_info *info = partial->func.common.arg_info; + zend_arg_info *iend = info + partial->func.common.num_args; + + while (info < iend) { + zval param; + + ZVAL_NULL(¶m); + + if (arg < aend) { + if (Z_IS_NOT_PLACEHOLDER_P(arg)) { + ZVAL_COPY(¶m, arg); + } + arg++; + } + + zend_partial_debug_add(&partial->func, ht, info, ¶m); + + info++; + } + + if (ZEND_PARTIAL_FUNC_FLAG(&partial->func, ZEND_ACC_VARIADIC)) { + zval variadics; + + array_init(&variadics); + + zend_partial_debug_add(&partial->func, ht, info, &variadics); + + while (arg < aend) { + zval param; + + ZVAL_NULL(¶m); + + if (Z_IS_NOT_PLACEHOLDER_P(arg)) { + ZVAL_COPY(¶m, arg); + } + + zend_hash_next_index_insert(Z_ARRVAL(variadics), ¶m); + arg++; + } + + if (partial->named) { + zend_hash_merge(Z_ARRVAL(variadics), partial->named, zval_copy_ctor, true); + } + } +} + +static HashTable *zend_partial_debug(zend_object *object, int *is_temp) { + zend_partial *partial = (zend_partial*) object; + zval args; + HashTable *ht; + + ht = zend_closure_get_debug_info(object, is_temp); + + array_init(&args); + zend_hash_update(ht, ZSTR_KNOWN(ZEND_STR_ARGS), &args); + + zend_partial_debug_fill(partial, Z_ARRVAL(args)); + + return ht; +} + +static HashTable *zend_partial_get_gc(zend_object *obj, zval **table, int *n) +{ + zend_partial *partial = (zend_partial *)obj; + + if (!partial->argc && !partial->named) { + *table = Z_TYPE(partial->This) == IS_OBJECT ? &partial->This : NULL; + *n = Z_TYPE(partial->This) == IS_OBJECT ? 1 : 0; + } else { + zend_get_gc_buffer *buffer = zend_get_gc_buffer_create(); + + if (Z_TYPE(partial->This) == IS_OBJECT) { + zend_get_gc_buffer_add_zval(buffer, &partial->This); + } + + for (uint32_t arg = 0; arg < partial->argc; arg++) { + zend_get_gc_buffer_add_zval(buffer, &partial->argv[arg]); + } + + if (partial->named) { + zval named; + + ZVAL_ARR(&named, partial->named); + + zend_get_gc_buffer_add_zval(buffer, &named); + } + + zend_get_gc_buffer_use(buffer, table, n); + } + + return NULL; +} + +static zend_function *zend_partial_get_method(zend_object **object, zend_string *method, const zval *key) /* {{{ */ +{ + if (zend_string_equals_literal_ci(method, ZEND_INVOKE_FUNC_NAME)) { + return zend_get_closure_invoke_method(*object); + } + + return zend_std_get_method(object, method, key); +} +/* }}} */ + +static int zend_partial_get_closure(zend_object *obj, zend_class_entry **ce_ptr, zend_function **fptr_ptr, zend_object **obj_ptr, bool check_only) +{ + zend_partial *partial = (zend_partial*) obj; + + *fptr_ptr = &partial->trampoline; + *obj_ptr = (zend_object*) &partial->std; + *ce_ptr = Z_TYPE(partial->This) == IS_OBJECT ? Z_OBJCE(partial->This) : NULL; + + return SUCCESS; +} + +zend_function *zend_partial_get_trampoline(zend_object *obj) { + zend_partial *partial = (zend_partial*) obj; + + return &partial->trampoline; +} + +static zend_op *zend_partial_get_recv_op(const zend_op_array *op_array, uint32_t offset) +{ + zend_op *op = op_array->opcodes; + const zend_op *end = op + op_array->last; + + ++offset; + while (op < end) { + if ((op->opcode == ZEND_RECV || op->opcode == ZEND_RECV_INIT + || op->opcode == ZEND_RECV_VARIADIC) && op->op1.num == offset) + { + return op; + } + ++op; + } + + return NULL; +} + +static zval *zend_partial_get_default_from_recv(const zend_op_array *op_array, uint32_t offset) { + + zend_op *recv = zend_partial_get_recv_op(op_array, offset); + + if (!recv) { + return NULL; + } + + if (recv->opcode != ZEND_RECV_INIT) { + return NULL; + } + + return RT_CONSTANT(recv, recv->op2); +} + +static uint32_t zend_partial_resolve_param_offset(zend_partial *partial, uint32_t offset) { + + zval *argv = partial->argv; + + for (uint32_t i = 0; i < partial->argc; i++) { + if (Z_IS_PLACEHOLDER_P(&argv[i])) { + if (offset == 0) { + return i; + } + offset--; + } + } + + return (uint32_t)-1; +} + +zval *zend_partial_get_param_default_value(zend_object *obj, uint32_t offset) { + zend_partial *partial = (zend_partial*)obj; + + offset = zend_partial_resolve_param_offset(partial, offset); + if (offset == (uint32_t)-1) { + return NULL; + } + + return zend_partial_get_default_from_recv(&partial->func.op_array, offset); +} + +static void zend_partial_free(zend_object *object) { + zend_partial *partial = (zend_partial*) object; + + zval *arg = partial->argv, + *end = arg + partial->argc; + + while (arg < end) { + if (Z_OPT_REFCOUNTED_P(arg)) { + zval_ptr_dtor(arg); + } + arg++; + } + + efree(partial->argv); + + if (partial->named) { + zend_array_release(partial->named); + } + + zend_arg_info *info = partial->trampoline.common.arg_info; + + if (partial->func.type == ZEND_INTERNAL_FUNCTION + && !ZEND_PARTIAL_FUNC_FLAG(&partial->func, ZEND_ACC_USER_ARG_INFO)) { + zend_arg_info *cur = info; + zend_arg_info *end = info + partial->trampoline.common.num_args; + if (ZEND_PARTIAL_FUNC_FLAG(&partial->trampoline, ZEND_ACC_VARIADIC)) { + end++; + } + while (cur < end) { + zend_string_release(cur->name); + if (cur->default_value) { + zend_string_release(cur->default_value); + } + cur++; + } + } + + if (ZEND_PARTIAL_FUNC_FLAG(&partial->func, ZEND_ACC_HAS_RETURN_TYPE)) { + info--; + } + + efree(info); + + if (partial->trampoline.op_array.filename) { + zend_string_release(partial->trampoline.op_array.filename); + } + if (partial->func.type == ZEND_USER_FUNCTION) { + destroy_op_array(&partial->func.op_array); + } + + if (partial->trampoline.common.attributes) { + zend_attribute *attr; + ZEND_HASH_PACKED_FOREACH_PTR(partial->trampoline.common.attributes, attr) { + if (attr->offset > 0) { + efree(attr); + } + } ZEND_HASH_FOREACH_END(); + zend_hash_destroy(partial->trampoline.common.attributes); + efree(partial->trampoline.common.attributes); + } + + if (Z_TYPE(partial->This) == IS_OBJECT) { + zval_ptr_dtor(&partial->This); + } + + zend_object_std_dtor(object); +} + +static void zend_partial_init_call_trampoline_op(void) { + zend_op_array tmp_op_array = { + .opcodes = partial_call_trampoline_op.opcodes, + .literals = (zval*)(partial_call_trampoline_op.opcodes + 2), + }; + + tmp_op_array.opcodes[0].opcode = ZEND_CALL_PARTIAL; + ZEND_VM_SET_OPCODE_HANDLER(&tmp_op_array.opcodes[0]); + + // TODO: still needed? + tmp_op_array.opcodes[1].opcode = ZEND_RETURN; + tmp_op_array.opcodes[1].op1_type = IS_CONST; + tmp_op_array.opcodes[1].op1.constant = tmp_op_array.last_literal++; + ZVAL_NULL(&tmp_op_array.literals[tmp_op_array.opcodes[1].op1.constant]); + ZEND_PASS_TWO_UPDATE_CONSTANT(&tmp_op_array, &tmp_op_array.opcodes[1], tmp_op_array.opcodes[1].op1); + ZEND_VM_SET_OPCODE_HANDLER(&tmp_op_array.opcodes[1]); +} + +void zend_partial_startup(void) { + memcpy(&zend_partial_handlers, + &std_object_handlers, sizeof(zend_object_handlers)); + + zend_partial_handlers.free_obj = zend_partial_free; + zend_partial_handlers.get_debug_info = zend_partial_debug; + zend_partial_handlers.get_gc = zend_partial_get_gc; + zend_partial_handlers.get_closure = zend_partial_get_closure; + zend_partial_handlers.get_method = zend_partial_get_method; + + memset(&zend_call_magic_arginfo, 0, sizeof(zend_arg_info) * 1); + + zend_call_magic_arginfo[0].name = ZSTR_KNOWN(ZEND_STR_ARGS); + zend_call_magic_arginfo[0].type = (zend_type) ZEND_TYPE_INIT_NONE(_ZEND_IS_VARIADIC_BIT); + + zend_partial_init_call_trampoline_op(); +} + +static zend_always_inline zend_string* zend_partial_function_name(zend_function *function) { + if (function->type == ZEND_INTERNAL_FUNCTION) { + if (function->internal_function.handler == zend_pass_function.handler) { + return zend_string_init("__construct", sizeof("__construct")-1, 0); + } + } + return zend_string_copy(function->common.function_name); +} + +static zend_always_inline zend_string* zend_partial_scope_name(zend_execute_data *execute_data, zend_function *function) { + if (function->type == ZEND_INTERNAL_FUNCTION) { + if (function->internal_function.handler == zend_pass_function.handler) { + if (Z_OBJ(EX(This))) { + return Z_OBJCE(EX(This))->name; + } + } + } + + if (function->common.scope) { + return function->common.scope->name; + } + + return NULL; +} + +zend_string* zend_partial_symbol_name_ex(zend_partial *partial) { + zend_string *name = zend_partial_function_name(&partial->func), + *scope = partial->func.common.scope ? partial->func.common.scope->name : NULL, + *symbol; + + if (scope) { + symbol = zend_create_member_string(scope, name); + } else { + symbol = zend_string_copy(name); + } + + zend_string_release(name); + return symbol; +} + +zend_string* zend_partial_symbol_name(zend_execute_data *call, zend_function *function) { + zend_string *name = zend_partial_function_name(function), + *scope = zend_partial_scope_name(call, function), + *symbol; + + if (scope) { + symbol = zend_create_member_string(scope, name); + } else { + symbol = zend_string_copy(name); + } + + zend_string_release(name); + return symbol; +} + +ZEND_COLD void zend_partial_prototype_underflow(zend_function *function, zend_string *symbol, uint32_t args, uint32_t expected, bool variadic) { + char *limit = variadic ? "at least" : "exactly"; + + if (function->type == ZEND_USER_FUNCTION) { + zend_throw_error(NULL, + "not enough arguments for application of %s, " + "%d given and %s %d expected, declared in %s on line %d", + ZSTR_VAL(symbol), args, limit, expected, + ZSTR_VAL(function->op_array.filename), function->op_array.line_start); + } else { + zend_throw_error(NULL, + "not enough arguments for application of %s, %d given and %s %d expected", + ZSTR_VAL(symbol), args, limit, expected); + } +} + +ZEND_COLD void zend_partial_prototype_overflow(zend_function *function, zend_string *symbol, uint32_t args, uint32_t expected) { + if (function->type == ZEND_USER_FUNCTION) { + zend_throw_error(NULL, + "too many arguments for application of %s, " + "%d given and a maximum of %d expected, declared in %s on line %d", + ZSTR_VAL(symbol), args, expected, + ZSTR_VAL(function->op_array.filename), function->op_array.line_start); + } else { + zend_throw_error(NULL, + "too many arguments for application of %s, %d given and a maximum of %d expected", + ZSTR_VAL(symbol), args, expected); + } +} + +static zend_always_inline void zend_partial_args_underflow(zend_function *function, zend_string *symbol, uint32_t args, uint32_t expected, bool calling, bool prototype) { + const char *what = calling ? + "arguments" : "arguments or placeholders"; + const char *from = prototype ? + "application" : "implementation"; + const char *limit = function->common.num_args <= function->common.required_num_args ? + "exactly" : "at least"; + + if (function->type == ZEND_USER_FUNCTION) { + zend_throw_error(NULL, + "not enough %s for %s of %s, " + "%d given and %s %d expected, declared in %s on line %d", + what, from, ZSTR_VAL(symbol), args, limit, expected, + ZSTR_VAL(function->op_array.filename), function->op_array.line_start); + } else { + zend_throw_error(NULL, + "not enough %s for %s of %s, %d given and %s %d expected", + what, from, ZSTR_VAL(symbol), args, limit, expected); + } +} + +static zend_always_inline void zend_partial_args_overflow(zend_function *function, zend_string *symbol, uint32_t args, uint32_t expected, bool calling, bool prototype) { + const char *what = calling ? + "arguments" : "arguments or placeholders"; + const char *from = prototype ? + "application" : "implementation"; + + if (function->type == ZEND_USER_FUNCTION) { + zend_throw_error(NULL, + "too many %s for %s of %s, " + "%d given and a maximum of %d expected, declared in %s on line %d", + what, from, ZSTR_VAL(symbol), args, expected, + ZSTR_VAL(function->op_array.filename), function->op_array.line_start); + } else { + zend_throw_error(NULL, + "too many %s for %s of %s, %d given and a maximum of %d expected", + what, from, ZSTR_VAL(symbol), args, expected); + } +} + +void zend_partial_args_check(zend_execute_data *call) { + /* this is invoked by VM before the creation of zend_partial */ + + if (ZEND_PARTIAL_CALL_FLAG(call, ZEND_CALL_HAS_EXTRA_NAMED_PARAMS)) { + zend_array *named_args = call->extra_named_params; + zval *arg; + zend_string *key; + ZEND_HASH_MAP_FOREACH_STR_KEY_VAL(named_args, key, arg) { + if (UNEXPECTED(Z_TYPE_P(arg) == _IS_PLACEHOLDER_ARG)) { + zend_throw_error(NULL, + "Cannot use named placeholder for unknown or variadic parameter $%s", + ZSTR_VAL(key)); + return; + } + } ZEND_HASH_FOREACH_END(); + } + + zend_function *function = call->func; + + /* Z_EXTRA(ZEND_CALL_ARG(call, 1)) is set by ZEND_SEND_PLACEHOLDER */ + bool variadic = Z_EXTRA_P(ZEND_CALL_ARG(call, 1)) == _IS_PLACEHOLDER_VARIADIC; + + uint32_t num = ZEND_CALL_NUM_ARGS(call) + (variadic ? -1 : 0); + + if (num < function->common.required_num_args) { + /* this check is delayed in the case of variadic application */ + if (variadic) { + return; + } + + zend_string *symbol = zend_partial_symbol_name(call, function); + zend_partial_args_underflow( + function, symbol, + num, function->common.required_num_args, false, true); + zend_string_release(symbol); + } else if (num > function->common.num_args && + !ZEND_PARTIAL_FUNC_FLAG(function, ZEND_ACC_VARIADIC)) { + zend_string *symbol = zend_partial_symbol_name(call, function); + zend_partial_args_overflow( + function, symbol, + num, function->common.num_args, false, true); + zend_string_release(symbol); + } +} + +static zend_always_inline void zend_partial_send_var( + zend_partial *partial, zend_execute_data *call, + uint32_t arg_num, zval *var, zval *dest) +{ + if (UNEXPECTED(ZEND_PARTIAL_CALL_FLAG(partial, ZEND_APPLY_BYREF)) + && ((EXPECTED(arg_num <= MAX_ARG_FLAG_NUM) + && UNEXPECTED(QUICK_ARG_SHOULD_BE_SENT_BY_REF(call->func, arg_num))) + || UNEXPECTED(ARG_SHOULD_BE_SENT_BY_REF(call->func, arg_num)))) { + if (UNEXPECTED(!Z_OPT_ISREF_P(var))) { + zend_param_must_be_ref(call->func, arg_num); + if (UNEXPECTED(EG(exception))) { + /* TODO: clean stack and let the exception unwind. + * For now, trigger a fatal error. */ + zend_error_noreturn(E_CORE_ERROR, "TODO"); + } + } + + ZVAL_COPY_VALUE(dest, var); + } else if (UNEXPECTED(Z_OPT_ISREF_P(var))) { + ZVAL_COPY(dest, Z_REFVAL_P(var)); + zval_ptr_dtor(var); + } else { + ZVAL_COPY_VALUE(dest, var); + } +} + +/* Rearranges args on the stack, inplace */ +static uint32_t zend_partial_apply_inplace(zend_partial *partial, + zend_execute_data *call, zval *argv, uint32_t argc) { + + uint32_t num_args = 0; + uint32_t type_flags = 0; + + uint32_t first_extra_arg_offset = partial->func.common.num_args; + zval *first_extra_arg = argv + first_extra_arg_offset; + size_t extra_arg_delta; + if (ZEND_PARTIAL_FUNC_FLAG(&partial->func, ZEND_ACC_CALL_VIA_TRAMPOLINE) + || partial->func.type == ZEND_INTERNAL_FUNCTION) { + /* Trampolines and internal functions expect contiguous args */ + extra_arg_delta = 0; + } else { + extra_arg_delta = ((partial->func.op_array.last_var + partial->func.op_array.T) - first_extra_arg_offset); + } + + /* Move extra trampoline args. These args are always extra args in + * partial->func as well, since the trampoline allows extra args only + * when partial->argc is at least as high as the func num args. */ + if (UNEXPECTED(argc > partial->trampoline.common.num_args)) { + ZEND_ASSERT(partial->argc >= partial->func.common.num_args); + + uint32_t num_extra_args = argc - partial->trampoline.common.num_args; + zval *dest = argv + partial->argc + num_extra_args - 1 + extra_arg_delta; + zval *src = argv + argc - 1; + + num_args = partial->argc + num_extra_args; + + do { + ZEND_ASSERT(src >= argv + partial->trampoline.common.num_args); + if (!ZEND_PARTIAL_FUNC_FLAG(&partial->func, ZEND_ACC_CALL_VIA_TRAMPOLINE)) { + ZEND_ASSERT(dest >= argv + partial->func.op_array.last_var + partial->func.op_array.T); + } + + zend_partial_send_var(partial, call, src - argv + 1, src, dest); + type_flags |= Z_TYPE_INFO_P(dest); + + if (dest != src) { + ZVAL_UNDEF(src); + } + dest--; + src--; + } while (--num_extra_args); + } + + zval *dest = argv + partial->argc - 1; + + zval *partial_first = partial->argv; + zval *partial_cur = partial->argv + partial->argc - 1; + + zval *call_cur = argv + partial->trampoline.common.num_args - 1; + + while (partial_cur >= partial_first) { + zval *actual_dest = dest >= first_extra_arg + ? dest + extra_arg_delta + : dest; + if (Z_IS_PLACEHOLDER_P(partial_cur)) { + if (call_cur - argv >= argc) { + /* Optional arg not specified */ + // TODO: needs initialization? + } else { + /* Move call arg to the right */ + if (actual_dest != call_cur) { + ZEND_ASSERT(actual_dest > call_cur); + zend_partial_send_var(partial, call, call_cur - argv + 1, call_cur, actual_dest); + if (dest >= first_extra_arg) { + if (call_cur != actual_dest) { + ZVAL_UNDEF(call_cur); + } + } + } + if (dest >= first_extra_arg) { + type_flags |= Z_TYPE_INFO_P(actual_dest); + } + if (num_args == 0) { + num_args = partial_cur - partial_first + 1; + } + } + call_cur--; + } else if (Z_ISUNDEF_P(partial_cur)) { + ZVAL_UNDEF(actual_dest); + } else { + /* Copy partial arg */ + ZVAL_COPY(actual_dest, partial_cur); + if (dest >= first_extra_arg) { + type_flags |= Z_TYPE_INFO_P(partial_cur); + } + if (num_args == 0) { + num_args = partial_cur - partial_first + 1; + } + } + dest--; + partial_cur--; + } + + if (Z_TYPE_INFO_REFCOUNTED(type_flags) + && !ZEND_PARTIAL_FUNC_FLAG(&partial->func, ZEND_ACC_CALL_VIA_TRAMPOLINE)) { + ZEND_ADD_CALL_FLAG(call, ZEND_CALL_FREE_EXTRA_ARGS); + } + + ZEND_ASSERT(partial_cur == partial_first - 1); + ZEND_ASSERT(dest == argv - 1); + ZEND_ASSERT(call_cur == argv - 1); + + return num_args; +} + +static zend_always_inline void zend_partial_apply( + zval *pStart, zval *pEnd, + zval *cStart, zval *cEnd, + zval *fParam) { + + while (pStart < pEnd) { + if (Z_IS_PLACEHOLDER_P(pStart)) { + if (cStart < cEnd) { + ZVAL_COPY_VALUE(fParam, cStart); + cStart++; + } else { + Z_TYPE_INFO_P(fParam) = _IS_PLACEHOLDER_ARG; + } + } else if (Z_ISUNDEF_P(pStart)) { + ZVAL_UNDEF(fParam); + } else { + ZVAL_COPY(fParam, pStart); + } + fParam++; + pStart++; + } + + while (cStart < cEnd) { + ZVAL_COPY_VALUE(fParam, cStart); + fParam++; + cStart++; + } +} + +/* Initializes a call to the real function. The call frame of the trampoline is + * reused. */ +zend_result zend_partial_init_call(zend_execute_data *call) +{ + ZEND_ASSERT(call->opline->opcode == ZEND_CALL_PARTIAL); + + /* call->func points to partial->trampoline */ + zend_partial *partial = (zend_partial*)((char*)call->func - XtOffsetOf(zend_partial, trampoline)); + + if (UNEXPECTED(ZEND_CALL_NUM_ARGS(call) < partial->trampoline.common.required_num_args)) { + zend_string *symbol = zend_partial_symbol_name_ex(partial); + zend_partial_prototype_underflow( + &partial->trampoline, symbol, ZEND_CALL_NUM_ARGS(call), + partial->trampoline.common.required_num_args, + ZEND_PARTIAL_CALL_FLAG(partial, ZEND_APPLY_VARIADIC)); + zend_string_release(symbol); + if (ZEND_PARTIAL_IS_CALL_TRAMPOLINE(&partial->trampoline)) { + EG(trampoline).common.function_name = NULL; + } + return FAILURE; + } else if (UNEXPECTED(ZEND_CALL_NUM_ARGS(call) > partial->trampoline.common.num_args && + !ZEND_PARTIAL_CALL_FLAG(partial, ZEND_APPLY_VARIADIC))) { + zend_string *symbol = zend_partial_symbol_name_ex(partial); + zend_partial_prototype_overflow( + &partial->trampoline, symbol, ZEND_CALL_NUM_ARGS(call), + partial->trampoline.common.num_args); + zend_string_release(symbol); + if (ZEND_PARTIAL_IS_CALL_TRAMPOLINE(&partial->trampoline)) { + EG(trampoline).common.function_name = NULL; + } + return FAILURE; + } + + ZEND_ASSERT(zend_vm_calc_used_stack(ZEND_CALL_NUM_ARGS(call), &partial->func) <= (size_t)(((char*)EG(vm_stack_end)) - (char*)call)); + + uint32_t num_args = zend_partial_apply_inplace(partial, call, + ZEND_CALL_ARG(call, 1), ZEND_CALL_NUM_ARGS(call)); + + uint32_t orig_call_info = ZEND_CALL_INFO(call); + uint32_t call_info = orig_call_info & (ZEND_CALL_NESTED | ZEND_CALL_TOP | ZEND_CALL_ALLOCATED | ZEND_CALL_FREE_EXTRA_ARGS | ZEND_CALL_HAS_EXTRA_NAMED_PARAMS); + void *object_or_called_scope; + if (!ZEND_PARTIAL_FUNC_FLAG(&partial->func, ZEND_ACC_CALL_VIA_TRAMPOLINE)) { + call_info |= ZEND_CALL_FAKE_CLOSURE; + } + if (Z_TYPE(partial->This) == IS_OBJECT) { + object_or_called_scope = Z_OBJ(partial->This); + ZEND_ADD_CALL_FLAG_EX(call_info, ZEND_CALL_HAS_THIS); + } else if (Z_TYPE(partial->This) == IS_UNDEF && Z_CE(partial->This)) { + object_or_called_scope = Z_CE(partial->This); + } else { + object_or_called_scope = NULL; + } + + zend_vm_init_call_frame(call, call_info, &partial->func, + num_args, object_or_called_scope); + + zend_array *named_args; + if (ZEND_CALL_INFO(call) & ZEND_CALL_HAS_EXTRA_NAMED_PARAMS) { + named_args = call->extra_named_params; + } else { + named_args = NULL; + } + + if (partial->named) { + if (!named_args) { + call->extra_named_params = partial->named; + GC_ADDREF(partial->named); + ZEND_CALL_INFO(call) |= ZEND_CALL_HAS_EXTRA_NAMED_PARAMS; + } else { + /* Preserve order: partial args first, then call args */ + zend_array *nested = zend_array_dup(partial->named); + zend_hash_merge(nested, named_args, zval_copy_ctor, true); + zend_array_release(named_args); + call->extra_named_params = nested; + } + } + + if (ZEND_PARTIAL_CALL_FLAG(partial, ZEND_APPLY_UNDEF)) { + /* zend_handle_undef_args() creates a fake frame that links to itself + * due to EG(current_execute_data) == call */ + EG(current_execute_data) = call->prev_execute_data; + zend_handle_undef_args(call); + EG(current_execute_data) = call; + } + + if (ZEND_PARTIAL_FUNC_FLAG(&partial->func, ZEND_ACC_CALL_VIA_TRAMPOLINE)) { + zend_string_addref(partial->func.common.function_name); + OBJ_RELEASE(&partial->std); + } + + return SUCCESS; +} + +void zend_partial_create(zval *result, uint32_t info, zval *this_ptr, zend_function *function, uint32_t argc, zval *argv, zend_array *extra_named_params) { + ZVAL_OBJ(result, zend_partial_new(zend_ce_closure, info)); + + // TODO: static vars / lexical vars + // TODO: run_time_cache? + + zend_partial *applied, *partial = (zend_partial*) Z_OBJ_P(result); + + ZEND_ASSERT(ZEND_PARTIAL_OBJECT(&partial->func) == &partial->std); + + if ((applied = zend_partial_fetch(this_ptr))) { + ZEND_ADD_CALL_FLAG(partial, ZEND_CALL_INFO(applied) & ~ZEND_APPLY_VARIADIC); + + function = &applied->func; + + /* Z_EXTRA(ZEND_CALL_ARG(call, 1)) is set by ZEND_SEND_PLACEHOLDER */ + if (Z_EXTRA(argv[0]) == _IS_PLACEHOLDER_VARIADIC + || (ZEND_CALL_INFO(applied) & ZEND_APPLY_VARIADIC)) { + ZEND_ADD_CALL_FLAG(partial, ZEND_APPLY_VARIADIC); + } + if (Z_EXTRA(argv[0]) == _IS_PLACEHOLDER_VARIADIC) { + if (Z_IS_PLACEHOLDER_VARIADIC_P(&argv[argc-1]) + && argc > applied->trampoline.common.num_args) { + argc--; + } + } + partial->argc = applied->argc - applied->trampoline.common.num_args + MAX(applied->trampoline.common.num_args, argc); + partial->argv = safe_emalloc(partial->argc, sizeof(zval), 0); + zend_partial_apply( + applied->argv, applied->argv + applied->argc, + argv, argv + argc, + partial->argv); + + if (ZEND_PARTIAL_CALL_FLAG(partial, ZEND_APPLY_VARIADIC)) { + for (uint32_t i = 0; i < partial->argc; i++) { + if (Z_ISUNDEF(partial->argv[i])) { + Z_TYPE_INFO(partial->argv[i]) = _IS_PLACEHOLDER_ARG; + } + } + } + + if (extra_named_params) { + if (applied->named) { + partial->named = zend_array_dup(applied->named); + + zend_hash_merge(partial->named, extra_named_params, zval_copy_ctor, true); + } else { + partial->named = extra_named_params; + GC_ADDREF(extra_named_params); + } + } + } else { + /* Z_EXTRA(ZEND_CALL_ARG(call, 1)) is set in ZEND_SEND_PLACEHOLDER */ + if (Z_EXTRA(argv[0]) == _IS_PLACEHOLDER_VARIADIC) { + ZEND_ADD_CALL_FLAG(partial, ZEND_APPLY_VARIADIC); + if (Z_IS_PLACEHOLDER_VARIADIC_P(&argv[argc-1]) + && argc > function->common.num_args) { + argc--; + } + partial->argc = MAX(argc, function->common.num_args); + } else { + partial->argc = argc; + } + + partial->argv = safe_emalloc(partial->argc, sizeof(zval), 0); + memcpy(partial->argv, argv, argc * sizeof(zval)); + + if (ZEND_PARTIAL_CALL_FLAG(partial, ZEND_APPLY_VARIADIC)) { + for (uint32_t i = 0; i < argc; i++) { + if (Z_ISUNDEF(partial->argv[i])) { + Z_TYPE_INFO(partial->argv[i]) = _IS_PLACEHOLDER_ARG; + } + } + for (uint32_t i = argc; i < partial->argc; i++) { + Z_TYPE_INFO(partial->argv[i]) = _IS_PLACEHOLDER_ARG; + } + } + + if (extra_named_params) { + partial->named = extra_named_params; + GC_ADDREF(extra_named_params); + } + } + + memcpy(&partial->func, function, ZEND_PARTIAL_FUNC_SIZE(function)); + + ZEND_PARTIAL_FUNC_DEL(&partial->func, ZEND_ACC_CLOSURE); + ZEND_PARTIAL_FUNC_ADD(&partial->func, ZEND_ACC_IMMUTABLE); + + if (ZEND_PARTIAL_FUNC_FLAG(&partial->func, ZEND_ACC_CALL_VIA_TRAMPOLINE)) { + ZEND_PARTIAL_FUNC_ADD(&partial->func, ZEND_ACC_TRAMPOLINE_PERMANENT); + } + + ZEND_PARTIAL_FUNC_ADD(&partial->func, ZEND_ACC_FAKE_CLOSURE); + + if (partial->func.type == ZEND_USER_FUNCTION) { + if (!ZEND_PARTIAL_FUNC_FLAG(&partial->func, ZEND_ACC_CALL_VIA_TRAMPOLINE)) { + /* When the function is a trampoline, the function name is already + * addref'ed as it's supposed to be consumed by the call. */ + zend_string_addref(partial->func.common.function_name); + } + + partial->func.op_array.refcount = NULL; + } + + zend_partial_trampoline_create(partial, &partial->trampoline); + + /* partial info may contain ZEND_APPLY_VARIADIC */ + uint32_t backup_info = ZEND_CALL_INFO(partial); + + if (Z_TYPE_P(this_ptr) == IS_UNDEF && Z_CE_P(this_ptr)) { + ZVAL_COPY_VALUE(&partial->This, this_ptr); + } else if (Z_TYPE_P(this_ptr) == IS_OBJECT) { + zval *This; + if (instanceof_function(Z_OBJCE_P(this_ptr), zend_ce_closure)) { + if (zend_string_equals_ci( + partial->func.common.function_name, + ZSTR_KNOWN(ZEND_STR_MAGIC_INVOKE))) { + This = this_ptr; + } else { + zend_partial *p = (zend_partial*)Z_OBJ_P(this_ptr); + This = &p->This; + } + } else { + This = this_ptr; + } + ZVAL_COPY(&partial->This, This); + } + + ZEND_ADD_CALL_FLAG(partial, backup_info); +} + +void zend_partial_bind(zval *result, zval *partial, zval *this_ptr, zend_class_entry *scope) { + zval This; + zend_partial *object = (zend_partial*) Z_OBJ_P(partial); + + ZVAL_UNDEF(&This); + + if (!this_ptr || Z_TYPE_P(this_ptr) != IS_OBJECT) { + ZEND_ASSERT(scope && "scope must be set"); + + Z_CE(This) = scope; + } else { + ZVAL_COPY_VALUE(&This, this_ptr); + } + + zend_partial_create(result, ZEND_CALL_INFO(object), &This, + &object->func, object->argc, object->argv, object->named); + + zval *argv = object->argv, + *end = argv + object->argc; + + while (argv < end) { + Z_TRY_ADDREF_P(argv); + argv++; + } +} diff --git a/Zend/zend_partial.h b/Zend/zend_partial.h new file mode 100644 index 0000000000000..5feaab2fef46d --- /dev/null +++ b/Zend/zend_partial.h @@ -0,0 +1,67 @@ +/* + +----------------------------------------------------------------------+ + | Zend Engine | + +----------------------------------------------------------------------+ + | Copyright (c) Zend Technologies Ltd. (http://www.zend.com) | + +----------------------------------------------------------------------+ + | This source file is subject to version 2.00 of the Zend license, | + | that is bundled with this package in the file LICENSE, and is | + | available through the world-wide-web at the following url: | + | http://www.zend.com/license/2_00.txt. | + | If you did not receive a copy of the Zend license and are unable to | + | obtain it through the world-wide-web, please send a note to | + | license@zend.com so we can mail you a copy immediately. | + +----------------------------------------------------------------------+ + | Authors: krakjoe | + +----------------------------------------------------------------------+ +*/ +#ifndef ZEND_PARTIAL_H +#define ZEND_PARTIAL_H + +#include "zend_types.h" +#include "zend_API.h" + +BEGIN_EXTERN_C() + +#define ZEND_PARTIAL_OBJECT(func) \ + ((zend_object*)((char*)(func) - zend_partial_func_offset())) + +#define ZEND_PARTIAL_OBJECT_FROM_TRAMPOLINE(trampoline) \ + ((zend_object*)((char*)(trampoline) - sizeof(zend_object))) + +typedef struct _zend_partial zend_partial; + +void zend_partial_startup(void); + +#define ZEND_APPLY_VARIADIC (1<<16) +#define ZEND_APPLY_UNDEF (1<<17) /* Some arguments maybe undef, default value needs to be fetched */ +#define ZEND_APPLY_BYREF (1<<18) /* Some arguments should be sent by ref */ + +void zend_partial_create(zval *result, uint32_t info, zval *this_ptr, zend_function *function, uint32_t argc, zval *argv, zend_array *extra_named_params); + +void zend_partial_bind(zval *result, zval *partial, zval *this_ptr, zend_class_entry *scope); + +void zend_partial_args_check(zend_execute_data *call); + +zend_function *zend_partial_get_trampoline(zend_object *object); + +bool zend_is_partial_trampoline(zend_function *function); + +zval *zend_partial_get_param_default_value(zend_object *obj, uint32_t offset); + +zend_result zend_partial_init_call(zend_execute_data *call); + +/* Offset of zend_partial.func */ +static zend_always_inline size_t zend_partial_func_offset(void) { + return XtOffsetOf(struct { + zend_object a; + zend_function b; + zval c; + uint32_t d; + zend_function func; + }, func); +} + +END_EXTERN_C() + +#endif diff --git a/Zend/zend_types.h b/Zend/zend_types.h index 4a6d00b9d73ea..a9556603dd321 100644 --- a/Zend/zend_types.h +++ b/Zend/zend_types.h @@ -649,6 +649,10 @@ struct _zend_ast_ref { #define _IS_BOOL 18 #define _IS_NUMBER 19 +/* used for place holders */ +#define _IS_PLACEHOLDER_ARG 20 +#define _IS_PLACEHOLDER_VARIADIC 21 + /* guard flags */ #define ZEND_GUARD_PROPERTY_GET (1<<0) #define ZEND_GUARD_PROPERTY_SET (1<<1) diff --git a/Zend/zend_vm_def.h b/Zend/zend_vm_def.h index 9f7042386a152..5ff2b7850faa5 100644 --- a/Zend/zend_vm_def.h +++ b/Zend/zend_vm_def.h @@ -23,6 +23,9 @@ * php zend_vm_gen.php */ +#include "zend_API.h" +#include "zend_compile.h" +#include "zend_portability.h" ZEND_VM_HELPER(zend_add_helper, ANY, ANY, zval *op_1, zval *op_2) { USE_OPLINE @@ -2975,9 +2978,17 @@ ZEND_VM_HOT_HELPER(zend_leave_helper, ANY, ANY) OBJ_RELEASE(Z_OBJ(execute_data->This)); } else if (UNEXPECTED(call_info & ZEND_CALL_CLOSURE)) { OBJ_RELEASE(ZEND_CLOSURE_OBJECT(EX(func))); + } else if (UNEXPECTED(call_info & ZEND_CALL_FAKE_CLOSURE)) { + OBJ_RELEASE(ZEND_PARTIAL_OBJECT(EX(func))); } + + zend_execute_data *prev_execute_data = EX(prev_execute_data); +#ifdef __SANITIZE_ADDRESS__ + size_t size = (size_t)EG(vm_stack_top) - (size_t)execute_data; + __asan_poison_memory_region(execute_data, size); +#endif EG(vm_stack_top) = (zval*)execute_data; - execute_data = EX(prev_execute_data); + execute_data = prev_execute_data; if (UNEXPECTED(EG(exception) != NULL)) { zend_rethrow_exception(execute_data); @@ -3009,6 +3020,8 @@ ZEND_VM_HOT_HELPER(zend_leave_helper, ANY, ANY) OBJ_RELEASE(Z_OBJ(execute_data->This)); } else if (UNEXPECTED(call_info & ZEND_CALL_CLOSURE)) { OBJ_RELEASE(ZEND_CLOSURE_OBJECT(EX(func))); + } else if (UNEXPECTED(call_info & ZEND_CALL_FAKE_CLOSURE)) { + OBJ_RELEASE(ZEND_PARTIAL_OBJECT(EX(func))); } old_execute_data = execute_data; @@ -3066,6 +3079,8 @@ ZEND_VM_HOT_HELPER(zend_leave_helper, ANY, ANY) } if (UNEXPECTED(call_info & ZEND_CALL_CLOSURE)) { OBJ_RELEASE(ZEND_CLOSURE_OBJECT(EX(func))); + } else if (UNEXPECTED(call_info & ZEND_CALL_FAKE_CLOSURE)) { + OBJ_RELEASE(ZEND_PARTIAL_OBJECT(EX(func))); } ZEND_VM_RETURN(); } else /* if (call_kind == ZEND_CALL_TOP_CODE) */ { @@ -4145,6 +4160,10 @@ ZEND_VM_HOT_HANDLER(129, ZEND_DO_ICALL, ANY, ANY, SPEC(RETVAL,OBSERVER)) } zend_vm_stack_free_call_frame_ex(call_info, call); } else { +#ifdef __SANITIZE_ADDRESS__ + size_t size = (size_t)EG(vm_stack_top) - (size_t)call; + __asan_poison_memory_region(call, size); +#endif EG(vm_stack_top) = (zval*)call; } @@ -4281,6 +4300,10 @@ ZEND_VM_C_LABEL(fcall_by_name_end): } zend_vm_stack_free_call_frame_ex(call_info, call); } else { +#ifdef __SANITIZE_ADDRESS__ + size_t size = (size_t)EG(vm_stack_top) - (size_t)call; + __asan_poison_memory_region(call, size); +#endif EG(vm_stack_top) = (zval*)call; } @@ -4701,8 +4724,13 @@ ZEND_VM_HANDLER(139, ZEND_GENERATOR_CREATE, ANY, ANY) call_info = EX_CALL_INFO(); EG(current_execute_data) = EX(prev_execute_data); if (EXPECTED(!(call_info & (ZEND_CALL_TOP|ZEND_CALL_ALLOCATED)))) { + zend_execute_data *prev_execute_data = EX(prev_execute_data); +#ifdef __SANITIZE_ADDRESS__ + size_t size = (size_t)EG(vm_stack_top) - (size_t)execute_data; + __asan_poison_memory_region(execute_data, size); +#endif EG(vm_stack_top) = (zval*)execute_data; - execute_data = EX(prev_execute_data); + execute_data = prev_execute_data; LOAD_NEXT_OPLINE(); ZEND_VM_LEAVE(); } else if (EXPECTED(!(call_info & ZEND_CALL_TOP))) { @@ -5975,7 +6003,7 @@ ZEND_VM_HANDLER(68, ZEND_NEW, UNUSED|CLASS_FETCH|CONST|VAR, UNUSED|CACHE_SLOT, N /* Perform a dummy function call */ call = zend_vm_stack_push_call_frame( ZEND_CALL_FUNCTION, (zend_function *) &zend_pass_function, - opline->extended_value, NULL); + opline->extended_value, Z_OBJ_P(result)); } else { if (EXPECTED(constructor->type == ZEND_USER_FUNCTION) && UNEXPECTED(!RUN_TIME_CACHE(&constructor->op_array))) { init_func_run_time_cache(&constructor->op_array); @@ -6158,7 +6186,7 @@ ZEND_VM_HANDLER(181, ZEND_FETCH_CLASS_CONSTANT, VAR|CONST|UNUSED|CLASS_FETCH, CO } bool is_constant_deprecated = ZEND_CLASS_CONST_FLAGS(c) & ZEND_ACC_DEPRECATED; - if (UNEXPECTED(is_constant_deprecated) && !CONST_IS_RECURSIVE(c)) { + if (UNEXPECTED(is_constant_deprecated) && !CONST_IS_RECURSIVE(c)) { if (c->ce->type == ZEND_USER_CLASS) { /* Recursion protection only applied to user constants, GH-18463 */ CONST_PROTECT_RECURSION(c); @@ -9203,6 +9231,156 @@ ZEND_VM_HANDLER(200, ZEND_FETCH_GLOBALS, UNUSED, UNUSED) ZEND_VM_NEXT_OPCODE(); } +ZEND_VM_HANDLER(211, ZEND_SEND_PLACEHOLDER, UNUSED, CONST|UNUSED) +{ + USE_OPLINE + zval *arg; + zend_execute_data *call = EX(call); + + 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); + } + + Z_TYPE_INFO_P(arg) = opline->op1.num; + + if (Z_TYPE_INFO_P(arg) == _IS_PLACEHOLDER_VARIADIC) { + Z_EXTRA_P(ZEND_CALL_ARG(call, 1)) = _IS_PLACEHOLDER_VARIADIC; + } else if (opline->op2.num == 1) { + Z_EXTRA_P(arg) = 0; + } + + ZEND_VM_NEXT_OPCODE(); +} + +ZEND_VM_HANDLER(214, ZEND_CALL_PARTIAL, ANY, ANY, SPEC(OBSERVER)) +{ + zval *ret = EX(return_value); + uint32_t call_info = EX_CALL_INFO(); + zval retval; + zend_function *fbc; + zend_execute_data *call; + + USE_OPLINE + + SAVE_OPLINE(); + + call = execute_data; + + if (zend_partial_init_call(call) == FAILURE) { + UNDEF_RESULT(); + HANDLE_EXCEPTION(); + } + + fbc = call->func; + + if (EXPECTED(fbc->type == ZEND_USER_FUNCTION)) { + execute_data = call; + i_init_func_execute_data_ex(&fbc->op_array, ret, true, true, true EXECUTE_DATA_CC); + if (EXPECTED(zend_execute_ex == execute_ex)) { + LOAD_OPLINE_EX(); + ZEND_OBSERVER_SAVE_OPLINE(); + ZEND_OBSERVER_FCALL_BEGIN(execute_data); + ZEND_VM_ENTER_EX(); + } else { + SAVE_OPLINE_EX(); + ZEND_OBSERVER_FCALL_BEGIN(execute_data); + execute_data = EX(prev_execute_data); + if (execute_data) { + LOAD_OPLINE(); + } + ZEND_ADD_CALL_FLAG(call, ZEND_CALL_TOP); + zend_execute_ex(call); + } + } else { + ZEND_ASSERT(fbc->type == ZEND_INTERNAL_FUNCTION); + +#if ZEND_DEBUG + bool should_throw = zend_internal_call_should_throw(fbc, call); +#endif + + if (!ret) { + ret = &retval; + } + ZVAL_NULL(ret); + + ZEND_OBSERVER_FCALL_BEGIN(call); + if (!zend_execute_internal) { + /* saves one function call if zend_execute_internal is not used */ + fbc->internal_function.handler(call, ret); + } else { + zend_execute_internal(call, ret); + } + +#if ZEND_DEBUG + if (!EG(exception) && call->func) { + if (should_throw) { + zend_internal_call_arginfo_violation(call->func); + } + ZEND_ASSERT(!(call->func->common.fn_flags & ZEND_ACC_HAS_RETURN_TYPE) || + zend_verify_internal_return_type(call->func, ret)); + ZEND_ASSERT((call->func->common.fn_flags & ZEND_ACC_RETURN_REFERENCE) + ? Z_ISREF_P(ret) : !Z_ISREF_P(ret)); + zend_verify_internal_func_info(call->func, ret); + } +#endif + ZEND_OBSERVER_FCALL_END(call, EG(exception) ? NULL : ret); + ZEND_VM_FCALL_INTERRUPT_CHECK(call); + + EG(current_execute_data) = call->prev_execute_data; + + zend_vm_stack_free_args(call); + if (UNEXPECTED(call_info & ZEND_CALL_HAS_EXTRA_NAMED_PARAMS)) { + zend_free_extra_named_params(call->extra_named_params); + } + if (ret == &retval) { + zval_ptr_dtor(ret); + } + + if (UNEXPECTED(ZEND_CALL_INFO(call) & ZEND_CALL_FAKE_CLOSURE)) { + OBJ_RELEASE(ZEND_PARTIAL_OBJECT(fbc)); + } + } + + execute_data = EG(current_execute_data); + + if (!execute_data || !EX(func) || !ZEND_USER_CODE(EX(func)->type) || (call_info & ZEND_CALL_TOP)) { + ZEND_VM_RETURN(); + } + + if (UNEXPECTED(call_info & ZEND_CALL_RELEASE_THIS)) { + zend_object *object = Z_OBJ(call->This); + OBJ_RELEASE(object); + } + zend_vm_stack_free_call_frame(call); + + if (UNEXPECTED(EG(exception) != NULL)) { + zend_rethrow_exception(execute_data); + HANDLE_EXCEPTION(); + } + + LOAD_OPLINE(); + ZEND_VM_INC_OPCODE(); + ZEND_VM_LEAVE(); +} + +ZEND_VM_HANDLER(213, ZEND_CHECK_PARTIAL_ARGS, UNUSED, UNUSED) +{ + USE_OPLINE + zend_execute_data *call = EX(call); + + SAVE_OPLINE(); + zend_partial_args_check(call); + ZEND_VM_NEXT_OPCODE_CHECK_EXCEPTION(); +} + ZEND_VM_HANDLER(186, ZEND_ISSET_ISEMPTY_THIS, UNUSED, UNUSED) { USE_OPLINE @@ -9705,6 +9883,38 @@ ZEND_VM_HANDLER(202, ZEND_CALLABLE_CONVERT, UNUSED, UNUSED) ZEND_VM_NEXT_OPCODE(); } +ZEND_VM_HANDLER(212, ZEND_CALLABLE_CONVERT_PARTIAL, UNUSED, ANY) +{ + USE_OPLINE + zend_execute_data *call = EX(call); + + zend_partial_create(EX_VAR(opline->result.var), 0, + &call->This, call->func, + ZEND_CALL_NUM_ARGS(call), ZEND_CALL_ARG(call, 1), + ZEND_CALL_INFO(call) & ZEND_CALL_HAS_EXTRA_NAMED_PARAMS ? + call->extra_named_params : NULL); + + if (ZEND_CALL_INFO(call) & ZEND_CALL_HAS_EXTRA_NAMED_PARAMS) { + zend_array_release(call->extra_named_params); + } + + if ((call->func->common.fn_flags & ZEND_ACC_CALL_VIA_TRAMPOLINE)) { + zend_free_trampoline(call->func); + } + + if (ZEND_CALL_INFO(call) & ZEND_CALL_RELEASE_THIS) { + OBJ_RELEASE(Z_OBJ(call->This)); + } else if (ZEND_CALL_INFO(call) & ZEND_CALL_CLOSURE) { + OBJ_RELEASE(ZEND_CLOSURE_OBJECT(call->func)); + } + + EX(call) = call->prev_execute_data; + + zend_vm_stack_free_call_frame(call); + + ZEND_VM_NEXT_OPCODE(); +} + ZEND_VM_HANDLER(208, ZEND_JMP_FRAMELESS, CONST, JMP_ADDR, NUM|CACHE_SLOT) { USE_OPLINE diff --git a/Zend/zend_vm_execute.h b/Zend/zend_vm_execute.h index cb2af9e49dd64..28e20c98c6812 100644 --- a/Zend/zend_vm_execute.h +++ b/Zend/zend_vm_execute.h @@ -1191,9 +1191,17 @@ static zend_never_inline ZEND_OPCODE_HANDLER_RET ZEND_FASTCALL zend_leave_helper OBJ_RELEASE(Z_OBJ(execute_data->This)); } else if (UNEXPECTED(call_info & ZEND_CALL_CLOSURE)) { OBJ_RELEASE(ZEND_CLOSURE_OBJECT(EX(func))); + } else if (UNEXPECTED(call_info & ZEND_CALL_FAKE_CLOSURE)) { + OBJ_RELEASE(ZEND_PARTIAL_OBJECT(EX(func))); } + + zend_execute_data *prev_execute_data = EX(prev_execute_data); +#ifdef __SANITIZE_ADDRESS__ + size_t size = (size_t)EG(vm_stack_top) - (size_t)execute_data; + __asan_poison_memory_region(execute_data, size); +#endif EG(vm_stack_top) = (zval*)execute_data; - execute_data = EX(prev_execute_data); + execute_data = prev_execute_data; if (UNEXPECTED(EG(exception) != NULL)) { zend_rethrow_exception(execute_data); @@ -1225,6 +1233,8 @@ static zend_never_inline ZEND_OPCODE_HANDLER_RET ZEND_FASTCALL zend_leave_helper OBJ_RELEASE(Z_OBJ(execute_data->This)); } else if (UNEXPECTED(call_info & ZEND_CALL_CLOSURE)) { OBJ_RELEASE(ZEND_CLOSURE_OBJECT(EX(func))); + } else if (UNEXPECTED(call_info & ZEND_CALL_FAKE_CLOSURE)) { + OBJ_RELEASE(ZEND_PARTIAL_OBJECT(EX(func))); } old_execute_data = execute_data; @@ -1282,6 +1292,8 @@ static zend_never_inline ZEND_OPCODE_HANDLER_RET ZEND_FASTCALL zend_leave_helper } if (UNEXPECTED(call_info & ZEND_CALL_CLOSURE)) { OBJ_RELEASE(ZEND_CLOSURE_OBJECT(EX(func))); + } else if (UNEXPECTED(call_info & ZEND_CALL_FAKE_CLOSURE)) { + OBJ_RELEASE(ZEND_PARTIAL_OBJECT(EX(func))); } ZEND_VM_RETURN(); } else /* if (call_kind == ZEND_CALL_TOP_CODE) */ { @@ -1368,6 +1380,10 @@ static ZEND_VM_HOT ZEND_OPCODE_HANDLER_RET ZEND_FASTCALL ZEND_DO_ICALL_SPEC_RETV } zend_vm_stack_free_call_frame_ex(call_info, call); } else { +#ifdef __SANITIZE_ADDRESS__ + size_t size = (size_t)EG(vm_stack_top) - (size_t)call; + __asan_poison_memory_region(call, size); +#endif EG(vm_stack_top) = (zval*)call; } @@ -1432,6 +1448,10 @@ static ZEND_VM_HOT ZEND_OPCODE_HANDLER_RET ZEND_FASTCALL ZEND_DO_ICALL_SPEC_RETV } zend_vm_stack_free_call_frame_ex(call_info, call); } else { +#ifdef __SANITIZE_ADDRESS__ + size_t size = (size_t)EG(vm_stack_top) - (size_t)call; + __asan_poison_memory_region(call, size); +#endif EG(vm_stack_top) = (zval*)call; } @@ -1497,6 +1517,10 @@ static ZEND_VM_COLD ZEND_OPCODE_HANDLER_RET ZEND_FASTCALL ZEND_DO_ICALL_SPEC_OBS } zend_vm_stack_free_call_frame_ex(call_info, call); } else { +#ifdef __SANITIZE_ADDRESS__ + size_t size = (size_t)EG(vm_stack_top) - (size_t)call; + __asan_poison_memory_region(call, size); +#endif EG(vm_stack_top) = (zval*)call; } @@ -1679,6 +1703,10 @@ static ZEND_VM_HOT ZEND_OPCODE_HANDLER_RET ZEND_FASTCALL ZEND_DO_FCALL_BY_NAME_S } zend_vm_stack_free_call_frame_ex(call_info, call); } else { +#ifdef __SANITIZE_ADDRESS__ + size_t size = (size_t)EG(vm_stack_top) - (size_t)call; + __asan_poison_memory_region(call, size); +#endif EG(vm_stack_top) = (zval*)call; } @@ -1788,6 +1816,10 @@ static ZEND_VM_HOT ZEND_OPCODE_HANDLER_RET ZEND_FASTCALL ZEND_DO_FCALL_BY_NAME_S } zend_vm_stack_free_call_frame_ex(call_info, call); } else { +#ifdef __SANITIZE_ADDRESS__ + size_t size = (size_t)EG(vm_stack_top) - (size_t)call; + __asan_poison_memory_region(call, size); +#endif EG(vm_stack_top) = (zval*)call; } @@ -1899,6 +1931,10 @@ static ZEND_VM_COLD ZEND_OPCODE_HANDLER_RET ZEND_FASTCALL ZEND_DO_FCALL_BY_NAME_ } zend_vm_stack_free_call_frame_ex(call_info, call); } else { +#ifdef __SANITIZE_ADDRESS__ + size_t size = (size_t)EG(vm_stack_top) - (size_t)call; + __asan_poison_memory_region(call, size); +#endif EG(vm_stack_top) = (zval*)call; } @@ -2351,8 +2387,13 @@ static ZEND_OPCODE_HANDLER_RET ZEND_FASTCALL ZEND_GENERATOR_CREATE_SPEC_HANDLER( call_info = EX_CALL_INFO(); EG(current_execute_data) = EX(prev_execute_data); if (EXPECTED(!(call_info & (ZEND_CALL_TOP|ZEND_CALL_ALLOCATED)))) { + zend_execute_data *prev_execute_data = EX(prev_execute_data); +#ifdef __SANITIZE_ADDRESS__ + size_t size = (size_t)EG(vm_stack_top) - (size_t)execute_data; + __asan_poison_memory_region(execute_data, size); +#endif EG(vm_stack_top) = (zval*)execute_data; - execute_data = EX(prev_execute_data); + execute_data = prev_execute_data; LOAD_NEXT_OPLINE(); ZEND_VM_LEAVE(); } else if (EXPECTED(!(call_info & ZEND_CALL_TOP))) { @@ -3810,6 +3851,227 @@ static ZEND_OPCODE_HANDLER_RET ZEND_FASTCALL ZEND_CALL_TRAMPOLINE_SPEC_OBSERVER_ ZEND_VM_LEAVE(); } +static ZEND_OPCODE_HANDLER_RET ZEND_FASTCALL ZEND_CALL_PARTIAL_SPEC_HANDLER(ZEND_OPCODE_HANDLER_ARGS) +{ + zval *ret = EX(return_value); + uint32_t call_info = EX_CALL_INFO(); + zval retval; + zend_function *fbc; + zend_execute_data *call; + + USE_OPLINE + + SAVE_OPLINE(); + + call = execute_data; + + if (zend_partial_init_call(call) == FAILURE) { + UNDEF_RESULT(); + HANDLE_EXCEPTION(); + } + + fbc = call->func; + + if (EXPECTED(fbc->type == ZEND_USER_FUNCTION)) { + execute_data = call; + i_init_func_execute_data_ex(&fbc->op_array, ret, true, true, true EXECUTE_DATA_CC); + if (EXPECTED(zend_execute_ex == execute_ex)) { + LOAD_OPLINE_EX(); + + + ZEND_VM_ENTER_EX(); + } else { + SAVE_OPLINE_EX(); + + execute_data = EX(prev_execute_data); + if (execute_data) { + LOAD_OPLINE(); + } + ZEND_ADD_CALL_FLAG(call, ZEND_CALL_TOP); + zend_execute_ex(call); + } + } else { + ZEND_ASSERT(fbc->type == ZEND_INTERNAL_FUNCTION); + +#if ZEND_DEBUG + bool should_throw = zend_internal_call_should_throw(fbc, call); +#endif + + if (!ret) { + ret = &retval; + } + ZVAL_NULL(ret); + + if (!zend_execute_internal) { + /* saves one function call if zend_execute_internal is not used */ + fbc->internal_function.handler(call, ret); + } else { + zend_execute_internal(call, ret); + } + +#if ZEND_DEBUG + if (!EG(exception) && call->func) { + if (should_throw) { + zend_internal_call_arginfo_violation(call->func); + } + ZEND_ASSERT(!(call->func->common.fn_flags & ZEND_ACC_HAS_RETURN_TYPE) || + zend_verify_internal_return_type(call->func, ret)); + ZEND_ASSERT((call->func->common.fn_flags & ZEND_ACC_RETURN_REFERENCE) + ? Z_ISREF_P(ret) : !Z_ISREF_P(ret)); + zend_verify_internal_func_info(call->func, ret); + } +#endif + + ZEND_VM_FCALL_INTERRUPT_CHECK(call); + + EG(current_execute_data) = call->prev_execute_data; + + zend_vm_stack_free_args(call); + if (UNEXPECTED(call_info & ZEND_CALL_HAS_EXTRA_NAMED_PARAMS)) { + zend_free_extra_named_params(call->extra_named_params); + } + if (ret == &retval) { + zval_ptr_dtor(ret); + } + + if (UNEXPECTED(ZEND_CALL_INFO(call) & ZEND_CALL_FAKE_CLOSURE)) { + OBJ_RELEASE(ZEND_PARTIAL_OBJECT(fbc)); + } + } + + execute_data = EG(current_execute_data); + + if (!execute_data || !EX(func) || !ZEND_USER_CODE(EX(func)->type) || (call_info & ZEND_CALL_TOP)) { + ZEND_VM_RETURN(); + } + + if (UNEXPECTED(call_info & ZEND_CALL_RELEASE_THIS)) { + zend_object *object = Z_OBJ(call->This); + OBJ_RELEASE(object); + } + zend_vm_stack_free_call_frame(call); + + if (UNEXPECTED(EG(exception) != NULL)) { + zend_rethrow_exception(execute_data); + HANDLE_EXCEPTION(); + } + + LOAD_OPLINE(); + ZEND_VM_INC_OPCODE(); + ZEND_VM_LEAVE(); +} + +static ZEND_OPCODE_HANDLER_RET ZEND_FASTCALL ZEND_CALL_PARTIAL_SPEC_OBSERVER_HANDLER(ZEND_OPCODE_HANDLER_ARGS) +{ + zval *ret = EX(return_value); + uint32_t call_info = EX_CALL_INFO(); + zval retval; + zend_function *fbc; + zend_execute_data *call; + + USE_OPLINE + + SAVE_OPLINE(); + + call = execute_data; + + if (zend_partial_init_call(call) == FAILURE) { + UNDEF_RESULT(); + HANDLE_EXCEPTION(); + } + + fbc = call->func; + + if (EXPECTED(fbc->type == ZEND_USER_FUNCTION)) { + execute_data = call; + i_init_func_execute_data_ex(&fbc->op_array, ret, true, true, true EXECUTE_DATA_CC); + if (EXPECTED(zend_execute_ex == execute_ex)) { + LOAD_OPLINE_EX(); + SAVE_OPLINE(); + zend_observer_fcall_begin_specialized(execute_data, false); + ZEND_VM_ENTER_EX(); + } else { + SAVE_OPLINE_EX(); + zend_observer_fcall_begin_specialized(execute_data, false); + execute_data = EX(prev_execute_data); + if (execute_data) { + LOAD_OPLINE(); + } + ZEND_ADD_CALL_FLAG(call, ZEND_CALL_TOP); + zend_execute_ex(call); + } + } else { + ZEND_ASSERT(fbc->type == ZEND_INTERNAL_FUNCTION); + +#if ZEND_DEBUG + bool should_throw = zend_internal_call_should_throw(fbc, call); +#endif + + if (!ret) { + ret = &retval; + } + ZVAL_NULL(ret); + + zend_observer_fcall_begin_specialized(call, false); + if (!zend_execute_internal) { + /* saves one function call if zend_execute_internal is not used */ + fbc->internal_function.handler(call, ret); + } else { + zend_execute_internal(call, ret); + } + +#if ZEND_DEBUG + if (!EG(exception) && call->func) { + if (should_throw) { + zend_internal_call_arginfo_violation(call->func); + } + ZEND_ASSERT(!(call->func->common.fn_flags & ZEND_ACC_HAS_RETURN_TYPE) || + zend_verify_internal_return_type(call->func, ret)); + ZEND_ASSERT((call->func->common.fn_flags & ZEND_ACC_RETURN_REFERENCE) + ? Z_ISREF_P(ret) : !Z_ISREF_P(ret)); + zend_verify_internal_func_info(call->func, ret); + } +#endif + zend_observer_fcall_end(call, EG(exception) ? NULL : ret); + ZEND_VM_FCALL_INTERRUPT_CHECK(call); + + EG(current_execute_data) = call->prev_execute_data; + + zend_vm_stack_free_args(call); + if (UNEXPECTED(call_info & ZEND_CALL_HAS_EXTRA_NAMED_PARAMS)) { + zend_free_extra_named_params(call->extra_named_params); + } + if (ret == &retval) { + zval_ptr_dtor(ret); + } + + if (UNEXPECTED(ZEND_CALL_INFO(call) & ZEND_CALL_FAKE_CLOSURE)) { + OBJ_RELEASE(ZEND_PARTIAL_OBJECT(fbc)); + } + } + + execute_data = EG(current_execute_data); + + if (!execute_data || !EX(func) || !ZEND_USER_CODE(EX(func)->type) || (call_info & ZEND_CALL_TOP)) { + ZEND_VM_RETURN(); + } + + if (UNEXPECTED(call_info & ZEND_CALL_RELEASE_THIS)) { + zend_object *object = Z_OBJ(call->This); + OBJ_RELEASE(object); + } + zend_vm_stack_free_call_frame(call); + + if (UNEXPECTED(EG(exception) != NULL)) { + zend_rethrow_exception(execute_data); + HANDLE_EXCEPTION(); + } + + LOAD_OPLINE(); + ZEND_VM_INC_OPCODE(); + ZEND_VM_LEAVE(); +} + static ZEND_OPCODE_HANDLER_RET ZEND_FASTCALL ZEND_FRAMELESS_ICALL_2_SPEC_HANDLER(ZEND_OPCODE_HANDLER_ARGS) { USE_OPLINE @@ -11060,7 +11322,7 @@ static ZEND_OPCODE_HANDLER_RET ZEND_FASTCALL ZEND_NEW_SPEC_CONST_UNUSED_HANDLER( /* Perform a dummy function call */ call = zend_vm_stack_push_call_frame( ZEND_CALL_FUNCTION, (zend_function *) &zend_pass_function, - opline->extended_value, NULL); + opline->extended_value, Z_OBJ_P(result)); } else { if (EXPECTED(constructor->type == ZEND_USER_FUNCTION) && UNEXPECTED(!RUN_TIME_CACHE(&constructor->op_array))) { init_func_run_time_cache(&constructor->op_array); @@ -30610,7 +30872,7 @@ static ZEND_OPCODE_HANDLER_RET ZEND_FASTCALL ZEND_NEW_SPEC_VAR_UNUSED_HANDLER(ZE /* Perform a dummy function call */ call = zend_vm_stack_push_call_frame( ZEND_CALL_FUNCTION, (zend_function *) &zend_pass_function, - opline->extended_value, NULL); + opline->extended_value, Z_OBJ_P(result)); } else { if (EXPECTED(constructor->type == ZEND_USER_FUNCTION) && UNEXPECTED(!RUN_TIME_CACHE(&constructor->op_array))) { init_func_run_time_cache(&constructor->op_array); @@ -33641,6 +33903,38 @@ static ZEND_OPCODE_HANDLER_RET ZEND_FASTCALL ZEND_FETCH_CLASS_NAME_SPEC_UNUSED_H ZEND_VM_NEXT_OPCODE(); } +static ZEND_OPCODE_HANDLER_RET ZEND_FASTCALL ZEND_CALLABLE_CONVERT_PARTIAL_SPEC_UNUSED_HANDLER(ZEND_OPCODE_HANDLER_ARGS) +{ + USE_OPLINE + zend_execute_data *call = EX(call); + + zend_partial_create(EX_VAR(opline->result.var), 0, + &call->This, call->func, + ZEND_CALL_NUM_ARGS(call), ZEND_CALL_ARG(call, 1), + ZEND_CALL_INFO(call) & ZEND_CALL_HAS_EXTRA_NAMED_PARAMS ? + call->extra_named_params : NULL); + + if (ZEND_CALL_INFO(call) & ZEND_CALL_HAS_EXTRA_NAMED_PARAMS) { + zend_array_release(call->extra_named_params); + } + + if ((call->func->common.fn_flags & ZEND_ACC_CALL_VIA_TRAMPOLINE)) { + zend_free_trampoline(call->func); + } + + if (ZEND_CALL_INFO(call) & ZEND_CALL_RELEASE_THIS) { + OBJ_RELEASE(Z_OBJ(call->This)); + } else if (ZEND_CALL_INFO(call) & ZEND_CALL_CLOSURE) { + OBJ_RELEASE(ZEND_CLOSURE_OBJECT(call->func)); + } + + EX(call) = call->prev_execute_data; + + zend_vm_stack_free_call_frame(call); + + ZEND_VM_NEXT_OPCODE(); +} + static ZEND_OPCODE_HANDLER_RET ZEND_FASTCALL ZEND_ASSIGN_OBJ_OP_SPEC_UNUSED_CONST_HANDLER(ZEND_OPCODE_HANDLER_ARGS) { USE_OPLINE @@ -35694,6 +35988,35 @@ static ZEND_OPCODE_HANDLER_RET ZEND_FASTCALL ZEND_YIELD_SPEC_UNUSED_CONST_HANDLE ZEND_VM_RETURN(); } +static ZEND_OPCODE_HANDLER_RET ZEND_FASTCALL ZEND_SEND_PLACEHOLDER_SPEC_UNUSED_CONST_HANDLER(ZEND_OPCODE_HANDLER_ARGS) +{ + USE_OPLINE + zval *arg; + zend_execute_data *call = EX(call); + + if (IS_CONST == 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); + } + + Z_TYPE_INFO_P(arg) = opline->op1.num; + + if (Z_TYPE_INFO_P(arg) == _IS_PLACEHOLDER_VARIADIC) { + Z_EXTRA_P(ZEND_CALL_ARG(call, 1)) = _IS_PLACEHOLDER_VARIADIC; + } else if (opline->op2.num == 1) { + Z_EXTRA_P(arg) = 0; + } + + ZEND_VM_NEXT_OPCODE(); +} + static ZEND_OPCODE_HANDLER_RET ZEND_FASTCALL ZEND_FETCH_CLASS_CONSTANT_SPEC_UNUSED_TMPVARCV_HANDLER(ZEND_OPCODE_HANDLER_ARGS) { zend_class_entry *ce, *scope; @@ -38077,7 +38400,7 @@ static ZEND_OPCODE_HANDLER_RET ZEND_FASTCALL ZEND_NEW_SPEC_UNUSED_UNUSED_HANDLER /* Perform a dummy function call */ call = zend_vm_stack_push_call_frame( ZEND_CALL_FUNCTION, (zend_function *) &zend_pass_function, - opline->extended_value, NULL); + opline->extended_value, Z_OBJ_P(result)); } else { if (EXPECTED(constructor->type == ZEND_USER_FUNCTION) && UNEXPECTED(!RUN_TIME_CACHE(&constructor->op_array))) { init_func_run_time_cache(&constructor->op_array); @@ -38262,6 +38585,45 @@ static ZEND_OPCODE_HANDLER_RET ZEND_FASTCALL ZEND_FETCH_GLOBALS_SPEC_UNUSED_UNUS ZEND_VM_NEXT_OPCODE(); } +static ZEND_OPCODE_HANDLER_RET ZEND_FASTCALL ZEND_SEND_PLACEHOLDER_SPEC_UNUSED_UNUSED_HANDLER(ZEND_OPCODE_HANDLER_ARGS) +{ + USE_OPLINE + zval *arg; + zend_execute_data *call = EX(call); + + if (IS_UNUSED == 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); + } + + Z_TYPE_INFO_P(arg) = opline->op1.num; + + if (Z_TYPE_INFO_P(arg) == _IS_PLACEHOLDER_VARIADIC) { + Z_EXTRA_P(ZEND_CALL_ARG(call, 1)) = _IS_PLACEHOLDER_VARIADIC; + } else if (opline->op2.num == 1) { + Z_EXTRA_P(arg) = 0; + } + + ZEND_VM_NEXT_OPCODE(); +} + +static ZEND_OPCODE_HANDLER_RET ZEND_FASTCALL ZEND_CHECK_PARTIAL_ARGS_SPEC_UNUSED_UNUSED_HANDLER(ZEND_OPCODE_HANDLER_ARGS) +{ + USE_OPLINE + zend_execute_data *call = EX(call); + + SAVE_OPLINE(); + zend_partial_args_check(call); + ZEND_VM_NEXT_OPCODE_CHECK_EXCEPTION(); +} + static ZEND_OPCODE_HANDLER_RET ZEND_FASTCALL ZEND_ISSET_ISEMPTY_THIS_SPEC_UNUSED_UNUSED_HANDLER(ZEND_OPCODE_HANDLER_ARGS) { USE_OPLINE @@ -57748,6 +58110,15 @@ ZEND_API void execute_ex(zend_execute_data *ex) (void*)&&ZEND_JMP_FRAMELESS_SPEC_CONST_LABEL, (void*)&&ZEND_INIT_PARENT_PROPERTY_HOOK_CALL_SPEC_CONST_UNUSED_LABEL, (void*)&&ZEND_DECLARE_ATTRIBUTED_CONST_SPEC_CONST_CONST_LABEL, + (void*)&&ZEND_SEND_PLACEHOLDER_SPEC_UNUSED_CONST_LABEL, + (void*)&&ZEND_NULL_LABEL, + (void*)&&ZEND_NULL_LABEL, + (void*)&&ZEND_SEND_PLACEHOLDER_SPEC_UNUSED_UNUSED_LABEL, + (void*)&&ZEND_NULL_LABEL, + (void*)&&ZEND_CALLABLE_CONVERT_PARTIAL_SPEC_UNUSED_LABEL, + (void*)&&ZEND_CHECK_PARTIAL_ARGS_SPEC_UNUSED_UNUSED_LABEL, + (void*)&&ZEND_CALL_PARTIAL_SPEC_LABEL, + (void*)&&ZEND_CALL_PARTIAL_SPEC_OBSERVER_LABEL, (void*)&&ZEND_INIT_FCALL_OFFSET_SPEC_CONST_LABEL, (void*)&&ZEND_RECV_NOTYPE_SPEC_LABEL, (void*)&&ZEND_NULL_LABEL, @@ -58792,9 +59163,17 @@ ZEND_API void execute_ex(zend_execute_data *ex) OBJ_RELEASE(Z_OBJ(execute_data->This)); } else if (UNEXPECTED(call_info & ZEND_CALL_CLOSURE)) { OBJ_RELEASE(ZEND_CLOSURE_OBJECT(EX(func))); + } else if (UNEXPECTED(call_info & ZEND_CALL_FAKE_CLOSURE)) { + OBJ_RELEASE(ZEND_PARTIAL_OBJECT(EX(func))); } + + zend_execute_data *prev_execute_data = EX(prev_execute_data); +#ifdef __SANITIZE_ADDRESS__ + size_t size = (size_t)EG(vm_stack_top) - (size_t)execute_data; + __asan_poison_memory_region(execute_data, size); +#endif EG(vm_stack_top) = (zval*)execute_data; - execute_data = EX(prev_execute_data); + execute_data = prev_execute_data; if (UNEXPECTED(EG(exception) != NULL)) { zend_rethrow_exception(execute_data); @@ -58826,6 +59205,8 @@ ZEND_API void execute_ex(zend_execute_data *ex) OBJ_RELEASE(Z_OBJ(execute_data->This)); } else if (UNEXPECTED(call_info & ZEND_CALL_CLOSURE)) { OBJ_RELEASE(ZEND_CLOSURE_OBJECT(EX(func))); + } else if (UNEXPECTED(call_info & ZEND_CALL_FAKE_CLOSURE)) { + OBJ_RELEASE(ZEND_PARTIAL_OBJECT(EX(func))); } old_execute_data = execute_data; @@ -58883,6 +59264,8 @@ ZEND_API void execute_ex(zend_execute_data *ex) } if (UNEXPECTED(call_info & ZEND_CALL_CLOSURE)) { OBJ_RELEASE(ZEND_CLOSURE_OBJECT(EX(func))); + } else if (UNEXPECTED(call_info & ZEND_CALL_FAKE_CLOSURE)) { + OBJ_RELEASE(ZEND_PARTIAL_OBJECT(EX(func))); } ZEND_VM_RETURN(); } else /* if (call_kind == ZEND_CALL_TOP_CODE) */ { @@ -59099,6 +59482,16 @@ ZEND_API void execute_ex(zend_execute_data *ex) ZEND_CALL_TRAMPOLINE_SPEC_OBSERVER_HANDLER(ZEND_OPCODE_HANDLER_ARGS_PASSTHRU); VM_TRACE_OP_END(ZEND_CALL_TRAMPOLINE_SPEC_OBSERVER) HYBRID_BREAK(); + HYBRID_CASE(ZEND_CALL_PARTIAL_SPEC): + VM_TRACE(ZEND_CALL_PARTIAL_SPEC) + ZEND_CALL_PARTIAL_SPEC_HANDLER(ZEND_OPCODE_HANDLER_ARGS_PASSTHRU); + VM_TRACE_OP_END(ZEND_CALL_PARTIAL_SPEC) + HYBRID_BREAK(); + HYBRID_CASE(ZEND_CALL_PARTIAL_SPEC_OBSERVER): + VM_TRACE(ZEND_CALL_PARTIAL_SPEC_OBSERVER) + ZEND_CALL_PARTIAL_SPEC_OBSERVER_HANDLER(ZEND_OPCODE_HANDLER_ARGS_PASSTHRU); + VM_TRACE_OP_END(ZEND_CALL_PARTIAL_SPEC_OBSERVER) + HYBRID_BREAK(); HYBRID_CASE(ZEND_FRAMELESS_ICALL_2_SPEC): VM_TRACE(ZEND_FRAMELESS_ICALL_2_SPEC) ZEND_FRAMELESS_ICALL_2_SPEC_HANDLER(ZEND_OPCODE_HANDLER_ARGS_PASSTHRU); @@ -62501,6 +62894,11 @@ ZEND_API void execute_ex(zend_execute_data *ex) ZEND_FETCH_CLASS_NAME_SPEC_UNUSED_HANDLER(ZEND_OPCODE_HANDLER_ARGS_PASSTHRU); VM_TRACE_OP_END(ZEND_FETCH_CLASS_NAME_SPEC_UNUSED) HYBRID_BREAK(); + HYBRID_CASE(ZEND_CALLABLE_CONVERT_PARTIAL_SPEC_UNUSED): + VM_TRACE(ZEND_CALLABLE_CONVERT_PARTIAL_SPEC_UNUSED) + ZEND_CALLABLE_CONVERT_PARTIAL_SPEC_UNUSED_HANDLER(ZEND_OPCODE_HANDLER_ARGS_PASSTHRU); + VM_TRACE_OP_END(ZEND_CALLABLE_CONVERT_PARTIAL_SPEC_UNUSED) + HYBRID_BREAK(); HYBRID_CASE(ZEND_ASSIGN_OBJ_OP_SPEC_UNUSED_CONST): VM_TRACE(ZEND_ASSIGN_OBJ_OP_SPEC_UNUSED_CONST) ZEND_ASSIGN_OBJ_OP_SPEC_UNUSED_CONST_HANDLER(ZEND_OPCODE_HANDLER_ARGS_PASSTHRU); @@ -62631,6 +63029,11 @@ ZEND_API void execute_ex(zend_execute_data *ex) ZEND_YIELD_SPEC_UNUSED_CONST_HANDLER(ZEND_OPCODE_HANDLER_ARGS_PASSTHRU); VM_TRACE_OP_END(ZEND_YIELD_SPEC_UNUSED_CONST) HYBRID_BREAK(); + HYBRID_CASE(ZEND_SEND_PLACEHOLDER_SPEC_UNUSED_CONST): + VM_TRACE(ZEND_SEND_PLACEHOLDER_SPEC_UNUSED_CONST) + ZEND_SEND_PLACEHOLDER_SPEC_UNUSED_CONST_HANDLER(ZEND_OPCODE_HANDLER_ARGS_PASSTHRU); + VM_TRACE_OP_END(ZEND_SEND_PLACEHOLDER_SPEC_UNUSED_CONST) + HYBRID_BREAK(); HYBRID_CASE(ZEND_FETCH_CLASS_CONSTANT_SPEC_UNUSED_TMPVARCV): VM_TRACE(ZEND_FETCH_CLASS_CONSTANT_SPEC_UNUSED_TMPVARCV) ZEND_FETCH_CLASS_CONSTANT_SPEC_UNUSED_TMPVARCV_HANDLER(ZEND_OPCODE_HANDLER_ARGS_PASSTHRU); @@ -62811,6 +63214,16 @@ ZEND_API void execute_ex(zend_execute_data *ex) ZEND_FETCH_GLOBALS_SPEC_UNUSED_UNUSED_HANDLER(ZEND_OPCODE_HANDLER_ARGS_PASSTHRU); VM_TRACE_OP_END(ZEND_FETCH_GLOBALS_SPEC_UNUSED_UNUSED) HYBRID_BREAK(); + HYBRID_CASE(ZEND_SEND_PLACEHOLDER_SPEC_UNUSED_UNUSED): + VM_TRACE(ZEND_SEND_PLACEHOLDER_SPEC_UNUSED_UNUSED) + ZEND_SEND_PLACEHOLDER_SPEC_UNUSED_UNUSED_HANDLER(ZEND_OPCODE_HANDLER_ARGS_PASSTHRU); + VM_TRACE_OP_END(ZEND_SEND_PLACEHOLDER_SPEC_UNUSED_UNUSED) + HYBRID_BREAK(); + HYBRID_CASE(ZEND_CHECK_PARTIAL_ARGS_SPEC_UNUSED_UNUSED): + VM_TRACE(ZEND_CHECK_PARTIAL_ARGS_SPEC_UNUSED_UNUSED) + ZEND_CHECK_PARTIAL_ARGS_SPEC_UNUSED_UNUSED_HANDLER(ZEND_OPCODE_HANDLER_ARGS_PASSTHRU); + VM_TRACE_OP_END(ZEND_CHECK_PARTIAL_ARGS_SPEC_UNUSED_UNUSED) + HYBRID_BREAK(); HYBRID_CASE(ZEND_ISSET_ISEMPTY_THIS_SPEC_UNUSED_UNUSED): VM_TRACE(ZEND_ISSET_ISEMPTY_THIS_SPEC_UNUSED_UNUSED) ZEND_ISSET_ISEMPTY_THIS_SPEC_UNUSED_UNUSED_HANDLER(ZEND_OPCODE_HANDLER_ARGS_PASSTHRU); @@ -66976,6 +67389,15 @@ void zend_vm_init(void) ZEND_JMP_FRAMELESS_SPEC_CONST_HANDLER, ZEND_INIT_PARENT_PROPERTY_HOOK_CALL_SPEC_CONST_UNUSED_HANDLER, ZEND_DECLARE_ATTRIBUTED_CONST_SPEC_CONST_CONST_HANDLER, + ZEND_SEND_PLACEHOLDER_SPEC_UNUSED_CONST_HANDLER, + ZEND_NULL_HANDLER, + ZEND_NULL_HANDLER, + ZEND_SEND_PLACEHOLDER_SPEC_UNUSED_UNUSED_HANDLER, + ZEND_NULL_HANDLER, + ZEND_CALLABLE_CONVERT_PARTIAL_SPEC_UNUSED_HANDLER, + ZEND_CHECK_PARTIAL_ARGS_SPEC_UNUSED_UNUSED_HANDLER, + ZEND_CALL_PARTIAL_SPEC_HANDLER, + ZEND_CALL_PARTIAL_SPEC_OBSERVER_HANDLER, ZEND_INIT_FCALL_OFFSET_SPEC_CONST_HANDLER, ZEND_RECV_NOTYPE_SPEC_HANDLER, ZEND_NULL_HANDLER, @@ -67939,7 +68361,7 @@ void zend_vm_init(void) 1255, 1256 | SPEC_RULE_OP1, 1261 | SPEC_RULE_OP1, - 3493, + 3502, 1266 | SPEC_RULE_OP1, 1271 | SPEC_RULE_OP1, 1276 | SPEC_RULE_OP2, @@ -67973,7 +68395,7 @@ void zend_vm_init(void) 1559 | SPEC_RULE_OP1 | SPEC_RULE_OP2, 1584 | SPEC_RULE_OP1, 1589, - 3493, + 3502, 1590 | SPEC_RULE_OP1, 1595 | SPEC_RULE_OP1 | SPEC_RULE_OP2, 1620 | SPEC_RULE_OP1 | SPEC_RULE_OP2, @@ -68105,51 +68527,51 @@ void zend_vm_init(void) 2575, 2576, 2577, - 3493, - 3493, - 3493, - 3493, - 3493, - 3493, - 3493, - 3493, - 3493, - 3493, - 3493, - 3493, - 3493, - 3493, - 3493, - 3493, - 3493, - 3493, - 3493, - 3493, - 3493, - 3493, - 3493, - 3493, - 3493, - 3493, - 3493, - 3493, - 3493, - 3493, - 3493, - 3493, - 3493, - 3493, - 3493, - 3493, - 3493, - 3493, - 3493, - 3493, - 3493, - 3493, - 3493, - 3493, - 3493, + 2578 | SPEC_RULE_OP2, + 2583, + 2584, + 2585 | SPEC_RULE_OBSERVER, + 3502, + 3502, + 3502, + 3502, + 3502, + 3502, + 3502, + 3502, + 3502, + 3502, + 3502, + 3502, + 3502, + 3502, + 3502, + 3502, + 3502, + 3502, + 3502, + 3502, + 3502, + 3502, + 3502, + 3502, + 3502, + 3502, + 3502, + 3502, + 3502, + 3502, + 3502, + 3502, + 3502, + 3502, + 3502, + 3502, + 3502, + 3502, + 3502, + 3502, + 3502, }; #if (ZEND_VM_KIND == ZEND_VM_KIND_HYBRID) zend_opcode_handler_funcs = labels; @@ -68322,7 +68744,7 @@ ZEND_API void ZEND_FASTCALL zend_vm_set_opcode_handler_ex(zend_op* op, uint32_t if (op->op1_type == IS_CONST && op->op2_type == IS_CONST) { break; } - spec = 2586 | SPEC_RULE_OP1 | SPEC_RULE_OP2 | SPEC_RULE_COMMUTATIVE; + spec = 2595 | SPEC_RULE_OP1 | SPEC_RULE_OP2 | SPEC_RULE_COMMUTATIVE; if (op->op1_type < op->op2_type) { zend_swap_operands(op); } @@ -68330,7 +68752,7 @@ ZEND_API void ZEND_FASTCALL zend_vm_set_opcode_handler_ex(zend_op* op, uint32_t if (op->op1_type == IS_CONST && op->op2_type == IS_CONST) { break; } - spec = 2611 | SPEC_RULE_OP1 | SPEC_RULE_OP2 | SPEC_RULE_COMMUTATIVE; + spec = 2620 | SPEC_RULE_OP1 | SPEC_RULE_OP2 | SPEC_RULE_COMMUTATIVE; if (op->op1_type < op->op2_type) { zend_swap_operands(op); } @@ -68338,7 +68760,7 @@ ZEND_API void ZEND_FASTCALL zend_vm_set_opcode_handler_ex(zend_op* op, uint32_t if (op->op1_type == IS_CONST && op->op2_type == IS_CONST) { break; } - spec = 2636 | SPEC_RULE_OP1 | SPEC_RULE_OP2 | SPEC_RULE_COMMUTATIVE; + spec = 2645 | SPEC_RULE_OP1 | SPEC_RULE_OP2 | SPEC_RULE_COMMUTATIVE; if (op->op1_type < op->op2_type) { zend_swap_operands(op); } @@ -68349,17 +68771,17 @@ ZEND_API void ZEND_FASTCALL zend_vm_set_opcode_handler_ex(zend_op* op, uint32_t if (op->op1_type == IS_CONST && op->op2_type == IS_CONST) { break; } - spec = 2661 | SPEC_RULE_OP1 | SPEC_RULE_OP2; + spec = 2670 | SPEC_RULE_OP1 | SPEC_RULE_OP2; } else if (op1_info == MAY_BE_LONG && op2_info == MAY_BE_LONG) { if (op->op1_type == IS_CONST && op->op2_type == IS_CONST) { break; } - spec = 2686 | SPEC_RULE_OP1 | SPEC_RULE_OP2; + spec = 2695 | SPEC_RULE_OP1 | SPEC_RULE_OP2; } else if (op1_info == MAY_BE_DOUBLE && op2_info == MAY_BE_DOUBLE) { if (op->op1_type == IS_CONST && op->op2_type == IS_CONST) { break; } - spec = 2711 | SPEC_RULE_OP1 | SPEC_RULE_OP2; + spec = 2720 | SPEC_RULE_OP1 | SPEC_RULE_OP2; } break; case ZEND_MUL: @@ -68370,17 +68792,17 @@ ZEND_API void ZEND_FASTCALL zend_vm_set_opcode_handler_ex(zend_op* op, uint32_t if (op->op1_type == IS_CONST && op->op2_type == IS_CONST) { break; } - spec = 2736 | SPEC_RULE_OP1 | SPEC_RULE_OP2 | SPEC_RULE_COMMUTATIVE; + spec = 2745 | SPEC_RULE_OP1 | SPEC_RULE_OP2 | SPEC_RULE_COMMUTATIVE; } else if (op1_info == MAY_BE_LONG && op2_info == MAY_BE_LONG) { if (op->op1_type == IS_CONST && op->op2_type == IS_CONST) { break; } - spec = 2761 | SPEC_RULE_OP1 | SPEC_RULE_OP2 | SPEC_RULE_COMMUTATIVE; + spec = 2770 | SPEC_RULE_OP1 | SPEC_RULE_OP2 | SPEC_RULE_COMMUTATIVE; } else if (op1_info == MAY_BE_DOUBLE && op2_info == MAY_BE_DOUBLE) { if (op->op1_type == IS_CONST && op->op2_type == IS_CONST) { break; } - spec = 2786 | SPEC_RULE_OP1 | SPEC_RULE_OP2 | SPEC_RULE_COMMUTATIVE; + spec = 2795 | SPEC_RULE_OP1 | SPEC_RULE_OP2 | SPEC_RULE_COMMUTATIVE; } break; case ZEND_IS_IDENTICAL: @@ -68391,16 +68813,16 @@ ZEND_API void ZEND_FASTCALL zend_vm_set_opcode_handler_ex(zend_op* op, uint32_t if (op->op1_type == IS_CONST && op->op2_type == IS_CONST) { break; } - spec = 2811 | SPEC_RULE_OP1 | SPEC_RULE_OP2 | SPEC_RULE_SMART_BRANCH | SPEC_RULE_COMMUTATIVE; + spec = 2820 | SPEC_RULE_OP1 | SPEC_RULE_OP2 | SPEC_RULE_SMART_BRANCH | SPEC_RULE_COMMUTATIVE; } else if (op1_info == MAY_BE_DOUBLE && op2_info == MAY_BE_DOUBLE) { if (op->op1_type == IS_CONST && op->op2_type == IS_CONST) { break; } - spec = 2886 | SPEC_RULE_OP1 | SPEC_RULE_OP2 | SPEC_RULE_SMART_BRANCH | SPEC_RULE_COMMUTATIVE; + spec = 2895 | SPEC_RULE_OP1 | SPEC_RULE_OP2 | SPEC_RULE_SMART_BRANCH | SPEC_RULE_COMMUTATIVE; } else if (op->op2_type == IS_CONST && (Z_TYPE_P(RT_CONSTANT(op, op->op2)) == IS_ARRAY && zend_hash_num_elements(Z_ARR_P(RT_CONSTANT(op, op->op2))) == 0)) { - spec = 3111 | SPEC_RULE_SMART_BRANCH | SPEC_RULE_COMMUTATIVE; + spec = 3120 | SPEC_RULE_SMART_BRANCH | SPEC_RULE_COMMUTATIVE; } else if (op->op1_type == IS_CV && (op->op2_type & (IS_CONST|IS_CV)) && !(op1_info & (MAY_BE_UNDEF|MAY_BE_REF)) && !(op2_info & (MAY_BE_UNDEF|MAY_BE_REF))) { - spec = 3117 | SPEC_RULE_OP2 | SPEC_RULE_COMMUTATIVE; + spec = 3126 | SPEC_RULE_OP2 | SPEC_RULE_COMMUTATIVE; } break; case ZEND_IS_NOT_IDENTICAL: @@ -68411,16 +68833,16 @@ ZEND_API void ZEND_FASTCALL zend_vm_set_opcode_handler_ex(zend_op* op, uint32_t if (op->op1_type == IS_CONST && op->op2_type == IS_CONST) { break; } - spec = 2961 | SPEC_RULE_OP1 | SPEC_RULE_OP2 | SPEC_RULE_SMART_BRANCH | SPEC_RULE_COMMUTATIVE; + spec = 2970 | SPEC_RULE_OP1 | SPEC_RULE_OP2 | SPEC_RULE_SMART_BRANCH | SPEC_RULE_COMMUTATIVE; } else if (op1_info == MAY_BE_DOUBLE && op2_info == MAY_BE_DOUBLE) { if (op->op1_type == IS_CONST && op->op2_type == IS_CONST) { break; } - spec = 3036 | SPEC_RULE_OP1 | SPEC_RULE_OP2 | SPEC_RULE_SMART_BRANCH | SPEC_RULE_COMMUTATIVE; + spec = 3045 | SPEC_RULE_OP1 | SPEC_RULE_OP2 | SPEC_RULE_SMART_BRANCH | SPEC_RULE_COMMUTATIVE; } else if (op->op2_type == IS_CONST && (Z_TYPE_P(RT_CONSTANT(op, op->op2)) == IS_ARRAY && zend_hash_num_elements(Z_ARR_P(RT_CONSTANT(op, op->op2))) == 0)) { - spec = 3114 | SPEC_RULE_SMART_BRANCH | SPEC_RULE_COMMUTATIVE; + spec = 3123 | SPEC_RULE_SMART_BRANCH | SPEC_RULE_COMMUTATIVE; } else if (op->op1_type == IS_CV && (op->op2_type & (IS_CONST|IS_CV)) && !(op1_info & (MAY_BE_UNDEF|MAY_BE_REF)) && !(op2_info & (MAY_BE_UNDEF|MAY_BE_REF))) { - spec = 3122 | SPEC_RULE_OP2 | SPEC_RULE_COMMUTATIVE; + spec = 3131 | SPEC_RULE_OP2 | SPEC_RULE_COMMUTATIVE; } break; case ZEND_IS_EQUAL: @@ -68431,12 +68853,12 @@ ZEND_API void ZEND_FASTCALL zend_vm_set_opcode_handler_ex(zend_op* op, uint32_t if (op->op1_type == IS_CONST && op->op2_type == IS_CONST) { break; } - spec = 2811 | SPEC_RULE_OP1 | SPEC_RULE_OP2 | SPEC_RULE_SMART_BRANCH | SPEC_RULE_COMMUTATIVE; + spec = 2820 | SPEC_RULE_OP1 | SPEC_RULE_OP2 | SPEC_RULE_SMART_BRANCH | SPEC_RULE_COMMUTATIVE; } else if (op1_info == MAY_BE_DOUBLE && op2_info == MAY_BE_DOUBLE) { if (op->op1_type == IS_CONST && op->op2_type == IS_CONST) { break; } - spec = 2886 | SPEC_RULE_OP1 | SPEC_RULE_OP2 | SPEC_RULE_SMART_BRANCH | SPEC_RULE_COMMUTATIVE; + spec = 2895 | SPEC_RULE_OP1 | SPEC_RULE_OP2 | SPEC_RULE_SMART_BRANCH | SPEC_RULE_COMMUTATIVE; } break; case ZEND_IS_NOT_EQUAL: @@ -68447,12 +68869,12 @@ ZEND_API void ZEND_FASTCALL zend_vm_set_opcode_handler_ex(zend_op* op, uint32_t if (op->op1_type == IS_CONST && op->op2_type == IS_CONST) { break; } - spec = 2961 | SPEC_RULE_OP1 | SPEC_RULE_OP2 | SPEC_RULE_SMART_BRANCH | SPEC_RULE_COMMUTATIVE; + spec = 2970 | SPEC_RULE_OP1 | SPEC_RULE_OP2 | SPEC_RULE_SMART_BRANCH | SPEC_RULE_COMMUTATIVE; } else if (op1_info == MAY_BE_DOUBLE && op2_info == MAY_BE_DOUBLE) { if (op->op1_type == IS_CONST && op->op2_type == IS_CONST) { break; } - spec = 3036 | SPEC_RULE_OP1 | SPEC_RULE_OP2 | SPEC_RULE_SMART_BRANCH | SPEC_RULE_COMMUTATIVE; + spec = 3045 | SPEC_RULE_OP1 | SPEC_RULE_OP2 | SPEC_RULE_SMART_BRANCH | SPEC_RULE_COMMUTATIVE; } break; case ZEND_IS_SMALLER: @@ -68460,12 +68882,12 @@ ZEND_API void ZEND_FASTCALL zend_vm_set_opcode_handler_ex(zend_op* op, uint32_t if (op->op1_type == IS_CONST && op->op2_type == IS_CONST) { break; } - spec = 3127 | SPEC_RULE_OP1 | SPEC_RULE_OP2 | SPEC_RULE_SMART_BRANCH; + spec = 3136 | SPEC_RULE_OP1 | SPEC_RULE_OP2 | SPEC_RULE_SMART_BRANCH; } else if (op1_info == MAY_BE_DOUBLE && op2_info == MAY_BE_DOUBLE) { if (op->op1_type == IS_CONST && op->op2_type == IS_CONST) { break; } - spec = 3202 | SPEC_RULE_OP1 | SPEC_RULE_OP2 | SPEC_RULE_SMART_BRANCH; + spec = 3211 | SPEC_RULE_OP1 | SPEC_RULE_OP2 | SPEC_RULE_SMART_BRANCH; } break; case ZEND_IS_SMALLER_OR_EQUAL: @@ -68473,79 +68895,79 @@ ZEND_API void ZEND_FASTCALL zend_vm_set_opcode_handler_ex(zend_op* op, uint32_t if (op->op1_type == IS_CONST && op->op2_type == IS_CONST) { break; } - spec = 3277 | SPEC_RULE_OP1 | SPEC_RULE_OP2 | SPEC_RULE_SMART_BRANCH; + spec = 3286 | SPEC_RULE_OP1 | SPEC_RULE_OP2 | SPEC_RULE_SMART_BRANCH; } else if (op1_info == MAY_BE_DOUBLE && op2_info == MAY_BE_DOUBLE) { if (op->op1_type == IS_CONST && op->op2_type == IS_CONST) { break; } - spec = 3352 | SPEC_RULE_OP1 | SPEC_RULE_OP2 | SPEC_RULE_SMART_BRANCH; + spec = 3361 | SPEC_RULE_OP1 | SPEC_RULE_OP2 | SPEC_RULE_SMART_BRANCH; } break; case ZEND_QM_ASSIGN: if (op1_info == MAY_BE_LONG) { - spec = 3439 | SPEC_RULE_OP1; + spec = 3448 | SPEC_RULE_OP1; } else if (op1_info == MAY_BE_DOUBLE) { - spec = 3444 | SPEC_RULE_OP1; + spec = 3453 | SPEC_RULE_OP1; } else if ((op->op1_type == IS_CONST) ? !Z_REFCOUNTED_P(RT_CONSTANT(op, op->op1)) : (!(op1_info & ((MAY_BE_ANY|MAY_BE_UNDEF)-(MAY_BE_NULL|MAY_BE_FALSE|MAY_BE_TRUE|MAY_BE_LONG|MAY_BE_DOUBLE))))) { - spec = 3449 | SPEC_RULE_OP1; + spec = 3458 | SPEC_RULE_OP1; } break; case ZEND_PRE_INC: if (res_info == MAY_BE_LONG && op1_info == MAY_BE_LONG) { - spec = 3427 | SPEC_RULE_RETVAL; + spec = 3436 | SPEC_RULE_RETVAL; } else if (op1_info == MAY_BE_LONG) { - spec = 3429 | SPEC_RULE_RETVAL; + spec = 3438 | SPEC_RULE_RETVAL; } break; case ZEND_PRE_DEC: if (res_info == MAY_BE_LONG && op1_info == MAY_BE_LONG) { - spec = 3431 | SPEC_RULE_RETVAL; + spec = 3440 | SPEC_RULE_RETVAL; } else if (op1_info == MAY_BE_LONG) { - spec = 3433 | SPEC_RULE_RETVAL; + spec = 3442 | SPEC_RULE_RETVAL; } break; case ZEND_POST_INC: if (res_info == MAY_BE_LONG && op1_info == MAY_BE_LONG) { - spec = 3435; + spec = 3444; } else if (op1_info == MAY_BE_LONG) { - spec = 3436; + spec = 3445; } break; case ZEND_POST_DEC: if (res_info == MAY_BE_LONG && op1_info == MAY_BE_LONG) { - spec = 3437; + spec = 3446; } else if (op1_info == MAY_BE_LONG) { - spec = 3438; + spec = 3447; } break; case ZEND_JMP: if (OP_JMP_ADDR(op, op->op1) > op) { - spec = 2585; + spec = 2594; } break; case ZEND_INIT_FCALL: if (Z_EXTRA_P(RT_CONSTANT(op, op->op2)) != 0) { - spec = 2578; + spec = 2587; } break; case ZEND_RECV: if (op->op2.num == MAY_BE_ANY) { - spec = 2579; + spec = 2588; } break; case ZEND_SEND_VAL: if (op->op1_type == IS_CONST && op->op2_type == IS_UNUSED && !Z_REFCOUNTED_P(RT_CONSTANT(op, op->op1))) { - spec = 3489; + spec = 3498; } break; case ZEND_SEND_VAR_EX: if (op->op2_type == IS_UNUSED && op->op2.num <= MAX_ARG_FLAG_NUM && (op1_info & (MAY_BE_UNDEF|MAY_BE_REF)) == 0) { - spec = 3484 | SPEC_RULE_OP1; + spec = 3493 | SPEC_RULE_OP1; } break; case ZEND_FE_FETCH_R: if (op->op2_type == IS_CV && (op1_info & (MAY_BE_ANY|MAY_BE_REF)) == MAY_BE_ARRAY) { - spec = 3491 | SPEC_RULE_RETVAL; + spec = 3500 | SPEC_RULE_RETVAL; } break; case ZEND_FETCH_DIM_R: @@ -68553,22 +68975,22 @@ ZEND_API void ZEND_FASTCALL zend_vm_set_opcode_handler_ex(zend_op* op, uint32_t if (op->op1_type == IS_CONST && op->op2_type == IS_CONST) { break; } - spec = 3454 | SPEC_RULE_OP1 | SPEC_RULE_OP2; + spec = 3463 | SPEC_RULE_OP1 | SPEC_RULE_OP2; } break; case ZEND_SEND_VAL_EX: if (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))) { - spec = 3490; + spec = 3499; } break; case ZEND_SEND_VAR: if (op->op2_type == IS_UNUSED && (op1_info & (MAY_BE_UNDEF|MAY_BE_REF)) == 0) { - spec = 3479 | SPEC_RULE_OP1; + spec = 3488 | SPEC_RULE_OP1; } break; case ZEND_COUNT: if ((op1_info & (MAY_BE_ANY|MAY_BE_UNDEF|MAY_BE_REF)) == MAY_BE_ARRAY) { - spec = 2580 | SPEC_RULE_OP1; + spec = 2589 | SPEC_RULE_OP1; } break; case ZEND_BW_OR: diff --git a/Zend/zend_vm_handlers.h b/Zend/zend_vm_handlers.h index 33d951141550e..1e93199559f8d 100644 --- a/Zend/zend_vm_handlers.h +++ b/Zend/zend_vm_handlers.h @@ -1373,508 +1373,514 @@ _(2575, ZEND_JMP_FRAMELESS_SPEC_CONST) \ _(2576, ZEND_INIT_PARENT_PROPERTY_HOOK_CALL_SPEC_CONST_UNUSED) \ _(2577, ZEND_DECLARE_ATTRIBUTED_CONST_SPEC_CONST_CONST) \ - _(2578, ZEND_INIT_FCALL_OFFSET_SPEC_CONST) \ - _(2579, ZEND_RECV_NOTYPE_SPEC) \ - _(2581, ZEND_COUNT_ARRAY_SPEC_TMPVAR_UNUSED) \ - _(2582, ZEND_COUNT_ARRAY_SPEC_TMPVAR_UNUSED) \ - _(2584, ZEND_COUNT_ARRAY_SPEC_CV_UNUSED) \ - _(2585, ZEND_JMP_FORWARD_SPEC) \ - _(2591, ZEND_ADD_LONG_NO_OVERFLOW_SPEC_TMPVARCV_CONST) \ - _(2592, ZEND_ADD_LONG_NO_OVERFLOW_SPEC_TMPVARCV_TMPVARCV) \ - _(2593, ZEND_ADD_LONG_NO_OVERFLOW_SPEC_TMPVARCV_TMPVARCV) \ - _(2595, ZEND_ADD_LONG_NO_OVERFLOW_SPEC_TMPVARCV_TMPVARCV) \ - _(2596, ZEND_ADD_LONG_NO_OVERFLOW_SPEC_TMPVARCV_CONST) \ - _(2597, ZEND_ADD_LONG_NO_OVERFLOW_SPEC_TMPVARCV_TMPVARCV) \ - _(2598, ZEND_ADD_LONG_NO_OVERFLOW_SPEC_TMPVARCV_TMPVARCV) \ - _(2600, ZEND_ADD_LONG_NO_OVERFLOW_SPEC_TMPVARCV_TMPVARCV) \ - _(2606, ZEND_ADD_LONG_NO_OVERFLOW_SPEC_TMPVARCV_CONST) \ + _(2578, ZEND_SEND_PLACEHOLDER_SPEC_UNUSED_CONST) \ + _(2581, ZEND_SEND_PLACEHOLDER_SPEC_UNUSED_UNUSED) \ + _(2583, ZEND_CALLABLE_CONVERT_PARTIAL_SPEC_UNUSED) \ + _(2584, ZEND_CHECK_PARTIAL_ARGS_SPEC_UNUSED_UNUSED) \ + _(2585, ZEND_CALL_PARTIAL_SPEC) \ + _(2586, ZEND_CALL_PARTIAL_SPEC_OBSERVER) \ + _(2587, ZEND_INIT_FCALL_OFFSET_SPEC_CONST) \ + _(2588, ZEND_RECV_NOTYPE_SPEC) \ + _(2590, ZEND_COUNT_ARRAY_SPEC_TMPVAR_UNUSED) \ + _(2591, ZEND_COUNT_ARRAY_SPEC_TMPVAR_UNUSED) \ + _(2593, ZEND_COUNT_ARRAY_SPEC_CV_UNUSED) \ + _(2594, ZEND_JMP_FORWARD_SPEC) \ + _(2600, ZEND_ADD_LONG_NO_OVERFLOW_SPEC_TMPVARCV_CONST) \ + _(2601, ZEND_ADD_LONG_NO_OVERFLOW_SPEC_TMPVARCV_TMPVARCV) \ + _(2602, ZEND_ADD_LONG_NO_OVERFLOW_SPEC_TMPVARCV_TMPVARCV) \ + _(2604, ZEND_ADD_LONG_NO_OVERFLOW_SPEC_TMPVARCV_TMPVARCV) \ + _(2605, ZEND_ADD_LONG_NO_OVERFLOW_SPEC_TMPVARCV_CONST) \ + _(2606, ZEND_ADD_LONG_NO_OVERFLOW_SPEC_TMPVARCV_TMPVARCV) \ _(2607, ZEND_ADD_LONG_NO_OVERFLOW_SPEC_TMPVARCV_TMPVARCV) \ - _(2608, ZEND_ADD_LONG_NO_OVERFLOW_SPEC_TMPVARCV_TMPVARCV) \ - _(2610, ZEND_ADD_LONG_NO_OVERFLOW_SPEC_TMPVARCV_TMPVARCV) \ - _(2616, ZEND_ADD_LONG_SPEC_TMPVARCV_CONST) \ - _(2617, ZEND_ADD_LONG_SPEC_TMPVARCV_TMPVARCV) \ - _(2618, ZEND_ADD_LONG_SPEC_TMPVARCV_TMPVARCV) \ - _(2620, ZEND_ADD_LONG_SPEC_TMPVARCV_TMPVARCV) \ - _(2621, ZEND_ADD_LONG_SPEC_TMPVARCV_CONST) \ - _(2622, ZEND_ADD_LONG_SPEC_TMPVARCV_TMPVARCV) \ - _(2623, ZEND_ADD_LONG_SPEC_TMPVARCV_TMPVARCV) \ - _(2625, ZEND_ADD_LONG_SPEC_TMPVARCV_TMPVARCV) \ - _(2631, ZEND_ADD_LONG_SPEC_TMPVARCV_CONST) \ + _(2609, ZEND_ADD_LONG_NO_OVERFLOW_SPEC_TMPVARCV_TMPVARCV) \ + _(2615, ZEND_ADD_LONG_NO_OVERFLOW_SPEC_TMPVARCV_CONST) \ + _(2616, ZEND_ADD_LONG_NO_OVERFLOW_SPEC_TMPVARCV_TMPVARCV) \ + _(2617, ZEND_ADD_LONG_NO_OVERFLOW_SPEC_TMPVARCV_TMPVARCV) \ + _(2619, ZEND_ADD_LONG_NO_OVERFLOW_SPEC_TMPVARCV_TMPVARCV) \ + _(2625, ZEND_ADD_LONG_SPEC_TMPVARCV_CONST) \ + _(2626, ZEND_ADD_LONG_SPEC_TMPVARCV_TMPVARCV) \ + _(2627, ZEND_ADD_LONG_SPEC_TMPVARCV_TMPVARCV) \ + _(2629, ZEND_ADD_LONG_SPEC_TMPVARCV_TMPVARCV) \ + _(2630, ZEND_ADD_LONG_SPEC_TMPVARCV_CONST) \ + _(2631, ZEND_ADD_LONG_SPEC_TMPVARCV_TMPVARCV) \ _(2632, ZEND_ADD_LONG_SPEC_TMPVARCV_TMPVARCV) \ - _(2633, ZEND_ADD_LONG_SPEC_TMPVARCV_TMPVARCV) \ - _(2635, ZEND_ADD_LONG_SPEC_TMPVARCV_TMPVARCV) \ - _(2641, ZEND_ADD_DOUBLE_SPEC_TMPVARCV_CONST) \ - _(2642, ZEND_ADD_DOUBLE_SPEC_TMPVARCV_TMPVARCV) \ - _(2643, ZEND_ADD_DOUBLE_SPEC_TMPVARCV_TMPVARCV) \ - _(2645, ZEND_ADD_DOUBLE_SPEC_TMPVARCV_TMPVARCV) \ - _(2646, ZEND_ADD_DOUBLE_SPEC_TMPVARCV_CONST) \ - _(2647, ZEND_ADD_DOUBLE_SPEC_TMPVARCV_TMPVARCV) \ - _(2648, ZEND_ADD_DOUBLE_SPEC_TMPVARCV_TMPVARCV) \ - _(2650, ZEND_ADD_DOUBLE_SPEC_TMPVARCV_TMPVARCV) \ - _(2656, ZEND_ADD_DOUBLE_SPEC_TMPVARCV_CONST) \ + _(2634, ZEND_ADD_LONG_SPEC_TMPVARCV_TMPVARCV) \ + _(2640, ZEND_ADD_LONG_SPEC_TMPVARCV_CONST) \ + _(2641, ZEND_ADD_LONG_SPEC_TMPVARCV_TMPVARCV) \ + _(2642, ZEND_ADD_LONG_SPEC_TMPVARCV_TMPVARCV) \ + _(2644, ZEND_ADD_LONG_SPEC_TMPVARCV_TMPVARCV) \ + _(2650, ZEND_ADD_DOUBLE_SPEC_TMPVARCV_CONST) \ + _(2651, ZEND_ADD_DOUBLE_SPEC_TMPVARCV_TMPVARCV) \ + _(2652, ZEND_ADD_DOUBLE_SPEC_TMPVARCV_TMPVARCV) \ + _(2654, ZEND_ADD_DOUBLE_SPEC_TMPVARCV_TMPVARCV) \ + _(2655, ZEND_ADD_DOUBLE_SPEC_TMPVARCV_CONST) \ + _(2656, ZEND_ADD_DOUBLE_SPEC_TMPVARCV_TMPVARCV) \ _(2657, ZEND_ADD_DOUBLE_SPEC_TMPVARCV_TMPVARCV) \ - _(2658, ZEND_ADD_DOUBLE_SPEC_TMPVARCV_TMPVARCV) \ - _(2660, ZEND_ADD_DOUBLE_SPEC_TMPVARCV_TMPVARCV) \ - _(2662, ZEND_SUB_LONG_NO_OVERFLOW_SPEC_CONST_TMPVARCV) \ - _(2663, ZEND_SUB_LONG_NO_OVERFLOW_SPEC_CONST_TMPVARCV) \ - _(2665, ZEND_SUB_LONG_NO_OVERFLOW_SPEC_CONST_TMPVARCV) \ - _(2666, ZEND_SUB_LONG_NO_OVERFLOW_SPEC_TMPVARCV_CONST) \ - _(2667, ZEND_SUB_LONG_NO_OVERFLOW_SPEC_TMPVARCV_TMPVARCV) \ - _(2668, ZEND_SUB_LONG_NO_OVERFLOW_SPEC_TMPVARCV_TMPVARCV) \ - _(2670, ZEND_SUB_LONG_NO_OVERFLOW_SPEC_TMPVARCV_TMPVARCV) \ - _(2671, ZEND_SUB_LONG_NO_OVERFLOW_SPEC_TMPVARCV_CONST) \ - _(2672, ZEND_SUB_LONG_NO_OVERFLOW_SPEC_TMPVARCV_TMPVARCV) \ - _(2673, ZEND_SUB_LONG_NO_OVERFLOW_SPEC_TMPVARCV_TMPVARCV) \ - _(2675, ZEND_SUB_LONG_NO_OVERFLOW_SPEC_TMPVARCV_TMPVARCV) \ - _(2681, ZEND_SUB_LONG_NO_OVERFLOW_SPEC_TMPVARCV_CONST) \ + _(2659, ZEND_ADD_DOUBLE_SPEC_TMPVARCV_TMPVARCV) \ + _(2665, ZEND_ADD_DOUBLE_SPEC_TMPVARCV_CONST) \ + _(2666, ZEND_ADD_DOUBLE_SPEC_TMPVARCV_TMPVARCV) \ + _(2667, ZEND_ADD_DOUBLE_SPEC_TMPVARCV_TMPVARCV) \ + _(2669, ZEND_ADD_DOUBLE_SPEC_TMPVARCV_TMPVARCV) \ + _(2671, ZEND_SUB_LONG_NO_OVERFLOW_SPEC_CONST_TMPVARCV) \ + _(2672, ZEND_SUB_LONG_NO_OVERFLOW_SPEC_CONST_TMPVARCV) \ + _(2674, ZEND_SUB_LONG_NO_OVERFLOW_SPEC_CONST_TMPVARCV) \ + _(2675, ZEND_SUB_LONG_NO_OVERFLOW_SPEC_TMPVARCV_CONST) \ + _(2676, ZEND_SUB_LONG_NO_OVERFLOW_SPEC_TMPVARCV_TMPVARCV) \ + _(2677, ZEND_SUB_LONG_NO_OVERFLOW_SPEC_TMPVARCV_TMPVARCV) \ + _(2679, ZEND_SUB_LONG_NO_OVERFLOW_SPEC_TMPVARCV_TMPVARCV) \ + _(2680, ZEND_SUB_LONG_NO_OVERFLOW_SPEC_TMPVARCV_CONST) \ + _(2681, ZEND_SUB_LONG_NO_OVERFLOW_SPEC_TMPVARCV_TMPVARCV) \ _(2682, ZEND_SUB_LONG_NO_OVERFLOW_SPEC_TMPVARCV_TMPVARCV) \ - _(2683, ZEND_SUB_LONG_NO_OVERFLOW_SPEC_TMPVARCV_TMPVARCV) \ - _(2685, ZEND_SUB_LONG_NO_OVERFLOW_SPEC_TMPVARCV_TMPVARCV) \ - _(2687, ZEND_SUB_LONG_SPEC_CONST_TMPVARCV) \ - _(2688, ZEND_SUB_LONG_SPEC_CONST_TMPVARCV) \ - _(2690, ZEND_SUB_LONG_SPEC_CONST_TMPVARCV) \ - _(2691, ZEND_SUB_LONG_SPEC_TMPVARCV_CONST) \ - _(2692, ZEND_SUB_LONG_SPEC_TMPVARCV_TMPVARCV) \ - _(2693, ZEND_SUB_LONG_SPEC_TMPVARCV_TMPVARCV) \ - _(2695, ZEND_SUB_LONG_SPEC_TMPVARCV_TMPVARCV) \ - _(2696, ZEND_SUB_LONG_SPEC_TMPVARCV_CONST) \ - _(2697, ZEND_SUB_LONG_SPEC_TMPVARCV_TMPVARCV) \ - _(2698, ZEND_SUB_LONG_SPEC_TMPVARCV_TMPVARCV) \ - _(2700, ZEND_SUB_LONG_SPEC_TMPVARCV_TMPVARCV) \ - _(2706, ZEND_SUB_LONG_SPEC_TMPVARCV_CONST) \ + _(2684, ZEND_SUB_LONG_NO_OVERFLOW_SPEC_TMPVARCV_TMPVARCV) \ + _(2690, ZEND_SUB_LONG_NO_OVERFLOW_SPEC_TMPVARCV_CONST) \ + _(2691, ZEND_SUB_LONG_NO_OVERFLOW_SPEC_TMPVARCV_TMPVARCV) \ + _(2692, ZEND_SUB_LONG_NO_OVERFLOW_SPEC_TMPVARCV_TMPVARCV) \ + _(2694, ZEND_SUB_LONG_NO_OVERFLOW_SPEC_TMPVARCV_TMPVARCV) \ + _(2696, ZEND_SUB_LONG_SPEC_CONST_TMPVARCV) \ + _(2697, ZEND_SUB_LONG_SPEC_CONST_TMPVARCV) \ + _(2699, ZEND_SUB_LONG_SPEC_CONST_TMPVARCV) \ + _(2700, ZEND_SUB_LONG_SPEC_TMPVARCV_CONST) \ + _(2701, ZEND_SUB_LONG_SPEC_TMPVARCV_TMPVARCV) \ + _(2702, ZEND_SUB_LONG_SPEC_TMPVARCV_TMPVARCV) \ + _(2704, ZEND_SUB_LONG_SPEC_TMPVARCV_TMPVARCV) \ + _(2705, ZEND_SUB_LONG_SPEC_TMPVARCV_CONST) \ + _(2706, ZEND_SUB_LONG_SPEC_TMPVARCV_TMPVARCV) \ _(2707, ZEND_SUB_LONG_SPEC_TMPVARCV_TMPVARCV) \ - _(2708, ZEND_SUB_LONG_SPEC_TMPVARCV_TMPVARCV) \ - _(2710, ZEND_SUB_LONG_SPEC_TMPVARCV_TMPVARCV) \ - _(2712, ZEND_SUB_DOUBLE_SPEC_CONST_TMPVARCV) \ - _(2713, ZEND_SUB_DOUBLE_SPEC_CONST_TMPVARCV) \ - _(2715, ZEND_SUB_DOUBLE_SPEC_CONST_TMPVARCV) \ - _(2716, ZEND_SUB_DOUBLE_SPEC_TMPVARCV_CONST) \ - _(2717, ZEND_SUB_DOUBLE_SPEC_TMPVARCV_TMPVARCV) \ - _(2718, ZEND_SUB_DOUBLE_SPEC_TMPVARCV_TMPVARCV) \ - _(2720, ZEND_SUB_DOUBLE_SPEC_TMPVARCV_TMPVARCV) \ - _(2721, ZEND_SUB_DOUBLE_SPEC_TMPVARCV_CONST) \ - _(2722, ZEND_SUB_DOUBLE_SPEC_TMPVARCV_TMPVARCV) \ - _(2723, ZEND_SUB_DOUBLE_SPEC_TMPVARCV_TMPVARCV) \ - _(2725, ZEND_SUB_DOUBLE_SPEC_TMPVARCV_TMPVARCV) \ - _(2731, ZEND_SUB_DOUBLE_SPEC_TMPVARCV_CONST) \ + _(2709, ZEND_SUB_LONG_SPEC_TMPVARCV_TMPVARCV) \ + _(2715, ZEND_SUB_LONG_SPEC_TMPVARCV_CONST) \ + _(2716, ZEND_SUB_LONG_SPEC_TMPVARCV_TMPVARCV) \ + _(2717, ZEND_SUB_LONG_SPEC_TMPVARCV_TMPVARCV) \ + _(2719, ZEND_SUB_LONG_SPEC_TMPVARCV_TMPVARCV) \ + _(2721, ZEND_SUB_DOUBLE_SPEC_CONST_TMPVARCV) \ + _(2722, ZEND_SUB_DOUBLE_SPEC_CONST_TMPVARCV) \ + _(2724, ZEND_SUB_DOUBLE_SPEC_CONST_TMPVARCV) \ + _(2725, ZEND_SUB_DOUBLE_SPEC_TMPVARCV_CONST) \ + _(2726, ZEND_SUB_DOUBLE_SPEC_TMPVARCV_TMPVARCV) \ + _(2727, ZEND_SUB_DOUBLE_SPEC_TMPVARCV_TMPVARCV) \ + _(2729, ZEND_SUB_DOUBLE_SPEC_TMPVARCV_TMPVARCV) \ + _(2730, ZEND_SUB_DOUBLE_SPEC_TMPVARCV_CONST) \ + _(2731, ZEND_SUB_DOUBLE_SPEC_TMPVARCV_TMPVARCV) \ _(2732, ZEND_SUB_DOUBLE_SPEC_TMPVARCV_TMPVARCV) \ - _(2733, ZEND_SUB_DOUBLE_SPEC_TMPVARCV_TMPVARCV) \ - _(2735, ZEND_SUB_DOUBLE_SPEC_TMPVARCV_TMPVARCV) \ - _(2741, ZEND_MUL_LONG_NO_OVERFLOW_SPEC_TMPVARCV_CONST) \ - _(2742, ZEND_MUL_LONG_NO_OVERFLOW_SPEC_TMPVARCV_TMPVARCV) \ - _(2743, ZEND_MUL_LONG_NO_OVERFLOW_SPEC_TMPVARCV_TMPVARCV) \ - _(2745, ZEND_MUL_LONG_NO_OVERFLOW_SPEC_TMPVARCV_TMPVARCV) \ - _(2746, ZEND_MUL_LONG_NO_OVERFLOW_SPEC_TMPVARCV_CONST) \ - _(2747, ZEND_MUL_LONG_NO_OVERFLOW_SPEC_TMPVARCV_TMPVARCV) \ - _(2748, ZEND_MUL_LONG_NO_OVERFLOW_SPEC_TMPVARCV_TMPVARCV) \ - _(2750, ZEND_MUL_LONG_NO_OVERFLOW_SPEC_TMPVARCV_TMPVARCV) \ - _(2756, ZEND_MUL_LONG_NO_OVERFLOW_SPEC_TMPVARCV_CONST) \ + _(2734, ZEND_SUB_DOUBLE_SPEC_TMPVARCV_TMPVARCV) \ + _(2740, ZEND_SUB_DOUBLE_SPEC_TMPVARCV_CONST) \ + _(2741, ZEND_SUB_DOUBLE_SPEC_TMPVARCV_TMPVARCV) \ + _(2742, ZEND_SUB_DOUBLE_SPEC_TMPVARCV_TMPVARCV) \ + _(2744, ZEND_SUB_DOUBLE_SPEC_TMPVARCV_TMPVARCV) \ + _(2750, ZEND_MUL_LONG_NO_OVERFLOW_SPEC_TMPVARCV_CONST) \ + _(2751, ZEND_MUL_LONG_NO_OVERFLOW_SPEC_TMPVARCV_TMPVARCV) \ + _(2752, ZEND_MUL_LONG_NO_OVERFLOW_SPEC_TMPVARCV_TMPVARCV) \ + _(2754, ZEND_MUL_LONG_NO_OVERFLOW_SPEC_TMPVARCV_TMPVARCV) \ + _(2755, ZEND_MUL_LONG_NO_OVERFLOW_SPEC_TMPVARCV_CONST) \ + _(2756, ZEND_MUL_LONG_NO_OVERFLOW_SPEC_TMPVARCV_TMPVARCV) \ _(2757, ZEND_MUL_LONG_NO_OVERFLOW_SPEC_TMPVARCV_TMPVARCV) \ - _(2758, ZEND_MUL_LONG_NO_OVERFLOW_SPEC_TMPVARCV_TMPVARCV) \ - _(2760, ZEND_MUL_LONG_NO_OVERFLOW_SPEC_TMPVARCV_TMPVARCV) \ - _(2766, ZEND_MUL_LONG_SPEC_TMPVARCV_CONST) \ - _(2767, ZEND_MUL_LONG_SPEC_TMPVARCV_TMPVARCV) \ - _(2768, ZEND_MUL_LONG_SPEC_TMPVARCV_TMPVARCV) \ - _(2770, ZEND_MUL_LONG_SPEC_TMPVARCV_TMPVARCV) \ - _(2771, ZEND_MUL_LONG_SPEC_TMPVARCV_CONST) \ - _(2772, ZEND_MUL_LONG_SPEC_TMPVARCV_TMPVARCV) \ - _(2773, ZEND_MUL_LONG_SPEC_TMPVARCV_TMPVARCV) \ - _(2775, ZEND_MUL_LONG_SPEC_TMPVARCV_TMPVARCV) \ - _(2781, ZEND_MUL_LONG_SPEC_TMPVARCV_CONST) \ + _(2759, ZEND_MUL_LONG_NO_OVERFLOW_SPEC_TMPVARCV_TMPVARCV) \ + _(2765, ZEND_MUL_LONG_NO_OVERFLOW_SPEC_TMPVARCV_CONST) \ + _(2766, ZEND_MUL_LONG_NO_OVERFLOW_SPEC_TMPVARCV_TMPVARCV) \ + _(2767, ZEND_MUL_LONG_NO_OVERFLOW_SPEC_TMPVARCV_TMPVARCV) \ + _(2769, ZEND_MUL_LONG_NO_OVERFLOW_SPEC_TMPVARCV_TMPVARCV) \ + _(2775, ZEND_MUL_LONG_SPEC_TMPVARCV_CONST) \ + _(2776, ZEND_MUL_LONG_SPEC_TMPVARCV_TMPVARCV) \ + _(2777, ZEND_MUL_LONG_SPEC_TMPVARCV_TMPVARCV) \ + _(2779, ZEND_MUL_LONG_SPEC_TMPVARCV_TMPVARCV) \ + _(2780, ZEND_MUL_LONG_SPEC_TMPVARCV_CONST) \ + _(2781, ZEND_MUL_LONG_SPEC_TMPVARCV_TMPVARCV) \ _(2782, ZEND_MUL_LONG_SPEC_TMPVARCV_TMPVARCV) \ - _(2783, ZEND_MUL_LONG_SPEC_TMPVARCV_TMPVARCV) \ - _(2785, ZEND_MUL_LONG_SPEC_TMPVARCV_TMPVARCV) \ - _(2791, ZEND_MUL_DOUBLE_SPEC_TMPVARCV_CONST) \ - _(2792, ZEND_MUL_DOUBLE_SPEC_TMPVARCV_TMPVARCV) \ - _(2793, ZEND_MUL_DOUBLE_SPEC_TMPVARCV_TMPVARCV) \ - _(2795, ZEND_MUL_DOUBLE_SPEC_TMPVARCV_TMPVARCV) \ - _(2796, ZEND_MUL_DOUBLE_SPEC_TMPVARCV_CONST) \ - _(2797, ZEND_MUL_DOUBLE_SPEC_TMPVARCV_TMPVARCV) \ - _(2798, ZEND_MUL_DOUBLE_SPEC_TMPVARCV_TMPVARCV) \ - _(2800, ZEND_MUL_DOUBLE_SPEC_TMPVARCV_TMPVARCV) \ - _(2806, ZEND_MUL_DOUBLE_SPEC_TMPVARCV_CONST) \ + _(2784, ZEND_MUL_LONG_SPEC_TMPVARCV_TMPVARCV) \ + _(2790, ZEND_MUL_LONG_SPEC_TMPVARCV_CONST) \ + _(2791, ZEND_MUL_LONG_SPEC_TMPVARCV_TMPVARCV) \ + _(2792, ZEND_MUL_LONG_SPEC_TMPVARCV_TMPVARCV) \ + _(2794, ZEND_MUL_LONG_SPEC_TMPVARCV_TMPVARCV) \ + _(2800, ZEND_MUL_DOUBLE_SPEC_TMPVARCV_CONST) \ + _(2801, ZEND_MUL_DOUBLE_SPEC_TMPVARCV_TMPVARCV) \ + _(2802, ZEND_MUL_DOUBLE_SPEC_TMPVARCV_TMPVARCV) \ + _(2804, ZEND_MUL_DOUBLE_SPEC_TMPVARCV_TMPVARCV) \ + _(2805, ZEND_MUL_DOUBLE_SPEC_TMPVARCV_CONST) \ + _(2806, ZEND_MUL_DOUBLE_SPEC_TMPVARCV_TMPVARCV) \ _(2807, ZEND_MUL_DOUBLE_SPEC_TMPVARCV_TMPVARCV) \ - _(2808, ZEND_MUL_DOUBLE_SPEC_TMPVARCV_TMPVARCV) \ - _(2810, ZEND_MUL_DOUBLE_SPEC_TMPVARCV_TMPVARCV) \ - _(2826, ZEND_IS_EQUAL_LONG_SPEC_TMPVARCV_CONST) \ - _(2827, ZEND_IS_EQUAL_LONG_SPEC_TMPVARCV_CONST_JMPZ) \ - _(2828, ZEND_IS_EQUAL_LONG_SPEC_TMPVARCV_CONST_JMPNZ) \ - _(2829, ZEND_IS_EQUAL_LONG_SPEC_TMPVARCV_TMPVARCV) \ - _(2830, ZEND_IS_EQUAL_LONG_SPEC_TMPVARCV_TMPVARCV_JMPZ) \ - _(2831, ZEND_IS_EQUAL_LONG_SPEC_TMPVARCV_TMPVARCV_JMPNZ) \ - _(2832, ZEND_IS_EQUAL_LONG_SPEC_TMPVARCV_TMPVARCV) \ - _(2833, ZEND_IS_EQUAL_LONG_SPEC_TMPVARCV_TMPVARCV_JMPZ) \ - _(2834, ZEND_IS_EQUAL_LONG_SPEC_TMPVARCV_TMPVARCV_JMPNZ) \ + _(2809, ZEND_MUL_DOUBLE_SPEC_TMPVARCV_TMPVARCV) \ + _(2815, ZEND_MUL_DOUBLE_SPEC_TMPVARCV_CONST) \ + _(2816, ZEND_MUL_DOUBLE_SPEC_TMPVARCV_TMPVARCV) \ + _(2817, ZEND_MUL_DOUBLE_SPEC_TMPVARCV_TMPVARCV) \ + _(2819, ZEND_MUL_DOUBLE_SPEC_TMPVARCV_TMPVARCV) \ + _(2835, ZEND_IS_EQUAL_LONG_SPEC_TMPVARCV_CONST) \ + _(2836, ZEND_IS_EQUAL_LONG_SPEC_TMPVARCV_CONST_JMPZ) \ + _(2837, ZEND_IS_EQUAL_LONG_SPEC_TMPVARCV_CONST_JMPNZ) \ _(2838, ZEND_IS_EQUAL_LONG_SPEC_TMPVARCV_TMPVARCV) \ _(2839, ZEND_IS_EQUAL_LONG_SPEC_TMPVARCV_TMPVARCV_JMPZ) \ _(2840, ZEND_IS_EQUAL_LONG_SPEC_TMPVARCV_TMPVARCV_JMPNZ) \ - _(2841, ZEND_IS_EQUAL_LONG_SPEC_TMPVARCV_CONST) \ - _(2842, ZEND_IS_EQUAL_LONG_SPEC_TMPVARCV_CONST_JMPZ) \ - _(2843, ZEND_IS_EQUAL_LONG_SPEC_TMPVARCV_CONST_JMPNZ) \ - _(2844, ZEND_IS_EQUAL_LONG_SPEC_TMPVARCV_TMPVARCV) \ - _(2845, ZEND_IS_EQUAL_LONG_SPEC_TMPVARCV_TMPVARCV_JMPZ) \ - _(2846, ZEND_IS_EQUAL_LONG_SPEC_TMPVARCV_TMPVARCV_JMPNZ) \ + _(2841, ZEND_IS_EQUAL_LONG_SPEC_TMPVARCV_TMPVARCV) \ + _(2842, ZEND_IS_EQUAL_LONG_SPEC_TMPVARCV_TMPVARCV_JMPZ) \ + _(2843, ZEND_IS_EQUAL_LONG_SPEC_TMPVARCV_TMPVARCV_JMPNZ) \ _(2847, ZEND_IS_EQUAL_LONG_SPEC_TMPVARCV_TMPVARCV) \ _(2848, ZEND_IS_EQUAL_LONG_SPEC_TMPVARCV_TMPVARCV_JMPZ) \ _(2849, ZEND_IS_EQUAL_LONG_SPEC_TMPVARCV_TMPVARCV_JMPNZ) \ + _(2850, ZEND_IS_EQUAL_LONG_SPEC_TMPVARCV_CONST) \ + _(2851, ZEND_IS_EQUAL_LONG_SPEC_TMPVARCV_CONST_JMPZ) \ + _(2852, ZEND_IS_EQUAL_LONG_SPEC_TMPVARCV_CONST_JMPNZ) \ _(2853, ZEND_IS_EQUAL_LONG_SPEC_TMPVARCV_TMPVARCV) \ _(2854, ZEND_IS_EQUAL_LONG_SPEC_TMPVARCV_TMPVARCV_JMPZ) \ _(2855, ZEND_IS_EQUAL_LONG_SPEC_TMPVARCV_TMPVARCV_JMPNZ) \ - _(2871, ZEND_IS_EQUAL_LONG_SPEC_TMPVARCV_CONST) \ - _(2872, ZEND_IS_EQUAL_LONG_SPEC_TMPVARCV_CONST_JMPZ) \ - _(2873, ZEND_IS_EQUAL_LONG_SPEC_TMPVARCV_CONST_JMPNZ) \ - _(2874, ZEND_IS_EQUAL_LONG_SPEC_TMPVARCV_TMPVARCV) \ - _(2875, ZEND_IS_EQUAL_LONG_SPEC_TMPVARCV_TMPVARCV_JMPZ) \ - _(2876, ZEND_IS_EQUAL_LONG_SPEC_TMPVARCV_TMPVARCV_JMPNZ) \ - _(2877, ZEND_IS_EQUAL_LONG_SPEC_TMPVARCV_TMPVARCV) \ - _(2878, ZEND_IS_EQUAL_LONG_SPEC_TMPVARCV_TMPVARCV_JMPZ) \ - _(2879, ZEND_IS_EQUAL_LONG_SPEC_TMPVARCV_TMPVARCV_JMPNZ) \ + _(2856, ZEND_IS_EQUAL_LONG_SPEC_TMPVARCV_TMPVARCV) \ + _(2857, ZEND_IS_EQUAL_LONG_SPEC_TMPVARCV_TMPVARCV_JMPZ) \ + _(2858, ZEND_IS_EQUAL_LONG_SPEC_TMPVARCV_TMPVARCV_JMPNZ) \ + _(2862, ZEND_IS_EQUAL_LONG_SPEC_TMPVARCV_TMPVARCV) \ + _(2863, ZEND_IS_EQUAL_LONG_SPEC_TMPVARCV_TMPVARCV_JMPZ) \ + _(2864, ZEND_IS_EQUAL_LONG_SPEC_TMPVARCV_TMPVARCV_JMPNZ) \ + _(2880, ZEND_IS_EQUAL_LONG_SPEC_TMPVARCV_CONST) \ + _(2881, ZEND_IS_EQUAL_LONG_SPEC_TMPVARCV_CONST_JMPZ) \ + _(2882, ZEND_IS_EQUAL_LONG_SPEC_TMPVARCV_CONST_JMPNZ) \ _(2883, ZEND_IS_EQUAL_LONG_SPEC_TMPVARCV_TMPVARCV) \ _(2884, ZEND_IS_EQUAL_LONG_SPEC_TMPVARCV_TMPVARCV_JMPZ) \ _(2885, ZEND_IS_EQUAL_LONG_SPEC_TMPVARCV_TMPVARCV_JMPNZ) \ - _(2901, ZEND_IS_EQUAL_DOUBLE_SPEC_TMPVARCV_CONST) \ - _(2902, ZEND_IS_EQUAL_DOUBLE_SPEC_TMPVARCV_CONST_JMPZ) \ - _(2903, ZEND_IS_EQUAL_DOUBLE_SPEC_TMPVARCV_CONST_JMPNZ) \ - _(2904, ZEND_IS_EQUAL_DOUBLE_SPEC_TMPVARCV_TMPVARCV) \ - _(2905, ZEND_IS_EQUAL_DOUBLE_SPEC_TMPVARCV_TMPVARCV_JMPZ) \ - _(2906, ZEND_IS_EQUAL_DOUBLE_SPEC_TMPVARCV_TMPVARCV_JMPNZ) \ - _(2907, ZEND_IS_EQUAL_DOUBLE_SPEC_TMPVARCV_TMPVARCV) \ - _(2908, ZEND_IS_EQUAL_DOUBLE_SPEC_TMPVARCV_TMPVARCV_JMPZ) \ - _(2909, ZEND_IS_EQUAL_DOUBLE_SPEC_TMPVARCV_TMPVARCV_JMPNZ) \ + _(2886, ZEND_IS_EQUAL_LONG_SPEC_TMPVARCV_TMPVARCV) \ + _(2887, ZEND_IS_EQUAL_LONG_SPEC_TMPVARCV_TMPVARCV_JMPZ) \ + _(2888, ZEND_IS_EQUAL_LONG_SPEC_TMPVARCV_TMPVARCV_JMPNZ) \ + _(2892, ZEND_IS_EQUAL_LONG_SPEC_TMPVARCV_TMPVARCV) \ + _(2893, ZEND_IS_EQUAL_LONG_SPEC_TMPVARCV_TMPVARCV_JMPZ) \ + _(2894, ZEND_IS_EQUAL_LONG_SPEC_TMPVARCV_TMPVARCV_JMPNZ) \ + _(2910, ZEND_IS_EQUAL_DOUBLE_SPEC_TMPVARCV_CONST) \ + _(2911, ZEND_IS_EQUAL_DOUBLE_SPEC_TMPVARCV_CONST_JMPZ) \ + _(2912, ZEND_IS_EQUAL_DOUBLE_SPEC_TMPVARCV_CONST_JMPNZ) \ _(2913, ZEND_IS_EQUAL_DOUBLE_SPEC_TMPVARCV_TMPVARCV) \ _(2914, ZEND_IS_EQUAL_DOUBLE_SPEC_TMPVARCV_TMPVARCV_JMPZ) \ _(2915, ZEND_IS_EQUAL_DOUBLE_SPEC_TMPVARCV_TMPVARCV_JMPNZ) \ - _(2916, ZEND_IS_EQUAL_DOUBLE_SPEC_TMPVARCV_CONST) \ - _(2917, ZEND_IS_EQUAL_DOUBLE_SPEC_TMPVARCV_CONST_JMPZ) \ - _(2918, ZEND_IS_EQUAL_DOUBLE_SPEC_TMPVARCV_CONST_JMPNZ) \ - _(2919, ZEND_IS_EQUAL_DOUBLE_SPEC_TMPVARCV_TMPVARCV) \ - _(2920, ZEND_IS_EQUAL_DOUBLE_SPEC_TMPVARCV_TMPVARCV_JMPZ) \ - _(2921, ZEND_IS_EQUAL_DOUBLE_SPEC_TMPVARCV_TMPVARCV_JMPNZ) \ + _(2916, ZEND_IS_EQUAL_DOUBLE_SPEC_TMPVARCV_TMPVARCV) \ + _(2917, ZEND_IS_EQUAL_DOUBLE_SPEC_TMPVARCV_TMPVARCV_JMPZ) \ + _(2918, ZEND_IS_EQUAL_DOUBLE_SPEC_TMPVARCV_TMPVARCV_JMPNZ) \ _(2922, ZEND_IS_EQUAL_DOUBLE_SPEC_TMPVARCV_TMPVARCV) \ _(2923, ZEND_IS_EQUAL_DOUBLE_SPEC_TMPVARCV_TMPVARCV_JMPZ) \ _(2924, ZEND_IS_EQUAL_DOUBLE_SPEC_TMPVARCV_TMPVARCV_JMPNZ) \ + _(2925, ZEND_IS_EQUAL_DOUBLE_SPEC_TMPVARCV_CONST) \ + _(2926, ZEND_IS_EQUAL_DOUBLE_SPEC_TMPVARCV_CONST_JMPZ) \ + _(2927, ZEND_IS_EQUAL_DOUBLE_SPEC_TMPVARCV_CONST_JMPNZ) \ _(2928, ZEND_IS_EQUAL_DOUBLE_SPEC_TMPVARCV_TMPVARCV) \ _(2929, ZEND_IS_EQUAL_DOUBLE_SPEC_TMPVARCV_TMPVARCV_JMPZ) \ _(2930, ZEND_IS_EQUAL_DOUBLE_SPEC_TMPVARCV_TMPVARCV_JMPNZ) \ - _(2946, ZEND_IS_EQUAL_DOUBLE_SPEC_TMPVARCV_CONST) \ - _(2947, ZEND_IS_EQUAL_DOUBLE_SPEC_TMPVARCV_CONST_JMPZ) \ - _(2948, ZEND_IS_EQUAL_DOUBLE_SPEC_TMPVARCV_CONST_JMPNZ) \ - _(2949, ZEND_IS_EQUAL_DOUBLE_SPEC_TMPVARCV_TMPVARCV) \ - _(2950, ZEND_IS_EQUAL_DOUBLE_SPEC_TMPVARCV_TMPVARCV_JMPZ) \ - _(2951, ZEND_IS_EQUAL_DOUBLE_SPEC_TMPVARCV_TMPVARCV_JMPNZ) \ - _(2952, ZEND_IS_EQUAL_DOUBLE_SPEC_TMPVARCV_TMPVARCV) \ - _(2953, ZEND_IS_EQUAL_DOUBLE_SPEC_TMPVARCV_TMPVARCV_JMPZ) \ - _(2954, ZEND_IS_EQUAL_DOUBLE_SPEC_TMPVARCV_TMPVARCV_JMPNZ) \ + _(2931, ZEND_IS_EQUAL_DOUBLE_SPEC_TMPVARCV_TMPVARCV) \ + _(2932, ZEND_IS_EQUAL_DOUBLE_SPEC_TMPVARCV_TMPVARCV_JMPZ) \ + _(2933, ZEND_IS_EQUAL_DOUBLE_SPEC_TMPVARCV_TMPVARCV_JMPNZ) \ + _(2937, ZEND_IS_EQUAL_DOUBLE_SPEC_TMPVARCV_TMPVARCV) \ + _(2938, ZEND_IS_EQUAL_DOUBLE_SPEC_TMPVARCV_TMPVARCV_JMPZ) \ + _(2939, ZEND_IS_EQUAL_DOUBLE_SPEC_TMPVARCV_TMPVARCV_JMPNZ) \ + _(2955, ZEND_IS_EQUAL_DOUBLE_SPEC_TMPVARCV_CONST) \ + _(2956, ZEND_IS_EQUAL_DOUBLE_SPEC_TMPVARCV_CONST_JMPZ) \ + _(2957, ZEND_IS_EQUAL_DOUBLE_SPEC_TMPVARCV_CONST_JMPNZ) \ _(2958, ZEND_IS_EQUAL_DOUBLE_SPEC_TMPVARCV_TMPVARCV) \ _(2959, ZEND_IS_EQUAL_DOUBLE_SPEC_TMPVARCV_TMPVARCV_JMPZ) \ _(2960, ZEND_IS_EQUAL_DOUBLE_SPEC_TMPVARCV_TMPVARCV_JMPNZ) \ - _(2976, ZEND_IS_NOT_EQUAL_LONG_SPEC_TMPVARCV_CONST) \ - _(2977, ZEND_IS_NOT_EQUAL_LONG_SPEC_TMPVARCV_CONST_JMPZ) \ - _(2978, ZEND_IS_NOT_EQUAL_LONG_SPEC_TMPVARCV_CONST_JMPNZ) \ - _(2979, ZEND_IS_NOT_EQUAL_LONG_SPEC_TMPVARCV_TMPVARCV) \ - _(2980, ZEND_IS_NOT_EQUAL_LONG_SPEC_TMPVARCV_TMPVARCV_JMPZ) \ - _(2981, ZEND_IS_NOT_EQUAL_LONG_SPEC_TMPVARCV_TMPVARCV_JMPNZ) \ - _(2982, ZEND_IS_NOT_EQUAL_LONG_SPEC_TMPVARCV_TMPVARCV) \ - _(2983, ZEND_IS_NOT_EQUAL_LONG_SPEC_TMPVARCV_TMPVARCV_JMPZ) \ - _(2984, ZEND_IS_NOT_EQUAL_LONG_SPEC_TMPVARCV_TMPVARCV_JMPNZ) \ + _(2961, ZEND_IS_EQUAL_DOUBLE_SPEC_TMPVARCV_TMPVARCV) \ + _(2962, ZEND_IS_EQUAL_DOUBLE_SPEC_TMPVARCV_TMPVARCV_JMPZ) \ + _(2963, ZEND_IS_EQUAL_DOUBLE_SPEC_TMPVARCV_TMPVARCV_JMPNZ) \ + _(2967, ZEND_IS_EQUAL_DOUBLE_SPEC_TMPVARCV_TMPVARCV) \ + _(2968, ZEND_IS_EQUAL_DOUBLE_SPEC_TMPVARCV_TMPVARCV_JMPZ) \ + _(2969, ZEND_IS_EQUAL_DOUBLE_SPEC_TMPVARCV_TMPVARCV_JMPNZ) \ + _(2985, ZEND_IS_NOT_EQUAL_LONG_SPEC_TMPVARCV_CONST) \ + _(2986, ZEND_IS_NOT_EQUAL_LONG_SPEC_TMPVARCV_CONST_JMPZ) \ + _(2987, ZEND_IS_NOT_EQUAL_LONG_SPEC_TMPVARCV_CONST_JMPNZ) \ _(2988, ZEND_IS_NOT_EQUAL_LONG_SPEC_TMPVARCV_TMPVARCV) \ _(2989, ZEND_IS_NOT_EQUAL_LONG_SPEC_TMPVARCV_TMPVARCV_JMPZ) \ _(2990, ZEND_IS_NOT_EQUAL_LONG_SPEC_TMPVARCV_TMPVARCV_JMPNZ) \ - _(2991, ZEND_IS_NOT_EQUAL_LONG_SPEC_TMPVARCV_CONST) \ - _(2992, ZEND_IS_NOT_EQUAL_LONG_SPEC_TMPVARCV_CONST_JMPZ) \ - _(2993, ZEND_IS_NOT_EQUAL_LONG_SPEC_TMPVARCV_CONST_JMPNZ) \ - _(2994, ZEND_IS_NOT_EQUAL_LONG_SPEC_TMPVARCV_TMPVARCV) \ - _(2995, ZEND_IS_NOT_EQUAL_LONG_SPEC_TMPVARCV_TMPVARCV_JMPZ) \ - _(2996, ZEND_IS_NOT_EQUAL_LONG_SPEC_TMPVARCV_TMPVARCV_JMPNZ) \ + _(2991, ZEND_IS_NOT_EQUAL_LONG_SPEC_TMPVARCV_TMPVARCV) \ + _(2992, ZEND_IS_NOT_EQUAL_LONG_SPEC_TMPVARCV_TMPVARCV_JMPZ) \ + _(2993, ZEND_IS_NOT_EQUAL_LONG_SPEC_TMPVARCV_TMPVARCV_JMPNZ) \ _(2997, ZEND_IS_NOT_EQUAL_LONG_SPEC_TMPVARCV_TMPVARCV) \ _(2998, ZEND_IS_NOT_EQUAL_LONG_SPEC_TMPVARCV_TMPVARCV_JMPZ) \ _(2999, ZEND_IS_NOT_EQUAL_LONG_SPEC_TMPVARCV_TMPVARCV_JMPNZ) \ + _(3000, ZEND_IS_NOT_EQUAL_LONG_SPEC_TMPVARCV_CONST) \ + _(3001, ZEND_IS_NOT_EQUAL_LONG_SPEC_TMPVARCV_CONST_JMPZ) \ + _(3002, ZEND_IS_NOT_EQUAL_LONG_SPEC_TMPVARCV_CONST_JMPNZ) \ _(3003, ZEND_IS_NOT_EQUAL_LONG_SPEC_TMPVARCV_TMPVARCV) \ _(3004, ZEND_IS_NOT_EQUAL_LONG_SPEC_TMPVARCV_TMPVARCV_JMPZ) \ _(3005, ZEND_IS_NOT_EQUAL_LONG_SPEC_TMPVARCV_TMPVARCV_JMPNZ) \ - _(3021, ZEND_IS_NOT_EQUAL_LONG_SPEC_TMPVARCV_CONST) \ - _(3022, ZEND_IS_NOT_EQUAL_LONG_SPEC_TMPVARCV_CONST_JMPZ) \ - _(3023, ZEND_IS_NOT_EQUAL_LONG_SPEC_TMPVARCV_CONST_JMPNZ) \ - _(3024, ZEND_IS_NOT_EQUAL_LONG_SPEC_TMPVARCV_TMPVARCV) \ - _(3025, ZEND_IS_NOT_EQUAL_LONG_SPEC_TMPVARCV_TMPVARCV_JMPZ) \ - _(3026, ZEND_IS_NOT_EQUAL_LONG_SPEC_TMPVARCV_TMPVARCV_JMPNZ) \ - _(3027, ZEND_IS_NOT_EQUAL_LONG_SPEC_TMPVARCV_TMPVARCV) \ - _(3028, ZEND_IS_NOT_EQUAL_LONG_SPEC_TMPVARCV_TMPVARCV_JMPZ) \ - _(3029, ZEND_IS_NOT_EQUAL_LONG_SPEC_TMPVARCV_TMPVARCV_JMPNZ) \ + _(3006, ZEND_IS_NOT_EQUAL_LONG_SPEC_TMPVARCV_TMPVARCV) \ + _(3007, ZEND_IS_NOT_EQUAL_LONG_SPEC_TMPVARCV_TMPVARCV_JMPZ) \ + _(3008, ZEND_IS_NOT_EQUAL_LONG_SPEC_TMPVARCV_TMPVARCV_JMPNZ) \ + _(3012, ZEND_IS_NOT_EQUAL_LONG_SPEC_TMPVARCV_TMPVARCV) \ + _(3013, ZEND_IS_NOT_EQUAL_LONG_SPEC_TMPVARCV_TMPVARCV_JMPZ) \ + _(3014, ZEND_IS_NOT_EQUAL_LONG_SPEC_TMPVARCV_TMPVARCV_JMPNZ) \ + _(3030, ZEND_IS_NOT_EQUAL_LONG_SPEC_TMPVARCV_CONST) \ + _(3031, ZEND_IS_NOT_EQUAL_LONG_SPEC_TMPVARCV_CONST_JMPZ) \ + _(3032, ZEND_IS_NOT_EQUAL_LONG_SPEC_TMPVARCV_CONST_JMPNZ) \ _(3033, ZEND_IS_NOT_EQUAL_LONG_SPEC_TMPVARCV_TMPVARCV) \ _(3034, ZEND_IS_NOT_EQUAL_LONG_SPEC_TMPVARCV_TMPVARCV_JMPZ) \ _(3035, ZEND_IS_NOT_EQUAL_LONG_SPEC_TMPVARCV_TMPVARCV_JMPNZ) \ - _(3051, ZEND_IS_NOT_EQUAL_DOUBLE_SPEC_TMPVARCV_CONST) \ - _(3052, ZEND_IS_NOT_EQUAL_DOUBLE_SPEC_TMPVARCV_CONST_JMPZ) \ - _(3053, ZEND_IS_NOT_EQUAL_DOUBLE_SPEC_TMPVARCV_CONST_JMPNZ) \ - _(3054, ZEND_IS_NOT_EQUAL_DOUBLE_SPEC_TMPVARCV_TMPVARCV) \ - _(3055, ZEND_IS_NOT_EQUAL_DOUBLE_SPEC_TMPVARCV_TMPVARCV_JMPZ) \ - _(3056, ZEND_IS_NOT_EQUAL_DOUBLE_SPEC_TMPVARCV_TMPVARCV_JMPNZ) \ - _(3057, ZEND_IS_NOT_EQUAL_DOUBLE_SPEC_TMPVARCV_TMPVARCV) \ - _(3058, ZEND_IS_NOT_EQUAL_DOUBLE_SPEC_TMPVARCV_TMPVARCV_JMPZ) \ - _(3059, ZEND_IS_NOT_EQUAL_DOUBLE_SPEC_TMPVARCV_TMPVARCV_JMPNZ) \ + _(3036, ZEND_IS_NOT_EQUAL_LONG_SPEC_TMPVARCV_TMPVARCV) \ + _(3037, ZEND_IS_NOT_EQUAL_LONG_SPEC_TMPVARCV_TMPVARCV_JMPZ) \ + _(3038, ZEND_IS_NOT_EQUAL_LONG_SPEC_TMPVARCV_TMPVARCV_JMPNZ) \ + _(3042, ZEND_IS_NOT_EQUAL_LONG_SPEC_TMPVARCV_TMPVARCV) \ + _(3043, ZEND_IS_NOT_EQUAL_LONG_SPEC_TMPVARCV_TMPVARCV_JMPZ) \ + _(3044, ZEND_IS_NOT_EQUAL_LONG_SPEC_TMPVARCV_TMPVARCV_JMPNZ) \ + _(3060, ZEND_IS_NOT_EQUAL_DOUBLE_SPEC_TMPVARCV_CONST) \ + _(3061, ZEND_IS_NOT_EQUAL_DOUBLE_SPEC_TMPVARCV_CONST_JMPZ) \ + _(3062, ZEND_IS_NOT_EQUAL_DOUBLE_SPEC_TMPVARCV_CONST_JMPNZ) \ _(3063, ZEND_IS_NOT_EQUAL_DOUBLE_SPEC_TMPVARCV_TMPVARCV) \ _(3064, ZEND_IS_NOT_EQUAL_DOUBLE_SPEC_TMPVARCV_TMPVARCV_JMPZ) \ _(3065, ZEND_IS_NOT_EQUAL_DOUBLE_SPEC_TMPVARCV_TMPVARCV_JMPNZ) \ - _(3066, ZEND_IS_NOT_EQUAL_DOUBLE_SPEC_TMPVARCV_CONST) \ - _(3067, ZEND_IS_NOT_EQUAL_DOUBLE_SPEC_TMPVARCV_CONST_JMPZ) \ - _(3068, ZEND_IS_NOT_EQUAL_DOUBLE_SPEC_TMPVARCV_CONST_JMPNZ) \ - _(3069, ZEND_IS_NOT_EQUAL_DOUBLE_SPEC_TMPVARCV_TMPVARCV) \ - _(3070, ZEND_IS_NOT_EQUAL_DOUBLE_SPEC_TMPVARCV_TMPVARCV_JMPZ) \ - _(3071, ZEND_IS_NOT_EQUAL_DOUBLE_SPEC_TMPVARCV_TMPVARCV_JMPNZ) \ + _(3066, ZEND_IS_NOT_EQUAL_DOUBLE_SPEC_TMPVARCV_TMPVARCV) \ + _(3067, ZEND_IS_NOT_EQUAL_DOUBLE_SPEC_TMPVARCV_TMPVARCV_JMPZ) \ + _(3068, ZEND_IS_NOT_EQUAL_DOUBLE_SPEC_TMPVARCV_TMPVARCV_JMPNZ) \ _(3072, ZEND_IS_NOT_EQUAL_DOUBLE_SPEC_TMPVARCV_TMPVARCV) \ _(3073, ZEND_IS_NOT_EQUAL_DOUBLE_SPEC_TMPVARCV_TMPVARCV_JMPZ) \ _(3074, ZEND_IS_NOT_EQUAL_DOUBLE_SPEC_TMPVARCV_TMPVARCV_JMPNZ) \ + _(3075, ZEND_IS_NOT_EQUAL_DOUBLE_SPEC_TMPVARCV_CONST) \ + _(3076, ZEND_IS_NOT_EQUAL_DOUBLE_SPEC_TMPVARCV_CONST_JMPZ) \ + _(3077, ZEND_IS_NOT_EQUAL_DOUBLE_SPEC_TMPVARCV_CONST_JMPNZ) \ _(3078, ZEND_IS_NOT_EQUAL_DOUBLE_SPEC_TMPVARCV_TMPVARCV) \ _(3079, ZEND_IS_NOT_EQUAL_DOUBLE_SPEC_TMPVARCV_TMPVARCV_JMPZ) \ _(3080, ZEND_IS_NOT_EQUAL_DOUBLE_SPEC_TMPVARCV_TMPVARCV_JMPNZ) \ - _(3096, ZEND_IS_NOT_EQUAL_DOUBLE_SPEC_TMPVARCV_CONST) \ - _(3097, ZEND_IS_NOT_EQUAL_DOUBLE_SPEC_TMPVARCV_CONST_JMPZ) \ - _(3098, ZEND_IS_NOT_EQUAL_DOUBLE_SPEC_TMPVARCV_CONST_JMPNZ) \ - _(3099, ZEND_IS_NOT_EQUAL_DOUBLE_SPEC_TMPVARCV_TMPVARCV) \ - _(3100, ZEND_IS_NOT_EQUAL_DOUBLE_SPEC_TMPVARCV_TMPVARCV_JMPZ) \ - _(3101, ZEND_IS_NOT_EQUAL_DOUBLE_SPEC_TMPVARCV_TMPVARCV_JMPNZ) \ - _(3102, ZEND_IS_NOT_EQUAL_DOUBLE_SPEC_TMPVARCV_TMPVARCV) \ - _(3103, ZEND_IS_NOT_EQUAL_DOUBLE_SPEC_TMPVARCV_TMPVARCV_JMPZ) \ - _(3104, ZEND_IS_NOT_EQUAL_DOUBLE_SPEC_TMPVARCV_TMPVARCV_JMPNZ) \ + _(3081, ZEND_IS_NOT_EQUAL_DOUBLE_SPEC_TMPVARCV_TMPVARCV) \ + _(3082, ZEND_IS_NOT_EQUAL_DOUBLE_SPEC_TMPVARCV_TMPVARCV_JMPZ) \ + _(3083, ZEND_IS_NOT_EQUAL_DOUBLE_SPEC_TMPVARCV_TMPVARCV_JMPNZ) \ + _(3087, ZEND_IS_NOT_EQUAL_DOUBLE_SPEC_TMPVARCV_TMPVARCV) \ + _(3088, ZEND_IS_NOT_EQUAL_DOUBLE_SPEC_TMPVARCV_TMPVARCV_JMPZ) \ + _(3089, ZEND_IS_NOT_EQUAL_DOUBLE_SPEC_TMPVARCV_TMPVARCV_JMPNZ) \ + _(3105, ZEND_IS_NOT_EQUAL_DOUBLE_SPEC_TMPVARCV_CONST) \ + _(3106, ZEND_IS_NOT_EQUAL_DOUBLE_SPEC_TMPVARCV_CONST_JMPZ) \ + _(3107, ZEND_IS_NOT_EQUAL_DOUBLE_SPEC_TMPVARCV_CONST_JMPNZ) \ _(3108, ZEND_IS_NOT_EQUAL_DOUBLE_SPEC_TMPVARCV_TMPVARCV) \ _(3109, ZEND_IS_NOT_EQUAL_DOUBLE_SPEC_TMPVARCV_TMPVARCV_JMPZ) \ _(3110, ZEND_IS_NOT_EQUAL_DOUBLE_SPEC_TMPVARCV_TMPVARCV_JMPNZ) \ - _(3111, ZEND_IS_IDENTICAL_EMPTY_ARRAY_SPEC_TMPVARCV_CONST) \ - _(3112, ZEND_IS_IDENTICAL_EMPTY_ARRAY_SPEC_TMPVARCV_CONST_JMPZ) \ - _(3113, ZEND_IS_IDENTICAL_EMPTY_ARRAY_SPEC_TMPVARCV_CONST_JMPNZ) \ - _(3114, ZEND_IS_NOT_IDENTICAL_EMPTY_ARRAY_SPEC_TMPVARCV_CONST) \ - _(3115, ZEND_IS_NOT_IDENTICAL_EMPTY_ARRAY_SPEC_TMPVARCV_CONST_JMPZ) \ - _(3116, ZEND_IS_NOT_IDENTICAL_EMPTY_ARRAY_SPEC_TMPVARCV_CONST_JMPNZ) \ - _(3117, ZEND_IS_IDENTICAL_NOTHROW_SPEC_CV_CONST) \ - _(3121, ZEND_IS_IDENTICAL_NOTHROW_SPEC_CV_CV) \ - _(3122, ZEND_IS_NOT_IDENTICAL_NOTHROW_SPEC_CV_CONST) \ - _(3126, ZEND_IS_NOT_IDENTICAL_NOTHROW_SPEC_CV_CV) \ - _(3130, ZEND_IS_SMALLER_LONG_SPEC_CONST_TMPVARCV) \ - _(3131, ZEND_IS_SMALLER_LONG_SPEC_CONST_TMPVARCV_JMPZ) \ - _(3132, ZEND_IS_SMALLER_LONG_SPEC_CONST_TMPVARCV_JMPNZ) \ - _(3133, ZEND_IS_SMALLER_LONG_SPEC_CONST_TMPVARCV) \ - _(3134, ZEND_IS_SMALLER_LONG_SPEC_CONST_TMPVARCV_JMPZ) \ - _(3135, ZEND_IS_SMALLER_LONG_SPEC_CONST_TMPVARCV_JMPNZ) \ + _(3111, ZEND_IS_NOT_EQUAL_DOUBLE_SPEC_TMPVARCV_TMPVARCV) \ + _(3112, ZEND_IS_NOT_EQUAL_DOUBLE_SPEC_TMPVARCV_TMPVARCV_JMPZ) \ + _(3113, ZEND_IS_NOT_EQUAL_DOUBLE_SPEC_TMPVARCV_TMPVARCV_JMPNZ) \ + _(3117, ZEND_IS_NOT_EQUAL_DOUBLE_SPEC_TMPVARCV_TMPVARCV) \ + _(3118, ZEND_IS_NOT_EQUAL_DOUBLE_SPEC_TMPVARCV_TMPVARCV_JMPZ) \ + _(3119, ZEND_IS_NOT_EQUAL_DOUBLE_SPEC_TMPVARCV_TMPVARCV_JMPNZ) \ + _(3120, ZEND_IS_IDENTICAL_EMPTY_ARRAY_SPEC_TMPVARCV_CONST) \ + _(3121, ZEND_IS_IDENTICAL_EMPTY_ARRAY_SPEC_TMPVARCV_CONST_JMPZ) \ + _(3122, ZEND_IS_IDENTICAL_EMPTY_ARRAY_SPEC_TMPVARCV_CONST_JMPNZ) \ + _(3123, ZEND_IS_NOT_IDENTICAL_EMPTY_ARRAY_SPEC_TMPVARCV_CONST) \ + _(3124, ZEND_IS_NOT_IDENTICAL_EMPTY_ARRAY_SPEC_TMPVARCV_CONST_JMPZ) \ + _(3125, ZEND_IS_NOT_IDENTICAL_EMPTY_ARRAY_SPEC_TMPVARCV_CONST_JMPNZ) \ + _(3126, ZEND_IS_IDENTICAL_NOTHROW_SPEC_CV_CONST) \ + _(3130, ZEND_IS_IDENTICAL_NOTHROW_SPEC_CV_CV) \ + _(3131, ZEND_IS_NOT_IDENTICAL_NOTHROW_SPEC_CV_CONST) \ + _(3135, ZEND_IS_NOT_IDENTICAL_NOTHROW_SPEC_CV_CV) \ _(3139, ZEND_IS_SMALLER_LONG_SPEC_CONST_TMPVARCV) \ _(3140, ZEND_IS_SMALLER_LONG_SPEC_CONST_TMPVARCV_JMPZ) \ _(3141, ZEND_IS_SMALLER_LONG_SPEC_CONST_TMPVARCV_JMPNZ) \ - _(3142, ZEND_IS_SMALLER_LONG_SPEC_TMPVARCV_CONST) \ - _(3143, ZEND_IS_SMALLER_LONG_SPEC_TMPVARCV_CONST_JMPZ) \ - _(3144, ZEND_IS_SMALLER_LONG_SPEC_TMPVARCV_CONST_JMPNZ) \ - _(3145, ZEND_IS_SMALLER_LONG_SPEC_TMPVARCV_TMPVARCV) \ - _(3146, ZEND_IS_SMALLER_LONG_SPEC_TMPVARCV_TMPVARCV_JMPZ) \ - _(3147, ZEND_IS_SMALLER_LONG_SPEC_TMPVARCV_TMPVARCV_JMPNZ) \ - _(3148, ZEND_IS_SMALLER_LONG_SPEC_TMPVARCV_TMPVARCV) \ - _(3149, ZEND_IS_SMALLER_LONG_SPEC_TMPVARCV_TMPVARCV_JMPZ) \ - _(3150, ZEND_IS_SMALLER_LONG_SPEC_TMPVARCV_TMPVARCV_JMPNZ) \ + _(3142, ZEND_IS_SMALLER_LONG_SPEC_CONST_TMPVARCV) \ + _(3143, ZEND_IS_SMALLER_LONG_SPEC_CONST_TMPVARCV_JMPZ) \ + _(3144, ZEND_IS_SMALLER_LONG_SPEC_CONST_TMPVARCV_JMPNZ) \ + _(3148, ZEND_IS_SMALLER_LONG_SPEC_CONST_TMPVARCV) \ + _(3149, ZEND_IS_SMALLER_LONG_SPEC_CONST_TMPVARCV_JMPZ) \ + _(3150, ZEND_IS_SMALLER_LONG_SPEC_CONST_TMPVARCV_JMPNZ) \ + _(3151, ZEND_IS_SMALLER_LONG_SPEC_TMPVARCV_CONST) \ + _(3152, ZEND_IS_SMALLER_LONG_SPEC_TMPVARCV_CONST_JMPZ) \ + _(3153, ZEND_IS_SMALLER_LONG_SPEC_TMPVARCV_CONST_JMPNZ) \ _(3154, ZEND_IS_SMALLER_LONG_SPEC_TMPVARCV_TMPVARCV) \ _(3155, ZEND_IS_SMALLER_LONG_SPEC_TMPVARCV_TMPVARCV_JMPZ) \ _(3156, ZEND_IS_SMALLER_LONG_SPEC_TMPVARCV_TMPVARCV_JMPNZ) \ - _(3157, ZEND_IS_SMALLER_LONG_SPEC_TMPVARCV_CONST) \ - _(3158, ZEND_IS_SMALLER_LONG_SPEC_TMPVARCV_CONST_JMPZ) \ - _(3159, ZEND_IS_SMALLER_LONG_SPEC_TMPVARCV_CONST_JMPNZ) \ - _(3160, ZEND_IS_SMALLER_LONG_SPEC_TMPVARCV_TMPVARCV) \ - _(3161, ZEND_IS_SMALLER_LONG_SPEC_TMPVARCV_TMPVARCV_JMPZ) \ - _(3162, ZEND_IS_SMALLER_LONG_SPEC_TMPVARCV_TMPVARCV_JMPNZ) \ + _(3157, ZEND_IS_SMALLER_LONG_SPEC_TMPVARCV_TMPVARCV) \ + _(3158, ZEND_IS_SMALLER_LONG_SPEC_TMPVARCV_TMPVARCV_JMPZ) \ + _(3159, ZEND_IS_SMALLER_LONG_SPEC_TMPVARCV_TMPVARCV_JMPNZ) \ _(3163, ZEND_IS_SMALLER_LONG_SPEC_TMPVARCV_TMPVARCV) \ _(3164, ZEND_IS_SMALLER_LONG_SPEC_TMPVARCV_TMPVARCV_JMPZ) \ _(3165, ZEND_IS_SMALLER_LONG_SPEC_TMPVARCV_TMPVARCV_JMPNZ) \ + _(3166, ZEND_IS_SMALLER_LONG_SPEC_TMPVARCV_CONST) \ + _(3167, ZEND_IS_SMALLER_LONG_SPEC_TMPVARCV_CONST_JMPZ) \ + _(3168, ZEND_IS_SMALLER_LONG_SPEC_TMPVARCV_CONST_JMPNZ) \ _(3169, ZEND_IS_SMALLER_LONG_SPEC_TMPVARCV_TMPVARCV) \ _(3170, ZEND_IS_SMALLER_LONG_SPEC_TMPVARCV_TMPVARCV_JMPZ) \ _(3171, ZEND_IS_SMALLER_LONG_SPEC_TMPVARCV_TMPVARCV_JMPNZ) \ - _(3187, ZEND_IS_SMALLER_LONG_SPEC_TMPVARCV_CONST) \ - _(3188, ZEND_IS_SMALLER_LONG_SPEC_TMPVARCV_CONST_JMPZ) \ - _(3189, ZEND_IS_SMALLER_LONG_SPEC_TMPVARCV_CONST_JMPNZ) \ - _(3190, ZEND_IS_SMALLER_LONG_SPEC_TMPVARCV_TMPVARCV) \ - _(3191, ZEND_IS_SMALLER_LONG_SPEC_TMPVARCV_TMPVARCV_JMPZ) \ - _(3192, ZEND_IS_SMALLER_LONG_SPEC_TMPVARCV_TMPVARCV_JMPNZ) \ - _(3193, ZEND_IS_SMALLER_LONG_SPEC_TMPVARCV_TMPVARCV) \ - _(3194, ZEND_IS_SMALLER_LONG_SPEC_TMPVARCV_TMPVARCV_JMPZ) \ - _(3195, ZEND_IS_SMALLER_LONG_SPEC_TMPVARCV_TMPVARCV_JMPNZ) \ + _(3172, ZEND_IS_SMALLER_LONG_SPEC_TMPVARCV_TMPVARCV) \ + _(3173, ZEND_IS_SMALLER_LONG_SPEC_TMPVARCV_TMPVARCV_JMPZ) \ + _(3174, ZEND_IS_SMALLER_LONG_SPEC_TMPVARCV_TMPVARCV_JMPNZ) \ + _(3178, ZEND_IS_SMALLER_LONG_SPEC_TMPVARCV_TMPVARCV) \ + _(3179, ZEND_IS_SMALLER_LONG_SPEC_TMPVARCV_TMPVARCV_JMPZ) \ + _(3180, ZEND_IS_SMALLER_LONG_SPEC_TMPVARCV_TMPVARCV_JMPNZ) \ + _(3196, ZEND_IS_SMALLER_LONG_SPEC_TMPVARCV_CONST) \ + _(3197, ZEND_IS_SMALLER_LONG_SPEC_TMPVARCV_CONST_JMPZ) \ + _(3198, ZEND_IS_SMALLER_LONG_SPEC_TMPVARCV_CONST_JMPNZ) \ _(3199, ZEND_IS_SMALLER_LONG_SPEC_TMPVARCV_TMPVARCV) \ _(3200, ZEND_IS_SMALLER_LONG_SPEC_TMPVARCV_TMPVARCV_JMPZ) \ _(3201, ZEND_IS_SMALLER_LONG_SPEC_TMPVARCV_TMPVARCV_JMPNZ) \ - _(3205, ZEND_IS_SMALLER_DOUBLE_SPEC_CONST_TMPVARCV) \ - _(3206, ZEND_IS_SMALLER_DOUBLE_SPEC_CONST_TMPVARCV_JMPZ) \ - _(3207, ZEND_IS_SMALLER_DOUBLE_SPEC_CONST_TMPVARCV_JMPNZ) \ - _(3208, ZEND_IS_SMALLER_DOUBLE_SPEC_CONST_TMPVARCV) \ - _(3209, ZEND_IS_SMALLER_DOUBLE_SPEC_CONST_TMPVARCV_JMPZ) \ - _(3210, ZEND_IS_SMALLER_DOUBLE_SPEC_CONST_TMPVARCV_JMPNZ) \ + _(3202, ZEND_IS_SMALLER_LONG_SPEC_TMPVARCV_TMPVARCV) \ + _(3203, ZEND_IS_SMALLER_LONG_SPEC_TMPVARCV_TMPVARCV_JMPZ) \ + _(3204, ZEND_IS_SMALLER_LONG_SPEC_TMPVARCV_TMPVARCV_JMPNZ) \ + _(3208, ZEND_IS_SMALLER_LONG_SPEC_TMPVARCV_TMPVARCV) \ + _(3209, ZEND_IS_SMALLER_LONG_SPEC_TMPVARCV_TMPVARCV_JMPZ) \ + _(3210, ZEND_IS_SMALLER_LONG_SPEC_TMPVARCV_TMPVARCV_JMPNZ) \ _(3214, ZEND_IS_SMALLER_DOUBLE_SPEC_CONST_TMPVARCV) \ _(3215, ZEND_IS_SMALLER_DOUBLE_SPEC_CONST_TMPVARCV_JMPZ) \ _(3216, ZEND_IS_SMALLER_DOUBLE_SPEC_CONST_TMPVARCV_JMPNZ) \ - _(3217, ZEND_IS_SMALLER_DOUBLE_SPEC_TMPVARCV_CONST) \ - _(3218, ZEND_IS_SMALLER_DOUBLE_SPEC_TMPVARCV_CONST_JMPZ) \ - _(3219, ZEND_IS_SMALLER_DOUBLE_SPEC_TMPVARCV_CONST_JMPNZ) \ - _(3220, ZEND_IS_SMALLER_DOUBLE_SPEC_TMPVARCV_TMPVARCV) \ - _(3221, ZEND_IS_SMALLER_DOUBLE_SPEC_TMPVARCV_TMPVARCV_JMPZ) \ - _(3222, ZEND_IS_SMALLER_DOUBLE_SPEC_TMPVARCV_TMPVARCV_JMPNZ) \ - _(3223, ZEND_IS_SMALLER_DOUBLE_SPEC_TMPVARCV_TMPVARCV) \ - _(3224, ZEND_IS_SMALLER_DOUBLE_SPEC_TMPVARCV_TMPVARCV_JMPZ) \ - _(3225, ZEND_IS_SMALLER_DOUBLE_SPEC_TMPVARCV_TMPVARCV_JMPNZ) \ + _(3217, ZEND_IS_SMALLER_DOUBLE_SPEC_CONST_TMPVARCV) \ + _(3218, ZEND_IS_SMALLER_DOUBLE_SPEC_CONST_TMPVARCV_JMPZ) \ + _(3219, ZEND_IS_SMALLER_DOUBLE_SPEC_CONST_TMPVARCV_JMPNZ) \ + _(3223, ZEND_IS_SMALLER_DOUBLE_SPEC_CONST_TMPVARCV) \ + _(3224, ZEND_IS_SMALLER_DOUBLE_SPEC_CONST_TMPVARCV_JMPZ) \ + _(3225, ZEND_IS_SMALLER_DOUBLE_SPEC_CONST_TMPVARCV_JMPNZ) \ + _(3226, ZEND_IS_SMALLER_DOUBLE_SPEC_TMPVARCV_CONST) \ + _(3227, ZEND_IS_SMALLER_DOUBLE_SPEC_TMPVARCV_CONST_JMPZ) \ + _(3228, ZEND_IS_SMALLER_DOUBLE_SPEC_TMPVARCV_CONST_JMPNZ) \ _(3229, ZEND_IS_SMALLER_DOUBLE_SPEC_TMPVARCV_TMPVARCV) \ _(3230, ZEND_IS_SMALLER_DOUBLE_SPEC_TMPVARCV_TMPVARCV_JMPZ) \ _(3231, ZEND_IS_SMALLER_DOUBLE_SPEC_TMPVARCV_TMPVARCV_JMPNZ) \ - _(3232, ZEND_IS_SMALLER_DOUBLE_SPEC_TMPVARCV_CONST) \ - _(3233, ZEND_IS_SMALLER_DOUBLE_SPEC_TMPVARCV_CONST_JMPZ) \ - _(3234, ZEND_IS_SMALLER_DOUBLE_SPEC_TMPVARCV_CONST_JMPNZ) \ - _(3235, ZEND_IS_SMALLER_DOUBLE_SPEC_TMPVARCV_TMPVARCV) \ - _(3236, ZEND_IS_SMALLER_DOUBLE_SPEC_TMPVARCV_TMPVARCV_JMPZ) \ - _(3237, ZEND_IS_SMALLER_DOUBLE_SPEC_TMPVARCV_TMPVARCV_JMPNZ) \ + _(3232, ZEND_IS_SMALLER_DOUBLE_SPEC_TMPVARCV_TMPVARCV) \ + _(3233, ZEND_IS_SMALLER_DOUBLE_SPEC_TMPVARCV_TMPVARCV_JMPZ) \ + _(3234, ZEND_IS_SMALLER_DOUBLE_SPEC_TMPVARCV_TMPVARCV_JMPNZ) \ _(3238, ZEND_IS_SMALLER_DOUBLE_SPEC_TMPVARCV_TMPVARCV) \ _(3239, ZEND_IS_SMALLER_DOUBLE_SPEC_TMPVARCV_TMPVARCV_JMPZ) \ _(3240, ZEND_IS_SMALLER_DOUBLE_SPEC_TMPVARCV_TMPVARCV_JMPNZ) \ + _(3241, ZEND_IS_SMALLER_DOUBLE_SPEC_TMPVARCV_CONST) \ + _(3242, ZEND_IS_SMALLER_DOUBLE_SPEC_TMPVARCV_CONST_JMPZ) \ + _(3243, ZEND_IS_SMALLER_DOUBLE_SPEC_TMPVARCV_CONST_JMPNZ) \ _(3244, ZEND_IS_SMALLER_DOUBLE_SPEC_TMPVARCV_TMPVARCV) \ _(3245, ZEND_IS_SMALLER_DOUBLE_SPEC_TMPVARCV_TMPVARCV_JMPZ) \ _(3246, ZEND_IS_SMALLER_DOUBLE_SPEC_TMPVARCV_TMPVARCV_JMPNZ) \ - _(3262, ZEND_IS_SMALLER_DOUBLE_SPEC_TMPVARCV_CONST) \ - _(3263, ZEND_IS_SMALLER_DOUBLE_SPEC_TMPVARCV_CONST_JMPZ) \ - _(3264, ZEND_IS_SMALLER_DOUBLE_SPEC_TMPVARCV_CONST_JMPNZ) \ - _(3265, ZEND_IS_SMALLER_DOUBLE_SPEC_TMPVARCV_TMPVARCV) \ - _(3266, ZEND_IS_SMALLER_DOUBLE_SPEC_TMPVARCV_TMPVARCV_JMPZ) \ - _(3267, ZEND_IS_SMALLER_DOUBLE_SPEC_TMPVARCV_TMPVARCV_JMPNZ) \ - _(3268, ZEND_IS_SMALLER_DOUBLE_SPEC_TMPVARCV_TMPVARCV) \ - _(3269, ZEND_IS_SMALLER_DOUBLE_SPEC_TMPVARCV_TMPVARCV_JMPZ) \ - _(3270, ZEND_IS_SMALLER_DOUBLE_SPEC_TMPVARCV_TMPVARCV_JMPNZ) \ + _(3247, ZEND_IS_SMALLER_DOUBLE_SPEC_TMPVARCV_TMPVARCV) \ + _(3248, ZEND_IS_SMALLER_DOUBLE_SPEC_TMPVARCV_TMPVARCV_JMPZ) \ + _(3249, ZEND_IS_SMALLER_DOUBLE_SPEC_TMPVARCV_TMPVARCV_JMPNZ) \ + _(3253, ZEND_IS_SMALLER_DOUBLE_SPEC_TMPVARCV_TMPVARCV) \ + _(3254, ZEND_IS_SMALLER_DOUBLE_SPEC_TMPVARCV_TMPVARCV_JMPZ) \ + _(3255, ZEND_IS_SMALLER_DOUBLE_SPEC_TMPVARCV_TMPVARCV_JMPNZ) \ + _(3271, ZEND_IS_SMALLER_DOUBLE_SPEC_TMPVARCV_CONST) \ + _(3272, ZEND_IS_SMALLER_DOUBLE_SPEC_TMPVARCV_CONST_JMPZ) \ + _(3273, ZEND_IS_SMALLER_DOUBLE_SPEC_TMPVARCV_CONST_JMPNZ) \ _(3274, ZEND_IS_SMALLER_DOUBLE_SPEC_TMPVARCV_TMPVARCV) \ _(3275, ZEND_IS_SMALLER_DOUBLE_SPEC_TMPVARCV_TMPVARCV_JMPZ) \ _(3276, ZEND_IS_SMALLER_DOUBLE_SPEC_TMPVARCV_TMPVARCV_JMPNZ) \ - _(3280, ZEND_IS_SMALLER_OR_EQUAL_LONG_SPEC_CONST_TMPVARCV) \ - _(3281, ZEND_IS_SMALLER_OR_EQUAL_LONG_SPEC_CONST_TMPVARCV_JMPZ) \ - _(3282, ZEND_IS_SMALLER_OR_EQUAL_LONG_SPEC_CONST_TMPVARCV_JMPNZ) \ - _(3283, ZEND_IS_SMALLER_OR_EQUAL_LONG_SPEC_CONST_TMPVARCV) \ - _(3284, ZEND_IS_SMALLER_OR_EQUAL_LONG_SPEC_CONST_TMPVARCV_JMPZ) \ - _(3285, ZEND_IS_SMALLER_OR_EQUAL_LONG_SPEC_CONST_TMPVARCV_JMPNZ) \ + _(3277, ZEND_IS_SMALLER_DOUBLE_SPEC_TMPVARCV_TMPVARCV) \ + _(3278, ZEND_IS_SMALLER_DOUBLE_SPEC_TMPVARCV_TMPVARCV_JMPZ) \ + _(3279, ZEND_IS_SMALLER_DOUBLE_SPEC_TMPVARCV_TMPVARCV_JMPNZ) \ + _(3283, ZEND_IS_SMALLER_DOUBLE_SPEC_TMPVARCV_TMPVARCV) \ + _(3284, ZEND_IS_SMALLER_DOUBLE_SPEC_TMPVARCV_TMPVARCV_JMPZ) \ + _(3285, ZEND_IS_SMALLER_DOUBLE_SPEC_TMPVARCV_TMPVARCV_JMPNZ) \ _(3289, ZEND_IS_SMALLER_OR_EQUAL_LONG_SPEC_CONST_TMPVARCV) \ _(3290, ZEND_IS_SMALLER_OR_EQUAL_LONG_SPEC_CONST_TMPVARCV_JMPZ) \ _(3291, ZEND_IS_SMALLER_OR_EQUAL_LONG_SPEC_CONST_TMPVARCV_JMPNZ) \ - _(3292, ZEND_IS_SMALLER_OR_EQUAL_LONG_SPEC_TMPVARCV_CONST) \ - _(3293, ZEND_IS_SMALLER_OR_EQUAL_LONG_SPEC_TMPVARCV_CONST_JMPZ) \ - _(3294, ZEND_IS_SMALLER_OR_EQUAL_LONG_SPEC_TMPVARCV_CONST_JMPNZ) \ - _(3295, ZEND_IS_SMALLER_OR_EQUAL_LONG_SPEC_TMPVARCV_TMPVARCV) \ - _(3296, ZEND_IS_SMALLER_OR_EQUAL_LONG_SPEC_TMPVARCV_TMPVARCV_JMPZ) \ - _(3297, ZEND_IS_SMALLER_OR_EQUAL_LONG_SPEC_TMPVARCV_TMPVARCV_JMPNZ) \ - _(3298, ZEND_IS_SMALLER_OR_EQUAL_LONG_SPEC_TMPVARCV_TMPVARCV) \ - _(3299, ZEND_IS_SMALLER_OR_EQUAL_LONG_SPEC_TMPVARCV_TMPVARCV_JMPZ) \ - _(3300, ZEND_IS_SMALLER_OR_EQUAL_LONG_SPEC_TMPVARCV_TMPVARCV_JMPNZ) \ + _(3292, ZEND_IS_SMALLER_OR_EQUAL_LONG_SPEC_CONST_TMPVARCV) \ + _(3293, ZEND_IS_SMALLER_OR_EQUAL_LONG_SPEC_CONST_TMPVARCV_JMPZ) \ + _(3294, ZEND_IS_SMALLER_OR_EQUAL_LONG_SPEC_CONST_TMPVARCV_JMPNZ) \ + _(3298, ZEND_IS_SMALLER_OR_EQUAL_LONG_SPEC_CONST_TMPVARCV) \ + _(3299, ZEND_IS_SMALLER_OR_EQUAL_LONG_SPEC_CONST_TMPVARCV_JMPZ) \ + _(3300, ZEND_IS_SMALLER_OR_EQUAL_LONG_SPEC_CONST_TMPVARCV_JMPNZ) \ + _(3301, ZEND_IS_SMALLER_OR_EQUAL_LONG_SPEC_TMPVARCV_CONST) \ + _(3302, ZEND_IS_SMALLER_OR_EQUAL_LONG_SPEC_TMPVARCV_CONST_JMPZ) \ + _(3303, ZEND_IS_SMALLER_OR_EQUAL_LONG_SPEC_TMPVARCV_CONST_JMPNZ) \ _(3304, ZEND_IS_SMALLER_OR_EQUAL_LONG_SPEC_TMPVARCV_TMPVARCV) \ _(3305, ZEND_IS_SMALLER_OR_EQUAL_LONG_SPEC_TMPVARCV_TMPVARCV_JMPZ) \ _(3306, ZEND_IS_SMALLER_OR_EQUAL_LONG_SPEC_TMPVARCV_TMPVARCV_JMPNZ) \ - _(3307, ZEND_IS_SMALLER_OR_EQUAL_LONG_SPEC_TMPVARCV_CONST) \ - _(3308, ZEND_IS_SMALLER_OR_EQUAL_LONG_SPEC_TMPVARCV_CONST_JMPZ) \ - _(3309, ZEND_IS_SMALLER_OR_EQUAL_LONG_SPEC_TMPVARCV_CONST_JMPNZ) \ - _(3310, ZEND_IS_SMALLER_OR_EQUAL_LONG_SPEC_TMPVARCV_TMPVARCV) \ - _(3311, ZEND_IS_SMALLER_OR_EQUAL_LONG_SPEC_TMPVARCV_TMPVARCV_JMPZ) \ - _(3312, ZEND_IS_SMALLER_OR_EQUAL_LONG_SPEC_TMPVARCV_TMPVARCV_JMPNZ) \ + _(3307, ZEND_IS_SMALLER_OR_EQUAL_LONG_SPEC_TMPVARCV_TMPVARCV) \ + _(3308, ZEND_IS_SMALLER_OR_EQUAL_LONG_SPEC_TMPVARCV_TMPVARCV_JMPZ) \ + _(3309, ZEND_IS_SMALLER_OR_EQUAL_LONG_SPEC_TMPVARCV_TMPVARCV_JMPNZ) \ _(3313, ZEND_IS_SMALLER_OR_EQUAL_LONG_SPEC_TMPVARCV_TMPVARCV) \ _(3314, ZEND_IS_SMALLER_OR_EQUAL_LONG_SPEC_TMPVARCV_TMPVARCV_JMPZ) \ _(3315, ZEND_IS_SMALLER_OR_EQUAL_LONG_SPEC_TMPVARCV_TMPVARCV_JMPNZ) \ + _(3316, ZEND_IS_SMALLER_OR_EQUAL_LONG_SPEC_TMPVARCV_CONST) \ + _(3317, ZEND_IS_SMALLER_OR_EQUAL_LONG_SPEC_TMPVARCV_CONST_JMPZ) \ + _(3318, ZEND_IS_SMALLER_OR_EQUAL_LONG_SPEC_TMPVARCV_CONST_JMPNZ) \ _(3319, ZEND_IS_SMALLER_OR_EQUAL_LONG_SPEC_TMPVARCV_TMPVARCV) \ _(3320, ZEND_IS_SMALLER_OR_EQUAL_LONG_SPEC_TMPVARCV_TMPVARCV_JMPZ) \ _(3321, ZEND_IS_SMALLER_OR_EQUAL_LONG_SPEC_TMPVARCV_TMPVARCV_JMPNZ) \ - _(3337, ZEND_IS_SMALLER_OR_EQUAL_LONG_SPEC_TMPVARCV_CONST) \ - _(3338, ZEND_IS_SMALLER_OR_EQUAL_LONG_SPEC_TMPVARCV_CONST_JMPZ) \ - _(3339, ZEND_IS_SMALLER_OR_EQUAL_LONG_SPEC_TMPVARCV_CONST_JMPNZ) \ - _(3340, ZEND_IS_SMALLER_OR_EQUAL_LONG_SPEC_TMPVARCV_TMPVARCV) \ - _(3341, ZEND_IS_SMALLER_OR_EQUAL_LONG_SPEC_TMPVARCV_TMPVARCV_JMPZ) \ - _(3342, ZEND_IS_SMALLER_OR_EQUAL_LONG_SPEC_TMPVARCV_TMPVARCV_JMPNZ) \ - _(3343, ZEND_IS_SMALLER_OR_EQUAL_LONG_SPEC_TMPVARCV_TMPVARCV) \ - _(3344, ZEND_IS_SMALLER_OR_EQUAL_LONG_SPEC_TMPVARCV_TMPVARCV_JMPZ) \ - _(3345, ZEND_IS_SMALLER_OR_EQUAL_LONG_SPEC_TMPVARCV_TMPVARCV_JMPNZ) \ + _(3322, ZEND_IS_SMALLER_OR_EQUAL_LONG_SPEC_TMPVARCV_TMPVARCV) \ + _(3323, ZEND_IS_SMALLER_OR_EQUAL_LONG_SPEC_TMPVARCV_TMPVARCV_JMPZ) \ + _(3324, ZEND_IS_SMALLER_OR_EQUAL_LONG_SPEC_TMPVARCV_TMPVARCV_JMPNZ) \ + _(3328, ZEND_IS_SMALLER_OR_EQUAL_LONG_SPEC_TMPVARCV_TMPVARCV) \ + _(3329, ZEND_IS_SMALLER_OR_EQUAL_LONG_SPEC_TMPVARCV_TMPVARCV_JMPZ) \ + _(3330, ZEND_IS_SMALLER_OR_EQUAL_LONG_SPEC_TMPVARCV_TMPVARCV_JMPNZ) \ + _(3346, ZEND_IS_SMALLER_OR_EQUAL_LONG_SPEC_TMPVARCV_CONST) \ + _(3347, ZEND_IS_SMALLER_OR_EQUAL_LONG_SPEC_TMPVARCV_CONST_JMPZ) \ + _(3348, ZEND_IS_SMALLER_OR_EQUAL_LONG_SPEC_TMPVARCV_CONST_JMPNZ) \ _(3349, ZEND_IS_SMALLER_OR_EQUAL_LONG_SPEC_TMPVARCV_TMPVARCV) \ _(3350, ZEND_IS_SMALLER_OR_EQUAL_LONG_SPEC_TMPVARCV_TMPVARCV_JMPZ) \ _(3351, ZEND_IS_SMALLER_OR_EQUAL_LONG_SPEC_TMPVARCV_TMPVARCV_JMPNZ) \ - _(3355, ZEND_IS_SMALLER_OR_EQUAL_DOUBLE_SPEC_CONST_TMPVARCV) \ - _(3356, ZEND_IS_SMALLER_OR_EQUAL_DOUBLE_SPEC_CONST_TMPVARCV_JMPZ) \ - _(3357, ZEND_IS_SMALLER_OR_EQUAL_DOUBLE_SPEC_CONST_TMPVARCV_JMPNZ) \ - _(3358, ZEND_IS_SMALLER_OR_EQUAL_DOUBLE_SPEC_CONST_TMPVARCV) \ - _(3359, ZEND_IS_SMALLER_OR_EQUAL_DOUBLE_SPEC_CONST_TMPVARCV_JMPZ) \ - _(3360, ZEND_IS_SMALLER_OR_EQUAL_DOUBLE_SPEC_CONST_TMPVARCV_JMPNZ) \ + _(3352, ZEND_IS_SMALLER_OR_EQUAL_LONG_SPEC_TMPVARCV_TMPVARCV) \ + _(3353, ZEND_IS_SMALLER_OR_EQUAL_LONG_SPEC_TMPVARCV_TMPVARCV_JMPZ) \ + _(3354, ZEND_IS_SMALLER_OR_EQUAL_LONG_SPEC_TMPVARCV_TMPVARCV_JMPNZ) \ + _(3358, ZEND_IS_SMALLER_OR_EQUAL_LONG_SPEC_TMPVARCV_TMPVARCV) \ + _(3359, ZEND_IS_SMALLER_OR_EQUAL_LONG_SPEC_TMPVARCV_TMPVARCV_JMPZ) \ + _(3360, ZEND_IS_SMALLER_OR_EQUAL_LONG_SPEC_TMPVARCV_TMPVARCV_JMPNZ) \ _(3364, ZEND_IS_SMALLER_OR_EQUAL_DOUBLE_SPEC_CONST_TMPVARCV) \ _(3365, ZEND_IS_SMALLER_OR_EQUAL_DOUBLE_SPEC_CONST_TMPVARCV_JMPZ) \ _(3366, ZEND_IS_SMALLER_OR_EQUAL_DOUBLE_SPEC_CONST_TMPVARCV_JMPNZ) \ - _(3367, ZEND_IS_SMALLER_OR_EQUAL_DOUBLE_SPEC_TMPVARCV_CONST) \ - _(3368, ZEND_IS_SMALLER_OR_EQUAL_DOUBLE_SPEC_TMPVARCV_CONST_JMPZ) \ - _(3369, ZEND_IS_SMALLER_OR_EQUAL_DOUBLE_SPEC_TMPVARCV_CONST_JMPNZ) \ - _(3370, ZEND_IS_SMALLER_OR_EQUAL_DOUBLE_SPEC_TMPVARCV_TMPVARCV) \ - _(3371, ZEND_IS_SMALLER_OR_EQUAL_DOUBLE_SPEC_TMPVARCV_TMPVARCV_JMPZ) \ - _(3372, ZEND_IS_SMALLER_OR_EQUAL_DOUBLE_SPEC_TMPVARCV_TMPVARCV_JMPNZ) \ - _(3373, ZEND_IS_SMALLER_OR_EQUAL_DOUBLE_SPEC_TMPVARCV_TMPVARCV) \ - _(3374, ZEND_IS_SMALLER_OR_EQUAL_DOUBLE_SPEC_TMPVARCV_TMPVARCV_JMPZ) \ - _(3375, ZEND_IS_SMALLER_OR_EQUAL_DOUBLE_SPEC_TMPVARCV_TMPVARCV_JMPNZ) \ + _(3367, ZEND_IS_SMALLER_OR_EQUAL_DOUBLE_SPEC_CONST_TMPVARCV) \ + _(3368, ZEND_IS_SMALLER_OR_EQUAL_DOUBLE_SPEC_CONST_TMPVARCV_JMPZ) \ + _(3369, ZEND_IS_SMALLER_OR_EQUAL_DOUBLE_SPEC_CONST_TMPVARCV_JMPNZ) \ + _(3373, ZEND_IS_SMALLER_OR_EQUAL_DOUBLE_SPEC_CONST_TMPVARCV) \ + _(3374, ZEND_IS_SMALLER_OR_EQUAL_DOUBLE_SPEC_CONST_TMPVARCV_JMPZ) \ + _(3375, ZEND_IS_SMALLER_OR_EQUAL_DOUBLE_SPEC_CONST_TMPVARCV_JMPNZ) \ + _(3376, ZEND_IS_SMALLER_OR_EQUAL_DOUBLE_SPEC_TMPVARCV_CONST) \ + _(3377, ZEND_IS_SMALLER_OR_EQUAL_DOUBLE_SPEC_TMPVARCV_CONST_JMPZ) \ + _(3378, ZEND_IS_SMALLER_OR_EQUAL_DOUBLE_SPEC_TMPVARCV_CONST_JMPNZ) \ _(3379, ZEND_IS_SMALLER_OR_EQUAL_DOUBLE_SPEC_TMPVARCV_TMPVARCV) \ _(3380, ZEND_IS_SMALLER_OR_EQUAL_DOUBLE_SPEC_TMPVARCV_TMPVARCV_JMPZ) \ _(3381, ZEND_IS_SMALLER_OR_EQUAL_DOUBLE_SPEC_TMPVARCV_TMPVARCV_JMPNZ) \ - _(3382, ZEND_IS_SMALLER_OR_EQUAL_DOUBLE_SPEC_TMPVARCV_CONST) \ - _(3383, ZEND_IS_SMALLER_OR_EQUAL_DOUBLE_SPEC_TMPVARCV_CONST_JMPZ) \ - _(3384, ZEND_IS_SMALLER_OR_EQUAL_DOUBLE_SPEC_TMPVARCV_CONST_JMPNZ) \ - _(3385, ZEND_IS_SMALLER_OR_EQUAL_DOUBLE_SPEC_TMPVARCV_TMPVARCV) \ - _(3386, ZEND_IS_SMALLER_OR_EQUAL_DOUBLE_SPEC_TMPVARCV_TMPVARCV_JMPZ) \ - _(3387, ZEND_IS_SMALLER_OR_EQUAL_DOUBLE_SPEC_TMPVARCV_TMPVARCV_JMPNZ) \ + _(3382, ZEND_IS_SMALLER_OR_EQUAL_DOUBLE_SPEC_TMPVARCV_TMPVARCV) \ + _(3383, ZEND_IS_SMALLER_OR_EQUAL_DOUBLE_SPEC_TMPVARCV_TMPVARCV_JMPZ) \ + _(3384, ZEND_IS_SMALLER_OR_EQUAL_DOUBLE_SPEC_TMPVARCV_TMPVARCV_JMPNZ) \ _(3388, ZEND_IS_SMALLER_OR_EQUAL_DOUBLE_SPEC_TMPVARCV_TMPVARCV) \ _(3389, ZEND_IS_SMALLER_OR_EQUAL_DOUBLE_SPEC_TMPVARCV_TMPVARCV_JMPZ) \ _(3390, ZEND_IS_SMALLER_OR_EQUAL_DOUBLE_SPEC_TMPVARCV_TMPVARCV_JMPNZ) \ + _(3391, ZEND_IS_SMALLER_OR_EQUAL_DOUBLE_SPEC_TMPVARCV_CONST) \ + _(3392, ZEND_IS_SMALLER_OR_EQUAL_DOUBLE_SPEC_TMPVARCV_CONST_JMPZ) \ + _(3393, ZEND_IS_SMALLER_OR_EQUAL_DOUBLE_SPEC_TMPVARCV_CONST_JMPNZ) \ _(3394, ZEND_IS_SMALLER_OR_EQUAL_DOUBLE_SPEC_TMPVARCV_TMPVARCV) \ _(3395, ZEND_IS_SMALLER_OR_EQUAL_DOUBLE_SPEC_TMPVARCV_TMPVARCV_JMPZ) \ _(3396, ZEND_IS_SMALLER_OR_EQUAL_DOUBLE_SPEC_TMPVARCV_TMPVARCV_JMPNZ) \ - _(3412, ZEND_IS_SMALLER_OR_EQUAL_DOUBLE_SPEC_TMPVARCV_CONST) \ - _(3413, ZEND_IS_SMALLER_OR_EQUAL_DOUBLE_SPEC_TMPVARCV_CONST_JMPZ) \ - _(3414, ZEND_IS_SMALLER_OR_EQUAL_DOUBLE_SPEC_TMPVARCV_CONST_JMPNZ) \ - _(3415, ZEND_IS_SMALLER_OR_EQUAL_DOUBLE_SPEC_TMPVARCV_TMPVARCV) \ - _(3416, ZEND_IS_SMALLER_OR_EQUAL_DOUBLE_SPEC_TMPVARCV_TMPVARCV_JMPZ) \ - _(3417, ZEND_IS_SMALLER_OR_EQUAL_DOUBLE_SPEC_TMPVARCV_TMPVARCV_JMPNZ) \ - _(3418, ZEND_IS_SMALLER_OR_EQUAL_DOUBLE_SPEC_TMPVARCV_TMPVARCV) \ - _(3419, ZEND_IS_SMALLER_OR_EQUAL_DOUBLE_SPEC_TMPVARCV_TMPVARCV_JMPZ) \ - _(3420, ZEND_IS_SMALLER_OR_EQUAL_DOUBLE_SPEC_TMPVARCV_TMPVARCV_JMPNZ) \ + _(3397, ZEND_IS_SMALLER_OR_EQUAL_DOUBLE_SPEC_TMPVARCV_TMPVARCV) \ + _(3398, ZEND_IS_SMALLER_OR_EQUAL_DOUBLE_SPEC_TMPVARCV_TMPVARCV_JMPZ) \ + _(3399, ZEND_IS_SMALLER_OR_EQUAL_DOUBLE_SPEC_TMPVARCV_TMPVARCV_JMPNZ) \ + _(3403, ZEND_IS_SMALLER_OR_EQUAL_DOUBLE_SPEC_TMPVARCV_TMPVARCV) \ + _(3404, ZEND_IS_SMALLER_OR_EQUAL_DOUBLE_SPEC_TMPVARCV_TMPVARCV_JMPZ) \ + _(3405, ZEND_IS_SMALLER_OR_EQUAL_DOUBLE_SPEC_TMPVARCV_TMPVARCV_JMPNZ) \ + _(3421, ZEND_IS_SMALLER_OR_EQUAL_DOUBLE_SPEC_TMPVARCV_CONST) \ + _(3422, ZEND_IS_SMALLER_OR_EQUAL_DOUBLE_SPEC_TMPVARCV_CONST_JMPZ) \ + _(3423, ZEND_IS_SMALLER_OR_EQUAL_DOUBLE_SPEC_TMPVARCV_CONST_JMPNZ) \ _(3424, ZEND_IS_SMALLER_OR_EQUAL_DOUBLE_SPEC_TMPVARCV_TMPVARCV) \ _(3425, ZEND_IS_SMALLER_OR_EQUAL_DOUBLE_SPEC_TMPVARCV_TMPVARCV_JMPZ) \ _(3426, ZEND_IS_SMALLER_OR_EQUAL_DOUBLE_SPEC_TMPVARCV_TMPVARCV_JMPNZ) \ - _(3427, ZEND_PRE_INC_LONG_NO_OVERFLOW_SPEC_CV_RETVAL_UNUSED) \ - _(3428, ZEND_PRE_INC_LONG_NO_OVERFLOW_SPEC_CV_RETVAL_USED) \ - _(3429, ZEND_PRE_INC_LONG_SPEC_CV_RETVAL_UNUSED) \ - _(3430, ZEND_PRE_INC_LONG_SPEC_CV_RETVAL_USED) \ - _(3431, ZEND_PRE_DEC_LONG_NO_OVERFLOW_SPEC_CV_RETVAL_UNUSED) \ - _(3432, ZEND_PRE_DEC_LONG_NO_OVERFLOW_SPEC_CV_RETVAL_USED) \ - _(3433, ZEND_PRE_DEC_LONG_SPEC_CV_RETVAL_UNUSED) \ - _(3434, ZEND_PRE_DEC_LONG_SPEC_CV_RETVAL_USED) \ - _(3435, ZEND_POST_INC_LONG_NO_OVERFLOW_SPEC_CV) \ - _(3436, ZEND_POST_INC_LONG_SPEC_CV) \ - _(3437, ZEND_POST_DEC_LONG_NO_OVERFLOW_SPEC_CV) \ - _(3438, ZEND_POST_DEC_LONG_SPEC_CV) \ - _(3439, ZEND_QM_ASSIGN_LONG_SPEC_CONST) \ - _(3440, ZEND_QM_ASSIGN_LONG_SPEC_TMPVARCV) \ - _(3441, ZEND_QM_ASSIGN_LONG_SPEC_TMPVARCV) \ - _(3443, ZEND_QM_ASSIGN_LONG_SPEC_TMPVARCV) \ - _(3444, ZEND_QM_ASSIGN_DOUBLE_SPEC_CONST) \ - _(3445, ZEND_QM_ASSIGN_DOUBLE_SPEC_TMPVARCV) \ - _(3446, ZEND_QM_ASSIGN_DOUBLE_SPEC_TMPVARCV) \ - _(3448, ZEND_QM_ASSIGN_DOUBLE_SPEC_TMPVARCV) \ - _(3449, ZEND_QM_ASSIGN_NOREF_SPEC_CONST) \ - _(3450, ZEND_QM_ASSIGN_NOREF_SPEC_TMPVARCV) \ - _(3451, ZEND_QM_ASSIGN_NOREF_SPEC_TMPVARCV) \ - _(3453, ZEND_QM_ASSIGN_NOREF_SPEC_TMPVARCV) \ - _(3455, ZEND_FETCH_DIM_R_INDEX_SPEC_CONST_TMPVARCV) \ - _(3456, ZEND_FETCH_DIM_R_INDEX_SPEC_CONST_TMPVARCV) \ - _(3458, ZEND_FETCH_DIM_R_INDEX_SPEC_CONST_TMPVARCV) \ - _(3459, ZEND_FETCH_DIM_R_INDEX_SPEC_TMPVAR_CONST) \ - _(3460, ZEND_FETCH_DIM_R_INDEX_SPEC_TMPVAR_TMPVARCV) \ - _(3461, ZEND_FETCH_DIM_R_INDEX_SPEC_TMPVAR_TMPVARCV) \ - _(3463, ZEND_FETCH_DIM_R_INDEX_SPEC_TMPVAR_TMPVARCV) \ - _(3464, ZEND_FETCH_DIM_R_INDEX_SPEC_TMPVAR_CONST) \ - _(3465, ZEND_FETCH_DIM_R_INDEX_SPEC_TMPVAR_TMPVARCV) \ - _(3466, ZEND_FETCH_DIM_R_INDEX_SPEC_TMPVAR_TMPVARCV) \ - _(3468, ZEND_FETCH_DIM_R_INDEX_SPEC_TMPVAR_TMPVARCV) \ - _(3474, ZEND_FETCH_DIM_R_INDEX_SPEC_CV_CONST) \ - _(3475, ZEND_FETCH_DIM_R_INDEX_SPEC_CV_TMPVARCV) \ - _(3476, ZEND_FETCH_DIM_R_INDEX_SPEC_CV_TMPVARCV) \ - _(3478, ZEND_FETCH_DIM_R_INDEX_SPEC_CV_TMPVARCV) \ - _(3481, ZEND_SEND_VAR_SIMPLE_SPEC_VAR) \ - _(3483, ZEND_SEND_VAR_SIMPLE_SPEC_CV) \ - _(3486, ZEND_SEND_VAR_EX_SIMPLE_SPEC_VAR_UNUSED) \ - _(3488, ZEND_SEND_VAR_EX_SIMPLE_SPEC_CV_UNUSED) \ - _(3489, ZEND_SEND_VAL_SIMPLE_SPEC_CONST) \ - _(3490, ZEND_SEND_VAL_EX_SIMPLE_SPEC_CONST) \ - _(3491, ZEND_FE_FETCH_R_SIMPLE_SPEC_VAR_CV_RETVAL_UNUSED) \ - _(3492, ZEND_FE_FETCH_R_SIMPLE_SPEC_VAR_CV_RETVAL_USED) \ - _(3492+1, ZEND_NULL) + _(3427, ZEND_IS_SMALLER_OR_EQUAL_DOUBLE_SPEC_TMPVARCV_TMPVARCV) \ + _(3428, ZEND_IS_SMALLER_OR_EQUAL_DOUBLE_SPEC_TMPVARCV_TMPVARCV_JMPZ) \ + _(3429, ZEND_IS_SMALLER_OR_EQUAL_DOUBLE_SPEC_TMPVARCV_TMPVARCV_JMPNZ) \ + _(3433, ZEND_IS_SMALLER_OR_EQUAL_DOUBLE_SPEC_TMPVARCV_TMPVARCV) \ + _(3434, ZEND_IS_SMALLER_OR_EQUAL_DOUBLE_SPEC_TMPVARCV_TMPVARCV_JMPZ) \ + _(3435, ZEND_IS_SMALLER_OR_EQUAL_DOUBLE_SPEC_TMPVARCV_TMPVARCV_JMPNZ) \ + _(3436, ZEND_PRE_INC_LONG_NO_OVERFLOW_SPEC_CV_RETVAL_UNUSED) \ + _(3437, ZEND_PRE_INC_LONG_NO_OVERFLOW_SPEC_CV_RETVAL_USED) \ + _(3438, ZEND_PRE_INC_LONG_SPEC_CV_RETVAL_UNUSED) \ + _(3439, ZEND_PRE_INC_LONG_SPEC_CV_RETVAL_USED) \ + _(3440, ZEND_PRE_DEC_LONG_NO_OVERFLOW_SPEC_CV_RETVAL_UNUSED) \ + _(3441, ZEND_PRE_DEC_LONG_NO_OVERFLOW_SPEC_CV_RETVAL_USED) \ + _(3442, ZEND_PRE_DEC_LONG_SPEC_CV_RETVAL_UNUSED) \ + _(3443, ZEND_PRE_DEC_LONG_SPEC_CV_RETVAL_USED) \ + _(3444, ZEND_POST_INC_LONG_NO_OVERFLOW_SPEC_CV) \ + _(3445, ZEND_POST_INC_LONG_SPEC_CV) \ + _(3446, ZEND_POST_DEC_LONG_NO_OVERFLOW_SPEC_CV) \ + _(3447, ZEND_POST_DEC_LONG_SPEC_CV) \ + _(3448, ZEND_QM_ASSIGN_LONG_SPEC_CONST) \ + _(3449, ZEND_QM_ASSIGN_LONG_SPEC_TMPVARCV) \ + _(3450, ZEND_QM_ASSIGN_LONG_SPEC_TMPVARCV) \ + _(3452, ZEND_QM_ASSIGN_LONG_SPEC_TMPVARCV) \ + _(3453, ZEND_QM_ASSIGN_DOUBLE_SPEC_CONST) \ + _(3454, ZEND_QM_ASSIGN_DOUBLE_SPEC_TMPVARCV) \ + _(3455, ZEND_QM_ASSIGN_DOUBLE_SPEC_TMPVARCV) \ + _(3457, ZEND_QM_ASSIGN_DOUBLE_SPEC_TMPVARCV) \ + _(3458, ZEND_QM_ASSIGN_NOREF_SPEC_CONST) \ + _(3459, ZEND_QM_ASSIGN_NOREF_SPEC_TMPVARCV) \ + _(3460, ZEND_QM_ASSIGN_NOREF_SPEC_TMPVARCV) \ + _(3462, ZEND_QM_ASSIGN_NOREF_SPEC_TMPVARCV) \ + _(3464, ZEND_FETCH_DIM_R_INDEX_SPEC_CONST_TMPVARCV) \ + _(3465, ZEND_FETCH_DIM_R_INDEX_SPEC_CONST_TMPVARCV) \ + _(3467, ZEND_FETCH_DIM_R_INDEX_SPEC_CONST_TMPVARCV) \ + _(3468, ZEND_FETCH_DIM_R_INDEX_SPEC_TMPVAR_CONST) \ + _(3469, ZEND_FETCH_DIM_R_INDEX_SPEC_TMPVAR_TMPVARCV) \ + _(3470, ZEND_FETCH_DIM_R_INDEX_SPEC_TMPVAR_TMPVARCV) \ + _(3472, ZEND_FETCH_DIM_R_INDEX_SPEC_TMPVAR_TMPVARCV) \ + _(3473, ZEND_FETCH_DIM_R_INDEX_SPEC_TMPVAR_CONST) \ + _(3474, ZEND_FETCH_DIM_R_INDEX_SPEC_TMPVAR_TMPVARCV) \ + _(3475, ZEND_FETCH_DIM_R_INDEX_SPEC_TMPVAR_TMPVARCV) \ + _(3477, ZEND_FETCH_DIM_R_INDEX_SPEC_TMPVAR_TMPVARCV) \ + _(3483, ZEND_FETCH_DIM_R_INDEX_SPEC_CV_CONST) \ + _(3484, ZEND_FETCH_DIM_R_INDEX_SPEC_CV_TMPVARCV) \ + _(3485, ZEND_FETCH_DIM_R_INDEX_SPEC_CV_TMPVARCV) \ + _(3487, ZEND_FETCH_DIM_R_INDEX_SPEC_CV_TMPVARCV) \ + _(3490, ZEND_SEND_VAR_SIMPLE_SPEC_VAR) \ + _(3492, ZEND_SEND_VAR_SIMPLE_SPEC_CV) \ + _(3495, ZEND_SEND_VAR_EX_SIMPLE_SPEC_VAR_UNUSED) \ + _(3497, ZEND_SEND_VAR_EX_SIMPLE_SPEC_CV_UNUSED) \ + _(3498, ZEND_SEND_VAL_SIMPLE_SPEC_CONST) \ + _(3499, ZEND_SEND_VAL_EX_SIMPLE_SPEC_CONST) \ + _(3500, ZEND_FE_FETCH_R_SIMPLE_SPEC_VAR_CV_RETVAL_UNUSED) \ + _(3501, ZEND_FE_FETCH_R_SIMPLE_SPEC_VAR_CV_RETVAL_USED) \ + _(3501+1, ZEND_NULL) diff --git a/Zend/zend_vm_opcodes.c b/Zend/zend_vm_opcodes.c index 00ad38baaafeb..fd36850a48336 100644 --- a/Zend/zend_vm_opcodes.c +++ b/Zend/zend_vm_opcodes.c @@ -22,7 +22,7 @@ #include #include -static const char *zend_vm_opcodes_names[211] = { +static const char *zend_vm_opcodes_names[215] = { "ZEND_NOP", "ZEND_ADD", "ZEND_SUB", @@ -234,9 +234,13 @@ static const char *zend_vm_opcodes_names[211] = { "ZEND_JMP_FRAMELESS", "ZEND_INIT_PARENT_PROPERTY_HOOK_CALL", "ZEND_DECLARE_ATTRIBUTED_CONST", + "ZEND_SEND_PLACEHOLDER", + "ZEND_CALLABLE_CONVERT_PARTIAL", + "ZEND_CHECK_PARTIAL_ARGS", + "ZEND_CALL_PARTIAL", }; -static uint32_t zend_vm_opcodes_flags[211] = { +static uint32_t zend_vm_opcodes_flags[215] = { 0x00000000, 0x00000b0b, 0x00000b0b, @@ -448,6 +452,10 @@ static uint32_t zend_vm_opcodes_flags[211] = { 0x01042003, 0x01001103, 0x00000303, + 0x00000301, + 0x00000001, + 0x00000101, + 0x00000000, }; ZEND_API const char* ZEND_FASTCALL zend_get_opcode_name(uint8_t opcode) { diff --git a/Zend/zend_vm_opcodes.h b/Zend/zend_vm_opcodes.h index 29469bb5f7dca..e625d3779a4eb 100644 --- a/Zend/zend_vm_opcodes.h +++ b/Zend/zend_vm_opcodes.h @@ -294,7 +294,11 @@ END_EXTERN_C() #define ZEND_JMP_FRAMELESS 208 #define ZEND_INIT_PARENT_PROPERTY_HOOK_CALL 209 #define ZEND_DECLARE_ATTRIBUTED_CONST 210 +#define ZEND_SEND_PLACEHOLDER 211 +#define ZEND_CALLABLE_CONVERT_PARTIAL 212 +#define ZEND_CHECK_PARTIAL_ARGS 213 +#define ZEND_CALL_PARTIAL 214 -#define ZEND_VM_LAST_OPCODE 210 +#define ZEND_VM_LAST_OPCODE 214 #endif diff --git a/configure.ac b/configure.ac index 663dc32fd28c4..2663007ca42f1 100644 --- a/configure.ac +++ b/configure.ac @@ -1770,6 +1770,7 @@ PHP_ADD_SOURCES([Zend], m4_normalize([ zend_observer.c zend_opcode.c zend_operators.c + zend_partial.c zend_property_hooks.c zend_ptr_stack.c zend_signal.c diff --git a/ext/opcache/jit/zend_jit.c b/ext/opcache/jit/zend_jit.c index fa2cc10e8b3a0..6a27abd9790fa 100644 --- a/ext/opcache/jit/zend_jit.c +++ b/ext/opcache/jit/zend_jit.c @@ -298,6 +298,7 @@ static int zend_jit_needs_call_chain(zend_call_info *call_info, uint32_t b, cons case ZEND_DO_FCALL_BY_NAME: case ZEND_DO_FCALL: case ZEND_CALLABLE_CONVERT: + case ZEND_CALLABLE_CONVERT_PARTIAL: return 0; case ZEND_SEND_VAL: case ZEND_SEND_VAR: @@ -383,6 +384,7 @@ static int zend_jit_needs_call_chain(zend_call_info *call_info, uint32_t b, cons case ZEND_DO_FCALL_BY_NAME: case ZEND_DO_FCALL: case ZEND_CALLABLE_CONVERT: + case ZEND_CALLABLE_CONVERT_PARTIAL: end = opline; if (end - op_array->opcodes >= ssa->cfg.blocks[b].start + ssa->cfg.blocks[b].len) { /* INIT_FCALL and DO_FCALL in different BasicBlocks */ @@ -862,6 +864,7 @@ static bool zend_jit_dec_call_level(uint8_t opcode) case ZEND_DO_UCALL: case ZEND_DO_FCALL_BY_NAME: case ZEND_CALLABLE_CONVERT: + case ZEND_CALLABLE_CONVERT_PARTIAL: return true; default: return false; diff --git a/ext/opcache/jit/zend_jit_helpers.c b/ext/opcache/jit/zend_jit_helpers.c index b20afffa47df0..f85bd1ef1b788 100644 --- a/ext/opcache/jit/zend_jit_helpers.c +++ b/ext/opcache/jit/zend_jit_helpers.c @@ -306,6 +306,18 @@ static zend_execute_data* ZEND_FASTCALL zend_jit_int_extend_stack_helper(uint32_ return call; } +#ifdef __SANITIZE_ADDRESS__ +static void ZEND_FASTCALL zend_jit_poison_memory_region_helper(void *addr, size_t size) +{ + __asan_poison_memory_region(addr, size); +} + +static void ZEND_FASTCALL zend_jit_unpoison_memory_region_helper(void *addr, size_t size) +{ + __asan_unpoison_memory_region(addr, size); +} +#endif + static zval* ZEND_FASTCALL zend_jit_symtable_find(HashTable *ht, zend_string *str) { zend_ulong idx; diff --git a/ext/opcache/jit/zend_jit_ir.c b/ext/opcache/jit/zend_jit_ir.c index 6aa7896554920..ff6e477705279 100644 --- a/ext/opcache/jit/zend_jit_ir.c +++ b/ext/opcache/jit/zend_jit_ir.c @@ -16,6 +16,7 @@ * +----------------------------------------------------------------------+ */ +#include "zend_partial.h" #include "jit/ir/ir.h" #include "jit/ir/ir_builder.h" @@ -3111,6 +3112,10 @@ static void zend_jit_setup_disasm(void) REGISTER_HELPER(zend_jit_uninit_static_prop); REGISTER_HELPER(zend_jit_rope_end); REGISTER_HELPER(zend_fcall_interrupt); +# ifdef __SANITIZE_ADDRESS__ + REGISTER_HELPER(zend_jit_poison_memory_region_helper); + REGISTER_HELPER(zend_jit_unpoison_memory_region_helper); +# endif #ifndef ZTS REGISTER_DATA(EG(current_execute_data)); @@ -8633,6 +8638,11 @@ static int zend_jit_push_call_frame(zend_jit_ctx *jit, const zend_op *opline, co #endif ir_STORE(ref, ir_ADD_A(top, used_stack_ref)); +#ifdef __SANITIZE_ADDRESS__ + ir_CALL_2(IR_VOID, ir_CONST_FC_FUNC(zend_jit_unpoison_memory_region_helper), + rx, used_stack_ref); +#endif + // JIT: zend_vm_init_call_frame(call, call_info, func, num_args, called_scope, object); if (JIT_G(trigger) != ZEND_JIT_ON_HOT_TRACE || opline->opcode != ZEND_INIT_METHOD_CALL) { // JIT: ZEND_SET_CALL_INFO(call, 0, call_info); @@ -11194,6 +11204,11 @@ static int zend_jit_leave_func(zend_jit_ctx *jit, may_throw = 1; } +#ifdef __SANITIZE_ADDRESS__ + ir_CALL_2(IR_VOID, ir_CONST_FC_FUNC(zend_jit_poison_memory_region_helper), + jit_FP(jit), ir_SUB_A(ir_LOAD_A(jit_EG(vm_stack_top)), jit_FP(jit))); +#endif + // JIT: EG(vm_stack_top) = (zval*)execute_data ir_STORE(jit_EG(vm_stack_top), jit_FP(jit)); diff --git a/ext/opcache/jit/zend_jit_vm_helpers.c b/ext/opcache/jit/zend_jit_vm_helpers.c index 4348fbd53ad48..bb968593c7b9b 100644 --- a/ext/opcache/jit/zend_jit_vm_helpers.c +++ b/ext/opcache/jit/zend_jit_vm_helpers.c @@ -27,6 +27,7 @@ #include #include "Optimizer/zend_func_info.h" #include "Optimizer/zend_call_graph.h" +#include "zend_partial.h" #include "zend_jit.h" #include "zend_jit_internal.h" @@ -59,6 +60,8 @@ ZEND_OPCODE_HANDLER_RET ZEND_FASTCALL zend_jit_leave_nested_func_helper(ZEND_OPC OBJ_RELEASE(Z_OBJ(execute_data->This)); } else if (UNEXPECTED(call_info & ZEND_CALL_CLOSURE)) { OBJ_RELEASE(ZEND_CLOSURE_OBJECT(EX(func))); + } else if (UNEXPECTED(call_info & ZEND_CALL_FAKE_CLOSURE)) { + OBJ_RELEASE(ZEND_PARTIAL_OBJECT(EX(func))); } if (UNEXPECTED(call_info & ZEND_CALL_HAS_EXTRA_NAMED_PARAMS)) { zend_free_extra_named_params(EX(extra_named_params)); @@ -100,6 +103,8 @@ ZEND_OPCODE_HANDLER_RET ZEND_FASTCALL zend_jit_leave_top_func_helper(ZEND_OPCODE } if (UNEXPECTED(call_info & ZEND_CALL_CLOSURE)) { OBJ_RELEASE(ZEND_CLOSURE_OBJECT(EX(func))); + } else if (UNEXPECTED(call_info & ZEND_CALL_FAKE_CLOSURE)) { + OBJ_RELEASE(ZEND_PARTIAL_OBJECT(EX(func))); } execute_data = EG(current_execute_data); #ifdef HAVE_GCC_GLOBAL_REGS @@ -692,7 +697,9 @@ zend_jit_trace_stop ZEND_FASTCALL zend_jit_trace_execute(zend_execute_data *ex, (zend_jit_op_array_trace_extension*)ZEND_FUNC_INFO(op_array); offset = jit_extension->offset; if (!op_array->function_name - || (op_array->fn_flags & ZEND_ACC_CLOSURE)) { + || (op_array->fn_flags & (ZEND_ACC_CLOSURE|ZEND_ACC_FAKE_CLOSURE))) { + /* op_array is a copy that may be freed during tracing. Also, it + * may be bound to a custom scope. Fetch the original op_array. */ op_array = jit_extension->op_array; } @@ -977,7 +984,8 @@ zend_jit_trace_stop ZEND_FASTCALL zend_jit_trace_execute(zend_execute_data *ex, TRACE_RECORD(ZEND_JIT_TRACE_DO_ICALL, 0, func); } } else if (opline->opcode == ZEND_INCLUDE_OR_EVAL - || opline->opcode == ZEND_CALLABLE_CONVERT) { + || opline->opcode == ZEND_CALLABLE_CONVERT + || opline->opcode == ZEND_CALLABLE_CONVERT_PARTIAL) { /* TODO: Support tracing JIT for ZEND_CALLABLE_CONVERT. */ stop = ZEND_JIT_TRACE_STOP_INTERPRETER; break; @@ -1016,7 +1024,9 @@ zend_jit_trace_stop ZEND_FASTCALL zend_jit_trace_execute(zend_execute_data *ex, } offset = jit_extension->offset; if (!op_array->function_name - || (op_array->fn_flags & ZEND_ACC_CLOSURE)) { + || (op_array->fn_flags & (ZEND_ACC_CLOSURE|ZEND_ACC_FAKE_CLOSURE))) { + /* op_array is a copy that may be freed during tracing. Also, it + * may be bound to a custom scope. Fetch the original op_array. */ op_array = jit_extension->op_array; } diff --git a/ext/opcache/zend_file_cache.c b/ext/opcache/zend_file_cache.c index fee90e42b574f..8f9fd87bfb7f4 100644 --- a/ext/opcache/zend_file_cache.c +++ b/ext/opcache/zend_file_cache.c @@ -387,6 +387,7 @@ static void zend_file_cache_serialize_ast(zend_ast *ast, } else if (ast->kind == ZEND_AST_CALLABLE_CONVERT) { zend_ast_fcc *fcc = (zend_ast_fcc*)ast; ZEND_MAP_PTR_INIT(fcc->fptr, NULL); + zend_file_cache_serialize_ast(fcc->args, script, info, buf); } else if (zend_ast_is_decl(ast)) { /* Not implemented. */ ZEND_UNREACHABLE(); @@ -1287,6 +1288,7 @@ static void zend_file_cache_unserialize_ast(zend_ast *ast, } else if (ast->kind == ZEND_AST_CALLABLE_CONVERT) { zend_ast_fcc *fcc = (zend_ast_fcc*)ast; ZEND_MAP_PTR_NEW(fcc->fptr); + zend_file_cache_unserialize_ast(fcc->args, script, buf); } else if (zend_ast_is_decl(ast)) { /* Not implemented. */ ZEND_UNREACHABLE(); @@ -2074,7 +2076,7 @@ void zend_file_cache_invalidate(zend_string *full_path) if (ZCG(accel_directives).file_cache_read_only) { return; } - + char *filename; filename = zend_file_cache_get_bin_file_path(full_path); diff --git a/ext/opcache/zend_persist.c b/ext/opcache/zend_persist.c index 05a7cb00077c1..c74843dbb2f7f 100644 --- a/ext/opcache/zend_persist.c +++ b/ext/opcache/zend_persist.c @@ -197,6 +197,7 @@ static zend_ast *zend_persist_ast(zend_ast *ast) node = (zend_ast *) copy; } else if (ast->kind == ZEND_AST_CALLABLE_CONVERT) { zend_ast_fcc *copy = zend_shared_memdup(ast, sizeof(zend_ast_fcc)); + copy->args = zend_persist_ast(copy->args); node = (zend_ast *) copy; } else if (zend_ast_is_decl(ast)) { /* Not implemented. */ diff --git a/ext/opcache/zend_persist_calc.c b/ext/opcache/zend_persist_calc.c index 639d7d5446705..2d79b4dd4e2e5 100644 --- a/ext/opcache/zend_persist_calc.c +++ b/ext/opcache/zend_persist_calc.c @@ -92,7 +92,9 @@ static void zend_persist_ast_calc(zend_ast *ast) ZVAL_PTR(&z, zend_ast_get_op_array(ast)->op_array); zend_persist_op_array_calc(&z); } else if (ast->kind == ZEND_AST_CALLABLE_CONVERT) { + zend_ast_fcc *fcc_ast = (zend_ast_fcc*)ast; ADD_SIZE(sizeof(zend_ast_fcc)); + zend_persist_ast_calc(fcc_ast->args); } else if (zend_ast_is_decl(ast)) { /* Not implemented. */ ZEND_UNREACHABLE(); diff --git a/ext/reflection/php_reflection.c b/ext/reflection/php_reflection.c index 8ef0269481cf7..0bd6a1696f659 100644 --- a/ext/reflection/php_reflection.c +++ b/ext/reflection/php_reflection.c @@ -51,6 +51,7 @@ #include "zend_smart_str.h" #include "zend_enum.h" #include "zend_fibers.h" +#include "zend_partial.h" #define REFLECTION_ATTRIBUTE_IS_INSTANCEOF (1 << 1) @@ -206,6 +207,7 @@ static zend_function *_copy_function(zend_function *fptr) /* {{{ */ zend_function *copy_fptr; copy_fptr = emalloc(sizeof(zend_function)); memcpy(copy_fptr, fptr, sizeof(zend_function)); + copy_fptr->internal_function.fn_flags &= ~ZEND_ACC_TRAMPOLINE_PERMANENT; copy_fptr->internal_function.function_name = zend_string_copy(fptr->internal_function.function_name); return copy_fptr; } else { @@ -218,7 +220,8 @@ static zend_function *_copy_function(zend_function *fptr) /* {{{ */ static void _free_function(zend_function *fptr) /* {{{ */ { if (fptr - && (fptr->internal_function.fn_flags & ZEND_ACC_CALL_VIA_TRAMPOLINE)) + && (fptr->internal_function.fn_flags & ZEND_ACC_CALL_VIA_TRAMPOLINE) + && !(fptr->internal_function.fn_flags & ZEND_ACC_TRAMPOLINE_PERMANENT)) { zend_string_release_ex(fptr->internal_function.function_name, 0); zend_free_trampoline(fptr); @@ -703,16 +706,22 @@ static zend_op *get_recv_op(const zend_op_array *op_array, uint32_t offset) } ++op; } - ZEND_ASSERT(0 && "Failed to find op"); + return NULL; } static zval *get_default_from_recv(zend_op_array *op_array, uint32_t offset) { + zend_op *recv = get_recv_op(op_array, offset); - if (!recv || recv->opcode != ZEND_RECV_INIT) { + + if (!recv) { return NULL; } + if (recv->opcode != ZEND_RECV_INIT) { + return NULL; + } + return RT_CONSTANT(recv, recv->op2); } @@ -805,7 +814,13 @@ static void _parameter_string(smart_str *str, zend_function *fptr, struct _zend_ smart_str_appends(str, ""); } } else { - zval *default_value = get_default_from_recv((zend_op_array*)fptr, offset); + zval *default_value; + if (zend_is_partial_trampoline(fptr)) { + default_value = zend_partial_get_param_default_value( + ZEND_PARTIAL_OBJECT_FROM_TRAMPOLINE(fptr), offset); + } else { + default_value = get_default_from_recv(&fptr->op_array, offset); + } if (default_value) { smart_str_appends(str, " = "); if (format_default_value(str, default_value) == FAILURE) { @@ -890,7 +905,18 @@ static void _function_string(smart_str *str, zend_function *fptr, zend_class_ent } smart_str_appendl(str, indent, strlen(indent)); - smart_str_appends(str, fptr->common.fn_flags & ZEND_ACC_CLOSURE ? "Closure [ " : (fptr->common.scope ? "Method [ " : "Function [ ")); + const char *prefix = "Function [ "; + if (fptr->common.fn_flags & (ZEND_ACC_CLOSURE|ZEND_ACC_FAKE_CLOSURE)) { + if (zend_is_partial_trampoline(fptr)) { + prefix = "Partial [ "; + } else { + prefix = "Closure [ "; + } + } else if (fptr->common.scope) { + prefix = "Method [ "; + } + + smart_str_appends(str, prefix); smart_str_appends(str, (fptr->type == ZEND_USER_FUNCTION) ? "common.fn_flags & ZEND_ACC_DEPRECATED) { smart_str_appends(str, ", deprecated"); @@ -1750,7 +1776,9 @@ ZEND_METHOD(ReflectionFunction, __construct) zval_ptr_dtor(reflection_prop_name(object)); } - ZVAL_STR_COPY(reflection_prop_name(object), fptr->common.function_name); + if (fptr->common.function_name) { + ZVAL_STR_COPY(reflection_prop_name(object), fptr->common.function_name); + } intern->ptr = fptr; intern->ref_type = REF_TYPE_FUNCTION; if (closure_obj) { @@ -1802,6 +1830,21 @@ ZEND_METHOD(ReflectionFunctionAbstract, isClosure) } /* }}} */ +/* {{{ Returns whether this is a partial closure */ +ZEND_METHOD(ReflectionFunctionAbstract, isPartial) +{ + reflection_object *intern; + zend_function *fptr; + + if (zend_parse_parameters_none() == FAILURE) { + RETURN_THROWS(); + } + GET_REFLECTION_OBJECT_PTR(fptr); + + RETURN_BOOL(zend_is_partial_trampoline(fptr)); +} +/* }}} */ + /* {{{ Returns this pointer bound to closure */ ZEND_METHOD(ReflectionFunctionAbstract, getClosureThis) { @@ -1813,7 +1856,7 @@ ZEND_METHOD(ReflectionFunctionAbstract, getClosureThis) GET_REFLECTION_OBJECT(); if (!Z_ISUNDEF(intern->obj)) { closure_this = zend_get_closure_this_ptr(&intern->obj); - if (!Z_ISUNDEF_P(closure_this)) { + if (closure_this && Z_TYPE_P(closure_this) == IS_OBJECT) { RETURN_OBJ_COPY(Z_OBJ_P(closure_this)); } } @@ -1830,6 +1873,7 @@ ZEND_METHOD(ReflectionFunctionAbstract, getClosureScopeClass) GET_REFLECTION_OBJECT(); if (!Z_ISUNDEF(intern->obj)) { closure_func = zend_get_closure_method_def(Z_OBJ(intern->obj)); + if (closure_func && closure_func->common.scope) { zend_reflection_class_factory(closure_func->common.scope, return_value); } diff --git a/ext/reflection/php_reflection.stub.php b/ext/reflection/php_reflection.stub.php index 63518b446ad86..3f4618f68ebff 100644 --- a/ext/reflection/php_reflection.stub.php +++ b/ext/reflection/php_reflection.stub.php @@ -30,6 +30,8 @@ public function inNamespace(): bool {} /** @tentative-return-type */ public function isClosure(): bool {} + public function isPartial(): bool {} + /** @tentative-return-type */ public function isDeprecated(): bool {} diff --git a/ext/reflection/php_reflection_arginfo.h b/ext/reflection/php_reflection_arginfo.h index d50dc04ae3d15..0d6b118145f53 100644 --- a/ext/reflection/php_reflection_arginfo.h +++ b/ext/reflection/php_reflection_arginfo.h @@ -1,5 +1,5 @@ /* This is a generated file, edit the .stub.php file instead. - * Stub hash: 7a8d126a96f0115783bd20a9adfc6bdc5ee88fda */ + * Stub hash: e2d1f96012efd0d2dffe6c9f745f1156e009239e */ ZEND_BEGIN_ARG_WITH_TENTATIVE_RETURN_TYPE_INFO_EX(arginfo_class_Reflection_getModifierNames, 0, 1, IS_ARRAY, 0) ZEND_ARG_TYPE_INFO(0, modifiers, IS_LONG, 0) @@ -13,6 +13,9 @@ ZEND_END_ARG_INFO() #define arginfo_class_ReflectionFunctionAbstract_isClosure arginfo_class_ReflectionFunctionAbstract_inNamespace +ZEND_BEGIN_ARG_WITH_RETURN_TYPE_INFO_EX(arginfo_class_ReflectionFunctionAbstract_isPartial, 0, 0, _IS_BOOL, 0) +ZEND_END_ARG_INFO() + #define arginfo_class_ReflectionFunctionAbstract_isDeprecated arginfo_class_ReflectionFunctionAbstract_inNamespace #define arginfo_class_ReflectionFunctionAbstract_isInternal arginfo_class_ReflectionFunctionAbstract_inNamespace @@ -75,8 +78,7 @@ ZEND_END_ARG_INFO() ZEND_BEGIN_ARG_WITH_TENTATIVE_RETURN_OBJ_INFO_EX(arginfo_class_ReflectionFunctionAbstract_getReturnType, 0, 0, ReflectionType, 1) ZEND_END_ARG_INFO() -ZEND_BEGIN_ARG_WITH_RETURN_TYPE_INFO_EX(arginfo_class_ReflectionFunctionAbstract_hasTentativeReturnType, 0, 0, _IS_BOOL, 0) -ZEND_END_ARG_INFO() +#define arginfo_class_ReflectionFunctionAbstract_hasTentativeReturnType arginfo_class_ReflectionFunctionAbstract_isPartial ZEND_BEGIN_ARG_WITH_RETURN_OBJ_INFO_EX(arginfo_class_ReflectionFunctionAbstract_getTentativeReturnType, 0, 0, ReflectionType, 1) ZEND_END_ARG_INFO() @@ -93,7 +95,7 @@ ZEND_END_ARG_INFO() ZEND_BEGIN_ARG_WITH_RETURN_TYPE_INFO_EX(arginfo_class_ReflectionFunction___toString, 0, 0, IS_STRING, 0) ZEND_END_ARG_INFO() -#define arginfo_class_ReflectionFunction_isAnonymous arginfo_class_ReflectionFunctionAbstract_hasTentativeReturnType +#define arginfo_class_ReflectionFunction_isAnonymous arginfo_class_ReflectionFunctionAbstract_isPartial #define arginfo_class_ReflectionFunction_isDisabled arginfo_class_ReflectionFunctionAbstract_inNamespace @@ -130,7 +132,7 @@ ZEND_END_ARG_INFO() ZEND_BEGIN_ARG_WITH_RETURN_OBJ_INFO_EX(arginfo_class_ReflectionGenerator_getExecutingGenerator, 0, 0, Generator, 0) ZEND_END_ARG_INFO() -#define arginfo_class_ReflectionGenerator_isClosed arginfo_class_ReflectionFunctionAbstract_hasTentativeReturnType +#define arginfo_class_ReflectionGenerator_isClosed arginfo_class_ReflectionFunctionAbstract_isPartial ZEND_BEGIN_ARG_INFO_EX(arginfo_class_ReflectionMethod___construct, 0, 0, 1) ZEND_ARG_TYPE_MASK(0, objectOrMethod, MAY_BE_OBJECT|MAY_BE_STRING, NULL) @@ -179,7 +181,7 @@ ZEND_END_ARG_INFO() ZEND_BEGIN_ARG_WITH_TENTATIVE_RETURN_OBJ_INFO_EX(arginfo_class_ReflectionMethod_getPrototype, 0, 0, ReflectionMethod, 0) ZEND_END_ARG_INFO() -#define arginfo_class_ReflectionMethod_hasPrototype arginfo_class_ReflectionFunctionAbstract_hasTentativeReturnType +#define arginfo_class_ReflectionMethod_hasPrototype arginfo_class_ReflectionFunctionAbstract_isPartial ZEND_BEGIN_ARG_WITH_TENTATIVE_RETURN_TYPE_INFO_EX(arginfo_class_ReflectionMethod_setAccessible, 0, 1, IS_VOID, 0) ZEND_ARG_TYPE_INFO(0, accessible, _IS_BOOL, 0) @@ -264,13 +266,13 @@ ZEND_END_ARG_INFO() #define arginfo_class_ReflectionClass_isTrait arginfo_class_ReflectionFunctionAbstract_inNamespace -#define arginfo_class_ReflectionClass_isEnum arginfo_class_ReflectionFunctionAbstract_hasTentativeReturnType +#define arginfo_class_ReflectionClass_isEnum arginfo_class_ReflectionFunctionAbstract_isPartial #define arginfo_class_ReflectionClass_isAbstract arginfo_class_ReflectionFunctionAbstract_inNamespace #define arginfo_class_ReflectionClass_isFinal arginfo_class_ReflectionFunctionAbstract_inNamespace -#define arginfo_class_ReflectionClass_isReadOnly arginfo_class_ReflectionFunctionAbstract_hasTentativeReturnType +#define arginfo_class_ReflectionClass_isReadOnly arginfo_class_ReflectionFunctionAbstract_isPartial #define arginfo_class_ReflectionClass_getModifiers arginfo_class_ReflectionFunctionAbstract_getNumberOfParameters @@ -417,23 +419,23 @@ ZEND_END_ARG_INFO() #define arginfo_class_ReflectionProperty_isProtected arginfo_class_ReflectionFunctionAbstract_inNamespace -#define arginfo_class_ReflectionProperty_isPrivateSet arginfo_class_ReflectionFunctionAbstract_hasTentativeReturnType +#define arginfo_class_ReflectionProperty_isPrivateSet arginfo_class_ReflectionFunctionAbstract_isPartial -#define arginfo_class_ReflectionProperty_isProtectedSet arginfo_class_ReflectionFunctionAbstract_hasTentativeReturnType +#define arginfo_class_ReflectionProperty_isProtectedSet arginfo_class_ReflectionFunctionAbstract_isPartial #define arginfo_class_ReflectionProperty_isStatic arginfo_class_ReflectionFunctionAbstract_inNamespace -#define arginfo_class_ReflectionProperty_isReadOnly arginfo_class_ReflectionFunctionAbstract_hasTentativeReturnType +#define arginfo_class_ReflectionProperty_isReadOnly arginfo_class_ReflectionFunctionAbstract_isPartial #define arginfo_class_ReflectionProperty_isDefault arginfo_class_ReflectionFunctionAbstract_inNamespace -#define arginfo_class_ReflectionProperty_isDynamic arginfo_class_ReflectionFunctionAbstract_hasTentativeReturnType +#define arginfo_class_ReflectionProperty_isDynamic arginfo_class_ReflectionFunctionAbstract_isPartial -#define arginfo_class_ReflectionProperty_isAbstract arginfo_class_ReflectionFunctionAbstract_hasTentativeReturnType +#define arginfo_class_ReflectionProperty_isAbstract arginfo_class_ReflectionFunctionAbstract_isPartial -#define arginfo_class_ReflectionProperty_isVirtual arginfo_class_ReflectionFunctionAbstract_hasTentativeReturnType +#define arginfo_class_ReflectionProperty_isVirtual arginfo_class_ReflectionFunctionAbstract_isPartial -#define arginfo_class_ReflectionProperty_isPromoted arginfo_class_ReflectionFunctionAbstract_hasTentativeReturnType +#define arginfo_class_ReflectionProperty_isPromoted arginfo_class_ReflectionFunctionAbstract_isPartial #define arginfo_class_ReflectionProperty_getModifiers arginfo_class_ReflectionFunctionAbstract_getNumberOfParameters @@ -449,14 +451,14 @@ ZEND_END_ARG_INFO() #define arginfo_class_ReflectionProperty_hasType arginfo_class_ReflectionFunctionAbstract_inNamespace -#define arginfo_class_ReflectionProperty_hasDefaultValue arginfo_class_ReflectionFunctionAbstract_hasTentativeReturnType +#define arginfo_class_ReflectionProperty_hasDefaultValue arginfo_class_ReflectionFunctionAbstract_isPartial ZEND_BEGIN_ARG_WITH_TENTATIVE_RETURN_TYPE_INFO_EX(arginfo_class_ReflectionProperty_getDefaultValue, 0, 0, IS_MIXED, 0) ZEND_END_ARG_INFO() #define arginfo_class_ReflectionProperty_getAttributes arginfo_class_ReflectionFunctionAbstract_getAttributes -#define arginfo_class_ReflectionProperty_hasHooks arginfo_class_ReflectionFunctionAbstract_hasTentativeReturnType +#define arginfo_class_ReflectionProperty_hasHooks arginfo_class_ReflectionFunctionAbstract_isPartial #define arginfo_class_ReflectionProperty_getHooks arginfo_class_ReflectionFunctionAbstract_getClosureUsedVariables @@ -468,7 +470,7 @@ ZEND_BEGIN_ARG_WITH_RETURN_OBJ_INFO_EX(arginfo_class_ReflectionProperty_getHook, ZEND_ARG_OBJ_INFO(0, type, PropertyHookType, 0) ZEND_END_ARG_INFO() -#define arginfo_class_ReflectionProperty_isFinal arginfo_class_ReflectionFunctionAbstract_hasTentativeReturnType +#define arginfo_class_ReflectionProperty_isFinal arginfo_class_ReflectionFunctionAbstract_isPartial #define arginfo_class_ReflectionClassConstant___clone arginfo_class_ReflectionFunctionAbstract___clone @@ -489,7 +491,7 @@ ZEND_END_ARG_INFO() #define arginfo_class_ReflectionClassConstant_isProtected arginfo_class_ReflectionFunctionAbstract_inNamespace -#define arginfo_class_ReflectionClassConstant_isFinal arginfo_class_ReflectionFunctionAbstract_hasTentativeReturnType +#define arginfo_class_ReflectionClassConstant_isFinal arginfo_class_ReflectionFunctionAbstract_isPartial #define arginfo_class_ReflectionClassConstant_getModifiers arginfo_class_ReflectionFunctionAbstract_getNumberOfParameters @@ -499,11 +501,11 @@ ZEND_END_ARG_INFO() #define arginfo_class_ReflectionClassConstant_getAttributes arginfo_class_ReflectionFunctionAbstract_getAttributes -#define arginfo_class_ReflectionClassConstant_isEnumCase arginfo_class_ReflectionFunctionAbstract_hasTentativeReturnType +#define arginfo_class_ReflectionClassConstant_isEnumCase arginfo_class_ReflectionFunctionAbstract_isPartial -#define arginfo_class_ReflectionClassConstant_isDeprecated arginfo_class_ReflectionFunctionAbstract_hasTentativeReturnType +#define arginfo_class_ReflectionClassConstant_isDeprecated arginfo_class_ReflectionFunctionAbstract_isPartial -#define arginfo_class_ReflectionClassConstant_hasType arginfo_class_ReflectionFunctionAbstract_hasTentativeReturnType +#define arginfo_class_ReflectionClassConstant_hasType arginfo_class_ReflectionFunctionAbstract_isPartial #define arginfo_class_ReflectionClassConstant_getType arginfo_class_ReflectionFunctionAbstract_getTentativeReturnType @@ -554,7 +556,7 @@ ZEND_END_ARG_INFO() #define arginfo_class_ReflectionParameter_isVariadic arginfo_class_ReflectionFunctionAbstract_inNamespace -#define arginfo_class_ReflectionParameter_isPromoted arginfo_class_ReflectionFunctionAbstract_hasTentativeReturnType +#define arginfo_class_ReflectionParameter_isPromoted arginfo_class_ReflectionFunctionAbstract_isPartial #define arginfo_class_ReflectionParameter_getAttributes arginfo_class_ReflectionFunctionAbstract_getAttributes @@ -635,7 +637,7 @@ ZEND_END_ARG_INFO() #define arginfo_class_ReflectionAttribute_getTarget arginfo_class_ReflectionGenerator_getExecutingLine -#define arginfo_class_ReflectionAttribute_isRepeated arginfo_class_ReflectionFunctionAbstract_hasTentativeReturnType +#define arginfo_class_ReflectionAttribute_isRepeated arginfo_class_ReflectionFunctionAbstract_isPartial #define arginfo_class_ReflectionAttribute_getArguments arginfo_class_ReflectionFunctionAbstract_getClosureUsedVariables @@ -660,7 +662,7 @@ ZEND_END_ARG_INFO() #define arginfo_class_ReflectionEnum_getCases arginfo_class_ReflectionFunctionAbstract_getClosureUsedVariables -#define arginfo_class_ReflectionEnum_isBacked arginfo_class_ReflectionFunctionAbstract_hasTentativeReturnType +#define arginfo_class_ReflectionEnum_isBacked arginfo_class_ReflectionFunctionAbstract_isPartial ZEND_BEGIN_ARG_WITH_RETURN_OBJ_INFO_EX(arginfo_class_ReflectionEnum_getBackingType, 0, 0, ReflectionNamedType, 1) ZEND_END_ARG_INFO() @@ -707,7 +709,7 @@ ZEND_END_ARG_INFO() ZEND_BEGIN_ARG_WITH_RETURN_TYPE_INFO_EX(arginfo_class_ReflectionConstant_getValue, 0, 0, IS_MIXED, 0) ZEND_END_ARG_INFO() -#define arginfo_class_ReflectionConstant_isDeprecated arginfo_class_ReflectionFunctionAbstract_hasTentativeReturnType +#define arginfo_class_ReflectionConstant_isDeprecated arginfo_class_ReflectionFunctionAbstract_isPartial ZEND_BEGIN_ARG_WITH_RETURN_TYPE_MASK_EX(arginfo_class_ReflectionConstant_getFileName, 0, 0, MAY_BE_STRING|MAY_BE_FALSE) ZEND_END_ARG_INFO() @@ -725,6 +727,7 @@ ZEND_METHOD(Reflection, getModifierNames); ZEND_METHOD(ReflectionClass, __clone); ZEND_METHOD(ReflectionFunctionAbstract, inNamespace); ZEND_METHOD(ReflectionFunctionAbstract, isClosure); +ZEND_METHOD(ReflectionFunctionAbstract, isPartial); ZEND_METHOD(ReflectionFunctionAbstract, isDeprecated); ZEND_METHOD(ReflectionFunctionAbstract, isInternal); ZEND_METHOD(ReflectionFunctionAbstract, isUserDefined); @@ -1000,6 +1003,7 @@ static const zend_function_entry class_ReflectionFunctionAbstract_methods[] = { ZEND_RAW_FENTRY("__clone", zim_ReflectionClass___clone, arginfo_class_ReflectionFunctionAbstract___clone, ZEND_ACC_PRIVATE, NULL, NULL) ZEND_ME(ReflectionFunctionAbstract, inNamespace, arginfo_class_ReflectionFunctionAbstract_inNamespace, ZEND_ACC_PUBLIC) ZEND_ME(ReflectionFunctionAbstract, isClosure, arginfo_class_ReflectionFunctionAbstract_isClosure, ZEND_ACC_PUBLIC) + ZEND_ME(ReflectionFunctionAbstract, isPartial, arginfo_class_ReflectionFunctionAbstract_isPartial, ZEND_ACC_PUBLIC) ZEND_ME(ReflectionFunctionAbstract, isDeprecated, arginfo_class_ReflectionFunctionAbstract_isDeprecated, ZEND_ACC_PUBLIC) ZEND_ME(ReflectionFunctionAbstract, isInternal, arginfo_class_ReflectionFunctionAbstract_isInternal, ZEND_ACC_PUBLIC) ZEND_ME(ReflectionFunctionAbstract, isUserDefined, arginfo_class_ReflectionFunctionAbstract_isUserDefined, ZEND_ACC_PUBLIC) diff --git a/ext/zend_test/fiber.c b/ext/zend_test/fiber.c index 199d1b28b8cd6..70be0177c2e31 100644 --- a/ext/zend_test/fiber.c +++ b/ext/zend_test/fiber.c @@ -89,6 +89,9 @@ static ZEND_STACK_ALIGNED void zend_test_fiber_execute(zend_fiber_transfer *tran EG(vm_stack_page_size) = ZEND_FIBER_VM_STACK_SIZE; execute_data = (zend_execute_data *) stack->top; +#ifdef __SANITIZE_ADDRESS__ + __asan_unpoison_memory_region(stack->top, sizeof(zend_execute_data)); +#endif memset(execute_data, 0, sizeof(zend_execute_data)); execute_data->func = (zend_function *) &zend_pass_function; diff --git a/win32/build/config.w32 b/win32/build/config.w32 index 403f0aa6efbfe..1efdc57bca4da 100644 --- a/win32/build/config.w32 +++ b/win32/build/config.w32 @@ -241,7 +241,7 @@ ADD_SOURCES("Zend", "zend_language_parser.c zend_language_scanner.c \ zend_float.c zend_string.c zend_generators.c zend_virtual_cwd.c zend_ast.c \ zend_inheritance.c zend_smart_str.c zend_cpuinfo.c zend_observer.c zend_system_id.c \ zend_enum.c zend_fibers.c zend_atomic.c zend_hrtime.c zend_frameless_function.c zend_property_hooks.c \ - zend_lazy_objects.c"); + zend_lazy_objects.c zend_partial.c"); ADD_SOURCES("Zend\\Optimizer", "zend_optimizer.c pass1.c pass3.c optimize_func_calls.c block_pass.c optimize_temp_vars_5.c nop_removal.c compact_literals.c zend_cfg.c zend_dfg.c dfa_pass.c zend_ssa.c zend_inference.c zend_func_info.c zend_call_graph.c zend_dump.c escape_analysis.c compact_vars.c dce.c sccp.c scdf.c"); var PHP_ASSEMBLER = PATH_PROG({