Skip to content

Commit

Permalink
Remove support for ZEND_SEND_USER
Browse files Browse the repository at this point in the history
  • Loading branch information
iluuu1994 committed May 30, 2024
1 parent b3a6425 commit 9068bce
Show file tree
Hide file tree
Showing 13 changed files with 110 additions and 221 deletions.
9 changes: 7 additions & 2 deletions Zend/tests/explicitSendByRef/__call.phpt
Original file line number Diff line number Diff line change
Expand Up @@ -22,7 +22,11 @@ class ForwardCalls {
$forward = new ForwardCalls(new Incrementor);

$i = 0;
$forward->inc(&$i);
try {
$forward->inc(&$i);
} catch (Error $e) {
echo $e->getMessage(), "\n";
}
var_dump($i);

$i = 0;
Expand All @@ -31,5 +35,6 @@ var_dump($i);

?>
--EXPECT--
int(1)
Cannot pass reference to by-value parameter 1
int(0)
int(0)
21 changes: 16 additions & 5 deletions Zend/tests/explicitSendByRef/basic.phpt
Original file line number Diff line number Diff line change
Expand Up @@ -14,10 +14,10 @@ var_dump($b);

// Works (prefer-ref arg)
$c = 42;
$vars = ['d' => &$c];
extract(&$vars, EXTR_REFS);
$d++;
var_dump($c);
$vars = ['b' => 2, 'a' => 1];
$vars2 = [2, 1];
array_multisort(&$vars, $vars2);
var_dump($vars, $vars2);

// Works (by-ref arg, by-ref function)
$e = 42;
Expand Down Expand Up @@ -68,7 +68,18 @@ array(1) {
int(43)
}
}
int(43)
array(2) {
["a"]=>
int(1)
["b"]=>
int(2)
}
array(2) {
[0]=>
int(1)
[1]=>
int(2)
}
int(43)
Cannot pass reference to by-value parameter 1
Cannot pass result of by-value function by reference
Expand Down
23 changes: 1 addition & 22 deletions Zend/tests/explicitSendByRef/call_user_func.phpt
Original file line number Diff line number Diff line change
Expand Up @@ -3,33 +3,12 @@ call_user_func() with explicit pass by ref
--FILE--
<?php

// Avoid VM builtin.
namespace Foo;

function inc(&$i) { $i++; }

$i = 0;
call_user_func('Foo\inc', $i);
var_dump($i);

$i = 0;
call_user_func('Foo\inc', &$i);
var_dump($i);

$i = 0;
\call_user_func('Foo\inc', $i);
var_dump($i);

$i = 0;
\call_user_func('Foo\inc', &$i);
var_dump($i);

?>
--EXPECTF--
Warning: Foo\inc(): Argument #1 ($i) must be passed by reference, value given in %s on line %d
int(0)
int(1)

Warning: Foo\inc(): Argument #1 ($i) must be passed by reference, value given in %s on line %d
int(0)
int(1)
Fatal error: Cannot pass reference to by-value parameter 2 in %s on line %d
10 changes: 4 additions & 6 deletions Zend/zend_compile.c
Original file line number Diff line number Diff line change
Expand Up @@ -4301,19 +4301,16 @@ static zend_result zend_compile_func_cuf(znode *result, zend_ast_list *args, zen
zend_ast *arg_ast = args->child[i];
znode arg_node;
zend_op *opline;
bool by_ref = 0;

if (arg_ast->kind == ZEND_AST_REF) {
zend_compile_var(&arg_node, arg_ast->child[0], BP_VAR_W, 1);
by_ref = 1;
} else {
zend_compile_expr(&arg_node, arg_ast);
zend_error_noreturn(E_COMPILE_ERROR,
"Cannot pass reference to by-value parameter %" PRIu32, i + 1);
}
zend_compile_expr(&arg_node, arg_ast);

opline = zend_emit_op(NULL, ZEND_SEND_USER, &arg_node, NULL);
opline->op2.num = i;
opline->result.var = EX_NUM_TO_VAR(i - 1);
opline->extended_value = by_ref;
}
zend_emit_op(result, ZEND_DO_FCALL, NULL, NULL);

