bcabdedc01a2ad6c422efbac1559108c280011c4
[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         if os.name != 'nt':
187             # For backwards compatibility, was always unconditionally added to the list.
188             data_dirs.append('/usr/share')
189         return data_dirs
190
191     def _find_include(self, include):
192         searchdirs = self._includepaths[:]
193         searchdirs.extend(GIRDIR)
194         for path in self._get_gi_data_dirs():
195             searchdirs.append(os.path.join(path, 'gir-1.0'))
196
197         girname = '%s-%s.gir' % (include.name, include.version)
198         for d in searchdirs:
199             path = os.path.join(d, girname)
200             if os.path.exists(path):
201                 return path
202         sys.stderr.write("Couldn't find include '%s' (search path: '%s')\n" %
203                          (girname, searchdirs))
204         sys.exit(1)
205
206     @classmethod
207     def parse_from_gir(cls, filename, extra_include_dirs=None):
208         self = cls(None)
209         if extra_include_dirs is not None:
210             self.set_include_paths(extra_include_dirs)
211         self.set_passthrough_mode()
212         parser = self._parse_include(filename)
213         self._namespace = parser.get_namespace()
214         del self._parsed_includes[self._namespace.name]
215         return self
216
217     def _parse_include(self, filename, uninstalled=False):
218         parser = None
219         if self._cachestore is not None:
220             parser = self._cachestore.load(filename)
221         if parser is None:
222             parser = GIRParser(types_only=not self._passthrough_mode)
223             parser.parse(filename)
224             if self._cachestore is not None:
225                 self._cachestore.store(filename, parser)
226
227         for include in parser.get_namespace().includes:
228             if include.name not in self._parsed_includes:
229                 dep_filename = self._find_include(include)
230                 self._parse_include(dep_filename)
231
232         if not uninstalled:
233             for pkg in parser.get_namespace().exported_packages:
234                 self._pkg_config_packages.add(pkg)
235         namespace = parser.get_namespace()
236         self._parsed_includes[namespace.name] = namespace
237         return parser
238
239     def _iter_namespaces(self):
240         """Return an iterator over all included namespaces; the
241 currently-scanned namespace is first."""
242         yield self._namespace
243         for ns in self._parsed_includes.values():
244             yield ns
245
246     def _sort_matches(self, val):
247         """Key sort which ensures items in self._namespace are last by returning
248         a tuple key starting with 1 for self._namespace entries and 0 for
249         everythin else.
250         """
251         if val[0] == self._namespace:
252             return 1, val[2]
253         else:
254             return 0, val[2]
255
256     def _split_c_string_for_namespace_matches(self, name, is_identifier=False):
257         if not is_identifier and self._symbol_filter_cmd:
258             proc = subprocess.Popen(self._symbol_filter_cmd,
259                                     stdin=subprocess.PIPE,
260                                     stdout=subprocess.PIPE,
261                                     stderr=subprocess.PIPE)
262             proc_name, err = proc.communicate(name.encode())
263             proc_name = proc_name.strip()
264             if proc.returncode:
265                 raise ValueError('filter: %r exited: %d with error: %s' %
266                                  (self._symbol_filter_cmd, proc.returncode, err))
267             name = proc_name.decode('ascii')
268             name = name.strip()
269
270         matches = []  # Namespaces which might contain this name
271         unprefixed_namespaces = []  # Namespaces with no prefix, last resort
272         for ns in self._iter_namespaces():
273             if is_identifier:
274                 prefixes = ns.identifier_prefixes
275             elif name[0].isupper():
276                 prefixes = ns._ucase_symbol_prefixes
277             else:
278                 prefixes = ns.symbol_prefixes
279             if prefixes:
280                 for prefix in prefixes:
281                     if (not is_identifier) and (not prefix.endswith('_')):
282                         prefix = prefix + '_'
283                     if name.startswith(prefix):
284                         matches.append((ns, name[len(prefix):], len(prefix)))
285                         break
286             else:
287                 unprefixed_namespaces.append(ns)
288         if matches:
289             matches.sort(key=self._sort_matches)
290             return list(map(lambda x: (x[0], x[1]), matches))
291         elif self._accept_unprefixed:
292             return [(self._namespace, name)]
293         elif unprefixed_namespaces:
294             # A bit of a hack; this function ideally shouldn't look through the
295             # contents of namespaces; but since we aren't scanning anything
296             # without a prefix, it's not too bad.
297             for ns in unprefixed_namespaces:
298                 if name in ns:
299                     return [(ns, name)]
300         raise ValueError("Unknown namespace for %s '%s'"
301                          % ('identifier' if is_identifier else 'symbol', name, ))
302
303     def split_ctype_namespaces(self, ident):
304         """Given a StudlyCaps string identifier like FooBar, return a
305 list of (namespace, stripped_identifier) sorted by namespace length,
306 or raise ValueError.  As a special case, if the current namespace matches,
307 it is always biggest (i.e. last)."""
308         return self._split_c_string_for_namespace_matches(ident, is_identifier=True)
309
310     def split_csymbol_namespaces(self, symbol):
311         """Given a C symbol like foo_bar_do_baz, return a list of
312 (namespace, stripped_symbol) sorted by namespace match probablity, or
313 raise ValueError."""
314         return self._split_c_string_for_namespace_matches(symbol, is_identifier=False)
315
316     def split_csymbol(self, symbol):
317         """Given a C symbol like foo_bar_do_baz, return the most probable
318 (namespace, stripped_symbol) match, or raise ValueError."""
319         matches = self._split_c_string_for_namespace_matches(symbol, is_identifier=False)
320         return matches[-1]
321
322     def strip_identifier(self, ident):
323         if self._identifier_filter_cmd:
324             proc = subprocess.Popen(self._identifier_filter_cmd,
325                                     stdin=subprocess.PIPE,
326                                     stdout=subprocess.PIPE,
327                                     stderr=subprocess.PIPE)
328             proc_ident, err = proc.communicate(ident.encode())
329             if proc.returncode:
330                 raise ValueError('filter: %r exited: %d with error: %s' %
331                                  (self._identifier_filter_cmd, proc.returncode, err))
332             ident = proc_ident.decode('ascii').strip()
333
334         hidden = ident.startswith('_')
335         if hidden:
336             ident = ident[1:]
337         try:
338             matches = self.split_ctype_namespaces(ident)
339         except ValueError as e:
340             raise TransformerException(str(e))
341         for ns, name in matches:
342             if ns is self._namespace:
343                 if hidden:
344                     return '_' + name
345                 return name
346         (ns, name) = matches[-1]
347         raise TransformerException(
348             "Skipping foreign identifier '%s' from namespace %s" % (ident, ns.name, ))
349         return None
350
351     def _strip_symbol(self, symbol):
352         ident = symbol.ident
353         hidden = ident.startswith('_')
354         if hidden:
355             ident = ident[1:]
356         try:
357             (ns, name) = self.split_csymbol(ident)
358         except ValueError as e:
359             raise TransformerException(str(e))
360         if ns != self._namespace:
361             raise TransformerException(
362                 "Skipping foreign symbol from namespace %s" % (ns.name, ))
363         if hidden:
364             return '_' + name
365         return name
366
367     def _traverse_one(self, symbol, stype=None, parent_symbol=None):
368         assert isinstance(symbol, SourceSymbol), symbol
369
370         if stype is None:
371             stype = symbol.type
372         if stype == CSYMBOL_TYPE_FUNCTION:
373             return self._create_function(symbol)
374         elif stype == CSYMBOL_TYPE_FUNCTION_MACRO:
375             return self._create_function_macro(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_function_macro(self, symbol):
459         if symbol.ident.startswith('_'):
460             return None
461
462         if (symbol.source_filename is None or not symbol.source_filename.endswith('.h')):
463             return None
464
465         parameters = list(self._create_parameters(symbol, symbol.base_type))
466         name = self._strip_symbol(symbol)
467         macro = ast.FunctionMacro(name, parameters, symbol.ident)
468         macro.add_symbol_reference(symbol)
469         return macro
470
471     def _create_source_type(self, source_type, is_parameter=False):
472         assert source_type is not None
473         if source_type.type == CTYPE_VOID:
474             value = 'void'
475         elif source_type.type == CTYPE_BASIC_TYPE:
476             value = source_type.name
477         elif source_type.type == CTYPE_TYPEDEF:
478             value = source_type.name
479         elif (source_type.type == CTYPE_POINTER or
480                 # Array to pointer adjustment as per 6.7.6.3.
481                 # This is performed only on the outermost array,
482                 # so we don't forward is_parameter.
483                 (source_type.type == CTYPE_ARRAY and is_parameter)):
484             value = self._create_source_type(source_type.base_type) + '*'
485         elif source_type.type == CTYPE_ARRAY:
486             return self._create_source_type(source_type.base_type)
487         else:
488             value = 'gpointer'
489         return value
490
491     def _create_complete_source_type(self, source_type, is_parameter=False):
492         assert source_type is not None
493
494         const = (source_type.type_qualifier & TYPE_QUALIFIER_CONST)
495         volatile = (source_type.type_qualifier & TYPE_QUALIFIER_VOLATILE)
496
497         if source_type.type == CTYPE_VOID:
498             return 'void'
499         elif source_type.type in [CTYPE_BASIC_TYPE,
500                                   CTYPE_TYPEDEF,
501                                   CTYPE_STRUCT,
502                                   CTYPE_UNION,
503                                   CTYPE_ENUM]:
504             value = source_type.name
505             if const:
506                 value = 'const ' + value
507             if volatile:
508                 value = 'volatile ' + value
509             return value
510         elif (source_type.type == CTYPE_POINTER or
511                 # Array to pointer adjustment as per 6.7.6.3.
512                 # This is performed only on the outermost array,
513                 # so we don't forward is_parameter.
514                 (source_type.type == CTYPE_ARRAY and is_parameter)):
515             value = self._create_complete_source_type(source_type.base_type) + '*'
516             # TODO: handle pointer to function as a special case?
517             if const:
518                 value += ' const'
519             if volatile:
520                 value += ' volatile'
521             return value
522         elif source_type.type == CTYPE_ARRAY:
523             return self._create_complete_source_type(source_type.base_type)
524         else:
525             if const:
526                 value = 'gconstpointer'
527             else:
528                 value = 'gpointer'
529             if volatile:
530                 value = 'volatile ' + value
531             return value
532
533     def _create_parameters(self, symbol, base_type):
534         for i, child in enumerate(base_type.child_list):
535             yield self._create_parameter(symbol, i, child)
536
537     def _synthesize_union_type(self, symbol, parent_symbol):
538         # Synthesize a named union so that it can be referenced.
539         parent_ident = parent_symbol.ident
540         # FIXME: Should split_ctype_namespaces handle the hidden case?
541         hidden = parent_ident.startswith('_')
542         if hidden:
543             parent_ident = parent_ident[1:]
544         matches = self.split_ctype_namespaces(parent_ident)
545         (namespace, parent_name) = matches[-1]
546         assert namespace and parent_name
547         if hidden:
548             parent_name = '_' + parent_name
549         fake_union = ast.Union("%s__%s__union" % (parent_name, symbol.ident))
550         # _parse_fields accesses <type>.base_type.child_list, so we have to
551         # pass symbol.base_type even though that refers to the array, not the
552         # union.
553         self._parse_fields(symbol.base_type, fake_union)
554         self._append_new_node(fake_union)
555         fake_type = ast.Type(
556             target_giname="%s.%s" % (namespace.name, fake_union.name))
557         return fake_type
558
559     def _create_member(self, symbol, parent_symbol=None):
560         source_type = symbol.base_type
561         if (source_type.type == CTYPE_POINTER
562         and symbol.base_type.base_type.type == CTYPE_FUNCTION):
563             node = self._create_callback(symbol, member=True)
564         elif source_type.type == CTYPE_STRUCT and source_type.name is None:
565             node = self._create_member_compound(ast.Record, symbol)
566         elif source_type.type == CTYPE_UNION and source_type.name is None:
567             node = self._create_member_compound(ast.Union, symbol)
568         else:
569             # Special handling for fields; we don't have annotations on them
570             # to apply later, yet.
571             if source_type.type == CTYPE_ARRAY:
572                 # Determine flattened array size and its element type.
573                 flattened_size = 1
574                 while source_type.type == CTYPE_ARRAY:
575                     for child in source_type.child_list:
576                         if flattened_size is not None:
577                             flattened_size *= child.const_int
578                         break
579                     else:
580                         flattened_size = None
581                     source_type = source_type.base_type
582
583                 # If the array contains anonymous unions, like in the GValue
584                 # struct, we need to handle this specially.  This is necessary
585                 # to be able to properly calculate the size of the compound
586                 # type (e.g. GValue) that contains this array, see
587                 # <https://bugzilla.gnome.org/show_bug.cgi?id=657040>.
588                 if source_type.type == CTYPE_UNION and source_type.name is None:
589                     element_type = self._synthesize_union_type(symbol, parent_symbol)
590                 else:
591                     ctype = self._create_source_type(source_type)
592                     complete_ctype = self._create_complete_source_type(source_type)
593                     element_type = self.create_type_from_ctype_string(ctype,
594                                                                       complete_ctype=complete_ctype)
595                 ftype = ast.Array(None, element_type)
596                 ftype.zeroterminated = False
597                 ftype.size = flattened_size
598             else:
599                 ftype = self._create_type_from_base(symbol.base_type)
600             # ast.Fields are assumed to be read-write
601             # (except for Objects, see also glibtransformer.py)
602             node = ast.Field(symbol.ident, ftype,
603                              readable=True, writable=True,
604                              bits=symbol.const_int)
605             if symbol.private:
606                 node.readable = False
607                 node.writable = False
608                 node.private = True
609         return node
610
611     def _create_typedef(self, symbol):
612         ctype = symbol.base_type.type
613         if (ctype == CTYPE_POINTER and symbol.base_type.base_type.type == CTYPE_FUNCTION):
614             node = self._create_typedef_callback(symbol)
615         elif (ctype == CTYPE_FUNCTION):
616             node = self._create_typedef_callback(symbol)
617         elif (ctype == CTYPE_POINTER and symbol.base_type.base_type.type == CTYPE_STRUCT):
618             node = self._create_typedef_compound(ast.Record, symbol, disguised=True)
619         elif ctype == CTYPE_STRUCT:
620             node = self._create_typedef_compound(ast.Record, symbol)
621         elif ctype == CTYPE_UNION:
622             node = self._create_typedef_compound(ast.Union, symbol)
623         elif ctype == CTYPE_ENUM:
624             return self._create_enum(symbol)
625         elif ctype in (CTYPE_TYPEDEF,
626                        CTYPE_POINTER,
627                        CTYPE_BASIC_TYPE,
628                        CTYPE_VOID):
629             name = self.strip_identifier(symbol.ident)
630             target = self._create_type_from_base(symbol.base_type)
631             if name in ast.type_names:
632                 return None
633             # https://bugzilla.gnome.org/show_bug.cgi?id=755882
634             if name.endswith('_autoptr'):
635                 return None
636             node = ast.Alias(name, target, ctype=symbol.ident)
637             node.add_symbol_reference(symbol)
638         else:
639             raise NotImplementedError(
640                 "symbol '%s' of type %s" % (symbol.ident, ctype_name(ctype)))
641         return node
642
643     def _canonicalize_ctype(self, ctype):
644         # First look up the ctype including any pointers;
645         # a few type names like 'char*' have their own aliases
646         # and we need pointer information for those.
647         firstpass = ast.type_names.get(ctype)
648
649         # If we have a particular alias for this, skip deep
650         # canonicalization to prevent changing
651         # e.g. char* -> int8*
652         if firstpass:
653             return firstpass.target_fundamental
654
655         if not ctype.endswith('*'):
656             return ctype
657
658         # We have a pointer type.
659         # Strip the end pointer, canonicalize our base type
660         base = ctype[:-1]
661         canonical_base = self._canonicalize_ctype(base)
662
663         # Append the pointer again
664         canonical = canonical_base + '*'
665
666         return canonical
667
668     def _create_type_from_base(self, source_type, is_parameter=False, is_return=False):
669         ctype = self._create_source_type(source_type, is_parameter=is_parameter)
670         complete_ctype = self._create_complete_source_type(source_type, is_parameter=is_parameter)
671         const = ((source_type.type == CTYPE_POINTER) and
672                  (source_type.base_type.type_qualifier & TYPE_QUALIFIER_CONST))
673         return self.create_type_from_ctype_string(ctype, is_const=const,
674                                                   is_parameter=is_parameter, is_return=is_return,
675                                                   complete_ctype=complete_ctype)
676
677     def _create_bare_container_type(self, base, ctype=None,
678                                     is_const=False, complete_ctype=None):
679         if base in ('GList', 'GSList', 'GLib.List', 'GLib.SList'):
680             if base in ('GList', 'GSList'):
681                 name = 'GLib.' + base[1:]
682             else:
683                 name = base
684             return ast.List(name, ast.TYPE_ANY, ctype=ctype,
685                         is_const=is_const, complete_ctype=complete_ctype)
686         elif base in ('GByteArray', 'GLib.ByteArray', 'GObject.ByteArray'):
687             return ast.Array('GLib.ByteArray', ast.TYPE_UINT8, ctype=ctype,
688                          is_const=is_const, complete_ctype=complete_ctype)
689         elif base in ('GArray', 'GPtrArray',
690                       'GLib.Array', 'GLib.PtrArray',
691                       'GObject.Array', 'GObject.PtrArray'):
692             if '.' in base:
693                 name = 'GLib.' + base.split('.', 1)[1]
694             else:
695                 name = 'GLib.' + base[1:]
696             return ast.Array(name, ast.TYPE_ANY, ctype=ctype,
697                          is_const=is_const, complete_ctype=complete_ctype)
698         elif base in ('GHashTable', 'GLib.HashTable', 'GObject.HashTable'):
699             return ast.Map(ast.TYPE_ANY, ast.TYPE_ANY, ctype=ctype, is_const=is_const,
700                            complete_ctype=complete_ctype)
701         return None
702
703     def create_type_from_ctype_string(self, ctype, is_const=False,
704                                       is_parameter=False, is_return=False,
705                                       complete_ctype=None):
706         canonical = self._canonicalize_ctype(ctype)
707         base = canonical.replace('*', '')
708
709         # While gboolean and _Bool are distinct types, they used to be treated
710         # by scanner as exactly the same one. In general this is incorrect
711         # because of different ABI, but this usually works fine,
712         # so for backward compatibility lets continue for now:
713         # https://gitlab.gnome.org/GNOME/gobject-introspection/merge_requests/24#note_92792
714         if canonical in ('_Bool', 'bool'):
715             canonical = 'gboolean'
716             base = canonical
717
718         # Special default: char ** -> ast.Array, same for GStrv
719         if (is_return and canonical == 'utf8*') or base == 'GStrv':
720             bare_utf8 = ast.TYPE_STRING.clone()
721             bare_utf8.ctype = None
722             return ast.Array(None, bare_utf8, ctype=ctype,
723                              is_const=is_const, complete_ctype=complete_ctype)
724
725         fundamental = ast.type_names.get(base)
726         if fundamental is not None:
727             return ast.Type(target_fundamental=fundamental.target_fundamental,
728                         ctype=ctype,
729                         is_const=is_const, complete_ctype=complete_ctype)
730         container = self._create_bare_container_type(base, ctype=ctype, is_const=is_const,
731                                                      complete_ctype=complete_ctype)
732         if container:
733             return container
734         return ast.Type(ctype=ctype, is_const=is_const, complete_ctype=complete_ctype)
735
736     def _create_parameter(self, parent_symbol, index, symbol):
737         if symbol.type == CSYMBOL_TYPE_ELLIPSIS:
738             return ast.Parameter('...', ast.Varargs())
739         else:
740             if symbol.base_type:
741                 ptype = self._create_type_from_base(symbol.base_type, is_parameter=True)
742             else:
743                 ptype = None
744
745             if symbol.ident is None:
746                 if symbol.base_type and symbol.base_type.type != CTYPE_VOID:
747                     message.warn_symbol(parent_symbol, "missing parameter name; undocumentable")
748                 ident = 'arg%d' % (index, )
749             else:
750                 ident = symbol.ident
751
752             return ast.Parameter(ident, ptype)
753
754     def _create_return(self, source_type):
755         typeval = self._create_type_from_base(source_type, is_return=True)
756         return ast.Return(typeval)
757
758     def _create_const(self, symbol):
759         if symbol.ident.startswith('_'):
760             return None
761
762         # Don't create constants for non-public things
763         # http://bugzilla.gnome.org/show_bug.cgi?id=572790
764         if (symbol.source_filename is None or not symbol.source_filename.endswith('.h')):
765             return None
766         name = self._strip_symbol(symbol)
767         if symbol.const_string is not None:
768             typeval = ast.TYPE_STRING
769             value = symbol.const_string
770         elif symbol.const_int is not None:
771             if symbol.base_type is not None:
772                 typeval = self._create_type_from_base(symbol.base_type)
773             else:
774                 typeval = ast.TYPE_INT
775             unaliased = typeval
776             self._resolve_type_from_ctype(unaliased)
777             if typeval.target_giname and typeval.ctype:
778                 target = self.lookup_giname(typeval.target_giname)
779                 target = self.resolve_aliases(target)
780                 if isinstance(target, ast.Type):
781                     unaliased = target
782             if unaliased == ast.TYPE_UINT64:
783                 value = str(symbol.const_int % 2 ** 64)
784             elif unaliased == ast.TYPE_UINT32:
785                 value = str(symbol.const_int % 2 ** 32)
786             elif unaliased == ast.TYPE_UINT16:
787                 value = str(symbol.const_int % 2 ** 16)
788             elif unaliased == ast.TYPE_UINT8:
789                 value = str(symbol.const_int % 2 ** 16)
790             else:
791                 value = str(symbol.const_int)
792         elif symbol.const_boolean is not None:
793             typeval = ast.TYPE_BOOLEAN
794             value = "true" if symbol.const_boolean else "false"
795         elif symbol.const_double is not None:
796             typeval = ast.TYPE_DOUBLE
797             value = '%f' % (symbol.const_double, )
798         else:
799             raise AssertionError()
800
801         const = ast.Constant(name, typeval, value,
802                              symbol.ident)
803         const.add_symbol_reference(symbol)
804         return const
805
806     def _create_typedef_compound(self, compound_class, symbol, disguised=False):
807         name = self.strip_identifier(symbol.ident)
808         assert symbol.base_type
809         if symbol.base_type.name:
810             tag_name = symbol.base_type.name
811         else:
812             tag_name = None
813
814         # If the struct already exists in the tag namespace, use it.
815         if tag_name in self._tag_ns:
816             compound = self._tag_ns[tag_name]
817             if compound.name:
818                 # If the struct name is set it means the struct has already been
819                 # promoted from the tag namespace to the main namespace by a
820                 # prior typedef struct. If we get here it means this is another
821                 # typedef of that struct. Instead of creating an alias to the
822                 # primary typedef that has been promoted, we create a new Record
823                 # with shared fields. This handles the case where we want to
824                 # give structs like GInitiallyUnowned its own Record:
825                 #    typedef struct _GObject GObject;
826                 #    typedef struct _GObject GInitiallyUnowned;
827                 # See: http://bugzilla.gnome.org/show_bug.cgi?id=569408
828                 new_compound = compound_class(name, symbol.ident, tag_name=tag_name)
829                 new_compound.fields = compound.fields
830                 new_compound.add_symbol_reference(symbol)
831                 return new_compound
832             else:
833                 # If the struct does not have its name set, it exists only in
834                 # the tag namespace. Set it here and return it which will
835                 # promote it to the main namespace. Essentially the first
836                 # typedef for a struct clobbers its name and ctype which is what
837                 # will be visible to GI.
838                 compound.name = name
839                 compound.ctype = symbol.ident
840         else:
841             # Create a new struct with a typedef name and tag name when available.
842             # Structs with a typedef name are promoted into the main namespace
843             # by it being returned to the "parse" function and are also added to
844             # the tag namespace if it has a tag_name set.
845             compound = compound_class(name, symbol.ident, disguised=disguised, tag_name=tag_name)
846             if tag_name:
847                 # Force the struct as disguised for now since we do not yet know
848                 # if it has fields that will be parsed. Note that this is using
849                 # an erroneous definition of disguised and we should eventually
850                 # only look at the field count when needed.
851                 compound.disguised = True
852             else:
853                 # Case where we have an anonymous struct which is typedef'd:
854                 #   typedef struct {...} Struct;
855                 # we need to parse the fields because we never get a struct
856                 # in the tag namespace which is normally where fields are parsed.
857                 self._parse_fields(symbol, compound)
858
859         compound.add_symbol_reference(symbol)
860         return compound
861
862     def _create_tag_ns_compound(self, compound_class, symbol):
863         # Get or create a struct from C's tag namespace
864         if symbol.ident in self._tag_ns:
865             compound = self._tag_ns[symbol.ident]
866         else:
867             compound = compound_class(None, symbol.ident, tag_name=symbol.ident)
868
869         # Make sure disguised is False as we are now about to parse the
870         # fields of the real struct.
871         compound.disguised = False
872         # Fields may need to be parsed in either of the above cases because the
873         # Record can be created with a typedef prior to the struct definition.
874         self._parse_fields(symbol, compound)
875         compound.add_symbol_reference(symbol)
876         return compound
877
878     def _create_member_compound(self, compound_class, symbol):
879         compound = compound_class(symbol.ident, symbol.ident)
880         self._parse_fields(symbol, compound)
881         compound.add_symbol_reference(symbol)
882         return compound
883
884     def _create_typedef_callback(self, symbol):
885         callback = self._create_callback(symbol)
886         if not callback:
887             return None
888         return callback
889
890     def _parse_fields(self, symbol, compound):
891         for child in symbol.base_type.child_list:
892             child_node = self._traverse_one(child, parent_symbol=symbol)
893             if not child_node:
894                 continue
895             if isinstance(child_node, ast.Field):
896                 field = child_node
897             else:
898                 field = ast.Field(child.ident, None, True, False,
899                               anonymous_node=child_node)
900             compound.fields.append(field)
901
902     def _create_callback(self, symbol, member=False):
903         if (symbol.base_type.type == CTYPE_FUNCTION):  # function
904             paramtype = symbol.base_type
905             retvaltype = symbol.base_type.base_type
906         elif (symbol.base_type.type == CTYPE_POINTER):  # function pointer
907             paramtype = symbol.base_type.base_type
908             retvaltype = symbol.base_type.base_type.base_type
909         parameters = list(self._create_parameters(symbol, paramtype))
910         retval = self._create_return(retvaltype)
911
912         # Mark the 'user_data' arguments
913         for i, param in enumerate(parameters):
914             if (param.type.target_fundamental == 'gpointer' and param.argname == 'user_data'):
915                 param.closure_name = param.argname
916
917         if member:
918             name = symbol.ident
919         elif symbol.ident.find('_') > 0:
920             name = self._strip_symbol(symbol)
921         else:
922             name = self.strip_identifier(symbol.ident)
923         callback = ast.Callback(name, retval, parameters, False,
924                                 ctype=symbol.ident)
925         callback.add_symbol_reference(symbol)
926
927         return callback
928
929     def create_type_from_user_string(self, typestr):
930         """Parse a C type string (as might be given from an
931         annotation) and resolve it.  For compatibility, we can consume
932 both GI type string (utf8, Foo.Bar) style, as well as C (char *, FooBar) style.
933
934 Note that type resolution may not succeed."""
935         if '.' in typestr:
936             container = self._create_bare_container_type(typestr)
937             if container:
938                 typeval = container
939             else:
940                 typeval = self._namespace.type_from_name(typestr)
941         else:
942             typeval = self.create_type_from_ctype_string(typestr)
943
944         self.resolve_type(typeval)
945         if typeval.resolved:
946             # Explicitly clear out the c_type; there isn't one in this case.
947             typeval.ctype = None
948         return typeval
949
950     def _resolve_type_from_ctype_all_namespaces(self, typeval, pointer_stripped):
951         # If we can't determine the namespace from the type name,
952         # fall back to trying all of our includes.  An example of this is mutter,
953         # which has nominal namespace of "Meta", but a few classes are
954         # "Mutter".  We don't export that data in introspection currently.
955         # Basically the library should be fixed, but we'll hack around it here.
956         for namespace in self._parsed_includes.values():
957             target = namespace.get_by_ctype(pointer_stripped)
958             if target:
959                 typeval.target_giname = '%s.%s' % (namespace.name, target.name)
960                 return True
961         return False
962
963     def _resolve_type_from_ctype(self, typeval):
964         assert typeval.ctype is not None
965         pointer_stripped = typeval.ctype.replace('*', '')
966         try:
967             matches = self.split_ctype_namespaces(pointer_stripped)
968         except ValueError:
969             return self._resolve_type_from_ctype_all_namespaces(typeval, pointer_stripped)
970         for namespace, name in matches:
971             target = namespace.get(name)
972             if not target:
973                 target = namespace.get_by_ctype(pointer_stripped)
974             if target:
975                 typeval.target_giname = '%s.%s' % (namespace.name, target.name)
976                 return True
977         return False
978
979     def _resolve_type_from_gtype_name(self, typeval):
980         assert typeval.gtype_name is not None
981         for ns in self._iter_namespaces():
982             node = ns.type_names.get(typeval.gtype_name, None)
983             if node is not None:
984                 typeval.target_giname = '%s.%s' % (ns.name, node.name)
985                 return True
986         return False
987
988     def _resolve_type_internal(self, typeval):
989         if isinstance(typeval, (ast.Array, ast.List)):
990             return self.resolve_type(typeval.element_type)
991         elif isinstance(typeval, ast.Map):
992             key_resolved = self.resolve_type(typeval.key_type)
993             value_resolved = self.resolve_type(typeval.value_type)
994             return key_resolved and value_resolved
995         elif typeval.resolved:
996             return True
997         elif typeval.ctype:
998             return self._resolve_type_from_ctype(typeval)
999         elif typeval.gtype_name:
1000             return self._resolve_type_from_gtype_name(typeval)
1001
1002     def resolve_type(self, typeval):
1003         if not self._resolve_type_internal(typeval):
1004             return False
1005
1006         if typeval.target_fundamental or typeval.target_foreign:
1007             return True
1008
1009         assert typeval.target_giname is not None
1010
1011         try:
1012             type_ = self.lookup_giname(typeval.target_giname)
1013         except KeyError:
1014             type_ = None
1015
1016         if type_ is None:
1017             typeval.target_giname = None
1018
1019         return typeval.resolved
1020
1021     def resolve_aliases(self, typenode):
1022         """Removes all aliases from typenode, returns first non-alias
1023         in the typenode alias chain.  Returns typenode argument if it
1024         is not an alias."""
1025         while isinstance(typenode, ast.Alias):
1026             if typenode.target.target_giname is not None:
1027                 typenode = self.lookup_giname(typenode.target.target_giname)
1028             elif typenode.target.target_fundamental is not None:
1029                 typenode = typenode.target
1030             else:
1031                 break
1032         return typenode