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,
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) + '*'
284 print 'TRANSFORMER: Unhandled source type %r' % (
289 def _create_parameters(self, base_type, directives=None):
290 if directives is None:
295 # warn if we see annotations for unknown parameters
296 param_names = set(child.ident for child in base_type.child_list)
298 dirs_for = dirs_for.difference(param_names)
299 dirs_for.discard('return')
301 print 'Unexpected annotations for %s, parameters are %s' % (
302 list(dirs_for), list(param_names), )
304 for child in base_type.child_list:
305 yield self._create_parameter(
306 child, dirs.get(child.ident, {}))
308 def _create_member(self, symbol):
309 ctype = symbol.base_type.type
310 if (ctype == CTYPE_POINTER and
311 symbol.base_type.base_type.type == CTYPE_FUNCTION):
312 node = self._create_callback(symbol)
314 ftype = self._create_type(symbol.base_type, {})
315 # Fields are assumed to be read-write
316 # (except for Objects, see also glibtransformer.py)
317 node = Field(symbol.ident, ftype, symbol.ident,
318 readable=True, writable=True, bits=symbol.const_int)
321 def _create_typedef(self, symbol):
322 ctype = symbol.base_type.type
323 if (ctype == CTYPE_POINTER and
324 symbol.base_type.base_type.type == CTYPE_FUNCTION):
325 node = self._create_callback(symbol)
326 elif ctype == CTYPE_STRUCT:
327 node = self._create_typedef_struct(symbol)
328 elif ctype == CTYPE_UNION:
329 node = self._create_typedef_union(symbol)
330 elif ctype == CTYPE_ENUM:
331 return self._create_enum(symbol)
332 elif ctype in (CTYPE_TYPEDEF,
336 name = self.remove_prefix(symbol.ident)
337 if symbol.base_type.name:
338 target = self.remove_prefix(symbol.base_type.name)
341 if name in type_names:
343 return Alias(name, target, ctype=symbol.ident)
345 raise NotImplementedError(
346 "symbol %r of type %s" % (symbol.ident, ctype_name(ctype)))
349 def _parse_ctype(self, ctype):
350 # First look up the ctype including any pointers;
351 # a few type names like 'char*' have their own aliases
352 # and we need pointer information for those.
353 firstpass = type_name_from_ctype(ctype)
355 # Remove all pointers - we require standard calling
356 # conventions. For example, an 'int' is always passed by
357 # value (unless it's out or inout).
358 derefed = firstpass.replace('*', '')
360 # Canonicalize our type again, this time without the pointer;
361 # this ensures we turn e.g. plain "guint" => "int"
362 return type_name_from_ctype(derefed)
364 def _create_type(self, source_type, options):
365 ctype = self._create_source_type(source_type)
366 if ctype == 'va_list':
368 # FIXME: FILE* should not be skipped, it should be handled
370 elif ctype == 'FILE*':
373 # Now check for a list/map/array type
374 if ctype in self._list_ctypes:
375 param = options.get('element-type')
377 contained_type = self._parse_ctype(param[0])
379 contained_type = None
380 return List(ctype.replace('*', ''),
383 elif ctype in self._map_ctypes:
384 param = options.get('element-type')
386 key_type = self._parse_ctype(param[0])
387 value_type = self._parse_ctype(param[1])
391 return Map(ctype.replace('*', ''),
393 key_type, value_type)
394 elif (ctype in default_array_types) or ('array' in options):
395 derefed = ctype[:-1] # strip the *
396 result = Array(ctype,
397 self._parse_ctype(derefed))
398 array_opts = options.get('array')
400 (_, len_name) = array_opts[0].split('=')
401 result.length_param_name = len_name
404 # string memory management - we just look at 'const'
405 if (type_name_from_ctype(ctype) == TYPE_STRING
406 and 'transfer' not in options):
407 if source_type.base_type.type_qualifier & TYPE_QUALIFIER_CONST:
408 options['transfer'] = ['none']
410 options['transfer'] = ['full']
412 derefed_name = self._parse_ctype(ctype)
414 # deduce direction for some types passed by reference
415 if (not ('out' in options or
417 'inout' in options or
418 'in-out' in options) and
419 source_type.type == CTYPE_POINTER and
420 derefed_name in default_out_types):
423 return Type(derefed_name, ctype)
425 def _handle_generic_param_options(self, param, options):
426 for option, data in options.iteritems():
427 if option == 'transfer':
430 if depth not in ('none', 'container', 'full'):
431 raise ValueError("Invalid transfer %r" % (depth, ))
434 param.transfer = depth
436 def _create_parameter(self, symbol, options):
437 options = self._parse_options(options)
438 if symbol.type == CSYMBOL_TYPE_ELLIPSIS:
441 ptype = self._create_type(symbol.base_type, options)
442 param = Parameter(symbol.ident, ptype)
443 for option, data in options.iteritems():
444 if option in ['in-out', 'inout']:
445 param.direction = 'inout'
447 param.direction = 'in'
448 elif option == 'out':
449 param.direction = 'out'
450 elif option == 'allow-none':
451 param.allow_none = True
452 elif option.startswith(('element-type', 'array')):
454 elif option == 'transfer':
457 print 'Unhandled parameter annotation option: %r' % (
459 self._handle_generic_param_options(param, options)
462 def _create_return(self, source_type, options=None):
466 options_map = self._parse_options(options)
467 rtype = self._create_type(source_type, options_map)
468 rtype = self.resolve_param_type(rtype)
469 return_ = Return(rtype)
470 self._handle_generic_param_options(return_, options_map)
471 for option, data in options_map.iteritems():
472 if option in ('transfer', 'element-type', 'out'):
475 print 'Unhandled return type annotation option: %r' % (
479 def _create_const(self, symbol):
480 name = self.remove_prefix(symbol.ident)
481 if symbol.const_string is None:
483 value = symbol.const_int
486 value = symbol.const_string
487 const = Constant(name, type_name, value)
490 def _create_typedef_struct(self, symbol):
491 name = self.remove_prefix(symbol.ident)
492 struct = Struct(name, symbol.ident)
493 self._typedefs_ns[symbol.ident] = struct
494 self._create_struct(symbol)
497 def _create_typedef_union(self, symbol):
498 name = self.remove_prefix(symbol.ident)
499 union = Union(name, symbol.ident)
500 self._typedefs_ns[symbol.ident] = union
501 self._create_union(symbol)
504 def _create_struct(self, symbol):
505 struct = self._typedefs_ns.get(symbol.ident, None)
507 # This is a bit of a hack; really we should try
508 # to resolve through the typedefs to find the real
510 if symbol.ident.startswith('_'):
511 name = symbol.ident[1:]
514 name = self.remove_prefix(name)
515 struct = Struct(name, symbol.ident)
517 for child in symbol.base_type.child_list:
518 field = self._traverse_one(child)
520 struct.fields.append(field)
524 def _create_union(self, symbol):
525 union = self._typedefs_ns.get(symbol.ident, None)
527 # This is a bit of a hack; really we should try
528 # to resolve through the typedefs to find the real
530 if symbol.ident.startswith('_'):
531 name = symbol.ident[1:]
534 name = self.remove_prefix(name)
535 union = Union(name, symbol.ident)
537 for child in symbol.base_type.child_list:
538 field = self._traverse_one(child)
540 union.fields.append(field)
544 def _create_callback(self, symbol):
545 parameters = self._create_parameters(symbol.base_type.base_type)
546 retval = self._create_return(symbol.base_type.base_type.base_type)
547 if symbol.ident.find('_') > 0:
548 name = self.remove_prefix(symbol.ident, True)
550 name = self.remove_prefix(symbol.ident)
551 return Callback(name, retval, list(parameters), symbol.ident)
553 def _typepair_to_str(self, item):
557 return '%s.%s' % (nsname, item.name)
559 def _resolve_type_name_1(self, type_name, ctype, names):
560 # First look using the built-in names
563 return type_names[ctype]
567 return type_names[type_name]
570 type_name = self.remove_prefix(type_name)
571 resolved = names.aliases.get(type_name)
573 return self._typepair_to_str(resolved)
574 resolved = names.names.get(type_name)
576 return self._typepair_to_str(resolved)
578 ctype = ctype.replace('*', '')
579 resolved = names.ctypes.get(ctype)
581 return self._typepair_to_str(resolved)
582 resolved = names.type_names.get(type_name)
584 return self._typepair_to_str(resolved)
585 raise KeyError("failed to find %r" % (type_name, ))
587 def resolve_type_name_full(self, type_name, ctype,
588 names, allow_invalid=True):
590 return self._resolve_type_name_1(type_name, ctype, names)
593 return self._resolve_type_name_1(type_name, ctype, self._names)
595 if not allow_invalid:
599 def resolve_type_name(self, type_name, ctype=None):
601 return self.resolve_type_name_full(type_name, ctype, self._names)
605 def gtypename_to_giname(self, gtname, names):
606 resolved = names.type_names.get(gtname)
608 return self._typepair_to_str(resolved)
609 resolved = self._names.type_names.get(gtname)
611 return self._typepair_to_str(resolved)
612 raise KeyError("Failed to resolve GType name: %r" % (gtname, ))
614 def ctype_of(self, obj):
615 if hasattr(obj, 'ctype'):
617 elif hasattr(obj, 'symbol'):
622 def resolve_param_type_full(self, ptype, names, **kwargs):
623 if isinstance(ptype, Node):
624 ptype.name = self.resolve_type_name_full(ptype.name,
625 self.ctype_of(ptype),
627 if isinstance(ptype, (Array, List)):
628 if ptype.element_type is not None:
629 ptype.element_type = \
630 self.resolve_param_type_full(ptype.element_type,
632 if isinstance(ptype, Map):
633 if ptype.key_type is not None:
635 self.resolve_param_type_full(ptype.key_type,
638 self.resolve_param_type_full(ptype.value_type,
640 elif isinstance(ptype, basestring):
641 return self.resolve_type_name_full(ptype, None, names, **kwargs)
643 raise AssertionError("Unhandled param: %r" % (ptype, ))
646 def resolve_param_type(self, ptype):
648 return self.resolve_param_type_full(ptype, self._names)
652 def follow_aliases(self, type_name, names):
654 resolved = names.aliases.get(type_name)
656 (ns, alias) = resolved
657 type_name = alias.target