[scanner] Move over remaining callsites to message
[platform/upstream/gobject-introspection.git] / giscanner / maintransformer.py
1 # -*- Mode: Python -*-
2 # Copyright (C) 2010 Red Hat, Inc.
3 #
4 # This library is free software; you can redistribute it and/or
5 # modify it under the terms of the GNU Lesser General Public
6 # License as published by the Free Software Foundation; either
7 # version 2 of the License, or (at your option) any later version.
8 #
9 # This library is distributed in the hope that it will be useful,
10 # but WITHOUT ANY WARRANTY; without even the implied warranty of
11 # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
12 # Lesser General Public License for more details.
13 #
14 # You should have received a copy of the GNU Lesser General Public
15 # License along with this library; if not, write to the
16 # Free Software Foundation, Inc., 59 Temple Place - Suite 330,
17 # Boston, MA 02111-1307, USA.
18 #
19
20 import re
21
22 from . import ast
23 from . import glibast
24 from . import message
25 from .annotationparser import (TAG_VFUNC, TAG_SINCE, TAG_DEPRECATED, TAG_RETURNS,
26                                TAG_ATTRIBUTES, TAG_RENAME_TO, TAG_TYPE, TAG_TRANSFER,
27                                TAG_UNREF_FUNC, TAG_REF_FUNC, TAG_SET_VALUE_FUNC,
28                                TAG_GET_VALUE_FUNC)
29 from .annotationparser import (OPT_ALLOW_NONE,
30                                OPT_ARRAY, OPT_ELEMENT_TYPE, OPT_IN, OPT_INOUT,
31                                OPT_INOUT_ALT, OPT_OUT, OPT_SCOPE,
32                                OPT_TYPE, OPT_CLOSURE, OPT_DESTROY, OPT_SKIP,
33                                OPT_FOREIGN, OPT_ARRAY_FIXED_SIZE,
34                                OPT_ARRAY_LENGTH, OPT_ARRAY_ZERO_TERMINATED)
35 from .annotationparser import AnnotationParser
36 from .transformer import TransformerException
37 from .utils import to_underscores, to_underscores_noprefix
38
39 class MainTransformer(object):
40
41     def __init__(self, transformer, blocks):
42         self._transformer = transformer
43         self._blocks = blocks
44         self._namespace = transformer.namespace
45         self._uscore_type_names = {}
46
47     # Public API
48
49     def transform(self):
50         # Dirty hack for now...maybe eventually we'll support the "typedef GSList FooSet"
51         # pattern.
52         if self._namespace.name == 'Atk':
53             attribute = self._namespace.get('Attribute')
54             attributeset = self._namespace.get('AttributeSet')
55             if attribute and attributeset:
56                 alias = ast.Alias('AttributeSet', target=ast.TYPE_ANY)
57                 self._namespace.append(alias, replace=True)
58
59         # We have a rough tree which should have most of of the types
60         # we know about.  Let's attempt closure; walk over all of the
61         # Type() types and see if they match up with something.
62         self._namespace.walk(self._pass_type_resolution)
63
64         # Determine some default values for transfer etc.
65         # based on the current tree.
66         self._namespace.walk(self._pass_callable_defaults)
67
68         # Read in most annotations now.
69         self._namespace.walk(self._pass_read_annotations)
70
71         # Now that we've possibly seen more types from annotations,
72         # do another type resolution pass.
73         self._namespace.walk(self._pass_type_resolution)
74
75         # Generate a reverse mapping "bar_baz" -> BarBaz
76         for node in self._namespace.itervalues():
77             if isinstance(node, (ast.Class, ast.Interface, glibast.GLibBoxed)):
78                 self._uscore_type_names[node.c_symbol_prefix] = node
79             elif isinstance(node, (ast.Record, ast.Union)):
80                 uscored = to_underscores_noprefix(node.name).lower()
81                 self._uscore_type_names[uscored] = node
82
83         for node in list(self._namespace.itervalues()):
84             if isinstance(node, ast.Function):
85                 # Discover which toplevel functions are actually methods
86                 self._pair_function(node)
87             if isinstance(node, (ast.Class, ast.Interface)):
88                 self._pair_class_virtuals(node)
89
90         # Some annotations need to be post function pairing
91         self._namespace.walk(self._pass_read_annotations2)
92
93         # Another type resolution pass after we've parsed virtuals, etc.
94         self._namespace.walk(self._pass_type_resolution)
95
96         self._namespace.walk(self._pass3)
97
98         # TODO - merge into pass3
99         self._resolve_quarks()
100
101     # Private
102
103     def _get_validate_parameter_name(self, parent, param_name, origin):
104         try:
105             param = parent.get_parameter(param_name)
106         except ValueError, e:
107             param = None
108         if param is None:
109             if isinstance(origin, ast.Parameter):
110                 origin_name = 'parameter %s' % (origin.argname, )
111             else:
112                 origin_name = 'return value'
113             message.log_node(
114                 message.FATAL, parent,
115                 "can't find parameter %s referenced by %s of %r"
116                 % (param_name, origin_name, parent.name))
117
118         return param.argname
119
120     def _apply_annotation_rename_to(self, node, chain, block):
121         if not block:
122             return
123         rename_to = block.get(TAG_RENAME_TO)
124         if not rename_to:
125             return
126         rename_to = rename_to.value
127         target = self._namespace.get_by_symbol(rename_to)
128         if not target:
129             message.warn_node(node,
130                 "Can't find symbol %r referenced by Rename annotation" % (
131                 rename_to, ))
132         elif target.shadowed_by:
133             message.warn_node(node,
134                 "Function %r already shadowed by %r, can't overwrite with %r" % (
135                 target.symbol,
136                 target.shadowed_by,
137                 rename_to))
138         elif target.shadows:
139             message.warn_node(node,
140                 "Function %r already shadows %r, can't multiply shadow with %r" % (
141                 target.symbol,
142                 target.shadows,
143                 rename_to))
144         else:
145             target.shadows = node.symbol
146             node.shadowed_by = target.symbol
147
148     def _apply_annotations_function(self, node, chain):
149         block = self._blocks.get(node.symbol)
150         self._apply_annotations_callable(node, chain, block)
151         self._apply_annotation_rename_to(node, chain, block)
152
153     def _pass_callable_defaults(self, node, chain):
154         if isinstance(node, (ast.Callable, glibast.GLibSignal)):
155             for param in node.parameters:
156                 if param.transfer is None:
157                     param.transfer = self._get_transfer_default(node, param)
158             if node.retval.transfer is None:
159                 node.retval.transfer = self._get_transfer_default(node, node.retval)
160         return True
161
162     def _pass_read_annotations(self, node, chain):
163         if not node.namespace:
164             return False
165         if isinstance(node, ast.Function):
166             self._apply_annotations_function(node, chain)
167         if isinstance(node, ast.Callback):
168             block = self._blocks.get(node.c_name)
169             self._apply_annotations_callable(node, chain, block)
170         if isinstance(node, (ast.Class, ast.Interface, ast.Record,
171                              ast.Union, ast.Enum, ast.Bitfield,
172                              ast.Callback)):
173             block = self._blocks.get(node.c_name)
174             self._apply_annotations_annotated(node, block)
175         if isinstance(node, (ast.Class, ast.Interface, ast.Record, ast.Union)):
176             for field in node.fields:
177                 self._blocks.get('%s::%s' % (node.c_name, field.name))
178                 self._apply_annotations_field(node, block, field)
179         if isinstance(node, (ast.Class, ast.Interface)):
180             for prop in node.properties:
181                 self._apply_annotations_property(node, prop)
182             for sig in node.signals:
183                 self._apply_annotations_signal(node, sig)
184         if isinstance(node, ast.Class):
185             block = self._blocks.get(node.c_name)
186             if block:
187                 tag = block.get(TAG_UNREF_FUNC)
188                 node.unref_func = tag.value if tag else None
189                 tag = block.get(TAG_REF_FUNC)
190                 node.ref_func = tag.value if tag else None
191                 tag = block.get(TAG_SET_VALUE_FUNC)
192                 node.set_value_func = tag.value if tag else None
193                 tag = block.get(TAG_GET_VALUE_FUNC)
194                 node.get_value_func = tag.value if tag else None
195         return True
196
197     def _adjust_container_type(self, parent, node, options):
198         has_element_type = OPT_ELEMENT_TYPE in options
199         has_array = OPT_ARRAY in options
200
201         if has_array:
202             self._apply_annotations_array(parent, node, options)
203         elif has_element_type:
204             self._apply_annotations_element_type(parent, node, options)
205
206     def _resolve(self, type_str, orig_node=None):
207         def grab_one(type_str, resolver, top_combiner, combiner):
208             """Return a complete type, and the trailing string part after it.
209             Use resolver() on each identifier, and combiner() on the parts of
210             each complete type. (top_combiner is used on the top-most type.)"""
211             bits = re.split(r'([,<>])', type_str, 1)
212             first, sep, rest = [bits[0], '', ''] if (len(bits)==1) else bits
213             args = [resolver(first)]
214             if sep == '<':
215                 while sep != '>':
216                     next, rest = grab_one(rest, resolver, combiner, combiner)
217                     args.append(next)
218                     sep, rest = rest[0], rest[1:]
219             else:
220                 rest = sep + rest
221             return top_combiner(*args), rest
222         def resolver(ident):
223             res = self._transformer.create_type_from_user_string(ident)
224             return res
225         def combiner(base, *rest):
226             if not rest:
227                 return base
228             if isinstance(base, ast.List) and len(rest) == 1:
229                 return ast.List(base.name, *rest)
230             if isinstance(base, ast.Map) and len(rest) == 2:
231                 return ast.Map(*rest)
232             message.warn(
233                 "Too many parameters in type specification %r" % (type_str, ))
234             return base
235         def top_combiner(base, *rest):
236             if orig_node is not None:
237                 base.is_const = orig_node.is_const
238             return combiner(base, *rest)
239
240         result, rest = grab_one(type_str, resolver, top_combiner, combiner)
241         if rest:
242             message.warn("Trailing components in type specification %r" % (
243                 type_str, ))
244         return result
245
246     def _apply_annotations_array(self, parent, node, options):
247         array_opt = options.get(OPT_ARRAY)
248         if array_opt:
249             array_values = array_opt.all()
250         else:
251             array_values = {}
252
253         element_type = options.get(OPT_ELEMENT_TYPE)
254         if element_type is not None:
255             element_type_node = self._resolve(element_type.one())
256         elif isinstance(node.type, ast.Array):
257             element_type_node = node.type.element_type
258         else:
259             # We're assuming here that Foo* with an (array) annotation
260             # and no (element-type) means array of Foo
261             element_type_node = node.type.clone()
262             # Explicitly erase ctype since it's no longer valid
263             element_type_node.ctype = None
264
265         if isinstance(node.type, ast.Array):
266             array_type = node.type.array_type
267         else:
268             array_type = None
269         container_type = ast.Array(array_type, element_type_node,
270                                ctype=node.type.ctype,
271                                is_const=node.type.is_const)
272         if OPT_ARRAY_ZERO_TERMINATED in array_values:
273             container_type.zeroterminated = array_values.get(
274                 OPT_ARRAY_ZERO_TERMINATED) == '1'
275         length = array_values.get(OPT_ARRAY_LENGTH)
276         if length is not None:
277             paramname = self._get_validate_parameter_name(parent, length, node)
278             if paramname:
279                 param = parent.get_parameter(paramname)
280                 param.direction = node.direction
281                 if param.direction == ast.PARAM_DIRECTION_OUT:
282                     param.transfer = ast.PARAM_TRANSFER_FULL
283                 container_type.length_param_name = param.argname
284         fixed = array_values.get(OPT_ARRAY_FIXED_SIZE)
285         if fixed:
286             container_type.size = int(fixed)
287         node.type = container_type
288
289     def _apply_annotations_element_type(self, parent, node, options):
290         element_type_opt = options.get(OPT_ELEMENT_TYPE)
291         element_type = element_type_opt.flat()
292         if isinstance(node.type, ast.List):
293             assert len(element_type) == 1
294             node.type.element_type = self._resolve(element_type[0])
295         elif isinstance(node.type, ast.Map):
296             assert len(element_type) == 2
297             node.type.key_type = self._resolve(element_type[0])
298             node.type.value_type = self._resolve(element_type[1])
299         elif isinstance(node.type, ast.Array):
300             node.type.element_type = self._resolve(element_type[0])
301         else:
302             message.warn_node(parent,
303                 "Unknown container %r for element-type annotation" % (node.type, ))
304
305     def _get_transfer_default_param(self, parent, node):
306         if node.direction in [ast.PARAM_DIRECTION_INOUT,
307                               ast.PARAM_DIRECTION_OUT]:
308             if node.caller_allocates:
309                 return ast.PARAM_TRANSFER_NONE
310             return ast.PARAM_TRANSFER_FULL
311         return ast.PARAM_TRANSFER_NONE
312
313     def _get_transfer_default_returntype_basic(self, typeval):
314         if (typeval.is_equiv(ast.BASIC_GIR_TYPES)
315             or typeval.is_const
316             or typeval.is_equiv(ast.TYPE_NONE)):
317             return ast.PARAM_TRANSFER_NONE
318         elif typeval.is_equiv(ast.TYPE_STRING):
319             # Non-const strings default to FULL
320             return ast.PARAM_TRANSFER_FULL
321         elif typeval.target_fundamental:
322             # This looks like just GType right now
323             return None
324         return None
325
326     def _is_gi_subclass(self, typeval, supercls_type):
327         cls = self._transformer.lookup_typenode(typeval)
328         assert cls, str(typeval)
329         supercls = self._transformer.lookup_typenode(supercls_type)
330         assert supercls
331         if cls is supercls:
332             return True
333         if cls.parent and cls.parent.target_giname != 'GObject.Object':
334             return self._is_gi_subclass(cls.parent, supercls_type)
335         return False
336
337     def _get_transfer_default_return(self, parent, node):
338         typeval = node.type
339         basic = self._get_transfer_default_returntype_basic(typeval)
340         if basic:
341             return basic
342         if not typeval.target_giname:
343             return None
344         target = self._transformer.lookup_typenode(typeval)
345         if isinstance(target, ast.Alias):
346             return self._get_transfer_default_returntype_basic(target.target)
347         elif isinstance(target, glibast.GLibBoxed):
348             return ast.PARAM_TRANSFER_FULL
349         elif isinstance(target, (ast.Enum, ast.Bitfield)):
350             return ast.PARAM_TRANSFER_NONE
351         # Handle constructors specially here
352         elif isinstance(parent, ast.Function) and parent.is_constructor:
353             if isinstance(target, ast.Class):
354                 initially_unowned_type = ast.Type(target_giname='GObject.InitiallyUnowned')
355                 initially_unowned = self._transformer.lookup_typenode(initially_unowned_type)
356                 if initially_unowned and self._is_gi_subclass(typeval, initially_unowned_type):
357                     return ast.PARAM_TRANSFER_NONE
358                 else:
359                     return ast.PARAM_TRANSFER_FULL
360             elif isinstance(target, (ast.Record, ast.Union)):
361                 return ast.PARAM_TRANSFER_FULL
362             else:
363                 assert False, "Invalid constructor"
364         elif isinstance(target, (ast.Class, ast.Record, ast.Union)):
365             # Explicitly no default for these
366             return None
367         else:
368             return None
369
370     def _get_transfer_default(self, parent, node):
371         if node.type.is_equiv(ast.TYPE_NONE) or isinstance(node.type, ast.Varargs):
372             return ast.PARAM_TRANSFER_NONE
373         elif isinstance(node, ast.Parameter):
374             return self._get_transfer_default_param(parent, node)
375         elif isinstance(node, ast.Return):
376             return self._get_transfer_default_return(parent, node)
377         elif isinstance(node, ast.Field):
378             return ast.PARAM_TRANSFER_NONE
379         elif isinstance(node, ast.Property):
380             return ast.PARAM_TRANSFER_NONE
381         else:
382             raise AssertionError(node)
383
384     def _apply_annotations_param_ret_common(self, parent, node, tag):
385         options = getattr(tag, 'options', {})
386
387         param_type = options.get(OPT_TYPE)
388         if param_type:
389             node.type = self._resolve(param_type.one(), node.type)
390
391         caller_allocates = False
392         annotated_direction = None
393         if (OPT_INOUT in options or
394             OPT_INOUT_ALT in options):
395             annotated_direction = ast.PARAM_DIRECTION_INOUT
396         elif OPT_OUT in options:
397             subtype = options[OPT_OUT]
398             if subtype is not None:
399                 subtype = subtype.one()
400             annotated_direction = ast.PARAM_DIRECTION_OUT
401             if subtype in (None, ''):
402                 if node.type.target_giname and node.type.ctype:
403                     caller_allocates = '**' not in node.type.ctype
404                 else:
405                     caller_allocates = False
406             elif subtype == 'caller-allocates':
407                 caller_allocates = True
408             elif subtype == 'callee-allocates':
409                 caller_allocates = False
410             else:
411                 message.fatal("out allocation for %s is invalid (%r)" % (
412                     node, subtype))
413         elif OPT_IN in options:
414             annotated_direction = ast.PARAM_DIRECTION_IN
415
416         if (annotated_direction is not None) and (annotated_direction != node.direction):
417             node.direction = annotated_direction
418             node.caller_allocates = caller_allocates
419             # Also reset the transfer default if we're toggling direction
420             node.transfer = self._get_transfer_default(parent, node)
421
422         transfer_tag = options.get(TAG_TRANSFER)
423         if transfer_tag:
424             node.transfer = transfer_tag.one()
425
426         self._adjust_container_type(parent, node, options)
427
428         if (OPT_ALLOW_NONE in options or
429             node.type.target_giname == 'Gio.Cancellable'):
430             node.allow_none = True
431
432         if tag is not None and tag.comment is not None:
433             node.doc = tag.comment
434
435         for key in options:
436             if '.' in key:
437                 value = options.get(key)
438                 if value:
439                     node.attributes.append((key, value.one()))
440
441     def _apply_annotations_annotated(self, node, block):
442         if block is None:
443             return
444
445         node.doc = block.comment
446
447         since_tag = block.get(TAG_SINCE)
448         if since_tag is not None:
449             node.version = since_tag.value
450
451         deprecated_tag = block.get(TAG_DEPRECATED)
452         if deprecated_tag is not None:
453             value = deprecated_tag.value
454             if ': ' in value:
455                 version, desc = value.split(': ')
456             else:
457                 desc = value
458                 version = None
459             node.deprecated = desc
460             if version is not None:
461                 node.deprecated_version = version
462
463         annos_tag = block.get(TAG_ATTRIBUTES)
464         if annos_tag is not None:
465             options = AnnotationParser.parse_options(annos_tag.value)
466             for key, value in options.iteritems():
467                 if value:
468                     node.attributes.append((key, value.one()))
469
470         if OPT_SKIP in block.options:
471             node.skip = True
472
473         if OPT_FOREIGN in block.options:
474             node.foreign = True
475
476     def _apply_annotations_param(self, parent, param, tag):
477         if tag:
478             options = tag.options
479         else:
480             options = {}
481         if isinstance(parent, ast.Function):
482             scope = options.get(OPT_SCOPE)
483             if scope:
484                 scope = scope.one()
485                 if scope not in [ast.PARAM_SCOPE_CALL,
486                                  ast.PARAM_SCOPE_ASYNC,
487                                  ast.PARAM_SCOPE_NOTIFIED]:
488                     message.warn(
489                         parent,
490                         "Invalid scope %r for parameter %r" % (scope, param.name))
491                 else:
492                     param.scope = scope
493                     param.transfer = ast.PARAM_TRANSFER_NONE
494
495             destroy = options.get(OPT_DESTROY)
496             if destroy:
497                 param.destroy_name = self._get_validate_parameter_name(parent,
498                                                                        destroy.one(),
499                                                                        param)
500                 if param.destroy_name is not None:
501                     param.scope = ast.PARAM_SCOPE_NOTIFIED
502                     destroy_param = parent.get_parameter(param.destroy_name)
503                     # This is technically bogus; we're setting the scope on the destroy
504                     # itself.  But this helps avoid tripping a warning from finaltransformer,
505                     # since we don't have a way right now to flag this callback a destroy.
506                     destroy_param.scope = ast.PARAM_SCOPE_NOTIFIED
507             closure = options.get(OPT_CLOSURE)
508             if closure:
509                 param.closure_name = self._get_validate_parameter_name(parent,
510                                                                        closure.one(),
511                                                                        param)
512         elif isinstance(parent, ast.Callback):
513             if OPT_CLOSURE in options:
514                 # For callbacks, (closure) appears without an
515                 # argument, and tags a parameter that is a closure. We
516                 # represent it (weirdly) in the gir and typelib by
517                 # setting param.closure_name to itself.
518                 param.closure_name = param.argname
519
520         self._apply_annotations_param_ret_common(parent, param, tag)
521
522     def _apply_annotations_return(self, parent, return_, block):
523         if block:
524             tag = block.get(TAG_RETURNS)
525         else:
526             tag = None
527         self._apply_annotations_param_ret_common(parent, return_, tag)
528
529     def _apply_annotations_params(self, parent, params, block):
530         for param in params:
531             if block:
532                 tag = block.get(param.argname)
533             else:
534                 tag = None
535             self._apply_annotations_param(parent, param, tag)
536
537     def _apply_annotations_callable(self, node, chain, block):
538         self._apply_annotations_annotated(node, block)
539         self._apply_annotations_params(node, node.parameters, block)
540         self._apply_annotations_return(node, node.retval, block)
541
542     def _check_arg_annotations(self, parent, params, block):
543         if block is None:
544             return
545         for tag in block.tags.keys():
546             if tag == TAG_RETURNS:
547                 continue
548             for param in params:
549                 if param.argname == tag:
550                     break
551             else:
552                 message.warn(
553                     "Annotation for '%s' refers to unknown argument '%s'"
554                     % (parent.name, tag))
555
556     def _apply_annotations_field(self, parent, block, field):
557         if not block:
558             return
559         tag = block.get(field.name)
560         if not tag:
561             return
562         t = tag.options.get('type')
563         if not t:
564             return
565         field.type = self._transformer.create_type_from_user_string(t.one())
566
567     def _apply_annotations_property(self, parent, prop):
568         block = self._blocks.get('%s:%s' % (parent.type_name, prop.name))
569         self._apply_annotations_annotated(prop, block)
570         if not block:
571             return
572         transfer_tag = block.get(TAG_TRANSFER)
573         if transfer_tag is not None:
574             prop.transfer = transfer_tag.value
575         else:
576             prop.transfer = self._get_transfer_default(parent, prop)
577         type_tag = block.get(TAG_TYPE)
578         if type_tag:
579             prop.type = self._resolve(type_tag.value, prop.type)
580
581     def _apply_annotations_signal(self, parent, signal):
582         block = self._blocks.get('%s::%s' % (parent.type_name, signal.name))
583         self._apply_annotations_annotated(signal, block)
584         # We're only attempting to name the signal parameters if
585         # the number of parameter tags (@foo) is the same or greater
586         # than the number of signal parameters
587         if block and len(block.tags) > len(signal.parameters):
588             names = block.tags.items()
589         else:
590             names = []
591         for i, param in enumerate(signal.parameters):
592             if names:
593                 name, tag = names[i+1]
594                 param.name = name
595                 options = getattr(tag, 'options', {})
596                 param_type = options.get(OPT_TYPE)
597                 if param_type:
598                     param.type = self._resolve(param_type.one(), param.type)
599             else:
600                 tag = None
601             self._apply_annotations_param(signal, param, tag)
602         self._apply_annotations_return(signal, signal.retval, block)
603
604     def _pass_read_annotations2(self, node, chain):
605         if isinstance(node, ast.Function):
606             self._apply_annotations2_function(node, chain)
607         return True
608
609     def _apply_annotations2_function(self, node, chain):
610         # Handle virtual invokers
611         parent = chain[-1] if chain else None
612         block = self._blocks.get(node.symbol)
613         if not (block and parent):
614             return
615         virtual = block.get(TAG_VFUNC)
616         if not virtual:
617             return
618         invoker_name = virtual.value
619         matched = False
620         for vfunc in parent.virtual_methods:
621             if vfunc.name == invoker_name:
622                 matched = True
623                 vfunc.invoker = node.name
624                 # Also merge in annotations
625                 self._apply_annotations_callable(vfunc, [parent], block)
626                 break
627         if not matched:
628             message.warn_symbol(node.symbol,
629                 "Virtual slot %r not found for %r annotation" % (invoker_name, TAG_VFUNC))
630
631     def _resolve_and_filter_type_list(self, typelist):
632         """Given a list of Type instances, return a new list of types with
633 the ones that failed to resolve removed."""
634         # Create a copy we'll modify
635         new_typelist = list(typelist)
636         for typeval in typelist:
637             resolved = self._transformer.resolve_type(typeval)
638             if not resolved:
639                 new_typelist.remove(typeval)
640         return new_typelist
641
642     def _pass_type_resolution(self, node, chain):
643         if isinstance(node, ast.Alias):
644             self._transformer.resolve_type(node.target)
645         if isinstance(node, ast.Callable):
646             for parameter in node.parameters:
647                 self._transformer.resolve_type(parameter.type)
648             self._transformer.resolve_type(node.retval.type)
649         if isinstance(node, ast.Constant):
650             self._transformer.resolve_type(node.value_type)
651         if isinstance(node, (ast.Class, ast.Interface, ast.Record, ast.Union)):
652             for field in node.fields:
653                 if field.anonymous_node:
654                     pass
655                 else:
656                     self._transformer.resolve_type(field.type)
657         if isinstance(node, (ast.Class, ast.Interface)):
658             resolved_parent = None
659             for parent in node.parent_chain:
660                 try:
661                     self._transformer.resolve_type(parent)
662                 except ValueError, e:
663                     continue
664                 target = self._transformer.lookup_typenode(parent)
665                 if target:
666                     node.parent = parent
667                     break
668             else:
669                 if isinstance(node, ast.Interface) or not node.fundamental:
670                     node.parent = ast.Type(target_giname='GObject.Object')
671                 else:
672                     node.parent = None
673             for prop in node.properties:
674                 self._transformer.resolve_type(prop.type)
675             for sig in node.signals:
676                 for param in sig.parameters:
677                     self._transformer.resolve_type(param.type)
678         if isinstance(node, ast.Class):
679             node.interfaces = self._resolve_and_filter_type_list(node.interfaces)
680         if isinstance(node, ast.Interface):
681             node.prerequisites = self._resolve_and_filter_type_list(node.prerequisites)
682         return True
683
684     def _resolve_quarks(self):
685         # self._uscore_type_names is an authoritative mapping of types
686         # to underscored versions, since it is based on get_type() methods;
687         # but only covers enums that are registered as GObject enums.
688         # Create a fallback mapping based on all known enums in this module.
689         uscore_enums = {}
690         for enum in self._namespace.itervalues():
691             if not isinstance(enum, ast.Enum):
692                 continue
693             type_name = enum.symbol
694             uscored = to_underscores(type_name).lower()
695
696             uscore_enums[uscored] = enum
697
698             try:
699                 no_uscore_prefixed = self._transformer.strip_identifier(type_name)
700             except TransformerException, e:
701                 message.warn(e)
702                 no_uscore_prefixed = None
703
704             if no_uscore_prefixed not in uscore_enums:
705                 uscore_enums[no_uscore_prefixed] = enum
706
707         for node in self._namespace.itervalues():
708             if not isinstance(node, ast.Function):
709                 continue
710             if node.retval.type.target_giname != 'GLib.Quark':
711                 continue
712             short = node.symbol[:-len('_quark')]
713             if short == "g_io_error":
714                 # Special case; GIOError was already taken forcing GIOErrorEnum
715                 assert self._namespace.name == 'Gio'
716                 enum = self._namespace.get('IOErrorEnum')
717             else:
718                 enum = self._uscore_type_names.get(short)
719                 if enum is None:
720                     enum = uscore_enums.get(short)
721             if enum is not None:
722                 enum.error_quark = node.symbol
723             else:
724                 message.warn_node(node,
725                     """%s: Couldn't find corresponding enumeration""" % (node.symbol, ))
726
727     def _split_uscored_by_type(self, uscored):
728         """'uscored' should be an un-prefixed uscore string.  This
729 function searches through the namespace for the longest type which
730 prefixes uscored, and returns (type, suffix).  Example, assuming
731 namespace Gtk, type is TextBuffer:
732
733 _split_uscored_by_type(text_buffer_try_new) -> (ast.Class(TextBuffer), 'try_new')"""
734         node = None
735         count = 0
736         prev_split_count = -1
737         while True:
738             components = uscored.rsplit('_', count)
739             if len(components) == prev_split_count:
740                 return None
741             prev_split_count = len(components)
742             type_string = components[0]
743             node = self._uscore_type_names.get(type_string)
744             if node:
745                 return (node, '_'.join(components[1:]))
746             count += 1
747
748     def _pair_function(self, func):
749         """Check to see whether a toplevel function should be a
750 method or constructor of some type."""
751         if func.symbol.endswith('_get_type') or func.symbol.startswith('_'):
752             return
753         (ns, subsymbol) = self._transformer.split_csymbol(func.symbol)
754         assert ns == self._namespace
755         if self._pair_constructor(func, subsymbol):
756             return
757         elif self._pair_method(func, subsymbol):
758             return
759         elif self._pair_static_method(func, subsymbol):
760             return
761
762     def _uscored_identifier_for_type(self, typeval):
763         """Given a Type(target_giname='Foo.BarBaz'), return 'bar_baz'."""
764         name = typeval.get_giname()
765         return to_underscores_noprefix(name).lower()
766
767     def _pair_method(self, func, subsymbol):
768         if not func.parameters:
769             return False
770         first = func.parameters[0]
771         target = self._transformer.lookup_typenode(first.type)
772         if not isinstance(target, (ast.Class, ast.Interface,
773                                    ast.Record, ast.Union,
774                                    glibast.GLibBoxedOther)):
775             return False
776
777         # A quick hack here...in the future we should catch C signature/GI signature
778         # mismatches in a general way in finaltransformer
779         if first.type.ctype.count('*') != 1:
780             return False
781
782         uscored = self._uscored_identifier_for_type(first.type)
783         if not subsymbol.startswith(uscored):
784             return False
785         del func.parameters[0]
786         subsym_idx = func.symbol.find(subsymbol)
787         self._namespace.float(func)
788         func.name = func.symbol[(subsym_idx + len(uscored) + 1):]
789         target.methods.append(func)
790         func.is_method = True
791         return True
792
793     def _pair_static_method(self, func, subsymbol):
794         split = self._split_uscored_by_type(subsymbol)
795         if split is None:
796             return False
797         (node, funcname) = split
798         if not isinstance(node, (ast.Class, ast.Interface,
799                                  ast.Record, ast.Union, glibast.GLibBoxedOther)):
800             return False
801         self._namespace.float(func)
802         func.name = funcname
803         node.static_methods.append(func)
804
805     def _pair_constructor(self, func, subsymbol):
806         if not (func.symbol.find('_new_') >= 0 or func.symbol.endswith('_new')):
807             return False
808         target = self._transformer.lookup_typenode(func.retval.type)
809         if not isinstance(target, (ast.Class, glibast.GLibBoxed)):
810             return False
811         new_idx = func.symbol.rfind('_new')
812         assert (new_idx >= 0)
813         prefix = func.symbol[:new_idx]
814         split = self._split_uscored_by_type(subsymbol)
815         if split is None:
816             # TODO - need a e.g. (method) annotation
817             message.warn_node(func,
818                 "Can't find matching type for constructor; symbol=%r" % (func.symbol, ))
819             return False
820         (origin_node, funcname) = split
821         if isinstance(target, ast.Class):
822             parent = origin_node
823             while parent and (not parent.create_type().target_giname == 'GObject.Object'):
824                 if parent == target:
825                     break
826                 if parent.parent:
827                     parent = self._transformer.lookup_typenode(parent.parent)
828                 else:
829                     parent = None
830                 if parent is None:
831                     message.warn_node(func,
832                         "Return value is not superclass for constructor; "
833                         "symbol=%r constructed=%r return=%r" % (
834                         func.symbol,
835                         str(origin_node.create_type()),
836                         str(func.retval.type)))
837                     return False
838         else:
839             if origin_node != target:
840                 message.warn_node(func,
841                     "Constructor return type mismatch symbol=%r "
842                     "constructed=%r return=%r" % (
843                     func.symbol,
844                     str(origin_node.create_type()),
845                     str(func.retval.type)))
846                 return False
847         self._namespace.float(func)
848         func.name = funcname
849         func.is_constructor = True
850         target.constructors.append(func)
851         # Constructors have default return semantics
852         if not func.retval.transfer:
853             func.retval.transfer = self._get_transfer_default_return(func, func.retval)
854         return True
855
856     def _pair_class_virtuals(self, node):
857         """Look for virtual methods from the class structure."""
858         if not node.glib_type_struct:
859             message.warn_node(node,
860                 "Failed to find class structure for %r" % (node.name, ))
861             return
862
863         node_type = node.create_type()
864         class_struct = self._transformer.lookup_typenode(node.glib_type_struct)
865
866         # Object class fields are assumed to be read-only
867         # (see also _introspect_object and transformer.py)
868         for field in class_struct.fields:
869             if isinstance(field, ast.Field):
870                 field.writable = False
871
872         # Loop through fields to determine which are virtual
873         # functions and which are signal slots by
874         # assuming everything that doesn't share a name
875         # with a known signal is a virtual slot.
876         for field in class_struct.fields:
877             if not isinstance(field.anonymous_node, ast.Callback):
878                 continue
879             callback = field.anonymous_node
880             # Check the first parameter is the object
881             if len(callback.parameters) == 0:
882                 continue
883             firstparam_type = callback.parameters[0].type
884             if firstparam_type != node_type:
885                 continue
886             # Also double check we don't have a signal with this
887             # name.
888             matched_signal = False
889             for signal in node.signals:
890                 if signal.name.replace('-', '_') == callback.name:
891                     matched_signal = True
892                     break
893             if matched_signal:
894                 continue
895             vfunc = ast.VFunction.from_callback(callback)
896             vfunc.inherit_file_positions(callback)
897             node.virtual_methods.append(vfunc)
898
899         # Take the set of virtual methods we found, and try
900         # to pair up with any matching methods using the
901         # name+signature.
902         for vfunc in node.virtual_methods:
903             for method in node.methods:
904                 if method.name != vfunc.name:
905                     continue
906                 if method.retval.type != vfunc.retval.type:
907                     continue
908                 if len(method.parameters) != len(vfunc.parameters):
909                     continue
910                 for i in xrange(len(method.parameters)):
911                     m_type = method.parameters[i].type
912                     v_type = vfunc.parameters[i].type
913                     if m_type != v_type:
914                         continue
915                 vfunc.invoker = method.name
916                 # Apply any annotations we have from the invoker to
917                 # the vfunc
918                 block = self._blocks.get(method.symbol)
919                 self._apply_annotations_callable(vfunc, [], block)
920
921     def _pass3(self, node, chain):
922         """Pass 3 is after we've loaded GType data and performed type
923         closure."""
924         if isinstance(node, ast.Callable):
925             self._pass3_callable_callbacks(node)
926             self._pass3_callable_throws(node)
927         return True
928
929     def _pass3_callable_callbacks(self, node):
930         """Check to see if we have anything that looks like a
931         callback+user_data+GDestroyNotify set."""
932
933         params = node.parameters
934
935         # First, do defaults for well-known callback types
936         for i, param in enumerate(params):
937             argnode = self._transformer.lookup_typenode(param.type)
938             if isinstance(argnode, ast.Callback):
939                 if param.type.target_giname in ('Gio.AsyncReadyCallback',
940                                                 'GLib.DestroyNotify'):
941                     param.scope = ast.PARAM_SCOPE_ASYNC
942                     param.transfer = ast.PARAM_TRANSFER_NONE
943
944         callback_param = None
945         for i, param in enumerate(params):
946             argnode = self._transformer.lookup_typenode(param.type)
947             is_destroynotify = False
948             if isinstance(argnode, ast.Callback):
949                 if param.type.target_giname == 'GLib.DestroyNotify':
950                     is_destroynotify = True
951                 else:
952                     callback_param = param
953                     continue
954             if callback_param is None:
955                 continue
956             if is_destroynotify:
957                 callback_param.destroy_name = param.argname
958                 callback_param.scope = ast.PARAM_SCOPE_NOTIFIED
959                 callback_param.transfer = ast.PARAM_TRANSFER_NONE
960             elif (param.type.is_equiv(ast.TYPE_ANY) and
961                   param.argname is not None and
962                   param.argname.endswith('data')):
963                 callback_param.closure_name = param.argname
964
965     def _pass3_callable_throws(self, node):
966         """Check to see if we have anything that looks like a
967         callback+user_data+GDestroyNotify set."""
968         if not node.parameters:
969             return
970         last_param = node.parameters[-1]
971         # Checking type.name=='GLib.Error' generates false positives
972         # on methods that take a 'GError *'
973         if last_param.type.ctype == 'GError**':
974             node.parameters.pop()
975             node.throws = True