diff options
-rw-r--r-- | gi/pygi-array.c | 26 | ||||
-rw-r--r-- | gi/pygi-cache.c | 61 | ||||
-rw-r--r-- | gi/pygi-cache.h | 10 | ||||
-rw-r--r-- | gi/pygi-ccallback.c | 3 | ||||
-rw-r--r-- | gi/pygi-closure.c | 6 | ||||
-rw-r--r-- | gi/pygi-invoke-state-struct.h | 42 | ||||
-rw-r--r-- | gi/pygi-invoke.c | 241 | ||||
-rw-r--r-- | gi/pygi-invoke.h | 2 | ||||
-rw-r--r-- | gi/pygi-marshal-cleanup.c | 4 |
9 files changed, 230 insertions, 165 deletions
diff --git a/gi/pygi-array.c b/gi/pygi-array.c index a50c1815..c17ace0e 100644 --- a/gi/pygi-array.c +++ b/gi/pygi-array.c @@ -370,24 +370,10 @@ array_success: PyGIArgCache *child_cache = _pygi_callable_cache_get_arg (callable_cache, array_cache->len_arg_index); - if (child_cache->direction == PYGI_DIRECTION_BIDIRECTIONAL) { - gint *len_arg = (gint *)state->in_args[child_cache->c_arg_index].v_pointer; - /* if we are not setup yet just set the in arg */ - if (len_arg == NULL) { - if (!gi_argument_from_py_ssize_t (&state->in_args[child_cache->c_arg_index], - length, - child_cache->type_tag)) { - goto err; - } - } else { - *len_arg = length; - } - } else { - if (!gi_argument_from_py_ssize_t (&state->in_args[child_cache->c_arg_index], - length, - child_cache->type_tag)) { - goto err; - } + if (!gi_argument_from_py_ssize_t (&state->arg_values[child_cache->c_arg_index], + length, + child_cache->type_tag)) { + goto err; } } @@ -528,7 +514,7 @@ _pygi_marshal_to_py_array (PyGIInvokeState *state, len = g_strv_length ((gchar **)arg->v_pointer); } } else { - GIArgument *len_arg = state->args[array_cache->len_arg_index]; + GIArgument *len_arg = &state->arg_values[array_cache->len_arg_index]; PyGIArgCache *arg_cache = _pygi_callable_cache_get_arg (callable_cache, array_cache->len_arg_index); @@ -684,7 +670,7 @@ _wrap_c_array (PyGIInvokeState *state, } else if (array_cache->is_zero_terminated) { len = g_strv_length ((gchar **)data); } else if (array_cache->len_arg_index >= 0) { - GIArgument *len_arg = state->args[array_cache->len_arg_index]; + GIArgument *len_arg = &state->arg_values[array_cache->len_arg_index]; len = len_arg->v_long; } diff --git a/gi/pygi-cache.c b/gi/pygi-cache.c index abf8e100..c29733f7 100644 --- a/gi/pygi-cache.c +++ b/gi/pygi-cache.c @@ -22,6 +22,7 @@ #include <girepository.h> +#include "pyglib.h" #include "pygi-info.h" #include "pygi-cache.h" #include "pygi-marshal-cleanup.h" @@ -132,6 +133,7 @@ pygi_callable_cache_free (PyGICallableCache *cache) if (cache->return_cache != NULL) pygi_arg_cache_free (cache->return_cache); + g_function_invoker_destroy (&cache->invoker); g_slice_free (PyGICallableCache, cache); } @@ -530,7 +532,6 @@ _args_cache_generate (GICallableInfo *callable_info, _pygi_callable_cache_set_arg (callable_cache, arg_index, instance_cache); arg_index++; - callable_cache->n_from_py_args++; callable_cache->n_py_args++; } @@ -552,8 +553,6 @@ _args_cache_generate (GICallableInfo *callable_info, arg_cache->meta_type = PYGI_META_ARG_TYPE_CLOSURE; arg_cache->c_arg_index = i; - callable_cache->n_from_py_args++; - } else { GITypeInfo *type_info; @@ -567,16 +566,15 @@ _args_cache_generate (GICallableInfo *callable_info, */ arg_cache = _pygi_callable_cache_get_arg (callable_cache, arg_index); if (arg_cache != NULL) { + /* ensure c_arg_index always aligns with callable_cache->args_cache + * and all of the various PyGIInvokeState arrays. */ + arg_cache->c_arg_index = arg_index; + if (arg_cache->meta_type == PYGI_META_ARG_TYPE_CHILD_WITH_PYARG) { arg_cache->py_arg_index = callable_cache->n_py_args; callable_cache->n_py_args++; } - if (direction & PYGI_DIRECTION_FROM_PYTHON) { - arg_cache->c_arg_index = callable_cache->n_from_py_args; - callable_cache->n_from_py_args++; - } - if (direction & PYGI_DIRECTION_TO_PYTHON) { callable_cache->n_to_py_args++; } @@ -590,7 +588,6 @@ _args_cache_generate (GICallableInfo *callable_info, if (direction & PYGI_DIRECTION_FROM_PYTHON) { py_arg_index = callable_cache->n_py_args; - callable_cache->n_from_py_args++; callable_cache->n_py_args++; } @@ -686,8 +683,47 @@ _args_cache_generate (GICallableInfo *callable_info, return TRUE; } +static gboolean +_setup_invoker (GICallableInfo *callable_info, + GIInfoType info_type, + GIFunctionInvoker *invoker, + GCallback function_ptr) +{ + GError *error = NULL; + + if (info_type == GI_INFO_TYPE_FUNCTION) { + if (g_function_info_prep_invoker ((GIFunctionInfo *)callable_info, + invoker, + &error)) { + return TRUE; + } + if (!pyglib_error_check (&error)) { + PyErr_Format (PyExc_RuntimeError, + "unknown error creating invoker for %s", + g_base_info_get_name ((GIBaseInfo *)callable_info)); + } + return FALSE; + + } else { + if (!g_function_invoker_new_for_address (function_ptr, + (GIFunctionInfo *)callable_info, + invoker, + &error)) { + if (!pyglib_error_check (&error)) { + PyErr_Format (PyExc_RuntimeError, + "unknown error creating invoker for %s", + g_base_info_get_name ((GIBaseInfo *)callable_info)); + } + return FALSE; + } + } + return TRUE; +} + PyGICallableCache * -pygi_callable_cache_new (GICallableInfo *callable_info, gboolean is_ccallback) +pygi_callable_cache_new (GICallableInfo *callable_info, + GCallback function_ptr, + gboolean is_ccallback) { gint n_args; PyGICallableCache *cache; @@ -699,6 +735,7 @@ pygi_callable_cache_new (GICallableInfo *callable_info, gboolean is_ccallback) return NULL; cache->name = g_base_info_get_name ((GIBaseInfo *)callable_info); + cache->throws = g_callable_info_can_throw_gerror ((GIBaseInfo *)callable_info); if (g_base_info_is_deprecated (callable_info)) { const gchar *deprecated = g_base_info_get_attribute (callable_info, "deprecated"); @@ -749,6 +786,10 @@ pygi_callable_cache_new (GICallableInfo *callable_info, gboolean is_ccallback) if (!_args_cache_generate (callable_info, cache)) goto err; + if (!_setup_invoker (callable_info, type, &cache->invoker, function_ptr)) { + goto err; + } + return cache; err: pygi_callable_cache_free (cache); diff --git a/gi/pygi-cache.h b/gi/pygi-cache.h index 5521605c..407e38ce 100644 --- a/gi/pygi-cache.h +++ b/gi/pygi-cache.h @@ -25,6 +25,7 @@ #include <Python.h> #include <girepository.h> +#include <girffi.h> #include "pygi-invoke-state-struct.h" @@ -167,14 +168,11 @@ struct _PyGICallableCache GSList *to_py_args; GSList *arg_name_list; /* for keyword arg matching */ GHashTable *arg_name_hash; + gboolean throws; /* Index of user_data arg that can eat variable args passed to a callable. */ gssize user_data_varargs_index; - /* Number of in args passed to g_function_info_invoke. - * This is used for the length of PyGIInvokeState.in_args */ - gssize n_from_py_args; - /* Number of out args passed to g_function_info_invoke. * This is used for the length of PyGIInvokeState.out_values */ gssize n_to_py_args; @@ -191,6 +189,9 @@ struct _PyGICallableCache /* Minimum number of args required to call the callable from Python. * This count does not include args with defaults. */ gssize n_py_required_args; + + /* An invoker with ffi_cif already setup */ + GIFunctionInvoker invoker; }; gboolean @@ -243,6 +244,7 @@ pygi_callable_cache_free (PyGICallableCache *cache); PyGICallableCache * pygi_callable_cache_new (GICallableInfo *callable_info, + GCallback function_ptr, gboolean is_ccallback); #define _pygi_callable_cache_args_len(cache) ((cache)->args_cache)->len diff --git a/gi/pygi-ccallback.c b/gi/pygi-ccallback.c index 01e109b3..2a6c520b 100644 --- a/gi/pygi-ccallback.c +++ b/gi/pygi-ccallback.c @@ -34,7 +34,7 @@ _ccallback_call(PyGICCallback *self, PyObject *args, PyObject *kwargs) PyObject *result; if (self->cache == NULL) { - self->cache = pygi_callable_cache_new (self->info, TRUE); + self->cache = pygi_callable_cache_new (self->info, self->callback, TRUE); if (self->cache == NULL) return NULL; } @@ -43,7 +43,6 @@ _ccallback_call(PyGICCallback *self, PyObject *args, PyObject *kwargs) args, kwargs, self->cache, - self->callback, self->user_data); return result; } diff --git a/gi/pygi-closure.c b/gi/pygi-closure.c index a30363fb..b803541a 100644 --- a/gi/pygi-closure.c +++ b/gi/pygi-closure.c @@ -800,7 +800,7 @@ _pygi_marshal_from_py_interface_callback (PyGIInvokeState *state, * The return trip to python will marshal this back and pull the python user data out. */ if (user_data_cache != NULL) { - state->in_args[user_data_cache->c_arg_index].v_pointer = closure; + state->arg_values[user_data_cache->c_arg_index].v_pointer = closure; } /* Setup a GDestroyNotify callback if this method supports it along with @@ -817,7 +817,7 @@ _pygi_marshal_from_py_interface_callback (PyGIInvokeState *state, if (destroy_cache) { if (user_data_cache != NULL) { PyGICClosure *destroy_notify = _pygi_destroy_notify_create (); - state->in_args[destroy_cache->c_arg_index].v_pointer = destroy_notify->closure; + state->arg_values[destroy_cache->c_arg_index].v_pointer = destroy_notify->closure; } else { gchar *msg = g_strdup_printf("Callables passed to %s will leak references because " "the method does not support a user_data argument. " @@ -829,7 +829,7 @@ _pygi_marshal_from_py_interface_callback (PyGIInvokeState *state, return FALSE; } g_free(msg); - state->in_args[destroy_cache->c_arg_index].v_pointer = _pygi_destroy_notify_dummy; + state->arg_values[destroy_cache->c_arg_index].v_pointer = _pygi_destroy_notify_dummy; } } diff --git a/gi/pygi-invoke-state-struct.h b/gi/pygi-invoke-state-struct.h index 139b8786..174f473a 100644 --- a/gi/pygi-invoke-state-struct.h +++ b/gi/pygi-invoke-state-struct.h @@ -11,39 +11,49 @@ typedef struct _PyGIInvokeState { PyObject *py_in_args; gssize n_py_in_args; - gssize current_arg; GType implementor_gtype; + /* Number of arguments the ffi wrapped C function takes. Used as the exact + * count for argument related arrays held in this struct. + */ + gssize n_args; + + /* List of arguments passed to ffi. Elements can point directly to values held in + * arg_values for "in/from Python" or indirectly via arg_pointers for + * "out/inout/to Python". In the latter case, the arg_pointers[x]->v_pointer + * member points to memory for the value storage. + */ GIArgument **args; - GIArgument *in_args; + + /* Holds memory for the C value of arguments marshaled "to" or "from" Python. */ + GIArgument *arg_values; + + /* Holds pointers to values in arg_values or a caller allocated chunk of + * memory via arg_pointers[x].v_pointer. + */ + GIArgument *arg_pointers; /* Array of pointers allocated to the same length as args which holds from_py * marshaler cleanup data. */ gpointer *args_cleanup_data; - /* Out args and out values - * In order to pass a parameter and get something back out in C - * we need to pass a pointer to the value, e.g. - * int *out_integer; - * - * so while out_args == out_integer, out_value == *out_integer - * or in other words out_args = &out_values - * - * We do all of our processing on out_values but we pass out_args to - * the actual function. - */ - GIArgument *out_args; - GIArgument *out_values; - + /* Memory to receive the result of the C ffi function call. */ GIArgument return_arg; + /* A GError exception which is indirectly bound into the last position of + * the "args" array if the callable caches "throws" member is set. + */ GError *error; gboolean failed; gpointer user_data; + + /* Function pointer to call with ffi. */ + gpointer function_ptr; + } PyGIInvokeState; G_END_DECLS diff --git a/gi/pygi-invoke.c b/gi/pygi-invoke.c index 1d89912e..3bb4dc6c 100644 --- a/gi/pygi-invoke.c +++ b/gi/pygi-invoke.c @@ -29,61 +29,24 @@ static inline gboolean _invoke_callable (PyGIInvokeState *state, PyGICallableCache *cache, - GICallableInfo *callable_info, - GCallback function_ptr) + GICallableInfo *callable_info) { - GError *error; - gint retval; - - error = NULL; + GIFFIReturnValue ffi_return_value = {0}; Py_BEGIN_ALLOW_THREADS; - /* FIXME: use this for now but we can streamline the calls */ - if (cache->function_type == PYGI_FUNCTION_TYPE_VFUNC) - retval = g_vfunc_info_invoke ( callable_info, - state->implementor_gtype, - state->in_args, - cache->n_from_py_args, - state->out_args, - cache->n_to_py_args, - &state->return_arg, - &error); - else if (g_base_info_get_type (callable_info) == GI_INFO_TYPE_CALLBACK) - retval = g_callable_info_invoke (callable_info, - function_ptr, - state->in_args, - cache->n_from_py_args, - state->out_args, - cache->n_to_py_args, - &state->return_arg, - FALSE, - FALSE, - &error); - else - retval = g_function_info_invoke ( callable_info, - state->in_args, - cache->n_from_py_args, - state->out_args, - cache->n_to_py_args, - &state->return_arg, - &error); - Py_END_ALLOW_THREADS; - - if (!retval) { - g_assert (error != NULL); - pyglib_error_check (&error); - - /* It is unclear if the error occured before or after the C - * function was invoked so for now assume success - * We eventually should marshal directly to FFI so we no longer - * have to use the reference implementation - */ - pygi_marshal_cleanup_args_from_py_marshal_success (state, cache); + ffi_call (&cache->invoker.cif, + state->function_ptr, + (void *)&ffi_return_value, + (void **)state->args); - return FALSE; - } + Py_END_ALLOW_THREADS; + /* If the callable throws, the address of state->error will be bound into + * the state->args as the last value. When the callee sets an error using + * the state->args passed, it will have the side effect of setting + * state->error allowing for easy checking here. + */ if (state->error != NULL) { if (pyglib_error_check (&(state->error))) { /* even though we errored out, the call itself was successful, @@ -93,6 +56,12 @@ _invoke_callable (PyGIInvokeState *state, } } + if (cache->return_cache) { + gi_type_info_extract_ffi_return_value (cache->return_cache->type_info, + &ffi_return_value, + &state->return_arg); + } + return TRUE; } @@ -280,13 +249,24 @@ _py_args_combine_and_check_length (PyGICallableCache *cache, } static inline gboolean -_invoke_state_init_from_callable_cache (PyGIInvokeState *state, +_invoke_state_init_from_callable_cache (GIBaseInfo *info, + PyGIInvokeState *state, PyGICallableCache *cache, PyObject *py_args, PyObject *kwargs) { PyObject *combined_args = NULL; state->implementor_gtype = 0; + state->n_args = _pygi_callable_cache_args_len (cache); + + if (cache->throws) { + state->n_args++; + } + + /* Copy the function pointer to the state for the normal case. For vfuncs, + * this will be filled out based on the implementor_gtype calculated below. + */ + state->function_ptr = cache->invoker.native_address; /* TODO: We don't use the class parameter sent in by the structure * so we remove it from the py_args tuple but we can keep it @@ -308,6 +288,8 @@ _invoke_state_init_from_callable_cache (PyGIInvokeState *state, } } else if (cache->function_type == PYGI_FUNCTION_TYPE_VFUNC) { PyObject *py_gtype; + GError *error = NULL; + py_gtype = PyTuple_GetItem (py_args, 0); if (py_gtype == NULL) { PyErr_SetString (PyExc_TypeError, @@ -319,6 +301,18 @@ _invoke_state_init_from_callable_cache (PyGIInvokeState *state, if (state->implementor_gtype == 0) return FALSE; + + /* vfunc addresses are pulled into the state at call time and cannot be + * cached because the call site can specify a different portion of the + * class hierarchy. e.g. Object.do_func vs. SubObject.do_func might + * retrieve a different vfunc address but GI gives us the same vfunc info. + */ + state->function_ptr = g_vfunc_info_get_address ((GIVFuncInfo *)info, + state->implementor_gtype, + &error); + if (pyglib_error_check (&error)) { + return FALSE; + } } if (cache->function_type == PYGI_FUNCTION_TYPE_CONSTRUCTOR || @@ -344,68 +338,65 @@ _invoke_state_init_from_callable_cache (PyGIInvokeState *state, } state->n_py_in_args = PyTuple_Size (state->py_in_args); - state->args = g_slice_alloc0 (_pygi_callable_cache_args_len (cache) * sizeof (GIArgument *)); - if (state->args == NULL && _pygi_callable_cache_args_len (cache) != 0) { + state->args = g_slice_alloc0 (state->n_args * sizeof (GIArgument *)); + if (state->args == NULL && state->n_args != 0) { PyErr_NoMemory(); return FALSE; } - state->args_cleanup_data = g_slice_alloc0 (_pygi_callable_cache_args_len (cache) * sizeof (gpointer)); - if (state->args_cleanup_data == NULL && _pygi_callable_cache_args_len (cache) != 0) { + state->args_cleanup_data = g_slice_alloc0 (state->n_args * sizeof (gpointer)); + if (state->args_cleanup_data == NULL && state->n_args != 0) { PyErr_NoMemory(); return FALSE; } - state->in_args = g_slice_alloc0 (cache->n_from_py_args * sizeof(GIArgument)); - if (state->in_args == NULL && cache->n_from_py_args != 0) { + state->arg_values = g_slice_alloc0 (state->n_args * sizeof(GIArgument)); + if (state->arg_values == NULL && state->n_args != 0) { PyErr_NoMemory (); return FALSE; } - state->out_values = g_slice_alloc0 (cache->n_to_py_args * sizeof(GIArgument)); - if (state->out_values == NULL && cache->n_to_py_args != 0) { - PyErr_NoMemory (); - return FALSE; - } - - state->out_args = g_slice_alloc0 (cache->n_to_py_args * sizeof(GIArgument)); - if (state->out_args == NULL && cache->n_to_py_args != 0) { + state->arg_pointers = g_slice_alloc0 (state->n_args * sizeof(GIArgument)); + if (state->arg_pointers == NULL && state->n_args != 0) { PyErr_NoMemory (); return FALSE; } state->error = NULL; + if (cache->throws) { + gssize error_index = state->n_args - 1; + /* The ffi argument for GError needs to be a triple pointer. */ + state->arg_pointers[error_index].v_pointer = &state->error; + state->args[error_index] = &(state->arg_pointers[error_index]); + } + return TRUE; } static inline void _invoke_state_clear (PyGIInvokeState *state, PyGICallableCache *cache) { - g_slice_free1 (_pygi_callable_cache_args_len (cache) * sizeof(GIArgument *), state->args); - g_slice_free1 (_pygi_callable_cache_args_len (cache) * sizeof(gpointer), state->args_cleanup_data); - g_slice_free1 (cache->n_from_py_args * sizeof(GIArgument), state->in_args); - g_slice_free1 (cache->n_to_py_args * sizeof(GIArgument), state->out_args); - g_slice_free1 (cache->n_to_py_args * sizeof(GIArgument), state->out_values); + g_slice_free1 (state->n_args * sizeof(GIArgument *), state->args); + g_slice_free1 (state->n_args * sizeof(gpointer), state->args_cleanup_data); + g_slice_free1 (state->n_args * sizeof(GIArgument), state->arg_values); + g_slice_free1 (state->n_args * sizeof(GIArgument), state->arg_pointers); Py_XDECREF (state->py_in_args); } -static gboolean _caller_alloc (PyGIInvokeState *state, - PyGIArgCache *arg_cache, - gssize arg_count, - gssize out_count) +static gboolean +_caller_alloc (PyGIArgCache *arg_cache, GIArgument *arg) { if (arg_cache->type_tag == GI_TYPE_TAG_INTERFACE) { PyGIInterfaceCache *iface_cache = (PyGIInterfaceCache *)arg_cache; - state->out_args[out_count].v_pointer = NULL; - state->args[arg_count] = &state->out_args[out_count]; + arg->v_pointer = NULL; if (g_type_is_a (iface_cache->g_type, G_TYPE_BOXED)) { - state->args[arg_count]->v_pointer = + arg->v_pointer = _pygi_boxed_alloc (iface_cache->interface_info, NULL); } else if (iface_cache->g_type == G_TYPE_VALUE) { - state->args[arg_count]->v_pointer = g_slice_new0 (GValue); + arg->v_pointer = g_slice_new0 (GValue); } else if (iface_cache->is_foreign) { PyObject *foreign_struct = pygi_struct_foreign_convert_from_g_argument ( @@ -415,34 +406,59 @@ static gboolean _caller_alloc (PyGIInvokeState *state, pygi_struct_foreign_convert_to_g_argument (foreign_struct, iface_cache->interface_info, GI_TRANSFER_EVERYTHING, - state->args[arg_count]); + arg); } else { gssize size = g_struct_info_get_size( (GIStructInfo *)iface_cache->interface_info); - state->args[arg_count]->v_pointer = g_malloc0 (size); + arg->v_pointer = g_malloc0 (size); } } else if (arg_cache->type_tag == GI_TYPE_TAG_ARRAY) { PyGIArgGArray *array_cache = (PyGIArgGArray *)arg_cache; - state->out_args[out_count].v_pointer = g_array_new (TRUE, TRUE, array_cache->item_size); - state->args[arg_count] = &state->out_args[out_count]; + arg->v_pointer = g_array_new (TRUE, TRUE, array_cache->item_size); } else { return FALSE; } - if (state->args[arg_count]->v_pointer == NULL) + if (arg->v_pointer == NULL) return FALSE; return TRUE; } +/* _invoke_marshal_in_args: + * + * Fills out the state struct argument lists. arg_values will always hold + * actual values marshaled either to or from Python and C. arg_pointers will + * hold pointers (via v_pointer) to auxilary value storage. This will normally + * point to values stored in arg_values. In the case of caller allocated + * out args, arg_pointers[x].v_pointer will point to newly allocated memory. + * arg_pointers inserts a level of pointer indirection between arg_values + * and the argument list ffi receives when dealing with non-caller allocated + * out arguments. + * + * For example: + * [[ + * void callee (int *i, int j) { *i = 50 - j; } + * void caller () { + * int i = 0; + * callee (&i, 8); + * } + * + * args[0] == &arg_pointers[0]; + * arg_pointers[0].v_pointer == &arg_values[0]; + * arg_values[0].v_int == 42; + * + * args[1] == &arg_values[1]; + * arg_values[1].v_int == 8; + * ]] + * + */ static inline gboolean _invoke_marshal_in_args (PyGIInvokeState *state, PyGICallableCache *cache) { - gssize i, in_count, out_count; - in_count = 0; - out_count = 0; + gssize i; if (state->n_py_in_args > cache->n_py_args) { PyErr_Format (PyExc_TypeError, @@ -454,14 +470,14 @@ _invoke_marshal_in_args (PyGIInvokeState *state, PyGICallableCache *cache) } for (i = 0; i < _pygi_callable_cache_args_len (cache); i++) { - GIArgument *c_arg; + GIArgument *c_arg = &state->arg_values[i]; PyGIArgCache *arg_cache = g_ptr_array_index (cache->args_cache, i); PyObject *py_arg = NULL; switch (arg_cache->direction) { case PYGI_DIRECTION_FROM_PYTHON: - state->args[i] = &(state->in_args[in_count]); - in_count++; + /* The ffi argument points directly at memory in arg_values. */ + state->args[i] = c_arg; if (arg_cache->meta_type == PYGI_META_ARG_TYPE_CLOSURE) { state->args[i]->v_pointer = state->user_data; @@ -491,13 +507,6 @@ _invoke_marshal_in_args (PyGIInvokeState *state, PyGICallableCache *cache) break; case PYGI_DIRECTION_BIDIRECTIONAL: - /* this will be filled in if it is an child value */ - if (state->in_args[in_count].v_pointer != NULL) - state->out_values[out_count] = state->in_args[in_count]; - - state->in_args[in_count].v_pointer = &state->out_values[out_count]; - in_count++; - if (arg_cache->meta_type != PYGI_META_ARG_TYPE_CHILD) { if (arg_cache->py_arg_index >= state->n_py_in_args) { PyErr_Format (PyExc_TypeError, @@ -515,9 +524,27 @@ _invoke_marshal_in_args (PyGIInvokeState *state, PyGICallableCache *cache) PyTuple_GET_ITEM (state->py_in_args, arg_cache->py_arg_index); } + /* Fall through */ + case PYGI_DIRECTION_TO_PYTHON: + /* arg_pointers always stores a pointer to the data to be marshaled "to python" + * even in cases where arg_pointers is not being used as indirection between + * ffi and arg_values. This gives a guarantee that out argument marshaling + * (_invoke_marshal_out_args) can always rely on arg_pointers pointing to + * the correct chunk of memory to marshal. + */ + state->arg_pointers[i].v_pointer = c_arg; + if (arg_cache->is_caller_allocates) { - if (!_caller_alloc (state, arg_cache, i, out_count)) { + /* In the case of caller allocated out args, we don't use + * an extra level of indirection and state->args will point + * directly at the data to be marshaled. However, as noted + * above, arg_pointers will also point to this caller allocated + * chunk of memory used by out argument marshaling. + */ + state->args[i] = c_arg; + + if (!_caller_alloc (arg_cache, c_arg)) { PyErr_Format (PyExc_TypeError, "Could not caller allocate argument %zd of callable %s", i, cache->name); @@ -527,14 +554,14 @@ _invoke_marshal_in_args (PyGIInvokeState *state, PyGICallableCache *cache) return FALSE; } } else { - state->out_args[out_count].v_pointer = &state->out_values[out_count]; - state->args[i] = &state->out_values[out_count]; + /* Non-caller allocated out args will use arg_pointers as an + * extra level of indirection */ + state->args[i] = &state->arg_pointers[i]; } - out_count++; + break; } - c_arg = state->args[i]; if (py_arg == _PyGIDefaultArgPlaceholder) { *c_arg = arg_cache->default_value; } else if (arg_cache->from_py_marshaller != NULL) { @@ -642,7 +669,7 @@ _invoke_marshal_out_args (PyGIInvokeState *state, PyGICallableCache *cache) py_out = arg_cache->to_py_marshaller (state, cache, arg_cache, - state->args[arg_cache->c_arg_index]); + state->arg_pointers[arg_cache->c_arg_index].v_pointer); if (py_out == NULL) { pygi_marshal_cleanup_args_to_py_parameter_fail (state, cache, @@ -665,7 +692,7 @@ _invoke_marshal_out_args (PyGIInvokeState *state, PyGICallableCache *cache) PyObject *py_obj = arg_cache->to_py_marshaller (state, cache, arg_cache, - state->args[arg_cache->c_arg_index]); + state->arg_pointers[arg_cache->c_arg_index].v_pointer); if (py_obj == NULL) { if (has_return) @@ -688,12 +715,12 @@ _invoke_marshal_out_args (PyGIInvokeState *state, PyGICallableCache *cache) PyObject * pygi_callable_info_invoke (GIBaseInfo *info, PyObject *py_args, PyObject *kwargs, PyGICallableCache *cache, - GCallback function_ptr, gpointer user_data) + gpointer user_data) { PyGIInvokeState state = { 0, }; PyObject *ret = NULL; - if (!_invoke_state_init_from_callable_cache (&state, cache, py_args, kwargs)) + if (!_invoke_state_init_from_callable_cache (info, &state, cache, py_args, kwargs)) goto err; if (cache->function_type == PYGI_FUNCTION_TYPE_CCALLBACK) @@ -702,7 +729,7 @@ pygi_callable_info_invoke (GIBaseInfo *info, PyObject *py_args, if (!_invoke_marshal_in_args (&state, cache)) goto err; - if (!_invoke_callable (&state, cache, info, function_ptr)) + if (!_invoke_callable (&state, cache, info)) goto err; ret = _invoke_marshal_out_args (&state, cache); @@ -720,10 +747,10 @@ _wrap_g_callable_info_invoke (PyGIBaseInfo *self, PyObject *py_args, PyObject *kwargs) { if (self->cache == NULL) { - self->cache = pygi_callable_cache_new (self->info, FALSE); + self->cache = pygi_callable_cache_new (self->info, NULL, FALSE); if (self->cache == NULL) return NULL; } - return pygi_callable_info_invoke (self->info, py_args, kwargs, self->cache, NULL, NULL); + return pygi_callable_info_invoke (self->info, py_args, kwargs, self->cache, NULL); } diff --git a/gi/pygi-invoke.h b/gi/pygi-invoke.h index 051bc8d6..a481be3f 100644 --- a/gi/pygi-invoke.h +++ b/gi/pygi-invoke.h @@ -32,7 +32,7 @@ G_BEGIN_DECLS PyObject *pygi_callable_info_invoke (GIBaseInfo *info, PyObject *py_args, PyObject *kwargs, PyGICallableCache *cache, - GCallback function_ptr, gpointer user_data); + gpointer user_data); PyObject *_wrap_g_callable_info_invoke (PyGIBaseInfo *self, PyObject *py_args, PyObject *kwargs); diff --git a/gi/pygi-marshal-cleanup.c b/gi/pygi-marshal-cleanup.c index 3d826016..169e149f 100644 --- a/gi/pygi-marshal-cleanup.c +++ b/gi/pygi-marshal-cleanup.c @@ -136,7 +136,7 @@ pygi_marshal_cleanup_args_to_py_marshal_success (PyGIInvokeState *state, while (cache_item) { PyGIArgCache *arg_cache = (PyGIArgCache *) cache_item->data; PyGIMarshalCleanupFunc cleanup_func = arg_cache->to_py_cleanup; - gpointer data = state->args[arg_cache->c_arg_index]->v_pointer; + gpointer data = state->arg_values[arg_cache->c_arg_index].v_pointer; if (cleanup_func != NULL && data != NULL) cleanup_func (state, @@ -168,7 +168,7 @@ pygi_marshal_cleanup_args_from_py_parameter_fail (PyGIInvokeState *state, for (i = 0; i < _pygi_callable_cache_args_len (cache) && i <= failed_arg_index; i++) { PyGIArgCache *arg_cache = _pygi_callable_cache_get_arg (cache, i); PyGIMarshalCleanupFunc cleanup_func = arg_cache->from_py_cleanup; - gpointer data = state->args[i]->v_pointer; + gpointer data = state->arg_values[i].v_pointer; PyObject *py_arg = PyTuple_GET_ITEM (state->py_in_args, arg_cache->py_arg_index); |