Author: Armin Rigo <[email protected]>
Branch:
Changeset: r164:fca0f1dca466
Date: 2014-12-05 16:22 +0100
http://bitbucket.org/cffi/creflect/changeset/fca0f1dca466/
Log: simple structs work
diff --git a/zeffir/builder.c b/zeffir/builder.c
--- a/zeffir/builder.c
+++ b/zeffir/builder.c
@@ -388,14 +388,42 @@
return _zef_array_type(cb, ctitem, (size_t)-1, NULL);
}
+static _crx_type_t *_zef_struct_or_union(_crx_builder_t *cb, const char *name,
+ int flag)
+{
+ if (PyErr_Occurred())
+ return NULL;
+
+ PyObject *name_obj;
+ CTypeDescrObject *ct;
+
+ name_obj = PyString_FromString(name);
+ if (name_obj == NULL)
+ return NULL;
+
+ ct = get_cached_type(cb, name_obj);
+ if (ct && (ct->ct_flags == flag))
+ goto done;
+
+ ct = ctypedescr_new(name_obj, PyString_GET_SIZE(name_obj));
+ if (ct == NULL)
+ goto done;
+
+ ct->ct_flags = flag;
+
+ done:
+ Py_DECREF(name_obj);
+ return ct;
+}
+
static _crx_type_t *zef_get_struct_type(_crx_builder_t *cb, const char *name)
{
- abort();
+ return _zef_struct_or_union(cb, name, CT_STRUCT);
}
static _crx_type_t *zef_get_union_type(_crx_builder_t *cb, const char *name)
{
- abort();
+ return _zef_struct_or_union(cb, name, CT_UNION);
}
static _crx_type_t *zef_get_enum_type(_crx_builder_t *cb, const char *name)
@@ -426,11 +454,258 @@
return _zef_primitive(cb, -1, name, CT_UNKNOWN);
}
-static void zef_complete(_crx_builder_t *cb, _crx_type_t *t,
+static CFieldObject *_add_field(PyObject *interned_fields,
+ const char *fname, CTypeDescrObject *ftype,
+ Py_ssize_t offset, int bitshift, int fbitsize)
+{
+ int err;
+ PyObject *fname_obj;
+ Py_ssize_t prev_size;
+ CFieldObject *cf = PyObject_New(CFieldObject, &CField_Type);
+ if (cf == NULL)
+ return NULL;
+
+ Py_INCREF(ftype);
+ cf->cf_type = ftype;
+ cf->cf_offset = offset;
+ cf->cf_bitshift = bitshift;
+ cf->cf_bitsize = fbitsize;
+
+ fname_obj = PyString_InternFromString(fname);
+ prev_size = PyDict_Size(interned_fields);
+ err = PyDict_SetItem(interned_fields, fname_obj, (PyObject *)cf);
+ Py_DECREF(fname_obj);
+ Py_DECREF(cf);
+ if (err < 0)
+ return NULL;
+
+ if (PyDict_Size(interned_fields) != prev_size + 1) {
+ PyErr_Format(PyExc_KeyError, "duplicate field name '%s'",
+ PyText_AS_UTF8(fname));
+ return NULL;
+ }
+ return cf; /* borrowed reference */
+}
+
+static void zef_complete(_crx_builder_t *cb, _crx_type_t *ct,
size_t sz, size_t align,
_crx_field_t fields[], int nfields)
{
- abort();
+ PyObject *interned_fields;
+ CFieldObject **previous;
+ int i;
+
+ assert(ct->ct_flags & (CT_STRUCT | CT_UNION));
+ if (ct->ct_size >= 0) {
+ PyErr_Format(ZefError, "duplicate declaration of the fields of '%s'",
+ ct->ct_name);
+ return;
+ }
+
+ interned_fields = PyDict_New();
+ if (interned_fields == NULL)
+ return;
+
+ previous = (CFieldObject **)&ct->ct_extra;
+
+ for (i = 0; i < nfields; i++) {
+ _crx_field_t *f = &fields[i];
+ CTypeDescrObject *ftype = f->type;
+
+ if (ftype->ct_size < 0) {
+ if ((ftype->ct_flags & CT_ARRAY) /* && fbitsize < 0
+ && (i == nb_fields - 1 || foffset != -1) */ ) {
+ ct->ct_flags |= CT_WITH_VAR_ARRAY;
+ }
+ else {
+ PyErr_Format(PyExc_TypeError,
+ "field '%s.%s' has ctype '%s' of unknown size",
+ ct->ct_name, f->name, ftype->ct_name);
+ goto error;
+ }
+ }
+
+ if (f->numbits < 0) {
+ /* not a bitfield: common case */
+ int bs_flag;
+
+ if ((ftype->ct_flags & CT_ARRAY) && ftype->ct_length == 0)
+ bs_flag = BS_EMPTY_ARRAY;
+ else
+ bs_flag = BS_REGULAR;
+
+#if 0 /* XXX */
+ if (PyText_GetSize(fname) == 0 &&
+ ftype->ct_flags & (CT_STRUCT|CT_UNION)) {
+ /* a nested anonymous struct or union */
+ CFieldObject *cfsrc = (CFieldObject *)ftype->ct_extra;
+ for (; cfsrc != NULL; cfsrc = cfsrc->cf_next) {
+ /* broken complexity in the call to get_field_name(),
+ but we'll assume you never do that with nested
+ anonymous structures with thousand of fields */
+ *previous = _add_field(interned_fields,
+ get_field_name(ftype, cfsrc),
+ cfsrc->cf_type,
+ boffset / 8 + cfsrc->cf_offset,
+ cfsrc->cf_bitshift,
+ cfsrc->cf_bitsize);
+ if (*previous == NULL)
+ goto error;
+ previous = &(*previous)->cf_next;
+ }
+ /* always forbid such structures from being passed by value */
+ ct->ct_flags |= CT_CUSTOM_FIELD_POS;
+ }
+ else
+#endif
+ {
+ *previous = _add_field(interned_fields, f->name, ftype,
+ f->offset, bs_flag, -1);
+ if (*previous == NULL)
+ goto error;
+ previous = &(*previous)->cf_next;
+ }
+ }
+ else {
+ abort();
+#if 0
+ /* this is the case of a bitfield */
+ Py_ssize_t field_offset_bytes;
+ int bits_already_occupied, bitshift;
+
+ if (foffset >= 0) {
+ PyErr_Format(PyExc_TypeError,
+ "field '%s.%s' is a bitfield, "
+ "but a fixed offset is specified",
+ ct->ct_name, PyText_AS_UTF8(fname));
+ goto error;
+ }
+
+ if (!(ftype->ct_flags & (CT_PRIMITIVE_SIGNED |
+ CT_PRIMITIVE_UNSIGNED |
+ CT_PRIMITIVE_CHAR))) {
+ PyErr_Format(PyExc_TypeError,
+ "field '%s.%s' declared as '%s' cannot be a bit field",
+ ct->ct_name, PyText_AS_UTF8(fname),
+ ftype->ct_name);
+ goto error;
+ }
+ if (fbitsize > 8 * ftype->ct_size) {
+ PyErr_Format(PyExc_TypeError,
+ "bit field '%s.%s' is declared '%s:%d', which "
+ "exceeds the width of the type",
+ ct->ct_name, PyText_AS_UTF8(fname),
+ ftype->ct_name, fbitsize);
+ goto error;
+ }
+
+ /* compute the starting position of the theoretical field
+ that covers a complete 'ftype', inside of which we will
+ locate the real bitfield */
+ field_offset_bytes = boffset / 8;
+ field_offset_bytes &= ~(falign - 1);
+
+ if (fbitsize == 0) {
+ if (PyText_GetSize(fname) > 0) {
+ PyErr_Format(PyExc_TypeError,
+ "field '%s.%s' is declared with :0",
+ ct->ct_name, PyText_AS_UTF8(fname));
+ goto error;
+ }
+ if (!(sflags & SF_MSVC_BITFIELDS)) {
+ /* GCC's notion of "ftype :0;" */
+
+ /* pad boffset to a value aligned for "ftype" */
+ if (boffset > field_offset_bytes * 8) {
+ field_offset_bytes += falign;
+ assert(boffset < field_offset_bytes * 8);
+ }
+ boffset = field_offset_bytes * 8;
+ }
+ else {
+ /* MSVC's notion of "ftype :0;" */
+
+ /* Mostly ignored. It seems they only serve as
+ separator between other bitfields, to force them
+ into separate words. */
+ }
+ prev_bitfield_size = 0;
+ }
+ else {
+ if (!(sflags & SF_MSVC_BITFIELDS)) {
+ /* GCC's algorithm */
+
+ /* Can the field start at the offset given by 'boffset'?
It
+ can if it would entirely fit into an aligned ftype
field. */
+ bits_already_occupied = boffset - (field_offset_bytes * 8);
+
+ if (bits_already_occupied + fbitsize > 8 * ftype->ct_size)
{
+ /* it would not fit, we need to start at the next
+ allowed position */
+ if ((sflags & SF_PACKED) &&
+ (bits_already_occupied & 7)) {
+ PyErr_Format(PyExc_NotImplementedError,
+ "with 'packed', gcc would compile field "
+ "'%s.%s' to reuse some bits in the previous "
+ "field", ct->ct_name, PyText_AS_UTF8(fname));
+ goto error;
+ }
+ field_offset_bytes += falign;
+ assert(boffset < field_offset_bytes * 8);
+ boffset = field_offset_bytes * 8;
+ bitshift = 0;
+ }
+ else {
+ bitshift = bits_already_occupied;
+ assert(bitshift >= 0);
+ }
+ boffset += fbitsize;
+ }
+ else {
+ /* MSVC's algorithm */
+
+ /* A bitfield is considered as taking the full width
+ of their declared type. It can share some bits
+ with the previous field only if it was also a
+ bitfield and used a type of the same size. */
+ if (prev_bitfield_size == ftype->ct_size &&
+ prev_bitfield_free >= fbitsize) {
+ /* yes: reuse */
+ bitshift = 8 * prev_bitfield_size - prev_bitfield_free;
+ }
+ else {
+ /* no: start a new full field */
+ boffset = (boffset + falign*8-1) & ~(falign*8-1);
/*align*/
+ boffset += ftype->ct_size * 8;
+ bitshift = 0;
+ prev_bitfield_size = ftype->ct_size;
+ prev_bitfield_free = 8 * prev_bitfield_size;
+ }
+ prev_bitfield_free -= fbitsize;
+ field_offset_bytes = boffset / 8 - ftype->ct_size;
+ }
+
+ if (sflags & SF_GCC_BIG_ENDIAN)
+ bitshift = 8 * ftype->ct_size - fbitsize - bitshift;
+
+ *previous = _add_field(interned_fields, fname, ftype,
+ field_offset_bytes, bitshift, fbitsize);
+ if (*previous == NULL)
+ goto error;
+ previous = &(*previous)->cf_next;
+ }
+#endif
+ }
+ }
+ *previous = NULL;
+
+ ct->ct_size = sz;
+ /* 'align' ignored for now */
+ ct->ct_stuff = interned_fields;
+ return;
+
+ error:
+ Py_DECREF(interned_fields);
}
static void zef_complete_enum(_crx_builder_t *cb, _crx_type_t *t,
diff --git a/zeffir/cdata.c b/zeffir/cdata.c
--- a/zeffir/cdata.c
+++ b/zeffir/cdata.c
@@ -6,17 +6,6 @@
PyObject *c_weakreflist;
} CDataObject;
-struct cfieldobject_s {
- PyObject_HEAD
- CTypeDescrObject *cf_type;
- Py_ssize_t cf_offset;
- short cf_bitshift; /* >= 0: bitshift; or BS_REGULAR or BS_EMPTY_ARRAY */
- short cf_bitsize;
- struct cfieldobject_s *cf_next;
-};
-#define BS_REGULAR (-1) /* a regular field, not with bitshift */
-#define BS_EMPTY_ARRAY (-2) /* a field which is an array 'type[0]' */
-
typedef union {
unsigned char m_char;
unsigned short m_short;
@@ -52,6 +41,70 @@
/************************************************************/
+struct cfieldobject_s {
+ PyObject_HEAD
+ CTypeDescrObject *cf_type;
+ Py_ssize_t cf_offset;
+ short cf_bitshift; /* >= 0: bitshift; or BS_REGULAR or BS_EMPTY_ARRAY */
+ short cf_bitsize;
+ struct cfieldobject_s *cf_next;
+};
+#define BS_REGULAR (-1) /* a regular field, not with bitshift */
+#define BS_EMPTY_ARRAY (-2) /* a field which is an array 'type[0]' */
+
+static void
+cfield_dealloc(CFieldObject *cf)
+{
+ Py_DECREF(cf->cf_type);
+ PyObject_Del(cf);
+}
+
+#undef OFF
+#define OFF(x) offsetof(CFieldObject, x)
+
+static PyMemberDef cfield_members[] = {
+ {"type", T_OBJECT, OFF(cf_type), READONLY},
+ {"offset", T_PYSSIZET, OFF(cf_offset), READONLY},
+ {"bitshift", T_SHORT, OFF(cf_bitshift), READONLY},
+ {"bitsize", T_SHORT, OFF(cf_bitsize), READONLY},
+ {NULL} /* Sentinel */
+};
+#undef OFF
+
+static PyTypeObject CField_Type = {
+ PyVarObject_HEAD_INIT(NULL, 0)
+ "zeffir.CField",
+ sizeof(CFieldObject),
+ 0,
+ (destructor)cfield_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 */
+ PyObject_GenericGetAttr, /* tp_getattro */
+ 0, /* tp_setattro */
+ 0, /* tp_as_buffer */
+ Py_TPFLAGS_DEFAULT, /* 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 */
+ cfield_members, /* tp_members */
+};
+
+/************************************************************/
+
static int
CDataObject_Or_PyFloat_Check(PyObject *ob)
{
@@ -343,10 +396,10 @@
return NULL;
}
+static PyObject *convert_to_object_bitfield(char *data, CFieldObject *cf)
+{
+ abort();
#if 0
-static PyObject *
-convert_to_object_bitfield(char *data, CFieldObject *cf)
-{
CTypeDescrObject *ct = cf->cf_type;
/*READ(data, ct->ct_size)*/
@@ -377,8 +430,8 @@
else
return PyLong_FromUnsignedLongLong(value);
}
+#endif
}
-#endif
static int _convert_overflow(PyObject *init, const char *ct_name)
{
@@ -1237,6 +1290,58 @@
return convert_from_object(c, ctitem, v);
}
+//2172
+static PyObject *cdata_getattro(CDataObject *cd, PyObject *attr)
+{
+ CFieldObject *cf;
+ CTypeDescrObject *ct = cd->c_type;
+
+ if (ct->ct_flags & CT_POINTER)
+ ct = ct->ct_itemdescr;
+
+ if ((ct->ct_flags & (CT_STRUCT|CT_UNION)) && ct->ct_stuff != NULL) {
+ cf = (CFieldObject *)PyDict_GetItem(ct->ct_stuff, attr);
+ if (cf != NULL) {
+ /* read the field 'cf' */
+ char *data = cd->c_data + cf->cf_offset;
+ if (cf->cf_bitshift == BS_REGULAR)
+ return convert_to_object(data, cf->cf_type);
+ else if (cf->cf_bitshift == BS_EMPTY_ARRAY)
+ return new_simple_cdata(data,
+ (CTypeDescrObject *)cf->cf_type->ct_stuff);
+ else
+ return convert_to_object_bitfield(data, cf);
+ }
+ }
+ return PyObject_GenericGetAttr((PyObject *)cd, attr);
+}
+
+//2198
+static int cdata_setattro(CDataObject *cd, PyObject *attr, PyObject *value)
+{
+ CFieldObject *cf;
+ CTypeDescrObject *ct = cd->c_type;
+
+ if (ct->ct_flags & CT_POINTER)
+ ct = ct->ct_itemdescr;
+
+ if ((ct->ct_flags & (CT_STRUCT|CT_UNION)) && ct->ct_stuff != NULL) {
+ cf = (CFieldObject *)PyDict_GetItem(ct->ct_stuff, attr);
+ if (cf != NULL) {
+ /* write the field 'cf' */
+ if (value != NULL) {
+ return convert_field_from_object(cd->c_data, cf, value);
+ }
+ else {
+ PyErr_SetString(PyExc_AttributeError,
+ "cannot delete struct field");
+ return -1;
+ }
+ }
+ }
+ return PyObject_GenericSetAttr((PyObject *)cd, attr, value);
+}
+
static PyMappingMethods CData_as_mapping = {
(lenfunc)cdata_length, /*mp_length*/
@@ -1261,8 +1366,8 @@
0,//(hashfunc)cdata_hash, /* tp_hash */
0,//(ternaryfunc)cdata_call, /* tp_call */
0, /* tp_str */
- 0,//(getattrofunc)cdata_getattro, /* tp_getattro */
- 0,//(setattrofunc)cdata_setattro, /* tp_setattro */
+ (getattrofunc)cdata_getattro, /* tp_getattro */
+ (setattrofunc)cdata_setattro, /* tp_setattro */
0, /* tp_as_buffer */
Py_TPFLAGS_DEFAULT | Py_TPFLAGS_CHECKTYPES, /* tp_flags */
0, /* tp_doc */
diff --git a/zeffir/test/struct.crx b/zeffir/test/struct.crx
new file mode 100644
--- /dev/null
+++ b/zeffir/test/struct.crx
@@ -0,0 +1,13 @@
+typedef struct {
+ int a;
+ long b;
+} mystruct_t;
+
+
+// CREFLECT: start
+
+typedef struct {
+ int a, b;
+} mystruct_t;
+
+// CREFLECT: end
diff --git a/zeffir/test/test_struct.py b/zeffir/test/test_struct.py
new file mode 100644
--- /dev/null
+++ b/zeffir/test/test_struct.py
@@ -0,0 +1,10 @@
+import support
+
+
+def test_simple_struct():
+ ffi, lib = support.compile_and_open('struct')
+ p = ffi.new('mystruct_t *', [-5, 0x600112233])
+ assert p.a == -5
+ assert p.b == 0x600112233
+ p.b += 1
+ assert p.b == 0x600112234
diff --git a/zeffir/zeffir.c b/zeffir/zeffir.c
--- a/zeffir/zeffir.c
+++ b/zeffir/zeffir.c
@@ -41,6 +41,8 @@
return;
if (PyType_Ready(&CTypeDescr_Type) < 0)
return;
+ if (PyType_Ready(&CField_Type) < 0)
+ return;
if (PyType_Ready(&CData_Type) < 0)
return;
if (PyType_Ready(&CDataOwning_Type) < 0)
diff --git a/zeffir/zeffir.h b/zeffir/zeffir.h
--- a/zeffir/zeffir.h
+++ b/zeffir/zeffir.h
@@ -6,7 +6,7 @@
static PyTypeObject ZefFFI_Type;
static PyTypeObject CTypeDescr_Type;
-//static PyTypeObject CField_Type;
+static PyTypeObject CField_Type;
static PyTypeObject CData_Type;
static PyTypeObject CDataOwning_Type;
static PyTypeObject CDataOwningGC_Type;
_______________________________________________
pypy-commit mailing list
[email protected]
https://mail.python.org/mailman/listinfo/pypy-commit