[pypy-commit] cffi default: ffi.addressof(lib, "function") now returns a regular cdata function pointer

arigo noreply at buildbot.pypy.org
Tue May 26 16:32:02 CEST 2015


Author: Armin Rigo <arigo at tunes.org>
Branch: 
Changeset: r2106:e216f2c939fb
Date: 2015-05-26 16:32 +0200
http://bitbucket.org/cffi/cffi/changeset/e216f2c939fb/

Log:	ffi.addressof(lib, "function") now returns a regular cdata function
	pointer

diff --git a/c/ffi_obj.c b/c/ffi_obj.c
--- a/c/ffi_obj.c
+++ b/c/ffi_obj.c
@@ -416,7 +416,7 @@
 "in case of nested structures or arrays.\n"
 "\n"
 "3. ffi.addressof(<library>, \"name\") returns the address of the named\n"
-"global variable.");
+"function or global variable.");
 
 static PyObject *address_of_global_var(PyObject *args);  /* forward */
 
diff --git a/c/lib_obj.c b/c/lib_obj.c
--- a/c/lib_obj.c
+++ b/c/lib_obj.c
@@ -13,6 +13,7 @@
 
 struct CPyExtFunc_s {
     PyMethodDef md;
+    void *direct_fn;
     int type_index;
 };
 static const char cpyextfunc_doc[] =
@@ -43,20 +44,9 @@
     return exf;
 }
 
-static PyObject *_cpyextfunc_type_index(PyObject *x)
+static PyObject *_cpyextfunc_type(LibObject *lib, struct CPyExtFunc_s *exf)
 {
-    struct CPyExtFunc_s *exf;
-    LibObject *lib;
     PyObject *tuple, *result;
-
-    assert(PyErr_Occurred());
-    exf = _cpyextfunc_get(x);
-    if (exf == NULL)
-        return NULL;    /* still the same exception is set */
-
-    PyErr_Clear();
-
-    lib = (LibObject *)PyCFunction_GET_SELF(x);
     tuple = realize_c_type_or_func(lib->l_types_builder,
                                    lib->l_types_builder->ctx.types,
                                    exf->type_index);
@@ -71,6 +61,22 @@
     return result;
 }
 
+static PyObject *_cpyextfunc_type_index(PyObject *x)
+{
+    struct CPyExtFunc_s *exf;
+    LibObject *lib;
+
+    assert(PyErr_Occurred());
+    exf = _cpyextfunc_get(x);
+    if (exf == NULL)
+        return NULL;    /* still the same exception is set */
+
+    PyErr_Clear();
+
+    lib = (LibObject *)PyCFunction_GET_SELF(x);
+    return _cpyextfunc_type(lib, exf);
+}
+
 static void cdlopen_close_ignore_errors(void *libhandle);  /* forward */
 static void *cdlopen_fetch(PyObject *libname, void *libhandle, char *symbol);
 
@@ -144,6 +150,7 @@
     xfunc->md.ml_flags = flags;
     xfunc->md.ml_name = g->name;
     xfunc->md.ml_doc = cpyextfunc_doc;
+    xfunc->direct_fn = g->size_or_direct_fn;
     xfunc->type_index = type_index;
 
     return PyCFunction_NewEx(&xfunc->md, (PyObject *)lib, lib->l_libname);
@@ -261,16 +268,18 @@
     }
 
     case _CFFI_OP_GLOBAL_VAR:
+    {
         /* global variable of the exact type specified here */
+        size_t g_size = (size_t)g->size_or_direct_fn;
         ct = realize_c_type(types_builder, types_builder->ctx.types,
                             _CFFI_GETARG(g->type_op));
         if (ct == NULL)
             return NULL;
-        if (g->size != ct->ct_size && g->size != 0 && ct->ct_size > 0) {
+        if (g_size != ct->ct_size && g_size != 0 && ct->ct_size > 0) {
             PyErr_Format(FFIError,
                          "global variable '%.200s' should be %zd bytes "
                          "according to the cdef, but is actually %zd",
-                         s, ct->ct_size, g->size);
+                         s, ct->ct_size, g_size);
             x = NULL;
         }
         else {
@@ -285,6 +294,7 @@
         }
         Py_DECREF(ct);
         break;
+    }
 
     case _CFFI_OP_DLOPEN_FUNC:
     {
@@ -489,14 +499,21 @@
     }
     else {
         struct CPyExtFunc_s *exf = _cpyextfunc_get(x);
-        /* XXX the exf case is strange: typing ffi.addressof(lib, 'func')
-           just returns the same thing as lib.func, so there is  no point
-           right now.  Maybe it should instead return a regular <cdata>
-           object of a function-pointer ctype, which would point to a
-           yet-to-be-defined function from the generated .c code. */
-        if (exf != NULL  ||  /* an OP_CPYTHON_BLTN: '&func' is 'func' in C */
-            ((CData_Check(x) &&  /* or, a constant functionptr cdata: same */
-              (((CDataObject *)x)->c_type->ct_flags & CT_FUNCTIONPTR) != 0))) {
+        if (exf != NULL) {  /* an OP_CPYTHON_BLTN: '&func' returns a cdata */
+            PyObject *ct;
+            if (exf->direct_fn == NULL) {
+                Py_INCREF(x);    /* backward compatibility */
+                return x;
+            }
+            ct = _cpyextfunc_type(lib, exf);
+            if (ct == NULL)
+                return NULL;
+            x = new_simple_cdata(exf->direct_fn, (CTypeDescrObject *)ct);
+            Py_DECREF(ct);
+            return x;
+        }
+        if (CData_Check(x) &&  /* a constant functionptr cdata: 'f == &f' */
+                (((CDataObject *)x)->c_type->ct_flags & CT_FUNCTIONPTR) != 0) {
             Py_INCREF(x);
             return x;
         }
diff --git a/cffi/parse_c_type.h b/cffi/parse_c_type.h
--- a/cffi/parse_c_type.h
+++ b/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/cffi/recompiler.py b/cffi/recompiler.py
--- a/cffi/recompiler.py
+++ b/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):
@@ -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 *')
@@ -671,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
@@ -678,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))
@@ -689,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()
@@ -721,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/doc/source/using.rst b/doc/source/using.rst
--- a/doc/source/using.rst
+++ b/doc/source/using.rst
@@ -332,7 +332,10 @@
 objects, but as a different type (on CPython, ``<built-in
 function>``).  This means you cannot e.g. pass them to some other C
 function expecting a function pointer argument.  Only ``ffi.typeof()``
-works on them.  If you really need a cdata pointer to the function,
+works on them.  To get a cdata containing a regular function pointer,
+use ``ffi.addressof(lib, "name")`` (new in version 1.0.4).
+
+Before version 1.0.4, if you really need a cdata pointer to the function,
 use the following workaround:
 
 .. code-block:: python
@@ -742,7 +745,9 @@
 and in C, where ``&array[index]`` is just ``array + index``.
 
 3. ``ffi.addressof(<library>, "name")`` returns the address of the
-named global variable from the given library object.
+named function or global variable from the given library object.
+*New in version 1.0.4:* for functions, it returns a regular cdata
+object containing a pointer to the function.
 
 Note that the case 1. cannot be used to take the address of a
 primitive or pointer, but only a struct or union.  It would be
diff --git a/testing/cffi1/test_recompiler.py b/testing/cffi1/test_recompiler.py
--- a/testing/cffi1/test_recompiler.py
+++ b/testing/cffi1/test_recompiler.py
@@ -760,7 +760,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.
@@ -805,3 +804,19 @@
     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