Skip to content

Commit 4b04e3b

Browse files
committed
Make JMP_NULL instruction return the NULL value
Or false/true for isset/empty respectively
1 parent 40cb1cf commit 4b04e3b

File tree

7 files changed

+104
-83
lines changed

7 files changed

+104
-83
lines changed

Zend/tests/nullsafe_method_call/008.phpt

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,8 +1,13 @@
11
--TEST--
22
Test nullsafe property coalesce assignment error
3+
--SKIPIF--
4+
<?php if(extension_loaded("Zend OPcache")) die("skip with opcache because SSA incorrectly optimized"); ?>
35
--FILE--
46
<?php
57

8+
$foo = null;
9+
var_dump($foo?->bar ??= 'bar');
10+
611
class Foo {
712
public $bar;
813
}
Lines changed: 17 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,17 @@
1+
--TEST--
2+
Test nullsafe property on reference
3+
--FILE--
4+
<?php
5+
6+
class Foo {
7+
public $bar;
8+
}
9+
10+
$foo = new Foo();
11+
$foo->bar = 'bar';
12+
13+
$fooRef = &$foo;
14+
var_dump($fooRef?->bar);
15+
16+
--EXPECT--
17+
string(3) "bar"

Zend/zend_compile.c

Lines changed: 17 additions & 37 deletions
Original file line numberDiff line numberDiff line change
@@ -63,7 +63,7 @@ typedef struct _zend_loop_var {
6363
#define ZEND_SHORT_CIRCUITING_SCOPE_EMPTY (1 << 1)
6464

6565
typedef struct _zend_short_circuiting_scope {
66-
zend_stack jump_opnums;
66+
zend_stack jmp_null_oplines;
6767
zend_bool flags;
6868
} zend_short_circuiting_scope;
6969

@@ -2249,48 +2249,27 @@ static void zend_begin_short_circuiting_scope()
22492249
CG(short_circuiting) = 1;
22502250

22512251
zend_short_circuiting_scope scope;
2252-
zend_stack_init(&scope.jump_opnums, sizeof(uint32_t));
2252+
zend_stack_init(&scope.jmp_null_oplines, sizeof(zend_op*));
22532253
scope.flags = 0;
22542254
zend_stack_push(&CG(short_circuiting_scopes), &scope);
22552255
}
22562256

