Commits

Armin Rigo committed f738cfc

Finish hopefully the support for anonymous structs.

Comments (0)

Files changed (5)

         #
         # nested anonymous structs or unions end up here
         if isinstance(typenode, pycparser.c_ast.Struct):
-            return self._get_struct_union_enum_type('struct', typenode, name)
+            return self._get_struct_union_enum_type('struct', typenode, name,
+                                                    nested=True)
         if isinstance(typenode, pycparser.c_ast.Union):
-            return self._get_struct_union_enum_type('union', typenode, name)
+            return self._get_struct_union_enum_type('union', typenode, name,
+                                                    nested=True)
         #
         raise api.FFIError("bad or unsupported type declaration")
 
             return const or 'const' in typenode.quals
         return False
 
-    def _get_struct_union_enum_type(self, kind, type, name=None):
+    def _get_struct_union_enum_type(self, kind, type, name=None, nested=False):
         # 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
                 # XXX pycparser is inconsistent: 'names' should be a list
                 # of strings, but is sometimes just one string.  Use
                 # str.join() as a way to cope with both.
-                self._make_partial(tp)
+                self._make_partial(tp, nested)
                 continue
             if decl.bitsize is None:
                 bitsize = -1
             self._partial_length = False
             type = self._get_type(decl.type, partial_length_ok=True)
             if self._partial_length:
-                self._make_partial(tp)
+                self._make_partial(tp, nested)
+            if isinstance(type, model.StructType) and type.partial:
+                self._make_partial(tp, nested)
             fldnames.append(decl.name or '')
             fldtypes.append(type)
             fldbitsize.append(bitsize)
                                           % (tp,))
         return tp
 
-    def _make_partial(self, tp):
+    def _make_partial(self, tp, nested):
         if not isinstance(tp, model.StructType):
             raise api.CDefError("%s cannot be partial" % (tp,))
-        if not tp.has_c_name():
-            raise api.CDefError("%s is partial but has no C name" % (tp,))
+        if not tp.has_c_name() and not nested:
+            raise NotImplementedError("%s is partial but has no C name" %(tp,))
         tp.partial = True
 
     def _parse_constant(self, exprnode, partial_length_ok=False):
             else:
                 yield (name, type, bitsize)
 
+    def force_flatten(self):
+        # force the struct or union to have a declaration that lists
+        # directly all fields returned by enumfields(), flattening
+        # nested anonymous structs/unions.
+        names = []
+        types = []
+        bitsizes = []
+        for name, type, bitsize in self.enumfields():
+            names.append(name)
+            types.append(type)
+            bitsizes.append(bitsize)
+        self.fldnames = tuple(names)
+        self.fldtypes = tuple(types)
+        self.fldbitsize = tuple(bitsizes)
+
     def finish_backend_type(self, ffi):
         BType = self.new_btype(ffi)
         ffi._cached_btypes[self] = BType

cffi/vengine_cpy.py

             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
 

cffi/vengine_gen.py

                 fieldofs.append(x)
                 fieldsize.append(function(num+1))
                 num += 2
+            tp.force_flatten()
             assert len(fieldofs) == len(fieldsize) == len(tp.fldnames)
             tp.fixedlayout = fieldofs, fieldsize, totalsize, totalalignment
 

testing/test_verify.py

     # assert did not crash
 
 def test_nested_anonymous_struct_exact():
+    if sys.platform == 'win32':
+        py.test.skip("nested anonymous struct/union")
     ffi = FFI()
     ffi.cdef("""
         struct foo_s { struct { int a; char b; }; union { char c, d; }; };
         struct foo_s { struct { int a; char b; }; union { char c, d; }; };
     """)
     p = ffi.new("struct foo_s *")
-    assert ffi.sizeof(p) == 3 * ffi.sizeof("int")    # with alignment
+    assert ffi.sizeof(p[0]) == 3 * ffi.sizeof("int")    # with alignment
     p.a = 1234567
     p.b = 'X'
     p.c = 'Y'
     assert p.d == 'Y'
 
 def test_nested_anonymous_struct_exact_error():
+    if sys.platform == 'win32':
+        py.test.skip("nested anonymous struct/union")
     ffi = FFI()
     ffi.cdef("""
         struct foo_s { struct { int a; char b; }; union { char c, d; }; };
     py.test.raises(VerificationError, ffi.verify, """
         struct foo_s { struct { int a; char e, b; }; union { char c, d; }; };
     """)
+
+def test_nested_anonymous_struct_inexact_1():
+    ffi = FFI()
+    ffi.cdef("""
+        struct foo_s { struct { char b; ...; }; union { char c, d; }; };
+    """)
+    ffi.verify("""
+        struct foo_s { int a, padding; char c, d, b; };
+    """)
+    assert ffi.sizeof("struct foo_s") == 3 * ffi.sizeof("int")
+
+def test_nested_anonymous_struct_inexact_2():
+    ffi = FFI()
+    ffi.cdef("""
+        struct foo_s { union { char c, d; }; struct { int a; char b; }; ...; };
+    """)
+    ffi.verify("""
+        struct foo_s { int a, padding; char c, d, b; };
+    """)
+    assert ffi.sizeof("struct foo_s") == 3 * ffi.sizeof("int")