[pypy-commit] cffi default: Some sort of simplification of the logic of discovering field

arigo noreply at buildbot.pypy.org
Sun Aug 26 17:42:36 CEST 2012


Author: Armin Rigo <arigo at tunes.org>
Branch: 
Changeset: r899:46b206cf2436
Date: 2012-08-26 17:42 +0200
http://bitbucket.org/cffi/cffi/changeset/46b206cf2436/

Log:	Some sort of simplification of the logic of discovering field
	offsets. It allows us to give much better error messages.

diff --git a/cffi/vengine_cpy.py b/cffi/vengine_cpy.py
--- a/cffi/vengine_cpy.py
+++ b/cffi/vengine_cpy.py
@@ -389,44 +389,17 @@
         prnt('%s(PyObject *self, PyObject *noarg)' % (layoutfuncname,))
         prnt('{')
         prnt('  struct _cffi_aligncheck { char x; %s y; };' % cname)
-        if isinstance(tp, model.StructType) and tp.partial:
-            prnt('  static Py_ssize_t nums[] = {')
-            prnt('    sizeof(%s),' % cname)
-            prnt('    offsetof(struct _cffi_aligncheck, y),')
-            for fname, _, fbitsize in tp.enumfields():
-                assert fbitsize < 0
-                prnt('    offsetof(%s, %s),' % (cname, fname))
-                prnt('    sizeof(((%s *)0)->%s),' % (cname, fname))
-            prnt('    -1')
-            prnt('  };')
-            prnt('  return _cffi_get_struct_layout(nums);')
-        else:
-            ffi = self.ffi
-            BStruct = ffi._get_cached_btype(tp)
-            conditions = [
-                'sizeof(%s) != %d' % (cname, ffi.sizeof(BStruct)),
-                'offsetof(struct _cffi_aligncheck, y) != %d' % (
-                    ffi.alignof(BStruct),)]
-            for fname, ftype, fbitsize in tp.enumfields():
-                if fbitsize >= 0:
-                    continue        # xxx ignore fbitsize for now
-                BField = ffi._get_cached_btype(ftype)
-                conditions += [
-                    'offsetof(%s, %s) != %d' % (
-                        cname, fname, ffi.offsetof(BStruct, fname)),
-                    'sizeof(((%s *)0)->%s) != %d' % (
-                        cname, fname, ffi.sizeof(BField))]
-            prnt('  if (%s ||' % conditions[0])
-            for i in range(1, len(conditions)-1):
-                prnt('      %s ||' % conditions[i])
-            prnt('      %s) {' % conditions[-1])
-            prnt('    Py_INCREF(Py_False);')
-            prnt('    return Py_False;')
-            prnt('  }')
-            prnt('  else {')
-            prnt('    Py_INCREF(Py_True);')
-            prnt('    return Py_True;')
-            prnt('  }')
+        prnt('  static Py_ssize_t nums[] = {')
+        prnt('    sizeof(%s),' % cname)
+        prnt('    offsetof(struct _cffi_aligncheck, y),')
+        for fname, _, fbitsize in tp.enumfields():
+            if fbitsize >= 0:
+                continue      # xxx ignore fbitsize for now
+            prnt('    offsetof(%s, %s),' % (cname, fname))
+            prnt('    sizeof(((%s *)0)->%s),' % (cname, fname))
+        prnt('    -1')
+        prnt('  };')
+        prnt('  return _cffi_get_struct_layout(nums);')
         prnt('  /* the next line is not executed, but compiled */')
         prnt('  %s(0);' % (checkfuncname,))
         prnt('}')
@@ -447,12 +420,9 @@
         #
         function = getattr(module, layoutfuncname)
         layout = function()
-        if layout is False:
-            raise ffiplatform.VerificationError(
-                "incompatible layout for %s" % cname)
-        elif layout is True:
-            assert isinstance(tp, model.UnionType) or not tp.partial
-        else:
+        if isinstance(tp, model.StructType) and tp.partial:
+            # use the function()'s sizes and offsets to guide the
+            # layout of the struct
             totalsize = layout[0]
             totalalignment = layout[1]
             fieldofs = layout[2::2]
@@ -460,6 +430,29 @@
             tp.force_flatten()
             assert len(fieldofs) == len(fieldsize) == len(tp.fldnames)
             tp.fixedlayout = fieldofs, fieldsize, totalsize, totalalignment
