stgdict.c revision d3d23636cbb2fb305f672fa826c3da6a2dc72384
1/*****************************************************************
2  This file should be kept compatible with Python 2.3, see PEP 291.
3 *****************************************************************/
4
5#include "Python.h"
6#include <ffi.h>
7#ifdef MS_WIN32
8#include <windows.h>
9#include <malloc.h>
10#endif
11#include "ctypes.h"
12
13/******************************************************************/
14/*
15  StdDict - a dictionary subclass, containing additional C accessible fields
16
17  XXX blabla more
18*/
19
20/* Seems we need this, otherwise we get problems when calling
21 * PyDict_SetItem() (ma_lookup is NULL)
22 */
23static int
24PyCStgDict_init(StgDictObject *self, PyObject *args, PyObject *kwds)
25{
26    if (PyDict_Type.tp_init((PyObject *)self, args, kwds) < 0)
27        return -1;
28    self->format = NULL;
29    self->ndim = 0;
30    self->shape = NULL;
31    return 0;
32}
33
34static int
35PyCStgDict_clear(StgDictObject *self)
36{
37    Py_CLEAR(self->proto);
38    Py_CLEAR(self->argtypes);
39    Py_CLEAR(self->converters);
40    Py_CLEAR(self->restype);
41    Py_CLEAR(self->checker);
42    return 0;
43}
44
45static void
46PyCStgDict_dealloc(StgDictObject *self)
47{
48    PyCStgDict_clear(self);
49    PyMem_Free(self->format);
50    PyMem_Free(self->shape);
51    PyMem_Free(self->ffi_type_pointer.elements);
52    PyDict_Type.tp_dealloc((PyObject *)self);
53}
54
55int
56PyCStgDict_clone(StgDictObject *dst, StgDictObject *src)
57{
58    char *d, *s;
59    Py_ssize_t size;
60
61    PyCStgDict_clear(dst);
62    PyMem_Free(dst->ffi_type_pointer.elements);
63    PyMem_Free(dst->format);
64    dst->format = NULL;
65    PyMem_Free(dst->shape);
66    dst->shape = NULL;
67    dst->ffi_type_pointer.elements = NULL;
68
69    d = (char *)dst;
70    s = (char *)src;
71    memcpy(d + sizeof(PyDictObject),
72           s + sizeof(PyDictObject),
73           sizeof(StgDictObject) - sizeof(PyDictObject));
74
75    Py_XINCREF(dst->proto);
76    Py_XINCREF(dst->argtypes);
77    Py_XINCREF(dst->converters);
78    Py_XINCREF(dst->restype);
79    Py_XINCREF(dst->checker);
80
81    if (src->format) {
82        dst->format = PyMem_Malloc(strlen(src->format) + 1);
83        if (dst->format == NULL)
84            return -1;
85        strcpy(dst->format, src->format);
86    }
87    if (src->shape) {
88        dst->shape = PyMem_Malloc(sizeof(Py_ssize_t) * src->ndim);
89        if (dst->shape == NULL)
90            return -1;
91        memcpy(dst->shape, src->shape,
92               sizeof(Py_ssize_t) * src->ndim);
93    }
94
95    if (src->ffi_type_pointer.elements == NULL)
96        return 0;
97    size = sizeof(ffi_type *) * (src->length + 1);
98    dst->ffi_type_pointer.elements = PyMem_Malloc(size);
99    if (dst->ffi_type_pointer.elements == NULL) {
100        PyErr_NoMemory();
101        return -1;
102    }
103    memcpy(dst->ffi_type_pointer.elements,
104           src->ffi_type_pointer.elements,
105           size);
106    return 0;
107}
108
109PyTypeObject PyCStgDict_Type = {
110    PyVarObject_HEAD_INIT(NULL, 0)
111    "StgDict",
112    sizeof(StgDictObject),
113    0,
114    (destructor)PyCStgDict_dealloc,             /* tp_dealloc */
115    0,                                          /* tp_print */
116    0,                                          /* tp_getattr */
117    0,                                          /* tp_setattr */
118    0,                                          /* tp_compare */
119    0,                                          /* tp_repr */
120    0,                                          /* tp_as_number */
121    0,                                          /* tp_as_sequence */
122    0,                                          /* tp_as_mapping */
123    0,                                          /* tp_hash */
124    0,                                          /* tp_call */
125    0,                                          /* tp_str */
126    0,                                          /* tp_getattro */
127    0,                                          /* tp_setattro */
128    0,                                          /* tp_as_buffer */
129    Py_TPFLAGS_DEFAULT | Py_TPFLAGS_BASETYPE, /* tp_flags */
130    0,                                          /* tp_doc */
131    0,                                          /* tp_traverse */
132    0,                                          /* tp_clear */
133    0,                                          /* tp_richcompare */
134    0,                                          /* tp_weaklistoffset */
135    0,                                          /* tp_iter */
136    0,                                          /* tp_iternext */
137    0,                                          /* tp_methods */
138    0,                                          /* tp_members */
139    0,                                          /* tp_getset */
140    0,                                          /* tp_base */
141    0,                                          /* tp_dict */
142    0,                                          /* tp_descr_get */
143    0,                                          /* tp_descr_set */
144    0,                                          /* tp_dictoffset */
145    (initproc)PyCStgDict_init,                          /* tp_init */
146    0,                                          /* tp_alloc */
147    0,                                          /* tp_new */
148    0,                                          /* tp_free */
149};
150
151/* May return NULL, but does not set an exception! */
152StgDictObject *
153PyType_stgdict(PyObject *obj)
154{
155    PyTypeObject *type;
156
157    if (!PyType_Check(obj))
158        return NULL;
159    type = (PyTypeObject *)obj;
160    if (!PyType_HasFeature(type, Py_TPFLAGS_HAVE_CLASS))
161        return NULL;
162    if (!type->tp_dict || !PyCStgDict_CheckExact(type->tp_dict))
163        return NULL;
164    return (StgDictObject *)type->tp_dict;
165}
166
167/* May return NULL, but does not set an exception! */
168/*
169  This function should be as fast as possible, so we don't call PyType_stgdict
170  above but inline the code, and avoid the PyType_Check().
171*/
172StgDictObject *
173PyObject_stgdict(PyObject *self)
174{
175    PyTypeObject *type = self->ob_type;
176    if (!PyType_HasFeature(type, Py_TPFLAGS_HAVE_CLASS))
177        return NULL;
178    if (!type->tp_dict || !PyCStgDict_CheckExact(type->tp_dict))
179        return NULL;
180    return (StgDictObject *)type->tp_dict;
181}
182
183/* descr is the descriptor for a field marked as anonymous.  Get all the
184 _fields_ descriptors from descr->proto, create new descriptors with offset
185 and index adjusted, and stuff them into type.
186 */
187static int
188MakeFields(PyObject *type, CFieldObject *descr,
189           Py_ssize_t index, Py_ssize_t offset)
190{
191    Py_ssize_t i;
192    PyObject *fields;
193    PyObject *fieldlist;
194
195    fields = PyObject_GetAttrString(descr->proto, "_fields_");
196    if (fields == NULL)
197        return -1;
198    fieldlist = PySequence_Fast(fields, "_fields_ must be a sequence");
199    Py_DECREF(fields);
200    if (fieldlist == NULL)
201        return -1;
202
203    for (i = 0; i < PySequence_Fast_GET_SIZE(fieldlist); ++i) {
204        PyObject *pair = PySequence_Fast_GET_ITEM(fieldlist, i); /* borrowed */
205        PyObject *fname, *ftype, *bits;
206        CFieldObject *fdescr;
207        CFieldObject *new_descr;
208        /* Convert to PyArg_UnpackTuple... */
209        if (!PyArg_ParseTuple(pair, "OO|O", &fname, &ftype, &bits)) {
210            Py_DECREF(fieldlist);
211            return -1;
212        }
213        fdescr = (CFieldObject *)PyObject_GetAttr(descr->proto, fname);
214        if (fdescr == NULL) {
215            Py_DECREF(fieldlist);
216            return -1;
217        }
218        if (Py_TYPE(fdescr) != &PyCField_Type) {
219            PyErr_SetString(PyExc_TypeError, "unexpected type");
220            Py_DECREF(fdescr);
221            Py_DECREF(fieldlist);
222            return -1;
223        }
224        if (fdescr->anonymous) {
225            int rc = MakeFields(type, fdescr,
226                                index + fdescr->index,
227                                offset + fdescr->offset);
228            Py_DECREF(fdescr);
229            if (rc == -1) {
230                Py_DECREF(fieldlist);
231                return -1;
232            }
233            continue;
234        }
235        new_descr = (CFieldObject *)PyObject_CallObject((PyObject *)&PyCField_Type, NULL);
236        if (new_descr == NULL) {
237            Py_DECREF(fdescr);
238            Py_DECREF(fieldlist);
239            return -1;
240        }
241        assert(Py_TYPE(new_descr) == &PyCField_Type);
242        new_descr->size = fdescr->size;
243        new_descr->offset = fdescr->offset + offset;
244        new_descr->index = fdescr->index + index;
245        new_descr->proto = fdescr->proto;
246        Py_XINCREF(new_descr->proto);
247        new_descr->getfunc = fdescr->getfunc;
248        new_descr->setfunc = fdescr->setfunc;
249
250        Py_DECREF(fdescr);
251
252        if (-1 == PyObject_SetAttr(type, fname, (PyObject *)new_descr)) {
253            Py_DECREF(fieldlist);
254            Py_DECREF(new_descr);
255            return -1;
256        }
257        Py_DECREF(new_descr);
258    }
259    Py_DECREF(fieldlist);
260    return 0;
261}
262
263/* Iterate over the names in the type's _anonymous_ attribute, if present,
264 */
265static int
266MakeAnonFields(PyObject *type)
267{
268    PyObject *anon;
269    PyObject *anon_names;
270    Py_ssize_t i;
271
272    anon = PyObject_GetAttrString(type, "_anonymous_");
273    if (anon == NULL) {
274        PyErr_Clear();
275        return 0;
276    }
277    anon_names = PySequence_Fast(anon, "_anonymous_ must be a sequence");
278    Py_DECREF(anon);
279    if (anon_names == NULL)
280        return -1;
281
282    for (i = 0; i < PySequence_Fast_GET_SIZE(anon_names); ++i) {
283        PyObject *fname = PySequence_Fast_GET_ITEM(anon_names, i); /* borrowed */
284        CFieldObject *descr = (CFieldObject *)PyObject_GetAttr(type, fname);
285        if (descr == NULL) {
286            Py_DECREF(anon_names);
287            return -1;
288        }
289        assert(Py_TYPE(descr) == &PyCField_Type);
290        descr->anonymous = 1;
291
292        /* descr is in the field descriptor. */
293        if (-1 == MakeFields(type, (CFieldObject *)descr,
294                             ((CFieldObject *)descr)->index,
295                             ((CFieldObject *)descr)->offset)) {
296            Py_DECREF(descr);
297            Py_DECREF(anon_names);
298            return -1;
299        }
300        Py_DECREF(descr);
301    }
302
303    Py_DECREF(anon_names);
304    return 0;
305}
306
307/*
308  Retrive the (optional) _pack_ attribute from a type, the _fields_ attribute,
309  and create an StgDictObject.  Used for Structure and Union subclasses.
310*/
311int
312PyCStructUnionType_update_stgdict(PyObject *type, PyObject *fields, int isStruct)
313{
314    StgDictObject *stgdict, *basedict;
315    Py_ssize_t len, offset, size, align, i;
316    Py_ssize_t union_size, total_align;
317    Py_ssize_t field_size = 0;
318    int bitofs;
319    PyObject *isPacked;
320    int pack = 0;
321    Py_ssize_t ffi_ofs;
322    int big_endian;
323
324    /* HACK Alert: I cannot be bothered to fix ctypes.com, so there has to
325       be a way to use the old, broken sematics: _fields_ are not extended
326       but replaced in subclasses.
327
328       XXX Remove this in ctypes 1.0!
329    */
330    int use_broken_old_ctypes_semantics;
331
332    if (fields == NULL)
333        return 0;
334
335#ifdef WORDS_BIGENDIAN
336    big_endian = PyObject_HasAttrString(type, "_swappedbytes_") ? 0 : 1;
337#else
338    big_endian = PyObject_HasAttrString(type, "_swappedbytes_") ? 1 : 0;
339#endif
340
341    use_broken_old_ctypes_semantics = \
342        PyObject_HasAttrString(type, "_use_broken_old_ctypes_structure_semantics_");
343
344    isPacked = PyObject_GetAttrString(type, "_pack_");
345    if (isPacked) {
346        pack = _PyInt_AsInt(isPacked);
347        if (pack < 0 || PyErr_Occurred()) {
348            Py_XDECREF(isPacked);
349            PyErr_SetString(PyExc_ValueError,
350                            "_pack_ must be a non-negative integer");
351            return -1;
352        }
353        Py_DECREF(isPacked);
354    } else
355        PyErr_Clear();
356
357    len = PySequence_Length(fields);
358    if (len == -1) {
359        PyErr_SetString(PyExc_TypeError,
360                        "'_fields_' must be a sequence of pairs");
361        return -1;
362    }
363
364    stgdict = PyType_stgdict(type);
365    if (!stgdict)
366        return -1;
367    /* If this structure/union is already marked final we cannot assign
368       _fields_ anymore. */
369
370    if (stgdict->flags & DICTFLAG_FINAL) {/* is final ? */
371        PyErr_SetString(PyExc_AttributeError,
372                        "_fields_ is final");
373        return -1;
374    }
375
376    if (stgdict->format) {
377        PyMem_Free(stgdict->format);
378        stgdict->format = NULL;
379    }
380
381    if (stgdict->ffi_type_pointer.elements)
382        PyMem_Free(stgdict->ffi_type_pointer.elements);
383
384    basedict = PyType_stgdict((PyObject *)((PyTypeObject *)type)->tp_base);
385    if (basedict && !use_broken_old_ctypes_semantics) {
386        size = offset = basedict->size;
387        align = basedict->align;
388        union_size = 0;
389        total_align = align ? align : 1;
390        stgdict->ffi_type_pointer.type = FFI_TYPE_STRUCT;
391        stgdict->ffi_type_pointer.elements = PyMem_Malloc(sizeof(ffi_type *) * (basedict->length + len + 1));
392        if (stgdict->ffi_type_pointer.elements == NULL) {
393            PyErr_NoMemory();
394            return -1;
395        }
396        memset(stgdict->ffi_type_pointer.elements, 0,
397               sizeof(ffi_type *) * (basedict->length + len + 1));
398        memcpy(stgdict->ffi_type_pointer.elements,
399               basedict->ffi_type_pointer.elements,
400               sizeof(ffi_type *) * (basedict->length));
401        ffi_ofs = basedict->length;
402    } else {
403        offset = 0;
404        size = 0;
405        align = 0;
406        union_size = 0;
407        total_align = 1;
408        stgdict->ffi_type_pointer.type = FFI_TYPE_STRUCT;
409        stgdict->ffi_type_pointer.elements = PyMem_Malloc(sizeof(ffi_type *) * (len + 1));
410        if (stgdict->ffi_type_pointer.elements == NULL) {
411            PyErr_NoMemory();
412            return -1;
413        }
414        memset(stgdict->ffi_type_pointer.elements, 0,
415               sizeof(ffi_type *) * (len + 1));
416        ffi_ofs = 0;
417    }
418
419    assert(stgdict->format == NULL);
420    if (isStruct && !isPacked) {
421        stgdict->format = _ctypes_alloc_format_string(NULL, "T{");
422    } else {
423        /* PEP3118 doesn't support union, or packed structures (well,
424           only standard packing, but we dont support the pep for
425           that). Use 'B' for bytes. */
426        stgdict->format = _ctypes_alloc_format_string(NULL, "B");
427    }
428
429#define realdict ((PyObject *)&stgdict->dict)
430    for (i = 0; i < len; ++i) {
431        PyObject *name = NULL, *desc = NULL;
432        PyObject *pair = PySequence_GetItem(fields, i);
433        PyObject *prop;
434        StgDictObject *dict;
435        int bitsize = 0;
436
437        if (!pair || !PyArg_ParseTuple(pair, "OO|i", &name, &desc, &bitsize)) {
438            PyErr_SetString(PyExc_AttributeError,
439                            "'_fields_' must be a sequence of pairs");
440            Py_XDECREF(pair);
441            return -1;
442        }
443        dict = PyType_stgdict(desc);
444        if (dict == NULL) {
445            Py_DECREF(pair);
446            PyErr_Format(PyExc_TypeError,
447#if (PY_VERSION_HEX < 0x02050000)
448                         "second item in _fields_ tuple (index %d) must be a C type",
449#else
450                         "second item in _fields_ tuple (index %zd) must be a C type",
451#endif
452                         i);
453            return -1;
454        }
455        stgdict->ffi_type_pointer.elements[ffi_ofs + i] = &dict->ffi_type_pointer;
456        if (dict->flags & (TYPEFLAG_ISPOINTER | TYPEFLAG_HASPOINTER))
457            stgdict->flags |= TYPEFLAG_HASPOINTER;
458        dict->flags |= DICTFLAG_FINAL; /* mark field type final */
459        if (PyTuple_Size(pair) == 3) { /* bits specified */
460            switch(dict->ffi_type_pointer.type) {
461            case FFI_TYPE_UINT8:
462            case FFI_TYPE_UINT16:
463            case FFI_TYPE_UINT32:
464            case FFI_TYPE_SINT64:
465            case FFI_TYPE_UINT64:
466                break;
467
468            case FFI_TYPE_SINT8:
469            case FFI_TYPE_SINT16:
470            case FFI_TYPE_SINT32:
471                if (dict->getfunc != _ctypes_get_fielddesc("c")->getfunc
472#ifdef CTYPES_UNICODE
473                    && dict->getfunc != _ctypes_get_fielddesc("u")->getfunc
474#endif
475                    )
476                    break;
477                /* else fall through */
478            default:
479                PyErr_Format(PyExc_TypeError,
480                             "bit fields not allowed for type %s",
481                             ((PyTypeObject *)desc)->tp_name);
482                Py_DECREF(pair);
483                return -1;
484            }
485            if (bitsize <= 0 || bitsize > dict->size * 8) {
486                PyErr_SetString(PyExc_ValueError,
487                                "number of bits invalid for bit field");
488                Py_DECREF(pair);
489                return -1;
490            }
491        } else
492            bitsize = 0;
493        if (isStruct && !isPacked) {
494            char *fieldfmt = dict->format ? dict->format : "B";
495            char *fieldname = PyString_AsString(name);
496            char *ptr;
497            Py_ssize_t len;
498            char *buf;
499
500            if (fieldname == NULL)
501            {
502                PyErr_Format(PyExc_TypeError,
503                             "structure field name must be string not %s",
504                             name->ob_type->tp_name);
505
506                Py_DECREF(pair);
507                return -1;
508            }
509
510            len = strlen(fieldname) + strlen(fieldfmt);
511
512            buf = PyMem_Malloc(len + 2 + 1);
513            if (buf == NULL) {
514                Py_DECREF(pair);
515                PyErr_NoMemory();
516                return -1;
517            }
518            sprintf(buf, "%s:%s:", fieldfmt, fieldname);
519
520            ptr = stgdict->format;
521            if (dict->shape != NULL) {
522                stgdict->format = _ctypes_alloc_format_string_with_shape(
523                    dict->ndim, dict->shape, stgdict->format, buf);
524            } else {
525                stgdict->format = _ctypes_alloc_format_string(stgdict->format, buf);
526            }
527            PyMem_Free(ptr);
528            PyMem_Free(buf);
529
530            if (stgdict->format == NULL) {
531                Py_DECREF(pair);
532                return -1;
533            }
534        }
535        if (isStruct) {
536            prop = PyCField_FromDesc(desc, i,
537                                   &field_size, bitsize, &bitofs,
538                                   &size, &offset, &align,
539                                   pack, big_endian);
540        } else /* union */ {
541            size = 0;
542            offset = 0;
543            align = 0;
544            prop = PyCField_FromDesc(desc, i,
545                                   &field_size, bitsize, &bitofs,
546                                   &size, &offset, &align,
547                                   pack, big_endian);
548            union_size = max(size, union_size);
549        }
550        total_align = max(align, total_align);
551
552        if (!prop) {
553            Py_DECREF(pair);
554            return -1;
555        }
556        if (-1 == PyObject_SetAttr(type, name, prop)) {
557            Py_DECREF(prop);
558            Py_DECREF(pair);
559            return -1;
560        }
561        Py_DECREF(pair);
562        Py_DECREF(prop);
563    }
564#undef realdict
565
566    if (isStruct && !isPacked) {
567        char *ptr = stgdict->format;
568        stgdict->format = _ctypes_alloc_format_string(stgdict->format, "}");
569        PyMem_Free(ptr);
570        if (stgdict->format == NULL)
571            return -1;
572    }
573
574    if (!isStruct)
575        size = union_size;
576
577    /* Adjust the size according to the alignment requirements */
578    size = ((size + total_align - 1) / total_align) * total_align;
579
580    stgdict->ffi_type_pointer.alignment = Py_SAFE_DOWNCAST(total_align,
581                                                           Py_ssize_t,
582                                                           unsigned short);
583    stgdict->ffi_type_pointer.size = size;
584
585    stgdict->size = size;
586    stgdict->align = total_align;
587    stgdict->length = len;      /* ADD ffi_ofs? */
588
589    /* We did check that this flag was NOT set above, it must not
590       have been set until now. */
591    if (stgdict->flags & DICTFLAG_FINAL) {
592        PyErr_SetString(PyExc_AttributeError,
593                        "Structure or union cannot contain itself");
594        return -1;
595    }
596    stgdict->flags |= DICTFLAG_FINAL;
597
598    return MakeAnonFields(type);
599}
600