/* ** $Id: luac.c,v 1.76 2018/06/19 01:32:02 lhf Exp $ ** 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 #include #include #include #include #include #include #include "lua.h" #include "lualib.h" #include "lauxlib.h" #include "ldebug.h" #include "lnodemcu.h" #include "lobject.h" #include "lstate.h" #include "lstring.h" #include "lundump.h" static void PrintFunction(const Proto* f, int full); #define luaU_print PrintFunction #define PROGNAME "luac.cross" /* 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 int flash = 0; /* output flash image */ static lu_int32 address = 0; /* output flash image at absolute location */ static lu_int32 maxSize = 0x40000; /* maximuum uncompressed image size */ static int lookup = 0; /* output lookup-style master combination header */ static const char *execute; /* executed a Lua file */ char *LFSimageName; #if 0 #define IROM0_SEG 0x40200000ul #define IROM0_SEGMAX 0x00100000ul #define IROM_OFFSET(a) (cast(lu_int32, (a)) - IROM0_SEG) #else // Safety checks disabled for now; would need to handle ESP32/ESP32S/ESP32C3 #define IROM_OFFSET(a) #endif static void fatal(const char *message) { fprintf(stderr, "%s: %s\n", progname, message); exit(EXIT_FAILURE); } static void cannot(const char *what) { fprintf(stderr, "%s: cannot %s %s: %s\n", progname, what, output, strerror(errno)); exit(EXIT_FAILURE); } 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); fprintf(stderr, "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" " -e name execute a lua source file\n" " -f output a flash image file\n" " -F name load a flash image file\n" " -a addr generate an absolute, rather than " "position independent flash image file\n" " (use with -F LFSimage -o absLFSimage to " "convert an image to absolute format)\n" " -i generate lookup combination master (default with option -f)\n" " -m size maximum LFS image in bytes\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); exit(EXIT_FAILURE); } #define IS(s) (strcmp(argv[i],s)==0) static int doargs(int argc, char *argv[]) { int i; int version = 0; lu_int32 offset = 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("-e")) { /* execute a lua source file file */ execute = argv[++i]; if (execute == NULL || *execute == 0 || *execute == '-') usage("\"-e\" needs a file argument"); } else if (IS("-F")) { /* execute a lua source file file */ LFSimageName = argv[++i]; if (LFSimageName == NULL || *LFSimageName == 0 || *LFSimageName == '-') usage("\"-F\" needs an LFS image file argument"); } else if (IS("-f")) { /* Flash image file */ flash = lookup = 1; } else if (IS("-a")) { /* Absolue flash image file */ flash = lookup = 1; address = strtol(argv[++i], NULL, 0); #if 0 offset = IROM_OFFSET(address); if (offset == 0 || offset > IROM0_SEGMAX) usage("\"-a\" absolute address must be valid flash address"); #endif } else if (IS("-i")) { /* lookup */ lookup = 1; } else if (IS("-l")) { /* list */ ++listing; } else if (IS("-m")) { /* specify a maximum image size */ flash = lookup = 1; maxSize = strtol(argv[++i], NULL, 0); if (maxSize & 0xFFF) usage("\"-m\" maximum size must be a multiple of 4,096"); } 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 (offset>0 && (output == NULL || LFSimageName == NULL || execute != NULL || i != argc)) usage("'-a' also requires '-o' and '-f' options without lua source files"); if (i == argc && (listing || !dumping)) { dumping = 0; argv[--i] = Output; } if (version) { printf("%s [%s]\n", LUA_COPYRIGHT, XLUA_OPT_STR); if (version == argc - 1) exit(EXIT_SUCCESS); } return i; } static const char *corename(lua_State *L, const TString *filename, int *len) { (void)L; const char *fn = getstr(filename) + 1; const char *s = strrchr(fn, '/'); if (!s) s = strrchr(fn, '\\'); s = s ? s + 1 : fn; while ( *s == '.') s++; const char *e = strchr(s, '.'); if (len) *len = e ? e - s : strlen(s); return s; } /* ** If the luac command line includes multiple files or has the -f option ** then luac generates a main function to reference all sub-main prototypes. ** This is one of two types: ** Type 0 The standard luac combination main ** Type 1 A lookup wrapper that is used for LFS image dumps */ #define toproto(L, i) getproto(L->top + (i)) static const Proto *combine(lua_State *L, int n, int type) { if (n == 1 && type == 0) { return toproto(L, -1); } else { Proto *f; int i, j; /* * Generate a minimal proto with 1 return, emtpy p, k & uv vectors */ if (luaL_loadbuffer(L, "\n", strlen("\n"), "=("PROGNAME ")") != LUA_OK) fatal(lua_tostring(L, -1)); f = toproto(L, -1); /* * Allocate the vector for and bind the sub-protos */ luaM_reallocvector(L, f->p, f->sizep, n, Proto *); f->sizep = n; 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; } f->numparams = 0; f->maxstacksize = 1; if (type == 1) { /* * For Type 1 main(), add a k vector of strings naming the corresponding * protos with the Unixtime of the compile appended. */ luaM_reallocvector(L, f->k, f->sizek, n+1, TValue); f->sizek = n + 1; for (i = 0; i < n; i++) { int len; const char *name = corename(L, f->p[i]->source, &len); TString* sname = luaS_newlstr(L, name, len); for (j = 0; j < i; j++) { if (tsvalue(f->k+j) == sname) fatal(lua_pushfstring(L, "Cannot have duplicate files ('%s') in LFS", name)); } setsvalue2n(L, f->k+i, sname); } setivalue(f->k+n, (lua_Integer) time(NULL)); } 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)[0]) != 1) && (size != 0); } static int msghandler (lua_State *L) { const char *msg = lua_tostring(L, 1); if (msg == NULL) /* is error object not a string? */ msg = lua_pushfstring(L, "(error object is a %s value)", luaL_typename(L, 1)); luaL_traceback(L, L, msg, 1); /* append a standard traceback */ return 1; /* return the traceback */ } static int dofile (lua_State *L, const char *name) { int status = luaL_loadfile(L, name); if (status == LUA_OK) { int base = lua_gettop(L); lua_pushcfunction(L, msghandler); /* push message handler */ lua_insert(L, base); /* put it under function and args */ status = lua_pcall(L, 0, 0, base); lua_remove(L, base); /* remove message handler from the stack */ } if (status != LUA_OK) { fprintf(stderr, "%s: %s\n", PROGNAME, lua_tostring(L, -1)); lua_pop(L, 1); /* remove message */ } return status; } /* ** This function is an inintended consequence of constraints in ltable.c ** rotable_findentry(). The file list generates a ROTable in LFS and the ** rule for ROTables is that metavalue entries must be at the head of the ** ROTableentry list so argv file names with basenames starting with "__" ** must be head of the list. This is a botch. Sorry. */ static void reorderfiles(lua_State *L, int argc, char **list, char **argv) { int i, j; for (i = 0; i < argc; i++ ) { TString *file = luaS_new(L,argv[i]); if (strcmp("__", corename(L, file, NULL))) { list[i] = argv[i]; /* add to the end of the new list */ } else { for (j = 0; j < i; j++) list[j+1] = list[j]; list[0] = argv[i]; /* add to the start of the new list */ } } } static int pmain(lua_State *L) { int argc = (int) lua_tointeger(L, 1); char **argv = (char **) lua_touserdata(L, 2); char **filelist = alloca(argc * sizeof(char *)); const Proto *f; int i, status; if (!lua_checkstack(L, argc + 1)) fatal("too many input files"); if (execute || address) { luaL_openlibs(L); /* the nodemcu open will throw to signal an LFS reload */ status = dofile(L, execute); if (status != LUA_OK) return 0; } if (argc == 0) return 0; reorderfiles(L, argc, filelist, argv); for (i = 0; i < argc; i++) { const char *filename = IS("-") ? NULL : filelist[i]; if (luaL_loadfile(L, filename) != LUA_OK) fatal(lua_tostring(L, -1)); //TODO: if strip = 2, replace proto->source by basename } f = combine(L, argc + (execute ? 1 : 0), lookup); if (listing) luaU_print(f, listing > 1); if (dumping) { int result; FILE *D = (output == NULL) ? stdout : fopen(output, "wb"); if (D == NULL) cannot("open"); lua_lock(L); if (flash) { UNUSED(address); UNUSED(maxSize); result = luaU_DumpAllProtos(L, f, writer, &D, stripping); } else { result = luaU_dump(L, f, writer, cast(void *, &D), stripping); } lua_unlock(L); if (result == LUA_ERR_CC_INTOVERFLOW) fatal("value too big or small for target integer type"); if (result == LUA_ERR_CC_NOTINTEGER) fatal("target lua_Number is integral but fractional value found"); 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); int j, status; argc -= i; argv += i; if (argc <= 0 && execute == 0 && address == 0) usage("no input files given"); if (address) luaN_setabsolute(address); for (j = 0; j < 2 ; j++) { L = luaL_newstate(); if (L == NULL) fatal("not enough memory for state"); lua_pushcfunction(L, &pmain); lua_pushinteger(L, argc); lua_pushlightuserdata(L, argv); status = lua_pcall(L, 2, 0, 0); if (status != LUA_OK) { const char *res = luaL_optstring(L, -1, ""); if (strcmp(res, "!LFSrestart!") == 0) { /*An LFS image has been loaded */ if (address) { /* write out as absolute image and exit */ lu_int32 size = cast(LFSHeader *, LFSregion)->flash_size; FILE *af = fopen(output, "wb"); if (af == NULL) cannot("open"); if (fwrite(LFSregion, size, 1, af) != 1) cannot("write"); fclose(af); exit(0); } /*otherwise simulate a restart */ lua_close(L); continue; /* and loop around once more simulating restart */ } char *err = strdup(lua_tostring(L, -1)); lua_close(L); fatal(err); } lua_close(L); break; } return EXIT_SUCCESS; } /* ** $Id: luac.c,v 1.76 2018/06/19 01:32:02 lhf Exp $ ** print bytecodes ** See Copyright Notice in lua.h */ #include #include #define luac_c #define LUA_CORE #include "ldebug.h" #include "lobject.h" #include "lopcodes.h" #define VOID(p) ((const void*)(p)) static void PrintString(const TString* ts) { const char* s=getstr(ts); size_t i,n=tsslen(ts); printf("%c",'"'); for (i=0; ik[i]; switch (ttype(o)) { case LUA_TNIL: printf("nil"); break; case LUA_TBOOLEAN: printf(bvalue(o) ? "true" : "false"); break; case LUA_TNUMFLT: { char buff[100]; sprintf(buff,LUA_NUMBER_FMT,fltvalue(o)); printf("%s",buff); if (buff[strspn(buff,"-0123456789")]=='\0') printf(".0"); break; } case LUA_TNUMINT: printf(LUA_INTEGER_FMT,ivalue(o)); break; case LUA_TSHRSTR: case LUA_TLNGSTR: PrintString(tsvalue(o)); break; default: /* cannot happen */ printf("? type=%d",ttype(o)); break; } } #define UPVALNAME(x) ((f->upvalues[x].name) ? getstr(f->upvalues[x].name) : "-") #define MYK(x) (-1-(x)) static void PrintCode(const Proto* f) { const Instruction* code=f->code; int pc,n=f->sizecode; for (pc=0; pc0) printf("[%d]\t",line); else printf("[-]\t"); printf("%-9s\t",luaP_opnames[o]); switch (getOpMode(o)) { case iABC: printf("%d",a); if (getBMode(o)!=OpArgN) printf(" %d",ISK(b) ? (MYK(INDEXK(b))) : b); if (getCMode(o)!=OpArgN) printf(" %d",ISK(c) ? (MYK(INDEXK(c))) : c); break; case iABx: printf("%d",a); if (getBMode(o)==OpArgK) printf(" %d",MYK(bx)); if (getBMode(o)==OpArgU) printf(" %d",bx); break; case iAsBx: printf("%d %d",a,sbx); break; case iAx: printf("%d",MYK(ax)); break; } switch (o) { case OP_LOADK: printf("\t; "); PrintConstant(f,bx); break; case OP_GETUPVAL: case OP_SETUPVAL: printf("\t; %s",UPVALNAME(b)); break; case OP_GETTABUP: printf("\t; %s",UPVALNAME(b)); if (ISK(c)) { printf(" "); PrintConstant(f,INDEXK(c)); } break; case OP_SETTABUP: printf("\t; %s",UPVALNAME(a)); if (ISK(b)) { printf(" "); PrintConstant(f,INDEXK(b)); } if (ISK(c)) { printf(" "); PrintConstant(f,INDEXK(c)); } break; case OP_GETTABLE: case OP_SELF: if (ISK(c)) { printf("\t; "); PrintConstant(f,INDEXK(c)); } break; case OP_SETTABLE: case OP_ADD: case OP_SUB: case OP_MUL: case OP_MOD: case OP_POW: case OP_DIV: case OP_IDIV: case OP_BAND: case OP_BOR: case OP_BXOR: case OP_SHL: case OP_SHR: case OP_EQ: case OP_LT: case OP_LE: if (ISK(b) || ISK(c)) { printf("\t; "); if (ISK(b)) PrintConstant(f,INDEXK(b)); else printf("-"); printf(" "); if (ISK(c)) PrintConstant(f,INDEXK(c)); else printf("-"); } break; case OP_JMP: case OP_FORLOOP: case OP_FORPREP: case OP_TFORLOOP: printf("\t; to %d",sbx+pc+2); break; case OP_CLOSURE: printf("\t; %p",VOID(f->p[bx])); break; case OP_SETLIST: if (c==0) printf("\t; %d",(int)code[++pc]); else printf("\t; %d",c); break; case OP_EXTRAARG: printf("\t; "); PrintConstant(f,ax); break; default: break; } printf("\n"); } } #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; isizelocvars; printf("locals (%d) for %p:\n",n,VOID(f)); for (i=0; ilocvars[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; iupvalues[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; ip[i],full); }