+        else:
+            # check that the function()'s sizes and offsets match
+            # the real ones
+            def check(realvalue, expectedvalue, msg):
+                if realvalue != expectedvalue:
+                    raise ffiplatform.VerificationError(
+                        "in %s: %s (we have %d, but C compiler says %d)"
+                        % (cname, msg, expectedvalue, realvalue))
+            ffi = self.ffi
+            BStruct = ffi._get_cached_btype(tp)
+            check(layout[0], ffi.sizeof(BStruct), "wrong total size")
+            check(layout[1], ffi.alignof(BStruct), "wrong total alignment")
+            i = 2
+            for fname, ftype, fbitsize in tp.enumfields():
+                if fbitsize >= 0:
+                    continue        # xxx ignore fbitsize for now
+                check(layout[i], ffi.offsetof(BStruct, fname),
+                      "wrong offset for field %r" % (fname,))
+                BField = ffi._get_cached_btype(ftype)
+                check(layout[i+1], ffi.sizeof(BField),
+                      "wrong size for field %r" % (fname,))
+                i += 2
+            assert i == len(layout)
 
     def _loaded_struct_or_union(self, tp):
         if tp.fldnames is None:
diff --git a/cffi/vengine_gen.py b/cffi/vengine_gen.py
--- a/cffi/vengine_gen.py
+++ b/cffi/vengine_gen.py
@@ -212,42 +212,17 @@
         prnt('ssize_t %s(ssize_t i)' % (layoutfuncname,))
         prnt('{')
         prnt('  struct _cffi_aligncheck { char x; %s y; };' % cname)
-        if isinstance(tp, model.StructType) and tp.partial:
-            prnt('  static ssize_t nums[] = {')
-            prnt('    1, sizeof(%s),' % cname)
-            prnt('    offsetof(struct _cffi_aligncheck, y),')
-            for fname, _, fbitsize in tp.enumfields():
-                assert fbitsize < 0
-                prnt('    offsetof(%s, %s),' % (cname, fname))
-                prnt('    sizeof(((%s *)0)->%s),' % (cname, fname))
-            prnt('    -1')
-            prnt('  };')
-            prnt('  return nums[i];')
-        else:
-            ffi = self.ffi
-            BStruct = ffi._get_cached_btype(tp)
-            conditions = [
-                'sizeof(%s) != %d' % (cname, ffi.sizeof(BStruct)),
-                'offsetof(struct _cffi_aligncheck, y) != %d' % (
-                    ffi.alignof(BStruct),)]
-            for fname, ftype, fbitsize in tp.enumfields():
-                if fbitsize >= 0:
-                    continue        # xxx ignore fbitsize for now
-                BField = ffi._get_cached_btype(ftype)
-                conditions += [
-                    'offsetof(%s, %s) != %d' % (
-                        cname, fname, ffi.offsetof(BStruct, fname)),
-                    'sizeof(((%s *)0)->%s) != %d' % (
-                        cname, fname, ffi.sizeof(BField))]
-            prnt('  if (%s ||' % conditions[0])
-            for i in range(1, len(conditions)-1):
-                prnt('      %s ||' % conditions[i])
-            prnt('      %s) {' % conditions[-1])
-            prnt('    return -1;')
-            prnt('  }')
-            prnt('  else {')
-            prnt('    return 0;')
-            prnt('  }')
+        prnt('  static ssize_t nums[] = {')
+        prnt('    sizeof(%s),' % cname)
+        prnt('    offsetof(struct _cffi_aligncheck, y),')
+        for fname, _, fbitsize in tp.enumfields():
+            if fbitsize >= 0:
+                continue      # xxx ignore fbitsize for now
+            prnt('    offsetof(%s, %s),' % (cname, fname))
+            prnt('    sizeof(((%s *)0)->%s),' % (cname, fname))
+        prnt('    -1')
+        prnt('  };')
+        prnt('  return nums[i];')
         prnt('  /* the next line is not executed, but compiled */')
         prnt('  %s(0);' % (checkfuncname,))
         prnt('}')
@@ -261,27 +236,46 @@
         #
         BFunc = self.ffi.typeof("ssize_t(*)(ssize_t)")
         function = module.load_function(BFunc, layoutfuncname)
