Skip to content

Instantly share code, notes, and snippets.

@nikic
Created September 16, 2014 17:10
Show Gist options
  • Star 1 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save nikic/c02f84105a393737c89d to your computer and use it in GitHub Desktop.
Save nikic/c02f84105a393737c89d to your computer and use it in GitHub Desktop.
diff --git a/Zend/zend_ast.h b/Zend/zend_ast.h
index 2a1582c..b571ee1 100644
--- a/Zend/zend_ast.h
+++ b/Zend/zend_ast.h
@@ -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,
diff --git a/Zend/zend_compile.c b/Zend/zend_compile.c
index 0f91a81..2ecadb6 100644
--- a/Zend/zend_compile.c
+++ b/Zend/zend_compile.c
@@ -6922,6 +6922,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];
@@ -7759,6 +7783,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;
diff --git a/Zend/zend_language_parser.y b/Zend/zend_language_parser.y
index 6bfef22..97fc4de 100644
--- a/Zend/zend_language_parser.y
+++ b/Zend/zend_language_parser.y
@@ -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); }
diff --git a/Zend/zend_language_scanner.l b/Zend/zend_language_scanner.l
index bcc341e..3da8406 100644
--- a/Zend/zend_language_scanner.l
+++ b/Zend/zend_language_scanner.l
@@ -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;
}
diff --git a/Zend/zend_opcode.c b/Zend/zend_opcode.c
index d57721e..8c294f1 100644
--- a/Zend/zend_opcode.c
+++ b/Zend/zend_opcode.c
@@ -738,6 +738,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:
diff --git a/Zend/zend_vm_def.h b/Zend/zend_vm_def.h
index a3ae246..d28227e 100644
--- a/Zend/zend_vm_def.h
+++ b/Zend/zend_vm_def.h
@@ -5238,6 +5238,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
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment