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