[gjs] object: Implement gobject inheritance



commit 4a2f7c82eb702d0e21d30643461239a480b878e4
Author: Jasper St. Pierre <jstpierre mecheye net>
Date:   Sun Nov 6 01:15:12 2011 -0400

    object: Implement gobject inheritance
    
    https://bugzilla.gnome.org/show_bug.cgi?id=663492

 gi/object.c                  |  132 ++++++++++++++++++++++++++++++++++++++----
 modules/lang.js              |    2 +
 modules/overrides/GObject.js |   42 +++++++++++++
 3 files changed, 164 insertions(+), 12 deletions(-)
---
diff --git a/gi/object.c b/gi/object.c
index d19be58..3966421 100644
--- a/gi/object.c
+++ b/gi/object.c
@@ -138,7 +138,10 @@ object_instance_get_prop(JSContext *context,
                      "Get prop '%s' hook obj %p priv %p", name, obj, priv);
 
     if (priv == NULL) {
-        ret = JS_FALSE; /* wrong class passed in */
+        /* We won't have a private until the initializer is called, so
+         * don't mark a call to _init() an error. */
+        if (!g_str_equal(name, "_init"))
+            ret = JS_FALSE;
         goto out;
     }
     if (priv->gobj == NULL) /* prototype, not an instance. */
@@ -691,6 +694,7 @@ init_object_private (JSContext *context,
         goto out;
     }
 
+    priv->gtype = proto_priv->gtype;
     priv->info = proto_priv->info;
     g_base_info_ref( (GIBaseInfo*) priv->info);
 
@@ -740,9 +744,12 @@ manage_js_gobject (JSContext      *context,
     g_object_unref(priv->gobj);
 }
 
