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, Sequence, Struct, Field,
25 Type, Alias, Interface, Class, Node, Union,
26 type_name_from_ctype, type_names)
27 from giscanner.config import DATADIR
28 from .glibast import GLibBoxed
29 from giscanner.sourcescanner import (
30 SourceSymbol, ctype_name, CTYPE_POINTER,
31 CTYPE_BASIC_TYPE, CTYPE_UNION, CTYPE_ARRAY, CTYPE_TYPEDEF,
32 CTYPE_VOID, CTYPE_ENUM, CTYPE_FUNCTION, CTYPE_STRUCT,
33 CSYMBOL_TYPE_FUNCTION, CSYMBOL_TYPE_TYPEDEF, CSYMBOL_TYPE_STRUCT,
34 CSYMBOL_TYPE_ENUM, CSYMBOL_TYPE_UNION, CSYMBOL_TYPE_OBJECT,
36 from .odict import odict
37 from .utils import strip_common_prefix, to_underscores
39 _xdg_data_dirs = [x for x in os.environ.get('XDG_DATA_DIRS', '').split(':') \
40 + [DATADIR, '/usr/share'] if x]
43 class SkipError(Exception):
48 names = property(lambda self: self._names)
49 aliases = property(lambda self: self._aliases)
50 type_names = property(lambda self: self._type_names)
51 ctypes = property(lambda self: self._ctypes)
54 super(Names, self).__init__()
55 self._names = odict() # Maps from GIName -> (namespace, node)
56 self._aliases = {} # Maps from GIName -> GIName
57 self._type_names = {} # Maps from GTName -> (namespace, node)
58 self._ctypes = {} # Maps from CType -> (namespace, node)
61 class Transformer(object):
63 def __init__(self, generator, namespace_name):
64 self.generator = generator
65 self._namespace = Namespace(namespace_name)
67 self._typedefs_ns = {}
68 self._strip_prefix = ''
69 self._includes = set()
70 self._includepaths = []
75 def get_includes(self):
78 def set_strip_prefix(self, strip_prefix):
79 self._strip_prefix = strip_prefix
83 for symbol in self.generator.get_symbols():
84 node = self._traverse_one(symbol)
86 return self._namespace
88 def register_include(self, filename):
89 (path, suffix) = os.path.splitext(filename)
90 name = os.path.basename(path)
91 if name in self._includes:
95 filename = path + suffix
98 if not os.path.exists(filename):
99 searchdirs = [os.path.join(d, 'gir') for d \
101 searchdirs.extend(self._includepaths)
104 source = os.path.join(d, filename)
105 if os.path.exists(source):
109 raise ValueError("Couldn't find include %r (search path: %r)"\
110 % (filename, searchdirs))
111 d = os.path.dirname(source)
112 if d not in self._includepaths:
113 self._includepaths.append(d)
114 self._includes.add(name)
115 from .girparser import GIRParser
116 parser = GIRParser(source)
118 raise NotImplementedError(filename)
119 for include in parser.get_includes():
120 self.register_include(include)
121 nsname = parser.get_namespace_name()
122 for node in parser.get_nodes():
123 if isinstance(node, Alias):
124 self._names.aliases[node.name] = (nsname, node)
125 elif isinstance(node, (GLibBoxed, Interface, Class)):
126 self._names.type_names[node.type_name] = (nsname, node)
127 self._names.names[node.name] = (nsname, node)
128 if hasattr(node, 'ctype'):
129 self._names.ctypes[node.ctype] = (nsname, node)
130 elif hasattr(node, 'symbol'):
131 self._names.ctypes[node.symbol] = (nsname, node)
133 def strip_namespace_object(self, name):
134 prefix = self._namespace.name.lower()
135 if len(name) > len(prefix) and name.lower().startswith(prefix):
136 return name[len(prefix):]
137 return self._remove_prefix(name)
141 def _add_node(self, node):
144 if node.name.startswith('_'):
146 self._namespace.nodes.append(node)
147 self._names.names[node.name] = (None, node)
149 def _strip_namespace_func(self, name):
150 prefix = self._namespace.name.lower() + '_'
151 if name.lower().startswith(prefix):
152 name = name[len(prefix):]
154 prefix = to_underscores(self._namespace.name).lower() + '_'
155 if name.lower().startswith(prefix):
156 name = name[len(prefix):]
157 return self._remove_prefix(name, isfunction=True)
159 def _remove_prefix(self, name, isfunction=False):
160 # when --strip-prefix=g:
161 # GHashTable -> HashTable
162 # g_hash_table_new -> hash_table_new
163 prefix = self._strip_prefix.lower()
166 if name.lower().startswith(prefix):
167 name = name[len(prefix):]
169 while name.startswith('_'):
173 def _traverse_one(self, symbol, stype=None):
174 assert isinstance(symbol, SourceSymbol), symbol
178 if stype == CSYMBOL_TYPE_FUNCTION:
180 return self._create_function(symbol)
183 elif stype == CSYMBOL_TYPE_TYPEDEF:
184 return self._create_typedef(symbol)
185 elif stype == CSYMBOL_TYPE_STRUCT:
186 return self._create_struct(symbol)
187 elif stype == CSYMBOL_TYPE_ENUM:
188 return self._create_enum(symbol)
189 elif stype == CSYMBOL_TYPE_OBJECT:
190 return self._create_object(symbol)
191 elif stype == CSYMBOL_TYPE_MEMBER:
192 return self._create_member(symbol)
193 elif stype == CSYMBOL_TYPE_UNION:
194 return self._create_union(symbol)
196 raise NotImplementedError(
197 'Transformer: unhandled symbol: %r' % (symbol, ))
199 def _create_enum(self, symbol):
201 for child in symbol.base_type.child_list:
202 name = strip_common_prefix(symbol.ident, child.ident).lower()
203 members.append(Member(name,
207 enum_name = self.strip_namespace_object(symbol.ident)
208 enum_name = symbol.ident[-len(enum_name):]
209 enum_name = self._remove_prefix(enum_name)
210 enum = Enum(enum_name, symbol.ident, members)
211 self._names.type_names[symbol.ident] = (None, enum)
214 def _create_object(self, symbol):
215 return Member(symbol.ident, symbol.base_type.name,
218 def _create_function(self, symbol):
219 directives = symbol.directives()
220 parameters = list(self._create_parameters(
221 symbol.base_type, directives))
222 return_ = self._create_return(symbol.base_type.base_type,
223 directives.get('return', []))
224 name = self._strip_namespace_func(symbol.ident)
225 func = Function(name, return_, parameters, symbol.ident)
226 deprecated = directives.get('deprecated', False)
229 # Split out gtk-doc version
230 func.deprecated = deprecated[0].split(':', 1)
231 except ValueError, e:
232 # No version, just include str
233 func.deprecated = (None, deprecated[0])
236 def _create_source_type(self, source_type):
237 if source_type is None:
239 if source_type.type == CTYPE_VOID:
241 elif source_type.type == CTYPE_BASIC_TYPE:
242 value = source_type.name
243 elif source_type.type == CTYPE_TYPEDEF:
244 value = source_type.name
245 elif source_type.type == CTYPE_ARRAY:
246 return self._create_source_type(source_type.base_type)
247 elif source_type.type == CTYPE_POINTER:
248 value = self._create_source_type(source_type.base_type) + '*'
250 print 'TRANSFORMER: Unhandled source type %r' % (
255 def _create_parameters(self, base_type, options=None):
258 for child in base_type.child_list:
259 yield self._create_parameter(
260 child, options.get(child.ident, []))
262 def _create_member(self, symbol):
263 ctype = symbol.base_type.type
264 if (ctype == CTYPE_POINTER and
265 symbol.base_type.base_type.type == CTYPE_FUNCTION):
266 node = self._create_callback(symbol)
268 ftype = self._create_type(symbol.base_type)
269 node = Field(symbol.ident, ftype, symbol.ident)
272 def _create_typedef(self, symbol):
273 ctype = symbol.base_type.type
274 if (ctype == CTYPE_POINTER and
275 symbol.base_type.base_type.type == CTYPE_FUNCTION):
276 node = self._create_callback(symbol)
277 elif ctype == CTYPE_STRUCT:
278 node = self._create_typedef_struct(symbol)
279 elif ctype == CTYPE_UNION:
280 node = self._create_typedef_union(symbol)
281 elif ctype == CTYPE_ENUM:
282 return self._create_enum(symbol)
283 elif ctype in (CTYPE_TYPEDEF,
287 name = self.strip_namespace_object(symbol.ident)
288 if symbol.base_type.name:
289 target = self.strip_namespace_object(symbol.base_type.name)
292 if name in type_names:
294 return Alias(name, target, ctype=symbol.ident)
296 raise NotImplementedError(
297 "symbol %r of type %s" % (symbol.ident, ctype_name(ctype)))
300 def _create_type(self, source_type):
301 ctype = self._create_source_type(source_type)
302 if ctype == 'va_list':
304 # FIXME: FILE* should not be skipped, it should be handled
306 elif ctype == 'FILE*':
308 type_name = type_name_from_ctype(ctype)
309 type_name = type_name.replace('*', '')
310 resolved_type_name = self.resolve_type_name(type_name)
311 return Type(resolved_type_name, ctype)
313 def _create_parameter(self, symbol, options):
314 ptype = self._create_type(symbol.base_type)
315 param = Parameter(symbol.ident, ptype)
316 for option in options:
317 if option in ['in-out', 'inout']:
318 param.direction = 'inout'
320 param.direction = 'in'
321 elif option == 'out':
322 param.direction = 'out'
323 elif option == 'callee-owns':
324 param.transfer = True
325 elif option == 'allow-none':
326 param.allow_none = True
328 print 'Unhandled parameter annotation option: %s' % (
332 def _create_return(self, source_type, options=None):
335 rtype = self._create_type(source_type)
336 rtype = self.resolve_param_type(rtype)
337 return_ = Return(rtype)
338 for option in options:
339 if option == 'caller-owns':
340 return_.transfer = True
341 elif option.startswith('seq '):
342 value, element_options = option[3:].split(None, 2)
343 c_element_type = self._parse_type_annotation(value)
344 element_type = c_element_type.replace('*', '')
345 element_type = self.resolve_type_name(element_type,
347 seq = Sequence(rtype.name,
348 type_name_from_ctype(rtype.name),
353 print 'Unhandled parameter annotation option: %s' % (
357 def _create_typedef_struct(self, symbol):
358 name = self.strip_namespace_object(symbol.ident)
359 struct = Struct(name, symbol.ident)
360 self._typedefs_ns[symbol.ident] = struct
363 def _create_typedef_union(self, symbol):
364 name = self._remove_prefix(symbol.ident)
365 name = self.strip_namespace_object(name)
366 union = Union(name, symbol.ident)
367 self._typedefs_ns[symbol.ident] = union
370 def _create_struct(self, symbol):
371 struct = self._typedefs_ns.get(symbol.ident, None)
373 # This is a bit of a hack; really we should try
374 # to resolve through the typedefs to find the real
376 if symbol.ident.startswith('_'):
377 name = symbol.ident[1:]
380 name = self.strip_namespace_object(name)
381 name = self.resolve_type_name(name)
382 struct = Struct(name, symbol.ident)
384 for child in symbol.base_type.child_list:
385 field = self._traverse_one(child)
387 struct.fields.append(field)
391 def _create_union(self, symbol):
392 union = self._typedefs_ns.get(symbol.ident, None)
394 # This is a bit of a hack; really we should try
395 # to resolve through the typedefs to find the real
397 if symbol.ident.startswith('_'):
398 name = symbol.ident[1:]
401 name = self.strip_namespace_object(name)
402 name = self.resolve_type_name(name)
403 union = Union(name, symbol.ident)
405 for child in symbol.base_type.child_list:
406 field = self._traverse_one(child)
408 union.fields.append(field)
412 def _create_callback(self, symbol):
413 parameters = self._create_parameters(symbol.base_type.base_type)
414 retval = self._create_return(symbol.base_type.base_type.base_type)
415 if symbol.ident.find('_') > 0:
416 name = self._strip_namespace_func(symbol.ident)
418 name = self.strip_namespace_object(symbol.ident)
419 return Callback(name, retval, list(parameters), symbol.ident)
421 def _parse_type_annotation(self, annotation):
422 if (annotation[0] == "[" and
423 annotation[-1] == "]"):
424 return Sequence(self._parse_type_annotation(annotation[1:-1]))
427 def _typepair_to_str(self, item):
431 return '%s.%s' % (nsname, item.name)
433 def _resolve_type_name_1(self, type_name, ctype, names):
434 # First look using the built-in names
437 return type_names[ctype]
441 return type_names[type_name]
444 type_name = self.strip_namespace_object(type_name)
445 resolved = names.aliases.get(type_name)
447 return self._typepair_to_str(resolved)
448 resolved = names.names.get(type_name)
450 return self._typepair_to_str(resolved)
452 ctype = ctype.replace('*', '')
453 resolved = names.ctypes.get(ctype)
455 return self._typepair_to_str(resolved)
456 resolved = names.type_names.get(type_name)
458 return self._typepair_to_str(resolved)
459 raise KeyError("failed to find %r" % (type_name, ))
461 def resolve_type_name_full(self, type_name, ctype,
464 return self._resolve_type_name_1(type_name, ctype, names)
467 return self._resolve_type_name_1(type_name, ctype, self._names)
471 def resolve_type_name(self, type_name, ctype=None):
473 return self.resolve_type_name_full(type_name, ctype, self._names)
477 def gtypename_to_giname(self, gtname, names):
478 resolved = names.type_names.get(gtname)
480 return self._typepair_to_str(resolved)
481 resolved = self._names.type_names.get(gtname)
483 return self._typepair_to_str(resolved)
484 raise KeyError("Failed to resolve GType name: %r" % (gtname, ))
486 def ctype_of(self, obj):
487 if hasattr(obj, 'ctype'):
489 elif hasattr(obj, 'symbol'):
494 def resolve_param_type_full(self, ptype, names):
495 if isinstance(ptype, Sequence):
496 ptype.element_type = \
497 self.resolve_param_type_full(ptype.element_type, names)
498 elif isinstance(ptype, Node):
499 ptype.name = self.resolve_type_name_full(ptype.name,
500 self.ctype_of(ptype),
502 elif isinstance(ptype, basestring):
503 return self.resolve_type_name_full(ptype, None, names)
505 raise AssertionError("Unhandled param: %r" % (ptype, ))
508 def resolve_param_type(self, ptype):
510 return self.resolve_param_type_full(ptype, self._names)
514 def follow_aliases(self, type_name, names):
516 resolved = names.aliases.get(type_name)
518 (ns, alias) = resolved
519 type_name = alias.target