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