[pygi] Move invocation code to its own file
- From: Tomeu Vizoso <tomeuv src gnome org>
- To: commits-list gnome org
- Cc:
- Subject: [pygi] Move invocation code to its own file
- Date: Mon, 3 May 2010 16:25:44 +0000 (UTC)
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]