Skip to content

Commit 14460c4

Browse files
committed
fix variadics with named and nesting
1 parent 0e2fcce commit 14460c4

File tree

4 files changed

+81
-65
lines changed

4 files changed

+81
-65
lines changed
Lines changed: 28 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,28 @@
1+
--TEST--
2+
Named variadics nested.
3+
--FILE--
4+
<?php
5+
function variadic($one, ...$two) {
6+
return [
7+
func_num_args(),
8+
func_get_args(),
9+
[$one, $two]];
10+
}
11+
12+
$zend = variadic(one: 1, two: [2], three: 3, four: 4);
13+
14+
$variadic = variadic(?, three: 3);
15+
16+
$variadic = $variadic(?, four: 4);
17+
18+
$partial = $variadic(one: 1, two: [2]);
19+
20+
if ($partial !== $zend) {
21+
echo "FAIL\n";
22+
var_dump($partial, $zend);
23+
} else {
24+
echo "OK\n";
25+
}
26+
?>
27+
--EXPECT--
28+
OK

Zend/zend_partial.c

Lines changed: 45 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -35,6 +35,7 @@ typedef struct _zend_partial_applied {
3535
zval This;
3636
uint32_t argc;
3737
zval *argv;
38+
zend_array *named;
3839
} zend_partial_applied;
3940

