Imported Upstream version 1.39.3
[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 message
24 from .annotationparser import (TAG_DEPRECATED, TAG_SINCE, TAG_STABILITY, TAG_RETURNS)
25 from .annotationparser import (ANN_ALLOW_NONE, ANN_ARRAY, ANN_ATTRIBUTES, ANN_CLOSURE,
26                                ANN_CONSTRUCTOR, ANN_DESTROY, ANN_ELEMENT_TYPE, ANN_FOREIGN,
27                                ANN_GET_VALUE_FUNC, ANN_IN, ANN_INOUT, ANN_METHOD, ANN_OUT,
28                                ANN_REF_FUNC, ANN_RENAME_TO, ANN_SCOPE, ANN_SET_VALUE_FUNC,
29                                ANN_SKIP, ANN_TRANSFER, ANN_TYPE, ANN_UNREF_FUNC, ANN_VALUE,
30                                ANN_VFUNC)
31 from .annotationparser import (OPT_ARRAY_FIXED_SIZE, OPT_ARRAY_LENGTH, OPT_ARRAY_ZERO_TERMINATED,
32                                OPT_OUT_CALLEE_ALLOCATES, OPT_OUT_CALLER_ALLOCATES,
33                                OPT_TRANSFER_FLOATING, OPT_TRANSFER_NONE)
34
35 from .utils import to_underscores_noprefix
36
37
38 class MainTransformer(object):
39
40     def __init__(self, transformer, blocks):
41         self._transformer = transformer
42         self._blocks = blocks
43         self._namespace = transformer.namespace
44         self._uscore_type_names = {}
45
46     # Public API
47
48     def transform(self):
49         if not self._namespace.names:
50             message.fatal('Namespace is empty; likely causes are:\n'
51                           '* Not including .h files to be scanned\n'
52                           '* Broken --identifier-prefix')
53
54         # Some initial namespace surgery
55         self._namespace.walk(self._pass_fixup_hidden_fields)
56
57         # We have a rough tree which should have most of of the types
58         # we know about.  Let's attempt closure; walk over all of the
59         # Type() types and see if they match up with something.
60         self._namespace.walk(self._pass_type_resolution)
61
62         # Read in annotations needed early
63         self._namespace.walk(self._pass_read_annotations_early)
64
65         # Determine some default values for transfer etc.
66         # based on the current tree.
67         self._namespace.walk(self._pass_callable_defaults)
68
69         # Read in most annotations now.
70         self._namespace.walk(self._pass_read_annotations)
71
72         # Now that we've possibly seen more types from annotations,
73         # do another type resolution pass.
74         self._namespace.walk(self._pass_type_resolution)
75
76         # Generate a reverse mapping "bar_baz" -> BarBaz
77         for node in self._namespace.itervalues():
78             if isinstance(node, ast.Registered) and node.get_type is not None:
79                 self._uscore_type_names[node.c_symbol_prefix] = node
80             elif isinstance(node, (ast.Record, ast.Union)):
81                 uscored = to_underscores_noprefix(node.name).lower()
82                 self._uscore_type_names[uscored] = node
83
84         for node in list(self._namespace.itervalues()):
85             if isinstance(node, ast.Function):
86                 # Discover which toplevel functions are actually methods
87                 self._pair_function(node)
88             if isinstance(node, (ast.Class, ast.Interface)):
89                 self._pair_class_virtuals(node)
90
91         # Some annotations need to be post function pairing
92         self._namespace.walk(self._pass_read_annotations2)
93
94         # Another type resolution pass after we've parsed virtuals, etc.
95         self._namespace.walk(self._pass_type_resolution)
96
97         self._namespace.walk(self._pass3)
98
99         # TODO - merge into pass3
100         self._pair_quarks_with_enums()
101
102     # Private
103
104     def _pass_fixup_hidden_fields(self, node, chain):
105         """Hide all callbacks starting with _; the typical
106         usage is void (*_gtk_reserved1)(void);"""
107         if isinstance(node, (ast.Class, ast.Interface, ast.Record, ast.Union)):
108             for field in node.fields:
109                 if (field
110                 and field.name is not None
111                 and field.name.startswith('_')
112                 and field.anonymous_node is not None
113                 and isinstance(field.anonymous_node, ast.Callback)):
114                     field.introspectable = False
115         return True
116
117     def _get_validate_parameter_name(self, parent, param_name, origin):
118         try:
119             param = parent.get_parameter(param_name)
120         except ValueError:
121             param = None
122         if param is None:
123             if isinstance(origin, ast.Parameter):
124                 origin_name = 'parameter %s' % (origin.argname, )
125             else:
126                 origin_name = 'return value'
127             message.log_node(
128                 message.FATAL, parent,
129                 "can't find parameter %s referenced by %s of %r"
130                 % (param_name, origin_name, parent.name))
131
132         return param.argname
133
134     def _get_validate_field_name(self, parent, field_name, origin):
135         try:
136             field = parent.get_field(field_name)
137         except ValueError:
138             field = None
139         if field is None:
140             origin_name = 'field %s' % (origin.name, )
141             message.log_node(
142                 message.FATAL, parent,
143                 "can't find field %s referenced by %s of %r"
144                 % (field_name, origin_name, parent.name))
145
146         return field.name
147
148     def _apply_annotation_rename_to(self, node, chain, block):
149         if not block:
150             return
151         rename_to = block.annotations.get(ANN_RENAME_TO)
152         if not rename_to:
153             return
154         rename_to = rename_to[0]
155         target = self._namespace.get_by_symbol(rename_to)
156         if not target:
157             message.warn_node(node,
158                 "Can't find symbol %r referenced by \"rename-to\" annotation" % (rename_to, ))
159         elif target.shadowed_by:
160             message.warn_node(node,
161                 "Function %r already shadowed by %r, can't overwrite "
162                 "with %r" % (target.symbol,
163                              target.shadowed_by,
164                              rename_to))
165         elif target.shadows:
166             message.warn_node(node,
167                 "Function %r already shadows %r, can't multiply shadow "
168                 "with %r" % (target.symbol,
169                              target.shadows,
170                              rename_to))
171         else:
172             target.shadowed_by = node.name
173             node.shadows = target.name
174
175     def _apply_annotations_function(self, node, chain):
176         block = self._blocks.get(node.symbol)
177         self._apply_annotations_callable(node, chain, block)
178
179     def _pass_read_annotations_early(self, node, chain):
180         if isinstance(node, ast.Record):
181             if node.ctype is not None:
182                 block = self._blocks.get(node.ctype)
183             else:
184                 block = self._blocks.get(node.c_name)
185             self._apply_annotations_annotated(node, block)
186         return True
187
188     def _pass_callable_defaults(self, node, chain):
189         if isinstance(node, (ast.Callable, ast.Signal)):
190             for param in node.parameters:
191                 if param.transfer is None:
192                     param.transfer = self._get_transfer_default(node, param)
193             if node.retval.transfer is None:
194                 node.retval.transfer = self._get_transfer_default(node, node.retval)
195         return True
196
197     def _get_annotation_name(self, node):
198         if isinstance(node, (ast.Class, ast.Interface, ast.Record,
199                              ast.Union, ast.Enum, ast.Bitfield,
200                              ast.Callback, ast.Alias, ast.Constant)):
201             if node.ctype is not None:
202                 return node.ctype
203             elif isinstance(node, ast.Registered) and node.gtype_name is not None:
204                 return node.gtype_name
205             return node.c_name
206         raise AssertionError("Unhandled node %r" % (node, ))
207
208     def _get_block(self, node):
209         return self._blocks.get(self._get_annotation_name(node))
210
211     def _pass_read_annotations(self, node, chain):
212         if not node.namespace:
213             return False
214         if isinstance(node, ast.Alias):
215             self._apply_annotations_alias(node, chain)
216         if isinstance(node, ast.Function):
217             self._apply_annotations_function(node, chain)
218         if isinstance(node, ast.Callback):
219             self._apply_annotations_callable(node, chain, block=self._get_block(node))
220         if isinstance(node, (ast.Class, ast.Interface, ast.Union, ast.Enum,
221                              ast.Bitfield, ast.Callback)):
222             self._apply_annotations_annotated(node, self._get_block(node))
223         if isinstance(node, (ast.Enum, ast.Bitfield)):
224             self._apply_annotations_enum_members(node, self._get_block(node))
225         if isinstance(node, (ast.Class, ast.Interface, ast.Record, ast.Union)):
226             block = self._get_block(node)
227             for field in node.fields:
228                 self._apply_annotations_field(node, block, field)
229             name = self._get_annotation_name(node)
230             section_name = 'SECTION:%s' % (name.lower(), )
231             block = self._blocks.get(section_name)
232             if block and block.description:
233                 node.doc = block.description
234         if isinstance(node, (ast.Class, ast.Interface)):
235             for prop in node.properties:
236                 self._apply_annotations_property(node, prop)
237             for sig in node.signals:
238                 self._apply_annotations_signal(node, sig)
239         if isinstance(node, ast.Class):
240             block = self._get_block(node)
241             if block:
242                 annotation = block.annotations.get(ANN_UNREF_FUNC)
243                 node.unref_func = annotation[0] if annotation else None
244                 annotation = block.annotations.get(ANN_REF_FUNC)
245                 node.ref_func = annotation[0] if annotation else None
246                 annotation = block.annotations.get(ANN_SET_VALUE_FUNC)
247                 node.set_value_func = annotation[0] if annotation else None
248                 annotation = block.annotations.get(ANN_GET_VALUE_FUNC)
249                 node.get_value_func = annotation[0] if annotation else None
250         if isinstance(node, ast.Constant):
251             self._apply_annotations_constant(node)
252         return True
253
254     def _adjust_container_type(self, parent, node, annotations):
255         if ANN_ARRAY in annotations:
256             self._apply_annotations_array(parent, node, annotations)
257         elif ANN_ELEMENT_TYPE in annotations:
258             self._apply_annotations_element_type(parent, node, annotations)
259
260         if isinstance(node.type, ast.Array):
261             self._check_array_element_type(node.type, annotations)
262
263     def _resolve(self, type_str, type_node=None, node=None, parent=None):
264         def grab_one(type_str, resolver, top_combiner, combiner):
265             """Return a complete type, and the trailing string part after it.
266             Use resolver() on each identifier, and combiner() on the parts of
267             each complete type. (top_combiner is used on the top-most type.)"""
268             bits = re.split(r'([,<>()])', type_str, 1)
269             first, sep, rest = [bits[0], '', ''] if (len(bits) == 1) else bits
270             args = [resolver(first)]
271             if sep == '<' or sep == '(':
272                 lastsep = '>' if (sep == '<') else ')'
273                 while sep != lastsep:
274                     next, rest = grab_one(rest, resolver, combiner, combiner)
275                     args.append(next)
276                     sep, rest = rest[0], rest[1:]
277             else:
278                 rest = sep + rest
279             return top_combiner(*args), rest
280
281         def resolver(ident):
282             res = self._transformer.create_type_from_user_string(ident)
283             return res
284
285         def combiner(base, *rest):
286             if not rest:
287                 return base
288             if isinstance(base, ast.List) and len(rest) == 1:
289                 return ast.List(base.name, *rest)
290             if isinstance(base, ast.Map) and len(rest) == 2:
291                 return ast.Map(*rest)
292             message.warn(
293                 "Too many parameters in type specification %r" % (type_str, ))
294             return base
295
296         def top_combiner(base, *rest):
297             if type_node is not None and isinstance(type_node, ast.Type):
298                 base.is_const = type_node.is_const
299             return combiner(base, *rest)
300
301         result, rest = grab_one(type_str, resolver, top_combiner, combiner)
302         if rest:
303             message.warn("Trailing components in type specification %r" % (
304                 type_str, ))
305
306         if not result.resolved:
307             position = None
308             if parent is not None and isinstance(parent, ast.Function):
309                 text = parent.symbol
310                 position = self._get_position(parent, node)
311             else:
312                 text = type_str
313             message.warn_node(parent, "%s: Unknown type: %r" %
314                               (text, type_str), positions=position)
315         return result
316
317     def _resolve_toplevel(self, type_str, type_node=None, node=None, parent=None):
318         """Like _resolve(), but attempt to preserve more attributes of original type."""
319         result = self._resolve(type_str, type_node=type_node, node=node, parent=parent)
320         # If we replace a node with a new type (such as an annotated) we
321         # might lose the ctype from the original node.
322         if type_node is not None:
323             result.ctype = type_node.ctype
324         return result
325
326     def _get_position(self, func, param):
327         block = self._blocks.get(func.symbol)
328         if block:
329             if isinstance(param, ast.Parameter):
330                 part = block.params.get(param.argname)
331             elif isinstance(param, ast.Return):
332                 part = block.tags.get(TAG_RETURNS)
333             else:
334                 part = None
335
336             if part.position:
337                 return part.position
338
339         return block.position
340
341     def _check_array_element_type(self, array, annotations):
342         array_type = array.array_type
343         element_type = array.element_type
344
345         # GPtrArrays are allowed to contain non basic types
346         # (except enums and flags) or basic types that are
347         # as big as a gpointer
348         if array_type == ast.Array.GLIB_PTRARRAY:
349             if ((element_type in ast.BASIC_GIR_TYPES and not element_type in ast.POINTER_TYPES)
350             or isinstance(element_type, (ast.Enum, ast.Bitfield))):
351                 message.warn("invalid (element-type) for a GPtrArray, "
352                              "must be a pointer", annotations.position)
353
354         # GByteArrays have (element-type) guint8 by default
355         if array_type == ast.Array.GLIB_BYTEARRAY:
356             if element_type == ast.TYPE_ANY:
357                 array.element_type = ast.TYPE_UINT8
358             elif not element_type in [ast.TYPE_UINT8, ast.TYPE_INT8, ast.TYPE_CHAR]:
359                 message.warn("invalid (element-type) for a GByteArray, "
360                              "must be one of guint8, gint8 or gchar",
361                              annotations.position)
362
363     def _apply_annotations_array(self, parent, node, annotations):
364         element_type_options = annotations.get(ANN_ELEMENT_TYPE)
365         if element_type_options:
366             element_type_node = self._resolve(element_type_options[0],
367                                               node.type, node, parent)
368         elif isinstance(node.type, ast.Array):
369             element_type_node = node.type.element_type
370         else:
371             # We're assuming here that Foo* with an (array) annotation
372             # and no (element-type) means array of Foo
373             element_type_node = node.type.clone()
374             # The element's ctype is the array's dereferenced
375             if element_type_node.ctype is not None and element_type_node.ctype.endswith('*'):
376                 element_type_node.ctype = element_type_node.ctype[:-1]
377
378         if isinstance(node.type, ast.Array):
379             array_type = node.type.array_type
380         else:
381             array_type = None
382
383         array_options = annotations.get(ANN_ARRAY)
384         container_type = ast.Array(array_type, element_type_node, ctype=node.type.ctype,
385                                    is_const=node.type.is_const)
386         if OPT_ARRAY_ZERO_TERMINATED in array_options:
387             container_type.zeroterminated = array_options.get(OPT_ARRAY_ZERO_TERMINATED) == '1'
388         else:
389             container_type.zeroterminated = False
390
391         length = array_options.get(OPT_ARRAY_LENGTH)
392         if length:
393             if isinstance(parent, ast.Compound):
394                 paramname = self._get_validate_field_name(parent, length, node)
395             else:
396                 paramname = self._get_validate_parameter_name(parent, length, node)
397                 if paramname:
398                     param = parent.get_parameter(paramname)
399                     param.direction = node.direction
400                     if param.direction == ast.PARAM_DIRECTION_OUT:
401                         param.transfer = ast.PARAM_TRANSFER_FULL
402             if paramname:
403                 container_type.length_param_name = paramname
404         fixed = array_options.get(OPT_ARRAY_FIXED_SIZE)
405         if fixed:
406             try:
407                 container_type.size = int(fixed)
408             except (TypeError, ValueError):
409                 # Already warned in annotationparser.py
410                 return
411         node.type = container_type
412
413     def _apply_annotations_element_type(self, parent, node, annotations):
414         element_type_options = annotations.get(ANN_ELEMENT_TYPE)
415         if element_type_options is None:
416             return
417
418         if isinstance(node.type, ast.List):
419             if len(element_type_options) != 1:
420                 message.warn(
421                     '"element-type" annotation for a list must have exactly '
422                     'one option, not %d options' % (len(element_type_options), ),
423                     annotations.position)
424                 return
425             node.type.element_type = self._resolve(element_type_options[0],
426                                                    node.type, node, parent)
427         elif isinstance(node.type, ast.Map):
428             if len(element_type_options) != 2:
429                 message.warn(
430                     '"element-type" annotation for a hash table must have exactly '
431                     'two options, not %d option(s)' % (len(element_type_options), ),
432                     annotations.position)
433                 return
434             node.type.key_type = self._resolve(element_type_options[0],
435                                                node.type, node, parent)
436             node.type.value_type = self._resolve(element_type_options[1],
437                                                  node.type, node, parent)
438         elif isinstance(node.type, ast.Array):
439             if len(element_type_options) != 1:
440                 message.warn(
441                     '"element-type" annotation for an array must have exactly '
442                     'one option, not %d options' % (len(element_type_options), ),
443                     annotations.position)
444                 return
445             node.type.element_type = self._resolve(element_type_options[0],
446                                                    node.type, node, parent)
447         else:
448             message.warn(
449                 "Unknown container %r for element-type annotation" % (node.type, ),
450                 annotations.position)
451
452     def _get_transfer_default_param(self, parent, node):
453         if node.direction in [ast.PARAM_DIRECTION_INOUT,
454                               ast.PARAM_DIRECTION_OUT]:
455             if node.caller_allocates:
456                 return ast.PARAM_TRANSFER_NONE
457             return ast.PARAM_TRANSFER_FULL
458         return ast.PARAM_TRANSFER_NONE
459
460     def _get_transfer_default_returntype_basic(self, typeval):
461         if (typeval.is_equiv(ast.BASIC_GIR_TYPES)
462         or typeval.is_const
463         or typeval.is_equiv(ast.TYPE_NONE)):
464             return ast.PARAM_TRANSFER_NONE
465         elif typeval.is_equiv(ast.TYPE_STRING):
466             # Non-const strings default to FULL
467             return ast.PARAM_TRANSFER_FULL
468         elif typeval.target_fundamental:
469             # This looks like just GType right now
470             return None
471         return None
472
473     def _is_gi_subclass(self, typeval, supercls_type):
474         cls = self._transformer.lookup_typenode(typeval)
475         assert cls, str(typeval)
476         supercls = self._transformer.lookup_typenode(supercls_type)
477         assert supercls
478         if cls is supercls:
479             return True
480         if cls.parent_type and cls.parent_type.target_giname != 'GObject.Object':
481             return self._is_gi_subclass(cls.parent_type, supercls_type)
482         return False
483
484     def _get_transfer_default_return(self, parent, node):
485         typeval = node.type
486         basic = self._get_transfer_default_returntype_basic(typeval)
487         if basic:
488             return basic
489         if not typeval.target_giname:
490             return None
491         target = self._transformer.lookup_typenode(typeval)
492         if isinstance(target, ast.Alias):
493             return self._get_transfer_default_returntype_basic(target.target)
494         elif (isinstance(target, ast.Boxed)
495               or (isinstance(target, (ast.Record, ast.Union))
496                   and (target.gtype_name is not None or target.foreign))):
497             return ast.PARAM_TRANSFER_FULL
498         elif isinstance(target, (ast.Enum, ast.Bitfield)):
499             return ast.PARAM_TRANSFER_NONE
500         # Handle constructors specially here
501         elif isinstance(parent, ast.Function) and parent.is_constructor:
502             if isinstance(target, ast.Class):
503                 initially_unowned_type = ast.Type(target_giname='GObject.InitiallyUnowned')
504                 initially_unowned = self._transformer.lookup_typenode(initially_unowned_type)
505                 if initially_unowned and self._is_gi_subclass(typeval, initially_unowned_type):
506                     return ast.PARAM_TRANSFER_NONE
507                 else:
508                     return ast.PARAM_TRANSFER_FULL
509             elif isinstance(target, (ast.Record, ast.Union)):
510                 return ast.PARAM_TRANSFER_FULL
511             else:
512                 raise AssertionError("Invalid constructor")
513         elif isinstance(target, (ast.Class, ast.Record, ast.Union)):
514             # Explicitly no default for these
515             return None
516         else:
517             return None
518
519     def _get_transfer_default(self, parent, node):
520         if node.type.is_equiv(ast.TYPE_NONE) or isinstance(node.type, ast.Varargs):
521             return ast.PARAM_TRANSFER_NONE
522         elif isinstance(node, ast.Parameter):
523             return self._get_transfer_default_param(parent, node)
524         elif isinstance(node, ast.Return):
525             return self._get_transfer_default_return(parent, node)
526         elif isinstance(node, ast.Field):
527             return ast.PARAM_TRANSFER_NONE
528         elif isinstance(node, ast.Property):
529             return ast.PARAM_TRANSFER_NONE
530         else:
531             raise AssertionError(node)
532
533     def _apply_annotations_param_ret_common(self, parent, node, tag):
534         annotations = tag.annotations if tag else {}
535
536         type_annotation = annotations.get(ANN_TYPE)
537         if type_annotation:
538             node.type = self._resolve_toplevel(type_annotation[0],
539                                                node.type, node, parent)
540
541         caller_allocates = False
542         annotated_direction = None
543         if ANN_INOUT in annotations:
544             annotated_direction = ast.PARAM_DIRECTION_INOUT
545         elif ANN_OUT in annotations:
546             annotated_direction = ast.PARAM_DIRECTION_OUT
547
548             options = annotations[ANN_OUT]
549             if len(options) == 0:
550                 if node.type.target_giname and node.type.ctype:
551                     target = self._transformer.lookup_giname(node.type.target_giname)
552                     target = self._transformer.resolve_aliases(target)
553                     has_double_indirection = '**' in node.type.ctype
554                     is_structure_or_union = isinstance(target, (ast.Record, ast.Union))
555                     caller_allocates = (not has_double_indirection and is_structure_or_union)
556                 else:
557                     caller_allocates = False
558             else:
559                 option = options[0]
560                 if option == OPT_OUT_CALLER_ALLOCATES:
561                     caller_allocates = True
562                 elif option == OPT_OUT_CALLEE_ALLOCATES:
563                     caller_allocates = False
564         elif ANN_IN in annotations:
565             annotated_direction = ast.PARAM_DIRECTION_IN
566
567         if (annotated_direction is not None) and (annotated_direction != node.direction):
568             node.direction = annotated_direction
569             node.caller_allocates = caller_allocates
570             # Also reset the transfer default if we're toggling direction
571             node.transfer = self._get_transfer_default(parent, node)
572
573         transfer_annotation = annotations.get(ANN_TRANSFER)
574         if transfer_annotation and len(transfer_annotation) == 1:
575             transfer = transfer_annotation[0]
576             if transfer == OPT_TRANSFER_FLOATING:
577                 transfer = OPT_TRANSFER_NONE
578             node.transfer = transfer
579
580         self._adjust_container_type(parent, node, annotations)
581
582         if (ANN_ALLOW_NONE in annotations
583         or node.type.target_giname == 'Gio.AsyncReadyCallback'
584         or node.type.target_giname == 'Gio.Cancellable'):
585             node.allow_none = True
586
587         if tag and tag.description:
588             node.doc = tag.description
589
590         if ANN_SKIP in annotations:
591             node.skip = True
592
593         if annotations:
594             attributes_annotation = annotations.get(ANN_ATTRIBUTES)
595             if attributes_annotation is not None:
596                 for key, value in attributes_annotation.items():
597                     if value:
598                         node.attributes[key] = value
599
600     def _apply_annotations_annotated(self, node, block):
601         if block is None:
602             return
603
604         if block.description:
605             node.doc = block.description
606
607         since_tag = block.tags.get(TAG_SINCE)
608         if since_tag is not None:
609             if since_tag.value:
610                 node.version = since_tag.value
611             if since_tag.description:
612                 node.version_doc = since_tag.description
613
614         deprecated_tag = block.tags.get(TAG_DEPRECATED)
615         if deprecated_tag is not None:
616             if deprecated_tag.value:
617                 node.deprecated = deprecated_tag.value
618             if deprecated_tag.description:
619                 node.deprecated_doc = deprecated_tag.description
620
621         stability_tag = block.tags.get(TAG_STABILITY)
622         if stability_tag is not None:
623             if stability_tag.value:
624                 node.stability = stability_tag.value
625             if stability_tag.description:
626                 node.stability_doc = stability_tag.description
627
628         attributes_annotation = block.annotations.get(ANN_ATTRIBUTES)
629         if attributes_annotation is not None:
630             for key, value in attributes_annotation.items():
631                 if value:
632                     node.attributes[key] = value
633
634         if ANN_SKIP in block.annotations:
635             node.skip = True
636
637         if ANN_FOREIGN in block.annotations:
638             node.foreign = True
639
640         if ANN_CONSTRUCTOR in block.annotations and isinstance(node, ast.Function):
641             node.is_constructor = True
642
643         if ANN_METHOD in block.annotations:
644             node.is_method = True
645
646     def _apply_annotations_alias(self, node, chain):
647         block = self._get_block(node)
648         self._apply_annotations_annotated(node, block)
649
650     def _apply_annotations_param(self, parent, param, tag):
651         annotations = tag.annotations if tag else {}
652
653         if isinstance(parent, (ast.Function, ast.VFunction)):
654             scope_annotation = annotations.get(ANN_SCOPE)
655             if scope_annotation and len(scope_annotation) == 1:
656                 param.scope = scope_annotation[0]
657                 param.transfer = ast.PARAM_TRANSFER_NONE
658
659             destroy_annotation = annotations.get(ANN_DESTROY)
660             if destroy_annotation:
661                 param.destroy_name = self._get_validate_parameter_name(parent,
662                                                                        destroy_annotation[0],
663                                                                        param)
664                 if param.destroy_name is not None:
665                     param.scope = ast.PARAM_SCOPE_NOTIFIED
666                     destroy_param = parent.get_parameter(param.destroy_name)
667                     # This is technically bogus; we're setting the scope on the destroy
668                     # itself.  But this helps avoid tripping a warning from finaltransformer,
669                     # since we don't have a way right now to flag this callback a destroy.
670                     destroy_param.scope = ast.PARAM_SCOPE_NOTIFIED
671
672             closure_annotation = annotations.get(ANN_CLOSURE)
673             if closure_annotation and len(closure_annotation) == 1:
674                 param.closure_name = self._get_validate_parameter_name(parent,
675                                                                    closure_annotation[0],
676                                                                    param)
677         elif isinstance(parent, ast.Callback):
678             if ANN_CLOSURE in annotations:
679                 # For callbacks, (closure) appears without an
680                 # argument, and tags a parameter that is a closure. We
681                 # represent it (weirdly) in the gir and typelib by
682                 # setting param.closure_name to itself.
683                 param.closure_name = param.argname
684
685         self._apply_annotations_param_ret_common(parent, param, tag)
686
687     def _apply_annotations_return(self, parent, return_, block):
688         if block:
689             tag = block.tags.get(TAG_RETURNS)
690         else:
691             tag = None
692         self._apply_annotations_param_ret_common(parent, return_, tag)
693
694     def _apply_annotations_params(self, parent, params, block):
695         declparams = set([])
696         if parent.instance_parameter:
697             if block:
698                 doc_param = block.params.get(parent.instance_parameter.argname)
699             else:
700                 doc_param = None
701             self._apply_annotations_param(parent, parent.instance_parameter, doc_param)
702             declparams.add(parent.instance_parameter.argname)
703
704         for param in params:
705             if block:
706                 doc_param = block.params.get(param.argname)
707             else:
708                 doc_param = None
709             self._apply_annotations_param(parent, param, doc_param)
710             declparams.add(param.argname)
711
712         if not block:
713             return
714         docparams = set(block.params)
715
716         unknown = docparams - declparams
717         unused = declparams - docparams
718
719         for doc_name in unknown:
720             if len(unused) == 0:
721                 text = ''
722             elif len(unused) == 1:
723                 (param, ) = unused
724                 text = ', should be %r' % (param, )
725             else:
726                 text = ', should be one of %s' % (', '.join(repr(p) for p in unused), )
727
728             param = block.params.get(doc_name)
729             message.warn('%s: unknown parameter %r in documentation '
730                          'comment%s' % (block.name, doc_name, text),
731                 param.position)
732
733     def _apply_annotations_callable(self, node, chain, block):
734         self._apply_annotations_annotated(node, block)
735         self._apply_annotations_params(node, node.parameters, block)
736         self._apply_annotations_return(node, node.retval, block)
737
738     def _apply_annotations_field(self, parent, block, field):
739         if not block:
740             return
741         tag = block.params.get(field.name)
742         if not tag:
743             return
744         type_annotation = tag.annotations.get(ANN_TYPE)
745         if type_annotation:
746             field.type = self._transformer.create_type_from_user_string(type_annotation[0])
747         field.doc = tag.description
748         try:
749             self._adjust_container_type(parent, field, tag.annotations)
750         except AttributeError, ex:
751             print ex
752
753     def _apply_annotations_property(self, parent, prop):
754         prefix = self._get_annotation_name(parent)
755         block = self._blocks.get('%s:%s' % (prefix, prop.name))
756         self._apply_annotations_annotated(prop, block)
757         if not block:
758             return
759         transfer_annotation = block.annotations.get(ANN_TRANSFER)
760         if transfer_annotation is not None:
761             transfer = transfer_annotation[0]
762             if transfer == OPT_TRANSFER_FLOATING:
763                 transfer = OPT_TRANSFER_NONE
764             prop.transfer = transfer
765         else:
766             prop.transfer = self._get_transfer_default(parent, prop)
767         type_annotation = block.annotations.get(ANN_TYPE)
768         if type_annotation:
769             prop.type = self._resolve_toplevel(type_annotation[0], prop.type, prop, parent)
770
771     def _apply_annotations_signal(self, parent, signal):
772         names = []
773         prefix = self._get_annotation_name(parent)
774         block = self._blocks.get('%s::%s' % (prefix, signal.name))
775
776         if block:
777             self._apply_annotations_annotated(signal, block)
778
779             # We're only attempting to name the signal parameters if
780             # the number of parameters (@foo) is the same or greater
781             # than the number of signal parameters
782             if len(block.params) > len(signal.parameters):
783                 names = block.params.items()
784                 # Resolve real parameter names early, so that in later
785                 # phase we can refer to them while resolving annotations.
786                 for i, param in enumerate(signal.parameters):
787                     param.argname, tag = names[i + 1]
788             elif len(signal.parameters) != 0:
789                 # Only warn about missing params if there are actually parameters
790                 # besides implicit self.
791                 message.warn("incorrect number of parameters in comment block, "
792                              "parameter annotations will be ignored.", block.position)
793
794         for i, param in enumerate(signal.parameters):
795             if names:
796                 name, tag = names[i + 1]
797                 if tag:
798                     type_annotation = tag.annotations.get(ANN_TYPE)
799                     if type_annotation:
800                         param.type = self._resolve_toplevel(type_annotation[0], param.type,
801                                                             param, parent)
802             else:
803                 tag = None
804             self._apply_annotations_param(signal, param, tag)
805         self._apply_annotations_return(signal, signal.retval, block)
806
807     def _apply_annotations_constant(self, node):
808         block = self._get_block(node)
809         if block is None:
810             return
811
812         self._apply_annotations_annotated(node, block)
813
814         value_annotation = block.annotations.get(ANN_VALUE)
815         if value_annotation:
816             node.value = value_annotation[0]
817
818     def _apply_annotations_enum_members(self, node, block):
819         if block is None:
820             return
821
822         for m in node.members:
823             param = block.params.get(m.symbol, None)
824             if param and param.description:
825                 m.doc = param.description
826
827     def _pass_read_annotations2(self, node, chain):
828         if isinstance(node, ast.Function):
829             block = self._blocks.get(node.symbol)
830
831             self._apply_annotation_rename_to(node, chain, block)
832
833             # Handle virtual invokers
834             parent = chain[-1] if chain else None
835             if (block and parent):
836                 virtual_annotation = block.annotations.get(ANN_VFUNC)
837                 if virtual_annotation:
838                     invoker_name = virtual_annotation[0]
839                     matched = False
840                     for vfunc in parent.virtual_methods:
841                         if vfunc.name == invoker_name:
842                             matched = True
843                             vfunc.invoker = node.name
844                             # Also merge in annotations
845                             self._apply_annotations_callable(vfunc, [parent], block)
846                             break
847                     if not matched:
848                         message.warn_node(node,
849                             "Virtual slot %r not found for %r annotation" % (invoker_name,
850                                                                              ANN_VFUNC))
851         return True
852
853     def _resolve_and_filter_type_list(self, typelist):
854         """Given a list of Type instances, return a new list of types with
855 the ones that failed to resolve removed."""
856         # Create a copy we'll modify
857         new_typelist = list(typelist)
858         for typeval in typelist:
859             resolved = self._transformer.resolve_type(typeval)
860             if not resolved:
861                 new_typelist.remove(typeval)
862         return new_typelist
863
864     def _pass_type_resolution(self, node, chain):
865         if isinstance(node, ast.Alias):
866             self._transformer.resolve_type(node.target)
867         if isinstance(node, ast.Callable):
868             for parameter in node.parameters:
869                 self._transformer.resolve_type(parameter.type)
870             self._transformer.resolve_type(node.retval.type)
871         if isinstance(node, ast.Constant):
872             self._transformer.resolve_type(node.value_type)
873         if isinstance(node, (ast.Class, ast.Interface, ast.Record, ast.Union)):
874             for field in node.fields:
875                 if field.anonymous_node:
876                     pass
877                 else:
878                     self._transformer.resolve_type(field.type)
879         if isinstance(node, (ast.Class, ast.Interface)):
880             for parent in node.parent_chain:
881                 try:
882                     self._transformer.resolve_type(parent)
883                 except ValueError:
884                     continue
885                 target = self._transformer.lookup_typenode(parent)
886                 if target:
887                     node.parent_type = parent
888                     break
889             else:
890                 if isinstance(node, ast.Interface):
891                     node.parent_type = ast.Type(target_giname='GObject.Object')
892             for prop in node.properties:
893                 self._transformer.resolve_type(prop.type)
894             for sig in node.signals:
895                 for param in sig.parameters:
896                     self._transformer.resolve_type(param.type)
897         if isinstance(node, ast.Class):
898             node.interfaces = self._resolve_and_filter_type_list(node.interfaces)
899         if isinstance(node, ast.Interface):
900             node.prerequisites = self._resolve_and_filter_type_list(node.prerequisites)
901         return True
902
903     def _pair_quarks_with_enums(self):
904         # self._uscore_type_names is an authoritative mapping of types
905         # to underscored versions, since it is based on get_type() methods;
906         # but only covers enums that are registered as GObject enums.
907         # Create a fallback mapping based on all known enums in this module.
908         uscore_enums = {}
909         for enum in self._namespace.itervalues():
910             if not isinstance(enum, ast.Enum):
911                 continue
912             uscored = to_underscores_noprefix(enum.name).lower()
913             uscore_enums[uscored] = enum
914             uscore_enums[enum.name] = enum
915
916         for node in self._namespace.itervalues():
917             if not isinstance(node, ast.ErrorQuarkFunction):
918                 continue
919             full = node.symbol[:-len('_quark')]
920             ns, short = self._transformer.split_csymbol(node.symbol)
921             short = short[:-len('_quark')]
922             if full == "g_io_error":
923                 # Special case; GIOError was already taken forcing GIOErrorEnum
924                 assert self._namespace.name == 'Gio'
925                 enum = self._namespace.get('IOErrorEnum')
926             else:
927                 enum = self._uscore_type_names.get(short)
928                 if enum is None:
929                     enum = uscore_enums.get(short)
930             if enum is not None:
931                 enum.error_domain = node.error_domain
932             else:
933                 message.warn_node(node,
934                     """%s: Couldn't find corresponding enumeration""" % (node.symbol, ))
935
936     def _split_uscored_by_type(self, uscored):
937         """'uscored' should be an un-prefixed uscore string.  This
938 function searches through the namespace for the longest type which
939 prefixes uscored, and returns (type, suffix).  Example, assuming
940 namespace Gtk, type is TextBuffer:
941
942 _split_uscored_by_type(text_buffer_try_new) -> (ast.Class(TextBuffer), 'try_new')"""
943         node = None
944         count = 0
945         prev_split_count = -1
946         while True:
947             components = uscored.rsplit('_', count)
948             if len(components) == prev_split_count:
949                 return None
950             prev_split_count = len(components)
951             type_string = components[0]
952             node = self._uscore_type_names.get(type_string)
953             if node:
954                 return (node, '_'.join(components[1:]))
955             count += 1
956
957     def _pair_function(self, func):
958         """Check to see whether a toplevel function should be a
959 method or constructor of some type."""
960
961         # Ignore internal symbols and type metadata functions
962         if func.symbol.startswith('_') or func.is_type_meta_function():
963             return
964
965         (ns, subsymbol) = self._transformer.split_csymbol(func.symbol)
966         assert ns == self._namespace
967         if self._is_constructor(func, subsymbol):
968             self._set_up_constructor(func, subsymbol)
969             return
970         elif self._is_method(func, subsymbol):
971             self._setup_method(func, subsymbol)
972             return
973         elif self._pair_static_method(func, subsymbol):
974             return
975
976     def _uscored_identifier_for_type(self, typeval):
977         """Given a Type(target_giname='Foo.BarBaz'), return 'bar_baz'."""
978         name = typeval.get_giname()
979         return to_underscores_noprefix(name).lower()
980
981     def _is_method(self, func, subsymbol):
982         if not func.parameters:
983             if func.is_method:
984                 message.warn_node(func,
985                     '%s: Methods must have parameters' % (func.symbol, ))
986             return False
987         first = func.parameters[0]
988         target = self._transformer.lookup_typenode(first.type)
989         if not isinstance(target, (ast.Class, ast.Interface,
990                                    ast.Record, ast.Union,
991                                    ast.Boxed)):
992             if func.is_method:
993                 message.warn_node(func,
994                     '%s: Methods must have a pointer as their first '
995                     'parameter' % (func.symbol, ))
996             return False
997         if target.namespace != self._namespace:
998             if func.is_method:
999                 message.warn_node(func,
1000                     '%s: Methods must belong to the same namespace as the '
1001                     'class they belong to' % (func.symbol, ))
1002             return False
1003         if first.direction == ast.PARAM_DIRECTION_OUT:
1004             if func.is_method:
1005                 message.warn_node(func,
1006                     '%s: The first argument of methods cannot be an '
1007                     'out-argument' % (func.symbol, ))
1008             return False
1009
1010         # A quick hack here...in the future we should catch C signature/GI signature
1011         # mismatches in a general way in finaltransformer
1012         if first.type.ctype is not None and first.type.ctype.count('*') > 1:
1013             return False
1014
1015         if not func.is_method:
1016             uscored_prefix = self._get_uscored_prefix(func, subsymbol)
1017             if not subsymbol.startswith(uscored_prefix):
1018                 return False
1019
1020         return True
1021
1022     def _setup_method(self, func, subsymbol):
1023         uscored_prefix = self._get_uscored_prefix(func, subsymbol)
1024         target = self._transformer.lookup_typenode(func.parameters[0].type)
1025
1026         func.instance_parameter = func.parameters.pop(0)
1027         self._namespace.float(func)
1028
1029         if not func.is_method:
1030             subsym_idx = func.symbol.find(subsymbol)
1031             func.name = func.symbol[(subsym_idx + len(uscored_prefix) + 1):]
1032             func.is_method = True
1033
1034         target.methods.append(func)
1035
1036     def _get_uscored_prefix(self, func, subsymbol):
1037         # Here we check both the c_symbol_prefix and (if that fails),
1038         # attempt to do a default uscoring of the type.  The reason we
1039         # look at a default underscore transformation is for
1040         # gdk_window_object_get_type(), which says to us that the
1041         # prefix is "gdk_window_object", when really it's just
1042         # "gdk_window".  Possibly need an annotation to override this.
1043         prefix_matches = False
1044         uscored_prefix = None
1045         first_arg = func.parameters[0]
1046         target = self._transformer.lookup_typenode(first_arg.type)
1047         if hasattr(target, 'c_symbol_prefix') and target.c_symbol_prefix is not None:
1048             prefix_matches = subsymbol.startswith(target.c_symbol_prefix)
1049             if prefix_matches:
1050                 uscored_prefix = target.c_symbol_prefix
1051         if not prefix_matches:
1052             uscored_prefix = self._uscored_identifier_for_type(first_arg.type)
1053
1054         return uscored_prefix
1055
1056     def _pair_static_method(self, func, subsymbol):
1057         split = self._split_uscored_by_type(subsymbol)
1058         if split is None:
1059             return False
1060         (node, funcname) = split
1061         if funcname == '':
1062             return False
1063
1064         if isinstance(node, ast.Class):
1065             self._namespace.float(func)
1066             func.name = funcname
1067             node.static_methods.append(func)
1068             return True
1069         elif isinstance(node, (ast.Interface, ast.Record, ast.Union,
1070                                ast.Boxed, ast.Enum, ast.Bitfield)):
1071             # prior to the introduction of this part of the code, only
1072             # ast.Class could have static methods.  so for backwards
1073             # compatibility, instead of removing the func from the namespace,
1074             # leave it there and get a copy instead.  modify the copy and push
1075             # it onto static_methods.  we need to copy the parameters list
1076             # separately, because in the third pass functions are flagged as
1077             # 'throws' depending on the presence of a GError parameter which is
1078             # then removed from the parameters list.  without the explicit
1079             # copy, only one of the two functions would thus get flagged as
1080             # 'throws'.  clone() does this for us.
1081             new_func = func.clone()
1082             new_func.name = funcname
1083             node.static_methods.append(new_func)
1084             # flag the func as a backwards-comptability kludge (thus it will
1085             # get pruned in the introspectable pass if introspectable=0).
1086             func.moved_to = node.name + '.' + new_func.name
1087             return True
1088
1089         return False
1090
1091     def _set_up_constructor(self, func, subsymbol):
1092         self._namespace.float(func)
1093
1094         func.name = self._get_constructor_name(func, subsymbol)
1095
1096         origin_node = self._get_constructor_class(func, subsymbol)
1097         origin_node.constructors.append(func)
1098
1099         func.is_constructor = True
1100
1101         # Constructors have default return semantics
1102         if not func.retval.transfer:
1103             func.retval.transfer = self._get_transfer_default_return(func,
1104                     func.retval)
1105
1106     def _get_constructor_class(self, func, subsymbol):
1107         origin_node = None
1108         split = self._split_uscored_by_type(subsymbol)
1109         if split is None:
1110             if func.is_constructor:
1111                 origin_node = self._transformer.lookup_typenode(func.retval.type)
1112         else:
1113             origin_node, _ = split
1114
1115         return origin_node
1116
1117     def _get_constructor_name(self, func, subsymbol):
1118         name = None
1119         split = self._split_uscored_by_type(subsymbol)
1120         if split is None:
1121             if func.is_constructor:
1122                 name = func.name
1123         else:
1124             _, name = split
1125
1126         return name
1127
1128     def _guess_constructor_by_name(self, symbol):
1129         # Normal constructors, gtk_button_new etc
1130         if symbol.endswith('_new'):
1131             return True
1132         # Alternative constructor, gtk_button_new_with_label
1133         if '_new_' in symbol:
1134             return True
1135         # gtk_list_store_newv,gtk_tree_store_newv etc
1136         if symbol.endswith('_newv'):
1137             return True
1138         return False
1139
1140     def _is_constructor(self, func, subsymbol):
1141         # func.is_constructor will be True if we have a (constructor) annotation
1142         if not func.is_constructor:
1143             if not self._guess_constructor_by_name(func.symbol):
1144                 return False
1145         target = self._transformer.lookup_typenode(func.retval.type)
1146         if not (isinstance(target, ast.Class)
1147                 or (isinstance(target, (ast.Record, ast.Union, ast.Boxed))
1148                     and (target.get_type is not None or target.foreign))):
1149             if func.is_constructor:
1150                 message.warn_node(func,
1151                     '%s: Constructors must return an instance of their class'
1152                     % (func.symbol, ))
1153             return False
1154
1155         origin_node = self._get_constructor_class(func, subsymbol)
1156         if origin_node is None:
1157             if func.is_constructor:
1158                 message.warn_node(
1159                     func,
1160                     "Can't find matching type for constructor; symbol=%r" % (func.symbol, ))
1161             return False
1162
1163         # Some sanity checks; only objects and boxeds can have ctors
1164         if not (isinstance(origin_node, ast.Class)
1165                 or (isinstance(origin_node, (ast.Record, ast.Union, ast.Boxed))
1166                     and (origin_node.get_type is not None or origin_node.foreign))):
1167             return False
1168         # Verify the namespace - don't want to append to foreign namespaces!
1169         if origin_node.namespace != self._namespace:
1170             if func.is_constructor:
1171                 message.warn_node(func,
1172                     '%s: Constructors must belong to the same namespace as the '
1173                     'class they belong to' % (func.symbol, ))
1174             return False
1175         # If it takes the object as a first arg, guess it's not a constructor
1176         if not func.is_constructor and len(func.parameters) > 0:
1177             first_arg = self._transformer.lookup_typenode(func.parameters[0].type)
1178             if (first_arg is not None) and first_arg.gi_name == origin_node.gi_name:
1179                 return False
1180
1181         if isinstance(target, ast.Class):
1182             parent = origin_node
1183             while parent and (not parent.gi_name == 'GObject.Object'):
1184                 if parent == target:
1185                     break
1186                 if parent.parent_type:
1187                     parent = self._transformer.lookup_typenode(parent.parent_type)
1188                 else:
1189                     parent = None
1190                 if parent is None:
1191                     message.warn_node(func,
1192                                       "Return value is not superclass for constructor; "
1193                                       "symbol=%r constructed=%r return=%r" %
1194                                       (func.symbol,
1195                                        str(origin_node.create_type()),
1196                                        str(func.retval.type)))
1197                     return False
1198         else:
1199             if origin_node != target:
1200                 message.warn_node(func,
1201                                   "Constructor return type mismatch symbol=%r "
1202                                   "constructed=%r return=%r" %
1203                                   (func.symbol,
1204                                    str(origin_node.create_type()),
1205                                    str(func.retval.type)))
1206                 return False
1207
1208         return True
1209
1210     def _pair_class_virtuals(self, node):
1211         """Look for virtual methods from the class structure."""
1212         if not node.glib_type_struct:
1213             # https://bugzilla.gnome.org/show_bug.cgi?id=629080
1214             #message.warn_node(node,
1215             #    "Failed to find class structure for %r" % (node.name, ))
1216             return
1217
1218         node_type = node.create_type()
1219         class_struct = self._transformer.lookup_typenode(node.glib_type_struct)
1220
1221         # Object class fields are assumed to be read-only
1222         # (see also _introspect_object and transformer.py)
1223         for field in class_struct.fields:
1224             if isinstance(field, ast.Field):
1225                 field.writable = False
1226
1227         for field in class_struct.fields:
1228             if not isinstance(field.anonymous_node, ast.Callback):
1229                 continue
1230             callback = field.anonymous_node
1231             # Check the first parameter is the object
1232             if len(callback.parameters) == 0:
1233                 continue
1234             firstparam_type = callback.parameters[0].type
1235             if firstparam_type != node_type:
1236                 continue
1237             vfunc = ast.VFunction.from_callback(callback)
1238             vfunc.instance_parameter = callback.parameters[0]
1239             vfunc.inherit_file_positions(callback)
1240
1241             prefix = self._get_annotation_name(class_struct)
1242             block = self._blocks.get('%s::%s' % (prefix, vfunc.name))
1243             self._apply_annotations_callable(vfunc, [node], block)
1244             node.virtual_methods.append(vfunc)
1245
1246         # Take the set of virtual methods we found, and try
1247         # to pair up with any matching methods using the
1248         # name+signature.
1249         for vfunc in node.virtual_methods:
1250             for method in node.methods:
1251                 if method.name != vfunc.name:
1252                     continue
1253                 if method.retval.type != vfunc.retval.type:
1254                     continue
1255                 if len(method.parameters) != len(vfunc.parameters):
1256                     continue
1257                 for i in xrange(len(method.parameters)):
1258                     m_type = method.parameters[i].type
1259                     v_type = vfunc.parameters[i].type
1260                     if m_type != v_type:
1261                         continue
1262                 vfunc.invoker = method.name
1263                 # Apply any annotations we have from the invoker to
1264                 # the vfunc
1265                 block = self._blocks.get(method.symbol)
1266                 self._apply_annotations_callable(vfunc, [], block)
1267                 break
1268
1269     def _pass3(self, node, chain):
1270         """Pass 3 is after we've loaded GType data and performed type
1271         closure."""
1272         if isinstance(node, ast.Callable):
1273             self._pass3_callable_callbacks(node)
1274             self._pass3_callable_throws(node)
1275         return True
1276
1277     def _pass3_callable_callbacks(self, node):
1278         """Check to see if we have anything that looks like a
1279         callback+user_data+GDestroyNotify set."""
1280
1281         params = node.parameters
1282
1283         # First, do defaults for well-known callback types
1284         for param in params:
1285             argnode = self._transformer.lookup_typenode(param.type)
1286             if isinstance(argnode, ast.Callback):
1287                 if param.type.target_giname in ('Gio.AsyncReadyCallback',
1288                                                 'GLib.DestroyNotify'):
1289                     param.scope = ast.PARAM_SCOPE_ASYNC
1290                     param.transfer = ast.PARAM_TRANSFER_NONE
1291
1292         callback_param = None
1293         for param in params:
1294             argnode = self._transformer.lookup_typenode(param.type)
1295             is_destroynotify = False
1296             if isinstance(argnode, ast.Callback):
1297                 if param.type.target_giname == 'GLib.DestroyNotify':
1298                     is_destroynotify = True
1299                 else:
1300                     callback_param = param
1301                     continue
1302             if callback_param is None:
1303                 continue
1304             if is_destroynotify:
1305                 callback_param.destroy_name = param.argname
1306                 callback_param.scope = ast.PARAM_SCOPE_NOTIFIED
1307                 callback_param.transfer = ast.PARAM_TRANSFER_NONE
1308             elif (param.type.is_equiv(ast.TYPE_ANY) and
1309                   param.argname is not None and
1310                   param.argname.endswith('data')):
1311                 callback_param.closure_name = param.argname
1312
1313     def _pass3_callable_throws(self, node):
1314         """Check to see if we have anything that looks like a
1315         callback+user_data+GDestroyNotify set."""
1316         if not node.parameters:
1317             return
1318         last_param = node.parameters[-1]
1319         # Checking type.name=='GLib.Error' generates false positives
1320         # on methods that take a 'GError *'
1321         if last_param.type.ctype == 'GError**':
1322             node.parameters.pop()
1323             node.throws = True