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)
134 def strip_namespace_object(self, name):
135 prefix = self._namespace.name.lower()
136 if len(name) > len(prefix) and name.lower().startswith(prefix):
137 return name[len(prefix):]
138 return self._remove_prefix(name)
142 def _add_node(self, node):
145 if node.name.startswith('_'):
147 self._namespace.nodes.append(node)
148 self._names.names[node.name] = (None, node)
150 def _strip_namespace_func(self, name):
151 prefix = self._namespace.name.lower() + '_'
152 if name.lower().startswith(prefix):
153 name = name[len(prefix):]
155 prefix = to_underscores(self._namespace.name).lower() + '_'
156 if name.lower().startswith(prefix):
157 name = name[len(prefix):]
158 return self._remove_prefix(name, isfunction=True)
160 def _remove_prefix(self, name, isfunction=False):
161 # when --strip-prefix=g:
162 # GHashTable -> HashTable
163 # g_hash_table_new -> hash_table_new
164 prefix = self._strip_prefix.lower()
167 if name.lower().startswith(prefix):
168 name = name[len(prefix):]
170 while name.startswith('_'):
174 def _traverse_one(self, symbol, stype=None):
175 assert isinstance(symbol, SourceSymbol), symbol
179 if stype == CSYMBOL_TYPE_FUNCTION:
181 return self._create_function(symbol)
184 elif stype == CSYMBOL_TYPE_TYPEDEF:
185 return self._create_typedef(symbol)
186 elif stype == CSYMBOL_TYPE_STRUCT:
187 return self._create_struct(symbol)
188 elif stype == CSYMBOL_TYPE_ENUM:
189 return self._create_enum(symbol)
190 elif stype == CSYMBOL_TYPE_OBJECT:
191 return self._create_object(symbol)
192 elif stype == CSYMBOL_TYPE_MEMBER:
193 return self._create_member(symbol)
194 elif stype == CSYMBOL_TYPE_UNION:
195 return self._create_union(symbol)
196 elif stype == CSYMBOL_TYPE_CONST:
197 return self._create_const(symbol)
199 raise NotImplementedError(
200 'Transformer: unhandled symbol: %r' % (symbol, ))
202 def _create_enum(self, symbol):
204 for child in symbol.base_type.child_list:
205 name = strip_common_prefix(symbol.ident, child.ident).lower()
206 members.append(Member(name,
210 enum_name = self.strip_namespace_object(symbol.ident)
211 enum_name = symbol.ident[-len(enum_name):]
212 enum_name = self._remove_prefix(enum_name)
213 enum = Enum(enum_name, symbol.ident, members)
214 self._names.type_names[symbol.ident] = (None, enum)
217 def _create_object(self, symbol):
218 return Member(symbol.ident, symbol.base_type.name,
221 def _parse_deprecated(self, node, directives):
222 deprecated = directives.get('deprecated', False)
224 deprecated_value = deprecated[0]
225 if ':' in deprecated_value:
226 # Split out gtk-doc version
227 (node.deprecated_version, node.deprecated) = \
228 [x.strip() for x in deprecated_value.split(':', 1)]
230 # No version, just include str
231 node.deprecated = deprecated_value.strip()
233 def _pair_array(self, params, array):
234 if not array.type.length_param_name:
236 target_name = array.type.length_param_name
237 for i, param in enumerate(params):
238 if param.name == array.type.length_param_name:
239 array.type.length_param_index = i
241 raise ValueError("Unmatched length parameter name %r"\
244 def _pair_annotations(self, params):
247 if param.name in names:
248 raise ValueError("Duplicate parameter name %r"\
250 names[param.name] = 1
251 if isinstance(param.type, Array):
252 self._pair_array(params, param)
254 # We take the annotations from the parser as strings; here we
255 # want to split them into components, so:
256 # (transfer full) -> {'transfer' : [ 'full' ]}
258 def _parse_options(self, options):
260 ws_re = re.compile(r'\s+')
262 items = ws_re.split(opt)
263 ret[items[0]] = items[1:]
266 def _create_function(self, symbol):
267 directives = symbol.directives()
268 parameters = list(self._create_parameters(
269 symbol.base_type, directives))
270 self._pair_annotations(parameters)
271 return_ = self._create_return(symbol.base_type.base_type,
272 directives.get('return', {}))
273 name = self._strip_namespace_func(symbol.ident)
274 func = Function(name, return_, parameters, symbol.ident)
275 self._parse_deprecated(func, directives)
278 def _create_source_type(self, source_type):
279 if source_type is None:
281 if source_type.type == CTYPE_VOID:
283 elif source_type.type == CTYPE_BASIC_TYPE:
284 value = source_type.name
285 elif source_type.type == CTYPE_TYPEDEF:
286 value = source_type.name
287 elif source_type.type == CTYPE_ARRAY:
288 return self._create_source_type(source_type.base_type)
289 elif source_type.type == CTYPE_POINTER:
290 value = self._create_source_type(source_type.base_type) + '*'
292 print 'TRANSFORMER: Unhandled source type %r' % (
297 def _create_parameters(self, base_type, directives=None):
298 if directives is None:
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, {})
313 node = Field(symbol.ident, ftype, symbol.ident, symbol.const_int)
316 def _create_typedef(self, symbol):
317 ctype = symbol.base_type.type
318 if (ctype == CTYPE_POINTER and
319 symbol.base_type.base_type.type == CTYPE_FUNCTION):
320 node = self._create_callback(symbol)
321 elif ctype == CTYPE_STRUCT:
322 node = self._create_typedef_struct(symbol)
323 elif ctype == CTYPE_UNION:
324 node = self._create_typedef_union(symbol)
325 elif ctype == CTYPE_ENUM:
326 return self._create_enum(symbol)
327 elif ctype in (CTYPE_TYPEDEF,
331 name = self.strip_namespace_object(symbol.ident)
332 if symbol.base_type.name:
333 target = self.strip_namespace_object(symbol.base_type.name)
336 if name in type_names:
338 return Alias(name, target, ctype=symbol.ident)
340 raise NotImplementedError(
341 "symbol %r of type %s" % (symbol.ident, ctype_name(ctype)))
344 def _parse_ctype(self, ctype):
345 derefed = ctype.replace('*', '')
346 return type_name_from_ctype(derefed)
348 def _create_type(self, source_type, options):
349 ctype = self._create_source_type(source_type)
350 if ctype == 'va_list':
352 # FIXME: FILE* should not be skipped, it should be handled
354 elif ctype == 'FILE*':
357 # Now check for a list/map/array type
358 if ctype in self._list_ctypes:
359 param = options.get('element-type')
361 contained_type = self._parse_ctype(param[0])
363 contained_type = None
364 return List(ctype.replace('*', ''),
367 elif ctype in self._map_ctypes:
368 param = options.get('element-type')
370 key_type = self._parse_ctype(param[0])
371 value_type = self._parse_ctype(param[1])
375 return Map(ctype.replace('*', ''),
377 key_type, value_type)
378 elif (ctype in default_array_types) or ('array' in options):
379 derefed = ctype[:-1] # strip the *
380 result = Array(ctype,
381 self._parse_ctype(derefed))
382 array_opts = options.get('array')
384 (_, len_name) = array_opts[0].split('=')
385 result.length_param_name = len_name
388 # string memory management - we just look at 'const'
389 if type_name_from_ctype(ctype) == TYPE_STRING:
390 if source_type.base_type.type_qualifier & TYPE_QUALIFIER_CONST:
391 options['transfer'] = ['none']
393 options['transfer'] = ['full']
395 derefed_name = self._parse_ctype(ctype)
397 # deduce direction for some types passed by reference
398 if (not ('out' in options or
400 'inout' in options or
401 'in-out' in options) and
402 source_type.type == CTYPE_POINTER and
403 derefed_name in default_out_types):
406 return Type(derefed_name, ctype)
408 def _handle_generic_param_options(self, param, options):
409 for option, data in options.iteritems():
410 if option == 'transfer':
413 if depth not in ('none', 'container', 'full'):
414 raise ValueError("Invalid transfer %r" % (depth, ))
417 param.transfer = depth
419 def _create_parameter(self, symbol, options):
420 options = self._parse_options(options)
421 if symbol.type == CSYMBOL_TYPE_ELLIPSIS:
424 ptype = self._create_type(symbol.base_type, options)
425 param = Parameter(symbol.ident, ptype)
426 for option, data in options.iteritems():
427 if option in ['in-out', 'inout']:
428 param.direction = 'inout'
430 param.direction = 'in'
431 elif option == 'out':
432 param.direction = 'out'
433 elif option == 'allow-none':
434 param.allow_none = True
435 elif option.startswith(('element-type', 'array')):
437 elif option == 'transfer':
440 print 'Unhandled parameter annotation option: %r' % (
442 self._handle_generic_param_options(param, options)
445 def _create_return(self, source_type, options=None):
449 options_map = self._parse_options(options)
450 rtype = self._create_type(source_type, options_map)
451 rtype = self.resolve_param_type(rtype)
452 return_ = Return(rtype)
453 self._handle_generic_param_options(return_, options_map)
454 for option, data in options_map.iteritems():
455 if option == 'transfer':
458 print 'Unhandled return type annotation option: %r' % (
462 def _create_const(self, symbol):
463 name = self._remove_prefix(symbol.ident)
464 name = self._strip_namespace_func(name)
465 if symbol.const_string is None:
467 value = symbol.const_int
470 value = symbol.const_string
471 const = Constant(name, type_name, value)
474 def _create_typedef_struct(self, symbol):
475 name = self.strip_namespace_object(symbol.ident)
476 struct = Struct(name, symbol.ident)
477 self._typedefs_ns[symbol.ident] = struct
478 self._create_struct(symbol)
481 def _create_typedef_union(self, symbol):
482 name = self._remove_prefix(symbol.ident)
483 name = self.strip_namespace_object(name)
484 union = Union(name, symbol.ident)
485 self._typedefs_ns[symbol.ident] = union
486 self._create_union(symbol)
489 def _create_struct(self, symbol):
490 struct = self._typedefs_ns.get(symbol.ident, None)
492 # This is a bit of a hack; really we should try
493 # to resolve through the typedefs to find the real
495 if symbol.ident.startswith('_'):
496 name = symbol.ident[1:]
499 name = self.strip_namespace_object(name)
500 struct = Struct(name, symbol.ident)
502 for child in symbol.base_type.child_list:
503 field = self._traverse_one(child)
505 struct.fields.append(field)
509 def _create_union(self, symbol):
510 union = self._typedefs_ns.get(symbol.ident, None)
512 # This is a bit of a hack; really we should try
513 # to resolve through the typedefs to find the real
515 if symbol.ident.startswith('_'):
516 name = symbol.ident[1:]
519 name = self.strip_namespace_object(name)
520 union = Union(name, symbol.ident)
522 for child in symbol.base_type.child_list:
523 field = self._traverse_one(child)
525 union.fields.append(field)
529 def _create_callback(self, symbol):
530 parameters = self._create_parameters(symbol.base_type.base_type)
531 retval = self._create_return(symbol.base_type.base_type.base_type)
532 if symbol.ident.find('_') > 0:
533 name = self._strip_namespace_func(symbol.ident)
535 name = self.strip_namespace_object(symbol.ident)
536 return Callback(name, retval, list(parameters), symbol.ident)
538 def _typepair_to_str(self, item):
542 return '%s.%s' % (nsname, item.name)
544 def _resolve_type_name_1(self, type_name, ctype, names):
545 # First look using the built-in names
548 return type_names[ctype]
552 return type_names[type_name]
555 type_name = self.strip_namespace_object(type_name)
556 resolved = names.aliases.get(type_name)
558 return self._typepair_to_str(resolved)
559 resolved = names.names.get(type_name)
561 return self._typepair_to_str(resolved)
563 ctype = ctype.replace('*', '')
564 resolved = names.ctypes.get(ctype)
566 return self._typepair_to_str(resolved)
567 resolved = names.type_names.get(type_name)
569 return self._typepair_to_str(resolved)
570 raise KeyError("failed to find %r" % (type_name, ))
572 def resolve_type_name_full(self, type_name, ctype,
575 return self._resolve_type_name_1(type_name, ctype, names)
578 return self._resolve_type_name_1(type_name, ctype, self._names)
582 def resolve_type_name(self, type_name, ctype=None):
584 return self.resolve_type_name_full(type_name, ctype, self._names)
588 def gtypename_to_giname(self, gtname, names):
589 resolved = names.type_names.get(gtname)
591 return self._typepair_to_str(resolved)
592 resolved = self._names.type_names.get(gtname)
594 return self._typepair_to_str(resolved)
595 raise KeyError("Failed to resolve GType name: %r" % (gtname, ))
597 def ctype_of(self, obj):
598 if hasattr(obj, 'ctype'):
600 elif hasattr(obj, 'symbol'):
605 def resolve_param_type_full(self, ptype, names):
606 if isinstance(ptype, Node):
607 ptype.name = self.resolve_type_name_full(ptype.name,
608 self.ctype_of(ptype),
610 if isinstance(ptype, (Array, List)):
611 if ptype.element_type is not None:
612 ptype.element_type = \
613 self.resolve_param_type_full(ptype.element_type, names)
614 if isinstance(ptype, Map):
615 if ptype.key_type is not None:
617 self.resolve_param_type_full(ptype.key_type, names)
619 self.resolve_param_type_full(ptype.value_type, names)
620 elif isinstance(ptype, basestring):
621 return self.resolve_type_name_full(ptype, None, names)
623 raise AssertionError("Unhandled param: %r" % (ptype, ))
626 def resolve_param_type(self, ptype):
628 return self.resolve_param_type_full(ptype, self._names)
632 def follow_aliases(self, type_name, names):
634 resolved = names.aliases.get(type_name)
636 (ns, alias) = resolved
637 type_name = alias.target