Author: Armin Rigo <[email protected]> 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)") _______________________________________________ pypy-commit mailing list [email protected] https://mail.python.org/mailman/listinfo/pypy-commit
