Initial packaging for Tizen
[profile/ivi/gobject-introspection.git] / giscanner / transformer.py
1 # -*- Mode: Python -*-
2 # GObject-Introspection - a framework for introspecting GObject libraries
3 # Copyright (C) 2008  Johan Dahlin
4 #
5 # This library is free software; you can redistribute it and/or
6 # modify it under the terms of the GNU Lesser General Public
7 # License as published by the Free Software Foundation; either
8 # version 2 of the License, or (at your option) any later version.
9 #
10 # This library is distributed in the hope that it will be useful,
11 # but WITHOUT ANY WARRANTY; without even the implied warranty of
12 # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
13 # Lesser General Public License for more details.
14 #
15 # You should have received a copy of the GNU Lesser General Public
16 # License along with this library; if not, write to the
17 # Free Software Foundation, Inc., 59 Temple Place - Suite 330,
18 # Boston, MA 02111-1307, USA.
19 #
20
21 import os
22 import sys
23
24 from . import ast
25 from . import message
26 from .cachestore import CacheStore
27 from .girparser import GIRParser
28 from .sourcescanner import (
29     SourceSymbol, ctype_name, CTYPE_POINTER,
30     CTYPE_BASIC_TYPE, CTYPE_UNION, CTYPE_ARRAY, CTYPE_TYPEDEF,
31     CTYPE_VOID, CTYPE_ENUM, CTYPE_FUNCTION, CTYPE_STRUCT,
32     CSYMBOL_TYPE_FUNCTION, CSYMBOL_TYPE_TYPEDEF, CSYMBOL_TYPE_STRUCT,
33     CSYMBOL_TYPE_ENUM, CSYMBOL_TYPE_UNION, CSYMBOL_TYPE_OBJECT,
34     CSYMBOL_TYPE_MEMBER, CSYMBOL_TYPE_ELLIPSIS, CSYMBOL_TYPE_CONST,
35     TYPE_QUALIFIER_CONST)
36
37 class TransformerException(Exception):
38     pass
39
40
41 _xdg_data_dirs = [x for x in os.environ.get('XDG_DATA_DIRS', '').split(os.pathsep)]
42 _xdg_data_dirs.append(DATADIR)
43
44 if os.name != 'nt':
45     _xdg_data_dirs.append('/usr/share')
46
47
48 class Transformer(object):
49     namespace = property(lambda self: self._namespace)
50
51     def __init__(self, namespace, accept_unprefixed=False):
52         self._cachestore = CacheStore()
53         self._accept_unprefixed = accept_unprefixed
54         self._namespace = namespace
55         self._pkg_config_packages = set()
56         self._typedefs_ns = {}
57         self._includes = {} # <string namespace -> Namespace>
58         self._include_names = set() # string namespace
59         self._includepaths = []
60         self._passthrough_mode = False
61         self._annotations = {}
62
63     def get_includes(self):
64         return self._include_names
65
66     def get_pkgconfig_packages(self):
67         return self._pkg_config_packages
68
69     def disable_cache(self):
70         self._cachestore = None
71
72     def set_passthrough_mode(self):
73         self._passthrough_mode = True
74
75     def set_annotations(self, annotations):
76         self._annotations = annotations
77
78     def _append_new_node(self, node):
79         original = self._namespace.get(node.name)
80         # Special case constants here; we allow duplication to sort-of
81         # handle #ifdef.  But this introduces an arch-dependency in the .gir
82         # file.  So far this has only come up scanning glib - in theory, other
83         # modules will just depend on that.
84         if isinstance(original, ast.Constant) and isinstance(node, ast.Constant):
85             pass
86         elif original:
87             positions = set()
88             positions.update(original.file_positions)
89             positions.update(node.file_positions)
90             message.fatal("Namespace conflict for '%s'" % (node.name, ),
91                           positions)
92         else:
93             self._namespace.append(node)
94
95     def parse(self, symbols):
96         for symbol in symbols:
97             ## WORKAROUND ##
98             # https://bugzilla.gnome.org/show_bug.cgi?id=550616
99             if symbol.ident in ['gst_g_error_get_type']:
100                 continue
101             node = self._traverse_one(symbol)
102             if node:
103                 self._append_new_node(node)
104
105         # Now look through the namespace for things like
106         # typedef struct _Foo Foo;
107         # where we've never seen the struct _Foo.  Just create
108         # an empty structure for these as "disguised"
109         # If we do have a class/interface, merge fields
110         for typedef, compound in self._typedefs_ns.iteritems():
111             ns_compound = self._namespace.get(compound.name)
112             if not ns_compound:
113                 ns_compound = self._namespace.get('_' + compound.name)
114             if (not ns_compound and isinstance(compound, (ast.Record, ast.Union))
115                 and len(compound.fields) == 0):
116                 disguised = ast.Record(compound.name, typedef, disguised=True)
117                 self._namespace.append(disguised)
118             elif not ns_compound:
119                 self._namespace.append(compound)
120             elif isinstance(ns_compound, (ast.Record, ast.Union)) and len(ns_compound.fields) == 0:
121                 ns_compound.fields = compound.fields
122         self._typedefs_ns = None
123
124     def set_include_paths(self, paths):
125         self._includepaths = list(paths)
126
127     def register_include(self, include):
128         if include in self._include_names:
129             return
130         filename = self._find_include(include)
131         self._parse_include(filename)
132         self._include_names.add(include)
133
134     def register_include_uninstalled(self, include_path):
135         basename = os.path.basename(include_path)
136         if not basename.endswith('.gir'):
137             raise SystemExit(
138 "Include path %r must be a filename path ending in .gir" % (include_path, ))
139         girname = basename[:-4]
140         include = ast.Include.from_string(girname)
141         if include in self._include_names:
142             return
143         self._parse_include(include_path, uninstalled=True)
144         self._include_names.add(include)
145
146     def lookup_giname(self, name):
147         """Given a name of the form Foo or Bar.Foo,
148 return the corresponding ast.Node, or None if none
149 available.  Will throw KeyError however for unknown
150 namespaces."""
151         if '.' not in name:
152             return self._namespace.get(name)
153         else:
154             (ns, name) = name.split('.', 1)
155             if ns == self._namespace.name:
156                 return self._namespace.get(name)
157             include = self._includes[ns]
158             return include.get(name)
159
160     def lookup_typenode(self, typeobj):
161         """Given a Type object, if it points to a giname,
162 calls lookup_giname() on the name.  Otherwise return
163 None."""
164         if typeobj.target_giname:
165             return self.lookup_giname(typeobj.target_giname)
166         return None
167
168     # Private
169
170     def _find_include(self, include):
171         searchdirs = self._includepaths[:]
172         for path in _xdg_data_dirs:
173             searchdirs.append(os.path.join(path, 'gir-1.0'))
174         searchdirs.append(os.path.join(DATADIR, 'gir-1.0'))
175
176         girname = '%s-%s.gir' % (include.name, include.version)
177         for d in searchdirs:
178             path = os.path.join(d, girname)
179             if os.path.exists(path):
180                 return path
181         sys.stderr.write("Couldn't find include %r (search path: %r)\n"\
182                          % (girname, searchdirs))
183         sys.exit(1)
184
185     @classmethod
186     def parse_from_gir(cls, filename, extra_include_dirs=None):
187         self = cls(None)
188         if extra_include_dirs is not None:
189             self.set_include_paths(extra_include_dirs)
190         self.set_passthrough_mode()
191         self._parse_include(filename)
192         parser = self._cachestore.load(filename)
193         self._namespace = parser.get_namespace()
194         del self._includes[self._namespace.name]
195         return self
196
197     def _parse_include(self, filename, uninstalled=False):
198         parser = None
199         if self._cachestore is not None:
200             parser = self._cachestore.load(filename)
201         if parser is None:
202             parser = GIRParser(types_only=not self._passthrough_mode)
203             parser.parse(filename)
204             if self._cachestore is not None:
205                 self._cachestore.store(filename, parser)
206
207         for include in parser.get_includes():
208             self.register_include(include)
209
210         if not uninstalled:
211             for pkg in parser.get_pkgconfig_packages():
212                 self._pkg_config_packages.add(pkg)
213         namespace = parser.get_namespace()
214         self._includes[namespace.name] = namespace
215
216     def _iter_namespaces(self):
217         """Return an iterator over all included namespaces; the
218 currently-scanned namespace is first."""
219         yield self._namespace
220         for ns in self._includes.itervalues():
221             yield ns
222
223     def _sort_matches(self, x, y):
224         if x[0] is self._namespace:
225             return 1
226         elif y[0] is self._namespace:
227             return -1
228         return cmp(x[2], y[2])
229
230     def _split_c_string_for_namespace_matches(self, name, is_identifier=False):
231         matches = []  # Namespaces which might contain this name
232         unprefixed_namespaces = [] # Namespaces with no prefix, last resort
233         for ns in self._iter_namespaces():
234             if is_identifier:
235                 prefixes = ns.identifier_prefixes
236             elif name[0].isupper():
237                 prefixes = ns._ucase_symbol_prefixes
238             else:
239                 prefixes = ns.symbol_prefixes
240             if prefixes:
241                 for prefix in prefixes:
242                     if (not is_identifier) and (not prefix.endswith('_')):
243                         prefix = prefix + '_'
244                     if name.startswith(prefix):
245                         matches.append((ns, name[len(prefix):], len(prefix)))
246                         break
247             else:
248                 unprefixed_namespaces.append(ns)
249         if matches:
250             matches.sort(self._sort_matches)
251             return map(lambda x: (x[0], x[1]), matches)
252         elif self._accept_unprefixed:
253             return [(self._namespace, name)]
254         elif unprefixed_namespaces:
255             # A bit of a hack; this function ideally shouldn't look through the
256             # contents of namespaces; but since we aren't scanning anything
257             # without a prefix, it's not too bad.
258             for ns in unprefixed_namespaces:
259                 if name in ns:
260                     return [(ns, name)]
261         raise ValueError("Unknown namespace for %s %r"
262                          % ('identifier' if is_identifier else 'symbol', name, ))
263
264     def split_ctype_namespaces(self, ident):
265         """Given a StudlyCaps string identifier like FooBar, return a
266 list of (namespace, stripped_identifier) sorted by namespace length,
267 or raise ValueError.  As a special case, if the current namespace matches,
268 it is always biggest (i.e. last)."""
269         return self._split_c_string_for_namespace_matches(ident, is_identifier=True)
270
271     def split_csymbol_namespaces(self, symbol):
272         """Given a C symbol like foo_bar_do_baz, return a list of
273 (namespace, stripped_symbol) sorted by namespace match probablity, or
274 raise ValueError."""
275         return self._split_c_string_for_namespace_matches(symbol, is_identifier=False)
276
277     def split_csymbol(self, symbol):
278         """Given a C symbol like foo_bar_do_baz, return the most probable
279 (namespace, stripped_symbol) match, or raise ValueError."""
280         matches = self._split_c_string_for_namespace_matches(symbol, is_identifier=False)
281         return matches[-1]
282
283     def strip_identifier(self, ident):
284         hidden = ident.startswith('_')
285         if hidden:
286             ident = ident[1:]
287         try:
288             matches = self.split_ctype_namespaces(ident)
289         except ValueError, e:
290             raise TransformerException(str(e))
291         for ns, name in matches:
292             if ns is self._namespace:
293                 if hidden:
294                     return '_' + name
295                 return name
296         (ns, name) = matches[-1]
297         raise TransformerException(
298             "Skipping foreign identifier %r from namespace %s" % (
299             ident, ns.name, ))
300         return None
301
302     def _strip_symbol(self, symbol):
303         ident = symbol.ident
304         hidden = ident.startswith('_')
305         if hidden:
306             ident = ident[1:]
307         try:
308             (ns, name) = self.split_csymbol(ident)
309         except ValueError, e:
310             raise TransformerException(str(e))
311         if ns != self._namespace:
312             raise TransformerException(
313                 "Skipping foreign symbol from namespace %s" % (ns.name, ))
314         if hidden:
315             return '_' + name
316         return name
317
318     def _traverse_one(self, symbol, stype=None, parent_symbol=None):
319         assert isinstance(symbol, SourceSymbol), symbol
320
321         if stype is None:
322             stype = symbol.type
323         if stype == CSYMBOL_TYPE_FUNCTION:
324             return self._create_function(symbol)
325         elif stype == CSYMBOL_TYPE_TYPEDEF:
326             return self._create_typedef(symbol)
327         elif stype == CSYMBOL_TYPE_STRUCT:
328             return self._create_struct(symbol)
329         elif stype == CSYMBOL_TYPE_ENUM:
330             return self._create_enum(symbol)
331         elif stype == CSYMBOL_TYPE_MEMBER:
332             return self._create_member(symbol, parent_symbol)
333         elif stype == CSYMBOL_TYPE_UNION:
334             return self._create_union(symbol)
335         elif stype == CSYMBOL_TYPE_CONST:
336             # Don't parse constants which are marked (skip)
337             docblock = self._annotations.get(symbol.ident)
338             if not docblock or not 'skip' in docblock.options:
339                 return self._create_const(symbol)
340         # Ignore variable declarations in the header
341         elif stype == CSYMBOL_TYPE_OBJECT:
342             pass
343         else:
344             print 'transformer: unhandled symbol: %r' % (symbol, )
345
346     def _enum_common_prefix(self, symbol):
347         def common_prefix(a, b):
348             commonparts = []
349             for aword, bword in zip(a.split('_'), b.split('_')):
350                 if aword != bword:
351                     return '_'.join(commonparts) + '_'
352                 commonparts.append(aword)
353             return min(a, b)
354
355         # Nothing less than 2 has a common prefix
356         if len(list(symbol.base_type.child_list)) < 2:
357             return None
358         prefix = None
359         for child in symbol.base_type.child_list:
360             if prefix is None:
361                 prefix = child.ident
362             else:
363                 prefix = common_prefix(prefix, child.ident)
364                 if prefix == '':
365                     return None
366         return prefix
367
368     def _create_enum(self, symbol):
369         prefix = self._enum_common_prefix(symbol)
370         if prefix:
371             prefixlen = len(prefix)
372         else:
373             prefixlen = 0
374         members = []
375         for child in symbol.base_type.child_list:
376             if child.private:
377                 continue
378             if prefixlen > 0:
379                 name = child.ident[prefixlen:]
380             else:
381                 # Ok, the enum members don't have a consistent prefix
382                 # among them, so let's just remove the global namespace
383                 # prefix.
384                 try:
385                     name = self._strip_symbol(child)
386                 except TransformerException, e:
387                     message.warn_symbol(symbol, e)
388                     return None
389             members.append(ast.Member(name.lower(),
390                                       child.const_int,
391                                       child.ident,
392                                       None))
393
394         try:
395             enum_name = self.strip_identifier(symbol.ident)
396         except TransformerException, e:
397             message.warn_symbol(symbol, e)
398             return None
399         if symbol.base_type.is_bitfield:
400             klass = ast.Bitfield
401         else:
402             klass = ast.Enum
403         node = klass(enum_name, symbol.ident, members=members)
404         node.add_symbol_reference(symbol)
405         return node
406
407     def _create_function(self, symbol):
408         # Drop functions that start with _ very early on here
409         if symbol.ident.startswith('_'):
410             return None
411         parameters = list(self._create_parameters(symbol.base_type))
412         return_ = self._create_return(symbol.base_type.base_type)
413         try:
414             name = self._strip_symbol(symbol)
415         except TransformerException, e:
416             message.warn_symbol(symbol, e)
417             return None
418         func = ast.Function(name, return_, parameters, False, symbol.ident)
419         func.add_symbol_reference(symbol)
420         return func
421
422     def _create_source_type(self, source_type):
423         if source_type is None:
424             return 'None'
425         if source_type.type == CTYPE_VOID:
426             value = 'void'
427         elif source_type.type == CTYPE_BASIC_TYPE:
428             value = source_type.name
429         elif source_type.type == CTYPE_TYPEDEF:
430             value = source_type.name
431         elif source_type.type == CTYPE_ARRAY:
432             return self._create_source_type(source_type.base_type)
433         elif source_type.type == CTYPE_POINTER:
434             value = self._create_source_type(source_type.base_type) + '*'
435         else:
436             value = 'gpointer'
437         return value
438
439     def _create_parameters(self, base_type):
440         # warn if we see annotations for unknown parameters
441         param_names = set(child.ident for child in base_type.child_list)
442         for child in base_type.child_list:
443             yield self._create_parameter(child)
444
445     def _synthesize_union_type(self, symbol, parent_symbol):
446         # Synthesize a named union so that it can be referenced.
447         parent_ident = parent_symbol.ident
448         # FIXME: Should split_ctype_namespaces handle the hidden case?
449         hidden = parent_ident.startswith('_')
450         if hidden:
451             parent_ident = parent_ident[1:]
452         matches = self.split_ctype_namespaces(parent_ident)
453         (namespace, parent_name) = matches[-1]
454         assert namespace and parent_name
455         if hidden:
456             parent_name = '_' + parent_name
457         fake_union = ast.Union("%s__%s__union" % (parent_name, symbol.ident))
458         # _parse_fields accesses <type>.base_type.child_list, so we have to
459         # pass symbol.base_type even though that refers to the array, not the
460         # union.
461         self._parse_fields(symbol.base_type, fake_union)
462         self._append_new_node(fake_union)
463         fake_type = ast.Type(
464             target_giname="%s.%s" % (namespace.name, fake_union.name))
465         return fake_type
466
467     def _create_member(self, symbol, parent_symbol=None):
468         source_type = symbol.base_type
469         if (source_type.type == CTYPE_POINTER and
470             symbol.base_type.base_type.type == CTYPE_FUNCTION):
471             node = self._create_callback(symbol, member=True)
472         elif source_type.type == CTYPE_STRUCT and source_type.name is None:
473             node = self._create_struct(symbol, anonymous=True)
474         elif source_type.type == CTYPE_UNION and source_type.name is None:
475             node = self._create_union(symbol, anonymous=True)
476         else:
477             # Special handling for fields; we don't have annotations on them
478             # to apply later, yet.
479             if source_type.type == CTYPE_ARRAY:
480                 # If the array contains anonymous unions, like in the GValue
481                 # struct, we need to handle this specially.  This is necessary
482                 # to be able to properly calculate the size of the compound
483                 # type (e.g. GValue) that contains this array, see
484                 # <https://bugzilla.gnome.org/show_bug.cgi?id=657040>.
485                 if (source_type.base_type.type == CTYPE_UNION and
486                     source_type.base_type.name is None):
487                     synthesized_type = self._synthesize_union_type(symbol, parent_symbol)
488                     ftype = ast.Array(None, synthesized_type)
489                 else:
490                     ctype = self._create_source_type(source_type)
491                     canonical_ctype = self._canonicalize_ctype(ctype)
492                     if canonical_ctype[-1] == '*':
493                         derefed_name = canonical_ctype[:-1]
494                     else:
495                         derefed_name = canonical_ctype
496                     ftype = ast.Array(None, self.create_type_from_ctype_string(ctype),
497                                       ctype=derefed_name)
498                 child_list = list(symbol.base_type.child_list)
499                 ftype.zeroterminated = False
500                 if child_list:
501                     ftype.size = child_list[0].const_int
502             else:
503                 ftype = self._create_type_from_base(symbol.base_type)
504             # ast.Fields are assumed to be read-write
505             # (except for Objects, see also glibtransformer.py)
506             node = ast.Field(symbol.ident, ftype,
507                              readable=True, writable=True,
508                              bits=symbol.const_int)
509             if symbol.private:
510                 node.readable = False
511                 node.writable = False
512                 node.private = True
513         return node
514
515     def _create_typedef(self, symbol):
516         ctype = symbol.base_type.type
517         if (ctype == CTYPE_POINTER and
518             symbol.base_type.base_type.type == CTYPE_FUNCTION):
519             node = self._create_typedef_callback(symbol)
520         elif (ctype == CTYPE_POINTER and
521             symbol.base_type.base_type.type == CTYPE_STRUCT):
522             node = self._create_typedef_struct(symbol, disguised=True)
523         elif ctype == CTYPE_STRUCT:
524             node = self._create_typedef_struct(symbol)
525         elif ctype == CTYPE_UNION:
526             node = self._create_typedef_union(symbol)
527         elif ctype == CTYPE_ENUM:
528             return self._create_enum(symbol)
529         elif ctype in (CTYPE_TYPEDEF,
530                        CTYPE_POINTER,
531                        CTYPE_BASIC_TYPE,
532                        CTYPE_VOID):
533             try:
534                 name = self.strip_identifier(symbol.ident)
535             except TransformerException, e:
536                 message.warn(e)
537                 return None
538             if symbol.base_type.name:
539                 target = self.create_type_from_ctype_string(symbol.base_type.name)
540             else:
541                 target = ast.TYPE_ANY
542             if name in ast.type_names:
543                 return None
544             return ast.Alias(name, target, ctype=symbol.ident)
545         else:
546             raise NotImplementedError(
547                 "symbol %r of type %s" % (symbol.ident, ctype_name(ctype)))
548         return node
549
550     def _canonicalize_ctype(self, ctype):
551         # First look up the ctype including any pointers;
552         # a few type names like 'char*' have their own aliases
553         # and we need pointer information for those.
554         firstpass = ast.type_names.get(ctype)
555
556         # If we have a particular alias for this, skip deep
557         # canonicalization to prevent changing
558         # e.g. char* -> int8*
559         if firstpass:
560             return firstpass.target_fundamental
561
562         if not ctype.endswith('*'):
563             return ctype
564
565         # We have a pointer type.
566         # Strip the end pointer, canonicalize our base type
567         base = ctype[:-1]
568         canonical_base = self._canonicalize_ctype(base)
569
570         # Append the pointer again
571         canonical = canonical_base + '*'
572
573         return canonical
574
575     def parse_ctype(self, ctype, is_member=False):
576         canonical = self._canonicalize_ctype(ctype)
577
578         # Remove all pointers - we require standard calling
579         # conventions.  For example, an 'int' is always passed by
580         # value (unless it's out or inout).
581         derefed_typename = canonical.replace('*', '')
582
583         # Preserve "pointerness" of struct/union members
584         if (is_member and canonical.endswith('*') and
585             derefed_typename in ast.basic_type_names):
586             return 'gpointer'
587         else:
588             return derefed_typename
589
590     def _create_type_from_base(self, source_type, is_parameter=False, is_return=False):
591         ctype = self._create_source_type(source_type)
592         const = ((source_type.type == CTYPE_POINTER) and
593                  (source_type.base_type.type_qualifier & TYPE_QUALIFIER_CONST))
594         return self.create_type_from_ctype_string(ctype, is_const=const,
595                                                   is_parameter=is_parameter, is_return=is_return)
596
597     def _create_bare_container_type(self, base, ctype=None,
598                                     is_const=False):
599         if base in ('GList', 'GSList', 'GLib.List', 'GLib.SList'):
600             if base in ('GList', 'GSList'):
601                 name = 'GLib.' + base[1:]
602             else:
603                 name = base
604             return ast.List(name, ast.TYPE_ANY, ctype=ctype,
605                         is_const=is_const)
606         elif base in ('GArray', 'GPtrArray', 'GByteArray',
607                       'GLib.Array', 'GLib.PtrArray', 'GLib.ByteArray',
608                       'GObject.Array', 'GObject.PtrArray', 'GObject.ByteArray'):
609             if '.' in base:
610                 name = 'GLib.' + base.split('.', 1)[1]
611             else:
612                 name = 'GLib.' + base[1:]
613             return ast.Array(name, ast.TYPE_ANY, ctype=ctype,
614                          is_const=is_const)
615         elif base in ('GHashTable', 'GLib.HashTable', 'GObject.HashTable'):
616             return ast.Map(ast.TYPE_ANY, ast.TYPE_ANY, ctype=ctype, is_const=is_const)
617         return None
618
619     def create_type_from_ctype_string(self, ctype, is_const=False,
620                                       is_parameter=False, is_return=False):
621         canonical = self._canonicalize_ctype(ctype)
622         base = canonical.replace('*', '')
623
624         # Special default: char ** -> ast.Array, same for GStrv
625         if (is_return and canonical == 'utf8*') or base == 'GStrv':
626             bare_utf8 = ast.TYPE_STRING.clone()
627             bare_utf8.ctype = None
628             return ast.Array(None, bare_utf8, ctype=ctype,
629                              is_const=is_const)
630
631         fundamental = ast.type_names.get(base)
632         if fundamental is not None:
633             return ast.Type(target_fundamental=fundamental.target_fundamental,
634                         ctype=ctype,
635                         is_const=is_const)
636         container = self._create_bare_container_type(base, ctype=ctype, is_const=is_const)
637         if container:
638             return container
639         return ast.Type(ctype=ctype, is_const=is_const)
640
641     def _create_parameter(self, symbol):
642         if symbol.type == CSYMBOL_TYPE_ELLIPSIS:
643             ptype = ast.Varargs()
644         else:
645             ptype = self._create_type_from_base(symbol.base_type, is_parameter=True)
646         return ast.Parameter(symbol.ident, ptype)
647
648     def _create_return(self, source_type):
649         typeval = self._create_type_from_base(source_type, is_return=True)
650         return ast.Return(typeval)
651
652     def _create_const(self, symbol):
653         # Don't create constants for non-public things
654         # http://bugzilla.gnome.org/show_bug.cgi?id=572790
655         if (symbol.source_filename is None or
656             not symbol.source_filename.endswith('.h')):
657             return None
658         try:
659             name = self._strip_symbol(symbol)
660         except TransformerException, e:
661             message.warn_symbol(symbol, e)
662             return None
663         if symbol.const_string is not None:
664             typeval = ast.TYPE_STRING
665             value = unicode(symbol.const_string, 'utf-8')
666         elif symbol.const_int is not None:
667             typeval = ast.TYPE_INT
668             value = '%d' % (symbol.const_int, )
669         elif symbol.const_double is not None:
670             typeval = ast.TYPE_DOUBLE
671             value = '%f' % (symbol.const_double, )
672         else:
673             raise AssertionError()
674
675         const = ast.Constant(name, typeval, value,
676                              symbol.ident)
677         const.add_symbol_reference(symbol)
678         return const
679
680     def _create_typedef_struct(self, symbol, disguised=False):
681         try:
682             name = self.strip_identifier(symbol.ident)
683         except TransformerException, e:
684             message.warn_symbol(symbol, e)
685             return None
686         struct = ast.Record(name, symbol.ident, disguised=disguised)
687         self._parse_fields(symbol, struct)
688         struct.add_symbol_reference(symbol)
689         self._typedefs_ns[symbol.ident] = struct
690         return None
691
692     def _create_typedef_union(self, symbol):
693         try:
694             name = self.strip_identifier(symbol.ident)
695         except TransformerException, e:
696             message.warn(e)
697             return None
698         union = ast.Union(name, symbol.ident)
699         self._parse_fields(symbol, union)
700         union.add_symbol_reference(symbol)
701         self._typedefs_ns[symbol.ident] = union
702         return None
703
704     def _create_typedef_callback(self, symbol):
705         callback = self._create_callback(symbol)
706         if not callback:
707             return None
708         self._typedefs_ns[callback.name] = callback
709         return callback
710
711     def _parse_fields(self, symbol, compound):
712         for child in symbol.base_type.child_list:
713             child_node = self._traverse_one(child, parent_symbol=symbol)
714             if not child_node:
715                 continue
716             if isinstance(child_node, ast.Field):
717                 field = child_node
718             else:
719                 field = ast.Field(child.ident, None, True, False,
720                               anonymous_node=child_node)
721             compound.fields.append(field)
722
723     def _create_compound(self, klass, symbol, anonymous):
724         if symbol.ident is None:
725             # the compound is an anonymous member of another union or a struct
726             assert anonymous
727             compound = klass(None, None)
728         else:
729             compound = self._typedefs_ns.get(symbol.ident, None)
730
731         if compound is None:
732             # This is a bit of a hack; really we should try
733             # to resolve through the typedefs to find the real
734             # name
735             if symbol.ident.startswith('_'):
736                 compound = self._typedefs_ns.get(symbol.ident[1:], None)
737             if compound is None:
738                 if anonymous:
739                     name = symbol.ident
740                 else:
741                     try:
742                         name = self.strip_identifier(symbol.ident)
743                     except TransformerException, e:
744                         message.warn(e)
745                         return None
746                 compound = klass(name, symbol.ident)
747
748         self._parse_fields(symbol, compound)
749         compound.add_symbol_reference(symbol)
750         return compound
751
752     def _create_struct(self, symbol, anonymous=False):
753         return self._create_compound(ast.Record, symbol, anonymous)
754
755     def _create_union(self, symbol, anonymous=False):
756         return self._create_compound(ast.Union, symbol, anonymous)
757
758     def _create_callback(self, symbol, member=False):
759         parameters = list(self._create_parameters(symbol.base_type.base_type))
760         retval = self._create_return(symbol.base_type.base_type.base_type)
761
762         # Mark the 'user_data' arguments
763         for i, param in enumerate(parameters):
764             if (param.type.target_fundamental == 'gpointer' and
765                 param.argname == 'user_data'):
766                 param.closure_name = param.argname
767
768         if member:
769             name = symbol.ident
770         elif symbol.ident.find('_') > 0:
771             try:
772                 name = self._strip_symbol(symbol)
773             except TransformerException, e:
774                 message.warn_symbol(symbol, e)
775                 return None
776         else:
777             try:
778                 name = self.strip_identifier(symbol.ident)
779             except TransformerException, e:
780                 message.warn(e)
781                 return None
782         callback = ast.Callback(name, retval, parameters, False,
783                                 ctype=symbol.ident)
784         callback.add_symbol_reference(symbol)
785
786         return callback
787
788     def create_type_from_user_string(self, typestr):
789         """Parse a C type string (as might be given from an
790         annotation) and resolve it.  For compatibility, we can consume
791 both GI type string (utf8, Foo.Bar) style, as well as C (char *, FooBar) style.
792
793 Note that type resolution may not succeed."""
794         if '.' in typestr:
795             container = self._create_bare_container_type(typestr)
796             if container:
797                 return container
798             return self._namespace.type_from_name(typestr)
799         typeval = self.create_type_from_ctype_string(typestr)
800         self.resolve_type(typeval)
801         if typeval.resolved:
802             # Explicitly clear out the c_type; there isn't one in this case.
803             typeval.ctype = None
804         return typeval
805
806     def _resolve_type_from_ctype_all_namespaces(self, typeval, pointer_stripped):
807         # If we can't determine the namespace from the type name,
808         # fall back to trying all of our includes.  An example of this is mutter,
809         # which has nominal namespace of "Meta", but a few classes are
810         # "Mutter".  We don't export that data in introspection currently.
811         # Basically the library should be fixed, but we'll hack around it here.
812         for namespace in self._includes.itervalues():
813             target = namespace.get_by_ctype(pointer_stripped)
814             if target:
815                 typeval.target_giname = '%s.%s' % (namespace.name, target.name)
816                 return True
817         return False
818
819     def _resolve_type_from_ctype(self, typeval):
820         assert typeval.ctype is not None
821         pointer_stripped = typeval.ctype.replace('*', '')
822         try:
823             matches = self.split_ctype_namespaces(pointer_stripped)
824         except ValueError, e:
825             return self._resolve_type_from_ctype_all_namespaces(typeval, pointer_stripped)
826         target_giname = None
827         for namespace, name in matches:
828             target = namespace.get(name)
829             if not target:
830                 target = namespace.get_by_ctype(pointer_stripped)
831             if target:
832                 typeval.target_giname = '%s.%s' % (namespace.name, target.name)
833                 return True
834         return False
835
836     def _resolve_type_from_gtype_name(self, typeval):
837         assert typeval.gtype_name is not None
838         for ns in self._iter_namespaces():
839             for node in ns.itervalues():
840                 if not (isinstance(node, (ast.Class, ast.Interface))
841                         or (isinstance(node, ast.Registered) and node.get_type is not None)):
842                     continue
843                 if node.gtype_name == typeval.gtype_name:
844                     typeval.target_giname = '%s.%s' % (ns.name, node.name)
845                     return True
846         return False
847
848     def resolve_type(self, typeval):
849         if isinstance(typeval, (ast.Array, ast.List)):
850             return self.resolve_type(typeval.element_type)
851         elif isinstance(typeval, ast.Map):
852             key_resolved = self.resolve_type(typeval.key_type)
853             value_resolved = self.resolve_type(typeval.value_type)
854             return key_resolved and value_resolved
855         elif typeval.resolved:
856             return True
857         elif typeval.ctype:
858             return self._resolve_type_from_ctype(typeval)
859         elif typeval.gtype_name:
860             return self._resolve_type_from_gtype_name(typeval)
861
862     def _typepair_to_str(self, item):
863         nsname, item = item
864         if nsname is None:
865             return item.name
866         return '%s.%s' % (nsname, item.name)
867
868     def gtypename_to_giname(self, gtname, names):
869         resolved = names.type_names.get(gtname)
870         if resolved:
871             return self._typepair_to_str(resolved)
872         resolved = self._names.type_names.get(gtname)
873         if resolved:
874             return self._typepair_to_str(resolved)
875         raise KeyError("Failed to resolve GType name: %r" % (gtname, ))
876
877     def ctype_of(self, obj):
878         if hasattr(obj, 'ctype'):
879             return obj.ctype
880         elif hasattr(obj, 'symbol'):
881             return obj.symbol
882         else:
883             return None
884
885     def resolve_aliases(self, typenode):
886         """Removes all aliases from typenode, returns first non-alias
887         in the typenode alias chain.  Returns typenode argument if it
888         is not an alias."""
889         while isinstance(typenode, ast.Alias):
890             if typenode.target.target_giname is not None:
891                 typenode = self.lookup_giname(typenode.target.target_giname)
892             elif typenode.target.target_fundamental is not None:
893                 typenode = ast.type_names[typenode.target.target_fundamental]
894             else:
895                 break
896         return typenode