|
|
|
@ -879,61 +879,6 @@ static void str_dtor(zval *zv) /* {{{ */ { |
|
|
|
} |
|
|
|
/* }}} */ |
|
|
|
|
|
|
|
void zend_resolve_goto_label(zend_op_array *op_array, zend_op *opline, int pass2) /* {{{ */ |
|
|
|
{ |
|
|
|
zend_label *dest; |
|
|
|
int current, distance; |
|
|
|
zval *label; |
|
|
|
|
|
|
|
if (pass2) { |
|
|
|
label = RT_CONSTANT(op_array, opline->op2); |
|
|
|
} else { |
|
|
|
label = CT_CONSTANT_EX(op_array, opline->op2.constant); |
|
|
|
} |
|
|
|
if (CG(context).labels == NULL || |
|
|
|
(dest = zend_hash_find_ptr(CG(context).labels, Z_STR_P(label))) == NULL) { |
|
|
|
|
|
|
|
if (pass2) { |
|
|
|
CG(in_compilation) = 1; |
|
|
|
CG(active_op_array) = op_array; |
|
|
|
CG(zend_lineno) = opline->lineno; |
|
|
|
zend_error_noreturn(E_COMPILE_ERROR, "'goto' to undefined label '%s'", Z_STRVAL_P(label)); |
|
|
|
} else { |
|
|
|
/* Label is not defined. Delay to pass 2. */ |
|
|
|
return; |
|
|
|
} |
|
|
|
} |
|
|
|
|
|
|
|
opline->op1.opline_num = dest->opline_num; |
|
|
|
zval_dtor(label); |
|
|
|
ZVAL_NULL(label); |
|
|
|
|
|
|
|
/* Check that we are not moving into loop or switch */ |
|
|
|
current = opline->extended_value; |
|
|
|
for (distance = 0; current != dest->brk_cont; distance++) { |
|
|
|
if (current == -1) { |
|
|
|
if (pass2) { |
|
|
|
CG(in_compilation) = 1; |
|
|
|
CG(active_op_array) = op_array; |
|
|
|
CG(zend_lineno) = opline->lineno; |
|
|
|
} |
|
|
|
zend_error_noreturn(E_COMPILE_ERROR, "'goto' into loop or switch statement is disallowed"); |
|
|
|
} |
|
|
|
current = CG(context).brk_cont_array[current].parent; |
|
|
|
} |
|
|
|
|
|
|
|
if (distance == 0) { |
|
|
|
/* Nothing to break out of, optimize to ZEND_JMP */ |
|
|
|
opline->opcode = ZEND_JMP; |
|
|
|
opline->extended_value = 0; |
|
|
|
SET_UNUSED(opline->op2); |
|
|
|
} else { |
|
|
|
/* Set real break distance */ |
|
|
|
ZVAL_LONG(label, distance); |
|
|
|
} |
|
|
|
} |
|
|
|
/* }}} */ |
|
|
|
|
|
|
|
static zend_bool zend_is_call(zend_ast *ast); |
|
|
|
|
|
|
|
static int generate_free_loop_var(znode *var) /* {{{ */ |
|
|
|
@ -3633,16 +3578,114 @@ void zend_compile_break_continue(zend_ast *ast) /* {{{ */ |
|
|
|
} |
|
|
|
/* }}} */ |
|
|
|
|
|
|
|
void zend_resolve_goto_label(zend_op_array *op_array, znode *label_node, zend_op *pass2_opline) /* {{{ */ |
|
|
|
{ |
|
|
|
zend_label *dest; |
|
|
|
int current, distance, free_vars; |
|
|
|
zval *label; |
|
|
|
znode *loop_var = NULL; |
|
|
|
|
|
|
|
if (pass2_opline) { |
|
|
|
label = RT_CONSTANT(op_array, pass2_opline->op2); |
|
|
|
} else { |
|
|
|
label = &label_node->u.constant; |
|
|
|
} |
|
|
|
if (CG(context).labels == NULL || |
|
|
|
(dest = zend_hash_find_ptr(CG(context).labels, Z_STR_P(label))) == NULL) { |
|
|
|
|
|
|
|
if (pass2_opline) { |
|
|
|
CG(in_compilation) = 1; |
|
|
|
CG(active_op_array) = op_array; |
|
|
|
CG(zend_lineno) = pass2_opline->lineno; |
|
|
|
zend_error_noreturn(E_COMPILE_ERROR, "'goto' to undefined label '%s'", Z_STRVAL_P(label)); |
|
|
|
} else { |
|
|
|
/* Label is not defined. Delay to pass 2. */ |
|
|
|
zend_op *opline; |
|
|
|
|
|
|
|
current = CG(context).current_brk_cont; |
|
|
|
while (current != -1) { |
|
|
|
if (CG(context).brk_cont_array[current].start >= 0) { |
|
|
|
zend_emit_op(NULL, ZEND_NOP, NULL, label_node); |
|
|
|
} |
|
|
|
current = CG(context).brk_cont_array[current].parent; |
|
|
|
} |
|
|
|
opline = zend_emit_op(NULL, ZEND_GOTO, NULL, label_node); |
|
|
|
opline->extended_value = CG(context).current_brk_cont; |
|
|
|
return; |
|
|
|
} |
|
|
|
} |
|
|
|
|
|
|
|
zval_dtor(label); |
|
|
|
ZVAL_NULL(label); |
|
|
|
|
|
|
|
/* Check that we are not moving into loop or switch */ |
|
|
|
if (pass2_opline) { |
|
|
|
current = pass2_opline->extended_value; |
|
|
|
} else { |
|
|
|
current = CG(context).current_brk_cont; |
|
|
|
} |
|
|
|
if (!pass2_opline) { |
|
|
|
loop_var = zend_stack_top(&CG(loop_var_stack)); |
|
|
|
} |
|
|
|
for (distance = 0, free_vars = 0; current != dest->brk_cont; distance++) { |
|
|
|
if (current == -1) { |
|
|
|
if (pass2_opline) { |
|
|
|
CG(in_compilation) = 1; |
|
|
|
CG(active_op_array) = op_array; |
|
|
|
CG(zend_lineno) = pass2_opline->lineno; |
|
|
|
} |
|
|
|
zend_error_noreturn(E_COMPILE_ERROR, "'goto' into loop or switch statement is disallowed"); |
|
|
|
} |
|
|
|
if (CG(context).brk_cont_array[current].start >= 0) { |
|
|
|
if (pass2_opline) { |
|
|
|
free_vars++; |
|
|
|
} else { |
|
|
|
generate_free_loop_var(loop_var); |
|
|
|
loop_var--; |
|
|
|
} |
|
|
|
} |
|
|
|
current = CG(context).brk_cont_array[current].parent; |
|
|
|
} |
|
|
|
|
|
|
|
if (pass2_opline) { |
|
|
|
if (free_vars) { |
|
|
|
current = pass2_opline->extended_value; |
|
|
|
while (current != dest->brk_cont) { |
|
|
|
if (CG(context).brk_cont_array[current].start >= 0) { |
|
|
|
zend_op *brk_opline = &op_array->opcodes[CG(context).brk_cont_array[current].brk]; |
|
|
|
|
|
|
|
if (brk_opline->opcode == ZEND_FREE) { |
|
|
|
(pass2_opline - free_vars)->opcode = ZEND_FREE; |
|
|
|
(pass2_opline - free_vars)->op1_type = brk_opline->op1_type; |
|
|
|
(pass2_opline - free_vars)->op1.var = brk_opline->op1.var; |
|
|
|
} else if (brk_opline->opcode == ZEND_FE_FREE) { |
|
|
|
(pass2_opline - free_vars)->opcode = ZEND_FE_FREE; |
|
|
|
(pass2_opline - free_vars)->op1_type = brk_opline->op1_type; |
|
|
|
(pass2_opline - free_vars)->op1.var = brk_opline->op1.var; |
|
|
|
} |
|
|
|
free_vars--; |
|
|
|
} |
|
|
|
current = CG(context).brk_cont_array[current].parent; |
|
|
|
} |
|
|
|
} |
|
|
|
pass2_opline->opcode = ZEND_JMP; |
|
|
|
pass2_opline->op1.opline_num = dest->opline_num; |
|
|
|
SET_UNUSED(pass2_opline->op2); |
|
|
|
pass2_opline->extended_value = 0; |
|
|
|
} else { |
|
|
|
zend_op *opline = zend_emit_op(NULL, ZEND_JMP, NULL, NULL); |
|
|
|
opline->op1.opline_num = dest->opline_num; |
|
|
|
} |
|
|
|
} |
|
|
|
/* }}} */ |
|
|
|
|
|
|
|
void zend_compile_goto(zend_ast *ast) /* {{{ */ |
|
|
|
{ |
|
|
|
zend_ast *label_ast = ast->child[0]; |
|
|
|
znode label_node; |
|
|
|
zend_op *opline; |
|
|
|
|
|
|
|
zend_compile_expr(&label_node, label_ast); |
|
|
|
opline = zend_emit_op(NULL, ZEND_GOTO, NULL, &label_node); |
|
|
|
opline->extended_value = CG(context).current_brk_cont; |
|
|
|
zend_resolve_goto_label(CG(active_op_array), opline, 0); |
|
|
|
zend_resolve_goto_label(CG(active_op_array), &label_node, NULL); |
|
|
|
} |
|
|
|
/* }}} */ |
|
|
|
|
|
|
|
|