Major rewrite
[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             for prefix in ns.identifier_prefixes:
284                 if ident.startswith(prefix):
285                     matches.append((ns, ident[len(prefix):], len(prefix)))
286                     break
287         if matches:
288             matches.sort(self._sort_matches)
289             return map(lambda x: (x[0], x[1]), matches)
290         raise ValueError("Unknown namespace for identifier %r" % (ident, ))
291
292     def split_csymbol(self, symbol):
293         """Given a C symbol like foo_bar_do_baz, return a pair of
294 (namespace, stripped_symbol) or raise ValueError."""
295         matches = []
296         for ns in self._iter_namespaces():
297             for prefix in ns.symbol_prefixes:
298                 if not prefix.endswith('_'):
299                     prefix = prefix + '_'
300                 if symbol.startswith(prefix):
301                     matches.append((ns, symbol[len(prefix):], len(prefix)))
302                     break
303         if matches:
304             matches.sort(self._sort_matches)
305             return (matches[-1][0], matches[-1][1])
306         raise ValueError("Unknown namespace for symbol %r" % (symbol, ))
307
308     def strip_identifier_or_warn(self, ident, fatal=False):
309         hidden = ident.startswith('_')
310         if hidden:
311             ident = ident[1:]
312         try:
313             matches = self.split_ctype_namespaces(ident)
314         except ValueError, e:
315             self.log_warning(str(e), fatal=fatal)
316             return None
317         for ns, name in matches:
318             if ns is self._namespace:
319                 if hidden:
320                     return '_' + name
321                 return name
322         (ns, name) = matches[-1]
323         self.log_warning("Skipping foreign identifier %r from namespace %s" % (ident, ns.name, ),
324                          fatal=fatal)
325         return None
326
327     def _strip_symbol_or_warn(self, symbol, is_constant=False, fatal=False):
328         ident = symbol.ident
329         if is_constant:
330             # Temporarily lowercase
331             ident = ident.lower()
332         hidden = ident.startswith('_')
333         if hidden:
334             ident = ident[1:]
335         try:
336             (ns, name) = self.split_csymbol(ident)
337         except ValueError, e:
338             self.log_symbol_warning(symbol, "Unknown namespace", fatal=fatal)
339             return None
340         if ns != self._namespace:
341             self.log_symbol_warning(symbol,
342 "Skipping foreign symbol from namespace %s" % (ns.name, ),
343                                     fatal=fatal)
344             return None
345         if is_constant:
346             name = name.upper()
347         if hidden:
348             return '_' + name
349         return name
350
351     def _traverse_one(self, symbol, stype=None):
352         assert isinstance(symbol, SourceSymbol), symbol
353
354         if stype is None:
355             stype = symbol.type
356         if stype == CSYMBOL_TYPE_FUNCTION:
357             return self._create_function(symbol)
358         elif stype == CSYMBOL_TYPE_TYPEDEF:
359             return self._create_typedef(symbol)
360         elif stype == CSYMBOL_TYPE_STRUCT:
361             return self._create_struct(symbol)
362         elif stype == CSYMBOL_TYPE_ENUM:
363             return self._create_enum(symbol)
364         elif stype == CSYMBOL_TYPE_MEMBER:
365             return self._create_member(symbol)
366         elif stype == CSYMBOL_TYPE_UNION:
367             return self._create_union(symbol)
368         elif stype == CSYMBOL_TYPE_CONST:
369             return self._create_const(symbol)
370         # Ignore variable declarations in the header
371         elif stype == CSYMBOL_TYPE_OBJECT:
372             pass
373         else:
374             print 'transformer: unhandled symbol: %r' % (symbol, )
375
376     def _enum_common_prefix(self, symbol):
377         def common_prefix(a, b):
378             commonparts = []
379             for aword, bword in zip(a.split('_'), b.split('_')):
380                 if aword != bword:
381                     return '_'.join(commonparts) + '_'
382                 commonparts.append(aword)
383             return min(a, b)
384
385         # Nothing less than 2 has a common prefix
386         if len(list(symbol.base_type.child_list)) < 2:
387             return None
388         prefix = None
389         for child in symbol.base_type.child_list:
390             if prefix is None:
391                 prefix = child.ident
392             else:
393                 prefix = common_prefix(prefix, child.ident)
394                 if prefix == '':
395                     return None
396         return prefix
397
398     def _create_enum(self, symbol):
399         prefix = self._enum_common_prefix(symbol)
400         if prefix:
401             prefixlen = len(prefix)
402         else:
403             prefixlen = 0
404         members = []
405         for child in symbol.base_type.child_list:
406             if prefixlen > 0:
407                 name = child.ident[prefixlen:]
408             else:
409                 # Ok, the enum members don't have a consistent prefix
410                 # among them, so let's just remove the global namespace
411                 # prefix.
412                 name = self._strip_symbol_or_warn(child, is_constant=True)
413                 if name is None:
414                     return None
415             members.append(ast.Member(name.lower(),
416                                   child.const_int,
417                                   child.ident))
418
419         enum_name = self.strip_identifier_or_warn(symbol.ident)
420         if not enum_name:
421             return None
422         if symbol.base_type.is_bitfield:
423             klass = ast.Bitfield
424         else:
425             klass = ast.Enum
426         node = klass(enum_name, symbol.ident, members)
427         node.add_symbol_reference(symbol)
428         return node
429
430     def _create_function(self, symbol):
431         parameters = list(self._create_parameters(symbol.base_type))
432         return_ = self._create_return(symbol.base_type.base_type)
433         name = self._strip_symbol_or_warn(symbol)
434         if not name:
435             return None
436         func = ast.Function(name, return_, parameters, False, symbol.ident)
437         func.add_symbol_reference(symbol)
438         return func
439
440     def _create_source_type(self, source_type):
441         if source_type is None:
442             return 'None'
443         if source_type.type == CTYPE_VOID:
444             value = 'void'
445         elif source_type.type == CTYPE_BASIC_TYPE:
446             value = source_type.name
447         elif source_type.type == CTYPE_TYPEDEF:
448             value = source_type.name
449         elif source_type.type == CTYPE_ARRAY:
450             return self._create_source_type(source_type.base_type)
451         elif source_type.type == CTYPE_POINTER:
452             value = self._create_source_type(source_type.base_type) + '*'
453         else:
454             value = 'gpointer'
455         return value
456
457     def _create_parameters(self, base_type):
458         # warn if we see annotations for unknown parameters
459         param_names = set(child.ident for child in base_type.child_list)
460         for child in base_type.child_list:
461             yield self._create_parameter(child)
462
463     def _create_member(self, symbol):
464         source_type = symbol.base_type
465         if (source_type.type == CTYPE_POINTER and
466             symbol.base_type.base_type.type == CTYPE_FUNCTION):
467             node = self._create_callback(symbol, member=True)
468         elif source_type.type == CTYPE_STRUCT and source_type.name is None:
469             node = self._create_struct(symbol, anonymous=True)
470         elif source_type.type == CTYPE_UNION and source_type.name is None:
471             node = self._create_union(symbol, anonymous=True)
472         else:
473             # Special handling for fields; we don't have annotations on them
474             # to apply later, yet.
475             if source_type.type == CTYPE_ARRAY:
476                 ctype = self._create_source_type(source_type)
477                 canonical_ctype = self._canonicalize_ctype(ctype)
478                 if canonical_ctype[-1] == '*':
479                     derefed_name = canonical_ctype[:-1]
480                 else:
481                     derefed_name = canonical_ctype
482                 ftype = ast.Array(None, self.create_type_from_ctype_string(ctype),
483                                   ctype=derefed_name)
484                 child_list = list(symbol.base_type.child_list)
485                 ftype.zeroterminated = False
486                 if child_list:
487                     ftype.size = child_list[0].const_int
488             else:
489                 ftype = self._create_type_from_base(symbol.base_type)
490             # ast.Fields are assumed to be read-write
491             # (except for Objects, see also glibtransformer.py)
492             node = ast.Field(symbol.ident, ftype,
493                          readable=True, writable=True, bits=symbol.const_int)
494         return node
495
496     def _create_typedef(self, symbol):
497         ctype = symbol.base_type.type
498         if (ctype == CTYPE_POINTER and
499             symbol.base_type.base_type.type == CTYPE_FUNCTION):
500             node = self._create_typedef_callback(symbol)
501         elif (ctype == CTYPE_POINTER and
502             symbol.base_type.base_type.type == CTYPE_STRUCT):
503             node = self._create_typedef_struct(symbol, disguised=True)
504         elif ctype == CTYPE_STRUCT:
505             node = self._create_typedef_struct(symbol)
506         elif ctype == CTYPE_UNION:
507             node = self._create_typedef_union(symbol)
508         elif ctype == CTYPE_ENUM:
509             return self._create_enum(symbol)
510         elif ctype in (CTYPE_TYPEDEF,
511                        CTYPE_POINTER,
512                        CTYPE_BASIC_TYPE,
513                        CTYPE_VOID):
514             name = self.strip_identifier_or_warn(symbol.ident)
515             if not name:
516                 return None
517             if symbol.base_type.name:
518                 target = self.create_type_from_ctype_string(symbol.base_type.name)
519             else:
520                 target = ast.TYPE_ANY
521             if name in ast.type_names:
522                 return None
523             return ast.Alias(name, target, ctype=symbol.ident)
524         else:
525             raise NotImplementedError(
526                 "symbol %r of type %s" % (symbol.ident, ctype_name(ctype)))
527         return node
528
529     def _canonicalize_ctype(self, ctype):
530         # First look up the ctype including any pointers;
531         # a few type names like 'char*' have their own aliases
532         # and we need pointer information for those.
533         firstpass = ast.type_names.get(ctype)
534
535         # If we have a particular alias for this, skip deep
536         # canonicalization to prevent changing
537         # e.g. char* -> int8*
538         if firstpass:
539             return firstpass.target_fundamental
540
541         if not ctype.endswith('*'):
542             return ctype
543
544         # We have a pointer type.
545         # Strip the end pointer, canonicalize our base type
546         base = ctype[:-1]
547         canonical_base = self._canonicalize_ctype(base)
548
549         # Append the pointer again
550         canonical = canonical_base + '*'
551
552         return canonical
553
554     def parse_ctype(self, ctype, is_member=False):
555         canonical = self._canonicalize_ctype(ctype)
556
557         # Remove all pointers - we require standard calling
558         # conventions.  For example, an 'int' is always passed by
559         # value (unless it's out or inout).
560         derefed_typename = canonical.replace('*', '')
561
562         # Preserve "pointerness" of struct/union members
563         if (is_member and canonical.endswith('*') and
564             derefed_typename in ast.basic_type_names):
565             return 'gpointer'
566         else:
567             return derefed_typename
568
569     def _create_type_from_base(self, source_type, is_parameter=False, is_return=False):
570         ctype = self._create_source_type(source_type)
571         const = ((source_type.type == CTYPE_POINTER) and
572                  (source_type.base_type.type_qualifier & TYPE_QUALIFIER_CONST))
573         return self.create_type_from_ctype_string(ctype, is_const=const,
574                                                   is_parameter=is_parameter, is_return=is_return)
575
576     def _create_bare_container_type(self, base, ctype=None,
577                                     is_const=False):
578         if base in ('GList', 'GSList', 'GLib.List', 'GLib.SList'):
579             if base in ('GList', 'GSList'):
580                 name = 'GLib.' + base[1:]
581             else:
582                 name = base
583             return ast.List(name, ast.TYPE_ANY, ctype=ctype,
584                         is_const=is_const)
585         elif base in ('GArray', 'GPtrArray', 'GByteArray',
586                       'GLib.Array', 'GLib.PtrArray', 'GLib.ByteArray'):
587             if base in ('GArray', 'GPtrArray', 'GByteArray'):
588                 name = 'GLib.' + base[1:]
589             else:
590                 name = base
591             return ast.Array(name, ast.TYPE_ANY, ctype=ctype,
592                          is_const=is_const)
593         elif base in ('GHashTable', 'GLib.HashTable'):
594             return ast.Map(ast.TYPE_ANY, ast.TYPE_ANY, ctype=ctype, is_const=is_const)
595         return None
596
597     def create_type_from_ctype_string(self, ctype, is_const=False,
598                                       is_parameter=False, is_return=False):
599         canonical = self._canonicalize_ctype(ctype)
600         base = canonical.replace('*', '')
601
602         # Special default: char ** -> ast.Array, same for GStrv
603         if (is_return and canonical == 'utf8*') or base == 'GStrv':
604             bare_utf8 = ast.TYPE_STRING.clone()
605             bare_utf8.ctype = None
606             return ast.Array(None, bare_utf8, ctype=ctype,
607                              is_const=is_const)
608
609         fundamental = ast.type_names.get(base)
610         if fundamental is not None:
611             return ast.Type(target_fundamental=fundamental.target_fundamental,
612                         ctype=ctype,
613                         is_const=is_const)
614         container = self._create_bare_container_type(base, ctype=ctype, is_const=is_const)
615         if container:
616             return container
617         return ast.Type(ctype=ctype, is_const=is_const)
618
619     def _create_parameter(self, symbol):
620         if symbol.type == CSYMBOL_TYPE_ELLIPSIS:
621             ptype = ast.Varargs()
622         else:
623             ptype = self._create_type_from_base(symbol.base_type, is_parameter=True)
624         return ast.Parameter(symbol.ident, ptype)
625
626     def _create_return(self, source_type):
627         typeval = self._create_type_from_base(source_type, is_return=True)
628         return ast.Return(typeval)
629
630     def _create_const(self, symbol):
631         # Don't create constants for non-public things
632         # http://bugzilla.gnome.org/show_bug.cgi?id=572790
633         if (symbol.source_filename is None or
634             not symbol.source_filename.endswith('.h')):
635             return None
636         # ignore non-uppercase defines
637         if not self.UCASE_CONSTANT_RE.match(symbol.ident):
638             return None
639         name = self._strip_symbol_or_warn(symbol, is_constant=True)
640         if not name:
641             return None
642         if symbol.const_string is not None:
643             typeval = ast.TYPE_STRING
644             value = symbol.const_string
645         elif symbol.const_int is not None:
646             typeval = ast.TYPE_INT
647             value = '%d' % (symbol.const_int, )
648         elif symbol.const_double is not None:
649             typeval = ast.TYPE_DOUBLE
650             value = '%f' % (symbol.const_double, )
651         else:
652             raise AssertionError()
653
654         const = ast.Constant(name, typeval, value)
655         const.add_symbol_reference(symbol)
656         return const
657
658     def _create_typedef_struct(self, symbol, disguised=False):
659         name = self.strip_identifier_or_warn(symbol.ident)
660         if not name:
661             return None
662         struct = ast.Record(name, symbol.ident, disguised)
663         self._parse_fields(symbol, struct)
664         struct.add_symbol_reference(symbol)
665         self._typedefs_ns[symbol.ident] = struct
666         return None
667
668     def _create_typedef_union(self, symbol):
669         name = self.strip_identifier_or_warn(symbol.ident)
670         if not name:
671             return None
672         union = ast.Union(name, symbol.ident)
673         self._parse_fields(symbol, union)
674         union.add_symbol_reference(symbol)
675         self._typedefs_ns[symbol.ident] = union
676         return None
677
678     def _create_typedef_callback(self, symbol):
679         callback = self._create_callback(symbol)
680         if not callback:
681             return None
682         self._typedefs_ns[callback.name] = callback
683         return callback
684
685     def _parse_fields(self, symbol, compound):
686         for child in symbol.base_type.child_list:
687             child_node = self._traverse_one(child)
688             if not child_node:
689                 continue
690             if isinstance(child_node, ast.Field):
691                 field = child_node
692             else:
693                 field = ast.Field(child.ident, None, True, False,
694                               anonymous_node=child_node)
695             compound.fields.append(field)
696
697     def _create_compound(self, klass, symbol, anonymous):
698         if symbol.ident is None:
699             # the compound is an anonymous member of another union or a struct
700             assert anonymous
701             compound = klass(None, None)
702         else:
703             compound = self._typedefs_ns.get(symbol.ident, None)
704
705         if compound is None:
706             # This is a bit of a hack; really we should try
707             # to resolve through the typedefs to find the real
708             # name
709             if symbol.ident.startswith('_'):
710                 compound = self._typedefs_ns.get(symbol.ident[1:], None)
711             if compound is None:
712                 if anonymous:
713                     name = symbol.ident
714                 else:
715                     name = self.strip_identifier_or_warn(symbol.ident)
716                     if not name:
717                         return None
718                 compound = klass(name, symbol.ident)
719
720         self._parse_fields(symbol, compound)
721         compound.add_symbol_reference(symbol)
722         return compound
723
724     def _create_struct(self, symbol, anonymous=False):
725         return self._create_compound(ast.Record, symbol, anonymous)
726
727     def _create_union(self, symbol, anonymous=False):
728         return self._create_compound(ast.Union, symbol, anonymous)
729
730     def _create_callback(self, symbol, member=False):
731         parameters = list(self._create_parameters(symbol.base_type.base_type))
732         retval = self._create_return(symbol.base_type.base_type.base_type)
733
734         # Mark the 'user_data' arguments
735         for i, param in enumerate(parameters):
736             if (param.type.target_fundamental == 'gpointer' and
737                 param.argname == 'user_data'):
738                 param.closure_name = param.argname
739
740         if member:
741             name = symbol.ident
742         elif symbol.ident.find('_') > 0:
743             name = self._strip_symbol_or_warn(symbol)
744             if not name:
745                 return None
746         else:
747             name = self.strip_identifier_or_warn(symbol.ident)
748             if not name:
749                 return None
750         callback = ast.Callback(name, retval, parameters, False)
751         callback.add_symbol_reference(symbol)
752
753         return callback
754
755     def create_type_from_user_string(self, typestr):
756         """Parse a C type string (as might be given from an
757         annotation) and resolve it.  For compatibility, we can consume
758 both GI type string (utf8, Foo.Bar) style, as well as C (char *, FooBar) style."""
759         if '.' in typestr:
760             container = self._create_bare_container_type(typestr)
761             if container:
762                 return container
763             return self._namespace.type_from_name(typestr)
764         typeval = self.create_type_from_ctype_string(typestr)
765         self.resolve_type(typeval)
766         # Explicitly clear out the c_type; there isn't one in this case.
767         typeval.ctype = None
768         return typeval
769
770     def resolve_type(self, typeval):
771         if isinstance(typeval, (ast.Array, ast.List)):
772             self.resolve_type(typeval.element_type)
773             return
774         elif isinstance(typeval, ast.Map):
775             self.resolve_type(typeval.key_type)
776             self.resolve_type(typeval.value_type)
777             return
778         elif not typeval.resolved and typeval.ctype:
779             pointer_stripped = typeval.ctype.replace('*', '')
780             try:
781                 matches = self.split_ctype_namespaces(pointer_stripped)
782             except ValueError, e:
783                 raise TypeResolutionException(e)
784             target_giname=None
785             for namespace, name in matches:
786                 target = namespace.get(name)
787                 if not target:
788                     target = namespace.get_by_ctype(pointer_stripped)
789                 if target:
790                     typeval.target_giname='%s.%s' % (namespace.name, target.name)
791                     return
792
793     def _typepair_to_str(self, item):
794         nsname, item = item
795         if nsname is None:
796             return item.name
797         return '%s.%s' % (nsname, item.name)
798
799     def gtypename_to_giname(self, gtname, names):
800         resolved = names.type_names.get(gtname)
801         if resolved:
802             return self._typepair_to_str(resolved)
803         resolved = self._names.type_names.get(gtname)
804         if resolved:
805             return self._typepair_to_str(resolved)
806         raise KeyError("Failed to resolve GType name: %r" % (gtname, ))
807
808     def ctype_of(self, obj):
809         if hasattr(obj, 'ctype'):
810             return obj.ctype
811         elif hasattr(obj, 'symbol'):
812             return obj.symbol
813         else:
814             return None
815
816     def follow_aliases(self, type_name, names):
817         while True:
818             resolved = names.aliases.get(type_name)
819             if resolved:
820                 (ns, alias) = resolved
821                 type_name = alias.target
822             else:
823                 break
824         return type_name