[gjs/wip/gobj-kitchen-sink: 6/20] Expose parent vfuncs on the parent prototype, prefixed with "vfunc_"
- From: Jasper St. Pierre <jstpierre src gnome org>
- To: commits-list gnome org
- Cc:
- Subject: [gjs/wip/gobj-kitchen-sink: 6/20] Expose parent vfuncs on the parent prototype, prefixed with "vfunc_"
- Date: Fri, 3 Feb 2012 16:54:10 +0000 (UTC)
commit 24043b02d81f1ebf0de5af29fa1320d24dbb4d87
Author: Jasper St. Pierre <jstpierre mecheye net>
Date: Mon Nov 28 13:55:52 2011 -0500
Expose parent vfuncs on the parent prototype, prefixed with "vfunc_"
As an example, the implementation of ClutterActor::get_preferred_width
will be exposed as Clutter.Actor.prototype.vfunc_get_preferred_width.
We need this so that GObject subclasses can chain up when implementing
their own vfunc overrides.
The main big change here is that we now require the GType when using
gjs_define_function. This is because the implementation's GType may
not be the GICallableInfo's owner: ClutterRectangle has its own
get_preferred_width implementation, but since the vfunc isn't declared
in ClutterRectangleClass, introspection for ClutterRectangle doesn't
have a GIVFuncInfo. If a user created a ClutterRectangle subclass, in
order to chain up properly we need to expose
Clutter.Rectangle.prototype.vfunc_get_preferred_width.
https://bugzilla.gnome.org/show_bug.cgi?id=663492
gi/boxed.c | 12 ++++-
gi/function.c | 123 +++++++++++++++++++++++++++++++++++++++++++++----------
gi/function.h | 8 +++-
gi/interface.c | 9 ++--
gi/object.c | 98 +++++++++++++++++++++++++++++++++++++++++++-
gi/repo.c | 2 +-
gi/union.c | 4 +-
7 files changed, 219 insertions(+), 37 deletions(-)
---
diff --git a/gi/boxed.c b/gi/boxed.c
index ef80395..99b778d 100644
--- a/gi/boxed.c
+++ b/gi/boxed.c
@@ -62,6 +62,7 @@ GJS_DEFINE_DYNAMIC_PRIV_FROM_JS(Boxed, gjs_boxed_class)
static JSBool
gjs_define_static_methods(JSContext *context,
JSObject *constructor,
+ GType gtype,
GIStructInfo *boxed_info)
{
int i;
@@ -84,7 +85,8 @@ gjs_define_static_methods(JSContext *context,
* like in the near future.
*/
if (!(flags & GI_FUNCTION_IS_METHOD)) {
- gjs_define_function(context, constructor, meth_info);
+ gjs_define_function(context, constructor, gtype,
+ (GICallableInfo *)meth_info);
}
g_base_info_unref((GIBaseInfo*) meth_info);
@@ -153,7 +155,9 @@ boxed_new_resolve(JSContext *context,
boxed_proto = obj;
- if (gjs_define_function(context, boxed_proto, method_info) == NULL) {
+ if (gjs_define_function(context, boxed_proto,
+ g_registered_type_info_get_g_type (priv->info),
+ (GICallableInfo *)method_info) == NULL) {
g_base_info_unref( (GIBaseInfo*) method_info);
goto out;
}
@@ -1175,7 +1179,9 @@ gjs_define_boxed_class(JSContext *context,
}
constructor = JSVAL_TO_OBJECT(value);
- gjs_define_static_methods (context, constructor, info);
+ gjs_define_static_methods (context, constructor,
+ g_registered_type_info_get_g_type (priv->info),
+ priv->info);
if (constructor_p)
*constructor_p = constructor;
diff --git a/gi/function.c b/gi/function.c
index 4ce4780..8b481aa 100644
--- a/gi/function.c
+++ b/gi/function.c
@@ -129,7 +129,8 @@ gjs_callback_trampoline_unref(GjsCallbackTrampoline *trampoline)
context = gjs_runtime_get_current_context(trampoline->runtime);
- JS_RemoveValueRoot(context, &trampoline->js_function);
+ if (!trampoline->is_vfunc)
+ JS_RemoveValueRoot(context, &trampoline->js_function);
g_callable_info_free_closure(trampoline->info, trampoline->closure);
g_base_info_unref( (GIBaseInfo*) trampoline->info);
g_slice_free(GjsCallbackTrampoline, trampoline);
@@ -210,6 +211,7 @@ gjs_callback_closure(ffi_cif *cif,
GjsCallbackTrampoline *trampoline;
int i, n_args, n_jsargs, n_outargs;
jsval *jsargs, rval;
+ JSObject *this_object;
GITypeInfo ret_type;
gboolean success = FALSE;
gboolean ret_type_is_void;
@@ -252,8 +254,13 @@ gjs_callback_closure(ffi_cif *cif,
goto out;
}
+ if (trampoline->is_vfunc)
+ this_object = JSVAL_TO_OBJECT(jsargs[0]);
+ else
+ this_object = NULL;
+
if (!JS_CallFunctionValue(context,
- NULL,
+ this_object,
trampoline->js_function,
n_jsargs,
jsargs,
@@ -393,7 +400,8 @@ GjsCallbackTrampoline*
gjs_callback_trampoline_new(JSContext *context,
jsval function,
GICallableInfo *callable_info,
- GIScopeType scope)
+ GIScopeType scope,
+ gboolean is_vfunc)
{
GjsCallbackTrampoline *trampoline;
@@ -409,11 +417,14 @@ gjs_callback_trampoline_new(JSContext *context,
trampoline->info = callable_info;
g_base_info_ref((GIBaseInfo*)trampoline->info);
trampoline->js_function = function;
- JS_AddValueRoot(context, &trampoline->js_function);
+
+ if (!is_vfunc)
+ JS_AddValueRoot(context, &trampoline->js_function);
trampoline->closure = g_callable_info_prepare_closure(callable_info, &trampoline->cif,
gjs_callback_closure, trampoline);
trampoline->scope = scope;
+ trampoline->is_vfunc = is_vfunc;
return trampoline;
}
@@ -561,10 +572,10 @@ gjs_invoke_c_function(JSContext *context,
GError *local_error = NULL;
gboolean failed, postinvoke_release_failed;
- GIFunctionInfoFlags flags;
gboolean is_method;
GITypeInfo return_info;
GITypeTag return_tag;
+ GIInfoType info_type;
jsval *return_values = NULL;
guint8 next_rval = 0; /* index into return_values */
GSList *iter;
@@ -582,9 +593,26 @@ gjs_invoke_c_function(JSContext *context,
completed_trampolines = NULL;
}
- flags = g_function_info_get_flags(function->info);
- is_method = (flags & GI_FUNCTION_IS_METHOD) != 0;
- can_throw_gerror = (flags & GI_FUNCTION_THROWS) != 0;
+ info_type = g_base_info_get_type((GIBaseInfo *)function->info);
+
+ switch (info_type) {
+ case GI_INFO_TYPE_FUNCTION:
+ {
+ GIFunctionInfoFlags flags;
+ flags = g_function_info_get_flags((GIFunctionInfo *)function->info);
+ is_method = (flags & GI_FUNCTION_IS_METHOD) != 0;
+ can_throw_gerror = (flags & GI_FUNCTION_THROWS) != 0;
+ }
+ break;
+ case GI_INFO_TYPE_CALLBACK:
+ case GI_INFO_TYPE_VFUNC:
+ is_method = TRUE;
+ can_throw_gerror = FALSE;
+ break;
+ default:
+ g_assert_not_reached();
+ }
+
c_argc = function->invoker.cif.nargs;
gi_argc = g_callable_info_get_n_args( (GICallableInfo*) function->info);
@@ -749,7 +777,8 @@ gjs_invoke_c_function(JSContext *context,
trampoline = gjs_callback_trampoline_new(context,
value,
callable_info,
- scope);
+ scope,
+ FALSE);
closure = trampoline->closure;
g_base_info_unref(callable_info);
}
@@ -1271,10 +1300,14 @@ function_to_string (JSContext *context,
if (priv == NULL) {
string = "function () {\n}";
free = FALSE;
- } else {
+ } else if (g_base_info_get_type(priv->info) == GI_INFO_TYPE_FUNCTION) {
string = g_strdup_printf("function %s(){\n\t/* proxy for native symbol %s(); */\n}",
g_base_info_get_name ((GIBaseInfo *) priv->info),
- g_function_info_get_symbol (priv->info));
+ g_function_info_get_symbol ((GIFunctionInfo *) priv->info));
+ free = TRUE;
+ } else {
+ string = g_strdup_printf("function %s(){\n\t/* proxy for native symbol */\n}",
+ g_base_info_get_name ((GIBaseInfo *) priv->info));
free = TRUE;
}
@@ -1330,15 +1363,40 @@ static JSFunctionSpec gjs_function_proto_funcs[] = {
static gboolean
init_cached_function_data (JSContext *context,
Function *function,
- GIFunctionInfo *info)
+ GType gtype,
+ GICallableInfo *info)
{
guint8 i, n_args, array_length_pos;
GError *error = NULL;
GITypeInfo return_type;
+ GIInfoType info_type;
- if (!g_function_info_prep_invoker(info, &(function->invoker), &error)) {
- gjs_throw_g_error(context, error);
- return FALSE;
+ info_type = g_base_info_get_type((GIBaseInfo *)info);
+
+ if (info_type == GI_INFO_TYPE_FUNCTION) {
+ if (!g_function_info_prep_invoker((GIFunctionInfo *)info,
+ &(function->invoker),
+ &error)) {
+ gjs_throw_g_error(context, error);
+ return FALSE;
+ }
+ } else if (info_type == GI_INFO_TYPE_VFUNC) {
+ gpointer addr;
+
+ addr = g_vfunc_info_get_address((GIVFuncInfo *)info, gtype, &error);
+ if (error != NULL) {
+ if (error->code != G_INVOKE_ERROR_SYMBOL_NOT_FOUND)
+ gjs_throw_g_error(context, error);
+
+ return FALSE;
+ }
+
+ if (!g_function_invoker_new_for_address(addr, info,
+ &(function->invoker),
+ &error)) {
+ gjs_throw_g_error(context, error);
+ return FALSE;
+ }
}
g_callable_info_load_return_type((GICallableInfo*)info, &return_type);
@@ -1448,7 +1506,8 @@ init_cached_function_data (JSContext *context,
static JSObject*
function_new(JSContext *context,
- GIFunctionInfo *info)
+ GType gtype,
+ GICallableInfo *info)
{
JSObject *function;
JSObject *global;
@@ -1504,7 +1563,7 @@ function_new(JSContext *context,
}
priv = priv_from_js(context, function);
- if (!init_cached_function_data(context, priv, info))
+ if (!init_cached_function_data(context, priv, gtype, (GICallableInfo *)info))
return NULL;
return function;
@@ -1513,13 +1572,19 @@ function_new(JSContext *context,
JSObject*
gjs_define_function(JSContext *context,
JSObject *in_object,
- GIFunctionInfo *info)
+ GType gtype,
+ GICallableInfo *info)
{
- JSObject *function;
+ JSObject *function = NULL;
+ GIInfoType info_type;
+ gchar *name;
+ gboolean free_name;
+
+ info_type = g_base_info_get_type((GIBaseInfo *)info);
JS_BeginRequest(context);
- function = function_new(context, info);
+ function = function_new(context, gtype, info);
if (function == NULL) {
gjs_move_exception(context, context);
@@ -1527,8 +1592,17 @@ gjs_define_function(JSContext *context,
return NULL;
}
- if (!JS_DefineProperty(context, in_object,
- g_base_info_get_name( (GIBaseInfo*) info),
+ 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("vfunc_%s", g_base_info_get_name((GIBaseInfo*) info));
+ free_name = TRUE;
+ } else {
+ g_assert_not_reached ();
+ }
+
+ if (!JS_DefineProperty(context, in_object, name,
OBJECT_TO_JSVAL(function),
NULL, NULL,
GJS_MODULE_PROP_FLAGS)) {
@@ -1538,6 +1612,9 @@ gjs_define_function(JSContext *context,
return NULL;
}
+ if (free_name)
+ g_free(name);
+
JS_EndRequest(context);
return function;
}
@@ -1555,7 +1632,7 @@ gjs_invoke_c_function_uncached (JSContext *context,
JSBool result;
memset (&function, 0, sizeof (Function));
- if (!init_cached_function_data (context, &function, info))
+ if (!init_cached_function_data (context, &function, 0, info))
return JS_FALSE;
result = gjs_invoke_c_function (context, &function, obj, argc, argv, rval);
diff --git a/gi/function.h b/gi/function.h
index 555def6..875144e 100644
--- a/gi/function.h
+++ b/gi/function.h
@@ -41,19 +41,23 @@ typedef struct {
ffi_cif cif;
ffi_closure *closure;
GIScopeType scope;
+ gboolean is_vfunc;
} GjsCallbackTrampoline;
GjsCallbackTrampoline* gjs_callback_trampoline_new(JSContext *context,
jsval function,
GICallableInfo *callable_info,
- GIScopeType scope);
+ GIScopeType scope,
+ gboolean is_vfunc);
void gjs_callback_trampoline_unref(GjsCallbackTrampoline *trampoline);
void gjs_callback_trampoline_ref(GjsCallbackTrampoline *trampoline);
JSObject* gjs_define_function (JSContext *context,
JSObject *in_object,
- GIFunctionInfo *info);
+ GType gtype,
+ GICallableInfo *info);
+
JSBool gjs_invoke_c_function_uncached (JSContext *context,
GIFunctionInfo *info,
JSObject *obj,
diff --git a/gi/interface.c b/gi/interface.c
index 87ad7b2..245e037 100644
--- a/gi/interface.c
+++ b/gi/interface.c
@@ -36,6 +36,7 @@
typedef struct {
GIInterfaceInfo *info;
+ GType gtype;
} Interface;
static struct JSClass gjs_interface_class;
@@ -105,7 +106,7 @@ gjs_define_static_methods(JSContext *context,
* like in the near future.
*/
if (!(flags & GI_FUNCTION_IS_METHOD)) {
- gjs_define_function(context, constructor,
+ gjs_define_function(context, constructor, gtype,
(GICallableInfo *)meth_info);
}
@@ -140,6 +141,7 @@ interface_new_resolve(JSContext *context,
if (method_info != NULL) {
if (gjs_define_function(context, obj,
+ priv->gtype,
(GICallableInfo*)method_info) == NULL) {
g_base_info_unref((GIBaseInfo*)method_info);
goto out;
@@ -254,14 +256,13 @@ gjs_define_interface_class(JSContext *context,
priv = g_slice_new0(Interface);
priv->info = info;
+ priv->gtype = g_registered_type_info_get_g_type(priv->info);
g_base_info_ref((GIBaseInfo*)priv->info);
JS_SetPrivate(context, prototype, priv);
gjs_object_get_property(context, in_object, constructor_name, &value);
constructor = JSVAL_TO_OBJECT(value);
- gjs_define_static_methods(context, constructor,
- g_registered_type_info_get_g_type(priv->info),
- priv->info);
+ gjs_define_static_methods(context, constructor, priv->gtype, priv->info);
if (prototype_p)
*prototype_p = prototype;
diff --git a/gi/object.c b/gi/object.c
index c74cbb2..d19be58 100644
--- a/gi/object.c
+++ b/gi/object.c
@@ -233,6 +233,57 @@ 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;
+ vfunc = g_object_info_find_vfunc(parent, name);
+ 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;
+}
+
/*
* Like JSResolveOp, but flags provide contextual information as follows:
*
@@ -283,6 +334,45 @@ object_instance_new_resolve(JSContext *context,
goto out;
}
+ if (g_str_has_prefix (name, "vfunc_")) {
+ /* 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_vfunc_ = &name[6];
+ GIVFuncInfo *vfunc;
+
+ vfunc = find_vfunc_on_parent(priv->info, name_without_vfunc_);
+ if (vfunc != NULL) {
+ /* 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;
+ }
+
+ /* If the vfunc wasn't found, fall through, back to normal
+ * method resolution. */
+ }
+
/* find_method does not look at methods on parent classes,
* we rely on javascript to walk up the __proto__ chain
* and find those and define them in the right prototype.
@@ -355,7 +445,7 @@ object_instance_new_resolve(JSContext *context,
g_base_info_get_namespace( (GIBaseInfo*) priv->info),
g_base_info_get_name( (GIBaseInfo*) priv->info));
- if (gjs_define_function(context, obj, method_info) == NULL) {
+ if (gjs_define_function(context, obj, priv->gtype, method_info) == NULL) {
g_base_info_unref( (GIBaseInfo*) method_info);
goto out;
}
@@ -1127,6 +1217,7 @@ static JSFunctionSpec gjs_object_instance_proto_funcs[] = {
static JSBool
gjs_define_static_methods(JSContext *context,
JSObject *constructor,
+ GType gtype,
GIObjectInfo *object_info)
{
int i;
@@ -1149,7 +1240,8 @@ gjs_define_static_methods(JSContext *context,
* like in the near future.
*/
if (!(flags & GI_FUNCTION_IS_METHOD)) {
- gjs_define_function(context, constructor, meth_info);
+ gjs_define_function(context, constructor, gtype,
+ (GICallableInfo *)meth_info);
}
g_base_info_unref((GIBaseInfo*) meth_info);
@@ -1367,7 +1459,7 @@ gjs_define_object_class(JSContext *context,
}
constructor = JSVAL_TO_OBJECT(value);
- gjs_define_static_methods(context, constructor, info);
+ gjs_define_static_methods(context, constructor, gtype, info);
}
value = OBJECT_TO_JSVAL(gjs_gtype_create_gtype_wrapper(context, gtype));
diff --git a/gi/repo.c b/gi/repo.c
index 08a6d4a..fa6a585 100644
--- a/gi/repo.c
+++ b/gi/repo.c
@@ -460,7 +460,7 @@ gjs_define_info(JSContext *context,
case GI_INFO_TYPE_FUNCTION:
{
JSObject *f;
- f = gjs_define_function(context, in_object, (GIFunctionInfo*) info);
+ f = gjs_define_function(context, in_object, 0, (GICallableInfo*) info);
if (f == NULL)
return JS_FALSE;
}
diff --git a/gi/union.c b/gi/union.c
index ac921b2..ec02af0 100644
--- a/gi/union.c
+++ b/gi/union.c
@@ -112,7 +112,9 @@ union_new_resolve(JSContext *context,
union_proto = obj;
- if (gjs_define_function(context, union_proto, method_info) == NULL) {
+ if (gjs_define_function(context, union_proto,
+ g_registered_type_info_get_g_type(priv->info),
+ method_info) == NULL) {
g_base_info_unref( (GIBaseInfo*) method_info);
ret = JS_FALSE;
goto out;
[
Date Prev][
Date Next] [
Thread Prev][
Thread Next]
[
Thread Index]
[
Date Index]
[
Author Index]