Skip to content

Commit

Permalink
Make JMP_NULL instruction return the NULL value
Browse files Browse the repository at this point in the history
Or false/true for isset/empty respectively
  • Loading branch information
iluuu1994 committed May 29, 2020
1 parent 40cb1cf commit 4b04e3b
Show file tree
Hide file tree
Showing 7 changed files with 104 additions and 83 deletions.
5 changes: 5 additions & 0 deletions Zend/tests/nullsafe_method_call/008.phpt
Original file line number Diff line number Diff line change
@@ -1,8 +1,13 @@
--TEST--
Test nullsafe property coalesce assignment error
--SKIPIF--
<?php if(extension_loaded("Zend OPcache")) die("skip with opcache because SSA incorrectly optimized"); ?>
--FILE--
<?php

$foo = null;
var_dump($foo?->bar ??= 'bar');

class Foo {
public $bar;
}
Expand Down
17 changes: 17 additions & 0 deletions Zend/tests/nullsafe_method_call/012.phpt
Original file line number Diff line number Diff line change
@@ -0,0 +1,17 @@
--TEST--
Test nullsafe property on reference
--FILE--
<?php

class Foo {
public $bar;
}

$foo = new Foo();
$foo->bar = 'bar';

$fooRef = &$foo;
var_dump($fooRef?->bar);

--EXPECT--
string(3) "bar"
54 changes: 17 additions & 37 deletions Zend/zend_compile.c
Original file line number Diff line number Diff line change
Expand Up @@ -63,7 +63,7 @@ typedef struct _zend_loop_var {
#define ZEND_SHORT_CIRCUITING_SCOPE_EMPTY (1 << 1)

typedef struct _zend_short_circuiting_scope {
zend_stack jump_opnums;
zend_stack jmp_null_oplines;
zend_bool flags;
} zend_short_circuiting_scope;

Expand Down Expand Up @@ -2249,48 +2249,27 @@ static void zend_begin_short_circuiting_scope()
CG(short_circuiting) = 1;

zend_short_circuiting_scope scope;
zend_stack_init(&scope.jump_opnums, sizeof(uint32_t));
zend_stack_init(&scope.jmp_null_oplines, sizeof(zend_op*));
scope.flags = 0;
zend_stack_push(&CG(short_circuiting_scopes), &scope);
}

