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
21 from giscanner.ast import (Callback, Enum, Function, Namespace, Member,
22 Parameter, Return, Sequence, Struct, Field,
23 Type, Alias, Interface, Class, Node, Union,
24 type_name_from_ctype, type_names)
25 from .glibast import GLibBoxed
26 from giscanner.sourcescanner import (
27 SourceSymbol, ctype_name, CTYPE_POINTER,
28 CTYPE_BASIC_TYPE, CTYPE_UNION, CTYPE_ARRAY, CTYPE_TYPEDEF,
29 CTYPE_VOID, CTYPE_ENUM, CTYPE_FUNCTION, CTYPE_STRUCT,
30 CSYMBOL_TYPE_FUNCTION, CSYMBOL_TYPE_TYPEDEF, CSYMBOL_TYPE_STRUCT,
31 CSYMBOL_TYPE_ENUM, CSYMBOL_TYPE_UNION, CSYMBOL_TYPE_OBJECT,
33 from .odict import odict
34 from .utils import strip_common_prefix
37 class SkipError(Exception):
42 names = property(lambda self: self._names)
43 aliases = property(lambda self: self._aliases)
44 type_names = property(lambda self: self._type_names)
45 ctypes = property(lambda self: self._ctypes)
48 super(Names, self).__init__()
49 self._names = odict() # Maps from GIName -> (namespace, node)
50 self._aliases = {} # Maps from GIName -> GIName
51 self._type_names = {} # Maps from GTName -> (namespace, node)
52 self._ctypes = {} # Maps from CType -> (namespace, node)
55 class Transformer(object):
57 def __init__(self, generator, namespace_name):
58 self.generator = generator
59 self._namespace = Namespace(namespace_name)
61 self._typedefs_ns = {}
62 self._strip_prefix = ''
67 def set_strip_prefix(self, strip_prefix):
68 self._strip_prefix = strip_prefix
72 for symbol in self.generator.get_symbols():
73 node = self._traverse_one(symbol)
75 return self._namespace
77 def register_include(self, filename):
78 if filename.endswith('.gir'):
79 from .girparser import GIRParser
80 parser = GIRParser(filename)
81 elif filename.endswith('.gidl'):
82 from .gidlparser import GIDLParser
83 parser = GIDLParser(filename)
85 raise NotImplementedError(filename)
86 nsname = parser.get_namespace_name()
87 for node in parser.get_nodes():
88 if isinstance(node, Alias):
89 self._names.aliases[node.name] = (nsname, node)
90 elif isinstance(node, (GLibBoxed, Interface, Class)):
91 self._names.type_names[node.type_name] = (nsname, node)
92 self._names.names[node.name] = (nsname, node)
93 if hasattr(node, 'ctype'):
94 self._names.ctypes[node.ctype] = (nsname, node)
95 elif hasattr(node, 'symbol'):
96 self._names.ctypes[node.symbol] = (nsname, node)
98 def strip_namespace_object(self, name):
99 prefix = self._namespace.name.lower()
100 if len(name) > len(prefix) and name.lower().startswith(prefix):
101 return name[len(prefix):]
102 return self._remove_prefix(name)
106 def _add_node(self, node):
109 if node.name.startswith('_'):
111 self._namespace.nodes.append(node)
112 self._names.names[node.name] = (None, node)
114 def _strip_namespace_func(self, name):
115 prefix = self._namespace.name.lower() + '_'
116 if name.lower().startswith(prefix):
117 name = name[len(prefix):]
118 return self._remove_prefix(name)
120 def _remove_prefix(self, name):
121 # when --strip-prefix=g:
122 # GHashTable -> HashTable
123 # g_hash_table_new -> hash_table_new
124 if name.lower().startswith(self._strip_prefix.lower()):
125 name = name[len(self._strip_prefix):]
127 while name.startswith('_'):
131 def _traverse_one(self, symbol, stype=None):
132 assert isinstance(symbol, SourceSymbol), symbol
136 if stype == CSYMBOL_TYPE_FUNCTION:
138 return self._create_function(symbol)
141 elif stype == CSYMBOL_TYPE_TYPEDEF:
142 return self._create_typedef(symbol)
143 elif stype == CSYMBOL_TYPE_STRUCT:
144 return self._create_struct(symbol)
145 elif stype == CSYMBOL_TYPE_ENUM:
146 return self._create_enum(symbol)
147 elif stype == CSYMBOL_TYPE_OBJECT:
148 return self._create_object(symbol)
149 elif stype == CSYMBOL_TYPE_MEMBER:
150 return self._create_member(symbol)
151 elif stype == CSYMBOL_TYPE_UNION:
152 return self._create_union(symbol)
154 raise NotImplementedError(
155 'Transformer: unhandled symbol: %r' % (symbol, ))
157 def _create_enum(self, symbol):
159 for child in symbol.base_type.child_list:
160 name = strip_common_prefix(symbol.ident, child.ident).lower()
161 members.append(Member(name,
165 enum_name = self.strip_namespace_object(symbol.ident)
166 enum_name = symbol.ident[-len(enum_name):]
167 enum_name = self._remove_prefix(enum_name)
168 enum = Enum(enum_name, symbol.ident, members)
169 self._names.type_names[symbol.ident] = (None, enum)
172 def _create_object(self, symbol):
173 return Member(symbol.ident, symbol.base_type.name,
176 def _create_function(self, symbol):
177 directives = symbol.directives()
178 parameters = list(self._create_parameters(
179 symbol.base_type, directives))
180 return_ = self._create_return(symbol.base_type.base_type,
181 directives.get('return', []))
182 name = self._remove_prefix(symbol.ident)
183 name = self._strip_namespace_func(name)
184 return Function(name, return_, parameters, symbol.ident)
186 def _create_source_type(self, source_type):
187 if source_type is None:
189 if source_type.type == CTYPE_VOID:
191 elif source_type.type == CTYPE_BASIC_TYPE:
192 value = source_type.name
193 elif source_type.type == CTYPE_TYPEDEF:
194 value = source_type.name
195 elif source_type.type == CTYPE_ARRAY:
196 return self._create_source_type(source_type.base_type)
197 elif source_type.type == CTYPE_POINTER:
198 value = self._create_source_type(source_type.base_type) + '*'
200 print 'TRANSFORMER: Unhandled source type %r' % (
205 def _create_parameters(self, base_type, options=None):
208 for child in base_type.child_list:
209 yield self._create_parameter(
210 child, options.get(child.ident, []))
212 def _create_member(self, symbol):
213 ctype = symbol.base_type.type
214 if (ctype == CTYPE_POINTER and
215 symbol.base_type.base_type.type == CTYPE_FUNCTION):
216 node = self._create_callback(symbol)
218 ftype = self._create_type(symbol.base_type)
219 node = Field(symbol.ident, ftype, symbol.ident)
222 def _create_typedef(self, symbol):
223 ctype = symbol.base_type.type
224 if (ctype == CTYPE_POINTER and
225 symbol.base_type.base_type.type == CTYPE_FUNCTION):
226 node = self._create_callback(symbol)
227 elif ctype == CTYPE_STRUCT:
228 node = self._create_typedef_struct(symbol)
229 elif ctype == CTYPE_UNION:
230 node = self._create_typedef_union(symbol)
231 elif ctype == CTYPE_ENUM:
232 return self._create_enum(symbol)
233 elif ctype in (CTYPE_TYPEDEF,
237 if symbol.base_type.name:
238 name = self.strip_namespace_object(symbol.ident)
239 target = self.strip_namespace_object(symbol.base_type.name)
240 return Alias(name, target, ctype=symbol.ident)
243 raise NotImplementedError(
244 "symbol %r of type %s" % (symbol.ident, ctype_name(ctype)))
247 def _create_type(self, source_type):
248 ctype = self._create_source_type(source_type)
249 if ctype == 'va_list':
251 # FIXME: FILE* should not be skipped, it should be handled
253 elif ctype == 'FILE*':
255 type_name = type_name_from_ctype(ctype)
256 type_name = type_name.replace('*', '')
257 resolved_type_name = self.resolve_type_name(type_name)
258 return Type(resolved_type_name, ctype)
260 def _create_parameter(self, symbol, options):
261 ptype = self._create_type(symbol.base_type)
262 param = Parameter(symbol.ident, ptype)
263 for option in options:
264 if option in ['in-out', 'inout']:
265 param.direction = 'inout'
267 param.direction = 'in'
268 elif option == 'out':
269 param.direction = 'out'
270 elif option == 'callee-owns':
271 param.transfer = True
272 elif option == 'allow-none':
273 param.allow_none = True
275 print 'Unhandled parameter annotation option: %s' % (
279 def _create_return(self, source_type, options=None):
282 rtype = self._create_type(source_type)
283 rtype = self.resolve_param_type(rtype)
284 return_ = Return(rtype)
285 for option in options:
286 if option == 'caller-owns':
287 return_.transfer = True
288 elif option.startswith('seq '):
289 value, element_options = option[3:].split(None, 2)
290 c_element_type = self._parse_type_annotation(value)
291 element_type = c_element_type.replace('*', '')
292 element_type = self.resolve_type_name(element_type,
294 seq = Sequence(rtype.name,
295 type_name_from_ctype(rtype.name),
300 print 'Unhandled parameter annotation option: %s' % (
304 def _create_typedef_struct(self, symbol):
305 name = self._remove_prefix(symbol.ident)
306 name = self.strip_namespace_object(name)
307 struct = Struct(name, symbol.ident)
308 self._typedefs_ns[symbol.ident] = struct
311 def _create_typedef_union(self, symbol):
312 name = self._remove_prefix(symbol.ident)
313 name = self.strip_namespace_object(name)
314 union = Union(name, symbol.ident)
315 self._typedefs_ns[symbol.ident] = union
318 def _create_struct(self, symbol):
319 struct = self._typedefs_ns.get(symbol.ident, None)
321 # This is a bit of a hack; really we should try
322 # to resolve through the typedefs to find the real
324 if symbol.ident.startswith('_'):
325 name = symbol.ident[1:]
328 name = self._remove_prefix(name)
329 name = self.strip_namespace_object(name)
330 name = self.resolve_type_name(name)
331 struct = Struct(name, symbol.ident)
333 for child in symbol.base_type.child_list:
334 field = self._traverse_one(child)
336 struct.fields.append(field)
340 def _create_union(self, symbol):
341 union = self._typedefs_ns.get(symbol.ident, None)
343 # This is a bit of a hack; really we should try
344 # to resolve through the typedefs to find the real
346 if symbol.ident.startswith('_'):
347 name = symbol.ident[1:]
350 name = self._remove_prefix(name)
351 name = self.strip_namespace_object(name)
352 name = self.resolve_type_name(name)
353 union = Union(name, symbol.ident)
355 for child in symbol.base_type.child_list:
356 field = self._traverse_one(child)
358 union.fields.append(field)
362 def _create_callback(self, symbol):
363 parameters = self._create_parameters(symbol.base_type.base_type)
364 retval = self._create_return(symbol.base_type.base_type.base_type)
365 name = self.strip_namespace_object(symbol.ident)
366 return Callback(name, retval, list(parameters), symbol.ident)
368 def _parse_type_annotation(self, annotation):
369 if (annotation[0] == "[" and
370 annotation[-1] == "]"):
371 return Sequence(self._parse_type_annotation(annotation[1:-1]))
374 def _typepair_to_str(self, item):
378 return '%s.%s' % (nsname, item.name)
380 def _resolve_type_name_1(self, type_name, ctype, names):
381 # First look using the built-in names
384 return type_names[ctype]
388 return type_names[type_name]
391 type_name = self.strip_namespace_object(type_name)
392 resolved = names.aliases.get(type_name)
394 return self._typepair_to_str(resolved)
395 resolved = names.names.get(type_name)
397 return self._typepair_to_str(resolved)
399 ctype = ctype.replace('*', '')
400 resolved = names.ctypes.get(ctype)
402 return self._typepair_to_str(resolved)
403 raise KeyError("failed to find %r" % (type_name, ))
405 def resolve_type_name_full(self, type_name, ctype,
408 return self._resolve_type_name_1(type_name, ctype, names)
411 return self._resolve_type_name_1(type_name, ctype, self._names)
415 def resolve_type_name(self, type_name, ctype=None):
417 return self.resolve_type_name_full(type_name, ctype, self._names)
421 def gtypename_to_giname(self, gtname, names):
422 resolved = names.type_names.get(gtname)
424 return self._typepair_to_str(resolved)
425 resolved = self._names.type_names.get(gtname)
427 return self._typepair_to_str(resolved)
428 raise KeyError("Failed to resolve GType name: %r" % (gtname, ))
430 def ctype_of(self, obj):
431 if hasattr(obj, 'ctype'):
433 elif hasattr(obj, 'symbol'):
438 def resolve_param_type_full(self, ptype, names):
439 if isinstance(ptype, Sequence):
440 ptype.element_type = \
441 self.resolve_param_type_full(ptype.element_type, names)
442 elif isinstance(ptype, Node):
443 ptype.name = self.resolve_type_name_full(ptype.name,
444 self.ctype_of(ptype),
446 elif isinstance(ptype, basestring):
447 return self.resolve_type_name_full(ptype, None, names)
449 raise AssertionError("Unhandled param: %r" % (ptype, ))
452 def resolve_param_type(self, ptype):
454 return self.resolve_param_type_full(ptype, self._names)