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