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,
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 .utils import strip_common_prefix
36 class SkipError(Exception):
40 class Transformer(object):
42 def __init__(self, generator, namespace_name):
43 self.generator = generator
44 self._namespace = Namespace(namespace_name)
46 self._alias_names = {} # Maps from GIName -> GIName
47 self._type_names = {} # Maps from GTName -> (namespace, node)
48 self._ctype_names = {} # Maps from CType -> (namespace, node)
49 self._typedefs_ns = {}
50 self._strip_prefix = ''
52 def get_type_names(self):
53 return self._type_names
55 def get_alias_names(self):
56 return self._alias_names
58 def set_strip_prefix(self, strip_prefix):
59 self._strip_prefix = strip_prefix
63 for symbol in self.generator.get_symbols():
64 node = self._traverse_one(symbol)
66 return self._namespace
68 def register_include(self, filename):
69 if filename.endswith('.gir'):
70 from .girparser import GIRParser
71 parser = GIRParser(filename)
72 elif filename.endswith('.gidl'):
73 from .gidlparser import GIDLParser
74 parser = GIDLParser(filename)
76 raise NotImplementedError(filename)
77 nsname = parser.get_namespace_name()
78 for node in parser.get_nodes():
79 if isinstance(node, Alias):
80 self._alias_names[node.ctype] = (nsname, node)
81 elif isinstance(node, (GLibBoxed, Interface, Class)):
82 self._type_names[node.type_name] = (nsname, node)
83 elif hasattr(node, 'ctype'):
84 self._ctype_names[node.ctype] = (nsname, node)
85 elif hasattr(node, 'symbol'):
86 self._ctype_names[node.symbol] = (nsname, node)
88 self._type_names[node.name] = (nsname, node)
90 def strip_namespace_object(self, name):
91 prefix = self._namespace.name.lower()
92 if len(name) > len(prefix) and name.lower().startswith(prefix):
93 return name[len(prefix):]
94 return self._remove_prefix(name)
98 def _add_node(self, node):
101 if node.name.startswith('_'):
103 self._namespace.nodes.append(node)
104 self._output_ns[node.name] = node
106 def _strip_namespace_func(self, name):
107 prefix = self._namespace.name.lower() + '_'
108 if name.lower().startswith(prefix):
109 name = name[len(prefix):]
110 return self._remove_prefix(name)
112 def _remove_prefix(self, name):
113 # when --strip-prefix=g:
114 # GHashTable -> HashTable
115 # g_hash_table_new -> hash_table_new
116 if name.lower().startswith(self._strip_prefix.lower()):
117 name = name[len(self._strip_prefix):]
119 while name.startswith('_'):
123 def _traverse_one(self, symbol, stype=None):
124 assert isinstance(symbol, SourceSymbol), symbol
128 if stype == CSYMBOL_TYPE_FUNCTION:
130 return self._create_function(symbol)
133 elif stype == CSYMBOL_TYPE_TYPEDEF:
134 return self._create_typedef(symbol)
135 elif stype == CSYMBOL_TYPE_STRUCT:
136 return self._create_struct(symbol)
137 elif stype == CSYMBOL_TYPE_ENUM:
138 return self._create_enum(symbol)
139 elif stype == CSYMBOL_TYPE_OBJECT:
140 return self._create_object(symbol)
141 elif stype == CSYMBOL_TYPE_MEMBER:
142 return self._create_member(symbol)
143 elif stype == CSYMBOL_TYPE_UNION:
144 # Unions are not supported
147 raise NotImplementedError(
148 'Transformer: unhandled symbol: %r' % (symbol, ))
150 def _create_enum(self, symbol):
152 for child in symbol.base_type.child_list:
153 name = strip_common_prefix(symbol.ident, child.ident).lower()
154 members.append(Member(name,
158 enum_name = self.strip_namespace_object(symbol.ident)
159 enum_name = symbol.ident[-len(enum_name):]
160 enum_name = self._remove_prefix(enum_name)
161 enum = Enum(enum_name, symbol.ident, members)
162 self._type_names[symbol.ident] = (None, enum)
165 def _create_object(self, symbol):
166 return Member(symbol.ident, symbol.base_type.name,
169 def _create_function(self, symbol):
170 directives = symbol.directives()
171 parameters = list(self._create_parameters(
172 symbol.base_type, directives))
173 return_ = self._create_return(symbol.base_type.base_type,
174 directives.get('return', []))
175 name = self._remove_prefix(symbol.ident)
176 name = self._strip_namespace_func(name)
177 return Function(name, return_, parameters, symbol.ident)
179 def _create_source_type(self, source_type):
180 if source_type is None:
182 if source_type.type == CTYPE_VOID:
184 elif source_type.type == CTYPE_BASIC_TYPE:
185 value = source_type.name
186 elif source_type.type == CTYPE_TYPEDEF:
187 value = source_type.name
188 elif source_type.type == CTYPE_ARRAY:
189 return self._create_source_type(source_type.base_type)
190 elif source_type.type == CTYPE_POINTER:
191 value = self._create_source_type(source_type.base_type) + '*'
193 print 'TRANSFORMER: Unhandled source type %r' % (
198 def _create_parameters(self, base_type, options=None):
201 for child in base_type.child_list:
202 yield self._create_parameter(
203 child, options.get(child.ident, []))
205 def _create_member(self, symbol):
206 ctype = symbol.base_type.type
207 if (ctype == CTYPE_POINTER and
208 symbol.base_type.base_type.type == CTYPE_FUNCTION):
209 node = self._create_callback(symbol)
211 ftype = self._create_type(symbol.base_type)
212 node = Field(symbol.ident, ftype, symbol.ident)
215 def _create_typedef(self, symbol):
216 ctype = symbol.base_type.type
217 if (ctype == CTYPE_POINTER and
218 symbol.base_type.base_type.type == CTYPE_FUNCTION):
219 node = self._create_callback(symbol)
220 elif ctype == CTYPE_STRUCT:
221 node = self._create_typedef_struct(symbol)
222 elif ctype == CTYPE_ENUM:
223 return self._create_enum(symbol)
224 elif ctype in (CTYPE_TYPEDEF,
229 if symbol.base_type.name:
230 name = self.strip_namespace_object(symbol.ident)
231 target = self.strip_namespace_object(symbol.base_type.name)
232 return Alias(name, target, ctype=symbol.ident)
235 raise NotImplementedError(
236 "symbol %r of type %s" % (symbol.ident, ctype_name(ctype)))
239 def _create_type(self, source_type):
240 ctype = self._create_source_type(source_type)
241 if ctype == 'va_list':
243 # FIXME: FILE* should not be skipped, it should be handled
245 elif ctype == 'FILE*':
247 type_name = type_name_from_ctype(ctype)
248 resolved_type_name = self.resolve_type_name(type_name)
249 return Type(resolved_type_name, ctype)
251 def _create_parameter(self, symbol, options):
252 ptype = self._create_type(symbol.base_type)
253 param = Parameter(symbol.ident, ptype)
254 for option in options:
255 if option in ['in-out', 'inout']:
256 param.direction = 'inout'
258 param.direction = 'in'
259 elif option == 'out':
260 param.direction = 'out'
261 elif option == 'callee-owns':
262 param.transfer = True
263 elif option == 'allow-none':
264 param.allow_none = True
266 print 'Unhandled parameter annotation option: %s' % (
270 def _create_return(self, source_type, options=None):
273 rtype = self._create_type(source_type)
274 rtype = self.resolve_param_type(rtype)
275 return_ = Return(rtype)
276 for option in options:
277 if option == 'caller-owns':
278 return_.transfer = True
279 elif option.startswith('seq '):
280 value, element_options = option[3:].split(None, 2)
281 element_type = self._parse_type_annotation(value)
282 seq = Sequence(rtype.name,
283 type_name_from_ctype(rtype.name),
288 print 'Unhandled parameter annotation option: %s' % (
292 def _create_typedef_struct(self, symbol):
293 name = self._remove_prefix(symbol.ident)
294 name = self.strip_namespace_object(name)
295 struct = Struct(name, symbol.ident)
296 self._typedefs_ns[symbol.ident] = struct
299 def _create_struct(self, symbol):
300 struct = self._typedefs_ns.get(symbol.ident, None)
302 # This is a bit of a hack; really we should try
303 # to resolve through the typedefs to find the real
305 if symbol.ident.startswith('_'):
306 name = symbol.ident[1:]
309 name = self._remove_prefix(name)
310 name = self.strip_namespace_object(name)
311 name = self.resolve_type_name(name)
312 struct = Struct(name, symbol.ident)
314 for child in symbol.base_type.child_list:
315 field = self._traverse_one(child)
317 struct.fields.append(field)
321 def _create_callback(self, symbol):
322 parameters = self._create_parameters(symbol.base_type.base_type)
323 retval = self._create_return(symbol.base_type.base_type.base_type)
324 name = self.strip_namespace_object(symbol.ident)
325 return Callback(name, retval, list(parameters), symbol.ident)
327 def _parse_type_annotation(self, annotation):
328 if (annotation[0] == "[" and
329 annotation[-1] == "]"):
330 return Sequence(self._parse_type_annotation(annotation[1:-1]))
333 def _typepair_to_str(self, item):
337 return '%s.%s' % (nsname, item.name)
339 def resolve_type_name(self, type_name, ctype=None):
340 type_name = type_name.replace('*', '')
341 type_name = self.strip_namespace_object(type_name)
342 resolved = self._alias_names.get(type_name)
344 return self._typepair_to_str(resolved)
345 resolved = self._type_names.get(type_name)
347 return self._typepair_to_str(resolved)
349 ctype = ctype.replace('*', '')
350 resolved = self._ctype_names.get(ctype)
352 return self._typepair_to_str(resolved)
355 def ctype_of(self, obj):
356 if hasattr(obj, 'ctype'):
358 elif hasattr(obj, 'symbol'):
363 def resolve_param_type(self, ptype):
364 ptype.name = self.resolve_type_name(ptype.name,
365 self.ctype_of(ptype))