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