[libpeas] Use Lua to implement the plugin loader's logic



commit d2bed913817ef7bd9e018b3f63a36369aac74b9f
Author: Garrett Regier <garrettregier gmail com>
Date:   Mon Jan 19 00:33:05 2015 -0800

    Use Lua to implement the plugin loader's logic
    
    This allows us to avoid using Lua's C API and have a
    more understandable implementation.
    
    https://bugzilla.gnome.org/show_bug.cgi?id=742410

 .gitignore                              |    2 +
 configure.ac                            |    2 +
 loaders/lua5.1/Makefile.am              |   21 +++
 loaders/lua5.1/peas-lua-compile.lua     |   60 ++++++
 loaders/lua5.1/peas-lua-internal.c      |  202 +++++++++++++++++++++
 loaders/lua5.1/peas-lua-internal.h      |   40 ++++
 loaders/lua5.1/peas-lua-internal.lua    |  162 +++++++++++++++++
 loaders/lua5.1/peas-lua-utils.c         |   57 ++++++-
 loaders/lua5.1/peas-lua-utils.h         |    6 +-
 loaders/lua5.1/peas-lua.gresource.xml   |    6 +
 loaders/lua5.1/peas-plugin-loader-lua.c |  297 ++++++-------------------------
 tests/libpeas/extension-lua.c           |    4 +-
 12 files changed, 612 insertions(+), 247 deletions(-)
---
diff --git a/.gitignore b/.gitignore
index 9a7932a..0accc2c 100644
--- a/.gitignore
+++ b/.gitignore
@@ -1,6 +1,7 @@
 *.bak
 *.lo
 *.la
+*.luac
 *.o
 *.pyc
 *.gir
@@ -51,6 +52,7 @@ Makefile.in
 /intltool-update.in
 /libtool
 /ltmain.sh
+/loaders/lua5.1/peas-lua-resources.c
 /loaders/python/peas-python-resources.c
 /loaders/python3/peas-python3-resources.c
 /m4
diff --git a/configure.ac b/configure.ac
index 992eea4..28d145f 100644
--- a/configure.ac
+++ b/configure.ac
@@ -323,8 +323,10 @@ else
     else
         AC_MSG_RESULT([$found_lua51 ($with_lua51)])
 
+        LUA51_BIN="$with_lua51"
         LUA51_CFLAGS=`$PKG_CONFIG --cflags $with_lua51`
         LUA51_LIBS=`$PKG_CONFIG --libs $with_lua51`
+        AC_SUBST(LUA51_BIN)
         AC_SUBST(LUA51_CFLAGS)
         AC_SUBST(LUA51_LIBS)
 
diff --git a/loaders/lua5.1/Makefile.am b/loaders/lua5.1/Makefile.am
index 6a71650..6be90ea 100644
--- a/loaders/lua5.1/Makefile.am
+++ b/loaders/lua5.1/Makefile.am
@@ -13,6 +13,9 @@ AM_CPPFLAGS = \
 loader_LTLIBRARIES = liblua51loader.la
 
 liblua51loader_la_SOURCES = \
+       peas-lua-internal.c             \
+       peas-lua-internal.h             \
+       peas-lua-resources.c            \
        peas-plugin-loader-lua.c        \
        peas-plugin-loader-lua.h        \
        peas-lua-utils.c                \
@@ -27,5 +30,23 @@ liblua51loader_la_LIBADD = \
        $(PEAS_LIBS)                            \
        $(LUA51_LIBS)
 
+%.luac: %.lua
+       $(AM_V_GEN) $(LUA51_BIN) $(srcdir)/peas-lua-compile.lua $< $@
+
+all-local: peas-lua-internal.luac
+
+loader_resources_deps = $(shell $(GLIB_COMPILE_RESOURCES) --sourcedir=$(srcdir) --generate-dependencies 
$(srcdir)/peas-lua.gresource.xml)
+peas-lua-resources.c: $(srcdir)/peas-lua.gresource.xml $(loader_resources_deps)
+       $(AM_V_GEN) $(GLIB_COMPILE_RESOURCES) --internal --target=$@ --sourcedir=$(srcdir) --generate-source 
$(srcdir)/peas-lua.gresource.xml
+
+EXTRA_DIST = \
+       peas-lua-compile.lua            \
+       peas-lua.gresource.xml          \
+       $(loader_resources_deps)
+
+CLEANFILES = \
+       peas-lua-internal.luac  \
+       peas-lua-resources.c
+
 gcov_sources = $(liblua51loader_la_SOURCES)
 include $(top_srcdir)/Makefile.gcov
