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