Author: Armin Rigo <[email protected]>
Branch: 
Changeset: r64666:5ae00b3c5ed9
Date: 2013-05-30 21:28 +0200
http://bitbucket.org/pypy/pypy/changeset/5ae00b3c5ed9/

Log:    Update to cffi/f49f86a66a15.

diff --git a/lib_pypy/cffi/vengine_cpy.py b/lib_pypy/cffi/vengine_cpy.py
--- a/lib_pypy/cffi/vengine_cpy.py
+++ b/lib_pypy/cffi/vengine_cpy.py
@@ -156,6 +156,9 @@
         class FFILibrary(object):
             _cffi_python_module = module
             _cffi_ffi = self.ffi
+            _cffi_dir = []
+            def __dir__(self):
+                return FFILibrary._cffi_dir + list(self.__dict__)
         library = FFILibrary()
         module._cffi_setup(lst, ffiplatform.VerificationError, library)
         #
@@ -701,7 +704,8 @@
             return ptr[0]
         def setter(library, value):
             ptr[0] = value
-        setattr(library.__class__, name, property(getter, setter))
+        setattr(type(library), name, property(getter, setter))
+        type(library)._cffi_dir.append(name)
 
     # ----------
 
diff --git a/lib_pypy/cffi/vengine_gen.py b/lib_pypy/cffi/vengine_gen.py
--- a/lib_pypy/cffi/vengine_gen.py
+++ b/lib_pypy/cffi/vengine_gen.py
@@ -74,6 +74,9 @@
         class FFILibrary(types.ModuleType):
             _cffi_generic_module = module
             _cffi_ffi = self.ffi
+            _cffi_dir = []
+            def __dir__(self):
+                return FFILibrary._cffi_dir
         library = FFILibrary("")
         #
         # finally, call the loaded_gen_xxx() functions.  This will set
@@ -168,21 +171,22 @@
             newfunction = self._load_constant(False, tp, name, module)
         else:
             indirections = []
-            if any(isinstance(type, model.StructOrUnion) for type in tp.args):
+            if any(isinstance(typ, model.StructOrUnion) for typ in tp.args):
                 indirect_args = []
-                for i, type in enumerate(tp.args):
-                    if isinstance(type, model.StructOrUnion):
-                        type = model.PointerType(type)
-                        indirections.append((i, type))
-                    indirect_args.append(type)
+                for i, typ in enumerate(tp.args):
+                    if isinstance(typ, model.StructOrUnion):
+                        typ = model.PointerType(typ)
+                        indirections.append((i, typ))
+                    indirect_args.append(typ)
                 tp = model.FunctionPtrType(tuple(indirect_args),
                                            tp.result, tp.ellipsis)
             BFunc = self.ffi._get_cached_btype(tp)
             wrappername = '_cffi_f_%s' % name
             newfunction = module.load_function(BFunc, wrappername)
-            for i, type in indirections:
-                newfunction = self._make_struct_wrapper(newfunction, i, type)
+            for i, typ in indirections:
+                newfunction = self._make_struct_wrapper(newfunction, i, typ)
         setattr(library, name, newfunction)
+        type(library)._cffi_dir.append(name)
 
     def _make_struct_wrapper(self, oldfunc, i, tp):
         backend = self.ffi._backend
@@ -390,6 +394,7 @@
         is_int = isinstance(tp, model.PrimitiveType) and tp.is_integer_type()
         value = self._load_constant(is_int, tp, name, module)
         setattr(library, name, value)
+        type(library)._cffi_dir.append(name)
 
     # ----------
     # enums
@@ -437,6 +442,7 @@
     def _loaded_gen_enum(self, tp, name, module, library):
         for enumerator, enumvalue in zip(tp.enumerators, tp.enumvalues):
             setattr(library, enumerator, enumvalue)
+            type(library)._cffi_dir.append(enumerator)
 
     # ----------
     # macros: for now only for integers
