Bug 557383 - Virtual function support
[gnome.gobject-introspection] / giscanner / glibtransformer.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 import sys
23 import re
24 import tempfile
25 import shutil
26 import subprocess
27
28 from .ast import (Alias, Bitfield, Callback, Constant, Enum, Function, Member,
29                   Namespace, Parameter, Property, Record, Return, Type, Union,
30                   Field, type_name_from_ctype,
31                   default_array_types, TYPE_UINT8, PARAM_TRANSFER_FULL)
32 from .transformer import Names
33 from .glibast import (GLibBoxed, GLibEnum, GLibEnumMember, GLibFlags,
34                       GLibInterface, GLibObject, GLibSignal, GLibBoxedStruct,
35                       GLibBoxedUnion, GLibBoxedOther, GLibRecord, type_names)
36 from .utils import to_underscores, to_underscores_noprefix
37
38 default_array_types['guchar*'] = TYPE_UINT8
39
40 # GParamFlags
41 G_PARAM_READABLE = 1 << 0
42 G_PARAM_WRITABLE = 1 << 1
43 G_PARAM_CONSTRUCT = 1 << 2
44 G_PARAM_CONSTRUCT_ONLY = 1 << 3
45 G_PARAM_LAX_VALIDATION = 1 << 4
46 G_PARAM_STATIC_NAME = 1 << 5
47 G_PARAM_STATIC_NICK = 1 << 6
48 G_PARAM_STATIC_BLURB = 1 << 7
49
50 SYMBOL_BLACKLIST = [
51     # These ones break GError conventions
52     'g_simple_async_result_new_from_error',
53     'g_simple_async_result_set_from_error',
54     'g_simple_async_result_propagate_error',
55     'g_simple_async_result_report_error_in_idle',
56     'gtk_print_operation_get_error',
57 ]
58
59 SYMBOL_BLACKLIST_RE = [re.compile(x) for x in \
60                            [r'\w+_marshal_[A-Z]+__', ]]
61
62
63 class IntrospectionBinary(object):
64
65     def __init__(self, args, tmpdir=None):
66         self.args = args
67         if tmpdir is None:
68             self.tmpdir = tempfile.mkdtemp('', 'tmp-introspect')
69         else:
70             self.tmpdir = tmpdir
71
72
73 class Unresolved(object):
74
75     def __init__(self, target):
76         self.target = target
77
78
79 class UnknownTypeError(Exception):
80     pass
81
82
83 class GLibTransformer(object):
84
85     def __init__(self, transformer, noclosure=False):
86         self._transformer = transformer
87         self._noclosure = noclosure
88         self._namespace_name = None
89         self._names = Names()
90         self._uscore_type_names = {}
91         self._binary = None
92         self._get_type_functions = []
93         self._error_quark_functions = []
94         self._gtype_data = {}
95         self._failed_types = {}
96         self._boxed_types = {}
97         self._private_internal_types = {}
98         self._validating = False
99
100     # Public API
101
102     def _print_statistics(self):
103         nodes = list(self._names.names.itervalues())
104
105         def count_type(otype):
106             return len([x for x in nodes
107                         if isinstance(x[1], otype)])
108         objectcount = count_type(GLibObject)
109         ifacecount = count_type(GLibInterface)
110         enumcount = count_type(GLibEnum)
111         print " %d nodes; %d objects, %d interfaces, %d enums" \
112             % (len(nodes), objectcount, ifacecount, enumcount)
113
114     def init_parse(self):
115         """Do parsing steps that don't involve the introspection binary
116
117         This does enough work that get_type_functions() can be called.
118
119         """
120
121         namespace = self._transformer.parse()
122         self._namespace_name = namespace.name
123         self._namespace_version = namespace.version
124
125         # First pass: parsing
126         for node in namespace.nodes:
127             self._parse_node(node)
128
129         # We don't want an alias for this - it's handled specially in
130         # the typelib compiler.
131         if namespace.name == 'GObject':
132             del self._names.aliases['Type']
133
134     def get_get_type_functions(self):
135         return self._get_type_functions
136
137     def set_introspection_binary(self, binary):
138         self._binary = binary
139
140     def parse(self):
141         """Do remaining parsing steps requiring introspection binary"""
142
143         # Get all the GObject data by passing our list of get_type
144         # functions to the compiled binary
145
146         self._execute_binary()
147
148         # Introspection is done from within parsing
149
150         # Second pass: pair boxed structures
151         for boxed in self._boxed_types.itervalues():
152             self._pair_boxed_type(boxed)
153         # Third pass: delete class structures, resolve
154         # all types we now know about
155         nodes = list(self._names.names.itervalues())
156         for (ns, node) in nodes:
157             try:
158                 self._resolve_node(node)
159             except KeyError, e:
160                 print "WARNING: DELETING node %s: %s" % (node.name, e)
161                 self._remove_attribute(node.name)
162         # Another pass, since we need to have the methods parsed
163         # in order to correctly modify them after class/record
164         # pairing
165         for (ns, node) in nodes:
166             # associate GtkButtonClass with GtkButton
167             if isinstance(node, Record):
168                 self._pair_class_record(node)
169         for (ns, alias) in self._names.aliases.itervalues():
170             self._resolve_alias(alias)
171         self._resolve_quarks()
172         # Fourth pass: ensure all types are known
173         if not self._noclosure:
174             self._validate(nodes)
175
176         # Create a new namespace with what we found
177         namespace = Namespace(self._namespace_name, self._namespace_version)
178         namespace.nodes = map(lambda x: x[1], self._names.aliases.itervalues())
179         for (ns, x) in self._names.names.itervalues():
180             namespace.nodes.append(x)
181         return namespace
182
183     # Private
184
185     def _add_attribute(self, node, replace=False):
186         node_name = node.name
187         if (not replace) and node_name in self._names.names:
188             return
189         self._names.names[node_name] = (None, node)
190
191     def _remove_attribute(self, name):
192         del self._names.names[name]
193
194     def _get_attribute(self, name):
195         node = self._names.names.get(name)
196         if node:
197             return node[1]
198         return None
199
200     def _lookup_node(self, name):
201         if name in type_names:
202             return None
203         node = self._get_attribute(name)
204         if node is None:
205             node = self._transformer.get_names().names.get(name)
206             if node:
207                 return node[1]
208         return node
209
210     def _register_internal_type(self, type_name, node):
211         self._names.type_names[type_name] = (None, node)
212         uscored = to_underscores(type_name).lower()
213         self._uscore_type_names[uscored] = node
214         # Besides the straight underscore conversion, we also try
215         # removing the underscores from the namespace as a possible C
216         # mapping; e.g. it's webkit_web_view, not web_kit_web_view
217         suffix = self._transformer.remove_prefix(type_name)
218         prefix = type_name[:-len(suffix)]
219         no_uscore_prefixed = (prefix + '_' + to_underscores(suffix)).lower()
220         self._uscore_type_names[no_uscore_prefixed] = node
221
222     def _resolve_quarks(self):
223         for node in self._error_quark_functions:
224             short = node.symbol[:-len('_quark')]
225             enum = self._uscore_type_names.get(short)
226             if enum is not None:
227                 enum.error_quark = node.symbol
228
229     # Helper functions
230
231     def _resolve_gtypename(self, gtype_name):
232         try:
233             return self._transformer.gtypename_to_giname(gtype_name,
234                                                          self._names)
235         except KeyError, e:
236             return Unresolved(gtype_name)
237
238     def _execute_binary(self):
239         in_path = os.path.join(self._binary.tmpdir, 'types.txt')
240         f = open(in_path, 'w')
241         # TODO: Introspect GQuark functions
242         for func in self._get_type_functions:
243             f.write(func)
244             f.write('\n')
245         f.close()
246         out_path = os.path.join(self._binary.tmpdir, 'dump.xml')
247
248         args = []
249         args.extend(self._binary.args)
250         args.append('--introspect-dump=%s,%s' % (in_path, out_path))
251
252         # Invoke the binary, having written our get_type functions to types.txt
253         try:
254             subprocess.check_call(args, stdout=sys.stdout, stderr=sys.stderr)
255         except subprocess.CalledProcessError, e:
256             raise SystemExit(e)
257         self._read_introspect_dump(out_path)
258
259         # Clean up temporaries
260         shutil.rmtree(self._binary.tmpdir)
261
262     def _read_introspect_dump(self, xmlpath):
263         from xml.etree.cElementTree import parse
264         tree = parse(xmlpath)
265         root = tree.getroot()
266         for child in root:
267             self._gtype_data[child.attrib['name']] = child
268         for child in root:
269             self._introspect_type(child)
270
271     def _create_gobject(self, node):
272         type_name = 'G' + node.name
273         if type_name == 'GObject':
274             parent_gitype = None
275             symbol = 'intern'
276         elif type_name == 'GInitiallyUnowned':
277             parent_type_name = 'GObject'
278             parent_gitype = self._resolve_gtypename(parent_type_name)
279             symbol = 'g_initially_unowned_get_type'
280         else:
281             assert False
282         gnode = GLibObject(node.name, parent_gitype, type_name, symbol, True)
283         if type_name == 'GObject':
284             gnode.fields.extend(node.fields)
285         else:
286             # http://bugzilla.gnome.org/show_bug.cgi?id=569408
287             # GInitiallyUnowned is actually a typedef for GObject, but
288             # that's not reflected in the GIR, where it appears as a
289             # subclass (as it appears in the GType hierarchy).  So
290             # what we do here is copy all of the GObject fields into
291             # GInitiallyUnowned so that struct offset computation
292             # works correctly.
293             gnode.fields = self._names.names['Object'][1].fields
294         self._add_attribute(gnode)
295         self._register_internal_type(type_name, gnode)
296
297     # Parser
298
299     def _parse_node(self, node):
300         if isinstance(node, Enum):
301             self._parse_enum(node)
302         elif isinstance(node, Bitfield):
303             self._parse_bitfield(node)
304         elif isinstance(node, Function):
305             self._parse_function(node)
306         elif isinstance(node, Record):
307             self._parse_record(node)
308         elif isinstance(node, Callback):
309             self._parse_callback(node)
310         elif isinstance(node, Alias):
311             self._parse_alias(node)
312         elif isinstance(node, Member):
313             # FIXME: atk_misc_instance singletons
314             pass
315         elif isinstance(node, Union):
316             self._parse_union(node)
317         elif isinstance(node, Constant):
318             self._parse_constant(node)
319         else:
320             print 'GLIB Transformer: Unhandled node:', node
321
322     def _parse_alias(self, alias):
323         self._names.aliases[alias.name] = (None, alias)
324
325     def _parse_enum(self, enum):
326         self._add_attribute(enum)
327
328     def _parse_bitfield(self, enum):
329         self._add_attribute(enum)
330
331     def _parse_constant(self, constant):
332         self._add_attribute(constant)
333
334     def _parse_function(self, func):
335         if func.symbol in SYMBOL_BLACKLIST:
336             return
337         if func.symbol.startswith('_'):
338             return
339         for regexp in SYMBOL_BLACKLIST_RE:
340             if regexp.match(func.symbol):
341                 return
342         if self._parse_get_type_function(func):
343             return
344         if self._parse_error_quark_function(func):
345             return
346
347         self._add_attribute(func)
348
349     def _parse_get_type_function(self, func):
350         symbol = func.symbol
351         if not symbol.endswith('_get_type'):
352             return False
353         if self._namespace_name == 'GLib':
354             # No GObjects in GLib
355             return False
356         if (self._namespace_name == 'GObject' and
357             symbol in ('g_object_get_type', 'g_initially_unowned_get_type')):
358             # We handle these internally, see _create_gobject
359             return True
360         if func.parameters:
361             return False
362         # GType *_get_type(void)
363         if func.retval.type.name not in ['Type',
364                                          'GType',
365                                          'GObject.Type',
366                                          'Gtk.Type']:
367             print ("Warning: *_get_type function returns '%r'"
368                    ", not GObject.Type") % (func.retval.type.name, )
369             return False
370
371         self._get_type_functions.append(symbol)
372         return True
373
374     def _parse_error_quark_function(self, func):
375         if not func.symbol.endswith('_error_quark'):
376             return False
377         if func.parameters:
378             return False
379         if func.retval.type.name not in ['GLib.Quark',
380                                          'GQuark']:
381             return False
382
383         self._error_quark_functions.append(func)
384         return True
385
386     def _name_is_internal_gtype(self, giname):
387         try:
388             node = self._get_attribute(giname)
389             return isinstance(node, (GLibObject, GLibInterface,
390                                      GLibBoxed, GLibEnum, GLibFlags))
391         except KeyError, e:
392             return False
393
394     def _parse_static_method(self, func):
395         components = func.symbol.split('_')
396         if len(components) < 2:
397             return None
398         target_klass = None
399         prefix_components = None
400         methname = None
401         for i in xrange(1, len(components)-1):
402             prefix_components = '_'.join(components[0:-i])
403             methname = '_'.join(components[-i:])
404             target_klass = self._uscore_type_names.get(prefix_components)
405             if target_klass and isinstance(target_klass, GLibObject):
406                 break
407             target_klass = None
408         if not target_klass:
409             return None
410         self._remove_attribute(func.name)
411         func.name = methname
412         target_klass.static_methods.append(func)
413         func.is_method = True
414         return func
415
416     def _parse_method(self, func):
417         if not func.parameters:
418             return False
419         return self._parse_method_common(func, True)
420
421     def _parse_constructor(self, func):
422         return self._parse_method_common(func, False)
423
424     def _parse_method_common(self, func, is_method):
425         # Skip _get_type functions, we processed them
426         # already
427         if func.symbol.endswith('_get_type'):
428             return None
429         if self._namespace_name == 'GLib':
430             # No GObjects in GLib
431             return None
432
433         if not is_method:
434             target_arg = func.retval
435         else:
436             target_arg = func.parameters[0]
437
438         if is_method:
439             # Methods require their first arg to be a known class
440             # Look at the original C type (before namespace stripping), without
441             # pointers: GtkButton -> gtk_button_, so we can figure out the
442             # method name
443             argtype = target_arg.type.ctype.replace('*', '')
444             name = self._transformer.remove_prefix(argtype)
445             name_uscore = to_underscores_noprefix(name).lower()
446             name_offset = func.symbol.find(name_uscore)
447             if name_offset < 0:
448                 return None
449             prefix = func.symbol[:name_offset+len(name_uscore)]
450         else:
451             # Constructors must have _new
452             # Take everything before that as class name
453             new_idx = func.symbol.find('_new')
454             if new_idx < 0:
455                 return None
456             # Constructors don't return basic types
457             derefed = self._transformer.follow_aliases(target_arg.type.name,
458                                                        self._names)
459             if derefed in type_names:
460                 #print "NOTE: Rejecting constructor returning basic: %r" \
461                 #    % (func.symbol, )
462                 return None
463             prefix = func.symbol[:new_idx]
464
465         klass = self._uscore_type_names.get(prefix)
466         if klass is None:
467             #print "NOTE: No valid matching class for likely "+\
468             #    "method or constructor: %r" % (func.symbol, )
469             return None
470         # Enums can't have ctors or methods
471         if isinstance(klass, (GLibEnum, GLibFlags)):
472             return None
473
474         # The _uscore_type_names member holds the plain GLibBoxed
475         # object; we want to actually use the struct/record associated
476         if isinstance(klass, (Record, Union)):
477             remove_prefix = klass.symbol
478         else:
479             remove_prefix = klass.type_name
480
481         name = self._transformer.remove_prefix(remove_prefix)
482         klass = self._get_attribute(name)
483         if klass is None:
484             return
485
486         if not is_method:
487             # Interfaces can't have constructors, punt to global scope
488             if isinstance(klass, GLibInterface):
489                 #print "NOTE: Rejecting constructor for"+\
490                 #    " interface type: %r" % (func.symbol, )
491                 return None
492             # TODO - check that the return type is a subclass of the
493             # class from the prefix
494             # But for now, ensure that constructor returns are always
495             # the most concrete class
496             name = self._transformer.remove_prefix(remove_prefix)
497             func.retval.type = Type(name, func.retval.type.ctype)
498
499         self._remove_attribute(func.name)
500         # Strip namespace and object prefix: gtk_window_new -> new
501         func.name = func.symbol[len(prefix)+1:]
502         if is_method:
503             # We don't need the "this" parameter
504             del func.parameters[0]
505             klass.methods.append(func)
506             func.is_method = True
507         else:
508             klass.constructors.append(func)
509         return func
510
511     def _parse_record(self, record):
512         # This is a hack, but GObject is a rather fundamental piece so.
513         internal_names = ["Object", 'InitiallyUnowned']
514         g_internal_names = ["G" + x for x in internal_names]
515         if (self._namespace_name == 'GObject' and
516             record.name in internal_names):
517             self._create_gobject(record)
518             return
519         elif record.name in g_internal_names:
520             # Avoid duplicates
521             return
522         node = self._names.names.get(record.name)
523         if node is None:
524             self._add_attribute(record, replace=True)
525             self._register_internal_type(record.symbol, record)
526             return
527         (ns, node) = node
528         node.fields = record.fields[:]
529
530     def _parse_union(self, union):
531         node = self._names.names.get(union.name)
532         if node is None:
533             self._add_attribute(union, replace=True)
534             self._register_internal_type(union.symbol, union)
535             return
536         (ns, node) = node
537         node.fields = union.fields[:]
538
539     def _parse_callback(self, callback):
540         self._add_attribute(callback)
541
542     def _strip_class_suffix(self, name):
543         if (name.endswith('Class') or
544             name.endswith('Iface')):
545             return name[:-5]
546         elif name.endswith('Interface'):
547             return name[:-9]
548         else:
549             return name
550
551     def _arg_is_failed(self, param):
552         ctype = self._transformer.ctype_of(param).replace('*', '')
553         uscored = to_underscores(self._strip_class_suffix(ctype)).lower()
554         if uscored in self._failed_types:
555             print "Warning: failed type: %r" % (param, )
556             return True
557         return False
558
559     def _pair_class_record(self, maybe_class):
560         name = self._strip_class_suffix(maybe_class.name)
561         if name == maybe_class.name:
562             return
563
564         class_struct = maybe_class
565         if self._arg_is_failed(class_struct):
566             print "WARNING: deleting no-type %r" % (class_struct.name, )
567             del self._names.names[class_struct.name]
568             return
569
570         pair_class = self._get_attribute(name)
571         if (not pair_class or
572             not isinstance(pair_class, (GLibObject, GLibInterface))):
573             return
574
575         # Object class fields are assumed to be read-only
576         # (see also _introspect_object and transformer.py)
577         for field in maybe_class.fields:
578             if isinstance(field, Field):
579                 field.writable = False
580
581         # Pair up virtual methods by finding a slot in
582         # the class with the same name
583         for field in maybe_class.fields:
584             if not isinstance(field, Callback):
585                 continue
586             matched = False
587             for method in pair_class.methods:
588                 if (method.name == field.name and
589                     len(method.parameters)+1 == len(field.parameters)):
590                     method.is_virtual = True
591                     break
592
593         gclass_struct = GLibRecord.from_record(class_struct)
594         self._remove_attribute(class_struct.name)
595         self._add_attribute(gclass_struct, True)
596         pair_class.glib_type_struct = gclass_struct
597         gclass_struct.is_gtype_struct_for = name
598
599     # Introspection
600
601     def _introspect_type(self, xmlnode):
602         if xmlnode.tag in ('enum', 'flags'):
603             self._introspect_enum(xmlnode)
604         elif xmlnode.tag == 'class':
605             self._introspect_object(xmlnode)
606         elif xmlnode.tag == 'interface':
607             self._introspect_interface(xmlnode)
608         elif xmlnode.tag == 'boxed':
609             self._introspect_boxed(xmlnode)
610         else:
611             raise ValueError("Unhandled introspection XML tag %s", xmlnode.tag)
612
613     def _introspect_enum(self, node):
614         members = []
615         for member in node.findall('member'):
616             members.append(GLibEnumMember(member.attrib['nick'],
617                                           member.attrib['value'],
618                                           member.attrib['name'],
619                                           member.attrib['nick']))
620
621         klass = (GLibFlags if node.tag == 'flags' else GLibEnum)
622         type_name = node.attrib['name']
623         enum_name = self._transformer.remove_prefix(type_name)
624         node = klass(enum_name, type_name, members, node.attrib['get-type'])
625         self._add_attribute(node, replace=True)
626         self._register_internal_type(type_name, node)
627
628     def _introspect_object(self, xmlnode):
629         type_name = xmlnode.attrib['name']
630         # We handle this specially above; in 2.16 and below there
631         # was no g_object_get_type, for later versions we need
632         # to skip it
633         if type_name == 'GObject':
634             return
635         parent_type_name = xmlnode.attrib['parent']
636         parent_gitype = self._resolve_gtypename(parent_type_name)
637         is_abstract = not not xmlnode.attrib.get('abstract', False)
638         node = GLibObject(
639             self._transformer.remove_prefix(type_name),
640             parent_gitype,
641             type_name,
642             xmlnode.attrib['get-type'], is_abstract)
643         self._introspect_properties(node, xmlnode)
644         self._introspect_signals(node, xmlnode)
645         self._introspect_implemented_interfaces(node, xmlnode)
646
647         # add record fields
648         record = self._get_attribute(node.name)
649         if record is not None:
650             node.fields = record.fields
651             for field in node.fields:
652                 if isinstance(field, Field):
653                     # Object instance fields are assumed to be read-only
654                     # (see also _pair_class_record and transformer.py)
655                     field.writable = False
656
657         self._add_attribute(node, replace=True)
658         self._register_internal_type(type_name, node)
659
660     def _introspect_interface(self, xmlnode):
661         type_name = xmlnode.attrib['name']
662         node = GLibInterface(
663             self._transformer.remove_prefix(type_name),
664             None,
665             type_name, xmlnode.attrib['get-type'])
666         self._introspect_properties(node, xmlnode)
667         self._introspect_signals(node, xmlnode)
668         for child in xmlnode.findall('prerequisite'):
669             name = child.attrib['name']
670             prereq = self._resolve_gtypename(name)
671             node.prerequisites.append(prereq)
672         # GtkFileChooserEmbed is an example of a private interface, we
673         # just filter them out
674         if xmlnode.attrib['get-type'].startswith('_'):
675             print "NOTICE: Marking %s as internal type" % (type_name, )
676             self._private_internal_types[type_name] = node
677         else:
678             self._add_attribute(node, replace=True)
679             self._register_internal_type(type_name, node)
680
681     def _introspect_boxed(self, xmlnode):
682         type_name = xmlnode.attrib['name']
683         # This one doesn't go in the main namespace; we associate it with
684         # the struct or union
685         node = GLibBoxed(type_name, xmlnode.attrib['get-type'])
686         self._boxed_types[node.type_name] = node
687         self._register_internal_type(type_name, node)
688
689     def _introspect_implemented_interfaces(self, node, xmlnode):
690         gt_interfaces = []
691         for interface in xmlnode.findall('implements'):
692             gitype = self._resolve_gtypename(interface.attrib['name'])
693             gt_interfaces.append(gitype)
694         node.interfaces = gt_interfaces
695
696     def _introspect_properties(self, node, xmlnode):
697         for pspec in xmlnode.findall('property'):
698             ctype = pspec.attrib['type']
699             flags = int(pspec.attrib['flags'])
700             readable = (flags & G_PARAM_READABLE) != 0
701             writable = (flags & G_PARAM_WRITABLE) != 0
702             construct = (flags & G_PARAM_CONSTRUCT) != 0
703             construct_only = (flags & G_PARAM_CONSTRUCT_ONLY) != 0
704             node.properties.append(Property(
705                 pspec.attrib['name'],
706                 type_name_from_ctype(ctype),
707                 readable, writable, construct, construct_only,
708                 ctype,
709                 ))
710
711     def _introspect_signals(self, node, xmlnode):
712         for signal_info in xmlnode.findall('signal'):
713             rctype = signal_info.attrib['return']
714             rtype = Type(self._transformer.parse_ctype(rctype), rctype)
715             return_ = Return(rtype, signal_info.attrib['return'])
716             return_.transfer = PARAM_TRANSFER_FULL
717             signal = GLibSignal(signal_info.attrib['name'], return_)
718             for i, parameter in enumerate(signal_info.findall('param')):
719                 if i == 0:
720                     name = 'object'
721                 else:
722                     name = 'p%s' % (i-1, )
723                 pctype = parameter.attrib['type']
724                 ptype = Type(self._transformer.parse_ctype(pctype), pctype)
725                 param = Parameter(name, ptype)
726                 param.transfer = 'none'
727                 signal.parameters.append(param)
728             node.signals.append(signal)
729
730     # Resolver
731
732     def _resolve_type_name(self, type_name, ctype=None):
733         # Workaround glib bug #548689, to be included in 2.18.0
734         if type_name == "GParam":
735             type_name = "GObject.ParamSpec"
736         res = self._transformer.resolve_type_name_full
737         try:
738             return res(type_name, ctype, self._names)
739         except KeyError, e:
740             return self._transformer.resolve_type_name(type_name, ctype)
741
742     def _resolve_param_type(self, ptype, **kwargs):
743         # Workaround glib bug #548689, to be included in 2.18.0
744         if ptype.name == "GParam":
745             ptype.name = "GObject.ParamSpec"
746         return self._transformer.resolve_param_type_full(ptype,
747                                                          self._names,
748                                                          **kwargs)
749
750     def _resolve_node(self, node):
751         if isinstance(node, Function):
752             self._resolve_function_toplevel(node)
753
754         elif isinstance(node, Callback):
755             self._resolve_function(node)
756         elif isinstance(node, GLibObject):
757             self._resolve_glib_object(node)
758         elif isinstance(node, GLibInterface):
759             self._resolve_glib_interface(node)
760         elif isinstance(node, Record):
761             self._resolve_record(node)
762         elif isinstance(node, Union):
763             self._resolve_union(node)
764         elif isinstance(node, Alias):
765             self._resolve_alias(node)
766
767     def _resolve_function_toplevel(self, func):
768         for parser in [self._parse_constructor,
769                        self._parse_method,
770                        self._parse_static_method]:
771             newfunc = parser(func)
772             if newfunc:
773                 self._resolve_function(newfunc)
774                 return
775         self._resolve_function(func)
776
777     def _pair_boxed_type(self, boxed):
778         name = self._transformer.remove_prefix(boxed.type_name)
779         pair_node = self._get_attribute(name)
780         if not pair_node:
781             boxed_item = GLibBoxedOther(name, boxed.type_name,
782                                         boxed.get_type)
783         elif isinstance(pair_node, Record):
784             boxed_item = GLibBoxedStruct(pair_node.name, boxed.type_name,
785                                          boxed.get_type)
786             boxed_item.fields = pair_node.fields
787         elif isinstance(pair_node, Union):
788             boxed_item = GLibBoxedUnion(pair_node.name, boxed.type_name,
789                                          boxed.get_type)
790             boxed_item.fields = pair_node.fields
791         else:
792             return False
793         self._add_attribute(boxed_item, replace=True)
794
795     def _resolve_record(self, node):
796         for field in node.fields:
797             self._resolve_field(field)
798
799     def _resolve_union(self, node):
800         for field in node.fields:
801             self._resolve_field(field)
802
803     def _force_resolve(self, item, allow_unknown=False):
804         if isinstance(item, Unresolved):
805             if item.target in self._private_internal_types:
806                 return None
807             try:
808                 return self._transformer.gtypename_to_giname(item.target,
809                                                              self._names)
810             except KeyError, e:
811                 if allow_unknown:
812                     print "WARNING: Skipping unknown interface %s" % \
813                         (item.target, )
814                     return None
815                 else:
816                     raise
817         if item in self._private_internal_types:
818             return None
819         return item
820
821     def _resolve_glib_interface(self, node):
822         node.parent = self._force_resolve(node.parent)
823         self._resolve_methods(node.methods)
824         self._resolve_properties(node.properties, node)
825         self._resolve_signals(node.signals)
826         node.prerequisites = filter(None,
827             [self._force_resolve(x, allow_unknown=True)
828              for x in node.prerequisites])
829
830     def _resolve_glib_object(self, node):
831         # If we can't find the parent class, just drop back to GObject.
832         # This supports hidden parent classes.
833         # http://bugzilla.gnome.org/show_bug.cgi?id=561360
834         try:
835             node.parent = self._force_resolve(node.parent)
836         except KeyError, e:
837             #print ("WARNING: Parent %r of class %r" +\
838             #       " not found; using GObject") % (node.parent.target,
839             #                                       node.name)
840             node.parent = self._transformer.gtypename_to_giname("GObject",
841                                                                 self._names)
842         node.interfaces = filter(None,
843             [self._force_resolve(x, allow_unknown=True)
844                                     for x in node.interfaces])
845         self._resolve_constructors(node.constructors)
846         self._resolve_methods(node.methods)
847         self._resolve_methods(node.static_methods)
848         self._resolve_properties(node.properties, node)
849         self._resolve_signals(node.signals)
850         for field in node.fields:
851             self._resolve_field(field)
852
853     def _resolve_glib_boxed(self, node):
854         self._resolve_constructors(node.constructors)
855         self._resolve_methods(node.methods)
856
857     def _resolve_constructors(self, constructors):
858         for ctor in constructors:
859             self._resolve_function(ctor)
860
861     def _resolve_methods(self, methods):
862         for method in methods:
863             self._resolve_function(method)
864
865     def _resolve_signals(self, signals):
866         for signal in signals:
867             self._resolve_function(signal)
868
869     def _resolve_properties(self, properties, context):
870         failed = []
871         for prop in properties:
872             try:
873                 self._resolve_property(prop)
874             except KeyError, e:
875                 failed.append(prop)
876         for fail in failed:
877             #print ("WARNING: Deleting object property %r (of %r) "
878             #       "with unknown type") % (fail, context)
879             properties.remove(fail)
880
881     def _resolve_property(self, prop):
882         prop.type = self._resolve_param_type(prop.type, allow_invalid=False)
883
884     def _adjust_throws(self, func):
885         if func.parameters == []:
886             return
887
888         last_param = func.parameters.pop()
889
890         if (last_param.type.name == 'GLib.Error' or
891             (self._namespace_name == 'GLib' and
892              last_param.type.name == 'Error')):
893             func.throws = True
894         else:
895             func.parameters.append(last_param)
896
897     def _resolve_function(self, func):
898         self._resolve_parameters(func.parameters)
899         func.retval.type = self._resolve_param_type(func.retval.type)
900         self._adjust_throws(func)
901
902     def _resolve_parameters(self, parameters):
903         for parameter in parameters:
904             parameter.type = self._resolve_param_type(parameter.type)
905
906     def _resolve_field(self, field):
907         if isinstance(field, Callback):
908             self._resolve_function(field)
909         elif isinstance(field, Record): # non-typedef'd struct
910             self._resolve_record(field)
911         elif isinstance(field, Union): # non-typedef'd union
912             self._resolve_union(field)
913         else:
914             field.type = self._resolve_param_type(field.type)
915
916     def _resolve_alias(self, alias):
917         alias.target = self._resolve_type_name(alias.target, alias.target)
918
919     # Validation
920
921     def _validate(self, nodes):
922         nodes = list(self._names.names.itervalues())
923         i = 0
924         self._validating = True
925         while True:
926             initlen = len(nodes)
927
928             #print "Type resolution; pass=%d" % (i, )
929             nodes = list(self._names.names.itervalues())
930             for node in nodes:
931                 try:
932                     self._resolve_node(node)
933                 except UnknownTypeError, e:
934                     print "WARNING: %s: Deleting %r" % (e, node)
935                     self._remove_attribute(node.name)
936             if len(nodes) == initlen:
937                 break
938             i += 1
939             self._print_statistics()
940         self._validating = False