From: Dmitry Stogov Date: Thu, 11 Apr 2019 21:49:45 +0000 (+0300) Subject: Replace "ZEND_CALL_CTOR" hack by additional live-range X-Git-Tag: php-7.4.0alpha1~536 X-Git-Url: https://granicus.if.org/sourcecode?a=commitdiff_plain;h=88a2268d6b9ff152399a8761dc826ce414c0b985;p=php Replace "ZEND_CALL_CTOR" hack by additional live-range --- diff --git a/Zend/tests/bug29368_3.phpt b/Zend/tests/bug29368_3.phpt new file mode 100644 index 0000000000..fafcc2a0ef --- /dev/null +++ b/Zend/tests/bug29368_3.phpt @@ -0,0 +1,33 @@ +--TEST-- +Bug #29368.3 (The destructor is called when an exception is thrown from the constructor). +--FILE-- + +--EXPECT-- +Foo::__construct +Bar::__construct +Foo::__destruct +Caught exception! diff --git a/Zend/zend_compile.c b/Zend/zend_compile.c index ccdbd8a1b7..1294c1f488 100644 --- a/Zend/zend_compile.c +++ b/Zend/zend_compile.c @@ -3066,7 +3066,6 @@ void zend_compile_call_common(znode *result, zend_ast *args_ast, zend_function * zend_op *opline; uint32_t opnum_init = get_next_op_number() - 1; uint32_t arg_count; - uint32_t call_flags; arg_count = zend_compile_args(args_ast, fbc); @@ -3079,10 +3078,7 @@ void zend_compile_call_common(znode *result, zend_ast *args_ast, zend_function * opline->op1.num = zend_vm_calc_used_stack(arg_count, fbc); } - call_flags = (opline->opcode == ZEND_NEW ? ZEND_CALL_CTOR : 0); opline = zend_emit_op(result, zend_get_call_op(opline, fbc), NULL, NULL); - opline->op1.num = call_flags; - zend_do_extended_fcall_end(); } /* }}} */ diff --git a/Zend/zend_compile.h b/Zend/zend_compile.h index 82d70fd4b0..523d04e794 100644 --- a/Zend/zend_compile.h +++ b/Zend/zend_compile.h @@ -172,7 +172,8 @@ typedef struct _zend_try_catch_element { #define ZEND_LIVE_LOOP 1 #define ZEND_LIVE_SILENCE 2 #define ZEND_LIVE_ROPE 3 -#define ZEND_LIVE_MASK 3 +#define ZEND_LIVE_NEW 4 +#define ZEND_LIVE_MASK 7 typedef struct _zend_live_range { uint32_t var; /* low bits are used for variable type (ZEND_LIVE_* macros) */ @@ -506,7 +507,6 @@ struct _zend_execute_data { #define ZEND_CALL_NESTED (0 << 1) #define ZEND_CALL_TOP (1 << 1) #define ZEND_CALL_FREE_EXTRA_ARGS (1 << 2) -#define ZEND_CALL_CTOR (1 << 3) #define ZEND_CALL_HAS_SYMBOL_TABLE (1 << 4) #define ZEND_CALL_CLOSURE (1 << 5) #define ZEND_CALL_RELEASE_THIS (1 << 6) diff --git a/Zend/zend_execute.c b/Zend/zend_execute.c index bb3ca87b04..6d13457a43 100644 --- a/Zend/zend_execute.c +++ b/Zend/zend_execute.c @@ -3675,12 +3675,6 @@ static void cleanup_unfinished_calls(zend_execute_data *execute_data, uint32_t o zend_vm_stack_free_args(EX(call)); if (ZEND_CALL_INFO(call) & ZEND_CALL_RELEASE_THIS) { - if (ZEND_CALL_INFO(call) & ZEND_CALL_CTOR) { - GC_DELREF(Z_OBJ(call->This)); - if (GC_REFCOUNT(Z_OBJ(call->This)) == 1) { - zend_object_store_ctor_failed(Z_OBJ(call->This)); - } - } OBJ_RELEASE(Z_OBJ(call->This)); } if (call->func->common.fn_flags & ZEND_ACC_CLOSURE) { @@ -3729,6 +3723,12 @@ static void cleanup_live_vars(zend_execute_data *execute_data, uint32_t op_num, if (kind == ZEND_LIVE_TMPVAR) { zval_ptr_dtor_nogc(var); + } else if (kind == ZEND_LIVE_NEW) { + zend_object *obj; + ZEND_ASSERT(Z_TYPE_P(var) == IS_OBJECT); + obj = Z_OBJ_P(var); + zend_object_store_ctor_failed(obj); + OBJ_RELEASE(obj); } else if (kind == ZEND_LIVE_LOOP) { if (Z_TYPE_P(var) != IS_ARRAY && Z_FE_ITER_P(var) != (uint32_t)-1) { zend_hash_iterator_del(Z_FE_ITER_P(var)); diff --git a/Zend/zend_opcode.c b/Zend/zend_opcode.c index 5b1914c99d..a44a7c562d 100644 --- a/Zend/zend_opcode.c +++ b/Zend/zend_opcode.c @@ -603,10 +603,15 @@ static void emit_live_range( start++; break; /* Objects created via ZEND_NEW are only fully initialized - * after the DO_FCALL (constructor call). */ + * after the DO_FCALL (constructor call). + * We are creating two live-ranges: ZEND_LINE_NEW for uninitialized + * part, and ZEND_LIVE_TMPVAR for initialized. + */ case ZEND_NEW: { int level = 0; + uint32_t orig_start = start; + while (def_opline + 1 < use_opline) { def_opline++; start++; @@ -635,6 +640,7 @@ static void emit_live_range( } } } + emit_live_range_raw(op_array, var_num, ZEND_LIVE_NEW, orig_start + 1, start + 1); if (start + 1 == end) { /* Trivial live-range, no need to store it. */ return; diff --git a/Zend/zend_vm_def.h b/Zend/zend_vm_def.h index d71694223f..e975a92014 100644 --- a/Zend/zend_vm_def.h +++ b/Zend/zend_vm_def.h @@ -2740,16 +2740,7 @@ ZEND_VM_HOT_HELPER(zend_leave_helper, ANY, ANY) EG(current_execute_data) = EX(prev_execute_data); if (UNEXPECTED(call_info & ZEND_CALL_RELEASE_THIS)) { - zend_object *object = Z_OBJ(execute_data->This); -#if 0 - if (UNEXPECTED(EG(exception) != NULL) && (EX(opline)->op1.num & ZEND_CALL_CTOR)) { -#else - if (UNEXPECTED(EG(exception) != NULL) && (call_info & ZEND_CALL_CTOR)) { -#endif - GC_DELREF(object); - zend_object_store_ctor_failed(object); - } - OBJ_RELEASE(object); + OBJ_RELEASE(Z_OBJ(execute_data->This)); } else if (UNEXPECTED(call_info & ZEND_CALL_CLOSURE)) { OBJ_RELEASE(ZEND_CLOSURE_OBJECT(EX(func))); } @@ -2771,16 +2762,7 @@ ZEND_VM_HOT_HELPER(zend_leave_helper, ANY, ANY) } EG(current_execute_data) = EX(prev_execute_data); if (UNEXPECTED(call_info & ZEND_CALL_RELEASE_THIS)) { - zend_object *object = Z_OBJ(execute_data->This); -#if 0 - if (UNEXPECTED(EG(exception) != NULL) && (EX(opline)->op1.num & ZEND_CALL_CTOR)) { -#else - if (UNEXPECTED(EG(exception) != NULL) && (call_info & ZEND_CALL_CTOR)) { -#endif - GC_DELREF(object); - zend_object_store_ctor_failed(object); - } - OBJ_RELEASE(object); + OBJ_RELEASE(Z_OBJ(execute_data->This)); } else if (UNEXPECTED(call_info & ZEND_CALL_CLOSURE)) { OBJ_RELEASE(ZEND_CLOSURE_OBJECT(EX(func))); } @@ -3985,7 +3967,6 @@ ZEND_VM_HOT_HANDLER(60, ZEND_DO_FCALL, ANY, ANY, SPEC(RETVAL)) USE_OPLINE zend_execute_data *call = EX(call); zend_function *fbc = call->func; - zend_object *object; zval *ret; SAVE_OPLINE(); @@ -4079,16 +4060,7 @@ ZEND_VM_HOT_HANDLER(60, ZEND_DO_FCALL, ANY, ANY, SPEC(RETVAL)) ZEND_VM_C_LABEL(fcall_end): if (UNEXPECTED(ZEND_CALL_INFO(call) & ZEND_CALL_RELEASE_THIS)) { - object = Z_OBJ(call->This); -#if 0 - if (UNEXPECTED(EG(exception) != NULL) && (opline->op1.num & ZEND_CALL_CTOR)) { -#else - if (UNEXPECTED(EG(exception) != NULL) && (ZEND_CALL_INFO(call) & ZEND_CALL_CTOR)) { -#endif - GC_DELREF(object); - zend_object_store_ctor_failed(object); - } - OBJ_RELEASE(object); + OBJ_RELEASE(Z_OBJ(call->This)); } zend_vm_stack_free_call_frame(call); @@ -5322,7 +5294,7 @@ ZEND_VM_HANDLER(68, ZEND_NEW, UNUSED|CLASS_FETCH|CONST|VAR, UNUSED|CACHE_SLOT, N } /* We are not handling overloaded classes right now */ call = zend_vm_stack_push_call_frame( - ZEND_CALL_FUNCTION | ZEND_CALL_RELEASE_THIS | ZEND_CALL_CTOR, + ZEND_CALL_FUNCTION | ZEND_CALL_RELEASE_THIS, constructor, opline->extended_value, ce, diff --git a/Zend/zend_vm_execute.h b/Zend/zend_vm_execute.h index 7977a2a4a0..ad4f978ad1 100644 --- a/Zend/zend_vm_execute.h +++ b/Zend/zend_vm_execute.h @@ -796,16 +796,7 @@ static ZEND_OPCODE_HANDLER_RET ZEND_FASTCALL zend_leave_helper_SPEC(ZEND_OPCODE_ EG(current_execute_data) = EX(prev_execute_data); if (UNEXPECTED(call_info & ZEND_CALL_RELEASE_THIS)) { - zend_object *object = Z_OBJ(execute_data->This); -#if 0 - if (UNEXPECTED(EG(exception) != NULL) && (EX(opline)->op1.num & ZEND_CALL_CTOR)) { -#else - if (UNEXPECTED(EG(exception) != NULL) && (call_info & ZEND_CALL_CTOR)) { -#endif - GC_DELREF(object); - zend_object_store_ctor_failed(object); - } - OBJ_RELEASE(object); + OBJ_RELEASE(Z_OBJ(execute_data->This)); } else if (UNEXPECTED(call_info & ZEND_CALL_CLOSURE)) { OBJ_RELEASE(ZEND_CLOSURE_OBJECT(EX(func))); } @@ -827,16 +818,7 @@ static ZEND_OPCODE_HANDLER_RET ZEND_FASTCALL zend_leave_helper_SPEC(ZEND_OPCODE_ } EG(current_execute_data) = EX(prev_execute_data); if (UNEXPECTED(call_info & ZEND_CALL_RELEASE_THIS)) { - zend_object *object = Z_OBJ(execute_data->This); -#if 0 - if (UNEXPECTED(EG(exception) != NULL) && (EX(opline)->op1.num & ZEND_CALL_CTOR)) { -#else - if (UNEXPECTED(EG(exception) != NULL) && (call_info & ZEND_CALL_CTOR)) { -#endif - GC_DELREF(object); - zend_object_store_ctor_failed(object); - } - OBJ_RELEASE(object); + OBJ_RELEASE(Z_OBJ(execute_data->This)); } else if (UNEXPECTED(call_info & ZEND_CALL_CLOSURE)) { OBJ_RELEASE(ZEND_CLOSURE_OBJECT(EX(func))); } @@ -1207,7 +1189,6 @@ static ZEND_VM_HOT ZEND_OPCODE_HANDLER_RET ZEND_FASTCALL ZEND_DO_FCALL_SPEC_RETV USE_OPLINE zend_execute_data *call = EX(call); zend_function *fbc = call->func; - zend_object *object; zval *ret; SAVE_OPLINE(); @@ -1301,16 +1282,7 @@ static ZEND_VM_HOT ZEND_OPCODE_HANDLER_RET ZEND_FASTCALL ZEND_DO_FCALL_SPEC_RETV fcall_end: if (UNEXPECTED(ZEND_CALL_INFO(call) & ZEND_CALL_RELEASE_THIS)) { - object = Z_OBJ(call->This); -#if 0 - if (UNEXPECTED(EG(exception) != NULL) && (opline->op1.num & ZEND_CALL_CTOR)) { -#else - if (UNEXPECTED(EG(exception) != NULL) && (ZEND_CALL_INFO(call) & ZEND_CALL_CTOR)) { -#endif - GC_DELREF(object); - zend_object_store_ctor_failed(object); - } - OBJ_RELEASE(object); + OBJ_RELEASE(Z_OBJ(call->This)); } zend_vm_stack_free_call_frame(call); @@ -1328,7 +1300,6 @@ static ZEND_VM_HOT ZEND_OPCODE_HANDLER_RET ZEND_FASTCALL ZEND_DO_FCALL_SPEC_RETV USE_OPLINE zend_execute_data *call = EX(call); zend_function *fbc = call->func; - zend_object *object; zval *ret; SAVE_OPLINE(); @@ -1422,16 +1393,7 @@ static ZEND_VM_HOT ZEND_OPCODE_HANDLER_RET ZEND_FASTCALL ZEND_DO_FCALL_SPEC_RETV fcall_end: if (UNEXPECTED(ZEND_CALL_INFO(call) & ZEND_CALL_RELEASE_THIS)) { - object = Z_OBJ(call->This); -#if 0 - if (UNEXPECTED(EG(exception) != NULL) && (opline->op1.num & ZEND_CALL_CTOR)) { -#else - if (UNEXPECTED(EG(exception) != NULL) && (ZEND_CALL_INFO(call) & ZEND_CALL_CTOR)) { -#endif - GC_DELREF(object); - zend_object_store_ctor_failed(object); - } - OBJ_RELEASE(object); + OBJ_RELEASE(Z_OBJ(call->This)); } zend_vm_stack_free_call_frame(call); @@ -9122,7 +9084,7 @@ static ZEND_OPCODE_HANDLER_RET ZEND_FASTCALL ZEND_NEW_SPEC_CONST_UNUSED_HANDLER( } /* We are not handling overloaded classes right now */ call = zend_vm_stack_push_call_frame( - ZEND_CALL_FUNCTION | ZEND_CALL_RELEASE_THIS | ZEND_CALL_CTOR, + ZEND_CALL_FUNCTION | ZEND_CALL_RELEASE_THIS, constructor, opline->extended_value, ce, @@ -29170,7 +29132,7 @@ static ZEND_OPCODE_HANDLER_RET ZEND_FASTCALL ZEND_NEW_SPEC_VAR_UNUSED_HANDLER(ZE } /* We are not handling overloaded classes right now */ call = zend_vm_stack_push_call_frame( - ZEND_CALL_FUNCTION | ZEND_CALL_RELEASE_THIS | ZEND_CALL_CTOR, + ZEND_CALL_FUNCTION | ZEND_CALL_RELEASE_THIS, constructor, opline->extended_value, ce, @@ -36681,7 +36643,7 @@ static ZEND_OPCODE_HANDLER_RET ZEND_FASTCALL ZEND_NEW_SPEC_UNUSED_UNUSED_HANDLER } /* We are not handling overloaded classes right now */ call = zend_vm_stack_push_call_frame( - ZEND_CALL_FUNCTION | ZEND_CALL_RELEASE_THIS | ZEND_CALL_CTOR, + ZEND_CALL_FUNCTION | ZEND_CALL_RELEASE_THIS, constructor, opline->extended_value, ce, @@ -59189,16 +59151,7 @@ zend_leave_helper_SPEC_LABEL: EG(current_execute_data) = EX(prev_execute_data); if (UNEXPECTED(call_info & ZEND_CALL_RELEASE_THIS)) { - zend_object *object = Z_OBJ(execute_data->This); -#if 0 - if (UNEXPECTED(EG(exception) != NULL) && (EX(opline)->op1.num & ZEND_CALL_CTOR)) { -#else - if (UNEXPECTED(EG(exception) != NULL) && (call_info & ZEND_CALL_CTOR)) { -#endif - GC_DELREF(object); - zend_object_store_ctor_failed(object); - } - OBJ_RELEASE(object); + OBJ_RELEASE(Z_OBJ(execute_data->This)); } else if (UNEXPECTED(call_info & ZEND_CALL_CLOSURE)) { OBJ_RELEASE(ZEND_CLOSURE_OBJECT(EX(func))); } @@ -59220,16 +59173,7 @@ zend_leave_helper_SPEC_LABEL: } EG(current_execute_data) = EX(prev_execute_data); if (UNEXPECTED(call_info & ZEND_CALL_RELEASE_THIS)) { - zend_object *object = Z_OBJ(execute_data->This); -#if 0 - if (UNEXPECTED(EG(exception) != NULL) && (EX(opline)->op1.num & ZEND_CALL_CTOR)) { -#else - if (UNEXPECTED(EG(exception) != NULL) && (call_info & ZEND_CALL_CTOR)) { -#endif - GC_DELREF(object); - zend_object_store_ctor_failed(object); - } - OBJ_RELEASE(object); + OBJ_RELEASE(Z_OBJ(execute_data->This)); } else if (UNEXPECTED(call_info & ZEND_CALL_CLOSURE)) { OBJ_RELEASE(ZEND_CLOSURE_OBJECT(EX(func))); } diff --git a/ext/opcache/Optimizer/zend_dump.c b/ext/opcache/Optimizer/zend_dump.c index 3bc0650ab4..01c907cfea 100644 --- a/ext/opcache/Optimizer/zend_dump.c +++ b/ext/opcache/Optimizer/zend_dump.c @@ -1027,6 +1027,9 @@ void zend_dump_op_array(const zend_op_array *op_array, uint32_t dump_flags, cons case ZEND_LIVE_ROPE: fprintf(stderr, "(rope)\n"); break; + case ZEND_LIVE_NEW: + fprintf(stderr, "(new)\n"); + break; } } } @@ -1083,6 +1086,9 @@ void zend_dump_op_array(const zend_op_array *op_array, uint32_t dump_flags, cons case ZEND_LIVE_ROPE: fprintf(stderr, "(rope)\n"); break; + case ZEND_LIVE_NEW: + fprintf(stderr, "(new)\n"); + break; } } } diff --git a/ext/opcache/tests/opt/dce_006.phpt b/ext/opcache/tests/opt/dce_006.phpt index b53a73e990..c84805247a 100644 --- a/ext/opcache/tests/opt/dce_006.phpt +++ b/ext/opcache/tests/opt/dce_006.phpt @@ -33,6 +33,8 @@ L3 (6): CV1($a) = QM_ASSIGN V2 L4 (7): ASSIGN_OBJ CV1($a) string("foo") L5 (7): OP_DATA CV0($x) L6 (8): RETURN null +LIVE RANGES: + 2: L2 - L3 (new) A::__destruct: ; (lines=1, args=0, vars=0, tmps=0) ; (after optimizer)