summaryrefslogtreecommitdiff
path: root/Zend/zend_compile.c
diff options
context:
space:
mode:
Diffstat (limited to 'Zend/zend_compile.c')
-rw-r--r--Zend/zend_compile.c161
1 files changed, 154 insertions, 7 deletions
diff --git a/Zend/zend_compile.c b/Zend/zend_compile.c
index 78266f6172..91175af6b3 100644
--- a/Zend/zend_compile.c
+++ b/Zend/zend_compile.c
@@ -1598,6 +1598,10 @@ int zendlex(zend_parser_stack_elem *elem) /* {{{ */
again:
ZVAL_UNDEF(&zv);
retval = lex_scan(&zv);
+ if (EG(exception)) {
+ return T_ERROR;
+ }
+
switch (retval) {
case T_COMMENT:
case T_DOC_COMMENT:
@@ -2749,7 +2753,8 @@ uint32_t zend_compile_args(zend_ast *ast, zend_function *fbc) /* {{{ */
}
} else {
zend_compile_expr(&arg_node, arg);
- if (arg_node.op_type & (IS_VAR|IS_CV)) {
+ ZEND_ASSERT(arg_node.op_type != IS_CV);
+ if (arg_node.op_type == IS_VAR) {
opcode = ZEND_SEND_VAR_NO_REF;
if (fbc && ARG_MUST_BE_SENT_BY_REF(fbc, arg_num)) {
flags |= ZEND_ARG_SEND_BY_REF;
@@ -3509,9 +3514,20 @@ void zend_compile_unset(zend_ast *ast) /* {{{ */
}
/* }}} */
-static void zend_free_foreach_and_switch_variables(void) /* {{{ */
+static void zend_free_foreach_and_switch_variables(uint32_t flags) /* {{{ */
{
+ uint32_t start_op_number = get_next_op_number(CG(active_op_array));
+
zend_stack_apply(&CG(loop_var_stack), ZEND_STACK_APPLY_TOPDOWN, (int (*)(void *element)) generate_free_loop_var);
+
+ if (flags) {
+ uint32_t end_op_number = get_next_op_number(CG(active_op_array));
+
+ while (start_op_number < end_op_number) {
+ CG(active_op_array)->opcodes[start_op_number].extended_value |= flags;
+ start_op_number++;
+ }
+ }
}
/* }}} */
@@ -3533,7 +3549,7 @@ void zend_compile_return(zend_ast *ast) /* {{{ */
zend_compile_expr(&expr_node, expr_ast);
}
- zend_free_foreach_and_switch_variables();
+ zend_free_foreach_and_switch_variables(ZEND_FREE_ON_RETURN);
if (CG(context).in_finally) {
opline = zend_emit_op(NULL, ZEND_DISCARD_EXCEPTION, NULL, NULL);
@@ -3636,6 +3652,118 @@ 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 (op_array->brk_cont_array[current].start >= 0) {
+ zend_emit_op(NULL, ZEND_NOP, NULL, NULL);
+ }
+ current = op_array->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 (op_array->brk_cont_array[current].start >= 0) {
+ if (pass2_opline) {
+ free_vars++;
+ } else {
+ generate_free_loop_var(loop_var);
+ loop_var--;
+ }
+ }
+ current = op_array->brk_cont_array[current].parent;
+ }
+
+ if (pass2_opline) {
+ if (free_vars) {
+ current = pass2_opline->extended_value;
+ while (current != dest->brk_cont) {
+ if (op_array->brk_cont_array[current].start >= 0) {
+ zend_op *brk_opline = &op_array->opcodes[op_array->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;
+ if (op_array->fn_flags & ZEND_ACC_HAS_FINALLY_BLOCK) {
+ (pass2_opline - free_vars)->op1.var = brk_opline->op1.var;
+ } else {
+ (pass2_opline - free_vars)->op1.var = (uint32_t)(zend_intptr_t)ZEND_CALL_VAR_NUM(NULL, op_array->last_var + brk_opline->op1.var);
+ ZEND_VM_SET_OPCODE_HANDLER(pass2_opline - free_vars);
+ }
+ free_vars--;
+ } 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;
+ if (op_array->fn_flags & ZEND_ACC_HAS_FINALLY_BLOCK) {
+ (pass2_opline - free_vars)->op1.var = brk_opline->op1.var;
+ } else {
+ (pass2_opline - free_vars)->op1.var = (uint32_t)(zend_intptr_t)ZEND_CALL_VAR_NUM(NULL, op_array->last_var + brk_opline->op1.var);
+ ZEND_VM_SET_OPCODE_HANDLER(pass2_opline - free_vars);
+ }
+ free_vars--;
+ }
+ }
+ current = op_array->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];
@@ -3989,14 +4117,26 @@ void zend_compile_try(zend_ast *ast) /* {{{ */
uint32_t i;
zend_op *opline;
- uint32_t try_catch_offset = zend_add_try_element(
- get_next_op_number(CG(active_op_array)));
+ uint32_t try_catch_offset;
uint32_t *jmp_opnums = safe_emalloc(sizeof(uint32_t), catches->children, 0);
if (catches->children == 0 && !finally_ast) {
zend_error_noreturn(E_COMPILE_ERROR, "Cannot use try without catch or finally");
}
+ /* label: try { } must not be equal to try { label: } */
+ if (CG(context).labels) {
+ zend_label *label;
+ ZEND_HASH_REVERSE_FOREACH_PTR(CG(context).labels, label) {
+ if (label->opline_num == get_next_op_number(CG(active_op_array))) {
+ zend_emit_op(NULL, ZEND_NOP, NULL, NULL);
+ }
+ break;
+ } ZEND_HASH_FOREACH_END();
+ }
+
+ try_catch_offset = zend_add_try_element(get_next_op_number(CG(active_op_array)));
+
zend_compile_stmt(try_ast);
if (catches->children != 0) {
@@ -4710,6 +4850,7 @@ void zend_compile_func_decl(znode *result, zend_ast *ast) /* {{{ */
zend_bool is_method = decl->kind == ZEND_AST_METHOD;
zend_op_array *orig_op_array = CG(active_op_array);
+ zend_class_entry *orig_ce = CG(active_class_entry);
zend_op_array *op_array = zend_arena_alloc(&CG(arena), sizeof(zend_op_array));
zend_oparray_context orig_oparray_context;
@@ -4734,6 +4875,11 @@ void zend_compile_func_decl(znode *result, zend_ast *ast) /* {{{ */
}
CG(active_op_array) = op_array;
+
+ if (!is_method) {
+ CG(active_class_entry) = NULL;
+ }
+
zend_oparray_context_begin(&orig_oparray_context);
if (CG(compiler_options) & ZEND_COMPILE_EXTENDED_INFO) {
@@ -4769,6 +4915,7 @@ void zend_compile_func_decl(znode *result, zend_ast *ast) /* {{{ */
/* Pop the loop variable stack separator */
zend_stack_del_top(&CG(loop_var_stack));
+ CG(active_class_entry) = orig_ce;
CG(active_op_array) = orig_op_array;
}
/* }}} */
@@ -5577,9 +5724,9 @@ static zend_bool zend_try_ct_eval_magic_const(zval *zv, zend_ast *ast) /* {{{ */
if (strcmp(ZSTR_VAL(dirname), ".") == 0) {
dirname = zend_string_extend(dirname, MAXPATHLEN, 0);
#if HAVE_GETCWD
- VCWD_GETCWD(ZSTR_VAL(dirname), MAXPATHLEN);
+ ZEND_IGNORE_VALUE(VCWD_GETCWD(ZSTR_VAL(dirname), MAXPATHLEN));
#elif HAVE_GETWD
- VCWD_GETWD(ZSTR_VAL(dirname));
+ ZEND_IGNORE_VALUE(VCWD_GETWD(ZSTR_VAL(dirname)));
#endif
}