Commit fdbe3cc3 authored by Colin Walters's avatar Colin Walters

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.
parent b8e31724
...@@ -1527,6 +1527,63 @@ g_object_info_get_vfunc (GIObjectInfo *info, ...@@ -1527,6 +1527,63 @@ g_object_info_get_vfunc (GIObjectInfo *info,
base->typelib, offset); base->typelib, offset);
} }
static GIVFuncInfo *
find_vfunc (GIBaseInfo *base,
guint32 offset,
gint n_vfuncs,
const gchar *name)
{
/* FIXME hash */
Header *header = (Header *)base->typelib->data;
gint i;
for (i = 0; i < n_vfuncs; i++)
{
VFuncBlob *fblob = (VFuncBlob *)&base->typelib->data[offset];
const gchar *fname = (const gchar *)&base->typelib->data[fblob->name];
if (strcmp (name, fname) == 0)
return (GIVFuncInfo *) g_info_new (GI_INFO_TYPE_VFUNC, base,
base->typelib, offset);
offset += header->vfunc_blob_size;
}
return NULL;
}
/**
* g_object_info_find_vfunc:
* @info: An #GIObjectInfo
* @name: The name of a virtual function to find.
*
* Locate a virtual function slot with name @name. Note that the namespace
* for virtuals is distinct from that of methods; there may or may not be
* a concrete method associated for a virtual. If there is one, it may
* be retrieved using #g_vfunc_info_get_invoker. See the documentation for
* that function for more information on invoking virtuals.
*
* Return value: (transfer full): A #GIVFuncInfo, or %NULL if none with name @name.
*/
GIVFuncInfo *
g_object_info_find_vfunc (GIObjectInfo *info,
const gchar *name)
{
gint offset;
GIBaseInfo *base = (GIBaseInfo *)info;
Header *header = (Header *)base->typelib->data;
ObjectBlob *blob = (ObjectBlob *)&base->typelib->data[base->offset];
offset = base->offset + header->object_blob_size
+ (blob->n_interfaces + blob->n_interfaces % 2) * 2
+ blob->n_fields * header->field_blob_size
+ blob->n_properties * header->property_blob_size
+ blob->n_methods * header->function_blob_size
+ blob->n_signals * header->signal_blob_size;
return find_vfunc (base, offset, blob->n_vfuncs, name);
}
gint gint
g_object_info_get_n_constants (GIObjectInfo *info) g_object_info_get_n_constants (GIObjectInfo *info)
{ {
...@@ -1728,6 +1785,34 @@ g_interface_info_get_vfunc (GIInterfaceInfo *info, ...@@ -1728,6 +1785,34 @@ g_interface_info_get_vfunc (GIInterfaceInfo *info,
base->typelib, offset); base->typelib, offset);
} }
/**
* g_interface_info_find_vfunc:
* @info: An #GIObjectInfo
* @name: The name of a virtual function to find.
*
* Locate a virtual function slot with name @name. See the documentation
* for #g_object_info_find_vfunc for more information on virtuals.
*
* Return value: (transfer full): A #GIVFuncInfo, or %NULL if none with name @name.
*/
GIVFuncInfo *
g_interface_info_find_vfunc (GIInterfaceInfo *info,
const gchar *name)
{
gint offset;
GIBaseInfo *base = (GIBaseInfo *)info;
Header *header = (Header *)base->typelib->data;
InterfaceBlob *blob = (InterfaceBlob *)&base->typelib->data[base->offset];
offset = base->offset + header->interface_blob_size
+ (blob->n_prerequisites + blob->n_prerequisites % 2) * 2
+ blob->n_properties * header->property_blob_size
+ blob->n_methods * header->function_blob_size
+ blob->n_signals * header->signal_blob_size;
return find_vfunc (base, offset, blob->n_vfuncs, name);
}
gint gint
g_interface_info_get_n_constants (GIInterfaceInfo *info) g_interface_info_get_n_constants (GIInterfaceInfo *info)
{ {
...@@ -1913,6 +1998,37 @@ g_vfunc_info_get_signal (GIVFuncInfo *info) ...@@ -1913,6 +1998,37 @@ g_vfunc_info_get_signal (GIVFuncInfo *info)
return NULL; return NULL;
} }
/**
* g_vfunc_info_get_invoker:
* @info: A #GIVFuncInfo
*
* If this virtual function has an associated invoker method, this
* method will return it. An invoker method is a C entry point.
*
* Not all virtuals will have invokers.
*
* Return value: (transfer full): An invoker function, or %NULL if none known
*/
GIFunctionInfo *
g_vfunc_info_get_invoker (GIVFuncInfo *info)
{
GIBaseInfo *base = (GIBaseInfo *)info;
VFuncBlob *blob = (VFuncBlob *)&base->typelib->data[base->offset];
GIBaseInfo *container = base->container;
GIInfoType parent_type;
/* 1023 = 0x3ff is the maximum of the 10 bits for invoker index */
if (blob->invoker == 1023)
return NULL;
parent_type = g_base_info_get_type (container);
if (parent_type == GI_INFO_TYPE_OBJECT)
return g_object_info_get_method ((GIObjectInfo*)container, blob->invoker);
else if (parent_type == GI_INFO_TYPE_INTERFACE)
return g_interface_info_get_method ((GIInterfaceInfo*)container, blob->invoker);
else
g_assert_not_reached ();
}
/* GIConstantInfo functions */ /* GIConstantInfo functions */
GITypeInfo * GITypeInfo *
......
...@@ -470,6 +470,8 @@ GISignalInfo * g_object_info_get_signal (GIObjectInfo *in ...@@ -470,6 +470,8 @@ GISignalInfo * g_object_info_get_signal (GIObjectInfo *in
gint g_object_info_get_n_vfuncs (GIObjectInfo *info); gint g_object_info_get_n_vfuncs (GIObjectInfo *info);
GIVFuncInfo * g_object_info_get_vfunc (GIObjectInfo *info, GIVFuncInfo * g_object_info_get_vfunc (GIObjectInfo *info,
gint n); gint n);
GIVFuncInfo * g_object_info_find_vfunc (GIObjectInfo *info,
const gchar *name);
gint g_object_info_get_n_constants (GIObjectInfo *info); gint g_object_info_get_n_constants (GIObjectInfo *info);
GIConstantInfo * g_object_info_get_constant (GIObjectInfo *info, GIConstantInfo * g_object_info_get_constant (GIObjectInfo *info,
gint n); gint n);
...@@ -495,6 +497,8 @@ GISignalInfo * g_interface_info_get_signal (GIInterfaceInfo *in ...@@ -495,6 +497,8 @@ GISignalInfo * g_interface_info_get_signal (GIInterfaceInfo *in
gint g_interface_info_get_n_vfuncs (GIInterfaceInfo *info); gint g_interface_info_get_n_vfuncs (GIInterfaceInfo *info);
GIVFuncInfo * g_interface_info_get_vfunc (GIInterfaceInfo *info, GIVFuncInfo * g_interface_info_get_vfunc (GIInterfaceInfo *info,
gint n); gint n);
GIVFuncInfo * g_interface_info_find_vfunc (GIInterfaceInfo *info,
const gchar *name);
gint g_interface_info_get_n_constants (GIInterfaceInfo *info); gint g_interface_info_get_n_constants (GIInterfaceInfo *info);
GIConstantInfo * g_interface_info_get_constant (GIInterfaceInfo *info, GIConstantInfo * g_interface_info_get_constant (GIInterfaceInfo *info,
gint n); gint n);
...@@ -527,6 +531,7 @@ typedef enum ...@@ -527,6 +531,7 @@ typedef enum
GIVFuncInfoFlags g_vfunc_info_get_flags (GIVFuncInfo *info); GIVFuncInfoFlags g_vfunc_info_get_flags (GIVFuncInfo *info);
gint g_vfunc_info_get_offset (GIVFuncInfo *info); gint g_vfunc_info_get_offset (GIVFuncInfo *info);
GISignalInfo * g_vfunc_info_get_signal (GIVFuncInfo *info); GISignalInfo * g_vfunc_info_get_signal (GIVFuncInfo *info);
GIFunctionInfo * g_vfunc_info_get_invoker (GIVFuncInfo *info);
/* GIConstantInfo */ /* GIConstantInfo */
......
...@@ -265,6 +265,7 @@ g_ir_node_free (GIrNode *node) ...@@ -265,6 +265,7 @@ g_ir_node_free (GIrNode *node)
GIrNodeVFunc *vfunc = (GIrNodeVFunc *)node; GIrNodeVFunc *vfunc = (GIrNodeVFunc *)node;
g_free (node->name); g_free (node->name);
g_free (vfunc->invoker);
for (l = vfunc->parameters; l; l = l->next) for (l = vfunc->parameters; l; l = l->next)
g_ir_node_free ((GIrNode *)l->data); g_ir_node_free ((GIrNode *)l->data);
g_list_free (vfunc->parameters); g_list_free (vfunc->parameters);
...@@ -1186,6 +1187,30 @@ g_ir_find_node (GIrModule *module, ...@@ -1186,6 +1187,30 @@ g_ir_find_node (GIrModule *module,
return node != NULL; return node != NULL;
} }
static int
get_index_of_member_type (GIrNodeInterface *node,
GIrNodeTypeId type,
const char *name)
{
guint index = -1;
GList *l;
for (l = node->members; l; l = l->next)
{
GIrNode *node = l->data;
if (node->type != type)
continue;
index++;
if (strcmp (node->name, name) == 0)
break;
}
return index;
}
static void static void
serialize_type (GIrModule *module, serialize_type (GIrModule *module,
GList *modules, GList *modules,
...@@ -1759,6 +1784,18 @@ g_ir_node_build_typelib (GIrNode *node, ...@@ -1759,6 +1784,18 @@ g_ir_node_build_typelib (GIrNode *node,
blob->class_closure = 0; /* FIXME */ blob->class_closure = 0; /* FIXME */
blob->reserved = 0; blob->reserved = 0;
if (vfunc->invoker)
{
int index = get_index_of_member_type ((GIrNodeInterface*)parent, G_IR_NODE_FUNCTION, vfunc->invoker);
if (index == -1)
{
g_error ("Unknown member function %s for vfunc %s", vfunc->invoker, node->name);
}
blob->invoker = (guint) index;
}
else
blob->invoker = 0x3ff; /* max of 10 bits */
blob->struct_offset = vfunc->offset; blob->struct_offset = vfunc->offset;
blob->reserved2 = 0; blob->reserved2 = 0;
blob->signature = signature; blob->signature = signature;
......
...@@ -214,6 +214,8 @@ struct _GIrNodeVFunc ...@@ -214,6 +214,8 @@ struct _GIrNodeVFunc
gboolean must_not_be_implemented; gboolean must_not_be_implemented;
gboolean is_class_closure; gboolean is_class_closure;
char *invoker;
GList *parameters; GList *parameters;
GIrNodeParam *result; GIrNodeParam *result;
......
...@@ -2080,7 +2080,7 @@ start_vfunc (GMarkupParseContext *context, ...@@ -2080,7 +2080,7 @@ start_vfunc (GMarkupParseContext *context,
ParseContext *ctx, ParseContext *ctx,
GError **error) GError **error)
{ {
if (strcmp (element_name, "vfunc") == 0 && if (strcmp (element_name, "virtual-method") == 0 &&
(ctx->state == STATE_CLASS || (ctx->state == STATE_CLASS ||
ctx->state == STATE_INTERFACE)) ctx->state == STATE_INTERFACE))
{ {
...@@ -2089,12 +2089,14 @@ start_vfunc (GMarkupParseContext *context, ...@@ -2089,12 +2089,14 @@ start_vfunc (GMarkupParseContext *context,
const gchar *override; const gchar *override;
const gchar *is_class_closure; const gchar *is_class_closure;
const gchar *offset; const gchar *offset;
const gchar *invoker;
name = find_attribute ("name", attribute_names, attribute_values); name = find_attribute ("name", attribute_names, attribute_values);
must_chain_up = find_attribute ("must-chain-up", attribute_names, attribute_values); must_chain_up = find_attribute ("must-chain-up", attribute_names, attribute_values);
override = find_attribute ("override", attribute_names, attribute_values); override = find_attribute ("override", attribute_names, attribute_values);
is_class_closure = find_attribute ("is-class-closure", attribute_names, attribute_values); is_class_closure = find_attribute ("is-class-closure", attribute_names, attribute_values);
offset = find_attribute ("offset", attribute_names, attribute_values); offset = find_attribute ("offset", attribute_names, attribute_values);
invoker = find_attribute ("invoker", attribute_names, attribute_values);
if (name == NULL) if (name == NULL)
MISSING_ATTRIBUTE (context, error, element_name, "name"); MISSING_ATTRIBUTE (context, error, element_name, "name");
...@@ -2138,6 +2140,8 @@ start_vfunc (GMarkupParseContext *context, ...@@ -2138,6 +2140,8 @@ start_vfunc (GMarkupParseContext *context,
else else
vfunc->offset = 0; vfunc->offset = 0;
vfunc->invoker = g_strdup (invoker);
iface = (GIrNodeInterface *)CURRENT_NODE (ctx); iface = (GIrNodeInterface *)CURRENT_NODE (ctx);
iface->members = g_list_append (iface->members, vfunc); iface->members = g_list_append (iface->members, vfunc);
......
...@@ -843,9 +843,10 @@ typedef struct { ...@@ -843,9 +843,10 @@ typedef struct {
* @class_closure: Set if this virtual function is the class closure of a signal. * @class_closure: Set if this virtual function is the class closure of a signal.
* @signal: The index of the signal in the list of signals of the object or * @signal: The index of the signal in the list of signals of the object or
* interface to which this virtual function belongs. * interface to which this virtual function belongs.
* @struct_offset: * @struct_offset: The offset of the function pointer in the class struct. The value
* The offset of the function pointer in the class struct. The value
* 0xFFFF indicates that the struct offset is unknown. * 0xFFFF indicates that the struct offset is unknown.
* @invoker: If a method invoker for this virtual exists, this is the offset in the
* class structure of the method. If no method is known, this value will be 0x3ff.
* @signature: * @signature:
* Offset of the SignatureBlob describing the parameter types and the * Offset of the SignatureBlob describing the parameter types and the
* return value type. * return value type.
...@@ -861,7 +862,8 @@ typedef struct { ...@@ -861,7 +862,8 @@ typedef struct {
guint16 signal; guint16 signal;
guint16 struct_offset; guint16 struct_offset;
guint16 reserved2; guint16 invoker : 10; /* Number of bits matches @index in FunctionBlob */
guint16 reserved2 : 6;
guint32 reserved3; guint32 reserved3;
guint32 signature; guint32 signature;
......
...@@ -41,6 +41,7 @@ from .glibast import GLibBoxed ...@@ -41,6 +41,7 @@ from .glibast import GLibBoxed
_COMMENT_HEADER = '*\n ' _COMMENT_HEADER = '*\n '
# Tags - annotations applyed to comment blocks # Tags - annotations applyed to comment blocks
TAG_VFUNC = 'virtual'
TAG_SINCE = 'since' TAG_SINCE = 'since'
TAG_DEPRECATED = 'deprecated' TAG_DEPRECATED = 'deprecated'
TAG_RETURNS = 'returns' TAG_RETURNS = 'returns'
...@@ -298,8 +299,9 @@ class AnnotationApplier(object): ...@@ -298,8 +299,9 @@ class AnnotationApplier(object):
block = self._blocks.get(class_.type_name) block = self._blocks.get(class_.type_name)
self._parse_node_common(class_, block) self._parse_node_common(class_, block)
self._parse_constructors(class_.constructors) self._parse_constructors(class_.constructors)
self._parse_methods(class_.methods) self._parse_methods(class_, class_.methods)
self._parse_methods(class_.static_methods) self._parse_vfuncs(class_, class_.virtual_methods)
self._parse_methods(class_, class_.static_methods)
self._parse_properties(class_, class_.properties) self._parse_properties(class_, class_.properties)
self._parse_signals(class_, class_.signals) self._parse_signals(class_, class_.signals)
self._parse_fields(class_, class_.fields) self._parse_fields(class_, class_.fields)
...@@ -309,7 +311,8 @@ class AnnotationApplier(object): ...@@ -309,7 +311,8 @@ class AnnotationApplier(object):
def _parse_interface(self, interface): def _parse_interface(self, interface):
block = self._blocks.get(interface.type_name) block = self._blocks.get(interface.type_name)
self._parse_node_common(interface, block) 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_properties(interface, interface.properties)
self._parse_signals(interface, interface.signals) self._parse_signals(interface, interface.signals)
self._parse_fields(interface, interface.fields) self._parse_fields(interface, interface.fields)
...@@ -320,7 +323,7 @@ class AnnotationApplier(object): ...@@ -320,7 +323,7 @@ class AnnotationApplier(object):
block = self._blocks.get(record.symbol) block = self._blocks.get(record.symbol)
self._parse_node_common(record, block) self._parse_node_common(record, block)
self._parse_constructors(record.constructors) self._parse_constructors(record.constructors)
self._parse_methods(record.methods) self._parse_methods(record, record.methods)
self._parse_fields(record, record.fields) self._parse_fields(record, record.fields)
if block: if block:
record.doc = block.comment record.doc = block.comment
...@@ -329,7 +332,7 @@ class AnnotationApplier(object): ...@@ -329,7 +332,7 @@ class AnnotationApplier(object):
block = self._blocks.get(boxed.name) block = self._blocks.get(boxed.name)
self._parse_node_common(boxed, block) self._parse_node_common(boxed, block)
self._parse_constructors(boxed.constructors) self._parse_constructors(boxed.constructors)
self._parse_methods(boxed.methods) self._parse_methods(boxed, boxed.methods)
if block: if block:
boxed.doc = block.comment boxed.doc = block.comment
...@@ -338,7 +341,7 @@ class AnnotationApplier(object): ...@@ -338,7 +341,7 @@ class AnnotationApplier(object):
self._parse_node_common(union, block) self._parse_node_common(union, block)
self._parse_fields(union, union.fields) self._parse_fields(union, union.fields)
self._parse_constructors(union.constructors) self._parse_constructors(union.constructors)
self._parse_methods(union.methods) self._parse_methods(union, union.methods)
if block: if block:
union.doc = block.comment union.doc = block.comment
...@@ -370,9 +373,13 @@ class AnnotationApplier(object): ...@@ -370,9 +373,13 @@ class AnnotationApplier(object):
for prop in properties: for prop in properties:
self._parse_property(parent, prop) self._parse_property(parent, prop)
def _parse_methods(self, methods): def _parse_methods(self, parent, methods):
for method in 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): def _parse_signals(self, parent, signals):
for signal in signals: for signal in signals:
...@@ -392,18 +399,20 @@ class AnnotationApplier(object): ...@@ -392,18 +399,20 @@ class AnnotationApplier(object):
if block: if block:
callback.doc = block.comment 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): def _parse_function(self, func):
block = self._blocks.get(func.symbol) block = self._blocks.get(func.symbol)
self._parse_node_common(func, block) self._parse_callable(func, block)
self._parse_params(func, func.parameters, block)
self._parse_return(func, func.retval, block)
if block:
func.doc = block.comment
def _parse_signal(self, parent, signal): def _parse_signal(self, parent, signal):
block = self._blocks.get('%s::%s' % (parent.type_name, signal.name)) block = self._blocks.get('%s::%s' % (parent.type_name, signal.name))
self._parse_node_common(signal, block) self._parse_node_common(signal, block)
self._parse_deprecated(signal, block)
# We're only attempting to name the signal parameters if # We're only attempting to name the signal parameters if
# the number of parameter tags (@foo) is the same or greater # the number of parameter tags (@foo) is the same or greater
# than the number of signal parameters # than the number of signal parameters
...@@ -426,6 +435,26 @@ class AnnotationApplier(object): ...@@ -426,6 +435,26 @@ class AnnotationApplier(object):
if block: if block:
signal.doc = block.comment 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): def _parse_field(self, parent, field):
if isinstance(field, Callback): if isinstance(field, Callback):
self._parse_callback(field) self._parse_callback(field)
......
...@@ -196,15 +196,25 @@ class Include(Node): ...@@ -196,15 +196,25 @@ class Include(Node):
def __str__(self): def __str__(self):
return '%s-%s' % (self.name, self.version) return '%s-%s' % (self.name, self.version)
class Callable(Node):
class Function(Node): def __init__(self, name, retval, parameters, throws):
def __init__(self, name, retval, parameters, symbol, throws=None):
Node.__init__(self, name) Node.__init__(self, name)
self.retval = retval self.retval = retval
self.parameters = parameters self.parameters = parameters
self.symbol = symbol
self.throws = not not throws 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.is_method = False
self.doc = None self.doc = None
...@@ -218,14 +228,18 @@ class Function(Node): ...@@ -218,14 +228,18 @@ class Function(Node):
if parameter.name == name: if parameter.name == name:
return parameter 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): def __init__(self, name, retval, parameters, throws):
pass 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): class Type(Node):
...@@ -411,6 +425,7 @@ class Class(Node): ...@@ -411,6 +425,7 @@ class Class(Node):
self.glib_type_struct = None self.glib_type_struct = None
self.is_abstract = is_abstract self.is_abstract = is_abstract
self.methods = [] self.methods = []
self.virtual_methods = []
self.static_methods = [] self.static_methods = []
self.interfaces = [] self.interfaces = []
self.constructors = [] self.constructors = []
...@@ -430,6 +445,7 @@ class Interface(Node): ...@@ -430,6 +445,7 @@ class Interface(Node):
Node.__init__(self, name) Node.__init__(self, name)
self.parent = parent self.parent = parent
self.methods = [] self.methods = []
self.virtual_methods = []
self.glib_type_struct = None self.glib_type_struct = None
self.properties = [] self.properties = []
self.fields = [] self.fields = []
......
...@@ -158,18 +158,22 @@ and/or use gtk-doc annotations. ''') ...@@ -158,18 +158,22 @@ and/or use gtk-doc annotations. ''')