[gjs] Install GObject properties during class_init



commit 8eae3f9fca09f4acb8b295e78e86bee65bd97a0f
Author: Colin Walters <walters verbum org>
Date:   Wed May 29 20:41:02 2013 +0100

    Install GObject properties during class_init
    
    See https://bugzilla.gnome.org/show_bug.cgi?id=701196
    
    Add a new hash table utility wrapper which hashes GSize; on x32
    it falls back to malloc().
    
    Use this to keep track of properties to be installed; our
    generic class_init function looks them up based on GType.
    
    V2: Don't use a callback, just pass property array to
        register_type().  Keep signal installation where it
        is for now, since passing that down too would require
        a new structure type.
    
    https://bugzilla.gnome.org/show_bug.cgi?id=701196

 Makefile.am                  |    2 +
 gi/gtype.c                   |    8 +++
 gi/gtype.h                   |    3 +
 gi/object.c                  |  120 ++++++++++++++++++++++-------------------
 modules/overrides/GObject.js |   28 +++++-----
 util/hash-x32.c              |   69 ++++++++++++++++++++++++
 util/hash-x32.h              |   42 +++++++++++++++
 7 files changed, 201 insertions(+), 71 deletions(-)
---
diff --git a/Makefile.am b/Makefile.am
index 20f7ee7..9a3c0ac 100644
--- a/Makefile.am
+++ b/Makefile.am
@@ -62,6 +62,7 @@ noinst_HEADERS +=             \
        gjs/profiler.h          \
        gi/proxyutils.h         \
        util/crash.h            \
+       util/hash-x32.h         \
        util/error.h            \
        util/glib.h             \
        util/log.h              \
@@ -121,6 +122,7 @@ libgjs_la_SOURCES =         \
        modules/modules.c       \
        modules/modules.h       \
        util/error.c            \
+       util/hash-x32.c         \
        util/glib.c             \
        util/crash.c            \
        util/log.c              \
diff --git a/gi/gtype.c b/gi/gtype.c
index 3d5f5ad..b5e1726 100644
--- a/gi/gtype.c
+++ b/gi/gtype.c
@@ -192,3 +192,11 @@ gjs_gtype_get_actual_gtype (JSContext *context,
 
     return _gjs_gtype_get_actual_gtype(context, object, 2);
 }
