[pypy-commit] pypy default: Update: ffi.addressof(lib, "funcname")

arigo noreply at buildbot.pypy.org
Tue May 26 19:02:40 CEST 2015


Author: Armin Rigo <arigo at tunes.org>
Branch: 
Changeset: r77589:39af68b61c76
Date: 2015-05-26 19:01 +0200
http://bitbucket.org/pypy/pypy/changeset/39af68b61c76/

Log:	Update: ffi.addressof(lib, "funcname")

diff --git a/lib_pypy/cffi.egg-info/PKG-INFO b/lib_pypy/cffi.egg-info/PKG-INFO
--- a/lib_pypy/cffi.egg-info/PKG-INFO
+++ b/lib_pypy/cffi.egg-info/PKG-INFO
@@ -1,6 +1,6 @@
 Metadata-Version: 1.1
 Name: cffi
-Version: 1.0.3
+Version: 1.0.4
 Summary: Foreign Function Interface for Python calling C code.
 Home-page: http://cffi.readthedocs.org
 Author: Armin Rigo, Maciej Fijalkowski
diff --git a/lib_pypy/cffi/__init__.py b/lib_pypy/cffi/__init__.py
--- a/lib_pypy/cffi/__init__.py
+++ b/lib_pypy/cffi/__init__.py
@@ -4,8 +4,8 @@
 from .api import FFI, CDefError, FFIError
 from .ffiplatform import VerificationError, VerificationMissing
 
-__version__ = "1.0.3"
-__version_info__ = (1, 0, 3)
+__version__ = "1.0.4"
+__version_info__ = (1, 0, 4)
 
 # The verifier module file names are based on the CRC32 of a string that
 # contains the following version number.  It may be older than __version__
diff --git a/lib_pypy/cffi/_cffi_include.h b/lib_pypy/cffi/_cffi_include.h
--- a/lib_pypy/cffi/_cffi_include.h
+++ b/lib_pypy/cffi/_cffi_include.h
@@ -51,6 +51,11 @@
 # endif
 #endif
 
+#ifdef __GNUC__
+# define _CFFI_UNUSED_FN  __attribute__((unused))
+#else
+# define _CFFI_UNUSED_FN  /* nothing */
+#endif
 
 /**********  CPython-specific section  **********/
 #ifndef PYPY_VERSION
@@ -82,7 +87,8 @@
             PyLong_FromLongLong((long long)x)))
 
 #define _cffi_to_c_int(o, type)                                          \
