Skip to content

Commit 3c0dd4a

Browse files
committed
Add support for INDIRECT delaying
1 parent 4e01a36 commit 3c0dd4a

18 files changed

+6551
-2445
lines changed

Zend/README.md

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -67,7 +67,7 @@ EXECUTE_DATA
6767
ZEND_VM_DISPATCH_TO_HANDLER(<OP>)
6868
return <OP>_helper(ZEND_OPCODE_HANDLER_ARGS_PASSTHRU)
6969
ZEND_VM_DISPATCH_TO_HELPER(<NAME>)
70-
return <NAME>(ZEND_OPCODE_HANDLER_ARGS_PASSTHRU)
70+
return <ZEND_VM_NEXT_OPCODEZEND_VM_NEXT_OPCODENAME>(ZEND_OPCODE_HANDLER_ARGS_PASSTHRU)
7171
ZEND_VM_DISPATCH_TO_HELPER_EX(<NAME>,<PARAM>,<VAL>)
7272
return <NAME>(<VAL>, ZEND_OPCODE_HANDLER_ARGS_PASSTHRU)
7373
ZEND_VM_CONTINUE()

Zend/tests/delayed_error_001.phpt

Lines changed: 41 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,41 @@
1+
--TEST--
2+
Delayed error 001
3+
--FILE--
4+
<?php
5+
$array[0][1] .= 'foo';
6+
$array[2][3]++;
7+
$array[3][4]--;
8+
++$array[5][6];
9+
--$array[7][8];
10+
$array[9][10] += 42;
11+
?>
12+
--EXPECTF--
13+
Warning: Undefined variable $array in %s on line %d
14+
15+
Warning: Undefined array key 0 in %s on line %d
16+
17+
Warning: Undefined array key 1 in %s on line %d
18+
19+
Warning: Undefined array key 2 in %s on line %d
20+
21+
Warning: Undefined array key 3 in %s on line %d
22+
23+
Warning: Decrement on type null has no effect, this will change in the next major version of PHP in %s on line %d
24+
25+
Warning: Undefined array key 3 in %s on line %d
26+
27+
Warning: Undefined array key 4 in %s on line %d
28+
29+
Warning: Undefined array key 5 in %s on line %d
30+
31+
Warning: Undefined array key 6 in %s on line %d
32+
33+
Warning: Decrement on type null has no effect, this will change in the next major version of PHP in %s on line %d
34+
35+
Warning: Undefined array key 7 in %s on line %d
36+
37+
Warning: Undefined array key 8 in %s on line %d
38+
39+
Warning: Undefined array key 9 in %s on line %d
40+
41+
Warning: Undefined array key 10 in %s on line %d

Zend/tests/undef_index_to_exception.phpt

