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:
294 for child in base_type.child_list:
295 yield self._create_parameter(
296 child, dirs.get(child.ident, {}))
298 def _create_member(self, symbol):
299 ctype = symbol.base_type.type
300 if (ctype == CTYPE_POINTER and
301 symbol.base_type.base_type.type == CTYPE_FUNCTION):
302 node = self._create_callback(symbol)
304 ftype = self._create_type(symbol.base_type, {})
305 # Fields are assumed to be read-write
306 # (except for Objects, see also glibtransformer.py)
307 node = Field(symbol.ident, ftype, symbol.ident,
308 readable=True, writable=True, bits=symbol.const_int)
311 def _create_typedef(self, symbol):
312 ctype = symbol.base_type.type
313 if (ctype == CTYPE_POINTER and
314 symbol.base_type.base_type.type == CTYPE_FUNCTION):
315 node = self._create_callback(symbol)
316 elif ctype == CTYPE_STRUCT:
317 node = self._create_typedef_struct(symbol)
318 elif ctype == CTYPE_UNION:
319 node = self._create_typedef_union(symbol)
320 elif ctype == CTYPE_ENUM:
321 return self._create_enum(symbol)
322 elif ctype in (CTYPE_TYPEDEF,
326 name = self.remove_prefix(symbol.ident)
327 if symbol.base_type.name:
328 target = self.remove_prefix(symbol.base_type.name)
331 if name in type_names:
333 return Alias(name, target, ctype=symbol.ident)
335 raise NotImplementedError(
336 "symbol %r of type %s" % (symbol.ident, ctype_name(ctype)))
339 def _parse_ctype(self, ctype):
340 # First look up the ctype including any pointers;
341 # a few type names like 'char*' have their own aliases
342 # and we need pointer information for those.
343 firstpass = type_name_from_ctype(ctype)
345 # Remove all pointers - we require standard calling
346 # conventions. For example, an 'int' is always passed by
347 # value (unless it's out or inout).
348 derefed = firstpass.replace('*', '')
350 # Canonicalize our type again, this time without the pointer;
351 # this ensures we turn e.g. plain "guint" => "int"
352 return type_name_from_ctype(derefed)
354 def _create_type(self, source_type, options):
355 ctype = self._create_source_type(source_type)
356 if ctype == 'va_list':
358 # FIXME: FILE* should not be skipped, it should be handled
360 elif ctype == 'FILE*':
363 # Now check for a list/map/array type
364 if ctype in self._list_ctypes:
365 param = options.get('element-type')
367 contained_type = self._parse_ctype(param[0])
369 contained_type = None
370 return List(ctype.replace('*', ''),
373 elif ctype in self._map_ctypes:
374 param = options.get('element-type')
376 key_type = self._parse_ctype(param[0])
377 value_type = self._parse_ctype(param[1])
381 return Map(ctype.replace('*', ''),
383 key_type, value_type)
384 elif (ctype in default_array_types) or ('array' in options):
385 derefed = ctype[:-1] # strip the *
386 result = Array(ctype,
387 self._parse_ctype(derefed))
388 array_opts = options.get('array')
390 (_, len_name) = array_opts[0].split('=')
391 result.length_param_name = len_name
394 # string memory management - we just look at 'const'
395 if type_name_from_ctype(ctype) == TYPE_STRING:
396 if source_type.base_type.type_qualifier & TYPE_QUALIFIER_CONST:
397 options['transfer'] = ['none']
399 options['transfer'] = ['full']
401 derefed_name = self._parse_ctype(ctype)
403 # deduce direction for some types passed by reference
404 if (not ('out' in options or
406 'inout' in options or
407 'in-out' in options) and
408 source_type.type == CTYPE_POINTER and
409 derefed_name in default_out_types):
412 return Type(derefed_name, ctype)
414 def _handle_generic_param_options(self, param, options):
415 for option, data in options.iteritems():
416 if option == 'transfer':
419 if depth not in ('none', 'container', 'full'):
420 raise ValueError("Invalid transfer %r" % (depth, ))
423 param.transfer = depth
425 def _create_parameter(self, symbol, options):
426 options = self._parse_options(options)
427 if symbol.type == CSYMBOL_TYPE_ELLIPSIS:
430 ptype = self._create_type(symbol.base_type, options)
431 param = Parameter(symbol.ident, ptype)
432 for option, data in options.iteritems():
433 if option in ['in-out', 'inout']:
434 param.direction = 'inout'
436 param.direction = 'in'
437 elif option == 'out':
438 param.direction = 'out'
439 elif option == 'allow-none':
440 param.allow_none = True
441 elif option.startswith(('element-type', 'array')):
443 elif option == 'transfer':
446 print 'Unhandled parameter annotation option: %r' % (
448 self._handle_generic_param_options(param, options)
451 def _create_return(self, source_type, options=None):
455 options_map = self._parse_options(options)
456 rtype = self._create_type(source_type, options_map)
457 rtype = self.resolve_param_type(rtype)
458 return_ = Return(rtype)
459 self._handle_generic_param_options(return_, options_map)
460 for option, data in options_map.iteritems():
461 if option in ('transfer', 'element-type', 'out'):
464 print 'Unhandled return type annotation option: %r' % (
468 def _create_const(self, symbol):
469 name = self.remove_prefix(symbol.ident)
470 if symbol.const_string is None:
472 value = symbol.const_int
475 value = symbol.const_string
476 const = Constant(name, type_name, value)
479 def _create_typedef_struct(self, symbol):
480 name = self.remove_prefix(symbol.ident)
481 struct = Struct(name, symbol.ident)
482 self._typedefs_ns[symbol.ident] = struct
483 self._create_struct(symbol)
486 def _create_typedef_union(self, symbol):
487 name = self.remove_prefix(symbol.ident)
488 union = Union(name, symbol.ident)
489 self._typedefs_ns[symbol.ident] = union
490 self._create_union(symbol)
493 def _create_struct(self, symbol):
494 struct = self._typedefs_ns.get(symbol.ident, None)
496 # This is a bit of a hack; really we should try
497 # to resolve through the typedefs to find the real
499 if symbol.ident.startswith('_'):
500 name = symbol.ident[1:]
503 name = self.remove_prefix(name)
504 struct = Struct(name, symbol.ident)
506 for child in symbol.base_type.child_list:
507 field = self._traverse_one(child)
509 struct.fields.append(field)
513 def _create_union(self, symbol):
514 union = self._typedefs_ns.get(symbol.ident, None)
516 # This is a bit of a hack; really we should try
517 # to resolve through the typedefs to find the real
519 if symbol.ident.startswith('_'):
520 name = symbol.ident[1:]
523 name = self.remove_prefix(name)
524 union = Union(name, symbol.ident)
526 for child in symbol.base_type.child_list:
527 field = self._traverse_one(child)
529 union.fields.append(field)
533 def _create_callback(self, symbol):
534 parameters = self._create_parameters(symbol.base_type.base_type)
535 retval = self._create_return(symbol.base_type.base_type.base_type)
536 if symbol.ident.find('_') > 0:
537 name = self.remove_prefix(symbol.ident, True)
539 name = self.remove_prefix(symbol.ident)
540 return Callback(name, retval, list(parameters), symbol.ident)
542 def _typepair_to_str(self, item):
546 return '%s.%s' % (nsname, item.name)
548 def _resolve_type_name_1(self, type_name, ctype, names):
549 # First look using the built-in names
552 return type_names[ctype]
556 return type_names[type_name]
559 type_name = self.remove_prefix(type_name)
560 resolved = names.aliases.get(type_name)
562 return self._typepair_to_str(resolved)
563 resolved = names.names.get(type_name)
565 return self._typepair_to_str(resolved)
567 ctype = ctype.replace('*', '')
568 resolved = names.ctypes.get(ctype)
570 return self._typepair_to_str(resolved)
571 resolved = names.type_names.get(type_name)
573 return self._typepair_to_str(resolved)
574 raise KeyError("failed to find %r" % (type_name, ))
576 def resolve_type_name_full(self, type_name, ctype,
577 names, allow_invalid=True):
579 return self._resolve_type_name_1(type_name, ctype, names)
582 return self._resolve_type_name_1(type_name, ctype, self._names)
584 if not allow_invalid:
588 def resolve_type_name(self, type_name, ctype=None):
590 return self.resolve_type_name_full(type_name, ctype, self._names)
594 def gtypename_to_giname(self, gtname, names):
595 resolved = names.type_names.get(gtname)
597 return self._typepair_to_str(resolved)
598 resolved = self._names.type_names.get(gtname)
600 return self._typepair_to_str(resolved)
601 raise KeyError("Failed to resolve GType name: %r" % (gtname, ))
603 def ctype_of(self, obj):
604 if hasattr(obj, 'ctype'):
606 elif hasattr(obj, 'symbol'):
611 def resolve_param_type_full(self, ptype, names, **kwargs):
612 if isinstance(ptype, Node):
613 ptype.name = self.resolve_type_name_full(ptype.name,
614 self.ctype_of(ptype),
616 if isinstance(ptype, (Array, List)):
617 if ptype.element_type is not None:
618 ptype.element_type = \
619 self.resolve_param_type_full(ptype.element_type,
621 if isinstance(ptype, Map):
622 if ptype.key_type is not None:
624 self.resolve_param_type_full(ptype.key_type,
627 self.resolve_param_type_full(ptype.value_type,
629 elif isinstance(ptype, basestring):
630 return self.resolve_type_name_full(ptype, None, names, **kwargs)
632 raise AssertionError("Unhandled param: %r" % (ptype, ))
635 def resolve_param_type(self, ptype):
637 return self.resolve_param_type_full(ptype, self._names)
641 def follow_aliases(self, type_name, names):
643 resolved = names.aliases.get(type_name)
645 (ns, alias) = resolved
646 type_name = alias.target