Add pyflakes.py and run it in make check. Update the source code to fix
[platform/upstream/gobject-introspection.git] / giscanner / glibtransformer.py
1 # -*- Mode: Python -*-
2 # GObject-Introspection - a framework for introspecting GObject libraries
3 # Copyright (C) 2008  Johan Dahlin
4 #
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.
9 #
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.
14 #
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
18 # 02110-1301, USA.
19 #
20
21 import ctypes
22
23 from . import cgobject
24 from .odict import odict
25 from .ast import (Callback, Enum, Function, Member, Namespace, Parameter,
26                   Property, Return, Struct, Type, Alias, type_name_from_ctype)
27 from .glibast import (GLibBoxed, GLibEnum, GLibEnumMember, GLibFlags,
28                       GLibInterface, GLibObject, GLibSignal)
29 from .utils import extract_libtool, to_underscores
30
31
32 class GLibTransformer(object):
33
34     def __init__(self, transformer):
35         self._transformer = transformer
36         self._namespace_name = None
37         self._aliases = []
38         self._output_ns = odict()
39         self._libraries = []
40         self._internal_types = {}
41
42     # Public API
43
44     def add_library(self, libname):
45         if libname.endswith('.la'):
46             libname = extract_libtool(libname)
47         self._libraries.append(ctypes.cdll.LoadLibrary(libname))
48
49     def parse(self):
50         namespace = self._transformer.parse()
51         self._namespace_name = namespace.name
52
53         # First pass, parsing
54         for node in namespace.nodes:
55             self._parse_node(node)
56
57         # Introspection is done from within parsing
58
59         # Second pass, resolving types
60         for node in self._output_ns.values():
61             # associate GtkButtonClass with GtkButton
62             if isinstance(node, Struct):
63                 self._pair_class_struct(node)
64             self._resolve_node(node)
65
66         # Create a new namespace with what we found
67         namespace = Namespace(namespace.name)
68         namespace.nodes = self._aliases + self._output_ns.values()
69         return namespace
70
71     # Private
72
73     def _add_attribute(self, node, replace=False):
74         node_name = node.name
75         if node_name in self._output_ns and not replace:
76             return
77         self._output_ns[node_name] = node
78
79     def _remove_attribute(self, name):
80         del self._output_ns[name]
81
82     def _get_attribute(self, name):
83         return self._output_ns.get(name)
84
85     def _register_internal_type(self, type_name, node):
86         self._internal_types[type_name] = node
87
88     # Helper functions
89
90     def _create_type(self, type_id):
91         ctype = cgobject.type_name(type_id)
92         type_name = type_name_from_ctype(ctype)
93         return Type(type_name, ctype)
94
95     def _create_gobject(self, node):
96         type_name = 'G' + node.name
97         if type_name == 'GObject':
98             parent_type_name = None
99             symbol = 'intern'
100         else:
101             type_id = cgobject.type_from_name(type_name)
102             parent_type_name = cgobject.type_name(
103                 cgobject.type_parent(type_id))
104             symbol = to_underscores(type_name).lower() + '_get_type'
105         node = GLibObject(node.name, parent_type_name, type_name, symbol)
106         type_id = cgobject.TYPE_OBJECT
107         self._introspect_properties(node, type_id)
108         self._introspect_signals(node, type_id)
109         self._add_attribute(node)
110         self._register_internal_type(type_name, node)
111
112     # Parser
113
114     def _parse_node(self, node):
115         if isinstance(node, Enum):
116             self._parse_enum(node)
117         elif isinstance(node, Function):
118             self._parse_function(node)
119         elif isinstance(node, Struct):
120             self._parse_struct(node)
121         elif isinstance(node, Callback):
122             self._parse_callback(node)
123         elif isinstance(node, Alias):
124             self._parse_alias(node)
125         elif isinstance(node, Member):
126             # FIXME: atk_misc_instance singletons
127             pass
128         else:
129             print 'GOBJECT BUILDER: Unhandled node:', node
130
131     def _parse_alias(self, alias):
132         self._aliases.append(alias)
133
134     def _parse_enum(self, enum):
135         self._add_attribute(enum)
136
137     def _parse_function(self, func):
138         if self._parse_get_type_function(func):
139             return
140         elif self._parse_constructor(func):
141             return
142         elif self._parse_method(func):
143             return
144
145         self._add_attribute(func)
146
147     def _parse_get_type_function(self, func):
148         if not self._libraries:
149             return False
150         # GType *_get_type(void)
151         symbol = func.symbol
152         if not symbol.endswith('_get_type'):
153             return False
154         if func.retval.type.name != 'GObject.GType':
155             return False
156         if func.parameters:
157             return False
158
159         for library in self._libraries:
160             try:
161                 func = getattr(library, symbol)
162                 break
163             except AttributeError:
164                 continue
165         else:
166             print 'Warning: could not find symbol: %s' % symbol
167             return False
168
169         func.restype = cgobject.GType
170         func.argtypes = []
171         type_id = func()
172         self._introspect_type(type_id, symbol)
173         return True
174
175     def _parse_method(self, func):
176         if not func.parameters:
177             return False
178
179         # FIXME: This is hackish, we should preserve the pointer structures
180         #        here, so we can find pointers to objects and not just
181         #        pointers to anything
182         first_arg = func.parameters[0].type.name
183         if first_arg.count('*') != 1:
184             return False
185
186         object_name = first_arg.replace('*', '')
187         return self._parse_method_common(func, object_name, is_method=True)
188
189     def _parse_constructor(self, func):
190         # FIXME: This is hackish, we should preserve the pointer structures
191         #        here, so we can find pointers to objects and not just
192         #        pointers to anything
193         rtype = func.retval.type
194         if rtype.name.count('*') != 1:
195             return False
196
197         object_name = rtype.name.replace('*', '')
198         return self._parse_method_common(func, object_name, is_method=False)
199
200     def _parse_method_common(self, func, object_name, is_method):
201         orig_name = object_name
202         if object_name.lower().startswith(self._namespace_name.lower()):
203             object_name = object_name[len(self._namespace_name):]
204         class_ = self._get_attribute(object_name)
205         if class_ is None or not isinstance(class_, (GLibObject, GLibBoxed)):
206             return False
207
208         # GtkButton -> gtk_button_, so we can figure out the method name
209         prefix = to_underscores(orig_name).lower() + '_'
210         if not func.symbol.startswith(prefix):
211             return False
212
213         # Strip namespace and object prefix: gtk_window_new -> new
214         func.name = func.symbol[len(prefix):]
215         if is_method:
216             class_.methods.append(func)
217         else:
218             class_.constructors.append(func)
219         return True
220
221     def _parse_struct(self, struct):
222         # This is a hack, but GObject is a rather fundamental piece so.
223         if (self._namespace_name == 'GObject' and
224             struct.name in ["Object", 'InitiallyUnowned']):
225             self._create_gobject(struct)
226             return
227         node = self._output_ns.get(struct.name)
228         if node is None:
229             self._add_attribute(struct, replace=True)
230             return
231         node.fields = struct.fields[:]
232
233     def _parse_callback(self, callback):
234         self._add_attribute(callback)
235
236     def _pair_class_struct(self, class_node):
237         name = class_node.name
238         if (name.endswith('Class') or
239             name.endswith('Iface')):
240             name = name[:-5]
241         elif name.endswith('Interface'):
242             name = name[:-9]
243         else:
244             return
245
246         node = self._output_ns.get(self._resolve_type_name(name))
247         del self._output_ns[class_node.name]
248         if node is None:
249             return
250         for field in class_node.fields[1:]:
251             node.fields.append(field)
252
253     # Introspection
254
255     def _introspect_type(self, type_id, symbol):
256         fundamental_type_id = cgobject.type_fundamental(type_id)
257         if (fundamental_type_id == cgobject.TYPE_ENUM or
258             fundamental_type_id == cgobject.TYPE_FLAGS):
259             self._introspect_enum(fundamental_type_id, type_id, symbol)
260         elif fundamental_type_id == cgobject.TYPE_OBJECT:
261             self._introspect_object(type_id, symbol)
262         elif fundamental_type_id == cgobject.TYPE_INTERFACE:
263             self._introspect_interface(type_id, symbol)
264         elif fundamental_type_id == cgobject.TYPE_BOXED:
265             self._introspect_boxed(type_id, symbol)
266         else:
267             print 'unhandled GType: %s' % (cgobject.type_name(type_id), )
268
269     def _introspect_enum(self, ftype_id, type_id, symbol):
270         type_class = cgobject.type_class_ref(type_id)
271         if type_class is None:
272             return
273
274         members = []
275         for enum_value in type_class.get_values():
276             members.append(GLibEnumMember(enum_value.value_nick,
277                                           enum_value.value,
278                                           enum_value.value_name,
279                                           enum_value.value_nick))
280
281         klass = (GLibFlags if ftype_id == cgobject.TYPE_FLAGS else GLibEnum)
282         type_name = cgobject.type_name(type_id)
283         enum_name = self._transformer.strip_namespace_object(type_name)
284         node = klass(enum_name, type_name, members, symbol)
285         self._add_attribute(node, replace=True)
286         self._register_internal_type(type_name, node)
287
288     def _introspect_object(self, type_id, symbol):
289         type_name = cgobject.type_name(type_id)
290         parent_type_name = cgobject.type_name(cgobject.type_parent(type_id))
291         node = GLibObject(
292             self._transformer.strip_namespace_object(type_name),
293             self._resolve_type_name(parent_type_name),
294             type_name, symbol)
295         self._introspect_properties(node, type_id)
296         self._introspect_signals(node, type_id)
297         self._add_attribute(node)
298         try:
299             self._remove_attribute(type_name)
300         except KeyError:
301             print 'Warning: could not remove %s' % type_name
302             pass
303         self._register_internal_type(type_name, node)
304
305     def _introspect_interface(self, type_id, symbol):
306         type_name = cgobject.type_name(type_id)
307         parent_type_name = cgobject.type_name(cgobject.type_parent(type_id))
308         node = GLibInterface(
309             self._transformer.strip_namespace_object(type_name),
310             self._resolve_type_name(parent_type_name),
311             type_name, symbol)
312         self._introspect_properties(node, type_id)
313         self._introspect_signals(node, type_id)
314         self._add_attribute(node)
315         self._register_internal_type(type_name, node)
316
317     def _introspect_boxed(self, type_id, symbol):
318         type_name = cgobject.type_name(type_id)
319         node = GLibBoxed(self._transformer.strip_namespace_object(type_name),
320                          type_name, symbol)
321         self._add_attribute(node)
322         # GdkEvent raises KeyError, FooBoxed ends up duplicated if we don't
323         try:
324             self._remove_attribute(type_name)
325         except KeyError:
326             pass
327         self._register_internal_type(type_name, node)
328
329     def _introspect_properties(self, node, type_id):
330         fundamental_type_id = cgobject.type_fundamental(type_id)
331         if fundamental_type_id == cgobject.TYPE_OBJECT:
332             pspecs = cgobject.object_class_list_properties(type_id)
333         elif fundamental_type_id == cgobject.TYPE_INTERFACE:
334             pspecs = cgobject.object_interface_list_properties(type_id)
335         else:
336             raise AssertionError
337
338         for pspec in pspecs:
339             if pspec.owner_type != type_id:
340                 continue
341             ctype = cgobject.type_name(pspec.value_type)
342             node.properties.append(Property(
343                 pspec.name,
344                 type_name_from_ctype(ctype),
345                 ctype,
346                 ))
347
348     def _introspect_signals(self, node, type_id):
349         for signal_info in cgobject.signal_list(type_id):
350             rtype = self._create_type(signal_info.return_type)
351             return_ = Return(rtype)
352             signal = GLibSignal(signal_info.signal_name, return_)
353             for i, parameter in enumerate(signal_info.get_params()):
354                 if i == 0:
355                     name = 'object'
356                 else:
357                     name = 'p%s' % (i-1, )
358                 ptype = self._create_type(parameter)
359                 param = Parameter(name, ptype)
360                 signal.parameters.append(param)
361             node.signals.append(signal)
362
363     # Resolver
364
365     def _resolve_type_name(self, type_name):
366         type_name = type_name.replace('*', '')
367         possible_name = self._transformer.resolve_type_name(type_name)
368         if possible_name != type_name:
369             return possible_name
370         possible_node = self._internal_types.get(type_name)
371         if possible_node:
372             return possible_node.name
373         return type_name
374
375     def _resolve_param_type(self, ptype):
376         ptype.name = ptype.name.replace('*', '')
377         type_name = ptype.name
378         type_name = self._transformer.resolve_possible_typedef(type_name)
379         possible_node = self._internal_types.get(type_name)
380         if possible_node:
381             ptype.name = possible_node.name
382         else:
383             ptype = self._transformer.resolve_param_type(ptype)
384         return ptype
385
386     def _resolve_node(self, node):
387         if isinstance(node, (Callback, Function)):
388             self._resolve_function(node)
389         elif isinstance(node, GLibObject):
390             self._resolve_glib_object(node)
391         elif isinstance(node, GLibInterface):
392             self._resolve_glib_interface(node)
393         elif isinstance(node, GLibBoxed):
394             self._resolve_glib_boxed(node)
395         elif isinstance(node, Struct):
396             self._resolve_struct(node)
397
398     def _resolve_struct(self, node):
399         for field in node.fields:
400             self._resolve_field(field)
401
402     def _resolve_glib_interface(self, node):
403         self._resolve_methods(node.methods)
404         self._resolve_properties(node.properties)
405         self._resolve_signals(node.signals)
406
407     def _resolve_glib_object(self, node):
408         self._resolve_constructors(node.constructors)
409         self._resolve_methods(node.methods)
410         self._resolve_properties(node.properties)
411         self._resolve_signals(node.signals)
412
413     def _resolve_glib_boxed(self, node):
414         self._resolve_constructors(node.constructors)
415         self._resolve_methods(node.methods)
416
417     def _resolve_constructors(self, constructors):
418         for ctor in constructors:
419             self._resolve_function(ctor)
420
421     def _resolve_methods(self, methods):
422         for method in methods:
423             self._resolve_function(method)
424
425     def _resolve_signals(self, signals):
426         for signal in signals:
427             self._resolve_function(signal)
428
429     def _resolve_properties(self, properties):
430         for prop in properties:
431             self._resolve_property(prop)
432
433     def _resolve_property(self, prop):
434         prop.type = self._resolve_param_type(prop.type)
435
436     def _resolve_function(self, func):
437         self._resolve_parameters(func.parameters)
438         func.retval.type = self._resolve_param_type(func.retval.type)
439
440     def _resolve_parameters(self, parameters):
441         for parameter in parameters:
442             parameter.type = self._resolve_param_type(parameter.type)
443
444     def _resolve_field(self, field):
445         if isinstance(field, Callback):
446             self._resolve_function(field)
447             return
448         field.type = self._resolve_param_type(field.type)