diff options
author | Torsten Schönfeld <kaffeetisch@gmx.de> | 2011-09-10 16:52:51 +0200 |
---|---|---|
committer | Torsten Schönfeld <kaffeetisch@gmx.de> | 2011-09-11 00:34:28 +0200 |
commit | ea68ec234ad31b0a1d92adbf0c911a5dd541b647 (patch) | |
tree | 125930f400320b4bddfa7948396a10915f83c1b5 | |
parent | c3da3d46ffa9c3b53e7f7132eaa9c9c722ebc859 (diff) | |
download | gobject-introspection-ea68ec234ad31b0a1d92adbf0c911a5dd541b647.tar.gz |
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
-rw-r--r-- | giscanner/transformer.py | 54 | ||||
-rw-r--r-- | tests/repository/Makefile.am | 2 | ||||
-rw-r--r-- | tests/repository/gitypelibtest.c | 26 | ||||
-rw-r--r-- | tests/scanner/Regress-1.0-expected.gir | 39 | ||||
-rw-r--r-- | tests/scanner/regress.h | 17 |
5 files changed, 126 insertions, 12 deletions
diff --git a/giscanner/transformer.py b/giscanner/transformer.py index 4ce2ac0e..d3a056b3 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 ffc635fe..6331cd30 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 2896846a..6e69b096 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 2f4b5c21..79ca1d31 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 f30fcd78..4afb9b06 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; |