gh-114314: ctypes: remove stgdict and switch to heap types (GH-116458)

Before this change, ctypes classes used a custom dict subclass, `StgDict`,
as their `tp_dict`. This acts like a regular dict but also includes extra information
about the type.

This replaces stgdict by `StgInfo`, a C struct on the type, accessed by
`PyObject_GetTypeData()` (PEP-697).
All usage of `StgDict` (mainly variables named `stgdict`, `dict`, `edict` etc.) is
converted to `StgInfo` (named `stginfo`, `info`, `einfo`, etc.).
Where the dict is actually used for class attributes (as a regular PyDict), it's now
called `attrdict`.

This change -- not overriding `tp_dict` -- is made to make me comfortable with
the next part of this PR: moving the initialization logic from `tp_new` to `tp_init`.

The `StgInfo` is set up in `__init__` of each class, with a guard that prevents
calling `__init__` more than once. Note that abstract classes (like `Array` or
`Structure`) are created using `PyType_FromMetaclass` and do not have
`__init__` called.
Previously, this was done in `__new__`, which also wasn't called for abstract
classes.
Since `__init__` can be called from Python code or skipped, there is a tested
guard to ensure `StgInfo` is initialized exactly once before it's used.

Co-authored-by: neonene <53406459+neonene@users.noreply.github.com>
Co-authored-by: Erlend E. Aasland <erlend.aasland@protonmail.com>
This commit is contained in:
Petr Viktorin 2024-03-20 17:33:08 +01:00 committed by GitHub
parent 44fbab43d8
commit dcaf33a41d
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
15 changed files with 1500 additions and 1415 deletions

View file

@ -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.

View file

@ -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))

View file

@ -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)

View file

@ -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)):

View file

@ -26,6 +26,29 @@ 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_swapped_type_creation(self):
cls = PyCSimpleType.__new__(PyCSimpleType, '', (), {'_type_': 'i'})
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')
def test_compare(self):
self.assertEqual(MyInt(3), MyInt(3))
self.assertNotEqual(MyInt(42), MyInt(43))

View file

@ -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'):

View file

@ -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):
@ -507,8 +524,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 +540,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 +555,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)]

View file

@ -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', (), {})

View file

@ -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.

File diff suppressed because it is too large Load diff

View file

