diff options
author | Simon Feltman <sfeltman@src.gnome.org> | 2013-12-25 04:17:50 -0800 |
---|---|---|
committer | Simon Feltman <sfeltman@src.gnome.org> | 2014-01-04 13:48:27 -0800 |
commit | 9d8d159c28a482de6bdaa06f3805f65d3c55e958 (patch) | |
tree | 6a0050bae709f575b851d76710e6455cc32cc6cc /giscanner | |
parent | 795e4bf1f3dc24380964367714ebaa76588cbf67 (diff) | |
download | gobject-introspection-9d8d159c28a482de6bdaa06f3805f65d3c55e958.tar.gz |
scanner: Fix parsing for various typedef struct orderings
Add structs parsed from C's "tag namespace" into their own cache for lookup
by typdef parsing. This fixes issues where a typedef declared after a
struct would not have a correct name. This also cleans up the need for
special casing struct tags prefixed with an underscore.
https://bugzilla.gnome.org/show_bug.cgi?id=581525
Diffstat (limited to 'giscanner')
-rw-r--r-- | giscanner/ast.py | 16 | ||||
-rw-r--r-- | giscanner/transformer.py | 100 |
2 files changed, 106 insertions, 10 deletions
diff --git a/giscanner/ast.py b/giscanner/ast.py index bd536c63..b5b2ad71 100644 --- a/giscanner/ast.py +++ b/giscanner/ast.py @@ -859,7 +859,8 @@ class Compound(Node, Registered): gtype_name=None, get_type=None, c_symbol_prefix=None, - disguised=False): + disguised=False, + tag_name=None): Node.__init__(self, name) Registered.__init__(self, gtype_name, get_type) self.ctype = ctype @@ -871,6 +872,7 @@ class Compound(Node, Registered): self.gtype_name = gtype_name self.get_type = get_type self.c_symbol_prefix = c_symbol_prefix + self.tag_name = tag_name def add_gtype(self, gtype_name, get_type): self.gtype_name = gtype_name @@ -930,13 +932,15 @@ class Record(Compound): gtype_name=None, get_type=None, c_symbol_prefix=None, - disguised=False): + disguised=False, + tag_name=None): Compound.__init__(self, name, ctype=ctype, gtype_name=gtype_name, get_type=get_type, c_symbol_prefix=c_symbol_prefix, - disguised=disguised) + disguised=disguised, + tag_name=tag_name) # If non-None, this record defines the FooClass C structure # for some Foo GObject (or similar for GInterface) self.is_gtype_struct_for = None @@ -949,13 +953,15 @@ class Union(Compound): gtype_name=None, get_type=None, c_symbol_prefix=None, - disguised=False): + disguised=False, + tag_name=None): Compound.__init__(self, name, ctype=ctype, gtype_name=gtype_name, get_type=get_type, c_symbol_prefix=c_symbol_prefix, - disguised=disguised) + disguised=disguised, + tag_name=tag_name) class Boxed(Node, Registered): 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: |