From 5efd54cb1534d5a195039f314512038cdd11314b Mon Sep 17 00:00:00 2001 From: Arnaud Le Blanc Date: Tue, 5 Aug 2025 10:14:06 +0200 Subject: [PATCH 01/15] Partial application RFC: https://wiki.php.net/rfc/partial_function_application_v2 Co-authored-by: Joe Watkins --- Zend/Optimizer/compact_literals.c | 5 + Zend/Optimizer/optimize_func_calls.c | 6 +- Zend/Optimizer/zend_call_graph.c | 6 + Zend/Optimizer/zend_inference.c | 1 + .../first_class_callable_non_unary_error.phpt | 10 - ...rst_class_callable_non_variadic_error.phpt | 10 - Zend/tests/partial_application/assert.phpt | 35 + .../partial_application/attributes_001.phpt | 88 ++ .../partial_application/attributes_002.phpt | 19 + .../partial_application/attributes_003.phpt | 16 + Zend/tests/partial_application/clone.phpt | 50 + .../compile_errors_001.phpt | 8 + .../compile_errors_002.phpt | 8 + .../compile_errors_003.phpt | 8 + .../compile_errors_004.phpt | 8 + .../compile_errors_005.phpt | 8 + .../compile_errors_006.phpt | 8 + .../tests/partial_application/errors_001.phpt | 62 + .../tests/partial_application/errors_002.phpt | 15 + .../tests/partial_application/errors_003.phpt | 77 ++ .../tests/partial_application/errors_004.phpt | 16 + .../tests/partial_application/errors_005.phpt | 15 + .../tests/partial_application/errors_006.phpt | 23 + .../tests/partial_application/export_001.phpt | 14 + .../partial_application/extra_named.phpt | 49 + .../partial_application/function_name.phpt | 19 + Zend/tests/partial_application/fuzz_001.phpt | 21 + 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/hook.phpt | 25 + Zend/tests/partial_application/invokable.phpt | 51 + Zend/tests/partial_application/jit_001.phpt | 9 + Zend/tests/partial_application/magic_001.phpt | 82 ++ Zend/tests/partial_application/magic_002.phpt | 65 + Zend/tests/partial_application/magic_003.phpt | 13 + Zend/tests/partial_application/magic_004.phpt | 13 + Zend/tests/partial_application/magic_005.phpt | 30 + Zend/tests/partial_application/magic_006.phpt | 20 + .../named_placeholders.phpt | 117 ++ .../non_dynamic_call_funcs.phpt | 46 + Zend/tests/partial_application/observers.phpt | 34 + .../partial_application/param_reorder.phpt | 202 +++ .../pipe_optimization_001.phpt | 47 + .../pipe_optimization_002.phpt | 51 + .../pipe_optimization_003.phpt | 51 + .../pipe_optimization_004.phpt | 85 ++ .../pipe_optimization_005.phpt | 51 + .../pipe_optimization_006.phpt | 55 + .../pipe_optimization_007.phpt | 85 ++ .../pipe_optimization_008.phpt | 99 ++ .../pipe_optimization_009.phpt | 103 ++ .../pipe_optimization_010.phpt | 58 + .../pipe_optimization_011.phpt | 110 ++ Zend/tests/partial_application/preloading.inc | 13 + .../tests/partial_application/preloading.phpt | 15 + .../partial_application/rebinding_001.phpt | 63 + .../partial_application/rebinding_002.phpt | 45 + .../partial_application/rebinding_003.phpt | 65 + .../recorded_warnings.phpt | 14 + .../partial_application/references_001.phpt | 24 + .../partial_application/references_002.phpt | 24 + .../partial_application/references_003.phpt | 20 + .../partial_application/references_004.phpt | 42 + .../partial_application/references_005.phpt | 26 + .../partial_application/reflection_001.phpt | 48 + .../partial_application/reflection_002.phpt | 57 + .../partial_application/reflection_003.phpt | 43 + .../partial_application/reflection_004.phpt | 16 + .../partial_application/reflection_005.phpt | 16 + .../relative_return_types.phpt | 133 ++ .../partial_application/return_type.phpt | 20 + .../partial_application/rfc_examples.inc | 75 ++ .../rfc_examples_const_expr.phpt | 25 + .../rfc_examples_debug.phpt | 25 + .../rfc_examples_errors.phpt | 34 + .../rfc_examples_eval_order.phpt | 30 + .../rfc_examples_extra_args.phpt | 54 + .../rfc_examples_incompatible_functions.phpt | 14 + .../rfc_examples_magic_methods.phpt | 47 + .../rfc_examples_overview.phpt | 44 + .../rfc_examples_scoping.phpt | 113 ++ .../rfc_examples_semantics.phpt | 30 + .../rfc_examples_semantics_examples.phpt | 217 ++++ .../static_method_001.phpt | 18 + .../partial_application/statics_001.phpt | 22 + .../partial_application/statics_002.phpt | 23 + .../partial_application/statics_003.phpt | 26 + .../superfluous_args_are_forwarded.phpt | 44 + Zend/tests/partial_application/this.phpt | 20 + .../variation_call_001.phpt | 31 + .../variation_closure_001.phpt | 23 + .../variation_closure_002.phpt | 28 + .../variation_closure_003.phpt | 46 + .../variation_debug_001.phpt | 40 + .../variation_debug_002.phpt | 49 + .../partial_application/variation_ex_001.phpt | 14 + .../partial_application/variation_gc_001.phpt | 19 + .../partial_application/variation_gc_002.phpt | 11 + .../partial_application/variation_gc_003.phpt | 15 + .../variation_invoke_001.phpt | 21 + .../variation_nocall_001.phpt | 12 + .../variation_nocall_002.phpt | 30 + .../variation_parent_001.phpt | 75 ++ .../variation_scope_001.phpt | 18 + .../variation_strict_001.phpt | 20 + .../variation_variadics_001.phpt | 31 + .../variation_variadics_002.phpt | 25 + .../variation_variadics_004.phpt | 65 + .../variation_variadics_006.phpt | 17 + .../variation_variadics_007.phpt | 14 + .../variation_variadics_008.phpt | 16 + Zend/zend_ast.c | 10 + Zend/zend_ast.h | 1 + Zend/zend_closures.c | 72 +- Zend/zend_closures.h | 1 + Zend/zend_compile.c | 286 +++- Zend/zend_compile.h | 4 +- Zend/zend_execute.c | 17 +- Zend/zend_execute.h | 3 + Zend/zend_execute_API.c | 4 + Zend/zend_globals.h | 1 + Zend/zend_language_scanner.l | 83 +- Zend/zend_object_handlers.c | 3 +- Zend/zend_partial.c | 1146 +++++++++++++++++ Zend/zend_partial.h | 34 + Zend/zend_string.h | 8 + Zend/zend_types.h | 2 + Zend/zend_vm_def.h | 63 + Zend/zend_vm_execute.h | 488 +++++-- Zend/zend_vm_handlers.h | 942 +++++++------- Zend/zend_vm_opcodes.c | 8 +- Zend/zend_vm_opcodes.h | 4 +- configure.ac | 1 + ext/opcache/ZendAccelerator.c | 216 ++++ ext/opcache/ZendAccelerator.h | 10 + ext/opcache/jit/zend_jit.c | 3 + ext/opcache/jit/zend_jit_ir.c | 2 +- ext/opcache/jit/zend_jit_vm_helpers.c | 3 +- win32/build/config.w32 | 2 +- 143 files changed, 7121 insertions(+), 671 deletions(-) delete mode 100644 Zend/tests/first_class_callable/first_class_callable_non_unary_error.phpt delete mode 100644 Zend/tests/first_class_callable/first_class_callable_non_variadic_error.phpt create mode 100644 Zend/tests/partial_application/assert.phpt 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/clone.phpt 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/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/errors_004.phpt create mode 100644 Zend/tests/partial_application/errors_005.phpt create mode 100644 Zend/tests/partial_application/errors_006.phpt create mode 100644 Zend/tests/partial_application/export_001.phpt create mode 100644 Zend/tests/partial_application/extra_named.phpt create mode 100644 Zend/tests/partial_application/function_name.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/hook.phpt create mode 100644 Zend/tests/partial_application/invokable.phpt create mode 100644 Zend/tests/partial_application/jit_001.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/magic_006.phpt create mode 100644 Zend/tests/partial_application/named_placeholders.phpt create mode 100644 Zend/tests/partial_application/non_dynamic_call_funcs.phpt create mode 100644 Zend/tests/partial_application/observers.phpt create mode 100644 Zend/tests/partial_application/param_reorder.phpt 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 create mode 100644 Zend/tests/partial_application/preloading.inc create mode 100644 Zend/tests/partial_application/preloading.phpt create mode 100644 Zend/tests/partial_application/rebinding_001.phpt create mode 100644 Zend/tests/partial_application/rebinding_002.phpt create mode 100644 Zend/tests/partial_application/rebinding_003.phpt create mode 100644 Zend/tests/partial_application/recorded_warnings.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 create mode 100644 Zend/tests/partial_application/references_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/reflection_004.phpt create mode 100644 Zend/tests/partial_application/reflection_005.phpt create mode 100644 Zend/tests/partial_application/relative_return_types.phpt create mode 100644 Zend/tests/partial_application/return_type.phpt create mode 100644 Zend/tests/partial_application/rfc_examples.inc create mode 100644 Zend/tests/partial_application/rfc_examples_const_expr.phpt create mode 100644 Zend/tests/partial_application/rfc_examples_debug.phpt create mode 100644 Zend/tests/partial_application/rfc_examples_errors.phpt create mode 100644 Zend/tests/partial_application/rfc_examples_eval_order.phpt create mode 100644 Zend/tests/partial_application/rfc_examples_extra_args.phpt create mode 100644 Zend/tests/partial_application/rfc_examples_incompatible_functions.phpt create mode 100644 Zend/tests/partial_application/rfc_examples_magic_methods.phpt create mode 100644 Zend/tests/partial_application/rfc_examples_overview.phpt create mode 100644 Zend/tests/partial_application/rfc_examples_scoping.phpt create mode 100644 Zend/tests/partial_application/rfc_examples_semantics.phpt create mode 100644 Zend/tests/partial_application/rfc_examples_semantics_examples.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/statics_002.phpt create mode 100644 Zend/tests/partial_application/statics_003.phpt create mode 100644 Zend/tests/partial_application/superfluous_args_are_forwarded.phpt create mode 100644 Zend/tests/partial_application/this.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_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_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_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/compact_literals.c b/Zend/Optimizer/compact_literals.c index 447a034530e1b..c534a5ecd8a45 100644 --- a/Zend/Optimizer/compact_literals.c +++ b/Zend/Optimizer/compact_literals.c @@ -735,6 +735,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; @@ -747,6 +748,10 @@ void zend_optimizer_compact_literals(zend_op_array *op_array, zend_optimizer_ctx cache_size += sizeof(void *); } break; + case ZEND_CALLABLE_CONVERT_PARTIAL: + opline->op1.num = cache_size; + cache_size += 2 * sizeof(void *); + break; } opline++; } diff --git a/Zend/Optimizer/optimize_func_calls.c b/Zend/Optimizer/optimize_func_calls.c index 62b50464e87ba..522a8927acc7f 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 645edd2f99914..4e414a95172fe 100644 --- a/Zend/Optimizer/zend_call_graph.c +++ b/Zend/Optimizer/zend_call_graph.c @@ -128,6 +128,7 @@ ZEND_API void zend_analyze_calls(zend_arena **arena, zend_script *script, uint32 case ZEND_DO_UCALL: case ZEND_DO_FCALL_BY_NAME: case ZEND_CALLABLE_CONVERT: + case ZEND_CALLABLE_CONVERT_PARTIAL: func_info->flags |= ZEND_FUNC_HAS_CALLS; if (call_info) { call_info->caller_call_opline = opline; @@ -144,11 +145,16 @@ 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 = true; break; } + if (opline->opcode == ZEND_SEND_PLACEHOLDER + && opline->op1.num == ZEND_PLACEHOLDER_VARIADIC) { + break; + } uint32_t num = opline->op2.num; if (num > 0) { diff --git a/Zend/Optimizer/zend_inference.c b/Zend/Optimizer/zend_inference.c index 54670c804d006..5aa001507d07f 100644 --- a/Zend/Optimizer/zend_inference.c +++ b/Zend/Optimizer/zend_inference.c @@ -3906,6 +3906,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/first_class_callable/first_class_callable_non_unary_error.phpt b/Zend/tests/first_class_callable/first_class_callable_non_unary_error.phpt deleted file mode 100644 index 74e36a9ad0df5..0000000000000 --- a/Zend/tests/first_class_callable/first_class_callable_non_unary_error.phpt +++ /dev/null @@ -1,10 +0,0 @@ ---TEST-- -First class callable error: more than one argument ---FILE-- - ---EXPECTF-- -Fatal error: Cannot create a Closure for call expression with more than one argument, or non-variadic placeholders in %s on line %d diff --git a/Zend/tests/first_class_callable/first_class_callable_non_variadic_error.phpt b/Zend/tests/first_class_callable/first_class_callable_non_variadic_error.phpt deleted file mode 100644 index efbd13b7593b6..0000000000000 --- a/Zend/tests/first_class_callable/first_class_callable_non_variadic_error.phpt +++ /dev/null @@ -1,10 +0,0 @@ ---TEST-- -First class callable error: non-variadic placeholder ---FILE-- - ---EXPECTF-- -Fatal error: Cannot create a Closure for call expression with more than one argument, or non-variadic placeholders in %s on line %d diff --git a/Zend/tests/partial_application/assert.phpt b/Zend/tests/partial_application/assert.phpt new file mode 100644 index 0000000000000..fe36e687f8d58 --- /dev/null +++ b/Zend/tests/partial_application/assert.phpt @@ -0,0 +1,35 @@ +--TEST-- +PFA of assert() behaves like a dynamic call to assert() +--FILE-- +getMessage(), "\n"; +} + +try { + echo "# Dynamic call:\n"; + (function ($f) { $f(false); })('assert'); +} catch (Error $e) { + echo $e::class, ": ", $e->getMessage() ?: '(no message)', "\n"; +} + +try { + echo "# PFA call:\n"; + $f = assert(?); + $f(false); +} catch (Error $e) { + echo $e::class, ": ", $e->getMessage() ?: '(no message)', "\n"; +} + +?> +--EXPECT-- +# Static call: +AssertionError: assert(false) +# Dynamic call: +AssertionError: (no message) +# PFA call: +AssertionError: (no message) diff --git a/Zend/tests/partial_application/attributes_001.phpt b/Zend/tests/partial_application/attributes_001.phpt new file mode 100644 index 0000000000000..827ad41321ec6 --- /dev/null +++ b/Zend/tests/partial_application/attributes_001.phpt @@ -0,0 +1,88 @@ +--TEST-- +PFA inherits NoDiscard and SensitiveParameter attributes +--FILE-- +getAttributes()); + + foreach ($r->getParameters() as $i => $p) { + echo "Parameter $i:\n"; + var_dump($p->getAttributes()); + } +} + +echo "# Orig attributes:\n"; + +dump_attributes('f'); + +$f = f(1, ?, ?, ...); + +echo "# PFA attributes:\n"; + +dump_attributes($f); + +?> +--EXPECTF-- +# Orig attributes: +array(2) { + [0]=> + object(ReflectionAttribute)#%d (1) { + ["name"]=> + string(9) "NoDiscard" + } + [1]=> + object(ReflectionAttribute)#%d (1) { + ["name"]=> + string(4) "Test" + } +} +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" + } +} +# PFA 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(0) { +} +Parameter 2: +array(0) { +} diff --git a/Zend/tests/partial_application/attributes_002.phpt b/Zend/tests/partial_application/attributes_002.phpt new file mode 100644 index 0000000000000..be1f1612f9389 --- /dev/null +++ b/Zend/tests/partial_application/attributes_002.phpt @@ -0,0 +1,19 @@ +--TEST-- +PFA preserves #[SensitiveParameter] +--FILE-- + +--EXPECTF-- +Fatal error: Uncaught Exception in %s:%d +Stack trace: +#0 %s(%d): f(1, Object(SensitiveParameterValue), 3, Object(SensitiveParameterValue), Object(SensitiveParameterValue)) +#1 %s(%d): {closure:pfa:%s:7}(Object(SensitiveParameterValue), 3, Object(SensitiveParameterValue), Object(SensitiveParameterValue)) +#2 {main} + thrown in %s on line %d diff --git a/Zend/tests/partial_application/attributes_003.phpt b/Zend/tests/partial_application/attributes_003.phpt new file mode 100644 index 0000000000000..d113d02eb8f79 --- /dev/null +++ b/Zend/tests/partial_application/attributes_003.phpt @@ -0,0 +1,16 @@ +--TEST-- +PFA preserves #[NoDiscard] +--FILE-- + +--EXPECTF-- +Warning: The return value of function {closure:%s}() should either be used or intentionally ignored by casting it as (void) in %s on line 7 diff --git a/Zend/tests/partial_application/clone.phpt b/Zend/tests/partial_application/clone.phpt new file mode 100644 index 0000000000000..f778d776b7173 --- /dev/null +++ b/Zend/tests/partial_application/clone.phpt @@ -0,0 +1,50 @@ +--TEST-- +clone() can be partially applied +--FILE-- + 7])); + +$clone = clone(?, ['a' => 8]); +var_dump($clone(new C(9, 10))); + +?> +--EXPECTF-- +object(C)#%d (2) { + ["a"]=> + int(1) + ["b"]=> + int(2) +} +object(C)#%d (2) { + ["a"]=> + int(3) + ["b"]=> + int(4) +} +object(C)#%d (2) { + ["a"]=> + int(7) + ["b"]=> + int(6) +} +object(C)#%d (2) { + ["a"]=> + int(8) + ["b"]=> + int(10) +} diff --git a/Zend/tests/partial_application/compile_errors_001.phpt b/Zend/tests/partial_application/compile_errors_001.phpt new file mode 100644 index 0000000000000..f08495a1f1e56 --- /dev/null +++ b/Zend/tests/partial_application/compile_errors_001.phpt @@ -0,0 +1,8 @@ +--TEST-- +PFA compile errors: multiple variadic placeholders +--FILE-- + +--EXPECTF-- +Fatal error: Variadic placeholder may only appear once in %s on line %d diff --git a/Zend/tests/partial_application/compile_errors_002.phpt b/Zend/tests/partial_application/compile_errors_002.phpt new file mode 100644 index 0000000000000..b6a2073a83638 --- /dev/null +++ b/Zend/tests/partial_application/compile_errors_002.phpt @@ -0,0 +1,8 @@ +--TEST-- +PFA compile errors: variadic placeholder must be last +--FILE-- + +--EXPECTF-- +Fatal error: Variadic placeholder must be last in %s on line %d diff --git a/Zend/tests/partial_application/compile_errors_003.phpt b/Zend/tests/partial_application/compile_errors_003.phpt new file mode 100644 index 0000000000000..26ff8435111b6 --- /dev/null +++ b/Zend/tests/partial_application/compile_errors_003.phpt @@ -0,0 +1,8 @@ +--TEST-- +PFA compile errors: placeholders can not appear after named args +--FILE-- + +--EXPECTF-- +Fatal error: Cannot use positional argument after named argument in %s on line %d diff --git a/Zend/tests/partial_application/compile_errors_004.phpt b/Zend/tests/partial_application/compile_errors_004.phpt new file mode 100644 index 0000000000000..ac7ec163c5dae --- /dev/null +++ b/Zend/tests/partial_application/compile_errors_004.phpt @@ -0,0 +1,8 @@ +--TEST-- +PFA compile errors: variadic placeholder must be last, including after named args +--FILE-- + +--EXPECTF-- +Fatal error: Variadic placeholder must be last in %s on line %d diff --git a/Zend/tests/partial_application/compile_errors_005.phpt b/Zend/tests/partial_application/compile_errors_005.phpt new file mode 100644 index 0000000000000..30e4aa12b488f --- /dev/null +++ b/Zend/tests/partial_application/compile_errors_005.phpt @@ -0,0 +1,8 @@ +--TEST-- +PFA compile errors: variadic placeholder must be last, including after positional args +--FILE-- + +--EXPECTF-- +Fatal error: Variadic placeholder must be last in %s on line %d diff --git a/Zend/tests/partial_application/compile_errors_006.phpt b/Zend/tests/partial_application/compile_errors_006.phpt new file mode 100644 index 0000000000000..90210be6acae7 --- /dev/null +++ b/Zend/tests/partial_application/compile_errors_006.phpt @@ -0,0 +1,8 @@ +--TEST-- +PFA compile errors: can not use unpacking in PFA, including with variadic placeholdres +--FILE-- + "bar"], ...); +?> +--EXPECTF-- +Fatal error: Cannot combine partial application and unpacking in %s on line %d diff --git a/Zend/tests/partial_application/errors_001.phpt b/Zend/tests/partial_application/errors_001.phpt new file mode 100644 index 0000000000000..2d5348cb28af3 --- /dev/null +++ b/Zend/tests/partial_application/errors_001.phpt @@ -0,0 +1,62 @@ +--TEST-- +PFA errors: PFA instantiation follows the usual argument count validation +--FILE-- +getMessage()); +} + +try { + foo(?, ?, ?, ?); +} catch (Error $ex) { + printf("%s: %s\n", $ex::class, $ex->getMessage()); +} + +try { + $c = new C(); + $c->f(?); +} catch (Error $ex) { + printf("%s: %s\n", $ex::class, $ex->getMessage()); +} + +try { + property_exists(?); +} catch (Error $ex) { + printf("%s: %s\n", $ex::class, $ex->getMessage()); +} + +try { + usleep(?, ?); +} catch (Error $ex) { + printf("%s: %s\n", $ex::class, $ex->getMessage()); +} + +try { + foo(?, ?, ?, ?, ...); +} catch (Error $ex) { + printf("%s: %s\n", $ex::class, $ex->getMessage()); +} + +/* It is allowed to specify less than the number of required params, when there + * is a variadic placeholder */ +foo(?, ...); + +?> +--EXPECT-- +ArgumentCountError: Partial application of foo() expects exactly 3 arguments, 1 given +ArgumentCountError: Partial application of foo() expects at most 3 arguments, 4 given +ArgumentCountError: Partial application of C::f() expects exactly 3 arguments, 1 given +ArgumentCountError: Partial application of property_exists() expects exactly 2 arguments, 1 given +ArgumentCountError: Partial application of usleep() expects at most 1 arguments, 2 given +ArgumentCountError: Partial application of foo() expects at most 3 arguments, 4 given diff --git a/Zend/tests/partial_application/errors_002.phpt b/Zend/tests/partial_application/errors_002.phpt new file mode 100644 index 0000000000000..0132d8873e2ab --- /dev/null +++ b/Zend/tests/partial_application/errors_002.phpt @@ -0,0 +1,15 @@ +--TEST-- +PFA errors: named parameter that resolve to the position of a placeholder is an error +--FILE-- +getMessage()); +} +?> +--EXPECT-- +Error: Named parameter $a overwrites previous placeholder diff --git a/Zend/tests/partial_application/errors_003.phpt b/Zend/tests/partial_application/errors_003.phpt new file mode 100644 index 0000000000000..bc1c16e64f9b8 --- /dev/null +++ b/Zend/tests/partial_application/errors_003.phpt @@ -0,0 +1,77 @@ +--TEST-- +PFA errors: PFA call follows the usual argument count validation +--FILE-- +getMessage()); +} + +$foo = foo(?, ?); + +try { + $foo(1); +} catch (Error $ex) { + printf("%s: %s\n", $ex::class, $ex->getMessage()); +} + +$bar = bar(?, ?, ...); + +try { + $bar(1); +} catch (Error $ex) { + printf("%s: %s\n", $ex::class, $ex->getMessage()); +} + +class Foo { + public function bar($a, ...$b) {} +} + +$foo = new Foo; + +$bar = $foo->bar(?); + +try { + $bar(); +} catch (Error $ex) { + printf("%s: %s\n", $ex::class, $ex->getMessage()); +} + +$repeat = str_repeat('a', ...); + +try { + $repeat(); +} catch (Error $ex) { + printf("%s: %s\n", $ex::class, $ex->getMessage()); +} + +$usleep = usleep(?); + +try { + $usleep(); +} catch (Error $ex) { + printf("%s: %s\n", $ex::class, $ex->getMessage()); +} + +try { + $usleep(1, 2); +} catch (Error $ex) { + printf("%s: %s\n", $ex::class, $ex->getMessage()); +} +?> +--EXPECTF-- +ArgumentCountError: Too few arguments to function {closure:%s:%d}(), 0 passed in %s on line %d and exactly 1 expected +ArgumentCountError: Too few arguments to function {closure:%s:%d}(), 1 passed in %s on line %d and exactly 2 expected +ArgumentCountError: Too few arguments to function {closure:%s:%d}(), 1 passed in %s on line %d and exactly 3 expected +ArgumentCountError: Too few arguments to function Foo::{closure:%s:%d}(), 0 passed in %s on line %d and exactly 1 expected +ArgumentCountError: Too few arguments to function {closure:%s:%d}(), 0 passed in %s on line %d and exactly 1 expected +ArgumentCountError: Too few arguments to function {closure:%s:%d}(), 0 passed in %s on line %d and exactly 1 expected diff --git a/Zend/tests/partial_application/errors_004.phpt b/Zend/tests/partial_application/errors_004.phpt new file mode 100644 index 0000000000000..e5e1432753b27 --- /dev/null +++ b/Zend/tests/partial_application/errors_004.phpt @@ -0,0 +1,16 @@ +--TEST-- +PFA errors: not specifying a required param is an error +--FILE-- + +--EXPECTF-- +Fatal error: Uncaught ArgumentCountError: f(): Argument #2 ($b) not passed in %s:6 +Stack trace: +#0 {main} + thrown in %s on line %d diff --git a/Zend/tests/partial_application/errors_005.phpt b/Zend/tests/partial_application/errors_005.phpt new file mode 100644 index 0000000000000..2c28f0565e2d8 --- /dev/null +++ b/Zend/tests/partial_application/errors_005.phpt @@ -0,0 +1,15 @@ +--TEST-- +PFA errors: Can not fetch default parameter value for Closure::__invoke() +--FILE-- +__invoke(0, 0, ?); +} catch (Error $e) { + echo $e::class, ": ", $e->getMessage(), "\n"; +} + +?> +--EXPECT-- +ArgumentCountError: Closure::__invoke(): Argument #3 ($c) must be passed explicitly, because the default value is not known diff --git a/Zend/tests/partial_application/errors_006.phpt b/Zend/tests/partial_application/errors_006.phpt new file mode 100644 index 0000000000000..aec2fc5dc0734 --- /dev/null +++ b/Zend/tests/partial_application/errors_006.phpt @@ -0,0 +1,23 @@ +--TEST-- +PFA errors: Some internal function parameters have UNKNOWN default value +--FILE-- + array_keys($array, ???, true) + $f = array_keys(?, strict: true); +} catch (Error $e) { + echo $e::class, ": ", $e->getMessage(), "\n"; +} + +try { + // fn (array $array, mixed $filter_value = ???) => array_keys($array, $filter_value, true) + $f = array_keys(?, strict: true, ...); +} catch (Error $e) { + echo $e::class, ": ", $e->getMessage(), "\n"; +} + +?> +--EXPECT-- +ArgumentCountError: array_keys(): Argument #2 ($filter_value) must be passed explicitly, because the default value is not known +ArgumentCountError: array_keys(): Argument #2 ($filter_value) must be passed explicitly, because the default value is not known diff --git a/Zend/tests/partial_application/export_001.phpt b/Zend/tests/partial_application/export_001.phpt new file mode 100644 index 0000000000000..b48146bcc65a8 --- /dev/null +++ b/Zend/tests/partial_application/export_001.phpt @@ -0,0 +1,14 @@ +--TEST-- +PFA AST export +--INI-- +assert.exception=1 +--FILE-- +getMessage()); +} +?> +--EXPECT-- +AssertionError: assert(0 && foo(?) && foo(new stdClass(), bar: 1, ...)) diff --git a/Zend/tests/partial_application/extra_named.phpt b/Zend/tests/partial_application/extra_named.phpt new file mode 100644 index 0000000000000..4dd80cfa0127f --- /dev/null +++ b/Zend/tests/partial_application/extra_named.phpt @@ -0,0 +1,49 @@ +--TEST-- +PFA extra named parameters are forwarded to the actual function +--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/function_name.phpt b/Zend/tests/partial_application/function_name.phpt new file mode 100644 index 0000000000000..5ed0c58696408 --- /dev/null +++ b/Zend/tests/partial_application/function_name.phpt @@ -0,0 +1,19 @@ +--TEST-- +Partial application function name +--FILE-- +getName()); +} + +f(); + +var_dump((new ReflectionFunction(g(?)))->getName()); + +?> +--EXPECTF-- +string(%d) "{closure:pfa:f():6}" +string(%d) "{closure:pfa:%sfunction_name.php:11}" diff --git a/Zend/tests/partial_application/fuzz_001.phpt b/Zend/tests/partial_application/fuzz_001.phpt new file mode 100644 index 0000000000000..f6544105a435b --- /dev/null +++ b/Zend/tests/partial_application/fuzz_001.phpt @@ -0,0 +1,21 @@ +--TEST-- +Closure application fuzz 001 +--FILE-- + +--EXPECTF-- +Closure [ static function {closure:%s:%d} ] { + @@ %s 4 - 4 + + - Bound Variables [2] { + Variable #0 [ $fn ] + Variable #1 [ $a ] + } + + - Parameters [1] { + Parameter #0 [ $b ] + } +} diff --git a/Zend/tests/partial_application/fuzz_002.phpt b/Zend/tests/partial_application/fuzz_002.phpt new file mode 100644 index 0000000000000..685cb706e69bb --- /dev/null +++ b/Zend/tests/partial_application/fuzz_002.phpt @@ -0,0 +1,13 @@ +--TEST-- +Closure application fuzz 002 +--FILE-- + +--EXPECTF-- +OK diff --git a/Zend/tests/partial_application/fuzz_003.phpt b/Zend/tests/partial_application/fuzz_003.phpt new file mode 100644 index 0000000000000..6e9d583fda99b --- /dev/null +++ b/Zend/tests/partial_application/fuzz_003.phpt @@ -0,0 +1,20 @@ +--TEST-- +Closure application fuzz 003 +--FILE-- +method(1, ...); +$bar(2); +?> +--EXPECT-- +array(2) { + [0]=> + int(1) + [1]=> + int(2) +} diff --git a/Zend/tests/partial_application/fuzz_004.phpt b/Zend/tests/partial_application/fuzz_004.phpt new file mode 100644 index 0000000000000..ea005304a3afb --- /dev/null +++ b/Zend/tests/partial_application/fuzz_004.phpt @@ -0,0 +1,19 @@ +--TEST-- +Closure application fuzz 004 +--FILE-- +__invoke(UNDEFINED); +} catch (\Throwable $e) { + echo $e::class, ": ", $e->getMessage(), "\n"; +} + +?> +--EXPECT-- +Error: Undefined constant "UNDEFINED" diff --git a/Zend/tests/partial_application/fuzz_005.phpt b/Zend/tests/partial_application/fuzz_005.phpt new file mode 100644 index 0000000000000..ea04862d83998 --- /dev/null +++ b/Zend/tests/partial_application/fuzz_005.phpt @@ -0,0 +1,16 @@ +--TEST-- +PFA fuzz 005 +--FILE-- + +==DONE== +--EXPECT-- +==DONE== diff --git a/Zend/tests/partial_application/fuzz_006.phpt b/Zend/tests/partial_application/fuzz_006.phpt new file mode 100644 index 0000000000000..26ec6e3e4dd1f --- /dev/null +++ b/Zend/tests/partial_application/fuzz_006.phpt @@ -0,0 +1,16 @@ +--TEST-- +PFA fuzz 006 +--FILE-- + +--EXPECT-- +int(1) diff --git a/Zend/tests/partial_application/fuzz_007.phpt b/Zend/tests/partial_application/fuzz_007.phpt new file mode 100644 index 0000000000000..123ce29d8b180 --- /dev/null +++ b/Zend/tests/partial_application/fuzz_007.phpt @@ -0,0 +1,19 @@ +--TEST-- +PFA fuzz 007 +--FILE-- +getMessage(), "\n"; +} + +?> +--EXPECT-- +Undefined constant "UNDEFINED" diff --git a/Zend/tests/partial_application/hook.phpt b/Zend/tests/partial_application/hook.phpt new file mode 100644 index 0000000000000..6402c5d01e747 --- /dev/null +++ b/Zend/tests/partial_application/hook.phpt @@ -0,0 +1,25 @@ +--TEST-- +Parent property hook call can not be partially applied +--FILE-- +a = 1; + +?> +--EXPECTF-- +Fatal error: Cannot create Closure for parent property hook call in %s on line %d diff --git a/Zend/tests/partial_application/invokable.phpt b/Zend/tests/partial_application/invokable.phpt new file mode 100644 index 0000000000000..c21030e7733a8 --- /dev/null +++ b/Zend/tests/partial_application/invokable.phpt @@ -0,0 +1,51 @@ +--TEST-- +__invoke() can be partially applied +--FILE-- + +--EXPECTF-- +Closure [ public method {closure:%s:%d} ] { + @@ %s.php 11 - 11 + + - Parameters [2] { + Parameter #0 [ int $a ] + Parameter #1 [ object $b ] + } + - Return [ C ] +} + +Closure [ public method {closure:%s:%d} ] { + @@ %s.php 15 - 15 + + - Bound Variables [1] { + Variable #0 [ $b ] + } + + - Parameters [1] { + Parameter #0 [ int $a ] + } + - Return [ C ] +} + +int(1) +object(stdClass)#%d (0) { +} diff --git a/Zend/tests/partial_application/jit_001.phpt b/Zend/tests/partial_application/jit_001.phpt new file mode 100644 index 0000000000000..84aefa05ab282 --- /dev/null +++ b/Zend/tests/partial_application/jit_001.phpt @@ -0,0 +1,9 @@ +--TEST-- +PFA JIT 001 +--FILE-- + +--EXPECT-- +int(1) +int(2) diff --git a/Zend/tests/partial_application/magic_001.phpt b/Zend/tests/partial_application/magic_001.phpt new file mode 100644 index 0000000000000..60f5a3f20dfb6 --- /dev/null +++ b/Zend/tests/partial_application/magic_001.phpt @@ -0,0 +1,82 @@ +--TEST-- +__call() can be partially applied +--FILE-- +method(?); + +echo (string) new ReflectionFunction($bar); + +try { + $bar(); +} catch (Error $ex) { + printf("%s: %s\n", $ex::class, $ex->getMessage()); +} + +try { + $bar(1, 2); +} catch (Error $ex) { + printf("%s: %s\n", $ex::class, $ex->getMessage()); +} + +$bar(1); + +$bar = $foo->method(?, ...); + +echo (string) new ReflectionFunction($bar); + +$bar(10); + +$bar = $foo->method(new Foo, ...); + +echo (string) new ReflectionFunction($bar); + +$bar(100); +?> +--EXPECTF-- +Closure [ public method {closure:%s:%d} ] { + @@ %s 12 - 12 + + - Parameters [1] { + Parameter #0 [ $args0 ] + } +} +ArgumentCountError: Too few arguments to function Foo::{closure:%s:%d}(), 0 passed in %s on line %d and exactly 1 expected +Foo::method +int(1) +Foo::method +int(1) +Closure [ public method {closure:%s:%d} ] { + @@ %s 30 - 30 + + - Parameters [2] { + Parameter #0 [ $args0 ] + Parameter #1 [ ...$args ] + } +} +Foo::method +int(10) +Closure [ public method {closure:%s:%d} ] { + @@ %s 36 - 36 + + - Bound Variables [1] { + Variable #0 [ $args0 ] + } + + - Parameters [1] { + Parameter #0 [ ...$args ] + } +} +Foo::method +object(Foo)#%d (0) { +} +int(100) diff --git a/Zend/tests/partial_application/magic_002.phpt b/Zend/tests/partial_application/magic_002.phpt new file mode 100644 index 0000000000000..d4baf7afc18ff --- /dev/null +++ b/Zend/tests/partial_application/magic_002.phpt @@ -0,0 +1,65 @@ +--TEST-- +__callStatic() can be partially applied +--FILE-- + +--EXPECTF-- +Closure [ static public method {closure:%s:%d} ] { + @@ %s 10 - 10 + + - Parameters [1] { + Parameter #0 [ $args0 ] + } +} +Foo::method +int(1) +Closure [ static public method {closure:%s:%d} ] { + @@ %s 16 - 16 + + - Parameters [2] { + Parameter #0 [ $args0 ] + Parameter #1 [ ...$args ] + } +} +Foo::method +int(10) +Closure [ static public method {closure:%s:%d} ] { + @@ %s 22 - 22 + + - Bound Variables [1] { + Variable #0 [ $args0 ] + } + + - Parameters [1] { + Parameter #0 [ ...$args ] + } +} +Foo::method +object(Foo)#%d (0) { +} +int(100) diff --git a/Zend/tests/partial_application/magic_003.phpt b/Zend/tests/partial_application/magic_003.phpt new file mode 100644 index 0000000000000..75e26c70b1a34 --- /dev/null +++ b/Zend/tests/partial_application/magic_003.phpt @@ -0,0 +1,13 @@ +--TEST-- +PFA magic trampoline release unused +--FILE-- + +--EXPECT-- +OK diff --git a/Zend/tests/partial_application/magic_004.phpt b/Zend/tests/partial_application/magic_004.phpt new file mode 100644 index 0000000000000..2a340b085f62f --- /dev/null +++ b/Zend/tests/partial_application/magic_004.phpt @@ -0,0 +1,13 @@ +--TEST-- +PFA magic trampoline release used +--FILE-- + +--EXPECT-- +OK diff --git a/Zend/tests/partial_application/magic_005.phpt b/Zend/tests/partial_application/magic_005.phpt new file mode 100644 index 0000000000000..8c4e878196fc7 --- /dev/null +++ b/Zend/tests/partial_application/magic_005.phpt @@ -0,0 +1,30 @@ +--TEST-- +PFA magic null ptr deref in arginfo +--FILE-- +method(?); +var_dump($bar); +?> +--EXPECTF-- +object(Closure)#%d (%d) { + ["name"]=> + string(%d) "{closure:%s}" + ["file"]=> + string(%d) "%smagic_005.php" + ["line"]=> + int(8) + ["this"]=> + object(Foo)#%d (0) { + } + ["parameter"]=> + array(1) { + ["$args0"]=> + string(10) "" + } +} diff --git a/Zend/tests/partial_application/magic_006.phpt b/Zend/tests/partial_application/magic_006.phpt new file mode 100644 index 0000000000000..29564d8b615a7 --- /dev/null +++ b/Zend/tests/partial_application/magic_006.phpt @@ -0,0 +1,20 @@ +--TEST-- +PFA magic varargs +--FILE-- +method(1,...); +$bar(2); +?> +--EXPECT-- +array(2) { + [0]=> + int(1) + [1]=> + int(2) +} diff --git a/Zend/tests/partial_application/named_placeholders.phpt b/Zend/tests/partial_application/named_placeholders.phpt new file mode 100644 index 0000000000000..6517a8946d36b --- /dev/null +++ b/Zend/tests/partial_application/named_placeholders.phpt @@ -0,0 +1,117 @@ +--TEST-- +PFA supports named placeholders +--FILE-- +getMessage(), "\n"; +} + +try { + $bar = bar(c: ?, ...); +} catch (\Throwable $e) { + echo $e->getMessage(), "\n"; +} + +?> +--EXPECTF-- +Closure [ static function {closure:%s:%d} ] { + @@ %snamed_placeholders.php 11 - 11 + + - Parameters [1] { + Parameter #0 [ $b = 2 ] + } +} +int(1) +object(B)#%d (0) { +} +int(3) +Closure [ static function {closure:%s:%d} ] { + @@ %snamed_placeholders.php 17 - 17 + + - Bound Variables [1] { + Variable #0 [ $fn ] + } + + - Parameters [1] { + Parameter #0 [ $b = 2 ] + } +} +int(1) +object(B)#%d (0) { +} +int(3) +Closure [ static function {closure:%s:%d} ] { + @@ %snamed_placeholders.php 24 - 24 + + - Bound Variables [1] { + Variable #0 [ $fn ] + } + + - Parameters [1] { + Parameter #0 [ $b = 2 ] + } +} +int(1) +object(B)#%d (0) { +} +int(3) +Closure [ static function {closure:%s:%d} ] { + @@ %snamed_placeholders.php 34 - 34 + + - Parameters [3] { + Parameter #0 [ $b = 2 ] + Parameter #1 [ $a = 1 ] + 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/non_dynamic_call_funcs.phpt b/Zend/tests/partial_application/non_dynamic_call_funcs.phpt new file mode 100644 index 0000000000000..0430986be2ac2 --- /dev/null +++ b/Zend/tests/partial_application/non_dynamic_call_funcs.phpt @@ -0,0 +1,46 @@ +--TEST-- +Functions that can not be called dynamically, can not be partially applied +--FILE-- +getMessage(), "\n"; + } +} + +?> +--EXPECT-- +Error: Cannot call func_get_arg() dynamically +Error: Cannot call compact() dynamically +Error: Cannot call extract() dynamically +ArgumentCountError: Partial application of func_get_args() expects at most 0 arguments, 1 given +ArgumentCountError: Partial application of func_num_args() expects at most 0 arguments, 1 given +ArgumentCountError: Partial application of get_defined_vars() expects at most 0 arguments, 1 given diff --git a/Zend/tests/partial_application/observers.phpt b/Zend/tests/partial_application/observers.phpt new file mode 100644 index 0000000000000..8717b38ca326d --- /dev/null +++ b/Zend/tests/partial_application/observers.phpt @@ -0,0 +1,34 @@ +--TEST-- +PFA support observers +--EXTENSIONS-- +zend_test +--INI-- +zend_test.observer.enabled=1 +zend_test.observer.show_output=1 +zend_test.observer.observe_all=1 +--FILE-- + +--EXPECTF-- + + + + <{closure:%s}> + + + + +int(1) +int(2) + + + + diff --git a/Zend/tests/partial_application/param_reorder.phpt b/Zend/tests/partial_application/param_reorder.phpt new file mode 100644 index 0000000000000..3ade1beb0af73 --- /dev/null +++ b/Zend/tests/partial_application/param_reorder.phpt @@ -0,0 +1,202 @@ +--TEST-- +Named parameters define the order of parameters in a PFA +--FILE-- +getMessage(), "\n"; +} + +?> +--EXPECTF-- +# All named +Closure [ static function {closure:%s:%d} ] { + @@ %sparam_reorder.php 13 - 13 + + - Parameters [4] { + Parameter #0 [ $d ] + Parameter #1 [ $c ] + Parameter #2 [ $b ] + Parameter #3 [ $a ] + } +} + +array(4) { + [0]=> + int(4) + [1]=> + int(3) + [2]=> + int(2) + [3]=> + int(1) +} +# Some named: Positional first, then named in specified order +Closure [ static function {closure:%s:%d} ] { + @@ %sparam_reorder.php 19 - 19 + + - Parameters [4] { + Parameter #0 [ $a ] + Parameter #1 [ $b ] + Parameter #2 [ $d ] + Parameter #3 [ $c ] + } +} + +array(4) { + [0]=> + int(1) + [1]=> + int(2) + [2]=> + int(4) + [3]=> + int(3) +} +# Some named, one unspecified +Closure [ static function {closure:%s:%d} ] { + @@ %sparam_reorder.php 25 - 25 + + - Parameters [3] { + Parameter #0 [ $a ] + Parameter #1 [ $c ] + Parameter #2 [ $b ] + } +} + +array(4) { + [0]=> + int(1) + [1]=> + int(3) + [2]=> + int(2) + [3]=> + NULL +} +# Some named, some implicit added by '...' +Closure [ static function {closure:%s:%d} ] { + @@ %sparam_reorder.php 31 - 31 + + - Parameters [4] { + Parameter #0 [ $c ] + Parameter #1 [ $b ] + Parameter #2 [ $a ] + Parameter #3 [ $d = NULL ] + } +} + +array(4) { + [0]=> + int(3) + [1]=> + int(2) + [2]=> + int(1) + [3]=> + int(4) +} +# Some named, some implicit added by '...' on variadic function +Closure [ static function {closure:%s:%d} ] { + @@ %sparam_reorder.php 37 - 37 + + - Parameters [4] { + Parameter #0 [ $c ] + Parameter #1 [ $b ] + Parameter #2 [ $a ] + Parameter #3 [ ...$d ] + } +} + +array(4) { + [0]=> + int(3) + [1]=> + int(2) + [2]=> + int(1) + [3]=> + array(3) { + [0]=> + int(4) + [1]=> + int(5) + [2]=> + int(6) + } +} +# Some prebound, some named +Closure [ static function {closure:%s:%d} ] { + @@ %sparam_reorder.php 43 - 43 + + - Bound Variables [2] { + Variable #0 [ $a ] + Variable #1 [ $d ] + } + + - Parameters [2] { + Parameter #0 [ $c ] + Parameter #1 [ $b ] + } +} + +array(4) { + [0]=> + int(-1) + [1]=> + int(2) + [2]=> + int(1) + [3]=> + int(-2) +} +# Some named, some required missing +ArgumentCountError: f(): Argument #1 ($a) not passed diff --git a/Zend/tests/partial_application/pipe_optimization_001.phpt b/Zend/tests/partial_application/pipe_optimization_001.phpt new file mode 100644 index 0000000000000..71bfee5dba44e --- /dev/null +++ b/Zend/tests/partial_application/pipe_optimization_001.phpt @@ -0,0 +1,47 @@ +--TEST-- +PFA optimization: PFA with single placeholder arg can be optimized +--EXTENSIONS-- +opcache +--INI-- +opcache.opt_debug_level=0x20000 +opcache.enable=1 +opcache.enable_cli=1 +opcache.file_cache= +opcache.file_cache_only=0 +--FILE-- + 0) { + function foo($a) { + var_dump($a); + } +} + +1 |> foo(?); + +?> +--EXPECTF-- +$_main: + ; (lines=9, args=0, vars=0, tmps=2) + ; (after optimizer) + ; %spipe_optimization_001.php:1-12 +0000 INIT_FCALL 0 %d string("time") +0001 V1 = DO_ICALL +0002 T0 = IS_SMALLER int(0) V1 +0003 JMPZ T0 0005 +0004 DECLARE_FUNCTION string("foo") 0 +0005 INIT_FCALL_BY_NAME 1 string("foo") +0006 SEND_VAL_EX int(1) 1 +0007 DO_FCALL_BY_NAME +0008 RETURN int(1) + +foo: + ; (lines=5, args=1, vars=1, tmps=0) + ; (after optimizer) + ; %spipe_optimization_001.php:4-6 +0000 CV0($a) = RECV 1 +0001 INIT_FCALL 1 %d string("var_dump") +0002 SEND_VAR CV0($a) 1 +0003 DO_ICALL +0004 RETURN null +int(1) diff --git a/Zend/tests/partial_application/pipe_optimization_002.phpt b/Zend/tests/partial_application/pipe_optimization_002.phpt new file mode 100644 index 0000000000000..ae5992e405a05 --- /dev/null +++ b/Zend/tests/partial_application/pipe_optimization_002.phpt @@ -0,0 +1,51 @@ +--TEST-- +PFA pipe optimization: PFA with only one placeholder can be optimized +--EXTENSIONS-- +opcache +--INI-- +opcache.opt_debug_level=0x20000 +opcache.enable=1 +opcache.enable_cli=1 +opcache.file_cache= +opcache.file_cache_only=0 +--FILE-- + 0) { + function foo($a, $b) { + var_dump($a, $b); + } +} + +2 |> foo(1, ?); + +?> +--EXPECTF-- +$_main: + ; (lines=10, args=0, vars=0, tmps=2) + ; (after optimizer) + ; %spipe_optimization_002.php:1-12 +0000 INIT_FCALL 0 %d string("time") +0001 V1 = DO_ICALL +0002 T0 = IS_SMALLER int(0) V1 +0003 JMPZ T0 0005 +0004 DECLARE_FUNCTION string("foo") 0 +0005 INIT_FCALL_BY_NAME 2 string("foo") +0006 SEND_VAL_EX int(1) 1 +0007 SEND_VAL_EX int(2) 2 +0008 DO_FCALL_BY_NAME +0009 RETURN int(1) + +foo: + ; (lines=7, args=2, vars=2, tmps=0) + ; (after optimizer) + ; %spipe_optimization_002.php:4-6 +0000 CV0($a) = RECV 1 +0001 CV1($b) = RECV 2 +0002 INIT_FCALL 2 %d string("var_dump") +0003 SEND_VAR CV0($a) 1 +0004 SEND_VAR CV1($b) 2 +0005 DO_ICALL +0006 RETURN null +int(1) +int(2) diff --git a/Zend/tests/partial_application/pipe_optimization_003.phpt b/Zend/tests/partial_application/pipe_optimization_003.phpt new file mode 100644 index 0000000000000..4f45eb5555e29 --- /dev/null +++ b/Zend/tests/partial_application/pipe_optimization_003.phpt @@ -0,0 +1,51 @@ +--TEST-- +PFA pipe optimization: PFA with only one placeholder can be optimized +--EXTENSIONS-- +opcache +--INI-- +opcache.opt_debug_level=0x20000 +opcache.enable=1 +opcache.enable_cli=1 +opcache.file_cache= +opcache.file_cache_only=0 +--FILE-- + 0) { + function foo($a, $b) { + var_dump($a, $b); + } +} + +2 |> foo(?, 1); + +?> +--EXPECTF-- +$_main: + ; (lines=10, args=0, vars=0, tmps=2) + ; (after optimizer) + ; %spipe_optimization_003.php:1-12 +0000 INIT_FCALL 0 %d string("time") +0001 V1 = DO_ICALL +0002 T0 = IS_SMALLER int(0) V1 +0003 JMPZ T0 0005 +0004 DECLARE_FUNCTION string("foo") 0 +0005 INIT_FCALL_BY_NAME 2 string("foo") +0006 SEND_VAL_EX int(2) 1 +0007 SEND_VAL_EX int(1) 2 +0008 DO_FCALL_BY_NAME +0009 RETURN int(1) + +foo: + ; (lines=7, args=2, vars=2, tmps=0) + ; (after optimizer) + ; %spipe_optimization_003.php:4-6 +0000 CV0($a) = RECV 1 +0001 CV1($b) = RECV 2 +0002 INIT_FCALL 2 %d string("var_dump") +0003 SEND_VAR CV0($a) 1 +0004 SEND_VAR CV1($b) 2 +0005 DO_ICALL +0006 RETURN null +int(2) +int(1) diff --git a/Zend/tests/partial_application/pipe_optimization_004.phpt b/Zend/tests/partial_application/pipe_optimization_004.phpt new file mode 100644 index 0000000000000..a2ad5f72ed585 --- /dev/null +++ b/Zend/tests/partial_application/pipe_optimization_004.phpt @@ -0,0 +1,85 @@ +--TEST-- +PFA pipe optimization: PFA with multiple placeholders can not be optimized +--EXTENSIONS-- +opcache +--INI-- +opcache.opt_debug_level=0x20000 +opcache.enable=1 +opcache.enable_cli=1 +opcache.file_cache= +opcache.file_cache_only=0 +--FILE-- + 0) { + function foo($a, $b) { + var_dump($a, $b); + } +} + +try { +2 |> foo(?, ?); +} catch (\Throwable $e) { + echo $e->getMessage(), "\n"; +} + +?> +--EXPECTF-- +$_main: + ; (lines=19, args=0, vars=1, tmps=2) + ; (after optimizer) + ; %spipe_optimization_004.php:1-16 +0000 INIT_FCALL 0 %d string("time") +0001 V2 = DO_ICALL +0002 T1 = IS_SMALLER int(0) V2 +0003 JMPZ T1 0005 +0004 DECLARE_FUNCTION string("foo") 0 +0005 INIT_FCALL_BY_NAME 2 string("foo") +0006 SEND_PLACEHOLDER +0007 SEND_PLACEHOLDER +0008 T1 = CALLABLE_CONVERT_PARTIAL %d +0009 INIT_DYNAMIC_CALL 1 T1 +0010 SEND_VAL_EX int(2) 1 +0011 DO_FCALL +0012 RETURN int(1) +0013 CV0($e) = CATCH string("Throwable") +0014 INIT_METHOD_CALL 0 CV0($e) string("getMessage") +0015 V1 = DO_FCALL +0016 ECHO V1 +0017 ECHO string("\n") +0018 RETURN int(1) +EXCEPTION TABLE: + 0005, 0013, -, - + +foo: + ; (lines=7, args=2, vars=2, tmps=0) + ; (after optimizer) + ; %spipe_optimization_004.php:4-6 +0000 CV0($a) = RECV 1 +0001 CV1($b) = RECV 2 +0002 INIT_FCALL 2 %d string("var_dump") +0003 SEND_VAR CV0($a) 1 +0004 SEND_VAR CV1($b) 2 +0005 DO_ICALL +0006 RETURN null + +$_main: + ; (lines=3, args=0, vars=0, tmps=1) + ; (after optimizer) + ; %s:1-10 +0000 T0 = DECLARE_LAMBDA_FUNCTION 0 +0001 FREE T0 +0002 RETURN int(1) + +{closure:%s:%d}: + ; (lines=7, args=2, vars=2, tmps=1) + ; (after optimizer) + ; %s:10-10 +0000 CV0($a) = RECV 1 +0001 CV1($b) = RECV 2 +0002 INIT_FCALL 2 %d string("foo") +0003 SEND_VAR CV0($a) 1 +0004 SEND_VAR CV1($b) 2 +0005 V2 = DO_UCALL +0006 RETURN V2 +Too few arguments to function {closure:%s:%d}(), 1 passed in %s on line %d and exactly 2 expected diff --git a/Zend/tests/partial_application/pipe_optimization_005.phpt b/Zend/tests/partial_application/pipe_optimization_005.phpt new file mode 100644 index 0000000000000..3844e09aa8ad2 --- /dev/null +++ b/Zend/tests/partial_application/pipe_optimization_005.phpt @@ -0,0 +1,51 @@ +--TEST-- +PFA pipe optimization: PFA with only one placeholder can be optimized +--EXTENSIONS-- +opcache +--INI-- +opcache.opt_debug_level=0x20000 +opcache.enable=1 +opcache.enable_cli=1 +opcache.file_cache= +opcache.file_cache_only=0 +--FILE-- + 0) { + function foo($a, $b) { + var_dump($a, $b); + } +} + +2 |> foo(1, ...); + +?> +--EXPECTF-- +$_main: + ; (lines=10, args=0, vars=0, tmps=2) + ; (after optimizer) + ; %spipe_optimization_005.php:1-12 +0000 INIT_FCALL 0 %d string("time") +0001 V1 = DO_ICALL +0002 T0 = IS_SMALLER int(0) V1 +0003 JMPZ T0 0005 +0004 DECLARE_FUNCTION string("foo") 0 +0005 INIT_FCALL_BY_NAME 2 string("foo") +0006 SEND_VAL_EX int(1) 1 +0007 SEND_VAL_EX int(2) 2 +0008 DO_FCALL_BY_NAME +0009 RETURN int(1) + +foo: + ; (lines=7, args=2, vars=2, tmps=0) + ; (after optimizer) + ; %spipe_optimization_005.php:4-6 +0000 CV0($a) = RECV 1 +0001 CV1($b) = RECV 2 +0002 INIT_FCALL 2 %d string("var_dump") +0003 SEND_VAR CV0($a) 1 +0004 SEND_VAR CV1($b) 2 +0005 DO_ICALL +0006 RETURN null +int(1) +int(2) diff --git a/Zend/tests/partial_application/pipe_optimization_006.phpt b/Zend/tests/partial_application/pipe_optimization_006.phpt new file mode 100644 index 0000000000000..2a83f2b83fe9a --- /dev/null +++ b/Zend/tests/partial_application/pipe_optimization_006.phpt @@ -0,0 +1,55 @@ +--TEST-- +PFA pipe optimization: PFA with only one placeholder can be optimized (named) +--EXTENSIONS-- +opcache +--INI-- +opcache.opt_debug_level=0x20000 +opcache.enable=1 +opcache.enable_cli=1 +opcache.file_cache= +opcache.file_cache_only=0 +--FILE-- + 0) { + function foo($a, $b = null, $c = null) { + var_dump($a, $b, $c); + } +} + +2 |> foo(1, c: ?); + +?> +--EXPECTF-- +$_main: + ; (lines=11, args=0, vars=0, tmps=2) + ; (after optimizer) + ; %spipe_optimization_006.php:1-12 +0000 INIT_FCALL 0 %d string("time") +0001 V1 = DO_ICALL +0002 T0 = IS_SMALLER int(0) V1 +0003 JMPZ T0 0005 +0004 DECLARE_FUNCTION string("foo") 0 +0005 INIT_FCALL_BY_NAME 1 string("foo") +0006 SEND_VAL_EX int(1) 1 +0007 SEND_VAL_EX int(2) string("c") +0008 CHECK_UNDEF_ARGS +0009 DO_FCALL_BY_NAME +0010 RETURN int(1) + +foo: + ; (lines=9, args=3, vars=3, tmps=0) + ; (after optimizer) + ; %spipe_optimization_006.php:4-6 +0000 CV0($a) = RECV 1 +0001 CV1($b) = RECV_INIT 2 null +0002 CV2($c) = RECV_INIT 3 null +0003 INIT_FCALL 3 %d string("var_dump") +0004 SEND_VAR CV0($a) 1 +0005 SEND_VAR CV1($b) 2 +0006 SEND_VAR CV2($c) 3 +0007 DO_ICALL +0008 RETURN null +int(1) +NULL +int(2) diff --git a/Zend/tests/partial_application/pipe_optimization_007.phpt b/Zend/tests/partial_application/pipe_optimization_007.phpt new file mode 100644 index 0000000000000..39dd48b632e4d --- /dev/null +++ b/Zend/tests/partial_application/pipe_optimization_007.phpt @@ -0,0 +1,85 @@ +--TEST-- +PFA pipe optimization: PFA with multiple placeholders can not be optimized +--EXTENSIONS-- +opcache +--INI-- +opcache.opt_debug_level=0x20000 +opcache.enable=1 +opcache.enable_cli=1 +opcache.file_cache= +opcache.file_cache_only=0 +--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=19, args=0, vars=1, tmps=2) + ; (after optimizer) + ; %spipe_optimization_007.php:1-16 +0000 INIT_FCALL 0 %d string("time") +0001 V2 = DO_ICALL +0002 T1 = IS_SMALLER int(0) V2 +0003 JMPZ T1 0005 +0004 DECLARE_FUNCTION string("foo") 0 +0005 INIT_FCALL_BY_NAME 0 string("foo") +0006 SEND_PLACEHOLDER string("a") +0007 SEND_PLACEHOLDER string("b") +0008 T1 = CALLABLE_CONVERT_PARTIAL %d array(...) +0009 INIT_DYNAMIC_CALL 1 T1 +0010 SEND_VAL_EX int(2) 1 +0011 DO_FCALL +0012 RETURN int(1) +0013 CV0($e) = CATCH string("Throwable") +0014 INIT_METHOD_CALL 0 CV0($e) string("getMessage") +0015 V1 = DO_FCALL +0016 ECHO V1 +0017 ECHO string("\n") +0018 RETURN int(1) +EXCEPTION TABLE: + 0005, 0013, -, - + +foo: + ; (lines=7, args=2, vars=2, tmps=0) + ; (after optimizer) + ; %spipe_optimization_007.php:4-6 +0000 CV0($a) = RECV 1 +0001 CV1($b) = RECV 2 +0002 INIT_FCALL 2 %d string("var_dump") +0003 SEND_VAR CV0($a) 1 +0004 SEND_VAR CV1($b) 2 +0005 DO_ICALL +0006 RETURN null + +$_main: + ; (lines=3, args=0, vars=0, tmps=1) + ; (after optimizer) + ; %s:1-10 +0000 T0 = DECLARE_LAMBDA_FUNCTION 0 +0001 FREE T0 +0002 RETURN int(1) + +{closure:%s:%d}: + ; (lines=7, args=2, vars=2, tmps=1) + ; (after optimizer) + ; %s:10-10 +0000 CV0($a) = RECV 1 +0001 CV1($b) = RECV 2 +0002 INIT_FCALL 2 %d string("foo") +0003 SEND_VAR CV0($a) 1 +0004 SEND_VAR CV1($b) 2 +0005 V2 = DO_UCALL +0006 RETURN V2 +Too few arguments to function {closure:%s:%d}(), 1 passed in %s on line %d and exactly 2 expected diff --git a/Zend/tests/partial_application/pipe_optimization_008.phpt b/Zend/tests/partial_application/pipe_optimization_008.phpt new file mode 100644 index 0000000000000..6293f782d4d44 --- /dev/null +++ b/Zend/tests/partial_application/pipe_optimization_008.phpt @@ -0,0 +1,99 @@ +--TEST-- +PFA pipe optimization: PFA with both a variadic placeholder and named arg can not be optimized +--EXTENSIONS-- +opcache +--INI-- +opcache.opt_debug_level=0x20000 +opcache.enable=1 +opcache.enable_cli=1 +opcache.file_cache= +opcache.file_cache_only=0 +--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=18, args=0, vars=1, tmps=2) + ; (after optimizer) + ; %spipe_optimization_008.php:1-16 +0000 INIT_FCALL 0 %d string("time") +0001 V2 = DO_ICALL +0002 T1 = IS_SMALLER int(0) V2 +0003 JMPZ T1 0005 +0004 DECLARE_FUNCTION string("foo") 0 +0005 INIT_FCALL_BY_NAME 0 string("foo") +0006 SEND_VAL_EX int(1) string("a") +0007 T1 = CALLABLE_CONVERT_PARTIAL %d +0008 INIT_DYNAMIC_CALL 1 T1 +0009 SEND_VAL_EX int(2) 1 +0010 DO_FCALL +0011 RETURN int(1) +0012 CV0($e) = CATCH string("Throwable") +0013 INIT_METHOD_CALL 0 CV0($e) string("getMessage") +0014 V1 = DO_FCALL +0015 ECHO V1 +0016 ECHO string("\n") +0017 RETURN int(1) +EXCEPTION TABLE: + 0005, 0012, -, - + +foo: + ; (lines=7, args=2, vars=2, tmps=0) + ; (after optimizer) + ; %spipe_optimization_008.php:4-6 +0000 CV0($a) = RECV 1 +0001 CV1($b) = RECV 2 +0002 INIT_FCALL 2 %d string("var_dump") +0003 SEND_VAR CV0($a) 1 +0004 SEND_VAR CV1($b) 2 +0005 DO_ICALL +0006 RETURN null + +$_main: + ; (lines=4, args=0, vars=1, tmps=1) + ; (after optimizer) + ; %s:1-10 +0000 T1 = DECLARE_LAMBDA_FUNCTION 0 +0001 BIND_LEXICAL T1 CV0($a) +0002 FREE T1 +0003 RETURN int(1) +LIVE RANGES: + 1: 0001 - 0002 (tmp/var) + +{closure:%s:%d}: + ; (lines=18, args=1, vars=2, tmps=2) + ; (after optimizer) + ; %s:10-10 +0000 CV0($b) = RECV 1 +0001 BIND_STATIC CV1($a) +0002 T3 = FUNC_NUM_ARGS +0003 T2 = IS_SMALLER_OR_EQUAL T3 int(1) +0004 JMPZ T2 0010 +0005 INIT_FCALL 2 %d string("foo") +0006 SEND_VAR CV1($a) 1 +0007 SEND_VAR CV0($b) 2 +0008 V2 = DO_UCALL +0009 RETURN V2 +0010 INIT_FCALL 2 %d string("foo") +0011 SEND_VAR CV1($a) 1 +0012 SEND_VAR CV0($b) 2 +0013 T2 = FUNC_GET_ARGS int(1) +0014 SEND_UNPACK T2 +0015 CHECK_UNDEF_ARGS +0016 V2 = DO_UCALL +0017 RETURN V2 +int(1) +int(2) diff --git a/Zend/tests/partial_application/pipe_optimization_009.phpt b/Zend/tests/partial_application/pipe_optimization_009.phpt new file mode 100644 index 0000000000000..0902786616373 --- /dev/null +++ b/Zend/tests/partial_application/pipe_optimization_009.phpt @@ -0,0 +1,103 @@ +--TEST-- +PFA pipe optimization: Evaluation order +--EXTENSIONS-- +opcache +--INI-- +opcache.opt_debug_level=0x20000 +opcache.enable=1 +opcache.enable_cli=1 +opcache.file_cache= +opcache.file_cache_only=0 +--FILE-- + 0) { + function foo($a, $b, $c) { + var_dump($a, $b, $c); + } + function lhs() { + echo __FUNCTION__, "\n"; + return 0; + } + function arg1() { + echo __FUNCTION__, "\n"; + return 1; + } + function arg2() { + echo __FUNCTION__, "\n"; + return 2; + } +} + +lhs() |> foo(arg1(), ?, arg2()); + +?> +--EXPECTF-- +$_main: + ; (lines=21, args=0, vars=0, tmps=2) + ; (after optimizer) + ; %spipe_optimization_009.php:1-24 +0000 INIT_FCALL 0 %d string("time") +0001 V1 = DO_ICALL +0002 T0 = IS_SMALLER int(0) V1 +0003 JMPZ T0 0008 +0004 DECLARE_FUNCTION string("foo") 0 +0005 DECLARE_FUNCTION string("lhs") 1 +0006 DECLARE_FUNCTION string("arg1") 2 +0007 DECLARE_FUNCTION string("arg2") 3 +0008 INIT_FCALL_BY_NAME 0 string("lhs") +0009 V1 = DO_FCALL_BY_NAME +0010 T0 = QM_ASSIGN V1 +0011 INIT_FCALL_BY_NAME 3 string("foo") +0012 INIT_FCALL_BY_NAME 0 string("arg1") +0013 V1 = DO_FCALL_BY_NAME +0014 SEND_VAR_NO_REF_EX V1 1 +0015 SEND_VAL_EX T0 2 +0016 INIT_FCALL_BY_NAME 0 string("arg2") +0017 V0 = DO_FCALL_BY_NAME +0018 SEND_VAR_NO_REF_EX V0 3 +0019 DO_FCALL_BY_NAME +0020 RETURN int(1) +LIVE RANGES: + 0: 0011 - 0015 (tmp/var) + +foo: + ; (lines=9, args=3, vars=3, tmps=0) + ; (after optimizer) + ; %spipe_optimization_009.php:4-6 +0000 CV0($a) = RECV 1 +0001 CV1($b) = RECV 2 +0002 CV2($c) = RECV 3 +0003 INIT_FCALL 3 %d string("var_dump") +0004 SEND_VAR CV0($a) 1 +0005 SEND_VAR CV1($b) 2 +0006 SEND_VAR CV2($c) 3 +0007 DO_ICALL +0008 RETURN null + +lhs: + ; (lines=2, args=0, vars=0, tmps=0) + ; (after optimizer) + ; %spipe_optimization_009.php:7-10 +0000 ECHO string("lhs\n") +0001 RETURN int(0) + +arg1: + ; (lines=2, args=0, vars=0, tmps=0) + ; (after optimizer) + ; %spipe_optimization_009.php:11-14 +0000 ECHO string("arg1\n") +0001 RETURN int(1) + +arg2: + ; (lines=2, args=0, vars=0, tmps=0) + ; (after optimizer) + ; %spipe_optimization_009.php:15-18 +0000 ECHO string("arg2\n") +0001 RETURN int(2) +lhs +arg1 +arg2 +int(1) +int(0) +int(2) diff --git a/Zend/tests/partial_application/pipe_optimization_010.phpt b/Zend/tests/partial_application/pipe_optimization_010.phpt new file mode 100644 index 0000000000000..60202e72cfd1d --- /dev/null +++ b/Zend/tests/partial_application/pipe_optimization_010.phpt @@ -0,0 +1,58 @@ +--TEST-- +PFA pipe optimization: References +--EXTENSIONS-- +opcache +--INI-- +opcache.opt_debug_level=0x20000 +opcache.enable=1 +opcache.enable_cli=1 +opcache.file_cache= +opcache.file_cache_only=0 +--FILE-- + 0) { + function foo(&$a, $b) { + var_dump($a, $b); + $a = 2; + } +} + +1 |> foo($a, ?); +var_dump($a); + +?> +--EXPECTF-- +$_main: + ; (lines=13, args=0, vars=1, tmps=2) + ; (after optimizer) + ; %spipe_optimization_010.php:1-14 +0000 INIT_FCALL 0 %d string("time") +0001 V2 = DO_ICALL +0002 T1 = IS_SMALLER int(0) V2 +0003 JMPZ T1 0005 +0004 DECLARE_FUNCTION string("foo") 0 +0005 INIT_FCALL_BY_NAME 2 string("foo") +0006 SEND_VAR_EX CV0($a) 1 +0007 SEND_VAL_EX int(1) 2 +0008 DO_FCALL_BY_NAME +0009 INIT_FCALL 1 %d string("var_dump") +0010 SEND_VAR CV0($a) 1 +0011 DO_ICALL +0012 RETURN int(1) + +foo: + ; (lines=8, args=2, vars=2, tmps=0) + ; (after optimizer) + ; %spipe_optimization_010.php:4-7 +0000 CV0($a) = RECV 1 +0001 CV1($b) = RECV 2 +0002 INIT_FCALL 2 %d string("var_dump") +0003 SEND_VAR CV0($a) 1 +0004 SEND_VAR CV1($b) 2 +0005 DO_ICALL +0006 ASSIGN CV0($a) int(2) +0007 RETURN null +NULL +int(1) +int(2) diff --git a/Zend/tests/partial_application/pipe_optimization_011.phpt b/Zend/tests/partial_application/pipe_optimization_011.phpt new file mode 100644 index 0000000000000..1a39e4ff2f0d6 --- /dev/null +++ b/Zend/tests/partial_application/pipe_optimization_011.phpt @@ -0,0 +1,110 @@ +--TEST-- +PFA pipe optimization: Evaluation order +--EXTENSIONS-- +opcache +--INI-- +opcache.opt_debug_level=0x20000 +opcache.enable=1 +opcache.enable_cli=1 +opcache.file_cache= +opcache.file_cache_only=0 +--FILE-- + 0) { + function foo($a, $b, $c) { + var_dump($a, $b, $c); + } + function lhs() { + echo __FUNCTION__, "\n"; + return 0; + } + function arg1() { + global $a; + $a = 2; + echo __FUNCTION__, "\n"; + return 1; + } + function arg2() { + global $a; + $a = 3; + echo __FUNCTION__, "\n"; + return 2; + } +} + +$a = 0; +$a |> foo(arg1(), ?, arg2()); + +?> +--EXPECTF-- +$_main: + ; (lines=20, args=0, vars=1, tmps=2) + ; (after optimizer) + ; %spipe_optimization_011.php:1-29 +0000 INIT_FCALL 0 %d string("time") +0001 V2 = DO_ICALL +0002 T1 = IS_SMALLER int(0) V2 +0003 JMPZ T1 0008 +0004 DECLARE_FUNCTION string("foo") 0 +0005 DECLARE_FUNCTION string("lhs") 1 +0006 DECLARE_FUNCTION string("arg1") 2 +0007 DECLARE_FUNCTION string("arg2") 3 +0008 ASSIGN CV0($a) int(0) +0009 T1 = QM_ASSIGN CV0($a) +0010 INIT_FCALL_BY_NAME 3 string("foo") +0011 INIT_FCALL_BY_NAME 0 string("arg1") +0012 V2 = DO_FCALL_BY_NAME +0013 SEND_VAR_NO_REF_EX V2 1 +0014 SEND_VAL_EX T1 2 +0015 INIT_FCALL_BY_NAME 0 string("arg2") +0016 V1 = DO_FCALL_BY_NAME +0017 SEND_VAR_NO_REF_EX V1 3 +0018 DO_FCALL_BY_NAME +0019 RETURN int(1) +LIVE RANGES: + 1: 0010 - 0014 (tmp/var) + +foo: + ; (lines=9, args=3, vars=3, tmps=0) + ; (after optimizer) + ; %spipe_optimization_011.php:4-6 +0000 CV0($a) = RECV 1 +0001 CV1($b) = RECV 2 +0002 CV2($c) = RECV 3 +0003 INIT_FCALL 3 %d string("var_dump") +0004 SEND_VAR CV0($a) 1 +0005 SEND_VAR CV1($b) 2 +0006 SEND_VAR CV2($c) 3 +0007 DO_ICALL +0008 RETURN null + +lhs: + ; (lines=2, args=0, vars=0, tmps=0) + ; (after optimizer) + ; %spipe_optimization_011.php:7-10 +0000 ECHO string("lhs\n") +0001 RETURN int(0) + +arg1: + ; (lines=4, args=0, vars=1, tmps=0) + ; (after optimizer) + ; %spipe_optimization_011.php:11-16 +0000 BIND_GLOBAL CV0($a) string("a") +0001 ASSIGN CV0($a) int(2) +0002 ECHO string("arg1\n") +0003 RETURN int(1) + +arg2: + ; (lines=4, args=0, vars=1, tmps=0) + ; (after optimizer) + ; %spipe_optimization_011.php:17-22 +0000 BIND_GLOBAL CV0($a) string("a") +0001 ASSIGN CV0($a) int(3) +0002 ECHO string("arg2\n") +0003 RETURN int(2) +arg1 +arg2 +int(1) +int(0) +int(2) diff --git a/Zend/tests/partial_application/preloading.inc b/Zend/tests/partial_application/preloading.inc new file mode 100644 index 0000000000000..885ed0c5b0f18 --- /dev/null +++ b/Zend/tests/partial_application/preloading.inc @@ -0,0 +1,13 @@ + diff --git a/Zend/tests/partial_application/preloading.phpt b/Zend/tests/partial_application/preloading.phpt new file mode 100644 index 0000000000000..5e3069c271a6d --- /dev/null +++ b/Zend/tests/partial_application/preloading.phpt @@ -0,0 +1,15 @@ +--TEST-- +PFA preloading +--INI-- +opcache.enable=1 +opcache.enable_cli=1 +opcache.preload={PWD}/preloading.inc +--FILE-- + +--EXPECT-- +int(2) +int(2) diff --git a/Zend/tests/partial_application/rebinding_001.phpt b/Zend/tests/partial_application/rebinding_001.phpt new file mode 100644 index 0000000000000..012de02c55368 --- /dev/null +++ b/Zend/tests/partial_application/rebinding_001.phpt @@ -0,0 +1,63 @@ +--TEST-- +PFA 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::{closure:%s:%d}() to object of class Unrelated, this will be an error in PHP 9 in %s on line %d +Value of type null is not callable +# Cannot unbind $this on instance method: + +Warning: Cannot unbind $this of method, this will be an error in PHP 9 in %s on line %d +Value of type null is not callable +# Can unbind $this on static method: +string(1) "C" diff --git a/Zend/tests/partial_application/rebinding_002.phpt b/Zend/tests/partial_application/rebinding_002.phpt new file mode 100644 index 0000000000000..fca6db08500f2 --- /dev/null +++ b/Zend/tests/partial_application/rebinding_002.phpt @@ -0,0 +1,45 @@ +--TEST-- +PFA 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, this will be an error in PHP 9 in %s on line %d +Value of type null is not callable +# Function cannot be refound to a different scope: + +Warning: Cannot bind an instance to a static closure, this will be an error in PHP 9 in %s on line %d +Value of type null is not callable diff --git a/Zend/tests/partial_application/rebinding_003.phpt b/Zend/tests/partial_application/rebinding_003.phpt new file mode 100644 index 0000000000000..7b8a29231c32c --- /dev/null +++ b/Zend/tests/partial_application/rebinding_003.phpt @@ -0,0 +1,65 @@ +--TEST-- +Rebinding PFA of Closure rebinds inner Closure +--FILE-- +f(?); +$g = $c->g(?); + +echo "# Can be rebound to \$this of the same class:\n"; +$f->bindTo(new C)($c); + +echo "# Can be rebound to \$this of a sub-class:\n"; +$f->bindTo(new SubClass)($c); + +echo "# Cannot be rebound to an unrelated class:\n"; +try { + $f->bindTo(new Unrelated)($c); +} catch (Error $e) { + echo $e->getMessage(), "\n"; +} + +echo "# Cannot unbind \$this on instance method:\n"; +try { + $f->bindTo(null)($c); +} catch (Error $e) { + echo $e->getMessage(), "\n"; +} + +echo "# Can unbind \$this on static method:\n"; +try { + $g->bindTo(null)($c); +} catch (Error $e) { + echo $e->getMessage(), "\n"; +} + +?> +--EXPECTF-- +# Can be rebound to $this of the same class: +object(C)#%d (0) { +} +bool(false) +# Can be rebound to $this of a sub-class: +object(SubClass)#%d (0) { +} +bool(false) +# Cannot be rebound to an unrelated class: + +Warning: Cannot bind method C::{closure:%s:%d}() to object of class Unrelated, this will be an error in PHP 9 in %s on line %d +Value of type null is not callable +# Cannot unbind $this on instance method: + +Warning: Cannot unbind $this of method, this will be an error in PHP 9 in %s on line %d +Value of type null is not callable +# Can unbind $this on static method: +string(1) "C" diff --git a/Zend/tests/partial_application/recorded_warnings.phpt b/Zend/tests/partial_application/recorded_warnings.phpt new file mode 100644 index 0000000000000..f25be826f5250 --- /dev/null +++ b/Zend/tests/partial_application/recorded_warnings.phpt @@ -0,0 +1,14 @@ +--TEST-- +PFA compilation warnings are recorded and replayed +--FILE-- + +--EXPECTF-- +Deprecated: Using "_" as a type name is deprecated since 8.4 in %s on line 3 + +Deprecated: Using "_" as a type name is deprecated since 8.4 in %s on line 5 diff --git a/Zend/tests/partial_application/references_001.phpt b/Zend/tests/partial_application/references_001.phpt new file mode 100644 index 0000000000000..4a6a8663d8708 --- /dev/null +++ b/Zend/tests/partial_application/references_001.phpt @@ -0,0 +1,24 @@ +--TEST-- +PFA returns by val if the actual function does +--FILE-- + +--EXPECT-- +array(1) { + [0]=> + &string(49) "unchanged because foo() doesn't take by reference" +} +string(49) "unchanged because foo() doesn't take by reference" diff --git a/Zend/tests/partial_application/references_002.phpt b/Zend/tests/partial_application/references_002.phpt new file mode 100644 index 0000000000000..c203b4fb907af --- /dev/null +++ b/Zend/tests/partial_application/references_002.phpt @@ -0,0 +1,24 @@ +--TEST-- +PFA receives by ref if the actual function does +--FILE-- + +--EXPECT-- +array(1) { + [0]=> + &int(2) +} +int(2) diff --git a/Zend/tests/partial_application/references_003.phpt b/Zend/tests/partial_application/references_003.phpt new file mode 100644 index 0000000000000..be116b06c79f5 --- /dev/null +++ b/Zend/tests/partial_application/references_003.phpt @@ -0,0 +1,20 @@ +--TEST-- +PFA receives by ref if the actual function does +--FILE-- +getMessage(), "\n"; +} + +?> +--EXPECTF-- +{closure:%s}(): Argument #1 ($b) could not be passed by reference diff --git a/Zend/tests/partial_application/references_004.phpt b/Zend/tests/partial_application/references_004.phpt new file mode 100644 index 0000000000000..d3b85856b37e1 --- /dev/null +++ b/Zend/tests/partial_application/references_004.phpt @@ -0,0 +1,42 @@ +--TEST-- +PFA receives variadic param by ref if the actual function does +--FILE-- + +--EXPECTF-- +Closure [ static function {closure:%s:%d} ] { + @@ %sreferences_004.php 13 - 13 + + - Bound Variables [2] { + Variable #0 [ $a ] + Variable #1 [ $args0 ] + } + + - Parameters [2] { + Parameter #0 [ &$args1 ] + Parameter #1 [ &...$args ] + } +} + +int(-2) +int(-3) +int(-4) diff --git a/Zend/tests/partial_application/references_005.phpt b/Zend/tests/partial_application/references_005.phpt new file mode 100644 index 0000000000000..e8c7c27a07be6 --- /dev/null +++ b/Zend/tests/partial_application/references_005.phpt @@ -0,0 +1,26 @@ +--TEST-- +PFA inherits return by ref +--FILE-- + +--EXPECT-- +array(1) { + [0]=> + int(1) +} +array(1) { + [0]=> + int(1) +} diff --git a/Zend/tests/partial_application/reflection_001.phpt b/Zend/tests/partial_application/reflection_001.phpt new file mode 100644 index 0000000000000..4ee997f562db1 --- /dev/null +++ b/Zend/tests/partial_application/reflection_001.phpt @@ -0,0 +1,48 @@ +--TEST-- +PFA reflection: required parameters +--FILE-- + +--EXPECTF-- +Closure [ static function {closure:%s:%d} ] { + @@ %sreflection_001.php 6 - 6 + + - Parameters [3] { + Parameter #0 [ $a = 1 ] + Parameter #1 [ $b = 5 ] + Parameter #2 [ $c = 10 ] + } +} +Closure [ static function {closure:%s:%d} ] { + @@ %sreflection_001.php 10 - 10 + + - Parameters [3] { + Parameter #0 [ $a = 1 ] + Parameter #1 [ $b = 5 ] + Parameter #2 [ $c = 10 ] + } +} +Closure [ static function {closure:%s:%d} ] { + @@ %sreflection_001.php 14 - 14 + + - Parameters [3] { + Parameter #0 [ $a = 1 ] + Parameter #1 [ $b = 5 ] + Parameter #2 [ $c = 10 ] + } +} diff --git a/Zend/tests/partial_application/reflection_002.phpt b/Zend/tests/partial_application/reflection_002.phpt new file mode 100644 index 0000000000000..da91a7af50cfe --- /dev/null +++ b/Zend/tests/partial_application/reflection_002.phpt @@ -0,0 +1,57 @@ +--TEST-- +PFA reflection: variadics +--FILE-- + +--EXPECTF-- +Closure [ static function {closure:%s:%d} ] { + @@ %s 6 - 6 + + - Parameters [1] { + Parameter #0 [ $a ] + } +} +Closure [ static function {closure:%s:%d} ] { + @@ %s 10 - 10 + + - Parameters [2] { + Parameter #0 [ $a ] + Parameter #1 [ ...$b ] + } +} +Closure [ static function {closure:%s:%d} ] { + @@ %s 14 - 14 + + - Parameters [2] { + Parameter #0 [ $a ] + Parameter #1 [ $b0 ] + } +} +Closure [ static function {closure:%s:%d} ] { + @@ %s 18 - 18 + + - Parameters [3] { + Parameter #0 [ $a ] + Parameter #1 [ $b0 ] + Parameter #2 [ $b1 ] + } +} diff --git a/Zend/tests/partial_application/reflection_003.phpt b/Zend/tests/partial_application/reflection_003.phpt new file mode 100644 index 0000000000000..90506d38a778a --- /dev/null +++ b/Zend/tests/partial_application/reflection_003.phpt @@ -0,0 +1,43 @@ +--TEST-- +PFA reflection: internal with variadics +--FILE-- + +--EXPECTF-- +Closure [ static function {closure:%s:%d} ] { + @@ %sreflection_003.php 2 - 2 + + - Parameters [1] { + Parameter #0 [ string $format ] + } + - Return [ string ] +} +Closure [ static function {closure:%s:%d} ] { + @@ %sreflection_003.php 6 - 6 + + - Parameters [2] { + Parameter #0 [ string $format ] + Parameter #1 [ mixed ...$values ] + } + - Return [ string ] +} +Closure [ static function {closure:%s:%d} ] { + @@ %sreflection_003.php 10 - 10 + + - Parameters [2] { + Parameter #0 [ string $format ] + Parameter #1 [ mixed $values0 ] + } + - Return [ string ] +} diff --git a/Zend/tests/partial_application/reflection_004.phpt b/Zend/tests/partial_application/reflection_004.phpt new file mode 100644 index 0000000000000..7226383f5afa0 --- /dev/null +++ b/Zend/tests/partial_application/reflection_004.phpt @@ -0,0 +1,16 @@ +--TEST-- +PFA reflection: ReflectionFunction::isAnonymous() is true for partials +--FILE-- +isAnonymous()); + +var_dump((new ReflectionFunction(function () {}))->isAnonymous()); + +var_dump((new ReflectionFunction(sprintf(?)))->isAnonymous()); + +?> +--EXPECT-- +bool(false) +bool(true) +bool(true) diff --git a/Zend/tests/partial_application/reflection_005.phpt b/Zend/tests/partial_application/reflection_005.phpt new file mode 100644 index 0000000000000..be86270c004c9 --- /dev/null +++ b/Zend/tests/partial_application/reflection_005.phpt @@ -0,0 +1,16 @@ +--TEST-- +PFA reflection: ReflectionFunction::isClosure() is true for partials +--FILE-- +isClosure()); + +var_dump((new ReflectionFunction(function () {}))->isClosure()); + +var_dump((new ReflectionFunction(sprintf(?)))->isClosure()); + +?> +--EXPECT-- +bool(false) +bool(true) +bool(true) diff --git a/Zend/tests/partial_application/relative_return_types.phpt b/Zend/tests/partial_application/relative_return_types.phpt new file mode 100644 index 0000000000000..f34e1889d394e --- /dev/null +++ b/Zend/tests/partial_application/relative_return_types.phpt @@ -0,0 +1,133 @@ +--TEST-- +PFA supports 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-- +Closure [ public method {closure:%s:%d} ] { + @@ %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 +Closure [ public method {closure:%s:%d} ] { + @@ %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 +Closure [ public method {closure:%s:%d} ] { + @@ %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 +Closure [ public method {closure:%s:%d} ] { + @@ %s.php 61 - 61 + + - Parameters [1] { + Parameter #0 [ object $o ] + } + - Return [ static ] +} + +object(D)#%d (0) { +} +object(D)#%d (0) { +} +C::getStatic(): Return value must be of type D, stdClass returned diff --git a/Zend/tests/partial_application/return_type.phpt b/Zend/tests/partial_application/return_type.phpt new file mode 100644 index 0000000000000..ae3738e2c660c --- /dev/null +++ b/Zend/tests/partial_application/return_type.phpt @@ -0,0 +1,20 @@ +--TEST-- +PFA inherits return type +--FILE-- + +--EXPECTF-- +Closure [ static function {closure:%s:%d} ] { + @@ %s 4 - 4 + + - Bound Variables [1] { + Variable #0 [ $a ] + } + + - Parameters [0] { + } + - Return [ array ] +} diff --git a/Zend/tests/partial_application/rfc_examples.inc b/Zend/tests/partial_application/rfc_examples.inc new file mode 100644 index 0000000000000..f6e7d9f6266dd --- /dev/null +++ b/Zend/tests/partial_application/rfc_examples.inc @@ -0,0 +1,75 @@ + [$pfa, $closure]) { + echo "# ", $test, ": "; + $pfaReflector = new ReflectionFunction($pfa); + $closureReflector = new ReflectionFunction($closure); + + try { + if (count($pfaReflector->getParameters()) !== count($closureReflector->getParameters())) { + throw new Exception(sprintf( + "Arity does not match: expected %d, got %d", + count($closureReflector->getParameters()), + count($pfaReflector->getParameters()), + )); + } + + $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(), + )); + } + } + + $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, + '' => "mixed($i)", + }; + } + + if ($pfaReflector->getClosureThis() !== $closureReflector->getClosureThis()) { + throw new Exception("\$this differs"); + } + + if ($pfa(...$args) !== $closure(...$args)) { + throw new Exception("PFA is not equivalent to closure"); + } + } catch (Exception $e) { + echo $e->getMessage(), "\n"; + echo $pfaReflector; + echo $closureReflector; + return; + } + + echo "Ok\n"; + } +} diff --git a/Zend/tests/partial_application/rfc_examples_const_expr.phpt b/Zend/tests/partial_application/rfc_examples_const_expr.phpt new file mode 100644 index 0000000000000..e2fc574402565 --- /dev/null +++ b/Zend/tests/partial_application/rfc_examples_const_expr.phpt @@ -0,0 +1,25 @@ +--TEST-- +PFA RFC examples: "Constant expressions" section +--XFAIL-- +PFA in constant expressions not implemented yet +--FILE-- + +==DONE== +--EXPECTF-- +==DONE== diff --git a/Zend/tests/partial_application/rfc_examples_debug.phpt b/Zend/tests/partial_application/rfc_examples_debug.phpt new file mode 100644 index 0000000000000..be06e24229f7d --- /dev/null +++ b/Zend/tests/partial_application/rfc_examples_debug.phpt @@ -0,0 +1,25 @@ +--TEST-- +PFA RFC examples: "Debug output" section +--FILE-- + +--EXPECTF-- +Fatal error: Uncaught Exception in %s:%d +Stack trace: +#0 %s(%d): g() +#1 %s(%d): f(1, Object(SensitiveParameterValue), 3) +#2 %s(%d): {closure:pfa:%s}(1, Object(SensitiveParameterValue)) +#3 {main} + thrown in %s on line %d diff --git a/Zend/tests/partial_application/rfc_examples_errors.phpt b/Zend/tests/partial_application/rfc_examples_errors.phpt new file mode 100644 index 0000000000000..21818d7446e33 --- /dev/null +++ b/Zend/tests/partial_application/rfc_examples_errors.phpt @@ -0,0 +1,34 @@ +--TEST-- +PFA RFC examples: "Error examples" section +--FILE-- +getMessage(), "\n"; +} + +try { + stuff(?, ?, ?, ?, ?, ?); +} catch (Error $e) { + echo $e::class, ": ", $e->getMessage(), "\n"; +} + +try { + stuff(?, ?, 3.5, $point, i: 5); +} catch (Error $e) { + echo $e::class, ": ", $e->getMessage(), "\n"; +} + +?> +--EXPECT-- +ArgumentCountError: Partial application of stuff() expects at least 4 arguments, 1 given +ArgumentCountError: Partial application of stuff() expects at most 5 arguments, 6 given +Error: Named parameter $i overwrites previous placeholder diff --git a/Zend/tests/partial_application/rfc_examples_eval_order.phpt b/Zend/tests/partial_application/rfc_examples_eval_order.phpt new file mode 100644 index 0000000000000..b54ce0ce15e42 --- /dev/null +++ b/Zend/tests/partial_application/rfc_examples_eval_order.phpt @@ -0,0 +1,30 @@ +--TEST-- +PFA RFC examples: "Evaluation order" section +--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_extra_args.phpt b/Zend/tests/partial_application/rfc_examples_extra_args.phpt new file mode 100644 index 0000000000000..f6a24df8cc713 --- /dev/null +++ b/Zend/tests/partial_application/rfc_examples_extra_args.phpt @@ -0,0 +1,54 @@ +--TEST-- +PFA RFC examples: "Variadics, func_get_args(), and extraneous arguments" section +--FILE-- + [ + foo(1, ?), + static function (int $j) { return foo(1, $j); }, + ], + 'If a PFA call has a ... placeholder, then any extraneous arguments will be passed through to the underlying function' => [ + foo(1, ?, ...), + static function (int $j) { return foo(1, $j, ...array_slice(func_get_args(), 1)); }, + ], + 'If a PFA call has a ... placeholder and the underlying function is variadic, then the trailing arguments will be forwarded directly but will get “collected” by the variadic parameter as normal' => [ + foo2(1, ?, ...), + static function (int $j, ...$extra) { return foo2(1, $j, ...$extra); }, + ], +]; + +check_equivalence($tests); + +echo "# The extra parameter here will be passed to the closure object, which will simply ignore it:\n"; +var_dump(foo(1, ?)(4, 'ignore me')); + +echo "# The extra parameter here will be passed to the closure object, which will forward it directly to the underlying function. It will be accessible only via ''func_get_args()'' et al:\n"; +var_dump(foo(1, ?, ...)(4, 'ignore me')); + +echo "# The extra parameter here will be passed to the closure object, which will forward it directly to the underlying function. It will show up as part of the \$extra array:\n"; +var_dump(foo2(1, ?, ...)(4, 'ignore me')); + +?> +==DONE== +--EXPECT-- +# If a PFA call has no ... placeholder, then any extraneous arguments to the resulting closure will be ignored. That is consistent with how manually writing the equivalent closure would behave, and is the same regardless of whether the underlying function is variadic: Ok +# If a PFA call has a ... placeholder, then any extraneous arguments will be passed through to the underlying function: Ok +# If a PFA call has a ... placeholder and the underlying function is variadic, then the trailing arguments will be forwarded directly but will get “collected” by the variadic parameter as normal: Ok +# The extra parameter here will be passed to the closure object, which will simply ignore it: +int(2) +# The extra parameter here will be passed to the closure object, which will forward it directly to the underlying function. It will be accessible only via ''func_get_args()'' et al: +int(3) +# The extra parameter here will be passed to the closure object, which will forward it directly to the underlying function. It will show up as part of the $extra array: +int(3) +==DONE== diff --git a/Zend/tests/partial_application/rfc_examples_incompatible_functions.phpt b/Zend/tests/partial_application/rfc_examples_incompatible_functions.phpt new file mode 100644 index 0000000000000..2455252b3eba9 --- /dev/null +++ b/Zend/tests/partial_application/rfc_examples_incompatible_functions.phpt @@ -0,0 +1,14 @@ +--TEST-- +PFA RFC examples: "Incompatible functions" section +--FILE-- +getMessage(), "\n"; +} + +?> +--EXPECT-- +Error: Cannot call func_get_args() dynamically diff --git a/Zend/tests/partial_application/rfc_examples_magic_methods.phpt b/Zend/tests/partial_application/rfc_examples_magic_methods.phpt new file mode 100644 index 0000000000000..bf3ce3404e662 --- /dev/null +++ b/Zend/tests/partial_application/rfc_examples_magic_methods.phpt @@ -0,0 +1,47 @@ +--TEST-- +PFA RFC examples: "Magic methods" section +--FILE-- + [ + $f->method(?, ?), + (function ($f) { + return fn($args0, $args1) => $f->method($args0, $args1); + })($f)->bindTo($f), + ], +]); + +try { + $f->method(?, ?)(a: 1, b: 2); +} catch (Error $e) { + echo $e::class, ": ", $e->getMessage(), "\n"; +} + +?> +--EXPECT-- +# Test 1: Foo::method +Array +( + [0] => mixed(0) + [1] => mixed(1) +) +Foo::method +Array +( + [0] => mixed(0) + [1] => mixed(1) +) +Ok +Error: Unknown named parameter $a diff --git a/Zend/tests/partial_application/rfc_examples_overview.phpt b/Zend/tests/partial_application/rfc_examples_overview.phpt new file mode 100644 index 0000000000000..b6fc8c0586c2f --- /dev/null +++ b/Zend/tests/partial_application/rfc_examples_overview.phpt @@ -0,0 +1,44 @@ +--TEST-- +PFA RFC examples: "Overview" section +--FILE-- + [ + foo(1, ?, 3, 4), + static fn(int $b): int => foo(1, $b, 3, 4), + ], + 'Test 2' => [ + foo(1, ?, 3, ?), + static fn(int $b, int $d): int => foo(1, $b, 3, $d), + ], + 'Test 3' => [ + foo(1, ...), + static fn(int $b, int $c, int $d): int => foo(1, $b, $c, $d), + ], + 'Test 4' => [ + foo(1, 2, ...), + static fn(int $c, int $d): int => foo(1, 2, $c, $d), + ], + 'Test 5' => [ + foo(1, ?, 3, ...), + static fn(int $b, int $d): int => foo(1, $b, 3, $d), + ], +]; + +check_equivalence($tests); + +?> +--EXPECT-- +# Test 1: Ok +# Test 2: Ok +# Test 3: Ok +# Test 4: Ok +# Test 5: Ok diff --git a/Zend/tests/partial_application/rfc_examples_scoping.phpt b/Zend/tests/partial_application/rfc_examples_scoping.phpt new file mode 100644 index 0000000000000..071b4a5a5a398 --- /dev/null +++ b/Zend/tests/partial_application/rfc_examples_scoping.phpt @@ -0,0 +1,113 @@ +--TEST-- +PFA RFC examples: "Scoping" section +--FILE-- + [ + foo(?, ?), + static fn(int $i, int $j = 0): string => foo($i, $j), + ], + 'Static closure 2' => [ + Foo::bar(?, ?), + static fn(int $i, int $j): string => Foo::bar($i, $j), + ], + 'Static closure 3' => [ + foo(?, ?)(1, ?), + static fn(int $j = 0): string => foo(1, $j), + ], + 'Static closure 4' => [ + foo(...)(?), + static fn(int $i): string => foo($i, 0), + ], +]; + +check_equivalence($tests); + +$c = new C(); +$f = $c->f(?); + +echo "# Cannot unbind \$this:\n"; +var_dump($f->bindTo(null, C::class)); // Warning: Cannot unbind $this of method, this will be an error in PHP 9 (returns null) + +echo "# Cannot rebind scope:\n"; +var_dump($f->bindTo($c, CSubClass::class)); // Warning: Cannot rebind scope of closure created from method, this will be an error in PHP 9 (returns null) + +echo "# Can rebind \$this with subclass:\n"; +var_dump($f->bindTo(new CSubClass, C::class)); // Allowed + +echo "# Cannot rebind \$this with unrelated class:\n"; +$f = $f->bindTo(new Unrelated, C::class); // Warning: Cannot bind method C::{closure:/path/to/test.php:11}() to object of class Unrelated, this will be an error in PHP 9 (returns null) + +echo "# self resolution:\n"; +$c = new CSubClass(); +var_dump($c->f(?)(1)); // string(1) "C" +var_dump($c->g(?)(1)); // string(9) "CSubClass" +var_dump($c->h(1)(1)); // string(1) "C" + +?> +--EXPECTF-- +# Static closure 1: Ok +# Static closure 2: Ok +# Static closure 3: Ok +# Static closure 4: Ok +# Cannot unbind $this: + +Warning: Cannot unbind $this of method, this will be an error in PHP 9 in %s on line %d +NULL +# Cannot rebind scope: + +Warning: Cannot rebind scope of closure created from method, this will be an error in PHP 9 in %s on line %d +NULL +# Can rebind $this with subclass: +object(Closure)#%d (5) { + ["name"]=> + string(%d) "{closure:pfa:%s}" + ["file"]=> + string(%d) "%s" + ["line"]=> + int(53) + ["this"]=> + object(CSubClass)#%d (0) { + } + ["parameter"]=> + array(1) { + ["$a"]=> + string(10) "" + } +} +# Cannot rebind $this with unrelated class: + +Warning: Cannot bind method C::{closure:pfa:%s}() to object of class Unrelated, this will be an error in PHP 9 in %s on line %d +# self resolution: +string(1) "C" +string(9) "CSubClass" +string(1) "C" diff --git a/Zend/tests/partial_application/rfc_examples_semantics.phpt b/Zend/tests/partial_application/rfc_examples_semantics.phpt new file mode 100644 index 0000000000000..a3ea25c30d6d5 --- /dev/null +++ b/Zend/tests/partial_application/rfc_examples_semantics.phpt @@ -0,0 +1,30 @@ +--TEST-- +PFA RFC examples: "Placeholder Semantics" section +--FILE-- + [ + foo(?, ?, ?, ?), + static fn(int $a, int $b, string $c0, string $c1) => foo($a, $b, $c0, $c1), + ], + 'Test 2' => [ + stuff(1, ?, p: ?, f: 3.14, ...), + static fn(string $s, Point $p, int $m = 0) => stuff(1, $s, 3.14, $p, $m), + ], +]; + +check_equivalence($tests); + +?> +--EXPECT-- +# Test 1: Ok +# Test 2: Ok diff --git a/Zend/tests/partial_application/rfc_examples_semantics_examples.phpt b/Zend/tests/partial_application/rfc_examples_semantics_examples.phpt new file mode 100644 index 0000000000000..9ee52d2786897 --- /dev/null +++ b/Zend/tests/partial_application/rfc_examples_semantics_examples.phpt @@ -0,0 +1,217 @@ +--TEST-- +PFA RFC examples: "Placeholder Semantics: Examples" section +--FILE-- + [ + stuff(?, ?, ...), + static fn(int $i1, string $s2, float $f3, Point $p4, int $m5 = 0): array + => stuff($i1, $s2, $f3, $p4, $m5), + ], + 'The degenerate "first class callables" case. (Supported since 8.1)' => [ + stuff(...), + static fn(int $i1, string $s2, float $f3, Point $p4, int $m5 = 0): array + => stuff($i1, $s2, $f3, $p4, $m5), + ], + 'Provide some values, require the rest to be provided later (1)' => [ + stuff(1, 'hi', ?, ?, ?), + static fn(float $f3, Point $p4, int $m5 = 0): array => stuff(1, 'hi', $f3, $p4, $m5), + ], + 'Provide some values, require the rest to be provided later (2)' => [ + stuff(1, 'hi', ...), + static fn(float $f3, Point $p4, int $m5 = 0): array => stuff(1, 'hi', $f3, $p4, $m5), + ], + 'Provide some values, but not just from the left (1)' => [ + stuff(1, ?, 3.5, ?, ?), + static fn(string $s2, Point $p4, int $m5 = 0): array => stuff(1, $s2, 3.5, $p4, $m5), + ], + 'Provide some values, but not just from the left (2)' => [ + stuff(1, ?, 3.5, ...), + static fn(string $s2, Point $p4, int $m5 = 0): array => stuff(1, $s2, 3.5, $p4, $m5), + ], + 'Provide just the last value' => [ + stuff(?, ?, ?, ?, 5), + static fn(int $i1, string $s2, float $f3, Point $p4): array + => stuff($i1, $s2, $f3, $p4, 5), + ], + 'Not accounting for an optional argument means it will always get its default value' => [ + stuff(?, ?, ?, ?), + static fn(int $i1, string $s2, float $f3, Point $p4): array + => stuff($i1, $s2, $f3, $p4), + ], + 'Named arguments can be pulled "out of order", and still work (1)' => [ + stuff(?, ?, f3: 3.5, p4: $point), + static fn(int $i1, string $s2): array => stuff($i1, $s2, 3.5, $point), + ], + 'Named arguments can be pulled "out of order", and still work (2)' => [ + stuff(?, ?, p4: $point, f3: 3.5), + static fn(int $i1, string $s2): array => stuff($i1, $s2, 3.5, $point), + ], + + 'But named placeholders adopt the order listed' => [ + stuff(s2: ?, i1: ?, p4: ?, f3: 3.5), + static fn(string $s2, int $i1, Point $p4): array => stuff($i1, $s2, 3.5, $p4), + ], + 'The ... "everything else" placeholder respects named arguments' => [ + stuff(?, ?, f3: 3.5, p4: $point, ...), + static fn(int $i1, string $s2, int $m5 = 0): array => stuff($i1, $s2, 3.5, $point, $m5), + ], + 'Prefill all parameters, making a "delayed call" or "thunk"' => [ + stuff(1, 'hi', 3.4, $point, 5, ...), + static fn(): array => stuff(1, 'hi', 3.4, $point, 5), + ], + + // Variadics + + 'FCC equivalent. The signature is unchanged' => [ + things(...), + static fn(int $i1, ?float $f3 = null, Point ...$points): array => things(...[$i1, $f3, ...$points]), + ], + 'Provide some values, but allow the variadic to remain variadic' => [ + things(1, 3.14, ...), + static 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(?, ?, ?, ?), + static fn(int $i1, ?float $f3, Point $points0, Point $points1): array => things($i1, $f3, $points0, $points1), + ], + + // Esoteric examples + + 'Esoteric 1' => [ + four(...), + static fn(int $a, int $b, int $c, int $d): string => four($a, $b, $c, $d), + ], + 'Esoteric 2' => [ + four(1, 2, ...), + static fn(int $c, int $d): string => four(1, 2, $c, $d), + ], + 'Esoteric 3' => [ + four(1, 2, 3, ?), + static fn(int $d): string => four(1, 2, 3, $d), + ], + 'Esoteric 4' => [ + four(1, ?, ?, 4), + static fn(int $b, int $c): string => four(1, $b, $c, 4), + ], + 'Esoteric 5' => [ + four(1, 2, 3, 4, ...), + static fn(): string => four(1, 2, 3, 4, ...array_slice(func_get_args(), 4)), + ], + 'Esoteric 6' => [ + four(d: 4, a: 1, ...), + static fn(int $b, int $c): string => four(1, $b, $c, 4, ...array_slice(func_get_args(), 4)), + ], + 'Esoteric 7' => [ + four(c: ?, d: 4, b: ?, a: 1), + static fn(int $c, int $b): string => four(1, $b, $c, 4, ...array_slice(func_get_args(), 4)), + ], + + // Other callable styles + + 'This is allowed. Note the method is static, thus the partial closure is static' => [ + E::make(1, ?), + static fn(int $y): E => E::make(1, $y), + ], + 'Note the method is non-static, so the partial closure is non-static' => (function () { + $eMaker = E::make(1, ?); + $e = $eMaker(2); + return [ + $e->foo(?, ?, 3), + (function ($e) { + return fn(int $a, int $b): array => $e->foo($a, $b, 3); + })($e)->bindTo($e), + ]; + })(), + '$c can then be further refined' => (function () { + $eMaker = E::make(1, ?); + $e = $eMaker(2); + $c = $e->foo(?, ?, 3); + return [ + $c(1, ?), + (function ($e) { + return fn(int $b): array => $e->foo(1, $b, 3); + })($e)->bindTo($e), + ]; + })(), + 'RunMe' => (function () { + $r = new RunMe(); + return [ + $r(?, 3), + (function ($r) { + return fn(int $a): string => $r($a, 3); + })($r)->bindTo($r), + ]; + })(), +]; + +check_equivalence($tests); + +?> +--EXPECT-- +# Manually specify the first two values, and pull the rest "as is": Ok +# The degenerate "first class callables" case. (Supported since 8.1): Ok +# Provide some values, require the rest to be provided later (1): Ok +# Provide some values, require the rest to be provided later (2): Ok +# Provide some values, but not just from the left (1): Ok +# Provide some values, but not just from the left (2): Ok +# Provide just the last value: Ok +# Not accounting for an optional argument means it will always get its default value: Ok +# Named arguments can be pulled "out of order", and still work (1): Ok +# Named arguments can be pulled "out of order", and still work (2): Ok +# But named placeholders adopt the order listed: Ok +# The ... "everything else" placeholder respects named arguments: Ok +# Prefill all parameters, making a "delayed call" or "thunk": Ok +# FCC equivalent. The signature is unchanged: Ok +# Provide some values, but allow the variadic to remain variadic: Ok +# 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: Ok +# Esoteric 1: Ok +# Esoteric 2: Ok +# Esoteric 3: Ok +# Esoteric 4: Ok +# Esoteric 5: Ok +# Esoteric 6: Ok +# Esoteric 7: Ok +# This is allowed. Note the method is static, thus the partial closure is static: Ok +# Note the method is non-static, so the partial closure is non-static: Ok +# $c can then be further refined: Ok +# RunMe: Ok diff --git a/Zend/tests/partial_application/static_method_001.phpt b/Zend/tests/partial_application/static_method_001.phpt new file mode 100644 index 0000000000000..ce4151441c336 --- /dev/null +++ b/Zend/tests/partial_application/static_method_001.phpt @@ -0,0 +1,18 @@ +--TEST-- +PFA supports static methods +--FILE-- + +--EXPECTF-- +Foo::method diff --git a/Zend/tests/partial_application/statics_001.phpt b/Zend/tests/partial_application/statics_001.phpt new file mode 100644 index 0000000000000..906c0dedf121c --- /dev/null +++ b/Zend/tests/partial_application/statics_001.phpt @@ -0,0 +1,22 @@ +--TEST-- +PFA static variables are shared (001) +--FILE-- + +--EXPECTF-- +OK diff --git a/Zend/tests/partial_application/statics_002.phpt b/Zend/tests/partial_application/statics_002.phpt new file mode 100644 index 0000000000000..8e1b6cefe00ae --- /dev/null +++ b/Zend/tests/partial_application/statics_002.phpt @@ -0,0 +1,23 @@ +--TEST-- +PFA static variables are shared (002) +--FILE-- + +--EXPECT-- +OK diff --git a/Zend/tests/partial_application/statics_003.phpt b/Zend/tests/partial_application/statics_003.phpt new file mode 100644 index 0000000000000..9fb2568a67e78 --- /dev/null +++ b/Zend/tests/partial_application/statics_003.phpt @@ -0,0 +1,26 @@ +--TEST-- +PFA static variables are shared (003) +--FILE-- + +--EXPECT-- +OK diff --git a/Zend/tests/partial_application/superfluous_args_are_forwarded.phpt b/Zend/tests/partial_application/superfluous_args_are_forwarded.phpt new file mode 100644 index 0000000000000..506092655217e --- /dev/null +++ b/Zend/tests/partial_application/superfluous_args_are_forwarded.phpt @@ -0,0 +1,44 @@ +--TEST-- +PFAs forwards superfluous args iff a variadic placeholder is specified +--FILE-- + +--EXPECT-- +array(3) { + [0]=> + int(1) + [1]=> + int(2) + [2]=> + int(3) +} +array(1) { + [0]=> + int(1) +} +array(3) { + [0]=> + int(1) + [1]=> + int(2) + [2]=> + int(3) +} diff --git a/Zend/tests/partial_application/this.phpt b/Zend/tests/partial_application/this.phpt new file mode 100644 index 0000000000000..bda6900bae398 --- /dev/null +++ b/Zend/tests/partial_application/this.phpt @@ -0,0 +1,20 @@ +--TEST-- +PFA $this +--FILE-- +method(new stdClass, ...); + +$baz = $bar(new stdClass, ...); + +var_dump($foo === $baz()); +?> +--EXPECT-- +bool(true) diff --git a/Zend/tests/partial_application/variation_call_001.phpt b/Zend/tests/partial_application/variation_call_001.phpt new file mode 100644 index 0000000000000..72bccd1292b36 --- /dev/null +++ b/Zend/tests/partial_application/variation_call_001.phpt @@ -0,0 +1,31 @@ +--TEST-- +PFA variation: call +--FILE-- +method(?, new Param); + +$closure(1); + +$closure->call(new Foo(), 10); +?> +--EXPECT-- +Bar: 1, Param +Foo: 10, Param diff --git a/Zend/tests/partial_application/variation_closure_001.phpt b/Zend/tests/partial_application/variation_closure_001.phpt new file mode 100644 index 0000000000000..c84f8d05f38c6 --- /dev/null +++ b/Zend/tests/partial_application/variation_closure_001.phpt @@ -0,0 +1,23 @@ +--TEST-- +PFA variation: Closure +--FILE-- + +--EXPECTF-- +Closure [ static function {closure:%s:%d} ] { + @@ %s 6 - 6 + + - Bound Variables [2] { + Variable #0 [ $fn ] + Variable #1 [ $a ] + } + + - Parameters [1] { + Parameter #0 [ $b ] + } +} diff --git a/Zend/tests/partial_application/variation_closure_002.phpt b/Zend/tests/partial_application/variation_closure_002.phpt new file mode 100644 index 0000000000000..41abe0aaab0eb --- /dev/null +++ b/Zend/tests/partial_application/variation_closure_002.phpt @@ -0,0 +1,28 @@ +--TEST-- +PFA variation: Closure::__invoke() +--FILE-- +__invoke(1, ?); + +echo (string) new ReflectionFunction($function); + +$function(10); +?> +--EXPECTF-- +Closure [ public method {closure:%s:%d} ] { + @@ %svariation_closure_002.php 6 - 6 + + - Bound Variables [1] { + Variable #0 [ $a ] + } + + - Parameters [1] { + Parameter #0 [ $b ] + } +} +int(1) +int(10) diff --git a/Zend/tests/partial_application/variation_closure_003.phpt b/Zend/tests/partial_application/variation_closure_003.phpt new file mode 100644 index 0000000000000..da567e179853c --- /dev/null +++ b/Zend/tests/partial_application/variation_closure_003.phpt @@ -0,0 +1,46 @@ +--TEST-- +PFA variation: Closure::__invoke() with $this +--FILE-- +bar(); + +$function = $closure->__invoke(1, ?); + +echo (string) new ReflectionFunction($function); + +var_dump($function(10)); +?> +--EXPECTF-- +Closure [ public method {closure:%s:%d} ] { + @@ %svariation_closure_003.php 14 - 14 + + - Bound Variables [1] { + Variable #0 [ $a ] + } + + - Parameters [1] { + Parameter #0 [ $b ] + } +} +array(2) { + [0]=> + object(Foo)#1 (0) { + } + [1]=> + array(2) { + [0]=> + int(1) + [1]=> + int(10) + } +} diff --git a/Zend/tests/partial_application/variation_debug_001.phpt b/Zend/tests/partial_application/variation_debug_001.phpt new file mode 100644 index 0000000000000..d2f6458634e90 --- /dev/null +++ b/Zend/tests/partial_application/variation_debug_001.phpt @@ -0,0 +1,40 @@ +--TEST-- +PFA variation: var_dump(), user function +--FILE-- + +--EXPECTF-- +object(Closure)#%d (5) { + ["name"]=> + string(%d) "{closure:%s}" + ["file"]=> + string(%d) "%svariation_debug_001.php" + ["line"]=> + int(6) + ["static"]=> + array(4) { + ["b"]=> + object(stdClass)#%d (0) { + } + ["c"]=> + int(20) + ["c0"]=> + object(stdClass)#%d (0) { + } + ["extra_named_params"]=> + array(1) { + ["four"]=> + int(4) + } + } + ["parameter"]=> + array(1) { + ["$a"]=> + string(10) "" + } +} diff --git a/Zend/tests/partial_application/variation_debug_002.phpt b/Zend/tests/partial_application/variation_debug_002.phpt new file mode 100644 index 0000000000000..46f80b8b2726e --- /dev/null +++ b/Zend/tests/partial_application/variation_debug_002.phpt @@ -0,0 +1,49 @@ +--TEST-- +PFA variation: var_dump(), internal function +--FILE-- + +--EXPECTF-- +object(Closure)#%d (5) { + ["name"]=> + string(%d) "{closure:%s}" + ["file"]=> + string(%d) "%svariation_debug_002.php" + ["line"]=> + int(2) + ["static"]=> + array(3) { + ["array"]=> + array(3) { + [0]=> + int(1) + [1]=> + int(2) + [2]=> + int(3) + } + ["arrays0"]=> + array(3) { + [0]=> + int(4) + [1]=> + int(5) + [2]=> + int(6) + } + ["extra_named_params"]=> + array(1) { + ["four"]=> + object(stdClass)#%d (0) { + } + } + } + ["parameter"]=> + array(2) { + ["$callback"]=> + string(10) "" + ["$arrays"]=> + string(10) "" + } +} diff --git a/Zend/tests/partial_application/variation_ex_001.phpt b/Zend/tests/partial_application/variation_ex_001.phpt new file mode 100644 index 0000000000000..48db63e70a5ed --- /dev/null +++ b/Zend/tests/partial_application/variation_ex_001.phpt @@ -0,0 +1,14 @@ +--TEST-- +PFA variation: UAF in cleanup unfinished calls +--FILE-- +getMessage(), "\n"; +} +?> +--EXPECTF-- +ArgumentCountError: Partial application of {closure:%s:%d}() expects at most 0 arguments, 1 given diff --git a/Zend/tests/partial_application/variation_gc_001.phpt b/Zend/tests/partial_application/variation_gc_001.phpt new file mode 100644 index 0000000000000..887156afc7e1e --- /dev/null +++ b/Zend/tests/partial_application/variation_gc_001.phpt @@ -0,0 +1,19 @@ +--TEST-- +PFA variation: GC (001) +--FILE-- +method = self::__construct(new stdClass, ...); + } +} + +$foo = new Foo(new stdClass); +$foo->bar = $foo; + +echo "OK"; +?> +--EXPECT-- +OK diff --git a/Zend/tests/partial_application/variation_gc_002.phpt b/Zend/tests/partial_application/variation_gc_002.phpt new file mode 100644 index 0000000000000..31c721e8e6819 --- /dev/null +++ b/Zend/tests/partial_application/variation_gc_002.phpt @@ -0,0 +1,11 @@ +--TEST-- +PFA variation: GC (002) +--FILE-- +prop = var_dump($obj, ?); + +echo "OK"; +?> +--EXPECT-- +OK diff --git a/Zend/tests/partial_application/variation_gc_003.phpt b/Zend/tests/partial_application/variation_gc_003.phpt new file mode 100644 index 0000000000000..23cdaa1c666ff --- /dev/null +++ b/Zend/tests/partial_application/variation_gc_003.phpt @@ -0,0 +1,15 @@ +--TEST-- +PFA variation: GC (003) +--FILE-- +prop = test(?, x: $obj); + +echo "OK"; +?> +--EXPECT-- +OK diff --git a/Zend/tests/partial_application/variation_invoke_001.phpt b/Zend/tests/partial_application/variation_invoke_001.phpt new file mode 100644 index 0000000000000..a6091e955b156 --- /dev/null +++ b/Zend/tests/partial_application/variation_invoke_001.phpt @@ -0,0 +1,21 @@ +--TEST-- +PFA variation: __invoke() +--FILE-- +__invoke(32) == 42); + +try { + $foo->nothing(); +} catch (Error $ex) { + echo $ex::class, ": ", $ex->getMessage(), "\n"; +} +?> +--EXPECT-- +bool(true) +Error: Call to undefined method Closure::nothing() diff --git a/Zend/tests/partial_application/variation_nocall_001.phpt b/Zend/tests/partial_application/variation_nocall_001.phpt new file mode 100644 index 0000000000000..3fbb3ec8d8c32 --- /dev/null +++ b/Zend/tests/partial_application/variation_nocall_001.phpt @@ -0,0 +1,12 @@ +--TEST-- +PFA variation: no call args leak +--FILE-- + +--EXPECT-- +OK diff --git a/Zend/tests/partial_application/variation_nocall_002.phpt b/Zend/tests/partial_application/variation_nocall_002.phpt new file mode 100644 index 0000000000000..cd4823f1bd056 --- /dev/null +++ b/Zend/tests/partial_application/variation_nocall_002.phpt @@ -0,0 +1,30 @@ +--TEST-- +PFA variation: no call, order of destruction +--FILE-- +id, "\n"; + } +} +$foo = new Foo; +$f = $foo->method(?); +$g = $f(?); + +$map = new WeakMap(); +$map[$f] = new Dtor(1); +$map[$g] = new Dtor(2); + +unset($f); +unset($g); + +echo "OK"; +?> +--EXPECT-- +Dtor::__destruct 2 +Dtor::__destruct 1 +OK diff --git a/Zend/tests/partial_application/variation_parent_001.phpt b/Zend/tests/partial_application/variation_parent_001.phpt new file mode 100644 index 0000000000000..6b7545daf2d02 --- /dev/null +++ b/Zend/tests/partial_application/variation_parent_001.phpt @@ -0,0 +1,75 @@ +--TEST-- +PFA variation: parent +--FILE-- +method(10, ...); +$baz = $bar(20, ...); + +var_dump($baz, $baz()); +?> +--EXPECTF-- +object(Closure)#%d (6) { + ["name"]=> + string(%d) "{closure:%s}" + ["file"]=> + string(%d) "%svariation_parent_001.php" + ["line"]=> + int(12) + ["static"]=> + array(2) { + ["fn"]=> + object(Closure)#%d (6) { + ["name"]=> + string(%d) "{closure:%s:%d}" + ["file"]=> + string(%d) "%s" + ["line"]=> + int(11) + ["static"]=> + array(1) { + ["a"]=> + int(10) + } + ["this"]=> + object(Foo)#%d (0) { + } + ["parameter"]=> + array(2) { + ["$b"]=> + string(10) "" + ["$c"]=> + string(10) "" + } + } + ["b"]=> + int(20) + } + ["this"]=> + object(Foo)#%d (0) { + } + ["parameter"]=> + array(1) { + ["$c"]=> + string(10) "" + } +} +object(Closure)#%d (4) { + ["name"]=> + string(25) "{closure:Foo::method():4}" + ["file"]=> + string(%d) "%s" + ["line"]=> + int(4) + ["this"]=> + object(Foo)#%d (0) { + } +} diff --git a/Zend/tests/partial_application/variation_scope_001.phpt b/Zend/tests/partial_application/variation_scope_001.phpt new file mode 100644 index 0000000000000..0dcea0921c436 --- /dev/null +++ b/Zend/tests/partial_application/variation_scope_001.phpt @@ -0,0 +1,18 @@ +--TEST-- +PFA variation: called scope +--FILE-- +method(new stdClass, ...); + +$bar(); +?> +--EXPECT-- +Foo::method diff --git a/Zend/tests/partial_application/variation_strict_001.phpt b/Zend/tests/partial_application/variation_strict_001.phpt new file mode 100644 index 0000000000000..835f6e1380557 --- /dev/null +++ b/Zend/tests/partial_application/variation_strict_001.phpt @@ -0,0 +1,20 @@ +--TEST-- +PFA variation: strict_types declared +--FILE-- +getMessage()); +} +?> +--EXPECTF-- +TypeError: {closure:%s:%d}(): Argument #1 ($int) must be of type int, string given, called in %s on line %d diff --git a/Zend/tests/partial_application/variation_variadics_001.phpt b/Zend/tests/partial_application/variation_variadics_001.phpt new file mode 100644 index 0000000000000..4710ca4d3b993 --- /dev/null +++ b/Zend/tests/partial_application/variation_variadics_001.phpt @@ -0,0 +1,31 @@ +--TEST-- +PFA variation: variadics, user function +--FILE-- + +--EXPECTF-- +Closure [ static function {closure:%s:%d} ] { + @@ %s 6 - 6 + + - Bound Variables [2] { + Variable #0 [ $a ] + Variable #1 [ $b0 ] + } + + - Parameters [1] { + Parameter #0 [ ...$b ] + } +} +int(10) +int(100) +int(1000) +int(10000) diff --git a/Zend/tests/partial_application/variation_variadics_002.phpt b/Zend/tests/partial_application/variation_variadics_002.phpt new file mode 100644 index 0000000000000..4269dd0e66f0b --- /dev/null +++ b/Zend/tests/partial_application/variation_variadics_002.phpt @@ -0,0 +1,25 @@ +--TEST-- +PFA variation: variadics, internal function +--FILE-- + +--EXPECTF-- +Closure [ static function {closure:%s:%d} ] { + @@ %svariation_variadics_002.php 2 - 2 + + - Bound Variables [2] { + Variable #0 [ $format ] + Variable #1 [ $values0 ] + } + + - Parameters [1] { + Parameter #0 [ mixed ...$values ] + } + - Return [ string ] +} +100 1000 10000 diff --git a/Zend/tests/partial_application/variation_variadics_004.phpt b/Zend/tests/partial_application/variation_variadics_004.phpt new file mode 100644 index 0000000000000..55ef6cd344f15 --- /dev/null +++ b/Zend/tests/partial_application/variation_variadics_004.phpt @@ -0,0 +1,65 @@ +--TEST-- +PFA variation: variadics and optional args +--FILE-- + $day, "month" => $month, "year" => $year]; +} + +$foo = foo(year: 2006, ...); + +var_dump($foo(2)); + +$foo = foo(month: 12, ...); + +$bar = $foo(year: 2016, ...); + +var_dump($foo(2)); + +var_dump($bar(2)); + +var_dump($foo()); + +var_dump($bar()); +?> +--EXPECTF-- +array(3) { + ["day"]=> + int(2) + ["month"]=> + int(1) + ["year"]=> + int(2006) +} +array(3) { + ["day"]=> + int(2) + ["month"]=> + int(12) + ["year"]=> + int(2005) +} +array(3) { + ["day"]=> + int(2) + ["month"]=> + int(12) + ["year"]=> + int(2016) +} +array(3) { + ["day"]=> + int(1) + ["month"]=> + int(12) + ["year"]=> + int(2005) +} +array(3) { + ["day"]=> + int(1) + ["month"]=> + int(12) + ["year"]=> + int(2016) +} diff --git a/Zend/tests/partial_application/variation_variadics_006.phpt b/Zend/tests/partial_application/variation_variadics_006.phpt new file mode 100644 index 0000000000000..cda62a2f3bfb6 --- /dev/null +++ b/Zend/tests/partial_application/variation_variadics_006.phpt @@ -0,0 +1,17 @@ +--TEST-- +PFA variation: named may overwrite variadic placeholder +--FILE-- + +--EXPECTF-- +array(1) { + [0]=> + string(1) "a" +} diff --git a/Zend/tests/partial_application/variation_variadics_007.phpt b/Zend/tests/partial_application/variation_variadics_007.phpt new file mode 100644 index 0000000000000..9624e05b449b6 --- /dev/null +++ b/Zend/tests/partial_application/variation_variadics_007.phpt @@ -0,0 +1,14 @@ +--TEST-- +PFA variation: extra through variadic +--FILE-- + $a + $b)); +?> +--EXPECT-- +int(3) diff --git a/Zend/tests/partial_application/variation_variadics_008.phpt b/Zend/tests/partial_application/variation_variadics_008.phpt new file mode 100644 index 0000000000000..4190bb759e68f --- /dev/null +++ b/Zend/tests/partial_application/variation_variadics_008.phpt @@ -0,0 +1,16 @@ +--TEST-- +PFA variation: variadics wrong signature checked +--FILE-- +getMessage() . PHP_EOL; +} +?> +--EXPECTF-- +Partial application of {closure:%s:%d}() expects at most 1 arguments, 2 given diff --git a/Zend/zend_ast.c b/Zend/zend_ast.c index e0a68a73bdf27..96b54f18c7fbd 100644 --- a/Zend/zend_ast.c +++ b/Zend/zend_ast.c @@ -1451,6 +1451,16 @@ ZEND_API zend_ast_ref * ZEND_FASTCALL zend_ast_copy(zend_ast *ast) return ref; } +ZEND_API zend_ast * ZEND_FASTCALL zend_ast_dup(zend_ast *ast) +{ + ZEND_ASSERT(ast != NULL); + + void *buf = zend_ast_alloc(zend_ast_tree_size(ast)); + zend_ast_tree_copy(ast, buf); + + return buf; +} + ZEND_API void ZEND_FASTCALL zend_ast_destroy(zend_ast *ast) { tail_call: diff --git a/Zend/zend_ast.h b/Zend/zend_ast.h index c212cd8367a87..33a43f536821a 100644 --- a/Zend/zend_ast.h +++ b/Zend/zend_ast.h @@ -353,6 +353,7 @@ ZEND_API zend_result ZEND_FASTCALL zend_ast_evaluate_ex(zval *result, zend_ast * ZEND_API zend_string *zend_ast_export(const char *prefix, zend_ast *ast, const char *suffix); ZEND_API zend_ast_ref * ZEND_FASTCALL zend_ast_copy(zend_ast *ast); +ZEND_API zend_ast * ZEND_FASTCALL zend_ast_dup(zend_ast *ast); ZEND_API void ZEND_FASTCALL zend_ast_destroy(zend_ast *ast); ZEND_API void ZEND_FASTCALL zend_ast_ref_destroy(zend_ast_ref *ast); diff --git a/Zend/zend_closures.c b/Zend/zend_closures.c index 05b6862044816..128dccac0fa17 100644 --- a/Zend/zend_closures.c +++ b/Zend/zend_closures.c @@ -28,6 +28,14 @@ #include "zend_globals.h" #include "zend_closures_arginfo.h" +/* Closure is a PFA */ +#define ZEND_PARTIAL OBJ_EXTRA_FLAG_PRIV_1 +/* Closure is a PFA of a Closure. Rebinding the PFA requires rebinding the inner Closure. */ +#define ZEND_PARTIAL_OF_CLOSURE OBJ_EXTRA_FLAG_PRIV_2 + +#define ZEND_CLOSURE_FLAGS(closure) ((closure)->std.extra_flags & (ZEND_PARTIAL|ZEND_PARTIAL_OF_CLOSURE)) +#define ZEND_CLOSURE_IS_FAKE(closure) ((closure)->func.common.fn_flags & ZEND_ACC_FAKE_CLOSURE) + typedef struct _zend_closure { zend_object std; zend_function func; @@ -41,6 +49,7 @@ ZEND_API zend_class_entry *zend_ce_closure; static zend_object_handlers closure_handlers; 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); +static void zend_create_closure_ex(zval *res, zend_function *func, zend_class_entry *scope, zend_class_entry *called_scope, zval *this_ptr, bool is_fake, uint32_t flags); ZEND_METHOD(Closure, __invoke) /* {{{ */ { @@ -78,7 +87,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->std.extra_flags & ZEND_PARTIAL); if (newthis) { if (func->common.fn_flags & ZEND_ACC_STATIC) { zend_error(E_WARNING, "Cannot bind an instance to a static closure, this will be an error in PHP 9"); @@ -161,7 +171,9 @@ ZEND_METHOD(Closure, call) if (closure->func.common.fn_flags & ZEND_ACC_GENERATOR) { zval new_closure; - zend_create_closure(&new_closure, &closure->func, newclass, closure->called_scope, newthis); + zend_create_closure_ex(&new_closure, &closure->func, newclass, + closure->called_scope, newthis, + ZEND_CLOSURE_IS_FAKE(closure), ZEND_CLOSURE_FLAGS(closure)); closure = (zend_closure *) Z_OBJ(new_closure); fci_cache.function_handler = &closure->func; @@ -177,6 +189,7 @@ ZEND_METHOD(Closure, call) memset(&fake_closure->std, 0, sizeof(fake_closure->std)); fake_closure->std.gc.refcount = 1; fake_closure->std.gc.u.type_info = GC_NULL; + fake_closure->std.extra_flags = ZEND_CLOSURE_FLAGS(closure); ZVAL_UNDEF(&fake_closure->this_ptr); fake_closure->called_scope = NULL; my_function = &fake_closure->func; @@ -223,7 +236,7 @@ ZEND_METHOD(Closure, call) } /* }}} */ -static void do_closure_bind(zval *return_value, zval *zclosure, zval *newthis, zend_object *scope_obj, zend_string *scope_str) +static zend_result do_closure_bind(zval *return_value, zval *zclosure, zval *newthis, zend_object *scope_obj, zend_string *scope_str) { zend_class_entry *ce, *called_scope; zend_closure *closure = (zend_closure *) Z_OBJ_P(zclosure); @@ -235,14 +248,15 @@ static void do_closure_bind(zval *return_value, zval *zclosure, zval *newthis, z ce = closure->func.common.scope; } else if ((ce = zend_lookup_class(scope_str)) == NULL) { zend_error(E_WARNING, "Class \"%s\" not found", ZSTR_VAL(scope_str)); - RETURN_NULL(); + RETVAL_NULL(); + return FAILURE; } } else { ce = NULL; } if (!zend_valid_closure_binding(closure, newthis, ce)) { - return; + return FAILURE; } if (newthis) { @@ -251,7 +265,31 @@ 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); + zend_create_closure_ex(return_value, &closure->func, ce, called_scope, newthis, + ZEND_CLOSURE_IS_FAKE(closure), ZEND_CLOSURE_FLAGS(closure)); + + if (ZEND_CLOSURE_FLAGS(closure) & ZEND_PARTIAL_OF_CLOSURE) { + /* Re-bind the inner closure */ + + HashTable *static_variables = ZEND_MAP_PTR_GET(closure->func.op_array.static_variables_ptr); + ZEND_ASSERT(static_variables->nNumOfElements > 0); + zval *inner = &static_variables->arData[0].val; + ZEND_ASSERT(Z_TYPE_P(inner) == IS_OBJECT && Z_OBJCE_P(inner) == zend_ce_closure); + + zval new_inner; + if (do_closure_bind(&new_inner, inner, newthis, scope_obj, scope_str) != SUCCESS) { + ZEND_UNREACHABLE(); + zval_ptr_dtor(return_value); + ZVAL_NULL(return_value); + return FAILURE; + } + + zend_object *garbage = Z_OBJ_P(inner); + ZVAL_COPY_VALUE(inner, &new_inner); + zend_object_release(garbage); + } + + return SUCCESS; } /* {{{ Create a closure from another one and bind to another object and scope */ @@ -588,8 +626,9 @@ static zend_object *zend_closure_clone(zend_object *zobject) /* {{{ */ zend_closure *closure = (zend_closure *)zobject; zval result; - zend_create_closure(&result, &closure->func, - closure->func.common.scope, closure->called_scope, &closure->this_ptr); + zend_create_closure_ex(&result, &closure->func, + closure->func.common.scope, closure->called_scope, &closure->this_ptr, + ZEND_CLOSURE_IS_FAKE(closure), ZEND_CLOSURE_FLAGS(closure)); return Z_OBJ(result); } /* }}} */ @@ -756,7 +795,7 @@ static ZEND_NAMED_FUNCTION(zend_closure_internal_handler) /* {{{ */ } /* }}} */ -static void zend_create_closure_ex(zval *res, zend_function *func, zend_class_entry *scope, zend_class_entry *called_scope, zval *this_ptr, bool is_fake) /* {{{ */ +static void zend_create_closure_ex(zval *res, zend_function *func, zend_class_entry *scope, zend_class_entry *called_scope, zval *this_ptr, bool is_fake, uint32_t flags) /* {{{ */ { zend_closure *closure; void *ptr; @@ -764,6 +803,7 @@ static void zend_create_closure_ex(zval *res, zend_function *func, zend_class_en object_init_ex(res, zend_ce_closure); closure = (zend_closure *)Z_OBJ_P(res); + closure->std.extra_flags = flags; if ((scope == NULL) && this_ptr && (Z_TYPE_P(this_ptr) != IS_UNDEF)) { /* use dummy scope if we're binding an object without specifying a scope */ @@ -861,14 +901,14 @@ static void zend_create_closure_ex(zval *res, zend_function *func, zend_class_en ZEND_API void zend_create_closure(zval *res, zend_function *func, zend_class_entry *scope, zend_class_entry *called_scope, zval *this_ptr) { zend_create_closure_ex(res, func, scope, called_scope, this_ptr, - /* is_fake */ (func->common.fn_flags & ZEND_ACC_FAKE_CLOSURE) != 0); + /* is_fake */ (func->common.fn_flags & ZEND_ACC_FAKE_CLOSURE) != 0, 0); } ZEND_API void zend_create_fake_closure(zval *res, zend_function *func, zend_class_entry *scope, zend_class_entry *called_scope, zval *this_ptr) /* {{{ */ { zend_closure *closure; - zend_create_closure_ex(res, func, scope, called_scope, this_ptr, /* is_fake */ true); + zend_create_closure_ex(res, func, scope, called_scope, this_ptr, /* is_fake */ true, 0); closure = (zend_closure *)Z_OBJ_P(res); closure->func.common.fn_flags |= ZEND_ACC_FAKE_CLOSURE; @@ -878,6 +918,16 @@ ZEND_API void zend_create_fake_closure(zval *res, zend_function *func, zend_clas } /* }}} */ +ZEND_API void zend_create_partial_closure(zval *res, zend_function *func, zend_class_entry *scope, zend_class_entry *called_scope, zval *this_ptr, bool partial_of_closure) +{ + int flags = ZEND_PARTIAL; + if (partial_of_closure) { + flags |= ZEND_PARTIAL_OF_CLOSURE; + } + zend_create_closure_ex(res, func, scope, called_scope, this_ptr, + /* is_fake */ false, flags); +} + static zend_arg_info trampoline_arg_info[1]; void zend_closure_from_frame(zval *return_value, const zend_execute_data *call) { /* {{{ */ diff --git a/Zend/zend_closures.h b/Zend/zend_closures.h index a118044c6e248..222724b1e8ba5 100644 --- a/Zend/zend_closures.h +++ b/Zend/zend_closures.h @@ -38,6 +38,7 @@ extern ZEND_API zend_class_entry *zend_ce_closure; ZEND_API void zend_create_closure(zval *res, zend_function *op_array, zend_class_entry *scope, zend_class_entry *called_scope, zval *this_ptr); ZEND_API void zend_create_fake_closure(zval *res, zend_function *op_array, zend_class_entry *scope, zend_class_entry *called_scope, zval *this_ptr); +ZEND_API void zend_create_partial_closure(zval *res, zend_function *func, zend_class_entry *scope, zend_class_entry *called_scope, zval *this_ptr, bool partial_of_closure); ZEND_API zend_function *zend_get_closure_invoke_method(zend_object *obj); ZEND_API const zend_function *zend_get_closure_method_def(zend_object *obj); ZEND_API zval* zend_get_closure_this_ptr(zval *obj); diff --git a/Zend/zend_compile.c b/Zend/zend_compile.c index 203201bcc12b5..f088d9e3830bc 100644 --- a/Zend/zend_compile.c +++ b/Zend/zend_compile.c @@ -29,6 +29,8 @@ #include "zend_exceptions.h" #include "zend_interfaces.h" #include "zend_types.h" +#include "zend_portability.h" +#include "zend_string.h" #include "zend_virtual_cwd.h" #include "zend_multibyte.h" #include "zend_language_scanner.h" @@ -3697,12 +3699,16 @@ static uint32_t zend_get_arg_num(const zend_function *fn, const zend_string *arg return (uint32_t) -1; } -static uint32_t zend_compile_args( - zend_ast *ast, const zend_function *fbc, bool *may_have_extra_named_args) /* {{{ */ +static uint32_t zend_compile_args_ex( + zend_ast *ast, const zend_function *fbc, + bool *may_have_extra_named_args, + bool is_call_partial, bool *uses_variadic_placeholder_p, + zval *named_positions) /* {{{ */ { const zend_ast_list *args = zend_ast_get_list(ast); uint32_t i; bool uses_arg_unpack = false; + bool uses_variadic_placeholder = false; uint32_t arg_count = 0; /* number of arguments not including unpacks */ /* Whether named arguments are used syntactically, to enforce language level limitations. @@ -3728,6 +3734,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 = true; if (!fbc || (fbc->common.fn_flags & ZEND_ACC_VARIADIC)) { @@ -3768,18 +3779,75 @@ static uint32_t zend_compile_args( may_have_undef = true; *may_have_extra_named_args = true; } + + if (uses_variadic_placeholder) { + zend_error_noreturn(E_COMPILE_ERROR, + "Variadic placeholder must be last"); + } } else { if (uses_arg_unpack) { zend_error_noreturn(E_COMPILE_ERROR, "Cannot use positional argument after argument unpacking"); } - if (uses_named_args) { + bool is_variadic_placeholder = arg->kind == ZEND_AST_PLACEHOLDER_ARG + && arg->attr == ZEND_PLACEHOLDER_VARIADIC; + + if (uses_named_args && !is_variadic_placeholder) { zend_error_noreturn(E_COMPILE_ERROR, "Cannot use positional argument after named argument"); } - arg_count++; + if (uses_variadic_placeholder) { + if (is_variadic_placeholder) { + zend_error_noreturn(E_COMPILE_ERROR, + "Variadic placeholder may only appear once"); + } else { + zend_error_noreturn(E_COMPILE_ERROR, + "Variadic placeholder must be last"); + } + } + + if (!is_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 == ZEND_PLACEHOLDER_VARIADIC) { + uses_variadic_placeholder = true; + /* Do not emit ZEND_SEND_PLACEHOLDER: We represent the variadic + * placeholder with a flag on the ZEND_CONVERT_CALLABLE_PARTIAL + * op instead. */ + continue; + } + + if (arg_name) { + if (Z_ISUNDEF_P(named_positions)) { + array_init(named_positions); + } + zval tmp; + ZVAL_LONG(&tmp, zend_hash_num_elements(Z_ARRVAL_P(named_positions))); + zend_hash_add(Z_ARRVAL_P(named_positions), arg_name, &tmp); + } + + opline = zend_emit_op(NULL, ZEND_SEND_PLACEHOLDER, NULL, NULL); + 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 if (arg->attr != ZEND_PLACEHOLDER_VARIADIC) { + opline->op2.opline_num = arg_num; + opline->result.var = EX_NUM_TO_VAR(arg_num - 1); + } + + continue; } /* Treat passing of $GLOBALS the same as passing a call. @@ -3892,14 +3960,24 @@ 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 { + *uses_variadic_placeholder_p = uses_variadic_placeholder; } return arg_count; } /* }}} */ +static uint32_t zend_compile_args(zend_ast *ast, const zend_function *fbc, + bool *may_have_extra_named_args) /* {{{ */ +{ + return zend_compile_args_ex(ast, fbc, may_have_extra_named_args, false, NULL, NULL); +} + ZEND_API uint8_t zend_get_call_op(const zend_op *init_op, const zend_function *fbc, bool result_used) /* {{{ */ { uint32_t no_discard = result_used ? 0 : ZEND_ACC_NODISCARD; @@ -3933,6 +4011,38 @@ ZEND_API uint8_t zend_get_call_op(const zend_op *init_op, const zend_function *f } /* }}} */ +static void zend_compile_call_partial(znode *result, uint32_t arg_count, + bool may_have_extra_named_args, bool uses_variadic_placeholder, + zval *named_positions, uint32_t opnum_init, const zend_function *fbc) { + + zend_op *init_opline = &CG(active_op_array)->opcodes[opnum_init]; + + init_opline->extended_value = arg_count; + + ZEND_ASSERT(init_opline->opcode != ZEND_NEW); + + if (init_opline->opcode == ZEND_INIT_FCALL) { + init_opline->op1.num = zend_vm_calc_used_stack(arg_count, fbc); + } + + zend_op *opline = zend_emit_op_tmp(result, ZEND_CALLABLE_CONVERT_PARTIAL, + NULL, NULL); + + opline->op1.num = zend_alloc_cache_slots(2); + + if (may_have_extra_named_args) { + opline->extended_value = ZEND_FCALL_MAY_HAVE_EXTRA_NAMED_PARAMS; + } + if (uses_variadic_placeholder) { + opline->extended_value |= ZEND_FCALL_USES_VARIADIC_PLACEHOLDER; + } + + if (!Z_ISUNDEF_P(named_positions)) { + opline->op2.constant = zend_add_literal(named_positions); + opline->op2_type = IS_CONST; + } +} + static bool zend_compile_call_common(znode *result, zend_ast *args_ast, const zend_function *fbc, uint32_t lineno) /* {{{ */ { zend_op *opline; @@ -3948,23 +4058,43 @@ static bool zend_compile_call_common(znode *result, zend_ast *args_ast, const ze zend_error_noreturn(E_COMPILE_ERROR, "Cannot create Closure for new expression"); } - zend_ast_list *args = zend_ast_get_list(((zend_ast_fcc*)args_ast)->args); - if (args->children != 1 || args->child[0]->attr != ZEND_PLACEHOLDER_VARIADIC) { - zend_error_noreturn(E_COMPILE_ERROR, "Cannot create a Closure for call expression with more than one argument, or non-variadic placeholders"); - } + zend_ast_list *fcc_args = zend_ast_get_list(((zend_ast_fcc*)args_ast)->args); - if (opcode == ZEND_INIT_FCALL) { - opline->op1.num = zend_vm_calc_used_stack(0, fbc); - } + /* FCCs are a special case of PFAs with a single variadic placeholder */ + if (fcc_args->children == 1 && fcc_args->child[0]->attr == ZEND_PLACEHOLDER_VARIADIC) { - zend_op *callable_convert_op = zend_emit_op_tmp(result, ZEND_CALLABLE_CONVERT, NULL, NULL); - if (opcode == ZEND_INIT_FCALL - || opcode == ZEND_INIT_FCALL_BY_NAME - || opcode == ZEND_INIT_NS_FCALL_BY_NAME) { - callable_convert_op->extended_value = zend_alloc_cache_slot(); - } else { - callable_convert_op->extended_value = (uint32_t)-1; + if (opline->opcode == ZEND_INIT_FCALL) { + opline->op1.num = zend_vm_calc_used_stack(0, fbc); + } + + zend_op *callable_convert_op = zend_emit_op_tmp(result, ZEND_CALLABLE_CONVERT, NULL, NULL); + if (opcode == ZEND_INIT_FCALL + || opcode == ZEND_INIT_FCALL_BY_NAME + || opcode == ZEND_INIT_NS_FCALL_BY_NAME) { + callable_convert_op->extended_value = zend_alloc_cache_slot(); + } else { + callable_convert_op->extended_value = (uint32_t)-1; + } + + return true; } + + args_ast = ((zend_ast_fcc*)args_ast)->args; + + bool may_have_extra_named_args; + bool uses_variadic_placeholder; + + zval named_positions; + ZVAL_UNDEF(&named_positions); + + uint32_t arg_count = zend_compile_args_ex(args_ast, fbc, + &may_have_extra_named_args, true, &uses_variadic_placeholder, + &named_positions); + + zend_compile_call_partial(result, arg_count, + may_have_extra_named_args, uses_variadic_placeholder, + &named_positions, opnum_init, fbc); + return true; } @@ -6571,6 +6701,76 @@ static bool zend_is_pipe_optimizable_callable_name(zend_ast *ast) return true; } +static zend_ast *zend_partial_apply(zend_ast *callable_ast, zend_ast *pipe_arg) +{ + if (callable_ast->kind != ZEND_AST_CALL + && callable_ast->kind != ZEND_AST_STATIC_CALL + && callable_ast->kind != ZEND_AST_METHOD_CALL) { + return NULL; + } + + zend_ast *args_ast = zend_ast_call_get_args(callable_ast); + if (!args_ast || args_ast->kind != ZEND_AST_CALLABLE_CONVERT) { + return NULL; + } + + if (callable_ast->kind == ZEND_AST_CALL && + !zend_is_pipe_optimizable_callable_name(callable_ast->child[0])) { + return NULL; + } + + zend_ast_list *arg_list = zend_ast_get_list(((zend_ast_fcc*)args_ast)->args); + + zend_ast *first_placeholder = NULL; + bool uses_named_args = 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) { + uses_named_args = true; + 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 in is this + * context, and will usually error due to a missing argument, + * so we don't optimize those. */ + return NULL; + } + if (arg->attr == ZEND_PLACEHOLDER_VARIADIC && uses_named_args) { + /* 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; + } + } + } + + ZEND_ASSERT(first_placeholder); + + zend_ast *new_arg_list = zend_ast_create_list(0, arg_list->kind); + for (uint32_t i = 0; i < arg_list->children; i++) { + zend_ast *arg = arg_list->child[i]; + if (arg == first_placeholder) { + new_arg_list = zend_ast_list_add(new_arg_list, pipe_arg); + } else if (arg->kind == ZEND_AST_NAMED_ARG + && arg->child[1] == first_placeholder) { + zend_ast *name = arg->child[0]; + new_arg_list = zend_ast_list_add(new_arg_list, + zend_ast_create(ZEND_AST_NAMED_ARG, name, pipe_arg)); + } else { + new_arg_list = zend_ast_list_add(new_arg_list, arg); + } + } + + return new_arg_list; +} + static void zend_compile_pipe(znode *result, zend_ast *ast) { zend_ast *operand_ast = ast->child[0]; @@ -6595,29 +6795,34 @@ static void zend_compile_pipe(znode *result, zend_ast *ast) } /* Turn the operand into a function parameter list. */ - zend_ast *arg_list_ast = zend_ast_create_list(1, ZEND_AST_ARG_LIST, zend_ast_create_znode(&wrapped_operand_result)); + zend_ast *arg = zend_ast_create_znode(&wrapped_operand_result); zend_ast *fcall_ast; znode callable_result; + 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 - && zend_is_pipe_optimizable_callable_name(callable_ast->child[0])) { - 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 */ + if ((pfa_arg_list_ast = zend_partial_apply(callable_ast, arg))) { + switch (callable_ast->kind) { + case ZEND_AST_CALL: + fcall_ast = zend_ast_create(ZEND_AST_CALL, + callable_ast->child[0], pfa_arg_list_ast); + break; + case ZEND_AST_STATIC_CALL: + fcall_ast = zend_ast_create(ZEND_AST_STATIC_CALL, + callable_ast->child[0], callable_ast->child[1], + pfa_arg_list_ast); + break; + case ZEND_AST_METHOD_CALL: + fcall_ast = zend_ast_create(ZEND_AST_METHOD_CALL, + callable_ast->child[0], callable_ast->child[1], + pfa_arg_list_ast); + break; + EMPTY_SWITCH_DEFAULT_CASE() + } /* Turn $foo |> $expr into ($expr)($foo) */ } else { + zend_ast *arg_list_ast = zend_ast_create_list(1, ZEND_AST_ARG_LIST, arg); zend_compile_expr(&callable_result, callable_ast); callable_ast = zend_ast_create_znode(&callable_result); fcall_ast = zend_ast_create(ZEND_AST_CALL, @@ -11606,6 +11811,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 != ZEND_PLACEHOLDER_VARIADIC) { + // TODO: PFAs + zend_error_noreturn(E_COMPILE_ERROR, "Constant expression contains invalid operations"); + } + ZEND_MAP_PTR_NEW(((zend_ast_fcc *)*args_ast)->fptr); switch ((*ast_ptr)->kind) { diff --git a/Zend/zend_compile.h b/Zend/zend_compile.h index 6075ae3d1e5d2..b95cdf3eab6fa 100644 --- a/Zend/zend_compile.h +++ b/Zend/zend_compile.h @@ -951,6 +951,7 @@ struct _zend_arena; ZEND_API zend_op_array *compile_file(zend_file_handle *file_handle, int type); ZEND_API zend_op_array *compile_string(zend_string *source_string, const char *filename, zend_compile_position position); ZEND_API zend_op_array *compile_filename(int type, zend_string *filename); +ZEND_API zend_op_array *zend_compile_ast(zend_ast *ast, int type, zend_string *filename); ZEND_API zend_ast *zend_compile_string_to_ast( zend_string *code, struct _zend_arena **ast_arena, zend_string *filename); ZEND_API zend_result zend_execute_scripts(int type, zval *retval, int file_count, ...); @@ -1125,7 +1126,8 @@ ZEND_API zend_string *zend_type_to_string(zend_type type); #define ZEND_THROW_IS_EXPR 1u -#define ZEND_FCALL_MAY_HAVE_EXTRA_NAMED_PARAMS 1 +#define ZEND_FCALL_MAY_HAVE_EXTRA_NAMED_PARAMS (1<<0) +#define ZEND_FCALL_USES_VARIADIC_PLACEHOLDER (1<<1) /* The send mode, the is_variadic, the is_promoted, and the is_tentative flags are stored as part of zend_type */ #define _ZEND_SEND_MODE_SHIFT _ZEND_TYPE_EXTRA_FLAGS_SHIFT diff --git a/Zend/zend_execute.c b/Zend/zend_execute.c index 518cbb98fc0f8..5659f4c9692a1 100644 --- a/Zend/zend_execute.c +++ b/Zend/zend_execute.c @@ -45,6 +45,7 @@ #include "zend_call_stack.h" #include "zend_attributes.h" #include "Optimizer/zend_func_info.h" +#include "zend_partial.h" /* Virtual current working directory support */ #include "zend_virtual_cwd.h" @@ -1231,6 +1232,13 @@ static zend_always_inline bool zend_check_type( return zend_check_type_slow(type, arg, ref, is_return_type, is_internal); } +ZEND_API bool zend_check_type_ex( + const zend_type *type, zval *arg, zend_class_entry *scope, + bool is_return_type, bool is_internal) +{ + return zend_check_type(type, arg, scope, is_return_type, is_internal); +} + ZEND_API bool zend_check_user_type_slow( const zend_type *type, zval *arg, const zend_reference *ref, bool is_return_type) { @@ -4644,6 +4652,7 @@ ZEND_API void zend_unfinished_calls_gc(zend_execute_data *execute_data, zend_exe case ZEND_DO_UCALL: case ZEND_DO_FCALL_BY_NAME: case ZEND_CALLABLE_CONVERT: + case ZEND_CALLABLE_CONVERT_PARTIAL: level++; break; case ZEND_INIT_FCALL: @@ -4700,6 +4709,7 @@ ZEND_API void zend_unfinished_calls_gc(zend_execute_data *execute_data, zend_exe case ZEND_DO_UCALL: case ZEND_DO_FCALL_BY_NAME: case ZEND_CALLABLE_CONVERT: + case ZEND_CALLABLE_CONVERT_PARTIAL: level++; break; case ZEND_INIT_FCALL: @@ -4780,6 +4790,7 @@ static void cleanup_unfinished_calls(zend_execute_data *execute_data, uint32_t o case ZEND_DO_UCALL: case ZEND_DO_FCALL_BY_NAME: case ZEND_CALLABLE_CONVERT: + case ZEND_CALLABLE_CONVERT_PARTIAL: level++; break; case ZEND_INIT_FCALL: @@ -4837,6 +4848,7 @@ static void cleanup_unfinished_calls(zend_execute_data *execute_data, uint32_t o case ZEND_DO_UCALL: case ZEND_DO_FCALL_BY_NAME: case ZEND_CALLABLE_CONVERT: + case ZEND_CALLABLE_CONVERT_PARTIAL: level++; break; case ZEND_INIT_FCALL: @@ -5552,9 +5564,10 @@ zval * ZEND_FASTCALL zend_handle_named_arg( } } else { arg = ZEND_CALL_VAR_NUM(call, arg_offset); + if (UNEXPECTED(!Z_ISUNDEF_P(arg))) { - zend_throw_error(NULL, "Named parameter $%s overwrites previous argument", - ZSTR_VAL(arg_name)); + zend_throw_error(NULL, "Named parameter $%s overwrites previous %s", + ZSTR_VAL(arg_name), Z_TYPE_P(arg) == _IS_PLACEHOLDER ? "placeholder" : "argument"); return NULL; } } diff --git a/Zend/zend_execute.h b/Zend/zend_execute.h index 8858f9fce96ae..ebec845a3d8c4 100644 --- a/Zend/zend_execute.h +++ b/Zend/zend_execute.h @@ -110,6 +110,9 @@ ZEND_API ZEND_COLD void zend_verify_never_error( ZEND_API bool zend_verify_ref_array_assignable(zend_reference *ref); ZEND_API bool zend_check_user_type_slow( const zend_type *type, zval *arg, const zend_reference *ref, bool is_return_type); +ZEND_API bool zend_check_type_ex( + const zend_type *type, zval *arg, zend_class_entry *scope, + bool is_return_type, bool is_internal); #if ZEND_DEBUG ZEND_API bool zend_internal_call_should_throw(const zend_function *fbc, zend_execute_data *call); diff --git a/Zend/zend_execute_API.c b/Zend/zend_execute_API.c index e134d3d496b6d..31d7a4191a8b1 100644 --- a/Zend/zend_execute_API.c +++ b/Zend/zend_execute_API.c @@ -39,6 +39,7 @@ #include "zend_observer.h" #include "zend_call_stack.h" #include "zend_frameless_function.h" +#include "zend_partial.h" #ifdef HAVE_SYS_TIME_H #include #endif @@ -204,6 +205,7 @@ void init_executor(void) /* {{{ */ zend_weakrefs_init(); zend_hash_init(&EG(callable_convert_cache), 8, NULL, ZVAL_PTR_DTOR, 0); + zend_hash_init(&EG(partial_function_application_cache), 8, NULL, zend_partial_op_array_dtor, 0); EG(active) = 1; } @@ -421,6 +423,7 @@ ZEND_API void zend_shutdown_executor_values(bool fast_shutdown) zend_stack_clean(&EG(user_exception_handlers), (void (*)(void *))ZVAL_PTR_DTOR, 1); zend_hash_clean(&EG(callable_convert_cache)); + zend_hash_clean(&EG(partial_function_application_cache)); #if ZEND_DEBUG if (!CG(unclean_shutdown)) { @@ -520,6 +523,7 @@ void shutdown_executor(void) /* {{{ */ } zend_hash_destroy(&EG(callable_convert_cache)); + zend_hash_destroy(&EG(partial_function_application_cache)); } #if ZEND_DEBUG diff --git a/Zend/zend_globals.h b/Zend/zend_globals.h index ef81ae5faaf25..c2d30c10dc75d 100644 --- a/Zend/zend_globals.h +++ b/Zend/zend_globals.h @@ -320,6 +320,7 @@ struct _zend_executor_globals { zend_strtod_state strtod_state; HashTable callable_convert_cache; + HashTable partial_function_application_cache; void *reserved[ZEND_MAX_RESERVED_RESOURCES]; }; diff --git a/Zend/zend_language_scanner.l b/Zend/zend_language_scanner.l index 1e26ddbd99199..fa5b0d0af2172 100644 --- a/Zend/zend_language_scanner.l +++ b/Zend/zend_language_scanner.l @@ -591,6 +591,43 @@ ZEND_API zend_result open_file_for_scanning(zend_file_handle *file_handle) return SUCCESS; } +static zend_op_array *zend_compile_ast_internal(int type) +{ + ZEND_ASSERT(CG(in_compilation)); + ZEND_ASSERT(CG(ast)); + + uint32_t last_lineno = CG(zend_lineno); + zend_file_context original_file_context; + zend_oparray_context original_oparray_context; + zend_op_array *original_active_op_array = CG(active_op_array); + + zend_op_array *op_array = emalloc(sizeof(zend_op_array)); + init_op_array(op_array, type, INITIAL_OP_ARRAY_SIZE); + CG(active_op_array) = op_array; + + /* Use heap to not waste arena memory */ + op_array->fn_flags |= ZEND_ACC_HEAP_RT_CACHE; + + if (zend_ast_process) { + zend_ast_process(CG(ast)); + } + + zend_file_context_begin(&original_file_context); + zend_oparray_context_begin(&original_oparray_context, op_array); + zend_compile_top_stmt(CG(ast)); + CG(zend_lineno) = last_lineno; + zend_emit_final_return(type == ZEND_USER_FUNCTION); + op_array->line_start = 1; + op_array->line_end = last_lineno; + pass_two(op_array); + zend_oparray_context_end(&original_oparray_context); + zend_file_context_end(&original_file_context); + + CG(active_op_array) = original_active_op_array; + + return op_array; +} + static zend_op_array *zend_compile(int type) { zend_op_array *op_array = NULL; @@ -601,34 +638,7 @@ static zend_op_array *zend_compile(int type) CG(ast_arena) = zend_arena_create(1024 * 32); if (!zendparse()) { - uint32_t last_lineno = CG(zend_lineno); - zend_file_context original_file_context; - zend_oparray_context original_oparray_context; - zend_op_array *original_active_op_array = CG(active_op_array); - - op_array = emalloc(sizeof(zend_op_array)); - init_op_array(op_array, type, INITIAL_OP_ARRAY_SIZE); - CG(active_op_array) = op_array; - - /* Use heap to not waste arena memory */ - op_array->fn_flags |= ZEND_ACC_HEAP_RT_CACHE; - - if (zend_ast_process) { - zend_ast_process(CG(ast)); - } - - zend_file_context_begin(&original_file_context); - zend_oparray_context_begin(&original_oparray_context, op_array); - zend_compile_top_stmt(CG(ast)); - CG(zend_lineno) = last_lineno; - zend_emit_final_return(type == ZEND_USER_FUNCTION); - op_array->line_start = 1; - op_array->line_end = last_lineno; - pass_two(op_array); - zend_oparray_context_end(&original_oparray_context); - zend_file_context_end(&original_file_context); - - CG(active_op_array) = original_active_op_array; + op_array = zend_compile_ast_internal(type); } zend_ast_destroy(CG(ast)); @@ -671,6 +681,23 @@ ZEND_API zend_op_array *compile_file(zend_file_handle *file_handle, int type) return op_array; } +ZEND_API zend_op_array *zend_compile_ast( + zend_ast *ast, int type, zend_string *filename) +{ + zend_string *original_compiled_filename = CG(compiled_filename); + bool original_in_compilation = CG(in_compilation); + CG(in_compilation) = 1; + CG(ast) = ast; + + zend_set_compiled_filename(filename); + zend_op_array *op_array = zend_compile_ast_internal(type); + + CG(in_compilation) = original_in_compilation; + zend_restore_compiled_filename(original_compiled_filename); + + return op_array; +} + ZEND_API zend_ast *zend_compile_string_to_ast( zend_string *code, zend_arena **ast_arena, zend_string *filename) { zval code_zv; diff --git a/Zend/zend_object_handlers.c b/Zend/zend_object_handlers.c index 88b7b1112d7b1..9a3a13b35e3d0 100644 --- a/Zend/zend_object_handlers.c +++ b/Zend/zend_object_handlers.c @@ -1685,7 +1685,6 @@ ZEND_API ZEND_ATTRIBUTE_NONNULL zend_function *zend_get_call_trampoline_func( * 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}}; if (EXPECTED(EG(trampoline).common.function_name == NULL)) { func = &EG(trampoline).op_array; @@ -1732,7 +1731,7 @@ ZEND_API ZEND_ATTRIBUTE_NONNULL zend_function *zend_get_call_trampoline_func( 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_call_trampoline_arginfo; return (zend_function*)func; } diff --git a/Zend/zend_partial.c b/Zend/zend_partial.c new file mode 100644 index 0000000000000..2a4195c4049bc --- /dev/null +++ b/Zend/zend_partial.c @@ -0,0 +1,1146 @@ +/* + +----------------------------------------------------------------------+ + | 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. | + +----------------------------------------------------------------------+ +*/ + +/** + * Partial Function Application: + * + * A partial application is compiled to the usual sequence of function call + * opcodes (INIT_FCALL, SEND_VAR, etc), but the sequence ends with a + * CALLABLE_CONVERT_PARTIAL opcode instead of DO_FCALL, similarly to + * first class callables. Placeholders are compiled to SEND_PLACEHOLDER opcodes: + * + * $f = f($a, ?) + * + * 0001 INIT_FCALL f + * 0002 SEND_VAR CV($a) + * 0003 SEND_PLACEHOLDER + * 0004 CV($f) = CALLABLE_CONVERT_PARTIAL + * + * SEND_PLACEHOLDER sets the argument slot type to _IS_PLACEHOLDER. + * + * CALLABLE_CONVERT_PARTIAL uses the information available on the stack to + * create a Closure and return it, consuming the stack frame in the process + * like an internal function call. + * + * We create the Closure by generating the relevant AST and compling it to an + * op_array. The op_array is cached in the Opcache SHM and inline caches. + * + * This file implements the Closure generation logic + * (see zend_partial_create(), zp_compile()). + */ + +#include "zend.h" +#include "zend_API.h" +#include "zend_arena.h" +#include "zend_ast.h" +#include "zend_compile.h" +#include "zend_closures.h" +#include "zend_attributes.h" +#include "zend_exceptions.h" +#include "ext/opcache/ZendAccelerator.h" + +#define Z_IS_PLACEHOLDER_P(p) (Z_TYPE_P(p) == _IS_PLACEHOLDER) + +#define IS_STATIC_CLOSURE(function) \ + (((function)->common.fn_flags & (ZEND_ACC_STATIC|ZEND_ACC_CLOSURE)) == (ZEND_ACC_STATIC|ZEND_ACC_CLOSURE)) + +static zend_never_inline ZEND_COLD void zp_args_underflow( + const zend_function *function, uint32_t args, uint32_t expected) +{ + zend_string *symbol = get_function_or_method_name(function); + const char *limit = function->common.num_args <= function->common.required_num_args ? + "exactly" : "at least"; + + zend_argument_count_error( + "Partial application of %s() expects %s %d arguments, %d given", + ZSTR_VAL(symbol), limit, expected, args); + + zend_string_release(symbol); +} + +static zend_never_inline ZEND_COLD void zp_args_overflow( + const zend_function *function, uint32_t args, uint32_t expected) +{ + zend_string *symbol = get_function_or_method_name(function); + + zend_argument_count_error( + "Partial application of %s() expects at most %d arguments, %d given", + ZSTR_VAL(symbol), expected, args); + + zend_string_release(symbol); +} + +static zend_result zp_args_check(const zend_function *function, + uint32_t argc, const zval *argv, + const zend_array *extra_named_args, + bool uses_variadic_placeholder) { + + if (extra_named_args) { + zval *arg; + zend_string *key; + ZEND_HASH_MAP_FOREACH_STR_KEY_VAL(extra_named_args, key, arg) { + if (UNEXPECTED(Z_IS_PLACEHOLDER_P(arg))) { + zend_throw_error(NULL, + "Cannot use named placeholder for unknown or variadic parameter $%s", + ZSTR_VAL(key)); + return FAILURE; + } + } ZEND_HASH_FOREACH_END(); + } + + if (argc < function->common.required_num_args) { + if (uses_variadic_placeholder) { + /* Missing args will be turned into placeholders */ + return SUCCESS; + } + + zp_args_underflow( + function, argc, function->common.required_num_args); + return FAILURE; + } else if (argc > function->common.num_args && + !(function->common.fn_flags & ZEND_ACC_VARIADIC)) { + zp_args_overflow(function, argc, function->common.num_args); + return FAILURE; + } + + return SUCCESS; +} + +static bool zp_name_exists(zend_string **names, uint32_t num_names, zend_string *name) +{ + for (uint32_t i = 0; i < num_names; i++) { + if (names[i] && zend_string_equals(names[i], name)) { + return true; + } + } + return false; +} + +static zend_string *zp_get_param_name(zend_function *function, uint32_t arg_offset) +{ + return zend_string_copy(function->common.arg_info[arg_offset].name); +} + +/* Assign a name for every variable that will be used in the generated closure, + * including params and used vars. */ +static void zp_assign_names(zend_string **names, uint32_t num_names, + uint32_t argc, zval *argv, + zend_function *function, bool variadic_partial, + zend_array *extra_named_params) +{ + /* Assign names for params. We never rename those. */ + for (uint32_t offset = 0; offset < MIN(argc, function->common.num_args); offset++) { + if (Z_IS_PLACEHOLDER_P(&argv[offset])) { + names[offset] = zp_get_param_name(function, offset); + } + } + + /* Assign name for the variadic param. Never renamed. */ + if (variadic_partial && (function->common.fn_flags & ZEND_ACC_VARIADIC)) { + names[argc] = zp_get_param_name(function, function->common.num_args); + } + + /* Assign names for placeholders that bind to the variadic param: + * + * function f($a, ...$args) {} + * f(?, ?, ...); // The second placeholder binds into the variadic param. + * + * By default these are named $origNameN with N the offset from the + * variadic param. In case of clash we increment N until a free name is + * found. */ + for (uint32_t offset = function->common.num_args; offset < argc; offset++) { + ZEND_ASSERT(function->common.fn_flags & ZEND_ACC_VARIADIC); + if (!Z_IS_PLACEHOLDER_P(&argv[offset])) { + continue; + } + int n = offset - function->common.num_args; + zend_string *orig_name = zp_get_param_name(function, function->common.num_args); + zend_string *new_name; + do { + new_name = zend_strpprintf_unchecked(0, "%S%d", orig_name, n); + if (!zp_name_exists(names, num_names, new_name)) { + break; + } + n++; + zend_string_release(new_name); + } while (true); + names[offset] = new_name; + zend_string_release(orig_name); + } + + /* Assign names for pre-bound params (lexical vars). + * There may be clashes, we ensure to generate unique names. */ + for (uint32_t offset = 0; offset < argc; offset++) { + if (Z_IS_PLACEHOLDER_P(&argv[offset]) || Z_ISUNDEF(argv[offset])) { + continue; + } + int n = -1; + zend_string *orig_name = zp_get_param_name(function, MIN(offset, function->common.num_args)); + zend_string *new_name = zend_string_copy(orig_name); + while (zp_name_exists(names, num_names, new_name)) { + zend_string_release(new_name); + n++; + new_name = zend_strpprintf_unchecked(0, "%S%d", orig_name, n); + } + names[offset] = new_name; + zend_string_release(orig_name); + } + + /* Assign name for $extra_named_params */ + if (extra_named_params) { + int n = 1; + zend_string *new_name = ZSTR_INIT_LITERAL("extra_named_params", 0); + while (zp_name_exists(names, num_names, new_name)) { + zend_string_release(new_name); + n++; + new_name = zend_strpprintf(0, "%s%d", "extra_named_params", n); + } + names[argc + variadic_partial] = new_name; + } + + /* Assign name for $fn */ + if (function->common.fn_flags & ZEND_ACC_CLOSURE) { + int n = 1; + zend_string *new_name = ZSTR_INIT_LITERAL("fn", 0); + while (zp_name_exists(names, num_names, new_name)) { + zend_string_release(new_name); + n++; + new_name = zend_strpprintf(0, "%s%d", "fn", n); + } + names[argc + variadic_partial + (extra_named_params != NULL)] = new_name; + } +} + +static bool zp_is_single_may_be_type(uint32_t type_mask) +{ + return ((type_mask > 0) && (type_mask & (type_mask - 1)) == 0) + || type_mask == MAY_BE_BOOL + || type_mask == MAY_BE_ANY; +} + +static zend_ast *zp_single_may_be_type_to_ast(uint32_t type) +{ + zend_string *name; + + switch (type) { + case MAY_BE_NULL: + name = ZSTR_KNOWN(ZEND_STR_NULL_LOWERCASE); + break; + case MAY_BE_TRUE: + name = ZSTR_KNOWN(ZEND_STR_TRUE); + break; + case MAY_BE_FALSE: + name = ZSTR_KNOWN(ZEND_STR_FALSE); + break; + case MAY_BE_LONG: + name = ZSTR_KNOWN(ZEND_STR_INT); + break; + case MAY_BE_DOUBLE: + name = ZSTR_KNOWN(ZEND_STR_FLOAT); + break; + case MAY_BE_STRING: + name = ZSTR_KNOWN(ZEND_STR_STRING); + break; + case MAY_BE_BOOL: + name = ZSTR_KNOWN(ZEND_STR_BOOL); + break; + case MAY_BE_VOID: + name = ZSTR_KNOWN(ZEND_STR_VOID); + break; + case MAY_BE_NEVER: + name = ZSTR_KNOWN(ZEND_STR_NEVER); + break; + case MAY_BE_OBJECT: + name = ZSTR_KNOWN(ZEND_STR_OBJECT); + break; + case MAY_BE_ANY: + name = ZSTR_KNOWN(ZEND_STR_MIXED); + break; + case MAY_BE_CALLABLE: + return zend_ast_create_ex(ZEND_AST_TYPE, IS_CALLABLE); + case MAY_BE_ARRAY: + return zend_ast_create_ex(ZEND_AST_TYPE, IS_ARRAY); + case MAY_BE_STATIC: + return zend_ast_create_ex(ZEND_AST_TYPE, IS_STATIC); + EMPTY_SWITCH_DEFAULT_CASE() + } + + zend_ast *ast = zend_ast_create_zval_from_str(name); + ast->attr = ZEND_NAME_NOT_FQ; + + return ast; +} + +static zend_ast *zp_type_name_to_ast(zend_string *name) +{ + zend_ast *ast = zend_ast_create_zval_from_str(name); + + if (zend_get_class_fetch_type(name) != ZEND_FETCH_CLASS_DEFAULT) { + ast->attr = ZEND_NAME_NOT_FQ; + } else { + ast->attr = ZEND_NAME_FQ; + } + + return ast; +} + +static zend_ast *zp_type_to_ast(const zend_type type) +{ + if (!ZEND_TYPE_IS_SET(type)) { + return NULL; + } + + if (ZEND_TYPE_IS_UNION(type) + || (ZEND_TYPE_IS_COMPLEX(type) && ZEND_TYPE_PURE_MASK(type)) + || (ZEND_TYPE_PURE_MASK(type) && !zp_is_single_may_be_type(ZEND_TYPE_PURE_MASK(type)))) { + zend_ast *type_ast = zend_ast_create_list(0, ZEND_AST_TYPE_UNION); + if (ZEND_TYPE_HAS_LIST(type)) { + const zend_type *type_ptr; + ZEND_TYPE_LIST_FOREACH(ZEND_TYPE_LIST(type), type_ptr) { + type_ast = zend_ast_list_add(type_ast, zp_type_to_ast(*type_ptr)); + } ZEND_TYPE_LIST_FOREACH_END(); + } else if (ZEND_TYPE_HAS_NAME(type)) { + zend_ast *name_ast = zp_type_name_to_ast( + zend_string_copy(ZEND_TYPE_NAME(type))); + type_ast = zend_ast_list_add(type_ast, name_ast); + } else if (ZEND_TYPE_IS_COMPLEX(type)) { + ZEND_UNREACHABLE(); + } + uint32_t type_mask = ZEND_TYPE_PURE_MASK(type); + if ((type_mask & MAY_BE_BOOL) == MAY_BE_BOOL) { + type_ast = zend_ast_list_add(type_ast, zp_single_may_be_type_to_ast(MAY_BE_BOOL)); + type_mask &= ~MAY_BE_BOOL; + } + for (uint32_t may_be_type = 1; may_be_type < _ZEND_TYPE_MAY_BE_MASK; may_be_type <<= 1) { + if (type_mask & may_be_type) { + type_ast = zend_ast_list_add(type_ast, zp_single_may_be_type_to_ast(may_be_type)); + } + } + return type_ast; + } + + if (ZEND_TYPE_IS_INTERSECTION(type)) { + zend_ast *type_ast = zend_ast_create_list(0, ZEND_AST_TYPE_INTERSECTION); + const zend_type *type_ptr; + ZEND_TYPE_LIST_FOREACH(ZEND_TYPE_LIST(type), type_ptr) { + type_ast = zend_ast_list_add(type_ast, zp_type_to_ast(*type_ptr)); + } ZEND_TYPE_LIST_FOREACH_END(); + ZEND_ASSERT(!ZEND_TYPE_PURE_MASK(type)); + return type_ast; + } + + if (ZEND_TYPE_HAS_NAME(type)) { + zend_ast *type_ast = zp_type_name_to_ast( + zend_string_copy(ZEND_TYPE_NAME(type))); + return type_ast; + } + + ZEND_ASSERT(!ZEND_TYPE_IS_COMPLEX(type)); + + uint32_t type_mask = ZEND_TYPE_PURE_MASK(type); + ZEND_ASSERT(zp_is_single_may_be_type(type_mask)); + + return zp_single_may_be_type_to_ast(type_mask); +} + +/* Can not use zend_argument_error() as the function is not on the stack */ +static zend_never_inline ZEND_COLD void zp_argument_error(zend_class_entry *error_ce, + zend_function *function, uint32_t arg_num, const char *format, ...) +{ + zend_string *func_name = get_function_or_method_name(function); + const char *arg_name = get_function_arg_name(function, arg_num); + + char *message = NULL; + + va_list va; + va_start(va, format); + zend_vspprintf(&message, 0, format, va); + va_end(va); + + zend_throw_error(error_ce, "%s(): Argument #%d%s%s%s %s", + ZSTR_VAL(func_name), arg_num, + arg_name ? " ($" : "", arg_name ? arg_name : "", arg_name ? ")" : "", message + ); + efree(message); + zend_string_release(func_name); +} + +static zend_result zp_get_param_default_value(zval *result, zend_function *function, uint32_t arg_offset) +{ + ZEND_ASSERT(arg_offset < function->common.num_args); + + if (function->type == ZEND_USER_FUNCTION) { + zend_op *opline = &function->op_array.opcodes[arg_offset]; + if (EXPECTED(opline->opcode == ZEND_RECV_INIT)) { + ZVAL_COPY(result, RT_CONSTANT(opline, opline->op2)); + return SUCCESS; + } else { + ZEND_ASSERT(opline->opcode == ZEND_RECV); + } + } else if (function->type == ZEND_INTERNAL_FUNCTION) { + if (function->common.fn_flags & ZEND_ACC_USER_ARG_INFO) { + goto error; + } + + const zend_arg_info *arg_info = &function->internal_function.arg_info[arg_offset]; + + if (zend_get_default_from_internal_arg_info(result, arg_info) == SUCCESS) { + return SUCCESS; + } + } + +error: + zp_argument_error(zend_ce_argument_count_error, function, arg_offset + 1, + "must be passed explicitly, because the default value is not known"); + + return FAILURE; +} + +static bool zp_arg_must_be_sent_by_ref(zend_function *function, uint32_t arg_num) +{ + if (EXPECTED(arg_num <= MAX_ARG_FLAG_NUM)) { + if (QUICK_ARG_MUST_BE_SENT_BY_REF(function, arg_num)) { + return true; + } + } else if (ARG_MUST_BE_SENT_BY_REF(function, arg_num)) { + return true; + } + return false; +} + +static zend_ast *zp_attribute_to_ast(zend_attribute *attribute) +{ + zend_ast *args_ast; + if (attribute->argc) { + args_ast = zend_ast_create_arg_list(0, ZEND_AST_ARG_LIST); + for (uint32_t i = 0; i < attribute->argc; i++) { + zend_ast *arg_ast = zend_ast_create_zval(&attribute->args[i].value); + if (attribute->args[i].name) { + arg_ast = zend_ast_create(ZEND_AST_NAMED_ARG, + zend_ast_create_zval_from_str( + zend_string_copy(attribute->args[i].name)), + arg_ast); + } + args_ast = zend_ast_list_add(args_ast, arg_ast); + } + } else { + args_ast = NULL; + } + return zend_ast_create(ZEND_AST_ATTRIBUTE, + zend_ast_create_zval_from_str(zend_string_copy(attribute->name)), + args_ast); +} + +static zend_ast *zp_param_attributes_to_ast(zend_function *function, + uint32_t offset) +{ + zend_ast *attributes_ast = NULL; + if (!function->common.attributes) { + return NULL; + } + + /* Inherit the SensitiveParameter attribute */ + zend_attribute *attr = zend_get_parameter_attribute_str( + function->common.attributes, + "sensitiveparameter", strlen("sensitiveparameter"), offset); + if (attr) { + attributes_ast = zend_ast_create_list(1, ZEND_AST_ATTRIBUTE_GROUP, + zp_attribute_to_ast(attr)); + attributes_ast = zend_ast_create_list(1, ZEND_AST_ATTRIBUTE_LIST, + attributes_ast); + } + + return attributes_ast; +} + +static zend_string *zp_pfa_name(const zend_op_array *declaring_op_array, + const zend_op *declaring_opline) +{ + zend_string *filename = declaring_op_array->filename; + uint32_t start_lineno = declaring_opline->lineno; + + zend_string *class = zend_empty_string; + zend_string *separator = zend_empty_string; + zend_string *function = filename; + const char *parens = ""; + + if (declaring_op_array->function_name) { + if (declaring_op_array->fn_flags & ZEND_ACC_CLOSURE) { + /* If the parent function is a closure, don't redundantly + * add the classname and parentheses. + */ + function = declaring_op_array->function_name; + } else { + function = declaring_op_array->function_name; + parens = "()"; + + if (declaring_op_array->scope && declaring_op_array->scope->name) { + class = declaring_op_array->scope->name; + separator = ZSTR_KNOWN(ZEND_STR_PAAMAYIM_NEKUDOTAYIM); + } + } + } + + zend_string *name = zend_strpprintf_unchecked( + 0, + "{closure:pfa:%S%S%S%s:%" PRIu32 "}", + class, + separator, + function, + parens, + start_lineno + ); + + return name; +} + +/* Generate the AST for calling the actual function */ +static zend_ast *zp_compile_forwarding_call( + zval *this_ptr, zend_function *function, + uint32_t argc, zval *argv, zend_array *extra_named_params, + zend_string **param_names, bool variadic_partial, uint32_t num_args, + zend_class_entry *called_scope, zend_type return_type, + bool forward_superfluous_args, + zend_ast *stmts_ast) +{ + bool is_assert = zend_string_equals(function->common.function_name, + ZSTR_KNOWN(ZEND_STR_ASSERT)); + + zend_ast *args_ast = zend_ast_create_list(0, ZEND_AST_ARG_LIST); + zend_ast *call_ast = NULL; + + if (is_assert) { + /* We are going to call assert() dynamically (via call_user_func), + * otherwise assert() would print the generated AST on failure, which is + * irrelevant. */ + args_ast = zend_ast_list_add(args_ast, + zend_ast_create_zval_from_str(ZSTR_KNOWN(ZEND_STR_ASSERT))); + } + + for (uint32_t offset = 0; offset < argc; offset++) { + if (Z_ISUNDEF(argv[offset])) { + /* Argument was not passed. Pass its default value. */ + if (offset < function->common.required_num_args) { + /* Required param was not passed. This can happen due to named + * args. Using the same exception CE and message as + * zend_handle_undef_args(). */ + zp_argument_error(zend_ce_argument_count_error, function, + offset + 1, "not passed"); + goto error; + } + zval default_value; + if (zp_get_param_default_value(&default_value, function, offset) == FAILURE) { + ZEND_ASSERT(EG(exception)); + goto error; + } + zend_ast *default_value_ast; + if (Z_TYPE(default_value) == IS_CONSTANT_AST) { + default_value_ast = zend_ast_dup(Z_ASTVAL(default_value)); + } else { + default_value_ast = zend_ast_create_zval(&default_value); + } + args_ast = zend_ast_list_add(args_ast, default_value_ast); + } else { + args_ast = zend_ast_list_add(args_ast, zend_ast_create(ZEND_AST_VAR, + zend_ast_create_zval_from_str(zend_string_copy(param_names[offset])))); + } + } + if (extra_named_params) { + args_ast = zend_ast_list_add(args_ast, zend_ast_create(ZEND_AST_UNPACK, + zend_ast_create(ZEND_AST_VAR, + zend_ast_create_zval_from_str(zend_string_copy(param_names[argc + variadic_partial]))))); + } + if (variadic_partial) { + if (function->common.fn_flags & ZEND_ACC_VARIADIC) { + args_ast = zend_ast_list_add(args_ast, zend_ast_create(ZEND_AST_UNPACK, + zend_ast_create(ZEND_AST_VAR, + zend_ast_create_zval_from_str(zend_string_copy(param_names[argc]))))); + } else if (forward_superfluous_args) { + /* When a '...' placeholder is used, and the underlying function is + * not variadic, superfluous arguments are forwarded. + * Add a ...array_slice(func_get_args(), n) argument, which should + * be compiled as ZEND_AST_UNPACK + ZEND_FUNC_GET_ARGS. */ + + zend_ast *func_get_args_name_ast = zend_ast_create_zval_from_str( + zend_string_copy(ZSTR_KNOWN(ZEND_STR_FUNC_GET_ARGS))); + func_get_args_name_ast->attr = ZEND_NAME_FQ; + + zend_ast *array_slice_name_ast = zend_ast_create_zval_from_str( + zend_string_copy(ZSTR_KNOWN(ZEND_STR_ARRAY_SLICE))); + array_slice_name_ast->attr = ZEND_NAME_FQ; + + args_ast = zend_ast_list_add(args_ast, + zend_ast_create(ZEND_AST_UNPACK, + zend_ast_create(ZEND_AST_CALL, + array_slice_name_ast, + zend_ast_create_list(2, ZEND_AST_ARG_LIST, + zend_ast_create(ZEND_AST_CALL, + func_get_args_name_ast, + zend_ast_create_list(0, ZEND_AST_ARG_LIST)), + zend_ast_create_zval_from_long(num_args))))); + } + } + + if (is_assert) { + zend_ast *func_name_ast = zend_ast_create_zval_from_str(ZSTR_KNOWN(ZEND_STR_CALL_USER_FUNC)); + func_name_ast->attr = ZEND_NAME_FQ; + call_ast = zend_ast_create(ZEND_AST_CALL, func_name_ast, args_ast); + } else if (function->common.fn_flags & ZEND_ACC_CLOSURE) { + zend_ast *fn_ast = zend_ast_create(ZEND_AST_VAR, + zend_ast_create_zval_from_str(zend_string_copy(param_names[argc + variadic_partial + (extra_named_params != NULL)]))); + call_ast = zend_ast_create(ZEND_AST_CALL, fn_ast, args_ast); + } else if (Z_TYPE_P(this_ptr) == IS_OBJECT) { + zend_ast *this_ast = zend_ast_create(ZEND_AST_VAR, + zend_ast_create_zval_from_str(ZSTR_KNOWN(ZEND_STR_THIS))); + zend_ast *method_name_ast = zend_ast_create_zval_from_str( + zend_string_copy(function->common.function_name)); + call_ast = zend_ast_create(ZEND_AST_METHOD_CALL, this_ast, + method_name_ast, args_ast); + } else if (called_scope) { + zend_ast *class_name_ast = zend_ast_create_zval_from_str(ZSTR_KNOWN(ZEND_STR_STATIC)); + class_name_ast->attr = ZEND_NAME_NOT_FQ; + zend_ast *method_name_ast = zend_ast_create_zval_from_str( + zend_string_copy(function->common.function_name)); + call_ast = zend_ast_create(ZEND_AST_STATIC_CALL, class_name_ast, + method_name_ast, args_ast); + } else { + zend_ast *func_name_ast = zend_ast_create_zval_from_str(zend_string_copy(function->common.function_name)); + func_name_ast->attr = ZEND_NAME_FQ; + call_ast = zend_ast_create(ZEND_AST_CALL, func_name_ast, args_ast); + } + + /* Void functions can not 'return $expr' */ + if (ZEND_TYPE_FULL_MASK(return_type) & MAY_BE_VOID) { + stmts_ast = zend_ast_list_add(stmts_ast, call_ast); + } else { + zend_ast *return_ast = zend_ast_create(ZEND_AST_RETURN, call_ast); + stmts_ast = zend_ast_list_add(stmts_ast, return_ast); + } + + return stmts_ast; + +error: + zend_ast_destroy(args_ast); + zend_ast_destroy(call_ast); + return NULL; +} + +static uint32_t zp_compute_num_required(zend_function *function, + uint32_t orig_offset, uint32_t new_offset, uint32_t num_required) { + if (orig_offset < function->common.num_args) { + if (orig_offset < function->common.required_num_args) { + num_required = MAX(num_required, new_offset + 1); + } + } else { + ZEND_ASSERT(function->common.fn_flags & ZEND_ACC_VARIADIC); + /* Placeholders that run into the variadic portion become + * required and make all params before them required */ + ZEND_ASSERT(orig_offset >= num_required); + num_required = new_offset + 1; + } + + return num_required; +} + +/* Functions that do not allow to be called dynamically */ +static const zend_known_string_id zp_non_dynamic_call_funcs[] = { + ZEND_STR_FUNC_GET_ARG, + ZEND_STR_COMPACT, + ZEND_STR_EXTRACT, + /* Omit nullary functions such as func_num_args(), as these can't be PFA'd*/ +}; + +static bool zp_is_non_dynamic_call_func(zend_function *function) +{ + for (int i = 0; i < sizeof(zp_non_dynamic_call_funcs) / sizeof(zp_non_dynamic_call_funcs[0]); i++) { + if (zend_string_equals(function->common.function_name, ZSTR_KNOWN(zp_non_dynamic_call_funcs[i]))) { + return true; + } + } + + return false; +} + +/* Compile PFA to an op_array */ +static zend_op_array *zp_compile(zval *this_ptr, zend_function *function, + uint32_t argc, zval *argv, zend_array *extra_named_params, + const zend_array *named_positions, + const zend_op_array *declaring_op_array, + const zend_op *declaring_opline, void **cache_slot, + bool uses_variadic_placeholder) { + + zend_op_array *op_array = NULL; + + if (UNEXPECTED(zp_is_non_dynamic_call_func(function))) { + zend_throw_error(NULL, "Cannot call %.*s() dynamically", + (int) ZSTR_LEN(function->common.function_name), ZSTR_VAL(function->common.function_name)); + return NULL; + } + + if (UNEXPECTED(zp_args_check(function, argc, argv, extra_named_params, uses_variadic_placeholder) != SUCCESS)) { + ZEND_ASSERT(EG(exception)); + return NULL; + } + + zend_class_entry *called_scope; + if (Z_TYPE_P(this_ptr) == IS_OBJECT) { + called_scope = Z_OBJCE_P(this_ptr); + } else { + called_scope = Z_CE_P(this_ptr); + } + + zend_arena *orig_ast_arena = CG(ast_arena); + CG(ast_arena) = zend_arena_create(1024 * 4); + + int orig_lineno = CG(zend_lineno); + CG(zend_lineno) = zend_get_executed_lineno(); + + int new_argc = argc; + + if (uses_variadic_placeholder) { + new_argc = MAX(new_argc, function->common.num_args); + } + + zval *tmp = zend_arena_alloc(&CG(ast_arena), new_argc * sizeof(zval)); + memcpy(tmp, argv, argc * sizeof(zval)); + argv = tmp; + + /* Compute number of required args and param positions, add implicit + * placeholders. + * + * Parameters are placed in the following order: + * - Positional placeholders + * - Then named placeholders in their syntax order + * - Then implicit placeholders added by '...' + */ + uint32_t num_params = 0; + uint32_t num_required = 0; + uint32_t *arg_to_param_offset_map = zend_arena_alloc(&CG(ast_arena), sizeof(uint32_t*) * new_argc); + { + uint32_t num_positional = 0; + + /* First, we handle explicit placeholders */ + for (uint32_t arg_offset = 0; arg_offset < argc; arg_offset++) { + if (!Z_IS_PLACEHOLDER_P(&argv[arg_offset])) { + continue; + } + + num_params++; + + zend_arg_info *arg_info = &function->common.arg_info[MIN(arg_offset, function->common.num_args)]; + zval *named_pos = named_positions ? zend_hash_find(named_positions, arg_info->name) : NULL; + uint32_t param_offset; + if (named_pos) { + /* Placeholder is sent as named arg. 'num_positional' can not + * change at this point. */ + param_offset = num_positional + Z_LVAL_P(named_pos); + } else { + /* Placeholder is sent as positional */ + param_offset = num_positional++; + } + + arg_to_param_offset_map[arg_offset] = param_offset; + + num_required = zp_compute_num_required(function, + arg_offset, param_offset, num_required); + } + + /* Handle implicit placeholders added by '...' */ + if (uses_variadic_placeholder) { + for (uint32_t arg_offset = 0; arg_offset < new_argc; arg_offset++) { + if (arg_offset < argc && !Z_ISUNDEF(argv[arg_offset])) { + continue; + } + + /* Unspecified parameters become placeholders */ + Z_TYPE_INFO(argv[arg_offset]) = _IS_PLACEHOLDER; + + num_params++; + + uint32_t param_offset = num_params - 1; + + arg_to_param_offset_map[arg_offset] = param_offset; + + num_required = zp_compute_num_required(function, + arg_offset, param_offset, num_required); + } + } + } + + argc = new_argc; + + /* Assign variable names */ + + uint32_t num_names = argc + uses_variadic_placeholder + (extra_named_params != NULL) + + ((function->common.fn_flags & ZEND_ACC_CLOSURE) != 0); + zend_string **param_names = zend_arena_calloc(&CG(ast_arena), + num_names, sizeof(zend_string*)); + memset(param_names, 0, sizeof(zend_string*) * num_names); + zp_assign_names(param_names, num_names, argc, argv, function, + uses_variadic_placeholder, extra_named_params); + + /* Generate AST */ + + zend_ast *lexical_vars_ast = zend_ast_create_list(0, ZEND_AST_CLOSURE_USES); + zend_ast *params_ast = zend_ast_create_list(0, ZEND_AST_ARG_LIST); + zend_ast *return_type_ast = NULL; + zend_ast *stmts_ast = zend_ast_create_list(0, ZEND_AST_STMT_LIST); + zend_ast *attributes_ast = NULL; + + /* Generate AST for params and lexical vars */ + { + /* The inner Closure, if any, is assumed to be the first lexical var by + * do_closure_bind(). */ + if (function->common.fn_flags & ZEND_ACC_CLOSURE) { + zend_ast *lexical_var_ast = zend_ast_create_zval_from_str( + zend_string_copy(param_names[argc + uses_variadic_placeholder + (extra_named_params != NULL)])); + lexical_vars_ast = zend_ast_list_add(lexical_vars_ast, lexical_var_ast); + } + + zend_ast **params = zend_arena_calloc(&CG(ast_arena), num_params, sizeof(zend_ast*)); + for (uint32_t offset = 0; offset < argc; offset++) { + if (Z_IS_PLACEHOLDER_P(&argv[offset])) { + zend_arg_info *arg_info = &function->common.arg_info[MIN(offset, function->common.num_args)]; + + int param_flags = 0; + if (zp_arg_must_be_sent_by_ref(function, offset+1)) { + param_flags |= ZEND_PARAM_REF; + } + + uint32_t param_offset = arg_to_param_offset_map[offset]; + zend_ast *param_type_ast = zp_type_to_ast(arg_info->type); + zend_ast *default_value_ast = NULL; + if (param_offset >= num_required) { + zval default_value; + if (zp_get_param_default_value(&default_value, function, offset) == FAILURE) { + for (uint32_t i = 0; i < num_params; i++) { + zend_ast_destroy(params[i]); + } + goto error; + } + default_value_ast = zend_ast_create_zval(&default_value); + } + + ZEND_ASSERT(offset < function->common.num_args || (function->common.fn_flags & ZEND_ACC_VARIADIC)); + + zend_ast *attributes_ast = zp_param_attributes_to_ast(function, MIN(offset, function->common.num_args)); + params[param_offset] = zend_ast_create_ex(ZEND_AST_PARAM, + param_flags, param_type_ast, + zend_ast_create_zval_from_str( + zend_string_copy(param_names[offset])), + default_value_ast, attributes_ast, NULL, NULL); + + } else if (!Z_ISUNDEF(argv[offset])) { + // TODO: If the pre-bound parameter is a literal, it can be a + // literal in the function body instead of a lexical var. + zend_ast *lexical_var_ast = zend_ast_create_zval_from_str( + zend_string_copy(param_names[offset])); + if (zp_arg_must_be_sent_by_ref(function, offset+1)) { + lexical_var_ast->attr = ZEND_BIND_REF; + } + lexical_vars_ast = zend_ast_list_add( + lexical_vars_ast, lexical_var_ast); + } + } + + for (uint32_t i = 0; i < num_params; i++) { + params_ast = zend_ast_list_add(params_ast, params[i]); + } + } + + if (extra_named_params) { + zend_ast *lexical_var_ast = zend_ast_create_zval_from_str( + zend_string_copy(param_names[argc + uses_variadic_placeholder])); + lexical_vars_ast = zend_ast_list_add(lexical_vars_ast, lexical_var_ast); + } + + /* If we have a variadic placeholder and the underlying function is + * variadic, add a variadic param. */ + if (uses_variadic_placeholder + && (function->common.fn_flags & ZEND_ACC_VARIADIC)) { + zend_arg_info *arg_info = &function->common.arg_info[function->common.num_args]; + int param_flags = ZEND_PARAM_VARIADIC; + if (zp_arg_must_be_sent_by_ref(function, function->common.num_args+1)) { + param_flags |= ZEND_PARAM_REF; + } + zend_ast *param_type_ast = zp_type_to_ast(arg_info->type); + zend_ast *attributes_ast = zp_param_attributes_to_ast(function, function->common.num_args); + params_ast = zend_ast_list_add(params_ast, zend_ast_create_ex(ZEND_AST_PARAM, + param_flags, param_type_ast, + zend_ast_create_zval_from_str( + zend_string_copy(param_names[argc])), + NULL, attributes_ast, NULL, NULL)); + } + + zend_type return_type = {0}; + if (function->common.fn_flags & ZEND_ACC_HAS_RETURN_TYPE) { + return_type = (function->common.arg_info-1)->type; + return_type_ast = zp_type_to_ast(return_type); + } + + /** + * Generate function body. + * + * If we may need to forward superflous arguments, do that conditionally, as + * it's faster: + * + * if (func_num_args() <= n) { + * // normal call + * } else { + * // call with superflous arg forwarding + * } + * + * The func_num_args() call should be compiled to a single FUNC_NUM_ARGS op. + */ + + if (uses_variadic_placeholder && !(function->common.fn_flags & ZEND_ACC_VARIADIC)) { + zend_ast *no_forwarding_ast = zend_ast_create_list(0, ZEND_AST_STMT_LIST); + zend_ast *forwarding_ast = zend_ast_create_list(0, ZEND_AST_STMT_LIST); + + no_forwarding_ast = zp_compile_forwarding_call(this_ptr, function, + argc, argv, extra_named_params, + param_names, uses_variadic_placeholder, num_params, + called_scope, return_type, false, no_forwarding_ast); + + if (!no_forwarding_ast) { + ZEND_ASSERT(EG(exception)); + goto error; + } + + forwarding_ast = zp_compile_forwarding_call(this_ptr, function, + argc, argv, extra_named_params, + param_names, uses_variadic_placeholder, num_params, + called_scope, return_type, true, forwarding_ast); + + if (!forwarding_ast) { + ZEND_ASSERT(EG(exception)); + zend_ast_destroy(no_forwarding_ast); + goto error; + } + + zend_ast *func_num_args_name_ast = zend_ast_create_zval_from_str( + zend_string_copy(ZSTR_KNOWN(ZEND_STR_FUNC_NUM_ARGS))); + func_num_args_name_ast->attr = ZEND_NAME_FQ; + + stmts_ast = zend_ast_list_add(stmts_ast, + zend_ast_create_list(2, ZEND_AST_IF, + zend_ast_create(ZEND_AST_IF_ELEM, + zend_ast_create_binary_op(ZEND_IS_SMALLER_OR_EQUAL, + zend_ast_create(ZEND_AST_CALL, func_num_args_name_ast, + zend_ast_create_list(0, ZEND_AST_ARG_LIST)), + zend_ast_create_zval_from_long(num_params)), + no_forwarding_ast), + zend_ast_create(ZEND_AST_IF_ELEM, + NULL, + forwarding_ast))); + } else { + stmts_ast = zp_compile_forwarding_call(this_ptr, function, + argc, argv, extra_named_params, + param_names, uses_variadic_placeholder, num_params, + called_scope, return_type, false, stmts_ast); + + if (!stmts_ast) { + ZEND_ASSERT(EG(exception)); + goto error; + } + } + + /* Inherit the NoDiscard attribute */ + if (function->common.attributes) { + zend_attribute *attr = zend_get_attribute_str( + function->common.attributes, "nodiscard", strlen("nodiscard")); + if (attr) { + attributes_ast = zend_ast_create_list(1, ZEND_AST_ATTRIBUTE_GROUP, + zp_attribute_to_ast(attr)); + attributes_ast = zend_ast_create_list(1, ZEND_AST_ATTRIBUTE_LIST, + attributes_ast); + } + } + + int closure_flags = function->common.fn_flags & ZEND_ACC_RETURN_REFERENCE; + zend_ast *closure_ast = zend_ast_create_decl(ZEND_AST_CLOSURE, + closure_flags, CG(zend_lineno), NULL, + NULL, params_ast, lexical_vars_ast, stmts_ast, + return_type_ast, attributes_ast); + + if (Z_TYPE_P(this_ptr) != IS_OBJECT || IS_STATIC_CLOSURE(function)) { + ((zend_ast_decl*)closure_ast)->flags |= ZEND_ACC_STATIC; + } + +#if ZEND_DEBUG + { + const char *tmp = getenv("DUMP_PFA_AST"); + if (tmp && ZEND_ATOL(tmp)) { + zend_string *str = zend_ast_export("", closure_ast, ""); + fprintf(stderr, "PFA AST: %s\n", ZSTR_VAL(str)); + zend_string_release(str); + } + } +#endif + + zend_string *pfa_name = zp_pfa_name(declaring_op_array, declaring_opline); + + op_array = zend_accel_compile_pfa(closure_ast, declaring_op_array, + declaring_opline, function, pfa_name); + + zend_ast_destroy(closure_ast); + +clean: + for (uint32_t i = 0; i < num_names; i++) { + if (param_names[i]) { + zend_string_release(param_names[i]); + } + } + + zend_arena_destroy(CG(ast_arena)); + CG(ast_arena) = orig_ast_arena; + CG(zend_lineno) = orig_lineno; + + return op_array; + +error: + zend_ast_destroy(lexical_vars_ast); + zend_ast_destroy(params_ast); + zend_ast_destroy(return_type_ast); + zend_ast_destroy(stmts_ast); + zend_ast_destroy(attributes_ast); + goto clean; +} + +static zend_op_array *zp_get_op_array(zval *this_ptr, zend_function *function, + uint32_t argc, zval *argv, zend_array *extra_named_params, + const zend_array *named_positions, + const zend_op_array *declaring_op_array, + const zend_op *declaring_opline, void **cache_slot, + bool uses_variadic_placeholder) { + + if (EXPECTED(function->type == ZEND_INTERNAL_FUNCTION + ? cache_slot[0] == function + : cache_slot[0] == function->op_array.opcodes)) { + return cache_slot[1]; + } + + zend_op_array *op_array = zend_accel_pfa_cache_get(declaring_op_array, + declaring_opline, function); + + if (UNEXPECTED(!op_array)) { + op_array = zp_compile(this_ptr, function, argc, argv, + extra_named_params, named_positions, declaring_op_array, declaring_opline, + cache_slot, uses_variadic_placeholder); + } + + if (EXPECTED(op_array) && !(function->common.fn_flags & ZEND_ACC_NEVER_CACHE)) { + cache_slot[0] = function->type == ZEND_INTERNAL_FUNCTION + ? (void*)function + : (void*)function->op_array.opcodes; + cache_slot[1] = op_array; + } + + return op_array; +} + +/* Bind pre-bound arguments as lexical vars */ +static void zp_bind(zval *result, zend_function *function, uint32_t argc, zval *argv, + zend_array *extra_named_params) { + + zend_arg_info *arg_infos = function->common.arg_info; + uint32_t bind_offset = 0; + + if (function->common.fn_flags & ZEND_ACC_CLOSURE) { + zval var; + ZVAL_OBJ(&var, ZEND_CLOSURE_OBJECT(function)); + Z_ADDREF(var); + zend_closure_bind_var_ex(result, bind_offset, &var); + bind_offset += sizeof(Bucket); + } + + for (uint32_t offset = 0; offset < argc; offset++) { + zval *var = &argv[offset]; + if (Z_IS_PLACEHOLDER_P(var) || Z_ISUNDEF_P(var)) { + continue; + } + zend_arg_info *arg_info; + if (offset < function->common.num_args) { + arg_info = &arg_infos[offset]; + } else if (function->common.fn_flags & ZEND_ACC_VARIADIC) { + arg_info = &arg_infos[function->common.num_args]; + } else { + arg_info = NULL; + } + if (arg_info && ZEND_TYPE_IS_SET(arg_info->type) + && UNEXPECTED(!zend_check_type_ex(&arg_info->type, var, function->common.scope, 0, 0))) { + zend_verify_arg_error(function, arg_info, offset+1, var); + zval_ptr_dtor(result); + ZVAL_NULL(result); + return; + } + ZEND_ASSERT(zp_arg_must_be_sent_by_ref(function, offset+1) ? Z_ISREF_P(var) : !Z_ISREF_P(var)); + zend_closure_bind_var_ex(result, bind_offset, var); + bind_offset += sizeof(Bucket); + } + + if (extra_named_params) { + zval var; + ZVAL_ARR(&var, extra_named_params); + Z_ADDREF(var); + zend_closure_bind_var_ex(result, bind_offset, &var); + } +} + +void zend_partial_create(zval *result, zval *this_ptr, zend_function *function, + uint32_t argc, zval *argv, zend_array *extra_named_params, + const zend_array *named_positions, + const zend_op_array *declaring_op_array, + const zend_op *declaring_opline, void **cache_slot, + bool uses_variadic_placeholder) { + + zend_op_array *op_array = zp_get_op_array(this_ptr, function, argc, argv, + extra_named_params, named_positions, + declaring_op_array, declaring_opline, + cache_slot, uses_variadic_placeholder); + + if (UNEXPECTED(!op_array)) { + ZEND_ASSERT(EG(exception)); + ZVAL_NULL(result); + return; + } + + zend_class_entry *called_scope; + zval object; + + if (Z_TYPE_P(this_ptr) == IS_OBJECT) { + called_scope = Z_OBJCE_P(this_ptr); + } else { + called_scope = Z_CE_P(this_ptr); + } + + if (Z_TYPE_P(this_ptr) == IS_OBJECT && !IS_STATIC_CLOSURE(function)) { + ZVAL_COPY_VALUE(&object, this_ptr); + } else { + ZVAL_UNDEF(&object); + } + + zend_create_partial_closure(result, (zend_function*)op_array, + function->common.scope, called_scope, &object, + (function->common.fn_flags & ZEND_ACC_CLOSURE) != 0); + + zp_bind(result, function, argc, argv, extra_named_params); +} + +void zend_partial_op_array_dtor(zval *pDest) +{ + destroy_op_array(Z_PTR_P(pDest)); +} diff --git a/Zend/zend_partial.h b/Zend/zend_partial.h new file mode 100644 index 0000000000000..7999dc99019c1 --- /dev/null +++ b/Zend/zend_partial.h @@ -0,0 +1,34 @@ +/* + +----------------------------------------------------------------------+ + | 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. | + +----------------------------------------------------------------------+ +*/ +#ifndef ZEND_PARTIAL_H +#define ZEND_PARTIAL_H + +#include "zend_compile.h" + +BEGIN_EXTERN_C() + +void zend_partial_create(zval *result, zval *this_ptr, zend_function *function, + uint32_t argc, zval *argv, zend_array *extra_named_params, + const zend_array *named_positions, + const zend_op_array *declaring_op_array, + const zend_op *declaring_opline, void **cache_slot, + bool uses_variadic_placeholder); + +void zend_partial_op_array_dtor(zval *pDest); + +END_EXTERN_C() + +#endif diff --git a/Zend/zend_string.h b/Zend/zend_string.h index fc7705ff78650..36aec8e16392a 100644 --- a/Zend/zend_string.h +++ b/Zend/zend_string.h @@ -630,6 +630,14 @@ EMPTY_SWITCH_DEFAULT_CASE() _(ZEND_STR_AUTOGLOBAL_ENV, "_ENV") \ _(ZEND_STR_AUTOGLOBAL_REQUEST, "_REQUEST") \ _(ZEND_STR_COUNT, "count") \ + _(ZEND_STR_FUNC_NUM_ARGS, "func_num_args") \ + _(ZEND_STR_FUNC_GET_ARGS, "func_get_args") \ + _(ZEND_STR_FUNC_GET_ARG, "func_get_arg") \ + _(ZEND_STR_COMPACT, "compact") \ + _(ZEND_STR_EXTRACT, "extract") \ + _(ZEND_STR_ASSERT, "assert") \ + _(ZEND_STR_CALL_USER_FUNC, "call_user_func") \ + _(ZEND_STR_ARRAY_SLICE, "array_slice") \ _(ZEND_STR_SENSITIVEPARAMETER, "SensitiveParameter") \ _(ZEND_STR_CONST_EXPR_PLACEHOLDER, "[constant expression]") \ _(ZEND_STR_DEPRECATED_CAPITALIZED, "Deprecated") \ diff --git a/Zend/zend_types.h b/Zend/zend_types.h index 22dbfa9be879b..6a17ed9a69e1d 100644 --- a/Zend/zend_types.h +++ b/Zend/zend_types.h @@ -858,6 +858,8 @@ static zend_always_inline uint32_t zval_gc_info(uint32_t gc_type_info) { #define IS_OBJ_LAZY_UNINITIALIZED (1U<<31) /* Virtual proxy or uninitialized Ghost */ #define IS_OBJ_LAZY_PROXY (1U<<30) /* Virtual proxy (may be initialized) */ +#define OBJ_EXTRA_FLAG_PRIV_1 (1U<<29) /* Reserved for private use by the object itself */ +#define OBJ_EXTRA_FLAG_PRIV_2 (1U<<28) /* Reserved for private use by the object itself */ #define OBJ_EXTRA_FLAGS(obj) ((obj)->extra_flags) diff --git a/Zend/zend_vm_def.h b/Zend/zend_vm_def.h index 1b91f11662c7a..cb36fd743f097 100644 --- a/Zend/zend_vm_def.h +++ b/Zend/zend_vm_def.h @@ -5684,6 +5684,30 @@ ZEND_VM_HELPER(zend_verify_recv_arg_type_helper, ANY, ANY, zval *op_1) ZEND_VM_NEXT_OPCODE(); } +ZEND_VM_HANDLER(211, ZEND_SEND_PLACEHOLDER, UNUSED, CONST|UNUSED) +{ + zval *arg; + + if (OP2_TYPE == IS_CONST) { + /* Named placeholder */ + USE_OPLINE + 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 { + /* Positional placeholder */ + arg = ZEND_CALL_VAR(EX(call), opline->result.var); + } + + Z_TYPE_INFO_P(arg) = _IS_PLACEHOLDER; + + ZEND_VM_NEXT_OPCODE(); +} + ZEND_VM_HOT_HANDLER(63, ZEND_RECV, NUM, UNUSED) { USE_OPLINE @@ -9762,6 +9786,45 @@ ZEND_VM_HANDLER(202, ZEND_CALLABLE_CONVERT, UNUSED, UNUSED, NUM|CACHE_SLOT) ZEND_VM_NEXT_OPCODE(); } +ZEND_VM_HANDLER(212, ZEND_CALLABLE_CONVERT_PARTIAL, CACHE_SLOT, CONST|UNUSED, NUM) +{ + USE_OPLINE + SAVE_OPLINE(); + + zend_execute_data *call = EX(call); + void **cache_slot = CACHE_ADDR(opline->op1.num); + zval *named_positions = GET_OP2_ZVAL_PTR(); + + zend_partial_create(EX_VAR(opline->result.var), + &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, + OP2_TYPE == IS_CONST ? Z_ARRVAL_P(named_positions) : NULL, + &EX(func)->op_array, opline, cache_slot, + opline->extended_value & ZEND_FCALL_USES_VARIADIC_PLACEHOLDER); + + 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_CHECK_EXCEPTION(); +} + 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 801bf0ee69e0d..ce66b5ca5e302 100644 --- a/Zend/zend_vm_execute.h +++ b/Zend/zend_vm_execute.h @@ -4255,6 +4255,45 @@ static ZEND_VM_HOT ZEND_OPCODE_HANDLER_RET ZEND_OPCODE_HANDLER_FUNC_CCONV ZEND_R ZEND_VM_NEXT_OPCODE(); } +static ZEND_OPCODE_HANDLER_RET ZEND_OPCODE_HANDLER_FUNC_CCONV ZEND_CALLABLE_CONVERT_PARTIAL_SPEC_CONST_HANDLER(ZEND_OPCODE_HANDLER_ARGS) +{ + USE_OPLINE + SAVE_OPLINE(); + + zend_execute_data *call = EX(call); + void **cache_slot = CACHE_ADDR(opline->op1.num); + zval *named_positions = RT_CONSTANT(opline, opline->op2); + + zend_partial_create(EX_VAR(opline->result.var), + &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, + IS_CONST == IS_CONST ? Z_ARRVAL_P(named_positions) : NULL, + &EX(func)->op_array, opline, cache_slot, + opline->extended_value & ZEND_FCALL_USES_VARIADIC_PLACEHOLDER); + + 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_CHECK_EXCEPTION(); +} + static ZEND_OPCODE_HANDLER_RET ZEND_OPCODE_HANDLER_FUNC_CCONV ZEND_INIT_DYNAMIC_CALL_SPEC_TMPVAR_HANDLER(ZEND_OPCODE_HANDLER_ARGS) { USE_OPLINE @@ -4399,6 +4438,45 @@ static ZEND_OPCODE_HANDLER_RET ZEND_OPCODE_HANDLER_FUNC_CCONV ZEND_RECV_VARIADIC ZEND_VM_NEXT_OPCODE_CHECK_EXCEPTION(); } +static ZEND_OPCODE_HANDLER_RET ZEND_OPCODE_HANDLER_FUNC_CCONV ZEND_CALLABLE_CONVERT_PARTIAL_SPEC_UNUSED_HANDLER(ZEND_OPCODE_HANDLER_ARGS) +{ + USE_OPLINE + SAVE_OPLINE(); + + zend_execute_data *call = EX(call); + void **cache_slot = CACHE_ADDR(opline->op1.num); + zval *named_positions = NULL; + + zend_partial_create(EX_VAR(opline->result.var), + &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, + IS_UNUSED == IS_CONST ? Z_ARRVAL_P(named_positions) : NULL, + &EX(func)->op_array, opline, cache_slot, + opline->extended_value & ZEND_FCALL_USES_VARIADIC_PLACEHOLDER); + + 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_CHECK_EXCEPTION(); +} + static ZEND_OPCODE_HANDLER_RET ZEND_OPCODE_HANDLER_FUNC_CCONV ZEND_FRAMELESS_ICALL_1_SPEC_UNUSED_HANDLER(ZEND_OPCODE_HANDLER_ARGS) { USE_OPLINE @@ -36091,6 +36169,30 @@ static ZEND_OPCODE_HANDLER_RET ZEND_OPCODE_HANDLER_FUNC_CCONV ZEND_CHECK_FUNC_AR ZEND_VM_NEXT_OPCODE(); } +static ZEND_OPCODE_HANDLER_RET ZEND_OPCODE_HANDLER_FUNC_CCONV ZEND_SEND_PLACEHOLDER_SPEC_UNUSED_CONST_HANDLER(ZEND_OPCODE_HANDLER_ARGS) +{ + zval *arg; + + if (IS_CONST == IS_CONST) { + /* Named placeholder */ + USE_OPLINE + 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 { + /* Positional placeholder */ + arg = ZEND_CALL_VAR(EX(call), opline->result.var); + } + + Z_TYPE_INFO_P(arg) = _IS_PLACEHOLDER; + + ZEND_VM_NEXT_OPCODE(); +} + static ZEND_VM_HOT ZEND_OPCODE_HANDLER_RET ZEND_OPCODE_HANDLER_FUNC_CCONV ZEND_FETCH_CONSTANT_SPEC_UNUSED_CONST_HANDLER(ZEND_OPCODE_HANDLER_ARGS) { USE_OPLINE @@ -38856,6 +38958,30 @@ static ZEND_VM_HOT ZEND_OPCODE_HANDLER_RET ZEND_OPCODE_HANDLER_FUNC_CCONV ZEND_C ZEND_VM_NEXT_OPCODE_CHECK_EXCEPTION(); } +static ZEND_OPCODE_HANDLER_RET ZEND_OPCODE_HANDLER_FUNC_CCONV ZEND_SEND_PLACEHOLDER_SPEC_UNUSED_UNUSED_HANDLER(ZEND_OPCODE_HANDLER_ARGS) +{ + zval *arg; + + if (IS_UNUSED == IS_CONST) { + /* Named placeholder */ + USE_OPLINE + 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 { + /* Positional placeholder */ + arg = ZEND_CALL_VAR(EX(call), opline->result.var); + } + + Z_TYPE_INFO_P(arg) = _IS_PLACEHOLDER; + + ZEND_VM_NEXT_OPCODE(); +} + static ZEND_OPCODE_HANDLER_RET ZEND_OPCODE_HANDLER_FUNC_CCONV ZEND_NEW_SPEC_UNUSED_UNUSED_HANDLER(ZEND_OPCODE_HANDLER_ARGS) { USE_OPLINE @@ -59923,6 +60049,45 @@ static ZEND_VM_HOT ZEND_OPCODE_HANDLER_RET ZEND_OPCODE_HANDLER_CCONV ZEND_RECV_I ZEND_VM_NEXT_OPCODE(); } +static ZEND_OPCODE_HANDLER_RET ZEND_OPCODE_HANDLER_CCONV ZEND_CALLABLE_CONVERT_PARTIAL_SPEC_CONST_TAILCALL_HANDLER(ZEND_OPCODE_HANDLER_ARGS) +{ + USE_OPLINE + SAVE_OPLINE(); + + zend_execute_data *call = EX(call); + void **cache_slot = CACHE_ADDR(opline->op1.num); + zval *named_positions = RT_CONSTANT(opline, opline->op2); + + zend_partial_create(EX_VAR(opline->result.var), + &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, + IS_CONST == IS_CONST ? Z_ARRVAL_P(named_positions) : NULL, + &EX(func)->op_array, opline, cache_slot, + opline->extended_value & ZEND_FCALL_USES_VARIADIC_PLACEHOLDER); + + 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_CHECK_EXCEPTION(); +} + static ZEND_OPCODE_HANDLER_RET ZEND_OPCODE_HANDLER_CCONV ZEND_INIT_DYNAMIC_CALL_SPEC_TMPVAR_TAILCALL_HANDLER(ZEND_OPCODE_HANDLER_ARGS) { USE_OPLINE @@ -60067,6 +60232,45 @@ static ZEND_OPCODE_HANDLER_RET ZEND_OPCODE_HANDLER_CCONV ZEND_RECV_VARIADIC_SPEC ZEND_VM_NEXT_OPCODE_CHECK_EXCEPTION(); } +static ZEND_OPCODE_HANDLER_RET ZEND_OPCODE_HANDLER_CCONV ZEND_CALLABLE_CONVERT_PARTIAL_SPEC_UNUSED_TAILCALL_HANDLER(ZEND_OPCODE_HANDLER_ARGS) +{ + USE_OPLINE + SAVE_OPLINE(); + + zend_execute_data *call = EX(call); + void **cache_slot = CACHE_ADDR(opline->op1.num); + zval *named_positions = NULL; + + zend_partial_create(EX_VAR(opline->result.var), + &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, + IS_UNUSED == IS_CONST ? Z_ARRVAL_P(named_positions) : NULL, + &EX(func)->op_array, opline, cache_slot, + opline->extended_value & ZEND_FCALL_USES_VARIADIC_PLACEHOLDER); + + 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_CHECK_EXCEPTION(); +} + static ZEND_OPCODE_HANDLER_RET ZEND_OPCODE_HANDLER_CCONV ZEND_FRAMELESS_ICALL_1_SPEC_UNUSED_TAILCALL_HANDLER(ZEND_OPCODE_HANDLER_ARGS) { USE_OPLINE @@ -91557,6 +91761,30 @@ static ZEND_OPCODE_HANDLER_RET ZEND_OPCODE_HANDLER_CCONV ZEND_CHECK_FUNC_ARG_SPE ZEND_VM_NEXT_OPCODE(); } +static ZEND_OPCODE_HANDLER_RET ZEND_OPCODE_HANDLER_CCONV ZEND_SEND_PLACEHOLDER_SPEC_UNUSED_CONST_TAILCALL_HANDLER(ZEND_OPCODE_HANDLER_ARGS) +{ + zval *arg; + + if (IS_CONST == IS_CONST) { + /* Named placeholder */ + USE_OPLINE + 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 { + /* Positional placeholder */ + arg = ZEND_CALL_VAR(EX(call), opline->result.var); + } + + Z_TYPE_INFO_P(arg) = _IS_PLACEHOLDER; + + ZEND_VM_NEXT_OPCODE(); +} + static ZEND_VM_HOT ZEND_OPCODE_HANDLER_RET ZEND_OPCODE_HANDLER_CCONV ZEND_FETCH_CONSTANT_SPEC_UNUSED_CONST_TAILCALL_HANDLER(ZEND_OPCODE_HANDLER_ARGS) { USE_OPLINE @@ -94322,6 +94550,30 @@ static ZEND_VM_HOT ZEND_OPCODE_HANDLER_RET ZEND_OPCODE_HANDLER_CCONV ZEND_CHECK_ ZEND_VM_NEXT_OPCODE_CHECK_EXCEPTION(); } +static ZEND_OPCODE_HANDLER_RET ZEND_OPCODE_HANDLER_CCONV ZEND_SEND_PLACEHOLDER_SPEC_UNUSED_UNUSED_TAILCALL_HANDLER(ZEND_OPCODE_HANDLER_ARGS) +{ + zval *arg; + + if (IS_UNUSED == IS_CONST) { + /* Named placeholder */ + USE_OPLINE + 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 { + /* Positional placeholder */ + arg = ZEND_CALL_VAR(EX(call), opline->result.var); + } + + Z_TYPE_INFO_P(arg) = _IS_PLACEHOLDER; + + ZEND_VM_NEXT_OPCODE(); +} + static ZEND_OPCODE_HANDLER_RET ZEND_OPCODE_HANDLER_CCONV ZEND_NEW_SPEC_UNUSED_UNUSED_TAILCALL_HANDLER(ZEND_OPCODE_HANDLER_ARGS) { USE_OPLINE @@ -115257,6 +115509,16 @@ ZEND_API void execute_ex(zend_execute_data *ex) (void*)&&ZEND_JMP_FRAMELESS_SPEC_CONST_LABEL, (void*)&&ZEND_INIT_PARENT_PROPERTY_HOOK_CALL_SPEC_CONST_UNUSED_LABEL, (void*)&&ZEND_DECLARE_ATTRIBUTED_CONST_SPEC_CONST_CONST_LABEL, + (void*)&&ZEND_SEND_PLACEHOLDER_SPEC_UNUSED_CONST_LABEL, + (void*)&&ZEND_NULL_LABEL, + (void*)&&ZEND_NULL_LABEL, + (void*)&&ZEND_SEND_PLACEHOLDER_SPEC_UNUSED_UNUSED_LABEL, + (void*)&&ZEND_NULL_LABEL, + (void*)&&ZEND_CALLABLE_CONVERT_PARTIAL_SPEC_CONST_LABEL, + (void*)&&ZEND_NULL_LABEL, + (void*)&&ZEND_NULL_LABEL, + (void*)&&ZEND_CALLABLE_CONVERT_PARTIAL_SPEC_UNUSED_LABEL, + (void*)&&ZEND_NULL_LABEL, (void*)&&ZEND_INIT_FCALL_OFFSET_SPEC_CONST_LABEL, (void*)&&ZEND_RECV_NOTYPE_SPEC_LABEL, (void*)&&ZEND_NULL_LABEL, @@ -116666,6 +116928,11 @@ ZEND_API void execute_ex(zend_execute_data *ex) ZEND_RECV_INIT_SPEC_CONST_HANDLER(ZEND_OPCODE_HANDLER_ARGS_PASSTHRU); VM_TRACE_OP_END(ZEND_RECV_INIT_SPEC_CONST) HYBRID_BREAK(); + HYBRID_CASE(ZEND_CALLABLE_CONVERT_PARTIAL_SPEC_CONST): + VM_TRACE(ZEND_CALLABLE_CONVERT_PARTIAL_SPEC_CONST) + ZEND_CALLABLE_CONVERT_PARTIAL_SPEC_CONST_HANDLER(ZEND_OPCODE_HANDLER_ARGS_PASSTHRU); + VM_TRACE_OP_END(ZEND_CALLABLE_CONVERT_PARTIAL_SPEC_CONST) + HYBRID_BREAK(); HYBRID_CASE(ZEND_INIT_DYNAMIC_CALL_SPEC_TMPVAR): VM_TRACE(ZEND_INIT_DYNAMIC_CALL_SPEC_TMPVAR) ZEND_INIT_DYNAMIC_CALL_SPEC_TMPVAR_HANDLER(ZEND_OPCODE_HANDLER_ARGS_PASSTHRU); @@ -116681,6 +116948,11 @@ ZEND_API void execute_ex(zend_execute_data *ex) ZEND_RECV_VARIADIC_SPEC_UNUSED_HANDLER(ZEND_OPCODE_HANDLER_ARGS_PASSTHRU); VM_TRACE_OP_END(ZEND_RECV_VARIADIC_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_FRAMELESS_ICALL_1_SPEC_UNUSED): VM_TRACE(ZEND_FRAMELESS_ICALL_1_SPEC_UNUSED) ZEND_FRAMELESS_ICALL_1_SPEC_UNUSED_HANDLER(ZEND_OPCODE_HANDLER_ARGS_PASSTHRU); @@ -120128,6 +120400,11 @@ ZEND_API void execute_ex(zend_execute_data *ex) ZEND_CHECK_FUNC_ARG_SPEC_UNUSED_CONST_HANDLER(ZEND_OPCODE_HANDLER_ARGS_PASSTHRU); VM_TRACE_OP_END(ZEND_CHECK_FUNC_ARG_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_CONSTANT_SPEC_UNUSED_CONST): VM_TRACE(ZEND_FETCH_CONSTANT_SPEC_UNUSED_CONST) ZEND_FETCH_CONSTANT_SPEC_UNUSED_CONST_HANDLER(ZEND_OPCODE_HANDLER_ARGS_PASSTHRU); @@ -120313,6 +120590,11 @@ ZEND_API void execute_ex(zend_execute_data *ex) ZEND_CHECK_UNDEF_ARGS_SPEC_UNUSED_UNUSED_HANDLER(ZEND_OPCODE_HANDLER_ARGS_PASSTHRU); VM_TRACE_OP_END(ZEND_CHECK_UNDEF_ARGS_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_NEW_SPEC_UNUSED_UNUSED): VM_TRACE(ZEND_NEW_SPEC_UNUSED_UNUSED) ZEND_NEW_SPEC_UNUSED_UNUSED_HANDLER(ZEND_OPCODE_HANDLER_ARGS_PASSTHRU); @@ -124508,6 +124790,16 @@ void zend_vm_init(void) ZEND_JMP_FRAMELESS_SPEC_CONST_HANDLER, ZEND_INIT_PARENT_PROPERTY_HOOK_CALL_SPEC_CONST_UNUSED_HANDLER, ZEND_DECLARE_ATTRIBUTED_CONST_SPEC_CONST_CONST_HANDLER, + ZEND_SEND_PLACEHOLDER_SPEC_UNUSED_CONST_HANDLER, + ZEND_NULL_HANDLER, + ZEND_NULL_HANDLER, + ZEND_SEND_PLACEHOLDER_SPEC_UNUSED_UNUSED_HANDLER, + ZEND_NULL_HANDLER, + ZEND_CALLABLE_CONVERT_PARTIAL_SPEC_CONST_HANDLER, + ZEND_NULL_HANDLER, + ZEND_NULL_HANDLER, + ZEND_CALLABLE_CONVERT_PARTIAL_SPEC_UNUSED_HANDLER, + ZEND_NULL_HANDLER, ZEND_INIT_FCALL_OFFSET_SPEC_CONST_HANDLER, ZEND_RECV_NOTYPE_SPEC_HANDLER, ZEND_NULL_HANDLER, @@ -128005,6 +128297,16 @@ void zend_vm_init(void) ZEND_JMP_FRAMELESS_SPEC_CONST_TAILCALL_HANDLER, ZEND_INIT_PARENT_PROPERTY_HOOK_CALL_SPEC_CONST_UNUSED_TAILCALL_HANDLER, ZEND_DECLARE_ATTRIBUTED_CONST_SPEC_CONST_CONST_TAILCALL_HANDLER, + ZEND_SEND_PLACEHOLDER_SPEC_UNUSED_CONST_TAILCALL_HANDLER, + ZEND_NULL_TAILCALL_HANDLER, + ZEND_NULL_TAILCALL_HANDLER, + ZEND_SEND_PLACEHOLDER_SPEC_UNUSED_UNUSED_TAILCALL_HANDLER, + ZEND_NULL_TAILCALL_HANDLER, + ZEND_CALLABLE_CONVERT_PARTIAL_SPEC_CONST_TAILCALL_HANDLER, + ZEND_NULL_TAILCALL_HANDLER, + ZEND_NULL_TAILCALL_HANDLER, + ZEND_CALLABLE_CONVERT_PARTIAL_SPEC_UNUSED_TAILCALL_HANDLER, + ZEND_NULL_TAILCALL_HANDLER, ZEND_INIT_FCALL_OFFSET_SPEC_CONST_TAILCALL_HANDLER, ZEND_RECV_NOTYPE_SPEC_TAILCALL_HANDLER, ZEND_NULL_TAILCALL_HANDLER, @@ -128973,7 +129275,7 @@ void zend_vm_init(void) 1255, 1256 | SPEC_RULE_OP1, 1261 | SPEC_RULE_OP1, - 3493, + 3503, 1266 | SPEC_RULE_OP1, 1271 | SPEC_RULE_OP1, 1276 | SPEC_RULE_OP2, @@ -129007,7 +129309,7 @@ void zend_vm_init(void) 1559 | SPEC_RULE_OP1 | SPEC_RULE_OP2, 1584 | SPEC_RULE_OP1, 1589, - 3493, + 3503, 1590 | SPEC_RULE_OP1, 1595 | SPEC_RULE_OP1 | SPEC_RULE_OP2, 1620 | SPEC_RULE_OP1 | SPEC_RULE_OP2, @@ -129139,51 +129441,51 @@ void zend_vm_init(void) 2575, 2576, 2577, - 3493, - 3493, - 3493, - 3493, - 3493, - 3493, - 3493, - 3493, - 3493, - 3493, - 3493, - 3493, - 3493, - 3493, - 3493, - 3493, - 3493, - 3493, - 3493, - 3493, - 3493, - 3493, - 3493, - 3493, - 3493, - 3493, - 3493, - 3493, - 3493, - 3493, - 3493, - 3493, - 3493, - 3493, - 3493, - 3493, - 3493, - 3493, - 3493, - 3493, - 3493, - 3493, - 3493, - 3493, - 3493, + 2578 | SPEC_RULE_OP2, + 2583 | SPEC_RULE_OP2, + 3503, + 3503, + 3503, + 3503, + 3503, + 3503, + 3503, + 3503, + 3503, + 3503, + 3503, + 3503, + 3503, + 3503, + 3503, + 3503, + 3503, + 3503, + 3503, + 3503, + 3503, + 3503, + 3503, + 3503, + 3503, + 3503, + 3503, + 3503, + 3503, + 3503, + 3503, + 3503, + 3503, + 3503, + 3503, + 3503, + 3503, + 3503, + 3503, + 3503, + 3503, + 3503, + 3503, }; #if 0 #elif (ZEND_VM_KIND == ZEND_VM_KIND_HYBRID) @@ -129360,7 +129662,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 = 2596 | SPEC_RULE_OP1 | SPEC_RULE_OP2 | SPEC_RULE_COMMUTATIVE; if (op->op1_type < op->op2_type) { zend_swap_operands(op); } @@ -129368,7 +129670,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 = 2621 | SPEC_RULE_OP1 | SPEC_RULE_OP2 | SPEC_RULE_COMMUTATIVE; if (op->op1_type < op->op2_type) { zend_swap_operands(op); } @@ -129376,7 +129678,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 = 2646 | SPEC_RULE_OP1 | SPEC_RULE_OP2 | SPEC_RULE_COMMUTATIVE; if (op->op1_type < op->op2_type) { zend_swap_operands(op); } @@ -129387,17 +129689,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 = 2671 | 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 = 2696 | 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 = 2721 | SPEC_RULE_OP1 | SPEC_RULE_OP2; } break; case ZEND_MUL: @@ -129408,17 +129710,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 = 2746 | 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 = 2771 | 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 = 2796 | SPEC_RULE_OP1 | SPEC_RULE_OP2 | SPEC_RULE_COMMUTATIVE; } break; case ZEND_IS_IDENTICAL: @@ -129429,16 +129731,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 = 2821 | 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 = 2896 | 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 = 3121 | 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 = 3127 | SPEC_RULE_OP2 | SPEC_RULE_COMMUTATIVE; } break; case ZEND_IS_NOT_IDENTICAL: @@ -129449,16 +129751,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 = 2971 | 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 = 3046 | 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 = 3124 | 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 = 3132 | SPEC_RULE_OP2 | SPEC_RULE_COMMUTATIVE; } break; case ZEND_IS_EQUAL: @@ -129469,12 +129771,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 = 2821 | 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 = 2896 | SPEC_RULE_OP1 | SPEC_RULE_OP2 | SPEC_RULE_SMART_BRANCH | SPEC_RULE_COMMUTATIVE; } break; case ZEND_IS_NOT_EQUAL: @@ -129485,12 +129787,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 = 2971 | 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 = 3046 | SPEC_RULE_OP1 | SPEC_RULE_OP2 | SPEC_RULE_SMART_BRANCH | SPEC_RULE_COMMUTATIVE; } break; case ZEND_IS_SMALLER: @@ -129498,12 +129800,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 = 3137 | 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 = 3212 | SPEC_RULE_OP1 | SPEC_RULE_OP2 | SPEC_RULE_SMART_BRANCH; } break; case ZEND_IS_SMALLER_OR_EQUAL: @@ -129511,79 +129813,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 = 3287 | 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 = 3362 | 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 = 3449 | SPEC_RULE_OP1; } else if (op1_info == MAY_BE_DOUBLE) { - spec = 3444 | SPEC_RULE_OP1; + spec = 3454 | 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 = 3459 | 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 = 3437 | SPEC_RULE_RETVAL; } else if (op1_info == MAY_BE_LONG) { - spec = 3429 | SPEC_RULE_RETVAL; + spec = 3439 | 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 = 3441 | SPEC_RULE_RETVAL; } else if (op1_info == MAY_BE_LONG) { - spec = 3433 | SPEC_RULE_RETVAL; + spec = 3443 | SPEC_RULE_RETVAL; } break; case ZEND_POST_INC: if (res_info == MAY_BE_LONG && op1_info == MAY_BE_LONG) { - spec = 3435; + spec = 3445; } else if (op1_info == MAY_BE_LONG) { - spec = 3436; + spec = 3446; } break; case ZEND_POST_DEC: if (res_info == MAY_BE_LONG && op1_info == MAY_BE_LONG) { - spec = 3437; + spec = 3447; } else if (op1_info == MAY_BE_LONG) { - spec = 3438; + spec = 3448; } break; case ZEND_JMP: if (OP_JMP_ADDR(op, op->op1) > op) { - spec = 2585; + spec = 2595; } break; case ZEND_INIT_FCALL: if (Z_EXTRA_P(RT_CONSTANT(op, op->op2)) != 0) { - spec = 2578; + spec = 2588; } break; case ZEND_RECV: if (op->op2.num == MAY_BE_ANY) { - spec = 2579; + spec = 2589; } 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 = 3499; } 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 = 3494 | 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 = 3501 | SPEC_RULE_RETVAL; } break; case ZEND_FETCH_DIM_R: @@ -129591,22 +129893,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 = 3464 | 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 = 3500; } 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 = 3489 | 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 = 2590 | SPEC_RULE_OP1; } break; case ZEND_BW_OR: diff --git a/Zend/zend_vm_handlers.h b/Zend/zend_vm_handlers.h index 33d951141550e..53c9912d7c46a 100644 --- a/Zend/zend_vm_handlers.h +++ b/Zend/zend_vm_handlers.h @@ -1373,508 +1373,512 @@ _(2575, ZEND_JMP_FRAMELESS_SPEC_CONST) \ _(2576, ZEND_INIT_PARENT_PROPERTY_HOOK_CALL_SPEC_CONST_UNUSED) \ _(2577, ZEND_DECLARE_ATTRIBUTED_CONST_SPEC_CONST_CONST) \ - _(2578, ZEND_INIT_FCALL_OFFSET_SPEC_CONST) \ - _(2579, ZEND_RECV_NOTYPE_SPEC) \ - _(2581, ZEND_COUNT_ARRAY_SPEC_TMPVAR_UNUSED) \ - _(2582, ZEND_COUNT_ARRAY_SPEC_TMPVAR_UNUSED) \ - _(2584, ZEND_COUNT_ARRAY_SPEC_CV_UNUSED) \ - _(2585, ZEND_JMP_FORWARD_SPEC) \ - _(2591, ZEND_ADD_LONG_NO_OVERFLOW_SPEC_TMPVARCV_CONST) \ - _(2592, ZEND_ADD_LONG_NO_OVERFLOW_SPEC_TMPVARCV_TMPVARCV) \ - _(2593, ZEND_ADD_LONG_NO_OVERFLOW_SPEC_TMPVARCV_TMPVARCV) \ - _(2595, ZEND_ADD_LONG_NO_OVERFLOW_SPEC_TMPVARCV_TMPVARCV) \ - _(2596, ZEND_ADD_LONG_NO_OVERFLOW_SPEC_TMPVARCV_CONST) \ - _(2597, ZEND_ADD_LONG_NO_OVERFLOW_SPEC_TMPVARCV_TMPVARCV) \ - _(2598, ZEND_ADD_LONG_NO_OVERFLOW_SPEC_TMPVARCV_TMPVARCV) \ - _(2600, ZEND_ADD_LONG_NO_OVERFLOW_SPEC_TMPVARCV_TMPVARCV) \ + _(2578, ZEND_SEND_PLACEHOLDER_SPEC_UNUSED_CONST) \ + _(2581, ZEND_SEND_PLACEHOLDER_SPEC_UNUSED_UNUSED) \ + _(2583, ZEND_CALLABLE_CONVERT_PARTIAL_SPEC_CONST) \ + _(2586, ZEND_CALLABLE_CONVERT_PARTIAL_SPEC_UNUSED) \ + _(2588, ZEND_INIT_FCALL_OFFSET_SPEC_CONST) \ + _(2589, ZEND_RECV_NOTYPE_SPEC) \ + _(2591, ZEND_COUNT_ARRAY_SPEC_TMPVAR_UNUSED) \ + _(2592, ZEND_COUNT_ARRAY_SPEC_TMPVAR_UNUSED) \ + _(2594, ZEND_COUNT_ARRAY_SPEC_CV_UNUSED) \ + _(2595, ZEND_JMP_FORWARD_SPEC) \ + _(2601, ZEND_ADD_LONG_NO_OVERFLOW_SPEC_TMPVARCV_CONST) \ + _(2602, ZEND_ADD_LONG_NO_OVERFLOW_SPEC_TMPVARCV_TMPVARCV) \ + _(2603, ZEND_ADD_LONG_NO_OVERFLOW_SPEC_TMPVARCV_TMPVARCV) \ + _(2605, 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) \ _(2610, ZEND_ADD_LONG_NO_OVERFLOW_SPEC_TMPVARCV_TMPVARCV) \ - _(2616, ZEND_ADD_LONG_SPEC_TMPVARCV_CONST) \ - _(2617, ZEND_ADD_LONG_SPEC_TMPVARCV_TMPVARCV) \ - _(2618, ZEND_ADD_LONG_SPEC_TMPVARCV_TMPVARCV) \ - _(2620, ZEND_ADD_LONG_SPEC_TMPVARCV_TMPVARCV) \ - _(2621, ZEND_ADD_LONG_SPEC_TMPVARCV_CONST) \ - _(2622, ZEND_ADD_LONG_SPEC_TMPVARCV_TMPVARCV) \ - _(2623, ZEND_ADD_LONG_SPEC_TMPVARCV_TMPVARCV) \ - _(2625, ZEND_ADD_LONG_SPEC_TMPVARCV_TMPVARCV) \ + _(2616, ZEND_ADD_LONG_NO_OVERFLOW_SPEC_TMPVARCV_CONST) \ + _(2617, ZEND_ADD_LONG_NO_OVERFLOW_SPEC_TMPVARCV_TMPVARCV) \ + _(2618, ZEND_ADD_LONG_NO_OVERFLOW_SPEC_TMPVARCV_TMPVARCV) \ + _(2620, ZEND_ADD_LONG_NO_OVERFLOW_SPEC_TMPVARCV_TMPVARCV) \ + _(2626, ZEND_ADD_LONG_SPEC_TMPVARCV_CONST) \ + _(2627, ZEND_ADD_LONG_SPEC_TMPVARCV_TMPVARCV) \ + _(2628, ZEND_ADD_LONG_SPEC_TMPVARCV_TMPVARCV) \ + _(2630, 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) \ _(2635, ZEND_ADD_LONG_SPEC_TMPVARCV_TMPVARCV) \ - _(2641, ZEND_ADD_DOUBLE_SPEC_TMPVARCV_CONST) \ - _(2642, ZEND_ADD_DOUBLE_SPEC_TMPVARCV_TMPVARCV) \ - _(2643, ZEND_ADD_DOUBLE_SPEC_TMPVARCV_TMPVARCV) \ - _(2645, ZEND_ADD_DOUBLE_SPEC_TMPVARCV_TMPVARCV) \ - _(2646, ZEND_ADD_DOUBLE_SPEC_TMPVARCV_CONST) \ - _(2647, ZEND_ADD_DOUBLE_SPEC_TMPVARCV_TMPVARCV) \ - _(2648, ZEND_ADD_DOUBLE_SPEC_TMPVARCV_TMPVARCV) \ - _(2650, ZEND_ADD_DOUBLE_SPEC_TMPVARCV_TMPVARCV) \ + _(2641, ZEND_ADD_LONG_SPEC_TMPVARCV_CONST) \ + _(2642, ZEND_ADD_LONG_SPEC_TMPVARCV_TMPVARCV) \ + _(2643, ZEND_ADD_LONG_SPEC_TMPVARCV_TMPVARCV) \ + _(2645, ZEND_ADD_LONG_SPEC_TMPVARCV_TMPVARCV) \ + _(2651, ZEND_ADD_DOUBLE_SPEC_TMPVARCV_CONST) \ + _(2652, ZEND_ADD_DOUBLE_SPEC_TMPVARCV_TMPVARCV) \ + _(2653, ZEND_ADD_DOUBLE_SPEC_TMPVARCV_TMPVARCV) \ + _(2655, 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) \ _(2660, ZEND_ADD_DOUBLE_SPEC_TMPVARCV_TMPVARCV) \ - _(2662, ZEND_SUB_LONG_NO_OVERFLOW_SPEC_CONST_TMPVARCV) \ - _(2663, ZEND_SUB_LONG_NO_OVERFLOW_SPEC_CONST_TMPVARCV) \ - _(2665, ZEND_SUB_LONG_NO_OVERFLOW_SPEC_CONST_TMPVARCV) \ - _(2666, ZEND_SUB_LONG_NO_OVERFLOW_SPEC_TMPVARCV_CONST) \ - _(2667, ZEND_SUB_LONG_NO_OVERFLOW_SPEC_TMPVARCV_TMPVARCV) \ - _(2668, ZEND_SUB_LONG_NO_OVERFLOW_SPEC_TMPVARCV_TMPVARCV) \ - _(2670, ZEND_SUB_LONG_NO_OVERFLOW_SPEC_TMPVARCV_TMPVARCV) \ - _(2671, ZEND_SUB_LONG_NO_OVERFLOW_SPEC_TMPVARCV_CONST) \ - _(2672, ZEND_SUB_LONG_NO_OVERFLOW_SPEC_TMPVARCV_TMPVARCV) \ - _(2673, ZEND_SUB_LONG_NO_OVERFLOW_SPEC_TMPVARCV_TMPVARCV) \ - _(2675, ZEND_SUB_LONG_NO_OVERFLOW_SPEC_TMPVARCV_TMPVARCV) \ + _(2666, ZEND_ADD_DOUBLE_SPEC_TMPVARCV_CONST) \ + _(2667, ZEND_ADD_DOUBLE_SPEC_TMPVARCV_TMPVARCV) \ + _(2668, ZEND_ADD_DOUBLE_SPEC_TMPVARCV_TMPVARCV) \ + _(2670, ZEND_ADD_DOUBLE_SPEC_TMPVARCV_TMPVARCV) \ + _(2672, ZEND_SUB_LONG_NO_OVERFLOW_SPEC_CONST_TMPVARCV) \ + _(2673, ZEND_SUB_LONG_NO_OVERFLOW_SPEC_CONST_TMPVARCV) \ + _(2675, ZEND_SUB_LONG_NO_OVERFLOW_SPEC_CONST_TMPVARCV) \ + _(2676, ZEND_SUB_LONG_NO_OVERFLOW_SPEC_TMPVARCV_CONST) \ + _(2677, ZEND_SUB_LONG_NO_OVERFLOW_SPEC_TMPVARCV_TMPVARCV) \ + _(2678, ZEND_SUB_LONG_NO_OVERFLOW_SPEC_TMPVARCV_TMPVARCV) \ + _(2680, 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) \ _(2685, ZEND_SUB_LONG_NO_OVERFLOW_SPEC_TMPVARCV_TMPVARCV) \ - _(2687, ZEND_SUB_LONG_SPEC_CONST_TMPVARCV) \ - _(2688, ZEND_SUB_LONG_SPEC_CONST_TMPVARCV) \ - _(2690, ZEND_SUB_LONG_SPEC_CONST_TMPVARCV) \ - _(2691, ZEND_SUB_LONG_SPEC_TMPVARCV_CONST) \ - _(2692, ZEND_SUB_LONG_SPEC_TMPVARCV_TMPVARCV) \ - _(2693, ZEND_SUB_LONG_SPEC_TMPVARCV_TMPVARCV) \ - _(2695, ZEND_SUB_LONG_SPEC_TMPVARCV_TMPVARCV) \ - _(2696, ZEND_SUB_LONG_SPEC_TMPVARCV_CONST) \ - _(2697, ZEND_SUB_LONG_SPEC_TMPVARCV_TMPVARCV) \ - _(2698, ZEND_SUB_LONG_SPEC_TMPVARCV_TMPVARCV) \ - _(2700, ZEND_SUB_LONG_SPEC_TMPVARCV_TMPVARCV) \ + _(2691, ZEND_SUB_LONG_NO_OVERFLOW_SPEC_TMPVARCV_CONST) \ + _(2692, ZEND_SUB_LONG_NO_OVERFLOW_SPEC_TMPVARCV_TMPVARCV) \ + _(2693, ZEND_SUB_LONG_NO_OVERFLOW_SPEC_TMPVARCV_TMPVARCV) \ + _(2695, ZEND_SUB_LONG_NO_OVERFLOW_SPEC_TMPVARCV_TMPVARCV) \ + _(2697, ZEND_SUB_LONG_SPEC_CONST_TMPVARCV) \ + _(2698, ZEND_SUB_LONG_SPEC_CONST_TMPVARCV) \ + _(2700, ZEND_SUB_LONG_SPEC_CONST_TMPVARCV) \ + _(2701, ZEND_SUB_LONG_SPEC_TMPVARCV_CONST) \ + _(2702, ZEND_SUB_LONG_SPEC_TMPVARCV_TMPVARCV) \ + _(2703, ZEND_SUB_LONG_SPEC_TMPVARCV_TMPVARCV) \ + _(2705, 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) \ _(2710, ZEND_SUB_LONG_SPEC_TMPVARCV_TMPVARCV) \ - _(2712, ZEND_SUB_DOUBLE_SPEC_CONST_TMPVARCV) \ - _(2713, ZEND_SUB_DOUBLE_SPEC_CONST_TMPVARCV) \ - _(2715, ZEND_SUB_DOUBLE_SPEC_CONST_TMPVARCV) \ - _(2716, ZEND_SUB_DOUBLE_SPEC_TMPVARCV_CONST) \ - _(2717, ZEND_SUB_DOUBLE_SPEC_TMPVARCV_TMPVARCV) \ - _(2718, ZEND_SUB_DOUBLE_SPEC_TMPVARCV_TMPVARCV) \ - _(2720, ZEND_SUB_DOUBLE_SPEC_TMPVARCV_TMPVARCV) \ - _(2721, ZEND_SUB_DOUBLE_SPEC_TMPVARCV_CONST) \ - _(2722, ZEND_SUB_DOUBLE_SPEC_TMPVARCV_TMPVARCV) \ - _(2723, ZEND_SUB_DOUBLE_SPEC_TMPVARCV_TMPVARCV) \ - _(2725, ZEND_SUB_DOUBLE_SPEC_TMPVARCV_TMPVARCV) \ + _(2716, ZEND_SUB_LONG_SPEC_TMPVARCV_CONST) \ + _(2717, ZEND_SUB_LONG_SPEC_TMPVARCV_TMPVARCV) \ + _(2718, ZEND_SUB_LONG_SPEC_TMPVARCV_TMPVARCV) \ + _(2720, ZEND_SUB_LONG_SPEC_TMPVARCV_TMPVARCV) \ + _(2722, ZEND_SUB_DOUBLE_SPEC_CONST_TMPVARCV) \ + _(2723, ZEND_SUB_DOUBLE_SPEC_CONST_TMPVARCV) \ + _(2725, ZEND_SUB_DOUBLE_SPEC_CONST_TMPVARCV) \ + _(2726, ZEND_SUB_DOUBLE_SPEC_TMPVARCV_CONST) \ + _(2727, ZEND_SUB_DOUBLE_SPEC_TMPVARCV_TMPVARCV) \ + _(2728, ZEND_SUB_DOUBLE_SPEC_TMPVARCV_TMPVARCV) \ + _(2730, 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) \ _(2735, ZEND_SUB_DOUBLE_SPEC_TMPVARCV_TMPVARCV) \ - _(2741, ZEND_MUL_LONG_NO_OVERFLOW_SPEC_TMPVARCV_CONST) \ - _(2742, ZEND_MUL_LONG_NO_OVERFLOW_SPEC_TMPVARCV_TMPVARCV) \ - _(2743, ZEND_MUL_LONG_NO_OVERFLOW_SPEC_TMPVARCV_TMPVARCV) \ - _(2745, ZEND_MUL_LONG_NO_OVERFLOW_SPEC_TMPVARCV_TMPVARCV) \ - _(2746, ZEND_MUL_LONG_NO_OVERFLOW_SPEC_TMPVARCV_CONST) \ - _(2747, ZEND_MUL_LONG_NO_OVERFLOW_SPEC_TMPVARCV_TMPVARCV) \ - _(2748, ZEND_MUL_LONG_NO_OVERFLOW_SPEC_TMPVARCV_TMPVARCV) \ - _(2750, ZEND_MUL_LONG_NO_OVERFLOW_SPEC_TMPVARCV_TMPVARCV) \ + _(2741, ZEND_SUB_DOUBLE_SPEC_TMPVARCV_CONST) \ + _(2742, ZEND_SUB_DOUBLE_SPEC_TMPVARCV_TMPVARCV) \ + _(2743, ZEND_SUB_DOUBLE_SPEC_TMPVARCV_TMPVARCV) \ + _(2745, ZEND_SUB_DOUBLE_SPEC_TMPVARCV_TMPVARCV) \ + _(2751, ZEND_MUL_LONG_NO_OVERFLOW_SPEC_TMPVARCV_CONST) \ + _(2752, ZEND_MUL_LONG_NO_OVERFLOW_SPEC_TMPVARCV_TMPVARCV) \ + _(2753, ZEND_MUL_LONG_NO_OVERFLOW_SPEC_TMPVARCV_TMPVARCV) \ + _(2755, 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) \ _(2760, ZEND_MUL_LONG_NO_OVERFLOW_SPEC_TMPVARCV_TMPVARCV) \ - _(2766, ZEND_MUL_LONG_SPEC_TMPVARCV_CONST) \ - _(2767, ZEND_MUL_LONG_SPEC_TMPVARCV_TMPVARCV) \ - _(2768, ZEND_MUL_LONG_SPEC_TMPVARCV_TMPVARCV) \ - _(2770, ZEND_MUL_LONG_SPEC_TMPVARCV_TMPVARCV) \ - _(2771, ZEND_MUL_LONG_SPEC_TMPVARCV_CONST) \ - _(2772, ZEND_MUL_LONG_SPEC_TMPVARCV_TMPVARCV) \ - _(2773, ZEND_MUL_LONG_SPEC_TMPVARCV_TMPVARCV) \ - _(2775, ZEND_MUL_LONG_SPEC_TMPVARCV_TMPVARCV) \ + _(2766, ZEND_MUL_LONG_NO_OVERFLOW_SPEC_TMPVARCV_CONST) \ + _(2767, ZEND_MUL_LONG_NO_OVERFLOW_SPEC_TMPVARCV_TMPVARCV) \ + _(2768, ZEND_MUL_LONG_NO_OVERFLOW_SPEC_TMPVARCV_TMPVARCV) \ + _(2770, ZEND_MUL_LONG_NO_OVERFLOW_SPEC_TMPVARCV_TMPVARCV) \ + _(2776, ZEND_MUL_LONG_SPEC_TMPVARCV_CONST) \ + _(2777, ZEND_MUL_LONG_SPEC_TMPVARCV_TMPVARCV) \ + _(2778, ZEND_MUL_LONG_SPEC_TMPVARCV_TMPVARCV) \ + _(2780, 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) \ _(2785, ZEND_MUL_LONG_SPEC_TMPVARCV_TMPVARCV) \ - _(2791, ZEND_MUL_DOUBLE_SPEC_TMPVARCV_CONST) \ - _(2792, ZEND_MUL_DOUBLE_SPEC_TMPVARCV_TMPVARCV) \ - _(2793, ZEND_MUL_DOUBLE_SPEC_TMPVARCV_TMPVARCV) \ - _(2795, ZEND_MUL_DOUBLE_SPEC_TMPVARCV_TMPVARCV) \ - _(2796, ZEND_MUL_DOUBLE_SPEC_TMPVARCV_CONST) \ - _(2797, ZEND_MUL_DOUBLE_SPEC_TMPVARCV_TMPVARCV) \ - _(2798, ZEND_MUL_DOUBLE_SPEC_TMPVARCV_TMPVARCV) \ - _(2800, ZEND_MUL_DOUBLE_SPEC_TMPVARCV_TMPVARCV) \ + _(2791, ZEND_MUL_LONG_SPEC_TMPVARCV_CONST) \ + _(2792, ZEND_MUL_LONG_SPEC_TMPVARCV_TMPVARCV) \ + _(2793, ZEND_MUL_LONG_SPEC_TMPVARCV_TMPVARCV) \ + _(2795, ZEND_MUL_LONG_SPEC_TMPVARCV_TMPVARCV) \ + _(2801, ZEND_MUL_DOUBLE_SPEC_TMPVARCV_CONST) \ + _(2802, ZEND_MUL_DOUBLE_SPEC_TMPVARCV_TMPVARCV) \ + _(2803, ZEND_MUL_DOUBLE_SPEC_TMPVARCV_TMPVARCV) \ + _(2805, 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) \ _(2810, ZEND_MUL_DOUBLE_SPEC_TMPVARCV_TMPVARCV) \ - _(2826, ZEND_IS_EQUAL_LONG_SPEC_TMPVARCV_CONST) \ - _(2827, ZEND_IS_EQUAL_LONG_SPEC_TMPVARCV_CONST_JMPZ) \ - _(2828, ZEND_IS_EQUAL_LONG_SPEC_TMPVARCV_CONST_JMPNZ) \ - _(2829, ZEND_IS_EQUAL_LONG_SPEC_TMPVARCV_TMPVARCV) \ - _(2830, ZEND_IS_EQUAL_LONG_SPEC_TMPVARCV_TMPVARCV_JMPZ) \ - _(2831, ZEND_IS_EQUAL_LONG_SPEC_TMPVARCV_TMPVARCV_JMPNZ) \ - _(2832, ZEND_IS_EQUAL_LONG_SPEC_TMPVARCV_TMPVARCV) \ - _(2833, ZEND_IS_EQUAL_LONG_SPEC_TMPVARCV_TMPVARCV_JMPZ) \ - _(2834, ZEND_IS_EQUAL_LONG_SPEC_TMPVARCV_TMPVARCV_JMPNZ) \ - _(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) \ - _(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) \ - _(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) \ - _(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) \ - _(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) \ - _(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) \ - _(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) \ - _(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) \ - _(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) \ - _(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) \ - _(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) \ - _(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) \ - _(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) \ - _(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) \ - _(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) \ - _(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) \ - _(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) \ - _(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) \ - _(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) \ - _(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) \ - _(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) \ - _(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) \ - _(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) \ - _(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) \ - _(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) \ - _(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) \ - _(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) \ - _(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) \ - _(3443, ZEND_QM_ASSIGN_LONG_SPEC_TMPVARCV) \ - _(3444, ZEND_QM_ASSIGN_DOUBLE_SPEC_CONST) \ - _(3445, ZEND_QM_ASSIGN_DOUBLE_SPEC_TMPVARCV) \ - _(3446, ZEND_QM_ASSIGN_DOUBLE_SPEC_TMPVARCV) \ - _(3448, ZEND_QM_ASSIGN_DOUBLE_SPEC_TMPVARCV) \ - _(3449, ZEND_QM_ASSIGN_NOREF_SPEC_CONST) \ - _(3450, ZEND_QM_ASSIGN_NOREF_SPEC_TMPVARCV) \ - _(3451, ZEND_QM_ASSIGN_NOREF_SPEC_TMPVARCV) \ - _(3453, ZEND_QM_ASSIGN_NOREF_SPEC_TMPVARCV) \ - _(3455, ZEND_FETCH_DIM_R_INDEX_SPEC_CONST_TMPVARCV) \ - _(3456, ZEND_FETCH_DIM_R_INDEX_SPEC_CONST_TMPVARCV) \ - _(3458, ZEND_FETCH_DIM_R_INDEX_SPEC_CONST_TMPVARCV) \ - _(3459, ZEND_FETCH_DIM_R_INDEX_SPEC_TMPVAR_CONST) \ - _(3460, ZEND_FETCH_DIM_R_INDEX_SPEC_TMPVAR_TMPVARCV) \ - _(3461, ZEND_FETCH_DIM_R_INDEX_SPEC_TMPVAR_TMPVARCV) \ - _(3463, ZEND_FETCH_DIM_R_INDEX_SPEC_TMPVAR_TMPVARCV) \ - _(3464, ZEND_FETCH_DIM_R_INDEX_SPEC_TMPVAR_CONST) \ - _(3465, ZEND_FETCH_DIM_R_INDEX_SPEC_TMPVAR_TMPVARCV) \ - _(3466, ZEND_FETCH_DIM_R_INDEX_SPEC_TMPVAR_TMPVARCV) \ - _(3468, ZEND_FETCH_DIM_R_INDEX_SPEC_TMPVAR_TMPVARCV) \ - _(3474, ZEND_FETCH_DIM_R_INDEX_SPEC_CV_CONST) \ - _(3475, ZEND_FETCH_DIM_R_INDEX_SPEC_CV_TMPVARCV) \ - _(3476, ZEND_FETCH_DIM_R_INDEX_SPEC_CV_TMPVARCV) \ - _(3478, ZEND_FETCH_DIM_R_INDEX_SPEC_CV_TMPVARCV) \ - _(3481, ZEND_SEND_VAR_SIMPLE_SPEC_VAR) \ - _(3483, ZEND_SEND_VAR_SIMPLE_SPEC_CV) \ - _(3486, ZEND_SEND_VAR_EX_SIMPLE_SPEC_VAR_UNUSED) \ - _(3488, ZEND_SEND_VAR_EX_SIMPLE_SPEC_CV_UNUSED) \ - _(3489, ZEND_SEND_VAL_SIMPLE_SPEC_CONST) \ - _(3490, ZEND_SEND_VAL_EX_SIMPLE_SPEC_CONST) \ - _(3491, ZEND_FE_FETCH_R_SIMPLE_SPEC_VAR_CV_RETVAL_UNUSED) \ - _(3492, ZEND_FE_FETCH_R_SIMPLE_SPEC_VAR_CV_RETVAL_USED) \ - _(3492+1, ZEND_NULL) + _(2816, ZEND_MUL_DOUBLE_SPEC_TMPVARCV_CONST) \ + _(2817, ZEND_MUL_DOUBLE_SPEC_TMPVARCV_TMPVARCV) \ + _(2818, ZEND_MUL_DOUBLE_SPEC_TMPVARCV_TMPVARCV) \ + _(2820, ZEND_MUL_DOUBLE_SPEC_TMPVARCV_TMPVARCV) \ + _(2836, ZEND_IS_EQUAL_LONG_SPEC_TMPVARCV_CONST) \ + _(2837, ZEND_IS_EQUAL_LONG_SPEC_TMPVARCV_CONST_JMPZ) \ + _(2838, ZEND_IS_EQUAL_LONG_SPEC_TMPVARCV_CONST_JMPNZ) \ + _(2839, ZEND_IS_EQUAL_LONG_SPEC_TMPVARCV_TMPVARCV) \ + _(2840, ZEND_IS_EQUAL_LONG_SPEC_TMPVARCV_TMPVARCV_JMPZ) \ + _(2841, ZEND_IS_EQUAL_LONG_SPEC_TMPVARCV_TMPVARCV_JMPNZ) \ + _(2842, ZEND_IS_EQUAL_LONG_SPEC_TMPVARCV_TMPVARCV) \ + _(2843, ZEND_IS_EQUAL_LONG_SPEC_TMPVARCV_TMPVARCV_JMPZ) \ + _(2844, ZEND_IS_EQUAL_LONG_SPEC_TMPVARCV_TMPVARCV_JMPNZ) \ + _(2848, ZEND_IS_EQUAL_LONG_SPEC_TMPVARCV_TMPVARCV) \ + _(2849, ZEND_IS_EQUAL_LONG_SPEC_TMPVARCV_TMPVARCV_JMPZ) \ + _(2850, ZEND_IS_EQUAL_LONG_SPEC_TMPVARCV_TMPVARCV_JMPNZ) \ + _(2851, ZEND_IS_EQUAL_LONG_SPEC_TMPVARCV_CONST) \ + _(2852, ZEND_IS_EQUAL_LONG_SPEC_TMPVARCV_CONST_JMPZ) \ + _(2853, ZEND_IS_EQUAL_LONG_SPEC_TMPVARCV_CONST_JMPNZ) \ + _(2854, ZEND_IS_EQUAL_LONG_SPEC_TMPVARCV_TMPVARCV) \ + _(2855, ZEND_IS_EQUAL_LONG_SPEC_TMPVARCV_TMPVARCV_JMPZ) \ + _(2856, ZEND_IS_EQUAL_LONG_SPEC_TMPVARCV_TMPVARCV_JMPNZ) \ + _(2857, ZEND_IS_EQUAL_LONG_SPEC_TMPVARCV_TMPVARCV) \ + _(2858, ZEND_IS_EQUAL_LONG_SPEC_TMPVARCV_TMPVARCV_JMPZ) \ + _(2859, ZEND_IS_EQUAL_LONG_SPEC_TMPVARCV_TMPVARCV_JMPNZ) \ + _(2863, ZEND_IS_EQUAL_LONG_SPEC_TMPVARCV_TMPVARCV) \ + _(2864, ZEND_IS_EQUAL_LONG_SPEC_TMPVARCV_TMPVARCV_JMPZ) \ + _(2865, ZEND_IS_EQUAL_LONG_SPEC_TMPVARCV_TMPVARCV_JMPNZ) \ + _(2881, ZEND_IS_EQUAL_LONG_SPEC_TMPVARCV_CONST) \ + _(2882, ZEND_IS_EQUAL_LONG_SPEC_TMPVARCV_CONST_JMPZ) \ + _(2883, ZEND_IS_EQUAL_LONG_SPEC_TMPVARCV_CONST_JMPNZ) \ + _(2884, ZEND_IS_EQUAL_LONG_SPEC_TMPVARCV_TMPVARCV) \ + _(2885, ZEND_IS_EQUAL_LONG_SPEC_TMPVARCV_TMPVARCV_JMPZ) \ + _(2886, ZEND_IS_EQUAL_LONG_SPEC_TMPVARCV_TMPVARCV_JMPNZ) \ + _(2887, ZEND_IS_EQUAL_LONG_SPEC_TMPVARCV_TMPVARCV) \ + _(2888, ZEND_IS_EQUAL_LONG_SPEC_TMPVARCV_TMPVARCV_JMPZ) \ + _(2889, ZEND_IS_EQUAL_LONG_SPEC_TMPVARCV_TMPVARCV_JMPNZ) \ + _(2893, ZEND_IS_EQUAL_LONG_SPEC_TMPVARCV_TMPVARCV) \ + _(2894, ZEND_IS_EQUAL_LONG_SPEC_TMPVARCV_TMPVARCV_JMPZ) \ + _(2895, ZEND_IS_EQUAL_LONG_SPEC_TMPVARCV_TMPVARCV_JMPNZ) \ + _(2911, ZEND_IS_EQUAL_DOUBLE_SPEC_TMPVARCV_CONST) \ + _(2912, ZEND_IS_EQUAL_DOUBLE_SPEC_TMPVARCV_CONST_JMPZ) \ + _(2913, ZEND_IS_EQUAL_DOUBLE_SPEC_TMPVARCV_CONST_JMPNZ) \ + _(2914, ZEND_IS_EQUAL_DOUBLE_SPEC_TMPVARCV_TMPVARCV) \ + _(2915, ZEND_IS_EQUAL_DOUBLE_SPEC_TMPVARCV_TMPVARCV_JMPZ) \ + _(2916, ZEND_IS_EQUAL_DOUBLE_SPEC_TMPVARCV_TMPVARCV_JMPNZ) \ + _(2917, ZEND_IS_EQUAL_DOUBLE_SPEC_TMPVARCV_TMPVARCV) \ + _(2918, ZEND_IS_EQUAL_DOUBLE_SPEC_TMPVARCV_TMPVARCV_JMPZ) \ + _(2919, ZEND_IS_EQUAL_DOUBLE_SPEC_TMPVARCV_TMPVARCV_JMPNZ) \ + _(2923, ZEND_IS_EQUAL_DOUBLE_SPEC_TMPVARCV_TMPVARCV) \ + _(2924, ZEND_IS_EQUAL_DOUBLE_SPEC_TMPVARCV_TMPVARCV_JMPZ) \ + _(2925, ZEND_IS_EQUAL_DOUBLE_SPEC_TMPVARCV_TMPVARCV_JMPNZ) \ + _(2926, ZEND_IS_EQUAL_DOUBLE_SPEC_TMPVARCV_CONST) \ + _(2927, ZEND_IS_EQUAL_DOUBLE_SPEC_TMPVARCV_CONST_JMPZ) \ + _(2928, ZEND_IS_EQUAL_DOUBLE_SPEC_TMPVARCV_CONST_JMPNZ) \ + _(2929, ZEND_IS_EQUAL_DOUBLE_SPEC_TMPVARCV_TMPVARCV) \ + _(2930, ZEND_IS_EQUAL_DOUBLE_SPEC_TMPVARCV_TMPVARCV_JMPZ) \ + _(2931, ZEND_IS_EQUAL_DOUBLE_SPEC_TMPVARCV_TMPVARCV_JMPNZ) \ + _(2932, ZEND_IS_EQUAL_DOUBLE_SPEC_TMPVARCV_TMPVARCV) \ + _(2933, ZEND_IS_EQUAL_DOUBLE_SPEC_TMPVARCV_TMPVARCV_JMPZ) \ + _(2934, ZEND_IS_EQUAL_DOUBLE_SPEC_TMPVARCV_TMPVARCV_JMPNZ) \ + _(2938, ZEND_IS_EQUAL_DOUBLE_SPEC_TMPVARCV_TMPVARCV) \ + _(2939, ZEND_IS_EQUAL_DOUBLE_SPEC_TMPVARCV_TMPVARCV_JMPZ) \ + _(2940, ZEND_IS_EQUAL_DOUBLE_SPEC_TMPVARCV_TMPVARCV_JMPNZ) \ + _(2956, ZEND_IS_EQUAL_DOUBLE_SPEC_TMPVARCV_CONST) \ + _(2957, ZEND_IS_EQUAL_DOUBLE_SPEC_TMPVARCV_CONST_JMPZ) \ + _(2958, ZEND_IS_EQUAL_DOUBLE_SPEC_TMPVARCV_CONST_JMPNZ) \ + _(2959, ZEND_IS_EQUAL_DOUBLE_SPEC_TMPVARCV_TMPVARCV) \ + _(2960, ZEND_IS_EQUAL_DOUBLE_SPEC_TMPVARCV_TMPVARCV_JMPZ) \ + _(2961, ZEND_IS_EQUAL_DOUBLE_SPEC_TMPVARCV_TMPVARCV_JMPNZ) \ + _(2962, ZEND_IS_EQUAL_DOUBLE_SPEC_TMPVARCV_TMPVARCV) \ + _(2963, ZEND_IS_EQUAL_DOUBLE_SPEC_TMPVARCV_TMPVARCV_JMPZ) \ + _(2964, ZEND_IS_EQUAL_DOUBLE_SPEC_TMPVARCV_TMPVARCV_JMPNZ) \ + _(2968, ZEND_IS_EQUAL_DOUBLE_SPEC_TMPVARCV_TMPVARCV) \ + _(2969, ZEND_IS_EQUAL_DOUBLE_SPEC_TMPVARCV_TMPVARCV_JMPZ) \ + _(2970, ZEND_IS_EQUAL_DOUBLE_SPEC_TMPVARCV_TMPVARCV_JMPNZ) \ + _(2986, ZEND_IS_NOT_EQUAL_LONG_SPEC_TMPVARCV_CONST) \ + _(2987, ZEND_IS_NOT_EQUAL_LONG_SPEC_TMPVARCV_CONST_JMPZ) \ + _(2988, ZEND_IS_NOT_EQUAL_LONG_SPEC_TMPVARCV_CONST_JMPNZ) \ + _(2989, ZEND_IS_NOT_EQUAL_LONG_SPEC_TMPVARCV_TMPVARCV) \ + _(2990, ZEND_IS_NOT_EQUAL_LONG_SPEC_TMPVARCV_TMPVARCV_JMPZ) \ + _(2991, ZEND_IS_NOT_EQUAL_LONG_SPEC_TMPVARCV_TMPVARCV_JMPNZ) \ + _(2992, ZEND_IS_NOT_EQUAL_LONG_SPEC_TMPVARCV_TMPVARCV) \ + _(2993, ZEND_IS_NOT_EQUAL_LONG_SPEC_TMPVARCV_TMPVARCV_JMPZ) \ + _(2994, ZEND_IS_NOT_EQUAL_LONG_SPEC_TMPVARCV_TMPVARCV_JMPNZ) \ + _(2998, ZEND_IS_NOT_EQUAL_LONG_SPEC_TMPVARCV_TMPVARCV) \ + _(2999, ZEND_IS_NOT_EQUAL_LONG_SPEC_TMPVARCV_TMPVARCV_JMPZ) \ + _(3000, ZEND_IS_NOT_EQUAL_LONG_SPEC_TMPVARCV_TMPVARCV_JMPNZ) \ + _(3001, ZEND_IS_NOT_EQUAL_LONG_SPEC_TMPVARCV_CONST) \ + _(3002, ZEND_IS_NOT_EQUAL_LONG_SPEC_TMPVARCV_CONST_JMPZ) \ + _(3003, ZEND_IS_NOT_EQUAL_LONG_SPEC_TMPVARCV_CONST_JMPNZ) \ + _(3004, ZEND_IS_NOT_EQUAL_LONG_SPEC_TMPVARCV_TMPVARCV) \ + _(3005, ZEND_IS_NOT_EQUAL_LONG_SPEC_TMPVARCV_TMPVARCV_JMPZ) \ + _(3006, ZEND_IS_NOT_EQUAL_LONG_SPEC_TMPVARCV_TMPVARCV_JMPNZ) \ + _(3007, ZEND_IS_NOT_EQUAL_LONG_SPEC_TMPVARCV_TMPVARCV) \ + _(3008, ZEND_IS_NOT_EQUAL_LONG_SPEC_TMPVARCV_TMPVARCV_JMPZ) \ + _(3009, ZEND_IS_NOT_EQUAL_LONG_SPEC_TMPVARCV_TMPVARCV_JMPNZ) \ + _(3013, ZEND_IS_NOT_EQUAL_LONG_SPEC_TMPVARCV_TMPVARCV) \ + _(3014, ZEND_IS_NOT_EQUAL_LONG_SPEC_TMPVARCV_TMPVARCV_JMPZ) \ + _(3015, ZEND_IS_NOT_EQUAL_LONG_SPEC_TMPVARCV_TMPVARCV_JMPNZ) \ + _(3031, ZEND_IS_NOT_EQUAL_LONG_SPEC_TMPVARCV_CONST) \ + _(3032, ZEND_IS_NOT_EQUAL_LONG_SPEC_TMPVARCV_CONST_JMPZ) \ + _(3033, ZEND_IS_NOT_EQUAL_LONG_SPEC_TMPVARCV_CONST_JMPNZ) \ + _(3034, ZEND_IS_NOT_EQUAL_LONG_SPEC_TMPVARCV_TMPVARCV) \ + _(3035, ZEND_IS_NOT_EQUAL_LONG_SPEC_TMPVARCV_TMPVARCV_JMPZ) \ + _(3036, ZEND_IS_NOT_EQUAL_LONG_SPEC_TMPVARCV_TMPVARCV_JMPNZ) \ + _(3037, ZEND_IS_NOT_EQUAL_LONG_SPEC_TMPVARCV_TMPVARCV) \ + _(3038, ZEND_IS_NOT_EQUAL_LONG_SPEC_TMPVARCV_TMPVARCV_JMPZ) \ + _(3039, ZEND_IS_NOT_EQUAL_LONG_SPEC_TMPVARCV_TMPVARCV_JMPNZ) \ + _(3043, ZEND_IS_NOT_EQUAL_LONG_SPEC_TMPVARCV_TMPVARCV) \ + _(3044, ZEND_IS_NOT_EQUAL_LONG_SPEC_TMPVARCV_TMPVARCV_JMPZ) \ + _(3045, ZEND_IS_NOT_EQUAL_LONG_SPEC_TMPVARCV_TMPVARCV_JMPNZ) \ + _(3061, ZEND_IS_NOT_EQUAL_DOUBLE_SPEC_TMPVARCV_CONST) \ + _(3062, ZEND_IS_NOT_EQUAL_DOUBLE_SPEC_TMPVARCV_CONST_JMPZ) \ + _(3063, ZEND_IS_NOT_EQUAL_DOUBLE_SPEC_TMPVARCV_CONST_JMPNZ) \ + _(3064, ZEND_IS_NOT_EQUAL_DOUBLE_SPEC_TMPVARCV_TMPVARCV) \ + _(3065, ZEND_IS_NOT_EQUAL_DOUBLE_SPEC_TMPVARCV_TMPVARCV_JMPZ) \ + _(3066, ZEND_IS_NOT_EQUAL_DOUBLE_SPEC_TMPVARCV_TMPVARCV_JMPNZ) \ + _(3067, ZEND_IS_NOT_EQUAL_DOUBLE_SPEC_TMPVARCV_TMPVARCV) \ + _(3068, ZEND_IS_NOT_EQUAL_DOUBLE_SPEC_TMPVARCV_TMPVARCV_JMPZ) \ + _(3069, ZEND_IS_NOT_EQUAL_DOUBLE_SPEC_TMPVARCV_TMPVARCV_JMPNZ) \ + _(3073, ZEND_IS_NOT_EQUAL_DOUBLE_SPEC_TMPVARCV_TMPVARCV) \ + _(3074, ZEND_IS_NOT_EQUAL_DOUBLE_SPEC_TMPVARCV_TMPVARCV_JMPZ) \ + _(3075, ZEND_IS_NOT_EQUAL_DOUBLE_SPEC_TMPVARCV_TMPVARCV_JMPNZ) \ + _(3076, ZEND_IS_NOT_EQUAL_DOUBLE_SPEC_TMPVARCV_CONST) \ + _(3077, ZEND_IS_NOT_EQUAL_DOUBLE_SPEC_TMPVARCV_CONST_JMPZ) \ + _(3078, ZEND_IS_NOT_EQUAL_DOUBLE_SPEC_TMPVARCV_CONST_JMPNZ) \ + _(3079, ZEND_IS_NOT_EQUAL_DOUBLE_SPEC_TMPVARCV_TMPVARCV) \ + _(3080, ZEND_IS_NOT_EQUAL_DOUBLE_SPEC_TMPVARCV_TMPVARCV_JMPZ) \ + _(3081, ZEND_IS_NOT_EQUAL_DOUBLE_SPEC_TMPVARCV_TMPVARCV_JMPNZ) \ + _(3082, ZEND_IS_NOT_EQUAL_DOUBLE_SPEC_TMPVARCV_TMPVARCV) \ + _(3083, ZEND_IS_NOT_EQUAL_DOUBLE_SPEC_TMPVARCV_TMPVARCV_JMPZ) \ + _(3084, ZEND_IS_NOT_EQUAL_DOUBLE_SPEC_TMPVARCV_TMPVARCV_JMPNZ) \ + _(3088, ZEND_IS_NOT_EQUAL_DOUBLE_SPEC_TMPVARCV_TMPVARCV) \ + _(3089, ZEND_IS_NOT_EQUAL_DOUBLE_SPEC_TMPVARCV_TMPVARCV_JMPZ) \ + _(3090, ZEND_IS_NOT_EQUAL_DOUBLE_SPEC_TMPVARCV_TMPVARCV_JMPNZ) \ + _(3106, ZEND_IS_NOT_EQUAL_DOUBLE_SPEC_TMPVARCV_CONST) \ + _(3107, ZEND_IS_NOT_EQUAL_DOUBLE_SPEC_TMPVARCV_CONST_JMPZ) \ + _(3108, ZEND_IS_NOT_EQUAL_DOUBLE_SPEC_TMPVARCV_CONST_JMPNZ) \ + _(3109, ZEND_IS_NOT_EQUAL_DOUBLE_SPEC_TMPVARCV_TMPVARCV) \ + _(3110, ZEND_IS_NOT_EQUAL_DOUBLE_SPEC_TMPVARCV_TMPVARCV_JMPZ) \ + _(3111, ZEND_IS_NOT_EQUAL_DOUBLE_SPEC_TMPVARCV_TMPVARCV_JMPNZ) \ + _(3112, ZEND_IS_NOT_EQUAL_DOUBLE_SPEC_TMPVARCV_TMPVARCV) \ + _(3113, ZEND_IS_NOT_EQUAL_DOUBLE_SPEC_TMPVARCV_TMPVARCV_JMPZ) \ + _(3114, ZEND_IS_NOT_EQUAL_DOUBLE_SPEC_TMPVARCV_TMPVARCV_JMPNZ) \ + _(3118, ZEND_IS_NOT_EQUAL_DOUBLE_SPEC_TMPVARCV_TMPVARCV) \ + _(3119, ZEND_IS_NOT_EQUAL_DOUBLE_SPEC_TMPVARCV_TMPVARCV_JMPZ) \ + _(3120, ZEND_IS_NOT_EQUAL_DOUBLE_SPEC_TMPVARCV_TMPVARCV_JMPNZ) \ + _(3121, ZEND_IS_IDENTICAL_EMPTY_ARRAY_SPEC_TMPVARCV_CONST) \ + _(3122, ZEND_IS_IDENTICAL_EMPTY_ARRAY_SPEC_TMPVARCV_CONST_JMPZ) \ + _(3123, ZEND_IS_IDENTICAL_EMPTY_ARRAY_SPEC_TMPVARCV_CONST_JMPNZ) \ + _(3124, ZEND_IS_NOT_IDENTICAL_EMPTY_ARRAY_SPEC_TMPVARCV_CONST) \ + _(3125, ZEND_IS_NOT_IDENTICAL_EMPTY_ARRAY_SPEC_TMPVARCV_CONST_JMPZ) \ + _(3126, ZEND_IS_NOT_IDENTICAL_EMPTY_ARRAY_SPEC_TMPVARCV_CONST_JMPNZ) \ + _(3127, ZEND_IS_IDENTICAL_NOTHROW_SPEC_CV_CONST) \ + _(3131, ZEND_IS_IDENTICAL_NOTHROW_SPEC_CV_CV) \ + _(3132, ZEND_IS_NOT_IDENTICAL_NOTHROW_SPEC_CV_CONST) \ + _(3136, ZEND_IS_NOT_IDENTICAL_NOTHROW_SPEC_CV_CV) \ + _(3140, ZEND_IS_SMALLER_LONG_SPEC_CONST_TMPVARCV) \ + _(3141, ZEND_IS_SMALLER_LONG_SPEC_CONST_TMPVARCV_JMPZ) \ + _(3142, ZEND_IS_SMALLER_LONG_SPEC_CONST_TMPVARCV_JMPNZ) \ + _(3143, ZEND_IS_SMALLER_LONG_SPEC_CONST_TMPVARCV) \ + _(3144, ZEND_IS_SMALLER_LONG_SPEC_CONST_TMPVARCV_JMPZ) \ + _(3145, ZEND_IS_SMALLER_LONG_SPEC_CONST_TMPVARCV_JMPNZ) \ + _(3149, ZEND_IS_SMALLER_LONG_SPEC_CONST_TMPVARCV) \ + _(3150, ZEND_IS_SMALLER_LONG_SPEC_CONST_TMPVARCV_JMPZ) \ + _(3151, ZEND_IS_SMALLER_LONG_SPEC_CONST_TMPVARCV_JMPNZ) \ + _(3152, ZEND_IS_SMALLER_LONG_SPEC_TMPVARCV_CONST) \ + _(3153, ZEND_IS_SMALLER_LONG_SPEC_TMPVARCV_CONST_JMPZ) \ + _(3154, ZEND_IS_SMALLER_LONG_SPEC_TMPVARCV_CONST_JMPNZ) \ + _(3155, ZEND_IS_SMALLER_LONG_SPEC_TMPVARCV_TMPVARCV) \ + _(3156, ZEND_IS_SMALLER_LONG_SPEC_TMPVARCV_TMPVARCV_JMPZ) \ + _(3157, ZEND_IS_SMALLER_LONG_SPEC_TMPVARCV_TMPVARCV_JMPNZ) \ + _(3158, ZEND_IS_SMALLER_LONG_SPEC_TMPVARCV_TMPVARCV) \ + _(3159, ZEND_IS_SMALLER_LONG_SPEC_TMPVARCV_TMPVARCV_JMPZ) \ + _(3160, ZEND_IS_SMALLER_LONG_SPEC_TMPVARCV_TMPVARCV_JMPNZ) \ + _(3164, ZEND_IS_SMALLER_LONG_SPEC_TMPVARCV_TMPVARCV) \ + _(3165, ZEND_IS_SMALLER_LONG_SPEC_TMPVARCV_TMPVARCV_JMPZ) \ + _(3166, ZEND_IS_SMALLER_LONG_SPEC_TMPVARCV_TMPVARCV_JMPNZ) \ + _(3167, ZEND_IS_SMALLER_LONG_SPEC_TMPVARCV_CONST) \ + _(3168, ZEND_IS_SMALLER_LONG_SPEC_TMPVARCV_CONST_JMPZ) \ + _(3169, ZEND_IS_SMALLER_LONG_SPEC_TMPVARCV_CONST_JMPNZ) \ + _(3170, ZEND_IS_SMALLER_LONG_SPEC_TMPVARCV_TMPVARCV) \ + _(3171, ZEND_IS_SMALLER_LONG_SPEC_TMPVARCV_TMPVARCV_JMPZ) \ + _(3172, ZEND_IS_SMALLER_LONG_SPEC_TMPVARCV_TMPVARCV_JMPNZ) \ + _(3173, ZEND_IS_SMALLER_LONG_SPEC_TMPVARCV_TMPVARCV) \ + _(3174, ZEND_IS_SMALLER_LONG_SPEC_TMPVARCV_TMPVARCV_JMPZ) \ + _(3175, ZEND_IS_SMALLER_LONG_SPEC_TMPVARCV_TMPVARCV_JMPNZ) \ + _(3179, ZEND_IS_SMALLER_LONG_SPEC_TMPVARCV_TMPVARCV) \ + _(3180, ZEND_IS_SMALLER_LONG_SPEC_TMPVARCV_TMPVARCV_JMPZ) \ + _(3181, ZEND_IS_SMALLER_LONG_SPEC_TMPVARCV_TMPVARCV_JMPNZ) \ + _(3197, ZEND_IS_SMALLER_LONG_SPEC_TMPVARCV_CONST) \ + _(3198, ZEND_IS_SMALLER_LONG_SPEC_TMPVARCV_CONST_JMPZ) \ + _(3199, ZEND_IS_SMALLER_LONG_SPEC_TMPVARCV_CONST_JMPNZ) \ + _(3200, ZEND_IS_SMALLER_LONG_SPEC_TMPVARCV_TMPVARCV) \ + _(3201, ZEND_IS_SMALLER_LONG_SPEC_TMPVARCV_TMPVARCV_JMPZ) \ + _(3202, ZEND_IS_SMALLER_LONG_SPEC_TMPVARCV_TMPVARCV_JMPNZ) \ + _(3203, ZEND_IS_SMALLER_LONG_SPEC_TMPVARCV_TMPVARCV) \ + _(3204, ZEND_IS_SMALLER_LONG_SPEC_TMPVARCV_TMPVARCV_JMPZ) \ + _(3205, ZEND_IS_SMALLER_LONG_SPEC_TMPVARCV_TMPVARCV_JMPNZ) \ + _(3209, ZEND_IS_SMALLER_LONG_SPEC_TMPVARCV_TMPVARCV) \ + _(3210, ZEND_IS_SMALLER_LONG_SPEC_TMPVARCV_TMPVARCV_JMPZ) \ + _(3211, ZEND_IS_SMALLER_LONG_SPEC_TMPVARCV_TMPVARCV_JMPNZ) \ + _(3215, ZEND_IS_SMALLER_DOUBLE_SPEC_CONST_TMPVARCV) \ + _(3216, ZEND_IS_SMALLER_DOUBLE_SPEC_CONST_TMPVARCV_JMPZ) \ + _(3217, ZEND_IS_SMALLER_DOUBLE_SPEC_CONST_TMPVARCV_JMPNZ) \ + _(3218, ZEND_IS_SMALLER_DOUBLE_SPEC_CONST_TMPVARCV) \ + _(3219, ZEND_IS_SMALLER_DOUBLE_SPEC_CONST_TMPVARCV_JMPZ) \ + _(3220, ZEND_IS_SMALLER_DOUBLE_SPEC_CONST_TMPVARCV_JMPNZ) \ + _(3224, ZEND_IS_SMALLER_DOUBLE_SPEC_CONST_TMPVARCV) \ + _(3225, ZEND_IS_SMALLER_DOUBLE_SPEC_CONST_TMPVARCV_JMPZ) \ + _(3226, ZEND_IS_SMALLER_DOUBLE_SPEC_CONST_TMPVARCV_JMPNZ) \ + _(3227, ZEND_IS_SMALLER_DOUBLE_SPEC_TMPVARCV_CONST) \ + _(3228, ZEND_IS_SMALLER_DOUBLE_SPEC_TMPVARCV_CONST_JMPZ) \ + _(3229, ZEND_IS_SMALLER_DOUBLE_SPEC_TMPVARCV_CONST_JMPNZ) \ + _(3230, ZEND_IS_SMALLER_DOUBLE_SPEC_TMPVARCV_TMPVARCV) \ + _(3231, ZEND_IS_SMALLER_DOUBLE_SPEC_TMPVARCV_TMPVARCV_JMPZ) \ + _(3232, ZEND_IS_SMALLER_DOUBLE_SPEC_TMPVARCV_TMPVARCV_JMPNZ) \ + _(3233, ZEND_IS_SMALLER_DOUBLE_SPEC_TMPVARCV_TMPVARCV) \ + _(3234, ZEND_IS_SMALLER_DOUBLE_SPEC_TMPVARCV_TMPVARCV_JMPZ) \ + _(3235, ZEND_IS_SMALLER_DOUBLE_SPEC_TMPVARCV_TMPVARCV_JMPNZ) \ + _(3239, ZEND_IS_SMALLER_DOUBLE_SPEC_TMPVARCV_TMPVARCV) \ + _(3240, ZEND_IS_SMALLER_DOUBLE_SPEC_TMPVARCV_TMPVARCV_JMPZ) \ + _(3241, ZEND_IS_SMALLER_DOUBLE_SPEC_TMPVARCV_TMPVARCV_JMPNZ) \ + _(3242, ZEND_IS_SMALLER_DOUBLE_SPEC_TMPVARCV_CONST) \ + _(3243, ZEND_IS_SMALLER_DOUBLE_SPEC_TMPVARCV_CONST_JMPZ) \ + _(3244, ZEND_IS_SMALLER_DOUBLE_SPEC_TMPVARCV_CONST_JMPNZ) \ + _(3245, ZEND_IS_SMALLER_DOUBLE_SPEC_TMPVARCV_TMPVARCV) \ + _(3246, ZEND_IS_SMALLER_DOUBLE_SPEC_TMPVARCV_TMPVARCV_JMPZ) \ + _(3247, ZEND_IS_SMALLER_DOUBLE_SPEC_TMPVARCV_TMPVARCV_JMPNZ) \ + _(3248, ZEND_IS_SMALLER_DOUBLE_SPEC_TMPVARCV_TMPVARCV) \ + _(3249, ZEND_IS_SMALLER_DOUBLE_SPEC_TMPVARCV_TMPVARCV_JMPZ) \ + _(3250, ZEND_IS_SMALLER_DOUBLE_SPEC_TMPVARCV_TMPVARCV_JMPNZ) \ + _(3254, ZEND_IS_SMALLER_DOUBLE_SPEC_TMPVARCV_TMPVARCV) \ + _(3255, ZEND_IS_SMALLER_DOUBLE_SPEC_TMPVARCV_TMPVARCV_JMPZ) \ + _(3256, ZEND_IS_SMALLER_DOUBLE_SPEC_TMPVARCV_TMPVARCV_JMPNZ) \ + _(3272, ZEND_IS_SMALLER_DOUBLE_SPEC_TMPVARCV_CONST) \ + _(3273, ZEND_IS_SMALLER_DOUBLE_SPEC_TMPVARCV_CONST_JMPZ) \ + _(3274, ZEND_IS_SMALLER_DOUBLE_SPEC_TMPVARCV_CONST_JMPNZ) \ + _(3275, ZEND_IS_SMALLER_DOUBLE_SPEC_TMPVARCV_TMPVARCV) \ + _(3276, ZEND_IS_SMALLER_DOUBLE_SPEC_TMPVARCV_TMPVARCV_JMPZ) \ + _(3277, ZEND_IS_SMALLER_DOUBLE_SPEC_TMPVARCV_TMPVARCV_JMPNZ) \ + _(3278, ZEND_IS_SMALLER_DOUBLE_SPEC_TMPVARCV_TMPVARCV) \ + _(3279, ZEND_IS_SMALLER_DOUBLE_SPEC_TMPVARCV_TMPVARCV_JMPZ) \ + _(3280, ZEND_IS_SMALLER_DOUBLE_SPEC_TMPVARCV_TMPVARCV_JMPNZ) \ + _(3284, ZEND_IS_SMALLER_DOUBLE_SPEC_TMPVARCV_TMPVARCV) \ + _(3285, ZEND_IS_SMALLER_DOUBLE_SPEC_TMPVARCV_TMPVARCV_JMPZ) \ + _(3286, ZEND_IS_SMALLER_DOUBLE_SPEC_TMPVARCV_TMPVARCV_JMPNZ) \ + _(3290, ZEND_IS_SMALLER_OR_EQUAL_LONG_SPEC_CONST_TMPVARCV) \ + _(3291, ZEND_IS_SMALLER_OR_EQUAL_LONG_SPEC_CONST_TMPVARCV_JMPZ) \ + _(3292, ZEND_IS_SMALLER_OR_EQUAL_LONG_SPEC_CONST_TMPVARCV_JMPNZ) \ + _(3293, ZEND_IS_SMALLER_OR_EQUAL_LONG_SPEC_CONST_TMPVARCV) \ + _(3294, ZEND_IS_SMALLER_OR_EQUAL_LONG_SPEC_CONST_TMPVARCV_JMPZ) \ + _(3295, ZEND_IS_SMALLER_OR_EQUAL_LONG_SPEC_CONST_TMPVARCV_JMPNZ) \ + _(3299, ZEND_IS_SMALLER_OR_EQUAL_LONG_SPEC_CONST_TMPVARCV) \ + _(3300, ZEND_IS_SMALLER_OR_EQUAL_LONG_SPEC_CONST_TMPVARCV_JMPZ) \ + _(3301, ZEND_IS_SMALLER_OR_EQUAL_LONG_SPEC_CONST_TMPVARCV_JMPNZ) \ + _(3302, ZEND_IS_SMALLER_OR_EQUAL_LONG_SPEC_TMPVARCV_CONST) \ + _(3303, ZEND_IS_SMALLER_OR_EQUAL_LONG_SPEC_TMPVARCV_CONST_JMPZ) \ + _(3304, ZEND_IS_SMALLER_OR_EQUAL_LONG_SPEC_TMPVARCV_CONST_JMPNZ) \ + _(3305, ZEND_IS_SMALLER_OR_EQUAL_LONG_SPEC_TMPVARCV_TMPVARCV) \ + _(3306, ZEND_IS_SMALLER_OR_EQUAL_LONG_SPEC_TMPVARCV_TMPVARCV_JMPZ) \ + _(3307, ZEND_IS_SMALLER_OR_EQUAL_LONG_SPEC_TMPVARCV_TMPVARCV_JMPNZ) \ + _(3308, ZEND_IS_SMALLER_OR_EQUAL_LONG_SPEC_TMPVARCV_TMPVARCV) \ + _(3309, ZEND_IS_SMALLER_OR_EQUAL_LONG_SPEC_TMPVARCV_TMPVARCV_JMPZ) \ + _(3310, ZEND_IS_SMALLER_OR_EQUAL_LONG_SPEC_TMPVARCV_TMPVARCV_JMPNZ) \ + _(3314, ZEND_IS_SMALLER_OR_EQUAL_LONG_SPEC_TMPVARCV_TMPVARCV) \ + _(3315, ZEND_IS_SMALLER_OR_EQUAL_LONG_SPEC_TMPVARCV_TMPVARCV_JMPZ) \ + _(3316, ZEND_IS_SMALLER_OR_EQUAL_LONG_SPEC_TMPVARCV_TMPVARCV_JMPNZ) \ + _(3317, ZEND_IS_SMALLER_OR_EQUAL_LONG_SPEC_TMPVARCV_CONST) \ + _(3318, ZEND_IS_SMALLER_OR_EQUAL_LONG_SPEC_TMPVARCV_CONST_JMPZ) \ + _(3319, ZEND_IS_SMALLER_OR_EQUAL_LONG_SPEC_TMPVARCV_CONST_JMPNZ) \ + _(3320, ZEND_IS_SMALLER_OR_EQUAL_LONG_SPEC_TMPVARCV_TMPVARCV) \ + _(3321, ZEND_IS_SMALLER_OR_EQUAL_LONG_SPEC_TMPVARCV_TMPVARCV_JMPZ) \ + _(3322, ZEND_IS_SMALLER_OR_EQUAL_LONG_SPEC_TMPVARCV_TMPVARCV_JMPNZ) \ + _(3323, ZEND_IS_SMALLER_OR_EQUAL_LONG_SPEC_TMPVARCV_TMPVARCV) \ + _(3324, ZEND_IS_SMALLER_OR_EQUAL_LONG_SPEC_TMPVARCV_TMPVARCV_JMPZ) \ + _(3325, ZEND_IS_SMALLER_OR_EQUAL_LONG_SPEC_TMPVARCV_TMPVARCV_JMPNZ) \ + _(3329, ZEND_IS_SMALLER_OR_EQUAL_LONG_SPEC_TMPVARCV_TMPVARCV) \ + _(3330, ZEND_IS_SMALLER_OR_EQUAL_LONG_SPEC_TMPVARCV_TMPVARCV_JMPZ) \ + _(3331, ZEND_IS_SMALLER_OR_EQUAL_LONG_SPEC_TMPVARCV_TMPVARCV_JMPNZ) \ + _(3347, ZEND_IS_SMALLER_OR_EQUAL_LONG_SPEC_TMPVARCV_CONST) \ + _(3348, ZEND_IS_SMALLER_OR_EQUAL_LONG_SPEC_TMPVARCV_CONST_JMPZ) \ + _(3349, ZEND_IS_SMALLER_OR_EQUAL_LONG_SPEC_TMPVARCV_CONST_JMPNZ) \ + _(3350, ZEND_IS_SMALLER_OR_EQUAL_LONG_SPEC_TMPVARCV_TMPVARCV) \ + _(3351, ZEND_IS_SMALLER_OR_EQUAL_LONG_SPEC_TMPVARCV_TMPVARCV_JMPZ) \ + _(3352, ZEND_IS_SMALLER_OR_EQUAL_LONG_SPEC_TMPVARCV_TMPVARCV_JMPNZ) \ + _(3353, ZEND_IS_SMALLER_OR_EQUAL_LONG_SPEC_TMPVARCV_TMPVARCV) \ + _(3354, ZEND_IS_SMALLER_OR_EQUAL_LONG_SPEC_TMPVARCV_TMPVARCV_JMPZ) \ + _(3355, ZEND_IS_SMALLER_OR_EQUAL_LONG_SPEC_TMPVARCV_TMPVARCV_JMPNZ) \ + _(3359, ZEND_IS_SMALLER_OR_EQUAL_LONG_SPEC_TMPVARCV_TMPVARCV) \ + _(3360, ZEND_IS_SMALLER_OR_EQUAL_LONG_SPEC_TMPVARCV_TMPVARCV_JMPZ) \ + _(3361, ZEND_IS_SMALLER_OR_EQUAL_LONG_SPEC_TMPVARCV_TMPVARCV_JMPNZ) \ + _(3365, ZEND_IS_SMALLER_OR_EQUAL_DOUBLE_SPEC_CONST_TMPVARCV) \ + _(3366, ZEND_IS_SMALLER_OR_EQUAL_DOUBLE_SPEC_CONST_TMPVARCV_JMPZ) \ + _(3367, ZEND_IS_SMALLER_OR_EQUAL_DOUBLE_SPEC_CONST_TMPVARCV_JMPNZ) \ + _(3368, ZEND_IS_SMALLER_OR_EQUAL_DOUBLE_SPEC_CONST_TMPVARCV) \ + _(3369, ZEND_IS_SMALLER_OR_EQUAL_DOUBLE_SPEC_CONST_TMPVARCV_JMPZ) \ + _(3370, ZEND_IS_SMALLER_OR_EQUAL_DOUBLE_SPEC_CONST_TMPVARCV_JMPNZ) \ + _(3374, ZEND_IS_SMALLER_OR_EQUAL_DOUBLE_SPEC_CONST_TMPVARCV) \ + _(3375, ZEND_IS_SMALLER_OR_EQUAL_DOUBLE_SPEC_CONST_TMPVARCV_JMPZ) \ + _(3376, ZEND_IS_SMALLER_OR_EQUAL_DOUBLE_SPEC_CONST_TMPVARCV_JMPNZ) \ + _(3377, ZEND_IS_SMALLER_OR_EQUAL_DOUBLE_SPEC_TMPVARCV_CONST) \ + _(3378, ZEND_IS_SMALLER_OR_EQUAL_DOUBLE_SPEC_TMPVARCV_CONST_JMPZ) \ + _(3379, ZEND_IS_SMALLER_OR_EQUAL_DOUBLE_SPEC_TMPVARCV_CONST_JMPNZ) \ + _(3380, ZEND_IS_SMALLER_OR_EQUAL_DOUBLE_SPEC_TMPVARCV_TMPVARCV) \ + _(3381, ZEND_IS_SMALLER_OR_EQUAL_DOUBLE_SPEC_TMPVARCV_TMPVARCV_JMPZ) \ + _(3382, ZEND_IS_SMALLER_OR_EQUAL_DOUBLE_SPEC_TMPVARCV_TMPVARCV_JMPNZ) \ + _(3383, ZEND_IS_SMALLER_OR_EQUAL_DOUBLE_SPEC_TMPVARCV_TMPVARCV) \ + _(3384, ZEND_IS_SMALLER_OR_EQUAL_DOUBLE_SPEC_TMPVARCV_TMPVARCV_JMPZ) \ + _(3385, ZEND_IS_SMALLER_OR_EQUAL_DOUBLE_SPEC_TMPVARCV_TMPVARCV_JMPNZ) \ + _(3389, ZEND_IS_SMALLER_OR_EQUAL_DOUBLE_SPEC_TMPVARCV_TMPVARCV) \ + _(3390, ZEND_IS_SMALLER_OR_EQUAL_DOUBLE_SPEC_TMPVARCV_TMPVARCV_JMPZ) \ + _(3391, ZEND_IS_SMALLER_OR_EQUAL_DOUBLE_SPEC_TMPVARCV_TMPVARCV_JMPNZ) \ + _(3392, ZEND_IS_SMALLER_OR_EQUAL_DOUBLE_SPEC_TMPVARCV_CONST) \ + _(3393, ZEND_IS_SMALLER_OR_EQUAL_DOUBLE_SPEC_TMPVARCV_CONST_JMPZ) \ + _(3394, ZEND_IS_SMALLER_OR_EQUAL_DOUBLE_SPEC_TMPVARCV_CONST_JMPNZ) \ + _(3395, ZEND_IS_SMALLER_OR_EQUAL_DOUBLE_SPEC_TMPVARCV_TMPVARCV) \ + _(3396, ZEND_IS_SMALLER_OR_EQUAL_DOUBLE_SPEC_TMPVARCV_TMPVARCV_JMPZ) \ + _(3397, ZEND_IS_SMALLER_OR_EQUAL_DOUBLE_SPEC_TMPVARCV_TMPVARCV_JMPNZ) \ + _(3398, ZEND_IS_SMALLER_OR_EQUAL_DOUBLE_SPEC_TMPVARCV_TMPVARCV) \ + _(3399, ZEND_IS_SMALLER_OR_EQUAL_DOUBLE_SPEC_TMPVARCV_TMPVARCV_JMPZ) \ + _(3400, ZEND_IS_SMALLER_OR_EQUAL_DOUBLE_SPEC_TMPVARCV_TMPVARCV_JMPNZ) \ + _(3404, ZEND_IS_SMALLER_OR_EQUAL_DOUBLE_SPEC_TMPVARCV_TMPVARCV) \ + _(3405, ZEND_IS_SMALLER_OR_EQUAL_DOUBLE_SPEC_TMPVARCV_TMPVARCV_JMPZ) \ + _(3406, ZEND_IS_SMALLER_OR_EQUAL_DOUBLE_SPEC_TMPVARCV_TMPVARCV_JMPNZ) \ + _(3422, ZEND_IS_SMALLER_OR_EQUAL_DOUBLE_SPEC_TMPVARCV_CONST) \ + _(3423, ZEND_IS_SMALLER_OR_EQUAL_DOUBLE_SPEC_TMPVARCV_CONST_JMPZ) \ + _(3424, ZEND_IS_SMALLER_OR_EQUAL_DOUBLE_SPEC_TMPVARCV_CONST_JMPNZ) \ + _(3425, ZEND_IS_SMALLER_OR_EQUAL_DOUBLE_SPEC_TMPVARCV_TMPVARCV) \ + _(3426, ZEND_IS_SMALLER_OR_EQUAL_DOUBLE_SPEC_TMPVARCV_TMPVARCV_JMPZ) \ + _(3427, ZEND_IS_SMALLER_OR_EQUAL_DOUBLE_SPEC_TMPVARCV_TMPVARCV_JMPNZ) \ + _(3428, ZEND_IS_SMALLER_OR_EQUAL_DOUBLE_SPEC_TMPVARCV_TMPVARCV) \ + _(3429, ZEND_IS_SMALLER_OR_EQUAL_DOUBLE_SPEC_TMPVARCV_TMPVARCV_JMPZ) \ + _(3430, ZEND_IS_SMALLER_OR_EQUAL_DOUBLE_SPEC_TMPVARCV_TMPVARCV_JMPNZ) \ + _(3434, ZEND_IS_SMALLER_OR_EQUAL_DOUBLE_SPEC_TMPVARCV_TMPVARCV) \ + _(3435, ZEND_IS_SMALLER_OR_EQUAL_DOUBLE_SPEC_TMPVARCV_TMPVARCV_JMPZ) \ + _(3436, ZEND_IS_SMALLER_OR_EQUAL_DOUBLE_SPEC_TMPVARCV_TMPVARCV_JMPNZ) \ + _(3437, ZEND_PRE_INC_LONG_NO_OVERFLOW_SPEC_CV_RETVAL_UNUSED) \ + _(3438, ZEND_PRE_INC_LONG_NO_OVERFLOW_SPEC_CV_RETVAL_USED) \ + _(3439, ZEND_PRE_INC_LONG_SPEC_CV_RETVAL_UNUSED) \ + _(3440, ZEND_PRE_INC_LONG_SPEC_CV_RETVAL_USED) \ + _(3441, ZEND_PRE_DEC_LONG_NO_OVERFLOW_SPEC_CV_RETVAL_UNUSED) \ + _(3442, ZEND_PRE_DEC_LONG_NO_OVERFLOW_SPEC_CV_RETVAL_USED) \ + _(3443, ZEND_PRE_DEC_LONG_SPEC_CV_RETVAL_UNUSED) \ + _(3444, ZEND_PRE_DEC_LONG_SPEC_CV_RETVAL_USED) \ + _(3445, ZEND_POST_INC_LONG_NO_OVERFLOW_SPEC_CV) \ + _(3446, ZEND_POST_INC_LONG_SPEC_CV) \ + _(3447, ZEND_POST_DEC_LONG_NO_OVERFLOW_SPEC_CV) \ + _(3448, ZEND_POST_DEC_LONG_SPEC_CV) \ + _(3449, ZEND_QM_ASSIGN_LONG_SPEC_CONST) \ + _(3450, ZEND_QM_ASSIGN_LONG_SPEC_TMPVARCV) \ + _(3451, ZEND_QM_ASSIGN_LONG_SPEC_TMPVARCV) \ + _(3453, ZEND_QM_ASSIGN_LONG_SPEC_TMPVARCV) \ + _(3454, ZEND_QM_ASSIGN_DOUBLE_SPEC_CONST) \ + _(3455, ZEND_QM_ASSIGN_DOUBLE_SPEC_TMPVARCV) \ + _(3456, ZEND_QM_ASSIGN_DOUBLE_SPEC_TMPVARCV) \ + _(3458, ZEND_QM_ASSIGN_DOUBLE_SPEC_TMPVARCV) \ + _(3459, ZEND_QM_ASSIGN_NOREF_SPEC_CONST) \ + _(3460, ZEND_QM_ASSIGN_NOREF_SPEC_TMPVARCV) \ + _(3461, ZEND_QM_ASSIGN_NOREF_SPEC_TMPVARCV) \ + _(3463, ZEND_QM_ASSIGN_NOREF_SPEC_TMPVARCV) \ + _(3465, ZEND_FETCH_DIM_R_INDEX_SPEC_CONST_TMPVARCV) \ + _(3466, ZEND_FETCH_DIM_R_INDEX_SPEC_CONST_TMPVARCV) \ + _(3468, ZEND_FETCH_DIM_R_INDEX_SPEC_CONST_TMPVARCV) \ + _(3469, ZEND_FETCH_DIM_R_INDEX_SPEC_TMPVAR_CONST) \ + _(3470, ZEND_FETCH_DIM_R_INDEX_SPEC_TMPVAR_TMPVARCV) \ + _(3471, ZEND_FETCH_DIM_R_INDEX_SPEC_TMPVAR_TMPVARCV) \ + _(3473, ZEND_FETCH_DIM_R_INDEX_SPEC_TMPVAR_TMPVARCV) \ + _(3474, ZEND_FETCH_DIM_R_INDEX_SPEC_TMPVAR_CONST) \ + _(3475, ZEND_FETCH_DIM_R_INDEX_SPEC_TMPVAR_TMPVARCV) \ + _(3476, ZEND_FETCH_DIM_R_INDEX_SPEC_TMPVAR_TMPVARCV) \ + _(3478, ZEND_FETCH_DIM_R_INDEX_SPEC_TMPVAR_TMPVARCV) \ + _(3484, ZEND_FETCH_DIM_R_INDEX_SPEC_CV_CONST) \ + _(3485, ZEND_FETCH_DIM_R_INDEX_SPEC_CV_TMPVARCV) \ + _(3486, ZEND_FETCH_DIM_R_INDEX_SPEC_CV_TMPVARCV) \ + _(3488, ZEND_FETCH_DIM_R_INDEX_SPEC_CV_TMPVARCV) \ + _(3491, ZEND_SEND_VAR_SIMPLE_SPEC_VAR) \ + _(3493, ZEND_SEND_VAR_SIMPLE_SPEC_CV) \ + _(3496, ZEND_SEND_VAR_EX_SIMPLE_SPEC_VAR_UNUSED) \ + _(3498, ZEND_SEND_VAR_EX_SIMPLE_SPEC_CV_UNUSED) \ + _(3499, ZEND_SEND_VAL_SIMPLE_SPEC_CONST) \ + _(3500, ZEND_SEND_VAL_EX_SIMPLE_SPEC_CONST) \ + _(3501, ZEND_FE_FETCH_R_SIMPLE_SPEC_VAR_CV_RETVAL_UNUSED) \ + _(3502, ZEND_FE_FETCH_R_SIMPLE_SPEC_VAR_CV_RETVAL_USED) \ + _(3502+1, ZEND_NULL) diff --git a/Zend/zend_vm_opcodes.c b/Zend/zend_vm_opcodes.c index 936a96e55e41f..c6ecc0a4dd069 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[213] = { "ZEND_NOP", "ZEND_ADD", "ZEND_SUB", @@ -234,9 +234,11 @@ static const char *zend_vm_opcodes_names[211] = { "ZEND_JMP_FRAMELESS", "ZEND_INIT_PARENT_PROPERTY_HOOK_CALL", "ZEND_DECLARE_ATTRIBUTED_CONST", + "ZEND_SEND_PLACEHOLDER", + "ZEND_CALLABLE_CONVERT_PARTIAL", }; -static uint32_t zend_vm_opcodes_flags[211] = { +static uint32_t zend_vm_opcodes_flags[213] = { 0x00000000, 0x00000b0b, 0x00000b0b, @@ -448,6 +450,8 @@ static uint32_t zend_vm_opcodes_flags[211] = { 0x01042003, 0x01001103, 0x00000303, + 0x00000301, + 0x010003a0, }; 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 287dec375f3a9..389ed31153b26 100644 --- a/Zend/zend_vm_opcodes.h +++ b/Zend/zend_vm_opcodes.h @@ -330,7 +330,9 @@ END_EXTERN_C() #define ZEND_JMP_FRAMELESS 208 #define ZEND_INIT_PARENT_PROPERTY_HOOK_CALL 209 #define ZEND_DECLARE_ATTRIBUTED_CONST 210 +#define ZEND_SEND_PLACEHOLDER 211 +#define ZEND_CALLABLE_CONVERT_PARTIAL 212 -#define ZEND_VM_LAST_OPCODE 210 +#define ZEND_VM_LAST_OPCODE 212 #endif diff --git a/configure.ac b/configure.ac index 77fc8c89cdf40..40bd29ae99a78 100644 --- a/configure.ac +++ b/configure.ac @@ -1779,6 +1779,7 @@ PHP_ADD_SOURCES([Zend], m4_normalize([ zend_observer.c zend_opcode.c zend_operators.c + zend_partial.c zend_property_hooks.c zend_ptr_stack.c zend_signal.c diff --git a/ext/opcache/ZendAccelerator.c b/ext/opcache/ZendAccelerator.c index 7acb14b778f8d..b7842fc23db0e 100644 --- a/ext/opcache/ZendAccelerator.c +++ b/ext/opcache/ZendAccelerator.c @@ -26,7 +26,9 @@ #include "zend_compile.h" #include "ZendAccelerator.h" #include "zend_modules.h" +#include "zend_operators.h" #include "zend_persist.h" +#include "zend_portability.h" #include "zend_shared_alloc.h" #include "zend_accelerator_module.h" #include "zend_accelerator_blacklist.h" @@ -50,6 +52,7 @@ #include "zend_system_id.h" #include "ext/pcre/php_pcre.h" #include "ext/standard/basic_functions.h" +#include "zend_vm_opcodes.h" #ifdef ZEND_WIN32 # include "ext/hash/php_hash.h" @@ -2014,6 +2017,219 @@ static int check_persistent_script_access(zend_persistent_script *persistent_scr } } +static const char hexchars[] = "0123456789abcdef"; + +static char *zend_accel_uintptr_hex(char *dest, uintptr_t n) +{ + char *start = dest; + dest += sizeof(uintptr_t)*2; + + while (n > 0) { + *--dest = hexchars[n % strlen(hexchars)]; + n /= strlen(hexchars); + } + while (dest > start) { + *--dest = '0'; + } + + return dest + sizeof(uintptr_t)*2; +} + +/* Prevents collisions with real scripts, as we don't cache paths prefixed with + * a scheme, except file:// and phar://. */ +#define PFA_KEY_PREFIX "pfa://" + +static zend_string *zend_accel_pfa_key(const zend_op *declaring_opline, + const zend_function *called_function) +{ + size_t key_len = strlen(PFA_KEY_PREFIX) + (sizeof(uintptr_t)*2) + strlen(":") + (sizeof(uintptr_t)*2); + zend_string *key = zend_string_alloc(key_len, 0); + char *dest = ZSTR_VAL(key); + + dest = zend_mempcpy(ZSTR_VAL(key), PFA_KEY_PREFIX, strlen(PFA_KEY_PREFIX)); + dest = zend_accel_uintptr_hex(dest, (uintptr_t)declaring_opline); + *dest++ = ':'; + + void *ptr; + if ((called_function->common.fn_flags & ZEND_ACC_CLOSURE) + && called_function->type == ZEND_USER_FUNCTION) { + /* Can not use 'called_function' as part of the key, as it's an inner + * pointer to a Closure, which may be freed. Use its opcodes instead. + * zend_accel_compile_pfa() ensures to extend the lifetime of opcodes + * in this case. */ + ptr = called_function->op_array.opcodes; + } else { + ptr = (void*) called_function; + } + dest = zend_accel_uintptr_hex(dest, (uintptr_t)ptr); + + ZEND_ASSERT(dest == ZSTR_VAL(key) + key_len); + + ZSTR_VAL(key)[key_len] = 0; + ZSTR_LEN(key) = key_len; + + return key; +} + +zend_op_array *zend_accel_pfa_cache_get(const zend_op_array *declaring_op_array, + const zend_op *declaring_opline, const zend_function *called_function) +{ + zend_string *key = zend_accel_pfa_key(declaring_opline, called_function); + zend_op_array *op_array = NULL; + + /* A PFA is SHM-cacheable if the declaring_op_array and called_function are + * cached. */ + if (ZCG(accelerator_enabled) + && !file_cache_only + && !declaring_op_array->refcount + && (called_function->type != ZEND_USER_FUNCTION || !called_function->op_array.refcount)) { + zend_persistent_script *persistent_script = zend_accel_hash_find(&ZCSG(hash), key); + if (persistent_script) { + op_array = persistent_script->script.main_op_array.dynamic_func_defs[0]; + if (persistent_script->num_warnings) { + zend_emit_recorded_errors_ex(persistent_script->num_warnings, + persistent_script->warnings); + } + } + } else { + op_array = zend_hash_find_ptr(&EG(partial_function_application_cache), key); + } + + zend_string_release(key); + + return op_array; +} + +zend_op_array *zend_accel_compile_pfa(zend_ast *ast, + const zend_op_array *declaring_op_array, + const zend_op *declaring_opline, + const zend_function *called_function, + zend_string *pfa_func_name) +{ + zend_begin_record_errors(); + zend_op_array *op_array; + + uint32_t orig_compiler_options = CG(compiler_options); + + zend_try { + CG(compiler_options) |= ZEND_COMPILE_HANDLE_OP_ARRAY; + CG(compiler_options) |= ZEND_COMPILE_DELAYED_BINDING; + CG(compiler_options) |= ZEND_COMPILE_NO_CONSTANT_SUBSTITUTION; + CG(compiler_options) |= ZEND_COMPILE_IGNORE_OTHER_FILES; + CG(compiler_options) |= ZEND_COMPILE_IGNORE_OBSERVER; +#ifdef ZEND_WIN32 + /* On Windows, don't compile with internal classes. Shm may be attached from different + * processes with internal classes living in different addresses. */ + CG(compiler_options) |= ZEND_COMPILE_IGNORE_INTERNAL_CLASSES; +#endif + + op_array = zend_compile_ast(ast, ZEND_USER_FUNCTION, declaring_op_array->filename); + + ZEND_ASSERT(op_array->num_dynamic_func_defs == 1); + + CG(compiler_options) = orig_compiler_options; + } zend_catch { + op_array = NULL; + CG(compiler_options) = orig_compiler_options; + EG(record_errors) = false; + zend_free_recorded_errors(); + zend_bailout(); + } zend_end_try(); + + if (!op_array) { + zend_emit_recorded_errors(); + zend_free_recorded_errors(); + return NULL; + } + + ZEND_ASSERT(op_array->num_dynamic_func_defs == 1); + + zend_string_release(op_array->dynamic_func_defs[0]->function_name); + op_array->dynamic_func_defs[0]->function_name = pfa_func_name; + + zend_string *key = zend_accel_pfa_key(declaring_opline, called_function); + + /* Cache op_array only if the declaring op_array and the called function + * are cached */ + if (!ZCG(accelerator_enabled) + || file_cache_only + || declaring_op_array->refcount + || (called_function->type == ZEND_USER_FUNCTION && called_function->op_array.refcount) + || (ZCSG(restart_in_progress) && accel_restart_is_active()) + || (!ZCG(counted) && accel_activate_add() == FAILURE)) { + zend_op_array *script_op_array = op_array; + zend_op_array *op_array = script_op_array->dynamic_func_defs[0]; + GC_ADDREF(op_array->function_name); + (*op_array->refcount)++; + destroy_op_array(script_op_array); + efree(script_op_array); + + if ((called_function->common.fn_flags & ZEND_ACC_CLOSURE) + && called_function->type == ZEND_USER_FUNCTION + && called_function->op_array.refcount) { + /* Extend the lifetime of the called opcodes if + * the called function is a closure. + * See comment in zend_accel_pfa_key(). */ + zend_op_array *copy = zend_arena_alloc(&CG(arena), sizeof(zend_function)); + memcpy(copy, called_function, sizeof(zend_op_array)); + zend_string_addref(copy->function_name); + (*copy->refcount)++; + /* Reference the copy in op_array->dynamic_func_defs so that it's + * destroyed when op_array is destroy. */ + ZEND_ASSERT(!op_array->dynamic_func_defs && !op_array->num_dynamic_func_defs); + op_array->dynamic_func_defs = emalloc(sizeof(zend_op_array*)); + op_array->dynamic_func_defs[0] = copy; + op_array->num_dynamic_func_defs = 1; + } + + zend_hash_add_new_ptr(&EG(partial_function_application_cache), key, op_array); + zend_string_release(key); + + zend_emit_recorded_errors(); + zend_free_recorded_errors(); + + return op_array; + } + + zend_persistent_script *new_persistent_script = create_persistent_script(); + new_persistent_script->script.main_op_array = *op_array; + efree_size(op_array, sizeof(zend_op_array)); + new_persistent_script->script.filename = key; + + if (ZCG(accel_directives).record_warnings) { + new_persistent_script->num_warnings = EG(num_errors); + new_persistent_script->warnings = EG(errors); + } + + HANDLE_BLOCK_INTERRUPTIONS(); + SHM_UNPROTECT(); + + bool from_shared_memory; + /* See GH-17246: we disable GC so that user code cannot be executed during the optimizer run. */ + bool orig_gc_state = gc_enable(false); + char *orig_file_cache = ZCG(accel_directives).file_cache; + /* Disable file_cache temporarily, as we can't guarantee consistency. */ + ZCG(accel_directives).file_cache = false; + new_persistent_script = cache_script_in_shared_memory(new_persistent_script, NULL, &from_shared_memory); + ZCG(accel_directives).file_cache = orig_file_cache; + gc_enable(orig_gc_state); + + SHM_PROTECT(); + HANDLE_UNBLOCK_INTERRUPTIONS(); + + /* We may have switched to an existing persistent script that was persisted in + * the meantime. Make sure to use its warnings if available. */ + if (ZCG(accel_directives).record_warnings) { + EG(record_errors) = false; + zend_emit_recorded_errors_ex(new_persistent_script->num_warnings, new_persistent_script->warnings); + } else { + zend_emit_recorded_errors(); + } + zend_free_recorded_errors(); + + return new_persistent_script->script.main_op_array.dynamic_func_defs[0]; +} + /* zend_compile() replacement */ zend_op_array *persistent_compile_file(zend_file_handle *file_handle, int type) { diff --git a/ext/opcache/ZendAccelerator.h b/ext/opcache/ZendAccelerator.h index 524a6f5e12139..7f14da4493d97 100644 --- a/ext/opcache/ZendAccelerator.h +++ b/ext/opcache/ZendAccelerator.h @@ -336,6 +336,16 @@ zend_string* ZEND_FASTCALL accel_new_interned_string(zend_string *str); uint32_t zend_accel_get_class_name_map_ptr(zend_string *type_name); +zend_op_array *zend_accel_pfa_cache_get(const zend_op_array *declaring_op_array, + const zend_op *declaring_opline, + const zend_function *called_function); + +zend_op_array *zend_accel_compile_pfa(zend_ast *ast, + const zend_op_array *declaring_op_array, + const zend_op *declaring_opline, + const zend_function *called_function, + zend_string *pfa_func_name); + END_EXTERN_C() /* memory write protection */ diff --git a/ext/opcache/jit/zend_jit.c b/ext/opcache/jit/zend_jit.c index 3ffb669e84742..42236f0af90fe 100644 --- a/ext/opcache/jit/zend_jit.c +++ b/ext/opcache/jit/zend_jit.c @@ -303,6 +303,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: @@ -388,6 +389,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 */ @@ -867,6 +869,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 1c5cab899e783..0bcf972447a93 100644 --- a/ext/opcache/jit/zend_jit_ir.c +++ b/ext/opcache/jit/zend_jit_ir.c @@ -10078,7 +10078,7 @@ static int zend_jit_do_fcall(zend_jit_ctx *jit, const zend_op *opline, const zen } bool may_have_extra_named_params = - opline->extended_value == ZEND_FCALL_MAY_HAVE_EXTRA_NAMED_PARAMS && + (opline->extended_value & ZEND_FCALL_MAY_HAVE_EXTRA_NAMED_PARAMS) && (!func || func->common.fn_flags & ZEND_ACC_VARIADIC); if (!jit->reuse_ip) { diff --git a/ext/opcache/jit/zend_jit_vm_helpers.c b/ext/opcache/jit/zend_jit_vm_helpers.c index bed5ab59992ed..da1ac594e5e95 100644 --- a/ext/opcache/jit/zend_jit_vm_helpers.c +++ b/ext/opcache/jit/zend_jit_vm_helpers.c @@ -1059,7 +1059,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; diff --git a/win32/build/config.w32 b/win32/build/config.w32 index 403f0aa6efbfe..1efdc57bca4da 100644 --- a/win32/build/config.w32 +++ b/win32/build/config.w32 @@ -241,7 +241,7 @@ ADD_SOURCES("Zend", "zend_language_parser.c zend_language_scanner.c \ zend_float.c zend_string.c zend_generators.c zend_virtual_cwd.c zend_ast.c \ zend_inheritance.c zend_smart_str.c zend_cpuinfo.c zend_observer.c zend_system_id.c \ zend_enum.c zend_fibers.c zend_atomic.c zend_hrtime.c zend_frameless_function.c zend_property_hooks.c \ - zend_lazy_objects.c"); + zend_lazy_objects.c zend_partial.c"); ADD_SOURCES("Zend\\Optimizer", "zend_optimizer.c pass1.c pass3.c optimize_func_calls.c block_pass.c optimize_temp_vars_5.c nop_removal.c compact_literals.c zend_cfg.c zend_dfg.c dfa_pass.c zend_ssa.c zend_inference.c zend_func_info.c zend_call_graph.c zend_dump.c escape_analysis.c compact_vars.c dce.c sccp.c scdf.c"); var PHP_ASSEMBLER = PATH_PROG({ From 0849041fcfa53dd9b5c1369133adedae62a29030 Mon Sep 17 00:00:00 2001 From: Arnaud Le Blanc Date: Tue, 6 Jan 2026 17:33:40 +0100 Subject: [PATCH 02/15] Remove unnecessary change --- Zend/Optimizer/zend_call_graph.c | 4 ---- 1 file changed, 4 deletions(-) diff --git a/Zend/Optimizer/zend_call_graph.c b/Zend/Optimizer/zend_call_graph.c index 4e414a95172fe..845b3d5813ee0 100644 --- a/Zend/Optimizer/zend_call_graph.c +++ b/Zend/Optimizer/zend_call_graph.c @@ -151,10 +151,6 @@ ZEND_API void zend_analyze_calls(zend_arena **arena, zend_script *script, uint32 call_info->named_args = true; break; } - if (opline->opcode == ZEND_SEND_PLACEHOLDER - && opline->op1.num == ZEND_PLACEHOLDER_VARIADIC) { - break; - } uint32_t num = opline->op2.num; if (num > 0) { From d14ca74c7f572ff6feed7c2156a21be4841879b1 Mon Sep 17 00:00:00 2001 From: Arnaud Le Blanc Date: Tue, 6 Jan 2026 17:33:54 +0100 Subject: [PATCH 03/15] Skip preload test on Windows --- Zend/tests/partial_application/preloading.phpt | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/Zend/tests/partial_application/preloading.phpt b/Zend/tests/partial_application/preloading.phpt index 5e3069c271a6d..23ad6edf2c4cb 100644 --- a/Zend/tests/partial_application/preloading.phpt +++ b/Zend/tests/partial_application/preloading.phpt @@ -4,6 +4,10 @@ PFA preloading opcache.enable=1 opcache.enable_cli=1 opcache.preload={PWD}/preloading.inc +--SKIPIF-- + --FILE-- Date: Tue, 6 Jan 2026 17:34:03 +0100 Subject: [PATCH 04/15] Comments --- Zend/zend_closures.c | 1 + Zend/zend_partial.c | 6 ++++-- ext/opcache/ZendAccelerator.c | 2 +- 3 files changed, 6 insertions(+), 3 deletions(-) diff --git a/Zend/zend_closures.c b/Zend/zend_closures.c index 128dccac0fa17..69174170b9ec2 100644 --- a/Zend/zend_closures.c +++ b/Zend/zend_closures.c @@ -87,6 +87,7 @@ static bool zend_valid_closure_binding( zend_closure *closure, zval *newthis, zend_class_entry *scope) /* {{{ */ { zend_function *func = &closure->func; + // TODO: rename variable bool is_fake_closure = (func->common.fn_flags & ZEND_ACC_FAKE_CLOSURE) != 0 || (closure->std.extra_flags & ZEND_PARTIAL); if (newthis) { diff --git a/Zend/zend_partial.c b/Zend/zend_partial.c index 2a4195c4049bc..aa7511153a16c 100644 --- a/Zend/zend_partial.c +++ b/Zend/zend_partial.c @@ -35,7 +35,7 @@ * create a Closure and return it, consuming the stack frame in the process * like an internal function call. * - * We create the Closure by generating the relevant AST and compling it to an + * We create the Closure by generating the relevant AST and compiling it to an * op_array. The op_array is cached in the Opcache SHM and inline caches. * * This file implements the Closure generation logic @@ -548,6 +548,7 @@ static zend_ast *zp_compile_forwarding_call( } zend_ast *default_value_ast; if (Z_TYPE(default_value) == IS_CONSTANT_AST) { + /* Must dup AST because we are doing to destroy it */ default_value_ast = zend_ast_dup(Z_ASTVAL(default_value)); } else { default_value_ast = zend_ast_create_zval(&default_value); @@ -702,6 +703,7 @@ static zend_op_array *zp_compile(zval *this_ptr, zend_function *function, called_scope = Z_CE_P(this_ptr); } + /* CG(ast_arena) is usually NULL, so we can't just make a snapshot */ zend_arena *orig_ast_arena = CG(ast_arena); CG(ast_arena) = zend_arena_create(1024 * 4); @@ -718,7 +720,7 @@ static zend_op_array *zp_compile(zval *this_ptr, zend_function *function, memcpy(tmp, argv, argc * sizeof(zval)); argv = tmp; - /* Compute number of required args and param positions, add implicit + /* Compute param positions and number of required args, add implicit * placeholders. * * Parameters are placed in the following order: diff --git a/ext/opcache/ZendAccelerator.c b/ext/opcache/ZendAccelerator.c index b7842fc23db0e..762ae1722ccef 100644 --- a/ext/opcache/ZendAccelerator.c +++ b/ext/opcache/ZendAccelerator.c @@ -2175,7 +2175,7 @@ zend_op_array *zend_accel_compile_pfa(zend_ast *ast, zend_string_addref(copy->function_name); (*copy->refcount)++; /* Reference the copy in op_array->dynamic_func_defs so that it's - * destroyed when op_array is destroy. */ + * destroyed when op_array is destroyed. */ ZEND_ASSERT(!op_array->dynamic_func_defs && !op_array->num_dynamic_func_defs); op_array->dynamic_func_defs = emalloc(sizeof(zend_op_array*)); op_array->dynamic_func_defs[0] = copy; From 98e64d59d11d5fbd71ff50050c2d35d75fbbb8e8 Mon Sep 17 00:00:00 2001 From: Arnaud Le Blanc Date: Tue, 6 Jan 2026 17:55:56 +0100 Subject: [PATCH 05/15] Improve type coherency --- Zend/zend_partial.c | 22 +++++++++++----------- 1 file changed, 11 insertions(+), 11 deletions(-) diff --git a/Zend/zend_partial.c b/Zend/zend_partial.c index aa7511153a16c..7fbdccb69b905 100644 --- a/Zend/zend_partial.c +++ b/Zend/zend_partial.c @@ -166,11 +166,11 @@ static void zp_assign_names(zend_string **names, uint32_t num_names, if (!Z_IS_PLACEHOLDER_P(&argv[offset])) { continue; } - int n = offset - function->common.num_args; + uint32_t n = offset - function->common.num_args; zend_string *orig_name = zp_get_param_name(function, function->common.num_args); zend_string *new_name; do { - new_name = zend_strpprintf_unchecked(0, "%S%d", orig_name, n); + new_name = zend_strpprintf_unchecked(0, "%S%" PRIu32, orig_name, n); if (!zp_name_exists(names, num_names, new_name)) { break; } @@ -187,13 +187,13 @@ static void zp_assign_names(zend_string **names, uint32_t num_names, if (Z_IS_PLACEHOLDER_P(&argv[offset]) || Z_ISUNDEF(argv[offset])) { continue; } - int n = -1; + uint32_t n = 0; zend_string *orig_name = zp_get_param_name(function, MIN(offset, function->common.num_args)); zend_string *new_name = zend_string_copy(orig_name); while (zp_name_exists(names, num_names, new_name)) { zend_string_release(new_name); + new_name = zend_strpprintf_unchecked(0, "%S%" PRIu32, orig_name, n); n++; - new_name = zend_strpprintf_unchecked(0, "%S%d", orig_name, n); } names[offset] = new_name; zend_string_release(orig_name); @@ -201,24 +201,24 @@ static void zp_assign_names(zend_string **names, uint32_t num_names, /* Assign name for $extra_named_params */ if (extra_named_params) { - int n = 1; + uint32_t n = 1; zend_string *new_name = ZSTR_INIT_LITERAL("extra_named_params", 0); while (zp_name_exists(names, num_names, new_name)) { zend_string_release(new_name); n++; - new_name = zend_strpprintf(0, "%s%d", "extra_named_params", n); + new_name = zend_strpprintf(0, "%s%" PRIu32, "extra_named_params", n); } names[argc + variadic_partial] = new_name; } /* Assign name for $fn */ if (function->common.fn_flags & ZEND_ACC_CLOSURE) { - int n = 1; + uint32_t n = 1; zend_string *new_name = ZSTR_INIT_LITERAL("fn", 0); while (zp_name_exists(names, num_names, new_name)) { zend_string_release(new_name); n++; - new_name = zend_strpprintf(0, "%s%d", "fn", n); + new_name = zend_strpprintf(0, "%s%" PRIu32, "fn", n); } names[argc + variadic_partial + (extra_named_params != NULL)] = new_name; } @@ -666,7 +666,7 @@ static const zend_known_string_id zp_non_dynamic_call_funcs[] = { static bool zp_is_non_dynamic_call_func(zend_function *function) { - for (int i = 0; i < sizeof(zp_non_dynamic_call_funcs) / sizeof(zp_non_dynamic_call_funcs[0]); i++) { + for (size_t i = 0; i < sizeof(zp_non_dynamic_call_funcs) / sizeof(zp_non_dynamic_call_funcs[0]); i++) { if (zend_string_equals(function->common.function_name, ZSTR_KNOWN(zp_non_dynamic_call_funcs[i]))) { return true; } @@ -707,10 +707,10 @@ static zend_op_array *zp_compile(zval *this_ptr, zend_function *function, zend_arena *orig_ast_arena = CG(ast_arena); CG(ast_arena) = zend_arena_create(1024 * 4); - int orig_lineno = CG(zend_lineno); + uint32_t orig_lineno = CG(zend_lineno); CG(zend_lineno) = zend_get_executed_lineno(); - int new_argc = argc; + uint32_t new_argc = argc; if (uses_variadic_placeholder) { new_argc = MAX(new_argc, function->common.num_args); From 6819628b5fc35e391a38712e09f2983404b3f5ed Mon Sep 17 00:00:00 2001 From: Arnaud Le Blanc Date: Tue, 6 Jan 2026 18:17:48 +0100 Subject: [PATCH 06/15] Clarify --- Zend/zend_partial.c | 30 +++++++++++++++++++----------- 1 file changed, 19 insertions(+), 11 deletions(-) diff --git a/Zend/zend_partial.c b/Zend/zend_partial.c index 7fbdccb69b905..4c07aa6ef5d33 100644 --- a/Zend/zend_partial.c +++ b/Zend/zend_partial.c @@ -224,14 +224,21 @@ static void zp_assign_names(zend_string **names, uint32_t num_names, } } -static bool zp_is_single_may_be_type(uint32_t type_mask) +static bool zp_is_power_of_two(uint32_t x) { - return ((type_mask > 0) && (type_mask & (type_mask - 1)) == 0) + return (x > 0) && !(x & (x - 1)); +} + +static bool zp_is_simple_type(uint32_t type_mask) +{ + ZEND_ASSERT(!(type_mask & ~_ZEND_TYPE_MAY_BE_MASK)); + + return zp_is_power_of_two(type_mask) || type_mask == MAY_BE_BOOL || type_mask == MAY_BE_ANY; } -static zend_ast *zp_single_may_be_type_to_ast(uint32_t type) +static zend_ast *zp_simple_type_to_ast(uint32_t type) { zend_string *name; @@ -305,7 +312,7 @@ static zend_ast *zp_type_to_ast(const zend_type type) if (ZEND_TYPE_IS_UNION(type) || (ZEND_TYPE_IS_COMPLEX(type) && ZEND_TYPE_PURE_MASK(type)) - || (ZEND_TYPE_PURE_MASK(type) && !zp_is_single_may_be_type(ZEND_TYPE_PURE_MASK(type)))) { + || (ZEND_TYPE_PURE_MASK(type) && !zp_is_simple_type(ZEND_TYPE_PURE_MASK(type)))) { zend_ast *type_ast = zend_ast_create_list(0, ZEND_AST_TYPE_UNION); if (ZEND_TYPE_HAS_LIST(type)) { const zend_type *type_ptr; @@ -316,33 +323,34 @@ static zend_ast *zp_type_to_ast(const zend_type type) zend_ast *name_ast = zp_type_name_to_ast( zend_string_copy(ZEND_TYPE_NAME(type))); type_ast = zend_ast_list_add(type_ast, name_ast); - } else if (ZEND_TYPE_IS_COMPLEX(type)) { - ZEND_UNREACHABLE(); + } else { + ZEND_ASSERT(!ZEND_TYPE_IS_COMPLEX(type)); } uint32_t type_mask = ZEND_TYPE_PURE_MASK(type); if ((type_mask & MAY_BE_BOOL) == MAY_BE_BOOL) { - type_ast = zend_ast_list_add(type_ast, zp_single_may_be_type_to_ast(MAY_BE_BOOL)); + type_ast = zend_ast_list_add(type_ast, zp_simple_type_to_ast(MAY_BE_BOOL)); type_mask &= ~MAY_BE_BOOL; } for (uint32_t may_be_type = 1; may_be_type < _ZEND_TYPE_MAY_BE_MASK; may_be_type <<= 1) { if (type_mask & may_be_type) { - type_ast = zend_ast_list_add(type_ast, zp_single_may_be_type_to_ast(may_be_type)); + type_ast = zend_ast_list_add(type_ast, zp_simple_type_to_ast(may_be_type)); } } return type_ast; } if (ZEND_TYPE_IS_INTERSECTION(type)) { + ZEND_ASSERT(!ZEND_TYPE_PURE_MASK(type)); zend_ast *type_ast = zend_ast_create_list(0, ZEND_AST_TYPE_INTERSECTION); const zend_type *type_ptr; ZEND_TYPE_LIST_FOREACH(ZEND_TYPE_LIST(type), type_ptr) { type_ast = zend_ast_list_add(type_ast, zp_type_to_ast(*type_ptr)); } ZEND_TYPE_LIST_FOREACH_END(); - ZEND_ASSERT(!ZEND_TYPE_PURE_MASK(type)); return type_ast; } if (ZEND_TYPE_HAS_NAME(type)) { + ZEND_ASSERT(!ZEND_TYPE_PURE_MASK(type)); zend_ast *type_ast = zp_type_name_to_ast( zend_string_copy(ZEND_TYPE_NAME(type))); return type_ast; @@ -351,9 +359,9 @@ static zend_ast *zp_type_to_ast(const zend_type type) ZEND_ASSERT(!ZEND_TYPE_IS_COMPLEX(type)); uint32_t type_mask = ZEND_TYPE_PURE_MASK(type); - ZEND_ASSERT(zp_is_single_may_be_type(type_mask)); + ZEND_ASSERT(zp_is_simple_type(type_mask)); - return zp_single_may_be_type_to_ast(type_mask); + return zp_simple_type_to_ast(type_mask); } /* Can not use zend_argument_error() as the function is not on the stack */ From 0326e5d7db0d93e40eb7dc3285a3023ad9b505d6 Mon Sep 17 00:00:00 2001 From: Arnaud Le Blanc Date: Tue, 6 Jan 2026 18:36:37 +0100 Subject: [PATCH 07/15] Useless memset --- Zend/zend_partial.c | 1 - 1 file changed, 1 deletion(-) diff --git a/Zend/zend_partial.c b/Zend/zend_partial.c index 4c07aa6ef5d33..04a34e8a74228 100644 --- a/Zend/zend_partial.c +++ b/Zend/zend_partial.c @@ -798,7 +798,6 @@ static zend_op_array *zp_compile(zval *this_ptr, zend_function *function, + ((function->common.fn_flags & ZEND_ACC_CLOSURE) != 0); zend_string **param_names = zend_arena_calloc(&CG(ast_arena), num_names, sizeof(zend_string*)); - memset(param_names, 0, sizeof(zend_string*) * num_names); zp_assign_names(param_names, num_names, argc, argv, function, uses_variadic_placeholder, extra_named_params); From 539954718970b28135d89b7972e969b9fa5c4db9 Mon Sep 17 00:00:00 2001 From: Arnaud Le Blanc Date: Wed, 7 Jan 2026 14:30:06 +0100 Subject: [PATCH 08/15] Move include --- Zend/zend_execute.c | 1 - Zend/zend_vm_execute.skl | 1 + 2 files changed, 1 insertion(+), 1 deletion(-) diff --git a/Zend/zend_execute.c b/Zend/zend_execute.c index 5659f4c9692a1..ed4e0c7bc986d 100644 --- a/Zend/zend_execute.c +++ b/Zend/zend_execute.c @@ -45,7 +45,6 @@ #include "zend_call_stack.h" #include "zend_attributes.h" #include "Optimizer/zend_func_info.h" -#include "zend_partial.h" /* Virtual current working directory support */ #include "zend_virtual_cwd.h" diff --git a/Zend/zend_vm_execute.skl b/Zend/zend_vm_execute.skl index 53b1ac6baf0a3..d664d17be6074 100644 --- a/Zend/zend_vm_execute.skl +++ b/Zend/zend_vm_execute.skl @@ -1,4 +1,5 @@ #include "Zend/zend_vm_opcodes.h" +#include "Zend/zend_partial.h" {%DEFINES%} From ccca2eb3e638fbf232a0389db5e8399884567f8f Mon Sep 17 00:00:00 2001 From: Arnaud Le Blanc Date: Wed, 7 Jan 2026 15:03:55 +0100 Subject: [PATCH 09/15] Rename tests --- Zend/tests/partial_application/magic_003.phpt | 2 +- Zend/tests/partial_application/magic_004.phpt | 3 ++- 2 files changed, 3 insertions(+), 2 deletions(-) diff --git a/Zend/tests/partial_application/magic_003.phpt b/Zend/tests/partial_application/magic_003.phpt index 75e26c70b1a34..242403ea38fc8 100644 --- a/Zend/tests/partial_application/magic_003.phpt +++ b/Zend/tests/partial_application/magic_003.phpt @@ -1,5 +1,5 @@ --TEST-- -PFA magic trampoline release unused +PFA magic trampoline release (result unused) --FILE-- From cbcdbc197fe6682cedff7306025b6f0f1f40fdf1 Mon Sep 17 00:00:00 2001 From: Arnaud Le Blanc Date: Wed, 7 Jan 2026 15:04:02 +0100 Subject: [PATCH 10/15] Fix test --- .../rfc_examples_incompatible_functions.phpt | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/Zend/tests/partial_application/rfc_examples_incompatible_functions.phpt b/Zend/tests/partial_application/rfc_examples_incompatible_functions.phpt index 2455252b3eba9..dd99cb229ae8a 100644 --- a/Zend/tests/partial_application/rfc_examples_incompatible_functions.phpt +++ b/Zend/tests/partial_application/rfc_examples_incompatible_functions.phpt @@ -4,11 +4,11 @@ PFA RFC examples: "Incompatible functions" section getMessage(), "\n"; } ?> --EXPECT-- -Error: Cannot call func_get_args() dynamically +Error: Cannot call func_get_arg() dynamically From 96c4a781d184880eb1564b96bacfeed58ff3eb61 Mon Sep 17 00:00:00 2001 From: Arnaud Le Blanc Date: Wed, 7 Jan 2026 15:04:53 +0100 Subject: [PATCH 11/15] Better type --- Zend/zend_ast.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Zend/zend_ast.c b/Zend/zend_ast.c index 96b54f18c7fbd..ffd74f54b666d 100644 --- a/Zend/zend_ast.c +++ b/Zend/zend_ast.c @@ -1455,7 +1455,7 @@ ZEND_API zend_ast * ZEND_FASTCALL zend_ast_dup(zend_ast *ast) { ZEND_ASSERT(ast != NULL); - void *buf = zend_ast_alloc(zend_ast_tree_size(ast)); + zend_ast *buf = zend_ast_alloc(zend_ast_tree_size(ast)); zend_ast_tree_copy(ast, buf); return buf; From ec25cd64c902af7fc007a96612b98edcf1afbf4e Mon Sep 17 00:00:00 2001 From: Arnaud Le Blanc Date: Wed, 7 Jan 2026 15:06:29 +0100 Subject: [PATCH 12/15] Generated file --- Zend/zend_vm_execute.h | 1 + 1 file changed, 1 insertion(+) diff --git a/Zend/zend_vm_execute.h b/Zend/zend_vm_execute.h index ce66b5ca5e302..19542dcfa21c4 100644 --- a/Zend/zend_vm_execute.h +++ b/Zend/zend_vm_execute.h @@ -307,6 +307,7 @@ static uint8_t zend_user_opcodes[256] = {0, }; #include "Zend/zend_vm_opcodes.h" +#include "Zend/zend_partial.h" #define SPEC_START_MASK 0x0000ffff #define SPEC_EXTRA_MASK 0xfffc0000 From 029481c8cc7c70ca8960557e176840c9ecf364c1 Mon Sep 17 00:00:00 2001 From: Arnaud Le Blanc Date: Thu, 8 Jan 2026 10:36:07 +0100 Subject: [PATCH 13/15] Consistency --- Zend/tests/partial_application/non_dynamic_call_funcs.phpt | 6 +++--- Zend/zend_partial.c | 6 ++++-- Zend/zend_string.h | 1 + 3 files changed, 8 insertions(+), 5 deletions(-) diff --git a/Zend/tests/partial_application/non_dynamic_call_funcs.phpt b/Zend/tests/partial_application/non_dynamic_call_funcs.phpt index 0430986be2ac2..db25ef3dcc93b 100644 --- a/Zend/tests/partial_application/non_dynamic_call_funcs.phpt +++ b/Zend/tests/partial_application/non_dynamic_call_funcs.phpt @@ -41,6 +41,6 @@ foreach (['_func_get_arg', '_compact', '_extract', '_func_get_args', '_func_num_ Error: Cannot call func_get_arg() dynamically Error: Cannot call compact() dynamically Error: Cannot call extract() dynamically -ArgumentCountError: Partial application of func_get_args() expects at most 0 arguments, 1 given -ArgumentCountError: Partial application of func_num_args() expects at most 0 arguments, 1 given -ArgumentCountError: Partial application of get_defined_vars() expects at most 0 arguments, 1 given +Error: Cannot call func_get_args() dynamically +Error: Cannot call func_num_args() dynamically +Error: Cannot call get_defined_vars() dynamically diff --git a/Zend/zend_partial.c b/Zend/zend_partial.c index 04a34e8a74228..dff8d58fec3df 100644 --- a/Zend/zend_partial.c +++ b/Zend/zend_partial.c @@ -666,10 +666,12 @@ static uint32_t zp_compute_num_required(zend_function *function, /* Functions that do not allow to be called dynamically */ static const zend_known_string_id zp_non_dynamic_call_funcs[] = { - ZEND_STR_FUNC_GET_ARG, ZEND_STR_COMPACT, ZEND_STR_EXTRACT, - /* Omit nullary functions such as func_num_args(), as these can't be PFA'd*/ + ZEND_STR_FUNC_GET_ARG, + ZEND_STR_FUNC_GET_ARGS, + ZEND_STR_FUNC_NUM_ARGS, + ZEND_STR_GET_DEFINED_VARS, }; static bool zp_is_non_dynamic_call_func(zend_function *function) diff --git a/Zend/zend_string.h b/Zend/zend_string.h index 36aec8e16392a..9ff948c03b643 100644 --- a/Zend/zend_string.h +++ b/Zend/zend_string.h @@ -638,6 +638,7 @@ EMPTY_SWITCH_DEFAULT_CASE() _(ZEND_STR_ASSERT, "assert") \ _(ZEND_STR_CALL_USER_FUNC, "call_user_func") \ _(ZEND_STR_ARRAY_SLICE, "array_slice") \ + _(ZEND_STR_GET_DEFINED_VARS, "get_defined_vars") \ _(ZEND_STR_SENSITIVEPARAMETER, "SensitiveParameter") \ _(ZEND_STR_CONST_EXPR_PLACEHOLDER, "[constant expression]") \ _(ZEND_STR_DEPRECATED_CAPITALIZED, "Deprecated") \ From 3fc7f7f8beaf4187e98f4f222bec6e26a6d37096 Mon Sep 17 00:00:00 2001 From: Arnaud Le Blanc Date: Thu, 8 Jan 2026 10:37:49 +0100 Subject: [PATCH 14/15] Fix test name --- Zend/tests/partial_application/references_001.phpt | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Zend/tests/partial_application/references_001.phpt b/Zend/tests/partial_application/references_001.phpt index 4a6a8663d8708..080b3085acc07 100644 --- a/Zend/tests/partial_application/references_001.phpt +++ b/Zend/tests/partial_application/references_001.phpt @@ -1,5 +1,5 @@ --TEST-- -PFA returns by val if the actual function does +PFA receives by val if the actual function does --FILE-- Date: Thu, 8 Jan 2026 10:44:05 +0100 Subject: [PATCH 15/15] Improve tests --- Zend/tests/partial_application/references_002.phpt | 2 +- .../tests/partial_application/rfc_examples_debug.phpt | 2 +- Zend/tests/partial_application/statics_001.phpt | 11 +++++------ Zend/tests/partial_application/statics_002.phpt | 9 ++++----- Zend/tests/partial_application/statics_003.phpt | 9 ++++----- Zend/tests/partial_application/variation_ex_001.phpt | 2 +- .../partial_application/variation_invoke_001.phpt | 4 ++-- 7 files changed, 18 insertions(+), 21 deletions(-) diff --git a/Zend/tests/partial_application/references_002.phpt b/Zend/tests/partial_application/references_002.phpt index c203b4fb907af..42a90648c1ae8 100644 --- a/Zend/tests/partial_application/references_002.phpt +++ b/Zend/tests/partial_application/references_002.phpt @@ -7,7 +7,7 @@ function foo($a, &$b) { $b = 2; } -$a = []; +$a = ['this will be changed']; $b = &$a[0]; $foo = foo(1, ?); diff --git a/Zend/tests/partial_application/rfc_examples_debug.phpt b/Zend/tests/partial_application/rfc_examples_debug.phpt index be06e24229f7d..9c5501090d4e0 100644 --- a/Zend/tests/partial_application/rfc_examples_debug.phpt +++ b/Zend/tests/partial_application/rfc_examples_debug.phpt @@ -12,7 +12,7 @@ function g() { } $f = f(?, ?, 3); -$f(1,2); +$f(1, 2); ?> --EXPECTF-- diff --git a/Zend/tests/partial_application/statics_001.phpt b/Zend/tests/partial_application/statics_001.phpt index 906c0dedf121c..610cddf37b547 100644 --- a/Zend/tests/partial_application/statics_001.phpt +++ b/Zend/tests/partial_application/statics_001.phpt @@ -10,13 +10,12 @@ function foo($a) { return $var; } -foo(new stdClass); +var_dump(foo(new stdClass)); $foo = foo(new stdClass, ...); -if ($foo() == 2) { - echo "OK"; -} +var_dump($foo()); ?> ---EXPECTF-- -OK +--EXPECT-- +int(1) +int(2) diff --git a/Zend/tests/partial_application/statics_002.phpt b/Zend/tests/partial_application/statics_002.phpt index 8e1b6cefe00ae..4e6cc82b720db 100644 --- a/Zend/tests/partial_application/statics_002.phpt +++ b/Zend/tests/partial_application/statics_002.phpt @@ -10,14 +10,13 @@ $closure = function ($a) { return $var; }; -$closure(new stdClass); +var_dump($closure(new stdClass)); $foo = $closure(new stdClass, ...); $closure = null; -if ($foo() == 2) { - echo "OK"; -} +var_dump($foo()); ?> --EXPECT-- -OK +int(1) +int(2) diff --git a/Zend/tests/partial_application/statics_003.phpt b/Zend/tests/partial_application/statics_003.phpt index 9fb2568a67e78..04328bf717b8a 100644 --- a/Zend/tests/partial_application/statics_003.phpt +++ b/Zend/tests/partial_application/statics_003.phpt @@ -10,7 +10,7 @@ $closure = function ($a) { return $var; }; -$closure(new stdClass); +var_dump($closure(new stdClass)); $foo = $closure(?); $closure = null; @@ -18,9 +18,8 @@ $closure = null; $bar = $foo(?); $foo = null; -if ($bar(new stdClass) == 2) { - echo "OK"; -} +var_dump($bar(new stdClass)); ?> --EXPECT-- -OK +int(1) +int(2) diff --git a/Zend/tests/partial_application/variation_ex_001.phpt b/Zend/tests/partial_application/variation_ex_001.phpt index 48db63e70a5ed..0f36e90f616d2 100644 --- a/Zend/tests/partial_application/variation_ex_001.phpt +++ b/Zend/tests/partial_application/variation_ex_001.phpt @@ -5,7 +5,7 @@ PFA variation: UAF in cleanup unfinished calls function test($a){} try { - test(1,...)(?); + test(1, ...)(?); } catch (Error $ex) { echo $ex::class, ": ", $ex->getMessage(), "\n"; } diff --git a/Zend/tests/partial_application/variation_invoke_001.phpt b/Zend/tests/partial_application/variation_invoke_001.phpt index a6091e955b156..9285c08da19b9 100644 --- a/Zend/tests/partial_application/variation_invoke_001.phpt +++ b/Zend/tests/partial_application/variation_invoke_001.phpt @@ -8,7 +8,7 @@ function foo($a, $b) { $foo = foo(b: 10, ...); -var_dump($foo->__invoke(32) == 42); +var_dump($foo->__invoke(32)); try { $foo->nothing(); @@ -17,5 +17,5 @@ try { } ?> --EXPECT-- -bool(true) +int(42) Error: Call to undefined method Closure::nothing()