74985ffebe455a4d07a7999b44b7aa1c07c112dc
[platform/upstream/gobject-introspection.git] / giscanner / transformer.py
1 # -*- Mode: Python -*-
2 # GObject-Introspection - a framework for introspecting GObject libraries
3 # Copyright (C) 2008  Johan Dahlin
4 #
5 # This library is free software; you can redistribute it and/or
6 # modify it under the terms of the GNU Lesser General Public
7 # License as published by the Free Software Foundation; either
8 # version 2 of the License, or (at your option) any later version.
9 #
10 # This library is distributed in the hope that it will be useful,
11 # but WITHOUT ANY WARRANTY; without even the implied warranty of
12 # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
13 # Lesser General Public License for more details.
14 #
15 # You should have received a copy of the GNU Lesser General Public
16 # License along with this library; if not, write to the
17 # Free Software Foundation, Inc., 59 Temple Place - Suite 330,
18 # Boston, MA 02111-1307, USA.
19 #
20
21 import os
22 import sys
23
24 from . import ast
25 from . import message
26 from .cachestore import CacheStore
27 from .girparser import GIRParser
28 from .sourcescanner import (
29     SourceSymbol, ctype_name, CTYPE_POINTER,
30     CTYPE_BASIC_TYPE, CTYPE_UNION, CTYPE_ARRAY, CTYPE_TYPEDEF,
31     CTYPE_VOID, CTYPE_ENUM, CTYPE_FUNCTION, CTYPE_STRUCT,
32     CSYMBOL_TYPE_FUNCTION, CSYMBOL_TYPE_TYPEDEF, CSYMBOL_TYPE_STRUCT,
33     CSYMBOL_TYPE_ENUM, CSYMBOL_TYPE_UNION, CSYMBOL_TYPE_OBJECT,
34     CSYMBOL_TYPE_MEMBER, CSYMBOL_TYPE_ELLIPSIS, CSYMBOL_TYPE_CONST,
35     TYPE_QUALIFIER_CONST)
36
37 class TransformerException(Exception):
38     pass
39
40
41 _xdg_data_dirs = [x for x in os.environ.get('XDG_DATA_DIRS', '').split(':') \
42                       + [DATADIR, '/usr/share'] if x]
43
44 class Transformer(object):
45     namespace = property(lambda self: self._namespace)
46
47     def __init__(self, namespace, accept_unprefixed=False):
48         self._cachestore = CacheStore()
49         self._accept_unprefixed = accept_unprefixed
50         self._namespace = namespace
51         self._pkg_config_packages = set()
52         self._typedefs_ns = {}
53         self._includes = {} # <string namespace -> Namespace>
54         self._include_names = set() # string namespace
55         self._includepaths = []
56         self._passthrough_mode = False
57         self._annotations = {}
58
59     def get_includes(self):
60         return self._include_names
61
62     def get_pkgconfig_packages(self):
63         return self._pkg_config_packages
64
65     def disable_cache(self):
66         self._cachestore = None
67
68     def set_passthrough_mode(self):
69         self._passthrough_mode = True
70
71     def set_annotations(self, annotations):
72         self._annotations = annotations
73
74     def _append_new_node(self, node):
75         original = self._namespace.get(node.name)
76         # Special case constants here; we allow duplication to sort-of
77         # handle #ifdef.  But this introduces an arch-dependency in the .gir
78         # file.  So far this has only come up scanning glib - in theory, other
79         # modules will just depend on that.
80         if isinstance(original, ast.Constant) and isinstance(node, ast.Constant):
81             pass
82         elif original:
83             positions = set()
84             positions.update(original.file_positions)
85             positions.update(node.file_positions)
86             message.fatal("Namespace conflict for '%s'" % (node.name, ),
87                           positions)
88         else:
89             self._namespace.append(node)
90
91     def parse(self, symbols):
92         for symbol in symbols:
93             ## WORKAROUND ##
94             # https://bugzilla.gnome.org/show_bug.cgi?id=550616
95             if symbol.ident in ['gst_g_error_get_type']:
96                 continue
97             node = self._traverse_one(symbol)
98             if node:
99                 self._append_new_node(node)
100
101         # Now look through the namespace for things like
102         # typedef struct _Foo Foo;
103         # where we've never seen the struct _Foo.  Just create
104         # an empty structure for these as "disguised"
105         # If we do have a class/interface, merge fields
106         for typedef, compound in self._typedefs_ns.iteritems():
107             ns_compound = self._namespace.get(compound.name)
108             if not ns_compound:
109                 ns_compound = self._namespace.get('_' + compound.name)
110             if (not ns_compound and isinstance(compound, (ast.Record, ast.Union))
111                 and len(compound.fields) == 0):
112                 disguised = ast.Record(compound.name, typedef, disguised=True)
113                 self._namespace.append(disguised)
114             elif not ns_compound:
115                 self._namespace.append(compound)
116             elif isinstance(ns_compound, (ast.Record, ast.Union)) and len(ns_compound.fields) == 0:
117                 ns_compound.fields = compound.fields
118         self._typedefs_ns = None
119
120     def set_include_paths(self, paths):
121         self._includepaths = list(paths)
122
123     def register_include(self, include):
124         if include in self._include_names:
125             return
126         filename = self._find_include(include)
127         self._parse_include(filename)
128         self._include_names.add(include)
129
130     def register_include_uninstalled(self, include_path):
131         basename = os.path.basename(include_path)
132         if not basename.endswith('.gir'):
133             raise SystemExit(
134 "Include path %r must be a filename path ending in .gir" % (include_path, ))
135         girname = basename[:-4]
136         include = ast.Include.from_string(girname)
137         if include in self._include_names:
138             return
139         self._parse_include(include_path, uninstalled=True)
140         self._include_names.add(include)
141
142     def lookup_giname(self, name):
143         """Given a name of the form Foo or Bar.Foo,
144 return the corresponding ast.Node, or None if none
145 available.  Will throw KeyError however for unknown
146 namespaces."""
147         if '.' not in name:
148             return self._namespace.get(name)
149         else:
150             (ns, name) = name.split('.', 1)
151             if ns == self._namespace.name:
152                 return self._namespace.get(name)
153             include = self._includes[ns]
154             return include.get(name)
155
156     def lookup_typenode(self, typeobj):
157         """Given a Type object, if it points to a giname,
158 calls lookup_giname() on the name.  Otherwise return
159 None."""
160         if typeobj.target_giname:
161             return self.lookup_giname(typeobj.target_giname)
162         return None
163
164     # Private
165
166     def _find_include(self, include):
167         searchdirs = self._includepaths[:]
168         for path in _xdg_data_dirs:
169             searchdirs.append(os.path.join(path, 'gir-1.0'))
170         searchdirs.append(os.path.join(DATADIR, 'gir-1.0'))
171
172         girname = '%s-%s.gir' % (include.name, include.version)
173         for d in searchdirs:
174             path = os.path.join(d, girname)
175             if os.path.exists(path):
176                 return path
177         sys.stderr.write("Couldn't find include %r (search path: %r)\n"\
178                          % (girname, searchdirs))
179         sys.exit(1)
180
181     @classmethod
182     def parse_from_gir(cls, filename, extra_include_dirs=None):
183         self = cls(None)
184         if extra_include_dirs is not None:
185             self.set_include_paths(extra_include_dirs)
186         self.set_passthrough_mode()
187         self._parse_include(filename)
188         parser = self._cachestore.load(filename)
189         self._namespace = parser.get_namespace()
190         del self._includes[self._namespace.name]
191         return self
192
193     def _parse_include(self, filename, uninstalled=False):
194         parser = None
195         if self._cachestore is not None:
196             parser = self._cachestore.load(filename)
197         if parser is None:
198             parser = GIRParser(types_only=not self._passthrough_mode)
199             parser.parse(filename)
200             if self._cachestore is not None:
201                 self._cachestore.store(filename, parser)
202
203         for include in parser.get_includes():
204             self.register_include(include)
205
206         if not uninstalled:
207             for pkg in parser.get_pkgconfig_packages():
208                 self._pkg_config_packages.add(pkg)
209         namespace = parser.get_namespace()
210         self._includes[namespace.name] = namespace
211
212     def _iter_namespaces(self):
213         """Return an iterator over all included namespaces; the
214 currently-scanned namespace is first."""
215         yield self._namespace
216         for ns in self._includes.itervalues():
217             yield ns
218
219     def _sort_matches(self, x, y):
220         if x[0] is self._namespace:
221             return 1
222         elif y[0] is self._namespace:
223             return -1
224         return cmp(x[2], y[2])
225
226     def _split_c_string_for_namespace_matches(self, name, is_identifier=False):
227         matches = []  # Namespaces which might contain this name
228         unprefixed_namespaces = [] # Namespaces with no prefix, last resort
229         for ns in self._iter_namespaces():
230             if is_identifier:
231                 prefixes = ns.identifier_prefixes
232             elif name[0].isupper():
233                 prefixes = ns._ucase_symbol_prefixes
234             else:
235                 prefixes = ns.symbol_prefixes
236             if prefixes:
237                 for prefix in prefixes:
238                     if (not is_identifier) and (not prefix.endswith('_')):
239                         prefix = prefix + '_'
240                     if name.startswith(prefix):
241                         matches.append((ns, name[len(prefix):], len(prefix)))
242                         break
243             else:
244                 unprefixed_namespaces.append(ns)
245         if matches:
246             matches.sort(self._sort_matches)
247             return map(lambda x: (x[0], x[1]), matches)
248         elif self._accept_unprefixed:
249             return [(self._namespace, name)]
250         elif unprefixed_namespaces:
251             # A bit of a hack; this function ideally shouldn't look through the
252             # contents of namespaces; but since we aren't scanning anything
253             # without a prefix, it's not too bad.
254             for ns in unprefixed_namespaces:
255                 if name in ns:
256                     return [(ns, name)]
257         raise ValueError("Unknown namespace for %s %r"
258                          % ('identifier' if is_identifier else 'symbol', name, ))
259
260     def split_ctype_namespaces(self, ident):
261         """Given a StudlyCaps string identifier like FooBar, return a
262 list of (namespace, stripped_identifier) sorted by namespace length,
263 or raise ValueError.  As a special case, if the current namespace matches,
264 it is always biggest (i.e. last)."""
265         return self._split_c_string_for_namespace_matches(ident, is_identifier=True)
266
267     def split_csymbol_namespaces(self, symbol):
268         """Given a C symbol like foo_bar_do_baz, return a list of
269 (namespace, stripped_symbol) sorted by namespace match probablity, or
270 raise ValueError."""
271         return self._split_c_string_for_namespace_matches(symbol, is_identifier=False)
272
273     def split_csymbol(self, symbol):
274         """Given a C symbol like foo_bar_do_baz, return the most probable
275 (namespace, stripped_symbol) match, or raise ValueError."""
276         matches = self._split_c_string_for_namespace_matches(symbol, is_identifier=False)
277         return matches[-1]
278
279     def strip_identifier(self, ident):
280         hidden = ident.startswith('_')
281         if hidden:
282             ident = ident[1:]
283         try:
284             matches = self.split_ctype_namespaces(ident)
285         except ValueError, e:
286             raise TransformerException(str(e))
287         for ns, name in matches:
288             if ns is self._namespace:
289                 if hidden:
290                     return '_' + name
291                 return name
292         (ns, name) = matches[-1]
293         raise TransformerException(
294             "Skipping foreign identifier %r from namespace %s" % (
295             ident, ns.name, ))
296         return None
297
298     def _strip_symbol(self, symbol):
299         ident = symbol.ident
300         hidden = ident.startswith('_')
301         if hidden:
302             ident = ident[1:]
303         try:
304             (ns, name) = self.split_csymbol(ident)
305         except ValueError, e:
306             raise TransformerException(str(e))
307         if ns != self._namespace:
308             raise TransformerException(
309                 "Skipping foreign symbol from namespace %s" % (ns.name, ))
310         if hidden:
311             return '_' + name
312         return name
313
314     def _traverse_one(self, symbol, stype=None):
315         assert isinstance(symbol, SourceSymbol), symbol
316
317         if stype is None:
318             stype = symbol.type
319         if stype == CSYMBOL_TYPE_FUNCTION:
320             return self._create_function(symbol)
321         elif stype == CSYMBOL_TYPE_TYPEDEF:
322             return self._create_typedef(symbol)
323         elif stype == CSYMBOL_TYPE_STRUCT:
324             return self._create_struct(symbol)
325         elif stype == CSYMBOL_TYPE_ENUM:
326             return self._create_enum(symbol)
327         elif stype == CSYMBOL_TYPE_MEMBER:
328             return self._create_member(symbol)
329         elif stype == CSYMBOL_TYPE_UNION:
330             return self._create_union(symbol)
331         elif stype == CSYMBOL_TYPE_CONST:
332             # Don't parse constants which are marked (skip)
333             docblock = self._annotations.get(symbol.ident)
334             if not docblock or not 'skip' in docblock.options:
335                 return self._create_const(symbol)
336         # Ignore variable declarations in the header
337         elif stype == CSYMBOL_TYPE_OBJECT:
338             pass
339         else:
340             print 'transformer: unhandled symbol: %r' % (symbol, )
341
342     def _enum_common_prefix(self, symbol):
343         def common_prefix(a, b):
344             commonparts = []
345             for aword, bword in zip(a.split('_'), b.split('_')):
346                 if aword != bword:
347                     return '_'.join(commonparts) + '_'
348                 commonparts.append(aword)
349             return min(a, b)
350
351         # Nothing less than 2 has a common prefix
352         if len(list(symbol.base_type.child_list)) < 2:
353             return None
354         prefix = None
355         for child in symbol.base_type.child_list:
356             if prefix is None:
357                 prefix = child.ident
358             else:
359                 prefix = common_prefix(prefix, child.ident)
360                 if prefix == '':
361                     return None
362         return prefix
363
364     def _create_enum(self, symbol):
365         prefix = self._enum_common_prefix(symbol)
366         if prefix:
367             prefixlen = len(prefix)
368         else:
369             prefixlen = 0
370         members = []
371         for child in symbol.base_type.child_list:
372             if child.private:
373                 continue
374             if prefixlen > 0:
375                 name = child.ident[prefixlen:]
376             else:
377                 # Ok, the enum members don't have a consistent prefix
378                 # among them, so let's just remove the global namespace
379                 # prefix.
380                 try:
381                     name = self._strip_symbol(child)
382                 except TransformerException, e:
383                     message.warn_symbol(symbol, e)
384                     return None
385             members.append(ast.Member(name.lower(),
386                                       child.const_int,
387                                       child.ident,
388                                       None))
389
390         try:
391             enum_name = self.strip_identifier(symbol.ident)
392         except TransformerException, e:
393             message.warn_symbol(symbol, e)
394             return None
395         if symbol.base_type.is_bitfield:
396             klass = ast.Bitfield
397         else:
398             klass = ast.Enum
399         node = klass(enum_name, symbol.ident, members=members)
400         node.add_symbol_reference(symbol)
401         return node
402
403     def _create_function(self, symbol):
404         # Drop functions that start with _ very early on here
405         if symbol.ident.startswith('_'):
406             return None
407         parameters = list(self._create_parameters(symbol.base_type))
408         return_ = self._create_return(symbol.base_type.base_type)
409         try:
410             name = self._strip_symbol(symbol)
411         except TransformerException, e:
412             message.warn_symbol(symbol, e)
413             return None
414         func = ast.Function(name, return_, parameters, False, symbol.ident)
415         func.add_symbol_reference(symbol)
416         return func
417
418     def _create_source_type(self, source_type):
419         if source_type is None:
420             return 'None'
421         if source_type.type == CTYPE_VOID:
422             value = 'void'
423         elif source_type.type == CTYPE_BASIC_TYPE:
424             value = source_type.name
425         elif source_type.type == CTYPE_TYPEDEF:
426             value = source_type.name
427         elif source_type.type == CTYPE_ARRAY:
428             return self._create_source_type(source_type.base_type)
429         elif source_type.type == CTYPE_POINTER:
430             value = self._create_source_type(source_type.base_type) + '*'
431         else:
432             value = 'gpointer'
433         return value
434
435     def _create_parameters(self, base_type):
436         # warn if we see annotations for unknown parameters
437         param_names = set(child.ident for child in base_type.child_list)
438         for child in base_type.child_list:
439             yield self._create_parameter(child)
440
441     def _create_member(self, symbol):
442         source_type = symbol.base_type
443         if (source_type.type == CTYPE_POINTER and
444             symbol.base_type.base_type.type == CTYPE_FUNCTION):
445             node = self._create_callback(symbol, member=True)
446         elif source_type.type == CTYPE_STRUCT and source_type.name is None:
447             node = self._create_struct(symbol, anonymous=True)
448         elif source_type.type == CTYPE_UNION and source_type.name is None:
449             node = self._create_union(symbol, anonymous=True)
450         else:
451             # Special handling for fields; we don't have annotations on them
452             # to apply later, yet.
453             if source_type.type == CTYPE_ARRAY:
454                 ctype = self._create_source_type(source_type)
455                 canonical_ctype = self._canonicalize_ctype(ctype)
456                 if canonical_ctype[-1] == '*':
457                     derefed_name = canonical_ctype[:-1]
458                 else:
459                     derefed_name = canonical_ctype
460                 ftype = ast.Array(None, self.create_type_from_ctype_string(ctype),
461                                   ctype=derefed_name)
462                 child_list = list(symbol.base_type.child_list)
463                 ftype.zeroterminated = False
464                 if child_list:
465                     ftype.size = child_list[0].const_int
466             else:
467                 ftype = self._create_type_from_base(symbol.base_type)
468             # ast.Fields are assumed to be read-write
469             # (except for Objects, see also glibtransformer.py)
470             node = ast.Field(symbol.ident, ftype,
471                              readable=True, writable=True,
472                              bits=symbol.const_int)
473             if symbol.private:
474                 node.readable = False
475                 node.writable = False
476                 node.private = True
477         return node
478
479     def _create_typedef(self, symbol):
480         ctype = symbol.base_type.type
481         if (ctype == CTYPE_POINTER and
482             symbol.base_type.base_type.type == CTYPE_FUNCTION):
483             node = self._create_typedef_callback(symbol)
484         elif (ctype == CTYPE_POINTER and
485             symbol.base_type.base_type.type == CTYPE_STRUCT):
486             node = self._create_typedef_struct(symbol, disguised=True)
487         elif ctype == CTYPE_STRUCT:
488             node = self._create_typedef_struct(symbol)
489         elif ctype == CTYPE_UNION:
490             node = self._create_typedef_union(symbol)
491         elif ctype == CTYPE_ENUM:
492             return self._create_enum(symbol)
493         elif ctype in (CTYPE_TYPEDEF,
494                        CTYPE_POINTER,
495                        CTYPE_BASIC_TYPE,
496                        CTYPE_VOID):
497             try:
498                 name = self.strip_identifier(symbol.ident)
499             except TransformerException, e:
500                 message.warn(e)
501                 return None
502             if symbol.base_type.name:
503                 target = self.create_type_from_ctype_string(symbol.base_type.name)
504             else:
505                 target = ast.TYPE_ANY
506             if name in ast.type_names:
507                 return None
508             return ast.Alias(name, target, ctype=symbol.ident)
509         else:
510             raise NotImplementedError(
511                 "symbol %r of type %s" % (symbol.ident, ctype_name(ctype)))
512         return node
513
514     def _canonicalize_ctype(self, ctype):
515         # First look up the ctype including any pointers;
516         # a few type names like 'char*' have their own aliases
517         # and we need pointer information for those.
518         firstpass = ast.type_names.get(ctype)
519
520         # If we have a particular alias for this, skip deep
521         # canonicalization to prevent changing
522         # e.g. char* -> int8*
523         if firstpass:
524             return firstpass.target_fundamental
525
526         if not ctype.endswith('*'):
527             return ctype
528
529         # We have a pointer type.
530         # Strip the end pointer, canonicalize our base type
531         base = ctype[:-1]
532         canonical_base = self._canonicalize_ctype(base)
533
534         # Append the pointer again
535         canonical = canonical_base + '*'
536
537         return canonical
538
539     def parse_ctype(self, ctype, is_member=False):
540         canonical = self._canonicalize_ctype(ctype)
541
542         # Remove all pointers - we require standard calling
543         # conventions.  For example, an 'int' is always passed by
544         # value (unless it's out or inout).
545         derefed_typename = canonical.replace('*', '')
546
547         # Preserve "pointerness" of struct/union members
548         if (is_member and canonical.endswith('*') and
549             derefed_typename in ast.basic_type_names):
550             return 'gpointer'
551         else:
552             return derefed_typename
553
554     def _create_type_from_base(self, source_type, is_parameter=False, is_return=False):
555         ctype = self._create_source_type(source_type)
556         const = ((source_type.type == CTYPE_POINTER) and
557                  (source_type.base_type.type_qualifier & TYPE_QUALIFIER_CONST))
558         return self.create_type_from_ctype_string(ctype, is_const=const,
559                                                   is_parameter=is_parameter, is_return=is_return)
560
561     def _create_bare_container_type(self, base, ctype=None,
562                                     is_const=False):
563         if base in ('GList', 'GSList', 'GLib.List', 'GLib.SList'):
564             if base in ('GList', 'GSList'):
565                 name = 'GLib.' + base[1:]
566             else:
567                 name = base
568             return ast.List(name, ast.TYPE_ANY, ctype=ctype,
569                         is_const=is_const)
570         elif base in ('GArray', 'GPtrArray', 'GByteArray',
571                       'GLib.Array', 'GLib.PtrArray', 'GLib.ByteArray',
572                       'GObject.Array', 'GObject.PtrArray', 'GObject.ByteArray'):
573             if '.' in base:
574                 name = 'GLib.' + base.split('.', 1)[1]
575             else:
576                 name = 'GLib.' + base[1:]
577             return ast.Array(name, ast.TYPE_ANY, ctype=ctype,
578                          is_const=is_const)
579         elif base in ('GHashTable', 'GLib.HashTable', 'GObject.HashTable'):
580             return ast.Map(ast.TYPE_ANY, ast.TYPE_ANY, ctype=ctype, is_const=is_const)
581         return None
582
583     def create_type_from_ctype_string(self, ctype, is_const=False,
584                                       is_parameter=False, is_return=False):
585         canonical = self._canonicalize_ctype(ctype)
586         base = canonical.replace('*', '')
587
588         # Special default: char ** -> ast.Array, same for GStrv
589         if (is_return and canonical == 'utf8*') or base == 'GStrv':
590             bare_utf8 = ast.TYPE_STRING.clone()
591             bare_utf8.ctype = None
592             return ast.Array(None, bare_utf8, ctype=ctype,
593                              is_const=is_const)
594
595         fundamental = ast.type_names.get(base)
596         if fundamental is not None:
597             return ast.Type(target_fundamental=fundamental.target_fundamental,
598                         ctype=ctype,
599                         is_const=is_const)
600         container = self._create_bare_container_type(base, ctype=ctype, is_const=is_const)
601         if container:
602             return container
603         return ast.Type(ctype=ctype, is_const=is_const)
604
605     def _create_parameter(self, symbol):
606         if symbol.type == CSYMBOL_TYPE_ELLIPSIS:
607             ptype = ast.Varargs()
608         else:
609             ptype = self._create_type_from_base(symbol.base_type, is_parameter=True)
610         return ast.Parameter(symbol.ident, ptype)
611
612     def _create_return(self, source_type):
613         typeval = self._create_type_from_base(source_type, is_return=True)
614         return ast.Return(typeval)
615
616     def _create_const(self, symbol):
617         # Don't create constants for non-public things
618         # http://bugzilla.gnome.org/show_bug.cgi?id=572790
619         if (symbol.source_filename is None or
620             not symbol.source_filename.endswith('.h')):
621             return None
622         try:
623             name = self._strip_symbol(symbol)
624         except TransformerException, e:
625             message.warn_symbol(symbol, e)
626             return None
627         if symbol.const_string is not None:
628             typeval = ast.TYPE_STRING
629             value = unicode(symbol.const_string, 'utf-8')
630         elif symbol.const_int is not None:
631             typeval = ast.TYPE_INT
632             value = '%d' % (symbol.const_int, )
633         elif symbol.const_double is not None:
634             typeval = ast.TYPE_DOUBLE
635             value = '%f' % (symbol.const_double, )
636         else:
637             raise AssertionError()
638
639         const = ast.Constant(name, typeval, value)
640         const.add_symbol_reference(symbol)
641         return const
642
643     def _create_typedef_struct(self, symbol, disguised=False):
644         try:
645             name = self.strip_identifier(symbol.ident)
646         except TransformerException, e:
647             message.warn_symbol(symbol, e)
648             return None
649         struct = ast.Record(name, symbol.ident, disguised=disguised)
650         self._parse_fields(symbol, struct)
651         struct.add_symbol_reference(symbol)
652         self._typedefs_ns[symbol.ident] = struct
653         return None
654
655     def _create_typedef_union(self, symbol):
656         try:
657             name = self.strip_identifier(symbol.ident)
658         except TransformerException, e:
659             message.warn(e)
660             return None
661         union = ast.Union(name, symbol.ident)
662         self._parse_fields(symbol, union)
663         union.add_symbol_reference(symbol)
664         self._typedefs_ns[symbol.ident] = union
665         return None
666
667     def _create_typedef_callback(self, symbol):
668         callback = self._create_callback(symbol)
669         if not callback:
670             return None
671         self._typedefs_ns[callback.name] = callback
672         return callback
673
674     def _parse_fields(self, symbol, compound):
675         for child in symbol.base_type.child_list:
676             child_node = self._traverse_one(child)
677             if not child_node:
678                 continue
679             if isinstance(child_node, ast.Field):
680                 field = child_node
681             else:
682                 field = ast.Field(child.ident, None, True, False,
683                               anonymous_node=child_node)
684             compound.fields.append(field)
685
686     def _create_compound(self, klass, symbol, anonymous):
687         if symbol.ident is None:
688             # the compound is an anonymous member of another union or a struct
689             assert anonymous
690             compound = klass(None, None)
691         else:
692             compound = self._typedefs_ns.get(symbol.ident, None)
693
694         if compound is None:
695             # This is a bit of a hack; really we should try
696             # to resolve through the typedefs to find the real
697             # name
698             if symbol.ident.startswith('_'):
699                 compound = self._typedefs_ns.get(symbol.ident[1:], None)
700             if compound is None:
701                 if anonymous:
702                     name = symbol.ident
703                 else:
704                     try:
705                         name = self.strip_identifier(symbol.ident)
706                     except TransformerException, e:
707                         message.warn(e)
708                         return None
709                 compound = klass(name, symbol.ident)
710
711         self._parse_fields(symbol, compound)
712         compound.add_symbol_reference(symbol)
713         return compound
714
715     def _create_struct(self, symbol, anonymous=False):
716         return self._create_compound(ast.Record, symbol, anonymous)
717
718     def _create_union(self, symbol, anonymous=False):
719         return self._create_compound(ast.Union, symbol, anonymous)
720
721     def _create_callback(self, symbol, member=False):
722         parameters = list(self._create_parameters(symbol.base_type.base_type))
723         retval = self._create_return(symbol.base_type.base_type.base_type)
724
725         # Mark the 'user_data' arguments
726         for i, param in enumerate(parameters):
727             if (param.type.target_fundamental == 'gpointer' and
728                 param.argname == 'user_data'):
729                 param.closure_name = param.argname
730
731         if member:
732             name = symbol.ident
733         elif symbol.ident.find('_') > 0:
734             try:
735                 name = self._strip_symbol(symbol)
736             except TransformerException, e:
737                 message.warn_symbol(symbol, e)
738                 return None
739         else:
740             try:
741                 name = self.strip_identifier(symbol.ident)
742             except TransformerException, e:
743                 message.warn(e)
744                 return None
745         callback = ast.Callback(name, retval, parameters, False,
746                                 ctype=symbol.ident)
747         callback.add_symbol_reference(symbol)
748
749         return callback
750
751     def create_type_from_user_string(self, typestr):
752         """Parse a C type string (as might be given from an
753         annotation) and resolve it.  For compatibility, we can consume
754 both GI type string (utf8, Foo.Bar) style, as well as C (char *, FooBar) style.
755
756 Note that type resolution may not succeed."""
757         if '.' in typestr:
758             container = self._create_bare_container_type(typestr)
759             if container:
760                 return container
761             return self._namespace.type_from_name(typestr)
762         typeval = self.create_type_from_ctype_string(typestr)
763         self.resolve_type(typeval)
764         if typeval.resolved:
765             # Explicitly clear out the c_type; there isn't one in this case.
766             typeval.ctype = None
767         return typeval
768
769     def _resolve_type_from_ctype_all_namespaces(self, typeval, pointer_stripped):
770         # If we can't determine the namespace from the type name,
771         # fall back to trying all of our includes.  An example of this is mutter,
772         # which has nominal namespace of "Meta", but a few classes are
773         # "Mutter".  We don't export that data in introspection currently.
774         # Basically the library should be fixed, but we'll hack around it here.
775         for namespace in self._includes.itervalues():
776             target = namespace.get_by_ctype(pointer_stripped)
777             if target:
778                 typeval.target_giname = '%s.%s' % (namespace.name, target.name)
779                 return True
780         return False
781
782     def _resolve_type_from_ctype(self, typeval):
783         assert typeval.ctype is not None
784         pointer_stripped = typeval.ctype.replace('*', '')
785         try:
786             matches = self.split_ctype_namespaces(pointer_stripped)
787         except ValueError, e:
788             return self._resolve_type_from_ctype_all_namespaces(typeval, pointer_stripped)
789         target_giname = None
790         for namespace, name in matches:
791             target = namespace.get(name)
792             if not target:
793                 target = namespace.get_by_ctype(pointer_stripped)
794             if target:
795                 typeval.target_giname = '%s.%s' % (namespace.name, target.name)
796                 return True
797         return False
798
799     def _resolve_type_from_gtype_name(self, typeval):
800         assert typeval.gtype_name is not None
801         for ns in self._iter_namespaces():
802             for node in ns.itervalues():
803                 if not (isinstance(node, (ast.Class, ast.Interface))
804                         or (isinstance(node, ast.Registered) and node.get_type is not None)):
805                     continue
806                 if node.gtype_name == typeval.gtype_name:
807                     typeval.target_giname = '%s.%s' % (ns.name, node.name)
808                     return True
809         return False
810
811     def resolve_type(self, typeval):
812         if isinstance(typeval, (ast.Array, ast.List)):
813             return self.resolve_type(typeval.element_type)
814         elif isinstance(typeval, ast.Map):
815             key_resolved = self.resolve_type(typeval.key_type)
816             value_resolved = self.resolve_type(typeval.value_type)
817             return key_resolved and value_resolved
818         elif typeval.resolved:
819             return True
820         elif typeval.ctype:
821             return self._resolve_type_from_ctype(typeval)
822         elif typeval.gtype_name:
823             return self._resolve_type_from_gtype_name(typeval)
824
825     def _typepair_to_str(self, item):
826         nsname, item = item
827         if nsname is None:
828             return item.name
829         return '%s.%s' % (nsname, item.name)
830
831     def gtypename_to_giname(self, gtname, names):
832         resolved = names.type_names.get(gtname)
833         if resolved:
834             return self._typepair_to_str(resolved)
835         resolved = self._names.type_names.get(gtname)
836         if resolved:
837             return self._typepair_to_str(resolved)
838         raise KeyError("Failed to resolve GType name: %r" % (gtname, ))
839
840     def ctype_of(self, obj):
841         if hasattr(obj, 'ctype'):
842             return obj.ctype
843         elif hasattr(obj, 'symbol'):
844             return obj.symbol
845         else:
846             return None
847
848     def resolve_aliases(self, typenode):
849         """Removes all aliases from typenode, returns first non-alias
850         in the typenode alias chain.  Returns typenode argument if it
851         is not an alias."""
852         while isinstance(typenode, ast.Alias):
853             if typenode.target.target_giname is not None:
854                 typenode = self.lookup_giname(typenode.target.target_giname)
855             elif typenode.target.target_fundamental is not None:
856                 typenode = ast.type_names[typenode.target.target_fundamental]
857             else:
858                 break
859         return typenode