Skip to content

Commit 7a4e7d5

Browse files
committed
Fix nullsafe operator on delayed oplines
1 parent 517c993 commit 7a4e7d5

File tree

8 files changed

+150
-5
lines changed

8 files changed

+150
-5
lines changed

Zend/tests/nullsafe_operator/034.phpt

Lines changed: 28 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,28 @@
1+
--TEST--
2+
Test nullsafe operator on delayed dim
3+
--FILE--
4+
<?php
5+
6+
$arr = [
7+
'foo' => null,
8+
'bar' => [
9+
'baz' => null,
10+
],
11+
];
12+
13+
var_dump($arr['foo']?->something);
14+
var_dump($arr['invalid']?->something);
15+
16+
var_dump($arr['bar']['baz']?->something);
17+
var_dump($arr['bar']['invalid']?->something);
18+
19+
?>
20+
--EXPECTF--
21+
NULL
22+
23+
Warning: Undefined array key "invalid" in %s.php on line 11
24+
NULL
25+
NULL
26+
27+
Warning: Undefined array key "invalid" in %s.php on line 14
28+
NULL

Zend/tests/nullsafe_operator/035.phpt

Lines changed: 27 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,27 @@
1+
--TEST--
2+
Test nullsafe operator on delayed var
3+
--FILE--
4+
<?php
5+
6+
class Foo {
7+
public ?Bar $bar;
8+
}
9+
10+
class Bar {
11+
public string $baz;
12+
}
13+
14+
$foo = new Foo();
15+
16+
$foo->bar = null;
17+
var_dump($foo->bar?->baz);
18+
19+
$bar = new Bar();
20+
$bar->baz = 'baz';
21+
$foo->bar = $bar;
22+
var_dump($foo->bar?->baz);
23+
24+
?>
25+
--EXPECT--
26+
NULL
27+
string(3) "baz"

Zend/tests/nullsafe_operator/036.phpt

Lines changed: 28 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,28 @@
1+
--TEST--
2+
Test nullsafe method call on delayed var
3+
--FILE--
4+
<?php
5+
6+
class Foo {
7+
public ?Bar $bar;
8+
}
9+
10+
class Bar {
11+
public function baz() {
12+
return 'baz';
13+
}
14+
}
15+
16+
$foo = new Foo();
17+
18+
$foo->bar = null;
19+
var_dump($foo->bar?->baz());
20+
21+
$bar = new Bar();
22+
$foo->bar = $bar;
23+
var_dump($foo->bar?->baz());
24+
25+
?>
26+
--EXPECT--
27+
NULL
28+
string(3) "baz"

Zend/tests/nullsafe_operator/037.phpt

Lines changed: 28 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,28 @@
1+
--TEST--
2+
Test nullsafe method call on delayed var
3+
--FILE--
4+
<?php
5+
6+
class Foo {
7+
public ?Bar $bar;
8+
}
9+
10+
class Bar {
11+
public function baz() {
12+
return 'baz';
13+
}
14+
}
15+
16+
$foo = new Foo();
17+
18+
$foo->bar = null;
19+
var_dump($foo?->bar?->baz());
20+
21+
$bar = new Bar();
22+
$foo->bar = $bar;
23+
var_dump($foo?->bar?->baz());
24+
25+
?>
26+
--EXPECT--
27+
NULL
28+
string(3) "baz"

Zend/tests/nullsafe_operator/038.phpt

Lines changed: 15 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,15 @@
1+
--TEST--
2+
Test nullsafe operator in netsted delayed dims
3+
--FILE--
4+
<?php
5+
6+
$foo = new stdClass();
7+
$foo->bar = 'bar';
8+
9+
$array = ['foo' => ['bar' => 'baz']];
10+
11+
var_dump($array['foo'][$foo?->bar]);
12+
13+
?>
14+
--EXPECT--
15+
string(3) "baz"

