diff options
author | Armin Rigo <arigo@tunes.org> | 2016-09-05 23:55:48 +0200 |
---|---|---|
committer | Armin Rigo <arigo@tunes.org> | 2016-09-05 23:55:48 +0200 |
commit | ea2c76897db97b5dab90e7e56720f15b1a154abf (patch) | |
tree | 0311aeabc4411cbb349955f6f7635c0168f07106 | |
parent | dfbbba5a2841fd97e323b7ea23f3941f913e5c80 (diff) | |
download | cffi-ea2c76897db97b5dab90e7e56720f15b1a154abf.tar.gz |
Issue #283: initializer for nested anonymous structs inside unions
-rw-r--r-- | c/_cffi_backend.c | 32 | ||||
-rw-r--r-- | testing/cffi0/backend_tests.py | 43 |
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()) |