]> granicus.if.org Git - php/commitdiff
Replace "ZEND_CALL_CTOR" hack by additional live-range
authorDmitry Stogov <dmitry@zend.com>
Thu, 11 Apr 2019 21:49:45 +0000 (00:49 +0300)
committerDmitry Stogov <dmitry@zend.com>
Thu, 11 Apr 2019 21:49:45 +0000 (00:49 +0300)
Zend/tests/bug29368_3.phpt [new file with mode: 0644]
Zend/zend_compile.c
Zend/zend_compile.h
Zend/zend_execute.c
Zend/zend_opcode.c
Zend/zend_vm_def.h
Zend/zend_vm_execute.h
ext/opcache/Optimizer/zend_dump.c
ext/opcache/tests/opt/dce_006.phpt

diff --git a/Zend/tests/bug29368_3.phpt b/Zend/tests/bug29368_3.phpt
new file mode 100644 (file)
index 0000000..fafcc2a
--- /dev/null
@@ -0,0 +1,33 @@
+--TEST--
+Bug #29368.3 (The destructor is called when an exception is thrown from the constructor).
+--FILE--
+<?php
+class Foo {
+       function __construct() {
+               echo __METHOD__ . "\n";
+       }
+       function __destruct() {
+               echo __METHOD__ . "\n";
+       }
+}
+class Bar {
+       function __construct() {
+               echo __METHOD__ . "\n";
+               throw new Exception;
+       }
+       function __destruct() {
+               echo __METHOD__ . "\n";
+       }
+}
+
+try {
+       new Foo() + new Bar();
+} catch(Exception $exc) {
+       echo "Caught exception!\n";
+}
+?>
+--EXPECT--
+Foo::__construct
+Bar::__construct
+Foo::__destruct
+Caught exception!
index ccdbd8a1b7699b2a011eb498284133e266bd494c..1294c1f488dbb0cc413ea5cabab5cd4602944cc1 100644 (file)
@@ -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();
 }
 /* }}} */
index 82d70fd4b026ddc51556965e31581781ec2f365c..523d04e794599b211f9482096f3dd6621ad6ee0a 100644 (file)
@@ -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)
index bb3ca87b04264330499f2a8cd8f4b9ab2c60f097..6d13457a431b42af227b208f6fae66c8055cbb9e 100644 (file)
@@ -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));
index 5b1914c99dd192d51389adba9bc3fa9e0da03797..a44a7c562d203630a67aa791cce78722037fe855 100644 (file)
@@ -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;
index d71694223f86b054ceb32bf185ed0156404304ac..e975a92014f2ecedb13563e4f36cb6b6179dbaed 100644 (file)
@@ -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,
index 7977a2a4a0b59ea0552a1a36da7bef1a7ee4b07a..ad4f978ad1d7101a300f60b3aa801265a208a72c 100644 (file)
@@ -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)));
                }
index 3bc0650ab476b7657e7840938c1a0995454feed9..01c907cfea61147ac512022995740d2ff690804a 100644 (file)
@@ -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;
                                }
                        }
                }
index b53a73e9905c2b5dc421ec2402b039ed420c933c..c84805247a2a16ed22356b8851c4fac600dc475a 100644 (file)
@@ -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)