Skip to content

Commit 40cb1cf

Browse files
committed
Implement new ZEND_JMP_NULL instruction
1 parent a40714f commit 40cb1cf

File tree

8 files changed

+637
-532
lines changed

8 files changed

+637
-532
lines changed

Zend/tests/nullsafe_method_call/011.phpt

Lines changed: 5 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -23,14 +23,11 @@ $foo->bar = 'bar';
2323
var_dump(isset($foo?->bar));
2424
var_dump(empty($foo?->bar));
2525

26-
--EXPECTF--
27-
Warning: Undefined variable $foo in %s011.php on line 8
28-
NULL
29-
30-
Warning: Undefined variable $foo in %s011.php on line 9
31-
NULL
32-
NULL
33-
NULL
26+
--EXPECT--
27+
bool(false)
28+
bool(true)
29+
bool(false)
30+
bool(true)
3431
bool(false)
3532
bool(true)
3633
bool(true)

Zend/zend_compile.c

Lines changed: 21 additions & 23 deletions
Original file line numberDiff line numberDiff line change
@@ -64,7 +64,7 @@ typedef struct _zend_loop_var {
6464

6565
typedef struct _zend_short_circuiting_scope {
6666
zend_stack jump_opnums;
67-
zend_bool skip;
67+
zend_bool flags;
6868
} zend_short_circuiting_scope;
6969

7070
static inline uint32_t zend_alloc_cache_slots(unsigned count) {
@@ -2177,6 +2177,7 @@ static inline void zend_update_jump_target(uint32_t opnum_jump, uint32_t opnum_t
21772177
case ZEND_JMPNZ_EX:
21782178
case ZEND_JMP_SET:
21792179
case ZEND_COALESCE:
2180+
case ZEND_JMP_NULL:
21802181
opline->op2.opline_num = opnum_target;
21812182
break;
21822183
EMPTY_SWITCH_DEFAULT_CASE()
@@ -2249,7 +2250,7 @@ static void zend_begin_short_circuiting_scope()
22492250

22502251
zend_short_circuiting_scope scope;
22512252
zend_stack_init(&scope.jump_opnums, sizeof(uint32_t));
2252-
scope.skip = 0;
2253+
scope.flags = 0;
22532254
zend_stack_push(&CG(short_circuiting_scopes), &scope);
22542255
}
22552256

@@ -2269,11 +2270,18 @@ static void zend_end_short_circuiting_scope(znode *result)
22692270
zend_stack_del_top(labels);
22702271
}
22712272

2272-
znode null_node;
2273-
null_node.op_type = IS_CONST;
2274-
ZVAL_NULL(&null_node.u.constant);
2273+
znode result_node;
2274+
result_node.op_type = IS_CONST;
22752275

2276-
zend_op *opline_call = zend_emit_op_tmp(NULL, ZEND_QM_ASSIGN, &null_node, NULL);
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);
22772285
SET_NODE(opline_call->result, result);
22782286

22792287
zend_update_jump_target_to_next(end_label);
@@ -2767,17 +2775,11 @@ static zend_op *zend_delayed_compile_prop(znode *result, zend_ast *ast, uint32_t
27672775
}
27682776

27692777
if (nullsafe) {
2770-
znode typecheck_node;
2771-
zend_op *typecheck_opline = zend_emit_op_tmp(&typecheck_node, ZEND_TYPE_CHECK, &obj_node, NULL);
2772-
typecheck_opline->extended_value = 1 << IS_NULL;
2773-
2774-
uint32_t jmp_call = zend_emit_cond_jump(ZEND_JMPZ, &typecheck_node, 0);
2778+
uint32_t jmp_null = zend_emit_cond_jump(ZEND_JMP_NULL, &obj_node, 0);
27752779

27762780
zend_short_circuiting_scope *short_circuiting_scope = zend_current_short_circuiting_scope();
27772781
zend_stack *short_circuiting_scopes = &short_circuiting_scope->jump_opnums;
2778-
uint32_t jmp_end = zend_emit_jump(0);
2779-
zend_stack_push(short_circuiting_scopes, &jmp_end);
2780-
zend_update_jump_target_to_next(jmp_call);
2782+
zend_stack_push(short_circuiting_scopes, &jmp_null);
27812783
}
27822784

