mirror of
				https://github.com/python/cpython.git
				synced 2025-11-03 19:34:08 +00:00 
			
		
		
		
	gh-93649: Split gc- and allocation tests from _testcapimodule.c (GH-104403)
This commit is contained in:
		
							parent
							
								
									b2c1b4da19
								
							
						
					
					
						commit
						19ee53d52e
					
				
					 6 changed files with 353 additions and 327 deletions
				
			
		| 
						 | 
				
			
			@ -169,7 +169,7 @@
 | 
			
		|||
@MODULE__XXTESTFUZZ_TRUE@_xxtestfuzz _xxtestfuzz/_xxtestfuzz.c _xxtestfuzz/fuzzer.c
 | 
			
		||||
@MODULE__TESTBUFFER_TRUE@_testbuffer _testbuffer.c
 | 
			
		||||
@MODULE__TESTINTERNALCAPI_TRUE@_testinternalcapi _testinternalcapi.c
 | 
			
		||||
@MODULE__TESTCAPI_TRUE@_testcapi _testcapimodule.c _testcapi/vectorcall.c _testcapi/vectorcall_limited.c _testcapi/heaptype.c _testcapi/unicode.c _testcapi/getargs.c _testcapi/pytime.c _testcapi/datetime.c _testcapi/docstring.c _testcapi/mem.c _testcapi/watchers.c _testcapi/long.c _testcapi/float.c _testcapi/structmember.c _testcapi/exceptions.c _testcapi/code.c _testcapi/buffer.c _testcapi/pyos.c _testcapi/immortal.c _testcapi/heaptype_relative.c
 | 
			
		||||
@MODULE__TESTCAPI_TRUE@_testcapi _testcapimodule.c _testcapi/vectorcall.c _testcapi/vectorcall_limited.c _testcapi/heaptype.c _testcapi/unicode.c _testcapi/getargs.c _testcapi/pytime.c _testcapi/datetime.c _testcapi/docstring.c _testcapi/mem.c _testcapi/watchers.c _testcapi/long.c _testcapi/float.c _testcapi/structmember.c _testcapi/exceptions.c _testcapi/code.c _testcapi/buffer.c _testcapi/pyos.c _testcapi/immortal.c _testcapi/heaptype_relative.c _testcapi/gc.c
 | 
			
		||||
@MODULE__TESTCLINIC_TRUE@_testclinic _testclinic.c
 | 
			
		||||
 | 
			
		||||
# Some testing modules MUST be built as shared libraries.
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
							
								
								
									
										344
									
								
								Modules/_testcapi/gc.c
									
										
									
									
									
										Normal file
									
								
							
							
						
						
									
										344
									
								
								Modules/_testcapi/gc.c
									
										
									
									
									
										Normal file
									
								
							| 
						 | 
				
			
			@ -0,0 +1,344 @@
 | 
			
		|||
#include "parts.h"
 | 
			
		||||
 | 
			
		||||
static PyObject*
 | 
			
		||||
