summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorArmin Rigo <arigo@tunes.org>2017-02-07 16:28:20 +0100
committerArmin Rigo <arigo@tunes.org>2017-02-07 16:28:20 +0100
commitf5d76e1b0d614520e6c9b79e995b14c177dc9575 (patch)
tree8d9b918c6f6419296292a876c52ffcfa27520804
parent0e4e34033dcbe9d4ff4070e5754fac392d79fff3 (diff)
downloadcffi-f5d76e1b0d614520e6c9b79e995b14c177dc9575.tar.gz
ffi.addressof(lib, "name") now also works in in-line mode
-rw-r--r--c/_cffi_backend.c18
-rw-r--r--cffi/api.py40
-rw-r--r--doc/source/whatsnew.rst4
-rw-r--r--testing/cffi0/test_ownlib.py18
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