From 11f950e77ecd8d54b6d3692d5f3a76b6489164e6 Mon Sep 17 00:00:00 2001 From: Nikita Popov Date: Mon, 18 Apr 2022 17:56:30 +0200 Subject: [PATCH] Don't optimize trailing args for prototype fbc --- Zend/Optimizer/optimize_func_calls.c | 22 ++++++++++++++++++---- Zend/tests/add_optional_by_ref_arg.phpt | 22 ++++++++++++++++++++++ 2 files changed, 40 insertions(+), 4 deletions(-) create mode 100644 Zend/tests/add_optional_by_ref_arg.phpt diff --git a/Zend/Optimizer/optimize_func_calls.c b/Zend/Optimizer/optimize_func_calls.c index 351010177f6..91ed11a4106 100644 --- a/Zend/Optimizer/optimize_func_calls.c +++ b/Zend/Optimizer/optimize_func_calls.c @@ -149,6 +149,20 @@ static void zend_try_inline_call(zend_op_array *op_array, zend_op *fcall, zend_o } } +/* arg_num is 1-based here, to match SEND encoding. */ +static bool has_known_send_mode(const optimizer_call_info *info, uint32_t arg_num) +{ + if (!info->func) { + return false; + } + + /* For prototype functions we should not make assumptions about arguments that are not part of + * the signature: And inheriting method can add an optional by-ref argument. */ + return !info->is_prototype + || arg_num <= info->func->common.num_args + || (info->func->common.fn_flags & ZEND_ACC_VARIADIC); +} + void zend_optimize_func_calls(zend_op_array *op_array, zend_optimizer_ctx *ctx) { zend_op *opline = op_array->opcodes; @@ -261,7 +275,7 @@ void zend_optimize_func_calls(zend_op_array *op_array, zend_optimizer_ctx *ctx) } break; case ZEND_SEND_VAL_EX: - if (call_stack[call - 1].func) { + if (has_known_send_mode(&call_stack[call - 1], opline->op2.num)) { if (opline->op2_type == IS_CONST) { call_stack[call - 1].try_inline = 0; break; @@ -276,7 +290,7 @@ void zend_optimize_func_calls(zend_op_array *op_array, zend_optimizer_ctx *ctx) } break; case ZEND_CHECK_FUNC_ARG: - if (call_stack[call - 1].func) { + if (has_known_send_mode(&call_stack[call - 1], opline->op2.num)) { if (opline->op2_type == IS_CONST) { call_stack[call - 1].try_inline = 0; call_stack[call - 1].func_arg_num = (uint32_t)-1; @@ -289,7 +303,7 @@ void zend_optimize_func_calls(zend_op_array *op_array, zend_optimizer_ctx *ctx) break; case ZEND_SEND_VAR_EX: case ZEND_SEND_FUNC_ARG: - if (call_stack[call - 1].func) { + if (has_known_send_mode(&call_stack[call - 1], opline->op2.num)) { if (opline->op2_type == IS_CONST) { call_stack[call - 1].try_inline = 0; break; @@ -304,7 +318,7 @@ void zend_optimize_func_calls(zend_op_array *op_array, zend_optimizer_ctx *ctx) } break; case ZEND_SEND_VAR_NO_REF_EX: - if (call_stack[call - 1].func) { + if (has_known_send_mode(&call_stack[call - 1], opline->op2.num)) { if (opline->op2_type == IS_CONST) { call_stack[call - 1].try_inline = 0; break; diff --git a/Zend/tests/add_optional_by_ref_arg.phpt b/Zend/tests/add_optional_by_ref_arg.phpt new file mode 100644 index 00000000000..7ce4fd5a46f --- /dev/null +++ b/Zend/tests/add_optional_by_ref_arg.phpt @@ -0,0 +1,22 @@ +--TEST-- +Adding an optional by-ref arg in a child method +--FILE-- +method2($x); + var_dump($x); + } + public function method2() {} +} +class Test2 extends Test1 { + public function method2(&$x = null) { + ++$x; + } +} +(new Test2)->method1(); + +?> +--EXPECT-- +int(1)