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