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