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
23 from giscanner.ast import (Callback, Enum, Function, Namespace, Member,
24 Parameter, Return, Array, Struct, Field,
25 Type, Alias, Interface, Class, Node, Union,
26 List, Map, Varargs, type_name_from_ctype,
27 type_names, default_array_types)
28 from giscanner.config import DATADIR
29 from .glibast import GLibBoxed
30 from giscanner.sourcescanner import (
31 SourceSymbol, ctype_name, CTYPE_POINTER,
32 CTYPE_BASIC_TYPE, CTYPE_UNION, CTYPE_ARRAY, CTYPE_TYPEDEF,
33 CTYPE_VOID, CTYPE_ENUM, CTYPE_FUNCTION, CTYPE_STRUCT,
34 CSYMBOL_TYPE_FUNCTION, CSYMBOL_TYPE_TYPEDEF, CSYMBOL_TYPE_STRUCT,
35 CSYMBOL_TYPE_ENUM, CSYMBOL_TYPE_UNION, CSYMBOL_TYPE_OBJECT,
36 CSYMBOL_TYPE_MEMBER, CSYMBOL_TYPE_ELLIPSIS)
37 from .odict import odict
38 from .utils import strip_common_prefix, to_underscores
40 _xdg_data_dirs = [x for x in os.environ.get('XDG_DATA_DIRS', '').split(':') \
41 + [DATADIR, '/usr/share'] if x]
44 class SkipError(Exception):
49 names = property(lambda self: self._names)
50 aliases = property(lambda self: self._aliases)
51 type_names = property(lambda self: self._type_names)
52 ctypes = property(lambda self: self._ctypes)
55 super(Names, self).__init__()
56 self._names = odict() # Maps from GIName -> (namespace, node)
57 self._aliases = {} # Maps from GIName -> GIName
58 self._type_names = {} # Maps from GTName -> (namespace, node)
59 self._ctypes = {} # Maps from CType -> (namespace, node)
62 class Transformer(object):
64 def __init__(self, generator, namespace_name):
65 self.generator = generator
66 self._namespace = Namespace(namespace_name)
68 self._typedefs_ns = {}
69 self._strip_prefix = ''
70 self._includes = set()
71 self._includepaths = []
73 self._list_ctypes = []
79 def get_includes(self):
82 def set_container_types(self, list_ctypes, map_ctypes):
83 self._list_ctypes = list_ctypes
84 self._map_ctypes = map_ctypes
86 def set_strip_prefix(self, strip_prefix):
87 self._strip_prefix = strip_prefix
91 for symbol in self.generator.get_symbols():
92 node = self._traverse_one(symbol)
94 return self._namespace
96 def register_include(self, filename):
97 (path, suffix) = os.path.splitext(filename)
98 name = os.path.basename(path)
99 if name in self._includes:
103 filename = path + suffix
106 if not os.path.exists(filename):
107 searchdirs = [os.path.join(d, 'gir') for d \
109 searchdirs.extend(self._includepaths)
112 source = os.path.join(d, filename)
113 if os.path.exists(source):
117 raise ValueError("Couldn't find include %r (search path: %r)"\
118 % (filename, searchdirs))
119 d = os.path.dirname(source)
120 if d not in self._includepaths:
121 self._includepaths.append(d)
122 self._includes.add(name)
123 from .girparser import GIRParser
124 parser = GIRParser(source)
126 raise NotImplementedError(filename)
127 for include in parser.get_includes():
128 self.register_include(include)
129 nsname = parser.get_namespace().name
130 for node in parser.get_namespace().nodes:
131 if isinstance(node, Alias):
132 self._names.aliases[node.name] = (nsname, node)
133 elif isinstance(node, (GLibBoxed, Interface, Class)):
134 self._names.type_names[node.type_name] = (nsname, node)
135 self._names.names[node.name] = (nsname, node)
136 if hasattr(node, 'ctype'):
137 self._names.ctypes[node.ctype] = (nsname, node)
138 elif hasattr(node, 'symbol'):
139 self._names.ctypes[node.symbol] = (nsname, node)
141 def strip_namespace_object(self, name):
142 prefix = self._namespace.name.lower()
143 if len(name) > len(prefix) and name.lower().startswith(prefix):
144 return name[len(prefix):]
145 return self._remove_prefix(name)
149 def _add_node(self, node):
152 if node.name.startswith('_'):
154 self._namespace.nodes.append(node)
155 self._names.names[node.name] = (None, node)
157 def _strip_namespace_func(self, name):
158 prefix = self._namespace.name.lower() + '_'
159 if name.lower().startswith(prefix):
160 name = name[len(prefix):]
162 prefix = to_underscores(self._namespace.name).lower() + '_'
163 if name.lower().startswith(prefix):
164 name = name[len(prefix):]
165 return self._remove_prefix(name, isfunction=True)
167 def _remove_prefix(self, name, isfunction=False):
168 # when --strip-prefix=g:
169 # GHashTable -> HashTable
170 # g_hash_table_new -> hash_table_new
171 prefix = self._strip_prefix.lower()
174 if name.lower().startswith(prefix):
175 name = name[len(prefix):]
177 while name.startswith('_'):
181 def _traverse_one(self, symbol, stype=None):
182 assert isinstance(symbol, SourceSymbol), symbol
186 if stype == CSYMBOL_TYPE_FUNCTION:
188 return self._create_function(symbol)
191 elif stype == CSYMBOL_TYPE_TYPEDEF:
192 return self._create_typedef(symbol)
193 elif stype == CSYMBOL_TYPE_STRUCT:
194 return self._create_struct(symbol)
195 elif stype == CSYMBOL_TYPE_ENUM:
196 return self._create_enum(symbol)
197 elif stype == CSYMBOL_TYPE_OBJECT:
198 return self._create_object(symbol)
199 elif stype == CSYMBOL_TYPE_MEMBER:
200 return self._create_member(symbol)
201 elif stype == CSYMBOL_TYPE_UNION:
202 return self._create_union(symbol)
204 raise NotImplementedError(
205 'Transformer: unhandled symbol: %r' % (symbol, ))
207 def _create_enum(self, symbol):
209 for child in symbol.base_type.child_list:
210 name = strip_common_prefix(symbol.ident, child.ident).lower()
211 members.append(Member(name,
215 enum_name = self.strip_namespace_object(symbol.ident)
216 enum_name = symbol.ident[-len(enum_name):]
217 enum_name = self._remove_prefix(enum_name)
218 enum = Enum(enum_name, symbol.ident, members)
219 self._names.type_names[symbol.ident] = (None, enum)
222 def _create_object(self, symbol):
223 return Member(symbol.ident, symbol.base_type.name,
226 def _parse_deprecated(self, node, directives):
227 deprecated = directives.get('deprecated', False)
229 deprecated_value = deprecated[0]
230 if ':' in deprecated_value:
231 # Split out gtk-doc version
232 (node.deprecated_version, node.deprecated) = \
233 [x.strip() for x in deprecated_value.split(':', 1)]
235 # No version, just include str
236 node.deprecated = deprecated_value.strip()
238 def _pair_array(self, params, array):
239 if not array.type.length_param_name:
241 target_name = array.type.length_param_name
242 for i, param in enumerate(params):
243 if param.name == array.type.length_param_name:
244 array.type.length_param_index = i
246 raise ValueError("Unmatched length parameter name %r"\
249 def _pair_annotations(self, params):
252 if param.name in names:
253 raise ValueError("Duplicate parameter name %r"\
255 names[param.name] = 1
256 if isinstance(param.type, Array):
257 self._pair_array(params, param)
259 def _create_function(self, symbol):
260 directives = symbol.directives()
261 parameters = list(self._create_parameters(
262 symbol.base_type, directives))
263 self._pair_annotations(parameters)
264 return_ = self._create_return(symbol.base_type.base_type,
265 directives.get('return', []))
266 name = self._strip_namespace_func(symbol.ident)
267 func = Function(name, return_, parameters, symbol.ident)
268 self._parse_deprecated(func, directives)
271 def _create_source_type(self, source_type):
272 if source_type is None:
274 if source_type.type == CTYPE_VOID:
276 elif source_type.type == CTYPE_BASIC_TYPE:
277 value = source_type.name
278 elif source_type.type == CTYPE_TYPEDEF:
279 value = source_type.name
280 elif source_type.type == CTYPE_ARRAY:
281 return self._create_source_type(source_type.base_type)
282 elif source_type.type == CTYPE_POINTER:
283 value = self._create_source_type(source_type.base_type) + '*'
285 print 'TRANSFORMER: Unhandled source type %r' % (
290 def _create_parameters(self, base_type, options=None):
293 for child in base_type.child_list:
294 yield self._create_parameter(
295 child, options.get(child.ident, []))
297 def _create_member(self, symbol):
298 ctype = symbol.base_type.type
299 if (ctype == CTYPE_POINTER and
300 symbol.base_type.base_type.type == CTYPE_FUNCTION):
301 node = self._create_callback(symbol)
303 ftype = self._create_type(symbol.base_type)
304 node = Field(symbol.ident, ftype, symbol.ident, symbol.const_int)
307 def _create_typedef(self, symbol):
308 ctype = symbol.base_type.type
309 if (ctype == CTYPE_POINTER and
310 symbol.base_type.base_type.type == CTYPE_FUNCTION):
311 node = self._create_callback(symbol)
312 elif ctype == CTYPE_STRUCT:
313 node = self._create_typedef_struct(symbol)
314 elif ctype == CTYPE_UNION:
315 node = self._create_typedef_union(symbol)
316 elif ctype == CTYPE_ENUM:
317 return self._create_enum(symbol)
318 elif ctype in (CTYPE_TYPEDEF,
322 name = self.strip_namespace_object(symbol.ident)
323 if symbol.base_type.name:
324 target = self.strip_namespace_object(symbol.base_type.name)
327 if name in type_names:
329 return Alias(name, target, ctype=symbol.ident)
331 raise NotImplementedError(
332 "symbol %r of type %s" % (symbol.ident, ctype_name(ctype)))
335 def _parse_ctype(self, ctype):
336 canonical = type_name_from_ctype(ctype)
337 derefed = canonical.replace('*', '')
340 def _create_type(self, source_type, options=[]):
341 ctype = self._create_source_type(source_type)
342 if ctype == 'va_list':
344 # FIXME: FILE* should not be skipped, it should be handled
346 elif ctype == 'FILE*':
348 if ctype in self._list_ctypes:
350 contained_type = self._parse_ctype(options[0])
353 contained_type = None
354 return List(ctype.replace('*', ''),
357 if ctype in self._list_ctypes:
359 key_type = self._parse_ctype(options[0])
360 value_type = self._parse_ctype(options[1])
365 return Map(ctype.replace('*', ''),
367 key_type, value_type)
368 if (ctype in default_array_types) or ('array' in options):
369 if 'array' in options:
370 options.remove('array')
371 derefed = ctype[:-1] # strip the *
373 self._parse_ctype(derefed))
374 resolved_type_name = self._parse_ctype(ctype)
375 return Type(resolved_type_name, ctype)
377 def _create_parameter(self, symbol, options):
378 if symbol.type == CSYMBOL_TYPE_ELLIPSIS:
381 ptype = self._create_type(symbol.base_type, options)
382 param = Parameter(symbol.ident, ptype)
383 for option in options:
384 if option in ['in-out', 'inout']:
385 param.direction = 'inout'
387 param.direction = 'in'
388 elif option == 'out':
389 param.direction = 'out'
390 elif option == 'transfer':
391 param.transfer = True
392 elif option == 'notransfer':
393 param.transfer = False
394 elif isinstance(ptype, Array) and option.startswith('length'):
395 (_, index_param) = option.split('=')
396 ptype.length_param_name = index_param
397 elif option == 'allow-none':
398 param.allow_none = True
400 print 'Unhandled parameter annotation option: %r' % (
404 def _create_return(self, source_type, options=[]):
405 rtype = self._create_type(source_type, options)
406 rtype = self.resolve_param_type(rtype)
407 return_ = Return(rtype)
408 for option in options:
409 if option == 'transfer':
410 return_.transfer = True
412 print 'Unhandled parameter annotation option: %r' % (
416 def _create_typedef_struct(self, symbol):
417 name = self.strip_namespace_object(symbol.ident)
418 struct = Struct(name, symbol.ident)
419 self._typedefs_ns[symbol.ident] = struct
420 self._create_struct(symbol)
423 def _create_typedef_union(self, symbol):
424 name = self._remove_prefix(symbol.ident)
425 name = self.strip_namespace_object(name)
426 union = Union(name, symbol.ident)
427 self._typedefs_ns[symbol.ident] = union
428 self._create_union(symbol)
431 def _create_struct(self, symbol):
432 struct = self._typedefs_ns.get(symbol.ident, None)
434 # This is a bit of a hack; really we should try
435 # to resolve through the typedefs to find the real
437 if symbol.ident.startswith('_'):
438 name = symbol.ident[1:]
441 name = self.strip_namespace_object(name)
442 name = self.resolve_type_name(name)
443 struct = Struct(name, symbol.ident)
445 for child in symbol.base_type.child_list:
446 field = self._traverse_one(child)
448 struct.fields.append(field)
452 def _create_union(self, symbol):
453 union = self._typedefs_ns.get(symbol.ident, None)
455 # This is a bit of a hack; really we should try
456 # to resolve through the typedefs to find the real
458 if symbol.ident.startswith('_'):
459 name = symbol.ident[1:]
462 name = self.strip_namespace_object(name)
463 name = self.resolve_type_name(name)
464 union = Union(name, symbol.ident)
466 for child in symbol.base_type.child_list:
467 field = self._traverse_one(child)
469 union.fields.append(field)
473 def _create_callback(self, symbol):
474 parameters = self._create_parameters(symbol.base_type.base_type)
475 retval = self._create_return(symbol.base_type.base_type.base_type)
476 if symbol.ident.find('_') > 0:
477 name = self._strip_namespace_func(symbol.ident)
479 name = self.strip_namespace_object(symbol.ident)
480 return Callback(name, retval, list(parameters), symbol.ident)
482 def _typepair_to_str(self, item):
486 return '%s.%s' % (nsname, item.name)
488 def _resolve_type_name_1(self, type_name, ctype, names):
489 # First look using the built-in names
492 return type_names[ctype]
496 return type_names[type_name]
499 type_name = self.strip_namespace_object(type_name)
500 resolved = names.aliases.get(type_name)
502 return self._typepair_to_str(resolved)
503 resolved = names.names.get(type_name)
505 return self._typepair_to_str(resolved)
507 ctype = ctype.replace('*', '')
508 resolved = names.ctypes.get(ctype)
510 return self._typepair_to_str(resolved)
511 resolved = names.type_names.get(type_name)
513 return self._typepair_to_str(resolved)
514 raise KeyError("failed to find %r" % (type_name, ))
516 def resolve_type_name_full(self, type_name, ctype,
519 return self._resolve_type_name_1(type_name, ctype, names)
522 return self._resolve_type_name_1(type_name, ctype, self._names)
526 def resolve_type_name(self, type_name, ctype=None):
528 return self.resolve_type_name_full(type_name, ctype, self._names)
532 def gtypename_to_giname(self, gtname, names):
533 resolved = names.type_names.get(gtname)
535 return self._typepair_to_str(resolved)
536 resolved = self._names.type_names.get(gtname)
538 return self._typepair_to_str(resolved)
539 raise KeyError("Failed to resolve GType name: %r" % (gtname, ))
541 def ctype_of(self, obj):
542 if hasattr(obj, 'ctype'):
544 elif hasattr(obj, 'symbol'):
549 def resolve_param_type_full(self, ptype, names):
550 if isinstance(ptype, Node):
551 ptype.name = self.resolve_type_name_full(ptype.name,
552 self.ctype_of(ptype),
554 if isinstance(ptype, (Array, List)):
555 if ptype.element_type is not None:
556 ptype.element_type = \
557 self.resolve_param_type_full(ptype.element_type, names)
558 if isinstance(ptype, Map):
559 if ptype.key_type is not None:
561 self.resolve_param_type_full(ptype.key_type, names)
563 self.resolve_param_type_full(ptype.value_type, names)
564 elif isinstance(ptype, basestring):
565 return self.resolve_type_name_full(ptype, None, names)
567 raise AssertionError("Unhandled param: %r" % (ptype, ))
570 def resolve_param_type(self, ptype):
572 return self.resolve_param_type_full(ptype, self._names)
576 def follow_aliases(self, type_name, names):
578 resolved = names.aliases.get(type_name)
580 (ns, alias) = resolved
581 type_name = alias.target