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