diff options
author | Guido van Rossum <guido@python.org> | 2002-06-04 19:52:53 (GMT) |
---|---|---|
committer | Guido van Rossum <guido@python.org> | 2002-06-04 19:52:53 (GMT) |
commit | 9923ffe2c0f96b1b79f2f5ae39b64b55e038c566 (patch) | |
tree | 7449f4009ac1d95cac9185ebad1d2d622546c708 /Objects | |
parent | e22bc1e84135765456dee6337e6e6f61245aa694 (diff) | |
download | cpython-9923ffe2c0f96b1b79f2f5ae39b64b55e038c566.zip cpython-9923ffe2c0f96b1b79f2f5ae39b64b55e038c566.tar.gz cpython-9923ffe2c0f96b1b79f2f5ae39b64b55e038c566.tar.bz2 |
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 tightened the rules for slot names: they must now be proper
identifiers (ignoring the dirty little fact that <ctype.h> is locale
sensitive).
Also set mp->flags = READONLY for the __weakref__ pseudo-slot.
Most of this is a 2.2 bugfix candidate; I'll apply it there myself.
Diffstat (limited to 'Objects')
-rw-r--r-- | Objects/typeobject.c | 186 |
1 files changed, 137 insertions, 49 deletions
diff --git a/Objects/typeobject.c b/Objects/typeobject.c index dd6f1b5..cb2c791 100644 --- a/Objects/typeobject.c +++ b/Objects/typeobject.c @@ -3,6 +3,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}, @@ -226,16 +240,43 @@ PyType_GenericNew(PyTypeObject *type, PyObject *args, PyObject *kwds) /* 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; + traverseproc basetraverse; - /* Find the nearest base with a different tp_traverse */ + /* 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); } @@ -243,14 +284,63 @@ subtype_traverse(PyObject *self, visitproc visit, void *arg) 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; } @@ -329,41 +419,24 @@ static void 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); @@ -385,8 +458,8 @@ subtype_dealloc(PyObject *self) _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) { @@ -396,16 +469,6 @@ subtype_dealloc(PyObject *self) 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 @@ -890,6 +953,33 @@ static PyMethodDef bozo_ml = {"__getstate__", bozo_func, METH_VARARGS}; static PyObject *bozo_obj = NULL; +static int +valid_identifier(PyObject *s) +{ + char *p; + int i, n; + + if (!PyString_Check(s)) { + PyErr_SetString(PyExc_TypeError, + "__slots__ must be strings"); + return 0; + } + p = PyString_AS_STRING(s); + n = PyString_GET_SIZE(s); + /* We must reject an empty name. As a hack, we bump the + length to 1 so that the loop will balk on the trailing \0. */ + if (n == 0) + n = 1; + for (i = 0; i < n; i++, p++) { + if (!(i == 0 ? isalpha(*p) : isalnum(*p)) && *p != '_') { + PyErr_SetString(PyExc_TypeError, + "__slots__ must be identifiers"); + return 0; + } + } + return 1; +} + static PyObject * type_new(PyTypeObject *metatype, PyObject *args, PyObject *kwds) { @@ -1004,13 +1094,10 @@ type_new(PyTypeObject *metatype, PyObject *args, PyObject *kwds) return NULL; } for (i = 0; i < nslots; i++) { - if (!PyString_Check(PyTuple_GET_ITEM(slots, i))) { - PyErr_SetString(PyExc_TypeError, - "__slots__ must be a sequence of strings"); + if (!valid_identifier(PyTuple_GET_ITEM(slots, i))) { Py_DECREF(slots); return NULL; } - /* XXX Check against null bytes in name */ } } if (slots != NULL) { @@ -1145,6 +1232,7 @@ type_new(PyTypeObject *metatype, PyObject *args, PyObject *kwds) if (base->tp_weaklistoffset == 0 && strcmp(mp->name, "__weakref__") == 0) { mp->type = T_OBJECT; + mp->flags = READONLY; type->tp_weaklistoffset = slotoffset; } slotoffset += sizeof(PyObject *); @@ -1194,7 +1282,7 @@ type_new(PyTypeObject *metatype, PyObject *args, PyObject *kwds) 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; |