[pypy-commit] cffi cffi-1.0: in-progress: loading C extension modules and calling functions from them

arigo noreply at buildbot.pypy.org
Tue Apr 14 17:28:48 CEST 2015


Author: Armin Rigo <arigo at tunes.org>
Branch: cffi-1.0
Changeset: r1704:102edaf76b7c
Date: 2015-04-14 17:29 +0200
http://bitbucket.org/cffi/cffi/changeset/102edaf76b7c/

Log:	in-progress: loading C extension modules and calling functions from
	them

diff --git a/new/cffi1_module.c b/new/cffi1_module.c
--- a/new/cffi1_module.c
+++ b/new/cffi1_module.c
@@ -10,7 +10,7 @@
 static PyObject *FFIError;
 
 #include "ffi_obj.c"
-//#include "lib_obj.c"
+#include "lib_obj.c"
 
 
 static int init_ffi_lib(PyObject *m)
@@ -32,6 +32,9 @@
     Py_INCREF(&FFI_Type);
     if (PyModule_AddObject(m, "FFI", (PyObject *)&FFI_Type) < 0)
         return -1;
+    Py_INCREF(&Lib_Type);
+    if (PyModule_AddObject(m, "Lib", (PyObject *)&Lib_Type) < 0)
+        return -1;
 
     return 0;
 }
@@ -47,5 +50,9 @@
     if (ffi == NULL || PyModule_AddObject(m, "ffi", (PyObject *)ffi) < 0)
         return -1;
 
+    LibObject *lib = lib_internal_new(ctx, module_name);
+    if (lib == NULL || PyModule_AddObject(m, "lib", (PyObject *)lib) < 0)
+        return -1;
+
     return 0;
 }
diff --git a/new/lib_obj.c b/new/lib_obj.c
--- a/new/lib_obj.c
+++ b/new/lib_obj.c
@@ -12,69 +12,143 @@
    __getattr__ which returns C globals, functions and constants.  It
    raises AttributeError for anything else, like '__class__'.
 
-   A Lib object has internally a reference back to the FFI object,
-   which holds the _cffi_type_context_s used to create lazily the
-   objects returned by __getattr__.  For a dlopen()ed Lib object, all
-   the 'address' fields in _cffi_global_s are NULL, and instead
-   dlsym() is used lazily on the l_dl_lib.
+   A Lib object has got a reference to the _cffi_type_context_s
+   structure, which is used to create lazily the objects returned by
+   __getattr__.  For a dlopen()ed Lib object, all the 'address' fields
+   in _cffi_global_s are NULL, and instead dlsym() is used lazily on
+   the l_dl_lib.
 */
 
+struct CPyExtFunc_s {
+    PyMethodDef md;
+    const struct _cffi_type_context_s *ctx;
+    int type_index;
+};
+
 struct LibObject_s {
     PyObject_HEAD
+    const struct _cffi_type_context_s *l_ctx;  /* ctx object */
     PyObject *l_dict;           /* content, built lazily */
-    struct FFIObject_s *l_ffi;  /* ffi object */
     void *l_dl_lib;             /* the result of 'dlopen()', or NULL */
+    PyObject *l_libname;        /* some string that gives the name of the lib */
 };
 
-#define ZefLib_Check(ob)  ((Py_TYPE(ob) == &ZefLib_Type))
+#define LibObject_Check(ob)  ((Py_TYPE(ob) == &Lib_Type))
 
-static void lib_dealloc(ZefLibObject *lib)
+static int lib_close(LibObject *lib);    /* forward */
+
+static void lib_dealloc(LibObject *lib)
 {
     (void)lib_close(lib);
+    Py_DECREF(lib->l_dict);
+    Py_DECREF(lib->l_libname);
     PyObject_Del(lib);
 }
 
-static PyObject *lib_repr(ZefLibObject *lib)
+static PyObject *lib_repr(LibObject *lib)
 {
-    return PyText_FromFormat("<zeffir.Lib object for '%.200s'%s>",
-                             lib->l_libname,
-                             lib->l_dl_lib == NULL ? " (closed)" : "");
+    return PyText_FromFormat("<cffi.Lib object for '%.200s'>",
+                             PyText_AS_UTF8(lib->l_libname));
 }
 
