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