| /***************************************************************** |
| This file should be kept compatible with Python 2.3, see PEP 291. |
| *****************************************************************/ |
| |
| #include "Python.h" |
| #include <ffi.h> |
| #ifdef MS_WIN32 |
| #include <windows.h> |
| #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) |
| */ |
| static int |
| StgDict_init(StgDictObject *self, PyObject *args, PyObject *kwds) |
| { |
| if (PyDict_Type.tp_init((PyObject *)self, args, kwds) < 0) |
| return -1; |
| return 0; |
| } |
| |
| static int |
| StgDict_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 |
| StgDict_dealloc(StgDictObject *self) |
| { |
| StgDict_clear(self); |
| PyMem_Free(self->ffi_type_pointer.elements); |
| PyDict_Type.tp_dealloc((PyObject *)self); |
| } |
| |
| int |
| StgDict_clone(StgDictObject *dst, StgDictObject *src) |
| { |
| char *d, *s; |
| int size; |
| |
| StgDict_clear(dst); |
| PyMem_Free(dst->ffi_type_pointer.elements); |
| dst->ffi_type_pointer.elements = NULL; |
| |
| d = (char *)dst; |
| s = (char *)src; |
| memcpy(d + sizeof(PyDictObject), |
| s + sizeof(PyDictObject), |
| sizeof(StgDictObject) - sizeof(PyDictObject)); |
| |
| Py_XINCREF(dst->proto); |
| Py_XINCREF(dst->argtypes); |
| Py_XINCREF(dst->converters); |
| Py_XINCREF(dst->restype); |
| Py_XINCREF(dst->checker); |
| |
| if (src->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) |
| return -1; |
| memcpy(dst->ffi_type_pointer.elements, |
| src->ffi_type_pointer.elements, |
| size); |
| return 0; |
| } |
| |
| PyTypeObject StgDict_Type = { |
| PyObject_HEAD_INIT(NULL) |
| 0, |
| "StgDict", |
| sizeof(StgDictObject), |
| 0, |
| (destructor)StgDict_dealloc, /* tp_dealloc */ |
| 0, /* tp_print */ |
| 0, /* tp_getattr */ |
| 0, /* tp_setattr */ |
| 0, /* tp_compare */ |
| 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 */ |
| 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 */ |
| (initproc)StgDict_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; |
| type = (PyTypeObject *)obj; |
| if (!type->tp_dict || !StgDict_CheckExact(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 = self->ob_type; |
| if (!type->tp_dict || !StgDict_CheckExact(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. |
| */ |
| static int |
| MakeFields(PyObject *type, CFieldObject *descr, |
| Py_ssize_t index, Py_ssize_t offset) |
| { |
| Py_ssize_t i; |
| PyObject *fields; |
| PyObject *fieldlist; |
| |
| fields = PyObject_GetAttrString(descr->proto, "_fields_"); |
| if (fields == NULL) |
| return -1; |
| fieldlist = PySequence_Fast(fields, "_fields_ must be a sequence"); |
| Py_DECREF(fields); |
| if (fieldlist == NULL) |
| return -1; |
| |
| for (i = 0; i < PySequence_Fast_GET_SIZE(fieldlist); ++i) { |
| PyObject *pair = PySequence_Fast_GET_ITEM(fieldlist, i); /* borrowed */ |
| PyObject *fname, *ftype, *bits; |
| CFieldObject *fdescr; |
| CFieldObject *new_descr; |
| /* Convert to PyArg_UnpackTuple... */ |
| if (!PyArg_ParseTuple(pair, "OO|O", &fname, &ftype, &bits)) { |
| Py_DECREF(fieldlist); |
| return -1; |
| } |
| fdescr = (CFieldObject *)PyObject_GetAttr(descr->proto, fname); |
| if (fdescr == NULL) { |
| Py_DECREF(fieldlist); |
| return -1; |
| } |
| if (fdescr->ob_type != &CField_Type) { |
| PyErr_SetString(PyExc_TypeError, "unexpected type"); |
| Py_DECREF(fdescr); |
| Py_DECREF(fieldlist); |
| return -1; |
| } |
| if (fdescr->anonymous) { |
| int rc = MakeFields(type, fdescr, |
| index + fdescr->index, |
| offset + fdescr->offset); |
| Py_DECREF(fdescr); |
| if (rc == -1) { |
| Py_DECREF(fieldlist); |
| return -1; |
| } |
| continue; |
| } |
| new_descr = (CFieldObject *)PyObject_CallObject((PyObject *)&CField_Type, NULL); |
| if (new_descr == NULL) { |
| Py_DECREF(fdescr); |
| Py_DECREF(fieldlist); |
| return -1; |
| } |
| assert(new_descr->ob_type == &CField_Type); |
| new_descr->size = fdescr->size; |
| new_descr->offset = fdescr->offset + offset; |
| new_descr->index = fdescr->index + index; |
| new_descr->proto = fdescr->proto; |
| Py_XINCREF(new_descr->proto); |
| new_descr->getfunc = fdescr->getfunc; |
| new_descr->setfunc = fdescr->setfunc; |
| |
| Py_DECREF(fdescr); |
| |
| if (-1 == PyObject_SetAttr(type, fname, (PyObject *)new_descr)) { |
| Py_DECREF(fieldlist); |
| Py_DECREF(new_descr); |
| return -1; |
| } |
| Py_DECREF(new_descr); |
| } |
| Py_DECREF(fieldlist); |
| return 0; |
| } |
| |
| /* Iterate over the names in the type's _anonymous_ attribute, if present, |
| */ |
| static int |
| MakeAnonFields(PyObject *type) |
| { |
| PyObject *anon; |
| PyObject *anon_names; |
| Py_ssize_t i; |
| |
| anon = PyObject_GetAttrString(type, "_anonymous_"); |
| if (anon == NULL) { |
| PyErr_Clear(); |
| return 0; |
| } |
| anon_names = PySequence_Fast(anon, "_anonymous_ must be a sequence"); |
| Py_DECREF(anon); |
| if (anon_names == NULL) |
| return -1; |
| |
| for (i = 0; i < PySequence_Fast_GET_SIZE(anon_names); ++i) { |
| PyObject *fname = PySequence_Fast_GET_ITEM(anon_names, i); /* borrowed */ |
| CFieldObject *descr = (CFieldObject *)PyObject_GetAttr(type, fname); |
| if (descr == NULL) { |
| Py_DECREF(anon_names); |
| return -1; |
| } |
| assert(descr->ob_type == &CField_Type); |
| descr->anonymous = 1; |
| |
| /* descr is in the field descriptor. */ |
| if (-1 == MakeFields(type, (CFieldObject *)descr, |
| ((CFieldObject *)descr)->index, |
| ((CFieldObject *)descr)->offset)) { |
| Py_DECREF(descr); |
| Py_DECREF(anon_names); |
| return -1; |
| } |
| Py_DECREF(descr); |
| } |
| |
| Py_DECREF(anon_names); |
| return 0; |
| } |
| |
| /* |
| Retrive the (optional) _pack_ attribute from a type, the _fields_ attribute, |
| and create an StgDictObject. Used for Structure and Union subclasses. |
| */ |
| int |
| StructUnionType_update_stgdict(PyObject *type, PyObject *fields, int isStruct) |
| { |
| StgDictObject *stgdict, *basedict; |
| int len, offset, size, align, i; |
| int union_size, total_align; |
| int field_size = 0; |
| int bitofs; |
| PyObject *isPacked; |
| int pack = 0; |
| int ffi_ofs; |
| int big_endian; |
| |
| /* HACK Alert: I cannot be bothered to fix ctypes.com, so there has to |
| be a way to use the old, broken sematics: _fields_ are not extended |
| but replaced in subclasses. |
| |
| XXX Remove this in ctypes 1.0! |
| */ |
| int use_broken_old_ctypes_semantics; |
| |
| if (fields == NULL) |
| return 0; |
| |
| #ifdef WORDS_BIGENDIAN |
| big_endian = PyObject_HasAttrString(type, "_swappedbytes_") ? 0 : 1; |
| #else |
| big_endian = PyObject_HasAttrString(type, "_swappedbytes_") ? 1 : 0; |
| #endif |
| |
| use_broken_old_ctypes_semantics = \ |
| PyObject_HasAttrString(type, "_use_broken_old_ctypes_structure_semantics_"); |
| |
| isPacked = PyObject_GetAttrString(type, "_pack_"); |
| if (isPacked) { |
| pack = PyInt_AsLong(isPacked); |
| if (pack < 0 || PyErr_Occurred()) { |
| Py_XDECREF(isPacked); |
| PyErr_SetString(PyExc_ValueError, |
| "_pack_ must be a non-negative integer"); |
| return -1; |
| } |
| Py_DECREF(isPacked); |
| } else |
| PyErr_Clear(); |
| |
| len = PySequence_Length(fields); |
| if (len == -1) { |
| PyErr_SetString(PyExc_TypeError, |
| "'_fields_' must be a sequence of pairs"); |
| return -1; |
| } |
| |
| stgdict = PyType_stgdict(type); |
| if (!stgdict) |
| return -1; |
| /* If this structure/union is already marked final we cannot assign |
| _fields_ anymore. */ |
| |
| if (stgdict->flags & DICTFLAG_FINAL) {/* is final ? */ |
| PyErr_SetString(PyExc_AttributeError, |
| "_fields_ is final"); |
| return -1; |
| } |
| |
| if (stgdict->ffi_type_pointer.elements) |
| PyMem_Free(stgdict->ffi_type_pointer.elements); |
| |
| basedict = PyType_stgdict((PyObject *)((PyTypeObject *)type)->tp_base); |
| if (basedict && !use_broken_old_ctypes_semantics) { |
| size = offset = basedict->size; |
| align = basedict->align; |
| union_size = 0; |
| total_align = align ? align : 1; |
| stgdict->ffi_type_pointer.type = FFI_TYPE_STRUCT; |
| stgdict->ffi_type_pointer.elements = PyMem_Malloc(sizeof(ffi_type *) * (basedict->length + len + 1)); |
| memset(stgdict->ffi_type_pointer.elements, 0, |
| sizeof(ffi_type *) * (basedict->length + len + 1)); |
| memcpy(stgdict->ffi_type_pointer.elements, |
| basedict->ffi_type_pointer.elements, |
| sizeof(ffi_type *) * (basedict->length)); |
| ffi_ofs = basedict->length; |
| } else { |
| offset = 0; |
| size = 0; |
| align = 0; |
| union_size = 0; |
| total_align = 1; |
| stgdict->ffi_type_pointer.type = FFI_TYPE_STRUCT; |
| stgdict->ffi_type_pointer.elements = PyMem_Malloc(sizeof(ffi_type *) * (len + 1)); |
| memset(stgdict->ffi_type_pointer.elements, 0, |
| sizeof(ffi_type *) * (len + 1)); |
| ffi_ofs = 0; |
| } |
| |
| #define realdict ((PyObject *)&stgdict->dict) |
| 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, "OO|i", &name, &desc, &bitsize)) { |
| PyErr_SetString(PyExc_AttributeError, |
| "'_fields_' must be a sequence of pairs"); |
| Py_XDECREF(pair); |
| return -1; |
| } |
| dict = PyType_stgdict(desc); |
| if (dict == NULL) { |
| Py_DECREF(pair); |
| PyErr_Format(PyExc_TypeError, |
| "second item in _fields_ tuple (index %d) must be a C type", |
| i); |
| return -1; |
| } |
| stgdict->ffi_type_pointer.elements[ffi_ofs + i] = &dict->ffi_type_pointer; |
| dict->flags |= DICTFLAG_FINAL; /* mark field type final */ |
| if (PyTuple_Size(pair) == 3) { /* bits specified */ |
| switch(dict->ffi_type_pointer.type) { |
| case FFI_TYPE_UINT8: |
| case FFI_TYPE_UINT16: |
| case FFI_TYPE_UINT32: |
| case FFI_TYPE_SINT64: |
| case FFI_TYPE_UINT64: |
| break; |
| |
| case FFI_TYPE_SINT8: |
| case FFI_TYPE_SINT16: |
| case FFI_TYPE_SINT32: |
| if (dict->getfunc != getentry("c")->getfunc |
| #ifdef CTYPES_UNICODE |
| && dict->getfunc != getentry("u")->getfunc |
| #endif |
| ) |
| break; |
| /* else fall through */ |
| default: |
| PyErr_Format(PyExc_TypeError, |
| "bit fields not allowed for type %s", |
| ((PyTypeObject *)desc)->tp_name); |
| Py_DECREF(pair); |
| return -1; |
| } |
| if (bitsize <= 0 || bitsize > dict->size * 8) { |
| PyErr_SetString(PyExc_ValueError, |
| "number of bits invalid for bit field"); |
| Py_DECREF(pair); |
| return -1; |
| } |
| } else |
| bitsize = 0; |
| if (isStruct) { |
| prop = CField_FromDesc(desc, i, |
| &field_size, bitsize, &bitofs, |
| &size, &offset, &align, |
| pack, big_endian); |
| } else /* union */ { |
| size = 0; |
| offset = 0; |
| align = 0; |
| prop = CField_FromDesc(desc, i, |
| &field_size, bitsize, &bitofs, |
| &size, &offset, &align, |
| pack, big_endian); |
| union_size = max(size, union_size); |
| } |
| total_align = max(align, total_align); |
| |
| if (!prop) { |
| Py_DECREF(pair); |
| Py_DECREF((PyObject *)stgdict); |
| return -1; |
| } |
| if (-1 == PyDict_SetItem(realdict, name, prop)) { |
| Py_DECREF(prop); |
| Py_DECREF(pair); |
| Py_DECREF((PyObject *)stgdict); |
| return -1; |
| } |
| Py_DECREF(pair); |
| Py_DECREF(prop); |
| } |
| #undef realdict |
| if (!isStruct) |
| size = union_size; |
| |
| /* Adjust the size according to the alignment requirements */ |
| size = ((size + total_align - 1) / total_align) * total_align; |
| |
| stgdict->ffi_type_pointer.alignment = total_align; |
| stgdict->ffi_type_pointer.size = size; |
| |
| stgdict->size = size; |
| stgdict->align = total_align; |
| stgdict->length = len; /* ADD ffi_ofs? */ |
| |
| /* We did check that this flag was NOT set above, it must not |
| have been set until now. */ |
| if (stgdict->flags & DICTFLAG_FINAL) { |
| PyErr_SetString(PyExc_AttributeError, |
| "Structure or union cannot contain itself"); |
| return -1; |
| } |
| stgdict->flags |= DICTFLAG_FINAL; |
| |
| return MakeAnonFields(type); |
| } |