@@ -450,6 +456,7 @@
     def _loaded_gen_macro(self, tp, name, module, library):
         value = self._load_constant(True, tp, name, module)
         setattr(library, name, value)
+        type(library)._cffi_dir.append(name)
 
     # ----------
     # global variables
@@ -475,6 +482,7 @@
                 BArray = self.ffi._get_cached_btype(tp)
                 value = self.ffi.cast(BArray, value)
             setattr(library, name, value)
+            type(library)._cffi_dir.append(name)
             return
         # remove ptr=<cdata 'int *'> from the library instance, and replace
         # it by a property on the class, which reads/writes into ptr[0].
@@ -486,7 +494,8 @@
             return ptr[0]
         def setter(library, value):
             ptr[0] = value
-        setattr(library.__class__, name, property(getter, setter))
+        setattr(type(library), name, property(getter, setter))
+        type(library)._cffi_dir.append(name)
 
 cffimod_header = r'''
 #include <stdio.h>
diff --git a/pypy/module/_cffi_backend/newtype.py 
b/pypy/module/_cffi_backend/newtype.py
--- a/pypy/module/_cffi_backend/newtype.py
+++ b/pypy/module/_cffi_backend/newtype.py
@@ -131,13 +131,12 @@
                                         " struct or union ctype"))
 
     is_union = isinstance(w_ctype, ctypestruct.W_CTypeUnion)
-    maxsize = 1
     alignment = 1
-    offset = 0
+    boffset = 0         # this number is in *bits*, not bytes!
+    boffsetmax = 0      # the maximum value of boffset, in bits too
     fields_w = space.listview(w_fields)
     fields_list = []
     fields_dict = {}
-    prev_bit_position = 0
     custom_field_pos = False
 
     for w_field in fields_w:
@@ -161,92 +160,129 @@
                     "field '%s.%s' has ctype '%s' of unknown size",
                                   w_ctype.name, fname, ftype.name)
         #
+        if is_union:
+            boffset = 0         # reset each field at offset 0
+        #
+        # update the total alignment requirement, but skip it if the
+        # field is an anonymous bitfield
         falign = ftype.alignof()
-        if alignment < falign:
+        if alignment < falign and (fbitsize < 0 or fname != ''):
             alignment = falign
         #
-        if foffset < 0:
+        if fbitsize < 0:
+            # not a bitfield: common case
+
+            if isinstance(ftype, ctypearray.W_CTypeArray) and ftype.length==0:
+                bs_flag = ctypestruct.W_CField.BS_EMPTY_ARRAY
+            else:
+                bs_flag = ctypestruct.W_CField.BS_REGULAR
+
             # align this field to its own 'falign' by inserting padding
-            offset = (offset + falign - 1) & ~(falign - 1)
+            boffset = (boffset + falign*8-1) & ~(falign*8-1)
+
+            if foffset >= 0:
+                # a forced field position: ignore the offset just computed,
+                # except to know if we must set 'custom_field_pos'
+                custom_field_pos |= (boffset != foffset * 8)
+                boffset = foffset * 8
+
+            if (fname == '' and
+                    isinstance(ftype, ctypestruct.W_CTypeStructOrUnion)):
+                # a nested anonymous struct or union
+                srcfield2names = {}
+                for name, srcfld in ftype.fields_dict.items():
+                    srcfield2names[srcfld] = name
+                for srcfld in ftype.fields_list:
+                    fld = srcfld.make_shifted(boffset // 8)
+                    fields_list.append(fld)
+                    try:
+                        fields_dict[srcfield2names[srcfld]] = fld
+                    except KeyError:
+                        pass
+                # always forbid such structures from being passed by value
+                custom_field_pos = True
+            else:
+                # a regular field
+                fld = ctypestruct.W_CField(ftype, boffset // 8, bs_flag, -1)
+                fields_list.append(fld)
+                fields_dict[fname] = fld
+
+            boffset += ftype.size * 8
+
         else:
-            # a forced field position: ignore the offset just computed,
-            # except to know if we must set 'custom_field_pos'
-            custom_field_pos |= (offset != foffset)
-            offset = foffset
-        #
-        if fbitsize < 0 or (
-                fbitsize == 8 * ftype.size and not
-                isinstance(ftype, ctypeprim.W_CTypePrimitiveCharOrUniChar)):
-            fbitsize = -1
-            if isinstance(ftype, ctypearray.W_CTypeArray) and ftype.length == 
0:
-                bitshift = ctypestruct.W_CField.BS_EMPTY_ARRAY
+            # this is the case of a bitfield
+
+            if foffset >= 0:
+                raise operationerrfmt(space.w_TypeError,
+                                      "field '%s.%s' is a bitfield, "
+                                      "but a fixed offset is specified",
+                                      w_ctype.name, fname)
+
+            if not (isinstance(ftype, ctypeprim.W_CTypePrimitiveSigned) or
+                    isinstance(ftype, ctypeprim.W_CTypePrimitiveUnsigned) or
+                    isinstance(ftype,ctypeprim.W_CTypePrimitiveCharOrUniChar)):
+                raise operationerrfmt(space.w_TypeError,
+                                      "field '%s.%s' declared as '%s' "
+                                      "cannot be a bit field",
+                                      w_ctype.name, fname, ftype.name)
+            if fbitsize > 8 * ftype.size:
+                raise operationerrfmt(space.w_TypeError,
+                                      "bit field '%s.%s' is declared '%s:%d',"
+                                      " which exceeds the width of the type",
+                                      w_ctype.name, fname,
+                                      ftype.name, fbitsize)
+
+            # compute the starting position of the theoretical field
+            # that covers a complete 'ftype', inside of which we will
+            # locate the real bitfield
+            field_offset_bytes = boffset // 8
+            field_offset_bytes &= ~(falign - 1)
+
+            if fbitsize == 0:
+                if fname != '':
+                    raise operationerrfmt(space.w_TypeError,
+                                          "field '%s.%s' is declared with :0",
+                                          w_ctype.name, fname)
+                if boffset > field_offset_bytes * 8:
+                    field_offset_bytes += falign
+                    assert boffset < field_offset_bytes * 8
+                boffset = field_offset_bytes * 8
             else:
-                bitshift = ctypestruct.W_CField.BS_REGULAR
-            prev_bit_position = 0
-        else:
-            if (not (isinstance(ftype, ctypeprim.W_CTypePrimitiveSigned) or
-                     isinstance(ftype, ctypeprim.W_CTypePrimitiveUnsigned) or
-                     isinstance(ftype, ctypeprim.W_CTypePrimitiveChar)) or
-                fbitsize == 0 or
-                fbitsize > 8 * ftype.size):
-                raise operationerrfmt(space.w_TypeError,
-                                      "invalid bit field '%s'", fname)
-            if prev_bit_position > 0:
-                prev_field = fields_list[-1]
-                assert prev_field.bitshift >= 0
-                if prev_field.ctype.size != ftype.size:
-                    raise OperationError(space.w_NotImplementedError,
-                        space.wrap("consecutive bit fields should be "
-                                   "declared with a same-sized type"))
-                if prev_bit_position + fbitsize > 8 * ftype.size:
-                    prev_bit_position = 0
+                # Can the field start at the offset given by 'boffset'?  It
+                # can if it would entirely fit into an aligned ftype field.
+                bits_already_occupied = boffset - (field_offset_bytes * 8)
+
+                if bits_already_occupied + fbitsize > 8 * ftype.size:
+                    # it would not fit, we need to start at the next
+                    # allowed position
+                    field_offset_bytes += falign
+                    assert boffset < field_offset_bytes * 8
+                    boffset = field_offset_bytes * 8
+                    bitshift = 0
                 else:
-                    # we can share the same field as 'prev_field'
-                    offset = prev_field.offset
-            bitshift = prev_bit_position
-            if not is_union:
-                prev_bit_position += fbitsize
-        #
-        if (len(fname) == 0 and
-            isinstance(ftype, ctypestruct.W_CTypeStructOrUnion)):
-            # a nested anonymous struct or union
-            srcfield2names = {}
-            for name, srcfld in ftype.fields_dict.items():
-                srcfield2names[srcfld] = name
-            for srcfld in ftype.fields_list:
-                fld = srcfld.make_shifted(offset)
+                    bitshift = bits_already_occupied
+
+                fld = ctypestruct.W_CField(ftype, field_offset_bytes,
+                                           bitshift, fbitsize)
                 fields_list.append(fld)
-                try:
-                    fields_dict[srcfield2names[srcfld]] = fld
-                except KeyError:
-                    pass
-            # always forbid such structures from being passed by value
-            custom_field_pos = True
-        else:
-            # a regular field
-            fld = ctypestruct.W_CField(ftype, offset, bitshift, fbitsize)
-            fields_list.append(fld)
-            fields_dict[fname] = fld
-        #
-        if maxsize < ftype.size:
-            maxsize = ftype.size
-        if not is_union:
-            offset += ftype.size
+                fields_dict[fname] = fld
+            
+                boffset += fbitsize
 
-    if is_union:
-        assert offset == 0
-        offset = maxsize
+        if boffset > boffsetmax:
+            boffsetmax = boffset
 
     # Like C, if the size of this structure would be zero, we compute it
     # as 1 instead.  But for ctypes support, we allow the manually-
     # specified totalsize to be zero in this case.
+    got = (boffsetmax + 7) // 8
     if totalsize < 0:
-        offset = (offset + alignment - 1) & ~(alignment - 1)
-        totalsize = offset or 1
-    elif totalsize < offset:
+        totalsize = (got + alignment - 1) & ~(alignment - 1)
+        totalsize = totalsize or 1
+    elif totalsize < got:
         raise operationerrfmt(space.w_TypeError,
                      "%s cannot be of size %d: there are fields at least "
-                     "up to %d", w_ctype.name, totalsize, offset)
+                     "up to %d", w_ctype.name, totalsize, got)
     if totalalignment < 0:
         totalalignment = alignment
 
diff --git a/pypy/module/_cffi_backend/test/_backend_test_c.py 
b/pypy/module/_cffi_backend/test/_backend_test_c.py
--- a/pypy/module/_cffi_backend/test/_backend_test_c.py
+++ b/pypy/module/_cffi_backend/test/_backend_test_c.py
@@ -2757,6 +2757,35 @@
     assert wr() is None
     py.test.raises(RuntimeError, from_handle, cast(BCharP, 0))
 
+def test_bitfield_as_gcc():
+    BChar = new_primitive_type("char")
+    BShort = new_primitive_type("short")
+    BInt = new_primitive_type("int")
+    BStruct = new_struct_type("foo1")
+    complete_struct_or_union(BStruct, [('a', BChar, -1),
+                                       ('b', BInt, 9),
+                                       ('c', BChar, -1)])
+    assert typeoffsetof(BStruct, 'c') == (BChar, 3)
+    assert sizeof(BStruct) == 4
+    assert alignof(BStruct) == 4
+    #
+    BStruct = new_struct_type("foo2")
+    complete_struct_or_union(BStruct, [('a', BChar, -1),
+                                       ('',  BShort, 9),
+                                       ('c', BChar, -1)])
+    assert typeoffsetof(BStruct, 'c') == (BChar, 4)
+    assert sizeof(BStruct) == 5
+    assert alignof(BStruct) == 1
+    #
+    BStruct = new_struct_type("foo2")
+    complete_struct_or_union(BStruct, [('a', BChar, -1),
+                                       ('',  BInt, 0),
+                                       ('',  BInt, 0),
+                                       ('c', BChar, -1)])
+    assert typeoffsetof(BStruct, 'c') == (BChar, 4)
+    assert sizeof(BStruct) == 5
+    assert alignof(BStruct) == 1
+
 
 def test_version():
     # this test is here mostly for PyPy
diff --git a/pypy/module/test_lib_pypy/cffi_tests/test_ffi_backend.py 
b/pypy/module/test_lib_pypy/cffi_tests/test_ffi_backend.py
--- a/pypy/module/test_lib_pypy/cffi_tests/test_ffi_backend.py
+++ b/pypy/module/test_lib_pypy/cffi_tests/test_ffi_backend.py
@@ -1,5 +1,5 @@
 # Generated by pypy/tool/import_cffi.py
-import py
+import py, sys
 from pypy.module.test_lib_pypy.cffi_tests import backend_tests, test_function, 
test_ownlib
 from cffi import FFI
 import _cffi_backend
@@ -37,3 +37,114 @@
         assert ffi.from_handle(p) is o
         assert ffi.from_handle(ffi.cast("char *", p)) is o
         py.test.raises(RuntimeError, ffi.from_handle, ffi.NULL)
+
+
+class TestBitfield:
+    def check(self, source, expected_ofs_y, expected_align, expected_size):
+        ffi = FFI()
+        ffi.cdef("struct s1 { %s };" % source)
+        ctype = ffi.typeof("struct s1")
+        # verify the information with gcc
+        if sys.platform != "win32":
+            ffi1 = FFI()
+            ffi1.cdef("""
+                static const int Gofs_y, Galign, Gsize;
+                struct s1 *try_with_value(int fieldnum, long long value);
+            """)
+            fnames = [name for name, cfield in ctype.fields
+                           if name and cfield.bitsize > 0]
+            setters = ['case %d: s.%s = value; break;' % iname
+                       for iname in enumerate(fnames)]
+            lib = ffi1.verify("""
+                struct s1 { %s };
+                struct sa { char a; struct s1 b; };
+                #define Gofs_y  offsetof(struct s1, y)
+                #define Galign  offsetof(struct sa, b)
+                #define Gsize   sizeof(struct s1)
+                struct s1 *try_with_value(int fieldnum, long long value)
+                {
+                    static struct s1 s;
+                    memset(&s, 0, sizeof(s));
+                    switch (fieldnum) { %s }
+                    return &s;
+                }
+            """ % (source, ' '.join(setters)))
+            assert lib.Gofs_y == expected_ofs_y
+            assert lib.Galign == expected_align
+            assert lib.Gsize  == expected_size
+        else:
+            lib = None
+            fnames = None
+        # the real test follows
+        assert ffi.offsetof("struct s1", "y") == expected_ofs_y
+        assert ffi.alignof("struct s1") == expected_align
+        assert ffi.sizeof("struct s1") == expected_size
+        # compare the actual storage of the two
+        for name, cfield in ctype.fields:
+            if cfield.bitsize < 0 or not name:
+                continue
+            if int(ffi.cast(cfield.type, -1)) == -1:   # signed
+                min_value = -(1 << (cfield.bitsize-1))
+                max_value = (1 << (cfield.bitsize-1)) - 1
+            else:
+                min_value = 0
+                max_value = (1 << cfield.bitsize) - 1
+            for t in [1, 2, 4, 8, 16, 128, 2813, 89728, 981729,
+                     -1,-2,-4,-8,-16,-128,-2813,-89728,-981729]:
+                if min_value <= t <= max_value:
+                    self._fieldcheck(ffi, lib, fnames, name, t)
+
+    def _fieldcheck(self, ffi, lib, fnames, name, value):
+        s = ffi.new("struct s1 *")
+        setattr(s, name, value)
+        assert getattr(s, name) == value
+        raw1 = bytes(ffi.buffer(s))
+        if lib is not None:
+            t = lib.try_with_value(fnames.index(name), value)
+            raw2 = bytes(ffi.buffer(t, len(raw1)))
+            assert raw1 == raw2
+
+    def test_bitfield_basic(self):
+        self.check("int a; int b:9; int c:20; int y;", 8, 4, 12)
+        self.check("int a; short b:9; short c:7; int y;", 8, 4, 12)
+        self.check("int a; short b:9; short c:9; int y;", 8, 4, 12)
+
+    def test_bitfield_reuse_if_enough_space(self):
+        self.check("int a:2; char y;", 1, 4, 4)
+        self.check("int a:1; char b  ; int c:1; char y;", 3, 4, 4)
+        self.check("int a:1; char b:8; int c:1; char y;", 3, 4, 4)
+        self.check("char a; int b:9; char y;", 3, 4, 4)
+        self.check("char a; short b:9; char y;", 4, 2, 6)
+        self.check("int a:2; char b:6; char y;", 1, 4, 4)
+        self.check("int a:2; char b:7; char y;", 2, 4, 4)
+        self.check("int a:2; short b:15; char c:2; char y;", 5, 4, 8)
+        self.check("int a:2; char b:1; char c:1; char y;", 1, 4, 4)
+
+    def test_bitfield_anonymous_no_align(self):
+        L = FFI().alignof("long long")
+        self.check("char y; int :1;", 0, 1, 2)
+        self.check("char x; int z:1; char y;", 2, 4, 4)
+        self.check("char x; int  :1; char y;", 2, 1, 3)
+        self.check("char x; long long z:48; char y;", 7, L, 8)
+        self.check("char x; long long  :48; char y;", 7, 1, 8)
+        self.check("char x; long long z:56; char y;", 8, L, 8 + L)
+        self.check("char x; long long  :56; char y;", 8, 1, 9)
+        self.check("char x; long long z:57; char y;", L + 8, L, L + 8 + L)
+        self.check("char x; long long  :57; char y;", L + 8, 1, L + 9)
+
+    def test_bitfield_zero(self):
+        L = FFI().alignof("long long")
+        self.check("char y; int :0;", 0, 1, 4)
+        self.check("char x; int :0; char y;", 4, 1, 5)
+        self.check("char x; long long :0; char y;", L, 1, L + 1)
+        self.check("short x, y; int :0; int :0;", 2, 2, 4)
+        self.check("char x; int :0; short b:1; char y;", 5, 2, 6)
+
+    def test_error_cases(self):
+        ffi = FFI()
+        py.test.raises(TypeError,
+            'ffi.cdef("struct s1 { float x:1; };"); ffi.new("struct s1 *")')
+        py.test.raises(TypeError,
+            'ffi.cdef("struct s2 { char x:0; };"); ffi.new("struct s2 *")')
+        py.test.raises(TypeError,
+            'ffi.cdef("struct s3 { char x:9; };"); ffi.new("struct s3 *")')
diff --git a/pypy/module/test_lib_pypy/cffi_tests/test_verify.py 
b/pypy/module/test_lib_pypy/cffi_tests/test_verify.py
--- a/pypy/module/test_lib_pypy/cffi_tests/test_verify.py
+++ b/pypy/module/test_lib_pypy/cffi_tests/test_verify.py
@@ -1620,3 +1620,18 @@
     ffi.cdef("int f(void *);")
     lib = ffi.verify("int f(void *x) { return ((char*)x)[0]; }")
     assert lib.f(b"foobar") == ord(b"f")
+
+def test_dir():
+    ffi = FFI()
+    ffi.cdef("""void somefunc(void);
+                extern int somevar, somearray[2];
+                static char *const sv2;
+                enum my_e { AA, BB, ... };
+                #define FOO ...""")
+    lib = ffi.verify("""void somefunc(void) { }
+                        int somevar, somearray[2];
+                        #define sv2 "text"
+                        enum my_e { AA, BB };
+                        #define FOO 42""")
+    assert dir(lib) == ['AA', 'BB', 'FOO', 'somearray',
+                        'somefunc', 'somevar', 'sv2']
_______________________________________________
pypy-commit mailing list
[email protected]
http://mail.python.org/mailman/listinfo/pypy-commit

Reply via email to