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