diff --git a/loaders/lua5.1/peas-lua-compile.lua b/loaders/lua5.1/peas-lua-compile.lua
new file mode 100644
index 0000000..bdad8cb
--- /dev/null
+++ b/loaders/lua5.1/peas-lua-compile.lua
@@ -0,0 +1,60 @@
+--
+--  Copyright (C) 2015 - 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.
+
+local io = require 'io'
+local os = require 'os'
+
+
+local function check(err, format, ...)
+    if err == nil then
+        return
+    end
+
+    io.stderr:write(('Error: %s:\n%s\n'):format(format:format(...), err))
+    os.exit(1)
+end
+
+
+local function main(arg)
+    for i = 1, #arg, 2 do
+        local filename = arg[i]
+        local output = arg[i + 1]
+
+        local input_file, err = io.open(filename, 'rb')
+        check(err, 'Failed to open file "%s"', filename)
+
+         -- Error includes the filename
+        local compiled, err = loadstring(input_file:read('*a'),
+                                         '@' .. filename)
+        check(err, 'Invalid Lua file')
+
+        local f, err = io.open(output, 'wb')
+        check(err, 'Failed to open file "%s"', output)
+
+        local success, err = f:write(string.dump(compiled))
+        check(err, 'Failed to write to "%s"', output)
+
+        local success, err = f:close()
+        check(err, 'Failed to save "%s"', output)
+    end
+end
+
+
+os.exit(main(arg) or 0)
+
+-- ex:ts=4:et:
diff --git a/loaders/lua5.1/peas-lua-internal.c b/loaders/lua5.1/peas-lua-internal.c
new file mode 100644
index 0000000..23d2d6b
--- /dev/null
+++ b/loaders/lua5.1/peas-lua-internal.c
@@ -0,0 +1,202 @@
+/*
+ * peas-lua-internal.c
+ * This file is part of libpeas
+ *
+ * Copyright (C) 2015 - 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-lua-internal.h"
+
+#include <gio/gio.h>
+
+#include <lauxlib.h>
+#include <lualib.h>
+
+#include "peas-lua-utils.h"
+
+
+static gpointer hooks_key = NULL;
+static gpointer failed_err_key = NULL;
+
+
+static int
+failed_fn (lua_State *L)
+{
+  gchar *msg;
+
+  /* The first parameter is the Hooks table instance */
+  luaL_checktype (L, 1, LUA_TTABLE);
+
+  /* The tracebacks have a trailing newline */
+  msg = g_strchomp (g_strdup (luaL_checkstring (L, 2)));
+
+  g_warning ("%s", msg);
+
+  /* peas_lua_internal_call() knows to check for this value */
+  lua_pushlightuserdata (L, &failed_err_key);
+
+  g_free (msg);
+  return lua_error (L);
+}
+
+gboolean
+peas_lua_internal_setup (lua_State *L)
+{
+  GBytes *internal_lua;
+  const gchar *code;
+  gsize code_len;
+
+  /* We don't use the byte-compiled Lua source
+   * because glib-compile-resources cannot output
+   * depends for generated files.
+   *
+   * There are also concerns that the bytecode is
+   * not stable enough between different Lua versions.
+   *
+   * https://bugzilla.gnome.org/show_bug.cgi?id=673101
+   */
+  internal_lua = g_resources_lookup_data ("/org/gnome/libpeas/loaders/"
+                                          "lua5.1/internal.lua",
+                                          G_RESOURCE_LOOKUP_FLAGS_NONE,
+                                          NULL);
+  g_return_val_if_fail (internal_lua != NULL, FALSE);
+
+  code = g_bytes_get_data (internal_lua, &code_len);
+
+  /* Filenames are prefixed with '@' */
+  if (luaL_loadbuffer (L, code, code_len, "@peas-lua-internal.lua") != 0)
+    {
+      g_warning ("Failed to load internal Lua code: %s",
+                 lua_tostring (L, -1));
+
+      /* Pop error */
+      lua_pop (L, 1);
+      g_bytes_unref (internal_lua);
+      return FALSE;
+    }
+
+  g_bytes_unref (internal_lua);
+
+  if (!peas_lua_utils_call (L, 0, 1))
+    {
+      g_warning ("Failed to run internal Lua code: %s",
+                 lua_tostring (L, -1));
+
+      /* Pop error */
+      lua_pop (L, 1);
+      return FALSE;
+    }
+
+  if (!lua_istable (L, -1))
+    {
+      g_warning ("Invalid result from internal Lua code: %s",
+                 lua_tostring (L, -1));
+
+      /* Pop result */
+      lua_pop (L, 1);
+      return FALSE;
+    }
+
+  /* Set Hooks.failed to failed_fn */
+  lua_pushcfunction (L, failed_fn);
+  lua_setfield (L, -2, "failed");
+
+  /* Set registry[&hooks_key] = hooks */
+  lua_pushlightuserdata (L, &hooks_key);
+  lua_pushvalue (L, -2);
+  lua_rawset (L, LUA_REGISTRYINDEX);
+
+  /* Pop hooks */
+  lua_pop (L, -1);
+  return TRUE;
+}
+
+void
+peas_lua_internal_shutdown (lua_State *L)
+{
+  lua_pushlightuserdata (L, &hooks_key);
+  lua_pushnil (L);
+  lua_rawset (L, LUA_REGISTRYINDEX);
+}
+
+gboolean
+peas_lua_internal_call (lua_State   *L,
+                        const gchar *name,
+                        guint        n_args,
+                        gint         return_type)
+{
+  /* Get the Hooks table */
+  lua_pushlightuserdata (L, &hooks_key);
+  lua_rawget (L, LUA_REGISTRYINDEX);
+
+  /* Get the method */
+  lua_getfield (L, -1, name);
+
+  /* Swap the method and the table */
+  lua_insert (L, -2);
+
+  if (n_args > 0)
+    {
+      /* Before: [args..., method, table]
+       * After:  [method, table, args...]
+       */
+      lua_insert (L, -n_args - 2);
+      lua_insert (L, -n_args - 2);
+    }
+
+  if (!peas_lua_utils_call (L, 1 + n_args, 1))
+    {
+      /* Raised by failed_fn() to prevent printing the error */
+      if (!lua_isuserdata (L, -1) ||
+          lua_touserdata (L, -1) != &failed_err_key)
+        {
+          g_warning ("Failed to run internal Lua hook '%s':\n%s",
+                     name, lua_tostring (L, -1));
+        }
+
+      /* Pop the error */
+      lua_pop (L, 1);
+      return FALSE;
+    }
+
+  if (lua_type (L, -1) != return_type)
+    {
+      /* Don't warn for a nil result */
+      if (lua_type (L, -1) != LUA_TNIL)
+        {
+          g_warning ("Invalid return value for internal Lua hook '%s': "
+                     "expected %s, got: %s (%s)", name,
+                     lua_typename (L, return_type),
+                     lua_typename (L, lua_type (L, -1)),
+                     lua_tostring (L, -1));
+        }
+
+      /* Pop result */
+      lua_pop (L, 1);
+      return FALSE;
+    }
+
+  /* Pop the result if nil */
+  if (return_type == LUA_TNIL)
+    lua_pop (L, 1);
+
+  return TRUE;
+}
diff --git a/loaders/lua5.1/peas-lua-internal.h b/loaders/lua5.1/peas-lua-internal.h
new file mode 100644
index 0000000..d605856
--- /dev/null
+++ b/loaders/lua5.1/peas-lua-internal.h
@@ -0,0 +1,40 @@
+/*
+ * peas-lua-internal.h
+ * This file is part of libpeas
+ *
+ * Copyright (C) 2015 - 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_LUA_INTERNAL_H__
+#define __PEAS_LUA_INTERNAL_H__
+
+#include <glib.h>
+#include <lua.h>
+
+G_BEGIN_DECLS
+
+gboolean  peas_lua_internal_setup    (lua_State   *L);
+void      peas_lua_internal_shutdown (lua_State   *L);
+
+gboolean  peas_lua_internal_call     (lua_State   *L,
+                                      const gchar *name,
+                                      guint        n_args,
+                                      gint         return_type);
+
+G_END_DECLS
+
+#endif /* __PEAS_LUA_INTERNAL_H__ */
diff --git a/loaders/lua5.1/peas-lua-internal.lua b/loaders/lua5.1/peas-lua-internal.lua
new file mode 100644
index 0000000..8f4e60d
--- /dev/null
+++ b/loaders/lua5.1/peas-lua-internal.lua
@@ -0,0 +1,162 @@
+--
+--  Copyright (C) 2015 - 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.
+
+local debug = require 'debug'
+local package = require 'package'
+
+local lgi = require 'lgi'
+local GObject = lgi.GObject
+local Peas = lgi.Peas
+
+
+local Hooks = {}
+Hooks.__index = Hooks
+
+function Hooks.new()
+    local self = { priv = {} }
+    setmetatable(self, Hooks)
+
+    self.priv.module_cache = {}
+    self.priv.extension_cache = {}
+    return self
+end
+
+function Hooks:failed(msg)
+    -- This is implemented by the plugin loader
+    error('Hooks:failed() was not implemented!')
+end
+
+local function add_package_path(package_path)
+    local paths = (';%s/?.lua;%s/?/init.lua'):format(package_path,
+                                                     package_path)
+
+    if not package.path:find(paths, 1, true) then
+        package.path = package.path .. paths
+    end
+end
+
+local function format_plugin_exception(err)
+    -- Format the error even if given a userdata
+    local formatted = debug.traceback(tostring(err), 2)
+
+    if type(formatted) ~= 'string' then
+        return formatted
+    end
+
+    -- Remove all mentions of this file
+    local lines = {}
+    for line in formatted:gmatch('([^\n]+\n?)') do
+        if line:find('peas-lua-internal.lua', 1, true) then
+            break
+        end
+
+        table.insert(lines, line)
+    end
+
+    return table.concat(lines, '')
+end
+
+function Hooks:load(filename, module_dir, module_name)
+    local module = self.priv.module_cache[filename]
+
+    if module ~= nil then
+        return module ~= false
+    end
+
+    if package.loaded[module_name] ~= nil then
+        local msg = ("Error loading plugin '%s': " ..
+                     "module name '%s' has already been used")
+        self:failed(msg:format(filename, module_name))
+    end
+
+    add_package_path(module_dir)
+
+    local success, result = xpcall(function()
+        return require(module_name)
+    end, format_plugin_exception)
+
+    if not success then
+        local msg = "Error loading plugin '%s':\n%s"
+        self:failed(msg:format(module_name, tostring(result)))
+    end
+
+    if type(result) ~= 'table' then
+        self.priv.module_cache[filename] = false
+
+        local msg = "Error loading plugin '%s': expected table, got: %s (%s)"
+        self:failed(msg:format(module_name, type(result), tostring(result)))
+    end
+
+    self.priv.module_cache[filename] = result
+    self.priv.extension_cache[filename] = {}
+    return true
+end
+
+function Hooks:find_extension_type(filename, gtype)
+    local module_gtypes = self.priv.extension_cache[filename]
+    local extension_type = module_gtypes[gtype]
+
+    if extension_type ~= nil then
+        if extension_type == false then
+            return nil
+        end
+
+        return extension_type
+    end
+
+    for _, value in pairs(self.priv.module_cache[filename]) do
+        local value_gtype = value._gtype
+
+        if value_gtype ~= nil then
+            if GObject.type_is_a(value_gtype, gtype) then
+                module_gtypes[gtype] = value_gtype
+                return value_gtype
+            end
+        end
+    end
+
+    module_gtypes[gtype] = false
+    return nil
+end
+
+local function check_native(native, wrapped, typename)
+    local msg = ('Invalid wrapper for %s: %s'):format(typename,
+                                                      tostring(wrapped))
+
+    -- Cannot compare userdata directly!
+    assert(wrapped ~= nil, msg)
+    assert(tostring(native) == tostring(wrapped._native), msg)
+end
+
+function Hooks:setup_extension(exten, info)
+    local wrapped_exten = GObject.Object(exten, false)
+    check_native(exten, wrapped_exten, 'extension')
+
+    local wrapped_info = Peas.PluginInfo(info, false)
+    check_native(info, wrapped_info, 'PeasPluginInfo')
+
+    wrapped_exten.priv.plugin_info = wrapped_info
+end
+
+function Hooks:garbage_collect()
+    collectgarbage()
+end
+
+return Hooks.new()
+
+-- ex:set ts=4 et sw=4 ai:
diff --git a/loaders/lua5.1/peas-lua-utils.c b/loaders/lua5.1/peas-lua-utils.c
index 4fc6ce9..76f7a9c 100644
--- a/loaders/lua5.1/peas-lua-utils.c
+++ b/loaders/lua5.1/peas-lua-utils.c
@@ -2,7 +2,7 @@
  * peas-lua-utils.c
  * This file is part of libpeas
  *
