[pypy-commit] cffi cffi-1.0: Reasonably messy to implement: ffi.addressof(lib, "var")

arigo noreply at buildbot.pypy.org
Mon May 11 15:36:57 CEST 2015


Author: Armin Rigo <arigo at tunes.org>
Branch: cffi-1.0
Changeset: r1971:758808b32477
Date: 2015-05-11 15:36 +0200
http://bitbucket.org/cffi/cffi/changeset/758808b32477/

Log:	Reasonably messy to implement: ffi.addressof(lib, "var")

diff --git a/_cffi1/cglob.c b/_cffi1/cglob.c
--- a/_cffi1/cglob.c
+++ b/_cffi1/cglob.c
@@ -60,9 +60,13 @@
     return convert_from_object(gs->gs_data, gs->gs_type, obj);
 }
 
-#if 0
-static PyObject *addressof_global_var(GlobSupportObject *gs)
+static PyObject *cg_addressof_global_var(GlobSupportObject *gs)
 {
-    return new_simple_cdata(gs->gs_data, gs->gs_type);
+    PyObject *x, *ptrtype = new_pointer_type(gs->gs_type);
+    if (ptrtype == NULL)
+        return NULL;
+
+    x = new_simple_cdata(gs->gs_data, (CTypeDescrObject *)ptrtype);
+    Py_DECREF(ptrtype);
+    return x;
 }
-#endif
diff --git a/_cffi1/ffi_obj.c b/_cffi1/ffi_obj.c
--- a/_cffi1/ffi_obj.c
+++ b/_cffi1/ffi_obj.c
@@ -19,6 +19,7 @@
 #define FFI_COMPLEXITY_OUTPUT   1200     /* xxx should grow as needed */
 
 #define FFIObject_Check(op) PyObject_TypeCheck(op, &FFI_Type)
+#define LibObject_Check(ob)  ((Py_TYPE(ob) == &Lib_Type))
 
 struct FFIObject_s {
     PyObject_HEAD
@@ -368,10 +369,19 @@
 }
 
 PyDoc_STRVAR(ffi_addressof_doc,
-"With a single arg, return the address of a <cdata 'struct-or-union'>.\n"
-"If 'fields_or_indexes' are given, returns the address of that field or\n"
-"array item in the structure or array, recursively in case of nested\n"
-"structures.");
+"Limited equivalent to the '&' operator in C:\n"
+"\n"
+"1. ffi.addressof(<cdata 'struct-or-union'>) returns a cdata that is a\n"
+"pointer to this struct or union.\n"
+"\n"
+"2. ffi.addressof(<cdata>, field-or-index...) returns the address of a\n"
+"field or array item inside the given structure or array, recursively\n"
+"in case of nested structures or arrays.\n"
+"\n"
+"3. ffi.addressof(<library>, \"name\") returns the address of the named\n"
+"global variable.");
+
+static PyObject *address_of_global_var(PyObject *args);  /* forward */
 
 static PyObject *ffi_addressof(FFIObject *self, PyObject *args)
 {
@@ -387,11 +397,17 @@
     }
 
     arg = PyTuple_GET_ITEM(args, 0);
