Commits

Antonio Cuni  committed 5881f06

split the _StructDescr initialization in two phases: instantiation and field definition. This allow to get its ffitype before it's actually complete, in case it's necessary to take a pointer to it

  • Participants
  • Parent commits 0d600e8
  • Branches ffistruct

Comments (0)

Files changed (3)

File pypy/module/_ffi/interp_ffitype.py

 from pypy.interpreter.baseobjspace import Wrappable
 from pypy.interpreter.typedef import TypeDef, interp_attrproperty
 from pypy.interpreter.gateway import interp2app
+from pypy.interpreter.error import OperationError
 
 class W_FFIType(Wrappable):
 
             raise ValueError("Operation not permitted on an incomplete type")
         return self._ffitype
 
+    def set_ffitype(self, ffitype):
+        if self._ffitype:
+            raise ValueError("The _ffitype is already set")
+        self._ffitype = ffitype
+
     def descr_deref_pointer(self, space):
         if self.w_pointer_to is None:
             return space.w_None
         return self.w_pointer_to
 
     def descr_sizeof(self, space):
-        return space.wrap(self.sizeof())
+        try:
+            return space.wrap(self.sizeof())
+        except ValueError:
+            msg = "Operation not permitted on an incomplete type"
+            raise OperationError(space.w_ValueError, space.wrap(msg))
 
     def sizeof(self):
         return intmask(self.get_ffitype().c_size)

File pypy/module/_ffi/interp_struct.py

 
 class W__StructDescr(Wrappable):
 
-    def __init__(self, space, name, fields_w, ffistruct):
+    def __init__(self, space, name):
         self.space = space
-        self.ffistruct = ffistruct
-        self.w_ffitype = W_FFIType('struct %s' % name, ffistruct.ffistruct, None)
+        self.w_ffitype = W_FFIType('struct %s' % name, clibffi.FFI_TYPE_NULL, None)
+        self.fields_w = None
+        self.name2w_field = {}
+
+    def define_fields(self, space, w_fields):
+        if self.fields_w is not None:
+            raise operationerrfmt(space.w_ValueError,
+                                  "%s's fields has already been defined",
+                                  self.w_ffitype.name)
+        space = self.space
+        fields_w = space.fixedview(w_fields)
+        # note that the fields_w returned by compute_size_and_alignement has a
+        # different annotation than the original: list(W_Root) vs list(W_Field)
+        size, alignment, fields_w = compute_size_and_alignement(space, fields_w)
         self.fields_w = fields_w
-        self.name2w_field = {}
+        field_types = [] # clibffi's types
         for w_field in fields_w:
+            field_types.append(w_field.w_ffitype.get_ffitype())
             self.name2w_field[w_field.name] = w_field
+        self.ffistruct = clibffi.make_struct_ffitype_e(size, alignment, field_types)
+        self.w_ffitype.set_ffitype(self.ffistruct.ffistruct)
 
     def allocate(self, space):
+        if self.fields_w is None:
+            raise operationerrfmt(space.w_ValueError, "%s has an incomplete type",
+                                  self.w_ffitype.name)
         return W__StructInstance(self)
 
     @jit.elidable_promote('0')
 
 
 @unwrap_spec(name=str)
-def descr_new_structdescr(space, w_type, name, w_fields):
-    fields_w = space.fixedview(w_fields)
-    # note that the fields_w returned by compute_size_and_alignement has a
-    # different annotation than the original: list(W_Root) vs list(W_Field)
-    size, alignment, fields_w = compute_size_and_alignement(space, fields_w)
-    field_types = [] # clibffi's types
-    for w_field in fields_w:
-        field_types.append(w_field.w_ffitype.get_ffitype())
-    ffistruct = clibffi.make_struct_ffitype_e(size, alignment, field_types)
-    return W__StructDescr(space, name, fields_w, ffistruct)
+def descr_new_structdescr(space, w_type, name, w_fields=None):
+    descr = W__StructDescr(space, name)
+    if w_fields is not space.w_None:
+        descr.define_fields(w_fields)
+    return descr
 
 def round_up(size, alignment):
     return (size + alignment - 1) & -alignment
     '_StructDescr',
     __new__ = interp2app(descr_new_structdescr),
     ffitype = interp_attrproperty('w_ffitype', W__StructDescr),
+    define_fields = interp2app(W__StructDescr.define_fields),
     allocate = interp2app(W__StructDescr.allocate),
     )
 

File pypy/module/_ffi/test/test_struct.py

         mem = self.read_raw_mem(struct.getaddr(), 'c_float', 1)
         assert mem == [123.5]
 
+    def test_define_fields(self):
+        from _ffi import _StructDescr, Field, types
+        longsize = types.slong.sizeof()
+        fields = [
+            Field('x', types.slong),
+            Field('y', types.slong),
+            ]
+        descr = _StructDescr('foo')
+        assert descr.ffitype.name == 'struct foo'
+        assert repr(descr.ffitype) == '<ffi type struct foo (incomplete)>'
+        raises(ValueError, "descr.ffitype.sizeof()")
+        raises(ValueError, "descr.allocate()")
+        #
+        descr.define_fields(fields)
+        assert repr(descr.ffitype) == '<ffi type struct foo>'
+        assert descr.ffitype.sizeof() == longsize*2
+        raises(ValueError, "descr.define_fields(fields)")
+        
+
     def test_compute_shape(self):
         from _ffi import Structure, Field, types
         class Point(Structure):