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