[gjs] Add native JSClass for interfaces



commit 43eade6dfc4be0305377fc8f84184969dd6902ce
Author: Jasper St. Pierre <jstpierre mecheye net>
Date:   Wed Nov 30 17:39:02 2011 -0500

    Add native JSClass for interfaces
    
    This allows us to retrieve static methods and methods from the prototype.
    This also lays the foundation for when we want to implement implementing
    interfaces.
    
    https://bugzilla.gnome.org/show_bug.cgi?id=666360

 Makefile.am    |    6 +-
 gi/interface.c |  271 ++++++++++++++++++++++++++++++++++++++++++++++++++++++++
 gi/interface.h |   43 +++++++++
 gi/repo.c      |    5 +
 gjs/mem.c      |    4 +-
 gjs/mem.h      |    1 +
 6 files changed, 327 insertions(+), 3 deletions(-)
---
diff --git a/Makefile.am b/Makefile.am
index 3039137..8f21a90 100644
--- a/Makefile.am
+++ b/Makefile.am
@@ -49,7 +49,8 @@ nobase_gjs_module_include_HEADERS =	\
 	gi/closure.h	\
 	gi/enumeration.h	\
 	gi/function.h	\
-	gi/keep-alive.h	
+	gi/keep-alive.h	\
+	gi/interface.h
 
 noinst_HEADERS +=		\
 	gjs/jsapi-private.h	\
@@ -131,7 +132,8 @@ libgjs_la_SOURCES += \
 	gi/param.c	\
         gi/repo.c	\
 	gi/union.c	\
-        gi/value.c
+        gi/value.c	\
+	gi/interface.c
 
 if ENABLE_DTRACE
 gjs_gi_probes.h: gi/gjs_gi_probes.d
