Imported Upstream version 1.39.3
[platform/upstream/gobject-introspection.git] / giscanner / girparser.py
1 # -*- Mode: Python -*-
2 # GObject-Introspection - a framework for introspecting GObject libraries
3 # Copyright (C) 2008  Johan Dahlin
4 #
5 # This library is free software; you can redistribute it and/or
6 # modify it under the terms of the GNU Lesser General Public
7 # License as published by the Free Software Foundation; either
8 # version 2 of the License, or (at your option) any later version.
9 #
10 # This library 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 GNU
13 # Lesser General Public License for more details.
14 #
15 # You should have received a copy of the GNU Lesser General Public
16 # License along with this library; if not, write to the
17 # Free Software Foundation, Inc., 59 Temple Place - Suite 330,
18 # Boston, MA 02111-1307, USA.
19 #
20
21 import os
22
23 from xml.etree.cElementTree import parse
24
25 from . import ast
26 from .girwriter import COMPATIBLE_GIR_VERSION
27
28 CORE_NS = "http://www.gtk.org/introspection/core/1.0"
29 C_NS = "http://www.gtk.org/introspection/c/1.0"
30 GLIB_NS = "http://www.gtk.org/introspection/glib/1.0"
31
32
33 def _corens(tag):
34     return '{%s}%s' % (CORE_NS, tag)
35
36
37 def _glibns(tag):
38     return '{%s}%s' % (GLIB_NS, tag)
39
40
41 def _cns(tag):
42     return '{%s}%s' % (C_NS, tag)
43
44
45 class GIRParser(object):
46
47     def __init__(self, types_only=False):
48         self._types_only = types_only
49         self._namespace = None
50         self._filename_stack = []
51
52     # Public API
53
54     def parse(self, filename):
55         filename = os.path.abspath(filename)
56         self._filename_stack.append(filename)
57         tree = parse(filename)
58         self.parse_tree(tree)
59         self._filename_stack.pop()
60
61     def parse_tree(self, tree):
62         self._namespace = None
63         self._pkgconfig_packages = set()
64         self._includes = set()
65         self._c_includes = set()
66         self._c_prefix = None
67         self._parse_api(tree.getroot())
68
69     def get_namespace(self):
70         return self._namespace
71
72     # Private
73
74     def _find_first_child(self, node, name_or_names):
75         if isinstance(name_or_names, str):
76             for child in node.getchildren():
77                 if child.tag == name_or_names:
78                     return child
79         else:
80             for child in node.getchildren():
81                 if child.tag in name_or_names:
82                     return child
83         return None
84
85     def _find_children(self, node, name):
86         return [child for child in node.getchildren() if child.tag == name]
87
88     def _get_current_file(self):
89         if not self._filename_stack:
90             return None
91         cwd = os.getcwd() + os.sep
92         curfile = self._filename_stack[-1]
93         if curfile.startswith(cwd):
94             return curfile[len(cwd):]
95         return curfile
96
97     def _parse_api(self, root):
98         assert root.tag == _corens('repository')
99         version = root.attrib['version']
100         if version != COMPATIBLE_GIR_VERSION:
101             raise SystemExit("%s: Incompatible version %s (supported: %s)" %
102                              (self._get_current_file(), version, COMPATIBLE_GIR_VERSION))
103
104         for node in root.getchildren():
105             if node.tag == _corens('include'):
106                 self._parse_include(node)
107             elif node.tag == _corens('package'):
108                 self._parse_pkgconfig_package(node)
109             elif node.tag == _cns('include'):
110                 self._parse_c_include(node)
111
112         ns = root.find(_corens('namespace'))
113         assert ns is not None
114         identifier_prefixes = ns.attrib.get(_cns('identifier-prefixes'))
115         if identifier_prefixes:
116             identifier_prefixes = identifier_prefixes.split(',')
117         symbol_prefixes = ns.attrib.get(_cns('symbol-prefixes'))
118         if symbol_prefixes:
119             symbol_prefixes = symbol_prefixes.split(',')
120         self._namespace = ast.Namespace(ns.attrib['name'],
121                                         ns.attrib['version'],
122                                         identifier_prefixes=identifier_prefixes,
123                                         symbol_prefixes=symbol_prefixes)
124         if 'shared-library' in ns.attrib:
125             self._namespace.shared_libraries = ns.attrib['shared-library'].split(',')
126         self._namespace.includes = self._includes
127         self._namespace.c_includes = self._c_includes
128         self._namespace.exported_packages = self._pkgconfig_packages
129
130         parser_methods = {
131             _corens('alias'): self._parse_alias,
132             _corens('bitfield'): self._parse_enumeration_bitfield,
133             _corens('callback'): self._parse_callback,
134             _corens('class'): self._parse_object_interface,
135             _corens('enumeration'): self._parse_enumeration_bitfield,
136             _corens('interface'): self._parse_object_interface,
137             _corens('record'): self._parse_record,
138             _corens('union'): self._parse_union,
139             _glibns('boxed'): self._parse_boxed}
140
141         if not self._types_only:
142             parser_methods[_corens('constant')] = self._parse_constant
143             parser_methods[_corens('function')] = self._parse_function
144
145         for node in ns.getchildren():
146             method = parser_methods.get(node.tag)
147             if method is not None:
148                 method(node)
149
150     def _parse_include(self, node):
151         include = ast.Include(node.attrib['name'], node.attrib['version'])
152         self._includes.add(include)
153
154     def _parse_pkgconfig_package(self, node):
155         self._pkgconfig_packages.add(node.attrib['name'])
156
157     def _parse_c_include(self, node):
158         self._c_includes.add(node.attrib['name'])
159
160     def _parse_alias(self, node):
161         typeval = self._parse_type(node)
162         alias = ast.Alias(node.attrib['name'], typeval, node.attrib.get(_cns('type')))
163         self._parse_generic_attribs(node, alias)
164         self._namespace.append(alias)
165
166     def _parse_generic_attribs(self, node, obj):
167         assert isinstance(obj, ast.Annotated)
168         introspectable = node.attrib.get('introspectable')
169         if introspectable:
170             obj.introspectable = int(introspectable) > 0
171         if self._types_only:
172             return
173         doc = node.find(_corens('doc'))
174         if doc is not None:
175             if doc.text:
176                 obj.doc = doc.text
177         version = node.attrib.get('version')
178         if version:
179             obj.version = version
180         version_doc = node.find(_corens('doc-version'))
181         if version_doc is not None:
182             if version_doc.text:
183                 obj.version_doc = version_doc.text
184         deprecated = node.attrib.get('deprecated-version')
185         if deprecated:
186             obj.deprecated = deprecated
187         deprecated_doc = node.find(_corens('doc-deprecated'))
188         if deprecated_doc is not None:
189             if deprecated_doc.text:
190                 obj.deprecated_doc = deprecated_doc.text
191         stability = node.attrib.get('stability')
192         if stability:
193             obj.stability = stability
194         stability_doc = node.find(_corens('doc-stability'))
195         if stability_doc is not None:
196             if stability_doc.text:
197                 obj.stability_doc = stability_doc.text
198
199     def _parse_object_interface(self, node):
200         parent = node.attrib.get('parent')
201         if parent:
202             parent_type = self._namespace.type_from_name(parent)
203         else:
204             parent_type = None
205
206         ctor_kwargs = {'name': node.attrib['name'],
207                        'parent_type': parent_type,
208                        'gtype_name': node.attrib[_glibns('type-name')],
209                        'get_type': node.attrib[_glibns('get-type')],
210                        'c_symbol_prefix': node.attrib.get(_cns('symbol-prefix')),
211                        'ctype': node.attrib.get(_cns('type'))}
212         if node.tag == _corens('interface'):
213             klass = ast.Interface
214         elif node.tag == _corens('class'):
215             klass = ast.Class
216             is_abstract = node.attrib.get('abstract')
217             is_abstract = is_abstract and is_abstract != '0'
218             ctor_kwargs['is_abstract'] = is_abstract
219         else:
220             raise AssertionError(node)
221
222         obj = klass(**ctor_kwargs)
223         self._parse_generic_attribs(node, obj)
224         type_struct = node.attrib.get(_glibns('type-struct'))
225         if type_struct:
226             obj.glib_type_struct = self._namespace.type_from_name(type_struct)
227         if klass == ast.Class:
228             is_fundamental = node.attrib.get(_glibns('fundamental'))
229             if is_fundamental and is_fundamental != '0':
230                 obj.fundamental = True
231             for func_id in ['ref-func', 'unref-func',
232                             'set-value-func', 'get-value-func']:
233                 func_name = node.attrib.get(_glibns(func_id))
234                 obj.__dict__[func_id.replace('-', '_')] = func_name
235
236         if self._types_only:
237             self._namespace.append(obj)
238             return
239
240         for iface in self._find_children(node, _corens('implements')):
241             obj.interfaces.append(self._namespace.type_from_name(iface.attrib['name']))
242         for iface in self._find_children(node, _corens('prerequisite')):
243             obj.prerequisites.append(self._namespace.type_from_name(iface.attrib['name']))
244         for func_node in self._find_children(node, _corens('function')):
245             func = self._parse_function_common(func_node, ast.Function, obj)
246             obj.static_methods.append(func)
247         for method in self._find_children(node, _corens('method')):
248             func = self._parse_function_common(method, ast.Function, obj)
249             func.is_method = True
250             obj.methods.append(func)
251         for method in self._find_children(node, _corens('virtual-method')):
252             func = self._parse_function_common(method, ast.VFunction, obj)
253             self._parse_generic_attribs(method, func)
254             func.is_method = True
255             func.invoker = method.get('invoker')
256             obj.virtual_methods.append(func)
257         for ctor in self._find_children(node, _corens('constructor')):
258             func = self._parse_function_common(ctor, ast.Function, obj)
259             func.is_constructor = True
260             obj.constructors.append(func)
261         obj.fields.extend(self._parse_fields(node, obj))
262         for prop in self._find_children(node, _corens('property')):
263             obj.properties.append(self._parse_property(prop, obj))
264         for signal in self._find_children(node, _glibns('signal')):
265             obj.signals.append(self._parse_function_common(signal, ast.Signal, obj))
266
267         self._namespace.append(obj)
268
269     def _parse_callback(self, node):
270         callback = self._parse_function_common(node, ast.Callback)
271         self._namespace.append(callback)
272
273     def _parse_function(self, node):
274         function = self._parse_function_common(node, ast.Function)
275         self._namespace.append(function)
276
277     def _parse_parameter(self, node):
278         typeval = self._parse_type(node)
279         param = ast.Parameter(node.attrib.get('name'),
280                               typeval,
281                               node.attrib.get('direction') or ast.PARAM_DIRECTION_IN,
282                               node.attrib.get('transfer-ownership'),
283                               node.attrib.get('allow-none') == '1',
284                               node.attrib.get('scope'),
285                               node.attrib.get('caller-allocates') == '1')
286         self._parse_generic_attribs(node, param)
287         return param
288
289     def _parse_function_common(self, node, klass, parent=None):
290         name = node.attrib['name']
291         returnnode = node.find(_corens('return-value'))
292         if not returnnode:
293             raise ValueError('node %r has no return-value' % (name, ))
294         transfer = returnnode.attrib.get('transfer-ownership')
295         retval = ast.Return(self._parse_type(returnnode), transfer)
296         self._parse_generic_attribs(returnnode, retval)
297         parameters = []
298
299         throws = (node.attrib.get('throws') == '1')
300
301         if klass is ast.Callback:
302             func = klass(name, retval, parameters, throws,
303                          node.attrib.get(_cns('type')))
304         elif klass is ast.Function:
305             identifier = node.attrib.get(_cns('identifier'))
306             func = klass(name, retval, parameters, throws, identifier)
307         elif klass is ast.VFunction:
308             func = klass(name, retval, parameters, throws)
309         elif klass is ast.Signal:
310             func = klass(name, retval, parameters,
311                          when=node.attrib.get('when'),
312                          no_recurse=node.attrib.get('no-recurse', '0') == '1',
313                          detailed=node.attrib.get('detailed', '0') == '1',
314                          action=node.attrib.get('action', '0') == '1',
315                          no_hooks=node.attrib.get('no-hooks', '0') == '1')
316         else:
317             assert False
318
319         func.shadows = node.attrib.get('shadows', None)
320         func.shadowed_by = node.attrib.get('shadowed-by', None)
321         func.moved_to = node.attrib.get('moved-to', None)
322         func.parent = parent
323
324         parameters_node = node.find(_corens('parameters'))
325         if (parameters_node is not None):
326             paramnode = self._find_first_child(parameters_node, _corens('instance-parameter'))
327             if paramnode:
328                 func.instance_parameter = self._parse_parameter(paramnode)
329             for paramnode in self._find_children(parameters_node, _corens('parameter')):
330                 parameters.append(self._parse_parameter(paramnode))
331             for i, paramnode in enumerate(self._find_children(parameters_node,
332                                                               _corens('parameter'))):
333                 param = parameters[i]
334                 self._parse_type_array_length(parameters, paramnode, param.type)
335                 closure = paramnode.attrib.get('closure')
336                 if closure:
337                     idx = int(closure)
338                     assert idx < len(parameters), "%d >= %d" % (idx, len(parameters))
339                     param.closure_name = parameters[idx].argname
340                 destroy = paramnode.attrib.get('destroy')
341                 if destroy:
342                     idx = int(destroy)
343                     assert idx < len(parameters), "%d >= %d" % (idx, len(parameters))
344                     param.destroy_name = parameters[idx].argname
345
346         self._parse_type_array_length(parameters, returnnode, retval.type)
347
348         self._parse_generic_attribs(node, func)
349
350         self._namespace.track(func)
351         return func
352
353     def _parse_fields(self, node, obj):
354         res = []
355         names = (_corens('field'), _corens('record'), _corens('union'), _corens('callback'))
356         for child in node.getchildren():
357             if child.tag in names:
358                 fieldobj = self._parse_field(child, obj)
359                 res.append(fieldobj)
360         return res
361
362     def _parse_compound(self, cls, node):
363         compound = cls(node.attrib.get('name'),
364                        ctype=node.attrib.get(_cns('type')),
365                        disguised=node.attrib.get('disguised') == '1',
366                        gtype_name=node.attrib.get(_glibns('type-name')),
367                        get_type=node.attrib.get(_glibns('get-type')),
368                        c_symbol_prefix=node.attrib.get(_cns('symbol-prefix')))
369         if node.attrib.get('foreign') == '1':
370             compound.foreign = True
371         self._parse_generic_attribs(node, compound)
372         if not self._types_only:
373             compound.fields.extend(self._parse_fields(node, compound))
374             for method in self._find_children(node, _corens('method')):
375                 func = self._parse_function_common(method, ast.Function, compound)
376                 func.is_method = True
377                 compound.methods.append(func)
378             for i, fieldnode in enumerate(self._find_children(node, _corens('field'))):
379                 field = compound.fields[i]
380                 self._parse_type_array_length(compound.fields, fieldnode, field.type)
381             for func in self._find_children(node, _corens('function')):
382                 compound.static_methods.append(
383                     self._parse_function_common(func, ast.Function, compound))
384             for ctor in self._find_children(node, _corens('constructor')):
385                 func = self._parse_function_common(ctor, ast.Function, compound)
386                 func.is_constructor = True
387                 compound.constructors.append(func)
388         return compound
389
390     def _parse_record(self, node, anonymous=False):
391         struct = self._parse_compound(ast.Record, node)
392         is_gtype_struct_for = node.attrib.get(_glibns('is-gtype-struct-for'))
393         if is_gtype_struct_for is not None:
394             struct.is_gtype_struct_for = self._namespace.type_from_name(is_gtype_struct_for)
395         if not anonymous:
396             self._namespace.append(struct)
397         return struct
398
399     def _parse_union(self, node, anonymous=False):
400         union = self._parse_compound(ast.Union, node)
401         if not anonymous:
402             self._namespace.append(union)
403         return union
404
405     def _parse_type_simple(self, typenode):
406         # ast.Fields can contain inline callbacks
407         if typenode.tag == _corens('callback'):
408             typeval = self._namespace.type_from_name(typenode.attrib['name'])
409             typeval.ctype = typenode.attrib.get(_cns('type'))
410             return typeval
411         # ast.Arrays have their own toplevel XML
412         elif typenode.tag == _corens('array'):
413             array_type = typenode.attrib.get('name')
414             element_type = self._parse_type(typenode)
415             array_ctype = typenode.attrib.get(_cns('type'))
416             ret = ast.Array(array_type, element_type, ctype=array_ctype)
417             # zero-terminated defaults to true...
418             zero = typenode.attrib.get('zero-terminated')
419             if zero and zero == '0':
420                 ret.zeroterminated = False
421             fixed_size = typenode.attrib.get('fixed-size')
422             if fixed_size:
423                 ret.size = int(fixed_size)
424
425             return ret
426         elif typenode.tag == _corens('varargs'):
427             return ast.Varargs()
428         elif typenode.tag == _corens('type'):
429             name = typenode.attrib.get('name')
430             ctype = typenode.attrib.get(_cns('type'))
431             if name is None:
432                 if ctype is None:
433                     return ast.TypeUnknown()
434                 return ast.Type(ctype=ctype)
435             elif name in ['GLib.List', 'GLib.SList']:
436                 subchild = self._find_first_child(typenode,
437                                                   map(_corens, ('callback', 'array',
438                                                                 'varargs', 'type')))
439                 if subchild is not None:
440                     element_type = self._parse_type(typenode)
441                 else:
442                     element_type = ast.TYPE_ANY
443                 return ast.List(name, element_type, ctype=ctype)
444             elif name == 'GLib.HashTable':
445                 subchildren = self._find_children(typenode, _corens('type'))
446                 subchildren_types = map(self._parse_type_simple, subchildren)
447                 while len(subchildren_types) < 2:
448                     subchildren_types.append(ast.TYPE_ANY)
449                 return ast.Map(subchildren_types[0], subchildren_types[1], ctype=ctype)
450             else:
451                 return self._namespace.type_from_name(name, ctype)
452         else:
453             assert False, "Failed to parse inner type"
454
455     def _parse_type(self, node):
456         for name in map(_corens, ('callback', 'array', 'varargs', 'type')):
457             typenode = node.find(name)
458             if typenode is not None:
459                 return self._parse_type_simple(typenode)
460         assert False, "Failed to parse toplevel type"
461
462     def _parse_type_array_length(self, siblings, node, typeval):
463         """A hack necessary to handle the integer parameter/field indexes on
464            array types."""
465         typenode = node.find(_corens('array'))
466         if typenode is None:
467             return
468         lenidx = typenode.attrib.get('length')
469         if lenidx is not None:
470             idx = int(lenidx)
471             assert idx < len(siblings), "%r %d >= %d" % (parent, idx, len(siblings))
472             if isinstance(siblings[idx], ast.Field):
473                 typeval.length_param_name = siblings[idx].name
474             else:
475                 typeval.length_param_name = siblings[idx].argname
476
477     def _parse_boxed(self, node):
478         obj = ast.Boxed(node.attrib[_glibns('name')],
479                         gtype_name=node.attrib[_glibns('type-name')],
480                         get_type=node.attrib[_glibns('get-type')],
481                         c_symbol_prefix=node.attrib.get(_cns('symbol-prefix')))
482         self._parse_generic_attribs(node, obj)
483
484         if self._types_only:
485             self._namespace.append(obj)
486             return
487
488         for method in self._find_children(node, _corens('method')):
489             func = self._parse_function_common(method, ast.Function, obj)
490             func.is_method = True
491             obj.methods.append(func)
492         for ctor in self._find_children(node, _corens('constructor')):
493             obj.constructors.append(
494                 self._parse_function_common(ctor, ast.Function, obj))
495         for callback in self._find_children(node, _corens('callback')):
496             obj.fields.append(
497                 self._parse_function_common(callback, ast.Callback, obj))
498         self._namespace.append(obj)
499
500     def _parse_field(self, node, parent):
501         type_node = None
502         anonymous_node = None
503         if node.tag in map(_corens, ('record', 'union')):
504             anonymous_elt = node
505         else:
506             anonymous_elt = self._find_first_child(node, _corens('callback'))
507         if anonymous_elt is not None:
508             if anonymous_elt.tag == _corens('callback'):
509                 anonymous_node = self._parse_function_common(anonymous_elt, ast.Callback)
510             elif anonymous_elt.tag == _corens('record'):
511                 anonymous_node = self._parse_record(anonymous_elt, anonymous=True)
512             elif anonymous_elt.tag == _corens('union'):
513                 anonymous_node = self._parse_union(anonymous_elt, anonymous=True)
514             else:
515                 assert False, anonymous_elt.tag
516         else:
517             assert node.tag == _corens('field'), node.tag
518             type_node = self._parse_type(node)
519         field = ast.Field(node.attrib.get('name'),
520                           type_node,
521                           node.attrib.get('readable') != '0',
522                           node.attrib.get('writable') == '1',
523                           node.attrib.get('bits'),
524                           anonymous_node=anonymous_node)
525         field.private = node.attrib.get('private') == '1'
526         field.parent = parent
527         self._parse_generic_attribs(node, field)
528         return field
529
530     def _parse_property(self, node, parent):
531         prop = ast.Property(node.attrib['name'],
532                             self._parse_type(node),
533                             node.attrib.get('readable') != '0',
534                             node.attrib.get('writable') == '1',
535                             node.attrib.get('construct') == '1',
536                             node.attrib.get('construct-only') == '1',
537                             node.attrib.get('transfer-ownership'))
538         self._parse_generic_attribs(node, prop)
539         prop.parent = parent
540         return prop
541
542     def _parse_member(self, node):
543         member = ast.Member(node.attrib['name'],
544                             node.attrib['value'],
545                             node.attrib.get(_cns('identifier')),
546                             node.attrib.get(_glibns('nick')))
547         self._parse_generic_attribs(node, member)
548         return member
549
550     def _parse_constant(self, node):
551         type_node = self._parse_type(node)
552         constant = ast.Constant(node.attrib['name'],
553                                 type_node,
554                                 node.attrib['value'],
555                                 node.attrib.get(_cns('type')))
556         self._parse_generic_attribs(node, constant)
557         self._namespace.append(constant)
558
559     def _parse_enumeration_bitfield(self, node):
560         name = node.attrib.get('name')
561         ctype = node.attrib.get(_cns('type'))
562         get_type = node.attrib.get(_glibns('get-type'))
563         type_name = node.attrib.get(_glibns('type-name'))
564         glib_error_domain = node.attrib.get(_glibns('error-domain'))
565         if node.tag == _corens('bitfield'):
566             klass = ast.Bitfield
567         else:
568             klass = ast.Enum
569         members = []
570         obj = klass(name, ctype,
571                     members=members,
572                     gtype_name=type_name,
573                     get_type=get_type)
574         obj.error_domain = glib_error_domain
575         obj.ctype = ctype
576         self._parse_generic_attribs(node, obj)
577
578         if self._types_only:
579             self._namespace.append(obj)
580             return
581
582         for member_node in self._find_children(node, _corens('member')):
583             member = self._parse_member(member_node)
584             member.parent = obj
585             members.append(member)
586         for func_node in self._find_children(node, _corens('function')):
587             func = self._parse_function_common(func_node, ast.Function)
588             obj.static_methods.append(func)
589         self._namespace.append(obj)