diff options
| author | Nikita Popov <nikita.ppv@gmail.com> | 2019-07-30 10:05:12 +0200 |
|---|---|---|
| committer | Nikita Popov <nikita.ppv@gmail.com> | 2019-07-30 10:07:08 +0200 |
| commit | 6913ec3282149914e999d91b056fe1cc68d15ed7 (patch) | |
| tree | 57a5c6a8d08b02303d676ccf4b381a0f0f72b35e | |
| parent | 8413df5e55735b8d40b5baeea4d2b68327103626 (diff) | |
| download | php-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.phpt | 22 | ||||
| -rw-r--r-- | Zend/zend_compile.c | 10 | ||||
| -rw-r--r-- | Zend/zend_vm_def.h | 6 | ||||
| -rw-r--r-- | Zend/zend_vm_execute.h | 6 |
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: |
