[gjs] Implement GTypeClass and GTypeInterface arguments



commit f3c9b6e33b9d316349911a881a5f7ff5d4c102c4
Author: Giovanni Campagna <gcampagna src gnome org>
Date:   Mon May 13 21:49:10 2013 +0200

    Implement GTypeClass and GTypeInterface arguments
    
    It is common in the GObject world to have non trivial functionality
    exposed as GObjectClass/GTypeClass methods, to be used from
    class_init. One example is the GtkWidget template system.
    This commit implements marshalling of GType structures as
    JS classes (constructors), going through the associated GType.
    
    Note that this commit breaks the testGObjectClass.js test
    
    https://bugzilla.gnome.org/show_bug.cgi?id=700347

 Makefile-insttest.am                 |    1 +
 gi/arg.cpp                           |   49 ++++++++++++++++++++++++++++++++-
 gi/function.cpp                      |   25 +++++++++++++++++
 gi/interface.cpp                     |   30 ++++++++++++++++++++
 gi/interface.h                       |    4 +++
 gi/object.cpp                        |   48 +++++++++++++++++++++++++++++++--
 gi/object.h                          |    3 ++
 gi/repo.cpp                          |   18 +++++++++++-
 gi/repo.h                            |    2 +
 installed-tests/js/testGTypeClass.js |   27 ++++++++++++++++++
 10 files changed, 200 insertions(+), 7 deletions(-)
---
diff --git a/Makefile-insttest.am b/Makefile-insttest.am
index 59d76e9..ca69021 100644
--- a/Makefile-insttest.am
+++ b/Makefile-insttest.am
@@ -117,6 +117,7 @@ dist_jstests_DATA += \
        installed-tests/js/testFundamental.js                   \
        installed-tests/js/testGIMarshalling.js         \
        installed-tests/js/testGObjectClass.js          \
+       installed-tests/js/testGTypeClass.js            \
        installed-tests/js/testJS1_8.js                 \
        installed-tests/js/testLang.js                  \
        installed-tests/js/testLocale.js                        \
diff --git a/gi/arg.cpp b/gi/arg.cpp
index 1da4fe6..6fbcaa5 100644
--- a/gi/arg.cpp
+++ b/gi/arg.cpp
@@ -26,6 +26,7 @@
 #include "arg.h"
 #include "gtype.h"
 #include "object.h"
+#include "interface.h"
 #include "foreign.h"
 #include "fundamental.h"
 #include "boxed.h"
