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)
28 from .annotationparser import (OPT_ALLOW_NONE, OPT_ARRAY, OPT_ATTRIBUTE,
29 OPT_ELEMENT_TYPE, OPT_IN, OPT_INOUT,
30 OPT_INOUT_ALT, OPT_OUT, OPT_SCOPE,
31 OPT_OUT_CALLER_ALLOCATES, OPT_OUT_CALLEE_ALLOCATES,
32 OPT_TYPE, OPT_CLOSURE, OPT_DESTROY, OPT_TRANSFER, OPT_SKIP,
33 OPT_FOREIGN, OPT_ARRAY_FIXED_SIZE,
34 OPT_ARRAY_LENGTH, OPT_ARRAY_ZERO_TERMINATED,
35 OPT_CONSTRUCTOR, OPT_METHOD,
36 OPT_TRANSFER_NONE, OPT_TRANSFER_FLOATING)
37 from .annotationparser import AnnotationParser
38 from .transformer import TransformerException
39 from .utils import to_underscores, to_underscores_noprefix
41 class MainTransformer(object):
43 def __init__(self, transformer, blocks):
44 self._transformer = transformer
46 self._namespace = transformer.namespace
47 self._uscore_type_names = {}
52 contents = list(self._namespace.itervalues())
53 if len(contents) == 0:
54 message.fatal("""Namespace is empty; likely causes are:
55 * Not including .h files to be scanned
56 * Broken --identifier-prefix
59 # Some initial namespace surgery
60 self._namespace.walk(self._pass_fixup_hidden_fields)
62 # We have a rough tree which should have most of of the types
63 # we know about. Let's attempt closure; walk over all of the
64 # Type() types and see if they match up with something.
65 self._namespace.walk(self._pass_type_resolution)
67 # Read in annotations needed early
68 self._namespace.walk(self._pass_read_annotations_early)
70 # Determine some default values for transfer etc.
71 # based on the current tree.
72 self._namespace.walk(self._pass_callable_defaults)
74 # Read in most annotations now.
75 self._namespace.walk(self._pass_read_annotations)
77 # Now that we've possibly seen more types from annotations,
78 # do another type resolution pass.
79 self._namespace.walk(self._pass_type_resolution)
81 # Generate a reverse mapping "bar_baz" -> BarBaz
82 for node in self._namespace.itervalues():
83 if isinstance(node, ast.Registered) and node.get_type is not None:
84 self._uscore_type_names[node.c_symbol_prefix] = node
85 elif isinstance(node, (ast.Record, ast.Union)):
86 uscored = to_underscores_noprefix(node.name).lower()
87 self._uscore_type_names[uscored] = node
89 for node in list(self._namespace.itervalues()):
90 if isinstance(node, ast.Function):
91 # Discover which toplevel functions are actually methods
92 self._pair_function(node)
93 if isinstance(node, (ast.Class, ast.Interface)):
94 self._pair_class_virtuals(node)
96 # Some annotations need to be post function pairing
97 self._namespace.walk(self._pass_read_annotations2)
99 # Another type resolution pass after we've parsed virtuals, etc.
100 self._namespace.walk(self._pass_type_resolution)
102 self._namespace.walk(self._pass3)
104 # TODO - merge into pass3
105 self._pair_quarks_with_enums()
109 def _pass_fixup_hidden_fields(self, node, chain):
110 """Hide all callbacks starting with _; the typical
111 usage is void (*_gtk_reserved1)(void);"""
112 if not isinstance(node, (ast.Class, ast.Interface,
113 ast.Record, ast.Union)):
115 for field in node.fields:
118 if (field.name.startswith('_')
119 and field.anonymous_node is not None
120 and isinstance(field.anonymous_node, ast.Callback)):
121 field.introspectable = False
124 def _get_validate_parameter_name(self, parent, param_name, origin):
126 param = parent.get_parameter(param_name)
127 except ValueError, e:
130 if isinstance(origin, ast.Parameter):
131 origin_name = 'parameter %s' % (origin.argname, )
133 origin_name = 'return value'
135 message.FATAL, parent,
136 "can't find parameter %s referenced by %s of %r"
137 % (param_name, origin_name, parent.name))
141 def _apply_annotation_rename_to(self, node, chain, block):
144 rename_to = block.get(TAG_RENAME_TO)
147 rename_to = rename_to.value
148 target = self._namespace.get_by_symbol(rename_to)
150 message.warn_node(node,
151 "Can't find symbol %r referenced by Rename annotation" % (
153 elif target.shadowed_by:
154 message.warn_node(node,
155 "Function %r already shadowed by %r, can't overwrite with %r" % (
160 message.warn_node(node,
161 "Function %r already shadows %r, can't multiply shadow with %r" % (
166 target.shadowed_by = node.name
167 node.shadows = target.name
169 def _apply_annotations_function(self, node, chain):
170 block = self._blocks.get(node.symbol)
171 self._apply_annotations_callable(node, chain, block)
173 def _pass_read_annotations_early(self, node, chain):
174 if isinstance(node, ast.Record):
175 if node.ctype is not None:
176 block = self._blocks.get(node.ctype)
178 block = self._blocks.get(node.c_name)
179 self._apply_annotations_annotated(node, block)
182 def _pass_callable_defaults(self, node, chain):
183 if isinstance(node, (ast.Callable, ast.Signal)):
184 for param in node.parameters:
185 if param.transfer is None:
186 param.transfer = self._get_transfer_default(node, param)
187 if node.retval.transfer is None:
188 node.retval.transfer = self._get_transfer_default(node, node.retval)
191 def _get_annotation_name(self, node):
192 if isinstance(node, (ast.Class, ast.Interface, ast.Record,
193 ast.Union, ast.Enum, ast.Bitfield,
194 ast.Callback, ast.Alias)):
195 if node.ctype is not None:
197 elif isinstance(node, ast.Registered) and node.gtype_name is not None:
198 return node.gtype_name
200 raise AssertionError("Unhandled node %r" % (node, ))
202 def _get_block(self, node):
203 return self._blocks.get(self._get_annotation_name(node))
205 def _pass_read_annotations(self, node, chain):
206 if not node.namespace:
208 if isinstance(node, ast.Alias):
209 self._apply_annotations_alias(node, chain)
210 if isinstance(node, ast.Function):
211 self._apply_annotations_function(node, chain)
212 if isinstance(node, ast.Callback):
213 self._apply_annotations_callable(node, chain, block = self._get_block(node))
214 if isinstance(node, (ast.Class, ast.Interface, ast.Union, ast.Enum,
215 ast.Bitfield, ast.Callback)):
216 self._apply_annotations_annotated(node, self._get_block(node))
217 if isinstance(node, (ast.Class, ast.Interface, ast.Record, ast.Union)):
218 block = self._get_block(node)
219 for field in node.fields:
220 self._apply_annotations_field(node, block, field)
221 name = self._get_annotation_name(node)
222 section_name = 'SECTION:' + name.lower()
223 block = self._blocks.get(section_name)
225 node.doc = block.comment
226 if isinstance(node, (ast.Class, ast.Interface)):
227 for prop in node.properties:
228 self._apply_annotations_property(node, prop)
229 for sig in node.signals:
230 self._apply_annotations_signal(node, sig)
231 if isinstance(node, ast.Class):
232 block = self._get_block(node)
234 tag = block.get(TAG_UNREF_FUNC)
235 node.unref_func = tag.value if tag else None
236 tag = block.get(TAG_REF_FUNC)
237 node.ref_func = tag.value if tag else None
238 tag = block.get(TAG_SET_VALUE_FUNC)
239 node.set_value_func = tag.value if tag else None
240 tag = block.get(TAG_GET_VALUE_FUNC)
241 node.get_value_func = tag.value if tag else None
242 if isinstance(node, ast.Constant):
243 self._apply_annotations_constant(node)
246 def _adjust_container_type(self, parent, node, options):
247 has_element_type = OPT_ELEMENT_TYPE in options
248 has_array = OPT_ARRAY in options
251 self._apply_annotations_array(parent, node, options)
252 elif has_element_type:
253 self._apply_annotations_element_type(parent, node, options)
255 if isinstance(node.type, ast.Array):
256 self._check_array_element_type(node.type, options)
258 def _resolve(self, type_str, type_node=None, node=None, parent=None):
259 def grab_one(type_str, resolver, top_combiner, combiner):
260 """Return a complete type, and the trailing string part after it.
261 Use resolver() on each identifier, and combiner() on the parts of
262 each complete type. (top_combiner is used on the top-most type.)"""
263 bits = re.split(r'([,<>()])', type_str, 1)
264 first, sep, rest = [bits[0], '', ''] if (len(bits)==1) else bits
265 args = [resolver(first)]
266 if sep == '<' or sep == '(':
267 lastsep = '>' if (sep == '<') else ')'
268 while sep != lastsep:
269 next, rest = grab_one(rest, resolver, combiner, combiner)
271 sep, rest = rest[0], rest[1:]
274 return top_combiner(*args), rest
276 res = self._transformer.create_type_from_user_string(ident)
278 def combiner(base, *rest):
281 if isinstance(base, ast.List) and len(rest) == 1:
282 return ast.List(base.name, *rest)
283 if isinstance(base, ast.Map) and len(rest) == 2:
284 return ast.Map(*rest)
286 "Too many parameters in type specification %r" % (type_str, ))
288 def top_combiner(base, *rest):
289 if type_node is not None and isinstance(type_node, ast.Type):
290 base.is_const = type_node.is_const
291 return combiner(base, *rest)
293 result, rest = grab_one(type_str, resolver, top_combiner, combiner)
295 message.warn("Trailing components in type specification %r" % (
298 if not result.resolved:
300 if parent is not None and isinstance(parent, ast.Function):
302 position = self._get_position(parent, node)
305 message.warn_node(parent, "%s: Unknown type: %r" %
306 (text, result.ctype), positions=position)
309 def _resolve_toplevel(self, type_str, type_node=None, node=None, parent=None):
310 """Like _resolve(), but attempt to preserve more attributes of original type."""
311 result = self._resolve(type_str, type_node=type_node, node=node, parent=parent)
312 # If we replace a node with a new type (such as an annotated) we
313 # might lose the ctype from the original node.
314 if type_node is not None:
315 result.ctype = type_node.ctype
318 def _get_position(self, func, param):
319 block = self._blocks.get(func.symbol)
321 if isinstance(param, ast.Parameter):
322 tag = block.tags.get(param.argname)
323 elif isinstance(param, ast.Return):
324 tag = block.tags.get(TAG_RETURNS)
331 return block.position
333 def _check_array_element_type(self, array, options):
334 # GPtrArrays are allowed to contain non basic types
335 # (except enums and flags) or basic types that are
336 # as big as a gpointer
337 if array.array_type == ast.Array.GLIB_PTRARRAY and \
338 ((array.element_type in ast.BASIC_GIR_TYPES
339 and not array.element_type in ast.POINTER_TYPES) or
340 isinstance(array.element_type, ast.Enum) or
341 isinstance(array.element_type, ast.Bitfield)):
342 message.warn("invalid (element-type) for a GPtrArray, "
343 "must be a pointer", options.position)
345 # GByteArrays have (element-type) guint8 by default
346 if array.array_type == ast.Array.GLIB_BYTEARRAY:
347 if array.element_type == ast.TYPE_ANY:
348 array.element_type = ast.TYPE_UINT8
349 elif not array.element_type in [ast.TYPE_UINT8,
352 message.warn("invalid (element-type) for a GByteArray, "
353 "must be one of guint8, gint8 or gchar",
356 def _apply_annotations_array(self, parent, node, options):
357 array_opt = options.get(OPT_ARRAY)
359 array_values = array_opt.all()
363 element_type = options.get(OPT_ELEMENT_TYPE)
364 if element_type is not None:
365 element_type_node = self._resolve(element_type.one(),
366 node.type, node, parent)
367 elif isinstance(node.type, ast.Array):
368 element_type_node = node.type.element_type
370 # We're assuming here that Foo* with an (array) annotation
371 # and no (element-type) means array of Foo
372 element_type_node = node.type.clone()
373 # The element's ctype is the array's dereferenced
374 if element_type_node.ctype is not None and \
375 element_type_node.ctype.endswith('*'):
376 element_type_node.ctype = element_type_node.ctype[:-1]
378 if isinstance(node.type, ast.Array):
379 array_type = node.type.array_type
382 container_type = ast.Array(array_type, element_type_node,
383 ctype=node.type.ctype,
384 is_const=node.type.is_const)
385 if OPT_ARRAY_ZERO_TERMINATED in array_values:
386 container_type.zeroterminated = array_values.get(
387 OPT_ARRAY_ZERO_TERMINATED) == '1'
389 container_type.zeroterminated = False
390 length = array_values.get(OPT_ARRAY_LENGTH)
391 if length is not None:
392 paramname = self._get_validate_parameter_name(parent, length, node)
394 param = parent.get_parameter(paramname)
395 param.direction = node.direction
396 if param.direction == ast.PARAM_DIRECTION_OUT:
397 param.transfer = ast.PARAM_TRANSFER_FULL
398 container_type.length_param_name = param.argname
399 fixed = array_values.get(OPT_ARRAY_FIXED_SIZE)
402 container_type.size = int(fixed)
404 # Already warned in annotationparser.py
406 node.type = container_type
408 def _apply_annotations_element_type(self, parent, node, options):
409 element_type_opt = options.get(OPT_ELEMENT_TYPE)
410 if element_type_opt is None:
412 'element-type annotation takes at least one option, '
417 if isinstance(node.type, ast.List):
418 if element_type_opt.length() != 1:
420 'element-type annotation for a list must have exactly '
421 'one option, not %d options' % (element_type_opt.length(), ),
424 node.type.element_type = self._resolve(element_type_opt.one(),
425 node.type, node, parent)
426 elif isinstance(node.type, ast.Map):
427 if element_type_opt.length() != 2:
429 'element-type annotation for a hash table must have exactly '
430 'two options, not %d option(s)' % (element_type_opt.length(), ),
433 element_type = element_type_opt.flat()
434 node.type.key_type = self._resolve(element_type[0],
435 node.type, node, parent)
436 node.type.value_type = self._resolve(element_type[1],
437 node.type, node, parent)
438 elif isinstance(node.type, ast.Array):
439 if element_type_opt.length() != 1:
441 'element-type annotation for an array must have exactly '
442 'one option, not %d options' % (element_type_opt.length(), ),
445 node.type.element_type = self._resolve(element_type_opt.one(),
446 node.type, node, parent)
448 message.warn_node(parent,
449 "Unknown container %r for element-type annotation" % (node.type, ))
451 def _get_transfer_default_param(self, parent, node):
452 if node.direction in [ast.PARAM_DIRECTION_INOUT,
453 ast.PARAM_DIRECTION_OUT]:
454 if node.caller_allocates:
455 return ast.PARAM_TRANSFER_NONE
456 return ast.PARAM_TRANSFER_FULL
457 return ast.PARAM_TRANSFER_NONE
459 def _get_transfer_default_returntype_basic(self, typeval):
460 if (typeval.is_equiv(ast.BASIC_GIR_TYPES)
462 or typeval.is_equiv(ast.TYPE_NONE)):
463 return ast.PARAM_TRANSFER_NONE
464 elif typeval.is_equiv(ast.TYPE_STRING):
465 # Non-const strings default to FULL
466 return ast.PARAM_TRANSFER_FULL
467 elif typeval.target_fundamental:
468 # This looks like just GType right now
472 def _is_gi_subclass(self, typeval, supercls_type):
473 cls = self._transformer.lookup_typenode(typeval)
474 assert cls, str(typeval)
475 supercls = self._transformer.lookup_typenode(supercls_type)
479 if cls.parent and cls.parent.target_giname != 'GObject.Object':
480 return self._is_gi_subclass(cls.parent, supercls_type)
483 def _get_transfer_default_return(self, parent, node):
485 basic = self._get_transfer_default_returntype_basic(typeval)
488 if not typeval.target_giname:
490 target = self._transformer.lookup_typenode(typeval)
491 if isinstance(target, ast.Alias):
492 return self._get_transfer_default_returntype_basic(target.target)
493 elif (isinstance(target, ast.Boxed)
494 or (isinstance(target, (ast.Record, ast.Union))
495 and (target.gtype_name is not None or target.foreign))):
496 return ast.PARAM_TRANSFER_FULL
497 elif isinstance(target, (ast.Enum, ast.Bitfield)):
498 return ast.PARAM_TRANSFER_NONE
499 # Handle constructors specially here
500 elif isinstance(parent, ast.Function) and parent.is_constructor:
501 if isinstance(target, ast.Class):
502 initially_unowned_type = ast.Type(target_giname='GObject.InitiallyUnowned')
503 initially_unowned = self._transformer.lookup_typenode(initially_unowned_type)
504 if initially_unowned and self._is_gi_subclass(typeval, initially_unowned_type):
505 return ast.PARAM_TRANSFER_NONE
507 return ast.PARAM_TRANSFER_FULL
508 elif isinstance(target, (ast.Record, ast.Union)):
509 return ast.PARAM_TRANSFER_FULL
511 raise AssertionError("Invalid constructor")
512 elif isinstance(target, (ast.Class, ast.Record, ast.Union)):
513 # Explicitly no default for these
518 def _get_transfer_default(self, parent, node):
519 if node.type.is_equiv(ast.TYPE_NONE) or isinstance(node.type, ast.Varargs):
520 return ast.PARAM_TRANSFER_NONE
521 elif isinstance(node, ast.Parameter):
522 return self._get_transfer_default_param(parent, node)
523 elif isinstance(node, ast.Return):
524 return self._get_transfer_default_return(parent, node)
525 elif isinstance(node, ast.Field):
526 return ast.PARAM_TRANSFER_NONE
527 elif isinstance(node, ast.Property):
528 return ast.PARAM_TRANSFER_NONE
530 raise AssertionError(node)
532 def _apply_annotations_param_ret_common(self, parent, node, tag):
533 options = getattr(tag, 'options', {})
535 param_type = options.get(OPT_TYPE)
537 node.type = self._resolve_toplevel(param_type.one(),
538 node.type, node, parent)
540 caller_allocates = False
541 annotated_direction = None
542 if (OPT_INOUT in options or
543 OPT_INOUT_ALT in options):
544 annotated_direction = ast.PARAM_DIRECTION_INOUT
545 elif OPT_OUT in options:
546 subtype = options[OPT_OUT]
547 if subtype is not None:
548 subtype = subtype.one()
549 annotated_direction = ast.PARAM_DIRECTION_OUT
550 if subtype in (None, ''):
551 if node.type.target_giname and node.type.ctype:
552 target = self._transformer.lookup_giname(node.type.target_giname)
553 target = self._transformer.resolve_aliases(target)
554 has_double_indirection = '**' in node.type.ctype
555 is_structure_or_union = isinstance(target, (ast.Record, ast.Union))
556 caller_allocates = (not has_double_indirection and is_structure_or_union)
558 caller_allocates = False
559 elif subtype == OPT_OUT_CALLER_ALLOCATES:
560 caller_allocates = True
561 elif subtype == OPT_OUT_CALLEE_ALLOCATES:
562 caller_allocates = False
563 elif OPT_IN in options:
564 annotated_direction = ast.PARAM_DIRECTION_IN
566 if (annotated_direction is not None) and (annotated_direction != node.direction):
567 node.direction = annotated_direction
568 node.caller_allocates = caller_allocates
569 # Also reset the transfer default if we're toggling direction
570 node.transfer = self._get_transfer_default(parent, node)
572 transfer_tag = options.get(OPT_TRANSFER)
573 if transfer_tag and transfer_tag.length() == 1:
574 transfer = transfer_tag.one()
575 if transfer == OPT_TRANSFER_FLOATING:
576 transfer = OPT_TRANSFER_NONE
577 node.transfer = transfer
579 self._adjust_container_type(parent, node, options)
581 if (OPT_ALLOW_NONE in options or
582 node.type.target_giname == 'Gio.AsyncReadyCallback' or
583 node.type.target_giname == 'Gio.Cancellable'):
584 node.allow_none = True
586 if tag is not None and tag.comment is not None:
587 node.doc = tag.comment
589 if OPT_SKIP in options:
593 for attribute in options.getall(OPT_ATTRIBUTE):
594 node.attributes.append(attribute.flat())
596 def _apply_annotations_annotated(self, node, block):
600 node.doc = block.comment
602 since_tag = block.get(TAG_SINCE)
603 if since_tag is not None:
604 node.version = since_tag.value
606 deprecated_tag = block.get(TAG_DEPRECATED)
607 if deprecated_tag is not None:
608 value = deprecated_tag.value
610 version, desc = value.split(': ')
614 node.deprecated = desc
615 if version is not None:
616 node.deprecated_version = version
618 annos_tag = block.get(TAG_ATTRIBUTES)
619 if annos_tag is not None:
620 options = AnnotationParser.parse_options(annos_tag, annos_tag.value)
621 for key, value in options.iteritems():
623 node.attributes.append((key, value.one()))
625 if OPT_SKIP in block.options:
628 if OPT_FOREIGN in block.options:
631 if OPT_CONSTRUCTOR in block.options and isinstance(node, ast.Function):
632 node.is_constructor = True
634 if OPT_METHOD in block.options:
635 node.is_method = True
637 def _apply_annotations_alias(self, node, chain):
638 block = self._get_block(node)
639 self._apply_annotations_annotated(node, block)
641 def _apply_annotations_param(self, parent, param, tag):
643 options = tag.options
646 if isinstance(parent, (ast.Function, ast.VFunction)):
647 scope = options.get(OPT_SCOPE)
648 if scope and scope.length() == 1:
649 param.scope = scope.one()
650 param.transfer = ast.PARAM_TRANSFER_NONE
652 destroy = options.get(OPT_DESTROY)
654 param.destroy_name = self._get_validate_parameter_name(parent,
657 if param.destroy_name is not None:
658 param.scope = ast.PARAM_SCOPE_NOTIFIED
659 destroy_param = parent.get_parameter(param.destroy_name)
660 # This is technically bogus; we're setting the scope on the destroy
661 # itself. But this helps avoid tripping a warning from finaltransformer,
662 # since we don't have a way right now to flag this callback a destroy.
663 destroy_param.scope = ast.PARAM_SCOPE_NOTIFIED
664 closure = options.get(OPT_CLOSURE)
665 if closure and closure.length() == 1:
666 param.closure_name = self._get_validate_parameter_name(parent,
669 elif isinstance(parent, ast.Callback):
670 if OPT_CLOSURE in options:
671 # For callbacks, (closure) appears without an
672 # argument, and tags a parameter that is a closure. We
673 # represent it (weirdly) in the gir and typelib by
674 # setting param.closure_name to itself.
675 param.closure_name = param.argname
677 self._apply_annotations_param_ret_common(parent, param, tag)
679 def _apply_annotations_return(self, parent, return_, block):
681 tag = block.get(TAG_RETURNS)
684 self._apply_annotations_param_ret_common(parent, return_, tag)
686 def _apply_annotations_params(self, parent, params, block):
688 if parent.instance_parameter:
689 declparams.add(parent.instance_parameter.argname)
692 tag = block.get(param.argname)
695 self._apply_annotations_param(parent, param, tag)
696 declparams.add(param.argname)
700 docparams = set(block.params)
702 unknown = docparams - declparams
703 unused = declparams - docparams
705 for doc_name in unknown:
706 # Skip varargs, see #629759
707 if doc_name.lower() in ['...', 'varargs', TAG_RETURNS]:
711 elif len(unused) == 1:
713 text = ', should be %r' % (param, )
715 text = ', should be one of %s' % (
716 ', '.join(repr(p) for p in unused), )
718 tag = block.get(doc_name)
720 '%s: unknown parameter %r in documentation comment%s' % (
721 block.name, doc_name, text),
724 def _apply_annotations_callable(self, node, chain, block):
725 self._apply_annotations_annotated(node, block)
726 self._apply_annotations_params(node, node.parameters, block)
727 self._apply_annotations_return(node, node.retval, block)
729 def _check_arg_annotations(self, parent, params, block):
732 for tag in block.tags.keys():
733 if tag == TAG_RETURNS:
736 if param.argname == tag:
740 "Annotation for '%s' refers to unknown argument '%s'"
741 % (parent.name, tag))
743 def _apply_annotations_field(self, parent, block, field):
746 tag = block.get(field.name)
749 t = tag.options.get(OPT_TYPE)
751 field.type = self._transformer.create_type_from_user_string(t.one())
754 self._adjust_container_type(parent, field, tag.options)
755 except AttributeError:
758 def _apply_annotations_property(self, parent, prop):
759 prefix = self._get_annotation_name(parent)
760 block = self._blocks.get('%s:%s' % (prefix, prop.name))
761 self._apply_annotations_annotated(prop, block)
764 transfer_tag = block.get(OPT_TRANSFER)
765 if transfer_tag is not None:
766 transfer = transfer_tag.value
767 if transfer == OPT_TRANSFER_FLOATING:
768 transfer = OPT_TRANSFER_NONE
769 prop.transfer = transfer
771 prop.transfer = self._get_transfer_default(parent, prop)
772 type_tag = block.get(TAG_TYPE)
774 prop.type = self._resolve_toplevel(type_tag.value, prop.type, prop, parent)
776 def _apply_annotations_signal(self, parent, signal):
777 prefix = self._get_annotation_name(parent)
778 block = self._blocks.get('%s::%s' % (prefix, signal.name))
779 self._apply_annotations_annotated(signal, block)
780 # We're only attempting to name the signal parameters if
781 # the number of parameter tags (@foo) is the same or greater
782 # than the number of signal parameters
783 if block and len(block.tags) > len(signal.parameters):
784 names = block.tags.items()
785 # Resolve real parameter names early, so that in later
786 # phase we can refer to them while resolving annotations.
787 for i, param in enumerate(signal.parameters):
788 param.argname, tag = names[i+1]
791 for i, param in enumerate(signal.parameters):
793 name, tag = names[i+1]
794 options = getattr(tag, 'options', {})
795 param_type = options.get(OPT_TYPE)
797 param.type = self._resolve_toplevel(param_type.one(), param.type,
801 self._apply_annotations_param(signal, param, tag)
802 self._apply_annotations_return(signal, signal.retval, block)
804 def _apply_annotations_constant(self, node):
805 block = self._blocks.get(node.ctype)
808 tag = block.get(TAG_VALUE)
810 node.value = tag.value
812 def _pass_read_annotations2(self, node, chain):
813 if isinstance(node, ast.Function):
814 self._apply_annotations2_function(node, chain)
817 def _apply_annotations2_function(self, node, chain):
818 block = self._blocks.get(node.symbol)
820 self._apply_annotation_rename_to(node, chain, block)
822 # Handle virtual invokers
823 parent = chain[-1] if chain else None
824 if not (block and parent):
826 virtual = block.get(TAG_VFUNC)
829 invoker_name = virtual.value
831 for vfunc in parent.virtual_methods:
832 if vfunc.name == invoker_name:
834 vfunc.invoker = node.name
835 # Also merge in annotations
836 self._apply_annotations_callable(vfunc, [parent], block)
839 message.warn_node(node,
840 "Virtual slot %r not found for %r annotation" % (invoker_name, TAG_VFUNC))
842 def _resolve_and_filter_type_list(self, typelist):
843 """Given a list of Type instances, return a new list of types with
844 the ones that failed to resolve removed."""
845 # Create a copy we'll modify
846 new_typelist = list(typelist)
847 for typeval in typelist:
848 resolved = self._transformer.resolve_type(typeval)
850 new_typelist.remove(typeval)
853 def _pass_type_resolution(self, node, chain):
854 if isinstance(node, ast.Alias):
855 self._transformer.resolve_type(node.target)
856 if isinstance(node, ast.Callable):
857 for parameter in node.parameters:
858 self._transformer.resolve_type(parameter.type)
859 self._transformer.resolve_type(node.retval.type)
860 if isinstance(node, ast.Constant):
861 self._transformer.resolve_type(node.value_type)
862 if isinstance(node, (ast.Class, ast.Interface, ast.Record, ast.Union)):
863 for field in node.fields:
864 if field.anonymous_node:
867 self._transformer.resolve_type(field.type)
868 if isinstance(node, (ast.Class, ast.Interface)):
869 resolved_parent = None
870 for parent in node.parent_chain:
872 self._transformer.resolve_type(parent)
873 except ValueError, e:
875 target = self._transformer.lookup_typenode(parent)
880 if isinstance(node, ast.Interface):
881 node.parent = ast.Type(target_giname='GObject.Object')
882 for prop in node.properties:
883 self._transformer.resolve_type(prop.type)
884 for sig in node.signals:
885 for param in sig.parameters:
886 self._transformer.resolve_type(param.type)
887 if isinstance(node, ast.Class):
888 node.interfaces = self._resolve_and_filter_type_list(node.interfaces)
889 if isinstance(node, ast.Interface):
890 node.prerequisites = self._resolve_and_filter_type_list(node.prerequisites)
893 def _pair_quarks_with_enums(self):
894 # self._uscore_type_names is an authoritative mapping of types
895 # to underscored versions, since it is based on get_type() methods;
896 # but only covers enums that are registered as GObject enums.
897 # Create a fallback mapping based on all known enums in this module.
899 for enum in self._namespace.itervalues():
900 if not isinstance(enum, ast.Enum):
902 type_name = enum.ctype
903 uscored = to_underscores(type_name).lower()
905 uscore_enums[uscored] = enum
908 no_uscore_prefixed = self._transformer.strip_identifier(type_name)
909 except TransformerException, e:
911 no_uscore_prefixed = None
913 if no_uscore_prefixed not in uscore_enums:
914 uscore_enums[no_uscore_prefixed] = enum
916 for node in self._namespace.itervalues():
917 if not isinstance(node, ast.ErrorQuarkFunction):
919 short = node.symbol[:-len('_quark')]
920 if short == "g_io_error":
921 # Special case; GIOError was already taken forcing GIOErrorEnum
922 assert self._namespace.name == 'Gio'
923 enum = self._namespace.get('IOErrorEnum')
925 enum = self._uscore_type_names.get(short)
927 enum = uscore_enums.get(short)
929 enum.error_domain = node.error_domain
931 message.warn_node(node,
932 """%s: Couldn't find corresponding enumeration""" % (node.symbol, ))
934 def _split_uscored_by_type(self, uscored):
935 """'uscored' should be an un-prefixed uscore string. This
936 function searches through the namespace for the longest type which
937 prefixes uscored, and returns (type, suffix). Example, assuming
938 namespace Gtk, type is TextBuffer:
940 _split_uscored_by_type(text_buffer_try_new) -> (ast.Class(TextBuffer), 'try_new')"""
943 prev_split_count = -1
945 components = uscored.rsplit('_', count)
946 if len(components) == prev_split_count:
948 prev_split_count = len(components)
949 type_string = components[0]
950 node = self._uscore_type_names.get(type_string)
952 return (node, '_'.join(components[1:]))
955 def _pair_function(self, func):
956 """Check to see whether a toplevel function should be a
957 method or constructor of some type."""
959 # Ignore internal symbols and type metadata functions
960 if func.symbol.startswith('_') or func.is_type_meta_function():
963 (ns, subsymbol) = self._transformer.split_csymbol(func.symbol)
964 assert ns == self._namespace
965 if self._is_constructor(func, subsymbol):
966 self._set_up_constructor(func, subsymbol)
968 elif self._is_method(func, subsymbol):
969 self._setup_method(func, subsymbol)
971 elif self._pair_static_method(func, subsymbol):
974 def _uscored_identifier_for_type(self, typeval):
975 """Given a Type(target_giname='Foo.BarBaz'), return 'bar_baz'."""
976 name = typeval.get_giname()
977 return to_underscores_noprefix(name).lower()
979 def _is_method(self, func, subsymbol):
980 if not func.parameters:
982 message.warn_node(func,
983 '%s: Methods must have parameters' % (func.symbol, ))
985 first = func.parameters[0]
986 target = self._transformer.lookup_typenode(first.type)
987 if not isinstance(target, (ast.Class, ast.Interface,
988 ast.Record, ast.Union,
991 message.warn_node(func,
992 '%s: Methods must have a pointer as their first '
993 'parameter' % (func.symbol, ))
995 if target.namespace != self._namespace:
997 message.warn_node(func,
998 '%s: Methods must belong to the same namespace as the '
999 'class they belong to' % (func.symbol, ))
1002 # A quick hack here...in the future we should catch C signature/GI signature
1003 # mismatches in a general way in finaltransformer
1004 if first.type.ctype is not None and first.type.ctype.count('*') > 1:
1007 if not func.is_method:
1008 uscored_prefix = self._get_uscored_prefix(func, subsymbol)
1009 if not subsymbol.startswith(uscored_prefix):
1014 def _setup_method(self, func, subsymbol):
1015 uscored_prefix = self._get_uscored_prefix(func, subsymbol)
1016 target = self._transformer.lookup_typenode(func.parameters[0].type)
1018 func.instance_parameter = func.parameters.pop(0)
1019 self._namespace.float(func)
1021 if not func.is_method:
1022 subsym_idx = func.symbol.find(subsymbol)
1023 func.name = func.symbol[(subsym_idx + len(uscored_prefix) + 1):]
1024 func.is_method = True
1026 target.methods.append(func)
1028 def _get_uscored_prefix(self, func, subsymbol):
1029 # Here we check both the c_symbol_prefix and (if that fails),
1030 # attempt to do a default uscoring of the type. The reason we
1031 # look at a default underscore transformation is for
1032 # gdk_window_object_get_type(), which says to us that the
1033 # prefix is "gdk_window_object", when really it's just
1034 # "gdk_window". Possibly need an annotation to override this.
1035 prefix_matches = False
1036 uscored_prefix = None
1037 first_arg = func.parameters[0]
1038 target = self._transformer.lookup_typenode(first_arg.type)
1039 if hasattr(target, 'c_symbol_prefix') and target.c_symbol_prefix is not None:
1040 prefix_matches = subsymbol.startswith(target.c_symbol_prefix)
1042 uscored_prefix = target.c_symbol_prefix
1043 if not prefix_matches:
1044 uscored_prefix = self._uscored_identifier_for_type(first_arg.type)
1046 return uscored_prefix
1048 def _pair_static_method(self, func, subsymbol):
1049 split = self._split_uscored_by_type(subsymbol)
1052 (node, funcname) = split
1056 if isinstance(node, ast.Class):
1057 self._namespace.float(func)
1058 func.name = funcname
1059 node.static_methods.append(func)
1061 elif isinstance(node, (ast.Interface, ast.Record, ast.Union,
1062 ast.Boxed, ast.Enum, ast.Bitfield)):
1063 # prior to the introduction of this part of the code, only
1064 # ast.Class could have static methods. so for backwards
1065 # compatibility, instead of removing the func from the namespace,
1066 # leave it there and get a copy instead. modify the copy and push
1067 # it onto static_methods. we need to copy the parameters list
1068 # separately, because in the third pass functions are flagged as
1069 # 'throws' depending on the presence of a GError parameter which is
1070 # then removed from the parameters list. without the explicit
1071 # copy, only one of the two functions would thus get flagged as
1072 # 'throws'. clone() does this for us.
1073 new_func = func.clone()
1074 new_func.name = funcname
1075 node.static_methods.append(new_func)
1076 # flag the func as a backwards-comptability kludge (thus it will
1077 # get pruned in the introspectable pass if introspectable=0).
1078 func.moved_to = node.name + '.' + new_func.name
1083 def _set_up_constructor(self, func, subsymbol):
1084 self._namespace.float(func)
1086 func.name = self._get_constructor_name(func, subsymbol)
1088 origin_node = self._get_constructor_class(func, subsymbol)
1089 origin_node.constructors.append(func)
1091 func.is_constructor = True
1093 # Constructors have default return semantics
1094 if not func.retval.transfer:
1095 func.retval.transfer = self._get_transfer_default_return(func,
1098 def _get_constructor_class(self, func, subsymbol):
1100 split = self._split_uscored_by_type(subsymbol)
1102 if func.is_constructor:
1103 origin_node = self._transformer.lookup_typenode(func.retval.type)
1105 origin_node, _ = split
1109 def _get_constructor_name(self, func, subsymbol):
1111 split = self._split_uscored_by_type(subsymbol)
1113 if func.is_constructor:
1120 def _guess_constructor_by_name(self, symbol):
1121 # Normal constructors, gtk_button_new etc
1122 if symbol.endswith('_new'):
1124 # Alternative constructor, gtk_button_new_with_label
1125 if '_new_' in symbol:
1127 # gtk_list_store_newv,gtk_tree_store_newv etc
1128 if symbol.endswith('_newv'):
1132 def _is_constructor(self, func, subsymbol):
1133 # func.is_constructor will be True if we have a (constructor) annotation
1134 if not func.is_constructor:
1135 if not self._guess_constructor_by_name(func.symbol):
1137 target = self._transformer.lookup_typenode(func.retval.type)
1138 if not (isinstance(target, ast.Class)
1139 or (isinstance(target, (ast.Record, ast.Union, ast.Boxed))
1140 and (target.get_type is not None or target.foreign))):
1141 if func.is_constructor:
1142 message.warn_node(func,
1143 '%s: Constructors must return an instance of their class'
1147 origin_node = self._get_constructor_class(func, subsymbol)
1148 if origin_node is None:
1149 message.warn_node(func,
1150 "Can't find matching type for constructor; symbol=%r" \
1154 # Some sanity checks; only objects and boxeds can have ctors
1155 if not (isinstance(origin_node, ast.Class)
1156 or (isinstance(origin_node, (ast.Record, ast.Union, ast.Boxed))
1157 and (origin_node.get_type is not None or origin_node.foreign))):
1159 # Verify the namespace - don't want to append to foreign namespaces!
1160 if origin_node.namespace != self._namespace:
1161 if func.is_constructor:
1162 message.warn_node(func,
1163 '%s: Constructors must belong to the same namespace as the '
1164 'class they belong to' % (func.symbol, ))
1166 # If it takes the object as a first arg, guess it's not a constructor
1167 if not func.is_constructor and len(func.parameters) > 0:
1168 first_arg = self._transformer.lookup_typenode(func.parameters[0].type)
1169 if (first_arg is not None) and first_arg.gi_name == origin_node.gi_name:
1172 if isinstance(target, ast.Class):
1173 parent = origin_node
1174 while parent and (not parent.gi_name == 'GObject.Object'):
1175 if parent == target:
1178 parent = self._transformer.lookup_typenode(parent.parent)
1182 message.warn_node(func,
1183 "Return value is not superclass for constructor; "
1184 "symbol=%r constructed=%r return=%r" % (
1186 str(origin_node.create_type()),
1187 str(func.retval.type)))
1190 if origin_node != target:
1191 message.warn_node(func,
1192 "Constructor return type mismatch symbol=%r "
1193 "constructed=%r return=%r" % (
1195 str(origin_node.create_type()),
1196 str(func.retval.type)))
1201 def _pair_class_virtuals(self, node):
1202 """Look for virtual methods from the class structure."""
1203 if not node.glib_type_struct:
1204 # https://bugzilla.gnome.org/show_bug.cgi?id=629080
1205 #message.warn_node(node,
1206 # "Failed to find class structure for %r" % (node.name, ))
1209 node_type = node.create_type()
1210 class_struct = self._transformer.lookup_typenode(node.glib_type_struct)
1212 # Object class fields are assumed to be read-only
1213 # (see also _introspect_object and transformer.py)
1214 for field in class_struct.fields:
1215 if isinstance(field, ast.Field):
1216 field.writable = False
1218 for field in class_struct.fields:
1219 if not isinstance(field.anonymous_node, ast.Callback):
1221 callback = field.anonymous_node
1222 # Check the first parameter is the object
1223 if len(callback.parameters) == 0:
1225 firstparam_type = callback.parameters[0].type
1226 if firstparam_type != node_type:
1228 vfunc = ast.VFunction.from_callback(callback)
1229 vfunc.instance_parameter = callback.parameters[0]
1230 vfunc.inherit_file_positions(callback)
1232 prefix = self._get_annotation_name(class_struct)
1233 block = self._blocks.get('%s::%s' % (prefix, vfunc.name))
1234 self._apply_annotations_callable(vfunc, [node], block)
1235 node.virtual_methods.append(vfunc)
1237 # Take the set of virtual methods we found, and try
1238 # to pair up with any matching methods using the
1240 for vfunc in node.virtual_methods:
1241 for method in node.methods:
1242 if method.name != vfunc.name:
1244 if method.retval.type != vfunc.retval.type:
1246 if len(method.parameters) != len(vfunc.parameters):
1248 for i in xrange(len(method.parameters)):
1249 m_type = method.parameters[i].type
1250 v_type = vfunc.parameters[i].type
1251 if m_type != v_type:
1253 vfunc.invoker = method.name
1254 # Apply any annotations we have from the invoker to
1256 block = self._blocks.get(method.symbol)
1257 self._apply_annotations_callable(vfunc, [], block)
1260 def _pass3(self, node, chain):
1261 """Pass 3 is after we've loaded GType data and performed type
1263 if isinstance(node, ast.Callable):
1264 self._pass3_callable_callbacks(node)
1265 self._pass3_callable_throws(node)
1268 def _pass3_callable_callbacks(self, node):
1269 """Check to see if we have anything that looks like a
1270 callback+user_data+GDestroyNotify set."""
1272 params = node.parameters
1274 # First, do defaults for well-known callback types
1275 for i, param in enumerate(params):
1276 argnode = self._transformer.lookup_typenode(param.type)
1277 if isinstance(argnode, ast.Callback):
1278 if param.type.target_giname in ('Gio.AsyncReadyCallback',
1279 'GLib.DestroyNotify'):
1280 param.scope = ast.PARAM_SCOPE_ASYNC
1281 param.transfer = ast.PARAM_TRANSFER_NONE
1283 callback_param = None
1284 for i, param in enumerate(params):
1285 argnode = self._transformer.lookup_typenode(param.type)
1286 is_destroynotify = False
1287 if isinstance(argnode, ast.Callback):
1288 if param.type.target_giname == 'GLib.DestroyNotify':
1289 is_destroynotify = True
1291 callback_param = param
1293 if callback_param is None:
1295 if is_destroynotify:
1296 callback_param.destroy_name = param.argname
1297 callback_param.scope = ast.PARAM_SCOPE_NOTIFIED
1298 callback_param.transfer = ast.PARAM_TRANSFER_NONE
1299 elif (param.type.is_equiv(ast.TYPE_ANY) and
1300 param.argname is not None and
1301 param.argname.endswith('data')):
1302 callback_param.closure_name = param.argname
1304 def _pass3_callable_throws(self, node):
1305 """Check to see if we have anything that looks like a
1306 callback+user_data+GDestroyNotify set."""
1307 if not node.parameters:
1309 last_param = node.parameters[-1]
1310 # Checking type.name=='GLib.Error' generates false positives
1311 # on methods that take a 'GError *'
1312 if last_param.type.ctype == 'GError**':
1313 node.parameters.pop()