-    (sizeof(type) == 1 ? (((type)-1) > 0 ? (type)_cffi_to_c_u8(o)        \
+    ((type)(                                                             \
+     sizeof(type) == 1 ? (((type)-1) > 0 ? (type)_cffi_to_c_u8(o)        \
                                          : (type)_cffi_to_c_i8(o)) :     \
      sizeof(type) == 2 ? (((type)-1) > 0 ? (type)_cffi_to_c_u16(o)       \
                                          : (type)_cffi_to_c_i16(o)) :    \
@@ -90,7 +96,7 @@
                                          : (type)_cffi_to_c_i32(o)) :    \
      sizeof(type) == 8 ? (((type)-1) > 0 ? (type)_cffi_to_c_u64(o)       \
                                          : (type)_cffi_to_c_i64(o)) :    \
-     (Py_FatalError("unsupported size for type " #type), (type)0))
+     (Py_FatalError("unsupported size for type " #type), (type)0)))
 
 #define _cffi_to_c_i8                                                    \
                  ((int(*)(PyObject *))_cffi_exports[1])
@@ -181,6 +187,20 @@
     return NULL;
 }
 
+_CFFI_UNUSED_FN
+static PyObject **_cffi_unpack_args(PyObject *args_tuple, Py_ssize_t expected,
+                                    const char *fnname)
+{
+    if (PyTuple_GET_SIZE(args_tuple) != expected) {
+        PyErr_Format(PyExc_TypeError,
+                     "%.150s() takes exactly %zd arguments (%zd given)",
+                     fnname, expected, PyTuple_GET_SIZE(args_tuple));
+        return NULL;
+    }
+    return &PyTuple_GET_ITEM(args_tuple, 0);   /* pointer to the first item,
+                                                  the others follow */
+}
+
 #endif
 /**********  end CPython-specific section  **********/
 
@@ -200,12 +220,6 @@
     ((got_nonpos) == (expected <= 0) &&                 \
      (got) == (unsigned long long)expected)
 
-#ifdef __GNUC__
-# define _CFFI_UNUSED_FN  __attribute__((unused))
-#else
-# define _CFFI_UNUSED_FN  /* nothing */
-#endif
-
 #ifdef __cplusplus
 }
 #endif
diff --git a/lib_pypy/cffi/parse_c_type.h b/lib_pypy/cffi/parse_c_type.h
--- a/lib_pypy/cffi/parse_c_type.h
+++ b/lib_pypy/cffi/parse_c_type.h
@@ -83,7 +83,8 @@
     const char *name;
     void *address;
     _cffi_opcode_t type_op;
-    size_t size;             // 0 if unknown
+    void *size_or_direct_fn;  // OP_GLOBAL_VAR: size, or 0 if unknown
+                              // OP_CPYTHON_BLTN_*: addr of direct function
 };
 
 struct _cffi_getconst_s {
diff --git a/lib_pypy/cffi/recompiler.py b/lib_pypy/cffi/recompiler.py
--- a/lib_pypy/cffi/recompiler.py
+++ b/lib_pypy/cffi/recompiler.py
@@ -19,7 +19,7 @@
         self.check_value = check_value
 
     def as_c_expr(self):
-        return '  { "%s", (void *)%s, %s, %s },' % (
+        return '  { "%s", (void *)%s, %s, (void *)%s },' % (
             self.name, self.address, self.type_op.as_c_expr(), self.size)
 
     def as_python_expr(self):
@@ -386,7 +386,7 @@
         prnt('#  ifdef _MSC_VER')
         prnt('     PyMODINIT_FUNC')
         prnt('#  if PY_MAJOR_VERSION >= 3')
-        prnt('     PyInit_%s(void) { return -1; }' % (base_module_name,))
+        prnt('     PyInit_%s(void) { return NULL; }' % (base_module_name,))
         prnt('#  else')
         prnt('     init%s(void) { }' % (base_module_name,))
         prnt('#  endif')
@@ -602,6 +602,26 @@
         else:
             argname = 'args'
         #
+        # ------------------------------
+        # the 'd' version of the function, only for addressof(lib, 'func')
+        arguments = []
+        call_arguments = []
+        context = 'argument of %s' % name
+        for i, type in enumerate(tp.args):
+            arguments.append(type.get_c_name(' x%d' % i, context))
+            call_arguments.append('x%d' % i)
+        repr_arguments = ', '.join(arguments)
+        repr_arguments = repr_arguments or 'void'
+        name_and_arguments = '_cffi_d_%s(%s)' % (name, repr_arguments)
+        prnt('static %s' % (tp.result.get_c_name(name_and_arguments),))
+        prnt('{')
+        call_arguments = ', '.join(call_arguments)
+        result_code = 'return '
+        if isinstance(tp.result, model.VoidType):
+            result_code = ''
+        prnt('  %s%s(%s);' % (result_code, name, call_arguments))
+        prnt('}')
+        #
         prnt('#ifndef PYPY_VERSION')        # ------------------------------
         #
         prnt('static PyObject *')
@@ -632,10 +652,13 @@
             rng = range(len(tp.args))
             for i in rng:
                 prnt('  PyObject *arg%d;' % i)
+            prnt('  PyObject **aa;')
             prnt()
-            prnt('  if (!PyArg_ParseTuple(args, "%s:%s", %s))' % (
-                'O' * numargs, name, ', '.join(['&arg%d' % i for i in rng])))
+            prnt('  aa = _cffi_unpack_args(args, %d, "%s");' % (len(rng), name))
+            prnt('  if (aa == NULL)')
             prnt('    return NULL;')
+            for i in rng:
+                prnt('  arg%d = aa[%d];' % (i, i))
         prnt()
         #
         for i, type in enumerate(tp.args):
@@ -668,6 +691,7 @@
         # the PyPy version: need to replace struct/union arguments with
         # pointers, and if the result is a struct/union, insert a first
         # arg that is a pointer to the result.
+        difference = False
         arguments = []
         call_arguments = []
         context = 'argument of %s' % name
@@ -675,6 +699,7 @@
             indirection = ''
             if isinstance(type, model.StructOrUnion):
                 indirection = '*'
+                difference = True
             arg = type.get_c_name(' %sx%d' % (indirection, i), context)
             arguments.append(arg)
             call_arguments.append('%sx%d' % (indirection, i))
@@ -686,18 +711,22 @@
             tp_result = model.void_type
             result_decl = None
             result_code = '*result = '
-        repr_arguments = ', '.join(arguments)
-        repr_arguments = repr_arguments or 'void'
-        name_and_arguments = '_cffi_f_%s(%s)' % (name, repr_arguments)
-        prnt('static %s' % (tp_result.get_c_name(name_and_arguments),))
-        prnt('{')
-        if result_decl:
-            prnt(result_decl)
-        call_arguments = ', '.join(call_arguments)
-        prnt('  { %s%s(%s); }' % (result_code, name, call_arguments))
-        if result_decl:
-            prnt('  return result;')
-        prnt('}')
+            difference = True
+        if difference:
+            repr_arguments = ', '.join(arguments)
+            repr_arguments = repr_arguments or 'void'
+            name_and_arguments = '_cffi_f_%s(%s)' % (name, repr_arguments)
+            prnt('static %s' % (tp_result.get_c_name(name_and_arguments),))
+            prnt('{')
+            if result_decl:
+                prnt(result_decl)
+            call_arguments = ', '.join(call_arguments)
+            prnt('  { %s%s(%s); }' % (result_code, name, call_arguments))
+            if result_decl:
+                prnt('  return result;')
+            prnt('}')
+        else:
+            prnt('#  define _cffi_f_%s _cffi_d_%s' % (name, name))
         #
         prnt('#endif')        # ------------------------------
         prnt()
@@ -718,7 +747,8 @@
             meth_kind = OP_CPYTHON_BLTN_V   # 'METH_VARARGS'
         self._lsts["global"].append(
             GlobalExpr(name, '_cffi_f_%s' % name,
-                       CffiOp(meth_kind, type_index), check_value=0))
+                       CffiOp(meth_kind, type_index), check_value=0,
+                       size='_cffi_d_%s' % name))
 
     # ----------
     # named structs or unions
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
@@ -886,7 +886,8 @@
             PyLong_FromLongLong((long long)x)))
 
 #define _cffi_to_c_int(o, type)                                          \
-    (sizeof(type) == 1 ? (((type)-1) > 0 ? (type)_cffi_to_c_u8(o)        \
+    ((type)(                                                             \
+     sizeof(type) == 1 ? (((type)-1) > 0 ? (type)_cffi_to_c_u8(o)        \
                                          : (type)_cffi_to_c_i8(o)) :     \
      sizeof(type) == 2 ? (((type)-1) > 0 ? (type)_cffi_to_c_u16(o)       \
                                          : (type)_cffi_to_c_i16(o)) :    \
@@ -894,7 +895,7 @@
                                          : (type)_cffi_to_c_i32(o)) :    \
      sizeof(type) == 8 ? (((type)-1) > 0 ? (type)_cffi_to_c_u64(o)       \
                                          : (type)_cffi_to_c_i64(o)) :    \
-     (Py_FatalError("unsupported size for type " #type), (type)0))
+     (Py_FatalError("unsupported size for type " #type), (type)0)))
 
 #define _cffi_to_c_i8                                                    \
                  ((int(*)(PyObject *))_cffi_exports[1])
diff --git a/pypy/module/_cffi_backend/__init__.py b/pypy/module/_cffi_backend/__init__.py
--- a/pypy/module/_cffi_backend/__init__.py
+++ b/pypy/module/_cffi_backend/__init__.py
@@ -2,7 +2,7 @@
 from pypy.interpreter.mixedmodule import MixedModule
 from rpython.rlib import rdynload
 
-VERSION = "1.0.3"
+VERSION = "1.0.4"
 
 
 class Module(MixedModule):
diff --git a/pypy/module/_cffi_backend/ffi_obj.py b/pypy/module/_cffi_backend/ffi_obj.py
--- a/pypy/module/_cffi_backend/ffi_obj.py
+++ b/pypy/module/_cffi_backend/ffi_obj.py
@@ -201,13 +201,13 @@
 in case of nested structures or arrays.
 
 3. ffi.addressof(<library>, "name") returns the address of the named
-global variable."""
+function or global variable."""
         #
         from pypy.module._cffi_backend.lib_obj import W_LibObject
         space = self.space
         if isinstance(w_arg, W_LibObject) and len(args_w) == 1:
             # case 3 in the docstring
-            return w_arg.address_of_global_var(space.str_w(args_w[0]))
+            return w_arg.address_of_func_or_global_var(space.str_w(args_w[0]))
         #
         w_ctype = self.ffi_type(w_arg, ACCEPT_CDATA)
         if len(args_w) == 0:
diff --git a/pypy/module/_cffi_backend/lib_obj.py b/pypy/module/_cffi_backend/lib_obj.py
--- a/pypy/module/_cffi_backend/lib_obj.py
+++ b/pypy/module/_cffi_backend/lib_obj.py
@@ -64,7 +64,7 @@
         #
         ptr = rffi.cast(rffi.CCHARP, g.c_address)
         assert ptr
-        return W_FunctionWrapper(self.space, ptr, w_ct,
+        return W_FunctionWrapper(self.space, ptr, g.c_size_or_direct_fn, w_ct,
                                  locs, rawfunctype, fnname)
 
     @jit.elidable_promote()
@@ -104,7 +104,7 @@
                 # A global variable of the exact type specified here
                 w_ct = realize_c_type.realize_c_type(
                     self.ffi, self.ctx.c_types, getarg(g.c_type_op))
-                g_size = rffi.getintfield(g, 'c_size')
+                g_size = rffi.cast(lltype.Signed, g.c_size_or_direct_fn)
                 if g_size != w_ct.size and g_size != 0 and w_ct.size > 0:
                     raise oefmt(self.ffi.w_FFIError,
                             "global variable '%s' should be %d bytes "
@@ -197,7 +197,7 @@
                    for i in range(total)]
         return space.newlist(names_w)
 
-    def address_of_global_var(self, varname):
+    def address_of_func_or_global_var(self, varname):
         # rebuild a string object from 'varname', to do typechecks and
         # to force a unicode back to a plain string
         space = self.space
@@ -206,9 +206,15 @@
             # regular case: a global variable
             return w_value.address()
         #
-        if ((isinstance(w_value, W_CData) and
-                isinstance(w_value.ctype, W_CTypeFunc))
-            or isinstance(w_value, W_FunctionWrapper)):
+        if isinstance(w_value, W_FunctionWrapper):
+            # '&func' returns a regular cdata pointer-to-function
+            if w_value.directfnptr:
+                return W_CData(space, w_value.directfnptr, w_value.ctype)
+            else:
+                return w_value    # backward compatibility
+        #
+        if (isinstance(w_value, W_CData) and
+                isinstance(w_value.ctype, W_CTypeFunc)):
             # '&func' is 'func' in C, for a constant function 'func'
             return w_value
         #
diff --git a/pypy/module/_cffi_backend/parse_c_type.py b/pypy/module/_cffi_backend/parse_c_type.py
--- a/pypy/module/_cffi_backend/parse_c_type.py
+++ b/pypy/module/_cffi_backend/parse_c_type.py
@@ -23,7 +23,7 @@
                        ('name', rffi.CCHARP),
                        ('address', rffi.VOIDP),
                        ('type_op', _CFFI_OPCODE_T),
-                       ('size', rffi.SIZE_T))
+                       ('size_or_direct_fn', rffi.CCHARP))
 CDL_INTCONST_S = lltype.Struct('cdl_intconst_s',
                        ('value', rffi.ULONGLONG),
                        ('neg', rffi.INT))
diff --git a/pypy/module/_cffi_backend/src/parse_c_type.h b/pypy/module/_cffi_backend/src/parse_c_type.h
--- a/pypy/module/_cffi_backend/src/parse_c_type.h
+++ b/pypy/module/_cffi_backend/src/parse_c_type.h
@@ -1,5 +1,5 @@
 
-/* See doc/parse_c_type.rst in the source of CFFI for more information */
+/* See doc/misc/parse_c_type.rst in the source of CFFI for more information */
 
 typedef void *_cffi_opcode_t;
 
@@ -83,7 +83,8 @@
     const char *name;
     void *address;
     _cffi_opcode_t type_op;
-    size_t size;             // 0 if unknown
+    void *size_or_direct_fn;  // OP_GLOBAL_VAR: size, or 0 if unknown
+                              // OP_CPYTHON_BLTN_*: addr of direct function
 };
 
 struct _cffi_getconst_s {
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
@@ -3335,4 +3335,4 @@
 
 def test_version():
     # this test is here mostly for PyPy
-    assert __version__ == "1.0.3"
+    assert __version__ == "1.0.4"
diff --git a/pypy/module/_cffi_backend/test/test_recompiler.py b/pypy/module/_cffi_backend/test/test_recompiler.py
--- a/pypy/module/_cffi_backend/test/test_recompiler.py
+++ b/pypy/module/_cffi_backend/test/test_recompiler.py
@@ -9,11 +9,14 @@
 @unwrap_spec(cdef=str, module_name=str, source=str)
 def prepare(space, cdef, module_name, source, w_includes=None):
     try:
+        import cffi
         from cffi import FFI            # <== the system one, which
-        from cffi import recompiler     # needs to be at least cffi 1.0.0
+        from cffi import recompiler     # needs to be at least cffi 1.0.4
         from cffi import ffiplatform
     except ImportError:
         py.test.skip("system cffi module not found or older than 1.0.0")
+    if cffi.__version_info__ < (1, 0, 4):
+        py.test.skip("system cffi module needs to be at least 1.0.4")
     space.appexec([], """():
         import _cffi_backend     # force it to be initialized
     """)
@@ -739,7 +742,6 @@
         #
         raises(AttributeError, ffi.addressof, lib, 'unknown_var')
         raises(AttributeError, ffi.addressof, lib, "FOOBAR")
-        assert ffi.addressof(lib, 'FetchRectBottom') == lib.FetchRectBottom
 
     def test_defines__CFFI_(self):
         # Check that we define the macro _CFFI_ automatically.
@@ -782,3 +784,18 @@
         assert str(e5.value) == "foo2() takes exactly 2 arguments (0 given)"
         assert str(e6.value) == "foo2() takes exactly 2 arguments (1 given)"
         assert str(e7.value) == "foo2() takes exactly 2 arguments (3 given)"
+
+    def test_address_of_function(self):
+        ffi, lib = self.prepare(
+            "long myfunc(long x);",
+            "test_addressof_function",
+            "char myfunc(char x) { return (char)(x + 42); }")
+        assert lib.myfunc(5) == 47
+        assert lib.myfunc(0xABC05) == 47
+        assert not isinstance(lib.myfunc, ffi.CData)
+        assert ffi.typeof(lib.myfunc) == ffi.typeof("long(*)(long)")
+        addr = ffi.addressof(lib, 'myfunc')
+        assert addr(5) == 47
+        assert addr(0xABC05) == 47
+        assert isinstance(addr, ffi.CData)
+        assert ffi.typeof(addr) == ffi.typeof("long(*)(long)")
diff --git a/pypy/module/_cffi_backend/wrapper.py b/pypy/module/_cffi_backend/wrapper.py
--- a/pypy/module/_cffi_backend/wrapper.py
+++ b/pypy/module/_cffi_backend/wrapper.py
@@ -22,13 +22,15 @@
     """
     _immutable_ = True
 
-    def __init__(self, space, fnptr, ctype, locs, rawfunctype, fnname):
+    def __init__(self, space, fnptr, directfnptr, ctype,
+                 locs, rawfunctype, fnname):
         assert isinstance(ctype, W_CTypeFunc)
         assert ctype.cif_descr is not None     # not for '...' functions
         assert locs is None or len(ctype.fargs) == len(locs)
         #
         self.space = space
         self.fnptr = fnptr
+        self.directfnptr = directfnptr
         self.ctype = ctype
         self.locs = locs
         self.rawfunctype = rawfunctype
diff --git a/pypy/module/test_lib_pypy/cffi_tests/cffi1/test_recompiler.py b/pypy/module/test_lib_pypy/cffi_tests/cffi1/test_recompiler.py
--- a/pypy/module/test_lib_pypy/cffi_tests/cffi1/test_recompiler.py
+++ b/pypy/module/test_lib_pypy/cffi_tests/cffi1/test_recompiler.py
@@ -761,7 +761,6 @@
     #
     py.test.raises(AttributeError, ffi.addressof, lib, 'unknown_var')
     py.test.raises(AttributeError, ffi.addressof, lib, "FOOBAR")
-    assert ffi.addressof(lib, 'FetchRectBottom') == lib.FetchRectBottom
 
 def test_defines__CFFI_():
     # Check that we define the macro _CFFI_ automatically.
@@ -777,3 +776,48 @@
     #endif
     """)
     assert lib.CORRECT == 1
+
+def test_unpack_args():
+    ffi = FFI()
+    ffi.cdef("void foo0(void); void foo1(int); void foo2(int, int);")
+    lib = verify(ffi, "test_unpack_args", """
+    void foo0(void) { }
+    void foo1(int x) { }
+    void foo2(int x, int y) { }
+    """)
+    assert 'foo0' in repr(lib.foo0)
+    assert 'foo1' in repr(lib.foo1)
+    assert 'foo2' in repr(lib.foo2)
+    lib.foo0()
+    lib.foo1(42)
+    lib.foo2(43, 44)
+    e1 = py.test.raises(TypeError, lib.foo0, 42)
+    e2 = py.test.raises(TypeError, lib.foo0, 43, 44)
+    e3 = py.test.raises(TypeError, lib.foo1)
+    e4 = py.test.raises(TypeError, lib.foo1, 43, 44)
+    e5 = py.test.raises(TypeError, lib.foo2)
+    e6 = py.test.raises(TypeError, lib.foo2, 42)
+    e7 = py.test.raises(TypeError, lib.foo2, 45, 46, 47)
+    assert str(e1.value) == "foo0() takes no arguments (1 given)"
+    assert str(e2.value) == "foo0() takes no arguments (2 given)"
+    assert str(e3.value) == "foo1() takes exactly one argument (0 given)"
+    assert str(e4.value) == "foo1() takes exactly one argument (2 given)"
+    assert str(e5.value) == "foo2() takes exactly 2 arguments (0 given)"
+    assert str(e6.value) == "foo2() takes exactly 2 arguments (1 given)"
+    assert str(e7.value) == "foo2() takes exactly 2 arguments (3 given)"
+
+def test_address_of_function():
+    ffi = FFI()
+    ffi.cdef("long myfunc(long x);")
+    lib = verify(ffi, "test_addressof_function", """
+        char myfunc(char x) { return (char)(x + 42); }
+    """)
+    assert lib.myfunc(5) == 47
+    assert lib.myfunc(0xABC05) == 47
+    assert not isinstance(lib.myfunc, ffi.CData)
+    assert ffi.typeof(lib.myfunc) == ffi.typeof("long(*)(long)")
+    addr = ffi.addressof(lib, 'myfunc')
+    assert addr(5) == 47
+    assert addr(0xABC05) == 47
+    assert isinstance(addr, ffi.CData)
+    assert ffi.typeof(addr) == ffi.typeof("long(*)(long)")


More information about the pypy-commit mailing list