Author: Armin Rigo <ar...@tunes.org> Branch: cffi-1.0 Changeset: r1770:9d55286c87b6 Date: 2015-04-19 10:34 +0200 http://bitbucket.org/cffi/cffi/changeset/9d55286c87b6/
Log: Reimplement the distinction between "...;" and non-"...;" structures diff --git a/c/_cffi_backend.c b/c/_cffi_backend.c --- a/c/_cffi_backend.c +++ b/c/_cffi_backend.c @@ -272,6 +272,8 @@ # include "wchar_helper.h" #endif +static PyObject *FFIError; + /************************************************************/ static CTypeDescrObject * @@ -3745,6 +3747,7 @@ #define SF_GCC_LITTLE_ENDIAN 0x40 #define SF_PACKED 0x08 +#define SF_STD_FIELD_POS 0x80 static int complete_sflags(int sflags) { @@ -3772,6 +3775,28 @@ return sflags; } +static int detect_custom_layout(CTypeDescrObject *ct, int sflags, + Py_ssize_t cdef_value, + Py_ssize_t compiler_value, + const char *msg1, const char *txt, + const char *msg2) +{ + if (compiler_value != cdef_value) { + if (sflags & SF_STD_FIELD_POS) { + PyErr_Format(FFIError, + "%s: %s%s%s (cdef says %zd, but C compiler says %zd)." + " fix it or use \"...;\" in the cdef for %s to " + "make it flexible", + ct->ct_name, msg1, txt, msg2, + cdef_value, compiler_value, + ct->ct_name); + return -1; + } + ct->ct_flags |= CT_CUSTOM_FIELD_POS; + } + return 0; +} + static PyObject *b_complete_struct_or_union(PyObject *self, PyObject *args) { CTypeDescrObject *ct; @@ -3805,6 +3830,7 @@ "first arg must be a non-initialized struct or union ctype"); return NULL; } + ct->ct_flags &= ~CT_CUSTOM_FIELD_POS; alignment = 1; boffset = 0; /* this number is in *bits*, not bytes! */ @@ -3882,8 +3908,10 @@ if (foffset >= 0) { /* a forced field position: ignore the offset just computed, except to know if we must set CT_CUSTOM_FIELD_POS */ - if (boffset != foffset * 8) - ct->ct_flags |= CT_CUSTOM_FIELD_POS; + if (detect_custom_layout(ct, sflags, boffset / 8, foffset, + "wrong offset for field '", + PyText_AS_UTF8(fname), "'") < 0) + goto error; boffset = foffset * 8; } @@ -4061,14 +4089,28 @@ if (totalsize == 0) totalsize = 1; } - else if (totalsize < boffsetmax) { - PyErr_Format(PyExc_TypeError, - "%s cannot be of size %zd: there are fields at least " - "up to %zd", ct->ct_name, totalsize, boffsetmax); - goto error; - } + else { + if (detect_custom_layout(ct, sflags, boffsetmax, totalsize, + "wrong total size", "", "") < 0) + goto error; + if (totalsize < boffsetmax) { + PyErr_Format(PyExc_TypeError, + "%s cannot be of size %zd: there are fields at least " + "up to %zd", ct->ct_name, totalsize, boffsetmax); + goto error; + } + } + if (totalalignment < 0) { + totalalignment = alignment; + } + else { + if (detect_custom_layout(ct, sflags, alignment, totalalignment, + "wrong total alignment", "", "") < 0) + goto error; + } + ct->ct_size = totalsize; - ct->ct_length = totalalignment < 0 ? alignment : totalalignment; + ct->ct_length = totalalignment; ct->ct_stuff = interned_fields; ct->ct_flags &= ~CT_IS_OPAQUE; @@ -4076,6 +4118,7 @@ return Py_None; error: + ct->ct_extra = NULL; Py_DECREF(interned_fields); return NULL; } diff --git a/new/cffi1_module.c b/new/cffi1_module.c --- a/new/cffi1_module.c +++ b/new/cffi1_module.c @@ -1,4 +1,3 @@ -static PyObject *FFIError; #include "parse_c_type.c" #include "realize_c_type.c" diff --git a/new/parse_c_type.h b/new/parse_c_type.h --- a/new/parse_c_type.h +++ b/new/parse_c_type.h @@ -75,8 +75,11 @@ int first_field_index; // -> _cffi_fields array int num_fields; }; -#define CT_UNION 128 -#define CT_IS_OPAQUE 4096 +#define CT_UNION 128 +#define CT_IS_OPAQUE 4096 +#define CT_CUSTOM_FIELD_POS 32768 +/* ^^^ if not CUSTOM_FIELD_POS, complain if fields are not in the + "standard layout" and/or if some are missing */ struct _cffi_field_s { const char *name; diff --git a/new/realize_c_type.c b/new/realize_c_type.c --- a/new/realize_c_type.c +++ b/new/realize_c_type.c @@ -451,14 +451,11 @@ return -1; } - if (ctf->ct_size != fld->field_size) { - PyErr_Format(FFIError, - "%s field '%s' was declared in the cdef to be" - " %zd bytes, but is actually %zd bytes", - ct->ct_name, fld->name, - ctf->ct_size, fld->field_size); + if (detect_custom_layout(ct, SF_STD_FIELD_POS, + ctf->ct_size, fld->field_size, + "wrong size for field '", + fld->name, "'") < 0) return -1; - } f = Py_BuildValue("(sOin)", fld->name, ctf, (int)-1, (Py_ssize_t)fld->field_offset); @@ -469,19 +466,24 @@ PyList_SET_ITEM(fields, i, f); } - PyObject *args = Py_BuildValue("(OOOnn)", ct, fields, + int sflags = (s->flags & CT_CUSTOM_FIELD_POS) ? 0 : SF_STD_FIELD_POS; + + PyObject *args = Py_BuildValue("(OOOnni)", ct, fields, Py_None, (Py_ssize_t)s->size, - (Py_ssize_t)s->alignment); + (Py_ssize_t)s->alignment, + sflags); Py_DECREF(fields); if (args == NULL) return -1; ct->ct_extra = NULL; ct->ct_flags |= CT_IS_OPAQUE; + ct->ct_flags &= ~CT_CUSTOM_FIELD_POS; PyObject *res = b_complete_struct_or_union(NULL, args); ct->ct_flags &= ~CT_IS_OPAQUE; Py_DECREF(args); + if (res == NULL) { ct->ct_extra = builder; return -1; diff --git a/new/recompiler.py b/new/recompiler.py --- a/new/recompiler.py +++ b/new/recompiler.py @@ -428,9 +428,12 @@ def _generate_cpy_struct_ctx(self, tp, name): type_index = self._typesdict[tp] - flags = '0' + flags = [] + if tp.partial: + flags.append('CT_CUSTOM_FIELD_POS') if isinstance(tp, model.UnionType): - flags = 'CT_UNION' + flags.append('CT_UNION') + flags = ('|'.join(flags)) or '0' if tp.fldtypes is not None: c_field = [name] for fldname, fldtype in zip(tp.fldnames, tp.fldtypes): diff --git a/new/test_recompiler.py b/new/test_recompiler.py --- a/new/test_recompiler.py +++ b/new/test_recompiler.py @@ -199,7 +199,7 @@ def test_verify_struct(): ffi = FFI() - ffi.cdef("""struct foo_s { int b; short a; }; + ffi.cdef("""struct foo_s { int b; short a; ...; }; struct bar_s { struct foo_s *f; };""") lib = verify(ffi, 'test_verify_struct', """struct foo_s { short a; int b; }; @@ -213,6 +213,16 @@ q = ffi.new("struct bar_s *", {'f': p}) assert q.f == p +def test_verify_exact_field_offset(): + ffi = FFI() + ffi.cdef("""struct foo_s { int b; short a; };""") + lib = verify(ffi, 'test_verify_exact_field_offset', + """struct foo_s { short a; int b; };""") + e = py.test.raises(ffi.error, ffi.new, "struct foo_s *") # lazily + assert str(e.value) == ("struct foo_s: wrong offset for field 'b' (cdef " + 'says 0, but C compiler says 4). fix it or use "...;" ' + "in the cdef for struct foo_s to make it flexible") + def test_type_caching(): ffi1 = FFI(); ffi1.cdef("struct foo_s;") ffi2 = FFI(); ffi2.cdef("struct foo_s;") # different one! @@ -260,8 +270,8 @@ assert ffi.sizeof("struct foo_s") == 24 # found by the actual C code # lazily build the fields and boom: e = py.test.raises(ffi.error, ffi.new, "struct foo_s *") - assert str(e.value) == ("struct foo_s field 'a' was declared in the " - "cdef to be 20 bytes, but is actually 24 bytes") + assert str(e.value).startswith("struct foo_s: wrong size for field 'a' " + "(cdef says 20, but C compiler says 24)") def test_open_array_in_struct(): ffi = FFI() diff --git a/new/test_verify1.py b/new/test_verify1.py --- a/new/test_verify1.py +++ b/new/test_verify1.py @@ -402,41 +402,45 @@ assert lib.bar(ffi.NULL) == 42 def test_ffi_full_struct(): - ffi = FFI() - ffi.cdef("struct foo_s { char x; int y; long *z; };") - ffi.verify("struct foo_s { char x; int y; long *z; };") + def check(verified_code): + ffi = FFI() + ffi.cdef("struct foo_s { char x; int y; long *z; };") + ffi.verify(verified_code) + ffi.new("struct foo_s *") + + check("struct foo_s { char x; int y; long *z; };") # if sys.platform != 'win32': # XXX fixme: only gives warnings - py.test.raises(VerificationError, ffi.verify, + py.test.raises(VerificationError, check, "struct foo_s { char x; int y; int *z; };") # - py.test.raises(VerificationError, ffi.verify, - "struct foo_s { int y; long *z; };") + py.test.raises(VerificationError, check, + "struct foo_s { int y; long *z; };") # cdef'ed field x is missing # - e = py.test.raises(VerificationError, ffi.verify, - "struct foo_s { int y; char x; long *z; };") - assert str(e.value) == ( + e = py.test.raises(FFI.error, check, + "struct foo_s { int y; char x; long *z; };") + assert str(e.value).startswith( "struct foo_s: wrong offset for field 'x'" - " (we have 0, but C compiler says 4)") + " (cdef says 0, but C compiler says 4)") # - e = py.test.raises(VerificationError, ffi.verify, + e = py.test.raises(FFI.error, check, "struct foo_s { char x; int y; long *z; char extra; };") - assert str(e.value) == ( + assert str(e.value).startswith( "struct foo_s: wrong total size" - " (we have %d, but C compiler says %d)" % ( - ffi.sizeof("struct foo_s"), - ffi.sizeof("struct foo_s") + ffi.sizeof("long*"))) + " (cdef says %d, but C compiler says %d)" % ( + 8 + FFI().sizeof('long *'), + 8 + FFI().sizeof('long *') * 2)) # # a corner case that we cannot really detect, but where it has no # bad consequences: the size is the same, but there is an extra field # that replaces what is just padding in our declaration above - ffi.verify("struct foo_s { char x, extra; int y; long *z; };") + check("struct foo_s { char x, extra; int y; long *z; };") # - e = py.test.raises(VerificationError, ffi.verify, + e = py.test.raises(FFI.error, check, "struct foo_s { char x; short pad; short y; long *z; };") - assert str(e.value) == ( + assert str(e.value).startswith( "struct foo_s: wrong size for field 'y'" - " (we have 4, but C compiler says 2)") + " (cdef says 4, but C compiler says 2)") def test_ffi_nonfull_struct(): ffi = FFI() _______________________________________________ pypy-commit mailing list pypy-commit@python.org https://mail.python.org/mailman/listinfo/pypy-commit