test_gc_control(PyObject *self, PyObject *Py_UNUSED(ignored))
 | 
			
		||||
{
 | 
			
		||||
    int orig_enabled = PyGC_IsEnabled();
 | 
			
		||||
    const char* msg = "ok";
 | 
			
		||||
    int old_state;
 | 
			
		||||
 | 
			
		||||
    old_state = PyGC_Enable();
 | 
			
		||||
    msg = "Enable(1)";
 | 
			
		||||
    if (old_state != orig_enabled) {
 | 
			
		||||
        goto failed;
 | 
			
		||||
    }
 | 
			
		||||
    msg = "IsEnabled(1)";
 | 
			
		||||
    if (!PyGC_IsEnabled()) {
 | 
			
		||||
        goto failed;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    old_state = PyGC_Disable();
 | 
			
		||||
    msg = "disable(2)";
 | 
			
		||||
    if (!old_state) {
 | 
			
		||||
        goto failed;
 | 
			
		||||
    }
 | 
			
		||||
    msg = "IsEnabled(2)";
 | 
			
		||||
    if (PyGC_IsEnabled()) {
 | 
			
		||||
        goto failed;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    old_state = PyGC_Enable();
 | 
			
		||||
    msg = "enable(3)";
 | 
			
		||||
    if (old_state) {
 | 
			
		||||
        goto failed;
 | 
			
		||||
    }
 | 
			
		||||
    msg = "IsEnabled(3)";
 | 
			
		||||
    if (!PyGC_IsEnabled()) {
 | 
			
		||||
        goto failed;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    if (!orig_enabled) {
 | 
			
		||||
        old_state = PyGC_Disable();
 | 
			
		||||
        msg = "disable(4)";
 | 
			
		||||
        if (old_state) {
 | 
			
		||||
            goto failed;
 | 
			
		||||
        }
 | 
			
		||||
        msg = "IsEnabled(4)";
 | 
			
		||||
        if (PyGC_IsEnabled()) {
 | 
			
		||||
            goto failed;
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    Py_RETURN_NONE;
 | 
			
		||||
 | 
			
		||||
failed:
 | 
			
		||||
    /* Try to clean up if we can. */
 | 
			
		||||
    if (orig_enabled) {
 | 
			
		||||
        PyGC_Enable();
 | 
			
		||||
    } else {
 | 
			
		||||
        PyGC_Disable();
 | 
			
		||||
    }
 | 
			
		||||
    PyErr_Format(PyExc_ValueError, "GC control failed in %s", msg);
 | 
			
		||||
    return NULL;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
static PyObject *
 | 
			
		||||
without_gc(PyObject *Py_UNUSED(self), PyObject *obj)
 | 
			
		||||
{
 | 
			
		||||
    PyTypeObject *tp = (PyTypeObject*)obj;
 | 
			
		||||
    if (!PyType_Check(obj) || !PyType_HasFeature(tp, Py_TPFLAGS_HEAPTYPE)) {
 | 
			
		||||
        return PyErr_Format(PyExc_TypeError, "heap type expected, got %R", obj);
 | 
			
		||||
    }
 | 
			
		||||
    if (PyType_IS_GC(tp)) {
 | 
			
		||||
        // Don't try this at home, kids:
 | 
			
		||||
        tp->tp_flags -= Py_TPFLAGS_HAVE_GC;
 | 
			
		||||
        tp->tp_free = PyObject_Del;
 | 
			
		||||
        tp->tp_traverse = NULL;
 | 
			
		||||
        tp->tp_clear = NULL;
 | 
			
		||||
    }
 | 
			
		||||
    assert(!PyType_IS_GC(tp));
 | 
			
		||||
    return Py_NewRef(obj);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
static void
 | 
			
		||||
slot_tp_del(PyObject *self)
 | 
			
		||||
{
 | 
			
		||||
    PyObject *del, *res;
 | 
			
		||||
 | 
			
		||||
    /* Temporarily resurrect the object. */
 | 
			
		||||
    assert(Py_REFCNT(self) == 0);
 | 
			
		||||
    Py_SET_REFCNT(self, 1);
 | 
			
		||||
 | 
			
		||||
    /* Save the current exception, if any. */
 | 
			
		||||
    PyObject *exc = PyErr_GetRaisedException();
 | 
			
		||||
 | 
			
		||||
    PyObject *tp_del = PyUnicode_InternFromString("__tp_del__");
 | 
			
		||||
    if (tp_del == NULL) {
 | 
			
		||||
        PyErr_WriteUnraisable(NULL);
 | 
			
		||||
        PyErr_SetRaisedException(exc);
 | 
			
		||||
        return;
 | 
			
		||||
    }
 | 
			
		||||
    /* Execute __del__ method, if any. */
 | 
			
		||||
    del = _PyType_Lookup(Py_TYPE(self), tp_del);
 | 
			
		||||
    Py_DECREF(tp_del);
 | 
			
		||||
    if (del != NULL) {
 | 
			
		||||
        res = PyObject_CallOneArg(del, self);
 | 
			
		||||
        if (res == NULL)
 | 
			
		||||
            PyErr_WriteUnraisable(del);
 | 
			
		||||
        else
 | 
			
		||||
            Py_DECREF(res);
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    /* Restore the saved exception. */
 | 
			
		||||
    PyErr_SetRaisedException(exc);
 | 
			
		||||
 | 
			
		||||
    /* Undo the temporary resurrection; can't use DECREF here, it would
 | 
			
		||||
     * cause a recursive call.
 | 
			
		||||
     */
 | 
			
		||||
    assert(Py_REFCNT(self) > 0);
 | 
			
		||||
    Py_SET_REFCNT(self, Py_REFCNT(self) - 1);
 | 
			
		||||
    if (Py_REFCNT(self) == 0) {
 | 
			
		||||
        /* this is the normal path out */
 | 
			
		||||
        return;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    /* __del__ resurrected it!  Make it look like the original Py_DECREF
 | 
			
		||||
     * never happened.
 | 
			
		||||
     */
 | 
			
		||||
    {
 | 
			
		||||
        Py_ssize_t refcnt = Py_REFCNT(self);
 | 
			
		||||
        _Py_NewReferenceNoTotal(self);
 | 
			
		||||
        Py_SET_REFCNT(self, refcnt);
 | 
			
		||||
    }
 | 
			
		||||
    assert(!PyType_IS_GC(Py_TYPE(self)) || PyObject_GC_IsTracked(self));
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
static PyObject *
 | 
			
		||||
with_tp_del(PyObject *self, PyObject *args)
 | 
			
		||||
{
 | 
			
		||||
    PyObject *obj;
 | 
			
		||||
    PyTypeObject *tp;
 | 
			
		||||
 | 
			
		||||
    if (!PyArg_ParseTuple(args, "O:with_tp_del", &obj))
 | 
			
		||||
        return NULL;
 | 
			
		||||
    tp = (PyTypeObject *) obj;
 | 
			
		||||
    if (!PyType_Check(obj) || !PyType_HasFeature(tp, Py_TPFLAGS_HEAPTYPE)) {
 | 
			
		||||
        PyErr_Format(PyExc_TypeError,
 | 
			
		||||
                     "heap type expected, got %R", obj);
 | 
			
		||||
        return NULL;
 | 
			
		||||
    }
 | 
			
		||||
    tp->tp_del = slot_tp_del;
 | 
			
		||||
    return Py_NewRef(obj);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
struct gc_visit_state_basic {
 | 
			
		||||
    PyObject *target;
 | 
			
		||||
    int found;
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
static int
 | 
			
		||||
gc_visit_callback_basic(PyObject *obj, void *arg)
 | 
			
		||||
{
 | 
			
		||||
    struct gc_visit_state_basic *state = (struct gc_visit_state_basic *)arg;
 | 
			
		||||
    if (obj == state->target) {
 | 
			
		||||
        state->found = 1;
 | 
			
		||||
        return 0;
 | 
			
		||||
    }
 | 
			
		||||
    return 1;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
static PyObject *
 | 
			
		||||
test_gc_visit_objects_basic(PyObject *Py_UNUSED(self),
 | 
			
		||||
                            PyObject *Py_UNUSED(ignored))
 | 
			
		||||
{
 | 
			
		||||
    PyObject *obj;
 | 
			
		||||
    struct gc_visit_state_basic state;
 | 
			
		||||
 | 
			
		||||
    obj = PyList_New(0);
 | 
			
		||||
    if (obj == NULL) {
 | 
			
		||||
        return NULL;
 | 
			
		||||
    }
 | 
			
		||||
    state.target = obj;
 | 
			
		||||
    state.found = 0;
 | 
			
		||||
 | 
			
		||||
    PyUnstable_GC_VisitObjects(gc_visit_callback_basic, &state);
 | 
			
		||||
    Py_DECREF(obj);
 | 
			
		||||
    if (!state.found) {
 | 
			
		||||
        PyErr_SetString(
 | 
			
		||||
             PyExc_AssertionError,
 | 
			
		||||
             "test_gc_visit_objects_basic: Didn't find live list");
 | 
			
		||||
         return NULL;
 | 
			
		||||
    }
 | 
			
		||||
    Py_RETURN_NONE;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
static int
 | 
			
		||||
gc_visit_callback_exit_early(PyObject *obj, void *arg)
 | 
			
		||||
 {
 | 
			
		||||
    int *visited_i = (int *)arg;
 | 
			
		||||
    (*visited_i)++;
 | 
			
		||||
    if (*visited_i == 2) {
 | 
			
		||||
        return 0;
 | 
			
		||||
    }
 | 
			
		||||
    return 1;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
static PyObject *
 | 
			
		||||
test_gc_visit_objects_exit_early(PyObject *Py_UNUSED(self),
 | 
			
		||||
                                 PyObject *Py_UNUSED(ignored))
 | 
			
		||||
{
 | 
			
		||||
    int visited_i = 0;
 | 
			
		||||
    PyUnstable_GC_VisitObjects(gc_visit_callback_exit_early, &visited_i);
 | 
			
		||||
    if (visited_i != 2) {
 | 
			
		||||
        PyErr_SetString(
 | 
			
		||||
            PyExc_AssertionError,
 | 
			
		||||
            "test_gc_visit_objects_exit_early: did not exit when expected");
 | 
			
		||||
    }
 | 
			
		||||
    Py_RETURN_NONE;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
typedef struct {
 | 
			
		||||
    PyObject_HEAD
 | 
			
		||||
} ObjExtraData;
 | 
			
		||||
 | 
			
		||||
static PyObject *
 | 
			
		||||
obj_extra_data_new(PyTypeObject *type, PyObject *args, PyObject *kwds)
 | 
			
		||||
{
 | 
			
		||||
    size_t extra_size = sizeof(PyObject *);
 | 
			
		||||
    PyObject *obj = PyUnstable_Object_GC_NewWithExtraData(type, extra_size);
 | 
			
		||||
    if (obj == NULL) {
 | 
			
		||||
        return PyErr_NoMemory();
 | 
			
		||||
    }
 | 
			
		||||
    PyObject_GC_Track(obj);
 | 
			
		||||
    return obj;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
static PyObject **
 | 
			
		||||
obj_extra_data_get_extra_storage(PyObject *self)
 | 
			
		||||
{
 | 
			
		||||
    return (PyObject **)((char *)self + Py_TYPE(self)->tp_basicsize);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
static PyObject *
 | 
			
		||||
obj_extra_data_get(PyObject *self, void *Py_UNUSED(ignored))
 | 
			
		||||
{
 | 
			
		||||
    PyObject **extra_storage = obj_extra_data_get_extra_storage(self);
 | 
			
		||||
    PyObject *value = *extra_storage;
 | 
			
		||||
    if (!value) {
 | 
			
		||||
        Py_RETURN_NONE;
 | 
			
		||||
    }
 | 
			
		||||
    return Py_NewRef(value);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
static int
 | 
			
		||||
obj_extra_data_set(PyObject *self, PyObject *newval, void *Py_UNUSED(ignored))
 | 
			
		||||
{
 | 
			
		||||
    PyObject **extra_storage = obj_extra_data_get_extra_storage(self);
 | 
			
		||||
    Py_CLEAR(*extra_storage);
 | 
			
		||||
    if (newval) {
 | 
			
		||||
        *extra_storage = Py_NewRef(newval);
 | 
			
		||||
    }
 | 
			
		||||
    return 0;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
static PyGetSetDef obj_extra_data_getset[] = {
 | 
			
		||||
    {"extra", (getter)obj_extra_data_get, (setter)obj_extra_data_set, NULL},
 | 
			
		||||
    {NULL}
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
static int
 | 
			
		||||
obj_extra_data_traverse(PyObject *self, visitproc visit, void *arg)
 | 
			
		||||
{
 | 
			
		||||
    PyObject **extra_storage = obj_extra_data_get_extra_storage(self);
 | 
			
		||||
    PyObject *value = *extra_storage;
 | 
			
		||||
    Py_VISIT(value);
 | 
			
		||||
    return 0;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
static int
 | 
			
		||||
obj_extra_data_clear(PyObject *self)
 | 
			
		||||
{
 | 
			
		||||
    PyObject **extra_storage = obj_extra_data_get_extra_storage(self);
 | 
			
		||||
    Py_CLEAR(*extra_storage);
 | 
			
		||||
    return 0;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
static void
 | 
			
		||||
obj_extra_data_dealloc(PyObject *self)
 | 
			
		||||
{
 | 
			
		||||
    PyTypeObject *tp = Py_TYPE(self);
 | 
			
		||||
    PyObject_GC_UnTrack(self);
 | 
			
		||||
    obj_extra_data_clear(self);
 | 
			
		||||
    tp->tp_free(self);
 | 
			
		||||
    Py_DECREF(tp);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
static PyType_Slot ObjExtraData_Slots[] = {
 | 
			
		||||
    {Py_tp_getset, obj_extra_data_getset},
 | 
			
		||||
    {Py_tp_dealloc, obj_extra_data_dealloc},
 | 
			
		||||
    {Py_tp_traverse, obj_extra_data_traverse},
 | 
			
		||||
    {Py_tp_clear, obj_extra_data_clear},
 | 
			
		||||
    {Py_tp_new, obj_extra_data_new},
 | 
			
		||||
    {Py_tp_free, PyObject_GC_Del},
 | 
			
		||||
    {0, NULL},
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
static PyType_Spec ObjExtraData_TypeSpec = {
 | 
			
		||||
    .name = "_testcapi.ObjExtraData",
 | 
			
		||||
    .basicsize = sizeof(ObjExtraData),
 | 
			
		||||
    .flags = Py_TPFLAGS_DEFAULT | Py_TPFLAGS_BASETYPE | Py_TPFLAGS_HAVE_GC,
 | 
			
		||||
    .slots = ObjExtraData_Slots,
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
static PyMethodDef test_methods[] = {
 | 
			
		||||
    {"test_gc_control", test_gc_control, METH_NOARGS},
 | 
			
		||||
    {"test_gc_visit_objects_basic", test_gc_visit_objects_basic, METH_NOARGS, NULL},
 | 
			
		||||
    {"test_gc_visit_objects_exit_early", test_gc_visit_objects_exit_early, METH_NOARGS, NULL},
 | 
			
		||||
    {"without_gc", without_gc, METH_O, NULL},
 | 
			
		||||
    {"with_tp_del", with_tp_del, METH_VARARGS, NULL},
 | 
			
		||||
    {NULL}
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
int _PyTestCapi_Init_GC(PyObject *mod)
 | 
			
		||||
{
 | 
			
		||||
    if (PyModule_AddFunctions(mod, test_methods) < 0) {
 | 
			
		||||
        return -1;
 | 
			
		||||
    }
 | 
			
		||||
    if (PyModule_AddFunctions(mod, test_methods) < 0) {
 | 
			
		||||
        return -1;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    PyObject *ObjExtraData_Type = PyType_FromModuleAndSpec(
 | 
			
		||||
        mod, &ObjExtraData_TypeSpec, NULL);
 | 
			
		||||
    if (ObjExtraData_Type == 0) {
 | 
			
		||||
        return -1;
 | 
			
		||||
    }
 | 
			
		||||
    int ret = PyModule_AddType(mod, (PyTypeObject*)ObjExtraData_Type);
 | 
			
		||||
    Py_DECREF(ObjExtraData_Type);
 | 
			
		||||
    if (ret < 0) {
 | 
			
		||||
        return ret;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    return 0;
 | 
			
		||||
}
 | 
			
		||||
| 
						 | 
				
			
			@ -41,6 +41,7 @@ int _PyTestCapi_Init_Code(PyObject *module);
 | 
			
		|||
int _PyTestCapi_Init_Buffer(PyObject *module);
 | 
			
		||||
int _PyTestCapi_Init_PyOS(PyObject *module);
 | 
			
		||||
int _PyTestCapi_Init_Immortal(PyObject *module);
 | 
			
		||||
int _PyTestCapi_Init_GC(PyObject *mod);
 | 
			
		||||
 | 
			
		||||
#ifdef LIMITED_API_AVAILABLE
 | 
			
		||||
int _PyTestCapi_Init_VectorcallLimited(PyObject *module);
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -154,68 +154,6 @@ test_sizeof_c_types(PyObject *self, PyObject *Py_UNUSED(ignored))
 | 
			
		|||
#endif
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
static PyObject*
 | 
			
		||||
test_gc_control(PyObject *self, PyObject *Py_UNUSED(ignored))
 | 
			
		||||
{
 | 
			
		||||
    int orig_enabled = PyGC_IsEnabled();
 | 
			
		||||
    const char* msg = "ok";
 | 
			
		||||
    int old_state;
 | 
			
		||||
 | 
			
		||||
    old_state = PyGC_Enable();
 | 
			
		||||
    msg = "Enable(1)";
 | 
			
		||||
    if (old_state != orig_enabled) {
 | 
			
		||||
        goto failed;
 | 
			
		||||
    }
 | 
			
		||||
    msg = "IsEnabled(1)";
 | 
			
		||||
    if (!PyGC_IsEnabled()) {
 | 
			
		||||
        goto failed;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    old_state = PyGC_Disable();
 | 
			
		||||
    msg = "disable(2)";
 | 
			
		||||
    if (!old_state) {
 | 
			
		||||
        goto failed;
 | 
			
		||||
    }
 | 
			
		||||
    msg = "IsEnabled(2)";
 | 
			
		||||
    if (PyGC_IsEnabled()) {
 | 
			
		||||
        goto failed;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    old_state = PyGC_Enable();
 | 
			
		||||
    msg = "enable(3)";
 | 
			
		||||
    if (old_state) {
 | 
			
		||||
        goto failed;
 | 
			
		||||
    }
 | 
			
		||||
    msg = "IsEnabled(3)";
 | 
			
		||||
    if (!PyGC_IsEnabled()) {
 | 
			
		||||
        goto failed;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    if (!orig_enabled) {
 | 
			
		||||
        old_state = PyGC_Disable();
 | 
			
		||||
        msg = "disable(4)";
 | 
			
		||||
        if (old_state) {
 | 
			
		||||
            goto failed;
 | 
			
		||||
        }
 | 
			
		||||
        msg = "IsEnabled(4)";
 | 
			
		||||
        if (PyGC_IsEnabled()) {
 | 
			
		||||
            goto failed;
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    Py_RETURN_NONE;
 | 
			
		||||
 | 
			
		||||
failed:
 | 
			
		||||
    /* Try to clean up if we can. */
 | 
			
		||||
    if (orig_enabled) {
 | 
			
		||||
        PyGC_Enable();
 | 
			
		||||
    } else {
 | 
			
		||||
        PyGC_Disable();
 | 
			
		||||
    }
 | 
			
		||||
    PyErr_Format(TestError, "GC control failed in %s", msg);
 | 
			
		||||
    return NULL;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
static PyObject*
 | 
			
		||||
test_list_api(PyObject *self, PyObject *Py_UNUSED(ignored))
 | 
			
		||||
{
 | 
			
		||||
| 
						 | 
				
			
			@ -1627,95 +1565,6 @@ restore_crossinterp_data(PyObject *self, PyObject *args)
 | 
			
		|||
    return _PyCrossInterpreterData_NewObject(data);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
static void
 | 
			
		||||
slot_tp_del(PyObject *self)
 | 
			
		||||
{
 | 
			
		||||
    PyObject *del, *res;
 | 
			
		||||
 | 
			
		||||
    /* Temporarily resurrect the object. */
 | 
			
		||||
    assert(Py_REFCNT(self) == 0);
 | 
			
		||||
    Py_SET_REFCNT(self, 1);
 | 
			
		||||
 | 
			
		||||
    /* Save the current exception, if any. */
 | 
			
		||||
    PyObject *exc = PyErr_GetRaisedException();
 | 
			
		||||
 | 
			
		||||
    PyObject *tp_del = PyUnicode_InternFromString("__tp_del__");
 | 
			
		||||
    if (tp_del == NULL) {
 | 
			
		||||
        PyErr_WriteUnraisable(NULL);
 | 
			
		||||
        PyErr_SetRaisedException(exc);
 | 
			
		||||
        return;
 | 
			
		||||
    }
 | 
			
		||||
    /* Execute __del__ method, if any. */
 | 
			
		||||
    del = _PyType_Lookup(Py_TYPE(self), tp_del);
 | 
			
		||||
    Py_DECREF(tp_del);
 | 
			
		||||
    if (del != NULL) {
 | 
			
		||||
        res = PyObject_CallOneArg(del, self);
 | 
			
		||||
        if (res == NULL)
 | 
			
		||||
            PyErr_WriteUnraisable(del);
 | 
			
		||||
        else
 | 
			
		||||
            Py_DECREF(res);
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    /* Restore the saved exception. */
 | 
			
		||||
    PyErr_SetRaisedException(exc);
 | 
			
		||||
 | 
			
		||||
    /* Undo the temporary resurrection; can't use DECREF here, it would
 | 
			
		||||
     * cause a recursive call.
 | 
			
		||||
     */
 | 
			
		||||
    assert(Py_REFCNT(self) > 0);
 | 
			
		||||
    Py_SET_REFCNT(self, Py_REFCNT(self) - 1);
 | 
			
		||||
    if (Py_REFCNT(self) == 0) {
 | 
			
		||||
        /* this is the normal path out */
 | 
			
		||||
        return;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    /* __del__ resurrected it!  Make it look like the original Py_DECREF
 | 
			
		||||
     * never happened.
 | 
			
		||||
     */
 | 
			
		||||
    {
 | 
			
		||||
        Py_ssize_t refcnt = Py_REFCNT(self);
 | 
			
		||||
        _Py_NewReferenceNoTotal(self);
 | 
			
		||||
        Py_SET_REFCNT(self, refcnt);
 | 
			
		||||
    }
 | 
			
		||||
    assert(!PyType_IS_GC(Py_TYPE(self)) || PyObject_GC_IsTracked(self));
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
static PyObject *
 | 
			
		||||
with_tp_del(PyObject *self, PyObject *args)
 | 
			
		||||
{
 | 
			
		||||
    PyObject *obj;
 | 
			
		||||
    PyTypeObject *tp;
 | 
			
		||||
 | 
			
		||||
    if (!PyArg_ParseTuple(args, "O:with_tp_del", &obj))
 | 
			
		||||
        return NULL;
 | 
			
		||||
    tp = (PyTypeObject *) obj;
 | 
			
		||||
    if (!PyType_Check(obj) || !PyType_HasFeature(tp, Py_TPFLAGS_HEAPTYPE)) {
 | 
			
		||||
        PyErr_Format(PyExc_TypeError,
 | 
			
		||||
                     "heap type expected, got %R", obj);
 | 
			
		||||
        return NULL;
 | 
			
		||||
    }
 | 
			
		||||
    tp->tp_del = slot_tp_del;
 | 
			
		||||
    return Py_NewRef(obj);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
static PyObject *
 | 
			
		||||
without_gc(PyObject *Py_UNUSED(self), PyObject *obj)
 | 
			
		||||
{
 | 
			
		||||
    PyTypeObject *tp = (PyTypeObject*)obj;
 | 
			
		||||
    if (!PyType_Check(obj) || !PyType_HasFeature(tp, Py_TPFLAGS_HEAPTYPE)) {
 | 
			
		||||
        return PyErr_Format(PyExc_TypeError, "heap type expected, got %R", obj);
 | 
			
		||||
    }
 | 
			
		||||
    if (PyType_IS_GC(tp)) {
 | 
			
		||||
        // Don't try this at home, kids:
 | 
			
		||||
        tp->tp_flags -= Py_TPFLAGS_HAVE_GC;
 | 
			
		||||
        tp->tp_free = PyObject_Del;
 | 
			
		||||
        tp->tp_traverse = NULL;
 | 
			
		||||
        tp->tp_clear = NULL;
 | 
			
		||||
    }
 | 
			
		||||
    assert(!PyType_IS_GC(tp));
 | 
			
		||||
    return Py_NewRef(obj);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
static PyMethodDef ml;
 | 
			
		||||
 | 
			
		||||
static PyObject *
 | 
			
		||||
| 
						 | 
				
			
			@ -3342,165 +3191,6 @@ function_set_kw_defaults(PyObject *self, PyObject *args)
 | 
			
		|||
    Py_RETURN_NONE;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
struct gc_visit_state_basic {
 | 
			
		||||
    PyObject *target;
 | 
			
		||||
    int found;
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
static int
 | 
			
		||||
gc_visit_callback_basic(PyObject *obj, void *arg)
 | 
			
		||||
{
 | 
			
		||||
    struct gc_visit_state_basic *state = (struct gc_visit_state_basic *)arg;
 | 
			
		||||
    if (obj == state->target) {
 | 
			
		||||
        state->found = 1;
 | 
			
		||||
        return 0;
 | 
			
		||||
    }
 | 
			
		||||
    return 1;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
static PyObject *
 | 
			
		||||
test_gc_visit_objects_basic(PyObject *Py_UNUSED(self),
 | 
			
		||||
                            PyObject *Py_UNUSED(ignored))
 | 
			
		||||
{
 | 
			
		||||
    PyObject *obj;
 | 
			
		||||
    struct gc_visit_state_basic state;
 | 
			
		||||
 | 
			
		||||
    obj = PyList_New(0);
 | 
			
		||||
    if (obj == NULL) {
 | 
			
		||||
        return NULL;
 | 
			
		||||
    }
 | 
			
		||||
    state.target = obj;
 | 
			
		||||
    state.found = 0;
 | 
			
		||||
 | 
			
		||||
    PyUnstable_GC_VisitObjects(gc_visit_callback_basic, &state);
 | 
			
		||||
    Py_DECREF(obj);
 | 
			
		||||
    if (!state.found) {
 | 
			
		||||
        PyErr_SetString(
 | 
			
		||||
             PyExc_AssertionError,
 | 
			
		||||
             "test_gc_visit_objects_basic: Didn't find live list");
 | 
			
		||||
         return NULL;
 | 
			
		||||
    }
 | 
			
		||||
    Py_RETURN_NONE;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
static int
 | 
			
		||||
gc_visit_callback_exit_early(PyObject *obj, void *arg)
 | 
			
		||||
 {
 | 
			
		||||
    int *visited_i = (int *)arg;
 | 
			
		||||
    (*visited_i)++;
 | 
			
		||||
    if (*visited_i == 2) {
 | 
			
		||||
        return 0;
 | 
			
		||||
    }
 | 
			
		||||
    return 1;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
static PyObject *
 | 
			
		||||
test_gc_visit_objects_exit_early(PyObject *Py_UNUSED(self),
 | 
			
		||||
                                 PyObject *Py_UNUSED(ignored))
 | 
			
		||||
{
 | 
			
		||||
    int visited_i = 0;
 | 
			
		||||
    PyUnstable_GC_VisitObjects(gc_visit_callback_exit_early, &visited_i);
 | 
			
		||||
    if (visited_i != 2) {
 | 
			
		||||
        PyErr_SetString(
 | 
			
		||||
            PyExc_AssertionError,
 | 
			
		||||
            "test_gc_visit_objects_exit_early: did not exit when expected");
 | 
			
		||||
    }
 | 
			
		||||
    Py_RETURN_NONE;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
typedef struct {
 | 
			
		||||
    PyObject_HEAD
 | 
			
		||||
} ObjExtraData;
 | 
			
		||||
 | 
			
		||||
static PyObject *
 | 
			
		||||
obj_extra_data_new(PyTypeObject *type, PyObject *args, PyObject *kwds)
 | 
			
		||||
{
 | 
			
		||||
    size_t extra_size = sizeof(PyObject *);
 | 
			
		||||
    PyObject *obj = PyUnstable_Object_GC_NewWithExtraData(type, extra_size);
 | 
			
		||||
    if (obj == NULL) {
 | 
			
		||||
        return PyErr_NoMemory();
 | 
			
		||||
    }
 | 
			
		||||
    PyObject_GC_Track(obj);
 | 
			
		||||
    return obj;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
static PyObject **
 | 
			
		||||
obj_extra_data_get_extra_storage(PyObject *self)
 | 
			
		||||
{
 | 
			
		||||
    return (PyObject **)((char *)self + Py_TYPE(self)->tp_basicsize);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
static PyObject *
 | 
			
		||||
obj_extra_data_get(PyObject *self, void *Py_UNUSED(ignored))
 | 
			
		||||
{
 | 
			
		||||
    PyObject **extra_storage = obj_extra_data_get_extra_storage(self);
 | 
			
		||||
    PyObject *value = *extra_storage;
 | 
			
		||||
    if (!value) {
 | 
			
		||||
        Py_RETURN_NONE;
 | 
			
		||||
    }
 | 
			
		||||
    return Py_NewRef(value);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
static int
 | 
			
		||||
obj_extra_data_set(PyObject *self, PyObject *newval, void *Py_UNUSED(ignored))
 | 
			
		||||
{
 | 
			
		||||
    PyObject **extra_storage = obj_extra_data_get_extra_storage(self);
 | 
			
		||||
    Py_CLEAR(*extra_storage);
 | 
			
		||||
    if (newval) {
 | 
			
		||||
        *extra_storage = Py_NewRef(newval);
 | 
			
		||||
    }
 | 
			
		||||
    return 0;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
static PyGetSetDef obj_extra_data_getset[] = {
 | 
			
		||||
    {"extra", (getter)obj_extra_data_get, (setter)obj_extra_data_set, NULL},
 | 
			
		||||
    {NULL}
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
static int
 | 
			
		||||
obj_extra_data_traverse(PyObject *self, visitproc visit, void *arg)
 | 
			
		||||
{
 | 
			
		||||
    PyObject **extra_storage = obj_extra_data_get_extra_storage(self);
 | 
			
		||||
    PyObject *value = *extra_storage;
 | 
			
		||||
    Py_VISIT(value);
 | 
			
		||||
    return 0;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
static int
 | 
			
		||||
obj_extra_data_clear(PyObject *self)
 | 
			
		||||
{
 | 
			
		||||
    PyObject **extra_storage = obj_extra_data_get_extra_storage(self);
 | 
			
		||||
    Py_CLEAR(*extra_storage);
 | 
			
		||||
    return 0;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
static void
 | 
			
		||||
obj_extra_data_dealloc(PyObject *self)
 | 
			
		||||
{
 | 
			
		||||
    PyTypeObject *tp = Py_TYPE(self);
 | 
			
		||||
    PyObject_GC_UnTrack(self);
 | 
			
		||||
    obj_extra_data_clear(self);
 | 
			
		||||
    tp->tp_free(self);
 | 
			
		||||
    Py_DECREF(tp);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
static PyType_Slot ObjExtraData_Slots[] = {
 | 
			
		||||
    {Py_tp_getset, obj_extra_data_getset},
 | 
			
		||||
    {Py_tp_dealloc, obj_extra_data_dealloc},
 | 
			
		||||
    {Py_tp_traverse, obj_extra_data_traverse},
 | 
			
		||||
    {Py_tp_clear, obj_extra_data_clear},
 | 
			
		||||
    {Py_tp_new, obj_extra_data_new},
 | 
			
		||||
    {Py_tp_free, PyObject_GC_Del},
 | 
			
		||||
    {0, NULL},
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
static PyType_Spec ObjExtraData_TypeSpec = {
 | 
			
		||||
    .name = "_testcapi.ObjExtraData",
 | 
			
		||||
    .basicsize = sizeof(ObjExtraData),
 | 
			
		||||
    .flags = Py_TPFLAGS_DEFAULT | Py_TPFLAGS_BASETYPE | Py_TPFLAGS_HAVE_GC,
 | 
			
		||||
    .slots = ObjExtraData_Slots,
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
struct atexit_data {
 | 
			
		||||
    int called;
 | 
			
		||||
};
 | 
			
		||||
| 
						 | 
				
			
			@ -3538,7 +3228,6 @@ static PyMethodDef TestMethods[] = {
 | 
			
		|||
    {"set_errno",               set_errno,                       METH_VARARGS},
 | 
			
		||||
    {"test_config",             test_config,                     METH_NOARGS},
 | 
			
		||||
    {"test_sizeof_c_types",     test_sizeof_c_types,             METH_NOARGS},
 | 
			
		||||
    {"test_gc_control",         test_gc_control,                 METH_NOARGS},
 | 
			
		||||
    {"test_list_api",           test_list_api,                   METH_NOARGS},
 | 
			
		||||
    {"test_dict_iteration",     test_dict_iteration,             METH_NOARGS},
 | 
			
		||||
    {"dict_getitem_knownhash",  dict_getitem_knownhash,          METH_VARARGS},
 | 
			
		||||
| 
						 | 
				
			
			@ -3590,7 +3279,6 @@ static PyMethodDef TestMethods[] = {
 | 
			
		|||
     METH_VARARGS | METH_KEYWORDS},
 | 
			
		||||
    {"get_crossinterp_data",    get_crossinterp_data,            METH_VARARGS},
 | 
			
		||||
    {"restore_crossinterp_data", restore_crossinterp_data,       METH_VARARGS},
 | 
			
		||||
    {"with_tp_del",             with_tp_del,                     METH_VARARGS},
 | 
			
		||||
    {"create_cfunction",        create_cfunction,                METH_NOARGS},
 | 
			
		||||
    {"call_in_temporary_c_thread", call_in_temporary_c_thread, METH_VARARGS,
 | 
			
		||||
     PyDoc_STR("set_error_class(error_class) -> None")},
 | 
			
		||||
| 
						 | 
				
			
			@ -3641,7 +3329,6 @@ static PyMethodDef TestMethods[] = {
 | 
			
		|||
    {"meth_fastcall", _PyCFunction_CAST(meth_fastcall), METH_FASTCALL},
 | 
			
		||||
    {"meth_fastcall_keywords", _PyCFunction_CAST(meth_fastcall_keywords), METH_FASTCALL|METH_KEYWORDS},
 | 
			
		||||
    {"pynumber_tobase", pynumber_tobase, METH_VARARGS},
 | 
			
		||||
    {"without_gc", without_gc, METH_O},
 | 
			
		||||
    {"test_set_type_size", test_set_type_size, METH_NOARGS},
 | 
			
		||||
    {"test_py_clear", test_py_clear, METH_NOARGS},
 | 
			
		||||
    {"test_py_setref", test_py_setref, METH_NOARGS},
 | 
			
		||||
| 
						 | 
				
			
			@ -3675,8 +3362,6 @@ static PyMethodDef TestMethods[] = {
 | 
			
		|||
    {"function_set_defaults", function_set_defaults, METH_VARARGS, NULL},
 | 
			
		||||
    {"function_get_kw_defaults", function_get_kw_defaults, METH_O, NULL},
 | 
			
		||||
    {"function_set_kw_defaults", function_set_kw_defaults, METH_VARARGS, NULL},
 | 
			
		||||
    {"test_gc_visit_objects_basic", test_gc_visit_objects_basic, METH_NOARGS, NULL},
 | 
			
		||||
    {"test_gc_visit_objects_exit_early", test_gc_visit_objects_exit_early, METH_NOARGS, NULL},
 | 
			
		||||
    {"test_atexit", test_atexit, METH_NOARGS},
 | 
			
		||||
    {NULL, NULL} /* sentinel */
 | 
			
		||||
};
 | 
			
		||||
| 
						 | 
				
			
			@ -4223,17 +3908,6 @@ PyInit__testcapi(void)
 | 
			
		|||
    Py_INCREF(&MethStatic_Type);
 | 
			
		||||
    PyModule_AddObject(m, "MethStatic", (PyObject *)&MethStatic_Type);
 | 
			
		||||
 | 
			
		||||
    PyObject *ObjExtraData_Type = PyType_FromModuleAndSpec(
 | 
			
		||||
        m, &ObjExtraData_TypeSpec, NULL);
 | 
			
		||||
    if (ObjExtraData_Type == 0) {
 | 
			
		||||
        return NULL;
 | 
			
		||||
    }
 | 
			
		||||
    int ret = PyModule_AddType(m, (PyTypeObject*)ObjExtraData_Type);
 | 
			
		||||
    Py_DECREF(ObjExtraData_Type);
 | 
			
		||||
    if (ret < 0) {
 | 
			
		||||
        return NULL;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    PyModule_AddObject(m, "CHAR_MAX", PyLong_FromLong(CHAR_MAX));
 | 
			
		||||
    PyModule_AddObject(m, "CHAR_MIN", PyLong_FromLong(CHAR_MIN));
 | 
			
		||||
    PyModule_AddObject(m, "UCHAR_MAX", PyLong_FromLong(UCHAR_MAX));
 | 
			
		||||
| 
						 | 
				
			
			@ -4327,6 +4001,9 @@ PyInit__testcapi(void)
 | 
			
		|||
    if (_PyTestCapi_Init_Immortal(m) < 0) {
 | 
			
		||||
        return NULL;
 | 
			
		||||
    }
 | 
			
		||||
    if (_PyTestCapi_Init_GC(m) < 0) {
 | 
			
		||||
        return NULL;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
#ifndef LIMITED_API_AVAILABLE
 | 
			
		||||
    PyModule_AddObjectRef(m, "LIMITED_API_AVAILABLE", Py_False);
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -113,6 +113,7 @@
 | 
			
		|||
    <ClCompile Include="..\Modules\_testcapi\buffer.c" />
 | 
			
		||||
    <ClCompile Include="..\Modules\_testcapi\pyos.c" />
 | 
			
		||||
    <ClCompile Include="..\Modules\_testcapi\immortal.c" />
 | 
			
		||||
    <ClCompile Include="..\Modules\_testcapi\gc.c" />
 | 
			
		||||
  </ItemGroup>
 | 
			
		||||
  <ItemGroup>
 | 
			
		||||
    <ResourceCompile Include="..\PC\python_nt.rc" />
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -66,6 +66,9 @@
 | 
			
		|||
    <ClCompile Include="..\Modules\_testcapi\pyos.c">
 | 
			
		||||
      <Filter>Source Files</Filter>
 | 
			
		||||
    </ClCompile>
 | 
			
		||||
    <ClCompile Include="..\Modules\_testcapi\gc.c">
 | 
			
		||||
      <Filter>Source Files</Filter>
 | 
			
		||||
    </ClCompile>
 | 
			
		||||
  </ItemGroup>
 | 
			
		||||
  <ItemGroup>
 | 
			
		||||
    <ResourceCompile Include="..\PC\python_nt.rc">
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
		Loading…
	
	Add table
		Add a link
		
	
		Reference in a new issue