From 1d0f120d77582509b4e75d83f500a1ace7ed6421 Mon Sep 17 00:00:00 2001 From: Simon Feltman Date: Sat, 12 Oct 2013 20:00:12 -0700 Subject: cache refactoring: Move various struct arg setup and marshaling to new file Move struct (boxed, union, gvalue, gclosure, variant, and pointer) argument cache setup and marshaling fragments into isolated file: pygi-struct-marshal.c. Remove redundant and dead code related to boxed and union marshaling. https://bugzilla.gnome.org/show_bug.cgi?id=709700 --- gi/Makefile.am | 2 + gi/pygi-argument.c | 1 + gi/pygi-array.c | 3 + gi/pygi-cache.c | 72 +------ gi/pygi-cache.h | 1 + gi/pygi-marshal-cleanup.c | 48 ----- gi/pygi-marshal-cleanup.h | 16 -- gi/pygi-marshal-from-py.c | 288 -------------------------- gi/pygi-marshal-from-py.h | 33 --- gi/pygi-marshal-to-py.c | 120 ----------- gi/pygi-marshal-to-py.h | 27 --- gi/pygi-object.c | 2 +- gi/pygi-struct-marshal.c | 516 ++++++++++++++++++++++++++++++++++++++++++++++ gi/pygi-struct-marshal.h | 73 +++++++ 14 files changed, 607 insertions(+), 595 deletions(-) create mode 100644 gi/pygi-struct-marshal.c create mode 100644 gi/pygi-struct-marshal.h diff --git a/gi/Makefile.am b/gi/Makefile.am index 3d153f4d..9c34c034 100644 --- a/gi/Makefile.am +++ b/gi/Makefile.am @@ -113,6 +113,8 @@ _gi_la_SOURCES = \ pygi-error.h \ pygi-object.c \ pygi-object.h \ + pygi-struct-marshal.c \ + pygi-struct-marshal.h \ pygi-hashtable.c \ pygi-hashtable.h _gi_la_CFLAGS = \ diff --git a/gi/pygi-argument.c b/gi/pygi-argument.c index 4624e0f8..f88ff62d 100644 --- a/gi/pygi-argument.c +++ b/gi/pygi-argument.c @@ -35,6 +35,7 @@ #include "pygi-marshal-to-py.h" #include "pygi-basictype.h" #include "pygi-object.h" +#include "pygi-struct-marshal.h" static gboolean diff --git a/gi/pygi-array.c b/gi/pygi-array.c index 7b515846..22eea53e 100644 --- a/gi/pygi-array.c +++ b/gi/pygi-array.c @@ -26,6 +26,9 @@ #include "pygi-private.h" #include "pygi-marshal-cleanup.h" +/* Needed for _pygi_marshal_cleanup_from_py_interface_struct_gvalue hack */ +#include "pygi-struct-marshal.h" + /* * GArray to Python */ diff --git a/gi/pygi-cache.c b/gi/pygi-cache.c index f26df8c6..aab25348 100644 --- a/gi/pygi-cache.c +++ b/gi/pygi-cache.c @@ -2,6 +2,7 @@ * vim: tabstop=4 shiftwidth=4 expandtab * * Copyright (C) 2011 John (J5) Palmieri + * Copyright (C) 2013 Simon Feltman * * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public @@ -34,6 +35,7 @@ #include "pygi-closure.h" #include "pygi-error.h" #include "pygi-object.h" +#include "pygi-struct-marshal.h" /* _arg_info_default_value @@ -250,48 +252,6 @@ _arg_cache_alloc (void) return g_slice_new0 (PyGIArgCache); } -static void -_arg_cache_from_py_interface_union_setup (PyGIArgCache *arg_cache, - GITransfer transfer) -{ - arg_cache->from_py_marshaller = _pygi_marshal_from_py_interface_struct_cache_adapter; -} - -static void -_arg_cache_to_py_interface_union_setup (PyGIArgCache *arg_cache, - GITransfer transfer) -{ - arg_cache->to_py_marshaller = _pygi_marshal_to_py_interface_struct_cache_adapter; -} - -static void -_arg_cache_from_py_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->from_py_marshaller = _pygi_marshal_from_py_interface_struct_cache_adapter; - - if (iface_cache->g_type == G_TYPE_VALUE) - arg_cache->from_py_cleanup = _pygi_marshal_cleanup_from_py_interface_struct_gvalue; - else if (iface_cache->is_foreign) - arg_cache->from_py_cleanup = _pygi_marshal_cleanup_from_py_interface_struct_foreign; -} - -static void -_arg_cache_to_py_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->to_py_marshaller = _pygi_marshal_to_py_interface_struct_cache_adapter; - - if (iface_cache->is_foreign) - arg_cache->to_py_cleanup = _pygi_marshal_cleanup_to_py_interface_struct_foreign; -} - static void _arg_cache_from_py_interface_enum_setup (PyGIArgCache *arg_cache, GITransfer transfer) @@ -351,6 +311,14 @@ _arg_cache_new_for_interface (GIInterfaceInfo *iface_info, transfer, direction, iface_info); + case GI_INFO_TYPE_BOXED: + case GI_INFO_TYPE_STRUCT: + case GI_INFO_TYPE_UNION: + return pygi_arg_struct_new_from_info (type_info, + arg_info, + transfer, + direction, + iface_info); default: ; /* pass through to old model of setup */ } @@ -364,26 +332,6 @@ _arg_cache_new_for_interface (GIInterfaceInfo *iface_info, return NULL; switch (info_type) { - case GI_INFO_TYPE_UNION: - if (direction & PYGI_DIRECTION_FROM_PYTHON) - _arg_cache_from_py_interface_union_setup (arg_cache, transfer); - - if (direction & PYGI_DIRECTION_TO_PYTHON) - _arg_cache_to_py_interface_union_setup (arg_cache, transfer); - - break; - case GI_INFO_TYPE_BOXED: - case GI_INFO_TYPE_STRUCT: - if (direction & PYGI_DIRECTION_FROM_PYTHON) - _arg_cache_from_py_interface_struct_setup (arg_cache, - iface_info, - transfer); - - if (direction & PYGI_DIRECTION_TO_PYTHON) - _arg_cache_to_py_interface_struct_setup (arg_cache, - iface_info, - transfer); - break; case GI_INFO_TYPE_ENUM: if (direction & PYGI_DIRECTION_FROM_PYTHON) _arg_cache_from_py_interface_enum_setup (arg_cache, transfer); diff --git a/gi/pygi-cache.h b/gi/pygi-cache.h index 3cbe2b60..28046f1a 100644 --- a/gi/pygi-cache.h +++ b/gi/pygi-cache.h @@ -2,6 +2,7 @@ * vim: tabstop=4 shiftwidth=4 expandtab * * Copyright (C) 2011 John (J5) Palmieri + * Copyright (C) 2013 Simon Feltman * * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public diff --git a/gi/pygi-marshal-cleanup.c b/gi/pygi-marshal-cleanup.c index 0bbfab68..3d826016 100644 --- a/gi/pygi-marshal-cleanup.c +++ b/gi/pygi-marshal-cleanup.c @@ -205,51 +205,3 @@ pygi_marshal_cleanup_args_to_py_parameter_fail (PyGIInvokeState *state, { state->failed = TRUE; } - -void -_pygi_marshal_cleanup_from_py_interface_struct_gvalue (PyGIInvokeState *state, - PyGIArgCache *arg_cache, - PyObject *py_arg, - gpointer data, - gboolean was_processed) -{ - /* Note py_arg can be NULL for hash table which is a bug. */ - if (was_processed && py_arg != NULL) { - GType py_object_type = - pyg_type_from_object_strict ( (PyObject *) py_arg->ob_type, FALSE); - - /* When a GValue was not passed, it means the marshalers created a new - * one to pass in, clean this up. - */ - if (py_object_type != G_TYPE_VALUE) { - g_value_unset ((GValue *) data); - g_slice_free (GValue, data); - } - } -} - -void -_pygi_marshal_cleanup_from_py_interface_struct_foreign (PyGIInvokeState *state, - PyGIArgCache *arg_cache, - PyObject *py_arg, - gpointer data, - gboolean was_processed) -{ - if (state->failed && was_processed) - pygi_struct_foreign_release ( - ( (PyGIInterfaceCache *)arg_cache)->interface_info, - data); -} - -void -_pygi_marshal_cleanup_to_py_interface_struct_foreign (PyGIInvokeState *state, - PyGIArgCache *arg_cache, - PyObject *dummy, - gpointer data, - gboolean was_processed) -{ - if (!was_processed && arg_cache->transfer == GI_TRANSFER_EVERYTHING) - pygi_struct_foreign_release ( - ( (PyGIInterfaceCache *)arg_cache)->interface_info, - data); -} diff --git a/gi/pygi-marshal-cleanup.h b/gi/pygi-marshal-cleanup.h index 2463cab1..e895f37a 100644 --- a/gi/pygi-marshal-cleanup.h +++ b/gi/pygi-marshal-cleanup.h @@ -39,22 +39,6 @@ void pygi_marshal_cleanup_args_return_fail (PyGIInvokeState *state, void pygi_marshal_cleanup_args_to_py_parameter_fail (PyGIInvokeState *state, PyGICallableCache *cache, gssize failed_to_py_arg_index); - -void _pygi_marshal_cleanup_from_py_interface_struct_gvalue (PyGIInvokeState *state, - PyGIArgCache *arg_cache, - PyObject *py_arg, - gpointer data, - gboolean was_processed); -void _pygi_marshal_cleanup_from_py_interface_struct_foreign (PyGIInvokeState *state, - PyGIArgCache *arg_cache, - PyObject *py_arg, - gpointer data, - gboolean was_processed); -void _pygi_marshal_cleanup_to_py_interface_struct_foreign (PyGIInvokeState *state, - PyGIArgCache *arg_cache, - PyObject *dummy, - gpointer data, - gboolean was_processed); G_END_DECLS #endif /* __PYGI_MARSHAL_CLEANUP_H__ */ diff --git a/gi/pygi-marshal-from-py.c b/gi/pygi-marshal-from-py.c index ba798591..259f9eaf 100644 --- a/gi/pygi-marshal-from-py.c +++ b/gi/pygi-marshal-from-py.c @@ -87,59 +87,6 @@ gi_argument_from_c_long (GIArgument *arg_out, } } -/* - * _is_union_member - check to see if the py_arg is actually a member of the - * expected C union - */ -static gboolean -_is_union_member (GIInterfaceInfo *interface_info, PyObject *py_arg) { - gint i; - gint n_fields; - GIUnionInfo *union_info; - GIInfoType info_type; - gboolean is_member = FALSE; - - info_type = g_base_info_get_type (interface_info); - - if (info_type != GI_INFO_TYPE_UNION) - return FALSE; - - union_info = (GIUnionInfo *) interface_info; - n_fields = g_union_info_get_n_fields (union_info); - - for (i = 0; i < n_fields; i++) { - GIFieldInfo *field_info; - GITypeInfo *field_type_info; - - field_info = g_union_info_get_field (union_info, i); - field_type_info = g_field_info_get_type (field_info); - - /* we can only check if the members are interfaces */ - if (g_type_info_get_tag (field_type_info) == GI_TYPE_TAG_INTERFACE) { - GIInterfaceInfo *field_iface_info; - PyObject *py_type; - - field_iface_info = g_type_info_get_interface (field_type_info); - py_type = _pygi_type_import_by_gi_info ((GIBaseInfo *) field_iface_info); - - if (py_type != NULL && PyObject_IsInstance (py_arg, py_type)) { - is_member = TRUE; - } - - Py_XDECREF (py_type); - g_base_info_unref ( ( GIBaseInfo *) field_iface_info); - } - - g_base_info_unref ( ( GIBaseInfo *) field_type_info); - g_base_info_unref ( ( GIBaseInfo *) field_info); - - if (is_member) - break; - } - - return is_member; -} - gboolean _pygi_marshal_from_py_interface_enum (PyGIInvokeState *state, PyGICallableCache *callable_cache, @@ -257,238 +204,3 @@ err: } -gboolean -_pygi_marshal_from_py_interface_struct_cache_adapter (PyGIInvokeState *state, - PyGICallableCache *callable_cache, - PyGIArgCache *arg_cache, - PyObject *py_arg, - GIArgument *arg, - gpointer *cleanup_data) -{ - PyGIInterfaceCache *iface_cache = (PyGIInterfaceCache *)arg_cache; - - gboolean res = _pygi_marshal_from_py_interface_struct (py_arg, - arg, - arg_cache->arg_name, - iface_cache->interface_info, - iface_cache->g_type, - iface_cache->py_type, - arg_cache->transfer, - TRUE, /*copy_reference*/ - iface_cache->is_foreign, - arg_cache->is_pointer); - - /* Assume struct marshaling is always a pointer and assign cleanup_data - * here rather than passing it further down the chain. - */ - *cleanup_data = arg->v_pointer; - return res; -} - -gboolean -_pygi_marshal_from_py_interface_boxed (PyGIInvokeState *state, - PyGICallableCache *callable_cache, - PyGIArgCache *arg_cache, - PyObject *py_arg, - GIArgument *arg, - gpointer *cleanup_data) -{ - PyErr_Format (PyExc_NotImplementedError, - "Marshalling for this type is not implemented yet"); - return FALSE; -} - -gboolean -_pygi_marshal_from_py_interface_union (PyGIInvokeState *state, - PyGICallableCache *callable_cache, - PyGIArgCache *arg_cache, - PyObject *py_arg, - GIArgument *arg, - gpointer *cleanup_data) -{ - PyErr_Format(PyExc_NotImplementedError, - "Marshalling for this type is not implemented yet"); - return FALSE; -} - - -/* _pygi_marshal_from_py_gvalue: - * py_arg: (in): - * arg: (out): - * transfer: - * copy_reference: TRUE if arg should use the pointer reference held by py_arg - * when it is already holding a GValue vs. copying the value. - */ -gboolean -_pygi_marshal_from_py_gvalue (PyObject *py_arg, - GIArgument *arg, - GITransfer transfer, - gboolean copy_reference) { - 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) { - GValue *source_value = pyg_boxed_get (py_arg, GValue); - if (copy_reference) { - value = source_value; - } else { - value = g_slice_new0 (GValue); - g_value_init (value, G_VALUE_TYPE (source_value)); - g_value_copy (source_value, value); - } - } 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; -} - -/* _pygi_marshal_from_py_gclosure: - * py_arg: (in): - * arg: (out): - */ -gboolean -_pygi_marshal_from_py_gclosure(PyObject *py_arg, - GIArgument *arg) -{ - 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; -} - -gboolean -_pygi_marshal_from_py_interface_struct (PyObject *py_arg, - GIArgument *arg, - const gchar *arg_name, - GIBaseInfo *interface_info, - GType g_type, - PyObject *py_type, - GITransfer transfer, - gboolean copy_reference, - gboolean is_foreign, - gboolean is_pointer) -{ - gboolean is_union = FALSE; - - 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 (g_type_is_a (g_type, G_TYPE_CLOSURE)) { - return _pygi_marshal_from_py_gclosure (py_arg, arg); - } else if (g_type_is_a (g_type, G_TYPE_VALUE)) { - return _pygi_marshal_from_py_gvalue(py_arg, - arg, - transfer, - copy_reference); - } else if (is_foreign) { - PyObject *success; - success = pygi_struct_foreign_convert_to_g_argument (py_arg, - interface_info, - transfer, - arg); - - return (success == Py_None); - } else if (!PyObject_IsInstance (py_arg, py_type)) { - /* first check to see if this is a member of the expected union */ - is_union = _is_union_member (interface_info, py_arg); - if (!is_union) { - goto type_error; - } - } - - if (g_type_is_a (g_type, G_TYPE_BOXED)) { - /* Additionally use pyg_type_from_object to pull the stashed __gtype__ - * attribute off of the input argument for type checking. This is needed - * to work around type discrepancies in cases with aliased (typedef) types. - * e.g. GtkAllocation, GdkRectangle. - * See: https://bugzilla.gnomethere are .org/show_bug.cgi?id=707140 - */ - if (is_union || pyg_boxed_check (py_arg, g_type) || - g_type_is_a (pyg_type_from_object (py_arg), g_type)) { - arg->v_pointer = pyg_boxed_get (py_arg, void); - if (transfer == GI_TRANSFER_EVERYTHING) { - arg->v_pointer = g_boxed_copy (g_type, arg->v_pointer); - } - } else { - goto type_error; - } - - } else if (g_type_is_a (g_type, G_TYPE_POINTER) || - g_type_is_a (g_type, G_TYPE_VARIANT) || - g_type == G_TYPE_NONE) { - g_warn_if_fail (g_type_is_a (g_type, G_TYPE_VARIANT) || !is_pointer || transfer == GI_TRANSFER_NOTHING); - - if (g_type_is_a (g_type, G_TYPE_VARIANT) && - pyg_type_from_object (py_arg) != G_TYPE_VARIANT) { - PyErr_SetString (PyExc_TypeError, "expected GLib.Variant"); - return FALSE; - } - arg->v_pointer = pyg_pointer_get (py_arg, void); - if (transfer == GI_TRANSFER_EVERYTHING) { - g_variant_ref ((GVariant *)arg->v_pointer); - } - - } else { - PyErr_Format (PyExc_NotImplementedError, - "structure type '%s' is not supported yet", - g_type_name(g_type)); - return FALSE; - } - return TRUE; - -type_error: - { - gchar *type_name = _pygi_g_base_info_get_fullname (interface_info); - PyObject *module = PyObject_GetAttrString(py_arg, "__module__"); - - PyErr_Format (PyExc_TypeError, "argument %s: Expected %s, but got %s%s%s", - arg_name ? arg_name : "self", - type_name, - module ? PYGLIB_PyUnicode_AsString(module) : "", - module ? "." : "", - py_arg->ob_type->tp_name); - if (module) - Py_DECREF (module); - g_free (type_name); - return FALSE; - } -} diff --git a/gi/pygi-marshal-from-py.h b/gi/pygi-marshal-from-py.h index 4b59601e..2daa3810 100644 --- a/gi/pygi-marshal-from-py.h +++ b/gi/pygi-marshal-from-py.h @@ -45,18 +45,6 @@ gboolean _pygi_marshal_from_py_interface_flags (PyGIInvokeState *state, PyObject *py_arg, GIArgument *arg, gpointer *cleanup_data); -gboolean _pygi_marshal_from_py_interface_struct_cache_adapter (PyGIInvokeState *state, - PyGICallableCache *callable_cache, - PyGIArgCache *arg_cache, - PyObject *py_arg, - GIArgument *arg, - gpointer *cleanup_data); -gboolean _pygi_marshal_from_py_interface_boxed (PyGIInvokeState *state, - PyGICallableCache *callable_cache, - PyGIArgCache *arg_cache, - PyObject *py_arg, - GIArgument *arg, - gpointer *cleanup_data); gboolean _pygi_marshal_from_py_interface_union (PyGIInvokeState *state, PyGICallableCache *callable_cache, PyGIArgCache *arg_cache, @@ -64,27 +52,6 @@ gboolean _pygi_marshal_from_py_interface_union (PyGIInvokeState *state, GIArgument *arg, gpointer *cleanup_data); -/* Simplified marshalers shared between vfunc/closure and direct function calls. */ - -gboolean _pygi_marshal_from_py_gvalue (PyObject *py_arg, /*in*/ - GIArgument *arg, /*out*/ - GITransfer transfer, - gboolean is_allocated); - -gboolean _pygi_marshal_from_py_gclosure(PyObject *py_arg, /*in*/ - GIArgument *arg); /*out*/ - -gboolean _pygi_marshal_from_py_interface_struct (PyObject *py_arg, - GIArgument *arg, - const gchar *arg_name, - GIBaseInfo *interface_info, - GType g_type, - PyObject *py_type, - GITransfer transfer, - gboolean is_allocated, - gboolean is_foreign, - gboolean is_pointer); - G_END_DECLS #endif /* __PYGI_MARSHAL_from_py_PY__ */ diff --git a/gi/pygi-marshal-to-py.c b/gi/pygi-marshal-to-py.c index 536193c5..8384a42b 100644 --- a/gi/pygi-marshal-to-py.c +++ b/gi/pygi-marshal-to-py.c @@ -150,123 +150,3 @@ _pygi_marshal_to_py_interface_flags (PyGIInvokeState *state, return py_obj; } -PyObject * -_pygi_marshal_to_py_interface_struct_cache_adapter (PyGIInvokeState *state, - PyGICallableCache *callable_cache, - PyGIArgCache *arg_cache, - GIArgument *arg) -{ - PyGIInterfaceCache *iface_cache = (PyGIInterfaceCache *)arg_cache; - - return _pygi_marshal_to_py_interface_struct (arg, - iface_cache->interface_info, - iface_cache->g_type, - iface_cache->py_type, - arg_cache->transfer, - arg_cache->is_caller_allocates, - iface_cache->is_foreign); -} - -PyObject * -_pygi_marshal_to_py_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_to_py_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_to_py_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; -} - -PyObject * -_pygi_marshal_to_py_interface_struct (GIArgument *arg, - GIInterfaceInfo *interface_info, - GType g_type, - PyObject *py_type, - GITransfer transfer, - gboolean is_allocated, - gboolean is_foreign) -{ - PyObject *py_obj = NULL; - - if (arg->v_pointer == NULL) { - Py_RETURN_NONE; - } - - if (g_type_is_a (g_type, G_TYPE_VALUE)) { - py_obj = pyg_value_as_pyobject (arg->v_pointer, FALSE); - } else if (is_foreign) { - py_obj = pygi_struct_foreign_convert_from_g_argument (interface_info, - arg->v_pointer); - } else if (g_type_is_a (g_type, G_TYPE_BOXED)) { - if (py_type) { - py_obj = _pygi_boxed_new ((PyTypeObject *) py_type, - arg->v_pointer, - transfer == GI_TRANSFER_EVERYTHING || is_allocated, - is_allocated ? - g_struct_info_get_size(interface_info) : 0); - } - } else if (g_type_is_a (g_type, G_TYPE_POINTER)) { - if (py_type == NULL || - !PyType_IsSubtype ((PyTypeObject *) py_type, &PyGIStruct_Type)) { - g_warn_if_fail (transfer == GI_TRANSFER_NOTHING); - py_obj = pyg_pointer_new (g_type, arg->v_pointer); - } else { - py_obj = _pygi_struct_new ( (PyTypeObject *) py_type, - arg->v_pointer, - transfer == GI_TRANSFER_EVERYTHING); - } - } else if (g_type_is_a (g_type, G_TYPE_VARIANT)) { - /* Note: sink the variant (add a ref) only if we are not transfered ownership. - * GLib.Variant overrides __del__ which will then call "g_variant_unref" for - * cleanup in either case. */ - if (py_type) { - if (transfer == GI_TRANSFER_NOTHING) { - g_variant_ref_sink (arg->v_pointer); - } - py_obj = _pygi_struct_new ((PyTypeObject *) py_type, - arg->v_pointer, - FALSE); - } - } else if (g_type == G_TYPE_NONE) { - if (py_type) { - py_obj = _pygi_struct_new ((PyTypeObject *) py_type, - arg->v_pointer, - transfer == GI_TRANSFER_EVERYTHING); - } - } else { - PyErr_Format (PyExc_NotImplementedError, - "structure type '%s' is not supported yet", - g_type_name (g_type)); - } - - return py_obj; -} diff --git a/gi/pygi-marshal-to-py.h b/gi/pygi-marshal-to-py.h index aa845031..ad80a3bd 100644 --- a/gi/pygi-marshal-to-py.h +++ b/gi/pygi-marshal-to-py.h @@ -30,32 +30,5 @@ PyObject *_pygi_marshal_to_py_interface_flags (PyGIInvokeState *state, PyGICallableCache *callable_cache, PyGIArgCache *arg_cache, GIArgument *arg); -PyObject *_pygi_marshal_to_py_interface_struct_cache_adapter (PyGIInvokeState *state, - PyGICallableCache *callable_cache, - PyGIArgCache *arg_cache, - GIArgument *arg); -PyObject *_pygi_marshal_to_py_interface_interface(PyGIInvokeState *state, - PyGICallableCache *callable_cache, - PyGIArgCache *arg_cache, - GIArgument *arg); -PyObject *_pygi_marshal_to_py_interface_boxed (PyGIInvokeState *state, - PyGICallableCache *callable_cache, - PyGIArgCache *arg_cache, - GIArgument *arg); -PyObject *_pygi_marshal_to_py_interface_union (PyGIInvokeState *state, - PyGICallableCache *callable_cache, - PyGIArgCache *arg_cache, - GIArgument *arg); - -/* Simplified marshalers shared between vfunc/closure and direct function calls. */ -PyObject *_pygi_marshal_to_py_interface_struct (GIArgument *arg, - GIInterfaceInfo *interface_info, - GType g_type, - PyObject *py_type, - GITransfer transfer, - gboolean is_allocated, - gboolean is_foreign); - -G_END_DECLS #endif /* __PYGI_MARSHAL_TO_PY_H__ */ diff --git a/gi/pygi-object.c b/gi/pygi-object.c index 4ad58b1b..9abfc6b9 100644 --- a/gi/pygi-object.c +++ b/gi/pygi-object.c @@ -22,7 +22,7 @@ #include #include -#include "pygi-arg-gobject.h" +#include "pygi-object.h" #include "pygi-private.h" #include "pygparamspec.h" diff --git a/gi/pygi-struct-marshal.c b/gi/pygi-struct-marshal.c new file mode 100644 index 00000000..a30f03e3 --- /dev/null +++ b/gi/pygi-struct-marshal.c @@ -0,0 +1,516 @@ +/* -*- Mode: C; c-basic-offset: 4 -*- + * vim: tabstop=4 shiftwidth=4 expandtab + * + * Copyright (C) 2011 John (J5) Palmieri + * Copyright (C) 2014 Simon Feltman + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, see . + */ + +#include +#include +#include + +#include "pygi-struct-marshal.h" +#include "pygi-private.h" + +/* + * _is_union_member - check to see if the py_arg is actually a member of the + * expected C union + */ +static gboolean +_is_union_member (GIInterfaceInfo *interface_info, PyObject *py_arg) { + gint i; + gint n_fields; + GIUnionInfo *union_info; + GIInfoType info_type; + gboolean is_member = FALSE; + + info_type = g_base_info_get_type (interface_info); + + if (info_type != GI_INFO_TYPE_UNION) + return FALSE; + + union_info = (GIUnionInfo *) interface_info; + n_fields = g_union_info_get_n_fields (union_info); + + for (i = 0; i < n_fields; i++) { + GIFieldInfo *field_info; + GITypeInfo *field_type_info; + + field_info = g_union_info_get_field (union_info, i); + field_type_info = g_field_info_get_type (field_info); + + /* we can only check if the members are interfaces */ + if (g_type_info_get_tag (field_type_info) == GI_TYPE_TAG_INTERFACE) { + GIInterfaceInfo *field_iface_info; + PyObject *py_type; + + field_iface_info = g_type_info_get_interface (field_type_info); + py_type = _pygi_type_import_by_gi_info ((GIBaseInfo *) field_iface_info); + + if (py_type != NULL && PyObject_IsInstance (py_arg, py_type)) { + is_member = TRUE; + } + + Py_XDECREF (py_type); + g_base_info_unref ( ( GIBaseInfo *) field_iface_info); + } + + g_base_info_unref ( ( GIBaseInfo *) field_type_info); + g_base_info_unref ( ( GIBaseInfo *) field_info); + + if (is_member) + break; + } + + return is_member; +} + + +/* + * GValue from Python + */ + +/* _pygi_marshal_from_py_gvalue: + * py_arg: (in): + * arg: (out): + * transfer: + * copy_reference: TRUE if arg should use the pointer reference held by py_arg + * when it is already holding a GValue vs. copying the value. + */ +gboolean +_pygi_marshal_from_py_gvalue (PyObject *py_arg, + GIArgument *arg, + GITransfer transfer, + gboolean copy_reference) { + 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) { + GValue *source_value = pyg_boxed_get (py_arg, GValue); + if (copy_reference) { + value = source_value; + } else { + value = g_slice_new0 (GValue); + g_value_init (value, G_VALUE_TYPE (source_value)); + g_value_copy (source_value, value); + } + } 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; +} + +void +_pygi_marshal_cleanup_from_py_interface_struct_gvalue (PyGIInvokeState *state, + PyGIArgCache *arg_cache, + PyObject *py_arg, + gpointer data, + gboolean was_processed) +{ + /* Note py_arg can be NULL for hash table which is a bug. */ + if (was_processed && py_arg != NULL) { + GType py_object_type = + pyg_type_from_object_strict ( (PyObject *) py_arg->ob_type, FALSE); + + /* When a GValue was not passed, it means the marshalers created a new + * one to pass in, clean this up. + */ + if (py_object_type != G_TYPE_VALUE) { + g_value_unset ((GValue *) data); + g_slice_free (GValue, data); + } + } +} + +/* _pygi_marshal_from_py_gclosure: + * py_arg: (in): + * arg: (out): + */ +gboolean +_pygi_marshal_from_py_gclosure(PyObject *py_arg, + GIArgument *arg) +{ + 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; +} + +/* _pygi_marshal_from_py_interface_struct: + * + * Dispatcher to various sub marshalers + */ +gboolean +_pygi_marshal_from_py_interface_struct (PyObject *py_arg, + GIArgument *arg, + const gchar *arg_name, + GIBaseInfo *interface_info, + GType g_type, + PyObject *py_type, + GITransfer transfer, + gboolean copy_reference, + gboolean is_foreign, + gboolean is_pointer) +{ + gboolean is_union = FALSE; + + 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 (g_type_is_a (g_type, G_TYPE_CLOSURE)) { + return _pygi_marshal_from_py_gclosure (py_arg, arg); + } else if (g_type_is_a (g_type, G_TYPE_VALUE)) { + return _pygi_marshal_from_py_gvalue(py_arg, + arg, + transfer, + copy_reference); + } else if (is_foreign) { + PyObject *success; + success = pygi_struct_foreign_convert_to_g_argument (py_arg, + interface_info, + transfer, + arg); + + return (success == Py_None); + } else if (!PyObject_IsInstance (py_arg, py_type)) { + /* first check to see if this is a member of the expected union */ + is_union = _is_union_member (interface_info, py_arg); + if (!is_union) { + goto type_error; + } + } + + if (g_type_is_a (g_type, G_TYPE_BOXED)) { + /* Additionally use pyg_type_from_object to pull the stashed __gtype__ + * attribute off of the input argument for type checking. This is needed + * to work around type discrepancies in cases with aliased (typedef) types. + * e.g. GtkAllocation, GdkRectangle. + * See: https://bugzilla.gnomethere are .org/show_bug.cgi?id=707140 + */ + if (is_union || pyg_boxed_check (py_arg, g_type) || + g_type_is_a (pyg_type_from_object (py_arg), g_type)) { + arg->v_pointer = pyg_boxed_get (py_arg, void); + if (transfer == GI_TRANSFER_EVERYTHING) { + arg->v_pointer = g_boxed_copy (g_type, arg->v_pointer); + } + } else { + goto type_error; + } + + } else if (g_type_is_a (g_type, G_TYPE_POINTER) || + g_type_is_a (g_type, G_TYPE_VARIANT) || + g_type == G_TYPE_NONE) { + g_warn_if_fail (g_type_is_a (g_type, G_TYPE_VARIANT) || !is_pointer || transfer == GI_TRANSFER_NOTHING); + + if (g_type_is_a (g_type, G_TYPE_VARIANT) && + pyg_type_from_object (py_arg) != G_TYPE_VARIANT) { + PyErr_SetString (PyExc_TypeError, "expected GLib.Variant"); + return FALSE; + } + arg->v_pointer = pyg_pointer_get (py_arg, void); + if (transfer == GI_TRANSFER_EVERYTHING) { + g_variant_ref ((GVariant *)arg->v_pointer); + } + + } else { + PyErr_Format (PyExc_NotImplementedError, + "structure type '%s' is not supported yet", + g_type_name(g_type)); + return FALSE; + } + return TRUE; + +type_error: + { + gchar *type_name = _pygi_g_base_info_get_fullname (interface_info); + PyObject *module = PyObject_GetAttrString(py_arg, "__module__"); + + PyErr_Format (PyExc_TypeError, "argument %s: Expected %s, but got %s%s%s", + arg_name ? arg_name : "self", + type_name, + module ? PYGLIB_PyUnicode_AsString(module) : "", + module ? "." : "", + py_arg->ob_type->tp_name); + if (module) + Py_DECREF (module); + g_free (type_name); + return FALSE; + } +} + +static gboolean +_pygi_marshal_from_py_interface_struct_cache_adapter (PyGIInvokeState *state, + PyGICallableCache *callable_cache, + PyGIArgCache *arg_cache, + PyObject *py_arg, + GIArgument *arg, + gpointer *cleanup_data) +{ + PyGIInterfaceCache *iface_cache = (PyGIInterfaceCache *)arg_cache; + + gboolean res = _pygi_marshal_from_py_interface_struct (py_arg, + arg, + arg_cache->arg_name, + iface_cache->interface_info, + iface_cache->g_type, + iface_cache->py_type, + arg_cache->transfer, + TRUE, /*copy_reference*/ + iface_cache->is_foreign, + arg_cache->is_pointer); + + /* Assume struct marshaling is always a pointer and assign cleanup_data + * here rather than passing it further down the chain. + */ + *cleanup_data = arg->v_pointer; + return res; +} + +static void +_pygi_marshal_cleanup_from_py_interface_struct_foreign (PyGIInvokeState *state, + PyGIArgCache *arg_cache, + PyObject *py_arg, + gpointer data, + gboolean was_processed) +{ + if (state->failed && was_processed) + pygi_struct_foreign_release ( + ( (PyGIInterfaceCache *)arg_cache)->interface_info, + data); +} + + +PyObject * +_pygi_marshal_to_py_interface_struct (GIArgument *arg, + GIInterfaceInfo *interface_info, + GType g_type, + PyObject *py_type, + GITransfer transfer, + gboolean is_allocated, + gboolean is_foreign) +{ + PyObject *py_obj = NULL; + + if (arg->v_pointer == NULL) { + Py_RETURN_NONE; + } + + if (g_type_is_a (g_type, G_TYPE_VALUE)) { + py_obj = pyg_value_as_pyobject (arg->v_pointer, FALSE); + } else if (is_foreign) { + py_obj = pygi_struct_foreign_convert_from_g_argument (interface_info, + arg->v_pointer); + } else if (g_type_is_a (g_type, G_TYPE_BOXED)) { + if (py_type) { + py_obj = _pygi_boxed_new ((PyTypeObject *) py_type, + arg->v_pointer, + transfer == GI_TRANSFER_EVERYTHING || is_allocated, + is_allocated ? + g_struct_info_get_size(interface_info) : 0); + } + } else if (g_type_is_a (g_type, G_TYPE_POINTER)) { + if (py_type == NULL || + !PyType_IsSubtype ((PyTypeObject *) py_type, &PyGIStruct_Type)) { + g_warn_if_fail (transfer == GI_TRANSFER_NOTHING); + py_obj = pyg_pointer_new (g_type, arg->v_pointer); + } else { + py_obj = _pygi_struct_new ( (PyTypeObject *) py_type, + arg->v_pointer, + transfer == GI_TRANSFER_EVERYTHING); + } + } else if (g_type_is_a (g_type, G_TYPE_VARIANT)) { + /* Note: sink the variant (add a ref) only if we are not transfered ownership. + * GLib.Variant overrides __del__ which will then call "g_variant_unref" for + * cleanup in either case. */ + if (py_type) { + if (transfer == GI_TRANSFER_NOTHING) { + g_variant_ref_sink (arg->v_pointer); + } + py_obj = _pygi_struct_new ((PyTypeObject *) py_type, + arg->v_pointer, + FALSE); + } + } else if (g_type == G_TYPE_NONE) { + if (py_type) { + py_obj = _pygi_struct_new ((PyTypeObject *) py_type, + arg->v_pointer, + transfer == GI_TRANSFER_EVERYTHING); + } + } else { + PyErr_Format (PyExc_NotImplementedError, + "structure type '%s' is not supported yet", + g_type_name (g_type)); + } + + return py_obj; +} + +static PyObject * +_pygi_marshal_to_py_interface_struct_cache_adapter (PyGIInvokeState *state, + PyGICallableCache *callable_cache, + PyGIArgCache *arg_cache, + GIArgument *arg) +{ + PyGIInterfaceCache *iface_cache = (PyGIInterfaceCache *)arg_cache; + + return _pygi_marshal_to_py_interface_struct (arg, + iface_cache->interface_info, + iface_cache->g_type, + iface_cache->py_type, + arg_cache->transfer, + arg_cache->is_caller_allocates, + iface_cache->is_foreign); +} + +static void +_pygi_marshal_cleanup_to_py_interface_struct_foreign (PyGIInvokeState *state, + PyGIArgCache *arg_cache, + PyObject *dummy, + gpointer data, + gboolean was_processed) +{ + if (!was_processed && arg_cache->transfer == GI_TRANSFER_EVERYTHING) + pygi_struct_foreign_release ( + ( (PyGIInterfaceCache *)arg_cache)->interface_info, + data); +} + + +static void +_arg_cache_from_py_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->from_py_marshaller = _pygi_marshal_from_py_interface_struct_cache_adapter; + + if (iface_cache->g_type == G_TYPE_VALUE) + arg_cache->from_py_cleanup = _pygi_marshal_cleanup_from_py_interface_struct_gvalue; + else if (iface_cache->is_foreign) + arg_cache->from_py_cleanup = _pygi_marshal_cleanup_from_py_interface_struct_foreign; +} + +static void +_arg_cache_to_py_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->to_py_marshaller = _pygi_marshal_to_py_interface_struct_cache_adapter; + + if (iface_cache->is_foreign) + arg_cache->to_py_cleanup = _pygi_marshal_cleanup_to_py_interface_struct_foreign; +} + +static gboolean +pygi_arg_struct_setup_from_info (PyGIArgCache *arg_cache, + GITypeInfo *type_info, + GIArgInfo *arg_info, + GITransfer transfer, + PyGIDirection direction, + GIInterfaceInfo *iface_info) +{ + /* NOTE: usage of pygi_arg_interface_new_from_info already calls + * pygi_arg_interface_setup so no need to do it here. + */ + + if (direction & PYGI_DIRECTION_FROM_PYTHON) { + _arg_cache_from_py_interface_struct_setup (arg_cache, + iface_info, + transfer); + } + + if (direction & PYGI_DIRECTION_TO_PYTHON) { + _arg_cache_to_py_interface_struct_setup (arg_cache, + iface_info, + transfer); + } + + return TRUE; +} + +PyGIArgCache * +pygi_arg_struct_new_from_info (GITypeInfo *type_info, + GIArgInfo *arg_info, + GITransfer transfer, + PyGIDirection direction, + GIInterfaceInfo *iface_info) +{ + gboolean res = FALSE; + PyGIArgCache *cache = NULL; + + cache = pygi_arg_interface_new_from_info (type_info, + arg_info, + transfer, + direction, + iface_info); + if (cache == NULL) + return NULL; + + res = pygi_arg_struct_setup_from_info (cache, + type_info, + arg_info, + transfer, + direction, + iface_info); + if (res) { + return cache; + } else { + _pygi_arg_cache_free (cache); + return NULL; + } +} diff --git a/gi/pygi-struct-marshal.h b/gi/pygi-struct-marshal.h new file mode 100644 index 00000000..66e3ecfe --- /dev/null +++ b/gi/pygi-struct-marshal.h @@ -0,0 +1,73 @@ +/* -*- Mode: C; c-basic-offset: 4 -*- + * vim: tabstop=4 shiftwidth=4 expandtab + * + * Copyright (C) 2011 John (J5) Palmieri + * Copyright (C) 2014 Simon Feltman + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, see . + */ + +#ifndef __PYGI_STRUCT_MARSHAL_H__ +#define __PYGI_STRUCT_MARSHAL_H__ + +#include +#include "pygi-cache.h" + +G_BEGIN_DECLS + +PyGIArgCache *pygi_arg_struct_new_from_info (GITypeInfo *type_info, + GIArgInfo *arg_info, /* may be null */ + GITransfer transfer, + PyGIDirection direction, + GIInterfaceInfo *iface_info); + + +gboolean _pygi_marshal_from_py_gvalue (PyObject *py_arg, /*in*/ + GIArgument *arg, /*out*/ + GITransfer transfer, + gboolean is_allocated); + + +gboolean _pygi_marshal_from_py_gclosure(PyObject *py_arg, /*in*/ + GIArgument *arg); /*out*/ + +gboolean _pygi_marshal_from_py_interface_struct (PyObject *py_arg, + GIArgument *arg, + const gchar *arg_name, + GIBaseInfo *interface_info, + GType g_type, + PyObject *py_type, + GITransfer transfer, + gboolean is_allocated, + gboolean is_foreign, + gboolean is_pointer); + +PyObject *_pygi_marshal_to_py_interface_struct (GIArgument *arg, + GIInterfaceInfo *interface_info, + GType g_type, + PyObject *py_type, + GITransfer transfer, + gboolean is_allocated, + gboolean is_foreign); + +/* Needed for hack in pygi-arg-garray.c */ +void _pygi_marshal_cleanup_from_py_interface_struct_gvalue (PyGIInvokeState *state, + PyGIArgCache *arg_cache, + PyObject *py_arg, + gpointer data, + gboolean was_processed); + +G_END_DECLS + +#endif /*__PYGI_STRUCT_MARSHAL_H__*/ -- cgit v1.2.1