This simplifies checks and allows reset this flag for "acyclic" arrays and objects.
zval_dtor_func(garbage);
return;
} else {
- GC_ZVAL_CHECK_POSSIBLE_ROOT(variable_ptr);
+ gc_check_possible_root(garbage);
}
}
ZVAL_REF(variable_ptr, ref);
zval *end = cv + EX(func)->op_array.last_var;
while (EXPECTED(cv != end)) {
if (Z_REFCOUNTED_P(cv)) {
- if (!Z_DELREF_P(cv)) {
- zend_refcounted *r = Z_COUNTED_P(cv);
+ zend_refcounted *r = Z_COUNTED_P(cv);
+ if (!--GC_REFCOUNT(r)) {
ZVAL_NULL(cv);
zval_dtor_func(r);
} else {
- GC_ZVAL_CHECK_POSSIBLE_ROOT(cv);
+ gc_check_possible_root(r);
}
}
cv++;
return variable_ptr;
} else { /* we need to split */
/* optimized version of GC_ZVAL_CHECK_POSSIBLE_ROOT(variable_ptr) */
- if ((Z_COLLECTABLE_P(variable_ptr)) &&
- UNEXPECTED(!GC_INFO(garbage))) {
+ if (UNEXPECTED(GC_MAY_LEAK(garbage))) {
gc_possible_root(garbage);
}
}
do {
p--;
if (Z_REFCOUNTED_P(p)) {
- if (!Z_DELREF_P(p)) {
- zend_refcounted *r = Z_COUNTED_P(p);
+ zend_refcounted *r = Z_COUNTED_P(p);
+ if (!--GC_REFCOUNT(r)) {
ZVAL_NULL(p);
zval_dtor_func(r);
} else {
- GC_ZVAL_CHECK_POSSIBLE_ROOT(p);
+ gc_check_possible_root(r);
}
}
} while (p != end);
ZEND_API int zend_gc_collect_cycles(void);
END_EXTERN_C()
-#define GC_ZVAL_CHECK_POSSIBLE_ROOT(z) \
- gc_check_possible_root((z))
-
#define GC_REMOVE_FROM_BUFFER(p) do { \
zend_refcounted *_p = (zend_refcounted*)(p); \
if (GC_ADDRESS(GC_INFO(_p))) { \
} \
} while (0)
-static zend_always_inline void gc_check_possible_root(zval *z)
+#define GC_MAY_LEAK(ref) \
+ ((GC_TYPE_INFO(ref) & \
+ (GC_INFO_MASK | (GC_COLLECTABLE << GC_FLAGS_SHIFT))) == \
+ (GC_COLLECTABLE << GC_FLAGS_SHIFT))
+
+static zend_always_inline void gc_check_possible_root(zend_refcounted *ref)
{
- ZVAL_DEREF(z);
- if (Z_COLLECTABLE_P(z) && UNEXPECTED(!Z_GC_INFO_P(z))) {
- gc_possible_root(Z_COUNTED_P(z));
+ if (GC_TYPE(ref) == IS_REFERENCE) {
+ zval *zv = &((zend_reference*)ref)->val;
+
+ if (!Z_REFCOUNTED_P(zv)) {
+ return;
+ }
+ ref = Z_COUNTED_P(zv);
+ }
+ if (UNEXPECTED(GC_MAY_LEAK(ref))) {
+ gc_possible_root(ref);
}
}
ZEND_API void ZEND_FASTCALL _zend_hash_init(HashTable *ht, uint32_t nSize, dtor_func_t pDestructor, zend_bool persistent ZEND_FILE_LINE_DC)
{
GC_REFCOUNT(ht) = 1;
- GC_TYPE_INFO(ht) = IS_ARRAY;
+ GC_TYPE_INFO(ht) = IS_ARRAY | (persistent ? 0 : (GC_COLLECTABLE << GC_FLAGS_SHIFT));
ht->u.flags = (persistent ? HASH_FLAG_PERSISTENT : 0) | HASH_FLAG_APPLY_PROTECTION | HASH_FLAG_STATIC_KEYS;
ht->nTableSize = zend_hash_check_size(nSize);
ht->nTableMask = HT_MIN_MASK;
ALLOC_HASHTABLE(target);
GC_REFCOUNT(target) = 1;
- GC_TYPE_INFO(target) = IS_ARRAY;
+ GC_TYPE_INFO(target) = IS_ARRAY | (GC_COLLECTABLE << GC_FLAGS_SHIFT);
target->nTableSize = source->nTableSize;
target->pDestructor = source->pDestructor;
zval *p, *end;
GC_REFCOUNT(object) = 1;
- GC_TYPE_INFO(object) = IS_OBJECT;
+ GC_TYPE_INFO(object) = IS_OBJECT | (GC_COLLECTABLE << GC_FLAGS_SHIFT);
object->ce = ce;
object->properties = NULL;
zend_objects_store_put(object);
{
if (--GC_REFCOUNT(obj) == 0) {
zend_objects_store_del(obj);
- } else if (UNEXPECTED(!GC_INFO(obj))) {
+ } else if (UNEXPECTED(GC_MAY_LEAK((zend_refcounted*)obj))) {
gc_possible_root((zend_refcounted*)obj);
}
}
#define Z_GC_TYPE_INFO(zval) GC_TYPE_INFO(Z_COUNTED(zval))
#define Z_GC_TYPE_INFO_P(zval_p) Z_GC_TYPE_INFO(*(zval_p))
+#define GC_FLAGS_SHIFT 8
+#define GC_INFO_SHIFT 16
+#define GC_INFO_MASK 0xffff0000
+
+/* zval.value->gc.u.v.flags */
+#define GC_COLLECTABLE (1<<7)
+
+#define GC_ARRAY (IS_ARRAY | (GC_COLLECTABLE << GC_FLAGS_SHIFT))
+#define GC_OBJECT (IS_OBJECT | (GC_COLLECTABLE << GC_FLAGS_SHIFT))
+
/* zval.u1.v.type_flags */
#define IS_TYPE_CONSTANT (1<<0)
#define IS_TYPE_IMMUTABLE (1<<1)
#define IS_TYPE_REFCOUNTED (1<<2)
-#define IS_TYPE_COLLECTABLE (1<<3)
#define IS_TYPE_COPYABLE (1<<4)
/* extended types */
#define IS_INTERNED_STRING_EX IS_STRING
-#define IS_STRING_EX (IS_STRING | (( IS_TYPE_REFCOUNTED | IS_TYPE_COPYABLE) << Z_TYPE_FLAGS_SHIFT))
-#define IS_ARRAY_EX (IS_ARRAY | (( IS_TYPE_REFCOUNTED | IS_TYPE_COLLECTABLE | IS_TYPE_COPYABLE) << Z_TYPE_FLAGS_SHIFT))
-#define IS_OBJECT_EX (IS_OBJECT | (( IS_TYPE_REFCOUNTED | IS_TYPE_COLLECTABLE ) << Z_TYPE_FLAGS_SHIFT))
-#define IS_RESOURCE_EX (IS_RESOURCE | (( IS_TYPE_REFCOUNTED ) << Z_TYPE_FLAGS_SHIFT))
-#define IS_REFERENCE_EX (IS_REFERENCE | (( IS_TYPE_REFCOUNTED ) << Z_TYPE_FLAGS_SHIFT))
+#define IS_STRING_EX (IS_STRING | (( IS_TYPE_REFCOUNTED | IS_TYPE_COPYABLE) << Z_TYPE_FLAGS_SHIFT))
+#define IS_ARRAY_EX (IS_ARRAY | (( IS_TYPE_REFCOUNTED | IS_TYPE_COPYABLE) << Z_TYPE_FLAGS_SHIFT))
+#define IS_OBJECT_EX (IS_OBJECT | (( IS_TYPE_REFCOUNTED ) << Z_TYPE_FLAGS_SHIFT))
+#define IS_RESOURCE_EX (IS_RESOURCE | (( IS_TYPE_REFCOUNTED ) << Z_TYPE_FLAGS_SHIFT))
+#define IS_REFERENCE_EX (IS_REFERENCE | (( IS_TYPE_REFCOUNTED ) << Z_TYPE_FLAGS_SHIFT))
-#define IS_CONSTANT_EX (IS_CONSTANT | ((IS_TYPE_CONSTANT | IS_TYPE_REFCOUNTED | IS_TYPE_COPYABLE) << Z_TYPE_FLAGS_SHIFT))
-#define IS_CONSTANT_AST_EX (IS_CONSTANT_AST | ((IS_TYPE_CONSTANT | IS_TYPE_REFCOUNTED | IS_TYPE_COPYABLE) << Z_TYPE_FLAGS_SHIFT))
+#define IS_CONSTANT_EX (IS_CONSTANT | ((IS_TYPE_CONSTANT | IS_TYPE_REFCOUNTED | IS_TYPE_COPYABLE) << Z_TYPE_FLAGS_SHIFT))
+#define IS_CONSTANT_AST_EX (IS_CONSTANT_AST | ((IS_TYPE_CONSTANT | IS_TYPE_REFCOUNTED | IS_TYPE_COPYABLE) << Z_TYPE_FLAGS_SHIFT))
/* zval.u1.v.const_flags */
#define IS_CONSTANT_UNQUALIFIED 0x010
#define Z_REFCOUNTED(zval) ((Z_TYPE_FLAGS(zval) & IS_TYPE_REFCOUNTED) != 0)
#define Z_REFCOUNTED_P(zval_p) Z_REFCOUNTED(*(zval_p))
-#define Z_COLLECTABLE(zval) ((Z_TYPE_FLAGS(zval) & IS_TYPE_COLLECTABLE) != 0)
-#define Z_COLLECTABLE_P(zval_p) Z_COLLECTABLE(*(zval_p))
-
#define Z_COPYABLE(zval) ((Z_TYPE_FLAGS(zval) & IS_TYPE_COPYABLE) != 0)
#define Z_COPYABLE_P(zval_p) Z_COPYABLE(*(zval_p))
#define Z_OPT_REFCOUNTED(zval) ((Z_TYPE_INFO(zval) & (IS_TYPE_REFCOUNTED << Z_TYPE_FLAGS_SHIFT)) != 0)
#define Z_OPT_REFCOUNTED_P(zval_p) Z_OPT_REFCOUNTED(*(zval_p))
-#define Z_OPT_COLLECTABLE(zval) ((Z_TYPE_INFO(zval) & (IS_TYPE_COLLECTABLE << Z_TYPE_FLAGS_SHIFT)) != 0)
-#define Z_OPT_COLLECTABLE_P(zval_p) Z_OPT_COLLECTABLE(*(zval_p))
-
#define Z_OPT_COPYABLE(zval) ((Z_TYPE_INFO(zval) & (IS_TYPE_COPYABLE << Z_TYPE_FLAGS_SHIFT)) != 0)
#define Z_OPT_COPYABLE_P(zval_p) Z_OPT_COPYABLE(*(zval_p))
static zend_always_inline void i_zval_ptr_dtor(zval *zval_ptr ZEND_FILE_LINE_DC)
{
if (Z_REFCOUNTED_P(zval_ptr)) {
- if (!Z_DELREF_P(zval_ptr)) {
- _zval_dtor_func(Z_COUNTED_P(zval_ptr) ZEND_FILE_LINE_RELAY_CC);
+ zend_refcounted *ref = Z_COUNTED_P(zval_ptr);
+ if (!--GC_REFCOUNT(ref)) {
+ _zval_dtor_func(ref ZEND_FILE_LINE_RELAY_CC);
} else {
- GC_ZVAL_CHECK_POSSIBLE_ROOT(zval_ptr);
+ gc_check_possible_root(ref);
}
}
}
if (Z_REFCOUNTED_P(var)) {
zend_refcounted *garbage = Z_COUNTED_P(var);
+ ZVAL_UNDEF(var);
if (!--GC_REFCOUNT(garbage)) {
- ZVAL_UNDEF(var);
zval_dtor_func(garbage);
} else {
- zval *z = var;
- ZVAL_DEREF(z);
- if (Z_COLLECTABLE_P(z) && UNEXPECTED(!Z_GC_INFO_P(z))) {
- ZVAL_UNDEF(var);
- gc_possible_root(Z_COUNTED_P(z));
- } else {
- ZVAL_UNDEF(var);
- }
+ gc_check_possible_root(garbage);
}
} else {
ZVAL_UNDEF(var);
variable_ptr = GET_OP1_ZVAL_PTR_PTR_UNDEF(BP_VAR_W);
if (UNEXPECTED(Z_REFCOUNTED_P(variable_ptr))) {
- uint32_t refcnt = Z_DELREF_P(variable_ptr);
+ zend_refcounted *ref = Z_COUNTED_P(variable_ptr);
+ uint32_t refcnt = --GC_REFCOUNT(ref);
if (EXPECTED(variable_ptr != value)) {
if (refcnt == 0) {
SAVE_OPLINE();
- zval_dtor_func(Z_COUNTED_P(variable_ptr));
+ zval_dtor_func(ref);
if (UNEXPECTED(EG(exception))) {
ZVAL_NULL(variable_ptr);
HANDLE_EXCEPTION();
}
} else {
- GC_ZVAL_CHECK_POSSIBLE_ROOT(variable_ptr);
+ gc_check_possible_root(ref);
}
}
}
if (Z_REFCOUNTED_P(var)) {
zend_refcounted *garbage = Z_COUNTED_P(var);
+ ZVAL_UNDEF(var);
if (!--GC_REFCOUNT(garbage)) {
- ZVAL_UNDEF(var);
zval_dtor_func(garbage);
} else {
- zval *z = var;
- ZVAL_DEREF(z);
- if (Z_COLLECTABLE_P(z) && UNEXPECTED(!Z_GC_INFO_P(z))) {
- ZVAL_UNDEF(var);
- gc_possible_root(Z_COUNTED_P(z));
- } else {
- ZVAL_UNDEF(var);
- }
+ gc_check_possible_root(garbage);
}
} else {
ZVAL_UNDEF(var);
variable_ptr = _get_zval_ptr_cv_undef_BP_VAR_W(execute_data, opline->op1.var);
if (UNEXPECTED(Z_REFCOUNTED_P(variable_ptr))) {
- uint32_t refcnt = Z_DELREF_P(variable_ptr);
+ zend_refcounted *ref = Z_COUNTED_P(variable_ptr);
+ uint32_t refcnt = --GC_REFCOUNT(ref);
if (EXPECTED(variable_ptr != value)) {
if (refcnt == 0) {
SAVE_OPLINE();
- zval_dtor_func(Z_COUNTED_P(variable_ptr));
+ zval_dtor_func(ref);
if (UNEXPECTED(EG(exception))) {
ZVAL_NULL(variable_ptr);
HANDLE_EXCEPTION();
}
} else {
- GC_ZVAL_CHECK_POSSIBLE_ROOT(variable_ptr);
+ gc_check_possible_root(ref);
}
}
}
if (Z_REFCOUNTED_P(var)) {
zend_refcounted *garbage = Z_COUNTED_P(var);
+ ZVAL_UNDEF(var);
if (!--GC_REFCOUNT(garbage)) {
- ZVAL_UNDEF(var);
zval_dtor_func(garbage);
} else {
- zval *z = var;
- ZVAL_DEREF(z);
- if (Z_COLLECTABLE_P(z) && UNEXPECTED(!Z_GC_INFO_P(z))) {
- ZVAL_UNDEF(var);
- gc_possible_root(Z_COUNTED_P(z));
- } else {
- ZVAL_UNDEF(var);
- }
+ gc_check_possible_root(garbage);
}
} else {
ZVAL_UNDEF(var);
if (Z_REFCOUNTED_P(var)) {
zend_refcounted *garbage = Z_COUNTED_P(var);
+ ZVAL_UNDEF(var);
if (!--GC_REFCOUNT(garbage)) {
- ZVAL_UNDEF(var);
zval_dtor_func(garbage);
} else {
- zval *z = var;
- ZVAL_DEREF(z);
- if (Z_COLLECTABLE_P(z) && UNEXPECTED(!Z_GC_INFO_P(z))) {
- ZVAL_UNDEF(var);
- gc_possible_root(Z_COUNTED_P(z));
- } else {
- ZVAL_UNDEF(var);
- }
+ gc_check_possible_root(garbage);
}
} else {
ZVAL_UNDEF(var);