mirror of
https://github.com/php/php-src.git
synced 2024-11-23 18:04:36 +08:00
Merge branch 'coalesce_operator'
* coalesce_operator: Extended coalesce operator test case for ordering/short-circuiting Ensure not evaluated twice Added test Initial coalesce operator implementation
This commit is contained in:
commit
2d069f640e
@ -115,6 +115,7 @@ enum _zend_ast_kind {
|
||||
ZEND_AST_NEW,
|
||||
ZEND_AST_INSTANCEOF,
|
||||
ZEND_AST_YIELD,
|
||||
ZEND_AST_COALESCE,
|
||||
|
||||
ZEND_AST_STATIC,
|
||||
ZEND_AST_WHILE,
|
||||
|
@ -5324,6 +5324,30 @@ void zend_compile_conditional(znode *result, zend_ast *ast TSRMLS_DC) /* {{{ */
|
||||
}
|
||||
/* }}} */
|
||||
|
||||
void zend_compile_coalesce(znode *result, zend_ast *ast TSRMLS_DC) /* {{{ */
|
||||
{
|
||||
zend_ast *expr_ast = ast->child[0];
|
||||
zend_ast *default_ast = ast->child[1];
|
||||
|
||||
znode expr_node, default_node;
|
||||
zend_op *opline;
|
||||
uint32_t opnum;
|
||||
|
||||
zend_compile_var(&expr_node, expr_ast, BP_VAR_IS TSRMLS_CC);
|
||||
|
||||
opnum = get_next_op_number(CG(active_op_array));
|
||||
zend_emit_op(result, ZEND_COALESCE, &expr_node, NULL TSRMLS_CC);
|
||||
|
||||
zend_compile_expr(&default_node, default_ast TSRMLS_CC);
|
||||
|
||||
opline = zend_emit_op(NULL, ZEND_QM_ASSIGN, &default_node, NULL TSRMLS_CC);
|
||||
SET_NODE(opline->result, result);
|
||||
|
||||
opline = &CG(active_op_array)->opcodes[opnum];
|
||||
opline->op2.opline_num = get_next_op_number(CG(active_op_array));
|
||||
}
|
||||
/* }}} */
|
||||
|
||||
void zend_compile_print(znode *result, zend_ast *ast TSRMLS_DC) /* {{{ */
|
||||
{
|
||||
zend_ast *expr_ast = ast->child[0];
|
||||
@ -6158,6 +6182,9 @@ void zend_compile_expr(znode *result, zend_ast *ast TSRMLS_DC) /* {{{ */
|
||||
case ZEND_AST_CONDITIONAL:
|
||||
zend_compile_conditional(result, ast TSRMLS_CC);
|
||||
return;
|
||||
case ZEND_AST_COALESCE:
|
||||
zend_compile_coalesce(result, ast TSRMLS_CC);
|
||||
return;
|
||||
case ZEND_AST_PRINT:
|
||||
zend_compile_print(result, ast TSRMLS_CC);
|
||||
return;
|
||||
|
@ -67,6 +67,7 @@ static YYSIZE_T zend_yytnamerr(char*, const char*);
|
||||
%right T_YIELD
|
||||
%left '=' T_PLUS_EQUAL T_MINUS_EQUAL T_MUL_EQUAL T_DIV_EQUAL T_CONCAT_EQUAL T_MOD_EQUAL T_AND_EQUAL T_OR_EQUAL T_XOR_EQUAL T_SL_EQUAL T_SR_EQUAL T_POW_EQUAL
|
||||
%left '?' ':'
|
||||
%right T_COALESCE
|
||||
%left T_BOOLEAN_OR
|
||||
%left T_BOOLEAN_AND
|
||||
%left '|'
|
||||
@ -221,6 +222,7 @@ static YYSIZE_T zend_yytnamerr(char*, const char*);
|
||||
%token T_NS_C "__NAMESPACE__ (T_NS_C)"
|
||||
%token T_NS_SEPARATOR "\\ (T_NS_SEPARATOR)"
|
||||
%token T_ELLIPSIS "... (T_ELLIPSIS)"
|
||||
%token T_COALESCE "?? (T_COALESCE)"
|
||||
%token T_POW "** (T_POW)"
|
||||
%token T_POW_EQUAL "**= (T_POW_EQUAL)"
|
||||
|
||||
@ -827,6 +829,8 @@ expr_without_variable:
|
||||
{ $$ = zend_ast_create(ZEND_AST_CONDITIONAL, $1, $3, $5); }
|
||||
| expr '?' ':' expr
|
||||
{ $$ = zend_ast_create(ZEND_AST_CONDITIONAL, $1, NULL, $4); }
|
||||
| expr T_COALESCE expr
|
||||
{ $$ = zend_ast_create(ZEND_AST_COALESCE, $1, $3); }
|
||||
| internal_functions_in_yacc { $$ = $1; }
|
||||
| T_INT_CAST expr { $$ = zend_ast_create_cast(IS_LONG, $2); }
|
||||
| T_DOUBLE_CAST expr { $$ = zend_ast_create_cast(IS_DOUBLE, $2); }
|
||||
|
File diff suppressed because it is too large
Load Diff
@ -1202,6 +1202,10 @@ NEWLINE ("\r"|"\n"|"\r\n")
|
||||
return T_ELLIPSIS;
|
||||
}
|
||||
|
||||
<ST_IN_SCRIPTING>"??" {
|
||||
return T_COALESCE;
|
||||
}
|
||||
|
||||
<ST_IN_SCRIPTING>"new" {
|
||||
return T_NEW;
|
||||
}
|
||||
|
@ -1,4 +1,4 @@
|
||||
/* Generated by re2c 0.13.5 */
|
||||
/* Generated by re2c 0.13.6 */
|
||||
#line 3 "Zend/zend_language_scanner_defs.h"
|
||||
|
||||
enum YYCONDTYPE {
|
||||
|
@ -741,6 +741,7 @@ ZEND_API int pass_two(zend_op_array *op_array TSRMLS_DC)
|
||||
case ZEND_JMPZ_EX:
|
||||
case ZEND_JMPNZ_EX:
|
||||
case ZEND_JMP_SET:
|
||||
case ZEND_COALESCE:
|
||||
case ZEND_NEW:
|
||||
case ZEND_FE_RESET:
|
||||
case ZEND_FE_FETCH:
|
||||
|
@ -5138,6 +5138,41 @@ ZEND_VM_HANDLER(152, ZEND_JMP_SET, CONST|TMP|VAR|CV, ANY)
|
||||
ZEND_VM_NEXT_OPCODE();
|
||||
}
|
||||
|
||||
ZEND_VM_HANDLER(169, ZEND_COALESCE, CONST|TMP|VAR|CV, ANY)
|
||||
{
|
||||
USE_OPLINE
|
||||
zend_free_op free_op1;
|
||||
zval *value;
|
||||
int is_ref = 0;
|
||||
|
||||
SAVE_OPLINE();
|
||||
value = GET_OP1_ZVAL_PTR(BP_VAR_IS);
|
||||
|
||||
if ((OP1_TYPE == IS_VAR || OP1_TYPE == IS_CV) && Z_ISREF_P(value)) {
|
||||
is_ref = 1;
|
||||
value = Z_REFVAL_P(value);
|
||||
}
|
||||
|
||||
if (Z_TYPE_P(value) > IS_NULL) {
|
||||
ZVAL_COPY_VALUE(EX_VAR(opline->result.var), value);
|
||||
if (OP1_TYPE == IS_CONST) {
|
||||
if (UNEXPECTED(Z_OPT_COPYABLE_P(value))) {
|
||||
zval_copy_ctor_func(EX_VAR(opline->result.var));
|
||||
}
|
||||
} else if (OP1_TYPE == IS_CV) {
|
||||
if (Z_OPT_REFCOUNTED_P(value)) Z_ADDREF_P(value);
|
||||
} else if (OP1_TYPE == IS_VAR && is_ref) {
|
||||
if (Z_OPT_REFCOUNTED_P(value)) Z_ADDREF_P(value);
|
||||
FREE_OP1();
|
||||
}
|
||||
ZEND_VM_JMP(opline->op2.jmp_addr);
|
||||
}
|
||||
|
||||
FREE_OP1();
|
||||
CHECK_EXCEPTION();
|
||||
ZEND_VM_NEXT_OPCODE();
|
||||
}
|
||||
|
||||
ZEND_VM_HANDLER(22, ZEND_QM_ASSIGN, CONST|TMP|VAR|CV, ANY)
|
||||
{
|
||||
USE_OPLINE
|
||||
|
@ -3291,6 +3291,40 @@ static int ZEND_FASTCALL ZEND_JMP_SET_SPEC_CONST_HANDLER(ZEND_OPCODE_HANDLER_AR
|
||||
ZEND_VM_NEXT_OPCODE();
|
||||
}
|
||||
|
||||
static int ZEND_FASTCALL ZEND_COALESCE_SPEC_CONST_HANDLER(ZEND_OPCODE_HANDLER_ARGS)
|
||||
{
|
||||
USE_OPLINE
|
||||
|
||||
zval *value;
|
||||
int is_ref = 0;
|
||||
|
||||
SAVE_OPLINE();
|
||||
value = opline->op1.zv;
|
||||
|
||||
if ((IS_CONST == IS_VAR || IS_CONST == IS_CV) && Z_ISREF_P(value)) {
|
||||
is_ref = 1;
|
||||
value = Z_REFVAL_P(value);
|
||||
}
|
||||
|
||||
if (Z_TYPE_P(value) > IS_NULL) {
|
||||
ZVAL_COPY_VALUE(EX_VAR(opline->result.var), value);
|
||||
if (IS_CONST == IS_CONST) {
|
||||
if (UNEXPECTED(Z_OPT_COPYABLE_P(value))) {
|
||||
zval_copy_ctor_func(EX_VAR(opline->result.var));
|
||||
}
|
||||
} else if (IS_CONST == IS_CV) {
|
||||
if (Z_OPT_REFCOUNTED_P(value)) Z_ADDREF_P(value);
|
||||
} else if (IS_CONST == IS_VAR && is_ref) {
|
||||
if (Z_OPT_REFCOUNTED_P(value)) Z_ADDREF_P(value);
|
||||
|
||||
}
|
||||
ZEND_VM_JMP(opline->op2.jmp_addr);
|
||||
}
|
||||
|
||||
CHECK_EXCEPTION();
|
||||
ZEND_VM_NEXT_OPCODE();
|
||||
}
|
||||
|
||||
static int ZEND_FASTCALL ZEND_QM_ASSIGN_SPEC_CONST_HANDLER(ZEND_OPCODE_HANDLER_ARGS)
|
||||
{
|
||||
USE_OPLINE
|
||||
@ -10011,6 +10045,41 @@ static int ZEND_FASTCALL ZEND_JMP_SET_SPEC_TMP_HANDLER(ZEND_OPCODE_HANDLER_ARGS
|
||||
ZEND_VM_NEXT_OPCODE();
|
||||
}
|
||||
|
||||
static int ZEND_FASTCALL ZEND_COALESCE_SPEC_TMP_HANDLER(ZEND_OPCODE_HANDLER_ARGS)
|
||||
{
|
||||
USE_OPLINE
|
||||
zend_free_op free_op1;
|
||||
zval *value;
|
||||
int is_ref = 0;
|
||||
|
||||
SAVE_OPLINE();
|
||||
value = _get_zval_ptr_tmp(opline->op1.var, execute_data, &free_op1 TSRMLS_CC);
|
||||
|
||||
if ((IS_TMP_VAR == IS_VAR || IS_TMP_VAR == IS_CV) && Z_ISREF_P(value)) {
|
||||
is_ref = 1;
|
||||
value = Z_REFVAL_P(value);
|
||||
}
|
||||
|
||||
if (Z_TYPE_P(value) > IS_NULL) {
|
||||
ZVAL_COPY_VALUE(EX_VAR(opline->result.var), value);
|
||||
if (IS_TMP_VAR == IS_CONST) {
|
||||
if (UNEXPECTED(Z_OPT_COPYABLE_P(value))) {
|
||||
zval_copy_ctor_func(EX_VAR(opline->result.var));
|
||||
}
|
||||
} else if (IS_TMP_VAR == IS_CV) {
|
||||
if (Z_OPT_REFCOUNTED_P(value)) Z_ADDREF_P(value);
|
||||
} else if (IS_TMP_VAR == IS_VAR && is_ref) {
|
||||
if (Z_OPT_REFCOUNTED_P(value)) Z_ADDREF_P(value);
|
||||
zval_dtor(free_op1.var);
|
||||
}
|
||||
ZEND_VM_JMP(opline->op2.jmp_addr);
|
||||
}
|
||||
|
||||
zval_dtor(free_op1.var);
|
||||
CHECK_EXCEPTION();
|
||||
ZEND_VM_NEXT_OPCODE();
|
||||
}
|
||||
|
||||
static int ZEND_FASTCALL ZEND_QM_ASSIGN_SPEC_TMP_HANDLER(ZEND_OPCODE_HANDLER_ARGS)
|
||||
{
|
||||
USE_OPLINE
|
||||
@ -16791,6 +16860,41 @@ static int ZEND_FASTCALL ZEND_JMP_SET_SPEC_VAR_HANDLER(ZEND_OPCODE_HANDLER_ARGS
|
||||
ZEND_VM_NEXT_OPCODE();
|
||||
}
|
||||
|
||||
static int ZEND_FASTCALL ZEND_COALESCE_SPEC_VAR_HANDLER(ZEND_OPCODE_HANDLER_ARGS)
|
||||
{
|
||||
USE_OPLINE
|
||||
zend_free_op free_op1;
|
||||
zval *value;
|
||||
int is_ref = 0;
|
||||
|
||||
SAVE_OPLINE();
|
||||
value = _get_zval_ptr_var(opline->op1.var, execute_data, &free_op1 TSRMLS_CC);
|
||||
|
||||
if ((IS_VAR == IS_VAR || IS_VAR == IS_CV) && Z_ISREF_P(value)) {
|
||||
is_ref = 1;
|
||||
value = Z_REFVAL_P(value);
|
||||
}
|
||||
|
||||
if (Z_TYPE_P(value) > IS_NULL) {
|
||||
ZVAL_COPY_VALUE(EX_VAR(opline->result.var), value);
|
||||
if (IS_VAR == IS_CONST) {
|
||||
if (UNEXPECTED(Z_OPT_COPYABLE_P(value))) {
|
||||
zval_copy_ctor_func(EX_VAR(opline->result.var));
|
||||
}
|
||||
} else if (IS_VAR == IS_CV) {
|
||||
if (Z_OPT_REFCOUNTED_P(value)) Z_ADDREF_P(value);
|
||||
} else if (IS_VAR == IS_VAR && is_ref) {
|
||||
if (Z_OPT_REFCOUNTED_P(value)) Z_ADDREF_P(value);
|
||||
zval_ptr_dtor_nogc(free_op1.var);
|
||||
}
|
||||
ZEND_VM_JMP(opline->op2.jmp_addr);
|
||||
}
|
||||
|
||||
zval_ptr_dtor_nogc(free_op1.var);
|
||||
CHECK_EXCEPTION();
|
||||
ZEND_VM_NEXT_OPCODE();
|
||||
}
|
||||
|
||||
static int ZEND_FASTCALL ZEND_QM_ASSIGN_SPEC_VAR_HANDLER(ZEND_OPCODE_HANDLER_ARGS)
|
||||
{
|
||||
USE_OPLINE
|
||||
@ -33794,6 +33898,40 @@ static int ZEND_FASTCALL ZEND_JMP_SET_SPEC_CV_HANDLER(ZEND_OPCODE_HANDLER_ARGS)
|
||||
ZEND_VM_NEXT_OPCODE();
|
||||
}
|
||||
|
||||
static int ZEND_FASTCALL ZEND_COALESCE_SPEC_CV_HANDLER(ZEND_OPCODE_HANDLER_ARGS)
|
||||
{
|
||||
USE_OPLINE
|
||||
|
||||
zval *value;
|
||||
int is_ref = 0;
|
||||
|
||||
SAVE_OPLINE();
|
||||
value = _get_zval_ptr_cv_BP_VAR_IS(execute_data, opline->op1.var TSRMLS_CC);
|
||||
|
||||
if ((IS_CV == IS_VAR || IS_CV == IS_CV) && Z_ISREF_P(value)) {
|
||||
is_ref = 1;
|
||||
value = Z_REFVAL_P(value);
|
||||
}
|
||||
|
||||
if (Z_TYPE_P(value) > IS_NULL) {
|
||||
ZVAL_COPY_VALUE(EX_VAR(opline->result.var), value);
|
||||
if (IS_CV == IS_CONST) {
|
||||
if (UNEXPECTED(Z_OPT_COPYABLE_P(value))) {
|
||||
zval_copy_ctor_func(EX_VAR(opline->result.var));
|
||||
}
|
||||
} else if (IS_CV == IS_CV) {
|
||||
if (Z_OPT_REFCOUNTED_P(value)) Z_ADDREF_P(value);
|
||||
} else if (IS_CV == IS_VAR && is_ref) {
|
||||
if (Z_OPT_REFCOUNTED_P(value)) Z_ADDREF_P(value);
|
||||
|
||||
}
|
||||
ZEND_VM_JMP(opline->op2.jmp_addr);
|
||||
}
|
||||
|
||||
CHECK_EXCEPTION();
|
||||
ZEND_VM_NEXT_OPCODE();
|
||||
}
|
||||
|
||||
static int ZEND_FASTCALL ZEND_QM_ASSIGN_SPEC_CV_HANDLER(ZEND_OPCODE_HANDLER_ARGS)
|
||||
{
|
||||
USE_OPLINE
|
||||
@ -47388,6 +47526,31 @@ void zend_init_opcodes_handlers(void)
|
||||
ZEND_NULL_HANDLER,
|
||||
ZEND_NULL_HANDLER,
|
||||
ZEND_NULL_HANDLER,
|
||||
ZEND_COALESCE_SPEC_CONST_HANDLER,
|
||||
ZEND_COALESCE_SPEC_CONST_HANDLER,
|
||||
ZEND_COALESCE_SPEC_CONST_HANDLER,
|
||||
ZEND_COALESCE_SPEC_CONST_HANDLER,
|
||||
ZEND_COALESCE_SPEC_CONST_HANDLER,
|
||||
ZEND_COALESCE_SPEC_TMP_HANDLER,
|
||||
ZEND_COALESCE_SPEC_TMP_HANDLER,
|
||||
ZEND_COALESCE_SPEC_TMP_HANDLER,
|
||||
ZEND_COALESCE_SPEC_TMP_HANDLER,
|
||||
ZEND_COALESCE_SPEC_TMP_HANDLER,
|
||||
ZEND_COALESCE_SPEC_VAR_HANDLER,
|
||||
ZEND_COALESCE_SPEC_VAR_HANDLER,
|
||||
ZEND_COALESCE_SPEC_VAR_HANDLER,
|
||||
ZEND_COALESCE_SPEC_VAR_HANDLER,
|
||||
ZEND_COALESCE_SPEC_VAR_HANDLER,
|
||||
ZEND_NULL_HANDLER,
|
||||
ZEND_NULL_HANDLER,
|
||||
ZEND_NULL_HANDLER,
|
||||
ZEND_NULL_HANDLER,
|
||||
ZEND_NULL_HANDLER,
|
||||
ZEND_COALESCE_SPEC_CV_HANDLER,
|
||||
ZEND_COALESCE_SPEC_CV_HANDLER,
|
||||
ZEND_COALESCE_SPEC_CV_HANDLER,
|
||||
ZEND_COALESCE_SPEC_CV_HANDLER,
|
||||
ZEND_COALESCE_SPEC_CV_HANDLER,
|
||||
ZEND_NULL_HANDLER
|
||||
};
|
||||
zend_opcode_handlers = (opcode_handler_t*)labels;
|
||||
|
@ -21,7 +21,7 @@
|
||||
#include <stdio.h>
|
||||
#include <zend.h>
|
||||
|
||||
const char *zend_vm_opcodes_map[169] = {
|
||||
const char *zend_vm_opcodes_map[170] = {
|
||||
"ZEND_NOP",
|
||||
"ZEND_ADD",
|
||||
"ZEND_SUB",
|
||||
@ -191,6 +191,7 @@ const char *zend_vm_opcodes_map[169] = {
|
||||
"ZEND_POW",
|
||||
"ZEND_ASSIGN_POW",
|
||||
"ZEND_BIND_GLOBAL",
|
||||
"ZEND_COALESCE",
|
||||
};
|
||||
|
||||
ZEND_API const char* zend_get_opcode_name(zend_uchar opcode) {
|
||||
|
@ -179,5 +179,6 @@ ZEND_API const char *zend_get_opcode_name(zend_uchar opcode);
|
||||
#define ZEND_POW 166
|
||||
#define ZEND_ASSIGN_POW 167
|
||||
#define ZEND_BIND_GLOBAL 168
|
||||
#define ZEND_COALESCE 169
|
||||
|
||||
#endif
|
||||
|
79
tests/lang/operators/coalesce.phpt
Normal file
79
tests/lang/operators/coalesce.phpt
Normal file
@ -0,0 +1,79 @@
|
||||
--TEST--
|
||||
Test ?? operator
|
||||
--FILE--
|
||||
<?php
|
||||
|
||||
$var = 7;
|
||||
$var2 = NULL;
|
||||
|
||||
$obj = new StdClass;
|
||||
$obj->boo = 7;
|
||||
|
||||
$arr = [
|
||||
2 => 7,
|
||||
"foo" => "bar",
|
||||
"foobar" => NULL,
|
||||
"qux" => $obj,
|
||||
"bing" => [
|
||||
"bang"
|
||||
]
|
||||
];
|
||||
|
||||
function foobar() {
|
||||
echo "called\n";
|
||||
return ['a'];
|
||||
}
|
||||
|
||||
var_dump($nonexistant_variable ?? 3);
|
||||
echo PHP_EOL;
|
||||
var_dump($var ?? 3);
|
||||
var_dump($var2 ?? 3);
|
||||
echo PHP_EOL;
|
||||
var_dump($obj->boo ?? 3);
|
||||
var_dump($obj->bing ?? 3);
|
||||
var_dump($arr["qux"]->boo ?? 3);
|
||||
var_dump($arr["qux"]->bing ?? 3);
|
||||
echo PHP_EOL;
|
||||
var_dump($arr[2] ?? 3);
|
||||
var_dump($arr["foo"] ?? 3);
|
||||
var_dump($arr["foobar"] ?? 3);
|
||||
var_dump($arr["qux"] ?? 3);
|
||||
var_dump($arr["bing"][0] ?? 3);
|
||||
var_dump($arr["bing"][1] ?? 3);
|
||||
echo PHP_EOL;
|
||||
var_dump(foobar()[0] ?? false);
|
||||
echo PHP_EOL;
|
||||
function f($x)
|
||||
{
|
||||
printf("%s(%d)\n", __FUNCTION__, $x);
|
||||
return $x;
|
||||
}
|
||||
|
||||
$a = f(null) ?? f(1) ?? f(2);
|
||||
?>
|
||||
--EXPECTF--
|
||||
int(3)
|
||||
|
||||
int(7)
|
||||
int(3)
|
||||
|
||||
int(7)
|
||||
int(3)
|
||||
int(7)
|
||||
int(3)
|
||||
|
||||
int(7)
|
||||
string(3) "bar"
|
||||
int(3)
|
||||
object(stdClass)#%d (%d) {
|
||||
["boo"]=>
|
||||
int(7)
|
||||
}
|
||||
string(4) "bang"
|
||||
int(3)
|
||||
|
||||
called
|
||||
string(1) "a"
|
||||
|
||||
f(0)
|
||||
f(1)
|
Loading…
Reference in New Issue
Block a user