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