Skip to content

Commit 177af75

Browse files
committed
Support named params for internal functions
1 parent 52490bb commit 177af75

File tree

6 files changed

+120
-14
lines changed

6 files changed

+120
-14
lines changed

Zend/tests/named_params/internal.phpt

Lines changed: 6 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -6,7 +6,6 @@ Named params on internal functions
66
var_dump(array_slice(arg => [1, 2, 3, 4, 5], offset => 2, length => 2));
77
var_dump(array_slice(length => 2, offset => 2, arg => [1, 2, 3, 4, 5]));
88

9-
// These don't work correctly!
109
var_dump(array_slice(arg => ['a' => 0, 'b' => 1], offset => 1, preserve_keys => true));
1110
var_dump(array_slice(['a' => 0, 'b' => 1], preserve_keys => true, offset => 1));
1211

@@ -24,7 +23,11 @@ array(2) {
2423
[1]=>
2524
int(4)
2625
}
27-
array(0) {
26+
array(1) {
27+
["b"]=>
28+
int(1)
2829
}
29-
array(0) {
30+
array(1) {
31+
["b"]=>
32+
int(1)
3033
}

Zend/zend_execute.c

Lines changed: 41 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -711,7 +711,7 @@ ZEND_API ZEND_COLD void zend_verify_arg_error(
711711

712712
zend_string_release(need_msg);
713713
} else {
714-
zend_missing_arg_error(ptr);
714+
zend_missing_arg_error(ptr, arg_num);
715715
}
716716
}
717717

@@ -1145,10 +1145,15 @@ static ZEND_COLD void zend_internal_call_arginfo_violation(zend_function *fbc)
11451145
}
11461146
#endif
11471147

