diff options
author | Armin Rigo <arigo@tunes.org> | 2017-02-07 16:28:20 +0100 |
---|---|---|
committer | Armin Rigo <arigo@tunes.org> | 2017-02-07 16:28:20 +0100 |
commit | f5d76e1b0d614520e6c9b79e995b14c177dc9575 (patch) | |
tree | 8d9b918c6f6419296292a876c52ffcfa27520804 | |
parent | 0e4e34033dcbe9d4ff4070e5754fac392d79fff3 (diff) | |
download | cffi-f5d76e1b0d614520e6c9b79e995b14c177dc9575.tar.gz |
ffi.addressof(lib, "name") now also works in in-line mode
-rw-r--r-- | c/_cffi_backend.c | 18 | ||||
-rw-r--r-- | cffi/api.py | 40 | ||||
-rw-r--r-- | doc/source/whatsnew.rst | 4 | ||||
-rw-r--r-- | testing/cffi0/test_ownlib.py | 18 |
4 files changed, 65 insertions, 15 deletions
diff --git a/c/_cffi_backend.c b/c/_cffi_backend.c index d9aaf6e..765d661 100644 --- a/c/_cffi_backend.c +++ b/c/_cffi_backend.c @@ -3691,19 +3691,14 @@ static PyObject *dl_load_function(DynLibObject *dlobj, PyObject *args) CTypeDescrObject *ct; char *funcname; void *funcptr; - int ok; if (!PyArg_ParseTuple(args, "O!s:load_function", &CTypeDescr_Type, &ct, &funcname)) return NULL; - ok = 0; - if (ct->ct_flags & CT_FUNCTIONPTR) - ok = 1; - if ((ct->ct_flags & CT_POINTER) && (ct->ct_itemdescr->ct_flags & CT_VOID)) - ok = 1; - if (!ok) { - PyErr_Format(PyExc_TypeError, "function cdata expected, got '%s'", + if (!(ct->ct_flags & (CT_FUNCTIONPTR | CT_POINTER | CT_ARRAY))) { + PyErr_Format(PyExc_TypeError, + "function or pointer or array cdata expected, got '%s'", ct->ct_name); return NULL; } @@ -3711,12 +3706,15 @@ static PyObject *dl_load_function(DynLibObject *dlobj, PyObject *args) funcptr = dlsym(dlobj->dl_handle, funcname); if (funcptr == NULL) { const char *error = dlerror(); - PyErr_Format(PyExc_KeyError, - "function '%s' not found in library '%s': %s", + PyErr_Format(PyExc_AttributeError, + "function/symbol '%s' not found in library '%s': %s", funcname, dlobj->dl_name, error); return NULL; } + if ((ct->ct_flags & CT_ARRAY) && ct->ct_length < 0) { + ct = (CTypeDescrObject *)ct->ct_stuff; + } return new_simple_cdata(funcptr, ct); } diff --git a/cffi/api.py b/cffi/api.py index 272a6a7..92b7628 100644 --- a/cffi/api.py +++ b/cffi/api.py @@ -462,7 +462,12 @@ class FFI(object): field or array item in the structure or array, recursively in case of nested structures. """ - ctype = self._backend.typeof(cdata) + try: + ctype = self._backend.typeof(cdata) + except TypeError: + if '__addressof__' in type(cdata).__dict__: + return type(cdata).__addressof__(cdata, *fields_or_indexes) + raise if fields_or_indexes: ctype, offset = self._typeoffsetof(ctype, *fields_or_indexes) else: @@ -775,10 +780,7 @@ def _make_ffi_library(ffi, libname, flags): key = 'function ' + name tp, _ = ffi._parser._declarations[key] BType = ffi._get_cached_btype(tp) - try: - value = backendlib.load_function(BType, name) - except KeyError as e: - raise AttributeError('%s: %s' % (name, e)) + value = backendlib.load_function(BType, name) library.__dict__[name] = value # def accessor_variable(name): @@ -791,6 +793,21 @@ def _make_ffi_library(ffi, libname, flags): lambda self: read_variable(BType, name), lambda self, value: write_variable(BType, name, value))) # + def addressof_var(name): + try: + return addr_variables[name] + except KeyError: + with ffi._lock: + if name not in addr_variables: + key = 'variable ' + name + tp, _ = ffi._parser._declarations[key] + BType = ffi._get_cached_btype(tp) + if BType.kind != 'array': + BType = model.pointer_cache(ffi, BType) + p = backendlib.load_function(BType, name) + addr_variables[name] = p + return addr_variables[name] + # def accessor_constant(name): raise NotImplementedError("non-integer constant '%s' cannot be " "accessed from a dlopen() library" % (name,)) @@ -800,6 +817,7 @@ def _make_ffi_library(ffi, libname, flags): # accessors = {} accessors_version = [False] + addr_variables = {} # def update_accessors(): if accessors_version[0] is ffi._cdef_version: @@ -850,6 +868,18 @@ def _make_ffi_library(ffi, libname, flags): with ffi._lock: update_accessors() return accessors.keys() + def __addressof__(self, name): + if name in library.__dict__: + return library.__dict__[name] + if name in FFILibrary.__dict__: + return addressof_var(name) + make_accessor(name) + if name in library.__dict__: + return library.__dict__[name] + if name in FFILibrary.__dict__: + return addressof_var(name) + raise AttributeError("cffi library has no function or " + "global variable named '%s'" % (name,)) # if libname is not None: try: diff --git a/doc/source/whatsnew.rst b/doc/source/whatsnew.rst index 9db3b96..d201019 100644 --- a/doc/source/whatsnew.rst +++ b/doc/source/whatsnew.rst @@ -46,6 +46,10 @@ v1.10 * ``ffi.buffer`` is now the name of cffi's buffer type, and ``ffi.buffer()`` works like before but is the constructor of that type. +* ``ffi.addressof(lib, "name")`` now works also in in-line mode, not + only in out-of-line mode. This is useful for taking the address of + global variables. + v1.9 ==== diff --git a/testing/cffi0/test_ownlib.py b/testing/cffi0/test_ownlib.py index afda543..0572a9a 100644 --- a/testing/cffi0/test_ownlib.py +++ b/testing/cffi0/test_ownlib.py @@ -282,3 +282,21 @@ class TestOwnLib(object): assert ret.right == ownlib.right assert ret.top == ownlib.top assert ret.bottom == ownlib.bottom + + def test_addressof_lib(self): + if self.module is None: + py.test.skip("fix the auto-generation of the tiny test lib") + if self.Backend is CTypesBackend: + py.test.skip("not implemented with the ctypes backend") + ffi = FFI(backend=self.Backend()) + ffi.cdef("long left; int test_getting_errno(void);") + lib = ffi.dlopen(self.module) + lib.left = 123456 + p = ffi.addressof(lib, "left") + assert ffi.typeof(p) == ffi.typeof("long *") + assert p[0] == 123456 + p[0] += 1 + assert lib.left == 123457 + pfn = ffi.addressof(lib, "test_getting_errno") + assert ffi.typeof(pfn) == ffi.typeof("int(*)(void)") + assert pfn == lib.test_getting_errno |