diff options
author | Colin Walters <walters@verbum.org> | 2009-02-27 19:02:48 -0500 |
---|---|---|
committer | Colin Walters <walters@verbum.org> | 2009-03-05 15:52:12 -0500 |
commit | fdbe3cc3e1cfaa546648a76b1dca72beead0b65b (patch) | |
tree | 01156e22ec59d29c642d59ce7ad75f383d77466a /giscanner | |
parent | b8e3172424ba956a0d18eae8deb305310b2cab74 (diff) | |
download | gobject-introspection-fdbe3cc3e1cfaa546648a76b1dca72beead0b65b.tar.gz |
Bug 557383 - Virtual method support
Broadly speaking, this change adds the concept of <vfunc> to the .gir.
The typelib already had most of the infrastructure for virtual functions,
though there is one API addition.
The scanner assumes that any class callback slot that doesn't match
a signal name is a virtual. In the .gir, we write out *both* the <method>
wrapper and a <vfunc>. If we can determine an association between
them (based on the names matching, or a new Virtual: annotation),
then we notate that in the .gir.
The typelib gains an association from the vfunc to the function, if
it exists. This will be useful for bindings since they already know
how to consume FunctionInfo.
Diffstat (limited to 'giscanner')
-rw-r--r-- | giscanner/annotationparser.py | 57 | ||||
-rw-r--r-- | giscanner/ast.py | 36 | ||||
-rw-r--r-- | giscanner/girwriter.py | 47 | ||||
-rw-r--r-- | giscanner/glibtransformer.py | 73 |
4 files changed, 157 insertions, 56 deletions
diff --git a/giscanner/annotationparser.py b/giscanner/annotationparser.py index 35300b03..f32486c0 100644 --- a/giscanner/annotationparser.py +++ b/giscanner/annotationparser.py @@ -41,6 +41,7 @@ from .glibast import GLibBoxed _COMMENT_HEADER = '*\n ' # Tags - annotations applyed to comment blocks +TAG_VFUNC = 'virtual' TAG_SINCE = 'since' TAG_DEPRECATED = 'deprecated' TAG_RETURNS = 'returns' @@ -298,8 +299,9 @@ class AnnotationApplier(object): block = self._blocks.get(class_.type_name) self._parse_node_common(class_, block) self._parse_constructors(class_.constructors) - self._parse_methods(class_.methods) - self._parse_methods(class_.static_methods) + self._parse_methods(class_, class_.methods) + self._parse_vfuncs(class_, class_.virtual_methods) + self._parse_methods(class_, class_.static_methods) self._parse_properties(class_, class_.properties) self._parse_signals(class_, class_.signals) self._parse_fields(class_, class_.fields) @@ -309,7 +311,8 @@ class AnnotationApplier(object): def _parse_interface(self, interface): block = self._blocks.get(interface.type_name) self._parse_node_common(interface, block) - self._parse_methods(interface.methods) + self._parse_methods(interface, interface.methods) + self._parse_vfuncs(interface, interface.virtual_methods) self._parse_properties(interface, interface.properties) self._parse_signals(interface, interface.signals) self._parse_fields(interface, interface.fields) @@ -320,7 +323,7 @@ class AnnotationApplier(object): block = self._blocks.get(record.symbol) self._parse_node_common(record, block) self._parse_constructors(record.constructors) - self._parse_methods(record.methods) + self._parse_methods(record, record.methods) self._parse_fields(record, record.fields) if block: record.doc = block.comment @@ -329,7 +332,7 @@ class AnnotationApplier(object): block = self._blocks.get(boxed.name) self._parse_node_common(boxed, block) self._parse_constructors(boxed.constructors) - self._parse_methods(boxed.methods) + self._parse_methods(boxed, boxed.methods) if block: boxed.doc = block.comment @@ -338,7 +341,7 @@ class AnnotationApplier(object): self._parse_node_common(union, block) self._parse_fields(union, union.fields) self._parse_constructors(union.constructors) - self._parse_methods(union.methods) + self._parse_methods(union, union.methods) if block: union.doc = block.comment @@ -370,9 +373,13 @@ class AnnotationApplier(object): for prop in properties: self._parse_property(parent, prop) - def _parse_methods(self, methods): + def _parse_methods(self, parent, methods): for method in methods: - self._parse_function(method) + self._parse_method(parent, method) + + def _parse_vfuncs(self, parent, vfuncs): + for vfunc in vfuncs: + self._parse_vfunc(parent, vfunc) def _parse_signals(self, parent, signals): for signal in signals: @@ -392,18 +399,20 @@ class AnnotationApplier(object): if block: callback.doc = block.comment + def _parse_callable(self, callable, block): + self._parse_node_common(callable, block) + self._parse_params(callable, callable.parameters, block) + self._parse_return(callable, callable.retval, block) + if block: + callable.doc = block.comment + def _parse_function(self, func): block = self._blocks.get(func.symbol) - self._parse_node_common(func, block) - self._parse_params(func, func.parameters, block) - self._parse_return(func, func.retval, block) - if block: - func.doc = block.comment + self._parse_callable(func, block) def _parse_signal(self, parent, signal): block = self._blocks.get('%s::%s' % (parent.type_name, signal.name)) self._parse_node_common(signal, block) - self._parse_deprecated(signal, block) # We're only attempting to name the signal parameters if # the number of parameter tags (@foo) is the same or greater # than the number of signal parameters @@ -426,6 +435,26 @@ class AnnotationApplier(object): if block: signal.doc = block.comment + def _parse_method(self, parent, meth): + block = self._blocks.get(meth.symbol) + self._parse_function(meth) + virtual = self._get_tag(block, TAG_VFUNC) + if virtual: + invoker_name = virtual.value + matched = False + for vfunc in parent.virtual_methods: + if vfunc.name == invoker_name: + matched = True + vfunc.invoker = meth.name + break + if not matched: + print "warning: unmatched virtual invoker %r for method %r" % \ + (invoker_name, meth.symbol) + + def _parse_vfunc(self, parent, vfunc): + key = '%s::%s' % (parent.type_name, vfunc.name) + self._parse_callable(vfunc, self._blocks.get(key)) + def _parse_field(self, parent, field): if isinstance(field, Callback): self._parse_callback(field) diff --git a/giscanner/ast.py b/giscanner/ast.py index d2bae873..0f0d1bb5 100644 --- a/giscanner/ast.py +++ b/giscanner/ast.py @@ -196,15 +196,25 @@ class Include(Node): def __str__(self): return '%s-%s' % (self.name, self.version) +class Callable(Node): -class Function(Node): - - def __init__(self, name, retval, parameters, symbol, throws=None): + def __init__(self, name, retval, parameters, throws): Node.__init__(self, name) self.retval = retval self.parameters = parameters - self.symbol = symbol self.throws = not not throws + self.doc = None + + def __repr__(self): + return '%s(%r, %r, %r)' % (self.__class__.__name__, + self.name, self.retval, + self.parameters) + +class Function(Callable): + + def __init__(self, name, retval, parameters, symbol, throws=None): + Callable.__init__(self, name, retval, parameters, throws) + self.symbol = symbol self.is_method = False self.doc = None @@ -218,14 +228,18 @@ class Function(Node): if parameter.name == name: return parameter - def __repr__(self): - return '%s(%r, %r, %r)' % (self.__class__.__name__, - self.name, self.retval, - self.parameters) +class VFunction(Callable): -class VFunction(Function): - pass + def __init__(self, name, retval, parameters, throws): + Callable.__init__(self, name, retval, parameters, throws) + self.invoker = None + + @classmethod + def from_callback(cls, cb): + obj = cls(cb.name, cb.retval, cb.parameters[1:], + cb.throws) + return obj class Type(Node): @@ -411,6 +425,7 @@ class Class(Node): self.glib_type_struct = None self.is_abstract = is_abstract self.methods = [] + self.virtual_methods = [] self.static_methods = [] self.interfaces = [] self.constructors = [] @@ -430,6 +445,7 @@ class Interface(Node): Node.__init__(self, name) self.parent = parent self.methods = [] + self.virtual_methods = [] self.glib_type_struct = None self.properties = [] self.fields = [] diff --git a/giscanner/girwriter.py b/giscanner/girwriter.py index 88510b04..e30bc6b9 100644 --- a/giscanner/girwriter.py +++ b/giscanner/girwriter.py @@ -158,18 +158,22 @@ and/or use gtk-doc annotations. ''') attrs.append(('c:type', alias.ctype)) self.write_tag('alias', attrs) - def _write_function(self, func, tag_name='function'): - attrs = [('name', func.name), - ('c:identifier', func.symbol)] - if func.doc: - attrs.append(('doc', func.doc)) - self._append_version(func, attrs) - self._append_deprecated(func, attrs) - self._append_throws(func, attrs) + def _write_callable(self, callable, tag_name, extra_attrs): + attrs = [('name', callable.name)] + attrs.extend(extra_attrs) + if callable.doc: + attrs.append(('doc', callable.doc)) + self._append_version(callable, attrs) + self._append_deprecated(callable, attrs) + self._append_throws(callable, attrs) with self.tagcontext(tag_name, attrs): - self._write_attributes(func) - self._write_return_type(func.retval) - self._write_parameters(func.parameters) + self._write_attributes(callable) + self._write_return_type(callable.retval) + self._write_parameters(callable.parameters) + + def _write_function(self, func, tag_name='function'): + attrs = [('c:identifier', func.symbol)] + self._write_callable(func, tag_name, attrs) def _write_method(self, method): self._write_function(method, tag_name='method') @@ -354,6 +358,8 @@ and/or use gtk-doc annotations. ''') self._write_constructor(method) for method in node.static_methods: self._write_static_method(method) + for vfunc in node.virtual_methods: + self._write_vfunc(vfunc) for method in node.methods: self._write_method(method) for prop in node.properties: @@ -395,18 +401,15 @@ and/or use gtk-doc annotations. ''') self._write_attributes(prop) self._write_type(prop.type) + def _write_vfunc(self, vf): + attrs = [] + if vf.invoker: + attrs.append(('invoker', vf.invoker)) + self._write_callable(vf, 'virtual-method', attrs) + def _write_callback(self, callback): - # FIXME: reuse _write_function - attrs = [('name', callback.name), ('c:type', callback.ctype)] - if callback.doc: - attrs.append(('doc', callback.doc)) - self._append_version(callback, attrs) - self._append_deprecated(callback, attrs) - self._append_throws(callback, attrs) - with self.tagcontext('callback', attrs): - self._write_attributes(callback) - self._write_return_type(callback.retval) - self._write_parameters(callback.parameters) + attrs = [('c:type', callback.ctype)] + self._write_callable(callback, 'callback', attrs) def _boxed_attrs(self, boxed): return [('glib:type-name', boxed.type_name), diff --git a/giscanner/glibtransformer.py b/giscanner/glibtransformer.py index 5a7a96d2..61d4cefc 100644 --- a/giscanner/glibtransformer.py +++ b/giscanner/glibtransformer.py @@ -27,12 +27,13 @@ import subprocess from .ast import (Alias, Bitfield, Callback, Constant, Enum, Function, Member, Namespace, Parameter, Property, Record, Return, Type, Union, - Field, type_name_from_ctype, + Field, VFunction, type_name_from_ctype, default_array_types, TYPE_UINT8, PARAM_TRANSFER_FULL) from .transformer import Names from .glibast import (GLibBoxed, GLibEnum, GLibEnumMember, GLibFlags, GLibInterface, GLibObject, GLibSignal, GLibBoxedStruct, - GLibBoxedUnion, GLibBoxedOther, GLibRecord, type_names) + GLibBoxedUnion, GLibBoxedOther, GLibRecord, + type_names) from .utils import to_underscores, to_underscores_noprefix default_array_types['guchar*'] = TYPE_UINT8 @@ -159,6 +160,10 @@ class GLibTransformer(object): except KeyError, e: print "WARNING: DELETING node %s: %s" % (node.name, e) self._remove_attribute(node.name) + # Another pass, since we need to have the methods parsed + # in order to correctly modify them after class/record + # pairing + for (ns, node) in nodes: # associate GtkButtonClass with GtkButton if isinstance(node, Record): self._pair_class_record(node) @@ -167,7 +172,9 @@ class GLibTransformer(object): self._resolve_quarks() # Fourth pass: ensure all types are known if not self._noclosure: - self._validate(nodes) + self._resolve_types(nodes) + + self._validate(nodes) # Create a new namespace with what we found namespace = Namespace(self._namespace_name, self._namespace_version) @@ -573,10 +580,43 @@ class GLibTransformer(object): for field in maybe_class.fields: if isinstance(field, Field): field.writable = False - # TODO: remove this, we should be computing vfuncs instead - if isinstance(pair_class, GLibInterface): - for field in maybe_class.fields[1:]: - pair_class.fields.append(field) + + # Loop through fields to determine which are virtual + # functions and which are signal slots by + # assuming everything that doesn't share a name + # with a known signal is a virtual slot. + for field in maybe_class.fields: + if not isinstance(field, Callback): + continue + # Check the first parameter is the object + if len(field.parameters) == 0: + continue + firstparam_type = field.parameters[0].type + if firstparam_type != pair_class: + continue + # Also double check we don't have a signal with this + # name. + matched_signal = False + for signal in pair_class.signals: + if signal.name.replace('-', '_') == field.name: + matched_signal = True + break + if matched_signal: + continue + vfunc = VFunction.from_callback(field) + pair_class.virtual_methods.append(vfunc) + + # Take the set of virtual methods we found, and try + # to pair up with any matching methods using the + # name+signature. + for vfunc in pair_class.virtual_methods: + for method in pair_class.methods: + if (method.name != vfunc.name or + method.retval != vfunc.retval or + method.parameters != vfunc.parameters): + continue + vfunc.invoker = method.name + gclass_struct = GLibRecord.from_record(class_struct) self._remove_attribute(class_struct.name) self._add_attribute(gclass_struct, True) @@ -903,9 +943,7 @@ class GLibTransformer(object): def _resolve_alias(self, alias): alias.target = self._resolve_type_name(alias.target, alias.target) - # Validation - - def _validate(self, nodes): + def _resolve_types(self, nodes): nodes = list(self._names.names.itervalues()) i = 0 self._validating = True @@ -925,3 +963,18 @@ class GLibTransformer(object): i += 1 self._print_statistics() self._validating = False + + # Validation + + def _validate_interface(self, iface): + for vfunc in iface.virtual_methods: + if not vfunc.invoker: + print ("warning: Interface %r virtual function %r " + \ + "has no known invoker") % (iface.name, vfunc.name) + + # This function is called at the very end, before we hand back the + # completed namespace to the writer. Add static analysis checks here. + def _validate(self, nodes): + for (name, node) in nodes: + if isinstance(node, GLibInterface): + self._validate_interface(node) |