@@ -1163,6 +1163,126 @@ test_get_type_name(PyObject *self, PyObject *Py_UNUSED(ignored))
11631163}
11641164
11651165
1166+ static PyObject *
1167+ simple_str (PyObject * self ) {
1168+ return PyUnicode_FromString ("<test>" );
1169+ }
1170+
1171+
1172+ static PyObject *
1173+ test_type_from_ephemeral_spec (PyObject * self , PyObject * Py_UNUSED (ignored ))
1174+ {
1175+ // Test that a heap type can be created from a spec that's later deleted
1176+ // (along with all its contents).
1177+ // All necessary data must be copied and held by the class
1178+ PyType_Spec * spec = NULL ;
1179+ char * name = NULL ;
1180+ char * doc = NULL ;
1181+ PyType_Slot * slots = NULL ;
1182+ PyObject * class = NULL ;
1183+ PyObject * instance = NULL ;
1184+ PyObject * obj = NULL ;
1185+ PyObject * result = NULL ;
1186+
1187+ /* create a spec (and all its contents) on the heap */
1188+
1189+ const char NAME [] = "testcapi._Test" ;
1190+ const char DOC [] = "a test class" ;
1191+
1192+ spec = PyMem_New (PyType_Spec , 1 );
1193+ if (spec == NULL ) {
1194+ PyErr_NoMemory ();
1195+ goto finally ;
1196+ }
1197+ name = PyMem_New (char , sizeof (NAME ));
1198+ if (name == NULL ) {
1199+ PyErr_NoMemory ();
1200+ goto finally ;
1201+ }
1202+ memcpy (name , NAME , sizeof (NAME ));
1203+
1204+ doc = PyMem_New (char , sizeof (DOC ));
1205+ if (name == NULL ) {
1206+ PyErr_NoMemory ();
1207+ goto finally ;
1208+ }
1209+ memcpy (doc , DOC , sizeof (DOC ));
1210+
1211+ spec -> name = name ;
1212+ spec -> basicsize = sizeof (PyObject );
1213+ spec -> itemsize = 0 ;
1214+ spec -> flags = Py_TPFLAGS_DEFAULT ;
1215+ slots = PyMem_New (PyType_Slot , 3 );
1216+ if (slots == NULL ) {
1217+ PyErr_NoMemory ();
1218+ goto finally ;
1219+ }
1220+ slots [0 ].slot = Py_tp_str ;
1221+ slots [0 ].pfunc = simple_str ;
1222+ slots [1 ].slot = Py_tp_doc ;
1223+ slots [1 ].pfunc = doc ;
1224+ slots [2 ].slot = 0 ;
1225+ slots [2 ].pfunc = NULL ;
1226+ spec -> slots = slots ;
1227+
1228+ /* create the class */
1229+
1230+ class = PyType_FromSpec (spec );
1231+ if (class == NULL ) {
1232+ goto finally ;
1233+ }
1234+
1235+ /* deallocate the spec (and all contents) */
1236+
1237+ // (Explicitly ovewrite memory before freeing,
1238+ // so bugs show themselves even without the debug allocator's help.)
1239+ memset (spec , 0xdd , sizeof (PyType_Spec ));
1240+ PyMem_Del (spec );
1241+ spec = NULL ;
1242+ memset (name , 0xdd , sizeof (NAME ));
1243+ PyMem_Del (name );
1244+ name = NULL ;
1245+ memset (doc , 0xdd , sizeof (DOC ));
1246+ PyMem_Del (doc );
1247+ doc = NULL ;
1248+ memset (slots , 0xdd , 3 * sizeof (PyType_Slot ));
1249+ PyMem_Del (slots );
1250+ slots = NULL ;
1251+
1252+ /* check that everything works */
1253+
1254+ PyTypeObject * class_tp = (PyTypeObject * )class ;
1255+ PyHeapTypeObject * class_ht = (PyHeapTypeObject * )class ;
1256+ assert (strcmp (class_tp -> tp_name , "testcapi._Test" ) == 0 );
1257+ assert (strcmp (PyUnicode_AsUTF8 (class_ht -> ht_name ), "_Test" ) == 0 );
1258+ assert (strcmp (PyUnicode_AsUTF8 (class_ht -> ht_qualname ), "_Test" ) == 0 );
1259+ assert (strcmp (class_tp -> tp_doc , "a test class" ) == 0 );
1260+
1261+ // call and check __str__
1262+ instance = PyObject_CallNoArgs (class );
1263+ if (instance == NULL ) {
1264+ goto finally ;
1265+ }
1266+ obj = PyObject_Str (instance );
1267+ if (obj == NULL ) {
1268+ goto finally ;
1269+ }
1270+ assert (strcmp (PyUnicode_AsUTF8 (obj ), "<test>" ) == 0 );
1271+ Py_CLEAR (obj );
1272+
1273+ result = Py_NewRef (Py_None );
1274+ finally :
1275+ PyMem_Del (spec );
1276+ PyMem_Del (name );
1277+ PyMem_Del (doc );
1278+ PyMem_Del (slots );
1279+ Py_XDECREF (class );
1280+ Py_XDECREF (instance );
1281+ Py_XDECREF (obj );
1282+ return result ;
1283+ }
1284+
1285+
11661286static PyObject *
11671287test_get_type_qualname (PyObject * self , PyObject * Py_UNUSED (ignored ))
11681288{
@@ -5804,6 +5924,7 @@ static PyMethodDef TestMethods[] = {
58045924 {"test_get_statictype_slots" , test_get_statictype_slots , METH_NOARGS },
58055925 {"test_get_type_name" , test_get_type_name , METH_NOARGS },
58065926 {"test_get_type_qualname" , test_get_type_qualname , METH_NOARGS },
5927+ {"test_type_from_ephemeral_spec" , test_type_from_ephemeral_spec , METH_NOARGS },
58075928 {"get_kwargs" , (PyCFunction )(void (* )(void ))get_kwargs ,
58085929 METH_VARARGS |METH_KEYWORDS },
58095930 {"getargs_tuple" , getargs_tuple , METH_VARARGS },
0 commit comments