Skip to content

Commit a16aa4c

Browse files
committed
Move most "finally" related code-generation from pass_two() to compiler.
1 parent 7438010 commit a16aa4c

File tree

8 files changed

+143
-248
lines changed

8 files changed

+143
-248
lines changed

Zend/tests/break_error_001.phpt

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,10 @@
1+
--TEST--
2+
'break' error (non positive numbers)
3+
--FILE--
4+
<?php
5+
function foo () {
6+
break 0;
7+
}
8+
?>
9+
--EXPECTF--
10+
Fatal error: 'break' operator accepts only positive numbers in %sbreak_error_001.php on line 3

Zend/tests/break_error_002.phpt

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,10 @@
1+
--TEST--
2+
'break' error (operator with non-constant operand)
3+
--FILE--
4+
<?php
5+
function foo () {
6+
break $x;
7+
}
8+
?>
9+
--EXPECTF--
10+
Fatal error: 'break' operator with non-constant operand is no longer supported in %sbreak_error_002.php on line 3

Zend/tests/break_error_003.phpt

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,10 @@
1+
--TEST--
2+
'break' error (not in the loop context)
3+
--FILE--
4+
<?php
5+
function foo () {
6+
break;
7+
}
8+
?>
9+
--EXPECTF--
10+
Fatal error: 'break' not in the 'loop' or 'switch' context in %sbreak_error_003.php on line 3

Zend/tests/break_error_004.phpt

Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,12 @@
1+
--TEST--
2+
'break' error (wrong level)
3+
--FILE--
4+
<?php
5+
function foo () {
6+
while (1) {
7+
break 2;
8+
}
9+
}
10+
?>
11+
--EXPECTF--
12+
Fatal error: Cannot 'break' 2 levels in %sbreak_error_004.php on line 4

Zend/zend_compile.c

Lines changed: 77 additions & 90 deletions
Original file line numberDiff line numberDiff line change
@@ -56,9 +56,12 @@
5656

5757
typedef struct _zend_loop_var {
5858
zend_uchar opcode;
59-
uint32_t try_catch_offset;
60-
uint32_t brk_cont_offset;
61-
znode var;
59+
zend_uchar var_type;
60+
uint32_t var_num;
61+
union {
62+
uint32_t try_catch_offset;
63+
uint32_t brk_cont_offset;
64+
} u;
6265
} zend_loop_var;
6366

6467
static inline void zend_alloc_cache_slot(uint32_t literal) {
@@ -565,7 +568,7 @@ void zend_stop_lexing(void)
565568
LANG_SCNG(yy_cursor) = LANG_SCNG(yy_limit);
566569
}
567570