Expand Down Expand Up @@ -6602,6 +6599,7 @@ static void zend_compile_declare(zend_ast *ast) /* {{{ */
if (Z_LVAL(value_zv) == 1) {
CG(active_op_array)->fn_flags |= ZEND_ACC_STRICT_TYPES;
}

} else {
zend_error(E_COMPILE_WARNING, "Unsupported declare '%s'", ZSTR_VAL(name));
}
Expand Down
54 changes: 13 additions & 41 deletions Zend/zend_compile.h
Original file line number Diff line number Diff line change
Expand Up @@ -1044,8 +1044,7 @@ ZEND_API zend_string *zend_type_to_string(zend_type type);

#define ZEND_SEND_BY_VAL 0u
#define ZEND_SEND_BY_REF 1u
#define ZEND_SEND_PREFER_VAL 2u
#define ZEND_SEND_PREFER_REF 3u
#define ZEND_SEND_PREFER_REF 2u

#define ZEND_THROW_IS_EXPR 1u

Expand Down Expand Up @@ -1078,7 +1077,7 @@ ZEND_API zend_string *zend_type_to_string(zend_type type);
#define IS_CONSTANT_CLASS 0x400 /* __CLASS__ in trait */
#define IS_CONSTANT_UNQUALIFIED_IN_NAMESPACE 0x800

static zend_always_inline bool zend_check_arg_must_be_sent_by_ref(const zend_function *zf, uint32_t arg_num)
static zend_always_inline bool zend_check_arg_send_type(const zend_function *zf, uint32_t arg_num, uint32_t mask)
{
arg_num--;
if (UNEXPECTED(arg_num >= zf->common.num_args)) {
Expand All @@ -1087,44 +1086,17 @@ static zend_always_inline bool zend_check_arg_must_be_sent_by_ref(const zend_fun
}
arg_num = zf->common.num_args;
}
return ZEND_ARG_SEND_MODE(&zf->common.arg_info[arg_num]) == ZEND_SEND_BY_REF;
}

static zend_always_inline int zend_check_arg_should_be_sent_by_ref(const zend_function *zf, uint32_t arg_num)
{
arg_num--;
if (UNEXPECTED(arg_num >= zf->common.num_args)) {
if (EXPECTED((zf->common.fn_flags & ZEND_ACC_VARIADIC) == 0)) {
return 0;
}
arg_num = zf->common.num_args;
}

/* The SEND_BY_REF bit is set for PREFER_REF as well. */
return (ZEND_ARG_SEND_MODE(&zf->common.arg_info[arg_num]) & ZEND_SEND_BY_REF) != 0;
}

static zend_always_inline int zend_check_arg_may_be_sent_by_ref(const zend_function *zf, uint32_t arg_num)
{
arg_num--;
if (UNEXPECTED(arg_num >= zf->common.num_args)) {
if (EXPECTED((zf->common.fn_flags & ZEND_ACC_VARIADIC) == 0)) {
return 0;
}
arg_num = zf->common.num_args;
}

return ZEND_ARG_SEND_MODE(&zf->common.arg_info[arg_num]) != ZEND_SEND_BY_VAL;
return UNEXPECTED((ZEND_ARG_SEND_MODE(&zf->common.arg_info[arg_num]) & mask) != 0);
}

#define ARG_MUST_BE_SENT_BY_REF(zf, arg_num) \
zend_check_arg_must_be_sent_by_ref(zf, arg_num)
zend_check_arg_send_type(zf, arg_num, ZEND_SEND_BY_REF)

#define ARG_SHOULD_BE_SENT_BY_REF(zf, arg_num) \
zend_check_arg_should_be_sent_by_ref(zf, arg_num)
zend_check_arg_send_type(zf, arg_num, ZEND_SEND_BY_REF|ZEND_SEND_PREFER_REF)

#define ARG_MAY_BE_SENT_BY_REF(zf, arg_num) \
zend_check_arg_may_be_sent_by_ref(zf, arg_num)
zend_check_arg_send_type(zf, arg_num, ZEND_SEND_PREFER_REF)