diff --git a/gi/interface.c b/gi/interface.c
new file mode 100644
index 0000000..983f639
--- /dev/null
+++ b/gi/interface.c
@@ -0,0 +1,271 @@
+/* -*- mode: C; c-basic-offset: 4; indent-tabs-mode: nil; -*- */
+/*
+ * Copyright (c) 2008  litl, LLC
+ * Copyright (c) 2012  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 <config.h>
+
+#include "function.h"
+#include "interface.h"
+
+#include <gjs/gjs-module.h>
+#include <gjs/compat.h>
+#include <util/log.h>
+
+#include <jsapi.h>
+#include <girepository.h>
+
+typedef struct {
+    GIInterfaceInfo *info;
+} Interface;
+
+static struct JSClass gjs_interface_class;
+
+GJS_DEFINE_DYNAMIC_PRIV_FROM_JS(Interface, gjs_interface_class)
+
+GJS_NATIVE_CONSTRUCTOR_DECLARE(interface)
+{
+    jsval obj;
+    JSObject *proto;
+    JSObject *constructor;
+    Interface *priv;
+
+    constructor = JSVAL_TO_OBJECT(JS_CALLEE(context, vp));
+    gjs_object_get_property(context, constructor, "prototype", &obj);
+    proto = JSVAL_TO_OBJECT(obj);
+    priv = priv_from_js(context, proto);
+
+    gjs_throw(context, "You cannot construct new instances of '%s.%s'",
+              g_base_info_get_namespace(priv->info),
+              g_base_info_get_name(priv->info));
+
+    return JS_TRUE;
+}
+
+static void
+interface_finalize(JSContext *context,
+                   JSObject  *obj)
+{
+    Interface *priv;
+
+    priv = priv_from_js(context, obj);
+
+    if (priv == NULL)
+        return;
+
+    if (priv->info != NULL)
+        g_base_info_unref((GIBaseInfo*)priv->info);
+
+    GJS_DEC_COUNTER(interface);
+    g_slice_free(Interface, priv);
+}
+
+static JSBool
+gjs_define_static_methods(JSContext       *context,
+                          JSObject        *constructor,
+                          GType            gtype,
+                          GIInterfaceInfo *info)
+{
+    int i;
+    int n_methods;
+
+    n_methods = g_interface_info_get_n_methods(info);
+
+    for (i = 0; i < n_methods; i++) {
+        GIFunctionInfo *meth_info;
+        GIFunctionInfoFlags flags;
+
+        meth_info = g_interface_info_get_method (info, i);
+        flags = g_function_info_get_flags (meth_info);
+
+        /* Anything that isn't a method we put on the prototype of the
+         * constructor.  This includes <constructor> introspection
+         * methods, as well as the forthcoming "static methods"
+         * support.  We may want to change this to use
+         * GI_FUNCTION_IS_CONSTRUCTOR and GI_FUNCTION_IS_STATIC or the
+         * like in the near future.
+         */
+        if (!(flags & GI_FUNCTION_IS_METHOD)) {
+            gjs_define_function(context, constructor, gtype,
+                                (GICallableInfo *)meth_info);
+        }
+
+        g_base_info_unref((GIBaseInfo*) meth_info);
+    }
+    return JS_TRUE;
+}
+
+static JSBool
+interface_new_resolve(JSContext *context,
+                      JSObject  *obj,
+                      jsid       id,
+                      uintN      flags,
+                      JSObject **objp)
+{
+    Interface *priv;
+    char *name;
+    JSBool ret = JS_FALSE;
+    GIFunctionInfo *method_info;
+
+    *objp = NULL;
+
+    if (!gjs_get_string_id(context, id, &name))
+        return JS_TRUE;
+
+    priv = priv_from_js(context, obj);
+
+    if (priv == NULL)
+        goto out;
+
+    method_info = g_interface_info_find_method((GIInterfaceInfo*) priv->info, name);
+
+    if (method_info != NULL) {
+        if (gjs_define_function(context, obj,
+                                g_registered_type_info_get_g_type(priv->info),
+                                (GICallableInfo*)method_info) == NULL) {
+            g_base_info_unref((GIBaseInfo*)method_info);
+            goto out;
+        }
+
+        *objp = obj;
+        g_base_info_unref((GIBaseInfo*)method_info);
+    }
+
+    ret = JS_TRUE;
+
+ out:
+    g_free (name);
+    return ret;
+}
+
+static struct JSClass gjs_interface_class = {
+    NULL, /* dynamic */
+    JSCLASS_HAS_PRIVATE |
+    JSCLASS_NEW_RESOLVE |
+    JSCLASS_NEW_RESOLVE_GETS_START,
+    JS_PropertyStub,
+    JS_PropertyStub,
+    JS_PropertyStub,
+    JS_StrictPropertyStub,
+    JS_EnumerateStub,
+    (JSResolveOp) interface_new_resolve,
+    JS_ConvertStub,
+    interface_finalize,
+    NULL,
+    NULL,
+    NULL,
+    NULL, NULL, NULL, NULL, NULL
+};
+
+static JSPropertySpec gjs_interface_proto_props[] = {
+    { NULL }
+};
+
+static JSFunctionSpec gjs_interface_proto_funcs[] = {
+    { NULL }
+};
+
+JSBool
+gjs_define_interface_class(JSContext       *context,
+                           JSObject        *in_object,
+                           GIInterfaceInfo *info,
+                           JSObject       **prototype_p)
+{
+    Interface *priv;
+    const char *constructor_name;
+    JSObject *constructor;
+    JSObject *prototype;
+    jsval value;
+
+    constructor_name = g_base_info_get_name((GIBaseInfo*)info);
+
+    gjs_object_get_property(context, in_object, constructor_name, &value);
+    if (value != JSVAL_VOID) {
+        JSObject *constructor;
+
+        if (!JSVAL_IS_OBJECT(value)) {
+            gjs_throw(context, "Existing property '%s' does not look like a constructor",
+                      constructor_name);
+            return JS_FALSE;
+        }
+
+        constructor = JSVAL_TO_OBJECT(value);
+
+        gjs_object_get_property(context, constructor, "prototype", &value);
+        if (!JSVAL_IS_OBJECT(value)) {
+            gjs_throw(context, "prototype property does not appear to exist or has wrong type");
+            return JS_FALSE;
+        } else {
+            if (prototype_p)
+                *prototype_p = JSVAL_TO_OBJECT(value);
+
+            return JS_TRUE;
+        }
+
+        return JS_TRUE;
+    }
+
+    prototype = gjs_init_class_dynamic(context, in_object,
+                                       /* parent prototype JSObject* for
+                                        * prototype; NULL for
+                                        * Object.prototype
+                                        */
+                                       NULL,
+                                       g_base_info_get_namespace((GIBaseInfo*)info),
+                                       constructor_name,
+                                       &gjs_interface_class,
+                                       /* constructor for instances (NULL for
+                                        * none - just name the prototype like
+                                        * Math - rarely correct)
+                                        */
+                                       gjs_interface_constructor,
+                                       /* number of constructor args */
+                                       0,
+                                       /* props of prototype */
+                                       &gjs_interface_proto_props[0],
+                                       /* funcs of prototype */
+                                       &gjs_interface_proto_funcs[0],
+                                       /* props of constructor, MyConstructor.myprop */
+                                       NULL,
+                                       /* funcs of constructor, MyConstructor.myfunc() */
+                                       NULL);
+    if (prototype == NULL)
+        gjs_fatal("Can't init class %s", constructor_name);
+
+    g_assert(gjs_object_has_property(context, in_object, constructor_name));
+
+    priv = g_slice_new0(Interface);
+    priv->info = 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);
+
+    if (prototype_p)
+        *prototype_p = prototype;
+
+    return JS_TRUE;
+}
diff --git a/gi/interface.h b/gi/interface.h
new file mode 100644
index 0000000..db49647
--- /dev/null
+++ b/gi/interface.h
@@ -0,0 +1,43 @@
+/* -*- mode: C; c-basic-offset: 4; indent-tabs-mode: nil; -*- */
+/*
+ * Copyright (c) 2008  litl, LLC
+ * Copyright (c) 2012  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_INTERFACE_H__
+#define __GJS_INTERFACE_H__
+
+#include <glib.h>
+
+#include <jsapi.h>
+
+#include <girepository.h>
+
+G_BEGIN_DECLS
+
+JSBool gjs_define_interface_class (JSContext       *context,
+                                   JSObject        *in_object,
+                                   GIInterfaceInfo *info,
+                                   JSObject       **prototype_p);
+
+G_END_DECLS
+
+#endif  /* __GJS_INTERFACE_H__ */
diff --git a/gi/repo.c b/gi/repo.c
index b7637b8..be8a615 100644
--- a/gi/repo.c
+++ b/gi/repo.c
@@ -32,6 +32,7 @@
 #include "enumeration.h"
 #include "arg.h"
 #include "foreign.h"