568-
static inline void zend_begin_loop(const znode *loop_var) /* {{{ */
571+
static inline void zend_begin_loop(zend_uchar free_opcode, const znode *loop_var) /* {{{ */
569572
{
570573
zend_brk_cont_element *brk_cont_element;
571574
int parent = CG(context).current_brk_cont;
@@ -576,9 +579,10 @@ static inline void zend_begin_loop(const znode *loop_var) /* {{{ */
576579
brk_cont_element->parent = parent;
577580

578581
if (loop_var && (loop_var->op_type & (IS_VAR|IS_TMP_VAR))) {
579-
info.opcode = loop_var->flag ? ZEND_FE_FREE : ZEND_FREE;
580-
info.var = *loop_var;
581-
info.brk_cont_offset = CG(context).current_brk_cont;
582+
info.opcode = free_opcode;
583+
info.var_type = loop_var->op_type;
584+
info.var_num = loop_var->u.op.var;
585+
info.u.brk_cont_offset = CG(context).current_brk_cont;
582586
brk_cont_element->start = get_next_op_number(CG(active_op_array));
583587
} else {
584588
info.opcode = ZEND_NOP;
@@ -888,49 +892,6 @@ static void str_dtor(zval *zv) /* {{{ */ {
888892

889893
static zend_bool zend_is_call(zend_ast *ast);
890894

891-
static zend_loop_var *generate_fast_calls(zend_loop_var *var) /* {{{ */
892-
{
893-
zend_loop_var *base = zend_stack_base(&CG(loop_var_stack));
894-
for (; var >= base && var->opcode == ZEND_FAST_CALL; var--) {
895-
zend_op *opline = get_next_op(CG(active_op_array));
896-
opline->opcode = ZEND_FAST_CALL;
897-
SET_NODE(opline->result, &var->var);
898-
SET_UNUSED(opline->op1);
899-
SET_UNUSED(opline->op2);
900-
opline->op1.num = var->try_catch_offset;
901-
opline->extended_value = ZEND_FAST_CALL_UNBOUND;
902-
}
903-
return var;
904-
}
905-
/* }}} */
906-
907-
static zend_loop_var *generate_free_loop_var(zend_loop_var *info) /* {{{ */
908-
{
909-
zend_op *opline;
910-
zend_loop_var *base = zend_stack_base(&CG(loop_var_stack));
911-
ZEND_ASSERT(info->opcode != ZEND_FAST_CALL);
912-
913-
if (info < base || info->opcode == ZEND_RETURN) {
914-
/* Stack separator */
915-
return NULL;
916-
}
917-
918-
if (info->opcode == ZEND_NOP) {
919-
/* Loop doesn't have freeable variable */
920-
return info - 1;
921-
}
922-
923-
ZEND_ASSERT(info->var.op_type == IS_VAR || info->var.op_type == IS_TMP_VAR);
924-
opline = get_next_op(CG(active_op_array));
925-
opline->opcode = info->opcode;
926-
SET_NODE(opline->op1, &info->var);
927-
SET_UNUSED(opline->op2);
928-
opline->op2.num = info->brk_cont_offset;
929-
opline->extended_value = ZEND_FREE_ON_RETURN;
930-
return info - 1;
931-
}
932-
/* }}} */
933-
934895
static uint32_t zend_add_try_element(uint32_t try_op) /* {{{ */
935896
{
936897
zend_op_array *op_array = CG(active_op_array);
@@ -3497,13 +3458,55 @@ void zend_compile_unset(zend_ast *ast) /* {{{ */
34973458
}
34983459
/* }}} */
34993460

3500-
static void zend_handle_loops_and_finally() /* {{{ */
3461+
static int zend_handle_loops_and_finally_ex(zend_long depth) /* {{{ */
35013462
{
3463+
zend_loop_var *base;
35023464
zend_loop_var *loop_var = zend_stack_top(&CG(loop_var_stack));
3503-
while (loop_var) {
3504-
loop_var = generate_fast_calls(loop_var);
3505-
loop_var = generate_free_loop_var(loop_var);
3465+
3466+
if (!loop_var) {
3467+
return 1;
35063468
}
3469+
base = zend_stack_base(&CG(loop_var_stack));
3470+
for (; loop_var >= base; loop_var--) {
3471+
if (loop_var->opcode == ZEND_FAST_CALL) {
3472+
zend_op *opline = get_next_op(CG(active_op_array));
3473+
3474+
opline->opcode = ZEND_FAST_CALL;
3475+
opline->result_type = IS_TMP_VAR;
3476+
opline->result.var = loop_var->var_num;
3477+
SET_UNUSED(opline->op1);
3478+
SET_UNUSED(opline->op2);
3479+
opline->op1.num = loop_var->u.try_catch_offset;
3480+
opline->extended_value = ZEND_FAST_CALL_UNBOUND;
3481+
} else if (loop_var->opcode == ZEND_RETURN) {
3482+
/* Stack separator */
3483+
break;
3484+
} else if (depth <= 1) {
3485+
return 1;
3486+
} else if (loop_var->opcode == ZEND_NOP) {
3487+
/* Loop doesn't have freeable variable */
3488+
depth--;
3489+
} else {
3490+
zend_op *opline;
3491+
3492+
ZEND_ASSERT(loop_var->var_type == IS_VAR || loop_var->var_type == IS_TMP_VAR);
3493+
opline = get_next_op(CG(active_op_array));
3494+
opline->opcode = loop_var->opcode;
3495+
opline->op1_type = loop_var->var_type;
3496+
opline->op1.var = loop_var->var_num;
3497+
SET_UNUSED(opline->op2);
3498+
opline->op2.num = loop_var->u.brk_cont_offset;
3499+
opline->extended_value = ZEND_FREE_ON_RETURN;
3500+
depth--;
3501+
}
3502+
}
3503+
return (depth == 0);
3504+
}
3505+
/* }}} */
3506+
3507+
static int zend_handle_loops_and_finally(void) /* {{{ */
3508+
{
3509+
zend_handle_loops_and_finally_ex(zend_stack_count(&CG(loop_var_stack)) + 1);
35073510
}
35083511
/* }}} */
35093512

@@ -3602,32 +3605,19 @@ void zend_compile_break_continue(zend_ast *ast) /* {{{ */
36023605
zend_error_noreturn(E_COMPILE_ERROR, "'%s' not in the 'loop' or 'switch' context",
36033606
ast->kind == ZEND_AST_BREAK ? "break" : "continue");
36043607
} else {
3605-
int array_offset = CG(context).current_brk_cont;
3606-
zend_long nest_level = depth;
3607-
zend_loop_var *loop_var = zend_stack_top(&CG(loop_var_stack));
3608-
3609-
do {
3610-
if (array_offset == -1) {
3611-
zend_error_noreturn(E_COMPILE_ERROR, "Cannot '%s' %d level%s",
3612-
ast->kind == ZEND_AST_BREAK ? "break" : "continue",
3613-
depth, depth == 1 ? "" : "s");
3614-
}
3615-
3616-
loop_var = generate_fast_calls(loop_var);
3617-
if (nest_level > 1) {
3618-
loop_var = generate_free_loop_var(loop_var);
3619-
}
3620-
3621-
array_offset = CG(active_op_array)->brk_cont_array[array_offset].parent;
3622-
} while (--nest_level > 0);
3608+
if (!zend_handle_loops_and_finally_ex(depth)) {
3609+
zend_error_noreturn(E_COMPILE_ERROR, "Cannot '%s' %d level%s",
3610+
ast->kind == ZEND_AST_BREAK ? "break" : "continue",
3611+
depth, depth == 1 ? "" : "s");
3612+
}
36233613
}
36243614
opline = zend_emit_op(NULL, ast->kind == ZEND_AST_BREAK ? ZEND_BRK : ZEND_CONT, NULL, NULL);
36253615
opline->op1.num = CG(context).current_brk_cont;
36263616
opline->op2.num = depth;
36273617
}
36283618
/* }}} */
36293619

3630-
zend_op *zend_resolve_goto_label(zend_op_array *op_array, zend_op *opline) /* {{{ */
3620+
void zend_resolve_goto_label(zend_op_array *op_array, zend_op *opline) /* {{{ */
36313621
{
36323622
zend_label *dest;
36333623
int current, remove_oplines = opline->op1.num;
@@ -3672,20 +3662,19 @@ zend_op *zend_resolve_goto_label(zend_op_array *op_array, zend_op *opline) /* {{
36723662
}
36733663
}
36743664

3675-
ZEND_ASSERT(remove_oplines >= 0);
3676-
while (remove_oplines--) {
3677-
MAKE_NOP(opline);
3678-
opline--;
3679-
}
3680-
36813665
opline->opcode = ZEND_JMP;
36823666
opline->op1.opline_num = dest->opline_num;
36833667
opline->extended_value = 0;
36843668
SET_UNUSED(opline->op1);
36853669
SET_UNUSED(opline->op2);
36863670
SET_UNUSED(opline->result);
36873671

3688-
return opline;
3672+
ZEND_ASSERT(remove_oplines >= 0);
3673+
while (remove_oplines--) {
3674+
opline--;
3675+
MAKE_NOP(opline);
3676+
ZEND_VM_SET_OPCODE_HANDLER(opline);
3677+
}
36893678
}
36903679
/* }}} */
36913680

@@ -3734,7 +3723,7 @@ void zend_compile_while(zend_ast *ast) /* {{{ */
37343723

37353724
opnum_jmp = zend_emit_jump(0);
37363725

3737-
zend_begin_loop(NULL);
3726+
zend_begin_loop(ZEND_NOP, NULL);
37383727

37393728
opnum_start = get_next_op_number(CG(active_op_array));
37403729
zend_compile_stmt(stmt_ast);
@@ -3757,7 +3746,7 @@ void zend_compile_do_while(zend_ast *ast) /* {{{ */
37573746
znode cond_node;
37583747
uint32_t opnum_start, opnum_cond;
37593748

3760-
zend_begin_loop(NULL);
3749+
zend_begin_loop(ZEND_NOP, NULL);
37613750

37623751
opnum_start = get_next_op_number(CG(active_op_array));
37633752
zend_compile_stmt(stmt_ast);
@@ -3808,7 +3797,7 @@ void zend_compile_for(zend_ast *ast) /* {{{ */
38083797

38093798
opnum_jmp = zend_emit_jump(0);
38103799

3811-
zend_begin_loop(NULL);
3800+
zend_begin_loop(ZEND_NOP, NULL);
38123801

38133802
opnum_start = get_next_op_number(CG(active_op_array));
38143803
zend_compile_stmt(stmt_ast);
@@ -3890,8 +3879,7 @@ void zend_compile_foreach(zend_ast *ast) /* {{{ */
38903879
zend_emit_assign_znode(key_ast, &key_node);
38913880
}
38923881

3893-
reset_node.flag = ZEND_FE_FREE;
3894-
zend_begin_loop(&reset_node);
3882+
zend_begin_loop(ZEND_FE_FREE, &reset_node);
38953883

38963884
zend_compile_stmt(stmt_ast);
38973885

@@ -3966,8 +3954,7 @@ void zend_compile_switch(zend_ast *ast) /* {{{ */
39663954

39673955
zend_compile_expr(&expr_node, expr_ast);
39683956

3969-
expr_node.flag = 0; /* Generate normal FREE */
3970-
zend_begin_loop(&expr_node);
3957+
zend_begin_loop(ZEND_FREE, &expr_node);
39713958

39723959
case_node.op_type = IS_TMP_VAR;
39733960
case_node.u.op.var = get_temporary_variable(CG(active_op_array));
@@ -4075,9 +4062,9 @@ void zend_compile_try(zend_ast *ast) /* {{{ */
40754062

40764063
/* Push FAST_CALL on unwind stack */
40774064
fast_call.opcode = ZEND_FAST_CALL;
4078-
fast_call.var.op_type = IS_TMP_VAR;
4079-
fast_call.var.u.op.var = CG(context).fast_call_var;
4080-
fast_call.try_catch_offset = try_catch_offset;
4065+
fast_call.var_type = IS_TMP_VAR;
4066+
fast_call.var_num = CG(context).fast_call_var;
4067+
fast_call.u.try_catch_offset = try_catch_offset;
40814068
zend_stack_push(&CG(loop_var_stack), &fast_call);
40824069
}
40834070

Zend/zend_compile.h

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -712,7 +712,7 @@ void zend_do_extended_fcall_end(void);
712712

713713
void zend_verify_namespace(void);
714714

715-
zend_op *zend_resolve_goto_label(zend_op_array *op_array, zend_op *pass2_opline);
715+
void zend_resolve_goto_label(zend_op_array *op_array, zend_op *opline);
716716

717717
ZEND_API void function_add_ref(zend_function *function);
718718

Zend/zend_execute.c

Lines changed: 0 additions & 25 deletions
Original file line numberDiff line numberDiff line change
@@ -1944,31 +1944,6 @@ static zend_always_inline void zend_fetch_property_address(zval *result, zval *c
19441944
}
19451945
}
19461946

