diff options
-rw-r--r-- | girepository/ginfo.c | 116 | ||||
-rw-r--r-- | girepository/girepository.h | 5 | ||||
-rw-r--r-- | girepository/girnode.c | 37 | ||||
-rw-r--r-- | girepository/girnode.h | 2 | ||||
-rw-r--r-- | girepository/girparser.c | 6 | ||||
-rw-r--r-- | girepository/gtypelib.h | 8 | ||||
-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 | ||||
-rw-r--r-- | tests/everything/everything.c | 25 | ||||
-rw-r--r-- | tests/everything/everything.h | 6 | ||||
-rw-r--r-- | tests/repository/Makefile.am | 4 | ||||
-rw-r--r-- | tests/repository/gitestrepo.c | 48 | ||||
-rw-r--r-- | tests/scanner/foo-1.0-expected.gir | 111 | ||||
-rw-r--r-- | tests/scanner/foo-1.0-expected.tgir | 79 | ||||
-rw-r--r-- | tests/scanner/foo.c | 53 | ||||
-rw-r--r-- | tests/scanner/foo.h | 17 | ||||
-rw-r--r-- | tools/generate.c | 9 |
19 files changed, 655 insertions, 84 deletions
diff --git a/girepository/ginfo.c b/girepository/ginfo.c index fcc5f098..d522a565 100644 --- a/girepository/ginfo.c +++ b/girepository/ginfo.c @@ -1527,6 +1527,63 @@ g_object_info_get_vfunc (GIObjectInfo *info, 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 g_object_info_get_n_constants (GIObjectInfo *info) { @@ -1728,6 +1785,34 @@ g_interface_info_get_vfunc (GIInterfaceInfo *info, 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 g_interface_info_get_n_constants (GIInterfaceInfo *info) { @@ -1913,6 +1998,37 @@ g_vfunc_info_get_signal (GIVFuncInfo *info) 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 */ GITypeInfo * diff --git a/girepository/girepository.h b/girepository/girepository.h index 4059adc0..1058570f 100644 --- a/girepository/girepository.h +++ b/girepository/girepository.h @@ -470,6 +470,8 @@ GISignalInfo * g_object_info_get_signal (GIObjectInfo *in gint g_object_info_get_n_vfuncs (GIObjectInfo *info); GIVFuncInfo * g_object_info_get_vfunc (GIObjectInfo *info, gint n); +GIVFuncInfo * g_object_info_find_vfunc (GIObjectInfo *info, + const gchar *name); gint g_object_info_get_n_constants (GIObjectInfo *info); GIConstantInfo * g_object_info_get_constant (GIObjectInfo *info, gint n); @@ -495,6 +497,8 @@ GISignalInfo * g_interface_info_get_signal (GIInterfaceInfo *in gint g_interface_info_get_n_vfuncs (GIInterfaceInfo *info); GIVFuncInfo * g_interface_info_get_vfunc (GIInterfaceInfo *info, gint n); +GIVFuncInfo * g_interface_info_find_vfunc (GIInterfaceInfo *info, + const gchar *name); gint g_interface_info_get_n_constants (GIInterfaceInfo *info); GIConstantInfo * g_interface_info_get_constant (GIInterfaceInfo *info, gint n); @@ -527,6 +531,7 @@ typedef enum GIVFuncInfoFlags g_vfunc_info_get_flags (GIVFuncInfo *info); gint g_vfunc_info_get_offset (GIVFuncInfo *info); GISignalInfo * g_vfunc_info_get_signal (GIVFuncInfo *info); +GIFunctionInfo * g_vfunc_info_get_invoker (GIVFuncInfo *info); /* GIConstantInfo */ diff --git a/girepository/girnode.c b/girepository/girnode.c index 22c0aee0..01e83ace 100644 --- a/girepository/girnode.c +++ b/girepository/girnode.c @@ -265,6 +265,7 @@ g_ir_node_free (GIrNode *node) GIrNodeVFunc *vfunc = (GIrNodeVFunc *)node; g_free (node->name); + g_free (vfunc->invoker); for (l = vfunc->parameters; l; l = l->next) g_ir_node_free ((GIrNode *)l->data); g_list_free (vfunc->parameters); @@ -1186,6 +1187,30 @@ g_ir_find_node (GIrModule *module, 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 serialize_type (GIrModule *module, GList *modules, @@ -1759,6 +1784,18 @@ g_ir_node_build_typelib (GIrNode *node, blob->class_closure = 0; /* FIXME */ 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->reserved2 = 0; blob->signature = signature; diff --git a/girepository/girnode.h b/girepository/girnode.h index 2a1f6b23..238593ae 100644 --- a/girepository/girnode.h +++ b/girepository/girnode.h @@ -214,6 +214,8 @@ struct _GIrNodeVFunc gboolean must_not_be_implemented; gboolean is_class_closure; + char *invoker; + GList *parameters; GIrNodeParam *result; diff --git a/girepository/girparser.c b/girepository/girparser.c index 006ed3b7..9afd8585 100644 --- a/girepository/girparser.c +++ b/girepository/girparser.c @@ -2080,7 +2080,7 @@ start_vfunc (GMarkupParseContext *context, ParseContext *ctx, GError **error) { - if (strcmp (element_name, "vfunc") == 0 && + if (strcmp (element_name, "virtual-method") == 0 && (ctx->state == STATE_CLASS || ctx->state == STATE_INTERFACE)) { @@ -2089,12 +2089,14 @@ start_vfunc (GMarkupParseContext *context, const gchar *override; const gchar *is_class_closure; const gchar *offset; + const gchar *invoker; name = find_attribute ("name", attribute_names, attribute_values); must_chain_up = find_attribute ("must-chain-up", attribute_names, attribute_values); override = find_attribute ("override", attribute_names, attribute_values); is_class_closure = find_attribute ("is-class-closure", attribute_names, attribute_values); offset = find_attribute ("offset", attribute_names, attribute_values); + invoker = find_attribute ("invoker", attribute_names, attribute_values); if (name == NULL) MISSING_ATTRIBUTE (context, error, element_name, "name"); @@ -2138,6 +2140,8 @@ start_vfunc (GMarkupParseContext *context, else vfunc->offset = 0; + vfunc->invoker = g_strdup (invoker); + iface = (GIrNodeInterface *)CURRENT_NODE (ctx); iface->members = g_list_append (iface->members, vfunc); diff --git a/girepository/gtypelib.h b/girepository/gtypelib.h index db5fe117..f6ad8c97 100644 --- a/girepository/gtypelib.h +++ b/girepository/gtypelib.h @@ -843,9 +843,10 @@ typedef struct { * @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 * interface to which this virtual function belongs. - * @struct_offset: - * The offset of the function pointer in the class struct. The value + * @struct_offset: The offset of the function pointer in the class struct. The value * 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: * Offset of the SignatureBlob describing the parameter types and the * return value type. @@ -861,7 +862,8 @@ typedef struct { guint16 signal; guint16 struct_offset; - guint16 reserved2; + guint16 invoker : 10; /* Number of bits matches @index in FunctionBlob */ + guint16 reserved2 : 6; guint32 reserved3; guint32 signature; 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) diff --git a/tests/everything/everything.c b/tests/everything/everything.c index 75842bf3..c597f858 100644 --- a/tests/everything/everything.c +++ b/tests/everything/everything.c @@ -789,6 +789,12 @@ test_obj_dispose (GObject *gobject) G_OBJECT_CLASS (test_obj_parent_class)->dispose (gobject); } +static int +test_obj_default_matrix (TestObj *obj, const char *somestr) +{ + return 42; +} + static void test_obj_class_init (TestObjClass *klass) { @@ -819,6 +825,8 @@ test_obj_class_init (TestObjClass *klass) g_object_class_install_property (gobject_class, PROP_TEST_OBJ_BARE, pspec); + + klass->matrix = test_obj_default_matrix; } static void @@ -854,6 +862,23 @@ test_obj_static_method (int x) } /** + * test_obj_do_matrix: + * @obj: A #TestObj + * @somestr: Meaningless string + * + * This method is virtual. Notably its name differs from the virtual + * slot name, which makes it useful for testing bindings handle this + * case. + * + * Virtual: matrix + */ +int +test_obj_do_matrix (TestObj *obj, const char *somestr) +{ + return TEST_OBJ_GET_CLASS (obj)->matrix (obj, somestr); +} + +/** * test_callback: * @callback: (scope call): * diff --git a/tests/everything/everything.h b/tests/everything/everything.h index dbb7995e..2474566d 100644 --- a/tests/everything/everything.h +++ b/tests/everything/everything.h @@ -178,6 +178,7 @@ gboolean test_boxed_equals (TestBoxed *boxed, #define TEST_TYPE_OBJ (test_obj_get_type ()) #define TEST_OBJECT(object) (G_TYPE_CHECK_INSTANCE_CAST ((object), TEST_TYPE_OBJ, TestObj)) #define TEST_IS_OBJECT(object) (G_TYPE_CHECK_INSTANCE_TYPE ((object), TEST_TYPE_OBJ)) +#define TEST_OBJ_GET_CLASS(obj) (G_TYPE_INSTANCE_GET_CLASS ((obj), TEST_TYPE_OBJ, TestObjClass)) typedef struct _TestObj TestObj; typedef struct _TestObjClass TestObjClass; @@ -197,6 +198,8 @@ struct _TestObjClass { GObjectClass parent_class; + int (*matrix) (TestObj *obj, const char *somestr); + guint test_signal; }; @@ -205,6 +208,9 @@ TestObj* test_obj_new_from_file (const char *x, GError **error); void test_obj_set_bare (TestObj *obj, GObject *bare); double test_obj_static_method (int x); +/* virtual */ +int test_obj_do_matrix (TestObj *obj, const char *somestr); + /* callback */ typedef int (*TestCallback) (); typedef int (*TestCallbackUserData) (gpointer user_data); diff --git a/tests/repository/Makefile.am b/tests/repository/Makefile.am index e21cff43..36f738d7 100644 --- a/tests/repository/Makefile.am +++ b/tests/repository/Makefile.am @@ -13,5 +13,5 @@ gitestthrows_CPPFLAGS = $(GIREPO_CFLAGS) -I$(top_srcdir)/girepository gitestthrows_LDADD = $(top_builddir)/girepository/libgirepository-1.0.la $(GIREPO_LIBS) TESTS = gitestrepo gitestthrows -TESTS_ENVIRONMENT=env top_builddir="$(top_builddir)" $(DEBUG) \ - XDG_DATA_DIRS="$(top_srcdir)/gir:$(XDG_DATA_DIRS)" +TESTS_ENVIRONMENT=env top_builddir="$(top_builddir)" \ + XDG_DATA_DIRS="$(top_srcdir)/gir:$(XDG_DATA_DIRS)" $(DEBUG)
\ No newline at end of file diff --git a/tests/repository/gitestrepo.c b/tests/repository/gitestrepo.c index 0e674670..a65116bc 100644 --- a/tests/repository/gitestrepo.c +++ b/tests/repository/gitestrepo.c @@ -73,5 +73,53 @@ main(int argc, char **argv) info = g_irepository_find_by_name (repo, "Gio", "ThisDoesNotExist"); g_assert (info == NULL); + /* vfunc tests */ + { + GIVFuncInfo *vfunc; + GIFunctionInfo *invoker; + + /* Test vfunc with invoker on interface */ + info = g_irepository_find_by_name (repo, "Gio", "File"); + g_assert (info != NULL); + + vfunc = g_interface_info_find_vfunc ((GIInterfaceInfo*)info, "read_async"); + g_assert (vfunc != NULL); + + invoker = g_vfunc_info_get_invoker (vfunc); + g_assert (invoker != NULL); + + g_assert (strcmp (g_base_info_get_name ((GIBaseInfo*)invoker), "read_async") == 0); + + /* Test vfunc with no known invoker on object */ + info = g_irepository_find_by_name (repo, "GObject", "Object"); + g_assert (info != NULL); + + vfunc = g_object_info_find_vfunc ((GIObjectInfo*)info, "dispose"); + g_assert (vfunc != NULL); + + /* Ok, maybe we should mark g_object_run_dispose as the invoker for + * dispose, but...eh. It's pretty special however you cut it. + */ + invoker = g_vfunc_info_get_invoker (vfunc); + g_assert (invoker == NULL); + + /* Test vfunc with invoker on object */ + info = g_irepository_find_by_name (repo, "Gio", "AppLaunchContext"); + g_assert (info != NULL); + + vfunc = g_object_info_find_vfunc ((GIObjectInfo*)info, "get_display"); + g_assert (vfunc != NULL); + + invoker = g_vfunc_info_get_invoker (vfunc); + g_assert (invoker != NULL); + g_assert (strcmp (g_base_info_get_name ((GIBaseInfo*)invoker), "get_display") == 0); + + /* And let's be sure we can find the method directly */ + + invoker = g_object_info_find_method ((GIObjectInfo*)info, "get_display"); + g_assert (invoker != NULL); + g_assert (strcmp (g_base_info_get_name ((GIBaseInfo*)invoker), "get_display") == 0); + } + exit(0); } diff --git a/tests/scanner/foo-1.0-expected.gir b/tests/scanner/foo-1.0-expected.gir index 0f6b1f1f..086170e5 100644 --- a/tests/scanner/foo-1.0-expected.gir +++ b/tests/scanner/foo-1.0-expected.gir @@ -232,16 +232,26 @@ and/or use gtk-doc annotations. --> glib:type-name="FooInterface" glib:get-type="foo_interface_get_type" glib:type-struct="InterfaceIface"> - <callback name="do_foo" c:type="do_foo"> + <virtual-method name="do_foo" invoker="do_foo"> <return-value transfer-ownership="none"> <type name="none" c:type="void"/> </return-value> <parameters> - <parameter name="self" transfer-ownership="none"> - <type name="Interface" c:type="FooInterface*"/> + <parameter name="x" transfer-ownership="none"> + <type name="int" c:type="int"/> </parameter> </parameters> - </callback> + </virtual-method> + <method name="do_foo" c:identifier="foo_interface_do_foo"> + <return-value transfer-ownership="none"> + <type name="none" c:type="void"/> + </return-value> + <parameters> + <parameter name="x" transfer-ownership="none"> + <type name="int" c:type="int"/> + </parameter> + </parameters> + </method> </interface> <record name="InterfaceIface" c:type="FooInterfaceIface" @@ -257,6 +267,9 @@ and/or use gtk-doc annotations. --> <parameter name="self" transfer-ownership="none"> <type name="Interface" c:type="FooInterface*"/> </parameter> + <parameter name="x" transfer-ownership="none"> + <type name="int" c:type="int"/> + </parameter> </parameters> </callback> </record> @@ -277,6 +290,29 @@ and/or use gtk-doc annotations. --> <type name="int" c:type="int"/> </return-value> </function> + <virtual-method name="virtual_method" invoker="virtual_method"> + <return-value transfer-ownership="none"> + <type name="boolean" c:type="gboolean"/> + </return-value> + <parameters> + <parameter name="first_param" transfer-ownership="none"> + <type name="int" c:type="int"/> + </parameter> + </parameters> + </virtual-method> + <virtual-method name="read_fn" invoker="read"> + <return-value transfer-ownership="none"> + <type name="none" c:type="void"/> + </return-value> + <parameters> + <parameter name="offset" transfer-ownership="none"> + <type name="int" c:type="int"/> + </parameter> + <parameter name="length" transfer-ownership="none"> + <type name="int" c:type="int"/> + </parameter> + </parameters> + </virtual-method> <method name="external_type" c:identifier="foo_object_external_type"> <return-value transfer-ownership="full"> <type name="utility.Object" c:type="UtilityObject*"/> @@ -359,6 +395,31 @@ and/or use gtk-doc annotations. --> </parameter> </parameters> </method> + <method name="virtual_method" c:identifier="foo_object_virtual_method"> + <return-value transfer-ownership="none"> + <type name="boolean" c:type="gboolean"/> + </return-value> + <parameters> + <parameter name="first_param" transfer-ownership="none"> + <type name="int" c:type="int"/> + </parameter> + </parameters> + </method> + <method name="read" + c:identifier="foo_object_read" + doc="Read some stuff."> + <return-value transfer-ownership="none"> + <type name="none" c:type="void"/> + </return-value> + <parameters> + <parameter name="offset" transfer-ownership="none"> + <type name="int" c:type="int"/> + </parameter> + <parameter name="length" transfer-ownership="none"> + <type name="int" c:type="int"/> + </parameter> + </parameters> + </method> <property name="string" writable="1" construct="1"> <type name="utf8" c:type="gchararray"/> </property> @@ -401,6 +462,22 @@ and/or use gtk-doc annotations. --> </parameter> </parameters> </callback> + <callback name="read_fn" c:type="read_fn"> + <return-value transfer-ownership="none"> + <type name="none" c:type="void"/> + </return-value> + <parameters> + <parameter name="object" transfer-ownership="none"> + <type name="Object" c:type="FooObject*"/> + </parameter> + <parameter name="offset" transfer-ownership="none"> + <type name="int" c:type="int"/> + </parameter> + <parameter name="length" transfer-ownership="none"> + <type name="int" c:type="int"/> + </parameter> + </parameters> + </callback> </record> <constant name="PIE_IS_TASTY" value="3.14159"> <type name="double"/> @@ -467,21 +544,21 @@ and/or use gtk-doc annotations. --> glib:get-type="foo_sub_interface_get_type" glib:type-struct="SubInterfaceIface"> <prerequisite name="Interface"/> + <virtual-method name="do_bar" invoker="do_bar"> + <return-value transfer-ownership="none"> + <type name="none" c:type="void"/> + </return-value> + </virtual-method> <method name="do_bar" c:identifier="foo_sub_interface_do_bar"> <return-value transfer-ownership="none"> <type name="none" c:type="void"/> </return-value> </method> - <callback name="do_bar" c:type="do_bar"> - <return-value transfer-ownership="none"> + <glib:signal name="destroy-event"> + <return-value transfer-ownership="full"> <type name="none" c:type="void"/> </return-value> - <parameters> - <parameter name="self" transfer-ownership="none"> - <type name="SubInterface" c:type="FooSubInterface*"/> - </parameter> - </parameters> - </callback> + </glib:signal> </interface> <record name="SubInterfaceIface" c:type="FooSubInterfaceIface" @@ -489,6 +566,16 @@ and/or use gtk-doc annotations. --> <field name="parent_iface"> <type name="GObject.TypeInterface" c:type="GTypeInterface"/> </field> + <callback name="destroy_event" c:type="destroy_event"> + <return-value transfer-ownership="none"> + <type name="none" c:type="void"/> + </return-value> + <parameters> + <parameter name="self" transfer-ownership="none"> + <type name="SubInterface" c:type="FooSubInterface*"/> + </parameter> + </parameters> + </callback> <callback name="do_bar" c:type="do_bar"> <return-value transfer-ownership="none"> <type name="none" c:type="void"/> diff --git a/tests/scanner/foo-1.0-expected.tgir b/tests/scanner/foo-1.0-expected.tgir index f8e1ee59..1d82b6a3 100644 --- a/tests/scanner/foo-1.0-expected.tgir +++ b/tests/scanner/foo-1.0-expected.tgir @@ -161,7 +161,28 @@ <member name="second" value="2"/> <member name="third" value="4"/> </bitfield> - <interface name="Interface" glib:type-name="FooInterface" glib:get-type="foo_interface_get_type" glib:type-struct="InterfaceIface"/> + <interface name="Interface" glib:type-name="FooInterface" glib:get-type="foo_interface_get_type" glib:type-struct="InterfaceIface"> + <method name="do_foo" c:identifier="foo_interface_do_foo"> + <return-value transfer-ownership="none"> + <type name="none"/> + </return-value> + <parameters> + <parameter name="x" transfer-ownership="none"> + <type name="int"/> + </parameter> + </parameters> + </method> + <virtual-method name="do_foo" offset="0" invoker="do_foo"> + <return-value transfer-ownership="none"> + <type name="none"/> + </return-value> + <parameters> + <parameter name="x" transfer-ownership="none"> + <type name="int"/> + </parameter> + </parameters> + </virtual-method> + </interface> <record name="InterfaceIface" glib:is-gtype-struct="1"> <field name="parent_iface"> <type name="GObject.TypeInterface"/> @@ -255,6 +276,29 @@ </parameter> </parameters> </method> + <method name="virtual_method" c:identifier="foo_object_virtual_method"> + <return-value transfer-ownership="none"> + <type name="boolean"/> + </return-value> + <parameters> + <parameter name="first_param" transfer-ownership="none"> + <type name="int"/> + </parameter> + </parameters> + </method> + <method name="read" c:identifier="foo_object_read"> + <return-value transfer-ownership="none"> + <type name="none"/> + </return-value> + <parameters> + <parameter name="offset" transfer-ownership="none"> + <type name="int"/> + </parameter> + <parameter name="length" transfer-ownership="none"> + <type name="int"/> + </parameter> + </parameters> + </method> <property name="string" writable="1" construct="1"> <type name="utf8"/> </property> @@ -271,6 +315,29 @@ </parameter> </parameters> </glib:signal> + <virtual-method name="virtual_method" offset="0" invoker="virtual_method"> + <return-value transfer-ownership="none"> + <type name="boolean"/> + </return-value> + <parameters> + <parameter name="first_param" transfer-ownership="none"> + <type name="int"/> + </parameter> + </parameters> + </virtual-method> + <virtual-method name="read_fn" offset="0" invoker="read"> + <return-value transfer-ownership="none"> + <type name="none"/> + </return-value> + <parameters> + <parameter name="offset" transfer-ownership="none"> + <type name="int"/> + </parameter> + <parameter name="length" transfer-ownership="none"> + <type name="int"/> + </parameter> + </parameters> + </virtual-method> </class> <record name="ObjectClass" glib:is-gtype-struct="1"> <field name="parent_class"> @@ -342,6 +409,16 @@ <type name="none"/> </return-value> </method> + <glib:signal name="destroy-event" when="LAST"> + <return-value transfer-ownership="full"> + <type name="none"/> + </return-value> + </glib:signal> + <virtual-method name="do_bar" offset="0" invoker="do_bar"> + <return-value transfer-ownership="none"> + <type name="none"/> + </return-value> + </virtual-method> </interface> <record name="SubInterfaceIface" glib:is-gtype-struct="1"> <field name="parent_iface"> diff --git a/tests/scanner/foo.c b/tests/scanner/foo.c index 04882603..8a9283d9 100644 --- a/tests/scanner/foo.c +++ b/tests/scanner/foo.c @@ -1,4 +1,5 @@ #include "foo.h" +#include "girepository.h" /* A hidden type not exposed publicly, similar to GUPNP's XML wrapper object */ @@ -8,8 +9,8 @@ int foo_init_argv (int argc, char **argv); int foo_init_argv_address (int *argc, char ***argv); void foo_private_function (FooObject *foo); void foo_test_unsigned (unsigned int uint); -void foo_interface_do_foo (FooInterface *self); -void foo_do_foo (FooInterface *self); +void foo_interface_do_foo (FooInterface *self, int x); +void foo_do_foo (FooInterface *self, int x); int foo_enum_method (FooEnumType foo_enum); FooHidden * foo_hidden_copy (const FooHidden *boxed); void foo_hidden_free (FooHidden *boxed); @@ -47,9 +48,9 @@ foo_interface_get_type (void) return object_type; } -void foo_interface_do_foo (FooInterface *self) +void foo_interface_do_foo (FooInterface *self, int x) { - FOO_INTERFACE_GET_INTERFACE(self)->do_foo (self); + FOO_INTERFACE_GET_INTERFACE(self)->do_foo (self, x); } enum { @@ -73,6 +74,16 @@ foo_foo_interface_init (gpointer g_iface, iface->do_foo = foo_do_foo; } +enum { + SUBIFACE_DESTROY_EVENT, + SUBIFACE_LAST_SIGNAL +}; + +static void +foo_sub_interface_class_init (gpointer g_class, gpointer class_data); + +static guint foo_subiface_signals[SUBIFACE_LAST_SIGNAL] = { 0 }; + GType foo_sub_interface_get_type (void) { @@ -83,7 +94,7 @@ foo_sub_interface_get_type (void) object_type = g_type_register_static_simple (G_TYPE_INTERFACE, "FooSubInterface", sizeof (FooSubInterfaceIface), - NULL, 0, NULL, 0); + foo_sub_interface_class_init, 0, NULL, 0); g_type_interface_add_prerequisite (object_type, FOO_TYPE_INTERFACE); } @@ -91,6 +102,20 @@ foo_sub_interface_get_type (void) return object_type; } +static void +foo_sub_interface_class_init (gpointer g_class, gpointer class_data) +{ + foo_subiface_signals[SUBIFACE_DESTROY_EVENT] = + g_signal_new ("destroy-event", FOO_TYPE_SUBINTERFACE, + G_SIGNAL_RUN_LAST, + G_STRUCT_OFFSET (FooSubInterfaceIface, destroy_event), + NULL, NULL, + gi_cclosure_marshal_generic, + G_TYPE_NONE, + 0, + G_TYPE_NONE); +} + void foo_sub_interface_do_bar (FooSubInterface *self) { FOO_SUBINTERFACE_GET_INTERFACE(self)->do_bar (self); @@ -191,7 +216,7 @@ foo_object_take_all (FooObject *object, int x, ...) } void -foo_do_foo (FooInterface *self) +foo_do_foo (FooInterface *self, int x) { @@ -214,6 +239,22 @@ foo_object_dup_name (FooObject *object) return g_strdup ("foo"); } +/** + * foo_object_read: + * @object: obj + * @offset: offset + * @length: length + * + * Read some stuff. + * + * Virtual: read_fn + */ +void +foo_object_read (FooObject *object, int offset, int length) +{ + +} + G_DEFINE_ABSTRACT_TYPE (FooSubobject, foo_subobject, FOO_TYPE_OBJECT); static void diff --git a/tests/scanner/foo.h b/tests/scanner/foo.h index da30df38..02f3cfec 100644 --- a/tests/scanner/foo.h +++ b/tests/scanner/foo.h @@ -48,15 +48,23 @@ struct _FooInterfaceIface { GTypeInterface parent_iface; - void (*do_foo) (FooInterface *self); + void (*do_foo) (FooInterface *self, int x); }; GType foo_interface_get_type (void) G_GNUC_CONST; +void foo_interface_do_foo (FooInterface *iface, int x); + struct _FooSubInterfaceIface { GTypeInterface parent_iface; + /* signals */ + + void (*destroy_event) (FooSubInterface *self); + + /* virtual table */ + void (*do_bar) (FooSubInterface *self); }; @@ -76,6 +84,9 @@ struct _FooObjectClass GObjectClass parent_class; gboolean (* virtual_method) (FooObject *object, int first_param); + + /* Intended to match GFile */ + void (*read_fn) (FooObject *object, int offset, int length); }; gint foo_init (void); @@ -105,6 +116,10 @@ char * foo_object_dup_name (FooObject *object); void foo_object_handle_glyph (FooObject *object, UtilityGlyph glyph); +gboolean foo_object_virtual_method (FooObject *object, int first_param); + +void foo_object_read (FooObject *object, int offset, int length); + int foo_object_static_meth (); struct _FooSubobject diff --git a/tools/generate.c b/tools/generate.c index c4d72912..48d7dfb6 100644 --- a/tools/generate.c +++ b/tools/generate.c @@ -869,6 +869,7 @@ write_vfunc_info (const gchar *namespace, { GIVFuncInfoFlags flags; const gchar *name; + GIFunctionInfo *invoker; gboolean deprecated; gint offset; @@ -876,8 +877,9 @@ write_vfunc_info (const gchar *namespace, flags = g_vfunc_info_get_flags (info); deprecated = g_base_info_is_deprecated ((GIBaseInfo *)info); offset = g_vfunc_info_get_offset (info); + invoker = g_vfunc_info_get_invoker (info); - xml_start_element (file, "vfunc"); + xml_start_element (file, "virtual-method"); xml_printf (file, " name=\"%s\"", name); if (deprecated) @@ -893,9 +895,12 @@ write_vfunc_info (const gchar *namespace, xml_printf (file, " offset=\"%d\"", offset); + if (invoker) + xml_printf (file, " invoker=\"%s\"", g_base_info_get_name ((GIBaseInfo*)invoker)); + write_callable_info (namespace, (GICallableInfo*)info, file); - xml_end_element (file, "vfunc"); + xml_end_element (file, "virtual-method"); } static void |