[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