[pypy-commit] cffi default: A non-passing test, a bit annoying: how are we supposed to fill in

arigo noreply at buildbot.pypy.org
Sun Jun 17 10:17:35 CEST 2012


Author: Armin Rigo <arigo at tunes.org>
Branch: 
Changeset: r396:874950ec032e
Date: 2012-06-16 18:42 +0200
http://bitbucket.org/cffi/cffi/changeset/874950ec032e/

Log:	A non-passing test, a bit annoying: how are we supposed to fill in
	the anonymous struct types declared in the middle of a typedef?

diff --git a/cffi/cparser.py b/cffi/cparser.py
--- a/cffi/cparser.py
+++ b/cffi/cparser.py
@@ -87,7 +87,7 @@
                         and decl.type.type.names == ['__dotdotdot__']):
                     realtype = model.OpaqueType(decl.name)
                 else:
-                    realtype = self._get_type(decl.type)
+                    realtype = self._get_type(decl.type, name=decl.name)
                 self._declare('typedef ' + decl.name, realtype)
             else:
                 raise api.CDefError("unrecognized construct", decl)
@@ -195,11 +195,11 @@
             #
             if isinstance(type, pycparser.c_ast.Struct):
                 # 'struct foobar'
-                return self._get_struct_or_union_type('struct', type, typenode)
+                return self._get_struct_or_union_type('struct', type, name)
             #
             if isinstance(type, pycparser.c_ast.Union):
                 # 'union foobar'
-                return self._get_struct_or_union_type('union', type, typenode)
+                return self._get_struct_or_union_type('union', type, name)
             #
             if isinstance(type, pycparser.c_ast.Enum):
                 # 'enum foobar'
@@ -242,7 +242,7 @@
             return const or 'const' in typenode.quals
         return False
 
-    def _get_struct_or_union_type(self, kind, type, typenode=None):
+    def _get_struct_or_union_type(self, kind, type, name=None):
         # First, a level of caching on the exact 'type' node of the AST.
         # This is obscure, but needed because pycparser "unrolls" declarations
         # such as "typedef struct { } foo_t, *foo_p" and we end up with
@@ -264,14 +264,15 @@
         # with no caching; in this case, the fields are either specified
         # right now or never.
         #
+        force_name = name
         name = type.name
         #
         # get the type or create it if needed
         if name is None:
-            # 'typenode' is only used to guess a more readable name for
+            # 'force_name' is used to guess a more readable name for
             # anonymous structs, for the common case "typedef struct { } foo".
-            if typenode is not None and isinstance(typenode.declname, str):
-                explicit_name = '$%s' % typenode.declname
+            if force_name is not None:
+                explicit_name = '$%s' % force_name
             else:
                 self._anonymous_counter += 1
                 explicit_name = '$%d' % self._anonymous_counter
@@ -290,6 +291,7 @@
                 raise AssertionError("kind = %r" % (kind,))
             if name is not None:
                 self._declare(key, tp)
+        tp.forcename = tp.forcename or force_name
         #
         self._structnode2type[type] = tp
         #
diff --git a/cffi/model.py b/cffi/model.py
--- a/cffi/model.py
+++ b/cffi/model.py
@@ -1,8 +1,17 @@
 
 class BaseType(object):
 
+    def get_c_name(self, replace_with=''):
+        result = self._get_c_name(replace_with)
+        if '$' in result:
+            from .ffiplatform import VerificationError
+            raise VerificationError(
+                "cannot generate '%s' in a C file: unknown type name"
+                % (result,))
+        return result
+
     def __repr__(self):