- * Copyright (C) 2014 - Garrett Regier
+ * Copyright (C) 2014-2015 - 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
@@ -120,3 +120,58 @@ error:
   g_strfreev (version_str_parts);
   return success;
 }
+
+static gint
+traceback (lua_State *L)
+{
+  /* Always ignore an error that isn't a string */
+  if (!lua_isstring (L, 1))
+    return 1;
+
+  lua_getglobal (L, "debug");
+  if (!lua_istable (L, -1))
+    {
+      lua_pop (L, 1);
+      return 1;
+    }
+
+  lua_getfield (L, -1, "traceback");
+  if (!lua_isfunction (L, -1))
+    {
+      lua_pop (L, 2);
+      return 1;
+    }
+
+  /* Replace debug with traceback */
+  lua_replace (L, -2);
+
+  /* Push the error */
+  lua_pushvalue (L, 1);
+
+  /* Skip this function when generating the traceback */
+  lua_pushinteger (L, 2);
+
+  /* If we fail we have a new error object... */
+  lua_pcall (L, 2, 1, 0);
+  return 1;
+}
+
+gboolean
+peas_lua_utils_call (lua_State *L,
+                     guint      n_args,
+                     guint      n_results)
+{
+  gboolean success;
+
+  /* Push the error function */
+  lua_pushcfunction (L, traceback);
+
+  /* Move traceback to before the arguments */
+  lua_insert (L, -2 - n_args);
+
+  success = lua_pcall (L, n_args, n_results, -2 - n_args) == 0;
+
+  /* Remove traceback */
+  lua_remove (L, -1 - (success ? n_results : 1));
+  return success;
+}
diff --git a/loaders/lua5.1/peas-lua-utils.h b/loaders/lua5.1/peas-lua-utils.h
index 03fb792..29069be 100644
--- a/loaders/lua5.1/peas-lua-utils.h
+++ b/loaders/lua5.1/peas-lua-utils.h
@@ -2,7 +2,7 @@
  * peas-lua-utils.h
  * This file is part of libpeas
  *
