summaryrefslogtreecommitdiff
path: root/giscanner/transformer.py
diff options
context:
space:
mode:
Diffstat (limited to 'giscanner/transformer.py')
-rw-r--r--giscanner/transformer.py100
1 files changed, 95 insertions, 5 deletions
diff --git a/giscanner/transformer.py b/giscanner/transformer.py
index 156148f4..00128064 100644
--- a/giscanner/transformer.py
+++ b/giscanner/transformer.py
@@ -59,6 +59,11 @@ class Transformer(object):
self._includepaths = []
self._passthrough_mode = False
+ # Cache a list of struct/unions in C's "tag namespace". This helps
+ # manage various orderings of typedefs and structs. See:
+ # https://bugzilla.gnome.org/show_bug.cgi?id=581525
+ self._tag_ns = {}
+
def get_pkgconfig_packages(self):
return self._pkg_config_packages
@@ -76,6 +81,12 @@ class Transformer(object):
# modules will just depend on that.
if isinstance(original, ast.Constant) and isinstance(node, ast.Constant):
pass
+ elif original is node:
+ # Ignore attempts to add the same node to the namespace. This can
+ # happen when parsing typedefs and structs in particular orderings:
+ # typedef struct _Foo Foo;
+ # struct _Foo {...};
+ pass
elif original:
positions = set()
positions.update(original.file_positions)
@@ -92,8 +103,11 @@ class Transformer(object):
if symbol.ident in ['gst_g_error_get_type']:
continue
node = self._traverse_one(symbol)
- if node:
+ if node and node.name:
self._append_new_node(node)
+ if isinstance(node, ast.Compound) and node.tag_name and \
+ node.tag_name not in self._tag_ns:
+ self._tag_ns[node.tag_name] = node
# Now look through the namespace for things like
# typedef struct _Foo Foo;
@@ -114,6 +128,18 @@ class Transformer(object):
ns_compound.fields = compound.fields
self._typedefs_ns = None
+ # Run through the tag namespace looking for structs that have not been
+ # promoted into the main namespace. In this case we simply promote them
+ # with their struct tag.
+ for tag_name, struct in self._tag_ns.iteritems():
+ if not struct.name:
+ try:
+ name = self.strip_identifier(tag_name)
+ struct.name = name
+ self._append_new_node(struct)
+ except TransformerException as e:
+ message.warn_node(node, e)
+
def set_include_paths(self, paths):
self._includepaths = list(paths)
@@ -323,7 +349,7 @@ raise ValueError."""
elif stype == CSYMBOL_TYPE_TYPEDEF:
return self._create_typedef(symbol)
elif stype == CSYMBOL_TYPE_STRUCT:
- return self._create_struct(symbol)
+ return self._create_tag_ns_struct(symbol)
elif stype == CSYMBOL_TYPE_ENUM:
return self._create_enum(symbol)
elif stype == CSYMBOL_TYPE_MEMBER:
@@ -748,11 +774,75 @@ raise ValueError."""
except TransformerException as e:
message.warn_symbol(symbol, e)
return None
- struct = ast.Record(name, symbol.ident, disguised=disguised)
+
+ assert symbol.base_type
+ if symbol.base_type.name:
+ tag_name = symbol.base_type.name
+ else:
+ tag_name = None
+
+ # If the struct already exists in the tag namespace, use it.
+ if tag_name in self._tag_ns:
+ struct = self._tag_ns[tag_name]
+ if struct.name:
+ # If the struct name is set it means the struct has already been
+ # promoted from the tag namespace to the main namespace by a
+ # prior typedef struct. If we get here it means this is another
+ # typedef of that struct. Instead of creating an alias to the
+ # primary typedef that has been promoted, we create a new Record
+ # which is forced as a disguised struct. This handles the case
+ # where we want to give GInitiallyUnowned its own Record:
+ # typedef struct _GObject GObject;
+ # typedef struct _GObject GInitiallyUnowned;
+ # GInitiallyUnowned is also special cased in gdumpparser.py to
+ # copy fields which may eventually be avoided by doing it here
+ # generically.
+ struct = ast.Record(name, symbol.ident, disguised=True, tag_name=tag_name)
+ else:
+ # If the struct does not have its name set, it exists only in
+ # the tag namespace. Set it here and return it which will
+ # promote it to the main namespace. Essentially the first
+ # typedef for a struct clobbers its name and ctype which is what
+ # will be visible to GI.
+ struct.name = name
+ struct.ctype = symbol.ident
+ else:
+ # Create a new struct with a typedef name and tag name when available.
+ # Structs with a typedef name are promoted into the main namespace
+ # by it being returned to the "parse" function and are also added to
+ # the tag namespace if it has a tag_name set.
+ struct = ast.Record(name, symbol.ident, disguised=disguised, tag_name=tag_name)
+ if tag_name:
+ # Force the struct as disguised for now since we do not yet know
+ # if it has fields that will be parsed. Note that this is using
+ # an erroneous definition of disguised and we should eventually
+ # only look at the field count when needed.
+ struct.disguised = True
+ else:
+ # Case where we have an anonymous struct which is typedef'd:
+ # typedef struct {...} Struct;
+ # we need to parse the fields because we never get a struct
+ # in the tag namespace which is normally where fields are parsed.
+ self._parse_fields(symbol, struct)
+
+ struct.add_symbol_reference(symbol)
+ return struct
+
+ def _create_tag_ns_struct(self, symbol):
+ # Get or create a struct from C's tag namespace
+ if symbol.ident in self._tag_ns:
+ struct = self._tag_ns[symbol.ident]
+ else:
+ struct = ast.Record(None, symbol.ident, tag_name=symbol.ident)
+
+ # Make sure disguised is False as we are now about to parse the
+ # fields of the real struct.
+ struct.disguised = False
+ # Fields may need to be parsed in either of the above cases because the
+ # Record can be created with a typedef prior to the struct definition.
self._parse_fields(symbol, struct)
struct.add_symbol_reference(symbol)
- self._typedefs_ns[symbol.ident] = struct
- return None
+ return struct
def _create_typedef_union(self, symbol):
try: