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