7904eeb6d2aa24259a2e8d0aa03c91016359b0d3
[platform/upstream/gobject-introspection.git] / giscanner / transformer.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 os
22 import sys
23
24 from giscanner.ast import (Callback, Enum, Function, Namespace, Member,
25                            Parameter, Return, Sequence, Struct, Field,
26                            Type, Alias, type_name_from_ctype)
27 from .glibast import (GLibBoxed, GLibEnum, GLibEnumMember, GLibFlags,
28                       GLibInterface, GLibObject, GLibSignal)
29 from giscanner.sourcescanner import (
30     SourceSymbol, ctype_name, symbol_type_name, CTYPE_POINTER,
31     CTYPE_BASIC_TYPE, CTYPE_UNION, CTYPE_ARRAY,
32     CTYPE_TYPEDEF, CTYPE_VOID, CTYPE_BASIC_TYPE, CTYPE_ENUM,
33     CTYPE_FUNCTION, CTYPE_STRUCT, CSYMBOL_TYPE_FUNCTION,
34     CSYMBOL_TYPE_TYPEDEF, CSYMBOL_TYPE_STRUCT, CSYMBOL_TYPE_ENUM,
35     CSYMBOL_TYPE_UNION, CSYMBOL_TYPE_OBJECT, CSYMBOL_TYPE_MEMBER)
36 from .utils import strip_common_prefix
37
38
39 class Transformer(object):
40
41     def __init__(self, generator, namespace_name):
42         self.generator = generator
43         self._namespace = Namespace(namespace_name)
44         self._output_ns = {}
45         self._alias_names = {}
46         self._type_names = {}
47         self._ctype_names = {}
48         self._typedefs_ns = {}
49         self._strip_prefix = ''
50         self._typedefs = {}
51
52     def get_type_names(self):
53         return self._type_names
54
55     def get_alias_names(self):
56         return self._alias_names
57
58     def set_strip_prefix(self, strip_prefix):
59         self._strip_prefix = strip_prefix
60
61     def resolve_possible_typedef(self, tname):
62         return self._typedefs.get(tname, tname)
63
64     def parse(self):
65         nodes = []
66         for symbol in self.generator.get_symbols():
67             node = self._traverse_one(symbol)
68             if node is None:
69                 continue
70             if node.name.startswith('_'):
71                 continue
72             self._namespace.nodes.append(node)
73             self._output_ns[node.name] = node
74         return self._namespace
75
76     def register_include(self, filename):
77         if filename.endswith('.gir'):
78             from .girparser import GIRParser
79             parser = GIRParser(filename)
80         elif filename.endswith('.gidl'):
81             from .gidlparser import GIDLParser
82             parser = GIDLParser(filename)
83         else:
84             raise NotImplementedError(filename)
85         nsname = parser.get_namespace_name()
86         for node in parser.get_nodes():
87             if hasattr(node, 'ctype'):
88                 self._ctype_names[node.ctype] = (nsname, node)
89             if isinstance(node, GLibBoxed) or isinstance(node, GLibInterface) \
90                     or isinstance(node, GLibObject):
91                 self._type_names[node.type_name] = (nsname, node)
92             elif isinstance(node, Alias):
93                 self._alias_names[node.name] = (nsname, node)
94             else:
95                 self._type_names[node.name] = (nsname, node)
96
97     def strip_namespace_object(self, name):
98         orig_name = name
99         prefix = self._namespace.name.lower()
100         name = name.lower()
101         if name.startswith(prefix):
102             name = orig_name[len(prefix):]
103         return name
104
105     # Private
106
107     def _strip_namespace_func(self, name):
108         orig_name = name
109         prefix = self._namespace.name.lower() + '_'
110         name = name.lower()
111         if name.startswith(prefix):
112             name = orig_name[len(prefix):]
113         return name
114
115     def _remove_prefix(self, name):
116         # when --strip-prefix=g:
117         #   GHashTable -> HashTable
118         #   g_hash_table_new -> hash_table_new
119         if name.lower().startswith(self._strip_prefix.lower()):
120             name = name[len(self._strip_prefix):]
121
122         while name.startswith('_'):
123             name = name[1:]
124         return name
125
126     def _traverse_one(self, symbol, stype=None):
127         assert isinstance(symbol, SourceSymbol), symbol
128
129         if stype is None:
130             stype = symbol.type
131         if stype == CSYMBOL_TYPE_FUNCTION:
132             return self._create_function(symbol)
133         elif stype == CSYMBOL_TYPE_TYPEDEF:
134             return self._create_typedef(symbol)
135         elif stype == CSYMBOL_TYPE_STRUCT:
136             return self._create_struct(symbol)
137         elif stype == CSYMBOL_TYPE_ENUM:
138             return self._create_enum(symbol)
139         elif stype == CSYMBOL_TYPE_OBJECT:
140             return self._create_object(symbol)
141         elif stype == CSYMBOL_TYPE_MEMBER:
142             return self._create_member(symbol)
143         elif stype == CSYMBOL_TYPE_UNION:
144             # Unions are not supported
145             pass
146         else:
147             raise NotImplementedError(
148                 'Transformer: unhandled symbol: %r' % (symbol, ))
149
150     def _create_enum(self, symbol):
151         members = []
152         for child in symbol.base_type.child_list:
153             name = strip_common_prefix(symbol.ident, child.ident).lower()
154             members.append(Member(name,
155                                   child.const_int,
156                                   child.ident))
157
158         enum_name = self.strip_namespace_object(symbol.ident)
159         return Enum(enum_name, symbol.ident, members)
160
161     def _create_object(self, symbol):
162         return Member(symbol.ident, symbol.base_type.name,
163                       symbol.ident)
164
165     def _create_function(self, symbol):
166         directives = symbol.directives()
167         parameters = list(self._create_parameters(
168             symbol.base_type, directives))
169         return_ = self._create_return(symbol.base_type.base_type,
170                                       directives.get('return', []))
171         name = self._remove_prefix(symbol.ident)
172         name = self._strip_namespace_func(name)
173         return Function(name, return_, parameters, symbol.ident)
174
175     def _create_source_type(self, source_type):
176         if source_type is None:
177             return 'None'
178         if source_type.type == CTYPE_VOID:
179             value = 'void'
180         elif source_type.type == CTYPE_BASIC_TYPE:
181             value = source_type.name
182         elif source_type.type == CTYPE_TYPEDEF:
183             value = source_type.name
184         elif source_type.type == CTYPE_ARRAY:
185             return self._create_source_type(source_type.base_type)
186         elif source_type.type == CTYPE_POINTER:
187             value = self._create_source_type(source_type.base_type) + '*'
188         else:
189             print 'TRANSFORMER: Unhandled source type %r' % (
190                 source_type, )
191             value = '???'
192         return value
193
194     def _create_parameters(self, base_type, options=None):
195         if not options:
196             options = {}
197         for child in base_type.child_list:
198             yield self._create_parameter(
199                 child, options.get(child.ident, []))
200
201     def _create_member(self, symbol):
202         ctype = symbol.base_type.type
203         if (ctype == CTYPE_POINTER and
204             symbol.base_type.base_type.type == CTYPE_FUNCTION):
205             node = self._create_callback(symbol)
206         else:
207             ftype = self._create_type(symbol.base_type)
208             node = Field(symbol.ident, ftype, symbol.ident)
209         return node
210
211     def _create_typedef(self, symbol):
212         ctype = symbol.base_type.type
213         if (ctype == CTYPE_POINTER and
214             symbol.base_type.base_type.type == CTYPE_FUNCTION):
215             node = self._create_callback(symbol)
216         elif ctype == CTYPE_STRUCT:
217             node = self._create_typedef_struct(symbol)
218         elif ctype == CTYPE_ENUM:
219             return self._create_enum(symbol)
220         elif ctype in (CTYPE_TYPEDEF,
221                        CTYPE_POINTER,
222                        CTYPE_BASIC_TYPE,
223                        CTYPE_UNION,
224                        CTYPE_VOID):
225             if symbol.base_type.name:
226                 return Alias(symbol.ident, symbol.base_type.name)
227             return None
228         else:
229             raise NotImplementedError(
230                 "symbol %r of type %s" % (symbol.ident, ctype_name(ctype)))
231         return node
232
233     def _create_type(self, source_type):
234         ctype = self._create_source_type(source_type)
235         type_name = type_name_from_ctype(ctype)
236         resolved_type_name = self.resolve_type_name(type_name)
237         return Type(resolved_type_name, ctype)
238
239     def _create_parameter(self, symbol, options):
240         ptype = self._create_type(symbol.base_type)
241         param = Parameter(symbol.ident, ptype)
242         for option in options:
243             if option in ['in-out', 'inout']:
244                 param.direction = 'inout'
245             elif option == 'in':
246                 param.direction = 'in'
247             elif option == 'out':
248                 param.direction = 'out'
249             elif option == 'callee-owns':
250                 param.transfer = True
251             elif option == 'allow-none':
252                 param.allow_none = True
253             else:
254                 print 'Unhandled parameter annotation option: %s' % (
255                     option, )
256         return param
257
258     def _create_return(self, source_type, options=None):
259         if not options:
260             options = []
261         rtype = self._create_type(source_type)
262         rtype = self.resolve_param_type(rtype)
263         return_ = Return(rtype)
264         for option in options:
265             if option == 'caller-owns':
266                 return_.transfer = True
267             elif option.startswith('seq '):
268                 value, element_options = option[3:].split(None, 2)
269                 element_type = self._parse_type_annotation(value)
270                 seq = Sequence(rtype.name,
271                                type_name_from_ctype(rtype.name),
272                                element_type)
273                 seq.transfer = True
274                 return_.type = seq
275             else:
276                 print 'Unhandled parameter annotation option: %s' % (
277                     option, )
278         return return_
279
280     def _create_typedef_struct(self, symbol):
281         name = self._remove_prefix(symbol.ident)
282         struct = Struct(name, symbol.ident)
283         self._typedefs_ns[symbol.ident] = struct
284         return struct
285
286     def _create_struct(self, symbol):
287         struct = self._typedefs_ns.get(symbol.ident, None)
288         if struct is None:
289             name = self._remove_prefix(symbol.ident)
290             name = self.resolve_type_name(name)
291             struct = Struct(name, symbol.ident)
292
293         for child in symbol.base_type.child_list:
294             field = self._traverse_one(child)
295             if field:
296                 struct.fields.append(field)
297
298         return struct
299
300     def _create_callback(self, symbol):
301         parameters = self._create_parameters(symbol.base_type.base_type)
302         retval = self._create_return(symbol.base_type.base_type.base_type)
303         return Callback(symbol.ident, retval, list(parameters))
304
305     def _parse_type_annotation(self, annotation):
306         if (annotation[0] == "[" and
307             annotation[-1] == "]"):
308             return Sequence(self._parse_type_annotation(annotation[1:-1]))
309         return annotation
310
311     def _typepair_to_str(self, item):
312         nsname, item = item
313         if nsname is None:
314             return item.name
315         return '%s.%s' % (nsname, item.name)
316
317     def resolve_type_name(self, type_name):
318         resolved = self._type_names.get(type_name)
319         if resolved:
320             return self._typepair_to_str(resolved)
321         resolved = self._alias_names.get(type_name)
322         if resolved:
323             return self._typepair_to_str(resolved)
324         return type_name
325
326     def resolve_param_type(self, ptype):
327         type_name = ptype.name.replace('*', '')
328         resolved = self._type_names.get(type_name)
329         if resolved:
330             ptype.name = self._typepair_to_str(resolved)
331             return ptype
332         if hasattr(ptype, 'ctype'):
333             ctype = ptype.ctype
334             resolved = self._ctype_names.get(ctype)
335             if resolved:
336                 ptype.name = self._typepair_to_str(resolved)
337                 return ptype
338         resolved = self._alias_names.get(type_name)
339         if resolved:
340             ptype.name = self._typepair_to_str(resolved)
341             return ptype
342         return ptype