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'
113 self._transformer.log_node_warning(parent,
114 "can't find parameter %s referenced by %s of %r"
115 % (param_name, origin_name, parent.name), fatal=True)
119 def _apply_annotation_rename_to(self, node, chain, block):
122 rename_to = block.get(TAG_RENAME_TO)
125 rename_to = rename_to.value
126 target = self._namespace.get_by_symbol(rename_to)
128 self._transformer.log_node_warning(node,
129 "Can't find symbol %r referenced by Rename annotation" % (rename_to, ))
130 elif target.shadowed_by:
131 self._transformer.log_node_warning(node,
132 "Function %r already shadowed by %r, can't overwrite with %r" % (target.symbol,
136 self._transformer.log_node_warning(node,
137 "Function %r already shadows %r, can't multiply shadow with %r" % (target.symbol,
141 target.shadows = node.symbol
142 node.shadowed_by = target.symbol
144 def _apply_annotations_function(self, node, chain):
145 block = self._blocks.get(node.symbol)
146 self._apply_annotations_callable(node, chain, block)
147 self._apply_annotation_rename_to(node, chain, block)
149 def _pass_callable_defaults(self, node, chain):
150 if isinstance(node, (ast.Callable, glibast.GLibSignal)):
151 for param in node.parameters:
152 if param.transfer is None:
153 param.transfer = self._get_transfer_default(node, param)
154 if node.retval.transfer is None:
155 node.retval.transfer = self._get_transfer_default(node, node.retval)
158 def _pass_read_annotations(self, node, chain):
159 if not node.namespace:
161 if isinstance(node, ast.Function):
162 self._apply_annotations_function(node, chain)
163 if isinstance(node, ast.Callback):
164 block = self._blocks.get(node.c_name)
165 self._apply_annotations_callable(node, chain, block)
166 if isinstance(node, (ast.Class, ast.Interface, ast.Record,
167 ast.Union, ast.Enum, ast.Bitfield,
169 block = self._blocks.get(node.c_name)
170 self._apply_annotations_annotated(node, block)
171 if isinstance(node, (ast.Class, ast.Interface, ast.Record, ast.Union)):
172 for field in node.fields:
173 self._blocks.get('%s::%s' % (node.c_name, field.name))
174 self._apply_annotations_field(node, block, field)
175 if isinstance(node, (ast.Class, ast.Interface)):
176 for prop in node.properties:
177 self._apply_annotations_property(node, prop)
178 for sig in node.signals:
179 self._apply_annotations_signal(node, sig)
180 if isinstance(node, ast.Class):
181 block = self._blocks.get(node.c_name)
183 tag = block.get(TAG_UNREF_FUNC)
184 node.unref_func = tag.value if tag else None
185 tag = block.get(TAG_REF_FUNC)
186 node.ref_func = tag.value if tag else None
187 tag = block.get(TAG_SET_VALUE_FUNC)
188 node.set_value_func = tag.value if tag else None
189 tag = block.get(TAG_GET_VALUE_FUNC)
190 node.get_value_func = tag.value if tag else None
193 def _adjust_container_type(self, parent, node, options):
194 has_element_type = OPT_ELEMENT_TYPE in options
195 has_array = OPT_ARRAY in options
198 self._apply_annotations_array(parent, node, options)
199 elif has_element_type:
200 self._apply_annotations_element_type(parent, node, options)
202 def _resolve(self, type_str, orig_node=None):
203 def grab_one(type_str, resolver, top_combiner, combiner):
204 """Return a complete type, and the trailing string part after it.
205 Use resolver() on each identifier, and combiner() on the parts of
206 each complete type. (top_combiner is used on the top-most type.)"""
207 bits = re.split(r'([,<>])', type_str, 1)
208 first, sep, rest = [bits[0], '', ''] if (len(bits)==1) else bits
209 args = [resolver(first)]
212 next, rest = grab_one(rest, resolver, combiner, combiner)
214 sep, rest = rest[0], rest[1:]
217 return top_combiner(*args), rest
219 res = self._transformer.create_type_from_user_string(ident)
221 def combiner(base, *rest):
224 if isinstance(base, ast.List) and len(rest) == 1:
225 return ast.List(base.name, *rest)
226 if isinstance(base, ast.Map) and len(rest) == 2:
227 return ast.Map(*rest)
228 self._transformer.log_warning(
229 "Too many parameters in type specification %r" % (type_str, ))
231 def top_combiner(base, *rest):
232 if orig_node is not None:
233 base.is_const = orig_node.is_const
234 return combiner(base, *rest)
236 result, rest = grab_one(type_str, resolver, top_combiner, combiner)
238 self._transformer.log_warning(
239 "Trailing components in type specification %r" % (type_str, ))
242 def _apply_annotations_array(self, parent, node, options):
243 array_opt = options.get(OPT_ARRAY)
245 array_values = array_opt.all()
249 element_type = options.get(OPT_ELEMENT_TYPE)
250 if element_type is not None:
251 element_type_node = self._resolve(element_type.one())
252 elif isinstance(node.type, ast.Array):
253 element_type_node = node.type.element_type
255 # We're assuming here that Foo* with an (array) annotation
256 # and no (element-type) means array of Foo
257 element_type_node = node.type.clone()
258 # Explicitly erase ctype since it's no longer valid
259 element_type_node.ctype = None
261 if isinstance(node.type, ast.Array):
262 array_type = node.type.array_type
265 container_type = ast.Array(array_type, element_type_node,
266 ctype=node.type.ctype,
267 is_const=node.type.is_const)
268 if OPT_ARRAY_ZERO_TERMINATED in array_values:
269 container_type.zeroterminated = array_values.get(
270 OPT_ARRAY_ZERO_TERMINATED) == '1'
271 length = array_values.get(OPT_ARRAY_LENGTH)
272 if length is not None:
273 paramname = self._get_validate_parameter_name(parent, length, node)
275 param = parent.get_parameter(paramname)
276 param.direction = node.direction
277 if param.direction == ast.PARAM_DIRECTION_OUT:
278 param.transfer = ast.PARAM_TRANSFER_FULL
279 container_type.length_param_name = param.argname
280 fixed = array_values.get(OPT_ARRAY_FIXED_SIZE)
282 container_type.size = int(fixed)
283 node.type = container_type
285 def _apply_annotations_element_type(self, parent, node, options):
286 element_type_opt = options.get(OPT_ELEMENT_TYPE)
287 element_type = element_type_opt.flat()
288 if isinstance(node.type, ast.List):
289 assert len(element_type) == 1
290 node.type.element_type = self._resolve(element_type[0])
291 elif isinstance(node.type, ast.Map):
292 assert len(element_type) == 2
293 node.type.key_type = self._resolve(element_type[0])
294 node.type.value_type = self._resolve(element_type[1])
295 elif isinstance(node.type, ast.Array):
296 node.type.element_type = self._resolve(element_type[0])
298 self._transformer.log_node_warning(parent,
299 "Unknown container %r for element-type annotation" % (node.type, ))
301 def _get_transfer_default_param(self, parent, node):
302 if node.direction in [ast.PARAM_DIRECTION_INOUT,
303 ast.PARAM_DIRECTION_OUT]:
304 if node.caller_allocates:
305 return ast.PARAM_TRANSFER_NONE
306 return ast.PARAM_TRANSFER_FULL
307 return ast.PARAM_TRANSFER_NONE
309 def _get_transfer_default_returntype_basic(self, typeval):
310 if (typeval.is_equiv(ast.BASIC_GIR_TYPES)
312 or typeval.is_equiv(ast.TYPE_NONE)):
313 return ast.PARAM_TRANSFER_NONE
314 elif typeval.is_equiv(ast.TYPE_STRING):
315 # Non-const strings default to FULL
316 return ast.PARAM_TRANSFER_FULL
317 elif typeval.target_fundamental:
318 # This looks like just GType right now
322 def _is_gi_subclass(self, typeval, supercls_type):
323 cls = self._transformer.lookup_typenode(typeval)
324 assert cls, str(typeval)
325 supercls = self._transformer.lookup_typenode(supercls_type)
329 if cls.parent and cls.parent.target_giname != 'GObject.Object':
330 return self._is_gi_subclass(cls.parent, supercls_type)
333 def _get_transfer_default_return(self, parent, node):
335 basic = self._get_transfer_default_returntype_basic(typeval)
338 if not typeval.target_giname:
340 target = self._transformer.lookup_typenode(typeval)
341 if isinstance(target, ast.Alias):
342 return self._get_transfer_default_returntype_basic(target.target)
343 elif isinstance(target, glibast.GLibBoxed):
344 return ast.PARAM_TRANSFER_FULL
345 elif isinstance(target, (ast.Enum, ast.Bitfield)):
346 return ast.PARAM_TRANSFER_NONE
347 # Handle constructors specially here
348 elif isinstance(parent, ast.Function) and parent.is_constructor:
349 if isinstance(target, ast.Class):
350 initially_unowned_type = ast.Type(target_giname='GObject.InitiallyUnowned')
351 initially_unowned = self._transformer.lookup_typenode(initially_unowned_type)
352 if initially_unowned and self._is_gi_subclass(typeval, initially_unowned_type):
353 return ast.PARAM_TRANSFER_NONE
355 return ast.PARAM_TRANSFER_FULL
356 elif isinstance(target, (ast.Record, ast.Union)):
357 return ast.PARAM_TRANSFER_FULL
359 assert False, "Invalid constructor"
360 elif isinstance(target, (ast.Class, ast.Record, ast.Union)):
361 # Explicitly no default for these
366 def _get_transfer_default(self, parent, node):
367 if node.type.is_equiv(ast.TYPE_NONE) or isinstance(node.type, ast.Varargs):
368 return ast.PARAM_TRANSFER_NONE
369 elif isinstance(node, ast.Parameter):
370 return self._get_transfer_default_param(parent, node)
371 elif isinstance(node, ast.Return):
372 return self._get_transfer_default_return(parent, node)
373 elif isinstance(node, ast.Field):
374 return ast.PARAM_TRANSFER_NONE
375 elif isinstance(node, ast.Property):
376 return ast.PARAM_TRANSFER_NONE
378 raise AssertionError(node)
380 def _apply_annotations_param_ret_common(self, parent, node, tag):
381 options = getattr(tag, 'options', {})
383 param_type = options.get(OPT_TYPE)
385 node.type = self._resolve(param_type.one(), node.type)
387 caller_allocates = False
388 annotated_direction = None
389 if (OPT_INOUT in options or
390 OPT_INOUT_ALT in options):
391 annotated_direction = ast.PARAM_DIRECTION_INOUT
392 elif OPT_OUT in options:
393 subtype = options[OPT_OUT]
394 if subtype is not None:
395 subtype = subtype.one()
396 annotated_direction = ast.PARAM_DIRECTION_OUT
397 if subtype in (None, ''):
398 if node.type.target_giname and node.type.ctype:
399 caller_allocates = '**' not in node.type.ctype
401 caller_allocates = False
402 elif subtype == 'caller-allocates':
403 caller_allocates = True
404 elif subtype == 'callee-allocates':
405 caller_allocates = False
407 self._transformer.log_warning(
408 "out allocation for %s is invalid (%r)" % (node, subtype), fatal=True)
409 elif OPT_IN in options:
410 annotated_direction = ast.PARAM_DIRECTION_IN
412 if (annotated_direction is not None) and (annotated_direction != node.direction):
413 node.direction = annotated_direction
414 node.caller_allocates = caller_allocates
415 # Also reset the transfer default if we're toggling direction
416 node.transfer = self._get_transfer_default(parent, node)
418 transfer_tag = options.get(TAG_TRANSFER)
420 node.transfer = transfer_tag.one()
422 self._adjust_container_type(parent, node, options)
424 if (OPT_ALLOW_NONE in options or
425 node.type.target_giname == 'Gio.Cancellable'):
426 node.allow_none = True
428 if tag is not None and tag.comment is not None:
429 node.doc = tag.comment
433 value = options.get(key)
435 node.attributes.append((key, value.one()))
437 def _apply_annotations_annotated(self, node, block):
441 node.doc = block.comment
443 since_tag = block.get(TAG_SINCE)
444 if since_tag is not None:
445 node.version = since_tag.value
447 deprecated_tag = block.get(TAG_DEPRECATED)
448 if deprecated_tag is not None:
449 value = deprecated_tag.value
451 version, desc = value.split(': ')
455 node.deprecated = desc
456 if version is not None:
457 node.deprecated_version = version
459 annos_tag = block.get(TAG_ATTRIBUTES)
460 if annos_tag is not None:
461 options = AnnotationParser.parse_options(annos_tag.value)
462 for key, value in options.iteritems():
464 node.attributes.append((key, value.one()))
466 if OPT_SKIP in block.options:
469 if OPT_FOREIGN in block.options:
472 def _apply_annotations_param(self, parent, param, tag):
474 options = tag.options
477 if isinstance(parent, ast.Function):
478 scope = options.get(OPT_SCOPE)
481 if scope not in [ast.PARAM_SCOPE_CALL,
482 ast.PARAM_SCOPE_ASYNC,
483 ast.PARAM_SCOPE_NOTIFIED]:
484 self._transformer.log_warning(parent,
485 "Invalid scope %r for parameter %r" % (scope, param.name))
488 param.transfer = ast.PARAM_TRANSFER_NONE
490 destroy = options.get(OPT_DESTROY)
492 param.destroy_name = self._get_validate_parameter_name(parent,
495 if param.destroy_name is not None:
496 param.scope = ast.PARAM_SCOPE_NOTIFIED
497 destroy_param = parent.get_parameter(param.destroy_name)
498 # This is technically bogus; we're setting the scope on the destroy
499 # itself. But this helps avoid tripping a warning from finaltransformer,
500 # since we don't have a way right now to flag this callback a destroy.
501 destroy_param.scope = ast.PARAM_SCOPE_NOTIFIED
502 closure = options.get(OPT_CLOSURE)
504 param.closure_name = self._get_validate_parameter_name(parent,
507 elif isinstance(parent, ast.Callback):
508 if OPT_CLOSURE in options:
509 # For callbacks, (closure) appears without an
510 # argument, and tags a parameter that is a closure. We
511 # represent it (weirdly) in the gir and typelib by
512 # setting param.closure_name to itself.
513 param.closure_name = param.argname
515 self._apply_annotations_param_ret_common(parent, param, tag)
517 def _apply_annotations_return(self, parent, return_, block):
519 tag = block.get(TAG_RETURNS)
522 self._apply_annotations_param_ret_common(parent, return_, tag)
524 def _apply_annotations_params(self, parent, params, block):
527 tag = block.get(param.argname)
530 self._apply_annotations_param(parent, param, tag)
532 def _apply_annotations_callable(self, node, chain, block):
533 self._apply_annotations_annotated(node, block)
534 self._apply_annotations_params(node, node.parameters, block)
535 self._apply_annotations_return(node, node.retval, block)
537 def _check_arg_annotations(self, parent, params, block):
540 for tag in block.tags.keys():
541 if tag == TAG_RETURNS:
544 if param.argname == tag:
547 self._transformer.log_warning(
548 "Annotation for '%s' refers to unknown argument '%s'"
549 % (parent.name, tag))
551 def _apply_annotations_field(self, parent, block, field):
554 tag = block.get(field.name)
557 t = tag.options.get('type')
560 field.type = self._transformer.create_type_from_user_string(t.one())
562 def _apply_annotations_property(self, parent, prop):
563 block = self._blocks.get('%s:%s' % (parent.type_name, prop.name))
564 self._apply_annotations_annotated(prop, block)
567 transfer_tag = block.get(TAG_TRANSFER)
568 if transfer_tag is not None:
569 prop.transfer = transfer_tag.value
571 prop.transfer = self._get_transfer_default(parent, prop)
572 type_tag = block.get(TAG_TYPE)
574 prop.type = self._resolve(type_tag.value, prop.type)
576 def _apply_annotations_signal(self, parent, signal):
577 block = self._blocks.get('%s::%s' % (parent.type_name, signal.name))
578 self._apply_annotations_annotated(signal, block)
579 # We're only attempting to name the signal parameters if
580 # the number of parameter tags (@foo) is the same or greater
581 # than the number of signal parameters
582 if block and len(block.tags) > len(signal.parameters):
583 names = block.tags.items()
586 for i, param in enumerate(signal.parameters):
588 name, tag = names[i+1]
590 options = getattr(tag, 'options', {})
591 param_type = options.get(OPT_TYPE)
593 param.type = self._resolve(param_type.one(), param.type)
596 self._apply_annotations_param(signal, param, tag)
597 self._apply_annotations_return(signal, signal.retval, block)
599 def _pass_read_annotations2(self, node, chain):
600 if isinstance(node, ast.Function):
601 self._apply_annotations2_function(node, chain)
604 def _apply_annotations2_function(self, node, chain):
605 # Handle virtual invokers
606 parent = chain[-1] if chain else None
607 block = self._blocks.get(node.symbol)
608 if not (block and parent):
610 virtual = block.get(TAG_VFUNC)
613 invoker_name = virtual.value
615 for vfunc in parent.virtual_methods:
616 if vfunc.name == invoker_name:
618 vfunc.invoker = node.name
619 # Also merge in annotations
620 self._apply_annotations_callable(vfunc, [parent], block)
623 self._transformer.log_symbol_warning(node.symbol,
624 "Virtual slot %r not found for %r annotation" % (invoker_name, TAG_VFUNC))
626 def _resolve_and_filter_type_list(self, typelist):
627 """Given a list of Type instances, return a new list of types with
628 the ones that failed to resolve removed."""
629 # Create a copy we'll modify
630 new_typelist = list(typelist)
631 for typeval in typelist:
632 resolved = self._transformer.resolve_type(typeval)
634 new_typelist.remove(typeval)
637 def _pass_type_resolution(self, node, chain):
638 if isinstance(node, ast.Alias):
639 self._transformer.resolve_type(node.target)
640 if isinstance(node, ast.Callable):
641 for parameter in node.parameters:
642 self._transformer.resolve_type(parameter.type)
643 self._transformer.resolve_type(node.retval.type)
644 if isinstance(node, ast.Constant):
645 self._transformer.resolve_type(node.value_type)
646 if isinstance(node, (ast.Class, ast.Interface, ast.Record, ast.Union)):
647 for field in node.fields:
648 if field.anonymous_node:
651 self._transformer.resolve_type(field.type)
652 if isinstance(node, (ast.Class, ast.Interface)):
653 resolved_parent = None
654 for parent in node.parent_chain:
656 self._transformer.resolve_type(parent)
657 except ValueError, e:
659 target = self._transformer.lookup_typenode(parent)
664 if isinstance(node, ast.Interface) or not node.fundamental:
665 node.parent = ast.Type(target_giname='GObject.Object')
668 for prop in node.properties:
669 self._transformer.resolve_type(prop.type)
670 for sig in node.signals:
671 for param in sig.parameters:
672 self._transformer.resolve_type(param.type)
673 if isinstance(node, ast.Class):
674 node.interfaces = self._resolve_and_filter_type_list(node.interfaces)
675 if isinstance(node, ast.Interface):
676 node.prerequisites = self._resolve_and_filter_type_list(node.prerequisites)
679 def _resolve_quarks(self):
680 # self._uscore_type_names is an authoritative mapping of types
681 # to underscored versions, since it is based on get_type() methods;
682 # but only covers enums that are registered as GObject enums.
683 # Create a fallback mapping based on all known enums in this module.
685 for enum in self._namespace.itervalues():
686 if not isinstance(enum, ast.Enum):
688 type_name = enum.symbol
689 uscored = to_underscores(type_name).lower()
691 uscore_enums[uscored] = enum
694 no_uscore_prefixed = self._transformer.strip_identifier(type_name)
695 except TransformerException, e:
697 no_uscore_prefixed = None
699 if no_uscore_prefixed not in uscore_enums:
700 uscore_enums[no_uscore_prefixed] = enum
702 for node in self._namespace.itervalues():
703 if not isinstance(node, ast.Function):
705 if node.retval.type.target_giname != 'GLib.Quark':
707 short = node.symbol[:-len('_quark')]
708 if short == "g_io_error":
709 # Special case; GIOError was already taken forcing GIOErrorEnum
710 assert self._namespace.name == 'Gio'
711 enum = self._namespace.get('IOErrorEnum')
713 enum = self._uscore_type_names.get(short)
715 enum = uscore_enums.get(short)
717 enum.error_quark = node.symbol
719 self._transformer.log_node_warning(node,
720 """%s: Couldn't find corresponding enumeration""" % (node.symbol, ))
722 def _split_uscored_by_type(self, uscored):
723 """'uscored' should be an un-prefixed uscore string. This
724 function searches through the namespace for the longest type which
725 prefixes uscored, and returns (type, suffix). Example, assuming
726 namespace Gtk, type is TextBuffer:
728 _split_uscored_by_type(text_buffer_try_new) -> (ast.Class(TextBuffer), 'try_new')"""
731 prev_split_count = -1
733 components = uscored.rsplit('_', count)
734 if len(components) == prev_split_count:
736 prev_split_count = len(components)
737 type_string = components[0]
738 node = self._uscore_type_names.get(type_string)
740 return (node, '_'.join(components[1:]))
743 def _pair_function(self, func):
744 """Check to see whether a toplevel function should be a
745 method or constructor of some type."""
746 if func.symbol.endswith('_get_type') or func.symbol.startswith('_'):
748 (ns, subsymbol) = self._transformer.split_csymbol(func.symbol)
749 assert ns == self._namespace
750 if self._pair_constructor(func, subsymbol):
752 elif self._pair_method(func, subsymbol):
754 elif self._pair_static_method(func, subsymbol):
757 def _uscored_identifier_for_type(self, typeval):
758 """Given a Type(target_giname='Foo.BarBaz'), return 'bar_baz'."""
759 name = typeval.get_giname()
760 return to_underscores_noprefix(name).lower()
762 def _pair_method(self, func, subsymbol):
763 if not func.parameters:
765 first = func.parameters[0]
766 target = self._transformer.lookup_typenode(first.type)
767 if not isinstance(target, (ast.Class, ast.Interface,
768 ast.Record, ast.Union,
769 glibast.GLibBoxedOther)):
772 # A quick hack here...in the future we should catch C signature/GI signature
773 # mismatches in a general way in finaltransformer
774 if first.type.ctype.count('*') != 1:
777 uscored = self._uscored_identifier_for_type(first.type)
778 if not subsymbol.startswith(uscored):
780 del func.parameters[0]
781 subsym_idx = func.symbol.find(subsymbol)
782 self._namespace.float(func)
783 func.name = func.symbol[(subsym_idx + len(uscored) + 1):]
784 target.methods.append(func)
785 func.is_method = True
788 def _pair_static_method(self, func, subsymbol):
789 split = self._split_uscored_by_type(subsymbol)
792 (node, funcname) = split
793 if not isinstance(node, (ast.Class, ast.Interface,
794 ast.Record, ast.Union, glibast.GLibBoxedOther)):
796 self._namespace.float(func)
798 node.static_methods.append(func)
800 def _pair_constructor(self, func, subsymbol):
801 if not (func.symbol.find('_new_') >= 0 or func.symbol.endswith('_new')):
803 target = self._transformer.lookup_typenode(func.retval.type)
804 if not isinstance(target, (ast.Class, glibast.GLibBoxed)):
806 new_idx = func.symbol.rfind('_new')
807 assert (new_idx >= 0)
808 prefix = func.symbol[:new_idx]
809 split = self._split_uscored_by_type(subsymbol)
811 # TODO - need a e.g. (method) annotation
812 self._transformer.log_node_warning(func,
813 "Can't find matching type for constructor; symbol=%r" % (func.symbol, ))
815 (origin_node, funcname) = split
816 if isinstance(target, ast.Class):
818 while parent and (not parent.create_type().target_giname == 'GObject.Object'):
822 parent = self._transformer.lookup_typenode(parent.parent)
826 self._transformer.log_node_warning(func,
827 "Return value is not superclass for constructor; symbol=%r constructed=%r return=%r"
828 % (func.symbol, str(origin_node.create_type()), str(func.retval.type)))
831 if origin_node != target:
832 self._transformer.log_node_warning(func,
833 "Constructor return type mismatch symbol=%r constructed=%r return=%r"
834 % (func.symbol, str(origin_node.create_type()), str(func.retval.type)))
836 self._namespace.float(func)
838 func.is_constructor = True
839 target.constructors.append(func)
840 # Constructors have default return semantics
841 if not func.retval.transfer:
842 func.retval.transfer = self._get_transfer_default_return(func, func.retval)
845 def _pair_class_virtuals(self, node):
846 """Look for virtual methods from the class structure."""
847 if not node.glib_type_struct:
848 self._transformer.log_node_warning(node,
849 "Failed to find class structure for %r" % (node.name, ))
852 node_type = node.create_type()
853 class_struct = self._transformer.lookup_typenode(node.glib_type_struct)
855 # Object class fields are assumed to be read-only
856 # (see also _introspect_object and transformer.py)
857 for field in class_struct.fields:
858 if isinstance(field, ast.Field):
859 field.writable = False
861 # Loop through fields to determine which are virtual
862 # functions and which are signal slots by
863 # assuming everything that doesn't share a name
864 # with a known signal is a virtual slot.
865 for field in class_struct.fields:
866 if not isinstance(field.anonymous_node, ast.Callback):
868 callback = field.anonymous_node
869 # Check the first parameter is the object
870 if len(callback.parameters) == 0:
872 firstparam_type = callback.parameters[0].type
873 if firstparam_type != node_type:
875 # Also double check we don't have a signal with this
877 matched_signal = False
878 for signal in node.signals:
879 if signal.name.replace('-', '_') == callback.name:
880 matched_signal = True
884 vfunc = ast.VFunction.from_callback(callback)
885 vfunc.inherit_file_positions(callback)
886 node.virtual_methods.append(vfunc)
888 # Take the set of virtual methods we found, and try
889 # to pair up with any matching methods using the
891 for vfunc in node.virtual_methods:
892 for method in node.methods:
893 if method.name != vfunc.name:
895 if method.retval.type != vfunc.retval.type:
897 if len(method.parameters) != len(vfunc.parameters):
899 for i in xrange(len(method.parameters)):
900 m_type = method.parameters[i].type
901 v_type = vfunc.parameters[i].type
904 vfunc.invoker = method.name
905 # Apply any annotations we have from the invoker to
907 block = self._blocks.get(method.symbol)
908 self._apply_annotations_callable(vfunc, [], block)
910 def _pass3(self, node, chain):
911 """Pass 3 is after we've loaded GType data and performed type
913 if isinstance(node, ast.Callable):
914 self._pass3_callable_callbacks(node)
915 self._pass3_callable_throws(node)
918 def _pass3_callable_callbacks(self, node):
919 """Check to see if we have anything that looks like a
920 callback+user_data+GDestroyNotify set."""
922 params = node.parameters
924 # First, do defaults for well-known callback types
925 for i, param in enumerate(params):
926 argnode = self._transformer.lookup_typenode(param.type)
927 if isinstance(argnode, ast.Callback):
928 if param.type.target_giname in ('Gio.AsyncReadyCallback',
929 'GLib.DestroyNotify'):
930 param.scope = ast.PARAM_SCOPE_ASYNC
931 param.transfer = ast.PARAM_TRANSFER_NONE
933 callback_param = None
934 for i, param in enumerate(params):
935 argnode = self._transformer.lookup_typenode(param.type)
936 is_destroynotify = False
937 if isinstance(argnode, ast.Callback):
938 if param.type.target_giname == 'GLib.DestroyNotify':
939 is_destroynotify = True
941 callback_param = param
943 if callback_param is None:
946 callback_param.destroy_name = param.argname
947 callback_param.scope = ast.PARAM_SCOPE_NOTIFIED
948 callback_param.transfer = ast.PARAM_TRANSFER_NONE
949 elif (param.type.is_equiv(ast.TYPE_ANY) and
950 param.argname is not None and
951 param.argname.endswith('data')):
952 callback_param.closure_name = param.argname
954 def _pass3_callable_throws(self, node):
955 """Check to see if we have anything that looks like a
956 callback+user_data+GDestroyNotify set."""
957 if not node.parameters:
959 last_param = node.parameters[-1]
960 # Checking type.name=='GLib.Error' generates false positives
961 # on methods that take a 'GError *'
962 if last_param.type.ctype == 'GError**':
963 node.parameters.pop()