Skip to content

Commit 6913ec3

Browse files
committed
Use RW fetch for argument unpacking
Argument unpacking may need to create references inside the array that is being unpacked. However, it currently can only do this if a plain variable is unpacked, not for any nested accesses, because the value is fetched for read. Resolve this by fetching the operands for RW.
1 parent 8413df5 commit 6913ec3

File tree

4 files changed

+41
-3
lines changed

4 files changed

+41
-3
lines changed
Lines changed: 22 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,22 @@
1+
--TEST--
2+
By-ref unpacking of a nested access
3+
--FILE--
4+
<?php
5+
6+
function inc(&$var) {
7+
$var++;
8+
}
9+
10+
$ary = [[1]];
11+
inc(...$ary[0]);
12+
var_dump($ary);
13+
14+
?>
15+
--EXPECT--
16+
array(1) {
17+
[0]=>
18+
array(1) {
19+
[0]=>
20+
int(2)
21+
}
22+
}

Zend/zend_compile.c

Lines changed: 9 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -2963,7 +2963,15 @@ uint32_t zend_compile_args(zend_ast *ast, zend_function *fbc) /* {{{ */
29632963
uses_arg_unpack = 1;
29642964
fbc = NULL;
29652965

2966-
zend_compile_expr(&arg_node, arg->child[0]);
2966+
/* Unpacking may need to create interior references in the unpacked array,
2967+
* but apart from that does not have any other reference semantics: It should
2968+
* generate a notice if the variable does not exist and it should not convert
2969+
* the variable itself into a reference. As such, use an RW fetch. */
2970+
if (zend_is_variable(arg->child[0])) {
2971+
zend_compile_var(&arg_node, arg->child[0], BP_VAR_RW, 0);
2972+
} else {
2973+
zend_compile_expr(&arg_node, arg->child[0]);
2974+
}
29672975
opline = zend_emit_op(NULL, ZEND_SEND_UNPACK, &arg_node, NULL);
29682976
opline->op2.num = arg_count;
29692977
opline->result.var = (uint32_t)(zend_intptr_t)ZEND_CALL_ARG(NULL, arg_count);

Zend/zend_vm_def.h

Lines changed: 5 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -4920,7 +4920,11 @@ ZEND_VM_HANDLER(165, ZEND_SEND_UNPACK, ANY, ANY)
49204920
int arg_num;
49214921

49224922
SAVE_OPLINE();
4923-
args = GET_OP1_ZVAL_PTR_UNDEF(BP_VAR_R);
4923+
if (OP1_TYPE & (IS_VAR|IS_CV)) {
4924+
args = GET_OP1_ZVAL_PTR_PTR_UNDEF(BP_VAR_RW);
4925+
} else {
4926+
args = GET_OP1_ZVAL_PTR_UNDEF(BP_VAR_R);
4927+
}
49244928
arg_num = ZEND_CALL_NUM_ARGS(EX(call)) + 1;
49254929

49264930
ZEND_VM_C_LABEL(send_again):

Zend/zend_vm_execute.h

Lines changed: 5 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1862,7 +1862,11 @@ static ZEND_OPCODE_HANDLER_RET ZEND_FASTCALL ZEND_SEND_UNPACK_SPEC_HANDLER(ZEND_
18621862
int arg_num;
18631863

18641864
SAVE_OPLINE();
1865-
args = get_zval_ptr_undef(opline->op1_type, opline->op1, &free_op1, BP_VAR_R);
1865+
if (opline->op1_type & (IS_VAR|IS_CV)) {
1866+
args = get_zval_ptr_ptr_undef(opline->op1_type, opline->op1, &free_op1, BP_VAR_RW);
1867+
} else {
1868+
args = get_zval_ptr_undef(opline->op1_type, opline->op1, &free_op1, BP_VAR_R);
1869+
}
18661870
arg_num = ZEND_CALL_NUM_ARGS(EX(call)) + 1;
18671871

18681872
send_again:

0 commit comments

Comments
 (0)