[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