1c95d19bed29a9f2f599f05a183fae0fde37c0a1
[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, 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             func.is_method = True
446         else:
447             klass.constructors.append(func)
448         return func
449
450     def _parse_record(self, recurd):
451         # This is a hack, but GObject is a rather fundamental piece so.
452         internal_names = ["Object", 'InitiallyUnowned']
453         g_internal_names = ["G" + x for x in internal_names]
454         if (self._namespace_name == 'GObject' and
455             recurd.name in internal_names):
456             self._create_gobject(recurd)
457             return
458         elif recurd.name in g_internal_names:
459             # Avoid duplicates
460             return
461         node = self._names.names.get(recurd.name)
462         if node is None:
463             self._add_attribute(recurd, replace=True)
464             return
465         (ns, node) = node
466         node.fields = recurd.fields[:]
467
468     def _parse_union(self, union):
469         node = self._names.names.get(union.name)
470         if node is None:
471             self._add_attribute(union, replace=True)
472             return
473         (ns, node) = node
474         node.fields = union.fields[:]
475
476     def _parse_callback(self, callback):
477         self._add_attribute(callback)
478
479     def _strip_class_suffix(self, name):
480         if (name.endswith('Class') or
481             name.endswith('Iface')):
482             return name[:-5]
483         elif name.endswith('Interface'):
484             return name[:-9]
485         else:
486             return name
487
488     def _arg_is_failed(self, param):
489         ctype = self._transformer.ctype_of(param).replace('*', '')
490         uscored = to_underscores(self._strip_class_suffix(ctype)).lower()
491         if uscored in self._failed_types:
492             print "Warning: failed type: %r" % (param, )
493             return True
494         return False
495
496     def _pair_class_record(self, maybe_class):
497         name = self._strip_class_suffix(maybe_class.name)
498         if name == maybe_class.name:
499             return
500
501         if self._arg_is_failed(maybe_class):
502             print "WARNING: deleting no-type %r" % (maybe_class.name, )
503             del self._names.names[maybe_class.name]
504             return
505
506         # Object class fields are assumed to be read-only
507         # (see also _introspect_object and transformer.py)
508         for field in maybe_class.fields:
509             if isinstance(field, Field):
510                 field.writable = False
511
512         name = self._resolve_type_name(name)
513         resolved = self._transformer.remove_prefix(name)
514         pair_class = self._get_attribute(resolved)
515         if pair_class and isinstance(pair_class, 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 recurd fields
575         recurd = self._get_attribute(node.name)
576         if recurd is not None:
577             node.fields = recurd.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_record 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 = PARAM_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, Record):
688             self._resolve_record(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         for parser in [self._parse_constructor,
696                        self._parse_method,
697                        self._parse_static_method]:
698             newfunc = parser(func)
699             if newfunc:
700                 self._resolve_function(newfunc)
701                 return
702         self._resolve_function(func)
703
704     def _pair_boxed_type(self, boxed):
705         name = self._transformer.remove_prefix(boxed.type_name)
706         pair_node = self._get_attribute(name)
707         if not pair_node:
708             boxed_item = GLibBoxedOther(name, boxed.type_name,
709                                         boxed.get_type)
710         elif isinstance(pair_node, Record):
711             boxed_item = GLibBoxedStruct(pair_node.name, boxed.type_name,
712                                          boxed.get_type)
713             boxed_item.fields = pair_node.fields
714         elif isinstance(pair_node, Union):
715             boxed_item = GLibBoxedUnion(pair_node.name, boxed.type_name,
716                                          boxed.get_type)
717             boxed_item.fields = pair_node.fields
718         else:
719             return False
720         self._add_attribute(boxed_item, replace=True)
721
722     def _resolve_record(self, node):
723         for field in node.fields:
724             self._resolve_field(field)
725
726     def _resolve_union(self, node):
727         for field in node.fields:
728             self._resolve_field(field)
729
730     def _force_resolve(self, item, allow_unknown=False):
731         if isinstance(item, Unresolved):
732             if item.target in self._private_internal_types:
733                 return None
734             try:
735                 return self._transformer.gtypename_to_giname(item.target,
736                                                              self._names)
737             except KeyError, e:
738                 if allow_unknown:
739                     print "WARNING: Skipping unknown interface %s" % \
740                         (item.target, )
741                     return None
742                 else:
743                     raise
744         if item in self._private_internal_types:
745             return None
746         return item
747
748     def _resolve_glib_interface(self, node):
749         node.parent = self._force_resolve(node.parent)
750         self._resolve_methods(node.methods)
751         self._resolve_properties(node.properties, node)
752         self._resolve_signals(node.signals)
753         node.prerequisites = filter(None,
754             [self._force_resolve(x, allow_unknown=True)
755              for x in node.prerequisites])
756
757     def _resolve_glib_object(self, node):
758         node.parent = self._force_resolve(node.parent)
759         node.interfaces = filter(None,
760             [self._force_resolve(x, allow_unknown=True)
761                                     for x in node.interfaces])
762         self._resolve_constructors(node.constructors)
763         self._resolve_methods(node.methods)
764         self._resolve_methods(node.static_methods)
765         self._resolve_properties(node.properties, node)
766         self._resolve_signals(node.signals)
767         for field in node.fields:
768             self._resolve_field(field)
769
770     def _resolve_glib_boxed(self, node):
771         self._resolve_constructors(node.constructors)
772         self._resolve_methods(node.methods)
773
774     def _resolve_constructors(self, constructors):
775         for ctor in constructors:
776             self._resolve_function(ctor)
777
778     def _resolve_methods(self, methods):
779         for method in methods:
780             self._resolve_function(method)
781
782     def _resolve_signals(self, signals):
783         for signal in signals:
784             self._resolve_function(signal)
785
786     def _resolve_properties(self, properties, context):
787         failed = []
788         for prop in properties:
789             try:
790                 self._resolve_property(prop)
791             except KeyError, e:
792                 failed.append(prop)
793         for fail in failed:
794             print ("WARNING: Deleting object property %r (of %r) "
795                    "with unknown type") % (fail, context)
796             properties.remove(fail)
797
798     def _resolve_property(self, prop):
799         prop.type = self._resolve_param_type(prop.type, allow_invalid=False)
800
801     def _adjust_throws(self, func):
802         if func.parameters == []:
803             return
804
805         last_param = func.parameters.pop()
806
807         if (last_param.type.name == 'GLib.Error' or
808             (self._namespace_name == 'GLib' and
809              last_param.type.name == 'Error')):
810             func.throws = True
811         else:
812             func.parameters.append(last_param)
813
814     def _resolve_function(self, func):
815         self._resolve_parameters(func.parameters)
816         func.retval.type = self._resolve_param_type(func.retval.type)
817         self._adjust_throws(func)
818
819     def _resolve_parameters(self, parameters):
820         for parameter in parameters:
821             parameter.type = self._resolve_param_type(parameter.type)
822
823     def _resolve_field(self, field):
824         if isinstance(field, Callback):
825             self._resolve_function(field)
826             return
827         field.type = self._resolve_param_type(field.type)
828
829     def _resolve_alias(self, alias):
830         alias.target = self._resolve_type_name(alias.target, alias.target)
831
832     # Validation
833
834     def _validate(self, nodes):
835         nodes = list(self._names.names.itervalues())
836         i = 0
837         self._validating = True
838         while True:
839             initlen = len(nodes)
840
841             print "Type resolution; pass=%d" % (i, )
842             nodes = list(self._names.names.itervalues())
843             for node in nodes:
844                 try:
845                     self._resolve_node(node)
846                 except UnknownTypeError, e:
847                     print "WARNING: %s: Deleting %r" % (e, node)
848                     self._remove_attribute(node.name)
849             if len(nodes) == initlen:
850                 break
851             i += 1
852             self._print_statistics()
853         self._validating = False