diff options
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 35300b0..f32486c 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 d2bae87..0f0d1bb 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 88510b0..e30bc6b 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 5a7a96d..61d4cef 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) |