-GJS_NATIVE_CONSTRUCTOR_DECLARE(object_instance)
+static JSBool
+object_instance_init (JSContext *context,
+                      JSObject **object,
+                      uintN      argc,
+                      jsval     *argv)
 {
-    GJS_NATIVE_CONSTRUCTOR_VARIABLES(object_instance)
     ObjectInstance *priv;
     GType gtype;
     GParameter *params;
@@ -750,11 +757,9 @@ GJS_NATIVE_CONSTRUCTOR_DECLARE(object_instance)
     GTypeQuery query;
     JSObject *old_jsobj;
 
-    GJS_NATIVE_CONSTRUCTOR_PRELUDE(object_instance);
-
-    priv = init_object_private(context, object);
+    priv = init_object_private(context, *object);
 
-    gtype = g_registered_type_info_get_g_type( (GIRegisteredTypeInfo*) priv->info);
+    gtype = priv->gtype;
     if (gtype == G_TYPE_NONE) {
         gjs_throw(context,
                   "No GType for object '%s'???",
@@ -762,7 +767,7 @@ GJS_NATIVE_CONSTRUCTOR_DECLARE(object_instance)
         return JS_FALSE;
     }
 
-    if (!object_instance_props_to_g_parameters(context, object, argc, argv,
+    if (!object_instance_props_to_g_parameters(context, *object, argc, argv,
                                                gtype,
                                                &params, &n_params)) {
         return JS_FALSE;
@@ -782,7 +787,7 @@ GJS_NATIVE_CONSTRUCTOR_DECLARE(object_instance)
          * we're not actually using it, so just let it get collected. Avoiding
          * this would require a non-trivial amount of work.
          * */
-        object = old_jsobj;
+        *object = old_jsobj;
         g_object_unref(priv->gobj); /* We already own a reference */
         priv->gobj = NULL;
         goto out;
@@ -806,7 +811,7 @@ GJS_NATIVE_CONSTRUCTOR_DECLARE(object_instance)
         /* we should already have a ref */
     }
 
-    manage_js_gobject(context, object, priv);
+    manage_js_gobject(context, *object, priv);
 
     gjs_debug_lifecycle(GJS_DEBUG_GOBJECT,
                         "JSObject created with GObject %p %s",
@@ -816,11 +821,19 @@ GJS_NATIVE_CONSTRUCTOR_DECLARE(object_instance)
                                g_base_info_get_name ( (GIBaseInfo*) priv->info) ));
 
  out:
-    GJS_NATIVE_CONSTRUCTOR_FINISH(object_instance);
-
     return JS_TRUE;
 }
 
+GJS_NATIVE_CONSTRUCTOR_DECLARE(object_instance)
+{
+    GJS_NATIVE_CONSTRUCTOR_VARIABLES(object_instance)
+    JSBool ret;
+    GJS_NATIVE_CONSTRUCTOR_PRELUDE(object_instance);
+    ret = object_instance_init(context, &object, argc, argv);
+    GJS_NATIVE_CONSTRUCTOR_FINISH(object_instance);
+    return ret;
+}
+
 static void
 object_instance_finalize(JSContext *context,
                          JSObject  *obj)
@@ -1201,11 +1214,25 @@ static struct JSClass gjs_object_instance_class = {
     JSCLASS_NO_OPTIONAL_MEMBERS
 };
 
+static JSBool
+init_func (JSContext *context,
+           uintN      argc,
+           jsval     *vp)
+{
+    jsval *argv = JS_ARGV(context, vp);
+    JSObject *obj = JS_THIS_OBJECT(context, vp);
+
+    JS_SET_RVAL(context, vp, JSVAL_VOID);
+
+    return object_instance_init(context, &obj, argc, argv);
+}
+
 static JSPropertySpec gjs_object_instance_proto_props[] = {
     { NULL }
 };
 
 static JSFunctionSpec gjs_object_instance_proto_funcs[] = {
+    { "_init", (JSNative)init_func, 0, 0 },
     { "connect", (JSNative)connect_func, 0, 0 },
     { "connect_after", (JSNative)connect_after_func, 0, 0 },
     { "disconnect", (JSNative)disconnect_func, 0, 0 },
@@ -1618,3 +1645,84 @@ gjs_g_object_from_object(JSContext    *context,
 
     return priv->gobj;
 }
+
+static JSBool
+gjs_register_type(JSContext *cx,
+                  uintN      argc,
+                  jsval     *vp)
+{
+    jsval *argv = JS_ARGV(cx, vp);
+    jsval gtype;
+    gchar *name;
+    JSObject *parent, *object;
+    GType instance_type, parent_type;
+    GTypeQuery query;
+    ObjectInstance *parent_priv, *priv;
+    GTypeInfo type_info = {
+        0, /* class_size */
+
+	(GBaseInitFunc) NULL,
+	(GBaseFinalizeFunc) NULL,
+
+	(GClassInitFunc) NULL,
+	(GClassFinalizeFunc) NULL,
+	NULL, /* class_data */
+
+	0,    /* instance_size */
+	0,    /* n_preallocs */
+	(GInstanceInitFunc) NULL,
+    };
+
+    JS_BeginRequest(cx);
+
+    if (!gjs_parse_args(cx, "register_type",
+                        "oos", argc, argv,
+                        "parent", &parent,
+                        "object", &object,
+                        "name", &name))
+        return JS_FALSE;
+
+    if (!parent)
+        return JS_FALSE;
+
+    parent_priv = priv_from_js(cx, parent);
+
+    if (!parent_priv)
+        return JS_FALSE;
+
+    parent_type = parent_priv->gtype;
+
+    g_type_query(parent_type, &query);
+    type_info.class_size = query.class_size;
+    type_info.instance_size = query.instance_size;
+
+    instance_type = g_type_register_static(parent_type,
+                                           name,
+                                           &type_info,
+                                           0);
+
+    priv = g_slice_new0(ObjectInstance);
+    priv->info = parent_priv->info;
+    priv->gtype = instance_type;
+
+    JS_SetPrivate(cx, object, priv);
+
+    JS_EndRequest(cx);
+
+    return JS_TRUE;
+}
+
+static JSBool
+gjs_define_stuff(JSContext *context,
+                    JSObject  *module_obj)
+{
+    if (!JS_DefineFunction(context, module_obj,
+                           "register_type",
+                           (JSNative)gjs_register_type,
+                           3, GJS_MODULE_PROP_FLAGS))
+        return JS_FALSE;
+
+    return JS_TRUE;
+}
+
+GJS_REGISTER_NATIVE_MODULE("_gi", gjs_define_stuff)
diff --git a/modules/lang.js b/modules/lang.js
index b44b0e2..9e5c6c4 100644
--- a/modules/lang.js
+++ b/modules/lang.js
@@ -21,6 +21,8 @@
 
 // Utilities that are "meta-language" things like manipulating object props
 
+const Gi = imports._gi;
+
 function countProperties(obj) {
     let count = 0;
     for (let property in obj) {
diff --git a/modules/overrides/GObject.js b/modules/overrides/GObject.js
index 760248c..2f692db 100644
--- a/modules/overrides/GObject.js
+++ b/modules/overrides/GObject.js
@@ -19,8 +19,41 @@
 // FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS
 // IN THE SOFTWARE.
 
+const Lang = imports.lang;
+const Gi = imports._gi;
+
 let GObject;
 
+const GObjectMeta = new Lang.Class({
+    Name: 'GObjectClass',
+    Extends: Lang.Class,
+
+    _init: function(params) {
+        if (!params.Extends)
+            params.Extends = GObject.Object;
+
+        if (!this._isValidClass(params.Extends))
+            throw new TypeError('GObject.Class used with invalid base class (is ' + params.Extends.prototype + ')');
+
+        this.parent(params);
+
+        Gi.register_type(params.Extends.prototype, this.prototype, params.Name);
+    },
+
+    _isValidClass: function(klass) {
+        let proto = klass.prototype;
+
+        if (!proto)
+            return false;
+
+        // If proto == GObject.Object.prototype, then
+        // proto.__proto__ is Object, so "proto instanceof GObject.Object"
+        // will return false.
+        return proto == GObject.Object.prototype ||
+            proto instanceof GObject.Object;
+    }
+});
+
 function _init() {
 
     GObject = this;
@@ -117,4 +150,13 @@ function _init() {
                                                nick, blurb, flags, default_value);
     };
 
+    this.Class = GObjectMeta;
+    this.Object.prototype.__metaclass__ = this.Class;
+
+    // For compatibility with Lang.Class... we need a _construct
+    // or the Lang.Class constructor will fail.
+    this.Object.prototype._construct = function() {
+        this._init.apply(this, arguments);
+        return this;
+    };
 }



[Date Prev][Date Next]   [Thread Prev][Thread Next]   [Thread Index] [Date Index] [Author Index]