From ad61063ee360b1641099f00fac1bcacfe5e20217 Mon Sep 17 00:00:00 2001 From: Sam Gross Date: Thu, 16 Jan 2025 20:37:39 +0000 Subject: [PATCH] Add regression test --- Lib/test/test_capi/test_type.py | 7 ++++ Modules/_testcapimodule.c | 64 +++++++++++++++++++++++++++++++++ 2 files changed, 71 insertions(+) diff --git a/Lib/test/test_capi/test_type.py b/Lib/test/test_capi/test_type.py index 92d056e802eeed..ffcaae73bca236 100644 --- a/Lib/test/test_capi/test_type.py +++ b/Lib/test/test_capi/test_type.py @@ -67,3 +67,10 @@ class FreezeThis(metaclass=Meta): Base.value = 3 type_freeze(FreezeThis) self.assertEqual(FreezeThis.value, 2) + + def test_manual_heap_type(self): + # gh-128923: test that a manually allocated and initailized heap type + # works correctly + ManualHeapType = _testcapi.ManualHeapType + for i in range(100): + self.assertIsInstance(ManualHeapType(), ManualHeapType) diff --git a/Modules/_testcapimodule.c b/Modules/_testcapimodule.c index 7d304add5999d1..c0f7b6ec1012e0 100644 --- a/Modules/_testcapimodule.c +++ b/Modules/_testcapimodule.c @@ -4149,6 +4149,61 @@ static PyTypeObject ContainerNoGC_type = { .tp_new = ContainerNoGC_new, }; +/* Manually allocated heap type */ + +typedef struct { + PyObject_HEAD + PyObject *dict; +} ManualHeapType; + +static int +ManualHeapType_traverse(PyObject *self, visitproc visit, void *arg) +{ + ManualHeapType *mht = (ManualHeapType *)self; + Py_VISIT(mht->dict); + return 0; +} + +static void +ManualHeapType_dealloc(PyObject *self) +{ + ManualHeapType *mht = (ManualHeapType *)self; + PyObject_GC_UnTrack(self); + Py_XDECREF(mht->dict); + PyTypeObject *type = Py_TYPE(self); + Py_TYPE(self)->tp_free(self); + Py_DECREF(type); +} + +static PyObject * +create_manual_heap_type(void) +{ + // gh-128923: Ensure that a heap type allocated through PyType_Type.tp_alloc + // with minimal initialization works correctly. + PyHeapTypeObject *heap_type = (PyHeapTypeObject *)PyType_Type.tp_alloc(&PyType_Type, 0); + if (heap_type == NULL) { + return NULL; + } + PyTypeObject* type = &heap_type->ht_type; + type->tp_basicsize = sizeof(ManualHeapType); + type->tp_flags = Py_TPFLAGS_DEFAULT | Py_TPFLAGS_HEAPTYPE | Py_TPFLAGS_HAVE_GC; + type->tp_new = PyType_GenericNew; + type->tp_name = "ManualHeapType"; + type->tp_dictoffset = offsetof(ManualHeapType, dict); + type->tp_traverse = ManualHeapType_traverse; + type->tp_dealloc = ManualHeapType_dealloc; + heap_type->ht_name = PyUnicode_FromString(type->tp_name); + if (!heap_type->ht_name) { + Py_DECREF(type); + return NULL; + } + heap_type->ht_qualname = Py_NewRef(heap_type->ht_name); + if (PyType_Ready(type) < 0) { + Py_DECREF(type); + return NULL; + } + return (PyObject *)type; +} static struct PyModuleDef _testcapimodule = { PyModuleDef_HEAD_INIT, @@ -4283,6 +4338,15 @@ PyInit__testcapi(void) (PyObject *) &ContainerNoGC_type) < 0) return NULL; + PyObject *manual_heap_type = create_manual_heap_type(); + if (manual_heap_type == NULL) { + return NULL; + } + if (PyModule_AddObject(m, "ManualHeapType", manual_heap_type) < 0) { + return NULL; + } + + /* Include tests from the _testcapi/ directory */ if (_PyTestCapi_Init_Vectorcall(m) < 0) { return NULL;