[pygobject] Fix array handling for interfaces, properties, and signals
- From: Martin Pitt <martinpitt src gnome org>
- To: commits-list gnome org
- Cc:
- Subject: [pygobject] Fix array handling for interfaces, properties, and signals
- Date: Thu, 12 Jul 2012 07:21:55 +0000 (UTC)
commit a31fabdc12f1da301c8df0af319ca3f4181671ee
Author: Mikkel Kamstrup Erlandsen <mikkel kamstrup canonical com>
Date: Thu Jul 12 09:19:42 2012 +0200
Fix array handling for interfaces, properties, and signals
Fix lots of corner cases where arrays are not handled properly.
_pygi_argument_to_object() now has the documented expectation of getting arrays
packed in GArrays. This was implicit before and not correctly done on most call
sites.
The helper _pygi_argument_to_array() has been improved to work on any kind of
array. Fix all call sites of _pygi_argument_to_object() to do the
array conversion appropriately before calling _pygi_argument_to_object().
Adds a test case that implements a GInterface with a method that takes an array
of variants as input.
https://bugzilla.gnome.org/show_bug.cgi?id=667244
gi/pygi-argument.c | 156 ++++++++++++++++++++++++++++++++--------------
gi/pygi-argument.h | 4 +-
gi/pygi-closure.c | 11 +++
gi/pygi-info.c | 20 ++++--
gi/pygi-property.c | 1 +
gi/pygi-signal-closure.c | 14 ++++-
tests/test_gi.py | 18 +++++
7 files changed, 167 insertions(+), 57 deletions(-)
---
diff --git a/gi/pygi-argument.c b/gi/pygi-argument.c
index fb798e1..b73973e 100644
--- a/gi/pygi-argument.c
+++ b/gi/pygi-argument.c
@@ -653,50 +653,105 @@ check_number_release:
return retval;
}
+/**
+ * _pygi_argument_to_array
+ * @arg: The argument to convert
+ * @args: Arguments to method invocation, possibly contaning the array length.
+ * Set to NULL if this is not for a method call
+ * @type_info: The type info for @arg
+ * @out_free_array: A return location for a gboolean that indicates whether
+ * or not the wrapped GArray should be freed
+ *
+ * Make sure an array type argument is wrapped in a GArray.
+ *
+ * Note: This method can *not* be folded into _pygi_argument_to_object() because
+ * arrays are special in the sense that they might require access to @args in
+ * order to get the length.
+ *
+ * Returns: A GArray wrapping @arg. If @out_free_array has been set to TRUE then
+ * free the array with g_array_free() without freeing the data members.
+ * Otherwise don't free the array.
+ */
GArray *
_pygi_argument_to_array (GIArgument *arg,
GIArgument *args[],
- GITypeInfo *type_info,
- gboolean is_method)
+ GITypeInfo *type_info,
+ gboolean *out_free_array)
{
GITypeInfo *item_type_info;
gboolean is_zero_terminated;
gsize item_size;
gssize length;
GArray *g_array;
+
+ g_return_val_if_fail (g_type_info_get_tag (type_info) == GI_TYPE_TAG_ARRAY, NULL);
if (arg->v_pointer == NULL) {
return NULL;
}
+
+ switch (g_type_info_get_array_type (type_info)) {
+ case GI_ARRAY_TYPE_C:
+ is_zero_terminated = g_type_info_is_zero_terminated (type_info);
+ item_type_info = g_type_info_get_param_type (type_info, 0);
- is_zero_terminated = g_type_info_is_zero_terminated (type_info);
- item_type_info = g_type_info_get_param_type (type_info, 0);
-
- item_size = _pygi_g_type_info_size (item_type_info);
+ item_size = _pygi_g_type_info_size (item_type_info);
- g_base_info_unref ( (GIBaseInfo *) item_type_info);
+ g_base_info_unref ( (GIBaseInfo *) item_type_info);
- if (is_zero_terminated) {
- length = g_strv_length (arg->v_pointer);
- } else {
- length = g_type_info_get_array_fixed_size (type_info);
- if (length < 0) {
- gint length_arg_pos;
+ if (is_zero_terminated) {
+ length = g_strv_length (arg->v_pointer);
+ } else {
+ length = g_type_info_get_array_fixed_size (type_info);
+ if (length < 0) {
+ if (G_UNLIKELY (args == NULL)) {
+ g_critical ("Unable to determine array length for %p",
+ arg->v_pointer);
+ g_array = g_array_new (is_zero_terminated, FALSE, item_size);
+ *out_free_array = TRUE;
+ return g_array;
+ }
+ gint length_arg_pos;
- length_arg_pos = g_type_info_get_array_length (type_info);
- g_assert (length_arg_pos >= 0);
+ length_arg_pos = g_type_info_get_array_length (type_info);
+ g_assert (length_arg_pos >= 0);
- /* FIXME: Take into account the type of the argument. */
- length = args[length_arg_pos]->v_int;
- }
- }
+ /* FIXME: Take into account the type of the length argument */
+ length = args[length_arg_pos]->v_int;
+ }
+ }
- g_assert (length >= 0);
+ g_assert (length >= 0);
- g_array = g_array_new (is_zero_terminated, FALSE, item_size);
+ g_array = g_array_new (is_zero_terminated, FALSE, item_size);
- g_array->data = arg->v_pointer;
- g_array->len = length;
+ g_array->data = arg->v_pointer;
+ g_array->len = length;
+ *out_free_array = TRUE;
+ break;
+ case GI_ARRAY_TYPE_ARRAY:
+ case GI_ARRAY_TYPE_BYTE_ARRAY:
+ /* Note: GByteArray is really just a GArray */
+ g_array = arg->v_pointer;
+ *out_free_array = FALSE;
+ break;
+ case GI_ARRAY_TYPE_PTR_ARRAY:
+ {
+ GPtrArray *ptr_array = (GPtrArray*) arg->v_pointer;
+ g_array = g_array_sized_new (FALSE, FALSE,
+ sizeof(gpointer),
+ ptr_array->len);
+ g_array->data = (char*) ptr_array->pdata;
+ g_array->len = ptr_array->len;
+ *out_free_array = TRUE;
+ break;
+ }
+ default:
+ g_critical ("Unexpected array type %u",
+ g_type_info_get_array_type (type_info));
+ g_array = NULL;
+ break;
+ }
return g_array;
}
@@ -1345,6 +1400,18 @@ hash_table_release:
return arg;
}
+/**
+ * _pygi_argument_to_object:
+ * @arg: The argument to convert to an object.
+ * @type_info: Type info for @arg
+ * @transfer:
+ *
+ * If the argument is of type array, it must be encoded in a GArray, by calling
+ * _pygi_argument_to_array(). This logic can not be folded into this method
+ * as determining array lengths may require access to method call arguments.
+ *
+ * Returns: A PyObject representing @arg
+ */
PyObject *
_pygi_argument_to_object (GIArgument *arg,
GITypeInfo *type_info,
@@ -1481,19 +1548,30 @@ _pygi_argument_to_object (GIArgument *arg,
}
case GI_TYPE_TAG_ARRAY:
{
+ /* Arrays are assumed to be packed in a GArray */
GArray *array;
GITypeInfo *item_type_info;
GITypeTag item_type_tag;
GITransfer item_transfer;
gsize i, item_size;
-
- array = arg->v_pointer;
-
+
+ if (arg->v_pointer == NULL)
+ return PyList_New (0);
+
item_type_info = g_type_info_get_param_type (type_info, 0);
g_assert (item_type_info != NULL);
item_type_tag = g_type_info_get_tag (item_type_info);
item_transfer = transfer == GI_TRANSFER_CONTAINER ? GI_TRANSFER_NOTHING : transfer;
+
+ array = arg->v_pointer;
+ item_size = g_array_get_element_size (array);
+
+ if (G_UNLIKELY (item_size > sizeof(GIArgument))) {
+ g_critical ("Stack overflow protection. "
+ "Can't copy array element into GIArgument.");
+ return PyList_New (0);
+ }
if (item_type_tag == GI_TYPE_TAG_UINT8) {
/* Return as a byte array */
@@ -1516,31 +1594,13 @@ _pygi_argument_to_object (GIArgument *arg,
break;
}
- }
- item_size = g_array_get_element_size (array);
+ }
for (i = 0; i < array->len; i++) {
- GIArgument item;
+ GIArgument item = { 0 };
PyObject *py_item;
- gboolean is_struct = FALSE;
-
- if (item_type_tag == GI_TYPE_TAG_INTERFACE) {
- GIBaseInfo *iface_info = g_type_info_get_interface (item_type_info);
- switch (g_base_info_get_type (iface_info)) {
- case GI_INFO_TYPE_STRUCT:
- case GI_INFO_TYPE_BOXED:
- is_struct = TRUE;
- default:
- break;
- }
- g_base_info_unref ( (GIBaseInfo *) iface_info);
- }
-
- if (is_struct) {
- item.v_pointer = &_g_array_index (array, GIArgument, i);
- } else {
- memcpy (&item, &_g_array_index (array, GIArgument, i), item_size);
- }
+
+ memcpy (&item, array->data + i * item_size, item_size);
py_item = _pygi_argument_to_object (&item, item_type_info, item_transfer);
if (py_item == NULL) {
diff --git a/gi/pygi-argument.h b/gi/pygi-argument.h
index 7224c75..668fabb 100644
--- a/gi/pygi-argument.h
+++ b/gi/pygi-argument.h
@@ -44,8 +44,8 @@ gint _pygi_g_registered_type_info_check_object (GIRegisteredTypeInfo *info,
GArray* _pygi_argument_to_array (GIArgument *arg,
GIArgument *args[],
- GITypeInfo *type_info,
- gboolean is_method);
+ GITypeInfo *type_info,
+ gboolean *out_free_array);
GIArgument _pygi_argument_from_object (PyObject *object,
GITypeInfo *type_info,
diff --git a/gi/pygi-closure.c b/gi/pygi-closure.c
index 241d91a..8c8e13d 100644
--- a/gi/pygi-closure.c
+++ b/gi/pygi-closure.c
@@ -185,10 +185,12 @@ _pygi_closure_convert_ffi_arguments (GICallableInfo *callable_info, void **args)
case GI_TYPE_TAG_GHASH:
case GI_TYPE_TAG_GLIST:
case GI_TYPE_TAG_GSLIST:
+ case GI_TYPE_TAG_ARRAY:
case GI_TYPE_TAG_VOID:
g_args[i].v_pointer = * (gpointer *) args[i];
break;
default:
+ g_warning ("Unhandled type tag %s", g_type_tag_to_string (tag));
g_args[i].v_pointer = 0;
}
}
@@ -235,6 +237,7 @@ _pygi_closure_convert_arguments (GICallableInfo *callable_info, void **args,
GITransfer transfer = g_arg_info_get_ownership_transfer (arg_info);
PyObject *value;
GIArgument *arg;
+ gboolean free_array = FALSE;
if (direction == GI_DIRECTION_IN && arg_tag == GI_TYPE_TAG_VOID &&
g_type_info_is_pointer (arg_type)) {
@@ -290,8 +293,16 @@ _pygi_closure_convert_arguments (GICallableInfo *callable_info, void **args,
arg = (GIArgument*) &g_args[i];
else
arg = (GIArgument*) g_args[i].v_pointer;
+
+ if (g_type_info_get_tag (arg_type) == GI_TYPE_TAG_ARRAY)
+ arg->v_pointer = _pygi_argument_to_array (arg, args,
+ arg_type, &free_array);
value = _pygi_argument_to_object (arg, arg_type, transfer);
+
+ if (free_array)
+ g_array_free (arg->v_pointer, FALSE);
+
if (value == NULL) {
g_base_info_unref (arg_type);
g_base_info_unref (arg_info);
diff --git a/gi/pygi-info.c b/gi/pygi-info.c
index f3fedab..8aafe8b 100644
--- a/gi/pygi-info.c
+++ b/gi/pygi-info.c
@@ -1147,6 +1147,7 @@ _wrap_g_constant_info_get_value (PyGIBaseInfo *self)
GITypeInfo *type_info;
GIArgument value;
PyObject *py_value;
+ gboolean free_array = FALSE;
if (g_constant_info_get_value ( (GIConstantInfo *) self->info, &value) < 0) {
PyErr_SetString (PyExc_RuntimeError, "unable to get value");
@@ -1155,7 +1156,16 @@ _wrap_g_constant_info_get_value (PyGIBaseInfo *self)
type_info = g_constant_info_get_type ( (GIConstantInfo *) self->info);
+ if (g_type_info_get_tag (type_info) == GI_TYPE_TAG_ARRAY) {
+ value.v_pointer = _pygi_argument_to_array (&value, NULL,
+ type_info, &free_array);
+ }
+
py_value = _pygi_argument_to_object (&value, type_info, GI_TRANSFER_NOTHING);
+
+ if (free_array) {
+ g_array_free (value.v_pointer, FALSE);
+ }
g_constant_info_free_value (self->info, &value);
g_base_info_unref ( (GIBaseInfo *) type_info);
@@ -1202,6 +1212,7 @@ _wrap_g_field_info_get_value (PyGIBaseInfo *self,
GITypeInfo *field_type_info;
GIArgument value;
PyObject *py_value = NULL;
+ gboolean free_array = FALSE;
memset(&value, 0, sizeof(GIArgument));
@@ -1278,18 +1289,15 @@ _wrap_g_field_info_get_value (PyGIBaseInfo *self,
goto out;
}
- if ( (g_type_info_get_tag (field_type_info) == GI_TYPE_TAG_ARRAY) &&
- (g_type_info_get_array_type (field_type_info) == GI_ARRAY_TYPE_C)) {
+ if (g_type_info_get_tag (field_type_info) == GI_TYPE_TAG_ARRAY) {
value.v_pointer = _pygi_argument_to_array (&value, NULL,
- field_type_info, FALSE);
+ field_type_info, &free_array);
}
argument_to_object:
py_value = _pygi_argument_to_object (&value, field_type_info, GI_TRANSFER_NOTHING);
- if ( (value.v_pointer != NULL) &&
- (g_type_info_get_tag (field_type_info) == GI_TYPE_TAG_ARRAY) &&
- (g_type_info_get_array_type (field_type_info) == GI_ARRAY_TYPE_C)) {
+ if (free_array) {
g_array_free (value.v_pointer, FALSE);
}
diff --git a/gi/pygi-property.c b/gi/pygi-property.c
index 56a9745..7c83eef 100644
--- a/gi/pygi-property.c
+++ b/gi/pygi-property.c
@@ -211,6 +211,7 @@ pygi_get_property_value_real (PyGObject *instance,
arg.v_pointer = g_value_get_boxed (&value);
break;
case GI_TYPE_TAG_GLIST:
+ case GI_TYPE_TAG_GSLIST:
arg.v_pointer = g_value_get_pointer (&value);
break;
case GI_TYPE_TAG_ARRAY:
diff --git a/gi/pygi-signal-closure.c b/gi/pygi-signal-closure.c
index 1482529..4e9dcb5 100644
--- a/gi/pygi-signal-closure.c
+++ b/gi/pygi-signal-closure.c
@@ -145,13 +145,25 @@ pygi_signal_closure_marshal(GClosure *closure,
GITransfer transfer;
GIArgument arg = { 0, };
PyObject *item = NULL;
+ gboolean free_array = FALSE;
g_callable_info_load_arg(signal_info, i - 1, &arg_info);
g_arg_info_load_type(&arg_info, &type_info);
transfer = g_arg_info_get_ownership_transfer(&arg_info);
arg = _pygi_argument_from_g_value(¶m_values[i], &type_info);
- item = _pygi_argument_to_object(&arg, &type_info, transfer);
+
+ if (g_type_info_get_tag (&type_info) == GI_TYPE_TAG_ARRAY) {
+ arg.v_pointer = _pygi_argument_to_array (&arg, NULL,
+ &type_info, &free_array);
+ }
+
+ item = _pygi_argument_to_object (&arg, &type_info, transfer);
+
+ if (free_array) {
+ g_array_free (arg.v_pointer, FALSE);
+ }
+
if (item == NULL) {
goto out;
diff --git a/tests/test_gi.py b/tests/test_gi.py
index ce988c0..428e7fd 100644
--- a/tests/test_gi.py
+++ b/tests/test_gi.py
@@ -1643,6 +1643,16 @@ class TestPythonGObject(unittest.TestCase):
def do_method_with_default_implementation(self, int8):
self.val = int8
+ class Interface3Impl(GObject.Object, GIMarshallingTests.Interface3):
+ def __init__(self):
+ GObject.Object.__init__(self)
+ self.variants = None
+ self.n_variants = None
+
+ def do_test_variant_array_in(self, variants, n_variants):
+ self.variants = variants
+ self.n_variants = n_variants
+
def test_object(self):
self.assertTrue(issubclass(self.Object, GIMarshallingTests.Object))
@@ -1732,6 +1742,14 @@ class TestPythonGObject(unittest.TestCase):
GIMarshallingTests.SubSubObject.do_method_deep_hierarchy(sub_sub_sub_object, 5)
self.assertEqual(sub_sub_sub_object.props.int, 5)
+ def test_interface3impl(self):
+ iface3 = self.Interface3Impl()
+ variants = [GLib.Variant('i', 27), GLib.Variant('s', 'Hello')]
+ iface3.test_variant_array_in(variants)
+ self.assertEqual(iface3.n_variants, 2)
+ self.assertEqual(iface3.variants[0].unpack(), 27)
+ self.assertEqual(iface3.variants[1].unpack(), 'Hello')
+
def test_python_subsubobject_vfunc(self):
class PySubObject(GIMarshallingTests.Object):
def __init__(self):
[
Date Prev][
Date Next] [
Thread Prev][
Thread Next]
[
Thread Index]
[
Date Index]
[
Author Index]