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