+#include "interface.h"
 
 #include <gjs/compat.h>
 
@@ -495,6 +496,10 @@ gjs_define_info(JSContext  *context,
         if (!gjs_define_constant(context, in_object, (GIConstantInfo*) info))
             return JS_FALSE;
         break;
+    case GI_INFO_TYPE_INTERFACE:
+        if (!gjs_define_interface_class(context, in_object, (GIInterfaceInfo*) info, NULL))
+            return JS_FALSE;
+        break;
     default:
         gjs_throw(context, "API of type %s not implemented, cannot define %s.%s",
                   gjs_info_type_name(g_base_info_get_type(info)),
diff --git a/gjs/mem.c b/gjs/mem.c
index 5e5ff01..6122a55 100644
--- a/gjs/mem.c
+++ b/gjs/mem.c
@@ -47,6 +47,7 @@ GJS_DEFINE_COUNTER(param)
 GJS_DEFINE_COUNTER(repo)
 GJS_DEFINE_COUNTER(resultset)
 GJS_DEFINE_COUNTER(weakhash)
+GJS_DEFINE_COUNTER(interface)
 
 #define GJS_LIST_COUNTER(name) \
     & gjs_counter_ ## name
@@ -63,7 +64,8 @@ static GjsMemCounter* counters[] = {
     GJS_LIST_COUNTER(param),
     GJS_LIST_COUNTER(repo),
     GJS_LIST_COUNTER(resultset),
-    GJS_LIST_COUNTER(weakhash)
+    GJS_LIST_COUNTER(weakhash),
+    GJS_LIST_COUNTER(interface)
 };
 
 void
diff --git a/gjs/mem.h b/gjs/mem.h
index c766cd2..3988b4f 100644
--- a/gjs/mem.h
+++ b/gjs/mem.h
@@ -55,6 +55,7 @@ GJS_DECLARE_COUNTER(param)
 GJS_DECLARE_COUNTER(repo)
 GJS_DECLARE_COUNTER(resultset)
 GJS_DECLARE_COUNTER(weakhash)
+GJS_DECLARE_COUNTER(interface)
 
 #define GJS_INC_COUNTER(name)                \
     do {                                        \



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