22572257
static void zend_end_short_circuiting_scope(znode *result)
22582258
{
22592259
zend_short_circuiting_scope *scope = zend_current_short_circuiting_scope();
2260-
zend_stack *labels = &scope->jump_opnums;
2261-
zend_bool contains_short_circuiting_operators = !zend_stack_is_empty(labels);
2260+
zend_stack *jmp_null_oplines = &scope->jmp_null_oplines;
22622261

2263-
uint32_t end_label = 0;
2264-
if (contains_short_circuiting_operators) {
2265-
end_label = zend_emit_jump(0);
2266-
2267-
while (!zend_stack_is_empty(labels)) {
2268-
uint32_t label = *(uint32_t *)zend_stack_top(labels);
2269-
zend_update_jump_target_to_next(label);
2270-
zend_stack_del_top(labels);
2271-
}
2272-
2273-
znode result_node;
2274-
result_node.op_type = IS_CONST;
2275-
2276-
if (scope->flags & ZEND_SHORT_CIRCUITING_SCOPE_ISSET) {
2277-
ZVAL_BOOL(&result_node.u.constant, 0);
2278-
} else if (scope->flags & ZEND_SHORT_CIRCUITING_SCOPE_EMPTY) {
2279-
ZVAL_BOOL(&result_node.u.constant, 1);
2280-
} else {
2281-
ZVAL_NULL(&result_node.u.constant);
2282-
}
2283-
2284-
zend_op *opline_call = zend_emit_op_tmp(NULL, ZEND_QM_ASSIGN, &result_node, NULL);
2285-
SET_NODE(opline_call->result, result);
2286-
2287-
zend_update_jump_target_to_next(end_label);
2262+
while (!zend_stack_is_empty(jmp_null_oplines)) {
2263+
zend_op *jmp_null_opline = *(zend_op **)zend_stack_top(jmp_null_oplines);
2264+
jmp_null_opline->op2.opline_num = get_next_op_number();
2265+
SET_NODE(jmp_null_opline->result, result);
2266+
jmp_null_opline->extended_value = scope->flags;
2267+
zend_stack_del_top(jmp_null_oplines);
22882268
}
22892269

22902270
CG(short_circuiting) = 0;
2291-
22922271
zend_stack_del_top(&CG(short_circuiting_scopes));
2293-
zend_stack_destroy(labels);
2272+
zend_stack_destroy(jmp_null_oplines);
22942273
}
22952274

22962275
#define ZEND_MEMOIZE_NONE 0
@@ -2775,11 +2754,11 @@ static zend_op *zend_delayed_compile_prop(znode *result, zend_ast *ast, uint32_t
27752754
}
27762755

27772756
if (nullsafe) {
2778-
uint32_t jmp_null = zend_emit_cond_jump(ZEND_JMP_NULL, &obj_node, 0);
2757+
zend_op *jmp_null_opline = zend_emit_op(NULL, ZEND_JMP_NULL, &obj_node, NULL);
27792758

27802759
zend_short_circuiting_scope *short_circuiting_scope = zend_current_short_circuiting_scope();
2781-
zend_stack *short_circuiting_scopes = &short_circuiting_scope->jump_opnums;
2782-
zend_stack_push(short_circuiting_scopes, &jmp_null);
2760+
zend_stack *jmp_null_oplines = &short_circuiting_scope->jmp_null_oplines;
2761+
zend_stack_push(jmp_null_oplines, &jmp_null_opline);
27832762
}
27842763

27852764
zend_compile_expr(&prop_node, prop_ast);
@@ -2791,6 +2770,7 @@ static zend_op *zend_delayed_compile_prop(znode *result, zend_ast *ast, uint32_t
27912770
}
27922771

27932772
zend_adjust_for_fetch_type(opline, result, type);
2773+
27942774
return opline;
27952775
}
27962776
/* }}} */
@@ -4251,11 +4231,11 @@ void zend_compile_method_call(znode *result, zend_ast *ast, uint32_t type) /* {{
42514231
}
42524232

42534233
if (nullsafe) {
4254-
uint32_t jmp_null = zend_emit_cond_jump(ZEND_JMP_NULL, &obj_node, 0);
4234+
zend_op *jmp_null_opline = zend_emit_op(NULL, ZEND_JMP_NULL, &obj_node, NULL);
42554235

42564236
zend_short_circuiting_scope *short_circuiting_scope = zend_current_short_circuiting_scope();
4257-
zend_stack *short_circuiting_scopes = &short_circuiting_scope->jump_opnums;
4258-
zend_stack_push(short_circuiting_scopes, &jmp_null);
4237+
zend_stack *jmp_null_oplines = &short_circuiting_scope->jmp_null_oplines;
4238+
zend_stack_push(jmp_null_oplines, &jmp_null_opline);
42594239
}
42604240

42614241
zend_compile_expr(&method_node, method_ast);

Zend/zend_vm_def.h

Lines changed: 11 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -7032,7 +7032,7 @@ ZEND_VM_COLD_CONST_HANDLER(169, ZEND_COALESCE, CONST|TMP|VAR|CV, JMP_ADDR)
70327032
ZEND_VM_NEXT_OPCODE();
70337033
}
70347034

7035-
ZEND_VM_HOT_NOCONST_HANDLER(195, ZEND_JMP_NULL, CONST|TMP|VAR|CV, JMP_ADDR)
7035+
ZEND_VM_HOT_NOCONST_HANDLER(195, ZEND_JMP_NULL, CONST|TMPVAR|CV, JMP_ADDR)
70367036
{
70377037
USE_OPLINE
70387038
zval *val;
@@ -7042,6 +7042,16 @@ ZEND_VM_HOT_NOCONST_HANDLER(195, ZEND_JMP_NULL, CONST|TMP|VAR|CV, JMP_ADDR)
70427042
if (Z_TYPE_INFO_P(val) != IS_NULL) {
70437043
ZEND_VM_NEXT_OPCODE();
70447044
} else {
7045+
zval *result = EX_VAR(opline->result.var);
7046+
7047+
if (EXPECTED(opline->extended_value == 0)) {
7048+
ZVAL_NULL(result);
7049+
} else if (EXPECTED(opline->extended_value == (1 << 0))) {
7050+
ZVAL_BOOL(result, 0);
7051+
} else if (EXPECTED(opline->extended_value == (1 << 1))) {
7052+
ZVAL_BOOL(result, 1);
7053+
}
7054+
70457055
FREE_OP1();
70467056
ZEND_VM_JMP_EX(OP_JMP_ADDR(opline, opline->op2), 0);
70477057
}

Zend/zend_vm_execute.h

Lines changed: 51 additions & 42 deletions
Original file line numberDiff line numberDiff line change
@@ -4292,6 +4292,15 @@ static ZEND_VM_COLD ZEND_OPCODE_HANDLER_RET ZEND_FASTCALL ZEND_JMP_NULL_SPEC_CON
42924292
if (Z_TYPE_INFO_P(val) != IS_NULL) {
42934293
ZEND_VM_NEXT_OPCODE();
42944294
} else {
4295+
zval *result = EX_VAR(opline->result.var);
4296+
4297+
if (EXPECTED(opline->extended_value == 0)) {
4298+
ZVAL_NULL(result);
4299+
} else if (EXPECTED(opline->extended_value == (1 << 0))) {
4300+
ZVAL_BOOL(result, 0);
4301+
} else if (EXPECTED(opline->extended_value == (1 << 1))) {
4302+
ZVAL_BOOL(result, 1);
4303+
}
42954304

42964305
ZEND_VM_JMP_EX(OP_JMP_ADDR(opline, opline->op2), 0);
42974306
}
@@ -13183,6 +13192,31 @@ static ZEND_OPCODE_HANDLER_RET ZEND_FASTCALL ZEND_INCLUDE_OR_EVAL_SPEC_TMPVAR_HA
1318313192
ZEND_VM_NEXT_OPCODE();
1318413193
}
1318513194

13195+
static ZEND_VM_HOT ZEND_OPCODE_HANDLER_RET ZEND_FASTCALL ZEND_JMP_NULL_SPEC_TMPVAR_HANDLER(ZEND_OPCODE_HANDLER_ARGS)
13196+
{
13197+
USE_OPLINE
13198+
zval *val;
13199+
13200+
val = _get_zval_ptr_var(opline->op1.var EXECUTE_DATA_CC);
13201+
13202+
if (Z_TYPE_INFO_P(val) != IS_NULL) {
13203+
ZEND_VM_NEXT_OPCODE();
13204+
} else {
13205+
zval *result = EX_VAR(opline->result.var);
13206+
13207+
if (EXPECTED(opline->extended_value == 0)) {
13208+
ZVAL_NULL(result);
13209+
} else if (EXPECTED(opline->extended_value == (1 << 0))) {
13210+
ZVAL_BOOL(result, 0);
13211+
} else if (EXPECTED(opline->extended_value == (1 << 1))) {
13212+
ZVAL_BOOL(result, 1);
13213+
}
13214+
13215+
zval_ptr_dtor_nogc(EX_VAR(opline->op1.var));
13216+
ZEND_VM_JMP_EX(OP_JMP_ADDR(opline, opline->op2), 0);
13217+
}
13218+
}
13219+
1318613220
static ZEND_OPCODE_HANDLER_RET ZEND_FASTCALL ZEND_YIELD_FROM_SPEC_TMPVAR_HANDLER(ZEND_OPCODE_HANDLER_ARGS)
1318713221
{
1318813222
USE_OPLINE
@@ -17907,21 +17941,6 @@ static ZEND_OPCODE_HANDLER_RET ZEND_FASTCALL ZEND_COALESCE_SPEC_TMP_HANDLER(ZEND
1790717941
ZEND_VM_NEXT_OPCODE();
1790817942
}
1790917943

17910-
static ZEND_VM_HOT ZEND_OPCODE_HANDLER_RET ZEND_FASTCALL ZEND_JMP_NULL_SPEC_TMP_HANDLER(ZEND_OPCODE_HANDLER_ARGS)
17911-
{
17912-
USE_OPLINE
17913-
zval *val;
17914-
17915-
val = _get_zval_ptr_tmp(opline->op1.var EXECUTE_DATA_CC);
17916-
17917-
if (Z_TYPE_INFO_P(val) != IS_NULL) {
17918-
ZEND_VM_NEXT_OPCODE();
17919-
} else {
17920-
zval_ptr_dtor_nogc(EX_VAR(opline->op1.var));
17921-
ZEND_VM_JMP_EX(OP_JMP_ADDR(opline, opline->op2), 0);
17922-
}
17923-
}
17924-
1792517944
static ZEND_VM_HOT ZEND_OPCODE_HANDLER_RET ZEND_FASTCALL ZEND_QM_ASSIGN_SPEC_TMP_HANDLER(ZEND_OPCODE_HANDLER_ARGS)
1792617945
{
1792717946
USE_OPLINE
@@ -20904,21 +20923,6 @@ static ZEND_OPCODE_HANDLER_RET ZEND_FASTCALL ZEND_COALESCE_SPEC_VAR_HANDLER(ZEND
2090420923
ZEND_VM_NEXT_OPCODE();
2090520924
}
2090620925

20907-
static ZEND_VM_HOT ZEND_OPCODE_HANDLER_RET ZEND_FASTCALL ZEND_JMP_NULL_SPEC_VAR_HANDLER(ZEND_OPCODE_HANDLER_ARGS)
20908-
{
20909-
USE_OPLINE
20910-
zval *val;
20911-
20912-
val = _get_zval_ptr_var(opline->op1.var EXECUTE_DATA_CC);
20913-
20914-
if (Z_TYPE_INFO_P(val) != IS_NULL) {
20915-
ZEND_VM_NEXT_OPCODE();
20916-
} else {
20917-
zval_ptr_dtor_nogc(EX_VAR(opline->op1.var));
20918-
ZEND_VM_JMP_EX(OP_JMP_ADDR(opline, opline->op2), 0);
20919-
}
20920-
}
20921-
2092220926
static ZEND_VM_HOT ZEND_OPCODE_HANDLER_RET ZEND_FASTCALL ZEND_QM_ASSIGN_SPEC_VAR_HANDLER(ZEND_OPCODE_HANDLER_ARGS)
2092320927
{
2092420928
USE_OPLINE
@@ -36582,6 +36586,15 @@ static ZEND_VM_HOT ZEND_OPCODE_HANDLER_RET ZEND_FASTCALL ZEND_JMP_NULL_SPEC_CV_H
3658236586
if (Z_TYPE_INFO_P(val) != IS_NULL) {
3658336587
ZEND_VM_NEXT_OPCODE();
3658436588
} else {
36589+
zval *result = EX_VAR(opline->result.var);
36590+
36591+
if (EXPECTED(opline->extended_value == 0)) {
36592+
ZVAL_NULL(result);
36593+
} else if (EXPECTED(opline->extended_value == (1 << 0))) {
36594+
ZVAL_BOOL(result, 0);
36595+
} else if (EXPECTED(opline->extended_value == (1 << 1))) {
36596+
ZVAL_BOOL(result, 1);
36597+
}
3658536598

3658636599
ZEND_VM_JMP_EX(OP_JMP_ADDR(opline, opline->op2), 0);
3658736600
}
@@ -50981,8 +50994,8 @@ ZEND_API void execute_ex(zend_execute_data *ex)
5098150994
(void*)&&ZEND_NULL_LABEL,
5098250995
(void*)&&ZEND_ARRAY_KEY_EXISTS_SPEC_CV_CV_LABEL,
5098350996
(void*)&&ZEND_JMP_NULL_SPEC_CONST_LABEL,
50984-
(void*)&&ZEND_JMP_NULL_SPEC_TMP_LABEL,
50985-
(void*)&&ZEND_JMP_NULL_SPEC_VAR_LABEL,
50997+
(void*)&&ZEND_JMP_NULL_SPEC_TMPVAR_LABEL,
50998+
(void*)&&ZEND_JMP_NULL_SPEC_TMPVAR_LABEL,
5098650999
(void*)&&ZEND_NULL_LABEL,
5098751000
(void*)&&ZEND_JMP_NULL_SPEC_CV_LABEL,
5098851001
(void*)&&ZEND_RECV_NOTYPE_SPEC_LABEL,
@@ -53477,6 +53490,10 @@ ZEND_API void execute_ex(zend_execute_data *ex)
5347753490
VM_TRACE(ZEND_INCLUDE_OR_EVAL_SPEC_TMPVAR)
5347853491
ZEND_INCLUDE_OR_EVAL_SPEC_TMPVAR_HANDLER(ZEND_OPCODE_HANDLER_ARGS_PASSTHRU);
5347953492
HYBRID_BREAK();
53493+
HYBRID_CASE(ZEND_JMP_NULL_SPEC_TMPVAR):
53494+
VM_TRACE(ZEND_JMP_NULL_SPEC_TMPVAR)
53495+
ZEND_JMP_NULL_SPEC_TMPVAR_HANDLER(ZEND_OPCODE_HANDLER_ARGS_PASSTHRU);
53496+
HYBRID_BREAK();
5348053497
HYBRID_CASE(ZEND_YIELD_FROM_SPEC_TMPVAR):
5348153498
VM_TRACE(ZEND_YIELD_FROM_SPEC_TMPVAR)
5348253499
ZEND_YIELD_FROM_SPEC_TMPVAR_HANDLER(ZEND_OPCODE_HANDLER_ARGS_PASSTHRU);
@@ -53898,10 +53915,6 @@ ZEND_API void execute_ex(zend_execute_data *ex)
5389853915
VM_TRACE(ZEND_COALESCE_SPEC_TMP)
5389953916
ZEND_COALESCE_SPEC_TMP_HANDLER(ZEND_OPCODE_HANDLER_ARGS_PASSTHRU);
5390053917
HYBRID_BREAK();
53901-
HYBRID_CASE(ZEND_JMP_NULL_SPEC_TMP):
53902-
VM_TRACE(ZEND_JMP_NULL_SPEC_TMP)
53903-
ZEND_JMP_NULL_SPEC_TMP_HANDLER(ZEND_OPCODE_HANDLER_ARGS_PASSTHRU);
53904-
HYBRID_BREAK();
5390553918
HYBRID_CASE(ZEND_QM_ASSIGN_SPEC_TMP):
5390653919
VM_TRACE(ZEND_QM_ASSIGN_SPEC_TMP)
5390753920
ZEND_QM_ASSIGN_SPEC_TMP_HANDLER(ZEND_OPCODE_HANDLER_ARGS_PASSTHRU);
@@ -54207,10 +54220,6 @@ ZEND_API void execute_ex(zend_execute_data *ex)
5420754220
VM_TRACE(ZEND_COALESCE_SPEC_VAR)
5420854221
ZEND_COALESCE_SPEC_VAR_HANDLER(ZEND_OPCODE_HANDLER_ARGS_PASSTHRU);
5420954222
HYBRID_BREAK();
54210-
HYBRID_CASE(ZEND_JMP_NULL_SPEC_VAR):
54211-
VM_TRACE(ZEND_JMP_NULL_SPEC_VAR)
54212-
ZEND_JMP_NULL_SPEC_VAR_HANDLER(ZEND_OPCODE_HANDLER_ARGS_PASSTHRU);
54213-
HYBRID_BREAK();
5421454223
HYBRID_CASE(ZEND_QM_ASSIGN_SPEC_VAR):
5421554224
VM_TRACE(ZEND_QM_ASSIGN_SPEC_VAR)
5421654225
ZEND_QM_ASSIGN_SPEC_VAR_HANDLER(ZEND_OPCODE_HANDLER_ARGS_PASSTHRU);
@@ -58546,8 +58555,8 @@ void zend_vm_init(void)
5854658555
ZEND_NULL_HANDLER,
5854758556
ZEND_ARRAY_KEY_EXISTS_SPEC_CV_CV_HANDLER,
5854858557
ZEND_JMP_NULL_SPEC_CONST_HANDLER,
58549-
ZEND_JMP_NULL_SPEC_TMP_HANDLER,
58550-
ZEND_JMP_NULL_SPEC_VAR_HANDLER,
58558+
ZEND_JMP_NULL_SPEC_TMPVAR_HANDLER,
58559+
ZEND_JMP_NULL_SPEC_TMPVAR_HANDLER,
5855158560
ZEND_NULL_HANDLER,
5855258561
ZEND_JMP_NULL_SPEC_CV_HANDLER,
5855358562
ZEND_RECV_NOTYPE_SPEC_HANDLER,

Zend/zend_vm_handlers.h

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1288,8 +1288,8 @@
12881288
_(2312, ZEND_ARRAY_KEY_EXISTS_SPEC_CV_TMPVAR) \
12891289
_(2314, ZEND_ARRAY_KEY_EXISTS_SPEC_CV_CV) \
12901290
_(2315, ZEND_JMP_NULL_SPEC_CONST) \
1291-
_(2316, ZEND_JMP_NULL_SPEC_TMP) \
1292-
_(2317, ZEND_JMP_NULL_SPEC_VAR) \
1291+
_(2316, ZEND_JMP_NULL_SPEC_TMPVAR) \
1292+
_(2317, ZEND_JMP_NULL_SPEC_TMPVAR) \
12931293
_(2319, ZEND_JMP_NULL_SPEC_CV) \
12941294
_(2320, ZEND_RECV_NOTYPE_SPEC) \
12951295
_(2321, ZEND_JMP_FORWARD_SPEC) \

Zend/zend_vm_opcodes.c

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -417,7 +417,7 @@ static uint32_t zend_vm_opcodes_flags[196] = {
417417
0x00000101,
418418
0x00000103,
419419
0x00000707,
420-
0x00002003,
420+
0x00002007,
421421
};
422422

423423
ZEND_API const char* ZEND_FASTCALL zend_get_opcode_name(zend_uchar opcode) {

0 commit comments

Comments
 (0)