Commits

Armin Rigo committed cd82a84

Detect mismatches in the size of fields.

Comments (0)

Files changed (4)

 
         if (ftype->ct_size < 0) {
             PyErr_Format(PyExc_TypeError,
-                         "field '%s' has ctype '%s' of unknown size",
-                         PyString_AS_STRING(fname),
+                         "field '%s.%s' has ctype '%s' of unknown size",
+                         ct->ct_name, PyString_AS_STRING(fname),
                          ftype->ct_name);
             goto error;
         }
             lst = zip(self.fldnames, fldtypes, self.fldbitsize)
             ffi._backend.complete_struct_or_union(BType, lst, self)
         else:
-            fieldofs, totalsize, totalalignment = self.fixedlayout
+            fieldofs, fieldsize, totalsize, totalalignment = self.fixedlayout
+            for fname, ftype, fsize in zip(self.fldnames, fldtypes, fieldsize):
+                if ffi.sizeof(ftype) != fsize:
+                    from .ffiplatform import VerificationError
+                    raise VerificationError, (
+                        "field '%s.%s' is declared as %d bytes, but is "
+                        "really %d bytes" % (self.name, fname,
+                                             ffi.sizeof(ftype), fsize))
             lst = zip(self.fldnames, fldtypes, self.fldbitsize, fieldofs)
             ffi._backend.complete_struct_or_union(BType, lst, self,
                                                   totalsize, totalalignment)
         module._cffi_setup(lst)
         del module._cffi_setup
         #
-        for name, tp in self.ffi._parser._declarations.iteritems():
-            kind, realname = name.split(' ', 1)
-            method = getattr(self, 'loading_cpy_%s' % (kind,))
-            method(tp, realname, module)
+        self.load(module, 'loading')
+        self.load(module, 'loaded')
         #
         return module
 
             method = getattr(self, 'generate_cpy_%s_%s' % (kind, step_name))
             method(tp, realname)
 
+    def load(self, module, step_name):
+        for name, tp in self.ffi._parser._declarations.iteritems():
+            kind, realname = name.split(' ', 1)
+            method = getattr(self, '%s_cpy_%s' % (step_name, kind))
+            method(tp, realname, module)
+
     def generate_nothing(self, tp, name):
         pass
 
     generate_cpy_typedef_method = generate_nothing
     generate_cpy_typedef_init   = generate_nothing
     loading_cpy_typedef         = loaded_noop
+    loaded_cpy_typedef          = loaded_noop
 
     # ----------
     # function declarations
 
     generate_cpy_function_init = generate_nothing
     loading_cpy_function       = loaded_noop
+    loaded_cpy_function        = loaded_noop
 
     # ----------
     # struct declarations
 
     def generate_cpy_struct_decl(self, tp, name):
+        assert tp.partial    # xxx
         assert name == tp.name
         prnt = self.prnt
         prnt('static PyObject *')
         prnt('    offsetof(struct _cffi_aligncheck, y),')
         for i in range(len(tp.fldnames)):
             prnt('    offsetof(struct %s, %s),' % (name, tp.fldnames[i]))
+            prnt('    sizeof(((struct %s *)0)->%s),' % (name, tp.fldnames[i]))
         prnt('    -1')
         prnt('  };')
         prnt('  return _cffi_get_struct_layout(nums);')
         layout = function()
         totalsize = layout[0]
         totalalignment = layout[1]
-        fieldofs = layout[2:]
-        assert len(fieldofs) == len(tp.fldnames)
-        tp.fixedlayout = fieldofs, totalsize, totalalignment
+        fieldofs = layout[2::2]
+        fieldsize = layout[3::2]
+        assert len(fieldofs) == len(fieldsize) == len(tp.fldnames)
+        tp.fixedlayout = fieldofs, fieldsize, totalsize, totalalignment
+
+    def loaded_cpy_struct(self, tp, name, module):
+        self.ffi._get_cached_btype(tp)   # force 'fixedlayout' to be considered
 
     # ----------
 

testing/test_verify.py

                      'signed char', 'unsigned char',
                      'unsigned short', 'unsigned int',
                      'unsigned long', 'unsigned long long']
+all_signed_integer_types = [_typename for _typename in all_integer_types
+                                      if not _typename.startswith('unsigned ')]
 all_float_types = ['float', 'double']
 
 def test_all_integer_and_float_types():
     """)
     assert ffi.sizeof('struct foo_s') == 6 * ffi.sizeof('int')
     assert ffi.offsetof('struct foo_s', 'x') == 2 * ffi.sizeof('int')
+
+def test_ffi_nonfull_alignment():
+    ffi = FFI()
+    ffi.cdef("struct foo_s { char x; ...; };")
+    ffi.verify("struct foo_s { int a, b; char x; };")
+    assert ffi.sizeof('struct foo_s') == 3 * ffi.sizeof('int')
+    assert ffi.alignof('struct foo_s') == ffi.sizeof('int')
+
+def test_struct_bad_sized_integer():
+    for typename in all_signed_integer_types:
+        for real in all_signed_integer_types:
+            ffi = FFI()
+            if ffi.sizeof(typename) != ffi.sizeof(real):
+                ffi.cdef("struct foo_s { %s x; ...; };" % typename)
+                try:
+                    ffi.verify("struct foo_s { %s x; };" % real)
+                except VerificationError:
+                    pass
+                else:
+                    raise AssertionError("%s != %s" % (typename, real))