[libpeas] Implemented a Lua plugin loader using the LGI bindings



commit 84a159374c7032f0bc536321df66d4ffac97d9fe
Author: Garrett Regier <garrett regier riftio com>
Date:   Tue Nov 4 05:43:20 2014 -0800

    Implemented a Lua plugin loader using the LGI bindings

 .gitignore                                         |    1 +
 README                                             |   12 +-
 configure.ac                                       |  130 +++++++-
 libpeas/peas-engine.c                              |   12 +-
 libpeas/peas-utils.c                               |    3 +-
 libpeas/peas-utils.h                               |    2 +-
 loaders/Makefile.am                                |    4 +
 loaders/lua5.1/Makefile.am                         |   31 ++
 loaders/lua5.1/peas-plugin-loader-lua-utils.c      |  122 ++++++
 loaders/lua5.1/peas-plugin-loader-lua-utils.h      |   42 ++
 loaders/lua5.1/peas-plugin-loader-lua.c            |  404 ++++++++++++++++++++
 loaders/lua5.1/peas-plugin-loader-lua.h            |   60 +++
 peas-demo/peas-demo.c                              |    1 +
 peas-demo/plugins/Makefile.am                      |    4 +
 peas-demo/plugins/luahello/Makefile.am             |    8 +
 peas-demo/plugins/luahello/luahello.lua            |   50 +++
 peas-demo/plugins/luahello/luahello.plugin         |    7 +
 tests/libpeas/Makefile.am                          |    7 +
 tests/libpeas/extension-lua.c                      |  213 ++++++++++
 tests/libpeas/plugins/Makefile.am                  |    5 +
 tests/libpeas/plugins/extension-lua/Makefile.am    |    7 +
 .../extension-lua/extension-lua51.gschema.xml      |    9 +
 .../plugins/extension-lua/extension-lua51.lua      |   91 +++++
 .../plugins/extension-lua/extension-lua51.plugin   |    7 +
 .../plugins/extension-lua51-nonexistent.plugin     |    7 +
 tests/libpeas/testing/testing-extension.c          |   13 +-
 26 files changed, 1241 insertions(+), 11 deletions(-)