1947-
static inline zend_brk_cont_element* zend_brk_cont(int nest_levels, int array_offset, const zend_op_array *op_array, const zend_execute_data *execute_data)
1948-
{
1949-
zend_brk_cont_element *jmp_to;
1950-
1951-
do {
1952-
ZEND_ASSERT(array_offset != -1);
1953-
jmp_to = &op_array->brk_cont_array[array_offset];
1954-
if (nest_levels > 1 && jmp_to->start >= 0) {
1955-
zend_op *brk_opline = &op_array->opcodes[jmp_to->brk];
1956-
1957-
if (brk_opline->opcode == ZEND_FREE) {
1958-
zval_ptr_dtor_nogc(EX_VAR(brk_opline->op1.var));
1959-
} else if (brk_opline->opcode == ZEND_FE_FREE) {
1960-
zval *var = EX_VAR(brk_opline->op1.var);
1961-
if (Z_TYPE_P(var) != IS_ARRAY && Z_FE_ITER_P(var) != (uint32_t)-1) {
1962-
zend_hash_iterator_del(Z_FE_ITER_P(var));
1963-
}
1964-
zval_ptr_dtor_nogc(var);
1965-
}
1966-
}
1967-
array_offset = jmp_to->parent;
1968-
} while (--nest_levels > 0);
1969-
return jmp_to;
1970-
}
1971-
19721947
#if ZEND_INTENSIVE_DEBUGGING
19731948

19741949
#define CHECK_SYMBOL_TABLES() \

0 commit comments

Comments
 (0)