Handle missing parameters better, allow - and + in .la dlname filenames.
[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 re
23 import os
24
25 from . import cgobject
26 from .odict import odict
27 from .ast import (Callback, Enum, Function, Parameter, Property, Return,
28                   Sequence, Struct, Type)
29 from .glibast import (GLibBoxed, GLibEnum, GLibEnumMember, GLibFlags,
30                       GLibInterface, GLibObject, GLibSignal)
31
32
33 # Copied from h2defs.py
34 _upperstr_pat1 = re.compile(r'([^A-Z])([A-Z])')
35 _upperstr_pat2 = re.compile(r'([A-Z][A-Z])([A-Z][0-9a-z])')
36 _upperstr_pat3 = re.compile(r'^([A-Z])([A-Z])')
37
38 def to_underscores(name):
39     """Converts a typename to the equivalent underscores name.
40     This is used to form the type conversion macros and enum/flag
41     name variables"""
42     name = _upperstr_pat1.sub(r'\1_\2', name)
43     name = _upperstr_pat2.sub(r'\1_\2', name)
44     name = _upperstr_pat3.sub(r'\1_\2', name, count=1)
45     return name
46
47 _libtool_pat = re.compile("dlname='([A-z0-9\.\-\+]+)'\n")
48
49 def resolve_libtool(libname):
50     data = open(libname).read()
51     filename = _libtool_pat.search(data).groups()[0]
52     libname = os.path.join(os.path.dirname(libname),
53                            '.libs', filename)
54     return libname
55
56
57 class GLibTransformer(object):
58     def __init__(self, namespace_name):
59         self._namespace_name = namespace_name
60         self._output_ns = odict()
61         self._library = None
62         self._type_names = {}
63
64     # Public API
65
66     def get_nodes(self):
67         return self._output_ns.values()
68
69     def load_library(self, libname):
70         if libname.endswith('.la'):
71             libname = resolve_libtool(libname)
72         self._library = ctypes.cdll.LoadLibrary(libname)
73
74     def parse(self, nodes):
75         for node in nodes:
76             self._parse_node(node)
77
78         # Second round
79         for node in self._output_ns.values():
80             # associate GtkButtonClass with GtkButton
81             if isinstance(node, Struct):
82                 self._pair_class_struct(node)
83
84     def register_include(self, filename):
85         if filename.endswith('.gir'):
86             from .girparser import GIRParser
87             parser = GIRParser(filename)
88         elif filename.endswith('.gidl'):
89             from .gidlparser import GIDLParser
90             parser = GIDLParser(filename)
91         else:
92             raise NotImplementedError(filename)
93         nsname = parser.get_namespace_name()
94         for node in parser.get_nodes():
95             self._type_names[node.type_name] = (nsname, node)
96
97     # Private
98
99     def _add_attribute(self, node, replace=False):
100         node_name = node.name
101         if node_name in self._output_ns and not replace:
102             return
103         self._output_ns[node_name] = node
104
105     def _remove_attribute(self, name):
106         del self._output_ns[name]
107
108     def _get_attribute(self, name):
109         return self._output_ns.get(name)
110
111     def _register_internal_type(self, type_name, node):
112         self._type_names[type_name] = (None, node)
113
114     def _strip_namespace_func(self, name):
115         orig_name = name
116         prefix = self._namespace_name.lower() + '_'
117         name = name.lower()
118         if name.startswith(prefix):
119             name = orig_name[len(prefix):]
120         return name
121
122     def _strip_namespace_object(self, name):
123         orig_name = name
124         prefix = self._namespace_name.lower()
125         name = name.lower()
126         if name.startswith(prefix):
127             name = orig_name[len(prefix):]
128         return name
129
130     def _resolve_type_name(self, type_name):
131         item = self._type_names.get(type_name)
132         if item is not None:
133             nsname, item = item
134             if nsname is None:
135                 return item.name
136             return '%s.%s' % (nsname, item.name)
137         return type_name
138
139     def _resolve_param_type(self, ptype):
140         type_name = ptype.name.replace('*', '')
141         ptype.name = self._resolve_type_name(type_name)
142         return ptype
143
144     def _parse_node(self, node):
145         if isinstance(node, Enum):
146             self._parse_enum(node)
147         elif isinstance(node, Function):
148             self._parse_function(node)
149         elif isinstance(node, Struct):
150             self._parse_struct(node)
151         elif isinstance(node, Callback):
152             self._parse_callback(node)
153         else:
154             print 'GOBJECT BUILDER: Unhandled node:', node
155
156     def _parse_enum(self, enum):
157         enum.name = self._strip_namespace_object(enum.name)
158         self._add_attribute(enum)
159
160     def _parse_function(self, func):
161         if self._parse_get_type_function(func):
162             return
163         elif self._parse_constructor(func):
164             return
165         elif self._parse_method(func):
166             return
167
168         self._parse_parameters(func.parameters)
169         func.retval.type = self._resolve_param_type(func.retval.type)
170
171         func.name = self._strip_namespace_func(func.name)
172         self._add_attribute(func)
173
174     def _parse_parameters(self, parameters):
175         for parameter in parameters:
176             parameter.type = self._resolve_param_type(parameter.type)
177
178     def _parse_get_type_function(self, func):
179         if self._library is None:
180             return False
181         # GType *_get_type(void)
182         symbol = func.symbol
183         if not symbol.endswith('_get_type'):
184             return False
185         if func.retval.type.name != 'GType':
186             return False
187         if func.parameters:
188             return False
189
190         func = getattr(self._library, symbol)
191         func.restype = cgobject.GType
192         func.argtypes = []
193         type_id = func()
194         self._introspect_type(type_id, symbol)
195         return True
196
197     def _parse_method(self, func):
198         if not func.parameters:
199             return False
200
201         # FIXME: This is hackish, we should preserve the pointer structures
202         #        here, so we can find pointers to objects and not just
203         #        pointers to anything
204         first_arg = func.parameters[0].type.name
205         if first_arg.count('*') != 1:
206             return False
207
208         object_name = first_arg.replace('*', '')
209         return self._parse_method_common(func, object_name, is_method=True)
210
211     def _parse_constructor(self, func):
212         # FIXME: This is hackish, we should preserve the pointer structures
213         #        here, so we can find pointers to objects and not just
214         #        pointers to anything
215         rtype = func.retval.type
216         if rtype.name.count('*') != 1:
217             return False
218
219         object_name = rtype.name.replace('*', '')
220         return self._parse_method_common(func, object_name, is_method=False)
221
222     def _parse_method_common(self, func, object_name, is_method):
223         orig_name = object_name
224         if object_name.lower().startswith(self._namespace_name.lower()):
225             object_name = object_name[len(self._namespace_name):]
226         class_ = self._get_attribute(object_name)
227         if class_ is None or not isinstance(class_, (GLibObject, GLibBoxed)):
228             return False
229
230         # GtkButton -> gtk_button_, so we can figure out the method name
231         prefix = to_underscores(orig_name).lower() + '_'
232         if not func.name.startswith(prefix):
233             return False
234
235         # Strip namespace and object prefix: gtk_window_new -> new
236         func.name = func.name[len(prefix):]
237         if is_method:
238             class_.methods.append(func)
239         else:
240             class_.constructors.append(func)
241         self._parse_parameters(func.parameters)
242         func.retval.type = self._resolve_param_type(func.retval.type)
243         return True
244
245     def _parse_struct(self, struct):
246         type_name = self._resolve_type_name(struct.name)
247         node = self._output_ns.get(type_name)
248         if node is None:
249             self._add_attribute(struct, replace=True)
250             return
251         node.fields = struct.fields[:]
252
253     def _parse_callback(self, callback):
254         self._add_attribute(callback)
255
256     def _pair_class_struct(self, class_node):
257         name = class_node.name
258         if (name.endswith('Class') or
259             name.endswith('Iface')):
260             name = name[:-5]
261         elif name.endswith('Interface'):
262             name = name[:-9]
263         else:
264             return
265
266         node = self._output_ns.get(self._resolve_type_name(name))
267         del self._output_ns[class_node.name]
268         if node is None:
269             return
270         for field in class_node.fields[1:]:
271             node.fields.append(field)
272
273     def _create_type(self, type_id):
274         type_name = cgobject.type_name(type_id)
275         return Type(type_name)
276
277     def _introspect_type(self, type_id, symbol):
278         fundamental_type_id = cgobject.type_fundamental(type_id)
279         if (fundamental_type_id == cgobject.TYPE_ENUM or
280             fundamental_type_id == cgobject.TYPE_FLAGS):
281             self._introspect_enum(fundamental_type_id, type_id, symbol)
282         elif fundamental_type_id == cgobject.TYPE_OBJECT:
283             self._introspect_object(type_id, symbol)
284         elif fundamental_type_id == cgobject.TYPE_INTERFACE:
285             self._introspect_interface(type_id, symbol)
286         elif fundamental_type_id == cgobject.TYPE_BOXED:
287             self._introspect_boxed(type_id, symbol)
288         else:
289             print 'unhandled GType: %s' % (cgobject.type_name(type_id),)
290
291     def _introspect_enum(self, ftype_id, type_id, symbol):
292         type_class = cgobject.type_class_ref(type_id)
293         if type_class is None:
294             return
295
296         members = []
297         for enum_value in type_class.get_values():
298             members.append(GLibEnumMember(enum_value.value_name,
299                                           enum_value.value,
300                                           enum_value.value_nick))
301
302         klass = (GLibFlags if ftype_id == cgobject.TYPE_FLAGS else GLibEnum)
303         type_name = cgobject.type_name(type_id)
304         node = klass(self._strip_namespace_object(type_name),
305                       members, type_name, symbol)
306         self._add_attribute(node, replace=True)
307         self._register_internal_type(type_name, node)
308
309     def _introspect_object(self, type_id, symbol):
310         type_name = cgobject.type_name(type_id)
311         parent_type_name = cgobject.type_name(cgobject.type_parent(type_id))
312         node = GLibObject(self._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._remove_attribute(type_name)
319         self._register_internal_type(type_name, node)
320
321     def _introspect_interface(self, type_id, symbol):
322         type_name = cgobject.type_name(type_id)
323         node = GLibInterface(self._strip_namespace_object(type_name),
324                              type_name, symbol)
325         self._introspect_properties(node, type_id)
326         self._introspect_signals(node, type_id)
327         self._add_attribute(node)
328         self._register_internal_type(type_name, node)
329
330     def _introspect_boxed(self, type_id, symbol):
331         type_name = cgobject.type_name(type_id)
332         node = GLibBoxed(self._strip_namespace_object(type_name),
333                          type_name, symbol)
334         self._add_attribute(node)
335         self._remove_attribute(type_name)
336         self._register_internal_type(type_name, node)
337
338     def _introspect_properties(self, node, type_id):
339         fundamental_type_id = cgobject.type_fundamental(type_id)
340         if fundamental_type_id == cgobject.TYPE_OBJECT:
341             pspecs = cgobject.object_class_list_properties(type_id)
342         elif fundamental_type_id == cgobject.TYPE_INTERFACE:
343             pspecs = cgobject.object_interface_list_properties(type_id)
344         else:
345             raise AssertionError
346
347         for pspec in pspecs:
348             if pspec.owner_type != type_id:
349                 continue
350             node.properties.append(Property(
351                 pspec.name,
352                 cgobject.type_name(pspec.value_type)))
353
354     def _introspect_signals(self, node, type_id):
355         for signal_info in cgobject.signal_list(type_id):
356             return_ = Return(cgobject.type_name(signal_info.return_type))
357             signal = GLibSignal(signal_info.signal_name, return_)
358             for i, parameter in enumerate(signal_info.get_params()):
359                 if i == 0:
360                     name = 'object'
361                 else:
362                     name = 'p%s' % (i-1,)
363                 ptype = self._create_type(parameter)
364                 param = Parameter(name, ptype)
365                 signal.parameters.append(param)
366             node.signals.append(signal)