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 register_include(self, filename):
100 (dirname, basename) = os.path.split(filename)
103 (name, suffix) = os.path.splitext(basename)
107 if name.endswith('.gir'):
108 (name, suffix) = os.path.splitext(name)
109 if name in self._includes:
113 girname = name + '.gir'
114 searchdirs = [os.path.join(d, 'gir') for d \
116 searchdirs.extend(self._includepaths)
118 path = os.path.join(d, girname)
119 if os.path.exists(path):
123 raise ValueError("Couldn't find include %r (search path: %r)"\
124 % (girname, searchdirs))
125 d = os.path.dirname(path)
126 if d not in self._includepaths:
127 self._includepaths.append(d)
128 self._includes.add(name)
129 from .girparser import GIRParser
130 parser = GIRParser(path)
131 for include in parser.get_includes():
132 self.register_include(include)
133 nsname = parser.get_namespace().name
134 for node in parser.get_namespace().nodes:
135 if isinstance(node, Alias):
136 self._names.aliases[node.name] = (nsname, node)
137 elif isinstance(node, (GLibBoxed, Interface, Class)):
138 self._names.type_names[node.type_name] = (nsname, node)
139 self._names.names[node.name] = (nsname, node)
140 if hasattr(node, 'ctype'):
141 self._names.ctypes[node.ctype] = (nsname, node)
142 elif hasattr(node, 'symbol'):
143 self._names.ctypes[node.symbol] = (nsname, node)
145 def strip_namespace_object(self, name):
146 prefix = self._namespace.name.lower()
147 if len(name) > len(prefix) and name.lower().startswith(prefix):
148 return name[len(prefix):]
149 return self._remove_prefix(name)
153 def _add_node(self, node):
156 if node.name.startswith('_'):
158 self._namespace.nodes.append(node)
159 self._names.names[node.name] = (None, node)
161 def _strip_namespace_func(self, name):
162 prefix = self._namespace.name.lower() + '_'
163 if name.lower().startswith(prefix):
164 name = name[len(prefix):]
166 prefix = to_underscores(self._namespace.name).lower() + '_'
167 if name.lower().startswith(prefix):
168 name = name[len(prefix):]
169 return self._remove_prefix(name, isfunction=True)
171 def _remove_prefix(self, name, isfunction=False):
172 # when --strip-prefix=g:
173 # GHashTable -> HashTable
174 # g_hash_table_new -> hash_table_new
175 prefix = self._strip_prefix.lower()
178 if name.lower().startswith(prefix):
179 name = name[len(prefix):]
181 while name.startswith('_'):
185 def _traverse_one(self, symbol, stype=None):
186 assert isinstance(symbol, SourceSymbol), symbol
190 if stype == CSYMBOL_TYPE_FUNCTION:
192 return self._create_function(symbol)
195 elif stype == CSYMBOL_TYPE_TYPEDEF:
196 return self._create_typedef(symbol)
197 elif stype == CSYMBOL_TYPE_STRUCT:
198 return self._create_struct(symbol)
199 elif stype == CSYMBOL_TYPE_ENUM:
200 return self._create_enum(symbol)
201 elif stype == CSYMBOL_TYPE_OBJECT:
202 return self._create_object(symbol)
203 elif stype == CSYMBOL_TYPE_MEMBER:
204 return self._create_member(symbol)
205 elif stype == CSYMBOL_TYPE_UNION:
206 return self._create_union(symbol)
207 elif stype == CSYMBOL_TYPE_CONST:
208 return self._create_const(symbol)
210 raise NotImplementedError(
211 'Transformer: unhandled symbol: %r' % (symbol, ))
213 def _create_enum(self, symbol):
215 for child in symbol.base_type.child_list:
216 name = strip_common_prefix(symbol.ident, child.ident).lower()
217 members.append(Member(name,
221 enum_name = self.strip_namespace_object(symbol.ident)
222 enum_name = symbol.ident[-len(enum_name):]
223 enum_name = self._remove_prefix(enum_name)
224 enum = Enum(enum_name, symbol.ident, members)
225 self._names.type_names[symbol.ident] = (None, enum)
228 def _create_object(self, symbol):
229 return Member(symbol.ident, symbol.base_type.name,
232 def _parse_deprecated(self, node, directives):
233 deprecated = directives.get('deprecated', False)
235 deprecated_value = deprecated[0]
236 if ':' in deprecated_value:
237 # Split out gtk-doc version
238 (node.deprecated_version, node.deprecated) = \
239 [x.strip() for x in deprecated_value.split(':', 1)]
241 # No version, just include str
242 node.deprecated = deprecated_value.strip()
244 def _pair_array(self, params, array):
245 if not array.type.length_param_name:
247 target_name = array.type.length_param_name
248 for i, param in enumerate(params):
249 if param.name == array.type.length_param_name:
250 array.type.length_param_index = i
252 raise ValueError("Unmatched length parameter name %r"\
255 def _pair_annotations(self, params):
258 if param.name in names:
259 raise ValueError("Duplicate parameter name %r"\
261 names[param.name] = 1
262 if isinstance(param.type, Array):
263 self._pair_array(params, param)
265 # We take the annotations from the parser as strings; here we
266 # want to split them into components, so:
267 # (transfer full) -> {'transfer' : [ 'full' ]}
269 def _parse_options(self, options):
271 ws_re = re.compile(r'\s+')
273 items = ws_re.split(opt)
274 ret[items[0]] = items[1:]
277 def _create_function(self, symbol):
278 directives = symbol.directives()
279 parameters = list(self._create_parameters(
280 symbol.base_type, directives))
281 self._pair_annotations(parameters)
282 return_ = self._create_return(symbol.base_type.base_type,
283 directives.get('return', {}))
284 name = self._strip_namespace_func(symbol.ident)
285 func = Function(name, return_, parameters, symbol.ident)
286 self._parse_deprecated(func, directives)
289 def _create_source_type(self, source_type):
290 if source_type is None:
292 if source_type.type == CTYPE_VOID:
294 elif source_type.type == CTYPE_BASIC_TYPE:
295 value = source_type.name
296 elif source_type.type == CTYPE_TYPEDEF:
297 value = source_type.name
298 elif source_type.type == CTYPE_ARRAY:
299 return self._create_source_type(source_type.base_type)
300 elif source_type.type == CTYPE_POINTER:
301 value = self._create_source_type(source_type.base_type) + '*'
303 print 'TRANSFORMER: Unhandled source type %r' % (
308 def _create_parameters(self, base_type, directives=None):
309 if directives is None:
313 for child in base_type.child_list:
314 yield self._create_parameter(
315 child, dirs.get(child.ident, {}))
317 def _create_member(self, symbol):
318 ctype = symbol.base_type.type
319 if (ctype == CTYPE_POINTER and
320 symbol.base_type.base_type.type == CTYPE_FUNCTION):
321 node = self._create_callback(symbol)
323 ftype = self._create_type(symbol.base_type, {})
324 node = Field(symbol.ident, ftype, symbol.ident, symbol.const_int)
327 def _create_typedef(self, symbol):
328 ctype = symbol.base_type.type
329 if (ctype == CTYPE_POINTER and
330 symbol.base_type.base_type.type == CTYPE_FUNCTION):
331 node = self._create_callback(symbol)
332 elif ctype == CTYPE_STRUCT:
333 node = self._create_typedef_struct(symbol)
334 elif ctype == CTYPE_UNION:
335 node = self._create_typedef_union(symbol)
336 elif ctype == CTYPE_ENUM:
337 return self._create_enum(symbol)
338 elif ctype in (CTYPE_TYPEDEF,
342 name = self.strip_namespace_object(symbol.ident)
343 if symbol.base_type.name:
344 target = self.strip_namespace_object(symbol.base_type.name)
347 if name in type_names:
349 return Alias(name, target, ctype=symbol.ident)
351 raise NotImplementedError(
352 "symbol %r of type %s" % (symbol.ident, ctype_name(ctype)))
355 def _parse_ctype(self, ctype):
356 canonical = type_name_from_ctype(ctype)
357 derefed = canonical.replace('*', '')
360 def _create_type(self, source_type, options):
361 ctype = self._create_source_type(source_type)
362 if ctype == 'va_list':
364 # FIXME: FILE* should not be skipped, it should be handled
366 elif ctype == 'FILE*':
368 if ctype in self._list_ctypes:
369 param = options.get('element-type')
371 contained_type = self._parse_ctype(param[0])
373 contained_type = None
374 return List(ctype.replace('*', ''),
377 if ctype in self._map_ctypes:
378 param = options.get('element-type')
380 key_type = self._parse_ctype(param[0])
381 value_type = self._parse_ctype(param[1])
385 return Map(ctype.replace('*', ''),
387 key_type, value_type)
388 if (ctype in default_array_types) or ('array' in options):
389 derefed = ctype[:-1] # strip the *
390 result = Array(ctype,
391 self._parse_ctype(derefed))
392 array_opts = options.get('array')
394 (_, len_name) = array_opts[0].split('=')
395 result.length_param_name = len_name
397 resolved_type_name = self._parse_ctype(ctype)
399 # string memory management
400 if type_name_from_ctype(ctype) == TYPE_STRING:
401 if source_type.base_type.type_qualifier & TYPE_QUALIFIER_CONST:
402 options['transfer'] = ['none']
404 options['transfer'] = ['full']
406 # deduce direction for some types passed by reference
407 if (not ('out' in options or
409 'inout' in options or
410 'in-out' in options) and
411 source_type.type == CTYPE_POINTER and
412 type_name_from_ctype(resolved_type_name) in default_out_types):
415 return Type(resolved_type_name, ctype)
417 def _handle_generic_param_options(self, param, options):
418 for option, data in options.iteritems():
419 if option == 'transfer':
422 if depth not in ('none', 'container', 'full'):
423 raise ValueError("Invalid transfer %r" % (depth, ))
426 param.transfer = depth
428 def _create_parameter(self, symbol, options):
429 options = self._parse_options(options)
430 if symbol.type == CSYMBOL_TYPE_ELLIPSIS:
433 ptype = self._create_type(symbol.base_type, options)
434 param = Parameter(symbol.ident, ptype)
435 for option, data in options.iteritems():
436 if option in ['in-out', 'inout']:
437 param.direction = 'inout'
439 param.direction = 'in'
440 elif option == 'out':
441 param.direction = 'out'
442 elif option == 'allow-none':
443 param.allow_none = True
444 elif option.startswith(('element-type', 'array')):
446 elif option == 'transfer':
449 print 'Unhandled parameter annotation option: %r' % (
451 self._handle_generic_param_options(param, options)
454 def _create_return(self, source_type, options=None):
458 options_map = self._parse_options(options)
459 rtype = self._create_type(source_type, options_map)
460 rtype = self.resolve_param_type(rtype)
461 return_ = Return(rtype)
462 self._handle_generic_param_options(return_, options_map)
463 for option, data in options_map.iteritems():
464 if option == 'transfer':
467 print 'Unhandled return type annotation option: %r' % (
471 def _create_const(self, symbol):
472 name = self._remove_prefix(symbol.ident)
473 name = self._strip_namespace_func(name)
474 if symbol.const_string is None:
476 value = symbol.const_int
479 value = symbol.const_string
480 const = Constant(name, type_name, value)
483 def _create_typedef_struct(self, symbol):
484 name = self.strip_namespace_object(symbol.ident)
485 struct = Struct(name, symbol.ident)
486 self._typedefs_ns[symbol.ident] = struct
487 self._create_struct(symbol)
490 def _create_typedef_union(self, symbol):
491 name = self._remove_prefix(symbol.ident)
492 name = self.strip_namespace_object(name)
493 union = Union(name, symbol.ident)
494 self._typedefs_ns[symbol.ident] = union
495 self._create_union(symbol)
498 def _create_struct(self, symbol):
499 struct = self._typedefs_ns.get(symbol.ident, None)
501 # This is a bit of a hack; really we should try
502 # to resolve through the typedefs to find the real
504 if symbol.ident.startswith('_'):
505 name = symbol.ident[1:]
508 name = self.strip_namespace_object(name)
509 name = self.resolve_type_name(name)
510 struct = Struct(name, symbol.ident)
512 for child in symbol.base_type.child_list:
513 field = self._traverse_one(child)
515 struct.fields.append(field)
519 def _create_union(self, symbol):
520 union = self._typedefs_ns.get(symbol.ident, None)
522 # This is a bit of a hack; really we should try
523 # to resolve through the typedefs to find the real
525 if symbol.ident.startswith('_'):
526 name = symbol.ident[1:]
529 name = self.strip_namespace_object(name)
530 name = self.resolve_type_name(name)
531 union = Union(name, symbol.ident)
533 for child in symbol.base_type.child_list:
534 field = self._traverse_one(child)
536 union.fields.append(field)
540 def _create_callback(self, symbol):
541 parameters = self._create_parameters(symbol.base_type.base_type)
542 retval = self._create_return(symbol.base_type.base_type.base_type)
543 if symbol.ident.find('_') > 0:
544 name = self._strip_namespace_func(symbol.ident)
546 name = self.strip_namespace_object(symbol.ident)
547 return Callback(name, retval, list(parameters), symbol.ident)
549 def _typepair_to_str(self, item):
553 return '%s.%s' % (nsname, item.name)
555 def _resolve_type_name_1(self, type_name, ctype, names):
556 # First look using the built-in names
559 return type_names[ctype]
563 return type_names[type_name]
566 type_name = self.strip_namespace_object(type_name)
567 resolved = names.aliases.get(type_name)
569 return self._typepair_to_str(resolved)
570 resolved = names.names.get(type_name)
572 return self._typepair_to_str(resolved)
574 ctype = ctype.replace('*', '')
575 resolved = names.ctypes.get(ctype)
577 return self._typepair_to_str(resolved)
578 resolved = names.type_names.get(type_name)
580 return self._typepair_to_str(resolved)
581 raise KeyError("failed to find %r" % (type_name, ))
583 def resolve_type_name_full(self, type_name, ctype,
586 return self._resolve_type_name_1(type_name, ctype, names)
589 return self._resolve_type_name_1(type_name, ctype, self._names)
593 def resolve_type_name(self, type_name, ctype=None):
595 return self.resolve_type_name_full(type_name, ctype, self._names)
599 def gtypename_to_giname(self, gtname, names):
600 resolved = names.type_names.get(gtname)
602 return self._typepair_to_str(resolved)
603 resolved = self._names.type_names.get(gtname)
605 return self._typepair_to_str(resolved)
606 raise KeyError("Failed to resolve GType name: %r" % (gtname, ))
608 def ctype_of(self, obj):
609 if hasattr(obj, 'ctype'):
611 elif hasattr(obj, 'symbol'):
616 def resolve_param_type_full(self, ptype, names):
617 if isinstance(ptype, Node):
618 ptype.name = self.resolve_type_name_full(ptype.name,
619 self.ctype_of(ptype),
621 if isinstance(ptype, (Array, List)):
622 if ptype.element_type is not None:
623 ptype.element_type = \
624 self.resolve_param_type_full(ptype.element_type, names)
625 if isinstance(ptype, Map):
626 if ptype.key_type is not None:
628 self.resolve_param_type_full(ptype.key_type, names)
630 self.resolve_param_type_full(ptype.value_type, names)
631 elif isinstance(ptype, basestring):
632 return self.resolve_type_name_full(ptype, None, names)
634 raise AssertionError("Unhandled param: %r" % (ptype, ))
637 def resolve_param_type(self, ptype):
639 return self.resolve_param_type_full(ptype, self._names)
643 def follow_aliases(self, type_name, names):
645 resolved = names.aliases.get(type_name)
647 (ns, alias) = resolved
648 type_name = alias.target