/* Quick API to check first 12 arguments */
#define MAX_ARG_FLAG_NUM 12
Expand All @@ -1133,24 +1105,24 @@ static zend_always_inline int zend_check_arg_may_be_sent_by_ref(const zend_funct
# define ZEND_SET_ARG_FLAG(zf, arg_num, mask) do { \
(zf)->quick_arg_flags |= ((mask) << ((arg_num) - 1) * 2); \
} while (0)
# define ZEND_GET_ARG_FLAG(zf, arg_num) \
(((zf)->quick_arg_flags >> (((arg_num) - 1) * 2)) & 0x3u)
# define ZEND_CHECK_ARG_FLAG(zf, arg_num, mask) \
(((zf)->quick_arg_flags >> (((arg_num) - 1) * 2)) & (mask))
#else
# define ZEND_SET_ARG_FLAG(zf, arg_num, mask) do { \
(zf)->quick_arg_flags |= (((mask) << 6) << (arg_num) * 2); \
} while (0)
# define ZEND_GET_ARG_FLAG(zf, arg_num) \
(((zf)->quick_arg_flags >> (((arg_num) + 3) * 2)) & 0x3u)
# define ZEND_CHECK_ARG_FLAG(zf, arg_num, mask) \
(((zf)->quick_arg_flags >> (((arg_num) + 3) * 2)) & (mask))
#endif

#define QUICK_ARG_MUST_BE_SENT_BY_REF(zf, arg_num) \
(ZEND_GET_ARG_FLAG(zf, arg_num) == ZEND_SEND_BY_REF)
ZEND_CHECK_ARG_FLAG(zf, arg_num, ZEND_SEND_BY_REF)

#define QUICK_ARG_SHOULD_BE_SENT_BY_REF(zf, arg_num) \
((ZEND_GET_ARG_FLAG(zf, arg_num) & ZEND_SEND_BY_REF) != 0)
ZEND_CHECK_ARG_FLAG(zf, arg_num, ZEND_SEND_BY_REF|ZEND_SEND_PREFER_REF)

#define QUICK_ARG_MAY_BE_SENT_BY_REF(zf, arg_num) \
(ZEND_GET_ARG_FLAG(zf, arg_num) != ZEND_SEND_BY_VAL)
ZEND_CHECK_ARG_FLAG(zf, arg_num, ZEND_SEND_PREFER_REF)

