Armin Rigo avatar Armin Rigo committed 6372688

Still trying to fix up the ordering of building stuff. One test passes,
but one test still fails.

Comments (0)

Files changed (3)

         try:
             BType = self._cached_btypes[type]
         except KeyError:
-            BType = type.finish_backend_type(self)
-            BType2 = self._cached_btypes.setdefault(type, BType)
-            assert BType2 is BType
+            finishlist = []
+            BType = type.get_cached_btype(self, finishlist)
+            for type in finishlist:
+                type.finish_backend_type(self, finishlist)
         return BType
 
     def verify(self, source='', tmpdir=None, **kwargs):
     def has_c_name(self):
         return '$' not in self._get_c_name('')
 
+    def get_cached_btype(self, ffi, finishlist, can_delay=False):
+        try:
+            BType = ffi._cached_btypes[self]
+        except KeyError:
+            BType = self.build_backend_type(ffi, finishlist)
+            BType2 = ffi._cached_btypes.setdefault(self, BType)
+            assert BType2 is BType
+        return BType
+
     def __repr__(self):
         return '<%s>' % (self._get_c_name(''),)
 
     def _get_c_name(self, replace_with):
         return 'void' + replace_with
 
-    def finish_backend_type(self, ffi):
+    def build_backend_type(self, ffi, finishlist):
         return global_cache(self, ffi, 'new_void_type')
 
 void_type = VoidType()
     def is_float_type(self):
         return self.name in ('double', 'float')
 
-    def finish_backend_type(self, ffi):
+    def build_backend_type(self, ffi, finishlist):
         return global_cache(self, ffi, 'new_primitive_type', self.name)
 
 
     # a function, but not a pointer-to-function.  The backend has no
     # notion of such a type; it's used temporarily by parsing.
 
-    def finish_backend_type(self, ffi):
+    def build_backend_type(self, ffi, finishlist):
         from . import api
         raise api.CDefError("cannot render the type %r: it is a function "
                             "type, not a pointer-to-function type" % (self,))
     def _get_c_name(self, replace_with):
         return BaseFunctionType._get_c_name(self, '*'+replace_with)
 
-    def finish_backend_type(self, ffi):
-        result = ffi._get_cached_btype(self.result)
+    def build_backend_type(self, ffi, finishlist):
+        result = self.result.get_cached_btype(ffi, finishlist)
         args = []
         for tp in self.args:
-            args.append(ffi._get_cached_btype(tp))
+            args.append(tp.get_cached_btype(ffi, finishlist))
         return global_cache(self, ffi, 'new_function_type',
                             tuple(args), result, self.ellipsis)
 
     def _get_c_name(self, replace_with):
         return self.totype._get_c_name('* ' + replace_with)
 
-    def finish_backend_type(self, ffi):
-        BItem = ffi._get_cached_btype(self.totype)
+    def build_backend_type(self, ffi, finishlist):
+        BItem = self.totype.get_cached_btype(ffi, finishlist, can_delay=True)
         return global_cache(self, ffi, 'new_pointer_type', BItem)
 
 
     def _get_c_name(self, replace_with):
         return self.totype._get_c_name(' const * ' + replace_with)
 
-    def finish_backend_type(self, ffi):
-        BPtr = ffi._get_cached_btype(PointerType(self.totype))
+    def build_backend_type(self, ffi, finishlist):
+        BPtr = PointerType(self.totype).get_cached_btype(ffi, finishlist)
         return BPtr
 
 
             brackets = '[%d]' % self.length
         return self.item._get_c_name(replace_with + brackets)
 
-    def finish_backend_type(self, ffi):
-        BPtrItem = ffi._get_cached_btype(PointerType(self.item))
+    def build_backend_type(self, ffi, finishlist):
+        self.item.get_cached_btype(ffi, finishlist)   # force the item BType
+        BPtrItem = PointerType(self.item).get_cached_btype(ffi, finishlist)
         return global_cache(self, ffi, 'new_array_type', BPtrItem, self.length)
 
 
 
 class StructOrUnion(StructOrUnionOrEnum):
     fixedlayout = None
+    completed = False
 
     def __init__(self, name, fldnames, fldtypes, fldbitsize):
         self.name = name
         self.fldtypes = tuple(types)
         self.fldbitsize = tuple(bitsizes)
 
-    def finish_backend_type(self, ffi):
-        BType = self.new_btype(ffi)
-        ffi._cached_btypes[self] = BType
+    def get_cached_btype(self, ffi, finishlist, can_delay=False):
+        BType = StructOrUnionOrEnum.get_cached_btype(self, ffi, finishlist,
+                                                     can_delay)
+        if not can_delay:
+            self.finish_backend_type(ffi, finishlist)
+        return BType
+
+    def finish_backend_type(self, ffi, finishlist):
+        if self.completed:
+            if self.completed != 2:
+                raise NotImplementedError("recursive structure declaration "
+                                          "for '%s'" % (self.name,))
+            return
+        BType = ffi._cached_btypes[self]
         if self.fldtypes is None:
