From 61552115de875dcb0f184d5e1a9eef3e7e2509c9 Mon Sep 17 00:00:00 2001 From: Petr Viktorin Date: Fri, 16 Feb 2024 17:33:12 +0100 Subject: [PATCH 01/59] Move paramfunc into a new struct, PyStgInfo In this bootstrap commit, PyStgInfo is part of StgDictObject, but that's a detail hidden by accessor functions. (In the accessors, `state` is currently unused.) --- Modules/_ctypes/_ctypes.c | 43 ++++++++++++++++++++++++++++++----- Modules/_ctypes/callproc.c | 12 +++++++--- Modules/_ctypes/ctypes.h | 19 +++++++++++++++- Modules/_ctypes/stgdict.c | 46 ++++++++++++++++++++++++++++++++++++++ 4 files changed, 111 insertions(+), 9 deletions(-) diff --git a/Modules/_ctypes/_ctypes.c b/Modules/_ctypes/_ctypes.c index 94245ae41afffc..7cd3cd94983bc9 100644 --- a/Modules/_ctypes/_ctypes.c +++ b/Modules/_ctypes/_ctypes.c @@ -562,8 +562,13 @@ StructUnionType_new(PyTypeObject *type, PyObject *args, PyObject *kwds, int isSt Py_DECREF(result); return NULL; } + StgInfo *info = PyStgInfo_Init(st, result); + if (!info) { + Py_DECREF(result); + return NULL; + } - dict->paramfunc = StructUnionType_paramfunc; + info->paramfunc = StructUnionType_paramfunc; if (PyDict_GetItemRef((PyObject *)dict, &_Py_ID(_fields_), &fields) < 0) { Py_DECREF(result); @@ -1054,7 +1059,7 @@ PyCPointerType_new(PyTypeObject *type, PyObject *args, PyObject *kwds) stgdict->align = _ctypes_get_fielddesc("P")->pffi_type->alignment; stgdict->length = 1; stgdict->ffi_type_pointer = ffi_type_pointer; - stgdict->paramfunc = PyCPointerType_paramfunc; + //stgdict->paramfunc = PyCPointerType_paramfunc; stgdict->flags |= TYPEFLAG_ISPOINTER; if (PyDict_GetItemRef(typedict, &_Py_ID(_type_), &proto) < 0) { @@ -1108,6 +1113,13 @@ PyCPointerType_new(PyTypeObject *type, PyObject *args, PyObject *kwds) } Py_SETREF(result->tp_dict, (PyObject *)stgdict); + StgInfo *info = PyStgInfo_Init(st, result); + if (!info) { + Py_DECREF((PyObject *)result); + return NULL; + } + info->paramfunc = PyCPointerType_paramfunc; + return (PyObject *)result; } @@ -1513,7 +1525,7 @@ PyCArrayType_new(PyTypeObject *type, PyObject *args, PyObject *kwds) stgdict->proto = type_attr; type_attr = NULL; - stgdict->paramfunc = &PyCArrayType_paramfunc; + //stgdict->paramfunc = &PyCArrayType_paramfunc; /* Arrays are passed as pointers to function calls. */ stgdict->ffi_type_pointer = ffi_type_pointer; @@ -1524,6 +1536,12 @@ PyCArrayType_new(PyTypeObject *type, PyObject *args, PyObject *kwds) Py_SETREF(result->tp_dict, (PyObject *)stgdict); /* steal the reference */ stgdict = NULL; + StgInfo *info = PyStgInfo_Init(st, result); + if (!info) { + goto error; + } + info->paramfunc = &PyCArrayType_paramfunc; + /* Special case for character arrays. A permanent annoyance: char arrays are also strings! */ @@ -2011,6 +2029,7 @@ PyCSimpleType_new(PyTypeObject *type, PyObject *args, PyObject *kwds) if (!stgdict) { goto error; } + stgdict->ffi_type_pointer = *fmt->pffi_type; stgdict->align = fmt->pffi_type->alignment; stgdict->length = 0; @@ -2029,7 +2048,7 @@ PyCSimpleType_new(PyTypeObject *type, PyObject *args, PyObject *kwds) return NULL; } - stgdict->paramfunc = PyCSimpleType_paramfunc; + //stgdict->paramfunc = PyCSimpleType_paramfunc; /* if (result->tp_base != st->Simple_Type) { stgdict->setfunc = NULL; @@ -2048,6 +2067,12 @@ PyCSimpleType_new(PyTypeObject *type, PyObject *args, PyObject *kwds) } Py_SETREF(result->tp_dict, (PyObject *)stgdict); + StgInfo *info = PyStgInfo_Init(st, result); + if (!info) { + goto error; + } + info->paramfunc = PyCSimpleType_paramfunc; + /* Install from_param class methods in ctypes base classes. Overrides the PyCSimpleType_from_param generic method. */ @@ -2438,7 +2463,8 @@ PyCFuncPtrType_new(PyTypeObject *type, PyObject *args, PyObject *kwds) if (!stgdict) { return NULL; } - stgdict->paramfunc = PyCFuncPtrType_paramfunc; + //stgdict->paramfunc = PyCFuncPtrType_paramfunc; + /* We do NOT expose the function signature in the format string. It is impossible, generally, because the only requirement for the argtypes items is that they have a .from_param method - we do not @@ -2468,6 +2494,13 @@ PyCFuncPtrType_new(PyTypeObject *type, PyObject *args, PyObject *kwds) } Py_SETREF(result->tp_dict, (PyObject *)stgdict); + StgInfo *info = PyStgInfo_Init(st, result); + if (!info) { + Py_DECREF((PyObject *)stgdict); + return NULL; + } + info->paramfunc = PyCFuncPtrType_paramfunc; + if (-1 == make_funcptrtype_dict(stgdict)) { Py_DECREF(result); return NULL; diff --git a/Modules/_ctypes/callproc.c b/Modules/_ctypes/callproc.c index 97d1dbaae03d4f..108d5407562b34 100644 --- a/Modules/_ctypes/callproc.c +++ b/Modules/_ctypes/callproc.c @@ -666,13 +666,20 @@ static int ConvParam(PyObject *obj, Py_ssize_t index, struct argument *pa) { StgDictObject *dict; pa->keep = NULL; /* so we cannot forget it later */ + ctypes_state *st = GLOBAL_STATE(); dict = PyObject_stgdict(obj); + StgInfo *info; + int result = PyStgInfo_FromObject(st, obj, &info); + if (result < 0) { + return -1; + } if (dict) { + assert(info); PyCArgObject *carg; - assert(dict->paramfunc); + assert(info->paramfunc); /* If it has an stgdict, it is a CDataObject */ - carg = dict->paramfunc((CDataObject *)obj); + carg = info->paramfunc((CDataObject *)obj); if (carg == NULL) return -1; pa->ffi_type = carg->pffi_type; @@ -681,7 +688,6 @@ static int ConvParam(PyObject *obj, Py_ssize_t index, struct argument *pa) return 0; } - ctypes_state *st = GLOBAL_STATE(); if (PyCArg_CheckExact(st, obj)) { PyCArgObject *carg = (PyCArgObject *)obj; pa->ffi_type = carg->pffi_type; diff --git a/Modules/_ctypes/ctypes.h b/Modules/_ctypes/ctypes.h index 02f48a9ed55843..cf887b43e33f8f 100644 --- a/Modules/_ctypes/ctypes.h +++ b/Modules/_ctypes/ctypes.h @@ -231,6 +231,22 @@ typedef struct { int anonymous; } CFieldObject; +typedef struct { + int initialized; + PARAMFUNC paramfunc; +} StgInfo; + +// Get a PyCTypeDataObject. These Return -1 on error, 0 if "not found", 1 on OK. +// from a type: +extern int PyStgInfo_FromType(ctypes_state *state, PyObject *obj, StgInfo **result); +// from an instance: +extern int PyStgInfo_FromObject(ctypes_state *state, PyObject *obj, StgInfo **result); +// from either a type or an instance: +extern int PyStgInfo_FromAny(ctypes_state *state, PyObject *obj, StgInfo **result); + +// Initialize StgInfo on a newly created type +extern StgInfo *PyStgInfo_Init(ctypes_state *state, PyTypeObject *type); + /* A subclass of PyDictObject, used as the instance dictionary of ctypes metatypes */ typedef struct { @@ -250,7 +266,7 @@ typedef struct { PyObject *proto; /* Only for Pointer/ArrayObject */ SETFUNC setfunc; /* Only for simple objects */ GETFUNC getfunc; /* Only for simple objects */ - PARAMFUNC paramfunc; + //PARAMFUNC paramfunc; /* Following fields only used by PyCFuncPtrType_Type instances */ PyObject *argtypes; /* tuple of CDataObjects */ @@ -266,6 +282,7 @@ typedef struct { /* Py_ssize_t *strides; */ /* unused in ctypes */ /* Py_ssize_t *suboffsets; */ /* unused in ctypes */ + StgInfo stginfo; } StgDictObject; /**************************************************************** diff --git a/Modules/_ctypes/stgdict.c b/Modules/_ctypes/stgdict.c index 32ee414a7a0cdd..366de053c35d54 100644 --- a/Modules/_ctypes/stgdict.c +++ b/Modules/_ctypes/stgdict.c @@ -16,6 +16,52 @@ #endif #include "ctypes.h" + + +// Get a PyCTypeDataObject. These Return -1 on error, 0 if "not found", 1 on OK. +// from a type: +//int PyStgInfo_FromType(PyObject *obj, StgInfo **result); +// from an instance: +int PyStgInfo_FromObject(ctypes_state *state, PyObject *obj, StgInfo **result) +{ + *result = NULL; + StgDictObject *dict = PyObject_stgdict(obj); + if (!dict) { + return 0; + } + StgInfo *info = &dict->stginfo; + if (!info->initialized) { + PyErr_Format(PyExc_SystemError, "%R StgInfo is not initialized.", obj); + return -1; + } + *result = info; + return 1; +} +// from either a type or an instance: +//int PyStgInfo_FromAny(PyObject *obj, StgInfo **result); + +// Initialize StgInfo on a newly created +StgInfo *PyStgInfo_Init(ctypes_state *state, PyTypeObject *type) +{ + StgDictObject *dict = PyType_stgdict((PyObject *)type); + if (!dict) { + PyErr_Format(PyExc_SystemError, + "'%s' is not a ctypes class.", + type->tp_name); + return NULL; + } + if (dict->stginfo.initialized) { + PyErr_Format(PyExc_SystemError, + "StgInfo of '%s' is already initialized.", + type->tp_name); + return NULL; + } + dict->stginfo.initialized = 1; + return &dict->stginfo; +} + + + /******************************************************************/ /* StdDict - a dictionary subclass, containing additional C accessible fields From 0171c30c03e62bc6aba78cda81ad787c6fefb652 Mon Sep 17 00:00:00 2001 From: Petr Viktorin Date: Fri, 16 Feb 2024 18:05:40 +0100 Subject: [PATCH 02/59] Move the StgInfo out of StgDictObject and into the classes themselves --- Modules/_ctypes/_ctypes.c | 59 +++++++++++++++++++++++++++++++++++---- Modules/_ctypes/ctypes.h | 2 +- Modules/_ctypes/stgdict.c | 19 +++++++------ 3 files changed, 64 insertions(+), 16 deletions(-) diff --git a/Modules/_ctypes/_ctypes.c b/Modules/_ctypes/_ctypes.c index 7cd3cd94983bc9..ad9cf4420f9dd1 100644 --- a/Modules/_ctypes/_ctypes.c +++ b/Modules/_ctypes/_ctypes.c @@ -459,6 +459,48 @@ static PyType_Spec structparam_spec = { .slots = structparam_slots, }; +/* + CType_Type - a base metaclass. Its instances (classes) have a StgInfo. + */ + +static int +CType_Type_traverse(PyObject *self, visitproc visit, void *arg) +{ + Py_VISIT(Py_TYPE(self)); + return 0; +} + +static int +CType_Type_clear(PyObject *self) +{ + return 0; +} + +static void +CType_Type_dealloc(PyObject *self) +{ + PyTypeObject *tp = Py_TYPE(self); + PyObject_GC_UnTrack(self); + (void)CType_Type_clear(self); + tp->tp_free(self); + Py_DECREF(tp); +} + +static PyType_Slot ctype_type_slots[] = { + {Py_tp_traverse, CType_Type_traverse}, + {Py_tp_clear, CType_Type_clear}, + {Py_tp_dealloc, CType_Type_dealloc}, + {0, NULL}, +}; + +static PyType_Spec pyctype_type_spec = { + .name = "_ctypes.CType_Type", + .basicsize = -(Py_ssize_t)sizeof(StgInfo), + .flags = (Py_TPFLAGS_DEFAULT | Py_TPFLAGS_IMMUTABLETYPE | + Py_TPFLAGS_HAVE_GC | Py_TPFLAGS_DISALLOW_INSTANTIATION | + Py_TPFLAGS_BASETYPE ), + .slots = ctype_type_slots, +}; /* PyCStructType_Type - a meta type/class. Creating a new class using this one as @@ -5616,21 +5658,26 @@ _ctypes_add_types(PyObject *mod) /* StgDict is derived from PyDict_Type */ TYPE_READY_BASE(st->PyCStgDict_Type, &PyDict_Type); + // Common Metaclass + CREATE_TYPE(mod, st->PyCType_Type, &pyctype_type_spec, + &PyType_Type); + /************************************************* * * Metaclasses */ CREATE_TYPE(mod, st->PyCStructType_Type, &pycstruct_type_spec, - &PyType_Type); - CREATE_TYPE(mod, st->UnionType_Type, &union_type_spec, &PyType_Type); + st->PyCType_Type); + CREATE_TYPE(mod, st->UnionType_Type, &union_type_spec, + st->PyCType_Type); CREATE_TYPE(mod, st->PyCPointerType_Type, &pycpointer_type_spec, - &PyType_Type); + st->PyCType_Type); CREATE_TYPE(mod, st->PyCArrayType_Type, &pycarray_type_spec, - &PyType_Type); + st->PyCType_Type); CREATE_TYPE(mod, st->PyCSimpleType_Type, &pycsimple_type_spec, - &PyType_Type); + st->PyCType_Type); CREATE_TYPE(mod, st->PyCFuncPtrType_Type, &pycfuncptr_type_spec, - &PyType_Type); + st->PyCType_Type); /************************************************* * diff --git a/Modules/_ctypes/ctypes.h b/Modules/_ctypes/ctypes.h index cf887b43e33f8f..ed231d84fa982f 100644 --- a/Modules/_ctypes/ctypes.h +++ b/Modules/_ctypes/ctypes.h @@ -59,6 +59,7 @@ typedef struct { #ifdef MS_WIN32 PyTypeObject *PyComError_Type; #endif + PyTypeObject *PyCType_Type; } ctypes_state; extern ctypes_state global_state; @@ -282,7 +283,6 @@ typedef struct { /* Py_ssize_t *strides; */ /* unused in ctypes */ /* Py_ssize_t *suboffsets; */ /* unused in ctypes */ - StgInfo stginfo; } StgDictObject; /**************************************************************** diff --git a/Modules/_ctypes/stgdict.c b/Modules/_ctypes/stgdict.c index 366de053c35d54..e8d1d978dcc3ae 100644 --- a/Modules/_ctypes/stgdict.c +++ b/Modules/_ctypes/stgdict.c @@ -25,13 +25,14 @@ int PyStgInfo_FromObject(ctypes_state *state, PyObject *obj, StgInfo **result) { *result = NULL; - StgDictObject *dict = PyObject_stgdict(obj); - if (!dict) { + PyTypeObject *type = Py_TYPE(obj); + if (!PyObject_IsInstance((PyObject *)type, (PyObject *)state->PyCType_Type)) { + // not a ctypes class. return 0; } - StgInfo *info = &dict->stginfo; + StgInfo *info = PyObject_GetTypeData((PyObject *)type, state->PyCType_Type); if (!info->initialized) { - PyErr_Format(PyExc_SystemError, "%R StgInfo is not initialized.", obj); + PyErr_Format(PyExc_SystemError, "%R StgInfo is not initialized.", type); return -1; } *result = info; @@ -43,21 +44,21 @@ int PyStgInfo_FromObject(ctypes_state *state, PyObject *obj, StgInfo **result) // Initialize StgInfo on a newly created StgInfo *PyStgInfo_Init(ctypes_state *state, PyTypeObject *type) { - StgDictObject *dict = PyType_stgdict((PyObject *)type); - if (!dict) { + if (!PyObject_IsInstance((PyObject *)type, (PyObject *)state->PyCType_Type)) { PyErr_Format(PyExc_SystemError, "'%s' is not a ctypes class.", type->tp_name); return NULL; } - if (dict->stginfo.initialized) { + StgInfo *info = PyObject_GetTypeData((PyObject *)type, state->PyCType_Type); + if (info->initialized) { PyErr_Format(PyExc_SystemError, "StgInfo of '%s' is already initialized.", type->tp_name); return NULL; } - dict->stginfo.initialized = 1; - return &dict->stginfo; + info->initialized = 1; + return info; } From acd91e9f27f4cf570eadb08b8e84eab26967cb25 Mon Sep 17 00:00:00 2001 From: Petr Viktorin Date: Mon, 19 Feb 2024 20:22:56 +0100 Subject: [PATCH 03/59] Move StgInfo init/retrieval right after the corresponding StgDict one --- Modules/_ctypes/_ctypes.c | 103 +++++++++++++++++++------------------- 1 file changed, 52 insertions(+), 51 deletions(-) diff --git a/Modules/_ctypes/_ctypes.c b/Modules/_ctypes/_ctypes.c index ad9cf4420f9dd1..af9c9b1dcd8ea3 100644 --- a/Modules/_ctypes/_ctypes.c +++ b/Modules/_ctypes/_ctypes.c @@ -591,6 +591,12 @@ StructUnionType_new(PyTypeObject *type, PyObject *args, PyObject *kwds, int isSt if (!isStruct) { dict->flags |= TYPEFLAG_HASUNION; } + StgInfo *info = PyStgInfo_Init(st, result); + if (!info) { + Py_DECREF(result); + return NULL; + } + /* replace the class dict by our updated stgdict, which holds info about storage requirements of the instances */ if (-1 == PyDict_Update((PyObject *)dict, result->tp_dict)) { @@ -604,11 +610,6 @@ StructUnionType_new(PyTypeObject *type, PyObject *args, PyObject *kwds, int isSt Py_DECREF(result); return NULL; } - StgInfo *info = PyStgInfo_Init(st, result); - if (!info) { - Py_DECREF(result); - return NULL; - } info->paramfunc = StructUnionType_paramfunc; @@ -1087,6 +1088,14 @@ PyCPointerType_new(PyTypeObject *type, PyObject *args, PyObject *kwds) if (!typedict) { return NULL; } + + /* create the new instance (which is a class, + since we are a metatype!) */ + result = (PyTypeObject *)PyType_Type.tp_new(type, args, kwds); + if (result == NULL) { + return NULL; + } + /* stgdict items size, align, length contain info about pointers itself, stgdict->proto has info about the pointed to type! @@ -1095,16 +1104,24 @@ PyCPointerType_new(PyTypeObject *type, PyObject *args, PyObject *kwds) stgdict = (StgDictObject *)_PyObject_CallNoArgs( (PyObject *)st->PyCStgDict_Type); if (!stgdict) { + Py_DECREF((PyObject *)result); + return NULL; + } + StgInfo *stginfo = PyStgInfo_Init(st, result); + if (!stginfo) { + Py_DECREF((PyObject *)result); + Py_DECREF((PyObject *)stgdict); return NULL; } stgdict->size = sizeof(void *); stgdict->align = _ctypes_get_fielddesc("P")->pffi_type->alignment; stgdict->length = 1; stgdict->ffi_type_pointer = ffi_type_pointer; - //stgdict->paramfunc = PyCPointerType_paramfunc; + stginfo->paramfunc = PyCPointerType_paramfunc; stgdict->flags |= TYPEFLAG_ISPOINTER; if (PyDict_GetItemRef(typedict, &_Py_ID(_type_), &proto) < 0) { + Py_DECREF((PyObject *)result); Py_DECREF((PyObject *)stgdict); return NULL; } @@ -1113,6 +1130,7 @@ PyCPointerType_new(PyTypeObject *type, PyObject *args, PyObject *kwds) const char *current_format; if (-1 == PyCPointerType_SetProto(stgdict, proto)) { Py_DECREF(proto); + Py_DECREF((PyObject *)result); Py_DECREF((PyObject *)stgdict); return NULL; } @@ -1134,19 +1152,12 @@ PyCPointerType_new(PyTypeObject *type, PyObject *args, PyObject *kwds) } Py_DECREF(proto); if (stgdict->format == NULL) { + Py_DECREF((PyObject *)result); Py_DECREF((PyObject *)stgdict); return NULL; } } - /* create the new instance (which is a class, - since we are a metatype!) */ - result = (PyTypeObject *)PyType_Type.tp_new(type, args, kwds); - if (result == NULL) { - Py_DECREF((PyObject *)stgdict); - return NULL; - } - /* replace the class dict by our updated spam dict */ if (-1 == PyDict_Update((PyObject *)stgdict, result->tp_dict)) { Py_DECREF(result); @@ -1155,13 +1166,6 @@ PyCPointerType_new(PyTypeObject *type, PyObject *args, PyObject *kwds) } Py_SETREF(result->tp_dict, (PyObject *)stgdict); - StgInfo *info = PyStgInfo_Init(st, result); - if (!info) { - Py_DECREF((PyObject *)result); - return NULL; - } - info->paramfunc = PyCPointerType_paramfunc; - return (PyObject *)result; } @@ -1526,6 +1530,10 @@ PyCArrayType_new(PyTypeObject *type, PyObject *args, PyObject *kwds) if (!stgdict) { goto error; } + StgInfo *stginfo = PyStgInfo_Init(st, result); + if (!stginfo) { + goto error; + } itemdict = PyType_stgdict(type_attr); if (!itemdict) { PyErr_SetString(PyExc_TypeError, @@ -1567,7 +1575,7 @@ PyCArrayType_new(PyTypeObject *type, PyObject *args, PyObject *kwds) stgdict->proto = type_attr; type_attr = NULL; - //stgdict->paramfunc = &PyCArrayType_paramfunc; + stginfo->paramfunc = &PyCArrayType_paramfunc; /* Arrays are passed as pointers to function calls. */ stgdict->ffi_type_pointer = ffi_type_pointer; @@ -1578,12 +1586,6 @@ PyCArrayType_new(PyTypeObject *type, PyObject *args, PyObject *kwds) Py_SETREF(result->tp_dict, (PyObject *)stgdict); /* steal the reference */ stgdict = NULL; - StgInfo *info = PyStgInfo_Init(st, result); - if (!info) { - goto error; - } - info->paramfunc = &PyCArrayType_paramfunc; - /* Special case for character arrays. A permanent annoyance: char arrays are also strings! */ @@ -2071,6 +2073,10 @@ PyCSimpleType_new(PyTypeObject *type, PyObject *args, PyObject *kwds) if (!stgdict) { goto error; } + StgInfo *stginfo = PyStgInfo_Init(st, result); + if (!stginfo) { + goto error; + } stgdict->ffi_type_pointer = *fmt->pffi_type; stgdict->align = fmt->pffi_type->alignment; @@ -2090,7 +2096,7 @@ PyCSimpleType_new(PyTypeObject *type, PyObject *args, PyObject *kwds) return NULL; } - //stgdict->paramfunc = PyCSimpleType_paramfunc; + stginfo->paramfunc = PyCSimpleType_paramfunc; /* if (result->tp_base != st->Simple_Type) { stgdict->setfunc = NULL; @@ -2109,12 +2115,6 @@ PyCSimpleType_new(PyTypeObject *type, PyObject *args, PyObject *kwds) } Py_SETREF(result->tp_dict, (PyObject *)stgdict); - StgInfo *info = PyStgInfo_Init(st, result); - if (!info) { - goto error; - } - info->paramfunc = PyCSimpleType_paramfunc; - /* Install from_param class methods in ctypes base classes. Overrides the PyCSimpleType_from_param generic method. */ @@ -2499,13 +2499,28 @@ PyCFuncPtrType_new(PyTypeObject *type, PyObject *args, PyObject *kwds) PyTypeObject *result; StgDictObject *stgdict; + /* create the new instance (which is a class, + since we are a metatype!) */ + result = (PyTypeObject *)PyType_Type.tp_new(type, args, kwds); + if (result == NULL) { + return NULL; + } + ctypes_state *st = GLOBAL_STATE(); stgdict = (StgDictObject *)_PyObject_CallNoArgs( (PyObject *)st->PyCStgDict_Type); if (!stgdict) { + Py_DECREF(result); return NULL; } - //stgdict->paramfunc = PyCFuncPtrType_paramfunc; + StgInfo *stginfo = PyStgInfo_Init(st, result); + if (!stginfo) { + Py_DECREF(result); + Py_DECREF((PyObject *)stgdict); + return NULL; + } + + stginfo->paramfunc = PyCFuncPtrType_paramfunc; /* We do NOT expose the function signature in the format string. It is impossible, generally, because the only requirement for the @@ -2515,19 +2530,12 @@ PyCFuncPtrType_new(PyTypeObject *type, PyObject *args, PyObject *kwds) */ stgdict->format = _ctypes_alloc_format_string(NULL, "X{}"); if (stgdict->format == NULL) { + Py_DECREF(result); Py_DECREF((PyObject *)stgdict); return NULL; } stgdict->flags |= TYPEFLAG_ISPOINTER; - /* create the new instance (which is a class, - since we are a metatype!) */ - result = (PyTypeObject *)PyType_Type.tp_new(type, args, kwds); - if (result == NULL) { - Py_DECREF((PyObject *)stgdict); - return NULL; - } - /* replace the class dict by our updated storage dict */ if (-1 == PyDict_Update((PyObject *)stgdict, result->tp_dict)) { Py_DECREF(result); @@ -2536,13 +2544,6 @@ PyCFuncPtrType_new(PyTypeObject *type, PyObject *args, PyObject *kwds) } Py_SETREF(result->tp_dict, (PyObject *)stgdict); - StgInfo *info = PyStgInfo_Init(st, result); - if (!info) { - Py_DECREF((PyObject *)stgdict); - return NULL; - } - info->paramfunc = PyCFuncPtrType_paramfunc; - if (-1 == make_funcptrtype_dict(stgdict)) { Py_DECREF(result); return NULL; From 55af50452683725374d280449d299837f42deea3 Mon Sep 17 00:00:00 2001 From: Petr Viktorin Date: Mon, 19 Feb 2024 21:27:07 +0100 Subject: [PATCH 04/59] Move `size` from StgDict to StgInfo --- Modules/_ctypes/_ctypes.c | 172 +++++++++++++++++++++++++++++------- Modules/_ctypes/callbacks.c | 10 ++- Modules/_ctypes/callproc.c | 33 +++++-- Modules/_ctypes/cfield.c | 26 ++++-- Modules/_ctypes/ctypes.h | 7 +- Modules/_ctypes/stgdict.c | 60 ++++++++++--- 6 files changed, 241 insertions(+), 67 deletions(-) diff --git a/Modules/_ctypes/_ctypes.c b/Modules/_ctypes/_ctypes.c index af9c9b1dcd8ea3..7f0cee7a237df1 100644 --- a/Modules/_ctypes/_ctypes.c +++ b/Modules/_ctypes/_ctypes.c @@ -632,8 +632,17 @@ StructUnionType_new(PyTypeObject *type, PyObject *args, PyObject *kwds, int isSt if (basedict == NULL) { return (PyObject *)result; } + + StgInfo *baseinfo; + if (PyStgInfo_FromType(st, (PyObject *)result->tp_base, + &baseinfo) < 0) { + Py_DECREF(result); + return NULL; + } + assert(baseinfo); + /* copy base dict */ - if (-1 == PyCStgDict_clone(dict, basedict)) { + if (-1 == PyCStgDict_clone(dict, basedict, info, baseinfo)) { Py_DECREF(result); return NULL; } @@ -694,6 +703,13 @@ CDataType_from_buffer(PyObject *type, PyObject *args) return NULL; } + ctypes_state *st = GLOBAL_STATE(); + StgInfo *info; + if (PyStgInfo_FromType(st, type, &info) < 0) { + return NULL; + } + assert(info); + if (!PyArg_ParseTuple(args, "O|n:from_buffer", &obj, &offset)) return NULL; @@ -724,11 +740,11 @@ CDataType_from_buffer(PyObject *type, PyObject *args) return NULL; } - if (dict->size > buffer->len - offset) { + if (info->size > buffer->len - offset) { PyErr_Format(PyExc_ValueError, "Buffer size too small " "(%zd instead of at least %zd bytes)", - buffer->len, dict->size + offset); + buffer->len, info->size + offset); Py_DECREF(mv); return NULL; } @@ -771,6 +787,13 @@ CDataType_from_buffer_copy(PyObject *type, PyObject *args) return NULL; } + ctypes_state *st = GLOBAL_STATE(); + StgInfo *info; + if (PyStgInfo_FromType(st, type, &info) < 0) { + return NULL; + } + assert(info); + if (!PyArg_ParseTuple(args, "y*|n:from_buffer_copy", &buffer, &offset)) return NULL; @@ -781,10 +804,10 @@ CDataType_from_buffer_copy(PyObject *type, PyObject *args) return NULL; } - if (dict->size > buffer.len - offset) { + if (info->size > buffer.len - offset) { PyErr_Format(PyExc_ValueError, "Buffer size too small (%zd instead of at least %zd bytes)", - buffer.len, dict->size + offset); + buffer.len, info->size + offset); PyBuffer_Release(&buffer); return NULL; } @@ -798,7 +821,7 @@ CDataType_from_buffer_copy(PyObject *type, PyObject *args) result = GenericPyCData_new((PyTypeObject *)type, NULL, NULL); if (result != NULL) { memcpy(((CDataObject *)result)->b_ptr, - (char *)buffer.buf + offset, dict->size); + (char *)buffer.buf + offset, info->size); } PyBuffer_Release(&buffer); return result; @@ -1113,7 +1136,7 @@ PyCPointerType_new(PyTypeObject *type, PyObject *args, PyObject *kwds) Py_DECREF((PyObject *)stgdict); return NULL; } - stgdict->size = sizeof(void *); + stginfo->size = sizeof(void *); stgdict->align = _ctypes_get_fielddesc("P")->pffi_type->alignment; stgdict->length = 1; stgdict->ffi_type_pointer = ffi_type_pointer; @@ -1541,6 +1564,12 @@ PyCArrayType_new(PyTypeObject *type, PyObject *args, PyObject *kwds) goto error; } + StgInfo *iteminfo; + if (PyStgInfo_FromType(st, type_attr, &iteminfo) < 0) { + goto error; + } + assert(iteminfo); + assert(itemdict->format); stgdict->format = _ctypes_alloc_format_string(NULL, itemdict->format); if (stgdict->format == NULL) @@ -1557,7 +1586,7 @@ PyCArrayType_new(PyTypeObject *type, PyObject *args, PyObject *kwds) sizeof(Py_ssize_t) * (stgdict->ndim - 1)); } - itemsize = itemdict->size; + itemsize = iteminfo->size; if (itemsize != 0 && length > PY_SSIZE_T_MAX / itemsize) { PyErr_SetString(PyExc_OverflowError, "array too large"); @@ -1569,7 +1598,7 @@ PyCArrayType_new(PyTypeObject *type, PyObject *args, PyObject *kwds) if (itemdict->flags & (TYPEFLAG_ISPOINTER | TYPEFLAG_HASPOINTER)) stgdict->flags |= TYPEFLAG_HASPOINTER; - stgdict->size = itemsize * length; + stginfo->size = itemsize * length; stgdict->align = itemalign; stgdict->length = length; stgdict->proto = type_attr; @@ -1963,10 +1992,17 @@ static PyObject *CreateSwappedType(PyTypeObject *type, PyObject *args, PyObject return NULL; } + StgInfo *stginfo = PyStgInfo_Init(st, result); + if (!stginfo) { + Py_DECREF(result); + Py_DECREF((PyObject *)stgdict); + return NULL; + } + stgdict->ffi_type_pointer = *fmt->pffi_type; stgdict->align = fmt->pffi_type->alignment; stgdict->length = 0; - stgdict->size = fmt->pffi_type->size; + stginfo->size = fmt->pffi_type->size; stgdict->setfunc = fmt->setfunc_swapped; stgdict->getfunc = fmt->getfunc_swapped; @@ -2081,7 +2117,7 @@ PyCSimpleType_new(PyTypeObject *type, PyObject *args, PyObject *kwds) stgdict->ffi_type_pointer = *fmt->pffi_type; stgdict->align = fmt->pffi_type->alignment; stgdict->length = 0; - stgdict->size = fmt->pffi_type->size; + stginfo->size = fmt->pffi_type->size; stgdict->setfunc = fmt->setfunc; stgdict->getfunc = fmt->getfunc; #ifdef WORDS_BIGENDIAN @@ -2405,14 +2441,14 @@ converters_from_argtypes(PyObject *ob) } static int -make_funcptrtype_dict(StgDictObject *stgdict) +make_funcptrtype_dict(StgDictObject *stgdict, StgInfo *stginfo) { PyObject *ob; PyObject *converters = NULL; stgdict->align = _ctypes_get_fielddesc("P")->pffi_type->alignment; stgdict->length = 1; - stgdict->size = sizeof(void *); + stginfo->size = sizeof(void *); stgdict->setfunc = NULL; stgdict->getfunc = NULL; stgdict->ffi_type_pointer = ffi_type_pointer; @@ -2544,7 +2580,7 @@ PyCFuncPtrType_new(PyTypeObject *type, PyObject *args, PyObject *kwds) } Py_SETREF(result->tp_dict, (PyObject *)stgdict); - if (-1 == make_funcptrtype_dict(stgdict)) { + if (-1 == make_funcptrtype_dict(stgdict, stginfo)) { Py_DECREF(result); return NULL; } @@ -2747,10 +2783,16 @@ PyCData_NewGetBuffer(PyObject *myself, Py_buffer *view, int flags) CDataObject *self = (CDataObject *)myself; StgDictObject *dict = PyObject_stgdict(myself); PyObject *item_type = PyCData_item_type((PyObject*)Py_TYPE(myself)); - StgDictObject *item_dict = PyType_stgdict(item_type); if (view == NULL) return 0; + ctypes_state *st = GLOBAL_STATE(); + StgInfo *item_info; + if (PyStgInfo_FromType(st, item_type, &item_info) < 0) { + return -1; + } + assert(item_info); + view->buf = self->b_ptr; view->obj = Py_NewRef(myself); view->len = self->b_size; @@ -2759,7 +2801,7 @@ PyCData_NewGetBuffer(PyObject *myself, Py_buffer *view, int flags) view->format = dict->format ? dict->format : "B"; view->ndim = dict->ndim; view->shape = dict->shape; - view->itemsize = item_dict->size; + view->itemsize = item_info->size; view->strides = NULL; view->suboffsets = NULL; view->internal = NULL; @@ -2891,9 +2933,10 @@ PyTypeObject PyCData_Type = { 0, /* tp_free */ }; -static int PyCData_MallocBuffer(CDataObject *obj, StgDictObject *dict) +static int +PyCData_MallocBuffer(CDataObject *obj, StgDictObject *dict, StgInfo *info) { - if ((size_t)dict->size <= sizeof(obj->b_value)) { + if ((size_t)info->size <= sizeof(obj->b_value)) { /* No need to call malloc, can use the default buffer */ obj->b_ptr = (char *)&obj->b_value; /* The b_needsfree flag does not mean that we actually did @@ -2907,15 +2950,15 @@ static int PyCData_MallocBuffer(CDataObject *obj, StgDictObject *dict) /* In python 2.4, and ctypes 0.9.6, the malloc call took about 33% of the creation time for c_int(). */ - obj->b_ptr = (char *)PyMem_Malloc(dict->size); + obj->b_ptr = (char *)PyMem_Malloc(info->size); if (obj->b_ptr == NULL) { PyErr_NoMemory(); return -1; } obj->b_needsfree = 1; - memset(obj->b_ptr, 0, dict->size); + memset(obj->b_ptr, 0, info->size); } - obj->b_size = dict->size; + obj->b_size = info->size; return 0; } @@ -2932,6 +2975,14 @@ PyCData_FromBaseObj(PyObject *type, PyObject *base, Py_ssize_t index, char *adr) "abstract class"); return NULL; } + + ctypes_state *st = GLOBAL_STATE(); + StgInfo *info; + if (PyStgInfo_FromType(st, type, &info) < 0) { + return NULL; + } + assert(info); + dict->flags |= DICTFLAG_FINAL; cmem = (CDataObject *)((PyTypeObject *)type)->tp_alloc((PyTypeObject *)type, 0); if (cmem == NULL) { @@ -2939,7 +2990,7 @@ PyCData_FromBaseObj(PyObject *type, PyObject *base, Py_ssize_t index, char *adr) } assert(CDataObject_Check(GLOBAL_STATE(), cmem)); cmem->b_length = dict->length; - cmem->b_size = dict->size; + cmem->b_size = info->size; if (base) { /* use base's buffer */ assert(CDataObject_Check(GLOBAL_STATE(), base)); cmem->b_ptr = adr; @@ -2947,11 +2998,11 @@ PyCData_FromBaseObj(PyObject *type, PyObject *base, Py_ssize_t index, char *adr) cmem->b_base = (CDataObject *)Py_NewRef(base); cmem->b_index = index; } else { /* copy contents of adr */ - if (-1 == PyCData_MallocBuffer(cmem, dict)) { + if (-1 == PyCData_MallocBuffer(cmem, dict, info)) { Py_DECREF(cmem); return NULL; } - memcpy(cmem->b_ptr, adr, dict->size); + memcpy(cmem->b_ptr, adr, info->size); cmem->b_index = index; } return (PyObject *)cmem; @@ -2977,6 +3028,14 @@ PyCData_AtAddress(PyObject *type, void *buf) "abstract class"); return NULL; } + + ctypes_state *st = GLOBAL_STATE(); + StgInfo *info; + if (PyStgInfo_FromType(st, type, &info) < 0) { + return NULL; + } + assert(info); + dict->flags |= DICTFLAG_FINAL; pd = (CDataObject *)((PyTypeObject *)type)->tp_alloc((PyTypeObject *)type, 0); @@ -2986,7 +3045,7 @@ PyCData_AtAddress(PyObject *type, void *buf) assert(CDataObject_Check(GLOBAL_STATE(), pd)); pd->b_ptr = (char *)buf; pd->b_length = dict->length; - pd->b_size = dict->size; + pd->b_size = info->size; return (PyObject *)pd; } @@ -3170,6 +3229,14 @@ GenericPyCData_new(PyTypeObject *type, PyObject *args, PyObject *kwds) "abstract class"); return NULL; } + + ctypes_state *st = GLOBAL_STATE(); + StgInfo *info; + if (PyStgInfo_FromType(st, (PyObject *)type, &info) < 0) { + return NULL; + } + assert(info); + dict->flags |= DICTFLAG_FINAL; obj = (CDataObject *)type->tp_alloc(type, 0); @@ -3181,7 +3248,7 @@ GenericPyCData_new(PyTypeObject *type, PyObject *args, PyObject *kwds) obj->b_objects = NULL; obj->b_length = dict->length; - if (-1 == PyCData_MallocBuffer(obj, dict)) { + if (-1 == PyCData_MallocBuffer(obj, dict, info)) { Py_DECREF(obj); return NULL; } @@ -4518,10 +4585,17 @@ Array_item(PyObject *myself, Py_ssize_t index) stgdict = PyObject_stgdict((PyObject *)self); assert(stgdict); /* Cannot be NULL for array instances */ + + ctypes_state *st = GLOBAL_STATE(); + StgInfo *stginfo; + if (PyStgInfo_FromObject(st, (PyObject *)self, &stginfo) < 0) { + return NULL; + } + /* Would it be clearer if we got the item size from stgdict->proto's stgdict? */ - size = stgdict->size / stgdict->length; + size = stginfo->size / stgdict->length; offset = index * size; return PyCData_get(stgdict->proto, stgdict->getfunc, (PyObject *)self, @@ -4651,12 +4725,20 @@ Array_ass_item(PyObject *myself, Py_ssize_t index, PyObject *value) stgdict = PyObject_stgdict((PyObject *)self); assert(stgdict); /* Cannot be NULL for array object instances */ + + ctypes_state *st = GLOBAL_STATE(); + StgInfo *stginfo; + if (PyStgInfo_FromObject(st, (PyObject *)self, &stginfo) < 0) { + return -1; + } + assert(stginfo); /* Cannot be NULL for array object instances */ + if (index < 0 || index >= stgdict->length) { PyErr_SetString(PyExc_IndexError, "invalid index"); return -1; } - size = stgdict->size / stgdict->length; + size = stginfo->size / stgdict->length; offset = index * size; ptr = self->b_ptr + offset; @@ -4889,7 +4971,15 @@ Simple_set_value(CDataObject *self, PyObject *value, void *Py_UNUSED(ignored)) } assert(dict); /* Cannot be NULL for CDataObject instances */ assert(dict->setfunc); - result = dict->setfunc(self->b_ptr, value, dict->size); + + ctypes_state *st = GLOBAL_STATE(); + StgInfo *info; + if (PyStgInfo_FromObject(st, (PyObject *)self, &info) < 0) { + return -1; + } + assert(info); + + result = dict->setfunc(self->b_ptr, value, info->size); if (!result) return -1; @@ -5049,8 +5139,16 @@ Pointer_item(PyObject *myself, Py_ssize_t index) assert(itemdict); /* proto is the item type of the pointer, a ctypes type, so this cannot be NULL */ - size = itemdict->size; - offset = index * itemdict->size; + ctypes_state *st = GLOBAL_STATE(); + StgInfo *iteminfo; + if (PyStgInfo_FromType(st, proto, &iteminfo) < 0) { + return NULL; + } + assert(iteminfo); /* proto is the item type of the pointer, a ctypes + type, so this cannot be NULL */ + + size = iteminfo->size; + offset = index * iteminfo->size; return PyCData_get(proto, stgdict->getfunc, (PyObject *)self, index, size, (*(char **)self->b_ptr) + offset); @@ -5087,8 +5185,16 @@ Pointer_ass_item(PyObject *myself, Py_ssize_t index, PyObject *value) assert(itemdict); /* Cannot be NULL because the itemtype of a pointer is always a ctypes type */ - size = itemdict->size; - offset = index * itemdict->size; + ctypes_state *st = GLOBAL_STATE(); + StgInfo *iteminfo; + if (PyStgInfo_FromType(st, proto, &iteminfo) < 0) { + return -1; + } + assert(iteminfo); /* Cannot be NULL because the itemtype of a pointer + is always a ctypes type */ + + size = iteminfo->size; + offset = index * iteminfo->size; return PyCData_set((PyObject *)self, proto, stgdict->setfunc, value, index, size, (*(char **)self->b_ptr) + offset); diff --git a/Modules/_ctypes/callbacks.c b/Modules/_ctypes/callbacks.c index f70479435915ff..03fc36867292df 100644 --- a/Modules/_ctypes/callbacks.c +++ b/Modules/_ctypes/callbacks.c @@ -157,8 +157,14 @@ static void _CallPythonObject(void *mem, StgDictObject *dict; dict = PyType_stgdict(cnv); + StgInfo *info; + if (PyStgInfo_FromType(st, cnv, &info) < 0) { + goto Done; + } + assert(info); + if (dict && dict->getfunc && !_ctypes_simple_instance(cnv)) { - PyObject *v = dict->getfunc(*pArgs, dict->size); + PyObject *v = dict->getfunc(*pArgs, info->size); if (!v) { PrintError("create argument %zd:\n", i); goto Done; @@ -181,7 +187,7 @@ static void _CallPythonObject(void *mem, PrintError("unexpected result of create argument %zd:\n", i); goto Done; } - memcpy(obj->b_ptr, *pArgs, dict->size); + memcpy(obj->b_ptr, *pArgs, info->size); args[i] = (PyObject *)obj; #ifdef MS_WIN32 TryAddRef(dict, obj); diff --git a/Modules/_ctypes/callproc.c b/Modules/_ctypes/callproc.c index 108d5407562b34..396d7c187dd097 100644 --- a/Modules/_ctypes/callproc.c +++ b/Modules/_ctypes/callproc.c @@ -1003,8 +1003,15 @@ static PyObject *GetResult(PyObject *restype, void *result, PyObject *checker) if (dict == NULL) return PyObject_CallFunction(restype, "i", *(int *)result); + ctypes_state *st = GLOBAL_STATE(); + StgInfo *info; + if (PyStgInfo_FromType(st, restype, &info) < 0) { + return NULL; + } + assert(info); + if (dict->getfunc && !_ctypes_simple_instance(restype)) { - retval = dict->getfunc(result, dict->size); + retval = dict->getfunc(result, info->size); /* If restype is py_object (detected by comparing getfunc with O_get), we have to call Py_DECREF because O_get has already called Py_INCREF. @@ -1689,13 +1696,16 @@ PyDoc_STRVAR(sizeof_doc, static PyObject * sizeof_func(PyObject *self, PyObject *obj) { - StgDictObject *dict; + ctypes_state *st = GLOBAL_STATE(); - dict = PyType_stgdict(obj); - if (dict) { - return PyLong_FromSsize_t(dict->size); + StgInfo *info; + if (PyStgInfo_FromType(st, obj, &info) < 0) { + return NULL; } - ctypes_state *st = GLOBAL_STATE(); + if (info) { + return PyLong_FromSsize_t(info->size); + } + if (CDataObject_Check(st, obj)) { return PyLong_FromSsize_t(((CDataObject *)obj)->b_size); } @@ -1844,10 +1854,17 @@ resize(PyObject *self, PyObject *args) "expected ctypes instance"); return NULL; } - if (size < dict->size) { + ctypes_state *st = GLOBAL_STATE(); + StgInfo *info; + int result = PyStgInfo_FromObject(st, (PyObject *)obj, &info); + if (result < 0) { + return NULL; + } + assert(info); + if (size < info->size) { PyErr_Format(PyExc_ValueError, "minimum size is %zd", - dict->size); + info->size); return NULL; } if (obj->b_needsfree == 0) { diff --git a/Modules/_ctypes/cfield.c b/Modules/_ctypes/cfield.c index 1d5b0b14bc39e5..4ba9288e06bfcc 100644 --- a/Modules/_ctypes/cfield.c +++ b/Modules/_ctypes/cfield.c @@ -73,14 +73,22 @@ PyCField_FromDesc(PyObject *desc, Py_ssize_t index, Py_DECREF(self); return NULL; } + + StgInfo *info; + if (PyStgInfo_FromType(st, desc, &info) < 0) { + Py_DECREF(self); + return NULL; + } + assert(info); + if (bitsize /* this is a bitfield request */ && *pfield_size /* we have a bitfield open */ #ifdef MS_WIN32 /* MSVC, GCC with -mms-bitfields */ - && dict->size * 8 == *pfield_size + && info->size * 8 == *pfield_size #else /* GCC */ - && dict->size * 8 <= *pfield_size + && info->size * 8 <= *pfield_size #endif && (*pbitofs + bitsize) <= *pfield_size) { /* continue bit field */ @@ -88,8 +96,8 @@ PyCField_FromDesc(PyObject *desc, Py_ssize_t index, #ifndef MS_WIN32 } else if (bitsize /* this is a bitfield request */ && *pfield_size /* we have a bitfield open */ - && dict->size * 8 >= *pfield_size - && (*pbitofs + bitsize) <= dict->size * 8) { + && info->size * 8 >= *pfield_size + && (*pbitofs + bitsize) <= info->size * 8) { /* expand bit field */ fieldtype = EXPAND_BITFIELD; #endif @@ -97,7 +105,7 @@ PyCField_FromDesc(PyObject *desc, Py_ssize_t index, /* start new bitfield */ fieldtype = NEW_BITFIELD; *pbitofs = 0; - *pfield_size = dict->size * 8; + *pfield_size = info->size * 8; } else { /* not a bit field */ fieldtype = NO_BITFIELD; @@ -105,7 +113,7 @@ PyCField_FromDesc(PyObject *desc, Py_ssize_t index, *pfield_size = 0; } - size = dict->size; + size = info->size; proto = desc; /* Field descriptors for 'c_char * n' are be scpecial cased to @@ -171,10 +179,10 @@ PyCField_FromDesc(PyObject *desc, Py_ssize_t index, break; case EXPAND_BITFIELD: - *poffset += dict->size - *pfield_size/8; - *psize += dict->size - *pfield_size/8; + *poffset += info->size - *pfield_size/8; + *psize += info->size - *pfield_size/8; - *pfield_size = dict->size * 8; + *pfield_size = info->size * 8; if (big_endian) self->size = (bitsize << 16) + *pfield_size - *pbitofs - bitsize; diff --git a/Modules/_ctypes/ctypes.h b/Modules/_ctypes/ctypes.h index ed231d84fa982f..acd317e7e4ae18 100644 --- a/Modules/_ctypes/ctypes.h +++ b/Modules/_ctypes/ctypes.h @@ -234,6 +234,8 @@ typedef struct { typedef struct { int initialized; + Py_ssize_t size; /* number of bytes */ + PARAMFUNC paramfunc; } StgInfo; @@ -260,7 +262,7 @@ typedef struct { too much risk to change that now, and there are other fields which doesn't belong into this structure anyway. Maybe in ctypes 2.0... (ctypes 2000?) */ - Py_ssize_t size; /* number of bytes */ + //Py_ssize_t size; /* number of bytes */ Py_ssize_t align; /* alignment requirements */ Py_ssize_t length; /* number of fields */ ffi_type ffi_type_pointer; @@ -331,7 +333,8 @@ extern StgDictObject *PyType_stgdict(PyObject *obj); /* May return NULL, but does not set an exception! */ extern StgDictObject *PyObject_stgdict(PyObject *self); -extern int PyCStgDict_clone(StgDictObject *src, StgDictObject *dst); +extern int PyCStgDict_clone(StgDictObject *src, StgDictObject *dst, + StgInfo *dst_info, StgInfo *src_info); typedef int(* PPROC)(void); diff --git a/Modules/_ctypes/stgdict.c b/Modules/_ctypes/stgdict.c index e8d1d978dcc3ae..00df76ae4bdd71 100644 --- a/Modules/_ctypes/stgdict.c +++ b/Modules/_ctypes/stgdict.c @@ -19,30 +19,40 @@ // Get a PyCTypeDataObject. These Return -1 on error, 0 if "not found", 1 on OK. -// from a type: -//int PyStgInfo_FromType(PyObject *obj, StgInfo **result); -// from an instance: -int PyStgInfo_FromObject(ctypes_state *state, PyObject *obj, StgInfo **result) +static int +_stginfo_from_type(ctypes_state *state, PyTypeObject *type, StgInfo **result) { *result = NULL; - PyTypeObject *type = Py_TYPE(obj); if (!PyObject_IsInstance((PyObject *)type, (PyObject *)state->PyCType_Type)) { // not a ctypes class. return 0; } StgInfo *info = PyObject_GetTypeData((PyObject *)type, state->PyCType_Type); if (!info->initialized) { - PyErr_Format(PyExc_SystemError, "%R StgInfo is not initialized.", type); - return -1; + //PyErr_Format(PyExc_SystemError, "%R StgInfo is not initialized.", type); + //return -1; } *result = info; return 1; } +// from a type: +int +PyStgInfo_FromType(ctypes_state *state, PyObject *type, StgInfo **result) +{ + return _stginfo_from_type(state, (PyTypeObject *)type, result); +} +// from an instance: +int +PyStgInfo_FromObject(ctypes_state *state, PyObject *obj, StgInfo **result) +{ + return _stginfo_from_type(state, Py_TYPE(obj), result); +} // from either a type or an instance: //int PyStgInfo_FromAny(PyObject *obj, StgInfo **result); // Initialize StgInfo on a newly created -StgInfo *PyStgInfo_Init(ctypes_state *state, PyTypeObject *type) +StgInfo * +PyStgInfo_Init(ctypes_state *state, PyTypeObject *type) { if (!PyObject_IsInstance((PyObject *)type, (PyObject *)state->PyCType_Type)) { PyErr_Format(PyExc_SystemError, @@ -121,7 +131,8 @@ PyCStgDict_sizeof(StgDictObject *self, void *unused) } int -PyCStgDict_clone(StgDictObject *dst, StgDictObject *src) +PyCStgDict_clone(StgDictObject *dst, StgDictObject *src, + StgInfo *dst_info, StgInfo *src_info) { char *d, *s; Py_ssize_t size; @@ -140,6 +151,8 @@ PyCStgDict_clone(StgDictObject *dst, StgDictObject *src) s + sizeof(PyDictObject), sizeof(StgDictObject) - sizeof(PyDictObject)); + memcpy(dst_info, src_info, sizeof(StgInfo)); + Py_XINCREF(dst->proto); Py_XINCREF(dst->argtypes); Py_XINCREF(dst->converters); @@ -509,6 +522,14 @@ PyCStructUnionType_update_stgdict(PyObject *type, PyObject *fields, int isStruct "ctypes state is not initialized"); return -1; } + + ctypes_state *st = GLOBAL_STATE(); + StgInfo *stginfo; + if (PyStgInfo_FromType(st, type, &stginfo) < 0) { + return -1; + } + assert(stginfo); + /* If this structure/union is already marked final we cannot assign _fields_ anymore. */ @@ -531,11 +552,18 @@ PyCStructUnionType_update_stgdict(PyObject *type, PyObject *fields, int isStruct stgdict->flags |= (basedict->flags & (TYPEFLAG_HASUNION | TYPEFLAG_HASBITFIELD)); } + + StgInfo *baseinfo; + if (PyStgInfo_FromType(st, (PyObject *)((PyTypeObject *)type)->tp_base, + &baseinfo) < 0) { + return -1; + } + if (!isStruct) { stgdict->flags |= TYPEFLAG_HASUNION; } if (basedict) { - size = offset = basedict->size; + size = offset = baseinfo->size; align = basedict->align; union_size = 0; total_align = align ? align : 1; @@ -581,7 +609,6 @@ PyCStructUnionType_update_stgdict(PyObject *type, PyObject *fields, int isStruct if (stgdict->format == NULL) return -1; - ctypes_state *st = GLOBAL_STATE(); for (i = 0; i < len; ++i) { PyObject *name = NULL, *desc = NULL; PyObject *pair = PySequence_GetItem(fields, i); @@ -606,6 +633,13 @@ PyCStructUnionType_update_stgdict(PyObject *type, PyObject *fields, int isStruct i); return -1; } + + StgInfo *info; + if (PyStgInfo_FromType(st, desc, &info) < 0) { + Py_DECREF(pair); + return -1; + } + stgdict->ffi_type_pointer.elements[ffi_ofs + i] = &dict->ffi_type_pointer; if (dict->flags & (TYPEFLAG_ISPOINTER | TYPEFLAG_HASPOINTER)) stgdict->flags |= TYPEFLAG_HASPOINTER; @@ -636,7 +670,7 @@ PyCStructUnionType_update_stgdict(PyObject *type, PyObject *fields, int isStruct Py_DECREF(pair); return -1; } - if (bitsize <= 0 || bitsize > dict->size * 8) { + if (bitsize <= 0 || bitsize > info->size * 8) { PyErr_SetString(PyExc_ValueError, "number of bits invalid for bit field"); Py_DECREF(pair); @@ -771,7 +805,7 @@ PyCStructUnionType_update_stgdict(PyObject *type, PyObject *fields, int isStruct unsigned short); stgdict->ffi_type_pointer.size = aligned_size; - stgdict->size = aligned_size; + stginfo->size = aligned_size; stgdict->align = total_align; stgdict->length = ffi_ofs + len; From 27e5c5056b99a3a91770207917b337e52e92e36b Mon Sep 17 00:00:00 2001 From: Petr Viktorin Date: Mon, 19 Feb 2024 21:32:22 +0100 Subject: [PATCH 05/59] Move `align` from StgDict to StgInfo --- Modules/_ctypes/_ctypes.c | 12 ++++++------ Modules/_ctypes/callproc.c | 18 ++++++++---------- Modules/_ctypes/cfield.c | 4 ++-- Modules/_ctypes/ctypes.h | 3 ++- Modules/_ctypes/stgdict.c | 12 +++++++++--- 5 files changed, 27 insertions(+), 22 deletions(-) diff --git a/Modules/_ctypes/_ctypes.c b/Modules/_ctypes/_ctypes.c index 7f0cee7a237df1..60e03f8d7b9c12 100644 --- a/Modules/_ctypes/_ctypes.c +++ b/Modules/_ctypes/_ctypes.c @@ -1137,7 +1137,7 @@ PyCPointerType_new(PyTypeObject *type, PyObject *args, PyObject *kwds) return NULL; } stginfo->size = sizeof(void *); - stgdict->align = _ctypes_get_fielddesc("P")->pffi_type->alignment; + stginfo->align = _ctypes_get_fielddesc("P")->pffi_type->alignment; stgdict->length = 1; stgdict->ffi_type_pointer = ffi_type_pointer; stginfo->paramfunc = PyCPointerType_paramfunc; @@ -1593,13 +1593,13 @@ PyCArrayType_new(PyTypeObject *type, PyObject *args, PyObject *kwds) goto error; } - itemalign = itemdict->align; + itemalign = iteminfo->align; if (itemdict->flags & (TYPEFLAG_ISPOINTER | TYPEFLAG_HASPOINTER)) stgdict->flags |= TYPEFLAG_HASPOINTER; stginfo->size = itemsize * length; - stgdict->align = itemalign; + stginfo->align = itemalign; stgdict->length = length; stgdict->proto = type_attr; type_attr = NULL; @@ -2000,7 +2000,7 @@ static PyObject *CreateSwappedType(PyTypeObject *type, PyObject *args, PyObject } stgdict->ffi_type_pointer = *fmt->pffi_type; - stgdict->align = fmt->pffi_type->alignment; + stginfo->align = fmt->pffi_type->alignment; stgdict->length = 0; stginfo->size = fmt->pffi_type->size; stgdict->setfunc = fmt->setfunc_swapped; @@ -2115,7 +2115,7 @@ PyCSimpleType_new(PyTypeObject *type, PyObject *args, PyObject *kwds) } stgdict->ffi_type_pointer = *fmt->pffi_type; - stgdict->align = fmt->pffi_type->alignment; + stginfo->align = fmt->pffi_type->alignment; stgdict->length = 0; stginfo->size = fmt->pffi_type->size; stgdict->setfunc = fmt->setfunc; @@ -2446,7 +2446,7 @@ make_funcptrtype_dict(StgDictObject *stgdict, StgInfo *stginfo) PyObject *ob; PyObject *converters = NULL; - stgdict->align = _ctypes_get_fielddesc("P")->pffi_type->alignment; + stginfo->align = _ctypes_get_fielddesc("P")->pffi_type->alignment; stgdict->length = 1; stginfo->size = sizeof(void *); stgdict->setfunc = NULL; diff --git a/Modules/_ctypes/callproc.c b/Modules/_ctypes/callproc.c index 396d7c187dd097..fb837839a5db52 100644 --- a/Modules/_ctypes/callproc.c +++ b/Modules/_ctypes/callproc.c @@ -1722,16 +1722,14 @@ PyDoc_STRVAR(alignment_doc, static PyObject * align_func(PyObject *self, PyObject *obj) { - StgDictObject *dict; - - dict = PyType_stgdict(obj); - if (dict) - return PyLong_FromSsize_t(dict->align); - - dict = PyObject_stgdict(obj); - if (dict) - return PyLong_FromSsize_t(dict->align); - + ctypes_state *st = GLOBAL_STATE(); + StgInfo *info; + if (PyStgInfo_FromAny(st, obj, &info) < 0) { + return NULL; + } + if (info) { + return PyLong_FromSsize_t(info->align); + } PyErr_SetString(PyExc_TypeError, "no alignment info"); return NULL; diff --git a/Modules/_ctypes/cfield.c b/Modules/_ctypes/cfield.c index 4ba9288e06bfcc..3ab09adbd722c4 100644 --- a/Modules/_ctypes/cfield.c +++ b/Modules/_ctypes/cfield.c @@ -159,9 +159,9 @@ PyCField_FromDesc(PyObject *desc, Py_ssize_t index, /* fall through */ case NO_BITFIELD: if (pack) - align = min(pack, dict->align); + align = min(pack, info->align); else - align = dict->align; + align = info->align; if (align && *poffset % align) { Py_ssize_t delta = align - (*poffset % align); *psize += delta; diff --git a/Modules/_ctypes/ctypes.h b/Modules/_ctypes/ctypes.h index acd317e7e4ae18..9e0439acc4f517 100644 --- a/Modules/_ctypes/ctypes.h +++ b/Modules/_ctypes/ctypes.h @@ -235,6 +235,7 @@ typedef struct { typedef struct { int initialized; Py_ssize_t size; /* number of bytes */ + Py_ssize_t align; /* alignment requirements */ PARAMFUNC paramfunc; } StgInfo; @@ -263,7 +264,7 @@ typedef struct { belong into this structure anyway. Maybe in ctypes 2.0... (ctypes 2000?) */ //Py_ssize_t size; /* number of bytes */ - Py_ssize_t align; /* alignment requirements */ + //Py_ssize_t align; /* alignment requirements */ Py_ssize_t length; /* number of fields */ ffi_type ffi_type_pointer; PyObject *proto; /* Only for Pointer/ArrayObject */ diff --git a/Modules/_ctypes/stgdict.c b/Modules/_ctypes/stgdict.c index 00df76ae4bdd71..abcfb360b157af 100644 --- a/Modules/_ctypes/stgdict.c +++ b/Modules/_ctypes/stgdict.c @@ -48,7 +48,13 @@ PyStgInfo_FromObject(ctypes_state *state, PyObject *obj, StgInfo **result) return _stginfo_from_type(state, Py_TYPE(obj), result); } // from either a type or an instance: -//int PyStgInfo_FromAny(PyObject *obj, StgInfo **result); +int +PyStgInfo_FromAny(ctypes_state *state, PyObject *obj, StgInfo **result) { + if (PyType_Check(obj)) { + return _stginfo_from_type(state, (PyTypeObject *)obj, result); + } + return _stginfo_from_type(state, Py_TYPE(obj), result); +} // Initialize StgInfo on a newly created StgInfo * @@ -564,7 +570,7 @@ PyCStructUnionType_update_stgdict(PyObject *type, PyObject *fields, int isStruct } if (basedict) { size = offset = baseinfo->size; - align = basedict->align; + align = baseinfo->align; union_size = 0; total_align = align ? align : 1; total_align = max(total_align, forced_alignment); @@ -806,7 +812,7 @@ PyCStructUnionType_update_stgdict(PyObject *type, PyObject *fields, int isStruct stgdict->ffi_type_pointer.size = aligned_size; stginfo->size = aligned_size; - stgdict->align = total_align; + stginfo->align = total_align; stgdict->length = ffi_ofs + len; /* From 01bc637ec1eb9ba79df3847765e18bbf72d7b428 Mon Sep 17 00:00:00 2001 From: Petr Viktorin Date: Mon, 19 Feb 2024 21:46:01 +0100 Subject: [PATCH 06/59] Move `length` from StgDict to StgInfo, except for sizeof calculation --- Modules/_ctypes/_ctypes.c | 35 ++++++++++++++++++++++------------- Modules/_ctypes/ctypes.h | 3 ++- Modules/_ctypes/stgdict.c | 36 +++++++++++++++++++++++++++--------- 3 files changed, 51 insertions(+), 23 deletions(-) diff --git a/Modules/_ctypes/_ctypes.c b/Modules/_ctypes/_ctypes.c index 60e03f8d7b9c12..1818bfb73cf475 100644 --- a/Modules/_ctypes/_ctypes.c +++ b/Modules/_ctypes/_ctypes.c @@ -1138,7 +1138,7 @@ PyCPointerType_new(PyTypeObject *type, PyObject *args, PyObject *kwds) } stginfo->size = sizeof(void *); stginfo->align = _ctypes_get_fielddesc("P")->pffi_type->alignment; - stgdict->length = 1; + stginfo->length = 1; stgdict->ffi_type_pointer = ffi_type_pointer; stginfo->paramfunc = PyCPointerType_paramfunc; stgdict->flags |= TYPEFLAG_ISPOINTER; @@ -1600,7 +1600,7 @@ PyCArrayType_new(PyTypeObject *type, PyObject *args, PyObject *kwds) stginfo->size = itemsize * length; stginfo->align = itemalign; - stgdict->length = length; + stginfo->length = length; stgdict->proto = type_attr; type_attr = NULL; @@ -2001,7 +2001,7 @@ static PyObject *CreateSwappedType(PyTypeObject *type, PyObject *args, PyObject stgdict->ffi_type_pointer = *fmt->pffi_type; stginfo->align = fmt->pffi_type->alignment; - stgdict->length = 0; + stginfo->length = 0; stginfo->size = fmt->pffi_type->size; stgdict->setfunc = fmt->setfunc_swapped; stgdict->getfunc = fmt->getfunc_swapped; @@ -2116,7 +2116,7 @@ PyCSimpleType_new(PyTypeObject *type, PyObject *args, PyObject *kwds) stgdict->ffi_type_pointer = *fmt->pffi_type; stginfo->align = fmt->pffi_type->alignment; - stgdict->length = 0; + stginfo->length = 0; stginfo->size = fmt->pffi_type->size; stgdict->setfunc = fmt->setfunc; stgdict->getfunc = fmt->getfunc; @@ -2447,7 +2447,7 @@ make_funcptrtype_dict(StgDictObject *stgdict, StgInfo *stginfo) PyObject *converters = NULL; stginfo->align = _ctypes_get_fielddesc("P")->pffi_type->alignment; - stgdict->length = 1; + stginfo->length = 1; stginfo->size = sizeof(void *); stgdict->setfunc = NULL; stgdict->getfunc = NULL; @@ -2989,7 +2989,7 @@ PyCData_FromBaseObj(PyObject *type, PyObject *base, Py_ssize_t index, char *adr) return NULL; } assert(CDataObject_Check(GLOBAL_STATE(), cmem)); - cmem->b_length = dict->length; + cmem->b_length = info->length; cmem->b_size = info->size; if (base) { /* use base's buffer */ assert(CDataObject_Check(GLOBAL_STATE(), base)); @@ -3044,7 +3044,7 @@ PyCData_AtAddress(PyObject *type, void *buf) } assert(CDataObject_Check(GLOBAL_STATE(), pd)); pd->b_ptr = (char *)buf; - pd->b_length = dict->length; + pd->b_length = info->length; pd->b_size = info->size; return (PyObject *)pd; } @@ -3246,7 +3246,7 @@ GenericPyCData_new(PyTypeObject *type, PyObject *args, PyObject *kwds) obj->b_base = NULL; obj->b_index = 0; obj->b_objects = NULL; - obj->b_length = dict->length; + obj->b_length = info->length; if (-1 == PyCData_MallocBuffer(obj, dict, info)) { Py_DECREF(obj); @@ -4380,6 +4380,15 @@ _init_pos_args(PyObject *self, PyTypeObject *type, } dict = PyType_stgdict((PyObject *)type); + assert(dict); + + ctypes_state *st = GLOBAL_STATE(); + StgInfo *info; + if (PyStgInfo_FromType(st, (PyObject *)type, &info) < 0) { + return -1; + } + assert(info); + fields = PyDict_GetItemWithError((PyObject *)dict, &_Py_ID(_fields_)); if (fields == NULL) { if (PyErr_Occurred()) { @@ -4389,7 +4398,7 @@ _init_pos_args(PyObject *self, PyTypeObject *type, } for (i = index; - i < dict->length && i < PyTuple_GET_SIZE(args); + i < info->length && i < PyTuple_GET_SIZE(args); ++i) { PyObject *pair = PySequence_GetItem(fields, i - index); PyObject *name, *val; @@ -4422,7 +4431,7 @@ _init_pos_args(PyObject *self, PyTypeObject *type, if (res == -1) return -1; } - return dict->length; + return info->length; } static int @@ -4595,7 +4604,7 @@ Array_item(PyObject *myself, Py_ssize_t index) /* Would it be clearer if we got the item size from stgdict->proto's stgdict? */ - size = stginfo->size / stgdict->length; + size = stginfo->size / stginfo->length; offset = index * size; return PyCData_get(stgdict->proto, stgdict->getfunc, (PyObject *)self, @@ -4733,12 +4742,12 @@ Array_ass_item(PyObject *myself, Py_ssize_t index, PyObject *value) } assert(stginfo); /* Cannot be NULL for array object instances */ - if (index < 0 || index >= stgdict->length) { + if (index < 0 || index >= stginfo->length) { PyErr_SetString(PyExc_IndexError, "invalid index"); return -1; } - size = stginfo->size / stgdict->length; + size = stginfo->size / stginfo->length; offset = index * size; ptr = self->b_ptr + offset; diff --git a/Modules/_ctypes/ctypes.h b/Modules/_ctypes/ctypes.h index 9e0439acc4f517..054341ba979df2 100644 --- a/Modules/_ctypes/ctypes.h +++ b/Modules/_ctypes/ctypes.h @@ -236,6 +236,7 @@ typedef struct { int initialized; Py_ssize_t size; /* number of bytes */ Py_ssize_t align; /* alignment requirements */ + Py_ssize_t length; /* number of fields */ PARAMFUNC paramfunc; } StgInfo; @@ -265,7 +266,7 @@ typedef struct { */ //Py_ssize_t size; /* number of bytes */ //Py_ssize_t align; /* alignment requirements */ - Py_ssize_t length; /* number of fields */ + //Py_ssize_t length; /* number of fields */ ffi_type ffi_type_pointer; PyObject *proto; /* Only for Pointer/ArrayObject */ SETFUNC setfunc; /* Only for simple objects */ diff --git a/Modules/_ctypes/stgdict.c b/Modules/_ctypes/stgdict.c index abcfb360b157af..231d999e6478c6 100644 --- a/Modules/_ctypes/stgdict.c +++ b/Modules/_ctypes/stgdict.c @@ -185,7 +185,7 @@ PyCStgDict_clone(StgDictObject *dst, StgDictObject *src, if (src->ffi_type_pointer.elements == NULL) return 0; - size = sizeof(ffi_type *) * (src->length + 1); + size = sizeof(ffi_type *) * (src_info->length + 1); dst->ffi_type_pointer.elements = PyMem_Malloc(size); if (dst->ffi_type_pointer.elements == NULL) { PyErr_NoMemory(); @@ -575,19 +575,19 @@ PyCStructUnionType_update_stgdict(PyObject *type, PyObject *fields, int isStruct total_align = align ? align : 1; total_align = max(total_align, forced_alignment); stgdict->ffi_type_pointer.type = FFI_TYPE_STRUCT; - stgdict->ffi_type_pointer.elements = PyMem_New(ffi_type *, basedict->length + len + 1); + stgdict->ffi_type_pointer.elements = PyMem_New(ffi_type *, baseinfo->length + len + 1); if (stgdict->ffi_type_pointer.elements == NULL) { PyErr_NoMemory(); return -1; } memset(stgdict->ffi_type_pointer.elements, 0, - sizeof(ffi_type *) * (basedict->length + len + 1)); - if (basedict->length > 0) { + sizeof(ffi_type *) * (baseinfo->length + len + 1)); + if (baseinfo->length > 0) { memcpy(stgdict->ffi_type_pointer.elements, basedict->ffi_type_pointer.elements, - sizeof(ffi_type *) * (basedict->length)); + sizeof(ffi_type *) * (baseinfo->length)); } - ffi_ofs = basedict->length; + ffi_ofs = baseinfo->length; } else { offset = 0; size = 0; @@ -645,6 +645,7 @@ PyCStructUnionType_update_stgdict(PyObject *type, PyObject *fields, int isStruct Py_DECREF(pair); return -1; } + assert(info); stgdict->ffi_type_pointer.elements[ffi_ofs + i] = &dict->ffi_type_pointer; if (dict->flags & (TYPEFLAG_ISPOINTER | TYPEFLAG_HASPOINTER)) @@ -813,7 +814,7 @@ PyCStructUnionType_update_stgdict(PyObject *type, PyObject *fields, int isStruct stginfo->size = aligned_size; stginfo->align = total_align; - stgdict->length = ffi_ofs + len; + stginfo->length = ffi_ofs + len; /* * The value of MAX_STRUCT_SIZE depends on the platform Python is running on. @@ -924,13 +925,21 @@ PyCStructUnionType_update_stgdict(PyObject *type, PyObject *fields, int isStruct i); return -1; } + + StgInfo *info; + if (PyStgInfo_FromType(st, desc, &info) < 0) { + Py_DECREF(pair); + return -1; + } + assert(info); + if (!PyCArrayTypeObject_Check(st, desc)) { /* Not an array. Just need an ffi_type pointer. */ num_ffi_type_pointers++; } else { /* It's an array. */ - Py_ssize_t length = dict->length; + Py_ssize_t length = info->length; StgDictObject *edict; edict = PyType_stgdict(dict->proto); @@ -1023,13 +1032,22 @@ PyCStructUnionType_update_stgdict(PyObject *type, PyObject *fields, int isStruct i); return -1; } + + StgInfo *info; + if (PyStgInfo_FromType(st, desc, &info) < 0) { + Py_DECREF(pair); + PyMem_Free(type_block); + return -1; + } + assert(info); + assert(element_index < (ffi_ofs + len)); /* will be used below */ if (!PyCArrayTypeObject_Check(st, desc)) { /* Not an array. Just copy over the element ffi_type. */ element_types[element_index++] = &dict->ffi_type_pointer; } else { - Py_ssize_t length = dict->length; + Py_ssize_t length = info->length; StgDictObject *edict; edict = PyType_stgdict(dict->proto); From c8017e92c6dbe9c04544780f93bcbd51bab5a478 Mon Sep 17 00:00:00 2001 From: Petr Viktorin Date: Mon, 19 Feb 2024 22:01:55 +0100 Subject: [PATCH 07/59] Move part of the sizeof calculation --- Modules/_ctypes/_ctypes.c | 28 ++++++++++++++++++++++++++++ Modules/_ctypes/stgdict.c | 2 -- 2 files changed, 28 insertions(+), 2 deletions(-) diff --git a/Modules/_ctypes/_ctypes.c b/Modules/_ctypes/_ctypes.c index 1818bfb73cf475..202dc09a95d1dc 100644 --- a/Modules/_ctypes/_ctypes.c +++ b/Modules/_ctypes/_ctypes.c @@ -486,10 +486,38 @@ CType_Type_dealloc(PyObject *self) Py_DECREF(tp); } +static PyObject * +CType_Type_sizeof(PyObject *self) { + Py_ssize_t size = Py_TYPE(self)->tp_basicsize; + size += Py_TYPE(self)->tp_itemsize * Py_SIZE(self); + + ctypes_state *st = GLOBAL_STATE(); + StgInfo *info; + if (PyStgInfo_FromType(st, self, &info) < 0) { + return NULL; + } + if (info) { + StgDictObject *dict = PyType_stgdict(self); + assert(dict); + if (dict->ffi_type_pointer.elements) { + size += (info->length + 1) * sizeof(ffi_type *); + } + } + + return PyLong_FromSsize_t(size); +} + +static PyMethodDef ctype_methods[] = { + {"__sizeof__", _PyCFunction_CAST(CType_Type_sizeof), + METH_NOARGS, PyDoc_STR("Return memory consumption of the type object.")}, + {0}, +}; + static PyType_Slot ctype_type_slots[] = { {Py_tp_traverse, CType_Type_traverse}, {Py_tp_clear, CType_Type_clear}, {Py_tp_dealloc, CType_Type_dealloc}, + {Py_tp_methods, ctype_methods}, {0, NULL}, }; diff --git a/Modules/_ctypes/stgdict.c b/Modules/_ctypes/stgdict.c index 231d999e6478c6..ae5e5cf0b27d1b 100644 --- a/Modules/_ctypes/stgdict.c +++ b/Modules/_ctypes/stgdict.c @@ -131,8 +131,6 @@ PyCStgDict_sizeof(StgDictObject *self, void *unused) if (self->format) res += strlen(self->format) + 1; res += self->ndim * sizeof(Py_ssize_t); - if (self->ffi_type_pointer.elements) - res += (self->length + 1) * sizeof(ffi_type *); return PyLong_FromSsize_t(res); } From 40b012fde04dc79adf85162a5c27d48a72e2e7ba Mon Sep 17 00:00:00 2001 From: Petr Viktorin Date: Mon, 19 Feb 2024 22:29:23 +0100 Subject: [PATCH 08/59] Move `ffi_type_pointer` from StgDict to StgInfo --- Modules/_ctypes/_ctypes.c | 36 ++++++++++++------- Modules/_ctypes/callbacks.c | 9 ++++- Modules/_ctypes/callproc.c | 25 ++++++++----- Modules/_ctypes/ctypes.h | 3 +- Modules/_ctypes/stgdict.c | 72 ++++++++++++++++++------------------- 5 files changed, 86 insertions(+), 59 deletions(-) diff --git a/Modules/_ctypes/_ctypes.c b/Modules/_ctypes/_ctypes.c index 202dc09a95d1dc..43b7ce40d1084a 100644 --- a/Modules/_ctypes/_ctypes.c +++ b/Modules/_ctypes/_ctypes.c @@ -479,6 +479,15 @@ CType_Type_clear(PyObject *self) static void CType_Type_dealloc(PyObject *self) { + ctypes_state *st = GLOBAL_STATE(); + StgInfo *info; + if (PyStgInfo_FromType(st, self, &info) < 0) { + // ignore exception + } + if (info) { + PyMem_Free(info->ffi_type_pointer.elements); + } + PyTypeObject *tp = Py_TYPE(self); PyObject_GC_UnTrack(self); (void)CType_Type_clear(self); @@ -497,9 +506,7 @@ CType_Type_sizeof(PyObject *self) { return NULL; } if (info) { - StgDictObject *dict = PyType_stgdict(self); - assert(dict); - if (dict->ffi_type_pointer.elements) { + if (info->ffi_type_pointer.elements) { size += (info->length + 1) * sizeof(ffi_type *); } } @@ -542,7 +549,6 @@ StructUnionType_paramfunc(CDataObject *self) { PyCArgObject *parg; PyObject *obj; - StgDictObject *stgdict; void *ptr; if ((size_t)self->b_size > sizeof(void*)) { @@ -577,10 +583,16 @@ StructUnionType_paramfunc(CDataObject *self) return NULL; } + ctypes_state *st = GLOBAL_STATE(); + StgInfo *stginfo; + if (PyStgInfo_FromObject(st, (PyObject *)self, &stginfo) < 0) { + Py_DECREF(obj); + return NULL; + } + assert(stginfo); /* Cannot be NULL for structure/union instances */ + parg->tag = 'V'; - stgdict = PyObject_stgdict((PyObject *)self); - assert(stgdict); /* Cannot be NULL for structure/union instances */ - parg->pffi_type = &stgdict->ffi_type_pointer; + parg->pffi_type = &stginfo->ffi_type_pointer; parg->value.p = ptr; parg->size = self->b_size; parg->obj = obj; @@ -1167,7 +1179,7 @@ PyCPointerType_new(PyTypeObject *type, PyObject *args, PyObject *kwds) stginfo->size = sizeof(void *); stginfo->align = _ctypes_get_fielddesc("P")->pffi_type->alignment; stginfo->length = 1; - stgdict->ffi_type_pointer = ffi_type_pointer; + stginfo->ffi_type_pointer = ffi_type_pointer; stginfo->paramfunc = PyCPointerType_paramfunc; stgdict->flags |= TYPEFLAG_ISPOINTER; @@ -1635,7 +1647,7 @@ PyCArrayType_new(PyTypeObject *type, PyObject *args, PyObject *kwds) stginfo->paramfunc = &PyCArrayType_paramfunc; /* Arrays are passed as pointers to function calls. */ - stgdict->ffi_type_pointer = ffi_type_pointer; + stginfo->ffi_type_pointer = ffi_type_pointer; /* replace the class dict by our updated spam dict */ if (-1 == PyDict_Update((PyObject *)stgdict, result->tp_dict)) @@ -2027,7 +2039,7 @@ static PyObject *CreateSwappedType(PyTypeObject *type, PyObject *args, PyObject return NULL; } - stgdict->ffi_type_pointer = *fmt->pffi_type; + stginfo->ffi_type_pointer = *fmt->pffi_type; stginfo->align = fmt->pffi_type->alignment; stginfo->length = 0; stginfo->size = fmt->pffi_type->size; @@ -2142,7 +2154,7 @@ PyCSimpleType_new(PyTypeObject *type, PyObject *args, PyObject *kwds) goto error; } - stgdict->ffi_type_pointer = *fmt->pffi_type; + stginfo->ffi_type_pointer = *fmt->pffi_type; stginfo->align = fmt->pffi_type->alignment; stginfo->length = 0; stginfo->size = fmt->pffi_type->size; @@ -2479,7 +2491,7 @@ make_funcptrtype_dict(StgDictObject *stgdict, StgInfo *stginfo) stginfo->size = sizeof(void *); stgdict->setfunc = NULL; stgdict->getfunc = NULL; - stgdict->ffi_type_pointer = ffi_type_pointer; + stginfo->ffi_type_pointer = ffi_type_pointer; if (PyDict_GetItemRef((PyObject *)stgdict, &_Py_ID(_flags_), &ob) < 0) { return -1; diff --git a/Modules/_ctypes/callbacks.c b/Modules/_ctypes/callbacks.c index 03fc36867292df..284d0a4cd9a2e4 100644 --- a/Modules/_ctypes/callbacks.c +++ b/Modules/_ctypes/callbacks.c @@ -379,13 +379,20 @@ CThunkObject *_ctypes_alloc_callback(PyObject *callable, p->ffi_restype = &ffi_type_void; } else { StgDictObject *dict = PyType_stgdict(restype); + + StgInfo *info; + if (PyStgInfo_FromType(st, restype, &info) < 0) { + goto error; + } + if (dict == NULL || dict->setfunc == NULL) { PyErr_SetString(PyExc_TypeError, "invalid result type for callback function"); goto error; } + assert(info); p->setfunc = dict->setfunc; - p->ffi_restype = &dict->ffi_type_pointer; + p->ffi_restype = &info->ffi_type_pointer; } cc = FFI_DEFAULT_ABI; diff --git a/Modules/_ctypes/callproc.c b/Modules/_ctypes/callproc.c index fb837839a5db52..3494e8ef6dd92b 100644 --- a/Modules/_ctypes/callproc.c +++ b/Modules/_ctypes/callproc.c @@ -784,26 +784,30 @@ int can_return_struct_as_sint64(size_t s) #endif +// returns NULL with exception set on error ffi_type *_ctypes_get_ffi_type(PyObject *obj) { - StgDictObject *dict; - if (obj == NULL) - return &ffi_type_sint; - dict = PyType_stgdict(obj); - if (dict == NULL) + ctypes_state *st = GLOBAL_STATE(); + StgInfo *info; + if (PyStgInfo_FromType(st, obj, &info) < 0) { + return NULL; + } + + if (info == NULL) { return &ffi_type_sint; + } #if defined(MS_WIN32) && !defined(_WIN32_WCE) /* This little trick works correctly with MSVC. It returns small structures in registers */ - if (dict->ffi_type_pointer.type == FFI_TYPE_STRUCT) { - if (can_return_struct_as_int(dict->ffi_type_pointer.size)) + if (info->ffi_type_pointer.type == FFI_TYPE_STRUCT) { + if (can_return_struct_as_int(info->ffi_type_pointer.size)) return &ffi_type_sint32; - else if (can_return_struct_as_sint64 (dict->ffi_type_pointer.size)) + else if (can_return_struct_as_sint64 (info->ffi_type_pointer.size)) return &ffi_type_sint64; } #endif - return &dict->ffi_type_pointer; + return &info->ffi_type_pointer; } @@ -1253,6 +1257,9 @@ PyObject *_ctypes_callproc(PPROC pProc, } else { rtype = _ctypes_get_ffi_type(restype); } + if (!rtype) { + goto cleanup; + } resbuf = alloca(max(rtype->size, sizeof(ffi_arg))); diff --git a/Modules/_ctypes/ctypes.h b/Modules/_ctypes/ctypes.h index 054341ba979df2..6204b5534f8bc5 100644 --- a/Modules/_ctypes/ctypes.h +++ b/Modules/_ctypes/ctypes.h @@ -237,6 +237,7 @@ typedef struct { Py_ssize_t size; /* number of bytes */ Py_ssize_t align; /* alignment requirements */ Py_ssize_t length; /* number of fields */ + ffi_type ffi_type_pointer; PARAMFUNC paramfunc; } StgInfo; @@ -267,7 +268,7 @@ typedef struct { //Py_ssize_t size; /* number of bytes */ //Py_ssize_t align; /* alignment requirements */ //Py_ssize_t length; /* number of fields */ - ffi_type ffi_type_pointer; + //ffi_type ffi_type_pointer; PyObject *proto; /* Only for Pointer/ArrayObject */ SETFUNC setfunc; /* Only for simple objects */ GETFUNC getfunc; /* Only for simple objects */ diff --git a/Modules/_ctypes/stgdict.c b/Modules/_ctypes/stgdict.c index ae5e5cf0b27d1b..d91ceeb3f5a514 100644 --- a/Modules/_ctypes/stgdict.c +++ b/Modules/_ctypes/stgdict.c @@ -117,7 +117,6 @@ PyCStgDict_dealloc(StgDictObject *self) PyCStgDict_clear(self); PyMem_Free(self->format); PyMem_Free(self->shape); - PyMem_Free(self->ffi_type_pointer.elements); PyDict_Type.tp_dealloc((PyObject *)self); } @@ -142,12 +141,12 @@ PyCStgDict_clone(StgDictObject *dst, StgDictObject *src, Py_ssize_t size; PyCStgDict_clear(dst); - PyMem_Free(dst->ffi_type_pointer.elements); + PyMem_Free(dst_info->ffi_type_pointer.elements); PyMem_Free(dst->format); dst->format = NULL; PyMem_Free(dst->shape); dst->shape = NULL; - dst->ffi_type_pointer.elements = NULL; + dst_info->ffi_type_pointer.elements = NULL; d = (char *)dst; s = (char *)src; @@ -181,16 +180,16 @@ PyCStgDict_clone(StgDictObject *dst, StgDictObject *src, sizeof(Py_ssize_t) * src->ndim); } - if (src->ffi_type_pointer.elements == NULL) + if (src_info->ffi_type_pointer.elements == NULL) return 0; size = sizeof(ffi_type *) * (src_info->length + 1); - dst->ffi_type_pointer.elements = PyMem_Malloc(size); - if (dst->ffi_type_pointer.elements == NULL) { + dst_info->ffi_type_pointer.elements = PyMem_Malloc(size); + if (dst_info->ffi_type_pointer.elements == NULL) { PyErr_NoMemory(); return -1; } - memcpy(dst->ffi_type_pointer.elements, - src->ffi_type_pointer.elements, + memcpy(dst_info->ffi_type_pointer.elements, + src_info->ffi_type_pointer.elements, size); return 0; } @@ -548,8 +547,8 @@ PyCStructUnionType_update_stgdict(PyObject *type, PyObject *fields, int isStruct stgdict->format = NULL; } - if (stgdict->ffi_type_pointer.elements) - PyMem_Free(stgdict->ffi_type_pointer.elements); + if (stginfo->ffi_type_pointer.elements) + PyMem_Free(stginfo->ffi_type_pointer.elements); basedict = PyType_stgdict((PyObject *)((PyTypeObject *)type)->tp_base); if (basedict) { @@ -572,17 +571,17 @@ PyCStructUnionType_update_stgdict(PyObject *type, PyObject *fields, int isStruct union_size = 0; total_align = align ? align : 1; total_align = max(total_align, forced_alignment); - stgdict->ffi_type_pointer.type = FFI_TYPE_STRUCT; - stgdict->ffi_type_pointer.elements = PyMem_New(ffi_type *, baseinfo->length + len + 1); - if (stgdict->ffi_type_pointer.elements == NULL) { + stginfo->ffi_type_pointer.type = FFI_TYPE_STRUCT; + stginfo->ffi_type_pointer.elements = PyMem_New(ffi_type *, baseinfo->length + len + 1); + if (stginfo->ffi_type_pointer.elements == NULL) { PyErr_NoMemory(); return -1; } - memset(stgdict->ffi_type_pointer.elements, 0, + memset(stginfo->ffi_type_pointer.elements, 0, sizeof(ffi_type *) * (baseinfo->length + len + 1)); if (baseinfo->length > 0) { - memcpy(stgdict->ffi_type_pointer.elements, - basedict->ffi_type_pointer.elements, + memcpy(stginfo->ffi_type_pointer.elements, + baseinfo->ffi_type_pointer.elements, sizeof(ffi_type *) * (baseinfo->length)); } ffi_ofs = baseinfo->length; @@ -592,13 +591,13 @@ PyCStructUnionType_update_stgdict(PyObject *type, PyObject *fields, int isStruct align = 0; union_size = 0; total_align = forced_alignment; - stgdict->ffi_type_pointer.type = FFI_TYPE_STRUCT; - stgdict->ffi_type_pointer.elements = PyMem_New(ffi_type *, len + 1); - if (stgdict->ffi_type_pointer.elements == NULL) { + stginfo->ffi_type_pointer.type = FFI_TYPE_STRUCT; + stginfo->ffi_type_pointer.elements = PyMem_New(ffi_type *, len + 1); + if (stginfo->ffi_type_pointer.elements == NULL) { PyErr_NoMemory(); return -1; } - memset(stgdict->ffi_type_pointer.elements, 0, + memset(stginfo->ffi_type_pointer.elements, 0, sizeof(ffi_type *) * (len + 1)); ffi_ofs = 0; } @@ -645,14 +644,14 @@ PyCStructUnionType_update_stgdict(PyObject *type, PyObject *fields, int isStruct } assert(info); - stgdict->ffi_type_pointer.elements[ffi_ofs + i] = &dict->ffi_type_pointer; + stginfo->ffi_type_pointer.elements[ffi_ofs + i] = &info->ffi_type_pointer; if (dict->flags & (TYPEFLAG_ISPOINTER | TYPEFLAG_HASPOINTER)) stgdict->flags |= TYPEFLAG_HASPOINTER; stgdict->flags |= dict->flags & (TYPEFLAG_HASUNION | TYPEFLAG_HASBITFIELD); dict->flags |= DICTFLAG_FINAL; /* mark field type final */ if (PyTuple_Size(pair) == 3) { /* bits specified */ stgdict->flags |= TYPEFLAG_HASBITFIELD; - switch(dict->ffi_type_pointer.type) { + switch(info->ffi_type_pointer.type) { case FFI_TYPE_UINT8: case FFI_TYPE_UINT16: case FFI_TYPE_UINT32: @@ -805,10 +804,10 @@ PyCStructUnionType_update_stgdict(PyObject *type, PyObject *fields, int isStruct return -1; } - stgdict->ffi_type_pointer.alignment = Py_SAFE_DOWNCAST(total_align, + stginfo->ffi_type_pointer.alignment = Py_SAFE_DOWNCAST(total_align, Py_ssize_t, unsigned short); - stgdict->ffi_type_pointer.size = aligned_size; + stginfo->ffi_type_pointer.size = aligned_size; stginfo->size = aligned_size; stginfo->align = total_align; @@ -991,7 +990,7 @@ PyCStructUnionType_update_stgdict(PyObject *type, PyObject *fields, int isStruct } if (ffi_ofs && (basedict != NULL)) { memcpy(element_types, - basedict->ffi_type_pointer.elements, + baseinfo->ffi_type_pointer.elements, ffi_ofs * sizeof(ffi_type *)); } element_index = ffi_ofs; @@ -1042,14 +1041,15 @@ PyCStructUnionType_update_stgdict(PyObject *type, PyObject *fields, int isStruct assert(element_index < (ffi_ofs + len)); /* will be used below */ if (!PyCArrayTypeObject_Check(st, desc)) { /* Not an array. Just copy over the element ffi_type. */ - element_types[element_index++] = &dict->ffi_type_pointer; + element_types[element_index++] = &info->ffi_type_pointer; } else { Py_ssize_t length = info->length; - StgDictObject *edict; - - edict = PyType_stgdict(dict->proto); - if (edict == NULL) { + StgInfo *einfo; + if (PyStgInfo_FromType(st, dict->proto, &einfo) < 0) { + return -1; + } + if (einfo == NULL) { Py_DECREF(pair); PyMem_Free(type_block); PyErr_Format(PyExc_TypeError, @@ -1058,15 +1058,15 @@ PyCStructUnionType_update_stgdict(PyObject *type, PyObject *fields, int isStruct return -1; } element_types[element_index++] = &structs[struct_index]; - structs[struct_index].size = length * edict->ffi_type_pointer.size; - structs[struct_index].alignment = edict->ffi_type_pointer.alignment; + structs[struct_index].size = length * einfo->ffi_type_pointer.size; + structs[struct_index].alignment = einfo->ffi_type_pointer.alignment; structs[struct_index].type = FFI_TYPE_STRUCT; structs[struct_index].elements = &dummy_types[dummy_index]; ++struct_index; /* Copy over the element's type, length times. */ while (length > 0) { assert(dummy_index < (num_ffi_type_pointers)); - dummy_types[dummy_index++] = &edict->ffi_type_pointer; + dummy_types[dummy_index++] = &einfo->ffi_type_pointer; length--; } assert(dummy_index < (num_ffi_type_pointers)); @@ -1080,9 +1080,9 @@ PyCStructUnionType_update_stgdict(PyObject *type, PyObject *fields, int isStruct * Replace the old elements with the new, taking into account * base class elements where necessary. */ - assert(stgdict->ffi_type_pointer.elements); - PyMem_Free(stgdict->ffi_type_pointer.elements); - stgdict->ffi_type_pointer.elements = element_types; + assert(stginfo->ffi_type_pointer.elements); + PyMem_Free(stginfo->ffi_type_pointer.elements); + stginfo->ffi_type_pointer.elements = element_types; } /* We did check that this flag was NOT set above, it must not From 537defbc66fb39f4591247eb1d16b6adff6bdb41 Mon Sep 17 00:00:00 2001 From: Petr Viktorin Date: Tue, 20 Feb 2024 20:45:31 +0100 Subject: [PATCH 09/59] Move `proto` from StgDict to StgInfo. Doesn't work just yet. --- Modules/_ctypes/_ctypes.c | 280 +++++++++++++++++++++++++------------- Modules/_ctypes/cfield.c | 11 +- Modules/_ctypes/ctypes.h | 3 +- Modules/_ctypes/stgdict.c | 7 +- 4 files changed, 197 insertions(+), 104 deletions(-) diff --git a/Modules/_ctypes/_ctypes.c b/Modules/_ctypes/_ctypes.c index 43b7ce40d1084a..b07594ce127625 100644 --- a/Modules/_ctypes/_ctypes.c +++ b/Modules/_ctypes/_ctypes.c @@ -473,6 +473,14 @@ CType_Type_traverse(PyObject *self, visitproc visit, void *arg) static int CType_Type_clear(PyObject *self) { + ctypes_state *st = GLOBAL_STATE(); + StgInfo *info; + if (PyStgInfo_FromType(st, self, &info) < 0) { + return -1; + } + if (info) { + Py_CLEAR(info->proto); + } return 0; } @@ -947,13 +955,14 @@ CDataType_from_param(PyObject *type, PyObject *value) PyCArgObject *p = (PyCArgObject *)value; PyObject *ob = p->obj; const char *ob_name; - StgDictObject *dict; - dict = PyType_stgdict(type); - + StgInfo *info; + if (PyStgInfo_FromType(st, type, &info) < 0) { + return NULL; + } /* If we got a PyCArgObject, we must check if the object packed in it is an instance of the type's dict->proto */ - if(dict && ob) { - res = PyObject_IsInstance(ob, dict->proto); + if(info && ob) { + res = PyObject_IsInstance(ob, info->proto); if (res == -1) return NULL; if (res) { @@ -1004,18 +1013,27 @@ CDataType_repeat(PyObject *self, Py_ssize_t length) static int CDataType_clear(PyTypeObject *self) { - StgDictObject *dict = PyType_stgdict((PyObject *)self); - if (dict) - Py_CLEAR(dict->proto); + ctypes_state *st = GLOBAL_STATE(); + StgInfo *info; + if (PyStgInfo_FromType(st, (PyObject *)self, &info) < 0) { + return -1; + } + if (info) { + Py_CLEAR(info->proto); + } return PyType_Type.tp_clear((PyObject *)self); } static int CDataType_traverse(PyTypeObject *self, visitproc visit, void *arg) { - StgDictObject *dict = PyType_stgdict((PyObject *)self); - if (dict) { - Py_VISIT(dict->proto); + ctypes_state *st = GLOBAL_STATE(); + StgInfo *info; + if (PyStgInfo_FromType(st, (PyObject *)self, &info) < 0) { + return -1; + } + if (info) { + Py_VISIT(info->proto); } Py_VISIT(Py_TYPE(self)); return PyType_Type.tp_traverse((PyObject *)self, visit, arg); @@ -1105,7 +1123,7 @@ size property/method, and the sequence protocol. */ static int -PyCPointerType_SetProto(StgDictObject *stgdict, PyObject *proto) +PyCPointerType_SetProto(StgInfo *stginfo, PyObject *proto) { if (!proto || !PyType_Check(proto)) { PyErr_SetString(PyExc_TypeError, @@ -1118,7 +1136,7 @@ PyCPointerType_SetProto(StgDictObject *stgdict, PyObject *proto) return -1; } Py_INCREF(proto); - Py_XSETREF(stgdict->proto, proto); + Py_XSETREF(stginfo->proto, proto); return 0; } @@ -1191,7 +1209,7 @@ PyCPointerType_new(PyTypeObject *type, PyObject *args, PyObject *kwds) if (proto) { StgDictObject *itemdict; const char *current_format; - if (-1 == PyCPointerType_SetProto(stgdict, proto)) { + if (-1 == PyCPointerType_SetProto(stginfo, proto)) { Py_DECREF(proto); Py_DECREF((PyObject *)result); Py_DECREF((PyObject *)stgdict); @@ -1245,8 +1263,14 @@ PyCPointerType_set_type(PyTypeObject *self, PyObject *type) "abstract class"); return NULL; } + ctypes_state *st = GLOBAL_STATE(); + StgInfo *info; + if (PyStgInfo_FromType(st, (PyObject *)self, &info) < 0) { + return NULL; + } + assert(info); - if (-1 == PyCPointerType_SetProto(dict, type)) + if (-1 == PyCPointerType_SetProto(info, type)) return NULL; if (-1 == PyDict_SetItem((PyObject *)dict, &_Py_ID(_type_), type)) @@ -1260,15 +1284,17 @@ static PyObject *_byref(PyObject *); static PyObject * PyCPointerType_from_param(PyObject *type, PyObject *value) { - StgDictObject *typedict; - if (value == Py_None) { /* ConvParam will convert to a NULL pointer later */ return Py_NewRef(value); } - typedict = PyType_stgdict(type); - if (!typedict) { + ctypes_state *st = GLOBAL_STATE(); + StgInfo *typeinfo; + if (PyStgInfo_FromType(st, type, &typeinfo) < 0) { + return NULL; + } + if (!typeinfo) { PyErr_SetString(PyExc_TypeError, "abstract class"); return NULL; @@ -1277,7 +1303,8 @@ PyCPointerType_from_param(PyObject *type, PyObject *value) /* If we expect POINTER(), but receive a instance, accept it by calling byref(). */ - switch (PyObject_IsInstance(value, typedict->proto)) { + assert(typeinfo->proto); + switch (PyObject_IsInstance(value, typeinfo->proto)) { case 1: Py_INCREF(value); /* _byref steals a refcount */ return _byref(value); @@ -1287,14 +1314,16 @@ PyCPointerType_from_param(PyObject *type, PyObject *value) break; } - ctypes_state *st = GLOBAL_STATE(); if (PointerObject_Check(st, value) || ArrayObject_Check(st, value)) { /* Array instances are also pointers when the item types are the same. */ - StgDictObject *v = PyObject_stgdict(value); + StgInfo *v; + if (PyStgInfo_FromObject(st, value, &v) < 0) { + return NULL; + } assert(v); /* Cannot be NULL for pointer or array objects */ - int ret = PyObject_IsSubclass(v->proto, typedict->proto); + int ret = PyObject_IsSubclass(v->proto, typeinfo->proto); if (ret < 0) { return NULL; } @@ -1641,7 +1670,7 @@ PyCArrayType_new(PyTypeObject *type, PyObject *args, PyObject *kwds) stginfo->size = itemsize * length; stginfo->align = itemalign; stginfo->length = length; - stgdict->proto = type_attr; + stginfo->proto = type_attr; type_attr = NULL; stginfo->paramfunc = &PyCArrayType_paramfunc; @@ -1740,10 +1769,13 @@ c_wchar_p_from_param(PyObject *type, PyObject *value) ctypes_state *st = GLOBAL_STATE(); if (ArrayObject_Check(st, value) || PointerObject_Check(st, value)) { /* c_wchar array instance or pointer(c_wchar(...)) */ - StgDictObject *dt = PyObject_stgdict(value); + StgInfo *it; + if (PyStgInfo_FromObject(st, value, &it) < 0) { + return NULL; + } StgDictObject *dict; - assert(dt); /* Cannot be NULL for pointer or array objects */ - dict = dt && dt->proto ? PyType_stgdict(dt->proto) : NULL; + assert(it); /* Cannot be NULL for pointer or array objects */ + dict = it && it->proto ? PyType_stgdict(it->proto) : NULL; if (dict && (dict->setfunc == _ctypes_get_fielddesc("u")->setfunc)) { return Py_NewRef(value); } @@ -1804,10 +1836,13 @@ c_char_p_from_param(PyObject *type, PyObject *value) ctypes_state *st = GLOBAL_STATE(); if (ArrayObject_Check(st, value) || PointerObject_Check(st, value)) { /* c_char array instance or pointer(c_char(...)) */ - StgDictObject *dt = PyObject_stgdict(value); + StgInfo *it; + if (PyStgInfo_FromObject(st, value, &it) < 0) { + return NULL; + } StgDictObject *dict; - assert(dt); /* Cannot be NULL for pointer or array objects */ - dict = dt && dt->proto ? PyType_stgdict(dt->proto) : NULL; + assert(it); /* Cannot be NULL for pointer or array objects */ + dict = it && it->proto ? PyType_stgdict(it->proto) : NULL; if (dict && (dict->setfunc == _ctypes_get_fielddesc("c")->setfunc)) { return Py_NewRef(value); } @@ -1838,7 +1873,6 @@ c_char_p_from_param(PyObject *type, PyObject *value) static PyObject * c_void_p_from_param(PyObject *type, PyObject *value) { - StgDictObject *stgd; PyObject *as_parameter; int res; @@ -1937,15 +1971,18 @@ c_void_p_from_param(PyObject *type, PyObject *value) return (PyObject *)parg; } /* c_char_p, c_wchar_p */ - stgd = PyObject_stgdict(value); - if (stgd + StgInfo *stgi; + if (PyStgInfo_FromObject(st, value, &stgi) < 0) { + return NULL; + } + if (stgi && CDataObject_Check(st, value) - && stgd->proto - && PyUnicode_Check(stgd->proto)) + && stgi->proto + && PyUnicode_Check(stgi->proto)) { PyCArgObject *parg; - switch (PyUnicode_AsUTF8(stgd->proto)[0]) { + switch (PyUnicode_AsUTF8(stgi->proto)[0]) { case 'z': /* c_char_p */ case 'Z': /* c_wchar_p */ parg = PyCArgObject_new(); @@ -2046,7 +2083,7 @@ static PyObject *CreateSwappedType(PyTypeObject *type, PyObject *args, PyObject stgdict->setfunc = fmt->setfunc_swapped; stgdict->getfunc = fmt->getfunc_swapped; - stgdict->proto = Py_NewRef(proto); + stginfo->proto = Py_NewRef(proto); /* replace the class dict by our updated spam dict */ if (-1 == PyDict_Update((PyObject *)stgdict, result->tp_dict)) { @@ -2062,14 +2099,17 @@ static PyObject *CreateSwappedType(PyTypeObject *type, PyObject *args, PyObject static PyCArgObject * PyCSimpleType_paramfunc(CDataObject *self) { - StgDictObject *dict; const char *fmt; PyCArgObject *parg; struct fielddesc *fd; - dict = PyObject_stgdict((PyObject *)self); - assert(dict); /* Cannot be NULL for CDataObject instances */ - fmt = PyUnicode_AsUTF8(dict->proto); + ctypes_state *st = GLOBAL_STATE(); + StgInfo *info; + if (PyStgInfo_FromObject(st, (PyObject *)self, &info) < 0) { + return NULL; + } + assert(info); /* Cannot be NULL for CDataObject instances */ + fmt = PyUnicode_AsUTF8(info->proto); assert(fmt); fd = _ctypes_get_fielddesc(fmt); @@ -2181,7 +2221,7 @@ PyCSimpleType_new(PyTypeObject *type, PyObject *args, PyObject *kwds) */ /* This consumes the refcount on proto which we have */ - stgdict->proto = proto; + stginfo->proto = proto; /* replace the class dict by our updated spam dict */ if (-1 == PyDict_Update((PyObject *)stgdict, result->tp_dict)) { @@ -2282,7 +2322,6 @@ PyCSimpleType_new(PyTypeObject *type, PyObject *args, PyObject *kwds) static PyObject * PyCSimpleType_from_param(PyObject *type, PyObject *value) { - StgDictObject *dict; const char *fmt; PyCArgObject *parg; struct fielddesc *fd; @@ -2298,15 +2337,19 @@ PyCSimpleType_from_param(PyObject *type, PyObject *value) return Py_NewRef(value); } - dict = PyType_stgdict(type); - if (!dict) { + ctypes_state *st = GLOBAL_STATE(); + StgInfo *info; + if (PyStgInfo_FromType(st, type, &info) < 0) { + return NULL; + } + if (!info) { PyErr_SetString(PyExc_TypeError, "abstract class"); return NULL; } /* I think we can rely on this being a one-character string */ - fmt = PyUnicode_AsUTF8(dict->proto); + fmt = PyUnicode_AsUTF8(info->proto); assert(fmt); fd = _ctypes_get_fielddesc(fmt); @@ -2802,13 +2845,15 @@ PyCData_item_type(PyObject *type) { ctypes_state *st = GLOBAL_STATE(); if (PyCArrayTypeObject_Check(st, type)) { - StgDictObject *stg_dict; PyObject *elem_type; /* asserts used here as these are all guaranteed by construction */ - stg_dict = PyType_stgdict(type); - assert(stg_dict); - elem_type = stg_dict->proto; + StgInfo *stg_info; + if (PyStgInfo_FromType(st, type, &stg_info) < 0) { + return NULL; + } + assert(stg_info); + elem_type = stg_info->proto; assert(elem_type); return PyCData_item_type(elem_type); } @@ -2823,6 +2868,9 @@ PyCData_NewGetBuffer(PyObject *myself, Py_buffer *view, int flags) CDataObject *self = (CDataObject *)myself; StgDictObject *dict = PyObject_stgdict(myself); PyObject *item_type = PyCData_item_type((PyObject*)Py_TYPE(myself)); + if (item_type == NULL) { + return 0; + } if (view == NULL) return 0; @@ -3189,11 +3237,16 @@ _PyCData_set(CDataObject *dst, PyObject *type, SETFUNC setfunc, PyObject *value, if (PyCPointerTypeObject_Check(st, type) && ArrayObject_Check(st, value)) { - StgDictObject *p1, *p2; PyObject *keep; - p1 = PyObject_stgdict(value); + + StgInfo *p1, *p2; + if (PyStgInfo_FromObject(st, value, &p1) < 0) { + return NULL; + } assert(p1); /* Cannot be NULL for array instances */ - p2 = PyType_stgdict(type); + if (PyStgInfo_FromType(st, type, &p2) < 0) { + return NULL; + } assert(p2); /* Cannot be NULL for pointer types */ if (p1->proto != p2->proto) { @@ -3465,7 +3518,6 @@ static PPROC FindAddress(void *handle, const char *name, PyObject *type) static int _check_outarg_type(PyObject *arg, Py_ssize_t index) { - StgDictObject *dict; ctypes_state *st = GLOBAL_STATE(); if (PyCPointerTypeObject_Check(st, arg)) { @@ -3474,12 +3526,15 @@ _check_outarg_type(PyObject *arg, Py_ssize_t index) if (PyCArrayTypeObject_Check(st, arg)) { return 1; } - dict = PyType_stgdict(arg); - if (dict + StgInfo *info; + if (PyStgInfo_FromType(st, arg, &info) < 0) { + return -1; + } + if (info /* simple pointer types, c_void_p, c_wchar_p, BSTR, ... */ - && PyUnicode_Check(dict->proto) + && PyUnicode_Check(info->proto) /* We only allow c_void_p, c_char_p and c_wchar_p as a simple output parameter type */ - && (strchr("PzZ", PyUnicode_AsUTF8(dict->proto)[0]))) { + && (strchr("PzZ", PyUnicode_AsUTF8(info->proto)[0]))) { return 1; } @@ -3920,7 +3975,6 @@ _build_callargs(PyCFuncPtrObject *self, PyObject *argtypes, { PyObject *paramflags = self->paramflags; PyObject *callargs; - StgDictObject *dict; Py_ssize_t i, len; int inargs_index = 0; /* It's a little bit difficult to determine how many arguments the @@ -4009,15 +4063,18 @@ _build_callargs(PyCFuncPtrObject *self, PyObject *argtypes, break; } ob = PyTuple_GET_ITEM(argtypes, i); - dict = PyType_stgdict(ob); - if (dict == NULL) { + StgInfo *info; + if (PyStgInfo_FromType(st, ob, &info) < 0) { + goto error; + } + if (info == NULL) { /* Cannot happen: _validate_paramflags() would not accept such an object */ PyErr_Format(PyExc_RuntimeError, - "NULL stgdict unexpected"); + "NULL stginfo unexpected"); goto error; } - if (PyUnicode_Check(dict->proto)) { + if (PyUnicode_Check(info->proto)) { PyErr_Format( PyExc_TypeError, "%s 'out' parameter must be passed as default value", @@ -4029,7 +4086,7 @@ _build_callargs(PyCFuncPtrObject *self, PyObject *argtypes, } else { /* Create an instance of the pointed-to type */ - ob = _PyObject_CallNoArgs(dict->proto); + ob = _PyObject_CallNoArgs(info->proto); } /* XXX Is the following correct any longer? @@ -4647,7 +4704,7 @@ Array_item(PyObject *myself, Py_ssize_t index) size = stginfo->size / stginfo->length; offset = index * size; - return PyCData_get(stgdict->proto, stgdict->getfunc, (PyObject *)self, + return PyCData_get(stginfo->proto, stgdict->getfunc, (PyObject *)self, index, size, self->b_ptr + offset); } @@ -4666,7 +4723,7 @@ Array_subscript(PyObject *myself, PyObject *item) return Array_item(myself, i); } else if (PySlice_Check(item)) { - StgDictObject *stgdict, *itemdict; + StgDictObject *itemdict; PyObject *proto; PyObject *np; Py_ssize_t start, stop, step, slicelen, i; @@ -4677,9 +4734,13 @@ Array_subscript(PyObject *myself, PyObject *item) } slicelen = PySlice_AdjustIndices(self->b_length, &start, &stop, step); - stgdict = PyObject_stgdict((PyObject *)self); - assert(stgdict); /* Cannot be NULL for array object instances */ - proto = stgdict->proto; + ctypes_state *st = GLOBAL_STATE(); + StgInfo *stginfo; + if (PyStgInfo_FromObject(st, (PyObject *)self, &stginfo) < 0) { + return NULL; + } + assert(stginfo); /* Cannot be NULL for array object instances */ + proto = stginfo->proto; itemdict = PyType_stgdict(proto); assert(itemdict); /* proto is the item type of the array, a ctypes type, so this cannot be NULL */ @@ -4791,7 +4852,7 @@ Array_ass_item(PyObject *myself, Py_ssize_t index, PyObject *value) offset = index * size; ptr = self->b_ptr + offset; - return PyCData_set((PyObject *)self, stgdict->proto, stgdict->setfunc, value, + return PyCData_set((PyObject *)self, stginfo->proto, stgdict->setfunc, value, index, size, ptr); } @@ -5182,13 +5243,19 @@ Pointer_item(PyObject *myself, Py_ssize_t index) stgdict = PyObject_stgdict((PyObject *)self); assert(stgdict); /* Cannot be NULL for pointer object instances */ - proto = stgdict->proto; + ctypes_state *st = GLOBAL_STATE(); + StgInfo *stginfo; + if (PyStgInfo_FromObject(st, (PyObject *)self, &stginfo) < 0) { + return NULL; + } + assert(stginfo); + + proto = stginfo->proto; assert(proto); itemdict = PyType_stgdict(proto); assert(itemdict); /* proto is the item type of the pointer, a ctypes type, so this cannot be NULL */ - ctypes_state *st = GLOBAL_STATE(); StgInfo *iteminfo; if (PyStgInfo_FromType(st, proto, &iteminfo) < 0) { return NULL; @@ -5227,14 +5294,20 @@ Pointer_ass_item(PyObject *myself, Py_ssize_t index, PyObject *value) stgdict = PyObject_stgdict((PyObject *)self); assert(stgdict); /* Cannot be NULL for pointer instances */ - proto = stgdict->proto; + ctypes_state *st = GLOBAL_STATE(); + StgInfo *stginfo; + if (PyStgInfo_FromObject(st, (PyObject *)self, &stginfo) < 0) { + return -1; + } + assert(stginfo); + + proto = stginfo->proto; assert(proto); itemdict = PyType_stgdict(proto); assert(itemdict); /* Cannot be NULL because the itemtype of a pointer is always a ctypes type */ - ctypes_state *st = GLOBAL_STATE(); StgInfo *iteminfo; if (PyStgInfo_FromType(st, proto, &iteminfo) < 0) { return -1; @@ -5252,17 +5325,20 @@ Pointer_ass_item(PyObject *myself, Py_ssize_t index, PyObject *value) static PyObject * Pointer_get_contents(CDataObject *self, void *closure) { - StgDictObject *stgdict; - if (*(void **)self->b_ptr == NULL) { PyErr_SetString(PyExc_ValueError, "NULL pointer access"); return NULL; } - stgdict = PyObject_stgdict((PyObject *)self); - assert(stgdict); /* Cannot be NULL for pointer instances */ - return PyCData_FromBaseObj(stgdict->proto, + ctypes_state *st = GLOBAL_STATE(); + StgInfo *stginfo; + if (PyStgInfo_FromObject(st, (PyObject *)self, &stginfo) < 0) { + return NULL; + } + assert(stginfo); /* Cannot be NULL for pointer instances */ + + return PyCData_FromBaseObj(stginfo->proto, (PyObject *)self, 0, *(void **)self->b_ptr); } @@ -5270,7 +5346,6 @@ Pointer_get_contents(CDataObject *self, void *closure) static int Pointer_set_contents(CDataObject *self, PyObject *value, void *closure) { - StgDictObject *stgdict; CDataObject *dst; PyObject *keep; @@ -5279,18 +5354,21 @@ Pointer_set_contents(CDataObject *self, PyObject *value, void *closure) "Pointer does not support item deletion"); return -1; } - stgdict = PyObject_stgdict((PyObject *)self); - assert(stgdict); /* Cannot be NULL for pointer instances */ - assert(stgdict->proto); ctypes_state *st = GLOBAL_STATE(); + StgInfo *stginfo; + if (PyStgInfo_FromObject(st, (PyObject *)self, &stginfo) < 0) { + return -1; + } + assert(stginfo); /* Cannot be NULL for pointer instances */ + assert(stginfo->proto); if (!CDataObject_Check(st, value)) { - int res = PyObject_IsInstance(value, stgdict->proto); + int res = PyObject_IsInstance(value, stginfo->proto); if (res == -1) return -1; if (!res) { PyErr_Format(PyExc_TypeError, "expected %s instead of %s", - ((PyTypeObject *)(stgdict->proto))->tp_name, + ((PyTypeObject *)(stginfo->proto))->tp_name, Py_TYPE(value)->tp_name); return -1; } @@ -5338,8 +5416,12 @@ Pointer_init(CDataObject *self, PyObject *args, PyObject *kw) static PyObject * Pointer_new(PyTypeObject *type, PyObject *args, PyObject *kw) { - StgDictObject *dict = PyType_stgdict((PyObject *)type); - if (!dict || !dict->proto) { + ctypes_state *st = GLOBAL_STATE(); + StgInfo *info; + if (PyStgInfo_FromType(st, (PyObject *)type, &info) < 0) { + return NULL; + } + if (!info || !info->proto) { PyErr_SetString(PyExc_TypeError, "Cannot create instance: has no _type_"); return NULL; @@ -5361,7 +5443,7 @@ Pointer_subscript(PyObject *myself, PyObject *item) PySliceObject *slice = (PySliceObject *)item; Py_ssize_t start, stop, step; PyObject *np; - StgDictObject *stgdict, *itemdict; + StgDictObject *itemdict; PyObject *proto; Py_ssize_t i, len; size_t cur; @@ -5415,9 +5497,13 @@ Pointer_subscript(PyObject *myself, PyObject *item) else len = (stop - start + 1) / step + 1; - stgdict = PyObject_stgdict((PyObject *)self); - assert(stgdict); /* Cannot be NULL for pointer instances */ - proto = stgdict->proto; + ctypes_state *st = GLOBAL_STATE(); + StgInfo *stginfo; + if (PyStgInfo_FromObject(st, (PyObject *)self, &stginfo) < 0) { + return NULL; + } + assert(stginfo); /* Cannot be NULL for pointer instances */ + proto = stginfo->proto; assert(proto); itemdict = PyType_stgdict(proto); assert(itemdict); @@ -5664,7 +5750,6 @@ string_at(const char *ptr, int size) static int cast_check_pointertype(PyObject *arg) { - StgDictObject *dict; ctypes_state *st = GLOBAL_STATE(); if (PyCPointerTypeObject_Check(st, arg)) { @@ -5673,10 +5758,13 @@ cast_check_pointertype(PyObject *arg) if (PyCFuncPtrTypeObject_Check(st, arg)) { return 1; } - dict = PyType_stgdict(arg); - if (dict != NULL && dict->proto != NULL) { - if (PyUnicode_Check(dict->proto) - && (strchr("sPzUZXO", PyUnicode_AsUTF8(dict->proto)[0]))) { + StgInfo *info; + if (PyStgInfo_FromType(st, arg, &info) < 0) { + return 0; + } + if (info != NULL && info->proto != NULL) { + if (PyUnicode_Check(info->proto) + && (strchr("sPzUZXO", PyUnicode_AsUTF8(info->proto)[0]))) { /* simple pointer types, c_void_p, c_wchar_p, BSTR, ... */ return 1; } diff --git a/Modules/_ctypes/cfield.c b/Modules/_ctypes/cfield.c index 3ab09adbd722c4..572a857d485856 100644 --- a/Modules/_ctypes/cfield.c +++ b/Modules/_ctypes/cfield.c @@ -120,10 +120,15 @@ PyCField_FromDesc(PyObject *desc, Py_ssize_t index, return a Python string instead of an Array object instance... */ if (PyCArrayTypeObject_Check(st, proto)) { - StgDictObject *adict = PyType_stgdict(proto); StgDictObject *idict; - if (adict && adict->proto) { - idict = PyType_stgdict(adict->proto); + StgInfo *ainfo; + if (PyStgInfo_FromType(st, proto, &ainfo) < 0) { + Py_DECREF(self); + return NULL; + } + + if (ainfo && ainfo->proto) { + idict = PyType_stgdict(ainfo->proto); if (!idict) { PyErr_SetString(PyExc_TypeError, "has no _stginfo_"); diff --git a/Modules/_ctypes/ctypes.h b/Modules/_ctypes/ctypes.h index 6204b5534f8bc5..3d8f13a299729e 100644 --- a/Modules/_ctypes/ctypes.h +++ b/Modules/_ctypes/ctypes.h @@ -238,6 +238,7 @@ typedef struct { Py_ssize_t align; /* alignment requirements */ Py_ssize_t length; /* number of fields */ ffi_type ffi_type_pointer; + PyObject *proto; /* Only for Pointer/ArrayObject */ PARAMFUNC paramfunc; } StgInfo; @@ -269,7 +270,7 @@ typedef struct { //Py_ssize_t align; /* alignment requirements */ //Py_ssize_t length; /* number of fields */ //ffi_type ffi_type_pointer; - PyObject *proto; /* Only for Pointer/ArrayObject */ + //PyObject *proto; /* Only for Pointer/ArrayObject */ SETFUNC setfunc; /* Only for simple objects */ GETFUNC getfunc; /* Only for simple objects */ //PARAMFUNC paramfunc; diff --git a/Modules/_ctypes/stgdict.c b/Modules/_ctypes/stgdict.c index d91ceeb3f5a514..a939bf40ece934 100644 --- a/Modules/_ctypes/stgdict.c +++ b/Modules/_ctypes/stgdict.c @@ -103,7 +103,6 @@ PyCStgDict_init(StgDictObject *self, PyObject *args, PyObject *kwds) static int PyCStgDict_clear(StgDictObject *self) { - Py_CLEAR(self->proto); Py_CLEAR(self->argtypes); Py_CLEAR(self->converters); Py_CLEAR(self->restype); @@ -156,7 +155,7 @@ PyCStgDict_clone(StgDictObject *dst, StgDictObject *src, memcpy(dst_info, src_info, sizeof(StgInfo)); - Py_XINCREF(dst->proto); + Py_XINCREF(dst_info->proto); Py_XINCREF(dst->argtypes); Py_XINCREF(dst->converters); Py_XINCREF(dst->restype); @@ -939,7 +938,7 @@ PyCStructUnionType_update_stgdict(PyObject *type, PyObject *fields, int isStruct Py_ssize_t length = info->length; StgDictObject *edict; - edict = PyType_stgdict(dict->proto); + edict = PyType_stgdict(info->proto); if (edict == NULL) { Py_DECREF(pair); PyErr_Format(PyExc_TypeError, @@ -1046,7 +1045,7 @@ PyCStructUnionType_update_stgdict(PyObject *type, PyObject *fields, int isStruct else { Py_ssize_t length = info->length; StgInfo *einfo; - if (PyStgInfo_FromType(st, dict->proto, &einfo) < 0) { + if (PyStgInfo_FromType(st, info->proto, &einfo) < 0) { return -1; } if (einfo == NULL) { From ace66231a6ef6161ad04fc63e347d423abe2aa78 Mon Sep 17 00:00:00 2001 From: Petr Viktorin Date: Tue, 20 Feb 2024 20:46:23 +0100 Subject: [PATCH 10/59] Hack: Reserve space for StgInfo in static type objects --- Modules/_ctypes/_ctypes.c | 77 ++++++++++++++++++++++++++++++--------- Modules/_ctypes/ctypes.h | 5 --- 2 files changed, 60 insertions(+), 22 deletions(-) diff --git a/Modules/_ctypes/_ctypes.c b/Modules/_ctypes/_ctypes.c index b07594ce127625..84bc3041a477a8 100644 --- a/Modules/_ctypes/_ctypes.c +++ b/Modules/_ctypes/_ctypes.c @@ -128,19 +128,34 @@ bytes(cdata) #include "pycore_long.h" // _PyLong_GetZero() -static PyTypeObject Union_Type; -static PyTypeObject Struct_Type; -static PyTypeObject Simple_Type; +/* Allow defining static types that have space for StgInfo at the end. + * These should all be converted to actual heap types that + * honor tp_basicsize of their metaclass! + */ +typedef struct { + PyHeapTypeObject ht; + StgInfo info; +} _HackyHeapType; + +static _HackyHeapType PyCData_Type; +static _HackyHeapType PyCData_Type; +static _HackyHeapType Union_Type; +static _HackyHeapType Struct_Type; +static _HackyHeapType Simple_Type; +static _HackyHeapType PyCArray_Type; +static _HackyHeapType PyCPointer_Type; +static _HackyHeapType PyCFuncPtr_Type; + ctypes_state global_state = { .PyCStgDict_Type = &PyCStgDict_Type, - .PyCData_Type = &PyCData_Type, - .Struct_Type = &Struct_Type, - .Union_Type = &Union_Type, - .PyCArray_Type = &PyCArray_Type, - .Simple_Type = &Simple_Type, - .PyCPointer_Type = &PyCPointer_Type, - .PyCFuncPtr_Type = &PyCFuncPtr_Type, + .PyCData_Type = (PyTypeObject*)&PyCData_Type, + .Struct_Type = (PyTypeObject*)&Struct_Type, + .Union_Type = (PyTypeObject*)&Union_Type, + .PyCArray_Type = (PyTypeObject*)&PyCArray_Type, + .Simple_Type = (PyTypeObject*)&Simple_Type, + .PyCPointer_Type = (PyTypeObject*)&PyCPointer_Type, + .PyCFuncPtr_Type = (PyTypeObject*)&PyCFuncPtr_Type, }; PyObject *PyExc_ArgError = NULL; @@ -2979,7 +2994,9 @@ static PyMethodDef PyCData_methods[] = { { NULL, NULL }, }; -PyTypeObject PyCData_Type = { +static _HackyHeapType PyCData_Type = { + .ht = { + .ht_type = { PyVarObject_HEAD_INIT(NULL, 0) "_ctypes._CData", sizeof(CDataObject), /* tp_basicsize */ @@ -3019,6 +3036,8 @@ PyTypeObject PyCData_Type = { 0, /* tp_alloc */ 0, /* tp_new */ 0, /* tp_free */ + } + } }; static int @@ -4405,7 +4424,9 @@ static PyNumberMethods PyCFuncPtr_as_number = { (inquiry)PyCFuncPtr_bool, /* nb_bool */ }; -PyTypeObject PyCFuncPtr_Type = { +static _HackyHeapType PyCFuncPtr_Type = { + .ht = { + .ht_type = { PyVarObject_HEAD_INIT(NULL, 0) "_ctypes.CFuncPtr", sizeof(PyCFuncPtrObject), /* tp_basicsize */ @@ -4445,6 +4466,8 @@ PyTypeObject PyCFuncPtr_Type = { 0, /* tp_alloc */ PyCFuncPtr_new, /* tp_new */ 0, /* tp_free */ + } + } }; /*****************************************************************/ @@ -4565,7 +4588,9 @@ Struct_init(PyObject *self, PyObject *args, PyObject *kwds) return 0; } -static PyTypeObject Struct_Type = { +static _HackyHeapType Struct_Type = { + .ht = { + .ht_type = { PyVarObject_HEAD_INIT(NULL, 0) "_ctypes.Structure", sizeof(CDataObject), /* tp_basicsize */ @@ -4605,9 +4630,13 @@ static PyTypeObject Struct_Type = { 0, /* tp_alloc */ GenericPyCData_new, /* tp_new */ 0, /* tp_free */ + } + } }; -static PyTypeObject Union_Type = { +static _HackyHeapType Union_Type = { + .ht = { + .ht_type = { PyVarObject_HEAD_INIT(NULL, 0) "_ctypes.Union", sizeof(CDataObject), /* tp_basicsize */ @@ -4647,6 +4676,8 @@ static PyTypeObject Union_Type = { 0, /* tp_alloc */ GenericPyCData_new, /* tp_new */ 0, /* tp_free */ + } + } }; @@ -4956,7 +4987,9 @@ PyDoc_STRVAR(array_doc, "reads, the resulting object is not itself an Array." ); -PyTypeObject PyCArray_Type = { +static _HackyHeapType PyCArray_Type = { + .ht = { + .ht_type = { PyVarObject_HEAD_INIT(NULL, 0) "_ctypes.Array", sizeof(CDataObject), /* tp_basicsize */ @@ -4996,6 +5029,8 @@ PyTypeObject PyCArray_Type = { 0, /* tp_alloc */ GenericPyCData_new, /* tp_new */ 0, /* tp_free */ + } + } }; PyObject * @@ -5179,7 +5214,9 @@ Simple_repr(CDataObject *self) return result; } -static PyTypeObject Simple_Type = { +static _HackyHeapType Simple_Type = { + .ht = { + .ht_type = { PyVarObject_HEAD_INIT(NULL, 0) "_ctypes._SimpleCData", sizeof(CDataObject), /* tp_basicsize */ @@ -5219,6 +5256,8 @@ static PyTypeObject Simple_Type = { 0, /* tp_alloc */ GenericPyCData_new, /* tp_new */ 0, /* tp_free */ + } + } }; /******************************************************************/ @@ -5603,7 +5642,9 @@ static PyNumberMethods Pointer_as_number = { (inquiry)Pointer_bool, /* nb_bool */ }; -PyTypeObject PyCPointer_Type = { +static _HackyHeapType PyCPointer_Type = { + .ht = { + .ht_type = { PyVarObject_HEAD_INIT(NULL, 0) "_ctypes._Pointer", sizeof(CDataObject), /* tp_basicsize */ @@ -5643,6 +5684,8 @@ PyTypeObject PyCPointer_Type = { 0, /* tp_alloc */ Pointer_new, /* tp_new */ 0, /* tp_free */ + } + } }; diff --git a/Modules/_ctypes/ctypes.h b/Modules/_ctypes/ctypes.h index 3d8f13a299729e..007d1924cba418 100644 --- a/Modules/_ctypes/ctypes.h +++ b/Modules/_ctypes/ctypes.h @@ -169,7 +169,6 @@ extern int PyObject_stginfo(PyObject *self, Py_ssize_t *psize, Py_ssize_t *palig -extern PyTypeObject PyCData_Type; #define CDataObject_CheckExact(st, v) Py_IS_TYPE((v), (st)->PyCData_Type) #define CDataObject_Check(st, v) PyObject_TypeCheck((v), (st)->PyCData_Type) #define _CDataObject_HasExternalBuffer(v) ((v)->b_ptr != (char *)&(v)->b_value) @@ -189,10 +188,6 @@ 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(st, v) PyObject_TypeCheck((v), (st)->PyCArrayType_Type) #define ArrayObject_Check(st, v) PyObject_TypeCheck((v), (st)->PyCArray_Type) #define PointerObject_Check(st, v) PyObject_TypeCheck((v), (st)->PyCPointer_Type) From 6f775b92f8c174da35996f1c76de1d54f15ceccc Mon Sep 17 00:00:00 2001 From: Petr Viktorin Date: Tue, 20 Feb 2024 21:02:59 +0100 Subject: [PATCH 11/59] If StgInfo is not initialized, act as if it's not there --- Modules/_ctypes/stgdict.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/Modules/_ctypes/stgdict.c b/Modules/_ctypes/stgdict.c index a939bf40ece934..ffcedba5456af6 100644 --- a/Modules/_ctypes/stgdict.c +++ b/Modules/_ctypes/stgdict.c @@ -29,8 +29,8 @@ _stginfo_from_type(ctypes_state *state, PyTypeObject *type, StgInfo **result) } StgInfo *info = PyObject_GetTypeData((PyObject *)type, state->PyCType_Type); if (!info->initialized) { - //PyErr_Format(PyExc_SystemError, "%R StgInfo is not initialized.", type); - //return -1; + // StgInfo is not initialized. This happens in abstract classes. + return 0; } *result = info; return 1; From 4aaa06a6f6c264fce3864654b12b826266dc6c40 Mon Sep 17 00:00:00 2001 From: Petr Viktorin Date: Tue, 20 Feb 2024 21:53:15 +0100 Subject: [PATCH 12/59] Move `getfunc` & `setfunc` --- Modules/_ctypes/_ctypes.c | 153 ++++++++++++++++++------------------ Modules/_ctypes/callbacks.c | 18 ++--- Modules/_ctypes/callproc.c | 15 ++-- Modules/_ctypes/cfield.c | 13 +-- Modules/_ctypes/ctypes.h | 6 +- Modules/_ctypes/stgdict.c | 4 +- 6 files changed, 103 insertions(+), 106 deletions(-) diff --git a/Modules/_ctypes/_ctypes.c b/Modules/_ctypes/_ctypes.c index 84bc3041a477a8..7f97b6cebe8a47 100644 --- a/Modules/_ctypes/_ctypes.c +++ b/Modules/_ctypes/_ctypes.c @@ -1702,11 +1702,11 @@ PyCArrayType_new(PyTypeObject *type, PyObject *args, PyObject *kwds) /* Special case for character arrays. A permanent annoyance: char arrays are also strings! */ - if (itemdict->getfunc == _ctypes_get_fielddesc("c")->getfunc) { + if (iteminfo->getfunc == _ctypes_get_fielddesc("c")->getfunc) { if (-1 == add_getset(result, CharArray_getsets)) goto error; } - else if (itemdict->getfunc == _ctypes_get_fielddesc("u")->getfunc) { + else if (iteminfo->getfunc == _ctypes_get_fielddesc("u")->getfunc) { if (-1 == add_getset(result, WCharArray_getsets)) goto error; } @@ -1788,18 +1788,25 @@ c_wchar_p_from_param(PyObject *type, PyObject *value) if (PyStgInfo_FromObject(st, value, &it) < 0) { return NULL; } - StgDictObject *dict; assert(it); /* Cannot be NULL for pointer or array objects */ - dict = it && it->proto ? PyType_stgdict(it->proto) : NULL; - if (dict && (dict->setfunc == _ctypes_get_fielddesc("u")->setfunc)) { + StgInfo *info = NULL; + if (it && it->proto) { + if (PyStgInfo_FromType(st, it->proto, &info) < 0) { + return NULL; + } + } + if (info && (info->setfunc == _ctypes_get_fielddesc("u")->setfunc)) { return Py_NewRef(value); } } if (PyCArg_CheckExact(st, value)) { /* byref(c_char(...)) */ PyCArgObject *a = (PyCArgObject *)value; - StgDictObject *dict = PyObject_stgdict(a->obj); - if (dict && (dict->setfunc == _ctypes_get_fielddesc("u")->setfunc)) { + StgInfo *info; + if (PyStgInfo_FromObject(st, a->obj, &info) < 0) { + return NULL; + } + if (info && (info->setfunc == _ctypes_get_fielddesc("u")->setfunc)) { return Py_NewRef(value); } } @@ -1855,18 +1862,25 @@ c_char_p_from_param(PyObject *type, PyObject *value) if (PyStgInfo_FromObject(st, value, &it) < 0) { return NULL; } - StgDictObject *dict; assert(it); /* Cannot be NULL for pointer or array objects */ - dict = it && it->proto ? PyType_stgdict(it->proto) : NULL; - if (dict && (dict->setfunc == _ctypes_get_fielddesc("c")->setfunc)) { + StgInfo *info = NULL; + if (it && it->proto) { + if (PyStgInfo_FromType(st, it->proto, &info) < 0) { + return NULL; + } + } + if (info && (info->setfunc == _ctypes_get_fielddesc("c")->setfunc)) { return Py_NewRef(value); } } if (PyCArg_CheckExact(st, value)) { /* byref(c_char(...)) */ PyCArgObject *a = (PyCArgObject *)value; - StgDictObject *dict = PyObject_stgdict(a->obj); - if (dict && (dict->setfunc == _ctypes_get_fielddesc("c")->setfunc)) { + StgInfo *info; + if (PyStgInfo_FromObject(st, a->obj, &info) < 0) { + return NULL; + } + if (info && (info->setfunc == _ctypes_get_fielddesc("c")->setfunc)) { return Py_NewRef(value); } } @@ -2095,8 +2109,8 @@ static PyObject *CreateSwappedType(PyTypeObject *type, PyObject *args, PyObject stginfo->align = fmt->pffi_type->alignment; stginfo->length = 0; stginfo->size = fmt->pffi_type->size; - stgdict->setfunc = fmt->setfunc_swapped; - stgdict->getfunc = fmt->getfunc_swapped; + stginfo->setfunc = fmt->setfunc_swapped; + stginfo->getfunc = fmt->getfunc_swapped; stginfo->proto = Py_NewRef(proto); @@ -2213,8 +2227,8 @@ PyCSimpleType_new(PyTypeObject *type, PyObject *args, PyObject *kwds) stginfo->align = fmt->pffi_type->alignment; stginfo->length = 0; stginfo->size = fmt->pffi_type->size; - stgdict->setfunc = fmt->setfunc; - stgdict->getfunc = fmt->getfunc; + stginfo->setfunc = fmt->setfunc; + stginfo->getfunc = fmt->getfunc; #ifdef WORDS_BIGENDIAN stgdict->format = _ctypes_alloc_format_string_for_type(proto_str[0], 1); #else @@ -2547,8 +2561,8 @@ make_funcptrtype_dict(StgDictObject *stgdict, StgInfo *stginfo) stginfo->align = _ctypes_get_fielddesc("P")->pffi_type->alignment; stginfo->length = 1; stginfo->size = sizeof(void *); - stgdict->setfunc = NULL; - stgdict->getfunc = NULL; + stginfo->setfunc = NULL; + stginfo->getfunc = NULL; stginfo->ffi_type_pointer = ffi_type_pointer; if (PyDict_GetItemRef((PyObject *)stgdict, &_Py_ID(_flags_), &ob) < 0) { @@ -3176,13 +3190,16 @@ PyObject * PyCData_get(PyObject *type, GETFUNC getfunc, PyObject *src, Py_ssize_t index, Py_ssize_t size, char *adr) { - StgDictObject *dict; if (getfunc) return getfunc(adr, size); assert(type); - dict = PyType_stgdict(type); - if (dict && dict->getfunc && !_ctypes_simple_instance(type)) - return dict->getfunc(adr, size); + ctypes_state *st = GLOBAL_STATE(); + StgInfo *info; + if (PyStgInfo_FromType(st, type, &info) < 0) { + return NULL; + } + if (info && info->getfunc && !_ctypes_simple_instance(type)) + return info->getfunc(adr, size); return PyCData_FromBaseObj(type, src, index, adr); } @@ -3201,9 +3218,12 @@ _PyCData_set(CDataObject *dst, PyObject *type, SETFUNC setfunc, PyObject *value, } ctypes_state *st = GLOBAL_STATE(); if (!CDataObject_Check(st, value)) { - StgDictObject *dict = PyType_stgdict(type); - if (dict && dict->setfunc) - return dict->setfunc(ptr, value, size); + StgInfo *info; + if (PyStgInfo_FromType(st, type, &info) < 0) { + return NULL; + } + if (info && info->setfunc) + return info->setfunc(ptr, value, size); /* If value is a tuple, we try to call the type with the tuple and use the result! @@ -4711,8 +4731,6 @@ Array_item(PyObject *myself, Py_ssize_t index) { CDataObject *self = (CDataObject *)myself; Py_ssize_t offset, size; - StgDictObject *stgdict; - if (index < 0 || index >= self->b_length) { PyErr_SetString(PyExc_IndexError, @@ -4720,9 +4738,6 @@ Array_item(PyObject *myself, Py_ssize_t index) return NULL; } - stgdict = PyObject_stgdict((PyObject *)self); - assert(stgdict); /* Cannot be NULL for array instances */ - ctypes_state *st = GLOBAL_STATE(); StgInfo *stginfo; if (PyStgInfo_FromObject(st, (PyObject *)self, &stginfo) < 0) { @@ -4730,12 +4745,12 @@ Array_item(PyObject *myself, Py_ssize_t index) } /* Would it be clearer if we got the item size from - stgdict->proto's stgdict? + stginfo->proto's stginfo? */ size = stginfo->size / stginfo->length; offset = index * size; - return PyCData_get(stginfo->proto, stgdict->getfunc, (PyObject *)self, + return PyCData_get(stginfo->proto, stginfo->getfunc, (PyObject *)self, index, size, self->b_ptr + offset); } @@ -4754,7 +4769,6 @@ Array_subscript(PyObject *myself, PyObject *item) return Array_item(myself, i); } else if (PySlice_Check(item)) { - StgDictObject *itemdict; PyObject *proto; PyObject *np; Py_ssize_t start, stop, step, slicelen, i; @@ -4772,11 +4786,14 @@ Array_subscript(PyObject *myself, PyObject *item) } assert(stginfo); /* Cannot be NULL for array object instances */ proto = stginfo->proto; - itemdict = PyType_stgdict(proto); - assert(itemdict); /* proto is the item type of the array, a + StgInfo *iteminfo; + if (PyStgInfo_FromType(st, proto, &iteminfo) < 0) { + return NULL; + } + assert(iteminfo); /* proto is the item type of the array, a ctypes type, so this cannot be NULL */ - if (itemdict->getfunc == _ctypes_get_fielddesc("c")->getfunc) { + if (iteminfo->getfunc == _ctypes_get_fielddesc("c")->getfunc) { char *ptr = (char *)self->b_ptr; char *dest; @@ -4800,7 +4817,7 @@ Array_subscript(PyObject *myself, PyObject *item) PyMem_Free(dest); return np; } - if (itemdict->getfunc == _ctypes_get_fielddesc("u")->getfunc) { + if (iteminfo->getfunc == _ctypes_get_fielddesc("u")->getfunc) { wchar_t *ptr = (wchar_t *)self->b_ptr; wchar_t *dest; @@ -4855,7 +4872,6 @@ Array_ass_item(PyObject *myself, Py_ssize_t index, PyObject *value) { CDataObject *self = (CDataObject *)myself; Py_ssize_t size, offset; - StgDictObject *stgdict; char *ptr; if (value == NULL) { @@ -4864,9 +4880,6 @@ Array_ass_item(PyObject *myself, Py_ssize_t index, PyObject *value) return -1; } - stgdict = PyObject_stgdict((PyObject *)self); - assert(stgdict); /* Cannot be NULL for array object instances */ - ctypes_state *st = GLOBAL_STATE(); StgInfo *stginfo; if (PyStgInfo_FromObject(st, (PyObject *)self, &stginfo) < 0) { @@ -4883,7 +4896,7 @@ Array_ass_item(PyObject *myself, Py_ssize_t index, PyObject *value) offset = index * size; ptr = self->b_ptr + offset; - return PyCData_set((PyObject *)self, stginfo->proto, stgdict->setfunc, value, + return PyCData_set((PyObject *)self, stginfo->proto, stginfo->setfunc, value, index, size, ptr); } @@ -5107,24 +5120,22 @@ static int Simple_set_value(CDataObject *self, PyObject *value, void *Py_UNUSED(ignored)) { PyObject *result; - StgDictObject *dict = PyObject_stgdict((PyObject *)self); if (value == NULL) { PyErr_SetString(PyExc_TypeError, "can't delete attribute"); return -1; } - assert(dict); /* Cannot be NULL for CDataObject instances */ - assert(dict->setfunc); ctypes_state *st = GLOBAL_STATE(); StgInfo *info; if (PyStgInfo_FromObject(st, (PyObject *)self, &info) < 0) { return -1; } - assert(info); + assert(info); /* Cannot be NULL for CDataObject instances */ + assert(info->setfunc); - result = dict->setfunc(self->b_ptr, value, info->size); + result = info->setfunc(self->b_ptr, value, info->size); if (!result) return -1; @@ -5146,11 +5157,14 @@ Simple_init(CDataObject *self, PyObject *args, PyObject *kw) static PyObject * Simple_get_value(CDataObject *self, void *Py_UNUSED(ignored)) { - StgDictObject *dict; - dict = PyObject_stgdict((PyObject *)self); - assert(dict); /* Cannot be NULL for CDataObject instances */ - assert(dict->getfunc); - return dict->getfunc(self->b_ptr, self->b_size); + ctypes_state *st = GLOBAL_STATE(); + StgInfo *info; + if (PyStgInfo_FromObject(st, (PyObject *)self, &info) < 0) { + return NULL; + } + assert(info); /* Cannot be NULL for CDataObject instances */ + assert(info->getfunc); + return info->getfunc(self->b_ptr, self->b_size); } static PyGetSetDef Simple_getsets[] = { @@ -5270,7 +5284,6 @@ Pointer_item(PyObject *myself, Py_ssize_t index) CDataObject *self = (CDataObject *)myself; Py_ssize_t size; Py_ssize_t offset; - StgDictObject *stgdict, *itemdict; PyObject *proto; if (*(void **)self->b_ptr == NULL) { @@ -5279,21 +5292,15 @@ Pointer_item(PyObject *myself, Py_ssize_t index) return NULL; } - stgdict = PyObject_stgdict((PyObject *)self); - assert(stgdict); /* Cannot be NULL for pointer object instances */ - ctypes_state *st = GLOBAL_STATE(); StgInfo *stginfo; if (PyStgInfo_FromObject(st, (PyObject *)self, &stginfo) < 0) { return NULL; } - assert(stginfo); + assert(stginfo); /* Cannot be NULL for pointer object instances */ proto = stginfo->proto; assert(proto); - itemdict = PyType_stgdict(proto); - assert(itemdict); /* proto is the item type of the pointer, a ctypes - type, so this cannot be NULL */ StgInfo *iteminfo; if (PyStgInfo_FromType(st, proto, &iteminfo) < 0) { @@ -5305,7 +5312,7 @@ Pointer_item(PyObject *myself, Py_ssize_t index) size = iteminfo->size; offset = index * iteminfo->size; - return PyCData_get(proto, stgdict->getfunc, (PyObject *)self, + return PyCData_get(proto, stginfo->getfunc, (PyObject *)self, index, size, (*(char **)self->b_ptr) + offset); } @@ -5315,7 +5322,6 @@ Pointer_ass_item(PyObject *myself, Py_ssize_t index, PyObject *value) CDataObject *self = (CDataObject *)myself; Py_ssize_t size; Py_ssize_t offset; - StgDictObject *stgdict, *itemdict; PyObject *proto; if (value == NULL) { @@ -5330,23 +5336,16 @@ Pointer_ass_item(PyObject *myself, Py_ssize_t index, PyObject *value) return -1; } - stgdict = PyObject_stgdict((PyObject *)self); - assert(stgdict); /* Cannot be NULL for pointer instances */ - ctypes_state *st = GLOBAL_STATE(); StgInfo *stginfo; if (PyStgInfo_FromObject(st, (PyObject *)self, &stginfo) < 0) { return -1; } - assert(stginfo); + assert(stginfo); /* Cannot be NULL for pointer instances */ proto = stginfo->proto; assert(proto); - itemdict = PyType_stgdict(proto); - assert(itemdict); /* Cannot be NULL because the itemtype of a pointer - is always a ctypes type */ - StgInfo *iteminfo; if (PyStgInfo_FromType(st, proto, &iteminfo) < 0) { return -1; @@ -5357,7 +5356,7 @@ Pointer_ass_item(PyObject *myself, Py_ssize_t index, PyObject *value) size = iteminfo->size; offset = index * iteminfo->size; - return PyCData_set((PyObject *)self, proto, stgdict->setfunc, value, + return PyCData_set((PyObject *)self, proto, stginfo->setfunc, value, index, size, (*(char **)self->b_ptr) + offset); } @@ -5482,7 +5481,6 @@ Pointer_subscript(PyObject *myself, PyObject *item) PySliceObject *slice = (PySliceObject *)item; Py_ssize_t start, stop, step; PyObject *np; - StgDictObject *itemdict; PyObject *proto; Py_ssize_t i, len; size_t cur; @@ -5544,9 +5542,12 @@ Pointer_subscript(PyObject *myself, PyObject *item) assert(stginfo); /* Cannot be NULL for pointer instances */ proto = stginfo->proto; assert(proto); - itemdict = PyType_stgdict(proto); - assert(itemdict); - if (itemdict->getfunc == _ctypes_get_fielddesc("c")->getfunc) { + StgInfo *iteminfo; + if (PyStgInfo_FromType(st, proto, &iteminfo) < 0) { + return NULL; + } + assert(iteminfo); + if (iteminfo->getfunc == _ctypes_get_fielddesc("c")->getfunc) { char *ptr = *(char **)self->b_ptr; char *dest; @@ -5566,7 +5567,7 @@ Pointer_subscript(PyObject *myself, PyObject *item) PyMem_Free(dest); return np; } - if (itemdict->getfunc == _ctypes_get_fielddesc("u")->getfunc) { + if (iteminfo->getfunc == _ctypes_get_fielddesc("u")->getfunc) { wchar_t *ptr = *(wchar_t **)self->b_ptr; wchar_t *dest; diff --git a/Modules/_ctypes/callbacks.c b/Modules/_ctypes/callbacks.c index 284d0a4cd9a2e4..24aa0ef834878d 100644 --- a/Modules/_ctypes/callbacks.c +++ b/Modules/_ctypes/callbacks.c @@ -154,28 +154,25 @@ static void _CallPythonObject(void *mem, ctypes_state *st = GLOBAL_STATE(); for (i = 0; i < nargs; i++) { PyObject *cnv = cnvs[i]; // borrowed ref - StgDictObject *dict; - dict = PyType_stgdict(cnv); StgInfo *info; if (PyStgInfo_FromType(st, cnv, &info) < 0) { goto Done; } - assert(info); - if (dict && dict->getfunc && !_ctypes_simple_instance(cnv)) { - PyObject *v = dict->getfunc(*pArgs, info->size); + if (info && info->getfunc && !_ctypes_simple_instance(cnv)) { + PyObject *v = info->getfunc(*pArgs, info->size); if (!v) { PrintError("create argument %zd:\n", i); goto Done; } args[i] = v; /* XXX XXX XX - We have the problem that c_byte or c_short have dict->size of + We have the problem that c_byte or c_short have info->size of 1 resp. 4, but these parameters are pushed as sizeof(int) bytes. BTW, the same problem occurs when they are pushed as parameters */ - } else if (dict) { + } else if (info) { /* Hm, shouldn't we use PyCData_AtAddress() or something like that instead? */ CDataObject *obj = (CDataObject *)_PyObject_CallNoArgs(cnv); if (!obj) { @@ -378,20 +375,17 @@ CThunkObject *_ctypes_alloc_callback(PyObject *callable, p->setfunc = NULL; p->ffi_restype = &ffi_type_void; } else { - StgDictObject *dict = PyType_stgdict(restype); - StgInfo *info; if (PyStgInfo_FromType(st, restype, &info) < 0) { goto error; } - if (dict == NULL || dict->setfunc == NULL) { + if (info == NULL || info->setfunc == NULL) { PyErr_SetString(PyExc_TypeError, "invalid result type for callback function"); goto error; } - assert(info); - p->setfunc = dict->setfunc; + p->setfunc = info->setfunc; p->ffi_restype = &info->ffi_type_pointer; } diff --git a/Modules/_ctypes/callproc.c b/Modules/_ctypes/callproc.c index 3494e8ef6dd92b..e5351e546fb1dc 100644 --- a/Modules/_ctypes/callproc.c +++ b/Modules/_ctypes/callproc.c @@ -993,7 +993,6 @@ static int _call_function_pointer(int flags, */ static PyObject *GetResult(PyObject *restype, void *result, PyObject *checker) { - StgDictObject *dict; PyObject *retval, *v; if (restype == NULL) @@ -1003,24 +1002,22 @@ static PyObject *GetResult(PyObject *restype, void *result, PyObject *checker) Py_RETURN_NONE; } - dict = PyType_stgdict(restype); - if (dict == NULL) - return PyObject_CallFunction(restype, "i", *(int *)result); - ctypes_state *st = GLOBAL_STATE(); StgInfo *info; if (PyStgInfo_FromType(st, restype, &info) < 0) { return NULL; } - assert(info); + if (info == NULL) { + return PyObject_CallFunction(restype, "i", *(int *)result); + } - if (dict->getfunc && !_ctypes_simple_instance(restype)) { - retval = dict->getfunc(result, info->size); + if (info->getfunc && !_ctypes_simple_instance(restype)) { + retval = info->getfunc(result, info->size); /* If restype is py_object (detected by comparing getfunc with O_get), we have to call Py_DECREF because O_get has already called Py_INCREF. */ - if (dict->getfunc == _ctypes_get_fielddesc("O")->getfunc) { + if (info->getfunc == _ctypes_get_fielddesc("O")->getfunc) { Py_DECREF(retval); } } else diff --git a/Modules/_ctypes/cfield.c b/Modules/_ctypes/cfield.c index 572a857d485856..f2fa9d971d62f7 100644 --- a/Modules/_ctypes/cfield.c +++ b/Modules/_ctypes/cfield.c @@ -120,7 +120,6 @@ PyCField_FromDesc(PyObject *desc, Py_ssize_t index, return a Python string instead of an Array object instance... */ if (PyCArrayTypeObject_Check(st, proto)) { - StgDictObject *idict; StgInfo *ainfo; if (PyStgInfo_FromType(st, proto, &ainfo) < 0) { Py_DECREF(self); @@ -128,19 +127,23 @@ PyCField_FromDesc(PyObject *desc, Py_ssize_t index, } if (ainfo && ainfo->proto) { - idict = PyType_stgdict(ainfo->proto); - if (!idict) { + StgInfo *iinfo; + if (PyStgInfo_FromType(st, ainfo->proto, &iinfo) < 0) { + Py_DECREF(self); + return NULL; + } + if (!iinfo) { PyErr_SetString(PyExc_TypeError, "has no _stginfo_"); Py_DECREF(self); return NULL; } - if (idict->getfunc == _ctypes_get_fielddesc("c")->getfunc) { + if (iinfo->getfunc == _ctypes_get_fielddesc("c")->getfunc) { struct fielddesc *fd = _ctypes_get_fielddesc("s"); getfunc = fd->getfunc; setfunc = fd->setfunc; } - if (idict->getfunc == _ctypes_get_fielddesc("u")->getfunc) { + if (iinfo->getfunc == _ctypes_get_fielddesc("u")->getfunc) { struct fielddesc *fd = _ctypes_get_fielddesc("U"); getfunc = fd->getfunc; setfunc = fd->setfunc; diff --git a/Modules/_ctypes/ctypes.h b/Modules/_ctypes/ctypes.h index 007d1924cba418..c1960a8dc6c036 100644 --- a/Modules/_ctypes/ctypes.h +++ b/Modules/_ctypes/ctypes.h @@ -234,6 +234,8 @@ typedef struct { Py_ssize_t length; /* number of fields */ ffi_type ffi_type_pointer; PyObject *proto; /* Only for Pointer/ArrayObject */ + SETFUNC setfunc; /* Only for simple objects */ + GETFUNC getfunc; /* Only for simple objects */ PARAMFUNC paramfunc; } StgInfo; @@ -266,8 +268,8 @@ typedef struct { //Py_ssize_t length; /* number of fields */ //ffi_type ffi_type_pointer; //PyObject *proto; /* Only for Pointer/ArrayObject */ - SETFUNC setfunc; /* Only for simple objects */ - GETFUNC getfunc; /* Only for simple objects */ + //SETFUNC setfunc; /* Only for simple objects */ + //GETFUNC getfunc; /* Only for simple objects */ //PARAMFUNC paramfunc; /* Following fields only used by PyCFuncPtrType_Type instances */ diff --git a/Modules/_ctypes/stgdict.c b/Modules/_ctypes/stgdict.c index ffcedba5456af6..2ea1881834404e 100644 --- a/Modules/_ctypes/stgdict.c +++ b/Modules/_ctypes/stgdict.c @@ -661,8 +661,8 @@ PyCStructUnionType_update_stgdict(PyObject *type, PyObject *fields, int isStruct case FFI_TYPE_SINT8: case FFI_TYPE_SINT16: case FFI_TYPE_SINT32: - if (dict->getfunc != _ctypes_get_fielddesc("c")->getfunc - && dict->getfunc != _ctypes_get_fielddesc("u")->getfunc + if (info->getfunc != _ctypes_get_fielddesc("c")->getfunc + && info->getfunc != _ctypes_get_fielddesc("u")->getfunc ) break; /* else fall through */ From 68085248c1654e6a6aecee46cb7730b3a9f332d9 Mon Sep 17 00:00:00 2001 From: Petr Viktorin Date: Tue, 20 Feb 2024 22:02:30 +0100 Subject: [PATCH 13/59] Move `argtypes` --- Modules/_ctypes/_ctypes.c | 49 ++++++++++++++++++++++++++++----------- Modules/_ctypes/ctypes.h | 6 +++-- Modules/_ctypes/stgdict.c | 3 +-- 3 files changed, 41 insertions(+), 17 deletions(-) diff --git a/Modules/_ctypes/_ctypes.c b/Modules/_ctypes/_ctypes.c index 7f97b6cebe8a47..d3458e2ffe103f 100644 --- a/Modules/_ctypes/_ctypes.c +++ b/Modules/_ctypes/_ctypes.c @@ -495,6 +495,7 @@ CType_Type_clear(PyObject *self) } if (info) { Py_CLEAR(info->proto); + Py_CLEAR(info->argtypes); } return 0; } @@ -2587,7 +2588,7 @@ make_funcptrtype_dict(StgDictObject *stgdict, StgInfo *stginfo) Py_DECREF(ob); return -1; } - stgdict->argtypes = ob; + stginfo->argtypes = ob; stgdict->converters = converters; } @@ -3478,14 +3479,17 @@ PyCFuncPtr_set_argtypes(PyCFuncPtrObject *self, PyObject *ob, void *Py_UNUSED(ig static PyObject * PyCFuncPtr_get_argtypes(PyCFuncPtrObject *self, void *Py_UNUSED(ignored)) { - StgDictObject *dict; if (self->argtypes) { return Py_NewRef(self->argtypes); } - dict = PyObject_stgdict((PyObject *)self); - assert(dict); /* Cannot be NULL for PyCFuncPtrObject instances */ - if (dict->argtypes) { - return Py_NewRef(dict->argtypes); + ctypes_state *st = GLOBAL_STATE(); + StgInfo *info; + if (PyStgInfo_FromObject(st, (PyObject *)self, &info) < 0) { + return NULL; + } + assert(info); /* Cannot be NULL for PyCFuncPtrObject instances */ + if (info->argtypes) { + return Py_NewRef(info->argtypes); } else { Py_RETURN_NONE; } @@ -3594,15 +3598,21 @@ _validate_paramflags(PyTypeObject *type, PyObject *paramflags) StgDictObject *dict; PyObject *argtypes; + ctypes_state *st = GLOBAL_STATE(); dict = PyType_stgdict((PyObject *)type); - if (!dict) { + StgInfo *info; + if (PyStgInfo_FromType(st, (PyObject *)type, &info) < 0) { + return -1; + } + if (!info) { PyErr_SetString(PyExc_TypeError, "abstract class"); return 0; } - argtypes = dict->argtypes; + assert(dict); + argtypes = info->argtypes; - if (paramflags == NULL || dict->argtypes == NULL) + if (paramflags == NULL || info->argtypes == NULL) return 1; if (!PyTuple_Check(paramflags)) { @@ -3612,7 +3622,7 @@ _validate_paramflags(PyTypeObject *type, PyObject *paramflags) } len = PyTuple_GET_SIZE(paramflags); - if (len != PyTuple_GET_SIZE(dict->argtypes)) { + if (len != PyTuple_GET_SIZE(info->argtypes)) { PyErr_SetString(PyExc_ValueError, "paramflags must have the same length as argtypes"); return 0; @@ -3893,17 +3903,23 @@ PyCFuncPtr_new(PyTypeObject *type, PyObject *args, PyObject *kwds) } */ + ctypes_state *st = GLOBAL_STATE(); dict = PyType_stgdict((PyObject *)type); + StgInfo *info; + if (PyStgInfo_FromType(st, (PyObject *)type, &info) < 0) { + return NULL; + } /* XXXX Fails if we do: 'PyCFuncPtr(lambda x: x)' */ - if (!dict || !dict->argtypes) { + if (!info || !info->argtypes) { PyErr_SetString(PyExc_TypeError, "cannot construct instance of this class:" " no argtypes"); return NULL; } + assert(dict); thunk = _ctypes_alloc_callback(callable, - dict->argtypes, + info->argtypes, dict->restype, dict->flags); if (!thunk) @@ -4258,11 +4274,18 @@ PyCFuncPtr_call(PyCFuncPtrObject *self, PyObject *inargs, PyObject *kwds) int outmask; unsigned int numretvals; + ctypes_state *st = GLOBAL_STATE(); + StgInfo *info; + if (PyStgInfo_FromObject(st, (PyObject *)self, &info) < 0) { + return NULL; + } + assert(info); /* Cannot be NULL for PyCFuncPtrObject instances */ + assert(dict); /* Cannot be NULL for PyCFuncPtrObject instances */ restype = self->restype ? self->restype : dict->restype; converters = self->converters ? self->converters : dict->converters; checker = self->checker ? self->checker : dict->checker; - argtypes = self->argtypes ? self->argtypes : dict->argtypes; + argtypes = self->argtypes ? self->argtypes : info->argtypes; /* later, we probably want to have an errcheck field in stgdict */ errcheck = self->errcheck /* ? self->errcheck : dict->errcheck */; diff --git a/Modules/_ctypes/ctypes.h b/Modules/_ctypes/ctypes.h index c1960a8dc6c036..483d38db9fcc51 100644 --- a/Modules/_ctypes/ctypes.h +++ b/Modules/_ctypes/ctypes.h @@ -236,8 +236,10 @@ typedef struct { PyObject *proto; /* Only for Pointer/ArrayObject */ SETFUNC setfunc; /* Only for simple objects */ GETFUNC getfunc; /* Only for simple objects */ - PARAMFUNC paramfunc; + + /* Following fields only used by PyCFuncPtrType_Type instances */ + PyObject *argtypes; /* tuple of CDataObjects */ } StgInfo; // Get a PyCTypeDataObject. These Return -1 on error, 0 if "not found", 1 on OK. @@ -273,7 +275,7 @@ typedef struct { //PARAMFUNC paramfunc; /* Following fields only used by PyCFuncPtrType_Type instances */ - PyObject *argtypes; /* tuple of CDataObjects */ + //PyObject *argtypes; /* tuple of CDataObjects */ PyObject *converters; /* tuple([t.from_param for t in argtypes]) */ PyObject *restype; /* CDataObject or NULL */ PyObject *checker; diff --git a/Modules/_ctypes/stgdict.c b/Modules/_ctypes/stgdict.c index 2ea1881834404e..56b29c8982bf77 100644 --- a/Modules/_ctypes/stgdict.c +++ b/Modules/_ctypes/stgdict.c @@ -103,7 +103,6 @@ PyCStgDict_init(StgDictObject *self, PyObject *args, PyObject *kwds) static int PyCStgDict_clear(StgDictObject *self) { - Py_CLEAR(self->argtypes); Py_CLEAR(self->converters); Py_CLEAR(self->restype); Py_CLEAR(self->checker); @@ -156,7 +155,7 @@ PyCStgDict_clone(StgDictObject *dst, StgDictObject *src, memcpy(dst_info, src_info, sizeof(StgInfo)); Py_XINCREF(dst_info->proto); - Py_XINCREF(dst->argtypes); + Py_XINCREF(dst_info->argtypes); Py_XINCREF(dst->converters); Py_XINCREF(dst->restype); Py_XINCREF(dst->checker); From f031c00a76941a895547fd2829f95822194701e7 Mon Sep 17 00:00:00 2001 From: Petr Viktorin Date: Tue, 20 Feb 2024 22:04:27 +0100 Subject: [PATCH 14/59] Move `converters` --- Modules/_ctypes/_ctypes.c | 5 +++-- Modules/_ctypes/ctypes.h | 3 ++- Modules/_ctypes/stgdict.c | 3 +-- 3 files changed, 6 insertions(+), 5 deletions(-) diff --git a/Modules/_ctypes/_ctypes.c b/Modules/_ctypes/_ctypes.c index d3458e2ffe103f..110786a0044ba0 100644 --- a/Modules/_ctypes/_ctypes.c +++ b/Modules/_ctypes/_ctypes.c @@ -496,6 +496,7 @@ CType_Type_clear(PyObject *self) if (info) { Py_CLEAR(info->proto); Py_CLEAR(info->argtypes); + Py_CLEAR(info->converters); } return 0; } @@ -2589,7 +2590,7 @@ make_funcptrtype_dict(StgDictObject *stgdict, StgInfo *stginfo) return -1; } stginfo->argtypes = ob; - stgdict->converters = converters; + stginfo->converters = converters; } if (PyDict_GetItemRef((PyObject *)stgdict, &_Py_ID(_restype_), &ob) < 0) { @@ -4283,7 +4284,7 @@ PyCFuncPtr_call(PyCFuncPtrObject *self, PyObject *inargs, PyObject *kwds) assert(dict); /* Cannot be NULL for PyCFuncPtrObject instances */ restype = self->restype ? self->restype : dict->restype; - converters = self->converters ? self->converters : dict->converters; + converters = self->converters ? self->converters : info->converters; checker = self->checker ? self->checker : dict->checker; argtypes = self->argtypes ? self->argtypes : info->argtypes; /* later, we probably want to have an errcheck field in stgdict */ diff --git a/Modules/_ctypes/ctypes.h b/Modules/_ctypes/ctypes.h index 483d38db9fcc51..ff7d0545c8859a 100644 --- a/Modules/_ctypes/ctypes.h +++ b/Modules/_ctypes/ctypes.h @@ -240,6 +240,7 @@ typedef struct { /* Following fields only used by PyCFuncPtrType_Type instances */ PyObject *argtypes; /* tuple of CDataObjects */ + PyObject *converters; /* tuple([t.from_param for t in argtypes]) */ } StgInfo; // Get a PyCTypeDataObject. These Return -1 on error, 0 if "not found", 1 on OK. @@ -276,7 +277,7 @@ typedef struct { /* Following fields only used by PyCFuncPtrType_Type instances */ //PyObject *argtypes; /* tuple of CDataObjects */ - PyObject *converters; /* tuple([t.from_param for t in argtypes]) */ + //PyObject *converters; /* tuple([t.from_param for t in argtypes]) */ PyObject *restype; /* CDataObject or NULL */ PyObject *checker; int flags; /* calling convention and such */ diff --git a/Modules/_ctypes/stgdict.c b/Modules/_ctypes/stgdict.c index 56b29c8982bf77..6414d7a284df24 100644 --- a/Modules/_ctypes/stgdict.c +++ b/Modules/_ctypes/stgdict.c @@ -103,7 +103,6 @@ PyCStgDict_init(StgDictObject *self, PyObject *args, PyObject *kwds) static int PyCStgDict_clear(StgDictObject *self) { - Py_CLEAR(self->converters); Py_CLEAR(self->restype); Py_CLEAR(self->checker); return 0; @@ -156,7 +155,7 @@ PyCStgDict_clone(StgDictObject *dst, StgDictObject *src, Py_XINCREF(dst_info->proto); Py_XINCREF(dst_info->argtypes); - Py_XINCREF(dst->converters); + Py_XINCREF(dst_info->converters); Py_XINCREF(dst->restype); Py_XINCREF(dst->checker); From 535176cad1245bd8fc3aaeb57a77fbdbca383684 Mon Sep 17 00:00:00 2001 From: Petr Viktorin Date: Tue, 20 Feb 2024 22:09:23 +0100 Subject: [PATCH 15/59] Move `restype` --- Modules/_ctypes/_ctypes.c | 20 ++++++++++++-------- Modules/_ctypes/ctypes.h | 3 ++- Modules/_ctypes/stgdict.c | 3 +-- 3 files changed, 15 insertions(+), 11 deletions(-) diff --git a/Modules/_ctypes/_ctypes.c b/Modules/_ctypes/_ctypes.c index 110786a0044ba0..028f7ce0e56eb6 100644 --- a/Modules/_ctypes/_ctypes.c +++ b/Modules/_ctypes/_ctypes.c @@ -497,6 +497,7 @@ CType_Type_clear(PyObject *self) Py_CLEAR(info->proto); Py_CLEAR(info->argtypes); Py_CLEAR(info->converters); + Py_CLEAR(info->restype); } return 0; } @@ -2603,7 +2604,7 @@ make_funcptrtype_dict(StgDictObject *stgdict, StgInfo *stginfo) Py_DECREF(ob); return -1; } - stgdict->restype = ob; + stginfo->restype = ob; if (PyObject_GetOptionalAttr(ob, &_Py_ID(_check_retval_), &stgdict->checker) < 0) { @@ -3445,14 +3446,17 @@ PyCFuncPtr_set_restype(PyCFuncPtrObject *self, PyObject *ob, void *Py_UNUSED(ign static PyObject * PyCFuncPtr_get_restype(PyCFuncPtrObject *self, void *Py_UNUSED(ignored)) { - StgDictObject *dict; if (self->restype) { return Py_NewRef(self->restype); } - dict = PyObject_stgdict((PyObject *)self); - assert(dict); /* Cannot be NULL for PyCFuncPtrObject instances */ - if (dict->restype) { - return Py_NewRef(dict->restype); + ctypes_state *st = GLOBAL_STATE(); + StgInfo *info; + if (PyStgInfo_FromObject(st, (PyObject *)self, &info) < 0) { + return NULL; + } + assert(info); /* Cannot be NULL for PyCFuncPtrObject instances */ + if (info->restype) { + return Py_NewRef(info->restype); } else { Py_RETURN_NONE; } @@ -3921,7 +3925,7 @@ PyCFuncPtr_new(PyTypeObject *type, PyObject *args, PyObject *kwds) thunk = _ctypes_alloc_callback(callable, info->argtypes, - dict->restype, + info->restype, dict->flags); if (!thunk) return NULL; @@ -4283,7 +4287,7 @@ PyCFuncPtr_call(PyCFuncPtrObject *self, PyObject *inargs, PyObject *kwds) assert(info); /* Cannot be NULL for PyCFuncPtrObject instances */ assert(dict); /* Cannot be NULL for PyCFuncPtrObject instances */ - restype = self->restype ? self->restype : dict->restype; + restype = self->restype ? self->restype : info->restype; converters = self->converters ? self->converters : info->converters; checker = self->checker ? self->checker : dict->checker; argtypes = self->argtypes ? self->argtypes : info->argtypes; diff --git a/Modules/_ctypes/ctypes.h b/Modules/_ctypes/ctypes.h index ff7d0545c8859a..48ec895ba27697 100644 --- a/Modules/_ctypes/ctypes.h +++ b/Modules/_ctypes/ctypes.h @@ -241,6 +241,7 @@ typedef struct { /* Following fields only used by PyCFuncPtrType_Type instances */ PyObject *argtypes; /* tuple of CDataObjects */ PyObject *converters; /* tuple([t.from_param for t in argtypes]) */ + PyObject *restype; /* CDataObject or NULL */ } StgInfo; // Get a PyCTypeDataObject. These Return -1 on error, 0 if "not found", 1 on OK. @@ -278,7 +279,7 @@ typedef struct { /* Following fields only used by PyCFuncPtrType_Type instances */ //PyObject *argtypes; /* tuple of CDataObjects */ //PyObject *converters; /* tuple([t.from_param for t in argtypes]) */ - PyObject *restype; /* CDataObject or NULL */ + //PyObject *restype; /* CDataObject or NULL */ PyObject *checker; int flags; /* calling convention and such */ diff --git a/Modules/_ctypes/stgdict.c b/Modules/_ctypes/stgdict.c index 6414d7a284df24..bcbaeb87abb646 100644 --- a/Modules/_ctypes/stgdict.c +++ b/Modules/_ctypes/stgdict.c @@ -103,7 +103,6 @@ PyCStgDict_init(StgDictObject *self, PyObject *args, PyObject *kwds) static int PyCStgDict_clear(StgDictObject *self) { - Py_CLEAR(self->restype); Py_CLEAR(self->checker); return 0; } @@ -156,7 +155,7 @@ PyCStgDict_clone(StgDictObject *dst, StgDictObject *src, Py_XINCREF(dst_info->proto); Py_XINCREF(dst_info->argtypes); Py_XINCREF(dst_info->converters); - Py_XINCREF(dst->restype); + Py_XINCREF(dst_info->restype); Py_XINCREF(dst->checker); if (src->format) { From 5260003d06d6fdbe3b05e3774cceb19cef5ac1b0 Mon Sep 17 00:00:00 2001 From: Petr Viktorin Date: Tue, 20 Feb 2024 22:21:25 +0100 Subject: [PATCH 16/59] Move `checker` --- Modules/_ctypes/_ctypes.c | 9 +++++---- Modules/_ctypes/ctypes.h | 3 ++- Modules/_ctypes/stgdict.c | 3 +-- 3 files changed, 8 insertions(+), 7 deletions(-) diff --git a/Modules/_ctypes/_ctypes.c b/Modules/_ctypes/_ctypes.c index 028f7ce0e56eb6..6aeeed65f84f85 100644 --- a/Modules/_ctypes/_ctypes.c +++ b/Modules/_ctypes/_ctypes.c @@ -498,6 +498,7 @@ CType_Type_clear(PyObject *self) Py_CLEAR(info->argtypes); Py_CLEAR(info->converters); Py_CLEAR(info->restype); + Py_CLEAR(info->checker); } return 0; } @@ -2606,7 +2607,7 @@ make_funcptrtype_dict(StgDictObject *stgdict, StgInfo *stginfo) } stginfo->restype = ob; if (PyObject_GetOptionalAttr(ob, &_Py_ID(_check_retval_), - &stgdict->checker) < 0) + &stginfo->checker) < 0) { return -1; } @@ -4289,10 +4290,10 @@ PyCFuncPtr_call(PyCFuncPtrObject *self, PyObject *inargs, PyObject *kwds) assert(dict); /* Cannot be NULL for PyCFuncPtrObject instances */ restype = self->restype ? self->restype : info->restype; converters = self->converters ? self->converters : info->converters; - checker = self->checker ? self->checker : dict->checker; + checker = self->checker ? self->checker : info->checker; argtypes = self->argtypes ? self->argtypes : info->argtypes; -/* later, we probably want to have an errcheck field in stgdict */ - errcheck = self->errcheck /* ? self->errcheck : dict->errcheck */; +/* later, we probably want to have an errcheck field in stginfo */ + errcheck = self->errcheck /* ? self->errcheck : info->errcheck */; pProc = *(void **)self->b_ptr; diff --git a/Modules/_ctypes/ctypes.h b/Modules/_ctypes/ctypes.h index 48ec895ba27697..8dc7ef16fd802d 100644 --- a/Modules/_ctypes/ctypes.h +++ b/Modules/_ctypes/ctypes.h @@ -242,6 +242,7 @@ typedef struct { PyObject *argtypes; /* tuple of CDataObjects */ PyObject *converters; /* tuple([t.from_param for t in argtypes]) */ PyObject *restype; /* CDataObject or NULL */ + PyObject *checker; } StgInfo; // Get a PyCTypeDataObject. These Return -1 on error, 0 if "not found", 1 on OK. @@ -280,7 +281,7 @@ typedef struct { //PyObject *argtypes; /* tuple of CDataObjects */ //PyObject *converters; /* tuple([t.from_param for t in argtypes]) */ //PyObject *restype; /* CDataObject or NULL */ - PyObject *checker; + //PyObject *checker; int flags; /* calling convention and such */ /* pep3118 fields, pointers need PyMem_Free */ diff --git a/Modules/_ctypes/stgdict.c b/Modules/_ctypes/stgdict.c index bcbaeb87abb646..a807d342a587f7 100644 --- a/Modules/_ctypes/stgdict.c +++ b/Modules/_ctypes/stgdict.c @@ -103,7 +103,6 @@ PyCStgDict_init(StgDictObject *self, PyObject *args, PyObject *kwds) static int PyCStgDict_clear(StgDictObject *self) { - Py_CLEAR(self->checker); return 0; } @@ -156,7 +155,7 @@ PyCStgDict_clone(StgDictObject *dst, StgDictObject *src, Py_XINCREF(dst_info->argtypes); Py_XINCREF(dst_info->converters); Py_XINCREF(dst_info->restype); - Py_XINCREF(dst->checker); + Py_XINCREF(dst_info->checker); if (src->format) { dst->format = PyMem_Malloc(strlen(src->format) + 1); From 0d3017eee53dfe40a84ab2a9bae2ec1f58ed670c Mon Sep 17 00:00:00 2001 From: Petr Viktorin Date: Tue, 20 Feb 2024 22:31:43 +0100 Subject: [PATCH 17/59] Move `flags` --- Modules/_ctypes/_ctypes.c | 67 +++++++++++++++++++-------------------- Modules/_ctypes/ctypes.h | 3 +- Modules/_ctypes/stgdict.c | 35 ++++++++++---------- 3 files changed, 51 insertions(+), 54 deletions(-) diff --git a/Modules/_ctypes/_ctypes.c b/Modules/_ctypes/_ctypes.c index 6aeeed65f84f85..cd757465c0983d 100644 --- a/Modules/_ctypes/_ctypes.c +++ b/Modules/_ctypes/_ctypes.c @@ -655,14 +655,14 @@ StructUnionType_new(PyTypeObject *type, PyObject *args, PyObject *kwds, int isSt Py_DECREF(result); return NULL; } - if (!isStruct) { - dict->flags |= TYPEFLAG_HASUNION; - } StgInfo *info = PyStgInfo_Init(st, result); if (!info) { Py_DECREF(result); return NULL; } + if (!isStruct) { + info->flags |= TYPEFLAG_HASUNION; + } /* replace the class dict by our updated stgdict, which holds info about storage requirements of the instances */ @@ -713,8 +713,8 @@ StructUnionType_new(PyTypeObject *type, PyObject *args, PyObject *kwds, int isSt Py_DECREF(result); return NULL; } - dict->flags &= ~DICTFLAG_FINAL; /* clear the 'final' flag in the subclass dict */ - basedict->flags |= DICTFLAG_FINAL; /* set the 'final' flag in the baseclass dict */ + info->flags &= ~DICTFLAG_FINAL; /* clear the 'final' flag in the subclass info */ + baseinfo->flags |= DICTFLAG_FINAL; /* set the 'final' flag in the baseclass info */ return (PyObject *)result; } } @@ -1218,7 +1218,7 @@ PyCPointerType_new(PyTypeObject *type, PyObject *args, PyObject *kwds) stginfo->length = 1; stginfo->ffi_type_pointer = ffi_type_pointer; stginfo->paramfunc = PyCPointerType_paramfunc; - stgdict->flags |= TYPEFLAG_ISPOINTER; + stginfo->flags |= TYPEFLAG_ISPOINTER; if (PyDict_GetItemRef(typedict, &_Py_ID(_type_), &proto) < 0) { Py_DECREF((PyObject *)result); @@ -1683,8 +1683,8 @@ PyCArrayType_new(PyTypeObject *type, PyObject *args, PyObject *kwds) itemalign = iteminfo->align; - if (itemdict->flags & (TYPEFLAG_ISPOINTER | TYPEFLAG_HASPOINTER)) - stgdict->flags |= TYPEFLAG_HASPOINTER; + if (iteminfo->flags & (TYPEFLAG_ISPOINTER | TYPEFLAG_HASPOINTER)) + stginfo->flags |= TYPEFLAG_HASPOINTER; stginfo->size = itemsize * length; stginfo->align = itemalign; @@ -2271,21 +2271,21 @@ PyCSimpleType_new(PyTypeObject *type, PyObject *args, PyObject *kwds) switch (*proto_str) { case 'z': /* c_char_p */ ml = &c_char_p_method; - stgdict->flags |= TYPEFLAG_ISPOINTER; + stginfo->flags |= TYPEFLAG_ISPOINTER; break; case 'Z': /* c_wchar_p */ ml = &c_wchar_p_method; - stgdict->flags |= TYPEFLAG_ISPOINTER; + stginfo->flags |= TYPEFLAG_ISPOINTER; break; case 'P': /* c_void_p */ ml = &c_void_p_method; - stgdict->flags |= TYPEFLAG_ISPOINTER; + stginfo->flags |= TYPEFLAG_ISPOINTER; break; case 's': case 'X': case 'O': ml = NULL; - stgdict->flags |= TYPEFLAG_ISPOINTER; + stginfo->flags |= TYPEFLAG_ISPOINTER; break; default: ml = NULL; @@ -2578,7 +2578,7 @@ make_funcptrtype_dict(StgDictObject *stgdict, StgInfo *stginfo) Py_XDECREF(ob); return -1; } - stgdict->flags = PyLong_AsUnsignedLongMask(ob) | TYPEFLAG_ISPOINTER; + stginfo->flags = PyLong_AsUnsignedLongMask(ob) | TYPEFLAG_ISPOINTER; Py_DECREF(ob); /* _argtypes_ is optional... */ @@ -2686,7 +2686,7 @@ PyCFuncPtrType_new(PyTypeObject *type, PyObject *args, PyObject *kwds) Py_DECREF((PyObject *)stgdict); return NULL; } - stgdict->flags |= TYPEFLAG_ISPOINTER; + stginfo->flags |= TYPEFLAG_ISPOINTER; /* replace the class dict by our updated storage dict */ if (-1 == PyDict_Update((PyObject *)stgdict, result->tp_dict)) { @@ -2949,7 +2949,14 @@ PyCData_reduce(PyObject *myself, PyObject *args) { CDataObject *self = (CDataObject *)myself; - if (PyObject_stgdict(myself)->flags & (TYPEFLAG_ISPOINTER|TYPEFLAG_HASPOINTER)) { + ctypes_state *st = GLOBAL_STATE(); + StgInfo *info; + if (PyStgInfo_FromObject(st, myself, &info) < 0) { + return NULL; + } + assert(info); + + if (info->flags & (TYPEFLAG_ISPOINTER|TYPEFLAG_HASPOINTER)) { PyErr_SetString(PyExc_ValueError, "ctypes objects containing pointers cannot be pickled"); return NULL; @@ -3108,7 +3115,7 @@ PyCData_FromBaseObj(PyObject *type, PyObject *base, Py_ssize_t index, char *adr) } assert(info); - dict->flags |= DICTFLAG_FINAL; + info->flags |= DICTFLAG_FINAL; cmem = (CDataObject *)((PyTypeObject *)type)->tp_alloc((PyTypeObject *)type, 0); if (cmem == NULL) { return NULL; @@ -3140,28 +3147,25 @@ PyObject * PyCData_AtAddress(PyObject *type, void *buf) { CDataObject *pd; - StgDictObject *dict; if (PySys_Audit("ctypes.cdata", "n", (Py_ssize_t)buf) < 0) { return NULL; } assert(PyType_Check(type)); - dict = PyType_stgdict(type); - if (!dict) { - PyErr_SetString(PyExc_TypeError, - "abstract class"); - return NULL; - } ctypes_state *st = GLOBAL_STATE(); StgInfo *info; if (PyStgInfo_FromType(st, type, &info) < 0) { return NULL; } - assert(info); + if (!info) { + PyErr_SetString(PyExc_TypeError, + "abstract class"); + return NULL; + } - dict->flags |= DICTFLAG_FINAL; + info->flags |= DICTFLAG_FINAL; pd = (CDataObject *)((PyTypeObject *)type)->tp_alloc((PyTypeObject *)type, 0); if (!pd) { @@ -3373,7 +3377,7 @@ GenericPyCData_new(PyTypeObject *type, PyObject *args, PyObject *kwds) } assert(info); - dict->flags |= DICTFLAG_FINAL; + info->flags |= DICTFLAG_FINAL; obj = (CDataObject *)type->tp_alloc(type, 0); if (!obj) @@ -3858,7 +3862,6 @@ PyCFuncPtr_new(PyTypeObject *type, PyObject *args, PyObject *kwds) { PyCFuncPtrObject *self; PyObject *callable; - StgDictObject *dict; CThunkObject *thunk; if (PyTuple_GET_SIZE(args) == 0) @@ -3910,7 +3913,6 @@ PyCFuncPtr_new(PyTypeObject *type, PyObject *args, PyObject *kwds) */ ctypes_state *st = GLOBAL_STATE(); - dict = PyType_stgdict((PyObject *)type); StgInfo *info; if (PyStgInfo_FromType(st, (PyObject *)type, &info) < 0) { return NULL; @@ -3922,12 +3924,11 @@ PyCFuncPtr_new(PyTypeObject *type, PyObject *args, PyObject *kwds) " no argtypes"); return NULL; } - assert(dict); thunk = _ctypes_alloc_callback(callable, info->argtypes, info->restype, - dict->flags); + info->flags); if (!thunk) return NULL; @@ -4267,7 +4268,6 @@ PyCFuncPtr_call(PyCFuncPtrObject *self, PyObject *inargs, PyObject *kwds) PyObject *converters; PyObject *checker; PyObject *argtypes; - StgDictObject *dict = PyObject_stgdict((PyObject *)self); PyObject *result; PyObject *callargs; PyObject *errcheck; @@ -4287,7 +4287,6 @@ PyCFuncPtr_call(PyCFuncPtrObject *self, PyObject *inargs, PyObject *kwds) } assert(info); /* Cannot be NULL for PyCFuncPtrObject instances */ - assert(dict); /* Cannot be NULL for PyCFuncPtrObject instances */ restype = self->restype ? self->restype : info->restype; converters = self->converters ? self->converters : info->converters; checker = self->checker ? self->checker : info->checker; @@ -4341,7 +4340,7 @@ PyCFuncPtr_call(PyCFuncPtrObject *self, PyObject *inargs, PyObject *kwds) int actual = Py_SAFE_DOWNCAST(PyTuple_GET_SIZE(callargs), Py_ssize_t, int); - if ((dict->flags & FUNCFLAG_CDECL) == FUNCFLAG_CDECL) { + if ((info->flags & FUNCFLAG_CDECL) == FUNCFLAG_CDECL) { /* For cdecl functions, we allow more actual arguments than the length of the argtypes tuple. */ @@ -4371,7 +4370,7 @@ PyCFuncPtr_call(PyCFuncPtrObject *self, PyObject *inargs, PyObject *kwds) piunk, self->iid, #endif - dict->flags, + info->flags, converters, restype, checker); diff --git a/Modules/_ctypes/ctypes.h b/Modules/_ctypes/ctypes.h index 8dc7ef16fd802d..59d0269e6060f2 100644 --- a/Modules/_ctypes/ctypes.h +++ b/Modules/_ctypes/ctypes.h @@ -243,6 +243,7 @@ typedef struct { PyObject *converters; /* tuple([t.from_param for t in argtypes]) */ PyObject *restype; /* CDataObject or NULL */ PyObject *checker; + int flags; /* calling convention and such */ } StgInfo; // Get a PyCTypeDataObject. These Return -1 on error, 0 if "not found", 1 on OK. @@ -282,7 +283,7 @@ typedef struct { //PyObject *converters; /* tuple([t.from_param for t in argtypes]) */ //PyObject *restype; /* CDataObject or NULL */ //PyObject *checker; - int flags; /* calling convention and such */ + //int flags; /* calling convention and such */ /* pep3118 fields, pointers need PyMem_Free */ char *format; diff --git a/Modules/_ctypes/stgdict.c b/Modules/_ctypes/stgdict.c index a807d342a587f7..ac652bdad81d87 100644 --- a/Modules/_ctypes/stgdict.c +++ b/Modules/_ctypes/stgdict.c @@ -435,7 +435,7 @@ _ctypes_alloc_format_padding(const char *prefix, Py_ssize_t padding) int PyCStructUnionType_update_stgdict(PyObject *type, PyObject *fields, int isStruct) { - StgDictObject *stgdict, *basedict; + StgDictObject *stgdict; Py_ssize_t len, offset, size, align, i; Py_ssize_t union_size, total_align, aligned_size; Py_ssize_t field_size = 0; @@ -531,7 +531,7 @@ PyCStructUnionType_update_stgdict(PyObject *type, PyObject *fields, int isStruct /* If this structure/union is already marked final we cannot assign _fields_ anymore. */ - if (stgdict->flags & DICTFLAG_FINAL) {/* is final ? */ + if (stginfo->flags & DICTFLAG_FINAL) {/* is final ? */ PyErr_SetString(PyExc_AttributeError, "_fields_ is final"); return -1; @@ -545,22 +545,19 @@ PyCStructUnionType_update_stgdict(PyObject *type, PyObject *fields, int isStruct if (stginfo->ffi_type_pointer.elements) PyMem_Free(stginfo->ffi_type_pointer.elements); - basedict = PyType_stgdict((PyObject *)((PyTypeObject *)type)->tp_base); - if (basedict) { - stgdict->flags |= (basedict->flags & - (TYPEFLAG_HASUNION | TYPEFLAG_HASBITFIELD)); - } - StgInfo *baseinfo; if (PyStgInfo_FromType(st, (PyObject *)((PyTypeObject *)type)->tp_base, &baseinfo) < 0) { return -1; } - + if (baseinfo) { + stginfo->flags |= (baseinfo->flags & + (TYPEFLAG_HASUNION | TYPEFLAG_HASBITFIELD)); + } if (!isStruct) { - stgdict->flags |= TYPEFLAG_HASUNION; + stginfo->flags |= TYPEFLAG_HASUNION; } - if (basedict) { + if (baseinfo) { size = offset = baseinfo->size; align = baseinfo->align; union_size = 0; @@ -640,12 +637,12 @@ PyCStructUnionType_update_stgdict(PyObject *type, PyObject *fields, int isStruct assert(info); stginfo->ffi_type_pointer.elements[ffi_ofs + i] = &info->ffi_type_pointer; - if (dict->flags & (TYPEFLAG_ISPOINTER | TYPEFLAG_HASPOINTER)) - stgdict->flags |= TYPEFLAG_HASPOINTER; - stgdict->flags |= dict->flags & (TYPEFLAG_HASUNION | TYPEFLAG_HASBITFIELD); - dict->flags |= DICTFLAG_FINAL; /* mark field type final */ + if (info->flags & (TYPEFLAG_ISPOINTER | TYPEFLAG_HASPOINTER)) + stginfo->flags |= TYPEFLAG_HASPOINTER; + stginfo->flags |= info->flags & (TYPEFLAG_HASUNION | TYPEFLAG_HASBITFIELD); + info->flags |= DICTFLAG_FINAL; /* mark field type final */ if (PyTuple_Size(pair) == 3) { /* bits specified */ - stgdict->flags |= TYPEFLAG_HASBITFIELD; + stginfo->flags |= TYPEFLAG_HASBITFIELD; switch(info->ffi_type_pointer.type) { case FFI_TYPE_UINT8: case FFI_TYPE_UINT16: @@ -983,7 +980,7 @@ PyCStructUnionType_update_stgdict(PyObject *type, PyObject *fields, int isStruct if (num_ffi_types > 0) { memset(structs, 0, num_ffi_types * sizeof(ffi_type)); } - if (ffi_ofs && (basedict != NULL)) { + if (ffi_ofs && (baseinfo != NULL)) { memcpy(element_types, baseinfo->ffi_type_pointer.elements, ffi_ofs * sizeof(ffi_type *)); @@ -1082,12 +1079,12 @@ PyCStructUnionType_update_stgdict(PyObject *type, PyObject *fields, int isStruct /* We did check that this flag was NOT set above, it must not have been set until now. */ - if (stgdict->flags & DICTFLAG_FINAL) { + if (stginfo->flags & DICTFLAG_FINAL) { PyErr_SetString(PyExc_AttributeError, "Structure or union cannot contain itself"); return -1; } - stgdict->flags |= DICTFLAG_FINAL; + stginfo->flags |= DICTFLAG_FINAL; return MakeAnonFields(type); } From 269b82569a38cd5ddeef6be9f0d65fb5ee6a12e8 Mon Sep 17 00:00:00 2001 From: Petr Viktorin Date: Tue, 20 Feb 2024 22:46:32 +0100 Subject: [PATCH 18/59] Move `format` --- Modules/_ctypes/_ctypes.c | 69 +++++++++++++++++++++++------------ Modules/_ctypes/callproc.c | 12 +++++-- Modules/_ctypes/ctypes.h | 5 ++- Modules/_ctypes/stgdict.c | 74 +++++++++++++++++--------------------- 4 files changed, 93 insertions(+), 67 deletions(-) diff --git a/Modules/_ctypes/_ctypes.c b/Modules/_ctypes/_ctypes.c index cd757465c0983d..e222ba594b2d0f 100644 --- a/Modules/_ctypes/_ctypes.c +++ b/Modules/_ctypes/_ctypes.c @@ -513,6 +513,7 @@ CType_Type_dealloc(PyObject *self) } if (info) { PyMem_Free(info->ffi_type_pointer.elements); + PyMem_Free(info->format); } PyTypeObject *tp = Py_TYPE(self); @@ -533,6 +534,9 @@ CType_Type_sizeof(PyObject *self) { return NULL; } if (info) { + if (info->format) { + size += strlen(info->format) + 1; + } if (info->ffi_type_pointer.elements) { size += (info->length + 1) * sizeof(ffi_type *); } @@ -672,8 +676,8 @@ StructUnionType_new(PyTypeObject *type, PyObject *args, PyObject *kwds, int isSt return NULL; } Py_SETREF(result->tp_dict, (PyObject *)dict); - dict->format = _ctypes_alloc_format_string(NULL, "B"); - if (dict->format == NULL) { + info->format = _ctypes_alloc_format_string(NULL, "B"); + if (info->format == NULL) { Py_DECREF(result); return NULL; } @@ -1235,23 +1239,31 @@ PyCPointerType_new(PyTypeObject *type, PyObject *args, PyObject *kwds) return NULL; } itemdict = PyType_stgdict(proto); + StgInfo *iteminfo; + if (PyStgInfo_FromType(st, proto, &iteminfo) < 0) { + Py_DECREF(proto); + Py_DECREF((PyObject *)result); + Py_DECREF((PyObject *)stgdict); + return NULL; + } /* PyCPointerType_SetProto has verified proto has a stgdict. */ + assert(iteminfo); assert(itemdict); - /* If itemdict->format is NULL, then this is a pointer to an + /* If iteminfo->format is NULL, then this is a pointer to an incomplete type. We create a generic format string 'pointer to bytes' in this case. XXX Better would be to fix the format string later... */ - current_format = itemdict->format ? itemdict->format : "B"; + current_format = iteminfo->format ? iteminfo->format : "B"; if (itemdict->shape != NULL) { /* pointer to an array: the shape needs to be prefixed */ - stgdict->format = _ctypes_alloc_format_string_with_shape( + stginfo->format = _ctypes_alloc_format_string_with_shape( itemdict->ndim, itemdict->shape, "&", current_format); } else { - stgdict->format = _ctypes_alloc_format_string("&", current_format); + stginfo->format = _ctypes_alloc_format_string("&", current_format); } Py_DECREF(proto); - if (stgdict->format == NULL) { + if (stginfo->format == NULL) { Py_DECREF((PyObject *)result); Py_DECREF((PyObject *)stgdict); return NULL; @@ -1658,9 +1670,9 @@ PyCArrayType_new(PyTypeObject *type, PyObject *args, PyObject *kwds) } assert(iteminfo); - assert(itemdict->format); - stgdict->format = _ctypes_alloc_format_string(NULL, itemdict->format); - if (stgdict->format == NULL) + assert(iteminfo->format); + stginfo->format = _ctypes_alloc_format_string(NULL, iteminfo->format); + if (stginfo->format == NULL) goto error; stgdict->ndim = itemdict->ndim + 1; stgdict->shape = PyMem_Malloc(sizeof(Py_ssize_t) * stgdict->ndim); @@ -2234,11 +2246,11 @@ PyCSimpleType_new(PyTypeObject *type, PyObject *args, PyObject *kwds) stginfo->setfunc = fmt->setfunc; stginfo->getfunc = fmt->getfunc; #ifdef WORDS_BIGENDIAN - stgdict->format = _ctypes_alloc_format_string_for_type(proto_str[0], 1); + stginfo->format = _ctypes_alloc_format_string_for_type(proto_str[0], 1); #else - stgdict->format = _ctypes_alloc_format_string_for_type(proto_str[0], 0); + stginfo->format = _ctypes_alloc_format_string_for_type(proto_str[0], 0); #endif - if (stgdict->format == NULL) { + if (stginfo->format == NULL) { Py_DECREF(result); Py_DECREF(proto); Py_DECREF((PyObject *)stgdict); @@ -2248,8 +2260,8 @@ PyCSimpleType_new(PyTypeObject *type, PyObject *args, PyObject *kwds) stginfo->paramfunc = PyCSimpleType_paramfunc; /* if (result->tp_base != st->Simple_Type) { - stgdict->setfunc = NULL; - stgdict->getfunc = NULL; + stginfo->setfunc = NULL; + stginfo->getfunc = NULL; } */ @@ -2317,26 +2329,30 @@ PyCSimpleType_new(PyTypeObject *type, PyObject *args, PyObject *kwds) { PyObject *swapped = CreateSwappedType(type, args, kwds, proto, fmt); - StgDictObject *sw_dict; if (swapped == NULL) { Py_DECREF(result); return NULL; } - sw_dict = PyType_stgdict(swapped); + StgInfo *sw_info; + if (PyStgInfo_FromType(st, swapped, &sw_info) < 0) { + Py_DECREF(result); + return NULL; + } + assert(sw_info); #ifdef WORDS_BIGENDIAN PyObject_SetAttrString((PyObject *)result, "__ctype_le__", swapped); PyObject_SetAttrString((PyObject *)result, "__ctype_be__", (PyObject *)result); PyObject_SetAttrString(swapped, "__ctype_be__", (PyObject *)result); PyObject_SetAttrString(swapped, "__ctype_le__", swapped); /* We are creating the type for the OTHER endian */ - sw_dict->format = _ctypes_alloc_format_string("<", stgdict->format+1); + sw_info->format = _ctypes_alloc_format_string("<", stginfo->format+1); #else PyObject_SetAttrString((PyObject *)result, "__ctype_be__", swapped); PyObject_SetAttrString((PyObject *)result, "__ctype_le__", (PyObject *)result); PyObject_SetAttrString(swapped, "__ctype_le__", (PyObject *)result); PyObject_SetAttrString(swapped, "__ctype_be__", swapped); /* We are creating the type for the OTHER endian */ - sw_dict->format = _ctypes_alloc_format_string(">", stgdict->format+1); + sw_info->format = _ctypes_alloc_format_string(">", stginfo->format+1); #endif Py_DECREF(swapped); if (PyErr_Occurred()) { @@ -2680,8 +2696,8 @@ PyCFuncPtrType_new(PyTypeObject *type, PyObject *args, PyObject *kwds) know the types of the arguments (although, in practice, most argtypes would be a ctypes type). */ - stgdict->format = _ctypes_alloc_format_string(NULL, "X{}"); - if (stgdict->format == NULL) { + stginfo->format = _ctypes_alloc_format_string(NULL, "X{}"); + if (stginfo->format == NULL) { Py_DECREF(result); Py_DECREF((PyObject *)stgdict); return NULL; @@ -2900,6 +2916,14 @@ PyCData_NewGetBuffer(PyObject *myself, Py_buffer *view, int flags) { CDataObject *self = (CDataObject *)myself; StgDictObject *dict = PyObject_stgdict(myself); + + ctypes_state *st = GLOBAL_STATE(); + StgInfo *info; + if (PyStgInfo_FromObject(st, myself, &info) < 0) { + return -1; + } + assert(info); + PyObject *item_type = PyCData_item_type((PyObject*)Py_TYPE(myself)); if (item_type == NULL) { return 0; @@ -2907,7 +2931,6 @@ PyCData_NewGetBuffer(PyObject *myself, Py_buffer *view, int flags) if (view == NULL) return 0; - ctypes_state *st = GLOBAL_STATE(); StgInfo *item_info; if (PyStgInfo_FromType(st, item_type, &item_info) < 0) { return -1; @@ -2919,7 +2942,7 @@ PyCData_NewGetBuffer(PyObject *myself, Py_buffer *view, int flags) view->len = self->b_size; view->readonly = 0; /* use default format character if not set */ - view->format = dict->format ? dict->format : "B"; + view->format = info->format ? info->format : "B"; view->ndim = dict->ndim; view->shape = dict->shape; view->itemsize = item_info->size; diff --git a/Modules/_ctypes/callproc.c b/Modules/_ctypes/callproc.c index e5351e546fb1dc..44843391f86df8 100644 --- a/Modules/_ctypes/callproc.c +++ b/Modules/_ctypes/callproc.c @@ -2035,11 +2035,19 @@ buffer_info(PyObject *self, PyObject *arg) if (dict == NULL) dict = PyObject_stgdict(arg); - if (dict == NULL) { + + ctypes_state *st = GLOBAL_STATE(); + StgInfo *info; + if (PyStgInfo_FromAny(st, arg, &info) < 0) { + return NULL; + } + + if (info == NULL) { PyErr_SetString(PyExc_TypeError, "not a ctypes type or object"); return NULL; } + assert(dict); shape = PyTuple_New(dict->ndim); if (shape == NULL) return NULL; @@ -2050,7 +2058,7 @@ buffer_info(PyObject *self, PyObject *arg) Py_DECREF(shape); return NULL; } - return Py_BuildValue("siN", dict->format, dict->ndim, shape); + return Py_BuildValue("siN", info->format, dict->ndim, shape); } diff --git a/Modules/_ctypes/ctypes.h b/Modules/_ctypes/ctypes.h index 59d0269e6060f2..145adb886bef09 100644 --- a/Modules/_ctypes/ctypes.h +++ b/Modules/_ctypes/ctypes.h @@ -244,6 +244,9 @@ typedef struct { PyObject *restype; /* CDataObject or NULL */ PyObject *checker; int flags; /* calling convention and such */ + + /* pep3118 fields, pointers need PyMem_Free */ + char *format; } StgInfo; // Get a PyCTypeDataObject. These Return -1 on error, 0 if "not found", 1 on OK. @@ -286,7 +289,7 @@ typedef struct { //int flags; /* calling convention and such */ /* pep3118 fields, pointers need PyMem_Free */ - char *format; + //char *format; int ndim; Py_ssize_t *shape; /* Py_ssize_t *strides; */ /* unused in ctypes */ diff --git a/Modules/_ctypes/stgdict.c b/Modules/_ctypes/stgdict.c index ac652bdad81d87..1a287345a88243 100644 --- a/Modules/_ctypes/stgdict.c +++ b/Modules/_ctypes/stgdict.c @@ -94,7 +94,6 @@ PyCStgDict_init(StgDictObject *self, PyObject *args, PyObject *kwds) { if (PyDict_Type.tp_init((PyObject *)self, args, kwds) < 0) return -1; - self->format = NULL; self->ndim = 0; self->shape = NULL; return 0; @@ -110,7 +109,6 @@ static void PyCStgDict_dealloc(StgDictObject *self) { PyCStgDict_clear(self); - PyMem_Free(self->format); PyMem_Free(self->shape); PyDict_Type.tp_dealloc((PyObject *)self); } @@ -122,8 +120,6 @@ PyCStgDict_sizeof(StgDictObject *self, void *unused) res = _PyDict_SizeOf((PyDictObject *)self); res += sizeof(StgDictObject) - sizeof(PyDictObject); - if (self->format) - res += strlen(self->format) + 1; res += self->ndim * sizeof(Py_ssize_t); return PyLong_FromSsize_t(res); } @@ -137,8 +133,8 @@ PyCStgDict_clone(StgDictObject *dst, StgDictObject *src, PyCStgDict_clear(dst); PyMem_Free(dst_info->ffi_type_pointer.elements); - PyMem_Free(dst->format); - dst->format = NULL; + PyMem_Free(dst_info->format); + dst_info->format = NULL; PyMem_Free(dst->shape); dst->shape = NULL; dst_info->ffi_type_pointer.elements = NULL; @@ -157,13 +153,13 @@ PyCStgDict_clone(StgDictObject *dst, StgDictObject *src, Py_XINCREF(dst_info->restype); Py_XINCREF(dst_info->checker); - if (src->format) { - dst->format = PyMem_Malloc(strlen(src->format) + 1); - if (dst->format == NULL) { + if (src_info->format) { + dst_info->format = PyMem_Malloc(strlen(src_info->format) + 1); + if (dst_info->format == NULL) { PyErr_NoMemory(); return -1; } - strcpy(dst->format, src->format); + strcpy(dst_info->format, src_info->format); } if (src->shape) { dst->shape = PyMem_Malloc(sizeof(Py_ssize_t) * src->ndim); @@ -435,7 +431,6 @@ _ctypes_alloc_format_padding(const char *prefix, Py_ssize_t padding) int PyCStructUnionType_update_stgdict(PyObject *type, PyObject *fields, int isStruct) { - StgDictObject *stgdict; Py_ssize_t len, offset, size, align, i; Py_ssize_t union_size, total_align, aligned_size; Py_ssize_t field_size = 0; @@ -514,19 +509,16 @@ PyCStructUnionType_update_stgdict(PyObject *type, PyObject *fields, int isStruct return -1; } - stgdict = PyType_stgdict(type); - if (!stgdict) { - PyErr_SetString(PyExc_TypeError, - "ctypes state is not initialized"); - return -1; - } - ctypes_state *st = GLOBAL_STATE(); StgInfo *stginfo; if (PyStgInfo_FromType(st, type, &stginfo) < 0) { return -1; } - assert(stginfo); + if (!stginfo) { + PyErr_SetString(PyExc_TypeError, + "ctypes state is not initialized"); + return -1; + } /* If this structure/union is already marked final we cannot assign _fields_ anymore. */ @@ -537,9 +529,9 @@ PyCStructUnionType_update_stgdict(PyObject *type, PyObject *fields, int isStruct return -1; } - if (stgdict->format) { - PyMem_Free(stgdict->format); - stgdict->format = NULL; + if (stginfo->format) { + PyMem_Free(stginfo->format); + stginfo->format = NULL; } if (stginfo->ffi_type_pointer.elements) @@ -594,14 +586,14 @@ PyCStructUnionType_update_stgdict(PyObject *type, PyObject *fields, int isStruct ffi_ofs = 0; } - assert(stgdict->format == NULL); + assert(stginfo->format == NULL); if (isStruct) { - stgdict->format = _ctypes_alloc_format_string(NULL, "T{"); + stginfo->format = _ctypes_alloc_format_string(NULL, "T{"); } else { /* PEP3118 doesn't support union. Use 'B' for bytes. */ - stgdict->format = _ctypes_alloc_format_string(NULL, "B"); + stginfo->format = _ctypes_alloc_format_string(NULL, "B"); } - if (stgdict->format == NULL) + if (stginfo->format == NULL) return -1; for (i = 0; i < len; ++i) { @@ -676,7 +668,7 @@ PyCStructUnionType_update_stgdict(PyObject *type, PyObject *fields, int isStruct bitsize = 0; if (isStruct) { - const char *fieldfmt = dict->format ? dict->format : "B"; + const char *fieldfmt = info->format ? info->format : "B"; const char *fieldname = PyUnicode_AsUTF8(name); char *ptr; Py_ssize_t len; @@ -706,10 +698,10 @@ PyCStructUnionType_update_stgdict(PyObject *type, PyObject *fields, int isStruct padding = ((CFieldObject *)prop)->offset - last_size; if (padding > 0) { - ptr = stgdict->format; - stgdict->format = _ctypes_alloc_format_padding(ptr, padding); + ptr = stginfo->format; + stginfo->format = _ctypes_alloc_format_padding(ptr, padding); PyMem_Free(ptr); - if (stgdict->format == NULL) { + if (stginfo->format == NULL) { Py_DECREF(pair); Py_DECREF(prop); return -1; @@ -727,17 +719,17 @@ PyCStructUnionType_update_stgdict(PyObject *type, PyObject *fields, int isStruct } sprintf(buf, "%s:%s:", fieldfmt, fieldname); - ptr = stgdict->format; + ptr = stginfo->format; if (dict->shape != NULL) { - stgdict->format = _ctypes_alloc_format_string_with_shape( - dict->ndim, dict->shape, stgdict->format, buf); + stginfo->format = _ctypes_alloc_format_string_with_shape( + dict->ndim, dict->shape, stginfo->format, buf); } else { - stgdict->format = _ctypes_alloc_format_string(stgdict->format, buf); + stginfo->format = _ctypes_alloc_format_string(stginfo->format, buf); } PyMem_Free(ptr); PyMem_Free(buf); - if (stgdict->format == NULL) { + if (stginfo->format == NULL) { Py_DECREF(pair); Py_DECREF(prop); return -1; @@ -781,18 +773,18 @@ PyCStructUnionType_update_stgdict(PyObject *type, PyObject *fields, int isStruct /* Pad up to the full size of the struct */ padding = aligned_size - size; if (padding > 0) { - ptr = stgdict->format; - stgdict->format = _ctypes_alloc_format_padding(ptr, padding); + ptr = stginfo->format; + stginfo->format = _ctypes_alloc_format_padding(ptr, padding); PyMem_Free(ptr); - if (stgdict->format == NULL) { + if (stginfo->format == NULL) { return -1; } } - ptr = stgdict->format; - stgdict->format = _ctypes_alloc_format_string(stgdict->format, "}"); + ptr = stginfo->format; + stginfo->format = _ctypes_alloc_format_string(stginfo->format, "}"); PyMem_Free(ptr); - if (stgdict->format == NULL) + if (stginfo->format == NULL) return -1; } From 946f47429f7aab114df652e18f9891937ecbb5bf Mon Sep 17 00:00:00 2001 From: Petr Viktorin Date: Tue, 20 Feb 2024 22:49:51 +0100 Subject: [PATCH 19/59] Move `ndim` --- Modules/_ctypes/_ctypes.c | 13 +++++++------ Modules/_ctypes/callproc.c | 6 +++--- Modules/_ctypes/ctypes.h | 3 ++- Modules/_ctypes/stgdict.c | 8 +++----- 4 files changed, 15 insertions(+), 15 deletions(-) diff --git a/Modules/_ctypes/_ctypes.c b/Modules/_ctypes/_ctypes.c index e222ba594b2d0f..8a39359ad1c008 100644 --- a/Modules/_ctypes/_ctypes.c +++ b/Modules/_ctypes/_ctypes.c @@ -540,6 +540,7 @@ CType_Type_sizeof(PyObject *self) { if (info->ffi_type_pointer.elements) { size += (info->length + 1) * sizeof(ffi_type *); } + size += info->ndim * sizeof(Py_ssize_t); } return PyLong_FromSsize_t(size); @@ -1258,7 +1259,7 @@ PyCPointerType_new(PyTypeObject *type, PyObject *args, PyObject *kwds) if (itemdict->shape != NULL) { /* pointer to an array: the shape needs to be prefixed */ stginfo->format = _ctypes_alloc_format_string_with_shape( - itemdict->ndim, itemdict->shape, "&", current_format); + iteminfo->ndim, itemdict->shape, "&", current_format); } else { stginfo->format = _ctypes_alloc_format_string("&", current_format); } @@ -1674,16 +1675,16 @@ PyCArrayType_new(PyTypeObject *type, PyObject *args, PyObject *kwds) stginfo->format = _ctypes_alloc_format_string(NULL, iteminfo->format); if (stginfo->format == NULL) goto error; - stgdict->ndim = itemdict->ndim + 1; - stgdict->shape = PyMem_Malloc(sizeof(Py_ssize_t) * stgdict->ndim); + stginfo->ndim = iteminfo->ndim + 1; + stgdict->shape = PyMem_Malloc(sizeof(Py_ssize_t) * stginfo->ndim); if (stgdict->shape == NULL) { PyErr_NoMemory(); goto error; } stgdict->shape[0] = length; - if (stgdict->ndim > 1) { + if (stginfo->ndim > 1) { memmove(&stgdict->shape[1], itemdict->shape, - sizeof(Py_ssize_t) * (stgdict->ndim - 1)); + sizeof(Py_ssize_t) * (stginfo->ndim - 1)); } itemsize = iteminfo->size; @@ -2943,7 +2944,7 @@ PyCData_NewGetBuffer(PyObject *myself, Py_buffer *view, int flags) view->readonly = 0; /* use default format character if not set */ view->format = info->format ? info->format : "B"; - view->ndim = dict->ndim; + view->ndim = info->ndim; view->shape = dict->shape; view->itemsize = item_info->size; view->strides = NULL; diff --git a/Modules/_ctypes/callproc.c b/Modules/_ctypes/callproc.c index 44843391f86df8..5539bea0bf5749 100644 --- a/Modules/_ctypes/callproc.c +++ b/Modules/_ctypes/callproc.c @@ -2048,17 +2048,17 @@ buffer_info(PyObject *self, PyObject *arg) return NULL; } assert(dict); - shape = PyTuple_New(dict->ndim); + shape = PyTuple_New(info->ndim); if (shape == NULL) return NULL; - for (i = 0; i < (int)dict->ndim; ++i) + for (i = 0; i < (int)info->ndim; ++i) PyTuple_SET_ITEM(shape, i, PyLong_FromSsize_t(dict->shape[i])); if (PyErr_Occurred()) { Py_DECREF(shape); return NULL; } - return Py_BuildValue("siN", info->format, dict->ndim, shape); + return Py_BuildValue("siN", info->format, info->ndim, shape); } diff --git a/Modules/_ctypes/ctypes.h b/Modules/_ctypes/ctypes.h index 145adb886bef09..5cb4d7da5942b6 100644 --- a/Modules/_ctypes/ctypes.h +++ b/Modules/_ctypes/ctypes.h @@ -247,6 +247,7 @@ typedef struct { /* pep3118 fields, pointers need PyMem_Free */ char *format; + int ndim; } StgInfo; // Get a PyCTypeDataObject. These Return -1 on error, 0 if "not found", 1 on OK. @@ -290,7 +291,7 @@ typedef struct { /* pep3118 fields, pointers need PyMem_Free */ //char *format; - int ndim; + //int ndim; Py_ssize_t *shape; /* Py_ssize_t *strides; */ /* unused in ctypes */ /* Py_ssize_t *suboffsets; */ /* unused in ctypes */ diff --git a/Modules/_ctypes/stgdict.c b/Modules/_ctypes/stgdict.c index 1a287345a88243..1a843d89d0381c 100644 --- a/Modules/_ctypes/stgdict.c +++ b/Modules/_ctypes/stgdict.c @@ -94,7 +94,6 @@ PyCStgDict_init(StgDictObject *self, PyObject *args, PyObject *kwds) { if (PyDict_Type.tp_init((PyObject *)self, args, kwds) < 0) return -1; - self->ndim = 0; self->shape = NULL; return 0; } @@ -120,7 +119,6 @@ PyCStgDict_sizeof(StgDictObject *self, void *unused) res = _PyDict_SizeOf((PyDictObject *)self); res += sizeof(StgDictObject) - sizeof(PyDictObject); - res += self->ndim * sizeof(Py_ssize_t); return PyLong_FromSsize_t(res); } @@ -162,13 +160,13 @@ PyCStgDict_clone(StgDictObject *dst, StgDictObject *src, strcpy(dst_info->format, src_info->format); } if (src->shape) { - dst->shape = PyMem_Malloc(sizeof(Py_ssize_t) * src->ndim); + dst->shape = PyMem_Malloc(sizeof(Py_ssize_t) * src_info->ndim); if (dst->shape == NULL) { PyErr_NoMemory(); return -1; } memcpy(dst->shape, src->shape, - sizeof(Py_ssize_t) * src->ndim); + sizeof(Py_ssize_t) * src_info->ndim); } if (src_info->ffi_type_pointer.elements == NULL) @@ -722,7 +720,7 @@ PyCStructUnionType_update_stgdict(PyObject *type, PyObject *fields, int isStruct ptr = stginfo->format; if (dict->shape != NULL) { stginfo->format = _ctypes_alloc_format_string_with_shape( - dict->ndim, dict->shape, stginfo->format, buf); + info->ndim, dict->shape, stginfo->format, buf); } else { stginfo->format = _ctypes_alloc_format_string(stginfo->format, buf); } From 607ed23ab4e8485fc409e337cc35b1b34e77f2f9 Mon Sep 17 00:00:00 2001 From: Petr Viktorin Date: Wed, 21 Feb 2024 20:34:27 +0100 Subject: [PATCH 20/59] Move `shape` --- Modules/_ctypes/_ctypes.c | 32 +++++++++++++------------------- Modules/_ctypes/callproc.c | 8 +------- Modules/_ctypes/ctypes.h | 5 ++++- Modules/_ctypes/stgdict.c | 38 +++++++++++++++++--------------------- 4 files changed, 35 insertions(+), 48 deletions(-) diff --git a/Modules/_ctypes/_ctypes.c b/Modules/_ctypes/_ctypes.c index 8a39359ad1c008..5cc55e9ef1f218 100644 --- a/Modules/_ctypes/_ctypes.c +++ b/Modules/_ctypes/_ctypes.c @@ -514,6 +514,7 @@ CType_Type_dealloc(PyObject *self) if (info) { PyMem_Free(info->ffi_type_pointer.elements); PyMem_Free(info->format); + PyMem_Free(info->shape); } PyTypeObject *tp = Py_TYPE(self); @@ -1231,7 +1232,6 @@ PyCPointerType_new(PyTypeObject *type, PyObject *args, PyObject *kwds) return NULL; } if (proto) { - StgDictObject *itemdict; const char *current_format; if (-1 == PyCPointerType_SetProto(stginfo, proto)) { Py_DECREF(proto); @@ -1239,7 +1239,6 @@ PyCPointerType_new(PyTypeObject *type, PyObject *args, PyObject *kwds) Py_DECREF((PyObject *)stgdict); return NULL; } - itemdict = PyType_stgdict(proto); StgInfo *iteminfo; if (PyStgInfo_FromType(st, proto, &iteminfo) < 0) { Py_DECREF(proto); @@ -1249,17 +1248,16 @@ PyCPointerType_new(PyTypeObject *type, PyObject *args, PyObject *kwds) } /* PyCPointerType_SetProto has verified proto has a stgdict. */ assert(iteminfo); - assert(itemdict); /* If iteminfo->format is NULL, then this is a pointer to an incomplete type. We create a generic format string 'pointer to bytes' in this case. XXX Better would be to fix the format string later... */ current_format = iteminfo->format ? iteminfo->format : "B"; - if (itemdict->shape != NULL) { + if (iteminfo->shape != NULL) { /* pointer to an array: the shape needs to be prefixed */ stginfo->format = _ctypes_alloc_format_string_with_shape( - iteminfo->ndim, itemdict->shape, "&", current_format); + iteminfo->ndim, iteminfo->shape, "&", current_format); } else { stginfo->format = _ctypes_alloc_format_string("&", current_format); } @@ -1590,7 +1588,6 @@ PyCArrayType_new(PyTypeObject *type, PyObject *args, PyObject *kwds) { PyTypeObject *result; StgDictObject *stgdict; - StgDictObject *itemdict; PyObject *length_attr, *type_attr; Py_ssize_t length; Py_ssize_t itemsize, itemalign; @@ -1658,32 +1655,30 @@ PyCArrayType_new(PyTypeObject *type, PyObject *args, PyObject *kwds) if (!stginfo) { goto error; } - itemdict = PyType_stgdict(type_attr); - if (!itemdict) { - PyErr_SetString(PyExc_TypeError, - "_type_ must have storage info"); - goto error; - } StgInfo *iteminfo; if (PyStgInfo_FromType(st, type_attr, &iteminfo) < 0) { goto error; } - assert(iteminfo); + if (!iteminfo) { + PyErr_SetString(PyExc_TypeError, + "_type_ must have storage info"); + goto error; + } assert(iteminfo->format); stginfo->format = _ctypes_alloc_format_string(NULL, iteminfo->format); if (stginfo->format == NULL) goto error; stginfo->ndim = iteminfo->ndim + 1; - stgdict->shape = PyMem_Malloc(sizeof(Py_ssize_t) * stginfo->ndim); - if (stgdict->shape == NULL) { + stginfo->shape = PyMem_Malloc(sizeof(Py_ssize_t) * stginfo->ndim); + if (stginfo->shape == NULL) { PyErr_NoMemory(); goto error; } - stgdict->shape[0] = length; + stginfo->shape[0] = length; if (stginfo->ndim > 1) { - memmove(&stgdict->shape[1], itemdict->shape, + memmove(&stginfo->shape[1], iteminfo->shape, sizeof(Py_ssize_t) * (stginfo->ndim - 1)); } @@ -2916,7 +2911,6 @@ static int PyCData_NewGetBuffer(PyObject *myself, Py_buffer *view, int flags) { CDataObject *self = (CDataObject *)myself; - StgDictObject *dict = PyObject_stgdict(myself); ctypes_state *st = GLOBAL_STATE(); StgInfo *info; @@ -2945,7 +2939,7 @@ PyCData_NewGetBuffer(PyObject *myself, Py_buffer *view, int flags) /* use default format character if not set */ view->format = info->format ? info->format : "B"; view->ndim = info->ndim; - view->shape = dict->shape; + view->shape = info->shape; view->itemsize = item_info->size; view->strides = NULL; view->suboffsets = NULL; diff --git a/Modules/_ctypes/callproc.c b/Modules/_ctypes/callproc.c index 5539bea0bf5749..1f133f928bcddf 100644 --- a/Modules/_ctypes/callproc.c +++ b/Modules/_ctypes/callproc.c @@ -2029,30 +2029,24 @@ create_pointer_inst(PyObject *module, PyObject *arg) static PyObject * buffer_info(PyObject *self, PyObject *arg) { - StgDictObject *dict = PyType_stgdict(arg); PyObject *shape; Py_ssize_t i; - if (dict == NULL) - dict = PyObject_stgdict(arg); - ctypes_state *st = GLOBAL_STATE(); StgInfo *info; if (PyStgInfo_FromAny(st, arg, &info) < 0) { return NULL; } - if (info == NULL) { PyErr_SetString(PyExc_TypeError, "not a ctypes type or object"); return NULL; } - assert(dict); shape = PyTuple_New(info->ndim); if (shape == NULL) return NULL; for (i = 0; i < (int)info->ndim; ++i) - PyTuple_SET_ITEM(shape, i, PyLong_FromSsize_t(dict->shape[i])); + PyTuple_SET_ITEM(shape, i, PyLong_FromSsize_t(info->shape[i])); if (PyErr_Occurred()) { Py_DECREF(shape); diff --git a/Modules/_ctypes/ctypes.h b/Modules/_ctypes/ctypes.h index 5cb4d7da5942b6..e948bd717feea7 100644 --- a/Modules/_ctypes/ctypes.h +++ b/Modules/_ctypes/ctypes.h @@ -248,6 +248,9 @@ typedef struct { /* pep3118 fields, pointers need PyMem_Free */ char *format; int ndim; + Py_ssize_t *shape; +/* Py_ssize_t *strides; */ /* unused in ctypes */ +/* Py_ssize_t *suboffsets; */ /* unused in ctypes */ } StgInfo; // Get a PyCTypeDataObject. These Return -1 on error, 0 if "not found", 1 on OK. @@ -292,7 +295,7 @@ typedef struct { /* pep3118 fields, pointers need PyMem_Free */ //char *format; //int ndim; - Py_ssize_t *shape; + //Py_ssize_t *shape; /* Py_ssize_t *strides; */ /* unused in ctypes */ /* Py_ssize_t *suboffsets; */ /* unused in ctypes */ diff --git a/Modules/_ctypes/stgdict.c b/Modules/_ctypes/stgdict.c index 1a843d89d0381c..e74b2c3fd7cf44 100644 --- a/Modules/_ctypes/stgdict.c +++ b/Modules/_ctypes/stgdict.c @@ -92,9 +92,9 @@ PyStgInfo_Init(ctypes_state *state, PyTypeObject *type) static int PyCStgDict_init(StgDictObject *self, PyObject *args, PyObject *kwds) { - if (PyDict_Type.tp_init((PyObject *)self, args, kwds) < 0) + if (PyDict_Type.tp_init((PyObject *)self, args, kwds) < 0) { return -1; - self->shape = NULL; + } return 0; } @@ -108,7 +108,6 @@ static void PyCStgDict_dealloc(StgDictObject *self) { PyCStgDict_clear(self); - PyMem_Free(self->shape); PyDict_Type.tp_dealloc((PyObject *)self); } @@ -133,8 +132,8 @@ PyCStgDict_clone(StgDictObject *dst, StgDictObject *src, PyMem_Free(dst_info->ffi_type_pointer.elements); PyMem_Free(dst_info->format); dst_info->format = NULL; - PyMem_Free(dst->shape); - dst->shape = NULL; + PyMem_Free(dst_info->shape); + dst_info->shape = NULL; dst_info->ffi_type_pointer.elements = NULL; d = (char *)dst; @@ -159,13 +158,13 @@ PyCStgDict_clone(StgDictObject *dst, StgDictObject *src, } strcpy(dst_info->format, src_info->format); } - if (src->shape) { - dst->shape = PyMem_Malloc(sizeof(Py_ssize_t) * src_info->ndim); - if (dst->shape == NULL) { + if (src_info->shape) { + dst_info->shape = PyMem_Malloc(sizeof(Py_ssize_t) * src_info->ndim); + if (dst_info->shape == NULL) { PyErr_NoMemory(); return -1; } - memcpy(dst->shape, src->shape, + memcpy(dst_info->shape, src_info->shape, sizeof(Py_ssize_t) * src_info->ndim); } @@ -598,7 +597,6 @@ PyCStructUnionType_update_stgdict(PyObject *type, PyObject *fields, int isStruct PyObject *name = NULL, *desc = NULL; PyObject *pair = PySequence_GetItem(fields, i); PyObject *prop; - StgDictObject *dict; int bitsize = 0; if (!pair || !PyArg_ParseTuple(pair, "UO|i", &name, &desc, &bitsize)) { @@ -610,21 +608,19 @@ PyCStructUnionType_update_stgdict(PyObject *type, PyObject *fields, int isStruct if (PyCArrayTypeObject_Check(st, desc)) { arrays_seen = 1; } - dict = PyType_stgdict(desc); - if (dict == NULL) { - Py_DECREF(pair); - PyErr_Format(PyExc_TypeError, - "second item in _fields_ tuple (index %zd) must be a C type", - i); - return -1; - } StgInfo *info; if (PyStgInfo_FromType(st, desc, &info) < 0) { Py_DECREF(pair); return -1; } - assert(info); + if (info == NULL) { + Py_DECREF(pair); + PyErr_Format(PyExc_TypeError, + "second item in _fields_ tuple (index %zd) must be a C type", + i); + return -1; + } stginfo->ffi_type_pointer.elements[ffi_ofs + i] = &info->ffi_type_pointer; if (info->flags & (TYPEFLAG_ISPOINTER | TYPEFLAG_HASPOINTER)) @@ -718,9 +714,9 @@ PyCStructUnionType_update_stgdict(PyObject *type, PyObject *fields, int isStruct sprintf(buf, "%s:%s:", fieldfmt, fieldname); ptr = stginfo->format; - if (dict->shape != NULL) { + if (info->shape != NULL) { stginfo->format = _ctypes_alloc_format_string_with_shape( - info->ndim, dict->shape, stginfo->format, buf); + info->ndim, info->shape, stginfo->format, buf); } else { stginfo->format = _ctypes_alloc_format_string(stginfo->format, buf); } From cb126b75f676d7e48cbeb4bf40b97b6392687271 Mon Sep 17 00:00:00 2001 From: Petr Viktorin Date: Wed, 21 Feb 2024 21:06:15 +0100 Subject: [PATCH 21/59] Replace more uses of StgDict --- Modules/_ctypes/_ctypes.c | 82 +++++++++++++++++++------------------- Modules/_ctypes/callproc.c | 17 +++----- Modules/_ctypes/cfield.c | 15 +++---- 3 files changed, 54 insertions(+), 60 deletions(-) diff --git a/Modules/_ctypes/_ctypes.c b/Modules/_ctypes/_ctypes.c index 5cc55e9ef1f218..f2d55450361b8e 100644 --- a/Modules/_ctypes/_ctypes.c +++ b/Modules/_ctypes/_ctypes.c @@ -712,7 +712,9 @@ StructUnionType_new(PyTypeObject *type, PyObject *args, PyObject *kwds, int isSt Py_DECREF(result); return NULL; } - assert(baseinfo); + if (baseinfo == NULL) { + return (PyObject *)result; + } /* copy base dict */ if (-1 == PyCStgDict_clone(dict, basedict, info, baseinfo)) { @@ -770,18 +772,15 @@ CDataType_from_buffer(PyObject *type, PyObject *args) Py_buffer *buffer; Py_ssize_t offset = 0; - StgDictObject *dict = PyType_stgdict(type); - if (!dict) { - PyErr_SetString(PyExc_TypeError, "abstract class"); - return NULL; - } - ctypes_state *st = GLOBAL_STATE(); StgInfo *info; if (PyStgInfo_FromType(st, type, &info) < 0) { return NULL; } - assert(info); + if (!info) { + PyErr_SetString(PyExc_TypeError, "abstract class"); + return NULL; + } if (!PyArg_ParseTuple(args, "O|n:from_buffer", &obj, &offset)) return NULL; @@ -854,18 +853,16 @@ CDataType_from_buffer_copy(PyObject *type, PyObject *args) Py_buffer buffer; Py_ssize_t offset = 0; PyObject *result; - StgDictObject *dict = PyType_stgdict(type); - if (!dict) { - PyErr_SetString(PyExc_TypeError, "abstract class"); - return NULL; - } ctypes_state *st = GLOBAL_STATE(); StgInfo *info; if (PyStgInfo_FromType(st, type, &info) < 0) { return NULL; } - assert(info); + if (!info) { + PyErr_SetString(PyExc_TypeError, "abstract class"); + return NULL; + } if (!PyArg_ParseTuple(args, "y*|n:from_buffer_copy", &buffer, &offset)) return NULL; @@ -1150,12 +1147,17 @@ size property/method, and the sequence protocol. static int PyCPointerType_SetProto(StgInfo *stginfo, PyObject *proto) { + ctypes_state *st = GLOBAL_STATE(); if (!proto || !PyType_Check(proto)) { PyErr_SetString(PyExc_TypeError, "_type_ must be a type"); return -1; } - if (!PyType_stgdict(proto)) { + StgInfo *info; + if (PyStgInfo_FromType(st, proto, &info) < 0) { + return -1; + } + if (!info) { PyErr_SetString(PyExc_TypeError, "_type_ must have storage info"); return -1; @@ -2611,7 +2613,12 @@ make_funcptrtype_dict(StgDictObject *stgdict, StgInfo *stginfo) return -1; } if (ob) { - if (ob != Py_None && !PyType_stgdict(ob) && !PyCallable_Check(ob)) { + StgInfo *info; + ctypes_state *st = GLOBAL_STATE(); + if (PyStgInfo_FromType(st, ob, &info) < 0) { + return -1; + } + if (ob != Py_None && !info && !PyCallable_Check(ob)) { PyErr_SetString(PyExc_TypeError, "_restype_ must be a type, a callable, or None"); Py_DECREF(ob); @@ -3084,7 +3091,7 @@ static _HackyHeapType PyCData_Type = { }; static int -PyCData_MallocBuffer(CDataObject *obj, StgDictObject *dict, StgInfo *info) +PyCData_MallocBuffer(CDataObject *obj, StgInfo *info) { if ((size_t)info->size <= sizeof(obj->b_value)) { /* No need to call malloc, can use the default buffer */ @@ -3116,22 +3123,19 @@ PyObject * PyCData_FromBaseObj(PyObject *type, PyObject *base, Py_ssize_t index, char *adr) { CDataObject *cmem; - StgDictObject *dict; assert(PyType_Check(type)); - dict = PyType_stgdict(type); - if (!dict) { - PyErr_SetString(PyExc_TypeError, - "abstract class"); - return NULL; - } ctypes_state *st = GLOBAL_STATE(); StgInfo *info; if (PyStgInfo_FromType(st, type, &info) < 0) { return NULL; } - assert(info); + if (!info) { + PyErr_SetString(PyExc_TypeError, + "abstract class"); + return NULL; + } info->flags |= DICTFLAG_FINAL; cmem = (CDataObject *)((PyTypeObject *)type)->tp_alloc((PyTypeObject *)type, 0); @@ -3148,7 +3152,7 @@ PyCData_FromBaseObj(PyObject *type, PyObject *base, Py_ssize_t index, char *adr) cmem->b_base = (CDataObject *)Py_NewRef(base); cmem->b_index = index; } else { /* copy contents of adr */ - if (-1 == PyCData_MallocBuffer(cmem, dict, info)) { + if (-1 == PyCData_MallocBuffer(cmem, info)) { Py_DECREF(cmem); return NULL; } @@ -3379,21 +3383,17 @@ static PyObject * GenericPyCData_new(PyTypeObject *type, PyObject *args, PyObject *kwds) { CDataObject *obj; - StgDictObject *dict; - - dict = PyType_stgdict((PyObject *)type); - if (!dict) { - PyErr_SetString(PyExc_TypeError, - "abstract class"); - return NULL; - } ctypes_state *st = GLOBAL_STATE(); StgInfo *info; if (PyStgInfo_FromType(st, (PyObject *)type, &info) < 0) { return NULL; } - assert(info); + if (!info) { + PyErr_SetString(PyExc_TypeError, + "abstract class"); + return NULL; + } info->flags |= DICTFLAG_FINAL; @@ -3406,7 +3406,7 @@ GenericPyCData_new(PyTypeObject *type, PyObject *args, PyObject *kwds) obj->b_objects = NULL; obj->b_length = info->length; - if (-1 == PyCData_MallocBuffer(obj, dict, info)) { + if (-1 == PyCData_MallocBuffer(obj, info)) { Py_DECREF(obj); return NULL; } @@ -3450,7 +3450,12 @@ PyCFuncPtr_set_restype(PyCFuncPtrObject *self, PyObject *ob, void *Py_UNUSED(ign Py_XDECREF(oldchecker); return 0; } - if (ob != Py_None && !PyType_stgdict(ob) && !PyCallable_Check(ob)) { + ctypes_state *st = GLOBAL_STATE(); + StgInfo *info; + if (PyStgInfo_FromType(st, ob, &info) < 0) { + return -1; + } + if (ob != Py_None && !info && !PyCallable_Check(ob)) { PyErr_SetString(PyExc_TypeError, "restype must be a type, a callable, or None"); return -1; @@ -3623,11 +3628,9 @@ static int _validate_paramflags(PyTypeObject *type, PyObject *paramflags) { Py_ssize_t i, len; - StgDictObject *dict; PyObject *argtypes; ctypes_state *st = GLOBAL_STATE(); - dict = PyType_stgdict((PyObject *)type); StgInfo *info; if (PyStgInfo_FromType(st, (PyObject *)type, &info) < 0) { return -1; @@ -3637,7 +3640,6 @@ _validate_paramflags(PyTypeObject *type, PyObject *paramflags) "abstract class"); return 0; } - assert(dict); argtypes = info->argtypes; if (paramflags == NULL || info->argtypes == NULL) diff --git a/Modules/_ctypes/callproc.c b/Modules/_ctypes/callproc.c index 1f133f928bcddf..104aad7264c904 100644 --- a/Modules/_ctypes/callproc.c +++ b/Modules/_ctypes/callproc.c @@ -664,17 +664,15 @@ struct argument { */ static int ConvParam(PyObject *obj, Py_ssize_t index, struct argument *pa) { - StgDictObject *dict; pa->keep = NULL; /* so we cannot forget it later */ ctypes_state *st = GLOBAL_STATE(); - dict = PyObject_stgdict(obj); StgInfo *info; int result = PyStgInfo_FromObject(st, obj, &info); if (result < 0) { return -1; } - if (dict) { + if (info) { assert(info); PyCArgObject *carg; assert(info->paramfunc); @@ -1842,7 +1840,6 @@ static PyObject * resize(PyObject *self, PyObject *args) { CDataObject *obj; - StgDictObject *dict; Py_ssize_t size; if (!PyArg_ParseTuple(args, @@ -1850,19 +1847,17 @@ resize(PyObject *self, PyObject *args) &obj, &size)) return NULL; - dict = PyObject_stgdict((PyObject *)obj); - if (dict == NULL) { - PyErr_SetString(PyExc_TypeError, - "expected ctypes instance"); - return NULL; - } ctypes_state *st = GLOBAL_STATE(); StgInfo *info; int result = PyStgInfo_FromObject(st, (PyObject *)obj, &info); if (result < 0) { return NULL; } - assert(info); + if (info == NULL) { + PyErr_SetString(PyExc_TypeError, + "expected ctypes instance"); + return NULL; + } if (size < info->size) { PyErr_Format(PyExc_ValueError, "minimum size is %zd", diff --git a/Modules/_ctypes/cfield.c b/Modules/_ctypes/cfield.c index f2fa9d971d62f7..16b66382bfe33f 100644 --- a/Modules/_ctypes/cfield.c +++ b/Modules/_ctypes/cfield.c @@ -54,7 +54,6 @@ PyCField_FromDesc(PyObject *desc, Py_ssize_t index, Py_ssize_t size, align; SETFUNC setfunc = NULL; GETFUNC getfunc = NULL; - StgDictObject *dict; int fieldtype; #define NO_BITFIELD 0 #define NEW_BITFIELD 1 @@ -66,20 +65,18 @@ PyCField_FromDesc(PyObject *desc, Py_ssize_t index, self = (CFieldObject *)tp->tp_alloc(tp, 0); if (self == NULL) return NULL; - dict = PyType_stgdict(desc); - if (!dict) { - PyErr_SetString(PyExc_TypeError, - "has no _stginfo_"); - Py_DECREF(self); - return NULL; - } StgInfo *info; if (PyStgInfo_FromType(st, desc, &info) < 0) { Py_DECREF(self); return NULL; } - assert(info); + if (!info) { + PyErr_SetString(PyExc_TypeError, + "has no _stginfo_"); + Py_DECREF(self); + return NULL; + } if (bitsize /* this is a bitfield request */ && *pfield_size /* we have a bitfield open */ From fd04c91a2cdbff56ef3a66863c488db6b201c8ef Mon Sep 17 00:00:00 2001 From: Petr Viktorin Date: Wed, 21 Feb 2024 21:13:22 +0100 Subject: [PATCH 22/59] Don't pass dicts to PyCStgDict_clone --- Modules/_ctypes/_ctypes.c | 4 ++-- Modules/_ctypes/ctypes.h | 3 +-- Modules/_ctypes/stgdict.c | 11 +---------- 3 files changed, 4 insertions(+), 14 deletions(-) diff --git a/Modules/_ctypes/_ctypes.c b/Modules/_ctypes/_ctypes.c index f2d55450361b8e..ac4586942f3783 100644 --- a/Modules/_ctypes/_ctypes.c +++ b/Modules/_ctypes/_ctypes.c @@ -716,8 +716,8 @@ StructUnionType_new(PyTypeObject *type, PyObject *args, PyObject *kwds, int isSt return (PyObject *)result; } - /* copy base dict */ - if (-1 == PyCStgDict_clone(dict, basedict, info, baseinfo)) { + /* copy base info */ + if (-1 == PyCStgDict_clone(info, baseinfo)) { Py_DECREF(result); return NULL; } diff --git a/Modules/_ctypes/ctypes.h b/Modules/_ctypes/ctypes.h index e948bd717feea7..7a066efc0a4308 100644 --- a/Modules/_ctypes/ctypes.h +++ b/Modules/_ctypes/ctypes.h @@ -341,14 +341,13 @@ typedef struct { *****************************************************************/ +extern int PyCStgDict_clone(StgInfo *dst_info, StgInfo *src_info); /* May return NULL, but does not set an exception! */ extern StgDictObject *PyType_stgdict(PyObject *obj); /* May return NULL, but does not set an exception! */ extern StgDictObject *PyObject_stgdict(PyObject *self); -extern int PyCStgDict_clone(StgDictObject *src, StgDictObject *dst, - StgInfo *dst_info, StgInfo *src_info); typedef int(* PPROC)(void); diff --git a/Modules/_ctypes/stgdict.c b/Modules/_ctypes/stgdict.c index e74b2c3fd7cf44..34926981c9c533 100644 --- a/Modules/_ctypes/stgdict.c +++ b/Modules/_ctypes/stgdict.c @@ -122,13 +122,10 @@ PyCStgDict_sizeof(StgDictObject *self, void *unused) } int -PyCStgDict_clone(StgDictObject *dst, StgDictObject *src, - StgInfo *dst_info, StgInfo *src_info) +PyCStgDict_clone(StgInfo *dst_info, StgInfo *src_info) { - char *d, *s; Py_ssize_t size; - PyCStgDict_clear(dst); PyMem_Free(dst_info->ffi_type_pointer.elements); PyMem_Free(dst_info->format); dst_info->format = NULL; @@ -136,12 +133,6 @@ PyCStgDict_clone(StgDictObject *dst, StgDictObject *src, dst_info->shape = NULL; dst_info->ffi_type_pointer.elements = NULL; - d = (char *)dst; - s = (char *)src; - memcpy(d + sizeof(PyDictObject), - s + sizeof(PyDictObject), - sizeof(StgDictObject) - sizeof(PyDictObject)); - memcpy(dst_info, src_info, sizeof(StgInfo)); Py_XINCREF(dst_info->proto); From 8dce549367d549c2d104eb859bfb5b04a9fd0a54 Mon Sep 17 00:00:00 2001 From: Petr Viktorin Date: Wed, 21 Feb 2024 21:15:54 +0100 Subject: [PATCH 23/59] Don't set StgDict as type dict; use name `attrdict` for the type __dict__ --- Modules/_ctypes/_ctypes.c | 155 +++++++++----------------------------- 1 file changed, 36 insertions(+), 119 deletions(-) diff --git a/Modules/_ctypes/_ctypes.c b/Modules/_ctypes/_ctypes.c index ac4586942f3783..54af400dbf5f73 100644 --- a/Modules/_ctypes/_ctypes.c +++ b/Modules/_ctypes/_ctypes.c @@ -637,7 +637,6 @@ StructUnionType_new(PyTypeObject *type, PyObject *args, PyObject *kwds, int isSt { PyTypeObject *result; PyObject *fields; - StgDictObject *dict; /* create the new instance (which is a class, since we are a metatype!) */ @@ -645,6 +644,11 @@ StructUnionType_new(PyTypeObject *type, PyObject *args, PyObject *kwds, int isSt if (!result) return NULL; + PyObject *attrdict = PyType_GetDict(result); + if (!attrdict) { + return NULL; + } + /* keep this for bw compatibility */ int r = PyDict_Contains(result->tp_dict, &_Py_ID(_abstract_)); if (r > 0) { @@ -656,11 +660,6 @@ StructUnionType_new(PyTypeObject *type, PyObject *args, PyObject *kwds, int isSt } ctypes_state *st = GLOBAL_STATE(); - dict = (StgDictObject *)_PyObject_CallNoArgs((PyObject *)st->PyCStgDict_Type); - if (!dict) { - Py_DECREF(result); - return NULL; - } StgInfo *info = PyStgInfo_Init(st, result); if (!info) { Py_DECREF(result); @@ -670,14 +669,6 @@ StructUnionType_new(PyTypeObject *type, PyObject *args, PyObject *kwds, int isSt info->flags |= TYPEFLAG_HASUNION; } - /* replace the class dict by our updated stgdict, which holds info - about storage requirements of the instances */ - if (-1 == PyDict_Update((PyObject *)dict, result->tp_dict)) { - Py_DECREF(result); - Py_DECREF((PyObject *)dict); - return NULL; - } - Py_SETREF(result->tp_dict, (PyObject *)dict); info->format = _ctypes_alloc_format_string(NULL, "B"); if (info->format == NULL) { Py_DECREF(result); @@ -686,7 +677,7 @@ StructUnionType_new(PyTypeObject *type, PyObject *args, PyObject *kwds, int isSt info->paramfunc = StructUnionType_paramfunc; - if (PyDict_GetItemRef((PyObject *)dict, &_Py_ID(_fields_), &fields) < 0) { + if (PyDict_GetItemRef((PyObject *)attrdict, &_Py_ID(_fields_), &fields) < 0) { Py_DECREF(result); return NULL; } @@ -1187,7 +1178,6 @@ static PyObject * PyCPointerType_new(PyTypeObject *type, PyObject *args, PyObject *kwds) { PyTypeObject *result; - StgDictObject *stgdict; PyObject *proto; PyObject *typedict; @@ -1205,20 +1195,13 @@ PyCPointerType_new(PyTypeObject *type, PyObject *args, PyObject *kwds) } /* - stgdict items size, align, length contain info about pointers itself, - stgdict->proto has info about the pointed to type! + stginfo items size, align, length contain info about pointers itself, + stginfo->proto has info about the pointed to type! */ ctypes_state *st = GLOBAL_STATE(); - stgdict = (StgDictObject *)_PyObject_CallNoArgs( - (PyObject *)st->PyCStgDict_Type); - if (!stgdict) { - Py_DECREF((PyObject *)result); - return NULL; - } StgInfo *stginfo = PyStgInfo_Init(st, result); if (!stginfo) { Py_DECREF((PyObject *)result); - Py_DECREF((PyObject *)stgdict); return NULL; } stginfo->size = sizeof(void *); @@ -1230,7 +1213,6 @@ PyCPointerType_new(PyTypeObject *type, PyObject *args, PyObject *kwds) if (PyDict_GetItemRef(typedict, &_Py_ID(_type_), &proto) < 0) { Py_DECREF((PyObject *)result); - Py_DECREF((PyObject *)stgdict); return NULL; } if (proto) { @@ -1238,17 +1220,15 @@ PyCPointerType_new(PyTypeObject *type, PyObject *args, PyObject *kwds) if (-1 == PyCPointerType_SetProto(stginfo, proto)) { Py_DECREF(proto); Py_DECREF((PyObject *)result); - Py_DECREF((PyObject *)stgdict); return NULL; } StgInfo *iteminfo; if (PyStgInfo_FromType(st, proto, &iteminfo) < 0) { Py_DECREF(proto); Py_DECREF((PyObject *)result); - Py_DECREF((PyObject *)stgdict); return NULL; } - /* PyCPointerType_SetProto has verified proto has a stgdict. */ + /* PyCPointerType_SetProto has verified proto has a stginfo. */ assert(iteminfo); /* If iteminfo->format is NULL, then this is a pointer to an incomplete type. We create a generic format string @@ -1266,19 +1246,10 @@ PyCPointerType_new(PyTypeObject *type, PyObject *args, PyObject *kwds) Py_DECREF(proto); if (stginfo->format == NULL) { Py_DECREF((PyObject *)result); - Py_DECREF((PyObject *)stgdict); return NULL; } } - /* replace the class dict by our updated spam dict */ - if (-1 == PyDict_Update((PyObject *)stgdict, result->tp_dict)) { - Py_DECREF(result); - Py_DECREF((PyObject *)stgdict); - return NULL; - } - Py_SETREF(result->tp_dict, (PyObject *)stgdict); - return (PyObject *)result; } @@ -1286,13 +1257,8 @@ PyCPointerType_new(PyTypeObject *type, PyObject *args, PyObject *kwds) static PyObject * PyCPointerType_set_type(PyTypeObject *self, PyObject *type) { - StgDictObject *dict; - - - dict = PyType_stgdict((PyObject *)self); - if (!dict) { - PyErr_SetString(PyExc_TypeError, - "abstract class"); + PyObject *attrdict = PyType_GetDict(self); + if (!attrdict) { return NULL; } ctypes_state *st = GLOBAL_STATE(); @@ -1300,12 +1266,16 @@ PyCPointerType_set_type(PyTypeObject *self, PyObject *type) if (PyStgInfo_FromType(st, (PyObject *)self, &info) < 0) { return NULL; } - assert(info); + if (!info) { + PyErr_SetString(PyExc_TypeError, + "abstract class"); + return NULL; + } if (-1 == PyCPointerType_SetProto(info, type)) return NULL; - if (-1 == PyDict_SetItem((PyObject *)dict, &_Py_ID(_type_), type)) + if (-1 == PyDict_SetItem(attrdict, &_Py_ID(_type_), type)) return NULL; Py_RETURN_NONE; @@ -1589,7 +1559,6 @@ static PyObject * PyCArrayType_new(PyTypeObject *type, PyObject *args, PyObject *kwds) { PyTypeObject *result; - StgDictObject *stgdict; PyObject *length_attr, *type_attr; Py_ssize_t length; Py_ssize_t itemsize, itemalign; @@ -1602,7 +1571,6 @@ PyCArrayType_new(PyTypeObject *type, PyObject *args, PyObject *kwds) /* Initialize these variables to NULL so that we can simplify error handling by using Py_XDECREF. */ - stgdict = NULL; type_attr = NULL; if (PyObject_GetOptionalAttr((PyObject *)result, &_Py_ID(_length_), &length_attr) < 0) { @@ -1648,11 +1616,6 @@ PyCArrayType_new(PyTypeObject *type, PyObject *args, PyObject *kwds) } ctypes_state *st = GLOBAL_STATE(); - stgdict = (StgDictObject *)_PyObject_CallNoArgs( - (PyObject *)st->PyCStgDict_Type); - if (!stgdict) { - goto error; - } StgInfo *stginfo = PyStgInfo_Init(st, result); if (!stginfo) { goto error; @@ -1707,12 +1670,6 @@ PyCArrayType_new(PyTypeObject *type, PyObject *args, PyObject *kwds) /* Arrays are passed as pointers to function calls. */ stginfo->ffi_type_pointer = ffi_type_pointer; - /* replace the class dict by our updated spam dict */ - if (-1 == PyDict_Update((PyObject *)stgdict, result->tp_dict)) - goto error; - Py_SETREF(result->tp_dict, (PyObject *)stgdict); /* steal the reference */ - stgdict = NULL; - /* Special case for character arrays. A permanent annoyance: char arrays are also strings! */ @@ -1727,7 +1684,6 @@ PyCArrayType_new(PyTypeObject *type, PyObject *args, PyObject *kwds) return (PyObject *)result; error: - Py_XDECREF((PyObject*)stgdict); Py_XDECREF(type_attr); Py_DECREF(result); return NULL; @@ -2062,7 +2018,6 @@ static PyObject *CreateSwappedType(PyTypeObject *type, PyObject *args, PyObject PyObject *proto, struct fielddesc *fmt) { PyTypeObject *result; - StgDictObject *stgdict; PyObject *name = PyTuple_GET_ITEM(args, 0); PyObject *newname; PyObject *swapped_args; @@ -2105,17 +2060,10 @@ static PyObject *CreateSwappedType(PyTypeObject *type, PyObject *args, PyObject return NULL; ctypes_state *st = GLOBAL_STATE(); - stgdict = (StgDictObject *)_PyObject_CallNoArgs( - (PyObject *)st->PyCStgDict_Type); - if (!stgdict) { - Py_DECREF(result); - return NULL; - } StgInfo *stginfo = PyStgInfo_Init(st, result); if (!stginfo) { Py_DECREF(result); - Py_DECREF((PyObject *)stgdict); return NULL; } @@ -2128,14 +2076,6 @@ static PyObject *CreateSwappedType(PyTypeObject *type, PyObject *args, PyObject stginfo->proto = Py_NewRef(proto); - /* replace the class dict by our updated spam dict */ - if (-1 == PyDict_Update((PyObject *)stgdict, result->tp_dict)) { - Py_DECREF(result); - Py_DECREF((PyObject *)stgdict); - return NULL; - } - Py_SETREF(result->tp_dict, (PyObject *)stgdict); - return (PyObject *)result; } @@ -2173,7 +2113,6 @@ static PyObject * PyCSimpleType_new(PyTypeObject *type, PyObject *args, PyObject *kwds) { PyTypeObject *result; - StgDictObject *stgdict; PyObject *proto; const char *proto_str; Py_ssize_t proto_len; @@ -2227,11 +2166,6 @@ PyCSimpleType_new(PyTypeObject *type, PyObject *args, PyObject *kwds) } ctypes_state *st = GLOBAL_STATE(); - stgdict = (StgDictObject *)_PyObject_CallNoArgs( - (PyObject *)st->PyCStgDict_Type); - if (!stgdict) { - goto error; - } StgInfo *stginfo = PyStgInfo_Init(st, result); if (!stginfo) { goto error; @@ -2251,7 +2185,6 @@ PyCSimpleType_new(PyTypeObject *type, PyObject *args, PyObject *kwds) if (stginfo->format == NULL) { Py_DECREF(result); Py_DECREF(proto); - Py_DECREF((PyObject *)stgdict); return NULL; } @@ -2266,14 +2199,6 @@ PyCSimpleType_new(PyTypeObject *type, PyObject *args, PyObject *kwds) /* This consumes the refcount on proto which we have */ stginfo->proto = proto; - /* replace the class dict by our updated spam dict */ - if (-1 == PyDict_Update((PyObject *)stgdict, result->tp_dict)) { - Py_DECREF(result); - Py_DECREF((PyObject *)stgdict); - return NULL; - } - Py_SETREF(result->tp_dict, (PyObject *)stgdict); - /* Install from_param class methods in ctypes base classes. Overrides the PyCSimpleType_from_param generic method. */ @@ -2571,7 +2496,7 @@ converters_from_argtypes(PyObject *ob) } static int -make_funcptrtype_dict(StgDictObject *stgdict, StgInfo *stginfo) +make_funcptrtype_dict(PyObject *attrdict, StgInfo *stginfo) { PyObject *ob; PyObject *converters = NULL; @@ -2583,7 +2508,7 @@ make_funcptrtype_dict(StgDictObject *stgdict, StgInfo *stginfo) stginfo->getfunc = NULL; stginfo->ffi_type_pointer = ffi_type_pointer; - if (PyDict_GetItemRef((PyObject *)stgdict, &_Py_ID(_flags_), &ob) < 0) { + if (PyDict_GetItemRef((PyObject *)attrdict, &_Py_ID(_flags_), &ob) < 0) { return -1; } if (!ob || !PyLong_Check(ob)) { @@ -2596,7 +2521,7 @@ make_funcptrtype_dict(StgDictObject *stgdict, StgInfo *stginfo) Py_DECREF(ob); /* _argtypes_ is optional... */ - if (PyDict_GetItemRef((PyObject *)stgdict, &_Py_ID(_argtypes_), &ob) < 0) { + if (PyDict_GetItemRef((PyObject *)attrdict, &_Py_ID(_argtypes_), &ob) < 0) { return -1; } if (ob) { @@ -2609,7 +2534,7 @@ make_funcptrtype_dict(StgDictObject *stgdict, StgInfo *stginfo) stginfo->converters = converters; } - if (PyDict_GetItemRef((PyObject *)stgdict, &_Py_ID(_restype_), &ob) < 0) { + if (PyDict_GetItemRef((PyObject *)attrdict, &_Py_ID(_restype_), &ob) < 0) { return -1; } if (ob) { @@ -2668,7 +2593,6 @@ static PyObject * PyCFuncPtrType_new(PyTypeObject *type, PyObject *args, PyObject *kwds) { PyTypeObject *result; - StgDictObject *stgdict; /* create the new instance (which is a class, since we are a metatype!) */ @@ -2677,17 +2601,15 @@ PyCFuncPtrType_new(PyTypeObject *type, PyObject *args, PyObject *kwds) return NULL; } - ctypes_state *st = GLOBAL_STATE(); - stgdict = (StgDictObject *)_PyObject_CallNoArgs( - (PyObject *)st->PyCStgDict_Type); - if (!stgdict) { - Py_DECREF(result); + PyObject *attrdict = PyType_GetDict(result); + if (!attrdict) { return NULL; } + + ctypes_state *st = GLOBAL_STATE(); StgInfo *stginfo = PyStgInfo_Init(st, result); if (!stginfo) { Py_DECREF(result); - Py_DECREF((PyObject *)stgdict); return NULL; } @@ -2702,20 +2624,11 @@ PyCFuncPtrType_new(PyTypeObject *type, PyObject *args, PyObject *kwds) stginfo->format = _ctypes_alloc_format_string(NULL, "X{}"); if (stginfo->format == NULL) { Py_DECREF(result); - Py_DECREF((PyObject *)stgdict); return NULL; } stginfo->flags |= TYPEFLAG_ISPOINTER; - /* replace the class dict by our updated storage dict */ - if (-1 == PyDict_Update((PyObject *)stgdict, result->tp_dict)) { - Py_DECREF(result); - Py_DECREF((PyObject *)stgdict); - return NULL; - } - Py_SETREF(result->tp_dict, (PyObject *)stgdict); - - if (-1 == make_funcptrtype_dict(stgdict, stginfo)) { + if (-1 == make_funcptrtype_dict(attrdict, stginfo)) { Py_DECREF(result); return NULL; } @@ -4555,11 +4468,16 @@ _init_pos_args(PyObject *self, PyTypeObject *type, PyObject *args, PyObject *kwds, Py_ssize_t index) { - StgDictObject *dict; + PyObject *attrdict; PyObject *fields; Py_ssize_t i; - if (PyType_stgdict((PyObject *)type->tp_base)) { + ctypes_state *st = GLOBAL_STATE(); + StgInfo *baseinfo; + if (PyStgInfo_FromType(st, (PyObject *)type->tp_base, &baseinfo) < 0) { + return -1; + } + if (baseinfo) { index = _init_pos_args(self, type->tp_base, args, kwds, index); @@ -4567,17 +4485,16 @@ _init_pos_args(PyObject *self, PyTypeObject *type, return -1; } - dict = PyType_stgdict((PyObject *)type); - assert(dict); + attrdict = PyType_GetDict(type); + assert(attrdict); - ctypes_state *st = GLOBAL_STATE(); StgInfo *info; if (PyStgInfo_FromType(st, (PyObject *)type, &info) < 0) { return -1; } assert(info); - fields = PyDict_GetItemWithError((PyObject *)dict, &_Py_ID(_fields_)); + fields = PyDict_GetItemWithError((PyObject *)attrdict, &_Py_ID(_fields_)); if (fields == NULL) { if (PyErr_Occurred()) { return -1; From 6705e0e3b6e4b3e4b315e9c0eba1ffd64f4f7ed2 Mon Sep 17 00:00:00 2001 From: Petr Viktorin Date: Wed, 21 Feb 2024 21:17:26 +0100 Subject: [PATCH 24/59] Remove more uses of StgDict --- Modules/_ctypes/_ctypes.c | 6 ------ Modules/_ctypes/stgdict.c | 43 ++++++++++++++++++--------------------- 2 files changed, 20 insertions(+), 29 deletions(-) diff --git a/Modules/_ctypes/_ctypes.c b/Modules/_ctypes/_ctypes.c index 54af400dbf5f73..47ce768ae1cf45 100644 --- a/Modules/_ctypes/_ctypes.c +++ b/Modules/_ctypes/_ctypes.c @@ -691,12 +691,6 @@ StructUnionType_new(PyTypeObject *type, PyObject *args, PyObject *kwds, int isSt return (PyObject *)result; } else { - StgDictObject *basedict = PyType_stgdict((PyObject *)result->tp_base); - - if (basedict == NULL) { - return (PyObject *)result; - } - StgInfo *baseinfo; if (PyStgInfo_FromType(st, (PyObject *)result->tp_base, &baseinfo) < 0) { diff --git a/Modules/_ctypes/stgdict.c b/Modules/_ctypes/stgdict.c index 34926981c9c533..3ce4d571a95dab 100644 --- a/Modules/_ctypes/stgdict.c +++ b/Modules/_ctypes/stgdict.c @@ -871,7 +871,6 @@ PyCStructUnionType_update_stgdict(PyObject *type, PyObject *fields, int isStruct for (i = 0; i < len; ++i) { PyObject *name, *desc; PyObject *pair = PySequence_GetItem(fields, i); - StgDictObject *dict; int bitsize = 0; if (pair == NULL) { @@ -883,21 +882,19 @@ PyCStructUnionType_update_stgdict(PyObject *type, PyObject *fields, int isStruct Py_DECREF(pair); return -1; } - dict = PyType_stgdict(desc); - if (dict == NULL) { - Py_DECREF(pair); - PyErr_Format(PyExc_TypeError, - "second item in _fields_ tuple (index %zd) must be a C type", - i); - return -1; - } StgInfo *info; if (PyStgInfo_FromType(st, desc, &info) < 0) { Py_DECREF(pair); return -1; } - assert(info); + if (info == NULL) { + Py_DECREF(pair); + PyErr_Format(PyExc_TypeError, + "second item in _fields_ tuple (index %zd) must be a C type", + i); + return -1; + } if (!PyCArrayTypeObject_Check(st, desc)) { /* Not an array. Just need an ffi_type pointer. */ @@ -906,10 +903,12 @@ PyCStructUnionType_update_stgdict(PyObject *type, PyObject *fields, int isStruct else { /* It's an array. */ Py_ssize_t length = info->length; - StgDictObject *edict; - edict = PyType_stgdict(info->proto); - if (edict == NULL) { + StgInfo *einfo; + if (PyStgInfo_FromType(st, info->proto, &einfo) < 0) { + return -1; + } + if (einfo == NULL) { Py_DECREF(pair); PyErr_Format(PyExc_TypeError, "second item in _fields_ tuple (index %zd) must be a C type", @@ -968,7 +967,6 @@ PyCStructUnionType_update_stgdict(PyObject *type, PyObject *fields, int isStruct for (i = 0; i < len; ++i) { PyObject *name, *desc; PyObject *pair = PySequence_GetItem(fields, i); - StgDictObject *dict; int bitsize = 0; if (pair == NULL) { @@ -988,24 +986,23 @@ PyCStructUnionType_update_stgdict(PyObject *type, PyObject *fields, int isStruct PyMem_Free(type_block); return -1; } - dict = PyType_stgdict(desc); - /* Possibly this check could be avoided, but see above comment. */ - if (dict == NULL) { + + StgInfo *info; + if (PyStgInfo_FromType(st, desc, &info) < 0) { Py_DECREF(pair); PyMem_Free(type_block); - PyErr_Format(PyExc_TypeError, - "second item in _fields_ tuple (index %zd) must be a C type", - i); return -1; } - StgInfo *info; - if (PyStgInfo_FromType(st, desc, &info) < 0) { + /* Possibly this check could be avoided, but see above comment. */ + if (info == NULL) { Py_DECREF(pair); PyMem_Free(type_block); + PyErr_Format(PyExc_TypeError, + "second item in _fields_ tuple (index %zd) must be a C type", + i); return -1; } - assert(info); assert(element_index < (ffi_ofs + len)); /* will be used below */ if (!PyCArrayTypeObject_Check(st, desc)) { From 8f993773833e3e484fcd7bd5619902a8767ed6e2 Mon Sep 17 00:00:00 2001 From: Petr Viktorin Date: Wed, 21 Feb 2024 21:17:50 +0100 Subject: [PATCH 25/59] Remove StgDict --- Modules/_ctypes/_ctypes.c | 11 ---- Modules/_ctypes/ctypes.h | 48 ---------------- Modules/_ctypes/stgdict.c | 115 -------------------------------------- 3 files changed, 174 deletions(-) diff --git a/Modules/_ctypes/_ctypes.c b/Modules/_ctypes/_ctypes.c index 47ce768ae1cf45..f4f4d8ea432aff 100644 --- a/Modules/_ctypes/_ctypes.c +++ b/Modules/_ctypes/_ctypes.c @@ -148,7 +148,6 @@ static _HackyHeapType PyCFuncPtr_Type; ctypes_state global_state = { - .PyCStgDict_Type = &PyCStgDict_Type, .PyCData_Type = (PyTypeObject*)&PyCData_Type, .Struct_Type = (PyTypeObject*)&Struct_Type, .Union_Type = (PyTypeObject*)&Union_Type, @@ -5867,13 +5866,6 @@ _ctypes_add_types(PyObject *mod) return -1; \ } -#define TYPE_READY_BASE(TYPE_EXPR, TP_BASE) \ - do { \ - PyTypeObject *type = (TYPE_EXPR); \ - type->tp_base = (TP_BASE); \ - TYPE_READY(type); \ - } while (0) - #define MOD_ADD_TYPE(TYPE_EXPR, TP_TYPE, TP_BASE) \ do { \ PyTypeObject *type = (TYPE_EXPR); \ @@ -5902,8 +5894,6 @@ _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(st->PyCData_Type); - /* StgDict is derived from PyDict_Type */ - TYPE_READY_BASE(st->PyCStgDict_Type, &PyDict_Type); // Common Metaclass CREATE_TYPE(mod, st->PyCType_Type, &pyctype_type_spec, @@ -5958,7 +5948,6 @@ _ctypes_add_types(PyObject *mod) #endif #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 7a066efc0a4308..373f9fc45ed03b 100644 --- a/Modules/_ctypes/ctypes.h +++ b/Modules/_ctypes/ctypes.h @@ -41,7 +41,6 @@ typedef struct { PyTypeObject *PyCArg_Type; PyTypeObject *PyCField_Type; PyTypeObject *PyCThunk_Type; - PyTypeObject *PyCStgDict_Type; PyTypeObject *StructParam_Type; PyTypeObject *PyCStructType_Type; PyTypeObject *UnionType_Type; @@ -159,10 +158,6 @@ typedef struct { PyObject *paramflags; } PyCFuncPtrObject; -extern PyTypeObject PyCStgDict_Type; -#define PyCStgDict_CheckExact(st, v) Py_IS_TYPE((v), (st)->PyCStgDict_Type) -#define PyCStgDict_Check(st, v) PyObject_TypeCheck((v), (st)->PyCStgDict_Type) - extern int PyCStructUnionType_update_stgdict(PyObject *fields, PyObject *type, int isStruct); extern int PyType_stginfo(PyTypeObject *self, Py_ssize_t *psize, Py_ssize_t *palign, Py_ssize_t *plength); extern int PyObject_stginfo(PyObject *self, Py_ssize_t *psize, Py_ssize_t *palign, Py_ssize_t *plength); @@ -264,43 +259,6 @@ extern int PyStgInfo_FromAny(ctypes_state *state, PyObject *obj, StgInfo **resul // Initialize StgInfo on a newly created type extern StgInfo *PyStgInfo_Init(ctypes_state *state, PyTypeObject *type); -/* A subclass of PyDictObject, used as the instance dictionary of ctypes - metatypes */ -typedef struct { - PyDictObject dict; /* first part identical to PyDictObject */ -/* The size and align fields are unneeded, they are in ffi_type as well. As - an experiment shows, it's trivial to get rid of them, the only thing to - remember is that in PyCArrayType_new the ffi_type fields must be filled in - - so far it was unneeded because libffi doesn't support arrays at all - (because they are passed as pointers to function calls anyway). But it's - too much risk to change that now, and there are other fields which doesn't - belong into this structure anyway. Maybe in ctypes 2.0... (ctypes 2000?) -*/ - //Py_ssize_t size; /* number of bytes */ - //Py_ssize_t align; /* alignment requirements */ - //Py_ssize_t length; /* number of fields */ - //ffi_type ffi_type_pointer; - //PyObject *proto; /* Only for Pointer/ArrayObject */ - //SETFUNC setfunc; /* Only for simple objects */ - //GETFUNC getfunc; /* Only for simple objects */ - //PARAMFUNC paramfunc; - - /* Following fields only used by PyCFuncPtrType_Type instances */ - //PyObject *argtypes; /* tuple of CDataObjects */ - //PyObject *converters; /* tuple([t.from_param for t in argtypes]) */ - //PyObject *restype; /* CDataObject or NULL */ - //PyObject *checker; - //int flags; /* calling convention and such */ - - /* pep3118 fields, pointers need PyMem_Free */ - //char *format; - //int ndim; - //Py_ssize_t *shape; -/* Py_ssize_t *strides; */ /* unused in ctypes */ -/* Py_ssize_t *suboffsets; */ /* unused in ctypes */ - -} StgDictObject; - /**************************************************************** StgDictObject fields @@ -342,12 +300,6 @@ typedef struct { *****************************************************************/ extern int PyCStgDict_clone(StgInfo *dst_info, StgInfo *src_info); -/* May return NULL, but does not set an exception! */ -extern StgDictObject *PyType_stgdict(PyObject *obj); - -/* May return NULL, but does not set an exception! */ -extern StgDictObject *PyObject_stgdict(PyObject *self); - typedef int(* PPROC)(void); diff --git a/Modules/_ctypes/stgdict.c b/Modules/_ctypes/stgdict.c index 3ce4d571a95dab..ba58f6de190ab6 100644 --- a/Modules/_ctypes/stgdict.c +++ b/Modules/_ctypes/stgdict.c @@ -86,41 +86,6 @@ PyStgInfo_Init(ctypes_state *state, PyTypeObject *type) XXX blabla more */ -/* Seems we need this, otherwise we get problems when calling - * PyDict_SetItem() (ma_lookup is NULL) - */ -static int -PyCStgDict_init(StgDictObject *self, PyObject *args, PyObject *kwds) -{ - if (PyDict_Type.tp_init((PyObject *)self, args, kwds) < 0) { - return -1; - } - return 0; -} - -static int -PyCStgDict_clear(StgDictObject *self) -{ - return 0; -} - -static void -PyCStgDict_dealloc(StgDictObject *self) -{ - PyCStgDict_clear(self); - PyDict_Type.tp_dealloc((PyObject *)self); -} - -static PyObject * -PyCStgDict_sizeof(StgDictObject *self, void *unused) -{ - Py_ssize_t res; - - res = _PyDict_SizeOf((PyDictObject *)self); - res += sizeof(StgDictObject) - sizeof(PyDictObject); - return PyLong_FromSsize_t(res); -} - int PyCStgDict_clone(StgInfo *dst_info, StgInfo *src_info) { @@ -173,86 +138,6 @@ PyCStgDict_clone(StgInfo *dst_info, StgInfo *src_info) return 0; } -static struct PyMethodDef PyCStgDict_methods[] = { - {"__sizeof__", (PyCFunction)PyCStgDict_sizeof, METH_NOARGS}, - {NULL, NULL} /* sentinel */ -}; - -PyTypeObject PyCStgDict_Type = { - PyVarObject_HEAD_INIT(NULL, 0) - "StgDict", - sizeof(StgDictObject), - 0, - (destructor)PyCStgDict_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_BASETYPE, /* tp_flags */ - 0, /* tp_doc */ - 0, /* tp_traverse */ - 0, /* tp_clear */ - 0, /* tp_richcompare */ - 0, /* tp_weaklistoffset */ - 0, /* tp_iter */ - 0, /* tp_iternext */ - PyCStgDict_methods, /* 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 */ - (initproc)PyCStgDict_init, /* tp_init */ - 0, /* tp_alloc */ - 0, /* tp_new */ - 0, /* tp_free */ -}; - -/* May return NULL, but does not set an exception! */ -StgDictObject * -PyType_stgdict(PyObject *obj) -{ - PyTypeObject *type; - - if (!PyType_Check(obj)) { - return NULL; - } - ctypes_state *st = GLOBAL_STATE(); - type = (PyTypeObject *)obj; - if (!type->tp_dict || !PyCStgDict_CheckExact(st, type->tp_dict)) { - return NULL; - } - return (StgDictObject *)type->tp_dict; -} - -/* May return NULL, but does not set an exception! */ -/* - This function should be as fast as possible, so we don't call PyType_stgdict - above but inline the code, and avoid the PyType_Check(). -*/ -StgDictObject * -PyObject_stgdict(PyObject *self) -{ - PyTypeObject *type = Py_TYPE(self); - ctypes_state *st = GLOBAL_STATE(); - if (!type->tp_dict || !PyCStgDict_CheckExact(st, type->tp_dict)) { - return NULL; - } - return (StgDictObject *)type->tp_dict; -} - /* descr is the descriptor for a field marked as anonymous. Get all the _fields_ descriptors from descr->proto, create new descriptors with offset and index adjusted, and stuff them into type. From daea3e502b80ca7737087cba200e4748ee49bc07 Mon Sep 17 00:00:00 2001 From: Petr Viktorin Date: Wed, 21 Feb 2024 21:43:48 +0100 Subject: [PATCH 26/59] Remove StgDict from comments, names, and platform-specific code --- Lib/test/test_ctypes/test_callbacks.py | 2 +- Lib/test/test_ctypes/test_struct_fields.py | 2 +- Lib/test/test_ctypes/test_structures.py | 8 ++--- Modules/_ctypes/_ctypes.c | 42 +++++++++++----------- Modules/_ctypes/callbacks.c | 6 ++-- Modules/_ctypes/callproc.c | 2 +- Modules/_ctypes/ctypes.h | 12 +++---- Modules/_ctypes/stgdict.c | 6 ++-- 8 files changed, 41 insertions(+), 39 deletions(-) diff --git a/Lib/test/test_ctypes/test_callbacks.py b/Lib/test/test_ctypes/test_callbacks.py index 64f92ffdca6a3f..8038169afe4304 100644 --- a/Lib/test/test_ctypes/test_callbacks.py +++ b/Lib/test/test_ctypes/test_callbacks.py @@ -106,7 +106,7 @@ def test_pyobject(self): def test_unsupported_restype_1(self): # Only "fundamental" result types are supported for callback - # functions, the type must have a non-NULL stgdict->setfunc. + # functions, the type must have a non-NULL stginfo->setfunc. # POINTER(c_double), for example, is not supported. prototype = self.functype.__func__(POINTER(c_double)) diff --git a/Lib/test/test_ctypes/test_struct_fields.py b/Lib/test/test_ctypes/test_struct_fields.py index f474a02fa8db06..7adab794809def 100644 --- a/Lib/test/test_ctypes/test_struct_fields.py +++ b/Lib/test/test_ctypes/test_struct_fields.py @@ -69,7 +69,7 @@ def test_cfield_inheritance_hierarchy(self): def test_gh99275(self): class BrokenStructure(Structure): def __init_subclass__(cls, **kwargs): - cls._fields_ = [] # This line will fail, `stgdict` is not ready + cls._fields_ = [] # This line will fail, `stginfo` is not ready with self.assertRaisesRegex(TypeError, 'ctypes state is not initialized'): diff --git a/Lib/test/test_ctypes/test_structures.py b/Lib/test/test_ctypes/test_structures.py index 98bc4bdcac9306..cbae38d0d4acc4 100644 --- a/Lib/test/test_ctypes/test_structures.py +++ b/Lib/test/test_ctypes/test_structures.py @@ -507,8 +507,8 @@ def _test_issue18060(self, Vector): @unittest.skipUnless(sys.byteorder == 'little', "can't test on this platform") def test_issue18060_a(self): # This test case calls - # PyCStructUnionType_update_stgdict() for each - # _fields_ assignment, and PyCStgDict_clone() + # PyCStructUnionType_update_stginfo() for each + # _fields_ assignment, and PyCStgInfo_clone() # for the Mid and Vector class definitions. class Base(Structure): _fields_ = [('y', c_double), @@ -523,7 +523,7 @@ class Vector(Mid): pass @unittest.skipUnless(sys.byteorder == 'little', "can't test on this platform") def test_issue18060_b(self): # This test case calls - # PyCStructUnionType_update_stgdict() for each + # PyCStructUnionType_update_stginfo() for each # _fields_ assignment. class Base(Structure): _fields_ = [('y', c_double), @@ -538,7 +538,7 @@ class Vector(Mid): @unittest.skipUnless(sys.byteorder == 'little', "can't test on this platform") def test_issue18060_c(self): # This test case calls - # PyCStructUnionType_update_stgdict() for each + # PyCStructUnionType_update_stginfo() for each # _fields_ assignment. class Base(Structure): _fields_ = [('y', c_double)] diff --git a/Modules/_ctypes/_ctypes.c b/Modules/_ctypes/_ctypes.c index f4f4d8ea432aff..78b836875653e8 100644 --- a/Modules/_ctypes/_ctypes.c +++ b/Modules/_ctypes/_ctypes.c @@ -2,7 +2,7 @@ ToDo: Get rid of the checker (and also the converters) field in PyCFuncPtrObject and - StgDictObject, and replace them by slot functions in StgDictObject. + StgInfo, and replace them by slot functions in StgInfo. think about a buffer-like object (memory? bytes?) @@ -36,7 +36,6 @@ PyCData_Type Simple_Type __new__(), __init__(), _as_parameter_ PyCField_Type -PyCStgDict_Type ============================================================================== @@ -82,7 +81,6 @@ bytes(cdata) */ /* - * PyCStgDict_Type * PyCStructType_Type * UnionType_Type * PyCPointerType_Type @@ -571,9 +569,8 @@ static PyType_Spec pyctype_type_spec = { /* PyCStructType_Type - a meta type/class. Creating a new class using this one as - __metaclass__ will call the constructor StructUnionType_new. It replaces the - tp_dict member with a new instance of StgDict, and initializes the C - accessible fields somehow. + __metaclass__ will call the constructor StructUnionType_new. + It initializes the C accessible fields somehow. */ static PyCArgObject * @@ -701,7 +698,7 @@ StructUnionType_new(PyTypeObject *type, PyObject *args, PyObject *kwds, int isSt } /* copy base info */ - if (-1 == PyCStgDict_clone(info, baseinfo)) { + if (-1 == PyCStgInfo_clone(info, baseinfo)) { Py_DECREF(result); return NULL; } @@ -1054,7 +1051,7 @@ PyCStructType_setattro(PyObject *self, PyObject *key, PyObject *value) if (value && PyUnicode_Check(key) && _PyUnicode_EqualToASCIIString(key, "_fields_")) - return PyCStructUnionType_update_stgdict(self, value, 1); + return PyCStructUnionType_update_stginfo(self, value, 1); return 0; } @@ -1068,7 +1065,7 @@ UnionType_setattro(PyObject *self, PyObject *key, PyObject *value) if (PyUnicode_Check(key) && _PyUnicode_EqualToASCIIString(key, "_fields_")) - return PyCStructUnionType_update_stgdict(self, value, 0); + return PyCStructUnionType_update_stginfo(self, value, 0); return 0; } @@ -2443,10 +2440,13 @@ converters_from_argtypes(PyObject *ob) * not bitfields, the bitfields check is also being disabled as a * precaution. - StgDictObject *stgdict = PyType_stgdict(tp); + StgInfo *stginfo; + if (PyStgInfo_FromType(st, tp, &stginfo) < 0) { + return -1; + } - if (stgdict != NULL) { - if (stgdict->flags & TYPEFLAG_HASUNION) { + if (stginfo != NULL) { + if (stginfo->flags & TYPEFLAG_HASUNION) { Py_DECREF(converters); Py_DECREF(ob); if (!PyErr_Occurred()) { @@ -2457,7 +2457,7 @@ converters_from_argtypes(PyObject *ob) } return NULL; } - if (stgdict->flags & TYPEFLAG_HASBITFIELD) { + if (stginfo->flags & TYPEFLAG_HASBITFIELD) { Py_DECREF(converters); Py_DECREF(ob); if (!PyErr_Occurred()) { @@ -2550,7 +2550,7 @@ make_funcptrtype_dict(PyObject *attrdict, StgInfo *stginfo) } } /* XXX later, maybe. - if (PyDict_GetItemRef((PyObject *)stgdict, &_Py _ID(_errcheck_), &ob) < 0) { + if (PyDict_GetItemRef((PyObject *)attrdict, &_Py _ID(_errcheck_), &ob) < 0) { return -1; } if (ob) { @@ -2560,7 +2560,7 @@ make_funcptrtype_dict(PyObject *attrdict, StgInfo *stginfo) Py_DECREF(ob); return -1; } - stgdict->errcheck = ob; + stginfo->errcheck = ob; } */ return 0; @@ -3460,7 +3460,6 @@ static PPROC FindAddress(void *handle, const char *name, PyObject *type) #else char *mangled_name; int i; - StgDictObject *dict; Py_BEGIN_ALLOW_THREADS address = (PPROC)GetProcAddress(handle, name); @@ -3471,9 +3470,12 @@ static PPROC FindAddress(void *handle, const char *name, PyObject *type) return NULL; } - dict = PyType_stgdict((PyObject *)type); - /* It should not happen that dict is NULL, but better be safe */ - if (dict==NULL || dict->flags & FUNCFLAG_CDECL) + StgInfo *info; + if (PyStgInfo_FromType(st, (PyObject *)type, &info) < 0) { + return -1; + } + /* It should not happen that info is NULL, but better be safe */ + if (info==NULL || info->flags & FUNCFLAG_CDECL) return address; /* for stdcall, try mangled names: @@ -5137,7 +5139,7 @@ Simple_from_outparm(PyObject *self, PyObject *args) if (_ctypes_simple_instance((PyObject *)Py_TYPE(self))) { return Py_NewRef(self); } - /* call stgdict->getfunc */ + /* call stginfo->getfunc */ return Simple_get_value((CDataObject *)self, NULL); } diff --git a/Modules/_ctypes/callbacks.c b/Modules/_ctypes/callbacks.c index 24aa0ef834878d..55c1d0f5fda88e 100644 --- a/Modules/_ctypes/callbacks.c +++ b/Modules/_ctypes/callbacks.c @@ -109,10 +109,10 @@ PrintError(const char *msg, ...) * slower. */ static void -TryAddRef(StgDictObject *dict, CDataObject *obj) +TryAddRef(PyObject *cnv, CDataObject *obj) { IUnknown *punk; - int r = PyDict_Contains((PyObject *)dict, &_Py_ID(_needs_com_addref_)); + int r = PyObject_GetAttrString((PyObject *)cnv, "_needs_com_addref_"); if (r <= 0) { if (r < 0) { PrintError("getting _needs_com_addref_"); @@ -187,7 +187,7 @@ static void _CallPythonObject(void *mem, memcpy(obj->b_ptr, *pArgs, info->size); args[i] = (PyObject *)obj; #ifdef MS_WIN32 - TryAddRef(dict, obj); + TryAddRef(cnv, obj); #endif } else { PyErr_SetString(PyExc_TypeError, diff --git a/Modules/_ctypes/callproc.c b/Modules/_ctypes/callproc.c index 104aad7264c904..11e6d1a5aa27a2 100644 --- a/Modules/_ctypes/callproc.c +++ b/Modules/_ctypes/callproc.c @@ -676,7 +676,7 @@ static int ConvParam(PyObject *obj, Py_ssize_t index, struct argument *pa) assert(info); PyCArgObject *carg; assert(info->paramfunc); - /* If it has an stgdict, it is a CDataObject */ + /* If it has an stginfo, it is a CDataObject */ carg = info->paramfunc((CDataObject *)obj); if (carg == NULL) return -1; diff --git a/Modules/_ctypes/ctypes.h b/Modules/_ctypes/ctypes.h index 373f9fc45ed03b..558f4f6384fe4b 100644 --- a/Modules/_ctypes/ctypes.h +++ b/Modules/_ctypes/ctypes.h @@ -144,7 +144,7 @@ typedef struct { CThunkObject *thunk; PyObject *callable; - /* These two fields will override the ones in the type's stgdict if + /* These two fields will override the ones in the type's stginfo if they are set */ PyObject *converters; PyObject *argtypes; @@ -158,7 +158,7 @@ typedef struct { PyObject *paramflags; } PyCFuncPtrObject; -extern int PyCStructUnionType_update_stgdict(PyObject *fields, PyObject *type, int isStruct); +extern int PyCStructUnionType_update_stginfo(PyObject *fields, PyObject *type, int isStruct); extern int PyType_stginfo(PyTypeObject *self, Py_ssize_t *psize, Py_ssize_t *palign, Py_ssize_t *plength); extern int PyObject_stginfo(PyObject *self, Py_ssize_t *psize, Py_ssize_t *palign, Py_ssize_t *plength); @@ -260,7 +260,7 @@ extern int PyStgInfo_FromAny(ctypes_state *state, PyObject *obj, StgInfo **resul extern StgInfo *PyStgInfo_Init(ctypes_state *state, PyTypeObject *type); /**************************************************************** - StgDictObject fields + StgInfo fields setfunc and getfunc is only set for simple data types, it is copied from the corresponding fielddesc entry. These are functions to set and get the value @@ -271,11 +271,11 @@ extern StgInfo *PyStgInfo_Init(ctypes_state *state, PyTypeObject *type); object. Probably all the magic ctypes methods (like from_param) should have C - callable wrappers in the StgDictObject. For simple data type, for example, + callable wrappers in the StgInfo. For simple data type, for example, the fielddesc table could have entries for C codec from_param functions or other methods as well, if a subtype overrides this method in Python at construction time, or assigns to it later, tp_setattro should update the - StgDictObject function to a generic one. + StgInfo function to a generic one. Currently, PyCFuncPtr types have 'converters' and 'checker' entries in their type dict. They are only used to cache attributes from other entries, which @@ -299,7 +299,7 @@ extern StgInfo *PyStgInfo_Init(ctypes_state *state, PyTypeObject *type); *****************************************************************/ -extern int PyCStgDict_clone(StgInfo *dst_info, StgInfo *src_info); +extern int PyCStgInfo_clone(StgInfo *dst_info, StgInfo *src_info); typedef int(* PPROC)(void); diff --git a/Modules/_ctypes/stgdict.c b/Modules/_ctypes/stgdict.c index ba58f6de190ab6..0b8c074c4d5325 100644 --- a/Modules/_ctypes/stgdict.c +++ b/Modules/_ctypes/stgdict.c @@ -87,7 +87,7 @@ PyStgInfo_Init(ctypes_state *state, PyTypeObject *type) */ int -PyCStgDict_clone(StgInfo *dst_info, StgInfo *src_info) +PyCStgInfo_clone(StgInfo *dst_info, StgInfo *src_info) { Py_ssize_t size; @@ -299,10 +299,10 @@ _ctypes_alloc_format_padding(const char *prefix, Py_ssize_t padding) /* Retrieve the (optional) _pack_ attribute from a type, the _fields_ attribute, - and create an StgDictObject. Used for Structure and Union subclasses. + and initialize StgInfo. Used for Structure and Union subclasses. */ int -PyCStructUnionType_update_stgdict(PyObject *type, PyObject *fields, int isStruct) +PyCStructUnionType_update_stginfo(PyObject *type, PyObject *fields, int isStruct) { Py_ssize_t len, offset, size, align, i; Py_ssize_t union_size, total_align, aligned_size; From f67211f4e24c3a0da7b78f7025097e718bf2efd2 Mon Sep 17 00:00:00 2001 From: Petr Viktorin Date: Fri, 23 Feb 2024 12:44:38 +0100 Subject: [PATCH 27/59] Switch PyCSimpleType_new to _init --- Modules/_ctypes/_ctypes.c | 68 ++++++++++++++++++--------------------- 1 file changed, 32 insertions(+), 36 deletions(-) diff --git a/Modules/_ctypes/_ctypes.c b/Modules/_ctypes/_ctypes.c index 78b836875653e8..53cde8f5d47858 100644 --- a/Modules/_ctypes/_ctypes.c +++ b/Modules/_ctypes/_ctypes.c @@ -562,7 +562,7 @@ static PyType_Spec pyctype_type_spec = { .name = "_ctypes.CType_Type", .basicsize = -(Py_ssize_t)sizeof(StgInfo), .flags = (Py_TPFLAGS_DEFAULT | Py_TPFLAGS_IMMUTABLETYPE | - Py_TPFLAGS_HAVE_GC | Py_TPFLAGS_DISALLOW_INSTANTIATION | + Py_TPFLAGS_HAVE_GC | Py_TPFLAGS_BASETYPE ), .slots = ctype_type_slots, }; @@ -2099,8 +2099,8 @@ PyCSimpleType_paramfunc(CDataObject *self) return parg; } -static PyObject * -PyCSimpleType_new(PyTypeObject *type, PyObject *args, PyObject *kwds) +static int +PyCSimpleType_init(PyObject *self, PyObject *args, PyObject *kwds) { PyTypeObject *result; PyObject *proto; @@ -2109,22 +2109,17 @@ PyCSimpleType_new(PyTypeObject *type, PyObject *args, PyObject *kwds) PyMethodDef *ml; struct fielddesc *fmt; - /* create the new instance (which is a class, - since we are a metatype!) */ - result = (PyTypeObject *)PyType_Type.tp_new(type, args, kwds); - if (result == NULL) - return NULL; + result = (PyTypeObject *)self; - if (PyObject_GetOptionalAttr((PyObject *)result, &_Py_ID(_type_), &proto) < 0) { - return NULL; + if (PyObject_GetOptionalAttr(self, &_Py_ID(_type_), &proto) < 0) { + return -1; } if (!proto) { PyErr_SetString(PyExc_AttributeError, "class must define a '_type_' attribute"); error: Py_XDECREF(proto); - Py_DECREF(result); - return NULL; + return -1; } if (PyUnicode_Check(proto)) { proto_str = PyUnicode_AsUTF8AndSize(proto, &proto_len); @@ -2156,7 +2151,7 @@ PyCSimpleType_new(PyTypeObject *type, PyObject *args, PyObject *kwds) } ctypes_state *st = GLOBAL_STATE(); - StgInfo *stginfo = PyStgInfo_Init(st, result); + StgInfo *stginfo = PyStgInfo_Init(st, (PyTypeObject *)self); if (!stginfo) { goto error; } @@ -2173,14 +2168,14 @@ PyCSimpleType_new(PyTypeObject *type, PyObject *args, PyObject *kwds) stginfo->format = _ctypes_alloc_format_string_for_type(proto_str[0], 0); #endif if (stginfo->format == NULL) { - Py_DECREF(result); + Py_DECREF(self); Py_DECREF(proto); - return NULL; + return -1; } stginfo->paramfunc = PyCSimpleType_paramfunc; /* - if (result->tp_base != st->Simple_Type) { + if (self->tp_base != st->Simple_Type) { stginfo->setfunc = NULL; stginfo->getfunc = NULL; } @@ -2192,7 +2187,7 @@ 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 == st->Simple_Type) { + if (((PyTypeObject *)self)->tp_base == st->Simple_Type) { switch (*proto_str) { case 'z': /* c_char_p */ ml = &c_char_p_method; @@ -2220,22 +2215,23 @@ PyCSimpleType_new(PyTypeObject *type, PyObject *args, PyObject *kwds) if (ml) { PyObject *meth; int x; - meth = PyDescr_NewClassMethod(result, ml); + meth = PyDescr_NewClassMethod((PyTypeObject*)self, ml); if (!meth) { - Py_DECREF(result); - return NULL; + Py_DECREF(self); + return -1; } - x = PyDict_SetItemString(result->tp_dict, + x = PyDict_SetItemString(((PyTypeObject*)self)->tp_dict, ml->ml_name, meth); Py_DECREF(meth); if (x == -1) { - Py_DECREF(result); - return NULL; + Py_DECREF(self); + return -1; } } } + PyTypeObject *type = Py_TYPE(self); if (type == st->PyCSimpleType_Type && fmt->setfunc_swapped && fmt->getfunc_swapped) @@ -2243,26 +2239,26 @@ PyCSimpleType_new(PyTypeObject *type, PyObject *args, PyObject *kwds) PyObject *swapped = CreateSwappedType(type, args, kwds, proto, fmt); if (swapped == NULL) { - Py_DECREF(result); - return NULL; + Py_DECREF(self); + return -1; } StgInfo *sw_info; if (PyStgInfo_FromType(st, swapped, &sw_info) < 0) { - Py_DECREF(result); - return NULL; + Py_DECREF(self); + return -1; } assert(sw_info); #ifdef WORDS_BIGENDIAN - PyObject_SetAttrString((PyObject *)result, "__ctype_le__", swapped); - PyObject_SetAttrString((PyObject *)result, "__ctype_be__", (PyObject *)result); - PyObject_SetAttrString(swapped, "__ctype_be__", (PyObject *)result); + PyObject_SetAttrString(self, "__ctype_le__", swapped); + PyObject_SetAttrString(self, "__ctype_be__", self); + PyObject_SetAttrString(swapped, "__ctype_be__", self); PyObject_SetAttrString(swapped, "__ctype_le__", swapped); /* We are creating the type for the OTHER endian */ sw_info->format = _ctypes_alloc_format_string("<", stginfo->format+1); #else - PyObject_SetAttrString((PyObject *)result, "__ctype_be__", swapped); - PyObject_SetAttrString((PyObject *)result, "__ctype_le__", (PyObject *)result); - PyObject_SetAttrString(swapped, "__ctype_le__", (PyObject *)result); + PyObject_SetAttrString(self, "__ctype_be__", swapped); + PyObject_SetAttrString(self, "__ctype_le__", self); + PyObject_SetAttrString(swapped, "__ctype_le__", self); PyObject_SetAttrString(swapped, "__ctype_be__", swapped); /* We are creating the type for the OTHER endian */ sw_info->format = _ctypes_alloc_format_string(">", stginfo->format+1); @@ -2270,11 +2266,11 @@ PyCSimpleType_new(PyTypeObject *type, PyObject *args, PyObject *kwds) Py_DECREF(swapped); if (PyErr_Occurred()) { Py_DECREF(result); - return NULL; + return -1; } }; - return (PyObject *)result; + return 0; } /* @@ -2366,7 +2362,7 @@ static PyMethodDef PyCSimpleType_methods[] = { static PyType_Slot pycsimple_type_slots[] = { {Py_tp_doc, PyDoc_STR("metatype for the PyCSimpleType Objects")}, {Py_tp_methods, PyCSimpleType_methods}, - {Py_tp_new, PyCSimpleType_new}, + {Py_tp_init, PyCSimpleType_init}, {Py_tp_traverse, CDataType_traverse}, {Py_tp_clear, CDataType_clear}, From 02aa9e1f17a36b9af1b258085c9ff91052fe4d9f Mon Sep 17 00:00:00 2001 From: Petr Viktorin Date: Fri, 23 Feb 2024 13:04:59 +0100 Subject: [PATCH 28/59] Switch _SimpleCData to a heap type --- Modules/_ctypes/_ctypes.c | 106 +++++++++++++++++--------------------- 1 file changed, 47 insertions(+), 59 deletions(-) diff --git a/Modules/_ctypes/_ctypes.c b/Modules/_ctypes/_ctypes.c index 53cde8f5d47858..ce099abfc68f79 100644 --- a/Modules/_ctypes/_ctypes.c +++ b/Modules/_ctypes/_ctypes.c @@ -2758,6 +2758,10 @@ PyCData_traverse(CDataObject *self, visitproc visit, void *arg) { Py_VISIT(self->b_objects); Py_VISIT((PyObject *)self->b_base); + PyTypeObject *type = Py_TYPE(self); + if (type->tp_flags & Py_TPFLAGS_HEAPTYPE) { + //TODO Py_VISIT(type); + } return 0; } @@ -2776,8 +2780,15 @@ PyCData_clear(CDataObject *self) static void PyCData_dealloc(PyObject *self) { + PyTypeObject *type = Py_TYPE(self); + if (type->tp_flags & Py_TPFLAGS_HAVE_GC) { + PyObject_GC_UnTrack(self); + } PyCData_clear((CDataObject *)self); - Py_TYPE(self)->tp_free(self); + type->tp_free(self); + if (type->tp_flags & Py_TPFLAGS_HEAPTYPE) { + //TODO Py_DECREF(type); + } } static PyMemberDef PyCData_members[] = { @@ -5149,19 +5160,6 @@ static int Simple_bool(CDataObject *self) return memcmp(self->b_ptr, "\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0", self->b_size); } -static PyNumberMethods Simple_as_number = { - 0, /* nb_add */ - 0, /* nb_subtract */ - 0, /* nb_multiply */ - 0, /* nb_remainder */ - 0, /* nb_divmod */ - 0, /* nb_power */ - 0, /* nb_negative */ - 0, /* nb_positive */ - 0, /* nb_absolute */ - (inquiry)Simple_bool, /* nb_bool */ -}; - /* "%s(%s)" % (self.__class__.__name__, self.value) */ static PyObject * Simple_repr(CDataObject *self) @@ -5184,52 +5182,27 @@ Simple_repr(CDataObject *self) return result; } -static _HackyHeapType Simple_Type = { - .ht = { - .ht_type = { - PyVarObject_HEAD_INIT(NULL, 0) - "_ctypes._SimpleCData", - sizeof(CDataObject), /* tp_basicsize */ - 0, /* tp_itemsize */ - 0, /* tp_dealloc */ - 0, /* tp_vectorcall_offset */ - 0, /* tp_getattr */ - 0, /* tp_setattr */ - 0, /* tp_as_async */ - (reprfunc)&Simple_repr, /* tp_repr */ - &Simple_as_number, /* 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 */ - &PyCData_as_buffer, /* tp_as_buffer */ - Py_TPFLAGS_DEFAULT | Py_TPFLAGS_BASETYPE, /* tp_flags */ - PyDoc_STR("XXX to be provided"), /* tp_doc */ - (traverseproc)PyCData_traverse, /* tp_traverse */ - (inquiry)PyCData_clear, /* tp_clear */ - 0, /* tp_richcompare */ - 0, /* tp_weaklistoffset */ - 0, /* tp_iter */ - 0, /* tp_iternext */ - Simple_methods, /* tp_methods */ - 0, /* tp_members */ - Simple_getsets, /* tp_getset */ - 0, /* tp_base */ - 0, /* tp_dict */ - 0, /* tp_descr_get */ - 0, /* tp_descr_set */ - 0, /* tp_dictoffset */ - (initproc)Simple_init, /* tp_init */ - 0, /* tp_alloc */ - GenericPyCData_new, /* tp_new */ - 0, /* tp_free */ - } - } +static PyType_Slot pycsimple_slots[] = { + {Py_tp_repr, &Simple_repr}, + {Py_tp_doc, PyDoc_STR("XXX to be provided")}, + {Py_tp_methods, Simple_methods}, + {Py_tp_getset, Simple_getsets}, + {Py_tp_init, Simple_init}, + {Py_tp_new, GenericPyCData_new}, + {Py_bf_getbuffer, PyCData_NewGetBuffer}, + {Py_nb_bool, Simple_bool}, + // traverse, dealloc, clear Py_TPFLAGS_HAVE_GC are inherited + {0, NULL}, +}; + +PyType_Spec pycsimple_spec = { + .name = "_ctypes._SimpleCData", + .flags = (Py_TPFLAGS_DEFAULT | Py_TPFLAGS_BASETYPE + | Py_TPFLAGS_IMMUTABLETYPE), + .slots = pycsimple_slots, }; + /******************************************************************/ /* PyCPointer_Type @@ -5883,6 +5856,18 @@ _ctypes_add_types(PyObject *mod) TP = (PyTypeObject *)type; \ } while (0) +#define MOD_ADD_TYPE_M(TP, SPEC, META, BASE) do { \ + PyObject *type = PyType_FromMetaclass(META, mod, SPEC, \ + (PyObject *)BASE); \ + if (type == NULL) { \ + return -1; \ + } \ + TP = (PyTypeObject *)type; \ + if (PyModule_AddType(mod, (PyTypeObject *)type) < 0) { \ + return -1; \ + } \ +} while (0) + ctypes_state *st = GLOBAL_STATE(); /* Note: @@ -5923,9 +5908,11 @@ _ctypes_add_types(PyObject *mod) MOD_ADD_TYPE(st->Union_Type, st->UnionType_Type, st->PyCData_Type); MOD_ADD_TYPE(st->PyCPointer_Type, st->PyCPointerType_Type, st->PyCData_Type); MOD_ADD_TYPE(st->PyCArray_Type, st->PyCArrayType_Type, st->PyCData_Type); - MOD_ADD_TYPE(st->Simple_Type, st->PyCSimpleType_Type, st->PyCData_Type); + //MOD_ADD_TYPE(st->Simple_Type, st->PyCSimpleType_Type, st->PyCData_Type); MOD_ADD_TYPE(st->PyCFuncPtr_Type, st->PyCFuncPtrType_Type, st->PyCData_Type); + MOD_ADD_TYPE_M(st->Simple_Type, &pycsimple_spec, st->PyCSimpleType_Type, st->PyCData_Type); // _ctypes._SimpleCData + /************************************************* * * Simple classes @@ -5946,6 +5933,7 @@ _ctypes_add_types(PyObject *mod) #endif #undef TYPE_READY +#undef MOD_ADD_TYPE_M #undef MOD_ADD_TYPE #undef CREATE_TYPE return 0; From 13feac758be83b9b509a212b2db632c98fb2a08f Mon Sep 17 00:00:00 2001 From: Petr Viktorin Date: Fri, 23 Feb 2024 13:14:34 +0100 Subject: [PATCH 29/59] Convert Struct and Union type _new to _init --- Modules/_ctypes/_ctypes.c | 68 ++++++++++++++++----------------------- 1 file changed, 27 insertions(+), 41 deletions(-) diff --git a/Modules/_ctypes/_ctypes.c b/Modules/_ctypes/_ctypes.c index ce099abfc68f79..240d8d7adf6b30 100644 --- a/Modules/_ctypes/_ctypes.c +++ b/Modules/_ctypes/_ctypes.c @@ -628,38 +628,29 @@ StructUnionType_paramfunc(CDataObject *self) return parg; } -static PyObject * -StructUnionType_new(PyTypeObject *type, PyObject *args, PyObject *kwds, int isStruct) +static int +StructUnionType_init(PyObject *self, PyObject *args, PyObject *kwds, int isStruct) { - PyTypeObject *result; PyObject *fields; - /* create the new instance (which is a class, - since we are a metatype!) */ - result = (PyTypeObject *)PyType_Type.tp_new(type, args, kwds); - if (!result) - return NULL; - - PyObject *attrdict = PyType_GetDict(result); + PyObject *attrdict = PyType_GetDict((PyTypeObject *)self); if (!attrdict) { - return NULL; + return -1; } /* keep this for bw compatibility */ - int r = PyDict_Contains(result->tp_dict, &_Py_ID(_abstract_)); + int r = PyDict_Contains(attrdict, &_Py_ID(_abstract_)); if (r > 0) { - return (PyObject *)result; + return 0; } if (r < 0) { - Py_DECREF(result); - return NULL; + return -1; } ctypes_state *st = GLOBAL_STATE(); - StgInfo *info = PyStgInfo_Init(st, result); + StgInfo *info = PyStgInfo_Init(st, (PyTypeObject *)self); if (!info) { - Py_DECREF(result); - return NULL; + return -1; } if (!isStruct) { info->flags |= TYPEFLAG_HASUNION; @@ -667,57 +658,52 @@ StructUnionType_new(PyTypeObject *type, PyObject *args, PyObject *kwds, int isSt info->format = _ctypes_alloc_format_string(NULL, "B"); if (info->format == NULL) { - Py_DECREF(result); - return NULL; + return -1; } info->paramfunc = StructUnionType_paramfunc; if (PyDict_GetItemRef((PyObject *)attrdict, &_Py_ID(_fields_), &fields) < 0) { - Py_DECREF(result); - return NULL; + return -1; } if (fields) { - if (PyObject_SetAttr((PyObject *)result, &_Py_ID(_fields_), fields) < 0) { - Py_DECREF(result); + if (PyObject_SetAttr(self, &_Py_ID(_fields_), fields) < 0) { Py_DECREF(fields); - return NULL; + return -1; } Py_DECREF(fields); - return (PyObject *)result; + return 0; } else { StgInfo *baseinfo; - if (PyStgInfo_FromType(st, (PyObject *)result->tp_base, + if (PyStgInfo_FromType(st, (PyObject *)((PyTypeObject *)self)->tp_base, &baseinfo) < 0) { - Py_DECREF(result); - return NULL; + return -1; } if (baseinfo == NULL) { - return (PyObject *)result; + return 0; } /* copy base info */ if (-1 == PyCStgInfo_clone(info, baseinfo)) { - Py_DECREF(result); - return NULL; + return -1; } info->flags &= ~DICTFLAG_FINAL; /* clear the 'final' flag in the subclass info */ baseinfo->flags |= DICTFLAG_FINAL; /* set the 'final' flag in the baseclass info */ - return (PyObject *)result; } + return 0; } -static PyObject * -PyCStructType_new(PyTypeObject *type, PyObject *args, PyObject *kwds) +static int +PyCStructType_init(PyObject *self, PyObject *args, PyObject *kwds) { - return StructUnionType_new(type, args, kwds, 1); + return StructUnionType_init(self, args, kwds, 1); } -static PyObject * -UnionType_new(PyTypeObject *type, PyObject *args, PyObject *kwds) +static int +UnionType_init(PyObject *self, PyObject *args, PyObject *kwds) { - return StructUnionType_new(type, args, kwds, 0); + return StructUnionType_init(self, args, kwds, 0); } PyDoc_STRVAR(from_address_doc, @@ -1075,7 +1061,7 @@ static PyType_Slot pycstruct_type_slots[] = { {Py_tp_traverse, CDataType_traverse}, {Py_tp_clear, CDataType_clear}, {Py_tp_methods, CDataType_methods}, - {Py_tp_new, PyCStructType_new}, + {Py_tp_init, PyCStructType_init}, // Sequence protocol. {Py_sq_repeat, CDataType_repeat}, @@ -1095,7 +1081,7 @@ static PyType_Slot union_type_slots[] = { {Py_tp_traverse, CDataType_traverse}, {Py_tp_clear, CDataType_clear}, {Py_tp_methods, CDataType_methods}, - {Py_tp_new, UnionType_new}, + {Py_tp_init, UnionType_init}, // Sequence protocol. {Py_sq_repeat, CDataType_repeat}, From a68417b53c451754f2c82f49c97ede5ef13b5c66 Mon Sep 17 00:00:00 2001 From: Petr Viktorin Date: Fri, 23 Feb 2024 13:34:24 +0100 Subject: [PATCH 30/59] Switch PyCPointerType_new to _init --- Modules/_ctypes/_ctypes.c | 66 +++++++++++++-------------------------- 1 file changed, 22 insertions(+), 44 deletions(-) diff --git a/Modules/_ctypes/_ctypes.c b/Modules/_ctypes/_ctypes.c index 240d8d7adf6b30..219690c1329224 100644 --- a/Modules/_ctypes/_ctypes.c +++ b/Modules/_ctypes/_ctypes.c @@ -1150,24 +1150,15 @@ PyCPointerType_paramfunc(CDataObject *self) return parg; } -static PyObject * -PyCPointerType_new(PyTypeObject *type, PyObject *args, PyObject *kwds) +static int +PyCPointerType_init(PyObject *self, PyObject *args, PyObject *kwds) { - PyTypeObject *result; PyObject *proto; PyObject *typedict; - typedict = PyTuple_GetItem(args, 2); if (!typedict) { - return NULL; - } - - /* create the new instance (which is a class, - since we are a metatype!) */ - result = (PyTypeObject *)PyType_Type.tp_new(type, args, kwds); - if (result == NULL) { - return NULL; + return -1; } /* @@ -1175,10 +1166,9 @@ PyCPointerType_new(PyTypeObject *type, PyObject *args, PyObject *kwds) stginfo->proto has info about the pointed to type! */ ctypes_state *st = GLOBAL_STATE(); - StgInfo *stginfo = PyStgInfo_Init(st, result); + StgInfo *stginfo = PyStgInfo_Init(st, (PyTypeObject *)self); if (!stginfo) { - Py_DECREF((PyObject *)result); - return NULL; + return -1; } stginfo->size = sizeof(void *); stginfo->align = _ctypes_get_fielddesc("P")->pffi_type->alignment; @@ -1188,21 +1178,18 @@ PyCPointerType_new(PyTypeObject *type, PyObject *args, PyObject *kwds) stginfo->flags |= TYPEFLAG_ISPOINTER; if (PyDict_GetItemRef(typedict, &_Py_ID(_type_), &proto) < 0) { - Py_DECREF((PyObject *)result); - return NULL; + return -1; } if (proto) { const char *current_format; if (-1 == PyCPointerType_SetProto(stginfo, proto)) { Py_DECREF(proto); - Py_DECREF((PyObject *)result); - return NULL; + return -1; } StgInfo *iteminfo; if (PyStgInfo_FromType(st, proto, &iteminfo) < 0) { Py_DECREF(proto); - Py_DECREF((PyObject *)result); - return NULL; + return -1; } /* PyCPointerType_SetProto has verified proto has a stginfo. */ assert(iteminfo); @@ -1221,12 +1208,11 @@ PyCPointerType_new(PyTypeObject *type, PyObject *args, PyObject *kwds) } Py_DECREF(proto); if (stginfo->format == NULL) { - Py_DECREF((PyObject *)result); - return NULL; + return -1; } } - return (PyObject *)result; + return 0; } @@ -1327,7 +1313,7 @@ static PyType_Slot pycpointer_type_slots[] = { {Py_tp_traverse, CDataType_traverse}, {Py_tp_clear, CDataType_clear}, {Py_tp_methods, PyCPointerType_methods}, - {Py_tp_new, PyCPointerType_new}, + {Py_tp_init, PyCPointerType_init}, // Sequence protocol. {Py_sq_repeat, CDataType_repeat}, @@ -1347,7 +1333,7 @@ static PyType_Spec pycpointer_type_spec = { PyCArrayType_Type */ /* - PyCArrayType_new ensures that the new Array subclass created has a _length_ + PyCArrayType_init ensures that the new Array subclass created has a _length_ attribute, and a _type_ attribute. */ @@ -1531,25 +1517,18 @@ PyCArrayType_paramfunc(CDataObject *self) return p; } -static PyObject * -PyCArrayType_new(PyTypeObject *type, PyObject *args, PyObject *kwds) +static int +PyCArrayType_init(PyObject *self, PyObject *args, PyObject *kwds) { - PyTypeObject *result; PyObject *length_attr, *type_attr; Py_ssize_t length; Py_ssize_t itemsize, itemalign; - /* create the new instance (which is a class, - since we are a metatype!) */ - result = (PyTypeObject *)PyType_Type.tp_new(type, args, kwds); - if (result == NULL) - return NULL; - /* Initialize these variables to NULL so that we can simplify error handling by using Py_XDECREF. */ type_attr = NULL; - if (PyObject_GetOptionalAttr((PyObject *)result, &_Py_ID(_length_), &length_attr) < 0) { + if (PyObject_GetOptionalAttr(self, &_Py_ID(_length_), &length_attr) < 0) { goto error; } if (!length_attr) { @@ -1582,7 +1561,7 @@ PyCArrayType_new(PyTypeObject *type, PyObject *args, PyObject *kwds) goto error; } - if (PyObject_GetOptionalAttr((PyObject *)result, &_Py_ID(_type_), &type_attr) < 0) { + if (PyObject_GetOptionalAttr(self, &_Py_ID(_type_), &type_attr) < 0) { goto error; } if (!type_attr) { @@ -1592,7 +1571,7 @@ PyCArrayType_new(PyTypeObject *type, PyObject *args, PyObject *kwds) } ctypes_state *st = GLOBAL_STATE(); - StgInfo *stginfo = PyStgInfo_Init(st, result); + StgInfo *stginfo = PyStgInfo_Init(st, (PyTypeObject*)self); if (!stginfo) { goto error; } @@ -1650,26 +1629,25 @@ PyCArrayType_new(PyTypeObject *type, PyObject *args, PyObject *kwds) A permanent annoyance: char arrays are also strings! */ if (iteminfo->getfunc == _ctypes_get_fielddesc("c")->getfunc) { - if (-1 == add_getset(result, CharArray_getsets)) + if (-1 == add_getset((PyTypeObject*)self, CharArray_getsets)) goto error; } else if (iteminfo->getfunc == _ctypes_get_fielddesc("u")->getfunc) { - if (-1 == add_getset(result, WCharArray_getsets)) + if (-1 == add_getset((PyTypeObject*)self, WCharArray_getsets)) goto error; } - return (PyObject *)result; + return 0; error: Py_XDECREF(type_attr); - Py_DECREF(result); - return NULL; + return -1; } static PyType_Slot pycarray_type_slots[] = { {Py_tp_doc, PyDoc_STR("metatype for the Array Objects")}, {Py_tp_traverse, CDataType_traverse}, {Py_tp_methods, CDataType_methods}, - {Py_tp_new, PyCArrayType_new}, + {Py_tp_init, PyCArrayType_init}, {Py_tp_clear, CDataType_clear}, // Sequence protocol. From ad5d7b4d98d7ec110b09ceda4b02d32889f01e23 Mon Sep 17 00:00:00 2001 From: Petr Viktorin Date: Fri, 23 Feb 2024 13:36:07 +0100 Subject: [PATCH 31/59] Switch PyCFuncPtrType_new to _init --- Modules/_ctypes/_ctypes.c | 34 +++++++++++----------------------- 1 file changed, 11 insertions(+), 23 deletions(-) diff --git a/Modules/_ctypes/_ctypes.c b/Modules/_ctypes/_ctypes.c index 219690c1329224..d66f9621ae34f2 100644 --- a/Modules/_ctypes/_ctypes.c +++ b/Modules/_ctypes/_ctypes.c @@ -1668,7 +1668,7 @@ static PyType_Spec pycarray_type_spec = { */ /* -PyCSimpleType_new ensures that the new Simple_Type subclass created has a valid +PyCSimpleType_init ensures that the new Simple_Type subclass created has a valid _type_ attribute. */ @@ -2542,28 +2542,18 @@ PyCFuncPtrType_paramfunc(CDataObject *self) return parg; } -static PyObject * -PyCFuncPtrType_new(PyTypeObject *type, PyObject *args, PyObject *kwds) +static int +PyCFuncPtrType_init(PyObject *self, PyObject *args, PyObject *kwds) { - PyTypeObject *result; - - /* create the new instance (which is a class, - since we are a metatype!) */ - result = (PyTypeObject *)PyType_Type.tp_new(type, args, kwds); - if (result == NULL) { - return NULL; - } - - PyObject *attrdict = PyType_GetDict(result); + PyObject *attrdict = PyType_GetDict((PyTypeObject *)self); if (!attrdict) { - return NULL; + return -1; } ctypes_state *st = GLOBAL_STATE(); - StgInfo *stginfo = PyStgInfo_Init(st, result); + StgInfo *stginfo = PyStgInfo_Init(st, (PyTypeObject *)self); if (!stginfo) { - Py_DECREF(result); - return NULL; + return -1; } stginfo->paramfunc = PyCFuncPtrType_paramfunc; @@ -2576,17 +2566,15 @@ PyCFuncPtrType_new(PyTypeObject *type, PyObject *args, PyObject *kwds) */ stginfo->format = _ctypes_alloc_format_string(NULL, "X{}"); if (stginfo->format == NULL) { - Py_DECREF(result); - return NULL; + return -1; } stginfo->flags |= TYPEFLAG_ISPOINTER; if (-1 == make_funcptrtype_dict(attrdict, stginfo)) { - Py_DECREF(result); - return NULL; + return -1; } - return (PyObject *)result; + return 0; } static PyType_Slot pycfuncptr_type_slots[] = { @@ -2594,7 +2582,7 @@ static PyType_Slot pycfuncptr_type_slots[] = { {Py_tp_traverse, CDataType_traverse}, {Py_tp_clear, CDataType_clear}, {Py_tp_methods, CDataType_methods}, - {Py_tp_new, PyCFuncPtrType_new}, + {Py_tp_init, PyCFuncPtrType_init}, // Sequence protocol. {Py_sq_repeat, CDataType_repeat}, From 8d5e230d6607c5431bb1b7359988bc35d1be10a2 Mon Sep 17 00:00:00 2001 From: Petr Viktorin Date: Fri, 23 Feb 2024 13:56:02 +0100 Subject: [PATCH 32/59] Switch CFuncPtr to heap type --- Modules/_ctypes/_ctypes.c | 85 ++++++++++++--------------------------- 1 file changed, 26 insertions(+), 59 deletions(-) diff --git a/Modules/_ctypes/_ctypes.c b/Modules/_ctypes/_ctypes.c index d66f9621ae34f2..f296367fc23db3 100644 --- a/Modules/_ctypes/_ctypes.c +++ b/Modules/_ctypes/_ctypes.c @@ -4317,8 +4317,11 @@ PyCFuncPtr_clear(PyCFuncPtrObject *self) static void PyCFuncPtr_dealloc(PyCFuncPtrObject *self) { + PyObject_GC_UnTrack(self); PyCFuncPtr_clear(self); - Py_TYPE(self)->tp_free((PyObject *)self); + PyTypeObject *type = Py_TYPE(self); + type->tp_free((PyObject *)self); + Py_DECREF(type); } static PyObject * @@ -4346,63 +4349,26 @@ PyCFuncPtr_bool(PyCFuncPtrObject *self) ); } -static PyNumberMethods PyCFuncPtr_as_number = { - 0, /* nb_add */ - 0, /* nb_subtract */ - 0, /* nb_multiply */ - 0, /* nb_remainder */ - 0, /* nb_divmod */ - 0, /* nb_power */ - 0, /* nb_negative */ - 0, /* nb_positive */ - 0, /* nb_absolute */ - (inquiry)PyCFuncPtr_bool, /* nb_bool */ +static PyType_Slot pycfuncptr_slots[] = { + {Py_tp_dealloc, PyCFuncPtr_dealloc}, + {Py_tp_repr, PyCFuncPtr_repr}, + {Py_tp_call, PyCFuncPtr_call}, + {Py_tp_doc, PyDoc_STR("Function Pointer")}, + {Py_tp_traverse, PyCFuncPtr_traverse}, + {Py_tp_clear, PyCFuncPtr_clear}, + {Py_tp_getset, PyCFuncPtr_getsets}, + {Py_tp_new, PyCFuncPtr_new}, + {Py_bf_getbuffer, PyCData_NewGetBuffer}, + {Py_nb_bool, PyCFuncPtr_bool}, + {0, NULL}, }; -static _HackyHeapType PyCFuncPtr_Type = { - .ht = { - .ht_type = { - PyVarObject_HEAD_INIT(NULL, 0) - "_ctypes.CFuncPtr", - sizeof(PyCFuncPtrObject), /* tp_basicsize */ - 0, /* tp_itemsize */ - (destructor)PyCFuncPtr_dealloc, /* tp_dealloc */ - 0, /* tp_vectorcall_offset */ - 0, /* tp_getattr */ - 0, /* tp_setattr */ - 0, /* tp_as_async */ - (reprfunc)PyCFuncPtr_repr, /* tp_repr */ - &PyCFuncPtr_as_number, /* tp_as_number */ - 0, /* tp_as_sequence */ - 0, /* tp_as_mapping */ - 0, /* tp_hash */ - (ternaryfunc)PyCFuncPtr_call, /* tp_call */ - 0, /* tp_str */ - 0, /* tp_getattro */ - 0, /* tp_setattro */ - &PyCData_as_buffer, /* tp_as_buffer */ - Py_TPFLAGS_DEFAULT | Py_TPFLAGS_BASETYPE, /* tp_flags */ - PyDoc_STR("Function Pointer"), /* tp_doc */ - (traverseproc)PyCFuncPtr_traverse, /* tp_traverse */ - (inquiry)PyCFuncPtr_clear, /* tp_clear */ - 0, /* tp_richcompare */ - 0, /* tp_weaklistoffset */ - 0, /* tp_iter */ - 0, /* tp_iternext */ - 0, /* tp_methods */ - 0, /* tp_members */ - PyCFuncPtr_getsets, /* 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 */ - PyCFuncPtr_new, /* tp_new */ - 0, /* tp_free */ - } - } +PyType_Spec pycfuncptr_spec = { + .name = "_ctypes.CFuncPtr", + .basicsize = sizeof(PyCFuncPtrObject), + .flags = (Py_TPFLAGS_DEFAULT | Py_TPFLAGS_BASETYPE | Py_TPFLAGS_HAVE_GC | + Py_TPFLAGS_IMMUTABLETYPE), + .slots = pycfuncptr_slots, }; /*****************************************************************/ @@ -5860,10 +5826,11 @@ _ctypes_add_types(PyObject *mod) MOD_ADD_TYPE(st->Union_Type, st->UnionType_Type, st->PyCData_Type); MOD_ADD_TYPE(st->PyCPointer_Type, st->PyCPointerType_Type, st->PyCData_Type); MOD_ADD_TYPE(st->PyCArray_Type, st->PyCArrayType_Type, st->PyCData_Type); - //MOD_ADD_TYPE(st->Simple_Type, st->PyCSimpleType_Type, st->PyCData_Type); - MOD_ADD_TYPE(st->PyCFuncPtr_Type, st->PyCFuncPtrType_Type, st->PyCData_Type); - MOD_ADD_TYPE_M(st->Simple_Type, &pycsimple_spec, st->PyCSimpleType_Type, st->PyCData_Type); // _ctypes._SimpleCData + MOD_ADD_TYPE_M(st->Simple_Type, &pycsimple_spec, + st->PyCSimpleType_Type, st->PyCData_Type); + MOD_ADD_TYPE_M(st->PyCFuncPtr_Type, &pycfuncptr_spec, + st->PyCFuncPtrType_Type, st->PyCData_Type); /************************************************* * From cdfae1309646080806feecfa833600971b6eb319 Mon Sep 17 00:00:00 2001 From: Petr Viktorin Date: Fri, 23 Feb 2024 14:07:14 +0100 Subject: [PATCH 33/59] Switch Array to heap type --- Modules/_ctypes/_ctypes.c | 87 ++++++++++----------------------------- 1 file changed, 22 insertions(+), 65 deletions(-) diff --git a/Modules/_ctypes/_ctypes.c b/Modules/_ctypes/_ctypes.c index f296367fc23db3..09e50a41367592 100644 --- a/Modules/_ctypes/_ctypes.c +++ b/Modules/_ctypes/_ctypes.c @@ -4855,26 +4855,6 @@ static PyMethodDef Array_methods[] = { { NULL, NULL } }; -static PySequenceMethods Array_as_sequence = { - Array_length, /* sq_length; */ - 0, /* sq_concat; */ - 0, /* sq_repeat; */ - Array_item, /* sq_item; */ - 0, /* sq_slice; */ - Array_ass_item, /* sq_ass_item; */ - 0, /* sq_ass_slice; */ - 0, /* sq_contains; */ - - 0, /* sq_inplace_concat; */ - 0, /* sq_inplace_repeat; */ -}; - -static PyMappingMethods Array_as_mapping = { - Array_length, - Array_subscript, - Array_ass_subscript, -}; - PyDoc_STRVAR(array_doc, "Abstract base class for arrays.\n" "\n" @@ -4885,50 +4865,26 @@ PyDoc_STRVAR(array_doc, "reads, the resulting object is not itself an Array." ); -static _HackyHeapType PyCArray_Type = { - .ht = { - .ht_type = { - PyVarObject_HEAD_INIT(NULL, 0) - "_ctypes.Array", - sizeof(CDataObject), /* tp_basicsize */ - 0, /* tp_itemsize */ - 0, /* tp_dealloc */ - 0, /* tp_vectorcall_offset */ - 0, /* tp_getattr */ - 0, /* tp_setattr */ - 0, /* tp_as_async */ - 0, /* tp_repr */ - 0, /* tp_as_number */ - &Array_as_sequence, /* tp_as_sequence */ - &Array_as_mapping, /* tp_as_mapping */ - 0, /* tp_hash */ - 0, /* tp_call */ - 0, /* tp_str */ - 0, /* tp_getattro */ - 0, /* tp_setattro */ - &PyCData_as_buffer, /* tp_as_buffer */ - Py_TPFLAGS_DEFAULT | Py_TPFLAGS_BASETYPE, /* tp_flags */ - array_doc, /* tp_doc */ - (traverseproc)PyCData_traverse, /* tp_traverse */ - (inquiry)PyCData_clear, /* tp_clear */ - 0, /* tp_richcompare */ - 0, /* tp_weaklistoffset */ - 0, /* tp_iter */ - 0, /* tp_iternext */ - Array_methods, /* 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 */ - (initproc)Array_init, /* tp_init */ - 0, /* tp_alloc */ - GenericPyCData_new, /* tp_new */ - 0, /* tp_free */ - } - } +static PyType_Slot pycarray_slots[] = { + {Py_tp_doc, (char*)array_doc}, + {Py_tp_methods, Array_methods}, + {Py_tp_init, Array_init}, + {Py_tp_new, GenericPyCData_new}, + {Py_bf_getbuffer, PyCData_NewGetBuffer}, + {Py_sq_length, Array_length}, + {Py_sq_item, Array_item}, + {Py_sq_ass_item, Array_ass_item}, + {Py_mp_length, Array_length}, + {Py_mp_subscript, Array_subscript}, + {Py_mp_ass_subscript, Array_ass_subscript}, + {0, NULL}, +}; + +PyType_Spec pycarray_spec = { + .name = "_ctypes.Array", + .flags = (Py_TPFLAGS_DEFAULT | Py_TPFLAGS_BASETYPE | + Py_TPFLAGS_IMMUTABLETYPE), + .slots = pycarray_slots, }; PyObject * @@ -5825,8 +5781,9 @@ _ctypes_add_types(PyObject *mod) MOD_ADD_TYPE(st->Struct_Type, st->PyCStructType_Type, st->PyCData_Type); MOD_ADD_TYPE(st->Union_Type, st->UnionType_Type, st->PyCData_Type); MOD_ADD_TYPE(st->PyCPointer_Type, st->PyCPointerType_Type, st->PyCData_Type); - MOD_ADD_TYPE(st->PyCArray_Type, st->PyCArrayType_Type, st->PyCData_Type); + MOD_ADD_TYPE_M(st->PyCArray_Type, &pycarray_spec, + st->PyCArrayType_Type, st->PyCData_Type); MOD_ADD_TYPE_M(st->Simple_Type, &pycsimple_spec, st->PyCSimpleType_Type, st->PyCData_Type); MOD_ADD_TYPE_M(st->PyCFuncPtr_Type, &pycfuncptr_spec, From 2d809603263a629c923b6411251f3ecd11243126 Mon Sep 17 00:00:00 2001 From: Petr Viktorin Date: Fri, 23 Feb 2024 14:15:01 +0100 Subject: [PATCH 34/59] Switch _Pointer to a heap type --- Modules/_ctypes/_ctypes.c | 95 ++++++++------------------------------- 1 file changed, 19 insertions(+), 76 deletions(-) diff --git a/Modules/_ctypes/_ctypes.c b/Modules/_ctypes/_ctypes.c index 09e50a41367592..922197c4f180cf 100644 --- a/Modules/_ctypes/_ctypes.c +++ b/Modules/_ctypes/_ctypes.c @@ -5408,91 +5408,33 @@ Pointer_subscript(PyObject *myself, PyObject *item) } } -static PySequenceMethods Pointer_as_sequence = { - 0, /* inquiry sq_length; */ - 0, /* binaryfunc sq_concat; */ - 0, /* intargfunc sq_repeat; */ - Pointer_item, /* intargfunc sq_item; */ - 0, /* intintargfunc sq_slice; */ - Pointer_ass_item, /* intobjargproc sq_ass_item; */ - 0, /* intintobjargproc sq_ass_slice; */ - 0, /* objobjproc sq_contains; */ - /* Added in release 2.0 */ - 0, /* binaryfunc sq_inplace_concat; */ - 0, /* intargfunc sq_inplace_repeat; */ -}; - -static PyMappingMethods Pointer_as_mapping = { - 0, - Pointer_subscript, -}; - static int Pointer_bool(CDataObject *self) { return (*(void **)self->b_ptr != NULL); } -static PyNumberMethods Pointer_as_number = { - 0, /* nb_add */ - 0, /* nb_subtract */ - 0, /* nb_multiply */ - 0, /* nb_remainder */ - 0, /* nb_divmod */ - 0, /* nb_power */ - 0, /* nb_negative */ - 0, /* nb_positive */ - 0, /* nb_absolute */ - (inquiry)Pointer_bool, /* nb_bool */ +static PyType_Slot pycpointer_slots[] = { + {Py_tp_doc, PyDoc_STR("XXX to be provided")}, + {Py_tp_getset, }, + {Py_tp_getset, Pointer_getsets}, + {Py_tp_init, Pointer_init}, + {Py_tp_new, Pointer_new}, + {Py_bf_getbuffer, PyCData_NewGetBuffer}, + {Py_nb_bool, Pointer_bool}, + {Py_mp_subscript, Pointer_subscript}, + {Py_sq_item, Pointer_item}, + {Py_sq_ass_item, Pointer_ass_item}, + {0, NULL}, }; -static _HackyHeapType PyCPointer_Type = { - .ht = { - .ht_type = { - PyVarObject_HEAD_INIT(NULL, 0) - "_ctypes._Pointer", - sizeof(CDataObject), /* tp_basicsize */ - 0, /* tp_itemsize */ - 0, /* tp_dealloc */ - 0, /* tp_vectorcall_offset */ - 0, /* tp_getattr */ - 0, /* tp_setattr */ - 0, /* tp_as_async */ - 0, /* tp_repr */ - &Pointer_as_number, /* tp_as_number */ - &Pointer_as_sequence, /* tp_as_sequence */ - &Pointer_as_mapping, /* tp_as_mapping */ - 0, /* tp_hash */ - 0, /* tp_call */ - 0, /* tp_str */ - 0, /* tp_getattro */ - 0, /* tp_setattro */ - &PyCData_as_buffer, /* tp_as_buffer */ - Py_TPFLAGS_DEFAULT | Py_TPFLAGS_BASETYPE, /* tp_flags */ - PyDoc_STR("XXX to be provided"), /* tp_doc */ - (traverseproc)PyCData_traverse, /* tp_traverse */ - (inquiry)PyCData_clear, /* tp_clear */ - 0, /* tp_richcompare */ - 0, /* tp_weaklistoffset */ - 0, /* tp_iter */ - 0, /* tp_iternext */ - 0, /* tp_methods */ - 0, /* tp_members */ - Pointer_getsets, /* tp_getset */ - 0, /* tp_base */ - 0, /* tp_dict */ - 0, /* tp_descr_get */ - 0, /* tp_descr_set */ - 0, /* tp_dictoffset */ - (initproc)Pointer_init, /* tp_init */ - 0, /* tp_alloc */ - Pointer_new, /* tp_new */ - 0, /* tp_free */ - } - } +PyType_Spec pycpointer_spec = { + .name = "_ctypes._Pointer", + .flags = (Py_TPFLAGS_DEFAULT | Py_TPFLAGS_BASETYPE | + Py_TPFLAGS_IMMUTABLETYPE), + .slots = pycpointer_slots, }; - /******************************************************************/ /* * Module initialization. @@ -5780,8 +5722,9 @@ _ctypes_add_types(PyObject *mod) MOD_ADD_TYPE(st->Struct_Type, st->PyCStructType_Type, st->PyCData_Type); MOD_ADD_TYPE(st->Union_Type, st->UnionType_Type, st->PyCData_Type); - MOD_ADD_TYPE(st->PyCPointer_Type, st->PyCPointerType_Type, st->PyCData_Type); + MOD_ADD_TYPE_M(st->PyCPointer_Type, &pycpointer_spec, + st->PyCPointerType_Type, st->PyCData_Type); MOD_ADD_TYPE_M(st->PyCArray_Type, &pycarray_spec, st->PyCArrayType_Type, st->PyCData_Type); MOD_ADD_TYPE_M(st->Simple_Type, &pycsimple_spec, From 3337ba412daf4f967c195e2f596fefc9fc9ec0a5 Mon Sep 17 00:00:00 2001 From: Petr Viktorin Date: Fri, 23 Feb 2024 15:31:47 +0100 Subject: [PATCH 35/59] Switch _ctypes.Union to heap type --- Modules/_ctypes/_ctypes.c | 60 ++++++++++----------------------------- 1 file changed, 15 insertions(+), 45 deletions(-) diff --git a/Modules/_ctypes/_ctypes.c b/Modules/_ctypes/_ctypes.c index 922197c4f180cf..678b401ca42bf5 100644 --- a/Modules/_ctypes/_ctypes.c +++ b/Modules/_ctypes/_ctypes.c @@ -4539,50 +4539,19 @@ static _HackyHeapType Struct_Type = { } }; -static _HackyHeapType Union_Type = { - .ht = { - .ht_type = { - PyVarObject_HEAD_INIT(NULL, 0) - "_ctypes.Union", - sizeof(CDataObject), /* tp_basicsize */ - 0, /* tp_itemsize */ - 0, /* 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 */ - &PyCData_as_buffer, /* tp_as_buffer */ - Py_TPFLAGS_DEFAULT | Py_TPFLAGS_BASETYPE, /* tp_flags */ - PyDoc_STR("Union base class"), /* tp_doc */ - (traverseproc)PyCData_traverse, /* tp_traverse */ - (inquiry)PyCData_clear, /* 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 */ - Struct_init, /* tp_init */ - 0, /* tp_alloc */ - GenericPyCData_new, /* tp_new */ - 0, /* tp_free */ - } - } +static PyType_Slot pycunion_slots[] = { + {Py_tp_doc, PyDoc_STR("Union base class")}, + {Py_tp_init, Struct_init}, + {Py_tp_new, GenericPyCData_new}, + {Py_bf_getbuffer, PyCData_NewGetBuffer}, + {0, NULL}, +}; + +PyType_Spec pycunion_spec = { + .name = "_ctypes.Union", + .flags = (Py_TPFLAGS_DEFAULT | Py_TPFLAGS_BASETYPE | + Py_TPFLAGS_IMMUTABLETYPE), + .slots = pycunion_slots, }; @@ -5721,8 +5690,9 @@ _ctypes_add_types(PyObject *mod) */ MOD_ADD_TYPE(st->Struct_Type, st->PyCStructType_Type, st->PyCData_Type); - MOD_ADD_TYPE(st->Union_Type, st->UnionType_Type, st->PyCData_Type); + MOD_ADD_TYPE_M(st->Union_Type, &pycunion_spec, + st->UnionType_Type, st->PyCData_Type); MOD_ADD_TYPE_M(st->PyCPointer_Type, &pycpointer_spec, st->PyCPointerType_Type, st->PyCData_Type); MOD_ADD_TYPE_M(st->PyCArray_Type, &pycarray_spec, From 596cb89071047c30648f445cbe61a71845c0ae11 Mon Sep 17 00:00:00 2001 From: Petr Viktorin Date: Fri, 23 Feb 2024 15:34:55 +0100 Subject: [PATCH 36/59] Switch Structure to heap type --- Modules/_ctypes/_ctypes.c | 61 ++++++++++----------------------------- 1 file changed, 15 insertions(+), 46 deletions(-) diff --git a/Modules/_ctypes/_ctypes.c b/Modules/_ctypes/_ctypes.c index 678b401ca42bf5..56046799125d37 100644 --- a/Modules/_ctypes/_ctypes.c +++ b/Modules/_ctypes/_ctypes.c @@ -4493,50 +4493,19 @@ Struct_init(PyObject *self, PyObject *args, PyObject *kwds) return 0; } -static _HackyHeapType Struct_Type = { - .ht = { - .ht_type = { - PyVarObject_HEAD_INIT(NULL, 0) - "_ctypes.Structure", - sizeof(CDataObject), /* tp_basicsize */ - 0, /* tp_itemsize */ - 0, /* 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 */ - &PyCData_as_buffer, /* tp_as_buffer */ - Py_TPFLAGS_DEFAULT | Py_TPFLAGS_BASETYPE, /* tp_flags */ - PyDoc_STR("Structure base class"), /* tp_doc */ - (traverseproc)PyCData_traverse, /* tp_traverse */ - (inquiry)PyCData_clear, /* 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 */ - Struct_init, /* tp_init */ - 0, /* tp_alloc */ - GenericPyCData_new, /* tp_new */ - 0, /* tp_free */ - } - } +static PyType_Slot pycstruct_slots[] = { + {Py_tp_doc, PyDoc_STR("Structure base class")}, + {Py_tp_init, Struct_init}, + {Py_tp_new, GenericPyCData_new}, + {Py_bf_getbuffer, PyCData_NewGetBuffer}, + {0, NULL}, +}; + +PyType_Spec pycstruct_spec = { + .name = "_ctypes.Structure", + .flags = (Py_TPFLAGS_DEFAULT | Py_TPFLAGS_BASETYPE | + Py_TPFLAGS_IMMUTABLETYPE), + .slots = pycstruct_slots, }; static PyType_Slot pycunion_slots[] = { @@ -5689,8 +5658,8 @@ _ctypes_add_types(PyObject *mod) * Classes using a custom metaclass */ - MOD_ADD_TYPE(st->Struct_Type, st->PyCStructType_Type, st->PyCData_Type); - + MOD_ADD_TYPE_M(st->Struct_Type, &pycstruct_spec, + st->PyCStructType_Type, st->PyCData_Type); MOD_ADD_TYPE_M(st->Union_Type, &pycunion_spec, st->UnionType_Type, st->PyCData_Type); MOD_ADD_TYPE_M(st->PyCPointer_Type, &pycpointer_spec, From 91feb64b4d3662aaa60cc5d3538c0ec5b7742af7 Mon Sep 17 00:00:00 2001 From: Petr Viktorin Date: Fri, 23 Feb 2024 15:41:19 +0100 Subject: [PATCH 37/59] Make the type-adding macros more regular --- Modules/_ctypes/_ctypes.c | 91 ++++++++++++++++----------------------- 1 file changed, 37 insertions(+), 54 deletions(-) diff --git a/Modules/_ctypes/_ctypes.c b/Modules/_ctypes/_ctypes.c index 56046799125d37..c3778b181fb4d2 100644 --- a/Modules/_ctypes/_ctypes.c +++ b/Modules/_ctypes/_ctypes.c @@ -5590,19 +5590,8 @@ _ctypes_add_types(PyObject *mod) if (PyType_Ready(TYPE) < 0) { \ return -1; \ } - -#define MOD_ADD_TYPE(TYPE_EXPR, TP_TYPE, TP_BASE) \ - do { \ - PyTypeObject *type = (TYPE_EXPR); \ - Py_SET_TYPE(type, TP_TYPE); \ - type->tp_base = TP_BASE; \ - if (PyModule_AddType(mod, type) < 0) { \ - return -1; \ - } \ - } while (0) - -#define CREATE_TYPE(MOD, TP, SPEC, BASE) do { \ - PyObject *type = PyType_FromMetaclass(NULL, MOD, SPEC, \ +#define CREATE_TYPE(TP, SPEC, META, BASE) do { \ + PyObject *type = PyType_FromMetaclass(META, mod, SPEC, \ (PyObject *)BASE); \ if (type == NULL) { \ return -1; \ @@ -5610,14 +5599,9 @@ _ctypes_add_types(PyObject *mod) TP = (PyTypeObject *)type; \ } while (0) -#define MOD_ADD_TYPE_M(TP, SPEC, META, BASE) do { \ - PyObject *type = PyType_FromMetaclass(META, mod, SPEC, \ - (PyObject *)BASE); \ - if (type == NULL) { \ - return -1; \ - } \ - TP = (PyTypeObject *)type; \ - if (PyModule_AddType(mod, (PyTypeObject *)type) < 0) { \ +#define MOD_ADD_TYPE(TP, SPEC, META, BASE) do { \ + CREATE_TYPE(TP, SPEC, META, BASE); \ + if (PyModule_AddType(mod, (PyTypeObject *)(TP)) < 0) { \ return -1; \ } \ } while (0) @@ -5628,70 +5612,69 @@ _ctypes_add_types(PyObject *mod) ob_type is the metatype (the 'type'), defaults to PyType_Type, tp_base is the base type, defaults to 'object' aka PyBaseObject_Type. */ - CREATE_TYPE(mod, st->PyCArg_Type, &carg_spec, NULL); - CREATE_TYPE(mod, st->PyCThunk_Type, &cthunk_spec, NULL); + CREATE_TYPE(st->PyCArg_Type, &carg_spec, NULL, NULL); + CREATE_TYPE(st->PyCThunk_Type, &cthunk_spec, NULL, NULL); TYPE_READY(st->PyCData_Type); // Common Metaclass - CREATE_TYPE(mod, st->PyCType_Type, &pyctype_type_spec, - &PyType_Type); + CREATE_TYPE(st->PyCType_Type, &pyctype_type_spec, + NULL, &PyType_Type); /************************************************* * * Metaclasses */ - CREATE_TYPE(mod, st->PyCStructType_Type, &pycstruct_type_spec, - st->PyCType_Type); - CREATE_TYPE(mod, st->UnionType_Type, &union_type_spec, - st->PyCType_Type); - CREATE_TYPE(mod, st->PyCPointerType_Type, &pycpointer_type_spec, - st->PyCType_Type); - CREATE_TYPE(mod, st->PyCArrayType_Type, &pycarray_type_spec, - st->PyCType_Type); - CREATE_TYPE(mod, st->PyCSimpleType_Type, &pycsimple_type_spec, - st->PyCType_Type); - CREATE_TYPE(mod, st->PyCFuncPtrType_Type, &pycfuncptr_type_spec, - st->PyCType_Type); + CREATE_TYPE(st->PyCStructType_Type, &pycstruct_type_spec, + NULL, st->PyCType_Type); + CREATE_TYPE(st->UnionType_Type, &union_type_spec, + NULL, st->PyCType_Type); + CREATE_TYPE(st->PyCPointerType_Type, &pycpointer_type_spec, + NULL, st->PyCType_Type); + CREATE_TYPE(st->PyCArrayType_Type, &pycarray_type_spec, + NULL, st->PyCType_Type); + CREATE_TYPE(st->PyCSimpleType_Type, &pycsimple_type_spec, + NULL, st->PyCType_Type); + CREATE_TYPE(st->PyCFuncPtrType_Type, &pycfuncptr_type_spec, + NULL, st->PyCType_Type); /************************************************* * * Classes using a custom metaclass */ - MOD_ADD_TYPE_M(st->Struct_Type, &pycstruct_spec, - st->PyCStructType_Type, st->PyCData_Type); - MOD_ADD_TYPE_M(st->Union_Type, &pycunion_spec, - st->UnionType_Type, st->PyCData_Type); - MOD_ADD_TYPE_M(st->PyCPointer_Type, &pycpointer_spec, - st->PyCPointerType_Type, st->PyCData_Type); - MOD_ADD_TYPE_M(st->PyCArray_Type, &pycarray_spec, - st->PyCArrayType_Type, st->PyCData_Type); - MOD_ADD_TYPE_M(st->Simple_Type, &pycsimple_spec, - st->PyCSimpleType_Type, st->PyCData_Type); - MOD_ADD_TYPE_M(st->PyCFuncPtr_Type, &pycfuncptr_spec, - st->PyCFuncPtrType_Type, st->PyCData_Type); + MOD_ADD_TYPE(st->Struct_Type, &pycstruct_spec, + st->PyCStructType_Type, st->PyCData_Type); + MOD_ADD_TYPE(st->Union_Type, &pycunion_spec, + st->UnionType_Type, st->PyCData_Type); + MOD_ADD_TYPE(st->PyCPointer_Type, &pycpointer_spec, + st->PyCPointerType_Type, st->PyCData_Type); + MOD_ADD_TYPE(st->PyCArray_Type, &pycarray_spec, + st->PyCArrayType_Type, st->PyCData_Type); + MOD_ADD_TYPE(st->Simple_Type, &pycsimple_spec, + st->PyCSimpleType_Type, st->PyCData_Type); + MOD_ADD_TYPE(st->PyCFuncPtr_Type, &pycfuncptr_spec, + st->PyCFuncPtrType_Type, st->PyCData_Type); /************************************************* * * Simple classes */ - CREATE_TYPE(mod, st->PyCField_Type, &cfield_spec, NULL); + CREATE_TYPE(st->PyCField_Type, &cfield_spec, NULL, NULL); /************************************************* * * Other stuff */ - CREATE_TYPE(mod, st->DictRemover_Type, &dictremover_spec, NULL); - CREATE_TYPE(mod, st->StructParam_Type, &structparam_spec, NULL); + CREATE_TYPE(st->DictRemover_Type, &dictremover_spec, NULL, NULL); + CREATE_TYPE(st->StructParam_Type, &structparam_spec, NULL, NULL); #ifdef MS_WIN32 - CREATE_TYPE(mod, st->PyComError_Type, &comerror_spec, PyExc_Exception); + CREATE_TYPE(st->PyComError_Type, &comerror_spec, NULL, PyExc_Exception); #endif #undef TYPE_READY -#undef MOD_ADD_TYPE_M #undef MOD_ADD_TYPE #undef CREATE_TYPE return 0; From 77c64fe46b17253e3e779a1a424bf4b0088b118c Mon Sep 17 00:00:00 2001 From: Petr Viktorin Date: Fri, 23 Feb 2024 15:50:44 +0100 Subject: [PATCH 38/59] Switch _CData to a heap type --- Modules/_ctypes/_ctypes.c | 69 +++++++++++---------------------------- 1 file changed, 19 insertions(+), 50 deletions(-) diff --git a/Modules/_ctypes/_ctypes.c b/Modules/_ctypes/_ctypes.c index c3778b181fb4d2..df7f7268700c59 100644 --- a/Modules/_ctypes/_ctypes.c +++ b/Modules/_ctypes/_ctypes.c @@ -2819,11 +2819,6 @@ PyCData_NewGetBuffer(PyObject *myself, Py_buffer *view, int flags) return 0; } -static PyBufferProcs PyCData_as_buffer = { - PyCData_NewGetBuffer, - NULL, -}; - /* * CData objects are mutable, so they cannot be hashable! */ @@ -2909,50 +2904,24 @@ static PyMethodDef PyCData_methods[] = { { NULL, NULL }, }; -static _HackyHeapType PyCData_Type = { - .ht = { - .ht_type = { - PyVarObject_HEAD_INIT(NULL, 0) - "_ctypes._CData", - sizeof(CDataObject), /* tp_basicsize */ - 0, /* tp_itemsize */ - PyCData_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 */ - PyCData_nohash, /* tp_hash */ - 0, /* tp_call */ - 0, /* tp_str */ - 0, /* tp_getattro */ - 0, /* tp_setattro */ - &PyCData_as_buffer, /* tp_as_buffer */ - Py_TPFLAGS_DEFAULT | Py_TPFLAGS_BASETYPE, /* tp_flags */ - PyDoc_STR("XXX to be provided"), /* tp_doc */ - (traverseproc)PyCData_traverse, /* tp_traverse */ - (inquiry)PyCData_clear, /* tp_clear */ - 0, /* tp_richcompare */ - 0, /* tp_weaklistoffset */ - 0, /* tp_iter */ - 0, /* tp_iternext */ - PyCData_methods, /* tp_methods */ - PyCData_members, /* 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 */ - } - } +static PyType_Slot pycdata_slots[] = { + {Py_tp_dealloc, PyCData_dealloc}, + {Py_tp_hash, PyCData_nohash}, + {Py_tp_doc, PyDoc_STR("XXX to be provided")}, + {Py_tp_traverse, PyCData_traverse}, + {Py_tp_clear, PyCData_clear}, + {Py_tp_methods, PyCData_methods}, + {Py_tp_members, PyCData_members}, + {Py_bf_getbuffer, PyCData_NewGetBuffer}, + {0, NULL}, +}; + +PyType_Spec pycdata_spec = { + .name = "_ctypes._CData", + .basicsize = sizeof(CDataObject), + .flags = (Py_TPFLAGS_DEFAULT | Py_TPFLAGS_BASETYPE | Py_TPFLAGS_HAVE_GC | + Py_TPFLAGS_IMMUTABLETYPE | Py_TPFLAGS_DISALLOW_INSTANTIATION), + .slots = pycdata_slots, }; static int @@ -5614,7 +5583,7 @@ _ctypes_add_types(PyObject *mod) */ CREATE_TYPE(st->PyCArg_Type, &carg_spec, NULL, NULL); CREATE_TYPE(st->PyCThunk_Type, &cthunk_spec, NULL, NULL); - TYPE_READY(st->PyCData_Type); + CREATE_TYPE(st->PyCData_Type, &pycdata_spec, NULL, NULL); // Common Metaclass CREATE_TYPE(st->PyCType_Type, &pyctype_type_spec, From 0be46a147e51e379c15bd143949eaf80256d9731 Mon Sep 17 00:00:00 2001 From: Petr Viktorin Date: Fri, 23 Feb 2024 15:53:17 +0100 Subject: [PATCH 39/59] Visit/decref type in PyCData visit/dealloc --- Modules/_ctypes/_ctypes.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/Modules/_ctypes/_ctypes.c b/Modules/_ctypes/_ctypes.c index df7f7268700c59..ce74a6f141615e 100644 --- a/Modules/_ctypes/_ctypes.c +++ b/Modules/_ctypes/_ctypes.c @@ -2712,7 +2712,7 @@ PyCData_traverse(CDataObject *self, visitproc visit, void *arg) Py_VISIT((PyObject *)self->b_base); PyTypeObject *type = Py_TYPE(self); if (type->tp_flags & Py_TPFLAGS_HEAPTYPE) { - //TODO Py_VISIT(type); + Py_VISIT(type); } return 0; } @@ -2739,7 +2739,7 @@ PyCData_dealloc(PyObject *self) PyCData_clear((CDataObject *)self); type->tp_free(self); if (type->tp_flags & Py_TPFLAGS_HEAPTYPE) { - //TODO Py_DECREF(type); + Py_DECREF(type); } } From 9be352038dd8b45747b965b2a8f11f042ce71122 Mon Sep 17 00:00:00 2001 From: Petr Viktorin Date: Fri, 23 Feb 2024 15:57:46 +0100 Subject: [PATCH 40/59] Remove _HackyHeapType --- Modules/_ctypes/_ctypes.c | 29 +---------------------------- 1 file changed, 1 insertion(+), 28 deletions(-) diff --git a/Modules/_ctypes/_ctypes.c b/Modules/_ctypes/_ctypes.c index ce74a6f141615e..65f131ac632658 100644 --- a/Modules/_ctypes/_ctypes.c +++ b/Modules/_ctypes/_ctypes.c @@ -126,34 +126,7 @@ bytes(cdata) #include "pycore_long.h" // _PyLong_GetZero() -/* Allow defining static types that have space for StgInfo at the end. - * These should all be converted to actual heap types that - * honor tp_basicsize of their metaclass! - */ -typedef struct { - PyHeapTypeObject ht; - StgInfo info; -} _HackyHeapType; - -static _HackyHeapType PyCData_Type; -static _HackyHeapType PyCData_Type; -static _HackyHeapType Union_Type; -static _HackyHeapType Struct_Type; -static _HackyHeapType Simple_Type; -static _HackyHeapType PyCArray_Type; -static _HackyHeapType PyCPointer_Type; -static _HackyHeapType PyCFuncPtr_Type; - - -ctypes_state global_state = { - .PyCData_Type = (PyTypeObject*)&PyCData_Type, - .Struct_Type = (PyTypeObject*)&Struct_Type, - .Union_Type = (PyTypeObject*)&Union_Type, - .PyCArray_Type = (PyTypeObject*)&PyCArray_Type, - .Simple_Type = (PyTypeObject*)&Simple_Type, - .PyCPointer_Type = (PyTypeObject*)&PyCPointer_Type, - .PyCFuncPtr_Type = (PyTypeObject*)&PyCFuncPtr_Type, -}; +ctypes_state global_state = {0}; PyObject *PyExc_ArgError = NULL; From e523c2f0debcfb1c26611053091a6a91fdc2e5b2 Mon Sep 17 00:00:00 2001 From: Petr Viktorin Date: Fri, 1 Mar 2024 13:29:15 +0100 Subject: [PATCH 41/59] Decref attrdicts --- Modules/_ctypes/_ctypes.c | 28 ++++++++++++++++++++++------ 1 file changed, 22 insertions(+), 6 deletions(-) diff --git a/Modules/_ctypes/_ctypes.c b/Modules/_ctypes/_ctypes.c index 65f131ac632658..bed525eaea8c27 100644 --- a/Modules/_ctypes/_ctypes.c +++ b/Modules/_ctypes/_ctypes.c @@ -614,15 +614,18 @@ StructUnionType_init(PyObject *self, PyObject *args, PyObject *kwds, int isStruc /* keep this for bw compatibility */ int r = PyDict_Contains(attrdict, &_Py_ID(_abstract_)); if (r > 0) { + Py_DECREF(attrdict); return 0; } if (r < 0) { + Py_DECREF(attrdict); return -1; } ctypes_state *st = GLOBAL_STATE(); StgInfo *info = PyStgInfo_Init(st, (PyTypeObject *)self); if (!info) { + Py_DECREF(attrdict); return -1; } if (!isStruct) { @@ -631,6 +634,7 @@ StructUnionType_init(PyObject *self, PyObject *args, PyObject *kwds, int isStruc info->format = _ctypes_alloc_format_string(NULL, "B"); if (info->format == NULL) { + Py_DECREF(attrdict); return -1; } @@ -639,6 +643,7 @@ StructUnionType_init(PyObject *self, PyObject *args, PyObject *kwds, int isStruc if (PyDict_GetItemRef((PyObject *)attrdict, &_Py_ID(_fields_), &fields) < 0) { return -1; } + Py_CLEAR(attrdict); if (fields) { if (PyObject_SetAttr(self, &_Py_ID(_fields_), fields) < 0) { Py_DECREF(fields); @@ -1199,20 +1204,27 @@ PyCPointerType_set_type(PyTypeObject *self, PyObject *type) ctypes_state *st = GLOBAL_STATE(); StgInfo *info; if (PyStgInfo_FromType(st, (PyObject *)self, &info) < 0) { + Py_DECREF(attrdict); return NULL; } if (!info) { PyErr_SetString(PyExc_TypeError, "abstract class"); + Py_DECREF(attrdict); return NULL; } - if (-1 == PyCPointerType_SetProto(info, type)) + if (-1 == PyCPointerType_SetProto(info, type)) { + Py_DECREF(attrdict); return NULL; + } - if (-1 == PyDict_SetItem(attrdict, &_Py_ID(_type_), type)) + if (-1 == PyDict_SetItem(attrdict, &_Py_ID(_type_), type)) { + Py_DECREF(attrdict); return NULL; + } + Py_DECREF(attrdict); Py_RETURN_NONE; } @@ -2526,6 +2538,7 @@ PyCFuncPtrType_init(PyObject *self, PyObject *args, PyObject *kwds) ctypes_state *st = GLOBAL_STATE(); StgInfo *stginfo = PyStgInfo_Init(st, (PyTypeObject *)self); if (!stginfo) { + Py_DECREF(attrdict); return -1; } @@ -2539,14 +2552,17 @@ PyCFuncPtrType_init(PyObject *self, PyObject *args, PyObject *kwds) */ stginfo->format = _ctypes_alloc_format_string(NULL, "X{}"); if (stginfo->format == NULL) { + Py_DECREF(attrdict); return -1; } stginfo->flags |= TYPEFLAG_ISPOINTER; if (-1 == make_funcptrtype_dict(attrdict, stginfo)) { + Py_DECREF(attrdict); return -1; } + Py_CLEAR(attrdict); return 0; } @@ -4330,7 +4346,6 @@ _init_pos_args(PyObject *self, PyTypeObject *type, PyObject *args, PyObject *kwds, Py_ssize_t index) { - PyObject *attrdict; PyObject *fields; Py_ssize_t i; @@ -4347,16 +4362,17 @@ _init_pos_args(PyObject *self, PyTypeObject *type, return -1; } - attrdict = PyType_GetDict(type); - assert(attrdict); - StgInfo *info; if (PyStgInfo_FromType(st, (PyObject *)type, &info) < 0) { return -1; } assert(info); + PyObject *attrdict = PyType_GetDict(type); + assert(attrdict); + fields = PyDict_GetItemWithError((PyObject *)attrdict, &_Py_ID(_fields_)); + Py_CLEAR(attrdict); if (fields == NULL) { if (PyErr_Occurred()) { return -1; From fb70e43c92fddd0fa34aa540b69a8c9726191bf6 Mon Sep 17 00:00:00 2001 From: Petr Viktorin Date: Tue, 5 Mar 2024 10:11:03 +0100 Subject: [PATCH 42/59] Delegate CType_Type deallocation to PyType_Type.tp_dealloc --- Modules/_ctypes/_ctypes.c | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/Modules/_ctypes/_ctypes.c b/Modules/_ctypes/_ctypes.c index bed525eaea8c27..bbc177e0795d60 100644 --- a/Modules/_ctypes/_ctypes.c +++ b/Modules/_ctypes/_ctypes.c @@ -488,9 +488,8 @@ CType_Type_dealloc(PyObject *self) } PyTypeObject *tp = Py_TYPE(self); - PyObject_GC_UnTrack(self); (void)CType_Type_clear(self); - tp->tp_free(self); + PyType_Type.tp_dealloc(self); Py_DECREF(tp); } From cc59deaddaa4657d708bed1c2c79db9ac334aa03 Mon Sep 17 00:00:00 2001 From: neonene <53406459+neonene@users.noreply.github.com> Date: Tue, 5 Mar 2024 20:31:40 +0100 Subject: [PATCH 43/59] Fix Windows-specific crashes --- Modules/_ctypes/callbacks.c | 6 +++++- Modules/_ctypes/callproc.c | 4 ++++ 2 files changed, 9 insertions(+), 1 deletion(-) diff --git a/Modules/_ctypes/callbacks.c b/Modules/_ctypes/callbacks.c index 55c1d0f5fda88e..072c71ad9e75d0 100644 --- a/Modules/_ctypes/callbacks.c +++ b/Modules/_ctypes/callbacks.c @@ -112,7 +112,11 @@ static void TryAddRef(PyObject *cnv, CDataObject *obj) { IUnknown *punk; - int r = PyObject_GetAttrString((PyObject *)cnv, "_needs_com_addref_"); + PyObject *attrdict = _PyType_GetDict((PyTypeObject *)cnv); + if (!attrdict) { + return; + } + int r = PyDict_Contains(attrdict, &_Py_ID(_needs_com_addref_)); if (r <= 0) { if (r < 0) { PrintError("getting _needs_com_addref_"); diff --git a/Modules/_ctypes/callproc.c b/Modules/_ctypes/callproc.c index 11e6d1a5aa27a2..6ebbb64d61b07a 100644 --- a/Modules/_ctypes/callproc.c +++ b/Modules/_ctypes/callproc.c @@ -785,6 +785,10 @@ int can_return_struct_as_sint64(size_t s) // returns NULL with exception set on error ffi_type *_ctypes_get_ffi_type(PyObject *obj) { + if (obj == NULL) { + return &ffi_type_sint; + } + ctypes_state *st = GLOBAL_STATE(); StgInfo *info; if (PyStgInfo_FromType(st, obj, &info) < 0) { From 9bd9eb1b9e63f98912fc2d58c4e54e9e8ff4ecba Mon Sep 17 00:00:00 2001 From: Petr Viktorin Date: Wed, 6 Mar 2024 15:08:39 +0100 Subject: [PATCH 44/59] Update comments --- Modules/_ctypes/ctypes.h | 80 ++++++++++++++++++++++----------------- Modules/_ctypes/stgdict.c | 17 +++------ 2 files changed, 51 insertions(+), 46 deletions(-) diff --git a/Modules/_ctypes/ctypes.h b/Modules/_ctypes/ctypes.h index 558f4f6384fe4b..702cf944ba1241 100644 --- a/Modules/_ctypes/ctypes.h +++ b/Modules/_ctypes/ctypes.h @@ -222,44 +222,19 @@ typedef struct { int anonymous; } CFieldObject; -typedef struct { - int initialized; - Py_ssize_t size; /* number of bytes */ - Py_ssize_t align; /* alignment requirements */ - Py_ssize_t length; /* number of fields */ - ffi_type ffi_type_pointer; - PyObject *proto; /* Only for Pointer/ArrayObject */ - SETFUNC setfunc; /* Only for simple objects */ - GETFUNC getfunc; /* Only for simple objects */ - PARAMFUNC paramfunc; - - /* Following fields only used by PyCFuncPtrType_Type instances */ - PyObject *argtypes; /* tuple of CDataObjects */ - PyObject *converters; /* tuple([t.from_param for t in argtypes]) */ - PyObject *restype; /* CDataObject or NULL */ - PyObject *checker; - int flags; /* calling convention and such */ +/**************************************************************** + StgInfo - /* pep3118 fields, pointers need PyMem_Free */ - char *format; - int ndim; - Py_ssize_t *shape; -/* Py_ssize_t *strides; */ /* unused in ctypes */ -/* Py_ssize_t *suboffsets; */ /* unused in ctypes */ -} StgInfo; + Since Python 3.13, ctypes-specific type information is stored in the + corresponding type object, in a `StgInfo` struct accessed by the helpers + below. + Before that, each type's `tp_dict` was set to a dict *subclass* that included + the fields that are now in StgInfo. The mechanism was called "StgDict"; a few + references to that name might remain. -// Get a PyCTypeDataObject. These Return -1 on error, 0 if "not found", 1 on OK. -// from a type: -extern int PyStgInfo_FromType(ctypes_state *state, PyObject *obj, StgInfo **result); -// from an instance: -extern int PyStgInfo_FromObject(ctypes_state *state, PyObject *obj, StgInfo **result); -// from either a type or an instance: -extern int PyStgInfo_FromAny(ctypes_state *state, PyObject *obj, StgInfo **result); -// Initialize StgInfo on a newly created type -extern StgInfo *PyStgInfo_Init(ctypes_state *state, PyTypeObject *type); + **************************************************************** -/**************************************************************** StgInfo fields setfunc and getfunc is only set for simple data types, it is copied from the @@ -299,6 +274,43 @@ extern StgInfo *PyStgInfo_Init(ctypes_state *state, PyTypeObject *type); *****************************************************************/ +typedef struct { + int initialized; + Py_ssize_t size; /* number of bytes */ + Py_ssize_t align; /* alignment requirements */ + Py_ssize_t length; /* number of fields */ + ffi_type ffi_type_pointer; + PyObject *proto; /* Only for Pointer/ArrayObject */ + SETFUNC setfunc; /* Only for simple objects */ + GETFUNC getfunc; /* Only for simple objects */ + PARAMFUNC paramfunc; + + /* Following fields only used by PyCFuncPtrType_Type instances */ + PyObject *argtypes; /* tuple of CDataObjects */ + PyObject *converters; /* tuple([t.from_param for t in argtypes]) */ + PyObject *restype; /* CDataObject or NULL */ + PyObject *checker; + int flags; /* calling convention and such */ + + /* pep3118 fields, pointers need PyMem_Free */ + char *format; + int ndim; + Py_ssize_t *shape; +/* Py_ssize_t *strides; */ /* unused in ctypes */ +/* Py_ssize_t *suboffsets; */ /* unused in ctypes */ +} StgInfo; + +// Get a PyCTypeDataObject. These Return -1 on error, 0 if "not found", 1 on OK. +// from a type: +extern int PyStgInfo_FromType(ctypes_state *state, PyObject *obj, StgInfo **result); +// from an instance: +extern int PyStgInfo_FromObject(ctypes_state *state, PyObject *obj, StgInfo **result); +// from either a type or an instance: +extern int PyStgInfo_FromAny(ctypes_state *state, PyObject *obj, StgInfo **result); + +// Initialize StgInfo on a newly created type +extern StgInfo *PyStgInfo_Init(ctypes_state *state, PyTypeObject *type); + extern int PyCStgInfo_clone(StgInfo *dst_info, StgInfo *src_info); typedef int(* PPROC)(void); diff --git a/Modules/_ctypes/stgdict.c b/Modules/_ctypes/stgdict.c index 0b8c074c4d5325..e6f7fac24579d5 100644 --- a/Modules/_ctypes/stgdict.c +++ b/Modules/_ctypes/stgdict.c @@ -16,9 +16,11 @@ #endif #include "ctypes.h" +/* This file relates to StgInfo -- type-specific information for ctypes. + * See ctypes.h for details. + */ - -// Get a PyCTypeDataObject. These Return -1 on error, 0 if "not found", 1 on OK. +// Get a PyCTypeDataObject. These return -1 on error, 0 if "not found", 1 on OK. static int _stginfo_from_type(ctypes_state *state, PyTypeObject *type, StgInfo **result) { @@ -56,7 +58,7 @@ PyStgInfo_FromAny(ctypes_state *state, PyObject *obj, StgInfo **result) { return _stginfo_from_type(state, Py_TYPE(obj), result); } -// Initialize StgInfo on a newly created +// Initialize StgInfo on a newly created type StgInfo * PyStgInfo_Init(ctypes_state *state, PyTypeObject *type) { @@ -77,15 +79,6 @@ PyStgInfo_Init(ctypes_state *state, PyTypeObject *type) return info; } - - -/******************************************************************/ -/* - StdDict - a dictionary subclass, containing additional C accessible fields - - XXX blabla more -*/ - int PyCStgInfo_clone(StgInfo *dst_info, StgInfo *src_info) { From 946a0b18a87d577103a2f89eb36bd06b8bd558be Mon Sep 17 00:00:00 2001 From: Petr Viktorin Date: Wed, 6 Mar 2024 15:56:04 +0100 Subject: [PATCH 45/59] Add tests for metaclass details --- Lib/test/test_ctypes/test_arrays.py | 16 ++++++++++++++++ Lib/test/test_ctypes/test_funcptr.py | 6 ++++++ Lib/test/test_ctypes/test_pointers.py | 5 +++++ Lib/test/test_ctypes/test_simplesubclasses.py | 15 +++++++++++++++ Lib/test/test_ctypes/test_structures.py | 17 +++++++++++++++++ Lib/test/test_ctypes/test_unions.py | 19 ++++++++++++++++++- 6 files changed, 77 insertions(+), 1 deletion(-) diff --git a/Lib/test/test_ctypes/test_arrays.py b/Lib/test/test_ctypes/test_arrays.py index 774316e227ff73..3568cf97f40b50 100644 --- a/Lib/test/test_ctypes/test_arrays.py +++ b/Lib/test/test_ctypes/test_arrays.py @@ -37,6 +37,22 @@ def test_type_flags(self): self.assertTrue(cls.__flags__ & Py_TPFLAGS_IMMUTABLETYPE) self.assertFalse(cls.__flags__ & Py_TPFLAGS_DISALLOW_INSTANTIATION) + def test_metaclass_details(self): + # Abstract classes (whose metaclass __init__ was not called) can't be + # instantiated directly + NewArray = PyCArrayType.__new__(PyCArrayType, 'NewArray', (Array,), {}) + for cls in Array, NewArray: + with self.subTest(cls=cls): + with self.assertRaisesRegex(TypeError, "abstract class"): + obj = cls() + + # Cannot call the metaclass __init__ more than once + class T(Array): + _type_ = c_int + _length_ = 13 + with self.assertRaisesRegex(SystemError, "already initialized"): + PyCArrayType.__init__(T, 'ptr', (), {}) + def test_simple(self): # create classes holding simple numeric types, and check # various properties. diff --git a/Lib/test/test_ctypes/test_funcptr.py b/Lib/test/test_ctypes/test_funcptr.py index 0eed39484fb39e..03cfddea6ea61a 100644 --- a/Lib/test/test_ctypes/test_funcptr.py +++ b/Lib/test/test_ctypes/test_funcptr.py @@ -29,6 +29,12 @@ def test_type_flags(self): self.assertTrue(_CFuncPtr.__flags__ & Py_TPFLAGS_IMMUTABLETYPE) self.assertFalse(_CFuncPtr.__flags__ & Py_TPFLAGS_DISALLOW_INSTANTIATION) + def test_metaclass_details(self): + # Cannot call the metaclass __init__ more than once + CdeclCallback = CFUNCTYPE(c_int, c_int, c_int) + with self.assertRaisesRegex(SystemError, "already initialized"): + PyCFuncPtrType.__init__(CdeclCallback, 'ptr', (), {}) + def test_basic(self): X = WINFUNCTYPE(c_int, c_int, c_int) diff --git a/Lib/test/test_ctypes/test_pointers.py b/Lib/test/test_ctypes/test_pointers.py index 8cf2114c282cab..3a5f3660dbbe23 100644 --- a/Lib/test/test_ctypes/test_pointers.py +++ b/Lib/test/test_ctypes/test_pointers.py @@ -33,6 +33,11 @@ def test_type_flags(self): self.assertTrue(_Pointer.__flags__ & Py_TPFLAGS_IMMUTABLETYPE) self.assertFalse(_Pointer.__flags__ & Py_TPFLAGS_DISALLOW_INSTANTIATION) + def test_metaclass_details(self): + # Cannot call the metaclass __init__ more than once + with self.assertRaisesRegex(SystemError, "already initialized"): + PyCPointerType.__init__(POINTER(c_byte), 'ptr', (), {}) + def test_pointer_crash(self): class A(POINTER(c_ulong)): diff --git a/Lib/test/test_ctypes/test_simplesubclasses.py b/Lib/test/test_ctypes/test_simplesubclasses.py index c96798e67f23f7..02f1746fc1c8ee 100644 --- a/Lib/test/test_ctypes/test_simplesubclasses.py +++ b/Lib/test/test_ctypes/test_simplesubclasses.py @@ -26,6 +26,21 @@ def test_type_flags(self): self.assertTrue(_SimpleCData.__flags__ & Py_TPFLAGS_IMMUTABLETYPE) self.assertFalse(_SimpleCData.__flags__ & Py_TPFLAGS_DISALLOW_INSTANTIATION) + def test_metaclass_details(self): + # Abstract classes (whose metaclass __init__ was not called) can't be + # instantiated directly + NewT = PyCSimpleType.__new__(PyCSimpleType, 'NewT', (_SimpleCData,), {}) + for cls in _SimpleCData, NewT: + with self.subTest(cls=cls): + with self.assertRaisesRegex(TypeError, "abstract class"): + obj = cls() + + # Cannot call the metaclass __init__ more than once + class T(_SimpleCData): + _type_ = "i" + with self.assertRaisesRegex(SystemError, "already initialized"): + PyCSimpleType.__init__(T, 'ptr', (), {}) + def test_compare(self): self.assertEqual(MyInt(3), MyInt(3)) self.assertNotEqual(MyInt(42), MyInt(43)) diff --git a/Lib/test/test_ctypes/test_structures.py b/Lib/test/test_ctypes/test_structures.py index cbae38d0d4acc4..8d83ce4f281b16 100644 --- a/Lib/test/test_ctypes/test_structures.py +++ b/Lib/test/test_ctypes/test_structures.py @@ -85,6 +85,23 @@ def test_type_flags(self): self.assertTrue(Structure.__flags__ & Py_TPFLAGS_IMMUTABLETYPE) self.assertFalse(Structure.__flags__ & Py_TPFLAGS_DISALLOW_INSTANTIATION) + def test_metaclass_details(self): + # Abstract classes (whose metaclass __init__ was not called) can't be + # instantiated directly + NewStructure = PyCStructType.__new__(PyCStructType, 'NewStructure', + (Structure,), {}) + for cls in Structure, NewStructure: + with self.subTest(cls=cls): + with self.assertRaisesRegex(TypeError, "abstract class"): + obj = cls() + + # Cannot call the metaclass __init__ more than once + class T(Structure): + _fields_ = [("x", c_char), + ("y", c_char)] + with self.assertRaisesRegex(SystemError, "already initialized"): + PyCStructType.__init__(T, 'ptr', (), {}) + def test_simple_structs(self): for code, tp in self.formats.items(): class X(Structure): diff --git a/Lib/test/test_ctypes/test_unions.py b/Lib/test/test_ctypes/test_unions.py index cf5344bdf19165..e2dff0f22a9213 100644 --- a/Lib/test/test_ctypes/test_unions.py +++ b/Lib/test/test_ctypes/test_unions.py @@ -1,5 +1,5 @@ import unittest -from ctypes import Union +from ctypes import Union, c_char from ._support import (_CData, UnionType, Py_TPFLAGS_DISALLOW_INSTANTIATION, Py_TPFLAGS_IMMUTABLETYPE) @@ -16,3 +16,20 @@ def test_type_flags(self): with self.subTest(cls=Union): self.assertTrue(Union.__flags__ & Py_TPFLAGS_IMMUTABLETYPE) self.assertFalse(Union.__flags__ & Py_TPFLAGS_DISALLOW_INSTANTIATION) + + def test_metaclass_details(self): + # Abstract classes (whose metaclass __init__ was not called) can't be + # instantiated directly + NewUnion = UnionType.__new__(UnionType, 'NewUnion', + (Union,), {}) + for cls in Union, NewUnion: + with self.subTest(cls=cls): + with self.assertRaisesRegex(TypeError, "abstract class"): + obj = cls() + + # Cannot call the metaclass __init__ more than once + class T(Union): + _fields_ = [("x", c_char), + ("y", c_char)] + with self.assertRaisesRegex(SystemError, "already initialized"): + UnionType.__init__(T, 'ptr', (), {}) From 970b41d2f9e038666cb6ec78d866fbeb85958afc Mon Sep 17 00:00:00 2001 From: Petr Viktorin Date: Thu, 7 Mar 2024 11:02:14 +0100 Subject: [PATCH 46/59] Fix getting the module state --- Modules/_ctypes/_ctypes.c | 1 + Modules/_ctypes/callbacks.c | 2 -- 2 files changed, 1 insertion(+), 2 deletions(-) diff --git a/Modules/_ctypes/_ctypes.c b/Modules/_ctypes/_ctypes.c index bbc177e0795d60..e2f38cd04dc357 100644 --- a/Modules/_ctypes/_ctypes.c +++ b/Modules/_ctypes/_ctypes.c @@ -3386,6 +3386,7 @@ static PPROC FindAddress(void *handle, const char *name, PyObject *type) return NULL; } + ctypes_state *st = GLOBAL_STATE(); StgInfo *info; if (PyStgInfo_FromType(st, (PyObject *)type, &info) < 0) { return -1; diff --git a/Modules/_ctypes/callbacks.c b/Modules/_ctypes/callbacks.c index 072c71ad9e75d0..b2be8659494275 100644 --- a/Modules/_ctypes/callbacks.c +++ b/Modules/_ctypes/callbacks.c @@ -355,10 +355,8 @@ CThunkObject *_ctypes_alloc_callback(PyObject *callable, if (p == NULL) return NULL; -#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) { From 2a450ad2209c3d3da5ee7bf1fd345fe6ac257a59 Mon Sep 17 00:00:00 2001 From: Petr Viktorin Date: Thu, 7 Mar 2024 11:11:44 +0100 Subject: [PATCH 47/59] Add a blurb, even though this *should* not affect usage --- .../Library/2024-03-07-11-10-27.gh-issue-114314.iEhAMH.rst | 3 +++ 1 file changed, 3 insertions(+) create mode 100644 Misc/NEWS.d/next/Library/2024-03-07-11-10-27.gh-issue-114314.iEhAMH.rst diff --git a/Misc/NEWS.d/next/Library/2024-03-07-11-10-27.gh-issue-114314.iEhAMH.rst b/Misc/NEWS.d/next/Library/2024-03-07-11-10-27.gh-issue-114314.iEhAMH.rst new file mode 100644 index 00000000000000..c241d966f9087d --- /dev/null +++ b/Misc/NEWS.d/next/Library/2024-03-07-11-10-27.gh-issue-114314.iEhAMH.rst @@ -0,0 +1,3 @@ +In :mod:`ctypes`, ctype data is now stored in type objects directly rather +than in a dict subclass. This is an internal change that should not affect +usage. From 9878809dad7a87abb4611d26c96f0324c398a371 Mon Sep 17 00:00:00 2001 From: Petr Viktorin Date: Mon, 11 Mar 2024 15:26:18 +0100 Subject: [PATCH 48/59] Apply suggestions from code review Co-authored-by: neonene <53406459+neonene@users.noreply.github.com> Co-authored-by: Erlend E. Aasland --- Modules/_ctypes/_ctypes.c | 24 +++++++++--------------- Modules/_ctypes/callbacks.c | 3 ++- Modules/_ctypes/stgdict.c | 7 ++++++- 3 files changed, 17 insertions(+), 17 deletions(-) diff --git a/Modules/_ctypes/_ctypes.c b/Modules/_ctypes/_ctypes.c index e2f38cd04dc357..89ed82dd671ed1 100644 --- a/Modules/_ctypes/_ctypes.c +++ b/Modules/_ctypes/_ctypes.c @@ -640,6 +640,7 @@ StructUnionType_init(PyObject *self, PyObject *args, PyObject *kwds, int isStruc info->paramfunc = StructUnionType_paramfunc; if (PyDict_GetItemRef((PyObject *)attrdict, &_Py_ID(_fields_), &fields) < 0) { + Py_DECREF(attrdict); return -1; } Py_CLEAR(attrdict); @@ -662,7 +663,7 @@ StructUnionType_init(PyObject *self, PyObject *args, PyObject *kwds, int isStruc } /* copy base info */ - if (-1 == PyCStgInfo_clone(info, baseinfo)) { + if (PyCStgInfo_clone(info, baseinfo) < 0) { return -1; } info->flags &= ~DICTFLAG_FINAL; /* clear the 'final' flag in the subclass info */ @@ -2561,7 +2562,7 @@ PyCFuncPtrType_init(PyObject *self, PyObject *args, PyObject *kwds) return -1; } - Py_CLEAR(attrdict); + Py_DECREF(attrdict); return 0; } @@ -2699,9 +2700,7 @@ PyCData_traverse(CDataObject *self, visitproc visit, void *arg) Py_VISIT(self->b_objects); Py_VISIT((PyObject *)self->b_base); PyTypeObject *type = Py_TYPE(self); - if (type->tp_flags & Py_TPFLAGS_HEAPTYPE) { - Py_VISIT(type); - } + Py_VISIT(type); return 0; } @@ -2721,14 +2720,10 @@ static void PyCData_dealloc(PyObject *self) { PyTypeObject *type = Py_TYPE(self); - if (type->tp_flags & Py_TPFLAGS_HAVE_GC) { - PyObject_GC_UnTrack(self); - } + PyObject_GC_UnTrack(self); PyCData_clear((CDataObject *)self); type->tp_free(self); - if (type->tp_flags & Py_TPFLAGS_HEAPTYPE) { - Py_DECREF(type); - } + Py_DECREF(type); } static PyMemberDef PyCData_members[] = { @@ -3389,7 +3384,7 @@ static PPROC FindAddress(void *handle, const char *name, PyObject *type) ctypes_state *st = GLOBAL_STATE(); StgInfo *info; if (PyStgInfo_FromType(st, (PyObject *)type, &info) < 0) { - return -1; + return NULL; } /* It should not happen that info is NULL, but better be safe */ if (info==NULL || info->flags & FUNCFLAG_CDECL) @@ -4967,8 +4962,8 @@ static PyType_Slot pycsimple_slots[] = { PyType_Spec pycsimple_spec = { .name = "_ctypes._SimpleCData", - .flags = (Py_TPFLAGS_DEFAULT | Py_TPFLAGS_BASETYPE - | Py_TPFLAGS_IMMUTABLETYPE), + .flags = (Py_TPFLAGS_DEFAULT | Py_TPFLAGS_BASETYPE | + Py_TPFLAGS_IMMUTABLETYPE), .slots = pycsimple_slots, }; @@ -5312,7 +5307,6 @@ Pointer_bool(CDataObject *self) static PyType_Slot pycpointer_slots[] = { {Py_tp_doc, PyDoc_STR("XXX to be provided")}, - {Py_tp_getset, }, {Py_tp_getset, Pointer_getsets}, {Py_tp_init, Pointer_init}, {Py_tp_new, Pointer_new}, diff --git a/Modules/_ctypes/callbacks.c b/Modules/_ctypes/callbacks.c index b2be8659494275..08d068e47ee2bf 100644 --- a/Modules/_ctypes/callbacks.c +++ b/Modules/_ctypes/callbacks.c @@ -176,7 +176,8 @@ static void _CallPythonObject(void *mem, 1 resp. 4, but these parameters are pushed as sizeof(int) bytes. BTW, the same problem occurs when they are pushed as parameters */ - } else if (info) { + } + else if (info) { /* Hm, shouldn't we use PyCData_AtAddress() or something like that instead? */ CDataObject *obj = (CDataObject *)_PyObject_CallNoArgs(cnv); if (!obj) { diff --git a/Modules/_ctypes/stgdict.c b/Modules/_ctypes/stgdict.c index e6f7fac24579d5..8d21bd724f5f66 100644 --- a/Modules/_ctypes/stgdict.c +++ b/Modules/_ctypes/stgdict.c @@ -30,6 +30,7 @@ _stginfo_from_type(ctypes_state *state, PyTypeObject *type, StgInfo **result) return 0; } StgInfo *info = PyObject_GetTypeData((PyObject *)type, state->PyCType_Type); + assert(info != NULL); if (!info->initialized) { // StgInfo is not initialized. This happens in abstract classes. return 0; @@ -51,7 +52,8 @@ PyStgInfo_FromObject(ctypes_state *state, PyObject *obj, StgInfo **result) } // from either a type or an instance: int -PyStgInfo_FromAny(ctypes_state *state, PyObject *obj, StgInfo **result) { +PyStgInfo_FromAny(ctypes_state *state, PyObject *obj, StgInfo **result) +{ if (PyType_Check(obj)) { return _stginfo_from_type(state, (PyTypeObject *)obj, result); } @@ -784,6 +786,7 @@ PyCStructUnionType_update_stginfo(PyObject *type, PyObject *fields, int isStruct StgInfo *einfo; if (PyStgInfo_FromType(st, info->proto, &einfo) < 0) { + Py_DECREF(pair); return -1; } if (einfo == NULL) { @@ -891,6 +894,8 @@ PyCStructUnionType_update_stginfo(PyObject *type, PyObject *fields, int isStruct Py_ssize_t length = info->length; StgInfo *einfo; if (PyStgInfo_FromType(st, info->proto, &einfo) < 0) { + Py_DECREF(pair); + PyMem_Free(type_block); return -1; } if (einfo == NULL) { From b2ba89a1dc45c0fca29cf8104718bce44353fad5 Mon Sep 17 00:00:00 2001 From: Petr Viktorin Date: Mon, 11 Mar 2024 14:32:46 +0100 Subject: [PATCH 49/59] Inline PyStgInfo_From* & PyStgInfo_Init for performance --- Modules/_ctypes/ctypes.h | 89 ++++++++++++++++++++++++++++++++------- Modules/_ctypes/stgdict.c | 61 --------------------------- 2 files changed, 73 insertions(+), 77 deletions(-) diff --git a/Modules/_ctypes/ctypes.h b/Modules/_ctypes/ctypes.h index 702cf944ba1241..d7d725a4fdf669 100644 --- a/Modules/_ctypes/ctypes.h +++ b/Modules/_ctypes/ctypes.h @@ -232,6 +232,8 @@ typedef struct { the fields that are now in StgInfo. The mechanism was called "StgDict"; a few references to that name might remain. + Functions for accessing StgInfo are `static inline` for performance; + see later in this file. **************************************************************** @@ -300,17 +302,6 @@ typedef struct { /* Py_ssize_t *suboffsets; */ /* unused in ctypes */ } StgInfo; -// Get a PyCTypeDataObject. These Return -1 on error, 0 if "not found", 1 on OK. -// from a type: -extern int PyStgInfo_FromType(ctypes_state *state, PyObject *obj, StgInfo **result); -// from an instance: -extern int PyStgInfo_FromObject(ctypes_state *state, PyObject *obj, StgInfo **result); -// from either a type or an instance: -extern int PyStgInfo_FromAny(ctypes_state *state, PyObject *obj, StgInfo **result); - -// Initialize StgInfo on a newly created type -extern StgInfo *PyStgInfo_Init(ctypes_state *state, PyTypeObject *type); - extern int PyCStgInfo_clone(StgInfo *dst_info, StgInfo *src_info); typedef int(* PPROC)(void); @@ -413,8 +404,74 @@ void *Py_ffi_closure_alloc(size_t size, void** codeloc); #define Py_ffi_closure_alloc ffi_closure_alloc #endif -/* - Local Variables: - compile-command: "python setup.py -q build install --home ~" - End: -*/ + +/**************************************************************** + * Accessing StgInfo -- these are inlined for performance reasons. + */ + +// `PyStgInfo_From**` functions get a PyCTypeDataObject. +// These return -1 on error, 0 if "not found", 1 on OK. +// (Currently, these do not return -1 in practice. This might change +// in the future.) + +// +// Common helper: +static inline int +_stginfo_from_type(ctypes_state *state, PyTypeObject *type, StgInfo **result) +{ + *result = NULL; + if (!PyObject_IsInstance((PyObject *)type, (PyObject *)state->PyCType_Type)) { + // not a ctypes class. + return 0; + } + StgInfo *info = PyObject_GetTypeData((PyObject *)type, state->PyCType_Type); + assert(info != NULL); + if (!info->initialized) { + // StgInfo is not initialized. This happens in abstract classes. + return 0; + } + *result = info; + return 1; +} +// from a type: +static inline int +PyStgInfo_FromType(ctypes_state *state, PyObject *type, StgInfo **result) +{ + return _stginfo_from_type(state, (PyTypeObject *)type, result); +} +// from an instance: +static inline int +PyStgInfo_FromObject(ctypes_state *state, PyObject *obj, StgInfo **result) +{ + return _stginfo_from_type(state, Py_TYPE(obj), result); +} +// from either a type or an instance: +static inline int +PyStgInfo_FromAny(ctypes_state *state, PyObject *obj, StgInfo **result) +{ + if (PyType_Check(obj)) { + return _stginfo_from_type(state, (PyTypeObject *)obj, result); + } + return _stginfo_from_type(state, Py_TYPE(obj), result); +} + +// Initialize StgInfo on a newly created type +static inline StgInfo * +PyStgInfo_Init(ctypes_state *state, PyTypeObject *type) +{ + if (!PyObject_IsInstance((PyObject *)type, (PyObject *)state->PyCType_Type)) { + PyErr_Format(PyExc_SystemError, + "'%s' is not a ctypes class.", + type->tp_name); + return NULL; + } + StgInfo *info = PyObject_GetTypeData((PyObject *)type, state->PyCType_Type); + if (info->initialized) { + PyErr_Format(PyExc_SystemError, + "StgInfo of '%s' is already initialized.", + type->tp_name); + return NULL; + } + info->initialized = 1; + return info; +} diff --git a/Modules/_ctypes/stgdict.c b/Modules/_ctypes/stgdict.c index 8d21bd724f5f66..8666ded5c2b3f2 100644 --- a/Modules/_ctypes/stgdict.c +++ b/Modules/_ctypes/stgdict.c @@ -20,67 +20,6 @@ * See ctypes.h for details. */ -// Get a PyCTypeDataObject. These return -1 on error, 0 if "not found", 1 on OK. -static int -_stginfo_from_type(ctypes_state *state, PyTypeObject *type, StgInfo **result) -{ - *result = NULL; - if (!PyObject_IsInstance((PyObject *)type, (PyObject *)state->PyCType_Type)) { - // not a ctypes class. - return 0; - } - StgInfo *info = PyObject_GetTypeData((PyObject *)type, state->PyCType_Type); - assert(info != NULL); - if (!info->initialized) { - // StgInfo is not initialized. This happens in abstract classes. - return 0; - } - *result = info; - return 1; -} -// from a type: -int -PyStgInfo_FromType(ctypes_state *state, PyObject *type, StgInfo **result) -{ - return _stginfo_from_type(state, (PyTypeObject *)type, result); -} -// from an instance: -int -PyStgInfo_FromObject(ctypes_state *state, PyObject *obj, StgInfo **result) -{ - return _stginfo_from_type(state, Py_TYPE(obj), result); -} -// from either a type or an instance: -int -PyStgInfo_FromAny(ctypes_state *state, PyObject *obj, StgInfo **result) -{ - if (PyType_Check(obj)) { - return _stginfo_from_type(state, (PyTypeObject *)obj, result); - } - return _stginfo_from_type(state, Py_TYPE(obj), result); -} - -// Initialize StgInfo on a newly created type -StgInfo * -PyStgInfo_Init(ctypes_state *state, PyTypeObject *type) -{ - if (!PyObject_IsInstance((PyObject *)type, (PyObject *)state->PyCType_Type)) { - PyErr_Format(PyExc_SystemError, - "'%s' is not a ctypes class.", - type->tp_name); - return NULL; - } - StgInfo *info = PyObject_GetTypeData((PyObject *)type, state->PyCType_Type); - if (info->initialized) { - PyErr_Format(PyExc_SystemError, - "StgInfo of '%s' is already initialized.", - type->tp_name); - return NULL; - } - info->initialized = 1; - return info; -} - int PyCStgInfo_clone(StgInfo *dst_info, StgInfo *src_info) { From 605c30c6b479169765ae61edea52ec30feaf8ab6 Mon Sep 17 00:00:00 2001 From: Petr Viktorin Date: Mon, 11 Mar 2024 14:43:28 +0100 Subject: [PATCH 50/59] Make CType_Type's clear & dealloc safer Check that the state and st->PyCType_Type is set. If they aren't, this will leak rather than crash. --- Modules/_ctypes/_ctypes.c | 54 +++++++++++++++++++++++++-------------- 1 file changed, 35 insertions(+), 19 deletions(-) diff --git a/Modules/_ctypes/_ctypes.c b/Modules/_ctypes/_ctypes.c index 89ed82dd671ed1..c37adcd214e02a 100644 --- a/Modules/_ctypes/_ctypes.c +++ b/Modules/_ctypes/_ctypes.c @@ -455,20 +455,29 @@ CType_Type_traverse(PyObject *self, visitproc visit, void *arg) return 0; } +static void +_ctype_clear_stginfo(StgInfo *info) +{ + assert(info); + Py_CLEAR(info->proto); + Py_CLEAR(info->argtypes); + Py_CLEAR(info->converters); + Py_CLEAR(info->restype); + Py_CLEAR(info->checker); +} + static int CType_Type_clear(PyObject *self) { ctypes_state *st = GLOBAL_STATE(); - StgInfo *info; - if (PyStgInfo_FromType(st, self, &info) < 0) { - return -1; - } - if (info) { - Py_CLEAR(info->proto); - Py_CLEAR(info->argtypes); - Py_CLEAR(info->converters); - Py_CLEAR(info->restype); - Py_CLEAR(info->checker); + if (st && st->PyCType_Type) { + StgInfo *info; + if (PyStgInfo_FromType(st, self, &info) < 0) { + return -1; + } + if (info) { + _ctype_clear_stginfo(info); + } } return 0; } @@ -477,18 +486,25 @@ static void CType_Type_dealloc(PyObject *self) { ctypes_state *st = GLOBAL_STATE(); - StgInfo *info; - if (PyStgInfo_FromType(st, self, &info) < 0) { - // ignore exception - } - if (info) { - PyMem_Free(info->ffi_type_pointer.elements); - PyMem_Free(info->format); - PyMem_Free(info->shape); + + if (st && st->PyCType_Type) { + StgInfo *info; + if (PyStgInfo_FromType(st, self, &info) < 0) { + PyErr_WriteUnraisable(self); + PyErr_Clear(); + } + if (info) { + PyMem_Free(info->ffi_type_pointer.elements); + info->ffi_type_pointer.elements = NULL; + PyMem_Free(info->format); + info->format = NULL; + PyMem_Free(info->shape); + info->shape = NULL; + _ctype_clear_stginfo(info); + } } PyTypeObject *tp = Py_TYPE(self); - (void)CType_Type_clear(self); PyType_Type.tp_dealloc(self); Py_DECREF(tp); } From 41dea1b6881e27aeefcc206f14bff500ef2a23f9 Mon Sep 17 00:00:00 2001 From: Petr Viktorin Date: Mon, 11 Mar 2024 15:12:53 +0100 Subject: [PATCH 51/59] Remove self decrefs in error cases in init functions --- Modules/_ctypes/_ctypes.c | 9 --------- 1 file changed, 9 deletions(-) diff --git a/Modules/_ctypes/_ctypes.c b/Modules/_ctypes/_ctypes.c index c37adcd214e02a..cf359b596247f3 100644 --- a/Modules/_ctypes/_ctypes.c +++ b/Modules/_ctypes/_ctypes.c @@ -2067,15 +2067,12 @@ PyCSimpleType_paramfunc(CDataObject *self) static int PyCSimpleType_init(PyObject *self, PyObject *args, PyObject *kwds) { - PyTypeObject *result; PyObject *proto; const char *proto_str; Py_ssize_t proto_len; PyMethodDef *ml; struct fielddesc *fmt; - result = (PyTypeObject *)self; - if (PyObject_GetOptionalAttr(self, &_Py_ID(_type_), &proto) < 0) { return -1; } @@ -2133,7 +2130,6 @@ PyCSimpleType_init(PyObject *self, PyObject *args, PyObject *kwds) stginfo->format = _ctypes_alloc_format_string_for_type(proto_str[0], 0); #endif if (stginfo->format == NULL) { - Py_DECREF(self); Py_DECREF(proto); return -1; } @@ -2182,7 +2178,6 @@ PyCSimpleType_init(PyObject *self, PyObject *args, PyObject *kwds) int x; meth = PyDescr_NewClassMethod((PyTypeObject*)self, ml); if (!meth) { - Py_DECREF(self); return -1; } x = PyDict_SetItemString(((PyTypeObject*)self)->tp_dict, @@ -2190,7 +2185,6 @@ PyCSimpleType_init(PyObject *self, PyObject *args, PyObject *kwds) meth); Py_DECREF(meth); if (x == -1) { - Py_DECREF(self); return -1; } } @@ -2204,12 +2198,10 @@ PyCSimpleType_init(PyObject *self, PyObject *args, PyObject *kwds) PyObject *swapped = CreateSwappedType(type, args, kwds, proto, fmt); if (swapped == NULL) { - Py_DECREF(self); return -1; } StgInfo *sw_info; if (PyStgInfo_FromType(st, swapped, &sw_info) < 0) { - Py_DECREF(self); return -1; } assert(sw_info); @@ -2230,7 +2222,6 @@ PyCSimpleType_init(PyObject *self, PyObject *args, PyObject *kwds) #endif Py_DECREF(swapped); if (PyErr_Occurred()) { - Py_DECREF(result); return -1; } }; From be0888ff4ea190d16dfe2f112f745a866f3eba93 Mon Sep 17 00:00:00 2001 From: Petr Viktorin Date: Mon, 11 Mar 2024 15:21:26 +0100 Subject: [PATCH 52/59] Add `static` to PyType_Spec --- Modules/_ctypes/_ctypes.c | 18 +++++++++--------- 1 file changed, 9 insertions(+), 9 deletions(-) diff --git a/Modules/_ctypes/_ctypes.c b/Modules/_ctypes/_ctypes.c index cf359b596247f3..21c5fdc277c264 100644 --- a/Modules/_ctypes/_ctypes.c +++ b/Modules/_ctypes/_ctypes.c @@ -1062,7 +1062,7 @@ static PyType_Slot pycstruct_type_slots[] = { {0, NULL}, }; -PyType_Spec pycstruct_type_spec = { +static PyType_Spec pycstruct_type_spec = { .name = "_ctypes.PyCStructType", .flags = (Py_TPFLAGS_DEFAULT | Py_TPFLAGS_BASETYPE | Py_TPFLAGS_HAVE_GC | Py_TPFLAGS_IMMUTABLETYPE), @@ -2327,7 +2327,7 @@ static PyType_Slot pycsimple_type_slots[] = { {0, NULL}, }; -PyType_Spec pycsimple_type_spec = { +static PyType_Spec pycsimple_type_spec = { .name = "_ctypes.PyCSimpleType", .flags = (Py_TPFLAGS_DEFAULT | Py_TPFLAGS_BASETYPE | Py_TPFLAGS_HAVE_GC | Py_TPFLAGS_IMMUTABLETYPE), @@ -2906,7 +2906,7 @@ static PyType_Slot pycdata_slots[] = { {0, NULL}, }; -PyType_Spec pycdata_spec = { +static PyType_Spec pycdata_spec = { .name = "_ctypes._CData", .basicsize = sizeof(CDataObject), .flags = (Py_TPFLAGS_DEFAULT | Py_TPFLAGS_BASETYPE | Py_TPFLAGS_HAVE_GC | @@ -4323,7 +4323,7 @@ static PyType_Slot pycfuncptr_slots[] = { {0, NULL}, }; -PyType_Spec pycfuncptr_spec = { +static PyType_Spec pycfuncptr_spec = { .name = "_ctypes.CFuncPtr", .basicsize = sizeof(PyCFuncPtrObject), .flags = (Py_TPFLAGS_DEFAULT | Py_TPFLAGS_BASETYPE | Py_TPFLAGS_HAVE_GC | @@ -4461,7 +4461,7 @@ static PyType_Slot pycstruct_slots[] = { {0, NULL}, }; -PyType_Spec pycstruct_spec = { +static PyType_Spec pycstruct_spec = { .name = "_ctypes.Structure", .flags = (Py_TPFLAGS_DEFAULT | Py_TPFLAGS_BASETYPE | Py_TPFLAGS_IMMUTABLETYPE), @@ -4476,7 +4476,7 @@ static PyType_Slot pycunion_slots[] = { {0, NULL}, }; -PyType_Spec pycunion_spec = { +static PyType_Spec pycunion_spec = { .name = "_ctypes.Union", .flags = (Py_TPFLAGS_DEFAULT | Py_TPFLAGS_BASETYPE | Py_TPFLAGS_IMMUTABLETYPE), @@ -4778,7 +4778,7 @@ static PyType_Slot pycarray_slots[] = { {0, NULL}, }; -PyType_Spec pycarray_spec = { +static PyType_Spec pycarray_spec = { .name = "_ctypes.Array", .flags = (Py_TPFLAGS_DEFAULT | Py_TPFLAGS_BASETYPE | Py_TPFLAGS_IMMUTABLETYPE), @@ -4967,7 +4967,7 @@ static PyType_Slot pycsimple_slots[] = { {0, NULL}, }; -PyType_Spec pycsimple_spec = { +static PyType_Spec pycsimple_spec = { .name = "_ctypes._SimpleCData", .flags = (Py_TPFLAGS_DEFAULT | Py_TPFLAGS_BASETYPE | Py_TPFLAGS_IMMUTABLETYPE), @@ -5325,7 +5325,7 @@ static PyType_Slot pycpointer_slots[] = { {0, NULL}, }; -PyType_Spec pycpointer_spec = { +static PyType_Spec pycpointer_spec = { .name = "_ctypes._Pointer", .flags = (Py_TPFLAGS_DEFAULT | Py_TPFLAGS_BASETYPE | Py_TPFLAGS_IMMUTABLETYPE), From ac6eb386b50d836e63dd978404e6ffea027051d8 Mon Sep 17 00:00:00 2001 From: Petr Viktorin Date: Mon, 11 Mar 2024 15:35:34 +0100 Subject: [PATCH 53/59] Remove redundant comment --- Modules/_ctypes/_ctypes.c | 1 - 1 file changed, 1 deletion(-) diff --git a/Modules/_ctypes/_ctypes.c b/Modules/_ctypes/_ctypes.c index 21c5fdc277c264..b74a7a22b236b0 100644 --- a/Modules/_ctypes/_ctypes.c +++ b/Modules/_ctypes/_ctypes.c @@ -4963,7 +4963,6 @@ static PyType_Slot pycsimple_slots[] = { {Py_tp_new, GenericPyCData_new}, {Py_bf_getbuffer, PyCData_NewGetBuffer}, {Py_nb_bool, Simple_bool}, - // traverse, dealloc, clear Py_TPFLAGS_HAVE_GC are inherited {0, NULL}, }; From 33be37f8ce37f9d8215bf4fe873ee25e16ba141f Mon Sep 17 00:00:00 2001 From: Petr Viktorin Date: Mon, 11 Mar 2024 15:44:32 +0100 Subject: [PATCH 54/59] Apply suggestions from code review Co-authored-by: Erlend E. Aasland --- 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 b74a7a22b236b0..14c99f4469cba0 100644 --- a/Modules/_ctypes/_ctypes.c +++ b/Modules/_ctypes/_ctypes.c @@ -510,7 +510,8 @@ CType_Type_dealloc(PyObject *self) } static PyObject * -CType_Type_sizeof(PyObject *self) { +CType_Type_sizeof(PyObject *self) +{ Py_ssize_t size = Py_TYPE(self)->tp_basicsize; size += Py_TYPE(self)->tp_itemsize * Py_SIZE(self); From 0b9d0a79de6e47e5112b6c187ff25dbcd6fcabb4 Mon Sep 17 00:00:00 2001 From: Petr Viktorin Date: Tue, 12 Mar 2024 12:50:16 +0100 Subject: [PATCH 55/59] Fix comment Co-authored-by: neonene <53406459+neonene@users.noreply.github.com> --- Modules/_ctypes/_ctypes.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Modules/_ctypes/_ctypes.c b/Modules/_ctypes/_ctypes.c index 14c99f4469cba0..f6922c6ade0af0 100644 --- a/Modules/_ctypes/_ctypes.c +++ b/Modules/_ctypes/_ctypes.c @@ -944,7 +944,7 @@ CDataType_from_param(PyObject *type, PyObject *value) return NULL; } /* If we got a PyCArgObject, we must check if the object packed in it - is an instance of the type's dict->proto */ + is an instance of the type's info->proto */ if(info && ob) { res = PyObject_IsInstance(ob, info->proto); if (res == -1) From f02bad6c64ba6601416e49b7fc39fba855ba8691 Mon Sep 17 00:00:00 2001 From: Petr Viktorin Date: Tue, 12 Mar 2024 12:44:08 +0100 Subject: [PATCH 56/59] PyErr_WriteUnraisable clears the exception --- Modules/_ctypes/_ctypes.c | 1 - 1 file changed, 1 deletion(-) diff --git a/Modules/_ctypes/_ctypes.c b/Modules/_ctypes/_ctypes.c index f6922c6ade0af0..8db25d93ab7d4a 100644 --- a/Modules/_ctypes/_ctypes.c +++ b/Modules/_ctypes/_ctypes.c @@ -491,7 +491,6 @@ CType_Type_dealloc(PyObject *self) StgInfo *info; if (PyStgInfo_FromType(st, self, &info) < 0) { PyErr_WriteUnraisable(self); - PyErr_Clear(); } if (info) { PyMem_Free(info->ffi_type_pointer.elements); From 16faac7836d9e2e296816f9a636f7e86f90979cb Mon Sep 17 00:00:00 2001 From: Petr Viktorin Date: Wed, 20 Mar 2024 13:56:24 +0100 Subject: [PATCH 57/59] Call tp_init for argument validation Co-authored-by: neonene <53406459+neonene@users.noreply.github.com> --- Modules/_ctypes/_ctypes.c | 3 +++ 1 file changed, 3 insertions(+) diff --git a/Modules/_ctypes/_ctypes.c b/Modules/_ctypes/_ctypes.c index 8db25d93ab7d4a..af094a0fb59e27 100644 --- a/Modules/_ctypes/_ctypes.c +++ b/Modules/_ctypes/_ctypes.c @@ -2073,6 +2073,9 @@ PyCSimpleType_init(PyObject *self, PyObject *args, PyObject *kwds) PyMethodDef *ml; struct fielddesc *fmt; + if (PyType_Type.tp_init(self, args, kwds) < 0) { + return -1; + } if (PyObject_GetOptionalAttr(self, &_Py_ID(_type_), &proto) < 0) { return -1; } From 2461e502a172eedbe7d07de3f5d75c5eb0278f7f Mon Sep 17 00:00:00 2001 From: neonene <53406459+neonene@users.noreply.github.com> Date: Wed, 20 Mar 2024 13:57:01 +0100 Subject: [PATCH 58/59] Add test for swapped type creation --- Lib/test/test_ctypes/test_simplesubclasses.py | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/Lib/test/test_ctypes/test_simplesubclasses.py b/Lib/test/test_ctypes/test_simplesubclasses.py index 02f1746fc1c8ee..c4a53b1a06ad87 100644 --- a/Lib/test/test_ctypes/test_simplesubclasses.py +++ b/Lib/test/test_ctypes/test_simplesubclasses.py @@ -41,6 +41,12 @@ class T(_SimpleCData): with self.assertRaisesRegex(SystemError, "already initialized"): PyCSimpleType.__init__(T, 'ptr', (), {}) + def test_swapped_type_creation(self): + cls = PyCSimpleType.__new__(PyCSimpleType, '', (), {'_type_': 'i'}) + PyCSimpleType.__init__(cls) + self.assertEqual(cls.__ctype_le__.__dict__.get('_type_'), 'i') + self.assertEqual(cls.__ctype_be__.__dict__.get('_type_'), 'i') + def test_compare(self): self.assertEqual(MyInt(3), MyInt(3)) self.assertNotEqual(MyInt(42), MyInt(43)) From de054afd4dafa2b353fb13985b63b64d8aff556f Mon Sep 17 00:00:00 2001 From: Petr Viktorin Date: Wed, 20 Mar 2024 14:13:03 +0100 Subject: [PATCH 59/59] Fix test_swapped_type_creation --- Lib/test/test_ctypes/test_simplesubclasses.py | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/Lib/test/test_ctypes/test_simplesubclasses.py b/Lib/test/test_ctypes/test_simplesubclasses.py index c4a53b1a06ad87..4e4bef3690f66a 100644 --- a/Lib/test/test_ctypes/test_simplesubclasses.py +++ b/Lib/test/test_ctypes/test_simplesubclasses.py @@ -43,7 +43,9 @@ class T(_SimpleCData): def test_swapped_type_creation(self): cls = PyCSimpleType.__new__(PyCSimpleType, '', (), {'_type_': 'i'}) - PyCSimpleType.__init__(cls) + with self.assertRaises(TypeError): + PyCSimpleType.__init__(cls) + PyCSimpleType.__init__(cls, '', (), {'_type_': 'i'}) self.assertEqual(cls.__ctype_le__.__dict__.get('_type_'), 'i') self.assertEqual(cls.__ctype_be__.__dict__.get('_type_'), 'i')