-        layout = function(0)
-        if layout < 0:
-            raise ffiplatform.VerificationError(
-                "incompatible layout for %s" % cname)
-        elif layout == 0:
-            assert isinstance(tp, model.UnionType) or not tp.partial
-        else:
-            totalsize = function(1)
-            totalalignment = function(2)
-            fieldofs = []
-            fieldsize = []
-            num = 3
-            while True:
-                x = function(num)
-                if x < 0: break
-                fieldofs.append(x)
-                fieldsize.append(function(num+1))
-                num += 2
+        layout = []
+        num = 0
+        while True:
+            x = function(num)
+            if x < 0: break
+            layout.append(x)
+            num += 1
+        if isinstance(tp, model.StructType) and tp.partial:
+            # use the function()'s sizes and offsets to guide the
+            # layout of the struct
+            totalsize = layout[0]
+            totalalignment = layout[1]
+            fieldofs = layout[2::2]
+            fieldsize = layout[3::2]
             tp.force_flatten()
             assert len(fieldofs) == len(fieldsize) == len(tp.fldnames)
             tp.fixedlayout = fieldofs, fieldsize, totalsize, totalalignment
+        else:
+            # check that the function()'s sizes and offsets match
+            # the real ones
+            def check(realvalue, expectedvalue, msg):
+                if realvalue != expectedvalue:
+                    raise ffiplatform.VerificationError(
+                        "in %s: %s (we have %d, but C compiler says %d)"
+                        % (cname, msg, expectedvalue, realvalue))
+            ffi = self.ffi
+            BStruct = ffi._get_cached_btype(tp)
+            check(layout[0], ffi.sizeof(BStruct), "wrong total size")
+            check(layout[1], ffi.alignof(BStruct), "wrong total alignment")
+            i = 2
+            for fname, ftype, fbitsize in tp.enumfields():
+                if fbitsize >= 0:
+                    continue        # xxx ignore fbitsize for now
+                check(layout[i], ffi.offsetof(BStruct, fname),
+                      "wrong offset for field %r" % (fname,))
+                BField = ffi._get_cached_btype(ftype)
+                check(layout[i+1], ffi.sizeof(BField),
+                      "wrong size for field %r" % (fname,))
+                i += 2
+            assert i == len(layout)
 
     def _loaded_struct_or_union(self, tp):
         if tp.fldnames is None:
diff --git a/testing/test_verify.py b/testing/test_verify.py
--- a/testing/test_verify.py
+++ b/testing/test_verify.py
@@ -270,21 +270,37 @@
     ffi.cdef("struct foo_s { char x; int y; long *z; };")
     ffi.verify("struct foo_s { char x; int y; long *z; };")
     #
-    if sys.platform == 'win32':
-        py.test.skip("XXX fixme: only gives warnings")
-    for real in [
-        "struct foo_s { char x; int y; int *z; };",
-        "struct foo_s { char x; long *z; int y; };",
-        "struct foo_s { int y; long *z; };",
-        "struct foo_s { char x; int y; long *z; char extra; };",
-        ]:
-        py.test.raises(VerificationError, ffi.verify, real)
+    if sys.platform != 'win32':  # XXX fixme: only gives warnings
+        py.test.raises(VerificationError, ffi.verify,
+            "struct foo_s { char x; int y; int *z; };")
+    #
+    py.test.raises(VerificationError, ffi.verify,
+        "struct foo_s { int y; long *z; };")
+    #
+    e = py.test.raises(VerificationError, ffi.verify,
+        "struct foo_s { int y; char x; long *z; };")
+    assert str(e.value) == (
+        "in struct foo_s: wrong offset for field 'x'"
+        " (we have 0, but C compiler says 4)")
+    #
+    e = py.test.raises(VerificationError, ffi.verify,
+        "struct foo_s { char x; int y; long *z; char extra; };")
+    assert str(e.value) == (
+        "in 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*")))
     #
     # 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; };")
-
+    #
+    e = py.test.raises(VerificationError, ffi.verify,
+        "struct foo_s { char x; short pad; short y; long *z; };")
+    assert str(e.value) == (
+        "in struct foo_s: wrong size for field 'y'"
+        " (we have 4, but C compiler says 2)")
 
 def test_ffi_nonfull_struct():
     ffi = FFI()


More information about the pypy-commit mailing list