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