-        return '<%s>' % (self.get_c_name(),)
+        return '<%s>' % (self._get_c_name(''),)
 
     def __eq__(self, other):
         return (self.__class__ == other.__class__ and
@@ -30,7 +39,7 @@
 class VoidType(BaseType):
     _attrs_ = ()
 
-    def get_c_name(self, replace_with=''):
+    def _get_c_name(self, replace_with):
         return 'void' + replace_with
 
     def new_backend_type(self, ffi):
@@ -45,7 +54,7 @@
     def __init__(self, name):
         self.name = name
 
-    def get_c_name(self, replace_with=''):
+    def _get_c_name(self, replace_with):
         return self.name + replace_with
 
     def is_char_type(self):
@@ -71,13 +80,13 @@
         self.result = result
         self.ellipsis = ellipsis
 
-    def get_c_name(self, replace_with=''):
-        reprargs = [arg.get_c_name() for arg in self.args]
+    def _get_c_name(self, replace_with):
+        reprargs = [arg._get_c_name('') for arg in self.args]
         if self.ellipsis:
             reprargs.append('...')
         reprargs = reprargs or ['void']
         replace_with = '(*%s)(%s)' % (replace_with, ', '.join(reprargs))
-        return self.result.get_c_name(replace_with)
+        return self.result._get_c_name(replace_with)
 
     def prepare_backend_type(self, ffi):
         args = [ffi._get_cached_btype(self.result)]
@@ -95,8 +104,8 @@
     def __init__(self, totype):
         self.totype = totype
 
-    def get_c_name(self, replace_with=''):
-        return self.totype.get_c_name('* ' + replace_with)
+    def _get_c_name(self, replace_with):
+        return self.totype._get_c_name('* ' + replace_with)
 
     def prepare_backend_type(self, ffi):
         return (ffi._get_cached_btype(self.totype),)
@@ -107,8 +116,8 @@
 
 class ConstPointerType(PointerType):
 
-    def get_c_name(self, replace_with=''):
-        return self.totype.get_c_name(' const * ' + replace_with)
+    def _get_c_name(self, replace_with):
+        return self.totype._get_c_name(' const * ' + replace_with)
 
     def prepare_backend_type(self, ffi):
         return (ffi._get_cached_btype(PointerType(self.totype)),)
@@ -127,12 +136,12 @@
     def resolve_length(self, newlength):
         return ArrayType(self.item, newlength)
 
-    def get_c_name(self, replace_with=''):
+    def _get_c_name(self, replace_with):
         if self.length is None:
             brackets = '[]'
         else:
             brackets = '[%d]' % self.length
-        return self.item.get_c_name(replace_with + brackets)
+        return self.item._get_c_name(replace_with + brackets)
 
     def prepare_backend_type(self, ffi):
         return (ffi._get_cached_btype(PointerType(self.item)),)
@@ -143,6 +152,7 @@
 
 class StructOrUnion(BaseType):
     _attrs_ = ('name',)
+    forcename = None
     fixedlayout = None
 
     def __init__(self, name, fldnames, fldtypes, fldbitsize):
@@ -151,8 +161,9 @@
         self.fldtypes = fldtypes
         self.fldbitsize = fldbitsize
 
-    def get_c_name(self, replace_with=''):
-        return '%s %s%s' % (self.kind, self.name, replace_with)
+    def _get_c_name(self, replace_with):
+        name = self.forcename or '%s %s' % (self.kind, self.name)
+        return name + replace_with
 
     def prepare_backend_type(self, ffi):
         BType = self.get_btype(ffi)
@@ -216,7 +227,7 @@
     def check_not_partial(self):
         if self.partial and self.fixedlayout is None:
             from . import ffiplatform
-            raise ffiplatform.VerificationMissing(self.get_c_name())
+            raise ffiplatform.VerificationMissing(self._get_c_name(''))
 
     def get_btype(self, ffi):
         self.check_not_partial()
@@ -239,13 +250,13 @@
         self.enumerators = enumerators
         self.enumvalues = enumvalues
 
-    def get_c_name(self, replace_with=''):
+    def _get_c_name(self, replace_with):
         return 'enum %s%s' % (self.name, replace_with)
 
     def check_not_partial(self):
         if self.partial:
             from . import ffiplatform
-            raise ffiplatform.VerificationMissing(self.get_c_name())
+            raise ffiplatform.VerificationMissing(self._get_c_name(''))
 
     def new_backend_type(self, ffi):
         self.check_not_partial()
@@ -259,7 +270,7 @@
     def __init__(self, name):
         self.name = name
 
-    def get_c_name(self, replace_with=''):
+    def _get_c_name(self, replace_with):
         return self.name + replace_with
 
     def new_backend_type(self, ffi):
diff --git a/testing/test_parsing.py b/testing/test_parsing.py
--- a/testing/test_parsing.py
+++ b/testing/test_parsing.py
@@ -1,5 +1,5 @@
 import py, sys
-from cffi import FFI, CDefError
+from cffi import FFI, CDefError, VerificationError
 
 class FakeBackend(object):
 
@@ -149,3 +149,18 @@
     e = py.test.raises(CDefError, ffi.cdef, "#define FOO 42")
     assert str(e.value) == \
            'only supports the syntax "#define FOO ..." for now (literally)'
+
+def test_unnamed_struct():
+    ffi = FFI(backend=FakeBackend())
+    ffi.cdef("typedef struct { int x; } foo_t;\n"
+             "typedef struct { int y; } *bar_p;\n")
+    assert 'typedef foo_t' in ffi._parser._declarations
+    assert 'typedef bar_p' in ffi._parser._declarations
+    #assert 'structdef foo_t' in ffi._parser._declarations ...
+    #assert 'structdef bar_p' in ffi._parser._declarations
+    type_foo = ffi._parser.parse_type("foo_t")
+    type_bar = ffi._parser.parse_type("bar_p").totype
+    assert repr(type_foo) == "<foo_t>"
+    assert repr(type_bar) == "<struct $1>"
+    py.test.raises(VerificationError, type_bar.get_c_name)
+    assert type_foo.get_c_name() == "foo_t"
diff --git a/testing/test_verify.py b/testing/test_verify.py
--- a/testing/test_verify.py
+++ b/testing/test_verify.py
@@ -452,3 +452,14 @@
     ffi = FFI(backend=CTypesBackend())
     ffi.cdef("int a;")
     py.test.raises(NotImplementedError, ffi.verify, "int a;")
+
+def test_call_with_struct_ptr():
+    ffi = FFI()
+    ffi.cdef("typedef struct { int x; ...; } foo_t; int foo(foo_t *);")
+    lib = ffi.verify("""
+        typedef struct { int y, x; } foo_t;
+        static int foo(foo_t *f) { return f->x * 7; }
+    """)
+    f = ffi.new("foo_t")
+    f.x = 6
+    assert lib.foo(f) == 42


More information about the pypy-commit mailing list