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

Reply via email to