---
diff --git a/.gitignore b/.gitignore
index 070467b..e7ae5cc 100644
--- a/.gitignore
+++ b/.gitignore
@@ -78,6 +78,7 @@ Makefile.in
 /tests/*/vgdump-*
 /tests/libpeas/engine
 /tests/libpeas/extension-c
+/tests/libpeas/extension-lua51
 /tests/libpeas/extension-python
 /tests/libpeas/extension-python3
 /tests/libpeas/extension-set
diff --git a/README b/README
index c7f92cc..3183089 100644
--- a/README
+++ b/README
@@ -20,11 +20,11 @@ GInterfaces the plugin writer will be able to implement as his plugin requires.
 On-demand programming language support
 --------------------------------------
 
-libpeas comes with a set of supported languages (currently, C, Python 2 and
-Python 3). Those languages are supported through “loaders” which are loaded
-on demand. What it means is that you only pay for what you use: if you have no
-Python plugin, the Python interpreter won't be loaded in memory. Of course, the
-same goes for the C loader.
+libpeas comes with a set of supported languages (currently, C, Lua 5.1,
+Python 2 and Python 3). Those languages are supported through “loaders” which
+are loaded on demand. What it means is that you only pay for what you use: if
+you have no Python plugin, the Python interpreter won't be loaded in memory.
+Of course, the same goes for the C loader.
 
 Damn simple to use (or at least we try hard)
 --------------------------------------------
@@ -82,7 +82,7 @@ Sample code
 -----------
 
 The libpeas package contains a sample application called peas-demo, and sample
-plugins written in C and Python.
+plugins written in C, Lua and Python.
 
 The global idea is this one: you create a new PeasEngine instance and give it
 the information needed for it to find your plugins. Then you load some plugins
diff --git a/configure.ac b/configure.ac
index bcd3d33..1786f09 100644
--- a/configure.ac
+++ b/configure.ac
@@ -232,6 +232,130 @@ fi
 AM_CONDITIONAL([ENABLE_GLADE_CATALOG],[test "x$found_glade_catalog" = "xyes"])
 
 dnl ================================================================
+dnl Lua
+dnl ================================================================
+
+LUA51_REQUIRED=5.1.0
+LUAJIT_REQUIRED=2.0
+
+LGI_MAJOR_VERSION=0
+LGI_MINOR_VERSION=8
+LGI_MICRO_VERSION=0
+LGI_REQUIRED=$LGI_MAJOR_VERSION.$LGI_MINOR_VERSION.$LGI_MICRO_VERSION
+AC_DEFINE_UNQUOTED(LGI_MAJOR_VERSION, [$LGI_MAJOR_VERSION], [LGI major version.])
+AC_DEFINE_UNQUOTED(LGI_MINOR_VERSION, [$LGI_MINOR_VERSION], [LGI minor version.])
+AC_DEFINE_UNQUOTED(LGI_MICRO_VERSION, [$LGI_MICRO_VERSION], [LGI micro version.])
+
+dnl
+dnl Test program for LGI version
+dnl
+m4_define([peas_lgi_version_test], [
+#include <lauxlib.h>
+#include <lualib.h>
+
+#include "peas-plugin-loader-lua-utils.c"
+
+int main(int argc, char **argv)
+{
+  lua_State *L;
+  gboolean success;
+
+  L = luaL_newstate ();
+  luaL_openlibs (L);
+
+  success = (peas_lua_utils_require (L, "lgi") &&
+             peas_lua_utils_check_version (L,
+                                           LGI_MAJOR_VERSION,
+                                           LGI_MINOR_VERSION,
+                                           LGI_MICRO_VERSION));
+
+  lua_close (L);
+  return success ? 0 : 1;
+}])
+
+AC_ARG_ENABLE(lua5.1,
+              AS_HELP_STRING([--enable-lua5.1],[Enable Lua 5.1 support]),
+              [enable_lua51=$enableval],
+              [enable_lua51=auto])
+
+AC_ARG_ENABLE(luajit,
+              AS_HELP_STRING([--enable-luajit],[Enable LuaJIT for Lua 5.1 support]),
+              [enable_luajit=$enableval],
+              [enable_luajit=auto])
+
+AC_MSG_CHECKING([for Lua 5.1 availability.])
+
+if test "x$enable_lua51" = "xno"; then
+    found_lua51="no (disabled, use --enable-lua5.1 to enable)"
+    AC_MSG_RESULT([$found_lua51])
+else
+    if test "x$enable_luajit" != "xno"; then
+        PKG_CHECK_EXISTS([luajit >= $LUAJIT_REQUIRED], [
+            found_lua51=yes
+            with_lua51=luajit
+        ], [
+            found_lua51=no
+        ])
+
+        if test "x$enable_luajit" = "xyes" -a "x$found_lua51" = "xno"; then
+            AC_MSG_ERROR([You need to have LuaJIT >= $LUAJIT_REQUIRED
+                          installed to build libpeas])
+        fi
+    fi
+
+    if test "x$found_lua51" != "xyes"; then
+        PKG_CHECK_EXISTS([lua5.1 >= $LUA51_REQUIRED], [
+            found_lua51=yes
+            with_lua51=lua5.1
+        ], [
+            found_lua51=no
+        ])
+
+        if test "x$enable_lua51" = "xyes" -a "x$found_lua51" = "xno"; then
+            AC_MSG_ERROR([You need to have Lua 5.1 >= $LUA51_REQUIRED
+                          installed to build libpeas])
+        fi
+    fi
+
+    if test "x$found_lua51" != "xyes"; then
+        AC_MSG_RESULT([$found_lua51])
+    else
+        AC_MSG_RESULT([$found_lua51 ($with_lua51)])
+
+        LUA51_CFLAGS=`$PKG_CONFIG --cflags $with_lua51`
+        LUA51_LIBS=`$PKG_CONFIG --libs $with_lua51`
+        AC_SUBST(LUA51_CFLAGS)
+        AC_SUBST(LUA51_LIBS)
+
+        AC_MSG_CHECKING(for LGI availability in Lua 5.1.)
+
+        peas_save_CFLAGS="$CFLAGS"
+        peas_save_LIBS="$LIBS"
+        CFLAGS="$PEAS_CFLAGS $LUA51_CFLAGS -Iloaders/lua5.1"
+        LIBS="$PEAS_LIBS $LUA51_LIBS"
+        AC_TRY_RUN(peas_lgi_version_test(0),
+                   found_lua51=yes,
+                   found_lua51=no,
+                   found_lua51=yes)
+        CFLAGS="$peas_save_CFLAGS"
+        LIBS="$peas_save_LIBS"
+
+        if test "x$enable_lua51" = "xyes" -a "x$found_lua51" = "xno"; then
+            AC_MSG_ERROR([You need to have LGI >= $LGI_REQUIRED
+                          installed to build libpeas])
+        fi
+
+        if test "x$found_lua51" = "xyes"; then
+            AC_DEFINE(ENABLE_LUA51, 1, [Define to compile with Lua support])
+        fi
+
+        AC_MSG_RESULT([$found_lua51])
+    fi
+fi
+
+AM_CONDITIONAL([ENABLE_LUA51], [test "x$found_lua51" = "xyes"])
+
+dnl ================================================================
 dnl Python
 dnl ================================================================
 
@@ -290,7 +414,7 @@ else
 fi
 
 if test "x$enable_python2" = "xyes" -a "x$found_python2" != "xyes"; then
-    AC_MSG_ERROR([You need to have Python 2 and PyGobject installed to build libpeas])
+    AC_MSG_ERROR([You need to have Python 2 and PyGObject installed to build libpeas])
 fi
 AC_MSG_RESULT([$found_python2])
 
@@ -393,6 +517,7 @@ docs/reference/version.xml
 libpeas/Makefile
 libpeas-gtk/Makefile
 loaders/Makefile
+loaders/lua5.1/Makefile
 loaders/python/Makefile
 loaders/python3/Makefile
 data/Makefile
@@ -403,6 +528,7 @@ data/libpeas-gtk-1.0.pc
 peas-demo/Makefile
 peas-demo/plugins/Makefile
 peas-demo/plugins/helloworld/Makefile
+peas-demo/plugins/luahello/Makefile
 peas-demo/plugins/pythonhello/Makefile
 peas-demo/plugins/secondtime/Makefile
 po/Makefile.in
@@ -410,6 +536,7 @@ tests/Makefile
 tests/libpeas/Makefile
 tests/libpeas/plugins/Makefile
 tests/libpeas/plugins/extension-c/Makefile
+tests/libpeas/plugins/extension-lua/Makefile
 tests/libpeas/plugins/extension-python/Makefile
 tests/libpeas/introspection/Makefile
 tests/libpeas/testing/Makefile
@@ -443,6 +570,7 @@ Configuration:
 
 Languages support:
 
+        Lua 5.1 support               : ${found_lua51}
         Python 2 support              : ${found_python2}
         Python 3 support              : ${found_python3}
 "
diff --git a/libpeas/peas-engine.c b/libpeas/peas-engine.c
index 249b357..c6b7043 100644
--- a/libpeas/peas-engine.c
+++ b/libpeas/peas-engine.c
@@ -589,6 +589,7 @@ static PeasPluginLoader *
 get_plugin_loader (PeasEngine *engine,
                    gint        loader_id)
 {
+  gint i, j;
   LoaderInfo *loader_info;
   const gchar *loader_name;
   gchar *module_name, *module_dir;
@@ -615,6 +616,15 @@ get_plugin_loader (PeasEngine *engine,
   module_name = g_strconcat (loader_name, "loader", NULL);
   module_dir = peas_dirs_get_plugin_loaders_dir ();
 
+  /* Remove '.'s from the module name */
+  for (i = 0, j = 0; module_name[i] != '\0'; ++i)
+    {
+      if (module_name[i] != '.')
+        module_name[j++] = module_name[i];
+    }
+
+  module_name[j] = '\0';
+
   loader_info->module = load_module (module_name, module_dir);
 
   if (loader_info->module == NULL)
