1
0
mirror of https://github.com/php/php-src.git synced 2026-03-24 00:02:20 +01:00

Make ASSIGN, ASSIGN_OP, INC and DEC opcodes to return IS_TMP_VAR instead of IS_VAR.

This helps to avoid unnecessary IS_REFERENCE checks.
This changes some notices "Only variables should be passed by reference" to exception "Cannot pass parameter %d by reference".

Also, for consistency, compile-time fatal error "Only variables can be passed by reference" was converted to exception "Cannot pass parameter %d by reference"
This commit is contained in:
Dmitry Stogov
2020-02-07 13:36:52 +03:00
parent ad3f768253
commit 64b40f69dc
16 changed files with 199 additions and 86 deletions

View File

@@ -125,6 +125,11 @@ PHP 8.0 UPGRADE NOTES
warning.
. Uncaught exceptions now go through "clean shutdown", which means that
destructors will be called after an uncaught exception.
. Compile time fatal error "Only variables can be passed by reference" has been
delayed until run-time and converted to "Cannot pass parameter by reference"
exception.
. Some "Only variables should be passed by reference" notices have been converted
to "Cannot pass parameter by reference" exception.
- COM:
. Removed the ability to import case-insensitive constants from type

View File

@@ -11,6 +11,8 @@ PHP 8.0 INTERNALS UPGRADE NOTES
h. zend_value_error()
i. get_closure() object handler
j. compare_objects() and compare() object handlers
k. The 'I' length modifier
l. Some VM instructions switched to IS_TMP_VAR result insted of IS_VAR
2. Build system changes
a. Abstract
@@ -89,6 +91,13 @@ PHP 8.0 INTERNALS UPGRADE NOTES
The 'v' format from the custom snprintf and spprintf implementations has
been removed. Use the standard 's' format instead.
l. Some VM instructions switched to IS_TMP_VAR result insted of IS_VAR.
Actually, all assignments (ZEND_ASSIGN, ZEND_ASSIGN_DIM, ZEND_ASSIGN_OBJ,
ZEND_ASSIGN_STATIC_PROP), all compound assignments (ZEND_ASSIGN_OP,
ZEND_ASSIGN_DIM_OP, ZEND_ASSIGN_OBJ_OP, ZEND_ASSIGN_STATIC_PROP_OP) and all
pre increments/decrements (ZEND_PRE_INC, ZEND_PRE_DEC, ZEND_PRE_INC_OBJ
ZEND_PRE_DEC_OBJ, ZEND_PRE_INC_STATIC_PROP ZEND_PRE_DEC_STATIC_PROP).
========================
2. Build system changes
========================

View File

@@ -3,23 +3,31 @@ Bug #72038 (Function calls with values to a by-ref parameter don't always throw
--FILE--
<?php
test($foo = new stdClass);
var_dump($foo);
test($bar = 2);
var_dump($bar);
test($baz = &$bar);
var_dump($baz);
try {
test($foo = new stdClass);
var_dump($foo);
} catch (Throwable $e) {
echo "Exception: " . $e->getMessage() . "\n";
}
try {
test($bar = 2);
var_dump($bar);
} catch (Throwable $e) {
echo "Exception: " . $e->getMessage() . "\n";
}
try {
test($baz = &$bar);
var_dump($baz);
} catch (Throwable $e) {
echo "Exception: " . $e->getMessage() . "\n";
}
function test(&$param) {
$param = 1;
}
?>
--EXPECTF--
Notice: Only variables should be passed by reference in %s on line %d
object(stdClass)#1 (0) {
}
Notice: Only variables should be passed by reference in %s on line %d
int(2)
--EXPECT--
Exception: Cannot pass parameter 1 by reference
Exception: Cannot pass parameter 1 by reference
int(1)

View File

@@ -12,4 +12,7 @@ change(list($val) = $array);
var_dump($array);
?>
--EXPECTF--
Fatal error: Only variables can be passed by reference in %s on line %d
Fatal error: Uncaught Error: Cannot pass parameter 1 by reference in %s:%d
Stack trace:
#0 {main}
thrown in %s on line %d

View File

