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