summaryrefslogtreecommitdiff
path: root/gi/pygi-invoke.c
diff options
context:
space:
mode:
authorDongHun Kwak <dh0128.kwak@samsung.com>2017-07-12 08:45:23 +0900
committerDongHun Kwak <dh0128.kwak@samsung.com>2017-07-12 08:45:27 +0900
commitee6939d73fd96606a92eee870014c58eafee5e63 (patch)
tree6a3526fab8e841879fbcab05749fcb4a91a4f580 /gi/pygi-invoke.c
parent0df45a72dc2a72275a5c49c38b87f73341987192 (diff)
downloadpygobject2-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.c725
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);
+}