From 3237c6de1490473954d58065138ed4a4204cd973 Mon Sep 17 00:00:00 2001 From: Andrew Pamment Date: Sat, 10 Dec 2016 07:31:44 +1000 Subject: [PATCH] Update LUA --- lua/Makefile | 7 +- lua/lapi.c | 48 ++-- lua/lauxlib.c | 29 ++- lua/lbaselib.c | 11 +- lua/lcode.c | 694 +++++++++++++++++++++++++++++++++---------------- lua/lcode.h | 5 +- lua/lcorolib.c | 4 +- lua/ldebug.c | 18 +- lua/ldo.c | 9 +- lua/ldo.h | 4 +- lua/lgc.c | 35 +-- lua/lgc.h | 4 +- lua/liolib.c | 19 +- lua/llex.c | 35 +-- lua/llex.h | 3 +- lua/lobject.c | 88 +++++-- lua/loslib.c | 91 ++++--- lua/lparser.c | 40 +-- lua/lparser.h | 53 ++-- lua/lstate.h | 15 +- lua/lstrlib.c | 143 ++++++---- lua/ltablib.c | 41 +-- lua/ltm.c | 18 +- lua/ltm.h | 5 +- lua/lua.h | 10 +- lua/luaconf.h | 4 +- lua/lvm.c | 105 +++++--- lua/lvm.h | 31 ++- 28 files changed, 975 insertions(+), 594 deletions(-) diff --git a/lua/Makefile b/lua/Makefile index 79f5ce8..d71c75c 100644 --- a/lua/Makefile +++ b/lua/Makefile @@ -26,7 +26,7 @@ MYOBJS= # == END OF USER SETTINGS -- NO NEED TO CHANGE ANYTHING BELOW THIS LINE ======= -PLATS= aix bsd c89 freebsd generic linux macosx mingw posix solaris minix +PLATS= aix bsd c89 freebsd generic linux macosx mingw posix solaris LUA_A= liblua.a CORE_O= lapi.o lcode.o lctype.o ldebug.o ldo.o ldump.o lfunc.o lgc.o llex.o \ @@ -101,11 +101,8 @@ c89: @echo '' -minix: - $(MAKE) $(ALL) CC="clang" SYSCFLAGS="-DLUA_USE_LINUX" SYSLIBS="-L/usr/pkg/lib -Wl,-E -lreadline" - freebsd: - $(MAKE) $(ALL) CC="cc" SYSCFLAGS="-DLUA_USE_LINUX -I/usr/local/include" SYSLIBS="-Wl,-E -L/usr/local/lib -lreadline" + $(MAKE) $(ALL) SYSCFLAGS="-DLUA_USE_LINUX" SYSLIBS="-Wl,-E -lreadline" generic: $(ALL) diff --git a/lua/lapi.c b/lua/lapi.c index 9a6a3fb..c9455a5 100644 --- a/lua/lapi.c +++ b/lua/lapi.c @@ -1,5 +1,5 @@ /* -** $Id: lapi.c,v 2.257 2015/11/02 18:48:07 roberto Exp $ +** $Id: lapi.c,v 2.259 2016/02/29 14:27:14 roberto Exp $ ** Lua API ** See Copyright Notice in lua.h */ @@ -378,9 +378,9 @@ LUA_API const char *lua_tolstring (lua_State *L, int idx, size_t *len) { return NULL; } lua_lock(L); /* 'luaO_tostring' may create a new string */ + luaO_tostring(L, o); luaC_checkGC(L); o = index2addr(L, idx); /* previous call may reallocate the stack */ - luaO_tostring(L, o); lua_unlock(L); } if (len != NULL) @@ -479,10 +479,10 @@ LUA_API void lua_pushinteger (lua_State *L, lua_Integer n) { LUA_API const char *lua_pushlstring (lua_State *L, const char *s, size_t len) { TString *ts; lua_lock(L); - luaC_checkGC(L); ts = (len == 0) ? luaS_new(L, "") : luaS_newlstr(L, s, len); setsvalue2s(L, L->top, ts); api_incr_top(L); + luaC_checkGC(L); lua_unlock(L); return getstr(ts); } @@ -494,12 +494,12 @@ LUA_API const char *lua_pushstring (lua_State *L, const char *s) { setnilvalue(L->top); else { TString *ts; - luaC_checkGC(L); ts = luaS_new(L, s); setsvalue2s(L, L->top, ts); s = getstr(ts); /* internal copy's address */ } api_incr_top(L); + luaC_checkGC(L); lua_unlock(L); return s; } @@ -509,8 +509,8 @@ LUA_API const char *lua_pushvfstring (lua_State *L, const char *fmt, va_list argp) { const char *ret; lua_lock(L); - luaC_checkGC(L); ret = luaO_pushvfstring(L, fmt, argp); + luaC_checkGC(L); lua_unlock(L); return ret; } @@ -520,10 +520,10 @@ LUA_API const char *lua_pushfstring (lua_State *L, const char *fmt, ...) { const char *ret; va_list argp; lua_lock(L); - luaC_checkGC(L); va_start(argp, fmt); ret = luaO_pushvfstring(L, fmt, argp); va_end(argp); + luaC_checkGC(L); lua_unlock(L); return ret; } @@ -538,7 +538,6 @@ LUA_API void lua_pushcclosure (lua_State *L, lua_CFunction fn, int n) { CClosure *cl; api_checknelems(L, n); api_check(L, n <= MAXUPVAL, "upvalue index too large"); - luaC_checkGC(L); cl = luaF_newCclosure(L, n); cl->f = fn; L->top -= n; @@ -549,6 +548,7 @@ LUA_API void lua_pushcclosure (lua_State *L, lua_CFunction fn, int n) { setclCvalue(L, L->top, cl); } api_incr_top(L); + luaC_checkGC(L); lua_unlock(L); } @@ -585,16 +585,16 @@ LUA_API int lua_pushthread (lua_State *L) { static int auxgetstr (lua_State *L, const TValue *t, const char *k) { - const TValue *aux; + const TValue *slot; TString *str = luaS_new(L, k); - if (luaV_fastget(L, t, str, aux, luaH_getstr)) { - setobj2s(L, L->top, aux); + if (luaV_fastget(L, t, str, slot, luaH_getstr)) { + setobj2s(L, L->top, slot); api_incr_top(L); } else { setsvalue2s(L, L->top, str); api_incr_top(L); - luaV_finishget(L, t, L->top - 1, L->top - 1, aux); + luaV_finishget(L, t, L->top - 1, L->top - 1, slot); } lua_unlock(L); return ttnov(L->top - 1); @@ -626,17 +626,17 @@ LUA_API int lua_getfield (lua_State *L, int idx, const char *k) { LUA_API int lua_geti (lua_State *L, int idx, lua_Integer n) { StkId t; - const TValue *aux; + const TValue *slot; lua_lock(L); t = index2addr(L, idx); - if (luaV_fastget(L, t, n, aux, luaH_getint)) { - setobj2s(L, L->top, aux); + if (luaV_fastget(L, t, n, slot, luaH_getint)) { + setobj2s(L, L->top, slot); api_incr_top(L); } else { setivalue(L->top, n); api_incr_top(L); - luaV_finishget(L, t, L->top - 1, L->top - 1, aux); + luaV_finishget(L, t, L->top - 1, L->top - 1, slot); } lua_unlock(L); return ttnov(L->top - 1); @@ -683,12 +683,12 @@ LUA_API int lua_rawgetp (lua_State *L, int idx, const void *p) { LUA_API void lua_createtable (lua_State *L, int narray, int nrec) { Table *t; lua_lock(L); - luaC_checkGC(L); t = luaH_new(L); sethvalue(L, L->top, t); api_incr_top(L); if (narray > 0 || nrec > 0) luaH_resize(L, t, narray, nrec); + luaC_checkGC(L); lua_unlock(L); } @@ -740,15 +740,15 @@ LUA_API int lua_getuservalue (lua_State *L, int idx) { ** t[k] = value at the top of the stack (where 'k' is a string) */ static void auxsetstr (lua_State *L, const TValue *t, const char *k) { - const TValue *aux; + const TValue *slot; TString *str = luaS_new(L, k); api_checknelems(L, 1); - if (luaV_fastset(L, t, str, aux, luaH_getstr, L->top - 1)) + if (luaV_fastset(L, t, str, slot, luaH_getstr, L->top - 1)) L->top--; /* pop value */ else { setsvalue2s(L, L->top, str); /* push 'str' (to make it a TValue) */ api_incr_top(L); - luaV_finishset(L, t, L->top - 1, L->top - 2, aux); + luaV_finishset(L, t, L->top - 1, L->top - 2, slot); L->top -= 2; /* pop value and key */ } lua_unlock(L); /* lock done by caller */ @@ -781,16 +781,16 @@ LUA_API void lua_setfield (lua_State *L, int idx, const char *k) { LUA_API void lua_seti (lua_State *L, int idx, lua_Integer n) { StkId t; - const TValue *aux; + const TValue *slot; lua_lock(L); api_checknelems(L, 1); t = index2addr(L, idx); - if (luaV_fastset(L, t, n, aux, luaH_getint, L->top - 1)) + if (luaV_fastset(L, t, n, slot, luaH_getint, L->top - 1)) L->top--; /* pop value */ else { setivalue(L->top, n); api_incr_top(L); - luaV_finishset(L, t, L->top - 1, L->top - 2, aux); + luaV_finishset(L, t, L->top - 1, L->top - 2, slot); L->top -= 2; /* pop value and key */ } lua_unlock(L); @@ -1140,7 +1140,6 @@ LUA_API void lua_concat (lua_State *L, int n) { lua_lock(L); api_checknelems(L, n); if (n >= 2) { - luaC_checkGC(L); luaV_concat(L, n); } else if (n == 0) { /* push empty string */ @@ -1148,6 +1147,7 @@ LUA_API void lua_concat (lua_State *L, int n) { api_incr_top(L); } /* else n == 1; nothing to do */ + luaC_checkGC(L); lua_unlock(L); } @@ -1183,10 +1183,10 @@ LUA_API void lua_setallocf (lua_State *L, lua_Alloc f, void *ud) { LUA_API void *lua_newuserdata (lua_State *L, size_t size) { Udata *u; lua_lock(L); - luaC_checkGC(L); u = luaS_newudata(L, size); setuvalue(L, L->top, u); api_incr_top(L); + luaC_checkGC(L); lua_unlock(L); return getudatamem(u); } diff --git a/lua/lauxlib.c b/lua/lauxlib.c index 5d362c3..bacf43b 100644 --- a/lua/lauxlib.c +++ b/lua/lauxlib.c @@ -1,5 +1,5 @@ /* -** $Id: lauxlib.c,v 1.284 2015/11/19 19:16:22 roberto Exp $ +** $Id: lauxlib.c,v 1.286 2016/01/08 15:33:09 roberto Exp $ ** Auxiliary functions for building Lua libraries ** See Copyright Notice in lua.h */ @@ -17,7 +17,8 @@ #include -/* This file uses only the official API of Lua. +/* +** This file uses only the official API of Lua. ** Any function declared here could be written as an application function. */ @@ -198,6 +199,10 @@ static void tag_error (lua_State *L, int arg, int tag) { } +/* +** The use of 'lua_pushfstring' ensures this function does not +** need reserved stack space when called. +*/ LUALIB_API void luaL_where (lua_State *L, int level) { lua_Debug ar; if (lua_getstack(L, level, &ar)) { /* check function at level */ @@ -207,10 +212,15 @@ LUALIB_API void luaL_where (lua_State *L, int level) { return; } } - lua_pushliteral(L, ""); /* else, no information available... */ + lua_pushfstring(L, ""); /* else, no information available... */ } +/* +** Again, the use of 'lua_pushvfstring' ensures this function does +** not need reserved stack space when called. (At worst, it generates +** an error with "stack overflow" instead of the given message.) +*/ LUALIB_API int luaL_error (lua_State *L, const char *fmt, ...) { va_list argp; va_start(argp, fmt); @@ -349,10 +359,15 @@ LUALIB_API int luaL_checkoption (lua_State *L, int arg, const char *def, } +/* +** Ensures the stack has at least 'space' extra slots, raising an error +** if it cannot fulfill the request. (The error handling needs a few +** extra slots to format the error message. In case of an error without +** this extra space, Lua will generate the same 'stack overflow' error, +** but without 'msg'.) +*/ LUALIB_API void luaL_checkstack (lua_State *L, int space, const char *msg) { - /* keep some extra space to run error routines, if needed */ - const int extra = LUA_MINSTACK; - if (!lua_checkstack(L, space + extra)) { + if (!lua_checkstack(L, space)) { if (msg) luaL_error(L, "stack overflow (%s)", msg); else @@ -678,7 +693,7 @@ static int skipcomment (LoadF *lf, int *cp) { if (c == '#') { /* first line is a comment (Unix exec. file)? */ do { /* skip first line */ c = getc(lf->f); - } while (c != EOF && c != '\n') ; + } while (c != EOF && c != '\n'); *cp = getc(lf->f); /* skip end-of-line, if present */ return 1; /* there was a comment */ } diff --git a/lua/lbaselib.c b/lua/lbaselib.c index 861823d..d481c4e 100644 --- a/lua/lbaselib.c +++ b/lua/lbaselib.c @@ -1,5 +1,5 @@ /* -** $Id: lbaselib.c,v 1.312 2015/10/29 15:21:04 roberto Exp $ +** $Id: lbaselib.c,v 1.313 2016/04/11 19:18:40 roberto Exp $ ** Basic library ** See Copyright Notice in lua.h */ @@ -102,8 +102,8 @@ static int luaB_tonumber (lua_State *L) { static int luaB_error (lua_State *L) { int level = (int)luaL_optinteger(L, 2, 1); lua_settop(L, 1); - if (lua_isstring(L, 1) && level > 0) { /* add extra information? */ - luaL_where(L, level); + if (lua_type(L, 1) == LUA_TSTRING && level > 0) { + luaL_where(L, level); /* add extra information */ lua_pushvalue(L, 1); lua_concat(L, 2); } @@ -251,9 +251,8 @@ static int ipairsaux (lua_State *L) { /* -** This function will use either 'ipairsaux' or 'ipairsaux_raw' to -** traverse a table, depending on whether the table has metamethods -** that can affect the traversal. +** 'ipairs' function. Returns 'ipairsaux', given "table", 0. +** (The given "table" may not be a table.) */ static int luaB_ipairs (lua_State *L) { #if defined(LUA_COMPAT_IPAIRS) diff --git a/lua/lcode.c b/lua/lcode.c index 7c6918f..2cd0dd2 100644 --- a/lua/lcode.c +++ b/lua/lcode.c @@ -1,5 +1,5 @@ /* -** $Id: lcode.c,v 2.103 2015/11/19 19:16:22 roberto Exp $ +** $Id: lcode.c,v 2.109 2016/05/13 19:09:21 roberto Exp $ ** Code generator for Lua ** See Copyright Notice in lua.h */ @@ -36,6 +36,10 @@ #define hasjumps(e) ((e)->t != (e)->f) +/* +** If expression is a numeric constant, fills 'v' with its value +** and returns 1. Otherwise, returns 0. +*/ static int tonumeral(expdesc *e, TValue *v) { if (hasjumps(e)) return 0; /* not a numeral */ @@ -51,13 +55,19 @@ static int tonumeral(expdesc *e, TValue *v) { } +/* +** Create a OP_LOADNIL instruction, but try to optimize: if the previous +** instruction is also OP_LOADNIL and ranges are compatible, adjust +** range of previous instruction instead of emitting a new one. (For +** instance, 'local a; local b' will generate a single opcode.) +*/ void luaK_nil (FuncState *fs, int from, int n) { Instruction *previous; int l = from + n - 1; /* last register to set nil */ if (fs->pc > fs->lasttarget) { /* no jumps to current position? */ previous = &fs->f->code[fs->pc-1]; - if (GET_OPCODE(*previous) == OP_LOADNIL) { - int pfrom = GETARG_A(*previous); + if (GET_OPCODE(*previous) == OP_LOADNIL) { /* previous is LOADNIL? */ + int pfrom = GETARG_A(*previous); /* get previous range */ int pl = pfrom + GETARG_B(*previous); if ((pfrom <= from && from <= pl + 1) || (from <= pfrom && pfrom <= l + 1)) { /* can connect both? */ @@ -73,37 +83,84 @@ void luaK_nil (FuncState *fs, int from, int n) { } +/* +** Gets the destination address of a jump instruction. Used to traverse +** a list of jumps. +*/ +static int getjump (FuncState *fs, int pc) { + int offset = GETARG_sBx(fs->f->code[pc]); + if (offset == NO_JUMP) /* point to itself represents end of list */ + return NO_JUMP; /* end of list */ + else + return (pc+1)+offset; /* turn offset into absolute position */ +} + + +/* +** Fix jump instruction at position 'pc' to jump to 'dest'. +** (Jump addresses are relative in Lua) +*/ +static void fixjump (FuncState *fs, int pc, int dest) { + Instruction *jmp = &fs->f->code[pc]; + int offset = dest - (pc + 1); + lua_assert(dest != NO_JUMP); + if (abs(offset) > MAXARG_sBx) + luaX_syntaxerror(fs->ls, "control structure too long"); + SETARG_sBx(*jmp, offset); +} + + +/* +** Concatenate jump-list 'l2' into jump-list 'l1' +*/ +void luaK_concat (FuncState *fs, int *l1, int l2) { + if (l2 == NO_JUMP) return; /* nothing to concatenate? */ + else if (*l1 == NO_JUMP) /* no original list? */ + *l1 = l2; /* 'l1' points to 'l2' */ + else { + int list = *l1; + int next; + while ((next = getjump(fs, list)) != NO_JUMP) /* find last element */ + list = next; + fixjump(fs, list, l2); /* last element links to 'l2' */ + } +} + + +/* +** Create a jump instruction and return its position, so its destination +** can be fixed later (with 'fixjump'). If there are jumps to +** this position (kept in 'jpc'), link them all together so that +** 'patchlistaux' will fix all them directly to the final destination. +*/ int luaK_jump (FuncState *fs) { int jpc = fs->jpc; /* save list of jumps to here */ int j; - fs->jpc = NO_JUMP; + fs->jpc = NO_JUMP; /* no more jumps to here */ j = luaK_codeAsBx(fs, OP_JMP, 0, NO_JUMP); luaK_concat(fs, &j, jpc); /* keep them on hold */ return j; } +/* +** Code a 'return' instruction +*/ void luaK_ret (FuncState *fs, int first, int nret) { luaK_codeABC(fs, OP_RETURN, first, nret+1, 0); } +/* +** Code a "conditional jump", that is, a test or comparison opcode +** followed by a jump. Return jump position. +*/ static int condjump (FuncState *fs, OpCode op, int A, int B, int C) { luaK_codeABC(fs, op, A, B, C); return luaK_jump(fs); } -static void fixjump (FuncState *fs, int pc, int dest) { - Instruction *jmp = &fs->f->code[pc]; - int offset = dest-(pc+1); - lua_assert(dest != NO_JUMP); - if (abs(offset) > MAXARG_sBx) - luaX_syntaxerror(fs->ls, "control structure too long"); - SETARG_sBx(*jmp, offset); -} - - /* ** returns current 'pc' and marks it as a jump target (to avoid wrong ** optimizations with consecutive instructions not in the same basic block). @@ -114,15 +171,11 @@ int luaK_getlabel (FuncState *fs) { } -static int getjump (FuncState *fs, int pc) { - int offset = GETARG_sBx(fs->f->code[pc]); - if (offset == NO_JUMP) /* point to itself represents end of list */ - return NO_JUMP; /* end of list */ - else - return (pc+1)+offset; /* turn offset into absolute position */ -} - - +/* +** Returns the position of the instruction "controlling" a given +** jump (that is, its condition), or the jump itself if it is +** unconditional. +*/ static Instruction *getjumpcontrol (FuncState *fs, int pc) { Instruction *pi = &fs->f->code[pc]; if (pc >= 1 && testTMode(GET_OPCODE(*(pi-1)))) @@ -133,37 +186,41 @@ static Instruction *getjumpcontrol (FuncState *fs, int pc) { /* -** check whether list has any jump that do not produce a value -** (or produce an inverted value) +** Patch destination register for a TESTSET instruction. +** If instruction in position 'node' is not a TESTSET, return 0 ("fails"). +** Otherwise, if 'reg' is not 'NO_REG', set it as the destination +** register. Otherwise, change instruction to a simple 'TEST' (produces +** no register value) */ -static int need_value (FuncState *fs, int list) { - for (; list != NO_JUMP; list = getjump(fs, list)) { - Instruction i = *getjumpcontrol(fs, list); - if (GET_OPCODE(i) != OP_TESTSET) return 1; - } - return 0; /* not found */ -} - - static int patchtestreg (FuncState *fs, int node, int reg) { Instruction *i = getjumpcontrol(fs, node); if (GET_OPCODE(*i) != OP_TESTSET) return 0; /* cannot patch other instructions */ if (reg != NO_REG && reg != GETARG_B(*i)) SETARG_A(*i, reg); - else /* no register to put value or register already has the value */ + else { + /* no register to put value or register already has the value; + change instruction to simple test */ *i = CREATE_ABC(OP_TEST, GETARG_B(*i), 0, GETARG_C(*i)); - + } return 1; } +/* +** Traverse a list of tests ensuring no one produces a value +*/ static void removevalues (FuncState *fs, int list) { for (; list != NO_JUMP; list = getjump(fs, list)) patchtestreg(fs, list, NO_REG); } +/* +** Traverse a list of tests, patching their destination address and +** registers: tests producing values jump to 'vtarget' (and put their +** values in 'reg'), other tests jump to 'dtarget'. +*/ static void patchlistaux (FuncState *fs, int list, int vtarget, int reg, int dtarget) { while (list != NO_JUMP) { @@ -177,15 +234,35 @@ static void patchlistaux (FuncState *fs, int list, int vtarget, int reg, } +/* +** Ensure all pending jumps to current position are fixed (jumping +** to current position with no values) and reset list of pending +** jumps +*/ static void dischargejpc (FuncState *fs) { patchlistaux(fs, fs->jpc, fs->pc, NO_REG, fs->pc); fs->jpc = NO_JUMP; } +/* +** Add elements in 'list' to list of pending jumps to "here" +** (current position) +*/ +void luaK_patchtohere (FuncState *fs, int list) { + luaK_getlabel(fs); /* mark "here" as a jump target */ + luaK_concat(fs, &fs->jpc, list); +} + + +/* +** Path all jumps in 'list' to jump to 'target'. +** (The assert means that we cannot fix a jump to a forward address +** because we only know addresses once code is generated.) +*/ void luaK_patchlist (FuncState *fs, int list, int target) { - if (target == fs->pc) - luaK_patchtohere(fs, list); + if (target == fs->pc) /* 'target' is current position? */ + luaK_patchtohere(fs, list); /* add list to pending jumps */ else { lua_assert(target < fs->pc); patchlistaux(fs, list, target, NO_REG, target); @@ -193,39 +270,26 @@ void luaK_patchlist (FuncState *fs, int list, int target) { } +/* +** Path all jumps in 'list' to close upvalues up to given 'level' +** (The assertion checks that jumps either were closing nothing +** or were closing higher levels, from inner blocks.) +*/ void luaK_patchclose (FuncState *fs, int list, int level) { level++; /* argument is +1 to reserve 0 as non-op */ - while (list != NO_JUMP) { - int next = getjump(fs, list); + for (; list != NO_JUMP; list = getjump(fs, list)) { lua_assert(GET_OPCODE(fs->f->code[list]) == OP_JMP && (GETARG_A(fs->f->code[list]) == 0 || GETARG_A(fs->f->code[list]) >= level)); SETARG_A(fs->f->code[list], level); - list = next; - } -} - - -void luaK_patchtohere (FuncState *fs, int list) { - luaK_getlabel(fs); - luaK_concat(fs, &fs->jpc, list); -} - - -void luaK_concat (FuncState *fs, int *l1, int l2) { - if (l2 == NO_JUMP) return; - else if (*l1 == NO_JUMP) - *l1 = l2; - else { - int list = *l1; - int next; - while ((next = getjump(fs, list)) != NO_JUMP) /* find last element */ - list = next; - fixjump(fs, list, l2); } } +/* +** Emit instruction 'i', checking for array sizes and saving also its +** line information. Return 'i' position. +*/ static int luaK_code (FuncState *fs, Instruction i) { Proto *f = fs->f; dischargejpc(fs); /* 'pc' will change */ @@ -241,6 +305,10 @@ static int luaK_code (FuncState *fs, Instruction i) { } +/* +** Format and emit an 'iABC' instruction. (Assertions check consistency +** of parameters versus opcode.) +*/ int luaK_codeABC (FuncState *fs, OpCode o, int a, int b, int c) { lua_assert(getOpMode(o) == iABC); lua_assert(getBMode(o) != OpArgN || b == 0); @@ -250,6 +318,9 @@ int luaK_codeABC (FuncState *fs, OpCode o, int a, int b, int c) { } +/* +** Format and emit an 'iABx' instruction. +*/ int luaK_codeABx (FuncState *fs, OpCode o, int a, unsigned int bc) { lua_assert(getOpMode(o) == iABx || getOpMode(o) == iAsBx); lua_assert(getCMode(o) == OpArgN); @@ -258,12 +329,20 @@ int luaK_codeABx (FuncState *fs, OpCode o, int a, unsigned int bc) { } +/* +** Emit an "extra argument" instruction (format 'iAx') +*/ static int codeextraarg (FuncState *fs, int a) { lua_assert(a <= MAXARG_Ax); return luaK_code(fs, CREATE_Ax(OP_EXTRAARG, a)); } +/* +** Emit a "load constant" instruction, using either 'OP_LOADK' +** (if constant index 'k' fits in 18 bits) or an 'OP_LOADKX' +** instruction with "extra argument". +*/ int luaK_codek (FuncState *fs, int reg, int k) { if (k <= MAXARG_Bx) return luaK_codeABx(fs, OP_LOADK, reg, k); @@ -275,6 +354,10 @@ int luaK_codek (FuncState *fs, int reg, int k) { } +/* +** Check register-stack level, keeping track of its maximum size +** in field 'maxstacksize' +*/ void luaK_checkstack (FuncState *fs, int n) { int newstack = fs->freereg + n; if (newstack > fs->f->maxstacksize) { @@ -286,12 +369,20 @@ void luaK_checkstack (FuncState *fs, int n) { } +/* +** Reserve 'n' registers in register stack +*/ void luaK_reserveregs (FuncState *fs, int n) { luaK_checkstack(fs, n); fs->freereg += n; } +/* +** Free register 'reg', if it is neither a constant index nor +** a local variable. +) +*/ static void freereg (FuncState *fs, int reg) { if (!ISK(reg) && reg >= fs->nactvar) { fs->freereg--; @@ -300,6 +391,9 @@ static void freereg (FuncState *fs, int reg) { } +/* +** Free register used by expression 'e' (if any) +*/ static void freeexp (FuncState *fs, expdesc *e) { if (e->k == VNONRELOC) freereg(fs, e->u.info); @@ -307,8 +401,29 @@ static void freeexp (FuncState *fs, expdesc *e) { /* +** Free registers used by expressions 'e1' and 'e2' (if any) in proper +** order. +*/ +static void freeexps (FuncState *fs, expdesc *e1, expdesc *e2) { + int r1 = (e1->k == VNONRELOC) ? e1->u.info : -1; + int r2 = (e2->k == VNONRELOC) ? e2->u.info : -1; + if (r1 > r2) { + freereg(fs, r1); + freereg(fs, r2); + } + else { + freereg(fs, r2); + freereg(fs, r1); + } +} + + +/* +** Add constant 'v' to prototype's list of constants (field 'k'). ** Use scanner's table to cache position of constants in constant list -** and try to reuse constants +** and try to reuse constants. Because some values should not be used +** as keys (nil cannot be a key, integer keys can collapse with float +** keys), the caller must provide a useful 'key' for indexing the cache. */ static int addk (FuncState *fs, TValue *key, TValue *v) { lua_State *L = fs->ls->L; @@ -337,17 +452,21 @@ static int addk (FuncState *fs, TValue *key, TValue *v) { } +/* +** Add a string to list of constants and return its index. +*/ int luaK_stringK (FuncState *fs, TString *s) { TValue o; setsvalue(fs->ls->L, &o, s); - return addk(fs, &o, &o); + return addk(fs, &o, &o); /* use string itself as key */ } /* -** Integers use userdata as keys to avoid collision with floats with same -** value; conversion to 'void*' used only for hashing, no "precision" -** problems +** Add an integer to list of constants and return its index. +** Integers use userdata as keys to avoid collision with floats with +** same value; conversion to 'void*' is used only for hashing, so there +** are no "precision" problems. */ int luaK_intK (FuncState *fs, lua_Integer n) { TValue k, o; @@ -356,21 +475,29 @@ int luaK_intK (FuncState *fs, lua_Integer n) { return addk(fs, &k, &o); } - +/* +** Add a float to list of constants and return its index. +*/ static int luaK_numberK (FuncState *fs, lua_Number r) { TValue o; setfltvalue(&o, r); - return addk(fs, &o, &o); + return addk(fs, &o, &o); /* use number itself as key */ } +/* +** Add a boolean to list of constants and return its index. +*/ static int boolK (FuncState *fs, int b) { TValue o; setbvalue(&o, b); - return addk(fs, &o, &o); + return addk(fs, &o, &o); /* use boolean itself as key */ } +/* +** Add nil to list of constants and return its index. +*/ static int nilK (FuncState *fs) { TValue k, v; setnilvalue(&v); @@ -380,54 +507,79 @@ static int nilK (FuncState *fs) { } +/* +** Fix an expression to return the number of results 'nresults'. +** Either 'e' is a multi-ret expression (function call or vararg) +** or 'nresults' is LUA_MULTRET (as any expression can satisfy that). +*/ void luaK_setreturns (FuncState *fs, expdesc *e, int nresults) { if (e->k == VCALL) { /* expression is an open function call? */ - SETARG_C(getcode(fs, e), nresults+1); + SETARG_C(getinstruction(fs, e), nresults + 1); } else if (e->k == VVARARG) { - SETARG_B(getcode(fs, e), nresults+1); - SETARG_A(getcode(fs, e), fs->freereg); + Instruction *pc = &getinstruction(fs, e); + SETARG_B(*pc, nresults + 1); + SETARG_A(*pc, fs->freereg); luaK_reserveregs(fs, 1); } + else lua_assert(nresults == LUA_MULTRET); } +/* +** Fix an expression to return one result. +** If expression is not a multi-ret expression (function call or +** vararg), it already returns one result, so nothing needs to be done. +** Function calls become VNONRELOC expressions (as its result comes +** fixed in the base register of the call), while vararg expressions +** become VRELOCABLE (as OP_VARARG puts its results where it wants). +** (Calls are created returning one result, so that does not need +** to be fixed.) +*/ void luaK_setoneret (FuncState *fs, expdesc *e) { if (e->k == VCALL) { /* expression is an open function call? */ - e->k = VNONRELOC; - e->u.info = GETARG_A(getcode(fs, e)); + /* already returns 1 value */ + lua_assert(GETARG_C(getinstruction(fs, e)) == 2); + e->k = VNONRELOC; /* result has fixed position */ + e->u.info = GETARG_A(getinstruction(fs, e)); } else if (e->k == VVARARG) { - SETARG_B(getcode(fs, e), 2); + SETARG_B(getinstruction(fs, e), 2); e->k = VRELOCABLE; /* can relocate its simple result */ } } +/* +** Ensure that expression 'e' is not a variable. +*/ void luaK_dischargevars (FuncState *fs, expdesc *e) { switch (e->k) { - case VLOCAL: { - e->k = VNONRELOC; + case VLOCAL: { /* already in a register */ + e->k = VNONRELOC; /* becomes a non-relocatable value */ break; } - case VUPVAL: { + case VUPVAL: { /* move value to some (pending) register */ e->u.info = luaK_codeABC(fs, OP_GETUPVAL, 0, e->u.info, 0); e->k = VRELOCABLE; break; } case VINDEXED: { - OpCode op = OP_GETTABUP; /* assume 't' is in an upvalue */ + OpCode op; freereg(fs, e->u.ind.idx); - if (e->u.ind.vt == VLOCAL) { /* 't' is in a register? */ + if (e->u.ind.vt == VLOCAL) { /* is 't' in a register? */ freereg(fs, e->u.ind.t); op = OP_GETTABLE; } + else { + lua_assert(e->u.ind.vt == VUPVAL); + op = OP_GETTABUP; /* 't' is in an upvalue */ + } e->u.info = luaK_codeABC(fs, op, 0, e->u.ind.t, e->u.ind.idx); e->k = VRELOCABLE; break; } - case VVARARG: - case VCALL: { + case VVARARG: case VCALL: { luaK_setoneret(fs, e); break; } @@ -436,12 +588,10 @@ void luaK_dischargevars (FuncState *fs, expdesc *e) { } -static int code_label (FuncState *fs, int A, int b, int jump) { - luaK_getlabel(fs); /* those instructions may be jump targets */ - return luaK_codeABC(fs, OP_LOADBOOL, A, b, jump); -} - - +/* +** Ensures expression value is in register 'reg' (and therefore +** 'e' will become a non-relocatable expression). +*/ static void discharge2reg (FuncState *fs, expdesc *e, int reg) { luaK_dischargevars(fs, e); switch (e->k) { @@ -466,8 +616,8 @@ static void discharge2reg (FuncState *fs, expdesc *e, int reg) { break; } case VRELOCABLE: { - Instruction *pc = &getcode(fs, e); - SETARG_A(*pc, reg); + Instruction *pc = &getinstruction(fs, e); + SETARG_A(*pc, reg); /* instruction will put result in 'reg' */ break; } case VNONRELOC: { @@ -476,7 +626,7 @@ static void discharge2reg (FuncState *fs, expdesc *e, int reg) { break; } default: { - lua_assert(e->k == VVOID || e->k == VJMP); + lua_assert(e->k == VJMP); return; /* nothing to do... */ } } @@ -485,17 +635,46 @@ static void discharge2reg (FuncState *fs, expdesc *e, int reg) { } +/* +** Ensures expression value is in any register. +*/ static void discharge2anyreg (FuncState *fs, expdesc *e) { - if (e->k != VNONRELOC) { - luaK_reserveregs(fs, 1); - discharge2reg(fs, e, fs->freereg-1); + if (e->k != VNONRELOC) { /* no fixed register yet? */ + luaK_reserveregs(fs, 1); /* get a register */ + discharge2reg(fs, e, fs->freereg-1); /* put value there */ } } +static int code_loadbool (FuncState *fs, int A, int b, int jump) { + luaK_getlabel(fs); /* those instructions may be jump targets */ + return luaK_codeABC(fs, OP_LOADBOOL, A, b, jump); +} + + +/* +** check whether list has any jump that do not produce a value +** or produce an inverted value +*/ +static int need_value (FuncState *fs, int list) { + for (; list != NO_JUMP; list = getjump(fs, list)) { + Instruction i = *getjumpcontrol(fs, list); + if (GET_OPCODE(i) != OP_TESTSET) return 1; + } + return 0; /* not found */ +} + + +/* +** Ensures final expression result (including results from its jump +** lists) is in register 'reg'. +** If expression has jumps, need to patch these jumps either to +** its final position or to "load" instructions (for those tests +** that do not produce values). +*/ static void exp2reg (FuncState *fs, expdesc *e, int reg) { discharge2reg(fs, e, reg); - if (e->k == VJMP) + if (e->k == VJMP) /* expression itself is a test? */ luaK_concat(fs, &e->t, e->u.info); /* put this jump in 't' list */ if (hasjumps(e)) { int final; /* position after whole expression */ @@ -503,8 +682,8 @@ static void exp2reg (FuncState *fs, expdesc *e, int reg) { int p_t = NO_JUMP; /* position of an eventual LOAD true */ if (need_value(fs, e->t) || need_value(fs, e->f)) { int fj = (e->k == VJMP) ? NO_JUMP : luaK_jump(fs); - p_f = code_label(fs, reg, 0, 1); - p_t = code_label(fs, reg, 1, 0); + p_f = code_loadbool(fs, reg, 0, 1); + p_t = code_loadbool(fs, reg, 1, 0); luaK_patchtohere(fs, fj); } final = luaK_getlabel(fs); @@ -517,6 +696,10 @@ static void exp2reg (FuncState *fs, expdesc *e, int reg) { } +/* +** Ensures final expression result (including results from its jump +** lists) is in next available register. +*/ void luaK_exp2nextreg (FuncState *fs, expdesc *e) { luaK_dischargevars(fs, e); freeexp(fs, e); @@ -525,26 +708,39 @@ void luaK_exp2nextreg (FuncState *fs, expdesc *e) { } +/* +** Ensures final expression result (including results from its jump +** lists) is in some (any) register and return that register. +*/ int luaK_exp2anyreg (FuncState *fs, expdesc *e) { luaK_dischargevars(fs, e); - if (e->k == VNONRELOC) { - if (!hasjumps(e)) return e->u.info; /* exp is already in a register */ + if (e->k == VNONRELOC) { /* expression already has a register? */ + if (!hasjumps(e)) /* no jumps? */ + return e->u.info; /* result is already in a register */ if (e->u.info >= fs->nactvar) { /* reg. is not a local? */ - exp2reg(fs, e, e->u.info); /* put value on it */ + exp2reg(fs, e, e->u.info); /* put final result in it */ return e->u.info; } } - luaK_exp2nextreg(fs, e); /* default */ + luaK_exp2nextreg(fs, e); /* otherwise, use next available register */ return e->u.info; } +/* +** Ensures final expression result is either in a register or in an +** upvalue. +*/ void luaK_exp2anyregup (FuncState *fs, expdesc *e) { if (e->k != VUPVAL || hasjumps(e)) luaK_exp2anyreg(fs, e); } +/* +** Ensures final expression result is either in a register or it is +** a constant. +*/ void luaK_exp2val (FuncState *fs, expdesc *e) { if (hasjumps(e)) luaK_exp2anyreg(fs, e); @@ -553,35 +749,26 @@ void luaK_exp2val (FuncState *fs, expdesc *e) { } +/* +** Ensures final expression result is in a valid R/K index +** (that is, it is either in a register or in 'k' with an index +** in the range of R/K indices). +** Returns R/K index. +*/ int luaK_exp2RK (FuncState *fs, expdesc *e) { luaK_exp2val(fs, e); - switch (e->k) { - case VTRUE: - case VFALSE: - case VNIL: { - if (fs->nk <= MAXINDEXRK) { /* constant fits in RK operand? */ - e->u.info = (e->k == VNIL) ? nilK(fs) : boolK(fs, (e->k == VTRUE)); - e->k = VK; - return RKASK(e->u.info); - } - else break; - } - case VKINT: { - e->u.info = luaK_intK(fs, e->u.ival); - e->k = VK; - goto vk; - } - case VKFLT: { - e->u.info = luaK_numberK(fs, e->u.nval); - e->k = VK; - } - /* FALLTHROUGH */ - case VK: { + switch (e->k) { /* move constants to 'k' */ + case VTRUE: e->u.info = boolK(fs, 1); goto vk; + case VFALSE: e->u.info = boolK(fs, 0); goto vk; + case VNIL: e->u.info = nilK(fs); goto vk; + case VKINT: e->u.info = luaK_intK(fs, e->u.ival); goto vk; + case VKFLT: e->u.info = luaK_numberK(fs, e->u.nval); goto vk; + case VK: vk: + e->k = VK; if (e->u.info <= MAXINDEXRK) /* constant fits in 'argC'? */ return RKASK(e->u.info); else break; - } default: break; } /* not a constant in the right range: put it in a register */ @@ -589,11 +776,14 @@ int luaK_exp2RK (FuncState *fs, expdesc *e) { } +/* +** Generate code to store result of expression 'ex' into variable 'var'. +*/ void luaK_storevar (FuncState *fs, expdesc *var, expdesc *ex) { switch (var->k) { case VLOCAL: { freeexp(fs, ex); - exp2reg(fs, ex, var->u.info); + exp2reg(fs, ex, var->u.info); /* compute 'ex' into proper place */ return; } case VUPVAL: { @@ -607,29 +797,32 @@ void luaK_storevar (FuncState *fs, expdesc *var, expdesc *ex) { luaK_codeABC(fs, op, var->u.ind.t, var->u.ind.idx, e); break; } - default: { - lua_assert(0); /* invalid var kind to store */ - break; - } + default: lua_assert(0); /* invalid var kind to store */ } freeexp(fs, ex); } +/* +** Emit SELF instruction (convert expression 'e' into 'e:key(e,'). +*/ void luaK_self (FuncState *fs, expdesc *e, expdesc *key) { int ereg; luaK_exp2anyreg(fs, e); ereg = e->u.info; /* register where 'e' was placed */ freeexp(fs, e); e->u.info = fs->freereg; /* base register for op_self */ - e->k = VNONRELOC; + e->k = VNONRELOC; /* self expression has a fixed register */ luaK_reserveregs(fs, 2); /* function and 'self' produced by op_self */ luaK_codeABC(fs, OP_SELF, e->u.info, ereg, luaK_exp2RK(fs, key)); freeexp(fs, key); } -static void invertjump (FuncState *fs, expdesc *e) { +/* +** Negate condition 'e' (where 'e' is a comparison). +*/ +static void negatecondition (FuncState *fs, expdesc *e) { Instruction *pc = getjumpcontrol(fs, e->u.info); lua_assert(testTMode(GET_OPCODE(*pc)) && GET_OPCODE(*pc) != OP_TESTSET && GET_OPCODE(*pc) != OP_TEST); @@ -637,9 +830,15 @@ static void invertjump (FuncState *fs, expdesc *e) { } +/* +** Emit instruction to jump if 'e' is 'cond' (that is, if 'cond' +** is true, code will jump if 'e' is true.) Return jump position. +** Optimize when 'e' is 'not' something, inverting the condition +** and removing the 'not'. +*/ static int jumponcond (FuncState *fs, expdesc *e, int cond) { if (e->k == VRELOCABLE) { - Instruction ie = getcode(fs, e); + Instruction ie = getinstruction(fs, e); if (GET_OPCODE(ie) == OP_NOT) { fs->pc--; /* remove previous OP_NOT */ return condjump(fs, OP_TEST, GETARG_B(ie), 0, !cond); @@ -652,13 +851,16 @@ static int jumponcond (FuncState *fs, expdesc *e, int cond) { } +/* +** Emit code to go through if 'e' is true, jump otherwise. +*/ void luaK_goiftrue (FuncState *fs, expdesc *e) { - int pc; /* pc of last jump */ + int pc; /* pc of new jump */ luaK_dischargevars(fs, e); switch (e->k) { - case VJMP: { - invertjump(fs, e); - pc = e->u.info; + case VJMP: { /* condition? */ + negatecondition(fs, e); /* jump when it is false */ + pc = e->u.info; /* save jump position */ break; } case VK: case VKFLT: case VKINT: case VTRUE: { @@ -666,22 +868,25 @@ void luaK_goiftrue (FuncState *fs, expdesc *e) { break; } default: { - pc = jumponcond(fs, e, 0); + pc = jumponcond(fs, e, 0); /* jump when false */ break; } } - luaK_concat(fs, &e->f, pc); /* insert last jump in 'f' list */ - luaK_patchtohere(fs, e->t); + luaK_concat(fs, &e->f, pc); /* insert new jump in false list */ + luaK_patchtohere(fs, e->t); /* true list jumps to here (to go through) */ e->t = NO_JUMP; } +/* +** Emit code to go through if 'e' is false, jump otherwise. +*/ void luaK_goiffalse (FuncState *fs, expdesc *e) { - int pc; /* pc of last jump */ + int pc; /* pc of new jump */ luaK_dischargevars(fs, e); switch (e->k) { case VJMP: { - pc = e->u.info; + pc = e->u.info; /* already jump if true */ break; } case VNIL: case VFALSE: { @@ -689,29 +894,32 @@ void luaK_goiffalse (FuncState *fs, expdesc *e) { break; } default: { - pc = jumponcond(fs, e, 1); + pc = jumponcond(fs, e, 1); /* jump if true */ break; } } - luaK_concat(fs, &e->t, pc); /* insert last jump in 't' list */ - luaK_patchtohere(fs, e->f); + luaK_concat(fs, &e->t, pc); /* insert new jump in 't' list */ + luaK_patchtohere(fs, e->f); /* false list jumps to here (to go through) */ e->f = NO_JUMP; } +/* +** Code 'not e', doing constant folding. +*/ static void codenot (FuncState *fs, expdesc *e) { luaK_dischargevars(fs, e); switch (e->k) { case VNIL: case VFALSE: { - e->k = VTRUE; + e->k = VTRUE; /* true == not nil == not false */ break; } case VK: case VKFLT: case VKINT: case VTRUE: { - e->k = VFALSE; + e->k = VFALSE; /* false == not "x" == not 0.5 == not 1 == not true */ break; } case VJMP: { - invertjump(fs, e); + negatecondition(fs, e); break; } case VRELOCABLE: @@ -722,30 +930,32 @@ static void codenot (FuncState *fs, expdesc *e) { e->k = VRELOCABLE; break; } - default: { - lua_assert(0); /* cannot happen */ - break; - } + default: lua_assert(0); /* cannot happen */ } /* interchange true and false lists */ { int temp = e->f; e->f = e->t; e->t = temp; } - removevalues(fs, e->f); + removevalues(fs, e->f); /* values are useless when negated */ removevalues(fs, e->t); } +/* +** Create expression 't[k]'. 't' must have its final result already in a +** register or upvalue. +*/ void luaK_indexed (FuncState *fs, expdesc *t, expdesc *k) { - lua_assert(!hasjumps(t)); - t->u.ind.t = t->u.info; - t->u.ind.idx = luaK_exp2RK(fs, k); - t->u.ind.vt = (t->k == VUPVAL) ? VUPVAL - : check_exp(vkisinreg(t->k), VLOCAL); + lua_assert(!hasjumps(t) && (vkisinreg(t->k) || t->k == VUPVAL)); + t->u.ind.t = t->u.info; /* register or upvalue index */ + t->u.ind.idx = luaK_exp2RK(fs, k); /* R/K index for key */ + t->u.ind.vt = (t->k == VUPVAL) ? VUPVAL : VLOCAL; t->k = VINDEXED; } /* -** return false if folding can raise an error +** Return false if folding can raise an error. +** Bitwise operations need operands convertible to integers; division +** operations cannot have 0 as divisor. */ static int validop (int op, TValue *v1, TValue *v2) { switch (op) { @@ -762,7 +972,8 @@ static int validop (int op, TValue *v1, TValue *v2) { /* -** Try to "constant-fold" an operation; return 1 iff successful +** Try to "constant-fold" an operation; return 1 iff successful. +** (In this case, 'e1' has the final result.) */ static int constfolding (FuncState *fs, int op, expdesc *e1, expdesc *e2) { TValue v1, v2, res; @@ -773,7 +984,7 @@ static int constfolding (FuncState *fs, int op, expdesc *e1, expdesc *e2) { e1->k = VKINT; e1->u.ival = ivalue(&res); } - else { /* folds neither NaN nor 0.0 (to avoid collapsing with -0.0) */ + else { /* folds neither NaN nor 0.0 (to avoid problems with -0.0) */ lua_Number n = fltvalue(&res); if (luai_numisnan(n) || n == 0) return 0; @@ -785,81 +996,97 @@ static int constfolding (FuncState *fs, int op, expdesc *e1, expdesc *e2) { /* -** Code for binary and unary expressions that "produce values" -** (arithmetic operations, bitwise operations, concat, length). First -** try to do constant folding (only for numeric [arithmetic and -** bitwise] operations, which is what 'lua_arith' accepts). -** Expression to produce final result will be encoded in 'e1'. +** Emit code for unary expressions that "produce values" +** (everything but 'not'). +** Expression to produce final result will be encoded in 'e'. */ -static void codeexpval (FuncState *fs, OpCode op, - expdesc *e1, expdesc *e2, int line) { - lua_assert(op >= OP_ADD); - if (op <= OP_BNOT && constfolding(fs, (op - OP_ADD) + LUA_OPADD, e1, e2)) - return; /* result has been folded */ - else { - int o1, o2; - /* move operands to registers (if needed) */ - if (op == OP_UNM || op == OP_BNOT || op == OP_LEN) { /* unary op? */ - o2 = 0; /* no second expression */ - o1 = luaK_exp2anyreg(fs, e1); /* cannot operate on constants */ - } - else { /* regular case (binary operators) */ - o2 = luaK_exp2RK(fs, e2); /* both operands are "RK" */ - o1 = luaK_exp2RK(fs, e1); - } - if (o1 > o2) { /* free registers in proper order */ - freeexp(fs, e1); - freeexp(fs, e2); - } - else { - freeexp(fs, e2); - freeexp(fs, e1); - } - e1->u.info = luaK_codeABC(fs, op, 0, o1, o2); /* generate opcode */ - e1->k = VRELOCABLE; /* all those operations are relocatable */ - luaK_fixline(fs, line); - } +static void codeunexpval (FuncState *fs, OpCode op, expdesc *e, int line) { + int r = luaK_exp2anyreg(fs, e); /* opcodes operate only on registers */ + freeexp(fs, e); + e->u.info = luaK_codeABC(fs, op, 0, r, 0); /* generate opcode */ + e->k = VRELOCABLE; /* all those operations are relocatable */ + luaK_fixline(fs, line); } -static void codecomp (FuncState *fs, OpCode op, int cond, expdesc *e1, - expdesc *e2) { - int o1 = luaK_exp2RK(fs, e1); - int o2 = luaK_exp2RK(fs, e2); - freeexp(fs, e2); - freeexp(fs, e1); - if (cond == 0 && op != OP_EQ) { - int temp; /* exchange args to replace by '<' or '<=' */ - temp = o1; o1 = o2; o2 = temp; /* o1 <==> o2 */ - cond = 1; +/* +** Emit code for binary expressions that "produce values" +** (everything but logical operators 'and'/'or' and comparison +** operators). +** Expression to produce final result will be encoded in 'e1'. +*/ +static void codebinexpval (FuncState *fs, OpCode op, + expdesc *e1, expdesc *e2, int line) { + int rk1 = luaK_exp2RK(fs, e1); /* both operands are "RK" */ + int rk2 = luaK_exp2RK(fs, e2); + freeexps(fs, e1, e2); + e1->u.info = luaK_codeABC(fs, op, 0, rk1, rk2); /* generate opcode */ + e1->k = VRELOCABLE; /* all those operations are relocatable */ + luaK_fixline(fs, line); +} + + +/* +** Emit code for comparisons. +** 'e1' was already put in R/K form by 'luaK_infix'. +*/ +static void codecomp (FuncState *fs, BinOpr opr, expdesc *e1, expdesc *e2) { + int rk1 = (e1->k == VK) ? RKASK(e1->u.info) + : check_exp(e1->k == VNONRELOC, e1->u.info); + int rk2 = luaK_exp2RK(fs, e2); + freeexps(fs, e1, e2); + switch (opr) { + case OPR_NE: { /* '(a ~= b)' ==> 'not (a == b)' */ + e1->u.info = condjump(fs, OP_EQ, 0, rk1, rk2); + break; + } + case OPR_GT: case OPR_GE: { + /* '(a > b)' ==> '(b < a)'; '(a >= b)' ==> '(b <= a)' */ + OpCode op = cast(OpCode, (opr - OPR_NE) + OP_EQ); + e1->u.info = condjump(fs, op, 1, rk2, rk1); /* invert operands */ + break; + } + default: { /* '==', '<', '<=' use their own opcodes */ + OpCode op = cast(OpCode, (opr - OPR_EQ) + OP_EQ); + e1->u.info = condjump(fs, op, 1, rk1, rk2); + break; + } } - e1->u.info = condjump(fs, op, cond, o1, o2); e1->k = VJMP; } +/* +** Aplly prefix operation 'op' to expression 'e'. +*/ void luaK_prefix (FuncState *fs, UnOpr op, expdesc *e, int line) { - expdesc e2; - e2.t = e2.f = NO_JUMP; e2.k = VKINT; e2.u.ival = 0; + static expdesc ef = {VKINT, {0}, NO_JUMP, NO_JUMP}; /* fake 2nd operand */ switch (op) { - case OPR_MINUS: case OPR_BNOT: case OPR_LEN: { - codeexpval(fs, cast(OpCode, (op - OPR_MINUS) + OP_UNM), e, &e2, line); + case OPR_MINUS: case OPR_BNOT: + if (constfolding(fs, op + LUA_OPUNM, e, &ef)) + break; + /* FALLTHROUGH */ + case OPR_LEN: + codeunexpval(fs, cast(OpCode, op + OP_UNM), e, line); break; - } case OPR_NOT: codenot(fs, e); break; default: lua_assert(0); } } +/* +** Process 1st operand 'v' of binary operation 'op' before reading +** 2nd operand. +*/ void luaK_infix (FuncState *fs, BinOpr op, expdesc *v) { switch (op) { case OPR_AND: { - luaK_goiftrue(fs, v); + luaK_goiftrue(fs, v); /* go ahead only if 'v' is true */ break; } case OPR_OR: { - luaK_goiffalse(fs, v); + luaK_goiffalse(fs, v); /* go ahead only if 'v' is false */ break; } case OPR_CONCAT: { @@ -871,7 +1098,9 @@ void luaK_infix (FuncState *fs, BinOpr op, expdesc *v) { case OPR_MOD: case OPR_POW: case OPR_BAND: case OPR_BOR: case OPR_BXOR: case OPR_SHL: case OPR_SHR: { - if (!tonumeral(v, NULL)) luaK_exp2RK(fs, v); + if (!tonumeral(v, NULL)) + luaK_exp2RK(fs, v); + /* else keep numeral, which may be folded with 2nd operand */ break; } default: { @@ -882,18 +1111,24 @@ void luaK_infix (FuncState *fs, BinOpr op, expdesc *v) { } +/* +** Finalize code for binary operation, after reading 2nd operand. +** For '(a .. b .. c)' (which is '(a .. (b .. c))', because +** concatenation is right associative), merge second CONCAT into first +** one. +*/ void luaK_posfix (FuncState *fs, BinOpr op, expdesc *e1, expdesc *e2, int line) { switch (op) { case OPR_AND: { - lua_assert(e1->t == NO_JUMP); /* list must be closed */ + lua_assert(e1->t == NO_JUMP); /* list closed by 'luK_infix' */ luaK_dischargevars(fs, e2); luaK_concat(fs, &e2->f, e1->f); *e1 = *e2; break; } case OPR_OR: { - lua_assert(e1->f == NO_JUMP); /* list must be closed */ + lua_assert(e1->f == NO_JUMP); /* list closed by 'luK_infix' */ luaK_dischargevars(fs, e2); luaK_concat(fs, &e2->t, e1->t); *e1 = *e2; @@ -901,15 +1136,16 @@ void luaK_posfix (FuncState *fs, BinOpr op, } case OPR_CONCAT: { luaK_exp2val(fs, e2); - if (e2->k == VRELOCABLE && GET_OPCODE(getcode(fs, e2)) == OP_CONCAT) { - lua_assert(e1->u.info == GETARG_B(getcode(fs, e2))-1); + if (e2->k == VRELOCABLE && + GET_OPCODE(getinstruction(fs, e2)) == OP_CONCAT) { + lua_assert(e1->u.info == GETARG_B(getinstruction(fs, e2))-1); freeexp(fs, e1); - SETARG_B(getcode(fs, e2), e1->u.info); + SETARG_B(getinstruction(fs, e2), e1->u.info); e1->k = VRELOCABLE; e1->u.info = e2->u.info; } else { luaK_exp2nextreg(fs, e2); /* operand must be on the 'stack' */ - codeexpval(fs, OP_CONCAT, e1, e2, line); + codebinexpval(fs, OP_CONCAT, e1, e2, line); } break; } @@ -917,15 +1153,13 @@ void luaK_posfix (FuncState *fs, BinOpr op, case OPR_IDIV: case OPR_MOD: case OPR_POW: case OPR_BAND: case OPR_BOR: case OPR_BXOR: case OPR_SHL: case OPR_SHR: { - codeexpval(fs, cast(OpCode, (op - OPR_ADD) + OP_ADD), e1, e2, line); - break; - } - case OPR_EQ: case OPR_LT: case OPR_LE: { - codecomp(fs, cast(OpCode, (op - OPR_EQ) + OP_EQ), 1, e1, e2); + if (!constfolding(fs, op + LUA_OPADD, e1, e2)) + codebinexpval(fs, cast(OpCode, op + OP_ADD), e1, e2, line); break; } + case OPR_EQ: case OPR_LT: case OPR_LE: case OPR_NE: case OPR_GT: case OPR_GE: { - codecomp(fs, cast(OpCode, (op - OPR_NE) + OP_EQ), 0, e1, e2); + codecomp(fs, op, e1, e2); break; } default: lua_assert(0); @@ -933,15 +1167,25 @@ void luaK_posfix (FuncState *fs, BinOpr op, } +/* +** Change line information associated with current position. +*/ void luaK_fixline (FuncState *fs, int line) { fs->f->lineinfo[fs->pc - 1] = line; } +/* +** Emit a SETLIST instruction. +** 'base' is register that keeps table; +** 'nelems' is #table plus those to be stored now; +** 'tostore' is number of values (in registers 'base + 1',...) to add to +** table (or LUA_MULTRET to add up to stack top). +*/ void luaK_setlist (FuncState *fs, int base, int nelems, int tostore) { int c = (nelems - 1)/LFIELDS_PER_FLUSH + 1; int b = (tostore == LUA_MULTRET) ? 0 : tostore; - lua_assert(tostore != 0); + lua_assert(tostore != 0 && tostore <= LFIELDS_PER_FLUSH); if (c <= MAXARG_C) luaK_codeABC(fs, OP_SETLIST, base, b, c); else if (c <= MAXARG_Ax) { diff --git a/lua/lcode.h b/lua/lcode.h index 43ab86d..cd306d5 100644 --- a/lua/lcode.h +++ b/lua/lcode.h @@ -1,5 +1,5 @@ /* -** $Id: lcode.h,v 1.63 2013/12/30 20:47:58 roberto Exp $ +** $Id: lcode.h,v 1.64 2016/01/05 16:22:37 roberto Exp $ ** Code generator for Lua ** See Copyright Notice in lua.h */ @@ -40,7 +40,8 @@ typedef enum BinOpr { typedef enum UnOpr { OPR_MINUS, OPR_BNOT, OPR_NOT, OPR_LEN, OPR_NOUNOPR } UnOpr; -#define getcode(fs,e) ((fs)->f->code[(e)->u.info]) +/* get (pointer to) instruction of given 'expdesc' */ +#define getinstruction(fs,e) ((fs)->f->code[(e)->u.info]) #define luaK_codeAsBx(fs,o,A,sBx) luaK_codeABx(fs,o,A,(sBx)+MAXARG_sBx) diff --git a/lua/lcorolib.c b/lua/lcorolib.c index 0c0b7fa..2303429 100644 --- a/lua/lcorolib.c +++ b/lua/lcorolib.c @@ -1,5 +1,5 @@ /* -** $Id: lcorolib.c,v 1.9 2014/11/02 19:19:04 roberto Exp $ +** $Id: lcorolib.c,v 1.10 2016/04/11 19:19:55 roberto Exp $ ** Coroutine Library ** See Copyright Notice in lua.h */ @@ -75,7 +75,7 @@ static int luaB_auxwrap (lua_State *L) { lua_State *co = lua_tothread(L, lua_upvalueindex(1)); int r = auxresume(L, co, lua_gettop(L)); if (r < 0) { - if (lua_isstring(L, -1)) { /* error object is a string? */ + if (lua_type(L, -1) == LUA_TSTRING) { /* error object is a string? */ luaL_where(L, 1); /* add extra info */ lua_insert(L, -2); lua_concat(L, 2); diff --git a/lua/ldebug.c b/lua/ldebug.c index 9bd86d0..e499ee3 100644 --- a/lua/ldebug.c +++ b/lua/ldebug.c @@ -1,5 +1,5 @@ /* -** $Id: ldebug.c,v 2.117 2015/11/02 18:48:07 roberto Exp $ +** $Id: ldebug.c,v 2.120 2016/03/31 19:01:21 roberto Exp $ ** Debug Interface ** See Copyright Notice in lua.h */ @@ -69,7 +69,13 @@ static void swapextra (lua_State *L) { /* -** this function can be called asynchronous (e.g. during a signal) +** This function can be called asynchronously (e.g. during a signal). +** Fields 'oldpc', 'basehookcount', and 'hookcount' (set by +** 'resethookcount') are for debug only, and it is no problem if they +** get arbitrary values (causes at most one wrong hook call). 'hookmask' +** is an atomic value. We assume that pointers are atomic too (e.g., gcc +** ensures that for all platforms where it runs). Moreover, 'hook' is +** always checked before being called (see 'luaD_hook'). */ LUA_API void lua_sethook (lua_State *L, lua_Hook func, int mask, int count) { if (func == NULL || mask == 0) { /* turn off hooks? */ @@ -558,7 +564,7 @@ static const char *varinfo (lua_State *L, const TValue *o) { l_noret luaG_typeerror (lua_State *L, const TValue *o, const char *op) { - const char *t = objtypename(o); + const char *t = luaT_objtypename(L, o); luaG_runerror(L, "attempt to %s a %s value%s", op, t, varinfo(L, o)); } @@ -590,9 +596,9 @@ l_noret luaG_tointerror (lua_State *L, const TValue *p1, const TValue *p2) { l_noret luaG_ordererror (lua_State *L, const TValue *p1, const TValue *p2) { - const char *t1 = objtypename(p1); - const char *t2 = objtypename(p2); - if (t1 == t2) + const char *t1 = luaT_objtypename(L, p1); + const char *t2 = luaT_objtypename(L, p2); + if (strcmp(t1, t2) == 0) luaG_runerror(L, "attempt to compare two %s values", t1); else luaG_runerror(L, "attempt to compare %s with %s", t1, t2); diff --git a/lua/ldo.c b/lua/ldo.c index 95efd56..8804c99 100644 --- a/lua/ldo.c +++ b/lua/ldo.c @@ -1,5 +1,5 @@ /* -** $Id: ldo.c,v 2.150 2015/11/19 19:16:22 roberto Exp $ +** $Id: ldo.c,v 2.151 2015/12/16 16:40:07 roberto Exp $ ** Stack and Call structure of Lua ** See Copyright Notice in lua.h */ @@ -242,9 +242,14 @@ void luaD_inctop (lua_State *L) { /* }================================================================== */ +/* +** Call a hook for the given event. Make sure there is a hook to be +** called. (Both 'L->hook' and 'L->hookmask', which triggers this +** function, can be changed asynchronously by signals.) +*/ void luaD_hook (lua_State *L, int event, int line) { lua_Hook hook = L->hook; - if (hook && L->allowhook) { + if (hook && L->allowhook) { /* make sure there is a hook */ CallInfo *ci = L->ci; ptrdiff_t top = savestack(L, L->top); ptrdiff_t ci_top = savestack(L, ci->top); diff --git a/lua/ldo.h b/lua/ldo.h index 80582dc..4f5d51c 100644 --- a/lua/ldo.h +++ b/lua/ldo.h @@ -1,5 +1,5 @@ /* -** $Id: ldo.h,v 2.28 2015/11/23 11:29:43 roberto Exp $ +** $Id: ldo.h,v 2.29 2015/12/21 13:02:14 roberto Exp $ ** Stack and Call structure of Lua ** See Copyright Notice in lua.h */ @@ -25,7 +25,7 @@ { pre; luaD_growstack(L, n); pos; } else { condmovestack(L,pre,pos); } /* In general, 'pre'/'pos' are empty (nothing to save) */ -#define luaD_checkstack(L,n) luaD_checkstackaux(L,n,,) +#define luaD_checkstack(L,n) luaD_checkstackaux(L,n,(void)0,(void)0) diff --git a/lua/lgc.c b/lua/lgc.c index 49d8ecb..7c29fb0 100644 --- a/lua/lgc.c +++ b/lua/lgc.c @@ -1,5 +1,5 @@ /* -** $Id: lgc.c,v 2.210 2015/11/03 18:10:44 roberto Exp $ +** $Id: lgc.c,v 2.212 2016/03/31 19:02:03 roberto Exp $ ** Garbage Collector ** See Copyright Notice in lua.h */ @@ -754,14 +754,11 @@ static GCObject **sweeplist (lua_State *L, GCObject **p, lu_mem count) { /* ** sweep a list until a live object (or end of list) */ -static GCObject **sweeptolive (lua_State *L, GCObject **p, int *n) { +static GCObject **sweeptolive (lua_State *L, GCObject **p) { GCObject **old = p; - int i = 0; do { - i++; p = sweeplist(L, p, 1); } while (p == old); - if (n) *n += i; return p; } @@ -856,10 +853,10 @@ static int runafewfinalizers (lua_State *L) { /* ** call all pending finalizers */ -static void callallpendingfinalizers (lua_State *L, int propagateerrors) { +static void callallpendingfinalizers (lua_State *L) { global_State *g = G(L); while (g->tobefnz) - GCTM(L, propagateerrors); + GCTM(L, 0); } @@ -909,7 +906,7 @@ void luaC_checkfinalizer (lua_State *L, GCObject *o, Table *mt) { if (issweepphase(g)) { makewhite(g, o); /* "sweep" object 'o' */ if (g->sweepgc == &o->next) /* should not remove 'sweepgc' object */ - g->sweepgc = sweeptolive(L, g->sweepgc, NULL); /* change 'sweepgc' */ + g->sweepgc = sweeptolive(L, g->sweepgc); /* change 'sweepgc' */ } /* search for pointer pointing to 'o' */ for (p = &g->allgc; *p != o; p = &(*p)->next) { /* empty */ } @@ -951,19 +948,16 @@ static void setpause (global_State *g) { /* ** Enter first sweep phase. -** The call to 'sweeptolive' makes pointer point to an object inside -** the list (instead of to the header), so that the real sweep do not -** need to skip objects created between "now" and the start of the real -** sweep. -** Returns how many objects it swept. +** The call to 'sweeplist' tries to make pointer point to an object +** inside the list (instead of to the header), so that the real sweep do +** not need to skip objects created between "now" and the start of the +** real sweep. */ -static int entersweep (lua_State *L) { +static void entersweep (lua_State *L) { global_State *g = G(L); - int n = 0; g->gcstate = GCSswpallgc; lua_assert(g->sweepgc == NULL); - g->sweepgc = sweeptolive(L, &g->allgc, &n); - return n; + g->sweepgc = sweeplist(L, &g->allgc, 1); } @@ -971,7 +965,7 @@ void luaC_freeallobjects (lua_State *L) { global_State *g = G(L); separatetobefnz(g, 1); /* separate all objects with finalizers */ lua_assert(g->finobj == NULL); - callallpendingfinalizers(L, 0); + callallpendingfinalizers(L); lua_assert(g->tobefnz == NULL); g->currentwhite = WHITEBITS; /* this "white" makes all objects look dead */ g->gckind = KGC_NORMAL; @@ -1064,12 +1058,11 @@ static lu_mem singlestep (lua_State *L) { } case GCSatomic: { lu_mem work; - int sw; propagateall(g); /* make sure gray list is empty */ work = atomic(L); /* work is what was traversed by 'atomic' */ - sw = entersweep(L); + entersweep(L); g->GCestimate = gettotalbytes(g); /* first estimate */; - return work + sw * GCSWEEPCOST; + return work; } case GCSswpallgc: { /* sweep "regular" objects */ return sweepstep(L, g, GCSswpfinobj, &g->finobj); diff --git a/lua/lgc.h b/lua/lgc.h index 1775ca4..aed3e18 100644 --- a/lua/lgc.h +++ b/lua/lgc.h @@ -1,5 +1,5 @@ /* -** $Id: lgc.h,v 2.90 2015/10/21 18:15:15 roberto Exp $ +** $Id: lgc.h,v 2.91 2015/12/21 13:02:14 roberto Exp $ ** Garbage Collector ** See Copyright Notice in lua.h */ @@ -112,7 +112,7 @@ condchangemem(L,pre,pos); } /* more often than not, 'pre'/'pos' are empty */ -#define luaC_checkGC(L) luaC_condGC(L,,) +#define luaC_checkGC(L) luaC_condGC(L,(void)0,(void)0) #define luaC_barrier(L,p,v) ( \ diff --git a/lua/liolib.c b/lua/liolib.c index a91ba39..aa78e59 100644 --- a/lua/liolib.c +++ b/lua/liolib.c @@ -1,5 +1,5 @@ /* -** $Id: liolib.c,v 2.148 2015/11/23 11:36:11 roberto Exp $ +** $Id: liolib.c,v 2.149 2016/05/02 14:03:19 roberto Exp $ ** Standard I/O (and system) library ** See Copyright Notice in lua.h */ @@ -375,14 +375,17 @@ static int io_lines (lua_State *L) { /* maximum length of a numeral */ -#define MAXRN 200 +#if !defined (L_MAXLENNUM) +#define L_MAXLENNUM 200 +#endif + /* auxiliary structure used by 'read_number' */ typedef struct { FILE *f; /* file being read */ int c; /* current character (look ahead) */ int n; /* number of elements in buffer 'buff' */ - char buff[MAXRN + 1]; /* +1 for ending '\0' */ + char buff[L_MAXLENNUM + 1]; /* +1 for ending '\0' */ } RN; @@ -390,7 +393,7 @@ typedef struct { ** Add current char to buffer (if not out of space) and read next one */ static int nextc (RN *rn) { - if (rn->n >= MAXRN) { /* buffer overflow? */ + if (rn->n >= L_MAXLENNUM) { /* buffer overflow? */ rn->buff[0] = '\0'; /* invalidate result */ return 0; /* fail */ } @@ -403,10 +406,10 @@ static int nextc (RN *rn) { /* -** Accept current char if it is in 'set' (of size 1 or 2) +** Accept current char if it is in 'set' (of size 2) */ static int test2 (RN *rn, const char *set) { - if (rn->c == set[0] || (rn->c == set[1] && rn->c != '\0')) + if (rn->c == set[0] || rn->c == set[1]) return nextc(rn); else return 0; } @@ -435,11 +438,11 @@ static int read_number (lua_State *L, FILE *f) { char decp[2]; rn.f = f; rn.n = 0; decp[0] = lua_getlocaledecpoint(); /* get decimal point from locale */ - decp[1] = '\0'; + decp[1] = '.'; /* always accept a dot */ l_lockfile(rn.f); do { rn.c = l_getc(rn.f); } while (isspace(rn.c)); /* skip spaces */ test2(&rn, "-+"); /* optional signal */ - if (test2(&rn, "0")) { + if (test2(&rn, "00")) { if (test2(&rn, "xX")) hex = 1; /* numeral is hexadecimal */ else count = 1; /* count initial '0' as a valid digit */ } diff --git a/lua/llex.c b/lua/llex.c index 16ea3eb..7032827 100644 --- a/lua/llex.c +++ b/lua/llex.c @@ -1,5 +1,5 @@ /* -** $Id: llex.c,v 2.95 2015/11/19 19:16:22 roberto Exp $ +** $Id: llex.c,v 2.96 2016/05/02 14:02:12 roberto Exp $ ** Lexical Analyzer ** See Copyright Notice in lua.h */ @@ -162,7 +162,6 @@ static void inclinenumber (LexState *ls) { void luaX_setinput (lua_State *L, LexState *ls, ZIO *z, TString *source, int firstchar) { ls->t.token = 0; - ls->decpoint = '.'; ls->L = L; ls->current = firstchar; ls->lookahead.token = TK_EOS; /* no look-ahead token */ @@ -207,35 +206,6 @@ static int check_next2 (LexState *ls, const char *set) { } -/* -** change all characters 'from' in buffer to 'to' -*/ -static void buffreplace (LexState *ls, char from, char to) { - if (from != to) { - size_t n = luaZ_bufflen(ls->buff); - char *p = luaZ_buffer(ls->buff); - while (n--) - if (p[n] == from) p[n] = to; - } -} - - -/* -** in case of format error, try to change decimal point separator to -** the one defined in the current locale and check again -*/ -static void trydecpoint (LexState *ls, TValue *o) { - char old = ls->decpoint; - ls->decpoint = lua_getlocaledecpoint(); - buffreplace(ls, old, ls->decpoint); /* try new decimal separator */ - if (luaO_str2num(luaZ_buffer(ls->buff), o) == 0) { - /* format error with correct decimal point: no more options */ - buffreplace(ls, ls->decpoint, '.'); /* undo change (for error message) */ - lexerror(ls, "malformed number", TK_FLT); - } -} - - /* LUA_NUMBER */ /* ** this function is quite liberal in what it accepts, as 'luaO_str2num' @@ -259,9 +229,8 @@ static int read_numeral (LexState *ls, SemInfo *seminfo) { else break; } save(ls, '\0'); - buffreplace(ls, '.', ls->decpoint); /* follow locale for decimal point */ if (luaO_str2num(luaZ_buffer(ls->buff), &obj) == 0) /* format error? */ - trydecpoint(ls, &obj); /* try to update decimal point separator */ + lexerror(ls, "malformed number", TK_FLT); if (ttisinteger(&obj)) { seminfo->i = ivalue(&obj); return TK_INT; diff --git a/lua/llex.h b/lua/llex.h index afb40b5..2363d87 100644 --- a/lua/llex.h +++ b/lua/llex.h @@ -1,5 +1,5 @@ /* -** $Id: llex.h,v 1.78 2014/10/29 15:38:24 roberto Exp $ +** $Id: llex.h,v 1.79 2016/05/02 14:02:12 roberto Exp $ ** Lexical Analyzer ** See Copyright Notice in lua.h */ @@ -69,7 +69,6 @@ typedef struct LexState { struct Dyndata *dyd; /* dynamic structures used by the parser */ TString *source; /* current source name */ TString *envn; /* environment variable name */ - char decpoint; /* locale decimal point */ } LexState; diff --git a/lua/lobject.c b/lua/lobject.c index e24723f..a44b385 100644 --- a/lua/lobject.c +++ b/lua/lobject.c @@ -1,5 +1,5 @@ /* -** $Id: lobject.c,v 2.108 2015/11/02 16:09:30 roberto Exp $ +** $Id: lobject.c,v 2.111 2016/05/20 14:07:48 roberto Exp $ ** Some generic functions over Lua objects ** See Copyright Notice in lua.h */ @@ -243,20 +243,59 @@ static lua_Number lua_strx2number (const char *s, char **endptr) { /* }====================================================== */ -static const char *l_str2d (const char *s, lua_Number *result) { +/* maximum length of a numeral */ +#if !defined (L_MAXLENNUM) +#define L_MAXLENNUM 200 +#endif + +static const char *l_str2dloc (const char *s, lua_Number *result, int mode) { char *endptr; - if (strpbrk(s, "nN")) /* reject 'inf' and 'nan' */ - return NULL; - else if (strpbrk(s, "xX")) /* hex? */ - *result = lua_strx2number(s, &endptr); - else - *result = lua_str2number(s, &endptr); - if (endptr == s) return NULL; /* nothing recognized */ - while (lisspace(cast_uchar(*endptr))) endptr++; - return (*endptr == '\0' ? endptr : NULL); /* OK if no trailing characters */ + *result = (mode == 'x') ? lua_strx2number(s, &endptr) /* try to convert */ + : lua_str2number(s, &endptr); + if (endptr == s) return NULL; /* nothing recognized? */ + while (lisspace(cast_uchar(*endptr))) endptr++; /* skip trailing spaces */ + return (*endptr == '\0') ? endptr : NULL; /* OK if no trailing characters */ } +/* +** Convert string 's' to a Lua number (put in 'result'). Return NULL +** on fail or the address of the ending '\0' on success. +** 'pmode' points to (and 'mode' contains) special things in the string: +** - 'x'/'X' means an hexadecimal numeral +** - 'n'/'N' means 'inf' or 'nan' (which should be rejected) +** - '.' just optimizes the search for the common case (nothing special) +** This function accepts both the current locale or a dot as the radix +** mark. If the convertion fails, it may mean number has a dot but +** locale accepts something else. In that case, the code copies 's' +** to a buffer (because 's' is read-only), changes the dot to the +** current locale radix mark, and tries to convert again. +*/ +static const char *l_str2d (const char *s, lua_Number *result) { + const char *endptr; + const char *pmode = strpbrk(s, ".xXnN"); + int mode = pmode ? ltolower(cast_uchar(*pmode)) : 0; + if (mode == 'n') /* reject 'inf' and 'nan' */ + return NULL; + endptr = l_str2dloc(s, result, mode); /* try to convert */ + if (endptr == NULL) { /* failed? may be a different locale */ + char buff[L_MAXLENNUM + 1]; + char *pdot = strchr(s, '.'); + if (strlen(s) > L_MAXLENNUM || pdot == NULL) + return NULL; /* string too long or no dot; fail */ + strcpy(buff, s); /* copy string to buffer */ + buff[pdot - s] = lua_getlocaledecpoint(); /* correct decimal point */ + endptr = l_str2dloc(buff, result, mode); /* try again */ + if (endptr != NULL) + endptr = s + (endptr - buff); /* make relative to 's' */ + } + return endptr; +} + + +#define MAXBY10 cast(lua_Unsigned, LUA_MAXINTEGER / 10) +#define MAXLASTD cast_int(LUA_MAXINTEGER % 10) + static const char *l_str2int (const char *s, lua_Integer *result) { lua_Unsigned a = 0; int empty = 1; @@ -273,7 +312,10 @@ static const char *l_str2int (const char *s, lua_Integer *result) { } else { /* decimal */ for (; lisdigit(cast_uchar(*s)); s++) { - a = a * 10 + *s - '0'; + int d = *s - '0'; + if (a >= MAXBY10 && (a > MAXBY10 || d > MAXLASTD + neg)) /* overflow? */ + return NULL; /* do not accept it (as integer) */ + a = a * 10 + d; empty = 0; } } @@ -351,8 +393,10 @@ static void pushstr (lua_State *L, const char *str, size_t l) { } -/* this function handles only '%d', '%c', '%f', '%p', and '%s' - conventional formats, plus Lua-specific '%I' and '%U' */ +/* +** this function handles only '%d', '%c', '%f', '%p', and '%s' + conventional formats, plus Lua-specific '%I' and '%U' +*/ const char *luaO_pushvfstring (lua_State *L, const char *fmt, va_list argp) { int n = 0; for (;;) { @@ -360,13 +404,13 @@ const char *luaO_pushvfstring (lua_State *L, const char *fmt, va_list argp) { if (e == NULL) break; pushstr(L, fmt, e - fmt); switch (*(e+1)) { - case 's': { + case 's': { /* zero-terminated string */ const char *s = va_arg(argp, char *); if (s == NULL) s = "(null)"; pushstr(L, s, strlen(s)); break; } - case 'c': { + case 'c': { /* an 'int' as a character */ char buff = cast(char, va_arg(argp, int)); if (lisprint(cast_uchar(buff))) pushstr(L, &buff, 1); @@ -374,28 +418,28 @@ const char *luaO_pushvfstring (lua_State *L, const char *fmt, va_list argp) { luaO_pushfstring(L, "<\\%d>", cast_uchar(buff)); break; } - case 'd': { + case 'd': { /* an 'int' */ setivalue(L->top, va_arg(argp, int)); goto top2str; } - case 'I': { + case 'I': { /* a 'lua_Integer' */ setivalue(L->top, cast(lua_Integer, va_arg(argp, l_uacInt))); goto top2str; } - case 'f': { + case 'f': { /* a 'lua_Number' */ setfltvalue(L->top, cast_num(va_arg(argp, l_uacNumber))); - top2str: + top2str: /* convert the top element to a string */ luaD_inctop(L); luaO_tostring(L, L->top - 1); break; } - case 'p': { + case 'p': { /* a pointer */ char buff[4*sizeof(void *) + 8]; /* should be enough space for a '%p' */ int l = l_sprintf(buff, sizeof(buff), "%p", va_arg(argp, void *)); pushstr(L, buff, l); break; } - case 'U': { + case 'U': { /* an 'int' as a UTF-8 sequence */ char buff[UTF8BUFFSZ]; int l = luaO_utf8esc(buff, cast(long, va_arg(argp, long))); pushstr(L, buff + UTF8BUFFSZ - l, l); diff --git a/lua/loslib.c b/lua/loslib.c index 7dae533..4810655 100644 --- a/lua/loslib.c +++ b/lua/loslib.c @@ -1,5 +1,5 @@ /* -** $Id: loslib.c,v 1.60 2015/11/19 19:16:22 roberto Exp $ +** $Id: loslib.c,v 1.64 2016/04/18 13:06:55 roberto Exp $ ** Standard Operating System library ** See Copyright Notice in lua.h */ @@ -24,18 +24,29 @@ /* ** {================================================================== -** list of valid conversion specifiers for the 'strftime' function +** List of valid conversion specifiers for the 'strftime' function; +** options are grouped by length; group of length 2 start with '||'. ** =================================================================== */ #if !defined(LUA_STRFTIMEOPTIONS) /* { */ -#if defined(LUA_USE_C89) -#define LUA_STRFTIMEOPTIONS { "aAbBcdHIjmMpSUwWxXyYz%", "" } +/* options for ANSI C 89 */ +#define L_STRFTIMEC89 "aAbBcdHIjmMpSUwWxXyYZ%" + +/* options for ISO C 99 and POSIX */ +#define L_STRFTIMEC99 "aAbBcCdDeFgGhHIjmMnprRStTuUVwWxXyYzZ%" \ + "||" "EcECExEXEyEY" "OdOeOHOIOmOMOSOuOUOVOwOWOy" + +/* options for Windows */ +#define L_STRFTIMEWIN "aAbBcdHIjmMpSUwWxXyYzZ%" \ + "||" "#c#x#d#H#I#j#m#M#S#U#w#W#y#Y" + +#if defined(LUA_USE_WINDOWS) +#define LUA_STRFTIMEOPTIONS L_STRFTIMEWIN +#elif defined(LUA_USE_C89) +#define LUA_STRFTIMEOPTIONS L_STRFTIMEC89 #else /* C99 specification */ -#define LUA_STRFTIMEOPTIONS \ - { "aAbBcCdDeFgGhHIjmMnprRStTuUVwWxXyYzZ%", "", \ - "E", "cCxXyY", \ - "O", "deHImMSuUVwWy" } +#define LUA_STRFTIMEOPTIONS L_STRFTIMEC99 #endif #endif /* } */ @@ -195,6 +206,23 @@ static void setboolfield (lua_State *L, const char *key, int value) { lua_setfield(L, -2, key); } + +/* +** Set all fields from structure 'tm' in the table on top of the stack +*/ +static void setallfields (lua_State *L, struct tm *stm) { + setfield(L, "sec", stm->tm_sec); + setfield(L, "min", stm->tm_min); + setfield(L, "hour", stm->tm_hour); + setfield(L, "day", stm->tm_mday); + setfield(L, "month", stm->tm_mon + 1); + setfield(L, "year", stm->tm_year + 1900); + setfield(L, "wday", stm->tm_wday + 1); + setfield(L, "yday", stm->tm_yday + 1); + setboolfield(L, "isdst", stm->tm_isdst); +} + + static int getboolfield (lua_State *L, const char *key) { int res; res = (lua_getfield(L, -1, key) == LUA_TNIL) ? -1 : lua_toboolean(L, -1); @@ -210,18 +238,18 @@ static int getboolfield (lua_State *L, const char *key) { static int getfield (lua_State *L, const char *key, int d, int delta) { int isnum; - int t = lua_getfield(L, -1, key); + int t = lua_getfield(L, -1, key); /* get field and its type */ lua_Integer res = lua_tointegerx(L, -1, &isnum); - if (!isnum) { /* field is not a number? */ + if (!isnum) { /* field is not an integer? */ if (t != LUA_TNIL) /* some other value? */ - return luaL_error(L, "field '%s' not an integer", key); + return luaL_error(L, "field '%s' is not an integer", key); else if (d < 0) /* absent field; no default? */ return luaL_error(L, "field '%s' missing in date table", key); res = d; } else { if (!(-L_MAXDATEFIELD <= res && res <= L_MAXDATEFIELD)) - return luaL_error(L, "field '%s' out-of-bounds", key); + return luaL_error(L, "field '%s' is out-of-bound", key); res -= delta; } lua_pop(L, 1); @@ -230,21 +258,15 @@ static int getfield (lua_State *L, const char *key, int d, int delta) { static const char *checkoption (lua_State *L, const char *conv, char *buff) { - static const char *const options[] = LUA_STRFTIMEOPTIONS; - unsigned int i; - for (i = 0; i < sizeof(options)/sizeof(options[0]); i += 2) { - if (*conv != '\0' && strchr(options[i], *conv) != NULL) { - buff[1] = *conv; - if (*options[i + 1] == '\0') { /* one-char conversion specifier? */ - buff[2] = '\0'; /* end buffer */ - return conv + 1; - } - else if (*(conv + 1) != '\0' && - strchr(options[i + 1], *(conv + 1)) != NULL) { - buff[2] = *(conv + 1); /* valid two-char conversion specifier */ - buff[3] = '\0'; /* end buffer */ - return conv + 2; - } + const char *option; + int oplen = 1; + for (option = LUA_STRFTIMEOPTIONS; *option != '\0'; option += oplen) { + if (*option == '|') /* next block? */ + oplen++; /* next length */ + else if (memcmp(conv, option, oplen) == 0) { /* match? */ + memcpy(buff, conv, oplen); /* copy valid option to buffer */ + buff[oplen] = '\0'; + return conv + oplen; /* return next item */ } } luaL_argerror(L, 1, @@ -271,18 +293,10 @@ static int os_date (lua_State *L) { luaL_error(L, "time result cannot be represented in this installation"); if (strcmp(s, "*t") == 0) { lua_createtable(L, 0, 9); /* 9 = number of fields */ - setfield(L, "sec", stm->tm_sec); - setfield(L, "min", stm->tm_min); - setfield(L, "hour", stm->tm_hour); - setfield(L, "day", stm->tm_mday); - setfield(L, "month", stm->tm_mon+1); - setfield(L, "year", stm->tm_year+1900); - setfield(L, "wday", stm->tm_wday+1); - setfield(L, "yday", stm->tm_yday+1); - setboolfield(L, "isdst", stm->tm_isdst); + setallfields(L, stm); } else { - char cc[4]; + char cc[4]; /* buffer for individual conversion specifiers */ luaL_Buffer b; cc[0] = '%'; luaL_buffinit(L, &b); @@ -292,7 +306,7 @@ static int os_date (lua_State *L) { else { size_t reslen; char *buff = luaL_prepbuffsize(&b, SIZETIMEFMT); - s = checkoption(L, s + 1, cc); + s = checkoption(L, s + 1, cc + 1); /* copy specifier to 'cc' */ reslen = strftime(buff, SIZETIMEFMT, cc, stm); luaL_addsize(&b, reslen); } @@ -319,6 +333,7 @@ static int os_time (lua_State *L) { ts.tm_year = getfield(L, "year", -1, 1900); ts.tm_isdst = getboolfield(L, "isdst"); t = mktime(&ts); + setallfields(L, &ts); /* update fields with normalized values */ } if (t != (time_t)(l_timet)t || t == (time_t)(-1)) luaL_error(L, "time result cannot be represented in this installation"); diff --git a/lua/lparser.c b/lua/lparser.c index 282a6b1..22530a5 100644 --- a/lua/lparser.c +++ b/lua/lparser.c @@ -1,5 +1,5 @@ /* -** $Id: lparser.c,v 2.149 2015/11/02 16:09:30 roberto Exp $ +** $Id: lparser.c,v 2.153 2016/05/13 19:10:16 roberto Exp $ ** Lua Parser ** See Copyright Notice in lua.h */ @@ -164,7 +164,8 @@ static int registerlocalvar (LexState *ls, TString *varname) { int oldsize = f->sizelocvars; luaM_growvector(ls->L, f->locvars, fs->nlocvars, f->sizelocvars, LocVar, SHRT_MAX, "local variables"); - while (oldsize < f->sizelocvars) f->locvars[oldsize++].varname = NULL; + while (oldsize < f->sizelocvars) + f->locvars[oldsize++].varname = NULL; f->locvars[fs->nlocvars].varname = varname; luaC_objbarrier(ls->L, f, varname); return fs->nlocvars++; @@ -230,7 +231,8 @@ static int newupvalue (FuncState *fs, TString *name, expdesc *v) { checklimit(fs, fs->nups + 1, MAXUPVAL, "upvalues"); luaM_growvector(fs->ls->L, f->upvalues, fs->nups, f->sizeupvalues, Upvaldesc, MAXUPVAL, "upvalues"); - while (oldsize < f->sizeupvalues) f->upvalues[oldsize++].name = NULL; + while (oldsize < f->sizeupvalues) + f->upvalues[oldsize++].name = NULL; f->upvalues[fs->nups].instack = (v->k == VLOCAL); f->upvalues[fs->nups].idx = cast_byte(v->u.info); f->upvalues[fs->nups].name = name; @@ -255,7 +257,8 @@ static int searchvar (FuncState *fs, TString *n) { */ static void markupval (FuncState *fs, int level) { BlockCnt *bl = fs->bl; - while (bl->nactvar > level) bl = bl->previous; + while (bl->nactvar > level) + bl = bl->previous; bl->upval = 1; } @@ -264,27 +267,26 @@ static void markupval (FuncState *fs, int level) { Find variable with given name 'n'. If it is an upvalue, add this upvalue into all intermediate functions. */ -static int singlevaraux (FuncState *fs, TString *n, expdesc *var, int base) { +static void singlevaraux (FuncState *fs, TString *n, expdesc *var, int base) { if (fs == NULL) /* no more levels? */ - return VVOID; /* default is global */ + init_exp(var, VVOID, 0); /* default is global */ else { int v = searchvar(fs, n); /* look up locals at current level */ if (v >= 0) { /* found? */ init_exp(var, VLOCAL, v); /* variable is local */ if (!base) markupval(fs, v); /* local will be used as an upval */ - return VLOCAL; } else { /* not found as local at current level; try upvalues */ int idx = searchupvalue(fs, n); /* try existing upvalues */ if (idx < 0) { /* not found? */ - if (singlevaraux(fs->prev, n, var, 0) == VVOID) /* try upper levels */ - return VVOID; /* not found; is a global */ + singlevaraux(fs->prev, n, var, 0); /* try upper levels */ + if (var->k == VVOID) /* not found? */ + return; /* it is a global */ /* else was LOCAL or UPVAL */ idx = newupvalue(fs, n, var); /* will be a new upvalue */ } - init_exp(var, VUPVAL, idx); - return VUPVAL; + init_exp(var, VUPVAL, idx); /* new or old upvalue */ } } } @@ -293,10 +295,11 @@ static int singlevaraux (FuncState *fs, TString *n, expdesc *var, int base) { static void singlevar (LexState *ls, expdesc *var) { TString *varname = str_checkname(ls); FuncState *fs = ls->fs; - if (singlevaraux(fs, varname, var, 1) == VVOID) { /* global name? */ + singlevaraux(fs, varname, var, 1); + if (var->k == VVOID) { /* global name? */ expdesc key; singlevaraux(fs, ls->envn, var, 1); /* get environment variable */ - lua_assert(var->k == VLOCAL || var->k == VUPVAL); + lua_assert(var->k != VVOID); /* this one must exist */ codestring(ls, &key, varname); /* key is variable name */ luaK_indexed(fs, var, &key); /* env[varname] */ } @@ -499,7 +502,8 @@ static Proto *addprototype (LexState *ls) { if (fs->np >= f->sizep) { int oldsize = f->sizep; luaM_growvector(L, f->p, fs->np, f->sizep, Proto *, MAXARG_Bx, "functions"); - while (oldsize < f->sizep) f->p[oldsize++] = NULL; + while (oldsize < f->sizep) + f->p[oldsize++] = NULL; } f->p[fs->np++] = clp = luaF_newproto(L); luaC_objbarrier(L, f, clp); @@ -1226,7 +1230,7 @@ static void labelstat (LexState *ls, TString *label, int line) { checkrepeated(fs, ll, label); /* check for repeated labels */ checknext(ls, TK_DBCOLON); /* skip double colon */ /* create new entry for this label */ - l = newlabelentry(ls, ll, label, line, fs->pc); + l = newlabelentry(ls, ll, label, line, luaK_getlabel(fs)); skipnoopstat(ls); /* skip other no-op statements */ if (block_follow(ls, 0)) { /* label is last no-op statement in the block? */ /* assume that locals are already out of scope */ @@ -1494,7 +1498,7 @@ static void exprstat (LexState *ls) { } else { /* stat -> func */ check_condition(ls, v.v.k == VCALL, "syntax error"); - SETARG_C(getcode(fs, &v.v), 1); /* call statement uses no results */ + SETARG_C(getinstruction(fs, &v.v), 1); /* call statement uses no results */ } } @@ -1511,8 +1515,8 @@ static void retstat (LexState *ls) { if (hasmultret(e.k)) { luaK_setmultret(fs, &e); if (e.k == VCALL && nret == 1) { /* tail call? */ - SET_OPCODE(getcode(fs,&e), OP_TAILCALL); - lua_assert(GETARG_A(getcode(fs,&e)) == fs->nactvar); + SET_OPCODE(getinstruction(fs,&e), OP_TAILCALL); + lua_assert(GETARG_A(getinstruction(fs,&e)) == fs->nactvar); } first = fs->nactvar; nret = LUA_MULTRET; /* return all values */ diff --git a/lua/lparser.h b/lua/lparser.h index 62c50ca..02e9b03 100644 --- a/lua/lparser.h +++ b/lua/lparser.h @@ -1,5 +1,5 @@ /* -** $Id: lparser.h,v 1.74 2014/10/25 11:50:46 roberto Exp $ +** $Id: lparser.h,v 1.76 2015/12/30 18:16:13 roberto Exp $ ** Lua Parser ** See Copyright Notice in lua.h */ @@ -13,25 +13,38 @@ /* -** Expression descriptor +** Expression and variable descriptor. +** Code generation for variables and expressions can be delayed to allow +** optimizations; An 'expdesc' structure describes a potentially-delayed +** variable/expression. It has a description of its "main" value plus a +** list of conditional jumps that can also produce its value (generated +** by short-circuit operators 'and'/'or'). */ +/* kinds of variables/expressions */ typedef enum { - VVOID, /* no value */ - VNIL, - VTRUE, - VFALSE, - VK, /* info = index of constant in 'k' */ - VKFLT, /* nval = numerical float value */ - VKINT, /* nval = numerical integer value */ - VNONRELOC, /* info = result register */ - VLOCAL, /* info = local register */ - VUPVAL, /* info = index of upvalue in 'upvalues' */ - VINDEXED, /* t = table register/upvalue; idx = index R/K */ - VJMP, /* info = instruction pc */ - VRELOCABLE, /* info = instruction pc */ - VCALL, /* info = instruction pc */ - VVARARG /* info = instruction pc */ + VVOID, /* when 'expdesc' describes the last expression a list, + this kind means an empty list (so, no expression) */ + VNIL, /* constant nil */ + VTRUE, /* constant true */ + VFALSE, /* constant false */ + VK, /* constant in 'k'; info = index of constant in 'k' */ + VKFLT, /* floating constant; nval = numerical float value */ + VKINT, /* integer constant; nval = numerical integer value */ + VNONRELOC, /* expression has its value in a fixed register; + info = result register */ + VLOCAL, /* local variable; info = local register */ + VUPVAL, /* upvalue variable; info = index of upvalue in 'upvalues' */ + VINDEXED, /* indexed variable; + ind.vt = whether 't' is register or upvalue; + ind.t = table register or upvalue; + ind.idx = key's R/K index */ + VJMP, /* expression is a test/comparison; + info = pc of corresponding jump instruction */ + VRELOCABLE, /* expression can put result in any register; + info = instruction pc */ + VCALL, /* expression is a function call; info = instruction pc */ + VVARARG /* vararg expression; info = instruction pc */ } expkind; @@ -41,14 +54,14 @@ typedef enum { typedef struct expdesc { expkind k; union { + lua_Integer ival; /* for VKINT */ + lua_Number nval; /* for VKFLT */ + int info; /* for generic use */ struct { /* for indexed variables (VINDEXED) */ short idx; /* index (R/K) */ lu_byte t; /* table (register or upvalue) */ lu_byte vt; /* whether 't' is register (VLOCAL) or upvalue (VUPVAL) */ } ind; - int info; /* for generic use */ - lua_Number nval; /* for VKFLT */ - lua_Integer ival; /* for VKINT */ } u; int t; /* patch list of 'exit when true' */ int f; /* patch list of 'exit when false' */ diff --git a/lua/lstate.h b/lua/lstate.h index 65c914d..b3033be 100644 --- a/lua/lstate.h +++ b/lua/lstate.h @@ -1,5 +1,5 @@ /* -** $Id: lstate.h,v 2.128 2015/11/13 12:16:51 roberto Exp $ +** $Id: lstate.h,v 2.130 2015/12/16 16:39:38 roberto Exp $ ** Global State ** See Copyright Notice in lua.h */ @@ -33,6 +33,15 @@ struct lua_longjmp; /* defined in ldo.c */ +/* +** Atomic type (relative to signals) to better ensure that 'lua_sethook' +** is thread safe +*/ +#if !defined(l_signalT) +#include +#define l_signalT sig_atomic_t +#endif + /* extra stack space to handle TM calls and some other extras */ #define EXTRA_STACK 5 @@ -162,14 +171,14 @@ struct lua_State { struct lua_State *twups; /* list of threads with open upvalues */ struct lua_longjmp *errorJmp; /* current error recover point */ CallInfo base_ci; /* CallInfo for first level (C calling Lua) */ - lua_Hook hook; + volatile lua_Hook hook; ptrdiff_t errfunc; /* current error handling function (stack index) */ int stacksize; int basehookcount; int hookcount; unsigned short nny; /* number of non-yieldable calls in stack */ unsigned short nCcalls; /* number of nested C calls */ - lu_byte hookmask; + l_signalT hookmask; lu_byte allowhook; }; diff --git a/lua/lstrlib.c b/lua/lstrlib.c index fe30e34..12264f8 100644 --- a/lua/lstrlib.c +++ b/lua/lstrlib.c @@ -1,5 +1,5 @@ /* -** $Id: lstrlib.c,v 1.239 2015/11/25 16:28:17 roberto Exp $ +** $Id: lstrlib.c,v 1.251 2016/05/20 14:13:21 roberto Exp $ ** Standard library for string operations and pattern-matching ** See Copyright Notice in lua.h */ @@ -13,6 +13,7 @@ #include #include #include +#include #include #include #include @@ -26,7 +27,8 @@ /* ** maximum number of captures that a pattern can do during -** pattern-matching. This limit is arbitrary. +** pattern-matching. This limit is arbitrary, but must fit in +** an unsigned char. */ #if !defined(LUA_MAXCAPTURES) #define LUA_MAXCAPTURES 32 @@ -214,9 +216,8 @@ typedef struct MatchState { const char *src_end; /* end ('\0') of source string */ const char *p_end; /* end ('\0') of pattern */ lua_State *L; - size_t nrep; /* limit to avoid non-linear complexity */ int matchdepth; /* control for recursive depth (to avoid C stack overflow) */ - int level; /* total number of captures (finished or unfinished) */ + unsigned char level; /* total number of captures (finished or unfinished) */ struct { const char *init; ptrdiff_t len; @@ -234,17 +235,6 @@ static const char *match (MatchState *ms, const char *s, const char *p); #endif -/* -** parameters to control the maximum number of operators handled in -** a match (to avoid non-linear complexity). The maximum will be: -** (subject length) * A_REPS + B_REPS -*/ -#if !defined(A_REPS) -#define A_REPS 4 -#define B_REPS 100000 -#endif - - #define L_ESC '%' #define SPECIALS "^$*+?.([%-" @@ -502,8 +492,6 @@ static const char *match (MatchState *ms, const char *s, const char *p) { s = NULL; /* fail */ } else { /* matched once */ - if (ms->nrep-- == 0) - luaL_error(ms->L, "pattern too complex"); switch (*ep) { /* handle optional suffix */ case '?': { /* optional */ const char *res; @@ -607,10 +595,6 @@ static void prepstate (MatchState *ms, lua_State *L, ms->src_init = s; ms->src_end = s + ls; ms->p_end = p + lp; - if (ls < (MAX_SIZET - B_REPS) / A_REPS) - ms->nrep = A_REPS * ls + B_REPS; - else /* overflow (very long subject) */ - ms->nrep = MAX_SIZET; /* no limit */ } @@ -681,6 +665,7 @@ static int str_match (lua_State *L) { typedef struct GMatchState { const char *src; /* current position */ const char *p; /* pattern */ + const char *lastmatch; /* end of last match */ MatchState ms; /* match state */ } GMatchState; @@ -688,14 +673,12 @@ typedef struct GMatchState { static int gmatch_aux (lua_State *L) { GMatchState *gm = (GMatchState *)lua_touserdata(L, lua_upvalueindex(3)); const char *src; + gm->ms.L = L; for (src = gm->src; src <= gm->ms.src_end; src++) { const char *e; reprepstate(&gm->ms); - if ((e = match(&gm->ms, src, gm->p)) != NULL) { - if (e == src) /* empty match? */ - gm->src =src + 1; /* go at least one position */ - else - gm->src = e; + if ((e = match(&gm->ms, src, gm->p)) != NULL && e != gm->lastmatch) { + gm->src = gm->lastmatch = e; return push_captures(&gm->ms, src, e); } } @@ -711,7 +694,7 @@ static int gmatch (lua_State *L) { lua_settop(L, 2); /* keep them on closure to avoid being collected */ gm = (GMatchState *)lua_newuserdata(L, sizeof(GMatchState)); prepstate(&gm->ms, L, s, ls, p, lp); - gm->src = s; gm->p = p; + gm->src = s; gm->p = p; gm->lastmatch = NULL; lua_pushcclosure(L, gmatch_aux, 3); return 1; } @@ -778,12 +761,13 @@ static void add_value (MatchState *ms, luaL_Buffer *b, const char *s, static int str_gsub (lua_State *L) { size_t srcl, lp; - const char *src = luaL_checklstring(L, 1, &srcl); - const char *p = luaL_checklstring(L, 2, &lp); - int tr = lua_type(L, 3); - lua_Integer max_s = luaL_optinteger(L, 4, srcl + 1); + const char *src = luaL_checklstring(L, 1, &srcl); /* subject */ + const char *p = luaL_checklstring(L, 2, &lp); /* pattern */ + const char *lastmatch = NULL; /* end of last match */ + int tr = lua_type(L, 3); /* replacement type */ + lua_Integer max_s = luaL_optinteger(L, 4, srcl + 1); /* max replacements */ int anchor = (*p == '^'); - lua_Integer n = 0; + lua_Integer n = 0; /* replacement count */ MatchState ms; luaL_Buffer b; luaL_argcheck(L, tr == LUA_TNUMBER || tr == LUA_TSTRING || @@ -796,16 +780,15 @@ static int str_gsub (lua_State *L) { prepstate(&ms, L, src, srcl, p, lp); while (n < max_s) { const char *e; - reprepstate(&ms); - if ((e = match(&ms, src, p)) != NULL) { + reprepstate(&ms); /* (re)prepare state for new match */ + if ((e = match(&ms, src, p)) != NULL && e != lastmatch) { /* match? */ n++; - add_value(&ms, &b, src, e, tr); + add_value(&ms, &b, src, e, tr); /* add replacement to buffer */ + src = lastmatch = e; } - if (e && e>src) /* non empty match? */ - src = e; /* skip it */ - else if (src < ms.src_end) + else if (src < ms.src_end) /* otherwise, skip one character */ luaL_addchar(&b, *src++); - else break; + else break; /* end of subject */ if (anchor) break; } luaL_addlstring(&b, src, ms.src_end-src); @@ -830,7 +813,6 @@ static int str_gsub (lua_State *L) { ** Hexadecimal floating-point formatter */ -#include #include #define SIZELENMOD (sizeof(LUA_NUMBER_FRMLEN)/sizeof(char)) @@ -922,16 +904,14 @@ static int lua_number2strx (lua_State *L, char *buff, int sz, #define MAX_FORMAT 32 -static void addquoted (lua_State *L, luaL_Buffer *b, int arg) { - size_t l; - const char *s = luaL_checklstring(L, arg, &l); +static void addquoted (luaL_Buffer *b, const char *s, size_t len) { luaL_addchar(b, '"'); - while (l--) { + while (len--) { if (*s == '"' || *s == '\\' || *s == '\n') { luaL_addchar(b, '\\'); luaL_addchar(b, *s); } - else if (*s == '\0' || iscntrl(uchar(*s))) { + else if (iscntrl(uchar(*s))) { char buff[10]; if (!isdigit(uchar(*(s+1)))) l_sprintf(buff, sizeof(buff), "\\%d", (int)uchar(*s)); @@ -946,6 +926,57 @@ static void addquoted (lua_State *L, luaL_Buffer *b, int arg) { luaL_addchar(b, '"'); } + +/* +** Ensures the 'buff' string uses a dot as the radix character. +*/ +static void checkdp (char *buff, int nb) { + if (memchr(buff, '.', nb) == NULL) { /* no dot? */ + char point = lua_getlocaledecpoint(); /* try locale point */ + char *ppoint = memchr(buff, point, nb); + if (ppoint) *ppoint = '.'; /* change it to a dot */ + } +} + + +static void addliteral (lua_State *L, luaL_Buffer *b, int arg) { + switch (lua_type(L, arg)) { + case LUA_TSTRING: { + size_t len; + const char *s = lua_tolstring(L, arg, &len); + addquoted(b, s, len); + break; + } + case LUA_TNUMBER: { + char *buff = luaL_prepbuffsize(b, MAX_ITEM); + int nb; + if (!lua_isinteger(L, arg)) { /* float? */ + lua_Number n = lua_tonumber(L, arg); /* write as hexa ('%a') */ + nb = lua_number2strx(L, buff, MAX_ITEM, "%" LUA_NUMBER_FRMLEN "a", n); + checkdp(buff, nb); /* ensure it uses a dot */ + } + else { /* integers */ + lua_Integer n = lua_tointeger(L, arg); + const char *format = (n == LUA_MININTEGER) /* corner case? */ + ? "0x%" LUA_INTEGER_FRMLEN "x" /* use hexa */ + : LUA_INTEGER_FMT; /* else use default format */ + nb = l_sprintf(buff, MAX_ITEM, format, n); + } + luaL_addsize(b, nb); + break; + } + case LUA_TNIL: case LUA_TBOOLEAN: { + luaL_tolstring(L, arg, NULL); + luaL_addvalue(b); + break; + } + default: { + luaL_argerror(L, arg, "value has no literal form"); + } + } +} + + static const char *scanformat (lua_State *L, const char *strfrmt, char *form) { const char *p = strfrmt; while (*p != '\0' && strchr(FLAGS, *p) != NULL) p++; /* skip flags */ @@ -1025,7 +1056,7 @@ static int str_format (lua_State *L) { break; } case 'q': { - addquoted(L, &b, arg); + addliteral(L, &b, arg); break; } case 's': { @@ -1070,8 +1101,8 @@ static int str_format (lua_State *L) { /* value used for padding */ -#if !defined(LUA_PACKPADBYTE) -#define LUA_PACKPADBYTE 0x00 +#if !defined(LUAL_PACKPADBYTE) +#define LUAL_PACKPADBYTE 0x00 #endif /* maximum size for the binary representation of an integer */ @@ -1308,7 +1339,7 @@ static int str_pack (lua_State *L) { KOption opt = getdetails(&h, totalsize, &fmt, &size, &ntoalign); totalsize += ntoalign + size; while (ntoalign-- > 0) - luaL_addchar(&b, LUA_PACKPADBYTE); /* fill alignment */ + luaL_addchar(&b, LUAL_PACKPADBYTE); /* fill alignment */ arg++; switch (opt) { case Kint: { /* signed integers */ @@ -1343,13 +1374,11 @@ static int str_pack (lua_State *L) { case Kchar: { /* fixed-size string */ size_t len; const char *s = luaL_checklstring(L, arg, &len); - if ((size_t)size <= len) /* string larger than (or equal to) needed? */ - luaL_addlstring(&b, s, size); /* truncate string to asked size */ - else { /* string smaller than needed */ - luaL_addlstring(&b, s, len); /* add it all */ - while (len++ < (size_t)size) /* pad extra space */ - luaL_addchar(&b, LUA_PACKPADBYTE); - } + luaL_argcheck(L, len <= (size_t)size, arg, + "string longer than given size"); + luaL_addlstring(&b, s, len); /* add string */ + while (len++ < (size_t)size) /* pad extra space */ + luaL_addchar(&b, LUAL_PACKPADBYTE); break; } case Kstring: { /* strings with length count */ @@ -1372,7 +1401,7 @@ static int str_pack (lua_State *L) { totalsize += len + 1; break; } - case Kpadding: luaL_addchar(&b, LUA_PACKPADBYTE); /* FALLTHROUGH */ + case Kpadding: luaL_addchar(&b, LUAL_PACKPADBYTE); /* FALLTHROUGH */ case Kpaddalign: case Knop: arg--; /* undo increment */ break; diff --git a/lua/ltablib.c b/lua/ltablib.c index b3c9a7c..98b2f87 100644 --- a/lua/ltablib.c +++ b/lua/ltablib.c @@ -1,5 +1,5 @@ /* -** $Id: ltablib.c,v 1.90 2015/11/25 12:48:57 roberto Exp $ +** $Id: ltablib.c,v 1.93 2016/02/25 19:41:54 roberto Exp $ ** Library for Table Manipulation ** See Copyright Notice in lua.h */ @@ -53,7 +53,7 @@ static void checktab (lua_State *L, int arg, int what) { lua_pop(L, n); /* pop metatable and tested metamethods */ } else - luaL_argerror(L, arg, "table expected"); /* force an error */ + luaL_checktype(L, arg, LUA_TTABLE); /* force an error */ } } @@ -139,7 +139,7 @@ static int tmove (lua_State *L) { n = e - f + 1; /* number of elements to move */ luaL_argcheck(L, t <= LUA_MAXINTEGER - n + 1, 4, "destination wrap around"); - if (t > e || t <= f || tt != 1) { + if (t > e || t <= f || (tt != 1 && !lua_compare(L, 1, tt, LUA_OPEQ))) { for (i = 0; i < n; i++) { lua_geti(L, 1, f + i); lua_seti(L, tt, t + i); @@ -152,7 +152,7 @@ static int tmove (lua_State *L) { } } } - lua_pushvalue(L, tt); /* return "to table" */ + lua_pushvalue(L, tt); /* return destination table */ return 1; } @@ -172,7 +172,7 @@ static int tconcat (lua_State *L) { size_t lsep; const char *sep = luaL_optlstring(L, 2, "", &lsep); lua_Integer i = luaL_optinteger(L, 3, 1); - last = luaL_opt(L, luaL_checkinteger, 4, last); + last = luaL_optinteger(L, 4, last); luaL_buffinit(L, &b); for (; i < last; i++) { addfield(L, &b, i); @@ -232,6 +232,10 @@ static int unpack (lua_State *L) { */ +/* type for array indices */ +typedef unsigned int IdxT; + + /* ** Produce a "random" 'unsigned int' to randomize pivot choice. This ** macro is used only when 'sort' detects a big imbalance in the result @@ -270,7 +274,7 @@ static unsigned int l_randomizePivot (void) { #define RANLIMIT 100u -static void set2 (lua_State *L, unsigned int i, unsigned int j) { +static void set2 (lua_State *L, IdxT i, IdxT j) { lua_seti(L, 1, i); lua_seti(L, 1, j); } @@ -303,10 +307,9 @@ static int sort_comp (lua_State *L, int a, int b) { ** Pos-condition: a[lo .. i - 1] <= a[i] == P <= a[i + 1 .. up] ** returns 'i'. */ -static unsigned int partition (lua_State *L, unsigned int lo, - unsigned int up) { - unsigned int i = lo; /* will be incremented before first use */ - unsigned int j = up - 1; /* will be decremented before first use */ +static IdxT partition (lua_State *L, IdxT lo, IdxT up) { + IdxT i = lo; /* will be incremented before first use */ + IdxT j = up - 1; /* will be decremented before first use */ /* loop invariant: a[lo .. i] <= P <= a[j .. up] */ for (;;) { /* next loop: repeat ++i while a[i] < P */ @@ -340,10 +343,9 @@ static unsigned int partition (lua_State *L, unsigned int lo, ** Choose an element in the middle (2nd-3th quarters) of [lo,up] ** "randomized" by 'rnd' */ -static unsigned int choosePivot (unsigned int lo, unsigned int up, - unsigned int rnd) { - unsigned int r4 = (unsigned int)(up - lo) / 4u; /* range/4 */ - unsigned int p = rnd % (r4 * 2) + (lo + r4); +static IdxT choosePivot (IdxT lo, IdxT up, unsigned int rnd) { + IdxT r4 = (up - lo) / 4; /* range/4 */ + IdxT p = rnd % (r4 * 2) + (lo + r4); lua_assert(lo + r4 <= p && p <= up - r4); return p; } @@ -352,11 +354,11 @@ static unsigned int choosePivot (unsigned int lo, unsigned int up, /* ** QuickSort algorithm (recursive function) */ -static void auxsort (lua_State *L, unsigned int lo, unsigned int up, +static void auxsort (lua_State *L, IdxT lo, IdxT up, unsigned int rnd) { while (lo < up) { /* loop for tail recursion */ - unsigned int p; /* Pivot index */ - unsigned int n; /* to be used later */ + IdxT p; /* Pivot index */ + IdxT n; /* to be used later */ /* sort elements 'lo', 'p', and 'up' */ lua_geti(L, 1, lo); lua_geti(L, 1, up); @@ -400,7 +402,7 @@ static void auxsort (lua_State *L, unsigned int lo, unsigned int up, n = up - p; /* size of smaller interval */ up = p - 1; /* tail call for [lo .. p - 1] (lower interval) */ } - if ((up - lo) / 128u > n) /* partition too imbalanced? */ + if ((up - lo) / 128 > n) /* partition too imbalanced? */ rnd = l_randomizePivot(); /* try a new randomization */ } /* tail call auxsort(L, lo, up, rnd) */ } @@ -410,11 +412,10 @@ static int sort (lua_State *L) { lua_Integer n = aux_getn(L, 1, TAB_RW); if (n > 1) { /* non-trivial interval? */ luaL_argcheck(L, n < INT_MAX, 1, "array too big"); - luaL_checkstack(L, 40, ""); /* assume array is smaller than 2^40 */ if (!lua_isnoneornil(L, 2)) /* is there a 2nd argument? */ luaL_checktype(L, 2, LUA_TFUNCTION); /* must be a function */ lua_settop(L, 2); /* make sure there are two arguments */ - auxsort(L, 1, (unsigned int)n, 0u); + auxsort(L, 1, (IdxT)n, 0); } return 0; } diff --git a/lua/ltm.c b/lua/ltm.c index 22b4df3..4650cc2 100644 --- a/lua/ltm.c +++ b/lua/ltm.c @@ -1,5 +1,5 @@ /* -** $Id: ltm.c,v 2.36 2015/11/03 15:47:30 roberto Exp $ +** $Id: ltm.c,v 2.37 2016/02/26 19:20:15 roberto Exp $ ** Tag methods ** See Copyright Notice in lua.h */ @@ -83,6 +83,22 @@ const TValue *luaT_gettmbyobj (lua_State *L, const TValue *o, TMS event) { } +/* +** Return the name of the type of an object. For tables and userdata +** with metatable, use their '__name' metafield, if present. +*/ +const char *luaT_objtypename (lua_State *L, const TValue *o) { + Table *mt; + if ((ttistable(o) && (mt = hvalue(o)->metatable) != NULL) || + (ttisfulluserdata(o) && (mt = uvalue(o)->metatable) != NULL)) { + const TValue *name = luaH_getshortstr(mt, luaS_new(L, "__name")); + if (ttisstring(name)) /* is '__name' a string? */ + return getstr(tsvalue(name)); /* use it as type name */ + } + return ttypename(ttnov(o)); /* else use standard type name */ +} + + void luaT_callTM (lua_State *L, const TValue *f, const TValue *p1, const TValue *p2, TValue *p3, int hasres) { ptrdiff_t result = savestack(L, p3); diff --git a/lua/ltm.h b/lua/ltm.h index 180179c..63db726 100644 --- a/lua/ltm.h +++ b/lua/ltm.h @@ -1,5 +1,5 @@ /* -** $Id: ltm.h,v 2.21 2014/10/25 11:50:46 roberto Exp $ +** $Id: ltm.h,v 2.22 2016/02/26 19:20:15 roberto Exp $ ** Tag methods ** See Copyright Notice in lua.h */ @@ -51,11 +51,12 @@ typedef enum { #define fasttm(l,et,e) gfasttm(G(l), et, e) #define ttypename(x) luaT_typenames_[(x) + 1] -#define objtypename(x) ttypename(ttnov(x)) LUAI_DDEC const char *const luaT_typenames_[LUA_TOTALTAGS]; +LUAI_FUNC const char *luaT_objtypename (lua_State *L, const TValue *o); + LUAI_FUNC const TValue *luaT_gettm (Table *events, TMS event, TString *ename); LUAI_FUNC const TValue *luaT_gettmbyobj (lua_State *L, const TValue *o, TMS event); diff --git a/lua/lua.h b/lua/lua.h index 06381d7..f78899f 100644 --- a/lua/lua.h +++ b/lua/lua.h @@ -1,5 +1,5 @@ /* -** $Id: lua.h,v 1.329 2015/11/13 17:18:42 roberto Exp $ +** $Id: lua.h,v 1.331 2016/05/30 15:53:28 roberto Exp $ ** Lua - A Scripting Language ** Lua.org, PUC-Rio, Brazil (http://www.lua.org) ** See Copyright Notice at the end of this file @@ -19,11 +19,11 @@ #define LUA_VERSION_MAJOR "5" #define LUA_VERSION_MINOR "3" #define LUA_VERSION_NUM 503 -#define LUA_VERSION_RELEASE "2" +#define LUA_VERSION_RELEASE "3" #define LUA_VERSION "Lua " LUA_VERSION_MAJOR "." LUA_VERSION_MINOR #define LUA_RELEASE LUA_VERSION "." LUA_VERSION_RELEASE -#define LUA_COPYRIGHT LUA_RELEASE " Copyright (C) 1994-2015 Lua.org, PUC-Rio" +#define LUA_COPYRIGHT LUA_RELEASE " Copyright (C) 1994-2016 Lua.org, PUC-Rio" #define LUA_AUTHORS "R. Ierusalimschy, L. H. de Figueiredo, W. Celes" @@ -361,7 +361,7 @@ LUA_API void (lua_setallocf) (lua_State *L, lua_Alloc f, void *ud); #define lua_pushliteral(L, s) lua_pushstring(L, "" s) #define lua_pushglobaltable(L) \ - lua_rawgeti(L, LUA_REGISTRYINDEX, LUA_RIDX_GLOBALS) + ((void)lua_rawgeti(L, LUA_REGISTRYINDEX, LUA_RIDX_GLOBALS)) #define lua_tostring(L,i) lua_tolstring(L, (i), NULL) @@ -460,7 +460,7 @@ struct lua_Debug { /****************************************************************************** -* Copyright (C) 1994-2015 Lua.org, PUC-Rio. +* Copyright (C) 1994-2016 Lua.org, PUC-Rio. * * Permission is hereby granted, free of charge, to any person obtaining * a copy of this software and associated documentation files (the diff --git a/lua/luaconf.h b/lua/luaconf.h index b4ed500..fd447cc 100644 --- a/lua/luaconf.h +++ b/lua/luaconf.h @@ -1,5 +1,5 @@ /* -** $Id: luaconf.h,v 1.254 2015/10/21 18:17:40 roberto Exp $ +** $Id: luaconf.h,v 1.255 2016/05/01 20:06:09 roberto Exp $ ** Configuration file for Lua ** See Copyright Notice in lua.h */ @@ -612,7 +612,7 @@ ** provide its own implementation. */ #if !defined(LUA_USE_C89) -#define lua_number2strx(L,b,sz,f,n) l_sprintf(b,sz,f,n) +#define lua_number2strx(L,b,sz,f,n) ((void)L, l_sprintf(b,sz,f,n)) #endif diff --git a/lua/lvm.c b/lua/lvm.c index aba7ace..84ade6b 100644 --- a/lua/lvm.c +++ b/lua/lvm.c @@ -1,5 +1,5 @@ /* -** $Id: lvm.c,v 2.265 2015/11/23 11:30:45 roberto Exp $ +** $Id: lvm.c,v 2.268 2016/02/05 19:59:14 roberto Exp $ ** Lua virtual machine ** See Copyright Notice in lua.h */ @@ -153,55 +153,69 @@ static int forlimit (const TValue *obj, lua_Integer *p, lua_Integer step, /* -** Complete a table access: if 't' is a table, 'tm' has its metamethod; -** otherwise, 'tm' is NULL. +** Finish the table access 'val = t[key]'. +** if 'slot' is NULL, 't' is not a table; otherwise, 'slot' points to +** t[k] entry (which must be nil). */ void luaV_finishget (lua_State *L, const TValue *t, TValue *key, StkId val, - const TValue *tm) { + const TValue *slot) { int loop; /* counter to avoid infinite loops */ - lua_assert(tm != NULL || !ttistable(t)); + const TValue *tm; /* metamethod */ for (loop = 0; loop < MAXTAGLOOP; loop++) { - if (tm == NULL) { /* no metamethod (from a table)? */ - if (ttisnil(tm = luaT_gettmbyobj(L, t, TM_INDEX))) + if (slot == NULL) { /* 't' is not a table? */ + lua_assert(!ttistable(t)); + tm = luaT_gettmbyobj(L, t, TM_INDEX); + if (ttisnil(tm)) luaG_typeerror(L, t, "index"); /* no metamethod */ + /* else will try the metamethod */ } - if (ttisfunction(tm)) { /* metamethod is a function */ + else { /* 't' is a table */ + lua_assert(ttisnil(slot)); + tm = fasttm(L, hvalue(t)->metatable, TM_INDEX); /* table's metamethod */ + if (tm == NULL) { /* no metamethod? */ + setnilvalue(val); /* result is nil */ + return; + } + /* else will try the metamethod */ + } + if (ttisfunction(tm)) { /* is metamethod a function? */ luaT_callTM(L, tm, t, key, val, 1); /* call it */ return; } - t = tm; /* else repeat access over 'tm' */ - if (luaV_fastget(L,t,key,tm,luaH_get)) { /* try fast track */ - setobj2s(L, val, tm); /* done */ + t = tm; /* else try to access 'tm[key]' */ + if (luaV_fastget(L,t,key,slot,luaH_get)) { /* fast track? */ + setobj2s(L, val, slot); /* done */ return; } - /* else repeat */ + /* else repeat (tail call 'luaV_finishget') */ } - luaG_runerror(L, "gettable chain too long; possible loop"); + luaG_runerror(L, "'__index' chain too long; possible loop"); } /* -** Main function for table assignment (invoking metamethods if needed). -** Compute 't[key] = val' +** Finish a table assignment 't[key] = val'. +** If 'slot' is NULL, 't' is not a table. Otherwise, 'slot' points +** to the entry 't[key]', or to 'luaO_nilobject' if there is no such +** entry. (The value at 'slot' must be nil, otherwise 'luaV_fastset' +** would have done the job.) */ void luaV_finishset (lua_State *L, const TValue *t, TValue *key, - StkId val, const TValue *oldval) { + StkId val, const TValue *slot) { int loop; /* counter to avoid infinite loops */ for (loop = 0; loop < MAXTAGLOOP; loop++) { - const TValue *tm; - if (oldval != NULL) { - lua_assert(ttistable(t) && ttisnil(oldval)); - /* must check the metamethod */ - if ((tm = fasttm(L, hvalue(t)->metatable, TM_NEWINDEX)) == NULL && - /* no metamethod; is there a previous entry in the table? */ - (oldval != luaO_nilobject || - /* no previous entry; must create one. (The next test is - always true; we only need the assignment.) */ - (oldval = luaH_newkey(L, hvalue(t), key), 1))) { + const TValue *tm; /* '__newindex' metamethod */ + if (slot != NULL) { /* is 't' a table? */ + Table *h = hvalue(t); /* save 't' table */ + lua_assert(ttisnil(slot)); /* old value must be nil */ + tm = fasttm(L, h->metatable, TM_NEWINDEX); /* get metamethod */ + if (tm == NULL) { /* no metamethod? */ + if (slot == luaO_nilobject) /* no previous entry? */ + slot = luaH_newkey(L, h, key); /* create one */ /* no metamethod and (now) there is an entry with given key */ - setobj2t(L, cast(TValue *, oldval), val); - invalidateTMcache(hvalue(t)); - luaC_barrierback(L, hvalue(t), val); + setobj2t(L, cast(TValue *, slot), val); /* set its new value */ + invalidateTMcache(h); + luaC_barrierback(L, h, val); return; } /* else will try the metamethod */ @@ -216,11 +230,11 @@ void luaV_finishset (lua_State *L, const TValue *t, TValue *key, return; } t = tm; /* else repeat assignment over 'tm' */ - if (luaV_fastset(L, t, key, oldval, luaH_get, val)) + if (luaV_fastset(L, t, key, slot, luaH_get, val)) return; /* done */ /* else loop */ } - luaG_runerror(L, "settable chain too long; possible loop"); + luaG_runerror(L, "'__newindex' chain too long; possible loop"); } @@ -738,18 +752,28 @@ void luaV_finishOp (lua_State *L) { luai_threadyield(L); } +/* fetch an instruction and prepare its execution */ +#define vmfetch() { \ + i = *(ci->u.l.savedpc++); \ + if (L->hookmask & (LUA_MASKLINE | LUA_MASKCOUNT)) \ + Protect(luaG_traceexec(L)); \ + ra = RA(i); /* WARNING: any stack reallocation invalidates 'ra' */ \ + lua_assert(base == ci->u.l.base); \ + lua_assert(base <= L->top && L->top < L->stack + L->stacksize); \ +} + #define vmdispatch(o) switch(o) #define vmcase(l) case l: #define vmbreak break /* -** copy of 'luaV_gettable', but protecting call to potential metamethod -** (which can reallocate the stack) +** copy of 'luaV_gettable', but protecting the call to potential +** metamethod (which can reallocate the stack) */ -#define gettableProtected(L,t,k,v) { const TValue *aux; \ - if (luaV_fastget(L,t,k,aux,luaH_get)) { setobj2s(L, v, aux); } \ - else Protect(luaV_finishget(L,t,k,v,aux)); } +#define gettableProtected(L,t,k,v) { const TValue *slot; \ + if (luaV_fastget(L,t,k,slot,luaH_get)) { setobj2s(L, v, slot); } \ + else Protect(luaV_finishget(L,t,k,v,slot)); } /* same for 'luaV_settable' */ @@ -772,14 +796,9 @@ void luaV_execute (lua_State *L) { base = ci->u.l.base; /* local copy of function's base */ /* main loop of interpreter */ for (;;) { - Instruction i = *(ci->u.l.savedpc++); + Instruction i; StkId ra; - if (L->hookmask & (LUA_MASKLINE | LUA_MASKCOUNT)) - Protect(luaG_traceexec(L)); - /* WARNING: several calls may realloc the stack and invalidate 'ra' */ - ra = RA(i); - lua_assert(base == ci->u.l.base); - lua_assert(base <= L->top && L->top < L->stack + L->stacksize); + vmfetch(); vmdispatch (GET_OPCODE(i)) { vmcase(OP_MOVE) { setobjs2s(L, ra, RB(i)); diff --git a/lua/lvm.h b/lua/lvm.h index fd0e748..bcf52d2 100644 --- a/lua/lvm.h +++ b/lua/lvm.h @@ -1,5 +1,5 @@ /* -** $Id: lvm.h,v 2.39 2015/09/09 13:44:07 roberto Exp $ +** $Id: lvm.h,v 2.40 2016/01/05 16:07:21 roberto Exp $ ** Lua virtual machine ** See Copyright Notice in lua.h */ @@ -49,25 +49,24 @@ /* -** fast track for 'gettable': 1 means 'aux' points to resulted value; -** 0 means 'aux' is metamethod (if 't' is a table) or NULL. 'f' is -** the raw get function to use. +** fast track for 'gettable': if 't' is a table and 't[k]' is not nil, +** return 1 with 'slot' pointing to 't[k]' (final result). Otherwise, +** return 0 (meaning it will have to check metamethod) with 'slot' +** pointing to a nil 't[k]' (if 't' is a table) or NULL (otherwise). +** 'f' is the raw get function to use. */ -#define luaV_fastget(L,t,k,aux,f) \ +#define luaV_fastget(L,t,k,slot,f) \ (!ttistable(t) \ - ? (aux = NULL, 0) /* not a table; 'aux' is NULL and result is 0 */ \ - : (aux = f(hvalue(t), k), /* else, do raw access */ \ - !ttisnil(aux) ? 1 /* result not nil? 'aux' has it */ \ - : (aux = fasttm(L, hvalue(t)->metatable, TM_INDEX), /* get metamethod */\ - aux != NULL ? 0 /* has metamethod? must call it */ \ - : (aux = luaO_nilobject, 1)))) /* else, final result is nil */ + ? (slot = NULL, 0) /* not a table; 'slot' is NULL and result is 0 */ \ + : (slot = f(hvalue(t), k), /* else, do raw access */ \ + !ttisnil(slot))) /* result not nil? */ /* ** standard implementation for 'gettable' */ -#define luaV_gettable(L,t,k,v) { const TValue *aux; \ - if (luaV_fastget(L,t,k,aux,luaH_get)) { setobj2s(L, v, aux); } \ - else luaV_finishget(L,t,k,v,aux); } +#define luaV_gettable(L,t,k,v) { const TValue *slot; \ + if (luaV_fastget(L,t,k,slot,luaH_get)) { setobj2s(L, v, slot); } \ + else luaV_finishget(L,t,k,v,slot); } /* @@ -100,9 +99,9 @@ LUAI_FUNC int luaV_lessequal (lua_State *L, const TValue *l, const TValue *r); LUAI_FUNC int luaV_tonumber_ (const TValue *obj, lua_Number *n); LUAI_FUNC int luaV_tointeger (const TValue *obj, lua_Integer *p, int mode); LUAI_FUNC void luaV_finishget (lua_State *L, const TValue *t, TValue *key, - StkId val, const TValue *tm); + StkId val, const TValue *slot); LUAI_FUNC void luaV_finishset (lua_State *L, const TValue *t, TValue *key, - StkId val, const TValue *oldval); + StkId val, const TValue *slot); LUAI_FUNC void luaV_finishOp (lua_State *L); LUAI_FUNC void luaV_execute (lua_State *L); LUAI_FUNC void luaV_concat (lua_State *L, int total);