summaryrefslogtreecommitdiff
path: root/Zend/zend_compile.c
diff options
context:
space:
mode:
authorDmitry Stogov <dmitry@zend.com>2015-07-10 03:31:52 +0300
committerDmitry Stogov <dmitry@zend.com>2015-07-10 03:31:52 +0300
commit549e8c495947d92083bab8101cef777dc4d1cb6f (patch)
tree4b75623fe149147a5ff5e34aa2a2f5187bac012e /Zend/zend_compile.c
parenta49ce7bb91bec02d6f26b3118404371df23242fe (diff)
downloadphp-git-549e8c495947d92083bab8101cef777dc4d1cb6f.tar.gz
Squashed commit of the following:
commit 03cf871f1576f08b2348c141b209894a7bf17a86 Author: Dmitry Stogov <dmitry@zend.com> Date: Fri Jul 10 02:45:31 2015 +0300 Revert "Fixed bug #62210 (Exceptions can leak temporary variables. As a part of the fix serious refactoring was done. op_array->brk_cont_array was removed, and replaced with more general and speed efficient op_array->T_liveliness. ZEND_GOTO opcode is always replaced by ZEND_JMP at compile time). (Bob, Dmitry, Laruence)" This reverts commit 5ee841325901a4b040cfea56292a24702fe224d9. commit 285a68227ce3d380e821a24fa389aa5239bd3fe1 Author: Dmitry Stogov <dmitry@zend.com> Date: Fri Jul 10 02:45:26 2015 +0300 Revert "Tuned off dubugging of live ranges" This reverts commit 404dc93d35f7061fc4b1b41ad6cb0721b9b52bcc. commit 93d9d11157301ee2ec99afb6f5744b126d17f637 Author: Dmitry Stogov <dmitry@zend.com> Date: Fri Jul 10 02:45:17 2015 +0300 Revert "Remove loop_var_stack" This reverts commit b3a4c05071c3786e27e1326fa1b4d5acad62fccd. commit ede68ebbc284aec79e3f719f2c8dbf9da6907752 Author: Dmitry Stogov <dmitry@zend.com> Date: Fri Jul 10 02:45:12 2015 +0300 Revert "ZEND_SEPARATE reuses temporaries" This reverts commit 1852f538b9f8d5e7d67fe5a4f6080396d8b10034. commit 96d8995dc1f517fb01b481736273767509f76c47 Author: Dmitry Stogov <dmitry@zend.com> Date: Fri Jul 10 02:45:10 2015 +0300 Revert "Add assertion in liveliness computation" This reverts commit ed14019e8c0c852480eebc6fc552d8c3d939dce1. commit 0649d7bfef152e6cc8e67b922534e9946c634d9c Author: Dmitry Stogov <dmitry@zend.com> Date: Fri Jul 10 02:45:07 2015 +0300 Revert "Fixed invalid live-range detection" This reverts commit 54f367ee2a2e4cb7c952b17915c226fdc56038ab. commit dfe8f3851f6b04595eb089323e3492115a59363e Author: Dmitry Stogov <dmitry@zend.com> Date: Fri Jul 10 02:45:04 2015 +0300 Revert "Add test guaranteeing that loop vars are only freed after potential return type exceptions" This reverts commit f5db5a558d550bf441373febebbb02f3884209d1. commit 52a94aad6f48a199358cc07f7e4f56bb73050504 Author: Dmitry Stogov <dmitry@zend.com> Date: Fri Jul 10 02:45:01 2015 +0300 Revert "Fixed exception habdling on "return" statement." This reverts commit 17c5315bdf8f8087979aeb55f6d3a512ba197cf5. commit 6e90ad7331901711e89c2ceb2bcab5023e5cee60 Author: Dmitry Stogov <dmitry@zend.com> Date: Fri Jul 10 02:44:58 2015 +0300 Revert "Fix too early terminated temporary range with break/cont/goto" This reverts commit cc876c04b420589cb1f62b650d0c0e24975dd4af. commit 7b766e44b1970e4031f75109c302c07ead2c05cb Author: Dmitry Stogov <dmitry@zend.com> Date: Fri Jul 10 02:44:55 2015 +0300 Revert "Fixed exception catching on break/continue" This reverts commit 8c3f701eebfa92d761bb368cfa8c2d1ccf821b9d.
Diffstat (limited to 'Zend/zend_compile.c')
-rw-r--r--Zend/zend_compile.c260
1 files changed, 126 insertions, 134 deletions
diff --git a/Zend/zend_compile.c b/Zend/zend_compile.c
index d4b2767940..0927f0cd23 100644
--- a/Zend/zend_compile.c
+++ b/Zend/zend_compile.c
@@ -32,7 +32,6 @@
#include "zend_multibyte.h"
#include "zend_language_scanner.h"
#include "zend_inheritance.h"
-#include "zend_vm.h"
#define SET_NODE(target, src) do { \
target ## _type = (src)->op_type; \
@@ -211,21 +210,16 @@ void zend_oparray_context_begin(zend_oparray_context *prev_context) /* {{{ */
CG(context).opcodes_size = INITIAL_OP_ARRAY_SIZE;
CG(context).vars_size = 0;
CG(context).literals_size = 0;
+ CG(context).current_brk_cont = -1;
CG(context).backpatch_count = 0;
CG(context).in_finally = 0;
CG(context).fast_call_var = -1;
- CG(context).current_brk_cont = -1;
- CG(context).last_brk_cont = 0;
- CG(context).brk_cont_array = NULL;
CG(context).labels = NULL;
}
/* }}} */
void zend_oparray_context_end(zend_oparray_context *prev_context) /* {{{ */
{
- if (CG(context).brk_cont_array) {
- efree(CG(context).brk_cont_array);
- }
if (CG(context).labels) {
zend_hash_destroy(CG(context).labels);
FREE_HASHTABLE(CG(context).labels);
@@ -289,6 +283,7 @@ void zend_file_context_end(zend_file_context *prev_context) /* {{{ */
void zend_init_compiler_data_structures(void) /* {{{ */
{
+ zend_stack_init(&CG(loop_var_stack), sizeof(znode));
zend_stack_init(&CG(delayed_oplines_stack), sizeof(zend_op));
CG(active_class_entry) = NULL;
CG(in_compilation) = 0;
@@ -321,6 +316,7 @@ void init_compiler(void) /* {{{ */
void shutdown_compiler(void) /* {{{ */
{
+ zend_stack_destroy(&CG(loop_var_stack));
zend_stack_destroy(&CG(delayed_oplines_stack));
zend_hash_destroy(&CG(filenames_table));
zend_hash_destroy(&CG(const_filenames));
@@ -566,13 +562,17 @@ static inline void zend_begin_loop(const znode *loop_var) /* {{{ */
zend_brk_cont_element *brk_cont_element;
int parent = CG(context).current_brk_cont;
- CG(context).current_brk_cont = CG(context).last_brk_cont;
+ CG(context).current_brk_cont = CG(active_op_array)->last_brk_cont;
brk_cont_element = get_next_brk_cont_element(CG(active_op_array));
brk_cont_element->parent = parent;
- if (loop_var && (loop_var->op_type & (IS_TMP_VAR|IS_VAR))) {
- brk_cont_element->loop_var = *loop_var;
+
+ if (loop_var) {
+ zend_stack_push(&CG(loop_var_stack), loop_var);
+ brk_cont_element->start = get_next_op_number(CG(active_op_array));
} else {
- brk_cont_element->loop_var.op_type = IS_UNUSED;
+ /* The start field is used to free temporary variables in case of exceptions.
+ * We won't try to free something of we don't have loop variable. */
+ brk_cont_element->start = -1;
}
}
/* }}} */
@@ -580,10 +580,14 @@ static inline void zend_begin_loop(const znode *loop_var) /* {{{ */
static inline void zend_end_loop(int cont_addr) /* {{{ */
{
zend_brk_cont_element *brk_cont_element
- = &CG(context).brk_cont_array[CG(context).current_brk_cont];
+ = &CG(active_op_array)->brk_cont_array[CG(context).current_brk_cont];
brk_cont_element->cont = cont_addr;
brk_cont_element->brk = get_next_op_number(CG(active_op_array));
CG(context).current_brk_cont = brk_cont_element->parent;
+
+ if (brk_cont_element->start >= 0) {
+ zend_stack_del_top(&CG(loop_var_stack));
+ }
}
/* }}} */
@@ -870,25 +874,83 @@ static void str_dtor(zval *zv) /* {{{ */ {
}
/* }}} */
-static zend_bool zend_is_call(zend_ast *ast);
-
-static void generate_free_loop_var_ex(znode *var, uint32_t flags) /* {{{ */
+void zend_resolve_goto_label(zend_op_array *op_array, zend_op *opline, int pass2) /* {{{ */
{
- if (var->op_type != IS_UNUSED) {
- zend_op *opline = get_next_op(CG(active_op_array));
+ 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) {
- opline->opcode = var->flag ? ZEND_FE_FREE : ZEND_FREE;
- SET_NODE(opline->op1, var);
+ 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 = op_array->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);
- opline->extended_value = flags;
+ } else {
+ /* Set real break distance */
+ ZVAL_LONG(label, distance);
}
}
/* }}} */
-static void generate_free_loop_var(znode *var) /* {{{ */
+static zend_bool zend_is_call(zend_ast *ast);
+
+static int generate_free_loop_var(znode *var) /* {{{ */
{
- generate_free_loop_var_ex(var, 0);
+ switch (var->op_type) {
+ case IS_UNUSED:
+ /* Stack separator on function boundary, stop applying */
+ return 1;
+ case IS_VAR:
+ case IS_TMP_VAR:
+ {
+ zend_op *opline = get_next_op(CG(active_op_array));
+
+ opline->opcode = var->flag ? ZEND_FE_FREE : ZEND_FREE;
+ SET_NODE(opline->op1, var);
+ SET_UNUSED(opline->op2);
+ }
+ }
+
+ return 0;
}
+/* }}} */
static uint32_t zend_add_try_element(uint32_t try_op) /* {{{ */
{
@@ -3452,14 +3514,9 @@ void zend_compile_unset(zend_ast *ast) /* {{{ */
}
/* }}} */
-static void zend_free_foreach_and_switch_variables(uint32_t flags) /* {{{ */
+static void zend_free_foreach_and_switch_variables(void) /* {{{ */
{
- int array_offset = CG(context).current_brk_cont;
- while (array_offset != -1) {
- zend_brk_cont_element *brk_cont = &CG(context).brk_cont_array[array_offset];
- generate_free_loop_var_ex(&brk_cont->loop_var, flags);
- array_offset = brk_cont->parent;
- }
+ zend_stack_apply(&CG(loop_var_stack), ZEND_STACK_APPLY_TOPDOWN, (int (*)(void *element)) generate_free_loop_var);
}
/* }}} */
@@ -3481,12 +3538,7 @@ void zend_compile_return(zend_ast *ast) /* {{{ */
zend_compile_expr(&expr_node, expr_ast);
}
- /* Generator return types are handled separately */
- if (!(CG(active_op_array)->fn_flags & ZEND_ACC_GENERATOR) && CG(active_op_array)->fn_flags & ZEND_ACC_HAS_RETURN_TYPE) {
- zend_emit_return_type_check(expr_ast ? &expr_node : NULL, CG(active_op_array)->arg_info - 1);
- }
-
- zend_free_foreach_and_switch_variables(ZEND_FREE_ON_RETURN);
+ zend_free_foreach_and_switch_variables();
if (CG(context).in_finally) {
opline = zend_emit_op(NULL, ZEND_DISCARD_EXCEPTION, NULL, NULL);
@@ -3494,6 +3546,10 @@ void zend_compile_return(zend_ast *ast) /* {{{ */
opline->op1.var = CG(context).fast_call_var;
}
+ /* Generator return types are handled separately */
+ if (!(CG(active_op_array)->fn_flags & ZEND_ACC_GENERATOR) && CG(active_op_array)->fn_flags & ZEND_ACC_HAS_RETURN_TYPE) {
+ zend_emit_return_type_check(expr_ast ? &expr_node : NULL, CG(active_op_array)->arg_info - 1);
+ }
opline = zend_emit_op(NULL, by_ref ? ZEND_RETURN_BY_REF : ZEND_RETURN,
&expr_node, NULL);
@@ -3562,6 +3618,7 @@ void zend_compile_break_continue(zend_ast *ast) /* {{{ */
} else {
int array_offset = CG(context).current_brk_cont;
zend_long nest_level = depth;
+ znode *loop_var = zend_stack_top(&CG(loop_var_stack));
do {
if (array_offset == -1) {
@@ -3570,11 +3627,12 @@ void zend_compile_break_continue(zend_ast *ast) /* {{{ */
depth, depth == 1 ? "" : "s");
}
- if (nest_level > 1) {
- generate_free_loop_var_ex(&CG(context).brk_cont_array[array_offset].loop_var, ZEND_FREE_ON_BREAK);
+ if (nest_level > 1 && CG(active_op_array)->brk_cont_array[array_offset].start >= 0) {
+ generate_free_loop_var(loop_var);
+ loop_var--;
}
- array_offset = CG(context).brk_cont_array[array_offset].parent;
+ array_offset = CG(active_op_array)->brk_cont_array[array_offset].parent;
} while (--nest_level > 0);
}
opline = zend_emit_op(NULL, ast->kind == ZEND_AST_BREAK ? ZEND_BRK : ZEND_CONT, NULL, NULL);
@@ -3583,108 +3641,16 @@ 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;
-
- 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].loop_var.op_type != IS_UNUSED) {
- zend_emit_op(NULL, ZEND_NOP, NULL, NULL);
- }
- 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;
- }
- 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].loop_var.op_type != IS_UNUSED) {
- if (pass2_opline) {
- free_vars++;
- } else {
- generate_free_loop_var_ex(&CG(context).brk_cont_array[current].loop_var, ZEND_FREE_ON_BREAK);
- }
- }
- 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].loop_var.op_type != IS_UNUSED) {
- zend_op *brk_opline = &op_array->opcodes[CG(context).brk_cont_array[current].brk];
-
- (pass2_opline - free_vars)->opcode = brk_opline->opcode;
- (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 = 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);
- zend_resolve_goto_label(CG(active_op_array), &label_node, NULL);
+ 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);
}
/* }}} */
@@ -4780,6 +4746,14 @@ void zend_compile_func_decl(znode *result, zend_ast *ast) /* {{{ */
opline_ext->lineno = decl->start_lineno;
}
+ {
+ /* Push a separator to the loop variable stack */
+ znode dummy_var;
+ dummy_var.op_type = IS_UNUSED;
+
+ zend_stack_push(&CG(loop_var_stack), (void *) &dummy_var);
+ }
+
zend_compile_params(params_ast, return_type_ast);
if (uses_ast) {
zend_compile_closure_uses(uses_ast);
@@ -4797,6 +4771,9 @@ void zend_compile_func_decl(znode *result, zend_ast *ast) /* {{{ */
pass_two(CG(active_op_array));
zend_oparray_context_end(&orig_oparray_context);
+ /* Pop the loop variable stack separator */
+ zend_stack_del_top(&CG(loop_var_stack));
+
CG(active_op_array) = orig_op_array;
}
/* }}} */
@@ -6313,7 +6290,10 @@ void zend_compile_silence(znode *result, zend_ast *ast) /* {{{ */
{
zend_ast *expr_ast = ast->child[0];
znode silence_node;
+ uint32_t begin_opline_num, end_opline_num;
+ zend_brk_cont_element *brk_cont_element;
+ begin_opline_num = get_next_op_number(CG(active_op_array));
zend_emit_op_tmp(&silence_node, ZEND_BEGIN_SILENCE, NULL, NULL);
if (expr_ast->kind == ZEND_AST_VAR) {
@@ -6324,7 +6304,15 @@ void zend_compile_silence(znode *result, zend_ast *ast) /* {{{ */
zend_compile_expr(result, expr_ast);
}
+ end_opline_num = get_next_op_number(CG(active_op_array));
zend_emit_op(NULL, ZEND_END_SILENCE, &silence_node, NULL);
+
+ /* Store BEGIN_SILENCE/END_SILENCE pair to restore previous
+ * EG(error_reporting) value on exception */
+ brk_cont_element = get_next_brk_cont_element(CG(active_op_array));
+ brk_cont_element->start = begin_opline_num;
+ brk_cont_element->cont = brk_cont_element->brk = end_opline_num;
+ brk_cont_element->parent = -1;
}
/* }}} */
@@ -6652,6 +6640,10 @@ static void zend_compile_encaps_list(znode *result, zend_ast *ast) /* {{{ */
GET_NODE(result, opline->result);
} else {
uint32_t var;
+ zend_brk_cont_element *info = get_next_brk_cont_element(CG(active_op_array));
+ info->start = rope_init_lineno;
+ info->parent = CG(context).current_brk_cont;
+ info->cont = info->brk = opline - CG(active_op_array)->opcodes;
init_opline->extended_value = j;
opline->opcode = ZEND_ROPE_END;