diff --git a/Zend/zend_compile.c b/Zend/zend_compile.c index 5a15392fe94..44a4c77ceb8 100644 --- a/Zend/zend_compile.c +++ b/Zend/zend_compile.c @@ -6276,10 +6276,8 @@ 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) { @@ -6290,15 +6288,7 @@ 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; } /* }}} */ @@ -6626,10 +6616,6 @@ 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; diff --git a/Zend/zend_compile.h b/Zend/zend_compile.h index a3bc17f4256..d478348fe3f 100644 --- a/Zend/zend_compile.h +++ b/Zend/zend_compile.h @@ -328,6 +328,9 @@ typedef struct _zend_internal_function_info { zend_bool _is_variadic; } zend_internal_function_info; +#define ZEND_LIVE_ROPE (1 << 0) +#define ZEND_LIVE_SILENCE (1 << 1) + struct _zend_op_array { /* Common elements */ zend_uchar type; @@ -351,6 +354,7 @@ struct _zend_op_array { int last_var; uint32_t T; zend_string **vars; + uint32_t *T_liveliness; int last_brk_cont; int last_try_catch; @@ -727,6 +731,7 @@ ZEND_API int zend_execute_scripts(int type, zval *retval, int file_count, ...); ZEND_API int open_file_for_scanning(zend_file_handle *file_handle); ZEND_API void init_op_array(zend_op_array *op_array, zend_uchar type, int initial_ops_size); ZEND_API void destroy_op_array(zend_op_array *op_array); +ZEND_API uint32_t *generate_var_liveliness_info(zend_op_array *op_array); ZEND_API void zend_destroy_file_handle(zend_file_handle *file_handle); ZEND_API void zend_cleanup_user_class_data(zend_class_entry *ce); ZEND_API void zend_cleanup_internal_class_data(zend_class_entry *ce); diff --git a/Zend/zend_execute.c b/Zend/zend_execute.c index bbe1537b888..9533683adfe 100644 --- a/Zend/zend_execute.c +++ b/Zend/zend_execute.c @@ -2385,6 +2385,41 @@ static zend_always_inline zend_generator *zend_get_running_generator(zend_execut static zend_always_inline void i_cleanup_unfinished_execution(zend_execute_data *execute_data, uint32_t op_num, uint32_t catch_op_num) /* {{{ */ { int i; + + if (EX(func)->op_array.T_liveliness && op_num < EX(func)->op_array.last) { + uint32_t *off = EX(func)->op_array.T_liveliness + EX(func)->op_array.T_liveliness[op_num]; + uint32_t *until = EX(func)->op_array.T_liveliness + EX(func)->op_array.T_liveliness[op_num + 1]; + while (off < until) { + uint32_t var = *(off++); + + if (var & ZEND_LIVE_ROPE) { + /* free incomplete rope */ + zend_string **rope = (zend_string **) EX_VAR(var & ~ZEND_LIVE_ROPE); + zend_op *last = EX(func)->op_array.opcodes + op_num; + while (last->opcode != ZEND_ROPE_ADD && last->opcode != ZEND_ROPE_INIT) { + ZEND_ASSERT(last >= EX(func)->op_array.opcodes); + last--; + } + if (last->opcode == ZEND_ROPE_INIT) { + zend_string_release(*rope); + } else { + int j = last->extended_value; + do { + zend_string_release(rope[j]); + } while (j--); + } + } else if (var & ZEND_LIVE_SILENCE) { + /* restore previous error_reporting value */ + var = var & ~ZEND_LIVE_SILENCE; + if (!EG(error_reporting) && Z_LVAL_P(EX_VAR(var)) != 0) { + EG(error_reporting) = Z_LVAL_P(EX_VAR(var)); + } + } else { + zval_ptr_dtor_nogc(EX_VAR(var)); + } + } + } + if (UNEXPECTED(EX(call))) { zend_execute_data *call = EX(call); zend_op *opline = EX(func)->op_array.opcodes + op_num; @@ -2511,34 +2546,13 @@ static zend_always_inline void i_cleanup_unfinished_execution(zend_execute_data if (!catch_op_num || catch_op_num >= brk_cont->brk) { zend_op *brk_opline = &EX(func)->op_array.opcodes[brk_cont->brk]; - if (brk_opline->opcode == ZEND_FREE) { - zval_ptr_dtor_nogc(EX_VAR(brk_opline->op1.var)); - } else if (brk_opline->opcode == ZEND_FE_FREE) { + /* ZEND_FREE opcodes are handled by temporary variable freeing */ + if (brk_opline->opcode == ZEND_FE_FREE) { zval *var = EX_VAR(brk_opline->op1.var); if (Z_TYPE_P(var) != IS_ARRAY && Z_FE_ITER_P(var) != (uint32_t)-1) { zend_hash_iterator_del(Z_FE_ITER_P(var)); } zval_ptr_dtor_nogc(var); - } else if (brk_opline->opcode == ZEND_ROPE_END) { - zend_string **rope = (zend_string **) EX_VAR(brk_opline->op1.var); - zend_op *last = EX(func)->op_array.opcodes + op_num; - while (last->opcode != ZEND_ROPE_ADD && last->opcode != ZEND_ROPE_INIT) { - ZEND_ASSERT(last >= EX(func)->op_array.opcodes); - last--; - } - if (last->opcode == ZEND_ROPE_INIT) { - zend_string_release(*rope); - } else { - int j = last->extended_value; - do { - zend_string_release(rope[j]); - } while (j--); - } - } else if (brk_opline->opcode == ZEND_END_SILENCE) { - /* restore previous error_reporting value */ - if (!EG(error_reporting) && Z_LVAL_P(EX_VAR(brk_opline->op1.var)) != 0) { - EG(error_reporting) = Z_LVAL_P(EX_VAR(brk_opline->op1.var)); - } } } } diff --git a/Zend/zend_opcode.c b/Zend/zend_opcode.c index a971a5e9004..3b2b4b7a0f4 100644 --- a/Zend/zend_opcode.c +++ b/Zend/zend_opcode.c @@ -65,6 +65,7 @@ void init_op_array(zend_op_array *op_array, zend_uchar type, int initial_ops_siz op_array->vars = NULL; op_array->T = 0; + op_array->T_liveliness = NULL; op_array->function_name = NULL; op_array->filename = zend_get_compiled_filename(); @@ -389,6 +390,9 @@ ZEND_API void destroy_op_array(zend_op_array *op_array) if (op_array->try_catch_array) { efree(op_array->try_catch_array); } + if (op_array->T_liveliness) { + efree(op_array->T_liveliness); + } if (op_array->fn_flags & ZEND_ACC_DONE_PASS_TWO) { zend_llist_apply_with_argument(&zend_extensions, (llist_apply_with_arg_func_t) zend_extension_op_array_dtor_handler, op_array); } @@ -576,7 +580,7 @@ static void zend_resolve_finally_call(zend_op_array *op_array, uint32_t op_num, fast_call_var = op_array->opcodes[op_array->try_catch_array[i].finally_end].op1.var; /* generate a FAST_CALL to finally block */ - start_op = get_next_op_number(op_array); + start_op = get_next_op_number(op_array); opline = get_next_op(op_array); opline->opcode = ZEND_FAST_CALL; @@ -751,6 +755,9 @@ ZEND_API int pass_two(zend_op_array *op_array) op_array->literals = (zval*)erealloc(op_array->literals, sizeof(zval) * op_array->last_literal); CG(context).literals_size = op_array->last_literal; } + + op_array->T_liveliness = generate_var_liveliness_info(op_array); + opline = op_array->opcodes; end = opline + op_array->last; while (opline < end) { @@ -840,6 +847,174 @@ int pass_two_wrapper(zval *el) return pass_two((zend_op_array *) Z_PTR_P(el)); } +typedef struct _var_live_info { + struct _var_live_info *next; + uint32_t start; + uint32_t end; +} var_live_info; + +typedef struct _op_var_info { + struct _op_var_info *next; + uint32_t var; +} op_var_info; + +ZEND_API uint32_t *generate_var_liveliness_info(zend_op_array *op_array) +{ + void *checkpoint = zend_arena_checkpoint(CG(arena)); + var_live_info **TsTop = zend_arena_alloc(&CG(arena), sizeof(var_live_info *) * op_array->T); + var_live_info **Ts = zend_arena_alloc(&CG(arena), sizeof(var_live_info *) * op_array->T); + int i, op_live_total = 0; + uint32_t *info, info_off = op_array->last + 1; + op_var_info *opTsTop = zend_arena_alloc(&CG(arena), sizeof(op_var_info) * (op_array->last + 1)); + op_var_info **opTs = zend_arena_alloc(&CG(arena), sizeof(op_var_info *) * (op_array->last + 1)); + + for (i = 0; i < op_array->T; i++) { + TsTop[i] = Ts[i] = zend_arena_alloc(&CG(arena), sizeof(var_live_info)); + Ts[i]->next = NULL; + Ts[i]->start = Ts[i]->end = -1; + } + + for (i = 0; i <= op_array->last; i++) { + opTs[i] = &opTsTop[i]; + opTs[i]->var = -1; + opTs[i]->next = NULL; + } + + zend_op *end_op = op_array->opcodes + op_array->last; + zend_op *cur_op = op_array->opcodes; + for (; cur_op < end_op; cur_op++) { + if ((cur_op->result_type & (IS_VAR | IS_TMP_VAR)) + && !(cur_op->result_type & EXT_TYPE_UNUSED) + && cur_op->opcode != ZEND_BOOL /* why ??? */ + && cur_op->opcode != ZEND_JMPZ_EX /* why ??? */ + && cur_op->opcode != ZEND_JMPNZ_EX /* why ??? */ + && (cur_op->opcode != ZEND_QM_ASSIGN + || (cur_op + 1)->opcode != ZEND_JMP) /* why ??? */ + && cur_op->opcode != ZEND_CASE /* ??? exception for opcache, is anyway bool */ + && cur_op->opcode != ZEND_FE_RESET_R /* FOREACH TMP is handled using brk_cont_array */ + && cur_op->opcode != ZEND_FE_RESET_RW /* FOREACH TMP is handled using brk_cont_array */ + && cur_op->opcode != ZEND_ROPE_ADD /* the following opocodes reuse TMP created before */ + && cur_op->opcode != ZEND_ADD_ARRAY_ELEMENT + && cur_op->opcode != ZEND_FAST_CALL /* passes fast_call */ + && cur_op->opcode != ZEND_FETCH_CLASS /* the following opcodes pass class_entry */ + && cur_op->opcode != ZEND_DECLARE_CLASS + && cur_op->opcode != ZEND_DECLARE_INHERITED_CLASS + && cur_op->opcode != ZEND_DECLARE_INHERITED_CLASS_DELAYED + && cur_op->opcode != ZEND_DECLARE_ANON_CLASS + && cur_op->opcode != ZEND_DECLARE_ANON_INHERITED_CLASS) { + var_live_info *T = Ts[cur_op->result.var]; + if (~T->end) { + T = Ts[cur_op->result.var] = T->next = zend_arena_alloc(&CG(arena), sizeof(var_live_info)); + T->next = NULL; + T->start = T->end = -1; + } + if (!~T->start) { + /* Objects created via ZEND_NEW are only fully initialized after the DO_FCALL (constructor call) */ + if (cur_op->opcode == ZEND_NEW) { + T->start = cur_op->op2.opline_num - 1; + } else { + T->start = cur_op - op_array->opcodes; + } + } + } + if ((cur_op->op1_type & (IS_VAR | IS_TMP_VAR)) + && cur_op->opcode != ZEND_FE_FETCH_R /* FOREACH TMP is handled using brk_cont_array */ + && cur_op->opcode != ZEND_FE_FETCH_RW /* FOREACH TMP is handled using brk_cont_array */ + && cur_op->opcode != ZEND_FE_FREE /* FOREACH TMP is handled using brk_cont_array */ + && cur_op->opcode != ZEND_ROPE_ADD /* the following opcodes don't free TMP */ + && cur_op->opcode != ZEND_CASE + && cur_op->opcode != ZEND_FETCH_LIST + && cur_op->opcode != ZEND_FAST_RET /* uses fast_call */ + && cur_op->opcode != ZEND_NEW /* the following opcodes use class_entry */ + && cur_op->opcode != ZEND_INIT_STATIC_METHOD_CALL + && cur_op->opcode != ZEND_FETCH_CONSTANT + && cur_op->opcode != ZEND_ADD_INTERFACE + && cur_op->opcode != ZEND_ADD_TRAIT + && cur_op->opcode != ZEND_BIND_TRAITS + && cur_op->opcode != ZEND_VERIFY_ABSTRACT_CLASS) { + var_live_info *T = Ts[cur_op->op1.var]; + if (~T->start) { + T->end = cur_op - op_array->opcodes; + if (cur_op->opcode == ZEND_OP_DATA) { + T->end--; + } + } + } + if ((cur_op->op2_type & (IS_VAR | IS_TMP_VAR)) + && cur_op->opcode != ZEND_FETCH_R /* the following opcodes use class_entry */ + && cur_op->opcode != ZEND_FETCH_W + && cur_op->opcode != ZEND_FETCH_RW + && cur_op->opcode != ZEND_FETCH_IS + && cur_op->opcode != ZEND_FETCH_FUNC_ARG + && cur_op->opcode != ZEND_FETCH_UNSET + && cur_op->opcode != ZEND_UNSET_VAR + && cur_op->opcode != ZEND_ISSET_ISEMPTY_VAR + && cur_op->opcode != ZEND_INSTANCEOF) { + var_live_info *T = Ts[cur_op->op2.var]; + if (~T->start) { + T->end = cur_op - op_array->opcodes; + if (cur_op->opcode == ZEND_OP_DATA) { + T->end--; + } + } + } + } + + for (i = 0; i < op_array->T; i++) { + int j; + var_live_info *T = TsTop[i]; + + do { + if (!~T->start) { + continue; + } + + ZEND_ASSERT(~T->end); + + for (j = T->start + 1; j < T->end; j++) { + if (op_array->opcodes[j].opcode != ZEND_THROW) { + op_var_info *opT = opTs[j]; + if (~opT->var) { + opT = opTs[j] = opT->next = zend_arena_alloc(&CG(arena), sizeof(op_var_info)); + opT->next = NULL; + } + opT->var = (uint32_t)(zend_intptr_t)ZEND_CALL_VAR_NUM(NULL, op_array->last_var + i);; + if (op_array->opcodes[T->end].opcode == ZEND_ROPE_END) { + opT->var |= ZEND_LIVE_ROPE; + } else if (op_array->opcodes[T->end].opcode == ZEND_END_SILENCE) { + opT->var |= ZEND_LIVE_SILENCE; + } + op_live_total++; + } + } + } while ((T = T->next)); + } + + if (!op_live_total) { + zend_arena_release(&CG(arena), checkpoint); + return NULL; + } + + info = emalloc(op_live_total * sizeof(uint32_t) + (op_array->last + 1) * sizeof(uint32_t)); + + for (i = 0; i < op_array->last; i++) { + op_var_info *opT = &opTsTop[i]; + info[i] = info_off; + if (!~opT->var) { + opT = NULL; + } + while (opT) { + info[info_off++] = opT->var; + opT = opT->next; + } + } + info[i] = info_off; + + zend_arena_release(&CG(arena), checkpoint); + + return info; +} + int print_class(zend_class_entry *class_entry) { printf("Class %s:\n", ZSTR_VAL(class_entry->name)); diff --git a/ext/opcache/Optimizer/block_pass.c b/ext/opcache/Optimizer/block_pass.c index ee9bda6094c..799fec42dc1 100644 --- a/ext/opcache/Optimizer/block_pass.c +++ b/ext/opcache/Optimizer/block_pass.c @@ -207,17 +207,13 @@ static int find_code_blocks(zend_op_array *op_array, zend_cfg *cfg, zend_optimiz for (i = 0; i< op_array->last_brk_cont; i++) { if (op_array->brk_cont_array[i].start >= 0 && (op_array->opcodes[op_array->brk_cont_array[i].brk].opcode == ZEND_FREE || - op_array->opcodes[op_array->brk_cont_array[i].brk].opcode == ZEND_FE_FREE || - op_array->opcodes[op_array->brk_cont_array[i].brk].opcode == ZEND_ROPE_END || - op_array->opcodes[op_array->brk_cont_array[i].brk].opcode == ZEND_END_SILENCE)) { + op_array->opcodes[op_array->brk_cont_array[i].brk].opcode == ZEND_FE_FREE)) { int parent = op_array->brk_cont_array[i].parent; while (parent >= 0 && op_array->brk_cont_array[parent].start < 0 && (op_array->opcodes[op_array->brk_cont_array[parent].brk].opcode != ZEND_FREE || - op_array->opcodes[op_array->brk_cont_array[parent].brk].opcode != ZEND_FE_FREE || - op_array->opcodes[op_array->brk_cont_array[i].brk].opcode != ZEND_ROPE_END || - op_array->opcodes[op_array->brk_cont_array[parent].brk].opcode != ZEND_END_SILENCE)) { + op_array->opcodes[op_array->brk_cont_array[parent].brk].opcode != ZEND_FE_FREE)) { parent = op_array->brk_cont_array[parent].parent; } op_array->brk_cont_array[i].parent = parent; @@ -232,9 +228,7 @@ static int find_code_blocks(zend_op_array *op_array, zend_cfg *cfg, zend_optimiz for (i = 0; i< op_array->last_brk_cont; i++) { if (op_array->brk_cont_array[i].start >= 0 && (op_array->opcodes[op_array->brk_cont_array[i].brk].opcode == ZEND_FREE || - op_array->opcodes[op_array->brk_cont_array[i].brk].opcode == ZEND_FE_FREE || - op_array->opcodes[op_array->brk_cont_array[i].brk].opcode == ZEND_ROPE_END || - op_array->opcodes[op_array->brk_cont_array[i].brk].opcode == ZEND_END_SILENCE)) { + op_array->opcodes[op_array->brk_cont_array[i].brk].opcode == ZEND_FE_FREE)) { if (i != j) { op_array->brk_cont_array[j] = op_array->brk_cont_array[i]; } diff --git a/ext/opcache/Optimizer/zend_optimizer.c b/ext/opcache/Optimizer/zend_optimizer.c index dc69d2511ec..44c400ca3fd 100644 --- a/ext/opcache/Optimizer/zend_optimizer.c +++ b/ext/opcache/Optimizer/zend_optimizer.c @@ -482,6 +482,8 @@ static void zend_accel_optimize(zend_op_array *op_array, { zend_op *opline, *end; + efree(op_array->T_liveliness); + /* Revert pass_two() */ opline = op_array->opcodes; end = opline + op_array->last; @@ -527,15 +529,39 @@ static void zend_accel_optimize(zend_op_array *op_array, /* Do actual optimizations */ zend_optimize(op_array, ctx); + opline = op_array->opcodes; + end = opline + op_array->last; + while (opline < end) { + if (opline->op1_type & (IS_VAR|IS_TMP_VAR)) { + opline->op1.var = EX_VAR_TO_NUM(opline->op1.var) - op_array->last_var; + } + if (opline->op2_type & (IS_VAR|IS_TMP_VAR)) { + opline->op2.var = EX_VAR_TO_NUM(opline->op2.var) - op_array->last_var; + } + if (opline->result_type & (IS_VAR|IS_TMP_VAR)) { + opline->result.var = EX_VAR_TO_NUM(opline->result.var) - op_array->last_var; + } + opline++; + } + + op_array->T_liveliness = generate_var_liveliness_info(op_array); + /* Redo pass_two() */ opline = op_array->opcodes; end = opline + op_array->last; while (opline < end) { if (opline->op1_type == IS_CONST) { ZEND_PASS_TWO_UPDATE_CONSTANT(op_array, opline->op1); + } else if (opline->op1_type & (IS_VAR|IS_TMP_VAR)) { + opline->op1.var = (uint32_t)(zend_intptr_t)ZEND_CALL_VAR_NUM(NULL, opline->op1.var + op_array->last_var); } if (opline->op2_type == IS_CONST) { ZEND_PASS_TWO_UPDATE_CONSTANT(op_array, opline->op2); + } else if (opline->op2_type & (IS_VAR|IS_TMP_VAR)) { + opline->op2.var = (uint32_t)(zend_intptr_t)ZEND_CALL_VAR_NUM(NULL, opline->op2.var + op_array->last_var); + } + if (opline->result_type & (IS_VAR|IS_TMP_VAR)) { + opline->result.var = (uint32_t)(zend_intptr_t)ZEND_CALL_VAR_NUM(NULL, opline->result.var + op_array->last_var); } switch (opline->opcode) { case ZEND_JMP: diff --git a/ext/opcache/zend_file_cache.c b/ext/opcache/zend_file_cache.c index 0781b91c9d3..084d9b9a9db 100644 --- a/ext/opcache/zend_file_cache.c +++ b/ext/opcache/zend_file_cache.c @@ -464,6 +464,7 @@ static void zend_file_cache_serialize_op_array(zend_op_array *op_arra SERIALIZE_STR(op_array->doc_comment); SERIALIZE_PTR(op_array->try_catch_array); SERIALIZE_PTR(op_array->prototype); + SERIALIZE_PTR(op_array->T_liveliness); } } @@ -987,6 +988,7 @@ static void zend_file_cache_unserialize_op_array(zend_op_array *op_arr UNSERIALIZE_STR(op_array->doc_comment); UNSERIALIZE_PTR(op_array->try_catch_array); UNSERIALIZE_PTR(op_array->prototype); + UNSERIALIZE_PTR(op_array->T_liveliness); } } diff --git a/ext/opcache/zend_persist.c b/ext/opcache/zend_persist.c index bbcb20713b0..309d44b80d6 100644 --- a/ext/opcache/zend_persist.c +++ b/ext/opcache/zend_persist.c @@ -618,6 +618,10 @@ static void zend_persist_op_array_ex(zend_op_array *op_array, zend_persistent_sc zend_accel_store(op_array->try_catch_array, sizeof(zend_try_catch_element) * op_array->last_try_catch); } + if (op_array->T_liveliness) { + zend_accel_store(op_array->T_liveliness, sizeof(uint32_t) * op_array->T_liveliness[op_array->last]); + } + if (op_array->vars) { if (already_stored) { persist_ptr = zend_shared_alloc_get_xlat_entry(op_array->vars); diff --git a/ext/opcache/zend_persist_calc.c b/ext/opcache/zend_persist_calc.c index d78cc592591..00f613273b4 100644 --- a/ext/opcache/zend_persist_calc.c +++ b/ext/opcache/zend_persist_calc.c @@ -241,6 +241,10 @@ static void zend_persist_op_array_calc_ex(zend_op_array *op_array) ADD_DUP_SIZE(op_array->try_catch_array, sizeof(zend_try_catch_element) * op_array->last_try_catch); } + if (op_array->T_liveliness) { + ADD_DUP_SIZE(op_array->T_liveliness, sizeof(uint32_t) * op_array->T_liveliness[op_array->last]); + } + if (op_array->vars) { int i; diff --git a/sapi/phpdbg/phpdbg_opcode.c b/sapi/phpdbg/phpdbg_opcode.c index 8adbbba36ff..ab5a1d9d500 100644 --- a/sapi/phpdbg/phpdbg_opcode.c +++ b/sapi/phpdbg/phpdbg_opcode.c @@ -24,6 +24,7 @@ #include "phpdbg_opcode.h" #include "phpdbg_utils.h" #include "ext/standard/php_string.h" +#include "zend_smart_str.h" ZEND_EXTERN_MODULE_GLOBALS(phpdbg); @@ -124,6 +125,44 @@ char *phpdbg_decode_opline(zend_op_array *ops, zend_op *op, HashTable *vars) /*{ break; } +#if 1 + if (ops->T_liveliness) { + uint32_t *var = ops->T_liveliness + ops->T_liveliness[op - ops->opcodes]; + uint32_t *end = ops->T_liveliness + ops->T_liveliness[op - ops->opcodes + 1]; + + if (var != end) { + smart_str str = {0}; + + smart_str_appends(&str, "; [@"); + smart_str_append_long(&str, EX_VAR_TO_NUM(((*var) & ~0x3)) - ops->last_var); + while (++var != end) { + smart_str_appends(&str, ", @"); + smart_str_append_long(&str, EX_VAR_TO_NUM(((*var) & ~0x3)) - ops->last_var); + } + smart_str_appendc(&str, ']'); + smart_str_0(&str); + + asprintf(&decode[0], + "%-20s %-20s %-20s%-20s", + decode[1] ? decode[1] : "", + decode[2] ? decode[2] : "", + decode[3] ? decode[3] : "", + ZSTR_VAL(str.s)); + + smart_str_free(&str); + + if (decode[1]) + free(decode[1]); + if (decode[2]) + free(decode[2]); + if (decode[3]) + free(decode[3]); + + return decode[0]; + } + } +#endif + asprintf(&decode[0], "%-20s %-20s %-20s", decode[1] ? decode[1] : "",