@@ -667,7 +677,7 @@ get_plugin_loader (PeasEngine *engine,
  *
  * Enable a loader, enables a loader for plugins.
  * The C plugin loader is always enabled. The other plugin
- * loaders are: python and python3.
+ * loaders are: lua5.1 and python and python3.
  *
  * For instance, the following code will enable python plugins
  * to be loaded:
diff --git a/libpeas/peas-utils.c b/libpeas/peas-utils.c
index 21668f8..a2adb52 100644
--- a/libpeas/peas-utils.c
+++ b/libpeas/peas-utils.c
@@ -29,7 +29,8 @@
 
 #include "peas-utils.h"
 
-static const gchar *all_plugin_loaders[] = {"c", "python", "python3"};
+static const gchar *all_plugin_loaders[] = {"c", "lua5.1",
+                                            "python", "python3"};
 G_STATIC_ASSERT (G_N_ELEMENTS (all_plugin_loaders) == PEAS_UTILS_N_LOADERS);
 
 static void
diff --git a/libpeas/peas-utils.h b/libpeas/peas-utils.h
index e7f55de..024d369 100644
--- a/libpeas/peas-utils.h
+++ b/libpeas/peas-utils.h
@@ -24,7 +24,7 @@
 
 #include <glib-object.h>
 
-#define PEAS_UTILS_N_LOADERS 3
+#define PEAS_UTILS_N_LOADERS 4
 
 gboolean  peas_utils_valist_to_parameter_list (GType         iface_type,
                                                const gchar  *first_property,
diff --git a/loaders/Makefile.am b/loaders/Makefile.am
index 5868750..c7c050b 100644
--- a/loaders/Makefile.am
+++ b/loaders/Makefile.am
@@ -1,5 +1,9 @@
 SUBDIRS =
 
+if ENABLE_LUA51
+SUBDIRS += lua5.1
+endif
+
 if ENABLE_PYTHON2
 SUBDIRS += python
 endif
diff --git a/loaders/lua5.1/Makefile.am b/loaders/lua5.1/Makefile.am
new file mode 100644
index 0000000..c49aa76
--- /dev/null
+++ b/loaders/lua5.1/Makefile.am
@@ -0,0 +1,31 @@
+# Lua 5.1 plugin loader
+
+loaderdir = $(libdir)/libpeas-1.0/loaders
+
+AM_CPPFLAGS = \
+       -I$(top_srcdir)         \
+       $(PEAS_CFLAGS)          \
+       $(GCOV_CFLAGS)          \
+       $(WARN_CFLAGS)          \
+       $(DISABLE_DEPRECATED)   \
+       $(LUA51_CFLAGS)
+
+loader_LTLIBRARIES = liblua51loader.la
+
+liblua51loader_la_SOURCES = \
+       peas-plugin-loader-lua.c        \
+       peas-plugin-loader-lua.h        \
+       peas-plugin-loader-lua-utils.c  \
+       peas-plugin-loader-lua-utils.h
+
+liblua51loader_la_LDFLAGS = \
+       $(LOADER_LIBTOOL_FLAGS)         \
+       $(GCOV_LDFLAGS)
+
+liblua51loader_la_LIBADD = \
+       $(top_builddir)/libpeas/libpeas-1.0.la  \
+       $(PEAS_LIBS)                            \
+       $(LUA51_LIBS)
+
+gcov_sources = $(liblua51loader_la_SOURCES)
+include $(top_srcdir)/Makefile.gcov
diff --git a/loaders/lua5.1/peas-plugin-loader-lua-utils.c b/loaders/lua5.1/peas-plugin-loader-lua-utils.c
new file mode 100644
index 0000000..fb043fb
--- /dev/null
+++ b/loaders/lua5.1/peas-plugin-loader-lua-utils.c
@@ -0,0 +1,122 @@
+/*
+ * peas-plugin-loader-lua-utils.c
+ * This file is part of libpeas
+ *
+ * Copyright (C) 2014 - 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., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+ */
+
+#ifdef HAVE_CONFIG_H
+#include <config.h>
+#endif
+
+#include "peas-plugin-loader-lua-utils.h"
+
+#include <string.h>
+
+#include <lauxlib.h>
+#include <lualib.h>
+
+
+gboolean
+peas_lua_utils_require (lua_State   *L,
+                        const gchar *name)
+{
+  luaL_checkstack (L, 2, "");
+
+  lua_getglobal (L, "require");
+  lua_pushstring (L, name);
+
+  if (lua_pcall (L, 1, 1, 0) != 0)
+    {
+      g_warning ("Error failed to load Lua module '%s': %s",
+                 name, lua_tostring (L, -1));
+
+      /* Pop error */
+      lua_pop (L, 1);
+      return FALSE;
+    }
+
+  if (!lua_istable (L, -1))
+    {
+      g_warning ("Error invalid Lua module for '%s': "
+                 "expected table, got: %s",
+                 name, lua_tostring (L, -1));
+
+      /* Pop the module's table */
+      lua_pop (L, 1);
+      return FALSE;
+    }
+
+  return TRUE;
+}
+
+gboolean
+peas_lua_utils_check_version (lua_State *L,
+                              guint      req_major,
+                              guint      req_minor,
+                              guint      req_micro)
+{
+  const gchar *version_str;
+  gchar **version_str_parts;
+  gint n_version_parts;
+  gint *version_parts;
+  gint i;
+  gboolean success = FALSE;
+
+  lua_getfield (L, -1, "_VERSION");
+  version_str = lua_tostring (L, -1);
+
+  version_str_parts = g_strsplit (version_str, ".", 0);
+
+  n_version_parts = g_strv_length (version_str_parts);
+  version_parts = g_newa (gint, n_version_parts);
+
+  for (i = 0; i < n_version_parts; ++i)
+    {
+      gchar *end;
+
+      version_parts[i] = g_ascii_strtoll (version_str_parts[i], &end, 10);
+
+      if (*end != '\0' ||
+          version_parts[i] < 0 ||
+          version_parts[i] == G_MAXINT64)
+        {
+          g_warning ("Invalid version string: %s", version_str);
+          goto error;
+        }
+    }
+
+  if (n_version_parts < 3 ||
+      version_parts[0] != req_major ||
+      version_parts[1] < req_minor ||
+      (version_parts[1] == req_minor && version_parts[2] < req_micro))
+    {
+      g_warning ("Version mismatch %d.%d.%d is required, found %s",
+                 req_major, req_minor, req_micro, version_str);
+      goto error;
+    }
+
+  success = TRUE;
+
+error:
+
+  /* Pop _VERSION */
+  lua_pop (L, 1);
+
+  g_strfreev (version_str_parts);
+  return success;
+}
diff --git a/loaders/lua5.1/peas-plugin-loader-lua-utils.h b/loaders/lua5.1/peas-plugin-loader-lua-utils.h
new file mode 100644
index 0000000..dafe94f
--- /dev/null
+++ b/loaders/lua5.1/peas-plugin-loader-lua-utils.h
@@ -0,0 +1,42 @@
+/*
+ * peas-plugin-loader-lua-utils.h
+ * This file is part of libpeas
+ *
+ * Copyright (C) 2014 - 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., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+ */
+
+#ifndef __PEAS_PLUGIN_LOADER_LUA_UTILS_H__
+#define __PEAS_PLUGIN_LOADER_LUA_UTILS_H__
+
+#include <glib.h>
+#include <lua.h>
+
+G_BEGIN_DECLS
+
+
+gboolean peas_lua_utils_require       (lua_State   *L,
+                                       const gchar *name);
+
+gboolean peas_lua_utils_check_version (lua_State   *L,
+                                       guint        req_major,
+                                       guint        req_minor,
+                                       guint        req_micro);
+
+G_END_DECLS
+
+#endif /* __PEAS_PLUGIN_LOADER_LUA_UTILS_H__ */
+
diff --git a/loaders/lua5.1/peas-plugin-loader-lua.c b/loaders/lua5.1/peas-plugin-loader-lua.c
new file mode 100644
index 0000000..ccc0ad0
--- /dev/null
+++ b/loaders/lua5.1/peas-plugin-loader-lua.c
@@ -0,0 +1,404 @@
+/*
+ * peas-plugin-loader-lua.c
+ * This file is part of libpeas
+ *
+ * Copyright (C) 2014 - 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., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+ */
+
+#ifdef HAVE_CONFIG_H
+#include <config.h>
+#endif
+
+#include "peas-plugin-loader-lua.h"
+
+#include <string.h>
+
+#include <lua.h>
+#include <lauxlib.h>
+#include <lualib.h>
+
+#include "peas-plugin-loader-lua-utils.h"
+
+
+struct _PeasPluginLoaderLuaPrivate {
+  lua_State *L;
+};
+
+G_DEFINE_TYPE (PeasPluginLoaderLua, peas_plugin_loader_lua, PEAS_TYPE_PLUGIN_LOADER)
+
+G_MODULE_EXPORT void
+peas_register_types (PeasObjectModule *module)
+{
+  peas_object_module_register_extension_type (module,
+                                              PEAS_TYPE_PLUGIN_LOADER,
+                                              PEAS_TYPE_PLUGIN_LOADER_LUA);
+}
+
+static gboolean
+_lua_add_package_path (lua_State   *L,
+                       const gchar *package_path)
+{
+  luaL_checkstack (L, 3, "");
+
+  lua_getglobal (L, "package");
+  lua_getfield (L, -1, "path");
+
+  if (!lua_isstring (L, -1))
+    {
+      g_warning ("Invalid Lua package.path: %s", lua_tostring (L, -1));
+
+      /* Pop path and package */
+      lua_pop (L, 2);
+      return FALSE;
+    }
+
+  /* ";package_path/?.lua;package_path/?/init.lua" */
+  lua_pushliteral (L, ";");
+  lua_pushstring (L, package_path);
+  lua_pushliteral (L, G_DIR_SEPARATOR_S "?.lua;");
+  lua_pushstring (L, package_path);
+  lua_pushliteral (L, G_DIR_SEPARATOR_S "?" G_DIR_SEPARATOR_S "init.lua");
+  lua_concat (L, 5);
+
+  if (strstr (lua_tostring (L, -2), lua_tostring (L, -1)) != NULL)
+    {
+      /* Pop new path and path */
+      lua_pop (L, 2);
+    }
+  else
+    {
+      /* Update package.path */
+      lua_concat (L, 2);
+      lua_setfield (L, -2, "path");
+    }
+
+  /* Pop package */
+  lua_pop (L, 1);
+  return TRUE;
+}
+
+static gboolean
+_lua_pushinstance (lua_State   *L,
+                   const gchar *namespace_,
+                   const gchar *name,
+                   GType        gtype,
+                   gpointer     instance)
+{
+  luaL_checkstack (L, 3, "");
+
+  if (!peas_lua_utils_require (L, "lgi"))
+    return FALSE;
+
+  lua_getfield (L, -1, namespace_);
+  lua_getfield (L, -1, name);
+
+  /* Remove the namespace and lgi's module table */
+  lua_replace (L, -2);
+  lua_replace (L, -2);
+
+  lua_pushlightuserdata (L, instance);
+  lua_pushboolean (L, FALSE);
+
+  /* new(addr[, already_own[, no_sink]]) */
+  if (lua_pcall (L, 2, 1, 0) != 0)
+    {
+      g_warning ("Failed to create Lua object of type '%s': %s",
+                 g_type_name (gtype), lua_tostring (L, -1));
+
+      /* Pop the error */
+      lua_pop (L, 1);
+      return FALSE;
+    }
+
+  /* Check that the Lua object was created correctly */
+  lua_getfield (L, -1, "_native");
+  g_assert (lua_islightuserdata (L, -1));
+  g_assert (lua_touserdata (L, -1) == instance);
+  lua_pop (L, 1);
+
+  return TRUE;
+}
+
+static GType
+_lua_get_gtype (lua_State *L,
+                int        index)
+{
+  GType gtype = G_TYPE_INVALID;
+
+  luaL_checkstack (L, 1, "");
+
+  lua_getfield (L, index, "_gtype");
+
+  if (lua_type (L, -1) == LUA_TLIGHTUSERDATA)
+    gtype = (GType) lua_touserdata (L, -1);
+
+  /* Pop _gtype */
+  lua_pop (L, 1);
+  return gtype;
+}
+
+static GType
+_lua_find_extension_type (lua_State      *L,
+                          PeasPluginInfo *info,
+                          GType           exten_type)
+{
+  GType found_type = G_TYPE_INVALID;
+
+  luaL_checkstack (L, 3, "");
+
+  /* Get the module's table */
+  lua_pushlightuserdata (L, info);
+  lua_rawget (L, LUA_REGISTRYINDEX);
+
+  /* Must always have a valid key */
+  lua_pushnil (L);
+  while (lua_next (L, -2) != 0 && found_type == G_TYPE_INVALID)
+    {
+      if (lua_istable (L, -1))
+        {
+          found_type = _lua_get_gtype (L, -1);
+
+          if (found_type != G_TYPE_INVALID &&
+              !g_type_is_a (found_type, exten_type))
+            {
+              found_type = G_TYPE_INVALID;
+            }
+        }
+
+      /* Pop the value but keep the key for the next iteration */
+      lua_pop (L, 1);
+    }
+
+  /* Pop the module's table */
+  lua_pop (L, 1);
+  return found_type;
+}
+
+static gboolean
+peas_plugin_loader_lua_provides_extension (PeasPluginLoader *loader,
+                                           PeasPluginInfo   *info,
+                                           GType             exten_type)
+{
+  PeasPluginLoaderLua *lua_loader = PEAS_PLUGIN_LOADER_LUA (loader);
+  lua_State *L = lua_loader->priv->L;
+  GType extension_type;
+
+  extension_type = _lua_find_extension_type (L, info, exten_type);
+
+  return extension_type != G_TYPE_INVALID;
+}
+
+static PeasExtension *
+peas_plugin_loader_lua_create_extension (PeasPluginLoader *loader,
+                                         PeasPluginInfo   *info,
+                                         GType             exten_type,
+                                         guint             n_parameters,
+                                         GParameter       *parameters)
+{
+  PeasPluginLoaderLua *lua_loader = PEAS_PLUGIN_LOADER_LUA (loader);
+  lua_State *L = lua_loader->priv->L;
+  GType the_type;
+  GObject *object;
+
+  the_type = _lua_find_extension_type (L, info, exten_type);
+  if (the_type == G_TYPE_INVALID)
+    return NULL;
+
+  if (!g_type_is_a (the_type, exten_type))
+    {
+      g_warn_if_fail (g_type_is_a (the_type, exten_type));
+      return NULL;
+    }
+
+  object = g_object_newv (the_type, n_parameters, parameters);
+  if (object == NULL)
+    return NULL;
+
+  /* As we do not instantiate a PeasExtensionWrapper, we have to remember
+   * somehow which interface we are instantiating, to make it possible to use
+   * the deprecated peas_extension_get_extension_type() method.
+   */
+  g_object_set_data (object, "peas-extension-type",
+                     GUINT_TO_POINTER (exten_type));
+
+  luaL_checkstack (L, 3, "");
+
+  if (!_lua_pushinstance (L, "GObject", "Object", the_type, object))
+    {
+      g_clear_object (&object);
+      return NULL;
+    }
+
+  lua_getfield (L, -1, "priv");
+
+  if (!_lua_pushinstance (L, "Peas", "PluginInfo",
+                          PEAS_TYPE_PLUGIN_INFO, info))
+    {
+      g_clear_object (&object);
+    }
+  else
+    {
+      /* Set the plugin info as self.priv.plugin_info */
+      lua_setfield (L, -2, "plugin_info");
+    }
+
+  /* Pop priv and object */
+  lua_pop (L, 2);
+
+  return object;
+}
+
+static gboolean
+peas_plugin_loader_lua_load (PeasPluginLoader *loader,
+                             PeasPluginInfo   *info)
+{
+  PeasPluginLoaderLua *lua_loader = PEAS_PLUGIN_LOADER_LUA (loader);
+  lua_State *L = lua_loader->priv->L;
+  gboolean success;
+
+  luaL_checkstack (L, 2, "");
+
+  /* Get the module's table */
+  lua_pushlightuserdata (L, info);
+  lua_rawget (L, LUA_REGISTRYINDEX);
+
+  if (!lua_isnil (L, -1))
+    {
+      success = lua_istable (L, -1);
+    }
+  else
+    {
+      const gchar *module_dir, *module_name;
+
+      module_dir = peas_plugin_info_get_module_dir (info);
+      module_name = peas_plugin_info_get_module_name (info);
+
+      /* Must push the key back onto the stack */
+      lua_pushlightuserdata (L, info);
+
+      if (!_lua_add_package_path (L, module_dir) ||
+          !peas_lua_utils_require (L, module_name))
+        {
+          /* Push something that isn't a table */
+          lua_pushboolean (L, FALSE);
+        }
+
+      success = lua_istable (L, -1);
+      lua_rawset (L, LUA_REGISTRYINDEX);
+    }
+
+  /* Pop the module's table */
+  lua_pop (L, 1);
+
+  return success;
+}
+
+static void
+peas_plugin_loader_lua_unload (PeasPluginLoader *loader,
+                               PeasPluginInfo   *info)
+{
+  /* Lua always keeps a reference anyways */
+}
+
+static void
+peas_plugin_loader_lua_garbage_collect (PeasPluginLoader *loader)
+{
+  PeasPluginLoaderLua *lua_loader = PEAS_PLUGIN_LOADER_LUA (loader);
+
+  lua_gc (lua_loader->priv->L, LUA_GCCOLLECT, 0);
+}
+
+static int
+atpanic_handler (lua_State *L)
+{
+  G_BREAKPOINT ();
+  return 0;
+}
+
+static gboolean
+peas_plugin_loader_lua_initialize (PeasPluginLoader *loader)
+{
+  PeasPluginLoaderLua *lua_loader = PEAS_PLUGIN_LOADER_LUA (loader);
+  lua_State *L;
+
+  L = luaL_newstate ();
+  if (L == NULL)
+    {
+      g_critical ("Failed to allocate lua_State");
+      return FALSE;
+    }
+
+  luaL_openlibs (L);
+
+  if (!peas_lua_utils_require (L, "lgi") ||
+      !peas_lua_utils_check_version (L,
+                                     LGI_MAJOR_VERSION,
+                                     LGI_MINOR_VERSION,
+                                     LGI_MICRO_VERSION))
+    {
+      /* Already warned */
+      lua_close (L);
+      return FALSE;
+    }
+
+  /* Pop lgi's module table */
+  lua_pop (L, 1);
+
+  if (g_getenv ("PEAS_LUA_DEBUG") != NULL)
+    {
+      lua_atpanic (L, atpanic_handler);
+    }
+
+  lua_loader->priv->L = L;
+  return TRUE;
+}
+
+static void
+peas_plugin_loader_lua_init (PeasPluginLoaderLua *lua_loader)
+{
+  lua_loader->priv = G_TYPE_INSTANCE_GET_PRIVATE (lua_loader,
+                                                  PEAS_TYPE_PLUGIN_LOADER_LUA,
+                                                  PeasPluginLoaderLuaPrivate);
+}
+
+static void
+peas_plugin_loader_lua_finalize (GObject *object)
+{
+  PeasPluginLoaderLua *lua_loader = PEAS_PLUGIN_LOADER_LUA (object);
+
+  g_clear_pointer (&lua_loader->priv->L, (GDestroyNotify) lua_close);
+
+  G_OBJECT_CLASS (peas_plugin_loader_lua_parent_class)->finalize (object);
+}
+
+static void
+peas_plugin_loader_lua_class_init (PeasPluginLoaderLuaClass *klass)
+{
+  GObjectClass *object_class = G_OBJECT_CLASS (klass);
+  PeasPluginLoaderClass *loader_class = PEAS_PLUGIN_LOADER_CLASS (klass);
+
+  object_class->finalize = peas_plugin_loader_lua_finalize;
+
+  loader_class->initialize = peas_plugin_loader_lua_initialize;
+  loader_class->load = peas_plugin_loader_lua_load;
+  loader_class->unload = peas_plugin_loader_lua_unload;
+  loader_class->create_extension = peas_plugin_loader_lua_create_extension;
+  loader_class->provides_extension = peas_plugin_loader_lua_provides_extension;
+  loader_class->garbage_collect = peas_plugin_loader_lua_garbage_collect;
+
+  g_type_class_add_private (object_class, sizeof (PeasPluginLoaderLuaPrivate));
+}
diff --git a/loaders/lua5.1/peas-plugin-loader-lua.h b/loaders/lua5.1/peas-plugin-loader-lua.h
new file mode 100644
index 0000000..efb64a1
--- /dev/null
+++ b/loaders/lua5.1/peas-plugin-loader-lua.h
@@ -0,0 +1,60 @@
+/*
+ * peas-plugin-loader-lua.h
+ * This file is part of libpeas
+ *
+ * Copyright (C) 2014 - 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., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+ */
+
+#ifndef __PEAS_PLUGIN_LOADER_LUA_H__
+#define __PEAS_PLUGIN_LOADER_LUA_H__
+
+#include <libpeas/peas-plugin-loader.h>
+#include <libpeas/peas-object-module.h>
+
+G_BEGIN_DECLS
+
+#define PEAS_TYPE_PLUGIN_LOADER_LUA             (peas_plugin_loader_lua_get_type ())
+#define PEAS_PLUGIN_LOADER_LUA(obj)             (G_TYPE_CHECK_INSTANCE_CAST ((obj), 
PEAS_TYPE_PLUGIN_LOADER_LUA, PeasPluginLoaderLua))
+#define PEAS_PLUGIN_LOADER_LUA_CONST(obj)       (G_TYPE_CHECK_INSTANCE_CAST ((obj), 
PEAS_TYPE_PLUGIN_LOADER_LUA, PeasPluginLoaderLua const))
+#define PEAS_PLUGIN_LOADER_LUA_CLASS(klass)     (G_TYPE_CHECK_CLASS_CAST ((klass), 
PEAS_TYPE_PLUGIN_LOADER_LUA, PeasPluginLoaderLuaClass))
+#define PEAS_IS_PLUGIN_LOADER_LUA(obj)          (G_TYPE_CHECK_INSTANCE_TYPE ((obj), 
PEAS_TYPE_PLUGIN_LOADER_LUA))
+#define PEAS_IS_PLUGIN_LOADER_LUA_CLASS(klass)  (G_TYPE_CHECK_CLASS_TYPE ((klass), 
PEAS_TYPE_PLUGIN_LOADER_LUA))
+#define PEAS_PLUGIN_LOADER_LUA_GET_CLASS(obj)   (G_TYPE_INSTANCE_GET_CLASS ((obj), 
PEAS_TYPE_PLUGIN_LOADER_LUA, PeasPluginLoaderLuaClass))
+
+typedef struct _PeasPluginLoaderLua         PeasPluginLoaderLua;
+typedef struct _PeasPluginLoaderLuaClass    PeasPluginLoaderLuaClass;
+typedef struct _PeasPluginLoaderLuaPrivate  PeasPluginLoaderLuaPrivate;
+
+struct _PeasPluginLoaderLua {
+  PeasPluginLoader parent;
+
+  PeasPluginLoaderLuaPrivate *priv;
+};
+
+struct _PeasPluginLoaderLuaClass {
+  PeasPluginLoaderClass parent_class;
+};
+
+GType                    peas_plugin_loader_lua_get_type  (void) G_GNUC_CONST;
+
+/* All the loaders must implement this function */
+G_MODULE_EXPORT void     peas_register_types              (PeasObjectModule *module);
+
+G_END_DECLS
+
+#endif /* __PEAS_PLUGIN_LOADER_LUA_H__ */
+
diff --git a/peas-demo/peas-demo.c b/peas-demo/peas-demo.c
index b1154f7..6a91eaf 100644
--- a/peas-demo/peas-demo.c
+++ b/peas-demo/peas-demo.c
@@ -128,6 +128,7 @@ main (int    argc,
 
   /* We don't care about leaking memory */
   g_setenv ("PEAS_ALLOW_ALL_LOADERS", "1", TRUE);
+  peas_engine_enable_loader (engine, "lua5.1");
   peas_engine_enable_loader (engine, "python3");
 
   if (run_from_build_dir)
diff --git a/peas-demo/plugins/Makefile.am b/peas-demo/plugins/Makefile.am
index 273b32b..3c5d222 100644
--- a/peas-demo/plugins/Makefile.am
+++ b/peas-demo/plugins/Makefile.am
@@ -1,5 +1,9 @@
 SUBDIRS = helloworld secondtime
 
+if ENABLE_LUA51
+SUBDIRS += luahello
+endif
+
 if ENABLE_PYTHON3
 SUBDIRS += pythonhello
 endif
diff --git a/peas-demo/plugins/luahello/Makefile.am b/peas-demo/plugins/luahello/Makefile.am
new file mode 100644
index 0000000..e6aef72
--- /dev/null
+++ b/peas-demo/plugins/luahello/Makefile.am
@@ -0,0 +1,8 @@
+plugindir = $(libdir)/peas-demo/plugins/luahello
+
+plugin_lua = \
+       luahello.py
+
+plugin_DATA = luahello.plugin
+
+EXTRA_DIST = $(plugin_DATA)
diff --git a/peas-demo/plugins/luahello/luahello.lua b/peas-demo/plugins/luahello/luahello.lua
new file mode 100644
index 0000000..8d50bc9
--- /dev/null
+++ b/peas-demo/plugins/luahello/luahello.lua
@@ -0,0 +1,50 @@
+local lgi = require 'lgi'
+
+local GObject = lgi.GObject
+local Gtk = lgi.Gtk
+local Peas = lgi.Peas
+local PeasGtk = lgi.PeasGtk
+
+
+LuaHelloPlugin = GObject.Object:derive('LuaHelloPlugin', {
+    Peas.Activatable
+})
+
+LuaHelloPlugin._property.object =
+    GObject.ParamSpecObject('object', 'object', 'object',
+                            GObject.Object._gtype,
+                            { GObject.ParamFlags.READABLE,
+                              GObject.ParamFlags.WRITABLE })
+
+function LuaHelloPlugin:do_activate()
+    window = self.priv.object
+    print('LuaHelloPlugin:do_activate', tostring(window))
+    self.priv.label = Gtk.Label.new('Lua Says Hello!')
+    self.priv.label:show()
+    window:get_child():pack_start(self.priv.label, true, true, 0)
+end
+
+function LuaHelloPlugin:do_deactivate()
+    window = self.priv.object
+    print('LuaHelloPlugin:do_deactivate', tostring(window))
+    window:get_child():remove(self.priv.label)
+    self.priv.label:destroy()
+end
+
+function LuaHelloPlugin:do_update_state()
+    window = self.priv.object
+    print('LuaHelloPlugin:do_update_state', tostring(window))
+end
+
+
+LuaHelloConfigurable = GObject.Object:derive('LuaHelloConfigurable', {
+    PeasGtk.Configurable
+})
+
+function LuaHelloConfigurable:do_create_configure_widget()
+    return Gtk.Label.new('Lua Hello configure widget')
+end
+
+return { LuaHelloPlugin, LuaHelloConfigurable }
+
+-- ex:set ts=4 et sw=4 ai:
diff --git a/peas-demo/plugins/luahello/luahello.plugin b/peas-demo/plugins/luahello/luahello.plugin
new file mode 100644
index 0000000..dddfe21
--- /dev/null
+++ b/peas-demo/plugins/luahello/luahello.plugin
@@ -0,0 +1,7 @@
+[Plugin]
+Module=luahello
+Loader=lua5.1
+Name=Lua Says Hello
+Description=Inserts a box containing "Lua Says Hello" in every windows.
+Authors=Garrett Regier <garrettregier gmail com>
+Copyright=Copyright © 2014 Garrett Regier
diff --git a/tests/libpeas/Makefile.am b/tests/libpeas/Makefile.am
index 0015ff0..6d6696f 100644
--- a/tests/libpeas/Makefile.am
+++ b/tests/libpeas/Makefile.am
@@ -25,6 +25,13 @@ TEST_PROGS += \
        extension-set   \
        plugin-info
 
+if ENABLE_LUA51
+TEST_PROGS               += extension-lua51
+extension_lua51_SOURCES   = extension-lua.c
+extension_lua51_CFLAGS    = $(LUA51_CFLAGS)
+extension_lua51_LDADD     = $(LDADD) $(LUA51_LIBS)
+endif
+
 if ENABLE_PYTHON2
 TEST_PROGS               += extension-python
 extension_python_SOURCES  = extension-py.c
diff --git a/tests/libpeas/extension-lua.c b/tests/libpeas/extension-lua.c
new file mode 100644
index 0000000..6cfd97a
--- /dev/null
+++ b/tests/libpeas/extension-lua.c
@@ -0,0 +1,213 @@
+/*
+ * extension-lua.c
+ * This file is part of libpeas
+ *
+ * Copyright (C) 2014 - 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., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+ */
+
+#ifdef HAVE_CONFIG_H
+#include <config.h>
+#endif
+
+#include <lua.h>
+#include <lauxlib.h>
+
+#include <libpeas/peas-activatable.h>
+#include "libpeas/peas-engine-priv.h"
+
+#include "testing/testing-extension.h"
+#include "introspection/introspection-base.h"
+
+
+/* We must stop and start the garbage collector
+ * when testing reference counts otherwise issues
+ * will occur if the garbage collector runs preemptively.
+ */
+static void
+set_garbage_collector_state (PeasEngine     *engine,
+                             PeasPluginInfo *info,
+                             gboolean        running)
+{
+  PeasExtension *extension;
+
+  extension = peas_engine_create_extension (engine, info,
+                                            PEAS_TYPE_ACTIVATABLE,
+                                            NULL);
+
+  if (running)
+    {
+      /* collectgarbage('restart') */
+      peas_activatable_activate (PEAS_ACTIVATABLE (extension));
+    }
+  else
+    {
+      /* collectgarbage('stop') */
+      peas_activatable_deactivate (PEAS_ACTIVATABLE (extension));
+    }
+
+  g_object_unref (extension);
+}
+
+static void
+test_extension_lua_instance_refcount (PeasEngine     *engine,
+                                      PeasPluginInfo *info)
+{
+  PeasExtension *extension;
+
+  set_garbage_collector_state (engine, info, FALSE);
+
+  extension = peas_engine_create_extension (engine, info,
+                                            PEAS_TYPE_ACTIVATABLE,
+                                            NULL);
+  g_object_add_weak_pointer (extension, (gpointer *) &extension);
+
+  g_assert (PEAS_IS_EXTENSION (extension));
+
+  /* The Lua wrapper created around the extension
+   * object should have increased its refcount by 1.
+   */
+  g_assert_cmpint (extension->ref_count, ==, 2);
+
+  /* The Lua wrapper around the extension has been garbage collected */
+  peas_engine_garbage_collect (engine);
+  g_assert_cmpint (G_OBJECT (extension)->ref_count, ==, 1);
+
+  /* Create a new Lua wrapper around the extension */
+  peas_activatable_update_state (PEAS_ACTIVATABLE (extension));
+  g_assert_cmpint (G_OBJECT (extension)->ref_count, ==, 2);
+
+  /* The Lua wrapper still exists */
+  g_object_unref (extension);
+  g_assert_cmpint (extension->ref_count, ==, 1);
+
+  /* The Lua wrapper around the extension has been garbage collected */
+  peas_engine_garbage_collect (engine);
+  g_assert (extension == NULL);
+
+  set_garbage_collector_state (engine, info, TRUE);
+}
+
+static void
+test_extension_lua_activatable_subject_refcount (PeasEngine     *engine,
+                                                 PeasPluginInfo *info)
+{
+  PeasExtension *extension;
+  GObject *object;
+
+  set_garbage_collector_state (engine, info, FALSE);
+
+  /* Create the 'object' property value, to be similar to a GtkWindow
+   * instance: a sunk GInitiallyUnowned object.
+   */
+  object = g_object_new (G_TYPE_INITIALLY_UNOWNED, NULL);
+  g_object_add_weak_pointer (object, (gpointer *) &object);
+  g_object_ref_sink (object);
+  g_assert_cmpint (object->ref_count, ==, 1);
+
+  /* We pre-create the wrapper to make it easier to check reference count */
+  extension = peas_engine_create_extension (engine, info,
+                                            PEAS_TYPE_ACTIVATABLE,
+                                            "object", object,
+                                            NULL);
+  g_object_add_weak_pointer (extension, (gpointer *) &extension);
+
+  g_assert (PEAS_IS_EXTENSION (extension));
+
+  /* The Lua wrapper created around our dummy
+   * object should have increased its refcount by 1.
+   */
+  peas_engine_garbage_collect (engine);
+  g_assert_cmpint (object->ref_count, ==, 2);
+
+  g_object_unref (extension);
+  g_assert (extension == NULL);
+
+  /* We unreffed the extension, so it should have been
+   * destroyed and our dummy object's refcount should be back to 1.
+   */
+  peas_engine_garbage_collect (engine);
+  g_assert_cmpint (object->ref_count, ==, 1);
+
+  g_object_unref (object);
+  g_assert (object == NULL);
+
+  set_garbage_collector_state (engine, info, TRUE);
+}
+
+#if GLIB_CHECK_VERSION (2, 38, 0)
+static void
+test_extension_lua_nonexistent (void)
+{
+  g_test_trap_subprocess (EXTENSION_TEST_NAME (lua5.1,
+                                               "nonexistent/subprocess"),
+                          0, G_TEST_SUBPROCESS_INHERIT_STDERR);
+  g_test_trap_assert_passed ();
+  g_test_trap_assert_stderr ("");
+}
+
+static void
+test_extension_lua_nonexistent_subprocess (PeasEngine *engine)
+{
+  PeasPluginInfo *info;
+
+  testing_util_push_log_hook ("Error failed to load Lua module "
+                              "'extension-lua51-nonexistent'*");
+  testing_util_push_log_hook ("Error loading plugin "
+                              "'extension-lua51-nonexistent'");
+
+  info = peas_engine_get_plugin_info (engine, "extension-lua51-nonexistent");
+
+  g_assert (!peas_engine_load_plugin (engine, info));
+}
+#endif
+
+int
+main (int   argc,
+      char *argv[])
+{
+  testing_init (&argc, &argv);
+
+  /* Only test the basics */
+  testing_extension_basic ("lua5.1");
+
+  /* We still need to add the callable tests
+   * because of peas_extension_call()
+   */
+  testing_extension_callable ("lua5.1");
+
+#undef EXTENSION_TEST
+#undef EXTENSION_TEST_FUNC
+  
+#define EXTENSION_TEST(loader, path, func) \
+  testing_extension_add (EXTENSION_TEST_NAME (loader, path), \
+                         (gpointer) test_extension_lua_##func)
+
+#define EXTENSION_TEST_FUNC(loader, path, func) \
+  g_test_add_func (EXTENSION_TEST_NAME (loader, path), \
+                   (gpointer) test_extension_lua_##func)
+
+  EXTENSION_TEST (lua5.1, "instance-refcount", instance_refcount);
+  EXTENSION_TEST (lua5.1, "activatable-subject-refcount",
+                  activatable_subject_refcount);
+
+#if GLIB_CHECK_VERSION (2, 38, 0)
+  EXTENSION_TEST_FUNC (lua5.1, "nonexistent", nonexistent);
+  EXTENSION_TEST (lua5.1, "nonexistent/subprocess", nonexistent_subprocess);
+#endif
+
+  return testing_extension_run_tests ();
+}
diff --git a/tests/libpeas/plugins/Makefile.am b/tests/libpeas/plugins/Makefile.am
index db49e17..2dfddb2 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_LUA51
+SUBDIRS += extension-lua
+endif
+
 if ENABLE_PYTHON3
 SUBDIRS += extension-python
 else
@@ -12,6 +16,7 @@ endif
 
 noinst_PLUGIN = \
        extension-c-nonexistent.plugin          \
+       extension-lua51-nonexistent.plugin      \
        extension-python-nonexistent.plugin     \
        extension-python3-nonexistent.plugin    \
        info-missing-module.plugin              \
diff --git a/tests/libpeas/plugins/extension-lua/Makefile.am b/tests/libpeas/plugins/extension-lua/Makefile.am
new file mode 100644
index 0000000..722ba7e
--- /dev/null
+++ b/tests/libpeas/plugins/extension-lua/Makefile.am
@@ -0,0 +1,7 @@
+include $(top_srcdir)/tests/Makefile.plugin
+
+noinst_DATA = \
+       extension-lua51.gschema.xml     \
+       extension-lua51.plugin          \
+       extension-lua51.lua
+
diff --git a/tests/libpeas/plugins/extension-lua/extension-lua51.gschema.xml 
b/tests/libpeas/plugins/extension-lua/extension-lua51.gschema.xml
new file mode 100644
index 0000000..d6e9a76
--- /dev/null
+++ b/tests/libpeas/plugins/extension-lua/extension-lua51.gschema.xml
@@ -0,0 +1,9 @@
+<schemalist>
+  <schema id="extension-lua51" path="/org/gnome/libpeas/tests/extension-lua51/">
+    <key name="a-setting" type="s">
+      <default>'Blah'</default>
+      <summary>Just a setting.</summary>
+      <description>Just a setting.</description>
+    </key>
+  </schema>
+</schemalist>
diff --git a/tests/libpeas/plugins/extension-lua/extension-lua51.lua 
b/tests/libpeas/plugins/extension-lua/extension-lua51.lua
new file mode 100644
index 0000000..3bb6acf
--- /dev/null
+++ b/tests/libpeas/plugins/extension-lua/extension-lua51.lua
@@ -0,0 +1,91 @@
+local lgi = require 'lgi'
+
+local GObject = lgi.GObject
+local Introspection = lgi.Introspection
+local Peas = lgi.Peas
+
+
+local ExtensionLuaPlugin = GObject.Object:derive('ExtensionLuaPlugin', {
+    Peas.Activatable,
+    Introspection.Base,
+    Introspection.Callable,
+    Introspection.HasPrerequisite,
+    Introspection.PropertiesPrerequisite,
+    Introspection.Properties
+})
+
+ExtensionLuaPlugin._property.object =
+    GObject.ParamSpecObject('object', 'object', 'object',
+                            GObject.Object._gtype,
+                            { GObject.ParamFlags.READABLE,
+                              GObject.ParamFlags.WRITABLE })
+
+ExtensionLuaPlugin._property.construct_only =
+    GObject.ParamSpecString('construct-only', 'construct-only',
+                            'construct-only',
+                            '',
+                            { GObject.ParamFlags.CONSTRUCT_ONLY,
+                              GObject.ParamFlags.READABLE,
+                              GObject.ParamFlags.WRITABLE })
+
+ExtensionLuaPlugin._property.read_only =
+    GObject.ParamSpecString('read-only', 'read-only', 'read-only',
+                            '',
+                            { GObject.ParamFlags.READABLE })
+
+ExtensionLuaPlugin._property.write_only =
+    GObject.ParamSpecString('write-only', 'write-only', 'write-only',
+                            '',
+                            { GObject.ParamFlags.WRITABLE })
+
+ExtensionLuaPlugin._property.readwrite =
+    GObject.ParamSpecString('readwrite', 'readwrite', 'readwrite',
+                            '',
+                            { GObject.ParamFlags.READABLE,
+                              GObject.ParamFlags.WRITABLE })
+
+ExtensionLuaPlugin._property.prerequisite =
+    GObject.ParamSpecString('prerequisite', 'prerequisite', 'prerequisite',
+                            '',
+                            { GObject.ParamFlags.CONSTRUCT_ONLY,
+                              GObject.ParamFlags.READABLE,
+                              GObject.ParamFlags.WRITABLE })
+
+function ExtensionLuaPlugin:do_activate()
+    collectgarbage('restart')
+end
+
+function ExtensionLuaPlugin:do_deactivate()
+    collectgarbage('stop')
+end
+
+function ExtensionLuaPlugin:do_update_state()
+end
+
+function ExtensionLuaPlugin:do_get_plugin_info()
+    return self.priv.plugin_info
+end
+
+function ExtensionLuaPlugin:do_get_settings()
+    return self.priv.plugin_info:get_settings(nil)
+end
+
+function ExtensionLuaPlugin:do_call_no_args()
+end
+
+function ExtensionLuaPlugin:do_call_with_return()
+    return 'Hello, World!'
+end
+
+function ExtensionLuaPlugin:do_call_single_arg()
+    return true
+end
+
+function ExtensionLuaPlugin:do_call_multi_args(in_, inout)
+    return inout, in_
+end
+
+ExtensionLuaPlugin()
+return { ExtensionLuaPlugin }
+
+-- ex:set ts=4 et sw=4 ai:
diff --git a/tests/libpeas/plugins/extension-lua/extension-lua51.plugin 
b/tests/libpeas/plugins/extension-lua/extension-lua51.plugin
new file mode 100644
index 0000000..ff4b51a
--- /dev/null
+++ b/tests/libpeas/plugins/extension-lua/extension-lua51.plugin
@@ -0,0 +1,7 @@
+[Plugin]
+Module=extension-lua51
+Loader=lua5.1
+Name=Extension lua5.1
+Description=This plugin is for the lua5.1 PeasExtension tests.
+Authors=Garrett Regier
+Copyright=Copyright © 2014 Garrett Regier
diff --git a/tests/libpeas/plugins/extension-lua51-nonexistent.plugin 
b/tests/libpeas/plugins/extension-lua51-nonexistent.plugin
new file mode 100644
index 0000000..0294240
--- /dev/null
+++ b/tests/libpeas/plugins/extension-lua51-nonexistent.plugin
@@ -0,0 +1,7 @@
+[Plugin]
+Module=extension-lua51-nonexistent
+Loader=lua5.1
+Name=Extension lua5.1 Nonexistent
+Description=This plugin is nonexistent.
+Authors=Garrett Regier
+Copyright=Copyright © 2014 Garrett Regier
diff --git a/tests/libpeas/testing/testing-extension.c b/tests/libpeas/testing/testing-extension.c
index 12f1db3..e1f6a59 100644
--- a/tests/libpeas/testing/testing-extension.c
+++ b/tests/libpeas/testing/testing-extension.c
@@ -171,6 +171,7 @@ test_extension_create_invalid (PeasEngine     *engine,
 
   /* This cannot be tested in PyGI and Seed's log handler messes this up */
   if (g_strcmp0 (extension_plugin, "extension-c") != 0 &&
+      g_strcmp0 (extension_plugin, "extension-lua51") != 0 &&
       g_strcmp0 (extension_plugin, "extension-python") != 0 &&
       g_strcmp0 (extension_plugin, "extension-python3") != 0)
     {
@@ -387,9 +388,19 @@ test_extension_call_multi_args (PeasEngine     *engine,
 void
 testing_extension_basic (const gchar *loader)
 {
+  gint i, j;
+  gchar *loader_name;
   PeasEngine *engine;
 
-  extension_plugin = g_strdup_printf ("extension-%s", loader);
+  loader_name = g_new0 (gchar, strlen (loader));
+  for (i = 0, j = 0; loader[i] != '\0'; ++i)
+    {
+      if (loader[i] != '.')
+        loader_name[j++] = loader[i];
+    }
+
+  extension_plugin = g_strdup_printf ("extension-%s", loader_name);
+  g_free (loader_name);
 
   engine = testing_engine_new ();
   peas_engine_enable_loader (engine, loader);


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