2 # GObject-Introspection - a framework for introspecting GObject libraries
3 # Copyright (C) 2008 Johan Dahlin
5 # This program is free software; you can redistribute it and/or
6 # modify it under the terms of the GNU General Public License
7 # as published by the Free Software Foundation; either version 2
8 # of the License, or (at your option) any later version.
10 # This program 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
13 # GNU General Public License for more details.
15 # You should have received a copy of the GNU General Public License
16 # along with this program; if not, write to the Free Software
17 # Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
24 from giscanner.ast import (Callback, Enum, Function, Namespace, Member,
25 Parameter, Return, Array, Struct, Field,
26 Type, Alias, Interface, Class, Node, Union,
27 List, Map, Varargs, Constant, type_name_from_ctype,
28 type_names, default_array_types, default_out_types,
29 TYPE_STRING, BASIC_GIR_TYPES, TYPE_NONE)
30 from giscanner.config import DATADIR
31 from .glibast import GLibBoxed
32 from giscanner.sourcescanner import (
33 SourceSymbol, ctype_name, CTYPE_POINTER,
34 CTYPE_BASIC_TYPE, CTYPE_UNION, CTYPE_ARRAY, CTYPE_TYPEDEF,
35 CTYPE_VOID, CTYPE_ENUM, CTYPE_FUNCTION, CTYPE_STRUCT,
36 CSYMBOL_TYPE_FUNCTION, CSYMBOL_TYPE_TYPEDEF, CSYMBOL_TYPE_STRUCT,
37 CSYMBOL_TYPE_ENUM, CSYMBOL_TYPE_UNION, CSYMBOL_TYPE_OBJECT,
38 CSYMBOL_TYPE_MEMBER, CSYMBOL_TYPE_ELLIPSIS, CSYMBOL_TYPE_CONST,
40 from .odict import odict
41 from .utils import strip_common_prefix, to_underscores
43 _xdg_data_dirs = [x for x in os.environ.get('XDG_DATA_DIRS', '').split(':') \
44 + [DATADIR, '/usr/share'] if x]
47 class SkipError(Exception):
52 names = property(lambda self: self._names)
53 aliases = property(lambda self: self._aliases)
54 type_names = property(lambda self: self._type_names)
55 ctypes = property(lambda self: self._ctypes)
58 super(Names, self).__init__()
59 self._names = odict() # Maps from GIName -> (namespace, node)
60 self._aliases = {} # Maps from GIName -> GIName
61 self._type_names = {} # Maps from GTName -> (namespace, node)
62 self._ctypes = {} # Maps from CType -> (namespace, node)
65 class Transformer(object):
67 def __init__(self, generator, namespace_name, namespace_version):
68 self.generator = generator
69 self._namespace = Namespace(namespace_name, namespace_version)
71 self._typedefs_ns = {}
72 self._strip_prefix = ''
73 self._includes = set()
74 self._includepaths = []
76 self._list_ctypes = []
82 def get_includes(self):
85 def set_container_types(self, list_ctypes, map_ctypes):
86 self._list_ctypes = list_ctypes
87 self._map_ctypes = map_ctypes
89 def set_strip_prefix(self, strip_prefix):
90 self._strip_prefix = strip_prefix
94 for symbol in self.generator.get_symbols():
95 node = self._traverse_one(symbol)
97 return self._namespace
99 def set_include_paths(self, paths):
100 self._includepaths = list(paths)
102 def register_include(self, include, path=None):
104 girname = '%s-%s.gir' % (include.name, include.version)
105 searchdirs = list(self._includepaths)
106 searchdirs.extend([os.path.join(d, 'gir')
107 for d in _xdg_data_dirs])
109 path = os.path.join(d, girname)
110 if os.path.exists(path):
114 raise ValueError("Couldn't find include %r (search path: %r)"\
115 % (girname, searchdirs))
116 d = os.path.dirname(path)
117 self._includes.add(include)
118 from .girparser import GIRParser
119 parser = GIRParser(path)
120 for include in parser.get_includes():
121 self.register_include(include)
122 nsname = parser.get_namespace().name
123 for node in parser.get_namespace().nodes:
124 if isinstance(node, Alias):
125 self._names.aliases[node.name] = (nsname, node)
126 elif isinstance(node, (GLibBoxed, Interface, Class)):
127 self._names.type_names[node.type_name] = (nsname, node)
128 self._names.names[node.name] = (nsname, node)
129 if hasattr(node, 'ctype'):
130 self._names.ctypes[node.ctype] = (nsname, node)
131 elif hasattr(node, 'symbol'):
132 self._names.ctypes[node.symbol] = (nsname, node)
136 def _add_node(self, node):
139 if node.name.startswith('_'):
141 self._namespace.nodes.append(node)
142 self._names.names[node.name] = (None, node)
144 def _strip_namespace_func(self, name):
145 prefix = self._namespace.name.lower() + '_'
146 if name.lower().startswith(prefix):
147 name = name[len(prefix):]
149 prefix = to_underscores(self._namespace.name).lower() + '_'
150 if name.lower().startswith(prefix):
151 name = name[len(prefix):]
152 return self.remove_prefix(name, isfunction=True)
154 def remove_prefix(self, name, isfunction=False):
155 # when --strip-prefix=g:
156 # GHashTable -> HashTable
157 # g_hash_table_new -> hash_table_new
158 prefix = self._strip_prefix.lower()
161 if len(name) > len(prefix) and name.lower().startswith(prefix):
162 name = name[len(prefix):]
164 while name.startswith('_'):
168 def _traverse_one(self, symbol, stype=None):
169 assert isinstance(symbol, SourceSymbol), symbol
173 if stype == CSYMBOL_TYPE_FUNCTION:
175 return self._create_function(symbol)
178 elif stype == CSYMBOL_TYPE_TYPEDEF:
179 return self._create_typedef(symbol)
180 elif stype == CSYMBOL_TYPE_STRUCT:
181 return self._create_struct(symbol)
182 elif stype == CSYMBOL_TYPE_ENUM:
183 return self._create_enum(symbol)
184 elif stype == CSYMBOL_TYPE_OBJECT:
185 return self._create_object(symbol)
186 elif stype == CSYMBOL_TYPE_MEMBER:
187 return self._create_member(symbol)
188 elif stype == CSYMBOL_TYPE_UNION:
189 return self._create_union(symbol)
190 elif stype == CSYMBOL_TYPE_CONST:
191 return self._create_const(symbol)
193 raise NotImplementedError(
194 'Transformer: unhandled symbol: %r' % (symbol, ))
196 def _create_enum(self, symbol):
198 for child in symbol.base_type.child_list:
199 name = strip_common_prefix(symbol.ident, child.ident).lower()
200 members.append(Member(name,
204 enum_name = self.remove_prefix(symbol.ident)
205 enum = Enum(enum_name, symbol.ident, members)
206 self._names.type_names[symbol.ident] = (None, enum)
209 def _create_object(self, symbol):
210 return Member(symbol.ident, symbol.base_type.name,
213 def _parse_deprecated(self, node, directives):
214 deprecated = directives.get('deprecated', False)
216 deprecated_value = deprecated[0]
217 if ':' in deprecated_value:
218 # Split out gtk-doc version
219 (node.deprecated_version, node.deprecated) = \
220 [x.strip() for x in deprecated_value.split(':', 1)]
222 # No version, just include str
223 node.deprecated = deprecated_value.strip()
225 def _pair_array(self, params, array):
226 if not array.type.length_param_name:
228 target_name = array.type.length_param_name
229 for i, param in enumerate(params):
230 if param.name == array.type.length_param_name:
231 array.type.length_param_index = i
233 raise ValueError("Unmatched length parameter name %r"\
236 def _pair_annotations(self, params):
239 if param.name in names:
240 raise ValueError("Duplicate parameter name %r"\
242 names[param.name] = 1
243 if isinstance(param.type, Array):
244 self._pair_array(params, param)
246 # We take the annotations from the parser as strings; here we
247 # want to split them into components, so:
248 # (transfer full) -> {'transfer' : [ 'full' ]}
250 def _parse_options(self, options):
252 ws_re = re.compile(r'\s+')
254 items = ws_re.split(opt)
255 ret[items[0]] = items[1:]
258 def _create_function(self, symbol):
259 directives = symbol.directives()
260 parameters = list(self._create_parameters(
261 symbol.base_type, directives))
262 self._pair_annotations(parameters)
263 return_ = self._create_return(symbol.base_type.base_type,
264 directives.get('return', {}))
265 name = self._strip_namespace_func(symbol.ident)
266 func = Function(name, return_, parameters, symbol.ident)
267 self._parse_deprecated(func, directives)
270 def _create_source_type(self, source_type):
271 if source_type is None:
273 if source_type.type == CTYPE_VOID:
275 elif source_type.type == CTYPE_BASIC_TYPE:
276 value = source_type.name
277 elif source_type.type == CTYPE_TYPEDEF:
278 value = source_type.name
279 elif source_type.type == CTYPE_ARRAY:
280 return self._create_source_type(source_type.base_type)
281 elif source_type.type == CTYPE_POINTER:
282 value = self._create_source_type(source_type.base_type) + '*'
287 def _create_parameters(self, base_type, directives=None):
288 if directives is None:
293 # warn if we see annotations for unknown parameters
294 param_names = set(child.ident for child in base_type.child_list)
296 dirs_for = dirs_for.difference(param_names)
297 dirs_for.discard('return')
299 print 'Unexpected annotations for %s, parameters are %s' % (
300 list(dirs_for), list(param_names), )
302 for child in base_type.child_list:
303 yield self._create_parameter(
304 child, dirs.get(child.ident, {}))
306 def _create_member(self, symbol):
307 ctype = symbol.base_type.type
308 if (ctype == CTYPE_POINTER and
309 symbol.base_type.base_type.type == CTYPE_FUNCTION):
310 node = self._create_callback(symbol)
312 ftype = self._create_type(symbol.base_type, {}, True)
313 # Fields are assumed to be read-write
314 # (except for Objects, see also glibtransformer.py)
315 node = Field(symbol.ident, ftype, symbol.ident,
316 readable=True, writable=True, bits=symbol.const_int)
319 def _create_typedef(self, symbol):
320 ctype = symbol.base_type.type
321 if (ctype == CTYPE_POINTER and
322 symbol.base_type.base_type.type == CTYPE_FUNCTION):
323 node = self._create_callback(symbol)
324 elif ctype == CTYPE_STRUCT:
325 node = self._create_typedef_struct(symbol)
326 elif ctype == CTYPE_UNION:
327 node = self._create_typedef_union(symbol)
328 elif ctype == CTYPE_ENUM:
329 return self._create_enum(symbol)
330 elif ctype in (CTYPE_TYPEDEF,
334 name = self.remove_prefix(symbol.ident)
335 if symbol.base_type.name:
336 target = self.remove_prefix(symbol.base_type.name)
339 if name in type_names:
341 return Alias(name, target, ctype=symbol.ident)
343 raise NotImplementedError(
344 "symbol %r of type %s" % (symbol.ident, ctype_name(ctype)))
347 def _parse_ctype(self, ctype):
348 # First look up the ctype including any pointers;
349 # a few type names like 'char*' have their own aliases
350 # and we need pointer information for those.
351 firstpass = type_name_from_ctype(ctype)
353 # Remove all pointers - we require standard calling
354 # conventions. For example, an 'int' is always passed by
355 # value (unless it's out or inout).
356 derefed = firstpass.replace('*', '')
358 # Canonicalize our type again, this time without the pointer;
359 # this ensures we turn e.g. plain "guint" => "int"
360 return type_name_from_ctype(derefed)
362 def _create_type(self, source_type, options, is_param):
363 ctype = self._create_source_type(source_type)
364 if ctype == 'va_list':
366 # FIXME: FILE* should not be skipped, it should be handled
368 elif ctype == 'FILE*':
371 # Now check for a list/map/array type
372 if ctype in self._list_ctypes:
373 param = options.get('element-type')
375 contained_type = self._parse_ctype(param[0])
377 contained_type = None
378 derefed_name = self._parse_ctype(ctype)
379 rettype = List(derefed_name,
382 elif ctype in self._map_ctypes:
383 param = options.get('element-type')
385 key_type = self._parse_ctype(param[0])
386 value_type = self._parse_ctype(param[1])
390 derefed_name = self._parse_ctype(ctype)
391 rettype = Map(derefed_name,
393 key_type, value_type)
394 elif (ctype in default_array_types) or ('array' in options):
395 derefed_name = ctype[:-1] # strip the *
396 rettype = Array(ctype,
397 self._parse_ctype(derefed_name))
398 array_opts = options.get('array')
400 (_, len_name) = array_opts[0].split('=')
401 rettype.length_param_name = len_name
403 derefed_name = self._parse_ctype(ctype)
404 rettype = Type(derefed_name, ctype)
406 # Deduce direction for some types passed by reference that
407 # aren't arrays; modifies the options array.
408 if ('array' not in options and
409 not ('out' in options or
411 'inout' in options or
412 'in-out' in options) and
413 source_type.type == CTYPE_POINTER and
414 derefed_name in default_out_types):
417 if 'transfer' in options:
418 # Transfer is specified, we don't question it.
421 canontype = type_name_from_ctype(ctype)
423 # Since no transfer is specified, we drop into a bunch of
424 # heuristics to guess it. This mutates the options array to
425 # set the 'transfer' option.
426 # Note that we inferred the transfer
427 options['transfer-inferred'] = []
429 if canontype == TYPE_STRING:
430 # It's a string - we just look at 'const'
431 if source_type.base_type.type_qualifier & TYPE_QUALIFIER_CONST:
432 options['transfer'] = ['none']
434 options['transfer'] = ['full']
435 elif 'array' in options or stype.type == CTYPE_ARRAY:
436 # It's rare to mutate arrays in public GObject APIs
437 options['transfer'] = ['none']
438 elif (canontype in BASIC_GIR_TYPES or
439 canontype == TYPE_NONE or
440 stype.type == CTYPE_ENUM):
441 # Basic types default to 'none'
442 options['transfer'] = ['none']
443 elif (stype.type == CTYPE_POINTER and
444 stype.base_type.type_qualifier & TYPE_QUALIFIER_CONST):
445 # Anything with 'const' gets none
446 options['transfer'] = ['none']
447 elif is_param and stype.type == CTYPE_POINTER:
448 # For generic pointer types, let's look at the argument
449 # direction. An out/inout argument gets full, everything
451 if ('out' in options or
452 'inout' in options or
453 'in-out' in options):
454 options['transfer'] = ['full']
456 options['transfer'] = ['none']
458 # For anything else we default to none for parameters;
459 # this covers enums and possibly some other corner cases.
460 # Return values of structures and the like will end up
463 options['transfer'] = ['none']
465 options['transfer'] = ['full']
469 def _handle_generic_param_options(self, param, options):
470 for option, data in options.iteritems():
471 if option == 'transfer':
474 if depth not in ('none', 'container', 'full'):
475 raise ValueError("Invalid transfer %r" % (depth, ))
478 param.transfer = depth
479 elif option == 'transfer-inferred':
480 # This is a purely internal flag; we don't expect
482 param.transfer_inferred = True
484 def _create_parameter(self, symbol, options):
485 options = self._parse_options(options)
486 if symbol.type == CSYMBOL_TYPE_ELLIPSIS:
488 if 'transfer' not in options:
489 options['transfer'] = ['none']
491 ptype = self._create_type(symbol.base_type, options, True)
492 param = Parameter(symbol.ident, ptype)
493 for option, data in options.iteritems():
494 if option in ['in-out', 'inout']:
495 param.direction = 'inout'
497 param.direction = 'in'
498 elif option == 'out':
499 param.direction = 'out'
500 elif option == 'allow-none':
501 param.allow_none = True
502 elif option.startswith(('element-type', 'array')):
504 elif option in ('transfer', 'transfer-inferred'):
507 print 'Unhandled parameter annotation option: %r' % (
509 self._handle_generic_param_options(param, options)
511 assert param.transfer is not None, param
514 def _create_return(self, source_type, options=None):
518 options_map = self._parse_options(options)
519 rtype = self._create_type(source_type, options_map, False)
520 rtype = self.resolve_param_type(rtype)
521 return_ = Return(rtype)
522 self._handle_generic_param_options(return_, options_map)
523 for option, data in options_map.iteritems():
524 if option in ('transfer', 'transfer-inferred',
525 'element-type', 'out'):
528 print 'Unhandled return type annotation option: %r' % (
531 assert return_.transfer is not None, return_
534 def _create_const(self, symbol):
535 name = self.remove_prefix(symbol.ident)
536 if symbol.const_string is None:
538 value = symbol.const_int
541 value = symbol.const_string
542 const = Constant(name, type_name, value)
545 def _create_typedef_struct(self, symbol):
546 name = self.remove_prefix(symbol.ident)
547 struct = Struct(name, symbol.ident)
548 self._typedefs_ns[symbol.ident] = struct
549 self._create_struct(symbol)
552 def _create_typedef_union(self, symbol):
553 name = self.remove_prefix(symbol.ident)
554 union = Union(name, symbol.ident)
555 self._typedefs_ns[symbol.ident] = union
556 self._create_union(symbol)
559 def _create_struct(self, symbol):
560 struct = self._typedefs_ns.get(symbol.ident, None)
562 # This is a bit of a hack; really we should try
563 # to resolve through the typedefs to find the real
565 if symbol.ident.startswith('_'):
566 name = symbol.ident[1:]
569 name = self.remove_prefix(name)
570 struct = Struct(name, symbol.ident)
572 for child in symbol.base_type.child_list:
573 field = self._traverse_one(child)
575 struct.fields.append(field)
579 def _create_union(self, symbol):
580 union = self._typedefs_ns.get(symbol.ident, None)
582 # This is a bit of a hack; really we should try
583 # to resolve through the typedefs to find the real
585 if symbol.ident.startswith('_'):
586 name = symbol.ident[1:]
589 name = self.remove_prefix(name)
590 union = Union(name, symbol.ident)
592 for child in symbol.base_type.child_list:
593 field = self._traverse_one(child)
595 union.fields.append(field)
599 def _create_callback(self, symbol):
600 directives = symbol.directives()
601 parameters = self._create_parameters(symbol.base_type.base_type,
603 retval = self._create_return(symbol.base_type.base_type.base_type,
604 directives.get('return', {}))
605 if symbol.ident.find('_') > 0:
606 name = self.remove_prefix(symbol.ident, True)
608 name = self.remove_prefix(symbol.ident)
609 return Callback(name, retval, list(parameters), symbol.ident)
611 def _typepair_to_str(self, item):
615 return '%s.%s' % (nsname, item.name)
617 def _resolve_type_name_1(self, type_name, ctype, names):
618 # First look using the built-in names
621 return type_names[ctype]
625 return type_names[type_name]
628 type_name = self.remove_prefix(type_name)
629 resolved = names.aliases.get(type_name)
631 return self._typepair_to_str(resolved)
632 resolved = names.names.get(type_name)
634 return self._typepair_to_str(resolved)
636 ctype = ctype.replace('*', '')
637 resolved = names.ctypes.get(ctype)
639 return self._typepair_to_str(resolved)
640 resolved = names.type_names.get(type_name)
642 return self._typepair_to_str(resolved)
643 raise KeyError("failed to find %r" % (type_name, ))
645 def resolve_type_name_full(self, type_name, ctype,
646 names, allow_invalid=True):
648 return self._resolve_type_name_1(type_name, ctype, names)
651 return self._resolve_type_name_1(type_name, ctype, self._names)
653 if not allow_invalid:
657 def resolve_type_name(self, type_name, ctype=None):
659 return self.resolve_type_name_full(type_name, ctype, self._names)
663 def gtypename_to_giname(self, gtname, names):
664 resolved = names.type_names.get(gtname)
666 return self._typepair_to_str(resolved)
667 resolved = self._names.type_names.get(gtname)
669 return self._typepair_to_str(resolved)
670 raise KeyError("Failed to resolve GType name: %r" % (gtname, ))
672 def ctype_of(self, obj):
673 if hasattr(obj, 'ctype'):
675 elif hasattr(obj, 'symbol'):
680 def resolve_param_type_full(self, ptype, names, **kwargs):
681 if isinstance(ptype, Node):
682 ptype.name = self.resolve_type_name_full(ptype.name,
683 self.ctype_of(ptype),
685 if isinstance(ptype, (Array, List)):
686 if ptype.element_type is not None:
687 ptype.element_type = \
688 self.resolve_param_type_full(ptype.element_type,
690 if isinstance(ptype, Map):
691 if ptype.key_type is not None:
693 self.resolve_param_type_full(ptype.key_type,
696 self.resolve_param_type_full(ptype.value_type,
698 elif isinstance(ptype, basestring):
699 return self.resolve_type_name_full(ptype, None, names, **kwargs)
701 raise AssertionError("Unhandled param: %r" % (ptype, ))
704 def resolve_param_type(self, ptype):
706 return self.resolve_param_type_full(ptype, self._names)
710 def follow_aliases(self, type_name, names):
712 resolved = names.aliases.get(type_name)
714 (ns, alias) = resolved
715 type_name = alias.target