27832785
zend_compile_expr(&prop_node, prop_ast);
@@ -4249,17 +4251,11 @@ void zend_compile_method_call(znode *result, zend_ast *ast, uint32_t type) /* {{
42494251
}
42504252

42514253
if (nullsafe) {
4252-
znode typecheck_node;
4253-
zend_op *typecheck_opline = zend_emit_op_tmp(&typecheck_node, ZEND_TYPE_CHECK, &obj_node, NULL);
4254-
typecheck_opline->extended_value = 1 << IS_NULL;
4255-
4256-
uint32_t jmp_call = zend_emit_cond_jump(ZEND_JMPZ, &typecheck_node, 0);
4254+
uint32_t jmp_null = zend_emit_cond_jump(ZEND_JMP_NULL, &obj_node, 0);
42574255

42584256
zend_short_circuiting_scope *short_circuiting_scope = zend_current_short_circuiting_scope();
42594257
zend_stack *short_circuiting_scopes = &short_circuiting_scope->jump_opnums;
4260-
uint32_t jmp_end = zend_emit_jump(0);
4261-
zend_stack_push(short_circuiting_scopes, &jmp_end);
4262-
zend_update_jump_target_to_next(jmp_call);
4258+
zend_stack_push(short_circuiting_scopes, &jmp_null);
42634259
}
42644260

42654261
zend_compile_expr(&method_node, method_ast);
@@ -8260,7 +8256,9 @@ void zend_compile_isset_or_empty(znode *result, zend_ast *ast) /* {{{ */
82608256
ZEND_ASSERT(ast->kind == ZEND_AST_ISSET || ast->kind == ZEND_AST_EMPTY);
82618257

82628258
zend_begin_short_circuiting_scope();
8263-
zend_current_short_circuiting_scope()->skip = 1;
8259+
zend_current_short_circuiting_scope()->flags = ast->kind == ZEND_AST_ISSET
8260+
? ZEND_SHORT_CIRCUITING_SCOPE_ISSET
8261+
: ZEND_SHORT_CIRCUITING_SCOPE_EMPTY;
82648262

82658263
if (!zend_is_variable(var_ast)) {
82668264
if (ast->kind == ZEND_AST_EMPTY) {

Zend/zend_opcode.c

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -987,6 +987,7 @@ ZEND_API int pass_two(zend_op_array *op_array)
987987
case ZEND_COALESCE:
988988
case ZEND_FE_RESET_R:
989989
case ZEND_FE_RESET_RW:
990+
case ZEND_JMP_NULL:
990991
ZEND_PASS_TWO_UPDATE_JMP_TARGET(op_array, opline, opline->op2);
991992
break;
992993
case ZEND_ASSERT_CHECK:

Zend/zend_vm_def.h

Lines changed: 15 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -7032,6 +7032,21 @@ 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)
7036+
{
7037+
USE_OPLINE
7038+
zval *val;
7039+
7040+
val = GET_OP1_ZVAL_PTR_UNDEF(BP_VAR_R);
7041+
7042+
if (Z_TYPE_INFO_P(val) != IS_NULL) {
7043+
ZEND_VM_NEXT_OPCODE();
7044+
} else {
7045+
FREE_OP1();
7046+
ZEND_VM_JMP_EX(OP_JMP_ADDR(opline, opline->op2), 0);
7047+
}
7048+
}
7049+
70357050
ZEND_VM_HOT_HANDLER(31, ZEND_QM_ASSIGN, CONST|TMP|VAR|CV, ANY)
70367051
{
70377052
USE_OPLINE

0 commit comments

Comments
 (0)