[gjs/wip/gobj-kitchen-sink: 25/26] object: Put vfuncs implementations in a separate namespace
- From: Jasper St. Pierre <jstpierre src gnome org>
- To: commits-list gnome org
- Cc:
- Subject: [gjs/wip/gobj-kitchen-sink: 25/26] object: Put vfuncs implementations in a separate namespace
- Date: Sat, 28 Jan 2012 09:05:39 +0000 (UTC)
commit f24087654f894c0527851b15a25ef901b3e095e5
Author: Jasper St. Pierre <jstpierre mecheye net>
Date: Sat Jan 28 00:15:00 2012 -0500
object: Put vfuncs implementations in a separate namespace
Rather than depending on a prefix, "do_", to expose parent vfuncs and hook
up automatically, introduce a new namespace, "$gobject_vfuncs", which exposes
implementations of vfuncs.
When subclassing a GObject, to have vfuncs hooked up automatically, use
a new special property, "VFuncImpls":
const MyVFuncableClass = new Lang.Class({
Name: 'MyVFuncableClass',
Extends: GObject.Object,
Implements: [ Gio.Initable ],
_init: function() {
this.parent();
this.inited = false;
},
VFuncImpls: {
// Implementation of the 'init' vfunc that
// Gio.Initable defines.
init: function(cancellable) {
this.inited = true;
}
}
});
Makefile.am | 1 +
gi/function.c | 14 +---
gi/object.c | 100 ++------------------
gi/vfuncwrapper.c | 206 ++++++++++++++++++++++++++++++++++++++++++
gjs/jsapi-util.h | 19 +++-
modules/lang.js | 7 ++-
modules/overrides/GObject.js | 19 ++++-
test/js/testGIMarshalling.js | 38 ++++----
test/js/testGObjectClass.js | 6 +-
9 files changed, 278 insertions(+), 132 deletions(-)
---
diff --git a/Makefile.am b/Makefile.am
index 83c6ebb..0d5c4dd 100644
--- a/Makefile.am
+++ b/Makefile.am
@@ -136,6 +136,7 @@ libgjs_la_SOURCES += \
gi/value.c \
gi/interface.c \
gi/gtype.c
+EXTRA_DIST += gi/vfuncwrapper.c
if ENABLE_DTRACE
gjs_gi_probes.h: gi/gjs_gi_probes.d
diff --git a/gi/function.c b/gi/function.c
index 48f4569..bc00caf 100644
--- a/gi/function.c
+++ b/gi/function.c
@@ -1554,7 +1554,6 @@ gjs_define_function(JSContext *context,
JSObject *function = NULL;
GIInfoType info_type;
gchar *name;
- gboolean free_name;
info_type = g_base_info_get_type((GIBaseInfo *)info);
@@ -1568,15 +1567,7 @@ gjs_define_function(JSContext *context,
return NULL;
}
- if (info_type == GI_INFO_TYPE_FUNCTION) {
- name = (gchar *) g_base_info_get_name((GIBaseInfo*) info);
- free_name = FALSE;
- } else if (info_type == GI_INFO_TYPE_VFUNC) {
- name = g_strdup_printf("do_%s", g_base_info_get_name((GIBaseInfo*) info));
- free_name = TRUE;
- } else {
- g_assert_not_reached ();
- }
+ name = (gchar *) g_base_info_get_name((GIBaseInfo*) info);
if (!JS_DefineProperty(context, in_object, name,
OBJECT_TO_JSVAL(function),
@@ -1588,9 +1579,6 @@ gjs_define_function(JSContext *context,
return NULL;
}
- if (free_name)
- g_free(name);
-
JS_EndRequest(context);
return function;
}
diff --git a/gi/object.c b/gi/object.c
index 7535a84..b59988b 100644
--- a/gi/object.c
+++ b/gi/object.c
@@ -296,60 +296,7 @@ object_instance_set_prop(JSContext *context,
return ret;
}
-static gboolean
-is_vfunc_unchanged(GIVFuncInfo *info,
- GType gtype)
-{
- GType ptype = g_type_parent(gtype);
- GError *error = NULL;
- gpointer addr1, addr2;
-
- addr1 = g_vfunc_info_get_address(info, gtype, &error);
- if (error) {
- g_clear_error(&error);
- return FALSE;
- }
-
- addr2 = g_vfunc_info_get_address(info, ptype, &error);
- if (error) {
- g_clear_error(&error);
- return FALSE;
- }
-
- return addr1 == addr2;
-}
-
-static GIVFuncInfo *
-find_vfunc_on_parent(GIObjectInfo *info,
- gchar *name)
-{
- GIVFuncInfo *vfunc = NULL;
- GIObjectInfo *parent, *old_parent;
-
- /* ref the first info so that we don't destroy
- * it when unrefing parents later */
- g_base_info_ref(info);
- parent = info;
-
- /* Since it isn't possible to override a vfunc on
- * an interface without reimplementing it, we don't need
- * to search the parent types when looking for a vfunc. */
- vfunc = g_object_info_find_vfunc_using_interfaces(parent, name, NULL);
- while (!vfunc && parent) {
- old_parent = parent;
- parent = g_object_info_get_parent(old_parent);
- if (!parent)
- break;
-
- g_base_info_unref(old_parent);
- vfunc = g_object_info_find_vfunc(parent, name);
- }
-
- if (parent)
- g_base_info_unref(parent);
-
- return vfunc;
-}
+#include "vfuncwrapper.c"
/*
* Like JSResolveOp, but flags provide contextual information as follows:
@@ -459,43 +406,16 @@ object_instance_new_resolve(JSContext *context,
goto out;
}
- if (g_str_has_prefix (name, "do_")) {
- /* The only time we find a vfunc info is when we're the base
- * class that defined the vfunc. If we let regular prototype
- * chaining resolve this, we'd have the implementation for the base's
- * vfunc on the base class, without any other "real" implementations
- * in the way. If we want to expose a "real" vfunc implementation,
- * we need to go down to the parent infos and look at their VFuncInfos.
- *
- * This is good, but it's memory-hungry -- we would define every
- * possible vfunc on every possible object, even if it's the same
- * "real" vfunc underneath. Instead, only expose vfuncs that are
- * different from their parent, and let prototype chaining do the
- * rest.
- */
-
- gchar *name_without_do = &name[3];
- GIVFuncInfo *vfunc;
-
- vfunc = find_vfunc_on_parent(priv->info, name_without_do);
- if (vfunc == NULL) {
- ret = JS_TRUE;
- goto out;
+ if (strcmp(name, "$gobject_vfuncs") == 0) {
+ JSObject *vfunc_wrapper;
+ vfunc_wrapper = gjs_vfunc_wrapper_new(context, obj);
+ if (vfunc_wrapper != NULL) {
+ JS_DefineProperty(context, obj, "$gobject_vfuncs",
+ OBJECT_TO_JSVAL(vfunc_wrapper),
+ NULL, NULL,
+ GJS_MODULE_PROP_FLAGS);
+ *objp = obj;
}
-
- /* In the event that the vfunc is unchanged, let regular
- * prototypal inheritance take over. */
- if (is_vfunc_unchanged(vfunc, priv->gtype)) {
- g_base_info_unref((GIBaseInfo *)vfunc);
- ret = JS_TRUE;
- goto out;
- }
-
- gjs_define_function(context, obj, priv->gtype, vfunc);
- *objp = obj;
- g_base_info_unref((GIBaseInfo *)vfunc);
- ret = JS_TRUE;
- goto out;
}
/* find_method does not look at methods on parent classes,
diff --git a/gi/vfuncwrapper.c b/gi/vfuncwrapper.c
new file mode 100644
index 0000000..44a52d7
--- /dev/null
+++ b/gi/vfuncwrapper.c
@@ -0,0 +1,206 @@
+/* -*- mode: C; c-basic-offset: 4; indent-tabs-mode: nil; -*- */
+/*
+ * Copyright (c) 2008 litl, LLC
+ *
+ * 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.
+ */
+
+/* This file is part of object.c - it has been split up to make
+ * maintainership easier. */
+
+static jsval gjs_vfunc_wrapper_create_proto (JSContext *context,
+ JSObject *module,
+ const char *proto_name,
+ JSObject *parent);
+
+static gboolean
+is_vfunc_unchanged(GIVFuncInfo *info,
+ GType gtype)
+{
+ GType ptype = g_type_parent(gtype);
+ GError *error = NULL;
+ gpointer addr1, addr2;
+
+ addr1 = g_vfunc_info_get_address(info, gtype, &error);
+ if (error) {
+ g_clear_error(&error);
+ return FALSE;
+ }
+
+ addr2 = g_vfunc_info_get_address(info, ptype, &error);
+ if (error) {
+ g_clear_error(&error);
+ return FALSE;
+ }
+
+ return addr1 == addr2;
+}
+
+static GIVFuncInfo *
+find_vfunc_on_parent(GIObjectInfo *info,
+ gchar *name)
+{
+ GIVFuncInfo *vfunc = NULL;
+ GIObjectInfo *parent, *old_parent;
+
+ /* ref the first info so that we don't destroy
+ * it when unrefing parents later */
+ g_base_info_ref(info);
+ parent = info;
+
+ /* Since it isn't possible to override a vfunc on
+ * an interface without reimplementing it, we don't need
+ * to search the parent types when looking for a vfunc. */
+ vfunc = g_object_info_find_vfunc_using_interfaces(parent, name, NULL);
+ while (!vfunc && parent) {
+ old_parent = parent;
+ parent = g_object_info_get_parent(old_parent);
+ if (!parent)
+ break;
+
+ g_base_info_unref(old_parent);
+ vfunc = g_object_info_find_vfunc(parent, name);
+ }
+
+ if (parent)
+ g_base_info_unref(parent);
+
+ return vfunc;
+}
+
+static JSBool
+gjs_vfunc_wrapper_new_resolve(JSContext *context,
+ JSObject *obj,
+ jsid id,
+ uintN flags,
+ JSObject **objp)
+{
+ ObjectInstance *priv;
+ char *name;
+ JSObject *owner;
+ GIVFuncInfo *vfunc;
+
+ if (!gjs_get_string_id(context, id, &name))
+ return JS_TRUE;
+
+ owner = JS_GetPrivate(context, obj);
+ if (owner == NULL)
+ goto out;
+
+ priv = priv_from_js(context, owner);
+ if (priv == NULL)
+ goto out;
+
+ /* The only time we find a vfunc info is when we're the base
+ * class that defined the vfunc. If we let regular prototype
+ * chaining resolve this, we'd have the implementation for the base's
+ * vfunc on the base class, without any other "real" implementations
+ * in the way. If we want to expose a "real" vfunc implementation,
+ * we need to go down to the parent infos and look at their VFuncInfos.
+ *
+ * This is good, but it's memory-hungry -- we would define every
+ * possible vfunc on every possible object, even if it's the same
+ * "real" vfunc underneath. Instead, only expose vfuncs that are
+ * different from their parent, and let prototype chaining do the
+ * rest.
+ */
+
+ vfunc = find_vfunc_on_parent(priv->info, name);
+ if (vfunc == NULL)
+ goto out;
+
+ /* In the event that the vfunc is unchanged, let regular
+ * prototypal inheritance take over. */
+ if (is_vfunc_unchanged(vfunc, priv->gtype)) {
+ g_base_info_unref((GIBaseInfo *)vfunc);
+ goto out;
+ }
+
+ gjs_define_function(context, obj, priv->gtype, vfunc);
+ *objp = obj;
+ g_base_info_unref((GIBaseInfo *)vfunc);
+
+ out:
+ g_free(name);
+ return JS_TRUE;
+}
+
+
+/* Default spidermonkey toString is worthless. Replace it
+ * with something that gives us both the introspection name
+ * and a memory address.
+ */
+static JSBool
+vfunc_wrapper_to_string_func(JSContext *context,
+ uintN argc,
+ jsval *vp)
+{
+ JSBool ret = JS_FALSE;
+ JSObject *obj = JS_THIS_OBJECT(context, vp);
+ JSObject *owner = JS_GetPrivate(context, obj);
+ gchar *owner_str;
+ gchar *strval;
+ jsval retval;
+
+ owner_str = gjs_value_debug_string(context, OBJECT_TO_JSVAL(owner));
+ strval = g_strdup_printf("[object VFuncWrapper for %s]", owner_str);
+ g_free(owner_str);
+
+ ret = gjs_string_from_utf8(context, strval, -1, &retval);
+ if (ret)
+ JS_SET_RVAL(context, vp, retval);
+ g_free(strval);
+ return ret;
+}
+
+static JSPropertySpec gjs_vfunc_wrapper_proto_props[] = {
+ { NULL }
+};
+
+static JSFunctionSpec gjs_vfunc_wrapper_proto_funcs[] = {
+ { "toString", (JSNative)vfunc_wrapper_to_string_func, 0, 0 },
+ { NULL }
+};
+
+_GJS_DUMMY_FINALIZE(vfunc_wrapper)
+_GJS_DEFINE_PROTO_FULL("VFuncWrapper", vfunc_wrapper, NULL);
+
+static JSObject *
+gjs_vfunc_wrapper_new (JSContext *context,
+ JSObject *owner)
+{
+ JSObject *object;
+ JSObject *global;
+
+ JS_BeginRequest(context);
+
+ /* put constructor for VFuncWrapper() in the global namespace */
+ global = gjs_get_import_global(context);
+ gjs_vfunc_wrapper_create_proto(context, global, "VFuncWrapper", NULL);
+
+ object = JS_NewObject(context, &gjs_vfunc_wrapper_class, NULL, NULL);
+ if (object == NULL)
+ goto out;
+
+ JS_SetPrivate(context, object, owner);
+
+ out:
+ JS_EndRequest(context);
+ return object;
+}
diff --git a/gjs/jsapi-util.h b/gjs/jsapi-util.h
index 0e186f9..a2db4aa 100644
--- a/gjs/jsapi-util.h
+++ b/gjs/jsapi-util.h
@@ -114,6 +114,7 @@ typedef struct GjsRootedArray GjsRootedArray;
*/
#define GJS_DEFINE_PROTO(tn, cn) \
GJS_NATIVE_CONSTRUCTOR_DECLARE(cn); \
+_GJS_DUMMY_RESOLVE(cn) \
_GJS_DEFINE_PROTO_FULL(tn, cn, gjs_##cn##_constructor)
/**
@@ -126,12 +127,15 @@ _GJS_DEFINE_PROTO_FULL(tn, cn, gjs_##cn##_constructor)
* you won't be able to instantiate it using the new keyword
*/
#define GJS_DEFINE_PROTO_ABSTRACT(tn, cn) \
+_GJS_DUMMY_RESOLVE(cn) \
_GJS_DEFINE_PROTO_FULL(tn, cn, NULL)
-#define _GJS_DEFINE_PROTO_FULL(type_name, cname, ctor) \
-static JSPropertySpec gjs_##cname##_proto_props[]; \
-static JSFunctionSpec gjs_##cname##_proto_funcs[]; \
-static void gjs_##cname##_finalize(JSContext *context, JSObject *obj); \
+#define _GJS_DUMMY_FINALIZE(cname) \
+static void gjs_##cname##_finalize(JSContext *context, JSObject *obj) \
+{ \
+}
+
+#define _GJS_DUMMY_RESOLVE(cname) \
static JSBool gjs_##cname##_new_resolve(JSContext *context, \
JSObject *obj, \
jsval id, \
@@ -139,7 +143,12 @@ static JSBool gjs_##cname##_new_resolve(JSContext *context, \
JSObject **objp) \
{ \
return JS_TRUE; \
-} \
+}
+
+#define _GJS_DEFINE_PROTO_FULL(type_name, cname, ctor) \
+static JSPropertySpec gjs_##cname##_proto_props[]; \
+static JSFunctionSpec gjs_##cname##_proto_funcs[]; \
+static void gjs_##cname##_finalize(JSContext *context, JSObject *obj); \
static struct JSClass gjs_##cname##_class = { \
type_name, \
JSCLASS_HAS_PRIVATE | \
diff --git a/modules/lang.js b/modules/lang.js
index 9e5c6c4..0956740 100644
--- a/modules/lang.js
+++ b/modules/lang.js
@@ -158,7 +158,12 @@ function _parent() {
let name = caller._name;
let parent = caller._owner.__super__;
- let previous = parent ? parent.prototype[name] : undefined;
+ let lookupOnParent = caller._lookupOnParent;
+ let previous;
+ if (lookupOnParent)
+ previous = parent ? lookupOnParent(parent, name) : undefined;
+ else
+ previous = parent ? parent.prototype[name] : undefined;
if (!previous)
throw new TypeError("The method '" + name + "' is not on the superclass");
diff --git a/modules/overrides/GObject.js b/modules/overrides/GObject.js
index bedd4bb..153d526 100644
--- a/modules/overrides/GObject.js
+++ b/modules/overrides/GObject.js
@@ -24,6 +24,10 @@ const Gi = imports._gi;
let GObject;
+function lookupOnParentVFunc(parent, name) {
+ return parent.prototype.$gobject_vfuncs[name];
+}
+
const GObjectMeta = new Lang.Class({
Name: 'GObjectClass',
Extends: Lang.Class,
@@ -36,10 +40,12 @@ const GObjectMeta = new Lang.Class({
let properties = params.Properties;
let signals = params.Signals;
let ifaces = params.Implements;
+ let vfuncImpls = params.VFuncImpls;
delete params.Properties;
delete params.Signals;
delete params.Implements;
+ delete params.VFuncImpls;
if (properties) {
for (let prop in properties) {
@@ -68,12 +74,19 @@ const GObjectMeta = new Lang.Class({
Gi.add_interface(this.prototype, ifaces[i]);
}
+ if (vfuncImpls) {
+ for (let prop in vfuncImpls) {
+ let value = this.wrapFunction(prop, vfuncImpls[prop]);
+ value._lookupOnParent = lookupOnParentVFunc;
+ this.prototype.$gobject_vfuncs[prop] = value;
+ Gi.hook_up_vfunc(this.prototype, prop, value);
+ }
+ }
+
for (let prop in params) {
let value = this.prototype[prop];
if (typeof value === 'function') {
- if (prop.slice(0, 3) == 'do_') {
- Gi.hook_up_vfunc(this.prototype, prop.slice(3), value);
- } else if (prop.slice(0, 3) == 'on_') {
+ if (prop.slice(0, 3) == 'on_') {
let id = GObject.signal_lookup(prop.slice(3).replace('_', '-'), this.$gtype);
if (id != 0) {
GObject.signal_override_class_closure(id, this.$gtype, function() {
diff --git a/test/js/testGIMarshalling.js b/test/js/testGIMarshalling.js
index 252c96b..c7cf3ed 100644
--- a/test/js/testGIMarshalling.js
+++ b/test/js/testGIMarshalling.js
@@ -234,24 +234,26 @@ const VFuncTester = new Lang.Class({
Name: 'VFuncTester',
Extends: GIMarshallingTests.Object,
- do_vfunc_return_value_only: function(obj) {
- return 42;
- },
-
- do_vfunc_one_out_parameter: function(obj) {
- return 43;
- },
-
- do_vfunc_multiple_out_parameters: function(obj) {
- return [44, 45];
- },
-
- do_vfunc_return_value_and_one_out_parameter: function(obj) {
- return [46, 47];
- },
-
- do_vfunc_return_value_and_multiple_out_parameters: function(obj) {
- return [48, 49, 50];
+ VFuncImpls: {
+ vfunc_return_value_only: function(obj) {
+ return 42;
+ },
+
+ vfunc_one_out_parameter: function(obj) {
+ return 43;
+ },
+
+ vfunc_multiple_out_parameters: function(obj) {
+ return [44, 45];
+ },
+
+ vfunc_return_value_and_one_out_parameter: function(obj) {
+ return [46, 47];
+ },
+
+ vfunc_return_value_and_multiple_out_parameters: function(obj) {
+ return [48, 49, 50];
+ }
}
});
diff --git a/test/js/testGObjectClass.js b/test/js/testGObjectClass.js
index 20a1e49..2b3efa3 100644
--- a/test/js/testGObjectClass.js
+++ b/test/js/testGObjectClass.js
@@ -141,8 +141,10 @@ const MyInitable = new Lang.Class({
this.inited = false;
},
- do_init: function(cancellable) { // error?
- this.inited = true;
+ VFuncImpls: {
+ init: function(cancellable) { // error?
+ this.inited = true;
+ }
}
});
[
Date Prev][
Date Next] [
Thread Prev][
Thread Next]
[
Thread Index]
[
Date Index]
[
Author Index]