4041
typedef struct _zend_partial {
@@ -379,6 +380,10 @@ static void zend_partial_free(zend_object *object) {
379380

380381
efree_size(partial->frame.argv, sizeof(zval) * partial->frame.argc);
381382

383+
if (partial->frame.named) {
384+
zend_array_release(partial->frame.named);
385+
}
386+
382387
zend_arg_info *info = partial->prototype.common.arg_info;
383388

384389
if (partial->func.common.fn_flags & ZEND_ACC_HAS_RETURN_TYPE) {
@@ -480,11 +485,30 @@ ZEND_METHOD(Partial, __invoke)
480485
arg++;
481486
}
482487

483-
fci.named_params = named_args;
488+
if (partial->frame.named) {
489+
if (!named_args) {
490+
named_args =
491+
partial->frame.named;
492+
GC_ADDREF(named_args);
493+
} else {
494+
named_args =
495+
zend_array_dup(named_args);
496+
zend_hash_merge(
497+
named_args,
498+
partial->frame.named, NULL, true);
499+
}
500+
fci.named_params = named_args;
501+
} else {
502+
fci.named_params = named_args;
503+
}
484504

485505
if (!EG(exception)) {
486506
zend_call_function(&fci, &fcc);
487507
}
508+
509+
if (partial->frame.named) {
510+
zend_array_release(named_args);
511+
}
488512

489513
if ((fcc.function_handler->common.fn_flags & ZEND_ACC_CTOR) ||
490514
(fcc.function_handler->type == ZEND_INTERNAL_FUNCTION &&
@@ -547,6 +571,20 @@ void zend_partial_apply(zval *res, zend_execute_data *call) {
547571
}
548572
arg++;
549573
}
574+
575+
if ((ZEND_CALL_INFO(call) & ZEND_CALL_HAS_EXTRA_NAMED_PARAMS)) {
576+
if (frame->named) {
577+
partial->frame.named = zend_array_dup(frame->named);
578+
579+
zend_hash_merge(
580+
partial->frame.named,
581+
call->extra_named_params, NULL, 1);
582+
} else {
583+
partial->frame.named =
584+
call->extra_named_params;
585+
GC_ADDREF(call->extra_named_params);
586+
}
587+
}
550588
} else {
551589
function = call->func;
552590

@@ -555,6 +593,12 @@ void zend_partial_apply(zval *res, zend_execute_data *call) {
555593

556594
memcpy(partial->frame.argv,
557595
ZEND_CALL_ARG(call, 1), partial->frame.argc * sizeof(zval));
596+
597+
if ((ZEND_CALL_INFO(call) & ZEND_CALL_HAS_EXTRA_NAMED_PARAMS)) {
598+
partial->frame.named =
599+
call->extra_named_params;
600+
GC_ADDREF(call->extra_named_params);
601+
}
558602
}
559603

560604
ZVAL_COPY(&partial->frame.This, &call->This);

Zend/zend_vm_def.h

Lines changed: 4 additions & 32 deletions
Original file line numberDiff line numberDiff line change
@@ -8790,38 +8790,6 @@ ZEND_VM_HANDLER(203, ZEND_PARTIAL_APPLY, ANY, ANY)
87908790
{
87918791
USE_OPLINE
87928792
zend_execute_data *call = EX(call);
8793-
8794-
if (UNEXPECTED((ZEND_CALL_INFO(call) & ZEND_CALL_HAS_EXTRA_NAMED_PARAMS) &&
8795-
(NULL != call->extra_named_params))) {
8796-
SAVE_OPLINE();
8797-
8798-
zend_string *name;
8799-
zval *arg;
8800-
8801-
zend_vm_stack_extend_call_frame(
8802-
&call,
8803-
ZEND_CALL_NUM_ARGS(call),
8804-
zend_array_count(call->extra_named_params));
8805-
8806-
ZEND_HASH_FOREACH_STR_KEY_VAL(call->extra_named_params, name, arg) {
8807-
void *cache_slot[2] = {NULL, NULL};
8808-
8809-
uint32_t num = zend_get_arg_offset_by_name(
8810-
call->func, name, cache_slot);
8811-
8812-
if (num == (uint32_t)-1) {
8813-
zend_throw_error(NULL,
8814-
"Unknown named parameter $%s", ZSTR_VAL(name));
8815-
break;
8816-
}
8817-
8818-
ZVAL_COPY_VALUE(ZEND_CALL_ARG(call, num+1), arg);
8819-
ZEND_CALL_NUM_ARGS(call)++;
8820-
} ZEND_HASH_FOREACH_END();
8821-
8822-
zend_array_release(call->extra_named_params);
8823-
}
8824-
88258793
zval *result;
88268794

88278795
if ((call->func->type == ZEND_INTERNAL_FUNCTION &&
@@ -8837,6 +8805,10 @@ ZEND_VM_HANDLER(203, ZEND_PARTIAL_APPLY, ANY, ANY)
88378805
}
88388806

88398807
zend_partial_apply(result, call);
8808+
8809+
if ((ZEND_CALL_INFO(call) & ZEND_CALL_HAS_EXTRA_NAMED_PARAMS)) {
8810+
zend_array_release(call->extra_named_params);
8811+
}
88408812

88418813
if (ZEND_CALL_INFO(call) & ZEND_CALL_RELEASE_THIS) {
88428814
OBJ_RELEASE(Z_OBJ(call->This));

Zend/zend_vm_execute.h

Lines changed: 4 additions & 32 deletions
Original file line numberDiff line numberDiff line change
@@ -3498,38 +3498,6 @@ static ZEND_OPCODE_HANDLER_RET ZEND_FASTCALL ZEND_PARTIAL_APPLY_SPEC_HANDLER(ZEN
34983498
{
34993499
USE_OPLINE
35003500
zend_execute_data *call = EX(call);
3501-
3502-
if (UNEXPECTED((ZEND_CALL_INFO(call) & ZEND_CALL_HAS_EXTRA_NAMED_PARAMS) &&
3503-
(NULL != call->extra_named_params))) {
3504-
SAVE_OPLINE();
3505-
3506-
zend_string *name;
3507-
zval *arg;
3508-
3509-
zend_vm_stack_extend_call_frame(
3510-
&call,
3511-
ZEND_CALL_NUM_ARGS(call),
3512-
zend_array_count(call->extra_named_params));
3513-
3514-
ZEND_HASH_FOREACH_STR_KEY_VAL(call->extra_named_params, name, arg) {
3515-
void *cache_slot[2] = {NULL, NULL};
3516-
3517-
uint32_t num = zend_get_arg_offset_by_name(
3518-
call->func, name, cache_slot);
3519-
3520-
if (num == (uint32_t)-1) {
3521-
zend_throw_error(NULL,
3522-
"Unknown named parameter $%s", ZSTR_VAL(name));
3523-
break;
3524-
}
3525-
3526-
ZVAL_COPY_VALUE(ZEND_CALL_ARG(call, num+1), arg);
3527-
ZEND_CALL_NUM_ARGS(call)++;
3528-
} ZEND_HASH_FOREACH_END();
3529-
3530-
zend_array_release(call->extra_named_params);
3531-
}
3532-
35333501
zval *result;
35343502

35353503
if ((call->func->type == ZEND_INTERNAL_FUNCTION &&
@@ -3546,6 +3514,10 @@ static ZEND_OPCODE_HANDLER_RET ZEND_FASTCALL ZEND_PARTIAL_APPLY_SPEC_HANDLER(ZEN
35463514

35473515
zend_partial_apply(result, call);
35483516

3517+
if ((ZEND_CALL_INFO(call) & ZEND_CALL_HAS_EXTRA_NAMED_PARAMS)) {
3518+
zend_array_release(call->extra_named_params);
3519+
}
3520+
35493521
if (ZEND_CALL_INFO(call) & ZEND_CALL_RELEASE_THIS) {
35503522
OBJ_RELEASE(Z_OBJ(call->This));
35513523
} else if (ZEND_CALL_INFO(call) & ZEND_CALL_CLOSURE) {

0 commit comments

Comments
 (0)