[gobject-introspection] scanner: correctly handle structs with arrays of anon unions



commit ea68ec234ad31b0a1d92adbf0c911a5dd541b647
Author: Torsten SchÃnfeld <kaffeetisch gmx de>
Date:   Sat Sep 10 16:52:51 2011 +0200

    scanner: correctly handle structs with arrays of anon unions
    
    This applies mainly to GValue, which is defined as:
    
      struct _GValue
      {
        /*< private >*/
        GType		g_type;
    
        /* public for GTypeValueTable methods */
        union {
          gint	v_int;
          guint	v_uint;
          glong	v_long;
          gulong	v_ulong;
          gint64      v_int64;
          guint64     v_uint64;
          gfloat	v_float;
          gdouble	v_double;
          gpointer	v_pointer;
        } data[2];
      };
    
    Previously, the scanner did not understand the array of unions.  This
    resulted in g_struct_info_get_size returning an incorrect size for
    GValue (at least on 32bit systems).
    
    Fix this by making up a separate union declaration for the GIR that can
    be referenced by the array.
    
    https://bugzilla.gnome.org/show_bug.cgi?id=657040

 giscanner/transformer.py               |   54 +++++++++++++++++++++++++------
 tests/repository/Makefile.am           |    2 +-
 tests/repository/gitypelibtest.c       |   26 +++++++++++++++
 tests/scanner/Regress-1.0-expected.gir |   39 +++++++++++++++++++++++
 tests/scanner/regress.h                |   17 ++++++++++
 5 files changed, 126 insertions(+), 12 deletions(-)
---
diff --git a/giscanner/transformer.py b/giscanner/transformer.py
index 4ce2ac0..d3a056b 100644
--- a/giscanner/transformer.py
+++ b/giscanner/transformer.py
@@ -315,7 +315,7 @@ raise ValueError."""
             return '_' + name
         return name
 
-    def _traverse_one(self, symbol, stype=None):
+    def _traverse_one(self, symbol, stype=None, parent_symbol=None):
         assert isinstance(symbol, SourceSymbol), symbol
 
         if stype is None:
@@ -329,7 +329,7 @@ raise ValueError."""
         elif stype == CSYMBOL_TYPE_ENUM:
             return self._create_enum(symbol)
         elif stype == CSYMBOL_TYPE_MEMBER:
-            return self._create_member(symbol)
+            return self._create_member(symbol, parent_symbol)
         elif stype == CSYMBOL_TYPE_UNION:
             return self._create_union(symbol)
         elif stype == CSYMBOL_TYPE_CONST:
@@ -442,7 +442,29 @@ raise ValueError."""
         for child in base_type.child_list:
             yield self._create_parameter(child)
 
-    def _create_member(self, symbol):
+    def _synthesize_union_type(self, symbol, parent_symbol):
+        # Synthesize a named union so that it can be referenced.
+        parent_ident = parent_symbol.ident
+        # FIXME: Should split_ctype_namespaces handle the hidden case?
+        hidden = parent_ident.startswith('_')
+        if hidden:
+            parent_ident = parent_ident[1:]
+        matches = self.split_ctype_namespaces(parent_ident)
+        (namespace, parent_name) = matches[-1]
+        assert namespace and parent_name
+        if hidden:
+            parent_name = '_' + parent_name
+        fake_union = ast.Union("%s__%s__union" % (parent_name, symbol.ident))
+        # _parse_fields accesses <type>.base_type.child_list, so we have to
+        # pass symbol.base_type even though that refers to the array, not the
+        # union.
+        self._parse_fields(symbol.base_type, fake_union)
+        self._append_new_node(fake_union)
+        fake_type = ast.Type(
+            target_giname="%s.%s" % (namespace.name, fake_union.name))
+        return fake_type
+
+    def _create_member(self, symbol, parent_symbol=None):
         source_type = symbol.base_type
         if (source_type.type == CTYPE_POINTER and
             symbol.base_type.base_type.type == CTYPE_FUNCTION):
@@ -455,14 +477,24 @@ raise ValueError."""
             # Special handling for fields; we don't have annotations on them
             # to apply later, yet.
             if source_type.type == CTYPE_ARRAY:
