6179c2b6ed787888650a7a1ba71492665a2b64f5
[gnome.gobject-introspection] / 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 .ast import (Bitfield, Callback, Enum, Function, Namespace, Member,
25                   Parameter, Return, Struct, Field,
26                   Type, Array, Alias, Interface, Class, Node, Union,
27                   Varargs, Constant, type_name_from_ctype,
28                   type_names, TYPE_STRING, BASIC_GIR_TYPES)
29 from .config import DATADIR, GIR_DIR, GIR_SUFFIX
30 from .glibast import GLibBoxed
31 from .girparser import GIRParser
32 from .odict import odict
33 from .sourcescanner import (
34     SourceSymbol, ctype_name, CTYPE_POINTER,
35     CTYPE_BASIC_TYPE, CTYPE_UNION, CTYPE_ARRAY, CTYPE_TYPEDEF,
36     CTYPE_VOID, CTYPE_ENUM, CTYPE_FUNCTION, CTYPE_STRUCT,
37     CSYMBOL_TYPE_FUNCTION, CSYMBOL_TYPE_TYPEDEF, CSYMBOL_TYPE_STRUCT,
38     CSYMBOL_TYPE_ENUM, CSYMBOL_TYPE_UNION, CSYMBOL_TYPE_OBJECT,
39     CSYMBOL_TYPE_MEMBER, CSYMBOL_TYPE_ELLIPSIS, CSYMBOL_TYPE_CONST,
40     TYPE_QUALIFIER_CONST)
41 from .utils import to_underscores
42
43 _xdg_data_dirs = [x for x in os.environ.get('XDG_DATA_DIRS', '').split(':') \
44                       + [DATADIR, '/usr/share'] if x]
45
46
47 class SkipError(Exception):
48     pass
49
50 class VaListSkipError(SkipError):
51     pass
52
53 class Names(object):
54     names = property(lambda self: self._names)
55     aliases = property(lambda self: self._aliases)
56     type_names = property(lambda self: self._type_names)
57     ctypes = property(lambda self: self._ctypes)
58
59     def __init__(self):
60         super(Names, self).__init__()
61         self._names = odict() # Maps from GIName -> (namespace, node)
62         self._aliases = {} # Maps from GIName -> GIName
63         self._type_names = {} # Maps from GTName -> (namespace, node)
64         self._ctypes = {} # Maps from CType -> (namespace, node)
65
66
67 class Transformer(object):
68
69     def __init__(self, cachestore, namespace_name, namespace_version):
70         self._cachestore = cachestore
71         self.generator = None
72         self._namespace = Namespace(namespace_name, namespace_version)
73         self._names = Names()
74         self._pkg_config_packages = set()
75         self._typedefs_ns = {}
76         self._strip_prefix = ''
77         self._includes = set()
78         self._includepaths = []
79
80     def get_names(self):
81         return self._names
82
83     def get_includes(self):
84         return self._includes
85
86     def set_strip_suffix(self, strip_suffix):
87         self._strip_suffix = strip_suffix
88
89     def set_strip_prefix(self, strip_prefix):
90         self._strip_prefix = strip_prefix
91
92     def get_strip_prefix(self):
93         return self._strip_prefix
94
95     def get_pkgconfig_packages(self):
96         return self._pkg_config_packages
97
98     def set_source_ast(self, src_ast):
99         self.generator = src_ast
100
101     def parse(self):
102         nodes = []
103         for symbol in self.generator.get_symbols():
104             try:
105                 node = self._traverse_one(symbol)
106             except SkipError:
107                 continue
108             self._add_node(node)
109         return self._namespace
110
111     def set_include_paths(self, paths):
112         self._includepaths = list(paths)
113
114     def register_include(self, include):
115         if include in self._includes:
116             return
117         filename = self._find_include(include)
118         self._parse_include(filename)
119         self._includes.add(include)
120
121     # Private
122
123     def _find_include(self, include):
124         searchdirs = self._includepaths[:]
125         for path in _xdg_data_dirs:
126             searchdirs.append(os.path.join(path, GIR_SUFFIX))
127         searchdirs.append(GIR_DIR)
128
129         girname = '%s-%s.gir' % (include.name, include.version)
130         for d in searchdirs:
131             path = os.path.join(d, girname)
132             if os.path.exists(path):
133                 return path
134         sys.stderr.write("Couldn't find include %r (search path: %r)\n"\
135                          % (girname, searchdirs))
136         sys.exit(1)
137
138     def _parse_include(self, filename):
139         parser = self._cachestore.load(filename)
140         if parser is None:
141             parser = GIRParser()
142             parser.set_include_parsing(True)
143             parser.parse(filename)
144             self._cachestore.store(filename, parser)
145
146         for include in parser.get_includes():
147             self.register_include(include)
148
149         for pkg in parser.get_pkgconfig_packages():
150             self._pkg_config_packages.add(pkg)
151         namespace = parser.get_namespace()
152         nsname = namespace.name
153         for node in namespace.nodes:
154             if isinstance(node, Alias):
155                 self._names.aliases[node.name] = (nsname, node)
156             elif isinstance(node, (GLibBoxed, Interface, Class)):
157                 self._names.type_names[node.type_name] = (nsname, node)
158             giname = '%s.%s' % (nsname, node.name)
159             self._names.names[giname] = (nsname, node)
160             if hasattr(node, 'ctype'):
161                 self._names.ctypes[node.ctype] = (nsname, node)
162             elif hasattr(node, 'symbol'):
163                 self._names.ctypes[node.symbol] = (nsname, node)
164
165     def _add_node(self, node):
166         if node is None:
167             return
168         if node.name.startswith('_'):
169             return
170         self._namespace.nodes.append(node)
171         self._names.names[node.name] = (None, node)
172
173     def _strip_namespace_func(self, name):
174         prefix = self._namespace.name.lower() + '_'
175         if name.lower().startswith(prefix):
176             name = name[len(prefix):]
177         else:
178             prefix = to_underscores(self._namespace.name).lower() + '_'
179             if name.lower().startswith(prefix):
180                 name = name[len(prefix):]
181         return self.remove_prefix(name, isfunction=True)
182
183     def remove_prefix(self, name, isfunction=False):
184         # when --strip-prefix=g:
185         #   GHashTable -> HashTable
186         #   g_hash_table_new -> hash_table_new
187         stripped = False
188         prefix = self._strip_prefix.lower()
189         
190         if isfunction and '_' in name:
191             prefix += '_'
192         if len(name) > len(prefix) and name.lower().startswith(prefix):
193             name = name[len(prefix):]
194             stripped = True
195
196         while name.startswith('_'):
197             name = name[1:]
198
199         if (stripped and self._strip_suffix and 
200             len(name) > len(self._strip_suffix) and
201             name.endswith(self._strip_suffix)
202             name = name[:-1*len(self._strip_suffix)]
203             
204         return name
205
206     def _traverse_one(self, symbol, stype=None):
207         assert isinstance(symbol, SourceSymbol), symbol
208
209         if stype is None:
210             stype = symbol.type
211         if stype == CSYMBOL_TYPE_FUNCTION:
212             return self._create_function(symbol)
213         elif stype == CSYMBOL_TYPE_TYPEDEF:
214             return self._create_typedef(symbol)
215         elif stype == CSYMBOL_TYPE_STRUCT:
216             return self._create_struct(symbol)
217         elif stype == CSYMBOL_TYPE_ENUM:
218             return self._create_enum(symbol)
219         elif stype == CSYMBOL_TYPE_OBJECT:
220             return self._create_object(symbol)
221         elif stype == CSYMBOL_TYPE_MEMBER:
222             return self._create_member(symbol)
223         elif stype == CSYMBOL_TYPE_UNION:
224             return self._create_union(symbol)
225         elif stype == CSYMBOL_TYPE_CONST:
226             return self._create_const(symbol)
227         else:
228             raise NotImplementedError(
229                 'Transformer: unhandled symbol: %r' % (symbol, ))
230
231     def _enum_common_prefix(self, symbol):
232         def common_prefix(a, b):
233             commonparts = []
234             for aword, bword in zip(a.split('_'), b.split('_')):
235                 if aword != bword:
236                     return '_'.join(commonparts) + '_'
237                 commonparts.append(aword)
238             return min(a, b)
239
240         # Nothing less than 2 has a common prefix
241         if len(list(symbol.base_type.child_list)) < 2:
242             return None
243         prefix = None
244         for child in symbol.base_type.child_list:
245             if prefix is None:
246                 prefix = child.ident
247             else:
248                 prefix = common_prefix(prefix, child.ident)
249                 if prefix == '':
250                     return None
251         return prefix
252
253     def _create_enum(self, symbol):
254         prefix = self._enum_common_prefix(symbol)
255         if prefix:
256             prefixlen = len(prefix)
257         else:
258             prefixlen = 0
259         members = []
260         for child in symbol.base_type.child_list:
261             if prefixlen > 0:
262                 name = child.ident[prefixlen:]
263             else:
264                 # Ok, the enum members don't have a consistent prefix
265                 # among them, so let's just remove the global namespace
266                 # prefix.
267                 name = self.remove_prefix(child.ident)
268             members.append(Member(name.lower(),
269                                   child.const_int,
270                                   child.ident))
271
272         enum_name = self.remove_prefix(symbol.ident)
273         if symbol.base_type.is_bitfield:
274             klass = Bitfield
275         else:
276             klass = Enum
277         node = klass(enum_name, symbol.ident, members)
278         self._names.type_names[symbol.ident] = (None, node)
279         return node
280
281     def _create_object(self, symbol):
282         return Member(symbol.ident, symbol.base_type.name,
283                       symbol.ident)
284
285     def _type_is_callback(self, type):
286         if isinstance(type, Callback):
287             return True
288         node = self._names.names.get(type.name)
289         if node and isinstance(node[1], Callback):
290             return True
291         return False
292
293     def _handle_closure(self, param, closure_idx, closure_param):
294         if (closure_param.type.name == 'any' and
295             closure_param.name.endswith('data')):
296             param.closure_name = closure_param.name
297             param.closure_index = closure_idx
298             return True
299         return False
300
301     def _handle_destroy(self, param, destroy_idx, destroy_param):
302         if (destroy_param.type.name == 'GLib.DestroyNotify' or
303             destroy_param.type.ctype == 'GDestroyNotify'):
304             param.destroy_name = destroy_param.name
305             param.destroy_index = destroy_idx
306             return True
307         return False
308
309     def _augment_callback_params(self, params):
310         for i, param in enumerate(params):
311             if not self._type_is_callback(param.type):
312                 continue
313
314             # set a default scope
315             if param.scope is None:
316                 param.scope = 'call'
317
318             # j is the index where we look for closure/destroy to
319             # group with the callback param
320             j = i + 1
321             if j == len(params):
322                 continue # no more args -> nothing to group
323             # look at the param directly following for either a
324             # closure or a destroy; only one of these will fire
325             had_closure = self._handle_closure(param, j, params[j])
326             had_destroy = self._handle_destroy(param, j, params[j])
327             j += 1
328             # are we out of params, or did we find neither?
329             if j == len(params) or (not had_closure and not had_destroy):
330                 continue
331             # we found either a closure or a destroy; check the
332             # parameter following for the other
333             if not had_closure:
334                 self._handle_closure(param, j, params[j])
335             if not had_destroy:
336                 self._handle_destroy(param, j, params[j])
337
338     def _create_function(self, symbol):
339         parameters = list(self._create_parameters(symbol.base_type))
340         return_ = self._create_return(symbol.base_type.base_type)
341         self._augment_callback_params(parameters)
342         name = self._strip_namespace_func(symbol.ident)
343         func = Function(name, return_, parameters, symbol.ident)
344         return func
345
346     def _create_source_type(self, source_type):
347         if source_type is None:
348             return 'None'
349         if source_type.type == CTYPE_VOID:
350             value = 'void'
351         elif source_type.type == CTYPE_BASIC_TYPE:
352             value = source_type.name
353         elif source_type.type == CTYPE_TYPEDEF:
354             value = source_type.name
355         elif source_type.type == CTYPE_ARRAY:
356             return self._create_source_type(source_type.base_type)
357         elif source_type.type == CTYPE_POINTER:
358             value = self._create_source_type(source_type.base_type) + '*'
359         else:
360             value = 'any'
361         return value
362
363     def _create_parameters(self, base_type):
364
365         # warn if we see annotations for unknown parameters
366         param_names = set(child.ident for child in base_type.child_list)
367         for child in base_type.child_list:
368             yield self._create_parameter(child)
369
370     def _create_member(self, symbol):
371         source_type = symbol.base_type
372         if (source_type.type == CTYPE_POINTER and
373             symbol.base_type.base_type.type == CTYPE_FUNCTION):
374             try:
375                 node = self._create_callback(symbol)
376             except VaListSkipError:
377                 #this handles va_list members, and converts them
378                 #to unwritable, unreadable void*
379                 ftype = Type("any", "void*")
380                 ftype = self.resolve_param_type(ftype)
381                 node = Field(symbol.ident, ftype, ftype.name,
382                          readable=False, writable=False, bits=symbol.const_int)
383
384
385         elif source_type.type == CTYPE_STRUCT and source_type.name is None:
386             node = self._create_struct(symbol, anonymous=True)
387         elif source_type.type == CTYPE_UNION and source_type.name is None:
388             node = self._create_union(symbol, anonymous=True)
389         else:
390             # Special handling for fields; we don't have annotations on them
391             # to apply later, yet.
392             if source_type.type == CTYPE_ARRAY:
393                 ctype = self._create_source_type(source_type)
394                 canonical_ctype = self._canonicalize_ctype(ctype)
395                 if canonical_ctype[-1] == '*':
396                     derefed_name = canonical_ctype[:-1]
397                 else:
398                     derefed_name = canonical_ctype
399                 derefed_name = self.resolve_param_type(derefed_name)
400                 ftype = Array(ctype, self.parse_ctype(derefed_name))
401                 child_list = list(symbol.base_type.child_list)
402                 ftype.zeroterminated = False
403                 if child_list:
404                     ftype.size = '%d' % (child_list[0].const_int, )
405             else:
406                 ftype = self._create_type(symbol.base_type,
407                                           is_param=False, is_retval=False)
408             ftype = self.resolve_param_type(ftype)
409             # Fields are assumed to be read-write
410             # (except for Objects, see also glibtransformer.py)
411             node = Field(symbol.ident, ftype, ftype.name,
412                          readable=True, writable=True, bits=symbol.const_int)
413         return node
414
415     def _create_typedef(self, symbol):
416         ctype = symbol.base_type.type
417         if (ctype == CTYPE_POINTER and
418             symbol.base_type.base_type.type == CTYPE_FUNCTION):
419             node = self._create_typedef_callback(symbol)
420         elif (ctype == CTYPE_POINTER and
421             symbol.base_type.base_type.type == CTYPE_STRUCT):
422             node = self._create_typedef_struct(symbol, disguised=True)
423         elif ctype == CTYPE_STRUCT:
424             node = self._create_typedef_struct(symbol)
425         elif ctype == CTYPE_UNION:
426             node = self._create_typedef_union(symbol)
427         elif ctype == CTYPE_ENUM:
428             return self._create_enum(symbol)
429         elif ctype in (CTYPE_TYPEDEF,
430                        CTYPE_POINTER,
431                        CTYPE_BASIC_TYPE,
432                        CTYPE_VOID):
433             name = self.remove_prefix(symbol.ident)
434             if symbol.base_type.name:
435                 target = self.remove_prefix(symbol.base_type.name)
436             else:
437                 target = 'none'
438             if name in type_names:
439                 return None
440             return Alias(name, target, ctype=symbol.ident)
441         else:
442             raise NotImplementedError(
443                 "symbol %r of type %s" % (symbol.ident, ctype_name(ctype)))
444         return node
445
446     def _canonicalize_ctype(self, ctype):
447         # First look up the ctype including any pointers;
448         # a few type names like 'char*' have their own aliases
449         # and we need pointer information for those.
450         firstpass = type_name_from_ctype(ctype)
451
452         # If we have a particular alias for this, skip deep
453         # canonicalization to prevent changing
454         # e.g. char* -> int8*
455         if firstpass != ctype:
456             return firstpass
457
458         # We're also done if the type is already a fundamental
459         # known type, or there are no pointers.
460         if ctype in type_names or not firstpass.endswith('*'):
461             return firstpass
462
463         # We have a pointer type.
464         # Strip the end pointer, canonicalize our base type
465         base = firstpass[:-1]
466         canonical_base = self._canonicalize_ctype(base)
467
468         # Append the pointer again
469         canonical = canonical_base + '*'
470
471         return canonical
472
473     def parse_ctype(self, ctype, is_member=False):
474         canonical = self._canonicalize_ctype(ctype)
475
476         # Remove all pointers - we require standard calling
477         # conventions.  For example, an 'int' is always passed by
478         # value (unless it's out or inout).
479         derefed_typename = canonical.replace('*', '')
480
481         # Preserve "pointerness" of struct/union members
482         if (is_member and canonical.endswith('*') and
483             derefed_typename in BASIC_GIR_TYPES):
484             return 'any'
485         else:
486             return derefed_typename
487
488     def _create_type(self, source_type, is_param, is_retval):
489         ctype = self._create_source_type(source_type)
490         if ctype.startswith('va_list'):
491             raise VaListSkipError()
492         # FIXME: FILE* should not be skipped, it should be handled
493         #        properly instead
494         elif ctype == 'FILE*':
495             raise SkipError
496
497         is_member = not (is_param or is_retval)
498         # Here we handle basic type parsing; most of the heavy lifting
499         # and inference comes in annotationparser.py when we merge
500         # in annotation data.
501         derefed_name = self.parse_ctype(ctype, is_member)
502         rettype = Type(derefed_name, ctype)
503         rettype.canonical = self._canonicalize_ctype(ctype)
504         derefed_ctype = ctype.replace('*', '')
505         rettype.derefed_canonical = self._canonicalize_ctype(derefed_ctype)
506
507         canontype = type_name_from_ctype(ctype)
508         # Is it a const char * or a const gpointer?
509         if ((canontype == TYPE_STRING or source_type.type == CTYPE_POINTER) and
510             (source_type.base_type.type_qualifier & TYPE_QUALIFIER_CONST)):
511             rettype.is_const = True
512         return rettype
513
514     def _create_parameter(self, symbol):
515         if symbol.type == CSYMBOL_TYPE_ELLIPSIS:
516             ptype = Varargs()
517         else:
518             ptype = self._create_type(symbol.base_type,
519                                       is_param=True, is_retval=False)
520             ptype = self.resolve_param_type(ptype)
521         return Parameter(symbol.ident, ptype)
522
523     def _create_return(self, source_type):
524         rtype = self._create_type(source_type,
525                                   is_param=False, is_retval=True)
526         rtype = self.resolve_param_type(rtype)
527         return_ = Return(rtype)
528         return return_
529
530     def _create_const(self, symbol):
531         # Don't create constants for non-public things
532         # http://bugzilla.gnome.org/show_bug.cgi?id=572790
533         if (symbol.source_filename is None or
534             not symbol.source_filename.endswith('.h')):
535             return None
536         name = self.remove_prefix(symbol.ident)
537         if symbol.const_string is not None:
538             type_name = 'utf8'
539             value = symbol.const_string
540         elif symbol.const_int is not None:
541             type_name = 'int'
542             value = symbol.const_int
543         elif symbol.const_double is not None:
544             type_name = 'double'
545             value = symbol.const_double
546         else:
547             raise AssertionError()
548
549         const = Constant(name, type_name, value)
550         return const
551
552     def _create_typedef_struct(self, symbol, disguised=False):
553         name = self.remove_prefix(symbol.ident)
554         struct = Struct(name, symbol.ident, disguised)
555         self._typedefs_ns[symbol.ident] = struct
556         self._create_struct(symbol)
557         return struct
558
559     def _create_typedef_union(self, symbol):
560         name = self.remove_prefix(symbol.ident)
561         union = Union(name, symbol.ident)
562         self._typedefs_ns[symbol.ident] = union
563         self._create_union(symbol)
564         return union
565
566     def _create_typedef_callback(self, symbol):
567         callback = self._create_callback(symbol)
568         self._typedefs_ns[callback.name] = callback
569         return callback
570
571     def _create_compound(self, klass, symbol, anonymous):
572         if symbol.ident is None:
573             # the compound is an anonymous member of another union or a struct
574             assert anonymous
575             compound = klass(None, None)
576         else:
577             compound = self._typedefs_ns.get(symbol.ident, None)
578
579         if compound is None:
580             # This is a bit of a hack; really we should try
581             # to resolve through the typedefs to find the real
582             # name
583             if symbol.ident.startswith('_'):
584                 name = symbol.ident[1:]
585                 compound = self._typedefs_ns.get(name, None)
586             else:
587                 name = symbol.ident
588             if compound is None:
589                 name = self.remove_prefix(name)
590                 compound = klass(name, symbol.ident)
591
592         for child in symbol.base_type.child_list:
593             field = self._traverse_one(child)
594             if field:
595                 compound.fields.append(field)
596
597         return compound
598
599     def _create_struct(self, symbol, anonymous=False):
600         return self._create_compound(Struct, symbol, anonymous)
601
602     def _create_union(self, symbol, anonymous=False):
603         return self._create_compound(Union, symbol, anonymous)
604
605     def _create_callback(self, symbol):
606         parameters = list(self._create_parameters(symbol.base_type.base_type))
607         retval = self._create_return(symbol.base_type.base_type.base_type)
608
609         # Mark the 'user_data' arguments
610         for i, param in enumerate(parameters):
611             if (param.type.name == 'any' and
612                 param.name == 'user_data'):
613                 param.closure_index = i
614
615         if symbol.ident.find('_') > 0:
616             name = self.remove_prefix(symbol.ident, True)
617         else:
618             name = self.remove_prefix(symbol.ident)
619         callback = Callback(name, retval, parameters, symbol.ident)
620
621         return callback
622
623     def _typepair_to_str(self, item):
624         nsname, item = item
625         if nsname is None:
626             return item.name
627         return '%s.%s' % (nsname, item.name)
628
629     def _resolve_type_name_1(self, type_name, ctype, names):
630         # First look using the built-in names
631         if ctype:
632             try:
633                 return type_names[ctype]
634             except KeyError, e:
635                 pass
636         try:
637             return type_names[type_name]
638         except KeyError, e:
639             pass
640
641         if ctype:
642             ctype = ctype.replace('*', '')
643             resolved = names.ctypes.get(ctype)
644             if resolved:
645                 return self._typepair_to_str(resolved)
646         type_name = self.remove_prefix(type_name)
647         resolved = names.aliases.get(type_name)
648         if resolved:
649             return self._typepair_to_str(resolved)
650         resolved = names.names.get(type_name)
651         if resolved:
652             return self._typepair_to_str(resolved)
653         resolved = names.type_names.get(type_name)
654         if resolved:
655             return self._typepair_to_str(resolved)
656         raise KeyError("failed to find %r" % (type_name, ))
657
658     def resolve_type_name_full(self, type_name, ctype,
659                                names, allow_invalid=True):
660         try:
661             return self._resolve_type_name_1(type_name, ctype, names)
662         except KeyError, e:
663             try:
664                 return self._resolve_type_name_1(type_name, ctype, self._names)
665             except KeyError, e:
666                 if not allow_invalid:
667                     raise
668                 return type_name
669
670     def resolve_type_name(self, type_name, ctype=None):
671         try:
672             return self.resolve_type_name_full(type_name, ctype, self._names)
673         except KeyError, e:
674             return type_name
675
676     def gtypename_to_giname(self, gtname, names):
677         resolved = names.type_names.get(gtname)
678         if resolved:
679             return self._typepair_to_str(resolved)
680         resolved = self._names.type_names.get(gtname)
681         if resolved:
682             return self._typepair_to_str(resolved)
683         raise KeyError("Failed to resolve GType name: %r" % (gtname, ))
684
685     def ctype_of(self, obj):
686         if hasattr(obj, 'ctype'):
687             return obj.ctype
688         elif hasattr(obj, 'symbol'):
689             return obj.symbol
690         else:
691             return None
692
693     def resolve_param_type_full(self, ptype, names, **kwargs):
694         if isinstance(ptype, Node):
695             ptype.name = self.resolve_type_name_full(ptype.name,
696                                                      self.ctype_of(ptype),
697                                                      names, **kwargs)
698         elif isinstance(ptype, basestring):
699             return self.resolve_type_name_full(ptype, ptype, names, **kwargs)
700         else:
701             raise AssertionError("Unhandled param: %r" % (ptype, ))
702         return ptype
703
704     def resolve_param_type(self, ptype):
705         try:
706             return self.resolve_param_type_full(ptype, self._names)
707         except KeyError, e:
708             return ptype
709
710     def follow_aliases(self, type_name, names):
711         while True:
712             resolved = names.aliases.get(type_name)
713             if resolved:
714                 (ns, alias) = resolved
715                 type_name = alias.target
716             else:
717                 break
718         return type_name
719
720     def iter_enums(self):
721         for node in self._namespace.nodes:
722             if isinstance(node, Enum):
723                 yield node