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 .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 .config import DATADIR
31 from .glibast import GLibBoxed
32 from .girparser import GIRParser
33 from .odict import odict
34 from .sourcescanner import (
35 SourceSymbol, ctype_name, CTYPE_POINTER,
36 CTYPE_BASIC_TYPE, CTYPE_UNION, CTYPE_ARRAY, CTYPE_TYPEDEF,
37 CTYPE_VOID, CTYPE_ENUM, CTYPE_FUNCTION, CTYPE_STRUCT,
38 CSYMBOL_TYPE_FUNCTION, CSYMBOL_TYPE_TYPEDEF, CSYMBOL_TYPE_STRUCT,
39 CSYMBOL_TYPE_ENUM, CSYMBOL_TYPE_UNION, CSYMBOL_TYPE_OBJECT,
40 CSYMBOL_TYPE_MEMBER, CSYMBOL_TYPE_ELLIPSIS, CSYMBOL_TYPE_CONST,
42 from .utils import strip_common_prefix, to_underscores
44 _xdg_data_dirs = [x for x in os.environ.get('XDG_DATA_DIRS', '').split(':') \
45 + [DATADIR, '/usr/share'] if x]
48 class SkipError(Exception):
53 names = property(lambda self: self._names)
54 aliases = property(lambda self: self._aliases)
55 type_names = property(lambda self: self._type_names)
56 ctypes = property(lambda self: self._ctypes)
59 super(Names, self).__init__()
60 self._names = odict() # Maps from GIName -> (namespace, node)
61 self._aliases = {} # Maps from GIName -> GIName
62 self._type_names = {} # Maps from GTName -> (namespace, node)
63 self._ctypes = {} # Maps from CType -> (namespace, node)
66 class Transformer(object):
68 def __init__(self, generator, namespace_name, namespace_version):
69 self.generator = generator
70 self._namespace = Namespace(namespace_name, namespace_version)
72 self._typedefs_ns = {}
73 self._strip_prefix = ''
74 self._includes = set()
75 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):
103 filename = self._find_include(include)
104 self._parse_include(filename)
105 self._includes.add(include)
109 def _find_include(self, include):
110 searchdirs = self._includepaths[:]
111 for path in _xdg_data_dirs:
112 searchdirs.append(os.path.join(path, 'gir'))
114 girname = '%s-%s.gir' % (include.name, include.version)
116 path = os.path.join(d, girname)
117 if os.path.exists(path):
120 raise ValueError("Couldn't find include %r (search path: %r)"\
121 % (girname, searchdirs))
123 def _parse_include(self, filename):
124 parser = GIRParser(filename, include_parsing=True)
125 for include in parser.get_includes():
126 self.register_include(include)
127 nsname = parser.get_namespace().name
128 for node in parser.get_namespace().nodes:
129 if isinstance(node, Alias):
130 self._names.aliases[node.name] = (nsname, node)
131 elif isinstance(node, (GLibBoxed, Interface, Class)):
132 self._names.type_names[node.type_name] = (nsname, node)
133 self._names.names[node.name] = (nsname, node)
134 if hasattr(node, 'ctype'):
135 self._names.ctypes[node.ctype] = (nsname, node)
136 elif hasattr(node, 'symbol'):
137 self._names.ctypes[node.symbol] = (nsname, node)
139 def _add_node(self, node):
142 if node.name.startswith('_'):
144 self._namespace.nodes.append(node)
145 self._names.names[node.name] = (None, node)
147 def _strip_namespace_func(self, name):
148 prefix = self._namespace.name.lower() + '_'
149 if name.lower().startswith(prefix):
150 name = name[len(prefix):]
152 prefix = to_underscores(self._namespace.name).lower() + '_'
153 if name.lower().startswith(prefix):
154 name = name[len(prefix):]
155 return self.remove_prefix(name, isfunction=True)
157 def remove_prefix(self, name, isfunction=False):
158 # when --strip-prefix=g:
159 # GHashTable -> HashTable
160 # g_hash_table_new -> hash_table_new
161 prefix = self._strip_prefix.lower()
164 if len(name) > len(prefix) and name.lower().startswith(prefix):
165 name = name[len(prefix):]
167 while name.startswith('_'):
171 def _traverse_one(self, symbol, stype=None):
172 assert isinstance(symbol, SourceSymbol), symbol
176 if stype == CSYMBOL_TYPE_FUNCTION:
178 return self._create_function(symbol)
181 elif stype == CSYMBOL_TYPE_TYPEDEF:
182 return self._create_typedef(symbol)
183 elif stype == CSYMBOL_TYPE_STRUCT:
184 return self._create_struct(symbol)
185 elif stype == CSYMBOL_TYPE_ENUM:
186 return self._create_enum(symbol)
187 elif stype == CSYMBOL_TYPE_OBJECT:
188 return self._create_object(symbol)
189 elif stype == CSYMBOL_TYPE_MEMBER:
190 return self._create_member(symbol)
191 elif stype == CSYMBOL_TYPE_UNION:
192 return self._create_union(symbol)
193 elif stype == CSYMBOL_TYPE_CONST:
194 return self._create_const(symbol)
196 raise NotImplementedError(
197 'Transformer: unhandled symbol: %r' % (symbol, ))
199 def _create_enum(self, symbol):
201 for child in symbol.base_type.child_list:
202 name = strip_common_prefix(symbol.ident, child.ident).lower()
203 members.append(Member(name,
207 enum_name = self.remove_prefix(symbol.ident)
208 enum = Enum(enum_name, symbol.ident, members)
209 self._names.type_names[symbol.ident] = (None, enum)
212 def _create_object(self, symbol):
213 return Member(symbol.ident, symbol.base_type.name,
216 def _parse_deprecated(self, node, directives):
217 deprecated = directives.get('deprecated', False)
219 deprecated_value = deprecated[0]
220 if ':' in deprecated_value:
221 # Split out gtk-doc version
222 (node.deprecated_version, node.deprecated) = \
223 [x.strip() for x in deprecated_value.split(':', 1)]
225 # No version, just include str
226 node.deprecated = deprecated_value.strip()
228 def _pair_array(self, params, array):
229 if not array.type.length_param_name:
231 target_name = array.type.length_param_name
232 for i, param in enumerate(params):
233 if param.name == array.type.length_param_name:
234 array.type.length_param_index = i
236 raise ValueError("Unmatched length parameter name %r"\
239 def _pair_annotations(self, params):
242 if param.name in names:
243 raise ValueError("Duplicate parameter name %r"\
245 names[param.name] = 1
246 if isinstance(param.type, Array):
247 self._pair_array(params, param)
249 # We take the annotations from the parser as strings; here we
250 # want to split them into components, so:
251 # (transfer full) -> {'transfer' : [ 'full' ]}
253 def _parse_options(self, options):
255 ws_re = re.compile(r'\s+')
257 items = ws_re.split(opt)
258 ret[items[0]] = items[1:]
261 def _create_function(self, symbol):
262 directives = symbol.directives()
263 parameters = list(self._create_parameters(
264 symbol.base_type, directives))
265 self._pair_annotations(parameters)
266 return_ = self._create_return(symbol.base_type.base_type,
267 directives.get('return', {}))
268 name = self._strip_namespace_func(symbol.ident)
269 func = Function(name, return_, parameters, symbol.ident)
270 self._parse_deprecated(func, directives)
273 def _create_source_type(self, source_type):
274 if source_type is None:
276 if source_type.type == CTYPE_VOID:
278 elif source_type.type == CTYPE_BASIC_TYPE:
279 value = source_type.name
280 elif source_type.type == CTYPE_TYPEDEF:
281 value = source_type.name
282 elif source_type.type == CTYPE_ARRAY:
283 return self._create_source_type(source_type.base_type)
284 elif source_type.type == CTYPE_POINTER:
285 value = self._create_source_type(source_type.base_type) + '*'
290 def _create_parameters(self, base_type, directives=None):
291 if directives is None:
296 # warn if we see annotations for unknown parameters
297 param_names = set(child.ident for child in base_type.child_list)
299 dirs_for = dirs_for.difference(param_names)
300 dirs_for.discard('return')
302 print 'Unexpected annotations for %s, parameters are %s' % (
303 list(dirs_for), list(param_names), )
305 for child in base_type.child_list:
306 yield self._create_parameter(
307 child, dirs.get(child.ident, {}))
309 def _create_member(self, symbol):
310 ctype = symbol.base_type.type
311 if (ctype == CTYPE_POINTER and
312 symbol.base_type.base_type.type == CTYPE_FUNCTION):
313 node = self._create_callback(symbol)
316 if ctype == CTYPE_ARRAY:
318 child_list = list(symbol.base_type.child_list)
320 size_opt = 'fixed-size=%d' % (child_list[0].const_int, )
321 opts['array'].append(size_opt)
322 ftype = self._create_type(symbol.base_type, opts, True)
323 # Fields are assumed to be read-write
324 # (except for Objects, see also glibtransformer.py)
325 node = Field(symbol.ident, ftype, symbol.ident,
326 readable=True, writable=True, bits=symbol.const_int)
329 def _create_typedef(self, symbol):
330 ctype = symbol.base_type.type
331 if (ctype == CTYPE_POINTER and
332 symbol.base_type.base_type.type == CTYPE_FUNCTION):
333 node = self._create_callback(symbol)
334 elif ctype == CTYPE_STRUCT:
335 node = self._create_typedef_struct(symbol)
336 elif ctype == CTYPE_UNION:
337 node = self._create_typedef_union(symbol)
338 elif ctype == CTYPE_ENUM:
339 return self._create_enum(symbol)
340 elif ctype in (CTYPE_TYPEDEF,
344 name = self.remove_prefix(symbol.ident)
345 if symbol.base_type.name:
346 target = self.remove_prefix(symbol.base_type.name)
349 if name in type_names:
351 return Alias(name, target, ctype=symbol.ident)
353 raise NotImplementedError(
354 "symbol %r of type %s" % (symbol.ident, ctype_name(ctype)))
357 def _parse_ctype(self, ctype):
358 # First look up the ctype including any pointers;
359 # a few type names like 'char*' have their own aliases
360 # and we need pointer information for those.
361 firstpass = type_name_from_ctype(ctype)
363 # Remove all pointers - we require standard calling
364 # conventions. For example, an 'int' is always passed by
365 # value (unless it's out or inout).
366 derefed = firstpass.replace('*', '')
368 # Canonicalize our type again, this time without the pointer;
369 # this ensures we turn e.g. plain "guint" => "int"
370 return type_name_from_ctype(derefed)
372 def _create_type(self, source_type, options, is_param):
373 ctype = self._create_source_type(source_type)
374 if ctype == 'va_list':
376 # FIXME: FILE* should not be skipped, it should be handled
378 elif ctype == 'FILE*':
381 # Now check for a list/map/array type
382 if ctype in self._list_ctypes:
383 param = options.get('element-type')
385 contained_type = self._parse_ctype(param[0])
387 contained_type = None
388 derefed_name = self._parse_ctype(ctype)
389 rettype = List(derefed_name,
392 elif ctype in self._map_ctypes:
393 param = options.get('element-type')
395 key_type = self._parse_ctype(param[0])
396 value_type = self._parse_ctype(param[1])
400 derefed_name = self._parse_ctype(ctype)
401 rettype = Map(derefed_name,
403 key_type, value_type)
404 elif (ctype in default_array_types) or ('array' in options):
405 derefed_name = ctype[:-1] if ctype[-1] == '*' else ctype
406 rettype = Array(ctype,
407 self._parse_ctype(derefed_name))
408 array_opts = dict([opt.split('=')
409 for opt in options.get('array', [])])
410 if 'length' in array_opts:
411 rettype.length_param_name = array_opts['length']
412 if 'fixed-size' in array_opts:
413 rettype.size = array_opts['fixed-size']
414 rettype.zeroterminated = False
416 derefed_name = self._parse_ctype(ctype)
417 rettype = Type(derefed_name, ctype)
419 # Deduce direction for some types passed by reference that
420 # aren't arrays; modifies the options array.
421 if ('array' not in options and
422 not ('out' in options or
424 'inout' in options or
425 'in-out' in options) and
426 source_type.type == CTYPE_POINTER and
427 derefed_name in default_out_types):
430 if 'transfer' in options:
431 # Transfer is specified, we don't question it.
434 canontype = type_name_from_ctype(ctype)
436 # Since no transfer is specified, we drop into a bunch of
437 # heuristics to guess it. This mutates the options array to
438 # set the 'transfer' option.
439 # Note that we inferred the transfer
440 options['transfer-inferred'] = []
442 if canontype == TYPE_STRING:
443 # It's a string - we just look at 'const'
444 if source_type.base_type.type_qualifier & TYPE_QUALIFIER_CONST:
445 options['transfer'] = ['none']
447 options['transfer'] = ['full']
448 elif 'array' in options or stype.type == CTYPE_ARRAY:
449 # It's rare to mutate arrays in public GObject APIs
450 options['transfer'] = ['none']
451 elif (canontype in BASIC_GIR_TYPES or
452 canontype == TYPE_NONE or
453 stype.type == CTYPE_ENUM):
454 # Basic types default to 'none'
455 options['transfer'] = ['none']
456 elif (stype.type == CTYPE_POINTER and
457 stype.base_type.type_qualifier & TYPE_QUALIFIER_CONST):
458 # Anything with 'const' gets none
459 options['transfer'] = ['none']
460 elif is_param and stype.type == CTYPE_POINTER:
461 # For generic pointer types, let's look at the argument
462 # direction. An out/inout argument gets full, everything
464 if ('out' in options or
465 'inout' in options or
466 'in-out' in options):
467 options['transfer'] = ['full']
469 options['transfer'] = ['none']
471 # For anything else we default to none for parameters;
472 # this covers enums and possibly some other corner cases.
473 # Return values of structures and the like will end up
476 options['transfer'] = ['none']
478 options['transfer'] = ['full']
482 def _handle_generic_param_options(self, param, options):
483 for option, data in options.iteritems():
484 if option == 'transfer':
487 if depth not in ('none', 'container', 'full'):
488 raise ValueError("Invalid transfer %r" % (depth, ))
491 param.transfer = depth
492 elif option == 'transfer-inferred':
493 # This is a purely internal flag; we don't expect
495 param.transfer_inferred = True
497 def _create_parameter(self, symbol, options):
498 options = self._parse_options(options)
499 if symbol.type == CSYMBOL_TYPE_ELLIPSIS:
501 if 'transfer' not in options:
502 options['transfer'] = ['none']
504 ptype = self._create_type(symbol.base_type, options, True)
505 param = Parameter(symbol.ident, ptype)
506 for option, data in options.iteritems():
507 if option in ['in-out', 'inout']:
508 param.direction = 'inout'
510 param.direction = 'in'
511 elif option == 'out':
512 param.direction = 'out'
513 elif option == 'allow-none':
514 param.allow_none = True
515 elif option.startswith(('element-type', 'array')):
517 elif option in ('transfer', 'transfer-inferred'):
520 print 'Unhandled parameter annotation option: %r' % (
522 self._handle_generic_param_options(param, options)
524 assert param.transfer is not None, param
527 def _create_return(self, source_type, options=None):
531 options_map = self._parse_options(options)
532 rtype = self._create_type(source_type, options_map, False)
533 rtype = self.resolve_param_type(rtype)
534 return_ = Return(rtype)
535 self._handle_generic_param_options(return_, options_map)
536 for option, data in options_map.iteritems():
537 if option in ('transfer', 'transfer-inferred',
538 'element-type', 'out'):
541 print 'Unhandled return type annotation option: %r' % (
544 assert return_.transfer is not None, return_
547 def _create_const(self, symbol):
548 name = self.remove_prefix(symbol.ident)
549 if symbol.const_string is None:
551 value = symbol.const_int
554 value = symbol.const_string
555 const = Constant(name, type_name, value)
558 def _create_typedef_struct(self, symbol):
559 name = self.remove_prefix(symbol.ident)
560 struct = Struct(name, symbol.ident)
561 self._typedefs_ns[symbol.ident] = struct
562 self._create_struct(symbol)
565 def _create_typedef_union(self, symbol):
566 name = self.remove_prefix(symbol.ident)
567 union = Union(name, symbol.ident)
568 self._typedefs_ns[symbol.ident] = union
569 self._create_union(symbol)
572 def _create_struct(self, symbol):
573 struct = self._typedefs_ns.get(symbol.ident, None)
575 # This is a bit of a hack; really we should try
576 # to resolve through the typedefs to find the real
578 if symbol.ident.startswith('_'):
579 name = symbol.ident[1:]
582 name = self.remove_prefix(name)
583 struct = Struct(name, symbol.ident)
585 for child in symbol.base_type.child_list:
586 field = self._traverse_one(child)
588 struct.fields.append(field)
592 def _create_union(self, symbol):
593 union = self._typedefs_ns.get(symbol.ident, None)
595 # This is a bit of a hack; really we should try
596 # to resolve through the typedefs to find the real
598 if symbol.ident.startswith('_'):
599 name = symbol.ident[1:]
602 name = self.remove_prefix(name)
603 union = Union(name, symbol.ident)
605 for child in symbol.base_type.child_list:
606 field = self._traverse_one(child)
608 union.fields.append(field)
612 def _create_callback(self, symbol):
613 directives = symbol.directives()
614 parameters = self._create_parameters(symbol.base_type.base_type,
616 retval = self._create_return(symbol.base_type.base_type.base_type,
617 directives.get('return', {}))
618 if symbol.ident.find('_') > 0:
619 name = self.remove_prefix(symbol.ident, True)
621 name = self.remove_prefix(symbol.ident)
622 return Callback(name, retval, list(parameters), symbol.ident)
624 def _typepair_to_str(self, item):
628 return '%s.%s' % (nsname, item.name)
630 def _resolve_type_name_1(self, type_name, ctype, names):
631 # First look using the built-in names
634 return type_names[ctype]
638 return type_names[type_name]
641 type_name = self.remove_prefix(type_name)
642 resolved = names.aliases.get(type_name)
644 return self._typepair_to_str(resolved)
645 resolved = names.names.get(type_name)
647 return self._typepair_to_str(resolved)
649 ctype = ctype.replace('*', '')
650 resolved = names.ctypes.get(ctype)
652 return self._typepair_to_str(resolved)
653 resolved = names.type_names.get(type_name)
655 return self._typepair_to_str(resolved)
656 raise KeyError("failed to find %r" % (type_name, ))
658 def resolve_type_name_full(self, type_name, ctype,
659 names, allow_invalid=True):
661 return self._resolve_type_name_1(type_name, ctype, names)
664 return self._resolve_type_name_1(type_name, ctype, self._names)
666 if not allow_invalid:
670 def resolve_type_name(self, type_name, ctype=None):
672 return self.resolve_type_name_full(type_name, ctype, self._names)
676 def gtypename_to_giname(self, gtname, names):
677 resolved = names.type_names.get(gtname)
679 return self._typepair_to_str(resolved)
680 resolved = self._names.type_names.get(gtname)
682 return self._typepair_to_str(resolved)
683 raise KeyError("Failed to resolve GType name: %r" % (gtname, ))
685 def ctype_of(self, obj):
686 if hasattr(obj, 'ctype'):
688 elif hasattr(obj, 'symbol'):
693 def resolve_param_type_full(self, ptype, names, **kwargs):
694 if isinstance(ptype, Node):
695 ptype.name = self.resolve_type_name_full(ptype.name,
696 self.ctype_of(ptype),
698 if isinstance(ptype, (Array, List)):
699 if ptype.element_type is not None:
700 ptype.element_type = \
701 self.resolve_param_type_full(ptype.element_type,
703 if isinstance(ptype, Map):
704 if ptype.key_type is not None:
706 self.resolve_param_type_full(ptype.key_type,
709 self.resolve_param_type_full(ptype.value_type,
711 elif isinstance(ptype, basestring):
712 return self.resolve_type_name_full(ptype, None, names, **kwargs)
714 raise AssertionError("Unhandled param: %r" % (ptype, ))
717 def resolve_param_type(self, ptype):
719 return self.resolve_param_type_full(ptype, self._names)
723 def follow_aliases(self, type_name, names):
725 resolved = names.aliases.get(type_name)
727 (ns, alias) = resolved
728 type_name = alias.target