summaryrefslogtreecommitdiff
path: root/ext
diff options
context:
space:
mode:
authorNikita Popov <nikita.ppv@gmail.com>2020-04-06 12:46:52 +0200
committerNikita Popov <nikita.ppv@gmail.com>2020-07-31 15:53:36 +0200
commitd92229d8c78aac25925284e23aa7903dca9ed005 (patch)
treeec01712dbe44a11cb7368492388cb39ab04dac17 /ext
parent9a71d47d7334b9e2f83db48498047750c04cd192 (diff)
downloadphp-git-d92229d8c78aac25925284e23aa7903dca9ed005.tar.gz
Implement named parameters
From an engine perspective, named parameters mainly add three concepts: * The SEND_* opcodes now accept a CONST op2, which is the argument name. For now, it is looked up by linear scan and runtime cached. * This may leave UNDEF arguments on the stack. To avoid having to deal with them in other places, a CHECK_UNDEF_ARGS opcode is used to either replace them with defaults, or error. * For variadic functions, EX(extra_named_params) are collected and need to be freed based on ZEND_CALL_HAS_EXTRA_NAMED_PARAMS. RFC: https://wiki.php.net/rfc/named_params Closes GH-5357.
Diffstat (limited to 'ext')
-rw-r--r--ext/curl/interface.c5
-rw-r--r--ext/dom/xpath.c1
-rw-r--r--ext/ffi/ffi.c1
-rw-r--r--ext/mysqli/mysqli.c1
-rw-r--r--ext/opcache/Optimizer/compact_literals.c14
-rw-r--r--ext/opcache/Optimizer/optimize_func_calls.c33
-rw-r--r--ext/opcache/Optimizer/zend_call_graph.c6
-rw-r--r--ext/opcache/Optimizer/zend_call_graph.h1
-rw-r--r--ext/opcache/Optimizer/zend_inference.c14
-rw-r--r--ext/opcache/jit/zend_jit.c21
-rw-r--r--ext/opcache/jit/zend_jit_trace.c21
-rw-r--r--ext/opcache/jit/zend_jit_vm_helpers.c6
-rw-r--r--ext/opcache/jit/zend_jit_x86.dasc38
-rw-r--r--ext/opcache/zend_file_cache.c6
-rw-r--r--ext/opcache/zend_persist.c5
-rw-r--r--ext/opcache/zend_persist_calc.c5
-rw-r--r--ext/pcre/php_pcre.c1
-rw-r--r--ext/pdo/pdo_dbh.c1
-rw-r--r--ext/pgsql/pgsql.c1
-rw-r--r--ext/reflection/php_reflection.c180
-rw-r--r--ext/spl/php_spl.c2
-rw-r--r--ext/spl/spl_directory.c1
-rwxr-xr-xext/standard/basic_functions.c11
-rw-r--r--ext/xml/xml.c1
-rw-r--r--ext/xsl/xsltprocessor.c1
25 files changed, 249 insertions, 128 deletions
diff --git a/ext/curl/interface.c b/ext/curl/interface.c
index e129c7708d..f8edbefa2f 100644
--- a/ext/curl/interface.c
+++ b/ext/curl/interface.c
@@ -1385,6 +1385,7 @@ static size_t curl_write(char *data, size_t size, size_t nmemb, void *ctx)
fci.retval = &retval;
fci.param_count = 2;
fci.params = argv;
+ fci.named_params = NULL;
ch->in_callback = 1;
error = zend_call_function(&fci, &t->fci_cache);
@@ -1431,6 +1432,7 @@ static int curl_fnmatch(void *ctx, const char *pattern, const char *string)
fci.retval = &retval;
fci.param_count = 3;
fci.params = argv;
+ fci.named_params = NULL;
ch->in_callback = 1;
error = zend_call_function(&fci, &t->fci_cache);
@@ -1483,6 +1485,7 @@ static size_t curl_progress(void *clientp, double dltotal, double dlnow, double
fci.retval = &retval;
fci.param_count = 5;
fci.params = argv;
+ fci.named_params = NULL;
ch->in_callback = 1;
error = zend_call_function(&fci, &t->fci_cache);
@@ -1538,6 +1541,7 @@ static size_t curl_read(char *data, size_t size, size_t nmemb, void *ctx)
fci.retval = &retval;
fci.param_count = 3;
fci.params = argv;
+ fci.named_params = NULL;
ch->in_callback = 1;
error = zend_call_function(&fci, &t->fci_cache);
@@ -1599,6 +1603,7 @@ static size_t curl_write_header(char *data, size_t size, size_t nmemb, void *ctx
fci.retval = &retval;
fci.param_count = 2;
fci.params = argv;
+ fci.named_params = NULL;
ch->in_callback = 1;
error = zend_call_function(&fci, &t->fci_cache);
diff --git a/ext/dom/xpath.c b/ext/dom/xpath.c
index c96f104229..06f52bb87f 100644
--- a/ext/dom/xpath.c
+++ b/ext/dom/xpath.c
@@ -150,6 +150,7 @@ static void dom_xpath_ext_function_php(xmlXPathParserContextPtr ctxt, int nargs,
xmlXPathFreeObject(obj);
fci.object = NULL;
+ fci.named_params = NULL;
fci.retval = &retval;
if (!zend_make_callable(&fci.function_name, &callable)) {
diff --git a/ext/ffi/ffi.c b/ext/ffi/ffi.c
index 6eac3a04a4..0be6ba4d4c 100644
--- a/ext/ffi/ffi.c
+++ b/ext/ffi/ffi.c
@@ -858,6 +858,7 @@ static void zend_ffi_callback_trampoline(ffi_cif* cif, void* ret, void** args, v
fci.params = do_alloca(sizeof(zval) *callback_data->arg_count, use_heap);
fci.object = NULL;
fci.param_count = callback_data->arg_count;
+ fci.named_params = NULL;
if (callback_data->type->func.args) {
int n = 0;
diff --git a/ext/mysqli/mysqli.c b/ext/mysqli/mysqli.c
index ba1fdf8033..505b4c05c6 100644
--- a/ext/mysqli/mysqli.c
+++ b/ext/mysqli/mysqli.c
@@ -1227,6 +1227,7 @@ void php_mysqli_fetch_into_hash(INTERNAL_FUNCTION_PARAMETERS, int override_flags
fci.retval = &retval;
fci.params = NULL;
fci.param_count = 0;
+ fci.named_params = NULL;
if (ctor_params && Z_TYPE_P(ctor_params) != IS_NULL) {
if (zend_fcall_info_args(&fci, ctor_params) == FAILURE) {
diff --git a/ext/opcache/Optimizer/compact_literals.c b/ext/opcache/Optimizer/compact_literals.c
index a8c770b3d0..3feba98d03 100644
--- a/ext/opcache/Optimizer/compact_literals.c
+++ b/ext/opcache/Optimizer/compact_literals.c
@@ -782,6 +782,20 @@ void zend_optimizer_compact_literals(zend_op_array *op_array, zend_optimizer_ctx
opline->extended_value = cache_size;
cache_size += sizeof(void *);
break;
+ case ZEND_SEND_VAL:
+ case ZEND_SEND_VAL_EX:
+ case ZEND_SEND_VAR:
+ case ZEND_SEND_VAR_EX:
+ case ZEND_SEND_VAR_NO_REF:
+ case ZEND_SEND_VAR_NO_REF_EX:
+ case ZEND_SEND_REF:
+ case ZEND_SEND_FUNC_ARG:
+ case ZEND_CHECK_FUNC_ARG:
+ if (opline->op2_type == IS_CONST) {
+ opline->result.num = cache_size;
+ cache_size += 2 * sizeof(void *);
+ }
+ break;
}
opline++;
}
diff --git a/ext/opcache/Optimizer/optimize_func_calls.c b/ext/opcache/Optimizer/optimize_func_calls.c
index c1ac58d0de..b1d8f6b929 100644
--- a/ext/opcache/Optimizer/optimize_func_calls.c
+++ b/ext/opcache/Optimizer/optimize_func_calls.c
@@ -230,8 +230,8 @@ void zend_optimize_func_calls(zend_op_array *op_array, zend_optimizer_ctx *ctx)
case ZEND_FETCH_STATIC_PROP_FUNC_ARG:
case ZEND_FETCH_OBJ_FUNC_ARG:
case ZEND_FETCH_DIM_FUNC_ARG:
- if (call_stack[call - 1].func) {
- ZEND_ASSERT(call_stack[call - 1].func_arg_num != (uint32_t)-1);
+ if (call_stack[call - 1].func
+ && call_stack[call - 1].func_arg_num != (uint32_t)-1) {
if (ARG_SHOULD_BE_SENT_BY_REF(call_stack[call - 1].func, call_stack[call - 1].func_arg_num)) {
if (opline->opcode != ZEND_FETCH_STATIC_PROP_FUNC_ARG) {
opline->opcode -= 9;
@@ -257,6 +257,11 @@ 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 (opline->op2_type == IS_CONST) {
+ call_stack[call - 1].try_inline = 0;
+ break;
+ }
+
if (ARG_MUST_BE_SENT_BY_REF(call_stack[call - 1].func, opline->op2.num)) {
/* We won't convert it into_DO_FCALL to emit error at run-time */
call_stack[call - 1].opline = NULL;
@@ -267,6 +272,12 @@ 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 (opline->op2_type == IS_CONST) {
+ call_stack[call - 1].try_inline = 0;
+ call_stack[call - 1].func_arg_num = (uint32_t)-1;
+ break;
+ }
+
call_stack[call - 1].func_arg_num = opline->op2.num;
MAKE_NOP(opline);
}
@@ -274,6 +285,11 @@ void zend_optimize_func_calls(zend_op_array *op_array, zend_optimizer_ctx *ctx)
case ZEND_SEND_VAR_EX:
case ZEND_SEND_FUNC_ARG:
if (call_stack[call - 1].func) {
+ if (opline->op2_type == IS_CONST) {
+ call_stack[call - 1].try_inline = 0;
+ break;
+ }
+
call_stack[call - 1].func_arg_num = (uint32_t)-1;
if (ARG_SHOULD_BE_SENT_BY_REF(call_stack[call - 1].func, opline->op2.num)) {
opline->opcode = ZEND_SEND_REF;
@@ -284,6 +300,11 @@ 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 (opline->op2_type == IS_CONST) {
+ call_stack[call - 1].try_inline = 0;
+ break;
+ }
+
if (ARG_MUST_BE_SENT_BY_REF(call_stack[call - 1].func, opline->op2.num)) {
opline->opcode = ZEND_SEND_VAR_NO_REF;
} else if (ARG_MAY_BE_SENT_BY_REF(call_stack[call - 1].func, opline->op2.num)) {
@@ -293,6 +314,14 @@ void zend_optimize_func_calls(zend_op_array *op_array, zend_optimizer_ctx *ctx)
}
}
break;
+ case ZEND_SEND_VAL:
+ case ZEND_SEND_VAR:
+ case ZEND_SEND_REF:
+ if (opline->op2_type == IS_CONST) {
+ call_stack[call - 1].try_inline = 0;
+ break;
+ }
+ break;
case ZEND_SEND_UNPACK:
case ZEND_SEND_USER:
case ZEND_SEND_ARRAY:
diff --git a/ext/opcache/Optimizer/zend_call_graph.c b/ext/opcache/Optimizer/zend_call_graph.c
index a32dacbb1b..0fa8945f09 100644
--- a/ext/opcache/Optimizer/zend_call_graph.c
+++ b/ext/opcache/Optimizer/zend_call_graph.c
@@ -124,8 +124,12 @@ int zend_analyze_calls(zend_arena **arena, zend_script *script, uint32_t build_f
case ZEND_SEND_VAR_NO_REF_EX:
case ZEND_SEND_USER:
if (call_info) {
- uint32_t num = opline->op2.num;
+ if (opline->op2_type == IS_CONST) {
+ call_info->named_args = 1;
+ break;
+ }
+ uint32_t num = opline->op2.num;
if (num > 0) {
num--;
}
diff --git a/ext/opcache/Optimizer/zend_call_graph.h b/ext/opcache/Optimizer/zend_call_graph.h
index 1c263f6f63..28522a1277 100644
--- a/ext/opcache/Optimizer/zend_call_graph.h
+++ b/ext/opcache/Optimizer/zend_call_graph.h
@@ -36,6 +36,7 @@ struct _zend_call_info {
zend_call_info *next_callee;
zend_bool recursive;
zend_bool send_unpack; /* Parameters passed by SEND_UNPACK or SEND_ARRAY */
+ zend_bool named_args; /* Function has named arguments */
int num_args;
zend_send_arg_info arg_info[1];
};
diff --git a/ext/opcache/Optimizer/zend_inference.c b/ext/opcache/Optimizer/zend_inference.c
index 1ca381d968..ab8b751acc 100644
--- a/ext/opcache/Optimizer/zend_inference.c
+++ b/ext/opcache/Optimizer/zend_inference.c
@@ -4354,13 +4354,8 @@ int zend_may_throw_ex(const zend_op *opline, const zend_ssa_op *ssa_op, const ze
case ZEND_JMP:
case ZEND_CHECK_VAR:
case ZEND_MAKE_REF:
- case ZEND_SEND_VAR:
case ZEND_BEGIN_SILENCE:
case ZEND_END_SILENCE:
- case ZEND_SEND_VAL:
- case ZEND_SEND_REF:
- case ZEND_SEND_VAR_EX:
- case ZEND_SEND_FUNC_ARG:
case ZEND_FREE:
case ZEND_SEPARATE:
case ZEND_TYPE_CHECK:
@@ -4375,10 +4370,17 @@ int zend_may_throw_ex(const zend_op *opline, const zend_ssa_op *ssa_op, const ze
case ZEND_FUNC_NUM_ARGS:
case ZEND_FUNC_GET_ARGS:
case ZEND_COPY_TMP:
- case ZEND_CHECK_FUNC_ARG:
case ZEND_CASE_STRICT:
case ZEND_JMP_NULL:
return 0;
+ case ZEND_SEND_VAR:
+ case ZEND_SEND_VAL:
+ case ZEND_SEND_REF:
+ case ZEND_SEND_VAR_EX:
+ case ZEND_SEND_FUNC_ARG:
+ case ZEND_CHECK_FUNC_ARG:
+ /* May throw for named params. */
+ return opline->op2_type == IS_CONST;
case ZEND_INIT_FCALL:
/* can't throw, because call is resolved at compile time */
return 0;
diff --git a/ext/opcache/jit/zend_jit.c b/ext/opcache/jit/zend_jit.c
index 02329eb20e..feff3de6d3 100644
--- a/ext/opcache/jit/zend_jit.c
+++ b/ext/opcache/jit/zend_jit.c
@@ -2521,6 +2521,10 @@ static int zend_jit(const zend_op_array *op_array, zend_ssa *ssa, const zend_op
goto done;
case ZEND_SEND_VAL:
case ZEND_SEND_VAL_EX:
+ if (opline->op2_type == IS_CONST) {
+ /* Named parameters not supported in JIT (yet) */
+ break;
+ }
if (opline->opcode == ZEND_SEND_VAL_EX
&& opline->op2.num > MAX_ARG_FLAG_NUM) {
break;
@@ -2531,6 +2535,10 @@ static int zend_jit(const zend_op_array *op_array, zend_ssa *ssa, const zend_op
}
goto done;
case ZEND_SEND_REF:
+ if (opline->op2_type == IS_CONST) {
+ /* Named parameters not supported in JIT (yet) */
+ break;
+ }
if (!zend_jit_send_ref(&dasm_state, opline, op_array,
OP1_INFO(), 0)) {
goto jit_failure;
@@ -2541,6 +2549,10 @@ static int zend_jit(const zend_op_array *op_array, zend_ssa *ssa, const zend_op
case ZEND_SEND_VAR_NO_REF:
case ZEND_SEND_VAR_NO_REF_EX:
case ZEND_SEND_FUNC_ARG:
+ if (opline->op2_type == IS_CONST) {
+ /* Named parameters not supported in JIT (yet) */
+ break;
+ }
if ((opline->opcode == ZEND_SEND_VAR_EX
|| opline->opcode == ZEND_SEND_VAR_NO_REF_EX)
&& opline->op2.num > MAX_ARG_FLAG_NUM) {
@@ -2560,6 +2572,10 @@ static int zend_jit(const zend_op_array *op_array, zend_ssa *ssa, const zend_op
}
goto done;
case ZEND_CHECK_FUNC_ARG:
+ if (opline->op2_type == IS_CONST) {
+ /* Named parameters not supported in JIT (yet) */
+ break;
+ }
if (opline->op2.num > MAX_ARG_FLAG_NUM) {
break;
}
@@ -2567,6 +2583,11 @@ static int zend_jit(const zend_op_array *op_array, zend_ssa *ssa, const zend_op
goto jit_failure;
}
goto done;
+ case ZEND_CHECK_UNDEF_ARGS:
+ if (!zend_jit_check_undef_args(&dasm_state, opline)) {
+ goto jit_failure;
+ }
+ goto done;
case ZEND_DO_UCALL:
is_terminated = 1;
/* break missing intentionally */
diff --git a/ext/opcache/jit/zend_jit_trace.c b/ext/opcache/jit/zend_jit_trace.c
index 39edc693c9..96fa37abb7 100644
--- a/ext/opcache/jit/zend_jit_trace.c
+++ b/ext/opcache/jit/zend_jit_trace.c
@@ -3538,6 +3538,10 @@ static const void *zend_jit_trace(zend_jit_trace_rec *trace_buffer, uint32_t par
goto done;
case ZEND_SEND_VAL:
case ZEND_SEND_VAL_EX:
+ if (opline->op2_type == IS_CONST) {
+ /* Named parameters not supported in JIT */
+ break;
+ }
if (opline->opcode == ZEND_SEND_VAL_EX
&& opline->op2.num > MAX_ARG_FLAG_NUM) {
break;
@@ -3562,6 +3566,10 @@ static const void *zend_jit_trace(zend_jit_trace_rec *trace_buffer, uint32_t par
}
goto done;
case ZEND_SEND_REF:
+ if (opline->op2_type == IS_CONST) {
+ /* Named parameters not supported in JIT */
+ break;
+ }
op1_info = OP1_INFO();
if (!zend_jit_send_ref(&dasm_state, opline, op_array,
op1_info, 0)) {
@@ -3573,6 +3581,10 @@ static const void *zend_jit_trace(zend_jit_trace_rec *trace_buffer, uint32_t par
case ZEND_SEND_VAR_NO_REF:
case ZEND_SEND_VAR_NO_REF_EX:
case ZEND_SEND_FUNC_ARG:
+ if (opline->op2_type == IS_CONST) {
+ /* Named parameters not supported in JIT */
+ break;
+ }
if ((opline->opcode == ZEND_SEND_VAR_EX
|| opline->opcode == ZEND_SEND_VAR_NO_REF_EX)
&& opline->op2.num > MAX_ARG_FLAG_NUM) {
@@ -3609,6 +3621,10 @@ static const void *zend_jit_trace(zend_jit_trace_rec *trace_buffer, uint32_t par
}
goto done;
case ZEND_CHECK_FUNC_ARG:
+ if (opline->op2_type == IS_CONST) {
+ /* Named parameters not supported in JIT */
+ break;
+ }
if (opline->op2.num > MAX_ARG_FLAG_NUM
&& (!JIT_G(current_frame)
|| !JIT_G(current_frame)->call
@@ -3619,6 +3635,11 @@ static const void *zend_jit_trace(zend_jit_trace_rec *trace_buffer, uint32_t par
goto jit_failure;
}
goto done;
+ case ZEND_CHECK_UNDEF_ARGS:
+ if (!zend_jit_check_undef_args(&dasm_state, opline)) {
+ goto jit_failure;
+ }
+ goto done;
case ZEND_DO_UCALL:
case ZEND_DO_ICALL:
case ZEND_DO_FCALL_BY_NAME:
diff --git a/ext/opcache/jit/zend_jit_vm_helpers.c b/ext/opcache/jit/zend_jit_vm_helpers.c
index 26e9b6f843..f7704b72a4 100644
--- a/ext/opcache/jit/zend_jit_vm_helpers.c
+++ b/ext/opcache/jit/zend_jit_vm_helpers.c
@@ -57,6 +57,9 @@ ZEND_OPCODE_HANDLER_RET ZEND_FASTCALL zend_jit_leave_nested_func_helper(uint32_t
} else if (UNEXPECTED(call_info & ZEND_CALL_CLOSURE)) {
OBJ_RELEASE(ZEND_CLOSURE_OBJECT(EX(func)));
}
+ if (UNEXPECTED(call_info & ZEND_CALL_HAS_EXTRA_NAMED_PARAMS)) {
+ zend_free_extra_named_params(EX(extra_named_params));
+ }
old_execute_data = execute_data;
execute_data = EX(prev_execute_data);
@@ -89,6 +92,9 @@ ZEND_OPCODE_HANDLER_RET ZEND_FASTCALL zend_jit_leave_top_func_helper(uint32_t ca
}
zend_vm_stack_free_extra_args_ex(call_info, execute_data);
}
+ if (UNEXPECTED(call_info & ZEND_CALL_HAS_EXTRA_NAMED_PARAMS)) {
+ zend_free_extra_named_params(EX(extra_named_params));
+ }
if (UNEXPECTED(call_info & ZEND_CALL_CLOSURE)) {
OBJ_RELEASE(ZEND_CLOSURE_OBJECT(EX(func)));
}
diff --git a/ext/opcache/jit/zend_jit_x86.dasc b/ext/opcache/jit/zend_jit_x86.dasc
index e5f7e6045a..561d6f937d 100644
--- a/ext/opcache/jit/zend_jit_x86.dasc
+++ b/ext/opcache/jit/zend_jit_x86.dasc
@@ -8567,7 +8567,8 @@ static int zend_jit_do_fcall(dasm_State **Dst, const zend_op *opline, const zend
#endif
}
- if ((opline-1)->opcode == ZEND_SEND_UNPACK|| (opline-1)->opcode == ZEND_SEND_ARRAY) {
+ if ((opline-1)->opcode == ZEND_SEND_UNPACK || (opline-1)->opcode == ZEND_SEND_ARRAY ||
+ (opline-1)->opcode == ZEND_CHECK_UNDEF_ARGS) {
unknown_num_args = 1;
}
@@ -8621,6 +8622,10 @@ static int zend_jit_do_fcall(dasm_State **Dst, const zend_op *opline, const zend
}
}
+ bool may_have_extra_named_params =
+ opline->extended_value == ZEND_FCALL_MAY_HAVE_EXTRA_NAMED_PARAMS &&
+ (!func || func->common.fn_flags & ZEND_ACC_VARIADIC);
+
if (!reuse_ip) {
zend_jit_start_reuse_ip();
| // call = EX(call);
@@ -9025,6 +9030,17 @@ static int zend_jit_do_fcall(dasm_State **Dst, const zend_op *opline, const zend
| mov FCARG1a, RX
| EXT_CALL zend_jit_vm_stack_free_args_helper, r0
}
+ if (may_have_extra_named_params) {
+ | test byte [RX + offsetof(zend_execute_data, This.u1.type_info) + 3], (ZEND_CALL_HAS_EXTRA_NAMED_PARAMS >> 24)
+ | jnz >1
+ |.cold_code
+ |1:
+ | mov FCARG1a, aword [RX + offsetof(zend_execute_data, extra_named_params)]
+ | EXT_CALL zend_free_extra_named_params, r0
+ | jmp >2
+ |.code
+ |2:
+ }
|8:
if (opline->opcode == ZEND_DO_FCALL) {
@@ -9183,6 +9199,24 @@ static int zend_jit_send_val(dasm_State **Dst, const zend_op *opline, const zend
return 1;
}
+static int zend_jit_check_undef_args(dasm_State **Dst, const zend_op *opline)
+{
+ | mov FCARG1a, EX->call
+ | test byte [FCARG1a + offsetof(zend_execute_data, This.u1.type_info) + 3], (ZEND_CALL_MAY_HAVE_UNDEF >> 24)
+ | jnz >1
+ |.cold_code
+ |1:
+ | SAVE_VALID_OPLINE opline, r0
+ | EXT_CALL zend_handle_undef_args, r0
+ | test r0, r0
+ | jnz ->exception_handler
+ | jmp >2
+ |.code
+ |2:
+
+ return 1;
+}
+
static int zend_jit_send_ref(dasm_State **Dst, const zend_op *opline, const zend_op_array *op_array, uint32_t op1_info, int cold)
{
zend_jit_addr op1_addr, arg_addr, ref_addr;
@@ -10002,7 +10036,7 @@ static int zend_jit_leave_func(dasm_State **Dst, const zend_op *opline, const ze
{
/* ZEND_CALL_FAKE_CLOSURE handled on slow path to eliminate check for ZEND_CALL_CLOSURE on fast path */
| mov FCARG1d, dword [FP + offsetof(zend_execute_data, This.u1.type_info)]
- | test FCARG1d, (ZEND_CALL_TOP|ZEND_CALL_HAS_SYMBOL_TABLE|ZEND_CALL_FREE_EXTRA_ARGS|ZEND_CALL_ALLOCATED|ZEND_CALL_FAKE_CLOSURE)
+ | test FCARG1d, (ZEND_CALL_TOP|ZEND_CALL_HAS_SYMBOL_TABLE|ZEND_CALL_FREE_EXTRA_ARGS|ZEND_CALL_ALLOCATED|ZEND_CALL_HAS_EXTRA_NAMED_PARAMS|ZEND_CALL_FAKE_CLOSURE)
if (trace && trace->op != ZEND_JIT_TRACE_END) {
| jnz >1
|.cold_code
diff --git a/ext/opcache/zend_file_cache.c b/ext/opcache/zend_file_cache.c
index 81034a38fe..9e9463c8fd 100644
--- a/ext/opcache/zend_file_cache.c
+++ b/ext/opcache/zend_file_cache.c
@@ -415,7 +415,8 @@ static void zend_file_cache_serialize_attribute(zval *zv,
SERIALIZE_STR(attr->lcname);
for (i = 0; i < attr->argc; i++) {
- zend_file_cache_serialize_zval(&attr->argv[i], script, info, buf);
+ SERIALIZE_STR(attr->args[i].name);
+ zend_file_cache_serialize_zval(&attr->args[i].value, script, info, buf);
}
}
@@ -1180,7 +1181,8 @@ static void zend_file_cache_unserialize_attribute(zval *zv, zend_persistent_scri
UNSERIALIZE_STR(attr->lcname);
for (i = 0; i < attr->argc; i++) {
- zend_file_cache_unserialize_zval(&attr->argv[i], script, buf);
+ UNSERIALIZE_STR(attr->args[i].name);
+ zend_file_cache_unserialize_zval(&attr->args[i].value, script, buf);
}
}
diff --git a/ext/opcache/zend_persist.c b/ext/opcache/zend_persist.c
index 1063106717..ed923a03cc 100644
--- a/ext/opcache/zend_persist.c
+++ b/ext/opcache/zend_persist.c
@@ -278,7 +278,10 @@ static HashTable *zend_persist_attributes(HashTable *attributes)
zend_accel_store_interned_string(copy->lcname);
for (i = 0; i < copy->argc; i++) {
- zend_persist_zval(&copy->argv[i]);
+ if (copy->args[i].name) {
+ zend_accel_store_interned_string(copy->args[i].name);
+ }
+ zend_persist_zval(&copy->args[i].value);
}
ZVAL_PTR(v, copy);
diff --git a/ext/opcache/zend_persist_calc.c b/ext/opcache/zend_persist_calc.c
index 7e43fc83c0..4a612751a6 100644
--- a/ext/opcache/zend_persist_calc.c
+++ b/ext/opcache/zend_persist_calc.c
@@ -165,7 +165,10 @@ static void zend_persist_attributes_calc(HashTable *attributes)
ADD_INTERNED_STRING(attr->lcname);
for (i = 0; i < attr->argc; i++) {
- zend_persist_zval_calc(&attr->argv[i]);
+ if (attr->args[i].name) {
+ ADD_INTERNED_STRING(attr->args[i].name);
+ }
+ zend_persist_zval_calc(&attr->args[i].value);
}
} ZEND_HASH_FOREACH_END();
}
diff --git a/ext/pcre/php_pcre.c b/ext/pcre/php_pcre.c
index 8138dd5e69..acaeb19dc6 100644
--- a/ext/pcre/php_pcre.c
+++ b/ext/pcre/php_pcre.c
@@ -2400,6 +2400,7 @@ PHP_FUNCTION(preg_replace_callback_array)
fci.size = sizeof(fci);
fci.object = NULL;
+ fci.named_params = NULL;
ZEND_HASH_FOREACH_STR_KEY_VAL(pattern, str_idx_regex, replace) {
if (!str_idx_regex) {
diff --git a/ext/pdo/pdo_dbh.c b/ext/pdo/pdo_dbh.c
index 583374240c..f172d44ea9 100644
--- a/ext/pdo/pdo_dbh.c
+++ b/ext/pdo/pdo_dbh.c
@@ -437,6 +437,7 @@ static void pdo_stmt_construct(zend_execute_data *execute_data, pdo_stmt_t *stmt
fci.retval = &retval;
fci.param_count = 0;
fci.params = NULL;
+ fci.named_params = NULL;
zend_fcall_info_args(&fci, ctor_args);
diff --git a/ext/pgsql/pgsql.c b/ext/pgsql/pgsql.c
index c9b1652ea4..6ffe0efd84 100644
--- a/ext/pgsql/pgsql.c
+++ b/ext/pgsql/pgsql.c
@@ -2589,6 +2589,7 @@ static void php_pgsql_fetch_hash(INTERNAL_FUNCTION_PARAMETERS, zend_long result_
fci.retval = &retval;
fci.params = NULL;
fci.param_count = 0;
+ fci.named_params = NULL;
if (ctor_params && Z_TYPE_P(ctor_params) != IS_NULL) {
if (zend_fcall_info_args(&fci, ctor_params) == FAILURE) {
diff --git a/ext/reflection/php_reflection.c b/ext/reflection/php_reflection.c
index decb8d5e0a..48367c7deb 100644
--- a/ext/reflection/php_reflection.c
+++ b/ext/reflection/php_reflection.c
@@ -1796,18 +1796,19 @@ ZEND_METHOD(ReflectionFunctionAbstract, getStaticVariables)
ZEND_METHOD(ReflectionFunction, invoke)
{
zval retval;
- zval *params = NULL;
- int result, num_args = 0;
+ zval *params;
+ int result, num_args;
+ HashTable *named_params;
zend_fcall_info fci;
zend_fcall_info_cache fcc;
reflection_object *intern;
zend_function *fptr;
- GET_REFLECTION_OBJECT_PTR(fptr);
+ ZEND_PARSE_PARAMETERS_START(0, -1)
+ Z_PARAM_VARIADIC_WITH_NAMED(params, num_args, named_params)
+ ZEND_PARSE_PARAMETERS_END();
- if (zend_parse_parameters(ZEND_NUM_ARGS(), "*", &params, &num_args) == FAILURE) {
- RETURN_THROWS();
- }
+ GET_REFLECTION_OBJECT_PTR(fptr);
fci.size = sizeof(fci);
ZVAL_UNDEF(&fci.function_name);
@@ -1815,6 +1816,7 @@ ZEND_METHOD(ReflectionFunction, invoke)
fci.retval = &retval;
fci.param_count = num_args;
fci.params = params;
+ fci.named_params = named_params;
fcc.function_handler = fptr;
fcc.called_scope = NULL;
@@ -1846,36 +1848,26 @@ ZEND_METHOD(ReflectionFunction, invoke)
ZEND_METHOD(ReflectionFunction, invokeArgs)
{
zval retval;
- zval *params, *val;
int result;
- int i, argc;
zend_fcall_info fci;
zend_fcall_info_cache fcc;
reflection_object *intern;
zend_function *fptr;
- zval *param_array;
+ HashTable *params;
GET_REFLECTION_OBJECT_PTR(fptr);
- if (zend_parse_parameters(ZEND_NUM_ARGS(), "a", &param_array) == FAILURE) {
+ if (zend_parse_parameters(ZEND_NUM_ARGS(), "h", &params) == FAILURE) {
RETURN_THROWS();
}
- argc = zend_hash_num_elements(Z_ARRVAL_P(param_array));
-
- params = safe_emalloc(sizeof(zval), argc, 0);
- argc = 0;
- ZEND_HASH_FOREACH_VAL(Z_ARRVAL_P(param_array), val) {
- ZVAL_COPY(&params[argc], val);
- argc++;
- } ZEND_HASH_FOREACH_END();
-
fci.size = sizeof(fci);
ZVAL_UNDEF(&fci.function_name);
fci.object = NULL;
fci.retval = &retval;
- fci.param_count = argc;
- fci.params = params;
+ fci.param_count = 0;
+ fci.params = NULL;
+ fci.named_params = params;
fcc.function_handler = fptr;
fcc.called_scope = NULL;
@@ -1888,11 +1880,6 @@ ZEND_METHOD(ReflectionFunction, invokeArgs)
result = zend_call_function(&fci, &fcc);
- for (i = 0; i < argc; i++) {
- zval_ptr_dtor(&params[i]);
- }
- efree(params);
-
if (result == FAILURE) {
zend_throw_exception_ex(reflection_exception_ptr, 0,
"Invocation of function %s() failed", ZSTR_VAL(fptr->common.function_name));
@@ -3127,14 +3114,14 @@ ZEND_METHOD(ReflectionMethod, getClosure)
static void reflection_method_invoke(INTERNAL_FUNCTION_PARAMETERS, int variadic)
{
zval retval;
- zval *params = NULL, *val, *object;
+ zval *params = NULL, *object;
+ HashTable *named_params = NULL;
reflection_object *intern;
zend_function *mptr;
- int i, argc = 0, result;
+ int argc = 0, result;
zend_fcall_info fci;
zend_fcall_info_cache fcc;
zend_class_entry *obj_ce;
- zval *param_array;
GET_REFLECTION_OBJECT_PTR(mptr);
@@ -3155,22 +3142,14 @@ static void reflection_method_invoke(INTERNAL_FUNCTION_PARAMETERS, int variadic)
}
if (variadic) {
- if (zend_parse_parameters(ZEND_NUM_ARGS(), "o!*", &object, &params, &argc) == FAILURE) {
- RETURN_THROWS();
- }
+ ZEND_PARSE_PARAMETERS_START(1, -1)
+ Z_PARAM_OBJECT_OR_NULL(object)
+ Z_PARAM_VARIADIC_WITH_NAMED(params, argc, named_params)
+ ZEND_PARSE_PARAMETERS_END();
} else {
- if (zend_parse_parameters(ZEND_NUM_ARGS(), "o!a", &object, &param_array) == FAILURE) {
+ if (zend_parse_parameters(ZEND_NUM_ARGS(), "o!h", &object, &named_params) == FAILURE) {
RETURN_THROWS();
}
-
- argc = zend_hash_num_elements(Z_ARRVAL_P(param_array));
-
- params = safe_emalloc(sizeof(zval), argc, 0);
- argc = 0;
- ZEND_HASH_FOREACH_VAL(Z_ARRVAL_P(param_array), val) {
- ZVAL_COPY(&params[argc], val);
- argc++;
- } ZEND_HASH_FOREACH_END();
}
/* In case this is a static method, we shouldn't pass an object_ptr
@@ -3207,6 +3186,7 @@ static void reflection_method_invoke(INTERNAL_FUNCTION_PARAMETERS, int variadic)
fci.retval = &retval;
fci.param_count = argc;
fci.params = params;
+ fci.named_params = named_params;
fcc.function_handler = mptr;
fcc.called_scope = intern->ce;
@@ -3221,13 +3201,6 @@ static void reflection_method_invoke(INTERNAL_FUNCTION_PARAMETERS, int variadic)
result = zend_call_function(&fci, &fcc);
- if (!variadic) {
- for (i = 0; i < argc; i++) {
- zval_ptr_dtor(&params[i]);
- }
- efree(params);
- }
-
if (result == FAILURE) {
zend_throw_exception_ex(reflection_exception_ptr, 0,
"Invocation of method %s::%s() failed", ZSTR_VAL(mptr->common.scope->name), ZSTR_VAL(mptr->common.function_name));
@@ -4676,8 +4649,9 @@ ZEND_METHOD(ReflectionClass, newInstance)
/* Run the constructor if there is one */
if (constructor) {
- zval *params = NULL;
- int i, num_args = 0;
+ zval *params;
+ int num_args;
+ HashTable *named_params;
if (!(constructor->common.fn_flags & ZEND_ACC_PUBLIC)) {
zend_throw_exception_ex(reflection_exception_ptr, 0, "Access to non-public constructor of class %s", ZSTR_VAL(ce->name));
@@ -4685,20 +4659,13 @@ ZEND_METHOD(ReflectionClass, newInstance)
RETURN_NULL();
}
- if (zend_parse_parameters(ZEND_NUM_ARGS(), "*", &params, &num_args) == FAILURE) {
- zval_ptr_dtor(return_value);
- RETURN_THROWS();
- }
-
- for (i = 0; i < num_args; i++) {
- Z_TRY_ADDREF(params[i]);
- }
-
- zend_call_known_instance_method(constructor, Z_OBJ_P(return_value), NULL, num_args, params);
+ ZEND_PARSE_PARAMETERS_START(0, -1)
+ Z_PARAM_VARIADIC_WITH_NAMED(params, num_args, named_params)
+ ZEND_PARSE_PARAMETERS_END();
- for (i = 0; i < num_args; i++) {
- zval_ptr_dtor(&params[i]);
- }
+ zend_call_known_function(
+ constructor, Z_OBJ_P(return_value), Z_OBJCE_P(return_value), NULL,
+ num_args, params, named_params);
if (EG(exception)) {
zend_object_store_ctor_failed(Z_OBJ_P(return_value));
@@ -4734,11 +4701,10 @@ ZEND_METHOD(ReflectionClass, newInstanceWithoutConstructor)
/* {{{ Returns an instance of this class */
ZEND_METHOD(ReflectionClass, newInstanceArgs)
{
- zval *val;
reflection_object *intern;
zend_class_entry *ce, *old_scope;
- int i, argc = 0;
- HashTable *args;
+ int argc = 0;
+ HashTable *args = NULL;
zend_function *constructor;
GET_REFLECTION_OBJECT_PTR(ce);
@@ -4747,8 +4713,8 @@ ZEND_METHOD(ReflectionClass, newInstanceArgs)
RETURN_THROWS();
}
- if (ZEND_NUM_ARGS() > 0) {
- argc = args->nNumOfElements;
+ if (args) {
+ argc = zend_hash_num_elements(args);
}
if (UNEXPECTED(object_init_ex(return_value, ce) != SUCCESS)) {
@@ -4762,31 +4728,14 @@ ZEND_METHOD(ReflectionClass, newInstanceArgs)
/* Run the constructor if there is one */
if (constructor) {
- zval *params = NULL;
-
if (!(constructor->common.fn_flags & ZEND_ACC_PUBLIC)) {
zend_throw_exception_ex(reflection_exception_ptr, 0, "Access to non-public constructor of class %s", ZSTR_VAL(ce->name));
zval_ptr_dtor(return_value);
RETURN_NULL();
}
- if (argc) {
- params = safe_emalloc(sizeof(zval), argc, 0);
- argc = 0;
- ZEND_HASH_FOREACH_VAL(args, val) {
- ZVAL_COPY(&params[argc], val);
- argc++;
- } ZEND_HASH_FOREACH_END();
- }
-
- zend_call_known_instance_method(constructor, Z_OBJ_P(return_value), NULL, argc, params);
-
- if (params) {
- for (i = 0; i < argc; i++) {
- zval_ptr_dtor(&params[i]);
- }
- efree(params);
- }
+ zend_call_known_function(
+ constructor, Z_OBJ_P(return_value), Z_OBJCE_P(return_value), NULL, 0, NULL, args);
if (EG(exception)) {
zend_object_store_ctor_failed(Z_OBJ_P(return_value));
@@ -6273,12 +6222,17 @@ ZEND_METHOD(ReflectionAttribute, getArguments)
RETURN_THROWS();
}
- add_next_index_zval(return_value, &tmp);
+ if (attr->data->args[i].name) {
+ /* We ensured at compile-time that there are no duplicate parameter names. */
+ zend_hash_add_new(Z_ARRVAL_P(return_value), attr->data->args[i].name, &tmp);
+ } else {
+ add_next_index_zval(return_value, &tmp);
+ }
}
}
/* }}} */
-static int call_attribute_constructor(zend_class_entry *ce, zend_object *obj, zval *args, uint32_t argc) /* {{{ */
+static int call_attribute_constructor(zend_class_entry *ce, zend_object *obj, zval *args, uint32_t argc, HashTable *named_params) /* {{{ */
{
zend_function *ctor = ce->constructor;
ZEND_ASSERT(ctor != NULL);
@@ -6288,7 +6242,8 @@ static int call_attribute_constructor(zend_class_entry *ce, zend_object *obj, zv
return FAILURE;
}
- zend_call_known_instance_method(ctor, obj, NULL, argc, args);
+ zend_call_known_function(ctor, obj, obj->ce, NULL, argc, args, named_params);
+
if (EG(exception)) {
zend_object_store_ctor_failed(obj);
return FAILURE;
@@ -6298,7 +6253,8 @@ static int call_attribute_constructor(zend_class_entry *ce, zend_object *obj, zv
}
/* }}} */
-static void attribute_ctor_cleanup(zval *obj, zval *args, uint32_t argc) /* {{{ */
+static void attribute_ctor_cleanup(
+ zval *obj, zval *args, uint32_t argc, HashTable *named_params) /* {{{ */
{
if (obj) {
zval_ptr_dtor(obj);
@@ -6313,6 +6269,10 @@ static void attribute_ctor_cleanup(zval *obj, zval *args, uint32_t argc) /* {{{
efree(args);
}
+
+ if (named_params) {
+ zend_array_destroy(named_params);
+ }
}
/* }}} */
@@ -6327,8 +6287,7 @@ ZEND_METHOD(ReflectionAttribute, newInstance)
zval obj;
zval *args = NULL;
- uint32_t count;
- uint32_t argc = 0;
+ HashTable *named_params = NULL;
if (zend_parse_parameters_none() == FAILURE) {
RETURN_THROWS();
@@ -6385,31 +6344,40 @@ ZEND_METHOD(ReflectionAttribute, newInstance)
RETURN_THROWS();
}
- count = attr->data->argc;
-
- if (count) {
- args = emalloc(count * sizeof(zval));
+ uint32_t argc = 0;
+ if (attr->data->argc) {
+ args = emalloc(attr->data->argc * sizeof(zval));
- for (argc = 0; argc < attr->data->argc; argc++) {
- if (FAILURE == zend_get_attribute_value(&args[argc], attr->data, argc, attr->scope)) {
- attribute_ctor_cleanup(&obj, args, argc);
+ for (uint32_t i = 0; i < attr->data->argc; i++) {
+ zval val;
+ if (FAILURE == zend_get_attribute_value(&val, attr->data, i, attr->scope)) {
+ attribute_ctor_cleanup(&obj, args, i, named_params);
RETURN_THROWS();
}
+ if (attr->data->args[i].name) {
+ if (!named_params) {
+ named_params = zend_new_array(0);
+ }
+ zend_hash_add_new(named_params, attr->data->args[i].name, &val);
+ } else {
+ ZVAL_COPY_VALUE(&args[i], &val);
+ argc++;
+ }
}
}
if (ce->constructor) {
- if (FAILURE == call_attribute_constructor(ce, Z_OBJ(obj), args, argc)) {
- attribute_ctor_cleanup(&obj, args, argc);
+ if (FAILURE == call_attribute_constructor(ce, Z_OBJ(obj), args, argc, named_params)) {
+ attribute_ctor_cleanup(&obj, args, argc, named_params);
RETURN_THROWS();
}
- } else if (argc) {
- attribute_ctor_cleanup(&obj, args, argc);
+ } else if (argc || named_params) {
+ attribute_ctor_cleanup(&obj, args, argc, named_params);
zend_throw_error(NULL, "Attribute class %s does not have a constructor, cannot pass arguments", ZSTR_VAL(ce->name));
RETURN_THROWS();
}
- attribute_ctor_cleanup(NULL, args, argc);
+ attribute_ctor_cleanup(NULL, args, argc, named_params);
RETURN_COPY_VALUE(&obj);
}
diff --git a/ext/spl/php_spl.c b/ext/spl/php_spl.c
index 43effd0c99..d92f774db4 100644
--- a/ext/spl/php_spl.c
+++ b/ext/spl/php_spl.c
@@ -437,7 +437,7 @@ static zend_class_entry *spl_perform_autoload(zend_string *class_name, zend_stri
zval param;
ZVAL_STR(&param, class_name);
- zend_call_known_function(func, alfi->obj, alfi->ce, NULL, 1, &param);
+ zend_call_known_function(func, alfi->obj, alfi->ce, NULL, 1, &param, NULL);
if (EG(exception)) {
break;
}
diff --git a/ext/spl/spl_directory.c b/ext/spl/spl_directory.c
index 45af9c270b..af337899f6 100644
--- a/ext/spl/spl_directory.c
+++ b/ext/spl/spl_directory.c
@@ -1904,6 +1904,7 @@ static int spl_filesystem_file_call(spl_filesystem_object *intern, zend_function
fci.retval = return_value;
fci.param_count = num_args;
fci.params = params;
+ fci.named_params = NULL;
ZVAL_STR(&fci.function_name, func_ptr->common.function_name);
fcic.function_handler = func_ptr;
diff --git a/ext/standard/basic_functions.c b/ext/standard/basic_functions.c
index 6f2ce46c88..9b756d8694 100755
--- a/ext/standard/basic_functions.c
+++ b/ext/standard/basic_functions.c
@@ -1549,7 +1549,7 @@ PHP_FUNCTION(call_user_func)
ZEND_PARSE_PARAMETERS_START(1, -1)
Z_PARAM_FUNC(fci, fci_cache)
- Z_PARAM_VARIADIC('*', fci.params, fci.param_count)
+ Z_PARAM_VARIADIC_WITH_NAMED(fci.params, fci.param_count, fci.named_params)
ZEND_PARSE_PARAMETERS_END();
fci.retval = &retval;
@@ -1567,16 +1567,17 @@ PHP_FUNCTION(call_user_func)
Warning: This function is special-cased by zend_compile.c and so is usually bypassed */
PHP_FUNCTION(call_user_func_array)
{
- zval *params, retval;
+ zval retval;
+ HashTable *params;
zend_fcall_info fci;
zend_fcall_info_cache fci_cache;
ZEND_PARSE_PARAMETERS_START(2, 2)
Z_PARAM_FUNC(fci, fci_cache)
- Z_PARAM_ARRAY(params)
+ Z_PARAM_ARRAY_HT(params)
ZEND_PARSE_PARAMETERS_END();
- zend_fcall_info_args(&fci, params);
+ fci.named_params = params;
fci.retval = &retval;
if (zend_call_function(&fci, &fci_cache) == SUCCESS && Z_TYPE(retval) != IS_UNDEF) {
@@ -1585,8 +1586,6 @@ PHP_FUNCTION(call_user_func_array)
}
ZVAL_COPY_VALUE(return_value, &retval);
}
-
- zend_fcall_info_args_clear(&fci, 1);
}
/* }}} */
diff --git a/ext/xml/xml.c b/ext/xml/xml.c
index 476d77444c..c5d3f71079 100644
--- a/ext/xml/xml.c
+++ b/ext/xml/xml.c
@@ -464,6 +464,7 @@ static void xml_call_handler(xml_parser *parser, zval *handler, zend_function *f
fci.retval = retval;
fci.param_count = argc;
fci.params = argv;
+ fci.named_params = NULL;
result = zend_call_function(&fci, NULL);
if (result == FAILURE) {
diff --git a/ext/xsl/xsltprocessor.c b/ext/xsl/xsltprocessor.c
index 0dc909d9d0..29b3ff5fb3 100644
--- a/ext/xsl/xsltprocessor.c
+++ b/ext/xsl/xsltprocessor.c
@@ -219,6 +219,7 @@ static void xsl_ext_function_php(xmlXPathParserContextPtr ctxt, int nargs, int t
}
fci.size = sizeof(fci);
+ fci.named_params = NULL;
if (fci.param_count > 0) {
fci.params = args;
} else {