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