+    if (LibObject_Check(arg)) {
+        /* case 3 in the docstring */
+        return address_of_global_var(args);
+    }
+
     ct = _ffi_type(self, arg, ACCEPT_CDATA);
     if (ct == NULL)
         return NULL;
 
     if (PyTuple_GET_SIZE(args) == 1) {
+        /* case 1 in the docstring */
         accepted_flags = CT_STRUCT | CT_UNION | CT_ARRAY;
         if ((ct->ct_flags & accepted_flags) == 0) {
             PyErr_SetString(PyExc_TypeError,
@@ -400,6 +416,7 @@
         }
     }
     else {
+        /* case 2 in the docstring */
         accepted_flags = CT_STRUCT | CT_UNION | CT_ARRAY | CT_POINTER;
         if ((ct->ct_flags & accepted_flags) == 0) {
             PyErr_SetString(PyExc_TypeError,
diff --git a/_cffi1/lib_obj.c b/_cffi1/lib_obj.c
--- a/_cffi1/lib_obj.c
+++ b/_cffi1/lib_obj.c
@@ -27,15 +27,9 @@
     FFIObject *l_ffi;           /* reference back to the ffi object */
 };
 
-#define LibObject_Check(ob)  ((Py_TYPE(ob) == &Lib_Type))
-
-static PyObject *_cpyextfunc_type_index(PyObject *x)
+static struct CPyExtFunc_s *_cpyextfunc_get(PyObject *x)
 {
     struct CPyExtFunc_s *exf;
-    LibObject *lib;
-    PyObject *tuple, *result;
-
-    assert(PyErr_Occurred());
 
     if (!PyCFunction_Check(x))
         return NULL;
@@ -46,6 +40,20 @@
     if (exf->md.ml_doc != cpyextfunc_doc)
         return NULL;
 
+    return exf;
+}
+
+static PyObject *_cpyextfunc_type_index(PyObject *x)
+{
+    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);
@@ -269,14 +277,21 @@
     return x;
 }
 
+#define LIB_GET_OR_CACHE_ADDR(x, lib, name, error)      \
+    do {                                                \
+        x = PyDict_GetItem(lib->l_dict, name);          \
+        if (x == NULL) {                                \
+            x = lib_build_and_cache_attr(lib, name, 0); \
+            if (x == NULL) {                            \
+                error;                                  \
+            }                                           \
+        }                                               \
+    } while (0)
+
 static PyObject *lib_getattr(LibObject *lib, PyObject *name)
 {
-    PyObject *x = PyDict_GetItem(lib->l_dict, name);
-    if (x == NULL) {
-        x = lib_build_and_cache_attr(lib, name, 0);
-        if (x == NULL)
-            return NULL;
-    }
+    PyObject *x;
+    LIB_GET_OR_CACHE_ADDR(x, lib, name, return NULL);
 
     if (GlobSupport_Check(x)) {
         return read_global_var((GlobSupportObject *)x);
@@ -287,12 +302,8 @@
 
 static int lib_setattr(LibObject *lib, PyObject *name, PyObject *val)
 {
-    PyObject *x = PyDict_GetItem(lib->l_dict, name);
-    if (x == NULL) {
-        x = lib_build_and_cache_attr(lib, name, 0);
-        if (x == NULL)
-            return -1;
-    }
+    PyObject *x;
+    LIB_GET_OR_CACHE_ADDR(x, lib, name, return -1);
 
     if (val == NULL) {
         PyErr_SetString(PyExc_AttributeError, "C attribute cannot be deleted");
@@ -396,3 +407,44 @@
     lib->l_ffi = ffi;
     return lib;
 }
+
+static PyObject *address_of_global_var(PyObject *args)
+{
+    LibObject *lib;
+    PyObject *x, *o_varname;
+    char *varname;
+
+    if (!PyArg_ParseTuple(args, "O!s", &Lib_Type, &lib, &varname))
+        return NULL;
+
+    /* rebuild a string from 'varname', to do typechecks and to force
+       a unicode back to a plain string */
+    o_varname = PyString_FromString(varname);
+    if (o_varname == NULL)
+        return NULL;
+
+    LIB_GET_OR_CACHE_ADDR(x, lib, o_varname, goto error);
+    Py_DECREF(o_varname);
+    if (GlobSupport_Check(x)) {
+        return cg_addressof_global_var((GlobSupportObject *)x);
+    }
+    else {
+        struct CPyExtFunc_s *exf = _cpyextfunc_get(x);
+        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))) {
+            Py_INCREF(x);
+            return x;
+        }
+        else {
+            PyErr_Format(PyExc_AttributeError,
+                         "cannot take the address of the constant '%.200s'",
+                         varname);
+            return NULL;
+        }
+    }
+
+ error:
+    Py_DECREF(o_varname);
+    return NULL;
+}
diff --git a/_cffi1/test_recompiler.py b/_cffi1/test_recompiler.py
--- a/_cffi1/test_recompiler.py
+++ b/_cffi1/test_recompiler.py
@@ -707,3 +707,39 @@
     assert repr(ffi.typeof("foo_t")) == "<ctype 'foo_t'>"
     assert repr(ffi.typeof("bar_p")) == "<ctype 'struct $1 *'>"
     assert repr(ffi.typeof("baz_pp")) == "<ctype 'struct $2 * *'>"
+
+def test_address_of_global_var():
+    ffi = FFI()
+    ffi.cdef("""
+        long bottom, bottoms[2];
+        long FetchRectBottom(void);
+        long FetchRectBottoms1(void);
+        #define FOOBAR 42
+    """)
+    lib = verify(ffi, "test_address_of_global_var", """
+        long bottom, bottoms[2];
+        long FetchRectBottom(void) { return bottom; }
+        long FetchRectBottoms1(void) { return bottoms[1]; }
+        #define FOOBAR 42
+    """)
+    lib.bottom = 300
+    assert lib.FetchRectBottom() == 300
+    lib.bottom += 1
+    assert lib.FetchRectBottom() == 301
+    lib.bottoms[1] = 500
+    assert lib.FetchRectBottoms1() == 500
+    lib.bottoms[1] += 2
+    assert lib.FetchRectBottoms1() == 502
+    #
+    p = ffi.addressof(lib, 'bottom')
+    assert ffi.typeof(p) == ffi.typeof("long *")
+    assert p[0] == 301
+    p[0] += 1
+    assert lib.FetchRectBottom() == 302
+    p = ffi.addressof(lib, 'bottoms')
+    assert ffi.typeof(p) == ffi.typeof("long(*)[2]")
+    assert p[0] == lib.bottoms
+    #
+    py.test.raises(AttributeError, ffi.addressof, lib, 'unknown_var')
+    py.test.raises(AttributeError, ffi.addressof, lib, "FOOBAR")
+    assert ffi.addressof(lib, 'FetchRectBottom') == lib.FetchRectBottom


More information about the pypy-commit mailing list