diff options
Diffstat (limited to 'gi/pygi-invoke.c')
-rw-r--r-- | gi/pygi-invoke.c | 177 |
1 files changed, 142 insertions, 35 deletions
diff --git a/gi/pygi-invoke.c b/gi/pygi-invoke.c index 64b3f31a..78984e5c 100644 --- a/gi/pygi-invoke.c +++ b/gi/pygi-invoke.c @@ -21,6 +21,7 @@ * USA */ +#include <pyglib.h> #include "pygi-invoke.h" struct invocation_state @@ -58,19 +59,46 @@ struct invocation_state GIArgument *backup_args; GIArgument return_arg; - PyObject *return_value; + PyObject *return_value; + + GType implementor_gtype; + + /* hack to avoid treating C arrays as GArrays during free + * due to overly complicated array handling + * this will be removed when the new invoke branch is merged + */ + gboolean c_arrays_are_wrapped; }; -static void +static gboolean _initialize_invocation_state (struct invocation_state *state, GIFunctionInfo *info, - PyObject *py_args) + PyObject *py_args, + PyObject *kwargs) { - GIFunctionInfoFlags flags; + if (g_base_info_get_type (info) == GI_INFO_TYPE_FUNCTION) { + GIFunctionInfoFlags flags = g_function_info_get_flags (info); - 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; + state->is_method = (flags & GI_FUNCTION_IS_METHOD) != 0; + state->is_constructor = (flags & GI_FUNCTION_IS_CONSTRUCTOR) != 0; + state->implementor_gtype = 0; + } else { + PyObject *obj; + + state->is_method = TRUE; + state->is_constructor = FALSE; + + obj = PyDict_GetItemString (kwargs, "gtype"); + if (obj == NULL) { + PyErr_SetString (PyExc_TypeError, + "need the GType of the implementor class"); + return FALSE; + } + + state->implementor_gtype = pyg_type_from_object (obj); + if (state->implementor_gtype == 0) + return FALSE; + } /* Count arguments. */ state->n_args = g_callable_info_get_n_args ( (GICallableInfo *) info); @@ -98,6 +126,13 @@ _initialize_invocation_state (struct invocation_state *state, state->out_args = NULL; state->out_values = NULL; state->backup_args = NULL; + + /* HACK: this gets marked FALSE whenever a C array in the args is + * not wrapped by a GArray + */ + state->c_arrays_are_wrapped = TRUE; + + return TRUE; } static gboolean @@ -343,9 +378,20 @@ _prepare_invocation_state (struct invocation_state *state, /* if caller allocates only use one level of indirection */ state->out_args[out_args_pos].v_pointer = NULL; state->args[i] = &state->out_args[out_args_pos]; - if (g_type_is_a (g_registered_type_info_get_g_type (info), G_TYPE_BOXED)) + if (g_type_is_a (g_registered_type_info_get_g_type (info), G_TYPE_BOXED)) { state->args[i]->v_pointer = _pygi_boxed_alloc (info, NULL); - else { + } else if (g_struct_info_is_foreign((GIStructInfo *) info) ) { + PyObject *foreign_struct = + pygi_struct_foreign_convert_from_g_argument(state->arg_type_infos[i], NULL); + + pygi_struct_foreign_convert_to_g_argument( + foreign_struct, + state->arg_type_infos[i], + GI_TRANSFER_EVERYTHING, + state->args[i]); + + Py_DECREF(foreign_struct); + } else { gssize size = g_struct_info_get_size ( (GIStructInfo *) info); state->args[i]->v_pointer = g_malloc0 (size); } @@ -521,6 +567,20 @@ _prepare_invocation_state (struct invocation_state *state, (g_type_info_get_array_type (state->arg_type_infos[i]) == GI_ARRAY_TYPE_C)) { state->args[i]->v_pointer = array->data; + /* HACK: We have unwrapped a C array so + * set the state to reflect this. + * If there is an error between now + * and when we rewrap the array + * we will leak C arrays due to + * being in an inconsitant state. + * e.g. for interfaces with more + * than one C array argument, an + * error may occure when not all + * C arrays have been rewrapped. + * This will be removed once the invoke + * rewrite branch is merged. + */ + state->c_arrays_are_wrapped = FALSE; 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); @@ -541,20 +601,36 @@ _prepare_invocation_state (struct invocation_state *state, static gboolean _invoke_function (struct invocation_state *state, - GIFunctionInfo *function_info, PyObject *py_args) + GICallableInfo *callable_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); + pyg_begin_allow_threads; + if (g_base_info_get_type (callable_info) == GI_INFO_TYPE_FUNCTION) { + retval = g_function_info_invoke ( (GIFunctionInfo *) callable_info, + state->in_args, + state->n_in_args, + state->out_args, + state->n_out_args, + &state->return_arg, + &error); + } else { + retval = g_vfunc_info_invoke ( (GIVFuncInfo *) callable_info, + state->implementor_gtype, + state->in_args, + state->n_in_args, + state->out_args, + state->n_out_args, + &state->return_arg, + &error); + } + pyg_end_allow_threads; + 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); + pyglib_error_check(&error); /* TODO: release input arguments. */ @@ -566,11 +642,7 @@ _invoke_function (struct invocation_state *state, 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); - + if (pyglib_error_check(error)) { /* TODO: release input arguments. */ return FALSE; @@ -653,6 +725,10 @@ _process_invocation_state (struct invocation_state *state, /* The new wrapper increased the reference count, so decrease it. */ g_object_unref (state->return_arg.v_pointer); } + if (state->is_constructor && G_IS_INITIALLY_UNOWNED (state->return_arg.v_pointer)) { + /* GInitiallyUnowned constructors always end up with one extra reference, so decrease it. */ + g_object_unref (state->return_arg.v_pointer); + } break; default: /* Other types don't have neither methods nor constructors. */ @@ -753,13 +829,16 @@ _process_invocation_state (struct invocation_state *state, if (type_tag == GI_TYPE_TAG_INTERFACE) { GIBaseInfo *info; GIInfoType info_type; + GType type; info = g_type_info_get_interface (state->arg_type_infos[i]); g_assert (info != NULL); info_type = g_base_info_get_type (info); + type = g_registered_type_info_get_g_type ( (GIRegisteredTypeInfo *) info); if ( (info_type == GI_INFO_TYPE_STRUCT) && - !g_struct_info_is_foreign((GIStructInfo *) info) ) { + !g_struct_info_is_foreign((GIStructInfo *) info) && + !g_type_is_a (type, G_TYPE_BOXED)) { if (g_arg_info_is_caller_allocates (state->arg_infos[i])) { transfer = GI_TRANSFER_EVERYTHING; } else if (transfer == GI_TRANSFER_EVERYTHING) { @@ -797,6 +876,14 @@ _process_invocation_state (struct invocation_state *state, } + /* HACK: We rewrapped any C arrays above in a GArray so they are ok to + * free as GArrays. We will always leak C arrays if there is + * an error before we reach this state as there is no easy way + * to know which arrays were wrapped if there are more than one. + * This will be removed with better array handling once merge + * the invoke rewrite branch. + */ + state->c_arrays_are_wrapped = TRUE; g_assert (state->n_return_values <= 1 || return_values_pos == state->n_return_values); } @@ -827,9 +914,7 @@ _free_invocation_state (struct invocation_state *state) continue; } - if (state->args != NULL - && state->args[i] != NULL - && state->arg_infos[i] != NULL + if (state->arg_infos[i] != NULL && state->arg_type_infos[i] != NULL) { GIDirection direction; GITypeTag type_tag; @@ -838,20 +923,38 @@ _free_invocation_state (struct invocation_state *state) 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]); - /* Release the argument. */ if (direction == GI_DIRECTION_INOUT) { - _pygi_argument_release (&state->backup_args[backup_args_pos], state->arg_type_infos[i], - transfer, GI_DIRECTION_IN); + if (state->args != NULL) { + _pygi_argument_release (&state->backup_args[backup_args_pos], + state->arg_type_infos[i], + transfer, GI_DIRECTION_IN); + } backup_args_pos += 1; } - _pygi_argument_release (state->args[i], state->arg_type_infos[i], transfer, direction); + if (state->args != NULL && state->args[i] != NULL) { + type_tag = g_type_info_get_tag (state->arg_type_infos[i]); + + if (type_tag == GI_TYPE_TAG_ARRAY && + (direction == GI_DIRECTION_IN || direction == GI_DIRECTION_INOUT) && + (g_type_info_get_array_type (state->arg_type_infos[i]) == GI_ARRAY_TYPE_C) && + !state->c_arrays_are_wrapped) { + /* HACK: Noop - we are in an inconsitant state due to + * complex array handler so leak any C arrays + * as we don't know if we can free them safely. + * This will be removed when we merge the + * invoke rewrite branch. + */ + } else { + _pygi_argument_release (state->args[i], state->arg_type_infos[i], + transfer, direction); + } - if (type_tag == GI_TYPE_TAG_ARRAY + 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); + /* We created an *out* #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); + } } } @@ -894,11 +997,15 @@ _free_invocation_state (struct invocation_state *state) PyObject * -_wrap_g_function_info_invoke (PyGIBaseInfo *self, PyObject *py_args) +_wrap_g_callable_info_invoke (PyGIBaseInfo *self, PyObject *py_args, + PyObject *kwargs) { struct invocation_state state = { 0, }; - _initialize_invocation_state (&state, self->info, py_args); + if (!_initialize_invocation_state (&state, self->info, py_args, kwargs)) { + _free_invocation_state (&state); + return NULL; + } if (!_prepare_invocation_state (&state, self->info, py_args)) { _free_invocation_state (&state); |