[pypy-commit] cffi default: Fix union initializers: it did not follow the C standard, and
arigo
noreply at buildbot.pypy.org
Wed Jun 27 15:26:52 CEST 2012
Author: Armin Rigo <arigo at tunes.org>
Branch:
Changeset: r544:295c5c07d0ef
Date: 2012-06-27 15:26 +0200
http://bitbucket.org/cffi/cffi/changeset/295c5c07d0ef/
Log: Fix union initializers: it did not follow the C standard, and was
subject to a subtle inconsistency.
diff --git a/c/_cffi_backend.c b/c/_cffi_backend.c
--- a/c/_cffi_backend.c
+++ b/c/_cffi_backend.c
@@ -828,7 +828,7 @@
data[0] = res;
return 0;
}
- if (ct->ct_flags & CT_STRUCT) {
+ if (ct->ct_flags & (CT_STRUCT|CT_UNION)) {
if (CData_Check(init)) {
if (((CDataObject *)init)->c_type == ct && ct->ct_size >= 0) {
@@ -836,6 +836,18 @@
return 0;
}
}
+ if (ct->ct_flags & CT_UNION) {
+ Py_ssize_t n = PyObject_Size(init);
+ if (n < 0)
+ return -1;
+ if (n > 1) {
+ PyErr_Format(PyExc_ValueError,
+ "initializer for '%s': %zd items given, but "
+ "only one supported (use a dict if needed)",
+ ct->ct_name, n);
+ return -1;
+ }
+ }
if (PyList_Check(init) || PyTuple_Check(init)) {
PyObject **items = PySequence_Fast_ITEMS(init);
Py_ssize_t i, n = PySequence_Fast_GET_SIZE(init);
@@ -875,21 +887,6 @@
expected = "list or tuple or dict or struct-cdata";
goto cannot_convert;
}
- if (ct->ct_flags & CT_UNION) {
-
- if (CData_Check(init)) {
- if (((CDataObject *)init)->c_type == ct && ct->ct_size >= 0) {
- memcpy(data, ((CDataObject *)init)->c_data, ct->ct_size);
- return 0;
- }
- }
- CFieldObject *cf = (CFieldObject *)ct->ct_extra; /* first field */
- if (cf == NULL) {
- PyErr_SetString(PyExc_ValueError, "empty union");
- return -1;
- }
- return convert_field_from_object(data, cf, init);
- }
PyErr_Format(PyExc_SystemError,
"convert_from_object: '%s'", ct->ct_name);
return -1;
diff --git a/c/test_c.py b/c/test_c.py
--- a/c/test_c.py
+++ b/c/test_c.py
@@ -584,9 +584,17 @@
BUInt = new_primitive_type("unsigned int")
BUnion = new_union_type("bar")
complete_struct_or_union(BUnion, [('a1', BInt, -1), ('a2', BUInt, -1)])
- p = newp(new_pointer_type(BUnion), -42)
+ p = newp(new_pointer_type(BUnion), [-42])
+ bigval = -42 + (1 << (8*size_of_int()))
assert p.a1 == -42
- assert p.a2 == -42 + (1 << (8*size_of_int()))
+ assert p.a2 == bigval
+ p = newp(new_pointer_type(BUnion), {'a2': bigval})
+ assert p.a1 == -42
+ assert p.a2 == bigval
+ py.test.raises(OverflowError, newp, new_pointer_type(BUnion),
+ {'a1': bigval})
+ p = newp(new_pointer_type(BUnion), [])
+ assert p.a1 == p.a2 == 0
def test_struct_pointer():
BInt = new_primitive_type("int")
@@ -957,7 +965,7 @@
#
BUnion = new_union_type("bar")
complete_struct_or_union(BUnion, [('a1', BInt, 1)])
- p = newp(new_pointer_type(BUnion), -1)
+ p = newp(new_pointer_type(BUnion), [-1])
assert p.a1 == -1
def test_weakref():
@@ -1078,7 +1086,7 @@
BUnion = new_union_type("foo_u")
BUnionPtr = new_pointer_type(BUnion)
complete_struct_or_union(BUnion, [('a1', BInt, -1)])
- u1 = newp(BUnionPtr, 42)
+ u1 = newp(BUnionPtr, [42])
u2 = newp(BUnionPtr, u1[0])
assert u2.a1 == 42
#
diff --git a/cffi/backend_ctypes.py b/cffi/backend_ctypes.py
--- a/cffi/backend_ctypes.py
+++ b/cffi/backend_ctypes.py
@@ -632,32 +632,31 @@
return result
CTypesStructOrUnion._create_ctype_obj = _create_ctype_obj
#
- if CTypesStructOrUnion._kind == 'struct':
- def initialize(blob, init):
- if not isinstance(init, dict):
- init = tuple(init)
- if len(init) > len(fnames):
- raise ValueError("too many values for %s initializer" %
- CTypesStructOrUnion._get_c_name())
- init = dict(zip(fnames, init))
- addr = ctypes.addressof(blob)
- for fname, value in init.items():
- BField, bitsize = name2fieldtype[fname]
- assert bitsize < 0, \
- "not implemented: initializer with bit fields"
- offset = CTypesStructOrUnion._offsetof(fname)
- PTR = ctypes.POINTER(BField._ctype)
- p = ctypes.cast(addr + offset, PTR)
- BField._initialize(p.contents, value)
- name2fieldtype = dict(zip(fnames, zip(btypes, bitfields)))
- #
- if CTypesStructOrUnion._kind == 'union':
- def initialize(blob, init):
- addr = ctypes.addressof(blob)
- #fname = fnames[0]
- BField = btypes[0]
+ def initialize(blob, init):
+ if is_union:
+ if len(init) > 1:
+ raise ValueError("union initializer: %d items given, but "
+ "only one supported (use a dict if needed)"
+ % (len(init),))
+ if not isinstance(init, dict):
+ if isinstance(init, str):
+ raise TypeError("union initializer: got a str")
+ init = tuple(init)
+ if len(init) > len(fnames):
+ raise ValueError("too many values for %s initializer" %
+ CTypesStructOrUnion._get_c_name())
+ init = dict(zip(fnames, init))
+ addr = ctypes.addressof(blob)
+ for fname, value in init.items():
+ BField, bitsize = name2fieldtype[fname]
+ assert bitsize < 0, \
+ "not implemented: initializer with bit fields"
+ offset = CTypesStructOrUnion._offsetof(fname)
PTR = ctypes.POINTER(BField._ctype)
- BField._initialize(ctypes.cast(addr, PTR).contents, init)
+ p = ctypes.cast(addr + offset, PTR)
+ BField._initialize(p.contents, value)
+ is_union = CTypesStructOrUnion._kind == 'union'
+ name2fieldtype = dict(zip(fnames, zip(btypes, bitfields)))
#
for fname, BField, bitsize in fields:
if hasattr(CTypesStructOrUnion, fname):
diff --git a/testing/backend_tests.py b/testing/backend_tests.py
--- a/testing/backend_tests.py
+++ b/testing/backend_tests.py
@@ -435,7 +435,7 @@
assert u.a != 0
py.test.raises(OverflowError, "u.b = 32768")
#
- u = ffi.new("union foo", -2)
+ u = ffi.new("union foo", [-2])
assert u.a == -2
py.test.raises((AttributeError, TypeError), "del u.a")
assert repr(u) == "<cdata 'union foo *' owning %d bytes>" % SIZE_OF_INT
@@ -446,6 +446,21 @@
u = ffi.new("union baz *") # this works
assert u[0] == ffi.NULL
+ def test_union_initializer(self):
+ ffi = FFI(backend=self.Backend())
+ ffi.cdef("union foo { char a; int b; };")
+ py.test.raises(TypeError, ffi.new, "union foo", 'A')
+ py.test.raises(TypeError, ffi.new, "union foo", 5)
+ py.test.raises(ValueError, ffi.new, "union foo", ['A', 5])
+ u = ffi.new("union foo", ['A'])
+ assert u.a == 'A'
+ py.test.raises(TypeError, ffi.new, "union foo", [5])
+ u = ffi.new("union foo", {'b': 12345})
+ assert u.b == 12345
+ u = ffi.new("union foo", [])
+ assert u.a == '\x00'
+ assert u.b == 0
+
def test_sizeof_type(self):
ffi = FFI(backend=self.Backend())
ffi.cdef("""
More information about the pypy-commit
mailing list