[gjs/wip/gobj-kitchen-sink: 7/19] object: Allow GObject subclasses to override parent vfuncs



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]