-                ctype = self._create_source_type(source_type)
-                canonical_ctype = self._canonicalize_ctype(ctype)
-                if canonical_ctype[-1] == '*':
-                    derefed_name = canonical_ctype[:-1]
+                # If the array contains anonymous unions, like in the GValue
+                # struct, we need to handle this specially.  This is necessary
+                # to be able to properly calculate the size of the compound
+                # type (e.g. GValue) that contains this array, see
+                # <https://bugzilla.gnome.org/show_bug.cgi?id=657040>.
+                if (source_type.base_type.type == CTYPE_UNION and
+                    source_type.base_type.name is None):
+                    synthesized_type = self._synthesize_union_type(symbol, parent_symbol)
+                    ftype = ast.Array(None, synthesized_type)
                 else:
-                    derefed_name = canonical_ctype
-                ftype = ast.Array(None, self.create_type_from_ctype_string(ctype),
-                                  ctype=derefed_name)
+                    ctype = self._create_source_type(source_type)
+                    canonical_ctype = self._canonicalize_ctype(ctype)
+                    if canonical_ctype[-1] == '*':
+                        derefed_name = canonical_ctype[:-1]
+                    else:
+                        derefed_name = canonical_ctype
+                    ftype = ast.Array(None, self.create_type_from_ctype_string(ctype),
+                                      ctype=derefed_name)
                 child_list = list(symbol.base_type.child_list)
                 ftype.zeroterminated = False
                 if child_list:
