[gjs/wip/gobj-kitchen-sink: 7/19] object: Allow GObject subclasses to override parent vfuncs
- From: Jasper St. Pierre <jstpierre src gnome org>
- To: commits-list gnome org
- Cc:
- Subject: [gjs/wip/gobj-kitchen-sink: 7/19] object: Allow GObject subclasses to override parent vfuncs
- Date: Fri, 3 Feb 2012 03:29:05 +0000 (UTC)
commit 9e5b254b0eb249eacf60ac5c56b2849ff4909b7b
Author: Jasper St. Pierre <jstpierre mecheye net>
Date: Sun Nov 6 01:15:52 2011 -0400
object: Allow GObject subclasses to override parent vfuncs
Allow GObject subclasses to override parent vfuncs. We automatically hook up
such vfuncs if the name of the function is prefixed with "vfunc_".
Some of the code here related to finding the vfunc's implementor struct was
stolen from pygobject. Maybe it should go in libgirepository instead.
https://bugzilla.gnome.org/show_bug.cgi?id=663492
gi/object.c | 144 ++++++++++++++++++++++++++++++++++++++++++
modules/overrides/GObject.js | 7 ++
test/js/testGIMarshalling.js | 48 ++++++++++++++
3 files changed, 199 insertions(+), 0 deletions(-)
---
diff --git a/gi/object.c b/gi/object.c
index ef77fda..8892dd1 100644
--- a/gi/object.c
+++ b/gi/object.c
@@ -1695,6 +1695,144 @@ gjs_g_object_from_object(JSContext *context,
return priv->gobj;
}
+static void
+find_vfunc_info (JSContext *context,
+ GType implementor_gtype,
+ GIBaseInfo *vfunc_info,
+ gchar *vfunc_name,
+ gpointer *implementor_vtable_ret,
+ GIFieldInfo **field_info_ret)
+{
+ GType ancestor_gtype;
+ int length, i;
+ GIBaseInfo *ancestor_info;
+ GIStructInfo *struct_info;
+ gpointer implementor_class;
+ gboolean is_interface;
+
+ *field_info_ret = NULL;
+ *implementor_vtable_ret = NULL;
+
+ ancestor_info = g_base_info_get_container(vfunc_info);
+ ancestor_gtype = g_registered_type_info_get_g_type((GIRegisteredTypeInfo*)ancestor_info);
+
+ is_interface = g_base_info_get_type(ancestor_info) == GI_INFO_TYPE_INTERFACE;
+
+ implementor_class = g_type_class_ref(implementor_gtype);
+ if (is_interface) {
+ GTypeInstance *implementor_iface_class;
+ implementor_iface_class = g_type_interface_peek(implementor_class,
+ ancestor_gtype);
+ if (implementor_iface_class == NULL) {
+ g_type_class_unref(implementor_class);
+ gjs_throw (context, "Couldn't find GType of implementor of interface %s.",
+ g_type_name(ancestor_gtype));
+ return;
+ }
+
+ *implementor_vtable_ret = implementor_iface_class;
+
+ struct_info = g_interface_info_get_iface_struct((GIInterfaceInfo*)ancestor_info);
+ } else {
+ struct_info = g_object_info_get_class_struct((GIObjectInfo*)ancestor_info);
+ *implementor_vtable_ret = implementor_class;
+ }
+
+ g_type_class_unref(implementor_class);
+
+ length = g_struct_info_get_n_fields(struct_info);
+ for (i = 0; i < length; i++) {
+ GIFieldInfo *field_info;
+ GITypeInfo *type_info;
+
+ field_info = g_struct_info_get_field(struct_info, i);
+
+ if (strcmp(g_base_info_get_name((GIBaseInfo*)field_info), vfunc_name) != 0) {
+ g_base_info_unref(field_info);
+ continue;
+ }
+
+ type_info = g_field_info_get_type(field_info);
+ if (g_type_info_get_tag(type_info) != GI_TYPE_TAG_INTERFACE) {
+ /* We have a field with the same name, but it's not a callback.
+ * There's no hope of being another field with a correct name,
+ * so just abort early. */
+ g_base_info_unref(type_info);
+ g_base_info_unref(field_info);
+ break;
+ } else {
+ g_base_info_unref(type_info);
+ *field_info_ret = field_info;
+ break;
+ }
+ }
+
+ g_base_info_unref(struct_info);
+}
+
+static JSBool
+gjs_hook_up_vfunc(JSContext *cx,
+ uintN argc,
+ jsval *vp)
+{
+ jsval *argv = JS_ARGV(cx, vp);
+ gchar *name;
+ JSObject *object;
+ JSObject *function;
+ ObjectInstance *priv;
+ GType gtype;
+ GIObjectInfo *info;
+ GIVFuncInfo *vfunc;
+ gpointer implementor_vtable;
+ GIFieldInfo *field_info;
+
+ if (!gjs_parse_args(cx, "hook_up_vfunc",
+ "oso", argc, argv,
+ "object", &object,
+ "name", &name,
+ "function", &function))
+ return JS_FALSE;
+
+ priv = priv_from_js(cx, object);
+ gtype = priv->gtype;
+ info = priv->info;
+
+ JS_SET_RVAL(cx, vp, JSVAL_VOID);
+
+ vfunc = find_vfunc_on_parent(info, name);
+
+ find_vfunc_info(cx, gtype, vfunc, name, &implementor_vtable, &field_info);
+ if (field_info != NULL) {
+ GITypeInfo *type_info;
+ GIBaseInfo *interface_info;
+ GICallbackInfo *callback_info;
+ gint offset;
+ gpointer method_ptr;
+ GjsCallbackTrampoline *trampoline;
+
+ type_info = g_field_info_get_type(field_info);
+
+ interface_info = g_type_info_get_interface(type_info);
+
+ callback_info = (GICallbackInfo*)interface_info;
+ offset = g_field_info_get_offset(field_info);
+ method_ptr = G_STRUCT_MEMBER_P(implementor_vtable, offset);
+
+ trampoline = gjs_callback_trampoline_new(cx, OBJECT_TO_JSVAL(function), callback_info,
+ GI_SCOPE_TYPE_NOTIFIED, TRUE);
+
+ *((ffi_closure **)method_ptr) = trampoline->closure;
+
+ g_base_info_unref(interface_info);
+ g_base_info_unref(type_info);
+ g_base_info_unref(field_info);
+ }
+
+ g_base_info_unref(vfunc);
+ g_free(name);
+ return JS_TRUE;
+}
+
static JSBool
gjs_register_type(JSContext *cx,
uintN argc,
@@ -1771,6 +1909,12 @@ gjs_define_stuff(JSContext *context,
3, GJS_MODULE_PROP_FLAGS))
return JS_FALSE;
+ if (!JS_DefineFunction(context, module_obj,
+ "hook_up_vfunc",
+ (JSNative)gjs_hook_up_vfunc,
+ 3, GJS_MODULE_PROP_FLAGS))
+ return JS_FALSE;
+
return JS_TRUE;
}
diff --git a/modules/overrides/GObject.js b/modules/overrides/GObject.js
index 2f692db..68e6682 100644
--- a/modules/overrides/GObject.js
+++ b/modules/overrides/GObject.js
@@ -38,6 +38,13 @@ const GObjectMeta = new Lang.Class({
this.parent(params);
Gi.register_type(params.Extends.prototype, this.prototype, params.Name);
+
+ for (let prop in params) {
+ let value = this.prototype[prop];
+ if (typeof value === 'function' && prop.slice(0, 6) == 'vfunc_') {
+ Gi.hook_up_vfunc(this.prototype, prop.slice(6), value);
+ }
+ }
},
_isValidClass: function(klass) {
diff --git a/test/js/testGIMarshalling.js b/test/js/testGIMarshalling.js
index 57dcbfc..02cdc89 100644
--- a/test/js/testGIMarshalling.js
+++ b/test/js/testGIMarshalling.js
@@ -230,4 +230,52 @@ function testGType() {
assertEquals(GObject.TYPE_INT, GIMarshallingTests.gtype_inout(GObject.TYPE_NONE));
}
+const VFuncTester = new Lang.Class({
+ Name: 'VFuncTester',
+ Extends: GIMarshallingTests.Object,
+
+ vfunc_vfunc_return_value_only: function(obj) {
+ return 42;
+ },
+
+ vfunc_vfunc_one_out_parameter: function(obj) {
+ return 43;
+ },
+
+ vfunc_vfunc_multiple_out_parameters: function(obj) {
+ return [44, 45];
+ },
+
+ vfunc_vfunc_return_value_and_one_out_parameter: function(obj) {
+ return [46, 47];
+ },
+
+ vfunc_vfunc_return_value_and_multiple_out_parameters: function(obj) {
+ return [48, 49, 50];
+ }
+});
+
+function testVFuncs() {
+ let tester = new VFuncTester();
+ let a, b, c;
+ a = tester.vfunc_return_value_only();
+ assertEquals(42, a);
+
+ a = tester.vfunc_one_out_parameter();
+ assertEquals(43, a);
+
+ [a, b] = tester.vfunc_multiple_out_parameters();
+ assertEquals(44, a);
+ assertEquals(45, b);
+
+ [a, b] = tester.vfunc_return_value_and_one_out_parameter();
+ assertEquals(46, a);
+ assertEquals(47, b);
+
+ [a, b, c] = tester.vfunc_return_value_and_multiple_out_parameters();
+ assertEquals(48, a);
+ assertEquals(49, b);
+ assertEquals(50, c);
+}
+
gjstestRun();
[
Date Prev][
Date Next] [
Thread Prev][
Thread Next]
[
Thread Index]
[
Date Index]
[
Author Index]