- * Copyright (C) 2014 - Garrett Regier
+ * Copyright (C) 2014-2015 - 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
@@ -36,6 +36,10 @@ gboolean peas_lua_utils_check_version (lua_State   *L,
                                        guint        req_minor,
                                        guint        req_micro);
 
+gboolean peas_lua_utils_call          (lua_State   *L,
+                                       guint        n_args,
+                                       guint        n_results);
+
 G_END_DECLS
 
 #endif /* __PEAS_LUA_UTILS_H__ */
diff --git a/loaders/lua5.1/peas-lua.gresource.xml b/loaders/lua5.1/peas-lua.gresource.xml
new file mode 100644
index 0000000..2eaf9cb
--- /dev/null
+++ b/loaders/lua5.1/peas-lua.gresource.xml
@@ -0,0 +1,6 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<gresources>
+  <gresource prefix="/org/gnome/libpeas/loaders/lua5.1">
+    <file alias="internal.lua">peas-lua-internal.lua</file>
+  </gresource>
+</gresources>
diff --git a/loaders/lua5.1/peas-plugin-loader-lua.c b/loaders/lua5.1/peas-plugin-loader-lua.c
index d477ecb..32c6418 100644
--- a/loaders/lua5.1/peas-plugin-loader-lua.c
+++ b/loaders/lua5.1/peas-plugin-loader-lua.c
@@ -2,7 +2,7 @@
  * peas-plugin-loader-lua.c
  * This file is part of libpeas
  *
