diff options
| author | Nikita Popov <nikita.ppv@gmail.com> | 2020-04-06 12:46:52 +0200 |
|---|---|---|
| committer | Nikita Popov <nikita.ppv@gmail.com> | 2020-07-31 15:53:36 +0200 |
| commit | d92229d8c78aac25925284e23aa7903dca9ed005 (patch) | |
| tree | ec01712dbe44a11cb7368492388cb39ab04dac17 /ext | |
| parent | 9a71d47d7334b9e2f83db48498047750c04cd192 (diff) | |
| download | php-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.c | 5 | ||||
| -rw-r--r-- | ext/dom/xpath.c | 1 | ||||
| -rw-r--r-- | ext/ffi/ffi.c | 1 | ||||
| -rw-r--r-- | ext/mysqli/mysqli.c | 1 | ||||
| -rw-r--r-- | ext/opcache/Optimizer/compact_literals.c | 14 | ||||
| -rw-r--r-- | ext/opcache/Optimizer/optimize_func_calls.c | 33 | ||||
| -rw-r--r-- | ext/opcache/Optimizer/zend_call_graph.c | 6 | ||||
| -rw-r--r-- | ext/opcache/Optimizer/zend_call_graph.h | 1 | ||||
| -rw-r--r-- | ext/opcache/Optimizer/zend_inference.c | 14 | ||||
| -rw-r--r-- | ext/opcache/jit/zend_jit.c | 21 | ||||
| -rw-r--r-- | ext/opcache/jit/zend_jit_trace.c | 21 | ||||
| -rw-r--r-- | ext/opcache/jit/zend_jit_vm_helpers.c | 6 | ||||
| -rw-r--r-- | ext/opcache/jit/zend_jit_x86.dasc | 38 | ||||
| -rw-r--r-- | ext/opcache/zend_file_cache.c | 6 | ||||
| -rw-r--r-- | ext/opcache/zend_persist.c | 5 | ||||
| -rw-r--r-- | ext/opcache/zend_persist_calc.c | 5 | ||||
| -rw-r--r-- | ext/pcre/php_pcre.c | 1 | ||||
| -rw-r--r-- | ext/pdo/pdo_dbh.c | 1 | ||||
| -rw-r--r-- | ext/pgsql/pgsql.c | 1 | ||||
| -rw-r--r-- | ext/reflection/php_reflection.c | 180 | ||||
| -rw-r--r-- | ext/spl/php_spl.c | 2 | ||||
| -rw-r--r-- | ext/spl/spl_directory.c | 1 | ||||
| -rwxr-xr-x | ext/standard/basic_functions.c | 11 | ||||
| -rw-r--r-- | ext/xml/xml.c | 1 | ||||
| -rw-r--r-- | ext/xsl/xsltprocessor.c | 1 |
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(©->argv[i]); + if (copy->args[i].name) { + zend_accel_store_interned_string(copy->args[i].name); + } + zend_persist_zval(©->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(), "*", ¶ms, &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", ¶m_array) == FAILURE) { + if (zend_parse_parameters(ZEND_NUM_ARGS(), "h", ¶ms) == 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(¶ms[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(¶ms[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, ¶ms, &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, ¶m_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(¶ms[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(¶ms[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(), "*", ¶ms, &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(¶ms[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(¶ms[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(¶ms[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(¶m, class_name); - zend_call_known_function(func, alfi->obj, alfi->ce, NULL, 1, ¶m); + zend_call_known_function(func, alfi->obj, alfi->ce, NULL, 1, ¶m, 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 { |
