[pygi] support for caller-allocates annotations for structs



commit 1e42ee6eb25a07a5201f24ffeac18d298a98477e
Author: John (J5) Palmieri <johnp redhat com>
Date:   Fri May 28 10:03:11 2010 -0400

    support for caller-allocates annotations for structs
    
    * out caller-allocates parameters expect an already constructed structure
      to be passed in by reference.  It is then modified and the caller uses the
      modified value.  We support this by using only one level of pointer
      indirection.
    * Only structs are considered to be caller-allocates parameters even if
      they are marked as such by GI.  This is because the GI scanner isn't smart
      enough to correctly guess 100% of the time
    * GValues are a special case of a caller-allocates parameter when cleaning
      up (e.g. g_value_unset is called).  GValues make no sense in a scripting
      language.  Developers should never deal with them.
    
    https://bugzilla.gnome.org/show_bug.cgi?id=620406

 gi/pygi-invoke.c         |   73 +++++++++++++++++++++++++++++++++++++++++++--
 tests/test_everything.py |   28 +++++++++++++++++
 2 files changed, 97 insertions(+), 4 deletions(-)
---
diff --git a/gi/pygi-invoke.c b/gi/pygi-invoke.c
index bf220e7..7ac1685 100644
--- a/gi/pygi-invoke.c
+++ b/gi/pygi-invoke.c
@@ -135,7 +135,7 @@ _prepare_invocation_state (struct invocation_state *state,
     }
 
     /* We do a first (well, second) pass here over the function to scan for special cases.
-     * This is currently array+length combinations and GError.
+     * This is currently array+length combinations, GError and GValue.
      */
     for (i = 0; i < state->n_args; i++) {
         GIDirection direction;
@@ -298,8 +298,11 @@ _prepare_invocation_state (struct invocation_state *state,
 
         for (i = 0; i < state->n_args; i++) {
             GIDirection direction;
+            GIBaseInfo *info;
+            gboolean is_caller_allocates;
 
             direction = g_arg_info_get_direction (state->arg_infos[i]);
+            is_caller_allocates = g_arg_info_is_caller_allocates (state->arg_infos[i]);
 
             switch (direction) {
                 case GI_DIRECTION_IN:
@@ -310,13 +313,51 @@ _prepare_invocation_state (struct invocation_state *state,
                 case GI_DIRECTION_INOUT:
                     g_assert (in_args_pos < state->n_in_args);
                     g_assert (out_args_pos < state->n_out_args);
+
                     state->in_args[in_args_pos].v_pointer = &state->out_values[out_args_pos];
                     in_args_pos += 1;
                 case GI_DIRECTION_OUT:
                     g_assert (out_args_pos < state->n_out_args);
-                    state->out_args[out_args_pos].v_pointer = &state->out_values[out_args_pos];
-                    state->out_values[out_args_pos].v_pointer = NULL;
-                    state->args[i] = &state->out_values[out_args_pos];
+
+                    /* caller allocates only applies to structures but GI has
+                     * no way to denote that yet, so we only use caller allocates
+                     * if we see  a structure
+                     */
+                    if (is_caller_allocates) {
+                        GITypeTag type_tag;
+
+                        is_caller_allocates = FALSE;
+                        type_tag = g_type_info_get_tag (state->arg_type_infos[i]);
+
+                        if (type_tag  == GI_TYPE_TAG_INTERFACE) {
+                            GIInfoType info_type;
+
+                            info = g_type_info_get_interface (state->arg_type_infos[i]);
+                            g_assert (info != NULL);
+                            info_type = g_base_info_get_type (info);
+
+                            if (info_type == GI_INFO_TYPE_STRUCT)
+                                is_caller_allocates = TRUE;
+                        }
+                    }
+
+                    if (is_caller_allocates) {
+                        gsize size;
+                        gpointer value;
+
+                        /* if caller allocates only use one level of indirection */
+                        state->out_args[out_args_pos].v_pointer = NULL;
+                        state->args[i] = &state->out_args[out_args_pos];
+
+                        size = g_struct_info_get_size ( (GIStructInfo *) info);
+
+                        state->args[i]->v_pointer = g_malloc0 (size);
+                    } else {
+                        state->out_args[out_args_pos].v_pointer = &state->out_values[out_args_pos];
+                        state->out_values[out_args_pos].v_pointer = NULL;
+                        state->args[i] = &state->out_values[out_args_pos];
+                    }
+
                     out_args_pos += 1;
             }
         }
@@ -851,6 +892,30 @@ _free_invocation_state (struct invocation_state *state)
     }
 
     for (i = 0; i < state->n_args; i++) {
+
+        /* check for caller-allocated values we need to free */
+        if (g_arg_info_is_caller_allocates (state->arg_infos[i])) {
+            GIBaseInfo *info;
+            GIInfoType info_type;
+
+            info = g_type_info_get_interface (state->arg_type_infos[i]);
+            g_assert (info != NULL);
+            info_type = g_base_info_get_type (info);
+
+            /* caller-allocates applies only to structs right now
+             * the GI scanner is overzealous when marking parameters
+             * as caller-allocates, so we only free if this was a struct
+             */
+            if (info_type == GI_INFO_TYPE_STRUCT) {
+                /* special case GValues so we make sure to unset them */
+                if (g_registered_type_info_get_g_type ( (GIRegisteredTypeInfo *) info) == G_TYPE_VALUE) {
+                    g_value_unset ( (GValue *) state->args[i]);
+                }
+
+                g_free (state->args[i]);
+            }
+        }
+
         if (state->arg_type_infos[i] != NULL)
             g_base_info_unref ( (GIBaseInfo *) state->arg_type_infos[i]);
         if (state->arg_infos[i] != NULL)
diff --git a/tests/test_everything.py b/tests/test_everything.py
index 41bc765..0463d2c 100644
--- a/tests/test_everything.py
+++ b/tests/test_everything.py
@@ -50,6 +50,34 @@ class TestEverything(unittest.TestCase):
     def test_floating(self):
         Everything.TestFloating()
 
+    def test_caller_allocates(self):
+        struct_a = Everything.TestStructA()
+        struct_a.some_int = 10
+        struct_a.some_int8 = 21
+        struct_a.some_double = 3.14
+        struct_a.some_enum = Everything.TestEnum.VALUE3
+
+        struct_a_clone = struct_a.clone()
+        self.assertTrue(struct_a != struct_a_clone)
+        self.assertEquals(struct_a.some_int, struct_a_clone.some_int)
+        self.assertEquals(struct_a.some_int8, struct_a_clone.some_int8)
+        self.assertEquals(struct_a.some_double, struct_a_clone.some_double)
+        self.assertEquals(struct_a.some_enum, struct_a_clone.some_enum)
+
+        struct_b = Everything.TestStructB()
+        struct_b.some_int8 = 8
+        struct_b.nested_a.some_int = 20
+        struct_b.nested_a.some_int8 = 12
+        struct_b.nested_a.some_double = 333.3333
+        struct_b.nested_a.some_enum = Everything.TestEnum.VALUE2
+
+        struct_b_clone = struct_b.clone()
+        self.assertTrue(struct_b != struct_b_clone)
+        self.assertEquals(struct_b.some_int8, struct_b_clone.some_int8)
+        self.assertEquals(struct_b.nested_a.some_int, struct_b_clone.nested_a.some_int)
+        self.assertEquals(struct_b.nested_a.some_int8, struct_b_clone.nested_a.some_int8)
+        self.assertEquals(struct_b.nested_a.some_double, struct_b_clone.nested_a.some_double)
+        self.assertEquals(struct_b.nested_a.some_enum, struct_b_clone.nested_a.some_enum)
 
 class TestNullableArgs(unittest.TestCase):
     def test_in_nullable_hash(self):



[Date Prev][Date Next]   [Thread Prev][Thread Next]   [Thread Index] [Date Index] [Author Index]