Look in XDG_DATA_DIR/gir, really.
[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 program is free software; you can redistribute it and/or
6 # modify it under the terms of the GNU General Public License
7 # as published by the Free Software Foundation; either version 2
8 # of the License, or (at your option) any later version.
9 #
10 # This program 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
13 # GNU General Public License for more details.
14 #
15 # You should have received a copy of the GNU General Public License
16 # along with this program; if not, write to the Free Software
17 # Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
18 # 02110-1301, USA.
19 #
20
21 import os
22 import re
23
24 from .ast import (Callback, Enum, Function, Namespace, Member,
25                   Parameter, Return, Array, Struct, Field,
26                   Type, Alias, Interface, Class, Node, Union,
27                   List, Map, Varargs, Constant, type_name_from_ctype,
28                   type_names, default_array_types, default_out_types,
29                   TYPE_STRING, BASIC_GIR_TYPES, TYPE_NONE)
30 from .config import DATADIR
31 from .glibast import GLibBoxed
32 from .girparser import GIRParser
33 from .odict import odict
34 from .sourcescanner import (
35     SourceSymbol, ctype_name, CTYPE_POINTER,
36     CTYPE_BASIC_TYPE, CTYPE_UNION, CTYPE_ARRAY, CTYPE_TYPEDEF,
37     CTYPE_VOID, CTYPE_ENUM, CTYPE_FUNCTION, CTYPE_STRUCT,
38     CSYMBOL_TYPE_FUNCTION, CSYMBOL_TYPE_TYPEDEF, CSYMBOL_TYPE_STRUCT,
39     CSYMBOL_TYPE_ENUM, CSYMBOL_TYPE_UNION, CSYMBOL_TYPE_OBJECT,
40     CSYMBOL_TYPE_MEMBER, CSYMBOL_TYPE_ELLIPSIS, CSYMBOL_TYPE_CONST,
41     TYPE_QUALIFIER_CONST)
42 from .utils import strip_common_prefix, to_underscores
43
44 _xdg_data_dirs = [x for x in os.environ.get('XDG_DATA_DIRS', '').split(':') \
45                       + [DATADIR, '/usr/share'] if x]
46
47
48 class SkipError(Exception):
49     pass
50
51
52 class Names(object):
53     names = property(lambda self: self._names)
54     aliases = property(lambda self: self._aliases)
55     type_names = property(lambda self: self._type_names)
56     ctypes = property(lambda self: self._ctypes)
57
58     def __init__(self):
59         super(Names, self).__init__()
60         self._names = odict() # Maps from GIName -> (namespace, node)
61         self._aliases = {} # Maps from GIName -> GIName
62         self._type_names = {} # Maps from GTName -> (namespace, node)
63         self._ctypes = {} # Maps from CType -> (namespace, node)
64
65
66 class Transformer(object):
67
68     def __init__(self, generator, namespace_name, namespace_version):
69         self.generator = generator
70         self._namespace = Namespace(namespace_name, namespace_version)
71         self._names = Names()
72         self._typedefs_ns = {}
73         self._strip_prefix = ''
74         self._includes = set()
75         self._includepaths = []
76         self._list_ctypes = []
77         self._map_ctypes = []
78
79     def get_names(self):
80         return self._names
81
82     def get_includes(self):
83         return self._includes
84
85     def set_container_types(self, list_ctypes, map_ctypes):
86         self._list_ctypes = list_ctypes
87         self._map_ctypes = map_ctypes
88
89     def set_strip_prefix(self, strip_prefix):
90         self._strip_prefix = strip_prefix
91
92     def parse(self):
93         nodes = []
94         for symbol in self.generator.get_symbols():
95             node = self._traverse_one(symbol)
96             self._add_node(node)
97         return self._namespace
98
99     def set_include_paths(self, paths):
100         self._includepaths = list(paths)
101
102     def register_include(self, include):
103         filename = self._find_include(include)
104         self._parse_include(filename)
105         self._includes.add(include)
106
107     # Private
108
109     def _find_include(self, include):
110         searchdirs = self._includepaths[:]
111         for path in _xdg_data_dirs:
112             searchdirs.append(os.path.join(path, 'gir'))
113
114         girname = '%s-%s.gir' % (include.name, include.version)
115         for d in searchdirs:
116             path = os.path.join(d, girname)
117             if os.path.exists(path):
118                 return path
119         else:
120             raise ValueError("Couldn't find include %r (search path: %r)"\
121                              % (girname, searchdirs))
122
123     def _parse_include(self, filename):
124         parser = GIRParser(filename, include_parsing=True)
125         for include in parser.get_includes():
126             self.register_include(include)
127         nsname = parser.get_namespace().name
128         for node in parser.get_namespace().nodes:
129             if isinstance(node, Alias):
130                 self._names.aliases[node.name] = (nsname, node)
131             elif isinstance(node, (GLibBoxed, Interface, Class)):
132                 self._names.type_names[node.type_name] = (nsname, node)
133             self._names.names[node.name] = (nsname, node)
134             if hasattr(node, 'ctype'):
135                 self._names.ctypes[node.ctype] = (nsname, node)
136             elif hasattr(node, 'symbol'):
137                 self._names.ctypes[node.symbol] = (nsname, node)
138
139     def _add_node(self, node):
140         if node is None:
141             return
142         if node.name.startswith('_'):
143             return
144         self._namespace.nodes.append(node)
145         self._names.names[node.name] = (None, node)
146
147     def _strip_namespace_func(self, name):
148         prefix = self._namespace.name.lower() + '_'
149         if name.lower().startswith(prefix):
150             name = name[len(prefix):]
151         else:
152             prefix = to_underscores(self._namespace.name).lower() + '_'
153             if name.lower().startswith(prefix):
154                 name = name[len(prefix):]
155         return self.remove_prefix(name, isfunction=True)
156
157     def remove_prefix(self, name, isfunction=False):
158         # when --strip-prefix=g:
159         #   GHashTable -> HashTable
160         #   g_hash_table_new -> hash_table_new
161         prefix = self._strip_prefix.lower()
162         if isfunction:
163             prefix += '_'
164         if len(name) > len(prefix) and name.lower().startswith(prefix):
165             name = name[len(prefix):]
166
167         while name.startswith('_'):
168             name = name[1:]
169         return name
170
171     def _traverse_one(self, symbol, stype=None):
172         assert isinstance(symbol, SourceSymbol), symbol
173
174         if stype is None:
175             stype = symbol.type
176         if stype == CSYMBOL_TYPE_FUNCTION:
177             try:
178                 return self._create_function(symbol)
179             except SkipError:
180                 return
181         elif stype == CSYMBOL_TYPE_TYPEDEF:
182             return self._create_typedef(symbol)
183         elif stype == CSYMBOL_TYPE_STRUCT:
184             return self._create_struct(symbol)
185         elif stype == CSYMBOL_TYPE_ENUM:
186             return self._create_enum(symbol)
187         elif stype == CSYMBOL_TYPE_OBJECT:
188             return self._create_object(symbol)
189         elif stype == CSYMBOL_TYPE_MEMBER:
190             return self._create_member(symbol)
191         elif stype == CSYMBOL_TYPE_UNION:
192             return self._create_union(symbol)
193         elif stype == CSYMBOL_TYPE_CONST:
194             return self._create_const(symbol)
195         else:
196             raise NotImplementedError(
197                 'Transformer: unhandled symbol: %r' % (symbol, ))
198
199     def _create_enum(self, symbol):
200         members = []
201         for child in symbol.base_type.child_list:
202             name = strip_common_prefix(symbol.ident, child.ident).lower()
203             members.append(Member(name,
204                                   child.const_int,
205                                   child.ident))
206
207         enum_name = self.remove_prefix(symbol.ident)
208         enum = Enum(enum_name, symbol.ident, members)
209         self._names.type_names[symbol.ident] = (None, enum)
210         return enum
211
212     def _create_object(self, symbol):
213         return Member(symbol.ident, symbol.base_type.name,
214                       symbol.ident)
215
216     def _parse_deprecated(self, node, directives):
217         deprecated = directives.get('deprecated', False)
218         if deprecated:
219             deprecated_value = deprecated[0]
220             if ':' in deprecated_value:
221                 # Split out gtk-doc version
222                 (node.deprecated_version, node.deprecated) = \
223                     [x.strip() for x in deprecated_value.split(':', 1)]
224             else:
225                 # No version, just include str
226                 node.deprecated = deprecated_value.strip()
227
228     def _pair_array(self, params, array):
229         if not array.type.length_param_name:
230             return
231         target_name = array.type.length_param_name
232         for i, param in enumerate(params):
233             if param.name == array.type.length_param_name:
234                 array.type.length_param_index = i
235                 return
236         raise ValueError("Unmatched length parameter name %r"\
237                              % (target_name, ))
238
239     def _pair_annotations(self, params):
240         names = {}
241         for param in params:
242             if param.name in names:
243                 raise ValueError("Duplicate parameter name %r"\
244                                      % (param.name, ))
245             names[param.name] = 1
246             if isinstance(param.type, Array):
247                 self._pair_array(params, param)
248
249     # We take the annotations from the parser as strings; here we
250     # want to split them into components, so:
251     # (transfer full) -> {'transfer' : [ 'full' ]}
252
253     def _parse_options(self, options):
254         ret = {}
255         ws_re = re.compile(r'\s+')
256         for opt in options:
257             items = ws_re.split(opt)
258             ret[items[0]] = items[1:]
259         return ret
260
261     def _create_function(self, symbol):
262         directives = symbol.directives()
263         parameters = list(self._create_parameters(
264             symbol.base_type, directives))
265         self._pair_annotations(parameters)
266         return_ = self._create_return(symbol.base_type.base_type,
267                                       directives.get('return', {}))
268         name = self._strip_namespace_func(symbol.ident)
269         func = Function(name, return_, parameters, symbol.ident)
270         self._parse_deprecated(func, directives)
271         return func
272
273     def _create_source_type(self, source_type):
274         if source_type is None:
275             return 'None'
276         if source_type.type == CTYPE_VOID:
277             value = 'void'
278         elif source_type.type == CTYPE_BASIC_TYPE:
279             value = source_type.name
280         elif source_type.type == CTYPE_TYPEDEF:
281             value = source_type.name
282         elif source_type.type == CTYPE_ARRAY:
283             return self._create_source_type(source_type.base_type)
284         elif source_type.type == CTYPE_POINTER:
285             value = self._create_source_type(source_type.base_type) + '*'
286         else:
287             value = 'any'
288         return value
289
290     def _create_parameters(self, base_type, directives=None):
291         if directives is None:
292             dirs = {}
293         else:
294             dirs = directives
295
296         # warn if we see annotations for unknown parameters
297         param_names = set(child.ident for child in base_type.child_list)
298         dirs_for = set(dirs)
299         dirs_for = dirs_for.difference(param_names)
300         dirs_for.discard('return')
301         if dirs_for:
302             print 'Unexpected annotations for %s, parameters are %s' % (
303                 list(dirs_for), list(param_names), )
304
305         for child in base_type.child_list:
306             yield self._create_parameter(
307                 child, dirs.get(child.ident, {}))
308
309     def _create_member(self, symbol):
310         ctype = symbol.base_type.type
311         if (ctype == CTYPE_POINTER and
312             symbol.base_type.base_type.type == CTYPE_FUNCTION):
313             node = self._create_callback(symbol)
314         else:
315             opts = {}
316             if ctype == CTYPE_ARRAY:
317                 opts['array'] = []
318                 child_list = list(symbol.base_type.child_list)
319                 if child_list:
320                     size_opt = 'fixed-size=%d' % (child_list[0].const_int, )
321                     opts['array'].append(size_opt)
322             ftype = self._create_type(symbol.base_type, opts, True)
323             # Fields are assumed to be read-write
324             # (except for Objects, see also glibtransformer.py)
325             node = Field(symbol.ident, ftype, symbol.ident,
326                        readable=True, writable=True, bits=symbol.const_int)
327         return node
328
329     def _create_typedef(self, symbol):
330         ctype = symbol.base_type.type
331         if (ctype == CTYPE_POINTER and
332             symbol.base_type.base_type.type == CTYPE_FUNCTION):
333             node = self._create_callback(symbol)
334         elif ctype == CTYPE_STRUCT:
335             node = self._create_typedef_struct(symbol)
336         elif ctype == CTYPE_UNION:
337             node = self._create_typedef_union(symbol)
338         elif ctype == CTYPE_ENUM:
339             return self._create_enum(symbol)
340         elif ctype in (CTYPE_TYPEDEF,
341                        CTYPE_POINTER,
342                        CTYPE_BASIC_TYPE,
343                        CTYPE_VOID):
344             name = self.remove_prefix(symbol.ident)
345             if symbol.base_type.name:
346                 target = self.remove_prefix(symbol.base_type.name)
347             else:
348                 target = 'none'
349             if name in type_names:
350                 return None
351             return Alias(name, target, ctype=symbol.ident)
352         else:
353             raise NotImplementedError(
354                 "symbol %r of type %s" % (symbol.ident, ctype_name(ctype)))
355         return node
356
357     def _parse_ctype(self, ctype):
358         # First look up the ctype including any pointers;
359         # a few type names like 'char*' have their own aliases
360         # and we need pointer information for those.
361         firstpass = type_name_from_ctype(ctype)
362
363         # Remove all pointers - we require standard calling
364         # conventions.  For example, an 'int' is always passed by
365         # value (unless it's out or inout).
366         derefed = firstpass.replace('*', '')
367
368         # Canonicalize our type again, this time without the pointer;
369         # this ensures we turn e.g. plain "guint" => "int"
370         return type_name_from_ctype(derefed)
371
372     def _create_type(self, source_type, options, is_param):
373         ctype = self._create_source_type(source_type)
374         if ctype == 'va_list':
375             raise SkipError()
376         # FIXME: FILE* should not be skipped, it should be handled
377         #        properly instead
378         elif ctype == 'FILE*':
379             raise SkipError
380
381         # Now check for a list/map/array type
382         if ctype in self._list_ctypes:
383             param = options.get('element-type')
384             if param:
385                 contained_type = self._parse_ctype(param[0])
386             else:
387                 contained_type = None
388             derefed_name = self._parse_ctype(ctype)
389             rettype = List(derefed_name,
390                            ctype,
391                            contained_type)
392         elif ctype in self._map_ctypes:
393             param = options.get('element-type')
394             if param:
395                 key_type = self._parse_ctype(param[0])
396                 value_type = self._parse_ctype(param[1])
397             else:
398                 key_type = None
399                 value_type = None
400             derefed_name = self._parse_ctype(ctype)
401             rettype = Map(derefed_name,
402                           ctype,
403                           key_type, value_type)
404         elif (ctype in default_array_types) or ('array' in options):
405             derefed_name = ctype[:-1] if ctype[-1] == '*' else ctype
406             rettype = Array(ctype,
407                             self._parse_ctype(derefed_name))
408             array_opts = dict([opt.split('=')
409                                for opt in options.get('array', [])])
410             if 'length' in array_opts:
411                 rettype.length_param_name = array_opts['length']
412             if 'fixed-size' in array_opts:
413                 rettype.size = array_opts['fixed-size']
414                 rettype.zeroterminated = False
415         else:
416             derefed_name = self._parse_ctype(ctype)
417             rettype = Type(derefed_name, ctype)
418
419         # Deduce direction for some types passed by reference that
420         # aren't arrays; modifies the options array.
421         if ('array' not in options and
422             not ('out' in options or
423                  'in' in options or
424                  'inout' in options or
425                  'in-out' in options) and
426             source_type.type == CTYPE_POINTER and
427             derefed_name in default_out_types):
428             options['out'] = []
429
430         if 'transfer' in options:
431             # Transfer is specified, we don't question it.
432             return rettype
433
434         canontype = type_name_from_ctype(ctype)
435
436         # Since no transfer is specified, we drop into a bunch of
437         # heuristics to guess it.  This mutates the options array to
438         # set the 'transfer' option.
439         # Note that we inferred the transfer
440         options['transfer-inferred'] = []
441         stype = source_type
442         if canontype == TYPE_STRING:
443             # It's a string - we just look at 'const'
444             if source_type.base_type.type_qualifier & TYPE_QUALIFIER_CONST:
445                 options['transfer'] = ['none']
446             else:
447                 options['transfer'] = ['full']
448         elif 'array' in options or stype.type == CTYPE_ARRAY:
449             # It's rare to mutate arrays in public GObject APIs
450             options['transfer'] = ['none']
451         elif (canontype in BASIC_GIR_TYPES or
452               canontype == TYPE_NONE or
453               stype.type == CTYPE_ENUM):
454             # Basic types default to 'none'
455             options['transfer'] = ['none']
456         elif (stype.type == CTYPE_POINTER and
457               stype.base_type.type_qualifier & TYPE_QUALIFIER_CONST):
458             # Anything with 'const' gets none
459             options['transfer'] = ['none']
460         elif is_param and stype.type == CTYPE_POINTER:
461             # For generic pointer types, let's look at the argument
462             # direction.  An out/inout argument gets full, everything
463             # else none.
464             if ('out' in options or
465                 'inout' in options or
466                 'in-out' in options):
467                 options['transfer'] = ['full']
468             else:
469                 options['transfer'] = ['none']
470         else:
471             # For anything else we default to none for parameters;
472             # this covers enums and possibly some other corner cases.
473             # Return values of structures and the like will end up
474             # full.
475             if is_param:
476                 options['transfer'] = ['none']
477             else:
478                 options['transfer'] = ['full']
479
480         return rettype
481
482     def _handle_generic_param_options(self, param, options):
483         for option, data in options.iteritems():
484             if option == 'transfer':
485                 if data:
486                     depth = data[0]
487                     if depth not in ('none', 'container', 'full'):
488                         raise ValueError("Invalid transfer %r" % (depth, ))
489                 else:
490                     depth = 'full'
491                 param.transfer = depth
492             elif option == 'transfer-inferred':
493                 # This is a purely internal flag; we don't expect
494                 # people to write it
495                 param.transfer_inferred = True
496
497     def _create_parameter(self, symbol, options):
498         options = self._parse_options(options)
499         if symbol.type == CSYMBOL_TYPE_ELLIPSIS:
500             ptype = Varargs()
501             if 'transfer' not in options:
502                 options['transfer'] = ['none']
503         else:
504             ptype = self._create_type(symbol.base_type, options, True)
505         param = Parameter(symbol.ident, ptype)
506         for option, data in options.iteritems():
507             if option in ['in-out', 'inout']:
508                 param.direction = 'inout'
509             elif option == 'in':
510                 param.direction = 'in'
511             elif option == 'out':
512                 param.direction = 'out'
513             elif option == 'allow-none':
514                 param.allow_none = True
515             elif option.startswith(('element-type', 'array')):
516                 pass
517             elif option in ('transfer', 'transfer-inferred'):
518                 pass
519             else:
520                 print 'Unhandled parameter annotation option: %r' % (
521                     option, )
522         self._handle_generic_param_options(param, options)
523
524         assert param.transfer is not None, param
525         return param
526
527     def _create_return(self, source_type, options=None):
528         if options is None:
529             options_map = {}
530         else:
531             options_map = self._parse_options(options)
532         rtype = self._create_type(source_type, options_map, False)
533         rtype = self.resolve_param_type(rtype)
534         return_ = Return(rtype)
535         self._handle_generic_param_options(return_, options_map)
536         for option, data in options_map.iteritems():
537             if option in ('transfer', 'transfer-inferred',
538                           'element-type', 'out'):
539                 pass
540             else:
541                 print 'Unhandled return type annotation option: %r' % (
542                     option, )
543
544         assert return_.transfer is not None, return_
545         return return_
546
547     def _create_const(self, symbol):
548         name = self.remove_prefix(symbol.ident)
549         if symbol.const_string is None:
550             type_name = 'int'
551             value = symbol.const_int
552         else:
553             type_name = 'utf8'
554             value = symbol.const_string
555         const = Constant(name, type_name, value)
556         return const
557
558     def _create_typedef_struct(self, symbol):
559         name = self.remove_prefix(symbol.ident)
560         struct = Struct(name, symbol.ident)
561         self._typedefs_ns[symbol.ident] = struct
562         self._create_struct(symbol)
563         return struct
564
565     def _create_typedef_union(self, symbol):
566         name = self.remove_prefix(symbol.ident)
567         union = Union(name, symbol.ident)
568         self._typedefs_ns[symbol.ident] = union
569         self._create_union(symbol)
570         return union
571
572     def _create_struct(self, symbol):
573         struct = self._typedefs_ns.get(symbol.ident, None)
574         if struct is None:
575             # This is a bit of a hack; really we should try
576             # to resolve through the typedefs to find the real
577             # name
578             if symbol.ident.startswith('_'):
579                 name = symbol.ident[1:]
580             else:
581                 name = symbol.ident
582             name = self.remove_prefix(name)
583             struct = Struct(name, symbol.ident)
584
585         for child in symbol.base_type.child_list:
586             field = self._traverse_one(child)
587             if field:
588                 struct.fields.append(field)
589
590         return struct
591
592     def _create_union(self, symbol):
593         union = self._typedefs_ns.get(symbol.ident, None)
594         if union is None:
595             # This is a bit of a hack; really we should try
596             # to resolve through the typedefs to find the real
597             # name
598             if symbol.ident.startswith('_'):
599                 name = symbol.ident[1:]
600             else:
601                 name = symbol.ident
602             name = self.remove_prefix(name)
603             union = Union(name, symbol.ident)
604
605         for child in symbol.base_type.child_list:
606             field = self._traverse_one(child)
607             if field:
608                 union.fields.append(field)
609
610         return union
611
612     def _create_callback(self, symbol):
613         directives = symbol.directives()
614         parameters = self._create_parameters(symbol.base_type.base_type,
615             directives)
616         retval = self._create_return(symbol.base_type.base_type.base_type,
617             directives.get('return', {}))
618         if symbol.ident.find('_') > 0:
619             name = self.remove_prefix(symbol.ident, True)
620         else:
621             name = self.remove_prefix(symbol.ident)
622         return Callback(name, retval, list(parameters), symbol.ident)
623
624     def _typepair_to_str(self, item):
625         nsname, item = item
626         if nsname is None:
627             return item.name
628         return '%s.%s' % (nsname, item.name)
629
630     def _resolve_type_name_1(self, type_name, ctype, names):
631         # First look using the built-in names
632         if ctype:
633             try:
634                 return type_names[ctype]
635             except KeyError, e:
636                 pass
637         try:
638             return type_names[type_name]
639         except KeyError, e:
640             pass
641         type_name = self.remove_prefix(type_name)
642         resolved = names.aliases.get(type_name)
643         if resolved:
644             return self._typepair_to_str(resolved)
645         resolved = names.names.get(type_name)
646         if resolved:
647             return self._typepair_to_str(resolved)
648         if ctype:
649             ctype = ctype.replace('*', '')
650             resolved = names.ctypes.get(ctype)
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             if isinstance(ptype, (Array, List)):
699                 if ptype.element_type is not None:
700                     ptype.element_type = \
701                         self.resolve_param_type_full(ptype.element_type,
702                                                      names, **kwargs)
703             if isinstance(ptype, Map):
704                 if ptype.key_type is not None:
705                     ptype.key_type = \
706                         self.resolve_param_type_full(ptype.key_type,
707                                                      names, **kwargs)
708                     ptype.value_type = \
709                         self.resolve_param_type_full(ptype.value_type,
710                                                      names, **kwargs)
711         elif isinstance(ptype, basestring):
712             return self.resolve_type_name_full(ptype, None, names, **kwargs)
713         else:
714             raise AssertionError("Unhandled param: %r" % (ptype, ))
715         return ptype
716
717     def resolve_param_type(self, ptype):
718         try:
719             return self.resolve_param_type_full(ptype, self._names)
720         except KeyError, e:
721             return ptype
722
723     def follow_aliases(self, type_name, names):
724         while True:
725             resolved = names.aliases.get(type_name)
726             if resolved:
727                 (ns, alias) = resolved
728                 type_name = alias.target
729             else:
730                 break
731         return type_name