From edc7e6e79440fccb2a495cbd8b8380b1bda65f05 Mon Sep 17 00:00:00 2001 From: Victor Stinner Date: Fri, 19 Jan 2024 15:13:49 +0100 Subject: [PATCH 1/3] gh-114314: Move types to ctypes_state Move types to the ctypes_state structure: * PyCArray_Type * PyCData_Type * PyCFuncPtr_Type * PyCPointer_Type * Simple_Type The following macros get an additional state parameter: * ArrayObject_Check() * CDataObject_Check() * CDataObject_CheckExact() * PointerObject_Check() * PyCFuncPtrObject_Check() The following function gets an additional state parameter: * _PyCData_set() Co-Authored-By: neonene <53406459+neonene@users.noreply.github.com> --- Modules/_ctypes/_ctypes.c | 92 ++++++++++++++++++++++--------------- Modules/_ctypes/callbacks.c | 7 +-- Modules/_ctypes/callproc.c | 20 +++++--- Modules/_ctypes/cfield.c | 6 ++- Modules/_ctypes/ctypes.h | 20 ++++---- 5 files changed, 86 insertions(+), 59 deletions(-) diff --git a/Modules/_ctypes/_ctypes.c b/Modules/_ctypes/_ctypes.c index b51a03b5497fed..a41ce8fc414191 100644 --- a/Modules/_ctypes/_ctypes.c +++ b/Modules/_ctypes/_ctypes.c @@ -1149,7 +1149,8 @@ PyCPointerType_from_param(PyObject *type, PyObject *value) break; } - if (PointerObject_Check(value) || ArrayObject_Check(value)) { + ctypes_state *state = GLOBAL_STATE(); + if (PointerObject_Check(state, value) || ArrayObject_Check(state, value)) { /* Array instances are also pointers when the item types are the same. */ @@ -1587,7 +1588,8 @@ c_wchar_p_from_param(PyObject *type, PyObject *value) if (res) { return Py_NewRef(value); } - if (ArrayObject_Check(value) || PointerObject_Check(value)) { + ctypes_state *state = GLOBAL_STATE(); + if (ArrayObject_Check(state, value) || PointerObject_Check(state, value)) { /* c_wchar array instance or pointer(c_wchar(...)) */ StgDictObject *dt = PyObject_stgdict(value); StgDictObject *dict; @@ -1651,7 +1653,8 @@ c_char_p_from_param(PyObject *type, PyObject *value) if (res) { return Py_NewRef(value); } - if (ArrayObject_Check(value) || PointerObject_Check(value)) { + ctypes_state *state = GLOBAL_STATE(); + if (ArrayObject_Check(state, value) || PointerObject_Check(state, value)) { /* c_char array instance or pointer(c_char(...)) */ StgDictObject *dt = PyObject_stgdict(value); StgDictObject *dict; @@ -1758,13 +1761,13 @@ c_void_p_from_param(PyObject *type, PyObject *value) return Py_NewRef(value); } /* ctypes array or pointer instance */ - if (ArrayObject_Check(value) || PointerObject_Check(value)) { + ctypes_state *state = GLOBAL_STATE(); + if (ArrayObject_Check(state, value) || PointerObject_Check(state, value)) { /* Any array or pointer is accepted */ return Py_NewRef(value); } /* byref(...) */ - ctypes_state *st = GLOBAL_STATE(); - if (PyCArg_CheckExact(st, value)) { + if (PyCArg_CheckExact(state, value)) { /* byref(c_xxx()) */ PyCArgObject *a = (PyCArgObject *)value; if (a->tag == 'P') { @@ -1772,7 +1775,7 @@ c_void_p_from_param(PyObject *type, PyObject *value) } } /* function pointer */ - if (PyCFuncPtrObject_Check(value)) { + if (PyCFuncPtrObject_Check(state, value)) { PyCArgObject *parg; PyCFuncPtrObject *func; func = (PyCFuncPtrObject *)value; @@ -1788,7 +1791,7 @@ c_void_p_from_param(PyObject *type, PyObject *value) } /* c_char_p, c_wchar_p */ stgd = PyObject_stgdict(value); - if (stgd && CDataObject_Check(value) && stgd->proto && PyUnicode_Check(stgd->proto)) { + if (stgd && CDataObject_Check(state, value) && stgd->proto && PyUnicode_Check(stgd->proto)) { PyCArgObject *parg; switch (PyUnicode_AsUTF8(stgd->proto)[0]) { @@ -2006,7 +2009,7 @@ PyCSimpleType_new(PyTypeObject *type, PyObject *args, PyObject *kwds) stgdict->paramfunc = PyCSimpleType_paramfunc; /* - if (result->tp_base != &Simple_Type) { + if (result->tp_base != state->Simple_Type) { stgdict->setfunc = NULL; stgdict->getfunc = NULL; } @@ -2026,7 +2029,8 @@ PyCSimpleType_new(PyTypeObject *type, PyObject *args, PyObject *kwds) /* Install from_param class methods in ctypes base classes. Overrides the PyCSimpleType_from_param generic method. */ - if (result->tp_base == &Simple_Type) { + ctypes_state *state = GLOBAL_STATE(); + if (result->tp_base == state->Simple_Type) { switch (*proto_str) { case 'z': /* c_char_p */ ml = &c_char_p_method; @@ -2834,12 +2838,13 @@ PyCData_FromBaseObj(PyObject *type, PyObject *base, Py_ssize_t index, char *adr) cmem = (CDataObject *)((PyTypeObject *)type)->tp_alloc((PyTypeObject *)type, 0); if (cmem == NULL) return NULL; - assert(CDataObject_Check(cmem)); + ctypes_state *state = GLOBAL_STATE(); + assert(CDataObject_Check(state, cmem)); cmem->b_length = dict->length; cmem->b_size = dict->size; if (base) { /* use base's buffer */ - assert(CDataObject_Check(base)); + assert(CDataObject_Check(state, base)); cmem->b_ptr = adr; cmem->b_needsfree = 0; cmem->b_base = (CDataObject *)Py_NewRef(base); @@ -2880,7 +2885,8 @@ PyCData_AtAddress(PyObject *type, void *buf) pd = (CDataObject *)((PyTypeObject *)type)->tp_alloc((PyTypeObject *)type, 0); if (!pd) return NULL; - assert(CDataObject_Check(pd)); + ctypes_state *state = GLOBAL_STATE(); + assert(CDataObject_Check(state, pd)); pd->b_ptr = (char *)buf; pd->b_length = dict->length; pd->b_size = dict->size; @@ -2896,8 +2902,10 @@ int _ctypes_simple_instance(PyObject *obj) { PyTypeObject *type = (PyTypeObject *)obj; - if (PyCSimpleTypeObject_Check(type)) - return type->tp_base != &Simple_Type; + if (PyCSimpleTypeObject_Check(type)) { + ctypes_state *state = GLOBAL_STATE(); + return type->tp_base != state->Simple_Type; + } return 0; } @@ -2919,8 +2927,9 @@ PyCData_get(PyObject *type, GETFUNC getfunc, PyObject *src, Helper function for PyCData_set below. */ static PyObject * -_PyCData_set(CDataObject *dst, PyObject *type, SETFUNC setfunc, PyObject *value, - Py_ssize_t size, char *ptr) +_PyCData_set(ctypes_state *state, CDataObject *dst, PyObject *type, + SETFUNC setfunc, PyObject *value, + Py_ssize_t size, char *ptr) { CDataObject *src; int err; @@ -2928,7 +2937,7 @@ _PyCData_set(CDataObject *dst, PyObject *type, SETFUNC setfunc, PyObject *value, if (setfunc) return setfunc(ptr, value, size); - if (!CDataObject_Check(value)) { + if (!CDataObject_Check(state, value)) { StgDictObject *dict = PyType_stgdict(type); if (dict && dict->setfunc) return dict->setfunc(ptr, value, size); @@ -2946,8 +2955,8 @@ _PyCData_set(CDataObject *dst, PyObject *type, SETFUNC setfunc, PyObject *value, ((PyTypeObject *)type)->tp_name); return NULL; } - result = _PyCData_set(dst, type, setfunc, ob, - size, ptr); + result = _PyCData_set(state, dst, type, setfunc, ob, + size, ptr); Py_DECREF(ob); return result; } else if (value == Py_None && PyCPointerTypeObject_Check(type)) { @@ -2983,7 +2992,7 @@ _PyCData_set(CDataObject *dst, PyObject *type, SETFUNC setfunc, PyObject *value, } if (PyCPointerTypeObject_Check(type) - && ArrayObject_Check(value)) { + && ArrayObject_Check(state, value)) { StgDictObject *p1, *p2; PyObject *keep; p1 = PyObject_stgdict(value); @@ -3032,14 +3041,15 @@ PyCData_set(PyObject *dst, PyObject *type, SETFUNC setfunc, PyObject *value, CDataObject *mem = (CDataObject *)dst; PyObject *result; - if (!CDataObject_Check(dst)) { + ctypes_state *state = GLOBAL_STATE(); + if (!CDataObject_Check(state, dst)) { PyErr_SetString(PyExc_TypeError, "not a ctype instance"); return -1; } - result = _PyCData_set(mem, type, setfunc, value, - size, ptr); + result = _PyCData_set(state, mem, type, setfunc, value, + size, ptr); if (result == NULL) return -1; @@ -3627,7 +3637,8 @@ static PyObject * _byref(PyObject *obj) { PyCArgObject *parg; - if (!CDataObject_Check(obj)) { + ctypes_state *state = GLOBAL_STATE(); + if (!CDataObject_Check(state, obj)) { PyErr_SetString(PyExc_TypeError, "expected CData instance"); return NULL; @@ -4734,11 +4745,11 @@ PyCArrayType_from_ctype(PyObject *itemtype, Py_ssize_t length) ((PyTypeObject *)itemtype)->tp_name, (long)length); #endif - ctypes_state *st = GLOBAL_STATE(); - result = PyObject_CallFunction((PyObject *)st->PyCArrayType_Type, + ctypes_state *state = GLOBAL_STATE(); + result = PyObject_CallFunction((PyObject *)state->PyCArrayType_Type, "s(O){s:n,s:O}", name, - &PyCArray_Type, + state->PyCArray_Type, "_length_", length, "_type_", @@ -4850,7 +4861,8 @@ Simple_repr(CDataObject *self) { PyObject *val, *result; - if (Py_TYPE(self)->tp_base != &Simple_Type) { + ctypes_state *state = GLOBAL_STATE(); + if (Py_TYPE(self)->tp_base != state->Simple_Type) { return PyUnicode_FromFormat("<%s object at %p>", Py_TYPE(self)->tp_name, self); } @@ -5013,7 +5025,8 @@ Pointer_set_contents(CDataObject *self, PyObject *value, void *closure) stgdict = PyObject_stgdict((PyObject *)self); assert(stgdict); /* Cannot be NULL for pointer instances */ assert(stgdict->proto); - if (!CDataObject_Check(value)) { + ctypes_state *state = GLOBAL_STATE(); + if (!CDataObject_Check(state, value)) { int res = PyObject_IsInstance(value, stgdict->proto); if (res == -1) return -1; @@ -5432,7 +5445,8 @@ cast(void *ptr, PyObject *src, PyObject *ctype) It must certainly contain the source objects one. It must contain the source object itself. */ - if (CDataObject_Check(src)) { + ctypes_state *state = GLOBAL_STATE(); + if (CDataObject_Check(state, src)) { CDataObject *obj = (CDataObject *)src; CDataObject *container; @@ -5537,6 +5551,8 @@ _ctypes_add_types(PyObject *mod) CREATE_TYPE(mod, st->PyCArg_Type, &carg_spec, NULL); CREATE_TYPE(mod, st->PyCThunk_Type, &cthunk_spec, NULL); TYPE_READY(&PyCData_Type); + st->PyCData_Type = &PyCData_Type; + /* StgDict is derived from PyDict_Type */ TYPE_READY_BASE(&PyCStgDict_Type, &PyDict_Type); @@ -5561,12 +5577,14 @@ _ctypes_add_types(PyObject *mod) * Classes using a custom metaclass */ - MOD_ADD_TYPE(&Struct_Type, st->PyCStructType_Type, &PyCData_Type); - MOD_ADD_TYPE(&Union_Type, st->UnionType_Type, &PyCData_Type); - MOD_ADD_TYPE(&PyCPointer_Type, st->PyCPointerType_Type, &PyCData_Type); - MOD_ADD_TYPE(&PyCArray_Type, st->PyCArrayType_Type, &PyCData_Type); - MOD_ADD_TYPE(&Simple_Type, st->PyCSimpleType_Type, &PyCData_Type); - MOD_ADD_TYPE(&PyCFuncPtr_Type, st->PyCFuncPtrType_Type, &PyCData_Type); + MOD_ADD_TYPE(&Struct_Type, st->PyCStructType_Type, st->PyCData_Type); + MOD_ADD_TYPE(&Union_Type, st->UnionType_Type, st->PyCData_Type); + MOD_ADD_TYPE(&PyCPointer_Type, st->PyCPointerType_Type, st->PyCData_Type); + MOD_ADD_TYPE(&PyCArray_Type, st->PyCArrayType_Type, st->PyCData_Type); + st->PyCArray_Type = &PyCArray_Type; + MOD_ADD_TYPE(&Simple_Type, st->PyCSimpleType_Type, st->PyCData_Type); + st->Simple_Type = &Simple_Type; + MOD_ADD_TYPE(&PyCFuncPtr_Type, st->PyCFuncPtrType_Type, st->PyCData_Type); /************************************************* * diff --git a/Modules/_ctypes/callbacks.c b/Modules/_ctypes/callbacks.c index 154e9f43983cdb..968674907376bc 100644 --- a/Modules/_ctypes/callbacks.c +++ b/Modules/_ctypes/callbacks.c @@ -144,7 +144,8 @@ static void _CallPythonObject(void *mem, Py_ssize_t i = 0, j = 0, nargs = 0; PyObject *error_object = NULL; int *space; - PyGILState_STATE state = PyGILState_Ensure(); + PyGILState_STATE gil_state = PyGILState_Ensure(); + ctypes_state *state = GLOBAL_STATE(); assert(PyTuple_Check(converters)); nargs = PyTuple_GET_SIZE(converters); @@ -175,7 +176,7 @@ static void _CallPythonObject(void *mem, PrintError("create argument %zd:\n", i); goto Done; } - if (!CDataObject_Check(obj)) { + if (!CDataObject_Check(state, obj)) { Py_DECREF(obj); PrintError("unexpected result of create argument %zd:\n", i); goto Done; @@ -285,7 +286,7 @@ static void _CallPythonObject(void *mem, for (j = 0; j < i; j++) { Py_DECREF(args[j]); } - PyGILState_Release(state); + PyGILState_Release(gil_state); } static void closure_fcn(ffi_cif *cif, diff --git a/Modules/_ctypes/callproc.c b/Modules/_ctypes/callproc.c index 3b11cd7f58ce4b..87ce54971914ec 100644 --- a/Modules/_ctypes/callproc.c +++ b/Modules/_ctypes/callproc.c @@ -1689,8 +1689,10 @@ sizeof_func(PyObject *self, PyObject *obj) if (dict) return PyLong_FromSsize_t(dict->size); - if (CDataObject_Check(obj)) + ctypes_state *state = GLOBAL_STATE(); + if (CDataObject_Check(state, obj)) { return PyLong_FromSsize_t(((CDataObject *)obj)->b_size); + } PyErr_SetString(PyExc_TypeError, "this type has no size"); return NULL; @@ -1744,7 +1746,8 @@ byref(PyObject *self, PyObject *args) if (offset == -1 && PyErr_Occurred()) return NULL; } - if (!CDataObject_Check(obj)) { + ctypes_state *state = GLOBAL_STATE(); + if (!CDataObject_Check(state, obj)) { PyErr_Format(PyExc_TypeError, "byref() argument must be a ctypes instance, not '%s'", Py_TYPE(obj)->tp_name); @@ -1769,7 +1772,8 @@ PyDoc_STRVAR(addressof_doc, static PyObject * addressof(PyObject *self, PyObject *obj) { - if (!CDataObject_Check(obj)) { + ctypes_state *state = GLOBAL_STATE(); + if (!CDataObject_Check(state, obj)) { PyErr_SetString(PyExc_TypeError, "invalid type"); return NULL; @@ -1925,13 +1929,15 @@ create_pointer_type(PyObject *module, PyObject *cls) // found or error return result; } + // not found + ctypes_state *state = GLOBAL_STATE(); if (PyUnicode_CheckExact(cls)) { PyObject *name = PyUnicode_FromFormat("LP_%U", cls); - result = PyObject_CallFunction((PyObject *)Py_TYPE(&PyCPointer_Type), + result = PyObject_CallFunction((PyObject *)Py_TYPE(state->PyCPointer_Type), "N(O){}", name, - &PyCPointer_Type); + state->PyCPointer_Type); if (result == NULL) return result; key = PyLong_FromVoidPtr(result); @@ -1942,10 +1948,10 @@ create_pointer_type(PyObject *module, PyObject *cls) } else if (PyType_Check(cls)) { typ = (PyTypeObject *)cls; PyObject *name = PyUnicode_FromFormat("LP_%s", typ->tp_name); - result = PyObject_CallFunction((PyObject *)Py_TYPE(&PyCPointer_Type), + result = PyObject_CallFunction((PyObject *)Py_TYPE(state->PyCPointer_Type), "N(O){sO}", name, - &PyCPointer_Type, + state->PyCPointer_Type, "_type_", cls); if (result == NULL) return result; diff --git a/Modules/_ctypes/cfield.c b/Modules/_ctypes/cfield.c index bfb40e5c5393fc..451e2d6d8e9ac8 100644 --- a/Modules/_ctypes/cfield.c +++ b/Modules/_ctypes/cfield.c @@ -204,7 +204,8 @@ PyCField_set(CFieldObject *self, PyObject *inst, PyObject *value) { CDataObject *dst; char *ptr; - if (!CDataObject_Check(inst)) { + ctypes_state *state = GLOBAL_STATE(); + if (!CDataObject_Check(state, inst)) { PyErr_SetString(PyExc_TypeError, "not a ctype instance"); return -1; @@ -227,7 +228,8 @@ PyCField_get(CFieldObject *self, PyObject *inst, PyTypeObject *type) if (inst == NULL) { return Py_NewRef(self); } - if (!CDataObject_Check(inst)) { + ctypes_state *state = GLOBAL_STATE(); + if (!CDataObject_Check(state, inst)) { PyErr_SetString(PyExc_TypeError, "not a ctype instance"); return NULL; diff --git a/Modules/_ctypes/ctypes.h b/Modules/_ctypes/ctypes.h index 55e9f777788079..867f287e64cc5e 100644 --- a/Modules/_ctypes/ctypes.h +++ b/Modules/_ctypes/ctypes.h @@ -47,6 +47,11 @@ typedef struct { PyTypeObject *PyCArrayType_Type; PyTypeObject *PyCSimpleType_Type; PyTypeObject *PyCFuncPtrType_Type; + PyTypeObject *PyCData_Type; + PyTypeObject *PyCArray_Type; + PyTypeObject *Simple_Type; + PyTypeObject *PyCPointer_Type; + PyTypeObject *PyCFuncPtr_Type; } ctypes_state; extern ctypes_state global_state; @@ -156,9 +161,8 @@ extern int PyObject_stginfo(PyObject *self, Py_ssize_t *psize, Py_ssize_t *palig -extern PyTypeObject PyCData_Type; -#define CDataObject_CheckExact(v) Py_IS_TYPE(v, &PyCData_Type) -#define CDataObject_Check(v) PyObject_TypeCheck(v, &PyCData_Type) +#define CDataObject_CheckExact(state, v) Py_IS_TYPE(v, (state)->PyCData_Type) +#define CDataObject_Check(state, v) PyObject_TypeCheck(v, (state)->PyCData_Type) #define _CDataObject_HasExternalBuffer(v) ((v)->b_ptr != (char *)&(v)->b_value) #define PyCSimpleTypeObject_CheckExact(v) Py_IS_TYPE(v, GLOBAL_STATE()->PyCSimpleType_Type) @@ -176,15 +180,11 @@ PyCField_FromDesc(PyObject *desc, Py_ssize_t index, extern PyObject *PyCData_AtAddress(PyObject *type, void *buf); extern PyObject *PyCData_FromBytes(PyObject *type, char *data, Py_ssize_t length); -extern PyTypeObject PyCArray_Type; -extern PyTypeObject PyCPointer_Type; -extern PyTypeObject PyCFuncPtr_Type; - #define PyCArrayTypeObject_Check(v) PyObject_TypeCheck(v, GLOBAL_STATE()->PyCArrayType_Type) -#define ArrayObject_Check(v) PyObject_TypeCheck(v, &PyCArray_Type) -#define PointerObject_Check(v) PyObject_TypeCheck(v, &PyCPointer_Type) +#define ArrayObject_Check(state, v) PyObject_TypeCheck(v, (state)->PyCArray_Type) +#define PointerObject_Check(state, v) PyObject_TypeCheck(v, (state)->PyCPointer_Type) #define PyCPointerTypeObject_Check(v) PyObject_TypeCheck(v, GLOBAL_STATE()->PyCPointerType_Type) -#define PyCFuncPtrObject_Check(v) PyObject_TypeCheck(v, &PyCFuncPtr_Type) +#define PyCFuncPtrObject_Check(state, v) PyObject_TypeCheck(v, (state)->PyCFuncPtr_Type) #define PyCFuncPtrTypeObject_Check(v) PyObject_TypeCheck(v, GLOBAL_STATE()->PyCFuncPtrType_Type) #define PyCStructTypeObject_Check(v) PyObject_TypeCheck(v, GLOBAL_STATE()->PyCStructType_Type) From c90c7ae0db3fb855bc67585f2c9a5655dbef99b1 Mon Sep 17 00:00:00 2001 From: Victor Stinner Date: Fri, 19 Jan 2024 16:09:29 +0100 Subject: [PATCH 2/3] Fix Windows build --- Modules/_ctypes/_ctypes.c | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/Modules/_ctypes/_ctypes.c b/Modules/_ctypes/_ctypes.c index a41ce8fc414191..73014a540ca0d6 100644 --- a/Modules/_ctypes/_ctypes.c +++ b/Modules/_ctypes/_ctypes.c @@ -3973,7 +3973,8 @@ PyCFuncPtr_call(PyCFuncPtrObject *self, PyObject *inargs, PyObject *kwds) "native com method call without 'this' parameter"); return NULL; } - if (!CDataObject_Check(this)) { + ctypes_state *state = GLOBAL_STATE(); + if (!CDataObject_Check(state, this)) { PyErr_SetString(PyExc_TypeError, "Expected a COM this pointer as first argument"); return NULL; From 78f9cfb179dec168308d62d21f00d06728ba712a Mon Sep 17 00:00:00 2001 From: Victor Stinner Date: Fri, 19 Jan 2024 16:17:54 +0100 Subject: [PATCH 3/3] Set PyCPointer_Type in state --- Modules/_ctypes/_ctypes.c | 1 + 1 file changed, 1 insertion(+) diff --git a/Modules/_ctypes/_ctypes.c b/Modules/_ctypes/_ctypes.c index 73014a540ca0d6..b1f35357d730e8 100644 --- a/Modules/_ctypes/_ctypes.c +++ b/Modules/_ctypes/_ctypes.c @@ -5581,6 +5581,7 @@ _ctypes_add_types(PyObject *mod) MOD_ADD_TYPE(&Struct_Type, st->PyCStructType_Type, st->PyCData_Type); MOD_ADD_TYPE(&Union_Type, st->UnionType_Type, st->PyCData_Type); MOD_ADD_TYPE(&PyCPointer_Type, st->PyCPointerType_Type, st->PyCData_Type); + st->PyCPointer_Type = &PyCPointer_Type; MOD_ADD_TYPE(&PyCArray_Type, st->PyCArrayType_Type, st->PyCData_Type); st->PyCArray_Type = &PyCArray_Type; MOD_ADD_TYPE(&Simple_Type, st->PyCSimpleType_Type, st->PyCData_Type);