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