diff options
Diffstat (limited to 'gi/pygi-invoke.c')
-rw-r--r-- | gi/pygi-invoke.c | 241 |
1 files changed, 134 insertions, 107 deletions
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); } |