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