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