From be1ea82dcd1301d1a856383a9597739277cf0fef Mon Sep 17 00:00:00 2001 From: Joe Watkins Date: Tue, 20 Apr 2021 01:43:36 +0200 Subject: [PATCH 01/28] partial application --- Zend/Optimizer/zend_call_graph.c | 2 + .../compile_errors_001.phpt | 9 + .../compile_errors_002.phpt | 9 + .../compile_errors_003.phpt | 9 + .../compile_errors_004.phpt | 8 + .../compile_errors_005.phpt | 9 + .../compile_errors_006.phpt | 9 + .../compile_errors_007.phpt | 9 + .../tests/partial_application/errors_001.phpt | 38 + .../tests/partial_application/errors_002.phpt | 17 + .../tests/partial_application/errors_003.phpt | 78 ++ .../tests/partial_application/export_001.phpt | 14 + .../extra_collect_001.phpt | 50 + .../partial_application/factory_001.phpt | 24 + .../partial_application/factory_002.phpt | 30 + .../partial_application/factory_003.phpt | 24 + .../partial_application/factory_004.phpt | 18 + Zend/tests/partial_application/magic_001.phpt | 76 ++ Zend/tests/partial_application/magic_002.phpt | 60 ++ Zend/tests/partial_application/magic_003.phpt | 13 + Zend/tests/partial_application/magic_004.phpt | 13 + Zend/tests/partial_application/magic_005.phpt | 33 + .../partial_application/reflection_001.phpt | 62 ++ .../partial_application/reflection_002.phpt | 57 ++ .../partial_application/reflection_003.phpt | 41 + .../partial_application/return_type_001.phpt | 17 + .../static_method_001.phpt | 18 + .../partial_application/statics_001.phpt | 22 + Zend/tests/partial_application/this_001.phpt | 22 + .../variation_apply_002.phpt | 18 + .../variation_bind_001.phpt | 74 ++ .../variation_bind_002.phpt | 67 ++ .../variation_call_001.phpt | 32 + .../variation_closure_001.phpt | 19 + .../variation_closure_002.phpt | 23 + .../variation_closure_003.phpt | 42 + .../variation_debug_001.phpt | 36 + .../variation_debug_002.phpt | 48 + .../partial_application/variation_ex_001.phpt | 14 + .../variation_factory_001.phpt | 17 + .../partial_application/variation_gc_001.phpt | 18 + .../partial_application/variation_gc_002.phpt | 11 + .../partial_application/variation_gc_003.phpt | 15 + .../variation_invoke_001.phpt | 23 + .../variation_nocall_001.phpt | 12 + .../variation_nocall_002.phpt | 14 + .../variation_parent_001.phpt | 47 + .../variation_pass_001.phpt | 14 + .../variation_scope_001.phpt | 18 + .../variation_strict_001.phpt | 21 + .../variation_variadics_001.phpt | 26 + .../variation_variadics_002.phpt | 19 + .../variation_variadics_003.phpt | 50 + .../variation_variadics_004.phpt | 65 ++ .../variation_variadics_006.phpt | 18 + .../variation_variadics_007.phpt | 14 + .../variation_variadics_008.phpt | 17 + Zend/zend.c | 3 + Zend/zend_ast.c | 7 + Zend/zend_ast.h | 1 + Zend/zend_closures.c | 45 +- Zend/zend_closures.h | 1 + Zend/zend_compile.c | 102 +- Zend/zend_compile.h | 7 + Zend/zend_execute.c | 27 +- Zend/zend_language_parser.y | 2 + Zend/zend_object_handlers.c | 12 +- Zend/zend_object_handlers.h | 2 +- Zend/zend_partial.c | 939 ++++++++++++++++++ Zend/zend_partial.h | 42 + Zend/zend_types.h | 4 + Zend/zend_vm_def.h | 90 +- Zend/zend_vm_execute.h | 299 ++++-- Zend/zend_vm_handlers.h | 751 +++++++------- Zend/zend_vm_opcodes.c | 10 +- Zend/zend_vm_opcodes.h | 5 +- configure.ac | 1 + ext/reflection/php_reflection.c | 72 +- win32/build/config.w32 | 2 +- 79 files changed, 3500 insertions(+), 507 deletions(-) create mode 100644 Zend/tests/partial_application/compile_errors_001.phpt create mode 100644 Zend/tests/partial_application/compile_errors_002.phpt create mode 100644 Zend/tests/partial_application/compile_errors_003.phpt create mode 100644 Zend/tests/partial_application/compile_errors_004.phpt create mode 100644 Zend/tests/partial_application/compile_errors_005.phpt create mode 100644 Zend/tests/partial_application/compile_errors_006.phpt create mode 100644 Zend/tests/partial_application/compile_errors_007.phpt create mode 100644 Zend/tests/partial_application/errors_001.phpt create mode 100644 Zend/tests/partial_application/errors_002.phpt create mode 100644 Zend/tests/partial_application/errors_003.phpt create mode 100644 Zend/tests/partial_application/export_001.phpt create mode 100644 Zend/tests/partial_application/extra_collect_001.phpt create mode 100644 Zend/tests/partial_application/factory_001.phpt create mode 100644 Zend/tests/partial_application/factory_002.phpt create mode 100644 Zend/tests/partial_application/factory_003.phpt create mode 100644 Zend/tests/partial_application/factory_004.phpt create mode 100644 Zend/tests/partial_application/magic_001.phpt create mode 100644 Zend/tests/partial_application/magic_002.phpt create mode 100644 Zend/tests/partial_application/magic_003.phpt create mode 100644 Zend/tests/partial_application/magic_004.phpt create mode 100644 Zend/tests/partial_application/magic_005.phpt create mode 100644 Zend/tests/partial_application/reflection_001.phpt create mode 100644 Zend/tests/partial_application/reflection_002.phpt create mode 100644 Zend/tests/partial_application/reflection_003.phpt create mode 100644 Zend/tests/partial_application/return_type_001.phpt create mode 100644 Zend/tests/partial_application/static_method_001.phpt create mode 100644 Zend/tests/partial_application/statics_001.phpt create mode 100644 Zend/tests/partial_application/this_001.phpt create mode 100644 Zend/tests/partial_application/variation_apply_002.phpt create mode 100644 Zend/tests/partial_application/variation_bind_001.phpt create mode 100644 Zend/tests/partial_application/variation_bind_002.phpt create mode 100644 Zend/tests/partial_application/variation_call_001.phpt create mode 100644 Zend/tests/partial_application/variation_closure_001.phpt create mode 100644 Zend/tests/partial_application/variation_closure_002.phpt create mode 100644 Zend/tests/partial_application/variation_closure_003.phpt create mode 100644 Zend/tests/partial_application/variation_debug_001.phpt create mode 100644 Zend/tests/partial_application/variation_debug_002.phpt create mode 100644 Zend/tests/partial_application/variation_ex_001.phpt create mode 100644 Zend/tests/partial_application/variation_factory_001.phpt create mode 100644 Zend/tests/partial_application/variation_gc_001.phpt create mode 100644 Zend/tests/partial_application/variation_gc_002.phpt create mode 100644 Zend/tests/partial_application/variation_gc_003.phpt create mode 100644 Zend/tests/partial_application/variation_invoke_001.phpt create mode 100644 Zend/tests/partial_application/variation_nocall_001.phpt create mode 100644 Zend/tests/partial_application/variation_nocall_002.phpt create mode 100644 Zend/tests/partial_application/variation_parent_001.phpt create mode 100644 Zend/tests/partial_application/variation_pass_001.phpt create mode 100644 Zend/tests/partial_application/variation_scope_001.phpt create mode 100644 Zend/tests/partial_application/variation_strict_001.phpt create mode 100644 Zend/tests/partial_application/variation_variadics_001.phpt create mode 100644 Zend/tests/partial_application/variation_variadics_002.phpt create mode 100644 Zend/tests/partial_application/variation_variadics_003.phpt create mode 100644 Zend/tests/partial_application/variation_variadics_004.phpt create mode 100644 Zend/tests/partial_application/variation_variadics_006.phpt create mode 100644 Zend/tests/partial_application/variation_variadics_007.phpt create mode 100644 Zend/tests/partial_application/variation_variadics_008.phpt create mode 100644 Zend/zend_partial.c create mode 100644 Zend/zend_partial.h diff --git a/Zend/Optimizer/zend_call_graph.c b/Zend/Optimizer/zend_call_graph.c index 8a2f8ea2a7e1..b6a8aa3f7c5d 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_DO_FCALL_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/tests/partial_application/compile_errors_001.phpt b/Zend/tests/partial_application/compile_errors_001.phpt new file mode 100644 index 000000000000..73eafa4e2cd4 --- /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 000000000000..29a346ec88a5 --- /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 000000000000..6d833b7ef1b6 --- /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: Named arguments must come after all placeholders 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 000000000000..efd544282c2e --- /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: Named arguments must come after all placeholders 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 000000000000..b6a898008617 --- /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 000000000000..21ad1a954e92 --- /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 %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 000000000000..5cc881ebf60f --- /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 000000000000..d6e37051c1ec --- /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 000000000000..2f852348fb1f --- /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 000000000000..0ce69bc9ad65 --- /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()); +} + +$usleep = usleep(...); + +try { + $usleep(); +} 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 2 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 implementation of usleep, 0 given and exactly 1 expected +not enough arguments for application of usleep, 0 given and exactly 1 expected +too many arguments for application of usleep, 2 given and a maximum of 1 expected diff --git a/Zend/tests/partial_application/export_001.phpt b/Zend/tests/partial_application/export_001.phpt new file mode 100644 index 000000000000..a4af669148b1 --- /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(...)) 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 000000000000..c5b44576f0e1 --- /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/factory_001.phpt b/Zend/tests/partial_application/factory_001.phpt new file mode 100644 index 000000000000..ad34aebd027f --- /dev/null +++ b/Zend/tests/partial_application/factory_001.phpt @@ -0,0 +1,24 @@ +--TEST-- +Partial application factory: pass +--FILE-- + +--EXPECTF-- +OK +Foo::__destruct +Foo::__destruct diff --git a/Zend/tests/partial_application/factory_002.phpt b/Zend/tests/partial_application/factory_002.phpt new file mode 100644 index 000000000000..2b94ead0379a --- /dev/null +++ b/Zend/tests/partial_application/factory_002.phpt @@ -0,0 +1,30 @@ +--TEST-- +Partial application factory: normal +--FILE-- + +--EXPECTF-- +Foo::__construct +Foo::__construct +OK +Foo::__destruct +Foo::__destruct diff --git a/Zend/tests/partial_application/factory_003.phpt b/Zend/tests/partial_application/factory_003.phpt new file mode 100644 index 000000000000..fb752b299b6c --- /dev/null +++ b/Zend/tests/partial_application/factory_003.phpt @@ -0,0 +1,24 @@ +--TEST-- +Partial application factory: exception +--FILE-- +getMessage()); +} +?> +--EXPECT-- +boo diff --git a/Zend/tests/partial_application/factory_004.phpt b/Zend/tests/partial_application/factory_004.phpt new file mode 100644 index 000000000000..dd46d203aacd --- /dev/null +++ b/Zend/tests/partial_application/factory_004.phpt @@ -0,0 +1,18 @@ +--TEST-- +Partial application factory object properties initialization +--FILE-- + +--EXPECTF-- +object(Foo)#%d (1) { + ["arg"]=> + int(1) +} + diff --git a/Zend/tests/partial_application/magic_001.phpt b/Zend/tests/partial_application/magic_001.phpt new file mode 100644 index 000000000000..db8770fbdc41 --- /dev/null +++ b/Zend/tests/partial_application/magic_001.phpt @@ -0,0 +1,76 @@ +--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(...); + +echo (string) new ReflectionFunction($bar); + +$bar(100); +?> +--EXPECTF-- +Method [ partial public method method ] { + @@ %s 12 - 12 + + - Parameters [1] { + Parameter #0 [ $args ] + } +} +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) +Method [ partial public method method ] { + @@ %s 30 - 30 + + - Parameters [2] { + Parameter #0 [ $args ] + Parameter #1 [ ...$args ] + } +} +Foo::method +int(10) +Method [ partial public method method ] { + @@ %s 36 - 36 + + - Parameters [1] { + Parameter #0 [ ...$args ] + } +} +Foo::method +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 000000000000..e137c87b7814 --- /dev/null +++ b/Zend/tests/partial_application/magic_002.phpt @@ -0,0 +1,60 @@ +--TEST-- +Partial application magic: __callStatic +--FILE-- + +--EXPECTF-- +Method [ partial static public method method ] { + @@ %s 10 - 10 + + - Parameters [1] { + Parameter #0 [ $args ] + } +} +Foo::method +int(1) +Method [ partial static public method method ] { + @@ %s 16 - 16 + + - Parameters [2] { + Parameter #0 [ $args ] + Parameter #1 [ ...$args ] + } +} +Foo::method +int(10) +Method [ partial static public method method ] { + @@ %s 22 - 22 + + - Parameters [1] { + Parameter #0 [ ...$args ] + } +} +Foo::method +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 000000000000..a46fb5df226b --- /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 000000000000..4c6f47ef366f --- /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 000000000000..ab66f9448e2c --- /dev/null +++ b/Zend/tests/partial_application/magic_005.phpt @@ -0,0 +1,33 @@ +--TEST-- +Partial application magic null ptr deref in arginfo +--FILE-- +method(?); +var_dump($bar); +?> +--EXPECTF-- +object(Closure)#%d (3) { + ["this"]=> + object(Foo)#%d (0) { + } + ["parameter"]=> + array(1) { + ["$args"]=> + string(10) "" + } + ["args"]=> + array(1) { + ["args"]=> + array(1) { + [0]=> + NULL + } + } +} + diff --git a/Zend/tests/partial_application/reflection_001.phpt b/Zend/tests/partial_application/reflection_001.phpt new file mode 100644 index 000000000000..e9f77a778687 --- /dev/null +++ b/Zend/tests/partial_application/reflection_001.phpt @@ -0,0 +1,62 @@ +--TEST-- +Partial application reflection: required parameters +--FILE-- + +--EXPECTF-- +Function [ partial function foo ] { + @@ %s 6 - 6 + + - Parameters [3] { + Parameter #0 [ $a = 1 ] + Parameter #1 [ $b = 5 ] + Parameter #2 [ $c = 10 ] + } +} +Function [ partial function foo ] { + @@ %s 10 - 10 + + - Parameters [3] { + Parameter #0 [ $a ] + Parameter #1 [ $b = 5 ] + Parameter #2 [ $c = 10 ] + } +} +Function [ partial function foo ] { + @@ %s 14 - 14 + + - Parameters [3] { + Parameter #0 [ $a ] + Parameter #1 [ $b ] + Parameter #2 [ $c = 10 ] + } +} +Function [ partial function foo ] { + @@ %s 18 - 18 + + - Parameters [3] { + Parameter #0 [ $a ] + Parameter #1 [ $b ] + Parameter #2 [ $c ] + } +} + diff --git a/Zend/tests/partial_application/reflection_002.phpt b/Zend/tests/partial_application/reflection_002.phpt new file mode 100644 index 000000000000..dbdbc56e815a --- /dev/null +++ b/Zend/tests/partial_application/reflection_002.phpt @@ -0,0 +1,57 @@ +--TEST-- +Partial application reflection: variadics +--FILE-- + +--EXPECTF-- +Function [ partial function foo ] { + @@ %s 6 - 6 + + - Parameters [1] { + Parameter #0 [ $a ] + } +} +Function [ partial function foo ] { + @@ %s 10 - 10 + + - Parameters [2] { + Parameter #0 [ $a ] + Parameter #1 [ ...$b ] + } +} +Function [ partial function foo ] { + @@ %s 14 - 14 + + - Parameters [2] { + Parameter #0 [ $a ] + Parameter #1 [ $b ] + } +} +Function [ partial function foo ] { + @@ %s 18 - 18 + + - Parameters [3] { + Parameter #0 [ $a ] + Parameter #1 [ $b ] + Parameter #2 [ $b ] + } +} diff --git a/Zend/tests/partial_application/reflection_003.phpt b/Zend/tests/partial_application/reflection_003.phpt new file mode 100644 index 000000000000..cd117c94ecf6 --- /dev/null +++ b/Zend/tests/partial_application/reflection_003.phpt @@ -0,0 +1,41 @@ +--TEST-- +Partial application reflection: internal with variadics +--FILE-- + +--EXPECTF-- +Function [ partial function sprintf ] { + + - Parameters [1] { + Parameter #0 [ string $format ] + } + - Return [ string ] +} +Function [ partial function sprintf ] { + + - Parameters [2] { + Parameter #0 [ string $format ] + Parameter #1 [ mixed ...$values ] + } + - Return [ string ] +} +Function [ partial function sprintf ] { + + - Parameters [2] { + Parameter #0 [ string $format ] + Parameter #1 [ mixed $values ] + } + - Return [ string ] +} + 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 000000000000..af7826ff160a --- /dev/null +++ b/Zend/tests/partial_application/return_type_001.phpt @@ -0,0 +1,17 @@ +--TEST-- +Partial application return type +--FILE-- + +--EXPECTF-- +Function [ partial function foo ] { + @@ %s 4 - 4 + + - Parameters [0] { + } + - Return [ array ] +} + 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 000000000000..a690873499ba --- /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 000000000000..c4edc8eaceb7 --- /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 000000000000..2113d9e78a0f --- /dev/null +++ b/Zend/tests/partial_application/this_001.phpt @@ -0,0 +1,22 @@ +--TEST-- +Partial application this +--FILE-- +method(...); + +$baz = $bar(...); + +var_dump($baz()); +?> +--EXPECTF-- +object(Foo)#%d (0) { +} + diff --git a/Zend/tests/partial_application/variation_apply_002.phpt b/Zend/tests/partial_application/variation_apply_002.phpt new file mode 100644 index 000000000000..abdf24788081 --- /dev/null +++ b/Zend/tests/partial_application/variation_apply_002.phpt @@ -0,0 +1,18 @@ +--TEST-- +Partial application variation type +--FILE-- + +--EXPECT-- +Foo::__construct diff --git a/Zend/tests/partial_application/variation_bind_001.phpt b/Zend/tests/partial_application/variation_bind_001.phpt new file mode 100644 index 000000000000..4b064d024ffc --- /dev/null +++ b/Zend/tests/partial_application/variation_bind_001.phpt @@ -0,0 +1,74 @@ +--TEST-- +Partial application variation binding +--FILE-- +method(?, new Param); + +$closure(1); + +var_dump($closure); + +$bound = $closure->bindTo(new Foo); + +$bound(1); + +var_dump($bound); +?> +--EXPECTF-- +Bar: 1, Param +object(Closure)#%d (3) { + ["this"]=> + object(Bar)#%d (0) { + } + ["parameter"]=> + array(1) { + ["$a"]=> + string(10) "" + } + ["args"]=> + array(2) { + ["a"]=> + NULL + ["b"]=> + object(Param)#%d (0) { + } + } +} +Foo: 1, Param +object(Closure)#%d (3) { + ["this"]=> + object(Foo)#%d (0) { + } + ["parameter"]=> + array(1) { + ["$a"]=> + string(10) "" + } + ["args"]=> + array(2) { + ["a"]=> + NULL + ["b"]=> + object(Param)#%d (0) { + } + } +} + diff --git a/Zend/tests/partial_application/variation_bind_002.phpt b/Zend/tests/partial_application/variation_bind_002.phpt new file mode 100644 index 000000000000..ccd3134e2683 --- /dev/null +++ b/Zend/tests/partial_application/variation_bind_002.phpt @@ -0,0 +1,67 @@ +--TEST-- +Partial application variation binding static +--FILE-- +bindTo(null, Foo::class); + +$bound(1); + +var_dump($bound); +?> +--EXPECTF-- +Bar: 1, Param +object(Closure)#%d (2) { + ["parameter"]=> + array(1) { + ["$a"]=> + string(10) "" + } + ["args"]=> + array(2) { + ["a"]=> + NULL + ["b"]=> + object(Param)#%d (0) { + } + } +} +Foo: 1, Param +object(Closure)#%d (2) { + ["parameter"]=> + array(1) { + ["$a"]=> + string(10) "" + } + ["args"]=> + array(2) { + ["a"]=> + NULL + ["b"]=> + object(Param)#%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 000000000000..099ca9c50fc0 --- /dev/null +++ b/Zend/tests/partial_application/variation_call_001.phpt @@ -0,0 +1,32 @@ +--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 000000000000..c9e7ea96f55d --- /dev/null +++ b/Zend/tests/partial_application/variation_closure_001.phpt @@ -0,0 +1,19 @@ +--TEST-- +Partial application variation closure +--FILE-- + +--EXPECTF-- +Function [ partial function {closure} ] { + @@ %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 000000000000..572c8bd0a674 --- /dev/null +++ b/Zend/tests/partial_application/variation_closure_002.phpt @@ -0,0 +1,23 @@ +--TEST-- +Partial application variation closure __invoke +--FILE-- +__invoke(1, ?); + +echo (string) new ReflectionFunction($function); + +$function(10); +?> +--EXPECTF-- +Method [ partial public method __invoke ] { + + - 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 000000000000..f1363efaf7b3 --- /dev/null +++ b/Zend/tests/partial_application/variation_closure_003.phpt @@ -0,0 +1,42 @@ +--TEST-- +Partial application variation closure __invoke with this +--FILE-- +bar(); + +$function = $closure->__invoke(1, ?); + +echo (string) new ReflectionFunction($function); + +var_dump($function(10)); +?> +--EXPECTF-- +Method [ partial public method __invoke ] { + + - 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 000000000000..6981885c09e7 --- /dev/null +++ b/Zend/tests/partial_application/variation_debug_001.phpt @@ -0,0 +1,36 @@ +--TEST-- +Partial application variation debug user +--FILE-- + +--EXPECTF-- +object(Closure)#%d (2) { + ["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 000000000000..ef6fa8e54533 --- /dev/null +++ b/Zend/tests/partial_application/variation_debug_002.phpt @@ -0,0 +1,48 @@ +--TEST-- +Partial application variation debug internal +--FILE-- + +--EXPECTF-- +object(Closure)#%d (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(3) { + [0]=> + array(3) { + [0]=> + int(4) + [1]=> + int(5) + [2]=> + int(6) + } + [1]=> + NULL + ["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 000000000000..dc72ccdae797 --- /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_factory_001.phpt b/Zend/tests/partial_application/variation_factory_001.phpt new file mode 100644 index 000000000000..52c73749bd90 --- /dev/null +++ b/Zend/tests/partial_application/variation_factory_001.phpt @@ -0,0 +1,17 @@ +--TEST-- +Partial application variation internal object factory +--FILE-- +getTimeZone()); +?> +--EXPECTF-- +object(DateTimeZone)#%d (2) { + ["timezone_type"]=> + int(3) + ["timezone"]=> + string(10) "Asia/Tokyo" +} + 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 000000000000..00b951a81ea9 --- /dev/null +++ b/Zend/tests/partial_application/variation_gc_001.phpt @@ -0,0 +1,18 @@ +--TEST-- +Partial application variation GC +--FILE-- +method = self::__construct(...); + } +} + +$foo = new Foo; +$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 000000000000..5f00705cf79b --- /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 000000000000..c919d488eac1 --- /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 000000000000..eed2ad12b17b --- /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 000000000000..1f570bad2cfa --- /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 000000000000..35bcddc15e2a --- /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(...)(...); + +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 000000000000..c6440a9481be --- /dev/null +++ b/Zend/tests/partial_application/variation_parent_001.phpt @@ -0,0 +1,47 @@ +--TEST-- +Partial application variation parent +--FILE-- +method(10, ...); +$baz = $bar(20, ...); + +var_dump($baz, $baz()); +?> +--EXPECTF-- +object(Closure)#%d (3) { + ["this"]=> + object(Foo)#%d (0) { + } + ["parameter"]=> + array(1) { + ["$c"]=> + string(10) "" + } + ["args"]=> + array(3) { + ["a"]=> + int(10) + ["b"]=> + int(20) + ["c"]=> + array(1) { + [0]=> + NULL + } + } +} +object(Closure)#%d (1) { + ["this"]=> + object(Foo)#%d (0) { + } +} + diff --git a/Zend/tests/partial_application/variation_pass_001.phpt b/Zend/tests/partial_application/variation_pass_001.phpt new file mode 100644 index 000000000000..e432e079b45d --- /dev/null +++ b/Zend/tests/partial_application/variation_pass_001.phpt @@ -0,0 +1,14 @@ +--TEST-- +Partial application variation pass +--FILE-- +getMessage()); +} +?> +--EXPECT-- +too many arguments or placeholders for application of Foo::__construct, 1 given and a maximum of 0 expected 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 000000000000..afdfba9e920d --- /dev/null +++ b/Zend/tests/partial_application/variation_scope_001.phpt @@ -0,0 +1,18 @@ +--TEST-- +Partial application variation called scope +--FILE-- +method(...); + +$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 000000000000..f1aebb7d2d01 --- /dev/null +++ b/Zend/tests/partial_application/variation_strict_001.phpt @@ -0,0 +1,21 @@ +--TEST-- +Partial application variation: strict_types declared +--FILE-- +getMessage()); +} +?> +--EXPECT-- +foo(): Argument #1 ($int) must be of type int, string given + 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 000000000000..79deca98f2f9 --- /dev/null +++ b/Zend/tests/partial_application/variation_variadics_001.phpt @@ -0,0 +1,26 @@ +--TEST-- +Partial application variation variadics user +--FILE-- + +--EXPECTF-- +Function [ 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 000000000000..fba07a365b14 --- /dev/null +++ b/Zend/tests/partial_application/variation_variadics_002.phpt @@ -0,0 +1,19 @@ +--TEST-- +Partial application variation variadics internal +--FILE-- + +--EXPECTF-- +Function [ partial function sprintf ] { + + - 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 000000000000..c5e41ab1aeb5 --- /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 000000000000..6cda0fc81f5e --- /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(1) + ["year"]=> + int(2005) +} +array(3) { + ["day"]=> + int(1) + ["month"]=> + int(12) + ["year"]=> + int(2005) +} 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 000000000000..923bddcbe18f --- /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 000000000000..1688112787ad --- /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 000000000000..214ed9b4db9c --- /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; +} +?> +--EXPECT-- +too many arguments or placeholders for application of Closure::__invoke, 2 given and a maximum of 1 expected + diff --git a/Zend/zend.c b/Zend/zend.c index 2d8a0f455f8b..7fb4423edfd1 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_ast.c b/Zend/zend_ast.c index beecf51216a9..d3643d094e23 100644 --- a/Zend/zend_ast.c +++ b/Zend/zend_ast.c @@ -2296,6 +2296,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: diff --git a/Zend/zend_ast.h b/Zend/zend_ast.h index c82ca66c9f57..b9f5475b04e4 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, diff --git a/Zend/zend_closures.c b/Zend/zend_closures.c index 5777e1a34a2b..7324228ee5da 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" @@ -40,6 +41,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) /* {{{ */ @@ -60,9 +63,11 @@ ZEND_METHOD(Closure, __invoke) /* {{{ */ 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 @@ -71,6 +76,8 @@ ZEND_METHOD(Closure, __invoke) /* {{{ */ * an invalid func pointer sitting on there, so this was changed in PHP 8.3. */ execute_data->func = NULL; + +#if ZEND_DEBUG } /* }}} */ @@ -79,6 +86,7 @@ static bool zend_valid_closure_binding( { zend_function *func = &closure->func; bool is_fake_closure = (func->common.fn_flags & ZEND_ACC_FAKE_CLOSURE) != 0; + 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,18 @@ ZEND_METHOD(Closure, call) ZVAL_UNDEF(&closure_result); fci.retval = &closure_result; - if (closure->func.common.fn_flags & ZEND_ACC_GENERATOR) { + if (closure->func.common.fn_flags & ZEND_ACC_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)); + + 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 +270,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->func.common.fn_flags & ZEND_ACC_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 +527,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 +611,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 +673,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 +749,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 ced1b5ba48c1..8c194978a61d 100644 --- a/Zend/zend_closures.h +++ b/Zend/zend_closures.h @@ -32,6 +32,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 2bc0cf7b703d..a958e87a38fb 100644 --- a/Zend/zend_compile.c +++ b/Zend/zend_compile.c @@ -3696,11 +3696,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 *may_have_extra_named_args, + bool *is_call_partial) /* {{{ */ { 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. @@ -3710,6 +3712,8 @@ static uint32_t zend_compile_args( bool may_have_undef = 0; /* Whether there may be any extra named arguments collected into a variadic. */ *may_have_extra_named_args = 0; + /* Whether this is a partial call */ + *is_call_partial = false; for (i = 0; i < args->children; ++i) { zend_ast *arg = args->child[i]; @@ -3726,6 +3730,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)) { @@ -3743,6 +3752,47 @@ static uint32_t zend_compile_args( continue; } + 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) { + if (uses_named_args) { + zend_error_noreturn(E_COMPILE_ERROR, + "Named arguments must come after all placeholders"); + } + + if (uses_variadic_placeholder) { + zend_error_noreturn(E_COMPILE_ERROR, + "Variadic placeholder may only appear once"); + } + + uses_variadic_placeholder = true; + } else if (arg->attr == _IS_PLACEHOLDER_ARG) { + if (uses_named_args) { + zend_error_noreturn(E_COMPILE_ERROR, + "Named arguments must come after all placeholders"); + } + + if (uses_variadic_placeholder) { + zend_error_noreturn(E_COMPILE_ERROR, + "Only named arguments may follow variadic placeholder"); + } + } + + fbc = NULL; + + opline = zend_emit_op(NULL, ZEND_SEND_PLACEHOLDER, NULL, NULL); + opline->op1.num = arg->attr; + opline->op2.num = arg_num; + + *is_call_partial = true; + arg_count++; + continue; + } + if (arg->kind == ZEND_AST_NAMED_ARG) { uses_named_args = 1; arg_name = zval_make_interned_string(zend_ast_get_zval(arg->child[0])); @@ -3777,6 +3827,11 @@ static uint32_t zend_compile_args( "Cannot use positional argument after named argument"); } + if (uses_variadic_placeholder) { + zend_error_noreturn(E_COMPILE_ERROR, + "Only named arguments may follow variadic placeholder"); + } + arg_count++; } @@ -3890,8 +3945,12 @@ 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 { + zend_emit_op(NULL, ZEND_CHECK_PARTIAL_ARGS, NULL, NULL); } return arg_count; @@ -3931,6 +3990,26 @@ 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]; + znode op1; + + opline->extended_value = arg_count; + + if (opline->opcode == ZEND_INIT_FCALL) { + opline->op1.num = zend_vm_calc_used_stack(arg_count, fbc); + } else if (opline->opcode == ZEND_NEW) { + GET_NODE(&op1, opline->result); + } + + opline = zend_emit_op(result, ZEND_DO_FCALL_PARTIAL, + (opline->opcode == ZEND_NEW) ? &op1 : 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; @@ -3953,7 +4032,14 @@ static bool zend_compile_call_common(znode *result, zend_ast *args_ast, zend_fun } bool may_have_extra_named_args; - uint32_t arg_count = zend_compile_args(args_ast, fbc, &may_have_extra_named_args); + bool is_partial_call; + uint32_t arg_count = zend_compile_args(args_ast, fbc, + &may_have_extra_named_args, &is_partial_call); + + if (is_partial_call) { + zend_compile_call_partial(result, arg_count, may_have_extra_named_args, opnum_init, fbc); + return; + } zend_do_extended_fcall_begin(); @@ -4030,12 +4116,14 @@ static void zend_compile_dynamic_call(znode *result, znode *name_node, zend_ast } /* }}} */ -static inline bool zend_args_contain_unpack_or_named(zend_ast_list *args) /* {{{ */ +static inline bool zend_args_contain_unpack_or_named_or_partial(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 || + arg->kind == ZEND_AST_PLACEHOLDER_ARG) { return 1; } } @@ -4282,7 +4370,7 @@ static zend_result zend_compile_func_cufa(znode *result, zend_ast_list *args, ze zend_string *name = zend_resolve_function_name(orig_name, args->child[1]->child[0]->attr, &is_fully_qualified); if (zend_string_equals_literal_ci(name, "array_slice") - && !zend_args_contain_unpack_or_named(list) + && !zend_args_contain_unpack_or_named_or_partial(list) && list->children == 3 && list->child[1]->kind == ZEND_AST_ZVAL) { zval *zv = zend_ast_get_zval(list->child[1]); diff --git a/Zend/zend_compile.h b/Zend/zend_compile.h index 62d0fbcded2e..86e80e373e63 100644 --- a/Zend/zend_compile.h +++ b/Zend/zend_compile.h @@ -403,6 +403,12 @@ typedef struct _zend_oparray_context { /* has #[\NoDiscard] attribute | | | */ #define ZEND_ACC_NODISCARD (1 << 29) /* | X | | */ /* | | | */ +/* flag used by partial application (int only) | | | */ +// TODO #define ZEND_ACC_PARTIAL (1 << 30) /* | X | | */ +/* | | | */ +/* trampoline is permanent | | | */ +#define ZEND_ACC_TRAMPOLINE_PERMANENT (1 << 28) /* | X | | */ +/* | | | */ /* op_array uses strict mode types | | | */ #define ZEND_ACC_STRICT_TYPES (1U << 31) /* | X | | */ @@ -654,6 +660,7 @@ struct _zend_execute_data { /* keep all local variables for "fcall_end" handler */ #define ZEND_CALL_JIT_RESERVED (1 << 29) /* reserved for tracing JIT */ #define ZEND_CALL_NEEDS_REATTACH (1 << 30) +// TODO #define ZEND_CALL_VARIADIC_PLACEHOLDER (1<<29) #define ZEND_CALL_SEND_ARG_BY_REF (1u << 31) #define ZEND_CALL_NESTED_FUNCTION (ZEND_CALL_FUNCTION | ZEND_CALL_NESTED) diff --git a/Zend/zend_execute.c b/Zend/zend_execute.c index 5d8d9f4caeb8..0e5ee4491aa1 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" @@ -1297,7 +1298,9 @@ static zend_never_inline ZEND_ATTRIBUTE_UNUSED bool zend_verify_internal_arg_typ * trust that arginfo matches what is enforced by zend_parse_parameters. */ ZEND_API bool zend_internal_call_should_throw(const zend_function *fbc, zend_execute_data *call) { - if (fbc->internal_function.handler == ZEND_FN(pass) || (fbc->internal_function.fn_flags & ZEND_ACC_FAKE_CLOSURE)) { + if (fbc->internal_function.handler == ZEND_FN(pass) + || (fbc->internal_function.fn_flags & ZEND_ACC_FAKE_CLOSURE) + || fbc->internal_function.handler == zend_partial_call_magic) { /* Be lenient about the special pass function and about fake closures. */ return 0; } @@ -4695,6 +4698,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_DO_FCALL_PARTIAL: level++; break; case ZEND_INIT_FCALL: @@ -4732,6 +4736,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; } @@ -4752,6 +4757,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_DO_FCALL_PARTIAL: level++; break; case ZEND_INIT_FCALL: @@ -4775,9 +4781,6 @@ static void cleanup_unfinished_calls(zend_execute_data *execute_data, uint32_t o zend_vm_stack_free_args(EX(call)); - if (ZEND_CALL_INFO(call) & ZEND_CALL_RELEASE_THIS) { - OBJ_RELEASE(Z_OBJ(call->This)); - } if (ZEND_CALL_INFO(call) & ZEND_CALL_HAS_EXTRA_NAMED_PARAMS) { zend_free_extra_named_params(call->extra_named_params); } @@ -4787,6 +4790,9 @@ static void cleanup_unfinished_calls(zend_execute_data *execute_data, uint32_t o zend_string_release_ex(call->func->common.function_name, 0); zend_free_trampoline(call->func); } + if (ZEND_CALL_INFO(call) & ZEND_CALL_RELEASE_THIS) { + OBJ_RELEASE(Z_OBJ(call->This)); + } EX(call) = call->prev_execute_data; zend_vm_stack_free_call_frame(call); @@ -5436,8 +5442,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; @@ -5466,9 +5472,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; } } diff --git a/Zend/zend_language_parser.y b/Zend/zend_language_parser.y index 816b8126cbf2..d79cb58f9932 100644 --- a/Zend/zend_language_parser.y +++ b/Zend/zend_language_parser.y @@ -918,6 +918,8 @@ argument: expr { $$ = $1; } | identifier ':' expr { $$ = zend_ast_create(ZEND_AST_NAMED_ARG, $1, $3); } + | '?' { $$ = zend_ast_create_ex(ZEND_AST_PLACEHOLDER_ARG, _IS_PLACEHOLDER_ARG); } + | T_ELLIPSIS { $$ = zend_ast_create_ex(ZEND_AST_PLACEHOLDER_ARG, _IS_PLACEHOLDER_VARIADIC); } | T_ELLIPSIS expr { $$ = zend_ast_create(ZEND_AST_UNPACK, $2); } ; diff --git a/Zend/zend_object_handlers.c b/Zend/zend_object_handlers.c index 7b804e7afe95..20672ebbdec8 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 @@ -1665,6 +1667,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() { + 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; @@ -1674,7 +1683,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); @@ -1725,7 +1733,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 7e7d3df37a6a..87c7a6f71002 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 000000000000..55da8c5f82f7 --- /dev/null +++ b/Zend/zend_partial.c @@ -0,0 +1,939 @@ +/* + +----------------------------------------------------------------------+ + | 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_interfaces.h" +#include "zend_closures.h" +#include "zend_partial.h" + +typedef struct _zend_partial { + zend_object std; + /* this is the prototype generated from application */ + zend_function prototype; + zval This; + /* this is the unmodified function that will be invoked at call time */ + zend_function func; + /* this will be returned by get_closure, and has arginfo of prototype + and will invoke func */ + zend_function trampoline; + 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]; + +#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) + +#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 = MAX(partial->func.common.num_args, 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 zend_always_inline zend_function* zend_partial_signature_create(zend_partial *partial, zend_function *prototype) { + zend_arg_info *signature = emalloc(zend_partial_signature_size(partial)), *info = signature; + + memcpy(&partial->prototype, prototype, ZEND_PARTIAL_FUNC_SIZE(prototype)); + + if (ZEND_PARTIAL_FUNC_FLAG(prototype, ZEND_ACC_HAS_RETURN_TYPE)) { + memcpy(info, + prototype->common.arg_info - 1, sizeof(zend_arg_info)); + info++; + } + + uint32_t offset = 0, num = 0, required = 0, limit = partial->argc; + + ZEND_PARTIAL_FUNC_DEL(&partial->prototype, ZEND_ACC_VARIADIC); + + while (offset < limit) { + zval *arg = &partial->argv[offset]; + + if (Z_IS_PLACEHOLDER_ARG_P(arg)) { + if (offset < prototype->common.num_args) { + num++; + required++; + memcpy(info, + &prototype->common.arg_info[offset], + sizeof(zend_arg_info)); + ZEND_TYPE_FULL_MASK(info->type) &= ~_ZEND_IS_VARIADIC_BIT; + info++; + } else if (ZEND_PARTIAL_FUNC_FLAG(prototype, ZEND_ACC_VARIADIC)) { + num++; + required++; + if (ZEND_PARTIAL_IS_CALL_TRAMPOLINE(prototype)) { + memcpy(info, zend_call_magic_arginfo, sizeof(zend_arg_info)); + } else { + memcpy(info, + prototype->common.arg_info + prototype->common.num_args, + sizeof(zend_arg_info)); + } + + ZEND_TYPE_FULL_MASK(info->type) &= ~_ZEND_IS_VARIADIC_BIT; + info++; + } else { + ZEND_ASSERT(0); + } + } else if (Z_IS_PLACEHOLDER_VARIADIC_P(arg) || Z_ISUNDEF_P(arg)) { + if (offset < partial->func.common.num_args) { + while (offset < partial->func.common.num_args) { + if ((offset < partial->argc) && + !Z_IS_PLACEHOLDER_P(&partial->argv[offset]) && + !Z_ISUNDEF(partial->argv[offset])) { + offset++; + continue; + } + + num++; + memcpy(info, + &partial->func.common.arg_info[offset], + sizeof(zend_arg_info)); + ZEND_TYPE_FULL_MASK(info->type) &= ~_ZEND_IS_VARIADIC_BIT; + info++; + offset++; + } + + if (ZEND_PARTIAL_FUNC_FLAG(prototype, ZEND_ACC_VARIADIC)) { + ZEND_ASSERT(!ZEND_PARTIAL_IS_CALL_TRAMPOLINE(prototype)); + memcpy(info, + prototype->common.arg_info + prototype->common.num_args, + sizeof(zend_arg_info)); + + if (ZEND_TYPE_FULL_MASK(info->type) & _ZEND_IS_VARIADIC_BIT) { + ZEND_PARTIAL_FUNC_ADD(&partial->prototype, ZEND_ACC_VARIADIC); + } + num++; + } + break; + } else if (ZEND_PARTIAL_FUNC_FLAG(prototype, ZEND_ACC_VARIADIC)) { + ZEND_PARTIAL_FUNC_ADD(&partial->prototype, ZEND_ACC_VARIADIC); + num++; + if (ZEND_PARTIAL_IS_CALL_TRAMPOLINE(prototype)) { + memcpy(info, zend_call_magic_arginfo, sizeof(zend_arg_info)); + } else { + memcpy(info, + prototype->common.arg_info + prototype->common.num_args, + sizeof(zend_arg_info)); + } + info++; + break; + } + } + + offset++; + } + + if (ZEND_PARTIAL_FUNC_FLAG(&partial->prototype, ZEND_ACC_VARIADIC)) { + num--; + } + + partial->prototype.common.num_args = num; + partial->prototype.common.required_num_args = required; + partial->prototype.common.arg_info = signature; + + if (ZEND_PARTIAL_FUNC_FLAG(&partial->prototype, ZEND_ACC_HAS_RETURN_TYPE)) { + partial->prototype.common.arg_info++; + } + + ZEND_PARTIAL_FUNC_ADD(&partial->prototype, ZEND_ACC_PARTIAL); + + partial->prototype.common.prototype = &partial->func; + + if (prototype->type == ZEND_INTERNAL_FUNCTION) { + return &partial->prototype; + } + + zend_string *filename = zend_get_executed_filename_ex(); + + if (!filename) { + if (partial->prototype.op_array.filename) { + zend_string_addref(partial->prototype.op_array.filename); + } + return &partial->prototype; + } + + partial->prototype.op_array.filename = zend_string_copy(filename); + partial->prototype.op_array.line_start = + partial->prototype.op_array.line_end = zend_get_executed_lineno(); + + return &partial->prototype; +} + +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 (!ZEND_PARTIAL_FUNC_FLAG(&ptr->prototype, ZEND_ACC_PARTIAL)) { + return NULL; + } + + return ptr; +} + +static zend_always_inline void zend_partial_trampoline_create(zend_partial *partial, zend_function *trampoline) +{ + const uint32_t keep_flags = + ZEND_ACC_RETURN_REFERENCE | ZEND_ACC_VARIADIC | ZEND_ACC_HAS_RETURN_TYPE | ZEND_ACC_STRICT_TYPES; + + trampoline->common = partial->prototype.common; + trampoline->type = ZEND_INTERNAL_FUNCTION; + trampoline->internal_function.fn_flags = + ZEND_ACC_PUBLIC | ZEND_ACC_CALL_VIA_HANDLER | ZEND_ACC_PARTIAL | ZEND_ACC_TRAMPOLINE_PERMANENT | (partial->prototype.common.fn_flags & keep_flags); + if (partial->func.type != ZEND_INTERNAL_FUNCTION || (partial->func.common.fn_flags & ZEND_ACC_USER_ARG_INFO)) { + trampoline->internal_function.fn_flags |= + ZEND_ACC_USER_ARG_INFO; + } + trampoline->internal_function.handler = zend_partial_call_magic; + trampoline->internal_function.module = 0; + trampoline->internal_function.scope = zend_ce_closure; + trampoline->internal_function.function_name = ZSTR_KNOWN(ZEND_STR_MAGIC_INVOKE); +} + +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; + + 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)) { + zend_partial *partial = (zend_partial*) *object; + + return &partial->trampoline; + } + + 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 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->prototype.common.arg_info; + + if (ZEND_PARTIAL_FUNC_FLAG(&partial->func, ZEND_ACC_HAS_RETURN_TYPE)) { + info--; + } + + efree(info); + + if (partial->func.type == ZEND_USER_FUNCTION) { + if (partial->prototype.op_array.filename) { + zend_string_release(partial->prototype.op_array.filename); + } + destroy_zend_function(&partial->func); + } + + if (Z_TYPE(partial->This) == IS_OBJECT) { + zval_ptr_dtor(&partial->This); + } + + zend_object_std_dtor(object); +} + +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); +} + +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; +} + +static zend_always_inline 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; +} + +static zend_always_inline 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); + } +} + +static zend_always_inline 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 */ + zend_function *function = call->func; + + uint32_t num = ZEND_CALL_NUM_ARGS(call) + + (ZEND_PARTIAL_CALL_FLAG(call, ZEND_CALL_VARIADIC_PLACEHOLDER) ? -1 : 0); + + if (num < function->common.required_num_args) { + /* this check is delayed in the case of variadic application */ + if (ZEND_PARTIAL_CALL_FLAG(call, ZEND_CALL_VARIADIC_PLACEHOLDER)) { + 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 uint32_t zend_partial_apply( + zval *pStart, zval *pEnd, + zval *cStart, zval *cEnd, + zval *fParam, bool call) { + + uint32_t pCount = 0; + + /* this optimizes the most general case, partial variadic application followed by + all arguments on call, and initial application */ + if ((pEnd - pStart) == 0 || + ((pEnd - pStart) == 1 && Z_IS_PLACEHOLDER_VARIADIC_P(pStart))) { + if ((pCount = (cEnd - cStart)) > 0) { + memcpy(fParam, cStart, sizeof(zval) * pCount); + } + + if (call) { + cStart = fParam; + cEnd = cStart + pCount; + while (cStart < cEnd) { + if (Z_IS_PLACEHOLDER_P(cStart)) { + ZVAL_UNDEF(cStart); + pCount--; + } + cStart++; + } + } + return pCount; + } + + /* this optimizes the second most general case: all arguments supplied upon first + application, followed by no arguments on call */ + if ((cEnd - cStart) == 0) { + if ((pCount = (pEnd - pStart)) > 0) { + memcpy(fParam, pStart, sizeof(zval) * pCount); + } + + if (call) { + cStart = fParam; + cEnd = cStart + pCount; + while (cStart < cEnd) { + if (Z_IS_PLACEHOLDER_P(cStart)) { + ZVAL_UNDEF(cStart); + pCount--; + } + cStart++; + } + } + return pCount; + } + + /* slow path is not able to handle all cases */ + while (pStart < pEnd) { + if (Z_IS_PLACEHOLDER_P(pStart)) { + if (cStart < cEnd) { + ZVAL_COPY_VALUE(fParam, cStart); + pCount++; + fParam++; + cStart++; + } + } else { + if (cStart < cEnd && Z_ISUNDEF_P(pStart)) { + ZVAL_COPY_VALUE(fParam, cStart); + pCount++; + fParam++; + cStart++; + } else { + ZVAL_COPY_VALUE(fParam, pStart); + pCount++; + fParam++; + } + } + pStart++; + } + + while (cStart < cEnd) { + ZVAL_COPY_VALUE(fParam, cStart); + pCount++; + fParam++; + cStart++; + } + + return pCount; +} + +ZEND_NAMED_FUNCTION(zend_partial_call_magic) +{ + zend_partial *partial = (zend_partial*) Z_OBJ_P(ZEND_THIS); + zend_object *object = NULL; + zval *params; + uint32_t num_params, num_all_params; + HashTable *named_args; + zend_fcall_info fci = empty_fcall_info; + zend_fcall_info_cache fcc = empty_fcall_info_cache; + + ZEND_PARSE_PARAMETERS_START(0, -1) + Z_PARAM_VARIADIC_WITH_NAMED(params, num_params, named_args) + ZEND_PARSE_PARAMETERS_END(); + + num_all_params = num_params + (named_args ? named_args->nNumUsed : 0); + + if (num_all_params < partial->prototype.common.required_num_args) { + zend_string *symbol = zend_partial_symbol_name(execute_data, &partial->prototype); + zend_partial_prototype_underflow( + &partial->prototype, symbol, num_all_params, + partial->prototype.common.required_num_args, + ZEND_PARTIAL_CALL_FLAG(partial, ZEND_APPLY_VARIADIC)); + zend_string_release(symbol); + if (ZEND_PARTIAL_IS_CALL_TRAMPOLINE(&partial->prototype)) { + EG(trampoline).common.function_name = NULL; + } + RETURN_THROWS(); + } else if (num_all_params > partial->prototype.common.num_args && + !ZEND_PARTIAL_CALL_FLAG(partial, ZEND_APPLY_VARIADIC)) { + zend_string *symbol = zend_partial_symbol_name(execute_data, &partial->prototype); + zend_partial_prototype_overflow( + &partial->prototype, symbol, num_all_params, + partial->prototype.common.num_args); + zend_string_release(symbol); + if (ZEND_PARTIAL_IS_CALL_TRAMPOLINE(&partial->prototype)) { + EG(trampoline).common.function_name = NULL; + } + RETURN_THROWS(); + } + + fci.size = sizeof(zend_fcall_info); + + if (UNEXPECTED(ZEND_PARTIAL_CALL_FLAG(partial, ZEND_APPLY_FACTORY))) { + zend_class_entry *type = Z_CE(partial->This); + zval instance; + + if (type->create_object) { + ZVAL_OBJ(&instance, type->create_object(type)); + } else { + ZVAL_OBJ(&instance, zend_objects_new(type)); + + object_properties_init(Z_OBJ(instance), type); + } + + object = Z_OBJ(instance); + + if (ZEND_PARTIAL_CALL_FLAG(partial, ZEND_APPLY_PASS)) { + /* nothing to pass any arguments too, so return immediately */ + RETURN_OBJ(object); + } + + GC_ADD_FLAGS(object, IS_OBJ_DESTRUCTOR_CALLED); + } else { + if (Z_TYPE(partial->This) == IS_OBJECT) { + object = Z_OBJ(partial->This); + } else if (Z_TYPE(partial->This) == IS_UNDEF && Z_CE(partial->This)) { + fcc.called_scope = Z_CE(partial->This); + } + } + + fci.retval = return_value; + fcc.function_handler = &partial->func; + + if (object) { + fci.object = object; + fcc.object = object; + } + + if (partial->named) { + if (!named_args) { + named_args = partial->named; + GC_ADDREF(named_args); + } else { + HashTable *nested = zend_array_dup(partial->named); + zend_hash_merge(nested, named_args, zval_copy_ctor, true); + named_args = nested; + } + fci.named_params = named_args; + } else { + fci.named_params = named_args; + } + + fci.params = ecalloc(sizeof(zval), partial->argc + num_params); + fci.param_count = zend_partial_apply( + partial->argv, partial->argv + partial->argc, + params ? params : NULL, + params ? params + num_params : NULL, fci.params, true); + + if (fci.param_count < partial->func.common.required_num_args) { + /* doesn't satisfy implementation */ + zend_string *symbol = zend_partial_symbol_name(execute_data, &partial->func); + zend_partial_args_underflow( + &partial->func, symbol, fci.param_count, + partial->func.common.required_num_args, true, false); + zend_string_release(symbol); + } else { + zend_call_function(&fci, &fcc); + } + + if (ZEND_PARTIAL_IS_CALL_TRAMPOLINE(&partial->prototype)) { + EG(trampoline).common.function_name = NULL; + } + + if (ZEND_PARTIAL_CALL_FLAG(partial, ZEND_APPLY_FACTORY)) { + if (!EG(exception)) { + GC_DEL_FLAGS(object, IS_OBJ_DESTRUCTOR_CALLED); + } + zval_ptr_dtor(return_value); + RETVAL_OBJ(object); + } + + if (partial->named) { + zend_array_release(named_args); + } + efree(fci.params); +} + +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) { + zend_function *prototype = NULL; + + ZVAL_OBJ(result, zend_partial_new(zend_ce_closure, info)); + + zend_partial *applied, *partial = (zend_partial*) Z_OBJ_P(result); + + if ((applied = zend_partial_fetch(this_ptr))) { + ZEND_ADD_CALL_FLAG(partial, ZEND_CALL_INFO(applied) & ~ZEND_APPLY_VARIADIC); + + function = &applied->func; + prototype = &applied->prototype; + + if (ZEND_PARTIAL_CALL_FLAG(partial, ZEND_APPLY_FACTORY)) { + Z_CE(partial->This) = Z_CE(applied->This); + } + + partial->argv = ecalloc(applied->argc + argc, sizeof(zval)); + partial->argc = zend_partial_apply( + applied->argv, applied->argv + applied->argc, + argv, argv + argc, + partial->argv, false); + + 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 { + partial->argv = ecalloc(argc, sizeof(zval)); + partial->argc = zend_partial_apply( + NULL, NULL, + argv, argv + argc, + partial->argv, false); + + 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); + } + + if (ZEND_PARTIAL_CALL_FLAG(partial, ZEND_APPLY_FACTORY)) { + if (!Z_CE(partial->This)) { + Z_CE(partial->This) = Z_OBJCE_P(this_ptr); + } + + if (ZEND_PARTIAL_CALL_FLAG(partial, ZEND_APPLY_PASS)) { + /* setting scope for pass function so that errors make sense */ + partial->func.common.scope = Z_CE(partial->This); + } + } + + ZEND_PARTIAL_FUNC_ADD(&partial->func, ZEND_ACC_FAKE_CLOSURE); + + if (partial->func.type == ZEND_USER_FUNCTION) { + zend_string_addref(partial->func.common.function_name); + + partial->func.op_array.refcount = NULL; + } + + if (!prototype) { + prototype = &partial->func; + } + + partial->func.common.prototype = zend_partial_signature_create(partial, prototype); + + if (!ZEND_PARTIAL_CALL_FLAG(partial, ZEND_APPLY_FACTORY)) { + /* 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 { + This = zend_get_closure_this_ptr(this_ptr); + } + } else { + This = this_ptr; + } + ZVAL_COPY(&partial->This, This); + } + + ZEND_ADD_CALL_FLAG(partial, backup_info); + } + + zend_partial_trampoline_create(partial, &partial->trampoline); +} + +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 000000000000..31d73bbda041 --- /dev/null +++ b/Zend/zend_partial.h @@ -0,0 +1,42 @@ +/* + +----------------------------------------------------------------------+ + | 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 + +BEGIN_EXTERN_C() + +void zend_partial_startup(void); + +#define ZEND_APPLY_NORMAL (1<<16) +#define ZEND_APPLY_FACTORY (1<<17) +#define ZEND_APPLY_PASS (1<<18) +#define ZEND_APPLY_VARIADIC (1<<19) + +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); + +ZEND_NAMED_FUNCTION(zend_partial_call_magic); + +END_EXTERN_C() + +#endif diff --git a/Zend/zend_types.h b/Zend/zend_types.h index 4a6d00b9d73e..870a54f88965 100644 --- a/Zend/zend_types.h +++ b/Zend/zend_types.h @@ -666,6 +666,10 @@ struct _zend_ast_ref { #define ZEND_GUARD_PROTECT_RECURSION(pg, t) *pg |= ZEND_GUARD_RECURSION_TYPE(t) #define ZEND_GUARD_UNPROTECT_RECURSION(pg, t) *pg &= ~ZEND_GUARD_RECURSION_TYPE(t) +/* used for place holders */ +#define _IS_PLACEHOLDER_ARG 20 +#define _IS_PLACEHOLDER_VARIADIC 21 + static zend_always_inline uint8_t zval_get_type(const zval* pz) { return pz->u1.v.type; } diff --git a/Zend/zend_vm_def.h b/Zend/zend_vm_def.h index 617e114dd05d..0121f9279a7e 100644 --- a/Zend/zend_vm_def.h +++ b/Zend/zend_vm_def.h @@ -5975,7 +5975,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); @@ -6156,7 +6156,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); @@ -9202,6 +9202,92 @@ ZEND_VM_HANDLER(200, ZEND_FETCH_GLOBALS, UNUSED, UNUSED) ZEND_VM_NEXT_OPCODE(); } +ZEND_VM_HANDLER(211, ZEND_SEND_PLACEHOLDER, UNUSED, UNUSED) +{ + USE_OPLINE + zval *arg; + zend_execute_data *call = EX(call); + + arg = ZEND_CALL_ARG(call, opline->op2.num); + + Z_TYPE_INFO_P(arg) = opline->op1.num; + + if (Z_TYPE_INFO_P(arg) == _IS_PLACEHOLDER_VARIADIC) { + ZEND_ADD_CALL_FLAG(call, ZEND_CALL_VARIADIC_PLACEHOLDER); + } + + ZEND_VM_NEXT_OPCODE(); +} + +ZEND_VM_HANDLER(212, ZEND_DO_FCALL_PARTIAL, ANY, ANY) +{ + USE_OPLINE + zend_execute_data *call = EX(call); + zval *result = NULL; + uint32_t info = ZEND_APPLY_NORMAL; + + if (opline->op1_type != IS_UNUSED) { + result = EX_VAR(opline->op1.var); + + if (!(call->func->common.fn_flags & ZEND_ACC_CTOR)) { + ZEND_ADD_CALL_FLAG_EX(info, ZEND_APPLY_PASS); + } + + ZEND_ADD_CALL_FLAG_EX(info, ZEND_APPLY_FACTORY); + } else if (opline->result_type != IS_UNUSED) { + result = EX_VAR(opline->result.var); + } + + if (ZEND_CALL_INFO(call) & ZEND_CALL_VARIADIC_PLACEHOLDER) { + ZEND_ADD_CALL_FLAG_EX(info, ZEND_APPLY_VARIADIC); + } + + if (result) { + zend_partial_create(result, info, + &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 (info & ZEND_APPLY_FACTORY) { + GC_ADD_FLAGS(Z_OBJ(call->This), IS_OBJ_DESTRUCTOR_CALLED); + OBJ_RELEASE(Z_OBJ(call->This)); + } + } else { + zend_vm_stack_free_args(call); + } + + 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(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 diff --git a/Zend/zend_vm_execute.h b/Zend/zend_vm_execute.h index 791e4b4e8843..1cd0a8326a17 100644 --- a/Zend/zend_vm_execute.h +++ b/Zend/zend_vm_execute.h @@ -3810,6 +3810,65 @@ static ZEND_OPCODE_HANDLER_RET ZEND_FASTCALL ZEND_CALL_TRAMPOLINE_SPEC_OBSERVER_ ZEND_VM_LEAVE(); } +static ZEND_OPCODE_HANDLER_RET ZEND_FASTCALL ZEND_DO_FCALL_PARTIAL_SPEC_HANDLER(ZEND_OPCODE_HANDLER_ARGS) +{ + USE_OPLINE + zend_execute_data *call = EX(call); + zval *result = NULL; + uint32_t info = ZEND_APPLY_NORMAL; + + if (opline->op1_type != IS_UNUSED) { + result = EX_VAR(opline->op1.var); + + if (!(call->func->common.fn_flags & ZEND_ACC_CTOR)) { + ZEND_ADD_CALL_FLAG_EX(info, ZEND_APPLY_PASS); + } + + ZEND_ADD_CALL_FLAG_EX(info, ZEND_APPLY_FACTORY); + } else if (opline->result_type != IS_UNUSED) { + result = EX_VAR(opline->result.var); + } + + if (ZEND_CALL_INFO(call) & ZEND_CALL_VARIADIC_PLACEHOLDER) { + ZEND_ADD_CALL_FLAG_EX(info, ZEND_APPLY_VARIADIC); + } + + if (result) { + zend_partial_create(result, info, + &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 (info & ZEND_APPLY_FACTORY) { + GC_ADD_FLAGS(Z_OBJ(call->This), IS_OBJ_DESTRUCTOR_CALLED); + OBJ_RELEASE(Z_OBJ(call->This)); + } + } else { + zend_vm_stack_free_args(call); + } + + 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_FRAMELESS_ICALL_2_SPEC_HANDLER(ZEND_OPCODE_HANDLER_ARGS) { USE_OPLINE @@ -11059,7 +11118,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); @@ -30607,7 +30666,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); @@ -38072,7 +38131,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); @@ -38257,6 +38316,33 @@ 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); + + arg = ZEND_CALL_ARG(call, opline->op2.num); + + Z_TYPE_INFO_P(arg) = opline->op1.num; + + if (Z_TYPE_INFO_P(arg) == _IS_PLACEHOLDER_VARIADIC) { + ZEND_ADD_CALL_FLAG(call, ZEND_CALL_VARIADIC_PLACEHOLDER); + } + + 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 @@ -57741,6 +57827,9 @@ 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_UNUSED_LABEL, + (void*)&&ZEND_DO_FCALL_PARTIAL_SPEC_LABEL, + (void*)&&ZEND_CHECK_PARTIAL_ARGS_SPEC_UNUSED_UNUSED_LABEL, (void*)&&ZEND_INIT_FCALL_OFFSET_SPEC_CONST_LABEL, (void*)&&ZEND_RECV_NOTYPE_SPEC_LABEL, (void*)&&ZEND_NULL_LABEL, @@ -59092,6 +59181,11 @@ 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_DO_FCALL_PARTIAL_SPEC): + VM_TRACE(ZEND_DO_FCALL_PARTIAL_SPEC) + ZEND_DO_FCALL_PARTIAL_SPEC_HANDLER(ZEND_OPCODE_HANDLER_ARGS_PASSTHRU); + VM_TRACE_OP_END(ZEND_DO_FCALL_PARTIAL_SPEC) + 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); @@ -62804,6 +62898,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); @@ -66969,6 +67073,9 @@ 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_UNUSED_HANDLER, + ZEND_DO_FCALL_PARTIAL_SPEC_HANDLER, + ZEND_CHECK_PARTIAL_ARGS_SPEC_UNUSED_UNUSED_HANDLER, ZEND_INIT_FCALL_OFFSET_SPEC_CONST_HANDLER, ZEND_RECV_NOTYPE_SPEC_HANDLER, ZEND_NULL_HANDLER, @@ -67932,7 +68039,7 @@ void zend_vm_init(void) 1255, 1256 | SPEC_RULE_OP1, 1261 | SPEC_RULE_OP1, - 3493, + 3496, 1266 | SPEC_RULE_OP1, 1271 | SPEC_RULE_OP1, 1276 | SPEC_RULE_OP2, @@ -67966,7 +68073,7 @@ void zend_vm_init(void) 1559 | SPEC_RULE_OP1 | SPEC_RULE_OP2, 1584 | SPEC_RULE_OP1, 1589, - 3493, + 3496, 1590 | SPEC_RULE_OP1, 1595 | SPEC_RULE_OP1 | SPEC_RULE_OP2, 1620 | SPEC_RULE_OP1 | SPEC_RULE_OP2, @@ -68098,51 +68205,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, + 2579, + 2580, + 3496, + 3496, + 3496, + 3496, + 3496, + 3496, + 3496, + 3496, + 3496, + 3496, + 3496, + 3496, + 3496, + 3496, + 3496, + 3496, + 3496, + 3496, + 3496, + 3496, + 3496, + 3496, + 3496, + 3496, + 3496, + 3496, + 3496, + 3496, + 3496, + 3496, + 3496, + 3496, + 3496, + 3496, + 3496, + 3496, + 3496, + 3496, + 3496, + 3496, + 3496, + 3496, }; #if (ZEND_VM_KIND == ZEND_VM_KIND_HYBRID) zend_opcode_handler_funcs = labels; @@ -68315,7 +68422,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 = 2589 | SPEC_RULE_OP1 | SPEC_RULE_OP2 | SPEC_RULE_COMMUTATIVE; if (op->op1_type < op->op2_type) { zend_swap_operands(op); } @@ -68323,7 +68430,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 = 2614 | SPEC_RULE_OP1 | SPEC_RULE_OP2 | SPEC_RULE_COMMUTATIVE; if (op->op1_type < op->op2_type) { zend_swap_operands(op); } @@ -68331,7 +68438,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 = 2639 | SPEC_RULE_OP1 | SPEC_RULE_OP2 | SPEC_RULE_COMMUTATIVE; if (op->op1_type < op->op2_type) { zend_swap_operands(op); } @@ -68342,17 +68449,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 = 2664 | 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 = 2689 | 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 = 2714 | SPEC_RULE_OP1 | SPEC_RULE_OP2; } break; case ZEND_MUL: @@ -68363,17 +68470,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 = 2739 | 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 = 2764 | 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 = 2789 | SPEC_RULE_OP1 | SPEC_RULE_OP2 | SPEC_RULE_COMMUTATIVE; } break; case ZEND_IS_IDENTICAL: @@ -68384,16 +68491,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 = 2814 | 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 = 2889 | 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 = 3114 | 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 = 3120 | SPEC_RULE_OP2 | SPEC_RULE_COMMUTATIVE; } break; case ZEND_IS_NOT_IDENTICAL: @@ -68404,16 +68511,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 = 2964 | 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 = 3039 | 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 = 3117 | 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 = 3125 | SPEC_RULE_OP2 | SPEC_RULE_COMMUTATIVE; } break; case ZEND_IS_EQUAL: @@ -68424,12 +68531,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 = 2814 | 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 = 2889 | SPEC_RULE_OP1 | SPEC_RULE_OP2 | SPEC_RULE_SMART_BRANCH | SPEC_RULE_COMMUTATIVE; } break; case ZEND_IS_NOT_EQUAL: @@ -68440,12 +68547,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 = 2964 | 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 = 3039 | SPEC_RULE_OP1 | SPEC_RULE_OP2 | SPEC_RULE_SMART_BRANCH | SPEC_RULE_COMMUTATIVE; } break; case ZEND_IS_SMALLER: @@ -68453,12 +68560,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 = 3130 | 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 = 3205 | SPEC_RULE_OP1 | SPEC_RULE_OP2 | SPEC_RULE_SMART_BRANCH; } break; case ZEND_IS_SMALLER_OR_EQUAL: @@ -68466,79 +68573,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 = 3280 | 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 = 3355 | 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 = 3442 | SPEC_RULE_OP1; } else if (op1_info == MAY_BE_DOUBLE) { - spec = 3444 | SPEC_RULE_OP1; + spec = 3447 | 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 = 3452 | 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 = 3430 | SPEC_RULE_RETVAL; } else if (op1_info == MAY_BE_LONG) { - spec = 3429 | SPEC_RULE_RETVAL; + spec = 3432 | 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 = 3434 | SPEC_RULE_RETVAL; } else if (op1_info == MAY_BE_LONG) { - spec = 3433 | SPEC_RULE_RETVAL; + spec = 3436 | SPEC_RULE_RETVAL; } break; case ZEND_POST_INC: if (res_info == MAY_BE_LONG && op1_info == MAY_BE_LONG) { - spec = 3435; + spec = 3438; } else if (op1_info == MAY_BE_LONG) { - spec = 3436; + spec = 3439; } break; case ZEND_POST_DEC: if (res_info == MAY_BE_LONG && op1_info == MAY_BE_LONG) { - spec = 3437; + spec = 3440; } else if (op1_info == MAY_BE_LONG) { - spec = 3438; + spec = 3441; } break; case ZEND_JMP: if (OP_JMP_ADDR(op, op->op1) > op) { - spec = 2585; + spec = 2588; } break; case ZEND_INIT_FCALL: if (Z_EXTRA_P(RT_CONSTANT(op, op->op2)) != 0) { - spec = 2578; + spec = 2581; } break; case ZEND_RECV: if (op->op2.num == MAY_BE_ANY) { - spec = 2579; + spec = 2582; } 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 = 3492; } 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 = 3487 | 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 = 3494 | SPEC_RULE_RETVAL; } break; case ZEND_FETCH_DIM_R: @@ -68546,22 +68653,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 = 3457 | 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 = 3493; } 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 = 3482 | 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 = 2583 | SPEC_RULE_OP1; } break; case ZEND_BW_OR: diff --git a/Zend/zend_vm_handlers.h b/Zend/zend_vm_handlers.h index 33d951141550..055a732afadd 100644 --- a/Zend/zend_vm_handlers.h +++ b/Zend/zend_vm_handlers.h @@ -1373,508 +1373,511 @@ _(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) \ + _(2578, ZEND_SEND_PLACEHOLDER_SPEC_UNUSED_UNUSED) \ + _(2579, ZEND_DO_FCALL_PARTIAL_SPEC) \ + _(2580, ZEND_CHECK_PARTIAL_ARGS_SPEC_UNUSED_UNUSED) \ + _(2581, ZEND_INIT_FCALL_OFFSET_SPEC_CONST) \ + _(2582, ZEND_RECV_NOTYPE_SPEC) \ + _(2584, ZEND_COUNT_ARRAY_SPEC_TMPVAR_UNUSED) \ + _(2585, ZEND_COUNT_ARRAY_SPEC_TMPVAR_UNUSED) \ + _(2587, ZEND_COUNT_ARRAY_SPEC_CV_UNUSED) \ + _(2588, ZEND_JMP_FORWARD_SPEC) \ + _(2594, ZEND_ADD_LONG_NO_OVERFLOW_SPEC_TMPVARCV_CONST) \ _(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) \ + _(2596, ZEND_ADD_LONG_NO_OVERFLOW_SPEC_TMPVARCV_TMPVARCV) \ _(2598, ZEND_ADD_LONG_NO_OVERFLOW_SPEC_TMPVARCV_TMPVARCV) \ + _(2599, ZEND_ADD_LONG_NO_OVERFLOW_SPEC_TMPVARCV_CONST) \ _(2600, ZEND_ADD_LONG_NO_OVERFLOW_SPEC_TMPVARCV_TMPVARCV) \ - _(2606, ZEND_ADD_LONG_NO_OVERFLOW_SPEC_TMPVARCV_CONST) \ - _(2607, ZEND_ADD_LONG_NO_OVERFLOW_SPEC_TMPVARCV_TMPVARCV) \ - _(2608, ZEND_ADD_LONG_NO_OVERFLOW_SPEC_TMPVARCV_TMPVARCV) \ + _(2601, ZEND_ADD_LONG_NO_OVERFLOW_SPEC_TMPVARCV_TMPVARCV) \ + _(2603, ZEND_ADD_LONG_NO_OVERFLOW_SPEC_TMPVARCV_TMPVARCV) \ + _(2609, ZEND_ADD_LONG_NO_OVERFLOW_SPEC_TMPVARCV_CONST) \ _(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) \ + _(2611, ZEND_ADD_LONG_NO_OVERFLOW_SPEC_TMPVARCV_TMPVARCV) \ + _(2613, ZEND_ADD_LONG_NO_OVERFLOW_SPEC_TMPVARCV_TMPVARCV) \ + _(2619, ZEND_ADD_LONG_SPEC_TMPVARCV_CONST) \ _(2620, ZEND_ADD_LONG_SPEC_TMPVARCV_TMPVARCV) \ - _(2621, ZEND_ADD_LONG_SPEC_TMPVARCV_CONST) \ - _(2622, ZEND_ADD_LONG_SPEC_TMPVARCV_TMPVARCV) \ + _(2621, ZEND_ADD_LONG_SPEC_TMPVARCV_TMPVARCV) \ _(2623, ZEND_ADD_LONG_SPEC_TMPVARCV_TMPVARCV) \ + _(2624, ZEND_ADD_LONG_SPEC_TMPVARCV_CONST) \ _(2625, ZEND_ADD_LONG_SPEC_TMPVARCV_TMPVARCV) \ - _(2631, ZEND_ADD_LONG_SPEC_TMPVARCV_CONST) \ - _(2632, ZEND_ADD_LONG_SPEC_TMPVARCV_TMPVARCV) \ - _(2633, ZEND_ADD_LONG_SPEC_TMPVARCV_TMPVARCV) \ + _(2626, ZEND_ADD_LONG_SPEC_TMPVARCV_TMPVARCV) \ + _(2628, ZEND_ADD_LONG_SPEC_TMPVARCV_TMPVARCV) \ + _(2634, ZEND_ADD_LONG_SPEC_TMPVARCV_CONST) \ _(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) \ + _(2636, ZEND_ADD_LONG_SPEC_TMPVARCV_TMPVARCV) \ + _(2638, ZEND_ADD_LONG_SPEC_TMPVARCV_TMPVARCV) \ + _(2644, ZEND_ADD_DOUBLE_SPEC_TMPVARCV_CONST) \ _(2645, ZEND_ADD_DOUBLE_SPEC_TMPVARCV_TMPVARCV) \ - _(2646, ZEND_ADD_DOUBLE_SPEC_TMPVARCV_CONST) \ - _(2647, ZEND_ADD_DOUBLE_SPEC_TMPVARCV_TMPVARCV) \ + _(2646, ZEND_ADD_DOUBLE_SPEC_TMPVARCV_TMPVARCV) \ _(2648, ZEND_ADD_DOUBLE_SPEC_TMPVARCV_TMPVARCV) \ + _(2649, ZEND_ADD_DOUBLE_SPEC_TMPVARCV_CONST) \ _(2650, ZEND_ADD_DOUBLE_SPEC_TMPVARCV_TMPVARCV) \ - _(2656, ZEND_ADD_DOUBLE_SPEC_TMPVARCV_CONST) \ - _(2657, ZEND_ADD_DOUBLE_SPEC_TMPVARCV_TMPVARCV) \ - _(2658, ZEND_ADD_DOUBLE_SPEC_TMPVARCV_TMPVARCV) \ + _(2651, ZEND_ADD_DOUBLE_SPEC_TMPVARCV_TMPVARCV) \ + _(2653, ZEND_ADD_DOUBLE_SPEC_TMPVARCV_TMPVARCV) \ + _(2659, ZEND_ADD_DOUBLE_SPEC_TMPVARCV_CONST) \ _(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) \ + _(2661, ZEND_ADD_DOUBLE_SPEC_TMPVARCV_TMPVARCV) \ + _(2663, ZEND_ADD_DOUBLE_SPEC_TMPVARCV_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) \ + _(2666, ZEND_SUB_LONG_NO_OVERFLOW_SPEC_CONST_TMPVARCV) \ + _(2668, ZEND_SUB_LONG_NO_OVERFLOW_SPEC_CONST_TMPVARCV) \ + _(2669, ZEND_SUB_LONG_NO_OVERFLOW_SPEC_TMPVARCV_CONST) \ _(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) \ + _(2671, ZEND_SUB_LONG_NO_OVERFLOW_SPEC_TMPVARCV_TMPVARCV) \ _(2673, ZEND_SUB_LONG_NO_OVERFLOW_SPEC_TMPVARCV_TMPVARCV) \ + _(2674, ZEND_SUB_LONG_NO_OVERFLOW_SPEC_TMPVARCV_CONST) \ _(2675, ZEND_SUB_LONG_NO_OVERFLOW_SPEC_TMPVARCV_TMPVARCV) \ - _(2681, ZEND_SUB_LONG_NO_OVERFLOW_SPEC_TMPVARCV_CONST) \ - _(2682, ZEND_SUB_LONG_NO_OVERFLOW_SPEC_TMPVARCV_TMPVARCV) \ - _(2683, ZEND_SUB_LONG_NO_OVERFLOW_SPEC_TMPVARCV_TMPVARCV) \ + _(2676, ZEND_SUB_LONG_NO_OVERFLOW_SPEC_TMPVARCV_TMPVARCV) \ + _(2678, ZEND_SUB_LONG_NO_OVERFLOW_SPEC_TMPVARCV_TMPVARCV) \ + _(2684, ZEND_SUB_LONG_NO_OVERFLOW_SPEC_TMPVARCV_CONST) \ _(2685, ZEND_SUB_LONG_NO_OVERFLOW_SPEC_TMPVARCV_TMPVARCV) \ - _(2687, ZEND_SUB_LONG_SPEC_CONST_TMPVARCV) \ - _(2688, ZEND_SUB_LONG_SPEC_CONST_TMPVARCV) \ + _(2686, ZEND_SUB_LONG_NO_OVERFLOW_SPEC_TMPVARCV_TMPVARCV) \ + _(2688, ZEND_SUB_LONG_NO_OVERFLOW_SPEC_TMPVARCV_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) \ + _(2691, ZEND_SUB_LONG_SPEC_CONST_TMPVARCV) \ + _(2693, ZEND_SUB_LONG_SPEC_CONST_TMPVARCV) \ + _(2694, ZEND_SUB_LONG_SPEC_TMPVARCV_CONST) \ _(2695, ZEND_SUB_LONG_SPEC_TMPVARCV_TMPVARCV) \ - _(2696, ZEND_SUB_LONG_SPEC_TMPVARCV_CONST) \ - _(2697, ZEND_SUB_LONG_SPEC_TMPVARCV_TMPVARCV) \ + _(2696, ZEND_SUB_LONG_SPEC_TMPVARCV_TMPVARCV) \ _(2698, ZEND_SUB_LONG_SPEC_TMPVARCV_TMPVARCV) \ + _(2699, ZEND_SUB_LONG_SPEC_TMPVARCV_CONST) \ _(2700, ZEND_SUB_LONG_SPEC_TMPVARCV_TMPVARCV) \ - _(2706, ZEND_SUB_LONG_SPEC_TMPVARCV_CONST) \ - _(2707, ZEND_SUB_LONG_SPEC_TMPVARCV_TMPVARCV) \ - _(2708, ZEND_SUB_LONG_SPEC_TMPVARCV_TMPVARCV) \ + _(2701, ZEND_SUB_LONG_SPEC_TMPVARCV_TMPVARCV) \ + _(2703, ZEND_SUB_LONG_SPEC_TMPVARCV_TMPVARCV) \ + _(2709, ZEND_SUB_LONG_SPEC_TMPVARCV_CONST) \ _(2710, ZEND_SUB_LONG_SPEC_TMPVARCV_TMPVARCV) \ - _(2712, ZEND_SUB_DOUBLE_SPEC_CONST_TMPVARCV) \ - _(2713, ZEND_SUB_DOUBLE_SPEC_CONST_TMPVARCV) \ + _(2711, ZEND_SUB_LONG_SPEC_TMPVARCV_TMPVARCV) \ + _(2713, ZEND_SUB_LONG_SPEC_TMPVARCV_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) \ + _(2716, ZEND_SUB_DOUBLE_SPEC_CONST_TMPVARCV) \ + _(2718, ZEND_SUB_DOUBLE_SPEC_CONST_TMPVARCV) \ + _(2719, ZEND_SUB_DOUBLE_SPEC_TMPVARCV_CONST) \ _(2720, ZEND_SUB_DOUBLE_SPEC_TMPVARCV_TMPVARCV) \ - _(2721, ZEND_SUB_DOUBLE_SPEC_TMPVARCV_CONST) \ - _(2722, ZEND_SUB_DOUBLE_SPEC_TMPVARCV_TMPVARCV) \ + _(2721, ZEND_SUB_DOUBLE_SPEC_TMPVARCV_TMPVARCV) \ _(2723, ZEND_SUB_DOUBLE_SPEC_TMPVARCV_TMPVARCV) \ + _(2724, ZEND_SUB_DOUBLE_SPEC_TMPVARCV_CONST) \ _(2725, ZEND_SUB_DOUBLE_SPEC_TMPVARCV_TMPVARCV) \ - _(2731, ZEND_SUB_DOUBLE_SPEC_TMPVARCV_CONST) \ - _(2732, ZEND_SUB_DOUBLE_SPEC_TMPVARCV_TMPVARCV) \ - _(2733, ZEND_SUB_DOUBLE_SPEC_TMPVARCV_TMPVARCV) \ + _(2726, ZEND_SUB_DOUBLE_SPEC_TMPVARCV_TMPVARCV) \ + _(2728, ZEND_SUB_DOUBLE_SPEC_TMPVARCV_TMPVARCV) \ + _(2734, ZEND_SUB_DOUBLE_SPEC_TMPVARCV_CONST) \ _(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) \ + _(2736, ZEND_SUB_DOUBLE_SPEC_TMPVARCV_TMPVARCV) \ + _(2738, ZEND_SUB_DOUBLE_SPEC_TMPVARCV_TMPVARCV) \ + _(2744, ZEND_MUL_LONG_NO_OVERFLOW_SPEC_TMPVARCV_CONST) \ _(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) \ + _(2746, ZEND_MUL_LONG_NO_OVERFLOW_SPEC_TMPVARCV_TMPVARCV) \ _(2748, ZEND_MUL_LONG_NO_OVERFLOW_SPEC_TMPVARCV_TMPVARCV) \ + _(2749, ZEND_MUL_LONG_NO_OVERFLOW_SPEC_TMPVARCV_CONST) \ _(2750, ZEND_MUL_LONG_NO_OVERFLOW_SPEC_TMPVARCV_TMPVARCV) \ - _(2756, ZEND_MUL_LONG_NO_OVERFLOW_SPEC_TMPVARCV_CONST) \ - _(2757, ZEND_MUL_LONG_NO_OVERFLOW_SPEC_TMPVARCV_TMPVARCV) \ - _(2758, ZEND_MUL_LONG_NO_OVERFLOW_SPEC_TMPVARCV_TMPVARCV) \ + _(2751, ZEND_MUL_LONG_NO_OVERFLOW_SPEC_TMPVARCV_TMPVARCV) \ + _(2753, ZEND_MUL_LONG_NO_OVERFLOW_SPEC_TMPVARCV_TMPVARCV) \ + _(2759, ZEND_MUL_LONG_NO_OVERFLOW_SPEC_TMPVARCV_CONST) \ _(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) \ + _(2761, ZEND_MUL_LONG_NO_OVERFLOW_SPEC_TMPVARCV_TMPVARCV) \ + _(2763, ZEND_MUL_LONG_NO_OVERFLOW_SPEC_TMPVARCV_TMPVARCV) \ + _(2769, ZEND_MUL_LONG_SPEC_TMPVARCV_CONST) \ _(2770, ZEND_MUL_LONG_SPEC_TMPVARCV_TMPVARCV) \ - _(2771, ZEND_MUL_LONG_SPEC_TMPVARCV_CONST) \ - _(2772, ZEND_MUL_LONG_SPEC_TMPVARCV_TMPVARCV) \ + _(2771, ZEND_MUL_LONG_SPEC_TMPVARCV_TMPVARCV) \ _(2773, ZEND_MUL_LONG_SPEC_TMPVARCV_TMPVARCV) \ + _(2774, ZEND_MUL_LONG_SPEC_TMPVARCV_CONST) \ _(2775, ZEND_MUL_LONG_SPEC_TMPVARCV_TMPVARCV) \ - _(2781, ZEND_MUL_LONG_SPEC_TMPVARCV_CONST) \ - _(2782, ZEND_MUL_LONG_SPEC_TMPVARCV_TMPVARCV) \ - _(2783, ZEND_MUL_LONG_SPEC_TMPVARCV_TMPVARCV) \ + _(2776, ZEND_MUL_LONG_SPEC_TMPVARCV_TMPVARCV) \ + _(2778, ZEND_MUL_LONG_SPEC_TMPVARCV_TMPVARCV) \ + _(2784, ZEND_MUL_LONG_SPEC_TMPVARCV_CONST) \ _(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) \ + _(2786, ZEND_MUL_LONG_SPEC_TMPVARCV_TMPVARCV) \ + _(2788, ZEND_MUL_LONG_SPEC_TMPVARCV_TMPVARCV) \ + _(2794, ZEND_MUL_DOUBLE_SPEC_TMPVARCV_CONST) \ _(2795, ZEND_MUL_DOUBLE_SPEC_TMPVARCV_TMPVARCV) \ - _(2796, ZEND_MUL_DOUBLE_SPEC_TMPVARCV_CONST) \ - _(2797, ZEND_MUL_DOUBLE_SPEC_TMPVARCV_TMPVARCV) \ + _(2796, ZEND_MUL_DOUBLE_SPEC_TMPVARCV_TMPVARCV) \ _(2798, ZEND_MUL_DOUBLE_SPEC_TMPVARCV_TMPVARCV) \ + _(2799, ZEND_MUL_DOUBLE_SPEC_TMPVARCV_CONST) \ _(2800, ZEND_MUL_DOUBLE_SPEC_TMPVARCV_TMPVARCV) \ - _(2806, ZEND_MUL_DOUBLE_SPEC_TMPVARCV_CONST) \ - _(2807, ZEND_MUL_DOUBLE_SPEC_TMPVARCV_TMPVARCV) \ - _(2808, ZEND_MUL_DOUBLE_SPEC_TMPVARCV_TMPVARCV) \ + _(2801, ZEND_MUL_DOUBLE_SPEC_TMPVARCV_TMPVARCV) \ + _(2803, ZEND_MUL_DOUBLE_SPEC_TMPVARCV_TMPVARCV) \ + _(2809, ZEND_MUL_DOUBLE_SPEC_TMPVARCV_CONST) \ _(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) \ + _(2811, ZEND_MUL_DOUBLE_SPEC_TMPVARCV_TMPVARCV) \ + _(2813, ZEND_MUL_DOUBLE_SPEC_TMPVARCV_TMPVARCV) \ + _(2829, ZEND_IS_EQUAL_LONG_SPEC_TMPVARCV_CONST) \ + _(2830, ZEND_IS_EQUAL_LONG_SPEC_TMPVARCV_CONST_JMPZ) \ + _(2831, ZEND_IS_EQUAL_LONG_SPEC_TMPVARCV_CONST_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) \ - _(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) \ + _(2835, ZEND_IS_EQUAL_LONG_SPEC_TMPVARCV_TMPVARCV) \ + _(2836, ZEND_IS_EQUAL_LONG_SPEC_TMPVARCV_TMPVARCV_JMPZ) \ + _(2837, 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) \ + _(2844, ZEND_IS_EQUAL_LONG_SPEC_TMPVARCV_CONST) \ + _(2845, ZEND_IS_EQUAL_LONG_SPEC_TMPVARCV_CONST_JMPZ) \ + _(2846, ZEND_IS_EQUAL_LONG_SPEC_TMPVARCV_CONST_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) \ - _(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) \ + _(2850, ZEND_IS_EQUAL_LONG_SPEC_TMPVARCV_TMPVARCV) \ + _(2851, ZEND_IS_EQUAL_LONG_SPEC_TMPVARCV_TMPVARCV_JMPZ) \ + _(2852, 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) \ + _(2874, ZEND_IS_EQUAL_LONG_SPEC_TMPVARCV_CONST) \ + _(2875, ZEND_IS_EQUAL_LONG_SPEC_TMPVARCV_CONST_JMPZ) \ + _(2876, ZEND_IS_EQUAL_LONG_SPEC_TMPVARCV_CONST_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) \ - _(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) \ + _(2880, ZEND_IS_EQUAL_LONG_SPEC_TMPVARCV_TMPVARCV) \ + _(2881, ZEND_IS_EQUAL_LONG_SPEC_TMPVARCV_TMPVARCV_JMPZ) \ + _(2882, ZEND_IS_EQUAL_LONG_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) \ + _(2904, ZEND_IS_EQUAL_DOUBLE_SPEC_TMPVARCV_CONST) \ + _(2905, ZEND_IS_EQUAL_DOUBLE_SPEC_TMPVARCV_CONST_JMPZ) \ + _(2906, ZEND_IS_EQUAL_DOUBLE_SPEC_TMPVARCV_CONST_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) \ - _(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) \ + _(2910, ZEND_IS_EQUAL_DOUBLE_SPEC_TMPVARCV_TMPVARCV) \ + _(2911, ZEND_IS_EQUAL_DOUBLE_SPEC_TMPVARCV_TMPVARCV_JMPZ) \ + _(2912, 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) \ + _(2919, ZEND_IS_EQUAL_DOUBLE_SPEC_TMPVARCV_CONST) \ + _(2920, ZEND_IS_EQUAL_DOUBLE_SPEC_TMPVARCV_CONST_JMPZ) \ + _(2921, ZEND_IS_EQUAL_DOUBLE_SPEC_TMPVARCV_CONST_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) \ - _(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) \ + _(2925, ZEND_IS_EQUAL_DOUBLE_SPEC_TMPVARCV_TMPVARCV) \ + _(2926, ZEND_IS_EQUAL_DOUBLE_SPEC_TMPVARCV_TMPVARCV_JMPZ) \ + _(2927, 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) \ + _(2949, ZEND_IS_EQUAL_DOUBLE_SPEC_TMPVARCV_CONST) \ + _(2950, ZEND_IS_EQUAL_DOUBLE_SPEC_TMPVARCV_CONST_JMPZ) \ + _(2951, ZEND_IS_EQUAL_DOUBLE_SPEC_TMPVARCV_CONST_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) \ - _(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) \ + _(2955, ZEND_IS_EQUAL_DOUBLE_SPEC_TMPVARCV_TMPVARCV) \ + _(2956, ZEND_IS_EQUAL_DOUBLE_SPEC_TMPVARCV_TMPVARCV_JMPZ) \ + _(2957, ZEND_IS_EQUAL_DOUBLE_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) \ + _(2979, ZEND_IS_NOT_EQUAL_LONG_SPEC_TMPVARCV_CONST) \ + _(2980, ZEND_IS_NOT_EQUAL_LONG_SPEC_TMPVARCV_CONST_JMPZ) \ + _(2981, ZEND_IS_NOT_EQUAL_LONG_SPEC_TMPVARCV_CONST_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) \ - _(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) \ + _(2985, ZEND_IS_NOT_EQUAL_LONG_SPEC_TMPVARCV_TMPVARCV) \ + _(2986, ZEND_IS_NOT_EQUAL_LONG_SPEC_TMPVARCV_TMPVARCV_JMPZ) \ + _(2987, 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) \ + _(2994, ZEND_IS_NOT_EQUAL_LONG_SPEC_TMPVARCV_CONST) \ + _(2995, ZEND_IS_NOT_EQUAL_LONG_SPEC_TMPVARCV_CONST_JMPZ) \ + _(2996, ZEND_IS_NOT_EQUAL_LONG_SPEC_TMPVARCV_CONST_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) \ - _(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) \ + _(3000, ZEND_IS_NOT_EQUAL_LONG_SPEC_TMPVARCV_TMPVARCV) \ + _(3001, ZEND_IS_NOT_EQUAL_LONG_SPEC_TMPVARCV_TMPVARCV_JMPZ) \ + _(3002, 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) \ + _(3024, ZEND_IS_NOT_EQUAL_LONG_SPEC_TMPVARCV_CONST) \ + _(3025, ZEND_IS_NOT_EQUAL_LONG_SPEC_TMPVARCV_CONST_JMPZ) \ + _(3026, ZEND_IS_NOT_EQUAL_LONG_SPEC_TMPVARCV_CONST_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) \ - _(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) \ + _(3030, ZEND_IS_NOT_EQUAL_LONG_SPEC_TMPVARCV_TMPVARCV) \ + _(3031, ZEND_IS_NOT_EQUAL_LONG_SPEC_TMPVARCV_TMPVARCV_JMPZ) \ + _(3032, ZEND_IS_NOT_EQUAL_LONG_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) \ + _(3054, ZEND_IS_NOT_EQUAL_DOUBLE_SPEC_TMPVARCV_CONST) \ + _(3055, ZEND_IS_NOT_EQUAL_DOUBLE_SPEC_TMPVARCV_CONST_JMPZ) \ + _(3056, ZEND_IS_NOT_EQUAL_DOUBLE_SPEC_TMPVARCV_CONST_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) \ - _(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) \ + _(3060, ZEND_IS_NOT_EQUAL_DOUBLE_SPEC_TMPVARCV_TMPVARCV) \ + _(3061, ZEND_IS_NOT_EQUAL_DOUBLE_SPEC_TMPVARCV_TMPVARCV_JMPZ) \ + _(3062, 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) \ + _(3069, ZEND_IS_NOT_EQUAL_DOUBLE_SPEC_TMPVARCV_CONST) \ + _(3070, ZEND_IS_NOT_EQUAL_DOUBLE_SPEC_TMPVARCV_CONST_JMPZ) \ + _(3071, ZEND_IS_NOT_EQUAL_DOUBLE_SPEC_TMPVARCV_CONST_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) \ - _(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) \ + _(3075, ZEND_IS_NOT_EQUAL_DOUBLE_SPEC_TMPVARCV_TMPVARCV) \ + _(3076, ZEND_IS_NOT_EQUAL_DOUBLE_SPEC_TMPVARCV_TMPVARCV_JMPZ) \ + _(3077, 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) \ + _(3099, ZEND_IS_NOT_EQUAL_DOUBLE_SPEC_TMPVARCV_CONST) \ + _(3100, ZEND_IS_NOT_EQUAL_DOUBLE_SPEC_TMPVARCV_CONST_JMPZ) \ + _(3101, ZEND_IS_NOT_EQUAL_DOUBLE_SPEC_TMPVARCV_CONST_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) \ - _(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) \ + _(3105, ZEND_IS_NOT_EQUAL_DOUBLE_SPEC_TMPVARCV_TMPVARCV) \ + _(3106, ZEND_IS_NOT_EQUAL_DOUBLE_SPEC_TMPVARCV_TMPVARCV_JMPZ) \ + _(3107, ZEND_IS_NOT_EQUAL_DOUBLE_SPEC_TMPVARCV_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) \ + _(3114, ZEND_IS_IDENTICAL_EMPTY_ARRAY_SPEC_TMPVARCV_CONST) \ + _(3115, ZEND_IS_IDENTICAL_EMPTY_ARRAY_SPEC_TMPVARCV_CONST_JMPZ) \ + _(3116, ZEND_IS_IDENTICAL_EMPTY_ARRAY_SPEC_TMPVARCV_CONST_JMPNZ) \ + _(3117, ZEND_IS_NOT_IDENTICAL_EMPTY_ARRAY_SPEC_TMPVARCV_CONST) \ + _(3118, ZEND_IS_NOT_IDENTICAL_EMPTY_ARRAY_SPEC_TMPVARCV_CONST_JMPZ) \ + _(3119, ZEND_IS_NOT_IDENTICAL_EMPTY_ARRAY_SPEC_TMPVARCV_CONST_JMPNZ) \ + _(3120, ZEND_IS_IDENTICAL_NOTHROW_SPEC_CV_CONST) \ + _(3124, ZEND_IS_IDENTICAL_NOTHROW_SPEC_CV_CV) \ + _(3125, ZEND_IS_NOT_IDENTICAL_NOTHROW_SPEC_CV_CONST) \ + _(3129, ZEND_IS_NOT_IDENTICAL_NOTHROW_SPEC_CV_CV) \ _(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) \ - _(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) \ + _(3136, ZEND_IS_SMALLER_LONG_SPEC_CONST_TMPVARCV) \ + _(3137, ZEND_IS_SMALLER_LONG_SPEC_CONST_TMPVARCV_JMPZ) \ + _(3138, ZEND_IS_SMALLER_LONG_SPEC_CONST_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) \ + _(3145, ZEND_IS_SMALLER_LONG_SPEC_TMPVARCV_CONST) \ + _(3146, ZEND_IS_SMALLER_LONG_SPEC_TMPVARCV_CONST_JMPZ) \ + _(3147, ZEND_IS_SMALLER_LONG_SPEC_TMPVARCV_CONST_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) \ - _(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) \ + _(3151, ZEND_IS_SMALLER_LONG_SPEC_TMPVARCV_TMPVARCV) \ + _(3152, ZEND_IS_SMALLER_LONG_SPEC_TMPVARCV_TMPVARCV_JMPZ) \ + _(3153, 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) \ + _(3160, ZEND_IS_SMALLER_LONG_SPEC_TMPVARCV_CONST) \ + _(3161, ZEND_IS_SMALLER_LONG_SPEC_TMPVARCV_CONST_JMPZ) \ + _(3162, ZEND_IS_SMALLER_LONG_SPEC_TMPVARCV_CONST_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) \ - _(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) \ + _(3166, ZEND_IS_SMALLER_LONG_SPEC_TMPVARCV_TMPVARCV) \ + _(3167, ZEND_IS_SMALLER_LONG_SPEC_TMPVARCV_TMPVARCV_JMPZ) \ + _(3168, 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) \ + _(3190, ZEND_IS_SMALLER_LONG_SPEC_TMPVARCV_CONST) \ + _(3191, ZEND_IS_SMALLER_LONG_SPEC_TMPVARCV_CONST_JMPZ) \ + _(3192, ZEND_IS_SMALLER_LONG_SPEC_TMPVARCV_CONST_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) \ - _(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) \ + _(3196, ZEND_IS_SMALLER_LONG_SPEC_TMPVARCV_TMPVARCV) \ + _(3197, ZEND_IS_SMALLER_LONG_SPEC_TMPVARCV_TMPVARCV_JMPZ) \ + _(3198, ZEND_IS_SMALLER_LONG_SPEC_TMPVARCV_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_DOUBLE_SPEC_CONST_TMPVARCV) \ _(3209, ZEND_IS_SMALLER_DOUBLE_SPEC_CONST_TMPVARCV_JMPZ) \ _(3210, ZEND_IS_SMALLER_DOUBLE_SPEC_CONST_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) \ + _(3211, ZEND_IS_SMALLER_DOUBLE_SPEC_CONST_TMPVARCV) \ + _(3212, ZEND_IS_SMALLER_DOUBLE_SPEC_CONST_TMPVARCV_JMPZ) \ + _(3213, ZEND_IS_SMALLER_DOUBLE_SPEC_CONST_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) \ + _(3220, ZEND_IS_SMALLER_DOUBLE_SPEC_TMPVARCV_CONST) \ + _(3221, ZEND_IS_SMALLER_DOUBLE_SPEC_TMPVARCV_CONST_JMPZ) \ + _(3222, ZEND_IS_SMALLER_DOUBLE_SPEC_TMPVARCV_CONST_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) \ - _(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) \ + _(3226, ZEND_IS_SMALLER_DOUBLE_SPEC_TMPVARCV_TMPVARCV) \ + _(3227, ZEND_IS_SMALLER_DOUBLE_SPEC_TMPVARCV_TMPVARCV_JMPZ) \ + _(3228, 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) \ + _(3235, ZEND_IS_SMALLER_DOUBLE_SPEC_TMPVARCV_CONST) \ + _(3236, ZEND_IS_SMALLER_DOUBLE_SPEC_TMPVARCV_CONST_JMPZ) \ + _(3237, ZEND_IS_SMALLER_DOUBLE_SPEC_TMPVARCV_CONST_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) \ - _(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) \ + _(3241, ZEND_IS_SMALLER_DOUBLE_SPEC_TMPVARCV_TMPVARCV) \ + _(3242, ZEND_IS_SMALLER_DOUBLE_SPEC_TMPVARCV_TMPVARCV_JMPZ) \ + _(3243, 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) \ + _(3265, ZEND_IS_SMALLER_DOUBLE_SPEC_TMPVARCV_CONST) \ + _(3266, ZEND_IS_SMALLER_DOUBLE_SPEC_TMPVARCV_CONST_JMPZ) \ + _(3267, ZEND_IS_SMALLER_DOUBLE_SPEC_TMPVARCV_CONST_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) \ - _(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) \ + _(3271, ZEND_IS_SMALLER_DOUBLE_SPEC_TMPVARCV_TMPVARCV) \ + _(3272, ZEND_IS_SMALLER_DOUBLE_SPEC_TMPVARCV_TMPVARCV_JMPZ) \ + _(3273, ZEND_IS_SMALLER_DOUBLE_SPEC_TMPVARCV_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_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) \ - _(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) \ + _(3286, ZEND_IS_SMALLER_OR_EQUAL_LONG_SPEC_CONST_TMPVARCV) \ + _(3287, ZEND_IS_SMALLER_OR_EQUAL_LONG_SPEC_CONST_TMPVARCV_JMPZ) \ + _(3288, ZEND_IS_SMALLER_OR_EQUAL_LONG_SPEC_CONST_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) \ + _(3295, ZEND_IS_SMALLER_OR_EQUAL_LONG_SPEC_TMPVARCV_CONST) \ + _(3296, ZEND_IS_SMALLER_OR_EQUAL_LONG_SPEC_TMPVARCV_CONST_JMPZ) \ + _(3297, ZEND_IS_SMALLER_OR_EQUAL_LONG_SPEC_TMPVARCV_CONST_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) \ - _(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) \ + _(3301, ZEND_IS_SMALLER_OR_EQUAL_LONG_SPEC_TMPVARCV_TMPVARCV) \ + _(3302, ZEND_IS_SMALLER_OR_EQUAL_LONG_SPEC_TMPVARCV_TMPVARCV_JMPZ) \ + _(3303, 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) \ + _(3310, ZEND_IS_SMALLER_OR_EQUAL_LONG_SPEC_TMPVARCV_CONST) \ + _(3311, ZEND_IS_SMALLER_OR_EQUAL_LONG_SPEC_TMPVARCV_CONST_JMPZ) \ + _(3312, ZEND_IS_SMALLER_OR_EQUAL_LONG_SPEC_TMPVARCV_CONST_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) \ - _(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) \ + _(3316, ZEND_IS_SMALLER_OR_EQUAL_LONG_SPEC_TMPVARCV_TMPVARCV) \ + _(3317, ZEND_IS_SMALLER_OR_EQUAL_LONG_SPEC_TMPVARCV_TMPVARCV_JMPZ) \ + _(3318, 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) \ + _(3340, ZEND_IS_SMALLER_OR_EQUAL_LONG_SPEC_TMPVARCV_CONST) \ + _(3341, ZEND_IS_SMALLER_OR_EQUAL_LONG_SPEC_TMPVARCV_CONST_JMPZ) \ + _(3342, ZEND_IS_SMALLER_OR_EQUAL_LONG_SPEC_TMPVARCV_CONST_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) \ - _(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) \ + _(3346, ZEND_IS_SMALLER_OR_EQUAL_LONG_SPEC_TMPVARCV_TMPVARCV) \ + _(3347, ZEND_IS_SMALLER_OR_EQUAL_LONG_SPEC_TMPVARCV_TMPVARCV_JMPZ) \ + _(3348, ZEND_IS_SMALLER_OR_EQUAL_LONG_SPEC_TMPVARCV_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_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) \ - _(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) \ + _(3361, ZEND_IS_SMALLER_OR_EQUAL_DOUBLE_SPEC_CONST_TMPVARCV) \ + _(3362, ZEND_IS_SMALLER_OR_EQUAL_DOUBLE_SPEC_CONST_TMPVARCV_JMPZ) \ + _(3363, ZEND_IS_SMALLER_OR_EQUAL_DOUBLE_SPEC_CONST_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) \ + _(3370, ZEND_IS_SMALLER_OR_EQUAL_DOUBLE_SPEC_TMPVARCV_CONST) \ + _(3371, ZEND_IS_SMALLER_OR_EQUAL_DOUBLE_SPEC_TMPVARCV_CONST_JMPZ) \ + _(3372, ZEND_IS_SMALLER_OR_EQUAL_DOUBLE_SPEC_TMPVARCV_CONST_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) \ - _(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) \ + _(3376, ZEND_IS_SMALLER_OR_EQUAL_DOUBLE_SPEC_TMPVARCV_TMPVARCV) \ + _(3377, ZEND_IS_SMALLER_OR_EQUAL_DOUBLE_SPEC_TMPVARCV_TMPVARCV_JMPZ) \ + _(3378, 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) \ + _(3385, ZEND_IS_SMALLER_OR_EQUAL_DOUBLE_SPEC_TMPVARCV_CONST) \ + _(3386, ZEND_IS_SMALLER_OR_EQUAL_DOUBLE_SPEC_TMPVARCV_CONST_JMPZ) \ + _(3387, ZEND_IS_SMALLER_OR_EQUAL_DOUBLE_SPEC_TMPVARCV_CONST_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) \ - _(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) \ + _(3391, ZEND_IS_SMALLER_OR_EQUAL_DOUBLE_SPEC_TMPVARCV_TMPVARCV) \ + _(3392, ZEND_IS_SMALLER_OR_EQUAL_DOUBLE_SPEC_TMPVARCV_TMPVARCV_JMPZ) \ + _(3393, 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) \ + _(3415, ZEND_IS_SMALLER_OR_EQUAL_DOUBLE_SPEC_TMPVARCV_CONST) \ + _(3416, ZEND_IS_SMALLER_OR_EQUAL_DOUBLE_SPEC_TMPVARCV_CONST_JMPZ) \ + _(3417, ZEND_IS_SMALLER_OR_EQUAL_DOUBLE_SPEC_TMPVARCV_CONST_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) \ - _(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) \ + _(3421, ZEND_IS_SMALLER_OR_EQUAL_DOUBLE_SPEC_TMPVARCV_TMPVARCV) \ + _(3422, ZEND_IS_SMALLER_OR_EQUAL_DOUBLE_SPEC_TMPVARCV_TMPVARCV_JMPZ) \ + _(3423, ZEND_IS_SMALLER_OR_EQUAL_DOUBLE_SPEC_TMPVARCV_TMPVARCV_JMPNZ) \ + _(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) \ + _(3430, ZEND_PRE_INC_LONG_NO_OVERFLOW_SPEC_CV_RETVAL_UNUSED) \ + _(3431, ZEND_PRE_INC_LONG_NO_OVERFLOW_SPEC_CV_RETVAL_USED) \ + _(3432, ZEND_PRE_INC_LONG_SPEC_CV_RETVAL_UNUSED) \ + _(3433, ZEND_PRE_INC_LONG_SPEC_CV_RETVAL_USED) \ + _(3434, ZEND_PRE_DEC_LONG_NO_OVERFLOW_SPEC_CV_RETVAL_UNUSED) \ + _(3435, ZEND_PRE_DEC_LONG_NO_OVERFLOW_SPEC_CV_RETVAL_USED) \ + _(3436, ZEND_PRE_DEC_LONG_SPEC_CV_RETVAL_UNUSED) \ + _(3437, ZEND_PRE_DEC_LONG_SPEC_CV_RETVAL_USED) \ + _(3438, ZEND_POST_INC_LONG_NO_OVERFLOW_SPEC_CV) \ + _(3439, ZEND_POST_INC_LONG_SPEC_CV) \ + _(3440, ZEND_POST_DEC_LONG_NO_OVERFLOW_SPEC_CV) \ + _(3441, ZEND_POST_DEC_LONG_SPEC_CV) \ + _(3442, ZEND_QM_ASSIGN_LONG_SPEC_CONST) \ _(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) \ + _(3444, ZEND_QM_ASSIGN_LONG_SPEC_TMPVARCV) \ + _(3446, ZEND_QM_ASSIGN_LONG_SPEC_TMPVARCV) \ + _(3447, ZEND_QM_ASSIGN_DOUBLE_SPEC_CONST) \ _(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) \ + _(3449, ZEND_QM_ASSIGN_DOUBLE_SPEC_TMPVARCV) \ + _(3451, ZEND_QM_ASSIGN_DOUBLE_SPEC_TMPVARCV) \ + _(3452, ZEND_QM_ASSIGN_NOREF_SPEC_CONST) \ _(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) \ + _(3454, ZEND_QM_ASSIGN_NOREF_SPEC_TMPVARCV) \ + _(3456, ZEND_QM_ASSIGN_NOREF_SPEC_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) \ + _(3459, ZEND_FETCH_DIM_R_INDEX_SPEC_CONST_TMPVARCV) \ + _(3461, ZEND_FETCH_DIM_R_INDEX_SPEC_CONST_TMPVARCV) \ + _(3462, ZEND_FETCH_DIM_R_INDEX_SPEC_TMPVAR_CONST) \ _(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) \ + _(3464, ZEND_FETCH_DIM_R_INDEX_SPEC_TMPVAR_TMPVARCV) \ _(3466, ZEND_FETCH_DIM_R_INDEX_SPEC_TMPVAR_TMPVARCV) \ + _(3467, ZEND_FETCH_DIM_R_INDEX_SPEC_TMPVAR_CONST) \ _(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) \ + _(3469, ZEND_FETCH_DIM_R_INDEX_SPEC_TMPVAR_TMPVARCV) \ + _(3471, ZEND_FETCH_DIM_R_INDEX_SPEC_TMPVAR_TMPVARCV) \ + _(3477, ZEND_FETCH_DIM_R_INDEX_SPEC_CV_CONST) \ _(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) + _(3479, ZEND_FETCH_DIM_R_INDEX_SPEC_CV_TMPVARCV) \ + _(3481, ZEND_FETCH_DIM_R_INDEX_SPEC_CV_TMPVARCV) \ + _(3484, ZEND_SEND_VAR_SIMPLE_SPEC_VAR) \ + _(3486, ZEND_SEND_VAR_SIMPLE_SPEC_CV) \ + _(3489, ZEND_SEND_VAR_EX_SIMPLE_SPEC_VAR_UNUSED) \ + _(3491, ZEND_SEND_VAR_EX_SIMPLE_SPEC_CV_UNUSED) \ + _(3492, ZEND_SEND_VAL_SIMPLE_SPEC_CONST) \ + _(3493, ZEND_SEND_VAL_EX_SIMPLE_SPEC_CONST) \ + _(3494, ZEND_FE_FETCH_R_SIMPLE_SPEC_VAR_CV_RETVAL_UNUSED) \ + _(3495, ZEND_FE_FETCH_R_SIMPLE_SPEC_VAR_CV_RETVAL_USED) \ + _(3495+1, ZEND_NULL) diff --git a/Zend/zend_vm_opcodes.c b/Zend/zend_vm_opcodes.c index 00ad38baaafe..88207966affa 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[214] = { "ZEND_NOP", "ZEND_ADD", "ZEND_SUB", @@ -234,9 +234,12 @@ 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_DO_FCALL_PARTIAL", + "ZEND_CHECK_PARTIAL_ARGS", }; -static uint32_t zend_vm_opcodes_flags[211] = { +static uint32_t zend_vm_opcodes_flags[214] = { 0x00000000, 0x00000b0b, 0x00000b0b, @@ -448,6 +451,9 @@ static uint32_t zend_vm_opcodes_flags[211] = { 0x01042003, 0x01001103, 0x00000303, + 0x00000101, + 0x00000000, + 0x00000101, }; 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 29469bb5f7dc..b1c7cfd458e4 100644 --- a/Zend/zend_vm_opcodes.h +++ b/Zend/zend_vm_opcodes.h @@ -294,7 +294,10 @@ 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_DO_FCALL_PARTIAL 212 +#define ZEND_CHECK_PARTIAL_ARGS 213 -#define ZEND_VM_LAST_OPCODE 210 +#define ZEND_VM_LAST_OPCODE 213 #endif diff --git a/configure.ac b/configure.ac index e4bd8162a2eb..2edc579b98b8 100644 --- a/configure.ac +++ b/configure.ac @@ -1774,6 +1774,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/reflection/php_reflection.c b/ext/reflection/php_reflection.c index 8ef0269481cf..001276ba485b 100644 --- a/ext/reflection/php_reflection.c +++ b/ext/reflection/php_reflection.c @@ -206,6 +206,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 { @@ -691,6 +692,10 @@ static void _enum_case_string(smart_str *str, const zend_string *name, zend_clas static zend_op *get_recv_op(const zend_op_array *op_array, uint32_t offset) { + if (op_array->fn_flags & ZEND_ACC_PARTIAL) { + op_array = (zend_op_array*) op_array->prototype; + } + zend_op *op = op_array->opcodes; const zend_op *end = op + op_array->last; @@ -703,15 +708,21 @@ 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); } @@ -923,6 +934,9 @@ static void _function_string(smart_str *str, zend_function *fptr, zend_class_ent if (fptr->common.fn_flags & ZEND_ACC_ABSTRACT) { smart_str_appends(str, "abstract "); } + if (fptr->common.fn_flags & ZEND_ACC_PARTIAL) { + smart_str_appends(str, "partial "); + } if (fptr->common.fn_flags & ZEND_ACC_FINAL) { smart_str_appends(str, "final "); } @@ -1429,6 +1443,31 @@ static void reflection_extension_factory(zval *object, const char *name_str) } /* }}} */ +static zend_always_inline uint32_t reflection_parameter_partial_offset(zend_function *fptr, zend_arg_info *info) { + zend_arg_info *arg = fptr->common.prototype->common.arg_info, + *end = arg + fptr->common.prototype->common.num_args; + uint32_t offset = 0; + + if (fptr->type == ZEND_USER_FUNCTION && + fptr->op_array.opcodes == &EG(call_trampoline_op)) { + return 0; + } + + if (fptr->common.fn_flags & ZEND_ACC_VARIADIC) { + end++; + } + + while (arg < end) { + if (zend_string_equals_ci(info->name, arg->name)) { + return offset; + } + offset++; + arg++; + } + ZEND_ASSERT(0); + return -1; +} + /* {{{ reflection_parameter_factory */ static void reflection_parameter_factory(zend_function *fptr, zval *closure_object, struct _zend_arg_info *arg_info, uint32_t offset, bool required, zval *object) { @@ -1440,7 +1479,11 @@ static void reflection_parameter_factory(zend_function *fptr, zval *closure_obje intern = Z_REFLECTION_P(object); reference = (parameter_reference*) emalloc(sizeof(parameter_reference)); reference->arg_info = arg_info; - reference->offset = offset; + if (fptr->common.fn_flags & ZEND_ACC_PARTIAL) { + reference->offset = reflection_parameter_partial_offset(fptr, arg_info); + } else { + reference->offset = offset; + } reference->required = required; reference->fptr = fptr; intern->ptr = reference; @@ -1750,7 +1793,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) { @@ -1798,7 +1843,21 @@ ZEND_METHOD(ReflectionFunctionAbstract, isClosure) ZEND_PARSE_PARAMETERS_NONE(); GET_REFLECTION_OBJECT_PTR(fptr); - RETURN_BOOL(fptr->common.fn_flags & ZEND_ACC_CLOSURE); + RETURN_BOOL(fptr->common.fn_flags & (ZEND_ACC_CLOSURE|ZEND_ACC_PARTIAL)); +} +/* }}} */ + +/* {{{ 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(fptr->common.fn_flags & ZEND_ACC_PARTIAL); } /* }}} */ @@ -1813,7 +1872,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 +1889,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/win32/build/config.w32 b/win32/build/config.w32 index f509e4eae933..6a6cd7a3c0b8 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({ From c419531cd499ae1867de6645dc28c3009b7123c1 Mon Sep 17 00:00:00 2001 From: Arnaud Le Blanc Date: Mon, 2 Jun 2025 19:35:05 +0200 Subject: [PATCH 02/28] Re-use trampoline call frame, use an opcode-based trampoline Like __call trampolines, we can use an opcode-based trampoline to reduce recursion/reentering, and reuse the call frame of the trampoline. Also: Rebase fixes, update tests, add tests. --- Zend/Optimizer/compact_literals.c | 1 + Zend/Optimizer/optimize_func_calls.c | 6 +- Zend/Optimizer/zend_call_graph.c | 2 +- Zend/Optimizer/zend_inference.c | 1 + .../compile_errors_003.phpt | 2 +- .../compile_errors_004.phpt | 2 +- .../compile_errors_006.phpt | 2 +- .../tests/partial_application/errors_003.phpt | 14 +- .../tests/partial_application/export_001.phpt | 4 +- .../partial_application/factory_001.phpt | 24 - .../partial_application/factory_002.phpt | 30 - .../partial_application/factory_003.phpt | 24 - .../partial_application/factory_004.phpt | 18 - Zend/tests/partial_application/fuzz_001.phpt | 16 + Zend/tests/partial_application/fuzz_002.phpt | 13 + Zend/tests/partial_application/fuzz_003.phpt | 20 + Zend/tests/partial_application/fuzz_004.phpt | 19 + Zend/tests/partial_application/fuzz_005.phpt | 16 + Zend/tests/partial_application/fuzz_006.phpt | 16 + Zend/tests/partial_application/fuzz_007.phpt | 19 + Zend/tests/partial_application/magic_001.phpt | 12 +- Zend/tests/partial_application/magic_002.phpt | 12 +- Zend/tests/partial_application/magic_005.phpt | 9 +- Zend/tests/partial_application/magic_006.phpt | 20 + .../named_placeholder_001.phpt | 109 +++ .../partial_application/references_001.phpt | 24 + .../partial_application/references_002.phpt | 24 + .../partial_application/references_003.phpt | 20 + .../partial_application/references_004.phpt | 17 + .../partial_application/reflection_001.phpt | 46 +- .../partial_application/reflection_002.phpt | 8 +- .../partial_application/reflection_003.phpt | 10 +- .../partial_application/return_type_001.phpt | 6 +- .../static_method_001.phpt | 6 +- .../partial_application/statics_001.phpt | 8 +- Zend/tests/partial_application/this_001.phpt | 6 +- .../variation_apply_002.phpt | 18 - .../variation_bind_001.phpt | 19 +- .../variation_bind_002.phpt | 19 +- .../variation_closure_001.phpt | 3 +- .../variation_closure_002.phpt | 3 +- .../variation_closure_003.phpt | 3 +- .../variation_debug_001.phpt | 10 +- .../variation_debug_002.phpt | 13 +- .../partial_application/variation_ex_001.phpt | 4 +- .../variation_factory_001.phpt | 17 - .../partial_application/variation_gc_001.phpt | 7 +- .../variation_nocall_002.phpt | 4 +- .../variation_parent_001.phpt | 21 +- .../variation_pass_001.phpt | 14 - .../variation_scope_001.phpt | 4 +- .../variation_strict_001.phpt | 4 +- .../variation_variadics_001.phpt | 2 +- .../variation_variadics_002.phpt | 3 +- .../variation_variadics_004.phpt | 4 +- .../variation_variadics_008.phpt | 4 +- Zend/zend_API.h | 3 +- Zend/zend_builtin_functions.c | 4 + Zend/zend_closures.c | 10 +- Zend/zend_closures.h | 2 + Zend/zend_compile.c | 94 +- Zend/zend_compile.h | 8 +- Zend/zend_execute.c | 42 +- Zend/zend_execute.h | 19 + Zend/zend_language_parser.y | 19 +- Zend/zend_object_handlers.c | 2 +- Zend/zend_partial.c | 830 +++++++++++------- Zend/zend_partial.h | 16 +- Zend/zend_vm_def.h | 188 +++- Zend/zend_vm_execute.h | 546 +++++++++--- Zend/zend_vm_handlers.h | 783 +++++++++-------- Zend/zend_vm_opcodes.c | 10 +- Zend/zend_vm_opcodes.h | 5 +- ext/reflection/php_reflection.c | 36 +- 74 files changed, 2124 insertions(+), 1255 deletions(-) delete mode 100644 Zend/tests/partial_application/factory_001.phpt delete mode 100644 Zend/tests/partial_application/factory_002.phpt delete mode 100644 Zend/tests/partial_application/factory_003.phpt delete mode 100644 Zend/tests/partial_application/factory_004.phpt create mode 100644 Zend/tests/partial_application/fuzz_001.phpt create mode 100644 Zend/tests/partial_application/fuzz_002.phpt create mode 100644 Zend/tests/partial_application/fuzz_003.phpt create mode 100644 Zend/tests/partial_application/fuzz_004.phpt create mode 100644 Zend/tests/partial_application/fuzz_005.phpt create mode 100644 Zend/tests/partial_application/fuzz_006.phpt create mode 100644 Zend/tests/partial_application/fuzz_007.phpt create mode 100644 Zend/tests/partial_application/magic_006.phpt create mode 100644 Zend/tests/partial_application/named_placeholder_001.phpt create mode 100644 Zend/tests/partial_application/references_001.phpt create mode 100644 Zend/tests/partial_application/references_002.phpt create mode 100644 Zend/tests/partial_application/references_003.phpt create mode 100644 Zend/tests/partial_application/references_004.phpt delete mode 100644 Zend/tests/partial_application/variation_apply_002.phpt delete mode 100644 Zend/tests/partial_application/variation_factory_001.phpt delete mode 100644 Zend/tests/partial_application/variation_pass_001.phpt diff --git a/Zend/Optimizer/compact_literals.c b/Zend/Optimizer/compact_literals.c index d0aaccec7ce2..28a506f68912 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 8b29f47c9497..39b62dc4d7af 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 b6a8aa3f7c5d..68ede34d88d0 100644 --- a/Zend/Optimizer/zend_call_graph.c +++ b/Zend/Optimizer/zend_call_graph.c @@ -128,7 +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_DO_FCALL_PARTIAL: + case ZEND_CALLABLE_CONVERT_PARTIAL: func_info->flags |= ZEND_FUNC_HAS_CALLS; if (call_info) { call_info->caller_call_opline = opline; diff --git a/Zend/Optimizer/zend_inference.c b/Zend/Optimizer/zend_inference.c index fc6b9b421b62..68bc2d4ce2b6 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/compile_errors_003.phpt b/Zend/tests/partial_application/compile_errors_003.phpt index 6d833b7ef1b6..c6b856a7199b 100644 --- a/Zend/tests/partial_application/compile_errors_003.phpt +++ b/Zend/tests/partial_application/compile_errors_003.phpt @@ -5,5 +5,5 @@ Partial application compile errors: named arguments must come after placeholder foo(n: 5, ?); ?> --EXPECTF-- -Fatal error: Named arguments must come after all placeholders in %s on line %d +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 index efd544282c2e..ad819416f07a 100644 --- a/Zend/tests/partial_application/compile_errors_004.phpt +++ b/Zend/tests/partial_application/compile_errors_004.phpt @@ -5,4 +5,4 @@ Partial application compile errors: named arguments must come after variadic pla foo(n: 5, ...); ?> --EXPECTF-- -Fatal error: Named arguments must come after all placeholders in %s on line %d +Fatal error: Cannot use positional argument after named argument 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 index 21ad1a954e92..9c819b95e9df 100644 --- a/Zend/tests/partial_application/compile_errors_006.phpt +++ b/Zend/tests/partial_application/compile_errors_006.phpt @@ -5,5 +5,5 @@ Partial application compile errors: mix application with unpack (placeholder aft foo(...["foo" => "bar"], ...); ?> --EXPECTF-- -Fatal error: Cannot combine partial application and unpacking %s on line %d +Fatal error: Cannot use positional argument after argument unpacking %s on line %d diff --git a/Zend/tests/partial_application/errors_003.phpt b/Zend/tests/partial_application/errors_003.phpt index 0ce69bc9ad65..dd5841ffcdc5 100644 --- a/Zend/tests/partial_application/errors_003.phpt +++ b/Zend/tests/partial_application/errors_003.phpt @@ -19,7 +19,7 @@ try { $foo = foo(?, ?); try { - $foo(1); + $foo(1); } catch (Error $ex) { printf("%s\n", $ex->getMessage()); } @@ -46,10 +46,10 @@ try { printf("%s\n", $ex->getMessage()); } -$usleep = usleep(...); +$repeat = str_repeat('a', ...); try { - $usleep(); + $repeat(); } catch (Error $ex) { printf("%s\n", $ex->getMessage()); } @@ -71,8 +71,8 @@ try { --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 2 expected, declared in %s on line 24 +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 implementation of usleep, 0 given and exactly 1 expected -not enough arguments for application of usleep, 0 given and exactly 1 expected -too many arguments for application of usleep, 2 given and a maximum of 1 expected +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 index a4af669148b1..b4a38de342a3 100644 --- a/Zend/tests/partial_application/export_001.phpt +++ b/Zend/tests/partial_application/export_001.phpt @@ -5,10 +5,10 @@ assert.exception=1 --FILE-- getMessage()); } ?> --EXPECT-- -assert(0 && foo(?) && foo(...)) +assert(0 && foo(?) && foo(new stdClass(), ...)) diff --git a/Zend/tests/partial_application/factory_001.phpt b/Zend/tests/partial_application/factory_001.phpt deleted file mode 100644 index ad34aebd027f..000000000000 --- a/Zend/tests/partial_application/factory_001.phpt +++ /dev/null @@ -1,24 +0,0 @@ ---TEST-- -Partial application factory: pass ---FILE-- - ---EXPECTF-- -OK -Foo::__destruct -Foo::__destruct diff --git a/Zend/tests/partial_application/factory_002.phpt b/Zend/tests/partial_application/factory_002.phpt deleted file mode 100644 index 2b94ead0379a..000000000000 --- a/Zend/tests/partial_application/factory_002.phpt +++ /dev/null @@ -1,30 +0,0 @@ ---TEST-- -Partial application factory: normal ---FILE-- - ---EXPECTF-- -Foo::__construct -Foo::__construct -OK -Foo::__destruct -Foo::__destruct diff --git a/Zend/tests/partial_application/factory_003.phpt b/Zend/tests/partial_application/factory_003.phpt deleted file mode 100644 index fb752b299b6c..000000000000 --- a/Zend/tests/partial_application/factory_003.phpt +++ /dev/null @@ -1,24 +0,0 @@ ---TEST-- -Partial application factory: exception ---FILE-- -getMessage()); -} -?> ---EXPECT-- -boo diff --git a/Zend/tests/partial_application/factory_004.phpt b/Zend/tests/partial_application/factory_004.phpt deleted file mode 100644 index dd46d203aacd..000000000000 --- a/Zend/tests/partial_application/factory_004.phpt +++ /dev/null @@ -1,18 +0,0 @@ ---TEST-- -Partial application factory object properties initialization ---FILE-- - ---EXPECTF-- -object(Foo)#%d (1) { - ["arg"]=> - int(1) -} - diff --git a/Zend/tests/partial_application/fuzz_001.phpt b/Zend/tests/partial_application/fuzz_001.phpt new file mode 100644 index 000000000000..aa3502d066ad --- /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 000000000000..46461b48bab9 --- /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 000000000000..32a11540be1b --- /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 000000000000..60e26ef120a7 --- /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 000000000000..a9927667078b --- /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 000000000000..a817c6b81285 --- /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 000000000000..8e2532f31cca --- /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/magic_001.phpt b/Zend/tests/partial_application/magic_001.phpt index db8770fbdc41..b83f8071fd72 100644 --- a/Zend/tests/partial_application/magic_001.phpt +++ b/Zend/tests/partial_application/magic_001.phpt @@ -5,7 +5,7 @@ Partial application magic: __call class Foo { public function __call($method, $args) { printf("%s::%s\n", __CLASS__, $method); - + var_dump(...$args); } } @@ -36,14 +36,14 @@ echo (string) new ReflectionFunction($bar); $bar(10); -$bar = $foo->method(...); +$bar = $foo->method(new Foo, ...); echo (string) new ReflectionFunction($bar); $bar(100); ?> --EXPECTF-- -Method [ partial public method method ] { +Partial [ public method method ] { @@ %s 12 - 12 - Parameters [1] { @@ -54,7 +54,7 @@ not enough arguments for application of Foo::method, 0 given and exactly 1 expec 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) -Method [ partial public method method ] { +Partial [ public method method ] { @@ %s 30 - 30 - Parameters [2] { @@ -64,7 +64,7 @@ Method [ partial public method method ] { } Foo::method int(10) -Method [ partial public method method ] { +Partial [ public method method ] { @@ %s 36 - 36 - Parameters [1] { @@ -72,5 +72,7 @@ Method [ partial public method method ] { } } 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 index e137c87b7814..2505599c2343 100644 --- a/Zend/tests/partial_application/magic_002.phpt +++ b/Zend/tests/partial_application/magic_002.phpt @@ -5,7 +5,7 @@ Partial application magic: __callStatic class Foo { public static function __callStatic($method, $args) { printf("%s::%s\n", __CLASS__, $method); - + var_dump(...$args); } } @@ -22,14 +22,14 @@ echo (string) new ReflectionFunction($bar); $bar(10); -$bar = Foo::method(...); +$bar = Foo::method(new Foo,...); echo (string) new ReflectionFunction($bar); $bar(100); ?> --EXPECTF-- -Method [ partial static public method method ] { +Partial [ public method method ] { @@ %s 10 - 10 - Parameters [1] { @@ -38,7 +38,7 @@ Method [ partial static public method method ] { } Foo::method int(1) -Method [ partial static public method method ] { +Partial [ public method method ] { @@ %s 16 - 16 - Parameters [2] { @@ -48,7 +48,7 @@ Method [ partial static public method method ] { } Foo::method int(10) -Method [ partial static public method method ] { +Partial [ public method method ] { @@ %s 22 - 22 - Parameters [1] { @@ -56,5 +56,7 @@ Method [ partial static public method method ] { } } Foo::method +object(Foo)#%d (0) { +} int(100) diff --git a/Zend/tests/partial_application/magic_005.phpt b/Zend/tests/partial_application/magic_005.phpt index ab66f9448e2c..cd62a64f34fd 100644 --- a/Zend/tests/partial_application/magic_005.phpt +++ b/Zend/tests/partial_application/magic_005.phpt @@ -12,7 +12,13 @@ $bar = $foo->method(?); var_dump($bar); ?> --EXPECTF-- -object(Closure)#%d (3) { +object(Closure)#%d (6) { + ["name"]=> + string(6) "method" + ["file"]=> + string(73) "%smagic_005.php" + ["line"]=> + int(8) ["this"]=> object(Foo)#%d (0) { } @@ -30,4 +36,3 @@ object(Closure)#%d (3) { } } } - diff --git a/Zend/tests/partial_application/magic_006.phpt b/Zend/tests/partial_application/magic_006.phpt new file mode 100644 index 000000000000..a5b4f1915c02 --- /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 000000000000..8da329bc7c5a --- /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 ] + } +} +int(1) +object(B)#%d (0) { +} +int(3) +Partial [ function foo ] { + @@ %snamed_placeholder_001.php 17 - 17 + + - Parameters [1] { + Parameter #0 [ $b ] + } +} +int(1) +object(B)#%d (0) { +} +int(3) +Partial [ function foo ] { + @@ %snamed_placeholder_001.php 24 - 24 + + - Parameters [1] { + Parameter #0 [ $b ] + } +} +int(1) +object(B)#%d (0) { +} +int(3) +Partial [ function bar ] { + @@ %snamed_placeholder_001.php 34 - 34 + + - Parameters [3] { + Parameter #0 [ $a ] + Parameter #1 [ $b ] + 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/references_001.phpt b/Zend/tests/partial_application/references_001.phpt new file mode 100644 index 000000000000..9e06069af218 --- /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 000000000000..2dc0c79b11da --- /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 000000000000..1a91fb79e655 --- /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 000000000000..208f81c2fedb --- /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 index e9f77a778687..cfb48b121a1e 100644 --- a/Zend/tests/partial_application/reflection_001.phpt +++ b/Zend/tests/partial_application/reflection_001.phpt @@ -6,13 +6,9 @@ function foo($a = 1, $b = 5, $c = 10) { } -$foo = foo(...); - -echo (string) new Reflectionfunction($foo); - $foo = foo(?, ...); -echo (string) new Reflectionfunction($foo); +echo (string) new ReflectionFunction($foo); $foo = foo(?, ?, ...); @@ -23,40 +19,30 @@ $foo = foo(?, ?, ?); echo (string) new ReflectionFunction($foo); ?> --EXPECTF-- -Function [ partial function foo ] { - @@ %s 6 - 6 - - - Parameters [3] { - Parameter #0 [ $a = 1 ] - Parameter #1 [ $b = 5 ] - Parameter #2 [ $c = 10 ] - } -} -Function [ partial function foo ] { - @@ %s 10 - 10 +Partial [ function foo ] { + @@ %sreflection_001.php 6 - 6 - Parameters [3] { - Parameter #0 [ $a ] - Parameter #1 [ $b = 5 ] - Parameter #2 [ $c = 10 ] + Parameter #0 [ $a ] + Parameter #1 [ $b ] + Parameter #2 [ $c ] } } -Function [ partial function foo ] { - @@ %s 14 - 14 +Partial [ function foo ] { + @@ %sreflection_001.php 10 - 10 - Parameters [3] { - Parameter #0 [ $a ] - Parameter #1 [ $b ] - Parameter #2 [ $c = 10 ] + Parameter #0 [ $a ] + Parameter #1 [ $b ] + Parameter #2 [ $c ] } } -Function [ partial function foo ] { - @@ %s 18 - 18 +Partial [ function foo ] { + @@ %sreflection_001.php 14 - 14 - Parameters [3] { - Parameter #0 [ $a ] - Parameter #1 [ $b ] - Parameter #2 [ $c ] + Parameter #0 [ $a ] + Parameter #1 [ $b ] + Parameter #2 [ $c ] } } - diff --git a/Zend/tests/partial_application/reflection_002.phpt b/Zend/tests/partial_application/reflection_002.phpt index dbdbc56e815a..4f677701938e 100644 --- a/Zend/tests/partial_application/reflection_002.phpt +++ b/Zend/tests/partial_application/reflection_002.phpt @@ -23,14 +23,14 @@ $foo = foo(?, ?, ?); echo (string) new ReflectionFunction($foo); ?> --EXPECTF-- -Function [ partial function foo ] { +Partial [ function foo ] { @@ %s 6 - 6 - Parameters [1] { Parameter #0 [ $a ] } } -Function [ partial function foo ] { +Partial [ function foo ] { @@ %s 10 - 10 - Parameters [2] { @@ -38,7 +38,7 @@ Function [ partial function foo ] { Parameter #1 [ ...$b ] } } -Function [ partial function foo ] { +Partial [ function foo ] { @@ %s 14 - 14 - Parameters [2] { @@ -46,7 +46,7 @@ Function [ partial function foo ] { Parameter #1 [ $b ] } } -Function [ partial function foo ] { +Partial [ function foo ] { @@ %s 18 - 18 - Parameters [3] { diff --git a/Zend/tests/partial_application/reflection_003.phpt b/Zend/tests/partial_application/reflection_003.phpt index cd117c94ecf6..db4827b066dd 100644 --- a/Zend/tests/partial_application/reflection_003.phpt +++ b/Zend/tests/partial_application/reflection_003.phpt @@ -15,14 +15,16 @@ $foo = sprintf(?, ?); echo (string) new ReflectionFunction($foo); ?> --EXPECTF-- -Function [ partial function sprintf ] { +Partial [ function sprintf ] { + @@ %sreflection_003.php 2 - 2 - Parameters [1] { Parameter #0 [ string $format ] } - Return [ string ] } -Function [ partial function sprintf ] { +Partial [ function sprintf ] { + @@ %sreflection_003.php 6 - 6 - Parameters [2] { Parameter #0 [ string $format ] @@ -30,7 +32,8 @@ Function [ partial function sprintf ] { } - Return [ string ] } -Function [ partial function sprintf ] { +Partial [ function sprintf ] { + @@ %sreflection_003.php 10 - 10 - Parameters [2] { Parameter #0 [ string $format ] @@ -38,4 +41,3 @@ Function [ partial function sprintf ] { } - Return [ string ] } - diff --git a/Zend/tests/partial_application/return_type_001.phpt b/Zend/tests/partial_application/return_type_001.phpt index af7826ff160a..571ac6b5a9d5 100644 --- a/Zend/tests/partial_application/return_type_001.phpt +++ b/Zend/tests/partial_application/return_type_001.phpt @@ -2,12 +2,12 @@ Partial application return type --FILE-- --EXPECTF-- -Function [ partial function foo ] { +Partial [ function foo ] { @@ %s 4 - 4 - Parameters [0] { diff --git a/Zend/tests/partial_application/static_method_001.phpt b/Zend/tests/partial_application/static_method_001.phpt index a690873499ba..d66bc4c45153 100644 --- a/Zend/tests/partial_application/static_method_001.phpt +++ b/Zend/tests/partial_application/static_method_001.phpt @@ -3,14 +3,14 @@ Partial application static method --FILE-- diff --git a/Zend/tests/partial_application/statics_001.phpt b/Zend/tests/partial_application/statics_001.phpt index c4edc8eaceb7..3f8d80a5c387 100644 --- a/Zend/tests/partial_application/statics_001.phpt +++ b/Zend/tests/partial_application/statics_001.phpt @@ -2,17 +2,17 @@ Partial application static variables shared --FILE-- method(...); +$bar = $foo->method(new stdClass, ...); -$baz = $bar(...); +$baz = $bar(new stdClass, ...); var_dump($baz()); ?> diff --git a/Zend/tests/partial_application/variation_apply_002.phpt b/Zend/tests/partial_application/variation_apply_002.phpt deleted file mode 100644 index abdf24788081..000000000000 --- a/Zend/tests/partial_application/variation_apply_002.phpt +++ /dev/null @@ -1,18 +0,0 @@ ---TEST-- -Partial application variation type ---FILE-- - ---EXPECT-- -Foo::__construct diff --git a/Zend/tests/partial_application/variation_bind_001.phpt b/Zend/tests/partial_application/variation_bind_001.phpt index 4b064d024ffc..46939780d3c7 100644 --- a/Zend/tests/partial_application/variation_bind_001.phpt +++ b/Zend/tests/partial_application/variation_bind_001.phpt @@ -11,7 +11,7 @@ class Param { class Foo { public function method($a, $b) { - printf("%s: %s, %s\n", get_called_class(), $a, $b); + printf("%s: %s, %s\n", get_called_class(), $a, $b); } } @@ -34,7 +34,13 @@ var_dump($bound); ?> --EXPECTF-- Bar: 1, Param -object(Closure)#%d (3) { +object(Closure)#%d (6) { + ["name"]=> + string(6) "method" + ["file"]=> + string(82) "%svariation_bind_001.php" + ["line"]=> + int(20) ["this"]=> object(Bar)#%d (0) { } @@ -53,7 +59,13 @@ object(Closure)#%d (3) { } } Foo: 1, Param -object(Closure)#%d (3) { +object(Closure)#%d (6) { + ["name"]=> + string(6) "method" + ["file"]=> + string(82) "%svariation_bind_001.php" + ["line"]=> + int(26) ["this"]=> object(Foo)#%d (0) { } @@ -71,4 +83,3 @@ object(Closure)#%d (3) { } } } - diff --git a/Zend/tests/partial_application/variation_bind_002.phpt b/Zend/tests/partial_application/variation_bind_002.phpt index ccd3134e2683..cd84ba955f61 100644 --- a/Zend/tests/partial_application/variation_bind_002.phpt +++ b/Zend/tests/partial_application/variation_bind_002.phpt @@ -11,7 +11,7 @@ class Param { class Foo { public static function method($a, $b) { - printf("%s: %s, %s\n", get_called_class(), $a, $b); + printf("%s: %s, %s\n", get_called_class(), $a, $b); } } @@ -33,7 +33,13 @@ var_dump($bound); ?> --EXPECTF-- Bar: 1, Param -object(Closure)#%d (2) { +object(Closure)#%d (5) { + ["name"]=> + string(6) "method" + ["file"]=> + string(82) "%svariation_bind_002.php" + ["line"]=> + int(19) ["parameter"]=> array(1) { ["$a"]=> @@ -49,7 +55,13 @@ object(Closure)#%d (2) { } } Foo: 1, Param -object(Closure)#%d (2) { +object(Closure)#%d (5) { + ["name"]=> + string(6) "method" + ["file"]=> + string(82) "%svariation_bind_002.php" + ["line"]=> + int(25) ["parameter"]=> array(1) { ["$a"]=> @@ -64,4 +76,3 @@ object(Closure)#%d (2) { } } } - diff --git a/Zend/tests/partial_application/variation_closure_001.phpt b/Zend/tests/partial_application/variation_closure_001.phpt index c9e7ea96f55d..fd6cfd00a111 100644 --- a/Zend/tests/partial_application/variation_closure_001.phpt +++ b/Zend/tests/partial_application/variation_closure_001.phpt @@ -9,11 +9,10 @@ $closure = function($a, $b) { echo (string) new ReflectionFunction($closure(1, ?)); ?> --EXPECTF-- -Function [ partial function {closure} ] { +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 index 572c8bd0a674..b439a0337551 100644 --- a/Zend/tests/partial_application/variation_closure_002.phpt +++ b/Zend/tests/partial_application/variation_closure_002.phpt @@ -13,7 +13,8 @@ echo (string) new ReflectionFunction($function); $function(10); ?> --EXPECTF-- -Method [ partial public method __invoke ] { +Partial [ public method __invoke ] { + @@ %svariation_closure_002.php 6 - 6 - Parameters [1] { Parameter #0 [ $b ] diff --git a/Zend/tests/partial_application/variation_closure_003.phpt b/Zend/tests/partial_application/variation_closure_003.phpt index f1363efaf7b3..52d0080fd390 100644 --- a/Zend/tests/partial_application/variation_closure_003.phpt +++ b/Zend/tests/partial_application/variation_closure_003.phpt @@ -21,7 +21,8 @@ echo (string) new ReflectionFunction($function); var_dump($function(10)); ?> --EXPECTF-- -Method [ partial public method __invoke ] { +Partial [ public method __invoke ] { + @@ %svariation_closure_003.php 14 - 14 - Parameters [1] { Parameter #0 [ $b ] diff --git a/Zend/tests/partial_application/variation_debug_001.phpt b/Zend/tests/partial_application/variation_debug_001.phpt index 6981885c09e7..5a6ee48998f9 100644 --- a/Zend/tests/partial_application/variation_debug_001.phpt +++ b/Zend/tests/partial_application/variation_debug_001.phpt @@ -9,11 +9,17 @@ function bar($a = 1, $b = 2, ...$c) { var_dump(bar(?, new stdClass, 20, new stdClass, four: 4)); ?> --EXPECTF-- -object(Closure)#%d (2) { +object(Closure)#%d (5) { + ["name"]=> + string(3) "bar" + ["file"]=> + string(83) "%svariation_debug_001.php" + ["line"]=> + int(6) ["parameter"]=> array(1) { ["$a"]=> - string(10) "" + string(10) "" } ["args"]=> array(3) { diff --git a/Zend/tests/partial_application/variation_debug_002.phpt b/Zend/tests/partial_application/variation_debug_002.phpt index ef6fa8e54533..3d3fe6a81dbf 100644 --- a/Zend/tests/partial_application/variation_debug_002.phpt +++ b/Zend/tests/partial_application/variation_debug_002.phpt @@ -5,7 +5,13 @@ Partial application variation debug internal var_dump(array_map(?, [1,2,3], [4,5, 6], ..., four: new stdClass)); ?> --EXPECTF-- -object(Closure)#%d (2) { +object(Closure)#%d (5) { + ["name"]=> + string(9) "array_map" + ["file"]=> + string(83) "%svariation_debug_002.php" + ["line"]=> + int(2) ["parameter"]=> array(2) { ["$callback"]=> @@ -27,7 +33,7 @@ object(Closure)#%d (2) { int(3) } ["arrays"]=> - array(3) { + array(2) { [0]=> array(3) { [0]=> @@ -37,12 +43,9 @@ object(Closure)#%d (2) { [2]=> int(6) } - [1]=> - NULL ["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 index dc72ccdae797..9965b6fa6e98 100644 --- a/Zend/tests/partial_application/variation_ex_001.phpt +++ b/Zend/tests/partial_application/variation_ex_001.phpt @@ -2,10 +2,10 @@ Partial application variation uaf in cleanup unfinished calls --FILE-- getTimeZone()); -?> ---EXPECTF-- -object(DateTimeZone)#%d (2) { - ["timezone_type"]=> - int(3) - ["timezone"]=> - string(10) "Asia/Tokyo" -} - diff --git a/Zend/tests/partial_application/variation_gc_001.phpt b/Zend/tests/partial_application/variation_gc_001.phpt index 00b951a81ea9..cbb198632f82 100644 --- a/Zend/tests/partial_application/variation_gc_001.phpt +++ b/Zend/tests/partial_application/variation_gc_001.phpt @@ -2,14 +2,15 @@ Partial application variation GC --FILE-- method = self::__construct(...); + public function __construct($a) { + $this->method = self::__construct(new stdClass, ...); } } -$foo = new Foo; +$foo = new Foo(new stdClass); $foo->bar = $foo; echo "OK"; diff --git a/Zend/tests/partial_application/variation_nocall_002.phpt b/Zend/tests/partial_application/variation_nocall_002.phpt index 35bcddc15e2a..f6b5f3fbf346 100644 --- a/Zend/tests/partial_application/variation_nocall_002.phpt +++ b/Zend/tests/partial_application/variation_nocall_002.phpt @@ -3,10 +3,10 @@ Partial application variation no call order of destruction --FILE-- method(...)(...); +$foo->method(new stdClass, ...)(new stdClass, ...); echo "OK"; ?> diff --git a/Zend/tests/partial_application/variation_parent_001.phpt b/Zend/tests/partial_application/variation_parent_001.phpt index c6440a9481be..cf1b2f06565b 100644 --- a/Zend/tests/partial_application/variation_parent_001.phpt +++ b/Zend/tests/partial_application/variation_parent_001.phpt @@ -17,7 +17,13 @@ $baz = $bar(20, ...); var_dump($baz, $baz()); ?> --EXPECTF-- -object(Closure)#%d (3) { +object(Closure)#%d (6) { + ["name"]=> + string(6) "method" + ["file"]=> + string(84) "%svariation_parent_001.php" + ["line"]=> + int(12) ["this"]=> object(Foo)#%d (0) { } @@ -33,15 +39,18 @@ object(Closure)#%d (3) { ["b"]=> int(20) ["c"]=> - array(1) { - [0]=> - NULL + array(0) { } } } -object(Closure)#%d (1) { +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_pass_001.phpt b/Zend/tests/partial_application/variation_pass_001.phpt deleted file mode 100644 index e432e079b45d..000000000000 --- a/Zend/tests/partial_application/variation_pass_001.phpt +++ /dev/null @@ -1,14 +0,0 @@ ---TEST-- -Partial application variation pass ---FILE-- -getMessage()); -} -?> ---EXPECT-- -too many arguments or placeholders for application of Foo::__construct, 1 given and a maximum of 0 expected diff --git a/Zend/tests/partial_application/variation_scope_001.phpt b/Zend/tests/partial_application/variation_scope_001.phpt index afdfba9e920d..4cda24a7834e 100644 --- a/Zend/tests/partial_application/variation_scope_001.phpt +++ b/Zend/tests/partial_application/variation_scope_001.phpt @@ -3,14 +3,14 @@ Partial application variation called scope --FILE-- method(...); +$bar = $foo->method(new stdClass, ...); $bar(); ?> diff --git a/Zend/tests/partial_application/variation_strict_001.phpt b/Zend/tests/partial_application/variation_strict_001.phpt index f1aebb7d2d01..0817a5f9ab17 100644 --- a/Zend/tests/partial_application/variation_strict_001.phpt +++ b/Zend/tests/partial_application/variation_strict_001.phpt @@ -16,6 +16,6 @@ try { printf("%s\n", $ex->getMessage()); } ?> ---EXPECT-- -foo(): Argument #1 ($int) must be of type int, string given +--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 index 79deca98f2f9..d92287e853ee 100644 --- a/Zend/tests/partial_application/variation_variadics_001.phpt +++ b/Zend/tests/partial_application/variation_variadics_001.phpt @@ -13,7 +13,7 @@ echo (string) new ReflectionFunction($foo); $foo(1000, 10000); ?> --EXPECTF-- -Function [ partial function foo ] { +Partial [ function foo ] { @@ %s 6 - 6 - Parameters [1] { diff --git a/Zend/tests/partial_application/variation_variadics_002.phpt b/Zend/tests/partial_application/variation_variadics_002.phpt index fba07a365b14..97173349fa8a 100644 --- a/Zend/tests/partial_application/variation_variadics_002.phpt +++ b/Zend/tests/partial_application/variation_variadics_002.phpt @@ -9,7 +9,8 @@ echo (string) new ReflectionFunction($sprintf); echo $sprintf(1000, 10000); ?> --EXPECTF-- -Function [ partial function sprintf ] { +Partial [ function sprintf ] { + @@ %svariation_variadics_002.php 2 - 2 - Parameters [1] { Parameter #0 [ mixed ...$values ] diff --git a/Zend/tests/partial_application/variation_variadics_004.phpt b/Zend/tests/partial_application/variation_variadics_004.phpt index 6cda0fc81f5e..e972f6c982ef 100644 --- a/Zend/tests/partial_application/variation_variadics_004.phpt +++ b/Zend/tests/partial_application/variation_variadics_004.phpt @@ -51,7 +51,7 @@ array(3) { ["day"]=> int(1) ["month"]=> - int(1) + int(12) ["year"]=> int(2005) } @@ -61,5 +61,5 @@ array(3) { ["month"]=> int(12) ["year"]=> - int(2005) + int(2016) } diff --git a/Zend/tests/partial_application/variation_variadics_008.phpt b/Zend/tests/partial_application/variation_variadics_008.phpt index 214ed9b4db9c..d22a4e58fd70 100644 --- a/Zend/tests/partial_application/variation_variadics_008.phpt +++ b/Zend/tests/partial_application/variation_variadics_008.phpt @@ -12,6 +12,6 @@ try { echo $ex->getMessage() . PHP_EOL; } ?> ---EXPECT-- -too many arguments or placeholders for application of Closure::__invoke, 2 given and a maximum of 1 expected +--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_API.h b/Zend/zend_API.h index a644de8e1513..1b2ad0b93f7d 100644 --- a/Zend/zend_API.h +++ b/Zend/zend_API.h @@ -849,7 +849,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_builtin_functions.c b/Zend/zend_builtin_functions.c index 7a07ceadce2e..ca61f9e99a9a 100644 --- a/Zend/zend_builtin_functions.c +++ b/Zend/zend_builtin_functions.c @@ -1695,6 +1695,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 7324228ee5da..59745d6c762e 100644 --- a/Zend/zend_closures.c +++ b/Zend/zend_closures.c @@ -33,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; @@ -59,7 +60,7 @@ 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); @@ -76,8 +77,6 @@ ZEND_METHOD(Closure, __invoke) /* {{{ */ * an invalid func pointer sitting on there, so this was changed in PHP 8.3. */ execute_data->func = NULL; - -#if ZEND_DEBUG } /* }}} */ @@ -167,11 +166,12 @@ ZEND_METHOD(Closure, call) ZVAL_UNDEF(&closure_result); fci.retval = &closure_result; - if (closure->func.common.fn_flags & ZEND_ACC_PARTIAL) { + 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); @@ -270,7 +270,7 @@ static void do_closure_bind(zval *return_value, zval *zclosure, zval *newthis, z called_scope = ce; } - if (closure->func.common.fn_flags & ZEND_ACC_PARTIAL) { + 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); diff --git a/Zend/zend_closures.h b/Zend/zend_closures.h index 8c194978a61d..d17451980ff4 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))) diff --git a/Zend/zend_compile.c b/Zend/zend_compile.c index a958e87a38fb..2d1f72002a84 100644 --- a/Zend/zend_compile.c +++ b/Zend/zend_compile.c @@ -3752,47 +3752,6 @@ static uint32_t zend_compile_args( continue; } - 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) { - if (uses_named_args) { - zend_error_noreturn(E_COMPILE_ERROR, - "Named arguments must come after all placeholders"); - } - - if (uses_variadic_placeholder) { - zend_error_noreturn(E_COMPILE_ERROR, - "Variadic placeholder may only appear once"); - } - - uses_variadic_placeholder = true; - } else if (arg->attr == _IS_PLACEHOLDER_ARG) { - if (uses_named_args) { - zend_error_noreturn(E_COMPILE_ERROR, - "Named arguments must come after all placeholders"); - } - - if (uses_variadic_placeholder) { - zend_error_noreturn(E_COMPILE_ERROR, - "Only named arguments may follow variadic placeholder"); - } - } - - fbc = NULL; - - opline = zend_emit_op(NULL, ZEND_SEND_PLACEHOLDER, NULL, NULL); - opline->op1.num = arg->attr; - opline->op2.num = arg_num; - - *is_call_partial = true; - arg_count++; - continue; - } - if (arg->kind == ZEND_AST_NAMED_ARG) { uses_named_args = 1; arg_name = zval_make_interned_string(zend_ast_get_zval(arg->child[0])); @@ -3828,13 +3787,47 @@ static uint32_t zend_compile_args( } if (uses_variadic_placeholder) { - zend_error_noreturn(E_COMPILE_ERROR, - "Only named arguments may follow 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); + } + + *is_call_partial = true; + 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)) { @@ -3950,6 +3943,7 @@ static uint32_t zend_compile_args( 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); } @@ -3996,13 +3990,15 @@ void zend_compile_call_partial(znode *result, uint32_t arg_count, bool may_have_ opline->extended_value = arg_count; + if (opline->opcode == ZEND_NEW) { + zend_error_noreturn(E_COMPILE_ERROR, "Cannot create partial application for new expression"); + } + if (opline->opcode == ZEND_INIT_FCALL) { opline->op1.num = zend_vm_calc_used_stack(arg_count, fbc); - } else if (opline->opcode == ZEND_NEW) { - GET_NODE(&op1, opline->result); } - opline = zend_emit_op(result, ZEND_DO_FCALL_PARTIAL, + opline = zend_emit_op_tmp(result, ZEND_CALLABLE_CONVERT_PARTIAL, (opline->opcode == ZEND_NEW) ? &op1 : NULL, NULL); if (may_have_extra_named_args) { @@ -4038,7 +4034,7 @@ static bool zend_compile_call_common(znode *result, zend_ast *args_ast, zend_fun if (is_partial_call) { zend_compile_call_partial(result, arg_count, may_have_extra_named_args, opnum_init, fbc); - return; + return true; } zend_do_extended_fcall_begin(); @@ -4801,7 +4797,7 @@ static void zend_compile_ns_call(znode *result, znode *name_node, zend_ast *args /* Find frameless function with same name. */ zend_function *frameless_function = NULL; if (args_ast->kind != ZEND_AST_CALLABLE_CONVERT - && !zend_args_contain_unpack_or_named(zend_ast_get_list(args_ast)) + && !zend_args_contain_unpack_or_named_or_partial(zend_ast_get_list(args_ast)) /* Avoid blowing up op count with nested frameless branches. */ && !CG(context).in_jmp_frameless_branch) { zend_string *lc_func_name = Z_STR_P(CT_CONSTANT_EX(CG(active_op_array), name_constants + 2)); @@ -5109,7 +5105,7 @@ static zend_result zend_try_compile_special_func(znode *result, zend_string *lcn return FAILURE; } - if (zend_args_contain_unpack_or_named(args)) { + if (zend_args_contain_unpack_or_named_or_partial(args)) { return FAILURE; } diff --git a/Zend/zend_compile.h b/Zend/zend_compile.h index 86e80e373e63..e96ebfedf6bc 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,11 +403,8 @@ typedef struct _zend_oparray_context { /* has #[\NoDiscard] attribute | | | */ #define ZEND_ACC_NODISCARD (1 << 29) /* | X | | */ /* | | | */ -/* flag used by partial application (int only) | | | */ -// TODO #define ZEND_ACC_PARTIAL (1 << 30) /* | X | | */ -/* | | | */ /* trampoline is permanent | | | */ -#define ZEND_ACC_TRAMPOLINE_PERMANENT (1 << 28) /* | X | | */ +#define ZEND_ACC_TRAMPOLINE_PERMANENT (1 << 30) /* | X | | */ /* | | | */ /* op_array uses strict mode types | | | */ #define ZEND_ACC_STRICT_TYPES (1U << 31) /* | X | | */ @@ -660,7 +657,6 @@ struct _zend_execute_data { /* keep all local variables for "fcall_end" handler */ #define ZEND_CALL_JIT_RESERVED (1 << 29) /* reserved for tracing JIT */ #define ZEND_CALL_NEEDS_REATTACH (1 << 30) -// TODO #define ZEND_CALL_VARIADIC_PLACEHOLDER (1<<29) #define ZEND_CALL_SEND_ARG_BY_REF (1u << 31) #define ZEND_CALL_NESTED_FUNCTION (ZEND_CALL_FUNCTION | ZEND_CALL_NESTED) diff --git a/Zend/zend_execute.c b/Zend/zend_execute.c index 0e5ee4491aa1..4fd54df08db3 100644 --- a/Zend/zend_execute.c +++ b/Zend/zend_execute.c @@ -1298,9 +1298,7 @@ static zend_never_inline ZEND_ATTRIBUTE_UNUSED bool zend_verify_internal_arg_typ * trust that arginfo matches what is enforced by zend_parse_parameters. */ ZEND_API bool zend_internal_call_should_throw(const zend_function *fbc, zend_execute_data *call) { - if (fbc->internal_function.handler == ZEND_FN(pass) - || (fbc->internal_function.fn_flags & ZEND_ACC_FAKE_CLOSURE) - || fbc->internal_function.handler == zend_partial_call_magic) { + if (fbc->internal_function.handler == ZEND_FN(pass) || (fbc->internal_function.fn_flags & ZEND_ACC_FAKE_CLOSURE)) { /* Be lenient about the special pass function and about fake closures. */ return 0; } @@ -4320,7 +4318,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); @@ -4337,7 +4335,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)) { @@ -4349,13 +4347,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) /* {{{ */ @@ -4492,6 +4497,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); @@ -4698,7 +4706,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_DO_FCALL_PARTIAL: + case ZEND_CALLABLE_CONVERT_PARTIAL: level++; break; case ZEND_INIT_FCALL: @@ -4757,7 +4765,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_DO_FCALL_PARTIAL: + case ZEND_CALLABLE_CONVERT_PARTIAL: level++; break; case ZEND_INIT_FCALL: @@ -4781,6 +4789,9 @@ static void cleanup_unfinished_calls(zend_execute_data *execute_data, uint32_t o zend_vm_stack_free_args(EX(call)); + if (ZEND_CALL_INFO(call) & ZEND_CALL_RELEASE_THIS) { + OBJ_RELEASE(Z_OBJ(call->This)); + } if (ZEND_CALL_INFO(call) & ZEND_CALL_HAS_EXTRA_NAMED_PARAMS) { zend_free_extra_named_params(call->extra_named_params); } @@ -4790,9 +4801,6 @@ static void cleanup_unfinished_calls(zend_execute_data *execute_data, uint32_t o zend_string_release_ex(call->func->common.function_name, 0); zend_free_trampoline(call->func); } - if (ZEND_CALL_INFO(call) & ZEND_CALL_RELEASE_THIS) { - OBJ_RELEASE(Z_OBJ(call->This)); - } EX(call) = call->prev_execute_data; zend_vm_stack_free_call_frame(call); @@ -5551,6 +5559,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); @@ -5627,11 +5639,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 cf15c9e3b2db..02e3d37bc65d 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_language_parser.y b/Zend/zend_language_parser.y index d79cb58f9932..133b3b3f3e2f 100644 --- a/Zend/zend_language_parser.y +++ b/Zend/zend_language_parser.y @@ -259,7 +259,7 @@ static YYSIZE_T zend_yytnamerr(char*, const char*); %type unprefixed_use_declarations const_decl inner_statement %type expr optional_expr while_statement for_statement foreach_variable %type foreach_statement declare_statement finally_statement unset_variable variable -%type extends_from parameter optional_type_without_static argument global_var +%type extends_from parameter optional_type_without_static argument argument_or_ellipsis ellipsis_argument global_var %type static_var class_statement trait_adaptation trait_precedence trait_alias %type absolute_trait_method_reference trait_method_reference property echo_expr %type new_dereferenceable new_non_dereferenceable anonymous_class class_name class_name_reference simple_variable @@ -907,10 +907,15 @@ argument_list: | '(' T_ELLIPSIS ')' { $$ = zend_ast_create_fcc(); } ; +/* TODO: unify FCC and PFA ASTs. For now we produce a dedicated AST for FCCs, + * which requires to disambiguate '(...)' vs arg lists that include '...' but + * have more than one arg. */ non_empty_argument_list: argument { $$ = zend_ast_create_list(1, ZEND_AST_ARG_LIST, $1); } - | non_empty_argument_list ',' argument + | ellipsis_argument ',' argument_or_ellipsis + { $$ = zend_ast_create_list(1, ZEND_AST_ARG_LIST, $1, $3); } + | non_empty_argument_list ',' argument_or_ellipsis { $$ = zend_ast_list_add($1, $3); } ; @@ -919,10 +924,18 @@ argument: | identifier ':' expr { $$ = zend_ast_create(ZEND_AST_NAMED_ARG, $1, $3); } | '?' { $$ = zend_ast_create_ex(ZEND_AST_PLACEHOLDER_ARG, _IS_PLACEHOLDER_ARG); } - | T_ELLIPSIS { $$ = zend_ast_create_ex(ZEND_AST_PLACEHOLDER_ARG, _IS_PLACEHOLDER_VARIADIC); } + | 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_or_ellipsis: + argument { $$ = $1; } + | ellipsis_argument { $$ = $1; } +; + +ellipsis_argument: + T_ELLIPSIS { $$ = zend_ast_create_ex(ZEND_AST_PLACEHOLDER_ARG, _IS_PLACEHOLDER_VARIADIC); } + global_var_list: global_var_list ',' global_var { $$ = zend_ast_list_add($1, $3); } | global_var { $$ = zend_ast_create_list(1, ZEND_AST_STMT_LIST, $1); } diff --git a/Zend/zend_object_handlers.c b/Zend/zend_object_handlers.c index 20672ebbdec8..8ff2326dad5a 100644 --- a/Zend/zend_object_handlers.c +++ b/Zend/zend_object_handlers.c @@ -1667,7 +1667,7 @@ 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() { +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); } diff --git a/Zend/zend_partial.c b/Zend/zend_partial.c index 55da8c5f82f7..d6b9c17e1ede 100644 --- a/Zend/zend_partial.c +++ b/Zend/zend_partial.c @@ -17,20 +17,27 @@ */ #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" typedef struct _zend_partial { + /* Common zend_closure fields */ zend_object std; - /* this is the prototype generated from application */ - zend_function prototype; + /* 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; - /* this will be returned by get_closure, and has arginfo of prototype - and will invoke func */ - zend_function trampoline; uint32_t argc; zval *argv; zend_array *named; @@ -40,6 +47,12 @@ 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) @@ -59,7 +72,7 @@ static zend_arg_info zend_call_magic_arginfo[1]; (((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) + (((func)->common.fn_flags & flags) != 0) #define ZEND_PARTIAL_FUNC_DEL(func, flag) \ ((func)->common.fn_flags &= ~flag) @@ -71,146 +84,160 @@ static zend_arg_info zend_call_magic_arginfo[1]; (ZEND_CALL_INFO(partial) & flag) static zend_always_inline uint32_t zend_partial_signature_size(zend_partial *partial) { - uint32_t count = MAX(partial->func.common.num_args, partial->argc); + 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)) { + 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 zend_always_inline zend_function* zend_partial_signature_create(zend_partial *partial, zend_function *prototype) { +static zend_always_inline void zend_partial_signature_create(zend_partial *partial) { zend_arg_info *signature = emalloc(zend_partial_signature_size(partial)), *info = signature; - memcpy(&partial->prototype, prototype, ZEND_PARTIAL_FUNC_SIZE(prototype)); - - if (ZEND_PARTIAL_FUNC_FLAG(prototype, ZEND_ACC_HAS_RETURN_TYPE)) { + if (ZEND_PARTIAL_FUNC_FLAG(&partial->trampoline, ZEND_ACC_HAS_RETURN_TYPE)) { memcpy(info, - prototype->common.arg_info - 1, sizeof(zend_arg_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; - ZEND_PARTIAL_FUNC_DEL(&partial->prototype, ZEND_ACC_VARIADIC); + memset(partial->trampoline.op_array.arg_flags, 0, + sizeof(partial->trampoline.op_array.arg_flags)); while (offset < limit) { zval *arg = &partial->argv[offset]; - if (Z_IS_PLACEHOLDER_ARG_P(arg)) { - if (offset < prototype->common.num_args) { + if (Z_IS_PLACEHOLDER_P(arg)) { + if (offset < partial->func.common.num_args) { num++; - required++; + if (offset < partial->func.common.required_num_args) { + required = num; + } memcpy(info, - &prototype->common.arg_info[offset], + &partial->func.common.arg_info[offset], sizeof(zend_arg_info)); - 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; + } + } info++; - } else if (ZEND_PARTIAL_FUNC_FLAG(prototype, ZEND_ACC_VARIADIC)) { + } else { + ZEND_ASSERT(ZEND_PARTIAL_FUNC_FLAG(&partial->func, ZEND_ACC_VARIADIC)); num++; - required++; - if (ZEND_PARTIAL_IS_CALL_TRAMPOLINE(prototype)) { + /* Placeholders that run into the variadic portion become + * required and make all params before them required */ + required = num; + // TODO: update arg name + if (ZEND_PARTIAL_IS_CALL_TRAMPOLINE(&partial->func)) { memcpy(info, zend_call_magic_arginfo, sizeof(zend_arg_info)); } else { memcpy(info, - prototype->common.arg_info + prototype->common.num_args, + partial->func.common.arg_info + partial->func.common.num_args, sizeof(zend_arg_info)); } - ZEND_TYPE_FULL_MASK(info->type) &= ~_ZEND_IS_VARIADIC_BIT; - info++; - } else { - ZEND_ASSERT(0); - } - } else if (Z_IS_PLACEHOLDER_VARIADIC_P(arg) || Z_ISUNDEF_P(arg)) { - if (offset < partial->func.common.num_args) { - while (offset < partial->func.common.num_args) { - if ((offset < partial->argc) && - !Z_IS_PLACEHOLDER_P(&partial->argv[offset]) && - !Z_ISUNDEF(partial->argv[offset])) { - offset++; - continue; - } - - num++; - memcpy(info, - &partial->func.common.arg_info[offset], - sizeof(zend_arg_info)); - ZEND_TYPE_FULL_MASK(info->type) &= ~_ZEND_IS_VARIADIC_BIT; - info++; - offset++; - } - - if (ZEND_PARTIAL_FUNC_FLAG(prototype, ZEND_ACC_VARIADIC)) { - ZEND_ASSERT(!ZEND_PARTIAL_IS_CALL_TRAMPOLINE(prototype)); - memcpy(info, - prototype->common.arg_info + prototype->common.num_args, - sizeof(zend_arg_info)); - - if (ZEND_TYPE_FULL_MASK(info->type) & _ZEND_IS_VARIADIC_BIT) { - ZEND_PARTIAL_FUNC_ADD(&partial->prototype, ZEND_ACC_VARIADIC); + 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; } - num++; - } - break; - } else if (ZEND_PARTIAL_FUNC_FLAG(prototype, ZEND_ACC_VARIADIC)) { - ZEND_PARTIAL_FUNC_ADD(&partial->prototype, ZEND_ACC_VARIADIC); - num++; - if (ZEND_PARTIAL_IS_CALL_TRAMPOLINE(prototype)) { - memcpy(info, zend_call_magic_arginfo, sizeof(zend_arg_info)); - } else { - memcpy(info, - prototype->common.arg_info + prototype->common.num_args, - sizeof(zend_arg_info)); } info++; - break; + } + } 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 (ZEND_PARTIAL_FUNC_FLAG(&partial->prototype, ZEND_ACC_VARIADIC)) { - num--; + /* 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; + } + info++; } - partial->prototype.common.num_args = num; - partial->prototype.common.required_num_args = required; - partial->prototype.common.arg_info = signature; - - if (ZEND_PARTIAL_FUNC_FLAG(&partial->prototype, ZEND_ACC_HAS_RETURN_TYPE)) { - partial->prototype.common.arg_info++; + if (byref) { + ZEND_ADD_CALL_FLAG(partial, ZEND_APPLY_BYREF); } - ZEND_PARTIAL_FUNC_ADD(&partial->prototype, ZEND_ACC_PARTIAL); + partial->trampoline.common.num_args = num; + partial->trampoline.common.required_num_args = required; + partial->trampoline.common.arg_info = signature; - partial->prototype.common.prototype = &partial->func; + if (ZEND_PARTIAL_FUNC_FLAG(&partial->trampoline, ZEND_ACC_HAS_RETURN_TYPE)) { + partial->trampoline.common.arg_info++; + } - if (prototype->type == ZEND_INTERNAL_FUNCTION) { - return &partial->prototype; + 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++; + } + while (info < end) { + zend_internal_arg_info *ii = (zend_internal_arg_info*) info; + 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); + } + info++; + } } - zend_string *filename = zend_get_executed_filename_ex(); + partial->trampoline.common.prototype = &partial->func; +} - if (!filename) { - if (partial->prototype.op_array.filename) { - zend_string_addref(partial->prototype.op_array.filename); - } - return &partial->prototype; +bool zend_is_partial_function(zend_function *function) { + if (!(function->common.fn_flags & ZEND_ACC_CLOSURE)) { + return false; } - partial->prototype.op_array.filename = zend_string_copy(filename); - partial->prototype.op_array.line_start = - partial->prototype.op_array.line_end = zend_get_executed_lineno(); + zend_partial *ptr = (zend_partial*)((char*)function - XtOffsetOf(zend_partial, trampoline)); + + if (!(ptr->closure_flags & ZEND_CLOSURE_PARTIAL)) { + return false; + } - return &partial->prototype; + return true; } static zend_always_inline zend_partial* zend_partial_fetch(zval *This) { @@ -224,7 +251,7 @@ static zend_always_inline zend_partial* zend_partial_fetch(zval *This) { zend_partial *ptr = (zend_partial*) Z_OBJ_P(This); - if (!ZEND_PARTIAL_FUNC_FLAG(&ptr->prototype, ZEND_ACC_PARTIAL)) { + if (!(ptr->closure_flags & ZEND_CLOSURE_PARTIAL)) { return NULL; } @@ -233,21 +260,46 @@ static zend_always_inline zend_partial* zend_partial_fetch(zval *This) { 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_VARIADIC | ZEND_ACC_HAS_RETURN_TYPE | ZEND_ACC_STRICT_TYPES; - - trampoline->common = partial->prototype.common; - trampoline->type = ZEND_INTERNAL_FUNCTION; - trampoline->internal_function.fn_flags = - ZEND_ACC_PUBLIC | ZEND_ACC_CALL_VIA_HANDLER | ZEND_ACC_PARTIAL | ZEND_ACC_TRAMPOLINE_PERMANENT | (partial->prototype.common.fn_flags & keep_flags); - if (partial->func.type != ZEND_INTERNAL_FUNCTION || (partial->func.common.fn_flags & ZEND_ACC_USER_ARG_INFO)) { - trampoline->internal_function.fn_flags |= - ZEND_ACC_USER_ARG_INFO; - } - trampoline->internal_function.handler = zend_partial_call_magic; - trampoline->internal_function.module = 0; - trampoline->internal_function.scope = zend_ce_closure; - trampoline->internal_function.function_name = ZSTR_KNOWN(ZEND_STR_MAGIC_INVOKE); + ZEND_ACC_RETURN_REFERENCE | ZEND_ACC_HAS_RETURN_TYPE | ZEND_ACC_STRICT_TYPES; + + 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) { @@ -256,6 +308,7 @@ static zend_always_inline zend_object* zend_partial_new(zend_class_entry *type, 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); @@ -372,9 +425,7 @@ static HashTable *zend_partial_get_gc(zend_object *obj, zval **table, int *n) 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)) { - zend_partial *partial = (zend_partial*) *object; - - return &partial->trampoline; + return zend_get_closure_invoke_method(*object); } return zend_std_get_method(object, method, key); @@ -417,7 +468,23 @@ static void zend_partial_free(zend_object *object) { zend_array_release(partial->named); } - zend_arg_info *info = partial->prototype.common.arg_info; + 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--; @@ -425,11 +492,11 @@ static void zend_partial_free(zend_object *object) { efree(info); + if (partial->trampoline.op_array.filename) { + zend_string_release(partial->trampoline.op_array.filename); + } if (partial->func.type == ZEND_USER_FUNCTION) { - if (partial->prototype.op_array.filename) { - zend_string_release(partial->prototype.op_array.filename); - } - destroy_zend_function(&partial->func); + destroy_op_array(&partial->func.op_array); } if (Z_TYPE(partial->This) == IS_OBJECT) { @@ -439,6 +506,23 @@ static void zend_partial_free(zend_object *object) { 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]); + + 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)); @@ -453,6 +537,8 @@ void zend_partial_startup(void) { 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) { @@ -480,11 +566,26 @@ static zend_always_inline zend_string* zend_partial_scope_name(zend_execute_data return NULL; } -static zend_always_inline zend_string* zend_partial_symbol_name(zend_execute_data *call, zend_function *function) { +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 { @@ -495,7 +596,7 @@ static zend_always_inline zend_string* zend_partial_symbol_name(zend_execute_dat return symbol; } -static zend_always_inline void zend_partial_prototype_underflow(zend_function *function, zend_string *symbol, uint32_t args, uint32_t expected, bool variadic) { +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) { @@ -511,7 +612,7 @@ static zend_always_inline void zend_partial_prototype_underflow(zend_function *f } } -static zend_always_inline void zend_partial_prototype_overflow(zend_function *function, zend_string *symbol, uint32_t args, uint32_t 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, " @@ -567,14 +668,31 @@ static zend_always_inline void zend_partial_args_overflow(zend_function *functio 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; - uint32_t num = ZEND_CALL_NUM_ARGS(call) + - (ZEND_PARTIAL_CALL_FLAG(call, ZEND_CALL_VARIADIC_PLACEHOLDER) ? -1 : 0); - + /* 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 (ZEND_PARTIAL_CALL_FLAG(call, ZEND_CALL_VARIADIC_PLACEHOLDER)) { + if (variadic) { return; } @@ -583,7 +701,7 @@ void zend_partial_args_check(zend_execute_data *call) { function, symbol, num, function->common.required_num_args, false, true); zend_string_release(symbol); - } else if (num > function->common.num_args && + } 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( @@ -593,239 +711,299 @@ void zend_partial_args_check(zend_execute_data *call) { } } -static zend_always_inline uint32_t zend_partial_apply( - zval *pStart, zval *pEnd, - zval *cStart, zval *cEnd, - zval *fParam, bool call) { +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"); + } + } - uint32_t pCount = 0; + 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); + } +} - /* this optimizes the most general case, partial variadic application followed by - all arguments on call, and initial application */ - if ((pEnd - pStart) == 0 || - ((pEnd - pStart) == 1 && Z_IS_PLACEHOLDER_VARIADIC_P(pStart))) { - if ((pCount = (cEnd - cStart)) > 0) { - memcpy(fParam, cStart, sizeof(zval) * pCount); - } +/* 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) { - if (call) { - cStart = fParam; - cEnd = cStart + pCount; - while (cStart < cEnd) { - if (Z_IS_PLACEHOLDER_P(cStart)) { - ZVAL_UNDEF(cStart); - pCount--; - } - cStart++; + 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); } - } - return pCount; + + 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); } - /* this optimizes the second most general case: all arguments supplied upon first - application, followed by no arguments on call */ - if ((cEnd - cStart) == 0) { - if ((pCount = (pEnd - pStart)) > 0) { - memcpy(fParam, pStart, sizeof(zval) * pCount); - } + zval *dest = argv + partial->argc - 1; - if (call) { - cStart = fParam; - cEnd = cStart + pCount; - while (cStart < cEnd) { - if (Z_IS_PLACEHOLDER_P(cStart)) { - ZVAL_UNDEF(cStart); - pCount--; + 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); + } + } } - cStart++; + 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; } } - return pCount; + 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); } - /* slow path is not able to handle all cases */ + 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); - pCount++; - fParam++; - cStart++; - } - } else { - if (cStart < cEnd && Z_ISUNDEF_P(pStart)) { - ZVAL_COPY_VALUE(fParam, cStart); - pCount++; - fParam++; cStart++; } else { - ZVAL_COPY_VALUE(fParam, pStart); - pCount++; - fParam++; + 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); - pCount++; fParam++; cStart++; } - - return pCount; } -ZEND_NAMED_FUNCTION(zend_partial_call_magic) +/* 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_partial *partial = (zend_partial*) Z_OBJ_P(ZEND_THIS); - zend_object *object = NULL; - zval *params; - uint32_t num_params, num_all_params; - HashTable *named_args; - zend_fcall_info fci = empty_fcall_info; - zend_fcall_info_cache fcc = empty_fcall_info_cache; - - ZEND_PARSE_PARAMETERS_START(0, -1) - Z_PARAM_VARIADIC_WITH_NAMED(params, num_params, named_args) - ZEND_PARSE_PARAMETERS_END(); - - num_all_params = num_params + (named_args ? named_args->nNumUsed : 0); - - if (num_all_params < partial->prototype.common.required_num_args) { - zend_string *symbol = zend_partial_symbol_name(execute_data, &partial->prototype); + 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->prototype, symbol, num_all_params, - partial->prototype.common.required_num_args, + &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->prototype)) { + if (ZEND_PARTIAL_IS_CALL_TRAMPOLINE(&partial->trampoline)) { EG(trampoline).common.function_name = NULL; } - RETURN_THROWS(); - } else if (num_all_params > partial->prototype.common.num_args && - !ZEND_PARTIAL_CALL_FLAG(partial, ZEND_APPLY_VARIADIC)) { - zend_string *symbol = zend_partial_symbol_name(execute_data, &partial->prototype); + 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->prototype, symbol, num_all_params, - partial->prototype.common.num_args); + &partial->trampoline, symbol, ZEND_CALL_NUM_ARGS(call), + partial->trampoline.common.num_args); zend_string_release(symbol); - if (ZEND_PARTIAL_IS_CALL_TRAMPOLINE(&partial->prototype)) { + if (ZEND_PARTIAL_IS_CALL_TRAMPOLINE(&partial->trampoline)) { EG(trampoline).common.function_name = NULL; } - RETURN_THROWS(); + return FAILURE; } - fci.size = sizeof(zend_fcall_info); - - if (UNEXPECTED(ZEND_PARTIAL_CALL_FLAG(partial, ZEND_APPLY_FACTORY))) { - zend_class_entry *type = Z_CE(partial->This); - zval instance; - - if (type->create_object) { - ZVAL_OBJ(&instance, type->create_object(type)); - } else { - ZVAL_OBJ(&instance, zend_objects_new(type)); - - object_properties_init(Z_OBJ(instance), type); - } - - object = Z_OBJ(instance); + ZEND_ASSERT(zend_vm_calc_used_stack(ZEND_CALL_NUM_ARGS(call), &partial->func) <= (size_t)(((char*)EG(vm_stack_end)) - (char*)call)); - if (ZEND_PARTIAL_CALL_FLAG(partial, ZEND_APPLY_PASS)) { - /* nothing to pass any arguments too, so return immediately */ - RETURN_OBJ(object); - } + uint32_t num_args = zend_partial_apply_inplace(partial, call, + ZEND_CALL_ARG(call, 1), ZEND_CALL_NUM_ARGS(call)); - GC_ADD_FLAGS(object, IS_OBJ_DESTRUCTOR_CALLED); + 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 (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 { - if (Z_TYPE(partial->This) == IS_OBJECT) { - object = Z_OBJ(partial->This); - } else if (Z_TYPE(partial->This) == IS_UNDEF && Z_CE(partial->This)) { - fcc.called_scope = Z_CE(partial->This); - } + object_or_called_scope = NULL; } - fci.retval = return_value; - fcc.function_handler = &partial->func; + zend_vm_init_call_frame(call, call_info, &partial->func, + num_args, object_or_called_scope); - if (object) { - fci.object = object; - fcc.object = object; + 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) { - named_args = partial->named; - GC_ADDREF(named_args); + call->extra_named_params = partial->named; + GC_ADDREF(partial->named); + ZEND_CALL_INFO(call) |= ZEND_CALL_HAS_EXTRA_NAMED_PARAMS; } else { - HashTable *nested = zend_array_dup(partial->named); + /* 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); - named_args = nested; + zend_array_release(named_args); + call->extra_named_params = nested; } - fci.named_params = named_args; - } else { - fci.named_params = named_args; } - fci.params = ecalloc(sizeof(zval), partial->argc + num_params); - fci.param_count = zend_partial_apply( - partial->argv, partial->argv + partial->argc, - params ? params : NULL, - params ? params + num_params : NULL, fci.params, true); - - if (fci.param_count < partial->func.common.required_num_args) { - /* doesn't satisfy implementation */ - zend_string *symbol = zend_partial_symbol_name(execute_data, &partial->func); - zend_partial_args_underflow( - &partial->func, symbol, fci.param_count, - partial->func.common.required_num_args, true, false); - zend_string_release(symbol); - } else { - zend_call_function(&fci, &fcc); + 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_IS_CALL_TRAMPOLINE(&partial->prototype)) { - EG(trampoline).common.function_name = NULL; + if (ZEND_PARTIAL_FUNC_FLAG(&partial->func, ZEND_ACC_CALL_VIA_TRAMPOLINE)) { + zend_string_addref(partial->func.common.function_name); } - if (ZEND_PARTIAL_CALL_FLAG(partial, ZEND_APPLY_FACTORY)) { - if (!EG(exception)) { - GC_DEL_FLAGS(object, IS_OBJ_DESTRUCTOR_CALLED); - } - zval_ptr_dtor(return_value); - RETVAL_OBJ(object); - } + OBJ_RELEASE(&partial->std); - if (partial->named) { - zend_array_release(named_args); - } - efree(fci.params); + 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) { - zend_function *prototype = NULL; - 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); if ((applied = zend_partial_fetch(this_ptr))) { ZEND_ADD_CALL_FLAG(partial, ZEND_CALL_INFO(applied) & ~ZEND_APPLY_VARIADIC); function = &applied->func; - prototype = &applied->prototype; - if (ZEND_PARTIAL_CALL_FLAG(partial, ZEND_APPLY_FACTORY)) { - Z_CE(partial->This) = Z_CE(applied->This); + /* 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); } - - partial->argv = ecalloc(applied->argc + argc, sizeof(zval)); - partial->argc = zend_partial_apply( + 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, false); + 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) { @@ -838,11 +1016,31 @@ void zend_partial_create(zval *result, uint32_t info, zval *this_ptr, zend_funct } } } else { - partial->argv = ecalloc(argc, sizeof(zval)); - partial->argc = zend_partial_apply( - NULL, NULL, - argv, argv + argc, - partial->argv, false); + /* 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; @@ -859,57 +1057,43 @@ void zend_partial_create(zval *result, uint32_t info, zval *this_ptr, zend_funct ZEND_PARTIAL_FUNC_ADD(&partial->func, ZEND_ACC_TRAMPOLINE_PERMANENT); } - if (ZEND_PARTIAL_CALL_FLAG(partial, ZEND_APPLY_FACTORY)) { - if (!Z_CE(partial->This)) { - Z_CE(partial->This) = Z_OBJCE_P(this_ptr); - } - - if (ZEND_PARTIAL_CALL_FLAG(partial, ZEND_APPLY_PASS)) { - /* setting scope for pass function so that errors make sense */ - partial->func.common.scope = Z_CE(partial->This); - } - } - ZEND_PARTIAL_FUNC_ADD(&partial->func, ZEND_ACC_FAKE_CLOSURE); if (partial->func.type == ZEND_USER_FUNCTION) { - zend_string_addref(partial->func.common.function_name); + 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; } - if (!prototype) { - prototype = &partial->func; - } - - partial->func.common.prototype = zend_partial_signature_create(partial, prototype); - - if (!ZEND_PARTIAL_CALL_FLAG(partial, ZEND_APPLY_FACTORY)) { - /* partial info may contain ZEND_APPLY_VARIADIC */ - uint32_t backup_info = ZEND_CALL_INFO(partial); + zend_partial_trampoline_create(partial, &partial->trampoline); - 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 { - This = zend_get_closure_this_ptr(this_ptr); - } - } else { + /* 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; } - ZVAL_COPY(&partial->This, This); + } else { + This = this_ptr; } - - ZEND_ADD_CALL_FLAG(partial, backup_info); + ZVAL_COPY(&partial->This, This); } - zend_partial_trampoline_create(partial, &partial->trampoline); + ZEND_ADD_CALL_FLAG(partial, backup_info); } void zend_partial_bind(zval *result, zval *partial, zval *this_ptr, zend_class_entry *scope) { diff --git a/Zend/zend_partial.h b/Zend/zend_partial.h index 31d73bbda041..fc3bd74b4c05 100644 --- a/Zend/zend_partial.h +++ b/Zend/zend_partial.h @@ -18,14 +18,18 @@ #ifndef ZEND_PARTIAL_H #define ZEND_PARTIAL_H +#include "zend_types.h" +#include "zend_API.h" + BEGIN_EXTERN_C() +typedef struct _zend_partial zend_partial; + void zend_partial_startup(void); -#define ZEND_APPLY_NORMAL (1<<16) -#define ZEND_APPLY_FACTORY (1<<17) -#define ZEND_APPLY_PASS (1<<18) -#define ZEND_APPLY_VARIADIC (1<<19) +#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); @@ -35,7 +39,9 @@ void zend_partial_args_check(zend_execute_data *call); zend_function *zend_partial_get_trampoline(zend_object *object); -ZEND_NAMED_FUNCTION(zend_partial_call_magic); +zend_result zend_partial_init_call(zend_execute_data *call); + +bool zend_is_partial_function(zend_function *function); END_EXTERN_C() diff --git a/Zend/zend_vm_def.h b/Zend/zend_vm_def.h index 0121f9279a7e..e19df802214b 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 @@ -9202,80 +9205,151 @@ ZEND_VM_HANDLER(200, ZEND_FETCH_GLOBALS, UNUSED, UNUSED) ZEND_VM_NEXT_OPCODE(); } -ZEND_VM_HANDLER(211, ZEND_SEND_PLACEHOLDER, UNUSED, UNUSED) +ZEND_VM_HANDLER(211, ZEND_SEND_PLACEHOLDER, UNUSED, CONST|UNUSED) { USE_OPLINE zval *arg; zend_execute_data *call = EX(call); - arg = ZEND_CALL_ARG(call, opline->op2.num); + 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) { - ZEND_ADD_CALL_FLAG(call, ZEND_CALL_VARIADIC_PLACEHOLDER); + 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(212, ZEND_DO_FCALL_PARTIAL, ANY, ANY) +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 - zend_execute_data *call = EX(call); - zval *result = NULL; - uint32_t info = ZEND_APPLY_NORMAL; - if (opline->op1_type != IS_UNUSED) { - result = EX_VAR(opline->op1.var); + SAVE_OPLINE(); - if (!(call->func->common.fn_flags & ZEND_ACC_CTOR)) { - ZEND_ADD_CALL_FLAG_EX(info, ZEND_APPLY_PASS); - } + call = execute_data; - ZEND_ADD_CALL_FLAG_EX(info, ZEND_APPLY_FACTORY); - } else if (opline->result_type != IS_UNUSED) { - result = EX_VAR(opline->result.var); + if (zend_partial_init_call(call) == FAILURE) { + UNDEF_RESULT(); + HANDLE_EXCEPTION(); } - if (ZEND_CALL_INFO(call) & ZEND_CALL_VARIADIC_PLACEHOLDER) { - ZEND_ADD_CALL_FLAG_EX(info, ZEND_APPLY_VARIADIC); - } + fbc = call->func; - if (result) { - zend_partial_create(result, info, - &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); + // TODO: deprecated check + if (UNEXPECTED(!RETURN_VALUE_USED(opline) && (fbc->common.fn_flags & ZEND_ACC_NODISCARD))) { + if ((fbc->common.fn_flags & ZEND_ACC_NODISCARD) && EG(exception) == NULL) { + zend_nodiscard_function(fbc); + } + if (UNEXPECTED(EG(exception) != NULL)) { + UNDEF_RESULT(); + HANDLE_EXCEPTION(); + } + } - if (info & ZEND_APPLY_FACTORY) { - GC_ADD_FLAGS(Z_OBJ(call->This), IS_OBJ_DESTRUCTOR_CALLED); - OBJ_RELEASE(Z_OBJ(call->This)); + 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_vm_stack_free_args(call); - } + ZEND_ASSERT(fbc->type == ZEND_INTERNAL_FUNCTION); - if (ZEND_CALL_INFO(call) & ZEND_CALL_HAS_EXTRA_NAMED_PARAMS) { - zend_array_release(call->extra_named_params); - } +#if ZEND_DEBUG + bool should_throw = zend_internal_call_should_throw(fbc, call); +#endif - if ((call->func->common.fn_flags & ZEND_ACC_CALL_VIA_TRAMPOLINE)) { - zend_free_trampoline(call->func); - } + if (!ret) { + ret = &retval; + } + ZVAL_NULL(ret); - 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)); + 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); + } } - EX(call) = call->prev_execute_data; + 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); - ZEND_VM_NEXT_OPCODE(); + 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) @@ -9790,6 +9864,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 1cd0a8326a17..b86c1aa3eb0c 100644 --- a/Zend/zend_vm_execute.h +++ b/Zend/zend_vm_execute.h @@ -3810,63 +3810,239 @@ static ZEND_OPCODE_HANDLER_RET ZEND_FASTCALL ZEND_CALL_TRAMPOLINE_SPEC_OBSERVER_ ZEND_VM_LEAVE(); } -static ZEND_OPCODE_HANDLER_RET ZEND_FASTCALL ZEND_DO_FCALL_PARTIAL_SPEC_HANDLER(ZEND_OPCODE_HANDLER_ARGS) +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 - zend_execute_data *call = EX(call); - zval *result = NULL; - uint32_t info = ZEND_APPLY_NORMAL; - if (opline->op1_type != IS_UNUSED) { - result = EX_VAR(opline->op1.var); + SAVE_OPLINE(); - if (!(call->func->common.fn_flags & ZEND_ACC_CTOR)) { - ZEND_ADD_CALL_FLAG_EX(info, ZEND_APPLY_PASS); - } + call = execute_data; - ZEND_ADD_CALL_FLAG_EX(info, ZEND_APPLY_FACTORY); - } else if (opline->result_type != IS_UNUSED) { - result = EX_VAR(opline->result.var); + if (zend_partial_init_call(call) == FAILURE) { + UNDEF_RESULT(); + HANDLE_EXCEPTION(); } - if (ZEND_CALL_INFO(call) & ZEND_CALL_VARIADIC_PLACEHOLDER) { - ZEND_ADD_CALL_FLAG_EX(info, ZEND_APPLY_VARIADIC); + fbc = call->func; + + // TODO: deprecated check + if (UNEXPECTED(!RETURN_VALUE_USED(opline) && (fbc->common.fn_flags & ZEND_ACC_NODISCARD))) { + if ((fbc->common.fn_flags & ZEND_ACC_NODISCARD) && EG(exception) == NULL) { + zend_nodiscard_function(fbc); + } + if (UNEXPECTED(EG(exception) != NULL)) { + UNDEF_RESULT(); + HANDLE_EXCEPTION(); + } } - if (result) { - zend_partial_create(result, info, - &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 (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(); - if (info & ZEND_APPLY_FACTORY) { - GC_ADD_FLAGS(Z_OBJ(call->This), IS_OBJ_DESTRUCTOR_CALLED); - OBJ_RELEASE(Z_OBJ(call->This)); + 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_vm_stack_free_args(call); + 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 (ZEND_CALL_INFO(call) & ZEND_CALL_HAS_EXTRA_NAMED_PARAMS) { - zend_array_release(call->extra_named_params); + 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 ((call->func->common.fn_flags & ZEND_ACC_CALL_VIA_TRAMPOLINE)) { - zend_free_trampoline(call->func); + 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 (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)); + if (UNEXPECTED(EG(exception) != NULL)) { + zend_rethrow_exception(execute_data); + HANDLE_EXCEPTION(); } - EX(call) = call->prev_execute_data; + 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; + + // TODO: deprecated check + if (UNEXPECTED(!RETURN_VALUE_USED(opline) && (fbc->common.fn_flags & ZEND_ACC_NODISCARD))) { + if ((fbc->common.fn_flags & ZEND_ACC_NODISCARD) && EG(exception) == NULL) { + zend_nodiscard_function(fbc); + } + if (UNEXPECTED(EG(exception) != NULL)) { + UNDEF_RESULT(); + HANDLE_EXCEPTION(); + } + } + + 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); + } + } + + 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); - ZEND_VM_NEXT_OPCODE(); + 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) @@ -33695,6 +33871,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 @@ -35748,6 +35956,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; @@ -38322,12 +38559,24 @@ static ZEND_OPCODE_HANDLER_RET ZEND_FASTCALL ZEND_SEND_PLACEHOLDER_SPEC_UNUSED_U zval *arg; zend_execute_data *call = EX(call); - arg = ZEND_CALL_ARG(call, opline->op2.num); + 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) { - ZEND_ADD_CALL_FLAG(call, ZEND_CALL_VARIADIC_PLACEHOLDER); + 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(); @@ -57827,9 +58076,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_DO_FCALL_PARTIAL_SPEC_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, @@ -59181,10 +59436,15 @@ 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_DO_FCALL_PARTIAL_SPEC): - VM_TRACE(ZEND_DO_FCALL_PARTIAL_SPEC) - ZEND_DO_FCALL_PARTIAL_SPEC_HANDLER(ZEND_OPCODE_HANDLER_ARGS_PASSTHRU); - VM_TRACE_OP_END(ZEND_DO_FCALL_PARTIAL_SPEC) + 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) @@ -62588,6 +62848,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); @@ -62718,6 +62983,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); @@ -67073,9 +67343,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_DO_FCALL_PARTIAL_SPEC_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, @@ -68039,7 +68315,7 @@ void zend_vm_init(void) 1255, 1256 | SPEC_RULE_OP1, 1261 | SPEC_RULE_OP1, - 3496, + 3502, 1266 | SPEC_RULE_OP1, 1271 | SPEC_RULE_OP1, 1276 | SPEC_RULE_OP2, @@ -68073,7 +68349,7 @@ void zend_vm_init(void) 1559 | SPEC_RULE_OP1 | SPEC_RULE_OP2, 1584 | SPEC_RULE_OP1, 1589, - 3496, + 3502, 1590 | SPEC_RULE_OP1, 1595 | SPEC_RULE_OP1 | SPEC_RULE_OP2, 1620 | SPEC_RULE_OP1 | SPEC_RULE_OP2, @@ -68205,51 +68481,51 @@ void zend_vm_init(void) 2575, 2576, 2577, - 2578, - 2579, - 2580, - 3496, - 3496, - 3496, - 3496, - 3496, - 3496, - 3496, - 3496, - 3496, - 3496, - 3496, - 3496, - 3496, - 3496, - 3496, - 3496, - 3496, - 3496, - 3496, - 3496, - 3496, - 3496, - 3496, - 3496, - 3496, - 3496, - 3496, - 3496, - 3496, - 3496, - 3496, - 3496, - 3496, - 3496, - 3496, - 3496, - 3496, - 3496, - 3496, - 3496, - 3496, - 3496, + 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; @@ -68422,7 +68698,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 = 2589 | 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); } @@ -68430,7 +68706,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 = 2614 | 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); } @@ -68438,7 +68714,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 = 2639 | 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); } @@ -68449,17 +68725,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 = 2664 | 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 = 2689 | 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 = 2714 | SPEC_RULE_OP1 | SPEC_RULE_OP2; + spec = 2720 | SPEC_RULE_OP1 | SPEC_RULE_OP2; } break; case ZEND_MUL: @@ -68470,17 +68746,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 = 2739 | 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 = 2764 | 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 = 2789 | 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: @@ -68491,16 +68767,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 = 2814 | 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 = 2889 | 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 = 3114 | 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 = 3120 | SPEC_RULE_OP2 | SPEC_RULE_COMMUTATIVE; + spec = 3126 | SPEC_RULE_OP2 | SPEC_RULE_COMMUTATIVE; } break; case ZEND_IS_NOT_IDENTICAL: @@ -68511,16 +68787,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 = 2964 | 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 = 3039 | 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 = 3117 | 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 = 3125 | SPEC_RULE_OP2 | SPEC_RULE_COMMUTATIVE; + spec = 3131 | SPEC_RULE_OP2 | SPEC_RULE_COMMUTATIVE; } break; case ZEND_IS_EQUAL: @@ -68531,12 +68807,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 = 2814 | 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 = 2889 | 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: @@ -68547,12 +68823,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 = 2964 | 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 = 3039 | 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: @@ -68560,12 +68836,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 = 3130 | 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 = 3205 | 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: @@ -68573,79 +68849,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 = 3280 | 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 = 3355 | 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 = 3442 | SPEC_RULE_OP1; + spec = 3448 | SPEC_RULE_OP1; } else if (op1_info == MAY_BE_DOUBLE) { - spec = 3447 | 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 = 3452 | 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 = 3430 | SPEC_RULE_RETVAL; + spec = 3436 | SPEC_RULE_RETVAL; } else if (op1_info == MAY_BE_LONG) { - spec = 3432 | 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 = 3434 | SPEC_RULE_RETVAL; + spec = 3440 | SPEC_RULE_RETVAL; } else if (op1_info == MAY_BE_LONG) { - spec = 3436 | 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 = 3438; + spec = 3444; } else if (op1_info == MAY_BE_LONG) { - spec = 3439; + spec = 3445; } break; case ZEND_POST_DEC: if (res_info == MAY_BE_LONG && op1_info == MAY_BE_LONG) { - spec = 3440; + spec = 3446; } else if (op1_info == MAY_BE_LONG) { - spec = 3441; + spec = 3447; } break; case ZEND_JMP: if (OP_JMP_ADDR(op, op->op1) > op) { - spec = 2588; + spec = 2594; } break; case ZEND_INIT_FCALL: if (Z_EXTRA_P(RT_CONSTANT(op, op->op2)) != 0) { - spec = 2581; + spec = 2587; } break; case ZEND_RECV: if (op->op2.num == MAY_BE_ANY) { - spec = 2582; + 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 = 3492; + 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 = 3487 | 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 = 3494 | SPEC_RULE_RETVAL; + spec = 3500 | SPEC_RULE_RETVAL; } break; case ZEND_FETCH_DIM_R: @@ -68653,22 +68929,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 = 3457 | 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 = 3493; + spec = 3499; } break; case ZEND_SEND_VAR: if (op->op2_type == IS_UNUSED && (op1_info & (MAY_BE_UNDEF|MAY_BE_REF)) == 0) { - spec = 3482 | 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 = 2583 | 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 055a732afadd..1e93199559f8 100644 --- a/Zend/zend_vm_handlers.h +++ b/Zend/zend_vm_handlers.h @@ -1373,511 +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_SEND_PLACEHOLDER_SPEC_UNUSED_UNUSED) \ - _(2579, ZEND_DO_FCALL_PARTIAL_SPEC) \ - _(2580, ZEND_CHECK_PARTIAL_ARGS_SPEC_UNUSED_UNUSED) \ - _(2581, ZEND_INIT_FCALL_OFFSET_SPEC_CONST) \ - _(2582, ZEND_RECV_NOTYPE_SPEC) \ - _(2584, ZEND_COUNT_ARRAY_SPEC_TMPVAR_UNUSED) \ - _(2585, ZEND_COUNT_ARRAY_SPEC_TMPVAR_UNUSED) \ - _(2587, ZEND_COUNT_ARRAY_SPEC_CV_UNUSED) \ - _(2588, ZEND_JMP_FORWARD_SPEC) \ - _(2594, ZEND_ADD_LONG_NO_OVERFLOW_SPEC_TMPVARCV_CONST) \ - _(2595, ZEND_ADD_LONG_NO_OVERFLOW_SPEC_TMPVARCV_TMPVARCV) \ - _(2596, ZEND_ADD_LONG_NO_OVERFLOW_SPEC_TMPVARCV_TMPVARCV) \ - _(2598, ZEND_ADD_LONG_NO_OVERFLOW_SPEC_TMPVARCV_TMPVARCV) \ - _(2599, ZEND_ADD_LONG_NO_OVERFLOW_SPEC_TMPVARCV_CONST) \ - _(2600, ZEND_ADD_LONG_NO_OVERFLOW_SPEC_TMPVARCV_TMPVARCV) \ + _(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) \ - _(2603, ZEND_ADD_LONG_NO_OVERFLOW_SPEC_TMPVARCV_TMPVARCV) \ - _(2609, ZEND_ADD_LONG_NO_OVERFLOW_SPEC_TMPVARCV_CONST) \ - _(2610, ZEND_ADD_LONG_NO_OVERFLOW_SPEC_TMPVARCV_TMPVARCV) \ - _(2611, ZEND_ADD_LONG_NO_OVERFLOW_SPEC_TMPVARCV_TMPVARCV) \ - _(2613, ZEND_ADD_LONG_NO_OVERFLOW_SPEC_TMPVARCV_TMPVARCV) \ - _(2619, ZEND_ADD_LONG_SPEC_TMPVARCV_CONST) \ - _(2620, ZEND_ADD_LONG_SPEC_TMPVARCV_TMPVARCV) \ - _(2621, ZEND_ADD_LONG_SPEC_TMPVARCV_TMPVARCV) \ - _(2623, ZEND_ADD_LONG_SPEC_TMPVARCV_TMPVARCV) \ - _(2624, ZEND_ADD_LONG_SPEC_TMPVARCV_CONST) \ - _(2625, ZEND_ADD_LONG_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) \ + _(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) \ - _(2628, ZEND_ADD_LONG_SPEC_TMPVARCV_TMPVARCV) \ - _(2634, ZEND_ADD_LONG_SPEC_TMPVARCV_CONST) \ - _(2635, ZEND_ADD_LONG_SPEC_TMPVARCV_TMPVARCV) \ - _(2636, ZEND_ADD_LONG_SPEC_TMPVARCV_TMPVARCV) \ - _(2638, ZEND_ADD_LONG_SPEC_TMPVARCV_TMPVARCV) \ - _(2644, ZEND_ADD_DOUBLE_SPEC_TMPVARCV_CONST) \ - _(2645, ZEND_ADD_DOUBLE_SPEC_TMPVARCV_TMPVARCV) \ - _(2646, ZEND_ADD_DOUBLE_SPEC_TMPVARCV_TMPVARCV) \ - _(2648, ZEND_ADD_DOUBLE_SPEC_TMPVARCV_TMPVARCV) \ - _(2649, ZEND_ADD_DOUBLE_SPEC_TMPVARCV_CONST) \ - _(2650, ZEND_ADD_DOUBLE_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) \ + _(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) \ - _(2653, ZEND_ADD_DOUBLE_SPEC_TMPVARCV_TMPVARCV) \ - _(2659, ZEND_ADD_DOUBLE_SPEC_TMPVARCV_CONST) \ - _(2660, ZEND_ADD_DOUBLE_SPEC_TMPVARCV_TMPVARCV) \ - _(2661, ZEND_ADD_DOUBLE_SPEC_TMPVARCV_TMPVARCV) \ - _(2663, ZEND_ADD_DOUBLE_SPEC_TMPVARCV_TMPVARCV) \ - _(2665, ZEND_SUB_LONG_NO_OVERFLOW_SPEC_CONST_TMPVARCV) \ - _(2666, ZEND_SUB_LONG_NO_OVERFLOW_SPEC_CONST_TMPVARCV) \ - _(2668, ZEND_SUB_LONG_NO_OVERFLOW_SPEC_CONST_TMPVARCV) \ - _(2669, ZEND_SUB_LONG_NO_OVERFLOW_SPEC_TMPVARCV_CONST) \ - _(2670, ZEND_SUB_LONG_NO_OVERFLOW_SPEC_TMPVARCV_TMPVARCV) \ - _(2671, ZEND_SUB_LONG_NO_OVERFLOW_SPEC_TMPVARCV_TMPVARCV) \ - _(2673, ZEND_SUB_LONG_NO_OVERFLOW_SPEC_TMPVARCV_TMPVARCV) \ - _(2674, ZEND_SUB_LONG_NO_OVERFLOW_SPEC_TMPVARCV_CONST) \ - _(2675, ZEND_SUB_LONG_NO_OVERFLOW_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) \ + _(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) \ - _(2678, ZEND_SUB_LONG_NO_OVERFLOW_SPEC_TMPVARCV_TMPVARCV) \ - _(2684, ZEND_SUB_LONG_NO_OVERFLOW_SPEC_TMPVARCV_CONST) \ - _(2685, ZEND_SUB_LONG_NO_OVERFLOW_SPEC_TMPVARCV_TMPVARCV) \ - _(2686, ZEND_SUB_LONG_NO_OVERFLOW_SPEC_TMPVARCV_TMPVARCV) \ - _(2688, ZEND_SUB_LONG_NO_OVERFLOW_SPEC_TMPVARCV_TMPVARCV) \ - _(2690, ZEND_SUB_LONG_SPEC_CONST_TMPVARCV) \ - _(2691, ZEND_SUB_LONG_SPEC_CONST_TMPVARCV) \ - _(2693, ZEND_SUB_LONG_SPEC_CONST_TMPVARCV) \ - _(2694, ZEND_SUB_LONG_SPEC_TMPVARCV_CONST) \ - _(2695, ZEND_SUB_LONG_SPEC_TMPVARCV_TMPVARCV) \ - _(2696, ZEND_SUB_LONG_SPEC_TMPVARCV_TMPVARCV) \ - _(2698, ZEND_SUB_LONG_SPEC_TMPVARCV_TMPVARCV) \ - _(2699, ZEND_SUB_LONG_SPEC_TMPVARCV_CONST) \ - _(2700, ZEND_SUB_LONG_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) \ + _(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) \ - _(2703, ZEND_SUB_LONG_SPEC_TMPVARCV_TMPVARCV) \ - _(2709, ZEND_SUB_LONG_SPEC_TMPVARCV_CONST) \ - _(2710, ZEND_SUB_LONG_SPEC_TMPVARCV_TMPVARCV) \ - _(2711, ZEND_SUB_LONG_SPEC_TMPVARCV_TMPVARCV) \ - _(2713, ZEND_SUB_LONG_SPEC_TMPVARCV_TMPVARCV) \ - _(2715, ZEND_SUB_DOUBLE_SPEC_CONST_TMPVARCV) \ - _(2716, ZEND_SUB_DOUBLE_SPEC_CONST_TMPVARCV) \ - _(2718, ZEND_SUB_DOUBLE_SPEC_CONST_TMPVARCV) \ - _(2719, ZEND_SUB_DOUBLE_SPEC_TMPVARCV_CONST) \ - _(2720, ZEND_SUB_DOUBLE_SPEC_TMPVARCV_TMPVARCV) \ - _(2721, ZEND_SUB_DOUBLE_SPEC_TMPVARCV_TMPVARCV) \ - _(2723, ZEND_SUB_DOUBLE_SPEC_TMPVARCV_TMPVARCV) \ - _(2724, ZEND_SUB_DOUBLE_SPEC_TMPVARCV_CONST) \ - _(2725, ZEND_SUB_DOUBLE_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) \ + _(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) \ - _(2728, ZEND_SUB_DOUBLE_SPEC_TMPVARCV_TMPVARCV) \ - _(2734, ZEND_SUB_DOUBLE_SPEC_TMPVARCV_CONST) \ - _(2735, ZEND_SUB_DOUBLE_SPEC_TMPVARCV_TMPVARCV) \ - _(2736, ZEND_SUB_DOUBLE_SPEC_TMPVARCV_TMPVARCV) \ - _(2738, ZEND_SUB_DOUBLE_SPEC_TMPVARCV_TMPVARCV) \ - _(2744, ZEND_MUL_LONG_NO_OVERFLOW_SPEC_TMPVARCV_CONST) \ - _(2745, ZEND_MUL_LONG_NO_OVERFLOW_SPEC_TMPVARCV_TMPVARCV) \ - _(2746, ZEND_MUL_LONG_NO_OVERFLOW_SPEC_TMPVARCV_TMPVARCV) \ - _(2748, ZEND_MUL_LONG_NO_OVERFLOW_SPEC_TMPVARCV_TMPVARCV) \ - _(2749, ZEND_MUL_LONG_NO_OVERFLOW_SPEC_TMPVARCV_CONST) \ - _(2750, ZEND_MUL_LONG_NO_OVERFLOW_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) \ + _(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) \ - _(2753, ZEND_MUL_LONG_NO_OVERFLOW_SPEC_TMPVARCV_TMPVARCV) \ - _(2759, ZEND_MUL_LONG_NO_OVERFLOW_SPEC_TMPVARCV_CONST) \ - _(2760, ZEND_MUL_LONG_NO_OVERFLOW_SPEC_TMPVARCV_TMPVARCV) \ - _(2761, ZEND_MUL_LONG_NO_OVERFLOW_SPEC_TMPVARCV_TMPVARCV) \ - _(2763, ZEND_MUL_LONG_NO_OVERFLOW_SPEC_TMPVARCV_TMPVARCV) \ - _(2769, ZEND_MUL_LONG_SPEC_TMPVARCV_CONST) \ - _(2770, ZEND_MUL_LONG_SPEC_TMPVARCV_TMPVARCV) \ - _(2771, ZEND_MUL_LONG_SPEC_TMPVARCV_TMPVARCV) \ - _(2773, ZEND_MUL_LONG_SPEC_TMPVARCV_TMPVARCV) \ - _(2774, ZEND_MUL_LONG_SPEC_TMPVARCV_CONST) \ - _(2775, ZEND_MUL_LONG_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) \ + _(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) \ - _(2778, ZEND_MUL_LONG_SPEC_TMPVARCV_TMPVARCV) \ - _(2784, ZEND_MUL_LONG_SPEC_TMPVARCV_CONST) \ - _(2785, ZEND_MUL_LONG_SPEC_TMPVARCV_TMPVARCV) \ - _(2786, ZEND_MUL_LONG_SPEC_TMPVARCV_TMPVARCV) \ - _(2788, ZEND_MUL_LONG_SPEC_TMPVARCV_TMPVARCV) \ - _(2794, ZEND_MUL_DOUBLE_SPEC_TMPVARCV_CONST) \ - _(2795, ZEND_MUL_DOUBLE_SPEC_TMPVARCV_TMPVARCV) \ - _(2796, ZEND_MUL_DOUBLE_SPEC_TMPVARCV_TMPVARCV) \ - _(2798, ZEND_MUL_DOUBLE_SPEC_TMPVARCV_TMPVARCV) \ - _(2799, ZEND_MUL_DOUBLE_SPEC_TMPVARCV_CONST) \ - _(2800, ZEND_MUL_DOUBLE_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) \ + _(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) \ - _(2803, ZEND_MUL_DOUBLE_SPEC_TMPVARCV_TMPVARCV) \ - _(2809, ZEND_MUL_DOUBLE_SPEC_TMPVARCV_CONST) \ - _(2810, ZEND_MUL_DOUBLE_SPEC_TMPVARCV_TMPVARCV) \ - _(2811, ZEND_MUL_DOUBLE_SPEC_TMPVARCV_TMPVARCV) \ - _(2813, ZEND_MUL_DOUBLE_SPEC_TMPVARCV_TMPVARCV) \ - _(2829, ZEND_IS_EQUAL_LONG_SPEC_TMPVARCV_CONST) \ - _(2830, ZEND_IS_EQUAL_LONG_SPEC_TMPVARCV_CONST_JMPZ) \ - _(2831, ZEND_IS_EQUAL_LONG_SPEC_TMPVARCV_CONST_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) \ - _(2835, ZEND_IS_EQUAL_LONG_SPEC_TMPVARCV_TMPVARCV) \ - _(2836, ZEND_IS_EQUAL_LONG_SPEC_TMPVARCV_TMPVARCV_JMPZ) \ - _(2837, ZEND_IS_EQUAL_LONG_SPEC_TMPVARCV_TMPVARCV_JMPNZ) \ + _(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) \ + _(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_TMPVARCV) \ _(2842, ZEND_IS_EQUAL_LONG_SPEC_TMPVARCV_TMPVARCV_JMPZ) \ _(2843, ZEND_IS_EQUAL_LONG_SPEC_TMPVARCV_TMPVARCV_JMPNZ) \ - _(2844, ZEND_IS_EQUAL_LONG_SPEC_TMPVARCV_CONST) \ - _(2845, ZEND_IS_EQUAL_LONG_SPEC_TMPVARCV_CONST_JMPZ) \ - _(2846, ZEND_IS_EQUAL_LONG_SPEC_TMPVARCV_CONST_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_TMPVARCV) \ - _(2851, ZEND_IS_EQUAL_LONG_SPEC_TMPVARCV_TMPVARCV_JMPZ) \ - _(2852, 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) \ _(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) \ - _(2874, ZEND_IS_EQUAL_LONG_SPEC_TMPVARCV_CONST) \ - _(2875, ZEND_IS_EQUAL_LONG_SPEC_TMPVARCV_CONST_JMPZ) \ - _(2876, ZEND_IS_EQUAL_LONG_SPEC_TMPVARCV_CONST_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) \ - _(2880, ZEND_IS_EQUAL_LONG_SPEC_TMPVARCV_TMPVARCV) \ - _(2881, ZEND_IS_EQUAL_LONG_SPEC_TMPVARCV_TMPVARCV_JMPZ) \ - _(2882, 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) \ _(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) \ - _(2904, ZEND_IS_EQUAL_DOUBLE_SPEC_TMPVARCV_CONST) \ - _(2905, ZEND_IS_EQUAL_DOUBLE_SPEC_TMPVARCV_CONST_JMPZ) \ - _(2906, ZEND_IS_EQUAL_DOUBLE_SPEC_TMPVARCV_CONST_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) \ - _(2910, ZEND_IS_EQUAL_DOUBLE_SPEC_TMPVARCV_TMPVARCV) \ - _(2911, ZEND_IS_EQUAL_DOUBLE_SPEC_TMPVARCV_TMPVARCV_JMPZ) \ - _(2912, ZEND_IS_EQUAL_DOUBLE_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_TMPVARCV) \ _(2917, ZEND_IS_EQUAL_DOUBLE_SPEC_TMPVARCV_TMPVARCV_JMPZ) \ _(2918, ZEND_IS_EQUAL_DOUBLE_SPEC_TMPVARCV_TMPVARCV_JMPNZ) \ - _(2919, ZEND_IS_EQUAL_DOUBLE_SPEC_TMPVARCV_CONST) \ - _(2920, ZEND_IS_EQUAL_DOUBLE_SPEC_TMPVARCV_CONST_JMPZ) \ - _(2921, ZEND_IS_EQUAL_DOUBLE_SPEC_TMPVARCV_CONST_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_TMPVARCV) \ - _(2926, ZEND_IS_EQUAL_DOUBLE_SPEC_TMPVARCV_TMPVARCV_JMPZ) \ - _(2927, 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) \ _(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) \ - _(2949, ZEND_IS_EQUAL_DOUBLE_SPEC_TMPVARCV_CONST) \ - _(2950, ZEND_IS_EQUAL_DOUBLE_SPEC_TMPVARCV_CONST_JMPZ) \ - _(2951, ZEND_IS_EQUAL_DOUBLE_SPEC_TMPVARCV_CONST_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) \ - _(2955, ZEND_IS_EQUAL_DOUBLE_SPEC_TMPVARCV_TMPVARCV) \ - _(2956, ZEND_IS_EQUAL_DOUBLE_SPEC_TMPVARCV_TMPVARCV_JMPZ) \ - _(2957, 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) \ _(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) \ - _(2979, ZEND_IS_NOT_EQUAL_LONG_SPEC_TMPVARCV_CONST) \ - _(2980, ZEND_IS_NOT_EQUAL_LONG_SPEC_TMPVARCV_CONST_JMPZ) \ - _(2981, ZEND_IS_NOT_EQUAL_LONG_SPEC_TMPVARCV_CONST_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) \ - _(2985, ZEND_IS_NOT_EQUAL_LONG_SPEC_TMPVARCV_TMPVARCV) \ - _(2986, ZEND_IS_NOT_EQUAL_LONG_SPEC_TMPVARCV_TMPVARCV_JMPZ) \ - _(2987, ZEND_IS_NOT_EQUAL_LONG_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_TMPVARCV) \ _(2992, ZEND_IS_NOT_EQUAL_LONG_SPEC_TMPVARCV_TMPVARCV_JMPZ) \ _(2993, ZEND_IS_NOT_EQUAL_LONG_SPEC_TMPVARCV_TMPVARCV_JMPNZ) \ - _(2994, ZEND_IS_NOT_EQUAL_LONG_SPEC_TMPVARCV_CONST) \ - _(2995, ZEND_IS_NOT_EQUAL_LONG_SPEC_TMPVARCV_CONST_JMPZ) \ - _(2996, ZEND_IS_NOT_EQUAL_LONG_SPEC_TMPVARCV_CONST_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_TMPVARCV) \ - _(3001, ZEND_IS_NOT_EQUAL_LONG_SPEC_TMPVARCV_TMPVARCV_JMPZ) \ - _(3002, 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) \ _(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) \ - _(3024, ZEND_IS_NOT_EQUAL_LONG_SPEC_TMPVARCV_CONST) \ - _(3025, ZEND_IS_NOT_EQUAL_LONG_SPEC_TMPVARCV_CONST_JMPZ) \ - _(3026, ZEND_IS_NOT_EQUAL_LONG_SPEC_TMPVARCV_CONST_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) \ - _(3030, ZEND_IS_NOT_EQUAL_LONG_SPEC_TMPVARCV_TMPVARCV) \ - _(3031, ZEND_IS_NOT_EQUAL_LONG_SPEC_TMPVARCV_TMPVARCV_JMPZ) \ - _(3032, 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) \ _(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) \ - _(3054, ZEND_IS_NOT_EQUAL_DOUBLE_SPEC_TMPVARCV_CONST) \ - _(3055, ZEND_IS_NOT_EQUAL_DOUBLE_SPEC_TMPVARCV_CONST_JMPZ) \ - _(3056, ZEND_IS_NOT_EQUAL_DOUBLE_SPEC_TMPVARCV_CONST_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) \ - _(3060, ZEND_IS_NOT_EQUAL_DOUBLE_SPEC_TMPVARCV_TMPVARCV) \ - _(3061, ZEND_IS_NOT_EQUAL_DOUBLE_SPEC_TMPVARCV_TMPVARCV_JMPZ) \ - _(3062, ZEND_IS_NOT_EQUAL_DOUBLE_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_TMPVARCV) \ _(3067, ZEND_IS_NOT_EQUAL_DOUBLE_SPEC_TMPVARCV_TMPVARCV_JMPZ) \ _(3068, ZEND_IS_NOT_EQUAL_DOUBLE_SPEC_TMPVARCV_TMPVARCV_JMPNZ) \ - _(3069, ZEND_IS_NOT_EQUAL_DOUBLE_SPEC_TMPVARCV_CONST) \ - _(3070, ZEND_IS_NOT_EQUAL_DOUBLE_SPEC_TMPVARCV_CONST_JMPZ) \ - _(3071, ZEND_IS_NOT_EQUAL_DOUBLE_SPEC_TMPVARCV_CONST_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_TMPVARCV) \ - _(3076, ZEND_IS_NOT_EQUAL_DOUBLE_SPEC_TMPVARCV_TMPVARCV_JMPZ) \ - _(3077, 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) \ _(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) \ - _(3099, ZEND_IS_NOT_EQUAL_DOUBLE_SPEC_TMPVARCV_CONST) \ - _(3100, ZEND_IS_NOT_EQUAL_DOUBLE_SPEC_TMPVARCV_CONST_JMPZ) \ - _(3101, ZEND_IS_NOT_EQUAL_DOUBLE_SPEC_TMPVARCV_CONST_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) \ - _(3105, ZEND_IS_NOT_EQUAL_DOUBLE_SPEC_TMPVARCV_TMPVARCV) \ - _(3106, ZEND_IS_NOT_EQUAL_DOUBLE_SPEC_TMPVARCV_TMPVARCV_JMPZ) \ - _(3107, 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_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) \ - _(3114, ZEND_IS_IDENTICAL_EMPTY_ARRAY_SPEC_TMPVARCV_CONST) \ - _(3115, ZEND_IS_IDENTICAL_EMPTY_ARRAY_SPEC_TMPVARCV_CONST_JMPZ) \ - _(3116, ZEND_IS_IDENTICAL_EMPTY_ARRAY_SPEC_TMPVARCV_CONST_JMPNZ) \ - _(3117, ZEND_IS_NOT_IDENTICAL_EMPTY_ARRAY_SPEC_TMPVARCV_CONST) \ - _(3118, ZEND_IS_NOT_IDENTICAL_EMPTY_ARRAY_SPEC_TMPVARCV_CONST_JMPZ) \ - _(3119, ZEND_IS_NOT_IDENTICAL_EMPTY_ARRAY_SPEC_TMPVARCV_CONST_JMPNZ) \ - _(3120, ZEND_IS_IDENTICAL_NOTHROW_SPEC_CV_CONST) \ - _(3124, ZEND_IS_IDENTICAL_NOTHROW_SPEC_CV_CV) \ - _(3125, ZEND_IS_NOT_IDENTICAL_NOTHROW_SPEC_CV_CONST) \ - _(3129, ZEND_IS_NOT_IDENTICAL_NOTHROW_SPEC_CV_CV) \ - _(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) \ - _(3136, ZEND_IS_SMALLER_LONG_SPEC_CONST_TMPVARCV) \ - _(3137, ZEND_IS_SMALLER_LONG_SPEC_CONST_TMPVARCV_JMPZ) \ - _(3138, ZEND_IS_SMALLER_LONG_SPEC_CONST_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_CONST_TMPVARCV) \ _(3143, ZEND_IS_SMALLER_LONG_SPEC_CONST_TMPVARCV_JMPZ) \ _(3144, ZEND_IS_SMALLER_LONG_SPEC_CONST_TMPVARCV_JMPNZ) \ - _(3145, ZEND_IS_SMALLER_LONG_SPEC_TMPVARCV_CONST) \ - _(3146, ZEND_IS_SMALLER_LONG_SPEC_TMPVARCV_CONST_JMPZ) \ - _(3147, ZEND_IS_SMALLER_LONG_SPEC_TMPVARCV_CONST_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) \ - _(3151, ZEND_IS_SMALLER_LONG_SPEC_TMPVARCV_TMPVARCV) \ - _(3152, ZEND_IS_SMALLER_LONG_SPEC_TMPVARCV_TMPVARCV_JMPZ) \ - _(3153, ZEND_IS_SMALLER_LONG_SPEC_TMPVARCV_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_TMPVARCV) \ _(3158, ZEND_IS_SMALLER_LONG_SPEC_TMPVARCV_TMPVARCV_JMPZ) \ _(3159, ZEND_IS_SMALLER_LONG_SPEC_TMPVARCV_TMPVARCV_JMPNZ) \ - _(3160, ZEND_IS_SMALLER_LONG_SPEC_TMPVARCV_CONST) \ - _(3161, ZEND_IS_SMALLER_LONG_SPEC_TMPVARCV_CONST_JMPZ) \ - _(3162, ZEND_IS_SMALLER_LONG_SPEC_TMPVARCV_CONST_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_TMPVARCV) \ - _(3167, ZEND_IS_SMALLER_LONG_SPEC_TMPVARCV_TMPVARCV_JMPZ) \ - _(3168, 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) \ _(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) \ - _(3190, ZEND_IS_SMALLER_LONG_SPEC_TMPVARCV_CONST) \ - _(3191, ZEND_IS_SMALLER_LONG_SPEC_TMPVARCV_CONST_JMPZ) \ - _(3192, ZEND_IS_SMALLER_LONG_SPEC_TMPVARCV_CONST_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) \ - _(3196, ZEND_IS_SMALLER_LONG_SPEC_TMPVARCV_TMPVARCV) \ - _(3197, ZEND_IS_SMALLER_LONG_SPEC_TMPVARCV_TMPVARCV_JMPZ) \ - _(3198, 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) \ _(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_DOUBLE_SPEC_CONST_TMPVARCV) \ - _(3209, ZEND_IS_SMALLER_DOUBLE_SPEC_CONST_TMPVARCV_JMPZ) \ - _(3210, ZEND_IS_SMALLER_DOUBLE_SPEC_CONST_TMPVARCV_JMPNZ) \ - _(3211, ZEND_IS_SMALLER_DOUBLE_SPEC_CONST_TMPVARCV) \ - _(3212, ZEND_IS_SMALLER_DOUBLE_SPEC_CONST_TMPVARCV_JMPZ) \ - _(3213, ZEND_IS_SMALLER_DOUBLE_SPEC_CONST_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_CONST_TMPVARCV) \ _(3218, ZEND_IS_SMALLER_DOUBLE_SPEC_CONST_TMPVARCV_JMPZ) \ _(3219, ZEND_IS_SMALLER_DOUBLE_SPEC_CONST_TMPVARCV_JMPNZ) \ - _(3220, ZEND_IS_SMALLER_DOUBLE_SPEC_TMPVARCV_CONST) \ - _(3221, ZEND_IS_SMALLER_DOUBLE_SPEC_TMPVARCV_CONST_JMPZ) \ - _(3222, ZEND_IS_SMALLER_DOUBLE_SPEC_TMPVARCV_CONST_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) \ - _(3226, ZEND_IS_SMALLER_DOUBLE_SPEC_TMPVARCV_TMPVARCV) \ - _(3227, ZEND_IS_SMALLER_DOUBLE_SPEC_TMPVARCV_TMPVARCV_JMPZ) \ - _(3228, ZEND_IS_SMALLER_DOUBLE_SPEC_TMPVARCV_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_TMPVARCV) \ _(3233, ZEND_IS_SMALLER_DOUBLE_SPEC_TMPVARCV_TMPVARCV_JMPZ) \ _(3234, ZEND_IS_SMALLER_DOUBLE_SPEC_TMPVARCV_TMPVARCV_JMPNZ) \ - _(3235, ZEND_IS_SMALLER_DOUBLE_SPEC_TMPVARCV_CONST) \ - _(3236, ZEND_IS_SMALLER_DOUBLE_SPEC_TMPVARCV_CONST_JMPZ) \ - _(3237, ZEND_IS_SMALLER_DOUBLE_SPEC_TMPVARCV_CONST_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_TMPVARCV) \ - _(3242, ZEND_IS_SMALLER_DOUBLE_SPEC_TMPVARCV_TMPVARCV_JMPZ) \ - _(3243, 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) \ _(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) \ - _(3265, ZEND_IS_SMALLER_DOUBLE_SPEC_TMPVARCV_CONST) \ - _(3266, ZEND_IS_SMALLER_DOUBLE_SPEC_TMPVARCV_CONST_JMPZ) \ - _(3267, ZEND_IS_SMALLER_DOUBLE_SPEC_TMPVARCV_CONST_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) \ - _(3271, ZEND_IS_SMALLER_DOUBLE_SPEC_TMPVARCV_TMPVARCV) \ - _(3272, ZEND_IS_SMALLER_DOUBLE_SPEC_TMPVARCV_TMPVARCV_JMPZ) \ - _(3273, 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) \ _(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_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) \ - _(3286, ZEND_IS_SMALLER_OR_EQUAL_LONG_SPEC_CONST_TMPVARCV) \ - _(3287, ZEND_IS_SMALLER_OR_EQUAL_LONG_SPEC_CONST_TMPVARCV_JMPZ) \ - _(3288, ZEND_IS_SMALLER_OR_EQUAL_LONG_SPEC_CONST_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_CONST_TMPVARCV) \ _(3293, ZEND_IS_SMALLER_OR_EQUAL_LONG_SPEC_CONST_TMPVARCV_JMPZ) \ _(3294, ZEND_IS_SMALLER_OR_EQUAL_LONG_SPEC_CONST_TMPVARCV_JMPNZ) \ - _(3295, ZEND_IS_SMALLER_OR_EQUAL_LONG_SPEC_TMPVARCV_CONST) \ - _(3296, ZEND_IS_SMALLER_OR_EQUAL_LONG_SPEC_TMPVARCV_CONST_JMPZ) \ - _(3297, ZEND_IS_SMALLER_OR_EQUAL_LONG_SPEC_TMPVARCV_CONST_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) \ - _(3301, ZEND_IS_SMALLER_OR_EQUAL_LONG_SPEC_TMPVARCV_TMPVARCV) \ - _(3302, ZEND_IS_SMALLER_OR_EQUAL_LONG_SPEC_TMPVARCV_TMPVARCV_JMPZ) \ - _(3303, ZEND_IS_SMALLER_OR_EQUAL_LONG_SPEC_TMPVARCV_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_TMPVARCV) \ _(3308, ZEND_IS_SMALLER_OR_EQUAL_LONG_SPEC_TMPVARCV_TMPVARCV_JMPZ) \ _(3309, ZEND_IS_SMALLER_OR_EQUAL_LONG_SPEC_TMPVARCV_TMPVARCV_JMPNZ) \ - _(3310, ZEND_IS_SMALLER_OR_EQUAL_LONG_SPEC_TMPVARCV_CONST) \ - _(3311, ZEND_IS_SMALLER_OR_EQUAL_LONG_SPEC_TMPVARCV_CONST_JMPZ) \ - _(3312, ZEND_IS_SMALLER_OR_EQUAL_LONG_SPEC_TMPVARCV_CONST_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_TMPVARCV) \ - _(3317, ZEND_IS_SMALLER_OR_EQUAL_LONG_SPEC_TMPVARCV_TMPVARCV_JMPZ) \ - _(3318, 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) \ _(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) \ - _(3340, ZEND_IS_SMALLER_OR_EQUAL_LONG_SPEC_TMPVARCV_CONST) \ - _(3341, ZEND_IS_SMALLER_OR_EQUAL_LONG_SPEC_TMPVARCV_CONST_JMPZ) \ - _(3342, ZEND_IS_SMALLER_OR_EQUAL_LONG_SPEC_TMPVARCV_CONST_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) \ - _(3346, ZEND_IS_SMALLER_OR_EQUAL_LONG_SPEC_TMPVARCV_TMPVARCV) \ - _(3347, ZEND_IS_SMALLER_OR_EQUAL_LONG_SPEC_TMPVARCV_TMPVARCV_JMPZ) \ - _(3348, 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) \ _(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_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) \ - _(3361, ZEND_IS_SMALLER_OR_EQUAL_DOUBLE_SPEC_CONST_TMPVARCV) \ - _(3362, ZEND_IS_SMALLER_OR_EQUAL_DOUBLE_SPEC_CONST_TMPVARCV_JMPZ) \ - _(3363, ZEND_IS_SMALLER_OR_EQUAL_DOUBLE_SPEC_CONST_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_CONST_TMPVARCV) \ _(3368, ZEND_IS_SMALLER_OR_EQUAL_DOUBLE_SPEC_CONST_TMPVARCV_JMPZ) \ _(3369, ZEND_IS_SMALLER_OR_EQUAL_DOUBLE_SPEC_CONST_TMPVARCV_JMPNZ) \ - _(3370, ZEND_IS_SMALLER_OR_EQUAL_DOUBLE_SPEC_TMPVARCV_CONST) \ - _(3371, ZEND_IS_SMALLER_OR_EQUAL_DOUBLE_SPEC_TMPVARCV_CONST_JMPZ) \ - _(3372, ZEND_IS_SMALLER_OR_EQUAL_DOUBLE_SPEC_TMPVARCV_CONST_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) \ - _(3376, ZEND_IS_SMALLER_OR_EQUAL_DOUBLE_SPEC_TMPVARCV_TMPVARCV) \ - _(3377, ZEND_IS_SMALLER_OR_EQUAL_DOUBLE_SPEC_TMPVARCV_TMPVARCV_JMPZ) \ - _(3378, ZEND_IS_SMALLER_OR_EQUAL_DOUBLE_SPEC_TMPVARCV_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_TMPVARCV) \ _(3383, ZEND_IS_SMALLER_OR_EQUAL_DOUBLE_SPEC_TMPVARCV_TMPVARCV_JMPZ) \ _(3384, ZEND_IS_SMALLER_OR_EQUAL_DOUBLE_SPEC_TMPVARCV_TMPVARCV_JMPNZ) \ - _(3385, ZEND_IS_SMALLER_OR_EQUAL_DOUBLE_SPEC_TMPVARCV_CONST) \ - _(3386, ZEND_IS_SMALLER_OR_EQUAL_DOUBLE_SPEC_TMPVARCV_CONST_JMPZ) \ - _(3387, ZEND_IS_SMALLER_OR_EQUAL_DOUBLE_SPEC_TMPVARCV_CONST_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_TMPVARCV) \ - _(3392, ZEND_IS_SMALLER_OR_EQUAL_DOUBLE_SPEC_TMPVARCV_TMPVARCV_JMPZ) \ - _(3393, 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) \ _(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) \ - _(3415, ZEND_IS_SMALLER_OR_EQUAL_DOUBLE_SPEC_TMPVARCV_CONST) \ - _(3416, ZEND_IS_SMALLER_OR_EQUAL_DOUBLE_SPEC_TMPVARCV_CONST_JMPZ) \ - _(3417, ZEND_IS_SMALLER_OR_EQUAL_DOUBLE_SPEC_TMPVARCV_CONST_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) \ - _(3421, ZEND_IS_SMALLER_OR_EQUAL_DOUBLE_SPEC_TMPVARCV_TMPVARCV) \ - _(3422, ZEND_IS_SMALLER_OR_EQUAL_DOUBLE_SPEC_TMPVARCV_TMPVARCV_JMPZ) \ - _(3423, 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_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) \ - _(3430, ZEND_PRE_INC_LONG_NO_OVERFLOW_SPEC_CV_RETVAL_UNUSED) \ - _(3431, ZEND_PRE_INC_LONG_NO_OVERFLOW_SPEC_CV_RETVAL_USED) \ - _(3432, ZEND_PRE_INC_LONG_SPEC_CV_RETVAL_UNUSED) \ - _(3433, ZEND_PRE_INC_LONG_SPEC_CV_RETVAL_USED) \ - _(3434, ZEND_PRE_DEC_LONG_NO_OVERFLOW_SPEC_CV_RETVAL_UNUSED) \ - _(3435, ZEND_PRE_DEC_LONG_NO_OVERFLOW_SPEC_CV_RETVAL_USED) \ - _(3436, ZEND_PRE_DEC_LONG_SPEC_CV_RETVAL_UNUSED) \ - _(3437, ZEND_PRE_DEC_LONG_SPEC_CV_RETVAL_USED) \ - _(3438, ZEND_POST_INC_LONG_NO_OVERFLOW_SPEC_CV) \ - _(3439, ZEND_POST_INC_LONG_SPEC_CV) \ - _(3440, ZEND_POST_DEC_LONG_NO_OVERFLOW_SPEC_CV) \ - _(3441, ZEND_POST_DEC_LONG_SPEC_CV) \ - _(3442, ZEND_QM_ASSIGN_LONG_SPEC_CONST) \ - _(3443, ZEND_QM_ASSIGN_LONG_SPEC_TMPVARCV) \ - _(3444, ZEND_QM_ASSIGN_LONG_SPEC_TMPVARCV) \ - _(3446, ZEND_QM_ASSIGN_LONG_SPEC_TMPVARCV) \ - _(3447, ZEND_QM_ASSIGN_DOUBLE_SPEC_CONST) \ - _(3448, ZEND_QM_ASSIGN_DOUBLE_SPEC_TMPVARCV) \ - _(3449, ZEND_QM_ASSIGN_DOUBLE_SPEC_TMPVARCV) \ - _(3451, ZEND_QM_ASSIGN_DOUBLE_SPEC_TMPVARCV) \ - _(3452, ZEND_QM_ASSIGN_NOREF_SPEC_CONST) \ - _(3453, ZEND_QM_ASSIGN_NOREF_SPEC_TMPVARCV) \ - _(3454, ZEND_QM_ASSIGN_NOREF_SPEC_TMPVARCV) \ - _(3456, ZEND_QM_ASSIGN_NOREF_SPEC_TMPVARCV) \ - _(3458, ZEND_FETCH_DIM_R_INDEX_SPEC_CONST_TMPVARCV) \ - _(3459, ZEND_FETCH_DIM_R_INDEX_SPEC_CONST_TMPVARCV) \ - _(3461, ZEND_FETCH_DIM_R_INDEX_SPEC_CONST_TMPVARCV) \ - _(3462, ZEND_FETCH_DIM_R_INDEX_SPEC_TMPVAR_CONST) \ - _(3463, ZEND_FETCH_DIM_R_INDEX_SPEC_TMPVAR_TMPVARCV) \ - _(3464, ZEND_FETCH_DIM_R_INDEX_SPEC_TMPVAR_TMPVARCV) \ - _(3466, ZEND_FETCH_DIM_R_INDEX_SPEC_TMPVAR_TMPVARCV) \ - _(3467, ZEND_FETCH_DIM_R_INDEX_SPEC_TMPVAR_CONST) \ - _(3468, ZEND_FETCH_DIM_R_INDEX_SPEC_TMPVAR_TMPVARCV) \ + _(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) \ - _(3471, ZEND_FETCH_DIM_R_INDEX_SPEC_TMPVAR_TMPVARCV) \ - _(3477, ZEND_FETCH_DIM_R_INDEX_SPEC_CV_CONST) \ - _(3478, ZEND_FETCH_DIM_R_INDEX_SPEC_CV_TMPVARCV) \ - _(3479, ZEND_FETCH_DIM_R_INDEX_SPEC_CV_TMPVARCV) \ - _(3481, ZEND_FETCH_DIM_R_INDEX_SPEC_CV_TMPVARCV) \ - _(3484, ZEND_SEND_VAR_SIMPLE_SPEC_VAR) \ - _(3486, ZEND_SEND_VAR_SIMPLE_SPEC_CV) \ - _(3489, ZEND_SEND_VAR_EX_SIMPLE_SPEC_VAR_UNUSED) \ - _(3491, ZEND_SEND_VAR_EX_SIMPLE_SPEC_CV_UNUSED) \ - _(3492, ZEND_SEND_VAL_SIMPLE_SPEC_CONST) \ - _(3493, ZEND_SEND_VAL_EX_SIMPLE_SPEC_CONST) \ - _(3494, ZEND_FE_FETCH_R_SIMPLE_SPEC_VAR_CV_RETVAL_UNUSED) \ - _(3495, ZEND_FE_FETCH_R_SIMPLE_SPEC_VAR_CV_RETVAL_USED) \ - _(3495+1, ZEND_NULL) + _(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 88207966affa..fd36850a4833 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[214] = { +static const char *zend_vm_opcodes_names[215] = { "ZEND_NOP", "ZEND_ADD", "ZEND_SUB", @@ -235,11 +235,12 @@ static const char *zend_vm_opcodes_names[214] = { "ZEND_INIT_PARENT_PROPERTY_HOOK_CALL", "ZEND_DECLARE_ATTRIBUTED_CONST", "ZEND_SEND_PLACEHOLDER", - "ZEND_DO_FCALL_PARTIAL", + "ZEND_CALLABLE_CONVERT_PARTIAL", "ZEND_CHECK_PARTIAL_ARGS", + "ZEND_CALL_PARTIAL", }; -static uint32_t zend_vm_opcodes_flags[214] = { +static uint32_t zend_vm_opcodes_flags[215] = { 0x00000000, 0x00000b0b, 0x00000b0b, @@ -451,9 +452,10 @@ static uint32_t zend_vm_opcodes_flags[214] = { 0x01042003, 0x01001103, 0x00000303, + 0x00000301, + 0x00000001, 0x00000101, 0x00000000, - 0x00000101, }; 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 b1c7cfd458e4..e625d3779a4e 100644 --- a/Zend/zend_vm_opcodes.h +++ b/Zend/zend_vm_opcodes.h @@ -295,9 +295,10 @@ END_EXTERN_C() #define ZEND_INIT_PARENT_PROPERTY_HOOK_CALL 209 #define ZEND_DECLARE_ATTRIBUTED_CONST 210 #define ZEND_SEND_PLACEHOLDER 211 -#define ZEND_DO_FCALL_PARTIAL 212 +#define ZEND_CALLABLE_CONVERT_PARTIAL 212 #define ZEND_CHECK_PARTIAL_ARGS 213 +#define ZEND_CALL_PARTIAL 214 -#define ZEND_VM_LAST_OPCODE 213 +#define ZEND_VM_LAST_OPCODE 214 #endif diff --git a/ext/reflection/php_reflection.c b/ext/reflection/php_reflection.c index 001276ba485b..9d37ca026616 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) @@ -219,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); @@ -692,10 +694,8 @@ static void _enum_case_string(smart_str *str, const zend_string *name, zend_clas static zend_op *get_recv_op(const zend_op_array *op_array, uint32_t offset) { - if (op_array->fn_flags & ZEND_ACC_PARTIAL) { - op_array = (zend_op_array*) op_array->prototype; - } - + // TODO: partial + zend_op *op = op_array->opcodes; const zend_op *end = op + op_array->last; @@ -719,7 +719,7 @@ static zval *get_default_from_recv(zend_op_array *op_array, uint32_t offset) { if (!recv) { return NULL; } - + if (recv->opcode != ZEND_RECV_INIT) { return NULL; } @@ -901,7 +901,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) { + if (zend_is_partial_function(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"); @@ -934,9 +945,6 @@ static void _function_string(smart_str *str, zend_function *fptr, zend_class_ent if (fptr->common.fn_flags & ZEND_ACC_ABSTRACT) { smart_str_appends(str, "abstract "); } - if (fptr->common.fn_flags & ZEND_ACC_PARTIAL) { - smart_str_appends(str, "partial "); - } if (fptr->common.fn_flags & ZEND_ACC_FINAL) { smart_str_appends(str, "final "); } @@ -1479,7 +1487,7 @@ static void reflection_parameter_factory(zend_function *fptr, zval *closure_obje intern = Z_REFLECTION_P(object); reference = (parameter_reference*) emalloc(sizeof(parameter_reference)); reference->arg_info = arg_info; - if (fptr->common.fn_flags & ZEND_ACC_PARTIAL) { + if (0 /* TODO: fptr->common.fn_flags & ZEND_ACC_PARTIAL*/) { reference->offset = reflection_parameter_partial_offset(fptr, arg_info); } else { reference->offset = offset; @@ -1843,7 +1851,7 @@ ZEND_METHOD(ReflectionFunctionAbstract, isClosure) ZEND_PARSE_PARAMETERS_NONE(); GET_REFLECTION_OBJECT_PTR(fptr); - RETURN_BOOL(fptr->common.fn_flags & (ZEND_ACC_CLOSURE|ZEND_ACC_PARTIAL)); + RETURN_BOOL(fptr->common.fn_flags & ZEND_ACC_CLOSURE/*TODO |ZEND_ACC_PARTIAL*/); } /* }}} */ @@ -1857,7 +1865,9 @@ ZEND_METHOD(ReflectionFunctionAbstract, isPartial) RETURN_THROWS(); } GET_REFLECTION_OBJECT_PTR(fptr); - RETURN_BOOL(fptr->common.fn_flags & ZEND_ACC_PARTIAL); + RETURN_BOOL(0); + (void)fptr; + // TODO RETURN_BOOL(fptr->common.fn_flags & ZEND_ACC_PARTIAL); } /* }}} */ From c6f97c921cf17ba092bddc64b556d30800cf5c41 Mon Sep 17 00:00:00 2001 From: Arnaud Le Blanc Date: Sun, 22 Jun 2025 10:29:44 +0200 Subject: [PATCH 03/28] Pipe optimization --- .../pipe_optimization_001.phpt | 43 +++++++ .../pipe_optimization_002.phpt | 47 ++++++++ .../pipe_optimization_003.phpt | 47 ++++++++ .../pipe_optimization_004.phpt | 62 ++++++++++ .../pipe_optimization_005.phpt | 47 ++++++++ .../pipe_optimization_006.phpt | 51 +++++++++ .../pipe_optimization_007.phpt | 62 ++++++++++ .../pipe_optimization_008.phpt | 63 +++++++++++ .../pipe_optimization_009.phpt | 99 ++++++++++++++++ .../pipe_optimization_010.phpt | 54 +++++++++ .../pipe_optimization_011.phpt | 106 ++++++++++++++++++ Zend/zend_ast.c | 19 +++- Zend/zend_ast.h | 2 + Zend/zend_compile.c | 105 ++++++++++++++++- 14 files changed, 804 insertions(+), 3 deletions(-) create mode 100644 Zend/tests/partial_application/pipe_optimization_001.phpt create mode 100644 Zend/tests/partial_application/pipe_optimization_002.phpt create mode 100644 Zend/tests/partial_application/pipe_optimization_003.phpt create mode 100644 Zend/tests/partial_application/pipe_optimization_004.phpt create mode 100644 Zend/tests/partial_application/pipe_optimization_005.phpt create mode 100644 Zend/tests/partial_application/pipe_optimization_006.phpt create mode 100644 Zend/tests/partial_application/pipe_optimization_007.phpt create mode 100644 Zend/tests/partial_application/pipe_optimization_008.phpt create mode 100644 Zend/tests/partial_application/pipe_optimization_009.phpt create mode 100644 Zend/tests/partial_application/pipe_optimization_010.phpt create mode 100644 Zend/tests/partial_application/pipe_optimization_011.phpt 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 000000000000..2fb2ef14a7aa --- /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 80 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 96 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 000000000000..2d1d8b6e23eb --- /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 80 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 112 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 000000000000..07b21e9f474b --- /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 80 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 112 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 000000000000..f94e5cc32aa5 --- /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 80 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 112 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 000000000000..4f9d0506eeba --- /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 80 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 112 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 000000000000..7285ec96011a --- /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 80 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 128 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 000000000000..5b231c3c2eb6 --- /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 80 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 112 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 000000000000..d01c9128bf91 --- /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 80 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 112 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 000000000000..f3e7449c764c --- /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 80 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 128 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 000000000000..0dd1f8a23d3e --- /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 80 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 96 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 112 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 000000000000..f655f6161eab --- /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 80 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 128 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/zend_ast.c b/Zend/zend_ast.c index d3643d094e23..16a845a4636f 100644 --- a/Zend/zend_ast.c +++ b/Zend/zend_ast.c @@ -1127,12 +1127,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, @@ -2969,3 +2969,18 @@ zend_ast * ZEND_FASTCALL zend_ast_with_attributes(zend_ast *ast, zend_ast *attr) return ast; } + +zend_ast_list * ZEND_FASTCALL zend_ast_call_get_arg_list(zend_ast *ast) +{ + if (ast->kind == ZEND_AST_CALL) { + if (ast->child[1]->kind == ZEND_AST_ARG_LIST) { + return zend_ast_get_list(ast->child[1]); + } + } else if (ast->kind == ZEND_AST_STATIC_CALL || ast->kind == ZEND_AST_METHOD_CALL) { + if (ast->child[2]->kind == ZEND_AST_ARG_LIST) { + return zend_ast_get_list(ast->child[2]); + } + } + + return NULL; +} diff --git a/Zend/zend_ast.h b/Zend/zend_ast.h index b9f5475b04e4..344a8f964970 100644 --- a/Zend/zend_ast.h +++ b/Zend/zend_ast.h @@ -427,4 +427,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_list * ZEND_FASTCALL zend_ast_call_get_arg_list(zend_ast *ast); + #endif diff --git a/Zend/zend_compile.c b/Zend/zend_compile.c index 2d1f72002a84..5eb1f33f3979 100644 --- a/Zend/zend_compile.c +++ b/Zend/zend_compile.c @@ -4112,6 +4112,18 @@ static void zend_compile_dynamic_call(znode *result, znode *name_node, zend_ast } /* }}} */ +static inline bool zend_args_contain_partial(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_PLACEHOLDER_ARG) { + return 1; + } + } + return 0; +} + static inline bool zend_args_contain_unpack_or_named_or_partial(zend_ast_list *args) /* {{{ */ { uint32_t i; @@ -6511,6 +6523,75 @@ 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_list *arg_list = zend_ast_call_get_arg_list(callable_ast); + if (!arg_list) { + return NULL; + } + + 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; + } + } + } + + if (first_placeholder == NULL) { + /* Not a PFA */ + return NULL; + } + + 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]; @@ -6531,11 +6612,14 @@ 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 *arg_list_ast = zend_ast_create_list(1, ZEND_AST_ARG_LIST, arg); zend_ast *fcall_ast; znode callable_result; + zend_ast *pfa_arg_list_ast = NULL; + /* Turn $foo |> bar(...) into bar($foo). */ if (callable_ast->kind == ZEND_AST_CALL && callable_ast->child[1]->kind == ZEND_AST_CALLABLE_CONVERT) { @@ -6551,6 +6635,25 @@ static void zend_compile_pipe(znode *result, zend_ast *ast) && 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); + /* Turn $foo |> PFA into plain function call if possible */ + } else 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_compile_expr(&callable_result, callable_ast); From a7dc4ed897110f58fb80d630071cdefcf645e8db Mon Sep 17 00:00:00 2001 From: Arnaud Le Blanc Date: Mon, 23 Jun 2025 10:48:02 +0200 Subject: [PATCH 04/28] ws --- Zend/tests/partial_application/pipe_optimization_008.phpt | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Zend/tests/partial_application/pipe_optimization_008.phpt b/Zend/tests/partial_application/pipe_optimization_008.phpt index d01c9128bf91..658015832563 100644 --- a/Zend/tests/partial_application/pipe_optimization_008.phpt +++ b/Zend/tests/partial_application/pipe_optimization_008.phpt @@ -14,7 +14,7 @@ if (time() > 0) { } try { -2 |> foo(..., a: 1); + 2 |> foo(..., a: 1); } catch (\Throwable $e) { echo $e->getMessage(), "\n"; } From fe910ad5be8db8a87c4d73f1b2af0cf93c0219c5 Mon Sep 17 00:00:00 2001 From: Arnaud Le Blanc Date: Mon, 23 Jun 2025 10:48:08 +0200 Subject: [PATCH 05/28] unused function --- Zend/zend_compile.c | 12 ------------ 1 file changed, 12 deletions(-) diff --git a/Zend/zend_compile.c b/Zend/zend_compile.c index 5eb1f33f3979..9b24355869a1 100644 --- a/Zend/zend_compile.c +++ b/Zend/zend_compile.c @@ -4112,18 +4112,6 @@ static void zend_compile_dynamic_call(znode *result, znode *name_node, zend_ast } /* }}} */ -static inline bool zend_args_contain_partial(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_PLACEHOLDER_ARG) { - return 1; - } - } - return 0; -} - static inline bool zend_args_contain_unpack_or_named_or_partial(zend_ast_list *args) /* {{{ */ { uint32_t i; From d73829e83d944ff02e466f58293c373e119b5793 Mon Sep 17 00:00:00 2001 From: Arnaud Le Blanc Date: Mon, 23 Jun 2025 10:48:14 +0200 Subject: [PATCH 06/28] comment --- Zend/zend_partial.c | 1 + 1 file changed, 1 insertion(+) diff --git a/Zend/zend_partial.c b/Zend/zend_partial.c index d6b9c17e1ede..e9dd4c552f19 100644 --- a/Zend/zend_partial.c +++ b/Zend/zend_partial.c @@ -515,6 +515,7 @@ static void zend_partial_init_call_trampoline_op(void) { 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++; From 3e13275a67e4a531dec11f4a02ed56c117167b4e Mon Sep 17 00:00:00 2001 From: Arnaud Le Blanc Date: Mon, 23 Jun 2025 12:04:17 +0200 Subject: [PATCH 07/28] Fix refcounting --- Zend/zend_partial.c | 8 ++++++-- Zend/zend_partial.h | 4 ++++ Zend/zend_vm_def.h | 10 ++++++++++ ext/reflection/php_reflection.c | 2 +- 4 files changed, 21 insertions(+), 3 deletions(-) diff --git a/Zend/zend_partial.c b/Zend/zend_partial.c index e9dd4c552f19..d1b4b02615e0 100644 --- a/Zend/zend_partial.c +++ b/Zend/zend_partial.c @@ -917,6 +917,9 @@ zend_result zend_partial_init_call(zend_execute_data *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); @@ -960,10 +963,9 @@ zend_result zend_partial_init_call(zend_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); } - OBJ_RELEASE(&partial->std); - return SUCCESS; } @@ -975,6 +977,8 @@ void zend_partial_create(zval *result, uint32_t info, zval *this_ptr, zend_funct 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); diff --git a/Zend/zend_partial.h b/Zend/zend_partial.h index fc3bd74b4c05..d423e8a7b6c3 100644 --- a/Zend/zend_partial.h +++ b/Zend/zend_partial.h @@ -23,6 +23,10 @@ BEGIN_EXTERN_C() +/* This macro depends on zend_closure structure layout */ +#define ZEND_PARTIAL_OBJECT(func) \ + ((zend_object*)((char*)(func) - XtOffsetOf(struct{uint32_t a; zend_function b;}, b) - sizeof(zval) - sizeof(zend_function) - sizeof(zend_object))) + typedef struct _zend_partial zend_partial; void zend_partial_startup(void); diff --git a/Zend/zend_vm_def.h b/Zend/zend_vm_def.h index e19df802214b..4046f38a3851 100644 --- a/Zend/zend_vm_def.h +++ b/Zend/zend_vm_def.h @@ -2978,6 +2978,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))); } EG(vm_stack_top) = (zval*)execute_data; execute_data = EX(prev_execute_data); @@ -3012,6 +3014,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; @@ -3069,6 +3073,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) */ { @@ -9328,6 +9334,10 @@ ZEND_VM_HANDLER(214, ZEND_CALL_PARTIAL, ANY, ANY, SPEC(OBSERVER)) 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); diff --git a/ext/reflection/php_reflection.c b/ext/reflection/php_reflection.c index 9d37ca026616..bc90bb7e00ae 100644 --- a/ext/reflection/php_reflection.c +++ b/ext/reflection/php_reflection.c @@ -902,7 +902,7 @@ static void _function_string(smart_str *str, zend_function *fptr, zend_class_ent smart_str_appendl(str, indent, strlen(indent)); const char *prefix = "Function [ "; - if (fptr->common.fn_flags & ZEND_ACC_CLOSURE) { + if (fptr->common.fn_flags & (ZEND_ACC_CLOSURE|ZEND_ACC_FAKE_CLOSURE)) { if (zend_is_partial_function(fptr)) { prefix = "Partial [ "; } else { From 6c18b30e79e0801f02bf05e41b599ed2a19f59c4 Mon Sep 17 00:00:00 2001 From: Arnaud Le Blanc Date: Mon, 23 Jun 2025 16:05:28 +0200 Subject: [PATCH 08/28] RFC examples --- .../partial_application/rfc_examples_001.phpt | 150 ++++++++++++++++++ .../partial_application/rfc_examples_002.phpt | 116 ++++++++++++++ .../partial_application/rfc_examples_003.phpt | 31 ++++ .../partial_application/rfc_examples_004.phpt | 13 ++ .../partial_application/rfc_examples_005.phpt | 13 ++ .../partial_application/rfc_examples_006.phpt | 28 ++++ .../partial_application/rfc_examples_007.phpt | 29 ++++ .../partial_application/rfc_examples_008.phpt | 25 +++ .../partial_application/rfc_examples_009.phpt | 24 +++ .../partial_application/rfc_examples_010.phpt | 24 +++ 10 files changed, 453 insertions(+) create mode 100644 Zend/tests/partial_application/rfc_examples_001.phpt create mode 100644 Zend/tests/partial_application/rfc_examples_002.phpt create mode 100644 Zend/tests/partial_application/rfc_examples_003.phpt create mode 100644 Zend/tests/partial_application/rfc_examples_004.phpt create mode 100644 Zend/tests/partial_application/rfc_examples_005.phpt create mode 100644 Zend/tests/partial_application/rfc_examples_006.phpt create mode 100644 Zend/tests/partial_application/rfc_examples_007.phpt create mode 100644 Zend/tests/partial_application/rfc_examples_008.phpt create mode 100644 Zend/tests/partial_application/rfc_examples_009.phpt create mode 100644 Zend/tests/partial_application/rfc_examples_010.phpt 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 000000000000..e537c3db23fe --- /dev/null +++ b/Zend/tests/partial_application/rfc_examples_001.phpt @@ -0,0 +1,150 @@ +--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 000000000000..dd921bde4505 --- /dev/null +++ b/Zend/tests/partial_application/rfc_examples_002.phpt @@ -0,0 +1,116 @@ +--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 000000000000..a925a334a8ae --- /dev/null +++ b/Zend/tests/partial_application/rfc_examples_003.phpt @@ -0,0 +1,31 @@ +--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 000000000000..91ef969b53ae --- /dev/null +++ b/Zend/tests/partial_application/rfc_examples_004.phpt @@ -0,0 +1,13 @@ +--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 000000000000..461c9b9d5e2a --- /dev/null +++ b/Zend/tests/partial_application/rfc_examples_005.phpt @@ -0,0 +1,13 @@ +--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 000000000000..7834b05dacf5 --- /dev/null +++ b/Zend/tests/partial_application/rfc_examples_006.phpt @@ -0,0 +1,28 @@ +--TEST-- +Partial application RFC examples: func_get_args() +--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 000000000000..6fd4b587499d --- /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 000000000000..c66b0bd9d563 --- /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 000000000000..09e1647baa2a --- /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) { +} From 78521dd11ee5680b69b1f3e9a1ab6c5401cae3d8 Mon Sep 17 00:00:00 2001 From: Arnaud Le Blanc Date: Mon, 23 Jun 2025 19:20:45 +0200 Subject: [PATCH 09/28] Generated file --- Zend/zend_vm_execute.h | 20 ++++++++++++++++++++ 1 file changed, 20 insertions(+) diff --git a/Zend/zend_vm_execute.h b/Zend/zend_vm_execute.h index b86c1aa3eb0c..434a1a4884ac 100644 --- a/Zend/zend_vm_execute.h +++ b/Zend/zend_vm_execute.h @@ -1191,6 +1191,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))); } EG(vm_stack_top) = (zval*)execute_data; execute_data = EX(prev_execute_data); @@ -1225,6 +1227,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 +1286,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) */ { @@ -3903,6 +3909,10 @@ static ZEND_OPCODE_HANDLER_RET ZEND_FASTCALL ZEND_CALL_PARTIAL_SPEC_HANDLER(ZEND 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); @@ -4021,6 +4031,10 @@ static ZEND_OPCODE_HANDLER_RET ZEND_FASTCALL ZEND_CALL_PARTIAL_SPEC_OBSERVER_HAN 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); @@ -59129,6 +59143,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))); } EG(vm_stack_top) = (zval*)execute_data; execute_data = EX(prev_execute_data); @@ -59163,6 +59179,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; @@ -59220,6 +59238,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) */ { From 3dcc430243af13ad6f3a846553658394c3f0edb6 Mon Sep 17 00:00:00 2001 From: Arnaud Le Blanc Date: Fri, 27 Jun 2025 09:07:57 +0200 Subject: [PATCH 10/28] Positional placeholders that run into the variadic portion generate an unnamed parameter --- Zend/tests/partial_application/magic_001.phpt | 4 ++-- Zend/tests/partial_application/magic_002.phpt | 4 ++-- Zend/tests/partial_application/magic_005.phpt | 2 +- .../partial_application/reflection_002.phpt | 6 +++--- .../partial_application/reflection_003.phpt | 2 +- Zend/zend_execute.c | 4 ++++ Zend/zend_partial.c | 17 +++++++++-------- 7 files changed, 22 insertions(+), 17 deletions(-) diff --git a/Zend/tests/partial_application/magic_001.phpt b/Zend/tests/partial_application/magic_001.phpt index b83f8071fd72..2b4fa5e497f0 100644 --- a/Zend/tests/partial_application/magic_001.phpt +++ b/Zend/tests/partial_application/magic_001.phpt @@ -47,7 +47,7 @@ Partial [ public method method ] { @@ %s 12 - 12 - Parameters [1] { - Parameter #0 [ $args ] + Parameter #0 [ $ ] } } not enough arguments for application of Foo::method, 0 given and exactly 1 expected, declared in %s on line 12 @@ -58,7 +58,7 @@ Partial [ public method method ] { @@ %s 30 - 30 - Parameters [2] { - Parameter #0 [ $args ] + Parameter #0 [ $ ] Parameter #1 [ ...$args ] } } diff --git a/Zend/tests/partial_application/magic_002.phpt b/Zend/tests/partial_application/magic_002.phpt index 2505599c2343..2280d8da34dd 100644 --- a/Zend/tests/partial_application/magic_002.phpt +++ b/Zend/tests/partial_application/magic_002.phpt @@ -33,7 +33,7 @@ Partial [ public method method ] { @@ %s 10 - 10 - Parameters [1] { - Parameter #0 [ $args ] + Parameter #0 [ $ ] } } Foo::method @@ -42,7 +42,7 @@ Partial [ public method method ] { @@ %s 16 - 16 - Parameters [2] { - Parameter #0 [ $args ] + Parameter #0 [ $ ] Parameter #1 [ ...$args ] } } diff --git a/Zend/tests/partial_application/magic_005.phpt b/Zend/tests/partial_application/magic_005.phpt index cd62a64f34fd..4a5e1a7b069a 100644 --- a/Zend/tests/partial_application/magic_005.phpt +++ b/Zend/tests/partial_application/magic_005.phpt @@ -24,7 +24,7 @@ object(Closure)#%d (6) { } ["parameter"]=> array(1) { - ["$args"]=> + ["$"]=> string(10) "" } ["args"]=> diff --git a/Zend/tests/partial_application/reflection_002.phpt b/Zend/tests/partial_application/reflection_002.phpt index 4f677701938e..cee99f0b7735 100644 --- a/Zend/tests/partial_application/reflection_002.phpt +++ b/Zend/tests/partial_application/reflection_002.phpt @@ -43,7 +43,7 @@ Partial [ function foo ] { - Parameters [2] { Parameter #0 [ $a ] - Parameter #1 [ $b ] + Parameter #1 [ $ ] } } Partial [ function foo ] { @@ -51,7 +51,7 @@ Partial [ function foo ] { - Parameters [3] { Parameter #0 [ $a ] - Parameter #1 [ $b ] - Parameter #2 [ $b ] + Parameter #1 [ $ ] + Parameter #2 [ $ ] } } diff --git a/Zend/tests/partial_application/reflection_003.phpt b/Zend/tests/partial_application/reflection_003.phpt index db4827b066dd..e3c2b487b673 100644 --- a/Zend/tests/partial_application/reflection_003.phpt +++ b/Zend/tests/partial_application/reflection_003.phpt @@ -37,7 +37,7 @@ Partial [ function sprintf ] { - Parameters [2] { Parameter #0 [ string $format ] - Parameter #1 [ mixed $values ] + Parameter #1 [ mixed $ ] } - Return [ string ] } diff --git a/Zend/zend_execute.c b/Zend/zend_execute.c index 4fd54df08db3..ed385194fcc3 100644 --- a/Zend/zend_execute.c +++ b/Zend/zend_execute.c @@ -5395,6 +5395,10 @@ static zend_always_inline uint32_t zend_get_arg_offset_by_name( return *(uintptr_t *)(cache_slot + 1); } + if (UNEXPECTED(ZSTR_LEN(arg_name) == 0)) { + return (uint32_t)-1; + } + // TODO: Use a hash table? uint32_t num_args = fbc->common.num_args; if (EXPECTED(fbc->type == ZEND_USER_FUNCTION) diff --git a/Zend/zend_partial.c b/Zend/zend_partial.c index d1b4b02615e0..71a58091f304 100644 --- a/Zend/zend_partial.c +++ b/Zend/zend_partial.c @@ -139,15 +139,14 @@ static zend_always_inline void zend_partial_signature_create(zend_partial *parti /* Placeholders that run into the variadic portion become * required and make all params before them required */ required = num; - // TODO: update arg name + info->name = zend_empty_string; + info->default_value = NULL; if (ZEND_PARTIAL_IS_CALL_TRAMPOLINE(&partial->func)) { - memcpy(info, zend_call_magic_arginfo, sizeof(zend_arg_info)); + info->type = (zend_type){0}; } else { - memcpy(info, - partial->func.common.arg_info + partial->func.common.num_args, - sizeof(zend_arg_info)); + info->type = (partial->func.common.arg_info + partial->func.common.num_args)->type; + ZEND_TYPE_FULL_MASK(info->type) &= ~_ZEND_IS_VARIADIC_BIT; } - 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) { @@ -213,13 +212,15 @@ static zend_always_inline void zend_partial_signature_create(zend_partial *parti if (ZEND_PARTIAL_FUNC_FLAG(&partial->trampoline, ZEND_ACC_HAS_RETURN_TYPE)) { info++; } - while (info < end) { + 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); } - info++; } } From 3ddb387f31a9235d3479653ba12f7185d4836b8f Mon Sep 17 00:00:00 2001 From: Arnaud Le Blanc Date: Wed, 2 Jul 2025 16:11:21 +0200 Subject: [PATCH 11/28] Add invokable test --- Zend/tests/partial_application/invokable.phpt | 47 +++++++++++++++++++ 1 file changed, 47 insertions(+) create mode 100644 Zend/tests/partial_application/invokable.phpt diff --git a/Zend/tests/partial_application/invokable.phpt b/Zend/tests/partial_application/invokable.phpt new file mode 100644 index 000000000000..c158b74d5c64 --- /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) { +} From bfc3e454495ce18f1c875618d702f29524534de7 Mon Sep 17 00:00:00 2001 From: Arnaud Le Blanc Date: Wed, 2 Jul 2025 16:12:25 +0200 Subject: [PATCH 12/28] Move _IS_PLACEHOLDER constants --- Zend/zend_types.h | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/Zend/zend_types.h b/Zend/zend_types.h index 870a54f88965..a9556603dd32 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) @@ -666,10 +670,6 @@ struct _zend_ast_ref { #define ZEND_GUARD_PROTECT_RECURSION(pg, t) *pg |= ZEND_GUARD_RECURSION_TYPE(t) #define ZEND_GUARD_UNPROTECT_RECURSION(pg, t) *pg &= ~ZEND_GUARD_RECURSION_TYPE(t) -/* used for place holders */ -#define _IS_PLACEHOLDER_ARG 20 -#define _IS_PLACEHOLDER_VARIADIC 21 - static zend_always_inline uint8_t zval_get_type(const zval* pz) { return pz->u1.v.type; } From 12bfa650c0209b2b8d1b0478fbfdf5aa5e0601d5 Mon Sep 17 00:00:00 2001 From: Arnaud Le Blanc Date: Wed, 2 Jul 2025 16:39:02 +0200 Subject: [PATCH 13/28] Add return type test --- .../relative_return_types.phpt | 133 ++++++++++++++++++ 1 file changed, 133 insertions(+) create mode 100644 Zend/tests/partial_application/relative_return_types.phpt 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 000000000000..5d5dd49c76ef --- /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 From 22dcd9813edde5bdf0890bc25650ad8b05ac2278 Mon Sep 17 00:00:00 2001 From: Arnaud Le Blanc Date: Wed, 2 Jul 2025 19:38:00 +0200 Subject: [PATCH 14/28] Use the same AST for FCCs and PFAs --- .../compile_errors_006.phpt | 2 +- Zend/zend_ast.c | 77 ++++++++++++++++--- Zend/zend_ast.h | 13 +++- Zend/zend_compile.c | 71 +++++++++-------- Zend/zend_fibers.c | 3 + Zend/zend_language_parser.y | 23 ++---- ext/opcache/zend_file_cache.c | 4 +- ext/opcache/zend_persist.c | 1 + ext/opcache/zend_persist_calc.c | 2 + ext/zend_test/fiber.c | 3 + 10 files changed, 131 insertions(+), 68 deletions(-) diff --git a/Zend/tests/partial_application/compile_errors_006.phpt b/Zend/tests/partial_application/compile_errors_006.phpt index 9c819b95e9df..4657f56207c8 100644 --- a/Zend/tests/partial_application/compile_errors_006.phpt +++ b/Zend/tests/partial_application/compile_errors_006.phpt @@ -5,5 +5,5 @@ Partial application compile errors: mix application with unpack (placeholder aft foo(...["foo" => "bar"], ...); ?> --EXPECTF-- -Fatal error: Cannot use positional argument after argument unpacking %s on line %d +Fatal error: Cannot combine partial application and unpacking in %s on line %d diff --git a/Zend/zend_ast.c b/Zend/zend_ast.c index 16a845a4636f..92efcf90a94a 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; @@ -508,6 +509,41 @@ 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_create_arg_list(zend_ast *arg) { + zend_ast *list = zend_ast_create_list(1, ZEND_AST_ARG_LIST, arg); + + if (arg->kind == ZEND_AST_PLACEHOLDER_ARG + || (arg->kind == ZEND_AST_NAMED_ARG + && arg->child[1]->kind == ZEND_AST_PLACEHOLDER_ARG)) { + zend_ast_fcc *fcc_ast = (zend_ast_fcc*)zend_ast_create_fcc(); + fcc_ast->args = zend_ast_create_list(1, ZEND_AST_ARG_LIST, arg); + return (zend_ast*)fcc_ast; + } + + return 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 (arg->kind == ZEND_AST_PLACEHOLDER_ARG + || (arg->kind == ZEND_AST_NAMED_ARG + && arg->child[1]->kind == ZEND_AST_PLACEHOLDER_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 +1094,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 +1129,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) { @@ -1246,7 +1297,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 +1375,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 +1460,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); } } @@ -2451,9 +2509,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, "::"); @@ -2970,16 +3029,12 @@ zend_ast * ZEND_FASTCALL zend_ast_with_attributes(zend_ast *ast, zend_ast *attr) return ast; } -zend_ast_list * ZEND_FASTCALL zend_ast_call_get_arg_list(zend_ast *ast) +zend_ast * ZEND_FASTCALL zend_ast_call_get_args(zend_ast *ast) { if (ast->kind == ZEND_AST_CALL) { - if (ast->child[1]->kind == ZEND_AST_ARG_LIST) { - return zend_ast_get_list(ast->child[1]); - } + return ast->child[1]; } else if (ast->kind == ZEND_AST_STATIC_CALL || ast->kind == ZEND_AST_METHOD_CALL) { - if (ast->child[2]->kind == ZEND_AST_ARG_LIST) { - return zend_ast_get_list(ast->child[2]); - } + return ast->child[2]; } return NULL; diff --git a/Zend/zend_ast.h b/Zend/zend_ast.h index 344a8f964970..277cde6e3d82 100644 --- a/Zend/zend_ast.h +++ b/Zend/zend_ast.h @@ -231,10 +231,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; @@ -324,6 +326,15 @@ ZEND_API zend_ast *zend_ast_create_list(uint32_t init_children, zend_ast_kind ki ZEND_API zend_ast * ZEND_FASTCALL zend_ast_list_add(zend_ast *list, zend_ast *op); +/* Wraps the list into a ZEND_AST_CALLABLE_CONVERT if arg is a + * ZEND_AST_PLACEHOLDER_ARG. */ +ZEND_API zend_ast * ZEND_FASTCALL zend_ast_create_arg_list(zend_ast *arg); + +/* 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 @@ -427,6 +438,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_list * ZEND_FASTCALL zend_ast_call_get_arg_list(zend_ast *ast); +zend_ast * ZEND_FASTCALL zend_ast_call_get_args(zend_ast *ast); #endif diff --git a/Zend/zend_compile.c b/Zend/zend_compile.c index 9b24355869a1..59f21d4cbdd8 100644 --- a/Zend/zend_compile.c +++ b/Zend/zend_compile.c @@ -3696,8 +3696,8 @@ 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, - bool *is_call_partial) /* {{{ */ + 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; @@ -3712,8 +3712,6 @@ static uint32_t zend_compile_args( bool may_have_undef = 0; /* Whether there may be any extra named arguments collected into a variadic. */ *may_have_extra_named_args = 0; - /* Whether this is a partial call */ - *is_call_partial = false; for (i = 0; i < args->children; ++i) { zend_ast *arg = args->child[i]; @@ -3730,7 +3728,7 @@ static uint32_t zend_compile_args( "Cannot use argument unpacking after named arguments"); } - if (*is_call_partial) { + if (is_call_partial) { zend_error_noreturn(E_COMPILE_ERROR, "Cannot combine partial application and unpacking"); } @@ -3824,7 +3822,6 @@ static uint32_t zend_compile_args( opline->result.var = EX_NUM_TO_VAR(arg_num - 1); } - *is_call_partial = true; continue; } @@ -3938,7 +3935,7 @@ static uint32_t zend_compile_args( } } - if (!*is_call_partial) { + if (!is_call_partial) { if (may_have_undef) { zend_emit_op(NULL, ZEND_CHECK_UNDEF_ARGS, NULL, NULL); } @@ -4010,8 +4007,11 @@ static bool zend_compile_call_common(znode *result, zend_ast *args_ast, zend_fun { 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; @@ -4019,18 +4019,26 @@ 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; - bool is_partial_call; uint32_t arg_count = zend_compile_args(args_ast, fbc, - &may_have_extra_named_args, &is_partial_call); + 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); @@ -6519,11 +6527,13 @@ static zend_ast *zend_partial_apply(zend_ast *callable_ast, zend_ast *pipe_arg) return NULL; } - zend_ast_list *arg_list = zend_ast_call_get_arg_list(callable_ast); - if (!arg_list) { + 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; @@ -6557,10 +6567,7 @@ static zend_ast *zend_partial_apply(zend_ast *callable_ast, zend_ast *pipe_arg) } } - if (first_placeholder == NULL) { - /* Not a PFA */ - return NULL; - } + 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++) { @@ -6601,30 +6608,14 @@ static void zend_compile_pipe(znode *result, zend_ast *ast) /* Turn the operand into a function parameter list. */ zend_ast *arg = zend_ast_create_znode(&wrapped_operand_result); - zend_ast *arg_list_ast = zend_ast_create_list(1, ZEND_AST_ARG_LIST, arg); zend_ast *fcall_ast; znode callable_result; zend_ast *pfa_arg_list_ast = NULL; - /* 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); /* Turn $foo |> PFA into plain function call if possible */ - } else if ((pfa_arg_list_ast = zend_partial_apply(callable_ast, arg))) { + 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, @@ -6644,6 +6635,7 @@ static void zend_compile_pipe(znode *result, zend_ast *ast) } /* 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, @@ -11570,6 +11562,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_fibers.c b/Zend/zend_fibers.c index 97b7cdcc911b..19cd7203b271 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 133b3b3f3e2f..1225337ecd6f 100644 --- a/Zend/zend_language_parser.y +++ b/Zend/zend_language_parser.y @@ -259,7 +259,7 @@ static YYSIZE_T zend_yytnamerr(char*, const char*); %type unprefixed_use_declarations const_decl inner_statement %type expr optional_expr while_statement for_statement foreach_variable %type foreach_statement declare_statement finally_statement unset_variable variable -%type extends_from parameter optional_type_without_static argument argument_or_ellipsis ellipsis_argument global_var +%type extends_from parameter optional_type_without_static argument global_var %type static_var class_statement trait_adaptation trait_precedence trait_alias %type absolute_trait_method_reference trait_method_reference property echo_expr %type new_dereferenceable new_non_dereferenceable anonymous_class class_name class_name_reference simple_variable @@ -904,38 +904,25 @@ return_type: argument_list: '(' ')' { $$ = zend_ast_create_list(0, ZEND_AST_ARG_LIST); } | '(' non_empty_argument_list possible_comma ')' { $$ = $2; } - | '(' T_ELLIPSIS ')' { $$ = zend_ast_create_fcc(); } ; -/* TODO: unify FCC and PFA ASTs. For now we produce a dedicated AST for FCCs, - * which requires to disambiguate '(...)' vs arg lists that include '...' but - * have more than one arg. */ non_empty_argument_list: argument - { $$ = zend_ast_create_list(1, ZEND_AST_ARG_LIST, $1); } - | ellipsis_argument ',' argument_or_ellipsis - { $$ = zend_ast_create_list(1, ZEND_AST_ARG_LIST, $1, $3); } - | non_empty_argument_list ',' argument_or_ellipsis - { $$ = zend_ast_list_add($1, $3); } + { $$ = zend_ast_create_arg_list($1); } + | non_empty_argument_list ',' argument + { $$ = zend_ast_arg_list_add($1, $3); } ; argument: expr { $$ = $1; } | identifier ':' expr { $$ = zend_ast_create(ZEND_AST_NAMED_ARG, $1, $3); } + | 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_or_ellipsis: - argument { $$ = $1; } - | ellipsis_argument { $$ = $1; } -; - -ellipsis_argument: - T_ELLIPSIS { $$ = zend_ast_create_ex(ZEND_AST_PLACEHOLDER_ARG, _IS_PLACEHOLDER_VARIADIC); } - global_var_list: global_var_list ',' global_var { $$ = zend_ast_list_add($1, $3); } | global_var { $$ = zend_ast_create_list(1, ZEND_AST_STMT_LIST, $1); } diff --git a/ext/opcache/zend_file_cache.c b/ext/opcache/zend_file_cache.c index fee90e42b574..8f9fd87bfb7f 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 202cd73c9042..86bd842f30eb 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 639d7d544670..2d79b4dd4e2e 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/zend_test/fiber.c b/ext/zend_test/fiber.c index 199d1b28b8cd..70be0177c2e3 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; From d0d142527272e6eb28c915720b973bdd48794db5 Mon Sep 17 00:00:00 2001 From: Arnaud Le Blanc Date: Wed, 2 Jul 2025 19:40:53 +0200 Subject: [PATCH 15/28] Add closing PHP tag in tests --- Zend/tests/partial_application/rfc_examples_001.phpt | 2 ++ Zend/tests/partial_application/rfc_examples_002.phpt | 2 ++ Zend/tests/partial_application/rfc_examples_003.phpt | 1 + Zend/tests/partial_application/rfc_examples_004.phpt | 1 + Zend/tests/partial_application/rfc_examples_005.phpt | 1 + Zend/tests/partial_application/rfc_examples_006.phpt | 1 + Zend/tests/partial_application/rfc_examples_007.phpt | 1 + Zend/tests/partial_application/variation_call_001.phpt | 5 ++--- 8 files changed, 11 insertions(+), 3 deletions(-) diff --git a/Zend/tests/partial_application/rfc_examples_001.phpt b/Zend/tests/partial_application/rfc_examples_001.phpt index e537c3db23fe..4054af8de386 100644 --- a/Zend/tests/partial_application/rfc_examples_001.phpt +++ b/Zend/tests/partial_application/rfc_examples_001.phpt @@ -132,6 +132,8 @@ foreach ($tests as $test => [$pfa, $closure]) { 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) diff --git a/Zend/tests/partial_application/rfc_examples_002.phpt b/Zend/tests/partial_application/rfc_examples_002.phpt index dd921bde4505..d1071bcd84cf 100644 --- a/Zend/tests/partial_application/rfc_examples_002.phpt +++ b/Zend/tests/partial_application/rfc_examples_002.phpt @@ -87,6 +87,8 @@ foreach ($tests as $test => [$pfa, $closure]) { 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 diff --git a/Zend/tests/partial_application/rfc_examples_003.phpt b/Zend/tests/partial_application/rfc_examples_003.phpt index a925a334a8ae..4ac2a386d40b 100644 --- a/Zend/tests/partial_application/rfc_examples_003.phpt +++ b/Zend/tests/partial_application/rfc_examples_003.phpt @@ -25,6 +25,7 @@ try { 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 diff --git a/Zend/tests/partial_application/rfc_examples_004.phpt b/Zend/tests/partial_application/rfc_examples_004.phpt index 91ef969b53ae..9b767a9f604c 100644 --- a/Zend/tests/partial_application/rfc_examples_004.phpt +++ b/Zend/tests/partial_application/rfc_examples_004.phpt @@ -9,5 +9,6 @@ if (time() > 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 index 461c9b9d5e2a..17a2e30a6922 100644 --- a/Zend/tests/partial_application/rfc_examples_005.phpt +++ b/Zend/tests/partial_application/rfc_examples_005.phpt @@ -9,5 +9,6 @@ if (time() > 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 index 7834b05dacf5..dc20af37f529 100644 --- a/Zend/tests/partial_application/rfc_examples_006.phpt +++ b/Zend/tests/partial_application/rfc_examples_006.phpt @@ -15,6 +15,7 @@ $f = f(?, ?); $f(1, 2); +?> --EXPECT-- 2 int(1) diff --git a/Zend/tests/partial_application/rfc_examples_007.phpt b/Zend/tests/partial_application/rfc_examples_007.phpt index ad03d071fdf2..9bef206caef8 100644 --- a/Zend/tests/partial_application/rfc_examples_007.phpt +++ b/Zend/tests/partial_application/rfc_examples_007.phpt @@ -20,6 +20,7 @@ $partial = speak(?, getArg()); print "Arnaud\n"; $partial('Larry'); +?> --EXPECT-- Arnaud getArg diff --git a/Zend/tests/partial_application/variation_call_001.phpt b/Zend/tests/partial_application/variation_call_001.phpt index 099ca9c50fc0..744e0aed7fd1 100644 --- a/Zend/tests/partial_application/variation_call_001.phpt +++ b/Zend/tests/partial_application/variation_call_001.phpt @@ -11,7 +11,7 @@ class Param { class Foo { public function method($a, $b) { - printf("%s: %s, %s\n", get_called_class(), $a, $b); + printf("%s: %s, %s\n", get_called_class(), $a, $b); } } @@ -24,8 +24,7 @@ $closure = $bar->method(?, new Param); $closure(1); -$closure->call( - new Foo(), 10); +$closure->call(new Foo(), 10); ?> --EXPECT-- Bar: 1, Param From 4646d0e172b5e48bb704efd57a42fe49aa0497f2 Mon Sep 17 00:00:00 2001 From: Arnaud Le Blanc Date: Thu, 3 Jul 2025 10:43:59 +0200 Subject: [PATCH 16/28] Minimal JIT support --- Zend/tests/partial_application/jit_001.phpt | 9 +++++++++ Zend/zend_partial.h | 14 ++++++++++++-- ext/opcache/jit/zend_jit.c | 3 +++ ext/opcache/jit/zend_jit_ir.c | 1 + ext/opcache/jit/zend_jit_vm_helpers.c | 16 +++++++++++++--- 5 files changed, 38 insertions(+), 5 deletions(-) create mode 100644 Zend/tests/partial_application/jit_001.phpt diff --git a/Zend/tests/partial_application/jit_001.phpt b/Zend/tests/partial_application/jit_001.phpt new file mode 100644 index 000000000000..47a5d380d8f9 --- /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/zend_partial.h b/Zend/zend_partial.h index d423e8a7b6c3..a358e750b0a4 100644 --- a/Zend/zend_partial.h +++ b/Zend/zend_partial.h @@ -23,9 +23,8 @@ BEGIN_EXTERN_C() -/* This macro depends on zend_closure structure layout */ #define ZEND_PARTIAL_OBJECT(func) \ - ((zend_object*)((char*)(func) - XtOffsetOf(struct{uint32_t a; zend_function b;}, b) - sizeof(zval) - sizeof(zend_function) - sizeof(zend_object))) + ((zend_object*)((char*)(func) - zend_partial_func_offset())) typedef struct _zend_partial zend_partial; @@ -47,6 +46,17 @@ zend_result zend_partial_init_call(zend_execute_data *call); bool zend_is_partial_function(zend_function *function); +/* 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/ext/opcache/jit/zend_jit.c b/ext/opcache/jit/zend_jit.c index 355178d9d51e..dcb1c07dc436 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_ir.c b/ext/opcache/jit/zend_jit_ir.c index 01a252a15d4f..6893f077f1e4 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" diff --git a/ext/opcache/jit/zend_jit_vm_helpers.c b/ext/opcache/jit/zend_jit_vm_helpers.c index 4348fbd53ad4..bb968593c7b9 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; } From c5b56727be8b002facbfcc22e228e242cb2fda02 Mon Sep 17 00:00:00 2001 From: Arnaud Le Blanc Date: Thu, 3 Jul 2025 11:06:00 +0200 Subject: [PATCH 17/28] $this/scope rebinding validation --- .../partial_application/rebinding_001.phpt | 65 ++++++++++++++ .../partial_application/rebinding_002.phpt | 45 ++++++++++ .../variation_bind_001.phpt | 85 ------------------- .../variation_bind_002.phpt | 78 ----------------- Zend/zend_closures.c | 3 +- 5 files changed, 112 insertions(+), 164 deletions(-) create mode 100644 Zend/tests/partial_application/rebinding_001.phpt create mode 100644 Zend/tests/partial_application/rebinding_002.phpt delete mode 100644 Zend/tests/partial_application/variation_bind_001.phpt delete mode 100644 Zend/tests/partial_application/variation_bind_002.phpt diff --git a/Zend/tests/partial_application/rebinding_001.phpt b/Zend/tests/partial_application/rebinding_001.phpt new file mode 100644 index 000000000000..594188fa405f --- /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 000000000000..61793ffa1a3d --- /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/variation_bind_001.phpt b/Zend/tests/partial_application/variation_bind_001.phpt deleted file mode 100644 index 46939780d3c7..000000000000 --- a/Zend/tests/partial_application/variation_bind_001.phpt +++ /dev/null @@ -1,85 +0,0 @@ ---TEST-- -Partial application variation binding ---FILE-- -method(?, new Param); - -$closure(1); - -var_dump($closure); - -$bound = $closure->bindTo(new Foo); - -$bound(1); - -var_dump($bound); -?> ---EXPECTF-- -Bar: 1, Param -object(Closure)#%d (6) { - ["name"]=> - string(6) "method" - ["file"]=> - string(82) "%svariation_bind_001.php" - ["line"]=> - int(20) - ["this"]=> - object(Bar)#%d (0) { - } - ["parameter"]=> - array(1) { - ["$a"]=> - string(10) "" - } - ["args"]=> - array(2) { - ["a"]=> - NULL - ["b"]=> - object(Param)#%d (0) { - } - } -} -Foo: 1, Param -object(Closure)#%d (6) { - ["name"]=> - string(6) "method" - ["file"]=> - string(82) "%svariation_bind_001.php" - ["line"]=> - int(26) - ["this"]=> - object(Foo)#%d (0) { - } - ["parameter"]=> - array(1) { - ["$a"]=> - string(10) "" - } - ["args"]=> - array(2) { - ["a"]=> - NULL - ["b"]=> - object(Param)#%d (0) { - } - } -} diff --git a/Zend/tests/partial_application/variation_bind_002.phpt b/Zend/tests/partial_application/variation_bind_002.phpt deleted file mode 100644 index cd84ba955f61..000000000000 --- a/Zend/tests/partial_application/variation_bind_002.phpt +++ /dev/null @@ -1,78 +0,0 @@ ---TEST-- -Partial application variation binding static ---FILE-- -bindTo(null, Foo::class); - -$bound(1); - -var_dump($bound); -?> ---EXPECTF-- -Bar: 1, Param -object(Closure)#%d (5) { - ["name"]=> - string(6) "method" - ["file"]=> - string(82) "%svariation_bind_002.php" - ["line"]=> - int(19) - ["parameter"]=> - array(1) { - ["$a"]=> - string(10) "" - } - ["args"]=> - array(2) { - ["a"]=> - NULL - ["b"]=> - object(Param)#%d (0) { - } - } -} -Foo: 1, Param -object(Closure)#%d (5) { - ["name"]=> - string(6) "method" - ["file"]=> - string(82) "%svariation_bind_002.php" - ["line"]=> - int(25) - ["parameter"]=> - array(1) { - ["$a"]=> - string(10) "" - } - ["args"]=> - array(2) { - ["a"]=> - NULL - ["b"]=> - object(Param)#%d (0) { - } - } -} diff --git a/Zend/zend_closures.c b/Zend/zend_closures.c index 59745d6c762e..916fb9a39399 100644 --- a/Zend/zend_closures.c +++ b/Zend/zend_closures.c @@ -84,7 +84,8 @@ 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) { From b977297dbac441ea53043434a540275acd655dc0 Mon Sep 17 00:00:00 2001 From: Arnaud Le Blanc Date: Thu, 3 Jul 2025 12:02:17 +0200 Subject: [PATCH 18/28] Attribute support --- .../partial_application/attributes_001.phpt | 90 +++++++++++++++++++ .../partial_application/attributes_002.phpt | 52 +++++++++++ .../partial_application/attributes_003.phpt | 16 ++++ .../partial_application/attributes_004.phpt | 14 +++ Zend/zend_partial.c | 50 ++++++++++- Zend/zend_vm_def.h | 11 --- Zend/zend_vm_execute.h | 22 ----- 7 files changed, 221 insertions(+), 34 deletions(-) create mode 100644 Zend/tests/partial_application/attributes_001.phpt create mode 100644 Zend/tests/partial_application/attributes_002.phpt create mode 100644 Zend/tests/partial_application/attributes_003.phpt create mode 100644 Zend/tests/partial_application/attributes_004.phpt diff --git a/Zend/tests/partial_application/attributes_001.phpt b/Zend/tests/partial_application/attributes_001.phpt new file mode 100644 index 000000000000..152a57c6aa2c --- /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 000000000000..189d002c63a9 --- /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 000000000000..e907cb65a376 --- /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 000000000000..b887849c2232 --- /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/zend_partial.c b/Zend/zend_partial.c index 71a58091f304..73d6a716557d 100644 --- a/Zend/zend_partial.c +++ b/Zend/zend_partial.c @@ -26,6 +26,7 @@ #include "zend_types.h" #include "zend_vm.h" #include "zend_observer.h" +#include "zend_attributes.h" typedef struct _zend_partial { /* Common zend_closure fields */ @@ -98,6 +99,23 @@ static zend_always_inline uint32_t zend_partial_signature_size(zend_partial *par 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; @@ -113,6 +131,16 @@ static zend_always_inline void zend_partial_signature_create(zend_partial *parti 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]; @@ -132,6 +160,9 @@ static zend_always_inline void zend_partial_signature_create(zend_partial *parti 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)); @@ -154,6 +185,9 @@ static zend_always_inline void zend_partial_signature_create(zend_partial *parti 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)) { @@ -188,6 +222,9 @@ static zend_always_inline void zend_partial_signature_create(zend_partial *parti } byref = true; } + if (UNEXPECTED(attributes)) { + zend_partial_signature_copy_attributes(attributes, partial->func.common.attributes, num + 1, partial->func.common.num_args + 1); + } info++; } @@ -267,7 +304,7 @@ static zend_always_inline void zend_partial_trampoline_create(zend_partial *part 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_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; @@ -500,6 +537,17 @@ static void zend_partial_free(zend_object *object) { 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); } diff --git a/Zend/zend_vm_def.h b/Zend/zend_vm_def.h index 4046f38a3851..449b6d99c8c3 100644 --- a/Zend/zend_vm_def.h +++ b/Zend/zend_vm_def.h @@ -9261,17 +9261,6 @@ ZEND_VM_HANDLER(214, ZEND_CALL_PARTIAL, ANY, ANY, SPEC(OBSERVER)) fbc = call->func; - // TODO: deprecated check - if (UNEXPECTED(!RETURN_VALUE_USED(opline) && (fbc->common.fn_flags & ZEND_ACC_NODISCARD))) { - if ((fbc->common.fn_flags & ZEND_ACC_NODISCARD) && EG(exception) == NULL) { - zend_nodiscard_function(fbc); - } - if (UNEXPECTED(EG(exception) != NULL)) { - UNDEF_RESULT(); - HANDLE_EXCEPTION(); - } - } - 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); diff --git a/Zend/zend_vm_execute.h b/Zend/zend_vm_execute.h index 434a1a4884ac..8147368927d5 100644 --- a/Zend/zend_vm_execute.h +++ b/Zend/zend_vm_execute.h @@ -3837,17 +3837,6 @@ static ZEND_OPCODE_HANDLER_RET ZEND_FASTCALL ZEND_CALL_PARTIAL_SPEC_HANDLER(ZEND fbc = call->func; - // TODO: deprecated check - if (UNEXPECTED(!RETURN_VALUE_USED(opline) && (fbc->common.fn_flags & ZEND_ACC_NODISCARD))) { - if ((fbc->common.fn_flags & ZEND_ACC_NODISCARD) && EG(exception) == NULL) { - zend_nodiscard_function(fbc); - } - if (UNEXPECTED(EG(exception) != NULL)) { - UNDEF_RESULT(); - HANDLE_EXCEPTION(); - } - } - 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); @@ -3958,17 +3947,6 @@ static ZEND_OPCODE_HANDLER_RET ZEND_FASTCALL ZEND_CALL_PARTIAL_SPEC_OBSERVER_HAN fbc = call->func; - // TODO: deprecated check - if (UNEXPECTED(!RETURN_VALUE_USED(opline) && (fbc->common.fn_flags & ZEND_ACC_NODISCARD))) { - if ((fbc->common.fn_flags & ZEND_ACC_NODISCARD) && EG(exception) == NULL) { - zend_nodiscard_function(fbc); - } - if (UNEXPECTED(EG(exception) != NULL)) { - UNDEF_RESULT(); - HANDLE_EXCEPTION(); - } - } - 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); From 1143b349f7f8103427b17c166b94e8ad6785f9b0 Mon Sep 17 00:00:00 2001 From: Arnaud Le Blanc Date: Thu, 3 Jul 2025 14:04:07 +0200 Subject: [PATCH 19/28] Fix stack poisoning --- Zend/zend_vm_def.h | 23 +++++++++++++++++++++-- ext/opcache/jit/zend_jit_helpers.c | 14 ++++++++++++++ ext/opcache/jit/zend_jit_ir.c | 12 ++++++++++++ 3 files changed, 47 insertions(+), 2 deletions(-) diff --git a/Zend/zend_vm_def.h b/Zend/zend_vm_def.h index 449b6d99c8c3..f3eeedf6331d 100644 --- a/Zend/zend_vm_def.h +++ b/Zend/zend_vm_def.h @@ -2981,8 +2981,14 @@ ZEND_VM_HOT_HELPER(zend_leave_helper, ANY, ANY) } 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); @@ -4154,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; } @@ -4290,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; } @@ -4710,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))) { diff --git a/ext/opcache/jit/zend_jit_helpers.c b/ext/opcache/jit/zend_jit_helpers.c index b20afffa47df..ab8756e30b6f 100644 --- a/ext/opcache/jit/zend_jit_helpers.c +++ b/ext/opcache/jit/zend_jit_helpers.c @@ -306,6 +306,20 @@ static zend_execute_data* ZEND_FASTCALL zend_jit_int_extend_stack_helper(uint32_ return call; } +static void ZEND_FASTCALL zend_jit_poison_memory_region_helper(void *addr, size_t size) +{ +#ifdef __SANITIZE_ADDRESS__ + __asan_poison_memory_region(addr, size); +#endif +} + +static void ZEND_FASTCALL zend_jit_unpoison_memory_region_helper(void *addr, size_t size) +{ +#ifdef __SANITIZE_ADDRESS__ + __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 6893f077f1e4..5cbacd87266d 100644 --- a/ext/opcache/jit/zend_jit_ir.c +++ b/ext/opcache/jit/zend_jit_ir.c @@ -3112,6 +3112,8 @@ 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); + REGISTER_HELPER(zend_jit_poison_memory_region_helper); + REGISTER_HELPER(zend_jit_unpoison_memory_region_helper); #ifndef ZTS REGISTER_DATA(EG(current_execute_data)); @@ -8632,6 +8634,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); @@ -11193,6 +11200,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)); From 1132a8b7ac85f0197cd5cd86522bf26363791897 Mon Sep 17 00:00:00 2001 From: Arnaud Le Blanc Date: Thu, 3 Jul 2025 14:18:34 +0200 Subject: [PATCH 20/28] Fix platform-dependent tests --- Zend/tests/partial_application/pipe_optimization_001.phpt | 4 ++-- Zend/tests/partial_application/pipe_optimization_002.phpt | 4 ++-- Zend/tests/partial_application/pipe_optimization_003.phpt | 4 ++-- Zend/tests/partial_application/pipe_optimization_004.phpt | 4 ++-- Zend/tests/partial_application/pipe_optimization_005.phpt | 4 ++-- Zend/tests/partial_application/pipe_optimization_006.phpt | 4 ++-- Zend/tests/partial_application/pipe_optimization_007.phpt | 4 ++-- Zend/tests/partial_application/pipe_optimization_008.phpt | 4 ++-- Zend/tests/partial_application/pipe_optimization_009.phpt | 4 ++-- Zend/tests/partial_application/pipe_optimization_010.phpt | 6 +++--- Zend/tests/partial_application/pipe_optimization_011.phpt | 4 ++-- 11 files changed, 23 insertions(+), 23 deletions(-) diff --git a/Zend/tests/partial_application/pipe_optimization_001.phpt b/Zend/tests/partial_application/pipe_optimization_001.phpt index 2fb2ef14a7aa..ce1956757756 100644 --- a/Zend/tests/partial_application/pipe_optimization_001.phpt +++ b/Zend/tests/partial_application/pipe_optimization_001.phpt @@ -21,7 +21,7 @@ $_main: ; (lines=9, args=0, vars=0, tmps=2) ; (after optimizer) ; %spipe_optimization_001.php:1-12 -0000 INIT_FCALL 0 80 string("time") +0000 INIT_FCALL 0 %d string("time") 0001 V1 = DO_ICALL 0002 T0 = IS_SMALLER int(0) V1 0003 JMPZ T0 0005 @@ -36,7 +36,7 @@ foo: ; (after optimizer) ; %spipe_optimization_001.php:4-6 0000 CV0($a) = RECV 1 -0001 INIT_FCALL 1 96 string("var_dump") +0001 INIT_FCALL 1 %d string("var_dump") 0002 SEND_VAR CV0($a) 1 0003 DO_ICALL 0004 RETURN null diff --git a/Zend/tests/partial_application/pipe_optimization_002.phpt b/Zend/tests/partial_application/pipe_optimization_002.phpt index 2d1d8b6e23eb..a94ebcc260f6 100644 --- a/Zend/tests/partial_application/pipe_optimization_002.phpt +++ b/Zend/tests/partial_application/pipe_optimization_002.phpt @@ -21,7 +21,7 @@ $_main: ; (lines=10, args=0, vars=0, tmps=2) ; (after optimizer) ; %spipe_optimization_002.php:1-12 -0000 INIT_FCALL 0 80 string("time") +0000 INIT_FCALL 0 %d string("time") 0001 V1 = DO_ICALL 0002 T0 = IS_SMALLER int(0) V1 0003 JMPZ T0 0005 @@ -38,7 +38,7 @@ foo: ; %spipe_optimization_002.php:4-6 0000 CV0($a) = RECV 1 0001 CV1($b) = RECV 2 -0002 INIT_FCALL 2 112 string("var_dump") +0002 INIT_FCALL 2 %d string("var_dump") 0003 SEND_VAR CV0($a) 1 0004 SEND_VAR CV1($b) 2 0005 DO_ICALL diff --git a/Zend/tests/partial_application/pipe_optimization_003.phpt b/Zend/tests/partial_application/pipe_optimization_003.phpt index 07b21e9f474b..58cebf25ab5b 100644 --- a/Zend/tests/partial_application/pipe_optimization_003.phpt +++ b/Zend/tests/partial_application/pipe_optimization_003.phpt @@ -21,7 +21,7 @@ $_main: ; (lines=10, args=0, vars=0, tmps=2) ; (after optimizer) ; %spipe_optimization_003.php:1-12 -0000 INIT_FCALL 0 80 string("time") +0000 INIT_FCALL 0 %d string("time") 0001 V1 = DO_ICALL 0002 T0 = IS_SMALLER int(0) V1 0003 JMPZ T0 0005 @@ -38,7 +38,7 @@ foo: ; %spipe_optimization_003.php:4-6 0000 CV0($a) = RECV 1 0001 CV1($b) = RECV 2 -0002 INIT_FCALL 2 112 string("var_dump") +0002 INIT_FCALL 2 %d string("var_dump") 0003 SEND_VAR CV0($a) 1 0004 SEND_VAR CV1($b) 2 0005 DO_ICALL diff --git a/Zend/tests/partial_application/pipe_optimization_004.phpt b/Zend/tests/partial_application/pipe_optimization_004.phpt index f94e5cc32aa5..c20d080824a0 100644 --- a/Zend/tests/partial_application/pipe_optimization_004.phpt +++ b/Zend/tests/partial_application/pipe_optimization_004.phpt @@ -25,7 +25,7 @@ $_main: ; (lines=20, args=0, vars=1, tmps=2) ; (after optimizer) ; %spipe_optimization_004.php:1-16 -0000 INIT_FCALL 0 80 string("time") +0000 INIT_FCALL 0 %d string("time") 0001 V2 = DO_ICALL 0002 T1 = IS_SMALLER int(0) V2 0003 JMPZ T1 0005 @@ -54,7 +54,7 @@ foo: ; %spipe_optimization_004.php:4-6 0000 CV0($a) = RECV 1 0001 CV1($b) = RECV 2 -0002 INIT_FCALL 2 112 string("var_dump") +0002 INIT_FCALL 2 %d string("var_dump") 0003 SEND_VAR CV0($a) 1 0004 SEND_VAR CV1($b) 2 0005 DO_ICALL diff --git a/Zend/tests/partial_application/pipe_optimization_005.phpt b/Zend/tests/partial_application/pipe_optimization_005.phpt index 4f9d0506eeba..1a84e6feaf0a 100644 --- a/Zend/tests/partial_application/pipe_optimization_005.phpt +++ b/Zend/tests/partial_application/pipe_optimization_005.phpt @@ -21,7 +21,7 @@ $_main: ; (lines=10, args=0, vars=0, tmps=2) ; (after optimizer) ; %spipe_optimization_005.php:1-12 -0000 INIT_FCALL 0 80 string("time") +0000 INIT_FCALL 0 %d string("time") 0001 V1 = DO_ICALL 0002 T0 = IS_SMALLER int(0) V1 0003 JMPZ T0 0005 @@ -38,7 +38,7 @@ foo: ; %spipe_optimization_005.php:4-6 0000 CV0($a) = RECV 1 0001 CV1($b) = RECV 2 -0002 INIT_FCALL 2 112 string("var_dump") +0002 INIT_FCALL 2 %d string("var_dump") 0003 SEND_VAR CV0($a) 1 0004 SEND_VAR CV1($b) 2 0005 DO_ICALL diff --git a/Zend/tests/partial_application/pipe_optimization_006.phpt b/Zend/tests/partial_application/pipe_optimization_006.phpt index 7285ec96011a..040033d770b0 100644 --- a/Zend/tests/partial_application/pipe_optimization_006.phpt +++ b/Zend/tests/partial_application/pipe_optimization_006.phpt @@ -21,7 +21,7 @@ $_main: ; (lines=11, args=0, vars=0, tmps=2) ; (after optimizer) ; %spipe_optimization_006.php:1-12 -0000 INIT_FCALL 0 80 string("time") +0000 INIT_FCALL 0 %d string("time") 0001 V1 = DO_ICALL 0002 T0 = IS_SMALLER int(0) V1 0003 JMPZ T0 0005 @@ -40,7 +40,7 @@ foo: 0000 CV0($a) = RECV 1 0001 CV1($b) = RECV_INIT 2 null 0002 CV2($c) = RECV_INIT 3 null -0003 INIT_FCALL 3 128 string("var_dump") +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 diff --git a/Zend/tests/partial_application/pipe_optimization_007.phpt b/Zend/tests/partial_application/pipe_optimization_007.phpt index 5b231c3c2eb6..b696375433b3 100644 --- a/Zend/tests/partial_application/pipe_optimization_007.phpt +++ b/Zend/tests/partial_application/pipe_optimization_007.phpt @@ -25,7 +25,7 @@ $_main: ; (lines=20, args=0, vars=1, tmps=2) ; (after optimizer) ; %spipe_optimization_007.php:1-16 -0000 INIT_FCALL 0 80 string("time") +0000 INIT_FCALL 0 %d string("time") 0001 V2 = DO_ICALL 0002 T1 = IS_SMALLER int(0) V2 0003 JMPZ T1 0005 @@ -54,7 +54,7 @@ foo: ; %spipe_optimization_007.php:4-6 0000 CV0($a) = RECV 1 0001 CV1($b) = RECV 2 -0002 INIT_FCALL 2 112 string("var_dump") +0002 INIT_FCALL 2 %d string("var_dump") 0003 SEND_VAR CV0($a) 1 0004 SEND_VAR CV1($b) 2 0005 DO_ICALL diff --git a/Zend/tests/partial_application/pipe_optimization_008.phpt b/Zend/tests/partial_application/pipe_optimization_008.phpt index 658015832563..3cf0a3e2db18 100644 --- a/Zend/tests/partial_application/pipe_optimization_008.phpt +++ b/Zend/tests/partial_application/pipe_optimization_008.phpt @@ -25,7 +25,7 @@ $_main: ; (lines=20, args=0, vars=1, tmps=2) ; (after optimizer) ; %spipe_optimization_008.php:1-16 -0000 INIT_FCALL 0 80 string("time") +0000 INIT_FCALL 0 %d string("time") 0001 V2 = DO_ICALL 0002 T1 = IS_SMALLER int(0) V2 0003 JMPZ T1 0005 @@ -54,7 +54,7 @@ foo: ; %spipe_optimization_008.php:4-6 0000 CV0($a) = RECV 1 0001 CV1($b) = RECV 2 -0002 INIT_FCALL 2 112 string("var_dump") +0002 INIT_FCALL 2 %d string("var_dump") 0003 SEND_VAR CV0($a) 1 0004 SEND_VAR CV1($b) 2 0005 DO_ICALL diff --git a/Zend/tests/partial_application/pipe_optimization_009.phpt b/Zend/tests/partial_application/pipe_optimization_009.phpt index f3e7449c764c..b2c0cfc963cf 100644 --- a/Zend/tests/partial_application/pipe_optimization_009.phpt +++ b/Zend/tests/partial_application/pipe_optimization_009.phpt @@ -33,7 +33,7 @@ $_main: ; (lines=21, args=0, vars=0, tmps=2) ; (after optimizer) ; %spipe_optimization_009.php:1-24 -0000 INIT_FCALL 0 80 string("time") +0000 INIT_FCALL 0 %d string("time") 0001 V1 = DO_ICALL 0002 T0 = IS_SMALLER int(0) V1 0003 JMPZ T0 0008 @@ -64,7 +64,7 @@ foo: 0000 CV0($a) = RECV 1 0001 CV1($b) = RECV 2 0002 CV2($c) = RECV 3 -0003 INIT_FCALL 3 128 string("var_dump") +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 diff --git a/Zend/tests/partial_application/pipe_optimization_010.phpt b/Zend/tests/partial_application/pipe_optimization_010.phpt index 0dd1f8a23d3e..3836bc633206 100644 --- a/Zend/tests/partial_application/pipe_optimization_010.phpt +++ b/Zend/tests/partial_application/pipe_optimization_010.phpt @@ -23,7 +23,7 @@ $_main: ; (lines=13, args=0, vars=1, tmps=2) ; (after optimizer) ; %spipe_optimization_010.php:1-14 -0000 INIT_FCALL 0 80 string("time") +0000 INIT_FCALL 0 %d string("time") 0001 V2 = DO_ICALL 0002 T1 = IS_SMALLER int(0) V2 0003 JMPZ T1 0005 @@ -32,7 +32,7 @@ $_main: 0006 SEND_VAR_EX CV0($a) 1 0007 SEND_VAL_EX int(1) 2 0008 DO_FCALL_BY_NAME -0009 INIT_FCALL 1 96 string("var_dump") +0009 INIT_FCALL 1 %d string("var_dump") 0010 SEND_VAR CV0($a) 1 0011 DO_ICALL 0012 RETURN int(1) @@ -43,7 +43,7 @@ foo: ; %spipe_optimization_010.php:4-7 0000 CV0($a) = RECV 1 0001 CV1($b) = RECV 2 -0002 INIT_FCALL 2 112 string("var_dump") +0002 INIT_FCALL 2 %d string("var_dump") 0003 SEND_VAR CV0($a) 1 0004 SEND_VAR CV1($b) 2 0005 DO_ICALL diff --git a/Zend/tests/partial_application/pipe_optimization_011.phpt b/Zend/tests/partial_application/pipe_optimization_011.phpt index f655f6161eab..1e1e39f9998e 100644 --- a/Zend/tests/partial_application/pipe_optimization_011.phpt +++ b/Zend/tests/partial_application/pipe_optimization_011.phpt @@ -38,7 +38,7 @@ $_main: ; (lines=20, args=0, vars=1, tmps=2) ; (after optimizer) ; %spipe_optimization_011.php:1-29 -0000 INIT_FCALL 0 80 string("time") +0000 INIT_FCALL 0 %d string("time") 0001 V2 = DO_ICALL 0002 T1 = IS_SMALLER int(0) V2 0003 JMPZ T1 0008 @@ -68,7 +68,7 @@ foo: 0000 CV0($a) = RECV 1 0001 CV1($b) = RECV 2 0002 CV2($c) = RECV 3 -0003 INIT_FCALL 3 128 string("var_dump") +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 From e84faa99a0d152d4ddbec16fd98be5bae58a7467 Mon Sep 17 00:00:00 2001 From: Arnaud Le Blanc Date: Thu, 3 Jul 2025 14:21:40 +0200 Subject: [PATCH 21/28] Fix handling of empty named arg --- Zend/zend_execute.c | 46 ++++++++++++++++++++++----------------------- 1 file changed, 22 insertions(+), 24 deletions(-) diff --git a/Zend/zend_execute.c b/Zend/zend_execute.c index ed385194fcc3..74c55e58375c 100644 --- a/Zend/zend_execute.c +++ b/Zend/zend_execute.c @@ -5395,31 +5395,29 @@ static zend_always_inline uint32_t zend_get_arg_offset_by_name( return *(uintptr_t *)(cache_slot + 1); } - if (UNEXPECTED(ZSTR_LEN(arg_name) == 0)) { - return (uint32_t)-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; + } } } } From 768be12d6e8c63cd186037d2469be586613bf44f Mon Sep 17 00:00:00 2001 From: Arnaud Le Blanc Date: Thu, 3 Jul 2025 14:22:44 +0200 Subject: [PATCH 22/28] Generated file --- Zend/zend_vm_execute.h | 47 +++++++++++++++++++++++++++++++++++++++--- 1 file changed, 44 insertions(+), 3 deletions(-) diff --git a/Zend/zend_vm_execute.h b/Zend/zend_vm_execute.h index 8147368927d5..9ce5dc65ed8a 100644 --- a/Zend/zend_vm_execute.h +++ b/Zend/zend_vm_execute.h @@ -1194,8 +1194,14 @@ static zend_never_inline ZEND_OPCODE_HANDLER_RET ZEND_FASTCALL zend_leave_helper } 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); @@ -1374,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; } @@ -1438,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; } @@ -1503,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; } @@ -1685,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; } @@ -1794,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; } @@ -1905,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; } @@ -2357,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))) { @@ -59124,8 +59159,14 @@ ZEND_API void execute_ex(zend_execute_data *ex) } 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); From f3f1ab485eac0873fbe3f6ab3343474480465907 Mon Sep 17 00:00:00 2001 From: Arnaud Le Blanc Date: Thu, 3 Jul 2025 17:30:55 +0200 Subject: [PATCH 23/28] Fix default value reflection --- .../named_placeholder_001.phpt | 10 +-- .../partial_application/reflection_001.phpt | 18 +++--- Zend/zend_partial.c | 62 ++++++++++++++++++- Zend/zend_partial.h | 9 ++- ext/reflection/php_reflection.c | 12 ++-- 5 files changed, 90 insertions(+), 21 deletions(-) diff --git a/Zend/tests/partial_application/named_placeholder_001.phpt b/Zend/tests/partial_application/named_placeholder_001.phpt index 8da329bc7c5a..f6884997c746 100644 --- a/Zend/tests/partial_application/named_placeholder_001.phpt +++ b/Zend/tests/partial_application/named_placeholder_001.phpt @@ -58,7 +58,7 @@ Partial [ function foo ] { @@ %snamed_placeholder_001.php 11 - 11 - Parameters [1] { - Parameter #0 [ $b ] + Parameter #0 [ $b = 2 ] } } int(1) @@ -69,7 +69,7 @@ Partial [ function foo ] { @@ %snamed_placeholder_001.php 17 - 17 - Parameters [1] { - Parameter #0 [ $b ] + Parameter #0 [ $b = 2 ] } } int(1) @@ -80,7 +80,7 @@ Partial [ function foo ] { @@ %snamed_placeholder_001.php 24 - 24 - Parameters [1] { - Parameter #0 [ $b ] + Parameter #0 [ $b = 2 ] } } int(1) @@ -91,8 +91,8 @@ Partial [ function bar ] { @@ %snamed_placeholder_001.php 34 - 34 - Parameters [3] { - Parameter #0 [ $a ] - Parameter #1 [ $b ] + Parameter #0 [ $a = 1 ] + Parameter #1 [ $b = 2 ] Parameter #2 [ ...$c ] } } diff --git a/Zend/tests/partial_application/reflection_001.phpt b/Zend/tests/partial_application/reflection_001.phpt index cfb48b121a1e..f5dfcfadb330 100644 --- a/Zend/tests/partial_application/reflection_001.phpt +++ b/Zend/tests/partial_application/reflection_001.phpt @@ -23,26 +23,26 @@ Partial [ function foo ] { @@ %sreflection_001.php 6 - 6 - Parameters [3] { - Parameter #0 [ $a ] - Parameter #1 [ $b ] - Parameter #2 [ $c ] + 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 ] - Parameter #1 [ $b ] - Parameter #2 [ $c ] + 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 ] - Parameter #1 [ $b ] - Parameter #2 [ $c ] + Parameter #0 [ $a = 1 ] + Parameter #1 [ $b = 5 ] + Parameter #2 [ $c = 10 ] } } diff --git a/Zend/zend_partial.c b/Zend/zend_partial.c index 73d6a716557d..eb9b07766b66 100644 --- a/Zend/zend_partial.c +++ b/Zend/zend_partial.c @@ -264,7 +264,7 @@ static zend_always_inline void zend_partial_signature_create(zend_partial *parti partial->trampoline.common.prototype = &partial->func; } -bool zend_is_partial_function(zend_function *function) { +bool zend_is_partial_trampoline(zend_function *function) { if (!(function->common.fn_flags & ZEND_ACC_CLOSURE)) { return false; } @@ -487,6 +487,66 @@ zend_function *zend_partial_get_trampoline(zend_object *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; diff --git a/Zend/zend_partial.h b/Zend/zend_partial.h index a358e750b0a4..5feaab2fef46 100644 --- a/Zend/zend_partial.h +++ b/Zend/zend_partial.h @@ -26,6 +26,9 @@ 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); @@ -42,9 +45,11 @@ void zend_partial_args_check(zend_execute_data *call); zend_function *zend_partial_get_trampoline(zend_object *object); -zend_result zend_partial_init_call(zend_execute_data *call); +bool zend_is_partial_trampoline(zend_function *function); -bool zend_is_partial_function(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) { diff --git a/ext/reflection/php_reflection.c b/ext/reflection/php_reflection.c index bc90bb7e00ae..454a1482dec4 100644 --- a/ext/reflection/php_reflection.c +++ b/ext/reflection/php_reflection.c @@ -694,8 +694,6 @@ static void _enum_case_string(smart_str *str, const zend_string *name, zend_clas static zend_op *get_recv_op(const zend_op_array *op_array, uint32_t offset) { - // TODO: partial - zend_op *op = op_array->opcodes; const zend_op *end = op + op_array->last; @@ -816,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) { @@ -903,7 +907,7 @@ static void _function_string(smart_str *str, zend_function *fptr, zend_class_ent smart_str_appendl(str, indent, strlen(indent)); const char *prefix = "Function [ "; if (fptr->common.fn_flags & (ZEND_ACC_CLOSURE|ZEND_ACC_FAKE_CLOSURE)) { - if (zend_is_partial_function(fptr)) { + if (zend_is_partial_trampoline(fptr)) { prefix = "Partial [ "; } else { prefix = "Closure [ "; From 1df5c8578c253e1ad502d969c4c262b24c09109b Mon Sep 17 00:00:00 2001 From: Arnaud Le Blanc Date: Thu, 3 Jul 2025 17:32:39 +0200 Subject: [PATCH 24/28] Remove dead code --- ext/reflection/php_reflection.c | 31 +------------------------------ 1 file changed, 1 insertion(+), 30 deletions(-) diff --git a/ext/reflection/php_reflection.c b/ext/reflection/php_reflection.c index 454a1482dec4..1f7ba952bf1d 100644 --- a/ext/reflection/php_reflection.c +++ b/ext/reflection/php_reflection.c @@ -1455,31 +1455,6 @@ static void reflection_extension_factory(zval *object, const char *name_str) } /* }}} */ -static zend_always_inline uint32_t reflection_parameter_partial_offset(zend_function *fptr, zend_arg_info *info) { - zend_arg_info *arg = fptr->common.prototype->common.arg_info, - *end = arg + fptr->common.prototype->common.num_args; - uint32_t offset = 0; - - if (fptr->type == ZEND_USER_FUNCTION && - fptr->op_array.opcodes == &EG(call_trampoline_op)) { - return 0; - } - - if (fptr->common.fn_flags & ZEND_ACC_VARIADIC) { - end++; - } - - while (arg < end) { - if (zend_string_equals_ci(info->name, arg->name)) { - return offset; - } - offset++; - arg++; - } - ZEND_ASSERT(0); - return -1; -} - /* {{{ reflection_parameter_factory */ static void reflection_parameter_factory(zend_function *fptr, zval *closure_object, struct _zend_arg_info *arg_info, uint32_t offset, bool required, zval *object) { @@ -1491,11 +1466,7 @@ static void reflection_parameter_factory(zend_function *fptr, zval *closure_obje intern = Z_REFLECTION_P(object); reference = (parameter_reference*) emalloc(sizeof(parameter_reference)); reference->arg_info = arg_info; - if (0 /* TODO: fptr->common.fn_flags & ZEND_ACC_PARTIAL*/) { - reference->offset = reflection_parameter_partial_offset(fptr, arg_info); - } else { - reference->offset = offset; - } + reference->offset = offset; reference->required = required; reference->fptr = fptr; intern->ptr = reference; From 13fa9fbbf6d155e6329a01e7f4f0adb4f6cc07be Mon Sep 17 00:00:00 2001 From: Arnaud Le Blanc Date: Thu, 3 Jul 2025 17:37:32 +0200 Subject: [PATCH 25/28] Fix ReflectionFunction::isPartial() --- .../partial_application/reflection_004.phpt | 13 +++++ .../partial_application/reflection_005.phpt | 16 ++++++ ext/reflection/php_reflection.c | 7 +-- ext/reflection/php_reflection.stub.php | 2 + ext/reflection/php_reflection_arginfo.h | 56 ++++++++++--------- 5 files changed, 64 insertions(+), 30 deletions(-) create mode 100644 Zend/tests/partial_application/reflection_004.phpt create mode 100644 Zend/tests/partial_application/reflection_005.phpt diff --git a/Zend/tests/partial_application/reflection_004.phpt b/Zend/tests/partial_application/reflection_004.phpt new file mode 100644 index 000000000000..bbb810f16ae4 --- /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 000000000000..f380ffab722c --- /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/ext/reflection/php_reflection.c b/ext/reflection/php_reflection.c index 1f7ba952bf1d..0bd6a1696f65 100644 --- a/ext/reflection/php_reflection.c +++ b/ext/reflection/php_reflection.c @@ -1826,7 +1826,7 @@ ZEND_METHOD(ReflectionFunctionAbstract, isClosure) ZEND_PARSE_PARAMETERS_NONE(); GET_REFLECTION_OBJECT_PTR(fptr); - RETURN_BOOL(fptr->common.fn_flags & ZEND_ACC_CLOSURE/*TODO |ZEND_ACC_PARTIAL*/); + RETURN_BOOL(fptr->common.fn_flags & ZEND_ACC_CLOSURE); } /* }}} */ @@ -1840,9 +1840,8 @@ ZEND_METHOD(ReflectionFunctionAbstract, isPartial) RETURN_THROWS(); } GET_REFLECTION_OBJECT_PTR(fptr); - RETURN_BOOL(0); - (void)fptr; - // TODO RETURN_BOOL(fptr->common.fn_flags & ZEND_ACC_PARTIAL); + + RETURN_BOOL(zend_is_partial_trampoline(fptr)); } /* }}} */ diff --git a/ext/reflection/php_reflection.stub.php b/ext/reflection/php_reflection.stub.php index 63518b446ad8..3f4618f68ebf 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 d50dc04ae3d1..0d6b118145f5 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) From 8cd83ebbe79622ff30791516cbfda6a31a519251 Mon Sep 17 00:00:00 2001 From: Arnaud Le Blanc Date: Thu, 3 Jul 2025 17:43:53 +0200 Subject: [PATCH 26/28] Cleanup --- Zend/zend_compile.c | 18 +++++++----------- Zend/zend_language_parser.y | 15 ++++++++++----- 2 files changed, 17 insertions(+), 16 deletions(-) diff --git a/Zend/zend_compile.c b/Zend/zend_compile.c index 59f21d4cbdd8..e71472cd80f7 100644 --- a/Zend/zend_compile.c +++ b/Zend/zend_compile.c @@ -3983,20 +3983,17 @@ 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]; - znode op1; opline->extended_value = arg_count; - if (opline->opcode == ZEND_NEW) { - zend_error_noreturn(E_COMPILE_ERROR, "Cannot create partial application for new expression"); - } + 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, - (opline->opcode == ZEND_NEW) ? &op1 : NULL, NULL); + NULL, NULL); if (may_have_extra_named_args) { opline->extended_value = ZEND_FCALL_MAY_HAVE_EXTRA_NAMED_PARAMS; @@ -4120,14 +4117,13 @@ static void zend_compile_dynamic_call(znode *result, znode *name_node, zend_ast } /* }}} */ -static inline bool zend_args_contain_unpack_or_named_or_partial(zend_ast_list *args) /* {{{ */ +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 || - arg->kind == ZEND_AST_PLACEHOLDER_ARG) { + arg->kind == ZEND_AST_NAMED_ARG) { return 1; } } @@ -4374,7 +4370,7 @@ static zend_result zend_compile_func_cufa(znode *result, zend_ast_list *args, ze zend_string *name = zend_resolve_function_name(orig_name, args->child[1]->child[0]->attr, &is_fully_qualified); if (zend_string_equals_literal_ci(name, "array_slice") - && !zend_args_contain_unpack_or_named_or_partial(list) + && !zend_args_contain_unpack_or_named(list) && list->children == 3 && list->child[1]->kind == ZEND_AST_ZVAL) { zval *zv = zend_ast_get_zval(list->child[1]); @@ -4805,7 +4801,7 @@ static void zend_compile_ns_call(znode *result, znode *name_node, zend_ast *args /* Find frameless function with same name. */ zend_function *frameless_function = NULL; if (args_ast->kind != ZEND_AST_CALLABLE_CONVERT - && !zend_args_contain_unpack_or_named_or_partial(zend_ast_get_list(args_ast)) + && !zend_args_contain_unpack_or_named(zend_ast_get_list(args_ast)) /* Avoid blowing up op count with nested frameless branches. */ && !CG(context).in_jmp_frameless_branch) { zend_string *lc_func_name = Z_STR_P(CT_CONSTANT_EX(CG(active_op_array), name_constants + 2)); @@ -5113,7 +5109,7 @@ static zend_result zend_try_compile_special_func(znode *result, zend_string *lcn return FAILURE; } - if (zend_args_contain_unpack_or_named_or_partial(args)) { + if (zend_args_contain_unpack_or_named(args)) { return FAILURE; } diff --git a/Zend/zend_language_parser.y b/Zend/zend_language_parser.y index 1225337ecd6f..2528c662bd17 100644 --- a/Zend/zend_language_parser.y +++ b/Zend/zend_language_parser.y @@ -914,13 +914,18 @@ non_empty_argument_list: ; argument: - expr { $$ = $1; } + expr + { $$ = $1; } | identifier ':' expr { $$ = zend_ast_create(ZEND_AST_NAMED_ARG, $1, $3); } - | 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); } + | 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); } ; global_var_list: From 00b53fb2682f6fe38eee0a50c98d84eba3f99ffd Mon Sep 17 00:00:00 2001 From: Arnaud Le Blanc Date: Thu, 3 Jul 2025 17:45:39 +0200 Subject: [PATCH 27/28] Fix build --- ext/opcache/jit/zend_jit_helpers.c | 6 ++---- ext/opcache/jit/zend_jit_ir.c | 2 ++ 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/ext/opcache/jit/zend_jit_helpers.c b/ext/opcache/jit/zend_jit_helpers.c index ab8756e30b6f..f85bd1ef1b78 100644 --- a/ext/opcache/jit/zend_jit_helpers.c +++ b/ext/opcache/jit/zend_jit_helpers.c @@ -306,19 +306,17 @@ 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) { -#ifdef __SANITIZE_ADDRESS__ __asan_poison_memory_region(addr, size); -#endif } static void ZEND_FASTCALL zend_jit_unpoison_memory_region_helper(void *addr, size_t size) { -#ifdef __SANITIZE_ADDRESS__ __asan_unpoison_memory_region(addr, size); -#endif } +#endif static zval* ZEND_FASTCALL zend_jit_symtable_find(HashTable *ht, zend_string *str) { diff --git a/ext/opcache/jit/zend_jit_ir.c b/ext/opcache/jit/zend_jit_ir.c index 5cbacd87266d..336cb02b74a3 100644 --- a/ext/opcache/jit/zend_jit_ir.c +++ b/ext/opcache/jit/zend_jit_ir.c @@ -3112,8 +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)); From ef69e82d9112ec0c4cc28d944a99903f95b6117e Mon Sep 17 00:00:00 2001 From: Arnaud Le Blanc Date: Thu, 3 Jul 2025 18:57:40 +0200 Subject: [PATCH 28/28] Handle clone syntax --- Zend/zend_ast.c | 89 ++++++++++++++++++++++++++++++------- Zend/zend_ast.h | 11 +++-- Zend/zend_language_parser.y | 15 +++---- 3 files changed, 86 insertions(+), 29 deletions(-) diff --git a/Zend/zend_ast.c b/Zend/zend_ast.c index 9a8873466bac..1917f3c87beb 100644 --- a/Zend/zend_ast.c +++ b/Zend/zend_ast.c @@ -158,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; @@ -401,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; @@ -480,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) { @@ -509,20 +580,6 @@ 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_create_arg_list(zend_ast *arg) { - zend_ast *list = zend_ast_create_list(1, ZEND_AST_ARG_LIST, arg); - - if (arg->kind == ZEND_AST_PLACEHOLDER_ARG - || (arg->kind == ZEND_AST_NAMED_ARG - && arg->child[1]->kind == ZEND_AST_PLACEHOLDER_ARG)) { - zend_ast_fcc *fcc_ast = (zend_ast_fcc*)zend_ast_create_fcc(); - fcc_ast->args = zend_ast_create_list(1, ZEND_AST_ARG_LIST, arg); - return (zend_ast*)fcc_ast; - } - - return 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) { @@ -533,9 +590,7 @@ ZEND_API zend_ast * ZEND_FASTCALL zend_ast_arg_list_add(zend_ast *list, zend_ast ZEND_ASSERT(list->kind == ZEND_AST_ARG_LIST); - if (arg->kind == ZEND_AST_PLACEHOLDER_ARG - || (arg->kind == ZEND_AST_NAMED_ARG - && arg->child[1]->kind == ZEND_AST_PLACEHOLDER_ARG)) { + 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; diff --git a/Zend/zend_ast.h b/Zend/zend_ast.h index 401bcaddfb71..5483eb7b88a3 100644 --- a/Zend/zend_ast.h +++ b/Zend/zend_ast.h @@ -310,25 +310,28 @@ 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); -/* Wraps the list into a ZEND_AST_CALLABLE_CONVERT if arg is a - * ZEND_AST_PLACEHOLDER_ARG. */ -ZEND_API zend_ast * ZEND_FASTCALL zend_ast_create_arg_list(zend_ast *arg); - /* 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. */ diff --git a/Zend/zend_language_parser.y b/Zend/zend_language_parser.y index 824129c54b9d..69d445005350 100644 --- a/Zend/zend_language_parser.y +++ b/Zend/zend_language_parser.y @@ -902,13 +902,13 @@ 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; } ; non_empty_argument_list: argument - { $$ = zend_ast_create_arg_list($1); } + { $$ = zend_ast_create_arg_list(1, ZEND_AST_ARG_LIST, $1); } | non_empty_argument_list ',' argument { $$ = zend_ast_arg_list_add($1, $3); } ; @@ -923,19 +923,18 @@ 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: