[libpeas] Use Lua to implement the plugin loader's logic
- From: Garrett Regier <gregier src gnome org>
- To: commits-list gnome org
- Cc:
- Subject: [libpeas] Use Lua to implement the plugin loader's logic
- Date: Sat, 14 Feb 2015 20:29:37 +0000 (UTC)
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]