diff options
-rw-r--r-- | configure.ac | 11 | ||||
-rwxr-xr-x | examples/cairo-demo.py | 6 | ||||
-rw-r--r-- | gi/Makefile.am | 18 | ||||
-rw-r--r-- | gi/overrides/GLib.py | 18 | ||||
-rw-r--r-- | gi/overrides/Gtk.py | 41 | ||||
-rw-r--r-- | gi/pygi-argument.c | 20 | ||||
-rw-r--r-- | gi/pygi-cache.c | 1404 | ||||
-rw-r--r-- | gi/pygi-cache.h | 172 | ||||
-rw-r--r-- | gi/pygi-foreign-gvariant.h | 6 | ||||
-rw-r--r-- | gi/pygi-foreign.c | 7 | ||||
-rw-r--r-- | gi/pygi-info.c | 5 | ||||
-rw-r--r-- | gi/pygi-invoke-ng.c | 464 | ||||
-rw-r--r-- | gi/pygi-invoke-state-struct.h | 44 | ||||
-rw-r--r-- | gi/pygi-invoke.h | 2 | ||||
-rw-r--r-- | gi/pygi-marshal-cleanup.c | 517 | ||||
-rw-r--r-- | gi/pygi-marshal-cleanup.h | 97 | ||||
-rw-r--r-- | gi/pygi-marshal-in.c | 1412 | ||||
-rw-r--r-- | gi/pygi-marshal-in.h | 186 | ||||
-rw-r--r-- | gi/pygi-marshal-out.c | 767 | ||||
-rw-r--r-- | gi/pygi-marshal-out.h | 144 | ||||
-rw-r--r-- | gi/pygi-private.h | 6 | ||||
-rw-r--r-- | gi/pygi.h | 4 | ||||
-rw-r--r-- | gobject/pygtype.c | 7 | ||||
-rw-r--r-- | tests/test_gi.py | 43 | ||||
-rw-r--r-- | tests/test_overrides.py | 45 |
25 files changed, 5396 insertions, 50 deletions
diff --git a/configure.ac b/configure.ac index fb692373..d4fcbe35 100644 --- a/configure.ac +++ b/configure.ac @@ -124,6 +124,16 @@ AC_ARG_ENABLE(thread, AC_HELP_STRING([--disable-thread], [Disable pygobject threading support]),, enable_thread=yes) +dnl Build with the new caching invoker +AC_ARG_ENABLE(invoke-ng, + AC_HELP_STRING([--disable-invoke-ng], [Disable the new caching invoke path]),, + enable_invoke_ng=yes) +if test "${enable_invoke_ng}" == yes; then + AC_DEFINE(ENABLE_INVOKE_NG, 1, Enable the new invoke caching code path) +fi + +AM_CONDITIONAL(ENABLE_INVOKE_NG, test x$enable_invoke_ng == xyes) + dnl Building documentation AC_ARG_ENABLE(docs, AC_HELP_STRING([--enable-docs], [Enable documentation building]),enable_docs=$enableval, @@ -284,4 +294,5 @@ AC_OUTPUT echo echo "libffi support: $have_libffi" echo "introspection support: $enable_introspection" +echo "invoke-ng support: $enable_invoke_ng" echo diff --git a/examples/cairo-demo.py b/examples/cairo-demo.py index 72a05333..9b8e8f9a 100755 --- a/examples/cairo-demo.py +++ b/examples/cairo-demo.py @@ -79,9 +79,7 @@ def fill_shapes(ctx, x, y): def stroke_shapes(ctx, x, y): draw_shapes(ctx, x, y, False) -def expose (da, event): - ctx = Gdk.cairo_create(da.window) - +def draw(da, ctx): ctx.set_source_rgb(0, 0, 0) ctx.set_line_width(SIZE / 4) @@ -114,7 +112,7 @@ def main(): drawingarea = Gtk.DrawingArea() win.add(drawingarea) - drawingarea.connect('expose_event', expose) + drawingarea.connect('draw', draw) win.show_all() Gtk.main() diff --git a/gi/Makefile.am b/gi/Makefile.am index 31f6c790..fe1e1145 100644 --- a/gi/Makefile.am +++ b/gi/Makefile.am @@ -31,7 +31,6 @@ _gi_la_SOURCES = \ pygi-repository.h \ pygi-info.c \ pygi-info.h \ - pygi-invoke.c \ pygi-invoke.h \ pygi-foreign.c \ pygi-foreign.h \ @@ -58,6 +57,23 @@ _gi_la_SOURCES = \ pygobject-external.h \ gimodule.c +if ENABLE_INVOKE_NG +_gi_la_SOURCES += \ + pygi-invoke-ng.c \ + pygi-invoke-state-struct.h \ + pygi-cache.h \ + pygi-cache.c \ + pygi-marshal-in.c \ + pygi-marshal-in.h \ + pygi-marshal-out.c \ + pygi-marshal-out.h \ + pygi-marshal-cleanup.c \ + pygi-marshal-cleanup.h +else +_gi_la_SOURCES += \ + pygi-invoke.c +endif + _gi_cairo_la_CFLAGS = \ $(PYTHON_INCLUDES) \ $(GI_CFLAGS) \ diff --git a/gi/overrides/GLib.py b/gi/overrides/GLib.py index ad9f2357..e42f854a 100644 --- a/gi/overrides/GLib.py +++ b/gi/overrides/GLib.py @@ -84,8 +84,7 @@ class _VariantCreator(object): '''Handle the case where the outermost type of format is a tuple.''' format = format[1:] # eat the '(' - builder = GLib.VariantBuilder() - builder.init(variant_type_from_string('r')) + builder = GLib.VariantBuilder.new(variant_type_from_string('r')) if args is not None: if not args or type(args[0]) != type(()): raise (TypeError, 'expected tuple argument') @@ -102,7 +101,7 @@ class _VariantCreator(object): def _create_dict(self, format, args): '''Handle the case where the outermost type of format is a dict.''' - builder = GLib.VariantBuilder() + builder = None if args is None or not args[0]: # empty value: we need to call _create() to parse the subtype, # and specify the element type precisely @@ -112,9 +111,9 @@ class _VariantCreator(object): raise ValueError('dictionary type string not closed with }') rest_format = rest_format[1:] # eat the } element_type = format[:len(format) - len(rest_format)] - builder.init(variant_type_from_string(element_type)) + builder = GLib.VariantBuilder.new(variant_type_from_string(element_type)) else: - builder.init(variant_type_from_string('a{?*}')) + builder = GLib.VariantBuilder.new(variant_type_from_string('a{?*}')) for k, v in args[0].items(): (key_v, rest_format, _) = self._create(format[2:], [k]) (val_v, rest_format, _) = self._create(rest_format, [v]) @@ -123,8 +122,7 @@ class _VariantCreator(object): raise ValueError('dictionary type string not closed with }') rest_format = rest_format[1:] # eat the } - entry = GLib.VariantBuilder() - entry.init(variant_type_from_string('{?*}')) + entry = GLib.VariantBuilder.new(variant_type_from_string('{?*}')) entry.add_value(key_v) entry.add_value(val_v) builder.add_value(entry.end()) @@ -136,15 +134,15 @@ class _VariantCreator(object): def _create_array(self, format, args): '''Handle the case where the outermost type of format is an array.''' - builder = GLib.VariantBuilder() + builder = None if args is None or not args[0]: # empty value: we need to call _create() to parse the subtype, # and specify the element type precisely rest_format = self._create(format[1:], None)[1] element_type = format[:len(format) - len(rest_format)] - builder.init(variant_type_from_string(element_type)) + builder = GLib.VariantBuilder.new(variant_type_from_string(element_type)) else: - builder.init(variant_type_from_string('a*')) + builder = GLib.VariantBuilder.new(variant_type_from_string('a*')) for i in range(len(args[0])): (v, rest_format, _) = self._create(format[1:], args[0][i:]) builder.add_value(v) diff --git a/gi/overrides/Gtk.py b/gi/overrides/Gtk.py index e4525921..9c35b068 100644 --- a/gi/overrides/Gtk.py +++ b/gi/overrides/Gtk.py @@ -35,6 +35,18 @@ else: Gtk = modules['Gtk']._introspection_module __all__ = [] +if Gtk._version == '2.0': + import warnings + warn_msg = "You have imported the Gtk 2.0 module. Because Gtk 2.0 \ +was not designed for use with introspection some of the \ +interfaces and API will fail. As such this is not supported \ +by the pygobject development team and we encourage you to \ +port your app to Gtk 3 or greater. PyGTK is the recomended \ +python module to use with Gtk 2.0" + + warnings.warn(warn_msg, RuntimeWarning) + + class Widget(Gtk.Widget): def translate_coordinates(self, dest_widget, src_x, src_y): @@ -365,7 +377,8 @@ class Dialog(Gtk.Dialog, Container): if flags & Gtk.DialogFlags.NO_SEPARATOR: self.set_has_separator(False) except AttributeError: - pass + import warnings + warnings.warn("Gtk.DialogFlags.NO_SEPARATOR has been depricated since Gtk+-3.0", DeprecationWarning) if buttons is not None: self.add_buttons(*buttons) @@ -401,16 +414,23 @@ class MessageDialog(Gtk.MessageDialog, Dialog): def __init__(self, parent=None, flags=0, - type=Gtk.MessageType.INFO, + message_type=Gtk.MessageType.INFO, buttons=Gtk.ButtonsType.NONE, message_format=None, **kwds): if message_format != None: kwds['text'] = message_format + + # type keyword is used for backwards compat with PyGTK + if 'type' in kwds: + import warnings + warnings.warn("The use of the keyword type as a parameter of the Gtk.MessageDialog constructor has been depricated. Please use message_type instead.", DeprecationWarning) + message_type = kwds.pop('type') + Gtk.MessageDialog.__init__(self, _buttons_property=buttons, - message_type=type, + message_type=message_type, **kwds) Dialog.__init__(self, parent=parent, flags=flags) @@ -738,9 +758,7 @@ class TreeModel(Gtk.TreeModel): # we may need to convert to a basic type type_ = self.get_column_type(column) - if type_ == GObject.TYPE_PYOBJECT: - pass # short-circut branching - elif type_ == GObject.TYPE_STRING: + if type_ == GObject.TYPE_STRING: if isinstance(value, str): value = str(value) elif sys.version_info < (3, 0): @@ -808,6 +826,9 @@ class TreeModel(Gtk.TreeModel): elif type_ == GObject.TYPE_UINT64: value_container.set_uint64(value) value = value_container + elif type_ == GObject.TYPE_PYOBJECT: + value_container.set_boxed(value) + value = value_container return value @@ -884,6 +905,14 @@ class ListStore(Gtk.ListStore, TreeModel, TreeSortable): return treeiter + def prepend(self, row=None): + treeiter = Gtk.ListStore.prepend(self) + + if row is not None: + self.set_row(treeiter, row) + + return treeiter + def set_value(self, treeiter, column, value): value = self._convert_value(treeiter, column, value) Gtk.ListStore.set_value(self, treeiter, column, value) diff --git a/gi/pygi-argument.c b/gi/pygi-argument.c index 2f14a94e..55cdfd18 100644 --- a/gi/pygi-argument.c +++ b/gi/pygi-argument.c @@ -29,6 +29,7 @@ #include <datetime.h> #include <pygobject.h> #include <pyglib-python-compat.h> +#include <pyglib.h> static void _pygi_g_type_tag_py_bounds (GITypeTag type_tag, @@ -1765,8 +1766,19 @@ _pygi_argument_to_object (GIArgument *arg, break; } case GI_TYPE_TAG_ERROR: - /* Errors should be handled in the invoke wrapper. */ - g_assert_not_reached(); + if (pyglib_error_check (&arg->v_pointer)) { + PyObject *err_type; + PyObject *err_value; + PyObject *err_trace; + PyErr_Fetch (&err_type, &err_value, &err_trace); + Py_XDECREF (err_type); + Py_XDECREF (err_trace); + object = err_value; + } else { + object = Py_None; + Py_INCREF (object); + break; + } } return object; @@ -1832,6 +1844,8 @@ _pygi_argument_from_g_value(const GValue *value, switch (info_type) { case GI_INFO_TYPE_FLAGS: + arg.v_long = g_value_get_flags (value); + break; case GI_INFO_TYPE_ENUM: arg.v_long = g_value_get_enum (value); break; @@ -1857,6 +1871,8 @@ _pygi_argument_from_g_value(const GValue *value, break; } case GI_TYPE_TAG_ERROR: + arg.v_pointer = g_value_get_boxed (value); + break; case GI_TYPE_TAG_VOID: g_critical("Converting of type '%s' is not implemented", g_type_tag_to_string(type_tag)); g_assert_not_reached(); diff --git a/gi/pygi-cache.c b/gi/pygi-cache.c new file mode 100644 index 00000000..aed59e09 --- /dev/null +++ b/gi/pygi-cache.c @@ -0,0 +1,1404 @@ +/* -*- Mode: C; c-basic-offset: 4 -*- + * vim: tabstop=4 shiftwidth=4 expandtab + * + * Copyright (C) 2011 John (J5) Palmieri <johnp@redhat.com> + * + * 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, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 + * USA + */ + +#include "pygi-info.h" +#include "pygi-cache.h" +#include "pygi-marshal-in.h" +#include "pygi-marshal-out.h" +#include "pygi-marshal-cleanup.h" +#include "pygi-type.h" +#include <girepository.h> + +PyGIArgCache * _arg_cache_new (GITypeInfo *type_info, + PyGICallableCache *callable_cache, + GIArgInfo *arg_info, + GITransfer transfer, + GIDirection direction, + gssize c_arg_index, + gssize py_arg_index); + +PyGIArgCache * _arg_cache_new_for_interface (GIInterfaceInfo *iface_info, + PyGICallableCache *callable_cache, + GIArgInfo *arg_info, + GITransfer transfer, + GIDirection direction, + gssize c_arg_index, + gssize py_arg_index); +/* cleanup */ +void +_pygi_arg_cache_free (PyGIArgCache *cache) +{ + if (cache == NULL) + return; + + if (cache->type_info != NULL) + g_base_info_unref ( (GIBaseInfo *)cache->type_info); + if (cache->destroy_notify) + cache->destroy_notify (cache); + else + g_slice_free (PyGIArgCache, cache); +} + +static void +_interface_cache_free_func (PyGIInterfaceCache *cache) +{ + if (cache != NULL) { + Py_XDECREF (cache->py_type); + if (cache->type_name != NULL) + g_free (cache->type_name); + if (cache->interface_info != NULL) + g_base_info_unref ( (GIBaseInfo *)cache->interface_info); + g_slice_free (PyGIInterfaceCache, cache); + } +} + +static void +_hash_cache_free_func (PyGIHashCache *cache) +{ + if (cache != NULL) { + _pygi_arg_cache_free (cache->key_cache); + _pygi_arg_cache_free (cache->value_cache); + g_slice_free (PyGIHashCache, cache); + } +} + +static void +_sequence_cache_free_func (PyGISequenceCache *cache) +{ + if (cache != NULL) { + _pygi_arg_cache_free (cache->item_cache); + g_slice_free (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) +{ + gssize i; + + if (cache == NULL) + return; + + g_slist_free (cache->out_args); + for (i = 0; i < cache->n_args; i++) { + PyGIArgCache *tmp = cache->args_cache[i]; + _pygi_arg_cache_free (tmp); + } + if (cache->return_cache != NULL) + _pygi_arg_cache_free (cache->return_cache); + + g_slice_free1 (cache->n_args * sizeof (PyGIArgCache *), cache->args_cache); + g_slice_free (PyGICallableCache, cache); +} + +/* cache generation */ + +static PyGIInterfaceCache * +_interface_cache_new (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) + return NULL; + + ic->type_name = _pygi_g_base_info_get_fullname (iface_info); + return ic; +} + +static PyGISequenceCache * +_sequence_cache_new (GITypeInfo *type_info, + GIDirection direction, + GITransfer transfer, + gssize child_offset) +{ + PyGISequenceCache *sc; + GITypeInfo *item_type_info; + GITypeTag item_type_tag; + GITransfer item_transfer; + + sc = g_slice_new0 (PyGISequenceCache); + ( (PyGIArgCache *)sc)->destroy_notify = (GDestroyNotify)_sequence_cache_free_func; + + sc->fixed_size = -1; + sc->len_arg_index = -1; + sc->is_zero_terminated = g_type_info_is_zero_terminated (type_info); + if (!sc->is_zero_terminated) { + sc->fixed_size = g_type_info_get_array_fixed_size (type_info); + if (sc->fixed_size < 0) + sc->len_arg_index = g_type_info_get_array_length (type_info) + child_offset; + } + + item_type_info = g_type_info_get_param_type (type_info, 0); + + item_transfer = + transfer == GI_TRANSFER_CONTAINER ? GI_TRANSFER_NOTHING : transfer; + + sc->item_cache = _arg_cache_new (item_type_info, + NULL, + NULL, + item_transfer, + direction, + 0, 0); + + if (sc->item_cache == NULL) { + _pygi_arg_cache_free ( (PyGIArgCache *)sc); + return NULL; + } + + sc->item_size = _pygi_g_type_info_size (item_type_info); + g_base_info_unref ( (GIBaseInfo *)item_type_info); + + return sc; +} +static PyGIHashCache * +_hash_cache_new (GITypeInfo *type_info, + GIDirection direction, + GITransfer transfer) +{ + PyGIHashCache *hc; + GITypeInfo *key_type_info; + GITypeInfo *value_type_info; + GITransfer item_transfer; + + hc = g_slice_new0 (PyGIHashCache); + ( (PyGIArgCache *)hc)->destroy_notify = (GDestroyNotify)_hash_cache_free_func; + key_type_info = g_type_info_get_param_type (type_info, 0); + value_type_info = g_type_info_get_param_type (type_info, 1); + + item_transfer = + transfer == GI_TRANSFER_CONTAINER ? GI_TRANSFER_NOTHING : transfer; + + hc->key_cache = _arg_cache_new (key_type_info, + NULL, + NULL, + item_transfer, + direction, + 0, 0); + + if (hc->key_cache == NULL) { + _pygi_arg_cache_free ( (PyGIArgCache *)hc); + return NULL; + } + + hc->value_cache = _arg_cache_new (value_type_info, + NULL, + NULL, + item_transfer, + direction, + 0, 0); + + if (hc->value_cache == NULL) { + _pygi_arg_cache_free ( (PyGIArgCache *)hc); + return NULL; + } + + g_base_info_unref( (GIBaseInfo *)key_type_info); + g_base_info_unref( (GIBaseInfo *)value_type_info); + + return hc; +} + +static PyGICallbackCache * +_callback_cache_new (GIArgInfo *arg_info, + GIInterfaceInfo *iface_info, + gssize child_offset) +{ + PyGICallbackCache *cc; + + cc = g_slice_new0 (PyGICallbackCache); + 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; +} + +static PyGIArgCache * +_arg_cache_alloc (void) +{ + return g_slice_new0 (PyGIArgCache); +} + +static void +_arg_cache_in_void_setup (PyGIArgCache *arg_cache) +{ + arg_cache->in_marshaller = _pygi_marshal_in_void; +} + +static void +_arg_cache_out_void_setup (PyGIArgCache *arg_cache) +{ + arg_cache->out_marshaller = _pygi_marshal_out_void; +} + +static void +_arg_cache_in_boolean_setup (PyGIArgCache *arg_cache) +{ + arg_cache->in_marshaller = _pygi_marshal_in_boolean; +} + +static void +_arg_cache_out_boolean_setup (PyGIArgCache *arg_cache) +{ + arg_cache->out_marshaller = _pygi_marshal_out_boolean; +} + +static void +_arg_cache_in_int8_setup (PyGIArgCache *arg_cache) +{ + arg_cache->in_marshaller = _pygi_marshal_in_int8; +} + +static void +_arg_cache_out_int8_setup (PyGIArgCache *arg_cache) +{ + arg_cache->out_marshaller = _pygi_marshal_out_int8; +} + +static void +_arg_cache_in_uint8_setup (PyGIArgCache *arg_cache) +{ + arg_cache->in_marshaller = _pygi_marshal_in_uint8; +} + +static void +_arg_cache_out_uint8_setup (PyGIArgCache *arg_cache) +{ + arg_cache->out_marshaller = _pygi_marshal_out_uint8; +} + +static void +_arg_cache_in_int16_setup (PyGIArgCache *arg_cache) +{ + arg_cache->in_marshaller = _pygi_marshal_in_int16; +} + +static void +_arg_cache_out_int16_setup (PyGIArgCache *arg_cache) +{ + arg_cache->out_marshaller = _pygi_marshal_out_int16; +} + +static void +_arg_cache_in_uint16_setup (PyGIArgCache *arg_cache) +{ + arg_cache->in_marshaller = _pygi_marshal_in_uint16; +} + +static void +_arg_cache_out_uint16_setup (PyGIArgCache *arg_cache) +{ + arg_cache->out_marshaller = _pygi_marshal_out_uint16; +} + +static void +_arg_cache_in_int32_setup (PyGIArgCache *arg_cache) +{ + arg_cache->in_marshaller = _pygi_marshal_in_int32; +} + +static void +_arg_cache_out_int32_setup (PyGIArgCache *arg_cache) +{ + arg_cache->out_marshaller = _pygi_marshal_out_int32; +} + +static void +_arg_cache_in_uint32_setup (PyGIArgCache *arg_cache) +{ + arg_cache->in_marshaller = _pygi_marshal_in_uint32; +} + +static void +_arg_cache_out_uint32_setup (PyGIArgCache *arg_cache) +{ + arg_cache->out_marshaller = _pygi_marshal_out_uint32; +} + +static void +_arg_cache_in_int64_setup (PyGIArgCache *arg_cache) +{ + arg_cache->in_marshaller = _pygi_marshal_in_int64; +} + +static void +_arg_cache_out_int64_setup (PyGIArgCache *arg_cache) +{ + arg_cache->out_marshaller = _pygi_marshal_out_int64; +} + +static void +_arg_cache_in_uint64_setup (PyGIArgCache *arg_cache) +{ + arg_cache->in_marshaller = _pygi_marshal_in_uint64; +} + +static void +_arg_cache_out_uint64_setup (PyGIArgCache *arg_cache) +{ + arg_cache->out_marshaller = _pygi_marshal_out_uint64; +} + +static void +_arg_cache_in_float_setup (PyGIArgCache *arg_cache) +{ + arg_cache->in_marshaller = _pygi_marshal_in_float; +} + +static void +_arg_cache_out_float_setup (PyGIArgCache *arg_cache) +{ + arg_cache->out_marshaller = _pygi_marshal_out_float; +} + +static void +_arg_cache_in_double_setup (PyGIArgCache *arg_cache) +{ + arg_cache->in_marshaller = _pygi_marshal_in_double; +} + +static void +_arg_cache_out_double_setup (PyGIArgCache *arg_cache) +{ + arg_cache->out_marshaller = _pygi_marshal_out_double; +} + +static void +_arg_cache_in_unichar_setup (PyGIArgCache *arg_cache) +{ + arg_cache->in_marshaller = _pygi_marshal_in_unichar; +} + +static void +_arg_cache_out_unichar_setup (PyGIArgCache *arg_cache) +{ + arg_cache->out_marshaller = _pygi_marshal_out_unichar; +} + +static void +_arg_cache_in_gtype_setup (PyGIArgCache *arg_cache) +{ + arg_cache->in_marshaller = _pygi_marshal_in_gtype; +} + +static void +_arg_cache_out_gtype_setup (PyGIArgCache *arg_cache) +{ + arg_cache->out_marshaller = _pygi_marshal_out_gtype; +} + +static void +_arg_cache_in_utf8_setup (PyGIArgCache *arg_cache, + GITransfer transfer) +{ + arg_cache->in_marshaller = _pygi_marshal_in_utf8; + arg_cache->in_cleanup = _pygi_marshal_cleanup_in_utf8; +} + +static void +_arg_cache_out_utf8_setup (PyGIArgCache *arg_cache, + GITransfer transfer) +{ + arg_cache->out_marshaller = _pygi_marshal_out_utf8; + arg_cache->out_cleanup = _pygi_marshal_cleanup_out_utf8; +} + +static void +_arg_cache_in_filename_setup (PyGIArgCache *arg_cache, + GITransfer transfer) +{ + arg_cache->in_marshaller = _pygi_marshal_in_filename; + arg_cache->in_cleanup = _pygi_marshal_cleanup_in_utf8; +} + +static void +_arg_cache_out_filename_setup (PyGIArgCache *arg_cache, + GITransfer transfer) +{ + arg_cache->out_marshaller = _pygi_marshal_out_filename; + arg_cache->out_cleanup = _pygi_marshal_cleanup_out_utf8; +} + +static gboolean +_arg_cache_in_array_setup (PyGIArgCache *arg_cache, + PyGICallableCache *callable_cache, + GITypeInfo *type_info, + GITransfer transfer, + GIDirection direction) +{ + PyGISequenceCache *seq_cache = (PyGISequenceCache *)arg_cache; + seq_cache->array_type = g_type_info_get_array_type (type_info); + + arg_cache->in_marshaller = _pygi_marshal_in_array; + + if (seq_cache->len_arg_index >= 0 && + direction == GI_DIRECTION_IN) { + PyGIArgCache *child_cache = + callable_cache->args_cache[seq_cache->len_arg_index]; + + if (child_cache == NULL) { + child_cache = _arg_cache_alloc (); + } else if (child_cache->meta_type == PYGI_META_ARG_TYPE_CHILD) { + return TRUE; + } + + child_cache->meta_type = PYGI_META_ARG_TYPE_CHILD; + child_cache->direction = direction; + child_cache->in_marshaller = NULL; + child_cache->out_marshaller = NULL; + + callable_cache->args_cache[seq_cache->len_arg_index] = child_cache; + } + + arg_cache->in_cleanup = _pygi_marshal_cleanup_in_array; + + return TRUE; +} + +static gboolean +_arg_cache_out_array_setup (PyGIArgCache *arg_cache, + PyGICallableCache *callable_cache, + GITypeInfo *type_info, + GITransfer transfer, + GIDirection direction, + gssize arg_index) +{ + PyGISequenceCache *seq_cache = (PyGISequenceCache *)arg_cache; + arg_cache->out_marshaller = _pygi_marshal_out_array; + arg_cache->out_cleanup = _pygi_marshal_cleanup_out_array; + + seq_cache->array_type = g_type_info_get_array_type (type_info); + + if (seq_cache->len_arg_index >= 0) { + PyGIArgCache *child_cache = callable_cache->args_cache[seq_cache->len_arg_index]; + if (seq_cache->len_arg_index < arg_index) + callable_cache->n_out_child_args++; + + if (child_cache != NULL) { + if (child_cache->meta_type == PYGI_META_ARG_TYPE_CHILD) + return TRUE; + + callable_cache->out_args = + g_slist_remove (callable_cache->out_args, child_cache); + } else { + child_cache = _arg_cache_alloc (); + } + + child_cache->meta_type = PYGI_META_ARG_TYPE_CHILD; + child_cache->direction = direction; + child_cache->in_marshaller = NULL; + child_cache->out_marshaller = NULL; + + callable_cache->args_cache[seq_cache->len_arg_index] = child_cache; + } + + return TRUE; +} + +static void +_arg_cache_in_glist_setup (PyGIArgCache *arg_cache, + GITransfer transfer) +{ + arg_cache->in_marshaller = _pygi_marshal_in_glist; + arg_cache->in_cleanup = _pygi_marshal_cleanup_in_glist; +} + +static void +_arg_cache_out_glist_setup (PyGIArgCache *arg_cache, + GITransfer transfer) +{ + arg_cache->out_marshaller = _pygi_marshal_out_glist; + arg_cache->in_cleanup = _pygi_marshal_cleanup_out_glist; +} + +static void +_arg_cache_in_gslist_setup (PyGIArgCache *arg_cache, + GITransfer transfer) +{ + arg_cache->in_marshaller = _pygi_marshal_in_gslist; + arg_cache->in_cleanup = _pygi_marshal_cleanup_in_glist; +} + +static void +_arg_cache_out_gslist_setup (PyGIArgCache *arg_cache, + GITransfer transfer) +{ + arg_cache->out_marshaller = _pygi_marshal_out_gslist; + arg_cache->out_cleanup = _pygi_marshal_cleanup_out_glist; +} + +static void +_arg_cache_in_ghash_setup (PyGIArgCache *arg_cache) +{ + arg_cache->in_marshaller = _pygi_marshal_in_ghash; + arg_cache->in_cleanup = _pygi_marshal_cleanup_in_ghash; +} + +static void +_arg_cache_out_ghash_setup (PyGIArgCache *arg_cache) +{ + arg_cache->out_marshaller = _pygi_marshal_out_ghash; + arg_cache->out_cleanup = _pygi_marshal_cleanup_out_ghash; +} + +static void +_arg_cache_in_gerror_setup (PyGIArgCache *arg_cache) +{ + arg_cache->in_marshaller = _pygi_marshal_in_gerror; + arg_cache->meta_type = PYGI_META_ARG_TYPE_CHILD; +} + +static void +_arg_cache_out_gerror_setup (PyGIArgCache *arg_cache) +{ + arg_cache->out_marshaller = _pygi_marshal_out_gerror; + arg_cache->meta_type = PYGI_META_ARG_TYPE_CHILD; +} + +static void +_arg_cache_in_interface_union_setup (PyGIArgCache *arg_cache, + GITransfer transfer) +{ + arg_cache->in_marshaller = _pygi_marshal_in_interface_struct; +} + +static void +_arg_cache_out_interface_union_setup (PyGIArgCache *arg_cache, + GITransfer transfer) +{ + arg_cache->out_marshaller = _pygi_marshal_out_interface_struct; +} + +static void +_arg_cache_in_interface_struct_setup (PyGIArgCache *arg_cache, + GIInterfaceInfo *iface_info, + GITransfer transfer) +{ + PyGIInterfaceCache *iface_cache = (PyGIInterfaceCache *)arg_cache; + iface_cache->is_foreign = g_struct_info_is_foreign ( (GIStructInfo*)iface_info); + arg_cache->in_marshaller = _pygi_marshal_in_interface_struct; + + if (iface_cache->g_type == G_TYPE_VALUE) + arg_cache->in_cleanup = _pygi_marshal_cleanup_in_interface_struct_gvalue; + else if (iface_cache->is_foreign) + arg_cache->in_cleanup = _pygi_marshal_cleanup_in_interface_struct_foreign; +} + +static void +_arg_cache_out_interface_struct_setup (PyGIArgCache *arg_cache, + GIInterfaceInfo *iface_info, + GITransfer transfer) +{ + PyGIInterfaceCache *iface_cache = (PyGIInterfaceCache *)arg_cache; + iface_cache->is_foreign = g_struct_info_is_foreign ( (GIStructInfo*)iface_info); + arg_cache->out_marshaller = _pygi_marshal_out_interface_struct; + + if (iface_cache->is_foreign) + arg_cache->in_cleanup = _pygi_marshal_cleanup_out_interface_struct_foreign; +} + +static void +_arg_cache_in_interface_object_setup (PyGIArgCache *arg_cache, + GITransfer transfer) +{ + arg_cache->in_marshaller = _pygi_marshal_in_interface_object; + arg_cache->in_cleanup = _pygi_marshal_cleanup_in_interface_object; +} + +static void +_arg_cache_out_interface_object_setup (PyGIArgCache *arg_cache, + GITransfer transfer) +{ + arg_cache->out_marshaller = _pygi_marshal_out_interface_object; + arg_cache->out_cleanup = _pygi_marshal_cleanup_out_interface_object; +} + +static void +_arg_cache_in_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; + callable_cache->args_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; + callable_cache->args_cache[callback_cache->destroy_notify_index] = destroy_arg_cache; + } + arg_cache->in_marshaller = _pygi_marshal_in_interface_callback; +} + +static void +_arg_cache_out_interface_callback_setup (void) +{ + PyErr_Format(PyExc_NotImplementedError, + "Callback returns are not supported"); +} + +static void +_arg_cache_in_interface_enum_setup (PyGIArgCache *arg_cache, + GITransfer transfer) +{ + arg_cache->in_marshaller = _pygi_marshal_in_interface_enum; +} + +static void +_arg_cache_out_interface_enum_setup (PyGIArgCache *arg_cache, + GITransfer transfer) +{ + arg_cache->out_marshaller = _pygi_marshal_out_interface_enum; +} + +static void +_arg_cache_in_interface_flags_setup (PyGIArgCache *arg_cache, + GITransfer transfer) +{ + arg_cache->in_marshaller = _pygi_marshal_in_interface_flags; +} + +static void +_arg_cache_out_interface_flags_setup (PyGIArgCache *arg_cache, + GITransfer transfer) +{ + arg_cache->out_marshaller = _pygi_marshal_out_interface_flags; +} + +PyGIArgCache * +_arg_cache_new_for_interface (GIInterfaceInfo *iface_info, + PyGICallableCache *callable_cache, + GIArgInfo *arg_info, + GITransfer transfer, + GIDirection direction, + gssize c_arg_index, + gssize py_arg_index) +{ + PyGIInterfaceCache *iface_cache = NULL; + 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; + + GI_IS_INTERFACE_INFO (iface_info); + + info_type = g_base_info_get_type ( (GIBaseInfo *)iface_info); + + /* Callbacks are special cased */ + if (info_type != GI_INFO_TYPE_CALLBACK) { + iface_cache = _interface_cache_new (iface_info); + + arg_cache = (PyGIArgCache *)iface_cache; + if (arg_cache == NULL) + return NULL; + } + + switch (info_type) { + case GI_INFO_TYPE_UNION: + if (direction == GI_DIRECTION_IN || direction == GI_DIRECTION_INOUT) + _arg_cache_in_interface_union_setup (arg_cache, transfer); + + if (direction == GI_DIRECTION_OUT || direction == GI_DIRECTION_INOUT) + _arg_cache_out_interface_union_setup (arg_cache, transfer); + + break; + case GI_INFO_TYPE_STRUCT: + if (direction == GI_DIRECTION_IN || direction == GI_DIRECTION_INOUT) + _arg_cache_in_interface_struct_setup (arg_cache, + iface_info, + transfer); + + if (direction == GI_DIRECTION_OUT || direction == GI_DIRECTION_INOUT) + _arg_cache_out_interface_struct_setup (arg_cache, + iface_info, + transfer); + + break; + case GI_INFO_TYPE_OBJECT: + case GI_INFO_TYPE_INTERFACE: + if (direction == GI_DIRECTION_IN || direction == GI_DIRECTION_INOUT) + _arg_cache_in_interface_object_setup (arg_cache, transfer); + + if (direction == GI_DIRECTION_OUT || direction == GI_DIRECTION_INOUT) + _arg_cache_out_interface_object_setup (arg_cache, transfer); + + break; + case GI_INFO_TYPE_BOXED: + if (direction == GI_DIRECTION_IN || direction == GI_DIRECTION_INOUT) + _arg_cache_in_interface_struct_setup (arg_cache, + iface_info, + transfer); + + if (direction == GI_DIRECTION_OUT || direction == GI_DIRECTION_INOUT) + _arg_cache_out_interface_struct_setup (arg_cache, + iface_info, + transfer); + + break; + case GI_INFO_TYPE_CALLBACK: + { + PyGICallbackCache *callback_cache; + + if (direction == GI_DIRECTION_OUT || direction == GI_DIRECTION_INOUT) { + _arg_cache_out_interface_callback_setup (); + return NULL; + } + + callback_cache = + _callback_cache_new (arg_info, + iface_info, + child_offset); + + arg_cache = (PyGIArgCache *)callback_cache; + if (arg_cache == NULL) + return NULL; + + if (direction == GI_DIRECTION_IN || direction == GI_DIRECTION_INOUT) + _arg_cache_in_interface_callback_setup (arg_cache, callable_cache); + + break; + } + case GI_INFO_TYPE_ENUM: + if (direction == GI_DIRECTION_IN || direction == GI_DIRECTION_INOUT) + _arg_cache_in_interface_enum_setup (arg_cache, transfer); + + if (direction == GI_DIRECTION_OUT || direction == GI_DIRECTION_INOUT) + _arg_cache_out_interface_enum_setup (arg_cache, transfer); + + break; + case GI_INFO_TYPE_FLAGS: + if (direction == GI_DIRECTION_IN || direction == GI_DIRECTION_INOUT) + _arg_cache_in_interface_flags_setup (arg_cache, transfer); + + if (direction == GI_DIRECTION_OUT || direction == GI_DIRECTION_INOUT) + _arg_cache_out_interface_flags_setup (arg_cache, transfer); + + break; + default: + g_assert_not_reached (); + } + + if (arg_cache != NULL) { + arg_cache->direction = direction; + arg_cache->transfer = transfer; + arg_cache->type_tag = GI_TYPE_TAG_INTERFACE; + arg_cache->py_arg_index = py_arg_index; + arg_cache->c_arg_index = c_arg_index; + + if (iface_cache != NULL) { + g_base_info_ref ( (GIBaseInfo *)iface_info); + iface_cache->interface_info = iface_info; + } + } + + return arg_cache; +} + +PyGIArgCache * +_arg_cache_new (GITypeInfo *type_info, + PyGICallableCache *callable_cache, + GIArgInfo *arg_info, + GITransfer transfer, + GIDirection direction, + gssize c_arg_index, + gssize py_arg_index) +{ + PyGIArgCache *arg_cache = NULL; + gssize child_offset = 0; + GITypeTag type_tag; + + GI_IS_TYPE_INFO (type_info); + + type_tag = g_type_info_get_tag (type_info); + + if (callable_cache != NULL) + child_offset = + (callable_cache->function_type == PYGI_FUNCTION_TYPE_METHOD || + callable_cache->function_type == PYGI_FUNCTION_TYPE_VFUNC) ? 1: 0; + + switch (type_tag) { + case GI_TYPE_TAG_VOID: + arg_cache = _arg_cache_alloc (); + if (arg_cache == NULL) + break; + + if (direction == GI_DIRECTION_IN || direction == GI_DIRECTION_INOUT) + _arg_cache_in_void_setup (arg_cache); + + if (direction == GI_DIRECTION_OUT || direction == GI_DIRECTION_INOUT) + _arg_cache_out_void_setup (arg_cache); + break; + case GI_TYPE_TAG_BOOLEAN: + arg_cache = _arg_cache_alloc (); + if (arg_cache == NULL) + break; + + if (direction == GI_DIRECTION_IN || direction == GI_DIRECTION_INOUT) + _arg_cache_in_boolean_setup (arg_cache); + + if (direction == GI_DIRECTION_OUT || direction == GI_DIRECTION_INOUT) + _arg_cache_out_boolean_setup (arg_cache); + + break; + case GI_TYPE_TAG_INT8: + arg_cache = _arg_cache_alloc (); + if (arg_cache == NULL) + break; + + if (direction == GI_DIRECTION_IN || direction == GI_DIRECTION_INOUT) + _arg_cache_in_int8_setup (arg_cache); + + if (direction == GI_DIRECTION_OUT || direction == GI_DIRECTION_INOUT) + _arg_cache_out_int8_setup (arg_cache); + + break; + case GI_TYPE_TAG_UINT8: + arg_cache = _arg_cache_alloc (); + if (arg_cache == NULL) + break; + + if (direction == GI_DIRECTION_IN || direction == GI_DIRECTION_INOUT) + _arg_cache_in_uint8_setup (arg_cache); + + if (direction == GI_DIRECTION_OUT || direction == GI_DIRECTION_INOUT) + _arg_cache_out_uint8_setup (arg_cache); + + break; + case GI_TYPE_TAG_INT16: + arg_cache = _arg_cache_alloc (); + if (arg_cache == NULL) + break; + + if (direction == GI_DIRECTION_IN || direction == GI_DIRECTION_INOUT) + _arg_cache_in_int16_setup (arg_cache); + + if (direction == GI_DIRECTION_OUT || direction == GI_DIRECTION_INOUT) + _arg_cache_out_int16_setup (arg_cache); + + break; + case GI_TYPE_TAG_UINT16: + arg_cache = _arg_cache_alloc (); + if (arg_cache == NULL) + break; + + if (direction == GI_DIRECTION_IN || direction == GI_DIRECTION_INOUT) + _arg_cache_in_uint16_setup (arg_cache); + + if (direction == GI_DIRECTION_OUT || direction == GI_DIRECTION_INOUT) + _arg_cache_out_uint16_setup (arg_cache); + + break; + case GI_TYPE_TAG_INT32: + arg_cache = _arg_cache_alloc (); + if (arg_cache == NULL) + break; + + if (direction == GI_DIRECTION_IN || direction == GI_DIRECTION_INOUT) + _arg_cache_in_int32_setup (arg_cache); + + if (direction == GI_DIRECTION_OUT || direction == GI_DIRECTION_INOUT) + _arg_cache_out_int32_setup (arg_cache); + + break; + case GI_TYPE_TAG_UINT32: + arg_cache = _arg_cache_alloc (); + if (arg_cache == NULL) + break; + + if (direction == GI_DIRECTION_IN || direction == GI_DIRECTION_INOUT) + _arg_cache_in_uint32_setup (arg_cache); + + if (direction == GI_DIRECTION_OUT || direction == GI_DIRECTION_INOUT) + _arg_cache_out_uint32_setup (arg_cache); + + break; + case GI_TYPE_TAG_INT64: + arg_cache = _arg_cache_alloc (); + if (arg_cache == NULL) + break; + + if (direction == GI_DIRECTION_IN || direction == GI_DIRECTION_INOUT) + _arg_cache_in_int64_setup (arg_cache); + + if (direction == GI_DIRECTION_OUT || direction == GI_DIRECTION_INOUT) + _arg_cache_out_int64_setup (arg_cache); + + break; + case GI_TYPE_TAG_UINT64: + arg_cache = _arg_cache_alloc (); + if (arg_cache == NULL) + break; + + if (direction == GI_DIRECTION_IN || direction == GI_DIRECTION_INOUT) + _arg_cache_in_uint64_setup (arg_cache); + + if (direction == GI_DIRECTION_OUT || direction == GI_DIRECTION_INOUT) + _arg_cache_out_uint64_setup (arg_cache); + + break; + case GI_TYPE_TAG_FLOAT: + arg_cache = _arg_cache_alloc (); + if (arg_cache == NULL) + break; + + if (direction == GI_DIRECTION_IN || direction == GI_DIRECTION_INOUT) + _arg_cache_in_float_setup (arg_cache); + + if (direction == GI_DIRECTION_OUT || direction == GI_DIRECTION_INOUT) + _arg_cache_out_float_setup (arg_cache); + + break; + case GI_TYPE_TAG_DOUBLE: + arg_cache = _arg_cache_alloc (); + if (arg_cache == NULL) + break; + + if (direction == GI_DIRECTION_IN || direction == GI_DIRECTION_INOUT) + _arg_cache_in_double_setup (arg_cache); + + if (direction == GI_DIRECTION_OUT || direction == GI_DIRECTION_INOUT) + _arg_cache_out_double_setup (arg_cache); + + break; + case GI_TYPE_TAG_UNICHAR: + arg_cache = _arg_cache_alloc (); + if (arg_cache == NULL) + break; + + if (direction == GI_DIRECTION_IN || direction == GI_DIRECTION_INOUT) + _arg_cache_in_unichar_setup (arg_cache); + + if (direction == GI_DIRECTION_OUT || direction == GI_DIRECTION_INOUT) + _arg_cache_out_unichar_setup (arg_cache); + + break; + case GI_TYPE_TAG_GTYPE: + arg_cache = _arg_cache_alloc (); + if (arg_cache == NULL) + break; + + if (direction == GI_DIRECTION_IN || direction == GI_DIRECTION_INOUT) + _arg_cache_in_gtype_setup (arg_cache); + + if (direction == GI_DIRECTION_OUT || direction == GI_DIRECTION_INOUT) + _arg_cache_out_gtype_setup (arg_cache); + + break; + case GI_TYPE_TAG_UTF8: + arg_cache = _arg_cache_alloc (); + if (arg_cache == NULL) + break; + + if (direction == GI_DIRECTION_IN || direction == GI_DIRECTION_INOUT) + _arg_cache_in_utf8_setup (arg_cache, transfer); + + if (direction == GI_DIRECTION_OUT || direction == GI_DIRECTION_INOUT) + _arg_cache_out_utf8_setup (arg_cache, transfer); + + break; + case GI_TYPE_TAG_FILENAME: + arg_cache = _arg_cache_alloc (); + if (arg_cache == NULL) + break; + + if (direction == GI_DIRECTION_IN || direction == GI_DIRECTION_INOUT) + _arg_cache_in_filename_setup (arg_cache, transfer); + + if (direction == GI_DIRECTION_OUT || direction == GI_DIRECTION_INOUT) + _arg_cache_out_filename_setup (arg_cache, transfer); + + break; + case GI_TYPE_TAG_ARRAY: + { + PyGISequenceCache *seq_cache = + _sequence_cache_new (type_info, + direction, + transfer, + child_offset); + + arg_cache = (PyGIArgCache *)seq_cache; + if (arg_cache == NULL) + break; + + if (direction == GI_DIRECTION_IN || direction == GI_DIRECTION_INOUT) + _arg_cache_in_array_setup (arg_cache, + callable_cache, + type_info, + transfer, + direction); + + if (direction == GI_DIRECTION_OUT || direction == GI_DIRECTION_INOUT) + _arg_cache_out_array_setup (arg_cache, + callable_cache, + type_info, + transfer, + direction, + c_arg_index); + + /* ugly edge case code: + * + * length can come before the array parameter which means we + * need to update indexes if this happens + */ + if (seq_cache->len_arg_index > -1 && + seq_cache->len_arg_index < c_arg_index) { + gssize i; + + py_arg_index -= 1; + callable_cache->n_py_args -= 1; + + for (i = seq_cache->len_arg_index + 1; + i < callable_cache->n_args; + i++) { + PyGIArgCache *update_cache = callable_cache->args_cache[i]; + if (update_cache == NULL) + break; + + update_cache->py_arg_index -= 1; + } + } + + break; + } + case GI_TYPE_TAG_GLIST: + { + PyGISequenceCache *seq_cache = + _sequence_cache_new (type_info, + direction, + transfer, + child_offset); + + arg_cache = (PyGIArgCache *)seq_cache; + if (arg_cache == NULL) + break; + + if (direction == GI_DIRECTION_IN || direction == GI_DIRECTION_INOUT) + _arg_cache_in_glist_setup (arg_cache, transfer); + + if (direction == GI_DIRECTION_OUT || direction == GI_DIRECTION_INOUT) + _arg_cache_out_glist_setup (arg_cache, transfer); + + + break; + } + case GI_TYPE_TAG_GSLIST: + { + PyGISequenceCache *seq_cache = + _sequence_cache_new (type_info, + direction, + transfer, + child_offset); + + arg_cache = (PyGIArgCache *)seq_cache; + if (arg_cache == NULL) + break; + + if (direction == GI_DIRECTION_IN || direction == GI_DIRECTION_INOUT) + _arg_cache_in_gslist_setup (arg_cache, transfer); + + if (direction == GI_DIRECTION_OUT || direction == GI_DIRECTION_INOUT) + _arg_cache_out_gslist_setup (arg_cache, transfer); + + break; + } + case GI_TYPE_TAG_GHASH: + arg_cache = + (PyGIArgCache *)_hash_cache_new (type_info, + direction, + transfer); + + if (arg_cache == NULL) + break; + + if (direction == GI_DIRECTION_IN || direction == GI_DIRECTION_INOUT) + _arg_cache_in_ghash_setup (arg_cache); + + if (direction == GI_DIRECTION_OUT || direction == GI_DIRECTION_INOUT) { + _arg_cache_out_ghash_setup (arg_cache); + } + + break; + case GI_TYPE_TAG_INTERFACE: + { + GIInterfaceInfo *interface_info = g_type_info_get_interface (type_info); + arg_cache = _arg_cache_new_for_interface (interface_info, + callable_cache, + arg_info, + transfer, + direction, + c_arg_index, + py_arg_index); + + g_base_info_unref ( (GIBaseInfo *)interface_info); + break; + } + case GI_TYPE_TAG_ERROR: + arg_cache = _arg_cache_alloc (); + if (arg_cache == NULL) + break; + + if (direction == GI_DIRECTION_IN || direction == GI_DIRECTION_INOUT) + _arg_cache_in_gerror_setup (arg_cache); + + if (direction == GI_DIRECTION_OUT || direction == GI_DIRECTION_INOUT) + _arg_cache_out_gerror_setup (arg_cache); + + break; + } + + if (arg_cache != NULL) { + arg_cache->direction = direction; + arg_cache->transfer = transfer; + arg_cache->type_tag = type_tag; + arg_cache->py_arg_index = py_arg_index; + arg_cache->c_arg_index = c_arg_index; + arg_cache->is_pointer = g_type_info_is_pointer (type_info); + g_base_info_ref ( (GIBaseInfo *) type_info); + arg_cache->type_info = type_info; + } + + return arg_cache; +} + +/* Generate the cache for the callable's arguments */ +static gboolean +_args_cache_generate (GICallableInfo *callable_info, + PyGICallableCache *callable_cache) +{ + gssize arg_index = 0; + gssize i; + GITypeInfo *return_info; + GITransfer return_transfer; + PyGIArgCache *return_cache; + + /* cache the return arg */ + return_info = + g_callable_info_get_return_type (callable_info); + return_transfer = + g_callable_info_get_caller_owns (callable_info); + return_cache = + _arg_cache_new (return_info, + callable_cache, + NULL, + return_transfer, + GI_DIRECTION_OUT, + -1, + -1); + + 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, + callable_cache, + NULL, + GI_TRANSFER_NOTHING, + GI_DIRECTION_IN, + arg_index, + 0); + + instance_cache->in_marshaller = _pygi_marshal_in_interface_instance; + g_base_info_unref ( (GIBaseInfo *)interface_info); + + if (instance_cache == NULL) + return FALSE; + + callable_cache->args_cache[arg_index] = instance_cache; + + arg_index++; + callable_cache->n_in_args++; + callable_cache->n_py_args++; + } + + + for (i=0; arg_index < callable_cache->n_args; arg_index++, i++) { + PyGIArgCache *arg_cache = NULL; + GIArgInfo *arg_info; + GITypeInfo *type_info; + GIDirection direction; + GITransfer transfer; + GITypeTag type_tag; + gboolean is_caller_allocates = FALSE; + gssize py_arg_index = -1; + + arg_info = + g_callable_info_get_arg (callable_info, i); + + direction = g_arg_info_get_direction (arg_info); + transfer = g_arg_info_get_ownership_transfer (arg_info); + type_info = g_arg_info_get_type (arg_info); + type_tag = g_type_info_get_tag (type_info); + + if (type_tag == GI_TYPE_TAG_INTERFACE) + is_caller_allocates = g_arg_info_is_caller_allocates (arg_info); + + /* must be an child arg filled in by its owner + * fill in it's c_arg_index, add to the in count + * and continue + */ + if (callable_cache->args_cache[arg_index] != NULL) { + arg_cache = callable_cache->args_cache[arg_index]; + if (arg_cache->meta_type == PYGI_META_ARG_TYPE_CHILD_WITH_PYARG) { + arg_cache->py_arg_index = callable_cache->n_py_args; + callable_cache->n_py_args++; + } + + if (direction == GI_DIRECTION_IN || direction == GI_DIRECTION_INOUT) { + arg_cache->c_arg_index = callable_cache->n_in_args; + callable_cache->n_in_args++; + } + + if (direction == GI_DIRECTION_OUT || direction == GI_DIRECTION_INOUT) { + callable_cache->n_out_args++; + callable_cache->n_out_child_args++; + } + + g_base_info_unref ( (GIBaseInfo *)arg_info); + continue; + } + + if (direction == GI_DIRECTION_IN || direction == GI_DIRECTION_INOUT) { + py_arg_index = callable_cache->n_py_args; + callable_cache->n_in_args++; + callable_cache->n_py_args++; + } + + arg_cache = + _arg_cache_new (type_info, + callable_cache, + arg_info, + transfer, + direction, + arg_index, + py_arg_index); + + if (arg_cache == NULL) + goto arg_err; + + arg_cache->allow_none = g_arg_info_may_be_null(arg_info); + arg_cache->is_caller_allocates = is_caller_allocates; + + if (direction == GI_DIRECTION_OUT || direction == GI_DIRECTION_INOUT) { + callable_cache->n_out_args++; + + if (arg_cache == NULL) + goto arg_err; + + callable_cache->out_args = + g_slist_append (callable_cache->out_args, arg_cache); + } + + callable_cache->args_cache[arg_index] = arg_cache; + g_base_info_unref( (GIBaseInfo *)type_info); + g_base_info_unref( (GIBaseInfo *)arg_info); + + continue; +arg_err: + g_base_info_unref( (GIBaseInfo *)type_info); + g_base_info_unref( (GIBaseInfo *)arg_info); + return FALSE; + } + return TRUE; +} + +PyGICallableCache * +_pygi_callable_cache_new (GICallableInfo *callable_info) +{ + PyGICallableCache *cache; + GIInfoType type = g_base_info_get_type ( (GIBaseInfo *)callable_info); + + cache = g_slice_new0 (PyGICallableCache); + + if (cache == NULL) + return NULL; + + cache->name = g_base_info_get_name ((GIBaseInfo *)callable_info); + + if (type == GI_INFO_TYPE_FUNCTION) { + GIFunctionInfoFlags flags; + + flags = g_function_info_get_flags ( (GIFunctionInfo *)callable_info); + + 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) { + cache->function_type = PYGI_FUNCTION_TYPE_CALLBACK; + } else { + cache->function_type = PYGI_FUNCTION_TYPE_METHOD; + } + + cache->n_args = g_callable_info_get_n_args (callable_info); + + /* 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->n_args++; + + if (cache->n_args > 0) + cache->args_cache = g_slice_alloc0 (cache->n_args * sizeof (PyGIArgCache *)); + + if (!_args_cache_generate (callable_info, cache)) + goto err; + + return cache; +err: + _pygi_callable_cache_free (cache); + return NULL; +} diff --git a/gi/pygi-cache.h b/gi/pygi-cache.h new file mode 100644 index 00000000..6509149f --- /dev/null +++ b/gi/pygi-cache.h @@ -0,0 +1,172 @@ +/* -*- Mode: C; c-basic-offset: 4 -*- + * vim: tabstop=4 shiftwidth=4 expandtab + * + * Copyright (C) 2011 John (J5) Palmieri <johnp@redhat.com> + * + * 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, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 + * USA + */ + +#ifdef ENABLE_INVOKE_NG +#ifndef __PYGI_CACHE_H__ +#define __PYGI_CACHE_H__ + +#include <Python.h> +#include <girepository.h> + +#include "pygi-invoke-state-struct.h" + +G_BEGIN_DECLS + +typedef struct _PyGICallableCache PyGICallableCache; +typedef struct _PyGIArgCache PyGIArgCache; + +typedef gboolean (*PyGIMarshalInFunc) (PyGIInvokeState *state, + PyGICallableCache *callable_cache, + PyGIArgCache *arg_cache, + PyObject *py_arg, + GIArgument *arg); + +typedef PyObject *(*PyGIMarshalOutFunc) (PyGIInvokeState *state, + PyGICallableCache *callable_cache, + PyGIArgCache *arg_cache, + GIArgument *arg); + +typedef void (*PyGIMarshalCleanupFunc) (PyGIInvokeState *state, + PyGIArgCache *arg_cache, + gpointer data, + gboolean was_processed); + +/* Argument meta types denote how we process the argument: + * - Parents (PYGI_META_ARG_TYPE_PARENT) may or may not have children + * but are always processed via the normal marshaller for their + * actual GI type. If they have children the marshaller will + * also handle marshalling the children. + * - Children without python argument (PYGI_META_ARG_TYPE_CHILD) are + * ignored by the marshallers and handled directly by their parents + * marshaller. + * - Children with pyargs (PYGI_META_ARG_TYPE_CHILD_WITH_PYARG) are processed + * the same as other child args but also have an index into the + * python parameters passed to the invoker + */ +typedef enum { + PYGI_META_ARG_TYPE_PARENT, + PYGI_META_ARG_TYPE_CHILD, + PYGI_META_ARG_TYPE_CHILD_WITH_PYARG +} 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. + */ + typedef enum { + PYGI_FUNCTION_TYPE_FUNCTION, + PYGI_FUNCTION_TYPE_METHOD, + PYGI_FUNCTION_TYPE_CONSTRUCTOR, + PYGI_FUNCTION_TYPE_VFUNC, + PYGI_FUNCTION_TYPE_CALLBACK + } PyGIFunctionType; + +struct _PyGIArgCache +{ + PyGIMetaArgType meta_type; + gboolean is_pointer; + gboolean is_caller_allocates; + gboolean allow_none; + + GIDirection direction; + GITransfer transfer; + GITypeTag type_tag; + GITypeInfo *type_info; + + PyGIMarshalInFunc in_marshaller; + PyGIMarshalOutFunc out_marshaller; + + PyGIMarshalCleanupFunc in_cleanup; + PyGIMarshalCleanupFunc out_cleanup; + + GDestroyNotify destroy_notify; + + gssize c_arg_index; + gssize py_arg_index; +}; + +typedef struct _PyGISequenceCache +{ + PyGIArgCache arg_cache; + gssize fixed_size; + gssize len_arg_index; + gboolean is_zero_terminated; + gsize item_size; + GIArrayType array_type; + PyGIArgCache *item_cache; +} PyGISequenceCache; + +typedef struct _PyGIInterfaceCache +{ + PyGIArgCache arg_cache; + gboolean is_foreign; + GType g_type; + PyObject *py_type; + GIInterfaceInfo *interface_info; + gchar *type_name; +} PyGIInterfaceCache; + +typedef struct _PyGIHashCache +{ + PyGIArgCache arg_cache; + PyGIArgCache *key_cache; + PyGIArgCache *value_cache; +} PyGIHashCache; + +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; + + PyGIFunctionType function_type; + + PyGIArgCache *return_cache; + PyGIArgCache **args_cache; + GSList *out_args; + + /* counts */ + gssize n_in_args; + gssize n_out_args; + gssize n_out_child_args; + + gssize n_args; + gssize n_py_args; +}; + +void _pygi_arg_cache_clear (PyGIArgCache *cache); +void _pygi_callable_cache_free (PyGICallableCache *cache); + +PyGICallableCache *_pygi_callable_cache_new (GICallableInfo *callable_info); + +G_END_DECLS + +#endif /* __PYGI_CACHE_H__ */ +#endif /* def ENABLE_INVOKE_NG */ diff --git a/gi/pygi-foreign-gvariant.h b/gi/pygi-foreign-gvariant.h index b0c97811..43ea9c79 100644 --- a/gi/pygi-foreign-gvariant.h +++ b/gi/pygi-foreign-gvariant.h @@ -26,9 +26,9 @@ #include "pygi-foreign.h" -PyObject *g_variant_to_arg(PyObject *value, - GITypeInfo *type_info, - GITransfer transfer, +PyObject *g_variant_to_arg(PyObject *value, + GIInterfaceInfo *interface_info, + GITransfer transfer, GIArgument *arg); PyObject *g_variant_from_arg(GITypeInfo *type_info, diff --git a/gi/pygi-foreign.c b/gi/pygi-foreign.c index 127f8eae..0398450e 100644 --- a/gi/pygi-foreign.c +++ b/gi/pygi-foreign.c @@ -117,8 +117,11 @@ pygi_struct_foreign_convert_to_g_argument (PyObject *value, GIBaseInfo *base_info = (GIBaseInfo *) interface_info; PyGIForeignStruct *foreign_struct = pygi_struct_foreign_lookup (base_info); - if (foreign_struct == NULL) - return NULL; + if (foreign_struct == NULL) { + PyErr_Format(PyExc_KeyError, "could not find foreign type %s", + g_base_info_get_name (base_info)); + return FALSE; + } result = foreign_struct->to_func (value, interface_info, transfer, arg); return result; diff --git a/gi/pygi-info.c b/gi/pygi-info.c index 1bfd7d82..56325110 100644 --- a/gi/pygi-info.c +++ b/gi/pygi-info.c @@ -22,6 +22,7 @@ */ #include "pygi-private.h" +#include "pygi-cache.h" #include <pygobject.h> #include <pyglib-python-compat.h> @@ -37,6 +38,10 @@ _base_info_dealloc (PyGIBaseInfo *self) g_base_info_unref (self->info); +#ifdef ENABLE_INVOKE_NG + _pygi_callable_cache_free(self->cache); +#endif + Py_TYPE( (PyObject *) self)->tp_free ( (PyObject *) self); } diff --git a/gi/pygi-invoke-ng.c b/gi/pygi-invoke-ng.c new file mode 100644 index 00000000..d42a9de4 --- /dev/null +++ b/gi/pygi-invoke-ng.c @@ -0,0 +1,464 @@ +/* -*- Mode: C; c-basic-offset: 4 -*- + * vim: tabstop=4 shiftwidth=4 expandtab + * + * Copyright (C) 2005-2009 Johan Dahlin <johan@gnome.org> + * Copyright (C) 2011 John (J5) Palimier <johnp@redhat.com> + * + * pygi-invoke.c: main invocation function + * + * 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, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 + * USA + */ + +#include <pyglib.h> +#include "pygi-invoke.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_in_args, + state->out_args, + cache->n_out_args, + &state->return_arg, + &error); + else + retval = g_function_info_invoke ( callable_info, + state->in_args, + cache->n_in_args, + state->out_args, + cache->n_out_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_in_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_in_marshal_success (state, cache); + return FALSE; + } + } + + return TRUE; +} + +static inline gboolean +_invoke_state_init_from_callable_cache (PyGIInvokeState *state, + PyGICallableCache *cache, + PyObject *py_args, + PyObject *kwargs) +{ + state->py_in_args = py_args; + state->n_py_in_args = PySequence_Length (py_args); + + /* 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; + } + + /* 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, state->n_py_in_args); + state->n_py_in_args--; + } else { + Py_INCREF (state->py_in_args); + } + state->implementor_gtype = 0; + if (cache->function_type == PYGI_FUNCTION_TYPE_VFUNC) { + PyObject *py_gtype; + py_gtype = PyDict_GetItemString (kwargs, "gtype"); + 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; + } + + state->args = g_slice_alloc0 (cache->n_args * sizeof (GIArgument *)); + if (state->args == NULL && cache->n_args != 0) { + PyErr_NoMemory(); + return FALSE; + } + + state->in_args = g_slice_alloc0 (cache->n_in_args * sizeof(GIArgument)); + if (state->in_args == NULL && cache->n_in_args != 0) { + PyErr_NoMemory (); + return FALSE; + } + + state->out_values = g_slice_alloc0 (cache->n_out_args * sizeof(GIArgument)); + if (state->out_values == NULL && cache->n_out_args != 0) { + PyErr_NoMemory (); + return FALSE; + } + + state->out_args = g_slice_alloc0 (cache->n_out_args * sizeof(GIArgument)); + if (state->out_args == NULL && cache->n_out_args != 0) { + PyErr_NoMemory (); + return FALSE; + } + + state->error = NULL; + + return TRUE; +} + +static inline void +_invoke_state_clear (PyGIInvokeState *state, PyGICallableCache *cache) +{ + g_slice_free1 (cache->n_args * sizeof(GIArgument *), state->args); + g_slice_free1 (cache->n_in_args * sizeof(GIArgument), state->in_args); + g_slice_free1 (cache->n_out_args * sizeof(GIArgument), state->out_args); + g_slice_free1 (cache->n_out_args * sizeof(GIArgument), state->out_values); + + Py_XDECREF (state->py_in_args); +} + +static gboolean _caller_alloc (PyGIInvokeState *state, + PyGIArgCache *arg_cache, + gssize arg_count, + gssize out_count) +{ + 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]); + } else { + gssize size = g_struct_info_get_size( + (GIStructInfo *)iface_cache->interface_info); + state->args[arg_count]->v_pointer = g_malloc0 (size); + } + + if (state->args[arg_count]->v_pointer == NULL) + return FALSE; + + + return TRUE; +} + +static inline gboolean +_invoke_marshal_in_args (PyGIInvokeState *state, PyGICallableCache *cache) +{ + gssize i, in_count, out_count; + in_count = 0; + out_count = 0; + + if (state->n_py_in_args > cache->n_py_args) { + PyErr_Format (PyExc_TypeError, + "%s() takes exactly %zd argument(s) (%zd given)", + cache->name, + cache->n_py_args, + state->n_py_in_args); + return FALSE; + } + + for (i = 0; i < cache->n_args; i++) { + GIArgument *c_arg; + PyGIArgCache *arg_cache = cache->args_cache[i]; + PyObject *py_arg = NULL; + + switch (arg_cache->direction) { + case GI_DIRECTION_IN: + state->args[i] = &(state->in_args[in_count]); + in_count++; + + if (arg_cache->meta_type > 0) + continue; + + if (arg_cache->py_arg_index >= state->n_py_in_args) { + PyErr_Format (PyExc_TypeError, + "%s() takes exactly %zd argument(s) (%zd given)", + cache->name, + cache->n_py_args, + state->n_py_in_args); + + /* clean up all of the args we have already marshalled, + * since invoke will not be called + */ + pygi_marshal_cleanup_args_in_parameter_fail (state, + cache, + i - 1); + return FALSE; + } + + py_arg = + PyTuple_GET_ITEM (state->py_in_args, + arg_cache->py_arg_index); + + break; + case GI_DIRECTION_INOUT: + /* 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) { + PyErr_Format (PyExc_TypeError, + "%s() takes exactly %zd argument(s) (%zd given)", + cache->name, + cache->n_py_args, + state->n_py_in_args); + pygi_marshal_cleanup_args_in_parameter_fail (state, + cache, + i - 1); + return FALSE; + } + + py_arg = + PyTuple_GET_ITEM (state->py_in_args, + arg_cache->py_arg_index); + } + case GI_DIRECTION_OUT: + if (arg_cache->is_caller_allocates) { + if (!_caller_alloc (state, arg_cache, i, out_count)) { + PyErr_Format (PyExc_TypeError, + "Could not caller allocate argument %zd of callable %s", + i, cache->name); + pygi_marshal_cleanup_args_in_parameter_fail (state, + cache, + i - 1); + return FALSE; + } + } else { + state->out_args[out_count].v_pointer = &state->out_values[out_count]; + state->args[i] = &state->out_values[out_count]; + } + out_count++; + break; + } + + c_arg = state->args[i]; + if (arg_cache->in_marshaller != NULL) { + if (!arg_cache->allow_none && py_arg == Py_None) { + PyErr_Format (PyExc_TypeError, + "Argument %i does not allow None as a value", + i); + + pygi_marshal_cleanup_args_in_parameter_fail (state, + cache, + i - 1); + return FALSE; + } + gboolean success = arg_cache->in_marshaller (state, + cache, + arg_cache, + py_arg, + c_arg); + if (!success) { + pygi_marshal_cleanup_args_in_parameter_fail (state, + cache, + i - 1); + return FALSE; + } + + } + + } + + return TRUE; +} + +static inline PyObject * +_invoke_marshal_out_args (PyGIInvokeState *state, PyGICallableCache *cache) +{ + PyObject *py_out = NULL; + PyObject *py_return = NULL; + gssize total_out_args = cache->n_out_args; + gboolean has_return = FALSE; + + if (cache->return_cache) { + + 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->out_marshaller ( state, + cache, + cache->return_cache, + &state->return_arg); + if (py_return == NULL) { + pygi_marshal_cleanup_args_return_fail (state, + cache); + return NULL; + } + + + if (cache->return_cache->type_tag != GI_TYPE_TAG_VOID) { + total_out_args++; + has_return = TRUE; + } + } + + total_out_args -= cache->n_out_child_args; + + if (cache->n_out_args - cache->n_out_child_args == 0) { + py_out = py_return; + } else if (total_out_args == 1) { + /* if we get here there is one out arg an no return */ + PyGIArgCache *arg_cache = (PyGIArgCache *)cache->out_args->data; + py_out = arg_cache->out_marshaller (state, + cache, + arg_cache, + state->args[arg_cache->c_arg_index]); + if (py_out == NULL) { + pygi_marshal_cleanup_args_out_parameter_fail (state, + cache, + 0); + return NULL; + } + + } else { + gssize py_arg_index = 0; + GSList *cache_item = cache->out_args; + /* return a tuple */ + py_out = PyTuple_New (total_out_args); + if (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++) { + PyGIArgCache *arg_cache = (PyGIArgCache *)cache_item->data; + PyObject *py_obj = arg_cache->out_marshaller (state, + cache, + arg_cache, + state->args[arg_cache->c_arg_index]); + + if (py_obj == NULL) { + if (has_return) + py_arg_index--; + + pygi_marshal_cleanup_args_out_parameter_fail (state, + cache, + py_arg_index); + Py_DECREF (py_out); + return NULL; + } + + PyTuple_SET_ITEM (py_out, py_arg_index, py_obj); + cache_item = cache_item->next; + } + } + return py_out; +} + +PyObject * +_wrap_g_callable_info_invoke (PyGIBaseInfo *self, + PyObject *py_args, + PyObject *kwargs) +{ + PyGIInvokeState state = { 0, }; + PyObject *ret = NULL; + + if (self->cache == NULL) { + self->cache = _pygi_callable_cache_new (self->info); + if (self->cache == NULL) + return NULL; + } + + _invoke_state_init_from_callable_cache (&state, self->cache, py_args, kwargs); + if (!_invoke_marshal_in_args (&state, self->cache)) + goto err; + + if (!_invoke_callable (&state, self->cache, self->info)) + goto err; + + pygi_marshal_cleanup_args_in_marshal_success (&state, self->cache); + + ret = _invoke_marshal_out_args (&state, self->cache); + if (ret) + pygi_marshal_cleanup_args_out_marshal_success (&state, self->cache); +err: + _invoke_state_clear (&state, self->cache); + return ret; +} diff --git a/gi/pygi-invoke-state-struct.h b/gi/pygi-invoke-state-struct.h new file mode 100644 index 00000000..a4072b78 --- /dev/null +++ b/gi/pygi-invoke-state-struct.h @@ -0,0 +1,44 @@ +#ifndef __PYGI_INVOKE_STATE_STRUCT_H__ +#define __PYGI_INVOKE_STATE_STRUCT_H__ + +#include <Python.h> + +#include <girepository.h> + +G_BEGIN_DECLS + +typedef struct _PyGIInvokeState +{ + PyObject *py_in_args; + gssize n_py_in_args; + gssize current_arg; + + GType implementor_gtype; + + GIArgument **args; + GIArgument *in_args; + + /* Out args and out values + * In order to pass a parameter and get something back out in C + * we need to pass a pointer to the value, e.g. + * int *out_integer; + * + * so while out_args == out_integer, out_value == *out_integer + * or in other words out_args = &out_values + * + * We do all of our processing on out_values but we pass out_args to + * the actual function. + */ + GIArgument *out_args; + GIArgument *out_values; + + GIArgument return_arg; + + GError *error; + + gboolean failed; +} PyGIInvokeState; + +G_END_DECLS + +#endif diff --git a/gi/pygi-invoke.h b/gi/pygi-invoke.h index dc1ce18a..0aa75806 100644 --- a/gi/pygi-invoke.h +++ b/gi/pygi-invoke.h @@ -27,7 +27,7 @@ #include <girepository.h> #include "pygi-private.h" - +#include "pygi-invoke-state-struct.h" G_BEGIN_DECLS PyObject *_wrap_g_callable_info_invoke (PyGIBaseInfo *self, PyObject *py_args, diff --git a/gi/pygi-marshal-cleanup.c b/gi/pygi-marshal-cleanup.c new file mode 100644 index 00000000..cd072f42 --- /dev/null +++ b/gi/pygi-marshal-cleanup.c @@ -0,0 +1,517 @@ +/* -*- Mode: C; c-basic-offset: 4 -*- + * vim: tabstop=4 shiftwidth=4 expandtab + * + * Copyright (C) 2011 John (J5) Palmieri <johnp@redhat.com>, Red Hat, Inc. + * + * 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, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 + * USA + */ + + #include "pygi-marshal-cleanup.h" + #include <glib.h> +static inline void +_cleanup_caller_allocates (PyGIInvokeState *state, + PyGIArgCache *cache, + gpointer data) +{ + PyGIInterfaceCache *iface_cache = (PyGIInterfaceCache *)cache; + + if (iface_cache->g_type == G_TYPE_BOXED) { + gsize size; + size = g_struct_info_get_size (iface_cache->interface_info); + g_slice_free1 (size, data); + } else if (iface_cache->g_type == G_TYPE_VALUE) { + g_slice_free (GValue, data); + } else if (iface_cache->is_foreign) { + pygi_struct_foreign_release ((GIBaseInfo *)iface_cache->interface_info, + data); + } else { + g_free (data); + } +} + +/** + * Cleanup during invoke can happen in multiple + * stages, each of which can be the result of a + * successful compleation of that stage or an error + * occured which requires partial cleanup. + * + * For the most part, either the C interface being + * invoked or the python object which wraps the + * parameters, handle their lifecycles but in some + * cases, where we have intermediate objects, + * or when we fail processing a parameter, we need + * to handle the clean up manually. + * + * There are two argument processing stages. + * They are the in stage, where we process python + * parameters into their C counterparts, and the out + * stage, where we process out C parameters back + * into python objects. The in stage also sets up + * temporary out structures for caller allocated + * parameters which need to be cleaned up either on + * in stage failure or at the completion of the out + * stage (either success or failure) + * + * The in stage must call one of these cleanup functions: + * - pygi_marshal_cleanup_args_in_marshal_success + * (continue to out stage) + * - pygi_marshal_cleanup_args_in_parameter_fail + * (final, exit from invoke) + * + * The out stage must call one of these cleanup functions which are all final: + * - pygi_marshal_cleanup_args_out_marshal_success + * - pygi_marshal_cleanup_args_return_fail + * - pygi_marshal_cleanup_args_out_parameter_fail + * + **/ +void +pygi_marshal_cleanup_args_in_marshal_success (PyGIInvokeState *state, + PyGICallableCache *cache) +{ + gssize i; + + /* For in success, call cleanup for all GI_DIRECTION_IN values only. */ + for (i = 0; i < cache->n_args; i++) { + PyGIArgCache *arg_cache = cache->args_cache[i]; + PyGIMarshalCleanupFunc cleanup_func = arg_cache->in_cleanup; + + if (cleanup_func && + arg_cache->direction == GI_DIRECTION_IN && + state->args[i]->v_pointer != NULL) + cleanup_func (state, arg_cache, state->args[i]->v_pointer, TRUE); + } +} + +void +pygi_marshal_cleanup_args_out_marshal_success (PyGIInvokeState *state, + PyGICallableCache *cache) +{ + /* clean up the return if available */ + if (cache->return_cache != NULL) { + PyGIMarshalCleanupFunc cleanup_func = cache->return_cache->out_cleanup; + if (cleanup_func && state->return_arg.v_pointer != NULL) + cleanup_func (state, + cache->return_cache, + state->return_arg.v_pointer, + TRUE); + } + + /* Now clean up args */ + GSList *cache_item = cache->out_args; + while (cache_item) { + PyGIArgCache *arg_cache = (PyGIArgCache *) cache_item->data; + PyGIMarshalCleanupFunc cleanup_func = arg_cache->out_cleanup; + gpointer data = state->args[arg_cache->c_arg_index]->v_pointer; + + if (cleanup_func != NULL && data != NULL) + cleanup_func (state, + arg_cache, + data, + TRUE); + + cache_item = cache_item->next; + } +} + +void +pygi_marshal_cleanup_args_in_parameter_fail (PyGIInvokeState *state, + PyGICallableCache *cache, + gssize failed_arg_index) +{ + gssize i; + + state->failed = TRUE; + + for (i = 0; i < cache->n_args && i <= failed_arg_index; i++) { + PyGIArgCache *arg_cache = cache->args_cache[i]; + PyGIMarshalCleanupFunc cleanup_func = arg_cache->in_cleanup; + gpointer data = state->args[i]->v_pointer; + + if (cleanup_func && + arg_cache->direction == GI_DIRECTION_IN && + data != NULL) { + cleanup_func (state, + arg_cache, + data, + i < failed_arg_index); + + } else if (arg_cache->is_caller_allocates && data != NULL) { + _cleanup_caller_allocates (state, + arg_cache, + data); + } + } +} + +void +pygi_marshal_cleanup_args_return_fail (PyGIInvokeState *state, + PyGICallableCache *cache) +{ + state->failed = TRUE; +} + +void +pygi_marshal_cleanup_args_out_parameter_fail (PyGIInvokeState *state, + PyGICallableCache *cache, + gssize failed_out_arg_index) +{ + state->failed = TRUE; +} + +void +_pygi_marshal_cleanup_closure_unref (PyGIInvokeState *state, + PyGIArgCache *arg_cache, + gpointer data, + gboolean was_processed) +{ + g_closure_unref ( (GClosure *)data); +} + +void +_pygi_marshal_cleanup_in_utf8 (PyGIInvokeState *state, + PyGIArgCache *arg_cache, + gpointer data, + gboolean was_processed) +{ + /* We strdup strings so always free if we have processed this + parameter for input */ + if (was_processed) + g_free (data); +} + +void +_pygi_marshal_cleanup_out_utf8 (PyGIInvokeState *state, + PyGIArgCache *arg_cache, + gpointer data, + gboolean was_processed) +{ + /* Python copies the string so we need to free it + if the interface is transfering ownership, + whether or not it has been processed yet */ + if (arg_cache->transfer == GI_TRANSFER_EVERYTHING) + g_free (data); +} + +void +_pygi_marshal_cleanup_in_interface_object (PyGIInvokeState *state, + PyGIArgCache *arg_cache, + 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_out_interface_object (PyGIInvokeState *state, + PyGIArgCache *arg_cache, + 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_in_interface_struct_gvalue (PyGIInvokeState *state, + PyGIArgCache *arg_cache, + gpointer data, + gboolean was_processed) +{ + if (was_processed) { + PyObject *py_arg = PyTuple_GET_ITEM (state->py_in_args, + arg_cache->py_arg_index); + GType py_object_type = + pyg_type_from_object_strict ( (PyObject *) py_arg->ob_type, FALSE); + + if (py_object_type != G_TYPE_VALUE) { + g_value_unset ((GValue *) data); + g_slice_free (GValue, data); + } + } +} + +void +_pygi_marshal_cleanup_in_interface_struct_foreign (PyGIInvokeState *state, + PyGIArgCache *arg_cache, + gpointer data, + gboolean was_processed) +{ + if (state->failed && was_processed) + pygi_struct_foreign_release ( + ( (PyGIInterfaceCache *)arg_cache)->interface_info, + data); +} + +void +_pygi_marshal_cleanup_out_interface_struct_foreign (PyGIInvokeState *state, + PyGIArgCache *arg_cache, + gpointer data, + gboolean was_processed) +{ + if (!was_processed && arg_cache->transfer == GI_TRANSFER_EVERYTHING) + pygi_struct_foreign_release ( + ( (PyGIInterfaceCache *)arg_cache)->interface_info, + data); +} + +void +_pygi_marshal_cleanup_in_array (PyGIInvokeState *state, + PyGIArgCache *arg_cache, + gpointer data, + gboolean was_processed) +{ + if (was_processed) { + GArray *array_; + PyGISequenceCache *sequence_cache = (PyGISequenceCache *)arg_cache; + + /* If this isn't a garray create one to help process variable sized + array elements */ + if (sequence_cache->array_type == GI_ARRAY_TYPE_C) { + gsize len; + if (sequence_cache->fixed_size >= 0) { + len = sequence_cache->fixed_size; + } else if (sequence_cache->is_zero_terminated) { + len = g_strv_length ((gchar **)data); + } else { + GIArgument *len_arg = state->args[sequence_cache->len_arg_index]; + len = len_arg->v_long; + } + + array_ = g_array_new (FALSE, + FALSE, + sequence_cache->item_size); + + if (array_ == NULL) + return; + + array_->data = data; + array_->len = len; + + } else { + array_ = (GArray *) data; + } + + /* clean up items first */ + if (sequence_cache->item_cache->in_cleanup != NULL) { + gsize i; + PyGIMarshalCleanupFunc cleanup_func = + sequence_cache->item_cache->in_cleanup; + + for(i = 0; i < array_->len; i++) { + cleanup_func (state, + sequence_cache->item_cache, + g_array_index (array_, gpointer, i), + TRUE); + } + } + + if (state->failed || + arg_cache->transfer == GI_TRANSFER_NOTHING || + arg_cache->transfer == GI_TRANSFER_CONTAINER) { + g_array_free (array_, TRUE); + } else if (sequence_cache->array_type == GI_ARRAY_TYPE_C) { + g_array_free (array_, FALSE); + } + } +} + +void +_pygi_marshal_cleanup_out_array (PyGIInvokeState *state, + PyGIArgCache *arg_cache, + gpointer data, + gboolean was_processed) +{ + PyGISequenceCache *sequence_cache = (PyGISequenceCache *)arg_cache; + + if (arg_cache->transfer == GI_TRANSFER_EVERYTHING || + arg_cache->transfer == GI_TRANSFER_CONTAINER) { + GArray *array_ = (GArray *) data; + + if (sequence_cache->array_type == GI_ARRAY_TYPE_C) { + g_free (data); + return; + } + + if (sequence_cache->item_cache->out_cleanup != NULL) { + gsize i; + + PyGIMarshalCleanupFunc cleanup_func = sequence_cache->item_cache->out_cleanup; + for (i = 0; i < array_->len; i++) { + cleanup_func (state, + sequence_cache->item_cache, + g_array_index (array_, gpointer, i), + was_processed); + } + } + + if (arg_cache->transfer == GI_TRANSFER_EVERYTHING) + g_array_free (array_, TRUE); + } +} + +void +_pygi_marshal_cleanup_in_glist (PyGIInvokeState *state, + PyGIArgCache *arg_cache, + gpointer data, + gboolean was_processed) +{ + if (was_processed) { + GSList *list_; + PyGISequenceCache *sequence_cache = (PyGISequenceCache *)arg_cache; + + list_ = (GSList *)data; + + /* clean up items first */ + if (sequence_cache->item_cache->in_cleanup != NULL) { + PyGIMarshalCleanupFunc cleanup_func = + sequence_cache->item_cache->in_cleanup; + GSList *node = list_; + while (node != NULL) { + cleanup_func (state, + sequence_cache->item_cache, + node->data, + TRUE); + node = node->next; + } + } + + if (state->failed || + arg_cache->transfer == GI_TRANSFER_NOTHING || + arg_cache->transfer == GI_TRANSFER_CONTAINER) { + switch (arg_cache->type_tag) { + case GI_TYPE_TAG_GLIST: + g_list_free ( (GList *)list_); + break; + case GI_TYPE_TAG_GSLIST: + g_slist_free (list_); + break; + default: + g_assert_not_reached(); + } + } + } +} + +void +_pygi_marshal_cleanup_out_glist (PyGIInvokeState *state, + PyGIArgCache *arg_cache, + gpointer data, + gboolean was_processed) +{ + PyGISequenceCache *sequence_cache = (PyGISequenceCache *)arg_cache; + + if (arg_cache->transfer == GI_TRANSFER_EVERYTHING || + arg_cache->transfer == GI_TRANSFER_CONTAINER) { + GSList *list_ = (GSList *)data; + + if (sequence_cache->item_cache->out_cleanup != NULL) { + PyGIMarshalCleanupFunc cleanup_func = + sequence_cache->item_cache->out_cleanup; + GSList *node = list_; + + while (node != NULL) { + cleanup_func (state, + sequence_cache->item_cache, + node->data, + was_processed); + node = node->next; + } + } + + if (arg_cache->transfer == GI_TRANSFER_EVERYTHING) { + switch (arg_cache->type_tag) { + case GI_TYPE_TAG_GLIST: + g_list_free ( (GList *)list_); + break; + case GI_TYPE_TAG_GSLIST: + g_slist_free (list_); + break; + default: + g_assert_not_reached(); + } + } + } +} + +void +_pygi_marshal_cleanup_in_ghash (PyGIInvokeState *state, + PyGIArgCache *arg_cache, + gpointer data, + gboolean was_processed) +{ + if (data == NULL) + return; + + if (was_processed) { + GHashTable *hash_; + PyGIHashCache *hash_cache = (PyGIHashCache *)arg_cache; + + hash_ = (GHashTable *)data; + + /* clean up keys and values first */ + if (hash_cache->key_cache->in_cleanup != NULL || + hash_cache->value_cache->in_cleanup != NULL) { + GHashTableIter hiter; + gpointer key; + gpointer value; + + PyGIMarshalCleanupFunc key_cleanup_func = + hash_cache->key_cache->in_cleanup; + PyGIMarshalCleanupFunc value_cleanup_func = + hash_cache->value_cache->in_cleanup; + + g_hash_table_iter_init (&hiter, hash_); + while (g_hash_table_iter_next (&hiter, &key, &value)) { + if (key != NULL && key_cleanup_func != NULL) + key_cleanup_func (state, + hash_cache->key_cache, + key, + TRUE); + if (value != NULL && value_cleanup_func != NULL) + value_cleanup_func (state, + hash_cache->value_cache, + value, + TRUE); + } + } + + if (state->failed || + arg_cache->transfer == GI_TRANSFER_NOTHING || + arg_cache->transfer == GI_TRANSFER_CONTAINER) + g_hash_table_destroy (hash_); + + } +} + +void +_pygi_marshal_cleanup_out_ghash (PyGIInvokeState *state, + PyGIArgCache *arg_cache, + gpointer data, + gboolean was_processed) +{ + if (data == NULL) + return; + + /* assume hashtable has boxed key and value */ + if (arg_cache->transfer == GI_TRANSFER_EVERYTHING) + g_hash_table_destroy ( (GHashTable *)data); +} diff --git a/gi/pygi-marshal-cleanup.h b/gi/pygi-marshal-cleanup.h new file mode 100644 index 00000000..3aff8fa1 --- /dev/null +++ b/gi/pygi-marshal-cleanup.h @@ -0,0 +1,97 @@ +/* -*- Mode: C; c-basic-offset: 4 -*- + * vim: tabstop=4 shiftwidth=4 expandtab + * + * Copyright (C) 2011 John (J5) Palmieri <johnp@redhat.com>, Red Hat, Inc. + * + * 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, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 + * USA + */ + +#ifndef __PYGI_MARSHAL_CLEANUP_H__ +#define __PYGI_MARSHAL_CLEANUP_H__ + +#include "pygi-private.h" + +G_BEGIN_DECLS + +void pygi_marshal_cleanup_args_in_marshal_success (PyGIInvokeState *state, + PyGICallableCache *cache); +void pygi_marshal_cleanup_args_in_parameter_fail (PyGIInvokeState *state, + PyGICallableCache *cache, + gssize failed_arg_index); + +void pygi_marshal_cleanup_args_out_marshal_success (PyGIInvokeState *state, + PyGICallableCache *cache); +void pygi_marshal_cleanup_args_return_fail (PyGIInvokeState *state, + PyGICallableCache *cache); +void pygi_marshal_cleanup_args_out_parameter_fail (PyGIInvokeState *state, + PyGICallableCache *cache, + gssize failed_out_arg_index); + +void _pygi_marshal_cleanup_in_utf8 (PyGIInvokeState *state, + PyGIArgCache *arg_cache, + gpointer data, + gboolean was_processed); +void _pygi_marshal_cleanup_out_utf8 (PyGIInvokeState *state, + PyGIArgCache *arg_cache, + gpointer data, + gboolean was_processed); +void _pygi_marshal_cleanup_in_interface_struct_gvalue (PyGIInvokeState *state, + PyGIArgCache *arg_cache, + gpointer data, + gboolean was_processed); +void _pygi_marshal_cleanup_in_interface_struct_foreign (PyGIInvokeState *state, + PyGIArgCache *arg_cache, + gpointer data, + gboolean was_processed); +void _pygi_marshal_cleanup_out_interface_struct_foreign (PyGIInvokeState *state, + PyGIArgCache *arg_cache, + gpointer data, + gboolean was_processed); +void _pygi_marshal_cleanup_in_interface_object (PyGIInvokeState *state, + PyGIArgCache *arg_cache, + gpointer data, + gboolean was_processed); +void _pygi_marshal_cleanup_out_interface_object (PyGIInvokeState *state, + PyGIArgCache *arg_cache, + gpointer data, + gboolean was_processed); +void _pygi_marshal_cleanup_in_array (PyGIInvokeState *state, + PyGIArgCache *arg_cache, + gpointer data, + gboolean was_processed); +void _pygi_marshal_cleanup_out_array (PyGIInvokeState *state, + PyGIArgCache *arg_cache, + gpointer data, + gboolean was_processed); +void _pygi_marshal_cleanup_in_glist (PyGIInvokeState *state, + PyGIArgCache *arg_cache, + gpointer data, + gboolean was_processed); +void _pygi_marshal_cleanup_out_glist (PyGIInvokeState *state, + PyGIArgCache *arg_cache, + gpointer data, + gboolean was_processed); +void _pygi_marshal_cleanup_in_ghash (PyGIInvokeState *state, + PyGIArgCache *arg_cache, + gpointer data, + gboolean was_processed); +void _pygi_marshal_cleanup_out_ghash (PyGIInvokeState *state, + PyGIArgCache *arg_cache, + gpointer data, + gboolean was_processed); +G_END_DECLS + +#endif /* __PYGI_MARSHAL_CLEANUP_H__ */ diff --git a/gi/pygi-marshal-in.c b/gi/pygi-marshal-in.c new file mode 100644 index 00000000..9ae7def8 --- /dev/null +++ b/gi/pygi-marshal-in.c @@ -0,0 +1,1412 @@ +/* -*- Mode: C; c-basic-offset: 4 -*- + * vim: tabstop=4 shiftwidth=4 expandtab + * + * Copyright (C) 2011 John (J5) Palmieri <johnp@redhat.com>, Red Hat, Inc. + * + * pygi-marshal-in.c: PyObject conversion functions for in parameters. + * + * 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, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 + * USA + */ + +#include "pygi-private.h" + +#include <string.h> +#include <time.h> + +#include <datetime.h> +#include <pygobject.h> +#include <pyglib-python-compat.h> + +#include "pygi-cache.h" +#include "pygi-marshal-cleanup.h" +#include "pygi-marshal-in.h" + +gboolean +_pygi_marshal_in_void (PyGIInvokeState *state, + PyGICallableCache *callable_cache, + PyGIArgCache *arg_cache, + PyObject *py_arg, + GIArgument *arg) +{ + g_warn_if_fail (arg_cache->transfer == GI_TRANSFER_NOTHING); + + arg->v_pointer = py_arg; + + return TRUE; +} + +gboolean +_pygi_marshal_in_boolean (PyGIInvokeState *state, + PyGICallableCache *callable_cache, + PyGIArgCache *arg_cache, + PyObject *py_arg, + GIArgument *arg) +{ + arg->v_boolean = PyObject_IsTrue (py_arg); + + return TRUE; +} + +gboolean +_pygi_marshal_in_int8 (PyGIInvokeState *state, + PyGICallableCache *callable_cache, + PyGIArgCache *arg_cache, + PyObject *py_arg, + GIArgument *arg) +{ + PyObject *py_long; + long long_; + + if (!PyNumber_Check (py_arg)) { + PyErr_Format (PyExc_TypeError, "Must be number, not %s", + py_arg->ob_type->tp_name); + return FALSE; + } + + py_long = PYGLIB_PyNumber_Long (py_arg); + if (!py_long) + return FALSE; + + long_ = PYGLIB_PyLong_AsLong (py_long); + Py_DECREF (py_long); + + if (PyErr_Occurred ()) { + PyErr_Clear (); + PyErr_Format (PyExc_ValueError, "%ld not in range %d to %d", long_, -128, 127); + return FALSE; + } + + if (long_ < -128 || long_ > 127) { + PyErr_Format (PyExc_ValueError, "%ld not in range %d to %d", long_, -128, 127); + return FALSE; + } + + arg->v_long = long_; + + return TRUE; +} + +gboolean +_pygi_marshal_in_uint8 (PyGIInvokeState *state, + PyGICallableCache *callable_cache, + PyGIArgCache *arg_cache, + PyObject *py_arg, + GIArgument *arg) +{ + unsigned long long_; + + if (PYGLIB_PyBytes_Check (py_arg)) { + + if (PYGLIB_PyBytes_Size (py_arg) != 1) { + PyErr_Format (PyExc_TypeError, "Must be a single character"); + return FALSE; + } + + long_ = (unsigned char)(PYGLIB_PyBytes_AsString (py_arg)[0]); + + } else if (PyNumber_Check (py_arg)) { + PyObject *py_long; + py_long = PYGLIB_PyNumber_Long (py_arg); + if (!py_long) + return FALSE; + + long_ = PYGLIB_PyLong_AsLong (py_long); + Py_DECREF (py_long); + + if (PyErr_Occurred ()) { + PyErr_Clear(); + + PyErr_Format (PyExc_ValueError, "%ld not in range %d to %d", long_, 0, 255); + return FALSE; + } + } else { + PyErr_Format (PyExc_TypeError, "Must be number or single byte string, not %s", + py_arg->ob_type->tp_name); + return FALSE; + } + + if (long_ < 0 || long_ > 255) { + PyErr_Format (PyExc_ValueError, "%ld not in range %d to %d", long_, 0, 255); + return FALSE; + } + + arg->v_long = long_; + + return TRUE; +} + +gboolean +_pygi_marshal_in_int16 (PyGIInvokeState *state, + PyGICallableCache *callable_cache, + PyGIArgCache *arg_cache, + PyObject *py_arg, + GIArgument *arg) +{ + PyObject *py_long; + long long_; + + if (!PyNumber_Check (py_arg)) { + PyErr_Format (PyExc_TypeError, "Must be number, not %s", + py_arg->ob_type->tp_name); + return FALSE; + } + + py_long = PYGLIB_PyNumber_Long (py_arg); + if (!py_long) + return FALSE; + + long_ = PYGLIB_PyLong_AsLong (py_long); + Py_DECREF (py_long); + + if (PyErr_Occurred ()) { + PyErr_Clear (); + PyErr_Format (PyExc_ValueError, "%ld not in range %d to %d", long_, -32768, 32767); + return FALSE; + } + + if (long_ < -32768 || long_ > 32767) { + PyErr_Format (PyExc_ValueError, "%ld not in range %d to %d", long_, -32768, 32767); + return FALSE; + } + + arg->v_long = long_; + + return TRUE; +} + +gboolean +_pygi_marshal_in_uint16 (PyGIInvokeState *state, + PyGICallableCache *callable_cache, + PyGIArgCache *arg_cache, + PyObject *py_arg, + GIArgument *arg) +{ + PyObject *py_long; + long long_; + + if (!PyNumber_Check (py_arg)) { + PyErr_Format (PyExc_TypeError, "Must be number, not %s", + py_arg->ob_type->tp_name); + return FALSE; + } + + py_long = PYGLIB_PyNumber_Long (py_arg); + if (!py_long) + return FALSE; + + long_ = PYGLIB_PyLong_AsLong (py_long); + Py_DECREF (py_long); + + if (PyErr_Occurred ()) { + PyErr_Clear (); + PyErr_Format (PyExc_ValueError, "%li not in range %d to %d", long_, 0, 65535); + return FALSE; + } + + if (long_ < 0 || long_ > 65535) { + PyErr_Format (PyExc_ValueError, "%li not in range %d to %d", long_, 0, 65535); + return FALSE; + } + + arg->v_long = long_; + + return TRUE; +} + +gboolean +_pygi_marshal_in_int32 (PyGIInvokeState *state, + PyGICallableCache *callable_cache, + PyGIArgCache *arg_cache, + PyObject *py_arg, + GIArgument *arg) +{ + PyObject *py_long; + long long_; + + if (!PyNumber_Check (py_arg)) { + PyErr_Format (PyExc_TypeError, "Must be number, not %s", + py_arg->ob_type->tp_name); + return FALSE; + } + + py_long = PYGLIB_PyNumber_Long (py_arg); + if (!py_long) + return FALSE; + + long_ = PYGLIB_PyLong_AsLong (py_long); + Py_DECREF (py_long); + + if (PyErr_Occurred ()) { + PyErr_Clear(); + PyErr_Format (PyExc_ValueError, "%ld not in range %d to %d", long_, G_MININT32, G_MAXINT32); + return FALSE; + } + + if (long_ < G_MININT32 || long_ > G_MAXINT32) { + PyErr_Format (PyExc_ValueError, "%ld not in range %d to %d", long_, G_MININT32, G_MAXINT32); + return FALSE; + } + + arg->v_long = long_; + + return TRUE; +} + +gboolean +_pygi_marshal_in_uint32 (PyGIInvokeState *state, + PyGICallableCache *callable_cache, + PyGIArgCache *arg_cache, + PyObject *py_arg, + GIArgument *arg) +{ + PyObject *py_long; + long long long_; + + if (!PyNumber_Check (py_arg)) { + PyErr_Format (PyExc_TypeError, "Must be number, not %s", + py_arg->ob_type->tp_name); + return FALSE; + } + + py_long = PYGLIB_PyNumber_Long (py_arg); + if (!py_long) + return FALSE; + +#if PY_VERSION_HEX < 0x03000000 + if (PyInt_Check (py_long)) + long_ = PyInt_AsLong (py_long); + else +#endif + long_ = PyLong_AsLongLong (py_long); + + Py_DECREF (py_long); + + if (PyErr_Occurred ()) { + PyErr_Clear (); + PyErr_Format (PyExc_ValueError, "%lli not in range %i to %u", long_, 0, G_MAXUINT32); + return FALSE; + } + + if (long_ < 0 || long_ > G_MAXUINT32) { + PyErr_Format (PyExc_ValueError, "%lli not in range %i to %u", long_, 0, G_MAXUINT32); + return FALSE; + } + + arg->v_uint64 = long_; + + return TRUE; +} + +gboolean +_pygi_marshal_in_int64 (PyGIInvokeState *state, + PyGICallableCache *callable_cache, + PyGIArgCache *arg_cache, + PyObject *py_arg, + GIArgument *arg) +{ + PyObject *py_long; + long long long_; + + if (!PyNumber_Check (py_arg)) { + PyErr_Format (PyExc_TypeError, "Must be number, not %s", + py_arg->ob_type->tp_name); + return FALSE; + } + + py_long = PYGLIB_PyNumber_Long (py_arg); + if (!py_long) + return FALSE; + +#if PY_VERSION_HEX < 0x03000000 + if (PyInt_Check (py_long)) + long_ = PyInt_AS_LONG (py_long); + else +#endif + long_ = PyLong_AsLongLong (py_long); + + Py_DECREF (py_long); + + if (PyErr_Occurred ()) { + /* OverflowError occured but range errors should be returned as ValueError */ + char *long_str; + PyObject *py_str; + + PyErr_Clear (); + + py_str = PyObject_Str (py_long); + + if (PyUnicode_Check (py_str)) { + PyObject *py_bytes = PyUnicode_AsUTF8String (py_str); + if (py_bytes == NULL) + return FALSE; + + long_str = g_strdup (PYGLIB_PyBytes_AsString (py_bytes)); + if (long_str == NULL) { + PyErr_NoMemory (); + return FALSE; + } + + Py_DECREF (py_bytes); + } else { + long_str = g_strdup (PYGLIB_PyBytes_AsString(py_str)); + } + + Py_DECREF (py_str); + PyErr_Format (PyExc_ValueError, "%s not in range %ld to %ld", + long_str, G_MININT64, G_MAXINT64); + + g_free (long_str); + return FALSE; + } + + if (long_ < G_MININT64 || long_ > G_MAXINT64) { + PyErr_Format (PyExc_ValueError, "%lld not in range %ld to %ld", long_, G_MININT64, G_MAXINT64); + return FALSE; + } + + arg->v_int64 = long_; + + return TRUE; +} + +gboolean +_pygi_marshal_in_uint64 (PyGIInvokeState *state, + PyGICallableCache *callable_cache, + PyGIArgCache *arg_cache, + PyObject *py_arg, + GIArgument *arg) +{ + PyObject *py_long; + guint64 ulong_; + + if (!PyNumber_Check (py_arg)) { + PyErr_Format (PyExc_TypeError, "Must be number, not %s", + py_arg->ob_type->tp_name); + return FALSE; + } + + py_long = PYGLIB_PyNumber_Long (py_arg); + if (!py_long) + return FALSE; + +#if PY_VERSION_HEX < 0x03000000 + if (PyInt_Check (py_long)) { + long long_ = PyInt_AsLong (py_long); + if (long_ < 0) { + PyErr_Format (PyExc_ValueError, "%ld not in range %d to %llu", + long_, 0, G_MAXUINT64); + return FALSE; + } + ulong_ = long_; + } else +#endif + ulong_ = PyLong_AsUnsignedLongLong (py_long); + + Py_DECREF (py_long); + + if (PyErr_Occurred ()) { + /* OverflowError occured but range errors should be returned as ValueError */ + char *long_str; + PyObject *py_str; + + PyErr_Clear (); + + py_str = PyObject_Str (py_long); + + if (PyUnicode_Check (py_str)) { + PyObject *py_bytes = PyUnicode_AsUTF8String (py_str); + if (py_bytes == NULL) + return FALSE; + + long_str = g_strdup (PYGLIB_PyBytes_AsString (py_bytes)); + if (long_str == NULL) { + PyErr_NoMemory (); + return FALSE; + } + + Py_DECREF (py_bytes); + } else { + long_str = g_strdup (PYGLIB_PyBytes_AsString (py_str)); + } + + Py_DECREF (py_str); + + PyErr_Format (PyExc_ValueError, "%s not in range %d to %llu", + long_str, 0, G_MAXUINT64); + + g_free (long_str); + return FALSE; + } + + if (ulong_ > G_MAXUINT64) { + PyErr_Format (PyExc_ValueError, "%llu not in range %d to %llu", ulong_, 0, G_MAXUINT64); + return FALSE; + } + + arg->v_uint64 = ulong_; + + return TRUE; +} + +gboolean +_pygi_marshal_in_float (PyGIInvokeState *state, + PyGICallableCache *callable_cache, + PyGIArgCache *arg_cache, + PyObject *py_arg, + GIArgument *arg) +{ + PyObject *py_float; + double double_; + + if (!PyNumber_Check (py_arg)) { + PyErr_Format (PyExc_TypeError, "Must be number, not %s", + py_arg->ob_type->tp_name); + return FALSE; + } + + py_float = PyNumber_Float (py_arg); + if (!py_float) + return FALSE; + + double_ = PyFloat_AsDouble (py_float); + Py_DECREF (py_float); + + if (PyErr_Occurred ()) { + PyErr_Clear (); + PyErr_Format (PyExc_ValueError, "%f not in range %f to %f", double_, -G_MAXFLOAT, G_MAXFLOAT); + return FALSE; + } + + if (double_ < -G_MAXFLOAT || double_ > G_MAXFLOAT) { + PyErr_Format (PyExc_ValueError, "%f not in range %f to %f", double_, -G_MAXFLOAT, G_MAXFLOAT); + return FALSE; + } + + arg->v_float = double_; + + return TRUE; +} + +gboolean +_pygi_marshal_in_double (PyGIInvokeState *state, + PyGICallableCache *callable_cache, + PyGIArgCache *arg_cache, + PyObject *py_arg, + GIArgument *arg) +{ + PyObject *py_float; + double double_; + + if (!PyNumber_Check (py_arg)) { + PyErr_Format (PyExc_TypeError, "Must be number, not %s", + py_arg->ob_type->tp_name); + return FALSE; + } + + py_float = PyNumber_Float (py_arg); + if (!py_float) + return FALSE; + + double_ = PyFloat_AsDouble (py_float); + Py_DECREF (py_float); + + if (PyErr_Occurred ()) { + PyErr_Clear (); + PyErr_Format (PyExc_ValueError, "%f not in range %f to %f", double_, -G_MAXDOUBLE, G_MAXDOUBLE); + return FALSE; + } + + if (double_ < -G_MAXDOUBLE || double_ > G_MAXDOUBLE) { + PyErr_Format (PyExc_ValueError, "%f not in range %f to %f", double_, -G_MAXDOUBLE, G_MAXDOUBLE); + return FALSE; + } + + arg->v_double = double_; + + return TRUE; +} + +gboolean +_pygi_marshal_in_unichar (PyGIInvokeState *state, + PyGICallableCache *callable_cache, + PyGIArgCache *arg_cache, + PyObject *py_arg, + GIArgument *arg) +{ + Py_ssize_t size; + gchar *string_; + + if (PyUnicode_Check (py_arg)) { + PyObject *py_bytes; + + size = PyUnicode_GET_SIZE (py_arg); + py_bytes = PyUnicode_AsUTF8String (py_arg); + string_ = strdup(PYGLIB_PyBytes_AsString (py_bytes)); + Py_DECREF (py_bytes); + +#if PY_VERSION_HEX < 0x03000000 + } else if (PyString_Check (py_arg)) { + PyObject *pyuni = PyUnicode_FromEncodedObject (py_arg, "UTF-8", "strict"); + if (!pyuni) + return FALSE; + + size = PyUnicode_GET_SIZE (pyuni); + string_ = g_strdup (PyString_AsString(py_arg)); + Py_DECREF (pyuni); +#endif + } else { + PyErr_Format (PyExc_TypeError, "Must be string, not %s", + py_arg->ob_type->tp_name); + return FALSE; + } + + if (size != 1) { + PyErr_Format (PyExc_TypeError, "Must be a one character string, not %ld characters", + size); + g_free (string_); + return FALSE; + } + + arg->v_uint32 = g_utf8_get_char (string_); + g_free (string_); + + return TRUE; +} +gboolean +_pygi_marshal_in_gtype (PyGIInvokeState *state, + PyGICallableCache *callable_cache, + PyGIArgCache *arg_cache, + PyObject *py_arg, + GIArgument *arg) +{ + long type_ = pyg_type_from_object (py_arg); + + if (type_ == 0) { + PyErr_Format (PyExc_TypeError, "Must be gobject.GType, not %s", + py_arg->ob_type->tp_name); + return FALSE; + } + + arg->v_long = type_; + return TRUE; +} +gboolean +_pygi_marshal_in_utf8 (PyGIInvokeState *state, + PyGICallableCache *callable_cache, + PyGIArgCache *arg_cache, + PyObject *py_arg, + GIArgument *arg) +{ + gchar *string_; + + if (py_arg == Py_None) { + arg->v_pointer = NULL; + return TRUE; + } + + if (PyUnicode_Check (py_arg)) { + PyObject *pystr_obj = PyUnicode_AsUTF8String (py_arg); + if (!pystr_obj) + return FALSE; + + string_ = g_strdup (PYGLIB_PyBytes_AsString (pystr_obj)); + Py_DECREF (pystr_obj); + } +#if PY_VERSION_HEX < 0x03000000 + else if (PyString_Check (py_arg)) { + string_ = g_strdup (PyString_AsString (py_arg)); + } +#endif + else { + PyErr_Format (PyExc_TypeError, "Must be string, not %s", + py_arg->ob_type->tp_name); + return FALSE; + } + + arg->v_string = string_; + return TRUE; +} + +gboolean +_pygi_marshal_in_filename (PyGIInvokeState *state, + PyGICallableCache *callable_cache, + PyGIArgCache *arg_cache, + PyObject *py_arg, + GIArgument *arg) +{ + gchar *string_; + GError *error = NULL; + + if (PyUnicode_Check (py_arg)) { + PyObject *pystr_obj = PyUnicode_AsUTF8String (py_arg); + if (!pystr_obj) + return FALSE; + + string_ = g_strdup (PYGLIB_PyBytes_AsString (pystr_obj)); + Py_DECREF (pystr_obj); + } +#if PY_VERSION_HEX < 0x03000000 + else if (PyString_Check (py_arg)) { + string_ = g_strdup (PyString_AsString (py_arg)); + } +#endif + else { + PyErr_Format (PyExc_TypeError, "Must be string, not %s", + py_arg->ob_type->tp_name); + return FALSE; + } + + arg->v_string = g_filename_from_utf8 (string_, -1, NULL, NULL, &error); + g_free (string_); + + if (arg->v_string == NULL) { + PyErr_SetString (PyExc_Exception, error->message); + g_error_free (error); + /* TODO: Convert the error to an exception. */ + return FALSE; + } + + return TRUE; +} + +gboolean +_pygi_marshal_in_array (PyGIInvokeState *state, + PyGICallableCache *callable_cache, + PyGIArgCache *arg_cache, + PyObject *py_arg, + GIArgument *arg) +{ + PyGIMarshalInFunc in_marshaller; + int i; + Py_ssize_t length; + gboolean is_ptr_array; + GArray *array_ = NULL; + PyGISequenceCache *sequence_cache = (PyGISequenceCache *)arg_cache; + + + if (py_arg == Py_None) { + arg->v_pointer = NULL; + return TRUE; + } + + if (!PySequence_Check (py_arg)) { + PyErr_Format (PyExc_TypeError, "Must be sequence, not %s", + py_arg->ob_type->tp_name); + return FALSE; + } + + length = PySequence_Length (py_arg); + if (length < 0) + return FALSE; + + if (sequence_cache->fixed_size >= 0 && + sequence_cache->fixed_size != length) { + PyErr_Format (PyExc_ValueError, "Must contain %zd items, not %zd", + sequence_cache->fixed_size, length); + + return FALSE; + } + + is_ptr_array = (sequence_cache->array_type == GI_ARRAY_TYPE_PTR_ARRAY); + if (is_ptr_array) { + array_ = (GArray *)g_ptr_array_new (); + } else { + array_ = g_array_sized_new (sequence_cache->is_zero_terminated, + FALSE, + sequence_cache->item_size, + length); + } + + if (array_ == NULL) { + PyErr_NoMemory (); + return FALSE; + } + + if (sequence_cache->item_cache->type_tag == GI_TYPE_TAG_UINT8 && + PYGLIB_PyBytes_Check (py_arg)) { + memcpy(array_->data, PYGLIB_PyBytes_AsString (py_arg), length); + + goto array_success; + } + + in_marshaller = sequence_cache->item_cache->in_marshaller; + for (i = 0; i < length; i++) { + GIArgument item; + PyObject *py_item = PySequence_GetItem (py_arg, i); + if (py_item == NULL) + goto err; + + if (!in_marshaller ( state, + callable_cache, + sequence_cache->item_cache, + py_item, + &item)) + goto err; + + /* FIXME: it is much more efficent to have seperate marshaller + * for ptr arrays than doing the evaluation + * and casting each loop iteration + */ + if (is_ptr_array) + g_ptr_array_add((GPtrArray *)array_, item.v_pointer); + else + g_array_insert_val (array_, i, item); + continue; +err: + if (sequence_cache->item_cache->in_cleanup != NULL) { + gsize j; + PyGIMarshalCleanupFunc cleanup_func = + sequence_cache->item_cache->in_cleanup; + + for(j = 0; j < i; j++) { + cleanup_func (state, + sequence_cache->item_cache, + g_array_index (array_, gpointer, j), + TRUE); + } + } + + if (is_ptr_array) + g_ptr_array_free (array_, TRUE); + else + g_array_free (array_, TRUE); + _PyGI_ERROR_PREFIX ("Item %i: ", i); + return FALSE; + } + +array_success: + if (sequence_cache->len_arg_index >= 0) { + /* we have an child arg to handle */ + PyGIArgCache *child_cache = + callable_cache->args_cache[sequence_cache->len_arg_index]; + + if (child_cache->direction == GI_DIRECTION_INOUT) { + gint *len_arg = (gint *)state->in_args[child_cache->c_arg_index].v_pointer; + /* if we are not setup yet just set the in arg */ + if (len_arg == NULL) + state->in_args[child_cache->c_arg_index].v_long = length; + else + *len_arg = length; + } else { + state->in_args[child_cache->c_arg_index].v_long = length; + } + } + + if (sequence_cache->array_type == GI_ARRAY_TYPE_C) { + arg->v_pointer = array_->data; + g_array_free (array_, FALSE); + } else { + arg->v_pointer = array_; + } + + return TRUE; +} + +gboolean +_pygi_marshal_in_glist (PyGIInvokeState *state, + PyGICallableCache *callable_cache, + PyGIArgCache *arg_cache, + PyObject *py_arg, + GIArgument *arg) +{ + PyGIMarshalInFunc in_marshaller; + int i; + Py_ssize_t length; + GList *list_ = NULL; + PyGISequenceCache *sequence_cache = (PyGISequenceCache *)arg_cache; + + + if (py_arg == Py_None) { + arg->v_pointer = NULL; + return TRUE; + } + + if (!PySequence_Check (py_arg)) { + PyErr_Format (PyExc_TypeError, "Must be sequence, not %s", + py_arg->ob_type->tp_name); + return FALSE; + } + + length = PySequence_Length (py_arg); + if (length < 0) + return FALSE; + + if (sequence_cache->fixed_size >= 0 && + sequence_cache->fixed_size != length) { + PyErr_Format (PyExc_ValueError, "Must contain %zd items, not %zd", + sequence_cache->fixed_size, length); + + return FALSE; + } + + in_marshaller = sequence_cache->item_cache->in_marshaller; + for (i = 0; i < length; i++) { + GIArgument item; + PyObject *py_item = PySequence_GetItem (py_arg, i); + if (py_item == NULL) + goto err; + + if (!in_marshaller ( state, + callable_cache, + sequence_cache->item_cache, + py_item, + &item)) + goto err; + + list_ = g_list_append (list_, item.v_pointer); + continue; +err: + if (sequence_cache->item_cache->in_cleanup != NULL) { + GDestroyNotify cleanup = sequence_cache->item_cache->in_cleanup; + } + + g_list_free (list_); + _PyGI_ERROR_PREFIX ("Item %i: ", i); + return FALSE; + } + + arg->v_pointer = list_; + return TRUE; +} + +gboolean +_pygi_marshal_in_gslist (PyGIInvokeState *state, + PyGICallableCache *callable_cache, + PyGIArgCache *arg_cache, + PyObject *py_arg, + GIArgument *arg) +{ + PyGIMarshalInFunc in_marshaller; + int i; + Py_ssize_t length; + GSList *list_ = NULL; + PyGISequenceCache *sequence_cache = (PyGISequenceCache *)arg_cache; + + if (py_arg == Py_None) { + arg->v_pointer = NULL; + return TRUE; + } + + if (!PySequence_Check (py_arg)) { + PyErr_Format (PyExc_TypeError, "Must be sequence, not %s", + py_arg->ob_type->tp_name); + return FALSE; + } + + length = PySequence_Length (py_arg); + if (length < 0) + return FALSE; + + if (sequence_cache->fixed_size >= 0 && + sequence_cache->fixed_size != length) { + PyErr_Format (PyExc_ValueError, "Must contain %zd items, not %zd", + sequence_cache->fixed_size, length); + + return FALSE; + } + + in_marshaller = sequence_cache->item_cache->in_marshaller; + for (i = 0; i < length; i++) { + GIArgument item; + PyObject *py_item = PySequence_GetItem (py_arg, i); + if (py_item == NULL) + goto err; + + if (!in_marshaller ( state, + callable_cache, + sequence_cache->item_cache, + py_item, + &item)) + goto err; + + list_ = g_slist_append (list_, item.v_pointer); + continue; +err: + if (sequence_cache->item_cache->in_cleanup != NULL) { + GDestroyNotify cleanup = sequence_cache->item_cache->in_cleanup; + } + + g_slist_free (list_); + _PyGI_ERROR_PREFIX ("Item %i: ", i); + return FALSE; + } + + arg->v_pointer = list_; + return TRUE; +} + +gboolean +_pygi_marshal_in_ghash (PyGIInvokeState *state, + PyGICallableCache *callable_cache, + PyGIArgCache *arg_cache, + PyObject *py_arg, + GIArgument *arg) +{ + PyGIMarshalInFunc key_in_marshaller; + PyGIMarshalInFunc value_in_marshaller; + + int i; + Py_ssize_t length; + PyObject *py_keys, *py_values; + + GHashFunc hash_func; + GEqualFunc equal_func; + + GHashTable *hash_ = NULL; + PyGIHashCache *hash_cache = (PyGIHashCache *)arg_cache; + + if (py_arg == Py_None) { + arg->v_pointer = NULL; + return TRUE; + } + + py_keys = PyMapping_Keys (py_arg); + if (py_keys == NULL) { + PyErr_Format (PyExc_TypeError, "Must be mapping, not %s", + py_arg->ob_type->tp_name); + return FALSE; + } + + length = PyMapping_Length (py_arg); + if (length < 0) { + Py_DECREF (py_keys); + return FALSE; + } + + py_values = PyMapping_Values (py_arg); + if (py_values == NULL) { + Py_DECREF (py_keys); + return FALSE; + } + + key_in_marshaller = hash_cache->key_cache->in_marshaller; + value_in_marshaller = hash_cache->value_cache->in_marshaller; + + switch (hash_cache->key_cache->type_tag) { + case GI_TYPE_TAG_UTF8: + case GI_TYPE_TAG_FILENAME: + hash_func = g_str_hash; + equal_func = g_str_equal; + break; + default: + hash_func = NULL; + equal_func = NULL; + } + + hash_ = g_hash_table_new (hash_func, equal_func); + if (hash_ == NULL) { + PyErr_NoMemory (); + Py_DECREF (py_keys); + Py_DECREF (py_values); + return FALSE; + } + + for (i = 0; i < length; i++) { + GIArgument key, value; + PyObject *py_key = PyList_GET_ITEM (py_keys, i); + PyObject *py_value = PyList_GET_ITEM (py_values, i); + if (py_key == NULL || py_value == NULL) + goto err; + + if (!key_in_marshaller ( state, + callable_cache, + hash_cache->key_cache, + py_key, + &key)) + goto err; + + if (!value_in_marshaller ( state, + callable_cache, + hash_cache->value_cache, + py_value, + &value)) + goto err; + + g_hash_table_insert (hash_, key.v_pointer, value.v_pointer); + continue; +err: + /* FIXME: cleanup hash keys and values */ + Py_XDECREF (py_key); + Py_XDECREF (py_value); + Py_DECREF (py_keys); + Py_DECREF (py_values); + g_hash_table_unref (hash_); + _PyGI_ERROR_PREFIX ("Item %i: ", i); + return FALSE; + } + + arg->v_pointer = hash_; + return TRUE; +} + +gboolean +_pygi_marshal_in_gerror (PyGIInvokeState *state, + PyGICallableCache *callable_cache, + PyGIArgCache *arg_cache, + PyObject *py_arg, + GIArgument *arg) +{ + PyErr_Format (PyExc_NotImplementedError, + "Marshalling for GErrors is not implemented"); + return FALSE; +} + +gboolean +_pygi_marshal_in_interface_callback (PyGIInvokeState *state, + PyGICallableCache *callable_cache, + PyGIArgCache *arg_cache, + PyObject *py_arg, + GIArgument *arg) +{ + 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 = callable_cache->args_cache[callback_cache->user_data_index]; + if (user_data_cache->py_arg_index < state->n_py_in_args) { + py_user_data = PyTuple_GetItem (state->py_in_args, user_data_cache->py_arg_index); + if (!py_user_data) + return FALSE; + } else { + py_user_data = Py_None; + Py_INCREF (Py_None); + } + } + + if (py_arg == Py_None && !(py_user_data == Py_None || py_user_data == NULL)) { + Py_DECREF (py_user_data); + PyErr_Format (PyExc_TypeError, + "When passing None for a callback userdata must also be None"); + + return FALSE; + } + + if (py_arg == Py_None) { + Py_XDECREF (py_user_data); + return TRUE; + } + + if (!PyCallable_Check (py_arg)) { + Py_XDECREF (py_user_data); + PyErr_Format (PyExc_TypeError, + "Callback needs to be a function or method not %s", + py_arg->ob_type->tp_name); + + return FALSE; + } + + if (callback_cache->destroy_notify_index > 0) + destroy_cache = callable_cache->args_cache[callback_cache->destroy_notify_index]; + + 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; + if (user_data_cache != NULL) { + state->in_args[user_data_cache->c_arg_index].v_pointer = closure; + } + + if (destroy_cache) { + PyGICClosure *destroy_notify = _pygi_destroy_notify_create (); + state->in_args[destroy_cache->c_arg_index].v_pointer = destroy_notify->closure; + } + + return TRUE; +} + +gboolean +_pygi_marshal_in_interface_enum (PyGIInvokeState *state, + PyGICallableCache *callable_cache, + PyGIArgCache *arg_cache, + PyObject *py_arg, + GIArgument *arg) +{ + PyObject *int_; + gint is_instance; + PyGIInterfaceCache *iface_cache = (PyGIInterfaceCache *)arg_cache; + + is_instance = PyObject_IsInstance (py_arg, iface_cache->py_type); + + int_ = PYGLIB_PyNumber_Long (py_arg); + if (int_ == NULL) { + PyErr_Clear(); + goto err; + } + + arg->v_long = PYGLIB_PyLong_AsLong (int_); + Py_DECREF (int_); + + /* If this is not an instance of the Enum type that we want + * we need to check if the value is equivilant to one of the + * Enum's memebers */ + if (!is_instance) { + int i; + gboolean is_found = FALSE; + + for (i = 0; i < g_enum_info_get_n_values (iface_cache->interface_info); i++) { + GIValueInfo *value_info = + g_enum_info_get_value (iface_cache->interface_info, i); + glong enum_value = g_value_info_get_value (value_info); + g_base_info_unref ( (GIBaseInfo *)value_info); + if (arg->v_long == enum_value) { + is_found = TRUE; + break; + } + } + + if (!is_found) + goto err; + } + + return TRUE; + +err: + PyErr_Format (PyExc_TypeError, "Expected a %s, but got %s", + iface_cache->type_name, py_arg->ob_type->tp_name); + return FALSE; +} + +gboolean +_pygi_marshal_in_interface_flags (PyGIInvokeState *state, + PyGICallableCache *callable_cache, + PyGIArgCache *arg_cache, + PyObject *py_arg, + GIArgument *arg) +{ + PyObject *int_; + gint is_instance; + PyGIInterfaceCache *iface_cache = (PyGIInterfaceCache *)arg_cache; + + is_instance = PyObject_IsInstance (py_arg, iface_cache->py_type); + + int_ = PYGLIB_PyNumber_Long (py_arg); + if (int_ == NULL) { + PyErr_Clear (); + goto err; + } + + arg->v_long = PYGLIB_PyLong_AsLong (int_); + Py_DECREF (int_); + + /* only 0 or argument of type Flag is allowed */ + if (!is_instance && arg->v_long != 0) + goto err; + + return TRUE; + +err: + PyErr_Format (PyExc_TypeError, "Expected a %s, but got %s", + iface_cache->type_name, py_arg->ob_type->tp_name); + return FALSE; + +} + +gboolean +_pygi_marshal_in_interface_struct (PyGIInvokeState *state, + PyGICallableCache *callable_cache, + PyGIArgCache *arg_cache, + PyObject *py_arg, + GIArgument *arg) +{ + PyGIInterfaceCache *iface_cache = (PyGIInterfaceCache *)arg_cache; + + if (py_arg == Py_None) { + arg->v_pointer = NULL; + return TRUE; + } + + /* FIXME: handle this large if statement in the cache + * and set the correct marshaller + */ + + if (iface_cache->g_type == G_TYPE_CLOSURE) { + GClosure *closure; + GType object_gtype = pyg_type_from_object_strict (py_arg, FALSE); + + if ( !(PyCallable_Check(py_arg) || + g_type_is_a (object_gtype, G_TYPE_CLOSURE))) { + PyErr_Format (PyExc_TypeError, "Must be callable, not %s", + py_arg->ob_type->tp_name); + return FALSE; + } + + if (g_type_is_a (object_gtype, G_TYPE_CLOSURE)) + closure = (GClosure *)pyg_boxed_get (py_arg, void); + else + closure = pyg_closure_new (py_arg, NULL, NULL); + + if (closure == NULL) { + PyErr_SetString (PyExc_RuntimeError, "PyObject conversion to GClosure failed"); + return FALSE; + } + + arg->v_pointer = closure; + return TRUE; + } else if (iface_cache->g_type == G_TYPE_VALUE) { + GValue *value; + GType object_type; + + object_type = pyg_type_from_object_strict ( (PyObject *) py_arg->ob_type, FALSE); + if (object_type == G_TYPE_INVALID) { + PyErr_SetString (PyExc_RuntimeError, "unable to retrieve object's GType"); + return FALSE; + } + + /* if already a gvalue, use that, else marshal into gvalue */ + if (object_type == G_TYPE_VALUE) { + value = (GValue *)( (PyGObject *)py_arg)->obj; + } else { + value = g_slice_new0 (GValue); + g_value_init (value, object_type); + if (pyg_value_from_pyobject (value, py_arg) < 0) { + g_slice_free (GValue, value); + PyErr_SetString (PyExc_RuntimeError, "PyObject conversion to GValue failed"); + return FALSE; + } + } + + arg->v_pointer = value; + return TRUE; + } else if (iface_cache->is_foreign) { + gboolean success; + success = pygi_struct_foreign_convert_to_g_argument (py_arg, + iface_cache->interface_info, + arg_cache->transfer, + arg); + + return success; + } else if (!PyObject_IsInstance (py_arg, iface_cache->py_type)) { + PyErr_Format (PyExc_TypeError, "Expected %s, but got %s", + iface_cache->type_name, + iface_cache->py_type->ob_type->tp_name); + return FALSE; + } + + if (g_type_is_a (iface_cache->g_type, G_TYPE_BOXED)) { + arg->v_pointer = pyg_boxed_get (py_arg, void); + if (arg_cache->transfer == GI_TRANSFER_EVERYTHING) { + arg->v_pointer = g_boxed_copy (iface_cache->g_type, arg->v_pointer); + } + } else if (g_type_is_a (iface_cache->g_type, G_TYPE_POINTER) || + g_type_is_a (iface_cache->g_type, G_TYPE_VARIANT) || + iface_cache->g_type == G_TYPE_NONE) { + arg->v_pointer = pyg_pointer_get (py_arg, void); + } else { + PyErr_Format (PyExc_NotImplementedError, + "structure type '%s' is not supported yet", + g_type_name(iface_cache->g_type)); + return FALSE; + } + return TRUE; +} + +gboolean +_pygi_marshal_in_interface_boxed (PyGIInvokeState *state, + PyGICallableCache *callable_cache, + PyGIArgCache *arg_cache, + PyObject *py_arg, + GIArgument *arg) +{ + PyErr_Format (PyExc_NotImplementedError, + "Marshalling for this type is not implemented yet"); + return FALSE; +} + +gboolean +_pygi_marshal_in_interface_object (PyGIInvokeState *state, + PyGICallableCache *callable_cache, + PyGIArgCache *arg_cache, + PyObject *py_arg, + GIArgument *arg) +{ + if (py_arg == Py_None) { + arg->v_pointer = NULL; + return TRUE; + } + + if (!PyObject_IsInstance (py_arg, ( (PyGIInterfaceCache *)arg_cache)->py_type)) { + PyErr_Format (PyExc_TypeError, "Expected %s, but got %s", + ( (PyGIInterfaceCache *)arg_cache)->type_name, + ( (PyGIInterfaceCache *)arg_cache)->py_type->ob_type->tp_name); + return FALSE; + } + + arg->v_pointer = pygobject_get(py_arg); + if (arg_cache->transfer == GI_TRANSFER_EVERYTHING) + g_object_ref (arg->v_pointer); + + return TRUE; +} + +gboolean +_pygi_marshal_in_interface_union (PyGIInvokeState *state, + PyGICallableCache *callable_cache, + PyGIArgCache *arg_cache, + PyObject *py_arg, + GIArgument *arg) +{ + PyErr_Format(PyExc_NotImplementedError, + "Marshalling for this type is not implemented yet"); + return FALSE; +} + +gboolean _pygi_marshal_in_interface_instance (PyGIInvokeState *state, + PyGICallableCache *callable_cache, + PyGIArgCache *arg_cache, + PyObject *py_arg, + GIArgument *arg) +{ + GIInfoType info_type; + PyGIInterfaceCache *iface_cache = (PyGIInterfaceCache *)arg_cache; + + /* FIXME: add instance checks */ + + info_type = g_base_info_get_type (iface_cache->interface_info); + switch (info_type) { + case GI_INFO_TYPE_UNION: + case GI_INFO_TYPE_STRUCT: + { + GType type = iface_cache->g_type; + if (g_type_is_a (type, G_TYPE_BOXED)) { + arg->v_pointer = pyg_boxed_get (py_arg, void); + } else if (g_type_is_a (type, G_TYPE_POINTER) || + g_type_is_a (type, G_TYPE_VARIANT) || + type == G_TYPE_NONE) { + arg->v_pointer = pyg_pointer_get (py_arg, void); + } else { + PyErr_Format (PyExc_TypeError, "unable to convert an instance of '%s'", g_type_name (type)); + return FALSE; + } + + break; + } + case GI_INFO_TYPE_OBJECT: + case GI_INFO_TYPE_INTERFACE: + arg->v_pointer = pygobject_get (py_arg); + break; + default: + /* Other types don't have methods. */ + g_assert_not_reached (); + } + + return TRUE; +} diff --git a/gi/pygi-marshal-in.h b/gi/pygi-marshal-in.h new file mode 100644 index 00000000..7d948bc9 --- /dev/null +++ b/gi/pygi-marshal-in.h @@ -0,0 +1,186 @@ +/* -*- Mode: C; c-basic-offset: 4 -*- + * vim: tabstop=4 shiftwidth=4 expandtab + * + * Copyright (C) 2011 John (J5) Palmieri <johnp@redhat.com>, Red Hat, Inc. + * + * 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, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 + * USA + */ + +#ifndef __PYGI_MARSHAL_H__ +#define __PYGI_MARSHAL_H__ + +#include <Python.h> + +#include <girepository.h> + +#include "pygi-private.h" + +G_BEGIN_DECLS + +gboolean _pygi_marshal_in_void (PyGIInvokeState *state, + PyGICallableCache *callable_cache, + PyGIArgCache *arg_cache, + PyObject *py_arg, + GIArgument *arg); +gboolean _pygi_marshal_in_boolean (PyGIInvokeState *state, + PyGICallableCache *callable_cache, + PyGIArgCache *arg_cache, + PyObject *py_arg, + GIArgument *arg); +gboolean _pygi_marshal_in_int8 (PyGIInvokeState *state, + PyGICallableCache *callable_cache, + PyGIArgCache *arg_cache, + PyObject *py_arg, + GIArgument *arg); +gboolean _pygi_marshal_in_uint8 (PyGIInvokeState *state, + PyGICallableCache *callable_cache, + PyGIArgCache *arg_cache, + PyObject *py_arg, + GIArgument *arg); +gboolean _pygi_marshal_in_int16 (PyGIInvokeState *state, + PyGICallableCache *callable_cache, + PyGIArgCache *arg_cache, + PyObject *py_arg, + GIArgument *arg); +gboolean _pygi_marshal_in_uint16 (PyGIInvokeState *state, + PyGICallableCache *callable_cache, + PyGIArgCache *arg_cache, + PyObject *py_arg, + GIArgument *arg); +gboolean _pygi_marshal_in_int32 (PyGIInvokeState *state, + PyGICallableCache *callable_cache, + PyGIArgCache *arg_cache, + PyObject *py_arg, + GIArgument *arg); +gboolean _pygi_marshal_in_uint32 (PyGIInvokeState *state, + PyGICallableCache *callable_cache, + PyGIArgCache *arg_cache, + PyObject *py_arg, + GIArgument *arg); +gboolean _pygi_marshal_in_int64 (PyGIInvokeState *state, + PyGICallableCache *callable_cache, + PyGIArgCache *arg_cache, + PyObject *py_arg, + GIArgument *arg); +gboolean _pygi_marshal_in_uint64 (PyGIInvokeState *state, + PyGICallableCache *callable_cache, + PyGIArgCache *arg_cache, + PyObject *py_arg, + GIArgument *arg); +gboolean _pygi_marshal_in_float (PyGIInvokeState *state, + PyGICallableCache *callable_cache, + PyGIArgCache *arg_cache, + PyObject *py_arg, + GIArgument *arg); +gboolean _pygi_marshal_in_double (PyGIInvokeState *state, + PyGICallableCache *callable_cache, + PyGIArgCache *arg_cache, + PyObject *py_arg, + GIArgument *arg); +gboolean _pygi_marshal_in_unichar (PyGIInvokeState *state, + PyGICallableCache *callable_cache, + PyGIArgCache *arg_cache, + PyObject *py_arg, + GIArgument *arg); +gboolean _pygi_marshal_in_gtype (PyGIInvokeState *state, + PyGICallableCache *callable_cache, + PyGIArgCache *arg_cache, + PyObject *py_arg, + GIArgument *arg); +gboolean _pygi_marshal_in_utf8 (PyGIInvokeState *state, + PyGICallableCache *callable_cache, + PyGIArgCache *arg_cache, + PyObject *py_arg, + GIArgument *arg); +gboolean _pygi_marshal_in_filename (PyGIInvokeState *state, + PyGICallableCache *callable_cache, + PyGIArgCache *arg_cache, + PyObject *py_arg, + GIArgument *arg); +gboolean _pygi_marshal_in_array (PyGIInvokeState *state, + PyGICallableCache *callable_cache, + PyGIArgCache *arg_cache, + PyObject *py_arg, + GIArgument *arg); +gboolean _pygi_marshal_in_glist (PyGIInvokeState *state, + PyGICallableCache *callable_cache, + PyGIArgCache *arg_cache, + PyObject *py_arg, + GIArgument *arg); +gboolean _pygi_marshal_in_gslist (PyGIInvokeState *state, + PyGICallableCache *callable_cache, + PyGIArgCache *arg_cache, + PyObject *py_arg, + GIArgument *arg); +gboolean _pygi_marshal_in_ghash (PyGIInvokeState *state, + PyGICallableCache *callable_cache, + PyGIArgCache *arg_cache, + PyObject *py_arg, + GIArgument *arg); +gboolean _pygi_marshal_in_gerror (PyGIInvokeState *state, + PyGICallableCache *callable_cache, + PyGIArgCache *arg_cache, + PyObject *py_arg, + GIArgument *arg); +gboolean _pygi_marshal_in_interface_callback (PyGIInvokeState *state, + PyGICallableCache *callable_cache, + PyGIArgCache *arg_cache, + PyObject *py_arg, + GIArgument *arg); +gboolean _pygi_marshal_in_interface_enum (PyGIInvokeState *state, + PyGICallableCache *callable_cache, + PyGIArgCache *arg_cache, + PyObject *py_arg, + GIArgument *arg); +gboolean _pygi_marshal_in_interface_flags (PyGIInvokeState *state, + PyGICallableCache *callable_cache, + PyGIArgCache *arg_cache, + PyObject *py_arg, + GIArgument *arg); +gboolean _pygi_marshal_in_interface_struct (PyGIInvokeState *state, + PyGICallableCache *callable_cache, + PyGIArgCache *arg_cache, + PyObject *py_arg, + GIArgument *arg); +gboolean _pygi_marshal_in_interface_interface(PyGIInvokeState *state, + PyGICallableCache *callable_cache, + PyGIArgCache *arg_cache, + PyObject *py_arg, + GIArgument *arg); +gboolean _pygi_marshal_in_interface_boxed (PyGIInvokeState *state, + PyGICallableCache *callable_cache, + PyGIArgCache *arg_cache, + PyObject *py_arg, + GIArgument *arg); +gboolean _pygi_marshal_in_interface_object (PyGIInvokeState *state, + PyGICallableCache *callable_cache, + PyGIArgCache *arg_cache, + PyObject *py_arg, + GIArgument *arg); +gboolean _pygi_marshal_in_interface_union (PyGIInvokeState *state, + PyGICallableCache *callable_cache, + PyGIArgCache *arg_cache, + PyObject *py_arg, + GIArgument *arg); +gboolean _pygi_marshal_in_interface_instance (PyGIInvokeState *state, + PyGICallableCache *callable_cache, + PyGIArgCache *arg_cache, + PyObject *py_arg, + GIArgument *arg); + +G_END_DECLS + +#endif /* __PYGI_MARSHAL_H__ */ diff --git a/gi/pygi-marshal-out.c b/gi/pygi-marshal-out.c new file mode 100644 index 00000000..f99ee256 --- /dev/null +++ b/gi/pygi-marshal-out.c @@ -0,0 +1,767 @@ +/* -*- Mode: C; c-basic-offset: 4 -*- + * vim: tabstop=4 shiftwidth=4 expandtab + * + * Copyright (C) 2011 John (J5) Palmieri <johnp@redhat.com>, Red Hat, Inc. + * + * pygi-marshal-out.c: PyObject conversion functions for out parameters. + * + * 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, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 + * USA + */ + +#include "pygi-private.h" + +#include <string.h> +#include <time.h> + +#include <datetime.h> +#include <pygobject.h> +#include <pyglib-python-compat.h> + +#include "pygi-cache.h" +#include "pygi-marshal-cleanup.h" +#include "pygi-marshal-out.h" + +PyObject * +_pygi_marshal_out_void (PyGIInvokeState *state, + PyGICallableCache *callable_cache, + PyGIArgCache *arg_cache, + GIArgument *arg) +{ + PyObject *py_obj = NULL; + if (arg_cache->is_pointer) + py_obj = arg->v_pointer; + else + py_obj = Py_None; + + Py_XINCREF (py_obj); + return py_obj; +} + +PyObject * +_pygi_marshal_out_boolean (PyGIInvokeState *state, + PyGICallableCache *callable_cache, + PyGIArgCache *arg_cache, + GIArgument *arg) +{ + PyObject *py_obj = PyBool_FromLong (arg->v_boolean); + return py_obj; +} + +PyObject * +_pygi_marshal_out_int8 (PyGIInvokeState *state, + PyGICallableCache *callable_cache, + PyGIArgCache *arg_cache, + GIArgument *arg) +{ + PyObject *py_obj = PYGLIB_PyLong_FromLong (arg->v_int8); + return py_obj; +} + +PyObject * +_pygi_marshal_out_uint8 (PyGIInvokeState *state, + PyGICallableCache *callable_cache, + PyGIArgCache *arg_cache, + GIArgument *arg) +{ + PyObject *py_obj = PYGLIB_PyLong_FromLong (arg->v_uint8); + + return py_obj; +} + +PyObject * +_pygi_marshal_out_int16 (PyGIInvokeState *state, + PyGICallableCache *callable_cache, + PyGIArgCache *arg_cache, + GIArgument *arg) +{ + PyObject *py_obj = PYGLIB_PyLong_FromLong (arg->v_int16); + + return py_obj; +} + +PyObject * +_pygi_marshal_out_uint16 (PyGIInvokeState *state, + PyGICallableCache *callable_cache, + PyGIArgCache *arg_cache, + GIArgument *arg) +{ + PyObject *py_obj = PYGLIB_PyLong_FromLong (arg->v_uint16); + + return py_obj; +} + +PyObject * +_pygi_marshal_out_int32 (PyGIInvokeState *state, + PyGICallableCache *callable_cache, + PyGIArgCache *arg_cache, + GIArgument *arg) +{ + PyObject *py_obj = PYGLIB_PyLong_FromLong (arg->v_int32); + + return py_obj; +} + +PyObject * +_pygi_marshal_out_uint32 (PyGIInvokeState *state, + PyGICallableCache *callable_cache, + PyGIArgCache *arg_cache, + GIArgument *arg) +{ + PyObject *py_obj = PyLong_FromLongLong (arg->v_uint32); + + return py_obj; +} + +PyObject * +_pygi_marshal_out_int64 (PyGIInvokeState *state, + PyGICallableCache *callable_cache, + PyGIArgCache *arg_cache, + GIArgument *arg) +{ + PyObject *py_obj = PyLong_FromLongLong (arg->v_int64); + + return py_obj; +} + +PyObject * +_pygi_marshal_out_uint64 (PyGIInvokeState *state, + PyGICallableCache *callable_cache, + PyGIArgCache *arg_cache, + GIArgument *arg) +{ + PyObject *py_obj = PyLong_FromUnsignedLongLong (arg->v_uint64); + + return py_obj; +} + +PyObject * +_pygi_marshal_out_float (PyGIInvokeState *state, + PyGICallableCache *callable_cache, + PyGIArgCache *arg_cache, + GIArgument *arg) +{ + PyObject *py_obj = PyFloat_FromDouble (arg->v_float); + + return py_obj; +} + +PyObject * +_pygi_marshal_out_double (PyGIInvokeState *state, + PyGICallableCache *callable_cache, + PyGIArgCache *arg_cache, + GIArgument *arg) +{ + PyObject *py_obj = PyFloat_FromDouble (arg->v_double); + + return py_obj; +} + +PyObject * +_pygi_marshal_out_unichar (PyGIInvokeState *state, + PyGICallableCache *callable_cache, + PyGIArgCache *arg_cache, + GIArgument *arg) +{ + PyObject *py_obj = NULL; + + /* Preserve the bidirectional mapping between 0 and "" */ + if (arg->v_uint32 == 0) { + py_obj = PYGLIB_PyUnicode_FromString (""); + } else if (g_unichar_validate (arg->v_uint32)) { + gchar utf8[6]; + gint bytes; + + bytes = g_unichar_to_utf8 (arg->v_uint32, utf8); + py_obj = PYGLIB_PyUnicode_FromStringAndSize ((char*)utf8, bytes); + } else { + /* TODO: Convert the error to an exception. */ + PyErr_Format (PyExc_TypeError, + "Invalid unicode codepoint %" G_GUINT32_FORMAT, + arg->v_uint32); + } + + return py_obj; +} + +PyObject * +_pygi_marshal_out_gtype (PyGIInvokeState *state, + PyGICallableCache *callable_cache, + PyGIArgCache *arg_cache, + GIArgument *arg) +{ + PyObject *py_obj = NULL; + + py_obj = pyg_type_wrapper_new ( (GType)arg->v_long); + return py_obj; +} + +PyObject * +_pygi_marshal_out_utf8 (PyGIInvokeState *state, + PyGICallableCache *callable_cache, + PyGIArgCache *arg_cache, + GIArgument *arg) +{ + PyObject *py_obj = NULL; + if (arg->v_string == NULL) { + py_obj = Py_None; + Py_INCREF (py_obj); + return py_obj; + } + + py_obj = PYGLIB_PyUnicode_FromString (arg->v_string); + return py_obj; +} + +PyObject * +_pygi_marshal_out_filename (PyGIInvokeState *state, + PyGICallableCache *callable_cache, + PyGIArgCache *arg_cache, + GIArgument *arg) +{ + gchar *string; + PyObject *py_obj = NULL; + GError *error = NULL; + + if (arg->v_string == NULL) { + py_obj = Py_None; + Py_INCREF (py_obj); + return py_obj; + } + + string = g_filename_to_utf8 (arg->v_string, -1, NULL, NULL, &error); + if (string == NULL) { + PyErr_SetString (PyExc_Exception, error->message); + /* TODO: Convert the error to an exception. */ + return NULL; + } + + py_obj = PYGLIB_PyUnicode_FromString (string); + g_free (string); + + return py_obj; +} + +PyObject * +_pygi_marshal_out_array (PyGIInvokeState *state, + PyGICallableCache *callable_cache, + PyGIArgCache *arg_cache, + GIArgument *arg) +{ + GArray *array_; + PyObject *py_obj = NULL; + PyGISequenceCache *seq_cache = (PyGISequenceCache *)arg_cache; + gsize processed_items = 0; + + array_ = arg->v_pointer; + + /* GArrays make it easier to iterate over arrays + * with different element sizes but requires that + * we allocate a GArray if the argument was a C array + */ + if (seq_cache->array_type == GI_ARRAY_TYPE_C) { + gsize len; + if (seq_cache->fixed_size >= 0) { + len = seq_cache->fixed_size; + } else if (seq_cache->is_zero_terminated) { + len = g_strv_length (arg->v_string); + } else { + GIArgument *len_arg = state->args[seq_cache->len_arg_index]; + len = len_arg->v_long; + } + + array_ = g_array_new (FALSE, + FALSE, + seq_cache->item_size); + if (array_ == NULL) { + PyErr_NoMemory (); + + if (arg_cache->transfer == GI_TRANSFER_EVERYTHING) + g_free (arg->v_pointer); + + return NULL; + } + + array_->data = arg->v_pointer; + array_->len = len; + } + + if (seq_cache->item_cache->type_tag == GI_TYPE_TAG_UINT8) { + if (arg->v_pointer == NULL) { + py_obj = PYGLIB_PyBytes_FromString (""); + } else { + py_obj = PYGLIB_PyBytes_FromStringAndSize (array_->data, array_->len); + } + } else { + if (arg->v_pointer == NULL) { + py_obj = PyList_New (0); + } else { + int i; + + gsize item_size; + PyGIMarshalOutFunc item_out_marshaller; + PyGIArgCache *item_arg_cache; + + py_obj = PyList_New (array_->len); + if (py_obj == NULL) + goto err; + + + item_arg_cache = seq_cache->item_cache; + item_out_marshaller = item_arg_cache->out_marshaller; + + item_size = g_array_get_element_size (array_); + + for (i = 0; i < array_->len; i++) { + GIArgument item_arg; + PyObject *py_item; + + if (seq_cache->array_type == GI_ARRAY_TYPE_PTR_ARRAY) { + item_arg.v_pointer = g_ptr_array_index ( ( GPtrArray *)array_, i); + } else if (item_arg_cache->type_tag == GI_TYPE_TAG_INTERFACE) { + item_arg.v_pointer = array_->data + i * item_size; + if (arg_cache->transfer == GI_TRANSFER_EVERYTHING) { + PyGIInterfaceCache *iface_cache = (PyGIInterfaceCache *) item_arg_cache; + switch (g_base_info_get_type (iface_cache->interface_info)) { + case GI_INFO_TYPE_STRUCT: + { + gpointer *_struct = g_malloc (item_size); + memcpy (_struct, item_arg.v_pointer, item_size); + item_arg.v_pointer = _struct; + break; + } + default: + break; + } + } + } else { + memcpy (&item_arg, array_->data + i * item_size, item_size); + } + + py_item = item_out_marshaller ( state, + callable_cache, + item_arg_cache, + &item_arg); + + if (py_item == NULL) { + Py_CLEAR (py_obj); + + if (seq_cache->array_type == GI_ARRAY_TYPE_C) + g_array_unref (array_); + + goto err; + } + PyList_SET_ITEM (py_obj, i, py_item); + processed_items++; + } + } + } + + if (seq_cache->array_type == GI_ARRAY_TYPE_C) + g_array_free (array_, FALSE); + + return py_obj; + +err: + if (seq_cache->array_type == GI_ARRAY_TYPE_C) { + g_array_free (array_, arg_cache->transfer == GI_TRANSFER_EVERYTHING); + } else { + /* clean up unprocessed items */ + if (seq_cache->item_cache->out_cleanup != NULL) { + int j; + PyGIMarshalCleanupFunc cleanup_func = seq_cache->item_cache->out_cleanup; + for (j = processed_items; j < array_->len; j++) { + cleanup_func (state, + seq_cache->item_cache, + g_array_index (array_, gpointer, j), + FALSE); + } + } + + if (arg_cache->transfer == GI_TRANSFER_EVERYTHING) + g_array_free (array_, TRUE); + } + + return NULL; +} + +PyObject * +_pygi_marshal_out_glist (PyGIInvokeState *state, + PyGICallableCache *callable_cache, + PyGIArgCache *arg_cache, + GIArgument *arg) +{ + GList *list_; + gsize length; + gsize i; + + PyGIMarshalOutFunc item_out_marshaller; + PyGIArgCache *item_arg_cache; + PyGISequenceCache *seq_cache = (PyGISequenceCache *)arg_cache; + + PyObject *py_obj = NULL; + + list_ = arg->v_pointer; + length = g_list_length (list_); + + py_obj = PyList_New (length); + if (py_obj == NULL) + return NULL; + + item_arg_cache = seq_cache->item_cache; + item_out_marshaller = item_arg_cache->out_marshaller; + + for (i = 0; list_ != NULL; list_ = g_list_next (list_), i++) { + GIArgument item_arg; + PyObject *py_item; + + item_arg.v_pointer = list_->data; + py_item = item_out_marshaller ( state, + callable_cache, + item_arg_cache, + &item_arg); + + if (py_item == NULL) { + Py_CLEAR (py_obj); + _PyGI_ERROR_PREFIX ("Item %zu: ", i); + return NULL; + } + + PyList_SET_ITEM (py_obj, i, py_item); + } + + return py_obj; +} + +PyObject * +_pygi_marshal_out_gslist (PyGIInvokeState *state, + PyGICallableCache *callable_cache, + PyGIArgCache *arg_cache, + GIArgument *arg) +{ + GSList *list_; + gsize length; + gsize i; + + PyGIMarshalOutFunc item_out_marshaller; + PyGIArgCache *item_arg_cache; + PyGISequenceCache *seq_cache = (PyGISequenceCache *)arg_cache; + + PyObject *py_obj = NULL; + + list_ = arg->v_pointer; + length = g_slist_length (list_); + + py_obj = PyList_New (length); + if (py_obj == NULL) + return NULL; + + item_arg_cache = seq_cache->item_cache; + item_out_marshaller = item_arg_cache->out_marshaller; + + for (i = 0; list_ != NULL; list_ = g_slist_next (list_), i++) { + GIArgument item_arg; + PyObject *py_item; + + item_arg.v_pointer = list_->data; + py_item = item_out_marshaller ( state, + callable_cache, + item_arg_cache, + &item_arg); + + if (py_item == NULL) { + Py_CLEAR (py_obj); + _PyGI_ERROR_PREFIX ("Item %zu: ", i); + return NULL; + } + + PyList_SET_ITEM (py_obj, i, py_item); + } + + return py_obj; +} + +PyObject * +_pygi_marshal_out_ghash (PyGIInvokeState *state, + PyGICallableCache *callable_cache, + PyGIArgCache *arg_cache, + GIArgument *arg) +{ + GHashTable *hash_; + GHashTableIter hash_table_iter; + + PyGIMarshalOutFunc key_out_marshaller; + PyGIMarshalOutFunc value_out_marshaller; + + PyGIArgCache *key_arg_cache; + PyGIArgCache *value_arg_cache; + PyGIHashCache *hash_cache = (PyGIHashCache *)arg_cache; + + GIArgument key_arg; + GIArgument value_arg; + + PyObject *py_obj = NULL; + + hash_ = arg->v_pointer; + + if (hash_ == NULL) { + py_obj = Py_None; + Py_INCREF (py_obj); + return py_obj; + } + + py_obj = PyDict_New (); + if (py_obj == NULL) + return NULL; + + key_arg_cache = hash_cache->key_cache; + key_out_marshaller = key_arg_cache->out_marshaller; + + value_arg_cache = hash_cache->value_cache; + value_out_marshaller = value_arg_cache->out_marshaller; + + g_hash_table_iter_init (&hash_table_iter, hash_); + while (g_hash_table_iter_next (&hash_table_iter, + &key_arg.v_pointer, + &value_arg.v_pointer)) { + PyObject *py_key; + PyObject *py_value; + int retval; + + py_key = key_out_marshaller ( state, + callable_cache, + key_arg_cache, + &key_arg); + + if (py_key == NULL) { + Py_CLEAR (py_obj); + return NULL; + } + + py_value = value_out_marshaller ( state, + callable_cache, + value_arg_cache, + &value_arg); + + if (py_value == NULL) { + Py_CLEAR (py_obj); + Py_DECREF(py_key); + return NULL; + } + + retval = PyDict_SetItem (py_obj, py_key, py_value); + + Py_DECREF (py_key); + Py_DECREF (py_value); + + if (retval < 0) { + Py_CLEAR (py_obj); + return NULL; + } + } + + return py_obj; +} + +PyObject * +_pygi_marshal_out_gerror (PyGIInvokeState *state, + PyGICallableCache *callable_cache, + PyGIArgCache *arg_cache, + GIArgument *arg) +{ + PyObject *py_obj = NULL; + + PyErr_Format (PyExc_NotImplementedError, + "Marshalling for gerror out is not implemented"); + return py_obj; +} + +PyObject * +_pygi_marshal_out_interface_callback (PyGIInvokeState *state, + PyGICallableCache *callable_cache, + PyGIArgCache *arg_cache, + GIArgument *arg) +{ + PyObject *py_obj = NULL; + + PyErr_Format (PyExc_NotImplementedError, + "Callback out values are not supported"); + return py_obj; +} + +PyObject * +_pygi_marshal_out_interface_enum (PyGIInvokeState *state, + PyGICallableCache *callable_cache, + PyGIArgCache *arg_cache, + GIArgument *arg) +{ + PyObject *py_obj = NULL; + PyGIInterfaceCache *iface_cache = (PyGIInterfaceCache *)arg_cache; + + if (iface_cache->g_type == G_TYPE_NONE) { + py_obj = PyObject_CallFunction (iface_cache->py_type, "l", arg->v_long); + } else { + py_obj = pyg_enum_from_gtype (iface_cache->g_type, arg->v_long); + } + return py_obj; +} + +PyObject * +_pygi_marshal_out_interface_flags (PyGIInvokeState *state, + PyGICallableCache *callable_cache, + PyGIArgCache *arg_cache, + GIArgument *arg) +{ + PyObject *py_obj = NULL; + PyGIInterfaceCache *iface_cache = (PyGIInterfaceCache *)arg_cache; + + if (iface_cache->g_type == G_TYPE_NONE) { + /* An enum with a GType of None is an enum without GType */ + + PyObject *py_type = _pygi_type_import_by_gi_info (iface_cache->interface_info); + PyObject *py_args = NULL; + + if (!py_type) + return NULL; + + py_args = PyTuple_New (1); + if (PyTuple_SetItem (py_args, 0, PyLong_FromLong (arg->v_long)) != 0) { + Py_DECREF (py_args); + Py_DECREF (py_type); + return NULL; + } + + py_obj = PyObject_CallFunction (py_type, "l", arg->v_long); + + Py_DECREF (py_args); + Py_DECREF (py_type); + } else { + py_obj = pyg_flags_from_gtype (iface_cache->g_type, arg->v_long); + } + + return py_obj; +} + +PyObject * +_pygi_marshal_out_interface_struct (PyGIInvokeState *state, + PyGICallableCache *callable_cache, + PyGIArgCache *arg_cache, + GIArgument *arg) +{ + PyObject *py_obj = NULL; + PyGIInterfaceCache *iface_cache = (PyGIInterfaceCache *)arg_cache; + GType type = iface_cache->g_type; + + if (arg->v_pointer == NULL) { + py_obj = Py_None; + Py_INCREF (py_obj); + return py_obj; + } + + if (g_type_is_a (type, G_TYPE_VALUE)) { + py_obj = pyg_value_as_pyobject (arg->v_pointer, FALSE); + } else if (iface_cache->is_foreign) { + py_obj = pygi_struct_foreign_convert_from_g_argument (iface_cache->interface_info, + arg->v_pointer); + } else if (g_type_is_a (type, G_TYPE_BOXED)) { + py_obj = _pygi_boxed_new ( (PyTypeObject *)iface_cache->py_type, arg->v_pointer, + arg_cache->transfer == GI_TRANSFER_EVERYTHING); + } else if (g_type_is_a (type, G_TYPE_POINTER)) { + if (iface_cache->py_type == NULL || + !PyType_IsSubtype ( (PyTypeObject *)iface_cache->py_type, &PyGIStruct_Type)) { + g_warn_if_fail(arg_cache->transfer == GI_TRANSFER_NOTHING); + py_obj = pyg_pointer_new (type, arg->v_pointer); + } else { + py_obj = _pygi_struct_new ( (PyTypeObject *)iface_cache->py_type, arg->v_pointer, + arg_cache->transfer == GI_TRANSFER_EVERYTHING); + } + } else if (g_type_is_a (type, G_TYPE_VARIANT)) { + g_variant_ref_sink (arg->v_pointer); + py_obj = _pygi_struct_new ( (PyTypeObject *)iface_cache->py_type, arg->v_pointer, + FALSE); + } else if (type == G_TYPE_NONE && iface_cache->is_foreign) { + py_obj = pygi_struct_foreign_convert_from_g_argument (iface_cache->interface_info, arg->v_pointer); + } else if (type == G_TYPE_NONE) { + py_obj = _pygi_struct_new ( (PyTypeObject *) iface_cache->py_type, arg->v_pointer, + arg_cache->transfer == GI_TRANSFER_EVERYTHING); + } else { + PyErr_Format (PyExc_NotImplementedError, + "structure type '%s' is not supported yet", + g_type_name (type)); + } + + return py_obj; +} + +PyObject * +_pygi_marshal_out_interface_interface (PyGIInvokeState *state, + PyGICallableCache *callable_cache, + PyGIArgCache *arg_cache, + GIArgument *arg) +{ + PyObject *py_obj = NULL; + + PyErr_Format (PyExc_NotImplementedError, + "Marshalling for this type is not implemented yet"); + return py_obj; +} + +PyObject * +_pygi_marshal_out_interface_boxed (PyGIInvokeState *state, + PyGICallableCache *callable_cache, + PyGIArgCache *arg_cache, + GIArgument *arg) +{ + PyObject *py_obj = NULL; + + PyErr_Format (PyExc_NotImplementedError, + "Marshalling for this type is not implemented yet"); + return py_obj; +} + +PyObject * +_pygi_marshal_out_interface_object (PyGIInvokeState *state, + PyGICallableCache *callable_cache, + PyGIArgCache *arg_cache, + GIArgument *arg) +{ + PyObject *py_obj; + + if (arg->v_pointer == NULL) { + py_obj = Py_None; + Py_INCREF (py_obj); + return py_obj; + } + + py_obj = pygobject_new (arg->v_pointer); + + if (arg_cache->transfer == GI_TRANSFER_EVERYTHING) + g_object_unref (arg->v_pointer); + + return py_obj; +} + +PyObject * +_pygi_marshal_out_interface_union (PyGIInvokeState *state, + PyGICallableCache *callable_cache, + PyGIArgCache *arg_cache, + GIArgument *arg) +{ + PyObject *py_obj = NULL; + + PyErr_Format (PyExc_NotImplementedError, + "Marshalling for this type is not implemented yet"); + return py_obj; +} diff --git a/gi/pygi-marshal-out.h b/gi/pygi-marshal-out.h new file mode 100644 index 00000000..db6bcfed --- /dev/null +++ b/gi/pygi-marshal-out.h @@ -0,0 +1,144 @@ +/* -*- Mode: C; c-basic-offset: 4 -*- + * vim: tabstop=4 shiftwidth=4 expandtab + * + * Copyright (C) 2011 John (J5) Palmieri <johnp@redhat.com>, Red Hat, Inc. + * + * 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, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 + * USA + */ + +#ifndef __PYGI_MARSHAL_OUT_H__ +#define __PYGI_MARSHAL_OUT_H__ + +PyObject *_pygi_marshal_out_void (PyGIInvokeState *state, + PyGICallableCache *callable_cache, + PyGIArgCache *arg_cache, + GIArgument *arg); +PyObject *_pygi_marshal_out_boolean (PyGIInvokeState *state, + PyGICallableCache *callable_cache, + PyGIArgCache *arg_cache, + GIArgument *arg); +PyObject *_pygi_marshal_out_int8 (PyGIInvokeState *state, + PyGICallableCache *callable_cache, + PyGIArgCache *arg_cache, + GIArgument *arg); +PyObject *_pygi_marshal_out_uint8 (PyGIInvokeState *state, + PyGICallableCache *callable_cache, + PyGIArgCache *arg_cache, + GIArgument *arg); +PyObject *_pygi_marshal_out_int16 (PyGIInvokeState *state, + PyGICallableCache *callable_cache, + PyGIArgCache *arg_cache, + GIArgument *arg); +PyObject *_pygi_marshal_out_uint16 (PyGIInvokeState *state, + PyGICallableCache *callable_cache, + PyGIArgCache *arg_cache, + GIArgument *arg); +PyObject *_pygi_marshal_out_int32 (PyGIInvokeState *state, + PyGICallableCache *callable_cache, + PyGIArgCache *arg_cache, + GIArgument *arg); +PyObject *_pygi_marshal_out_uint32 (PyGIInvokeState *state, + PyGICallableCache *callable_cache, + PyGIArgCache *arg_cache, + GIArgument *arg); +PyObject *_pygi_marshal_out_int64 (PyGIInvokeState *state, + PyGICallableCache *callable_cache, + PyGIArgCache *arg_cache, + GIArgument *arg); +PyObject *_pygi_marshal_out_uint64 (PyGIInvokeState *state, + PyGICallableCache *callable_cache, + PyGIArgCache *arg_cache, + GIArgument *arg); +PyObject *_pygi_marshal_out_float (PyGIInvokeState *state, + PyGICallableCache *callable_cache, + PyGIArgCache *arg_cache, + GIArgument *arg); +PyObject *_pygi_marshal_out_double (PyGIInvokeState *state, + PyGICallableCache *callable_cache, + PyGIArgCache *arg_cache, + GIArgument *arg); +PyObject *_pygi_marshal_out_unichar (PyGIInvokeState *state, + PyGICallableCache *callable_cache, + PyGIArgCache *arg_cache, + GIArgument *arg); +PyObject *_pygi_marshal_out_gtype (PyGIInvokeState *state, + PyGICallableCache *callable_cache, + PyGIArgCache *arg_cache, + GIArgument *arg); +PyObject *_pygi_marshal_out_utf8 (PyGIInvokeState *state, + PyGICallableCache *callable_cache, + PyGIArgCache *arg_cache, + GIArgument *arg); +PyObject *_pygi_marshal_out_filename (PyGIInvokeState *state, + PyGICallableCache *callable_cache, + PyGIArgCache *arg_cache, + GIArgument *arg); +PyObject *_pygi_marshal_out_array (PyGIInvokeState *state, + PyGICallableCache *callable_cache, + PyGIArgCache *arg_cache, + GIArgument *arg); +PyObject *_pygi_marshal_out_glist (PyGIInvokeState *state, + PyGICallableCache *callable_cache, + PyGIArgCache *arg_cache, + GIArgument *arg); +PyObject *_pygi_marshal_out_gslist (PyGIInvokeState *state, + PyGICallableCache *callable_cache, + PyGIArgCache *arg_cache, + GIArgument *arg); +PyObject *_pygi_marshal_out_ghash (PyGIInvokeState *state, + PyGICallableCache *callable_cache, + PyGIArgCache *arg_cache, + GIArgument *arg); +PyObject *_pygi_marshal_out_gerror (PyGIInvokeState *state, + PyGICallableCache *callable_cache, + PyGIArgCache *arg_cache, + GIArgument *arg); +PyObject *_pygi_marshal_out_interface_callback(PyGIInvokeState *state, + PyGICallableCache *callable_cache, + PyGIArgCache *arg_cache, + GIArgument *arg); +PyObject *_pygi_marshal_out_interface_enum (PyGIInvokeState *state, + PyGICallableCache *callable_cache, + PyGIArgCache *arg_cache, + GIArgument *arg); +PyObject *_pygi_marshal_out_interface_flags (PyGIInvokeState *state, + PyGICallableCache *callable_cache, + PyGIArgCache *arg_cache, + GIArgument *arg); +PyObject *_pygi_marshal_out_interface_struct (PyGIInvokeState *state, + PyGICallableCache *callable_cache, + PyGIArgCache *arg_cache, + GIArgument *arg); +PyObject *_pygi_marshal_out_interface_interface(PyGIInvokeState *state, + PyGICallableCache *callable_cache, + PyGIArgCache *arg_cache, + GIArgument *arg); +PyObject *_pygi_marshal_out_interface_boxed (PyGIInvokeState *state, + PyGICallableCache *callable_cache, + PyGIArgCache *arg_cache, + GIArgument *arg); +PyObject *_pygi_marshal_out_interface_object (PyGIInvokeState *state, + PyGICallableCache *callable_cache, + PyGIArgCache *arg_cache, + GIArgument *arg); +PyObject *_pygi_marshal_out_interface_union (PyGIInvokeState *state, + PyGICallableCache *callable_cache, + PyGIArgCache *arg_cache, + GIArgument *arg); + +G_END_DECLS + +#endif /* __PYGI_MARSHAL_OUT_H__ */ diff --git a/gi/pygi-private.h b/gi/pygi-private.h index efe62c85..8b277788 100644 --- a/gi/pygi-private.h +++ b/gi/pygi-private.h @@ -27,9 +27,13 @@ #include "pygi-foreign.h" #include "pygi-closure.h" #include "pygi-callbacks.h" -#include "pygi-invoke.h" #include "pygi-property.h" #include "pygi-signal-closure.h" +#include "pygi-invoke.h" + +#ifdef ENABLE_INVOKE_NG + #include "pygi-cache.h" +#endif G_BEGIN_DECLS #if PY_VERSION_HEX >= 0x03000000 @@ -32,6 +32,7 @@ #if ENABLE_INTROSPECTION #include <girepository.h> +#include "pygi-cache.h" typedef struct { PyObject_HEAD @@ -42,6 +43,9 @@ typedef struct { PyObject_HEAD GIBaseInfo *info; PyObject *inst_weakreflist; +#ifdef ENABLE_INVOKE_NG + PyGICallableCache *cache; +#endif } PyGIBaseInfo; typedef struct { diff --git a/gobject/pygtype.c b/gobject/pygtype.c index 63ac1c66..ffd99a55 100644 --- a/gobject/pygtype.c +++ b/gobject/pygtype.c @@ -743,11 +743,12 @@ int pyg_value_from_pyobject(GValue *value, PyObject *obj) { PyObject *tmp; + GType value_type = G_VALUE_TYPE(value); - switch (G_TYPE_FUNDAMENTAL(G_VALUE_TYPE(value))) { + switch (G_TYPE_FUNDAMENTAL(value_type)) { case G_TYPE_INTERFACE: /* we only handle interface types that have a GObject prereq */ - if (g_type_is_a(G_VALUE_TYPE(value), G_TYPE_OBJECT)) { + if (g_type_is_a(value_type, G_TYPE_OBJECT)) { if (obj == Py_None) g_value_set_object(value, NULL); else { @@ -755,7 +756,7 @@ pyg_value_from_pyobject(GValue *value, PyObject *obj) return -1; } if (!G_TYPE_CHECK_INSTANCE_TYPE(pygobject_get(obj), - G_VALUE_TYPE(value))) { + value_type)) { return -1; } g_value_set_object(value, pygobject_get(obj)); diff --git a/tests/test_gi.py b/tests/test_gi.py index 6990225c..0436a6ec 100644 --- a/tests/test_gi.py +++ b/tests/test_gi.py @@ -742,7 +742,6 @@ class TestArray(unittest.TestCase): def test_gstrv_inout(self): self.assertEquals(['-1', '0', '1', '2'], GIMarshallingTests.gstrv_inout(['0', '1', '2'])) - class TestGArray(unittest.TestCase): def test_garray_int_none_return(self): @@ -786,6 +785,48 @@ class TestGArray(unittest.TestCase): def test_garray_utf8_full_inout(self): self.assertEquals(['-2', '-1','0', '1'], GIMarshallingTests.garray_utf8_full_inout(['0', '1', '2'])) +class TestGPtrArray(unittest.TestCase): + + def test_gptrarray_int_none_return(self): + self.assertEquals([0, 1, 2, 3], GIMarshallingTests.gptrarray_int_none_return()) + + def test_gptrarray_utf8_none_return(self): + self.assertEquals(['0', '1', '2'], GIMarshallingTests.gptrarray_utf8_none_return()) + + def test_gptrarray_utf8_container_return(self): + self.assertEquals(['0', '1', '2'], GIMarshallingTests.gptrarray_utf8_container_return()) + + def test_gptrarray_utf8_full_return(self): + self.assertEquals(['0', '1', '2'], GIMarshallingTests.gptrarray_utf8_full_return()) + + def test_gptrarray_int_none_in(self): + GIMarshallingTests.gptrarray_int_none_in(Sequence([0, 1, 2, 3])) + + self.assertRaises(TypeError, GIMarshallingTests.gptrarray_int_none_in, Sequence([-1, '0', 1, 2])) + + self.assertRaises(TypeError, GIMarshallingTests.gptrarray_int_none_in, 42) + self.assertRaises(TypeError, GIMarshallingTests.gptrarray_int_none_in, None) + + def test_gptrarray_utf8_none_in(self): + GIMarshallingTests.gptrarray_utf8_none_in(Sequence(['0', '1', '2'])) + + def test_gptrarray_utf8_none_out(self): + self.assertEquals(['0', '1', '2'], GIMarshallingTests.gptrarray_utf8_none_out()) + + def test_gptrarray_utf8_container_out(self): + self.assertEquals(['0', '1', '2'], GIMarshallingTests.gptrarray_utf8_container_out()) + + def test_gptrarray_utf8_full_out(self): + self.assertEquals(['0', '1', '2'], GIMarshallingTests.gptrarray_utf8_full_out()) + + def test_gptrarray_utf8_none_inout(self): + self.assertEquals(['-2', '-1', '0', '1'], GIMarshallingTests.gptrarray_utf8_none_inout(Sequence(('0', '1', '2')))) + + def test_gptrarray_utf8_container_inout(self): + self.assertEquals(['-2', '-1','0', '1'], GIMarshallingTests.gptrarray_utf8_container_inout(['0', '1', '2'])) + + def test_gptrarray_utf8_full_inout(self): + self.assertEquals(['-2', '-1','0', '1'], GIMarshallingTests.gptrarray_utf8_full_inout(['0', '1', '2'])) class TestGList(unittest.TestCase): diff --git a/tests/test_overrides.py b/tests/test_overrides.py index fe1fe98f..3e46b335 100644 --- a/tests/test_overrides.py +++ b/tests/test_overrides.py @@ -248,8 +248,7 @@ class TestGLib(unittest.TestCase): self.assertEqual(res, (-1, 'hello')) # array - vb = GLib.VariantBuilder() - vb.init(gi._gi.variant_type_from_string('ai')) + vb = GLib.VariantBuilder.new(gi._gi.variant_type_from_string('ai')) vb.add_value(GLib.Variant.new_int32(-1)) vb.add_value(GLib.Variant.new_int32(3)) res = vb.end().unpack() @@ -261,8 +260,7 @@ class TestGLib(unittest.TestCase): def test_gvariant_iteration(self): # array index access - vb = GLib.VariantBuilder() - vb.init(gi._gi.variant_type_from_string('ai')) + vb = GLib.VariantBuilder.new(gi._gi.variant_type_from_string('ai')) vb.add_value(GLib.Variant.new_int32(-1)) vb.add_value(GLib.Variant.new_int32(3)) v = vb.end() @@ -709,6 +707,7 @@ class TestGtk(unittest.TestCase): tree_store = Gtk.TreeStore(int, 'gchararray', TestGtk.TestClass, + GObject.TYPE_PYOBJECT, object, object, object, @@ -728,6 +727,7 @@ class TestGtk(unittest.TestCase): parent = tree_store.append(parent, (i, label, testobj, + testobj, test_pyobj, test_pydict, test_pylist, @@ -756,31 +756,33 @@ class TestGtk(unittest.TestCase): s = tree_store.get_value(treeiter, 1) obj = tree_store.get_value(treeiter, 2) obj.check(i, s) + obj2 = tree_store.get_value(treeiter, 3) + self.assertEquals(obj, obj2); - pyobj = tree_store.get_value(treeiter, 3) + pyobj = tree_store.get_value(treeiter, 4) self.assertEquals(pyobj, test_pyobj) - pydict = tree_store.get_value(treeiter, 4) + pydict = tree_store.get_value(treeiter, 5) self.assertEquals(pydict, test_pydict) - pylist = tree_store.get_value(treeiter, 5) + pylist = tree_store.get_value(treeiter, 6) self.assertEquals(pylist, test_pylist) - bool_1 = tree_store.get_value(treeiter, 6) - bool_2 = tree_store.get_value(treeiter, 7) + bool_1 = tree_store.get_value(treeiter, 7) + bool_2 = tree_store.get_value(treeiter, 8) self.assertEquals(bool_1, bool_2) self.assertTrue(isinstance(bool_1, bool)) self.assertTrue(isinstance(bool_2, bool)) - uint_ = tree_store.get_value(treeiter, 8) + uint_ = tree_store.get_value(treeiter, 9) self.assertEquals(uint_, i) - ulong_ = tree_store.get_value(treeiter, 9) + ulong_ = tree_store.get_value(treeiter, 10) self.assertEquals(ulong_, GObject.G_MAXULONG) - int64_ = tree_store.get_value(treeiter, 10) + int64_ = tree_store.get_value(treeiter, 11) self.assertEquals(int64_, GObject.G_MININT64) - uint64_ = tree_store.get_value(treeiter, 11) + uint64_ = tree_store.get_value(treeiter, 12) self.assertEquals(uint64_, 0xffffffffffffffff) - uchar_ = tree_store.get_value(treeiter, 12) + uchar_ = tree_store.get_value(treeiter, 13) self.assertEquals(ord(uchar_), 254) - char_ = tree_store.get_value(treeiter, 13) + char_ = tree_store.get_value(treeiter, 14) self.assertEquals(char_, 'a') parent = treeiter @@ -797,7 +799,7 @@ class TestGtk(unittest.TestCase): test_pylist = [1,"2", "3"] list_store = Gtk.ListStore(int, str, 'GIOverrideTreeAPITest', object, object, object, bool, bool) - for i in range(93): + for i in range(1, 93): label = 'this is row #%d' % i testobj = TestGtk.TestClass(self, i, label) parent = list_store.append((i, @@ -821,6 +823,17 @@ class TestGtk(unittest.TestCase): list_store.set_value(treeiter, 6, 1) list_store.set_value(treeiter, 7, True) + # test prepend + label = 'this is row #0' + list_store.prepend((0, + label, + TestGtk.TestClass(self, 0, label), + test_pyobj, + test_pydict, + test_pylist, + 0, + False)) + # test automatic unicode->str conversion i = 94 label = _unicode('this is row #94') |