lua-users home
lua-l archive

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


Building Lua is complicated by the fact that luac must be statically
linked to the Lua library even when lua is dynamically linked to it.
For example, the mingw build target in lua-5.1.4/src/Makefile is

mingw:
	$(MAKE) "LUA_A=lua51.dll" "LUA_T=lua.exe" \
	"AR=$(CC) -shared -o" "RANLIB=strip --strip-unneeded" \
	"MYCFLAGS=-DLUA_BUILD_AS_DLL" "MYLIBS=" "MYLDFLAGS=-s" lua.exe
	$(MAKE) "LUAC_T=luac.exe" luac.exe

Here's some other reports about this:

  http://lua-users.org/lists/lua-l/2006-04/msg00317.html
  http://lua-users.org/lists/lua-l/2007-03/msg00362.html

At the least, it feels out of place.  Moreover, it may complicate
merging lua and luac into a single process or other things one might
want to do.  It would be preferable, design-wise, if luac had no
special access to Lua.

The reason luac currently statically links to the Lua library is to
gain access to some internal functions (LUAI_FUNC).  One solution of
course is to export those functions from the shared library (though
perhaps still keep them undocumented), while maybe also exporting the
variables in lopcodes.c through getter functions, assuming one has
access to rebuilding the shared library.

However, expanding the shared library interface is not strictly
necessary.  As Lhf's luac.lua implementation shows, the functionality
of luac can be implemented in plain Lua:

  http://www.tecgraf.puc-rio.br/~lhf/ftp/lua/5.1/luac.lua

Although the code above was not intended to be equivalent in behavior
to luac, and it doesn't support debug stripping, the full behavior
could be implemented with extra work following "A No-Frills
Introduction to Lua 5.1 VM Instructions".

In the below patch, I modified luac.c to take an approach somewhat
like the luac.lua above, eliminating the dependencies on internal
functions in the Lua library.  The code is shorter through the C
interfaces and it fully supports debug stripping.  In fact, my tests
are confirming that the generated bytecodes are identical to those
from a standard luac (with or without -s).

This patched luac.c still accesses the internal structure of the Lua
state L (mainly, the function prototype objects), which is not ideal,
though it now does so entirely through the standard API functions
exported from the shared library.  It's also unfortunate that lua_dump
(unlike the corresponding internal function luaU_dump) doesn't provide
an option to strip the debug symbols.  My workaround was to remove the
debug symbols from the function prototype structures and then to call
lua_dump.  The program exits after calling lua_dump, so it's not
likely this information will be needed.  I might not of properly
handled garbage collection in this process though, so please indicate
if not.  The lua_dump concerns could be avoided, however, by linking
in the ldump.c code as well as copying/pasting luaU_header from
lundump.c (since this is shared by the dumper) into luac.  Yet another
possibility is to move luac.c's "combine" function into lcode.c into
order to reduce access to L's internals form outside the Lua library.
However, L's internals are still accessed from luac via luaU_print in
print.c.  A way to avoid that is to access the bytecodes through
lua_dump rather than through toproto(L,-1).

Opinions?

--- luac.c~	2008-12-06 18:10:17.328125000 -0500
+++ luac.c	2008-12-06 22:26:49.921875000 -0500
@@ -23,6 +23,8 @@
 #include "lstring.h"
 #include "lundump.h"

+#include "lopcodes.c"
+
 #define PROGNAME	"luac"		/* default program name */
 #define	OUTPUT		PROGNAME ".out"	/* default output file */

@@ -116,34 +118,44 @@

 #define toproto(L,i) (clvalue(L->top+(i))->l.p)

-static const Proto* combine(lua_State* L, int n)
+static Proto* combine(lua_State* L, int n)
 {
  if (n==1)
   return toproto(L,-1);
  else
  {
-  int i,pc;
-  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;
-  f->code=luaM_newvector(L,pc,Instruction);
-  f->sizecode=pc;
-  f->p=luaM_newvector(L,n,Proto*);
-  f->sizep=n;
-  pc=0;
-  for (i=0; i<n; i++)
-  {
-   f->p[i]=toproto(L,i-n-1);
-   f->code[pc++]=CREATE_ABx(OP_CLOSURE,0,i);
-   f->code[pc++]=CREATE_ABC(OP_CALL,0,1,1);
-  }
-  f->code[pc++]=CREATE_ABC(OP_RETURN,0,1,0);
+  luaL_Buffer b;
+  int i;
+  Proto * f;
+  luaL_checkstack(L,2,"");
+  luaL_buffinit(L,&b);
+  for (i=0; i<n; i++) luaL_addlstring(&b, "(function()end)();\n", 19);
+  luaL_pushresult(&b);
+  if (luaL_loadbuffer(L, lua_tostring(L,-1), 19*n, "=(" PROGNAME ")") != 0)
+  { fatal(lua_tostring(L,-1)); }
+  lua_remove(L,-2);
+  f = toproto(L,-1);
+  f->is_vararg = 0; /* rather than VARARG_ISVARARG */
+  f->maxstacksize = 1; /* rather than 2 */
+  for (i=0; i<n; i++) f->p[i] = toproto(L, i-n-1);
+  /* strip */
+  f->sizelineinfo = 0;
+  f->sizelocvars = 0;
+  f->sizeupvalues = 0;
   return f;
  }
 }

+static void strip(Proto* f)
+{
+ int i;
+ f->sizelineinfo = 0;
+ f->sizelocvars = 0;
+ f->sizeupvalues = 0;
+ f->source = NULL; /* FIX: garbage collect? */
+ for (i=0; i < f->sizep; i++) strip(f->p[i]);
+}
+
 static int writer(lua_State* L, const void* p, size_t size, void* u)
 {
  UNUSED(L);
@@ -160,7 +172,7 @@
  struct Smain* s = (struct Smain*)lua_touserdata(L, 1);
  int argc=s->argc;
  char** argv=s->argv;
- const Proto* f;
+ Proto* f;
  int i;
  if (!lua_checkstack(L,argc)) fatal("too many input files");
  for (i=0; i<argc; i++)
@@ -174,9 +186,8 @@
  {
   FILE* D= (output==NULL) ? stdout : fopen(output,"wb");
   if (D==NULL) cannot("open");
-  lua_lock(L);
-  luaU_dump(L,f,writer,D,stripping);
-  lua_unlock(L);
+  if (stripping) strip(f);
+  lua_dump(L,writer,D);
   if (ferror(D)) cannot("write");
   if (fclose(D)) cannot("close");
  }