@@ -4,20 +4,23 @@ Bug #78154: SEND_VAR_NO_REF does not always send reference
<?php
namespace {
var_dump(similar_text('a', 'a', $c=0x44444444));
var_dump($c);
try {
var_dump(similar_text('a', 'a', $c=0x44444444));
var_dump($c);
} catch (Throwable $e) {
echo "Exception: " . $e->getMessage() . "\n";
}
}
namespace Foo {
var_dump(similar_text('a', 'a', $d=0x44444444));
var_dump($d);
try {
var_dump(similar_text('a', 'a', $d=0x44444444));
var_dump($d);
} catch (\Throwable $e) {
echo "Exception: " . $e->getMessage() . "\n";
}
}
?>
--EXPECTF--
Notice: Only variables should be passed by reference in %s on line %d
int(1)
int(1145324612)
Notice: Only variables should be passed by reference in %s on line %d
int(1)
int(1145324612)
--EXPECT--
Exception: Cannot pass parameter 3 by reference
Exception: Cannot pass parameter 3 by reference

View File

@@ -11,4 +11,7 @@ foo(1);
echo "Done\n";
?>
--EXPECTF--
Fatal error: Only variables can be passed by reference in %s on line %d
Fatal error: Uncaught Error: Cannot pass parameter 1 by reference in %s:%d
Stack trace:
#0 {main}
thrown in %s on line %d

View File

@@ -9,4 +9,7 @@ test(1);
?>
--EXPECTF--
Fatal error: Only variables can be passed by reference in %s on line %d
Fatal error: Uncaught Error: Cannot pass parameter 1 by reference in %s:%d
Stack trace:
#0 {main}
thrown in %s on line %d

View File

