[Date Prev][Date Next][Thread Prev][Thread Next]
[Date Index]
[Thread Index]
- Subject: luac - library preload support
- From: "Robert G. Jakabosky" <bobby@...>
- Date: Sat, 17 Oct 2009 20:38:01 -0700
I have added library preloading support to luac (Lua compiler) with the
attached patch. It works like luac.lua [1], except it works on systems with
different bit sizes then x86 (it hacks the bytecode and has the sizes
hardcoded).
Also this patch uses 'require' to find & preload Lua modules (not C-modules
only pure Lua modules).
The command line options are a bit different from luac.lua [1], here is an
example:
luac -L a.lua -L b -L sub-folder/c.lua x.lua y.lua
That command will compile all 5 lua scripts into a single Lua bytecode
file 'luac.out'.
a.lua, lua module "b" & sub-folder/c.lua are compiled & preloaded
into "package.preload", which can then be required as a, b, or c from x.lua &
y.lua. Module "b" would need to be somewhere in the search path for Lua
modules.
Note that the "-L" option accepts Lua module names or file names. The patch
adds support to the require() function to allow preloading Lua modules
without running them. luac uses this preload feature to search for Lua
modules that it needs to preload.
The patch is only needed for luac, the compiled bytecode file can still run on
a normal lua interpreter that doesn't have this patch.
Here is what the compiled Lua code from the above command would look like:
local t=package.preload
t[a] = function(...) print("hello from a") end -- preload of a.lua
t[b] = function(...) print("hello from b") end -- preload of b.lua
t[c] = function(...) print("hello from c") end -- preload of c.lua
(function(...) require("a"); require("b") end) () -- execute x.lua
(function(...) require("c") end) () -- execute y.lua
That is basically the same as what luac.lua [1] generates.
The change to require() adds a second boolean parameter 'preload'. if it is
missing or false, then require() will work like normal. If it is true, then
require will try to find and load the module like normal but instead of
executing the module (i.e. fully loading it), it will place the module
into "package.preload" and return the module as a function.
Lets say we have module a.lua:
print("hello from a")
and b.lua:
print("hello from b")
then you run main.lua:
print(require("a"))
print(require("a"))
print(require("b",true))
print(require("b",true))
require("b",true) ()
require("b",true) ()
print(require("b"))
print(require("b",true))
you would get the follow output:
hello from a
true
true
function: 0x62a580
function: 0x62a580
hello from b
hello from b
hello from b
true
function: 0x62a580
[1] http://lua-users.org/lists/lua-l/2008-08/msg00092.html
--
Robert G. Jakabosky
diff --git a/src/loadlib.c b/src/loadlib.c
index 0d401eb..23020c4 100644
--- a/src/loadlib.c
+++ b/src/loadlib.c
@@ -450,9 +450,10 @@ static const int sentinel_ = 0;
static int ll_require (lua_State *L) {
const char *name = luaL_checkstring(L, 1);
+ int preload = lua_toboolean(L, 2);
int i;
- lua_settop(L, 1); /* _LOADED table will be at index 2 */
- lua_getfield(L, LUA_REGISTRYINDEX, "_LOADED");
+ lua_settop(L, 1); /* _PRELOAD/_LOADED table will be at index 2 */
+ lua_getfield(L, LUA_REGISTRYINDEX, (preload)?"_PRELOAD":"_LOADED");
lua_getfield(L, 2, name);
if (lua_toboolean(L, -1)) { /* is it there? */
if (lua_touserdata(L, -1) == sentinel) /* check loops */
@@ -478,6 +479,11 @@ static int ll_require (lua_State *L) {
else
lua_pop(L, 1);
}
+ if (preload) { /* add library to preload list, don't run it yet. */
+ lua_pushvalue(L, -1); /* dup module function */
+ lua_setfield(L, 2, name); /* _PRELOAD[name] = library load function. */
+ return 1;
+ }
lua_pushlightuserdata(L, sentinel);
lua_setfield(L, 2, name); /* _LOADED[name] = sentinel */
lua_pushstring(L, name); /* pass name as argument to module */
@@ -656,7 +662,7 @@ LUALIB_API int luaopen_package (lua_State *L) {
luaL_findtable(L, LUA_REGISTRYINDEX, "_LOADED", 2);
lua_setfield(L, -2, "loaded");
/* set field `preload' */
- lua_newtable(L);
+ luaL_findtable(L, LUA_REGISTRYINDEX, "_PRELOAD", 0);
lua_setfield(L, -2, "preload");
lua_pushvalue(L, LUA_GLOBALSINDEX);
luaL_register(L, NULL, ll_funcs); /* open lib into global table */
diff --git a/src/luac.c b/src/luac.c
index d070173..d4179d4 100644
--- a/src/luac.c
+++ b/src/luac.c
@@ -14,6 +14,7 @@
#include "lua.h"
#include "lauxlib.h"
+#include "lualib.h"
#include "ldo.h"
#include "lfunc.h"
@@ -33,6 +34,10 @@ static char Output[]={ OUTPUT }; /* default output file name */
static const char* output=Output; /* actual output file name */
static const char* progname=PROGNAME; /* actual program name */
+#define MAX_PRELOADS (MAXINDEXRK - 2) /* max preloads about 250, to simplify code. */
+static char* preload_libs[MAX_PRELOADS];
+static int preloads=0;
+
static void fatal(const char* message)
{
fprintf(stderr,"%s: %s\n",progname,message);
@@ -56,6 +61,7 @@ static void usage(const char* message)
"Available options are:\n"
" - process stdin\n"
" -l list\n"
+ " -L name preload lua library " LUA_QL("name") "\n"
" -o name output to file " LUA_QL("name") " (default is \"%s\")\n"
" -p parse only\n"
" -s strip debug information\n"
@@ -84,6 +90,12 @@ static int doargs(int argc, char* argv[])
}
else if (IS("-")) /* end of options; use stdin */
break;
+ else if (IS("-L")) /* preload library */
+ {
+ if (preloads >= MAX_PRELOADS) usage(LUA_QL("-L") " too many preloads");
+ preload_libs[preloads]=argv[++i];
+ preloads++;
+ }
else if (IS("-l")) /* list */
++listing;
else if (IS("-o")) /* output file */
@@ -116,28 +128,69 @@ static int doargs(int argc, char* argv[])
#define toproto(L,i) (clvalue(L->top+(i))->l.p)
-static const Proto* combine(lua_State* L, int n)
+static const Proto* combine(lua_State* L, int scripts)
{
- if (n==1)
+ if (scripts == 1 && preloads == 0)
return toproto(L,-1);
else
{
- int i,pc;
+ TString *s;
+ TValue *k;
+ int i,pc,n;
Proto* f=luaF_newproto(L);
setptvalue2s(L,L->top,f); incr_top(L);
f->source=luaS_newliteral(L,"=(" PROGNAME ")");
f->maxstacksize=1;
- pc=2*n+1;
+ pc=(2*scripts) + 1;
+ if(preloads > 0)
+ {
+ pc+=(2*preloads) + 2;
+ }
f->code=luaM_newvector(L,pc,Instruction);
f->sizecode=pc;
+ n=(scripts + preloads);
f->p=luaM_newvector(L,n,Proto*);
f->sizep=n;
pc=0;
- for (i=0; i<n; i++)
+ n=0;
+ /* preload libraries. */
+ if (preloads > 0)
+ {
+ /* create constants array. */
+ f->k=luaM_newvector(L, (preloads + 2),TValue);
+ f->sizek=(preloads + 2);
+ /* make room for "local t" variable. */
+ f->maxstacksize=2;
+ /* add "package" & "preload" constants. */
+ k=&(f->k[0]);
+ s=luaS_newliteral(L, "package");
+ setsvalue2n(L,k,s);
+ k=&(f->k[1]);
+ s=luaS_newliteral(L, "preload");
+ setsvalue2n(L,k,s);
+ /* code: local t = package.preload */
+ f->code[pc++]=CREATE_ABx(OP_GETGLOBAL,0,0);
+ f->code[pc++]=CREATE_ABC(OP_GETTABLE,0,0,RKASK(1));
+ }
+ /* add preload libraries to "package.preload" */
+ for (i=0; i < preloads; i++)
+ {
+ /* create constant for library name. */
+ k=&(f->k[i+2]);
+ s=luaS_new(L, preload_libs[i]);
+ setsvalue2n(L,k,s);
+ /* code: t['name'] = function() --[[ lib code ]] end */
+ f->code[pc++]=CREATE_ABx(OP_CLOSURE,1,n);
+ f->code[pc++]=CREATE_ABC(OP_SETTABLE,0,RKASK(i+2),1);
+ f->p[n++]=toproto(L,i-preloads-1);
+ }
+ /* call scripts. */
+ for (i=0; i < scripts; i++)
{
- f->p[i]=toproto(L,i-n-1);
- f->code[pc++]=CREATE_ABx(OP_CLOSURE,0,i);
+ /* code: (function() --[[ script code ]] end)() */
+ f->code[pc++]=CREATE_ABx(OP_CLOSURE,0,n);
f->code[pc++]=CREATE_ABC(OP_CALL,0,1,1);
+ f->p[n++]=toproto(L,i-scripts-1-preloads);
}
f->code[pc++]=CREATE_ABC(OP_RETURN,0,1,0);
return f;
@@ -161,14 +214,60 @@ static int pmain(lua_State* L)
int argc=s->argc;
char** argv=s->argv;
const Proto* f;
+ int scripts=0;
int i;
if (!lua_checkstack(L,argc)) fatal("too many input files");
- for (i=0; i<argc; i++)
- {
+ lua_gc(L, LUA_GCSTOP, 0); /* stop collector during initialization */
+ luaL_openlibs(L); /* open libraries */
+ lua_gc(L, LUA_GCRESTART, 0);
+ /* compile each script from command line into a Lua function. */
+ for (i=0; i<argc; i++) {
const char* filename=IS("-") ? NULL : argv[i];
+ if(IS("-L")) break;
if (luaL_loadfile(L,filename)!=0) fatal(lua_tostring(L,-1));
+ scripts++;
+ }
+ /* compile each preload library from the command line into a Lua function. */
+ for (i=0; i<preloads; i++) {
+ char* filename=preload_libs[i];
+ char* p;
+ /* try loading library as if it is a normal file. */
+ if (luaL_loadfile(L,filename)!=0) {
+ /* try pre-loading library with 'require' module loading system. */
+ lua_getglobal(L, "require");
+ lua_pushstring(L, filename);
+ lua_pushboolean(L, 1);
+ lua_call(L, 2, 1);
+ if (lua_iscfunction(L, -1)) { /* make sure it is not a C-Function. */
+ lua_pop(L, 1);
+ lua_pushfstring(L, "\nCan't preload C module: '%s'\n", filename);
+ lua_concat(L, 2); /* accumulate with error from luaL_findfile */
+ fatal(lua_tostring(L,-1));
+ }
+ if (!lua_isfunction(L, -1)) { /* did we get an error? */
+ lua_pushliteral(L, "\n");
+ lua_concat(L, 3); /* accumulate with error from luaL_findfile */
+ fatal(lua_tostring(L,-1));
+ } else {
+ lua_remove(L, -2); /* remove error from luaL_findfile. */
+ }
+ } else {
+ /* convert filename into package name. */
+ p= filename + strlen(filename);
+ for(;p >= filename; p--) {
+ if(p[0] == '.') { /* Remove file extension. */
+ p[0] = '\0';
+ continue;
+ }
+ if(p[0] == '/') { /* Remove file path. */
+ preload_libs[i] = p+1;
+ break;
+ }
+ }
+ }
}
- f=combine(L,argc);
+ /* generate a new Lua function to combine all of the compiled scripts. */
+ f=combine(L, scripts);
if (listing) luaU_print(f,listing>1);
if (dumping)
{