2 # Copyright (C) 2010 Red Hat, Inc.
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.
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.
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.
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,
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
42 class MainTransformer(object):
44 def __init__(self, transformer, blocks):
45 self._transformer = transformer
47 self._namespace = transformer.namespace
48 self._uscore_type_names = {}
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
60 # Some initial namespace surgery
61 self._namespace.walk(self._pass_fixup_hidden_fields)
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)
68 # Read in annotations needed early
69 self._namespace.walk(self._pass_read_annotations_early)
71 # Determine some default values for transfer etc.
72 # based on the current tree.
73 self._namespace.walk(self._pass_callable_defaults)
75 # Read in most annotations now.
76 self._namespace.walk(self._pass_read_annotations)
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)
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
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)
97 # Some annotations need to be post function pairing
98 self._namespace.walk(self._pass_read_annotations2)
100 # Another type resolution pass after we've parsed virtuals, etc.
101 self._namespace.walk(self._pass_type_resolution)
103 self._namespace.walk(self._pass3)
105 # TODO - merge into pass3
106 self._pair_quarks_with_enums()
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)):
116 for field in node.fields:
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
125 def _get_validate_parameter_name(self, parent, param_name, origin):
127 param = parent.get_parameter(param_name)
131 if isinstance(origin, ast.Parameter):
132 origin_name = 'parameter %s' % (origin.argname, )
134 origin_name = 'return value'
136 message.FATAL, parent,
137 "can't find parameter %s referenced by %s of %r"
138 % (param_name, origin_name, parent.name))
142 def _apply_annotation_rename_to(self, node, chain, block):
145 rename_to = block.get_tag(TAG_RENAME_TO)
148 rename_to = rename_to.value
149 target = self._namespace.get_by_symbol(rename_to)
151 message.warn_node(node,
152 "Can't find symbol %r referenced by Rename annotation" % (
154 elif target.shadowed_by:
155 message.warn_node(node,
156 "Function %r already shadowed by %r, can't overwrite with %r" % (
161 message.warn_node(node,
162 "Function %r already shadows %r, can't multiply shadow with %r" % (
167 target.shadowed_by = node.name
168 node.shadows = target.name
170 def _apply_annotations_function(self, node, chain):
171 block = self._blocks.get(node.symbol)
172 self._apply_annotations_callable(node, chain, block)
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)
179 block = self._blocks.get(node.c_name)
180 self._apply_annotations_annotated(node, block)
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)
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:
198 elif isinstance(node, ast.Registered) and node.gtype_name is not None:
199 return node.gtype_name
201 raise AssertionError("Unhandled node %r" % (node, ))
203 def _get_block(self, node):
204 return self._blocks.get(self._get_annotation_name(node))
206 def _pass_read_annotations(self, node, chain):
207 if not node.namespace:
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)
228 node.doc = block.comment if block.comment else ''
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)
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)
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
254 self._apply_annotations_array(parent, node, options)
255 elif has_element_type:
256 self._apply_annotations_element_type(parent, node, options)
258 if isinstance(node.type, ast.Array):
259 self._check_array_element_type(node.type, options)
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)
274 sep, rest = rest[0], rest[1:]
277 return top_combiner(*args), rest
279 res = self._transformer.create_type_from_user_string(ident)
281 def combiner(base, *rest):
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)
289 "Too many parameters in type specification %r" % (type_str, ))
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)
296 result, rest = grab_one(type_str, resolver, top_combiner, combiner)
298 message.warn("Trailing components in type specification %r" % (
301 if not result.resolved:
303 if parent is not None and isinstance(parent, ast.Function):
305 position = self._get_position(parent, node)
308 message.warn_node(parent, "%s: Unknown type: %r" %
309 (text, result.ctype), positions=position)
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
321 def _get_position(self, func, param):
322 block = self._blocks.get(func.symbol)
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)
334 return block.position
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)
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,
355 message.warn("invalid (element-type) for a GByteArray, "
356 "must be one of guint8, gint8 or gchar",
359 def _apply_annotations_array(self, parent, node, options):
360 array_opt = options.get(OPT_ARRAY)
362 array_values = array_opt.all()
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
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]
381 if isinstance(node.type, ast.Array):
382 array_type = node.type.array_type
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'
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)
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)
405 container_type.size = int(fixed)
407 # Already warned in annotationparser.py
409 node.type = container_type
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:
415 'element-type annotation takes at least one option, '
420 if isinstance(node.type, ast.List):
421 if element_type_opt.length() != 1:
423 'element-type annotation for a list must have exactly '
424 'one option, not %d options' % (element_type_opt.length(), ),
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:
432 'element-type annotation for a hash table must have exactly '
433 'two options, not %d option(s)' % (element_type_opt.length(), ),
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:
444 'element-type annotation for an array must have exactly '
445 'one option, not %d options' % (element_type_opt.length(), ),
448 node.type.element_type = self._resolve(element_type_opt.one(),
449 node.type, node, parent)
451 message.warn_node(parent,
452 "Unknown container %r for element-type annotation" % (node.type, ))
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
462 def _get_transfer_default_returntype_basic(self, typeval):
463 if (typeval.is_equiv(ast.BASIC_GIR_TYPES)
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
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)
482 if cls.parent and cls.parent.target_giname != 'GObject.Object':
483 return self._is_gi_subclass(cls.parent, supercls_type)
486 def _get_transfer_default_return(self, parent, node):
488 basic = self._get_transfer_default_returntype_basic(typeval)
491 if not typeval.target_giname:
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
510 return ast.PARAM_TRANSFER_FULL
511 elif isinstance(target, (ast.Record, ast.Union)):
512 return ast.PARAM_TRANSFER_FULL
514 raise AssertionError("Invalid constructor")
515 elif isinstance(target, (ast.Class, ast.Record, ast.Union)):
516 # Explicitly no default for these
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
533 raise AssertionError(node)
535 def _apply_annotations_param_ret_common(self, parent, node, tag):
536 options = getattr(tag, 'options', {})
538 param_type = options.get(OPT_TYPE)
540 node.type = self._resolve_toplevel(param_type.one(),
541 node.type, node, parent)
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)
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
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)
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
582 self._adjust_container_type(parent, node, options)
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
589 if tag is not None and tag.comment is not None:
590 node.doc = tag.comment
592 if OPT_SKIP in options:
596 for attribute in options.getall(OPT_ATTRIBUTE):
597 node.attributes.append(attribute.flat())
599 def _apply_annotations_annotated(self, node, block):
603 node.doc = block.comment if block.comment else ''
605 since_tag = block.get_tag(TAG_SINCE)
606 if since_tag is not None:
607 node.version = since_tag.value
609 deprecated_tag = block.get_tag(TAG_DEPRECATED)
610 if deprecated_tag is not None:
611 value = deprecated_tag.value
613 colon = value.find(': ')
614 version = value[:colon]
615 desc = value[colon+2:]
619 node.deprecated = desc
620 if version is not None:
621 node.deprecated_version = version
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
629 message.warn('unknown value "%s" for Stability tag' % (
630 stability_tag.value), stability_tag.position)
632 annos_tag = block.get_tag(TAG_ATTRIBUTES)
633 if annos_tag is not None:
634 for key, value in annos_tag.options.items():
636 node.attributes.append((key, value.one()))
638 if OPT_SKIP in block.options:
641 if OPT_FOREIGN in block.options:
644 if OPT_CONSTRUCTOR in block.options and isinstance(node, ast.Function):
645 node.is_constructor = True
647 if OPT_METHOD in block.options:
648 node.is_method = True
650 def _apply_annotations_alias(self, node, chain):
651 block = self._get_block(node)
652 self._apply_annotations_annotated(node, block)
654 def _apply_annotations_param(self, parent, param, tag):
656 options = tag.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
665 destroy = options.get(OPT_DESTROY)
667 param.destroy_name = self._get_validate_parameter_name(parent,
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,
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
690 self._apply_annotations_param_ret_common(parent, param, tag)
692 def _apply_annotations_return(self, parent, return_, block):
694 tag = block.get_tag(TAG_RETURNS)
697 self._apply_annotations_param_ret_common(parent, return_, tag)
699 def _apply_annotations_params(self, parent, params, block):
701 if parent.instance_parameter:
702 declparams.add(parent.instance_parameter.argname)
705 tag = block.get_param(param.argname)
708 self._apply_annotations_param(parent, param, tag)
709 declparams.add(param.argname)
713 docparams = set(block.params)
715 unknown = docparams - declparams
716 unused = declparams - docparams
718 for doc_name in unknown:
719 # Skip varargs, see #629759
720 if doc_name.lower() in ['...', 'varargs', TAG_RETURNS]:
724 elif len(unused) == 1:
726 text = ', should be %r' % (param, )
728 text = ', should be one of %s' % (
729 ', '.join(repr(p) for p in unused), )
731 tag = block.get_param(doc_name)
733 '%s: unknown parameter %r in documentation comment%s' % (
734 block.name, doc_name, text),
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)
742 def _check_arg_annotations(self, parent, params, block):
745 for tag in block.tags.keys():
746 if tag == TAG_RETURNS:
749 if param.argname == tag:
753 "Annotation for '%s' refers to unknown argument '%s'"
754 % (parent.name, tag))
756 def _apply_annotations_field(self, parent, block, field):
759 tag = block.get_param(field.name)
762 t = tag.options.get(OPT_TYPE)
764 field.type = self._transformer.create_type_from_user_string(t.one())
767 self._adjust_container_type(parent, field, tag.options)
768 except AttributeError:
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)
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
784 prop.transfer = self._get_transfer_default(parent, prop)
785 type_tag = block.get_tag(TAG_TYPE)
787 prop.type = self._resolve_toplevel(type_tag.value, prop.type, prop, parent)
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]
804 for i, param in enumerate(signal.parameters):
806 name, tag = names[i+1]
807 options = getattr(tag, 'options', {})
808 param_type = options.get(OPT_TYPE)
810 param.type = self._resolve_toplevel(param_type.one(), param.type,
814 self._apply_annotations_param(signal, param, tag)
815 self._apply_annotations_return(signal, signal.retval, block)
817 def _apply_annotations_constant(self, node):
818 block = self._blocks.get(node.ctype)
821 tag = block.get_tag(TAG_VALUE)
823 node.value = tag.value
825 def _apply_annotations_enum_members(self, node, block):
829 for m in node.members:
830 tag = block.params.get(m.symbol, None)
834 def _pass_read_annotations2(self, node, chain):
835 if isinstance(node, ast.Function):
836 self._apply_annotations2_function(node, chain)
839 def _apply_annotations2_function(self, node, chain):
840 block = self._blocks.get(node.symbol)
842 self._apply_annotation_rename_to(node, chain, block)
844 # Handle virtual invokers
845 parent = chain[-1] if chain else None
846 if not (block and parent):
848 virtual = block.get_tag(TAG_VFUNC)
851 invoker_name = virtual.value
853 for vfunc in parent.virtual_methods:
854 if vfunc.name == invoker_name:
856 vfunc.invoker = node.name
857 # Also merge in annotations
858 self._apply_annotations_callable(vfunc, [parent], block)
861 message.warn_node(node,
862 "Virtual slot %r not found for %r annotation" % (invoker_name, TAG_VFUNC))
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)
872 new_typelist.remove(typeval)
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:
889 self._transformer.resolve_type(field.type)
890 if isinstance(node, (ast.Class, ast.Interface)):
891 for parent in node.parent_chain:
893 self._transformer.resolve_type(parent)
896 target = self._transformer.lookup_typenode(parent)
901 if isinstance(node, ast.Interface):
902 node.parent = ast.Type(target_giname='GObject.Object')
903 for prop in node.properties:
904 self._transformer.resolve_type(prop.type)
905 for sig in node.signals:
906 for param in sig.parameters:
907 self._transformer.resolve_type(param.type)
908 if isinstance(node, ast.Class):
909 node.interfaces = self._resolve_and_filter_type_list(node.interfaces)
910 if isinstance(node, ast.Interface):
911 node.prerequisites = self._resolve_and_filter_type_list(node.prerequisites)
914 def _pair_quarks_with_enums(self):
915 # self._uscore_type_names is an authoritative mapping of types
916 # to underscored versions, since it is based on get_type() methods;
917 # but only covers enums that are registered as GObject enums.
918 # Create a fallback mapping based on all known enums in this module.
920 for enum in self._namespace.itervalues():
921 if not isinstance(enum, ast.Enum):
923 uscored = to_underscores_noprefix(enum.name).lower()
924 uscore_enums[uscored] = enum
925 uscore_enums[enum.name] = enum
927 for node in self._namespace.itervalues():
928 if not isinstance(node, ast.ErrorQuarkFunction):
930 full = node.symbol[:-len('_quark')]
931 ns, short = self._transformer.split_csymbol(node.symbol)
932 short = short[:-len('_quark')]
933 if full == "g_io_error":
934 # Special case; GIOError was already taken forcing GIOErrorEnum
935 assert self._namespace.name == 'Gio'
936 enum = self._namespace.get('IOErrorEnum')
938 enum = self._uscore_type_names.get(short)
940 enum = uscore_enums.get(short)
942 enum.error_domain = node.error_domain
944 message.warn_node(node,
945 """%s: Couldn't find corresponding enumeration""" % (node.symbol, ))
947 def _split_uscored_by_type(self, uscored):
948 """'uscored' should be an un-prefixed uscore string. This
949 function searches through the namespace for the longest type which
950 prefixes uscored, and returns (type, suffix). Example, assuming
951 namespace Gtk, type is TextBuffer:
953 _split_uscored_by_type(text_buffer_try_new) -> (ast.Class(TextBuffer), 'try_new')"""
956 prev_split_count = -1
958 components = uscored.rsplit('_', count)
959 if len(components) == prev_split_count:
961 prev_split_count = len(components)
962 type_string = components[0]
963 node = self._uscore_type_names.get(type_string)
965 return (node, '_'.join(components[1:]))
968 def _pair_function(self, func):
969 """Check to see whether a toplevel function should be a
970 method or constructor of some type."""
972 # Ignore internal symbols and type metadata functions
973 if func.symbol.startswith('_') or func.is_type_meta_function():
976 (ns, subsymbol) = self._transformer.split_csymbol(func.symbol)
977 assert ns == self._namespace
978 if self._is_constructor(func, subsymbol):
979 self._set_up_constructor(func, subsymbol)
981 elif self._is_method(func, subsymbol):
982 self._setup_method(func, subsymbol)
984 elif self._pair_static_method(func, subsymbol):
987 def _uscored_identifier_for_type(self, typeval):
988 """Given a Type(target_giname='Foo.BarBaz'), return 'bar_baz'."""
989 name = typeval.get_giname()
990 return to_underscores_noprefix(name).lower()
992 def _is_method(self, func, subsymbol):
993 if not func.parameters:
995 message.warn_node(func,
996 '%s: Methods must have parameters' % (func.symbol, ))
998 first = func.parameters[0]
999 target = self._transformer.lookup_typenode(first.type)
1000 if not isinstance(target, (ast.Class, ast.Interface,
1001 ast.Record, ast.Union,
1004 message.warn_node(func,
1005 '%s: Methods must have a pointer as their first '
1006 'parameter' % (func.symbol, ))
1008 if target.namespace != self._namespace:
1010 message.warn_node(func,
1011 '%s: Methods must belong to the same namespace as the '
1012 'class they belong to' % (func.symbol, ))
1014 if first.direction == ast.PARAM_DIRECTION_OUT:
1016 message.warn_node(func,
1017 '%s: The first argument of methods cannot be an '
1018 'out-argument' % (func.symbol, ))
1021 # A quick hack here...in the future we should catch C signature/GI signature
1022 # mismatches in a general way in finaltransformer
1023 if first.type.ctype is not None and first.type.ctype.count('*') > 1:
1026 if not func.is_method:
1027 uscored_prefix = self._get_uscored_prefix(func, subsymbol)
1028 if not subsymbol.startswith(uscored_prefix):
1033 def _setup_method(self, func, subsymbol):
1034 uscored_prefix = self._get_uscored_prefix(func, subsymbol)
1035 target = self._transformer.lookup_typenode(func.parameters[0].type)
1037 func.instance_parameter = func.parameters.pop(0)
1038 self._namespace.float(func)
1040 if not func.is_method:
1041 subsym_idx = func.symbol.find(subsymbol)
1042 func.name = func.symbol[(subsym_idx + len(uscored_prefix) + 1):]
1043 func.is_method = True
1045 target.methods.append(func)
1047 def _get_uscored_prefix(self, func, subsymbol):
1048 # Here we check both the c_symbol_prefix and (if that fails),
1049 # attempt to do a default uscoring of the type. The reason we
1050 # look at a default underscore transformation is for
1051 # gdk_window_object_get_type(), which says to us that the
1052 # prefix is "gdk_window_object", when really it's just
1053 # "gdk_window". Possibly need an annotation to override this.
1054 prefix_matches = False
1055 uscored_prefix = None
1056 first_arg = func.parameters[0]
1057 target = self._transformer.lookup_typenode(first_arg.type)
1058 if hasattr(target, 'c_symbol_prefix') and target.c_symbol_prefix is not None:
1059 prefix_matches = subsymbol.startswith(target.c_symbol_prefix)
1061 uscored_prefix = target.c_symbol_prefix
1062 if not prefix_matches:
1063 uscored_prefix = self._uscored_identifier_for_type(first_arg.type)
1065 return uscored_prefix
1067 def _pair_static_method(self, func, subsymbol):
1068 split = self._split_uscored_by_type(subsymbol)
1071 (node, funcname) = split
1075 if isinstance(node, ast.Class):
1076 self._namespace.float(func)
1077 func.name = funcname
1078 node.static_methods.append(func)
1080 elif isinstance(node, (ast.Interface, ast.Record, ast.Union,
1081 ast.Boxed, ast.Enum, ast.Bitfield)):
1082 # prior to the introduction of this part of the code, only
1083 # ast.Class could have static methods. so for backwards
1084 # compatibility, instead of removing the func from the namespace,
1085 # leave it there and get a copy instead. modify the copy and push
1086 # it onto static_methods. we need to copy the parameters list
1087 # separately, because in the third pass functions are flagged as
1088 # 'throws' depending on the presence of a GError parameter which is
1089 # then removed from the parameters list. without the explicit
1090 # copy, only one of the two functions would thus get flagged as
1091 # 'throws'. clone() does this for us.
1092 new_func = func.clone()
1093 new_func.name = funcname
1094 node.static_methods.append(new_func)
1095 # flag the func as a backwards-comptability kludge (thus it will
1096 # get pruned in the introspectable pass if introspectable=0).
1097 func.moved_to = node.name + '.' + new_func.name
1102 def _set_up_constructor(self, func, subsymbol):
1103 self._namespace.float(func)
1105 func.name = self._get_constructor_name(func, subsymbol)
1107 origin_node = self._get_constructor_class(func, subsymbol)
1108 origin_node.constructors.append(func)
1110 func.is_constructor = True
1112 # Constructors have default return semantics
1113 if not func.retval.transfer:
1114 func.retval.transfer = self._get_transfer_default_return(func,
1117 def _get_constructor_class(self, func, subsymbol):
1119 split = self._split_uscored_by_type(subsymbol)
1121 if func.is_constructor:
1122 origin_node = self._transformer.lookup_typenode(func.retval.type)
1124 origin_node, _ = split
1128 def _get_constructor_name(self, func, subsymbol):
1130 split = self._split_uscored_by_type(subsymbol)
1132 if func.is_constructor:
1139 def _guess_constructor_by_name(self, symbol):
1140 # Normal constructors, gtk_button_new etc
1141 if symbol.endswith('_new'):
1143 # Alternative constructor, gtk_button_new_with_label
1144 if '_new_' in symbol:
1146 # gtk_list_store_newv,gtk_tree_store_newv etc
1147 if symbol.endswith('_newv'):
1151 def _is_constructor(self, func, subsymbol):
1152 # func.is_constructor will be True if we have a (constructor) annotation
1153 if not func.is_constructor:
1154 if not self._guess_constructor_by_name(func.symbol):
1156 target = self._transformer.lookup_typenode(func.retval.type)
1157 if not (isinstance(target, ast.Class)
1158 or (isinstance(target, (ast.Record, ast.Union, ast.Boxed))
1159 and (target.get_type is not None or target.foreign))):
1160 if func.is_constructor:
1161 message.warn_node(func,
1162 '%s: Constructors must return an instance of their class'
1166 origin_node = self._get_constructor_class(func, subsymbol)
1167 if origin_node is None:
1168 if func.is_constructor:
1169 message.warn_node(func,
1170 "Can't find matching type for constructor; symbol=%r" \
1174 # Some sanity checks; only objects and boxeds can have ctors
1175 if not (isinstance(origin_node, ast.Class)
1176 or (isinstance(origin_node, (ast.Record, ast.Union, ast.Boxed))
1177 and (origin_node.get_type is not None or origin_node.foreign))):
1179 # Verify the namespace - don't want to append to foreign namespaces!
1180 if origin_node.namespace != self._namespace:
1181 if func.is_constructor:
1182 message.warn_node(func,
1183 '%s: Constructors must belong to the same namespace as the '
1184 'class they belong to' % (func.symbol, ))
1186 # If it takes the object as a first arg, guess it's not a constructor
1187 if not func.is_constructor and len(func.parameters) > 0:
1188 first_arg = self._transformer.lookup_typenode(func.parameters[0].type)
1189 if (first_arg is not None) and first_arg.gi_name == origin_node.gi_name:
1192 if isinstance(target, ast.Class):
1193 parent = origin_node
1194 while parent and (not parent.gi_name == 'GObject.Object'):
1195 if parent == target:
1198 parent = self._transformer.lookup_typenode(parent.parent)
1202 message.warn_node(func,
1203 "Return value is not superclass for constructor; "
1204 "symbol=%r constructed=%r return=%r" % (
1206 str(origin_node.create_type()),
1207 str(func.retval.type)))
1210 if origin_node != target:
1211 message.warn_node(func,
1212 "Constructor return type mismatch symbol=%r "
1213 "constructed=%r return=%r" % (
1215 str(origin_node.create_type()),
1216 str(func.retval.type)))
1221 def _pair_class_virtuals(self, node):
1222 """Look for virtual methods from the class structure."""
1223 if not node.glib_type_struct:
1224 # https://bugzilla.gnome.org/show_bug.cgi?id=629080
1225 #message.warn_node(node,
1226 # "Failed to find class structure for %r" % (node.name, ))
1229 node_type = node.create_type()
1230 class_struct = self._transformer.lookup_typenode(node.glib_type_struct)
1232 # Object class fields are assumed to be read-only
1233 # (see also _introspect_object and transformer.py)
1234 for field in class_struct.fields:
1235 if isinstance(field, ast.Field):
1236 field.writable = False
1238 for field in class_struct.fields:
1239 if not isinstance(field.anonymous_node, ast.Callback):
1241 callback = field.anonymous_node
1242 # Check the first parameter is the object
1243 if len(callback.parameters) == 0:
1245 firstparam_type = callback.parameters[0].type
1246 if firstparam_type != node_type:
1248 vfunc = ast.VFunction.from_callback(callback)
1249 vfunc.instance_parameter = callback.parameters[0]
1250 vfunc.inherit_file_positions(callback)
1252 prefix = self._get_annotation_name(class_struct)
1253 block = self._blocks.get('%s::%s' % (prefix, vfunc.name))
1254 self._apply_annotations_callable(vfunc, [node], block)
1255 node.virtual_methods.append(vfunc)
1257 # Take the set of virtual methods we found, and try
1258 # to pair up with any matching methods using the
1260 for vfunc in node.virtual_methods:
1261 for method in node.methods:
1262 if method.name != vfunc.name:
1264 if method.retval.type != vfunc.retval.type:
1266 if len(method.parameters) != len(vfunc.parameters):
1268 for i in xrange(len(method.parameters)):
1269 m_type = method.parameters[i].type
1270 v_type = vfunc.parameters[i].type
1271 if m_type != v_type:
1273 vfunc.invoker = method.name
1274 # Apply any annotations we have from the invoker to
1276 block = self._blocks.get(method.symbol)
1277 self._apply_annotations_callable(vfunc, [], block)
1280 def _pass3(self, node, chain):
1281 """Pass 3 is after we've loaded GType data and performed type
1283 if isinstance(node, ast.Callable):
1284 self._pass3_callable_callbacks(node)
1285 self._pass3_callable_throws(node)
1288 def _pass3_callable_callbacks(self, node):
1289 """Check to see if we have anything that looks like a
1290 callback+user_data+GDestroyNotify set."""
1292 params = node.parameters
1294 # First, do defaults for well-known callback types
1295 for param in params:
1296 argnode = self._transformer.lookup_typenode(param.type)
1297 if isinstance(argnode, ast.Callback):
1298 if param.type.target_giname in ('Gio.AsyncReadyCallback',
1299 'GLib.DestroyNotify'):
1300 param.scope = ast.PARAM_SCOPE_ASYNC
1301 param.transfer = ast.PARAM_TRANSFER_NONE
1303 callback_param = None
1304 for param in params:
1305 argnode = self._transformer.lookup_typenode(param.type)
1306 is_destroynotify = False
1307 if isinstance(argnode, ast.Callback):
1308 if param.type.target_giname == 'GLib.DestroyNotify':
1309 is_destroynotify = True
1311 callback_param = param
1313 if callback_param is None:
1315 if is_destroynotify:
1316 callback_param.destroy_name = param.argname
1317 callback_param.scope = ast.PARAM_SCOPE_NOTIFIED
1318 callback_param.transfer = ast.PARAM_TRANSFER_NONE
1319 elif (param.type.is_equiv(ast.TYPE_ANY) and
1320 param.argname is not None and
1321 param.argname.endswith('data')):
1322 callback_param.closure_name = param.argname
1324 def _pass3_callable_throws(self, node):
1325 """Check to see if we have anything that looks like a
1326 callback+user_data+GDestroyNotify set."""
1327 if not node.parameters:
1329 last_param = node.parameters[-1]
1330 # Checking type.name=='GLib.Error' generates false positives
1331 # on methods that take a 'GError *'
1332 if last_param.type.ctype == 'GError**':
1333 node.parameters.pop()