Add Garbage Collection support to new-style classes (not yet to their
instances).
Also added GC support to various auxiliary types: super, property,
descriptors, wrappers, dictproxy. (Only type objects have a tp_clear
field; the other types are.)
One change was necessary to the GC infrastructure. We have statically
allocated type objects that don't have a GC header (and can't easily
be given one) and heap-allocated type objects that do have a GC
header. Giving these different metatypes would be really ugly: I
tried, and I had to modify pickle.py, cPickle.c, copy.py, add a new
invent a new name for the new metatype and make it a built-in, change
affected tests... In short, a mess. So instead, we add a new type
slot tp_is_gc, which is a simple Boolean function that determines
whether a particular instance has GC headers or not. This slot is
only relevant for types that have the (new) GC flag bit set. If the
tp_is_gc slot is NULL (by far the most common case), all instances of
the type are deemed to have GC headers. This slot is called by the
PyObject_IS_GC() macro (which is only used twice, both times in
gcmodule.c).
I also changed the extern declarations for a bunch of GC-related
functions (_PyObject_GC_Del etc.): these always exist but objimpl.h
only declared them when WITH_CYCLE_GC was defined, but I needed to be
able to reference them without #ifdefs. (When WITH_CYCLE_GC is not
defined, they do the same as their non-GC counterparts anyway.)
diff --git a/Objects/typeobject.c b/Objects/typeobject.c
index 8a11dff..295be89 100644
--- a/Objects/typeobject.c
+++ b/Objects/typeobject.c
@@ -265,7 +265,7 @@
/* Finalize GC if the base doesn't do GC and we do */
if (PyType_IS_GC(type) && !PyType_IS_GC(base))
- PyObject_GC_Fini(self);
+ _PyObject_GC_UNTRACK(self);
/* Call the base tp_dealloc() */
assert(f);
@@ -864,6 +864,8 @@
Py_TPFLAGS_BASETYPE;
if (dynamic)
type->tp_flags |= Py_TPFLAGS_DYNAMICTYPE;
+ if (base->tp_flags & Py_TPFLAGS_HAVE_GC)
+ type->tp_flags |= Py_TPFLAGS_HAVE_GC;
/* It's a new-style number unless it specifically inherits any
old-style numeric behavior */
@@ -934,7 +936,8 @@
else {
if (add_dict) {
if (base->tp_itemsize)
- type->tp_dictoffset = -(long)sizeof(PyObject *);
+ type->tp_dictoffset =
+ -(long)sizeof(PyObject *);
else
type->tp_dictoffset = slotoffset;
slotoffset += sizeof(PyObject *);
@@ -966,7 +969,13 @@
/* Always override allocation strategy to use regular heap */
type->tp_alloc = PyType_GenericAlloc;
- type->tp_free = _PyObject_Del;
+ if (type->tp_flags & Py_TPFLAGS_HAVE_GC) {
+ type->tp_free = _PyObject_GC_Del;
+ type->tp_traverse = base->tp_traverse;
+ type->tp_clear = base->tp_clear;
+ }
+ else
+ type->tp_free = _PyObject_Del;
/* Initialize the rest */
if (PyType_Ready(type) < 0) {
@@ -1080,6 +1089,7 @@
/* Assert this is a heap-allocated type object */
assert(type->tp_flags & Py_TPFLAGS_HEAPTYPE);
+ _PyObject_GC_UNTRACK(type);
et = (etype *)type;
Py_XDECREF(type->tp_base);
Py_XDECREF(type->tp_dict);
@@ -1102,6 +1112,72 @@
"type(object) -> the object's type\n"
"type(name, bases, dict) -> a new type";
+static int
+type_traverse(PyTypeObject *type, visitproc visit, void *arg)
+{
+ etype *et;
+ int err;
+
+ if (!(type->tp_flags & Py_TPFLAGS_HEAPTYPE))
+ return 0;
+
+ et = (etype *)type;
+
+#define VISIT(SLOT) \
+ if (SLOT) { \
+ err = visit((PyObject *)(SLOT), arg); \
+ if (err) \
+ return err; \
+ }
+
+ VISIT(type->tp_dict);
+ VISIT(type->tp_defined);
+ VISIT(type->tp_mro);
+ VISIT(type->tp_bases);
+ VISIT(type->tp_base);
+ VISIT(et->slots);
+
+#undef VISIT
+
+ return 0;
+}
+
+static int
+type_clear(PyTypeObject *type)
+{
+ etype *et;
+ PyObject *tmp;
+
+ if (!(type->tp_flags & Py_TPFLAGS_HEAPTYPE))
+ return 0;
+
+ et = (etype *)type;
+
+#define CLEAR(SLOT) \
+ if (SLOT) { \
+ tmp = (PyObject *)(SLOT); \
+ SLOT = NULL; \
+ Py_DECREF(tmp); \
+ }
+
+ CLEAR(type->tp_dict);
+ CLEAR(type->tp_defined);
+ CLEAR(type->tp_mro);
+ CLEAR(type->tp_bases);
+ CLEAR(type->tp_base);
+ CLEAR(et->slots);
+
+#undef CLEAR
+
+ return 0;
+}
+
+static int
+type_is_gc(PyTypeObject *type)
+{
+ return type->tp_flags & Py_TPFLAGS_HEAPTYPE;
+}
+
PyTypeObject PyType_Type = {
PyObject_HEAD_INIT(&PyType_Type)
0, /* ob_size */
@@ -1123,10 +1199,11 @@
(getattrofunc)type_getattro, /* tp_getattro */
(setattrofunc)type_setattro, /* tp_setattro */
0, /* tp_as_buffer */
- Py_TPFLAGS_DEFAULT | Py_TPFLAGS_BASETYPE, /* tp_flags */
+ Py_TPFLAGS_DEFAULT | Py_TPFLAGS_HAVE_GC |
+ Py_TPFLAGS_BASETYPE, /* tp_flags */
type_doc, /* tp_doc */
- 0, /* tp_traverse */
- 0, /* tp_clear */
+ (traverseproc)type_traverse, /* tp_traverse */
+ (inquiry)type_clear, /* tp_clear */
0, /* tp_richcompare */
0, /* tp_weaklistoffset */
0, /* tp_iter */
@@ -1142,6 +1219,8 @@
0, /* tp_init */
0, /* tp_alloc */
type_new, /* tp_new */
+ _PyObject_GC_Del, /* tp_free */
+ (inquiry)type_is_gc, /* tp_is_gc */
};
@@ -3531,6 +3610,7 @@
{
superobject *su = (superobject *)self;
+ _PyObject_GC_UNTRACK(self);
Py_XDECREF(su->obj);
Py_XDECREF(su->type);
self->ob_type->tp_free(self);
@@ -3666,6 +3746,27 @@
" def meth(self, arg):\n"
" super(C, self).meth(arg)";
+static int
+super_traverse(PyObject *self, visitproc visit, void *arg)
+{
+ superobject *su = (superobject *)self;
+ int err;
+
+#define VISIT(SLOT) \
+ if (SLOT) { \
+ err = visit((PyObject *)(SLOT), arg); \
+ if (err) \
+ return err; \
+ }
+
+ VISIT(su->obj);
+ VISIT(su->type);
+
+#undef VISIT
+
+ return 0;
+}
+
PyTypeObject PySuper_Type = {
PyObject_HEAD_INIT(&PyType_Type)
0, /* ob_size */
@@ -3688,9 +3789,10 @@
super_getattro, /* tp_getattro */
0, /* tp_setattro */
0, /* tp_as_buffer */
- Py_TPFLAGS_DEFAULT | Py_TPFLAGS_BASETYPE, /* tp_flags */
+ Py_TPFLAGS_DEFAULT | Py_TPFLAGS_HAVE_GC |
+ Py_TPFLAGS_BASETYPE, /* tp_flags */
super_doc, /* tp_doc */
- 0, /* tp_traverse */
+ super_traverse, /* tp_traverse */
0, /* tp_clear */
0, /* tp_richcompare */
0, /* tp_weaklistoffset */
@@ -3707,5 +3809,5 @@
super_init, /* tp_init */
PyType_GenericAlloc, /* tp_alloc */
PyType_GenericNew, /* tp_new */
- _PyObject_Del, /* tp_free */
+ _PyObject_GC_Del, /* tp_free */
};