1148-
ZEND_API ZEND_COLD void ZEND_FASTCALL zend_missing_arg_error(zend_execute_data *execute_data)
1148+
ZEND_API ZEND_COLD void ZEND_FASTCALL zend_missing_arg_error(zend_execute_data *execute_data, uint32_t arg_num)
11491149
{
11501150
zend_execute_data *ptr = EX(prev_execute_data);
11511151

1152+
if (arg_num < EX(func)->common.required_num_args) {
1153+
zend_argument_error(zend_ce_argument_count_error, arg_num, "not passed");
1154+
return;
1155+
}
1156+
11521157
if (ptr && ptr->func && ZEND_USER_CODE(ptr->func->common.type)) {
11531158
zend_throw_error(zend_ce_argument_count_error, "Too few arguments to function %s%s%s(), %d passed in %s on line %d and %s %d expected",
11541159
EX(func)->common.scope ? ZSTR_VAL(EX(func)->common.scope->name) : "",
@@ -4347,6 +4352,40 @@ static zval * ZEND_FASTCALL zend_handle_named_arg(
43474352
return arg;
43484353
}
43494354

4355+
static int zend_handle_icall_undef_args(zend_execute_data *call) {
4356+
zend_function *fbc = call->func;
4357+
uint32_t num_args = ZEND_CALL_NUM_ARGS(call);
4358+
for (uint32_t i = 0; i < num_args; i++) {
4359+
zval *arg = ZEND_CALL_VAR_NUM(call, i);
4360+
if (!Z_ISUNDEF_P(arg)) {
4361+
continue;
4362+
}
4363+
4364+
zend_internal_arg_info *arg_info = &fbc->internal_function.arg_info[i];
4365+
if (i < fbc->common.required_num_args) {
4366+
zend_argument_error(zend_ce_argument_count_error, i + 1, "not passed");
4367+
return FAILURE;
4368+
}
4369+
4370+
zval default_value;
4371+
if (zend_get_default_from_internal_arg_info(&default_value, arg_info) == FAILURE) {
4372+
zend_argument_error(zend_ce_argument_count_error, i + 1,
4373+
"must be passed explicitly, because the default value is not known");
4374+
return FAILURE;
4375+
}
4376+
4377+
if (Z_TYPE(default_value) == IS_CONSTANT_AST) {
4378+
if (zval_update_constant_ex(&default_value, fbc->common.scope) == FAILURE) {
4379+
return FAILURE;
4380+
}
4381+
}
4382+
4383+
ZVAL_COPY_VALUE(arg, &default_value);
4384+
}
4385+
4386+
return SUCCESS;
4387+
}
4388+
43504389
#if defined(ZEND_VM_IP_GLOBAL_REG) && ((ZEND_VM_KIND == ZEND_VM_KIND_CALL) || (ZEND_VM_KIND == ZEND_VM_KIND_HYBRID))
43514390
/* Special versions of functions that sets EX(opline) before calling zend_vm_stack_extend() */
43524391
static zend_always_inline zend_execute_data *_zend_vm_stack_push_call_frame_ex(uint32_t used_stack, uint32_t call_info, zend_function *func, uint32_t num_args, void *object_or_called_scope) /* {{{ */

Zend/zend_execute.h

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -52,7 +52,7 @@ ZEND_API int zend_eval_stringl_ex(const char *str, size_t str_len, zval *retval_
5252
/* export zend_pass_function to allow comparisons against it */
5353
extern ZEND_API const zend_internal_function zend_pass_function;
5454

55-
ZEND_API ZEND_COLD void ZEND_FASTCALL zend_missing_arg_error(zend_execute_data *execute_data);
55+
ZEND_API ZEND_COLD void ZEND_FASTCALL zend_missing_arg_error(zend_execute_data *execute_data, uint32_t arg_num);
5656

5757
ZEND_API zend_bool ZEND_FASTCALL zend_verify_ref_assignable_zval(zend_reference *ref, zval *zv, zend_bool strict);
5858
ZEND_API zend_bool ZEND_FASTCALL zend_verify_prop_assignable_by_ref(zend_property_info *prop_info, zval *orig_val, zend_bool strict);

Zend/zend_vm_def.h

Lines changed: 25 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -3870,6 +3870,12 @@ ZEND_VM_HOT_HANDLER(129, ZEND_DO_ICALL, ANY, ANY, SPEC(RETVAL))
38703870
ret = RETURN_VALUE_USED(opline) ? EX_VAR(opline->result.var) : &retval;
38713871
ZVAL_NULL(ret);
38723872

3873+
if (UNEXPECTED(ZEND_CALL_INFO(call) & ZEND_CALL_MAY_HAVE_UNDEF)) {
3874+
if (zend_handle_icall_undef_args(call) == FAILURE) {
3875+
ZEND_VM_C_GOTO(do_icall_cleanup);
3876+
}
3877+
}
3878+
38733879
fbc->internal_function.handler(call, ret);
38743880

38753881
#if ZEND_DEBUG
@@ -3884,6 +3890,7 @@ ZEND_VM_HOT_HANDLER(129, ZEND_DO_ICALL, ANY, ANY, SPEC(RETVAL))
38843890
}
38853891
#endif
38863892

3893+
ZEND_VM_C_LABEL(do_icall_cleanup):
38873894
EG(current_execute_data) = execute_data;
38883895
zend_vm_stack_free_args(call);
38893896
zend_vm_stack_free_call_frame(call);
@@ -3972,6 +3979,13 @@ ZEND_VM_HOT_HANDLER(131, ZEND_DO_FCALL_BY_NAME, ANY, ANY, SPEC(RETVAL))
39723979
ret = RETURN_VALUE_USED(opline) ? EX_VAR(opline->result.var) : &retval;
39733980
ZVAL_NULL(ret);
39743981

3982+
if (UNEXPECTED(ZEND_CALL_INFO(call) & ZEND_CALL_MAY_HAVE_UNDEF)) {
3983+
if (zend_handle_icall_undef_args(call) == FAILURE) {
3984+
EG(current_execute_data) = execute_data;
3985+
ZEND_VM_C_GOTO(fcall_by_name_end);
3986+
}
3987+
}
3988+
39753989
fbc->internal_function.handler(call, ret);
39763990

39773991
#if ZEND_DEBUG
@@ -4061,6 +4075,13 @@ ZEND_VM_HOT_HANDLER(60, ZEND_DO_FCALL, ANY, ANY, SPEC(RETVAL))
40614075
ret = RETURN_VALUE_USED(opline) ? EX_VAR(opline->result.var) : &retval;
40624076
ZVAL_NULL(ret);
40634077

4078+
if (UNEXPECTED(ZEND_CALL_INFO(call) & ZEND_CALL_MAY_HAVE_UNDEF)) {
4079+
if (zend_handle_icall_undef_args(call) == FAILURE) {
4080+
EG(current_execute_data) = execute_data;
4081+
ZEND_VM_C_GOTO(fcall_end);
4082+
}
4083+
}
4084+
40644085
if (!zend_execute_internal) {
40654086
/* saves one function call if zend_execute_internal is not used */
40664087
fbc->internal_function.handler(call, ret);
@@ -5170,14 +5191,14 @@ ZEND_VM_HANDLER(120, ZEND_SEND_USER, CONST|TMP|VAR|CV, NUM)
51705191
ZEND_VM_NEXT_OPCODE_CHECK_EXCEPTION();
51715192
}
51725193

5173-
ZEND_VM_COLD_HELPER(zend_missing_arg_helper, ANY, ANY)
5194+
ZEND_VM_COLD_HELPER(zend_missing_arg_helper, ANY, ANY, uint32_t arg_num)
51745195
{
51755196
#ifdef ZEND_VM_IP_GLOBAL_REG
51765197
USE_OPLINE
51775198

51785199
SAVE_OPLINE();
51795200
#endif
5180-
zend_missing_arg_error(execute_data);
5201+
zend_missing_arg_error(execute_data, arg_num);
51815202
HANDLE_EXCEPTION();
51825203
}
51835204

@@ -5201,7 +5222,7 @@ ZEND_VM_HOT_HANDLER(63, ZEND_RECV, NUM, UNUSED, CACHE_SLOT)
52015222

52025223
param = EX_VAR(opline->result.var);
52035224
if (UNEXPECTED(arg_num > EX_NUM_ARGS() || Z_ISUNDEF_P(param))) {
5204-
ZEND_VM_DISPATCH_TO_HELPER(zend_missing_arg_helper);
5225+
ZEND_VM_DISPATCH_TO_HELPER(zend_missing_arg_helper, arg_num, arg_num);
52055226
}
52065227

52075228
if (UNEXPECTED(!(opline->op2.num & (1u << Z_TYPE_P(param))))) {
@@ -5217,7 +5238,7 @@ ZEND_VM_HOT_TYPE_SPEC_HANDLER(ZEND_RECV, op->op2.num == MAY_BE_ANY, ZEND_RECV_NO
52175238
uint32_t arg_num = opline->op1.num;
52185239

52195240
if (UNEXPECTED(arg_num > EX_NUM_ARGS())) {
5220-
ZEND_VM_DISPATCH_TO_HELPER(zend_missing_arg_helper);
5241+
ZEND_VM_DISPATCH_TO_HELPER(zend_missing_arg_helper, arg_num, arg_num);
52215242
}
52225243

52235244
ZEND_VM_NEXT_OPCODE();

Zend/zend_vm_execute.h

Lines changed: 46 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -1233,6 +1233,12 @@ static ZEND_VM_HOT ZEND_OPCODE_HANDLER_RET ZEND_FASTCALL ZEND_DO_ICALL_SPEC_RETV
12331233
ret = 0 ? EX_VAR(opline->result.var) : &retval;
12341234
ZVAL_NULL(ret);
12351235

1236+
if (UNEXPECTED(ZEND_CALL_INFO(call) & ZEND_CALL_MAY_HAVE_UNDEF)) {
1237+
if (zend_handle_icall_undef_args(call) == FAILURE) {
1238+
goto do_icall_cleanup;
1239+
}
1240+
}
1241+
12361242
fbc->internal_function.handler(call, ret);
12371243

12381244
#if ZEND_DEBUG
@@ -1247,6 +1253,7 @@ static ZEND_VM_HOT ZEND_OPCODE_HANDLER_RET ZEND_FASTCALL ZEND_DO_ICALL_SPEC_RETV
12471253
}
12481254
#endif
12491255

1256+
do_icall_cleanup:
12501257
EG(current_execute_data) = execute_data;
12511258
zend_vm_stack_free_args(call);
12521259
zend_vm_stack_free_call_frame(call);
@@ -1285,6 +1292,12 @@ static ZEND_VM_HOT ZEND_OPCODE_HANDLER_RET ZEND_FASTCALL ZEND_DO_ICALL_SPEC_RETV
12851292
ret = 1 ? EX_VAR(opline->result.var) : &retval;
12861293
ZVAL_NULL(ret);
12871294

1295+
if (UNEXPECTED(ZEND_CALL_INFO(call) & ZEND_CALL_MAY_HAVE_UNDEF)) {
1296+
if (zend_handle_icall_undef_args(call) == FAILURE) {
1297+
goto do_icall_cleanup;
1298+
}
1299+
}
1300+
12881301
fbc->internal_function.handler(call, ret);
12891302

12901303
#if ZEND_DEBUG
@@ -1299,6 +1312,7 @@ static ZEND_VM_HOT ZEND_OPCODE_HANDLER_RET ZEND_FASTCALL ZEND_DO_ICALL_SPEC_RETV
12991312
}
13001313
#endif
13011314

1315+
do_icall_cleanup:
13021316
EG(current_execute_data) = execute_data;
13031317
zend_vm_stack_free_args(call);
13041318
zend_vm_stack_free_call_frame(call);
@@ -1410,6 +1424,13 @@ static ZEND_VM_HOT ZEND_OPCODE_HANDLER_RET ZEND_FASTCALL ZEND_DO_FCALL_BY_NAME_S
14101424
ret = 0 ? EX_VAR(opline->result.var) : &retval;
14111425
ZVAL_NULL(ret);
14121426

1427+
if (UNEXPECTED(ZEND_CALL_INFO(call) & ZEND_CALL_MAY_HAVE_UNDEF)) {
1428+
if (zend_handle_icall_undef_args(call) == FAILURE) {
1429+
EG(current_execute_data) = execute_data;
1430+
goto fcall_by_name_end;
1431+
}
1432+
}
1433+
14131434
fbc->internal_function.handler(call, ret);
14141435

14151436
#if ZEND_DEBUG
@@ -1491,6 +1512,13 @@ static ZEND_VM_HOT ZEND_OPCODE_HANDLER_RET ZEND_FASTCALL ZEND_DO_FCALL_BY_NAME_S
14911512
ret = 1 ? EX_VAR(opline->result.var) : &retval;
14921513
ZVAL_NULL(ret);
14931514

1515+
if (UNEXPECTED(ZEND_CALL_INFO(call) & ZEND_CALL_MAY_HAVE_UNDEF)) {
1516+
if (zend_handle_icall_undef_args(call) == FAILURE) {
1517+
EG(current_execute_data) = execute_data;
1518+
goto fcall_by_name_end;
1519+
}
1520+
}
1521+
14941522
fbc->internal_function.handler(call, ret);
14951523

14961524
#if ZEND_DEBUG
@@ -1580,6 +1608,13 @@ static ZEND_VM_HOT ZEND_OPCODE_HANDLER_RET ZEND_FASTCALL ZEND_DO_FCALL_SPEC_RETV
15801608
ret = 0 ? EX_VAR(opline->result.var) : &retval;
15811609
ZVAL_NULL(ret);
15821610

1611+
if (UNEXPECTED(ZEND_CALL_INFO(call) & ZEND_CALL_MAY_HAVE_UNDEF)) {
1612+
if (zend_handle_icall_undef_args(call) == FAILURE) {
1613+
EG(current_execute_data) = execute_data;
1614+
goto fcall_end;
1615+
}
1616+
}
1617+
15831618
if (!zend_execute_internal) {
15841619
/* saves one function call if zend_execute_internal is not used */
15851620
fbc->internal_function.handler(call, ret);
@@ -1678,6 +1713,13 @@ static ZEND_VM_HOT ZEND_OPCODE_HANDLER_RET ZEND_FASTCALL ZEND_DO_FCALL_SPEC_RETV
16781713
ret = 1 ? EX_VAR(opline->result.var) : &retval;
16791714
ZVAL_NULL(ret);
16801715

1716+
if (UNEXPECTED(ZEND_CALL_INFO(call) & ZEND_CALL_MAY_HAVE_UNDEF)) {
1717+
if (zend_handle_icall_undef_args(call) == FAILURE) {
1718+
EG(current_execute_data) = execute_data;
1719+
goto fcall_end;
1720+
}
1721+
}
1722+
16811723
if (!zend_execute_internal) {
16821724
/* saves one function call if zend_execute_internal is not used */
16831725
fbc->internal_function.handler(call, ret);
@@ -2064,14 +2106,14 @@ static ZEND_OPCODE_HANDLER_RET ZEND_FASTCALL ZEND_SEND_ARRAY_SPEC_HANDLER(ZEND_O
20642106
ZEND_VM_NEXT_OPCODE_CHECK_EXCEPTION();
20652107
}
20662108

2067-
static zend_never_inline ZEND_COLD ZEND_OPCODE_HANDLER_RET ZEND_FASTCALL zend_missing_arg_helper_SPEC(ZEND_OPCODE_HANDLER_ARGS)
2109+
static zend_never_inline ZEND_COLD ZEND_OPCODE_HANDLER_RET ZEND_FASTCALL zend_missing_arg_helper_SPEC(uint32_t arg_num ZEND_OPCODE_HANDLER_ARGS_DC)
20682110
{
20692111
#ifdef ZEND_VM_IP_GLOBAL_REG
20702112
USE_OPLINE
20712113

20722114
SAVE_OPLINE();
20732115
#endif
2074-
zend_missing_arg_error(execute_data);
2116+
zend_missing_arg_error(execute_data, arg_num);
20752117
HANDLE_EXCEPTION();
20762118
}
20772119

@@ -2093,7 +2135,7 @@ static ZEND_VM_HOT ZEND_OPCODE_HANDLER_RET ZEND_FASTCALL ZEND_RECV_NOTYPE_SPEC_H
20932135
uint32_t arg_num = opline->op1.num;
20942136

20952137
if (UNEXPECTED(arg_num > EX_NUM_ARGS())) {
2096-
ZEND_VM_TAIL_CALL(zend_missing_arg_helper_SPEC(ZEND_OPCODE_HANDLER_ARGS_PASSTHRU));
2138+
ZEND_VM_TAIL_CALL(zend_missing_arg_helper_SPEC(arg_num ZEND_OPCODE_HANDLER_ARGS_PASSTHRU_CC));
20972139
}
20982140

20992141
ZEND_VM_NEXT_OPCODE();
@@ -3103,7 +3145,7 @@ static ZEND_VM_HOT ZEND_OPCODE_HANDLER_RET ZEND_FASTCALL ZEND_RECV_SPEC_UNUSED_H
31033145

31043146
param = EX_VAR(opline->result.var);
31053147
if (UNEXPECTED(arg_num > EX_NUM_ARGS() || Z_ISUNDEF_P(param))) {
3106-
ZEND_VM_TAIL_CALL(zend_missing_arg_helper_SPEC(ZEND_OPCODE_HANDLER_ARGS_PASSTHRU));
3148+
ZEND_VM_TAIL_CALL(zend_missing_arg_helper_SPEC(arg_num ZEND_OPCODE_HANDLER_ARGS_PASSTHRU_CC));
31073149
}
31083150

31093151
if (UNEXPECTED(!(opline->op2.num & (1u << Z_TYPE_P(param))))) {

ext/opcache/jit/zend_jit_x86.dasc

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -10064,6 +10064,7 @@ static int zend_jit_recv(dasm_State **Dst, const zend_op *opline, const zend_op_
1006410064
|1:
1006510065
| SAVE_VALID_OPLINE opline, r0
1006610066
| mov FCARG1a, FP
10067+
| mov FCARG2d, arg_num
1006710068
| EXT_CALL zend_missing_arg_error, r0
1006810069
| jmp ->exception_handler
1006910070
|.code

0 commit comments

Comments
 (0)