[libpeas] Implemented GJS plugin loader
- From: Steve Frécinaux <sfre src gnome org>
- To: commits-list gnome org
- Cc:
- Subject: [libpeas] Implemented GJS plugin loader
- Date: Mon, 4 Apr 2011 21:21:56 +0000 (UTC)
commit 1dfe5804501b8e785e4a58e5d180a02ca7a9d531
Author: Garrett Regier <alias301 gmail com>
Date: Mon Mar 21 07:29:13 2011 -0700
Implemented GJS plugin loader
.gitignore | 1 +
configure.ac | 41 ++
loaders/Makefile.am | 4 +
loaders/gjs/Makefile.am | 21 +
loaders/gjs/peas-extension-gjs.c | 403 ++++++++++++++++++++
loaders/gjs/peas-extension-gjs.h | 59 +++
loaders/gjs/peas-plugin-loader-gjs.c | 307 +++++++++++++++
loaders/gjs/peas-plugin-loader-gjs.h | 55 +++
peas-demo/peas-demo.c | 1 +
peas-demo/plugins/Makefile.am | 4 +
peas-demo/plugins/gjshello/Makefile.am | 7 +
peas-demo/plugins/gjshello/gjshello.js | 33 ++
peas-demo/plugins/gjshello/gjshello.plugin | 9 +
tests/libpeas/Makefile.am | 7 +
tests/libpeas/extension-gjs.c | 80 ++++
tests/libpeas/plugins/Makefile.am | 4 +
tests/libpeas/plugins/extension-gjs/Makefile.am | 5 +
.../libpeas/plugins/extension-gjs/extension-gjs.js | 24 ++
.../plugins/extension-gjs/extension-gjs.plugin | 9 +
19 files changed, 1074 insertions(+), 0 deletions(-)
---
diff --git a/.gitignore b/.gitignore
index 70d75ad..87b5f14 100644
--- a/.gitignore
+++ b/.gitignore
@@ -74,6 +74,7 @@ Makefile.in
/tests/*/vgdump-*
/tests/libpeas/engine
/tests/libpeas/extension-c
+/tests/libpeas/extension-gjs
/tests/libpeas/extension-python
/tests/libpeas/extension-seed
/tests/libpeas/extension-set
diff --git a/configure.ac b/configure.ac
index 0f1cebd..667f3b0 100644
--- a/configure.ac
+++ b/configure.ac
@@ -266,6 +266,43 @@ fi
AM_CONDITIONAL([ENABLE_SEED],[test "x$found_seed" = "xyes"])
dnl ================================================================
+dnl GJS Javascript Engine
+dnl ================================================================
+
+GJS_REQUIRED=0.7.8
+
+AC_ARG_ENABLE(gjs,
+ AS_HELP_STRING([--enable-gjs],[Enable GJS support]),
+ [enable_gjs=$enableval],
+ [enable_gjs=auto])
+
+AC_MSG_CHECKING([for GJS JS availability.])
+
+if test "x$enable_gjs" = "xno"; then
+ found_gjs="no (disabled, use --enable-gjs to enable)"
+else
+ PKG_CHECK_EXISTS([gjs-internals-1.0 >= $GJS_REQUIRED gjs-gi-1.0],
+ [found_gjs=yes],
+ [found_gjs=no])
+fi
+
+if test "$enable_gjs" = "yes" -a "$found_gjs" = "no"; then
+ AC_MSG_ERROR([You need to have gjs-internals-1.0 >= $GJS_REQUIRED installed to build libpeas])
+fi
+AC_MSG_RESULT([$found_gjs])
+
+if test "$found_gjs" = "yes"; then
+ GJS_CFLAGS=`$PKG_CONFIG --cflags gjs-internals-1.0 gjs-gi-1.0`
+ GJS_LIBS=`$PKG_CONFIG --libs gjs-internals-1.0 gjs-gi-1.0`
+ AC_SUBST(GJS_CFLAGS)
+ AC_SUBST(GJS_LIBS)
+
+ AC_DEFINE(ENABLE_GJS,1,[Define to compile with GJS support])
+fi
+
+AM_CONDITIONAL([ENABLE_GJS],[test "x$found_gjs" = "xyes"])
+
+dnl ================================================================
dnl Python
dnl ================================================================
@@ -434,6 +471,7 @@ libpeas/Makefile
libpeas-gtk/Makefile
loaders/Makefile
loaders/c/Makefile
+loaders/gjs/Makefile
loaders/python/Makefile
loaders/seed/Makefile
data/Makefile
@@ -443,6 +481,7 @@ data/libpeas-1.0.pc
data/libpeas-gtk-1.0.pc
peas-demo/Makefile
peas-demo/plugins/Makefile
+peas-demo/plugins/gjshello/Makefile
peas-demo/plugins/helloworld/Makefile
peas-demo/plugins/pythonhello/Makefile
peas-demo/plugins/secondtime/Makefile
@@ -453,6 +492,7 @@ tests/Makefile
tests/libpeas/Makefile
tests/libpeas/plugins/Makefile
tests/libpeas/plugins/extension-c/Makefile
+tests/libpeas/plugins/extension-gjs/Makefile
tests/libpeas/plugins/extension-python/Makefile
tests/libpeas/plugins/extension-seed/Makefile
tests/libpeas/introspection/Makefile
@@ -484,6 +524,7 @@ Configuration:
Coverage testing : ${enable_gcov}
Glade Catalog : ${found_glade_catalog}
Seed JS support : ${found_seed}
+ GJS JS support : ${found_gjs}
Python support : ${found_python}
Vala support : ${found_vala}
"
diff --git a/loaders/Makefile.am b/loaders/Makefile.am
index 6e6c67a..f7832ae 100644
--- a/loaders/Makefile.am
+++ b/loaders/Makefile.am
@@ -1,5 +1,9 @@
SUBDIRS = c
+if ENABLE_GJS
+SUBDIRS += gjs
+endif
+
if ENABLE_PYTHON
SUBDIRS += python
endif
diff --git a/loaders/gjs/Makefile.am b/loaders/gjs/Makefile.am
new file mode 100644
index 0000000..f768d16
--- /dev/null
+++ b/loaders/gjs/Makefile.am
@@ -0,0 +1,21 @@
+# GJS plugin loader
+
+loaderdir = $(libdir)/libpeas-1.0/loaders
+
+INCLUDES = \
+ -I$(top_srcdir) \
+ $(PEAS_CFLAGS) \
+ $(WARN_CFLAGS) \
+ $(DISABLE_DEPRECATED) \
+ $(GJS_CFLAGS)
+
+loader_LTLIBRARIES = libgjsloader.la
+
+libgjsloader_la_SOURCES = \
+ peas-extension-gjs.c \
+ peas-extension-gjs.h \
+ peas-plugin-loader-gjs.c \
+ peas-plugin-loader-gjs.h
+
+libgjsloader_la_LDFLAGS = $(LOADER_LIBTOOL_FLAGS)
+libgjsloader_la_LIBADD = $(PEAS_LIBS) $(GJS_LIBS)
diff --git a/loaders/gjs/peas-extension-gjs.c b/loaders/gjs/peas-extension-gjs.c
new file mode 100644
index 0000000..f93da35
--- /dev/null
+++ b/loaders/gjs/peas-extension-gjs.c
@@ -0,0 +1,403 @@
+/*
+ * peas-extension-gjs.c
+ * This file is part of libpeas
+ *
+ * Copyright (C) 2011 - Garrett Regier, Steve Frécinaux
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU Library General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU Library General Public License for more details.
+ *
+ * You should have received a copy of the GNU Library General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+ */
+
+#ifdef HAVE_CONFIG_H
+#include <config.h>
+#endif
+
+#include <string.h>
+
+#include <girepository.h>
+#include <gjs/gi/arg.h>
+#include <gjs/gi/value.h>
+
+#include <libpeas/peas-introspection.h>
+#include <libpeas/peas-extension-subclasses.h>
+
+#include "peas-extension-gjs.h"
+
+G_DEFINE_TYPE (PeasExtensionGjs, peas_extension_gjs, PEAS_TYPE_EXTENSION);
+
+typedef struct {
+ GIArgInfo arg_info;
+ GITypeInfo type_info;
+
+ /* Only used by out arguments */
+ gpointer ptr;
+} CachedArg;
+
+static void
+peas_extension_gjs_init (PeasExtensionGjs *gexten)
+{
+}
+
+static gchar *
+convert_property_name (const gchar *pname)
+{
+ gint i;
+ gchar *prop_name;
+
+ prop_name = g_strdup (pname);
+
+ for (i = 0; prop_name[i] != '\0'; ++i)
+ {
+ if (prop_name[i] == '-')
+ prop_name[i] = '_';
+ }
+
+ return prop_name;
+}
+
+static void
+peas_extension_gjs_set_property (GObject *object,
+ guint prop_id,
+ const GValue *value,
+ GParamSpec *pspec)
+{
+ PeasExtensionGjs *gexten = PEAS_EXTENSION_GJS (object);
+ gchar *prop_name;
+ jsval js_value;
+
+ prop_name = convert_property_name (g_param_spec_get_name (pspec));
+
+ if (!gjs_value_from_g_value (gexten->js_context, &js_value, value))
+ {
+ g_warning ("Error: failed to convert GValue to "
+ "jsval for property '%s'", prop_name);
+ }
+ else if (!JS_SetProperty (gexten->js_context, gexten->js_object,
+ prop_name, &js_value))
+ {
+ g_warning ("Error: failed to set property '%s'", prop_name);
+ }
+
+ g_free (prop_name);
+}
+
+static void
+peas_extension_gjs_get_property (GObject *object,
+ guint prop_id,
+ GValue *value,
+ GParamSpec *pspec)
+{
+ PeasExtensionGjs *gexten = PEAS_EXTENSION_GJS (object);
+ gchar *prop_name;
+ jsval js_value;
+
+ prop_name = convert_property_name (g_param_spec_get_name (pspec));
+
+ if (!JS_GetProperty (gexten->js_context, gexten->js_object,
+ prop_name, &js_value))
+ {
+ g_warning ("Error: failed to get property '%s'", prop_name);
+ }
+ else if (!gjs_value_to_g_value (gexten->js_context, js_value, value))
+ {
+ g_warning ("Error: failed to convert jsval to "
+ "GValue for property '%s'", prop_name);
+ }
+
+ g_free (prop_name);
+}
+
+static void
+peas_extension_gjs_dispose (GObject *object)
+{
+ PeasExtensionGjs *gexten = PEAS_EXTENSION_GJS (object);
+
+ if (gexten->js_context != NULL)
+ {
+ JS_RemoveObjectRoot (gexten->js_context, &gexten->js_object);
+ gexten->js_context = NULL;
+ gexten->js_object = NULL;
+ }
+}
+
+static gboolean
+set_out_arg (JSContext *js_context,
+ GIFunctionInfo *func_info,
+ gboolean is_return_value,
+ GIArgInfo *arg_info,
+ GITypeInfo *type_info,
+ gpointer ptr,
+ jsval js_value)
+{
+ gboolean nullable;
+ GITransfer transfer;
+ GIArgument argument;
+
+ if (is_return_value)
+ {
+ nullable = g_callable_info_may_return_null (func_info);
+ transfer = g_callable_info_get_caller_owns (func_info);
+ }
+ else
+ {
+ nullable = g_arg_info_may_be_null (arg_info);
+ transfer = g_arg_info_get_ownership_transfer (arg_info);
+ }
+
+ if (!gjs_value_to_g_argument (js_context, js_value, type_info, NULL,
+ GJS_ARGUMENT_RETURN_VALUE, /* ? */
+ transfer, nullable, &argument))
+ {
+ if (is_return_value)
+ {
+ g_warning ("Error failed to convert return value to GIArgument");
+ }
+ else
+ {
+ g_warning ("Error failed to convert OUT argument '%s' from "
+ "jsval to GIArgument", g_base_info_get_name (type_info));
+ }
+
+ return FALSE;
+ }
+
+ peas_gi_argument_to_pointer (type_info, &argument, ptr);
+
+ return TRUE;
+}
+
+static gboolean
+peas_extension_gjs_call (PeasExtension *exten,
+ const gchar *method_name,
+ GIArgument *args,
+ GIArgument *retval)
+{
+ PeasExtensionGjs *gexten = PEAS_EXTENSION_GJS (exten);
+ GType exten_type;
+ gboolean success = FALSE;
+ jsval js_method, js_retval;
+ GICallableInfo *func_info;
+ jsval *js_args;
+ CachedArg *arg_cache;
+ gint i, n_args, nth_out_arg;
+ gint n_in_args = 0;
+ gint n_out_args = 0;
+ gint cached_args = 0;
+
+ exten_type = peas_extension_get_extension_type (exten);
+
+ /* Fetch the JS method we want to call */
+ if (!JS_GetProperty (gexten->js_context, gexten->js_object,
+ method_name, &js_method) ||
+ JSVAL_IS_VOID (js_method))
+ {
+ g_warning ("Method '%s.%s' was not found",
+ g_type_name (exten_type), method_name);
+ return FALSE;
+ }
+
+ if (JSVAL_IS_NULL (js_method) || !JSVAL_IS_OBJECT (js_method) ||
+ !JS_ObjectIsFunction (gexten->js_context, JSVAL_TO_OBJECT (js_method)))
+ {
+ g_warning ("Method '%s.%s' in not a function",
+ g_type_name (exten_type), method_name);
+ return FALSE;
+ }
+
+ /* Prepare the arguments */
+ func_info = peas_gi_get_method_info (exten_type, method_name);
+ if (func_info == NULL)
+ return FALSE;
+
+ n_args = g_callable_info_get_n_args (func_info);
+ if (n_args < 0)
+ {
+ g_warn_if_fail (n_args >= 0);
+ goto out;
+ }
+
+ js_args = g_newa (jsval, n_args);
+ arg_cache = g_newa (CachedArg, n_args + 1);
+
+ /* Return value is an out arg */
+ g_callable_info_load_return_type (func_info, &arg_cache[0].type_info);
+ if (g_type_info_get_tag (&arg_cache[0].type_info) != GI_TYPE_TAG_VOID)
+ {
+ ++n_out_args;
+ arg_cache[cached_args++].ptr = &retval->v_pointer;
+ }
+
+ /* Handle the arguments */
+ for (i = 0; i < n_args; ++i, ++cached_args)
+ {
+ GIDirection direction;
+
+ g_callable_info_load_arg (func_info, i, &arg_cache[cached_args].arg_info);
+ direction = g_arg_info_get_direction (&arg_cache[cached_args].arg_info);
+ g_arg_info_load_type (&arg_cache[cached_args].arg_info,
+ &arg_cache[cached_args].type_info);
+
+ if ((direction == GI_DIRECTION_IN || direction == GI_DIRECTION_INOUT) &&
+ !gjs_value_from_g_argument (gexten->js_context, &js_args[n_in_args++],
+ &arg_cache[cached_args].type_info,
+ &args[i]))
+ {
+ g_warning ("Error failed to convert argument '%s'",
+ g_base_info_get_name (&arg_cache[cached_args].arg_info));
+ goto out;
+ }
+
+ if (direction == GI_DIRECTION_OUT || direction == GI_DIRECTION_INOUT)
+ {
+ ++n_out_args;
+ arg_cache[cached_args].ptr = args[i].v_pointer;
+ }
+ }
+
+ success = JS_CallFunctionValue (gexten->js_context, gexten->js_object,
+ js_method, n_in_args, js_args, &js_retval);
+
+ if (!success)
+ {
+ g_warning ("Error while calling '%s.%s'",
+ g_type_name (exten_type), method_name);
+ goto out;
+ }
+
+ /* First we need to release in argument */
+ for (i = 0; i < cached_args; ++i)
+ {
+ GIDirection direction;
+
+ /* First cached argument may be the return value */
+ if (i == 0 && cached_args > n_args)
+ continue;
+
+ direction = g_arg_info_get_direction (&arg_cache[i].arg_info);
+
+ if (direction == GI_DIRECTION_IN || direction == GI_DIRECTION_INOUT)
+ {
+ GITransfer transfer;
+
+ transfer = g_arg_info_get_ownership_transfer (&arg_cache[i].arg_info);
+
+ if (!gjs_g_argument_release_in_arg (gexten->js_context, transfer,
+ &arg_cache[i].type_info,
+ &args[i]))
+ {
+ g_warning ("Error failed to release IN argument '%s'",
+ g_base_info_get_name (&arg_cache[i].arg_info));
+ }
+ }
+ }
+
+ /* Check that we have a valid return value */
+ if (n_out_args > 1)
+ {
+ if (!JSVAL_IS_OBJECT (js_retval) ||
+ !JS_IsArrayObject (gexten->js_context, JSVAL_TO_OBJECT (js_retval)))
+ {
+ g_warning ("Error return value is not an array");
+ success = FALSE;
+ goto out;
+ }
+ }
+
+ /* Set out arguments */
+ for (i = 0, nth_out_arg = 0; i < cached_args && success; ++i, ++nth_out_arg)
+ {
+ gboolean is_return_value;
+
+ is_return_value = i == 0 && cached_args > n_args;
+
+ /* Return value does not have a GIArgInfo and is always out */
+ if (!is_return_value)
+ {
+ GIDirection direction;
+
+ direction = g_arg_info_get_direction (&arg_cache[i].arg_info);
+
+ if (direction == GI_DIRECTION_IN)
+ continue;
+ }
+
+ if (n_out_args == 1)
+ {
+ success = set_out_arg (gexten->js_context, func_info, is_return_value,
+ &arg_cache[i].arg_info, &arg_cache[i].type_info,
+ arg_cache[i].ptr, js_retval);
+ break;
+ }
+ else if (n_out_args > 1)
+ {
+ jsval js_value;
+
+ if (!JS_GetElement (gexten->js_context, JSVAL_TO_OBJECT (js_retval),
+ nth_out_arg, &js_value))
+ {
+ g_warning ("Error failed to get out argument %i", nth_out_arg);
+ success = FALSE;
+ }
+ else
+ {
+ success = set_out_arg (gexten->js_context, func_info,
+ is_return_value, &arg_cache[i].arg_info,
+ &arg_cache[i].type_info, arg_cache[i].ptr,
+ js_value);
+ }
+ }
+ }
+
+out:
+
+ g_base_info_unref (func_info);
+
+ return success;
+}
+
+static void
+peas_extension_gjs_class_init (PeasExtensionGjsClass *klass)
+{
+ GObjectClass *object_class = G_OBJECT_CLASS (klass);
+ PeasExtensionClass *extension_class = PEAS_EXTENSION_CLASS (klass);
+
+ object_class->set_property = peas_extension_gjs_set_property;
+ object_class->get_property = peas_extension_gjs_get_property;
+ object_class->dispose = peas_extension_gjs_dispose;
+
+ extension_class->call = peas_extension_gjs_call;
+}
+
+PeasExtension *
+peas_extension_gjs_new (GType exten_type,
+ JSContext *js_context,
+ JSObject *js_object)
+{
+ PeasExtensionGjs *gexten;
+ GType real_type;
+
+ g_return_val_if_fail (js_context != NULL, NULL);
+ g_return_val_if_fail (js_object != NULL, NULL);
+
+ real_type = peas_extension_register_subclass (PEAS_TYPE_EXTENSION_GJS, exten_type);
+ gexten = PEAS_EXTENSION_GJS (g_object_new (real_type,
+ "extension-type", exten_type,
+ NULL));
+
+ gexten->js_context = js_context;
+ gexten->js_object = js_object;
+ JS_AddObjectRoot (gexten->js_context, &gexten->js_object);
+
+ return PEAS_EXTENSION (gexten);
+}
diff --git a/loaders/gjs/peas-extension-gjs.h b/loaders/gjs/peas-extension-gjs.h
new file mode 100644
index 0000000..2f8ddfc
--- /dev/null
+++ b/loaders/gjs/peas-extension-gjs.h
@@ -0,0 +1,59 @@
+/*
+ * peas-extension-gjs.h
+ * This file is part of libpeas
+ *
+ * Copyright (C) 2011 - Garrett Regier, Steve Frécinaux
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU Library General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU Library General Public License for more details.
+ *
+ * You should have received a copy of the GNU Library General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+ */
+
+#ifndef __PEAS_EXTENSION_GJS_H__
+#define __PEAS_EXTENSION_GJS_H__
+
+#include <libpeas/peas-extension-priv.h>
+#include <gjs/gjs-module.h>
+
+G_BEGIN_DECLS
+
+#define PEAS_TYPE_EXTENSION_GJS (peas_extension_gjs_get_type ())
+#define PEAS_EXTENSION_GJS(obj) (G_TYPE_CHECK_INSTANCE_CAST ((obj), PEAS_TYPE_EXTENSION_GJS, PeasExtensionGjs))
+#define PEAS_EXTENSION_GJS_CLASS(klass) (G_TYPE_CHECK_CLASS_CAST ((klass), PEAS_TYPE_EXTENSION_GJS, PeasExtensionGjsClass))
+#define PEAS_IS_EXTENSION_GJS(obj) (G_TYPE_CHECK_INSTANCE_TYPE ((obj), PEAS_TYPE_EXTENSION_GJS))
+#define PEAS_IS_EXTENSION_GJS_CLASS(klass) (G_TYPE_CHECK_CLASS_TYPE ((klass), PEAS_TYPE_EXTENSION_GJS))
+#define PEAS_EXTENSION_GJS_GET_CLASS(obj) (G_TYPE_INSTANCE_GET_CLASS ((obj), PEAS_TYPE_EXTENSION_GJS, PeasExtensionGjsClass))
+
+typedef struct _PeasExtensionGjs PeasExtensionGjs;
+typedef struct _PeasExtensionGjsClass PeasExtensionGjsClass;
+
+struct _PeasExtensionGjs {
+ PeasExtension parent;
+
+ JSContext *js_context;
+ JSObject *js_object;
+};
+
+struct _PeasExtensionGjsClass {
+ PeasExtensionClass parent_class;
+};
+
+GType peas_extension_gjs_get_type (void) G_GNUC_CONST;
+
+PeasExtension *peas_extension_gjs_new (GType exten_type,
+ JSContext *js_context,
+ JSObject *js_object);
+
+G_END_DECLS
+
+#endif /* __PEAS_EXTENSION_GJS_H__ */
diff --git a/loaders/gjs/peas-plugin-loader-gjs.c b/loaders/gjs/peas-plugin-loader-gjs.c
new file mode 100644
index 0000000..c3a4539
--- /dev/null
+++ b/loaders/gjs/peas-plugin-loader-gjs.c
@@ -0,0 +1,307 @@
+/*
+ * peas-plugin-loader-gjs.c
+ * This file is part of libpeas
+ *
+ * Copyright (C) 2011 - Garrett Regier, Steve Frécinaux
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU Library General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU Library General Public License for more details.
+ *
+ * You should have received a copy of the GNU Library General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+ */
+
+#ifdef HAVE_CONFIG_H
+#include <config.h>
+#endif
+
+#include <gjs/gjs-module.h>
+#include <gjs/gi/object.h>
+#include <gjs/gi/repo.h>
+#include <gjs/gi/value.h>
+
+#include "peas-plugin-loader-gjs.h"
+#include "peas-extension-gjs.h"
+
+typedef struct {
+ JSObject *extensions;
+ GjsContext *context;
+} GjsInfo;
+
+G_DEFINE_TYPE (PeasPluginLoaderGjs, peas_plugin_loader_gjs, PEAS_TYPE_PLUGIN_LOADER);
+
+static gchar *
+get_script_filename_for_plugin_info (PeasPluginInfo *info)
+{
+ gchar *basename;
+ gchar *filename;
+
+ basename = g_strconcat (peas_plugin_info_get_module_name (info), ".js", NULL);
+ filename = g_build_filename (peas_plugin_info_get_module_dir (info), basename, NULL);
+
+ g_free (basename);
+
+ return filename;
+}
+
+static gboolean
+peas_plugin_loader_gjs_load (PeasPluginLoader *loader,
+ PeasPluginInfo *info)
+{
+ PeasPluginLoaderGjs *gloader = PEAS_PLUGIN_LOADER_GJS (loader);
+ gchar **search_paths;
+ const gchar *version;
+ GjsContext *context;
+ gchar *filename;
+ GjsInfo *ginfo;
+ GError *error = NULL;
+ JSContext *js_context;
+ JSObject *global;
+ jsval extensions;
+
+ filename = get_script_filename_for_plugin_info (info);
+
+ g_debug ("GJS script filename is '%s'", filename);
+
+ search_paths = g_new (gchar *, 2);
+ search_paths[0] = g_strdup (peas_plugin_info_get_module_dir (info));
+ search_paths[1] = NULL;
+
+ version = gjs_context_scan_file_for_js_version (filename);
+
+ context = g_object_new (GJS_TYPE_CONTEXT,
+ "search-path", search_paths,
+ "js-version", version,
+ NULL);
+
+ gjs_context_eval_file (context, filename, NULL, &error);
+
+ g_free (search_paths);
+ g_free (filename);
+
+ if (error != NULL)
+ {
+ g_warning ("Error: %s", error->message);
+ g_error_free (error);
+ g_object_unref (context);
+ return FALSE;
+ }
+
+ js_context = gjs_context_get_native_context (context);
+ global = JS_GetGlobalObject (js_context);
+
+ if (!JS_GetProperty (js_context, global, "extensions", &extensions))
+ {
+ g_warning ("Error: could not find extensions");
+ return FALSE;
+ }
+
+ if (!JSVAL_IS_OBJECT (extensions) || JSVAL_IS_NULL (extensions))
+ {
+ g_warning ("Error: 'extensions' is of invalid type '%s'",
+ gjs_get_type_name (extensions));
+ return FALSE;
+ }
+
+ ginfo = g_slice_new (GjsInfo);
+ ginfo->context = g_object_ref (context);
+ ginfo->extensions = JSVAL_TO_OBJECT (extensions);
+ JS_AddObjectRoot (js_context, &ginfo->extensions);
+
+ g_hash_table_insert (gloader->loaded_plugins, info, ginfo);
+
+ g_object_unref (context);
+
+ return TRUE;
+}
+
+static gboolean
+peas_plugin_loader_gjs_provides_extension (PeasPluginLoader *loader,
+ PeasPluginInfo *info,
+ GType exten_type)
+{
+ PeasPluginLoaderGjs *gloader = PEAS_PLUGIN_LOADER_GJS (loader);
+ GjsInfo *ginfo;
+ JSContext *js_context;
+ jsval extension;
+
+ ginfo = g_hash_table_lookup (gloader->loaded_plugins, info);
+
+ js_context = gjs_context_get_native_context (ginfo->context);
+
+ return JS_GetProperty (js_context, ginfo->extensions,
+ g_type_name (exten_type), &extension) &&
+ JSVAL_IS_OBJECT (extension) && !JSVAL_IS_NULL (extension);
+}
+
+static PeasExtension *
+peas_plugin_loader_gjs_create_extension (PeasPluginLoader *loader,
+ PeasPluginInfo *info,
+ GType exten_type,
+ guint n_parameters,
+ GParameter *parameters)
+{
+ PeasPluginLoaderGjs *gloader = PEAS_PLUGIN_LOADER_GJS (loader);
+ GjsInfo *ginfo;
+ JSContext *js_context;
+ jsval extension_methods;
+ JSObject *extension;
+ guint i;
+ jsval js_value;
+ GValue gvalue = { 0 };
+
+ ginfo = g_hash_table_lookup (gloader->loaded_plugins, info);
+
+ js_context = gjs_context_get_native_context (ginfo->context);
+
+ if (!JS_GetProperty (js_context, ginfo->extensions,
+ g_type_name (exten_type), &extension_methods) ||
+ JSVAL_IS_VOID (extension_methods) || JSVAL_IS_NULL (extension_methods))
+ return NULL;
+
+ if (!JSVAL_IS_OBJECT (extension_methods))
+ {
+ g_warning ("Extension '%s' in plugin '%s' in not a valid object",
+ g_type_name (exten_type),
+ peas_plugin_info_get_module_name (info));
+ return NULL;
+ }
+
+ /* Copy the original extension_methods object to a new specific object. */
+ extension = JS_NewObject (js_context, NULL,
+ JSVAL_TO_OBJECT (extension_methods), NULL);
+
+ /* Cannot use g_object_set_property()
+ * because the property may be construct-only
+ */
+ for (i = 0; i < n_parameters; i++)
+ {
+ guint j;
+ gchar *prop_name;
+
+ prop_name = g_strdup (parameters[i].name);
+ for (j = 0; prop_name[j] != '\0'; ++j)
+ {
+ if (prop_name[j] == '-')
+ prop_name[j] = '_';
+ }
+
+ if (!gjs_value_from_g_value (js_context, &js_value, ¶meters[i].value))
+ {
+ g_warning ("Error: failed to convert GValue to "
+ "jsval for property '%s'", prop_name);
+ }
+ else if (!JS_SetProperty (js_context, extension, prop_name, &js_value))
+ {
+ g_warning ("Error: failed to set property '%s'", prop_name);
+ }
+
+ g_free (prop_name);
+ }
+
+ /* Set the plugin info as an attribute of the instance */
+ g_value_init (&gvalue, PEAS_TYPE_PLUGIN_INFO);
+ g_value_set_boxed (&gvalue, info);
+
+ if (!gjs_value_from_g_value (js_context, &js_value, &gvalue))
+ {
+ g_warning ("Error: failed to convert PeasPluginInfo GValue to jsvalue");
+ }
+ else if (!JS_SetProperty (js_context, extension, "plugin_info", &js_value))
+ {
+ g_warning ("Error: failed to set property 'plugin_info'");
+ }
+
+ g_value_unset (&gvalue);
+
+ return peas_extension_gjs_new (exten_type, js_context, extension);
+}
+
+static void
+peas_plugin_loader_gjs_unload (PeasPluginLoader *loader,
+ PeasPluginInfo *info)
+{
+ PeasPluginLoaderGjs *gloader = PEAS_PLUGIN_LOADER_GJS (loader);
+
+ g_hash_table_remove (gloader->loaded_plugins, info);
+}
+
+static void
+garbage_collect (PeasPluginInfo *info,
+ GjsInfo *ginfo)
+{
+ JS_GC (gjs_context_get_native_context (ginfo->context));
+}
+
+static void
+peas_plugin_loader_gjs_garbage_collect (PeasPluginLoader *loader)
+{
+ PeasPluginLoaderGjs *gloader = PEAS_PLUGIN_LOADER_GJS (loader);
+
+ g_hash_table_foreach (gloader->loaded_plugins,
+ (GHFunc) garbage_collect,
+ NULL);
+}
+
+static void
+peas_plugin_loader_gjs_finalize (GObject *object)
+{
+ PeasPluginLoaderGjs *gloader = PEAS_PLUGIN_LOADER_GJS (object);
+
+ g_hash_table_destroy (gloader->loaded_plugins);
+
+ G_OBJECT_CLASS (peas_plugin_loader_gjs_parent_class)->finalize (object);
+}
+
+static void
+peas_plugin_loader_gjs_class_init (PeasPluginLoaderGjsClass *klass)
+{
+ GObjectClass *gobject_class = G_OBJECT_CLASS (klass);
+ PeasPluginLoaderClass *loader_class = PEAS_PLUGIN_LOADER_CLASS (klass);
+
+ gobject_class->finalize = peas_plugin_loader_gjs_finalize;
+
+ loader_class->load = peas_plugin_loader_gjs_load;
+ loader_class->provides_extension = peas_plugin_loader_gjs_provides_extension;
+ loader_class->create_extension = peas_plugin_loader_gjs_create_extension;
+ loader_class->unload = peas_plugin_loader_gjs_unload;
+ loader_class->garbage_collect = peas_plugin_loader_gjs_garbage_collect;
+}
+
+static void
+destroy_gjs_info (GjsInfo *ginfo)
+{
+ JSContext *js_context;
+
+ js_context = gjs_context_get_native_context (ginfo->context);
+
+ JS_RemoveObjectRoot (js_context, &ginfo->extensions);
+ g_object_unref (ginfo->context);
+
+ g_slice_free (GjsInfo, ginfo);
+}
+
+static void
+peas_plugin_loader_gjs_init (PeasPluginLoaderGjs *gloader)
+{
+ gloader->loaded_plugins = g_hash_table_new_full (g_direct_hash,
+ g_direct_equal,
+ NULL,
+ (GDestroyNotify) destroy_gjs_info);
+}
+
+G_MODULE_EXPORT void
+peas_register_types (PeasObjectModule *module)
+{
+ peas_object_module_register_extension_type (module,
+ PEAS_TYPE_PLUGIN_LOADER,
+ PEAS_TYPE_PLUGIN_LOADER_GJS);
+}
diff --git a/loaders/gjs/peas-plugin-loader-gjs.h b/loaders/gjs/peas-plugin-loader-gjs.h
new file mode 100644
index 0000000..c42ee4a
--- /dev/null
+++ b/loaders/gjs/peas-plugin-loader-gjs.h
@@ -0,0 +1,55 @@
+/*
+ * peas-plugin-loader-gjs.h
+ * This file is part of libpeas
+ *
+ * Copyright (C) 2011 - Garrett Regier, Steve Frécinaux
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU Library General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU Library General Public License for more details.
+ *
+ * You should have received a copy of the GNU Library General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+ */
+
+#ifndef __PEAS_PLUGIN_LOADER_GJS_H__
+#define __PEAS_PLUGIN_LOADER_GJS_H__
+
+#include <libpeas/peas-plugin-loader.h>
+#include <libpeas/peas-object-module.h>
+
+G_BEGIN_DECLS
+
+#define PEAS_TYPE_PLUGIN_LOADER_GJS (peas_plugin_loader_gjs_get_type ())
+#define PEAS_PLUGIN_LOADER_GJS(obj) (G_TYPE_CHECK_INSTANCE_CAST ((obj), PEAS_TYPE_PLUGIN_LOADER_GJS, PeasPluginLoaderGjs))
+#define PEAS_PLUGIN_LOADER_GJS_CLASS(klass) (G_TYPE_CHECK_CLASS_CAST ((klass), PEAS_TYPE_PLUGIN_LOADER_GJS, PeasPluginLoaderGjsClass))
+#define PEAS_IS_PLUGIN_LOADER_GJS(obj) (G_TYPE_CHECK_INSTANCE_TYPE ((obj), PEAS_TYPE_PLUGIN_LOADER_GJS))
+#define PEAS_IS_PLUGIN_LOADER_GJS_CLASS(klass) (G_TYPE_CHECK_CLASS_TYPE ((klass), PEAS_TYPE_PLUGIN_LOADER_GJS))
+#define PEAS_PLUGIN_LOADER_GJS_GET_CLASS(obj) (G_TYPE_INSTANCE_GET_CLASS ((obj), PEAS_TYPE_PLUGIN_LOADER_GJS, PeasPluginLoaderGjsClass))
+
+typedef struct _PeasPluginLoaderGjs PeasPluginLoaderGjs;
+typedef struct _PeasPluginLoaderGjsClass PeasPluginLoaderGjsClass;
+
+struct _PeasPluginLoaderGjs {
+ PeasPluginLoader parent;
+
+ GHashTable *loaded_plugins;
+};
+
+struct _PeasPluginLoaderGjsClass {
+ PeasPluginLoaderClass parent_class;
+};
+
+GType peas_plugin_loader_gjs_get_type (void) G_GNUC_CONST;
+G_MODULE_EXPORT void peas_register_types (PeasObjectModule *module);
+
+G_END_DECLS
+
+#endif /* __PEAS_PLUGIN_LOADER_GJS_H__ */
diff --git a/peas-demo/peas-demo.c b/peas-demo/peas-demo.c
index 83cd6a3..debe8a9 100644
--- a/peas-demo/peas-demo.c
+++ b/peas-demo/peas-demo.c
@@ -125,6 +125,7 @@ main (int argc,
peas_engine_add_search_path (engine, plugin_dir, plugin_dir);
g_free (plugin_dir);
+ peas_engine_enable_loader (engine, "gjs");
peas_engine_enable_loader (engine, "python");
peas_engine_enable_loader (engine, "seed");
diff --git a/peas-demo/plugins/Makefile.am b/peas-demo/plugins/Makefile.am
index 37ca9db..1298928 100644
--- a/peas-demo/plugins/Makefile.am
+++ b/peas-demo/plugins/Makefile.am
@@ -1,5 +1,9 @@
SUBDIRS = helloworld secondtime
+if ENABLE_GJS
+SUBDIRS += gjshello
+endif
+
if ENABLE_PYTHON
SUBDIRS += pythonhello
endif
diff --git a/peas-demo/plugins/gjshello/Makefile.am b/peas-demo/plugins/gjshello/Makefile.am
new file mode 100644
index 0000000..1a688da
--- /dev/null
+++ b/peas-demo/plugins/gjshello/Makefile.am
@@ -0,0 +1,7 @@
+plugindir = $(libdir)/peas-demo/plugins/gjshello
+
+plugin_DATA = \
+ gjshello.js \
+ gjshello.plugin
+
+EXTRA_DIST = $(plugin_DATA)
diff --git a/peas-demo/plugins/gjshello/gjshello.js b/peas-demo/plugins/gjshello/gjshello.js
new file mode 100644
index 0000000..6301289
--- /dev/null
+++ b/peas-demo/plugins/gjshello/gjshello.js
@@ -0,0 +1,33 @@
+const Gtk = imports.gi.Gtk;
+
+var LABEL_STRING = "GJS Also Says Hello!";
+
+print("LABEL_STRING=" + LABEL_STRING);
+
+var activatable_extension = {
+ activate: function() {
+ print("GJSHelloPlugin.activate");
+ this.object._gjshello_label = new Gtk.Label({ label: LABEL_STRING });
+ this.object._gjshello_label.show();
+ this.object.get_child().add(this.object._gjshello_label);
+ },
+ deactivate: function() {
+ print("GJSHelloPlugin.deactivate");
+ this.object.get_child().remove(this.object._gjshello_label);
+ this.object._gjshello_label.destroy();
+ },
+ update_state: function() {
+ print("GJSHelloPlugin.update_state");
+ }
+};
+
+var configurable_extension = {
+ create_configure_widget: function () {
+ return new Gtk.Label({ label: "Example of configuration dialog for a GJS plugin" });
+ }
+};
+
+var extensions = {
+ 'PeasActivatable': activatable_extension,
+ 'PeasGtkConfigurable': configurable_extension,
+};
diff --git a/peas-demo/plugins/gjshello/gjshello.plugin b/peas-demo/plugins/gjshello/gjshello.plugin
new file mode 100644
index 0000000..8d8f6f0
--- /dev/null
+++ b/peas-demo/plugins/gjshello/gjshello.plugin
@@ -0,0 +1,9 @@
+[Plugin]
+Module=gjshello
+Loader=gjs
+IAge=2
+Name=GJS Also Says Hello
+Description=Inserts a box containing "GJS Also Says Hello" in every windows.
+Authors=Steve Frécinaux <code istique net>
+Copyright=Copyright © 2009 Steve Frécinaux
+Website=http://code.istique.net/
diff --git a/tests/libpeas/Makefile.am b/tests/libpeas/Makefile.am
index a859306..de308bf 100644
--- a/tests/libpeas/Makefile.am
+++ b/tests/libpeas/Makefile.am
@@ -28,6 +28,13 @@ TEST_PROGS += extension-c
extension_c_SOURCES = extension-c.c
extension_c_LDADD = $(progs_ldadd)
+if ENABLE_GJS
+TEST_PROGS += extension-gjs
+extension_gjs_SOURCES = extension-gjs.c
+extension_gjs_CFLAGS = $(GJS_CFLAGS)
+extension_gjs_LDADD = $(progs_ldadd) $(GJS_LIBS)
+endif
+
if ENABLE_PYTHON
TEST_PROGS += extension-python
extension_python_SOURCES = extension-python.c
diff --git a/tests/libpeas/extension-gjs.c b/tests/libpeas/extension-gjs.c
new file mode 100644
index 0000000..deff079
--- /dev/null
+++ b/tests/libpeas/extension-gjs.c
@@ -0,0 +1,80 @@
+/*
+ * extension-gjs.c
+ * This file is part of libpeas
+ *
+ * Copyright (C) 2011 - Garrett Regier
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU Library General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU Library General Public License for more details.
+ *
+ * You should have received a copy of the GNU Library General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+ */
+
+#ifdef HAVE_CONFIG_H
+#include <config.h>
+#endif
+
+#include <gjs/gi/value.h>
+
+#include "loaders/gjs/peas-extension-gjs.h"
+
+#include "testing/testing-extension.h"
+#include "introspection/introspection-callable.h"
+
+static void
+test_extension_gjs_plugin_info (PeasEngine *engine)
+{
+ PeasPluginInfo *info;
+ PeasExtension *extension;
+ PeasExtensionGjs *gexten;
+ jsval js_value;
+ GValue gvalue = { 0 };
+
+ info = peas_engine_get_plugin_info (engine, "extension-gjs");
+
+ g_assert (peas_engine_load_plugin (engine, info));
+
+ extension = peas_engine_create_extension (engine, info,
+ INTROSPECTION_TYPE_CALLABLE,
+ NULL);
+
+ g_assert (PEAS_IS_EXTENSION (extension));
+
+ gexten = (PeasExtensionGjs *) extension;
+
+ g_value_init (&gvalue, PEAS_TYPE_PLUGIN_INFO);
+
+ g_assert (JS_GetProperty (gexten->js_context, gexten->js_object,
+ "plugin_info", &js_value));
+ g_assert (gjs_value_to_g_value (gexten->js_context, js_value, &gvalue));
+
+ g_assert (g_value_get_boxed (&gvalue) == info);
+
+ g_value_unset (&gvalue);
+
+ g_object_unref (extension);
+}
+
+int
+main (int argc,
+ char *argv[])
+{
+ g_test_init (&argc, &argv, NULL);
+
+ g_type_init ();
+
+ EXTENSION_TESTS (gjs);
+
+ EXTENSION_TEST (gjs, "plugin-info", plugin_info);
+
+ return g_test_run ();
+}
diff --git a/tests/libpeas/plugins/Makefile.am b/tests/libpeas/plugins/Makefile.am
index 0d9037d..2c7bac0 100644
--- a/tests/libpeas/plugins/Makefile.am
+++ b/tests/libpeas/plugins/Makefile.am
@@ -2,6 +2,10 @@ include $(top_srcdir)/tests/Makefile.plugin
SUBDIRS = extension-c
+if ENABLE_GJS
+SUBDIRS += extension-gjs
+endif
+
if ENABLE_PYTHON
SUBDIRS += extension-python
endif
diff --git a/tests/libpeas/plugins/extension-gjs/Makefile.am b/tests/libpeas/plugins/extension-gjs/Makefile.am
new file mode 100644
index 0000000..c6ea14d
--- /dev/null
+++ b/tests/libpeas/plugins/extension-gjs/Makefile.am
@@ -0,0 +1,5 @@
+noinst_DATA = \
+ extension-gjs.js \
+ extension-gjs.plugin
+
+EXTRA_DIST = $(noinst_DATA)
diff --git a/tests/libpeas/plugins/extension-gjs/extension-gjs.js b/tests/libpeas/plugins/extension-gjs/extension-gjs.js
new file mode 100644
index 0000000..e547f18
--- /dev/null
+++ b/tests/libpeas/plugins/extension-gjs/extension-gjs.js
@@ -0,0 +1,24 @@
+var callable_extension = {
+ call_with_return: function() {
+ return "Hello, World!"
+ },
+ call_no_args: function() {
+ },
+ call_single_arg: function() {
+ return true
+ },
+ call_multi_args: function() {
+ return [ true, true, true ]
+ }
+};
+
+var properties_extension = {
+ read_only: "read-only",
+ readwrite: "readwrite"
+};
+
+var extensions = {
+ "IntrospectionCallable": callable_extension,
+ "IntrospectionProperties" : properties_extension
+};
+
diff --git a/tests/libpeas/plugins/extension-gjs/extension-gjs.plugin b/tests/libpeas/plugins/extension-gjs/extension-gjs.plugin
new file mode 100644
index 0000000..dba5504
--- /dev/null
+++ b/tests/libpeas/plugins/extension-gjs/extension-gjs.plugin
@@ -0,0 +1,9 @@
+[Plugin]
+Module=extension-gjs
+Loader=gjs
+IAge=2
+Name=Extension GJS
+Description=This plugin is for the GJS PeasExtension tests.
+Authors=Garrett Regier
+Copyright=Copyright © 2011 Garrett Regier
+Website=http://live.gnome.org/Libpeas
[
Date Prev][
Date Next] [
Thread Prev][
Thread Next]
[
Thread Index]
[
Date Index]
[
Author Index]