/*
** $Id: luac.c $
** Lua compiler (saves bytecodes to files; also lists bytecodes)
** See Copyright Notice in lua.h
*/
#define luac_c
#define LUA_CORE
#include "lprefix.h"
#include <ctype.h>
#include <errno.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include "lua.h"
#include "lauxlib.h"
#include "ldebug.h"
#include "lobject.h"
#include "lopcodes.h"
#include "lopnames.h"
#include "lstate.h"
#include "lundump.h"
static void PrintFunction(const Proto* f, int full);
#define luaU_print PrintFunction
#define PROGNAME "luac" /* default program name */
#define OUTPUT PROGNAME ".out" /* default output file */
static int listing=0; /* list bytecodes? */
static int dumping=1; /* dump bytecodes? */
static int stripping=0; /* strip debug information? */
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 */
static TString **tmname;
static void fatal(const char* message)
{
fprintf(stderr
,"%s: %s\n",progname
,message
);
}
static void cannot(const char* what)
{
fprintf(stderr
,"%s: cannot %s %s: %s\n",progname
,what
,output
,strerror(errno
));
}
static void usage(const char* message)
{
if (*message=='-')
fprintf(stderr
,"%s: unrecognized option '%s'\n",progname
,message
);
else
fprintf(stderr
,"%s: %s\n",progname
,message
);
"usage: %s [options] [filenames]\n"
"Available options are:\n"
" -l list (use -l -l for full listing)\n"
" -o name output to file 'name' (default is \"%s\")\n"
" -p parse only\n"
" -s strip debug information\n"
" -v show version information\n"
" -- stop handling options\n"
" - stop handling options and process stdin\n"
,progname,Output);
}
#define IS(s) (strcmp(argv[i],s)==0)
static int doargs(int argc, char* argv[])
{
int i;
int version=0;
if (argv[0]!=NULL && *argv[0]!=0) progname=argv[0];
for (i=1; i<argc; i++)
{
if (*argv[i]!='-') /* end of options; keep it */
break;
else if (IS("--")) /* end of options; skip it */
{
++i;
if (version) ++version;
break;
}
else if (IS("-")) /* end of options; use stdin */
break;
else if (IS("-l")) /* list */
++listing;
else if (IS("-o")) /* output file */
{
output=argv[++i];
if (output==NULL || *output==0 || (*output=='-' && output[1]!=0))
usage("'-o' needs argument");
if (IS("-")) output=NULL;
}
else if (IS("-p")) /* parse only */
dumping=0;
else if (IS("-s")) /* strip debug information */
stripping=1;
else if (IS("-v")) /* show version */
++version;
else /* unknown option */
usage(argv[i]);
}
if (i==argc && (listing || !dumping))
{
dumping=0;
argv[--i]=Output;
}
if (version)
{
if (version
==argc
-1) exit(EXIT_SUCCESS
);
}
return i;
}
#define FUNCTION "(function()end)();"
static const char* reader(lua_State* L, void* ud, size_t* size)
{
UNUSED(L);
if ((*(int*)ud)--)
{
*size=sizeof(FUNCTION)-1;
return FUNCTION;
}
else
{
*size=0;
return NULL;
}
}
#define toproto(L,i) getproto(s2v(L->top+(i)))
static const Proto* combine(lua_State* L, int n)
{
if (n==1)
return toproto(L,-1);
else
{
Proto* f;
int i=n;
if (lua_load(L,reader,&i,"=(" PROGNAME ")",NULL)!=LUA_OK) fatal(lua_tostring(L,-1));
f=toproto(L,-1);
for (i=0; i<n; i++)
{
f->p[i]=toproto(L,i-n-1);
if (f->p[i]->sizeupvalues>0) f->p[i]->upvalues[0].instack=0;
}
luaM_freearray(L,f->lineinfo,f->sizelineinfo);
f->sizelineinfo=0;
return f;
}
}
static int writer(lua_State* L, const void* p, size_t size, void* u)
{
UNUSED(L);
return (fwrite(p
,size
,1,(FILE
*)u
)!=1) && (size
!=0);
}
static int pmain(lua_State* L)
{
int argc=(int)lua_tointeger(L,1);
char** argv=(char**)lua_touserdata(L,2);
const Proto* f;
int i;
tmname=G(L)->tmname;
if (!lua_checkstack(L,argc)) fatal("too many input files");
for (i=0; i<argc; i++)
{
const char* filename=IS("-") ? NULL : argv[i];
if (luaL_loadfile(L,filename)!=LUA_OK) fatal(lua_tostring(L,-1));
}
f=combine(L,argc);
if (listing) luaU_print(f,listing>1);
if (dumping)
{
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 (ferror(D
)) cannot
("write");
if (fclose(D
)) cannot
("close");
}
return 0;
}
int main(int argc, char* argv[])
{
lua_State* L;
int i=doargs(argc,argv);
argc-=i; argv+=i;
if (argc<=0) usage("no input files given");
L=luaL_newstate();
if (L==NULL) fatal("cannot create state: not enough memory");
lua_pushcfunction(L,&pmain);
lua_pushinteger(L,argc);
lua_pushlightuserdata(L,argv);
if (lua_pcall(L,2,0,0)!=LUA_OK) fatal(lua_tostring(L,-1));
lua_close(L);
return EXIT_SUCCESS;
}
/*
** print bytecodes
*/
#define UPVALNAME(x) ((f->upvalues[x].name) ? getstr(f->upvalues[x].name) : "-")
#define VOID(p) ((const void*)(p))
#define eventname(i) (getstr(tmname[i]))
static void PrintString(const TString* ts)
{
const char* s=getstr(ts);
size_t i,n=tsslen(ts);
for (i=0; i<n; i++)
{
int c=(int)(unsigned char)s[i];
switch (c)
{
case '"':
break;
case '\\':
break;
case '\a':
break;
case '\b':
break;
case '\f':
break;
case '\n':
break;
case '\r':
break;
case '\t':
break;
case '\v':
break;
default:
break;
}
}
}
static void PrintType(const Proto* f, int i)
{
const TValue* o=&f->k[i];
switch (ttypetag(o))
{
case LUA_VNIL:
break;
case LUA_VFALSE:
case LUA_VTRUE:
break;
case LUA_VNUMFLT:
break;
case LUA_VNUMINT:
break;
case LUA_VSHRSTR:
case LUA_VLNGSTR:
break;
default: /* cannot happen */
break;
}
}
static void PrintConstant(const Proto* f, int i)
{
const TValue* o=&f->k[i];
switch (ttypetag(o))
{
case LUA_VNIL:
break;
case LUA_VFALSE:
break;
case LUA_VTRUE:
break;
case LUA_VNUMFLT:
{
char buff[100];
sprintf(buff
,LUA_NUMBER_FMT
,fltvalue
(o
));
break;
}
case LUA_VNUMINT:
printf(LUA_INTEGER_FMT
,ivalue
(o
));
break;
case LUA_VSHRSTR:
case LUA_VLNGSTR:
PrintString(tsvalue(o));
break;
default: /* cannot happen */
break;
}
}
#define COMMENT "\t; "
#define EXTRAARG GETARG_Ax(code[pc+1])
#define EXTRAARGC (EXTRAARG*(MAXARG_C+1))
#define ISK (isk ? "k" : "")
static void PrintCode(const Proto* f)
{
const Instruction* code=f->code;
int pc,n=f->sizecode;
for (pc=0; pc<n; pc++)
{
Instruction i=code[pc];
OpCode o=GET_OPCODE(i);
int a=GETARG_A(i);
int b=GETARG_B(i);
int c=GETARG_C(i);
int ax=GETARG_Ax(i);
int bx=GETARG_Bx(i);
int sb=GETARG_sB(i);
int sc=GETARG_sC(i);
int sbx=GETARG_sBx(i);
int isk=GETARG_k(i);
int line=luaG_getfuncline(f,pc);
switch (o)
{
case OP_MOVE:
break;
case OP_LOADI:
break;
case OP_LOADF:
break;
case OP_LOADK:
printf(COMMENT
); PrintConstant
(f
,bx
);
break;
case OP_LOADKX:
printf(COMMENT
); PrintConstant
(f
,EXTRAARG
);
break;
case OP_LOADFALSE:
break;
case OP_LFALSESKIP:
break;
case OP_LOADTRUE:
break;
case OP_LOADNIL:
break;
case OP_GETUPVAL:
printf(COMMENT
"%s",UPVALNAME
(b
));
break;
case OP_SETUPVAL:
printf(COMMENT
"%s",UPVALNAME
(b
));
break;
case OP_GETTABUP:
printf(COMMENT
"%s",UPVALNAME
(b
));
printf(" "); PrintConstant
(f
,c
);
break;
case OP_GETTABLE:
break;
case OP_GETI:
break;
case OP_GETFIELD:
printf(COMMENT
); PrintConstant
(f
,c
);
break;
case OP_SETTABUP:
printf("%d %d %d%s",a
,b
,c
,ISK
);
printf(COMMENT
"%s",UPVALNAME
(a
));
printf(" "); PrintConstant
(f
,b
);
if (isk
) { printf(" "); PrintConstant
(f
,c
); }
break;
case OP_SETTABLE:
printf("%d %d %d%s",a
,b
,c
,ISK
);
if (isk
) { printf(COMMENT
); PrintConstant
(f
,c
); }
break;
case OP_SETI:
printf("%d %d %d%s",a
,b
,c
,ISK
);
if (isk
) { printf(COMMENT
); PrintConstant
(f
,c
); }
break;
case OP_SETFIELD:
printf("%d %d %d%s",a
,b
,c
,ISK
);
printf(COMMENT
); PrintConstant
(f
,b
);
if (isk
) { printf(" "); PrintConstant
(f
,c
); }
break;
case OP_NEWTABLE:
printf(COMMENT
"%d",c
+EXTRAARGC
);
break;
case OP_SELF:
printf("%d %d %d%s",a
,b
,c
,ISK
);
if (isk
) { printf(COMMENT
); PrintConstant
(f
,c
); }
break;
case OP_ADDI:
break;
case OP_ADDK:
printf(COMMENT
); PrintConstant
(f
,c
);
break;
case OP_SUBK:
printf(COMMENT
); PrintConstant
(f
,c
);
break;
case OP_MULK:
printf(COMMENT
); PrintConstant
(f
,c
);
break;
case OP_MODK:
printf(COMMENT
); PrintConstant
(f
,c
);
break;
case OP_POWK:
printf(COMMENT
); PrintConstant
(f
,c
);
break;
case OP_DIVK:
printf(COMMENT
); PrintConstant
(f
,c
);
break;
case OP_IDIVK:
printf(COMMENT
); PrintConstant
(f
,c
);
break;
case OP_BANDK:
printf(COMMENT
); PrintConstant
(f
,c
);
break;
case OP_BORK:
printf(COMMENT
); PrintConstant
(f
,c
);
break;
case OP_BXORK:
printf(COMMENT
); PrintConstant
(f
,c
);
break;
case OP_SHRI:
break;
case OP_SHLI:
break;
case OP_ADD:
break;
case OP_SUB:
break;
case OP_MUL:
break;
case OP_MOD:
break;
case OP_POW:
break;
case OP_DIV:
break;
case OP_IDIV:
break;
case OP_BAND:
break;
case OP_BOR:
break;
case OP_BXOR:
break;
case OP_SHL:
break;
case OP_SHR:
break;
case OP_MMBIN:
printf(COMMENT
"%s",eventname
(c
));
break;
case OP_MMBINI:
printf("%d %d %d %d",a
,sb
,c
,isk
);
printf(COMMENT
"%s",eventname
(c
));
break;
case OP_MMBINK:
printf("%d %d %d %d",a
,b
,c
,isk
);
printf(COMMENT
"%s ",eventname
(c
)); PrintConstant
(f
,b
);
break;
case OP_UNM:
break;
case OP_BNOT:
break;
case OP_NOT:
break;
case OP_LEN:
break;
case OP_CONCAT:
break;
case OP_CLOSE:
break;
case OP_TBC:
break;
case OP_JMP:
printf(COMMENT
"to %d",GETARG_sJ
(i
)+pc
+2);
break;
case OP_EQ:
break;
case OP_LT:
break;
case OP_LE:
break;
case OP_EQK:
printf(COMMENT
); PrintConstant
(f
,b
);
break;
case OP_EQI:
break;
case OP_LTI:
break;
case OP_LEI:
break;
case OP_GTI:
break;
case OP_GEI:
break;
case OP_TEST:
break;
case OP_TESTSET:
break;
case OP_CALL:
break;
case OP_TAILCALL:
printf("%d %d %d%s",a
,b
,c
,ISK
);
break;
case OP_RETURN:
printf("%d %d %d%s",a
,b
,c
,ISK
);
break;
case OP_RETURN0:
break;
case OP_RETURN1:
break;
case OP_FORLOOP:
printf(COMMENT
"to %d",pc
-bx
+2);
break;
case OP_FORPREP:
printf(COMMENT
"exit to %d",pc
+bx
+3);
break;
case OP_TFORPREP:
printf(COMMENT
"to %d",pc
+bx
+2);
break;
case OP_TFORCALL:
break;
case OP_TFORLOOP:
printf(COMMENT
"to %d",pc
-bx
+2);
break;
case OP_SETLIST:
if (isk
) printf(COMMENT
"%d",c
+EXTRAARGC
);
break;
case OP_CLOSURE:
printf(COMMENT
"%p",VOID
(f
->p
[bx
]));
break;
case OP_VARARG:
break;
case OP_VARARGPREP:
break;
case OP_EXTRAARG:
break;
#if 0
default:
printf(COMMENT
"not handled");
break;
#endif
}
}
}
#define SS(x) ((x==1)?"":"s")
#define S(x) (int)(x),SS(x)
static void PrintHeader(const Proto* f)
{
const char* s=f->source ? getstr(f->source) : "=?";
if (*s=='@' || *s=='=')
s++;
else if (*s==LUA_SIGNATURE[0])
s="(bstring)";
else
s="(string)";
printf("\n%s <%s:%d,%d> (%d instruction%s at %p)\n",
(f->linedefined==0)?"main":"function",s,
f->linedefined,f->lastlinedefined,
S(f->sizecode),VOID(f));
printf("%d%s param%s, %d slot%s, %d upvalue%s, ",
(int)(f->numparams),f->is_vararg?"+":"",SS(f->numparams),
S(f->maxstacksize),S(f->sizeupvalues));
printf("%d local%s, %d constant%s, %d function%s\n",
S(f->sizelocvars),S(f->sizek),S(f->sizep));
}
static void PrintDebug(const Proto* f)
{
int i,n;
n=f->sizek;
printf("constants (%d) for %p:\n",n
,VOID
(f
));
for (i=0; i<n; i++)
{
PrintType(f,i);
PrintConstant(f,i);
}
n=f->sizelocvars;
printf("locals (%d) for %p:\n",n
,VOID
(f
));
for (i=0; i<n; i++)
{
i,getstr(f->locvars[i].varname),f->locvars[i].startpc+1,f->locvars[i].endpc+1);
}
n=f->sizeupvalues;
printf("upvalues (%d) for %p:\n",n
,VOID
(f
));
for (i=0; i<n; i++)
{
i,UPVALNAME(i),f->upvalues[i].instack,f->upvalues[i].idx);
}
}
static void PrintFunction(const Proto* f, int full)
{
int i,n=f->sizep;
PrintHeader(f);
PrintCode(f);
if (full) PrintDebug(f);
for (i=0; i<n; i++) PrintFunction(f->p[i],full);
}