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