Author: Armin Rigo <[email protected]>
Branch: static-callback
Changeset: r2390:f1a86aeb8daf
Date: 2015-11-13 17:51 +0100
http://bitbucket.org/cffi/cffi/changeset/f1a86aeb8daf/

Log:    ffi.call_python()

diff --git a/c/_cffi_backend.c b/c/_cffi_backend.c
--- a/c/_cffi_backend.c
+++ b/c/_cffi_backend.c
@@ -4903,7 +4903,8 @@
 
 static int convert_from_object_fficallback(char *result,
                                            CTypeDescrObject *ctype,
-                                           PyObject *pyobj)
+                                           PyObject *pyobj,
+                                           int encode_result_for_libffi)
 {
     /* work work work around a libffi irregularity: for integer return
        types we have to fill at least a complete 'ffi_arg'-sized result
@@ -4919,6 +4920,8 @@
                 return -1;
             }
         }
+        if (!encode_result_for_libffi)
+            goto skip;
         if (ctype->ct_flags & CT_PRIMITIVE_SIGNED) {
             PY_LONG_LONG value;
             /* It's probably fine to always zero-extend, but you never
@@ -4949,6 +4952,7 @@
 #endif
         }
     }
+ skip:
     return convert_from_object(result, ctype, pyobj);
 }
 
@@ -4983,14 +4987,9 @@
     Py_XDECREF(tb);
 }
 
-static void invoke_callback(ffi_cif *cif, void *result, void **args,
-                            void *userdata)
-{
-    save_errno();
-    {
-#ifdef WITH_THREAD
-    PyGILState_STATE state = PyGILState_Ensure();
-#endif
+static void general_invoke_callback(int decode_args_from_libffi,
+                                    void *result, char *args, void *userdata)
+{
     PyObject *cb_args = (PyObject *)userdata;
     CTypeDescrObject *ct = (CTypeDescrObject *)PyTuple_GET_ITEM(cb_args, 0);
     PyObject *signature = ct->ct_stuff;
@@ -5012,7 +5011,20 @@
         goto error;
 
     for (i=0; i<n; i++) {
-        PyObject *a = convert_to_object(args[i], SIGNATURE(2 + i));
+        char *a_src;
+        PyObject *a;
+        CTypeDescrObject *a_ct = SIGNATURE(2 + i);
+
+        if (decode_args_from_libffi) {
+            a_src = ((void **)args)[i];
+        }
+        else {
+            a_src = args + i * 8;
+            if (a_ct->ct_flags & (CT_IS_LONGDOUBLE | CT_STRUCT | CT_UNION)) {
+                abort();
+            }
+        }
+        a = convert_to_object(a_src, a_ct);
         if (a == NULL)
             goto error;
         PyTuple_SET_ITEM(py_args, i, a);
@@ -5021,7 +5033,8 @@
     py_res = PyObject_Call(py_ob, py_args, NULL);
     if (py_res == NULL)
         goto error;
-    if (convert_from_object_fficallback(result, SIGNATURE(1), py_res) < 0) {
+    if (convert_from_object_fficallback(result, SIGNATURE(1), py_res,
+                                        decode_args_from_libffi) < 0) {
         extra_error_line = "Trying to convert the result back to C:\n";
         goto error;
     }
@@ -5029,10 +5042,6 @@
     Py_XDECREF(py_args);
     Py_XDECREF(py_res);
     Py_DECREF(cb_args);
-#ifdef WITH_THREAD
-    PyGILState_Release(state);
-#endif
-    restore_errno();
     return;
 
  error:
@@ -5057,7 +5066,8 @@
                                             NULL);
         if (res1 != NULL) {
             if (res1 != Py_None)
-                convert_from_object_fficallback(result, SIGNATURE(1), res1);
+                convert_from_object_fficallback(result, SIGNATURE(1), res1,
+                                                decode_args_from_libffi);
             Py_DECREF(res1);
         }
         if (!PyErr_Occurred()) {
@@ -5078,25 +5088,38 @@
         }
     }
     goto done;
-    }
 
 #undef SIGNATURE
 }
 
-static PyObject *b_callback(PyObject *self, PyObject *args)
-{
-    CTypeDescrObject *ct, *ctresult;
-    CDataObject *cd;
-    PyObject *ob, *error_ob = Py_None, *onerror_ob = Py_None;
-    PyObject *py_rawerr, *infotuple = NULL;
-    cif_description_t *cif_descr;
-    ffi_closure *closure;
+static void invoke_callback(ffi_cif *cif, void *result, void **args,
+                            void *userdata)
+{
+    save_errno();
+    {
+#ifdef WITH_THREAD
+        PyGILState_STATE state = PyGILState_Ensure();
+#endif
+
+        general_invoke_callback(1, result, (char *)args, userdata);
+
+#ifdef WITH_THREAD
+        PyGILState_Release(state);
+#endif
+    }
+    restore_errno();
+}
+
+static PyObject *prepare_callback_info_tuple(CTypeDescrObject *ct,
+                                             PyObject *ob,
+                                             PyObject *error_ob,
+                                             PyObject *onerror_ob,
+                                             int decode_args_from_libffi)
+{
+    CTypeDescrObject *ctresult;
+    PyObject *py_rawerr, *infotuple;
     Py_ssize_t size;
 
-    if (!PyArg_ParseTuple(args, "O!O|OO:callback", &CTypeDescr_Type, &ct, &ob,
-                          &error_ob, &onerror_ob))
-        return NULL;
-
     if (!(ct->ct_flags & CT_FUNCTIONPTR)) {
         PyErr_Format(PyExc_TypeError, "expected a function ctype, got '%s'",
                      ct->ct_name);
@@ -5125,13 +5148,31 @@
     memset(PyBytes_AS_STRING(py_rawerr), 0, size);
     if (error_ob != Py_None) {
         if (convert_from_object_fficallback(
-                PyBytes_AS_STRING(py_rawerr), ctresult, error_ob) < 0) {
+                PyBytes_AS_STRING(py_rawerr), ctresult, error_ob,
+                decode_args_from_libffi) < 0) {
             Py_DECREF(py_rawerr);
             return NULL;
         }
     }
     infotuple = Py_BuildValue("OOOO", ct, ob, py_rawerr, onerror_ob);
     Py_DECREF(py_rawerr);
+    return infotuple;
+}
+
+static PyObject *b_callback(PyObject *self, PyObject *args)
+{
+    CTypeDescrObject *ct;
+    CDataObject *cd;
+    PyObject *ob, *error_ob = Py_None, *onerror_ob = Py_None;
+    PyObject *infotuple;
+    cif_description_t *cif_descr;
+    ffi_closure *closure;
+
+    if (!PyArg_ParseTuple(args, "O!O|OO:callback", &CTypeDescr_Type, &ct, &ob,
+                          &error_ob, &onerror_ob))
+        return NULL;
+
+    infotuple = prepare_callback_info_tuple(ct, ob, error_ob, onerror_ob, 1);
     if (infotuple == NULL)
         return NULL;
 
diff --git a/c/call_python.c b/c/call_python.c
--- a/c/call_python.c
+++ b/c/call_python.c
@@ -1,3 +1,62 @@
+
+static PyObject *_ffi_call_python_decorator(PyObject *outer_args, PyObject *fn)
+{
+#if PY_MAJOR_VERSION >= 3
+#  error review!
+#endif
+    char *s;
+    PyObject *error, *onerror, *infotuple, *x;
+    int index;
+    const struct _cffi_global_s *g;
+    struct _cffi_callpy_s *callpy;
+    CTypeDescrObject *ct;
+    FFIObject *ffi;
+    builder_c_t *types_builder;
+
+    if (!PyArg_ParseTuple(outer_args, "OzOO", &ffi, &s, &error, &onerror))
+        return NULL;
+
+    if (s == NULL) {
+        abort();
+    }
+
+    types_builder = &ffi->types_builder;
+    index = search_in_globals(&types_builder->ctx, s, strlen(s));
+    if (index < 0)
+        goto not_found;
+    g = &types_builder->ctx.globals[index];
+    if (_CFFI_GETOP(g->type_op) != _CFFI_OP_CALL_PYTHON)
+        goto not_found;
+
+    ct = realize_c_type(types_builder, types_builder->ctx.types,
+                        _CFFI_GETARG(g->type_op));
+    if (ct == NULL)
+        return NULL;
+
+    infotuple = prepare_callback_info_tuple(ct, fn, error, onerror, 0);
+    if (infotuple == NULL) {
+        Py_DECREF(ct);
+        return NULL;
+    }
+
+    /* attach infotuple to reserved1, where it will stay forever
+       unless a new version is attached later */
+    callpy = (struct _cffi_callpy_s *)g->address;
+    x = (PyObject *)callpy->reserved1;
+    callpy->reserved1 = (void *)infotuple;
+    Py_XDECREF(x);
+
+    /* return a cdata of type function-pointer, equal to the one
+       obtained by reading 'lib.bar' (see lib_obj.c) */
+    x = convert_to_object((char *)&g->size_or_direct_fn, ct);
+    Py_DECREF(ct);
+    return x;
+
+ not_found:
+    abort();
+    return NULL;
+}
+
 
 static void _cffi_call_python(struct _cffi_callpy_s *callpy, char *args)
 {
@@ -23,11 +82,9 @@
 #ifdef WITH_THREAD
     PyGILState_STATE state = PyGILState_Ensure();
 #endif
-    const struct _cffi_type_context_s *ctx;
-    ctx = (const struct _cffi_type_context_s *)callpy->reserved1;
 
-    if (ctx == NULL) {
-        /* uninitialized! */
+    if (callpy->reserved1 == NULL) {
+        /* not initialized! */
         PyObject *f = PySys_GetObject("stderr");
         if (f != NULL) {
             PyFile_WriteString("CFFI_CALL_PYTHON: function ", f);
@@ -38,10 +95,10 @@
             PyFile_WriteString("').  Returning 0.\n", f);
         }
         memset(args, 0, callpy->size_of_result);
-        return;
     }
-
-    abort();
+    else {
+        general_invoke_callback(0, args, args, callpy->reserved1);
+    }
 
 #ifdef WITH_THREAD
     PyGILState_Release(state);
diff --git a/c/ffi_obj.c b/c/ffi_obj.c
--- a/c/ffi_obj.c
+++ b/c/ffi_obj.c
@@ -732,6 +732,35 @@
 #define ffi_gc  b_gcp     /* ffi_gc() => b_gcp()
                              from _cffi_backend.c */
 
+PyDoc_STRVAR(ffi_call_python_doc,
+"XXX document me");
+
+/* forward; see call_python.c */
+static PyObject *_ffi_call_python_decorator(PyObject *, PyObject *);
+
+static PyObject *ffi_call_python(FFIObject *self, PyObject *args,
+                                 PyObject *kwds)
+{
+    static PyMethodDef md = {"call_python_decorator",
+                             (PyCFunction)_ffi_call_python_decorator, METH_O};
+    PyObject *name = Py_None, *error = Py_None;
+    PyObject *res, *onerror = Py_None;
+    static char *keywords[] = {"name", "python_callable", "error",
+                               "onerror", NULL};
+
+    if (!PyArg_ParseTupleAndKeywords(args, kwds, "|OOOO", keywords,
+                                     &name, &error, &onerror))
+        return NULL;
+
+    args = Py_BuildValue("(OOOO)", (PyObject *)self, name, error, onerror);
+    if (args == NULL)
+        return NULL;
+
+    res = PyCFunction_New(&md, args);
+    Py_DECREF(args);
+    return res;
+}
+
 PyDoc_STRVAR(ffi_callback_doc,
 "Return a callback object or a decorator making such a callback object.\n"
 "'cdecl' must name a C function pointer type.  The callback invokes the\n"
@@ -875,6 +904,7 @@
  {"addressof",  (PyCFunction)ffi_addressof,  METH_VARARGS, ffi_addressof_doc},
  {"alignof",    (PyCFunction)ffi_alignof,    METH_O,       ffi_alignof_doc},
  {"buffer",     (PyCFunction)ffi_buffer,     METH_VKW,     ffi_buffer_doc},
+ {"call_python",(PyCFunction)ffi_call_python,METH_VKW,     
ffi_call_python_doc},
  {"callback",   (PyCFunction)ffi_callback,   METH_VKW,     ffi_callback_doc},
  {"cast",       (PyCFunction)ffi_cast,       METH_VARARGS, ffi_cast_doc},
  {"dlclose",    (PyCFunction)ffi_dlclose,    METH_VARARGS, ffi_dlclose_doc},
diff --git a/cffi/parse_c_type.h b/cffi/parse_c_type.h
--- a/cffi/parse_c_type.h
+++ b/cffi/parse_c_type.h
@@ -163,8 +163,7 @@
 
 struct _cffi_callpy_s {
     const char *name;
-    int type_index;
-    int size_of_result;
+    size_t size_of_result;
     void *reserved1, *reserved2;
 };
 
diff --git a/cffi/recompiler.py b/cffi/recompiler.py
--- a/cffi/recompiler.py
+++ b/cffi/recompiler.py
@@ -1120,7 +1120,6 @@
 
     def _generate_cpy_call_python_decl(self, tp, name):
         prnt = self._prnt
-        type_index = self._typesdict[tp.as_raw_function()]
         if isinstance(tp.result, model.VoidType):
             size_of_result = '0'
         else:
@@ -1128,7 +1127,7 @@
             size_of_result = '(int)sizeof(%s)' % (
                 tp.result.get_c_name('', context),)
         prnt('static struct _cffi_callpy_s _cffi_callpy__%s =' % name)
-        prnt('  { "%s", %d, %s };' % (name, type_index, size_of_result))
+        prnt('  { "%s", %s };' % (name, size_of_result))
         prnt()
         #
         arguments = []
diff --git a/testing/cffi1/test_recompiler.py b/testing/cffi1/test_recompiler.py
--- a/testing/cffi1/test_recompiler.py
+++ b/testing/cffi1/test_recompiler.py
@@ -1507,6 +1507,16 @@
         "CFFI_CALL_PYTHON: function bar() called, but no code was attached "
         "to it yet with ffi.call_python('bar').  Returning 0.\n")
 
+    @ffi.call_python("bar")
+    def my_bar(x, y):
+        seen.append((x, y))
+        return x * y
+    assert my_bar == lib.bar
+    seen = []
+    res = lib.bar(6, 7)
+    assert seen == [(6, 7)]
+    assert res == 42
+
 def test_call_python_2():
     ffi = FFI()
     ffi.cdef("""
_______________________________________________
pypy-commit mailing list
[email protected]
https://mail.python.org/mailman/listinfo/pypy-commit

Reply via email to