From 1206b137979a5e10583ee74f01ba18fc237004e9 Mon Sep 17 00:00:00 2001 From: "Erlend E. Aasland" Date: Fri, 24 Mar 2023 11:23:00 +0100 Subject: [PATCH 1/9] Establish global state --- Modules/_ctypes/_ctypes.c | 2 ++ Modules/_ctypes/ctypes.h | 7 +++++++ 2 files changed, 9 insertions(+) diff --git a/Modules/_ctypes/_ctypes.c b/Modules/_ctypes/_ctypes.c index 6f92ca08dd537b..a2469d6298a6ee 100644 --- a/Modules/_ctypes/_ctypes.c +++ b/Modules/_ctypes/_ctypes.c @@ -126,6 +126,8 @@ bytes(cdata) #include "pycore_long.h" // _PyLong_GetZero() +ctypes_state global_state; + PyObject *PyExc_ArgError = NULL; /* This dict maps ctypes types to POINTER types */ diff --git a/Modules/_ctypes/ctypes.h b/Modules/_ctypes/ctypes.h index a7029b6e6da2b8..0b7193ae6ba318 100644 --- a/Modules/_ctypes/ctypes.h +++ b/Modules/_ctypes/ctypes.h @@ -32,6 +32,13 @@ #endif #endif +typedef struct { +} ctypes_state; + +extern ctypes_state global_state; + +#define GLOBAL_STATE() (&global_state) + typedef struct tagPyCArgObject PyCArgObject; typedef struct tagCDataObject CDataObject; typedef PyObject *(* GETFUNC)(void *, Py_ssize_t size); From 1371dbd9d1336966e0bf260718c51dbb2198b88c Mon Sep 17 00:00:00 2001 From: "Erlend E. Aasland" Date: Fri, 24 Mar 2023 11:32:10 +0100 Subject: [PATCH 2/9] Adapt DictRemover type --- Modules/_ctypes/_ctypes.c | 100 +++++++++++--------- Modules/_ctypes/ctypes.h | 1 + Tools/c-analyzer/cpython/globals-to-fix.tsv | 1 - 3 files changed, 54 insertions(+), 48 deletions(-) diff --git a/Modules/_ctypes/_ctypes.c b/Modules/_ctypes/_ctypes.c index a2469d6298a6ee..1b42f3b125095a 100644 --- a/Modules/_ctypes/_ctypes.c +++ b/Modules/_ctypes/_ctypes.c @@ -152,13 +152,32 @@ typedef struct { PyObject *dict; } DictRemoverObject; +static int +_DictRemover_traverse(DictRemoverObject *self, visitproc visit, void *arg) +{ + Py_VISIT(Py_TYPE(self)); + Py_VISIT(self->key); + Py_VISIT(self->dict); + return 0; +} + +static int +_DictRemover_clear(DictRemoverObject *self) +{ + Py_CLEAR(self->key); + Py_CLEAR(self->dict); + return 0; +} + static void _DictRemover_dealloc(PyObject *myself) { + PyTypeObject *tp = Py_TYPE(myself); DictRemoverObject *self = (DictRemoverObject *)myself; - Py_XDECREF(self->key); - Py_XDECREF(self->dict); - Py_TYPE(self)->tp_free(myself); + PyObject_GC_UnTrack(myself); + (void)_DictRemover_clear(self); + tp->tp_free(myself); + Py_DECREF(tp); } static PyObject * @@ -175,47 +194,23 @@ _DictRemover_call(PyObject *myself, PyObject *args, PyObject *kw) Py_RETURN_NONE; } -static PyTypeObject DictRemover_Type = { - PyVarObject_HEAD_INIT(NULL, 0) - "_ctypes.DictRemover", /* tp_name */ - sizeof(DictRemoverObject), /* tp_basicsize */ - 0, /* tp_itemsize */ - _DictRemover_dealloc, /* tp_dealloc */ - 0, /* tp_vectorcall_offset */ - 0, /* tp_getattr */ - 0, /* tp_setattr */ - 0, /* tp_as_async */ - 0, /* tp_repr */ - 0, /* tp_as_number */ - 0, /* tp_as_sequence */ - 0, /* tp_as_mapping */ - 0, /* tp_hash */ - _DictRemover_call, /* tp_call */ - 0, /* tp_str */ - 0, /* tp_getattro */ - 0, /* tp_setattro */ - 0, /* tp_as_buffer */ -/* XXX should participate in GC? */ - Py_TPFLAGS_DEFAULT, /* tp_flags */ - PyDoc_STR("deletes a key from a dictionary"), /* tp_doc */ - 0, /* tp_traverse */ - 0, /* tp_clear */ - 0, /* tp_richcompare */ - 0, /* tp_weaklistoffset */ - 0, /* tp_iter */ - 0, /* tp_iternext */ - 0, /* tp_methods */ - 0, /* tp_members */ - 0, /* tp_getset */ - 0, /* tp_base */ - 0, /* tp_dict */ - 0, /* tp_descr_get */ - 0, /* tp_descr_set */ - 0, /* tp_dictoffset */ - 0, /* tp_init */ - 0, /* tp_alloc */ - 0, /* tp_new */ - 0, /* tp_free */ +PyDoc_STRVAR(dictremover_doc, "deletes a key from a dictionary"); + +static PyType_Slot dictremover_slots[] = { + {Py_tp_dealloc, _DictRemover_dealloc}, + {Py_tp_traverse, _DictRemover_traverse}, + {Py_tp_clear, _DictRemover_clear}, + {Py_tp_call, _DictRemover_call}, + {Py_tp_doc, (void *)dictremover_doc}, + {0, NULL}, +}; + +static PyType_Spec dictremover_spec = { + .name = "_ctypes.DictRemover", + .basicsize = sizeof(DictRemoverObject), + .flags = (Py_TPFLAGS_DEFAULT | Py_TPFLAGS_HAVE_GC | + Py_TPFLAGS_IMMUTABLETYPE), + .slots = dictremover_slots, }; int @@ -226,7 +221,8 @@ PyDict_SetItemProxy(PyObject *dict, PyObject *key, PyObject *item) PyObject *proxy; int result; - obj = _PyObject_CallNoArgs((PyObject *)&DictRemover_Type); + ctypes_state *st = GLOBAL_STATE(); + obj = _PyObject_CallNoArgs((PyObject *)st->DictRemover_Type); if (obj == NULL) return -1; @@ -5637,6 +5633,15 @@ _ctypes_add_types(PyObject *mod) } \ } while (0) +#define CREATE_TYPE(MOD, TP, SPEC) \ + do { \ + PyObject *type = PyType_FromMetaclass(NULL, MOD, SPEC, NULL); \ + if (type == NULL) { \ + return -1; \ + } \ + TP = (PyTypeObject *)type; \ + } while (0) + /* Note: ob_type is the metatype (the 'type'), defaults to PyType_Type, tp_base is the base type, defaults to 'object' aka PyBaseObject_Type. @@ -5683,8 +5688,8 @@ _ctypes_add_types(PyObject *mod) * Other stuff */ - DictRemover_Type.tp_new = PyType_GenericNew; - TYPE_READY(&DictRemover_Type); + ctypes_state *st = GLOBAL_STATE(); + CREATE_TYPE(mod, st->DictRemover_Type, &dictremover_spec); TYPE_READY(&StructParam_Type); #ifdef MS_WIN32 @@ -5694,6 +5699,7 @@ _ctypes_add_types(PyObject *mod) #undef TYPE_READY #undef TYPE_READY_BASE #undef MOD_ADD_TYPE +#undef CREATE_TYPE return 0; } diff --git a/Modules/_ctypes/ctypes.h b/Modules/_ctypes/ctypes.h index 0b7193ae6ba318..071dc2c40a96f4 100644 --- a/Modules/_ctypes/ctypes.h +++ b/Modules/_ctypes/ctypes.h @@ -33,6 +33,7 @@ #endif typedef struct { + PyTypeObject *DictRemover_Type; } ctypes_state; extern ctypes_state global_state; diff --git a/Tools/c-analyzer/cpython/globals-to-fix.tsv b/Tools/c-analyzer/cpython/globals-to-fix.tsv index 849fd5d9a1e8d5..aa1af47ac826a1 100644 --- a/Tools/c-analyzer/cpython/globals-to-fix.tsv +++ b/Tools/c-analyzer/cpython/globals-to-fix.tsv @@ -341,7 +341,6 @@ Modules/_testcapi/vectorcall.c - MethodDescriptor2_Type - ##----------------------- ## static types -Modules/_ctypes/_ctypes.c - DictRemover_Type - Modules/_ctypes/_ctypes.c - PyCArrayType_Type - Modules/_ctypes/_ctypes.c - PyCArray_Type - Modules/_ctypes/_ctypes.c - PyCData_Type - From 0e391509d8292ccf5625e951d4df756e470d5216 Mon Sep 17 00:00:00 2001 From: "Erlend E. Aasland" Date: Tue, 28 Mar 2023 23:54:35 +0200 Subject: [PATCH 3/9] Adapt PyCArg type --- Modules/_ctypes/_ctypes.c | 17 +++++---- Modules/_ctypes/callproc.c | 74 ++++++++++++++++++++------------------ Modules/_ctypes/ctypes.h | 6 ++-- 3 files changed, 55 insertions(+), 42 deletions(-) diff --git a/Modules/_ctypes/_ctypes.c b/Modules/_ctypes/_ctypes.c index 1b42f3b125095a..4c27bb1ea1ce49 100644 --- a/Modules/_ctypes/_ctypes.c +++ b/Modules/_ctypes/_ctypes.c @@ -798,7 +798,8 @@ CDataType_from_param(PyObject *type, PyObject *value) if (res) { return Py_NewRef(value); } - if (PyCArg_CheckExact(value)) { + ctypes_state *st = GLOBAL_STATE(); + if (PyCArg_CheckExact(st, value)) { PyCArgObject *p = (PyCArgObject *)value; PyObject *ob = p->obj; const char *ob_name; @@ -1681,7 +1682,8 @@ c_wchar_p_from_param(PyObject *type, PyObject *value) return Py_NewRef(value); } } - if (PyCArg_CheckExact(value)) { + ctypes_state *st = GLOBAL_STATE(); + if (PyCArg_CheckExact(st, value)) { /* byref(c_char(...)) */ PyCArgObject *a = (PyCArgObject *)value; StgDictObject *dict = PyObject_stgdict(a->obj); @@ -1744,7 +1746,8 @@ c_char_p_from_param(PyObject *type, PyObject *value) return Py_NewRef(value); } } - if (PyCArg_CheckExact(value)) { + ctypes_state *st = GLOBAL_STATE(); + if (PyCArg_CheckExact(st, value)) { /* byref(c_char(...)) */ PyCArgObject *a = (PyCArgObject *)value; StgDictObject *dict = PyObject_stgdict(a->obj); @@ -1845,7 +1848,8 @@ c_void_p_from_param(PyObject *type, PyObject *value) return Py_NewRef(value); } /* byref(...) */ - if (PyCArg_CheckExact(value)) { + ctypes_state *st = GLOBAL_STATE(); + if (PyCArg_CheckExact(st, value)) { /* byref(c_xxx()) */ PyCArgObject *a = (PyCArgObject *)value; if (a->tag == 'P') { @@ -5642,11 +5646,13 @@ _ctypes_add_types(PyObject *mod) TP = (PyTypeObject *)type; \ } while (0) + ctypes_state *st = GLOBAL_STATE(); + /* Note: ob_type is the metatype (the 'type'), defaults to PyType_Type, tp_base is the base type, defaults to 'object' aka PyBaseObject_Type. */ - TYPE_READY(&PyCArg_Type); + CREATE_TYPE(mod, st->PyCArg_Type, &carg_spec); TYPE_READY(&PyCThunk_Type); TYPE_READY(&PyCData_Type); /* StgDict is derived from PyDict_Type */ @@ -5688,7 +5694,6 @@ _ctypes_add_types(PyObject *mod) * Other stuff */ - ctypes_state *st = GLOBAL_STATE(); CREATE_TYPE(mod, st->DictRemover_Type, &dictremover_spec); TYPE_READY(&StructParam_Type); diff --git a/Modules/_ctypes/callproc.c b/Modules/_ctypes/callproc.c index 4438727332bc11..6fd8a1dd140d6f 100644 --- a/Modules/_ctypes/callproc.c +++ b/Modules/_ctypes/callproc.c @@ -469,21 +469,41 @@ PyCArgObject * PyCArgObject_new(void) { PyCArgObject *p; - p = PyObject_New(PyCArgObject, &PyCArg_Type); + ctypes_state *st = GLOBAL_STATE(); + p = PyObject_GC_New(PyCArgObject, st->PyCArg_Type); if (p == NULL) return NULL; p->pffi_type = NULL; p->tag = '\0'; p->obj = NULL; memset(&p->value, 0, sizeof(p->value)); + PyObject_GC_Track(p); return p; } +static int +PyCArg_traverse(PyCArgObject *self, visitproc visit, void *arg) +{ + Py_VISIT(Py_TYPE(self)); + Py_VISIT(self->obj); + return 0; +} + +static int +PyCArg_clear(PyCArgObject *self) +{ + Py_CLEAR(self->obj); + return 0; +} + static void PyCArg_dealloc(PyCArgObject *self) { - Py_XDECREF(self->obj); - PyObject_Free(self); + PyTypeObject *tp = Py_TYPE(self); + PyObject_GC_UnTrack(self); + (void)PyCArg_clear(self); + tp->tp_free((PyObject *)self); + Py_DECREF(tp); } static int @@ -567,36 +587,21 @@ static PyMemberDef PyCArgType_members[] = { { NULL }, }; -PyTypeObject PyCArg_Type = { - PyVarObject_HEAD_INIT(NULL, 0) - "CArgObject", - sizeof(PyCArgObject), - 0, - (destructor)PyCArg_dealloc, /* tp_dealloc */ - 0, /* tp_vectorcall_offset */ - 0, /* tp_getattr */ - 0, /* tp_setattr */ - 0, /* tp_as_async */ - (reprfunc)PyCArg_repr, /* tp_repr */ - 0, /* tp_as_number */ - 0, /* tp_as_sequence */ - 0, /* tp_as_mapping */ - 0, /* tp_hash */ - 0, /* tp_call */ - 0, /* tp_str */ - 0, /* tp_getattro */ - 0, /* tp_setattro */ - 0, /* tp_as_buffer */ - Py_TPFLAGS_DEFAULT, /* tp_flags */ - 0, /* tp_doc */ - 0, /* tp_traverse */ - 0, /* tp_clear */ - 0, /* tp_richcompare */ - 0, /* tp_weaklistoffset */ - 0, /* tp_iter */ - 0, /* tp_iternext */ - 0, /* tp_methods */ - PyCArgType_members, /* tp_members */ +static PyType_Slot carg_slots[] = { + {Py_tp_dealloc, PyCArg_dealloc}, + {Py_tp_traverse, PyCArg_traverse}, + {Py_tp_clear, PyCArg_clear}, + {Py_tp_repr, PyCArg_repr}, + {Py_tp_members, PyCArgType_members}, + {0, NULL}, +}; + +PyType_Spec carg_spec = { + .name = "CArgObject", + .basicsize = sizeof(PyCArgObject), + .flags = (Py_TPFLAGS_DEFAULT | Py_TPFLAGS_HAVE_GC | + Py_TPFLAGS_IMMUTABLETYPE | Py_TPFLAGS_DISALLOW_INSTANTIATION), + .slots = carg_slots, }; /****************************************************************/ @@ -669,7 +674,8 @@ static int ConvParam(PyObject *obj, Py_ssize_t index, struct argument *pa) return 0; } - if (PyCArg_CheckExact(obj)) { + ctypes_state *st = GLOBAL_STATE(); + if (PyCArg_CheckExact(st, obj)) { PyCArgObject *carg = (PyCArgObject *)obj; pa->ffi_type = carg->pffi_type; pa->keep = Py_NewRef(obj); diff --git a/Modules/_ctypes/ctypes.h b/Modules/_ctypes/ctypes.h index 071dc2c40a96f4..e32e7e62029143 100644 --- a/Modules/_ctypes/ctypes.h +++ b/Modules/_ctypes/ctypes.h @@ -34,12 +34,15 @@ typedef struct { PyTypeObject *DictRemover_Type; + PyTypeObject *PyCArg_Type; } ctypes_state; extern ctypes_state global_state; #define GLOBAL_STATE() (&global_state) +extern PyType_Spec carg_spec; + typedef struct tagPyCArgObject PyCArgObject; typedef struct tagCDataObject CDataObject; typedef PyObject *(* GETFUNC)(void *, Py_ssize_t size); @@ -342,8 +345,7 @@ struct tagPyCArgObject { Py_ssize_t size; /* for the 'V' tag */ }; -extern PyTypeObject PyCArg_Type; -#define PyCArg_CheckExact(v) Py_IS_TYPE(v, &PyCArg_Type) +#define PyCArg_CheckExact(st, v) Py_IS_TYPE(v, st->PyCArg_Type) extern PyCArgObject *PyCArgObject_new(void); extern PyObject * From 90b5c8fa4531aacf20d64212798a3aaf52e16337 Mon Sep 17 00:00:00 2001 From: "Erlend E. Aasland" Date: Wed, 26 Apr 2023 22:23:59 +0200 Subject: [PATCH 4/9] Adapt PyCThunk type --- Modules/_ctypes/_ctypes.c | 2 +- Modules/_ctypes/callbacks.c | 81 +++++++++------------ Modules/_ctypes/ctypes.h | 5 +- Tools/c-analyzer/cpython/globals-to-fix.tsv | 2 - 4 files changed, 40 insertions(+), 50 deletions(-) diff --git a/Modules/_ctypes/_ctypes.c b/Modules/_ctypes/_ctypes.c index 4c27bb1ea1ce49..8005cdf5dd8b59 100644 --- a/Modules/_ctypes/_ctypes.c +++ b/Modules/_ctypes/_ctypes.c @@ -5653,7 +5653,7 @@ _ctypes_add_types(PyObject *mod) tp_base is the base type, defaults to 'object' aka PyBaseObject_Type. */ CREATE_TYPE(mod, st->PyCArg_Type, &carg_spec); - TYPE_READY(&PyCThunk_Type); + CREATE_TYPE(mod, st->PyCThunk_Type, &cthunk_spec); TYPE_READY(&PyCData_Type); /* StgDict is derived from PyDict_Type */ TYPE_READY_BASE(&PyCStgDict_Type, &PyDict_Type); diff --git a/Modules/_ctypes/callbacks.c b/Modules/_ctypes/callbacks.c index bc8750091f65f3..8e694ba852c1d4 100644 --- a/Modules/_ctypes/callbacks.c +++ b/Modules/_ctypes/callbacks.c @@ -28,23 +28,11 @@ /**************************************************************/ -static void -CThunkObject_dealloc(PyObject *myself) -{ - CThunkObject *self = (CThunkObject *)myself; - PyObject_GC_UnTrack(self); - Py_XDECREF(self->converters); - Py_XDECREF(self->callable); - Py_XDECREF(self->restype); - if (self->pcl_write) - Py_ffi_closure_free(self->pcl_write); - PyObject_GC_Del(self); -} - static int CThunkObject_traverse(PyObject *myself, visitproc visit, void *arg) { CThunkObject *self = (CThunkObject *)myself; + Py_VISIT(Py_TYPE(self)); Py_VISIT(self->converters); Py_VISIT(self->callable); Py_VISIT(self->restype); @@ -61,36 +49,35 @@ CThunkObject_clear(PyObject *myself) return 0; } -PyTypeObject PyCThunk_Type = { - PyVarObject_HEAD_INIT(NULL, 0) - "_ctypes.CThunkObject", - sizeof(CThunkObject), /* tp_basicsize */ - sizeof(ffi_type), /* tp_itemsize */ - CThunkObject_dealloc, /* tp_dealloc */ - 0, /* tp_vectorcall_offset */ - 0, /* tp_getattr */ - 0, /* tp_setattr */ - 0, /* tp_as_async */ - 0, /* tp_repr */ - 0, /* tp_as_number */ - 0, /* tp_as_sequence */ - 0, /* tp_as_mapping */ - 0, /* tp_hash */ - 0, /* tp_call */ - 0, /* tp_str */ - 0, /* tp_getattro */ - 0, /* tp_setattro */ - 0, /* tp_as_buffer */ - Py_TPFLAGS_DEFAULT | Py_TPFLAGS_HAVE_GC, /* tp_flags */ - PyDoc_STR("CThunkObject"), /* tp_doc */ - CThunkObject_traverse, /* tp_traverse */ - CThunkObject_clear, /* tp_clear */ - 0, /* tp_richcompare */ - 0, /* tp_weaklistoffset */ - 0, /* tp_iter */ - 0, /* tp_iternext */ - 0, /* tp_methods */ - 0, /* tp_members */ +static void +CThunkObject_dealloc(PyObject *myself) +{ + CThunkObject *self = (CThunkObject *)myself; + PyTypeObject *tp = Py_TYPE(myself); + PyObject_GC_UnTrack(self); + (void)CThunkObject_clear(myself); + if (self->pcl_write) { + Py_ffi_closure_free(self->pcl_write); + } + PyObject_GC_Del(self); + Py_DECREF(tp); +} + +static PyType_Slot cthunk_slots[] = { + {Py_tp_doc, (void *)PyDoc_STR("CThunkObject")}, + {Py_tp_dealloc, CThunkObject_dealloc}, + {Py_tp_traverse, CThunkObject_traverse}, + {Py_tp_clear, CThunkObject_clear}, + {0, NULL}, +}; + +PyType_Spec cthunk_spec = { + .name = "_ctypes.CThunkObject", + .basicsize = sizeof(CThunkObject), + .itemsize = sizeof(ffi_type), + .flags = (Py_TPFLAGS_DEFAULT | Py_TPFLAGS_HAVE_GC | + Py_TPFLAGS_IMMUTABLETYPE | Py_TPFLAGS_DISALLOW_INSTANTIATION), + .slots = cthunk_slots, }; /**************************************************************/ @@ -320,7 +307,8 @@ static CThunkObject* CThunkObject_new(Py_ssize_t nargs) CThunkObject *p; Py_ssize_t i; - p = PyObject_GC_NewVar(CThunkObject, &PyCThunk_Type, nargs); + ctypes_state *st = GLOBAL_STATE(); + p = PyObject_GC_NewVar(CThunkObject, st->PyCThunk_Type, nargs); if (p == NULL) { return NULL; } @@ -357,7 +345,10 @@ CThunkObject *_ctypes_alloc_callback(PyObject *callable, if (p == NULL) return NULL; - assert(CThunk_CheckExact((PyObject *)p)); +#ifdef Py_DEBUG + ctypes_state *st = GLOBAL_STATE(); + assert(CThunk_CheckExact(st, (PyObject *)p)); +#endif p->pcl_write = Py_ffi_closure_alloc(sizeof(ffi_closure), &p->pcl_exec); if (p->pcl_write == NULL) { diff --git a/Modules/_ctypes/ctypes.h b/Modules/_ctypes/ctypes.h index e32e7e62029143..ec1f3c09719767 100644 --- a/Modules/_ctypes/ctypes.h +++ b/Modules/_ctypes/ctypes.h @@ -35,6 +35,7 @@ typedef struct { PyTypeObject *DictRemover_Type; PyTypeObject *PyCArg_Type; + PyTypeObject *PyCThunk_Type; } ctypes_state; extern ctypes_state global_state; @@ -42,6 +43,7 @@ extern ctypes_state global_state; #define GLOBAL_STATE() (&global_state) extern PyType_Spec carg_spec; +extern PyType_Spec cthunk_spec; typedef struct tagPyCArgObject PyCArgObject; typedef struct tagCDataObject CDataObject; @@ -99,8 +101,7 @@ typedef struct { ffi_type *ffi_restype; ffi_type *atypes[1]; } CThunkObject; -extern PyTypeObject PyCThunk_Type; -#define CThunk_CheckExact(v) Py_IS_TYPE(v, &PyCThunk_Type) +#define CThunk_CheckExact(st, v) Py_IS_TYPE(v, st->PyCThunk_Type) typedef struct { /* First part identical to tagCDataObject */ diff --git a/Tools/c-analyzer/cpython/globals-to-fix.tsv b/Tools/c-analyzer/cpython/globals-to-fix.tsv index aa1af47ac826a1..6b9739b53d34be 100644 --- a/Tools/c-analyzer/cpython/globals-to-fix.tsv +++ b/Tools/c-analyzer/cpython/globals-to-fix.tsv @@ -355,7 +355,6 @@ Modules/_ctypes/_ctypes.c - StructParam_Type - Modules/_ctypes/_ctypes.c - Struct_Type - Modules/_ctypes/_ctypes.c - UnionType_Type - Modules/_ctypes/_ctypes.c - Union_Type - -Modules/_ctypes/callbacks.c - PyCThunk_Type - Modules/_ctypes/callproc.c - PyCArg_Type - Modules/_ctypes/cfield.c - PyCField_Type - Modules/_ctypes/ctypes.h - PyCArg_Type - @@ -370,7 +369,6 @@ Modules/_ctypes/ctypes.h - PyCPointer_Type - Modules/_ctypes/ctypes.h - PyCSimpleType_Type - Modules/_ctypes/ctypes.h - PyCStgDict_Type - Modules/_ctypes/ctypes.h - PyCStructType_Type - -Modules/_ctypes/ctypes.h - PyCThunk_Type - Modules/_ctypes/ctypes.h - PyExc_ArgError - Modules/_ctypes/ctypes.h - _ctypes_conversion_encoding - Modules/_ctypes/ctypes.h - _ctypes_conversion_errors - From bcd391fc950bbbae96bed8808f74802a62f3a1c4 Mon Sep 17 00:00:00 2001 From: "Erlend E. Aasland" Date: Wed, 26 Apr 2023 22:47:52 +0200 Subject: [PATCH 5/9] Adapt PyCField type --- Modules/_ctypes/_ctypes.c | 3 +- Modules/_ctypes/cfield.c | 67 ++++++++------------- Modules/_ctypes/ctypes.h | 3 +- Modules/_ctypes/stgdict.c | 12 ++-- Tools/c-analyzer/cpython/globals-to-fix.tsv | 2 - 5 files changed, 36 insertions(+), 51 deletions(-) diff --git a/Modules/_ctypes/_ctypes.c b/Modules/_ctypes/_ctypes.c index 8005cdf5dd8b59..9de5475d09e9a9 100644 --- a/Modules/_ctypes/_ctypes.c +++ b/Modules/_ctypes/_ctypes.c @@ -5686,8 +5686,7 @@ _ctypes_add_types(PyObject *mod) * Simple classes */ - /* PyCField_Type is derived from PyBaseObject_Type */ - TYPE_READY(&PyCField_Type); + CREATE_TYPE(mod, st->PyCField_Type, &cfield_spec); /************************************************* * diff --git a/Modules/_ctypes/cfield.c b/Modules/_ctypes/cfield.c index 796a1bec966de1..128506a9eed920 100644 --- a/Modules/_ctypes/cfield.c +++ b/Modules/_ctypes/cfield.c @@ -61,7 +61,9 @@ PyCField_FromDesc(PyObject *desc, Py_ssize_t index, #define CONT_BITFIELD 2 #define EXPAND_BITFIELD 3 - self = (CFieldObject *)PyCField_Type.tp_alloc((PyTypeObject *)&PyCField_Type, 0); + ctypes_state *st = GLOBAL_STATE(); + PyTypeObject *tp = st->PyCField_Type; + self = (CFieldObject *)tp->tp_alloc(tp, 0); if (self == NULL) return NULL; dict = PyType_stgdict(desc); @@ -256,6 +258,7 @@ static PyGetSetDef PyCField_getset[] = { static int PyCField_traverse(CFieldObject *self, visitproc visit, void *arg) { + Py_VISIT(Py_TYPE(self)); Py_VISIT(self->proto); return 0; } @@ -270,9 +273,11 @@ PyCField_clear(CFieldObject *self) static void PyCField_dealloc(PyObject *self) { + PyTypeObject *tp = Py_TYPE(self); PyObject_GC_UnTrack(self); - PyCField_clear((CFieldObject *)self); + (void)PyCField_clear((CFieldObject *)self); Py_TYPE(self)->tp_free((PyObject *)self); + Py_DECREF(tp); } static PyObject * @@ -296,46 +301,24 @@ PyCField_repr(CFieldObject *self) return result; } -PyTypeObject PyCField_Type = { - PyVarObject_HEAD_INIT(NULL, 0) - "_ctypes.CField", /* tp_name */ - sizeof(CFieldObject), /* tp_basicsize */ - 0, /* tp_itemsize */ - PyCField_dealloc, /* tp_dealloc */ - 0, /* tp_vectorcall_offset */ - 0, /* tp_getattr */ - 0, /* tp_setattr */ - 0, /* tp_as_async */ - (reprfunc)PyCField_repr, /* tp_repr */ - 0, /* tp_as_number */ - 0, /* tp_as_sequence */ - 0, /* tp_as_mapping */ - 0, /* tp_hash */ - 0, /* tp_call */ - 0, /* tp_str */ - 0, /* tp_getattro */ - 0, /* tp_setattro */ - 0, /* tp_as_buffer */ - Py_TPFLAGS_DEFAULT | Py_TPFLAGS_HAVE_GC, /* tp_flags */ - PyDoc_STR("Structure/Union member"), /* tp_doc */ - (traverseproc)PyCField_traverse, /* tp_traverse */ - (inquiry)PyCField_clear, /* tp_clear */ - 0, /* tp_richcompare */ - 0, /* tp_weaklistoffset */ - 0, /* tp_iter */ - 0, /* tp_iternext */ - 0, /* tp_methods */ - 0, /* tp_members */ - PyCField_getset, /* tp_getset */ - 0, /* tp_base */ - 0, /* tp_dict */ - (descrgetfunc)PyCField_get, /* tp_descr_get */ - (descrsetfunc)PyCField_set, /* tp_descr_set */ - 0, /* tp_dictoffset */ - 0, /* tp_init */ - 0, /* tp_alloc */ - 0, /* tp_new */ - 0, /* tp_free */ +static PyType_Slot cfield_slots[] = { + {Py_tp_dealloc, PyCField_dealloc}, + {Py_tp_repr, PyCField_repr}, + {Py_tp_doc, (void *)PyDoc_STR("Structure/Union member")}, + {Py_tp_traverse, PyCField_traverse}, + {Py_tp_clear, PyCField_clear}, + {Py_tp_getset, PyCField_getset}, + {Py_tp_descr_get, PyCField_get}, + {Py_tp_descr_set, PyCField_set}, + {0, NULL}, +}; + +PyType_Spec cfield_spec = { + .name = "_ctypes.CField", + .basicsize = sizeof(CFieldObject), + .flags = (Py_TPFLAGS_DEFAULT | Py_TPFLAGS_HAVE_GC | + Py_TPFLAGS_IMMUTABLETYPE | Py_TPFLAGS_DISALLOW_INSTANTIATION), + .slots = cfield_slots, }; diff --git a/Modules/_ctypes/ctypes.h b/Modules/_ctypes/ctypes.h index ec1f3c09719767..68b4e80e19508e 100644 --- a/Modules/_ctypes/ctypes.h +++ b/Modules/_ctypes/ctypes.h @@ -35,6 +35,7 @@ typedef struct { PyTypeObject *DictRemover_Type; PyTypeObject *PyCArg_Type; + PyTypeObject *PyCField_Type; PyTypeObject *PyCThunk_Type; } ctypes_state; @@ -43,6 +44,7 @@ extern ctypes_state global_state; #define GLOBAL_STATE() (&global_state) extern PyType_Spec carg_spec; +extern PyType_Spec cfield_spec; extern PyType_Spec cthunk_spec; typedef struct tagPyCArgObject PyCArgObject; @@ -153,7 +155,6 @@ extern PyTypeObject PyCSimpleType_Type; #define PyCSimpleTypeObject_CheckExact(v) Py_IS_TYPE(v, &PyCSimpleType_Type) #define PyCSimpleTypeObject_Check(v) PyObject_TypeCheck(v, &PyCSimpleType_Type) -extern PyTypeObject PyCField_Type; extern struct fielddesc *_ctypes_get_fielddesc(const char *fmt); diff --git a/Modules/_ctypes/stgdict.c b/Modules/_ctypes/stgdict.c index 83a52757d60979..b1b2bac1455e67 100644 --- a/Modules/_ctypes/stgdict.c +++ b/Modules/_ctypes/stgdict.c @@ -225,6 +225,8 @@ MakeFields(PyObject *type, CFieldObject *descr, if (fieldlist == NULL) return -1; + ctypes_state *st = GLOBAL_STATE(); + PyTypeObject *cfield_tp = st->PyCField_Type; for (i = 0; i < PySequence_Fast_GET_SIZE(fieldlist); ++i) { PyObject *pair = PySequence_Fast_GET_ITEM(fieldlist, i); /* borrowed */ PyObject *fname, *ftype, *bits; @@ -240,7 +242,7 @@ MakeFields(PyObject *type, CFieldObject *descr, Py_DECREF(fieldlist); return -1; } - if (!Py_IS_TYPE(fdescr, &PyCField_Type)) { + if (!Py_IS_TYPE(fdescr, cfield_tp)) { PyErr_SetString(PyExc_TypeError, "unexpected type"); Py_DECREF(fdescr); Py_DECREF(fieldlist); @@ -257,13 +259,13 @@ MakeFields(PyObject *type, CFieldObject *descr, } continue; } - new_descr = (CFieldObject *)PyCField_Type.tp_alloc((PyTypeObject *)&PyCField_Type, 0); + new_descr = (CFieldObject *)cfield_tp->tp_alloc(cfield_tp, 0); if (new_descr == NULL) { Py_DECREF(fdescr); Py_DECREF(fieldlist); return -1; } - assert(Py_IS_TYPE(new_descr, &PyCField_Type)); + assert(Py_IS_TYPE(new_descr, cfield_tp)); new_descr->size = fdescr->size; new_descr->offset = fdescr->offset + offset; new_descr->index = fdescr->index + index; @@ -304,6 +306,8 @@ MakeAnonFields(PyObject *type) if (anon_names == NULL) return -1; + ctypes_state *st = GLOBAL_STATE(); + PyTypeObject *cfield_tp = st->PyCField_Type; for (i = 0; i < PySequence_Fast_GET_SIZE(anon_names); ++i) { PyObject *fname = PySequence_Fast_GET_ITEM(anon_names, i); /* borrowed */ CFieldObject *descr = (CFieldObject *)PyObject_GetAttr(type, fname); @@ -311,7 +315,7 @@ MakeAnonFields(PyObject *type) Py_DECREF(anon_names); return -1; } - if (!Py_IS_TYPE(descr, &PyCField_Type)) { + if (!Py_IS_TYPE(descr, cfield_tp)) { PyErr_Format(PyExc_AttributeError, "'%U' is specified in _anonymous_ but not in " "_fields_", diff --git a/Tools/c-analyzer/cpython/globals-to-fix.tsv b/Tools/c-analyzer/cpython/globals-to-fix.tsv index 6b9739b53d34be..c93c13889ffb67 100644 --- a/Tools/c-analyzer/cpython/globals-to-fix.tsv +++ b/Tools/c-analyzer/cpython/globals-to-fix.tsv @@ -356,12 +356,10 @@ Modules/_ctypes/_ctypes.c - Struct_Type - Modules/_ctypes/_ctypes.c - UnionType_Type - Modules/_ctypes/_ctypes.c - Union_Type - Modules/_ctypes/callproc.c - PyCArg_Type - -Modules/_ctypes/cfield.c - PyCField_Type - Modules/_ctypes/ctypes.h - PyCArg_Type - Modules/_ctypes/ctypes.h - PyCArrayType_Type - Modules/_ctypes/ctypes.h - PyCArray_Type - Modules/_ctypes/ctypes.h - PyCData_Type - -Modules/_ctypes/ctypes.h - PyCField_Type - Modules/_ctypes/ctypes.h - PyCFuncPtrType_Type - Modules/_ctypes/ctypes.h - PyCFuncPtr_Type - Modules/_ctypes/ctypes.h - PyCPointerType_Type - From a0d3a681bf5bfce96ca8f2586f2a62deb23ceee7 Mon Sep 17 00:00:00 2001 From: "Erlend E. Aasland" Date: Wed, 26 Apr 2023 23:09:34 +0200 Subject: [PATCH 6/9] Adapt StructParam type --- Modules/_ctypes/_ctypes.c | 44 ++++++++++++++++----- Modules/_ctypes/ctypes.h | 1 + Tools/c-analyzer/cpython/globals-to-fix.tsv | 1 - 3 files changed, 35 insertions(+), 11 deletions(-) diff --git a/Modules/_ctypes/_ctypes.c b/Modules/_ctypes/_ctypes.c index 9de5475d09e9a9..e24bceba0c54dc 100644 --- a/Modules/_ctypes/_ctypes.c +++ b/Modules/_ctypes/_ctypes.c @@ -413,23 +413,45 @@ typedef struct { PyObject *keep; // If set, a reference to the original CDataObject. } StructParamObject; +static int +StructParam_traverse(StructParamObject *self, visitproc visit, void *arg) +{ + Py_VISIT(Py_TYPE(self)); + return 0; +} + +static int +StructParam_clear(StructParamObject *self) +{ + Py_CLEAR(self->keep); + return 0; +} static void StructParam_dealloc(PyObject *myself) { StructParamObject *self = (StructParamObject *)myself; - Py_XDECREF(self->keep); + PyTypeObject *tp = Py_TYPE(self); + PyObject_GC_UnTrack(myself); + (void)StructParam_clear(self); PyMem_Free(self->ptr); - Py_TYPE(self)->tp_free(myself); + tp->tp_free(myself); + Py_DECREF(tp); } +static PyType_Slot structparam_slots[] = { + {Py_tp_traverse, StructParam_traverse}, + {Py_tp_clear, StructParam_clear}, + {Py_tp_dealloc, StructParam_dealloc}, + {0, NULL}, +}; -static PyTypeObject StructParam_Type = { - PyVarObject_HEAD_INIT(NULL, 0) - .tp_name = "_ctypes.StructParam_Type", - .tp_basicsize = sizeof(StructParamObject), - .tp_dealloc = StructParam_dealloc, - .tp_flags = Py_TPFLAGS_DEFAULT, +static PyType_Spec structparam_spec = { + .name = "_ctypes.StructParam_Type", + .basicsize = sizeof(StructParamObject), + .flags = (Py_TPFLAGS_DEFAULT | Py_TPFLAGS_IMMUTABLETYPE | + Py_TPFLAGS_HAVE_GC | Py_TPFLAGS_DISALLOW_INSTANTIATION), + .slots = structparam_slots, }; @@ -458,7 +480,9 @@ StructUnionType_paramfunc(CDataObject *self) /* Create a Python object which calls PyMem_Free(ptr) in its deallocator. The object will be destroyed at _ctypes_callproc() cleanup. */ - obj = (&StructParam_Type)->tp_alloc(&StructParam_Type, 0); + ctypes_state *st = GLOBAL_STATE(); + PyTypeObject *tp = st->StructParam_Type; + obj = tp->tp_alloc(tp, 0); if (obj == NULL) { PyMem_Free(ptr); return NULL; @@ -5694,7 +5718,7 @@ _ctypes_add_types(PyObject *mod) */ CREATE_TYPE(mod, st->DictRemover_Type, &dictremover_spec); - TYPE_READY(&StructParam_Type); + CREATE_TYPE(mod, st->StructParam_Type, &structparam_spec); #ifdef MS_WIN32 TYPE_READY_BASE(&PyComError_Type, (PyTypeObject*)PyExc_Exception); diff --git a/Modules/_ctypes/ctypes.h b/Modules/_ctypes/ctypes.h index 68b4e80e19508e..252d9da7dbb56d 100644 --- a/Modules/_ctypes/ctypes.h +++ b/Modules/_ctypes/ctypes.h @@ -37,6 +37,7 @@ typedef struct { PyTypeObject *PyCArg_Type; PyTypeObject *PyCField_Type; PyTypeObject *PyCThunk_Type; + PyTypeObject *StructParam_Type; } ctypes_state; extern ctypes_state global_state; diff --git a/Tools/c-analyzer/cpython/globals-to-fix.tsv b/Tools/c-analyzer/cpython/globals-to-fix.tsv index c93c13889ffb67..90a47efe6d3dec 100644 --- a/Tools/c-analyzer/cpython/globals-to-fix.tsv +++ b/Tools/c-analyzer/cpython/globals-to-fix.tsv @@ -351,7 +351,6 @@ Modules/_ctypes/_ctypes.c - PyCPointer_Type - Modules/_ctypes/_ctypes.c - PyCSimpleType_Type - Modules/_ctypes/_ctypes.c - PyCStructType_Type - Modules/_ctypes/_ctypes.c - Simple_Type - -Modules/_ctypes/_ctypes.c - StructParam_Type - Modules/_ctypes/_ctypes.c - Struct_Type - Modules/_ctypes/_ctypes.c - UnionType_Type - Modules/_ctypes/_ctypes.c - Union_Type - From 77882d66d1722bf5c5b492265da27089b44803f7 Mon Sep 17 00:00:00 2001 From: "Erlend E. Aasland" Date: Wed, 26 Apr 2023 23:23:07 +0200 Subject: [PATCH 7/9] Clean up macro --- Modules/_ctypes/_ctypes.c | 15 +++++++-------- 1 file changed, 7 insertions(+), 8 deletions(-) diff --git a/Modules/_ctypes/_ctypes.c b/Modules/_ctypes/_ctypes.c index e24bceba0c54dc..c7ed6bd2229c79 100644 --- a/Modules/_ctypes/_ctypes.c +++ b/Modules/_ctypes/_ctypes.c @@ -5661,14 +5661,13 @@ _ctypes_add_types(PyObject *mod) } \ } while (0) -#define CREATE_TYPE(MOD, TP, SPEC) \ - do { \ - PyObject *type = PyType_FromMetaclass(NULL, MOD, SPEC, NULL); \ - if (type == NULL) { \ - return -1; \ - } \ - TP = (PyTypeObject *)type; \ - } while (0) +#define CREATE_TYPE(MOD, TP, SPEC) do { \ + PyObject *type = PyType_FromMetaclass(NULL, MOD, SPEC, NULL); \ + if (type == NULL) { \ + return -1; \ + } \ + TP = (PyTypeObject *)type; \ +} while (0) ctypes_state *st = GLOBAL_STATE(); From e5ce31ad8402dc882437849a4f46098968a6236a Mon Sep 17 00:00:00 2001 From: "Erlend E. Aasland" Date: Wed, 26 Apr 2023 23:50:08 +0200 Subject: [PATCH 8/9] Add global state to c analyzer --- Tools/c-analyzer/cpython/globals-to-fix.tsv | 2 ++ 1 file changed, 2 insertions(+) diff --git a/Tools/c-analyzer/cpython/globals-to-fix.tsv b/Tools/c-analyzer/cpython/globals-to-fix.tsv index 90a47efe6d3dec..e5d76bfaefc515 100644 --- a/Tools/c-analyzer/cpython/globals-to-fix.tsv +++ b/Tools/c-analyzer/cpython/globals-to-fix.tsv @@ -448,6 +448,8 @@ Modules/_decimal/_decimal.c - SignalTuple - Modules/_asynciomodule.c - fi_freelist - Modules/_asynciomodule.c - fi_freelist_len - Modules/_ctypes/_ctypes.c - _ctypes_ptrtype_cache - +Modules/_ctypes/_ctypes.c - global_state - +Modules/_ctypes/ctypes.h - global_state - Modules/_tkinter.c - tcl_lock - Modules/_tkinter.c - excInCmd - Modules/_tkinter.c - valInCmd - From a2767539656a507b13f70d0ffd13f5140cf2fc91 Mon Sep 17 00:00:00 2001 From: "Erlend E. Aasland" Date: Wed, 26 Apr 2023 23:52:54 +0200 Subject: [PATCH 9/9] Fix 'has no __module__ attribute' warning --- Modules/_ctypes/callproc.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Modules/_ctypes/callproc.c b/Modules/_ctypes/callproc.c index 6fd8a1dd140d6f..93bc784df5386f 100644 --- a/Modules/_ctypes/callproc.c +++ b/Modules/_ctypes/callproc.c @@ -597,7 +597,7 @@ static PyType_Slot carg_slots[] = { }; PyType_Spec carg_spec = { - .name = "CArgObject", + .name = "_ctypes.CArgObject", .basicsize = sizeof(PyCArgObject), .flags = (Py_TPFLAGS_DEFAULT | Py_TPFLAGS_HAVE_GC | Py_TPFLAGS_IMMUTABLETYPE | Py_TPFLAGS_DISALLOW_INSTANTIATION),