diff options
author | DongHun Kwak <dh0128.kwak@samsung.com> | 2017-07-12 08:45:23 +0900 |
---|---|---|
committer | DongHun Kwak <dh0128.kwak@samsung.com> | 2017-07-12 08:45:27 +0900 |
commit | ee6939d73fd96606a92eee870014c58eafee5e63 (patch) | |
tree | 6a3526fab8e841879fbcab05749fcb4a91a4f580 /gi/pygi-invoke.c | |
parent | 0df45a72dc2a72275a5c49c38b87f73341987192 (diff) | |
download | pygobject2-ee6939d73fd96606a92eee870014c58eafee5e63.tar.gz pygobject2-ee6939d73fd96606a92eee870014c58eafee5e63.tar.bz2 pygobject2-ee6939d73fd96606a92eee870014c58eafee5e63.zip |
Imported Upstream version 3.20.0
Change-Id: I8106882e9a0d7a8fb554f9549e7c2cde111c104b
Signed-off-by: DongHun Kwak <dh0128.kwak@samsung.com>
Diffstat (limited to 'gi/pygi-invoke.c')
-rw-r--r-- | gi/pygi-invoke.c | 725 |
1 files changed, 418 insertions, 307 deletions
diff --git a/gi/pygi-invoke.c b/gi/pygi-invoke.c index 42cdd01..02de46e 100644 --- a/gi/pygi-invoke.c +++ b/gi/pygi-invoke.c @@ -17,75 +17,16 @@ * 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 <pyglib.h> #include "pygi-invoke.h" #include "pygi-marshal-cleanup.h" - -static inline gboolean -_invoke_callable (PyGIInvokeState *state, - PyGICallableCache *cache, - GICallableInfo *callable_info) -{ - GError *error; - gint retval; - - error = NULL; - - pyg_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 - 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); - pyg_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); - - return FALSE; - } - - if (state->error != NULL) { - if (pyglib_error_check (&(state->error))) { - /* even though we errored out, the call itself was successful, - so we assume the call processed all of the parameters */ - pygi_marshal_cleanup_args_from_py_marshal_success (state, cache); - return FALSE; - } - } - - return TRUE; -} +#include "pygi-error.h" static gboolean -_check_for_unexpected_kwargs (const gchar *function_name, +_check_for_unexpected_kwargs (PyGICallableCache *cache, GHashTable *arg_name_hash, PyObject *py_kwargs) { @@ -108,12 +49,18 @@ _check_for_unexpected_kwargs (const gchar *function_name, } } - if (g_hash_table_lookup (arg_name_hash, PyBytes_AsString(key)) == NULL) { + /* Use extended lookup because it returns whether or not the key actually + * exists in the hash table. g_hash_table_lookup returns NULL for keys not + * found which maps to index 0 for our hash lookup. + */ + if (!g_hash_table_lookup_extended (arg_name_hash, PyBytes_AsString(key), NULL, NULL)) { + char *full_name = pygi_callable_cache_get_full_name (cache); PyErr_Format (PyExc_TypeError, "%.200s() got an unexpected keyword argument '%.400s'", - function_name, + full_name, PyBytes_AsString (key)); Py_DECREF (key); + g_free (full_name); return FALSE; } @@ -124,54 +71,59 @@ _check_for_unexpected_kwargs (const gchar *function_name, /** * _py_args_combine_and_check_length: - * @function_name: the name of the function being called. Used for error messages. - * @arg_name_list: a list of the string names for each argument. The length - * of this list is the number of required arguments for the - * function. If an argument has no name, NULL is put in its - * position in the list. - * @py_args: the tuple of positional arguments. A referece is stolen, and this - tuple will be either decreffed or returned as is. + * @cache: PyGICallableCache + * @py_args: the tuple of positional arguments. * @py_kwargs: the dict of keyword arguments to be merged with py_args. - * A reference is borrowed. * - * Returns: The py_args and py_kwargs combined into one tuple. + * Returns: New value reference to the combined py_args and py_kwargs. */ static PyObject * -_py_args_combine_and_check_length (const gchar *function_name, - GSList *arg_name_list, - GHashTable *arg_name_hash, +_py_args_combine_and_check_length (PyGICallableCache *cache, PyObject *py_args, PyObject *py_kwargs) { PyObject *combined_py_args = NULL; Py_ssize_t n_py_args, n_py_kwargs, i; - guint n_expected_args; + guint n_expected_args = cache->n_py_args; GSList *l; n_py_args = PyTuple_GET_SIZE (py_args); - n_py_kwargs = PyDict_Size (py_kwargs); - - n_expected_args = g_slist_length (arg_name_list); + if (py_kwargs == NULL) + n_py_kwargs = 0; + else + n_py_kwargs = PyDict_Size (py_kwargs); - if (n_py_kwargs == 0 && n_py_args == n_expected_args) { + /* Fast path, we already have the exact number of args and not kwargs. */ + if (n_py_kwargs == 0 && n_py_args == n_expected_args && cache->user_data_varargs_index < 0) { + Py_INCREF (py_args); return py_args; } - if (n_expected_args < n_py_args) { + if (cache->user_data_varargs_index < 0 && n_expected_args < n_py_args) { + char *full_name = pygi_callable_cache_get_full_name (cache); PyErr_Format (PyExc_TypeError, "%.200s() takes exactly %d %sargument%s (%zd given)", - function_name, + full_name, n_expected_args, n_py_kwargs > 0 ? "non-keyword " : "", n_expected_args == 1 ? "" : "s", n_py_args); + g_free (full_name); + return NULL; + } - Py_DECREF (py_args); + if (cache->user_data_varargs_index >= 0 && n_py_kwargs > 0 && n_expected_args < n_py_args) { + char *full_name = pygi_callable_cache_get_full_name (cache); + PyErr_Format (PyExc_TypeError, + "%.200s() cannot use variable user data arguments with keyword arguments", + full_name); + g_free (full_name); return NULL; } - if (!_check_for_unexpected_kwargs (function_name, arg_name_hash, py_kwargs)) { - Py_DECREF (py_args); + if (n_py_kwargs > 0 && !_check_for_unexpected_kwargs (cache, + cache->arg_name_hash, + py_kwargs)) { return NULL; } @@ -179,48 +131,84 @@ _py_args_combine_and_check_length (const gchar *function_name, * when they are combined into a single tuple */ combined_py_args = PyTuple_New (n_expected_args); - for (i = 0; i < n_py_args; i++) { - PyObject *item = PyTuple_GET_ITEM (py_args, i); - Py_INCREF (item); - PyTuple_SET_ITEM (combined_py_args, i, item); - } + for (i = 0, l = cache->arg_name_list; i < n_expected_args && l; i++, l = l->next) { + PyObject *py_arg_item = NULL; + PyObject *kw_arg_item = NULL; + const gchar *arg_name = l->data; + int arg_cache_index = -1; + gboolean is_varargs_user_data = FALSE; - Py_CLEAR(py_args); + if (arg_name != NULL) + arg_cache_index = GPOINTER_TO_INT (g_hash_table_lookup (cache->arg_name_hash, arg_name)); - for (i = 0, l = arg_name_list; i < n_expected_args && l; i++, l = l->next) { - PyObject *py_arg_item, *kw_arg_item = NULL; - const gchar *arg_name = l->data; + is_varargs_user_data = cache->user_data_varargs_index >= 0 && + arg_cache_index == cache->user_data_varargs_index; - if (arg_name != NULL) { + if (n_py_kwargs > 0 && arg_name != NULL) { /* NULL means this argument has no keyword name */ /* ex. the first argument to a method or constructor */ kw_arg_item = PyDict_GetItemString (py_kwargs, arg_name); } - py_arg_item = PyTuple_GET_ITEM (combined_py_args, i); - if (kw_arg_item != NULL && py_arg_item == NULL) { - Py_INCREF (kw_arg_item); - PyTuple_SET_ITEM (combined_py_args, i, kw_arg_item); + /* use a bounded retrieval of the original input */ + if (i < n_py_args) + py_arg_item = PyTuple_GET_ITEM (py_args, i); + + if (kw_arg_item == NULL && py_arg_item != NULL) { + if (is_varargs_user_data) { + /* For tail end user_data varargs, pull a slice off and we are done. */ + PyObject *user_data = PyTuple_GetSlice (py_args, i, PY_SSIZE_T_MAX); + PyTuple_SET_ITEM (combined_py_args, i, user_data); + return combined_py_args; + } else { + Py_INCREF (py_arg_item); + PyTuple_SET_ITEM (combined_py_args, i, py_arg_item); + } + } else if (kw_arg_item != NULL && py_arg_item == NULL) { + if (is_varargs_user_data) { + /* Special case where user_data is passed as a keyword argument (user_data=foo) + * Wrap the value in a tuple to represent variable args for marshaling later on. + */ + PyObject *user_data = Py_BuildValue("(O)", kw_arg_item, NULL); + PyTuple_SET_ITEM (combined_py_args, i, user_data); + } else { + Py_INCREF (kw_arg_item); + PyTuple_SET_ITEM (combined_py_args, i, kw_arg_item); + } } else if (kw_arg_item == NULL && py_arg_item == NULL) { - PyErr_Format (PyExc_TypeError, - "%.200s() takes exactly %d %sargument%s (%zd given)", - function_name, - n_expected_args, - n_py_kwargs > 0 ? "non-keyword " : "", - n_expected_args == 1 ? "" : "s", - n_py_args); - - Py_DECREF (combined_py_args); - return NULL; - + if (is_varargs_user_data) { + /* For varargs user_data, pass an empty tuple when nothing is given. */ + PyTuple_SET_ITEM (combined_py_args, i, PyTuple_New (0)); + } else if (arg_cache_index >= 0 && _pygi_callable_cache_get_arg (cache, arg_cache_index)->has_default) { + /* If the argument supports a default, use a place holder in the + * argument tuple, this will be checked later during marshaling. + */ + Py_INCREF (_PyGIDefaultArgPlaceholder); + PyTuple_SET_ITEM (combined_py_args, i, _PyGIDefaultArgPlaceholder); + } else { + char *full_name = pygi_callable_cache_get_full_name (cache); + PyErr_Format (PyExc_TypeError, + "%.200s() takes exactly %d %sargument%s (%zd given)", + full_name, + n_expected_args, + n_py_kwargs > 0 ? "non-keyword " : "", + n_expected_args == 1 ? "" : "s", + n_py_args); + g_free (full_name); + + Py_DECREF (combined_py_args); + return NULL; + } } else if (kw_arg_item != NULL && py_arg_item != NULL) { + char *full_name = pygi_callable_cache_get_full_name (cache); PyErr_Format (PyExc_TypeError, "%.200s() got multiple values for keyword argument '%.200s'", - function_name, + full_name, arg_name); Py_DECREF (combined_py_args); + g_free (full_name); return NULL; } } @@ -228,194 +216,230 @@ _py_args_combine_and_check_length (const gchar *function_name, return combined_py_args; } -static inline gboolean -_invoke_state_init_from_callable_cache (PyGIInvokeState *state, - PyGICallableCache *cache, - PyObject *py_args, - PyObject *kwargs) -{ - state->implementor_gtype = 0; +/* To reduce calls to g_slice_*() we (1) allocate all the memory depended on + * the argument count in one go and (2) keep one version per argument count + * around for faster reuse. + */ - /* 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 - * around if we want to call actual gobject constructors - * in the future instead of calling g_object_new - */ - if (cache->function_type == PYGI_FUNCTION_TYPE_CONSTRUCTOR) { - PyObject *constructor_class; - constructor_class = PyTuple_GetItem (py_args, 0); +#define PyGI_INVOKE_ARG_STATE_SIZE(n) (n * (sizeof (PyGIInvokeArgState) + sizeof (GIArgument *))) +#define PyGI_INVOKE_ARG_STATE_N_MAX 10 +static gpointer free_arg_state[PyGI_INVOKE_ARG_STATE_N_MAX]; - if (constructor_class == NULL) { - PyErr_Clear (); - PyErr_Format (PyExc_TypeError, - "Constructors require the class to be passed in as an argument, " - "No arguments passed to the %s constructor.", - cache->name); - - return FALSE; - } - } else if (cache->function_type == PYGI_FUNCTION_TYPE_VFUNC) { - PyObject *py_gtype; - py_gtype = PyTuple_GetItem (py_args, 0); - if (py_gtype == NULL) { - PyErr_SetString (PyExc_TypeError, - "need the GType of the implementor class"); - return FALSE; - } +/** + * _pygi_invoke_arg_state_init: + * Sets PyGIInvokeState.args and PyGIInvokeState.ffi_args. + * On error returns FALSE and sets an exception. + */ +gboolean +_pygi_invoke_arg_state_init (PyGIInvokeState *state) { - state->implementor_gtype = pyg_type_from_object (py_gtype); + gpointer mem; - if (state->implementor_gtype == 0) - return FALSE; + if (state->n_args < PyGI_INVOKE_ARG_STATE_N_MAX && (mem = free_arg_state[state->n_args]) != NULL) { + free_arg_state[state->n_args] = NULL; + memset (mem, 0, PyGI_INVOKE_ARG_STATE_SIZE (state->n_args)); + } else { + mem = g_slice_alloc0 (PyGI_INVOKE_ARG_STATE_SIZE (state->n_args)); } - if (cache->function_type == PYGI_FUNCTION_TYPE_CONSTRUCTOR || - cache->function_type == PYGI_FUNCTION_TYPE_VFUNC) { + if (mem == NULL && state->n_args != 0) { + PyErr_NoMemory(); + return FALSE; + } - /* we could optimize this by using offsets instead of modifying the tuple but it makes the - * code more error prone and confusing so don't do that unless profiling shows - * significant gain - */ - state->py_in_args = PyTuple_GetSlice (py_args, 1, PyTuple_Size (py_args)); - } else { - state->py_in_args = py_args; - Py_INCREF (state->py_in_args); + if (mem != NULL) { + state->args = mem; + state->ffi_args = (gpointer)((gchar *)mem + state->n_args * sizeof (PyGIInvokeArgState)); } - state->py_in_args = _py_args_combine_and_check_length (cache->name, - cache->arg_name_list, - cache->arg_name_hash, - state->py_in_args, - kwargs); + return TRUE; +} - if (state->py_in_args == NULL) { - return FALSE; +/** + * _pygi_invoke_arg_state_free: + * Frees PyGIInvokeState.args and PyGIInvokeState.ffi_args + */ +void +_pygi_invoke_arg_state_free(PyGIInvokeState *state) { + if (state->n_args < PyGI_INVOKE_ARG_STATE_N_MAX && free_arg_state[state->n_args] == NULL) { + free_arg_state[state->n_args] = state->args; + return; } - state->n_py_in_args = PyTuple_Size (state->py_in_args); - state->args = g_slice_alloc0 (cache->n_args * sizeof (GIArgument *)); - if (state->args == NULL && cache->n_args != 0) { - PyErr_NoMemory(); - return FALSE; - } + g_slice_free1 (PyGI_INVOKE_ARG_STATE_SIZE (state->n_args), state->args); +} - state->in_args = g_slice_alloc0 (cache->n_from_py_args * sizeof(GIArgument)); - if (state->in_args == NULL && cache->n_from_py_args != 0) { - PyErr_NoMemory (); - return FALSE; +static gboolean +_invoke_state_init_from_cache (PyGIInvokeState *state, + PyGIFunctionCache *function_cache, + PyObject *py_args, + PyObject *kwargs) +{ + PyGICallableCache *cache = (PyGICallableCache *) function_cache; + + state->n_args = _pygi_callable_cache_args_len (cache); + + if (cache->throws) { + state->n_args++; } - 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 (); + /* Copy the function pointer to the state for the normal case. For vfuncs, + * this has already been filled out based on the implementor's GType. + */ + if (state->function_ptr == NULL) + state->function_ptr = function_cache->invoker.native_address; + + state->py_in_args = _py_args_combine_and_check_length (cache, + py_args, + kwargs); + + if (state->py_in_args == NULL) { return FALSE; } + state->n_py_in_args = PyTuple_Size (state->py_in_args); - state->out_args = g_slice_alloc0 (cache->n_to_py_args * sizeof(GIArgument)); - if (state->out_args == NULL && cache->n_to_py_args != 0) { - PyErr_NoMemory (); + if (!_pygi_invoke_arg_state_init (state)) { 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->args[error_index].arg_pointer.v_pointer = &state->error; + state->ffi_args[error_index] = &(state->args[error_index].arg_pointer); + } + return TRUE; } -static inline void -_invoke_state_clear (PyGIInvokeState *state, PyGICallableCache *cache) +static void +_invoke_state_clear (PyGIInvokeState *state, PyGIFunctionCache *function_cache) { - g_slice_free1 (cache->n_args * sizeof(GIArgument *), state->args); - 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); - + _pygi_invoke_arg_state_free (state); 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) { - PyGIInterfaceCache *iface_cache; - - g_assert (arg_cache->type_tag == GI_TYPE_TAG_INTERFACE); - - iface_cache = (PyGIInterfaceCache *)arg_cache; - - state->out_args[out_count].v_pointer = NULL; - state->args[arg_count] = &state->out_args[out_count]; - if (iface_cache->g_type == G_TYPE_BOXED) { - state->args[arg_count]->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); - } else if (iface_cache->is_foreign) { - PyObject *foreign_struct = - pygi_struct_foreign_convert_from_g_argument ( - iface_cache->interface_info, - NULL); - - pygi_struct_foreign_convert_to_g_argument (foreign_struct, - iface_cache->interface_info, - GI_TRANSFER_EVERYTHING, - state->args[arg_count]); + if (arg_cache->type_tag == GI_TYPE_TAG_INTERFACE) { + PyGIInterfaceCache *iface_cache = (PyGIInterfaceCache *)arg_cache; + + arg->v_pointer = NULL; + if (g_type_is_a (iface_cache->g_type, G_TYPE_BOXED)) { + arg->v_pointer = + _pygi_boxed_alloc (iface_cache->interface_info, NULL); + } else if (iface_cache->g_type == G_TYPE_VALUE) { + arg->v_pointer = g_slice_new0 (GValue); + } else if (iface_cache->is_foreign) { + PyObject *foreign_struct = + pygi_struct_foreign_convert_from_g_argument ( + iface_cache->interface_info, + GI_TRANSFER_NOTHING, + NULL); + + pygi_struct_foreign_convert_to_g_argument (foreign_struct, + iface_cache->interface_info, + GI_TRANSFER_EVERYTHING, + arg); + } else { + gssize size = g_struct_info_get_size( + (GIStructInfo *)iface_cache->interface_info); + arg->v_pointer = g_malloc0 (size); + } + } else if (arg_cache->type_tag == GI_TYPE_TAG_ARRAY) { + PyGIArgGArray *array_cache = (PyGIArgGArray *)arg_cache; + + arg->v_pointer = g_array_new (TRUE, TRUE, array_cache->item_size); } else { - gssize size = g_struct_info_get_size( - (GIStructInfo *)iface_cache->interface_info); - state->args[arg_count]->v_pointer = g_malloc0 (size); + return FALSE; } - if (state->args[arg_count]->v_pointer == NULL) + if (arg->v_pointer == NULL) return FALSE; return TRUE; } -static inline gboolean -_invoke_marshal_in_args (PyGIInvokeState *state, PyGICallableCache *cache) +/* pygi_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 gboolean +_invoke_marshal_in_args (PyGIInvokeState *state, PyGIFunctionCache *function_cache) { - gssize i, in_count, out_count; - in_count = 0; - out_count = 0; + PyGICallableCache *cache = (PyGICallableCache *) function_cache; + gssize i; if (state->n_py_in_args > cache->n_py_args) { + char *full_name = pygi_callable_cache_get_full_name (cache); PyErr_Format (PyExc_TypeError, "%s() takes exactly %zd argument(s) (%zd given)", - cache->name, + full_name, cache->n_py_args, state->n_py_in_args); + g_free (full_name); return FALSE; } - for (i = 0; i < cache->n_args; i++) { - GIArgument *c_arg; - PyGIArgCache *arg_cache = cache->args_cache[i]; + for (i = 0; i < _pygi_callable_cache_args_len (cache); i++) { + GIArgument *c_arg = &state->args[i].arg_value; + 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->ffi_args[i] = c_arg; - if (arg_cache->meta_type > 0) + if (arg_cache->meta_type == PYGI_META_ARG_TYPE_CLOSURE) { + state->ffi_args[i]->v_pointer = state->user_data; + continue; + } else if (arg_cache->meta_type != PYGI_META_ARG_TYPE_PARENT) continue; if (arg_cache->py_arg_index >= state->n_py_in_args) { + char *full_name = pygi_callable_cache_get_full_name (cache); PyErr_Format (PyExc_TypeError, "%s() takes exactly %zd argument(s) (%zd given)", - cache->name, + full_name, cache->n_py_args, state->n_py_in_args); + g_free (full_name); /* clean up all of the args we have already marshalled, * since invoke will not be called */ pygi_marshal_cleanup_args_from_py_parameter_fail (state, cache, - i - 1); + i); return FALSE; } @@ -425,23 +449,18 @@ _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) { + char *full_name = pygi_callable_cache_get_full_name (cache); PyErr_Format (PyExc_TypeError, "%s() takes exactly %zd argument(s) (%zd given)", - cache->name, + full_name, cache->n_py_args, state->n_py_in_args); + g_free (full_name); pygi_marshal_cleanup_args_from_py_parameter_fail (state, cache, - i - 1); + i); return FALSE; } @@ -449,27 +468,53 @@ _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->args[i].arg_pointer.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->ffi_args[i] = c_arg; + + if (!_caller_alloc (arg_cache, c_arg)) { + char *full_name = pygi_callable_cache_get_full_name (cache); PyErr_Format (PyExc_TypeError, "Could not caller allocate argument %zd of callable %s", - i, cache->name); + i, full_name); + g_free (full_name); pygi_marshal_cleanup_args_from_py_parameter_fail (state, cache, - i - 1); + i); 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->ffi_args[i] = &state->args[i].arg_pointer; } - out_count++; + break; } - c_arg = state->args[i]; - if (arg_cache->from_py_marshaller != NULL) { + if (py_arg == _PyGIDefaultArgPlaceholder) { + *c_arg = arg_cache->default_value; + } else if (arg_cache->from_py_marshaller != NULL && + arg_cache->meta_type != PYGI_META_ARG_TYPE_CHILD) { + gboolean success; + gpointer cleanup_data = NULL; + if (!arg_cache->allow_none && py_arg == Py_None) { PyErr_Format (PyExc_TypeError, "Argument %zd does not allow None as a value", @@ -477,18 +522,21 @@ _invoke_marshal_in_args (PyGIInvokeState *state, PyGICallableCache *cache) pygi_marshal_cleanup_args_from_py_parameter_fail (state, cache, - i - 1); + i); return FALSE; } - gboolean success = arg_cache->from_py_marshaller (state, - cache, - arg_cache, - py_arg, - c_arg); + success = arg_cache->from_py_marshaller (state, + cache, + arg_cache, + py_arg, + c_arg, + &cleanup_data); + state->args[i].arg_cleanup_data = cleanup_data; + if (!success) { pygi_marshal_cleanup_args_from_py_parameter_fail (state, cache, - i - 1); + i); return FALSE; } @@ -499,25 +547,16 @@ _invoke_marshal_in_args (PyGIInvokeState *state, PyGICallableCache *cache) return TRUE; } -static inline PyObject * -_invoke_marshal_out_args (PyGIInvokeState *state, PyGICallableCache *cache) +static PyObject * +_invoke_marshal_out_args (PyGIInvokeState *state, PyGIFunctionCache *function_cache) { + PyGICallableCache *cache = (PyGICallableCache *) function_cache; PyObject *py_out = NULL; PyObject *py_return = NULL; - gssize total_out_args = cache->n_to_py_args; - gboolean has_return = FALSE; + gssize n_out_args = cache->n_to_py_args - cache->n_to_py_child_args; if (cache->return_cache) { if (!cache->return_cache->is_skipped) { - if (cache->function_type == PYGI_FUNCTION_TYPE_CONSTRUCTOR) { - if (state->return_arg.v_pointer == NULL) { - PyErr_SetString (PyExc_TypeError, "constructor returned NULL"); - pygi_marshal_cleanup_args_return_fail (state, - cache); - return NULL; - } - } - py_return = cache->return_cache->to_py_marshaller ( state, cache, cache->return_cache, @@ -527,12 +566,6 @@ _invoke_marshal_out_args (PyGIInvokeState *state, PyGICallableCache *cache) cache); return NULL; } - - - if (cache->return_cache->type_tag != GI_TYPE_TAG_VOID) { - total_out_args++; - has_return = TRUE; - } } else { if (cache->return_cache->transfer == GI_TRANSFER_EVERYTHING) { PyGIMarshalCleanupFunc to_py_cleanup = @@ -541,15 +574,14 @@ _invoke_marshal_out_args (PyGIInvokeState *state, PyGICallableCache *cache) if (to_py_cleanup != NULL) to_py_cleanup ( state, cache->return_cache, + NULL, &state->return_arg, FALSE); } } } - total_out_args -= cache->n_to_py_child_args; - - if (cache->n_to_py_args - cache->n_to_py_child_args == 0) { + if (n_out_args == 0) { if (cache->return_cache->is_skipped && state->error == NULL) { /* we skip the return value and have no (out) arguments to return, * so py_return should be NULL. But we must not return NULL, @@ -561,13 +593,13 @@ _invoke_marshal_out_args (PyGIInvokeState *state, PyGICallableCache *cache) } py_out = py_return; - } else if (total_out_args == 1) { + } else if (!cache->has_return && n_out_args == 1) { /* if we get here there is one out arg an no return */ PyGIArgCache *arg_cache = (PyGIArgCache *)cache->to_py_args->data; py_out = arg_cache->to_py_marshaller (state, cache, arg_cache, - state->args[arg_cache->c_arg_index]); + state->args[arg_cache->c_arg_index].arg_pointer.v_pointer); if (py_out == NULL) { pygi_marshal_cleanup_args_to_py_parameter_fail (state, cache, @@ -576,26 +608,36 @@ _invoke_marshal_out_args (PyGIInvokeState *state, PyGICallableCache *cache) } } else { + /* return a tuple */ gssize py_arg_index = 0; GSList *cache_item = cache->to_py_args; - /* return a tuple */ - py_out = PyTuple_New (total_out_args); - if (has_return) { + gssize tuple_len = cache->has_return + n_out_args; + + py_out = pygi_resulttuple_new (cache->resulttuple_type, tuple_len); + + if (py_out == NULL) { + pygi_marshal_cleanup_args_to_py_parameter_fail (state, + cache, + py_arg_index); + return NULL; + } + + if (cache->has_return) { PyTuple_SET_ITEM (py_out, py_arg_index, py_return); py_arg_index++; } - for(; py_arg_index < total_out_args; py_arg_index++) { + for (; py_arg_index < tuple_len; py_arg_index++) { PyGIArgCache *arg_cache = (PyGIArgCache *)cache_item->data; PyObject *py_obj = arg_cache->to_py_marshaller (state, cache, arg_cache, - state->args[arg_cache->c_arg_index]); + state->args[arg_cache->c_arg_index].arg_pointer.v_pointer); if (py_obj == NULL) { - if (has_return) + if (cache->has_return) py_arg_index--; - + pygi_marshal_cleanup_args_to_py_parameter_fail (state, cache, py_arg_index); @@ -611,34 +653,103 @@ _invoke_marshal_out_args (PyGIInvokeState *state, PyGICallableCache *cache) } PyObject * -_wrap_g_callable_info_invoke (PyGIBaseInfo *self, - PyObject *py_args, - PyObject *kwargs) +pygi_invoke_c_callable (PyGIFunctionCache *function_cache, + PyGIInvokeState *state, + PyObject *py_args, + PyObject *py_kwargs) { - PyGIInvokeState state = { 0, }; + PyGICallableCache *cache = (PyGICallableCache *) function_cache; + GIFFIReturnValue ffi_return_value = {0}; PyObject *ret = NULL; - if (self->cache == NULL) { - self->cache = _pygi_callable_cache_new (self->info); - if (self->cache == NULL) - return NULL; - } + if (!_invoke_state_init_from_cache (state, function_cache, + py_args, py_kwargs)) + goto err; + + if (!_invoke_marshal_in_args (state, function_cache)) + goto err; + + Py_BEGIN_ALLOW_THREADS; + + ffi_call (&function_cache->invoker.cif, + state->function_ptr, + (void *) &ffi_return_value, + (void **) state->ffi_args); + + Py_END_ALLOW_THREADS; - if (!_invoke_state_init_from_callable_cache (&state, self->cache, py_args, kwargs)) - goto err; + /* 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 (pygi_error_check (&state->error)) { + /* even though we errored out, the call itself was successful, + so we assume the call processed all of the parameters */ + pygi_marshal_cleanup_args_from_py_marshal_success (state, cache); + goto err; + } + } - if (!_invoke_marshal_in_args (&state, self->cache)) - goto err; + if (cache->return_cache) { + gi_type_info_extract_ffi_return_value (cache->return_cache->type_info, + &ffi_return_value, + &state->return_arg); + } - if (!_invoke_callable (&state, self->cache, self->info)) - goto err; + ret = _invoke_marshal_out_args (state, function_cache); + pygi_marshal_cleanup_args_from_py_marshal_success (state, cache); - pygi_marshal_cleanup_args_from_py_marshal_success (&state, self->cache); + if (ret != NULL) + pygi_marshal_cleanup_args_to_py_marshal_success (state, cache); - ret = _invoke_marshal_out_args (&state, self->cache); - if (ret) - pygi_marshal_cleanup_args_to_py_marshal_success (&state, self->cache); err: - _invoke_state_clear (&state, self->cache); + _invoke_state_clear (state, function_cache); return ret; } + +PyObject * +pygi_callable_info_invoke (GIBaseInfo *info, PyObject *py_args, + PyObject *kwargs, PyGICallableCache *cache, + gpointer user_data) +{ + return pygi_function_cache_invoke ((PyGIFunctionCache *) cache, + py_args, kwargs); +} + +PyObject * +_wrap_g_callable_info_invoke (PyGIBaseInfo *self, PyObject *py_args, + PyObject *kwargs) +{ + if (self->cache == NULL) { + PyGIFunctionCache *function_cache; + GIInfoType type = g_base_info_get_type (self->info); + + if (type == GI_INFO_TYPE_FUNCTION) { + GIFunctionInfoFlags flags; + + flags = g_function_info_get_flags ( (GIFunctionInfo *)self->info); + + if (flags & GI_FUNCTION_IS_CONSTRUCTOR) { + function_cache = pygi_constructor_cache_new (self->info); + } else if (flags & GI_FUNCTION_IS_METHOD) { + function_cache = pygi_method_cache_new (self->info); + } else { + function_cache = pygi_function_cache_new (self->info); + } + } else if (type == GI_INFO_TYPE_VFUNC) { + function_cache = pygi_vfunc_cache_new (self->info); + } else if (type == GI_INFO_TYPE_CALLBACK) { + g_error ("Cannot invoke callback types"); + } else { + function_cache = pygi_method_cache_new (self->info); + } + + self->cache = (PyGICallableCache *)function_cache; + if (self->cache == NULL) + return NULL; + } + + return pygi_callable_info_invoke (self->info, py_args, kwargs, self->cache, NULL); +} |