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