@@ -1021,6 +1021,38 @@ type_call(PyTypeObject *type, PyObject *args, PyObject *kwds)
1021
1021
return obj ;
1022
1022
}
1023
1023
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
+
1024
1056
PyObject *
1025
1057
PyType_GenericAlloc (PyTypeObject * type , Py_ssize_t nitems )
1026
1058
{
@@ -2853,6 +2885,36 @@ static const short slotoffsets[] = {
2853
2885
#include "typeslots.inc"
2854
2886
};
2855
2887
2888
+ static int
2889
+ PyType_FromSpec_tp_traverse (PyObject * self , visitproc visit , void * arg )
2890
+ {
2891
+ PyTypeObject * parent = Py_TYPE (self );
2892
+
2893
+ // Only a instance of a type that is directly created by
2894
+ // PyType_FromSpec (not subclasses) must visit its parent.
2895
+ if (parent -> tp_traverse == PyType_FromSpec_tp_traverse ) {
2896
+ Py_VISIT (parent );
2897
+ }
2898
+
2899
+ // Search for the original type that was created using PyType_FromSpec
2900
+ PyTypeObject * base ;
2901
+ base = parent ;
2902
+ while (base -> tp_traverse != PyType_FromSpec_tp_traverse ) {
2903
+ base = base -> tp_base ;
2904
+ assert (base );
2905
+ }
2906
+
2907
+ // Extract the user defined traverse function that we placed at the end
2908
+ // of the type and call it.
2909
+ size_t size = Py_SIZE (base );
2910
+ size_t _offset = _PyObject_VAR_SIZE (& PyType_Type , size + 1 );
2911
+ traverseproc fun = * (traverseproc * )((char * )base + _offset );
2912
+ if (fun == NULL ) {
2913
+ return 0 ;
2914
+ }
2915
+ return fun (self , visit , arg );
2916
+ }
2917
+
2856
2918
PyObject *
2857
2919
PyType_FromSpecWithBases (PyType_Spec * spec , PyObject * bases )
2858
2920
{
@@ -2886,7 +2948,7 @@ PyType_FromSpecWithBases(PyType_Spec *spec, PyObject *bases)
2886
2948
}
2887
2949
}
2888
2950
2889
- res = (PyHeapTypeObject * )PyType_GenericAlloc (& PyType_Type , nmembers );
2951
+ res = (PyHeapTypeObject * )PyType_FromSpec_Alloc (& PyType_Type , nmembers );
2890
2952
if (res == NULL )
2891
2953
return NULL ;
2892
2954
res_start = (char * )res ;
@@ -2991,6 +3053,26 @@ PyType_FromSpecWithBases(PyType_Spec *spec, PyObject *bases)
2991
3053
memcpy (PyHeapType_GET_MEMBERS (res ), slot -> pfunc , len );
2992
3054
type -> tp_members = PyHeapType_GET_MEMBERS (res );
2993
3055
}
3056
+ else if (slot -> slot == Py_tp_traverse ) {
3057
+
3058
+ /* Types created by PyType_FromSpec own a strong reference to their
3059
+ * type, but this was added in Python 3.8. The tp_traverse function
3060
+ * needs to call Py_VISIT on the type but all existing traverse
3061
+ * functions cannot be updated (especially the ones from existing user
3062
+ * functions) so we need to provide a tp_traverse that manually calls
3063
+ * Py_VISIT(Py_TYPE(self)) and then call the provided tp_traverse. In
3064
+ * this way, user functions do not need to be updated, preserve
3065
+ * backwards compatibility.
3066
+ *
3067
+ * We store the user-provided traverse function at the end of the type
3068
+ * (we have allocated space for it) so we can call it from our
3069
+ * PyType_FromSpec_tp_traverse wrapper. */
3070
+
3071
+ type -> tp_traverse = PyType_FromSpec_tp_traverse ;
3072
+ size_t _offset = _PyObject_VAR_SIZE (& PyType_Type , nmembers + 1 );
3073
+ traverseproc * user_traverse = (traverseproc * )((char * )type + _offset );
3074
+ * user_traverse = slot -> pfunc ;
3075
+ }
2994
3076
else {
2995
3077
/* Copy other slots directly */
2996
3078
* (void * * )(res_start + slotoffsets [slot -> slot ]) = slot -> pfunc ;
0 commit comments