[gjs] Install GObject properties during class_init
- From: Colin Walters <walters src gnome org>
- To: commits-list gnome org
- Cc:
- Subject: [gjs] Install GObject properties during class_init
- Date: Fri, 7 Jun 2013 18:28:28 +0000 (UTC)
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]