summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorArmin Rigo <arigo@tunes.org>2016-09-05 23:55:48 +0200
committerArmin Rigo <arigo@tunes.org>2016-09-05 23:55:48 +0200
commitea2c76897db97b5dab90e7e56720f15b1a154abf (patch)
tree0311aeabc4411cbb349955f6f7635c0168f07106
parentdfbbba5a2841fd97e323b7ea23f3941f913e5c80 (diff)
downloadcffi-ea2c76897db97b5dab90e7e56720f15b1a154abf.tar.gz
Issue #283: initializer for nested anonymous structs inside unions
-rw-r--r--c/_cffi_backend.c32
-rw-r--r--testing/cffi0/backend_tests.py43
2 files changed, 51 insertions, 24 deletions
diff --git a/c/_cffi_backend.c b/c/_cffi_backend.c
index be336ff..800c448 100644
--- a/c/_cffi_backend.c
+++ b/c/_cffi_backend.c
@@ -187,10 +187,12 @@ typedef struct cfieldobject_s {
Py_ssize_t cf_offset;
short cf_bitshift; /* >= 0: bitshift; or BS_REGULAR or BS_EMPTY_ARRAY */
short cf_bitsize;
+ unsigned char cf_flags; /* BF_... */
struct cfieldobject_s *cf_next;
} CFieldObject;
#define BS_REGULAR (-1) /* a regular field, not with bitshift */
#define BS_EMPTY_ARRAY (-2) /* a field which is an array 'type[0]' */
+#define BF_IGNORE_IN_CTOR 0x01 /* union field not in the first place */
static PyTypeObject CTypeDescr_Type;
static PyTypeObject CField_Type;
@@ -657,6 +659,7 @@ static PyMemberDef cfield_members[] = {
{"offset", T_PYSSIZET, OFF(cf_offset), READONLY},
{"bitshift", T_SHORT, OFF(cf_bitshift), READONLY},
{"bitsize", T_SHORT, OFF(cf_bitsize), READONLY},
+ {"flags", T_UBYTE, OFF(cf_flags), READONLY},
{NULL} /* Sentinel */
};
#undef OFF
@@ -1274,24 +1277,14 @@ convert_struct_from_object(char *data, CTypeDescrObject *ct, PyObject *init,
return -1;
}
- if (ct->ct_flags & CT_UNION) {
- Py_ssize_t n = PyObject_Size(init);
- if (n < 0)
- return -1;
- if (n > 1) {
- PyErr_Format(PyExc_ValueError,
- "initializer for '%s': %zd items given, but "
- "only one supported (use a dict if needed)",
- ct->ct_name, n);
- return -1;
- }
- }
if (PyList_Check(init) || PyTuple_Check(init)) {
PyObject **items = PySequence_Fast_ITEMS(init);
Py_ssize_t i, n = PySequence_Fast_GET_SIZE(init);
CFieldObject *cf = (CFieldObject *)ct->ct_extra;
for (i=0; i<n; i++) {
+ while (cf != NULL && (cf->cf_flags & BF_IGNORE_IN_CTOR))
+ cf = cf->cf_next;
if (cf == NULL) {
PyErr_Format(PyExc_ValueError,
"too many initializers for '%s' (got %zd)",
@@ -4085,7 +4078,7 @@ static PyObject *b_new_union_type(PyObject *self, PyObject *args)
static CFieldObject *
_add_field(PyObject *interned_fields, PyObject *fname, CTypeDescrObject *ftype,
- Py_ssize_t offset, int bitshift, int fbitsize)
+ Py_ssize_t offset, int bitshift, int fbitsize, int flags)
{
int err;
Py_ssize_t prev_size;
@@ -4098,6 +4091,7 @@ _add_field(PyObject *interned_fields, PyObject *fname, CTypeDescrObject *ftype,
cf->cf_offset = offset;
cf->cf_bitshift = bitshift;
cf->cf_bitsize = fbitsize;
+ cf->cf_flags = flags;
Py_INCREF(fname);
PyText_InternInPlace(&fname);
@@ -4184,7 +4178,7 @@ static PyObject *b_complete_struct_or_union(PyObject *self, PyObject *args)
int totalalignment = -1;
CFieldObject **previous;
int prev_bitfield_size, prev_bitfield_free;
- int sflags = 0;
+ int sflags = 0, fflags;
if (!PyArg_ParseTuple(args, "O!O!|Onii:complete_struct_or_union",
&CTypeDescr_Type, &ct,
@@ -4270,6 +4264,8 @@ static PyObject *b_complete_struct_or_union(PyObject *self, PyObject *args)
if (alignment < falign && do_align)
alignment = falign;
+ fflags = (is_union && i > 0) ? BF_IGNORE_IN_CTOR : 0;
+
if (fbitsize < 0) {
/* not a bitfield: common case */
int bs_flag;
@@ -4305,7 +4301,8 @@ static PyObject *b_complete_struct_or_union(PyObject *self, PyObject *args)
cfsrc->cf_type,
boffset / 8 + cfsrc->cf_offset,
cfsrc->cf_bitshift,
- cfsrc->cf_bitsize);
+ cfsrc->cf_bitsize,
+ cfsrc->cf_flags | fflags);
if (*previous == NULL)
goto error;
previous = &(*previous)->cf_next;
@@ -4315,7 +4312,7 @@ static PyObject *b_complete_struct_or_union(PyObject *self, PyObject *args)
}
else {
*previous = _add_field(interned_fields, fname, ftype,
- boffset / 8, bs_flag, -1);
+ boffset / 8, bs_flag, -1, fflags);
if (*previous == NULL)
goto error;
previous = &(*previous)->cf_next;
@@ -4445,7 +4442,8 @@ static PyObject *b_complete_struct_or_union(PyObject *self, PyObject *args)
bitshift = 8 * ftype->ct_size - fbitsize - bitshift;
*previous = _add_field(interned_fields, fname, ftype,
- field_offset_bytes, bitshift, fbitsize);
+ field_offset_bytes, bitshift, fbitsize,
+ fflags);
if (*previous == NULL)
goto error;
previous = &(*previous)->cf_next;
diff --git a/testing/cffi0/backend_tests.py b/testing/cffi0/backend_tests.py
index 989508a..0deae0f 100644
--- a/testing/cffi0/backend_tests.py
+++ b/testing/cffi0/backend_tests.py
@@ -1414,6 +1414,7 @@ class BackendTests:
assert p.b == 12
assert p.c == 14
assert p.d == 14
+ py.test.raises(ValueError, ffi.new, "struct foo_s *", [0, 0, 0, 0])
def test_nested_field_offset_align(self):
ffi = FFI(backend=self.Backend())
@@ -1453,14 +1454,42 @@ class BackendTests:
assert p.b == 0
assert p.c == 14
assert p.d == 14
- p = ffi.new("union foo_u *", {'b': 12})
- assert p.a == 0
+ p = ffi.new("union foo_u *", {'a': -63, 'b': 12})
+ assert p.a == -63
assert p.b == 12
- assert p.c == 0
- assert p.d == 0
- # we cannot specify several items in the dict, even though
- # in theory in this particular case it would make sense
- # to give both 'a' and 'b'
+ assert p.c == -63
+ assert p.d == -63
+ p = ffi.new("union foo_u *", [123, 456])
+ assert p.a == 123
+ assert p.b == 456
+ assert p.c == 123
+ assert p.d == 123
+ py.test.raises(ValueError, ffi.new, "union foo_u *", [0, 0, 0])
+
+ def test_nested_anonymous_struct_2(self):
+ ffi = FFI(backend=self.Backend())
+ ffi.cdef("""
+ struct foo_s {
+ int a;
+ union { int b; union { int c, d; }; };
+ int e;
+ };
+ """)
+ assert ffi.sizeof("struct foo_s") == 3 * SIZE_OF_INT
+ p = ffi.new("struct foo_s *", [11, 22, 33])
+ assert p.a == 11
+ assert p.b == p.c == p.d == 22
+ assert p.e == 33
+ py.test.raises(ValueError, ffi.new, "struct foo_s *", [11, 22, 33, 44])
+ FOO = ffi.typeof("struct foo_s")
+ fields = [(name, fld.offset, fld.flags) for (name, fld) in FOO.fields]
+ assert fields == [
+ ('a', 0 * SIZE_OF_INT, 0),
+ ('b', 1 * SIZE_OF_INT, 0),
+ ('c', 1 * SIZE_OF_INT, 1),
+ ('d', 1 * SIZE_OF_INT, 1),
+ ('e', 2 * SIZE_OF_INT, 0),
+ ]
def test_cast_to_array_type(self):
ffi = FFI(backend=self.Backend())