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