static void zend_end_short_circuiting_scope(znode *result)
{
zend_short_circuiting_scope *scope = zend_current_short_circuiting_scope();
zend_stack *labels = &scope->jump_opnums;
zend_bool contains_short_circuiting_operators = !zend_stack_is_empty(labels);
zend_stack *jmp_null_oplines = &scope->jmp_null_oplines;

uint32_t end_label = 0;
if (contains_short_circuiting_operators) {
end_label = zend_emit_jump(0);

while (!zend_stack_is_empty(labels)) {
uint32_t label = *(uint32_t *)zend_stack_top(labels);
zend_update_jump_target_to_next(label);
zend_stack_del_top(labels);
}

znode result_node;
result_node.op_type = IS_CONST;

if (scope->flags & ZEND_SHORT_CIRCUITING_SCOPE_ISSET) {
ZVAL_BOOL(&result_node.u.constant, 0);
} else if (scope->flags & ZEND_SHORT_CIRCUITING_SCOPE_EMPTY) {
ZVAL_BOOL(&result_node.u.constant, 1);
} else {
ZVAL_NULL(&result_node.u.constant);
}

zend_op *opline_call = zend_emit_op_tmp(NULL, ZEND_QM_ASSIGN, &result_node, NULL);
SET_NODE(opline_call->result, result);

zend_update_jump_target_to_next(end_label);
while (!zend_stack_is_empty(jmp_null_oplines)) {
zend_op *jmp_null_opline = *(zend_op **)zend_stack_top(jmp_null_oplines);
jmp_null_opline->op2.opline_num = get_next_op_number();
SET_NODE(jmp_null_opline->result, result);
jmp_null_opline->extended_value = scope->flags;
zend_stack_del_top(jmp_null_oplines);
}

CG(short_circuiting) = 0;

zend_stack_del_top(&CG(short_circuiting_scopes));
zend_stack_destroy(labels);
zend_stack_destroy(jmp_null_oplines);
}

#define ZEND_MEMOIZE_NONE 0
Expand Down Expand Up @@ -2775,11 +2754,11 @@ static zend_op *zend_delayed_compile_prop(znode *result, zend_ast *ast, uint32_t
}

if (nullsafe) {
uint32_t jmp_null = zend_emit_cond_jump(ZEND_JMP_NULL, &obj_node, 0);
zend_op *jmp_null_opline = zend_emit_op(NULL, ZEND_JMP_NULL, &obj_node, NULL);

zend_short_circuiting_scope *short_circuiting_scope = zend_current_short_circuiting_scope();
zend_stack *short_circuiting_scopes = &short_circuiting_scope->jump_opnums;
zend_stack_push(short_circuiting_scopes, &jmp_null);
zend_stack *jmp_null_oplines = &short_circuiting_scope->jmp_null_oplines;
zend_stack_push(jmp_null_oplines, &jmp_null_opline);
}

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

zend_adjust_for_fetch_type(opline, result, type);

return opline;
}
/* }}} */
Expand Down Expand Up @@ -4251,11 +4231,11 @@ void zend_compile_method_call(znode *result, zend_ast *ast, uint32_t type) /* {{
}

if (nullsafe) {
uint32_t jmp_null = zend_emit_cond_jump(ZEND_JMP_NULL, &obj_node, 0);
zend_op *jmp_null_opline = zend_emit_op(NULL, ZEND_JMP_NULL, &obj_node, NULL);

zend_short_circuiting_scope *short_circuiting_scope = zend_current_short_circuiting_scope();
zend_stack *short_circuiting_scopes = &short_circuiting_scope->jump_opnums;
zend_stack_push(short_circuiting_scopes, &jmp_null);
zend_stack *jmp_null_oplines = &short_circuiting_scope->jmp_null_oplines;
zend_stack_push(jmp_null_oplines, &jmp_null_opline);
}

zend_compile_expr(&method_node, method_ast);
Expand Down
12 changes: 11 additions & 1 deletion Zend/zend_vm_def.h
Original file line number Diff line number Diff line change
Expand Up @@ -7032,7 +7032,7 @@ ZEND_VM_COLD_CONST_HANDLER(169, ZEND_COALESCE, CONST|TMP|VAR|CV, JMP_ADDR)
ZEND_VM_NEXT_OPCODE();
}

ZEND_VM_HOT_NOCONST_HANDLER(195, ZEND_JMP_NULL, CONST|TMP|VAR|CV, JMP_ADDR)
ZEND_VM_HOT_NOCONST_HANDLER(195, ZEND_JMP_NULL, CONST|TMPVAR|CV, JMP_ADDR)
{
USE_OPLINE
zval *val;
Expand All @@ -7042,6 +7042,16 @@ ZEND_VM_HOT_NOCONST_HANDLER(195, ZEND_JMP_NULL, CONST|TMP|VAR|CV, JMP_ADDR)
if (Z_TYPE_INFO_P(val) != IS_NULL) {
ZEND_VM_NEXT_OPCODE();
} else {
zval *result = EX_VAR(opline->result.var);

if (EXPECTED(opline->extended_value == 0)) {
ZVAL_NULL(result);
} else if (EXPECTED(opline->extended_value == (1 << 0))) {
ZVAL_BOOL(result, 0);
} else if (EXPECTED(opline->extended_value == (1 << 1))) {
ZVAL_BOOL(result, 1);
}

FREE_OP1();
ZEND_VM_JMP_EX(OP_JMP_ADDR(opline, opline->op2), 0);
}
Expand Down
93 changes: 51 additions & 42 deletions Zend/zend_vm_execute.h
Original file line number Diff line number Diff line change
Expand Up @@ -4292,6 +4292,15 @@ static ZEND_VM_COLD ZEND_OPCODE_HANDLER_RET ZEND_FASTCALL ZEND_JMP_NULL_SPEC_CON
if (Z_TYPE_INFO_P(val) != IS_NULL) {
ZEND_VM_NEXT_OPCODE();
} else {
zval *result = EX_VAR(opline->result.var);

if (EXPECTED(opline->extended_value == 0)) {
ZVAL_NULL(result);
} else if (EXPECTED(opline->extended_value == (1 << 0))) {
ZVAL_BOOL(result, 0);
} else if (EXPECTED(opline->extended_value == (1 << 1))) {
ZVAL_BOOL(result, 1);
}

ZEND_VM_JMP_EX(OP_JMP_ADDR(opline, opline->op2), 0);
}
Expand Down Expand Up @@ -13183,6 +13192,31 @@ static ZEND_OPCODE_HANDLER_RET ZEND_FASTCALL ZEND_INCLUDE_OR_EVAL_SPEC_TMPVAR_HA
ZEND_VM_NEXT_OPCODE();
}

static ZEND_VM_HOT ZEND_OPCODE_HANDLER_RET ZEND_FASTCALL ZEND_JMP_NULL_SPEC_TMPVAR_HANDLER(ZEND_OPCODE_HANDLER_ARGS)
{
USE_OPLINE
zval *val;

val = _get_zval_ptr_var(opline->op1.var EXECUTE_DATA_CC);

if (Z_TYPE_INFO_P(val) != IS_NULL) {
ZEND_VM_NEXT_OPCODE();
} else {
zval *result = EX_VAR(opline->result.var);

if (EXPECTED(opline->extended_value == 0)) {
ZVAL_NULL(result);
} else if (EXPECTED(opline->extended_value == (1 << 0))) {
ZVAL_BOOL(result, 0);
} else if (EXPECTED(opline->extended_value == (1 << 1))) {
ZVAL_BOOL(result, 1);
}

zval_ptr_dtor_nogc(EX_VAR(opline->op1.var));
ZEND_VM_JMP_EX(OP_JMP_ADDR(opline, opline->op2), 0);
}
}

static ZEND_OPCODE_HANDLER_RET ZEND_FASTCALL ZEND_YIELD_FROM_SPEC_TMPVAR_HANDLER(ZEND_OPCODE_HANDLER_ARGS)
{
USE_OPLINE
Expand Down Expand Up @@ -17907,21 +17941,6 @@ static ZEND_OPCODE_HANDLER_RET ZEND_FASTCALL ZEND_COALESCE_SPEC_TMP_HANDLER(ZEND
ZEND_VM_NEXT_OPCODE();
}

static ZEND_VM_HOT ZEND_OPCODE_HANDLER_RET ZEND_FASTCALL ZEND_JMP_NULL_SPEC_TMP_HANDLER(ZEND_OPCODE_HANDLER_ARGS)
{
USE_OPLINE
zval *val;

val = _get_zval_ptr_tmp(opline->op1.var EXECUTE_DATA_CC);

if (Z_TYPE_INFO_P(val) != IS_NULL) {
ZEND_VM_NEXT_OPCODE();
} else {
zval_ptr_dtor_nogc(EX_VAR(opline->op1.var));
ZEND_VM_JMP_EX(OP_JMP_ADDR(opline, opline->op2), 0);
}
}

static ZEND_VM_HOT ZEND_OPCODE_HANDLER_RET ZEND_FASTCALL ZEND_QM_ASSIGN_SPEC_TMP_HANDLER(ZEND_OPCODE_HANDLER_ARGS)
{
USE_OPLINE
Expand Down Expand Up @@ -20904,21 +20923,6 @@ static ZEND_OPCODE_HANDLER_RET ZEND_FASTCALL ZEND_COALESCE_SPEC_VAR_HANDLER(ZEND
ZEND_VM_NEXT_OPCODE();
}

static ZEND_VM_HOT ZEND_OPCODE_HANDLER_RET ZEND_FASTCALL ZEND_JMP_NULL_SPEC_VAR_HANDLER(ZEND_OPCODE_HANDLER_ARGS)
{
USE_OPLINE
zval *val;

val = _get_zval_ptr_var(opline->op1.var EXECUTE_DATA_CC);

if (Z_TYPE_INFO_P(val) != IS_NULL) {
ZEND_VM_NEXT_OPCODE();
} else {
zval_ptr_dtor_nogc(EX_VAR(opline->op1.var));
ZEND_VM_JMP_EX(OP_JMP_ADDR(opline, opline->op2), 0);
}
}

static ZEND_VM_HOT ZEND_OPCODE_HANDLER_RET ZEND_FASTCALL ZEND_QM_ASSIGN_SPEC_VAR_HANDLER(ZEND_OPCODE_HANDLER_ARGS)
{
USE_OPLINE
Expand Down Expand Up @@ -36582,6 +36586,15 @@ static ZEND_VM_HOT ZEND_OPCODE_HANDLER_RET ZEND_FASTCALL ZEND_JMP_NULL_SPEC_CV_H
if (Z_TYPE_INFO_P(val) != IS_NULL) {
ZEND_VM_NEXT_OPCODE();
} else {
zval *result = EX_VAR(opline->result.var);

if (EXPECTED(opline->extended_value == 0)) {
ZVAL_NULL(result);
} else if (EXPECTED(opline->extended_value == (1 << 0))) {
ZVAL_BOOL(result, 0);
} else if (EXPECTED(opline->extended_value == (1 << 1))) {
ZVAL_BOOL(result, 1);
}

ZEND_VM_JMP_EX(OP_JMP_ADDR(opline, opline->op2), 0);
}
Expand Down Expand Up @@ -50981,8 +50994,8 @@ ZEND_API void execute_ex(zend_execute_data *ex)
(void*)&&ZEND_NULL_LABEL,
(void*)&&ZEND_ARRAY_KEY_EXISTS_SPEC_CV_CV_LABEL,
(void*)&&ZEND_JMP_NULL_SPEC_CONST_LABEL,
(void*)&&ZEND_JMP_NULL_SPEC_TMP_LABEL,
(void*)&&ZEND_JMP_NULL_SPEC_VAR_LABEL,
(void*)&&ZEND_JMP_NULL_SPEC_TMPVAR_LABEL,
(void*)&&ZEND_JMP_NULL_SPEC_TMPVAR_LABEL,
(void*)&&ZEND_NULL_LABEL,
(void*)&&ZEND_JMP_NULL_SPEC_CV_LABEL,
(void*)&&ZEND_RECV_NOTYPE_SPEC_LABEL,
Expand Down Expand Up @@ -53477,6 +53490,10 @@ ZEND_API void execute_ex(zend_execute_data *ex)
VM_TRACE(ZEND_INCLUDE_OR_EVAL_SPEC_TMPVAR)
ZEND_INCLUDE_OR_EVAL_SPEC_TMPVAR_HANDLER(ZEND_OPCODE_HANDLER_ARGS_PASSTHRU);
HYBRID_BREAK();
HYBRID_CASE(ZEND_JMP_NULL_SPEC_TMPVAR):
VM_TRACE(ZEND_JMP_NULL_SPEC_TMPVAR)
ZEND_JMP_NULL_SPEC_TMPVAR_HANDLER(ZEND_OPCODE_HANDLER_ARGS_PASSTHRU);
HYBRID_BREAK();
HYBRID_CASE(ZEND_YIELD_FROM_SPEC_TMPVAR):
VM_TRACE(ZEND_YIELD_FROM_SPEC_TMPVAR)
ZEND_YIELD_FROM_SPEC_TMPVAR_HANDLER(ZEND_OPCODE_HANDLER_ARGS_PASSTHRU);
Expand Down Expand Up @@ -53898,10 +53915,6 @@ ZEND_API void execute_ex(zend_execute_data *ex)
VM_TRACE(ZEND_COALESCE_SPEC_TMP)
ZEND_COALESCE_SPEC_TMP_HANDLER(ZEND_OPCODE_HANDLER_ARGS_PASSTHRU);
HYBRID_BREAK();
HYBRID_CASE(ZEND_JMP_NULL_SPEC_TMP):
VM_TRACE(ZEND_JMP_NULL_SPEC_TMP)
ZEND_JMP_NULL_SPEC_TMP_HANDLER(ZEND_OPCODE_HANDLER_ARGS_PASSTHRU);
HYBRID_BREAK();
HYBRID_CASE(ZEND_QM_ASSIGN_SPEC_TMP):
VM_TRACE(ZEND_QM_ASSIGN_SPEC_TMP)
ZEND_QM_ASSIGN_SPEC_TMP_HANDLER(ZEND_OPCODE_HANDLER_ARGS_PASSTHRU);
Expand Down Expand Up @@ -54207,10 +54220,6 @@ ZEND_API void execute_ex(zend_execute_data *ex)
VM_TRACE(ZEND_COALESCE_SPEC_VAR)
ZEND_COALESCE_SPEC_VAR_HANDLER(ZEND_OPCODE_HANDLER_ARGS_PASSTHRU);
HYBRID_BREAK();
HYBRID_CASE(ZEND_JMP_NULL_SPEC_VAR):
VM_TRACE(ZEND_JMP_NULL_SPEC_VAR)
ZEND_JMP_NULL_SPEC_VAR_HANDLER(ZEND_OPCODE_HANDLER_ARGS_PASSTHRU);
HYBRID_BREAK();
HYBRID_CASE(ZEND_QM_ASSIGN_SPEC_VAR):
VM_TRACE(ZEND_QM_ASSIGN_SPEC_VAR)
ZEND_QM_ASSIGN_SPEC_VAR_HANDLER(ZEND_OPCODE_HANDLER_ARGS_PASSTHRU);
Expand Down Expand Up @@ -58546,8 +58555,8 @@ void zend_vm_init(void)
ZEND_NULL_HANDLER,
ZEND_ARRAY_KEY_EXISTS_SPEC_CV_CV_HANDLER,
ZEND_JMP_NULL_SPEC_CONST_HANDLER,
ZEND_JMP_NULL_SPEC_TMP_HANDLER,
ZEND_JMP_NULL_SPEC_VAR_HANDLER,
ZEND_JMP_NULL_SPEC_TMPVAR_HANDLER,
ZEND_JMP_NULL_SPEC_TMPVAR_HANDLER,
ZEND_NULL_HANDLER,
ZEND_JMP_NULL_SPEC_CV_HANDLER,
ZEND_RECV_NOTYPE_SPEC_HANDLER,
Expand Down
4 changes: 2 additions & 2 deletions Zend/zend_vm_handlers.h
Original file line number Diff line number Diff line change
Expand Up @@ -1288,8 +1288,8 @@
_(2312, ZEND_ARRAY_KEY_EXISTS_SPEC_CV_TMPVAR) \
_(2314, ZEND_ARRAY_KEY_EXISTS_SPEC_CV_CV) \
_(2315, ZEND_JMP_NULL_SPEC_CONST) \
_(2316, ZEND_JMP_NULL_SPEC_TMP) \
_(2317, ZEND_JMP_NULL_SPEC_VAR) \
_(2316, ZEND_JMP_NULL_SPEC_TMPVAR) \
_(2317, ZEND_JMP_NULL_SPEC_TMPVAR) \
_(2319, ZEND_JMP_NULL_SPEC_CV) \
_(2320, ZEND_RECV_NOTYPE_SPEC) \
_(2321, ZEND_JMP_FORWARD_SPEC) \
Expand Down
2 changes: 1 addition & 1 deletion Zend/zend_vm_opcodes.c
Original file line number Diff line number Diff line change
Expand Up @@ -417,7 +417,7 @@ static uint32_t zend_vm_opcodes_flags[196] = {
0x00000101,
0x00000103,
0x00000707,
0x00002003,
0x00002007,
};

ZEND_API const char* ZEND_FASTCALL zend_get_opcode_name(zend_uchar opcode) {
Expand Down

0 comments on commit 4b04e3b

Please sign in to comment.