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.
25 from .annotationparser import (TAG_VFUNC, TAG_SINCE, TAG_DEPRECATED, TAG_RETURNS,
26 TAG_ATTRIBUTES, TAG_RENAME_TO, TAG_TYPE, TAG_TRANSFER,
27 TAG_UNREF_FUNC, TAG_REF_FUNC, TAG_SET_VALUE_FUNC,
29 from .annotationparser import (OPT_ALLOW_NONE,
30 OPT_ARRAY, OPT_ELEMENT_TYPE, OPT_IN, OPT_INOUT,
31 OPT_INOUT_ALT, OPT_OUT, OPT_SCOPE,
32 OPT_TYPE, OPT_CLOSURE, OPT_DESTROY, OPT_SKIP,
33 OPT_FOREIGN, OPT_ARRAY_FIXED_SIZE,
34 OPT_ARRAY_LENGTH, OPT_ARRAY_ZERO_TERMINATED)
35 from .annotationparser import AnnotationParser
36 from .transformer import TransformerException
37 from .utils import to_underscores, to_underscores_noprefix
39 class MainTransformer(object):
41 def __init__(self, transformer, blocks):
42 self._transformer = transformer
44 self._namespace = transformer.namespace
45 self._uscore_type_names = {}
50 # Dirty hack for now...maybe eventually we'll support the "typedef GSList FooSet"
52 if self._namespace.name == 'Atk':
53 attribute = self._namespace.get('Attribute')
54 attributeset = self._namespace.get('AttributeSet')
55 if attribute and attributeset:
56 alias = ast.Alias('AttributeSet', target=ast.TYPE_ANY)
57 self._namespace.append(alias, replace=True)
59 # We have a rough tree which should have most of of the types
60 # we know about. Let's attempt closure; walk over all of the
61 # Type() types and see if they match up with something.
62 self._namespace.walk(self._pass_type_resolution)
64 # Determine some default values for transfer etc.
65 # based on the current tree.
66 self._namespace.walk(self._pass_callable_defaults)
68 # Read in most annotations now.
69 self._namespace.walk(self._pass_read_annotations)
71 # Now that we've possibly seen more types from annotations,
72 # do another type resolution pass.
73 self._namespace.walk(self._pass_type_resolution)
75 # Generate a reverse mapping "bar_baz" -> BarBaz
76 for node in self._namespace.itervalues():
77 if isinstance(node, (ast.Class, ast.Interface, glibast.GLibBoxed)):
78 self._uscore_type_names[node.c_symbol_prefix] = node
79 elif isinstance(node, (ast.Record, ast.Union)):
80 uscored = to_underscores_noprefix(node.name).lower()
81 self._uscore_type_names[uscored] = node
83 for node in list(self._namespace.itervalues()):
84 if isinstance(node, ast.Function):
85 # Discover which toplevel functions are actually methods
86 self._pair_function(node)
87 if isinstance(node, (ast.Class, ast.Interface)):
88 self._pair_class_virtuals(node)
90 # Some annotations need to be post function pairing
91 self._namespace.walk(self._pass_read_annotations2)
93 # Another type resolution pass after we've parsed virtuals, etc.
94 self._namespace.walk(self._pass_type_resolution)
96 self._namespace.walk(self._pass3)
98 # TODO - merge into pass3
99 self._resolve_quarks()
103 def _get_validate_parameter_name(self, parent, param_name, origin):
105 param = parent.get_parameter(param_name)
106 except ValueError, e:
109 if isinstance(origin, ast.Parameter):
110 origin_name = 'parameter %s' % (origin.argname, )
112 origin_name = 'return value'
114 message.FATAL, parent,
115 "can't find parameter %s referenced by %s of %r"
116 % (param_name, origin_name, parent.name))
120 def _apply_annotation_rename_to(self, node, chain, block):
123 rename_to = block.get(TAG_RENAME_TO)
126 rename_to = rename_to.value
127 target = self._namespace.get_by_symbol(rename_to)
129 message.warn_node(node,
130 "Can't find symbol %r referenced by Rename annotation" % (
132 elif target.shadowed_by:
133 message.warn_node(node,
134 "Function %r already shadowed by %r, can't overwrite with %r" % (
139 message.warn_node(node,
140 "Function %r already shadows %r, can't multiply shadow with %r" % (
145 target.shadows = node.symbol
146 node.shadowed_by = target.symbol
148 def _apply_annotations_function(self, node, chain):
149 block = self._blocks.get(node.symbol)
150 self._apply_annotations_callable(node, chain, block)
151 self._apply_annotation_rename_to(node, chain, block)
153 def _pass_callable_defaults(self, node, chain):
154 if isinstance(node, (ast.Callable, glibast.GLibSignal)):
155 for param in node.parameters:
156 if param.transfer is None:
157 param.transfer = self._get_transfer_default(node, param)
158 if node.retval.transfer is None:
159 node.retval.transfer = self._get_transfer_default(node, node.retval)
162 def _pass_read_annotations(self, node, chain):
163 if not node.namespace:
165 if isinstance(node, ast.Function):
166 self._apply_annotations_function(node, chain)
167 if isinstance(node, ast.Callback):
168 block = self._blocks.get(node.c_name)
169 self._apply_annotations_callable(node, chain, block)
170 if isinstance(node, (ast.Class, ast.Interface, ast.Record,
171 ast.Union, ast.Enum, ast.Bitfield,
173 block = self._blocks.get(node.c_name)
174 self._apply_annotations_annotated(node, block)
175 if isinstance(node, (ast.Class, ast.Interface, ast.Record, ast.Union)):
176 for field in node.fields:
177 self._blocks.get('%s::%s' % (node.c_name, field.name))
178 self._apply_annotations_field(node, block, field)
179 if isinstance(node, (ast.Class, ast.Interface)):
180 for prop in node.properties:
181 self._apply_annotations_property(node, prop)
182 for sig in node.signals:
183 self._apply_annotations_signal(node, sig)
184 if isinstance(node, ast.Class):
185 block = self._blocks.get(node.c_name)
187 tag = block.get(TAG_UNREF_FUNC)
188 node.unref_func = tag.value if tag else None
189 tag = block.get(TAG_REF_FUNC)
190 node.ref_func = tag.value if tag else None
191 tag = block.get(TAG_SET_VALUE_FUNC)
192 node.set_value_func = tag.value if tag else None
193 tag = block.get(TAG_GET_VALUE_FUNC)
194 node.get_value_func = tag.value if tag else None
197 def _adjust_container_type(self, parent, node, options):
198 has_element_type = OPT_ELEMENT_TYPE in options
199 has_array = OPT_ARRAY in options
202 self._apply_annotations_array(parent, node, options)
203 elif has_element_type:
204 self._apply_annotations_element_type(parent, node, options)
206 def _resolve(self, type_str, orig_node=None):
207 def grab_one(type_str, resolver, top_combiner, combiner):
208 """Return a complete type, and the trailing string part after it.
209 Use resolver() on each identifier, and combiner() on the parts of
210 each complete type. (top_combiner is used on the top-most type.)"""
211 bits = re.split(r'([,<>])', type_str, 1)
212 first, sep, rest = [bits[0], '', ''] if (len(bits)==1) else bits
213 args = [resolver(first)]
216 next, rest = grab_one(rest, resolver, combiner, combiner)
218 sep, rest = rest[0], rest[1:]
221 return top_combiner(*args), rest
223 res = self._transformer.create_type_from_user_string(ident)
225 def combiner(base, *rest):
228 if isinstance(base, ast.List) and len(rest) == 1:
229 return ast.List(base.name, *rest)
230 if isinstance(base, ast.Map) and len(rest) == 2:
231 return ast.Map(*rest)
233 "Too many parameters in type specification %r" % (type_str, ))
235 def top_combiner(base, *rest):
236 if orig_node is not None:
237 base.is_const = orig_node.is_const
238 return combiner(base, *rest)
240 result, rest = grab_one(type_str, resolver, top_combiner, combiner)
242 message.warn("Trailing components in type specification %r" % (
246 def _apply_annotations_array(self, parent, node, options):
247 array_opt = options.get(OPT_ARRAY)
249 array_values = array_opt.all()
253 element_type = options.get(OPT_ELEMENT_TYPE)
254 if element_type is not None:
255 element_type_node = self._resolve(element_type.one())
256 elif isinstance(node.type, ast.Array):
257 element_type_node = node.type.element_type
259 # We're assuming here that Foo* with an (array) annotation
260 # and no (element-type) means array of Foo
261 element_type_node = node.type.clone()
262 # Explicitly erase ctype since it's no longer valid
263 element_type_node.ctype = None
265 if isinstance(node.type, ast.Array):
266 array_type = node.type.array_type
269 container_type = ast.Array(array_type, element_type_node,
270 ctype=node.type.ctype,
271 is_const=node.type.is_const)
272 if OPT_ARRAY_ZERO_TERMINATED in array_values:
273 container_type.zeroterminated = array_values.get(
274 OPT_ARRAY_ZERO_TERMINATED) == '1'
275 length = array_values.get(OPT_ARRAY_LENGTH)
276 if length is not None:
277 paramname = self._get_validate_parameter_name(parent, length, node)
279 param = parent.get_parameter(paramname)
280 param.direction = node.direction
281 if param.direction == ast.PARAM_DIRECTION_OUT:
282 param.transfer = ast.PARAM_TRANSFER_FULL
283 container_type.length_param_name = param.argname
284 fixed = array_values.get(OPT_ARRAY_FIXED_SIZE)
286 container_type.size = int(fixed)
287 node.type = container_type
289 def _apply_annotations_element_type(self, parent, node, options):
290 element_type_opt = options.get(OPT_ELEMENT_TYPE)
291 element_type = element_type_opt.flat()
292 if isinstance(node.type, ast.List):
293 assert len(element_type) == 1
294 node.type.element_type = self._resolve(element_type[0])
295 elif isinstance(node.type, ast.Map):
296 assert len(element_type) == 2
297 node.type.key_type = self._resolve(element_type[0])
298 node.type.value_type = self._resolve(element_type[1])
299 elif isinstance(node.type, ast.Array):
300 node.type.element_type = self._resolve(element_type[0])
302 message.warn_node(parent,
303 "Unknown container %r for element-type annotation" % (node.type, ))
305 def _get_transfer_default_param(self, parent, node):
306 if node.direction in [ast.PARAM_DIRECTION_INOUT,
307 ast.PARAM_DIRECTION_OUT]:
308 if node.caller_allocates:
309 return ast.PARAM_TRANSFER_NONE
310 return ast.PARAM_TRANSFER_FULL
311 return ast.PARAM_TRANSFER_NONE
313 def _get_transfer_default_returntype_basic(self, typeval):
314 if (typeval.is_equiv(ast.BASIC_GIR_TYPES)
316 or typeval.is_equiv(ast.TYPE_NONE)):
317 return ast.PARAM_TRANSFER_NONE
318 elif typeval.is_equiv(ast.TYPE_STRING):
319 # Non-const strings default to FULL
320 return ast.PARAM_TRANSFER_FULL
321 elif typeval.target_fundamental:
322 # This looks like just GType right now
326 def _is_gi_subclass(self, typeval, supercls_type):
327 cls = self._transformer.lookup_typenode(typeval)
328 assert cls, str(typeval)
329 supercls = self._transformer.lookup_typenode(supercls_type)
333 if cls.parent and cls.parent.target_giname != 'GObject.Object':
334 return self._is_gi_subclass(cls.parent, supercls_type)
337 def _get_transfer_default_return(self, parent, node):
339 basic = self._get_transfer_default_returntype_basic(typeval)
342 if not typeval.target_giname:
344 target = self._transformer.lookup_typenode(typeval)
345 if isinstance(target, ast.Alias):
346 return self._get_transfer_default_returntype_basic(target.target)
347 elif isinstance(target, glibast.GLibBoxed):
348 return ast.PARAM_TRANSFER_FULL
349 elif isinstance(target, (ast.Enum, ast.Bitfield)):
350 return ast.PARAM_TRANSFER_NONE
351 # Handle constructors specially here
352 elif isinstance(parent, ast.Function) and parent.is_constructor:
353 if isinstance(target, ast.Class):
354 initially_unowned_type = ast.Type(target_giname='GObject.InitiallyUnowned')
355 initially_unowned = self._transformer.lookup_typenode(initially_unowned_type)
356 if initially_unowned and self._is_gi_subclass(typeval, initially_unowned_type):
357 return ast.PARAM_TRANSFER_NONE
359 return ast.PARAM_TRANSFER_FULL
360 elif isinstance(target, (ast.Record, ast.Union)):
361 return ast.PARAM_TRANSFER_FULL
363 assert False, "Invalid constructor"
364 elif isinstance(target, (ast.Class, ast.Record, ast.Union)):
365 # Explicitly no default for these
370 def _get_transfer_default(self, parent, node):
371 if node.type.is_equiv(ast.TYPE_NONE) or isinstance(node.type, ast.Varargs):
372 return ast.PARAM_TRANSFER_NONE
373 elif isinstance(node, ast.Parameter):
374 return self._get_transfer_default_param(parent, node)
375 elif isinstance(node, ast.Return):
376 return self._get_transfer_default_return(parent, node)
377 elif isinstance(node, ast.Field):
378 return ast.PARAM_TRANSFER_NONE
379 elif isinstance(node, ast.Property):
380 return ast.PARAM_TRANSFER_NONE
382 raise AssertionError(node)
384 def _apply_annotations_param_ret_common(self, parent, node, tag):
385 options = getattr(tag, 'options', {})
387 param_type = options.get(OPT_TYPE)
389 node.type = self._resolve(param_type.one(), node.type)
391 caller_allocates = False
392 annotated_direction = None
393 if (OPT_INOUT in options or
394 OPT_INOUT_ALT in options):
395 annotated_direction = ast.PARAM_DIRECTION_INOUT
396 elif OPT_OUT in options:
397 subtype = options[OPT_OUT]
398 if subtype is not None:
399 subtype = subtype.one()
400 annotated_direction = ast.PARAM_DIRECTION_OUT
401 if subtype in (None, ''):
402 if node.type.target_giname and node.type.ctype:
403 caller_allocates = '**' not in node.type.ctype
405 caller_allocates = False
406 elif subtype == 'caller-allocates':
407 caller_allocates = True
408 elif subtype == 'callee-allocates':
409 caller_allocates = False
411 message.fatal("out allocation for %s is invalid (%r)" % (
413 elif OPT_IN in options:
414 annotated_direction = ast.PARAM_DIRECTION_IN
416 if (annotated_direction is not None) and (annotated_direction != node.direction):
417 node.direction = annotated_direction
418 node.caller_allocates = caller_allocates
419 # Also reset the transfer default if we're toggling direction
420 node.transfer = self._get_transfer_default(parent, node)
422 transfer_tag = options.get(TAG_TRANSFER)
424 node.transfer = transfer_tag.one()
426 self._adjust_container_type(parent, node, options)
428 if (OPT_ALLOW_NONE in options or
429 node.type.target_giname == 'Gio.Cancellable'):
430 node.allow_none = True
432 if tag is not None and tag.comment is not None:
433 node.doc = tag.comment
437 value = options.get(key)
439 node.attributes.append((key, value.one()))
441 def _apply_annotations_annotated(self, node, block):
445 node.doc = block.comment
447 since_tag = block.get(TAG_SINCE)
448 if since_tag is not None:
449 node.version = since_tag.value
451 deprecated_tag = block.get(TAG_DEPRECATED)
452 if deprecated_tag is not None:
453 value = deprecated_tag.value
455 version, desc = value.split(': ')
459 node.deprecated = desc
460 if version is not None:
461 node.deprecated_version = version
463 annos_tag = block.get(TAG_ATTRIBUTES)
464 if annos_tag is not None:
465 options = AnnotationParser.parse_options(annos_tag.value)
466 for key, value in options.iteritems():
468 node.attributes.append((key, value.one()))
470 if OPT_SKIP in block.options:
473 if OPT_FOREIGN in block.options:
476 def _apply_annotations_param(self, parent, param, tag):
478 options = tag.options
481 if isinstance(parent, ast.Function):
482 scope = options.get(OPT_SCOPE)
485 if scope not in [ast.PARAM_SCOPE_CALL,
486 ast.PARAM_SCOPE_ASYNC,
487 ast.PARAM_SCOPE_NOTIFIED]:
490 "Invalid scope %r for parameter %r" % (scope, param.name))
493 param.transfer = ast.PARAM_TRANSFER_NONE
495 destroy = options.get(OPT_DESTROY)
497 param.destroy_name = self._get_validate_parameter_name(parent,
500 if param.destroy_name is not None:
501 param.scope = ast.PARAM_SCOPE_NOTIFIED
502 destroy_param = parent.get_parameter(param.destroy_name)
503 # This is technically bogus; we're setting the scope on the destroy
504 # itself. But this helps avoid tripping a warning from finaltransformer,
505 # since we don't have a way right now to flag this callback a destroy.
506 destroy_param.scope = ast.PARAM_SCOPE_NOTIFIED
507 closure = options.get(OPT_CLOSURE)
509 param.closure_name = self._get_validate_parameter_name(parent,
512 elif isinstance(parent, ast.Callback):
513 if OPT_CLOSURE in options:
514 # For callbacks, (closure) appears without an
515 # argument, and tags a parameter that is a closure. We
516 # represent it (weirdly) in the gir and typelib by
517 # setting param.closure_name to itself.
518 param.closure_name = param.argname
520 self._apply_annotations_param_ret_common(parent, param, tag)
522 def _apply_annotations_return(self, parent, return_, block):
524 tag = block.get(TAG_RETURNS)
527 self._apply_annotations_param_ret_common(parent, return_, tag)
529 def _apply_annotations_params(self, parent, params, block):
532 tag = block.get(param.argname)
535 self._apply_annotations_param(parent, param, tag)
537 def _apply_annotations_callable(self, node, chain, block):
538 self._apply_annotations_annotated(node, block)
539 self._apply_annotations_params(node, node.parameters, block)
540 self._apply_annotations_return(node, node.retval, block)
542 def _check_arg_annotations(self, parent, params, block):
545 for tag in block.tags.keys():
546 if tag == TAG_RETURNS:
549 if param.argname == tag:
553 "Annotation for '%s' refers to unknown argument '%s'"
554 % (parent.name, tag))
556 def _apply_annotations_field(self, parent, block, field):
559 tag = block.get(field.name)
562 t = tag.options.get('type')
565 field.type = self._transformer.create_type_from_user_string(t.one())
567 def _apply_annotations_property(self, parent, prop):
568 block = self._blocks.get('%s:%s' % (parent.type_name, prop.name))
569 self._apply_annotations_annotated(prop, block)
572 transfer_tag = block.get(TAG_TRANSFER)
573 if transfer_tag is not None:
574 prop.transfer = transfer_tag.value
576 prop.transfer = self._get_transfer_default(parent, prop)
577 type_tag = block.get(TAG_TYPE)
579 prop.type = self._resolve(type_tag.value, prop.type)
581 def _apply_annotations_signal(self, parent, signal):
582 block = self._blocks.get('%s::%s' % (parent.type_name, signal.name))
583 self._apply_annotations_annotated(signal, block)
584 # We're only attempting to name the signal parameters if
585 # the number of parameter tags (@foo) is the same or greater
586 # than the number of signal parameters
587 if block and len(block.tags) > len(signal.parameters):
588 names = block.tags.items()
591 for i, param in enumerate(signal.parameters):
593 name, tag = names[i+1]
595 options = getattr(tag, 'options', {})
596 param_type = options.get(OPT_TYPE)
598 param.type = self._resolve(param_type.one(), param.type)
601 self._apply_annotations_param(signal, param, tag)
602 self._apply_annotations_return(signal, signal.retval, block)
604 def _pass_read_annotations2(self, node, chain):
605 if isinstance(node, ast.Function):
606 self._apply_annotations2_function(node, chain)
609 def _apply_annotations2_function(self, node, chain):
610 # Handle virtual invokers
611 parent = chain[-1] if chain else None
612 block = self._blocks.get(node.symbol)
613 if not (block and parent):
615 virtual = block.get(TAG_VFUNC)
618 invoker_name = virtual.value
620 for vfunc in parent.virtual_methods:
621 if vfunc.name == invoker_name:
623 vfunc.invoker = node.name
624 # Also merge in annotations
625 self._apply_annotations_callable(vfunc, [parent], block)
628 message.warn_symbol(node.symbol,
629 "Virtual slot %r not found for %r annotation" % (invoker_name, TAG_VFUNC))
631 def _resolve_and_filter_type_list(self, typelist):
632 """Given a list of Type instances, return a new list of types with
633 the ones that failed to resolve removed."""
634 # Create a copy we'll modify
635 new_typelist = list(typelist)
636 for typeval in typelist:
637 resolved = self._transformer.resolve_type(typeval)
639 new_typelist.remove(typeval)
642 def _pass_type_resolution(self, node, chain):
643 if isinstance(node, ast.Alias):
644 self._transformer.resolve_type(node.target)
645 if isinstance(node, ast.Callable):
646 for parameter in node.parameters:
647 self._transformer.resolve_type(parameter.type)
648 self._transformer.resolve_type(node.retval.type)
649 if isinstance(node, ast.Constant):
650 self._transformer.resolve_type(node.value_type)
651 if isinstance(node, (ast.Class, ast.Interface, ast.Record, ast.Union)):
652 for field in node.fields:
653 if field.anonymous_node:
656 self._transformer.resolve_type(field.type)
657 if isinstance(node, (ast.Class, ast.Interface)):
658 resolved_parent = None
659 for parent in node.parent_chain:
661 self._transformer.resolve_type(parent)
662 except ValueError, e:
664 target = self._transformer.lookup_typenode(parent)
669 if isinstance(node, ast.Interface) or not node.fundamental:
670 node.parent = ast.Type(target_giname='GObject.Object')
673 for prop in node.properties:
674 self._transformer.resolve_type(prop.type)
675 for sig in node.signals:
676 for param in sig.parameters:
677 self._transformer.resolve_type(param.type)
678 if isinstance(node, ast.Class):
679 node.interfaces = self._resolve_and_filter_type_list(node.interfaces)
680 if isinstance(node, ast.Interface):
681 node.prerequisites = self._resolve_and_filter_type_list(node.prerequisites)
684 def _resolve_quarks(self):
685 # self._uscore_type_names is an authoritative mapping of types
686 # to underscored versions, since it is based on get_type() methods;
687 # but only covers enums that are registered as GObject enums.
688 # Create a fallback mapping based on all known enums in this module.
690 for enum in self._namespace.itervalues():
691 if not isinstance(enum, ast.Enum):
693 type_name = enum.symbol
694 uscored = to_underscores(type_name).lower()
696 uscore_enums[uscored] = enum
699 no_uscore_prefixed = self._transformer.strip_identifier(type_name)
700 except TransformerException, e:
702 no_uscore_prefixed = None
704 if no_uscore_prefixed not in uscore_enums:
705 uscore_enums[no_uscore_prefixed] = enum
707 for node in self._namespace.itervalues():
708 if not isinstance(node, ast.Function):
710 if node.retval.type.target_giname != 'GLib.Quark':
712 short = node.symbol[:-len('_quark')]
713 if short == "g_io_error":
714 # Special case; GIOError was already taken forcing GIOErrorEnum
715 assert self._namespace.name == 'Gio'
716 enum = self._namespace.get('IOErrorEnum')
718 enum = self._uscore_type_names.get(short)
720 enum = uscore_enums.get(short)
722 enum.error_quark = node.symbol
724 message.warn_node(node,
725 """%s: Couldn't find corresponding enumeration""" % (node.symbol, ))
727 def _split_uscored_by_type(self, uscored):
728 """'uscored' should be an un-prefixed uscore string. This
729 function searches through the namespace for the longest type which
730 prefixes uscored, and returns (type, suffix). Example, assuming
731 namespace Gtk, type is TextBuffer:
733 _split_uscored_by_type(text_buffer_try_new) -> (ast.Class(TextBuffer), 'try_new')"""
736 prev_split_count = -1
738 components = uscored.rsplit('_', count)
739 if len(components) == prev_split_count:
741 prev_split_count = len(components)
742 type_string = components[0]
743 node = self._uscore_type_names.get(type_string)
745 return (node, '_'.join(components[1:]))
748 def _pair_function(self, func):
749 """Check to see whether a toplevel function should be a
750 method or constructor of some type."""
751 if func.symbol.endswith('_get_type') or func.symbol.startswith('_'):
753 (ns, subsymbol) = self._transformer.split_csymbol(func.symbol)
754 assert ns == self._namespace
755 if self._pair_constructor(func, subsymbol):
757 elif self._pair_method(func, subsymbol):
759 elif self._pair_static_method(func, subsymbol):
762 def _uscored_identifier_for_type(self, typeval):
763 """Given a Type(target_giname='Foo.BarBaz'), return 'bar_baz'."""
764 name = typeval.get_giname()
765 return to_underscores_noprefix(name).lower()
767 def _pair_method(self, func, subsymbol):
768 if not func.parameters:
770 first = func.parameters[0]
771 target = self._transformer.lookup_typenode(first.type)
772 if not isinstance(target, (ast.Class, ast.Interface,
773 ast.Record, ast.Union,
774 glibast.GLibBoxedOther)):
777 # A quick hack here...in the future we should catch C signature/GI signature
778 # mismatches in a general way in finaltransformer
779 if first.type.ctype.count('*') != 1:
782 uscored = self._uscored_identifier_for_type(first.type)
783 if not subsymbol.startswith(uscored):
785 del func.parameters[0]
786 subsym_idx = func.symbol.find(subsymbol)
787 self._namespace.float(func)
788 func.name = func.symbol[(subsym_idx + len(uscored) + 1):]
789 target.methods.append(func)
790 func.is_method = True
793 def _pair_static_method(self, func, subsymbol):
794 split = self._split_uscored_by_type(subsymbol)
797 (node, funcname) = split
798 if not isinstance(node, (ast.Class, ast.Interface,
799 ast.Record, ast.Union, glibast.GLibBoxedOther)):
801 self._namespace.float(func)
803 node.static_methods.append(func)
805 def _pair_constructor(self, func, subsymbol):
806 if not (func.symbol.find('_new_') >= 0 or func.symbol.endswith('_new')):
808 target = self._transformer.lookup_typenode(func.retval.type)
809 if not isinstance(target, (ast.Class, glibast.GLibBoxed)):
811 new_idx = func.symbol.rfind('_new')
812 assert (new_idx >= 0)
813 prefix = func.symbol[:new_idx]
814 split = self._split_uscored_by_type(subsymbol)
816 # TODO - need a e.g. (method) annotation
817 message.warn_node(func,
818 "Can't find matching type for constructor; symbol=%r" % (func.symbol, ))
820 (origin_node, funcname) = split
821 if isinstance(target, ast.Class):
823 while parent and (not parent.create_type().target_giname == 'GObject.Object'):
827 parent = self._transformer.lookup_typenode(parent.parent)
831 message.warn_node(func,
832 "Return value is not superclass for constructor; "
833 "symbol=%r constructed=%r return=%r" % (
835 str(origin_node.create_type()),
836 str(func.retval.type)))
839 if origin_node != target:
840 message.warn_node(func,
841 "Constructor return type mismatch symbol=%r "
842 "constructed=%r return=%r" % (
844 str(origin_node.create_type()),
845 str(func.retval.type)))
847 self._namespace.float(func)
849 func.is_constructor = True
850 target.constructors.append(func)
851 # Constructors have default return semantics
852 if not func.retval.transfer:
853 func.retval.transfer = self._get_transfer_default_return(func, func.retval)
856 def _pair_class_virtuals(self, node):
857 """Look for virtual methods from the class structure."""
858 if not node.glib_type_struct:
859 message.warn_node(node,
860 "Failed to find class structure for %r" % (node.name, ))
863 node_type = node.create_type()
864 class_struct = self._transformer.lookup_typenode(node.glib_type_struct)
866 # Object class fields are assumed to be read-only
867 # (see also _introspect_object and transformer.py)
868 for field in class_struct.fields:
869 if isinstance(field, ast.Field):
870 field.writable = False
872 # Loop through fields to determine which are virtual
873 # functions and which are signal slots by
874 # assuming everything that doesn't share a name
875 # with a known signal is a virtual slot.
876 for field in class_struct.fields:
877 if not isinstance(field.anonymous_node, ast.Callback):
879 callback = field.anonymous_node
880 # Check the first parameter is the object
881 if len(callback.parameters) == 0:
883 firstparam_type = callback.parameters[0].type
884 if firstparam_type != node_type:
886 # Also double check we don't have a signal with this
888 matched_signal = False
889 for signal in node.signals:
890 if signal.name.replace('-', '_') == callback.name:
891 matched_signal = True
895 vfunc = ast.VFunction.from_callback(callback)
896 vfunc.inherit_file_positions(callback)
897 node.virtual_methods.append(vfunc)
899 # Take the set of virtual methods we found, and try
900 # to pair up with any matching methods using the
902 for vfunc in node.virtual_methods:
903 for method in node.methods:
904 if method.name != vfunc.name:
906 if method.retval.type != vfunc.retval.type:
908 if len(method.parameters) != len(vfunc.parameters):
910 for i in xrange(len(method.parameters)):
911 m_type = method.parameters[i].type
912 v_type = vfunc.parameters[i].type
915 vfunc.invoker = method.name
916 # Apply any annotations we have from the invoker to
918 block = self._blocks.get(method.symbol)
919 self._apply_annotations_callable(vfunc, [], block)
921 def _pass3(self, node, chain):
922 """Pass 3 is after we've loaded GType data and performed type
924 if isinstance(node, ast.Callable):
925 self._pass3_callable_callbacks(node)
926 self._pass3_callable_throws(node)
929 def _pass3_callable_callbacks(self, node):
930 """Check to see if we have anything that looks like a
931 callback+user_data+GDestroyNotify set."""
933 params = node.parameters
935 # First, do defaults for well-known callback types
936 for i, param in enumerate(params):
937 argnode = self._transformer.lookup_typenode(param.type)
938 if isinstance(argnode, ast.Callback):
939 if param.type.target_giname in ('Gio.AsyncReadyCallback',
940 'GLib.DestroyNotify'):
941 param.scope = ast.PARAM_SCOPE_ASYNC
942 param.transfer = ast.PARAM_TRANSFER_NONE
944 callback_param = None
945 for i, param in enumerate(params):
946 argnode = self._transformer.lookup_typenode(param.type)
947 is_destroynotify = False
948 if isinstance(argnode, ast.Callback):
949 if param.type.target_giname == 'GLib.DestroyNotify':
950 is_destroynotify = True
952 callback_param = param
954 if callback_param is None:
957 callback_param.destroy_name = param.argname
958 callback_param.scope = ast.PARAM_SCOPE_NOTIFIED
959 callback_param.transfer = ast.PARAM_TRANSFER_NONE
960 elif (param.type.is_equiv(ast.TYPE_ANY) and
961 param.argname is not None and
962 param.argname.endswith('data')):
963 callback_param.closure_name = param.argname
965 def _pass3_callable_throws(self, node):
966 """Check to see if we have anything that looks like a
967 callback+user_data+GDestroyNotify set."""
968 if not node.parameters:
970 last_param = node.parameters[-1]
971 # Checking type.name=='GLib.Error' generates false positives
972 # on methods that take a 'GError *'
973 if last_param.type.ctype == 'GError**':
974 node.parameters.pop()