Lines changed: 6 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -37,10 +37,14 @@ try {
3737
?>
3838
--EXPECT--
3939
Undefined array key 0
40-
array(0) {
40+
array(1) {
41+
[0]=>
42+
string(3) "xyz"
4143
}
4244
Undefined array key "key"
43-
array(0) {
45+
array(1) {
46+
[0]=>
47+
string(3) "xyz"
4448
}
4549
Undefined global variable $test
4650
Undefined variable $test

Zend/zend.c

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1718,7 +1718,8 @@ ZEND_API void zend_free_recorded_errors(void)
17181718
EG(num_errors) = 0;
17191719
}
17201720

1721-
ZEND_API ZEND_COLD void zend_error_delayed(int type, const char *format, ...) {
1721+
ZEND_API ZEND_COLD void zend_error_delayed(int type, const char *format, ...)
1722+
{
17221723
ZEND_ASSERT(!(type & E_FATAL_ERRORS) && "Cannot delay fatal error");
17231724
zend_error_info *info = emalloc(sizeof(zend_error_info));
17241725
info->type = type;
@@ -1735,6 +1736,8 @@ ZEND_API ZEND_COLD void zend_error_delayed(int type, const char *format, ...) {
17351736
if (EG(current_execute_data)->opline != EG(delayed_error_op)) {
17361737
EG(opline_before_exception) = EG(current_execute_data)->opline;
17371738
EG(current_execute_data)->opline = EG(delayed_error_op);
1739+
/* Reset to ZEND_HANDLE_DELAYED_ERROR */
1740+
EG(delayed_error_op)[1] = EG(delayed_error_op)[2];
17381741
}
17391742
}
17401743

Zend/zend.h

Lines changed: 0 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -343,9 +343,6 @@ extern ZEND_API zend_string *(*zend_resolve_path)(zend_string *filename);
343343
extern ZEND_API zend_result (*zend_post_startup_cb)(void);
344344
extern ZEND_API void (*zend_post_shutdown_cb)(void);
345345

346-
/* Callback for loading of not preloaded part of the script */
347-
extern ZEND_API zend_result (*zend_preload_autoload)(zend_string *filename);
348-
349346
ZEND_API ZEND_COLD void zend_error(int type, const char *format, ...) ZEND_ATTRIBUTE_FORMAT(printf, 2, 3);
350347
ZEND_API ZEND_COLD ZEND_NORETURN void zend_error_noreturn(int type, const char *format, ...) ZEND_ATTRIBUTE_FORMAT(printf, 2, 3);
351348
/* For custom format specifiers like H */

Zend/zend_compile.h

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -738,6 +738,13 @@ ZEND_STATIC_ASSERT(ZEND_MM_ALIGNED_SIZE(sizeof(zval)) == sizeof(zval),
738738
# define RT_CONSTANT(opline, node) \
739739
((zval*)(((char*)(opline)) + (int32_t)(node).constant))
740740

741+
/* */
742+
# define RT_CONSTANT_DELAYED(opline, node) \
743+
((zval*)(((char*)RT_CONSTANT_DELAYED_OPLINE(opline)) + (int32_t)(node).constant))
744+
745+
# define RT_CONSTANT_DELAYED_OPLINE(opline) \
746+
((zend_op*)(opline) != &EG(delayed_error_op)[0] ? (zend_op*)(opline) : EG(opline_before_exception))
747+
741748
/* convert constant from compile-time to run-time */
742749
# define ZEND_PASS_TWO_UPDATE_CONSTANT(op_array, opline, node) do { \
743750
(node).constant = \

Zend/zend_execute.c

Lines changed: 81 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -118,14 +118,21 @@
118118
typedef int (ZEND_FASTCALL *incdec_t)(zval *);
119119

120120
#define get_zval_ptr(op_type, node, type) _get_zval_ptr(op_type, node, type EXECUTE_DATA_CC OPLINE_CC)
121+
#define get_zval_ptr_delayed(op_type, node, type) _get_zval_ptr_delayed(op_type, node, type EXECUTE_DATA_CC OPLINE_CC)
121122
#define get_zval_ptr_deref(op_type, node, type) _get_zval_ptr_deref(op_type, node, type EXECUTE_DATA_CC OPLINE_CC)
123+
#define get_zval_ptr_deref_delayed(op_type, node, type) _get_zval_ptr_deref_delayed(op_type, node, type EXECUTE_DATA_CC OPLINE_CC)
122124
#define get_zval_ptr_undef(op_type, node, type) _get_zval_ptr_undef(op_type, node, type EXECUTE_DATA_CC OPLINE_CC)
125+
#define get_zval_ptr_undef_delayed(op_type, node, type) _get_zval_ptr_undef_delayed(op_type, node, type EXECUTE_DATA_CC OPLINE_CC)
123126
#define get_op_data_zval_ptr_r(op_type, node) _get_op_data_zval_ptr_r(op_type, node EXECUTE_DATA_CC OPLINE_CC)
127+
#define get_op_data_zval_ptr_r_delayed(op_type, node) _get_op_data_zval_ptr_r_delayed(op_type, node EXECUTE_DATA_CC OPLINE_CC)
124128
#define get_op_data_zval_ptr_deref_r(op_type, node) _get_op_data_zval_ptr_deref_r(op_type, node EXECUTE_DATA_CC OPLINE_CC)
129+
#define get_op_data_zval_ptr_deref_r_delayed(op_type, node) _get_op_data_zval_ptr_deref_r_delayed(op_type, node EXECUTE_DATA_CC OPLINE_CC)
125130
#define get_zval_ptr_ptr(op_type, node, type) _get_zval_ptr_ptr(op_type, node, type EXECUTE_DATA_CC)
126131
#define get_zval_ptr_ptr_undef(op_type, node, type) _get_zval_ptr_ptr(op_type, node, type EXECUTE_DATA_CC)
127132
#define get_obj_zval_ptr(op_type, node, type) _get_obj_zval_ptr(op_type, node, type EXECUTE_DATA_CC OPLINE_CC)
133+
#define get_obj_zval_ptr_delayed(op_type, node, type) _get_obj_zval_ptr_delayed(op_type, node, type EXECUTE_DATA_CC OPLINE_CC)
128134
#define get_obj_zval_ptr_undef(op_type, node, type) _get_obj_zval_ptr_undef(op_type, node, type EXECUTE_DATA_CC OPLINE_CC)
135+
#define get_obj_zval_ptr_undef_delayed(op_type, node, type) _get_obj_zval_ptr_undef_delayed(op_type, node, type EXECUTE_DATA_CC OPLINE_CC)
129136
#define get_obj_zval_ptr_ptr(op_type, node, type) _get_obj_zval_ptr_ptr(op_type, node, type EXECUTE_DATA_CC)
130137

131138
#define RETURN_VALUE_USED(opline) ((opline)->result_type != IS_UNUSED)
@@ -425,6 +432,14 @@ static zend_always_inline zval *_get_zval_ptr(int op_type, znode_op node, int ty
425432
}
426433
}
427434

435+
static zend_always_inline zval *_get_zval_ptr_delayed(int op_type, znode_op node, int type EXECUTE_DATA_DC OPLINE_DC)
436+
{
437+
if (op_type == IS_CONST) {
438+
return RT_CONSTANT_DELAYED(opline, node);
439+
}
440+
return get_zval_ptr(op_type, node, type);
441+
}
442+
428443
static zend_always_inline zval *_get_op_data_zval_ptr_r(int op_type, znode_op node EXECUTE_DATA_DC OPLINE_DC)
429444
{
430445
if (op_type & (IS_TMP_VAR|IS_VAR)) {
@@ -445,6 +460,14 @@ static zend_always_inline zval *_get_op_data_zval_ptr_r(int op_type, znode_op no
445460
}
446461
}
447462

463+
static zend_always_inline zval *_get_op_data_zval_ptr_r_delayed(int op_type, znode_op node EXECUTE_DATA_DC OPLINE_DC)
464+
{
465+
if (op_type == IS_CONST) {
466+
return RT_CONSTANT(RT_CONSTANT_DELAYED_OPLINE(opline)+1, (opline+1)->op1);
467+
}
468+
return get_op_data_zval_ptr_r(op_type, node);
469+
}
470+
448471
static zend_always_inline ZEND_ATTRIBUTE_UNUSED zval *_get_zval_ptr_deref(int op_type, znode_op node, int type EXECUTE_DATA_DC OPLINE_DC)
449472
{
450473
if (op_type & (IS_TMP_VAR|IS_VAR)) {
@@ -465,6 +488,14 @@ static zend_always_inline ZEND_ATTRIBUTE_UNUSED zval *_get_zval_ptr_deref(int op
465488
}
466489
}
467490

491+
static zend_always_inline ZEND_ATTRIBUTE_UNUSED zval *_get_zval_ptr_deref_delayed(int op_type, znode_op node, int type EXECUTE_DATA_DC OPLINE_DC)
492+
{
493+
if (op_type == IS_CONST) {
494+
return RT_CONSTANT_DELAYED(opline, node);
495+
}
496+
return get_zval_ptr_deref(op_type, node, type);
497+
}
498+
468499
static zend_always_inline ZEND_ATTRIBUTE_UNUSED zval *_get_op_data_zval_ptr_deref_r(int op_type, znode_op node EXECUTE_DATA_DC OPLINE_DC)
469500
{
470501
if (op_type & (IS_TMP_VAR|IS_VAR)) {
@@ -485,6 +516,14 @@ static zend_always_inline ZEND_ATTRIBUTE_UNUSED zval *_get_op_data_zval_ptr_dere
485516
}
486517
}
487518

519+
static zend_always_inline ZEND_ATTRIBUTE_UNUSED zval *_get_op_data_zval_ptr_deref_r_delayed(int op_type, znode_op node EXECUTE_DATA_DC OPLINE_DC)
520+
{
521+
if (op_type == IS_CONST) {
522+
return RT_CONSTANT(RT_CONSTANT_DELAYED_OPLINE(opline)+1, (opline+1)->op1);
523+
}
524+
return get_op_data_zval_ptr_deref_r(op_type, node);
525+
}
526+
488527
static zend_always_inline zval *_get_zval_ptr_undef(int op_type, znode_op node, int type EXECUTE_DATA_DC OPLINE_DC)
489528
{
490529
if (op_type & (IS_TMP_VAR|IS_VAR)) {
@@ -505,6 +544,14 @@ static zend_always_inline zval *_get_zval_ptr_undef(int op_type, znode_op node,
505544
}
506545
}
507546

547+
static zend_always_inline zval *_get_zval_ptr_undef_delayed(int op_type, znode_op node, int type EXECUTE_DATA_DC OPLINE_DC)
548+
{
549+
if (op_type == IS_CONST) {
550+
return RT_CONSTANT_DELAYED(opline, node);
551+
}
552+
return get_zval_ptr_undef(op_type, node, type);
553+
}
554+
508555
static zend_always_inline zval *_get_zval_ptr_ptr_var(uint32_t var EXECUTE_DATA_DC)
509556
{
510557
zval *ret = EX_VAR(var);
@@ -533,6 +580,14 @@ static inline ZEND_ATTRIBUTE_UNUSED zval *_get_obj_zval_ptr(int op_type, znode_o
533580
return get_zval_ptr(op_type, op, type);
534581
}
535582

583+
static inline ZEND_ATTRIBUTE_UNUSED zval *_get_obj_zval_ptr_delayed(int op_type, znode_op op, int type EXECUTE_DATA_DC OPLINE_DC)
584+
{
585+
if (op_type == IS_UNUSED) {
586+
return &EX(This);
587+
}
588+
return get_zval_ptr_delayed(op_type, op, type);
589+
}
590+
536591
static inline ZEND_ATTRIBUTE_UNUSED zval *_get_obj_zval_ptr_undef(int op_type, znode_op op, int type EXECUTE_DATA_DC OPLINE_DC)
537592
{
538593
if (op_type == IS_UNUSED) {
@@ -541,6 +596,14 @@ static inline ZEND_ATTRIBUTE_UNUSED zval *_get_obj_zval_ptr_undef(int op_type, z
541596
return get_zval_ptr_undef(op_type, op, type);
542597
}
543598

599+
static inline ZEND_ATTRIBUTE_UNUSED zval *_get_obj_zval_ptr_undef_delayed(int op_type, znode_op op, int type EXECUTE_DATA_DC OPLINE_DC)
600+
{
601+
if (op_type == IS_UNUSED) {
602+
return &EX(This);
603+
}
604+
return get_zval_ptr_undef_delayed(op_type, op, type);
605+
}
606+
544607
static inline ZEND_ATTRIBUTE_UNUSED zval *_get_obj_zval_ptr_ptr(int op_type, znode_op node, int type EXECUTE_DATA_DC)
545608
{
546609
if (op_type == IS_UNUSED) {
@@ -2231,6 +2294,23 @@ ZEND_API ZEND_COLD void ZEND_FASTCALL zend_undefined_offset_delayed(zend_long lv
22312294
zend_error_delayed(E_WARNING, "Undefined array key " ZEND_LONG_FMT, lval);
22322295
}
22332296

2297+
ZEND_API void ZEND_FASTCALL zend_handle_delayed_errors(void)
2298+
{
2299+
/* Clear EG(delayed_errors), as more errors may be delayed while we are handling these. */
2300+
HashTable ht;
2301+
memcpy(&ht, &EG(delayed_errors), sizeof(HashTable));
2302+
zend_hash_init(&EG(delayed_errors), 0, NULL, NULL, 0);
2303+
2304+
zend_error_info *info;
2305+
ZEND_HASH_FOREACH_PTR(&ht, info) {
2306+
zend_error_zstr_at(info->type, info->filename, info->lineno, info->message);
2307+
zend_string_release(info->filename);
2308+
zend_string_release(info->message);
2309+
efree(info);
2310+
} ZEND_HASH_FOREACH_END();
2311+
zend_hash_destroy(&ht);
2312+
}
2313+
22342314
ZEND_API ZEND_COLD zval* ZEND_FASTCALL zend_undefined_index_write(HashTable *ht, zend_string *offset)
22352315
{
22362316
zval *retval;
@@ -3242,6 +3322,7 @@ static zend_always_inline void zend_fetch_property_address(zval *result, zval *c
32423322

32433323
if (prop_op_type == IS_CONST) {
32443324
name = Z_STR_P(prop_ptr);
3325+
tmp_name = NULL;
32453326
} else {
32463327
name = zval_get_tmp_string(prop_ptr, &tmp_name);
32473328
}

Zend/zend_execute.h

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -91,6 +91,8 @@ ZEND_API ZEND_COLD void ZEND_FASTCALL zend_object_released_while_assigning_to_pr
9191
ZEND_API ZEND_COLD void ZEND_FASTCALL zend_cannot_add_element(void);
9292
ZEND_API ZEND_COLD void ZEND_FASTCALL zend_undefined_offset_delayed(zend_long lval);
9393

94+
ZEND_API void ZEND_FASTCALL zend_handle_delayed_errors(void);
95+
9496
ZEND_API bool zend_verify_scalar_type_hint(uint32_t type_mask, zval *arg, bool strict, bool is_internal_arg);
9597
ZEND_API ZEND_COLD void zend_verify_arg_error(
9698
const zend_function *zf, const zend_arg_info *arg_info, uint32_t arg_num, zval *value);

Zend/zend_execute_API.c

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -172,6 +172,7 @@ void init_executor(void) /* {{{ */
172172
zend_objects_store_init(&EG(objects_store), 1024);
173173

174174
EG(full_tables_cleanup) = 0;
175+
EG(delayed_handlers) = 0;
175176
ZEND_ATOMIC_BOOL_INIT(&EG(vm_interrupt), false);
176177
ZEND_ATOMIC_BOOL_INIT(&EG(timed_out), false);
177178

Zend/zend_globals.h

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -206,6 +206,8 @@ struct _zend_executor_globals {
206206

207207
bool full_tables_cleanup;
208208

209+
bool delayed_handlers;
210+
209211
zend_atomic_bool vm_interrupt;
210212
zend_atomic_bool timed_out;
211213

@@ -246,6 +248,7 @@ struct _zend_executor_globals {
246248
zend_object *exception, *prev_exception;
247249
const zend_op *opline_before_exception;
248250
zend_op exception_op[3];
251+
/* FIXME: Is this safe for VM reentry? We may have to save/restore this value. */
249252
zend_op delayed_error_op[3];
250253

251254
struct _zend_module_entry *current_module;

0 commit comments

Comments
 (0)