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