Web   ·   Wiki   ·   Activities   ·   Blog   ·   Lists   ·   Chat   ·   Meeting   ·   Bugs   ·   Git   ·   Translate   ·   Archive   ·   People   ·   Donate
summaryrefslogtreecommitdiffstats
path: root/giscanner
diff options
context:
space:
mode:
Diffstat (limited to 'giscanner')
-rw-r--r--giscanner/annotationparser.py57
-rw-r--r--giscanner/ast.py36
-rw-r--r--giscanner/girwriter.py47
-rw-r--r--giscanner/glibtransformer.py73
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)