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