#define ZEND_RETURN_VAL 0
#define ZEND_RETURN_REF 1
Expand Down
4 changes: 2 additions & 2 deletions Zend/zend_execute_API.c
Original file line number Diff line number Diff line change
Expand Up @@ -847,7 +847,7 @@ zend_result zend_call_function(zend_fcall_info *fci, zend_fcall_info_cache *fci_

if (ARG_SHOULD_BE_SENT_BY_REF(func, i + 1)) {
if (UNEXPECTED(!Z_ISREF_P(arg))) {
if (ARG_MUST_BE_SENT_BY_REF(func, i + 1)) {
if (!ARG_MAY_BE_SENT_BY_REF(func, i + 1)) {
/* By-value send is not allowed -- emit a warning,
* and perform the call with the value wrapped in a reference. */
zend_param_must_be_ref(func, i + 1);
Expand Down Expand Up @@ -905,7 +905,7 @@ zend_result zend_call_function(zend_fcall_info *fci, zend_fcall_info_cache *fci_

if (ARG_SHOULD_BE_SENT_BY_REF(func, arg_num)) {
if (UNEXPECTED(!Z_ISREF_P(arg))) {
if (ARG_MUST_BE_SENT_BY_REF(func, arg_num)) {
if (!ARG_MAY_BE_SENT_BY_REF(func, arg_num)) {
/* By-value send is not allowed -- emit a warning,
* and perform the call with the value wrapped in a reference. */
zend_param_must_be_ref(func, arg_num);
Expand Down
16 changes: 5 additions & 11 deletions Zend/zend_object_handlers.c
Original file line number Diff line number Diff line change
Expand Up @@ -1317,10 +1317,6 @@ ZEND_API bool zend_check_protected(const zend_class_entry *ce, const zend_class_
}
/* }}} */

ZEND_BEGIN_ARG_INFO_EX(zend_call_arg_info, 0, 0, 0)
ZEND_ARG_VARIADIC_INFO(ZEND_SEND_PREFER_VAL, args)
ZEND_END_ARG_INFO()

ZEND_API zend_function *zend_get_call_trampoline_func(const zend_class_entry *ce, zend_string *method_name, bool is_static) /* {{{ */
{
size_t mname_len;
Expand All @@ -1330,9 +1326,7 @@ ZEND_API zend_function *zend_get_call_trampoline_func(const zend_class_entry *ce
* The low bit must be zero, to not be interpreted as a MAP_PTR offset.
*/
static const void *dummy = (void*)(intptr_t)2;
unsigned char arg_flags =
(ZEND_SEND_PREFER_VAL << 6) | (ZEND_SEND_PREFER_VAL << 4) |
(ZEND_SEND_PREFER_VAL << 2) | ZEND_SEND_PREFER_VAL;
static const zend_arg_info arg_info[1] = {{0}};

ZEND_ASSERT(fbc);

Expand All @@ -1343,9 +1337,9 @@ ZEND_API zend_function *zend_get_call_trampoline_func(const zend_class_entry *ce
}

func->type = ZEND_USER_FUNCTION;
func->arg_flags[0] = arg_flags;
func->arg_flags[1] = arg_flags;
func->arg_flags[2] = arg_flags;
func->arg_flags[0] = 0;
func->arg_flags[1] = 0;
func->arg_flags[2] = 0;
func->fn_flags = ZEND_ACC_CALL_VIA_TRAMPOLINE | ZEND_ACC_PUBLIC | ZEND_ACC_VARIADIC;
if (is_static) {
func->fn_flags |= ZEND_ACC_STATIC;
Expand Down Expand Up @@ -1376,7 +1370,7 @@ ZEND_API zend_function *zend_get_call_trampoline_func(const zend_class_entry *ce
func->prototype = NULL;
func->num_args = 0;
func->required_num_args = 0;
func->arg_info = (zend_arg_info *) zend_call_arg_info + 1;
func->arg_info = (zend_arg_info *) arg_info;

return (zend_function*)func;
}
Expand Down
40 changes: 15 additions & 25 deletions Zend/zend_vm_def.h
Original file line number Diff line number Diff line change
Expand Up @@ -4889,7 +4889,7 @@ ZEND_VM_HOT_SEND_HANDLER(50, ZEND_SEND_VAR_NO_REF_EX, VAR, CONST|UNUSED|NUM, SPE
ZVAL_COPY_VALUE(arg, varptr);

if (EXPECTED(Z_ISREF_P(varptr) ||
!ARG_MUST_BE_SENT_BY_REF(EX(call)->func, arg_num))) {
QUICK_ARG_MAY_BE_SENT_BY_REF(EX(call)->func, arg_num))) {
ZEND_VM_NEXT_OPCODE();
}
} else {
Expand All @@ -4901,7 +4901,7 @@ ZEND_VM_HOT_SEND_HANDLER(50, ZEND_SEND_VAR_NO_REF_EX, VAR, CONST|UNUSED|NUM, SPE
ZVAL_COPY_VALUE(arg, varptr);

if (EXPECTED(Z_ISREF_P(varptr) ||
!ARG_MUST_BE_SENT_BY_REF(EX(call)->func, arg_num))) {
ARG_MAY_BE_SENT_BY_REF(EX(call)->func, arg_num))) {
ZEND_VM_NEXT_OPCODE();
}
}
Expand Down Expand Up @@ -4966,10 +4966,10 @@ ZEND_VM_HANDLER(209, ZEND_SEND_EXPLICIT_REF, VAR|CV, NUM, SPEC(QUICK_ARG))
uint32_t arg_num = opline->op2.num;

if (EXPECTED(arg_num <= MAX_ARG_FLAG_NUM)) {
if (!QUICK_ARG_MAY_BE_SENT_BY_REF(EX(call)->func, arg_num)) {
if (!QUICK_ARG_SHOULD_BE_SENT_BY_REF(EX(call)->func, arg_num)) {
ZEND_VM_C_GOTO(invalid_send_ref);
}
} else if (!ARG_MAY_BE_SENT_BY_REF(EX(call)->func, arg_num)) {
} else if (!ARG_SHOULD_BE_SENT_BY_REF(EX(call)->func, arg_num)) {
ZEND_VM_C_LABEL(invalid_send_ref):
SAVE_OPLINE();
zend_throw_error(NULL, "Cannot pass reference to by-value parameter %" PRIu32, arg_num);
Expand Down Expand Up @@ -5000,10 +5000,10 @@ ZEND_VM_HANDLER(210, ZEND_SEND_EXPLICIT_REF_FUNC, VAR, NUM)
uint32_t arg_num = opline->op2.num;

if (EXPECTED(arg_num <= MAX_ARG_FLAG_NUM)) {
if (!QUICK_ARG_MAY_BE_SENT_BY_REF(EX(call)->func, arg_num)) {
if (!QUICK_ARG_SHOULD_BE_SENT_BY_REF(EX(call)->func, arg_num)) {
ZEND_VM_C_GOTO(invalid_send_ref);
}
} else if (!ARG_MAY_BE_SENT_BY_REF(EX(call)->func, arg_num)) {
} else if (!ARG_SHOULD_BE_SENT_BY_REF(EX(call)->func, arg_num)) {
ZEND_VM_C_LABEL(invalid_send_ref):
SAVE_OPLINE();
zend_throw_error(NULL, "Cannot pass reference to by-value parameter %" PRIu32, arg_num);
Expand Down Expand Up @@ -5455,7 +5455,7 @@ ZEND_VM_C_LABEL(send_array):
break;
} else if (ARG_SHOULD_BE_SENT_BY_REF(EX(call)->func, arg_num)) {
if (UNEXPECTED(!Z_ISREF_P(arg))) {
if (ARG_MUST_BE_SENT_BY_REF(EX(call)->func, arg_num)) {
if (!ARG_MAY_BE_SENT_BY_REF(EX(call)->func, arg_num)) {
/* By-value send is not allowed -- emit a warning,
* but still perform the call. */
zend_param_must_be_ref(EX(call)->func, arg_num);
Expand Down Expand Up @@ -5507,7 +5507,7 @@ ZEND_VM_C_LABEL(send_array):
bool must_wrap = 0;
if (ARG_SHOULD_BE_SENT_BY_REF(EX(call)->func, arg_num)) {
if (UNEXPECTED(!Z_ISREF_P(arg))) {
if (ARG_MUST_BE_SENT_BY_REF(EX(call)->func, arg_num)) {
if (!ARG_MAY_BE_SENT_BY_REF(EX(call)->func, arg_num)) {
/* By-value send is not allowed -- emit a warning,
* but still perform the call. */
zend_param_must_be_ref(EX(call)->func, arg_num);
Expand Down Expand Up @@ -5540,31 +5540,21 @@ ZEND_VM_C_LABEL(send_array):
ZEND_VM_NEXT_OPCODE_CHECK_EXCEPTION();
}

ZEND_VM_HANDLER(120, ZEND_SEND_USER, CONST|TMP|VAR|CV, NUM, REF)
ZEND_VM_HANDLER(120, ZEND_SEND_USER, CONST|TMP|VAR|CV, NUM)
{
USE_OPLINE
zval *arg, *param;

SAVE_OPLINE();

arg = GET_OP1_ZVAL_PTR_DEREF(BP_VAR_R);
param = ZEND_CALL_VAR(EX(call), opline->result.var);
if (opline->extended_value) {
arg = GET_OP1_ZVAL_PTR_PTR(BP_VAR_W);
if (Z_ISREF_P(arg)) {
Z_ADDREF_P(arg);
} else {
ZVAL_MAKE_REF_EX(arg, 2);
}
ZVAL_REF(param, Z_REF_P(arg));
if (UNEXPECTED(ARG_MUST_BE_SENT_BY_REF(EX(call)->func, opline->op2.num))) {
zend_param_must_be_ref(EX(call)->func, opline->op2.num);
Z_TRY_ADDREF_P(arg);
ZVAL_NEW_REF(param, arg);
} else {
arg = GET_OP1_ZVAL_PTR_DEREF(BP_VAR_R);
if (UNEXPECTED(ARG_MUST_BE_SENT_BY_REF(EX(call)->func, opline->op2.num))) {
zend_param_must_be_ref(EX(call)->func, opline->op2.num);
Z_TRY_ADDREF_P(arg);
ZVAL_NEW_REF(param, arg);
} else {
ZVAL_COPY(param, arg);
}
ZVAL_COPY(param, arg);
}

FREE_OP1();
Expand Down
Loading

0 comments on commit 9068bce

Please sign in to comment.