@@ -1021,6 +1021,38 @@ type_call(PyTypeObject *type, PyObject *args, PyObject *kwds)
10211021 return obj ;
10221022}
10231023
1024+ PyObject *
1025+ PyType_FromSpec_Alloc (PyTypeObject * type , Py_ssize_t nitems )
1026+ {
1027+ PyObject * obj ;
1028+ const size_t size = _Py_SIZE_ROUND_UP (
1029+ _PyObject_VAR_SIZE (type , nitems + 1 ) + sizeof (traverseproc ),
1030+ SIZEOF_VOID_P );
1031+ /* note that we need to add one, for the sentinel and space for the
1032+ provided tp-traverse: See bpo-40217 for more details */
1033+
1034+ if (PyType_IS_GC (type ))
1035+ obj = _PyObject_GC_Malloc (size );
1036+ else
1037+ obj = (PyObject * )PyObject_MALLOC (size );
1038+
1039+ if (obj == NULL )
1040+ return PyErr_NoMemory ();
1041+
1042+ obj = obj ;
1043+
1044+ memset (obj , '\0' , size );
1045+
1046+ if (type -> tp_itemsize == 0 )
1047+ (void )PyObject_INIT (obj , type );
1048+ else
1049+ (void ) PyObject_INIT_VAR ((PyVarObject * )obj , type , nitems );
1050+
1051+ if (PyType_IS_GC (type ))
1052+ _PyObject_GC_TRACK (obj );
1053+ return obj ;
1054+ }
1055+
10241056PyObject *
10251057PyType_GenericAlloc (PyTypeObject * type , Py_ssize_t nitems )
10261058{
@@ -2846,6 +2878,36 @@ static const short slotoffsets[] = {
28462878#include "typeslots.inc"
28472879};
28482880
2881+ static int
2882+ PyType_FromSpec_tp_traverse (PyObject * self , visitproc visit , void * arg )
2883+ {
2884+ PyTypeObject * parent = Py_TYPE (self );
2885+
2886+ // Only a instance of a type that is directly created by
2887+ // PyType_FromSpec (not subclasses) must visit its parent.
2888+ if (parent -> tp_traverse == PyType_FromSpec_tp_traverse ) {
2889+ Py_VISIT (parent );
2890+ }
2891+
2892+ // Search for the original type that was created using PyType_FromSpec
2893+ PyTypeObject * base ;
2894+ base = parent ;
2895+ while (base -> tp_traverse != PyType_FromSpec_tp_traverse ) {
2896+ base = base -> tp_base ;
2897+ assert (base );
2898+ }
2899+
2900+ // Extract the user defined traverse function that we placed at the end
2901+ // of the type and call it.
2902+ size_t size = Py_SIZE (base );
2903+ size_t _offset = _PyObject_VAR_SIZE (& PyType_Type , size + 1 );
2904+ traverseproc fun = * (traverseproc * )((char * )base + _offset );
2905+ if (fun == NULL ) {
2906+ return 0 ;
2907+ }
2908+ return fun (self , visit , arg );
2909+ }
2910+
28492911PyObject *
28502912PyType_FromSpecWithBases (PyType_Spec * spec , PyObject * bases )
28512913{
@@ -2880,7 +2942,7 @@ PyType_FromSpecWithBases(PyType_Spec *spec, PyObject *bases)
28802942 }
28812943 }
28822944
2883- res = (PyHeapTypeObject * )PyType_GenericAlloc (& PyType_Type , nmembers );
2945+ res = (PyHeapTypeObject * )PyType_FromSpec_Alloc (& PyType_Type , nmembers );
28842946 if (res == NULL )
28852947 return NULL ;
28862948 res_start = (char * )res ;
@@ -2985,6 +3047,26 @@ PyType_FromSpecWithBases(PyType_Spec *spec, PyObject *bases)
29853047 memcpy (PyHeapType_GET_MEMBERS (res ), slot -> pfunc , len );
29863048 type -> tp_members = PyHeapType_GET_MEMBERS (res );
29873049 }
3050+ else if (slot -> slot == Py_tp_traverse ) {
3051+
3052+ /* Types created by PyType_FromSpec own a strong reference to their
3053+ * type, but this was added in Python 3.8. The tp_traverse function
3054+ * needs to call Py_VISIT on the type but all existing traverse
3055+ * functions cannot be updated (especially the ones from existing user
3056+ * functions) so we need to provide a tp_traverse that manually calls
3057+ * Py_VISIT(Py_TYPE(self)) and then call the provided tp_traverse. In
3058+ * this way, user functions do not need to be updated, preserve
3059+ * backwards compatibility.
3060+ *
3061+ * We store the user-provided traverse function at the end of the type
3062+ * (we have allocated space for it) so we can call it from our
3063+ * PyType_FromSpec_tp_traverse wrapper. */
3064+
3065+ type -> tp_traverse = PyType_FromSpec_tp_traverse ;
3066+ size_t _offset = _PyObject_VAR_SIZE (& PyType_Type , nmembers + 1 );
3067+ traverseproc * user_traverse = (traverseproc * )((char * )type + _offset );
3068+ * user_traverse = slot -> pfunc ;
3069+ }
29883070 else {
29893071 /* Copy other slots directly */
29903072 * (void * * )(res_start + slotoffsets [slot -> slot ]) = slot -> pfunc ;
0 commit comments