+
+JSBool
+gjs_typecheck_gtype (JSContext             *context,
+                     JSObject              *obj,
+                     JSBool                 throw)
+{
+    return do_base_typecheck(context, obj, throw);
+}
diff --git a/gi/gtype.h b/gi/gtype.h
index 0214785..88832b0 100644
--- a/gi/gtype.h
+++ b/gi/gtype.h
@@ -42,6 +42,9 @@ JSObject * gjs_gtype_create_gtype_wrapper (JSContext *context,
 GType      gjs_gtype_get_actual_gtype (JSContext *context,
                                        JSObject  *object);
 
+JSBool    gjs_typecheck_gtype         (JSContext             *context,
+                                       JSObject              *obj,
+                                       JSBool                 throw);
 
 G_END_DECLS
 
diff --git a/gi/object.c b/gi/object.c
index 7410caf..18c1339 100644
--- a/gi/object.c
+++ b/gi/object.c
@@ -30,6 +30,7 @@
 #include "gtype.h"
 #include "arg.h"
 #include "repo.h"
+#include "gtype.h"
 #include "function.h"
 #include "proxyutils.h"
 #include "param.h"
@@ -44,6 +45,7 @@
 #include <gjs/runtime.h>
 
 #include <util/log.h>
+#include <util/hash-x32.h>
 #include <girepository.h>
 
 typedef struct {
@@ -86,6 +88,7 @@ enum {
 };
 
 static GSList *object_init_list;
+static GHashTable *class_init_properties;
 
 static struct JSClass gjs_object_instance_class;
 static GThread *gjs_eval_thread;
@@ -2357,10 +2360,27 @@ static void
 gjs_object_class_init(GObjectClass *class,
                       gpointer      user_data)
 {
+    GPtrArray *properties;
+    GType gtype;
+    gint i;
+
+    gtype = G_OBJECT_CLASS_TYPE (class);
+
     class->set_property = gjs_object_set_gproperty;
     class->get_property = gjs_object_get_gproperty;
 
     gjs_eval_thread = g_thread_self();
+
+    properties = gjs_hash_table_for_gsize_lookup (class_init_properties, gtype);
+    if (properties != NULL) {
+        for (i = 0; i < properties->len; i++) {
+            GParamSpec *pspec = properties->pdata[i];
+            g_param_spec_set_qdata(pspec, gjs_is_custom_property_quark(), GINT_TO_POINTER(1));
+            g_object_class_install_property (class, i+1, pspec);
+        }
+        
+        gjs_hash_table_for_gsize_remove (class_init_properties, gtype);
+    }
 }
 
 static void
@@ -2409,7 +2429,7 @@ gjs_register_type(JSContext *cx,
 {
     jsval *argv = JS_ARGV(cx, vp);
     gchar *name;
-    JSObject *parent, *constructor, *interfaces, *module;
+    JSObject *parent, *constructor, *interfaces, *properties, *module;
     GType instance_type, parent_type;
     GTypeQuery query;
     GTypeModule *type_module;
@@ -2428,17 +2448,19 @@ gjs_register_type(JSContext *cx,
        0,    /* n_preallocs */
        gjs_object_custom_init,
     };
-    guint32 i, n_interfaces;
+    guint32 i, n_interfaces, n_properties;
+    GPtrArray *properties_native = NULL;
     GType *iface_types;
     JSBool retval = JS_FALSE;
 
     JS_BeginRequest(cx);
 
     if (!gjs_parse_args(cx, "register_type",
-                        "oso", argc, argv,
+                        "osoo", argc, argv,
                         "parent", &parent,
                         "name", &name,
-                        "interfaces", &interfaces))
+                        "interfaces", &interfaces,
+                        "properties", &properties))
         goto out;
 
     if (!parent)
@@ -2455,6 +2477,14 @@ gjs_register_type(JSContext *cx,
     if (!JS_GetArrayLength(cx, interfaces, &n_interfaces))
         goto out;
 
+    if (!JS_IsArrayObject(cx, properties)) {
+        gjs_throw(cx, "Invalid parameter properties (expected Array)");
+        goto out;
+    }
+
+    if (!JS_GetArrayLength(cx, properties, &n_properties))
+        goto out;
+
     iface_types = g_alloca(sizeof(GType) * n_interfaces);
 
     /* We do interface addition in two passes so that any failure
@@ -2508,6 +2538,28 @@ gjs_register_type(JSContext *cx,
 
     g_type_set_qdata (instance_type, gjs_is_custom_type_quark(), GINT_TO_POINTER (1));
 
+    if (!class_init_properties)
+        class_init_properties = gjs_hash_table_new_for_gsize ((GDestroyNotify)g_ptr_array_unref);
+    properties_native = g_ptr_array_new_with_free_func ((GDestroyNotify)g_param_spec_unref);
+    for (i = 0; i < n_properties; i++) {
+        jsval prop_val;
+        JSObject *prop_obj;
+
+        if (!JS_GetElement(cx, properties, i, &prop_val))
+            goto out;
+
+        if (!JSVAL_IS_OBJECT(prop_val)) {
+            gjs_throw (cx, "Invalid parameter, expected object");
+            goto out;
+        }
+        prop_obj = JSVAL_TO_OBJECT(prop_val);
+        if (!gjs_typecheck_param(cx, prop_obj, G_TYPE_NONE, JS_TRUE))
+            goto out;
+        g_ptr_array_add (properties_native, g_param_spec_ref (gjs_g_param_from_param (cx, prop_obj)));
+    }
+    gjs_hash_table_for_gsize_insert (class_init_properties, (gsize)instance_type,
+                                     g_ptr_array_ref (properties_native));
+
     for (i = 0; i < n_interfaces; i++)
         gjs_add_interface(instance_type, iface_types[i]);
 
@@ -2520,56 +2572,20 @@ gjs_register_type(JSContext *cx,
     retval = JS_TRUE;
 
 out:
+    g_clear_pointer(&properties_native, g_ptr_array_unref);
     JS_EndRequest(cx);
 
     return retval;
 }
 
 static JSBool
-gjs_register_property(JSContext *cx,
-                      unsigned   argc,
-                      jsval     *vp)
-{
-    jsval *argv = JS_ARGV(cx, vp);
-    JSObject *obj;
-    JSObject *pspec_js;
-    GParamSpec *pspec;
-    ObjectInstance *priv;
-
-    if (argc != 2)
-        return JS_FALSE;
-
-    if (!JSVAL_IS_OBJECT(argv[0]) ||
-        !JSVAL_IS_OBJECT(argv[1]))
-        return JS_FALSE;
-
-    obj = JSVAL_TO_OBJECT(argv[0]);
-    pspec_js = JSVAL_TO_OBJECT(argv[1]);
-
-    if (!do_base_typecheck(cx, obj, JS_TRUE))
-        return JS_FALSE;
-    if (!gjs_typecheck_param(cx, pspec_js, G_TYPE_NONE, JS_TRUE))
-        return JS_FALSE;
-
-    priv = priv_from_js(cx, obj);
-    pspec = gjs_g_param_from_param(cx, pspec_js);
-
-    g_param_spec_set_qdata(pspec, gjs_is_custom_property_quark(), GINT_TO_POINTER(1));
-
-    g_object_class_install_property(G_OBJECT_CLASS (priv->klass), PROP_JS_HANDLED, pspec);
-
-    JS_SET_RVAL(cx, vp, JSVAL_VOID);
-    return JS_TRUE;
-}
-
-static JSBool
 gjs_signal_new(JSContext *cx,
                unsigned   argc,
                jsval     *vp)
 {
     jsval *argv = JS_ARGV(cx, vp);
     JSObject *obj;
-    ObjectInstance *priv;
+    GType gtype;
     gchar *signal_name = NULL;
     GSignalAccumulator accumulator;
     gint signal_id;
@@ -2588,12 +2604,8 @@ gjs_signal_new(JSContext *cx,
     }
 
     obj = JSVAL_TO_OBJECT(argv[0]);
-    if (!do_base_typecheck(cx, obj, JS_TRUE)) {
-        ret = JS_FALSE;
-        goto out;
-    }
-
-    priv = priv_from_js(cx, obj);
+    if (!gjs_typecheck_gtype(cx, obj, JS_TRUE))
+        return JS_FALSE;
 
     /* we only support standard accumulators for now */
     switch (JSVAL_TO_INT(argv[3])) {
@@ -2632,8 +2644,10 @@ gjs_signal_new(JSContext *cx,
         params[i] = gjs_gtype_get_actual_gtype(cx, JSVAL_TO_OBJECT(gtype_val));
     }
 
+    gtype = gjs_gtype_get_actual_gtype(cx, obj);
+
     signal_id = g_signal_newv(signal_name,
-                              priv->gtype,
+                              gtype,
                               JSVAL_TO_INT(argv[2]), /* signal_flags */
                               NULL, /* class closure */
                               accumulator,
@@ -2660,7 +2674,7 @@ gjs_define_private_gi_stuff(JSContext *context,
     if (!JS_DefineFunction(context, module_obj,
                            "register_type",
                            (JSNative)gjs_register_type,
-                           2, GJS_MODULE_PROP_FLAGS))
+                           4, GJS_MODULE_PROP_FLAGS))
         return JS_FALSE;
 
     if (!JS_DefineFunction(context, module_obj,
@@ -2676,12 +2690,6 @@ gjs_define_private_gi_stuff(JSContext *context,
         return JS_FALSE;
 
     if (!JS_DefineFunction(context, module_obj,
-                           "register_property",
-                           (JSNative)gjs_register_property,
-                           2, GJS_MODULE_PROP_FLAGS))
-        return JS_FALSE;
-
-    if (!JS_DefineFunction(context, module_obj,
                            "signal_new",
                            (JSNative)gjs_signal_new,
                            6, GJS_MODULE_PROP_FLAGS))
diff --git a/modules/overrides/GObject.js b/modules/overrides/GObject.js
index 7902abc..b5651a0 100644
--- a/modules/overrides/GObject.js
+++ b/modules/overrides/GObject.js
@@ -28,23 +28,13 @@ const GObjectMeta = new Lang.Class({
     Name: 'GObjectClass',
     Extends: Lang.Class,
 
-    _init: function(params) {
-        // retrieve all parameters and remove them from params before chaining
-
-        let properties = params.Properties;
-        let signals = params.Signals;
-
-        delete params.Properties;
+    _init: function (params) {
+        // retrieve signals and remove them from params before chaining
+       let signals = params.Signals;
         delete params.Signals;
 
         this.parent(params);
 
-        if (properties) {
-            for (let prop in properties) {
-                Gi.register_property(this.prototype, properties[prop]);
-            }
-        }
-
         if (signals) {
             for (let signalName in signals) {
                 let obj = signals[signalName];
@@ -54,7 +44,7 @@ const GObjectMeta = new Lang.Class({
                 let paramtypes = (obj.param_types !== undefined) ? obj.param_types : [];
 
                 try {
-                    obj.signal_id = Gi.signal_new(this.prototype, signalName, flags, accumulator, rtype, 
paramtypes);
+                    obj.signal_id = Gi.signal_new(this.$gtype, signalName, flags, accumulator, rtype, 
paramtypes);
                 } catch(e) {
                     throw new TypeError('Invalid signal ' + signalName + ': ' + e.message);
                 }
@@ -115,9 +105,17 @@ const GObjectMeta = new Lang.Class({
             throw new TypeError('GObject.Class used with invalid base class (is ' + parent + ')');
 
         let interfaces = params.Implements || [];
+        let properties = params.Properties;
         delete params.Implements;
+        delete params.Properties;
 
-        let newClass = Gi.register_type(parent.prototype, gtypename, interfaces);
+       let propertiesArray = [];
+        if (properties) {
+            for (let prop in properties) {
+               propertiesArray.push(properties[prop]);
+            }
+        }
+        let newClass = Gi.register_type(parent.prototype, gtypename, interfaces, propertiesArray);
 
         // See Class.prototype._construct in lang.js for the reasoning
         // behind this direct __proto__ set.
diff --git a/util/hash-x32.c b/util/hash-x32.c
new file mode 100644
index 0000000..9a8a9a9
--- /dev/null
+++ b/util/hash-x32.c
@@ -0,0 +1,69 @@
+/* -*- mode: C; c-basic-offset: 4; indent-tabs-mode: nil; -*- */
+/*
+ * Copyright (c) 2013 Red Hat, Inc.
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a copy
+ * of this software and associated documentation files (the "Software"), to
+ * deal in the Software without restriction, including without limitation the
+ * rights to use, copy, modify, merge, publish, distribute, sublicense, and/or
+ * sell copies of the Software, and to permit persons to whom the Software is
+ * furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in
+ * all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+ * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
+ * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS
+ * IN THE SOFTWARE.
+ */
+
+#include "hash-x32.h"
+
+/* Note: Not actually tested on x32 */
+
+#define HASH_GSIZE_FITS_POINTER (sizeof(gsize) == sizeof(gpointer))
+
+GHashTable *
+gjs_hash_table_new_for_gsize (GDestroyNotify value_destroy)
+{
+    if (HASH_GSIZE_FITS_POINTER) {
+        return g_hash_table_new_full (NULL, NULL, NULL, value_destroy);
+    } else {
+        return g_hash_table_new_full (g_int64_hash, g_int64_equal, g_free, value_destroy);
+    }
+}
+
+void
+gjs_hash_table_for_gsize_insert (GHashTable *table, gsize key, gpointer value)
+{
+    if (HASH_GSIZE_FITS_POINTER) {
+        g_hash_table_insert (table, (gpointer)key, value);
+    } else {
+        guint64 *keycopy = g_new (guint64, 1);
+        *keycopy = (guint64) key;
+        g_hash_table_insert (table, keycopy, value);
+    }
+}
+
+void
+gjs_hash_table_for_gsize_remove (GHashTable *table, gsize key)
+{
+    if (HASH_GSIZE_FITS_POINTER)
+        g_hash_table_remove (table, (gpointer)key);
+    else
+        g_hash_table_remove (table, &key);
+}
+
+gpointer
+gjs_hash_table_for_gsize_lookup (GHashTable *table, gsize key)
+{
+    if (HASH_GSIZE_FITS_POINTER)
+        return g_hash_table_lookup (table, (gpointer)key);
+    else
+        return g_hash_table_lookup (table, &key);
+}
+
diff --git a/util/hash-x32.h b/util/hash-x32.h
new file mode 100644
index 0000000..4024c40
--- /dev/null
+++ b/util/hash-x32.h
@@ -0,0 +1,42 @@
+/* -*- mode: C; c-basic-offset: 4; indent-tabs-mode: nil; -*- */
+/*
+ * Copyright (c) 2013 Red Hat, Inc.
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a copy
+ * of this software and associated documentation files (the "Software"), to
+ * deal in the Software without restriction, including without limitation the
+ * rights to use, copy, modify, merge, publish, distribute, sublicense, and/or
+ * sell copies of the Software, and to permit persons to whom the Software is
+ * furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in
+ * all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+ * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
+ * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS
+ * IN THE SOFTWARE.
+ */
+#ifndef __GJS_UTIL_HASH_X32_H__
+#define __GJS_UTIL_HASH_X32_H__
+
+#include <glib.h>
+
+G_BEGIN_DECLS
+
+/* Hash table that operates on gsize; on every architecture except x32,
+ * sizeof(gsize) == sizeof(gpointer), and so we can just use it as a
+ * hash key directly.  But on x32, we have to fall back to malloc().
+ */
+
+GHashTable *gjs_hash_table_new_for_gsize (GDestroyNotify value_destroy);
+void gjs_hash_table_for_gsize_insert (GHashTable *table, gsize key, gpointer value);
+void gjs_hash_table_for_gsize_remove (GHashTable *table, gsize key);
+gpointer gjs_hash_table_for_gsize_lookup (GHashTable *table, gsize key);
+
+G_END_DECLS
+
+#endif


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