Commit 2cddba81 authored by Simon Feltman's avatar Simon Feltman

cache refactoring: Move closure setup and marshaling into pygi-closure

Move closure argument caching and marshaling fragments into
pygi-closure.c.

https://bugzilla.gnome.org/show_bug.cgi?id=709700
parent 18d82747
......@@ -31,6 +31,7 @@
#include "pygi-basictype.h"
#include "pygi-list.h"
#include "pygi-array.h"
#include "pygi-closure.h"
#include "pygi-error.h"
......@@ -162,17 +163,6 @@ _sequence_cache_free_func (PyGISequenceCache *cache)
}
}
static void
_callback_cache_free_func (PyGICallbackCache *cache)
{
if (cache != NULL) {
if (cache->interface_info != NULL)
g_base_info_unref ( (GIBaseInfo *)cache->interface_info);
g_slice_free (PyGICallbackCache, cache);
}
}
void
_pygi_callable_cache_free (PyGICallableCache *cache)
{
......@@ -248,28 +238,6 @@ pygi_arg_sequence_setup (PyGISequenceCache *sc,
return TRUE;
}
static PyGICallbackCache *
_callback_cache_new (GIArgInfo *arg_info,
GIInterfaceInfo *iface_info,
gssize child_offset)
{
PyGICallbackCache *cc;
cc = g_slice_new0 (PyGICallbackCache);
( (PyGIArgCache *)cc)->destroy_notify = (GDestroyNotify)_callback_cache_free_func;
cc->user_data_index = g_arg_info_get_closure (arg_info);
if (cc->user_data_index != -1)
cc->user_data_index += child_offset;
cc->destroy_notify_index = g_arg_info_get_destroy (arg_info);
if (cc->destroy_notify_index != -1)
cc->destroy_notify_index += child_offset;
cc->scope = g_arg_info_get_scope (arg_info);
g_base_info_ref( (GIBaseInfo *)iface_info);
cc->interface_info = iface_info;
return cc;
}
PyGIArgCache *
_arg_cache_alloc (void)
{
......@@ -334,38 +302,6 @@ _arg_cache_to_py_interface_object_setup (PyGIArgCache *arg_cache,
arg_cache->to_py_cleanup = _pygi_marshal_cleanup_to_py_interface_object;
}
static void
_arg_cache_from_py_interface_callback_setup (PyGIArgCache *arg_cache,
PyGICallableCache *callable_cache)
{
PyGICallbackCache *callback_cache = (PyGICallbackCache *)arg_cache;
if (callback_cache->user_data_index >= 0) {
PyGIArgCache *user_data_arg_cache = _arg_cache_alloc ();
user_data_arg_cache->meta_type = PYGI_META_ARG_TYPE_CHILD_WITH_PYARG;
user_data_arg_cache->direction = PYGI_DIRECTION_FROM_PYTHON;
user_data_arg_cache->has_default = TRUE; /* always allow user data with a NULL default. */
_pygi_callable_cache_set_arg (callable_cache, callback_cache->user_data_index,
user_data_arg_cache);
}
if (callback_cache->destroy_notify_index >= 0) {
PyGIArgCache *destroy_arg_cache = _arg_cache_alloc ();
destroy_arg_cache->meta_type = PYGI_META_ARG_TYPE_CHILD;
destroy_arg_cache->direction = PYGI_DIRECTION_FROM_PYTHON;
_pygi_callable_cache_set_arg (callable_cache, callback_cache->destroy_notify_index,
destroy_arg_cache);
}
arg_cache->from_py_marshaller = _pygi_marshal_from_py_interface_callback;
arg_cache->from_py_cleanup = _pygi_marshal_cleanup_from_py_interface_callback;
}
static void
_arg_cache_to_py_interface_callback_setup (void)
{
PyErr_Format(PyExc_NotImplementedError,
"Callback returns are not supported");
}
static void
_arg_cache_from_py_interface_enum_setup (PyGIArgCache *arg_cache,
GITransfer transfer)
......@@ -404,45 +340,22 @@ _arg_cache_new_for_interface (GIInterfaceInfo *iface_info,
PyGICallableCache *callable_cache)
{
PyGIArgCache *arg_cache = NULL;
gssize child_offset = 0;
GIInfoType info_type;
if (callable_cache != NULL)
child_offset =
(callable_cache->function_type == PYGI_FUNCTION_TYPE_METHOD ||
callable_cache->function_type == PYGI_FUNCTION_TYPE_VFUNC) ? 1: 0;
info_type = g_base_info_get_type ( (GIBaseInfo *)iface_info);
/* Callbacks are special cased */
if (info_type == GI_INFO_TYPE_CALLBACK) {
PyGICallbackCache *callback_cache;
if (direction & PYGI_DIRECTION_TO_PYTHON) {
_arg_cache_to_py_interface_callback_setup ();
return NULL;
switch (info_type) {
case GI_INFO_TYPE_CALLBACK:
{
return pygi_arg_callback_new_from_info (type_info,
arg_info,
transfer,
direction,
iface_info,
callable_cache);
}
callback_cache =
_callback_cache_new (arg_info,
iface_info,
child_offset);
arg_cache = (PyGIArgCache *)callback_cache;
if (arg_cache == NULL)
return NULL;
pygi_arg_base_setup (arg_cache,
type_info,
arg_info,
transfer,
direction);
if (direction & PYGI_DIRECTION_FROM_PYTHON)
_arg_cache_from_py_interface_callback_setup (arg_cache, callable_cache);
return arg_cache;
default:
; /* pass through to old model of setup */
}
arg_cache = (PyGIArgCache *)_interface_cache_new (iface_info);
......
......@@ -155,15 +155,6 @@ typedef struct _PyGIInterfaceCache
gchar *type_name;
} PyGIInterfaceCache;
typedef struct _PyGICallbackCache
{
PyGIArgCache arg_cache;
gssize user_data_index;
gssize destroy_notify_index;
GIScopeType scope;
GIInterfaceInfo *interface_info;
} PyGICallbackCache;
struct _PyGICallableCache
{
const gchar *name;
......
......@@ -14,12 +14,23 @@
* 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 "pygi-private.h"
#include "pygi-closure.h"
typedef struct _PyGICallbackCache
{
PyGIArgCache arg_cache;
gssize user_data_index;
gssize destroy_notify_index;
GIScopeType scope;
GIInterfaceInfo *interface_info;
} PyGICallbackCache;
static PyGICClosure *global_destroy_notify;
/* This maintains a list of closures which can be free'd whenever
as they have been called. We will free them on the next
......@@ -669,3 +680,300 @@ _pygi_make_native_closure (GICallableInfo* info,
return closure;
}
/* _pygi_destroy_notify_dummy:
*
* Dummy method used in the occasion when a method has a GDestroyNotify
* argument without user data.
*/
static void
_pygi_destroy_notify_dummy (gpointer data) {
}
static void
_pygi_destroy_notify_callback_closure (ffi_cif *cif,
void *result,
void **args,
void *data)
{
PyGICClosure *info = * (void**) (args[0]);
g_assert (info);
_pygi_invoke_closure_free (info);
}
/* _pygi_destroy_notify_create:
*
* Method used in the occasion when a method has a GDestroyNotify
* argument with user data.
*/
static PyGICClosure*
_pygi_destroy_notify_create (void)
{
if (!global_destroy_notify) {
PyGICClosure *destroy_notify = g_slice_new0 (PyGICClosure);
GIBaseInfo* glib_destroy_notify;
g_assert (destroy_notify);
glib_destroy_notify = g_irepository_find_by_name (NULL, "GLib", "DestroyNotify");
g_assert (glib_destroy_notify != NULL);
g_assert (g_base_info_get_type (glib_destroy_notify) == GI_INFO_TYPE_CALLBACK);
destroy_notify->closure = g_callable_info_prepare_closure ( (GICallableInfo*) glib_destroy_notify,
&destroy_notify->cif,
_pygi_destroy_notify_callback_closure,
NULL);
global_destroy_notify = destroy_notify;
}
return global_destroy_notify;
}
static gboolean
_pygi_marshal_from_py_interface_callback (PyGIInvokeState *state,
PyGICallableCache *callable_cache,
PyGIArgCache *arg_cache,
PyObject *py_arg,
GIArgument *arg,
gpointer *cleanup_data)
{
GICallableInfo *callable_info;
PyGICClosure *closure;
PyGIArgCache *user_data_cache = NULL;
PyGIArgCache *destroy_cache = NULL;
PyGICallbackCache *callback_cache;
PyObject *py_user_data = NULL;
callback_cache = (PyGICallbackCache *)arg_cache;
if (callback_cache->user_data_index > 0) {
user_data_cache = _pygi_callable_cache_get_arg (callable_cache, callback_cache->user_data_index);
if (user_data_cache->py_arg_index < state->n_py_in_args) {
/* py_user_data is a borrowed reference. */
py_user_data = PyTuple_GetItem (state->py_in_args, user_data_cache->py_arg_index);
if (!py_user_data)
return FALSE;
/* NULL out user_data if it was not supplied and the default arg placeholder
* was used instead.
*/
if (py_user_data == _PyGIDefaultArgPlaceholder) {
py_user_data = NULL;
} else if (callable_cache->user_data_varargs_index < 0) {
/* For non-variable length user data, place the user data in a
* single item tuple which is concatenated to the callbacks arguments.
* This allows callback input arg marshaling to always expect a
* tuple for user data. Note the
*/
py_user_data = Py_BuildValue("(O)", py_user_data, NULL);
} else {
/* increment the ref borrowed from PyTuple_GetItem above */
Py_INCREF (py_user_data);
}
}
}
if (py_arg == Py_None) {
return TRUE;
}
if (!PyCallable_Check (py_arg)) {
PyErr_Format (PyExc_TypeError,
"Callback needs to be a function or method not %s",
py_arg->ob_type->tp_name);
return FALSE;
}
callable_info = (GICallableInfo *)callback_cache->interface_info;
closure = _pygi_make_native_closure (callable_info, callback_cache->scope, py_arg, py_user_data);
arg->v_pointer = closure->closure;
/* always decref the user data as _pygi_make_native_closure adds its own ref */
Py_XDECREF (py_user_data);
/* The PyGICClosure instance is used as user data passed into the C function.
* The return trip to python will marshal this back and pull the python user data out.
*/
if (user_data_cache != NULL) {
state->in_args[user_data_cache->c_arg_index].v_pointer = closure;
}
/* Setup a GDestroyNotify callback if this method supports it along with
* a user data field. The user data field is a requirement in order
* free resources and ref counts associated with this arguments closure.
* In case a user data field is not available, show a warning giving
* explicit information and setup a dummy notification to avoid a crash
* later on in _pygi_destroy_notify_callback_closure.
*/
if (callback_cache->destroy_notify_index > 0) {
destroy_cache = _pygi_callable_cache_get_arg (callable_cache, callback_cache->destroy_notify_index);
}
if (destroy_cache) {
if (user_data_cache != NULL) {
PyGICClosure *destroy_notify = _pygi_destroy_notify_create ();
state->in_args[destroy_cache->c_arg_index].v_pointer = destroy_notify->closure;
} else {
gchar *msg = g_strdup_printf("Callables passed to %s will leak references because "
"the method does not support a user_data argument. "
"See: https://bugzilla.gnome.org/show_bug.cgi?id=685598",
callable_cache->name);
if (PyErr_WarnEx(PyExc_RuntimeWarning, msg, 2)) {
g_free(msg);
_pygi_invoke_closure_free(closure);
return FALSE;
}
g_free(msg);
state->in_args[destroy_cache->c_arg_index].v_pointer = _pygi_destroy_notify_dummy;
}
}
/* Use the PyGIClosure as data passed to cleanup for GI_SCOPE_TYPE_CALL. */
*cleanup_data = closure;
return TRUE;
}
static PyObject *
_pygi_marshal_to_py_interface_callback (PyGIInvokeState *state,
PyGICallableCache *callable_cache,
PyGIArgCache *arg_cache,
GIArgument *arg)
{
PyObject *py_obj = NULL;
PyErr_Format (PyExc_NotImplementedError,
"Marshalling a callback to PyObject is not supported");
return py_obj;
}
static void
_callback_cache_free_func (PyGICallbackCache *cache)
{
if (cache != NULL) {
if (cache->interface_info != NULL)
g_base_info_unref ( (GIBaseInfo *)cache->interface_info);
g_slice_free (PyGICallbackCache, cache);
}
}
static void
_pygi_marshal_cleanup_from_py_interface_callback (PyGIInvokeState *state,
PyGIArgCache *arg_cache,
PyObject *py_arg,
gpointer data,
gboolean was_processed)
{
PyGICallbackCache *callback_cache = (PyGICallbackCache *)arg_cache;
if (was_processed && callback_cache->scope == GI_SCOPE_TYPE_CALL) {
_pygi_invoke_closure_free (data);
}
}
static void
_arg_cache_from_py_interface_callback_setup (PyGIArgCache *arg_cache,
PyGICallableCache *callable_cache)
{
PyGICallbackCache *callback_cache = (PyGICallbackCache *)arg_cache;
if (callback_cache->user_data_index >= 0) {
PyGIArgCache *user_data_arg_cache = _arg_cache_alloc ();
user_data_arg_cache->meta_type = PYGI_META_ARG_TYPE_CHILD_WITH_PYARG;
user_data_arg_cache->direction = PYGI_DIRECTION_FROM_PYTHON;
user_data_arg_cache->has_default = TRUE; /* always allow user data with a NULL default. */
_pygi_callable_cache_set_arg (callable_cache, callback_cache->user_data_index,
user_data_arg_cache);
}
if (callback_cache->destroy_notify_index >= 0) {
PyGIArgCache *destroy_arg_cache = _arg_cache_alloc ();
destroy_arg_cache->meta_type = PYGI_META_ARG_TYPE_CHILD;
destroy_arg_cache->direction = PYGI_DIRECTION_FROM_PYTHON;
_pygi_callable_cache_set_arg (callable_cache, callback_cache->destroy_notify_index,
destroy_arg_cache);
}
arg_cache->from_py_marshaller = _pygi_marshal_from_py_interface_callback;
arg_cache->from_py_cleanup = _pygi_marshal_cleanup_from_py_interface_callback;
}
static gboolean
pygi_arg_callback_setup_from_info (PyGICallbackCache *arg_cache,
GITypeInfo *type_info,
GIArgInfo *arg_info, /* may be null */
GITransfer transfer,
PyGIDirection direction,
GIInterfaceInfo *iface_info,
PyGICallableCache *callable_cache)
{
gssize child_offset = 0;
if (!pygi_arg_base_setup ((PyGIArgCache *)arg_cache,
type_info,
arg_info,
transfer,
direction)) {
return FALSE;
}
if (direction & PYGI_DIRECTION_TO_PYTHON) {
((PyGIArgCache *)arg_cache)->to_py_marshaller = _pygi_marshal_to_py_interface_callback;
}
if (callable_cache != NULL)
child_offset =
(callable_cache->function_type == PYGI_FUNCTION_TYPE_METHOD ||
callable_cache->function_type == PYGI_FUNCTION_TYPE_VFUNC) ? 1: 0;
( (PyGIArgCache *)arg_cache)->destroy_notify = (GDestroyNotify)_callback_cache_free_func;
arg_cache->user_data_index = g_arg_info_get_closure (arg_info);
if (arg_cache->user_data_index != -1)
arg_cache->user_data_index += child_offset;
arg_cache->destroy_notify_index = g_arg_info_get_destroy (arg_info);
if (arg_cache->destroy_notify_index != -1)
arg_cache->destroy_notify_index += child_offset;
arg_cache->scope = g_arg_info_get_scope (arg_info);
g_base_info_ref( (GIBaseInfo *)iface_info);
arg_cache->interface_info = iface_info;
if (direction & PYGI_DIRECTION_FROM_PYTHON)
_arg_cache_from_py_interface_callback_setup ((PyGIArgCache *)arg_cache, callable_cache);
return TRUE;
}
PyGIArgCache *
pygi_arg_callback_new_from_info (GITypeInfo *type_info,
GIArgInfo *arg_info, /* may be null */
GITransfer transfer,
PyGIDirection direction,
GIInterfaceInfo *iface_info,
PyGICallableCache *callable_cache)
{
gboolean res = FALSE;
PyGICallbackCache *callback_cache;
callback_cache = g_slice_new0 (PyGICallbackCache);
if (callback_cache == NULL)
return NULL;
res = pygi_arg_callback_setup_from_info (callback_cache,
type_info,
arg_info,
transfer,
direction,
iface_info,
callable_cache);
if (res) {
return (PyGIArgCache *)callback_cache;
} else {
_pygi_arg_cache_free ((PyGIArgCache *)callback_cache);
return NULL;
}
}
......@@ -12,9 +12,7 @@
* 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/>.
*/
#ifndef __PYGI_CLOSURE_H__
......@@ -24,6 +22,8 @@
#include <girffi.h>
#include <ffi.h>
#include "pygi-cache.h"
G_BEGIN_DECLS
......@@ -52,6 +52,13 @@ PyGICClosure* _pygi_make_native_closure (GICallableInfo* info,
PyObject *function,
gpointer user_data);
PyGIArgCache *pygi_arg_callback_new_from_info (GITypeInfo *type_info,
GIArgInfo *arg_info, /* may be null */
GITransfer transfer,
PyGIDirection direction,
GIInterfaceInfo *iface_info,
PyGICallableCache *callable_cache);
G_END_DECLS
#endif /* __PYGI_CLOSURE_H__ */
......@@ -233,20 +233,6 @@ _pygi_marshal_cleanup_to_py_interface_object (PyGIInvokeState *state,
g_object_unref (G_OBJECT(data));
}
void
_pygi_marshal_cleanup_from_py_interface_callback (PyGIInvokeState *state,
PyGIArgCache *arg_cache,
PyObject *py_arg,
gpointer data,
gboolean was_processed)
{
PyGICallbackCache *callback_cache = (PyGICallbackCache *)arg_cache;
if (was_processed && callback_cache->scope == GI_SCOPE_TYPE_CALL) {
_pygi_invoke_closure_free (data);
}
}
void
_pygi_marshal_cleanup_from_py_interface_struct_gvalue (PyGIInvokeState *state,
PyGIArgCache *arg_cache,
......
......@@ -65,11 +65,6 @@ void _pygi_marshal_cleanup_to_py_interface_object (PyGIInvokeState *stat
PyObject *dummy,
gpointer data,
gboolean was_processed);
void _pygi_marshal_cleanup_from_py_interface_callback (PyGIInvokeState *state,
PyGIArgCache *arg_cache,
PyObject *py_arg,
gpointer data,
gboolean was_processed);
G_END_DECLS
#endif /* __PYGI_MARSHAL_CLEANUP_H__ */
......@@ -140,166 +140,6 @@ _is_union_member (GIInterfaceInfo *interface_info, PyObject *py_arg) {
return is_member;
}
/* _pygi_destroy_notify_dummy:
*
* Dummy method used in the occasion when a method has a GDestroyNotify
* argument without user data.
*/
static void
_pygi_destroy_notify_dummy (gpointer data) {
}
static PyGICClosure *global_destroy_notify;
static void
_pygi_destroy_notify_callback_closure (ffi_cif *cif,
void *result,
void **args,
void *data)
{
PyGICClosure *info = * (void**) (args[0]);
g_assert (info);
_pygi_invoke_closure_free (info);
}
/* _pygi_destroy_notify_create:
*
* Method used in the occasion when a method has a GDestroyNotify
* argument with user data.
*/
static PyGICClosure*
_pygi_destroy_notify_create (void)
{
if (!global_destroy_notify) {
PyGICClosure *destroy_notify = g_slice_new0 (PyGICClosure);
GIBaseInfo* glib_destroy_notify;
g_assert (destroy_notify);
glib_destroy_notify = g_irepository_find_by_name (NULL, "GLib", "DestroyNotify");
g_assert (glib_destroy_notify != NULL);
g_assert (g_base_info_get_type (glib_destroy_notify) == GI_INFO_TYPE_CALLBACK);
destroy_notify->closure = g_callable_info_prepare_closure ( (GICallableInfo*) glib_destroy_notify,
&destroy_notify->cif,
_pygi_destroy_notify_callback_closure,
NULL);
global_destroy_notify = destroy_notify;
}
return global_destroy_notify;
}
gboolean
_pygi_marshal_from_py_interface_callback (PyGIInvokeState *state,
PyGICallableCache *callable_cache,
PyGIArgCache *arg_cache,
PyObject *py_arg,
GIArgument *arg,
gpointer *cleanup_data)
{
GICallableInfo *callable_info;