[pygi] Move invocation code to its own file



commit 9669acd0fad193013ef3505ae231588307f9834c
Author: Tomeu Vizoso <tomeu sugarlabs org>
Date:   Mon May 3 12:23:58 2010 +0200

    Move invocation code to its own file
    
    https://bugzilla.gnome.org/show_bug.cgi?id=617107

 gi/Makefile.am    |    2 +
 gi/pygi-info.c    |  884 ---------------------------------------------------
 gi/pygi-invoke.c  |  909 +++++++++++++++++++++++++++++++++++++++++++++++++++++
 gi/pygi-invoke.h  |   37 +++
 gi/pygi-private.h |    1 +
 5 files changed, 949 insertions(+), 884 deletions(-)
---
diff --git a/gi/Makefile.am b/gi/Makefile.am
index ff32761..8efe051 100644
--- a/gi/Makefile.am
+++ b/gi/Makefile.am
@@ -30,6 +30,8 @@ _gi_la_SOURCES = \
 	pygi-repository.h \
 	pygi-info.c \
 	pygi-info.h \
+	pygi-invoke.c \
+	pygi-invoke.h \
 	pygi-foreign.c \
 	pygi-foreign.h \
 	pygi-foreign-cairo.c \
diff --git a/gi/pygi-info.c b/gi/pygi-info.c
index 44ab832..9882fd7 100644
--- a/gi/pygi-info.c
+++ b/gi/pygi-info.c
@@ -486,890 +486,6 @@ _pygi_g_type_info_size (GITypeInfo *type_info)
     return size;
 }
 
