Skip to content

Commit 16e9e66

Browse files
committed
Encode string offset error reason in extended_value
For FETCH_DIM_W etc encode the context it is being used in (dim, obj, ref or incdec) so we can throw an appropriate error message for invalid string offset use, in a way that does not require inspecting neighboring opcodes. The implementation is similar to the flags used for FETCH_OBJ. This means that we do not have to be careful about preserving following opcodes during optimization. Closes phpGH-7599.
1 parent 9edf825 commit 16e9e66

File tree

3 files changed

+60
-74
lines changed

3 files changed

+60
-74
lines changed

Zend/zend_compile.c

Lines changed: 36 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -2723,7 +2723,7 @@ static inline void zend_emit_assign_znode(zend_ast *var_ast, znode *value_node)
27232723
}
27242724
/* }}} */
27252725

2726-
static zend_op *zend_delayed_compile_dim(znode *result, zend_ast *ast, uint32_t type) /* {{{ */
2726+
static zend_op *zend_delayed_compile_dim(znode *result, zend_ast *ast, uint32_t type, bool by_ref)
27272727
{
27282728
if (ast->attr == ZEND_DIM_ALTERNATIVE_SYNTAX) {
27292729
zend_error(E_COMPILE_ERROR, "Array and string offset access syntax with curly braces is no longer supported");
@@ -2751,8 +2751,15 @@ static zend_op *zend_delayed_compile_dim(znode *result, zend_ast *ast, uint32_t
27512751
} else {
27522752
zend_short_circuiting_mark_inner(var_ast);
27532753
opline = zend_delayed_compile_var(&var_node, var_ast, type, 0);
2754-
if (opline && type == BP_VAR_W && (opline->opcode == ZEND_FETCH_STATIC_PROP_W || opline->opcode == ZEND_FETCH_OBJ_W)) {
2755-
opline->extended_value |= ZEND_FETCH_DIM_WRITE;
2754+
if (opline) {
2755+
if (type == BP_VAR_W && (opline->opcode == ZEND_FETCH_STATIC_PROP_W || opline->opcode == ZEND_FETCH_OBJ_W)) {
2756+
opline->extended_value |= ZEND_FETCH_DIM_WRITE;
2757+
} else if (opline->opcode == ZEND_FETCH_DIM_W
2758+
|| opline->opcode == ZEND_FETCH_DIM_RW
2759+
|| opline->opcode == ZEND_FETCH_DIM_FUNC_ARG
2760+
|| opline->opcode == ZEND_FETCH_DIM_UNSET) {
2761+
opline->extended_value = ZEND_FETCH_DIM_DIM;
2762+
}
27562763
}
27572764
}
27582765

@@ -2772,18 +2779,20 @@ static zend_op *zend_delayed_compile_dim(znode *result, zend_ast *ast, uint32_t
27722779

27732780
opline = zend_delayed_emit_op(result, ZEND_FETCH_DIM_R, &var_node, &dim_node);
27742781
zend_adjust_for_fetch_type(opline, result, type);
2782+
if (by_ref) {
2783+
opline->extended_value = ZEND_FETCH_DIM_REF;
2784+
}
27752785

27762786
if (dim_node.op_type == IS_CONST) {
27772787
zend_handle_numeric_dim(opline, &dim_node);
27782788
}
27792789
return opline;
27802790
}
2781-
/* }}} */
27822791

2783-
static zend_op *zend_compile_dim(znode *result, zend_ast *ast, uint32_t type) /* {{{ */
2792+
static zend_op *zend_compile_dim(znode *result, zend_ast *ast, uint32_t type, bool by_ref) /* {{{ */
27842793
{
27852794
uint32_t offset = zend_delayed_compile_begin();
2786-
zend_delayed_compile_dim(result, ast, type);
2795+
zend_delayed_compile_dim(result, ast, type, by_ref);
27872796
return zend_delayed_compile_end(offset);
27882797
}
27892798
/* }}} */
@@ -2810,6 +2819,13 @@ static zend_op *zend_delayed_compile_prop(znode *result, zend_ast *ast, uint32_t
28102819
} else {
28112820
zend_short_circuiting_mark_inner(obj_ast);
28122821
opline = zend_delayed_compile_var(&obj_node, obj_ast, type, 0);
2822+
if (opline && (opline->opcode == ZEND_FETCH_DIM_W
2823+
|| opline->opcode == ZEND_FETCH_DIM_RW
2824+
|| opline->opcode == ZEND_FETCH_DIM_FUNC_ARG
2825+
|| opline->opcode == ZEND_FETCH_DIM_UNSET)) {
2826+
opline->extended_value = ZEND_FETCH_DIM_OBJ;
2827+
}
2828+
28132829
zend_separate_if_call_and_write(&obj_node, obj_ast, type);
28142830
if (nullsafe) {
28152831
/* We will push to the short_circuiting_opnums stack in zend_delayed_compile_end(). */
@@ -3113,7 +3129,7 @@ static void zend_compile_assign(znode *result, zend_ast *ast) /* {{{ */
31133129
return;
31143130
case ZEND_AST_DIM:
31153131
offset = zend_delayed_compile_begin();
3116-
zend_delayed_compile_dim(result, var_ast, BP_VAR_W);
3132+
zend_delayed_compile_dim(result, var_ast, BP_VAR_W, /* by_ref */ false);
31173133

31183134
if (zend_is_assign_to_self(var_ast, expr_ast)
31193135
&& !is_this_fetch(expr_ast)) {
@@ -3296,7 +3312,7 @@ static void zend_compile_compound_assign(znode *result, zend_ast *ast) /* {{{ */
32963312
return;
32973313
case ZEND_AST_DIM:
32983314
offset = zend_delayed_compile_begin();
3299-
zend_delayed_compile_dim(result, var_ast, BP_VAR_RW);
3315+
zend_delayed_compile_dim(result, var_ast, BP_VAR_RW, /* by_ref */ false);
33003316
zend_compile_expr(&expr_node, expr_ast);
33013317

33023318
opline = zend_delayed_compile_end(offset);
@@ -4709,7 +4725,7 @@ static void zend_compile_unset(zend_ast *ast) /* {{{ */
47094725
}
47104726
return;
47114727
case ZEND_AST_DIM:
4712-
opline = zend_compile_dim(NULL, var_ast, BP_VAR_UNSET);
4728+
opline = zend_compile_dim(NULL, var_ast, BP_VAR_UNSET, /* by_ref */ false);
47134729
opline->opcode = ZEND_UNSET_DIM;
47144730
return;
47154731
case ZEND_AST_PROP:
@@ -8681,7 +8697,10 @@ static void zend_compile_post_incdec(znode *result, zend_ast *ast) /* {{{ */
86818697
zend_make_tmp_result(result, opline);
86828698
} else {
86838699
znode var_node;
8684-
zend_compile_var(&var_node, var_ast, BP_VAR_RW, 0);
8700+
zend_op *opline = zend_compile_var(&var_node, var_ast, BP_VAR_RW, 0);
8701+
if (opline && opline->opcode == ZEND_FETCH_DIM_RW) {
8702+
opline->extended_value = ZEND_FETCH_DIM_INCDEC;
8703+
}
86858704
zend_emit_op_tmp(result, ast->kind == ZEND_AST_POST_INC ? ZEND_POST_INC : ZEND_POST_DEC,
86868705
&var_node, NULL);
86878706
}
@@ -8707,7 +8726,10 @@ static void zend_compile_pre_incdec(znode *result, zend_ast *ast) /* {{{ */
87078726
result->op_type = IS_TMP_VAR;
87088727
} else {
87098728
znode var_node;
8710-
zend_compile_var(&var_node, var_ast, BP_VAR_RW, 0);
8729+
zend_op *opline = zend_compile_var(&var_node, var_ast, BP_VAR_RW, 0);
8730+
if (opline && opline->opcode == ZEND_FETCH_DIM_RW) {
8731+
opline->extended_value = ZEND_FETCH_DIM_INCDEC;
8732+
}
87118733
zend_emit_op_tmp(result, ast->kind == ZEND_AST_PRE_INC ? ZEND_PRE_INC : ZEND_PRE_DEC,
87128734
&var_node, NULL);
87138735
}
@@ -9154,7 +9176,7 @@ static void zend_compile_isset_or_empty(znode *result, zend_ast *ast) /* {{{ */
91549176
}
91559177
break;
91569178
case ZEND_AST_DIM:
9157-
opline = zend_compile_dim(result, var_ast, BP_VAR_IS);
9179+
opline = zend_compile_dim(result, var_ast, BP_VAR_IS, /* by_ref */ false);
91589180
opline->opcode = ZEND_ISSET_ISEMPTY_DIM_OBJ;
91599181
break;
91609182
case ZEND_AST_PROP:
@@ -10114,7 +10136,7 @@ static zend_op *zend_compile_var_inner(znode *result, zend_ast *ast, uint32_t ty
1011410136
case ZEND_AST_VAR:
1011510137
return zend_compile_simple_var(result, ast, type, 0);
1011610138
case ZEND_AST_DIM:
10117-
return zend_compile_dim(result, ast, type);
10139+
return zend_compile_dim(result, ast, type, by_ref);
1011810140
case ZEND_AST_PROP:
1011910141
case ZEND_AST_NULLSAFE_PROP:
1012010142
return zend_compile_prop(result, ast, type, by_ref);
@@ -10158,7 +10180,7 @@ static zend_op *zend_delayed_compile_var(znode *result, zend_ast *ast, uint32_t
1015810180
case ZEND_AST_VAR:
1015910181
return zend_compile_simple_var(result, ast, type, 1);
1016010182
case ZEND_AST_DIM:
10161-
return zend_delayed_compile_dim(result, ast, type);
10183+
return zend_delayed_compile_dim(result, ast, type, by_ref);
1016210184
case ZEND_AST_PROP:
1016310185
case ZEND_AST_NULLSAFE_PROP:
1016410186
{

Zend/zend_compile.h

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -961,6 +961,13 @@ ZEND_API zend_string *zend_type_to_string(zend_type type);
961961
#define ZEND_FETCH_DIM_WRITE 2
962962
#define ZEND_FETCH_OBJ_FLAGS 3
963963

964+
/* Used to mark what kind of operation a writing FETCH_DIM is used in,
965+
* to produce a more precise error on incorrect string offset use. */
966+
#define ZEND_FETCH_DIM_REF 1
967+
#define ZEND_FETCH_DIM_DIM 2
968+
#define ZEND_FETCH_DIM_OBJ 3
969+
#define ZEND_FETCH_DIM_INCDEC 4
970+
964971
#define ZEND_ISEMPTY (1<<0)
965972

966973
#define ZEND_LAST_CATCH (1<<0)

Zend/zend_execute.c

Lines changed: 17 additions & 60 deletions
Original file line numberDiff line numberDiff line change
@@ -1530,7 +1530,6 @@ ZEND_API ZEND_COLD void zend_wrong_string_offset_error(void)
15301530
const char *msg = NULL;
15311531
const zend_execute_data *execute_data = EG(current_execute_data);
15321532
const zend_op *opline = execute_data->opline;
1533-
uint32_t var;
15341533

15351534
if (UNEXPECTED(EG(exception) != NULL)) {
15361535
return;
@@ -1540,69 +1539,27 @@ ZEND_API ZEND_COLD void zend_wrong_string_offset_error(void)
15401539
case ZEND_ASSIGN_DIM_OP:
15411540
msg = "Cannot use assign-op operators with string offsets";
15421541
break;
1542+
case ZEND_FETCH_LIST_W:
1543+
msg = "Cannot create references to/from string offsets";
1544+
break;
15431545
case ZEND_FETCH_DIM_W:
15441546
case ZEND_FETCH_DIM_RW:
15451547
case ZEND_FETCH_DIM_FUNC_ARG:
15461548
case ZEND_FETCH_DIM_UNSET:
1547-
case ZEND_FETCH_LIST_W:
1548-
/* TODO: Encode the "reason" into opline->extended_value??? */
1549-
var = opline->result.var;
1550-
opline++;
1551-
ZEND_ASSERT(opline < execute_data->func->op_array.opcodes +
1552-
execute_data->func->op_array.last);
1553-
if (opline->op1_type == IS_VAR && opline->op1.var == var) {
1554-
switch (opline->opcode) {
1555-
case ZEND_FETCH_OBJ_W:
1556-
case ZEND_FETCH_OBJ_RW:
1557-
case ZEND_FETCH_OBJ_FUNC_ARG:
1558-
case ZEND_FETCH_OBJ_UNSET:
1559-
case ZEND_ASSIGN_OBJ:
1560-
case ZEND_ASSIGN_OBJ_OP:
1561-
case ZEND_ASSIGN_OBJ_REF:
1562-
case ZEND_PRE_INC_OBJ:
1563-
case ZEND_PRE_DEC_OBJ:
1564-
case ZEND_POST_INC_OBJ:
1565-
case ZEND_POST_DEC_OBJ:
1566-
case ZEND_UNSET_OBJ:
1567-
msg = "Cannot use string offset as an object";
1568-
break;
1569-
case ZEND_FETCH_DIM_W:
1570-
case ZEND_FETCH_DIM_RW:
1571-
case ZEND_FETCH_DIM_FUNC_ARG:
1572-
case ZEND_FETCH_DIM_UNSET:
1573-
case ZEND_FETCH_LIST_W:
1574-
case ZEND_ASSIGN_DIM:
1575-
case ZEND_ASSIGN_DIM_OP:
1576-
case ZEND_UNSET_DIM:
1577-
msg = "Cannot use string offset as an array";
1578-
break;
1579-
case ZEND_PRE_INC:
1580-
case ZEND_PRE_DEC:
1581-
case ZEND_POST_INC:
1582-
case ZEND_POST_DEC:
1583-
msg = "Cannot increment/decrement string offsets";
1584-
break;
1585-
case ZEND_ASSIGN_REF:
1586-
case ZEND_ADD_ARRAY_ELEMENT:
1587-
case ZEND_INIT_ARRAY:
1588-
case ZEND_MAKE_REF:
1589-
case ZEND_RETURN_BY_REF:
1590-
case ZEND_VERIFY_RETURN_TYPE:
1591-
case ZEND_YIELD:
1592-
case ZEND_SEND_REF:
1593-
case ZEND_SEND_VAR_EX:
1594-
case ZEND_SEND_FUNC_ARG:
1595-
case ZEND_FE_RESET_RW:
1596-
msg = "Cannot create references to/from string offsets";
1597-
break;
1598-
EMPTY_SWITCH_DEFAULT_CASE();
1599-
}
1600-
break;
1601-
}
1602-
if (opline->op2_type == IS_VAR && opline->op2.var == var) {
1603-
ZEND_ASSERT(opline->opcode == ZEND_ASSIGN_REF);
1604-
msg = "Cannot create references to/from string offsets";
1605-
break;
1549+
switch (opline->extended_value) {
1550+
case ZEND_FETCH_DIM_REF:
1551+
msg = "Cannot create references to/from string offsets";
1552+
break;
1553+
case ZEND_FETCH_DIM_DIM:
1554+
msg = "Cannot use string offset as an array";
1555+
break;
1556+
case ZEND_FETCH_DIM_OBJ:
1557+
msg = "Cannot use string offset as an object";
1558+
break;
1559+
case ZEND_FETCH_DIM_INCDEC:
1560+
msg = "Cannot increment/decrement string offsets";
1561+
break;
1562+
EMPTY_SWITCH_DEFAULT_CASE();
16061563
}
16071564
break;
16081565
EMPTY_SWITCH_DEFAULT_CASE();

0 commit comments

Comments
 (0)