diff --git a/Include/descrobject.h b/Include/descrobject.h --- a/Include/descrobject.h +++ b/Include/descrobject.h @@ -40,14 +40,14 @@ typedef struct { PyObject_HEAD - PyTypeObject *d_type; + PyObject *d_w_type; /* a weakref, to prevent reference cycles */ PyObject *d_name; PyObject *d_qualname; } PyDescrObject; #define PyDescr_COMMON PyDescrObject d_common -#define PyDescr_TYPE(x) (((PyDescrObject *)(x))->d_type) +#define PyDescr_TYPE(x) ((PyTypeObject*)PyWeakref_GET_OBJECT(((PyDescrObject *)(x))->d_w_type)) #define PyDescr_NAME(x) (((PyDescrObject *)(x))->d_name) typedef struct { diff --git a/Lib/test/test_gc.py b/Lib/test/test_gc.py --- a/Lib/test/test_gc.py +++ b/Lib/test/test_gc.py @@ -94,7 +94,7 @@ pass gc.collect() del A - self.assertNotEqual(gc.collect(), 0) + self.assertEqual(gc.collect(), 0) def test_instance(self): class A: @@ -123,7 +123,7 @@ del a self.assertNotEqual(gc.collect(), 0) del B, C - self.assertNotEqual(gc.collect(), 0) + self.assertEqual(gc.collect(), 0) A.a = A() del A self.assertNotEqual(gc.collect(), 0) diff --git a/Objects/descrobject.c b/Objects/descrobject.c --- a/Objects/descrobject.c +++ b/Objects/descrobject.c @@ -3,11 +3,16 @@ #include "Python.h" #include "structmember.h" /* Why is this not included in Python.h? */ +#define PyDescr_TYPENAME(descr) \ + ((PyObject*)PyDescr_TYPE(descr) != Py_None ? \ + PyDescr_TYPE(descr)->tp_name : \ + "?") + static void descr_dealloc(PyDescrObject *descr) { _PyObject_GC_UNTRACK(descr); - Py_XDECREF(descr->d_type); + Py_XDECREF(descr->d_w_type); Py_XDECREF(descr->d_name); Py_XDECREF(descr->d_qualname); PyObject_GC_Del(descr); @@ -28,7 +33,7 @@ if (descr->d_name != NULL && PyUnicode_Check(descr->d_name)) name = descr->d_name; - return PyUnicode_FromFormat(format, name, "?", descr->d_type->tp_name); + return PyUnicode_FromFormat(format, name, "?", PyDescr_TYPENAME(descr)); } static PyObject * @@ -67,12 +72,12 @@ *pres = (PyObject *)descr; return 1; } - if (!PyObject_TypeCheck(obj, descr->d_type)) { + if (!PyObject_TypeCheck(obj, PyDescr_TYPE(descr))) { PyErr_Format(PyExc_TypeError, "descriptor '%V' for '%s' objects " "doesn't apply to '%s' object", descr_name((PyDescrObject *)descr), "?", - descr->d_type->tp_name, + PyDescr_TYPENAME(descr), obj->ob_type->tp_name); *pres = NULL; return 1; @@ -169,12 +174,12 @@ int *pres) { assert(obj != NULL); - if (!PyObject_TypeCheck(obj, descr->d_type)) { + if (!PyObject_TypeCheck(obj, PyDescr_TYPE(descr))) { PyErr_Format(PyExc_TypeError, "descriptor '%V' for '%.100s' objects " "doesn't apply to '%.100s' object", descr_name(descr), "?", - descr->d_type->tp_name, + PyDescr_TYPENAME(descr), obj->ob_type->tp_name); *pres = -1; return 1; @@ -372,7 +377,7 @@ return NULL; } - type_qualname = _PyObject_GetAttrId((PyObject *)descr->d_type, + type_qualname = _PyObject_GetAttrId((PyObject *)PyDescr_TYPE(descr), &PyId___qualname__); if (type_qualname == NULL) return NULL; @@ -398,8 +403,15 @@ return descr->d_qualname; } +static PyObject * +descr_get_objclass(PyDescrObject *descr) +{ + PyObject *result = (PyObject*)PyDescr_TYPE(descr); + Py_INCREF(result); + return result; +} + static PyMemberDef descr_members[] = { - {"__objclass__", T_OBJECT, offsetof(PyDescrObject, d_type), READONLY}, {"__name__", T_OBJECT, offsetof(PyDescrObject, d_name), READONLY}, {0} }; @@ -407,6 +419,7 @@ static PyGetSetDef method_getset[] = { {"__doc__", (getter)method_get_doc}, {"__qualname__", (getter)descr_get_qualname}, + {"__objclass__", (getter)descr_get_objclass}, {0} }; @@ -423,6 +436,7 @@ static PyGetSetDef member_getset[] = { {"__doc__", (getter)member_get_doc}, {"__qualname__", (getter)descr_get_qualname}, + {"__objclass__", (getter)descr_get_objclass}, {0} }; @@ -439,6 +453,7 @@ static PyGetSetDef getset_getset[] = { {"__doc__", (getter)getset_get_doc}, {"__qualname__", (getter)descr_get_qualname}, + {"__objclass__", (getter)descr_get_objclass}, {0} }; @@ -455,6 +470,7 @@ static PyGetSetDef wrapperdescr_getset[] = { {"__doc__", (getter)wrapperdescr_get_doc}, {"__qualname__", (getter)descr_get_qualname}, + {"__objclass__", (getter)descr_get_objclass}, {0} }; @@ -462,7 +478,7 @@ descr_traverse(PyObject *self, visitproc visit, void *arg) { PyDescrObject *descr = (PyDescrObject *)self; - Py_VISIT(descr->d_type); + Py_VISIT(descr->d_w_type); return 0; } @@ -659,8 +675,7 @@ descr = (PyDescrObject *)PyType_GenericAlloc(descrtype, 0); if (descr != NULL) { - Py_XINCREF(type); - descr->d_type = type; + descr->d_w_type = PyWeakref_NewRef((PyObject*)type, NULL); descr->d_name = PyUnicode_InternFromString(name); if (descr->d_name == NULL) { Py_DECREF(descr); diff --git a/Objects/typeobject.c b/Objects/typeobject.c --- a/Objects/typeobject.c +++ b/Objects/typeobject.c @@ -35,6 +35,46 @@ static struct method_cache_entry method_cache[1 << MCACHE_SIZE_EXP]; static unsigned int next_version_tag = 0; +/* The type is the first member of its tp_mro. But this generates a cycle. + * So, we normally just store a None there, but fix it up just in time + * when needed. + * To support custom mro where the first item isn't necessarily the class, + * we have a check for this case. + */ + +#define NERF_MRO(type) do { \ + PyObject *mro = (type)->tp_mro; \ + assert(Py_REFCNT(mro) == 1); \ + if ((type->tp_flags & Py_TPFLAGS_HEAPTYPE) && \ + PyTuple_GET_ITEM(mro, 0) == (PyObject*)(type)) { \ + Py_INCREF(Py_None); \ + PyTuple_SET_ITEM(mro, 0, Py_None); \ + Py_DECREF(type); \ + } \ +} while (0) + +#define FIX_MRO(type, mro) do { \ + assert(Py_REFCNT(mro) == 1); \ + if (PyTuple_GET_ITEM((mro), 0) == Py_None) { \ + Py_INCREF(type); \ + PyTuple_SET_ITEM((mro), 0, (PyObject*)(type)); \ + Py_DECREF(Py_None); \ + } \ +} while (0) + +/* a region with the MRO fixed. No need to juggle reference counts */ +#define START_FIX_MRO(mro, type) { \ + int mro_fixed = 0; \ + if (PyTuple_GET_ITEM((mro), 0) == Py_None) { \ + PyTuple_SET_ITEM((mro), 0, (PyObject*)(type)); \ + mro_fixed = 1; \ + } + +#define END_FIX_MRO(mro) \ + if (mro_fixed) \ + PyTuple_SET_ITEM((mro), 0, Py_None); \ +} + _Py_IDENTIFIER(__class__); _Py_IDENTIFIER(__dict__); _Py_IDENTIFIER(__doc__); @@ -208,7 +248,6 @@ {"__base__", T_OBJECT, offsetof(PyTypeObject, tp_base), READONLY}, {"__dictoffset__", T_LONG, offsetof(PyTypeObject, tp_dictoffset), READONLY}, - {"__mro__", T_OBJECT, offsetof(PyTypeObject, tp_mro), READONLY}, {0} }; @@ -640,6 +679,30 @@ return _PyDict_SetItemId(type->tp_dict, &PyId___doc__, value); } + +static PyObject * +type_get_mro(PyTypeObject *type) +{ + PyObject *result, *item; + int i; + if (type->tp_mro == NULL) + return NULL; + /* return a new tuple with the class as the first entry. + * manually duplicate tuple. Types may not be ready + * so the abstract helpers in PyObject_ may not work + */ + result = PyTuple_New(PyTuple_GET_SIZE(type->tp_mro)); + if (result) { + for (i=0; itp_mro); i++) { + item = PyTuple_GET_ITEM(type->tp_mro, i); + Py_XINCREF(item); + PyTuple_SET_ITEM(result, i, item); + } + FIX_MRO(type, result); + } + return result; +} + static PyObject * type___instancecheck__(PyObject *type, PyObject *inst) { @@ -677,6 +740,7 @@ (setter)type_set_abstractmethods, NULL}, {"__dict__", (getter)type_dict, NULL, NULL}, {"__doc__", (getter)type_get_doc, (setter)type_set_doc, NULL}, + {"__mro__", (getter)type_get_mro, NULL, NULL}, {0} }; @@ -1147,6 +1211,11 @@ Py_ssize_t i, n; assert(PyTuple_Check(mro)); n = PyTuple_GET_SIZE(mro); + /* special case to deal with the "nerfed" mro that contains a Py_None instead of + * the self type + */ + if (a == b) + return 1; for (i = 0; i < n; i++) { if (PyTuple_GET_ITEM(mro, i) == (PyObject *)b) return 1; @@ -1533,6 +1602,11 @@ Py_DECREF(to_merge); return NULL; } + if (PyList_GET_ITEM(parentMRO, 0) == Py_None) { + /* undo the nerf. Same as FIX_MRO but for a list */ + Py_INCREF(base); + PyList_SetItem(parentMRO, 0, base); + } PyList_SET_ITEM(to_merge, i, parentMRO); } @@ -1630,6 +1704,10 @@ type->tp_mro = tuple; type_mro_modified(type, type->tp_mro); + + /* nerf the new mro, so that it does not include the cyclic reference to its type */ + NERF_MRO(type); + /* corner case: the super class might have been hidden from the custom MRO */ type_mro_modified(type, type->tp_bases); @@ -2549,6 +2627,7 @@ /* keep a strong reference to mro because type->tp_mro can be replaced during PyDict_GetItem(dict, name) */ Py_INCREF(mro); + START_FIX_MRO(mro, type) assert(PyTuple_Check(mro)); n = PyTuple_GET_SIZE(mro); for (i = 0; i < n; i++) { @@ -2560,6 +2639,7 @@ if (res != NULL) break; } + END_FIX_MRO(mro) Py_DECREF(mro); if (MCACHE_CACHEABLE_NAME(name) && assign_version_tag(type)) { @@ -6323,6 +6403,7 @@ PyTypeObject *starttype; descrgetfunc f; Py_ssize_t i, n; + int found = 0; starttype = su->obj_type; mro = starttype->tp_mro; @@ -6333,6 +6414,7 @@ assert(PyTuple_Check(mro)); n = PyTuple_GET_SIZE(mro); } + START_FIX_MRO(mro, starttype) for (i = 0; i < n; i++) { if ((PyObject *)(su->type) == PyTuple_GET_ITEM(mro, i)) break; @@ -6366,11 +6448,14 @@ Py_DECREF(res); res = tmp; } - Py_DECREF(mro); - return res; + found = 1; + break; } } + END_FIX_MRO(mro) Py_DECREF(mro); + if (found) + return res; } return PyObject_GenericGetAttr(self, name); } diff --git a/Python/pythonrun.c b/Python/pythonrun.c --- a/Python/pythonrun.c +++ b/Python/pythonrun.c @@ -614,10 +614,6 @@ _PyGILState_Fini(); #endif /* WITH_THREAD */ - /* Delete current thread */ - PyThreadState_Swap(NULL); - PyInterpreterState_Delete(interp); - /* Sundry finalizers */ PyMethod_Fini(); PyFrame_Fini(); @@ -632,6 +628,10 @@ PyDict_Fini(); PySlice_Fini(); + /* Delete current thread */ + PyThreadState_Swap(NULL); + PyInterpreterState_Delete(interp); + /* Cleanup Unicode implementation */ _PyUnicode_Fini();

AltStyle によって変換されたページ (->オリジナル) /