[pypy-commit] cffi linux-only: In-progress: verify fully-declared structs.

arigo noreply at buildbot.pypy.org
Tue Jun 12 11:33:49 CEST 2012


Author: Armin Rigo <arigo at tunes.org>
Branch: linux-only
Changeset: r282:369b47a0aaf7
Date: 2012-06-12 10:35 +0200
http://bitbucket.org/cffi/cffi/changeset/369b47a0aaf7/

Log:	In-progress: verify fully-declared structs.

diff --git a/c/_ffi_backend.c b/c/_ffi_backend.c
--- a/c/_ffi_backend.c
+++ b/c/_ffi_backend.c
@@ -3005,40 +3005,35 @@
     return PyInt_FromLong(align);
 }
 
-static PyObject *b_sizeof_type(PyObject *self, PyObject *arg)
+static PyObject *b_sizeof(PyObject *self, PyObject *arg)
 {
-    if (!CTypeDescr_Check(arg)) {
-        PyErr_SetString(PyExc_TypeError, "expected a 'ctype' object");
+    Py_ssize_t size;
+
+    if (CData_Check(arg)) {
+        CDataObject *cd = (CDataObject *)arg;
+
+        if (cd->c_type->ct_flags & CT_ARRAY)
+            size = get_array_length(cd) * cd->c_type->ct_itemdescr->ct_size;
+        else
+            size = cd->c_type->ct_size;
+    }
+    else if (CTypeDescr_Check(arg)) {
+        if (((CTypeDescrObject *)arg)->ct_size < 0) {
+            PyErr_Format(PyExc_ValueError, "ctype '%s' is of unknown size",
+                         ((CTypeDescrObject *)arg)->ct_name);
+            return NULL;
+        }
+        size = ((CTypeDescrObject *)arg)->ct_size;
+    }
+    else {
+        PyErr_SetString(PyExc_TypeError,
+                        "expected a 'cdata' or 'ctype' object");
         return NULL;
     }
-    if (((CTypeDescrObject *)arg)->ct_size < 0) {
-        PyErr_Format(PyExc_ValueError, "ctype '%s' is of unknown size",
-                     ((CTypeDescrObject *)arg)->ct_name);
-        return NULL;
-    }
-    return PyInt_FromSsize_t(((CTypeDescrObject *)arg)->ct_size);
-}
-
-static PyObject *b_sizeof_instance(PyObject *self, PyObject *arg)
-{
-    CDataObject *cd;
-    Py_ssize_t size;
-
-    if (!CData_Check(arg)) {
-        PyErr_SetString(PyExc_TypeError, "expected a 'cdata' object");
-        return NULL;
-    }
-    cd = (CDataObject *)arg;
-
-    if (cd->c_type->ct_flags & CT_ARRAY)
-        size = get_array_length(cd) * cd->c_type->ct_itemdescr->ct_size;
-    else
-        size = cd->c_type->ct_size;
-
     return PyInt_FromSsize_t(size);
 }
 
-static PyObject *b_typeof_instance(PyObject *self, PyObject *arg)
+static PyObject *b_typeof(PyObject *self, PyObject *arg)
 {
     PyObject *res;
 
@@ -3230,9 +3225,8 @@
     {"cast", b_cast, METH_VARARGS},
     {"callback", b_callback, METH_VARARGS},
     {"alignof", b_alignof, METH_O},
-    {"sizeof_type", b_sizeof_type, METH_O},
-    {"sizeof_instance", b_sizeof_instance, METH_O},
-    {"typeof_instance", b_typeof_instance, METH_O},
+    {"sizeof", b_sizeof, METH_O},
+    {"typeof", b_typeof, METH_O},
     {"offsetof", b_offsetof, METH_VARARGS},
     {"string", b_string, METH_VARARGS},
     {"get_errno", b_get_errno, METH_NOARGS},
diff --git a/cffi/api.py b/cffi/api.py
--- a/cffi/api.py
+++ b/cffi/api.py
@@ -93,7 +93,7 @@
                 self._parsed_types[cdecl] = btype
                 return btype
         else:
-            return self._backend.typeof_instance(cdecl)
+            return self._backend.typeof(cdecl)
 
     def sizeof(self, cdecl):
         """Return the size in bytes of the argument.  It can be a
@@ -101,9 +101,9 @@
         """
         if isinstance(cdecl, (str, unicode)):
             BType = self.typeof(cdecl)
-            return self._backend.sizeof_type(BType)
+            return self._backend.sizeof(BType)
         else:
-            return self._backend.sizeof_instance(cdecl)
+            return self._backend.sizeof(cdecl)
 
     def alignof(self, cdecl):
         """Return the natural alignment size in bytes of the C type
@@ -182,7 +182,7 @@
         """ Verify that the current ffi signatures compile on this machine
         """
         from .verifier import Verifier
-        return Verifier().verify(self, preamble, **kwargs)
+        return Verifier(self).verify(preamble, **kwargs)
 
 def _make_ffi_library(ffi, libname):
     name = libname
diff --git a/cffi/backend_ctypes.py b/cffi/backend_ctypes.py
--- a/cffi/backend_ctypes.py
+++ b/cffi/backend_ctypes.py
@@ -760,13 +760,12 @@
         p = ctypes.cast(bptr._as_ctype_ptr, ctypes.POINTER(ctypes.c_char))
         return ''.join([p[i] for i in range(length)])
 
-    def sizeof_type(self, BType):
-        assert issubclass(BType, CTypesData)
-        return BType._get_size()
-
-    def sizeof_instance(self, cdata):
-        assert isinstance(cdata, CTypesData)
-        return cdata._get_size_of_instance()
+    def sizeof(self, cdata_or_BType):
+        if isinstance(cdata_or_BType, CTypesData):
+            return cdata_or_BType._get_size_of_instance()
+        else:
+            assert issubclass(cdata_or_BType, CTypesData)
+            return cdata_or_BType._get_size()
 
     def alignof(self, BType):
         assert issubclass(BType, CTypesData)
@@ -785,7 +784,7 @@
     def callback(self, BType, source):
         return BType(source)
 
-    typeof_instance = type
+    typeof = type
 
 
 class CTypesLibrary(object):
diff --git a/cffi/cparser.py b/cffi/cparser.py
--- a/cffi/cparser.py
+++ b/cffi/cparser.py
@@ -300,7 +300,7 @@
                 nextenumvalue += 1
             enumvalues = tuple(enumvalues) 
             tp = model.EnumType(name, enumerators, enumvalues)
-            self._declarations[key] = tp
+            self._declare(key, tp)
         else:   # opaque enum
             enumerators = ()
             enumvalues = ()
diff --git a/cffi/model.py b/cffi/model.py
--- a/cffi/model.py
+++ b/cffi/model.py
@@ -170,9 +170,20 @@
         return ffi._backend.new_struct_type(self.name)
 
     def verifier_declare_struct(self, verifier, name):
-        if self.fldnames is None:
-            assert not self.partial
-            return
+        if self.partial:
+            self.verifier_decl_partial(verifier, name)
+        else:
+            self.verifier_decl_notpartial(verifier, name)
+
+    def verifier_decl_notpartial(self, verifier, name):
+        if self.fldnames is None:    # not partial, but fully opaque:
+            return                   # cannot really test even for existence
+        struct = verifier.ffi._get_cached_btype(self)
+        verifier.write('__sameconstant__(sizeof(struct %s), %d)' % (
+            name, verifier.ffi.sizeof(struct)))
+
+    def verifier_decl_partial(self, verifier, name):
+        assert self.fldnames is not None
         verifier.write('{')
         verifier.write('struct __aligncheck__ { char x; struct %s y; };' %
                        self.name)
diff --git a/cffi/verifier.py b/cffi/verifier.py
--- a/cffi/verifier.py
+++ b/cffi/verifier.py
@@ -3,6 +3,9 @@
 
 class Verifier(object):
 
+    def __init__(self, ffi):
+        self.ffi = ffi
+
     def write(self, what):
         print >> self.f, '  ' + what
 
@@ -14,25 +17,30 @@
             print >> self.f, '  printf("%s\\n", %s);' % (
                 what, ', '.join(args))
 
-    def verify(self, ffi, preamble, **kwargs):
+    def verify(self, preamble, **kwargs):
         tst_file_base = ffiplatform._get_test_file_base()
         self.has_printf = False
         with open(tst_file_base + '.c', 'w') as f:
-            f.write('#include <stdio.h>\n')
-            f.write('#include <stdint.h>\n')
-            f.write('#include <stddef.h>\n')
-            f.write('#include <unistd.h>\n')
+            f.write('#include <stdio.h>\n'
+                    '#include <stdint.h>\n'
+                    '#include <stddef.h>\n'
+                    '#include <unistd.h>\n'
+                    '\n'
+                    '#define __sameconstant__(x, y) \\\n'
+                    '  { int result[1-2*((x)-(y))*((x)-(y))]; }\n'
+                    '\n'
+                    )
             f.write(preamble + "\n\n")
             f.write('int main() {\n')
             self.f = f
-            for name, tp in ffi._parser._declarations.iteritems():
+            for name, tp in self.ffi._parser._declarations.iteritems():
                 kind, realname = name.split(' ', 1)
                 method = getattr(tp, 'verifier_declare_' + kind)
                 method(self, realname)
             del self.f
             f.write('  return 0;\n')
             f.write('}\n')
-        err = os.system('gcc -Werror -c %s.c -o %s.o' %
+        err = os.system('gcc -Werror -S %s.c -o %s.s' %
                         (tst_file_base, tst_file_base))
         if err:
             raise ffiplatform.VerificationError(
diff --git a/testing/backend_tests.py b/testing/backend_tests.py
--- a/testing/backend_tests.py
+++ b/testing/backend_tests.py
@@ -6,7 +6,7 @@
 SIZE_OF_LONG  = ctypes.sizeof(ctypes.c_long)
 SIZE_OF_SHORT = ctypes.sizeof(ctypes.c_short)
 SIZE_OF_PTR   = ctypes.sizeof(ctypes.c_void_p)
-SIZE_OF_WCHAR = ctypes.sizeof(ctypes.c_wchar)
+#SIZE_OF_WCHAR = ctypes.sizeof(ctypes.c_wchar)
 
 
 class BackendTests:
@@ -41,7 +41,7 @@
         self._test_int_type(ffi, 'ptrdiff_t', SIZE_OF_PTR, False)
         self._test_int_type(ffi, 'size_t', SIZE_OF_PTR, True)
         self._test_int_type(ffi, 'ssize_t', SIZE_OF_PTR, False)
-        self._test_int_type(ffi, 'wchar_t', SIZE_OF_WCHAR, True)
+        #self._test_int_type(ffi, 'wchar_t', SIZE_OF_WCHAR, True)
 
     def _test_int_type(self, ffi, c_decl, size, unsigned):
         if unsigned:
diff --git a/testing/test_cdata.py b/testing/test_cdata.py
--- a/testing/test_cdata.py
+++ b/testing/test_cdata.py
@@ -6,7 +6,7 @@
     def nonstandard_integer_types(self):
         return {}
 
-    def sizeof_type(self, name):
+    def sizeof(self, name):
         return 1
 
     def load_library(self, path):
diff --git a/testing/test_parsing.py b/testing/test_parsing.py
--- a/testing/test_parsing.py
+++ b/testing/test_parsing.py
@@ -5,7 +5,7 @@
     def nonstandard_integer_types(self):
         return {}
 
-    def sizeof_type(self, name):
+    def sizeof(self, name):
         return 1
 
     def load_library(self, name):
diff --git a/testing/test_verify.py b/testing/test_verify.py
--- a/testing/test_verify.py
+++ b/testing/test_verify.py
@@ -37,6 +37,25 @@
                                "typedef %s foo_t;" % real)
 
 
+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; };")
+    #
+    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)
+    #
+    # 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; };")
+
+
 def test_ffi_nonfull_struct():
     py.test.skip("XXX")
     ffi = FFI()


More information about the pypy-commit mailing list