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