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