diff Objects/typeobject.c @ 23687:c4d2d305fd98 2.2

Backport to 2.2.x: Address SF bug 519621: slots weren't traversed by GC. While I was at it, I added a tp_clear handler and changed the tp_dealloc handler to use the clear_slots helper for the tp_clear handler. Also set mp->flags = READONLY for the __weakref__ pseudo-slot. [Note that I am *not* backporting the part of that patch that tightened the __slot__ rules.]
author Guido van Rossum <guido@python.org>
date Tue, 04 Jun 2002 21:19:55 +0000
parents a34523121cf4
children 2c8ea7f9a3ce
line wrap: on
line diff
--- a/Objects/typeobject.c	Tue Jun 04 16:29:24 2002 +0000
+++ b/Objects/typeobject.c	Tue Jun 04 21:19:55 2002 +0000
@@ -4,6 +4,20 @@
 #include "Python.h"
 #include "structmember.h"
 
+#include <ctype.h>
+
+/* The *real* layout of a type object when allocated on the heap */
+/* XXX Should we publish this in a header file? */
+typedef struct {
+	PyTypeObject type;
+	PyNumberMethods as_number;
+	PySequenceMethods as_sequence;
+	PyMappingMethods as_mapping;
+	PyBufferProcs as_buffer;
+	PyObject *name, *slots;
+	PyMemberDef members[1];
+} etype;
+
 static PyMemberDef type_members[] = {
 	{"__basicsize__", T_INT, offsetof(PyTypeObject,tp_basicsize),READONLY},
 	{"__itemsize__", T_INT, offsetof(PyTypeObject, tp_itemsize), READONLY},
@@ -223,16 +237,43 @@
 /* Helpers for subtyping */
 
 static int
+traverse_slots(PyTypeObject *type, PyObject *self, visitproc visit, void *arg)
+{
+	int i, n;
+	PyMemberDef *mp;
+
+	n = type->ob_size;
+	mp = ((etype *)type)->members;
+	for (i = 0; i < n; i++, mp++) {
+		if (mp->type == T_OBJECT_EX) {
+			char *addr = (char *)self + mp->offset;
+			PyObject *obj = *(PyObject **)addr;
+			if (obj != NULL) {
+				int err = visit(obj, arg);
+				if (err)
+					return err;
+			}
+		}
+	}
+	return 0;
+}
+
+static int
 subtype_traverse(PyObject *self, visitproc visit, void *arg)
 {
 	PyTypeObject *type, *base;
-	traverseproc f;
-	int err;
-
-	/* Find the nearest base with a different tp_traverse */
+	traverseproc basetraverse;
+
+	/* Find the nearest base with a different tp_traverse,
+	   and traverse slots while we're at it */
 	type = self->ob_type;
-	base = type->tp_base;
-	while ((f = base->tp_traverse) == subtype_traverse) {
+	base = type;
+	while ((basetraverse = base->tp_traverse) == subtype_traverse) {
+		if (base->ob_size) {
+			int err = traverse_slots(base, self, visit, arg);
+			if (err)
+				return err;
+		}
 		base = base->tp_base;
 		assert(base);
 	}
@@ -240,14 +281,63 @@
 	if (type->tp_dictoffset != base->tp_dictoffset) {
 		PyObject **dictptr = _PyObject_GetDictPtr(self);
 		if (dictptr && *dictptr) {
-			err = visit(*dictptr, arg);
+			int err = visit(*dictptr, arg);
 			if (err)
 				return err;
 		}
 	}
 
-	if (f)
-		return f(self, visit, arg);
+	if (basetraverse)
+		return basetraverse(self, visit, arg);
+	return 0;
+}
+
+static void
+clear_slots(PyTypeObject *type, PyObject *self)
+{
+	int i, n;
+	PyMemberDef *mp;
+
+	n = type->ob_size;
+	mp = ((etype *)type)->members;
+	for (i = 0; i < n; i++, mp++) {
+		if (mp->type == T_OBJECT_EX && !(mp->flags & READONLY)) {
+			char *addr = (char *)self + mp->offset;
+			PyObject *obj = *(PyObject **)addr;
+			if (obj != NULL) {
+				Py_DECREF(obj);
+				*(PyObject **)addr = NULL;
+			}
+		}
+	}
+}
+
+static int
+subtype_clear(PyObject *self)
+{
+	PyTypeObject *type, *base;
+	inquiry baseclear;
+
+	/* Find the nearest base with a different tp_clear
+	   and clear slots while we're at it */
+	type = self->ob_type;
+	base = type;
+	while ((baseclear = base->tp_clear) == subtype_clear) {
+		if (base->ob_size)
+			clear_slots(base, self);
+		base = base->tp_base;
+		assert(base);
+	}
+
+	if (type->tp_dictoffset != base->tp_dictoffset) {
+		PyObject **dictptr = _PyObject_GetDictPtr(self);
+		if (dictptr && *dictptr) {
+			PyDict_Clear(*dictptr);
+		}
+	}
+
+	if (baseclear)
+		return baseclear(self);
 	return 0;
 }
 
@@ -326,41 +416,24 @@
 subtype_dealloc(PyObject *self)
 {
 	PyTypeObject *type, *base;
-	destructor f;
+	destructor basedealloc;
 
 	/* This exists so we can DECREF self->ob_type */
 
 	if (call_finalizer(self) < 0)
 		return;
 
-	/* Find the nearest base with a different tp_dealloc */
+	/* Find the nearest base with a different tp_dealloc
+	   and clear slots while we're at it */
 	type = self->ob_type;
-	base = type->tp_base;
-	while ((f = base->tp_dealloc) == subtype_dealloc) {
+	base = type;
+	while ((basedealloc = base->tp_dealloc) == subtype_dealloc) {
+		if (base->ob_size)
+			clear_slots(base, self);
 		base = base->tp_base;
 		assert(base);
 	}
 
-	/* Clear __slots__ variables */
-	if (type->tp_basicsize != base->tp_basicsize &&
-	    type->tp_itemsize == 0)
-	{
-		char *addr = ((char *)self);
-		char *p = addr + base->tp_basicsize;
-		char *q = addr + type->tp_basicsize;
-		for (; p < q; p += sizeof(PyObject *)) {
-			PyObject **pp;
-			if (p == addr + type->tp_dictoffset ||
-			    p == addr + type->tp_weaklistoffset)
-				continue;
-			pp = (PyObject **)p;
-			if (*pp != NULL) {
-				Py_DECREF(*pp);
-				*pp = NULL;
-			}
-		}
-	}
-
 	/* If we added a dict, DECREF it */
 	if (type->tp_dictoffset && !base->tp_dictoffset) {
 		PyObject **dictptr = _PyObject_GetDictPtr(self);
@@ -382,8 +455,8 @@
 		_PyObject_GC_UNTRACK(self);
 
 	/* Call the base tp_dealloc() */
-	assert(f);
-	f(self);
+	assert(basedealloc);
+	basedealloc(self);
 
 	/* Can't reference self beyond this point */
 	if (type->tp_flags & Py_TPFLAGS_HEAPTYPE) {
@@ -393,16 +466,6 @@
 
 staticforward PyTypeObject *solid_base(PyTypeObject *type);
 
-typedef struct {
-	PyTypeObject type;
-	PyNumberMethods as_number;
-	PySequenceMethods as_sequence;
-	PyMappingMethods as_mapping;
-	PyBufferProcs as_buffer;
-	PyObject *name, *slots;
-	PyMemberDef members[1];
-} etype;
-
 /* type test with subclassing support */
 
 int
@@ -1142,6 +1205,7 @@
 			if (base->tp_weaklistoffset == 0 &&
 			    strcmp(mp->name, "__weakref__") == 0) {
 				mp->type = T_OBJECT;
+				mp->flags = READONLY;
 				type->tp_weaklistoffset = slotoffset;
 			}
 			slotoffset += sizeof(PyObject *);
@@ -1191,7 +1255,7 @@
 	if (type->tp_flags & Py_TPFLAGS_HAVE_GC) {
 		type->tp_free = _PyObject_GC_Del;
 		type->tp_traverse = subtype_traverse;
-		type->tp_clear = base->tp_clear;
+		type->tp_clear = subtype_clear;
 	}
 	else
 		type->tp_free = _PyObject_Del;