[pygobject] Split the callable cache into the different types
- From: Garrett Regier <gregier src gnome org>
- To: commits-list gnome org
- Cc:
- Subject: [pygobject] Split the callable cache into the different types
- Date: Wed, 6 Aug 2014 13:13:05 +0000 (UTC)
commit e6d48b4eadbeb1014c4eb140317b579e69eb8d88
Author: Garrett Regier <Garrett Regier riftio com>
Date: Fri Aug 1 11:40:08 2014 -0400
Split the callable cache into the different types
Instead of doing different things based on the various function types
this adds vfuncs for generate_args_cache() and invoke() which are then
specialized for the various function types. Also add a calling context
to the callable cache which is then used to determine the direction
when generating the arg caches.
This is in preparation for adding closure caches.
https://bugzilla.gnome.org/show_bug.cgi?id=727004
gi/pygi-cache.c | 514 ++++++++++++++++++++++++++++++-----------
gi/pygi-cache.h | 96 ++++++---
gi/pygi-ccallback.c | 16 +-
gi/pygi-info.c | 3 +-
gi/pygi-invoke-state-struct.h | 2 -
gi/pygi-invoke.c | 244 ++++++++------------
gi/pygi-invoke.h | 4 +
gi/pygi.h | 2 +-
8 files changed, 564 insertions(+), 317 deletions(-)
---
diff --git a/gi/pygi-cache.c b/gi/pygi-cache.c
index 5f60766..e6dc0e0 100644
--- a/gi/pygi-cache.c
+++ b/gi/pygi-cache.c
@@ -117,25 +117,6 @@ pygi_arg_cache_free (PyGIArgCache *cache)
g_slice_free (PyGIArgCache, cache);
}
-void
-pygi_callable_cache_free (PyGICallableCache *cache)
-{
- if (cache == NULL)
- return;
-
- g_slist_free (cache->to_py_args);
- g_slist_free (cache->arg_name_list);
- g_hash_table_destroy (cache->arg_name_hash);
- g_ptr_array_unref (cache->args_cache);
-
- if (cache->return_cache != NULL)
- pygi_arg_cache_free (cache->return_cache);
-
- g_function_invoker_destroy (&cache->invoker);
- g_slice_free (PyGICallableCache, cache);
-}
-
-
/* PyGIInterfaceCache */
static void
@@ -447,6 +428,8 @@ pygi_arg_cache_new (GITypeInfo *type_info,
return arg_cache;
}
+/* PyGICallableCache */
+
static PyGIDirection
_pygi_get_direction (PyGICallableCache *callable_cache, GIDirection gi_direction)
{
@@ -454,11 +437,11 @@ _pygi_get_direction (PyGICallableCache *callable_cache, GIDirection gi_direction
if (gi_direction == GI_DIRECTION_INOUT) {
return PYGI_DIRECTION_BIDIRECTIONAL;
} else if (gi_direction == GI_DIRECTION_IN) {
- if (callable_cache->function_type == PYGI_FUNCTION_TYPE_CALLBACK)
+ if (callable_cache->calling_context != PYGI_CALLING_CONTEXT_IS_FROM_PY)
return PYGI_DIRECTION_TO_PYTHON;
return PYGI_DIRECTION_FROM_PYTHON;
} else {
- if (callable_cache->function_type == PYGI_FUNCTION_TYPE_CALLBACK)
+ if (callable_cache->calling_context != PYGI_CALLING_CONTEXT_IS_FROM_PY)
return PYGI_DIRECTION_FROM_PYTHON;
return PYGI_DIRECTION_TO_PYTHON;
}
@@ -466,11 +449,11 @@ _pygi_get_direction (PyGICallableCache *callable_cache, GIDirection gi_direction
/* Generate the cache for the callable's arguments */
static gboolean
-_args_cache_generate (GICallableInfo *callable_info,
- PyGICallableCache *callable_cache)
+_callable_cache_generate_args_cache_real (PyGICallableCache *callable_cache,
+ GICallableInfo *callable_info)
{
- gssize arg_index = 0;
gssize i;
+ gssize arg_index;
GITypeInfo *return_info;
GITransfer return_transfer;
PyGIArgCache *return_cache;
@@ -500,41 +483,9 @@ _args_cache_generate (GICallableInfo *callable_info,
callable_cache->return_cache = return_cache;
g_base_info_unref (return_info);
- /* first arg is the instance */
- if (callable_cache->function_type == PYGI_FUNCTION_TYPE_METHOD ||
- callable_cache->function_type == PYGI_FUNCTION_TYPE_VFUNC) {
- GIInterfaceInfo *interface_info;
- PyGIArgCache *instance_cache;
-
- interface_info = g_base_info_get_container ( (GIBaseInfo *)callable_info);
-
- instance_cache =
- _arg_cache_new_for_interface (interface_info,
- NULL,
- NULL,
- GI_TRANSFER_NOTHING,
- PYGI_DIRECTION_FROM_PYTHON,
- callable_cache);
-
- g_base_info_unref ( (GIBaseInfo *)interface_info);
-
- if (instance_cache == NULL)
- return FALSE;
-
- /* Because we are not supplied a GITypeInfo for instance arguments,
- * assume some defaults. */
- instance_cache->is_pointer = TRUE;
- instance_cache->py_arg_index = 0;
- instance_cache->c_arg_index = 0;
-
- _pygi_callable_cache_set_arg (callable_cache, arg_index, instance_cache);
-
- arg_index++;
- callable_cache->n_py_args++;
- }
-
-
- for (i=0; arg_index < _pygi_callable_cache_args_len (callable_cache); arg_index++, i++) {
+ for (i = 0, arg_index = callable_cache->args_offset;
+ arg_index < _pygi_callable_cache_args_len (callable_cache);
+ i++, arg_index++) {
PyGIArgCache *arg_cache = NULL;
GIArgInfo *arg_info;
PyGIDirection direction;
@@ -681,59 +632,32 @@ _args_cache_generate (GICallableInfo *callable_info,
return TRUE;
}
-static gboolean
-_setup_invoker (GICallableInfo *callable_info,
- GIInfoType info_type,
- GIFunctionInvoker *invoker,
- GCallback function_ptr)
+static void
+_callable_cache_deinit_real (PyGICallableCache *cache)
{
- GError *error = NULL;
-
- if (info_type == GI_INFO_TYPE_FUNCTION) {
- if (g_function_info_prep_invoker ((GIFunctionInfo *)callable_info,
- invoker,
- &error)) {
- return TRUE;
- }
- if (!pygi_error_check (&error)) {
- PyErr_Format (PyExc_RuntimeError,
- "unknown error creating invoker for %s",
- g_base_info_get_name ((GIBaseInfo *)callable_info));
- }
- return FALSE;
+ g_slist_free (cache->to_py_args);
+ g_slist_free (cache->arg_name_list);
+ g_hash_table_destroy (cache->arg_name_hash);
+ g_ptr_array_unref (cache->args_cache);
- } else {
- if (!g_function_invoker_new_for_address (function_ptr,
- (GIFunctionInfo *)callable_info,
- invoker,
- &error)) {
- if (!pygi_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;
+ if (cache->return_cache != NULL)
+ pygi_arg_cache_free (cache->return_cache);
}
-PyGICallableCache *
-pygi_callable_cache_new (GICallableInfo *callable_info,
- GCallback function_ptr,
- gboolean is_ccallback)
+static gboolean
+_callable_cache_init (PyGICallableCache *cache,
+ GICallableInfo *callable_info)
{
gint n_args;
- PyGICallableCache *cache;
- GIInfoType type = g_base_info_get_type ( (GIBaseInfo *)callable_info);
- cache = g_slice_new0 (PyGICallableCache);
+ if (cache->deinit == NULL)
+ cache->deinit = _callable_cache_deinit_real;
- if (cache == NULL)
- return NULL;
+ if (cache->generate_args_cache == NULL)
+ cache->generate_args_cache = _callable_cache_generate_args_cache_real;
- cache->name = g_base_info_get_name ((GIBaseInfo *)callable_info);
- cache->throws = g_callable_info_can_throw_gerror ((GIBaseInfo *)callable_info);
+ 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");
@@ -745,51 +669,373 @@ pygi_callable_cache_new (GICallableInfo *callable_info,
else
warning = g_strdup_printf ("%s.%s is deprecated",
g_base_info_get_namespace (callable_info), cache->name);
- PyErr_WarnEx(PyExc_DeprecationWarning, warning, 0);
+ PyErr_WarnEx (PyExc_DeprecationWarning, warning, 0);
g_free (warning);
}
- if (type == GI_INFO_TYPE_FUNCTION) {
- GIFunctionInfoFlags flags;
+ n_args = cache->args_offset + g_callable_info_get_n_args (callable_info);
- flags = g_function_info_get_flags ( (GIFunctionInfo *)callable_info);
+ if (n_args >= 0) {
+ cache->args_cache = g_ptr_array_new_full (n_args, (GDestroyNotify) pygi_arg_cache_free);
+ g_ptr_array_set_size (cache->args_cache, n_args);
+ }
- if (flags & GI_FUNCTION_IS_CONSTRUCTOR)
- cache->function_type = PYGI_FUNCTION_TYPE_CONSTRUCTOR;
- else if (flags & GI_FUNCTION_IS_METHOD)
- cache->function_type = PYGI_FUNCTION_TYPE_METHOD;
- } else if (type == GI_INFO_TYPE_VFUNC) {
- cache->function_type = PYGI_FUNCTION_TYPE_VFUNC;
- } else if (type == GI_INFO_TYPE_CALLBACK) {
- if (is_ccallback)
- cache->function_type = PYGI_FUNCTION_TYPE_CCALLBACK;
- else
- cache->function_type = PYGI_FUNCTION_TYPE_CALLBACK;
+ if (!cache->generate_args_cache (cache, callable_info)) {
+ _callable_cache_deinit_real (cache);
+ return FALSE;
+ }
+
+ return TRUE;
+}
+
+void
+pygi_callable_cache_free (PyGICallableCache *cache)
+{
+ cache->deinit (cache);
+ g_free (cache);
+}
+
+/* PyGIFunctionCache */
+
+static PyObject *
+_function_cache_invoke_real (PyGIFunctionCache *function_cache,
+ PyGIInvokeState *state,
+ PyObject *py_args,
+ PyObject *py_kwargs)
+{
+ return pygi_invoke_c_callable (function_cache, state,
+ py_args, py_kwargs);
+}
+
+static void
+_function_cache_deinit_real (PyGICallableCache *callable_cache)
+{
+ g_function_invoker_destroy (&((PyGIFunctionCache *) callable_cache)->invoker);
+
+ _callable_cache_deinit_real (callable_cache);
+}
+
+static gboolean
+_function_cache_init (PyGIFunctionCache *function_cache,
+ GICallableInfo *callable_info)
+{
+ PyGICallableCache *callable_cache = (PyGICallableCache *) function_cache;
+ GIFunctionInvoker *invoker = &function_cache->invoker;
+ GError *error = NULL;
+
+ callable_cache->calling_context = PYGI_CALLING_CONTEXT_IS_FROM_PY;
+
+ if (callable_cache->deinit == NULL)
+ callable_cache->deinit = _function_cache_deinit_real;
+
+ if (function_cache->invoke == NULL)
+ function_cache->invoke = _function_cache_invoke_real;
+
+ if (!_callable_cache_init (callable_cache, callable_info))
+ return FALSE;
+
+ /* Set by PyGICCallbackCache and PyGIVFuncCache */
+ if (invoker->native_address == NULL) {
+ if (g_function_info_prep_invoker ((GIFunctionInfo *) callable_info,
+ invoker,
+ &error)) {
+ return TRUE;
+ }
} else {
- cache->function_type = PYGI_FUNCTION_TYPE_METHOD;
+ if (g_function_invoker_new_for_address (invoker->native_address,
+ (GIFunctionInfo *) callable_info,
+ invoker,
+ &error)) {
+ return TRUE;
+ }
}
- /* if we are a method or vfunc make sure the instance parameter is counted */
- if (cache->function_type == PYGI_FUNCTION_TYPE_METHOD ||
- cache->function_type == PYGI_FUNCTION_TYPE_VFUNC)
- cache->args_offset = 1;
+ if (!pygi_error_check (&error)) {
+ PyErr_Format (PyExc_RuntimeError,
+ "unknown error creating invoker for %s",
+ g_base_info_get_name ((GIBaseInfo *) callable_info));
+ }
- n_args = cache->args_offset + g_callable_info_get_n_args (callable_info);
+ _callable_cache_deinit_real (callable_cache);
+ return FALSE;
+}
- if (n_args >= 0) {
- cache->args_cache = g_ptr_array_new_full (n_args, (GDestroyNotify) pygi_arg_cache_free);
- g_ptr_array_set_size (cache->args_cache, n_args);
+PyGIFunctionCache *
+pygi_function_cache_new (GICallableInfo *info)
+{
+ PyGIFunctionCache *function_cache;
+
+ function_cache = g_new0 (PyGIFunctionCache, 1);
+
+ if (!_function_cache_init (function_cache, info)) {
+ g_free (function_cache);
+ return NULL;
}
- if (!_args_cache_generate (callable_info, cache))
- goto err;
+ return function_cache;
+}
+
+PyObject *
+pygi_function_cache_invoke (PyGIFunctionCache *function_cache,
+ PyObject *py_args,
+ PyObject *py_kwargs)
+{
+ PyGIInvokeState state = { 0, };
+
+ return function_cache->invoke (function_cache, &state,
+ py_args, py_kwargs);
+}
+
+/* PyGICCallbackCache */
+
+PyGIFunctionCache *
+pygi_ccallback_cache_new (GICallableInfo *info,
+ GCallback function_ptr)
+{
+ PyGICCallbackCache *ccallback_cache;
+ PyGIFunctionCache *function_cache;
+
+ ccallback_cache = g_new0 (PyGICCallbackCache, 1);
+ function_cache = (PyGIFunctionCache *) ccallback_cache;
+
+ function_cache->invoker.native_address = function_ptr;
+
+ if (!_function_cache_init (function_cache, info)) {
+ g_free (ccallback_cache);
+ return NULL;
+ }
+
+ return function_cache;
+}
- if (!_setup_invoker (callable_info, type, &cache->invoker, function_ptr)) {
- goto err;
+PyObject *
+pygi_ccallback_cache_invoke (PyGICCallbackCache *ccallback_cache,
+ PyObject *py_args,
+ PyObject *py_kwargs,
+ gpointer user_data)
+{
+ PyGIFunctionCache *function_cache = (PyGIFunctionCache *) ccallback_cache;
+ PyGIInvokeState state = { 0, };
+
+ state.user_data = user_data;
+
+ return function_cache->invoke (function_cache, &state,
+ py_args, py_kwargs);
+}
+
+/* PyGIConstructorCache */
+
+static PyObject *
+_constructor_cache_invoke_real (PyGIFunctionCache *function_cache,
+ PyGIInvokeState *state,
+ PyObject *py_args,
+ PyObject *py_kwargs)
+{
+ PyGICallableCache *cache = (PyGICallableCache *) function_cache;
+ PyObject *constructor_class;
+ PyObject *ret;
+
+ constructor_class = PyTuple_GetItem (py_args, 0);
+ 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.",
+ ((PyGICallableCache *) function_cache)->name);
+
+ return FALSE;
}
- return cache;
-err:
- pygi_callable_cache_free (cache);
+ py_args = PyTuple_GetSlice (py_args, 1, PyTuple_Size (py_args));
+ ret = _function_cache_invoke_real (function_cache, state,
+ py_args, py_kwargs);
+ Py_DECREF (py_args);
+
+ if (cache->return_cache == NULL || cache->return_cache->is_skipped)
+ return ret;
+
+ if (ret != NULL && ret != Py_None) {
+ if (!PyTuple_Check (ret))
+ return ret;
+
+ if (PyTuple_GET_ITEM (ret, 0) != Py_None)
+ return ret;
+ }
+
+ PyErr_SetString (PyExc_TypeError, "constructor returned NULL");
+
+ Py_XDECREF (ret);
return NULL;
}
+
+PyGIFunctionCache *
+pygi_constructor_cache_new (GICallableInfo *info)
+{
+ PyGIConstructorCache *constructor_cache;
+ PyGIFunctionCache *function_cache;
+
+ constructor_cache = g_new0 (PyGIConstructorCache, 1);
+ function_cache = (PyGIFunctionCache *) constructor_cache;
+
+ function_cache->invoke = _constructor_cache_invoke_real;
+
+ if (!_function_cache_init (function_cache, info)) {
+ g_free (constructor_cache);
+ return NULL;
+ }
+
+ return function_cache;
+}
+
+/* PyGIFunctionWithInstanceCache */
+
+static gboolean
+_function_with_instance_cache_generate_args_cache_real (PyGICallableCache *callable_cache,
+ GICallableInfo *callable_info)
+{
+ GIInterfaceInfo *interface_info;
+ PyGIArgCache *instance_cache;
+
+ interface_info = g_base_info_get_container ((GIBaseInfo *) callable_info);
+
+ instance_cache =
+ _arg_cache_new_for_interface (interface_info,
+ NULL,
+ NULL,
+ GI_TRANSFER_NOTHING,
+ PYGI_DIRECTION_FROM_PYTHON,
+ callable_cache);
+
+ g_base_info_unref ((GIBaseInfo *) interface_info);
+
+ if (instance_cache == NULL)
+ return FALSE;
+
+ /* Because we are not supplied a GITypeInfo for instance arguments,
+ * assume some defaults. */
+ instance_cache->is_pointer = TRUE;
+ instance_cache->py_arg_index = 0;
+ instance_cache->c_arg_index = 0;
+
+ _pygi_callable_cache_set_arg (callable_cache, 0, instance_cache);
+
+ callable_cache->n_py_args++;
+
+ return _callable_cache_generate_args_cache_real (callable_cache,
+ callable_info);
+}
+
+static gboolean
+_function_with_instance_cache_init (PyGIFunctionWithInstanceCache *fwi_cache,
+ GICallableInfo *info)
+{
+ PyGICallableCache *callable_cache = (PyGICallableCache *) fwi_cache;
+
+ callable_cache->args_offset += 1;
+ callable_cache->generate_args_cache = _function_with_instance_cache_generate_args_cache_real;
+
+ return _function_cache_init ((PyGIFunctionCache *) fwi_cache, info);
+}
+
+/* PyGIMethodCache */
+
+PyGIFunctionCache *
+pygi_method_cache_new (GICallableInfo *info)
+{
+ PyGIMethodCache *method_cache;
+ PyGIFunctionWithInstanceCache *fwi_cache;
+
+ method_cache = g_new0 (PyGIMethodCache, 1);
+ fwi_cache = (PyGIFunctionWithInstanceCache *) method_cache;
+
+ if (!_function_with_instance_cache_init (fwi_cache, info)) {
+ g_free (method_cache);
+ return NULL;
+ }
+
+ return (PyGIFunctionCache *) method_cache;
+}
+
+/* PyGIVFuncCache */
+
+static PyObject *
+_vfunc_cache_invoke_real (PyGIFunctionCache *function_cache,
+ PyGIInvokeState *state,
+ PyObject *py_args,
+ PyObject *py_kwargs)
+{
+ PyGIVFuncCache *vfunc_cache = (PyGIVFuncCache *) function_cache;
+ PyObject *py_gtype;
+ GType implementor_gtype;
+ GError *error = NULL;
+ PyObject *ret;
+
+ py_gtype = PyTuple_GetItem (py_args, 0);
+ if (py_gtype == NULL) {
+ PyErr_SetString (PyExc_TypeError,
+ "need the GType of the implementor class");
+ return FALSE;
+ }
+
+ implementor_gtype = pyg_type_from_object (py_gtype);
+ if (implementor_gtype == G_TYPE_INVALID)
+ 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 *) vfunc_cache->info,
+ implementor_gtype,
+ &error);
+ if (pygi_error_check (&error)) {
+ return FALSE;
+ }
+
+ py_args = PyTuple_GetSlice (py_args, 1, PyTuple_Size (py_args));
+ ret = _function_cache_invoke_real (function_cache, state,
+ py_args, py_kwargs);
+ Py_DECREF (py_args);
+
+ return ret;
+}
+
+static void
+_vfunc_cache_deinit_real (PyGICallableCache *callable_cache)
+{
+ g_base_info_unref (((PyGIVFuncCache *) callable_cache)->info);
+
+ _function_cache_deinit_real (callable_cache);
+}
+
+PyGIFunctionCache *
+pygi_vfunc_cache_new (GICallableInfo *info)
+{
+ PyGIVFuncCache *vfunc_cache;
+ PyGIFunctionCache *function_cache;
+ PyGIFunctionWithInstanceCache *fwi_cache;
+
+ vfunc_cache = g_new0 (PyGIVFuncCache, 1);
+ function_cache = (PyGIFunctionCache *) vfunc_cache;
+ fwi_cache = (PyGIFunctionWithInstanceCache *) vfunc_cache;
+
+ ((PyGICallableCache *) vfunc_cache)->deinit = _vfunc_cache_deinit_real;
+
+ /* This must be non-NULL for _function_cache_init() to create the
+ * invoker, the real address will be set in _vfunc_cache_invoke_real().
+ */
+ function_cache->invoker.native_address = (gpointer) 0xdeadbeef;
+
+ function_cache->invoke = _vfunc_cache_invoke_real;
+
+ if (!_function_with_instance_cache_init (fwi_cache, info)) {
+ g_free (vfunc_cache);
+ return NULL;
+ }
+
+ /* Required by _vfunc_cache_invoke_real() */
+ vfunc_cache->info = g_base_info_ref ((GIBaseInfo *) info);
+
+ return function_cache;
+}
diff --git a/gi/pygi-cache.h b/gi/pygi-cache.h
index 1a160c4..f8ad3e4 100644
--- a/gi/pygi-cache.h
+++ b/gi/pygi-cache.h
@@ -29,8 +29,15 @@
G_BEGIN_DECLS
-typedef struct _PyGICallableCache PyGICallableCache;
typedef struct _PyGIArgCache PyGIArgCache;
+typedef struct _PyGICallableCache PyGICallableCache;
+typedef struct _PyGIFunctionCache PyGIFunctionCache;
+typedef struct _PyGIVFuncCache PyGIVFuncCache;
+
+typedef PyGIFunctionCache PyGICCallbackCache;
+typedef PyGIFunctionCache PyGIConstructorCache;
+typedef PyGIFunctionCache PyGIFunctionWithInstanceCache;
+typedef PyGIFunctionCache PyGIMethodCache;
typedef gboolean (*PyGIMarshalFromPyFunc) (PyGIInvokeState *state,
PyGICallableCache *callable_cache,
@@ -70,32 +77,24 @@ typedef enum {
} PyGIMetaArgType;
/*
- * GI determines function types via a combination of flags and info classes.
- * Since for branching purposes they are mutually exclusive, the
- * PyGIFunctionType enum consolidates them into one enumeration for ease of
- * branching and debugging.
+ * Argument direction types denotes how we marshal,
+ * e.g. to Python or from Python or both.
*/
typedef enum {
- PYGI_FUNCTION_TYPE_FUNCTION,
- PYGI_FUNCTION_TYPE_METHOD,
- PYGI_FUNCTION_TYPE_CONSTRUCTOR,
- PYGI_FUNCTION_TYPE_VFUNC,
- PYGI_FUNCTION_TYPE_CALLBACK,
- PYGI_FUNCTION_TYPE_CCALLBACK,
- } PyGIFunctionType;
+ PYGI_DIRECTION_TO_PYTHON = 1 << 0,
+ PYGI_DIRECTION_FROM_PYTHON = 1 << 1,
+ PYGI_DIRECTION_BIDIRECTIONAL = PYGI_DIRECTION_TO_PYTHON | PYGI_DIRECTION_FROM_PYTHON
+ } PyGIDirection;
/*
* In PyGI IN and OUT arguments mean different things depending on the context
- * of the callable (e.g. is it a callback that is being called from C or a
- * function that is being called from python). We don't as much care if the
- * parameter is an IN or OUT C parameter, than we do if the parameter is being
- * marshalled into Python or from Python.
+ * of the callable, e.g. is it a callback that is being called from C or a
+ * function that is being called from Python.
*/
typedef enum {
- PYGI_DIRECTION_TO_PYTHON = 1 << 0,
- PYGI_DIRECTION_FROM_PYTHON = 1 << 1,
- PYGI_DIRECTION_BIDIRECTIONAL = PYGI_DIRECTION_TO_PYTHON | PYGI_DIRECTION_FROM_PYTHON
- } PyGIDirection;
+ PYGI_CALLING_CONTEXT_IS_FROM_C,
+ PYGI_CALLING_CONTEXT_IS_FROM_PY
+} PyGICallingContext;
struct _PyGIArgCache
@@ -159,7 +158,7 @@ struct _PyGICallableCache
{
const gchar *name;
- PyGIFunctionType function_type;
+ PyGICallingContext calling_context;
PyGIArgCache *return_cache;
GPtrArray *args_cache;
@@ -191,9 +190,30 @@ struct _PyGICallableCache
* This count does not include args with defaults. */
gssize n_py_required_args;
+ void (*deinit) (PyGICallableCache *callable_cache);
+
+ gboolean (*generate_args_cache) (PyGICallableCache *callable_cache,
+ GICallableInfo *callable_info);
+};
+
+typedef struct _PyGIFunctionCache {
+ PyGICallableCache callable_cache;
+
/* An invoker with ffi_cif already setup */
GIFunctionInvoker invoker;
-};
+
+ PyObject *(*invoke) (PyGIFunctionCache *function_cache,
+ PyGIInvokeState *state,
+ PyObject *py_args,
+ PyObject *py_kwargs);
+} PyGIFunctionCache;
+
+typedef struct _PyGIVFuncCache {
+ PyGIFunctionWithInstanceCache fwi_cache;
+
+ GIBaseInfo *info;
+} PyGIVFuncCache;
+
gboolean
pygi_arg_base_setup (PyGIArgCache *arg_cache,
@@ -241,12 +261,34 @@ void
pygi_arg_cache_free (PyGIArgCache *cache);
void
-pygi_callable_cache_free (PyGICallableCache *cache);
+pygi_callable_cache_free (PyGICallableCache *cache);
+
+PyGIFunctionCache *
+pygi_function_cache_new (GICallableInfo *info);
+
+PyObject *
+pygi_function_cache_invoke (PyGIFunctionCache *function_cache,
+ PyObject *py_args,
+ PyObject *py_kwargs);
+
+PyGIFunctionCache *
+pygi_ccallback_cache_new (GICallableInfo *info,
+ GCallback function_ptr);
+
+PyObject *
+pygi_ccallback_cache_invoke (PyGIFunctionCache *function_cache,
+ PyObject *py_args,
+ PyObject *py_kwargs,
+ gpointer user_data);
+
+PyGIFunctionCache *
+pygi_constructor_cache_new (GICallableInfo *info);
+
+PyGIFunctionCache *
+pygi_method_cache_new (GICallableInfo *info);
-PyGICallableCache *
-pygi_callable_cache_new (GICallableInfo *callable_info,
- GCallback function_ptr,
- gboolean is_ccallback);
+PyGIFunctionCache *
+pygi_vfunc_cache_new (GICallableInfo *info);
#define _pygi_callable_cache_args_len(cache) ((cache)->args_cache)->len
diff --git a/gi/pygi-ccallback.c b/gi/pygi-ccallback.c
index 9f63ea0..f28487f 100644
--- a/gi/pygi-ccallback.c
+++ b/gi/pygi-ccallback.c
@@ -32,16 +32,16 @@ _ccallback_call(PyGICCallback *self, PyObject *args, PyObject *kwargs)
PyObject *result;
if (self->cache == NULL) {
- self->cache = pygi_callable_cache_new (self->info, self->callback, TRUE);
+ self->cache = (PyGICCallbackCache *)pygi_ccallback_cache_new (self->info,
+ self->callback);
if (self->cache == NULL)
return NULL;
}
- result = pygi_callable_info_invoke( (GIBaseInfo *) self->info,
- args,
- kwargs,
- self->cache,
- self->user_data);
+ result = pygi_ccallback_cache_invoke (self->cache,
+ args,
+ kwargs,
+ self->user_data);
return result;
}
@@ -78,6 +78,10 @@ static void
_ccallback_dealloc (PyGICCallback *self)
{
g_base_info_unref ( (GIBaseInfo *)self->info);
+
+ if (self->cache != NULL) {
+ pygi_callable_cache_free ( (PyGICallableCache *)self->cache);
+ }
}
void
diff --git a/gi/pygi-info.c b/gi/pygi-info.c
index 43ee711..fdf12b3 100644
--- a/gi/pygi-info.c
+++ b/gi/pygi-info.c
@@ -175,7 +175,8 @@ _base_info_dealloc (PyGIBaseInfo *self)
g_base_info_unref (self->info);
- pygi_callable_cache_free(self->cache);
+ if (self->cache != NULL)
+ pygi_callable_cache_free ( (PyGICallableCache *) self->cache);
Py_TYPE( (PyObject *) self)->tp_free ( (PyObject *) self);
}
diff --git a/gi/pygi-invoke-state-struct.h b/gi/pygi-invoke-state-struct.h
index 174f473..732f3b6 100644
--- a/gi/pygi-invoke-state-struct.h
+++ b/gi/pygi-invoke-state-struct.h
@@ -12,8 +12,6 @@ typedef struct _PyGIInvokeState
PyObject *py_in_args;
gssize n_py_in_args;
- 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.
*/
diff --git a/gi/pygi-invoke.c b/gi/pygi-invoke.c
index 6dc93e2..3f72576 100644
--- a/gi/pygi-invoke.c
+++ b/gi/pygi-invoke.c
@@ -25,45 +25,6 @@
#include "pygi-marshal-cleanup.h"
#include "pygi-error.h"
-static inline gboolean
-_invoke_callable (PyGIInvokeState *state,
- PyGICallableCache *cache,
- GICallableInfo *callable_info)
-{
- GIFFIReturnValue ffi_return_value = {0};
-
- Py_BEGIN_ALLOW_THREADS;
-
- ffi_call (&cache->invoker.cif,
- state->function_ptr,
- (void *)&ffi_return_value,
- (void **)state->args);
-
- 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 (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);
- return FALSE;
- }
- }
-
- if (cache->return_cache) {
- gi_type_info_extract_ffi_return_value (cache->return_cache->type_info,
- &ffi_return_value,
- &state->return_arg);
- }
-
- return TRUE;
-}
-
static gboolean
_check_for_unexpected_kwargs (const gchar *function_name,
GHashTable *arg_name_hash,
@@ -247,15 +208,14 @@ _py_args_combine_and_check_length (PyGICallableCache *cache,
return combined_py_args;
}
-static inline gboolean
-_invoke_state_init_from_callable_cache (GIBaseInfo *info,
- PyGIInvokeState *state,
- PyGICallableCache *cache,
- PyObject *py_args,
- PyObject *kwargs)
+static gboolean
+_invoke_state_init_from_cache (PyGIInvokeState *state,
+ PyGIFunctionCache *function_cache,
+ PyObject *py_args,
+ PyObject *kwargs)
{
- PyObject *combined_args = NULL;
- state->implementor_gtype = 0;
+ PyGICallableCache *cache = (PyGICallableCache *) function_cache;
+
state->n_args = _pygi_callable_cache_args_len (cache);
if (cache->throws) {
@@ -263,74 +223,14 @@ _invoke_state_init_from_callable_cache (GIBaseInfo *info,
}
/* 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.
+ * this has already been filled out based on the implementor's GType.
*/
- 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
- * 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);
-
- 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;
- GError *error = NULL;
-
- py_gtype = PyTuple_GetItem (py_args, 0);
- if (py_gtype == NULL) {
- PyErr_SetString (PyExc_TypeError,
- "need the GType of the implementor class");
- return FALSE;
- }
-
- state->implementor_gtype = pyg_type_from_object (py_gtype);
-
- 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 (pygi_error_check (&error)) {
- return FALSE;
- }
- }
-
- if (cache->function_type == PYGI_FUNCTION_TYPE_CONSTRUCTOR ||
- cache->function_type == PYGI_FUNCTION_TYPE_VFUNC) {
-
- /* 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
- */
- combined_args = PyTuple_GetSlice (py_args, 1, PyTuple_Size (py_args));
- } else {
- combined_args = py_args;
- Py_INCREF (combined_args);
- }
+ if (state->function_ptr == NULL)
+ state->function_ptr = function_cache->invoker.native_address;
state->py_in_args = _py_args_combine_and_check_length (cache,
- combined_args,
+ py_args,
kwargs);
- Py_DECREF (combined_args);
if (state->py_in_args == NULL) {
return FALSE;
@@ -373,8 +273,8 @@ _invoke_state_init_from_callable_cache (GIBaseInfo *info,
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 (state->n_args * sizeof(GIArgument *), state->args);
g_slice_free1 (state->n_args * sizeof(gpointer), state->args_cleanup_data);
@@ -427,7 +327,7 @@ _caller_alloc (PyGIArgCache *arg_cache, GIArgument *arg)
return TRUE;
}
-/* _invoke_marshal_in_args:
+/* 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
@@ -455,9 +355,10 @@ _caller_alloc (PyGIArgCache *arg_cache, GIArgument *arg)
* ]]
*
*/
-static inline gboolean
-_invoke_marshal_in_args (PyGIInvokeState *state, PyGICallableCache *cache)
+static gboolean
+_invoke_marshal_in_args (PyGIInvokeState *state, PyGIFunctionCache *function_cache)
{
+ PyGICallableCache *cache = (PyGICallableCache *) function_cache;
gssize i;
if (state->n_py_in_args > cache->n_py_args) {
@@ -600,9 +501,10 @@ _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;
@@ -610,15 +512,6 @@ _invoke_marshal_out_args (PyGIInvokeState *state, PyGICallableCache *cache)
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,
@@ -697,7 +590,7 @@ _invoke_marshal_out_args (PyGIInvokeState *state, PyGICallableCache *cache)
if (py_obj == NULL) {
if (has_return)
py_arg_index--;
-
+
pygi_marshal_cleanup_args_to_py_parameter_fail (state,
cache,
py_arg_index);
@@ -713,41 +606,100 @@ _invoke_marshal_out_args (PyGIInvokeState *state, PyGICallableCache *cache)
}
PyObject *
-pygi_callable_info_invoke (GIBaseInfo *info, PyObject *py_args,
- PyObject *kwargs, PyGICallableCache *cache,
- gpointer user_data)
+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 (!_invoke_state_init_from_callable_cache (info, &state, cache, py_args, kwargs))
- goto err;
+ 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;
- if (cache->function_type == PYGI_FUNCTION_TYPE_CCALLBACK)
- state.user_data = user_data;
+ ffi_call (&function_cache->invoker.cif,
+ state->function_ptr,
+ (void *) &ffi_return_value,
+ (void **) state->args);
+
+ 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 (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, 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, cache, info))
- goto err;
+ ret = _invoke_marshal_out_args (state, function_cache);
+ pygi_marshal_cleanup_args_from_py_marshal_success (state, cache);
- ret = _invoke_marshal_out_args (&state, cache);
- pygi_marshal_cleanup_args_from_py_marshal_success (&state, cache);
+ if (ret != NULL)
+ pygi_marshal_cleanup_args_to_py_marshal_success (state, cache);
- if (ret)
- pygi_marshal_cleanup_args_to_py_marshal_success (&state, cache);
err:
- _invoke_state_clear (&state, 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) {
- self->cache = pygi_callable_cache_new (self->info, NULL, FALSE);
+ 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;
}
diff --git a/gi/pygi-invoke.h b/gi/pygi-invoke.h
index b6bb4b9..b49ffa7 100644
--- a/gi/pygi-invoke.h
+++ b/gi/pygi-invoke.h
@@ -26,8 +26,12 @@
#include "pygi-private.h"
#include "pygi-invoke-state-struct.h"
+
G_BEGIN_DECLS
+PyObject *pygi_invoke_c_callable (PyGIFunctionCache *function_cache,
+ PyGIInvokeState *state,
+ PyObject *py_args, PyObject *py_kwargs);
PyObject *pygi_callable_info_invoke (GIBaseInfo *info, PyObject *py_args,
PyObject *kwargs, PyGICallableCache *cache,
gpointer user_data);
diff --git a/gi/pygi.h b/gi/pygi.h
index 2a9ee14..3caf6f3 100644
--- a/gi/pygi.h
+++ b/gi/pygi.h
@@ -75,7 +75,7 @@ typedef struct {
gpointer user_data;
GIScopeType scope;
GDestroyNotify destroy_notify_func;
- PyGICallableCache *cache;
+ PyGICCallbackCache *cache;
} PyGICCallback;
[
Date Prev][
Date Next] [
Thread Prev][
Thread Next]
[
Thread Index]
[
Date Index]
[
Author Index]