-struct invocation_state
-{
-    gboolean is_method;
-    gboolean is_constructor;
-
-    gsize n_args;
-    gsize n_in_args;
-    gsize n_out_args;
-    gsize n_backup_args;
-    Py_ssize_t n_py_args;
-    gsize n_aux_in_args;
-    gsize n_aux_out_args;
-    gsize n_return_values;
-
-    guint8 callback_index;
-    guint8 user_data_index;
-    guint8 destroy_notify_index;
-    PyGICClosure *closure;
-    
-    glong error_arg_pos;
-
-    GIArgInfo **arg_infos;
-    GITypeInfo **arg_type_infos;
-    GITypeInfo *return_type_info;
-    GITypeTag return_type_tag;
-
-    GArgument **args;
-    gboolean *args_is_auxiliary;
-
-    GArgument *in_args;
-    GArgument *out_args;
-    GArgument *out_values;
-    GArgument *backup_args;
-    GArgument return_arg;
-
-    PyObject *return_value;
-};
-
-static void
-_initialize_invocation_state (struct invocation_state *state,
-                              GIFunctionInfo *info,
-                              PyObject *py_args)
-{
-    GIFunctionInfoFlags flags;
-
-    flags = g_function_info_get_flags(info);
-    state->is_method = (flags & GI_FUNCTION_IS_METHOD) != 0;
-    state->is_constructor = (flags & GI_FUNCTION_IS_CONSTRUCTOR) != 0;
-
-    /* Count arguments. */
-    state->n_args = g_callable_info_get_n_args((GICallableInfo *)info);
-    state->n_in_args = 0;
-    state->n_out_args = 0;
-    state->n_backup_args = 0;
-    state->n_aux_in_args = 0;
-    state->n_aux_out_args = 0;
-    
-    /* Check the argument count. */
-    state->n_py_args = PyTuple_Size(py_args);
-    g_assert(state->n_py_args >= 0);
-
-    state->error_arg_pos = -1;
-
-    state->arg_infos = g_slice_alloc0(sizeof(gpointer) * state->n_args);
-    state->arg_type_infos = g_slice_alloc0(sizeof(gpointer) * state->n_args);
-    state->args_is_auxiliary = g_slice_alloc0(sizeof(gboolean) * state->n_args);
-
-    state->return_value = NULL;
-    state->closure = NULL;
-    state->return_type_info = NULL;
-    state->args = NULL;
-    state->in_args = NULL;
-    state->out_args = NULL;
-    state->out_values = NULL;
-    state->backup_args = NULL;
-}
-
-static gboolean
-_prepare_invocation_state (struct invocation_state *state,
-                           GIFunctionInfo *function_info, PyObject *py_args)
-{
-    gsize i;
-
-    if (!_pygi_scan_for_callbacks (function_info, state->is_method,
-                                   &state->callback_index, &state->user_data_index,
-                                   &state->destroy_notify_index))
-        return FALSE;
-        
-    if (state->callback_index != G_MAXUINT8) {
-        if (!_pygi_create_callback (function_info, state->is_method, 
-                                    state->n_args, state->n_py_args, 
-                                    py_args, state->callback_index,
-                                    state->user_data_index,
-                                    state->destroy_notify_index, &state->closure))
-            return FALSE;
-
-        state->args_is_auxiliary[state->callback_index] = FALSE;
-        if (state->destroy_notify_index != G_MAXUINT8) {
-            state->args_is_auxiliary[state->destroy_notify_index] = TRUE;
-            state->n_aux_in_args += 1;
-        }
-    }
-
-    if (state->is_method) {
-        /* The first argument is the instance. */
-        state->n_in_args += 1;
-    }
-
-    /* We do a first (well, second) pass here over the function to scan for special cases.
-     * This is currently array+length combinations and GError.
-     */
-    for (i = 0; i < state->n_args; i++) {
-        GIDirection direction;
-        GITransfer transfer;
-        GITypeTag arg_type_tag;
-        
-        state->arg_infos[i] = g_callable_info_get_arg((GICallableInfo *)function_info,
-                                                 i);
-
-        state->arg_type_infos[i] = g_arg_info_get_type(state->arg_infos[i]);
-        
-        direction = g_arg_info_get_direction(state->arg_infos[i]);
-        transfer = g_arg_info_get_ownership_transfer(state->arg_infos[i]);
-        arg_type_tag = g_type_info_get_tag(state->arg_type_infos[i]);
-
-        if (direction == GI_DIRECTION_IN || direction == GI_DIRECTION_INOUT) {
-            state->n_in_args += 1;
-            if (transfer == GI_TRANSFER_CONTAINER) {
-                state->n_backup_args += 1;
-            }
-        }
-        if (direction == GI_DIRECTION_OUT || direction == GI_DIRECTION_INOUT) {
-            state->n_out_args += 1;
-        }
-
-        if (direction == GI_DIRECTION_INOUT && transfer == GI_TRANSFER_NOTHING) {
-            state->n_backup_args += 1;
-        }
-
-        switch (arg_type_tag) {
-            case GI_TYPE_TAG_ARRAY:
-            {
-                gint length_arg_pos;
-
-                length_arg_pos = g_type_info_get_array_length(state->arg_type_infos[i]);
-
-                if (state->is_method)
-                    length_arg_pos--; // length_arg_pos refers to C args
-
-                if (length_arg_pos < 0) {
-                    break;
-                }
-
-                g_assert(length_arg_pos < state->n_args);
-                state->args_is_auxiliary[length_arg_pos] = TRUE;
-
-                if (direction == GI_DIRECTION_IN || direction == GI_DIRECTION_INOUT) {
-                    state->n_aux_in_args += 1;
-                }
-                if (direction == GI_DIRECTION_OUT || direction == GI_DIRECTION_INOUT) {
-                    state->n_aux_out_args += 1;
-                }
-
-                break;
-            }
-            case GI_TYPE_TAG_ERROR:
-                g_warn_if_fail(state->error_arg_pos < 0);
-                state->error_arg_pos = i;
-                break;
-            default:
-                break;
-        }
-    }
-
-    state->return_type_info = g_callable_info_get_return_type((GICallableInfo *)function_info);
-    state->return_type_tag = g_type_info_get_tag(state->return_type_info);
-
-    if (state->return_type_tag == GI_TYPE_TAG_ARRAY) {
-        gint length_arg_pos;
-        length_arg_pos = g_type_info_get_array_length(state->return_type_info);
-
-        if (state->is_method)
-            length_arg_pos--; // length_arg_pos refers to C args
-
-        if (length_arg_pos >= 0) {
-            g_assert(length_arg_pos < state->n_args);
-            state->args_is_auxiliary[length_arg_pos] = TRUE;
-            state->n_aux_out_args += 1;
-        }
-    }
-
-    state->n_return_values = state->n_out_args - state->n_aux_out_args;
-    if (state->return_type_tag != GI_TYPE_TAG_VOID) {
-        state->n_return_values += 1;
-    }
-
-    {
-        gsize n_py_args_expected;
-        Py_ssize_t py_args_pos;
-
-        n_py_args_expected = state->n_in_args
-            + (state->is_constructor ? 1 : 0)
-            - state->n_aux_in_args
-            - (state->error_arg_pos >= 0 ? 1 : 0);
-
-        if (state->n_py_args != n_py_args_expected) {
-            PyErr_Format(PyExc_TypeError,
-                "takes exactly %zd argument(s) (%zd given)",
-                n_py_args_expected, state->n_py_args);
-            return FALSE;
-        }
-
-        /* Check argument typestate-> */
-        py_args_pos = 0;
-        if (state->is_constructor || state->is_method) {
-            py_args_pos += 1;
-        }
-
-        for (i = 0; i < state->n_args; i++) {
-            GIDirection direction;
-            GITypeTag type_tag;
-            PyObject *py_arg;
-            gint retval;
-            gboolean allow_none;
-
-            direction = g_arg_info_get_direction(state->arg_infos[i]);
-            type_tag = g_type_info_get_tag(state->arg_type_infos[i]);
-
-            if (direction == GI_DIRECTION_OUT
-                    || state->args_is_auxiliary[i]
-                    || type_tag == GI_TYPE_TAG_ERROR) {
-                continue;
-            }
-
-            g_assert(py_args_pos < state->n_py_args);
-            py_arg = PyTuple_GET_ITEM(py_args, py_args_pos);
-
-            allow_none = g_arg_info_may_be_null(state->arg_infos[i]);
-
-            retval = _pygi_g_type_info_check_object(state->arg_type_infos[i],
-                                                    py_arg,
-                                                    allow_none);
-
-            if (retval < 0) {
-                return FALSE;
-            } else if (!retval) {
-                _PyGI_ERROR_PREFIX("argument %zd: ", py_args_pos);
-                return FALSE;
-            }
-
-            py_args_pos += 1;
-        }
-
-        g_assert(py_args_pos == state->n_py_args);
-    }
-
-    state->args = g_slice_alloc0(sizeof(gpointer) * state->n_args);
-    state->in_args = g_slice_alloc0(sizeof(GArgument) * state->n_in_args);
-    state->out_args = g_slice_alloc0(sizeof(GArgument) * state->n_out_args);
-    state->out_values = g_slice_alloc0(sizeof(GArgument) * state->n_out_args);
-    state->backup_args = g_slice_alloc0(sizeof(GArgument) * state->n_backup_args);
-
-    /* Bind args so we can use an unique index. */
-    {
-        gsize in_args_pos;
-        gsize out_args_pos;
-
-        in_args_pos = state->is_method ? 1 : 0;
-        out_args_pos = 0;
-
-        for (i = 0; i < state->n_args; i++) {
-            GIDirection direction;
-
-            direction = g_arg_info_get_direction(state->arg_infos[i]);
-
-            switch (direction) {
-                case GI_DIRECTION_IN:
-                    g_assert(in_args_pos < state->n_in_args);
-                    state->args[i] = &state->in_args[in_args_pos];
-                    in_args_pos += 1;
-                    break;
-                case GI_DIRECTION_INOUT:
-                    g_assert(in_args_pos < state->n_in_args);
-                    g_assert(out_args_pos < state->n_out_args);
-                    state->in_args[in_args_pos].v_pointer = &state->out_values[out_args_pos];
-                    in_args_pos += 1;
-                case GI_DIRECTION_OUT:
-                    g_assert(out_args_pos < state->n_out_args);
-                    state->out_args[out_args_pos].v_pointer = &state->out_values[out_args_pos];
-                    state->out_values[out_args_pos].v_pointer = NULL;
-                    state->args[i] = &state->out_values[out_args_pos];
-                    out_args_pos += 1;
-            }
-        }
-
-        g_assert(in_args_pos == state->n_in_args);
-        g_assert(out_args_pos == state->n_out_args);
-    }
-
-    /* Convert the input arguments. */
-    {
-        Py_ssize_t py_args_pos;
-        gsize backup_args_pos;
-
-        py_args_pos = 0;
-        backup_args_pos = 0;
-
-        if (state->is_constructor) {
-            /* Skip the first argument. */
-            py_args_pos += 1;
-        } else if (state->is_method) {
-            /* Get the instance. */
-            GIBaseInfo *container_info;
-            GIInfoType container_info_type;
-            PyObject *py_arg;
-
-            container_info = g_base_info_get_container(function_info);
-            container_info_type = g_base_info_get_type(container_info);
-
-            g_assert(py_args_pos < state->n_py_args);
-            py_arg = PyTuple_GET_ITEM(py_args, py_args_pos);
-
-            switch(container_info_type) {
-                case GI_INFO_TYPE_UNION:
-                case GI_INFO_TYPE_STRUCT:
-                {
-                    GType type;
-
-                    type = g_registered_type_info_get_g_type((GIRegisteredTypeInfo *)container_info);
-
-                    if (g_type_is_a(type, G_TYPE_BOXED)) {
-                        g_assert(state->n_in_args > 0);
-                        state->in_args[0].v_pointer = pyg_boxed_get(py_arg, void);
-                    } else if (g_type_is_a(type, G_TYPE_POINTER) || type == G_TYPE_NONE) {
-                        g_assert(state->n_in_args > 0);
-                        state->in_args[0].v_pointer = pyg_pointer_get(py_arg, void);
-                    } else {
-                        PyErr_Format(PyExc_TypeError, "unable to convert an instance of '%s'", g_type_name(type));
-                        return FALSE;
-                    }
-
-                    break;
-                }
-                case GI_INFO_TYPE_OBJECT:
-                case GI_INFO_TYPE_INTERFACE:
-                    g_assert(state->n_in_args > 0);
-                    state->in_args[0].v_pointer = pygobject_get(py_arg);
-                    break;
-                default:
-                    /* Other types don't have methods. */
-                    g_assert_not_reached();
-            }
-
-            py_args_pos += 1;
-        }
-
-        for (i = 0; i < state->n_args; i++) {
-            GIDirection direction;
-
-            if (i == state->callback_index) {
-                state->args[i]->v_pointer = state->closure->closure;
-                py_args_pos++;
-                continue;
-            } else if (i == state->user_data_index) {
-                state->args[i]->v_pointer = state->closure;
-                py_args_pos++;
-                continue;
-            } else if (i == state->destroy_notify_index) {
-                state->args[i]->v_pointer = _pygi_destroy_notify_create();
-                continue;
-            }
-            
-            if (state->args_is_auxiliary[i]) {
-                continue;
-            }
-
-            direction = g_arg_info_get_direction(state->arg_infos[i]);
-
-            if (direction == GI_DIRECTION_IN || direction == GI_DIRECTION_INOUT) {
-                PyObject *py_arg;
-                GITypeTag arg_type_tag;
-                GITransfer transfer;
-
-                arg_type_tag = g_type_info_get_tag(state->arg_type_infos[i]);
-
-                if (arg_type_tag == GI_TYPE_TAG_ERROR) {
-                    GError **error;
-
-                    error = g_slice_new(GError *);
-                    *error = NULL;
-
-                    state->args[i]->v_pointer = error;
-                    continue;
-                }
-
-                transfer = g_arg_info_get_ownership_transfer(state->arg_infos[i]);
-
-                g_assert(py_args_pos < state->n_py_args);
-                py_arg = PyTuple_GET_ITEM(py_args, py_args_pos);
-
-                *state->args[i] = _pygi_argument_from_object(py_arg, state->arg_type_infos[i], transfer);
-
-                if (PyErr_Occurred()) {
-                    /* TODO: release previous input arguments. */
-                    return FALSE;
-                }
-
-                if (direction == GI_DIRECTION_INOUT && transfer == GI_TRANSFER_NOTHING) {
-                    /* We need to keep a copy of the argument to be able to release it later. */
-                    g_assert(backup_args_pos < state->n_backup_args);
-                    state->backup_args[backup_args_pos] = *state->args[i];
-                    backup_args_pos += 1;
-                } else if (transfer == GI_TRANSFER_CONTAINER) {
-                    /* We need to keep a copy of the items to be able to release them later. */
-                    switch (arg_type_tag) {
-                        case GI_TYPE_TAG_ARRAY:
-                        {
-                            GArray *array;
-                            gsize item_size;
-                            GArray *new_array;
-
-                            array = state->args[i]->v_pointer;
-
-                            item_size = g_array_get_element_size(array);
-
-                            new_array = g_array_sized_new(FALSE, FALSE, item_size, array->len);
-                            g_array_append_vals(new_array, array->data, array->len);
-
-                            g_assert(backup_args_pos < state->n_backup_args);
-                            state->backup_args[backup_args_pos].v_pointer = new_array;
-
-                            break;
-                        }
-                        case GI_TYPE_TAG_GLIST:
-                            g_assert(backup_args_pos < state->n_backup_args);
-                            state->backup_args[backup_args_pos].v_pointer = g_list_copy(state->args[i]->v_pointer);
-                            break;
-                        case GI_TYPE_TAG_GSLIST:
-                            g_assert(backup_args_pos < state->n_backup_args);
-                            state->backup_args[backup_args_pos].v_pointer = g_slist_copy(state->args[i]->v_pointer);
-                            break;
-                        case GI_TYPE_TAG_GHASH:
-                        {
-                            GHashTable *hash_table;
-                            GList *keys;
-                            GList *values;
-
-                            hash_table = state->args[i]->v_pointer;
-
-                            keys = g_hash_table_get_keys(hash_table);
-                            values = g_hash_table_get_values(hash_table);
-
-                            g_assert(backup_args_pos < state->n_backup_args);
-                            state->backup_args[backup_args_pos].v_pointer = g_list_concat(keys, values);
-
-                            break;
-                        }
-                        default:
-                            g_warn_if_reached();
-                    }
-
-                    backup_args_pos += 1;
-                }
-
-                if (arg_type_tag == GI_TYPE_TAG_ARRAY) {
-                    GArray *array;
-                    gssize length_arg_pos;
-
-                    array = state->args[i]->v_pointer;
-
-                    length_arg_pos = g_type_info_get_array_length(state->arg_type_infos[i]);
-                    if (state->is_method)
-                        length_arg_pos--; // length_arg_pos refers to C args
-                    if (length_arg_pos >= 0) {
-                        /* Set the auxiliary argument holding the length. */
-                        state->args[length_arg_pos]->v_size = array->len;
-                    }
-
-                    /* Get rid of the GArray. */
-                    if (array != NULL) {
-                        state->args[i]->v_pointer = array->data;
-
-                        if (direction != GI_DIRECTION_INOUT || transfer != GI_TRANSFER_NOTHING) {
-                            /* The array hasn't been referenced anywhere, so free it to avoid losing memory. */
-                            g_array_free(array, FALSE);
-                        }
-                    }
-                }
-
-                py_args_pos += 1;
-            }
-        }
-
-        g_assert(py_args_pos == state->n_py_args);
-        g_assert(backup_args_pos == state->n_backup_args);
-    }
-
-    return TRUE;
-}
-
-static gboolean
-_invoke_function (struct invocation_state *state,
-                  GIFunctionInfo *function_info, PyObject *py_args)
-{
-    GError *error;
-    gint retval;
-
-    error = NULL;
-
-    retval = g_function_info_invoke((GIFunctionInfo *)function_info,
-            state->in_args, state->n_in_args, state->out_args, state->n_out_args, &state->return_arg, &error);
-    if (!retval) {
-        g_assert(error != NULL);
-        /* TODO: raise the right error, out of the error domain. */
-        PyErr_SetString(PyExc_RuntimeError, error->message);
-        g_error_free(error);
-
-        /* TODO: release input arguments. */
-
-        return FALSE;
-    }
-
-    if (state->error_arg_pos >= 0) {
-        GError **error;
-
-        error = state->args[state->error_arg_pos]->v_pointer;
-
-        if (*error != NULL) {
-            /* TODO: raise the right error, out of the error domain, if applicable. */
-            PyErr_SetString(PyExc_Exception, (*error)->message);
-            g_error_free(*error);
-
-            /* TODO: release input arguments. */
-
-            return FALSE;
-        }
-    }
-
-    return TRUE;
-}
-
-static gboolean
-_process_invocation_state (struct invocation_state *state,
-                           GIFunctionInfo *function_info, PyObject *py_args)
-{
-    gsize i;
-
-    /* Convert the return value. */
-    if (state->is_constructor) {
-        PyTypeObject *py_type;
-        GIBaseInfo *info;
-        GIInfoType info_type;
-        GITransfer transfer;
-
-        g_assert(state->n_py_args > 0);
-        py_type = (PyTypeObject *)PyTuple_GET_ITEM(py_args, 0);
-
-        info = g_type_info_get_interface(state->return_type_info);
-        g_assert(info != NULL);
-
-        info_type = g_base_info_get_type(info);
-
-        transfer = g_callable_info_get_caller_owns((GICallableInfo *)function_info);
-
-        switch (info_type) {
-            case GI_INFO_TYPE_UNION:
-                /* TODO */
-                PyErr_SetString(PyExc_NotImplementedError, "creating unions is not supported yet");
-                g_base_info_unref(info);
-                return FALSE;
-            case GI_INFO_TYPE_STRUCT:
-            {
-                GType type;
-
-                type = g_registered_type_info_get_g_type((GIRegisteredTypeInfo *)info);
-
-                if (g_type_is_a(type, G_TYPE_BOXED)) {
-                    if (state->return_arg.v_pointer == NULL) {
-                        PyErr_SetString(PyExc_TypeError, "constructor returned NULL");
-                        break;
-                    }
-                    g_warn_if_fail(transfer == GI_TRANSFER_EVERYTHING);
-                    state->return_value = _pygi_boxed_new(py_type, state->return_arg.v_pointer, transfer == GI_TRANSFER_EVERYTHING);
-                } else if (g_type_is_a(type, G_TYPE_POINTER) || type == G_TYPE_NONE) {
-                    if (state->return_arg.v_pointer == NULL) {
-                        PyErr_SetString(PyExc_TypeError, "constructor returned NULL");
-                        break;
-                    }
-                    g_warn_if_fail(transfer == GI_TRANSFER_NOTHING);
-                    state->return_value = _pygi_struct_new(py_type, state->return_arg.v_pointer, transfer == GI_TRANSFER_EVERYTHING);
-                } else {
-                    PyErr_Format(PyExc_TypeError, "cannot create '%s' instances", py_type->tp_name);
-                    g_base_info_unref(info);
-                    return FALSE;
-                }
-
-                break;
-            }
-            case GI_INFO_TYPE_OBJECT:
-                if (state->return_arg.v_pointer == NULL) {
-                    PyErr_SetString(PyExc_TypeError, "constructor returned NULL");
-                    break;
-                }
-                state->return_value = pygobject_new(state->return_arg.v_pointer);
-                if (transfer == GI_TRANSFER_EVERYTHING) {
-                    /* The new wrapper increased the reference count, so decrease it. */
-                    g_object_unref (state->return_arg.v_pointer);
-                }
-                break;
-            default:
-                /* Other types don't have neither methods nor constructors. */
-                g_assert_not_reached();
-        }
-
-        g_base_info_unref(info);
-
-        if (state->return_value == NULL) {
-            /* TODO: release arguments. */
-            return FALSE;
-        }
-    } else {
-        GITransfer transfer;
-
-        if (state->return_type_tag == GI_TYPE_TAG_ARRAY) {
-            /* Create a #GArray. */
-            state->return_arg.v_pointer = _pygi_argument_to_array(&state->return_arg, state->args, state->return_type_info, state->is_method);
-        }
-
-        transfer = g_callable_info_get_caller_owns((GICallableInfo *)function_info);
-
-        state->return_value = _pygi_argument_to_object(&state->return_arg, state->return_type_info, transfer);
-        if (state->return_value == NULL) {
-            /* TODO: release argument. */
-            return FALSE;
-        }
-
-        _pygi_argument_release(&state->return_arg, state->return_type_info, transfer, GI_DIRECTION_OUT);
-
-        if (state->return_type_tag == GI_TYPE_TAG_ARRAY
-                && transfer == GI_TRANSFER_NOTHING) {
-            /* We created a #GArray, so free it. */
-            state->return_arg.v_pointer = g_array_free(state->return_arg.v_pointer, FALSE);
-        }
-    }
-
-    /* Convert output arguments and release arguments. */
-    {
-        gsize backup_args_pos;
-        gsize return_values_pos;
-
-        backup_args_pos = 0;
-        return_values_pos = 0;
-
-        if (state->n_return_values > 1) {
-            /* Return a tuple. */
-            PyObject *return_values;
-
-            return_values = PyTuple_New(state->n_return_values);
-            if (return_values == NULL) {
-                /* TODO: release arguments. */
-                return FALSE;
-            }
-
-            if (state->return_type_tag == GI_TYPE_TAG_VOID) {
-                /* The current return value is None. */
-                Py_DECREF(state->return_value);
-            } else {
-                /* Put the return value first. */
-                g_assert(state->return_value != NULL);
-                PyTuple_SET_ITEM(return_values, return_values_pos, state->return_value);
-                return_values_pos += 1;
-            }
-
-            state->return_value = return_values;
-        }
-
-        for (i = 0; i < state->n_args; i++) {
-            GIDirection direction;
-            GITypeTag type_tag;
-            GITransfer transfer;
-
-            if (state->args_is_auxiliary[i]) {
-                /* Auxiliary arguments are handled at the same time as their relatives. */
-                continue;
-            }
-
-            direction = g_arg_info_get_direction(state->arg_infos[i]);
-            transfer = g_arg_info_get_ownership_transfer(state->arg_infos[i]);
-
-            type_tag = g_type_info_get_tag(state->arg_type_infos[i]);
-
-            if (type_tag == GI_TYPE_TAG_ARRAY
-                    && (direction != GI_DIRECTION_IN || transfer == GI_TRANSFER_NOTHING)) {
-                /* Create a #GArray. */
-                state->args[i]->v_pointer = _pygi_argument_to_array(state->args[i], state->args, state->arg_type_infos[i], state->is_method);
-            }
-
-            if (direction == GI_DIRECTION_INOUT || direction == GI_DIRECTION_OUT) {
-                /* Convert the argument. */
-                PyObject *obj;
-
-                obj = _pygi_argument_to_object(state->args[i], state->arg_type_infos[i], transfer);
-                if (obj == NULL) {
-                    /* TODO: release arguments. */
-                    return FALSE;
-                }
-
-                g_assert(return_values_pos < state->n_return_values);
-
-                if (state->n_return_values > 1) {
-                    PyTuple_SET_ITEM(state->return_value, return_values_pos, obj);
-                } else {
-                    /* The current return value is None. */
-                    Py_DECREF(state->return_value);
-                    state->return_value = obj;
-                }
-
-                return_values_pos += 1;
-            }
-
-            /* Release the argument. */
-
-            if ((direction == GI_DIRECTION_IN || direction == GI_DIRECTION_INOUT)
-                    && transfer == GI_TRANSFER_CONTAINER) {
-                /* Release the items we kept in another container. */
-                switch (type_tag) {
-                    case GI_TYPE_TAG_ARRAY:
-                    case GI_TYPE_TAG_GLIST:
-                    case GI_TYPE_TAG_GSLIST:
-                        g_assert(backup_args_pos < state->n_backup_args);
-                        _pygi_argument_release(&state->backup_args[backup_args_pos], state->arg_type_infos[i],
-                            transfer, GI_DIRECTION_IN);
-                        break;
-                    case GI_TYPE_TAG_GHASH:
-                    {
-                        GITypeInfo *key_type_info;
-                        GITypeInfo *value_type_info;
-                        GList *item;
-                        gsize length;
-                        gsize j;
-
-                        key_type_info = g_type_info_get_param_type(state->arg_type_infos[i], 0);
-                        value_type_info = g_type_info_get_param_type(state->arg_type_infos[i], 1);
-
-                        g_assert(backup_args_pos < state->n_backup_args);
-                        item = state->backup_args[backup_args_pos].v_pointer;
-
-                        length = g_list_length(item) / 2;
-
-                        for (j = 0; j < length; j++, item = g_list_next(item)) {
-                            _pygi_argument_release((GArgument *)&item->data, key_type_info,
-                                GI_TRANSFER_NOTHING, GI_DIRECTION_IN);
-                        }
-
-                        for (j = 0; j < length; j++, item = g_list_next(item)) {
-                            _pygi_argument_release((GArgument *)&item->data, value_type_info,
-                                GI_TRANSFER_NOTHING, GI_DIRECTION_IN);
-                        }
-
-                        g_list_free(state->backup_args[backup_args_pos].v_pointer);
-
-                        break;
-                    }
-                    default:
-                        g_warn_if_reached();
-                }
-
-                if (direction == GI_DIRECTION_INOUT) {
-                    /* Release the output argument. */
-                    _pygi_argument_release(state->args[i], state->arg_type_infos[i], GI_TRANSFER_CONTAINER,
-                        GI_DIRECTION_OUT);
-                }
-
-                backup_args_pos += 1;
-            } else if (direction == GI_DIRECTION_INOUT) {
-                if (transfer == GI_TRANSFER_NOTHING) {
-                    g_assert(backup_args_pos < state->n_backup_args);
-                    _pygi_argument_release(&state->backup_args[backup_args_pos], state->arg_type_infos[i],
-                        GI_TRANSFER_NOTHING, GI_DIRECTION_IN);
-                    backup_args_pos += 1;
-                }
-
-                _pygi_argument_release(state->args[i], state->arg_type_infos[i], transfer,
-                    GI_DIRECTION_OUT);
-            } else {
-                _pygi_argument_release(state->args[i], state->arg_type_infos[i], transfer, direction);
-            }
-
-            if (type_tag == GI_TYPE_TAG_ARRAY
-                    && (direction != GI_DIRECTION_IN && transfer == GI_TRANSFER_NOTHING)) {
-                /* We created a #GArray and it has not been released above, so free it. */
-                state->args[i]->v_pointer = g_array_free(state->args[i]->v_pointer, FALSE);
-            }
-        }
-
-        g_assert(state->n_return_values <= 1 || return_values_pos == state->n_return_values);
-        g_assert(backup_args_pos == state->n_backup_args);
-    }
-
-    return TRUE;
-}
-
-static void
-_free_invocation_state (struct invocation_state *state)
-{
-    gsize i;
-
-    if (state->return_type_info != NULL) {
-        g_base_info_unref((GIBaseInfo *)state->return_type_info);
-    }
-
-    if (state->closure != NULL) {
-        if (state->closure->scope == GI_SCOPE_TYPE_CALL) 
-            _pygi_invoke_closure_free(state->closure);
-    }
-
-    for (i = 0; i < state->n_args; i++) {
-        g_base_info_unref((GIBaseInfo *)state->arg_type_infos[i]);
-        g_base_info_unref((GIBaseInfo *)state->arg_infos[i]);
-    }
-
-    if (state->arg_infos != NULL) {
-        g_slice_free1(sizeof(gpointer) * state->n_args, state->arg_infos);
-    }
-
-    if (state->arg_type_infos != NULL) {
-        g_slice_free1(sizeof(gpointer) * state->n_args, state->arg_type_infos);
-    }
-
-    if (state->args != NULL) {
-        g_slice_free1(sizeof(gpointer) * state->n_args, state->args);
-    }
-
-    if (state->args_is_auxiliary != NULL) {
-        g_slice_free1(sizeof(gboolean) * state->n_args, state->args_is_auxiliary);
-    }
-
-    if (state->in_args != NULL) {
-        g_slice_free1(sizeof(GArgument) * state->n_in_args, state->in_args);
-    }
-
-    if (state->out_args != NULL) {
-        g_slice_free1(sizeof(GArgument) * state->n_out_args, state->out_args);
-    }
-
-    if (state->out_values != NULL) {
-        g_slice_free1(sizeof(GArgument) * state->n_out_args, state->out_values);
-    }
-
-    if (state->backup_args != NULL) {
-        g_slice_free1(sizeof(GArgument) * state->n_backup_args, state->backup_args);
-    }
-
-    if (PyErr_Occurred()) {
-        Py_CLEAR(state->return_value);
-    }
-}
-
-
-static PyObject *
-_wrap_g_function_info_invoke (PyGIBaseInfo *self, PyObject *py_args)
-{
-    struct invocation_state state;
-
-    _initialize_invocation_state (&state, self->info, py_args);
-
-    if (!_prepare_invocation_state (&state, self->info, py_args)) {
-        _free_invocation_state (&state);
-        return NULL;
-    }
-
-    if (!_invoke_function (&state, self->info, py_args)) {
-        _free_invocation_state (&state);
-        return NULL;
-    }
-
-    if (!_process_invocation_state (&state, self->info, py_args)) {
-        _free_invocation_state (&state);
-        return NULL;
-    }
-
-    return state.return_value;
-}
-
 static PyMethodDef _PyGIFunctionInfo_methods[] = {
     { "is_constructor", (PyCFunction)_wrap_g_function_info_is_constructor, METH_NOARGS },
     { "is_method", (PyCFunction)_wrap_g_function_info_is_method, METH_NOARGS },
diff --git a/gi/pygi-invoke.c b/gi/pygi-invoke.c
new file mode 100644
index 0000000..431e0fe
--- /dev/null
+++ b/gi/pygi-invoke.c
@@ -0,0 +1,909 @@
+/* -*- Mode: C; c-basic-offset: 4 -*-
+ * vim: tabstop=4 shiftwidth=4 expandtab
+ *
+ * Copyright (C) 2005-2009 Johan Dahlin <johan gnome org>
+ *
+ *   pygi-invoke.c: main invocation function
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2.1 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301
+ * USA
+ */
+
+#include "pygi-invoke.h"
+
+struct invocation_state
+{
+    gboolean is_method;
+    gboolean is_constructor;
+
+    gsize n_args;
+    gsize n_in_args;
+    gsize n_out_args;
+    gsize n_backup_args;
+    Py_ssize_t n_py_args;
+    gsize n_aux_in_args;
+    gsize n_aux_out_args;
+    gsize n_return_values;
+
+    guint8 callback_index;
+    guint8 user_data_index;
+    guint8 destroy_notify_index;
+    PyGICClosure *closure;
+    
+    glong error_arg_pos;
+
+    GIArgInfo **arg_infos;
+    GITypeInfo **arg_type_infos;
+    GITypeInfo *return_type_info;
+    GITypeTag return_type_tag;
+
+    GArgument **args;
+    gboolean *args_is_auxiliary;
+
+    GArgument *in_args;
+    GArgument *out_args;
+    GArgument *out_values;
+    GArgument *backup_args;
+    GArgument return_arg;
+
+    PyObject *return_value;
+};
+
+static void
+_initialize_invocation_state (struct invocation_state *state,
+                              GIFunctionInfo *info,
+                              PyObject *py_args)
+{
+    GIFunctionInfoFlags flags;
+
+    flags = g_function_info_get_flags(info);
+    state->is_method = (flags & GI_FUNCTION_IS_METHOD) != 0;
+    state->is_constructor = (flags & GI_FUNCTION_IS_CONSTRUCTOR) != 0;
+
+    /* Count arguments. */
+    state->n_args = g_callable_info_get_n_args((GICallableInfo *)info);
+    state->n_in_args = 0;
+    state->n_out_args = 0;
+    state->n_backup_args = 0;
+    state->n_aux_in_args = 0;
+    state->n_aux_out_args = 0;
+    
+    /* Check the argument count. */
+    state->n_py_args = PyTuple_Size(py_args);
+    g_assert(state->n_py_args >= 0);
+
+    state->error_arg_pos = -1;
+
+    state->arg_infos = g_slice_alloc0(sizeof(gpointer) * state->n_args);
+    state->arg_type_infos = g_slice_alloc0(sizeof(gpointer) * state->n_args);
+    state->args_is_auxiliary = g_slice_alloc0(sizeof(gboolean) * state->n_args);
+
+    state->return_value = NULL;
+    state->closure = NULL;
+    state->return_type_info = NULL;
+    state->args = NULL;
+    state->in_args = NULL;
+    state->out_args = NULL;
+    state->out_values = NULL;
+    state->backup_args = NULL;
+}
+
+static gboolean
+_prepare_invocation_state (struct invocation_state *state,
+                           GIFunctionInfo *function_info, PyObject *py_args)
+{
+    gsize i;
+
+    if (!_pygi_scan_for_callbacks (function_info, state->is_method,
+                                   &state->callback_index, &state->user_data_index,
+                                   &state->destroy_notify_index))
+        return FALSE;
+        
+    if (state->callback_index != G_MAXUINT8) {
+        if (!_pygi_create_callback (function_info, state->is_method, 
+                                    state->n_args, state->n_py_args, 
+                                    py_args, state->callback_index,
+                                    state->user_data_index,
+                                    state->destroy_notify_index, &state->closure))
+            return FALSE;
+
+        state->args_is_auxiliary[state->callback_index] = FALSE;
+        if (state->destroy_notify_index != G_MAXUINT8) {
+            state->args_is_auxiliary[state->destroy_notify_index] = TRUE;
+            state->n_aux_in_args += 1;
+        }
+    }
+
+    if (state->is_method) {
+        /* The first argument is the instance. */
+        state->n_in_args += 1;
+    }
+
+    /* We do a first (well, second) pass here over the function to scan for special cases.
+     * This is currently array+length combinations and GError.
+     */
+    for (i = 0; i < state->n_args; i++) {
+        GIDirection direction;
+        GITransfer transfer;
+        GITypeTag arg_type_tag;
+        
+        state->arg_infos[i] = g_callable_info_get_arg((GICallableInfo *)function_info,
+                                                 i);
+
+        state->arg_type_infos[i] = g_arg_info_get_type(state->arg_infos[i]);
+        
+        direction = g_arg_info_get_direction(state->arg_infos[i]);
+        transfer = g_arg_info_get_ownership_transfer(state->arg_infos[i]);
+        arg_type_tag = g_type_info_get_tag(state->arg_type_infos[i]);
+
+        if (direction == GI_DIRECTION_IN || direction == GI_DIRECTION_INOUT) {
+            state->n_in_args += 1;
+            if (transfer == GI_TRANSFER_CONTAINER) {
+                state->n_backup_args += 1;
+            }
+        }
+        if (direction == GI_DIRECTION_OUT || direction == GI_DIRECTION_INOUT) {
+            state->n_out_args += 1;
+        }
+
+        if (direction == GI_DIRECTION_INOUT && transfer == GI_TRANSFER_NOTHING) {
+            state->n_backup_args += 1;
+        }
+
+        switch (arg_type_tag) {
+            case GI_TYPE_TAG_ARRAY:
+            {
+                gint length_arg_pos;
+
+                length_arg_pos = g_type_info_get_array_length(state->arg_type_infos[i]);
+
+                if (state->is_method)
+                    length_arg_pos--; // length_arg_pos refers to C args
+
+                if (length_arg_pos < 0) {
+                    break;
+                }
+
+                g_assert(length_arg_pos < state->n_args);
+                state->args_is_auxiliary[length_arg_pos] = TRUE;
+
+                if (direction == GI_DIRECTION_IN || direction == GI_DIRECTION_INOUT) {
+                    state->n_aux_in_args += 1;
+                }
+                if (direction == GI_DIRECTION_OUT || direction == GI_DIRECTION_INOUT) {
+                    state->n_aux_out_args += 1;
+                }
+
+                break;
+            }
+            case GI_TYPE_TAG_ERROR:
+                g_warn_if_fail(state->error_arg_pos < 0);
+                state->error_arg_pos = i;
+                break;
+            default:
+                break;
+        }
+    }
+
+    state->return_type_info = g_callable_info_get_return_type((GICallableInfo *)function_info);
+    state->return_type_tag = g_type_info_get_tag(state->return_type_info);
+
+    if (state->return_type_tag == GI_TYPE_TAG_ARRAY) {
+        gint length_arg_pos;
+        length_arg_pos = g_type_info_get_array_length(state->return_type_info);
+
+        if (state->is_method)
+            length_arg_pos--; // length_arg_pos refers to C args
+
+        if (length_arg_pos >= 0) {
+            g_assert(length_arg_pos < state->n_args);
+            state->args_is_auxiliary[length_arg_pos] = TRUE;
+            state->n_aux_out_args += 1;
+        }
+    }
+
+    state->n_return_values = state->n_out_args - state->n_aux_out_args;
+    if (state->return_type_tag != GI_TYPE_TAG_VOID) {
+        state->n_return_values += 1;
+    }
+
+    {
+        gsize n_py_args_expected;
+        Py_ssize_t py_args_pos;
+
+        n_py_args_expected = state->n_in_args
+            + (state->is_constructor ? 1 : 0)
+            - state->n_aux_in_args
+            - (state->error_arg_pos >= 0 ? 1 : 0);
+
+        if (state->n_py_args != n_py_args_expected) {
+            PyErr_Format(PyExc_TypeError,
+                "takes exactly %zd argument(s) (%zd given)",
+                n_py_args_expected, state->n_py_args);
+            return FALSE;
+        }
+
+        /* Check argument typestate-> */
+        py_args_pos = 0;
+        if (state->is_constructor || state->is_method) {
+            py_args_pos += 1;
+        }
+
+        for (i = 0; i < state->n_args; i++) {
+            GIDirection direction;
+            GITypeTag type_tag;
+            PyObject *py_arg;
+            gint retval;
+            gboolean allow_none;
+
+            direction = g_arg_info_get_direction(state->arg_infos[i]);
+            type_tag = g_type_info_get_tag(state->arg_type_infos[i]);
+
+            if (direction == GI_DIRECTION_OUT
+                    || state->args_is_auxiliary[i]
+                    || type_tag == GI_TYPE_TAG_ERROR) {
+                continue;
+            }
+
+            g_assert(py_args_pos < state->n_py_args);
+            py_arg = PyTuple_GET_ITEM(py_args, py_args_pos);
+
+            allow_none = g_arg_info_may_be_null(state->arg_infos[i]);
+
+            retval = _pygi_g_type_info_check_object(state->arg_type_infos[i],
+                                                    py_arg,
+                                                    allow_none);
+
+            if (retval < 0) {
+                return FALSE;
+            } else if (!retval) {
+                _PyGI_ERROR_PREFIX("argument %zd: ", py_args_pos);
+                return FALSE;
+            }
+
+            py_args_pos += 1;
+        }
+
+        g_assert(py_args_pos == state->n_py_args);
+    }
+
+    state->args = g_slice_alloc0(sizeof(gpointer) * state->n_args);
+    state->in_args = g_slice_alloc0(sizeof(GArgument) * state->n_in_args);
+    state->out_args = g_slice_alloc0(sizeof(GArgument) * state->n_out_args);
+    state->out_values = g_slice_alloc0(sizeof(GArgument) * state->n_out_args);
+    state->backup_args = g_slice_alloc0(sizeof(GArgument) * state->n_backup_args);
+
+    /* Bind args so we can use an unique index. */
+    {
+        gsize in_args_pos;
+        gsize out_args_pos;
+
+        in_args_pos = state->is_method ? 1 : 0;
+        out_args_pos = 0;
+
+        for (i = 0; i < state->n_args; i++) {
+            GIDirection direction;
+
+            direction = g_arg_info_get_direction(state->arg_infos[i]);
+
+            switch (direction) {
+                case GI_DIRECTION_IN:
+                    g_assert(in_args_pos < state->n_in_args);
+                    state->args[i] = &state->in_args[in_args_pos];
+                    in_args_pos += 1;
+                    break;
+                case GI_DIRECTION_INOUT:
+                    g_assert(in_args_pos < state->n_in_args);
+                    g_assert(out_args_pos < state->n_out_args);
+                    state->in_args[in_args_pos].v_pointer = &state->out_values[out_args_pos];
+                    in_args_pos += 1;
+                case GI_DIRECTION_OUT:
+                    g_assert(out_args_pos < state->n_out_args);
+                    state->out_args[out_args_pos].v_pointer = &state->out_values[out_args_pos];
+                    state->out_values[out_args_pos].v_pointer = NULL;
+                    state->args[i] = &state->out_values[out_args_pos];
+                    out_args_pos += 1;
+            }
+        }
+
+        g_assert(in_args_pos == state->n_in_args);
+        g_assert(out_args_pos == state->n_out_args);
+    }
+
+    /* Convert the input arguments. */
+    {
+        Py_ssize_t py_args_pos;
+        gsize backup_args_pos;
+
+        py_args_pos = 0;
+        backup_args_pos = 0;
+
+        if (state->is_constructor) {
+            /* Skip the first argument. */
+            py_args_pos += 1;
+        } else if (state->is_method) {
+            /* Get the instance. */
+            GIBaseInfo *container_info;
+            GIInfoType container_info_type;
+            PyObject *py_arg;
+
+            container_info = g_base_info_get_container(function_info);
+            container_info_type = g_base_info_get_type(container_info);
+
+            g_assert(py_args_pos < state->n_py_args);
+            py_arg = PyTuple_GET_ITEM(py_args, py_args_pos);
+
+            switch(container_info_type) {
+                case GI_INFO_TYPE_UNION:
+                case GI_INFO_TYPE_STRUCT:
+                {
+                    GType type;
+
+                    type = g_registered_type_info_get_g_type((GIRegisteredTypeInfo *)container_info);
+
+                    if (g_type_is_a(type, G_TYPE_BOXED)) {
+                        g_assert(state->n_in_args > 0);
+                        state->in_args[0].v_pointer = pyg_boxed_get(py_arg, void);
+                    } else if (g_type_is_a(type, G_TYPE_POINTER) || type == G_TYPE_NONE) {
+                        g_assert(state->n_in_args > 0);
+                        state->in_args[0].v_pointer = pyg_pointer_get(py_arg, void);
+                    } else {
+                        PyErr_Format(PyExc_TypeError, "unable to convert an instance of '%s'", g_type_name(type));
+                        return FALSE;
+                    }
+
+                    break;
+                }
+                case GI_INFO_TYPE_OBJECT:
+                case GI_INFO_TYPE_INTERFACE:
+                    g_assert(state->n_in_args > 0);
+                    state->in_args[0].v_pointer = pygobject_get(py_arg);
+                    break;
+                default:
+                    /* Other types don't have methods. */
+                    g_assert_not_reached();
+            }
+
+            py_args_pos += 1;
+        }
+
+        for (i = 0; i < state->n_args; i++) {
+            GIDirection direction;
+
+            if (i == state->callback_index) {
+                state->args[i]->v_pointer = state->closure->closure;
+                py_args_pos++;
+                continue;
+            } else if (i == state->user_data_index) {
+                state->args[i]->v_pointer = state->closure;
+                py_args_pos++;
+                continue;
+            } else if (i == state->destroy_notify_index) {
+                state->args[i]->v_pointer = _pygi_destroy_notify_create();
+                continue;
+            }
+            
+            if (state->args_is_auxiliary[i]) {
+                continue;
+            }
+
+            direction = g_arg_info_get_direction(state->arg_infos[i]);
+
+            if (direction == GI_DIRECTION_IN || direction == GI_DIRECTION_INOUT) {
+                PyObject *py_arg;
+                GITypeTag arg_type_tag;
+                GITransfer transfer;
+
+                arg_type_tag = g_type_info_get_tag(state->arg_type_infos[i]);
+
+                if (arg_type_tag == GI_TYPE_TAG_ERROR) {
+                    GError **error;
+
+                    error = g_slice_new(GError *);
+                    *error = NULL;
+
+                    state->args[i]->v_pointer = error;
+                    continue;
+                }
+
+                transfer = g_arg_info_get_ownership_transfer(state->arg_infos[i]);
+
+                g_assert(py_args_pos < state->n_py_args);
+                py_arg = PyTuple_GET_ITEM(py_args, py_args_pos);
+
+                *state->args[i] = _pygi_argument_from_object(py_arg, state->arg_type_infos[i], transfer);
+
+                if (PyErr_Occurred()) {
+                    /* TODO: release previous input arguments. */
+                    return FALSE;
+                }
+
+                if (direction == GI_DIRECTION_INOUT && transfer == GI_TRANSFER_NOTHING) {
+                    /* We need to keep a copy of the argument to be able to release it later. */
+                    g_assert(backup_args_pos < state->n_backup_args);
+                    state->backup_args[backup_args_pos] = *state->args[i];
+                    backup_args_pos += 1;
+                } else if (transfer == GI_TRANSFER_CONTAINER) {
+                    /* We need to keep a copy of the items to be able to release them later. */
+                    switch (arg_type_tag) {
+                        case GI_TYPE_TAG_ARRAY:
+                        {
+                            GArray *array;
+                            gsize item_size;
+                            GArray *new_array;
+
+                            array = state->args[i]->v_pointer;
+
+                            item_size = g_array_get_element_size(array);
+
+                            new_array = g_array_sized_new(FALSE, FALSE, item_size, array->len);
+                            g_array_append_vals(new_array, array->data, array->len);
+
+                            g_assert(backup_args_pos < state->n_backup_args);
+                            state->backup_args[backup_args_pos].v_pointer = new_array;
+
+                            break;
+                        }
+                        case GI_TYPE_TAG_GLIST:
+                            g_assert(backup_args_pos < state->n_backup_args);
+                            state->backup_args[backup_args_pos].v_pointer = g_list_copy(state->args[i]->v_pointer);
+                            break;
+                        case GI_TYPE_TAG_GSLIST:
+                            g_assert(backup_args_pos < state->n_backup_args);
+                            state->backup_args[backup_args_pos].v_pointer = g_slist_copy(state->args[i]->v_pointer);
+                            break;
+                        case GI_TYPE_TAG_GHASH:
+                        {
+                            GHashTable *hash_table;
+                            GList *keys;
+                            GList *values;
+
+                            hash_table = state->args[i]->v_pointer;
+
+                            keys = g_hash_table_get_keys(hash_table);
+                            values = g_hash_table_get_values(hash_table);
+
+                            g_assert(backup_args_pos < state->n_backup_args);
+                            state->backup_args[backup_args_pos].v_pointer = g_list_concat(keys, values);
+
+                            break;
+                        }
+                        default:
+                            g_warn_if_reached();
+                    }
+
+                    backup_args_pos += 1;
+                }
+
+                if (arg_type_tag == GI_TYPE_TAG_ARRAY) {
+                    GArray *array;
+                    gssize length_arg_pos;
+
+                    array = state->args[i]->v_pointer;
+
+                    length_arg_pos = g_type_info_get_array_length(state->arg_type_infos[i]);
+                    if (state->is_method)
+                        length_arg_pos--; // length_arg_pos refers to C args
+                    if (length_arg_pos >= 0) {
+                        /* Set the auxiliary argument holding the length. */
+                        state->args[length_arg_pos]->v_size = array->len;
+                    }
+
+                    /* Get rid of the GArray. */
+                    if (array != NULL) {
+                        state->args[i]->v_pointer = array->data;
+
+                        if (direction != GI_DIRECTION_INOUT || transfer != GI_TRANSFER_NOTHING) {
+                            /* The array hasn't been referenced anywhere, so free it to avoid losing memory. */
+                            g_array_free(array, FALSE);
+                        }
+                    }
+                }
+
+                py_args_pos += 1;
+            }
+        }
+
+        g_assert(py_args_pos == state->n_py_args);
+        g_assert(backup_args_pos == state->n_backup_args);
+    }
+
+    return TRUE;
+}
+
+static gboolean
+_invoke_function (struct invocation_state *state,
+                  GIFunctionInfo *function_info, PyObject *py_args)
+{
+    GError *error;
+    gint retval;
+
+    error = NULL;
+
+    retval = g_function_info_invoke((GIFunctionInfo *)function_info,
+            state->in_args, state->n_in_args, state->out_args, state->n_out_args, &state->return_arg, &error);
+    if (!retval) {
+        g_assert(error != NULL);
+        /* TODO: raise the right error, out of the error domain. */
+        PyErr_SetString(PyExc_RuntimeError, error->message);
+        g_error_free(error);
+
+        /* TODO: release input arguments. */
+
+        return FALSE;
+    }
+
+    if (state->error_arg_pos >= 0) {
+        GError **error;
+
+        error = state->args[state->error_arg_pos]->v_pointer;
+
+        if (*error != NULL) {
+            /* TODO: raise the right error, out of the error domain, if applicable. */
+            PyErr_SetString(PyExc_Exception, (*error)->message);
+            g_error_free(*error);
+
+            /* TODO: release input arguments. */
+
+            return FALSE;
+        }
+    }
+
+    return TRUE;
+}
+
+static gboolean
+_process_invocation_state (struct invocation_state *state,
+                           GIFunctionInfo *function_info, PyObject *py_args)
+{
+    gsize i;
+
+    /* Convert the return value. */
+    if (state->is_constructor) {
+        PyTypeObject *py_type;
+        GIBaseInfo *info;
+        GIInfoType info_type;
+        GITransfer transfer;
+
+        g_assert(state->n_py_args > 0);
+        py_type = (PyTypeObject *)PyTuple_GET_ITEM(py_args, 0);
+
+        info = g_type_info_get_interface(state->return_type_info);
+        g_assert(info != NULL);
+
+        info_type = g_base_info_get_type(info);
+
+        transfer = g_callable_info_get_caller_owns((GICallableInfo *)function_info);
+
+        switch (info_type) {
+            case GI_INFO_TYPE_UNION:
+                /* TODO */
+                PyErr_SetString(PyExc_NotImplementedError, "creating unions is not supported yet");
+                g_base_info_unref(info);
+                return FALSE;
+            case GI_INFO_TYPE_STRUCT:
+            {
+                GType type;
+
+                type = g_registered_type_info_get_g_type((GIRegisteredTypeInfo *)info);
+
+                if (g_type_is_a(type, G_TYPE_BOXED)) {
+                    if (state->return_arg.v_pointer == NULL) {
+                        PyErr_SetString(PyExc_TypeError, "constructor returned NULL");
+                        break;
+                    }
+                    g_warn_if_fail(transfer == GI_TRANSFER_EVERYTHING);
+                    state->return_value = _pygi_boxed_new(py_type, state->return_arg.v_pointer, transfer == GI_TRANSFER_EVERYTHING);
+                } else if (g_type_is_a(type, G_TYPE_POINTER) || type == G_TYPE_NONE) {
+                    if (state->return_arg.v_pointer == NULL) {
+                        PyErr_SetString(PyExc_TypeError, "constructor returned NULL");
+                        break;
+                    }
+                    g_warn_if_fail(transfer == GI_TRANSFER_NOTHING);
+                    state->return_value = _pygi_struct_new(py_type, state->return_arg.v_pointer, transfer == GI_TRANSFER_EVERYTHING);
+                } else {
+                    PyErr_Format(PyExc_TypeError, "cannot create '%s' instances", py_type->tp_name);
+                    g_base_info_unref(info);
+                    return FALSE;
+                }
+
+                break;
+            }
+            case GI_INFO_TYPE_OBJECT:
+                if (state->return_arg.v_pointer == NULL) {
+                    PyErr_SetString(PyExc_TypeError, "constructor returned NULL");
+                    break;
+                }
+                state->return_value = pygobject_new(state->return_arg.v_pointer);
+                if (transfer == GI_TRANSFER_EVERYTHING) {
+                    /* The new wrapper increased the reference count, so decrease it. */
+                    g_object_unref (state->return_arg.v_pointer);
+                }
+                break;
+            default:
+                /* Other types don't have neither methods nor constructors. */
+                g_assert_not_reached();
+        }
+
+        g_base_info_unref(info);
+
+        if (state->return_value == NULL) {
+            /* TODO: release arguments. */
+            return FALSE;
+        }
+    } else {
+        GITransfer transfer;
+
+        if (state->return_type_tag == GI_TYPE_TAG_ARRAY) {
+            /* Create a #GArray. */
+            state->return_arg.v_pointer = _pygi_argument_to_array(&state->return_arg, state->args, state->return_type_info, state->is_method);
+        }
+
+        transfer = g_callable_info_get_caller_owns((GICallableInfo *)function_info);
+
+        state->return_value = _pygi_argument_to_object(&state->return_arg, state->return_type_info, transfer);
+        if (state->return_value == NULL) {
+            /* TODO: release argument. */
+            return FALSE;
+        }
+
+        _pygi_argument_release(&state->return_arg, state->return_type_info, transfer, GI_DIRECTION_OUT);
+
+        if (state->return_type_tag == GI_TYPE_TAG_ARRAY
+                && transfer == GI_TRANSFER_NOTHING) {
+            /* We created a #GArray, so free it. */
+            state->return_arg.v_pointer = g_array_free(state->return_arg.v_pointer, FALSE);
+        }
+    }
+
+    /* Convert output arguments and release arguments. */
+    {
+        gsize backup_args_pos;
+        gsize return_values_pos;
+
+        backup_args_pos = 0;
+        return_values_pos = 0;
+
+        if (state->n_return_values > 1) {
+            /* Return a tuple. */
+            PyObject *return_values;
+
+            return_values = PyTuple_New(state->n_return_values);
+            if (return_values == NULL) {
+                /* TODO: release arguments. */
+                return FALSE;
+            }
+
+            if (state->return_type_tag == GI_TYPE_TAG_VOID) {
+                /* The current return value is None. */
+                Py_DECREF(state->return_value);
+            } else {
+                /* Put the return value first. */
+                g_assert(state->return_value != NULL);
+                PyTuple_SET_ITEM(return_values, return_values_pos, state->return_value);
+                return_values_pos += 1;
+            }
+
+            state->return_value = return_values;
+        }
+
+        for (i = 0; i < state->n_args; i++) {
+            GIDirection direction;
+            GITypeTag type_tag;
+            GITransfer transfer;
+
+            if (state->args_is_auxiliary[i]) {
+                /* Auxiliary arguments are handled at the same time as their relatives. */
+                continue;
+            }
+
+            direction = g_arg_info_get_direction(state->arg_infos[i]);
+            transfer = g_arg_info_get_ownership_transfer(state->arg_infos[i]);
+
+            type_tag = g_type_info_get_tag(state->arg_type_infos[i]);
+
+            if (type_tag == GI_TYPE_TAG_ARRAY
+                    && (direction != GI_DIRECTION_IN || transfer == GI_TRANSFER_NOTHING)) {
+                /* Create a #GArray. */
+                state->args[i]->v_pointer = _pygi_argument_to_array(state->args[i], state->args, state->arg_type_infos[i], state->is_method);
+            }
+
+            if (direction == GI_DIRECTION_INOUT || direction == GI_DIRECTION_OUT) {
+                /* Convert the argument. */
+                PyObject *obj;
+
+                obj = _pygi_argument_to_object(state->args[i], state->arg_type_infos[i], transfer);
+                if (obj == NULL) {
+                    /* TODO: release arguments. */
+                    return FALSE;
+                }
+
+                g_assert(return_values_pos < state->n_return_values);
+
+                if (state->n_return_values > 1) {
+                    PyTuple_SET_ITEM(state->return_value, return_values_pos, obj);
+                } else {
+                    /* The current return value is None. */
+                    Py_DECREF(state->return_value);
+                    state->return_value = obj;
+                }
+
+                return_values_pos += 1;
+            }
+
+            /* Release the argument. */
+
+            if ((direction == GI_DIRECTION_IN || direction == GI_DIRECTION_INOUT)
+                    && transfer == GI_TRANSFER_CONTAINER) {
+                /* Release the items we kept in another container. */
+                switch (type_tag) {
+                    case GI_TYPE_TAG_ARRAY:
+                    case GI_TYPE_TAG_GLIST:
+                    case GI_TYPE_TAG_GSLIST:
+                        g_assert(backup_args_pos < state->n_backup_args);
+                        _pygi_argument_release(&state->backup_args[backup_args_pos], state->arg_type_infos[i],
+                            transfer, GI_DIRECTION_IN);
+                        break;
+                    case GI_TYPE_TAG_GHASH:
+                    {
+                        GITypeInfo *key_type_info;
+                        GITypeInfo *value_type_info;
+                        GList *item;
+                        gsize length;
+                        gsize j;
+
+                        key_type_info = g_type_info_get_param_type(state->arg_type_infos[i], 0);
+                        value_type_info = g_type_info_get_param_type(state->arg_type_infos[i], 1);
+
+                        g_assert(backup_args_pos < state->n_backup_args);
+                        item = state->backup_args[backup_args_pos].v_pointer;
+
+                        length = g_list_length(item) / 2;
+
+                        for (j = 0; j < length; j++, item = g_list_next(item)) {
+                            _pygi_argument_release((GArgument *)&item->data, key_type_info,
+                                GI_TRANSFER_NOTHING, GI_DIRECTION_IN);
+                        }
+
+                        for (j = 0; j < length; j++, item = g_list_next(item)) {
+                            _pygi_argument_release((GArgument *)&item->data, value_type_info,
+                                GI_TRANSFER_NOTHING, GI_DIRECTION_IN);
+                        }
+
+                        g_list_free(state->backup_args[backup_args_pos].v_pointer);
+
+                        break;
+                    }
+                    default:
+                        g_warn_if_reached();
+                }
+
+                if (direction == GI_DIRECTION_INOUT) {
+                    /* Release the output argument. */
+                    _pygi_argument_release(state->args[i], state->arg_type_infos[i], GI_TRANSFER_CONTAINER,
+                        GI_DIRECTION_OUT);
+                }
+
+                backup_args_pos += 1;
+            } else if (direction == GI_DIRECTION_INOUT) {
+                if (transfer == GI_TRANSFER_NOTHING) {
+                    g_assert(backup_args_pos < state->n_backup_args);
+                    _pygi_argument_release(&state->backup_args[backup_args_pos], state->arg_type_infos[i],
+                        GI_TRANSFER_NOTHING, GI_DIRECTION_IN);
+                    backup_args_pos += 1;
+                }
+
+                _pygi_argument_release(state->args[i], state->arg_type_infos[i], transfer,
+                    GI_DIRECTION_OUT);
+            } else {
+                _pygi_argument_release(state->args[i], state->arg_type_infos[i], transfer, direction);
+            }
+
+            if (type_tag == GI_TYPE_TAG_ARRAY
+                    && (direction != GI_DIRECTION_IN && transfer == GI_TRANSFER_NOTHING)) {
+                /* We created a #GArray and it has not been released above, so free it. */
+                state->args[i]->v_pointer = g_array_free(state->args[i]->v_pointer, FALSE);
+            }
+        }
+
+        g_assert(state->n_return_values <= 1 || return_values_pos == state->n_return_values);
+        g_assert(backup_args_pos == state->n_backup_args);
+    }
+
+    return TRUE;
+}
+
+static void
+_free_invocation_state (struct invocation_state *state)
+{
+    gsize i;
+
+    if (state->return_type_info != NULL) {
+        g_base_info_unref((GIBaseInfo *)state->return_type_info);
+    }
+
+    if (state->closure != NULL) {
+        if (state->closure->scope == GI_SCOPE_TYPE_CALL) 
+            _pygi_invoke_closure_free(state->closure);
+    }
+
+    for (i = 0; i < state->n_args; i++) {
+        g_base_info_unref((GIBaseInfo *)state->arg_type_infos[i]);
+        g_base_info_unref((GIBaseInfo *)state->arg_infos[i]);
+    }
+
+    if (state->arg_infos != NULL) {
+        g_slice_free1(sizeof(gpointer) * state->n_args, state->arg_infos);
+    }
+
+    if (state->arg_type_infos != NULL) {
+        g_slice_free1(sizeof(gpointer) * state->n_args, state->arg_type_infos);
+    }
+
+    if (state->args != NULL) {
+        g_slice_free1(sizeof(gpointer) * state->n_args, state->args);
+    }
+
+    if (state->args_is_auxiliary != NULL) {
+        g_slice_free1(sizeof(gboolean) * state->n_args, state->args_is_auxiliary);
+    }
+
+    if (state->in_args != NULL) {
+        g_slice_free1(sizeof(GArgument) * state->n_in_args, state->in_args);
+    }
+
+    if (state->out_args != NULL) {
+        g_slice_free1(sizeof(GArgument) * state->n_out_args, state->out_args);
+    }
+
+    if (state->out_values != NULL) {
+        g_slice_free1(sizeof(GArgument) * state->n_out_args, state->out_values);
+    }
+
+    if (state->backup_args != NULL) {
+        g_slice_free1(sizeof(GArgument) * state->n_backup_args, state->backup_args);
+    }
+
+    if (PyErr_Occurred()) {
+        Py_CLEAR(state->return_value);
+    }
+}
+
+
+PyObject *
+_wrap_g_function_info_invoke (PyGIBaseInfo *self, PyObject *py_args)
+{
+    struct invocation_state state;
+
+    _initialize_invocation_state (&state, self->info, py_args);
+
+    if (!_prepare_invocation_state (&state, self->info, py_args)) {
+        _free_invocation_state (&state);
+        return NULL;
+    }
+
+    if (!_invoke_function (&state, self->info, py_args)) {
+        _free_invocation_state (&state);
+        return NULL;
+    }
+
+    if (!_process_invocation_state (&state, self->info, py_args)) {
+        _free_invocation_state (&state);
+        return NULL;
+    }
+
+    return state.return_value;
+}
+
diff --git a/gi/pygi-invoke.h b/gi/pygi-invoke.h
new file mode 100644
index 0000000..0d07b21
--- /dev/null
+++ b/gi/pygi-invoke.h
@@ -0,0 +1,37 @@
+/* -*- Mode: C; c-basic-offset: 4 -*-
+ * vim: tabstop=4 shiftwidth=4 expandtab
+ *
+ * Copyright (C) 2005-2009 Johan Dahlin <johan gnome org>
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2.1 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301
+ * USA
+ */
+
+#ifndef __PYGI_INVOKE_H__
+#define __PYGI_INVOKE_H__
+
+#include <Python.h>
+
+#include <girepository.h>
+
+#include "pygi-private.h"
+
+G_BEGIN_DECLS
+
+PyObject *_wrap_g_function_info_invoke (PyGIBaseInfo *self, PyObject *py_args);
+
+G_END_DECLS
+
+#endif /* __PYGI_INVOKE_H__ */
diff --git a/gi/pygi-private.h b/gi/pygi-private.h
index 7d80b7d..0ff5df7 100644
--- a/gi/pygi-private.h
+++ b/gi/pygi-private.h
@@ -27,6 +27,7 @@
 #include "pygi-foreign.h"
 #include "pygi-closure.h"
 #include "pygi-callbacks.h"
+#include "pygi-invoke.h"
 
 G_BEGIN_DECLS
 



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