Bug 558436 - avoid having scanner load app code
[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):
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     # We take the annotations from the parser as strings; here we
263     # want to split them into components, so:
264     # (transfer full) -> {'transfer' : [ 'full' ]}
265
266     def _parse_options(self, options):
267         ret = {}
268         ws_re = re.compile(r'\s+')
269         for opt in options:
270             items = ws_re.split(opt)
271             ret[items[0]] = items[1:]
272         return ret
273
274     def _create_function(self, symbol):
275         directives = symbol.directives()
276         parameters = list(self._create_parameters(
277             symbol.base_type, directives))
278         self._pair_annotations(parameters)
279         return_ = self._create_return(symbol.base_type.base_type,
280                                       directives.get('return', {}))
281         name = self._strip_namespace_func(symbol.ident)
282         func = Function(name, return_, parameters, symbol.ident)
283         self._parse_deprecated(func, directives)
284         return func
285
286     def _create_source_type(self, source_type):
287         if source_type is None:
288             return 'None'
289         if source_type.type == CTYPE_VOID:
290             value = 'void'
291         elif source_type.type == CTYPE_BASIC_TYPE:
292             value = source_type.name
293         elif source_type.type == CTYPE_TYPEDEF:
294             value = source_type.name
295         elif source_type.type == CTYPE_ARRAY:
296             return self._create_source_type(source_type.base_type)
297         elif source_type.type == CTYPE_POINTER:
298             value = self._create_source_type(source_type.base_type) + '*'
299         else:
300             value = 'any'
301         return value
302
303     def _create_parameters(self, base_type, directives=None):
304         if directives is None:
305             dirs = {}
306         else:
307             dirs = directives
308
309         # warn if we see annotations for unknown parameters
310         param_names = set(child.ident for child in base_type.child_list)
311         dirs_for = set(dirs)
312         dirs_for = dirs_for.difference(param_names)
313         dirs_for.discard('return')
314         if dirs_for:
315             print 'Unexpected annotations for %s, parameters are %s' % (
316                 list(dirs_for), list(param_names), )
317
318         for child in base_type.child_list:
319             yield self._create_parameter(
320                 child, dirs.get(child.ident, {}))
321
322     def _create_member(self, symbol):
323         ctype = symbol.base_type.type
324         if (ctype == CTYPE_POINTER and
325             symbol.base_type.base_type.type == CTYPE_FUNCTION):
326             node = self._create_callback(symbol)
327         else:
328             opts = {}
329             if ctype == CTYPE_ARRAY:
330                 opts['array'] = []
331                 child_list = list(symbol.base_type.child_list)
332                 if child_list:
333                     size_opt = 'fixed-size=%d' % (child_list[0].const_int, )
334                     opts['array'].append(size_opt)
335             ftype = self._create_type(symbol.base_type, opts, True)
336             ftype = self.resolve_param_type(ftype)
337             # Fields are assumed to be read-write
338             # (except for Objects, see also glibtransformer.py)
339             node = Field(symbol.ident, ftype, symbol.ident,
340                        readable=True, writable=True, bits=symbol.const_int)
341         return node
342
343     def _create_typedef(self, symbol):
344         ctype = symbol.base_type.type
345         if (ctype == CTYPE_POINTER and
346             symbol.base_type.base_type.type == CTYPE_FUNCTION):
347             node = self._create_callback(symbol)
348         elif (ctype == CTYPE_POINTER and
349             symbol.base_type.base_type.type == CTYPE_STRUCT):
350             node = self._create_typedef_struct(symbol, disguised=True)
351         elif ctype == CTYPE_STRUCT:
352             node = self._create_typedef_struct(symbol)
353         elif ctype == CTYPE_UNION:
354             node = self._create_typedef_union(symbol)
355         elif ctype == CTYPE_ENUM:
356             return self._create_enum(symbol)
357         elif ctype in (CTYPE_TYPEDEF,
358                        CTYPE_POINTER,
359                        CTYPE_BASIC_TYPE,
360                        CTYPE_VOID):
361             name = self.remove_prefix(symbol.ident)
362             if symbol.base_type.name:
363                 target = self.remove_prefix(symbol.base_type.name)
364             else:
365                 target = 'none'
366             if name in type_names:
367                 return None
368             return Alias(name, target, ctype=symbol.ident)
369         else:
370             raise NotImplementedError(
371                 "symbol %r of type %s" % (symbol.ident, ctype_name(ctype)))
372         return node
373
374     def parse_ctype(self, ctype):
375         # First look up the ctype including any pointers;
376         # a few type names like 'char*' have their own aliases
377         # and we need pointer information for those.
378         firstpass = type_name_from_ctype(ctype)
379
380         # Remove all pointers - we require standard calling
381         # conventions.  For example, an 'int' is always passed by
382         # value (unless it's out or inout).
383         derefed = firstpass.replace('*', '')
384
385         # Canonicalize our type again, this time without the pointer;
386         # this ensures we turn e.g. plain "guint" => "int"
387         return type_name_from_ctype(derefed)
388
389     def _create_type(self, source_type, options, is_param):
390         ctype = self._create_source_type(source_type)
391         if ctype == 'va_list':
392             raise SkipError()
393         # FIXME: FILE* should not be skipped, it should be handled
394         #        properly instead
395         elif ctype == 'FILE*':
396             raise SkipError
397
398         # Now check for a list/map/array type
399         if ctype in self._list_ctypes:
400             param = options.get('element-type')
401             if param:
402                 contained_type = self.parse_ctype(param[0])
403             else:
404                 contained_type = None
405             derefed_name = self.parse_ctype(ctype)
406             rettype = List(derefed_name,
407                            ctype,
408                            contained_type)
409         elif ctype in self._map_ctypes:
410             param = options.get('element-type')
411             if param:
412                 key_type = self.parse_ctype(param[0])
413                 value_type = self.parse_ctype(param[1])
414             else:
415                 key_type = None
416                 value_type = None
417             derefed_name = self.parse_ctype(ctype)
418             rettype = Map(derefed_name,
419                           ctype,
420                           key_type, value_type)
421         elif (ctype in default_array_types) or ('array' in options):
422             derefed_name = ctype[:-1] if ctype[-1] == '*' else ctype
423             rettype = Array(ctype,
424                             self.parse_ctype(derefed_name))
425             array_opts = dict([opt.split('=')
426                                for opt in options.get('array', [])])
427             if 'length' in array_opts:
428                 rettype.length_param_name = array_opts['length']
429             if 'fixed-size' in array_opts:
430                 rettype.size = array_opts['fixed-size']
431                 rettype.zeroterminated = False
432         else:
433             derefed_name = self.parse_ctype(ctype)
434             rettype = Type(derefed_name, ctype)
435
436         # Deduce direction for some types passed by reference that
437         # aren't arrays; modifies the options array.
438         if ('array' not in options and
439             not ('out' in options or
440                  'in' in options or
441                  'inout' in options or
442                  'in-out' in options) and
443             source_type.type == CTYPE_POINTER and
444             derefed_name in default_out_types):
445             options['out'] = []
446
447         if 'transfer' in options:
448             # Transfer is specified, we don't question it.
449             return rettype
450
451         canontype = type_name_from_ctype(ctype)
452
453         # Since no transfer is specified, we drop into a bunch of
454         # heuristics to guess it.  This mutates the options array to
455         # set the 'transfer' option.
456         # Note that we inferred the transfer
457         options['transfer-inferred'] = []
458         stype = source_type
459         if canontype == TYPE_STRING:
460             # It's a string - we just look at 'const'
461             if source_type.base_type.type_qualifier & TYPE_QUALIFIER_CONST:
462                 options['transfer'] = ['none']
463             else:
464                 options['transfer'] = ['full']
465         elif 'array' in options or stype.type == CTYPE_ARRAY:
466             # It's rare to mutate arrays in public GObject APIs
467             options['transfer'] = ['none']
468         elif (canontype in BASIC_GIR_TYPES or
469               canontype == TYPE_NONE or
470               stype.type == CTYPE_ENUM):
471             # Basic types default to 'none'
472             options['transfer'] = ['none']
473         elif (stype.type == CTYPE_POINTER and
474               stype.base_type.type_qualifier & TYPE_QUALIFIER_CONST):
475             # Anything with 'const' gets none
476             options['transfer'] = ['none']
477         elif is_param and stype.type == CTYPE_POINTER:
478             # For generic pointer types, let's look at the argument
479             # direction.  An out/inout argument gets full, everything
480             # else none.
481             if ('out' in options or
482                 'inout' in options or
483                 'in-out' in options):
484                 options['transfer'] = ['full']
485             else:
486                 options['transfer'] = ['none']
487         else:
488             # For anything else we default to none for parameters;
489             # this covers enums and possibly some other corner cases.
490             # Return values of structures and the like will end up
491             # full.
492             if is_param:
493                 options['transfer'] = ['none']
494             else:
495                 options['transfer'] = ['full']
496
497         return rettype
498
499     def _handle_generic_param_options(self, param, options):
500         for option, data in options.iteritems():
501             if option == 'transfer':
502                 if data:
503                     depth = data[0]
504                     if depth not in ('none', 'container', 'full'):
505                         raise ValueError("Invalid transfer %r" % (depth, ))
506                 else:
507                     depth = 'full'
508                 param.transfer = depth
509             elif option == 'transfer-inferred':
510                 # This is a purely internal flag; we don't expect
511                 # people to write it
512                 param.transfer_inferred = True
513
514     def _create_parameter(self, symbol, options):
515         options = self._parse_options(options)
516         if symbol.type == CSYMBOL_TYPE_ELLIPSIS:
517             ptype = Varargs()
518             if 'transfer' not in options:
519                 options['transfer'] = ['none']
520         else:
521             ptype = self._create_type(symbol.base_type, options, True)
522             ptype = self.resolve_param_type(ptype)
523         param = Parameter(symbol.ident, ptype)
524         for option, data in options.iteritems():
525             if option in ['in-out', 'inout']:
526                 param.direction = 'inout'
527             elif option == 'in':
528                 param.direction = 'in'
529             elif option == 'out':
530                 param.direction = 'out'
531             elif option == 'allow-none':
532                 param.allow_none = True
533             elif option.startswith(('element-type', 'array')):
534                 pass
535             elif option in ('transfer', 'transfer-inferred'):
536                 pass
537             else:
538                 print 'Unhandled parameter annotation option: %r' % (
539                     option, )
540         self._handle_generic_param_options(param, options)
541
542         assert param.transfer is not None, param
543         return param
544
545     def _create_return(self, source_type, options=None):
546         if options is None:
547             options_map = {}
548         else:
549             options_map = self._parse_options(options)
550         rtype = self._create_type(source_type, options_map, False)
551         rtype = self.resolve_param_type(rtype)
552         return_ = Return(rtype)
553         self._handle_generic_param_options(return_, options_map)
554         for option, data in options_map.iteritems():
555             if option in ('transfer', 'transfer-inferred',
556                           'element-type', 'out'):
557                 pass
558             else:
559                 print 'Unhandled return type annotation option: %r' % (
560                     option, )
561
562         assert return_.transfer is not None, return_
563         return return_
564
565     def _create_const(self, symbol):
566         name = self.remove_prefix(symbol.ident)
567         if symbol.const_string is None:
568             type_name = 'int'
569             value = symbol.const_int
570         else:
571             type_name = 'utf8'
572             value = symbol.const_string
573         const = Constant(name, type_name, value)
574         return const
575
576     def _create_typedef_struct(self, symbol, disguised=False):
577         name = self.remove_prefix(symbol.ident)
578         struct = Struct(name, symbol.ident, disguised)
579         self._typedefs_ns[symbol.ident] = struct
580         self._create_struct(symbol)
581         return struct
582
583     def _create_typedef_union(self, symbol):
584         name = self.remove_prefix(symbol.ident)
585         union = Union(name, symbol.ident)
586         self._typedefs_ns[symbol.ident] = union
587         self._create_union(symbol)
588         return union
589
590     def _create_struct(self, symbol):
591         struct = self._typedefs_ns.get(symbol.ident, None)
592         if struct is None:
593             # This is a bit of a hack; really we should try
594             # to resolve through the typedefs to find the real
595             # name
596             if symbol.ident.startswith('_'):
597                 name = symbol.ident[1:]
598             else:
599                 name = symbol.ident
600             name = self.remove_prefix(name)
601             struct = Struct(name, symbol.ident)
602
603         for child in symbol.base_type.child_list:
604             field = self._traverse_one(child)
605             if field:
606                 struct.fields.append(field)
607
608         return struct
609
610     def _create_union(self, symbol):
611         union = self._typedefs_ns.get(symbol.ident, None)
612         if union is None:
613             # This is a bit of a hack; really we should try
614             # to resolve through the typedefs to find the real
615             # name
616             if symbol.ident.startswith('_'):
617                 name = symbol.ident[1:]
618             else:
619                 name = symbol.ident
620             name = self.remove_prefix(name)
621             union = Union(name, symbol.ident)
622
623         for child in symbol.base_type.child_list:
624             field = self._traverse_one(child)
625             if field:
626                 union.fields.append(field)
627
628         return union
629
630     def _create_callback(self, symbol):
631         directives = symbol.directives()
632         parameters = self._create_parameters(symbol.base_type.base_type,
633             directives)
634         retval = self._create_return(symbol.base_type.base_type.base_type,
635             directives.get('return', {}))
636         if symbol.ident.find('_') > 0:
637             name = self.remove_prefix(symbol.ident, True)
638         else:
639             name = self.remove_prefix(symbol.ident)
640         return Callback(name, retval, list(parameters), symbol.ident)
641
642     def _typepair_to_str(self, item):
643         nsname, item = item
644         if nsname is None:
645             return item.name
646         return '%s.%s' % (nsname, item.name)
647
648     def _resolve_type_name_1(self, type_name, ctype, names):
649         # First look using the built-in names
650         if ctype:
651             try:
652                 return type_names[ctype]
653             except KeyError, e:
654                 pass
655         try:
656             return type_names[type_name]
657         except KeyError, e:
658             pass
659
660         if ctype:
661             ctype = ctype.replace('*', '')
662             resolved = names.ctypes.get(ctype)
663             if resolved:
664                 return self._typepair_to_str(resolved)
665         type_name = self.remove_prefix(type_name)
666         resolved = names.aliases.get(type_name)
667         if resolved:
668             return self._typepair_to_str(resolved)
669         resolved = names.names.get(type_name)
670         if resolved:
671             return self._typepair_to_str(resolved)
672         resolved = names.type_names.get(type_name)
673         if resolved:
674             return self._typepair_to_str(resolved)
675         raise KeyError("failed to find %r" % (type_name, ))
676
677     def resolve_type_name_full(self, type_name, ctype,
678                                names, allow_invalid=True):
679         try:
680             return self._resolve_type_name_1(type_name, ctype, names)
681         except KeyError, e:
682             try:
683                 return self._resolve_type_name_1(type_name, ctype, self._names)
684             except KeyError, e:
685                 if not allow_invalid:
686                     raise
687                 return type_name
688
689     def resolve_type_name(self, type_name, ctype=None):
690         try:
691             return self.resolve_type_name_full(type_name, ctype, self._names)
692         except KeyError, e:
693             return type_name
694
695     def gtypename_to_giname(self, gtname, names):
696         resolved = names.type_names.get(gtname)
697         if resolved:
698             return self._typepair_to_str(resolved)
699         resolved = self._names.type_names.get(gtname)
700         if resolved:
701             return self._typepair_to_str(resolved)
702         raise KeyError("Failed to resolve GType name: %r" % (gtname, ))
703
704     def ctype_of(self, obj):
705         if hasattr(obj, 'ctype'):
706             return obj.ctype
707         elif hasattr(obj, 'symbol'):
708             return obj.symbol
709         else:
710             return None
711
712     def resolve_param_type_full(self, ptype, names, **kwargs):
713         if isinstance(ptype, Node):
714             ptype.name = self.resolve_type_name_full(ptype.name,
715                                                      self.ctype_of(ptype),
716                                                      names, **kwargs)
717             if isinstance(ptype, (Array, List)):
718                 if ptype.element_type is not None:
719                     ptype.element_type = \
720                         self.resolve_param_type_full(ptype.element_type,
721                                                      names, **kwargs)
722             if isinstance(ptype, Map):
723                 if ptype.key_type is not None:
724                     ptype.key_type = \
725                         self.resolve_param_type_full(ptype.key_type,
726                                                      names, **kwargs)
727                     ptype.value_type = \
728                         self.resolve_param_type_full(ptype.value_type,
729                                                      names, **kwargs)
730         elif isinstance(ptype, basestring):
731             return self.resolve_type_name_full(ptype, None, names, **kwargs)
732         else:
733             raise AssertionError("Unhandled param: %r" % (ptype, ))
734         return ptype
735
736     def resolve_param_type(self, ptype):
737         try:
738             return self.resolve_param_type_full(ptype, self._names)
739         except KeyError, e:
740             return ptype
741
742     def follow_aliases(self, type_name, names):
743         while True:
744             resolved = names.aliases.get(type_name)
745             if resolved:
746                 (ns, alias) = resolved
747                 type_name = alias.target
748             else:
749                 break
750         return type_name