From 4dcaa2b988239e01224994098c3e7cbe8b455fe0 Mon Sep 17 00:00:00 2001 From: Simon Feltman Date: Sat, 12 Oct 2013 16:40:58 -0700 Subject: cache refactoring: Move GObject arg setup and marshaling into new file Move GObject argument cache setup and marshaling fragments into isolated file: pygi-object.c. Break GIInterfaceCache creation and setup into API for interface based argument cache usage. https://bugzilla.gnome.org/show_bug.cgi?id=709700 --- gi/Makefile.am | 2 + gi/pygi-argument.c | 7 +- gi/pygi-cache.c | 183 ++++++++++++++----------------- gi/pygi-cache.h | 7 ++ gi/pygi-marshal-cleanup.c | 27 ----- gi/pygi-marshal-cleanup.h | 10 -- gi/pygi-marshal-from-py.c | 123 --------------------- gi/pygi-marshal-from-py.h | 12 -- gi/pygi-marshal-to-py.c | 31 ------ gi/pygi-marshal-to-py.h | 8 -- gi/pygi-object.c | 273 ++++++++++++++++++++++++++++++++++++++++++++++ gi/pygi-object.h | 46 ++++++++ 12 files changed, 416 insertions(+), 313 deletions(-) create mode 100644 gi/pygi-object.c create mode 100644 gi/pygi-object.h diff --git a/gi/Makefile.am b/gi/Makefile.am index d19a19d6..3d153f4d 100644 --- a/gi/Makefile.am +++ b/gi/Makefile.am @@ -111,6 +111,8 @@ _gi_la_SOURCES = \ pygi-array.h \ pygi-error.c \ pygi-error.h \ + pygi-object.c \ + pygi-object.h \ pygi-hashtable.c \ pygi-hashtable.h _gi_la_CFLAGS = \ diff --git a/gi/pygi-argument.c b/gi/pygi-argument.c index c58afa37..4624e0f8 100644 --- a/gi/pygi-argument.c +++ b/gi/pygi-argument.c @@ -34,6 +34,7 @@ #include "pygi-marshal-from-py.h" #include "pygi-marshal-to-py.h" #include "pygi-basictype.h" +#include "pygi-object.h" static gboolean @@ -1063,7 +1064,7 @@ array_success: case GI_INFO_TYPE_INTERFACE: case GI_INFO_TYPE_OBJECT: /* An error within this call will result in a NULL arg */ - _pygi_marshal_from_py_gobject_out_arg (object, &arg, transfer); + pygi_arg_gobject_out_arg_from_py (object, &arg, transfer); break; default: @@ -1436,10 +1437,10 @@ _pygi_argument_to_object (GIArgument *arg, transfer == GI_TRANSFER_NOTHING && g_object_is_floating (arg->v_pointer)) { g_object_ref (arg->v_pointer); - object = _pygi_marshal_to_py_object (arg, GI_TRANSFER_EVERYTHING); + object = pygi_arg_gobject_to_py (arg, GI_TRANSFER_EVERYTHING); g_object_force_floating (arg->v_pointer); } else { - object = _pygi_marshal_to_py_object (arg, transfer); + object = pygi_arg_gobject_to_py (arg, transfer); } break; diff --git a/gi/pygi-cache.c b/gi/pygi-cache.c index aded0256..f26df8c6 100644 --- a/gi/pygi-cache.c +++ b/gi/pygi-cache.c @@ -33,15 +33,7 @@ #include "pygi-array.h" #include "pygi-closure.h" #include "pygi-error.h" - - -PyGIArgCache * _arg_cache_new_for_interface (GIInterfaceInfo *iface_info, - GITypeInfo *type_info, - GIArgInfo *arg_info, - GITransfer transfer, - PyGIDirection direction, - /* will be removed */ - PyGICallableCache *callable_cache); +#include "pygi-object.h" /* _arg_info_default_value @@ -100,32 +92,6 @@ pygi_arg_base_setup (PyGIArgCache *arg_cache, return TRUE; } - -gboolean -pygi_arg_interface_setup (PyGIInterfaceCache *iface_cache, - GITypeInfo *type_info, - GIArgInfo *arg_info, /* may be NULL for return arguments */ - GITransfer transfer, - PyGIDirection direction, - GIInterfaceInfo *iface_info) -{ - if (!pygi_arg_base_setup ((PyGIArgCache *)iface_cache, - type_info, - arg_info, - transfer, - direction)) { - return FALSE; - } - - g_base_info_ref ( (GIBaseInfo *)iface_info); - iface_cache->interface_info = iface_info; - iface_cache->arg_cache.type_tag = GI_TYPE_TAG_INTERFACE; - - return TRUE; -} - - -/* cleanup */ void _pygi_arg_cache_free (PyGIArgCache *cache) { @@ -140,6 +106,26 @@ _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_slice_free (PyGICallableCache, cache); +} + + +/* PyGIInterfaceCache */ + static void _interface_cache_free_func (PyGIInterfaceCache *cache) { @@ -153,50 +139,70 @@ _interface_cache_free_func (PyGIInterfaceCache *cache) } } - -static void -_sequence_cache_free_func (PyGISequenceCache *cache) +gboolean +pygi_arg_interface_setup (PyGIInterfaceCache *iface_cache, + GITypeInfo *type_info, + GIArgInfo *arg_info, /* may be NULL for return arguments */ + GITransfer transfer, + PyGIDirection direction, + GIInterfaceInfo *iface_info) { - if (cache != NULL) { - _pygi_arg_cache_free (cache->item_cache); - g_slice_free (PyGISequenceCache, cache); + if (!pygi_arg_base_setup ((PyGIArgCache *)iface_cache, + type_info, + arg_info, + transfer, + direction)) { + return FALSE; } -} -void -_pygi_callable_cache_free (PyGICallableCache *cache) -{ - if (cache == NULL) - return; + ( (PyGIArgCache *)iface_cache)->destroy_notify = (GDestroyNotify)_interface_cache_free_func; - 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); + g_base_info_ref ( (GIBaseInfo *)iface_info); + iface_cache->interface_info = iface_info; + iface_cache->arg_cache.type_tag = GI_TYPE_TAG_INTERFACE; + iface_cache->type_name = _pygi_g_base_info_get_fullname (iface_info); + iface_cache->g_type = g_registered_type_info_get_g_type ( (GIRegisteredTypeInfo *)iface_info); + iface_cache->py_type = _pygi_type_import_by_gi_info ( (GIBaseInfo *) iface_info); - if (cache->return_cache != NULL) - _pygi_arg_cache_free (cache->return_cache); + if (iface_cache->py_type == NULL) { + return FALSE; + } - g_slice_free (PyGICallableCache, cache); + return TRUE; } -/* cache generation */ - -static PyGIInterfaceCache * -_interface_cache_new (GIInterfaceInfo *iface_info) +PyGIArgCache * +pygi_arg_interface_new_from_info (GITypeInfo *type_info, + GIArgInfo *arg_info, /* may be NULL for return arguments */ + GITransfer transfer, + PyGIDirection direction, + GIInterfaceInfo *iface_info) { PyGIInterfaceCache *ic; ic = g_slice_new0 (PyGIInterfaceCache); - ( (PyGIArgCache *)ic)->destroy_notify = (GDestroyNotify)_interface_cache_free_func; - ic->g_type = g_registered_type_info_get_g_type ( (GIRegisteredTypeInfo *)iface_info); - ic->py_type = _pygi_type_import_by_gi_info ( (GIBaseInfo *) iface_info); - - if (ic->py_type == NULL) + if (!pygi_arg_interface_setup (ic, + type_info, + arg_info, + transfer, + direction, + iface_info)) { + _pygi_arg_cache_free ((PyGIArgCache *)ic); return NULL; + } - ic->type_name = _pygi_g_base_info_get_fullname (iface_info); - return ic; + return (PyGIArgCache *)ic; +} + +/* PyGISequenceCache */ + +static void +_sequence_cache_free_func (PyGISequenceCache *cache) +{ + if (cache != NULL) { + _pygi_arg_cache_free (cache->item_cache); + g_slice_free (PyGISequenceCache, cache); + } } gboolean @@ -286,22 +292,6 @@ _arg_cache_to_py_interface_struct_setup (PyGIArgCache *arg_cache, arg_cache->to_py_cleanup = _pygi_marshal_cleanup_to_py_interface_struct_foreign; } -static void -_arg_cache_from_py_interface_object_setup (PyGIArgCache *arg_cache, - GITransfer transfer) -{ - arg_cache->from_py_marshaller = _pygi_marshal_from_py_interface_object; - arg_cache->from_py_cleanup = _pygi_marshal_cleanup_from_py_interface_object; -} - -static void -_arg_cache_to_py_interface_object_setup (PyGIArgCache *arg_cache, - GITransfer transfer) -{ - arg_cache->to_py_marshaller = _pygi_marshal_to_py_interface_object_cache_adapter; - arg_cache->to_py_cleanup = _pygi_marshal_cleanup_to_py_interface_object; -} - static void _arg_cache_from_py_interface_enum_setup (PyGIArgCache *arg_cache, GITransfer transfer) @@ -331,7 +321,7 @@ _arg_cache_to_py_interface_flags_setup (PyGIArgCache *arg_cache, } -PyGIArgCache * +static PyGIArgCache * _arg_cache_new_for_interface (GIInterfaceInfo *iface_info, GITypeInfo *type_info, GIArgInfo *arg_info, @@ -354,11 +344,22 @@ _arg_cache_new_for_interface (GIInterfaceInfo *iface_info, iface_info, callable_cache); } + case GI_INFO_TYPE_OBJECT: + case GI_INFO_TYPE_INTERFACE: + return pygi_arg_gobject_new_from_info (type_info, + arg_info, + transfer, + direction, + iface_info); default: ; /* pass through to old model of setup */ } - arg_cache = (PyGIArgCache *)_interface_cache_new (iface_info); + arg_cache = pygi_arg_interface_new_from_info (type_info, + arg_info, + transfer, + direction, + iface_info); if (arg_cache == NULL) return NULL; @@ -383,15 +384,6 @@ _arg_cache_new_for_interface (GIInterfaceInfo *iface_info, iface_info, transfer); break; - case GI_INFO_TYPE_OBJECT: - case GI_INFO_TYPE_INTERFACE: - if (direction & PYGI_DIRECTION_FROM_PYTHON) - _arg_cache_from_py_interface_object_setup (arg_cache, transfer); - - if (direction & PYGI_DIRECTION_TO_PYTHON) - _arg_cache_to_py_interface_object_setup (arg_cache, transfer); - - break; case GI_INFO_TYPE_ENUM: if (direction & PYGI_DIRECTION_FROM_PYTHON) _arg_cache_from_py_interface_enum_setup (arg_cache, transfer); @@ -412,13 +404,6 @@ _arg_cache_new_for_interface (GIInterfaceInfo *iface_info, g_assert_not_reached (); } - pygi_arg_interface_setup ((PyGIInterfaceCache *)arg_cache, - type_info, - arg_info, - transfer, - direction, - iface_info); - return arg_cache; } diff --git a/gi/pygi-cache.h b/gi/pygi-cache.h index d20cb9f9..3cbe2b60 100644 --- a/gi/pygi-cache.h +++ b/gi/pygi-cache.h @@ -214,6 +214,13 @@ pygi_arg_sequence_setup (PyGISequenceCache *sc, GITransfer transfer, PyGIDirection direction); +PyGIArgCache * +pygi_arg_interface_new_from_info (GITypeInfo *type_info, + GIArgInfo *arg_info, /* may be NULL for return arguments */ + GITransfer transfer, + PyGIDirection direction, + GIInterfaceInfo *iface_info); + PyGIArgCache * _arg_cache_alloc (void); PyGIArgCache * _arg_cache_new (GITypeInfo *type_info, GIArgInfo *arg_info, diff --git a/gi/pygi-marshal-cleanup.c b/gi/pygi-marshal-cleanup.c index 0376e37b..0bbfab68 100644 --- a/gi/pygi-marshal-cleanup.c +++ b/gi/pygi-marshal-cleanup.c @@ -206,33 +206,6 @@ pygi_marshal_cleanup_args_to_py_parameter_fail (PyGIInvokeState *state, state->failed = TRUE; } -void -_pygi_marshal_cleanup_from_py_interface_object (PyGIInvokeState *state, - PyGIArgCache *arg_cache, - PyObject *py_arg, - gpointer data, - gboolean was_processed) -{ - /* If we processed the parameter but fail before invoking the method, - we need to remove the ref we added */ - if (was_processed && state->failed && data != NULL && - arg_cache->transfer == GI_TRANSFER_EVERYTHING) - g_object_unref (G_OBJECT(data)); -} - -void -_pygi_marshal_cleanup_to_py_interface_object (PyGIInvokeState *state, - PyGIArgCache *arg_cache, - PyObject *dummy, - gpointer data, - gboolean was_processed) -{ - /* If we error out and the object is not marshalled into a PyGObject - we must take care of removing the ref */ - if (!was_processed && arg_cache->transfer == GI_TRANSFER_EVERYTHING) - g_object_unref (G_OBJECT(data)); -} - void _pygi_marshal_cleanup_from_py_interface_struct_gvalue (PyGIInvokeState *state, PyGIArgCache *arg_cache, diff --git a/gi/pygi-marshal-cleanup.h b/gi/pygi-marshal-cleanup.h index 530f5f31..2463cab1 100644 --- a/gi/pygi-marshal-cleanup.h +++ b/gi/pygi-marshal-cleanup.h @@ -55,16 +55,6 @@ void _pygi_marshal_cleanup_to_py_interface_struct_foreign (PyGIInvokeState *stat PyObject *dummy, gpointer data, gboolean was_processed); -void _pygi_marshal_cleanup_from_py_interface_object (PyGIInvokeState *state, - PyGIArgCache *arg_cache, - PyObject *py_arg, - gpointer data, - gboolean was_processed); -void _pygi_marshal_cleanup_to_py_interface_object (PyGIInvokeState *state, - PyGIArgCache *arg_cache, - PyObject *dummy, - gpointer data, - gboolean was_processed); G_END_DECLS #endif /* __PYGI_MARSHAL_CLEANUP_H__ */ diff --git a/gi/pygi-marshal-from-py.c b/gi/pygi-marshal-from-py.c index 92c89c84..ba798591 100644 --- a/gi/pygi-marshal-from-py.c +++ b/gi/pygi-marshal-from-py.c @@ -298,40 +298,6 @@ _pygi_marshal_from_py_interface_boxed (PyGIInvokeState *state, return FALSE; } -gboolean -_pygi_marshal_from_py_interface_object (PyGIInvokeState *state, - PyGICallableCache *callable_cache, - PyGIArgCache *arg_cache, - PyObject *py_arg, - GIArgument *arg, - gpointer *cleanup_data) -{ - gboolean res = FALSE; - - if (py_arg == Py_None) { - arg->v_pointer = NULL; - return TRUE; - } - - if (!PyObject_IsInstance (py_arg, ( (PyGIInterfaceCache *)arg_cache)->py_type)) { - PyObject *module = PyObject_GetAttrString(py_arg, "__module__"); - - PyErr_Format (PyExc_TypeError, "argument %s: Expected %s, but got %s%s%s", - arg_cache->arg_name ? arg_cache->arg_name : "self", - ( (PyGIInterfaceCache *)arg_cache)->type_name, - module ? PYGLIB_PyUnicode_AsString(module) : "", - module ? "." : "", - py_arg->ob_type->tp_name); - if (module) - Py_DECREF (module); - return FALSE; - } - - res = _pygi_marshal_from_py_gobject (py_arg, arg, arg_cache->transfer); - *cleanup_data = arg->v_pointer; - return res; -} - gboolean _pygi_marshal_from_py_interface_union (PyGIInvokeState *state, PyGICallableCache *callable_cache, @@ -345,95 +311,6 @@ _pygi_marshal_from_py_interface_union (PyGIInvokeState *state, return FALSE; } -/* _pygi_marshal_from_py_gobject: - * py_arg: (in): - * arg: (out): - */ -gboolean -_pygi_marshal_from_py_gobject (PyObject *py_arg, /*in*/ - GIArgument *arg, /*out*/ - GITransfer transfer) { - GObject *gobj; - - if (py_arg == Py_None) { - arg->v_pointer = NULL; - return TRUE; - } - - if (!pygobject_check (py_arg, &PyGObject_Type)) { - PyObject *repr = PyObject_Repr (py_arg); - PyErr_Format(PyExc_TypeError, "expected GObject but got %s", - PYGLIB_PyUnicode_AsString (repr)); - Py_DECREF (repr); - return FALSE; - } - - gobj = pygobject_get (py_arg); - if (transfer == GI_TRANSFER_EVERYTHING) { - /* For transfer everything, add a new ref that the callee will take ownership of. - * Pythons existing ref to the GObject will be managed with the PyGObject wrapper. - */ - g_object_ref (gobj); - } - - arg->v_pointer = gobj; - return TRUE; -} - -/* _pygi_marshal_from_py_gobject_out_arg: - * py_arg: (in): - * arg: (out): - * - * A specialization for marshaling Python GObjects used for out/return values - * from a Python implemented vfuncs, signals, or an assignment to a GObject property. - */ -gboolean -_pygi_marshal_from_py_gobject_out_arg (PyObject *py_arg, /*in*/ - GIArgument *arg, /*out*/ - GITransfer transfer) { - GObject *gobj; - if (!_pygi_marshal_from_py_gobject (py_arg, arg, transfer)) { - return FALSE; - } - - /* HACK: At this point the basic marshaling of the GObject was successful - * but we add some special case hacks for vfunc returns due to buggy APIs: - * https://bugzilla.gnome.org/show_bug.cgi?id=693393 - */ - gobj = arg->v_pointer; - if (py_arg->ob_refcnt == 1 && gobj->ref_count == 1) { - /* If both object ref counts are only 1 at this point (the reference held - * in a return tuple), we assume the GObject will be free'd before reaching - * its target and become invalid. So instead of getting invalid object errors - * we add a new GObject ref. - */ - g_object_ref (gobj); - - if (((PyGObject *)py_arg)->private_flags.flags & PYGOBJECT_GOBJECT_WAS_FLOATING) { - /* - * We want to re-float instances that were floating and the Python - * wrapper assumed ownership. With the additional caveat that there - * are not any strong references beyond the return tuple. - */ - g_object_force_floating (gobj); - - } else { - PyObject *repr = PyObject_Repr (py_arg); - gchar *msg = g_strdup_printf ("Expecting to marshal a borrowed reference for %s, " - "but nothing in Python is holding a reference to this object. " - "See: https://bugzilla.gnome.org/show_bug.cgi?id=687522", - PYGLIB_PyUnicode_AsString(repr)); - Py_DECREF (repr); - if (PyErr_WarnEx (PyExc_RuntimeWarning, msg, 2)) { - g_free (msg); - return FALSE; - } - g_free (msg); - } - } - - return TRUE; -} /* _pygi_marshal_from_py_gvalue: * py_arg: (in): diff --git a/gi/pygi-marshal-from-py.h b/gi/pygi-marshal-from-py.h index 0172c197..4b59601e 100644 --- a/gi/pygi-marshal-from-py.h +++ b/gi/pygi-marshal-from-py.h @@ -57,12 +57,6 @@ gboolean _pygi_marshal_from_py_interface_boxed (PyGIInvokeState *state, PyObject *py_arg, GIArgument *arg, gpointer *cleanup_data); -gboolean _pygi_marshal_from_py_interface_object (PyGIInvokeState *state, - PyGICallableCache *callable_cache, - PyGIArgCache *arg_cache, - PyObject *py_arg, - GIArgument *arg, - gpointer *cleanup_data); gboolean _pygi_marshal_from_py_interface_union (PyGIInvokeState *state, PyGICallableCache *callable_cache, PyGIArgCache *arg_cache, @@ -71,12 +65,6 @@ gboolean _pygi_marshal_from_py_interface_union (PyGIInvokeState *state, gpointer *cleanup_data); /* Simplified marshalers shared between vfunc/closure and direct function calls. */ -gboolean _pygi_marshal_from_py_gobject (PyObject *py_arg, /*in*/ - GIArgument *arg, /*out*/ - GITransfer transfer); -gboolean _pygi_marshal_from_py_gobject_out_arg (PyObject *py_arg, /*in*/ - GIArgument *arg, /*out*/ - GITransfer transfer); gboolean _pygi_marshal_from_py_gvalue (PyObject *py_arg, /*in*/ GIArgument *arg, /*out*/ diff --git a/gi/pygi-marshal-to-py.c b/gi/pygi-marshal-to-py.c index 69a7c1ae..536193c5 100644 --- a/gi/pygi-marshal-to-py.c +++ b/gi/pygi-marshal-to-py.c @@ -193,15 +193,6 @@ _pygi_marshal_to_py_interface_boxed (PyGIInvokeState *state, return py_obj; } -PyObject * -_pygi_marshal_to_py_interface_object_cache_adapter (PyGIInvokeState *state, - PyGICallableCache *callable_cache, - PyGIArgCache *arg_cache, - GIArgument *arg) -{ - return _pygi_marshal_to_py_object(arg, arg_cache->transfer); -} - PyObject * _pygi_marshal_to_py_interface_union (PyGIInvokeState *state, PyGICallableCache *callable_cache, @@ -215,28 +206,6 @@ _pygi_marshal_to_py_interface_union (PyGIInvokeState *state, return py_obj; } -PyObject * -_pygi_marshal_to_py_object (GIArgument *arg, GITransfer transfer) { - PyObject *pyobj; - - if (arg->v_pointer == NULL) { - pyobj = Py_None; - Py_INCREF (pyobj); - - } else if (G_IS_PARAM_SPEC(arg->v_pointer)) { - pyobj = pyg_param_spec_new (arg->v_pointer); - if (transfer == GI_TRANSFER_EVERYTHING) - g_param_spec_unref (arg->v_pointer); - - } else { - pyobj = pygobject_new_full (arg->v_pointer, - /*steal=*/ transfer == GI_TRANSFER_EVERYTHING, - /*type=*/ NULL); - } - - return pyobj; -} - PyObject * _pygi_marshal_to_py_interface_struct (GIArgument *arg, GIInterfaceInfo *interface_info, diff --git a/gi/pygi-marshal-to-py.h b/gi/pygi-marshal-to-py.h index de8f85b7..aa845031 100644 --- a/gi/pygi-marshal-to-py.h +++ b/gi/pygi-marshal-to-py.h @@ -42,20 +42,12 @@ PyObject *_pygi_marshal_to_py_interface_boxed (PyGIInvokeState *state, PyGICallableCache *callable_cache, PyGIArgCache *arg_cache, GIArgument *arg); -PyObject *_pygi_marshal_to_py_interface_object_cache_adapter (PyGIInvokeState *state, - PyGICallableCache *callable_cache, - PyGIArgCache *arg_cache, - GIArgument *arg); PyObject *_pygi_marshal_to_py_interface_union (PyGIInvokeState *state, PyGICallableCache *callable_cache, PyGIArgCache *arg_cache, GIArgument *arg); /* Simplified marshalers shared between vfunc/closure and direct function calls. */ - -PyObject *_pygi_marshal_to_py_object (GIArgument *arg, - GITransfer transfer); - PyObject *_pygi_marshal_to_py_interface_struct (GIArgument *arg, GIInterfaceInfo *interface_info, GType g_type, diff --git a/gi/pygi-object.c b/gi/pygi-object.c new file mode 100644 index 00000000..4ad58b1b --- /dev/null +++ b/gi/pygi-object.c @@ -0,0 +1,273 @@ +/* -*- Mode: C; c-basic-offset: 4 -*- + * vim: tabstop=4 shiftwidth=4 expandtab + * + * Copyright (C) 2011 John (J5) Palmieri + * Copyright (C) 2014 Simon Feltman + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * 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, see . + */ + +#include +#include +#include + +#include "pygi-arg-gobject.h" +#include "pygi-private.h" +#include "pygparamspec.h" + +/* + * GObject from Python + */ + +/* _pygi_marshal_from_py_gobject: + * py_arg: (in): + * arg: (out): + */ +static gboolean +_pygi_marshal_from_py_gobject (PyObject *py_arg, /*in*/ + GIArgument *arg, /*out*/ + GITransfer transfer) { + GObject *gobj; + + if (py_arg == Py_None) { + arg->v_pointer = NULL; + return TRUE; + } + + if (!pygobject_check (py_arg, &PyGObject_Type)) { + PyObject *repr = PyObject_Repr (py_arg); + PyErr_Format(PyExc_TypeError, "expected GObject but got %s", + PYGLIB_PyUnicode_AsString (repr)); + Py_DECREF (repr); + return FALSE; + } + + gobj = pygobject_get (py_arg); + if (transfer == GI_TRANSFER_EVERYTHING) { + /* For transfer everything, add a new ref that the callee will take ownership of. + * Pythons existing ref to the GObject will be managed with the PyGObject wrapper. + */ + g_object_ref (gobj); + } + + arg->v_pointer = gobj; + return TRUE; +} + +/* pygi_arg_gobject_out_arg_from_py: + * py_arg: (in): + * arg: (out): + * + * A specialization for marshaling Python GObjects used for out/return values + * from a Python implemented vfuncs, signals, or an assignment to a GObject property. + */ +gboolean +pygi_arg_gobject_out_arg_from_py (PyObject *py_arg, /*in*/ + GIArgument *arg, /*out*/ + GITransfer transfer) { + GObject *gobj; + if (!_pygi_marshal_from_py_gobject (py_arg, arg, transfer)) { + return FALSE; + } + + /* HACK: At this point the basic marshaling of the GObject was successful + * but we add some special case hacks for vfunc returns due to buggy APIs: + * https://bugzilla.gnome.org/show_bug.cgi?id=693393 + */ + gobj = arg->v_pointer; + if (py_arg->ob_refcnt == 1 && gobj->ref_count == 1) { + /* If both object ref counts are only 1 at this point (the reference held + * in a return tuple), we assume the GObject will be free'd before reaching + * its target and become invalid. So instead of getting invalid object errors + * we add a new GObject ref. + */ + g_object_ref (gobj); + + if (((PyGObject *)py_arg)->private_flags.flags & PYGOBJECT_GOBJECT_WAS_FLOATING) { + /* + * We want to re-float instances that were floating and the Python + * wrapper assumed ownership. With the additional caveat that there + * are not any strong references beyond the return tuple. + */ + g_object_force_floating (gobj); + + } else { + PyObject *repr = PyObject_Repr (py_arg); + gchar *msg = g_strdup_printf ("Expecting to marshal a borrowed reference for %s, " + "but nothing in Python is holding a reference to this object. " + "See: https://bugzilla.gnome.org/show_bug.cgi?id=687522", + PYGLIB_PyUnicode_AsString(repr)); + Py_DECREF (repr); + if (PyErr_WarnEx (PyExc_RuntimeWarning, msg, 2)) { + g_free (msg); + return FALSE; + } + g_free (msg); + } + } + + return TRUE; +} + +static gboolean +_pygi_marshal_from_py_interface_object (PyGIInvokeState *state, + PyGICallableCache *callable_cache, + PyGIArgCache *arg_cache, + PyObject *py_arg, + GIArgument *arg, + gpointer *cleanup_data) +{ + gboolean res = FALSE; + + if (py_arg == Py_None) { + arg->v_pointer = NULL; + return TRUE; + } + + if (!PyObject_IsInstance (py_arg, ( (PyGIInterfaceCache *)arg_cache)->py_type)) { + PyObject *module = PyObject_GetAttrString(py_arg, "__module__"); + + PyErr_Format (PyExc_TypeError, "argument %s: Expected %s, but got %s%s%s", + arg_cache->arg_name ? arg_cache->arg_name : "self", + ( (PyGIInterfaceCache *)arg_cache)->type_name, + module ? PYGLIB_PyUnicode_AsString(module) : "", + module ? "." : "", + py_arg->ob_type->tp_name); + if (module) + Py_DECREF (module); + return FALSE; + } + + res = _pygi_marshal_from_py_gobject (py_arg, arg, arg_cache->transfer); + *cleanup_data = arg->v_pointer; + return res; +} + +static void +_pygi_marshal_cleanup_from_py_interface_object (PyGIInvokeState *state, + PyGIArgCache *arg_cache, + PyObject *py_arg, + gpointer data, + gboolean was_processed) +{ + /* If we processed the parameter but fail before invoking the method, + we need to remove the ref we added */ + if (was_processed && state->failed && data != NULL && + arg_cache->transfer == GI_TRANSFER_EVERYTHING) + g_object_unref (G_OBJECT(data)); +} + + +/* + * GObject to Python + */ + +PyObject * +pygi_arg_gobject_to_py (GIArgument *arg, GITransfer transfer) { + PyObject *pyobj; + + if (arg->v_pointer == NULL) { + pyobj = Py_None; + Py_INCREF (pyobj); + + } else if (G_IS_PARAM_SPEC(arg->v_pointer)) { + pyobj = pyg_param_spec_new (arg->v_pointer); + if (transfer == GI_TRANSFER_EVERYTHING) + g_param_spec_unref (arg->v_pointer); + + } else { + pyobj = pygobject_new_full (arg->v_pointer, + /*steal=*/ transfer == GI_TRANSFER_EVERYTHING, + /*type=*/ NULL); + } + + return pyobj; +} + +static PyObject * +_pygi_marshal_to_py_interface_object_cache_adapter (PyGIInvokeState *state, + PyGICallableCache *callable_cache, + PyGIArgCache *arg_cache, + GIArgument *arg) +{ + return pygi_arg_gobject_to_py(arg, arg_cache->transfer); +} + +static void +_pygi_marshal_cleanup_to_py_interface_object (PyGIInvokeState *state, + PyGIArgCache *arg_cache, + PyObject *dummy, + gpointer data, + gboolean was_processed) +{ + /* If we error out and the object is not marshalled into a PyGObject + we must take care of removing the ref */ + if (!was_processed && arg_cache->transfer == GI_TRANSFER_EVERYTHING) + g_object_unref (G_OBJECT(data)); +} + +static gboolean +pygi_arg_gobject_setup_from_info (PyGIArgCache *arg_cache, + GITypeInfo *type_info, + GIArgInfo *arg_info, + GITransfer transfer, + PyGIDirection direction) +{ + /* NOTE: usage of pygi_arg_interface_new_from_info already calls + * pygi_arg_interface_setup so no need to do it here. + */ + + if (direction & PYGI_DIRECTION_FROM_PYTHON) { + arg_cache->from_py_marshaller = _pygi_marshal_from_py_interface_object; + arg_cache->from_py_cleanup = _pygi_marshal_cleanup_from_py_interface_object; + } + + if (direction & PYGI_DIRECTION_TO_PYTHON) { + arg_cache->to_py_marshaller = _pygi_marshal_to_py_interface_object_cache_adapter; + arg_cache->to_py_cleanup = _pygi_marshal_cleanup_to_py_interface_object; + } + + return TRUE; +} + +PyGIArgCache * +pygi_arg_gobject_new_from_info (GITypeInfo *type_info, + GIArgInfo *arg_info, + GITransfer transfer, + PyGIDirection direction, + GIInterfaceInfo *iface_info) +{ + gboolean res = FALSE; + PyGIArgCache *cache = NULL; + + cache = pygi_arg_interface_new_from_info (type_info, + arg_info, + transfer, + direction, + iface_info); + if (cache == NULL) + return NULL; + + res = pygi_arg_gobject_setup_from_info (cache, + type_info, + arg_info, + transfer, + direction); + if (res) { + return cache; + } else { + _pygi_arg_cache_free (cache); + return NULL; + } +} diff --git a/gi/pygi-object.h b/gi/pygi-object.h new file mode 100644 index 00000000..4a8800e3 --- /dev/null +++ b/gi/pygi-object.h @@ -0,0 +1,46 @@ +/* -*- Mode: C; c-basic-offset: 4 -*- + * vim: tabstop=4 shiftwidth=4 expandtab + * + * Copyright (C) 2014 Simon Feltman + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * 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, see . + */ + +#ifndef __PYGI_OBJECT_H__ +#define __PYGI_OBJECT_H__ + +#include +#include "pygi-cache.h" + +G_BEGIN_DECLS + +gboolean +pygi_arg_gobject_out_arg_from_py (PyObject *py_arg, /* in */ + GIArgument *arg, /* out */ + GITransfer transfer); + +PyObject * +pygi_arg_gobject_to_py (GIArgument *arg, + GITransfer transfer); + +PyGIArgCache * +pygi_arg_gobject_new_from_info (GITypeInfo *type_info, + GIArgInfo *arg_info, /* may be null */ + GITransfer transfer, + PyGIDirection direction, + GIInterfaceInfo *iface_info); + +G_END_DECLS + +#endif /*__PYGI_OBJECT_H__*/ -- cgit v1.2.1