Zend/zend.c

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1303,6 +1303,7 @@ static ZEND_COLD void zend_error_impl(
13031303
zend_class_entry *saved_class_entry;
13041304
zend_stack loop_var_stack;
13051305
zend_stack delayed_oplines_stack;
1306+
zend_stack delayed_oplines_offset_stack;
13061307
int type = orig_type & E_ALL;
13071308

13081309
/* Report about uncaught exception in case of fatal errors */
@@ -1381,6 +1382,7 @@ static ZEND_COLD void zend_error_impl(
13811382
CG(active_class_entry) = NULL;
13821383
SAVE_STACK(loop_var_stack);
13831384
SAVE_STACK(delayed_oplines_stack);
1385+
SAVE_STACK(delayed_oplines_offset_stack);
13841386
CG(in_compilation) = 0;
13851387
}
13861388

@@ -1400,6 +1402,7 @@ static ZEND_COLD void zend_error_impl(
14001402
CG(active_class_entry) = saved_class_entry;
14011403
RESTORE_STACK(loop_var_stack);
14021404
RESTORE_STACK(delayed_oplines_stack);
1405+
RESTORE_STACK(delayed_oplines_offset_stack);
14031406
CG(in_compilation) = 1;
14041407
}
14051408

Zend/zend_compile.c

Lines changed: 20 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -368,6 +368,7 @@ void zend_init_compiler_data_structures(void) /* {{{ */
368368
{
369369
zend_stack_init(&CG(loop_var_stack), sizeof(zend_loop_var));
370370
zend_stack_init(&CG(delayed_oplines_stack), sizeof(zend_op));
371+
zend_stack_init(&CG(delayed_oplines_offset_stack), sizeof(uint32_t));
371372
zend_stack_init(&CG(short_circuiting_opnums), sizeof(uint32_t));
372373
CG(active_class_entry) = NULL;
373374
CG(in_compilation) = 0;
@@ -422,6 +423,7 @@ void shutdown_compiler(void) /* {{{ */
422423
{
423424
zend_stack_destroy(&CG(loop_var_stack));
424425
zend_stack_destroy(&CG(delayed_oplines_stack));
426+
zend_stack_destroy(&CG(delayed_oplines_offset_stack));
425427
zend_stack_destroy(&CG(short_circuiting_opnums));
426428
zend_hash_destroy(&CG(filenames_table));
427429
zend_arena_destroy(CG(arena));
@@ -2208,7 +2210,9 @@ static inline zend_op *zend_delayed_emit_op(znode *result, zend_uchar opcode, zn
22082210

22092211
static inline uint32_t zend_delayed_compile_begin(void) /* {{{ */
22102212
{
2211-
return zend_stack_count(&CG(delayed_oplines_stack));
2213+
uint32_t offset = zend_stack_count(&CG(delayed_oplines_stack));
2214+
zend_stack_push(&CG(delayed_oplines_offset_stack), &offset);
2215+
return offset;
22122216
}
22132217
/* }}} */
22142218

@@ -2223,6 +2227,7 @@ static zend_op *zend_delayed_compile_end(uint32_t offset) /* {{{ */
22232227
memcpy(opline, &oplines[i], sizeof(zend_op));
22242228
}
22252229
CG(delayed_oplines_stack).top = offset;
2230+
CG(delayed_oplines_offset_stack).top -= 1;
22262231
return opline;
22272232
}
22282233
/* }}} */
@@ -2305,10 +2310,20 @@ static void zend_short_circuiting_commit(uint32_t checkpoint, znode *result, zen
23052310
}
23062311
}
23072312

2308-
static void zend_emit_jmp_null(znode *obj_node)
2313+
static void zend_emit_jmp_null(znode *obj_node, zend_bool delay)
23092314
{
23102315
uint32_t jmp_null_opnum = get_next_op_number();
2311-
zend_op *opline = zend_emit_op(NULL, ZEND_JMP_NULL, obj_node, NULL);
2316+
zend_op *opline;
2317+
2318+
if (delay) {
2319+
uint32_t delayed_oplines_count = zend_stack_count(&CG(delayed_oplines_stack));
2320+
uint32_t delayed_oplines_offset = *(uint32_t *)zend_stack_top(&CG(delayed_oplines_offset_stack));
2321+
jmp_null_opnum += (delayed_oplines_count - delayed_oplines_offset);
2322+
opline = zend_delayed_emit_op(NULL, ZEND_JMP_NULL, obj_node, NULL);
2323+
} else {
2324+
opline = zend_emit_op(NULL, ZEND_JMP_NULL, obj_node, NULL);
2325+
}
2326+
23122327
if (opline->op1_type == IS_CONST) {
23132328
Z_TRY_ADDREF_P(CT_CONSTANT(opline->op1));
23142329
}
@@ -2796,7 +2811,7 @@ static zend_op *zend_delayed_compile_prop(znode *result, zend_ast *ast, uint32_t
27962811
opline = zend_delayed_compile_var(&obj_node, obj_ast, type, 0);
27972812
zend_separate_if_call_and_write(&obj_node, obj_ast, type);
27982813
if (nullsafe) {
2799-
zend_emit_jmp_null(&obj_node);
2814+
zend_emit_jmp_null(&obj_node, 1);
28002815
}
28012816
}
28022817

@@ -4356,7 +4371,7 @@ void zend_compile_method_call(znode *result, zend_ast *ast, uint32_t type) /* {{
43564371
zend_short_circuiting_mark_inner(obj_ast);
43574372
zend_compile_expr(&obj_node, obj_ast);
43584373
if (nullsafe) {
4359-
zend_emit_jmp_null(&obj_node);
4374+
zend_emit_jmp_null(&obj_node, 0);
43604375
}
43614376
}
43624377

Zend/zend_globals.h

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -119,6 +119,7 @@ struct _zend_compiler_globals {
119119
zend_arena *ast_arena;
120120

121121
zend_stack delayed_oplines_stack;
122+
zend_stack delayed_oplines_offset_stack;
122123
HashTable *memoized_exprs;
123124
int memoize_mode;
124125

0 commit comments

Comments
 (0)