Record typedefs as <alias> elements. Also attempt to look up types in
[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 resolve_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._output_ns = odict()
41         self._library = None
42         self._type_names = {}
43
44     # Public API
45
46     def load_library(self, libname):
47         if libname.endswith('.la'):
48             libname = resolve_libtool(libname)
49         self._library = ctypes.cdll.LoadLibrary(libname)
50
51     def parse(self):
52         namespace = self._transformer.parse()
53         self._namespace_name = namespace.name
54         self._type_names = self._transformer.get_type_names()
55
56         for node in namespace.nodes:
57             self._parse_node(node)
58
59         # Second round
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         namespace = Namespace(namespace.name)
67         namespace.nodes = self._output_ns.values()
68         return namespace
69
70     # Private
71
72     def _add_attribute(self, node, replace=False):
73         node_name = node.name
74         if node_name in self._output_ns and not replace:
75             return
76         self._output_ns[node_name] = node
77
78     def _remove_attribute(self, name):
79         del self._output_ns[name]
80
81     def _get_attribute(self, name):
82         return self._output_ns.get(name)
83
84     def _register_internal_type(self, type_name, node):
85         self._type_names[type_name] = (None, node)
86
87     def _resolve_type_name(self, type_name):
88         item = self._type_names.get(type_name)
89         if item is not None:
90             nsname, item = item
91             if nsname is None:
92                 return item.name
93             return '%s.%s' % (nsname, item.name)
94         return type_name
95
96     def _resolve_param_type(self, ptype):
97         type_name = ptype.name.replace('*', '')
98         type_name = self._transformer.resolve_possible_typedef(type_name)
99         ptype.name = self._resolve_type_name(type_name)
100         return ptype
101
102     def _parse_node(self, node):
103         if isinstance(node, Enum):
104             self._parse_enum(node)
105         elif isinstance(node, Function):
106             self._parse_function(node)
107         elif isinstance(node, Struct):
108             self._parse_struct(node)
109         elif isinstance(node, Callback):
110             self._parse_callback(node)
111         elif isinstance(node, Alias):
112             self._parse_alias(node)
113         elif isinstance(node, Member):
114             # FIXME: atk_misc_instance singletons
115             pass
116         else:
117             print 'GOBJECT BUILDER: Unhandled node:', node
118
119     def _resolve_node(self, node):
120
121         def isany(*klasses):
122             for klass in klasses:
123                 if isinstance(node, klass):
124                     return True
125             return False
126
127         if isany(Callback, Function):
128             self._resolve_function(node)
129         if isany(GLibObject, GLibBoxed, GLibInterface):
130             for meth in node.methods:
131                 self._resolve_function(meth)
132         if isany(GLibObject, GLibBoxed):
133             for ctor in node.constructors:
134                 self._resolve_function(ctor)
135         if isany(GLibObject, GLibInterface):
136             for prop in node.properties:
137                 self._resolve_property(prop)
138             for sig in node.signals:
139                 self._resolve_function(sig)
140         if isany(Struct, ):
141             for field in node.fields:
142                 if isinstance(field, Field):
143                     self._resolve_field(field)
144                 elif isinstance(field, Callback):
145                     self._resolve_function(field)
146
147     def _parse_alias(self, alias):
148         self._add_attribute(alias)
149
150     def _parse_enum(self, enum):
151         self._add_attribute(enum)
152
153     def _parse_function(self, func):
154         if self._parse_get_type_function(func):
155             return
156         elif self._parse_constructor(func):
157             return
158         elif self._parse_method(func):
159             return
160
161         self._add_attribute(func)
162
163     def _resolve_property(self, prop):
164         prop.type = self._resolve_param_type(prop.type)
165
166     def _resolve_function(self, func):
167         self._resolve_parameters(func.parameters)
168         func.retval.type = self._resolve_param_type(func.retval.type)
169
170     def _resolve_parameters(self, parameters):
171         for parameter in parameters:
172             parameter.type = self._resolve_param_type(parameter.type)
173
174     def _resolve_field(self, field):
175         field.type = self._resolve_param_type(field.type)
176
177     def _parse_get_type_function(self, func):
178         if self._library is None:
179             return False
180         # GType *_get_type(void)
181         symbol = func.symbol
182         if not symbol.endswith('_get_type'):
183             return False
184         if func.retval.type.name != 'GType':
185             return False
186         if func.parameters:
187             return False
188
189         try:
190             func = getattr(self._library, symbol)
191         except AttributeError:
192             print 'Warning: could not find symbol: %s' % symbol
193             return False
194
195         func.restype = cgobject.GType
196         func.argtypes = []
197         type_id = func()
198         self._introspect_type(type_id, symbol)
199         return True
200
201     def _parse_method(self, func):
202         if not func.parameters:
203             return False
204
205         # FIXME: This is hackish, we should preserve the pointer structures
206         #        here, so we can find pointers to objects and not just
207         #        pointers to anything
208         first_arg = func.parameters[0].type.name
209         if first_arg.count('*') != 1:
210             return False
211
212         object_name = first_arg.replace('*', '')
213         return self._parse_method_common(func, object_name, is_method=True)
214
215     def _parse_constructor(self, func):
216         # FIXME: This is hackish, we should preserve the pointer structures
217         #        here, so we can find pointers to objects and not just
218         #        pointers to anything
219         rtype = func.retval.type
220         if rtype.name.count('*') != 1:
221             return False
222
223         object_name = rtype.name.replace('*', '')
224         return self._parse_method_common(func, object_name, is_method=False)
225
226     def _parse_method_common(self, func, object_name, is_method):
227         orig_name = object_name
228         if object_name.lower().startswith(self._namespace_name.lower()):
229             object_name = object_name[len(self._namespace_name):]
230         class_ = self._get_attribute(object_name)
231         if class_ is None or not isinstance(class_, (GLibObject, GLibBoxed)):
232             return False
233
234         # GtkButton -> gtk_button_, so we can figure out the method name
235         prefix = to_underscores(orig_name).lower() + '_'
236         if not func.symbol.startswith(prefix):
237             return False
238
239         # Strip namespace and object prefix: gtk_window_new -> new
240         func.name = func.symbol[len(prefix):]
241         if is_method:
242             class_.methods.append(func)
243         else:
244             class_.constructors.append(func)
245         return True
246
247     def _parse_struct(self, struct):
248         node = self._output_ns.get(struct.name)
249         if node is None:
250             self._add_attribute(struct, replace=True)
251             return
252         node.fields = struct.fields[:]
253
254     def _parse_callback(self, callback):
255         self._add_attribute(callback)
256
257     def _pair_class_struct(self, class_node):
258         name = class_node.name
259         if (name.endswith('Class') or
260             name.endswith('Iface')):
261             name = name[:-5]
262         elif name.endswith('Interface'):
263             name = name[:-9]
264         else:
265             return
266
267         node = self._output_ns.get(self._resolve_type_name(name))
268         del self._output_ns[class_node.name]
269         if node is None:
270             return
271         for field in class_node.fields[1:]:
272             node.fields.append(field)
273
274     def _create_type(self, type_id):
275         ctype = cgobject.type_name(type_id)
276         type_name = type_name_from_ctype(ctype)
277         return Type(type_name, ctype)
278
279     def _introspect_type(self, type_id, symbol):
280         fundamental_type_id = cgobject.type_fundamental(type_id)
281         if (fundamental_type_id == cgobject.TYPE_ENUM or
282             fundamental_type_id == cgobject.TYPE_FLAGS):
283             self._introspect_enum(fundamental_type_id, type_id, symbol)
284         elif fundamental_type_id == cgobject.TYPE_OBJECT:
285             self._introspect_object(type_id, symbol)
286         elif fundamental_type_id == cgobject.TYPE_INTERFACE:
287             self._introspect_interface(type_id, symbol)
288         elif fundamental_type_id == cgobject.TYPE_BOXED:
289             self._introspect_boxed(type_id, symbol)
290         else:
291             print 'unhandled GType: %s' % (cgobject.type_name(type_id), )
292
293     def _introspect_enum(self, ftype_id, type_id, symbol):
294         type_class = cgobject.type_class_ref(type_id)
295         if type_class is None:
296             return
297
298         members = []
299         for enum_value in type_class.get_values():
300             members.append(GLibEnumMember(enum_value.value_nick,
301                                           enum_value.value,
302                                           enum_value.value_name,
303                                           enum_value.value_nick))
304
305         klass = (GLibFlags if ftype_id == cgobject.TYPE_FLAGS else GLibEnum)
306         type_name = cgobject.type_name(type_id)
307         enum_name = self._transformer.strip_namespace_object(type_name)
308         node = klass(enum_name, type_name, members, symbol)
309         self._add_attribute(node, replace=True)
310         self._register_internal_type(type_name, node)
311
312     def _introspect_object(self, type_id, symbol):
313         type_name = cgobject.type_name(type_id)
314         parent_type_name = cgobject.type_name(cgobject.type_parent(type_id))
315         node = GLibObject(self._transformer.strip_namespace_object(type_name),
316                           self._resolve_type_name(parent_type_name),
317                           type_name, symbol)
318         self._introspect_properties(node, type_id)
319         self._introspect_signals(node, type_id)
320         self._add_attribute(node)
321         try:
322             self._remove_attribute(type_name)
323         except KeyError:
324             print 'Warning: could not remove %s' % type_name
325             pass
326         self._register_internal_type(type_name, node)
327
328     def _introspect_interface(self, type_id, symbol):
329         type_name = cgobject.type_name(type_id)
330         node = GLibInterface(
331             self._transformer.strip_namespace_object(type_name),
332             type_name, symbol)
333         self._introspect_properties(node, type_id)
334         self._introspect_signals(node, type_id)
335         self._add_attribute(node)
336         self._register_internal_type(type_name, node)
337
338     def _introspect_boxed(self, type_id, symbol):
339         type_name = cgobject.type_name(type_id)
340         node = GLibBoxed(self._transformer.strip_namespace_object(type_name),
341                          type_name, symbol)
342         self._add_attribute(node)
343         # GdkEvent raises KeyError, FooBoxed ends up duplicated if we don't
344         try:
345             self._remove_attribute(type_name)
346         except KeyError:
347             pass
348         self._register_internal_type(type_name, node)
349
350     def _introspect_properties(self, node, type_id):
351         fundamental_type_id = cgobject.type_fundamental(type_id)
352         if fundamental_type_id == cgobject.TYPE_OBJECT:
353             pspecs = cgobject.object_class_list_properties(type_id)
354         elif fundamental_type_id == cgobject.TYPE_INTERFACE:
355             pspecs = cgobject.object_interface_list_properties(type_id)
356         else:
357             raise AssertionError
358
359         for pspec in pspecs:
360             if pspec.owner_type != type_id:
361                 continue
362             ctype = cgobject.type_name(pspec.value_type)
363             node.properties.append(Property(
364                 pspec.name,
365                 type_name_from_ctype(ctype),
366                 ctype,
367                 ))
368
369     def _introspect_signals(self, node, type_id):
370         for signal_info in cgobject.signal_list(type_id):
371             rtype = self._create_type(signal_info.return_type)
372             return_ = Return(rtype)
373             signal = GLibSignal(signal_info.signal_name, return_)
374             for i, parameter in enumerate(signal_info.get_params()):
375                 if i == 0:
376                     name = 'object'
377                 else:
378                     name = 'p%s' % (i-1, )
379                 ptype = self._create_type(parameter)
380                 param = Parameter(name, ptype)
381                 signal.parameters.append(param)
382             node.signals.append(signal)