diff options
author | TizenOpenSource <tizenopensrc@samsung.com> | 2023-12-08 13:16:12 +0900 |
---|---|---|
committer | TizenOpenSource <tizenopensrc@samsung.com> | 2023-12-08 13:16:12 +0900 |
commit | 454aa52f950f2348756dfffc9c26fb74d4aa3bef (patch) | |
tree | 4e8be3f8c8218dbafe65e22dfc73e9351c1d5bbb /gi/pygi-closure.c | |
parent | f3eae5a895fc60cb99c0c366bdd011018ce3bc7b (diff) | |
download | pygobject2-454aa52f950f2348756dfffc9c26fb74d4aa3bef.tar.gz pygobject2-454aa52f950f2348756dfffc9c26fb74d4aa3bef.tar.bz2 pygobject2-454aa52f950f2348756dfffc9c26fb74d4aa3bef.zip |
Imported Upstream version 3.46.0upstream/3.46.0
Diffstat (limited to 'gi/pygi-closure.c')
-rw-r--r-- | gi/pygi-closure.c | 1074 |
1 files changed, 701 insertions, 373 deletions
diff --git a/gi/pygi-closure.c b/gi/pygi-closure.c index 2f5548a..4e19c5e 100644 --- a/gi/pygi-closure.c +++ b/gi/pygi-closure.c @@ -14,12 +14,27 @@ * 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 + * License along with this library; if not, see <http://www.gnu.org/licenses/>. */ -#include "pygi-private.h" +#include "pygi-closure.h" +#include "pygi-error.h" +#include "pygi-marshal-cleanup.h" +#include "pygi-invoke.h" +#include "pygi-ccallback.h" +#include "pygi-info.h" + +extern PyObject *_PyGIDefaultArgPlaceholder; + +typedef struct _PyGICallbackCache +{ + PyGIArgCache arg_cache; + gssize user_data_index; + gssize destroy_notify_index; + GIScopeType scope; + GIInterfaceInfo *interface_info; + PyGIClosureCache *closure_cache; +} PyGICallbackCache; /* This maintains a list of closures which can be free'd whenever as they have been called. We will free them on the next @@ -28,493 +43,494 @@ static GSList* async_free_list; static void -_pygi_closure_assign_pyobj_to_retval (gpointer retval, PyObject *object, - GITypeInfo *type_info, - GITransfer transfer) +_pygi_closure_assign_pyobj_to_retval (gpointer retval, + GIArgument *arg, + PyGIArgCache *arg_cache) { - GIArgument arg = _pygi_argument_from_object (object, type_info, transfer); - GITypeTag type_tag; - if (PyErr_Occurred ()) - return; - - type_tag = g_type_info_get_tag (type_info); - if (retval == NULL) return; - switch (type_tag) { + switch (arg_cache->type_tag) { case GI_TYPE_TAG_BOOLEAN: - *((ffi_sarg *) retval) = arg.v_boolean; + *((ffi_sarg *) retval) = arg->v_boolean; break; case GI_TYPE_TAG_INT8: - *((ffi_sarg *) retval) = arg.v_int8; + *((ffi_sarg *) retval) = arg->v_int8; break; case GI_TYPE_TAG_UINT8: - *((ffi_arg *) retval) = arg.v_uint8; + *((ffi_arg *) retval) = arg->v_uint8; break; case GI_TYPE_TAG_INT16: - *((ffi_sarg *) retval) = arg.v_int16; + *((ffi_sarg *) retval) = arg->v_int16; break; case GI_TYPE_TAG_UINT16: - *((ffi_arg *) retval) = arg.v_uint16; + *((ffi_arg *) retval) = arg->v_uint16; break; case GI_TYPE_TAG_INT32: - *((ffi_sarg *) retval) = arg.v_int32; + *((ffi_sarg *) retval) = arg->v_int32; break; case GI_TYPE_TAG_UINT32: - *((ffi_arg *) retval) = arg.v_uint32; + *((ffi_arg *) retval) = arg->v_uint32; break; case GI_TYPE_TAG_INT64: - *((ffi_sarg *) retval) = arg.v_int64; + *((ffi_sarg *) retval) = arg->v_int64; break; case GI_TYPE_TAG_UINT64: - *((ffi_arg *) retval) = arg.v_uint64; + *((ffi_arg *) retval) = arg->v_uint64; break; case GI_TYPE_TAG_FLOAT: - *((gfloat *) retval) = arg.v_float; + *((gfloat *) retval) = arg->v_float; break; case GI_TYPE_TAG_DOUBLE: - *((gdouble *) retval) = arg.v_double; + *((gdouble *) retval) = arg->v_double; break; case GI_TYPE_TAG_GTYPE: - *((ffi_arg *) retval) = arg.v_ulong; + *((ffi_arg *) retval) = arg->v_size; break; case GI_TYPE_TAG_UNICHAR: - *((ffi_arg *) retval) = arg.v_uint32; + *((ffi_arg *) retval) = arg->v_uint32; break; case GI_TYPE_TAG_INTERFACE: { - GIBaseInfo* interface_info; - GIInfoType interface_type; + GIBaseInfo *interface_info; - interface_info = g_type_info_get_interface(type_info); - interface_type = g_base_info_get_type(interface_info); + interface_info = ((PyGIInterfaceCache *) arg_cache)->interface_info; - switch (interface_type) { + switch (g_base_info_get_type (interface_info)) { case GI_INFO_TYPE_ENUM: - *(ffi_sarg *) retval = arg.v_int; + *(ffi_sarg *) retval = arg->v_int; break; case GI_INFO_TYPE_FLAGS: - *(ffi_arg *) retval = arg.v_uint; + *(ffi_arg *) retval = arg->v_uint; break; default: - *(ffi_arg *) retval = (ffi_arg) arg.v_pointer; + *(ffi_arg *) retval = (ffi_arg) arg->v_pointer; break; } - g_base_info_unref (interface_info); break; } default: - *(ffi_arg *) retval = (ffi_arg) arg.v_pointer; + *(ffi_arg *) retval = (ffi_arg) arg->v_pointer; break; } } static void -_pygi_closure_clear_retval (GICallableInfo *callable_info, gpointer retval) -{ - GITypeInfo return_type_info; - GITypeTag return_type_tag; - - g_callable_info_load_return_type (callable_info, &return_type_info); - return_type_tag = g_type_info_get_tag (&return_type_info); - if (return_type_tag != GI_TYPE_TAG_VOID) { - *((ffi_arg *) retval) = 0; - } -} - -static void -_pygi_closure_assign_pyobj_to_out_argument (gpointer out_arg, PyObject *object, - GITypeInfo *type_info, - GITransfer transfer) +_pygi_closure_assign_pyobj_to_out_argument (gpointer out_arg, + GIArgument *arg, + PyGIArgCache *arg_cache) { - GIArgument arg = _pygi_argument_from_object (object, type_info, transfer); - GITypeTag type_tag = g_type_info_get_tag (type_info); - if (out_arg == NULL) return; - switch (type_tag) { + switch (arg_cache->type_tag) { case GI_TYPE_TAG_BOOLEAN: - *((gboolean *) out_arg) = arg.v_boolean; + *((gboolean *) out_arg) = arg->v_boolean; break; case GI_TYPE_TAG_INT8: - *((gint8 *) out_arg) = arg.v_int8; + *((gint8 *) out_arg) = arg->v_int8; break; case GI_TYPE_TAG_UINT8: - *((guint8 *) out_arg) = arg.v_uint8; + *((guint8 *) out_arg) = arg->v_uint8; break; case GI_TYPE_TAG_INT16: - *((gint16 *) out_arg) = arg.v_int16; + *((gint16 *) out_arg) = arg->v_int16; break; case GI_TYPE_TAG_UINT16: - *((guint16 *) out_arg) = arg.v_uint16; + *((guint16 *) out_arg) = arg->v_uint16; break; case GI_TYPE_TAG_INT32: - *((gint32 *) out_arg) = arg.v_int32; + *((gint32 *) out_arg) = arg->v_int32; break; case GI_TYPE_TAG_UINT32: - *((guint32 *) out_arg) = arg.v_uint32; + *((guint32 *) out_arg) = arg->v_uint32; break; case GI_TYPE_TAG_INT64: - *((gint64 *) out_arg) = arg.v_int64; + *((gint64 *) out_arg) = arg->v_int64; break; case GI_TYPE_TAG_UINT64: - *((glong *) out_arg) = arg.v_uint64; + *((guint64 *) out_arg) = arg->v_uint64; break; case GI_TYPE_TAG_FLOAT: - *((gfloat *) out_arg) = arg.v_float; + *((gfloat *) out_arg) = arg->v_float; break; case GI_TYPE_TAG_DOUBLE: - *((gdouble *) out_arg) = arg.v_double; + *((gdouble *) out_arg) = arg->v_double; break; case GI_TYPE_TAG_GTYPE: - *((gulong *) out_arg) = arg.v_ulong; + *((GType *) out_arg) = arg->v_size; break; case GI_TYPE_TAG_UNICHAR: - *((guint32 *) out_arg) = arg.v_uint32; + *((guint32 *) out_arg) = arg->v_uint32; break; case GI_TYPE_TAG_INTERFACE: { - GIBaseInfo *interface; - GIInfoType interface_type; + GIBaseInfo *interface_info; - interface = g_type_info_get_interface (type_info); - interface_type = g_base_info_get_type (interface); + interface_info = ((PyGIInterfaceCache *) arg_cache)->interface_info; - switch (interface_type) { + switch (g_base_info_get_type (interface_info)) { case GI_INFO_TYPE_ENUM: - *(gint *) out_arg = arg.v_int; + *(gint *) out_arg = arg->v_int; break; case GI_INFO_TYPE_FLAGS: - *(guint *) out_arg = arg.v_uint; + *(guint *) out_arg = arg->v_uint; break; case GI_INFO_TYPE_STRUCT: - if (!g_type_info_is_pointer (type_info)) { - if (object != Py_None) { - gsize item_size = _pygi_g_type_info_size (type_info); - memcpy (out_arg, arg.v_pointer, item_size); + if (!arg_cache->is_pointer) { + if (arg->v_pointer != NULL) { + gsize item_size = _pygi_g_type_info_size (arg_cache->type_info); + memcpy (out_arg, arg->v_pointer, item_size); } break; } - - /* Fall through if pointer */ + *((gpointer *) out_arg) = arg->v_pointer; + break; default: - *((gpointer *) out_arg) = arg.v_pointer; + *((gpointer *) out_arg) = arg->v_pointer; break; } - - g_base_info_unref (interface); break; } default: - *((gpointer *) out_arg) = arg.v_pointer; + *((gpointer *) out_arg) = arg->v_pointer; break; } } -static GIArgument * -_pygi_closure_convert_ffi_arguments (GICallableInfo *callable_info, void **args) +static void +_pygi_closure_convert_ffi_arguments (PyGIInvokeArgState *state, + PyGICallableCache *cache, + void **args) { - gint num_args, i; - GIArgInfo arg_info; - GITypeInfo arg_type; - GITypeTag tag; - GIDirection direction; - GIArgument *g_args; - - num_args = g_callable_info_get_n_args (callable_info); - g_args = g_new0 (GIArgument, num_args); - - for (i = 0; i < num_args; i++) { - g_callable_info_load_arg (callable_info, i, &arg_info); - g_arg_info_load_type (&arg_info, &arg_type); - tag = g_type_info_get_tag (&arg_type); - direction = g_arg_info_get_direction (&arg_info); - - if (direction == GI_DIRECTION_OUT || direction == GI_DIRECTION_INOUT) { - g_args[i].v_pointer = * (gpointer *) args[i]; + guint i; + + for (i = 0; i < _pygi_callable_cache_args_len (cache); i++) { + PyGIArgCache *arg_cache = g_ptr_array_index (cache->args_cache, i); + gpointer arg_pointer; + + if (arg_cache->direction & PYGI_DIRECTION_FROM_PYTHON) { + state[i].arg_value.v_pointer = * (gpointer *) args[i]; + + if (state[i].arg_value.v_pointer == NULL) + continue; + + state[i].arg_pointer.v_pointer = state[i].arg_value.v_pointer; + arg_pointer = state[i].arg_value.v_pointer; } else { - switch (tag) { - case GI_TYPE_TAG_BOOLEAN: - g_args[i].v_boolean = * (gboolean *) args[i]; - break; - case GI_TYPE_TAG_INT8: - g_args[i].v_int8 = * (gint8 *) args[i]; - break; - case GI_TYPE_TAG_UINT8: - g_args[i].v_uint8 = * (guint8 *) args[i]; - break; - case GI_TYPE_TAG_INT16: - g_args[i].v_int16 = * (gint16 *) args[i]; - break; - case GI_TYPE_TAG_UINT16: - g_args[i].v_uint16 = * (guint16 *) args[i]; - break; - case GI_TYPE_TAG_INT32: - g_args[i].v_int32 = * (gint32 *) args[i]; - break; - case GI_TYPE_TAG_UINT32: - g_args[i].v_uint32 = * (guint32 *) args[i]; - break; - case GI_TYPE_TAG_INT64: - g_args[i].v_int64 = * (glong *) args[i]; - break; - case GI_TYPE_TAG_UINT64: - g_args[i].v_uint64 = * (glong *) args[i]; - break; - case GI_TYPE_TAG_FLOAT: - g_args[i].v_float = * (gfloat *) args[i]; - break; - case GI_TYPE_TAG_DOUBLE: - g_args[i].v_double = * (gdouble *) args[i]; - break; - case GI_TYPE_TAG_UTF8: - g_args[i].v_string = * (gchar **) args[i]; - break; - case GI_TYPE_TAG_INTERFACE: - { - GIBaseInfo *interface; - GIInfoType interface_type; - - interface = g_type_info_get_interface (&arg_type); - interface_type = g_base_info_get_type (interface); - - if (interface_type == GI_INFO_TYPE_OBJECT || - interface_type == GI_INFO_TYPE_INTERFACE) { - g_args[i].v_pointer = * (gpointer *) args[i]; - g_base_info_unref (interface); - break; - } else if (interface_type == GI_INFO_TYPE_ENUM || - interface_type == GI_INFO_TYPE_FLAGS) { - g_args[i].v_uint = * (guint *) args[i]; - g_base_info_unref (interface); - break; - } else if (interface_type == GI_INFO_TYPE_STRUCT || - interface_type == GI_INFO_TYPE_CALLBACK) { - g_args[i].v_pointer = * (gpointer *) args[i]; - g_base_info_unref (interface); - break; - } + arg_pointer = args[i]; + } + + switch (arg_cache->type_tag) { + case GI_TYPE_TAG_BOOLEAN: + state[i].arg_value.v_boolean = * (gboolean *) arg_pointer; + break; + case GI_TYPE_TAG_INT8: + state[i].arg_value.v_int8 = * (gint8 *) arg_pointer; + break; + case GI_TYPE_TAG_UINT8: + state[i].arg_value.v_uint8 = * (guint8 *) arg_pointer; + break; + case GI_TYPE_TAG_INT16: + state[i].arg_value.v_int16 = * (gint16 *) arg_pointer; + break; + case GI_TYPE_TAG_UINT16: + state[i].arg_value.v_uint16 = * (guint16 *) arg_pointer; + break; + case GI_TYPE_TAG_INT32: + state[i].arg_value.v_int32 = * (gint32 *) arg_pointer; + break; + case GI_TYPE_TAG_UINT32: + state[i].arg_value.v_uint32 = * (guint32 *) arg_pointer; + break; + case GI_TYPE_TAG_INT64: + state[i].arg_value.v_int64 = * (gint64 *) arg_pointer; + break; + case GI_TYPE_TAG_UINT64: + state[i].arg_value.v_uint64 = * (guint64 *) arg_pointer; + break; + case GI_TYPE_TAG_FLOAT: + state[i].arg_value.v_float = * (gfloat *) arg_pointer; + break; + case GI_TYPE_TAG_DOUBLE: + state[i].arg_value.v_double = * (gdouble *) arg_pointer; + break; + case GI_TYPE_TAG_UTF8: + state[i].arg_value.v_string = * (gchar **) arg_pointer; + break; + case GI_TYPE_TAG_INTERFACE: + { + GIBaseInfo *interface; + GIInfoType interface_type; + + interface = ((PyGIInterfaceCache *) arg_cache)->interface_info; + interface_type = g_base_info_get_type (interface); - g_base_info_unref (interface); + if (interface_type == GI_INFO_TYPE_ENUM) { + state[i].arg_value.v_int = * (gint *) arg_pointer; + } else if (interface_type == GI_INFO_TYPE_FLAGS) { + state[i].arg_value.v_uint = * (guint *) arg_pointer; + } else { + state[i].arg_value.v_pointer = * (gpointer *) arg_pointer; } - case GI_TYPE_TAG_ERROR: - case GI_TYPE_TAG_GHASH: - case GI_TYPE_TAG_GLIST: - case GI_TYPE_TAG_GSLIST: - case GI_TYPE_TAG_ARRAY: - case GI_TYPE_TAG_VOID: - g_args[i].v_pointer = * (gpointer *) args[i]; - break; - default: - g_warning ("Unhandled type tag %s", g_type_tag_to_string (tag)); - g_args[i].v_pointer = 0; + break; } + case GI_TYPE_TAG_UNICHAR: + state[i].arg_value.v_uint32 = * (guint32 *) arg_pointer; + break; + case GI_TYPE_TAG_ERROR: + case GI_TYPE_TAG_GHASH: + case GI_TYPE_TAG_GLIST: + case GI_TYPE_TAG_GSLIST: + case GI_TYPE_TAG_ARRAY: + case GI_TYPE_TAG_VOID: + state[i].arg_value.v_pointer = * (gpointer *) arg_pointer; + break; + default: + g_warning ("Unhandled type tag %s", + g_type_tag_to_string (arg_cache->type_tag)); + state[i].arg_value.v_pointer = 0; } } - return g_args; + + if (cache->throws) { + gssize error_index = _pygi_callable_cache_args_len (cache); + + state[error_index].arg_value.v_pointer = * (gpointer *) args[error_index]; + } } static gboolean -_pygi_closure_convert_arguments (GICallableInfo *callable_info, void **args, - void *user_data, PyObject **py_args, - GIArgument **out_args) +_invoke_state_init_from_cache (PyGIInvokeState *state, + PyGIClosureCache *closure_cache, + void **args) { - int n_args = g_callable_info_get_n_args (callable_info); - int n_in_args = 0; - int n_out_args = 0; - int i; - int user_data_arg = -1; - int destroy_notify_arg = -1; - - GIArgument *g_args = NULL; - - *py_args = NULL; - *py_args = PyTuple_New (n_args); - if (*py_args == NULL) - goto error; - - *out_args = NULL; - *out_args = g_new0 (GIArgument, n_args); - g_args = _pygi_closure_convert_ffi_arguments (callable_info, args); - - for (i = 0; i < n_args; i++) { - GIArgInfo arg_info; - GIDirection direction; - - /* Special case callbacks and skip over userdata and Destroy Notify */ - if (i == user_data_arg || i == destroy_notify_arg) - continue; - - g_callable_info_load_arg (callable_info, i, &arg_info); - direction = g_arg_info_get_direction (&arg_info); - - if (direction == GI_DIRECTION_IN || direction == GI_DIRECTION_INOUT) { - GITypeInfo arg_type; - GITypeTag arg_tag; - GITransfer transfer; - PyObject *value; - GIArgument *arg; - gboolean free_array; + PyGICallableCache *cache = (PyGICallableCache *) closure_cache; + + state->n_args = _pygi_callable_cache_args_len (cache); + state->n_py_in_args = state->n_args; + + /* Increment after setting the number of Python input args */ + if (cache->throws) { + state->n_args++; + } + + state->py_in_args = PyTuple_New (state->n_py_in_args); + if (state->py_in_args == NULL) { + PyErr_NoMemory (); + return FALSE; + } + + state->args = NULL; + state->error = NULL; + + if (!_pygi_invoke_arg_state_init (state)) { + return FALSE; + } + + state->ffi_args = NULL; + + _pygi_closure_convert_ffi_arguments (state->args, cache, args); + return TRUE; +} + +static void +_invoke_state_clear (PyGIInvokeState *state) +{ + _pygi_invoke_arg_state_free (state); + Py_XDECREF (state->py_in_args); +} + +static gboolean +_pygi_closure_convert_arguments (PyGIInvokeState *state, + PyGIClosureCache *closure_cache) +{ + PyGICallableCache *cache = (PyGICallableCache *) closure_cache; + gssize n_in_args = 0; + gssize i; - g_arg_info_load_type (&arg_info, &arg_type); - arg_tag = g_type_info_get_tag (&arg_type); - transfer = g_arg_info_get_ownership_transfer (&arg_info); - free_array = FALSE; + for (i = 0; (gsize)i < _pygi_callable_cache_args_len (cache); i++) { + PyGIArgCache *arg_cache; - if (direction == GI_DIRECTION_IN && arg_tag == GI_TYPE_TAG_VOID && - g_type_info_is_pointer (&arg_type)) { + arg_cache = g_ptr_array_index (cache->args_cache, i); + + if (arg_cache->direction & PYGI_DIRECTION_TO_PYTHON) { + PyObject *value; - if (user_data == NULL) { + if (cache->user_data_index == i) { + if (state->user_data == NULL) { + /* user_data can be NULL for connect functions which don't accept + * user_data or as the default for user_data in the middle of function + * arguments. + */ Py_INCREF (Py_None); value = Py_None; } else { - value = user_data; - Py_INCREF (value); + /* Extend the callbacks args with user_data as variable args. */ + gssize j, user_data_len; + PyObject *py_user_data = state->user_data; + + if (!PyTuple_Check (py_user_data)) { + PyErr_SetString (PyExc_TypeError, "expected tuple for callback user_data"); + return FALSE; + } + + user_data_len = PyTuple_Size (py_user_data); + _PyTuple_Resize (&state->py_in_args, + state->n_py_in_args + user_data_len - 1); + + for (j = 0; j < user_data_len; j++, n_in_args++) { + value = PyTuple_GetItem (py_user_data, j); + Py_INCREF (value); + PyTuple_SET_ITEM (state->py_in_args, n_in_args, value); + } + /* We can assume user_data args are never going to be inout, + * so just continue here. + */ + continue; } - } else if (direction == GI_DIRECTION_IN && - arg_tag == GI_TYPE_TAG_INTERFACE) { - /* Handle callbacks as a special case */ - GIBaseInfo *info; - GIInfoType info_type; - - info = g_type_info_get_interface (&arg_type); - info_type = g_base_info_get_type (info); - - arg = (GIArgument*) &g_args[i]; - - if (info_type == GI_INFO_TYPE_CALLBACK) { - gpointer user_data = NULL; - GDestroyNotify destroy_notify = NULL; - GIScopeType scope = g_arg_info_get_scope(&arg_info); - - user_data_arg = g_arg_info_get_closure(&arg_info); - destroy_notify_arg = g_arg_info_get_destroy(&arg_info); - - if (user_data_arg != -1) - user_data = g_args[user_data_arg].v_pointer; - - if (destroy_notify_arg != -1) - user_data = (GDestroyNotify) g_args[destroy_notify_arg].v_pointer; - - value = _pygi_ccallback_new(arg->v_pointer, - user_data, - scope, - (GIFunctionInfo *) info, - destroy_notify); - } else - value = _pygi_argument_to_object (arg, &arg_type, transfer); - - g_base_info_unref (info); - if (value == NULL) - goto error; + } else if (arg_cache->meta_type != PYGI_META_ARG_TYPE_PARENT) { + continue; } else { - if (direction == GI_DIRECTION_IN) - arg = (GIArgument*) &g_args[i]; - else - arg = (GIArgument*) g_args[i].v_pointer; - - if (g_type_info_get_tag (&arg_type) == GI_TYPE_TAG_ARRAY) - arg->v_pointer = _pygi_argument_to_array (arg, (GIArgument **) args, NULL, - callable_info, &arg_type, &free_array); - - value = _pygi_argument_to_object (arg, &arg_type, transfer); - - if (free_array) - g_array_free (arg->v_pointer, FALSE); - - if (value == NULL) - goto error; + gpointer cleanup_data = NULL; + + value = arg_cache->to_py_marshaller (state, + cache, + arg_cache, + &state->args[i].arg_value, + &cleanup_data); + state->args[i].to_py_arg_cleanup_data = cleanup_data; + + if (value == NULL) { + pygi_marshal_cleanup_args_to_py_parameter_fail (state, + cache, + i); + return FALSE; + } } - PyTuple_SET_ITEM (*py_args, n_in_args, value); - n_in_args++; - } - if (direction == GI_DIRECTION_OUT || direction == GI_DIRECTION_INOUT) { - (*out_args) [n_out_args] = g_args[i]; - n_out_args++; + PyTuple_SET_ITEM (state->py_in_args, n_in_args, value); + n_in_args++; } - } - if (_PyTuple_Resize (py_args, n_in_args) == -1) - goto error; + if (_PyTuple_Resize (&state->py_in_args, n_in_args) == -1) + return FALSE; - g_free (g_args); return TRUE; - -error: - Py_CLEAR (*py_args); - g_free (*out_args); - *out_args = NULL; - g_free (g_args); - - return FALSE; } -static void -_pygi_closure_set_out_arguments (GICallableInfo *callable_info, - PyObject *py_retval, GIArgument *out_args, +static gboolean +_pygi_closure_set_out_arguments (PyGIInvokeState *state, + PyGICallableCache *cache, + PyObject *py_retval, void *resp) { - int n_args, i, i_py_retval, i_out_args; - GITypeInfo return_type_info; - GITypeTag return_type_tag; - - i_py_retval = 0; - g_callable_info_load_return_type (callable_info, &return_type_info); - return_type_tag = g_type_info_get_tag (&return_type_info); - if (return_type_tag != GI_TYPE_TAG_VOID) { - GITransfer transfer = g_callable_info_get_caller_owns (callable_info); + gssize i; + gssize i_py_retval = 0; + gboolean success; + + if (cache->return_cache->type_tag != GI_TYPE_TAG_VOID) { + PyObject *item = py_retval; + if (PyTuple_Check (py_retval)) { - PyObject *item = PyTuple_GET_ITEM (py_retval, 0); - _pygi_closure_assign_pyobj_to_retval (resp, item, - &return_type_info, transfer); - } else { - _pygi_closure_assign_pyobj_to_retval (resp, py_retval, - &return_type_info, transfer); + item = PyTuple_GET_ITEM (py_retval, 0); + } + + success = cache->return_cache->from_py_marshaller (state, + cache, + cache->return_cache, + item, + &state->return_arg, + &state->args[0].arg_cleanup_data); + + if (!success) { + pygi_marshal_cleanup_args_return_fail (state, + cache); + return FALSE; } + + _pygi_closure_assign_pyobj_to_retval (resp, &state->return_arg, + cache->return_cache); i_py_retval++; } - i_out_args = 0; - n_args = g_callable_info_get_n_args (callable_info); - for (i = 0; i < n_args; i++) { - GIArgInfo arg_info; - GITypeInfo type_info; - GIDirection direction; - g_callable_info_load_arg (callable_info, i, &arg_info); - g_arg_info_load_type (&arg_info, &type_info); - direction = g_arg_info_get_direction (&arg_info); - - if (direction == GI_DIRECTION_OUT || direction == GI_DIRECTION_INOUT) { - GITransfer transfer = g_arg_info_get_ownership_transfer (&arg_info); - - if (g_type_info_get_tag (&type_info) == GI_TYPE_TAG_ERROR) { - /* TODO: check if an exception has been set and convert it to a GError */ - out_args[i_out_args].v_pointer = NULL; - i_out_args++; + for (i = 0; (gsize)i < _pygi_callable_cache_args_len (cache); i++) { + PyGIArgCache *arg_cache = g_ptr_array_index (cache->args_cache, i); + + if (arg_cache->direction & PYGI_DIRECTION_FROM_PYTHON) { + PyObject *item = py_retval; + + if (arg_cache->type_tag == GI_TYPE_TAG_ERROR) { + * (GError **) state->args[i].arg_pointer.v_pointer = NULL; continue; } if (PyTuple_Check (py_retval)) { - PyObject *item = PyTuple_GET_ITEM (py_retval, i_py_retval); - _pygi_closure_assign_pyobj_to_out_argument ( - out_args[i_out_args].v_pointer, item, &type_info, transfer); - } else if (i_py_retval == 0) { - _pygi_closure_assign_pyobj_to_out_argument ( - out_args[i_out_args].v_pointer, py_retval, &type_info, - transfer); - } else - g_assert_not_reached(); - - i_out_args++; + item = PyTuple_GET_ITEM (py_retval, i_py_retval); + } else if (i_py_retval != 0) { + pygi_marshal_cleanup_args_to_py_parameter_fail (state, + cache, + i_py_retval); + return FALSE; + } + + success = arg_cache->from_py_marshaller (state, + cache, + arg_cache, + item, + &state->args[i].arg_value, + &state->args[i_py_retval].arg_cleanup_data); + + if (!success) { + pygi_marshal_cleanup_args_to_py_parameter_fail (state, + cache, + i_py_retval); + return FALSE; + } + + _pygi_closure_assign_pyobj_to_out_argument (state->args[i].arg_pointer.v_pointer, + &state->args[i].arg_value, arg_cache); + i_py_retval++; } } + + return TRUE; +} + +static void +_pygi_closure_clear_retvals (PyGIInvokeState *state, + PyGICallableCache *cache, + gpointer resp) +{ + gsize i; + GIArgument arg = { 0, }; + + if (cache->return_cache->type_tag != GI_TYPE_TAG_VOID) { + _pygi_closure_assign_pyobj_to_retval (resp, &arg, + cache->return_cache); + } + + for (i = 0; i < _pygi_callable_cache_args_len (cache); i++) { + PyGIArgCache *arg_cache = g_ptr_array_index (cache->args_cache, i); + + if (arg_cache->direction & PYGI_DIRECTION_FROM_PYTHON) { + _pygi_closure_assign_pyobj_to_out_argument (state->args[i].arg_pointer.v_pointer, + &arg, arg_cache); + } + } + + if (cache->throws) { + gssize error_index = state->n_args - 1; + GError **error = (GError **) state->args[error_index].arg_value.v_pointer; + + if (error != NULL) { + pygi_gerror_exception_check (error); + } + } } static void @@ -534,43 +550,56 @@ _pygi_closure_handle (ffi_cif *cif, void **args, void *data) { - PyGILState_STATE state; + PyGILState_STATE py_state; PyGICClosure *closure = data; PyObject *retval; - PyObject *py_args; - GIArgument *out_args = NULL; + gboolean success; + PyGIInvokeState state = { 0, }; + + /* Ignore closures when Python is not initialized. This can happen in cases + * where calling Python implemented vfuncs can happen at shutdown time. + * See: https://bugzilla.gnome.org/show_bug.cgi?id=722562 */ + if (!Py_IsInitialized()) { + return; + } /* Lock the GIL as we are coming into this code without the lock and we may be executing python code */ - state = PyGILState_Ensure(); + py_state = PyGILState_Ensure (); + + if (closure->cache == NULL) + goto end; + + state.user_data = closure->user_data; - if (!_pygi_closure_convert_arguments ( (GICallableInfo *) closure->info, args, - closure->user_data, - &py_args, &out_args)) { - if (PyErr_Occurred ()) - PyErr_Print(); + _invoke_state_init_from_cache (&state, closure->cache, args); + + if (!_pygi_closure_convert_arguments (&state, closure->cache)) { + _pygi_closure_clear_retvals (&state, closure->cache, result); goto end; } - retval = PyObject_CallObject ( (PyObject *) closure->function, py_args); - Py_DECREF (py_args); + retval = PyObject_CallObject ( (PyObject *) closure->function, state.py_in_args); if (retval == NULL) { - _pygi_closure_clear_retval (closure->info, result); - PyErr_Print(); + _pygi_closure_clear_retvals (&state, closure->cache, result); goto end; } - _pygi_closure_set_out_arguments (closure->info, retval, out_args, result); - if (PyErr_Occurred ()) { - _pygi_closure_clear_retval (closure->info, result); - PyErr_Print(); + pygi_marshal_cleanup_args_to_py_marshal_success (&state, closure->cache); + success = _pygi_closure_set_out_arguments (&state, closure->cache, retval, result); + + if (!success) { + pygi_marshal_cleanup_args_from_py_marshal_success (&state, closure->cache); + _pygi_closure_clear_retvals (&state, closure->cache, result); } Py_DECREF (retval); end: - g_free (out_args); + + if (PyErr_Occurred ()) + PyErr_Print (); /* Now that the closure has finished we can make a decision about how to free it. Scope call gets free'd at the end of wrap_g_function_info_invoke. @@ -591,23 +620,31 @@ end: async_free_list = g_slist_prepend (async_free_list, closure); break; default: - g_error ("Invalid scope reached inside %s. Possibly a bad annotation?", - g_base_info_get_name (closure->info)); + /* Handle new scopes added by gobject-introspection */ + g_critical ("Unknown scope reached inside %s. Please file an issue " + "at https://gitlab.gnome.org/GNOME/pygobject/issues/new", + g_base_info_get_name (closure->info)); } - PyGILState_Release (state); + _invoke_state_clear (&state); + PyGILState_Release (py_state); } -void _pygi_invoke_closure_free (gpointer data) +void _pygi_invoke_closure_free (PyGICClosure* invoke_closure) { - PyGICClosure* invoke_closure = (PyGICClosure *) data; - +#if GI_CHECK_VERSION (1, 72, 0) + g_callable_info_destroy_closure (invoke_closure->info, + invoke_closure->closure); +#else g_callable_info_free_closure (invoke_closure->info, invoke_closure->closure); +#endif if (invoke_closure->info) g_base_info_unref ( (GIBaseInfo*) invoke_closure->info); + invoke_closure->cache = NULL; + _pygi_invoke_closure_clear_py_data(invoke_closure); g_slice_free (PyGICClosure, invoke_closure); @@ -616,9 +653,10 @@ void _pygi_invoke_closure_free (gpointer data) PyGICClosure* _pygi_make_native_closure (GICallableInfo* info, + PyGIClosureCache *cache, GIScopeType scope, PyObject *py_function, - gpointer py_user_data) + PyObject *py_user_data) { PyGICClosure *closure; ffi_closure *fficlosure; @@ -631,14 +669,22 @@ _pygi_make_native_closure (GICallableInfo* info, closure = g_slice_new0 (PyGICClosure); closure->info = (GICallableInfo *) g_base_info_ref ( (GIBaseInfo *) info); closure->function = py_function; - closure->user_data = py_user_data ? py_user_data : Py_None; + closure->user_data = py_user_data; + closure->cache = cache; Py_INCREF (py_function); - Py_INCREF (closure->user_data); + Py_XINCREF (closure->user_data); +#if GI_CHECK_VERSION (1, 72, 0) + fficlosure = + g_callable_info_create_closure (info, &closure->cif, _pygi_closure_handle, + closure); +#else fficlosure = g_callable_info_prepare_closure (info, &closure->cif, _pygi_closure_handle, closure); +#endif + closure->closure = fficlosure; /* Give the closure the information it needs to determine when @@ -647,3 +693,285 @@ _pygi_make_native_closure (GICallableInfo* info, return closure; } + +/* _pygi_destroy_notify_dummy: + * + * Dummy method used in the occasion when a method has a GDestroyNotify + * argument without user data. + */ +static void +_pygi_destroy_notify_dummy (gpointer data) { +} + +static gboolean +_pygi_marshal_from_py_interface_callback (PyGIInvokeState *state, + PyGICallableCache *callable_cache, + PyGIArgCache *arg_cache, + PyObject *py_arg, + GIArgument *arg, + gpointer *cleanup_data) +{ + GICallableInfo *callable_info; + PyGICClosure *closure; + PyGIArgCache *user_data_cache = NULL; + PyGIArgCache *destroy_cache = NULL; + PyGICallbackCache *callback_cache; + PyObject *py_user_data = NULL; + + callback_cache = (PyGICallbackCache *)arg_cache; + + if (callback_cache->user_data_index > 0) { + user_data_cache = _pygi_callable_cache_get_arg (callable_cache, (guint)callback_cache->user_data_index); + if (user_data_cache->py_arg_index < state->n_py_in_args) { + /* py_user_data is a borrowed reference. */ + py_user_data = PyTuple_GetItem (state->py_in_args, user_data_cache->py_arg_index); + if (!py_user_data) + return FALSE; + /* NULL out user_data if it was not supplied and the default arg placeholder + * was used instead. + */ + if (py_user_data == _PyGIDefaultArgPlaceholder) { + py_user_data = NULL; + } else if (callable_cache->user_data_varargs_index < 0) { + /* For non-variable length user data, place the user data in a + * single item tuple which is concatenated to the callbacks arguments. + * This allows callback input arg marshaling to always expect a + * tuple for user data. Note the + */ + py_user_data = Py_BuildValue("(O)", py_user_data, NULL); + } else { + /* increment the ref borrowed from PyTuple_GetItem above */ + Py_INCREF (py_user_data); + } + } + } + + if (py_arg == Py_None) { + return TRUE; + } + + if (!PyCallable_Check (py_arg)) { + PyErr_Format (PyExc_TypeError, + "Callback needs to be a function or method not %s", + Py_TYPE (py_arg)->tp_name); + + return FALSE; + } + + callable_info = (GICallableInfo *)callback_cache->interface_info; + + closure = _pygi_make_native_closure ( + callable_info, callback_cache->closure_cache, callback_cache->scope, + py_arg, py_user_data); + +#if GI_CHECK_VERSION (1, 72, 0) + if (closure->closure != NULL) + arg->v_pointer = g_callable_info_get_closure_native_address (callable_info, closure->closure); + else + arg->v_pointer = NULL; +#else + arg->v_pointer = closure->closure; +#endif + + /* always decref the user data as _pygi_make_native_closure adds its own ref */ + Py_XDECREF (py_user_data); + + /* The PyGICClosure instance is used as user data passed into the C function. + * The return trip to python will marshal this back and pull the python user data out. + */ + if (user_data_cache != NULL) { + state->args[user_data_cache->c_arg_index].arg_value.v_pointer = closure; + } + + /* Setup a GDestroyNotify callback if this method supports it along with + * a user data field. The user data field is a requirement in order + * free resources and ref counts associated with this arguments closure. + * In case a user data field is not available, show a warning giving + * explicit information and setup a dummy notification to avoid a crash + * later on in _pygi_destroy_notify_callback_closure. + */ + if (callback_cache->destroy_notify_index > 0) { + destroy_cache = _pygi_callable_cache_get_arg (callable_cache, (guint)callback_cache->destroy_notify_index); + } + + if (destroy_cache) { + if (user_data_cache != NULL) { + state->args[destroy_cache->c_arg_index].arg_value.v_pointer = _pygi_invoke_closure_free; + } else { + char *full_name = pygi_callable_cache_get_full_name (callable_cache); + gchar *msg = g_strdup_printf("Callables passed to %s will leak references because " + "the method does not support a user_data argument. " + "See: https://bugzilla.gnome.org/show_bug.cgi?id=685598", + full_name); + g_free (full_name); + if (PyErr_WarnEx(PyExc_RuntimeWarning, msg, 2)) { + g_free(msg); + _pygi_invoke_closure_free(closure); + return FALSE; + } + g_free(msg); + state->args[destroy_cache->c_arg_index].arg_value.v_pointer = _pygi_destroy_notify_dummy; + } + } + + /* Use the PyGIClosure as data passed to cleanup for GI_SCOPE_TYPE_CALL. */ + *cleanup_data = closure; + + return TRUE; +} + +static PyObject * +_pygi_marshal_to_py_interface_callback (PyGIInvokeState *state, + PyGICallableCache *callable_cache, + PyGIArgCache *arg_cache, + GIArgument *arg, + gpointer *arg_cleanup_data) +{ + PyGICallbackCache *callback_cache = (PyGICallbackCache *) arg_cache; + gssize user_data_index; + gssize destroy_notify_index; + gpointer user_data = NULL; + GDestroyNotify destroy_notify = NULL; + + user_data_index = callback_cache->user_data_index; + destroy_notify_index = callback_cache->destroy_notify_index; + + if (user_data_index != -1) + user_data = state->args[user_data_index].arg_value.v_pointer; + + if (destroy_notify_index != -1) + destroy_notify = state->args[destroy_notify_index].arg_value.v_pointer; + + return _pygi_ccallback_new (arg->v_pointer, + user_data, + callback_cache->scope, + (GIFunctionInfo *) callback_cache->interface_info, + destroy_notify); +} + +static void +_callback_cache_free_func (PyGICallbackCache *cache) +{ + if (cache != NULL) { + if (cache->interface_info != NULL) + g_base_info_unref ( (GIBaseInfo *)cache->interface_info); + + if (cache->closure_cache != NULL) { + pygi_callable_cache_free ((PyGICallableCache *) cache->closure_cache); + cache->closure_cache = NULL; + } + + g_slice_free (PyGICallbackCache, cache); + } +} + +static void +_pygi_marshal_cleanup_from_py_interface_callback (PyGIInvokeState *state, + PyGIArgCache *arg_cache, + PyObject *py_arg, + gpointer data, + gboolean was_processed) +{ + PyGICallbackCache *callback_cache = (PyGICallbackCache *)arg_cache; + + if (was_processed && callback_cache->scope == GI_SCOPE_TYPE_CALL) { + _pygi_invoke_closure_free (data); + } +} + +static gboolean +pygi_arg_callback_setup_from_info (PyGICallbackCache *arg_cache, + GITypeInfo *type_info, + GIArgInfo *arg_info, /* may be null */ + GITransfer transfer, + PyGIDirection direction, + GIInterfaceInfo *iface_info, + PyGICallableCache *callable_cache) +{ + PyGIArgCache *cache = (PyGIArgCache *)arg_cache; + gssize child_offset = 0; + + if (!pygi_arg_base_setup ((PyGIArgCache *)arg_cache, + type_info, + arg_info, + transfer, + direction)) { + return FALSE; + } + + if (callable_cache != NULL) + child_offset = callable_cache->args_offset; + + ( (PyGIArgCache *)arg_cache)->destroy_notify = (GDestroyNotify)_callback_cache_free_func; + + arg_cache->user_data_index = g_arg_info_get_closure (arg_info); + if (arg_cache->user_data_index != -1) + arg_cache->user_data_index += child_offset; + + arg_cache->destroy_notify_index = g_arg_info_get_destroy (arg_info); + if (arg_cache->destroy_notify_index != -1) + arg_cache->destroy_notify_index += child_offset; + + if (arg_cache->user_data_index >= 0) { + PyGIArgCache *user_data_arg_cache = pygi_arg_cache_alloc (); + user_data_arg_cache->meta_type = PYGI_META_ARG_TYPE_CHILD_WITH_PYARG; + user_data_arg_cache->direction = direction; + user_data_arg_cache->has_default = TRUE; /* always allow user data with a NULL default. */ + _pygi_callable_cache_set_arg (callable_cache, (guint)arg_cache->user_data_index, + user_data_arg_cache); + } + + if (arg_cache->destroy_notify_index >= 0) { + PyGIArgCache *destroy_arg_cache = pygi_arg_cache_alloc (); + destroy_arg_cache->meta_type = PYGI_META_ARG_TYPE_CHILD; + destroy_arg_cache->direction = direction; + _pygi_callable_cache_set_arg (callable_cache, (guint)arg_cache->destroy_notify_index, + destroy_arg_cache); + } + + arg_cache->scope = g_arg_info_get_scope (arg_info); + g_base_info_ref( (GIBaseInfo *)iface_info); + arg_cache->interface_info = iface_info; + + if (direction & PYGI_DIRECTION_FROM_PYTHON) { + arg_cache->closure_cache = pygi_closure_cache_new (arg_cache->interface_info); + cache->from_py_marshaller = _pygi_marshal_from_py_interface_callback; + cache->from_py_cleanup = _pygi_marshal_cleanup_from_py_interface_callback; + } + + if (direction & PYGI_DIRECTION_TO_PYTHON) { + cache->to_py_marshaller = _pygi_marshal_to_py_interface_callback; + } + + return TRUE; +} + +PyGIArgCache * +pygi_arg_callback_new_from_info (GITypeInfo *type_info, + GIArgInfo *arg_info, /* may be null */ + GITransfer transfer, + PyGIDirection direction, + GIInterfaceInfo *iface_info, + PyGICallableCache *callable_cache) +{ + gboolean res = FALSE; + PyGICallbackCache *callback_cache; + + callback_cache = g_slice_new0 (PyGICallbackCache); + if (callback_cache == NULL) + return NULL; + + res = pygi_arg_callback_setup_from_info (callback_cache, + type_info, + arg_info, + transfer, + direction, + iface_info, + callable_cache); + if (res) { + return (PyGIArgCache *)callback_cache; + } else { + pygi_arg_cache_free ((PyGIArgCache *)callback_cache); + return NULL; + } +} |