@@ -1417,8 +1418,33 @@ gjs_value_to_g_argument(JSContext      *context,
             } else if (JSVAL_IS_NULL(value)) {
                 arg->v_pointer = NULL;
             } else if (JSVAL_IS_OBJECT(value)) {
-                /* Handle Struct/Union first since we don't necessarily need a GType for them */
-                if ((interface_type == GI_INFO_TYPE_STRUCT || interface_type == GI_INFO_TYPE_BOXED) &&
+                if (interface_type == GI_INFO_TYPE_STRUCT &&
+                    g_struct_info_is_gtype_struct((GIStructInfo*)interface_info)) {
+                    GType gtype;
+                    gpointer klass;
+
+                    gtype = gjs_gtype_get_actual_gtype(context, JSVAL_TO_OBJECT(value));
+
+                    if (gtype == G_TYPE_NONE) {
+                        wrong = TRUE;
+                        report_type_mismatch = TRUE;
+                        break;
+                    }
+
+                    /* We use peek here to simplify reference counting (we just ignore
+                       transfer annotation, as GType classes are never really freed)
+                       We know that the GType class is referenced at least once when
+                       the JS constructor is initialized.
+                    */
+
+                    if (g_type_is_a(gtype, G_TYPE_INTERFACE))
+                        klass = g_type_default_interface_peek(gtype);
+                    else
+                        klass = g_type_class_peek(gtype);
+
+                    arg->v_pointer = klass;
+                } else if ((interface_type == GI_INFO_TYPE_STRUCT || interface_type == GI_INFO_TYPE_BOXED) &&
+                    /* Handle Struct/Union first since we don't necessarily need a GType for them */
                     /* We special case Closures later, so skip them here */
                     !g_type_is_a(gtype, G_TYPE_CLOSURE)) {
                     JSObject *obj = JSVAL_TO_OBJECT(value);
@@ -2597,6 +2623,25 @@ gjs_value_from_g_argument (JSContext  *context,
                 goto out;
             }
 
+            if (interface_type == GI_INFO_TYPE_STRUCT &&
+                g_struct_info_is_gtype_struct((GIStructInfo*)interface_info)) {
+                JSBool ret;
+
+                /* XXX: here we make the implicit assumption that GTypeClass is the same
+                   as GTypeInterface. This is true for the GType field, which is what we
+                   use, but not for the rest of the structure!
+                */
+                gtype = G_TYPE_FROM_CLASS(arg->v_pointer);
+
+                if (g_type_is_a(gtype, G_TYPE_INTERFACE))
+                    ret = gjs_lookup_interface_constructor(context, gtype, value_p);
+                else
+                    ret = gjs_lookup_object_constructor(context, gtype, value_p);
+
+                g_base_info_unref(interface_info);
+                return ret;
+            }
+
             gtype = g_registered_type_info_get_g_type((GIRegisteredTypeInfo*)interface_info);
             if (G_TYPE_IS_INSTANTIATABLE(gtype) ||
                 G_TYPE_IS_INTERFACE(gtype))
diff --git a/gi/function.cpp b/gi/function.cpp
index 6b55273..7994d04 100644
--- a/gi/function.cpp
+++ b/gi/function.cpp
@@ -556,6 +556,31 @@ gjs_fill_method_instance (JSContext  *context,
             out_arg->v_pointer = gjs_gerror_from_error(context, obj);
             if (transfer == GI_TRANSFER_EVERYTHING)
                 out_arg->v_pointer = g_error_copy ((GError*) out_arg->v_pointer);
+        } else if (type == GI_INFO_TYPE_STRUCT &&
+                   g_struct_info_is_gtype_struct((GIStructInfo*) container)) {
+            /* And so do GType structures */
+            GType gtype;
+            gpointer klass;
+
+            gtype = gjs_gtype_get_actual_gtype(context, obj);
+
+            if (gtype == G_TYPE_NONE) {
+                gjs_throw(context, "Invalid GType class passed for instance parameter");
+                return JS_FALSE;
+            }
+
+            /* We use peek here to simplify reference counting (we just ignore
+               transfer annotation, as GType classes are never really freed)
+               We know that the GType class is referenced at least once when
+               the JS constructor is initialized.
+            */
+
+            if (g_type_is_a(gtype, G_TYPE_INTERFACE))
+                klass = g_type_default_interface_peek(gtype);
+            else
+                klass = g_type_class_peek(gtype);
+
+            out_arg->v_pointer = klass;
         } else {
             if (!gjs_typecheck_boxed(context, obj,
                                      container, gtype,
diff --git a/gi/interface.cpp b/gi/interface.cpp
index e13cf3a..96c7230 100644
--- a/gi/interface.cpp
+++ b/gi/interface.cpp
@@ -27,6 +27,7 @@
 #include "function.h"
 #include "gtype.h"
 #include "interface.h"
+#include "repo.h"
 
 #include <gjs/gjs-module.h>
 #include <gjs/compat.h>
@@ -215,3 +216,32 @@ gjs_define_interface_class(JSContext       *context,
 
     return JS_TRUE;
 }
+
+JSBool
+gjs_lookup_interface_constructor(JSContext *context,
+                                 GType      gtype,
+                                 jsval     *value_p)
+{
+    JSObject *constructor;
+    GIBaseInfo *interface_info;
+
+    interface_info = g_irepository_find_by_gtype(NULL, gtype);
+
+    if (interface_info == NULL) {
+        gjs_throw(context, "Cannot expose non introspectable interface %s",
+                  g_type_name(gtype));
+        return JS_FALSE;
+    }
+
+    g_assert(g_base_info_get_type(interface_info) ==
+             GI_INFO_TYPE_INTERFACE);
+
+    constructor = gjs_lookup_generic_constructor(context, interface_info);
+    if (G_UNLIKELY (constructor == NULL))
+        return JS_FALSE;
+
+    g_base_info_unref(interface_info);
+
+    *value_p = OBJECT_TO_JSVAL(constructor);
+    return JS_TRUE;
+}
diff --git a/gi/interface.h b/gi/interface.h
index e926285..cdb41ab 100644
--- a/gi/interface.h
+++ b/gi/interface.h
@@ -35,6 +35,10 @@ JSBool gjs_define_interface_class (JSContext       *context,
                                    JSObject        *in_object,
                                    GIInterfaceInfo *info);
 
+JSBool gjs_lookup_interface_constructor (JSContext     *context,
+                                         GType          gtype,
+                                         jsval         *value_p);
+
 G_END_DECLS
 
 #endif  /* __GJS_INTERFACE_H__ */
diff --git a/gi/object.cpp b/gi/object.cpp
index 1fb4de5..4cffc40 100644
--- a/gi/object.cpp
+++ b/gi/object.cpp
@@ -1460,9 +1460,9 @@ object_instance_finalize(JSFreeOp  *fop,
 }
 
 static JSObject *
-gjs_lookup_object_prototype_from_info(JSContext    *context,
-                                      GIObjectInfo *info,
-                                      GType         gtype)
+gjs_lookup_object_constructor_from_info(JSContext    *context,
+                                        GIObjectInfo *info,
+                                        GType         gtype)
 {
     JSObject *in_object;
     JSObject *constructor;
@@ -1497,6 +1497,22 @@ gjs_lookup_object_prototype_from_info(JSContext    *context,
 
     g_assert(constructor != NULL);
 
+    return constructor;
+}
+
+static JSObject *
+gjs_lookup_object_prototype_from_info(JSContext    *context,
+                                      GIObjectInfo *info,
+                                      GType         gtype)
+{
+    JSObject *constructor;
+    jsval value;
+
+    constructor = gjs_lookup_object_constructor_from_info(context, info, gtype);
+
+    if (G_UNLIKELY (constructor == NULL))
+        return NULL;
+
     if (!gjs_object_get_property_const(context, constructor,
                                        GJS_STRING_PROTOTYPE, &value))
         return NULL;
@@ -2743,3 +2759,29 @@ gjs_define_private_gi_stuff(JSContext *context,
 
     return JS_TRUE;
 }
+
+JSBool
+gjs_lookup_object_constructor(JSContext *context,
+                              GType      gtype,
+                              jsval     *value_p)
+{
+    JSObject *constructor;
+    GIObjectInfo *object_info;
+
+    object_info = (GIObjectInfo*)g_irepository_find_by_gtype(NULL, gtype);
+
+    g_assert(object_info == NULL ||
+             g_base_info_get_type((GIBaseInfo*)object_info) ==
+             GI_INFO_TYPE_OBJECT);
+
+    constructor = gjs_lookup_object_constructor_from_info(context, object_info, gtype);
+
+    if (G_UNLIKELY (constructor == NULL))
+        return JS_FALSE;
+
+    if (object_info)
+        g_base_info_unref((GIBaseInfo*)object_info);
+
+    *value_p = OBJECT_TO_JSVAL(constructor);
+    return JS_TRUE;
+}
diff --git a/gi/object.h b/gi/object.h
index b6579cb..c54b2a3 100644
--- a/gi/object.h
+++ b/gi/object.h
@@ -35,6 +35,9 @@ void      gjs_define_object_class       (JSContext     *context,
                                          GIObjectInfo  *info,
                                          GType          gtype,
                                          JSObject     **constructor_p);
+JSBool    gjs_lookup_object_constructor (JSContext     *context,
+                                         GType          gtype,
+                                         jsval         *value_p);
 JSObject* gjs_object_from_g_object      (JSContext     *context,
                                          GObject       *gobj);
 GObject*  gjs_g_object_from_object      (JSContext     *context,
diff --git a/gi/repo.cpp b/gi/repo.cpp
index 54a84d6..2516fc8 100644
--- a/gi/repo.cpp
+++ b/gi/repo.cpp
@@ -749,8 +749,8 @@ gjs_hyphen_from_camel(const char *camel_name)
 }
 
 JSObject *
-gjs_lookup_generic_prototype(JSContext  *context,
-                             GIBaseInfo *info)
+gjs_lookup_generic_constructor(JSContext  *context,
+                               GIBaseInfo *info)
 {
     JSObject *in_object;
     JSObject *constructor;
@@ -772,6 +772,20 @@ gjs_lookup_generic_prototype(JSContext  *context,
     constructor = JSVAL_TO_OBJECT(value);
     g_assert(constructor != NULL);
 
+    return constructor;
+}
+
+JSObject *
+gjs_lookup_generic_prototype(JSContext  *context,
+                             GIBaseInfo *info)
+{
+    JSObject *constructor;
+    jsval value;
+
+    constructor = gjs_lookup_generic_constructor(context, info);
+    if (G_UNLIKELY (constructor == NULL))
+        return NULL;
+
     if (!gjs_object_get_property_const(context, constructor,
                                        GJS_STRING_PROTOTYPE, &value))
         return NULL;
diff --git a/gi/repo.h b/gi/repo.h
index 60d5be4..918d528 100644
--- a/gi/repo.h
+++ b/gi/repo.h
@@ -43,6 +43,8 @@ JSObject*   gjs_lookup_namespace_object_by_name (JSContext      *context,
                                                  jsid            name);
 JSObject*   gjs_lookup_function_object          (JSContext      *context,
                                                  GIFunctionInfo *info);
+JSObject *  gjs_lookup_generic_constructor      (JSContext      *context,
+                                                 GIBaseInfo     *info);
 JSObject *  gjs_lookup_generic_prototype        (JSContext      *context,
                                                  GIBaseInfo     *info);
 JSBool      gjs_define_info                     (JSContext      *context,
diff --git a/installed-tests/js/testGTypeClass.js b/installed-tests/js/testGTypeClass.js
new file mode 100644
index 0000000..0db60e0
--- /dev/null
+++ b/installed-tests/js/testGTypeClass.js
@@ -0,0 +1,27 @@
+// application/javascript;version=1.8
+
+const JSUnit = imports.jsUnit;
+const Everything = imports.gi.Regress;
+const WarnLib = imports.gi.WarnLib;
+
+// We use Gio to have some objects that we know exist
+const GLib = imports.gi.GLib;
+const Gio = imports.gi.Gio;
+const GObject = imports.gi.GObject;
+
+function testGObjectClass() {
+    let find_property = GObject.ObjectClass.prototype.find_property;
+
+    let p1 = find_property.call(Gio.ThemedIcon, 'name');
+
+    JSUnit.assert(p1 instanceof GObject.ParamSpec);
+    JSUnit.assertEquals('name', p1.name);
+
+    let p2 = find_property.call(Gio.SimpleAction, 'enabled');
+
+    JSUnit.assert(p2 instanceof GObject.ParamSpec);
+    JSUnit.assertEquals('enabled', p2.name);
+    JSUnit.assertEquals(true, p2.default_value);
+}
+
+JSUnit.gjstestRun(this, JSUnit.setUp, JSUnit.tearDown);


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