[gjs/wip/gobj-kitchen-sink: 16/19] object: Make custom types have a custom JSClass
- From: Jasper St. Pierre <jstpierre src gnome org>
- To: commits-list gnome org
- Cc:
- Subject: [gjs/wip/gobj-kitchen-sink: 16/19] object: Make custom types have a custom JSClass
- Date: Fri, 3 Feb 2012 03:29:50 +0000 (UTC)
commit 4358188d24c92b6823bffb3eac9cc1a34d412666
Author: Giovanni Campagna <gcampagna src gnome org>
Date: Fri Dec 16 18:38:40 2011 +0100
object: Make custom types have a custom JSClass
Use gjs_define_object_class to create a dynamic JSClass for the
newly registered gtype, which doesn't have any introspection info.
gi/function.c | 7 ++
gi/object.c | 141 +++++++++++++++++++++++++++---------------
modules/overrides/GObject.js | 74 ++++++++++++++++------
modules/overrides/Gio.js | 17 ++++-
4 files changed, 167 insertions(+), 72 deletions(-)
---
diff --git a/gi/function.c b/gi/function.c
index 8b481aa..37d125a 100644
--- a/gi/function.c
+++ b/gi/function.c
@@ -666,6 +666,13 @@ gjs_invoke_c_function(JSContext *context,
GType gtype;
in_arg_cvalues[0].v_pointer = gjs_g_object_from_object(context, obj);
+ if (in_arg_cvalues[0].v_pointer == NULL) {
+ /* priv == NULL (user probably forgot to chain _init).
+ * Anyway, in this case we've thrown an exception, so just
+ * make sure we fail. */
+ failed = TRUE;
+ goto release;
+ }
gtype = g_registered_type_info_get_g_type ((GIRegisteredTypeInfo *)container);
if (!g_type_is_a (G_TYPE_FROM_INSTANCE (in_arg_cvalues[0].v_pointer),
diff --git a/gi/object.c b/gi/object.c
index 8ae382e..563d448 100644
--- a/gi/object.c
+++ b/gi/object.c
@@ -94,6 +94,16 @@ gjs_is_custom_type_quark (void)
return val;
}
+static GQuark
+gjs_is_custom_property_quark (void)
+{
+ static GQuark val = 0;
+ if (!val)
+ val = g_quark_from_static_string ("gjs::custom-property");
+
+ return val;
+}
+
static void
throw_priv_is_null_error(JSContext *context)
{
@@ -108,7 +118,8 @@ init_g_param_from_property(JSContext *context,
const char *js_prop_name,
jsval js_value,
GType gtype,
- GParameter *parameter)
+ GParameter *parameter,
+ gboolean constructing)
{
char *gname;
GParamSpec *param_spec;
@@ -129,6 +140,13 @@ init_g_param_from_property(JSContext *context,
return NO_SUCH_G_PROPERTY;
}
+ /* Do not set JS overridden properties through GObject, to avoid
+ * infinite recursion (but set them when constructing) */
+ if (!constructing &&
+ g_param_spec_get_qdata(param_spec, gjs_is_custom_property_quark()))
+ return NO_SUCH_G_PROPERTY;
+
+
if ((param_spec->flags & G_PARAM_WRITABLE) == 0) {
/* prevent setting the prop even in JS */
gjs_throw(context, "Property %s (GObject %s) is not writable",
@@ -175,13 +193,9 @@ object_instance_get_prop(JSContext *context,
"Get prop '%s' hook obj %p priv %p", name, obj, priv);
if (priv == NULL) {
- /* 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;
- throw_priv_is_null_error (context);
- }
-
+ /* If we reach this point, either object_instance_new_resolve
+ * did not throw (so name == "_init"), or the property actually
+ * exists and it's not something we should be concerned with */
goto out;
}
if (priv->gobj == NULL) /* prototype, not an instance. */
@@ -197,6 +211,11 @@ object_instance_get_prop(JSContext *context,
goto out;
}
+ /* Do not fetch JS overridden properties from GObject, to avoid
+ * infinite recursion. */
+ if (g_param_spec_get_qdata(param, gjs_is_custom_property_quark()))
+ goto out;
+
if ((param->flags & G_PARAM_READABLE) == 0)
goto out;
@@ -242,8 +261,7 @@ object_instance_set_prop(JSContext *context,
"Set prop '%s' hook obj %p priv %p", name, obj, priv);
if (priv == NULL) {
- ret = JS_FALSE; /* wrong class passed in */
- throw_priv_is_null_error(context);
+ /* see the comment in object_instance_get_prop() on this */
goto out;
}
if (priv->gobj == NULL) /* prototype, not an instance. */
@@ -252,7 +270,8 @@ object_instance_set_prop(JSContext *context,
switch (init_g_param_from_property(context, name,
*value_p,
G_TYPE_FROM_INSTANCE(priv->gobj),
- ¶m)) {
+ ¶m,
+ FALSE /* constructing */)) {
case SOME_ERROR_OCCURRED:
ret = JS_FALSE;
case NO_SUCH_G_PROPERTY:
@@ -561,26 +580,6 @@ object_instance_props_to_g_parameters(JSContext *context,
if (n_gparams_p)
*n_gparams_p = 0;
- if (argc == 0)
- return JS_TRUE;
-
- if (!JSVAL_IS_OBJECT(argv[0])) {
- gjs_throw(context, "argument should be a hash with props to set");
- return JS_FALSE;
- }
-
- props = JSVAL_TO_OBJECT(argv[0]);
-
- iter = JS_NewPropertyIterator(context, props);
- if (iter == NULL) {
- gjs_throw(context, "Failed to create property iterator for object props hash");
- return JS_FALSE;
- }
-
- prop_id = JSID_VOID;
- if (!JS_NextProperty(context, iter, &prop_id))
- return JS_FALSE;
-
gparams = g_array_new(/* nul term */ FALSE, /* clear */ TRUE,
sizeof(GParameter));
@@ -606,6 +605,26 @@ object_instance_props_to_g_parameters(JSContext *context,
g_array_append_val(gparams, gparam);
}
+ if (argc == 0 || JSVAL_IS_VOID(argv[0]))
+ goto out;
+
+ if (!JSVAL_IS_OBJECT(argv[0])) {
+ gjs_throw(context, "argument should be a hash with props to set");
+ goto free_array_and_fail;
+ }
+
+ props = JSVAL_TO_OBJECT(argv[0]);
+
+ iter = JS_NewPropertyIterator(context, props);
+ if (iter == NULL) {
+ gjs_throw(context, "Failed to create property iterator for object props hash");
+ goto free_array_and_fail;
+ }
+
+ prop_id = JSID_VOID;
+ if (!JS_NextProperty(context, iter, &prop_id))
+ goto free_array_and_fail;
+
while (!JSID_IS_VOID(prop_id)) {
char *name;
jsval value;
@@ -622,7 +641,8 @@ object_instance_props_to_g_parameters(JSContext *context,
switch (init_g_param_from_property(context, name,
value,
gtype,
- &gparam)) {
+ &gparam,
+ TRUE /* constructing */)) {
case NO_SUCH_G_PROPERTY:
gjs_throw(context, "No property %s on this GObject %s",
name, g_type_name(gtype));
@@ -642,6 +662,7 @@ object_instance_props_to_g_parameters(JSContext *context,
goto free_array_and_fail;
}
+ out:
if (n_gparams_p)
*n_gparams_p = gparams->len;
if (gparams_p)
@@ -917,9 +938,21 @@ GJS_NATIVE_CONSTRUCTOR_DECLARE(object_instance)
{
GJS_NATIVE_CONSTRUCTOR_VARIABLES(object_instance)
JSBool ret;
+ jsval initer;
+ jsval rval;
+
GJS_NATIVE_CONSTRUCTOR_PRELUDE(object_instance);
- ret = object_instance_init(context, &object, argc, argv);
- GJS_NATIVE_CONSTRUCTOR_FINISH(object_instance);
+
+ if (!gjs_object_require_property(context, object, "GObject instance", "_init", &initer))
+ return JS_FALSE;
+
+ rval = JSVAL_VOID;
+ ret = gjs_call_function_value(context, object, initer, argc, argv, &rval);
+
+ if (JSVAL_IS_VOID(rval))
+ rval = OBJECT_TO_JSVAL(object);
+
+ JS_SET_RVAL(context, vp, rval);
return ret;
}
@@ -1149,7 +1182,7 @@ emit_func(JSContext *context,
GSignalQuery signal_query;
char *signal_name;
GValue *instance_and_args;
- GValue rvalue;
+ GValue rvalue = G_VALUE_INIT;
unsigned int i;
gboolean failed;
jsval retval;
@@ -1324,10 +1357,14 @@ init_func (JSContext *context,
{
jsval *argv = JS_ARGV(context, vp);
JSObject *obj = JS_THIS_OBJECT(context, vp);
+ JSBool ret;
- JS_SET_RVAL(context, vp, JSVAL_VOID);
+ ret = object_instance_init(context, &obj, argc, argv);
+
+ if (ret)
+ JS_SET_RVAL(context, vp, OBJECT_TO_JSVAL(obj));
- return object_instance_init(context, &obj, argc, argv);
+ return ret;
}
static JSPropertySpec gjs_object_instance_proto_props[] = {
@@ -1715,14 +1752,18 @@ gjs_g_object_from_object(JSContext *context,
priv = priv_from_js(context, obj);
- if (priv == NULL)
+ if (priv == NULL) {
+ gjs_throw(context,
+ "Object instance or prototype has not been properly initialized yet. "
+ "Did you forget to chain-up from _init()?");
return NULL;
+ }
if (priv->gobj == NULL) {
gjs_throw(context,
"Object is %s.%s.prototype, not an object instance - cannot convert to GObject*",
- g_base_info_get_namespace( (GIBaseInfo*) priv->info),
- g_base_info_get_name( (GIBaseInfo*) priv->info));
+ priv->info ? g_base_info_get_namespace( (GIBaseInfo*) priv->info) : "",
+ priv->info ? g_base_info_get_name( (GIBaseInfo*) priv->info) : g_type_name(priv->gtype));
return NULL;
}
@@ -2015,12 +2056,11 @@ gjs_register_type(JSContext *cx,
jsval *vp)
{
jsval *argv = JS_ARGV(cx, vp);
- jsval gtype;
gchar *name;
- JSObject *parent, *object;
+ JSObject *parent, *constructor;
GType instance_type, parent_type;
GTypeQuery query;
- ObjectInstance *parent_priv, *priv;
+ ObjectInstance *parent_priv;
GTypeInfo type_info = {
0, /* class_size */
@@ -2039,9 +2079,8 @@ gjs_register_type(JSContext *cx,
JS_BeginRequest(cx);
if (!gjs_parse_args(cx, "register_type",
- "oos", argc, argv,
+ "os", argc, argv,
"parent", &parent,
- "object", &object,
"name", &name))
return JS_FALSE;
@@ -2066,11 +2105,11 @@ gjs_register_type(JSContext *cx,
g_type_set_qdata (instance_type, gjs_is_custom_type_quark(), GINT_TO_POINTER (1));
- priv = g_slice_new0(ObjectInstance);
- priv->info = parent_priv->info;
- priv->gtype = instance_type;
+ /* create a custom JSClass */
+ if (!gjs_define_object_class(cx, NULL, instance_type, &constructor, NULL))
+ return JS_FALSE;
- JS_SetPrivate(cx, object, priv);
+ JS_SET_RVAL(cx, vp, OBJECT_TO_JSVAL(constructor));
JS_EndRequest(cx);
@@ -2128,6 +2167,8 @@ gjs_register_property(JSContext *cx,
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));
+
oclass = g_type_class_ref(priv->gtype);
g_object_class_install_property(oclass, PROP_JS_HANDLED, pspec);
g_type_class_unref(oclass);
@@ -2229,7 +2270,7 @@ gjs_define_stuff(JSContext *context,
if (!JS_DefineFunction(context, module_obj,
"register_type",
(JSNative)gjs_register_type,
- 3, GJS_MODULE_PROP_FLAGS))
+ 2, GJS_MODULE_PROP_FLAGS))
return JS_FALSE;
if (!JS_DefineFunction(context, module_obj,
diff --git a/modules/overrides/GObject.js b/modules/overrides/GObject.js
index 0cb7d9b..cd728e9 100644
--- a/modules/overrides/GObject.js
+++ b/modules/overrides/GObject.js
@@ -29,47 +29,45 @@ const GObjectMeta = new Lang.Class({
Extends: Lang.Class,
_init: function(params) {
- if (!params.Extends)
- params.Extends = GObject.Object;
+ this.parent(params);
- if (!this._isValidClass(params.Extends))
- throw new TypeError('GObject.Class used with invalid base class (is ' + params.Extends.prototype + ')');
+ // retrieve all parameters and remove them from params before chaining
- this.parent(params);
+ let properties = params.Properties;
+ let signals = params.Signals;
+ let ifaces = params.Implements;
- Gi.register_type(params.Extends.prototype, this.prototype, params.Name);
+ delete params.Properties;
+ delete params.Signals;
+ delete params.Implements;
- if (params.Properties) {
- for (let prop in params.Properties) {
- Gi.register_property(this.prototype, params.Properties[prop]);
+ if (properties) {
+ for (let prop in properties) {
+ Gi.register_property(this.prototype, properties[prop]);
}
}
- if (params.Signals) {
- for (let signalName in params.Signals) {
- let obj = params.Signals[signalName];
+ if (signals) {
+ for (let signalName in signals) {
+ let obj = signals[signalName];
let flags = (obj.flags !== undefined) ? obj.flags : GObject.SignalFlags.RUN_FIRST;
let accumulator = (obj.accumulator !== undefined) ? obj.accumulator : GObject.AccumulatorType.NONE;
let rtype = (obj.return_type !== undefined) ? obj.return_type : GObject.TYPE_NONE;
let paramtypes = (obj.param_types !== undefined) ? obj.param_types : [];
try {
- obj.signal_id = Gi.signal_new(this.prototype, signal_name, flags, accumulator, rtype, paramtypes);
+ obj.signal_id = Gi.signal_new(this.prototype, signalName, flags, accumulator, rtype, paramtypes);
} catch(e) {
throw new TypeError('Invalid signal ' + signal_name + ': ' + e.message);
}
}
}
- if (params.Implements) {
- for (let i = 0; i < params.Implements.length; i++)
+ if (ifaces) {
+ for (let i = 0; i < ifaces.length; i++)
Gi.add_interface(this.prototype, ifaces[i]);
}
- delete params.Properties;
- delete params.Signals;
- delete params.Implements;
-
for (let prop in params) {
let value = this.prototype[prop];
if (typeof value === 'function') {
@@ -101,6 +99,44 @@ const GObjectMeta = new Lang.Class({
// will return false.
return proto == GObject.Object.prototype ||
proto instanceof GObject.Object;
+ },
+
+ // If we want an object with a custom JSClass, we can't just
+ // use a function. We have to use a custom constructor here.
+ _construct: function(params) {
+ if (!params.Name)
+ throw new TypeError("Classes require an explicit 'Name' parameter.");
+ let name = params.Name;
+
+ let gtypename;
+ if (params.GTypeName)
+ gtypename = params.GTypeName;
+ else
+ gtypename = 'Gjs_' + params.Name;
+
+ if (!params.Extends)
+ params.Extends = GObject.Object;
+ let parent = params.Extends;
+
+ if (!this._isValidClass(parent))
+ throw new TypeError('GObject.Class used with invalid base class (is ' + parent + ')');
+
+ let newClass = Gi.register_type(parent.prototype, gtypename);
+
+ // See Class.prototype._construct in lang.js for the reasoning
+ // behind this direct __proto__ set.
+ newClass.__proto__ = this.constructor.prototype;
+ newClass.__super__ = parent;
+
+ newClass._init.apply(newClass, arguments);
+
+ Object.defineProperty(newClass.prototype, '__metaclass__',
+ { writable: false,
+ configurable: false,
+ enumerable: false,
+ value: this.constructor });
+
+ return newClass;
}
});
diff --git a/modules/overrides/Gio.js b/modules/overrides/Gio.js
index 0098832..6db07d7 100644
--- a/modules/overrides/Gio.js
+++ b/modules/overrides/Gio.js
@@ -171,9 +171,15 @@ const DBusProxyClass = new Lang.Class({
Name: 'DBusProxyClass',
Extends: GObject.Class,
- _init: function(params) {
+ _construct: function(params) {
+ /* params.Extends cannot be set in _init(), as
+ GObject.Class._construct needs it */
params.Extends = Gio.DBusProxy;
+ return this.parent(params);
+ },
+
+ _init: function(params) {
if (!params.Interface)
throw new TypeError('Interface must be specified in the declaration of a DBusProxyClass');
if (!(params.Interface instanceof Gio.DBusInterfaceInfo))
@@ -424,6 +430,13 @@ const DBusImplementerClass = new Lang.Class({
Name: 'DBusImplementerClass',
Extends: Lang.Class,
+ _construct: function(params) {
+ /* this must be set inside _construct, not _init */
+ params.Extends = DBusImplementerBase;
+
+ return this.parent(params);
+ },
+
_init: function(params) {
if (!params.Interface)
throw new TypeError('Interface must be specified in the declaration of a DBusImplementerClass');
@@ -434,8 +447,6 @@ const DBusImplementerClass = new Lang.Class({
this.Interface = params.Interface;
delete params.Interface;
- params.Extends = DBusImplementerBase;
-
this.parent(params);
}
});
[
Date Prev][
Date Next] [
Thread Prev][
Thread Next]
[
Thread Index]
[
Date Index]
[
Author Index]