@@ -677,7 +709,7 @@ raise ValueError."""
 
     def _parse_fields(self, symbol, compound):
         for child in symbol.base_type.child_list:
-            child_node = self._traverse_one(child)
+            child_node = self._traverse_one(child, parent_symbol=symbol)
             if not child_node:
                 continue
             if isinstance(child_node, ast.Field):
diff --git a/tests/repository/Makefile.am b/tests/repository/Makefile.am
index ffc635f..6331cd3 100644
--- a/tests/repository/Makefile.am
+++ b/tests/repository/Makefile.am
@@ -17,5 +17,5 @@ gitypelibtest_CPPFLAGS = $(GIREPO_CFLAGS) -I$(top_srcdir)/girepository
 gitypelibtest_LDADD = $(top_builddir)/libgirepository-1.0.la $(GIREPO_LIBS)
 
 TESTS = gitestrepo gitestthrows gitypelibtest
-TESTS_ENVIRONMENT=env GI_TYPELIB_PATH=$(top_builddir):$(top_builddir)/tests \
+TESTS_ENVIRONMENT=env GI_TYPELIB_PATH=$(top_builddir):$(top_builddir)/tests:$(top_builddir)/tests/scanner \
    XDG_DATA_DIRS="$(top_srcdir)/gir:$(XDG_DATA_DIRS)" $(DEBUG)
diff --git a/tests/repository/gitypelibtest.c b/tests/repository/gitypelibtest.c
index 2896846..6e69b09 100644
--- a/tests/repository/gitypelibtest.c
+++ b/tests/repository/gitypelibtest.c
@@ -106,6 +106,31 @@ test_enum_and_flags_static_methods(GIRepository *repo)
     g_base_info_unref (enum_info);
 }
 
+static void
+test_size_of_struct_with_array_of_anon_unions(GIRepository *repo)
+{
+    GITypelib *ret;
+    GError *error = NULL;
+    GIBaseInfo *struct_info;
+
+    ret = g_irepository_require (repo, "Regress", NULL, 0, &error);
+    if (!ret)
+        g_error ("%s", error->message);
+
+    struct_info = g_irepository_find_by_name (repo, "Regress", "TestStructE");
+    if (!struct_info)
+        g_error ("Could not find Regress.TestStructE");
+    g_assert (g_struct_info_get_size (struct_info)
+                == sizeof (GType) + 2*sizeof (gint64));
+    g_base_info_unref (struct_info);
+
+    struct_info = g_irepository_find_by_name (repo, "GObject", "Value");
+    if (!struct_info)
+        g_error ("Could not find GObject.Value");
+    g_assert (g_struct_info_get_size (struct_info) == sizeof (GValue));
+    g_base_info_unref (struct_info);
+}
+
 int
 main(int argc, char **argv)
 {
@@ -118,6 +143,7 @@ main(int argc, char **argv)
     /* do tests */
     test_enum_and_flags_cidentifier (repo);
     test_enum_and_flags_static_methods (repo);
+    test_size_of_struct_with_array_of_anon_unions (repo);
 
     exit(0);
 }
diff --git a/tests/scanner/Regress-1.0-expected.gir b/tests/scanner/Regress-1.0-expected.gir
index 2f4b5c2..79ca1d3 100644
--- a/tests/scanner/Regress-1.0-expected.gir
+++ b/tests/scanner/Regress-1.0-expected.gir
@@ -1162,6 +1162,45 @@ TpAccount::status-changed</doc>
         </array>
       </field>
     </record>
+    <record name="TestStructE" c:type="RegressTestStructE">
+      <field name="some_type" writable="1">
+        <type name="GType" c:type="GType"/>
+      </field>
+      <field name="some_union" writable="1">
+        <array zero-terminated="0" fixed-size="2">
+          <type name="TestStructE__some_union__union"/>
+        </array>
+      </field>
+    </record>
+    <union name="TestStructE__some_union__union">
+      <field name="v_int" writable="1">
+        <type name="gint" c:type="gint"/>
+      </field>
+      <field name="v_uint" writable="1">
+        <type name="guint" c:type="guint"/>
+      </field>
+      <field name="v_long" writable="1">
+        <type name="glong" c:type="glong"/>
+      </field>
+      <field name="v_ulong" writable="1">
+        <type name="gulong" c:type="gulong"/>
+      </field>
+      <field name="v_int64" writable="1">
+        <type name="gint64" c:type="gint64"/>
+      </field>
+      <field name="v_uint64" writable="1">
+        <type name="guint64" c:type="guint64"/>
+      </field>
+      <field name="v_float" writable="1">
+        <type name="gfloat" c:type="gfloat"/>
+      </field>
+      <field name="v_double" writable="1">
+        <type name="gdouble" c:type="gdouble"/>
+      </field>
+      <field name="v_pointer" writable="1">
+        <type name="gpointer" c:type="gpointer"/>
+      </field>
+    </union>
     <record name="TestStructFixedArray" c:type="RegressTestStructFixedArray">
       <field name="just_int" writable="1">
         <type name="gint" c:type="gint"/>
diff --git a/tests/scanner/regress.h b/tests/scanner/regress.h
index f30fcd7..4afb9b0 100644
--- a/tests/scanner/regress.h
+++ b/tests/scanner/regress.h
@@ -243,6 +243,23 @@ struct _RegressTestStructD
   GPtrArray           *garray;
 };
 
+/* This one has an array of anonymous unions, inspired by GValue */
+struct RegressTestStructE
+{
+  GType some_type;
+  union {
+    gint	v_int;
+    guint	v_uint;
+    glong	v_long;
+    gulong	v_ulong;
+    gint64      v_int64;
+    guint64     v_uint64;
+    gfloat	v_float;
+    gdouble	v_double;
+    gpointer	v_pointer;
+  } some_union[2];
+};
+
 /* plain-old-data boxed types */
 typedef struct _RegressTestSimpleBoxedA RegressTestSimpleBoxedA;
 typedef struct _RegressTestSimpleBoxedB RegressTestSimpleBoxedB;



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