Bug 561617 – Return value array annotations
[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 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, cachestore, generator,
69                  namespace_name, namespace_version):
70         self._cachestore = cachestore
71         self.generator = generator
72         self._namespace = Namespace(namespace_name, namespace_version)
73         self._names = Names()
74         self._typedefs_ns = {}
75         self._strip_prefix = ''
76         self._includes = set()
77         self._includepaths = []
78         self._list_ctypes = []
79         self._map_ctypes = []
80
81     def get_names(self):
82         return self._names
83
84     def get_includes(self):
85         return self._includes
86
87     def set_container_types(self, list_ctypes, map_ctypes):
88         self._list_ctypes = list_ctypes
89         self._map_ctypes = map_ctypes
90
91     def set_strip_prefix(self, strip_prefix):
92         self._strip_prefix = strip_prefix
93
94     def parse(self):
95         nodes = []
96         for symbol in self.generator.get_symbols():
97             node = self._traverse_one(symbol)
98             self._add_node(node)
99         return self._namespace
100
101     def set_include_paths(self, paths):
102         self._includepaths = list(paths)
103
104     def register_include(self, include):
105         if include in self._includes:
106             return
107         filename = self._find_include(include)
108         self._parse_include(filename)
109         self._includes.add(include)
110
111     # Private
112
113     def _find_include(self, include):
114         searchdirs = self._includepaths[:]
115         for path in _xdg_data_dirs:
116             searchdirs.append(os.path.join(path, 'gir'))
117
118         girname = '%s-%s.gir' % (include.name, include.version)
119         for d in searchdirs:
120             path = os.path.join(d, girname)
121             if os.path.exists(path):
122                 return path
123         else:
124             raise ValueError("Couldn't find include %r (search path: %r)"\
125                              % (girname, searchdirs))
126
127     def _parse_include(self, filename):
128         parser = self._cachestore.load(filename)
129         if parser is None:
130             parser = GIRParser()
131             parser.set_include_parsing(True)
132             parser.parse(filename)
133             self._cachestore.store(filename, parser)
134
135         for include in parser.get_includes():
136             self.register_include(include)
137
138         namespace = parser.get_namespace()
139         nsname = namespace.name
140         for node in namespace.nodes:
141             if isinstance(node, Alias):
142                 self._names.aliases[node.name] = (nsname, node)
143             elif isinstance(node, (GLibBoxed, Interface, Class)):
144                 self._names.type_names[node.type_name] = (nsname, node)
145             giname = '%s.%s' % (nsname, node.name)
146             self._names.names[giname] = (nsname, node)
147             if hasattr(node, 'ctype'):
148                 self._names.ctypes[node.ctype] = (nsname, node)
149             elif hasattr(node, 'symbol'):
150                 self._names.ctypes[node.symbol] = (nsname, node)
151
152     def _add_node(self, node):
153         if node is None:
154             return
155         if node.name.startswith('_'):
156             return
157         self._namespace.nodes.append(node)
158         self._names.names[node.name] = (None, node)
159
160     def _strip_namespace_func(self, name):
161         prefix = self._namespace.name.lower() + '_'
162         if name.lower().startswith(prefix):
163             name = name[len(prefix):]
164         else:
165             prefix = to_underscores(self._namespace.name).lower() + '_'
166             if name.lower().startswith(prefix):
167                 name = name[len(prefix):]
168         return self.remove_prefix(name, isfunction=True)
169
170     def remove_prefix(self, name, isfunction=False):
171         # when --strip-prefix=g:
172         #   GHashTable -> HashTable
173         #   g_hash_table_new -> hash_table_new
174         prefix = self._strip_prefix.lower()
175         if isfunction:
176             prefix += '_'
177         if len(name) > len(prefix) and name.lower().startswith(prefix):
178             name = name[len(prefix):]
179
180         while name.startswith('_'):
181             name = name[1:]
182         return name
183
184     def _traverse_one(self, symbol, stype=None):
185         assert isinstance(symbol, SourceSymbol), symbol
186
187         if stype is None:
188             stype = symbol.type
189         if stype == CSYMBOL_TYPE_FUNCTION:
190             try:
191                 return self._create_function(symbol)
192             except SkipError:
193                 return
194         elif stype == CSYMBOL_TYPE_TYPEDEF:
195             return self._create_typedef(symbol)
196         elif stype == CSYMBOL_TYPE_STRUCT:
197             return self._create_struct(symbol)
198         elif stype == CSYMBOL_TYPE_ENUM:
199             return self._create_enum(symbol)
200         elif stype == CSYMBOL_TYPE_OBJECT:
201             return self._create_object(symbol)
202         elif stype == CSYMBOL_TYPE_MEMBER:
203             return self._create_member(symbol)
204         elif stype == CSYMBOL_TYPE_UNION:
205             return self._create_union(symbol)
206         elif stype == CSYMBOL_TYPE_CONST:
207             return self._create_const(symbol)
208         else:
209             raise NotImplementedError(
210                 'Transformer: unhandled symbol: %r' % (symbol, ))
211
212     def _create_enum(self, symbol):
213         members = []
214         for child in symbol.base_type.child_list:
215             name = strip_common_prefix(symbol.ident, child.ident).lower()
216             members.append(Member(name,
217                                   child.const_int,
218                                   child.ident))
219
220         enum_name = self.remove_prefix(symbol.ident)
221         enum = Enum(enum_name, symbol.ident, members)
222         self._names.type_names[symbol.ident] = (None, enum)
223         return enum
224
225     def _create_object(self, symbol):
226         return Member(symbol.ident, symbol.base_type.name,
227                       symbol.ident)
228
229     def _parse_deprecated(self, node, directives):
230         deprecated = directives.get('deprecated', False)
231         if deprecated:
232             deprecated_value = deprecated[0]
233             if ':' in deprecated_value:
234                 # Split out gtk-doc version
235                 (node.deprecated_version, node.deprecated) = \
236                     [x.strip() for x in deprecated_value.split(':', 1)]
237             else:
238                 # No version, just include str
239                 node.deprecated = deprecated_value.strip()
240
241     def _pair_array(self, params, array):
242         if not array.type.length_param_name:
243             return
244         target_name = array.type.length_param_name
245         for i, param in enumerate(params):
246             if param.name == array.type.length_param_name:
247                 array.type.length_param_index = i
248                 return
249         raise ValueError("Unmatched length parameter name %r"\
250                              % (target_name, ))
251
252     def _pair_annotations(self, params, return_):
253         names = {}
254         for param in params:
255             if param.name in names:
256                 raise ValueError("Duplicate parameter name %r"\
257                                      % (param.name, ))
258             names[param.name] = 1
259             if isinstance(param.type, Array):
260                 self._pair_array(params, param)
261
262         if isinstance(return_.type, Array):
263             self._pair_array(params, return_)
264
265     # We take the annotations from the parser as strings; here we
266     # want to split them into components, so:
267     # (transfer full) -> {'transfer' : [ 'full' ]}
268
269     def _parse_options(self, options):
270         ret = {}
271         ws_re = re.compile(r'\s+')
272         for opt in options:
273             items = ws_re.split(opt)
274             ret[items[0]] = items[1:]
275         return ret
276
277     def _create_function(self, symbol):
278         directives = symbol.directives()
279         parameters = list(self._create_parameters(
280             symbol.base_type, directives))
281         return_ = self._create_return(symbol.base_type.base_type,
282                                       directives.get('return', {}))
283         self._pair_annotations(parameters, return_)
284         name = self._strip_namespace_func(symbol.ident)
285         func = Function(name, return_, parameters, symbol.ident)
286         self._parse_deprecated(func, directives)
287         return func
288
289     def _create_source_type(self, source_type):
290         if source_type is None:
291             return 'None'
292         if source_type.type == CTYPE_VOID:
293             value = 'void'
294         elif source_type.type == CTYPE_BASIC_TYPE:
295             value = source_type.name
296         elif source_type.type == CTYPE_TYPEDEF:
297             value = source_type.name
298         elif source_type.type == CTYPE_ARRAY:
299             return self._create_source_type(source_type.base_type)
300         elif source_type.type == CTYPE_POINTER:
301             value = self._create_source_type(source_type.base_type) + '*'
302         else:
303             value = 'any'
304         return value
305
306     def _create_parameters(self, base_type, directives=None):
307         if directives is None:
308             dirs = {}
309         else:
310             dirs = directives
311
312         # warn if we see annotations for unknown parameters
313         param_names = set(child.ident for child in base_type.child_list)
314         dirs_for = set(dirs)
315         dirs_for = dirs_for.difference(param_names)
316         dirs_for.discard('return')
317         if dirs_for:
318             print 'Unexpected annotations for %s, parameters are %s' % (
319                 list(dirs_for), list(param_names), )
320
321         for child in base_type.child_list:
322             yield self._create_parameter(
323                 child, dirs.get(child.ident, {}))
324
325     def _create_member(self, symbol):
326         ctype = symbol.base_type.type
327         if (ctype == CTYPE_POINTER and
328             symbol.base_type.base_type.type == CTYPE_FUNCTION):
329             node = self._create_callback(symbol)
330         else:
331             opts = {}
332             if ctype == CTYPE_ARRAY:
333                 opts['array'] = []
334                 child_list = list(symbol.base_type.child_list)
335                 if child_list:
336                     size_opt = 'fixed-size=%d' % (child_list[0].const_int, )
337                     opts['array'].append(size_opt)
338             ftype = self._create_type(symbol.base_type, opts,
339                                       is_param=False, is_retval=False)
340             ftype = self.resolve_param_type(ftype)
341             # Fields are assumed to be read-write
342             # (except for Objects, see also glibtransformer.py)
343             node = Field(symbol.ident, ftype, symbol.ident,
344                        readable=True, writable=True, bits=symbol.const_int)
345         return node
346
347     def _create_typedef(self, symbol):
348         ctype = symbol.base_type.type
349         if (ctype == CTYPE_POINTER and
350             symbol.base_type.base_type.type == CTYPE_FUNCTION):
351             node = self._create_callback(symbol)
352         elif (ctype == CTYPE_POINTER and
353             symbol.base_type.base_type.type == CTYPE_STRUCT):
354             node = self._create_typedef_struct(symbol, disguised=True)
355         elif ctype == CTYPE_STRUCT:
356             node = self._create_typedef_struct(symbol)
357         elif ctype == CTYPE_UNION:
358             node = self._create_typedef_union(symbol)
359         elif ctype == CTYPE_ENUM:
360             return self._create_enum(symbol)
361         elif ctype in (CTYPE_TYPEDEF,
362                        CTYPE_POINTER,
363                        CTYPE_BASIC_TYPE,
364                        CTYPE_VOID):
365             name = self.remove_prefix(symbol.ident)
366             if symbol.base_type.name:
367                 target = self.remove_prefix(symbol.base_type.name)
368             else:
369                 target = 'none'
370             if name in type_names:
371                 return None
372             return Alias(name, target, ctype=symbol.ident)
373         else:
374             raise NotImplementedError(
375                 "symbol %r of type %s" % (symbol.ident, ctype_name(ctype)))
376         return node
377
378     def _canonicalize_ctype(self, ctype):
379         # First look up the ctype including any pointers;
380         # a few type names like 'char*' have their own aliases
381         # and we need pointer information for those.
382         firstpass = type_name_from_ctype(ctype)
383
384         # If we have a particular alias for this, skip deep
385         # canonicalization to prevent changing
386         # e.g. char* -> int8*
387         if firstpass != ctype:
388             return firstpass
389
390         # We're also done if the type is already a fundamental
391         # known type, or there are no pointers.
392         if ctype in type_names or not firstpass.endswith('*'):
393             return firstpass
394
395         # We have a pointer type.
396         # Strip the end pointer, canonicalize our base type
397         base = firstpass[:-1]
398         canonical_base = self._canonicalize_ctype(base)
399
400         # Append the pointer again
401         canonical = canonical_base + '*'
402
403         return canonical
404
405     def parse_ctype(self, ctype, is_member=False):
406         canonical = self._canonicalize_ctype(ctype)
407
408         # Remove all pointers - we require standard calling
409         # conventions.  For example, an 'int' is always passed by
410         # value (unless it's out or inout).
411         derefed_typename = canonical.replace('*', '')
412
413         # Preserve "pointerness" of struct/union members
414         if (is_member and canonical.endswith('*') and
415             derefed_typename in BASIC_GIR_TYPES):
416             return 'any'
417         else:
418             return derefed_typename
419
420     def _create_type(self, source_type, options, is_param, is_retval):
421         ctype = self._create_source_type(source_type)
422         if ctype == 'va_list':
423             raise SkipError()
424         # FIXME: FILE* should not be skipped, it should be handled
425         #        properly instead
426         elif ctype == 'FILE*':
427             raise SkipError
428
429         canonical_ctype = self._canonicalize_ctype(ctype)
430
431         # Now check for a list/map/array type
432         if canonical_ctype in self._list_ctypes:
433             param = options.get('element-type')
434             if param:
435                 contained_type = self.parse_ctype(param[0])
436             else:
437                 contained_type = None
438             derefed_name = self.parse_ctype(ctype)
439             rettype = List(derefed_name,
440                            ctype,
441                            contained_type)
442         elif canonical_ctype in self._map_ctypes:
443             param = options.get('element-type')
444             if param:
445                 key_type = self.parse_ctype(param[0])
446                 value_type = self.parse_ctype(param[1])
447             else:
448                 key_type = None
449                 value_type = None
450             derefed_name = self.parse_ctype(ctype)
451             rettype = Map(derefed_name,
452                           ctype,
453                           key_type, value_type)
454         elif ((is_param and canonical_ctype in default_array_types)
455               or ('array' in options)):
456             if canonical_ctype[-1] == '*':
457                 derefed_name = canonical_ctype[:-1]
458             else:
459                 derefed_name = canonical_ctype
460             rettype = Array(ctype,
461                             self.parse_ctype(derefed_name))
462             array_opts = dict([opt.split('=')
463                                for opt in options.get('array', [])])
464             if 'length' in array_opts:
465                 rettype.length_param_name = array_opts['length']
466                 rettype.zeroterminated = False
467             if 'fixed-size' in array_opts:
468                 rettype.size = array_opts['fixed-size']
469                 rettype.zeroterminated = False
470             if 'zero-terminated' in array_opts:
471                 rettype.zeroterminated = array_opts['zero-terminated'] != '0'
472         else:
473             derefed_name = self.parse_ctype(ctype,
474                                             not (is_param or is_retval))
475             rettype = Type(derefed_name, ctype)
476
477         # Deduce direction for some types passed by reference that
478         # aren't arrays; modifies the options array.
479         if ('array' not in options and
480             not ('out' in options or
481                  'in' in options or
482                  'inout' in options or
483                  'in-out' in options) and
484             source_type.type == CTYPE_POINTER and
485             derefed_name in default_out_types):
486             options['out'] = []
487
488         if 'transfer' in options:
489             # Transfer is specified, we don't question it.
490             return rettype
491
492         canontype = type_name_from_ctype(ctype)
493
494         # Since no transfer is specified, we drop into a bunch of
495         # heuristics to guess it.  This mutates the options array to
496         # set the 'transfer' option.
497         # Note that we inferred the transfer
498         options['transfer-inferred'] = []
499         stype = source_type
500         if canontype == TYPE_STRING:
501             # It's a string - we just look at 'const'
502             if source_type.base_type.type_qualifier & TYPE_QUALIFIER_CONST:
503                 options['transfer'] = ['none']
504             else:
505                 options['transfer'] = ['full']
506         elif 'array' in options or stype.type == CTYPE_ARRAY:
507             # It's rare to mutate arrays in public GObject APIs
508             options['transfer'] = ['none']
509         elif (canontype in BASIC_GIR_TYPES or
510               canontype == TYPE_NONE or
511               stype.type == CTYPE_ENUM):
512             # Basic types default to 'none'
513             options['transfer'] = ['none']
514         elif (stype.type == CTYPE_POINTER and
515               stype.base_type.type_qualifier & TYPE_QUALIFIER_CONST):
516             # Anything with 'const' gets none
517             options['transfer'] = ['none']
518         elif is_param and stype.type == CTYPE_POINTER:
519             # For generic pointer types, let's look at the argument
520             # direction.  An out/inout argument gets full, everything
521             # else none.
522             if ('out' in options or
523                 'inout' in options or
524                 'in-out' in options):
525                 options['transfer'] = ['full']
526             else:
527                 options['transfer'] = ['none']
528         else:
529             # For anything else we default to none for parameters;
530             # this covers enums and possibly some other corner cases.
531             # Return values of structures and the like will end up
532             # full.
533             if is_param:
534                 options['transfer'] = ['none']
535             else:
536                 options['transfer'] = ['full']
537
538         return rettype
539
540     def _handle_generic_param_options(self, param, options):
541         for option, data in options.iteritems():
542             if option == 'transfer':
543                 if data:
544                     depth = data[0]
545                     if depth not in ('none', 'container', 'full'):
546                         raise ValueError("Invalid transfer %r" % (depth, ))
547                 else:
548                     depth = 'full'
549                 param.transfer = depth
550             elif option == 'transfer-inferred':
551                 # This is a purely internal flag; we don't expect
552                 # people to write it
553                 param.transfer_inferred = True
554
555     def _create_parameter(self, symbol, options):
556         options = self._parse_options(options)
557         if symbol.type == CSYMBOL_TYPE_ELLIPSIS:
558             ptype = Varargs()
559             if 'transfer' not in options:
560                 options['transfer'] = ['none']
561         else:
562             ptype = self._create_type(symbol.base_type, options,
563                                       is_param=True, is_retval=False)
564             ptype = self.resolve_param_type(ptype)
565         param = Parameter(symbol.ident, ptype)
566         for option, data in options.iteritems():
567             if option in ['in-out', 'inout']:
568                 param.direction = 'inout'
569             elif option == 'in':
570                 param.direction = 'in'
571             elif option == 'out':
572                 param.direction = 'out'
573             elif option == 'allow-none':
574                 param.allow_none = True
575             elif option.startswith(('element-type', 'array')):
576                 pass
577             elif option in ('transfer', 'transfer-inferred'):
578                 pass
579             else:
580                 print 'Unhandled parameter annotation option: %r' % (
581                     option, )
582         self._handle_generic_param_options(param, options)
583
584         assert param.transfer is not None, param
585         return param
586
587     def _create_return(self, source_type, options=None):
588         if options is None:
589             options_map = {}
590         else:
591             options_map = self._parse_options(options)
592         rtype = self._create_type(source_type, options_map,
593                                   is_param=False, is_retval=True)
594         rtype = self.resolve_param_type(rtype)
595         return_ = Return(rtype)
596         self._handle_generic_param_options(return_, options_map)
597         for option, data in options_map.iteritems():
598             if option in ('transfer', 'transfer-inferred',
599                           'element-type', 'out'):
600                 pass
601             elif option.startswith(('element-type', 'array')):
602                 pass
603             else:
604                 print 'Unhandled return type annotation option: %r' % (
605                     option, )
606
607         assert return_.transfer is not None, return_
608         return return_
609
610     def _create_const(self, symbol):
611         name = self.remove_prefix(symbol.ident)
612         if symbol.const_string is None:
613             type_name = 'int'
614             value = symbol.const_int
615         else:
616             type_name = 'utf8'
617             value = symbol.const_string
618         const = Constant(name, type_name, value)
619         return const
620
621     def _create_typedef_struct(self, symbol, disguised=False):
622         name = self.remove_prefix(symbol.ident)
623         struct = Struct(name, symbol.ident, disguised)
624         self._typedefs_ns[symbol.ident] = struct
625         self._create_struct(symbol)
626         return struct
627
628     def _create_typedef_union(self, symbol):
629         name = self.remove_prefix(symbol.ident)
630         union = Union(name, symbol.ident)
631         self._typedefs_ns[symbol.ident] = union
632         self._create_union(symbol)
633         return union
634
635     def _create_struct(self, symbol):
636         struct = self._typedefs_ns.get(symbol.ident, None)
637         if struct is None:
638             # This is a bit of a hack; really we should try
639             # to resolve through the typedefs to find the real
640             # name
641             if symbol.ident.startswith('_'):
642                 name = symbol.ident[1:]
643             else:
644                 name = symbol.ident
645             name = self.remove_prefix(name)
646             struct = Struct(name, symbol.ident)
647
648         for child in symbol.base_type.child_list:
649             field = self._traverse_one(child)
650             if field:
651                 struct.fields.append(field)
652
653         return struct
654
655     def _create_union(self, symbol):
656         union = self._typedefs_ns.get(symbol.ident, None)
657         if union is None:
658             # This is a bit of a hack; really we should try
659             # to resolve through the typedefs to find the real
660             # name
661             if symbol.ident.startswith('_'):
662                 name = symbol.ident[1:]
663             else:
664                 name = symbol.ident
665             name = self.remove_prefix(name)
666             union = Union(name, symbol.ident)
667
668         for child in symbol.base_type.child_list:
669             field = self._traverse_one(child)
670             if field:
671                 union.fields.append(field)
672
673         return union
674
675     def _create_callback(self, symbol):
676         directives = symbol.directives()
677         parameters = self._create_parameters(symbol.base_type.base_type,
678             directives)
679         retval = self._create_return(symbol.base_type.base_type.base_type,
680             directives.get('return', {}))
681         if symbol.ident.find('_') > 0:
682             name = self.remove_prefix(symbol.ident, True)
683         else:
684             name = self.remove_prefix(symbol.ident)
685         return Callback(name, retval, list(parameters), symbol.ident)
686
687     def _typepair_to_str(self, item):
688         nsname, item = item
689         if nsname is None:
690             return item.name
691         return '%s.%s' % (nsname, item.name)
692
693     def _resolve_type_name_1(self, type_name, ctype, names):
694         # First look using the built-in names
695         if ctype:
696             try:
697                 return type_names[ctype]
698             except KeyError, e:
699                 pass
700         try:
701             return type_names[type_name]
702         except KeyError, e:
703             pass
704
705         if ctype:
706             ctype = ctype.replace('*', '')
707             resolved = names.ctypes.get(ctype)
708             if resolved:
709                 return self._typepair_to_str(resolved)
710         type_name = self.remove_prefix(type_name)
711         resolved = names.aliases.get(type_name)
712         if resolved:
713             return self._typepair_to_str(resolved)
714         resolved = names.names.get(type_name)
715         if resolved:
716             return self._typepair_to_str(resolved)
717         resolved = names.type_names.get(type_name)
718         if resolved:
719             return self._typepair_to_str(resolved)
720         raise KeyError("failed to find %r" % (type_name, ))
721
722     def resolve_type_name_full(self, type_name, ctype,
723                                names, allow_invalid=True):
724         try:
725             return self._resolve_type_name_1(type_name, ctype, names)
726         except KeyError, e:
727             try:
728                 return self._resolve_type_name_1(type_name, ctype, self._names)
729             except KeyError, e:
730                 if not allow_invalid:
731                     raise
732                 return type_name
733
734     def resolve_type_name(self, type_name, ctype=None):
735         try:
736             return self.resolve_type_name_full(type_name, ctype, self._names)
737         except KeyError, e:
738             return type_name
739
740     def gtypename_to_giname(self, gtname, names):
741         resolved = names.type_names.get(gtname)
742         if resolved:
743             return self._typepair_to_str(resolved)
744         resolved = self._names.type_names.get(gtname)
745         if resolved:
746             return self._typepair_to_str(resolved)
747         raise KeyError("Failed to resolve GType name: %r" % (gtname, ))
748
749     def ctype_of(self, obj):
750         if hasattr(obj, 'ctype'):
751             return obj.ctype
752         elif hasattr(obj, 'symbol'):
753             return obj.symbol
754         else:
755             return None
756
757     def resolve_param_type_full(self, ptype, names, **kwargs):
758         if isinstance(ptype, Node):
759             ptype.name = self.resolve_type_name_full(ptype.name,
760                                                      self.ctype_of(ptype),
761                                                      names, **kwargs)
762             if isinstance(ptype, (Array, List)):
763                 if ptype.element_type is not None:
764                     ptype.element_type = \
765                         self.resolve_param_type_full(ptype.element_type,
766                                                      names, **kwargs)
767             if isinstance(ptype, Map):
768                 if ptype.key_type is not None:
769                     ptype.key_type = \
770                         self.resolve_param_type_full(ptype.key_type,
771                                                      names, **kwargs)
772                     ptype.value_type = \
773                         self.resolve_param_type_full(ptype.value_type,
774                                                      names, **kwargs)
775         elif isinstance(ptype, basestring):
776             return self.resolve_type_name_full(ptype, None, names, **kwargs)
777         else:
778             raise AssertionError("Unhandled param: %r" % (ptype, ))
779         return ptype
780
781     def resolve_param_type(self, ptype):
782         try:
783             return self.resolve_param_type_full(ptype, self._names)
784         except KeyError, e:
785             return ptype
786
787     def follow_aliases(self, type_name, names):
788         while True:
789             resolved = names.aliases.get(type_name)
790             if resolved:
791                 (ns, alias) = resolved
792                 type_name = alias.target
793             else:
794                 break
795         return type_name