[pygi] Add support for out args in callbacks



commit 8eb809468fe3e1f8e4f92bd7f25d96f9cf802cd4
Author: Tomeu Vizoso <tomeu vizoso collabora co uk>
Date:   Sat May 22 15:12:37 2010 +0200

    Add support for out args in callbacks
    
    This patch refactors argument marshalling for closures in
    preparation for more complete support.
    
    Also fixes a bug in the memory management of user_data args.
    
    https://bugzilla.gnome.org/show_bug.cgi?id=617780

 gi/pygi-closure.c |  335 +++++++++++++++++++++++++++++++++++++++++------------
 tests/test_gi.py  |    4 +
 2 files changed, 263 insertions(+), 76 deletions(-)
---
diff --git a/gi/pygi-closure.c b/gi/pygi-closure.c
index 2c8d0b3..d3e32fa 100644
--- a/gi/pygi-closure.c
+++ b/gi/pygi-closure.c
@@ -27,6 +27,256 @@
  */
 static GSList* async_free_list;
 
+
+static GArgument *
+_pygi_closure_convert_ffi_arguments(GICallableInfo *callable_info, void **args)
+{
+    gint num_args, i;
+    GIArgInfo *arg_info;
+    GITypeInfo *arg_type;
+    GITypeTag tag;
+    GIDirection direction;
+    GArgument *g_args;
+
+    num_args = g_callable_info_get_n_args(callable_info);
+    g_args = g_new0(GArgument, num_args);
+
+    for (i = 0; i < num_args; i++) {
+        arg_info = g_callable_info_get_arg(callable_info, i);
+        arg_type = g_arg_info_get_type(arg_info);
+        tag = g_type_info_get_tag(arg_type);
+        direction = g_arg_info_get_direction(arg_info);
+
+        if (direction == GI_DIRECTION_OUT || direction == GI_DIRECTION_INOUT) {
+            g_args[i].v_pointer = *(gpointer *) args[i];
+        } else {
+            switch (tag) {
+                case GI_TYPE_TAG_BOOLEAN:
+                    g_args[i].v_boolean = *(gboolean *) args[i];
+                    break;
+                case GI_TYPE_TAG_INT8:
+                    g_args[i].v_int8 = *(gint8 *) args[i];
+                    break;
+                case GI_TYPE_TAG_UINT8:
+                    g_args[i].v_uint8 = *(guint8 *) args[i];
+                    break;
+                case GI_TYPE_TAG_INT16:
+                    g_args[i].v_int16 = *(gint16 *) args[i];
+                    break;
+                case GI_TYPE_TAG_UINT16:
+                    g_args[i].v_uint16 = *(guint16 *) args[i];
+                    break;
+                case GI_TYPE_TAG_INT32:
+                    g_args[i].v_int32 = *(gint32 *) args[i];
+                    break;
+                case GI_TYPE_TAG_UINT32:
+                    g_args[i].v_uint32 = *(guint32 *) args[i];
+                    break;
+                case GI_TYPE_TAG_LONG:
+                case GI_TYPE_TAG_INT64:
+                    g_args[i].v_int64 = *(glong *) args[i];
+                    break;
+                case GI_TYPE_TAG_ULONG:
+                case GI_TYPE_TAG_UINT64:
+                    g_args[i].v_uint64 = *(glong *) args[i];
+                    break;
+                case GI_TYPE_TAG_INT:
+                case GI_TYPE_TAG_SSIZE:
+                case GI_TYPE_TAG_SIZE:
+                    g_args[i].v_int32 = *(gint *) args[i];
+                    break;
+                case GI_TYPE_TAG_UINT:
+                    g_args[i].v_uint32 = *(guint *) args[i];
+                    break;
+                case GI_TYPE_TAG_FLOAT:
+                    g_args[i].v_float = *(gfloat *) args[i];
+                    break;
+                case GI_TYPE_TAG_DOUBLE:
+                    g_args[i].v_double = *(gdouble *) args[i];
+                    break;
+                case GI_TYPE_TAG_UTF8:
+                    g_args[i].v_string = *(gchar **) args[i];
+                    break;
+                case GI_TYPE_TAG_INTERFACE:
+                    {
+                        GIBaseInfo *interface;
+                        GIInfoType interface_type;
+
+                        interface = g_type_info_get_interface(arg_type);
+                        interface_type = g_base_info_get_type(interface);
+
+                        if (interface_type == GI_INFO_TYPE_OBJECT ||
+                                interface_type == GI_INFO_TYPE_INTERFACE) {
+                            g_args[i].v_pointer = *(gpointer *) args[i];
+                            g_base_info_unref(interface);
+                            break;
+                        } else if (interface_type == GI_INFO_TYPE_ENUM ||
+                                   interface_type == GI_INFO_TYPE_FLAGS) {
+                            g_args[i].v_double = *(double *) args[i];
+                            g_base_info_unref(interface);
+                            break;
+                        } else if (interface_type == GI_INFO_TYPE_STRUCT) {
+                            g_args[i].v_pointer = *(gpointer *) args[i];
+                            g_base_info_unref(interface);
+                            break;
+                        }
+
+                        g_base_info_unref(interface);
+                    }
+                case GI_TYPE_TAG_GLIST:
+                case GI_TYPE_TAG_GSLIST:
+                    g_args[i].v_pointer = *(gpointer *) args[i];
+                    break;
+                default:
+                    g_args[i].v_pointer = 0;
+            }
+        }
+        g_base_info_unref((GIBaseInfo *) arg_info);
+        g_base_info_unref((GIBaseInfo *) arg_type);
+    }
+    return g_args;
+}
+
+static gboolean
+_pygi_closure_convert_arguments(GICallableInfo *callable_info, void **args,
+                                 void *user_data, PyObject **py_args,
+                                 GArgument **out_args)
+{
+    int n_args = g_callable_info_get_n_args(callable_info);
+    int n_in_args = 0;
+    int n_out_args = 0;
+    int i;
+    GArgument *g_args = NULL;
+
+    *py_args = NULL;
+    *py_args = PyTuple_New(n_args);
+    if (*py_args == NULL)
+        goto error;
+
+    *out_args = NULL;
+    *out_args = g_new0(GArgument, n_args);
+    g_args = _pygi_closure_convert_ffi_arguments(callable_info, args);
+
+    for (i = 0; i < n_args; i++) {
+        GIArgInfo *arg_info = g_callable_info_get_arg(callable_info, i);
+        GIDirection direction = g_arg_info_get_direction(arg_info);
+
+        if (direction == GI_DIRECTION_IN || direction == GI_DIRECTION_INOUT) {
+            GITypeInfo *arg_type = g_arg_info_get_type(arg_info);
+            GITypeTag arg_tag = g_type_info_get_tag(arg_type);
+            GITransfer transfer = g_arg_info_get_ownership_transfer(arg_info);
+            PyObject *value;
+            GArgument *arg;
+
+            if (direction == GI_DIRECTION_IN && arg_tag == GI_TYPE_TAG_VOID &&
+                g_type_info_is_pointer(arg_type)) {
+
+                if (user_data == NULL) {
+                    Py_INCREF(Py_None);
+                    value = Py_None;
+                } else {
+                    value = user_data;
+                    Py_INCREF(value);
+                }
+            } else {
+                if (direction == GI_DIRECTION_IN)
+                    arg = (GArgument*) &g_args[i];
+                else
+                    arg = (GArgument*) g_args[i].v_pointer;
+
+                value = _pygi_argument_to_object(arg, arg_type, transfer);
+                if (value == NULL) {
+                    g_base_info_unref(arg_type);
+                    g_base_info_unref(arg_info);
+                    goto error;
+                }
+            }
+            PyTuple_SET_ITEM(*py_args, n_in_args, value);
+            n_in_args++;
+
+            g_base_info_unref(arg_type);
+        }
+
+        if (direction == GI_DIRECTION_OUT || direction == GI_DIRECTION_INOUT) {
+            (*out_args)[n_out_args] = g_args[i];
+            n_out_args++;
+        }
+
+        g_base_info_unref(arg_info);
+    }
+
+    if (_PyTuple_Resize(py_args, n_in_args) == -1)
+        goto error;
+
+    return TRUE;
+
+error:
+    Py_CLEAR(*py_args);
+
+    if (*out_args != NULL)
+        g_free(*out_args);
+
+    if (g_args != NULL)
+        g_free(g_args);
+
+    return FALSE;
+}
+
+static void
+_pygi_closure_set_out_arguments(GICallableInfo *callable_info,
+                                 PyObject *py_retval, GArgument *out_args,
+                                 void *resp)
+{
+    int n_args, i, i_py_retval, i_out_args;
+    GITypeInfo *return_type_info;
+    GITypeTag return_type_tag;
+
+    i_py_retval = 0;
+    return_type_info = g_callable_info_get_return_type(callable_info);
+    return_type_tag = g_type_info_get_tag(return_type_info);
+    if (return_type_tag != GI_TYPE_TAG_VOID) {
+        GArgument arg;
+        GITransfer transfer = g_callable_info_get_caller_owns(callable_info);
+        if (PyTuple_Check(py_retval)) {
+            PyObject *item = PyTuple_GET_ITEM(py_retval, 0);
+            arg = _pygi_argument_from_object(item, return_type_info, transfer);
+            *((GArgument*)resp) = arg;
+        } else {
+            arg = _pygi_argument_from_object(py_retval, return_type_info, transfer);
+            *((GArgument*)resp) = arg;
+        }
+        i_py_retval++;
+    }
+    g_base_info_unref(return_type_info);
+
+    i_out_args = 0;
+    n_args = g_callable_info_get_n_args(callable_info);
+    for (i = 1; i < n_args; i++) {
+        GIArgInfo *arg_info = g_callable_info_get_arg(callable_info, i);
+        GITypeInfo *type_info = g_arg_info_get_type(arg_info);
+        GIDirection direction = g_arg_info_get_direction(arg_info);
+
+        if (direction == GI_DIRECTION_OUT || direction == GI_DIRECTION_INOUT) {
+            GITransfer transfer = g_arg_info_get_ownership_transfer(arg_info);
+            GArgument arg;
+            if (PyTuple_Check(py_retval)) {
+                PyObject *item = PyTuple_GET_ITEM(py_retval, i_py_retval);
+                arg = _pygi_argument_from_object(item, type_info, transfer);
+                *((GArgument*)out_args[i_out_args].v_pointer) = arg;
+            } else if (i_py_retval == 0) {
+                arg = _pygi_argument_from_object(py_retval, type_info, transfer);
+                *((GArgument*)out_args[i_out_args].v_pointer) = arg;
+            } else
+                g_assert_not_reached();
+
+            i_out_args++;
+            i_py_retval++;
+        }
+        g_base_info_unref(type_info);
+        g_base_info_unref(arg_info);
+    }
+}
+
 void
 _pygi_closure_handle (ffi_cif *cif,
                       void    *result,
@@ -35,20 +285,12 @@ _pygi_closure_handle (ffi_cif *cif,
 {
     PyGILState_STATE state;
     PyGICClosure *closure = data;
-    gint n_args, i;
-    GIArgInfo  *arg_info;
-    GIDirection arg_direction;
-    GITypeInfo *arg_type;
-    GITransfer arg_transfer;
-    GITypeTag  arg_tag;
     GITypeTag  return_tag;
     GITransfer return_transfer;
     GITypeInfo *return_type;
     PyObject *retval;
     PyObject *py_args;
-    PyObject *pyarg;
-    gint n_in_args, n_out_args;
-
+    GArgument *out_args;
 
     /* Lock the GIL as we are coming into this code without the lock and we
       may be executing python code */
@@ -58,72 +300,15 @@ _pygi_closure_handle (ffi_cif *cif,
     return_tag = g_type_info_get_tag(return_type);
     return_transfer = g_callable_info_get_caller_owns(closure->info);
 
-    n_args = g_callable_info_get_n_args (closure->info);  
-
-    py_args = PyTuple_New(n_args);
-    if (py_args == NULL) {
-        PyErr_Print();
-        goto end;
-    }
-
-    n_in_args = 0;
-
-    for (i = 0; i < n_args; i++) {
-        arg_info = g_callable_info_get_arg (closure->info, i);
-        arg_type = g_arg_info_get_type (arg_info);
-        arg_transfer = g_arg_info_get_ownership_transfer(arg_info);
-        arg_tag = g_type_info_get_tag(arg_type);
-        arg_direction = g_arg_info_get_direction(arg_info);
-        switch (arg_tag){
-            case GI_TYPE_TAG_VOID:
-                {
-                    if (g_type_info_is_pointer(arg_type)) {
-                        if (closure->user_data == NULL) {
-                            Py_INCREF(Py_None);
-                            if(PyTuple_SetItem(py_args, n_in_args, Py_None) != 0) {
-                                PyErr_Clear();
-                                goto end;
-                            }
-                        } else if (PyTuple_SetItem(py_args, n_in_args, closure->user_data) != 0) {
-                            PyErr_Print();
-                            goto end;
-                        }
-                        
-                        Py_XINCREF(closure->user_data);
-
-                        n_in_args++;
-                        continue;
-                    }
-                }
-            case GI_TYPE_TAG_ERROR:
-                 {
-                     continue;
-                 }
-            default:
-                {
-                    pyarg = _pygi_argument_to_object (args[i],
-                                                      arg_type,
-                                                      arg_transfer);
-                    
-                    if(PyTuple_SetItem(py_args, n_in_args, pyarg) != 0) {
-                        PyErr_Print();
-                        goto end;
-                    }
-                    n_in_args++;
-                    g_base_info_unref((GIBaseInfo*)arg_info);
-                    g_base_info_unref((GIBaseInfo*)arg_type);                                    
-                }
-        }
-        
-    }
-
-    if(_PyTuple_Resize (&py_args, n_in_args) != 0) {
-        PyErr_Print();
+    if (!_pygi_closure_convert_arguments((GICallableInfo *)closure->info, args,
+                                         closure->user_data,
+                                         &py_args, &out_args)) {
+        if (PyErr_Occurred ())
+            PyErr_Print();
         goto end;
     }
 
     retval = PyObject_CallObject((PyObject *)closure->function, py_args);
-
     Py_DECREF(py_args);
 
     if (retval == NULL) {
@@ -131,16 +316,13 @@ _pygi_closure_handle (ffi_cif *cif,
         goto end;
     }
 
-    *(GArgument*)result = _pygi_argument_from_object(retval, return_type, return_transfer);
+    _pygi_closure_set_out_arguments(closure->info, retval, out_args, result);
 
 end:
     g_base_info_unref((GIBaseInfo*)return_type);
 
     PyGILState_Release(state);
 
-    if (closure->user_data)
-        Py_XDECREF(closure->user_data);
-
     /* Now that the closure has finished we can make a decision about how
        to free it.  Scope call gets free'd at the end of wrap_g_function_info_invoke
        scope notified will be freed,  when the notify is called and we can free async 
@@ -165,7 +347,6 @@ end:
 void _pygi_invoke_closure_free(gpointer data)
 {
     PyGICClosure* invoke_closure = (PyGICClosure *)data;
-    
 
     Py_DECREF(invoke_closure->function);
 
@@ -175,6 +356,8 @@ void _pygi_invoke_closure_free(gpointer data)
     if (invoke_closure->info)
         g_base_info_unref((GIBaseInfo*)invoke_closure->info);
 
+    Py_XDECREF(invoke_closure->user_data);
+
     g_slice_free(PyGICClosure, invoke_closure);
 }
 
diff --git a/tests/test_gi.py b/tests/test_gi.py
index 5e108a3..0a3f771 100644
--- a/tests/test_gi.py
+++ b/tests/test_gi.py
@@ -1409,6 +1409,9 @@ class TestPythonGObject(unittest.TestCase):
         def do_method_int8_in(self, int8):
             self.val = int8
 
+        def do_method_int8_out(self):
+            return 42
+
         def do_method_with_default_implementation(self, int8):
             self.props.int = int8 * 2
 
@@ -1425,6 +1428,7 @@ class TestPythonGObject(unittest.TestCase):
         object_ = self.Object(int = 42)
         object_.method_int8_in(84)
         self.assertEqual(object_.val, 84)
+        self.assertEqual(object_.method_int8_out(), 42)
 
         object_.method_with_default_implementation(42)
         self.assertEqual(object_.val, 84)



[Date Prev][Date Next]   [Thread Prev][Thread Next]   [Thread Index] [Date Index] [Author Index]