--- /dev/null
+--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!
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);
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();
}
/* }}} */
#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) */
#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)
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) {
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));
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++;
}
}
}
+ 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;
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)));
}
}
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)));
}
USE_OPLINE
zend_execute_data *call = EX(call);
zend_function *fbc = call->func;
- zend_object *object;
zval *ret;
SAVE_OPLINE();
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);
}
/* 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,
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)));
}
}
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)));
}
USE_OPLINE
zend_execute_data *call = EX(call);
zend_function *fbc = call->func;
- zend_object *object;
zval *ret;
SAVE_OPLINE();
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);
USE_OPLINE
zend_execute_data *call = EX(call);
zend_function *fbc = call->func;
- zend_object *object;
zval *ret;
SAVE_OPLINE();
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);
}
/* 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,
}
/* 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,
}
/* 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,
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)));
}
}
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)));
}
case ZEND_LIVE_ROPE:
fprintf(stderr, "(rope)\n");
break;
+ case ZEND_LIVE_NEW:
+ fprintf(stderr, "(new)\n");
+ break;
}
}
}
case ZEND_LIVE_ROPE:
fprintf(stderr, "(rope)\n");
break;
+ case ZEND_LIVE_NEW:
+ fprintf(stderr, "(new)\n");
+ break;
}
}
}
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)