-static PyObject *lib_findattr(ZefLibObject *lib, PyObject *name, PyObject *exc)
+static PyObject *lib_build_cpython_func(LibObject *lib,
+                                        const struct _cffi_global_s *g,
+                                        const char *s, int flags)
+{
+    /* xxx the few bytes of memory we allocate here leak, but it's a
+       minor concern because it should only occur for CPYTHON_BLTN.
+       There is one per real C function in a CFFI C extension module.
+       CPython never unloads its C extension modules anyway.
+    */
+    struct CPyExtFunc_s *xfunc = calloc(1, sizeof(struct CPyExtFunc_s));
+    if (xfunc == NULL)
+        goto no_memory;
+
+    xfunc->md.ml_meth = (PyCFunction)g->address;
+    xfunc->md.ml_flags = flags;
+    xfunc->md.ml_name = strdup(s);
+    /*xfunc->md.ml_doc = ... */
+    if (xfunc->md.ml_name == NULL)
+        goto no_memory;
+
+    xfunc->ctx = lib->l_ctx;
+    xfunc->type_index = _CFFI_GETARG(g->type_op);
+
+    return PyCFunction_NewEx(&xfunc->md, NULL, lib->l_libname);
+
+ no_memory:
+    PyErr_NoMemory();
+    return NULL;
+}
+
+static PyObject *lib_build_and_cache_attr(LibObject *lib, PyObject *name)
 {
     /* does not return a new reference! */
 
-    if (lib->l_dict == NULL) {
-        PyErr_Format(ZefError, "lib '%.200s' was closed", lib->l_libname);
+    if (lib->l_ctx == NULL) {
+        PyErr_Format(FFIError, "lib '%.200s' is already closed",
+                     PyText_AS_UTF8(lib->l_libname));
         return NULL;
     }
 
-    PyObject *x = PyDict_GetItem(lib->l_dict, name);
-    if (x == NULL) {
-        PyErr_Format(exc,
+    char *s = PyText_AsUTF8(name);
+    if (s == NULL)
+        return NULL;
+
+    int index = search_in_globals(lib->l_ctx, s, strlen(s));
+    if (index < 0) {
+        PyErr_Format(PyExc_AttributeError,
                      "lib '%.200s' has no function,"
-                     " global variable or constant '%.200s'",
-                     lib->l_libname,
+                     " global variable or constant named '%.200s'",
+                     PyText_AS_UTF8(lib->l_libname),
                      PyText_Check(name) ? PyText_AS_UTF8(name) : "?");
         return NULL;
     }
+
+    const struct _cffi_global_s *g = &lib->l_ctx->globals[index];
+    PyObject *x;
+
+    switch (_CFFI_GETOP(g->type_op)) {
+
+    case _CFFI_OP_CPYTHON_BLTN_V:
+        x = lib_build_cpython_func(lib, g, s, METH_VARARGS);
+        break;
+
+    case _CFFI_OP_CPYTHON_BLTN_N:
+        x = lib_build_cpython_func(lib, g, s, METH_NOARGS);
+        break;
+
+    case _CFFI_OP_CPYTHON_BLTN_O:
+        x = lib_build_cpython_func(lib, g, s, METH_O);
+        break;
+
+    default:
+        PyErr_SetString(PyExc_NotImplementedError, "in lib_build_attr");
+        return NULL;
+    }
+
+    if (x != NULL) {
+        int err = PyDict_SetItem(lib->l_dict, name, x);
+        Py_DECREF(x);
+        if (err < 0)     /* else there is still one ref left in the dict */
+            return NULL;
+    }
     return x;
 }
 
-static PyObject *lib_getattr(ZefLibObject *lib, PyObject *name)
+static PyObject *lib_getattr(LibObject *lib, PyObject *name)
 {
-    PyObject *x = lib_findattr(lib, name, PyExc_AttributeError);
+    PyObject *x = PyDict_GetItem(lib->l_dict, name);
     if (x == NULL)
-        return NULL;
+        x = lib_build_and_cache_attr(lib, name);
 
-    if (ZefGlobSupport_Check(x)) {
-        return read_global_var((ZefGlobSupportObject *)x);
-    }
-    Py_INCREF(x);
+    //if (ZefGlobSupport_Check(x)) {
+    //    return read_global_var((ZefGlobSupportObject *)x);
+    //}
     return x;
 }
 
+#if 0
 static int lib_setattr(ZefLibObject *lib, PyObject *name, PyObject *val)
 {
     PyObject *x = lib_findattr(lib, name, PyExc_AttributeError);
@@ -106,11 +180,12 @@
     {"__dir__",   lib_dir,  METH_NOARGS},
     {NULL,        NULL}           /* sentinel */
 };
+#endif
 
-static PyTypeObject ZefLib_Type = {
+static PyTypeObject Lib_Type = {
     PyVarObject_HEAD_INIT(NULL, 0)
-    "zeffir.Lib",
-    sizeof(ZefLibObject),
+    "cffi.Lib",
+    sizeof(LibObject),
     0,
     (destructor)lib_dealloc,                    /* tp_dealloc */
     0,                                          /* tp_print */
@@ -125,7 +200,11 @@
     0,                                          /* tp_call */
     0,                                          /* tp_str */
     (getattrofunc)lib_getattr,                  /* tp_getattro */
+#if 0 // XXX
     (setattrofunc)lib_setattr,                  /* tp_setattro */
+#else
+    0,
+#endif
     0,                                          /* tp_as_buffer */
     Py_TPFLAGS_DEFAULT,                         /* tp_flags */
     0,                                          /* tp_doc */
@@ -135,63 +214,68 @@
     0,                                          /* tp_weaklistoffset */
     0,                                          /* tp_iter */
     0,                                          /* tp_iternext */
+#if 0 // XXX
     lib_methods,                                /* tp_methods */
+#else
+    0,
+#endif
     0,                                          /* tp_members */
     0,                                          /* tp_getset */
     0,                                          /* tp_base */
     0,                                          /* tp_dict */
     0,                                          /* tp_descr_get */
     0,                                          /* tp_descr_set */
-    offsetof(ZefLibObject, l_dict),             /* tp_dictoffset */
+    offsetof(LibObject, l_dict),                /* tp_dictoffset */
 };
 
-static void lib_dlerror(ZefLibObject *lib)
+
+static void lib_dlerror(LibObject *lib)
 {
     char *error = dlerror();
     if (error == NULL)
         error = "(no error reported)";
-    PyErr_Format(PyExc_OSError, "%s: %s", lib->l_libname, error);
+    PyErr_Format(PyExc_OSError, "%s: %s", PyText_AS_UTF8(lib->l_libname),
+                 error);
 }
 
-static ZefLibObject *lib_create(PyObject *path)
+static int lib_close(LibObject *lib)
 {
-    ZefLibObject *lib;
+    void *dll;
+    lib->l_ctx = NULL;
+    PyDict_Clear(lib->l_dict);
 
-    lib = PyObject_New(ZefLibObject, &ZefLib_Type);
-    if (lib == NULL)
-        return NULL;
-
-    lib->l_dl_lib = NULL;
-    lib->l_libname = PyString_AsString(path);
-    Py_INCREF(path);
-    lib->l_libname_obj = path;
-    lib->l_dict = PyDict_New();
-    if (lib->l_dict == NULL) {
-        Py_DECREF(lib);
-        return NULL;
-    }
-
-    lib->l_dl_lib = dlopen(lib->l_libname, RTLD_LAZY);
-    if (lib->l_dl_lib == NULL) {
-        lib_dlerror(lib);
-        Py_DECREF(lib);
-        return NULL;
-    }
-    return lib;
-}
-
-static int lib_close(ZefLibObject *lib)
-{
-    void *dl_lib;
-    Py_CLEAR(lib->l_dict);
-
-    dl_lib = lib->l_dl_lib;
-    if (dl_lib != NULL) {
+    dll = lib->l_dl_lib;
+    if (dll != NULL) {
         lib->l_dl_lib = NULL;
-        if (dlclose(dl_lib) != 0) {
+        if (dlclose(dll) != 0) {
             lib_dlerror(lib);
             return -1;
         }
     }
     return 0;
 }
+
+static LibObject *lib_internal_new(const struct _cffi_type_context_s *ctx,
+                                   char *module_name)
+{
+    LibObject *lib;
+    PyObject *libname, *dict;
+
+    libname = PyString_FromString(module_name);
+    dict = PyDict_New();
+    if (libname == NULL || dict == NULL) {
+        Py_XDECREF(dict);
+        Py_XDECREF(libname);
+        return NULL;
+    }
+
+    lib = PyObject_New(LibObject, &Lib_Type);
+    if (lib == NULL)
+        return NULL;
+
+    lib->l_ctx = ctx;
+    lib->l_dict = dict;
+    lib->l_dl_lib = NULL;
+    lib->l_libname = libname;
+    return lib;
+}
diff --git a/new/manual.c b/new/manual.c
--- a/new/manual.c
+++ b/new/manual.c
@@ -80,8 +80,8 @@
 }
 
 static const struct _cffi_global_s _cffi_globals[] = {
-    { "foo42", &_cffi_f_foo42, _CFFI_OP(_CFFI_OP_CPYTHON_BLTN, 0) },
-    { "foo64", &_cffi_f_foo64, _CFFI_OP(_CFFI_OP_CPYTHON_BLTN, 4) },
+    { "foo42", &_cffi_f_foo42, _CFFI_OP(_CFFI_OP_CPYTHON_BLTN_V, 0) },
+    { "foo64", &_cffi_f_foo64, _CFFI_OP(_CFFI_OP_CPYTHON_BLTN_O, 4) },
 };
 
 static const struct _cffi_type_context_s _cffi_type_context = {
@@ -92,7 +92,7 @@
     NULL,
     NULL,
     NULL,
-    1,  /* num_globals */
+    2,  /* num_globals */
     0,
     0,
     0,
diff --git a/new/parse_c_type.c b/new/parse_c_type.c
--- a/new/parse_c_type.c
+++ b/new/parse_c_type.c
@@ -349,43 +349,33 @@
     return _CFFI_GETARG(result);
 }
 
-static int search_struct_union(const struct _cffi_type_context_s *ctx,
-                               const char *search, size_t search_len)
-{
-    int left = 0, right = ctx->num_structs_unions;
 
-    while (left < right) {
-        int middle = (left + right) / 2;
-        const char *src = ctx->structs_unions[middle].name;
-        int diff = strncmp(src, search, search_len);
-        if (diff == 0 && src[search_len] == '\0')
-            return middle;
-        else if (diff >= 0)
-            right = middle;
-        else
-            left = middle + 1;
-    }
-    return -1;
-}
+#define MAKE_SEARCH_FUNC(FIELD)                                 \
+  int search_in_##FIELD(const struct _cffi_type_context_s *ctx, \
+                        const char *search, size_t search_len)  \
+  {                                                             \
+      int left = 0, right = ctx->num_##FIELD;                   \
+                                                                \
+      while (left < right) {                                    \
+          int middle = (left + right) / 2;                      \
+          const char *src = ctx->FIELD[middle].name;            \
+          int diff = strncmp(src, search, search_len);          \
+          if (diff == 0 && src[search_len] == '\0')             \
+              return middle;                                    \
+          else if (diff >= 0)                                   \
+              right = middle;                                   \
+          else                                                  \
+              left = middle + 1;                                \
+      }                                                         \
+      return -1;                                                \
+  }
 
-static int search_typename(const struct _cffi_type_context_s *ctx,
-                           const char *search, size_t search_len)
-{
-    int left = 0, right = ctx->num_typenames;
+MAKE_SEARCH_FUNC(globals)
+MAKE_SEARCH_FUNC(structs_unions)
+MAKE_SEARCH_FUNC(typenames)
 
-    while (left < right) {
-        int middle = (left + right) / 2;
-        const char *src = ctx->typenames[middle].name;
-        int diff = strncmp(src, search, search_len);
-        if (diff == 0 && src[search_len] == '\0')
-            return middle;
-        else if (diff >= 0)
-            right = middle;
-        else
-            left = middle + 1;
-    }
-    return -1;
-}
+#undef MAKE_SEARCH_FUNC
+
 
 static int parse_complete(token_t *tok)
 {
@@ -512,7 +502,7 @@
             break;
         case TOK_IDENTIFIER:
         {
-            int n = search_typename(tok->info->ctx, tok->p, tok->size);
+            int n = search_in_typenames(tok->info->ctx, tok->p, tok->size);
             if (n < 0)
                 return parse_error(tok, "undefined type name");
 
@@ -527,7 +517,7 @@
             if (tok->kind != TOK_IDENTIFIER)
                 return parse_error(tok, "struct or union name expected");
 
-            int n = search_struct_union(tok->info->ctx, tok->p, tok->size);
+            int n = search_in_structs_unions(tok->info->ctx, tok->p, tok->size);
             if (n < 0)
                 return parse_error(tok, "undefined struct/union name");
             if (((tok->info->ctx->structs_unions[n].flags & CT_UNION) != 0)
diff --git a/new/parse_c_type.h b/new/parse_c_type.h
--- a/new/parse_c_type.h
+++ b/new/parse_c_type.h
@@ -18,7 +18,9 @@
 #define _CFFI_OP_FUNCTION_END   17
 #define _CFFI_OP_NOOP           19
 #define _CFFI_OP_BITFIELD       21
-#define _CFFI_OP_CPYTHON_BLTN   23
+#define _CFFI_OP_CPYTHON_BLTN_V 23   // varargs
+#define _CFFI_OP_CPYTHON_BLTN_N 25   // noargs
+#define _CFFI_OP_CPYTHON_BLTN_O 27   // O  (i.e. a single arg)
 
 #define _CFFI_PRIM_VOID          0
 #define _CFFI_PRIM_BOOL          1
@@ -103,3 +105,5 @@
 };
 
 int parse_c_type(struct _cffi_parse_info_s *info, const char *input);
+int search_in_globals(const struct _cffi_type_context_s *ctx,
+                      const char *search, size_t search_len);


More information about the pypy-commit mailing list