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