Commits

Armin Rigo committed 5fbd86e Merge

hg merge default

Comments (0)

Files changed (7)

c/_cffi_backend.c

         /* then enough room for the result --- which means at least
            sizeof(ffi_arg), according to the ffi docs */
         i = fb->rtype->size;
-        if (i < sizeof(ffi_arg))
+        if (i < (Py_ssize_t)sizeof(ffi_arg))
             i = sizeof(ffi_arg);
         exchange_offset += i;
     }
     /* work work work around a libffi irregularity: for integer return
        types we have to fill at least a complete 'ffi_arg'-sized result
        buffer. */
-    if (ctype->ct_size < sizeof(ffi_arg)) {
+    if (ctype->ct_size < (Py_ssize_t)sizeof(ffi_arg)) {
+        if (ctype->ct_flags & CT_VOID) {
+            if (pyobj == Py_None) {
+                return 0;
+            }
+            else {
+                PyErr_SetString(PyExc_TypeError,
+                    "callback with the return type 'void' must return None");
+                return -1;
+            }
+        }
         if ((ctype->ct_flags & (CT_PRIMITIVE_SIGNED | CT_IS_ENUM))
                 == CT_PRIMITIVE_SIGNED) {
             PY_LONG_LONG value;
     py_res = PyEval_CallObject(py_ob, py_args);
     if (py_res == NULL)
         goto error;
-
-    if (SIGNATURE(1)->ct_size > 0) {
-        if (convert_from_object_fficallback(result, SIGNATURE(1), py_res) < 0)
-            goto error;
-    }
-    else if (py_res != Py_None) {
-        PyErr_SetString(PyExc_TypeError, "callback with the return type 'void'"
-                                         " must return None");
+    if (convert_from_object_fficallback(result, SIGNATURE(1), py_res) < 0)
         goto error;
-    }
  done:
     Py_XDECREF(py_args);
     Py_XDECREF(py_res);
 
     ctresult = (CTypeDescrObject *)PyTuple_GET_ITEM(ct->ct_stuff, 1);
     size = ctresult->ct_size;
-    if (ctresult->ct_flags & (CT_PRIMITIVE_CHAR | CT_PRIMITIVE_SIGNED |
-                              CT_PRIMITIVE_UNSIGNED)) {
-        if (size < sizeof(ffi_arg))
-            size = sizeof(ffi_arg);
-    }
-    else if (size < 0) {
-        size = 0;
-    }
+    if (size < (Py_ssize_t)sizeof(ffi_arg))
+        size = sizeof(ffi_arg);
     py_rawerr = PyString_FromStringAndSize(NULL, size);
     if (py_rawerr == NULL)
         return NULL;
     assert s.a == -10
     assert s.b == 1E-42
 
+def test_callback_returning_void():
+    BVoid = new_void_type()
+    BFunc = new_function_type((), BVoid, False)
+    def cb():
+        seen.append(42)
+    f = callback(BFunc, cb)
+    seen = []
+    f()
+    assert seen == [42]
+    py.test.raises(TypeError, callback, BFunc, cb, -42)
+
 def test_enum_type():
     BEnum = new_enum_type("foo", (), ())
     assert repr(BEnum) == "<ctype 'enum foo'>"

cffi/backend_ctypes.py

         return CTypesFunctionPtr
 
     def new_enum_type(self, name, enumerators, enumvalues):
+        assert isinstance(name, str)
         mapping = dict(zip(enumerators, enumvalues))
         reverse_mapping = dict(reversed(zip(enumvalues, enumerators)))
         CTypesInt = self.ffi._get_cached_btype(model.PrimitiveType('int'))
                            "not immediately constant expression")
 
     def _get_enum_type(self, type):
+        # See _get_struct_or_union_type() for the reason of the
+        # complicated logic here.  This is still a simplified version,
+        # assuming that it's ok to assume the more complicated cases
+        # don't occur...
+        try:
+            return self._structnode2type[type]
+        except KeyError:
+            pass
         name = type.name
+        if name is None:
+            self._anonymous_counter += 1
+            explicit_name = '$%d' % self._anonymous_counter
+            key = None
+        else:
+            explicit_name = name
+            key = 'enum %s' % (name,)
+            tp = self._declarations.get(key, None)
+            if tp is not None:
+                return tp
+        #
         decls = type.values
-        key = 'enum %s' % (name,)
-        if key in self._declarations:
-            return self._declarations[key]
         if decls is not None:
             enumerators = [enum.name for enum in decls.enumerators]
             partial = False
                 enumvalues.append(nextenumvalue)
                 nextenumvalue += 1
             enumvalues = tuple(enumvalues) 
-            tp = model.EnumType(name, enumerators, enumvalues)
+            tp = model.EnumType(explicit_name, enumerators, enumvalues)
             tp.partial = partial
-            self._declare(key, tp)
+            if key is not None:
+                self._declare(key, tp)
         else:   # opaque enum
             enumerators = ()
             enumvalues = ()
-            tp = model.EnumType(name, (), ())
+            tp = model.EnumType(explicit_name, (), ())
+        self._structnode2type[type] = tp
         return tp

cffi/verifier.py

File contents unchanged.

doc/source/index.rst

 * a C compiler is required to use CFFI during development, but not to run
   correctly-installed programs that use CFFI.
 
+* `py.test`_ is needed to run the tests of CFFI.
+
+.. _`py.test`: http://pypi.python.org/pypi/pytest
+
 Download and Installation:
 
 * https://bitbucket.org/cffi/cffi/downloads

testing/backend_tests.py

         assert ffi1.typeof("enum foo") is not ffi2.typeof("enum foo")
         # sanity check: twice 'ffi1'
         assert ffi1.typeof("struct foo*") is ffi1.typeof("struct foo *")
+
+    def test_anonymous_enum(self):
+        ffi = FFI(backend=self.Backend())
+        ffi.cdef("typedef enum { Value0 = 0 } e, *pe;\n"
+                 "typedef enum { Value1 = 1 } e1;")
+        assert ffi.getctype("e*") == 'enum $1 *'
+        assert ffi.getctype("pe") == 'enum $1 *'
+        assert ffi.getctype("e1*") == 'enum $2 *'