@@ -718,7 +718,8 @@ void zend_do_free(znode *op1) /* {{{ */
if (op1->op_type == IS_TMP_VAR) {
zend_op *opline = &CG(active_op_array)->opcodes[CG(active_op_array)->last-1];
while (opline->opcode == ZEND_END_SILENCE) {
while (opline->opcode == ZEND_END_SILENCE ||
opline->opcode == ZEND_OP_DATA) {
opline--;
}
@@ -738,6 +739,22 @@ void zend_do_free(znode *op1) /* {{{ */
opline->opcode -= 2;
opline->result_type = IS_UNUSED;
return;
case ZEND_ASSIGN:
case ZEND_ASSIGN_DIM:
case ZEND_ASSIGN_OBJ:
case ZEND_ASSIGN_STATIC_PROP:
case ZEND_ASSIGN_OP:
case ZEND_ASSIGN_DIM_OP:
case ZEND_ASSIGN_OBJ_OP:
case ZEND_ASSIGN_STATIC_PROP_OP:
case ZEND_PRE_INC_STATIC_PROP:
case ZEND_PRE_DEC_STATIC_PROP:
case ZEND_PRE_INC_OBJ:
case ZEND_PRE_DEC_OBJ:
case ZEND_PRE_INC:
case ZEND_PRE_DEC:
opline->result_type = IS_UNUSED;
return;
}
}
@@ -2921,7 +2938,7 @@ void zend_compile_assign(znode *result, zend_ast *ast) /* {{{ */
zend_delayed_compile_var(&var_node, var_ast, BP_VAR_W, 0);
zend_compile_expr(&expr_node, expr_ast);
zend_delayed_compile_end(offset);
zend_emit_op(result, ZEND_ASSIGN, &var_node, &expr_node);
zend_emit_op_tmp(result, ZEND_ASSIGN, &var_node, &expr_node);
return;
case ZEND_AST_STATIC_PROP:
offset = zend_delayed_compile_begin();
@@ -2930,6 +2947,8 @@ void zend_compile_assign(znode *result, zend_ast *ast) /* {{{ */
opline = zend_delayed_compile_end(offset);
opline->opcode = ZEND_ASSIGN_STATIC_PROP;
opline->result_type = IS_TMP_VAR;
result->op_type = IS_TMP_VAR;
zend_emit_op_data(&expr_node);
return;
@@ -2953,6 +2972,8 @@ void zend_compile_assign(znode *result, zend_ast *ast) /* {{{ */
opline = zend_delayed_compile_end(offset);
opline->opcode = ZEND_ASSIGN_DIM;
opline->result_type = IS_TMP_VAR;
result->op_type = IS_TMP_VAR;
opline = zend_emit_op_data(&expr_node);
return;
@@ -2963,6 +2984,8 @@ void zend_compile_assign(znode *result, zend_ast *ast) /* {{{ */
opline = zend_delayed_compile_end(offset);
opline->opcode = ZEND_ASSIGN_OBJ;
opline->result_type = IS_TMP_VAR;
result->op_type = IS_TMP_VAR;
zend_emit_op_data(&expr_node);
return;
@@ -3086,7 +3109,7 @@ void zend_compile_compound_assign(znode *result, zend_ast *ast) /* {{{ */
zend_delayed_compile_var(&var_node, var_ast, BP_VAR_RW, 0);
zend_compile_expr(&expr_node, expr_ast);
zend_delayed_compile_end(offset);
opline = zend_emit_op(result, ZEND_ASSIGN_OP, &var_node, &expr_node);
opline = zend_emit_op_tmp(result, ZEND_ASSIGN_OP, &var_node, &expr_node);
opline->extended_value = opcode;
return;
case ZEND_AST_STATIC_PROP:
@@ -3098,6 +3121,8 @@ void zend_compile_compound_assign(znode *result, zend_ast *ast) /* {{{ */
cache_slot = opline->extended_value;
opline->opcode = ZEND_ASSIGN_STATIC_PROP_OP;
opline->extended_value = opcode;
opline->result_type = IS_TMP_VAR;
result->op_type = IS_TMP_VAR;
opline = zend_emit_op_data(&expr_node);
opline->extended_value = cache_slot;
@@ -3110,6 +3135,8 @@ void zend_compile_compound_assign(znode *result, zend_ast *ast) /* {{{ */
opline = zend_delayed_compile_end(offset);
opline->opcode = ZEND_ASSIGN_DIM_OP;
opline->extended_value = opcode;
opline->result_type = IS_TMP_VAR;
result->op_type = IS_TMP_VAR;
zend_emit_op_data(&expr_node);
return;
@@ -3122,6 +3149,8 @@ void zend_compile_compound_assign(znode *result, zend_ast *ast) /* {{{ */
cache_slot = opline->extended_value;
opline->opcode = ZEND_ASSIGN_OBJ_OP;
opline->extended_value = opcode;
opline->result_type = IS_TMP_VAR;
result->op_type = IS_TMP_VAR;
opline = zend_emit_op_data(&expr_node);
opline->extended_value = cache_slot;
@@ -3236,11 +3265,9 @@ uint32_t zend_compile_args(zend_ast *ast, zend_function *fbc) /* {{{ */
opcode = ZEND_SEND_VAR_EX;
}
} else {
if (fbc) {
/* Delay "Only variables can be passed by reference" error to execution */
if (fbc && !ARG_MUST_BE_SENT_BY_REF(fbc, arg_num)) {
opcode = ZEND_SEND_VAL;
if (ARG_MUST_BE_SENT_BY_REF(fbc, arg_num)) {
zend_error_noreturn(E_COMPILE_ERROR, "Only variables can be passed by reference");
}
} else {
opcode = ZEND_SEND_VAL_EX;
}
@@ -7577,13 +7604,17 @@ void zend_compile_pre_incdec(znode *result, zend_ast *ast) /* {{{ */
if (var_ast->kind == ZEND_AST_PROP) {
zend_op *opline = zend_compile_prop(result, var_ast, BP_VAR_RW, 0);
opline->opcode = ast->kind == ZEND_AST_PRE_INC ? ZEND_PRE_INC_OBJ : ZEND_PRE_DEC_OBJ;
opline->result_type = IS_TMP_VAR;
result->op_type = IS_TMP_VAR;
} else if (var_ast->kind == ZEND_AST_STATIC_PROP) {
zend_op *opline = zend_compile_static_prop(result, var_ast, BP_VAR_RW, 0, 0);
opline->opcode = ast->kind == ZEND_AST_PRE_INC ? ZEND_PRE_INC_STATIC_PROP : ZEND_PRE_DEC_STATIC_PROP;
opline->result_type = IS_TMP_VAR;
result->op_type = IS_TMP_VAR;
} else {
znode var_node;
zend_compile_var(&var_node, var_ast, BP_VAR_RW, 0);
zend_emit_op(result, ast->kind == ZEND_AST_PRE_INC ? ZEND_PRE_INC : ZEND_PRE_DEC,
zend_emit_op_tmp(result, ast->kind == ZEND_AST_PRE_INC ? ZEND_PRE_INC : ZEND_PRE_DEC,
&var_node, NULL);
}
}
@@ -7764,20 +7795,26 @@ void zend_compile_assign_coalesce(znode *result, zend_ast *ast) /* {{{ */
opline = &CG(active_op_array)->opcodes[CG(active_op_array)->last-1];
switch (var_ast->kind) {
case ZEND_AST_VAR:
zend_emit_op(&assign_node, ZEND_ASSIGN, &var_node_w, &default_node);
zend_emit_op_tmp(&assign_node, ZEND_ASSIGN, &var_node_w, &default_node);
break;
case ZEND_AST_STATIC_PROP:
opline->opcode = ZEND_ASSIGN_STATIC_PROP;
opline->result_type = IS_TMP_VAR;
var_node_w.op_type = IS_TMP_VAR;
zend_emit_op_data(&default_node);
assign_node = var_node_w;
break;
case ZEND_AST_DIM:
opline->opcode = ZEND_ASSIGN_DIM;
opline->result_type = IS_TMP_VAR;
var_node_w.op_type = IS_TMP_VAR;
zend_emit_op_data(&default_node);
assign_node = var_node_w;
break;
case ZEND_AST_PROP:
opline->opcode = ZEND_ASSIGN_OBJ;
opline->result_type = IS_TMP_VAR;
var_node_w.op_type = IS_TMP_VAR;
zend_emit_op_data(&default_node);
assign_node = var_node_w;
break;

View File

@@ -250,13 +250,38 @@ static void zend_optimize_block(zend_basic_block *block, zend_op_array *op_array
case ZEND_FREE:
if (opline->op1_type == IS_TMP_VAR) {
src = VAR_SOURCE(opline->op1);
if (src &&
(src->opcode == ZEND_BOOL || src->opcode == ZEND_BOOL_NOT)) {
/* T = BOOL(X), FREE(T) => T = BOOL(X) */
/* The remaining BOOL is removed by a separate optimization */
VAR_SOURCE(opline->op1) = NULL;
MAKE_NOP(opline);
++(*opt_count);
if (src) {
switch (src->opcode) {
case ZEND_BOOL:
case ZEND_BOOL_NOT:
/* T = BOOL(X), FREE(T) => T = BOOL(X) */
/* The remaining BOOL is removed by a separate optimization */
VAR_SOURCE(opline->op1) = NULL;
MAKE_NOP(opline);
++(*opt_count);
break;
case ZEND_ASSIGN:
case ZEND_ASSIGN_DIM:
case ZEND_ASSIGN_OBJ:
case ZEND_ASSIGN_STATIC_PROP:
case ZEND_ASSIGN_OP:
case ZEND_ASSIGN_DIM_OP:
case ZEND_ASSIGN_OBJ_OP:
case ZEND_ASSIGN_STATIC_PROP_OP:
case ZEND_PRE_INC:
case ZEND_PRE_DEC:
case ZEND_PRE_INC_OBJ:
case ZEND_PRE_DEC_OBJ:
case ZEND_PRE_INC_STATIC_PROP:
case ZEND_PRE_DEC_STATIC_PROP:
src->result_type = IS_UNUSED;
VAR_SOURCE(opline->op1) = NULL;
MAKE_NOP(opline);
++(*opt_count);
break;
default:
break;
}
}
} else if (opline->op1_type == IS_VAR) {
src = VAR_SOURCE(opline->op1);
@@ -1649,7 +1674,7 @@ static void zend_t_usage(zend_cfg *cfg, zend_op_array *op_array, zend_bitset use
while (opline >= end) {
/* usage checks */
if (opline->result_type == IS_VAR) {
if (opline->result_type & (IS_VAR|IS_TMP_VAR)) {
if (!zend_bitset_in(usage, VAR_NUM(opline->result.var))) {
switch (opline->opcode) {
case ZEND_ASSIGN_OP:
@@ -1666,13 +1691,6 @@ static void zend_t_usage(zend_cfg *cfg, zend_op_array *op_array, zend_bitset use
case ZEND_DO_FCALL_BY_NAME:
opline->result_type = IS_UNUSED;
break;
}
} else {
zend_bitset_excl(usage, VAR_NUM(opline->result.var));
}
} else if (opline->result_type == IS_TMP_VAR) {
if (!zend_bitset_in(usage, VAR_NUM(opline->result.var))) {
switch (opline->opcode) {
case ZEND_POST_INC:
case ZEND_POST_DEC:
case ZEND_POST_INC_OBJ:

View File

@@ -427,7 +427,7 @@ static void place_essa_pis(
pi_range_not_equals(pi, -1, 1);
}
}
} else if (opline->op1_type == IS_VAR &&
} else if (opline->op1_type == IS_TMP_VAR &&
((opline-1)->opcode == ZEND_PRE_INC ||
(opline-1)->opcode == ZEND_PRE_DEC) &&
opline->op1.var == (opline-1)->result.var &&

View File

@@ -16,4 +16,9 @@ var_dump(preg_match_all($regex, $subject, 'test'));
echo "Done";
?>
--EXPECTF--
Fatal error: Only variables can be passed by reference in %spreg_match_all_error3.php on line %d
*** Testing preg_match_all() : error conditions ***
Fatal error: Uncaught Error: Cannot pass parameter 3 by reference in %s:%d
Stack trace:
#0 {main}
thrown in %s on line %d

View File

@@ -8,4 +8,7 @@ function f() {
var_dump(next(array(1, 2)));
?>
--EXPECTF--
Fatal error: Only variables can be passed by reference in %s on line %d
Fatal error: Uncaught Error: Cannot pass parameter 1 by reference in %s:%d
Stack trace:
#0 {main}
thrown in %s on line %d

View File

@@ -15,4 +15,7 @@ prev - ensure we cannot pass a temporary
var_dump(prev(array(1, 2)));
?>
--EXPECTF--
Fatal error: Only variables can be passed by reference in %s on line %d
Fatal error: Uncaught Error: Cannot pass parameter 1 by reference in %s:%d
Stack trace:
#0 {main}
thrown in %s on line %d

View File

@@ -16,4 +16,7 @@ Basic class support - attempting to pass a class constant by reference.
var_dump(aclass::myConst);
?>
--EXPECTF--
Fatal error: Only variables can be passed by reference in %s on line 12
Fatal error: Uncaught Error: Cannot pass parameter 1 by reference in %s:%d
Stack trace:
#0 {main}
thrown in %s on line %d

View File

@@ -12,4 +12,7 @@ f(2);
?>
--EXPECTF--
Fatal error: Only variables can be passed by reference in %s on line 8
Fatal error: Uncaught Error: Cannot pass parameter 1 by reference in %s:%d
Stack trace:
#0 {main}
thrown in %s on line %d

View File

@@ -9,42 +9,52 @@ function f(&$a) {
}
echo "\n\n---> Pass constant assignment by reference:\n";
f($a="a.original");
var_dump($a);
try {
f($a="a.original");
var_dump($a);
} catch (Throwable $e) {
echo "Exception: " . $e->getMessage() ."\n";
}
echo "\n\n---> Pass variable assignment by reference:\n";
unset($a);
$a = "a.original";
f($b = $a);
var_dump($a);
try {
unset($a);
$a = "a.original";
f($b = $a);
var_dump($a);
} catch (Throwable $e) {
echo "Exception: " . $e->getMessage() ."\n";
}
echo "\n\n---> Pass reference assignment by reference:\n";
unset($a, $b);
$a = "a.original";
f($b =& $a);
var_dump($a);
try {
unset($a, $b);
$a = "a.original";
f($b =& $a);
var_dump($a);
} catch (Throwable $e) {
echo "Exception: " . $e->getMessage() ."\n";
}
echo "\n\n---> Pass concat assignment by reference:\n";
unset($a, $b);
$b = "b.original";
$a = "a.original";
f($b .= $a);
var_dump($a);
try {
unset($a, $b);
$b = "b.original";
$a = "a.original";
f($b .= $a);
var_dump($a);
} catch (Throwable $e) {
echo "Exception: " . $e->getMessage() ."\n";
}
?>
--EXPECTF--
--EXPECT--
---> Pass constant assignment by reference:
Notice: Only variables should be passed by reference in %s on line 9
string(10) "a.original"
string(10) "a.original"
Exception: Cannot pass parameter 1 by reference
---> Pass variable assignment by reference:
Notice: Only variables should be passed by reference in %s on line 15
string(10) "a.original"
string(10) "a.original"
Exception: Cannot pass parameter 1 by reference
---> Pass reference assignment by reference:
@@ -53,7 +63,4 @@ string(9) "a.changed"
---> Pass concat assignment by reference:
Notice: Only variables should be passed by reference in %s on line 28
string(20) "b.originala.original"
string(10) "a.original"
Exception: Cannot pass parameter 1 by reference