diff options
Diffstat (limited to 'Zend/zend_opcode.c')
-rw-r--r-- | Zend/zend_opcode.c | 360 |
1 files changed, 96 insertions, 264 deletions
diff --git a/Zend/zend_opcode.c b/Zend/zend_opcode.c index a971a5e900..91787aaad7 100644 --- a/Zend/zend_opcode.c +++ b/Zend/zend_opcode.c @@ -2,7 +2,7 @@ +----------------------------------------------------------------------+ | Zend Engine | +----------------------------------------------------------------------+ - | Copyright (c) 1998-2015 Zend Technologies Ltd. (http://www.zend.com) | + | Copyright (c) 1998-2016 Zend Technologies Ltd. (http://www.zend.com) | +----------------------------------------------------------------------+ | This source file is subject to version 2.00 of the Zend license, | | that is bundled with this package in the file LICENSE, and is | @@ -14,6 +14,7 @@ +----------------------------------------------------------------------+ | Authors: Andi Gutmans <andi@zend.com> | | Zeev Suraski <zeev@zend.com> | + | Dmitry Stogov <dmitry@zend.com> | +----------------------------------------------------------------------+ */ @@ -77,9 +78,9 @@ void init_op_array(zend_op_array *op_array, zend_uchar type, int initial_ops_siz op_array->scope = NULL; op_array->prototype = NULL; - op_array->brk_cont_array = NULL; + op_array->live_range = NULL; op_array->try_catch_array = NULL; - op_array->last_brk_cont = 0; + op_array->last_live_range = 0; op_array->static_variables = NULL; op_array->last_try_catch = 0; @@ -98,7 +99,9 @@ void init_op_array(zend_op_array *op_array, zend_uchar type, int initial_ops_siz memset(op_array->reserved, 0, ZEND_MAX_RESERVED_RESOURCES * sizeof(void*)); - zend_llist_apply_with_argument(&zend_extensions, (llist_apply_with_arg_func_t) zend_extension_op_array_ctor_handler, op_array); + if (zend_extension_flags & ZEND_EXTENSIONS_HAVE_OP_ARRAY_CTOR) { + zend_llist_apply_with_argument(&zend_extensions, (llist_apply_with_arg_func_t) zend_extension_op_array_ctor_handler, op_array); + } } ZEND_API void destroy_zend_function(zend_function *function) @@ -284,7 +287,19 @@ ZEND_API void destroy_zend_class(zval *zv) zend_hash_destroy(&ce->properties_info); zend_string_release(ce->name); zend_hash_destroy(&ce->function_table); - zend_hash_destroy(&ce->constants_table); + if (zend_hash_num_elements(&ce->constants_table)) { + zend_class_constant *c; + + ZEND_HASH_FOREACH_PTR(&ce->constants_table, c) { + if (c->ce == ce) { + zval_ptr_dtor(&c->value); + if (c->doc_comment) { + zend_string_release(c->doc_comment); + } + } + } ZEND_HASH_FOREACH_END(); + zend_hash_destroy(&ce->constants_table); + } if (ce->num_interfaces > 0 && ce->interfaces) { efree(ce->interfaces); } @@ -319,7 +334,17 @@ ZEND_API void destroy_zend_class(zval *zv) zend_hash_destroy(&ce->properties_info); zend_string_release(ce->name); zend_hash_destroy(&ce->function_table); - zend_hash_destroy(&ce->constants_table); + if (zend_hash_num_elements(&ce->constants_table)) { + zend_class_constant *c; + + ZEND_HASH_FOREACH_PTR(&ce->constants_table, c) { + zval_internal_ptr_dtor(&c->value); + if (c->doc_comment && c->ce == ce) { + zend_string_release(c->doc_comment); + } + } ZEND_HASH_FOREACH_END(); + zend_hash_destroy(&ce->constants_table); + } if (ce->num_interfaces > 0) { free(ce->interfaces); } @@ -343,16 +368,17 @@ ZEND_API void destroy_op_array(zend_op_array *op_array) if (op_array->static_variables && !(GC_FLAGS(op_array->static_variables) & IS_ARRAY_IMMUTABLE)) { - if (--GC_REFCOUNT(op_array->static_variables) == 0) { + if (--GC_REFCOUNT(op_array->static_variables) == 0) { zend_array_destroy(op_array->static_variables); } } if (op_array->run_time_cache && !op_array->function_name) { efree(op_array->run_time_cache); + op_array->run_time_cache = NULL; } - if (!op_array->refcount || --(*op_array->refcount)>0) { + if (!op_array->refcount || --(*op_array->refcount) > 0) { return; } @@ -383,19 +409,20 @@ ZEND_API void destroy_op_array(zend_op_array *op_array) if (op_array->doc_comment) { zend_string_release(op_array->doc_comment); } - if (op_array->brk_cont_array) { - efree(op_array->brk_cont_array); + if (op_array->live_range) { + efree(op_array->live_range); } if (op_array->try_catch_array) { efree(op_array->try_catch_array); } - 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); + if (zend_extension_flags & ZEND_EXTENSIONS_HAVE_OP_ARRAY_DTOR) { + 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); + } } if (op_array->arg_info) { - int32_t num_args = op_array->num_args; + uint32_t num_args = op_array->num_args; zend_arg_info *arg_info = op_array->arg_info; - int32_t i; if (op_array->fn_flags & ZEND_ACC_HAS_RETURN_TYPE) { arg_info--; @@ -445,11 +472,11 @@ int get_next_op_number(zend_op_array *op_array) return op_array->last; } -zend_brk_cont_element *get_next_brk_cont_element(zend_op_array *op_array) +zend_brk_cont_element *get_next_brk_cont_element(void) { - op_array->last_brk_cont++; - op_array->brk_cont_array = erealloc(op_array->brk_cont_array, sizeof(zend_brk_cont_element)*op_array->last_brk_cont); - return &op_array->brk_cont_array[op_array->last_brk_cont-1]; + CG(context).last_brk_cont++; + CG(context).brk_cont_array = erealloc(CG(context).brk_cont_array, sizeof(zend_brk_cont_element) * CG(context).last_brk_cont); + return &CG(context).brk_cont_array[CG(context).last_brk_cont-1]; } static void zend_update_extended_info(zend_op_array *op_array) @@ -507,172 +534,12 @@ static void zend_check_finally_breakout(zend_op_array *op_array, uint32_t op_num } } -static void zend_adjust_fast_call(zend_op_array *op_array, uint32_t fast_call, uint32_t start, uint32_t end) -{ - int i; - uint32_t op_num = 0; - - for (i = 0; i < op_array->last_try_catch; i++) { - if (op_array->try_catch_array[i].finally_op > start - && op_array->try_catch_array[i].finally_end < end) { - op_num = op_array->try_catch_array[i].finally_op; - start = op_array->try_catch_array[i].finally_end; - } - } - - if (op_num) { - /* Must be ZEND_FAST_CALL */ - ZEND_ASSERT(op_array->opcodes[op_num - 2].opcode == ZEND_FAST_CALL); - op_array->opcodes[op_num - 2].extended_value = ZEND_FAST_CALL_FROM_FINALLY; - op_array->opcodes[op_num - 2].op2.opline_num = fast_call; - } -} - -static void zend_resolve_fast_call(zend_op_array *op_array, uint32_t fast_call, uint32_t op_num) -{ - int i; - uint32_t finally_op_num = 0; - - for (i = 0; i < op_array->last_try_catch; i++) { - if (op_num >= op_array->try_catch_array[i].finally_op - && op_num < op_array->try_catch_array[i].finally_end) { - finally_op_num = op_array->try_catch_array[i].finally_op; - } - } - - if (finally_op_num) { - /* Must be ZEND_FAST_CALL */ - ZEND_ASSERT(op_array->opcodes[finally_op_num - 2].opcode == ZEND_FAST_CALL); - if (op_array->opcodes[fast_call].extended_value == 0) { - op_array->opcodes[fast_call].extended_value = ZEND_FAST_CALL_FROM_FINALLY; - op_array->opcodes[fast_call].op2.opline_num = finally_op_num - 2; - } - } -} - -static void zend_resolve_finally_call(zend_op_array *op_array, uint32_t op_num, uint32_t dst_num) -{ - uint32_t start_op; - zend_op *opline; - uint32_t i = op_array->last_try_catch; - - if (dst_num != (uint32_t)-1) { - zend_check_finally_breakout(op_array, op_num, dst_num); - } - - /* the backward order is mater */ - while (i > 0) { - i--; - if (op_array->try_catch_array[i].finally_op && - op_num >= op_array->try_catch_array[i].try_op && - op_num < op_array->try_catch_array[i].finally_op - 1 && - (dst_num < op_array->try_catch_array[i].try_op || - dst_num > op_array->try_catch_array[i].finally_end)) { - /* we have a jump out of try block that needs executing finally */ - uint32_t fast_call_var; - - /* Must be ZEND_FAST_RET */ - ZEND_ASSERT(op_array->opcodes[op_array->try_catch_array[i].finally_end].opcode == ZEND_FAST_RET); - 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); - - opline = get_next_op(op_array); - opline->opcode = ZEND_FAST_CALL; - opline->result_type = IS_TMP_VAR; - opline->result.var = fast_call_var; - SET_UNUSED(opline->op1); - SET_UNUSED(opline->op2); - zend_adjust_fast_call(op_array, start_op, - op_array->try_catch_array[i].finally_op, - op_array->try_catch_array[i].finally_end); - if (op_array->try_catch_array[i].catch_op) { - opline->extended_value = ZEND_FAST_CALL_FROM_CATCH; - opline->op2.opline_num = op_array->try_catch_array[i].catch_op; - opline->op1.opline_num = get_next_op_number(op_array); - /* generate a FAST_CALL to hole CALL_FROM_FINALLY */ - opline = get_next_op(op_array); - opline->opcode = ZEND_FAST_CALL; - opline->result_type = IS_TMP_VAR; - opline->result.var = fast_call_var; - SET_UNUSED(opline->op1); - SET_UNUSED(opline->op2); - zend_resolve_fast_call(op_array, start_op + 1, op_array->try_catch_array[i].finally_op - 2); - } else { - zend_resolve_fast_call(op_array, start_op, op_array->try_catch_array[i].finally_op - 2); - } - opline->op1.opline_num = op_array->try_catch_array[i].finally_op; - - /* generate a sequence of FAST_CALL to upward finally block */ - while (i > 0) { - i--; - if (op_array->try_catch_array[i].finally_op && - op_num >= op_array->try_catch_array[i].try_op && - op_num < op_array->try_catch_array[i].finally_op - 1 && - (dst_num < op_array->try_catch_array[i].try_op || - dst_num > op_array->try_catch_array[i].finally_end)) { - - opline = get_next_op(op_array); - opline->opcode = ZEND_FAST_CALL; - opline->result_type = IS_TMP_VAR; - opline->result.var = fast_call_var; - SET_UNUSED(opline->op1); - SET_UNUSED(opline->op2); - opline->op1.opline_num = op_array->try_catch_array[i].finally_op; - } - } - - /* Finish the sequence with original opcode */ - opline = get_next_op(op_array); - *opline = op_array->opcodes[op_num]; - - /* Replace original opcode with jump to this sequence */ - opline = op_array->opcodes + op_num; - opline->opcode = ZEND_JMP; - SET_UNUSED(opline->op1); - SET_UNUSED(opline->op2); - opline->op1.opline_num = start_op; - - break; - } - } -} - -static void zend_resolve_finally_ret(zend_op_array *op_array, uint32_t op_num) -{ - int i; - uint32_t catch_op_num = 0, finally_op_num = 0; - - for (i = 0; i < op_array->last_try_catch; i++) { - if (op_array->try_catch_array[i].try_op > op_num) { - break; - } - if (op_num < op_array->try_catch_array[i].finally_op) { - finally_op_num = op_array->try_catch_array[i].finally_op; - } - if (op_num < op_array->try_catch_array[i].catch_op) { - catch_op_num = op_array->try_catch_array[i].catch_op; - } - } - - if (finally_op_num && (!catch_op_num || catch_op_num >= finally_op_num)) { - /* in case of unhandled exception return to upward finally block */ - op_array->opcodes[op_num].extended_value = ZEND_FAST_RET_TO_FINALLY; - op_array->opcodes[op_num].op2.opline_num = finally_op_num; - } else if (catch_op_num) { - /* in case of unhandled exception return to upward catch block */ - op_array->opcodes[op_num].extended_value = ZEND_FAST_RET_TO_CATCH; - op_array->opcodes[op_num].op2.opline_num = catch_op_num; - } -} - static uint32_t zend_get_brk_cont_target(const zend_op_array *op_array, const zend_op *opline) { int nest_levels = opline->op2.num; int array_offset = opline->op1.num; zend_brk_cont_element *jmp_to; do { - jmp_to = &op_array->brk_cont_array[array_offset]; + jmp_to = &CG(context).brk_cont_array[array_offset]; if (nest_levels > 1) { array_offset = jmp_to->parent; } @@ -681,47 +548,6 @@ static uint32_t zend_get_brk_cont_target(const zend_op_array *op_array, const ze return opline->opcode == ZEND_BRK ? jmp_to->brk : jmp_to->cont; } -static void zend_resolve_finally_calls(zend_op_array *op_array) -{ - uint32_t i, j; - zend_op *opline; - - for (i = 0, j = op_array->last; i < j; i++) { - opline = op_array->opcodes + i; - switch (opline->opcode) { - case ZEND_RETURN: - case ZEND_RETURN_BY_REF: - case ZEND_GENERATOR_RETURN: - zend_resolve_finally_call(op_array, i, (uint32_t)-1); - break; - case ZEND_BRK: - case ZEND_CONT: - zend_resolve_finally_call(op_array, i, zend_get_brk_cont_target(op_array, opline)); - break; - case ZEND_GOTO: - if (Z_TYPE_P(CT_CONSTANT_EX(op_array, opline->op2.constant)) != IS_LONG) { - uint32_t num = opline->op2.constant; - - ZEND_PASS_TWO_UPDATE_CONSTANT(op_array, opline->op2); - zend_resolve_goto_label(op_array, opline, 1); - opline->op2.constant = num; - } - /* break omitted intentionally */ - case ZEND_JMP: - zend_resolve_finally_call(op_array, i, opline->op1.opline_num); - break; - case ZEND_FAST_CALL: - zend_resolve_fast_call(op_array, i, i); - break; - case ZEND_FAST_RET: - zend_resolve_finally_ret(op_array, i); - break; - default: - break; - } - } -} - ZEND_API int pass_two(zend_op_array *op_array) { zend_op *opline, *end; @@ -729,14 +555,13 @@ ZEND_API int pass_two(zend_op_array *op_array) if (!ZEND_USER_CODE(op_array->type)) { return 0; } - if (op_array->fn_flags & ZEND_ACC_HAS_FINALLY_BLOCK) { - zend_resolve_finally_calls(op_array); - } if (CG(compiler_options) & ZEND_COMPILE_EXTENDED_INFO) { zend_update_extended_info(op_array); } if (CG(compiler_options) & ZEND_COMPILE_HANDLE_OP_ARRAY) { - zend_llist_apply_with_argument(&zend_extensions, (llist_apply_with_arg_func_t) zend_extension_op_array_handler, op_array); + if (zend_extension_flags & ZEND_EXTENSIONS_HAVE_OP_ARRAY_HANDLER) { + zend_llist_apply_with_argument(&zend_extensions, (llist_apply_with_arg_func_t) zend_extension_op_array_handler, op_array); + } } if (CG(context).vars_size != op_array->last_var) { @@ -754,31 +579,19 @@ ZEND_API int pass_two(zend_op_array *op_array) 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, op_array->last_var + opline->op1.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, op_array->last_var + opline->op2.var); - } - if (opline->result_type & (IS_VAR|IS_TMP_VAR)) { - opline->result.var = (uint32_t)(zend_intptr_t)ZEND_CALL_VAR_NUM(NULL, op_array->last_var + opline->result.var); - } switch (opline->opcode) { - case ZEND_DECLARE_ANON_INHERITED_CLASS: + case ZEND_FAST_CALL: + opline->op1.opline_num = op_array->try_catch_array[opline->op1.num].finally_op; ZEND_PASS_TWO_UPDATE_JMP_TARGET(op_array, opline, opline->op1); - /* break omitted intentionally */ - case ZEND_DECLARE_INHERITED_CLASS: - case ZEND_DECLARE_INHERITED_CLASS_DELAYED: - opline->extended_value = (uint32_t)(zend_intptr_t)ZEND_CALL_VAR_NUM(NULL, op_array->last_var + opline->extended_value); break; case ZEND_BRK: case ZEND_CONT: { uint32_t jmp_target = zend_get_brk_cont_target(op_array, opline); + + if (op_array->fn_flags & ZEND_ACC_HAS_FINALLY_BLOCK) { + zend_check_finally_breakout(op_array, opline - op_array->opcodes, jmp_target); + } opline->opcode = ZEND_JMP; opline->op1.opline_num = jmp_target; opline->op2.num = 0; @@ -786,13 +599,12 @@ ZEND_API int pass_two(zend_op_array *op_array) } break; case ZEND_GOTO: - if (Z_TYPE_P(RT_CONSTANT(op_array, opline->op2)) != IS_LONG) { - zend_resolve_goto_label(op_array, opline, 1); + zend_resolve_goto_label(op_array, opline); + if (op_array->fn_flags & ZEND_ACC_HAS_FINALLY_BLOCK) { + zend_check_finally_breakout(op_array, opline - op_array->opcodes, opline->op1.opline_num); } /* break omitted intentionally */ case ZEND_JMP: - case ZEND_FAST_CALL: - case ZEND_DECLARE_ANON_CLASS: ZEND_PASS_TWO_UPDATE_JMP_TARGET(op_array, opline, opline->op1); break; case ZEND_JMPZNZ: @@ -805,21 +617,31 @@ ZEND_API int pass_two(zend_op_array *op_array) case ZEND_JMPNZ_EX: case ZEND_JMP_SET: case ZEND_COALESCE: - case ZEND_NEW: case ZEND_FE_RESET_R: case ZEND_FE_RESET_RW: + ZEND_PASS_TWO_UPDATE_JMP_TARGET(op_array, opline, opline->op2); + break; case ZEND_ASSERT_CHECK: + { + /* If result of assert is unused, result of check is unused as well */ + zend_op *call = &op_array->opcodes[opline->op2.opline_num - 1]; + if (call->opcode == ZEND_EXT_FCALL_END) { + call--; + } + if (call->result_type == IS_UNUSED) { + opline->result_type = IS_UNUSED; + } ZEND_PASS_TWO_UPDATE_JMP_TARGET(op_array, opline, opline->op2); break; + } + case ZEND_DECLARE_ANON_CLASS: + case ZEND_DECLARE_ANON_INHERITED_CLASS: + case ZEND_CATCH: case ZEND_FE_FETCH_R: case ZEND_FE_FETCH_RW: + /* absolute index to relative offset */ opline->extended_value = ZEND_OPLINE_NUM_TO_OFFSET(op_array, opline, opline->extended_value); break; - case ZEND_VERIFY_RETURN_TYPE: - if (op_array->fn_flags & ZEND_ACC_GENERATOR) { - MAKE_NOP(opline); - } - break; case ZEND_RETURN: case ZEND_RETURN_BY_REF: if (op_array->fn_flags & ZEND_ACC_GENERATOR) { @@ -827,24 +649,34 @@ ZEND_API int pass_two(zend_op_array *op_array) } break; } + 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, op_array->last_var + opline->op1.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, op_array->last_var + opline->op2.var); + } + if (opline->result_type & (IS_VAR|IS_TMP_VAR)) { + opline->result.var = (uint32_t)(zend_intptr_t)ZEND_CALL_VAR_NUM(NULL, op_array->last_var + opline->result.var); + } ZEND_VM_SET_OPCODE_HANDLER(opline); opline++; } - op_array->fn_flags |= ZEND_ACC_DONE_PASS_TWO; - return 0; -} + if (op_array->live_range) { + int i; -int pass_two_wrapper(zval *el) -{ - return pass_two((zend_op_array *) Z_PTR_P(el)); -} + for (i = 0; i < op_array->last_live_range; i++) { + op_array->live_range[i].var = + (uint32_t)(zend_intptr_t)ZEND_CALL_VAR_NUM(NULL, op_array->last_var + (op_array->live_range[i].var / sizeof(zval))) | + (op_array->live_range[i].var & ZEND_LIVE_MASK); + } + } -int print_class(zend_class_entry *class_entry) -{ - printf("Class %s:\n", ZSTR_VAL(class_entry->name)); - zend_hash_apply(&class_entry->function_table, pass_two_wrapper); - printf("End of class %s.\n\n", ZSTR_VAL(class_entry->name)); + op_array->fn_flags |= ZEND_ACC_DONE_PASS_TWO; return 0; } |