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