@ -109,10 +109,14 @@ 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_));
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_");
@ -154,22 +158,26 @@ 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);
if (dict && dict->getfunc && !_ctypes_simple_instance(cnv)) {
PyObject *v = dict->getfunc(*pArgs, dict->size);
StgInfo *info;
if (PyStgInfo_FromType(st, cnv, &info) < 0) {
goto Done;
}
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) {
@ -181,10 +189,10 @@ 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);
TryAddRef(cnv, obj);
#endif
} else {
PyErr_SetString(PyExc_TypeError,
@ -348,10 +356,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) {
@ -372,14 +378,18 @@ CThunkObject *_ctypes_alloc_callback(PyObject *callable,
p->setfunc = NULL;
p->ffi_restype = &ffi_type_void;
} else {
StgDictObject *dict = PyType_stgdict(restype);
if (dict == NULL || dict->setfunc == NULL) {
StgInfo *info;
if (PyStgInfo_FromType(st, restype, &info) < 0) {
goto error;
}
if (info == NULL || info->setfunc == NULL) {
PyErr_SetString(PyExc_TypeError,
"invalid result type for callback function");
goto error;
}
p->setfunc = dict->setfunc;
p->ffi_restype = &dict->ffi_type_pointer;
p->setfunc = info->setfunc;
p->ffi_restype = &info->ffi_type_pointer;
}
cc = FFI_DEFAULT_ABI;

View file

@ -664,15 +664,20 @@ 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);
if (dict) {
StgInfo *info;
int result = PyStgInfo_FromObject(st, obj, &info);
if (result < 0) {
return -1;
}
if (info) {
assert(info);
PyCArgObject *carg;
assert(dict->paramfunc);
/* If it has an stgdict, it is a CDataObject */
carg = dict->paramfunc((CDataObject *)obj);
assert(info->paramfunc);
/* If it has an stginfo, it is a CDataObject */
carg = info->paramfunc((CDataObject *)obj);
if (carg == NULL)
return -1;
pa->ffi_type = carg->pffi_type;
@ -681,7 +686,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;
@ -778,26 +782,34 @@ 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)
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;
}
@ -983,7 +995,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)
@ -993,17 +1004,22 @@ static PyObject *GetResult(PyObject *restype, void *result, PyObject *checker)
Py_RETURN_NONE;
}
dict = PyType_stgdict(restype);
if (dict == NULL)
ctypes_state *st = GLOBAL_STATE();
StgInfo *info;
if (PyStgInfo_FromType(st, restype, &info) < 0) {
return NULL;
}
if (info == NULL) {
return PyObject_CallFunction(restype, "i", *(int *)result);
}
if (dict->getfunc && !_ctypes_simple_instance(restype)) {
retval = dict->getfunc(result, dict->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
@ -1240,6 +1256,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)));
@ -1683,13 +1702,16 @@ PyDoc_STRVAR(sizeof_doc,
static PyObject *
sizeof_func(PyObject *self, PyObject *obj)
{
StgDictObject *dict;
dict = PyType_stgdict(obj);
if (dict) {
return PyLong_FromSsize_t(dict->size);
}
ctypes_state *st = GLOBAL_STATE();
StgInfo *info;
if (PyStgInfo_FromType(st, obj, &info) < 0) {
return NULL;
}
if (info) {
return PyLong_FromSsize_t(info->size);
}
if (CDataObject_Check(st, obj)) {
return PyLong_FromSsize_t(((CDataObject *)obj)->b_size);
}
@ -1706,16 +1728,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;
@ -1824,7 +1844,6 @@ static PyObject *
resize(PyObject *self, PyObject *args)
{
CDataObject *obj;
StgDictObject *dict;
Py_ssize_t size;
if (!PyArg_ParseTuple(args,
@ -1832,16 +1851,21 @@ resize(PyObject *self, PyObject *args)
&obj, &size))
return NULL;
dict = PyObject_stgdict((PyObject *)obj);
if (dict == NULL) {
ctypes_state *st = GLOBAL_STATE();
StgInfo *info;
int result = PyStgInfo_FromObject(st, (PyObject *)obj, &info);
if (result < 0) {
return NULL;
}
if (info == NULL) {
PyErr_SetString(PyExc_TypeError,
"expected ctypes instance");
return NULL;
}
if (size < dict->size) {
if (size < info->size) {
PyErr_Format(PyExc_ValueError,
"minimum size is %zd",
dict->size);
info->size);
return NULL;
}
if (obj->b_needsfree == 0) {
@ -2004,28 +2028,30 @@ 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);
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;
}
shape = PyTuple_New(dict->ndim);
shape = PyTuple_New(info->ndim);
if (shape == NULL)
return NULL;
for (i = 0; i < (int)dict->ndim; ++i)
PyTuple_SET_ITEM(shape, i, PyLong_FromSsize_t(dict->shape[i]));
for (i = 0; i < (int)info->ndim; ++i)
PyTuple_SET_ITEM(shape, i, PyLong_FromSsize_t(info->shape[i]));
if (PyErr_Occurred()) {
Py_DECREF(shape);
return NULL;
}
return Py_BuildValue("siN", dict->format, dict->ndim, shape);
return Py_BuildValue("siN", info->format, info->ndim, shape);
}

View file

@ -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,21 +65,27 @@ 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) {
StgInfo *info;
if (PyStgInfo_FromType(st, desc, &info) < 0) {
Py_DECREF(self);
return NULL;
}
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 */
#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 +93,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 +102,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,29 +110,37 @@ 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
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);
if (!idict) {
StgInfo *ainfo;
if (PyStgInfo_FromType(st, proto, &ainfo) < 0) {
Py_DECREF(self);
return NULL;
}
if (ainfo && ainfo->proto) {
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;
@ -151,9 +164,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;
@ -171,10 +184,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;

View file

@ -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;
@ -59,6 +58,7 @@ typedef struct {
#ifdef MS_WIN32
PyTypeObject *PyComError_Type;
#endif
PyTypeObject *PyCType_Type;
} ctypes_state;
extern ctypes_state global_state;
@ -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,17 +158,12 @@ 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 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);
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)
@ -188,10 +183,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)
@ -231,45 +222,22 @@ typedef struct {
int anonymous;
} CFieldObject;
/* 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
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.
Functions for accessing StgInfo are `static inline` for performance;
see later in this file.
****************************************************************
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
@ -280,11 +248,11 @@ typedef struct {
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
@ -308,13 +276,33 @@ typedef struct {
*****************************************************************/
/* May return NULL, but does not set an exception! */
extern StgDictObject *PyType_stgdict(PyObject *obj);
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;
/* May return NULL, but does not set an exception! */
extern StgDictObject *PyObject_stgdict(PyObject *self);
/* 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 */
extern int PyCStgDict_clone(StgDictObject *src, StgDictObject *dst);
/* 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;
extern int PyCStgInfo_clone(StgInfo *dst_info, StgInfo *src_info);
typedef int(* PPROC)(void);
@ -416,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;
}

View file

@ -16,201 +16,62 @@
#endif
#include "ctypes.h"
/******************************************************************/
/*
StdDict - a dictionary subclass, containing additional C accessible fields
XXX blabla more
*/
/* Seems we need this, otherwise we get problems when calling
* PyDict_SetItem() (ma_lookup is NULL)
/* This file relates to StgInfo -- type-specific information for ctypes.
* See ctypes.h for details.
*/
static int
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;
}
static int
PyCStgDict_clear(StgDictObject *self)
{
Py_CLEAR(self->proto);
Py_CLEAR(self->argtypes);
Py_CLEAR(self->converters);
Py_CLEAR(self->restype);
Py_CLEAR(self->checker);
return 0;
}
static void
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);
}
static PyObject *
PyCStgDict_sizeof(StgDictObject *self, void *unused)
{
Py_ssize_t res;
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);
if (self->ffi_type_pointer.elements)
res += (self->length + 1) * sizeof(ffi_type *);
return PyLong_FromSsize_t(res);
}
int
PyCStgDict_clone(StgDictObject *dst, StgDictObject *src)
PyCStgInfo_clone(StgInfo *dst_info, StgInfo *src_info)
{
char *d, *s;
Py_ssize_t size;
PyCStgDict_clear(dst);
PyMem_Free(dst->ffi_type_pointer.elements);
PyMem_Free(dst->format);
dst->format = NULL;
PyMem_Free(dst->shape);
dst->shape = NULL;
dst->ffi_type_pointer.elements = NULL;
PyMem_Free(dst_info->ffi_type_pointer.elements);
PyMem_Free(dst_info->format);
dst_info->format = NULL;
PyMem_Free(dst_info->shape);
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->proto);
Py_XINCREF(dst->argtypes);
Py_XINCREF(dst->converters);
Py_XINCREF(dst->restype);
Py_XINCREF(dst->checker);
Py_XINCREF(dst_info->proto);
Py_XINCREF(dst_info->argtypes);
Py_XINCREF(dst_info->converters);
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);
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,
sizeof(Py_ssize_t) * src->ndim);
memcpy(dst_info->shape, src_info->shape,
sizeof(Py_ssize_t) * src_info->ndim);
}
if (src->ffi_type_pointer.elements == NULL)
if (src_info->ffi_type_pointer.elements == NULL)
return 0;
size = sizeof(ffi_type *) * (src->length + 1);
dst->ffi_type_pointer.elements = PyMem_Malloc(size);
if (dst->ffi_type_pointer.elements == NULL) {
size = sizeof(ffi_type *) * (src_info->length + 1);
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;
}
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.
@ -372,12 +233,11 @@ _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)
{
StgDictObject *stgdict, *basedict;
Py_ssize_t len, offset, size, align, i;
Py_ssize_t union_size, total_align, aligned_size;
Py_ssize_t field_size = 0;
@ -456,90 +316,97 @@ PyCStructUnionType_update_stgdict(PyObject *type, PyObject *fields, int isStruct
return -1;
}
stgdict = PyType_stgdict(type);
if (!stgdict) {
ctypes_state *st = GLOBAL_STATE();
StgInfo *stginfo;
if (PyStgInfo_FromType(st, type, &stginfo) < 0) {
return -1;
}
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. */
if (stgdict->flags & DICTFLAG_FINAL) {/* is final ? */
if (stginfo->flags & DICTFLAG_FINAL) {/* is final ? */
PyErr_SetString(PyExc_AttributeError,
"_fields_ is final");
return -1;
}
if (stgdict->format) {
PyMem_Free(stgdict->format);
stgdict->format = NULL;
if (stginfo->format) {
PyMem_Free(stginfo->format);
stginfo->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) {
stgdict->flags |= (basedict->flags &
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) {
size = offset = basedict->size;
align = basedict->align;
if (baseinfo) {
size = offset = baseinfo->size;
align = baseinfo->align;
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 *, basedict->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,
sizeof(ffi_type *) * (basedict->length + len + 1));
if (basedict->length > 0) {
memcpy(stgdict->ffi_type_pointer.elements,
basedict->ffi_type_pointer.elements,
sizeof(ffi_type *) * (basedict->length));
memset(stginfo->ffi_type_pointer.elements, 0,
sizeof(ffi_type *) * (baseinfo->length + len + 1));
if (baseinfo->length > 0) {
memcpy(stginfo->ffi_type_pointer.elements,
baseinfo->ffi_type_pointer.elements,
sizeof(ffi_type *) * (baseinfo->length));
}
ffi_ofs = basedict->length;
ffi_ofs = baseinfo->length;
} else {
offset = 0;
size = 0;
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;
}
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;
ctypes_state *st = GLOBAL_STATE();
for (i = 0; i < len; ++i) {
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)) {
@ -551,22 +418,28 @@ PyCStructUnionType_update_stgdict(PyObject *type, PyObject *fields, int isStruct
if (PyCArrayTypeObject_Check(st, desc)) {
arrays_seen = 1;
}
dict = PyType_stgdict(desc);
if (dict == NULL) {
StgInfo *info;
if (PyStgInfo_FromType(st, desc, &info) < 0) {
Py_DECREF(pair);
return -1;
}
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;
}
stgdict->ffi_type_pointer.elements[ffi_ofs + i] = &dict->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 */
stginfo->ffi_type_pointer.elements[ffi_ofs + i] = &info->ffi_type_pointer;
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;
switch(dict->ffi_type_pointer.type) {
stginfo->flags |= TYPEFLAG_HASBITFIELD;
switch(info->ffi_type_pointer.type) {
case FFI_TYPE_UINT8:
case FFI_TYPE_UINT16:
case FFI_TYPE_UINT32:
@ -577,8 +450,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 */
@ -589,7 +462,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);
@ -599,7 +472,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;
@ -629,10 +502,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;
@ -650,17 +523,17 @@ PyCStructUnionType_update_stgdict(PyObject *type, PyObject *fields, int isStruct
}
sprintf(buf, "%s:%s:", fieldfmt, fieldname);
ptr = stgdict->format;
if (dict->shape != NULL) {
stgdict->format = _ctypes_alloc_format_string_with_shape(
dict->ndim, dict->shape, stgdict->format, buf);
ptr = stginfo->format;
if (info->shape != NULL) {
stginfo->format = _ctypes_alloc_format_string_with_shape(
info->ndim, info->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;
@ -704,29 +577,29 @@ 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;
}
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;
stgdict->size = aligned_size;
stgdict->align = total_align;
stgdict->length = ffi_ofs + len;
stginfo->size = aligned_size;
stginfo->align = total_align;
stginfo->length = ffi_ofs + len;
/*
* The value of MAX_STRUCT_SIZE depends on the platform Python is running on.
@ -817,7 +690,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) {
@ -829,25 +701,34 @@ PyCStructUnionType_update_stgdict(PyObject *type, PyObject *fields, int isStruct
Py_DECREF(pair);
return -1;
}
dict = PyType_stgdict(desc);
if (dict == NULL) {
StgInfo *info;
if (PyStgInfo_FromType(st, desc, &info) < 0) {
Py_DECREF(pair);
return -1;
}
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. */
num_ffi_type_pointers++;
}
else {
/* It's an array. */
Py_ssize_t length = dict->length;
StgDictObject *edict;
Py_ssize_t length = info->length;
edict = PyType_stgdict(dict->proto);
if (edict == NULL) {
StgInfo *einfo;
if (PyStgInfo_FromType(st, info->proto, &einfo) < 0) {
Py_DECREF(pair);
return -1;
}
if (einfo == NULL) {
Py_DECREF(pair);
PyErr_Format(PyExc_TypeError,
"second item in _fields_ tuple (index %zd) must be a C type",
@ -895,9 +776,9 @@ 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,
basedict->ffi_type_pointer.elements,
baseinfo->ffi_type_pointer.elements,
ffi_ofs * sizeof(ffi_type *));
}
element_index = ffi_ofs;
@ -906,7 +787,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) {
@ -926,9 +806,16 @@ PyCStructUnionType_update_stgdict(PyObject *type, PyObject *fields, int isStruct
PyMem_Free(type_block);
return -1;
}
dict = PyType_stgdict(desc);
StgInfo *info;
if (PyStgInfo_FromType(st, desc, &info) < 0) {
Py_DECREF(pair);
PyMem_Free(type_block);
return -1;
}
/* Possibly this check could be avoided, but see above comment. */
if (dict == NULL) {
if (info == NULL) {
Py_DECREF(pair);
PyMem_Free(type_block);
PyErr_Format(PyExc_TypeError,
@ -936,17 +823,21 @@ PyCStructUnionType_update_stgdict(PyObject *type, PyObject *fields, int isStruct
i);
return -1;
}
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 = dict->length;
StgDictObject *edict;
edict = PyType_stgdict(dict->proto);
if (edict == NULL) {
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) {
Py_DECREF(pair);
PyMem_Free(type_block);
PyErr_Format(PyExc_TypeError,
@ -955,15 +846,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));
@ -977,19 +868,19 @@ 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
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);
}