-            return BType    # not completing it: it's an opaque struct
+            return    # not completing it: it's an opaque struct
         #
-        fldtypes = tuple(ffi._get_cached_btype(tp) for tp in self.fldtypes)
+        self.completed = 1
+        fldtypes = tuple(tp.get_cached_btype(ffi, finishlist)
+                         for tp in self.fldtypes)
         #
         if self.fixedlayout is None:
             lst = list(zip(self.fldnames, fldtypes, self.fldbitsize))
                 #
                 if isinstance(ftype, ArrayType) and ftype.length is None:
                     # fix the length to match the total size
-                    BItemType = ffi._get_cached_btype(ftype.item)
+                    BItemType = ftype.item.get_cached_btype(ffi, finishlist)
                     nlen, nrest = divmod(fsize, ffi.sizeof(BItemType))
                     if nrest != 0:
                         self._verification_error(
                     ftype = ftype.resolve_length(nlen)
                     self.fldtypes = (self.fldtypes[:i] + (ftype,) +
                                      self.fldtypes[i+1:])
-                    BArrayType = ffi._get_cached_btype(ftype)
+                    BArrayType = ftype.get_cached_btype(ffi, finishlist)
                     fldtypes = (fldtypes[:i] + (BArrayType,) +
                                 fldtypes[i+1:])
                     continue
             lst = list(zip(self.fldnames, fldtypes, self.fldbitsize, fieldofs))
             ffi._backend.complete_struct_or_union(BType, lst, self,
                                                   totalsize, totalalignment)
-        return BType
+        self.completed = 2
 
     def _verification_error(self, msg):
         from .ffiplatform import VerificationError
             from . import ffiplatform
             raise ffiplatform.VerificationMissing(self._get_c_name(''))
 
-    def new_btype(self, ffi):
+    def build_backend_type(self, ffi, finishlist):
         self.check_not_partial()
+        finishlist.append(self)
         return ffi._backend.new_struct_type(self.name)
 
 
 class UnionType(StructOrUnion):
     kind = 'union'
 
-    def new_btype(self, ffi):
+    def build_backend_type(self, ffi, finishlist):
+        finishlist.append(self)
         return ffi._backend.new_union_type(self.name)
 
 
             from . import ffiplatform
             raise ffiplatform.VerificationMissing(self._get_c_name(''))
 
-    def finish_backend_type(self, ffi):
+    def build_backend_type(self, ffi, finishlist):
         self.check_not_partial()
         return ffi._backend.new_enum_type(self.name, self.enumerators,
                                           self.enumvalues)

testing/backend_tests.py

     def test_use_own_bool(self):
         ffi = FFI(backend=self.Backend())
         ffi.cdef("""typedef int bool;""")
+
+    def test_ordering_bug1(self):
+        ffi = FFI(backend=self.Backend())
+        ffi.cdef("""
+            struct foo_s {
+                struct bar_s *p;
+            };
+            struct bar_s {
+                struct foo_s foo;
+            };
+        """)
+        q = ffi.new("struct foo_s *")
+        bar = ffi.new("struct bar_s *")
+        q.p = bar
+        assert q.p.foo.p == ffi.NULL
+
+    def test_ordering_bug2(self):
+        ffi = FFI(backend=self.Backend())
+        ffi.cdef("""
+            struct foo_s {
+                struct bar_s (*p)[3];
+            };
+            struct bar_s {
+                struct foo_s foo;
+            };
+        """)
+        q = ffi.new("struct foo_s *")
+        bar = ffi.new("struct bar_s *")
+        q.p[1] = bar
+        assert q.p[1].foo.p[1] == ffi.NULL
Tip: Filter by directory path e.g. /media app.js to search for public/media/app.js.
Tip: Use camelCasing e.g. ProjME to search for ProjectModifiedEvent.java.
Tip: Filter by extension type e.g. /repo .js to search for all .js files in the /repo directory.
Tip: Separate your search with spaces e.g. /ssh pom.xml to search for src/ssh/pom.xml.
Tip: Use ↑ and ↓ arrow keys to navigate and return to view the file.
Tip: You can also navigate files with Ctrl+j (next) and Ctrl+k (previous) and view the file with Ctrl+o.
Tip: You can also navigate files with Alt+j (next) and Alt+k (previous) and view the file with Alt+o.