- * Copyright (C) 2014 - Garrett Regier
+ * Copyright (C) 2014-2015 - 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
@@ -32,6 +32,7 @@
 #include <lauxlib.h>
 #include <lualib.h>
 
+#include "peas-lua-internal.h"
 #include "peas-lua-utils.h"
 
 
@@ -64,173 +65,6 @@ peas_register_types (PeasObjectModule *module)
                                               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_has_package (lua_State   *L,
-                  const gchar *package_name)
-{
-  gboolean has_package;
-
-  luaL_checkstack (L, 3, "");
-
-  lua_getglobal (L, "package");
-  lua_getfield (L, -1, "loaded");
-  lua_getfield (L, -1, package_name);
-
-  has_package = !lua_isnil (L, -1);
-
-  /* Pop package, loaded and package's table */
-  lua_pop (L, 3);
-  return has_package;
-}
-
-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_pushstring (L, info->filename);
-  lua_rawget (L, LUA_REGISTRYINDEX);
-
-  /* Must always have a valid key */
-  lua_pushnil (L);
-  while (lua_next (L, -2) != 0)
-    {
-      if (lua_istable (L, -1))
-        {
-          found_type = _lua_get_gtype (L, -1);
-
-          if (found_type != G_TYPE_INVALID)
-            {
-              if (!g_type_is_a (found_type, exten_type))
-                {
-                  found_type = G_TYPE_INVALID;
-                }
-              else
-                {
-                  /* Pop value and key */
-                  lua_pop (L, 2);
-                  break;
-                }
-            }
-        }
-
-      /* 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 lua_State *
 thread_enter (PeasPluginLoaderLua *lua_loader,
               PeasPluginInfo      *info)
@@ -277,6 +111,33 @@ thread_leave (PeasPluginLoaderLua  *lua_loader,
   priv->lgi_leave_func (priv->lgi_lock);
 }
 
+static GType
+find_lua_extension_type (lua_State      *L,
+                         PeasPluginInfo *info,
+                         GType           exten_type)
+{
+  luaL_checkstack (L, 2, "");
+  lua_pushstring (L, info->filename);
+  lua_pushlightuserdata (L, GSIZE_TO_POINTER (exten_type));
+
+  if (peas_lua_internal_call (L, "find_extension_type",
+                              2, LUA_TLIGHTUSERDATA))
+    {
+      GType extension_type;
+
+      extension_type = (GType) lua_touserdata (L, -1);
+      lua_pop (L, 1);
+
+      if (g_type_is_a (extension_type, exten_type))
+        return extension_type;
+
+      g_warning ("Found invalid extension type '%s' for '%s'",
+                 g_type_name (extension_type), g_type_name (exten_type));
+    }
+
+  return G_TYPE_INVALID;
+}
+
 static gboolean
 peas_plugin_loader_lua_provides_extension (PeasPluginLoader *loader,
                                            PeasPluginInfo   *info,
@@ -284,14 +145,14 @@ peas_plugin_loader_lua_provides_extension (PeasPluginLoader *loader,
 {
   PeasPluginLoaderLua *lua_loader = PEAS_PLUGIN_LOADER_LUA (loader);
   lua_State *L;
-  GType extension_type;
+  GType the_type;
 
   L = thread_enter (lua_loader, info);
 
-  extension_type = _lua_find_extension_type (L, info, exten_type);
+  the_type = find_lua_extension_type (L, info, exten_type);
 
   thread_leave (lua_loader, info, &L);
-  return extension_type != G_TYPE_INVALID;
+  return the_type != G_TYPE_INVALID;
 }
 
 static PeasExtension *
@@ -308,16 +169,10 @@ peas_plugin_loader_lua_create_extension (PeasPluginLoader *loader,
 
   L = thread_enter (lua_loader, info);
 
-  the_type = _lua_find_extension_type (L, info, exten_type);
+  the_type = find_lua_extension_type (L, info, exten_type);
   if (the_type == G_TYPE_INVALID)
     goto out;
 
-  if (!g_type_is_a (the_type, exten_type))
-    {
-      g_warn_if_fail (g_type_is_a (the_type, exten_type));
-      goto out;
-    }
-
   object = g_object_newv (the_type, n_parameters, parameters);
   if (object == NULL)
     goto out;
@@ -328,29 +183,12 @@ peas_plugin_loader_lua_create_extension (PeasPluginLoader *loader,
   g_object_set_qdata (object, extension_type_quark (),
                       GSIZE_TO_POINTER (exten_type));
 
-  luaL_checkstack (L, 3, "");
-
-  if (!_lua_pushinstance (L, "GObject", "Object", the_type, object))
-    {
-      g_clear_object (&object);
-      goto out;
-    }
-
-  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");
-    }
+  luaL_checkstack (L, 2, "");
+  lua_pushlightuserdata (L, object);
+  lua_pushlightuserdata (L, info);
 
-  /* Pop priv and object */
-  lua_pop (L, 2);
+  if (!peas_lua_internal_call (L, "setup_extension", 2, LUA_TNIL))
+    g_clear_object (&object);
 
 out:
 
@@ -364,53 +202,21 @@ peas_plugin_loader_lua_load (PeasPluginLoader *loader,
 {
   PeasPluginLoaderLua *lua_loader = PEAS_PLUGIN_LOADER_LUA (loader);
   lua_State *L;
-  gboolean success;
+  gboolean success = FALSE;
 
   L = thread_enter (lua_loader, info);
 
-  luaL_checkstack (L, 2, "");
-
-  /* Get the module's table */
+  luaL_checkstack (L, 3, "");
   lua_pushstring (L, info->filename);
-  lua_rawget (L, LUA_REGISTRYINDEX);
+  lua_pushstring (L, peas_plugin_info_get_module_dir (info));
+  lua_pushstring (L, peas_plugin_info_get_module_name (info));
 
-  if (!lua_isnil (L, -1))
-    {
-      success = lua_istable (L, -1);
-    }
-  else
+  if (peas_lua_internal_call (L, "load", 3, LUA_TBOOLEAN))
     {
-      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_pushstring (L, info->filename);
-
-      /* Push something that isn't a table */
-      lua_pushboolean (L, FALSE);
-
-      if (_lua_has_package (L, module_name))
-        {
-          g_warning ("Error loading plugin '%s': "
-                     "module name '%s' has already been used",
-                     info->filename, module_name);
-        }
-      else if (_lua_add_package_path (L, module_dir) &&
-               peas_lua_utils_require (L, module_name))
-        {
-          /* Remove the boolean */
-          lua_replace (L, -2);
-        }
-
-      success = lua_istable (L, -1);
-      lua_rawset (L, LUA_REGISTRYINDEX);
+      success = lua_toboolean (L, -1);
+      lua_pop (L, 1);
     }
 
-  /* Pop the module's table */
-  lua_pop (L, 1);
-
   thread_leave (lua_loader, info, &L);
   return success;
 }
@@ -433,9 +239,9 @@ peas_plugin_loader_lua_unload (PeasPluginLoader *loader,
   lua_pushnil (L);
   lua_rawset (L, LUA_REGISTRYINDEX);
 
-  priv->lgi_leave_func (priv->lgi_lock);
-
   info->loader_data = NULL;
+
+  priv->lgi_leave_func (priv->lgi_lock);
 }
 
 static void
@@ -447,7 +253,7 @@ peas_plugin_loader_lua_garbage_collect (PeasPluginLoader *loader)
 
   priv->lgi_enter_func (priv->lgi_lock);
 
-  lua_gc (L, LUA_GCCOLLECT, 0);
+  peas_lua_internal_call (L, "garbage_collect", 0, LUA_TNIL);
 
   priv->lgi_leave_func (priv->lgi_lock);
 }
@@ -515,6 +321,12 @@ peas_plugin_loader_lua_initialize (PeasPluginLoader *loader)
   /* Pop lgi's module table */
   lua_pop (L, 1);
 
+  if (!peas_lua_internal_setup (L))
+    {
+      /* Already warned */
+      return FALSE;
+    }
+
   /* Assert that no values were leaked to the stack */
   g_assert_cmpint (lua_gettop (L), ==, 0);
 
@@ -548,6 +360,7 @@ peas_plugin_loader_lua_finalize (GObject *object)
   if (priv->lgi_enter_func != NULL)
     priv->lgi_enter_func (priv->lgi_lock);
 
+  peas_lua_internal_shutdown (priv->L);
   g_clear_pointer (&priv->L, (GDestroyNotify) lua_close);
 
   G_OBJECT_CLASS (peas_plugin_loader_lua_parent_class)->finalize (object);
diff --git a/tests/libpeas/extension-lua.c b/tests/libpeas/extension-lua.c
index 3d42556..6f01ee1 100644
--- a/tests/libpeas/extension-lua.c
+++ b/tests/libpeas/extension-lua.c
@@ -153,10 +153,8 @@ test_extension_lua_nonexistent (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'");
+                              "'extension-lua51-nonexistent'*");
 
   info = peas_engine_get_plugin_info (engine, "extension-lua51-nonexistent");
 


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