summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorNikita Popov <nikita.ppv@gmail.com>2019-07-30 10:05:12 +0200
committerNikita Popov <nikita.ppv@gmail.com>2019-07-30 10:07:08 +0200
commit6913ec3282149914e999d91b056fe1cc68d15ed7 (patch)
tree57a5c6a8d08b02303d676ccf4b381a0f0f72b35e
parent8413df5e55735b8d40b5baeea4d2b68327103626 (diff)
downloadphp-git-6913ec3282149914e999d91b056fe1cc68d15ed7.tar.gz
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.
-rw-r--r--Zend/tests/arg_unpack/nested_by_ref.phpt22
-rw-r--r--Zend/zend_compile.c10
-rw-r--r--Zend/zend_vm_def.h6
-rw-r--r--Zend/zend_vm_execute.h6
4 files changed, 41 insertions, 3 deletions
diff --git a/Zend/tests/arg_unpack/nested_by_ref.phpt b/Zend/tests/arg_unpack/nested_by_ref.phpt
new file mode 100644
index 0000000000..599c181c2c
--- /dev/null
+++ b/Zend/tests/arg_unpack/nested_by_ref.phpt
@@ -0,0 +1,22 @@
+--TEST--
+By-ref unpacking of a nested access
+--FILE--
+<?php
+
+function inc(&$var) {
+ $var++;
+}
+
+$ary = [[1]];
+inc(...$ary[0]);
+var_dump($ary);
+
+?>
+--EXPECT--
+array(1) {
+ [0]=>
+ array(1) {
+ [0]=>
+ int(2)
+ }
+}
diff --git a/Zend/zend_compile.c b/Zend/zend_compile.c
index 10a6b294f7..dfa845cd73 100644
--- a/Zend/zend_compile.c
+++ b/Zend/zend_compile.c
@@ -2963,7 +2963,15 @@ uint32_t zend_compile_args(zend_ast *ast, zend_function *fbc) /* {{{ */
uses_arg_unpack = 1;
fbc = NULL;
- zend_compile_expr(&arg_node, arg->child[0]);
+ /* Unpacking may need to create interior references in the unpacked array,
+ * but apart from that does not have any other reference semantics: It should
+ * generate a notice if the variable does not exist and it should not convert
+ * the variable itself into a reference. As such, use an RW fetch. */
+ if (zend_is_variable(arg->child[0])) {
+ zend_compile_var(&arg_node, arg->child[0], BP_VAR_RW, 0);
+ } else {
+ zend_compile_expr(&arg_node, arg->child[0]);
+ }
opline = zend_emit_op(NULL, ZEND_SEND_UNPACK, &arg_node, NULL);
opline->op2.num = arg_count;
opline->result.var = (uint32_t)(zend_intptr_t)ZEND_CALL_ARG(NULL, arg_count);
diff --git a/Zend/zend_vm_def.h b/Zend/zend_vm_def.h
index 930e1cc746..80c8ff68bc 100644
--- a/Zend/zend_vm_def.h
+++ b/Zend/zend_vm_def.h
@@ -4920,7 +4920,11 @@ ZEND_VM_HANDLER(165, ZEND_SEND_UNPACK, ANY, ANY)
int arg_num;
SAVE_OPLINE();
- args = GET_OP1_ZVAL_PTR_UNDEF(BP_VAR_R);
+ if (OP1_TYPE & (IS_VAR|IS_CV)) {
+ args = GET_OP1_ZVAL_PTR_PTR_UNDEF(BP_VAR_RW);
+ } else {
+ args = GET_OP1_ZVAL_PTR_UNDEF(BP_VAR_R);
+ }
arg_num = ZEND_CALL_NUM_ARGS(EX(call)) + 1;
ZEND_VM_C_LABEL(send_again):
diff --git a/Zend/zend_vm_execute.h b/Zend/zend_vm_execute.h
index 6dfa6f2899..23d41dbdbd 100644
--- a/Zend/zend_vm_execute.h
+++ b/Zend/zend_vm_execute.h
@@ -1862,7 +1862,11 @@ static ZEND_OPCODE_HANDLER_RET ZEND_FASTCALL ZEND_SEND_UNPACK_SPEC_HANDLER(ZEND_
int arg_num;
SAVE_OPLINE();
- args = get_zval_ptr_undef(opline->op1_type, opline->op1, &free_op1, BP_VAR_R);
+ if (opline->op1_type & (IS_VAR|IS_CV)) {
+ args = get_zval_ptr_ptr_undef(opline->op1_type, opline->op1, &free_op1, BP_VAR_RW);
+ } else {
+ args = get_zval_ptr_undef(opline->op1_type, opline->op1, &free_op1, BP_VAR_R);
+ }
arg_num = ZEND_CALL_NUM_ARGS(EX(call)) + 1;
send_again: