diff --git a/lapi.c b/lapi.c index 7b30617f7e..fb9945947d 100644 --- a/lapi.c +++ b/lapi.c @@ -187,7 +187,7 @@ LUA_API void lua_settop (lua_State *L, int idx) { api_check(L, idx <= ci->top.p - (func + 1), "new top too large"); diff = ((func + 1) + idx) - L->top.p; for (; diff > 0; diff--) - setnilvalue(s2v(L->top.p++)); /* clear new slots */ + setnilvalue2s(L->top.p++); /* clear new slots */ } else { api_check(L, -(idx+1) <= (L->top.p - (func + 1)), "invalid new top"); @@ -210,7 +210,7 @@ LUA_API void lua_closeslot (lua_State *L, int idx) { api_check(L, (L->ci->callstatus & CIST_TBC) && (L->tbclist.p == level), "no variable to close at given level"); level = luaF_close(L, level, CLOSEKTOP, 0); - setnilvalue(s2v(level)); + setnilvalue2s(level); lua_unlock(L); } @@ -366,7 +366,7 @@ LUA_API int lua_compare (lua_State *L, int index1, int index2, int op) { } -LUA_API unsigned (lua_numbertocstring) (lua_State *L, int idx, char *buff) { +LUA_API unsigned lua_numbertocstring (lua_State *L, int idx, char *buff) { const TValue *o = index2value(L, idx); if (ttisnumber(o)) { unsigned len = luaO_tostringbuff(o, buff); @@ -440,7 +440,13 @@ LUA_API lua_Unsigned lua_rawlen (lua_State *L, int idx) { case LUA_VSHRSTR: return cast(lua_Unsigned, tsvalue(o)->shrlen); case LUA_VLNGSTR: return cast(lua_Unsigned, tsvalue(o)->u.lnglen); case LUA_VUSERDATA: return cast(lua_Unsigned, uvalue(o)->len); - case LUA_VTABLE: return luaH_getn(hvalue(o)); + case LUA_VTABLE: { + lua_Unsigned res; + lua_lock(L); + res = luaH_getn(L, hvalue(o)); + lua_unlock(L); + return res; + } default: return 0; } } @@ -478,7 +484,7 @@ LUA_API lua_State *lua_tothread (lua_State *L, int idx) { /* ** Returns a pointer to the internal representation of an object. -** Note that ANSI C does not allow the conversion of a pointer to +** Note that ISO C does not allow the conversion of a pointer to ** function to a 'void*', so the conversion here goes through ** a 'size_t'. (As the returned pointer is only informative, this ** conversion should not be a problem.) @@ -507,7 +513,7 @@ LUA_API const void *lua_topointer (lua_State *L, int idx) { LUA_API void lua_pushnil (lua_State *L) { lua_lock(L); - setnilvalue(s2v(L->top.p)); + setnilvalue2s(L->top.p); api_incr_top(L); lua_unlock(L); } @@ -564,7 +570,7 @@ LUA_API const char *lua_pushexternalstring (lua_State *L, LUA_API const char *lua_pushstring (lua_State *L, const char *s) { lua_lock(L); if (s == NULL) - setnilvalue(s2v(L->top.p)); + setnilvalue2s(L->top.p); else { TString *ts; ts = luaS_new(L, s); @@ -593,12 +599,8 @@ LUA_API const char *lua_pushfstring (lua_State *L, const char *fmt, ...) { const char *ret; va_list argp; lua_lock(L); - va_start(argp, fmt); - ret = luaO_pushvfstring(L, fmt, argp); - va_end(argp); + pushvfstring(L, argp, fmt, ret); luaC_checkGC(L); - if (ret == NULL) /* error? */ - luaD_throw(L, LUA_ERRMEM); lua_unlock(L); return ret; } @@ -655,7 +657,7 @@ LUA_API int lua_pushthread (lua_State *L) { setthvalue(L, s2v(L->top.p), L); api_incr_top(L); lua_unlock(L); - return (G(L)->mainthread == L); + return (mainthread(G(L)) == L); } @@ -681,6 +683,11 @@ static int auxgetstr (lua_State *L, const TValue *t, const char *k) { } +/* +** The following function assumes that the registry cannot be a weak +** table; so, an emergency collection while using the global table +** cannot collect it. +*/ static void getGlobalTable (lua_State *L, TValue *gt) { Table *registry = hvalue(&G(L)->l_registry); lu_byte tag = luaH_getint(registry, LUA_RIDX_GLOBALS, gt); @@ -736,7 +743,7 @@ LUA_API int lua_geti (lua_State *L, int idx, lua_Integer n) { static int finishrawget (lua_State *L, lu_byte tag) { if (tagisempty(tag)) /* avoid copying empty items to the stack */ - setnilvalue(s2v(L->top.p)); + setnilvalue2s(L->top.p); api_incr_top(L); lua_unlock(L); return novariant(tag); @@ -829,7 +836,7 @@ LUA_API int lua_getiuservalue (lua_State *L, int idx, int n) { o = index2value(L, idx); api_check(L, ttisfulluserdata(o), "full userdata expected"); if (n <= 0 || n > uvalue(o)->nuvalue) { - setnilvalue(s2v(L->top.p)); + setnilvalue2s(L->top.p); t = LUA_TNONE; } else { @@ -883,9 +890,8 @@ LUA_API void lua_settable (lua_State *L, int idx) { api_checkpop(L, 2); t = index2value(L, idx); luaV_fastset(t, s2v(L->top.p - 2), s2v(L->top.p - 1), hres, luaH_pset); - if (hres == HOK) { + if (hres == HOK) luaV_finishfastset(L, t, s2v(L->top.p - 1)); - } else luaV_finishset(L, t, s2v(L->top.p - 2), s2v(L->top.p - 1), hres); L->top.p -= 2; /* pop index and value */ @@ -1070,7 +1076,7 @@ static void f_call (lua_State *L, void *ud) { LUA_API int lua_pcallk (lua_State *L, int nargs, int nresults, int errfunc, lua_KContext ctx, lua_KFunction k) { struct CallS c; - int status; + TStatus status; ptrdiff_t func; lua_lock(L); api_check(L, k == NULL || !isLua(L->ci), @@ -1107,15 +1113,16 @@ LUA_API int lua_pcallk (lua_State *L, int nargs, int nresults, int errfunc, } adjustresults(L, nresults); lua_unlock(L); - return status; + return APIstatus(status); } LUA_API int lua_load (lua_State *L, lua_Reader reader, void *data, const char *chunkname, const char *mode) { ZIO z; - int status; + TStatus status; lua_lock(L); + luaC_checkGC(L); if (!chunkname) chunkname = "?"; luaZ_init(L, &z, reader, data); status = luaD_protectedparser(L, &z, chunkname, mode); @@ -1131,7 +1138,7 @@ LUA_API int lua_load (lua_State *L, lua_Reader reader, void *data, } } lua_unlock(L); - return status; + return APIstatus(status); } @@ -1154,7 +1161,7 @@ LUA_API int lua_dump (lua_State *L, lua_Writer writer, void *data, int strip) { LUA_API int lua_status (lua_State *L) { - return L->status; + return APIstatus(L->status); } @@ -1195,11 +1202,16 @@ LUA_API int lua_gc (lua_State *L, int what, ...) { case LUA_GCSTEP: { lu_byte oldstp = g->gcstp; l_mem n = cast(l_mem, va_arg(argp, size_t)); + l_mem newdebt; int work = 0; /* true if GC did some work */ g->gcstp = 0; /* allow GC to run (other bits must be zero here) */ if (n <= 0) - n = g->GCdebt; /* force to run one basic step */ - luaE_setdebt(g, g->GCdebt - n); + newdebt = 0; /* force to run one basic step */ + else if (g->GCdebt >= n - MAX_LMEM) /* no overflow? */ + newdebt = g->GCdebt - n; + else /* overflow */ + newdebt = -MAX_LMEM; /* set debt to miminum value */ + luaE_setdebt(g, newdebt); luaC_condGC(L, (void)0, work = 1); if (work && g->gcstate == GCSpause) /* end of cycle? */ res = 1; /* signal it */ diff --git a/lauxlib.c b/lauxlib.c index 5bca18166d..af44418a0f 100644 --- a/lauxlib.c +++ b/lauxlib.c @@ -94,14 +94,14 @@ static int pushglobalfuncname (lua_State *L, lua_Debug *ar) { static void pushfuncname (lua_State *L, lua_Debug *ar) { - if (pushglobalfuncname(L, ar)) { /* try first a global name */ - lua_pushfstring(L, "function '%s'", lua_tostring(L, -1)); - lua_remove(L, -2); /* remove name */ - } - else if (*ar->namewhat != '\0') /* is there a name from code? */ + if (*ar->namewhat != '\0') /* is there a name from code? */ lua_pushfstring(L, "%s '%s'", ar->namewhat, ar->name); /* use it */ else if (*ar->what == 'm') /* main? */ lua_pushliteral(L, "main chunk"); + else if (pushglobalfuncname(L, ar)) { /* try a global name */ + lua_pushfstring(L, "function '%s'", lua_tostring(L, -1)); + lua_remove(L, -2); /* remove name */ + } else if (*ar->what != 'C') /* for Lua functions, use */ lua_pushfstring(L, "function <%s:%d>", ar->short_src, ar->linedefined); else /* nothing left... */ @@ -541,17 +541,17 @@ static void newbox (lua_State *L) { /* ** Compute new size for buffer 'B', enough to accommodate extra 'sz' -** bytes plus one for a terminating zero. (The test for "not big enough" -** also gets the case when the computation of 'newsize' overflows.) +** bytes plus one for a terminating zero. */ static size_t newbuffsize (luaL_Buffer *B, size_t sz) { - size_t newsize = (B->size / 2) * 3; /* buffer size * 1.5 */ - if (l_unlikely(sz > MAX_SIZE - B->n - 1)) + size_t newsize = B->size; + if (l_unlikely(sz >= MAX_SIZE - B->n)) return cast_sizet(luaL_error(B->L, "resulting string too large")); - if (newsize < B->n + sz + 1 || newsize > MAX_SIZE) { - /* newsize was not big enough or too big */ + /* else B->n + sz + 1 <= MAX_SIZE */ + if (newsize <= MAX_SIZE/3 * 2) /* no overflow? */ + newsize += (newsize >> 1); /* new size *= 1.5 */ + if (newsize < B->n + sz + 1) /* not big enough? */ newsize = B->n + sz + 1; - } return newsize; } @@ -742,7 +742,7 @@ typedef struct LoadF { static const char *getF (lua_State *L, void *ud, size_t *size) { LoadF *lf = (LoadF *)ud; - (void)L; /* not used */ + UNUSED(L); if (lf->n > 0) { /* are there pre-read characters to be read? */ *size = lf->n; /* return them (chars already in buffer) */ lf->n = 0; /* no more pre-read characters */ @@ -856,7 +856,7 @@ typedef struct LoadS { static const char *getS (lua_State *L, void *ud, size_t *size) { LoadS *ls = (LoadS *)ud; - (void)L; /* not used */ + UNUSED(L); if (ls->size == 0) return NULL; *size = ls->size; ls->size = 0; @@ -874,7 +874,7 @@ LUALIB_API int luaL_loadbufferx (lua_State *L, const char *buff, size_t size, LUALIB_API int luaL_loadstring (lua_State *L, const char *s) { - return luaL_loadbuffer(L, s, strlen(s), s); + return luaL_loadbufferx(L, s, strlen(s), s, "t"); } /* }====================================================== */ @@ -1046,8 +1046,8 @@ LUALIB_API const char *luaL_gsub (lua_State *L, const char *s, } -static void *l_alloc (void *ud, void *ptr, size_t osize, size_t nsize) { - (void)ud; (void)osize; /* not used */ +void *luaL_alloc (void *ud, void *ptr, size_t osize, size_t nsize) { + UNUSED(ud); UNUSED(osize); if (nsize == 0) { free(ptr); return NULL; @@ -1172,16 +1172,20 @@ static unsigned int luai_makeseed (void) { LUALIB_API unsigned int luaL_makeseed (lua_State *L) { - (void)L; /* unused */ + UNUSED(L); return luai_makeseed(); } -LUALIB_API lua_State *luaL_newstate (void) { - lua_State *L = lua_newstate(l_alloc, NULL, luai_makeseed()); +/* +** Use the name with parentheses so that headers can redefine it +** as a macro. +*/ +LUALIB_API lua_State *(luaL_newstate) (void) { + lua_State *L = lua_newstate(luaL_alloc, NULL, luaL_makeseed(NULL)); if (l_likely(L)) { lua_atpanic(L, &panic); - lua_setwarnf(L, warnfoff, L); /* default is warnings off */ + lua_setwarnf(L, warnfon, L); } return L; } diff --git a/lauxlib.h b/lauxlib.h index 4be008b90d..2d015362ff 100644 --- a/lauxlib.h +++ b/lauxlib.h @@ -81,6 +81,9 @@ LUALIB_API int (luaL_checkoption) (lua_State *L, int arg, const char *def, LUALIB_API int (luaL_fileresult) (lua_State *L, int stat, const char *fname); LUALIB_API int (luaL_execresult) (lua_State *L, int stat); +LUALIB_API void *(luaL_alloc) (void *ud, void *ptr, size_t osize, + size_t nsize); + /* predefined references */ #define LUA_NOREF (-2) @@ -100,7 +103,7 @@ LUALIB_API int (luaL_loadstring) (lua_State *L, const char *s); LUALIB_API lua_State *(luaL_newstate) (void); -LUALIB_API unsigned luaL_makeseed (lua_State *L); +LUALIB_API unsigned (luaL_makeseed) (lua_State *L); LUALIB_API lua_Integer (luaL_len) (lua_State *L, int idx); @@ -165,7 +168,11 @@ LUALIB_API void (luaL_requiref) (lua_State *L, const char *modname, /* push the value used to represent failure/error */ +#if defined(LUA_FAILISFALSE) +#define luaL_pushfail(L) lua_pushboolean(L, 0) +#else #define luaL_pushfail(L) lua_pushnil(L) +#endif diff --git a/lbaselib.c b/lbaselib.c index b296c4b761..3962ea539c 100644 --- a/lbaselib.c +++ b/lbaselib.c @@ -279,21 +279,22 @@ static int luaB_next (lua_State *L) { static int pairscont (lua_State *L, int status, lua_KContext k) { (void)L; (void)status; (void)k; /* unused */ - return 3; + return 4; /* __pairs did all the work, just return its results */ } static int luaB_pairs (lua_State *L) { luaL_checkany(L, 1); if (luaL_getmetafield(L, 1, "__pairs") == LUA_TNIL) { /* no metamethod? */ - lua_pushcfunction(L, luaB_next); /* will return generator, */ - lua_pushvalue(L, 1); /* state, */ - lua_pushnil(L); /* and initial value */ + lua_pushcfunction(L, luaB_next); /* will return generator and */ + lua_pushvalue(L, 1); /* state */ + lua_pushnil(L); /* initial value */ + lua_pushnil(L); /* to-be-closed object */ } else { lua_pushvalue(L, 1); /* argument 'self' to metamethod */ - lua_callk(L, 1, 3, 0, pairscont); /* get 3 values from metamethod */ + lua_callk(L, 1, 4, 0, pairscont); /* get 4 values from metamethod */ } - return 3; + return 4; } @@ -339,9 +340,11 @@ static int load_aux (lua_State *L, int status, int envidx) { static const char *getMode (lua_State *L, int idx) { - const char *mode = luaL_optstring(L, idx, "bt"); - if (strchr(mode, 'B') != NULL) /* Lua code cannot use fixed buffers */ + const char *mode = luaL_optstring(L, idx, NULL); + if (mode != NULL && strchr(mode, 'B') != NULL) { + /* Lua code cannot use fixed buffers */ luaL_argerror(L, idx, "invalid mode"); + } return mode; } @@ -363,33 +366,24 @@ static int luaB_loadfile (lua_State *L) { /* -** reserved slot, above all arguments, to hold a copy of the returned -** string to avoid it being collected while parsed. 'load' has four -** optional arguments (chunk, source name, mode, and environment). -*/ -#define RESERVEDSLOT 5 - - -/* -** Reader for generic 'load' function: 'lua_load' uses the -** stack for internal stuff, so the reader cannot change the -** stack top. Instead, it keeps its resulting string in a -** reserved slot inside the stack. +** Reader for generic 'load' function. */ static const char *generic_reader (lua_State *L, void *ud, size_t *size) { - (void)(ud); /* not used */ + int *firstcall = cast(int *, ud); luaL_checkstack(L, 2, "too many nested functions"); + if (*firstcall) + *firstcall = 0; + else + lua_pop(L, 1); /* remove previous result */ lua_pushvalue(L, 1); /* get function */ lua_call(L, 0, 1); /* call it */ if (lua_isnil(L, -1)) { - lua_pop(L, 1); /* pop result */ *size = 0; return NULL; } else if (l_unlikely(!lua_isstring(L, -1))) luaL_error(L, "reader function must return a string"); - lua_replace(L, RESERVEDSLOT); /* save string in reserved slot */ - return lua_tolstring(L, RESERVEDSLOT, size); + return lua_tolstring(L, -1, size); } @@ -404,10 +398,10 @@ static int luaB_load (lua_State *L) { status = luaL_loadbufferx(L, s, l, chunkname, mode); } else { /* loading from a reader function */ + int firstcall = 1; /* userdata for generic_reader */ const char *chunkname = luaL_optstring(L, 2, "=(load)"); luaL_checktype(L, 1, LUA_TFUNCTION); - lua_settop(L, RESERVEDSLOT); /* create reserved slot */ - status = lua_load(L, generic_reader, NULL, chunkname, mode); + status = lua_load(L, generic_reader, &firstcall, chunkname, mode); } return load_aux(L, status, env); } @@ -424,7 +418,7 @@ static int dofilecont (lua_State *L, int d1, lua_KContext d2) { static int luaB_dofile (lua_State *L) { const char *fname = luaL_optstring(L, 1, NULL); lua_settop(L, 1); - if (l_unlikely(luaL_loadfile(L, fname) != LUA_OK)) + if (l_unlikely(luaL_loadfilex(L, fname, "bt") != LUA_OK)) return lua_error(L); lua_callk(L, 0, LUA_MULTRET, 0, dofilecont); return dofilecont(L, 0, 0); diff --git a/lcode.c b/lcode.c index 8c04d8ab16..e002443262 100644 --- a/lcode.c +++ b/lcode.c @@ -40,8 +40,12 @@ static int codesJ (FuncState *fs, OpCode o, int sj, int k); /* semantic error */ -l_noret luaK_semerror (LexState *ls, const char *msg) { +l_noret luaK_semerror (LexState *ls, const char *fmt, ...) { + const char *msg; + va_list argp; + pushvfstring(ls->L, argp, fmt, msg); ls->t.token = 0; /* remove "near " from final message */ + ls->linenumber = ls->lastline; /* back to line of last used token */ luaX_syntaxerror(ls, msg); } @@ -562,20 +566,20 @@ static int k2proto (FuncState *fs, TValue *key, TValue *v) { TValue val; Proto *f = fs->f; int tag = luaH_get(fs->kcache, key, &val); /* query scanner table */ - int k; if (!tagisempty(tag)) { /* is there an index there? */ - k = cast_int(ivalue(&val)); + int k = cast_int(ivalue(&val)); /* collisions can happen only for float keys */ lua_assert(ttisfloat(key) || luaV_rawequalobj(&f->k[k], v)); return k; /* reuse index */ } - /* constant not found; create a new entry */ - k = addk(fs, f, v); - /* cache it for reuse; numerical value does not need GC barrier; - table is not a metatable, so it does not need to invalidate cache */ - setivalue(&val, k); - luaH_set(fs->ls->L, fs->kcache, key, &val); - return k; + else { /* constant not found; create a new entry */ + int k = addk(fs, f, v); + /* cache it for reuse; numerical value does not need GC barrier; + table is not a metatable, so it does not need to invalidate cache */ + setivalue(&val, k); + luaH_set(fs->ls->L, fs->kcache, key, &val); + return k; + } } @@ -601,13 +605,14 @@ static int luaK_intK (FuncState *fs, lua_Integer n) { /* ** Add a float to list of constants and return its index. Floats ** with integral values need a different key, to avoid collision -** with actual integers. To that, we add to the number its smaller +** with actual integers. To that end, we add to the number its smaller ** power-of-two fraction that is still significant in its scale. -** For doubles, that would be 1/2^52. +** (For doubles, the fraction would be 2^-52). ** This method is not bulletproof: different numbers may generate the ** same key (e.g., very large numbers will overflow to 'inf') and for -** floats larger than 2^53 the result is still an integer. At worst, -** this only wastes an entry with a duplicate. +** floats larger than 2^53 the result is still an integer. For those +** cases, just generate a new entry. At worst, this only wastes an entry +** with a duplicate. */ static int luaK_numberK (FuncState *fs, lua_Number r) { TValue o, kv; @@ -622,7 +627,7 @@ static int luaK_numberK (FuncState *fs, lua_Number r) { const lua_Number k = r * (1 + q); /* key */ lua_Integer ik; setfltvalue(&kv, k); /* key as a TValue */ - if (!luaV_flttointeger(k, &ik, F2Ieq)) { /* not an integral value? */ + if (!luaV_flttointeger(k, &ik, F2Ieq)) { /* not an integer value? */ int n = k2proto(fs, &kv, &o); /* use key */ if (luaV_rawequalobj(&fs->f->k[n], &o)) /* correct value? */ return n; @@ -658,11 +663,11 @@ static int boolT (FuncState *fs) { ** Add nil to list of constants and return its index. */ static int nilK (FuncState *fs) { - TValue k, v; - setnilvalue(&v); + lua_State *L = fs->ls->L; + TValue k; /* cannot use nil as key; instead use table itself */ - sethvalue(fs->ls->L, &k, fs->kcache); - return k2proto(fs, &k, &v); + sethvalue(L, &k, fs->kcache); + return k2proto(fs, &k, &G(L)->nilvalue); } @@ -701,6 +706,22 @@ static void luaK_float (FuncState *fs, int reg, lua_Number f) { } +/* +** Get the value of 'var' in a register and generate an opcode to check +** whether that register is nil. 'k' is the index of the variable name +** in the list of constants. If its value cannot be encoded in Bx, a 0 +** will use '?' for the name. +*/ +void luaK_codecheckglobal (FuncState *fs, expdesc *var, int k, int line) { + luaK_exp2anyreg(fs, var); + luaK_fixline(fs, line); + k = (k >= MAXARG_Bx) ? 0 : k + 1; + luaK_codeABx(fs, OP_ERRNNIL, var->u.info, k); + luaK_fixline(fs, line); + freeexp(fs, var); +} + + /* ** Convert a constant in 'v' into an expression description 'e' */ @@ -750,10 +771,11 @@ void luaK_setreturns (FuncState *fs, expdesc *e, int nresults) { /* ** Convert a VKSTR to a VK */ -static void str2K (FuncState *fs, expdesc *e) { +static int str2K (FuncState *fs, expdesc *e) { lua_assert(e->k == VKSTR); e->u.info = stringK(fs, e->u.strval); e->k = VK; + return e->u.info; } @@ -780,6 +802,15 @@ void luaK_setoneret (FuncState *fs, expdesc *e) { } } +/* +** Change a vararg parameter into a regular local variable +*/ +void luaK_vapar2local (FuncState *fs, expdesc *var) { + needvatab(fs->f); /* function will need a vararg table */ + /* now a vararg parameter is equivalent to a regular local variable */ + var->k = VLOCAL; +} + /* ** Ensure that expression 'e' is not a variable (nor a ). @@ -791,9 +822,12 @@ void luaK_dischargevars (FuncState *fs, expdesc *e) { const2exp(const2val(fs, e), e); break; } + case VVARGVAR: { + luaK_vapar2local(fs, e); /* turn it into a local variable */ + } /* FALLTHROUGH */ case VLOCAL: { /* already in a register */ int temp = e->u.var.ridx; - e->u.info = temp; /* (can't do a direct assignment; values overlap) */ + e->u.info = temp; /* (avoid a direct assignment; values overlap) */ e->k = VNONRELOC; /* becomes a non-relocatable value */ break; } @@ -825,6 +859,12 @@ void luaK_dischargevars (FuncState *fs, expdesc *e) { e->k = VRELOC; break; } + case VVARGIND: { + freeregs(fs, e->u.ind.t, e->u.ind.idx); + e->u.info = luaK_codeABC(fs, OP_GETVARG, 0, e->u.ind.t, e->u.ind.idx); + e->k = VRELOC; + break; + } case VVARARG: case VCALL: { luaK_setoneret(fs, e); break; @@ -987,11 +1027,11 @@ int luaK_exp2anyreg (FuncState *fs, expdesc *e) { /* -** Ensures final expression result is either in a register -** or in an upvalue. +** Ensures final expression result is either in a register, +** in an upvalue, or it is the vararg parameter. */ void luaK_exp2anyregup (FuncState *fs, expdesc *e) { - if (e->k != VUPVAL || hasjumps(e)) + if ((e->k != VUPVAL && e->k != VVARGVAR) || hasjumps(e)) luaK_exp2anyreg(fs, e); } @@ -1086,6 +1126,10 @@ void luaK_storevar (FuncState *fs, expdesc *var, expdesc *ex) { codeABRK(fs, OP_SETFIELD, var->u.ind.t, var->u.ind.idx, ex); break; } + case VVARGIND: { + needvatab(fs->f); /* function will need a vararg table */ + /* now, assignment is to a regular table */ + } /* FALLTHROUGH */ case VINDEXED: { codeABRK(fs, OP_SETTABLE, var->u.ind.t, var->u.ind.idx, ex); break; @@ -1158,7 +1202,7 @@ void luaK_goiftrue (FuncState *fs, expdesc *e) { /* ** Emit code to go through if 'e' is false, jump otherwise. */ -void luaK_goiffalse (FuncState *fs, expdesc *e) { +static void luaK_goiffalse (FuncState *fs, expdesc *e) { int pc; /* pc of new jump */ luaK_dischargevars(fs, e); switch (e->k) { @@ -1219,7 +1263,7 @@ static void codenot (FuncState *fs, expdesc *e) { ** Check whether expression 'e' is a short literal string */ static int isKstr (FuncState *fs, expdesc *e) { - return (e->k == VK && !hasjumps(e) && e->u.info <= MAXARG_B && + return (e->k == VK && !hasjumps(e) && e->u.info <= MAXINDEXRK && ttisshrstring(&fs->f->k[e->u.info])); } @@ -1297,6 +1341,13 @@ void luaK_self (FuncState *fs, expdesc *e, expdesc *key) { } +/* auxiliary function to define indexing expressions */ +static void fillidxk (expdesc *t, int idx, expkind k) { + t->u.ind.idx = cast_byte(idx); + t->k = k; +} + + /* ** Create expression 't[k]'. 't' must have its final result already in a ** register or upvalue. Upvalues can only be indexed by literal strings. @@ -1304,35 +1355,40 @@ void luaK_self (FuncState *fs, expdesc *e, expdesc *key) { ** values in registers. */ void luaK_indexed (FuncState *fs, expdesc *t, expdesc *k) { + int keystr = -1; if (k->k == VKSTR) - str2K(fs, k); + keystr = str2K(fs, k); lua_assert(!hasjumps(t) && - (t->k == VLOCAL || t->k == VNONRELOC || t->k == VUPVAL)); + (t->k == VLOCAL || t->k == VVARGVAR || + t->k == VNONRELOC || t->k == VUPVAL)); if (t->k == VUPVAL && !isKstr(fs, k)) /* upvalue indexed by non 'Kstr'? */ luaK_exp2anyreg(fs, t); /* put it in a register */ if (t->k == VUPVAL) { lu_byte temp = cast_byte(t->u.info); /* upvalue index */ + t->u.ind.t = temp; /* (avoid a direct assignment; values overlap) */ lua_assert(isKstr(fs, k)); - t->u.ind.t = temp; /* (can't do a direct assignment; values overlap) */ - t->u.ind.idx = cast(short, k->u.info); /* literal short string */ - t->k = VINDEXUP; + fillidxk(t, k->u.info, VINDEXUP); /* literal short string */ + } + else if (t->k == VVARGVAR) { /* indexing the vararg parameter? */ + int kreg = luaK_exp2anyreg(fs, k); /* put key in some register */ + lu_byte vreg = cast_byte(t->u.var.ridx); /* register with vararg param. */ + lua_assert(vreg == fs->f->numparams); + t->u.ind.t = vreg; /* (avoid a direct assignment; values may overlap?) */ + fillidxk(t, kreg, VVARGIND); /* 't' represents 'vararg[k]' */ } else { /* register index of the table */ - t->u.ind.t = cast_byte((t->k == VLOCAL) ? t->u.var.ridx: t->u.info); - if (isKstr(fs, k)) { - t->u.ind.idx = cast(short, k->u.info); /* literal short string */ - t->k = VINDEXSTR; - } - else if (isCint(k)) { /* int. constant in proper range? */ - t->u.ind.idx = cast(short, k->u.ival); - t->k = VINDEXI; - } - else { - t->u.ind.idx = cast(short, luaK_exp2anyreg(fs, k)); /* register */ - t->k = VINDEXED; - } + lu_byte temp = cast_byte((t->k == VLOCAL) ? t->u.var.ridx: t->u.info); + t->u.ind.t = temp; /* (avoid a direct assignment; values may overlap?) */ + if (isKstr(fs, k)) + fillidxk(t, k->u.info, VINDEXSTR); /* literal short string */ + else if (isCint(k)) /* int. constant in proper range? */ + fillidxk(t, cast_int(k->u.ival), VINDEXI); + else + fillidxk(t, luaK_exp2anyreg(fs, k), VINDEXED); /* register */ } + t->u.ind.keystr = keystr; /* string index in 'k' */ + t->u.ind.ro = 0; /* by default, not read-only */ } @@ -1874,6 +1930,8 @@ static int finaltarget (Instruction *code, int i) { void luaK_finish (FuncState *fs) { int i; Proto *p = fs->f; + if (p->flag & PF_VATAB) /* will it use a vararg table? */ + p->flag &= cast_byte(~PF_VAHID); /* then it will not use hidden args. */ for (i = 0; i < fs->pc; i++) { Instruction *pc = &p->code[i]; /* avoid "not used" warnings when assert is off (for 'onelua.c') */ @@ -1881,7 +1939,7 @@ void luaK_finish (FuncState *fs) { lua_assert(i == 0 || luaP_isOT(*(pc - 1)) == luaP_isIT(*pc)); switch (GET_OPCODE(*pc)) { case OP_RETURN0: case OP_RETURN1: { - if (!(fs->needclose || (p->flag & PF_ISVARARG))) + if (!(fs->needclose || (p->flag & PF_VAHID))) break; /* no extra work */ /* else use OP_RETURN to do the extra work */ SET_OPCODE(*pc, OP_RETURN); @@ -1889,13 +1947,23 @@ void luaK_finish (FuncState *fs) { case OP_RETURN: case OP_TAILCALL: { if (fs->needclose) SETARG_k(*pc, 1); /* signal that it needs to close */ - if (p->flag & PF_ISVARARG) - SETARG_C(*pc, p->numparams + 1); /* signal that it is vararg */ + if (p->flag & PF_VAHID) /* does it use hidden arguments? */ + SETARG_C(*pc, p->numparams + 1); /* signal that */ + break; + } + case OP_GETVARG: { + if (p->flag & PF_VATAB) /* function has a vararg table? */ + SET_OPCODE(*pc, OP_GETTABLE); /* must get vararg there */ + break; + } + case OP_VARARG: { + if (p->flag & PF_VATAB) /* function has a vararg table? */ + SETARG_k(*pc, 1); /* must get vararg there */ break; } - case OP_JMP: { + case OP_JMP: { /* to optimize jumps to jumps */ int target = finaltarget(p->code, i); - fixjump(fs, i, target); + fixjump(fs, i, target); /* jump directly to final target */ break; } default: break; diff --git a/lcode.h b/lcode.h index 414ebe3999..09e5c802b0 100644 --- a/lcode.h +++ b/lcode.h @@ -68,9 +68,12 @@ LUAI_FUNC int luaK_codevABCk (FuncState *fs, OpCode o, int A, int B, int C, LUAI_FUNC int luaK_exp2const (FuncState *fs, const expdesc *e, TValue *v); LUAI_FUNC void luaK_fixline (FuncState *fs, int line); LUAI_FUNC void luaK_nil (FuncState *fs, int from, int n); +LUAI_FUNC void luaK_codecheckglobal (FuncState *fs, expdesc *var, int k, + int line); LUAI_FUNC void luaK_reserveregs (FuncState *fs, int n); LUAI_FUNC void luaK_checkstack (FuncState *fs, int n); LUAI_FUNC void luaK_int (FuncState *fs, int reg, lua_Integer n); +LUAI_FUNC void luaK_vapar2local (FuncState *fs, expdesc *var); LUAI_FUNC void luaK_dischargevars (FuncState *fs, expdesc *e); LUAI_FUNC int luaK_exp2anyreg (FuncState *fs, expdesc *e); LUAI_FUNC void luaK_exp2anyregup (FuncState *fs, expdesc *e); @@ -79,7 +82,6 @@ LUAI_FUNC void luaK_exp2val (FuncState *fs, expdesc *e); LUAI_FUNC void luaK_self (FuncState *fs, expdesc *e, expdesc *key); LUAI_FUNC void luaK_indexed (FuncState *fs, expdesc *t, expdesc *k); LUAI_FUNC void luaK_goiftrue (FuncState *fs, expdesc *e); -LUAI_FUNC void luaK_goiffalse (FuncState *fs, expdesc *e); LUAI_FUNC void luaK_storevar (FuncState *fs, expdesc *var, expdesc *e); LUAI_FUNC void luaK_setreturns (FuncState *fs, expdesc *e, int nresults); LUAI_FUNC void luaK_setoneret (FuncState *fs, expdesc *e); @@ -97,7 +99,7 @@ LUAI_FUNC void luaK_settablesize (FuncState *fs, int pc, int ra, int asize, int hsize); LUAI_FUNC void luaK_setlist (FuncState *fs, int base, int nelems, int tostore); LUAI_FUNC void luaK_finish (FuncState *fs); -LUAI_FUNC l_noret luaK_semerror (LexState *ls, const char *msg); +LUAI_FUNC l_noret luaK_semerror (LexState *ls, const char *fmt, ...); #endif diff --git a/lcorolib.c b/lcorolib.c index 3d95f8735a..eb30bf4da5 100644 --- a/lcorolib.c +++ b/lcorolib.c @@ -154,8 +154,13 @@ static int luaB_costatus (lua_State *L) { } +static lua_State *getoptco (lua_State *L) { + return (lua_isnone(L, 1) ? L : getco(L)); +} + + static int luaB_yieldable (lua_State *L) { - lua_State *co = lua_isnone(L, 1) ? L : getco(L); + lua_State *co = getoptco(L); lua_pushboolean(L, lua_isyieldable(co)); return 1; } @@ -169,7 +174,7 @@ static int luaB_corunning (lua_State *L) { static int luaB_close (lua_State *L) { - lua_State *co = getco(L); + lua_State *co = getoptco(L); int status = auxstatus(L, co); switch (status) { case COS_DEAD: case COS_YIELD: { @@ -184,8 +189,17 @@ static int luaB_close (lua_State *L) { return 2; } } - default: /* normal or running coroutine */ + case COS_NORM: return luaL_error(L, "cannot close a %s coroutine", statname[status]); + case COS_RUN: + lua_geti(L, LUA_REGISTRYINDEX, LUA_RIDX_MAINTHREAD); /* get main */ + if (lua_tothread(L, -1) == co) + return luaL_error(L, "cannot close main thread"); + lua_closethread(co, L); /* close itself */ + /* previous call does not return *//* FALLTHROUGH */ + default: + lua_assert(0); + return 0; } } diff --git a/lctype.c b/lctype.c index 9542280942..b1a43e44b0 100644 --- a/lctype.c +++ b/lctype.c @@ -18,7 +18,7 @@ #if defined (LUA_UCID) /* accept UniCode IDentifiers? */ -/* consider all non-ascii codepoints to be alphabetic */ +/* consider all non-ASCII codepoints to be alphabetic */ #define NONA 0x01 #else #define NONA 0x00 /* default */ diff --git a/ldblib.c b/ldblib.c index c7b74812e8..051327cc14 100644 --- a/ldblib.c +++ b/ldblib.c @@ -427,7 +427,7 @@ static int db_debug (lua_State *L) { if (fgets(buffer, sizeof(buffer), stdin) == NULL || strcmp(buffer, "cont\n") == 0) return 0; - if (luaL_loadbuffer(L, buffer, strlen(buffer), "=(debug command)") || + if (luaL_loadbufferx(L, buffer, strlen(buffer), "=(debug command)", "t") || lua_pcall(L, 0, 0, 0)) lua_writestringerror("%s\n", luaL_tolstring(L, -1, NULL)); lua_settop(L, 0); /* remove eventual returns */ diff --git a/ldebug.c b/ldebug.c index af3b758334..5293ccb9f3 100644 --- a/ldebug.c +++ b/ldebug.c @@ -33,6 +33,8 @@ #define LuaClosure(f) ((f) != NULL && (f)->c.tt == LUA_VLCL) +static const char strlocal[] = "local"; +static const char strupval[] = "upvalue"; static const char *funcnamefromcall (lua_State *L, CallInfo *ci, const char **name); @@ -182,7 +184,7 @@ static const char *upvalname (const Proto *p, int uv) { static const char *findvararg (CallInfo *ci, int n, StkId *pos) { - if (clLvalue(s2v(ci->func.p))->p->flag & PF_ISVARARG) { + if (clLvalue(s2v(ci->func.p))->p->flag & PF_VAHID) { int nextra = ci->u.l.nextraargs; if (n >= -nextra) { /* 'n' is negative */ *pos = ci->func.p - nextra - (n + 1); @@ -289,7 +291,7 @@ static int nextline (const Proto *p, int currentline, int pc) { static void collectvalidlines (lua_State *L, Closure *f) { if (!LuaClosure(f)) { - setnilvalue(s2v(L->top.p)); + setnilvalue2s(L->top.p); api_incr_top(L); } else { @@ -302,7 +304,7 @@ static void collectvalidlines (lua_State *L, Closure *f) { int i; TValue v; setbtvalue(&v); /* boolean 'true' to be the value of all indices */ - if (!(p->flag & PF_ISVARARG)) /* regular function? */ + if (!(isvararg(p))) /* regular function? */ i = 0; /* consider all instructions */ else { /* vararg function */ lua_assert(GET_OPCODE(p->code[0]) == OP_VARARGPREP); @@ -346,7 +348,7 @@ static int auxgetinfo (lua_State *L, const char *what, lua_Debug *ar, ar->nparams = 0; } else { - ar->isvararg = (f->l.p->flag & PF_ISVARARG) ? 1 : 0; + ar->isvararg = (isvararg(f->l.p)) ? 1 : 0; ar->nparams = f->l.p->numparams; } break; @@ -505,7 +507,7 @@ static const char *basicgetobjname (const Proto *p, int *ppc, int reg, int pc = *ppc; *name = luaF_getlocalname(p, reg + 1, pc); if (*name) /* is a local? */ - return "local"; + return strlocal; /* else try symbolic execution */ *ppc = pc = findsetreg(p, pc, reg); if (pc != -1) { /* could find instruction? */ @@ -520,7 +522,7 @@ static const char *basicgetobjname (const Proto *p, int *ppc, int reg, } case OP_GETUPVAL: { *name = upvalname(p, GETARG_B(i)); - return "upvalue"; + return strupval; } case OP_LOADK: return kname(p, GETARG_Bx(i), name); case OP_LOADKX: return kname(p, GETARG_Ax(p->code[pc + 1]), name); @@ -550,8 +552,13 @@ static const char *isEnv (const Proto *p, int pc, Instruction i, int isup) { const char *name; /* name of indexed variable */ if (isup) /* is 't' an upvalue? */ name = upvalname(p, t); - else /* 't' is a register */ - basicgetobjname(p, &pc, t, &name); + else { /* 't' is a register */ + const char *what = basicgetobjname(p, &pc, t, &name); + /* 'name' must be the name of a local variable (at the current + level or an upvalue) */ + if (what != strlocal && what != strupval) + name = NULL; /* cannot be the variable _ENV */ + } return (name && strcmp(name, LUA_ENV) == 0) ? "global" : "field"; } @@ -573,7 +580,7 @@ static const char *getobjname (const Proto *p, int lastpc, int reg, kname(p, k, name); return isEnv(p, lastpc, i, 1); } - case OP_GETTABLE: { + case OP_GETTABLE: case OP_GETVARG: { int k = GETARG_C(i); /* key index */ rname(p, lastpc, k, name); return isEnv(p, lastpc, i, 0); @@ -698,7 +705,7 @@ static const char *getupvalname (CallInfo *ci, const TValue *o, for (i = 0; i < c->nupvalues; i++) { if (c->upvals[i]->v.p == o) { *name = upvalname(c->p, i); - return "upvalue"; + return strupval; } } return NULL; @@ -807,19 +814,26 @@ l_noret luaG_ordererror (lua_State *L, const TValue *p1, const TValue *p2) { } +l_noret luaG_errnnil (lua_State *L, LClosure *cl, int k) { + const char *globalname = "?"; /* default name if k == 0 */ + if (k > 0) + kname(cl->p, k - 1, &globalname); + luaG_runerror(L, "global '%s' already defined", globalname); +} + + /* add src:line information to 'msg' */ const char *luaG_addinfo (lua_State *L, const char *msg, TString *src, int line) { - char buff[LUA_IDSIZE]; - if (src) { + if (src == NULL) /* no debug information? */ + return luaO_pushfstring(L, "?:?: %s", msg); + else { + char buff[LUA_IDSIZE]; size_t idlen; const char *id = getlstr(src, idlen); luaO_chunkid(buff, id, idlen); + return luaO_pushfstring(L, "%s:%d: %s", buff, line, msg); } - else { /* no source available; use "?" instead */ - buff[0] = '?'; buff[1] = '\0'; - } - return luaO_pushfstring(L, "%s:%d: %s", buff, line, msg); } @@ -832,6 +846,10 @@ l_noret luaG_errormsg (lua_State *L) { L->top.p++; /* assume EXTRA_STACK */ luaD_callnoyield(L, L->top.p - 2, 1); /* call it */ } + if (ttisnil(s2v(L->top.p - 1))) { /* error object is nil? */ + /* change it to a proper message */ + setsvalue2s(L, L->top.p - 1, luaS_newliteral(L, "")); + } luaD_throw(L, LUA_ERRRUN); } @@ -841,10 +859,8 @@ l_noret luaG_runerror (lua_State *L, const char *fmt, ...) { const char *msg; va_list argp; luaC_checkGC(L); /* error message uses memory */ - va_start(argp, fmt); - msg = luaO_pushvfstring(L, fmt, argp); /* format message */ - va_end(argp); - if (msg != NULL && isLua(ci)) { /* Lua function? (and no error) */ + pushvfstring(L, argp, fmt, msg); + if (isLua(ci)) { /* Lua function? */ /* add source:line information */ luaG_addinfo(L, msg, ci_func(ci)->p->source, getcurrentline(ci)); setobjs2s(L, L->top.p - 2, L->top.p - 1); /* remove 'msg' */ @@ -896,7 +912,7 @@ int luaG_tracecall (lua_State *L) { Proto *p = ci_func(ci)->p; ci->u.l.trap = 1; /* ensure hooks will be checked */ if (ci->u.l.savedpc == p->code) { /* first instruction (not resuming)? */ - if (p->flag & PF_ISVARARG) + if (isvararg(p)) return 0; /* hooks will start at VARARGPREP instruction */ else if (!(ci->callstatus & CIST_HOOKYIELD)) /* not yielded? */ luaD_hookcall(L, ci); /* check 'call' hook */ diff --git a/ldebug.h b/ldebug.h index 2bfce3cb5e..20d07818b4 100644 --- a/ldebug.h +++ b/ldebug.h @@ -53,6 +53,7 @@ LUAI_FUNC l_noret luaG_tointerror (lua_State *L, const TValue *p1, const TValue *p2); LUAI_FUNC l_noret luaG_ordererror (lua_State *L, const TValue *p1, const TValue *p2); +LUAI_FUNC l_noret luaG_errnnil (lua_State *L, LClosure *cl, int k); LUAI_FUNC l_noret luaG_runerror (lua_State *L, const char *fmt, ...); LUAI_FUNC const char *luaG_addinfo (lua_State *L, const char *msg, TString *src, int line); diff --git a/ldo.c b/ldo.c index 31c00a2169..1f18b18614 100644 --- a/ldo.c +++ b/ldo.c @@ -57,10 +57,18 @@ ** ======================================================= */ +/* chained list of long jump buffers */ +typedef struct lua_longjmp { + struct lua_longjmp *previous; + jmp_buf b; + volatile TStatus status; /* error code */ +} lua_longjmp; + + /* ** LUAI_THROW/LUAI_TRY define how Lua does exception handling. By ** default, Lua handles errors with exceptions when compiling as -** C++ code, with _longjmp/_setjmp when asked to use them, and with +** C++ code, with _longjmp/_setjmp when available (POSIX), and with ** longjmp/setjmp otherwise. */ #if !defined(LUAI_THROW) /* { */ @@ -69,74 +77,64 @@ /* C++ exceptions */ #define LUAI_THROW(L,c) throw(c) -#define LUAI_TRY(L,c,f,ud) \ - try { (f)(L, ud); } catch(...) { if ((c)->status == 0) (c)->status = -1; } -#define luai_jmpbuf int /* dummy field */ + +static void LUAI_TRY (lua_State *L, lua_longjmp *c, Pfunc f, void *ud) { + try { + f(L, ud); /* call function protected */ + } + catch (lua_longjmp *c1) { /* Lua error */ + if (c1 != c) /* not the correct level? */ + throw; /* rethrow to upper level */ + } + catch (...) { /* non-Lua exception */ + c->status = -1; /* create some error code */ + } +} + #elif defined(LUA_USE_POSIX) /* }{ */ -/* in POSIX, try _longjmp/_setjmp (more efficient) */ +/* in POSIX, use _longjmp/_setjmp (more efficient) */ #define LUAI_THROW(L,c) _longjmp((c)->b, 1) #define LUAI_TRY(L,c,f,ud) if (_setjmp((c)->b) == 0) ((f)(L, ud)) -#define luai_jmpbuf jmp_buf #else /* }{ */ /* ISO C handling with long jumps */ #define LUAI_THROW(L,c) longjmp((c)->b, 1) #define LUAI_TRY(L,c,f,ud) if (setjmp((c)->b) == 0) ((f)(L, ud)) -#define luai_jmpbuf jmp_buf #endif /* } */ #endif /* } */ - -/* chain list of long jump buffers */ -struct lua_longjmp { - struct lua_longjmp *previous; - luai_jmpbuf b; - volatile int status; /* error code */ -}; - - -void luaD_seterrorobj (lua_State *L, int errcode, StkId oldtop) { - switch (errcode) { - case LUA_ERRMEM: { /* memory error? */ - setsvalue2s(L, oldtop, G(L)->memerrmsg); /* reuse preregistered msg. */ - break; - } - case LUA_ERRERR: { - setsvalue2s(L, oldtop, luaS_newliteral(L, "error in error handling")); - break; - } - case LUA_OK: { /* special case only for closing upvalues */ - setnilvalue(s2v(oldtop)); /* no error message */ - break; - } - default: { - lua_assert(errorstatus(errcode)); /* real error */ - setobjs2s(L, oldtop, L->top.p - 1); /* error message on current top */ - break; - } +void luaD_seterrorobj (lua_State *L, TStatus errcode, StkId oldtop) { + if (errcode == LUA_ERRMEM) { /* memory error? */ + setsvalue2s(L, oldtop, G(L)->memerrmsg); /* reuse preregistered msg. */ + } + else { + lua_assert(errorstatus(errcode)); /* must be a real error */ + lua_assert(!ttisnil(s2v(L->top.p - 1))); /* with a non-nil object */ + setobjs2s(L, oldtop, L->top.p - 1); /* move it to 'oldtop' */ } - L->top.p = oldtop + 1; + L->top.p = oldtop + 1; /* top goes back to old top plus error object */ } -l_noret luaD_throw (lua_State *L, int errcode) { +l_noret luaD_throw (lua_State *L, TStatus errcode) { if (L->errorJmp) { /* thread has an error handler? */ L->errorJmp->status = errcode; /* set status */ LUAI_THROW(L, L->errorJmp); /* jump to it */ } else { /* thread has no error handler */ global_State *g = G(L); + lua_State *mainth = mainthread(g); errcode = luaE_resetthread(L, errcode); /* close all upvalues */ - L->status = cast_byte(errcode); - if (g->mainthread->errorJmp) { /* main thread has a handler? */ - setobjs2s(L, g->mainthread->top.p++, L->top.p - 1); /* copy error obj. */ - luaD_throw(g->mainthread, errcode); /* re-throw in main thread */ + L->status = errcode; + if (mainth->errorJmp) { /* main thread has a handler? */ + setobjs2s(L, mainth->top.p++, L->top.p - 1); /* copy error obj. */ + luaD_throw(mainth, errcode); /* re-throw in main thread */ } else { /* no handler at all; abort */ if (g->panic) { /* panic function? */ @@ -149,9 +147,19 @@ l_noret luaD_throw (lua_State *L, int errcode) { } -int luaD_rawrunprotected (lua_State *L, Pfunc f, void *ud) { +l_noret luaD_throwbaselevel (lua_State *L, TStatus errcode) { + if (L->errorJmp) { + /* unroll error entries up to the first level */ + while (L->errorJmp->previous != NULL) + L->errorJmp = L->errorJmp->previous; + } + luaD_throw(L, errcode); +} + + +TStatus luaD_rawrunprotected (lua_State *L, Pfunc f, void *ud) { l_uint32 oldnCcalls = L->nCcalls; - struct lua_longjmp lj; + lua_longjmp lj; lj.status = LUA_OK; lj.previous = L->errorJmp; /* chain new error handler */ L->errorJmp = &lj; @@ -174,6 +182,20 @@ int luaD_rawrunprotected (lua_State *L, Pfunc f, void *ud) { #define STACKERRSPACE 200 +/* +** LUAI_MAXSTACK limits the size of the Lua stack. +** It must fit into INT_MAX/2. +*/ + +#if !defined(LUAI_MAXSTACK) +#if 1000000 < (INT_MAX / 2) +#define LUAI_MAXSTACK 1000000 +#else +#define LUAI_MAXSTACK (INT_MAX / 2u) +#endif +#endif + + /* maximum stack size that respects size_t */ #define MAXSTACK_BYSIZET ((MAX_SIZET / sizeof(StackValue)) - STACKERRSPACE) @@ -189,16 +211,49 @@ int luaD_rawrunprotected (lua_State *L, Pfunc f, void *ud) { #define ERRORSTACKSIZE (MAXSTACK + STACKERRSPACE) +/* raise a stack error while running the message handler */ +l_noret luaD_errerr (lua_State *L) { + TString *msg = luaS_newliteral(L, "error in error handling"); + setsvalue2s(L, L->top.p, msg); + L->top.p++; /* assume EXTRA_STACK */ + luaD_throw(L, LUA_ERRERR); +} + + +/* +** Check whether stacks have enough space to run a simple function (such +** as a finalizer): At least BASIC_STACK_SIZE in the Lua stack, two +** available CallInfos, and two "slots" in the C stack. +*/ +int luaD_checkminstack (lua_State *L) { + if (getCcalls(L) >= LUAI_MAXCCALLS - 2) + return 0; /* not enough C-stack slots */ + if (L->ci->next == NULL && luaE_extendCI(L, 0) == NULL) + return 0; /* unable to allocate first ci */ + if (L->ci->next->next == NULL && luaE_extendCI(L, 0) == NULL) + return 0; /* unable to allocate second ci */ + if (L->stack_last.p - L->top.p >= BASIC_STACK_SIZE) + return 1; /* enough (BASIC_STACK_SIZE) free slots in the Lua stack */ + else /* try to grow stack to a size with enough free slots */ + return luaD_growstack(L, BASIC_STACK_SIZE, 0); +} + + /* ** In ISO C, any pointer use after the pointer has been deallocated is -** undefined behavior. So, before a stack reallocation, all pointers are -** changed to offsets, and after the reallocation they are changed back -** to pointers. As during the reallocation the pointers are invalid, the -** reallocation cannot run emergency collections. -** +** undefined behavior. So, before a stack reallocation, all pointers +** should be changed to offsets, and after the reallocation they should +** be changed back to pointers. As during the reallocation the pointers +** are invalid, the reallocation cannot run emergency collections. +** Alternatively, we can use the old address after the deallocation. +** That is not strict ISO C, but seems to work fine everywhere. +** The following macro chooses how strict is the code. */ +#if !defined(LUAI_STRICT_ADDRESS) +#define LUAI_STRICT_ADDRESS 1 +#endif -#if 1 +#if LUAI_STRICT_ADDRESS /* ** Change all pointers to the stack into offsets. */ @@ -237,12 +292,16 @@ static void correctstack (lua_State *L, StkId oldstack) { #else /* -** Alternatively, we can use the old address after the deallocation. -** That is not strict ISO C, but seems to work fine everywhere. +** Assume that it is fine to use an address after its deallocation, +** as long as we do not dereference it. */ -static void relstack (lua_State *L) { UNUSED(L); } +static void relstack (lua_State *L) { UNUSED(L); } /* do nothing */ + +/* +** Correct pointers into 'oldstack' to point into 'L->stack'. +*/ static void correctstack (lua_State *L, StkId oldstack) { CallInfo *ci; UpVal *up; @@ -260,7 +319,6 @@ static void correctstack (lua_State *L, StkId oldstack) { ci->u.l.trap = 1; /* signal to update 'trap' in 'luaV_execute' */ } } - #endif @@ -291,7 +349,7 @@ int luaD_reallocstack (lua_State *L, int newsize, int raiseerror) { correctstack(L, oldstack); /* change offsets back to pointers */ L->stack_last.p = L->stack.p + newsize; for (i = oldsize + EXTRA_STACK; i < newsize + EXTRA_STACK; i++) - setnilvalue(s2v(newstack + i)); /* erase new segment */ + setnilvalue2s(newstack + i); /* erase new segment */ return 1; } @@ -308,11 +366,11 @@ int luaD_growstack (lua_State *L, int n, int raiseerror) { a stack error; cannot grow further than that. */ lua_assert(stacksize(L) == ERRORSTACKSIZE); if (raiseerror) - luaD_throw(L, LUA_ERRERR); /* error inside message handler */ + luaD_errerr(L); /* stack error inside message handler */ return 0; /* if not 'raiseerror', just signal it */ } else if (n < MAXSTACK) { /* avoids arithmetic overflows */ - int newsize = 2 * size; /* tentative new size */ + int newsize = size + (size >> 1); /* tentative new size (size * 1.5) */ int needed = cast_int(L->top.p - L->stack.p) + n; if (newsize > MAXSTACK) /* cannot cross the limit */ newsize = MAXSTACK; @@ -448,7 +506,7 @@ static void rethook (lua_State *L, CallInfo *ci, int nres) { int ftransfer; if (isLua(ci)) { Proto *p = ci_func(ci)->p; - if (p->flag & PF_ISVARARG) + if (p->flag & PF_VAHID) delta = ci->u.l.nextraargs + p->numparams + 1; } ci->func.p += delta; /* if vararg, back to virtual 'func' */ @@ -496,7 +554,7 @@ l_sinline void genmoveresults (lua_State *L, StkId res, int nres, for (i = 0; i < nres; i++) /* move all results to correct place */ setobjs2s(L, res + i, firstresult + i); for (; i < wanted; i++) /* complete wanted number of results */ - setnilvalue(s2v(res + i)); + setnilvalue2s(res + i); L->top.p = res + wanted; /* top points after the last result */ } @@ -506,7 +564,7 @@ l_sinline void genmoveresults (lua_State *L, StkId res, int nres, ** to 'res'. Handle most typical cases (zero results for commands, ** one result for expressions, multiple results for tail calls/single ** parameters) separated. The flag CIST_TBC in 'fwanted', if set, -** forces the swicth to go to the default case. +** forces the switch to go to the default case. */ l_sinline void moveresults (lua_State *L, StkId res, int nres, l_uint32 fwanted) { @@ -516,7 +574,7 @@ l_sinline void moveresults (lua_State *L, StkId res, int nres, return; case 1 + 1: /* one value needed */ if (nres == 0) /* no results? */ - setnilvalue(s2v(res)); /* adjust with nil */ + setnilvalue2s(res); /* adjust with nil */ else /* at least one result */ setobjs2s(L, res, L->top.p - nres); /* move it to proper place */ L->top.p = res + 1; @@ -566,7 +624,7 @@ void luaD_poscall (lua_State *L, CallInfo *ci, int nres) { -#define next_ci(L) (L->ci->next ? L->ci->next : luaE_extendCI(L)) +#define next_ci(L) (L->ci->next ? L->ci->next : luaE_extendCI(L, 1)) /* @@ -636,7 +694,7 @@ int luaD_pretailcall (lua_State *L, CallInfo *ci, StkId func, setobjs2s(L, ci->func.p + i, func + i); func = ci->func.p; /* moved-down function */ for (; narg1 <= nfixparams; narg1++) - setnilvalue(s2v(func + narg1)); /* complete missing arguments */ + setnilvalue2s(func + narg1); /* complete missing arguments */ ci->top.p = func + 1 + fsize; /* top for new function */ lua_assert(ci->top.p <= L->stack_last.p); ci->u.l.savedpc = p->code; /* starting point */ @@ -683,7 +741,7 @@ CallInfo *luaD_precall (lua_State *L, StkId func, int nresults) { L->ci = ci = prepCallInfo(L, func, status, func + 1 + fsize); ci->u.l.savedpc = p->code; /* starting point */ for (; narg < nfixparams; narg++) - setnilvalue(s2v(L->top.p++)); /* complete missing arguments */ + setnilvalue2s(L->top.p++); /* complete missing arguments */ lua_assert(ci->top.p <= L->stack_last.p); return ci; } @@ -751,8 +809,8 @@ void luaD_callnoyield (lua_State *L, StkId func, int nResults) { ** particular, field CIST_RECST preserves the error status across these ** multiple runs, changing only if there is a new error. */ -static int finishpcallk (lua_State *L, CallInfo *ci) { - int status = getcistrecst(ci); /* get original status */ +static TStatus finishpcallk (lua_State *L, CallInfo *ci) { + TStatus status = getcistrecst(ci); /* get original status */ if (l_likely(status == LUA_OK)) /* no error? */ status = LUA_YIELD; /* was interrupted by an yield */ else { /* error */ @@ -792,14 +850,15 @@ static void finishCcall (lua_State *L, CallInfo *ci) { /* don't need to reset CIST_CLSRET, as it will be set again anyway */ } else { - int status = LUA_YIELD; /* default if there were no errors */ + TStatus status = LUA_YIELD; /* default if there were no errors */ + lua_KFunction kf = ci->u.c.k; /* continuation function */ /* must have a continuation and must be able to call it */ - lua_assert(ci->u.c.k != NULL && yieldable(L)); + lua_assert(kf != NULL && yieldable(L)); if (ci->callstatus & CIST_YPCALL) /* was inside a 'lua_pcallk'? */ status = finishpcallk(L, ci); /* finish it */ adjustresults(L, LUA_MULTRET); /* finish 'lua_callk' */ lua_unlock(L); - n = (*ci->u.c.k)(L, status, ci->u.c.ctx); /* call continuation */ + n = (*kf)(L, APIstatus(status), ci->u.c.ctx); /* call continuation */ lua_lock(L); api_checknelems(L, n); } @@ -901,7 +960,7 @@ static void resume (lua_State *L, void *ud) { ** (status == LUA_YIELD), or an unprotected error ('findpcall' doesn't ** find a recover point). */ -static int precover (lua_State *L, int status) { +static TStatus precover (lua_State *L, TStatus status) { CallInfo *ci; while (errorstatus(status) && (ci = findpcall(L)) != NULL) { L->ci = ci; /* go down to recovery functions */ @@ -914,7 +973,7 @@ static int precover (lua_State *L, int status) { LUA_API int lua_resume (lua_State *L, lua_State *from, int nargs, int *nresults) { - int status; + TStatus status; lua_lock(L); if (L->status == LUA_OK) { /* may be starting a coroutine */ if (L->ci != &L->base_ci) /* not in base level? */ @@ -936,14 +995,14 @@ LUA_API int lua_resume (lua_State *L, lua_State *from, int nargs, if (l_likely(!errorstatus(status))) lua_assert(status == L->status); /* normal end or yield */ else { /* unrecoverable error */ - L->status = cast_byte(status); /* mark thread as 'dead' */ + L->status = status; /* mark thread as 'dead' */ luaD_seterrorobj(L, status, L->top.p); /* push error message */ L->ci->top.p = L->top.p; } *nresults = (status == LUA_YIELD) ? L->ci->u2.nyield : cast_int(L->top.p - (L->ci->func.p + 1)); lua_unlock(L); - return status; + return APIstatus(status); } @@ -960,7 +1019,7 @@ LUA_API int lua_yieldk (lua_State *L, int nresults, lua_KContext ctx, ci = L->ci; api_checkpop(L, nresults); if (l_unlikely(!yieldable(L))) { - if (L != G(L)->mainthread) + if (L != mainthread(G(L))) luaG_runerror(L, "attempt to yield across a C-call boundary"); else luaG_runerror(L, "attempt to yield from outside a coroutine"); @@ -988,7 +1047,7 @@ LUA_API int lua_yieldk (lua_State *L, int nresults, lua_KContext ctx, */ struct CloseP { StkId level; - int status; + TStatus status; }; @@ -1005,7 +1064,7 @@ static void closepaux (lua_State *L, void *ud) { ** Calls 'luaF_close' in protected mode. Return the original status ** or, in case of errors, the new status. */ -int luaD_closeprotected (lua_State *L, ptrdiff_t level, int status) { +TStatus luaD_closeprotected (lua_State *L, ptrdiff_t level, TStatus status) { CallInfo *old_ci = L->ci; lu_byte old_allowhooks = L->allowhook; for (;;) { /* keep closing upvalues until no more errors */ @@ -1027,9 +1086,9 @@ int luaD_closeprotected (lua_State *L, ptrdiff_t level, int status) { ** thread information ('allowhook', etc.) and in particular ** its stack level in case of errors. */ -int luaD_pcall (lua_State *L, Pfunc func, void *u, - ptrdiff_t old_top, ptrdiff_t ef) { - int status; +TStatus luaD_pcall (lua_State *L, Pfunc func, void *u, ptrdiff_t old_top, + ptrdiff_t ef) { + TStatus status; CallInfo *old_ci = L->ci; lu_byte old_allowhooks = L->allowhook; ptrdiff_t old_errfunc = L->errfunc; @@ -1069,32 +1128,58 @@ static void checkmode (lua_State *L, const char *mode, const char *x) { } +/* +** Before the first call to the reader function, Lua reserves a slot +** with a table for anchoring stuff. +*/ static void f_parser (lua_State *L, void *ud) { LClosure *cl; struct SParser *p = cast(struct SParser *, ud); const char *mode = p->mode ? p->mode : "bt"; - int c = zgetc(p->z); /* read first character */ + int c; + Table *anchor; + ptrdiff_t otop = savestack(L, L->top.p); /* original top */ + luaD_checkstack(L, 2); + anchor = luaH_new(L); /* create the anchor table */ + sethvalue2s(L, L->top.p++, anchor); /* anchor the anchor table */ + c = zgetc(p->z); /* read first character */ if (c == LUA_SIGNATURE[0]) { int fixed = 0; if (strchr(mode, 'B') != NULL) fixed = 1; else checkmode(L, mode, "binary"); - cl = luaU_undump(L, p->z, p->name, fixed); + cl = luaU_undump(L, p->z, anchor, p->name, fixed); } else { checkmode(L, mode, "text"); - cl = luaY_parser(L, p->z, &p->buff, &p->dyd, p->name, c); + cl = luaY_parser(L, p->z, anchor, &p->buff, &p->dyd, p->name, c); } + L->top.p = restorestack(L, otop); /* restore stack */ + setclLvalue2s(L, L->top.p++, cl); /* push closure */ lua_assert(cl->nupvalues == cl->p->sizeupvalues); luaF_initupvals(L, cl); } -int luaD_protectedparser (lua_State *L, ZIO *z, const char *name, - const char *mode) { +/* +** Anchor an object in a table in the stack. First, anchor the object +** temporarily in the stack, as luaH_set may call an emergency GC. +** Then, add it in the table with itself as its key. +*/ +void luaD_anchorobj (lua_State *L, Table *anchor, GCObject *obj) { + setgcovalue(L, s2v(L->top.p++), obj); /* temporary anchor in the stack */ + luaH_set(L, anchor, s2v(L->top.p - 1), s2v(L->top.p - 1)); + /* Because this is a new key, luaH_set will call the GC barrier, so + we don't need to call the barrier again here */ + L->top.p--; +} + + +TStatus luaD_protectedparser (lua_State *L, ZIO *z, const char *name, + const char *mode) { struct SParser p; - int status; + TStatus status; incnny(L); /* cannot yield during parsing */ p.z = z; p.name = name; p.mode = mode; p.dyd.actvar.arr = NULL; p.dyd.actvar.size = 0; diff --git a/ldo.h b/ldo.h index b52a353fda..2b3b0db4cb 100644 --- a/ldo.h +++ b/ldo.h @@ -67,8 +67,10 @@ /* type of protected functions, to be ran by 'runprotected' */ typedef void (*Pfunc) (lua_State *L, void *ud); -LUAI_FUNC void luaD_seterrorobj (lua_State *L, int errcode, StkId oldtop); -LUAI_FUNC int luaD_protectedparser (lua_State *L, ZIO *z, const char *name, +LUAI_FUNC l_noret luaD_errerr (lua_State *L); +LUAI_FUNC void luaD_seterrorobj (lua_State *L, TStatus errcode, StkId oldtop); +LUAI_FUNC TStatus luaD_protectedparser (lua_State *L, ZIO *z, + const char *name, const char *mode); LUAI_FUNC void luaD_hook (lua_State *L, int event, int line, int fTransfer, int nTransfer); @@ -78,17 +80,21 @@ LUAI_FUNC int luaD_pretailcall (lua_State *L, CallInfo *ci, StkId func, LUAI_FUNC CallInfo *luaD_precall (lua_State *L, StkId func, int nResults); LUAI_FUNC void luaD_call (lua_State *L, StkId func, int nResults); LUAI_FUNC void luaD_callnoyield (lua_State *L, StkId func, int nResults); -LUAI_FUNC int luaD_closeprotected (lua_State *L, ptrdiff_t level, int status); -LUAI_FUNC int luaD_pcall (lua_State *L, Pfunc func, void *u, +LUAI_FUNC TStatus luaD_closeprotected (lua_State *L, ptrdiff_t level, + TStatus status); +LUAI_FUNC TStatus luaD_pcall (lua_State *L, Pfunc func, void *u, ptrdiff_t oldtop, ptrdiff_t ef); LUAI_FUNC void luaD_poscall (lua_State *L, CallInfo *ci, int nres); LUAI_FUNC int luaD_reallocstack (lua_State *L, int newsize, int raiseerror); LUAI_FUNC int luaD_growstack (lua_State *L, int n, int raiseerror); LUAI_FUNC void luaD_shrinkstack (lua_State *L); LUAI_FUNC void luaD_inctop (lua_State *L); +LUAI_FUNC int luaD_checkminstack (lua_State *L); +LUAI_FUNC void luaD_anchorobj (lua_State *L, Table *anchor, GCObject *obj); -LUAI_FUNC l_noret luaD_throw (lua_State *L, int errcode); -LUAI_FUNC int luaD_rawrunprotected (lua_State *L, Pfunc f, void *ud); +LUAI_FUNC l_noret luaD_throw (lua_State *L, TStatus errcode); +LUAI_FUNC l_noret luaD_throwbaselevel (lua_State *L, TStatus errcode); +LUAI_FUNC TStatus luaD_rawrunprotected (lua_State *L, Pfunc f, void *ud); #endif diff --git a/ldump.c b/ldump.c index 71d9a5b1c9..5795788922 100644 --- a/ldump.c +++ b/ldump.c @@ -31,7 +31,7 @@ typedef struct { int strip; int status; Table *h; /* table to track saved strings */ - lua_Integer nstr; /* counter for counting saved strings */ + lua_Unsigned nstr; /* counter for counting saved strings */ } DumpState; @@ -87,12 +87,12 @@ static void dumpByte (DumpState *D, int y) { ** size for 'dumpVarint' buffer: each byte can store up to 7 bits. ** (The "+6" rounds up the division.) */ -#define DIBS ((sizeof(size_t) * CHAR_BIT + 6) / 7) +#define DIBS ((l_numbits(lua_Unsigned) + 6) / 7) /* ** Dumps an unsigned integer using the MSB Varint encoding */ -static void dumpVarint (DumpState *D, size_t x) { +static void dumpVarint (DumpState *D, lua_Unsigned x) { lu_byte buff[DIBS]; unsigned n = 1; buff[DIBS - 1] = x & 0x7f; /* fill least-significant byte */ @@ -103,12 +103,13 @@ static void dumpVarint (DumpState *D, size_t x) { static void dumpSize (DumpState *D, size_t sz) { - dumpVarint(D, sz); + dumpVarint(D, cast(lua_Unsigned, sz)); } + static void dumpInt (DumpState *D, int x) { lua_assert(x >= 0); - dumpVarint(D, cast(size_t, x)); + dumpVarint(D, cast_uint(x)); } @@ -117,37 +118,49 @@ static void dumpNumber (DumpState *D, lua_Number x) { } +/* +** Signed integers are coded to keep small values small. (Coding -1 as +** 0xfff...fff would use too many bytes to save a quite common value.) +** A non-negative x is coded as 2x; a negative x is coded as -2x - 1. +** (0 => 0; -1 => 1; 1 => 2; -2 => 3; 2 => 4; ...) +*/ static void dumpInteger (DumpState *D, lua_Integer x) { - dumpVar(D, x); + lua_Unsigned cx = (x >= 0) ? 2u * l_castS2U(x) + : (2u * ~l_castS2U(x)) + 1; + dumpVarint(D, cx); } /* -** Dump a String. First dump its "size": size==0 means NULL; -** size==1 is followed by an index and means "reuse saved string with -** that index"; size>=2 is followed by the string contents with real -** size==size-2 and means that string, which will be saved with -** the next available index. +** Dump a String. First dump its "size": +** size==0 is followed by an index and means "reuse saved string with +** that index"; index==0 means NULL. +** size>=1 is followed by the string contents with real size==size-1 and +** means that string, which will be saved with the next available index. +** The real size does not include the ending '\0' (which is not dumped), +** so adding 1 to it cannot overflow a size_t. */ static void dumpString (DumpState *D, TString *ts) { - if (ts == NULL) - dumpSize(D, 0); + if (ts == NULL) { + dumpVarint(D, 0); /* will "reuse" NULL */ + dumpVarint(D, 0); /* special index for NULL */ + } else { TValue idx; int tag = luaH_getstr(D->h, ts, &idx); if (!tagisempty(tag)) { /* string already saved? */ - dumpSize(D, 1); /* reuse a saved string */ - dumpSize(D, cast_sizet(ivalue(&idx))); /* index of saved string */ + dumpVarint(D, 0); /* reuse a saved string */ + dumpVarint(D, l_castS2U(ivalue(&idx))); /* index of saved string */ } else { /* must write and save the string */ TValue key, value; /* to save the string in the hash */ size_t size; const char *s = getlstr(ts, size); - dumpSize(D, size + 2); + dumpSize(D, size + 1); dumpVector(D, s, size + 1); /* include ending '\0' */ D->nstr++; /* one more saved string */ setsvalue(D->L, &key, ts); /* the string is the key */ - setivalue(&value, D->nstr); /* its index is the value */ + setivalue(&value, l_castU2S(D->nstr)); /* its index is the value */ luaH_set(D->L, D->h, &key, &value); /* h[ts] = nstr */ /* integer value does not need barrier */ } @@ -253,16 +266,19 @@ static void dumpFunction (DumpState *D, const Proto *f) { } +#define dumpNumInfo(D, tvar, value) \ + { tvar i = value; dumpByte(D, sizeof(tvar)); dumpVar(D, i); } + + static void dumpHeader (DumpState *D) { dumpLiteral(D, LUA_SIGNATURE); dumpByte(D, LUAC_VERSION); dumpByte(D, LUAC_FORMAT); dumpLiteral(D, LUAC_DATA); - dumpByte(D, sizeof(Instruction)); - dumpByte(D, sizeof(lua_Integer)); - dumpByte(D, sizeof(lua_Number)); - dumpInteger(D, LUAC_INT); - dumpNumber(D, LUAC_NUM); + dumpNumInfo(D, int, LUAC_INT); + dumpNumInfo(D, Instruction, LUAC_INST); + dumpNumInfo(D, lua_Integer, LUAC_INT); + dumpNumInfo(D, lua_Number, LUAC_NUM); } diff --git a/lfunc.c b/lfunc.c index 0ea05e009a..b6fd9ceb55 100644 --- a/lfunc.c +++ b/lfunc.c @@ -100,21 +100,23 @@ UpVal *luaF_findupval (lua_State *L, StkId level) { /* -** Call closing method for object 'obj' with error message 'err'. The +** Call closing method for object 'obj' with error object 'err'. The ** boolean 'yy' controls whether the call is yieldable. ** (This function assumes EXTRA_STACK.) */ static void callclosemethod (lua_State *L, TValue *obj, TValue *err, int yy) { StkId top = L->top.p; + StkId func = top; const TValue *tm = luaT_gettmbyobj(L, obj, TM_CLOSE); - setobj2s(L, top, tm); /* will call metamethod... */ - setobj2s(L, top + 1, obj); /* with 'self' as the 1st argument */ - setobj2s(L, top + 2, err); /* and error msg. as 2nd argument */ - L->top.p = top + 3; /* add function and arguments */ + setobj2s(L, top++, tm); /* will call metamethod... */ + setobj2s(L, top++, obj); /* with 'self' as the 1st argument */ + if (err != NULL) /* if there was an error... */ + setobj2s(L, top++, err); /* then error object will be 2nd argument */ + L->top.p = top; /* add function and arguments */ if (yy) - luaD_call(L, top, 0); + luaD_call(L, func, 0); else - luaD_callnoyield(L, top, 0); + luaD_callnoyield(L, func, 0); } @@ -140,26 +142,28 @@ static void checkclosemth (lua_State *L, StkId level) { ** the 'level' of the upvalue being closed, as everything after that ** won't be used again. */ -static void prepcallclosemth (lua_State *L, StkId level, int status, int yy) { +static void prepcallclosemth (lua_State *L, StkId level, TStatus status, + int yy) { TValue *uv = s2v(level); /* value being closed */ TValue *errobj; - if (status == CLOSEKTOP) - errobj = &G(L)->nilvalue; /* error object is nil */ - else { /* 'luaD_seterrorobj' will set top to level + 2 */ - errobj = s2v(level + 1); /* error object goes after 'uv' */ - luaD_seterrorobj(L, status, level + 1); /* set error object */ + switch (status) { + case LUA_OK: + L->top.p = level + 1; /* call will be at this level */ + /* FALLTHROUGH */ + case CLOSEKTOP: /* don't need to change top */ + errobj = NULL; /* no error object */ + break; + default: /* 'luaD_seterrorobj' will set top to level + 2 */ + errobj = s2v(level + 1); /* error object goes after 'uv' */ + luaD_seterrorobj(L, status, level + 1); /* set error object */ + break; } callclosemethod(L, uv, errobj, yy); } -/* -** Maximum value for deltas in 'tbclist', dependent on the type -** of delta. (This macro assumes that an 'L' is in scope where it -** is used.) -*/ -#define MAXDELTA \ - ((256ul << ((sizeof(L->stack.p->tbclist.delta) - 1) * 8)) - 1) +/* Maximum value for deltas in 'tbclist' */ +#define MAXDELTA USHRT_MAX /* @@ -192,8 +196,7 @@ void luaF_unlinkupval (UpVal *uv) { */ void luaF_closeupval (lua_State *L, StkId level) { UpVal *uv; - StkId upl; /* stack index pointed by 'uv' */ - while ((uv = L->openupval) != NULL && (upl = uplevel(uv)) >= level) { + while ((uv = L->openupval) != NULL && uplevel(uv) >= level) { TValue *slot = &uv->u.value; /* new position for value */ lua_assert(uplevel(uv) < L->top.p); luaF_unlinkupval(uv); /* remove upvalue from 'openupval' list */ @@ -224,7 +227,7 @@ static void poptbclist (lua_State *L) { ** Close all upvalues and to-be-closed variables up to the given stack ** level. Return restored 'level'. */ -StkId luaF_close (lua_State *L, StkId level, int status, int yy) { +StkId luaF_close (lua_State *L, StkId level, TStatus status, int yy) { ptrdiff_t levelrel = savestack(L, level); luaF_closeupval(L, level); /* first, close the upvalues */ while (L->tbclist.p >= level) { /* traverse tbc's down to that level */ diff --git a/lfunc.h b/lfunc.h index 342389e48a..d6aad3a6df 100644 --- a/lfunc.h +++ b/lfunc.h @@ -44,7 +44,7 @@ /* special status to close upvalues preserving the top of the stack */ -#define CLOSEKTOP (-1) +#define CLOSEKTOP (LUA_ERRERR + 1) LUAI_FUNC Proto *luaF_newproto (lua_State *L); @@ -54,7 +54,7 @@ LUAI_FUNC void luaF_initupvals (lua_State *L, LClosure *cl); LUAI_FUNC UpVal *luaF_findupval (lua_State *L, StkId level); LUAI_FUNC void luaF_newtbcupval (lua_State *L, StkId level); LUAI_FUNC void luaF_closeupval (lua_State *L, StkId level); -LUAI_FUNC StkId luaF_close (lua_State *L, StkId level, int status, int yy); +LUAI_FUNC StkId luaF_close (lua_State *L, StkId level, TStatus status, int yy); LUAI_FUNC void luaF_unlinkupval (UpVal *uv); LUAI_FUNC lu_mem luaF_protosize (Proto *p); LUAI_FUNC void luaF_freeproto (lua_State *L, Proto *f); diff --git a/lgc.c b/lgc.c index 1e9f75698c..a463e41e6d 100644 --- a/lgc.c +++ b/lgc.c @@ -78,7 +78,7 @@ ((*getArrTag(t,i) & BIT_ISCOLLECTABLE) ? getArrVal(t,i)->gc : NULL) -#define markvalue(g,o) { checkliveness(g->mainthread,o); \ +#define markvalue(g,o) { checkliveness(mainthread(g),o); \ if (valiswhite(o)) reallymarkobject(g,gcvalue(o)); } #define markkey(g, n) { if keyiswhite(n) reallymarkobject(g,gckey(n)); } @@ -126,7 +126,6 @@ static l_mem objsize (GCObject *o) { CClosure *cl = gco2ccl(o); res = sizeCclosure(cl->nupvalues); break; - break; } case LUA_VUSERDATA: { Udata *u = gco2u(o); @@ -441,7 +440,7 @@ static void cleargraylists (global_State *g) { static void restartcollection (global_State *g) { cleargraylists(g); g->GCmarked = 0; - markobject(g, g->mainthread); + markobject(g, mainthread(g)); markvalue(g, &g->l_registry); markmt(g); markbeingfnz(g); /* mark any finalizing object left from previous cycle */ @@ -465,6 +464,8 @@ static void restartcollection (global_State *g) { ** TOUCHED1 objects need to be in the list. TOUCHED2 doesn't need to go ** back to a gray list, but then it must become OLD. (That is what ** 'correctgraylist' does when it finds a TOUCHED2 object.) +** This function is a no-op in incremental mode, as objects cannot be +** marked as touched in that mode. */ static void genlink (global_State *g, GCObject *o) { lua_assert(isblack(o)); @@ -480,7 +481,8 @@ static void genlink (global_State *g, GCObject *o) { ** Traverse a table with weak values and link it to proper list. During ** propagate phase, keep it in 'grayagain' list, to be revisited in the ** atomic phase. In the atomic phase, if table has any white value, -** put it in 'weak' list, to be cleared. +** put it in 'weak' list, to be cleared; otherwise, call 'genlink' +** to check table age in generational mode. */ static void traverseweakvalue (global_State *g, Table *h) { Node *n, *limit = gnodelast(h); @@ -497,10 +499,12 @@ static void traverseweakvalue (global_State *g, Table *h) { hasclears = 1; /* table will have to be cleared */ } } - if (g->gcstate == GCSatomic && hasclears) - linkgclist(h, g->weak); /* has to be cleared later */ - else + if (g->gcstate == GCSpropagate) linkgclist(h, g->grayagain); /* must retraverse it in atomic phase */ + else if (hasclears) + linkgclist(h, g->weak); /* has to be cleared later */ + else + genlink(g, obj2gco(h)); } @@ -585,26 +589,42 @@ static void traversestrongtable (global_State *g, Table *h) { } -static l_mem traversetable (global_State *g, Table *h) { - const char *weakkey, *weakvalue; +/* +** (result & 1) iff weak values; (result & 2) iff weak keys. +*/ +static int getmode (global_State *g, Table *h) { const TValue *mode = gfasttm(g, h->metatable, TM_MODE); - TString *smode; + if (mode == NULL || !ttisstring(mode)) + return 0; /* ignore non-string modes */ + else { + const char *smode = getstr(tsvalue(mode)); + const char *weakkey = strchr(smode, 'k'); + const char *weakvalue = strchr(smode, 'v'); + return ((weakkey != NULL) << 1) | (weakvalue != NULL); + } +} + + +static l_mem traversetable (global_State *g, Table *h) { markobjectN(g, h->metatable); - if (mode && ttisshrstring(mode) && /* is there a weak mode? */ - (cast_void(smode = tsvalue(mode)), - cast_void(weakkey = strchr(getshrstr(smode), 'k')), - cast_void(weakvalue = strchr(getshrstr(smode), 'v')), - (weakkey || weakvalue))) { /* is really weak? */ - if (!weakkey) /* strong keys? */ + switch (getmode(g, h)) { + case 0: /* not weak */ + traversestrongtable(g, h); + break; + case 1: /* weak values */ traverseweakvalue(g, h); - else if (!weakvalue) /* strong values? */ + break; + case 2: /* weak keys */ traverseephemeron(g, h, 0); - else /* all weak */ - linkgclist(h, g->allweak); /* nothing to traverse now */ + break; + case 3: /* all weak; nothing to traverse */ + if (g->gcstate == GCSpropagate) + linkgclist(h, g->grayagain); /* must visit again its metatable */ + else + linkgclist(h, g->allweak); /* must clear collected entries */ + break; } - else /* not weak */ - traversestrongtable(g, h); - return 1 + 2*sizenode(h) + h->asize; + return cast(l_mem, 1 + 2*sizenode(h) + h->asize); } @@ -689,7 +709,7 @@ static l_mem traversethread (global_State *g, lua_State *th) { if (!g->gcemergency) luaD_shrinkstack(th); /* do not change stack in emergency cycle */ for (o = th->top.p; o < th->stack_last.p + EXTRA_STACK; o++) - setnilvalue(s2v(o)); /* clear dead stack slice */ + setnilvalue2s(o); /* clear dead stack slice */ /* 'remarkupvals' may have removed thread from 'twups' list */ if (!isintwups(th) && th->openupval != NULL) { th->twups = g->twups; /* link it back to the list */ @@ -953,7 +973,7 @@ static void GCTM (lua_State *L) { setgcovalue(L, &v, udata2finalize(g)); tm = luaT_gettmbyobj(L, &v, TM_GC); if (!notm(tm)) { /* is there a finalizer? */ - int status; + TStatus status; lu_byte oldah = L->allowhook; lu_byte oldgcstp = g->gcstp; g->gcstp |= GCSTPGC; /* avoid GC steps */ @@ -1273,7 +1293,7 @@ static void finishgencycle (lua_State *L, global_State *g) { correctgraylists(g); checkSizes(L, g); g->gcstate = GCSpropagate; /* skip restart */ - if (!g->gcemergency) + if (g->tobefnz != NULL && !g->gcemergency && luaD_checkminstack(L)) callallpendingfinalizers(L); } @@ -1513,7 +1533,7 @@ void luaC_freeallobjects (lua_State *L) { separatetobefnz(g, 1); /* separate all objects with finalizers */ lua_assert(g->finobj == NULL); callallpendingfinalizers(L); - deletelist(L, g->allgc, obj2gco(g->mainthread)); + deletelist(L, g->allgc, obj2gco(mainthread(g))); lua_assert(g->finobj == NULL); /* no new finalizers */ deletelist(L, g->fixedgc, NULL); /* collect fixed objects */ lua_assert(g->strt.nuse == 0); @@ -1526,7 +1546,7 @@ static void atomic (lua_State *L) { GCObject *grayagain = g->grayagain; /* save original list */ g->grayagain = NULL; lua_assert(g->ephemeron == NULL && g->weak == NULL); - lua_assert(!iswhite(g->mainthread)); + lua_assert(!iswhite(mainthread(g))); g->gcstate = GCSatomic; markobject(g, L); /* mark running thread */ /* registry and global metatables may be changed by API */ @@ -1647,12 +1667,13 @@ static l_mem singlestep (lua_State *L, int fast) { break; } case GCScallfin: { /* call finalizers */ - if (g->tobefnz && !g->gcemergency) { + if (g->tobefnz && !g->gcemergency && luaD_checkminstack(L)) { g->gcstopem = 0; /* ok collections during finalizers */ GCTM(L); /* call one finalizer */ stepresult = CWUFIN; } - else { /* emergency mode or no more finalizers */ + else { /* no more finalizers or emergency mode or not enough stack + to run finalizers */ g->gcstate = GCSpause; /* finish collection */ stepresult = step2pause; } diff --git a/liolib.c b/liolib.c index a0988db06a..57615e6f32 100644 --- a/liolib.c +++ b/liolib.c @@ -114,7 +114,7 @@ static int l_checkmode (const char *mode) { #if !defined(l_fseek) /* { */ -#if defined(LUA_USE_POSIX) /* { */ +#if defined(LUA_USE_POSIX) || defined(LUA_USE_OFF_T) /* { */ #include @@ -662,11 +662,12 @@ static int io_readline (lua_State *L) { static int g_write (lua_State *L, FILE *f, int arg) { int nargs = lua_gettop(L) - arg; - int status = 1; + size_t totalbytes = 0; /* total number of bytes written */ errno = 0; - for (; nargs--; arg++) { + for (; nargs--; arg++) { /* for each argument */ char buff[LUA_N2SBUFFSZ]; const char *s; + size_t numbytes; /* bytes written in one call to 'fwrite' */ size_t len = lua_numbertocstring(L, arg, buff); /* try as a number */ if (len > 0) { /* did conversion work (value was a number)? */ s = buff; @@ -674,12 +675,15 @@ static int g_write (lua_State *L, FILE *f, int arg) { } else /* must be a string */ s = luaL_checklstring(L, arg, &len); - status = status && (fwrite(s, sizeof(char), len, f) == len); + numbytes = fwrite(s, sizeof(char), len, f); + totalbytes += numbytes; + if (numbytes < len) { /* write error? */ + int n = luaL_fileresult(L, 0, NULL); + lua_pushinteger(L, cast_st2S(totalbytes)); + return n + 1; /* return fail, error msg., error code, and counter */ + } } - if (l_likely(status)) - return 1; /* file handle already on stack top */ - else - return luaL_fileresult(L, status, NULL); + return 1; /* no errors; file handle already on stack top */ } @@ -728,18 +732,19 @@ static int f_setvbuf (lua_State *L) { } - -static int io_flush (lua_State *L) { - FILE *f = getiofile(L, IO_OUTPUT); +static int aux_flush (lua_State *L, FILE *f) { errno = 0; return luaL_fileresult(L, fflush(f) == 0, NULL); } static int f_flush (lua_State *L) { - FILE *f = tofile(L); - errno = 0; - return luaL_fileresult(L, fflush(f) == 0, NULL); + return aux_flush(L, tofile(L)); +} + + +static int io_flush (lua_State *L) { + return aux_flush(L, getiofile(L, IO_OUTPUT)); } diff --git a/ljumptab.h b/ljumptab.h index 8306f250cc..52fa6d746e 100644 --- a/ljumptab.h +++ b/ljumptab.h @@ -21,7 +21,7 @@ static const void *const disptab[NUM_OPCODES] = { #if 0 ** you can update the following list with this command: ** -** sed -n '/^OP_/\!d; s/OP_/\&\&L_OP_/ ; s/,.*/,/ ; s/\/.*// ; p' lopcodes.h +** sed -n '/^OP_/!d; s/OP_/\&\&L_OP_/ ; s/,.*/,/ ; s/\/.*// ; p' lopcodes.h ** #endif @@ -57,8 +57,8 @@ static const void *const disptab[NUM_OPCODES] = { &&L_OP_BANDK, &&L_OP_BORK, &&L_OP_BXORK, -&&L_OP_SHRI, &&L_OP_SHLI, +&&L_OP_SHRI, &&L_OP_ADD, &&L_OP_SUB, &&L_OP_MUL, @@ -106,6 +106,8 @@ static const void *const disptab[NUM_OPCODES] = { &&L_OP_SETLIST, &&L_OP_CLOSURE, &&L_OP_VARARG, +&&L_OP_GETVARG, +&&L_OP_ERRNNIL, &&L_OP_VARARGPREP, &&L_OP_EXTRAARG diff --git a/llex.c b/llex.c index 1c4227ca4c..7cd9fcaf52 100644 --- a/llex.c +++ b/llex.c @@ -44,7 +44,7 @@ /* ORDER RESERVED */ static const char *const luaX_tokens [] = { "and", "break", "do", "else", "elseif", - "end", "false", "for", "function", "goto", "if", + "end", "false", "for", "function", "global", "goto", "if", "in", "local", "nil", "not", "or", "repeat", "return", "then", "true", "until", "while", "//", "..", "...", "==", ">=", "<=", "~=", @@ -62,10 +62,10 @@ static l_noret lexerror (LexState *ls, const char *msg, int token); static void save (LexState *ls, int c) { Mbuffer *b = ls->buff; if (luaZ_bufflen(b) + 1 > luaZ_sizebuffer(b)) { - size_t newsize; - if (luaZ_sizebuffer(b) >= MAX_SIZE/2) + size_t newsize = luaZ_sizebuffer(b); /* get old size */; + if (newsize >= (MAX_SIZE/3 * 2)) /* larger than MAX_SIZE/1.5 ? */ lexerror(ls, "lexical element too long", 0); - newsize = luaZ_sizebuffer(b) * 2; + newsize += (newsize >> 1); /* new size is 1.5 times the old one */ luaZ_resizebuffer(ls->L, b, newsize); } b->buffer[luaZ_bufflen(b)++] = cast_char(c); @@ -184,7 +184,15 @@ void luaX_setinput (lua_State *L, LexState *ls, ZIO *z, TString *source, ls->linenumber = 1; ls->lastline = 1; ls->source = source; - ls->envn = luaS_newliteral(L, LUA_ENV); /* get env name */ + /* all three strings here ("_ENV", "break", "global") were fixed, + so they cannot be collected */ + ls->envn = luaS_newliteral(L, LUA_ENV); /* get env string */ + ls->brkn = luaS_newliteral(L, "break"); /* get "break" string */ +#if LUA_COMPAT_GLOBAL + /* compatibility mode: "global" is not a reserved word */ + ls->glbn = luaS_newliteral(L, "global"); /* get "global" string */ + ls->glbn->extra = 0; /* mark it as not reserved */ +#endif luaZ_resizebuffer(ls->L, ls->buff, LUA_MINBUFFER); /* initialize buffer */ } @@ -354,12 +362,12 @@ static int readhexaesc (LexState *ls) { ** for error reporting in case of errors; 'i' counts the number of ** saved characters, so that they can be removed if case of success. */ -static unsigned long readutf8esc (LexState *ls) { - unsigned long r; +static l_uint32 readutf8esc (LexState *ls) { + l_uint32 r; int i = 4; /* number of chars to be removed: start with #"\u{X" */ save_and_next(ls); /* skip 'u' */ esccheck(ls, ls->current == '{', "missing '{'"); - r = cast_ulong(gethexa(ls)); /* must have at least one digit */ + r = cast_uint(gethexa(ls)); /* must have at least one digit */ while (cast_void(save_and_next(ls)), lisxdigit(ls->current)) { i++; esccheck(ls, r <= (0x7FFFFFFFu >> 4), "UTF-8 value too large"); diff --git a/llex.h b/llex.h index c3500ef6a8..37016e8a3f 100644 --- a/llex.h +++ b/llex.h @@ -33,8 +33,8 @@ enum RESERVED { /* terminal symbols denoted by reserved words */ TK_AND = FIRST_RESERVED, TK_BREAK, TK_DO, TK_ELSE, TK_ELSEIF, TK_END, TK_FALSE, TK_FOR, TK_FUNCTION, - TK_GOTO, TK_IF, TK_IN, TK_LOCAL, TK_NIL, TK_NOT, TK_OR, TK_REPEAT, - TK_RETURN, TK_THEN, TK_TRUE, TK_UNTIL, TK_WHILE, + TK_GLOBAL, TK_GOTO, TK_IF, TK_IN, TK_LOCAL, TK_NIL, TK_NOT, TK_OR, + TK_REPEAT, TK_RETURN, TK_THEN, TK_TRUE, TK_UNTIL, TK_WHILE, /* other terminal symbols */ TK_IDIV, TK_CONCAT, TK_DOTS, TK_EQ, TK_GE, TK_LE, TK_NE, TK_SHL, TK_SHR, @@ -75,6 +75,8 @@ typedef struct LexState { struct Dyndata *dyd; /* dynamic structures used by the parser */ TString *source; /* current source name */ TString *envn; /* environment variable name */ + TString *brkn; /* "break" name (used as a label) */ + TString *glbn; /* "global" name (when not a reserved word) */ } LexState; diff --git a/llimits.h b/llimits.h index d98171ae6b..3f0372552a 100644 --- a/llimits.h +++ b/llimits.h @@ -15,11 +15,13 @@ #include "lua.h" +#define l_numbits(t) cast_int(sizeof(t) * CHAR_BIT) + /* ** 'l_mem' is a signed integer big enough to count the total memory ** used by Lua. (It is signed due to the use of debt in several -** computations.) Usually, 'ptrdiff_t' should work, but we use 'long' -** for 16-bit machines. +** computations.) 'lu_mem' is a corresponding unsigned type. Usually, +** 'ptrdiff_t' should work, but we use 'long' for 16-bit machines. */ #if defined(LUAI_MEM) /* { external definitions? */ typedef LUAI_MEM l_mem; @@ -33,7 +35,7 @@ typedef unsigned long lu_mem; #endif /* } */ #define MAX_LMEM \ - cast(l_mem, (cast(lu_mem, 1) << (sizeof(l_mem) * 8 - 1)) - 1) + cast(l_mem, (cast(lu_mem, 1) << (l_numbits(l_mem) - 1)) - 1) /* chars used as small naturals (so that 'char' is reserved for characters) */ @@ -41,6 +43,12 @@ typedef unsigned char lu_byte; typedef signed char ls_byte; +/* Type for thread status/error codes */ +typedef lu_byte TStatus; + +/* The C API still uses 'int' for status/error codes */ +#define APIstatus(st) cast_int(st) + /* maximum value for size_t */ #define MAX_SIZET ((size_t)(~(size_t)0)) @@ -51,13 +59,6 @@ typedef signed char ls_byte; #define MAX_SIZE (sizeof(size_t) < sizeof(lua_Integer) ? MAX_SIZET \ : cast_sizet(LUA_MAXINTEGER)) -/* -** floor of the log2 of the maximum signed value for integral type 't'. -** (That is, maximum 'n' such that '2^n' fits in the given signed type.) -*/ -#define log2maxs(t) cast_int(sizeof(t) * 8 - 2) - - /* ** test whether an unsigned value is a power of 2 (or zero) */ @@ -129,13 +130,15 @@ typedef LUAI_UACINT l_uacInt; #define cast_voidp(i) cast(void *, (i)) #define cast_num(i) cast(lua_Number, (i)) #define cast_int(i) cast(int, (i)) +#define cast_short(i) cast(short, (i)) #define cast_uint(i) cast(unsigned int, (i)) -#define cast_ulong(i) cast(unsigned long, (i)) #define cast_byte(i) cast(lu_byte, (i)) #define cast_uchar(i) cast(unsigned char, (i)) #define cast_char(i) cast(char, (i)) #define cast_charp(i) cast(char *, (i)) #define cast_sizet(i) cast(size_t, (i)) +#define cast_Integer(i) cast(lua_Integer, (i)) +#define cast_Inst(i) cast(Instruction, (i)) /* cast a signed lua_Integer to lua_Unsigned */ @@ -231,12 +234,12 @@ typedef unsigned long l_uint32; /* floor division (defined as 'floor(a/b)') */ #if !defined(luai_numidiv) -#define luai_numidiv(L,a,b) ((void)L, l_floor(luai_numdiv(L,a,b))) +#define luai_numidiv(L,a,b) l_floor(luai_numdiv(L,a,b)) #endif /* float division */ #if !defined(luai_numdiv) -#define luai_numdiv(L,a,b) ((a)/(b)) +#define luai_numdiv(L,a,b) ((void)L, (a)/(b)) #endif /* @@ -264,10 +267,10 @@ typedef unsigned long l_uint32; /* the others are quite standard operations */ #if !defined(luai_numadd) -#define luai_numadd(L,a,b) ((a)+(b)) -#define luai_numsub(L,a,b) ((a)-(b)) -#define luai_nummul(L,a,b) ((a)*(b)) -#define luai_numunm(L,a) (-(a)) +#define luai_numadd(L,a,b) ((void)L, (a)+(b)) +#define luai_numsub(L,a,b) ((void)L, (a)-(b)) +#define luai_nummul(L,a,b) ((void)L, (a)*(b)) +#define luai_numunm(L,a) ((void)L, -(a)) #define luai_numeq(a,b) ((a)==(b)) #define luai_numlt(a,b) ((a)<(b)) #define luai_numle(a,b) ((a)<=(b)) @@ -277,6 +280,55 @@ typedef unsigned long l_uint32; #endif + +/* +** lua_numbertointeger converts a float number with an integral value +** to an integer, or returns 0 if the float is not within the range of +** a lua_Integer. (The range comparisons are tricky because of +** rounding. The tests here assume a two-complement representation, +** where MININTEGER always has an exact representation as a float; +** MAXINTEGER may not have one, and therefore its conversion to float +** may have an ill-defined value.) +*/ +#define lua_numbertointeger(n,p) \ + ((n) >= (LUA_NUMBER)(LUA_MININTEGER) && \ + (n) < -(LUA_NUMBER)(LUA_MININTEGER) && \ + (*(p) = (LUA_INTEGER)(n), 1)) + + + +/* +** LUAI_FUNC is a mark for all extern functions that are not to be +** exported to outside modules. +** LUAI_DDEF and LUAI_DDEC are marks for all extern (const) variables, +** none of which to be exported to outside modules (LUAI_DDEF for +** definitions and LUAI_DDEC for declarations). +** Elf and MACH/gcc (versions 3.2 and later) mark them as "hidden" to +** optimize access when Lua is compiled as a shared library. Not all elf +** targets support this attribute. Unfortunately, gcc does not offer +** a way to check whether the target offers that support, and those +** without support give a warning about it. To avoid these warnings, +** change to the default definition. +*/ +#if !defined(LUAI_FUNC) + +#if defined(__GNUC__) && ((__GNUC__*100 + __GNUC_MINOR__) >= 302) && \ + (defined(__ELF__) || defined(__MACH__)) +#define LUAI_FUNC __attribute__((visibility("internal"))) extern +#else +#define LUAI_FUNC extern +#endif + +#define LUAI_DDEC(dec) LUAI_FUNC dec +#define LUAI_DDEF /* empty */ + +#endif + + +/* Give these macros simpler names for internal use */ +#define l_likely(x) luai_likely(x) +#define l_unlikely(x) luai_unlikely(x) + /* ** {================================================================== ** "Abstraction Layer" for basic report of messages and errors diff --git a/lmathlib.c b/lmathlib.c index c7418e69ec..a6b13f969c 100644 --- a/lmathlib.c +++ b/lmathlib.c @@ -38,31 +38,37 @@ static int math_abs (lua_State *L) { return 1; } + static int math_sin (lua_State *L) { lua_pushnumber(L, l_mathop(sin)(luaL_checknumber(L, 1))); return 1; } + static int math_cos (lua_State *L) { lua_pushnumber(L, l_mathop(cos)(luaL_checknumber(L, 1))); return 1; } + static int math_tan (lua_State *L) { lua_pushnumber(L, l_mathop(tan)(luaL_checknumber(L, 1))); return 1; } + static int math_asin (lua_State *L) { lua_pushnumber(L, l_mathop(asin)(luaL_checknumber(L, 1))); return 1; } + static int math_acos (lua_State *L) { lua_pushnumber(L, l_mathop(acos)(luaL_checknumber(L, 1))); return 1; } + static int math_atan (lua_State *L) { lua_Number y = luaL_checknumber(L, 1); lua_Number x = luaL_optnumber(L, 2, 1); @@ -167,6 +173,7 @@ static int math_ult (lua_State *L) { return 1; } + static int math_log (lua_State *L) { lua_Number x = luaL_checknumber(L, 1); lua_Number res; @@ -188,22 +195,42 @@ static int math_log (lua_State *L) { return 1; } + static int math_exp (lua_State *L) { lua_pushnumber(L, l_mathop(exp)(luaL_checknumber(L, 1))); return 1; } + static int math_deg (lua_State *L) { lua_pushnumber(L, luaL_checknumber(L, 1) * (l_mathop(180.0) / PI)); return 1; } + static int math_rad (lua_State *L) { lua_pushnumber(L, luaL_checknumber(L, 1) * (PI / l_mathop(180.0))); return 1; } +static int math_frexp (lua_State *L) { + lua_Number x = luaL_checknumber(L, 1); + int ep; + lua_pushnumber(L, l_mathop(frexp)(x, &ep)); + lua_pushinteger(L, ep); + return 2; +} + + +static int math_ldexp (lua_State *L) { + lua_Number x = luaL_checknumber(L, 1); + int ep = (int)luaL_checkinteger(L, 2); + lua_pushnumber(L, l_mathop(ldexp)(x, ep)); + return 1; +} + + static int math_min (lua_State *L) { int n = lua_gettop(L); /* number of arguments */ int imin = 1; /* index of current minimum value */ @@ -251,7 +278,7 @@ static int math_type (lua_State *L) { */ /* -** This code uses lots of shifts. ANSI C does not allow shifts greater +** This code uses lots of shifts. ISO C does not allow shifts greater ** than or equal to the width of the type being shifted, so some shifts ** are written in convoluted ways to match that restriction. For ** preprocessor tests, it assumes a width of 32 bits, so the maximum @@ -533,7 +560,7 @@ typedef struct { ** Project the random integer 'ran' into the interval [0, n]. ** Because 'ran' has 2^B possible values, the projection can only be ** uniform when the size of the interval is a power of 2 (exact -** division). Otherwise, to get a uniform projection into [0, n], we +** division). So, to get a uniform projection into [0, n], we ** first compute 'lim', the smallest Mersenne number not smaller than ** 'n'. We then project 'ran' into the interval [0, lim]. If the result ** is inside [0, n], we are done. Otherwise, we try with another 'ran', @@ -541,26 +568,14 @@ typedef struct { */ static lua_Unsigned project (lua_Unsigned ran, lua_Unsigned n, RanState *state) { - if ((n & (n + 1)) == 0) /* is 'n + 1' a power of 2? */ - return ran & n; /* no bias */ - else { - lua_Unsigned lim = n; - /* compute the smallest (2^b - 1) not smaller than 'n' */ - lim |= (lim >> 1); - lim |= (lim >> 2); - lim |= (lim >> 4); - lim |= (lim >> 8); - lim |= (lim >> 16); -#if (LUA_MAXUNSIGNED >> 31) >= 3 - lim |= (lim >> 32); /* integer type has more than 32 bits */ -#endif - lua_assert((lim & (lim + 1)) == 0 /* 'lim + 1' is a power of 2, */ - && lim >= n /* not smaller than 'n', */ - && (lim >> 1) < n); /* and it is the smallest one */ - while ((ran &= lim) > n) /* project 'ran' into [0..lim] */ - ran = I2UInt(nextrand(state->s)); /* not inside [0..n]? try again */ - return ran; - } + lua_Unsigned lim = n; /* to compute the Mersenne number */ + int sh; /* how much to spread bits to the right in 'lim' */ + /* spread '1' bits in 'lim' until it becomes a Mersenne number */ + for (sh = 1; (lim & (lim + 1)) != 0; sh *= 2) + lim |= (lim >> sh); /* spread '1's to the right */ + while ((ran &= lim) > n) /* project 'ran' into [0..lim] and test */ + ran = I2UInt(nextrand(state->s)); /* not inside [0..n]? try again */ + return ran; } @@ -593,8 +608,8 @@ static int math_random (lua_State *L) { /* random integer in the interval [low, up] */ luaL_argcheck(L, low <= up, 1, "interval is empty"); /* project random integer into the interval [0, up - low] */ - p = project(I2UInt(rv), (lua_Unsigned)up - (lua_Unsigned)low, state); - lua_pushinteger(L, l_castU2S(p) + low); + p = project(I2UInt(rv), l_castS2U(up) - l_castS2U(low), state); + lua_pushinteger(L, l_castU2S(p + l_castS2U(low))); return 1; } @@ -678,20 +693,6 @@ static int math_pow (lua_State *L) { return 1; } -static int math_frexp (lua_State *L) { - int e; - lua_pushnumber(L, l_mathop(frexp)(luaL_checknumber(L, 1), &e)); - lua_pushinteger(L, e); - return 2; -} - -static int math_ldexp (lua_State *L) { - lua_Number x = luaL_checknumber(L, 1); - int ep = (int)luaL_checkinteger(L, 2); - lua_pushnumber(L, l_mathop(ldexp)(x, ep)); - return 1; -} - static int math_log10 (lua_State *L) { lua_pushnumber(L, l_mathop(log10)(luaL_checknumber(L, 1))); return 1; @@ -714,7 +715,9 @@ static const luaL_Reg mathlib[] = { {"tointeger", math_toint}, {"floor", math_floor}, {"fmod", math_fmod}, + {"frexp", math_frexp}, {"ult", math_ult}, + {"ldexp", math_ldexp}, {"log", math_log}, {"max", math_max}, {"min", math_min}, @@ -730,8 +733,6 @@ static const luaL_Reg mathlib[] = { {"sinh", math_sinh}, {"tanh", math_tanh}, {"pow", math_pow}, - {"frexp", math_frexp}, - {"ldexp", math_ldexp}, {"log10", math_log10}, #endif /* placeholders */ diff --git a/lmem.h b/lmem.h index 083585920d..dc714fb2e4 100644 --- a/lmem.h +++ b/lmem.h @@ -57,7 +57,8 @@ #define luaM_freearray(L, b, n) luaM_free_(L, (b), (n)*sizeof(*(b))) #define luaM_new(L,t) cast(t*, luaM_malloc_(L, sizeof(t), 0)) -#define luaM_newvector(L,n,t) cast(t*, luaM_malloc_(L, (n)*sizeof(t), 0)) +#define luaM_newvector(L,n,t) \ + cast(t*, luaM_malloc_(L, cast_sizet(n)*sizeof(t), 0)) #define luaM_newvectorchecked(L,n,t) \ (luaM_checksize(L,n,sizeof(t)), luaM_newvector(L,n,t)) diff --git a/loadlib.c b/loadlib.c index 5f0c170296..ef09c9a546 100644 --- a/loadlib.c +++ b/loadlib.c @@ -306,6 +306,16 @@ static void setpath (lua_State *L, const char *fieldname, /* }================================================================== */ +/* +** External strings created by DLLs may need the DLL code to be +** deallocated. This implies that a DLL can only be unloaded after all +** its strings were deallocated. To ensure that, we create a 'library +** string' to represent each DLL, and when this string is deallocated +** it closes its corresponding DLL. +** (The string itself is irrelevant; its userdata is the DLL pointer.) +*/ + + /* ** return registry.CLIBS[path] */ @@ -320,34 +330,41 @@ static void *checkclib (lua_State *L, const char *path) { /* -** registry.CLIBS[path] = plib -- for queries -** registry.CLIBS[#CLIBS + 1] = plib -- also keep a list of all libraries +** Deallocate function for library strings. +** Unload the DLL associated with the string being deallocated. */ -static void addtoclib (lua_State *L, const char *path, void *plib) { - lua_getfield(L, LUA_REGISTRYINDEX, CLIBS); - lua_pushlightuserdata(L, plib); - lua_pushvalue(L, -1); - lua_setfield(L, -3, path); /* CLIBS[path] = plib */ - lua_rawseti(L, -2, luaL_len(L, -2) + 1); /* CLIBS[#CLIBS + 1] = plib */ - lua_pop(L, 1); /* pop CLIBS table */ +static void *freelib (void *ud, void *ptr, size_t osize, size_t nsize) { + /* string itself is irrelevant and static */ + (void)ptr; (void)osize; (void)nsize; + lsys_unloadlib(ud); /* unload library represented by the string */ + return NULL; } /* -** __gc tag method for CLIBS table: calls 'lsys_unloadlib' for all lib -** handles in list CLIBS +** Create a library string that, when deallocated, will unload 'plib' */ -static int gctm (lua_State *L) { - lua_Integer n = luaL_len(L, 1); - for (; n >= 1; n--) { /* for each handle, in reverse order */ - lua_rawgeti(L, 1, n); /* get handle CLIBS[n] */ - lsys_unloadlib(lua_touserdata(L, -1)); - lua_pop(L, 1); /* pop handle */ - } - return 0; +static void createlibstr (lua_State *L, void *plib) { + /* common content for all library strings */ + static const char dummy[] = "01234567890"; + lua_pushexternalstring(L, dummy, sizeof(dummy) - 1, freelib, plib); } +/* +** registry.CLIBS[path] = plib -- for queries. +** Also create a reference to strlib, so that the library string will +** only be collected when registry.CLIBS is collected. +*/ +static void addtoclib (lua_State *L, const char *path, void *plib) { + lua_getfield(L, LUA_REGISTRYINDEX, CLIBS); + lua_pushlightuserdata(L, plib); + lua_setfield(L, -2, path); /* CLIBS[path] = plib */ + createlibstr(L, plib); + luaL_ref(L, -2); /* keep library string in CLIBS */ + lua_pop(L, 1); /* pop CLIBS table */ +} + /* error codes for 'lookforfunc' */ #define ERRLIB 1 @@ -361,8 +378,8 @@ static int gctm (lua_State *L) { ** Then, if 'sym' is '*', return true (as library has been loaded). ** Otherwise, look for symbol 'sym' in the library and push a ** C function with that symbol. -** Return 0 and 'true' or a function in the stack; in case of -** errors, return an error code and an error message in the stack. +** Return 0 with 'true' or a function in the stack; in case of +** errors, return an error code with an error message in the stack. */ static int lookforfunc (lua_State *L, const char *path, const char *sym) { void *reg = checkclib(L, path); /* check loaded C libraries */ @@ -524,7 +541,7 @@ static int searcher_Lua (lua_State *L) { const char *name = luaL_checkstring(L, 1); filename = findfile(L, name, "path", LUA_LSUBSEP); if (filename == NULL) return 1; /* module not found in this path */ - return checkload(L, (luaL_loadfile(L, filename) == LUA_OK), filename); + return checkload(L, (luaL_loadfilex(L, filename, "bt") == LUA_OK), filename); } @@ -704,21 +721,9 @@ static void createsearcherstable (lua_State *L) { } -/* -** create table CLIBS to keep track of loaded C libraries, -** setting a finalizer to close all libraries when closing state. -*/ -static void createclibstable (lua_State *L) { - luaL_getsubtable(L, LUA_REGISTRYINDEX, CLIBS); /* create CLIBS table */ - lua_createtable(L, 0, 1); /* create metatable for CLIBS */ - lua_pushcfunction(L, gctm); - lua_setfield(L, -2, "__gc"); /* set finalizer for CLIBS table */ - lua_setmetatable(L, -2); -} - - LUAMOD_API int luaopen_package (lua_State *L) { - createclibstable(L); + luaL_getsubtable(L, LUA_REGISTRYINDEX, CLIBS); /* create CLIBS table */ + lua_pop(L, 1); /* will not use it now */ luaL_newlib(L, pk_funcs); /* create 'package' table */ createsearcherstable(L); /* set paths */ diff --git a/lobject.c b/lobject.c index c0fd182f13..763b484609 100644 --- a/lobject.c +++ b/lobject.c @@ -31,7 +31,8 @@ /* -** Computes ceil(log2(x)) +** Computes ceil(log2(x)), which is the smallest integer n such that +** x <= (1 << n). */ lu_byte luaO_ceillog2 (unsigned int x) { static const lu_byte log_2[256] = { /* log_2[i - 1] = ceil(log2(i)) */ @@ -86,7 +87,7 @@ lu_byte luaO_codeparam (unsigned int p) { ** overflow, so we check which order is best. */ l_mem luaO_applyparam (lu_byte p, l_mem x) { - unsigned int m = p & 0xF; /* mantissa */ + int m = p & 0xF; /* mantissa */ int e = (p >> 4); /* exponent */ if (e > 0) { /* normalized? */ e--; /* correct exponent */ @@ -247,7 +248,7 @@ static lua_Number lua_strx2number (const char *s, char **endptr) { nosigdig++; else if (++sigdig <= MAXSIGDIG) /* can read it without overflow? */ r = (r * l_mathop(16.0)) + luaO_hexavalue(*s); - else e++; /* too many digits; ignore, but still count for exponent */ + else e++; /* too many digits; ignore, but still count for exponent */ if (hasdot) e--; /* decimal digit? correct exponent */ } else break; /* neither a dot nor a digit */ @@ -382,10 +383,10 @@ size_t luaO_str2num (const char *s, TValue *o) { } -int luaO_utf8esc (char *buff, unsigned long x) { +int luaO_utf8esc (char *buff, l_uint32 x) { int n = 1; /* number of bytes put in buffer (backwards) */ lua_assert(x <= 0x7FFFFFFFu); - if (x < 0x80) /* ascii? */ + if (x < 0x80) /* ASCII? */ buff[UTF8BUFFSZ - 1] = cast_char(x); else { /* need continuation bytes */ unsigned int mfb = 0x3f; /* maximum that fits in first byte */ @@ -512,18 +513,18 @@ static void initbuff (lua_State *L, BuffFS *buff) { static void pushbuff (lua_State *L, void *ud) { BuffFS *buff = cast(BuffFS*, ud); switch (buff->err) { - case 1: + case 1: /* memory error */ luaD_throw(L, LUA_ERRMEM); break; case 2: /* length overflow: Add "..." at the end of result */ if (buff->buffsize - buff->blen < 3) - strcpy(buff->b + buff->blen - 3, "..."); /* 'blen' must be > 3 */ + strcpy(buff->b + buff->blen - 3, "..."); /* 'blen' must be > 3 */ else { /* there is enough space left for the "..." */ strcpy(buff->b + buff->blen, "..."); buff->blen += 3; } /* FALLTHROUGH */ - default: { /* no errors */ + default: { /* no errors, but it can raise one creating the new string */ TString *ts = luaS_newlstr(L, buff->b, buff->blen); setsvalue2s(L, L->top.p, ts); L->top.p++; @@ -618,7 +619,7 @@ const char *luaO_pushvfstring (lua_State *L, const char *fmt, va_list argp) { } case 'I': { /* a 'lua_Integer' */ TValue num; - setivalue(&num, cast(lua_Integer, va_arg(argp, l_uacInt))); + setivalue(&num, cast_Integer(va_arg(argp, l_uacInt))); addnum2buff(&buff, &num); break; } @@ -637,7 +638,8 @@ const char *luaO_pushvfstring (lua_State *L, const char *fmt, va_list argp) { } case 'U': { /* an 'unsigned long' as a UTF-8 sequence */ char bf[UTF8BUFFSZ]; - int len = luaO_utf8esc(bf, va_arg(argp, unsigned long)); + unsigned long arg = va_arg(argp, unsigned long); + int len = luaO_utf8esc(bf, cast(l_uint32, arg)); addstr2buff(&buff, bf + UTF8BUFFSZ - len, cast_uint(len)); break; } diff --git a/lobject.h b/lobject.h index 8c06a224cc..bf5b65b469 100644 --- a/lobject.h +++ b/lobject.h @@ -208,7 +208,8 @@ typedef union { #define ttisstrictnil(o) checktag((o), LUA_VNIL) -#define setnilvalue(obj) settt_(obj, LUA_VNIL) +#define setnilvalue(obj) settt_(obj, LUA_VNIL) +#define setnilvalue2s(stk) setnilvalue(s2v(stk)) #define isabstkey(v) checktag((v), LUA_VABSTKEY) @@ -418,6 +419,7 @@ typedef struct TString { #define strisshr(ts) ((ts)->shrlen >= 0) +#define isextstr(ts) (ttislngstring(ts) && tsvalue(ts)->shrlen != LSTRREG) /* @@ -582,9 +584,18 @@ typedef struct AbsLineInfo { /* ** Flags in Prototypes */ -#define PF_ISVARARG 1 -#define PF_FIXED 2 /* prototype has parts in fixed memory */ +#define PF_VAHID 1 /* function has hidden vararg arguments */ +#define PF_VATAB 2 /* function has vararg table */ +#define PF_FIXED 4 /* prototype has parts in fixed memory */ +/* a vararg function either has hidden args. or a vararg table */ +#define isvararg(p) ((p)->flag & (PF_VAHID | PF_VATAB)) + +/* +** mark that a function needs a vararg table. (The flag PF_VAHID will +** be cleared later.) +*/ +#define needvatab(p) ((p)->flag |= PF_VATAB) /* ** Function Prototypes @@ -822,7 +833,16 @@ typedef struct Table { /* size of buffer for 'luaO_utf8esc' function */ #define UTF8BUFFSZ 8 -LUAI_FUNC int luaO_utf8esc (char *buff, unsigned long x); + +/* macro to call 'luaO_pushvfstring' correctly */ +#define pushvfstring(L, argp, fmt, msg) \ + { va_start(argp, fmt); \ + msg = luaO_pushvfstring(L, fmt, argp); \ + va_end(argp); \ + if (msg == NULL) luaD_throw(L, LUA_ERRMEM); /* only after 'va_end' */ } + + +LUAI_FUNC int luaO_utf8esc (char *buff, l_uint32 x); LUAI_FUNC lu_byte luaO_ceillog2 (unsigned int x); LUAI_FUNC lu_byte luaO_codeparam (unsigned int p); LUAI_FUNC l_mem luaO_applyparam (lu_byte p, l_mem x); diff --git a/lopcodes.c b/lopcodes.c index 092c390206..da64ff18d1 100644 --- a/lopcodes.c +++ b/lopcodes.c @@ -53,8 +53,8 @@ LUAI_DDEF const lu_byte luaP_opmodes[NUM_OPCODES] = { ,opmode(0, 0, 0, 0, 1, iABC) /* OP_BANDK */ ,opmode(0, 0, 0, 0, 1, iABC) /* OP_BORK */ ,opmode(0, 0, 0, 0, 1, iABC) /* OP_BXORK */ - ,opmode(0, 0, 0, 0, 1, iABC) /* OP_SHRI */ ,opmode(0, 0, 0, 0, 1, iABC) /* OP_SHLI */ + ,opmode(0, 0, 0, 0, 1, iABC) /* OP_SHRI */ ,opmode(0, 0, 0, 0, 1, iABC) /* OP_ADD */ ,opmode(0, 0, 0, 0, 1, iABC) /* OP_SUB */ ,opmode(0, 0, 0, 0, 1, iABC) /* OP_MUL */ @@ -102,7 +102,9 @@ LUAI_DDEF const lu_byte luaP_opmodes[NUM_OPCODES] = { ,opmode(0, 0, 1, 0, 0, ivABC) /* OP_SETLIST */ ,opmode(0, 0, 0, 0, 1, iABx) /* OP_CLOSURE */ ,opmode(0, 1, 0, 0, 1, iABC) /* OP_VARARG */ - ,opmode(0, 0, 1, 0, 1, iABC) /* OP_VARARGPREP */ + ,opmode(0, 0, 0, 0, 1, iABC) /* OP_GETVARG */ + ,opmode(0, 0, 0, 0, 0, iABx) /* OP_ERRNNIL */ + ,opmode(0, 0, 0, 0, 0, iABC) /* OP_VARARGPREP */ ,opmode(0, 0, 0, 0, 0, iAx) /* OP_EXTRAARG */ }; diff --git a/lopcodes.h b/lopcodes.h index 7511eb2237..b6bd182ea2 100644 --- a/lopcodes.h +++ b/lopcodes.h @@ -126,14 +126,14 @@ enum OpMode {iABC, ivABC, iABx, iAsBx, iAx, isJ}; #define GET_OPCODE(i) (cast(OpCode, ((i)>>POS_OP) & MASK1(SIZE_OP,0))) #define SET_OPCODE(i,o) ((i) = (((i)&MASK0(SIZE_OP,POS_OP)) | \ - ((cast(Instruction, o)<>(pos)) & MASK1(size,0))) #define setarg(i,v,pos,size) ((i) = (((i)&MASK0(size,pos)) | \ - ((cast(Instruction, v)<> sC */ OP_SHLI,/* A B sC R[A] := sC << R[B] */ +OP_SHRI,/* A B sC R[A] := R[B] >> sC */ OP_ADD,/* A B C R[A] := R[B] + R[C] */ OP_SUB,/* A B C R[A] := R[B] - R[C] */ @@ -289,7 +289,7 @@ OP_BXOR,/* A B C R[A] := R[B] ~ R[C] */ OP_SHL,/* A B C R[A] := R[B] << R[C] */ OP_SHR,/* A B C R[A] := R[B] >> R[C] */ -OP_MMBIN,/* A B C call C metamethod over R[A] and R[B] (*) */ +OP_MMBIN,/* A B C call C metamethod over R[A] and R[B] */ OP_MMBINI,/* A sB C k call C metamethod over R[A] and sB */ OP_MMBINK,/* A B C k call C metamethod over R[A] and K[B] */ @@ -315,12 +315,12 @@ OP_GTI,/* A sB k if ((R[A] > sB) ~= k) then pc++ */ OP_GEI,/* A sB k if ((R[A] >= sB) ~= k) then pc++ */ OP_TEST,/* A k if (not R[A] == k) then pc++ */ -OP_TESTSET,/* A B k if (not R[B] == k) then pc++ else R[A] := R[B] (*) */ +OP_TESTSET,/* A B k if (not R[B] == k) then pc++ else R[A] := R[B] */ OP_CALL,/* A B C R[A], ... ,R[A+C-2] := R[A](R[A+1], ... ,R[A+B-1]) */ OP_TAILCALL,/* A B C k return R[A](R[A+1], ... ,R[A+B-1]) */ -OP_RETURN,/* A B C k return R[A], ... ,R[A+B-2] (see note) */ +OP_RETURN,/* A B C k return R[A], ... ,R[A+B-2] */ OP_RETURN0,/* return */ OP_RETURN1,/* A return R[A] */ @@ -336,9 +336,13 @@ OP_SETLIST,/* A vB vC k R[A][vC+i] := R[A+i], 1 <= i <= vB */ OP_CLOSURE,/* A Bx R[A] := closure(KPROTO[Bx]) */ -OP_VARARG,/* A C R[A], R[A+1], ..., R[A+C-2] = vararg */ +OP_VARARG,/* A B C k R[A], ..., R[A+C-2] = varargs */ + +OP_GETVARG, /* A B C R[A] := R[B][R[C]], R[B] is vararg parameter */ -OP_VARARGPREP,/*A (adjust vararg parameters) */ +OP_ERRNNIL,/* A Bx raise error if R[A] ~= nil (K[Bx - 1] is global name)*/ + +OP_VARARGPREP,/* (adjust varargs) */ OP_EXTRAARG/* Ax extra (larger) argument for previous opcode */ } OpCode; @@ -367,7 +371,8 @@ OP_EXTRAARG/* Ax extra (larger) argument for previous opcode */ OP_RETURN*, OP_SETLIST) may use 'top'. (*) In OP_VARARG, if (C == 0) then use actual number of varargs and - set top (like in OP_CALL with C == 0). + set top (like in OP_CALL with C == 0). 'k' means function has a + vararg table, which is in R[B]. (*) In OP_RETURN, if (B == 0) then return up to 'top'. @@ -378,22 +383,27 @@ OP_EXTRAARG/* Ax extra (larger) argument for previous opcode */ real C = EXTRAARG _ C (the bits of EXTRAARG concatenated with the bits of C). - (*) In OP_NEWTABLE, B is log2 of the hash size (which is always a + (*) In OP_NEWTABLE, vB is log2 of the hash size (which is always a power of 2) plus 1, or zero for size zero. If not k, the array size - is C. Otherwise, the array size is EXTRAARG _ C. + is vC. Otherwise, the array size is EXTRAARG _ vC. + + (*) In OP_ERRNNIL, (Bx == 0) means index of global name doesn't + fit in Bx. (So, that name is not available for the error message.) (*) For comparisons, k specifies what condition the test should accept (true or false). (*) In OP_MMBINI/OP_MMBINK, k means the arguments were flipped - (the constant is the first operand). + (the constant is the first operand). - (*) All 'skips' (pc++) assume that next instruction is a jump. + (*) All comparison and test instructions assume that the instruction + being skipped (pc++) is a jump. (*) In instructions OP_RETURN/OP_TAILCALL, 'k' specifies that the function builds upvalues, which may need to be closed. C > 0 means - the function is vararg, so that its 'func' must be corrected before - returning; in this case, (C - 1) is its number of fixed parameters. + the function has hidden vararg arguments, so that its 'func' must be + corrected before returning; in this case, (C - 1) is its number of + fixed parameters. (*) In comparisons with an immediate operand, C signals whether the original operand was a float. (It must be corrected in case of diff --git a/lopnames.h b/lopnames.h index 965cec9bf2..0554a2e9a1 100644 --- a/lopnames.h +++ b/lopnames.h @@ -45,8 +45,8 @@ static const char *const opnames[] = { "BANDK", "BORK", "BXORK", - "SHRI", "SHLI", + "SHRI", "ADD", "SUB", "MUL", @@ -94,6 +94,8 @@ static const char *const opnames[] = { "SETLIST", "CLOSURE", "VARARG", + "GETVARG", + "ERRNNIL", "VARARGPREP", "EXTRAARG", NULL diff --git a/loslib.c b/loslib.c index 4623ad5ecf..b7a2b0d15f 100644 --- a/loslib.c +++ b/loslib.c @@ -34,7 +34,7 @@ #if defined(LUA_USE_WINDOWS) #define LUA_STRFTIMEOPTIONS "aAbBcdHIjmMpSUwWxXyYzZ%" \ "||" "#c#x#d#H#I#j#m#M#S#U#w#W#y#Y" /* two-char options */ -#elif defined(LUA_USE_C89) /* ANSI C 89 (only 1-char options) */ +#elif defined(LUA_USE_C89) /* C89 (only 1-char options) */ #define LUA_STRFTIMEOPTIONS "aAbBcdHIjmMpSUwWxXyYZ%" #else /* C99 specification */ #define LUA_STRFTIMEOPTIONS "aAbBcCdDeFgGhHIjmMnprRStTuUVwWxXyYzZ%" \ @@ -273,7 +273,7 @@ static int getfield (lua_State *L, const char *key, int d, int delta) { static const char *checkoption (lua_State *L, const char *conv, - ptrdiff_t convlen, char *buff) { + size_t convlen, char *buff) { const char *option = LUA_STRFTIMEOPTIONS; unsigned oplen = 1; /* length of options being checked */ for (; *option != '\0' && oplen <= convlen; option += oplen) { @@ -333,7 +333,8 @@ static int os_date (lua_State *L) { size_t reslen; char *buff = luaL_prepbuffsize(&b, SIZETIMEFMT); s++; /* skip '%' */ - s = checkoption(L, s, se - s, cc + 1); /* copy specifier to 'cc' */ + /* copy specifier to 'cc' */ + s = checkoption(L, s, ct_diff2sz(se - s), cc + 1); reslen = strftime(buff, SIZETIMEFMT, cc, stm); luaL_addsize(&b, reslen); } diff --git a/lparser.c b/lparser.c index 380e45f58c..af2b64d1ca 100644 --- a/lparser.c +++ b/lparser.c @@ -30,8 +30,8 @@ -/* maximum number of local variables per function (must be smaller - than 250, due to the bytecode format) */ +/* maximum number of variable declarations per function (must be + smaller than 250, due to the bytecode format) */ #define MAXVARS 200 @@ -50,7 +50,7 @@ typedef struct BlockCnt { struct BlockCnt *previous; /* chain */ int firstlabel; /* index of first label in this block */ int firstgoto; /* index of first pending goto in this block */ - lu_byte nactvar; /* # active locals outside the block */ + short nactvar; /* number of active declarations at block entry */ lu_byte upval; /* true if some variable in the block is an upvalue */ lu_byte isloop; /* 1 if 'block' is a loop; 2 if it has pending breaks */ lu_byte insidetbc; /* true if inside the scope of a to-be-closed var. */ @@ -188,18 +188,16 @@ static short registerlocalvar (LexState *ls, FuncState *fs, /* -** Create a new local variable with the given 'name' and given 'kind'. +** Create a new variable with the given 'name' and given 'kind'. ** Return its index in the function. */ -static int new_localvarkind (LexState *ls, TString *name, lu_byte kind) { +static int new_varkind (LexState *ls, TString *name, lu_byte kind) { lua_State *L = ls->L; FuncState *fs = ls->fs; Dyndata *dyd = ls->dyd; Vardesc *var; - luaY_checklimit(fs, dyd->actvar.n + 1 - fs->firstlocal, - MAXVARS, "local variables"); luaM_growvector(L, dyd->actvar.arr, dyd->actvar.n + 1, - dyd->actvar.size, Vardesc, SHRT_MAX, "local variables"); + dyd->actvar.size, Vardesc, SHRT_MAX, "variable declarations"); var = &dyd->actvar.arr[dyd->actvar.n++]; var->vd.kind = kind; /* default */ var->vd.name = name; @@ -211,7 +209,7 @@ static int new_localvarkind (LexState *ls, TString *name, lu_byte kind) { ** Create a new local variable with the given 'name' and regular kind. */ static int new_localvar (LexState *ls, TString *name) { - return new_localvarkind(ls, name, VDKREG); + return new_varkind(ls, name, VDKREG); } #define new_localvarliteral(ls,v) \ @@ -238,7 +236,7 @@ static Vardesc *getlocalvardesc (FuncState *fs, int vidx) { static lu_byte reglevel (FuncState *fs, int nvar) { while (nvar-- > 0) { Vardesc *vd = getlocalvardesc(fs, nvar); /* get previous variable */ - if (vd->vd.kind != RDKCTC) /* is in a register? */ + if (varinreg(vd)) /* is in a register? */ return cast_byte(vd->vd.ridx + 1); } return 0; /* no variables in registers */ @@ -259,7 +257,7 @@ lu_byte luaY_nvarstack (FuncState *fs) { */ static LocVar *localdebuginfo (FuncState *fs, int vidx) { Vardesc *vd = getlocalvardesc(fs, vidx); - if (vd->vd.kind == RDKCTC) + if (!varinreg(vd)) return NULL; /* no debug info. for constants */ else { int idx = vd->vd.pidx; @@ -275,13 +273,15 @@ static LocVar *localdebuginfo (FuncState *fs, int vidx) { static void init_var (FuncState *fs, expdesc *e, int vidx) { e->f = e->t = NO_JUMP; e->k = VLOCAL; - e->u.var.vidx = cast(unsigned short, vidx); + e->u.var.vidx = cast_short(vidx); e->u.var.ridx = getlocalvardesc(fs, vidx)->vd.ridx; } /* -** Raises an error if variable described by 'e' is read only +** Raises an error if variable described by 'e' is read only; moreover, +** if 'e' is t[exp] where t is the vararg parameter, change it to index +** a real table. (Virtual vararg tables cannot be changed.) */ static void check_readonly (LexState *ls, expdesc *e) { FuncState *fs = ls->fs; @@ -291,7 +291,7 @@ static void check_readonly (LexState *ls, expdesc *e) { varname = ls->dyd->actvar.arr[e->u.info].vd.name; break; } - case VLOCAL: { + case VLOCAL: case VVARGVAR: { Vardesc *vardesc = getlocalvardesc(fs, e->u.var.vidx); if (vardesc->vd.kind != VDKREG) /* not a regular variable? */ varname = vardesc->vd.name; @@ -303,14 +303,22 @@ static void check_readonly (LexState *ls, expdesc *e) { varname = up->name; break; } + case VVARGIND: { + needvatab(fs->f); /* function will need a vararg table */ + e->k = VINDEXED; + } /* FALLTHROUGH */ + case VINDEXUP: case VINDEXSTR: case VINDEXED: { /* global variable */ + if (e->u.ind.ro) /* read-only? */ + varname = tsvalue(&fs->f->k[e->u.ind.keystr]); + break; + } default: - return; /* other cases cannot be read-only */ - } - if (varname) { - const char *msg = luaO_pushfstring(ls->L, - "attempt to assign to const variable '%s'", getstr(varname)); - luaK_semerror(ls, msg); /* error */ + lua_assert(e->k == VINDEXI); /* this one doesn't need any check */ + return; /* integer index cannot be read-only */ } + if (varname) + luaK_semerror(ls, "attempt to assign to const variable '%s'", + getstr(varname)); } @@ -326,6 +334,7 @@ static void adjustlocalvars (LexState *ls, int nvars) { Vardesc *var = getlocalvardesc(fs, vidx); var->vd.ridx = cast_byte(reglevel++); var->vd.pidx = registerlocalvar(ls, fs, var->vd.name); + luaY_checklimit(fs, reglevel, MAXVARS, "local variables"); } } @@ -392,19 +401,42 @@ static int newupvalue (FuncState *fs, TString *name, expdesc *v) { /* -** Look for an active local variable with the name 'n' in the +** Look for an active variable with the name 'n' in the ** function 'fs'. If found, initialize 'var' with it and return -** its expression kind; otherwise return -1. +** its expression kind; otherwise return -1. While searching, +** var->u.info==-1 means that the preambular global declaration is +** active (the default while there is no other global declaration); +** var->u.info==-2 means there is no active collective declaration +** (some previous global declaration but no collective declaration); +** and var->u.info>=0 points to the inner-most (the first one found) +** collective declaration, if there is one. */ static int searchvar (FuncState *fs, TString *n, expdesc *var) { int i; for (i = cast_int(fs->nactvar) - 1; i >= 0; i--) { Vardesc *vd = getlocalvardesc(fs, i); - if (eqstr(n, vd->vd.name)) { /* found? */ + if (varglobal(vd)) { /* global declaration? */ + if (vd->vd.name == NULL) { /* collective declaration? */ + if (var->u.info < 0) /* no previous collective declaration? */ + var->u.info = fs->firstlocal + i; /* this is the first one */ + } + else { /* global name */ + if (eqstr(n, vd->vd.name)) { /* found? */ + init_exp(var, VGLOBAL, fs->firstlocal + i); + return VGLOBAL; + } + else if (var->u.info == -1) /* active preambular declaration? */ + var->u.info = -2; /* invalidate preambular declaration */ + } + } + else if (eqstr(n, vd->vd.name)) { /* found? */ if (vd->vd.kind == RDKCTC) /* compile-time constant? */ init_exp(var, VCONST, fs->firstlocal + i); - else /* real variable */ + else { /* local variable */ init_var(fs, var, i); + if (vd->vd.kind == RDKVAVAR) /* vararg parameter? */ + var->k = VVARGVAR; + } return cast_int(var->k); } } @@ -442,48 +474,72 @@ static void marktobeclosed (FuncState *fs) { ** 'var' as 'void' as a flag. */ static void singlevaraux (FuncState *fs, TString *n, expdesc *var, int base) { - if (fs == NULL) /* no more levels? */ - init_exp(var, VVOID, 0); /* default is global */ - else { - int v = searchvar(fs, n, var); /* look up locals at current level */ - if (v >= 0) { /* found? */ - if (v == VLOCAL && !base) - markupval(fs, var->u.var.vidx); /* local will be used as an upval */ + int v = searchvar(fs, n, var); /* look up variables at current level */ + if (v >= 0) { /* found? */ + if (!base) { + if (var->k == VVARGVAR) /* vararg parameter? */ + luaK_vapar2local(fs, var); /* change it to a regular local */ + if (var->k == VLOCAL) + markupval(fs, var->u.var.vidx); /* will be used as an upvalue */ } - else { /* not found as local at current level; try upvalues */ - int idx = searchupvalue(fs, n); /* try existing upvalues */ - if (idx < 0) { /* not found? */ + /* else nothing else to be done */ + } + else { /* not found at current level; try upvalues */ + int idx = searchupvalue(fs, n); /* try existing upvalues */ + if (idx < 0) { /* not found? */ + if (fs->prev != NULL) /* more levels? */ singlevaraux(fs->prev, n, var, 0); /* try upper levels */ - if (var->k == VLOCAL || var->k == VUPVAL) /* local or upvalue? */ - idx = newupvalue(fs, n, var); /* will be a new upvalue */ - else /* it is a global or a constant */ - return; /* don't need to do anything at this level */ - } - init_exp(var, VUPVAL, idx); /* new or old upvalue */ + if (var->k == VLOCAL || var->k == VUPVAL) /* local or upvalue? */ + idx = newupvalue(fs, n, var); /* will be a new upvalue */ + else /* it is a global or a constant */ + return; /* don't need to do anything at this level */ } + init_exp(var, VUPVAL, idx); /* new or old upvalue */ } } +static void buildglobal (LexState *ls, TString *varname, expdesc *var) { + FuncState *fs = ls->fs; + expdesc key; + init_exp(var, VGLOBAL, -1); /* global by default */ + singlevaraux(fs, ls->envn, var, 1); /* get environment variable */ + if (var->k == VGLOBAL) + luaK_semerror(ls, "%s is global when accessing variable '%s'", + LUA_ENV, getstr(varname)); + luaK_exp2anyregup(fs, var); /* _ENV could be a constant */ + codestring(&key, varname); /* key is variable name */ + luaK_indexed(fs, var, &key); /* 'var' represents _ENV[varname] */ +} + + /* ** Find a variable with the given name 'n', handling global variables ** too. */ -static void singlevar (LexState *ls, expdesc *var) { - TString *varname = str_checkname(ls); +static void buildvar (LexState *ls, TString *varname, expdesc *var) { FuncState *fs = ls->fs; + init_exp(var, VGLOBAL, -1); /* global by default */ 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 != VVOID); /* this one must exist */ - luaK_exp2anyregup(fs, var); /* but could be a constant */ - codestring(&key, varname); /* key is variable name */ - luaK_indexed(fs, var, &key); /* env[varname] */ + if (var->k == VGLOBAL) { /* global name? */ + int info = var->u.info; + /* global by default in the scope of a global declaration? */ + if (info == -2) + luaK_semerror(ls, "variable '%s' not declared", getstr(varname)); + buildglobal(ls, varname, var); + if (info != -1 && ls->dyd->actvar.arr[info].vd.kind == GDKCONST) + var->u.ind.ro = 1; /* mark variable as read-only */ + else /* anyway must be a global */ + lua_assert(info == -1 || ls->dyd->actvar.arr[info].vd.kind == GDKREG); } } +static void singlevar (LexState *ls, expdesc *var) { + buildvar(ls, str_checkname(ls), var); +} + + /* ** Adjust the number of results from an expression list 'e' with 'nexps' ** expressions to 'nvars' values. @@ -491,6 +547,7 @@ static void singlevar (LexState *ls, expdesc *var) { static void adjust_assign (LexState *ls, int nvars, int nexps, expdesc *e) { FuncState *fs = ls->fs; int needed = nvars - nexps; /* extra values needed */ + luaK_checkstack(fs, needed); if (hasmultret(e->k)) { /* last expression has multiple returns? */ int extra = needed + 1; /* discount last expression itself */ if (extra < 0) @@ -518,14 +575,14 @@ static void adjust_assign (LexState *ls, int nvars, int nexps, expdesc *e) { /* ** Generates an error that a goto jumps into the scope of some -** local variable. +** variable declaration. */ static l_noret jumpscopeerror (LexState *ls, Labeldesc *gt) { TString *tsname = getlocalvardesc(ls->fs, gt->nactvar)->vd.name; - const char *varname = getstr(tsname); - const char *msg = " at line %d jumps into the scope of local '%s'"; - msg = luaO_pushfstring(ls->L, msg, getstr(gt->name), gt->line, varname); - luaK_semerror(ls, msg); /* raise the error */ + const char *varname = (tsname != NULL) ? getstr(tsname) : "*"; + luaK_semerror(ls, + " at line %d jumps into the scope of '%s'", + getstr(gt->name), gt->line, varname); /* raise the error */ } @@ -563,7 +620,7 @@ static void closegoto (LexState *ls, int g, Labeldesc *label, int bup) { /* ** Search for an active label with the given name, starting at -** index 'ilb' (so that it can searh for all labels in current block +** index 'ilb' (so that it can search for all labels in current block ** or all labels in current function). */ static Labeldesc *findlabel (LexState *ls, TString *name, int ilb) { @@ -630,7 +687,7 @@ static void createlabel (LexState *ls, TString *name, int line, int last) { /* -** Traverse the pending goto's of the finishing block checking whether +** Traverse the pending gotos of the finishing block checking whether ** each match some label of that block. Those that do not match are ** "exported" to the outer block, to be solved there. In particular, ** its 'nactvar' is updated with the level of the inner block, @@ -666,8 +723,9 @@ static void enterblock (FuncState *fs, BlockCnt *bl, lu_byte isloop) { bl->firstlabel = fs->ls->dyd->label.n; bl->firstgoto = fs->ls->dyd->gt.n; bl->upval = 0; + /* inherit 'insidetbc' from enclosing block */ bl->insidetbc = (fs->bl != NULL && fs->bl->insidetbc); - bl->previous = fs->bl; + bl->previous = fs->bl; /* link block in function's block list */ fs->bl = bl; lua_assert(fs->freereg == luaY_nvarstack(fs)); } @@ -677,11 +735,10 @@ static void enterblock (FuncState *fs, BlockCnt *bl, lu_byte isloop) { ** generates an error for an undefined 'goto'. */ static l_noret undefgoto (LexState *ls, Labeldesc *gt) { - const char *msg = "no visible label '%s' for at line %d"; - msg = luaO_pushfstring(ls->L, msg, getstr(gt->name), gt->line); /* breaks are checked when created, cannot be undefined */ - lua_assert(!eqstr(gt->name, luaS_newliteral(ls->L, "break"))); - luaK_semerror(ls, msg); + lua_assert(!eqstr(gt->name, ls->brkn)); + luaK_semerror(ls, "no visible label '%s' for at line %d", + getstr(gt->name), gt->line); } @@ -695,7 +752,7 @@ static void leaveblock (FuncState *fs) { removevars(fs, bl->nactvar); /* remove block locals */ lua_assert(bl->nactvar == fs->nactvar); /* back to level on entry */ if (bl->isloop == 2) /* has to fix pending breaks? */ - createlabel(ls, luaS_newliteral(ls->L, "break"), 0, 0); + createlabel(ls, ls->brkn, 0, 0); solvegotos(fs, bl); if (bl->previous == NULL) { /* was it the last block? */ if (bl->firstgoto < ls->dyd->gt.n) /* still pending gotos? */ @@ -764,8 +821,7 @@ static void open_func (LexState *ls, FuncState *fs, BlockCnt *bl) { luaC_objbarrier(L, f, f->source); f->maxstacksize = 2; /* registers 0/1 are always valid */ fs->kcache = luaH_new(L); /* create table for function */ - sethvalue2s(L, L->top.p, fs->kcache); /* anchor it */ - luaD_inctop(L); + luaD_anchorobj(L, ls->h, obj2gco(fs->kcache)); /* anchor it */ enterblock(fs, bl, 0); } @@ -774,6 +830,7 @@ static void close_func (LexState *ls) { lua_State *L = ls->L; FuncState *fs = ls->fs; Proto *f = fs->f; + TValue temp; luaK_ret(fs, luaY_nvarstack(fs), 0); /* final return */ leaveblock(fs); lua_assert(fs->bl == NULL); @@ -786,8 +843,10 @@ static void close_func (LexState *ls) { luaM_shrinkvector(L, f->p, f->sizep, fs->np, Proto *); luaM_shrinkvector(L, f->locvars, f->sizelocvars, fs->ndebugvars, LocVar); luaM_shrinkvector(L, f->upvalues, f->sizeupvalues, fs->nups, Upvaldesc); + /* remove kcache table from scanner table ("weigh" its anchor) */ + sethvalue(L, &temp, fs->kcache); /* key to be set to nil */ + luaH_set(L, ls->h, &temp, &G(L)->nilvalue); ls->fs = fs->prev; - L->top.p--; /* pop kcache table */ luaC_checkGC(L); } @@ -863,15 +922,26 @@ typedef struct ConsControl { } ConsControl; +/* +** Maximum number of elements in a constructor, to control the following: +** * counter overflows; +** * overflows in 'extra' for OP_NEWTABLE and OP_SETLIST; +** * overflows when adding multiple returns in OP_SETLIST. +*/ +#define MAX_CNST (INT_MAX/2) +#if MAX_CNST/(MAXARG_vC + 1) > MAXARG_Ax +#undef MAX_CNST +#define MAX_CNST (MAXARG_Ax * (MAXARG_vC + 1)) +#endif + + static void recfield (LexState *ls, ConsControl *cc) { /* recfield -> (NAME | '['exp']') = exp */ FuncState *fs = ls->fs; lu_byte reg = ls->fs->freereg; expdesc tab, key, val; - if (ls->t.token == TK_NAME) { - luaY_checklimit(fs, cc->nh, INT_MAX / 2, "items in a constructor"); + if (ls->t.token == TK_NAME) codename(ls, &key); - } else /* ls->t.token == '[' */ yindex(ls, &key); cc->nh++; @@ -885,7 +955,7 @@ static void recfield (LexState *ls, ConsControl *cc) { static void closelistfield (FuncState *fs, ConsControl *cc) { - if (cc->v.k == VVOID) return; /* there is no list item */ + lua_assert(cc->tostore > 0); luaK_exp2nextreg(fs, &cc->v); cc->v.k = VVOID; if (cc->tostore >= cc->maxtostore) { @@ -973,10 +1043,12 @@ static void constructor (LexState *ls, expdesc *t) { checknext(ls, '{' /*}*/); cc.maxtostore = maxtostore(fs); do { - lua_assert(cc.v.k == VVOID || cc.tostore > 0); if (ls->t.token == /*{*/ '}') break; - closelistfield(fs, &cc); + if (cc.v.k != VVOID) /* is there a previous list item? */ + closelistfield(fs, &cc); /* close it */ field(ls, &cc); + luaY_checklimit(fs, cc.tostore + cc.na + cc.nh, MAX_CNST, + "items in a constructor"); } while (testnext(ls, ',') || testnext(ls, ';')); check_match(ls, /*{*/ '}', '{' /*}*/, line); lastlistfield(fs, &cc); @@ -986,9 +1058,9 @@ static void constructor (LexState *ls, expdesc *t) { /* }====================================================================== */ -static void setvararg (FuncState *fs, int nparams) { - fs->f->flag |= PF_ISVARARG; - luaK_codeABC(fs, OP_VARARGPREP, nparams, 0, 0); +static void setvararg (FuncState *fs) { + fs->f->flag |= PF_VAHID; /* by default, use hidden vararg arguments */ + luaK_codeABC(fs, OP_VARARGPREP, 0, 0, 0); } @@ -997,7 +1069,7 @@ static void parlist (LexState *ls) { FuncState *fs = ls->fs; Proto *f = fs->f; int nparams = 0; - int isvararg = 0; + int varargk = 0; if (ls->t.token != ')') { /* is 'parlist' not empty? */ do { switch (ls->t.token) { @@ -1007,19 +1079,26 @@ static void parlist (LexState *ls) { break; } case TK_DOTS: { - luaX_next(ls); - isvararg = 1; + varargk = 1; + luaX_next(ls); /* skip '...' */ + if (ls->t.token == TK_NAME) + new_varkind(ls, str_checkname(ls), RDKVAVAR); + else + new_localvarliteral(ls, "(vararg table)"); break; } default: luaX_syntaxerror(ls, " or '...' expected"); } - } while (!isvararg && testnext(ls, ',')); + } while (!varargk && testnext(ls, ',')); } adjustlocalvars(ls, nparams); f->numparams = cast_byte(fs->nactvar); - if (isvararg) - setvararg(fs, f->numparams); /* declared vararg */ - luaK_reserveregs(fs, fs->nactvar); /* reserve registers for parameters */ + if (varargk) { + setvararg(fs); /* declared vararg */ + adjustlocalvars(ls, 1); /* vararg parameter */ + } + /* reserve registers for parameters (plus vararg parameter, if present) */ + luaK_reserveregs(fs, fs->nactvar); } @@ -1087,6 +1166,7 @@ static void funcargs (LexState *ls, expdesc *f) { } default: { luaX_syntaxerror(ls, "function arguments expected"); + return; /* to avoid warnings */ } } lua_assert(f->k == VNONRELOC); @@ -1206,9 +1286,9 @@ static void simpleexp (LexState *ls, expdesc *v) { } case TK_DOTS: { /* vararg */ FuncState *fs = ls->fs; - check_condition(ls, fs->f->flag & PF_ISVARARG, + check_condition(ls, isvararg(fs->f), "cannot use '...' outside a vararg function"); - init_exp(v, VVARARG, luaK_codeABC(fs, OP_VARARG, 0, 0, 1)); + init_exp(v, VVARARG, luaK_codeABC(fs, OP_VARARG, 0, fs->f->numparams, 1)); break; } case '{' /*}*/: { /* constructor */ @@ -1402,6 +1482,15 @@ static void check_conflict (LexState *ls, struct LHS_assign *lh, expdesc *v) { } } + +/* Create code to store the "top" register in 'var' */ +static void storevartop (FuncState *fs, expdesc *var) { + expdesc e; + init_exp(&e, VNONRELOC, fs->freereg - 1); + luaK_storevar(fs, var, &e); /* will also free the top register */ +} + + /* ** Parse and compile a multiple assignment. The first "variable" ** (a 'suffixedexp') was already read by the caller. @@ -1435,8 +1524,7 @@ static void restassign (LexState *ls, struct LHS_assign *lh, int nvars) { return; /* avoid default */ } } - init_exp(&e, VNONRELOC, ls->fs->freereg-1); /* default assignment */ - luaK_storevar(ls->fs, &lh->v, &e); + storevartop(ls->fs, &lh->v); /* default assignment */ } @@ -1469,7 +1557,7 @@ static void breakstat (LexState *ls, int line) { ok: bl->isloop = 2; /* signal that block has pending breaks */ luaX_next(ls); /* skip break */ - newgotoentry(ls, luaS_newliteral(ls->L, "break"), line); + newgotoentry(ls, ls->brkn, line); } @@ -1479,11 +1567,9 @@ static void breakstat (LexState *ls, int line) { */ static void checkrepeated (LexState *ls, TString *name) { Labeldesc *lb = findlabel(ls, name, ls->fs->firstlabel); - if (l_unlikely(lb != NULL)) { /* already defined? */ - const char *msg = "label '%s' already defined on line %d"; - msg = luaO_pushfstring(ls->L, msg, getstr(name), lb->line); - luaK_semerror(ls, msg); /* error */ - } + if (l_unlikely(lb != NULL)) /* already defined? */ + luaK_semerror(ls, "label '%s' already defined on line %d", + getstr(name), lb->line); /* error */ } @@ -1599,13 +1685,22 @@ static void forbody (LexState *ls, int base, int line, int nvars, int isgen) { } +/* +** Control whether for-loop control variables are read-only +*/ +#if LUA_COMPAT_LOOPVAR +#define LOOPVARKIND VDKREG +#else /* by default, these variables are read only */ +#define LOOPVARKIND RDKCONST +#endif + static void fornum (LexState *ls, TString *varname, int line) { /* fornum -> NAME = exp,exp[,exp] forbody */ FuncState *fs = ls->fs; int base = fs->freereg; new_localvarliteral(ls, "(for state)"); new_localvarliteral(ls, "(for state)"); - new_localvarkind(ls, varname, RDKCONST); /* control variable */ + new_varkind(ls, varname, LOOPVARKIND); /* control variable */ checknext(ls, '='); exp1(ls); /* initial value */ checknext(ls, ','); @@ -1632,7 +1727,7 @@ static void forlist (LexState *ls, TString *indexname) { new_localvarliteral(ls, "(for state)"); /* iterator function */ new_localvarliteral(ls, "(for state)"); /* state */ new_localvarliteral(ls, "(for state)"); /* closing var. (after swap) */ - new_localvarkind(ls, indexname, RDKCONST); /* control variable */ + new_varkind(ls, indexname, LOOPVARKIND); /* control variable */ /* other declared variables */ while (testnext(ls, ',')) { new_localvar(ls, str_checkname(ls)); @@ -1707,8 +1802,8 @@ static void localfunc (LexState *ls) { } -static lu_byte getlocalattribute (LexState *ls) { - /* ATTRIB -> ['<' Name '>'] */ +static lu_byte getvarattribute (LexState *ls, lu_byte df) { + /* attrib -> ['<' NAME '>'] */ if (testnext(ls, '<')) { TString *ts = str_checkname(ls); const char *attr = getstr(ts); @@ -1718,10 +1813,9 @@ static lu_byte getlocalattribute (LexState *ls) { else if (strcmp(attr, "close") == 0) return RDKTOCLOSE; /* to-be-closed variable */ else - luaK_semerror(ls, - luaO_pushfstring(ls->L, "unknown attribute '%s'", attr)); + luaK_semerror(ls, "unknown attribute '%s'", attr); } - return VDKREG; /* regular variable */ + return df; /* return default value */ } @@ -1734,7 +1828,7 @@ static void checktoclose (FuncState *fs, int level) { static void localstat (LexState *ls) { - /* stat -> LOCAL NAME ATTRIB { ',' NAME ATTRIB } ['=' explist] */ + /* stat -> LOCAL NAME attrib { ',' NAME attrib } ['=' explist] */ FuncState *fs = ls->fs; int toclose = -1; /* index of to-be-closed variable (if any) */ Vardesc *var; /* last variable */ @@ -1742,10 +1836,12 @@ static void localstat (LexState *ls) { int nvars = 0; int nexps; expdesc e; - do { - TString *vname = str_checkname(ls); - lu_byte kind = getlocalattribute(ls); - vidx = new_localvarkind(ls, vname, kind); + /* get prefixed attribute (if any); default is regular local variable */ + lu_byte defkind = getvarattribute(ls, VDKREG); + do { /* for each variable */ + TString *vname = str_checkname(ls); /* get its name */ + lu_byte kind = getvarattribute(ls, defkind); /* postfixed attribute */ + vidx = new_varkind(ls, vname, kind); /* predeclare it */ if (kind == RDKTOCLOSE) { /* to-be-closed? */ if (toclose != -1) /* one already present? */ luaK_semerror(ls, "multiple to-be-closed variables in local list"); @@ -1753,13 +1849,13 @@ static void localstat (LexState *ls) { } nvars++; } while (testnext(ls, ',')); - if (testnext(ls, '=')) + if (testnext(ls, '=')) /* initialization? */ nexps = explist(ls, &e); else { e.k = VVOID; nexps = 0; } - var = getlocalvardesc(fs, vidx); /* get last variable */ + var = getlocalvardesc(fs, vidx); /* retrieve last variable */ if (nvars == nexps && /* no adjustments? */ var->vd.kind == RDKCONST && /* last variable is const? */ luaK_exp2const(fs, &e, &var->k)) { /* compile-time constant? */ @@ -1775,6 +1871,116 @@ static void localstat (LexState *ls) { } +static lu_byte getglobalattribute (LexState *ls, lu_byte df) { + lu_byte kind = getvarattribute(ls, df); + switch (kind) { + case RDKTOCLOSE: + luaK_semerror(ls, "global variables cannot be to-be-closed"); + return kind; /* to avoid warnings */ + case RDKCONST: + return GDKCONST; /* adjust kind for global variable */ + default: + return kind; + } +} + + +static void checkglobal (LexState *ls, TString *varname, int line) { + FuncState *fs = ls->fs; + expdesc var; + int k; + buildglobal(ls, varname, &var); /* create global variable in 'var' */ + k = var.u.ind.keystr; /* index of global name in 'k' */ + luaK_codecheckglobal(fs, &var, k, line); +} + + +/* +** Recursively traverse list of globals to be initalized. When +** going, generate table description for the global. In the end, +** after all indices have been generated, read list of initializing +** expressions. When returning, generate the assignment of the value on +** the stack to the corresponding table description. 'n' is the variable +** being handled, range [0, nvars - 1]. +*/ +static void initglobal (LexState *ls, int nvars, int firstidx, int n, + int line) { + if (n == nvars) { /* traversed all variables? */ + expdesc e; + int nexps = explist(ls, &e); /* read list of expressions */ + adjust_assign(ls, nvars, nexps, &e); + } + else { /* handle variable 'n' */ + FuncState *fs = ls->fs; + expdesc var; + TString *varname = getlocalvardesc(fs, firstidx + n)->vd.name; + buildglobal(ls, varname, &var); /* create global variable in 'var' */ + enterlevel(ls); /* control recursion depth */ + initglobal(ls, nvars, firstidx, n + 1, line); + leavelevel(ls); + checkglobal(ls, varname, line); + storevartop(fs, &var); + } +} + + +static void globalnames (LexState *ls, lu_byte defkind) { + FuncState *fs = ls->fs; + int nvars = 0; + int lastidx; /* index of last registered variable */ + do { /* for each name */ + TString *vname = str_checkname(ls); + lu_byte kind = getglobalattribute(ls, defkind); + lastidx = new_varkind(ls, vname, kind); + nvars++; + } while (testnext(ls, ',')); + if (testnext(ls, '=')) /* initialization? */ + initglobal(ls, nvars, lastidx - nvars + 1, 0, ls->linenumber); + fs->nactvar = cast_short(fs->nactvar + nvars); /* activate declaration */ +} + + +static void globalstat (LexState *ls) { + /* globalstat -> (GLOBAL) attrib '*' + globalstat -> (GLOBAL) attrib NAME attrib {',' NAME attrib} */ + FuncState *fs = ls->fs; + /* get prefixed attribute (if any); default is regular global variable */ + lu_byte defkind = getglobalattribute(ls, GDKREG); + if (!testnext(ls, '*')) + globalnames(ls, defkind); + else { + /* use NULL as name to represent '*' entries */ + new_varkind(ls, NULL, defkind); + fs->nactvar++; /* activate declaration */ + } +} + + +static void globalfunc (LexState *ls, int line) { + /* globalfunc -> (GLOBAL FUNCTION) NAME body */ + expdesc var, b; + FuncState *fs = ls->fs; + TString *fname = str_checkname(ls); + new_varkind(ls, fname, GDKREG); /* declare global variable */ + fs->nactvar++; /* enter its scope */ + buildglobal(ls, fname, &var); + body(ls, &b, 0, ls->linenumber); /* compile and return closure in 'b' */ + checkglobal(ls, fname, line); + luaK_storevar(fs, &var, &b); + luaK_fixline(fs, line); /* definition "happens" in the first line */ +} + + +static void globalstatfunc (LexState *ls, int line) { + /* stat -> GLOBAL globalfunc | GLOBAL globalstat */ + luaX_next(ls); /* skip 'global' */ + if (testnext(ls, TK_FUNCTION)) + globalfunc(ls, line); + else + globalstat(ls); +} + + static int funcname (LexState *ls, expdesc *v) { /* funcname -> NAME {fieldsel} [':' NAME] */ int ismethod = 0; @@ -1795,8 +2001,8 @@ static void funcstat (LexState *ls, int line) { expdesc v, b; luaX_next(ls); /* skip FUNCTION */ ismethod = funcname(ls, &v); - body(ls, &b, ismethod, line); check_readonly(ls, &v); + body(ls, &b, ismethod, line); luaK_storevar(ls->fs, &v, &b); luaK_fixline(ls->fs, line); /* definition "happens" in the first line */ } @@ -1894,6 +2100,10 @@ static void statement (LexState *ls) { localstat(ls); break; } + case TK_GLOBAL: { /* stat -> globalstatfunc */ + globalstatfunc(ls, line); + break; + } case TK_DBCOLON: { /* stat -> label */ luaX_next(ls); /* skip double colon */ labelstat(ls, str_checkname(ls), line); @@ -1913,6 +2123,22 @@ static void statement (LexState *ls) { gotostat(ls, line); break; } +#if LUA_COMPAT_GLOBAL + case TK_NAME: { + /* compatibility code to parse global keyword when "global" + is not reserved */ + if (ls->t.seminfo.ts == ls->glbn) { /* current = "global"? */ + int lk = luaX_lookahead(ls); + if (lk == '<' || lk == TK_NAME || lk == '*' || lk == TK_FUNCTION) { + /* 'global ' or 'global name' or 'global *' or + 'global function' */ + globalstatfunc(ls, line); + break; + } + } /* else... */ + } +#endif + /* FALLTHROUGH */ default: { /* stat -> func | assignment */ exprstat(ls); break; @@ -1937,7 +2163,7 @@ static void mainfunc (LexState *ls, FuncState *fs) { BlockCnt bl; Upvaldesc *env; open_func(ls, fs, &bl); - setvararg(fs, 0); /* main function is always declared vararg */ + setvararg(fs); /* main function is always vararg */ env = allocupvalue(fs); /* ...set environment upvalue */ env->instack = 1; env->idx = 0; @@ -1951,16 +2177,14 @@ static void mainfunc (LexState *ls, FuncState *fs) { } -LClosure *luaY_parser (lua_State *L, ZIO *z, Mbuffer *buff, +LClosure *luaY_parser (lua_State *L, ZIO *z, Table *anchor, Mbuffer *buff, Dyndata *dyd, const char *name, int firstchar) { LexState lexstate; FuncState funcstate; - LClosure *cl = luaF_newLclosure(L, 1); /* create main closure */ - setclLvalue2s(L, L->top.p, cl); /* anchor it (to avoid being collected) */ - luaD_inctop(L); - lexstate.h = luaH_new(L); /* create table for scanner */ - sethvalue2s(L, L->top.p, lexstate.h); /* anchor it */ - luaD_inctop(L); + LClosure *cl; + lexstate.h = anchor; /* table for scanner */ + cl = luaF_newLclosure(L, 1); /* create main closure */ + luaD_anchorobj(L, anchor, obj2gco(cl)); /* anchor it in scanner table */ funcstate.f = cl->p = luaF_newproto(L); luaC_objbarrier(L, cl, cl->p); funcstate.f->source = luaS_new(L, name); /* create and anchor TString */ @@ -1973,7 +2197,6 @@ LClosure *luaY_parser (lua_State *L, ZIO *z, Mbuffer *buff, lua_assert(!funcstate.prev && funcstate.nups == 1 && !lexstate.fs); /* all scopes should be correctly finished */ lua_assert(dyd->actvar.n == 0 && dyd->gt.n == 0 && dyd->label.n == 0); - L->top.p--; /* remove scanner's table */ - return cl; /* closure is on the stack, too */ + return cl; } diff --git a/lparser.h b/lparser.h index a3063569c7..4fad6bdc19 100644 --- a/lparser.h +++ b/lparser.h @@ -37,21 +37,31 @@ typedef enum { info = result register */ VLOCAL, /* local variable; var.ridx = register index; var.vidx = relative index in 'actvar.arr' */ + VVARGVAR, /* vararg parameter; var.ridx = register index; + var.vidx = relative index in 'actvar.arr' */ + VGLOBAL, /* global variable; + info = relative index in 'actvar.arr' (or -1 for + implicit declaration) */ VUPVAL, /* upvalue variable; info = index of upvalue in 'upvalues' */ VCONST, /* compile-time variable; info = absolute index in 'actvar.arr' */ VINDEXED, /* indexed variable; ind.t = table register; - ind.idx = key's R index */ + ind.idx = key's R index; + ind.ro = true if it represents a read-only global; + ind.keystr = if key is a string, index in 'k' of that string; + -1 if key is not a string */ + VVARGIND, /* indexed vararg parameter; + ind.* as in VINDEXED */ VINDEXUP, /* indexed upvalue; - ind.t = table upvalue; - ind.idx = key's K index */ + ind.idx = key's K index; + ind.* as in VINDEXED */ VINDEXI, /* indexed variable with constant integer; ind.t = table register; ind.idx = key's value */ VINDEXSTR, /* indexed variable with literal string; - ind.t = table register; - ind.idx = key's K index */ + ind.idx = key's K index; + ind.* as in VINDEXED */ VJMP, /* expression is a test/comparison; info = pc of corresponding jump instruction */ VRELOC, /* expression can put result in any register; @@ -75,10 +85,12 @@ typedef struct expdesc { struct { /* for indexed variables */ short idx; /* index (R or "long" K) */ lu_byte t; /* table (register or upvalue) */ + lu_byte ro; /* true if variable is read-only */ + int keystr; /* index in 'k' of string key, or -1 if not a string */ } ind; struct { /* for local variables */ lu_byte ridx; /* register holding the variable */ - unsigned short vidx; /* compiler index (in 'actvar.arr') */ + short vidx; /* index in 'actvar.arr' */ } var; } u; int t; /* patch list of 'exit when true' */ @@ -87,12 +99,22 @@ typedef struct expdesc { /* kinds of variables */ -#define VDKREG 0 /* regular */ -#define RDKCONST 1 /* constant */ -#define RDKTOCLOSE 2 /* to-be-closed */ -#define RDKCTC 3 /* compile-time constant */ +#define VDKREG 0 /* regular local */ +#define RDKCONST 1 /* local constant */ +#define RDKVAVAR 2 /* vararg parameter */ +#define RDKTOCLOSE 3 /* to-be-closed */ +#define RDKCTC 4 /* local compile-time constant */ +#define GDKREG 5 /* regular global */ +#define GDKCONST 6 /* global constant */ + +/* variables that live in registers */ +#define varinreg(v) ((v)->vd.kind <= RDKTOCLOSE) + +/* test for global variables */ +#define varglobal(v) ((v)->vd.kind >= GDKREG) + -/* description of an active local variable */ +/* description of an active variable */ typedef union Vardesc { struct { TValuefields; /* constant value (if it is a compile-time constant) */ @@ -111,7 +133,7 @@ typedef struct Labeldesc { TString *name; /* label identifier */ int pc; /* position in code */ int line; /* line where it appeared */ - lu_byte nactvar; /* number of active variables in that position */ + short nactvar; /* number of active variables in that position */ lu_byte close; /* true for goto that escapes upvalues */ } Labeldesc; @@ -156,7 +178,7 @@ typedef struct FuncState { int firstlocal; /* index of first local var (in Dyndata array) */ int firstlabel; /* index of first label (in 'dyd->label->arr') */ short ndebugvars; /* number of elements in 'f->locvars' */ - lu_byte nactvar; /* number of active local variables */ + short nactvar; /* number of active variable declarations */ lu_byte nups; /* number of upvalues */ lu_byte freereg; /* first free register */ lu_byte iwthabs; /* instructions issued since last absolute line info */ @@ -167,8 +189,9 @@ typedef struct FuncState { LUAI_FUNC lu_byte luaY_nvarstack (FuncState *fs); LUAI_FUNC void luaY_checklimit (FuncState *fs, int v, int l, const char *what); -LUAI_FUNC LClosure *luaY_parser (lua_State *L, ZIO *z, Mbuffer *buff, - Dyndata *dyd, const char *name, int firstchar); +LUAI_FUNC LClosure *luaY_parser (lua_State *L, ZIO *z, Table *anchor, + Mbuffer *buff, Dyndata *dyd, + const char *name, int firstchar); #endif diff --git a/lstate.c b/lstate.c index 0e1cb01ebb..ada0b8566a 100644 --- a/lstate.c +++ b/lstate.c @@ -29,25 +29,6 @@ -/* -** thread state + extra space -*/ -typedef struct LX { - lu_byte extra_[LUA_EXTRASPACE]; - lua_State l; -} LX; - - -/* -** Main thread combines a thread state and the global state -*/ -typedef struct LG { - LX l; - global_State g; -} LG; - - - #define fromstate(L) (cast(LX *, cast(lu_byte *, (L)) - offsetof(LX, l))) @@ -87,14 +68,19 @@ void luaE_setdebt (global_State *g, l_mem debt) { } -CallInfo *luaE_extendCI (lua_State *L) { +CallInfo *luaE_extendCI (lua_State *L, int err) { CallInfo *ci; - lua_assert(L->ci->next == NULL); - ci = luaM_new(L, CallInfo); - lua_assert(L->ci->next == NULL); - L->ci->next = ci; + ci = luaM_reallocvector(L, NULL, 0, 1, CallInfo); + if (l_unlikely(ci == NULL)) { /* allocation failed? */ + if (err) + luaM_error(L); /* raise the error */ + return NULL; /* else only report it */ + } + ci->next = L->ci->next; ci->previous = L->ci; - ci->next = NULL; + L->ci->next = ci; + if (ci->next) + ci->next->previous = ci; ci->u.l.trap = 0; L->nci++; return ci; @@ -151,7 +137,7 @@ void luaE_checkcstack (lua_State *L) { if (getCcalls(L) == LUAI_MAXCCALLS) luaG_runerror(L, "C stack overflow"); else if (getCcalls(L) >= (LUAI_MAXCCALLS / 10 * 11)) - luaD_throw(L, LUA_ERRERR); /* error while handling stack error */ + luaD_errerr(L); /* error while handling stack error */ } @@ -162,25 +148,29 @@ LUAI_FUNC void luaE_incCstack (lua_State *L) { } +static void resetCI (lua_State *L) { + CallInfo *ci = L->ci = &L->base_ci; + ci->func.p = L->stack.p; + setnilvalue2s(ci->func.p); /* 'function' entry for basic 'ci' */ + ci->top.p = ci->func.p + 1 + LUA_MINSTACK; /* +1 for 'function' entry */ + ci->u.c.k = NULL; + ci->callstatus = CIST_C; + L->status = LUA_OK; + L->errfunc = 0; /* stack unwind can "throw away" the error function */ +} + + static void stack_init (lua_State *L1, lua_State *L) { - int i; CallInfo *ci; + int i; /* initialize stack array */ L1->stack.p = luaM_newvector(L, BASIC_STACK_SIZE + EXTRA_STACK, StackValue); L1->tbclist.p = L1->stack.p; for (i = 0; i < BASIC_STACK_SIZE + EXTRA_STACK; i++) - setnilvalue(s2v(L1->stack.p + i)); /* erase new stack */ - L1->top.p = L1->stack.p; + setnilvalue2s(L1->stack.p + i); /* erase new stack */ L1->stack_last.p = L1->stack.p + BASIC_STACK_SIZE; /* initialize first ci */ - ci = &L1->base_ci; - ci->next = ci->previous = NULL; - ci->callstatus = CIST_C; - ci->func.p = L1->top.p; - ci->u.c.k = NULL; - setnilvalue(s2v(L1->top.p)); /* 'function' entry for this 'ci' */ - L1->top.p++; - ci->top.p = L1->top.p + LUA_MINSTACK; - L1->ci = ci; + resetCI(L1); + L1->top.p = L1->stack.p + 1; /* +1 for 'function' entry */ } @@ -254,6 +244,7 @@ static void preinit_thread (lua_State *L, global_State *g) { L->status = LUA_OK; L->errfunc = 0; L->oldpc = 0; + L->base_ci.previous = L->base_ci.next = NULL; } @@ -271,15 +262,16 @@ static void close_state (lua_State *L) { if (!completestate(g)) /* closing a partially built state? */ luaC_freeallobjects(L); /* just collect its objects */ else { /* closing a fully built state */ - L->ci = &L->base_ci; /* unwind CallInfo list */ + resetCI(L); luaD_closeprotected(L, 1, LUA_OK); /* close all upvalues */ + L->top.p = L->stack.p + 1; /* empty the stack to run finalizers */ luaC_freeallobjects(L); /* collect all objects */ luai_userstateclose(L); } luaM_freearray(L, G(L)->strt.hash, cast_sizet(G(L)->strt.size)); freestack(L); - lua_assert(gettotalbytes(g) == sizeof(LG)); - (*g->frealloc)(g->ud, fromstate(L), sizeof(LG), 0); /* free main block */ + lua_assert(gettotalbytes(g) == sizeof(global_State)); + (*g->frealloc)(g->ud, g, sizeof(global_State), 0); /* free main block */ } @@ -301,7 +293,7 @@ LUA_API lua_State *lua_newthread (lua_State *L) { L1->hook = L->hook; resethookcount(L1); /* initialize L1 extra space */ - memcpy(lua_getextraspace(L1), lua_getextraspace(g->mainthread), + memcpy(lua_getextraspace(L1), lua_getextraspace(mainthread(g)), LUA_EXTRASPACE); luai_userstatethread(L, L1); stack_init(L1, L); /* init stack */ @@ -320,43 +312,39 @@ void luaE_freethread (lua_State *L, lua_State *L1) { } -int luaE_resetthread (lua_State *L, int status) { - CallInfo *ci = L->ci = &L->base_ci; /* unwind CallInfo list */ - setnilvalue(s2v(L->stack.p)); /* 'function' entry for basic 'ci' */ - ci->func.p = L->stack.p; - ci->callstatus = CIST_C; +TStatus luaE_resetthread (lua_State *L, TStatus status) { + resetCI(L); if (status == LUA_YIELD) status = LUA_OK; - L->status = LUA_OK; /* so it can run __close metamethods */ status = luaD_closeprotected(L, 1, status); if (status != LUA_OK) /* errors? */ luaD_seterrorobj(L, status, L->stack.p + 1); else L->top.p = L->stack.p + 1; - ci->top.p = L->top.p + LUA_MINSTACK; - luaD_reallocstack(L, cast_int(ci->top.p - L->stack.p), 0); + luaD_reallocstack(L, cast_int(L->ci->top.p - L->stack.p), 0); return status; } LUA_API int lua_closethread (lua_State *L, lua_State *from) { - int status; + TStatus status; lua_lock(L); L->nCcalls = (from) ? getCcalls(from) : 0; status = luaE_resetthread(L, L->status); + if (L == from) /* closing itself? */ + luaD_throwbaselevel(L, status); lua_unlock(L); - return status; + return APIstatus(status); } LUA_API lua_State *lua_newstate (lua_Alloc f, void *ud, unsigned seed) { int i; lua_State *L; - global_State *g; - LG *l = cast(LG *, (*f)(ud, NULL, LUA_TTHREAD, sizeof(LG))); - if (l == NULL) return NULL; - L = &l->l.l; - g = &l->g; + global_State *g = cast(global_State*, + (*f)(ud, NULL, LUA_TTHREAD, sizeof(global_State))); + if (g == NULL) return NULL; + L = &g->mainth.l; L->tt = LUA_VTHREAD; g->currentwhite = bitmask(WHITE0BIT); L->marked = luaC_white(g); @@ -368,7 +356,6 @@ LUA_API lua_State *lua_newstate (lua_Alloc f, void *ud, unsigned seed) { g->ud = ud; g->warnf = NULL; g->ud_warn = NULL; - g->mainthread = L; g->seed = seed; g->gcstp = GCSTPGC; /* no GC while building state */ g->strt.size = g->strt.nuse = 0; @@ -386,7 +373,7 @@ LUA_API lua_State *lua_newstate (lua_Alloc f, void *ud, unsigned seed) { g->gray = g->grayagain = NULL; g->weak = g->ephemeron = g->allweak = NULL; g->twups = NULL; - g->GCtotalbytes = sizeof(LG); + g->GCtotalbytes = sizeof(global_State); g->GCmarked = 0; g->GCdebt = 0; setivalue(&g->nilvalue, 0); /* to signal that state is not yet built */ @@ -408,7 +395,7 @@ LUA_API lua_State *lua_newstate (lua_Alloc f, void *ud, unsigned seed) { LUA_API void lua_close (lua_State *L) { lua_lock(L); - L = G(L)->mainthread; /* only the main thread can be closed */ + L = mainthread(G(L)); /* only the main thread can be closed */ close_state(L); } diff --git a/lstate.h b/lstate.h index 635f41d2ec..013872835d 100644 --- a/lstate.h +++ b/lstate.h @@ -85,7 +85,7 @@ typedef struct CallInfo CallInfo; ** they must be visited again at the end of the cycle), but they are ** marked black because assignments to them must activate barriers (to ** move them back to TOUCHED1). -** - Open upvales are kept gray to avoid barriers, but they stay out +** - Open upvalues are kept gray to avoid barriers, but they stay out ** of gray lists. (They don't even have a 'gclist' field.) */ @@ -232,7 +232,7 @@ struct CallInfo { /* call is running a C function (still in first 16 bits) */ #define CIST_C (1u << (CIST_RECST + 3)) /* call is on a fresh "luaV_execute" frame */ -#define CIST_FRESH cast(l_uint32, CIST_C << 1) +#define CIST_FRESH (cast(l_uint32, CIST_C) << 1) /* function is closing tbc variables */ #define CIST_CLSRET (CIST_FRESH << 1) /* function has tbc variables to close */ @@ -249,10 +249,6 @@ struct CallInfo { #define CIST_HOOKYIELD (CIST_TAIL << 1) /* function "called" a finalizer */ #define CIST_FIN (CIST_HOOKYIELD << 1) -#if defined(LUA_COMPAT_LT_LE) -/* using __lt for __le */ -#define CIST_LEQ (CIST_FIN << 1) -#endif #define get_nresults(cs) (cast_int((cs) & CIST_NRESULTS) - 1) @@ -283,6 +279,48 @@ struct CallInfo { #define getoah(ci) (((ci)->callstatus & CIST_OAH) ? 1 : 0) +/* +** 'per thread' state +*/ +struct lua_State { + CommonHeader; + lu_byte allowhook; + TStatus status; + StkIdRel top; /* first free slot in the stack */ + struct global_State *l_G; + CallInfo *ci; /* call info for current function */ + StkIdRel stack_last; /* end of stack (last element + 1) */ + StkIdRel stack; /* stack base */ + UpVal *openupval; /* list of open upvalues in this stack */ + StkIdRel tbclist; /* list of to-be-closed variables */ + GCObject *gclist; + 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 host) */ + volatile lua_Hook hook; + ptrdiff_t errfunc; /* current error handling function (stack index) */ + l_uint32 nCcalls; /* number of nested non-yieldable or C calls */ + int oldpc; /* last pc traced */ + int nci; /* number of items in 'ci' list */ + int basehookcount; + int hookcount; + volatile l_signalT hookmask; + struct { /* info about transferred values (for call/return hooks) */ + int ftransfer; /* offset of first value transferred */ + int ntransfer; /* number of values transferred */ + } transferinfo; +}; + + +/* +** thread state + extra space +*/ +typedef struct LX { + lu_byte extra_[LUA_EXTRASPACE]; + lua_State l; +} LX; + + /* ** 'global state', shared by all threads of this state */ @@ -324,50 +362,18 @@ typedef struct global_State { GCObject *finobjrold; /* list of really old objects with finalizers */ struct lua_State *twups; /* list of threads with open upvalues */ lua_CFunction panic; /* to be called in unprotected errors */ - struct lua_State *mainthread; TString *memerrmsg; /* message for memory-allocation errors */ TString *tmname[TM_N]; /* array with tag-method names */ struct Table *mt[LUA_NUMTYPES]; /* metatables for basic types */ TString *strcache[STRCACHE_N][STRCACHE_M]; /* cache for strings in API */ lua_WarnFunction warnf; /* warning function */ void *ud_warn; /* auxiliary data to 'warnf' */ + LX mainth; /* main thread of this state */ } global_State; -/* -** 'per thread' state -*/ -struct lua_State { - CommonHeader; - lu_byte status; - lu_byte allowhook; - unsigned short nci; /* number of items in 'ci' list */ - StkIdRel top; /* first free slot in the stack */ - global_State *l_G; - CallInfo *ci; /* call info for current function */ - StkIdRel stack_last; /* end of stack (last element + 1) */ - StkIdRel stack; /* stack base */ - UpVal *openupval; /* list of open upvalues in this stack */ - StkIdRel tbclist; /* list of to-be-closed variables */ - GCObject *gclist; - 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) */ - volatile lua_Hook hook; - ptrdiff_t errfunc; /* current error handling function (stack index) */ - l_uint32 nCcalls; /* number of nested (non-yieldable | C) calls */ - int oldpc; /* last pc traced */ - int basehookcount; - int hookcount; - volatile l_signalT hookmask; - struct { /* info about transferred values (for call/return hooks) */ - int ftransfer; /* offset of first value transferred */ - int ntransfer; /* number of values transferred */ - } transferinfo; -}; - - #define G(L) (L->l_G) +#define mainthread(G) (&(G)->mainth.l) /* ** 'g->nilvalue' being a nil value flags that the state was completely @@ -420,9 +426,9 @@ union GCUnion { /* ** macro to convert a Lua object into a GCObject -** (The access to 'tt' tries to ensure that 'v' is actually a Lua object.) */ -#define obj2gco(v) check_exp((v)->tt >= LUA_TSTRING, &(cast_u(v)->gc)) +#define obj2gco(v) \ + check_exp(novariant((v)->tt) >= LUA_TSTRING, &(cast_u(v)->gc)) /* actual number of total memory allocated */ @@ -432,13 +438,13 @@ union GCUnion { LUAI_FUNC void luaE_setdebt (global_State *g, l_mem debt); LUAI_FUNC void luaE_freethread (lua_State *L, lua_State *L1); LUAI_FUNC lu_mem luaE_threadsize (lua_State *L); -LUAI_FUNC CallInfo *luaE_extendCI (lua_State *L); +LUAI_FUNC CallInfo *luaE_extendCI (lua_State *L, int err); LUAI_FUNC void luaE_shrinkCI (lua_State *L); LUAI_FUNC void luaE_checkcstack (lua_State *L); LUAI_FUNC void luaE_incCstack (lua_State *L); LUAI_FUNC void luaE_warning (lua_State *L, const char *msg, int tocont); LUAI_FUNC void luaE_warnerror (lua_State *L, const char *where); -LUAI_FUNC int luaE_resetthread (lua_State *L, int status); +LUAI_FUNC TStatus luaE_resetthread (lua_State *L, TStatus status); #endif diff --git a/lstring.c b/lstring.c index 0c89a51b07..75635142e9 100644 --- a/lstring.c +++ b/lstring.c @@ -39,18 +39,18 @@ /* -** equality for long strings +** generic equality for strings */ -int luaS_eqlngstr (TString *a, TString *b) { - size_t len = a->u.lnglen; - lua_assert(a->tt == LUA_VLNGSTR && b->tt == LUA_VLNGSTR); - return (a == b) || /* same instance or... */ - ((len == b->u.lnglen) && /* equal length and ... */ - (memcmp(getlngstr(a), getlngstr(b), len) == 0)); /* equal contents */ +int luaS_eqstr (TString *a, TString *b) { + size_t len1, len2; + const char *s1 = getlstr(a, len1); + const char *s2 = getlstr(b, len2); + return ((len1 == len2) && /* equal length and ... */ + (memcmp(s1, s2, len1) == 0)); /* equal contents */ } -unsigned luaS_hash (const char *str, size_t l, unsigned seed) { +static unsigned luaS_hash (const char *str, size_t l, unsigned seed) { unsigned int h = seed ^ cast_uint(l); for (; l > 0; l--) h ^= ((h<<5) + (h>>2) + cast_byte(str[l - 1])); @@ -315,28 +315,9 @@ static void f_newext (lua_State *L, void *ud) { } -static void f_pintern (lua_State *L, void *ud) { - struct NewExt *ne = cast(struct NewExt *, ud); - ne->ts = internshrstr(L, ne->s, ne->len); -} - - TString *luaS_newextlstr (lua_State *L, const char *s, size_t len, lua_Alloc falloc, void *ud) { struct NewExt ne; - if (len <= LUAI_MAXSHORTLEN) { /* short string? */ - ne.s = s; ne.len = len; - if (!falloc) - f_pintern(L, &ne); /* just internalize string */ - else { - int status = luaD_rawrunprotected(L, f_pintern, &ne); - (*falloc)(ud, cast_voidp(s), len + 1, 0); /* free external string */ - if (status != LUA_OK) /* memory error? */ - luaM_error(L); /* re-raise memory error */ - } - return ne.ts; - } - /* "normal" case: long strings */ if (!falloc) { ne.kind = LSTRFIX; f_newext(L, &ne); /* just create header */ @@ -357,3 +338,16 @@ TString *luaS_newextlstr (lua_State *L, } +/* +** Normalize an external string: If it is short, internalize it. +*/ +TString *luaS_normstr (lua_State *L, TString *ts) { + size_t len = ts->u.lnglen; + if (len > LUAI_MAXSHORTLEN) + return ts; /* long string; keep the original */ + else { + const char *str = getlngstr(ts); + return internshrstr(L, str, len); + } +} + diff --git a/lstring.h b/lstring.h index 1751e0434e..1643c3d82b 100644 --- a/lstring.h +++ b/lstring.h @@ -54,9 +54,8 @@ #define eqshrstr(a,b) check_exp((a)->tt == LUA_VSHRSTR, (a) == (b)) -LUAI_FUNC unsigned luaS_hash (const char *str, size_t l, unsigned seed); LUAI_FUNC unsigned luaS_hashlongstr (TString *ts); -LUAI_FUNC int luaS_eqlngstr (TString *a, TString *b); +LUAI_FUNC int luaS_eqstr (TString *a, TString *b); LUAI_FUNC void luaS_resize (lua_State *L, int newsize); LUAI_FUNC void luaS_clearcache (global_State *g); LUAI_FUNC void luaS_init (lua_State *L); @@ -69,5 +68,6 @@ LUAI_FUNC TString *luaS_createlngstrobj (lua_State *L, size_t l); LUAI_FUNC TString *luaS_newextlstr (lua_State *L, const char *s, size_t len, lua_Alloc falloc, void *ud); LUAI_FUNC size_t luaS_sizelngstr (size_t len, int kind); +LUAI_FUNC TString *luaS_normstr (lua_State *L, TString *ts); #endif diff --git a/lstrlib.c b/lstrlib.c index 321d6a0b0a..dd3c0fd08c 100644 --- a/lstrlib.c +++ b/lstrlib.c @@ -132,27 +132,31 @@ static int str_upper (lua_State *L) { } +/* +** MAX_SIZE is limited both by size_t and lua_Integer. +** When x <= MAX_SIZE, x can be safely cast to size_t or lua_Integer. +*/ static int str_rep (lua_State *L) { - size_t l, lsep; - const char *s = luaL_checklstring(L, 1, &l); + size_t len, lsep; + const char *s = luaL_checklstring(L, 1, &len); lua_Integer n = luaL_checkinteger(L, 2); const char *sep = luaL_optlstring(L, 3, "", &lsep); - if (n <= 0) - lua_pushliteral(L, ""); - else if (l_unlikely(l + lsep < l || l + lsep > MAX_SIZE / cast_sizet(n))) + if (n <= 0 || (len | lsep) == 0) + lua_pushliteral(L, ""); /* no repetitions or both strings empty */ + else if (l_unlikely(len > MAX_SIZE - lsep || + cast_st2S(len + lsep) > cast_st2S(MAX_SIZE) / n)) return luaL_error(L, "resulting string too large"); else { - size_t totallen = ((size_t)n * (l + lsep)) - lsep; + size_t totallen = (cast_sizet(n) * (len + lsep)) - lsep; luaL_Buffer b; char *p = luaL_buffinitsize(L, &b, totallen); while (n-- > 1) { /* first n-1 copies (followed by separator) */ - memcpy(p, s, l * sizeof(char)); p += l; + memcpy(p, s, len * sizeof(char)); p += len; if (lsep > 0) { /* empty 'memcpy' is not that cheap */ - memcpy(p, sep, lsep * sizeof(char)); - p += lsep; + memcpy(p, sep, lsep * sizeof(char)); p += lsep; } } - memcpy(p, s, l * sizeof(char)); /* last copy (not followed by separator) */ + memcpy(p, s, len * sizeof(char)); /* last copy without separator */ luaL_pushresultsize(&b, totallen); } return 1; @@ -265,11 +269,18 @@ static int tonum (lua_State *L, int arg) { } -static void trymt (lua_State *L, const char *mtname) { +/* +** To be here, either the first operand was a string or the first +** operand didn't have a corresponding metamethod. (Otherwise, that +** other metamethod would have been called.) So, if this metamethod +** doesn't work, the only other option would be for the second +** operand to have a different metamethod. +*/ +static void trymt (lua_State *L, const char *mtkey, const char *opname) { lua_settop(L, 2); /* back to the original arguments */ if (l_unlikely(lua_type(L, 2) == LUA_TSTRING || - !luaL_getmetafield(L, 2, mtname))) - luaL_error(L, "attempt to %s a '%s' with a '%s'", mtname + 2, + !luaL_getmetafield(L, 2, mtkey))) + luaL_error(L, "attempt to %s a '%s' with a '%s'", opname, luaL_typename(L, -2), luaL_typename(L, -1)); lua_insert(L, -3); /* put metamethod before arguments */ lua_call(L, 2, 1); /* call metamethod */ @@ -280,7 +291,7 @@ static int arith (lua_State *L, int op, const char *mtname) { if (tonum(L, 1) && tonum(L, 2)) lua_arith(L, op); /* result will be on the top */ else - trymt(L, mtname); + trymt(L, mtname, mtname + 2); return 1; } @@ -746,19 +757,25 @@ static int nospecials (const char *p, size_t l) { } +/* +** Prepare state for matches. These fields are not affected by each match. +*/ static void prepstate (MatchState *ms, lua_State *L, const char *s, size_t ls, const char *p, size_t lp) { ms->L = L; - ms->matchdepth = MAXCCALLS; ms->src_init = s; ms->src_end = s + ls; ms->p_end = p + lp; } +/* +** (Re)prepare state for a match, setting fields that change during +** each match. +*/ static void reprepstate (MatchState *ms) { + ms->matchdepth = MAXCCALLS; ms->level = 0; - lua_assert(ms->matchdepth == MAXCCALLS); } @@ -957,7 +974,7 @@ static int str_gsub (lua_State *L) { reprepstate(&ms); /* (re)prepare state for new match */ if ((e = match(&ms, src, p)) != NULL && e != lastmatch) { /* match? */ n++; - changed = add_value(&ms, &b, src, e, tr) | changed; + changed = add_value(&ms, &b, src, e, tr) || changed; src = lastmatch = e; } else if (src < ms.src_end) /* otherwise, skip one character */ @@ -1544,8 +1561,10 @@ static KOption getdetails (Header *h, size_t totalsize, const char **fmt, else { if (align > h->maxalign) /* enforce maximum alignment */ align = h->maxalign; - if (l_unlikely(!ispow2(align))) /* not a power of 2? */ + if (l_unlikely(!ispow2(align))) { /* not a power of 2? */ + *ntoalign = 0; /* to avoid warnings */ luaL_argerror(h->L, 1, "format asks for alignment not power of 2"); + } else { /* 'szmoda' = totalsize % align */ unsigned szmoda = cast_uint(totalsize & (align - 1)); @@ -1713,7 +1732,7 @@ static int str_packsize (lua_State *L) { luaL_argcheck(L, opt != Kstring && opt != Kzstr, 1, "variable-length format"); size += ntoalign; /* total space used by option */ - luaL_argcheck(L, totalsize <= LUA_MAXINTEGER - size, + luaL_argcheck(L, totalsize <= MAX_SIZE - size, 1, "format result too large"); totalsize += size; } @@ -1809,8 +1828,8 @@ static int str_unpack (lua_State *L) { lua_Unsigned len = (lua_Unsigned)unpackint(L, data + pos, h.islittle, cast_int(size), 0); luaL_argcheck(L, len <= ld - pos - size, 2, "data string too short"); - lua_pushlstring(L, data + pos + size, len); - pos += len; /* skip string */ + lua_pushlstring(L, data + pos + size, cast_sizet(len)); + pos += cast_sizet(len); /* skip string */ break; } case Kzstr: { diff --git a/ltable.c b/ltable.c index 8df9a4fbfe..2f2b5c1f5c 100644 --- a/ltable.c +++ b/ltable.c @@ -67,7 +67,7 @@ typedef union { ** MAXABITS is the largest integer such that 2^MAXABITS fits in an ** unsigned int. */ -#define MAXABITS cast_int(sizeof(int) * CHAR_BIT - 1) +#define MAXABITS (l_numbits(int) - 1) /* @@ -156,7 +156,7 @@ static Node *hashint (const Table *t, lua_Integer i) { ** The main computation should be just ** n = frexp(n, &i); return (n * INT_MAX) + i ** but there are some numerical subtleties. -** In a two-complement representation, INT_MAX does not has an exact +** In a two-complement representation, INT_MAX may not have an exact ** representation as a float, but INT_MIN does; because the absolute ** value of 'frexp' is smaller than 1 (unless 'n' is inf/NaN), the ** absolute value of the product 'frexp * -INT_MIN' is smaller or equal @@ -234,41 +234,51 @@ l_sinline Node *mainpositionfromnode (const Table *t, Node *nd) { ** Check whether key 'k1' is equal to the key in node 'n2'. This ** equality is raw, so there are no metamethods. Floats with integer ** values have been normalized, so integers cannot be equal to -** floats. It is assumed that 'eqshrstr' is simply pointer equality, so -** that short strings are handled in the default case. -** A true 'deadok' means to accept dead keys as equal to their original -** values. All dead keys are compared in the default case, by pointer -** identity. (Only collectable objects can produce dead keys.) Note that -** dead long strings are also compared by identity. -** Once a key is dead, its corresponding value may be collected, and -** then another value can be created with the same address. If this -** other value is given to 'next', 'equalkey' will signal a false -** positive. In a regular traversal, this situation should never happen, -** as all keys given to 'next' came from the table itself, and therefore -** could not have been collected. Outside a regular traversal, we -** have garbage in, garbage out. What is relevant is that this false -** positive does not break anything. (In particular, 'next' will return -** some other valid item on the table or nil.) +** floats. It is assumed that 'eqshrstr' is simply pointer equality, +** so that short strings are handled in the default case. The flag +** 'deadok' means to accept dead keys as equal to their original values. +** (Only collectable objects can produce dead keys.) Note that dead +** long strings are also compared by identity. Once a key is dead, +** its corresponding value may be collected, and then another value +** can be created with the same address. If this other value is given +** to 'next', 'equalkey' will signal a false positive. In a regular +** traversal, this situation should never happen, as all keys given to +** 'next' came from the table itself, and therefore could not have been +** collected. Outside a regular traversal, we have garbage in, garbage +** out. What is relevant is that this false positive does not break +** anything. (In particular, 'next' will return some other valid item +** on the table or nil.) */ static int equalkey (const TValue *k1, const Node *n2, int deadok) { - if ((rawtt(k1) != keytt(n2)) && /* not the same variants? */ - !(deadok && keyisdead(n2) && iscollectable(k1))) - return 0; /* cannot be same key */ - switch (keytt(n2)) { - case LUA_VNIL: case LUA_VFALSE: case LUA_VTRUE: - return 1; - case LUA_VNUMINT: - return (ivalue(k1) == keyival(n2)); - case LUA_VNUMFLT: - return luai_numeq(fltvalue(k1), fltvalueraw(keyval(n2))); - case LUA_VLIGHTUSERDATA: - return pvalue(k1) == pvalueraw(keyval(n2)); - case LUA_VLCF: - return fvalue(k1) == fvalueraw(keyval(n2)); - case ctb(LUA_VLNGSTR): - return luaS_eqlngstr(tsvalue(k1), keystrval(n2)); - default: + if (rawtt(k1) != keytt(n2)) { /* not the same variants? */ + if (keyisshrstr(n2) && ttislngstring(k1)) { + /* an external string can be equal to a short-string key */ + return luaS_eqstr(tsvalue(k1), keystrval(n2)); + } + else if (deadok && keyisdead(n2) && iscollectable(k1)) { + /* a collectable value can be equal to a dead key */ return gcvalue(k1) == gcvalueraw(keyval(n2)); + } + else + return 0; /* otherwise, different variants cannot be equal */ + } + else { /* equal variants */ + switch (keytt(n2)) { + case LUA_VNIL: case LUA_VFALSE: case LUA_VTRUE: + return 1; + case LUA_VNUMINT: + return (ivalue(k1) == keyival(n2)); + case LUA_VNUMFLT: + return luai_numeq(fltvalue(k1), fltvalueraw(keyval(n2))); + case LUA_VLIGHTUSERDATA: + return pvalue(k1) == pvalueraw(keyval(n2)); + case LUA_VLCF: + return fvalue(k1) == fvalueraw(keyval(n2)); + case ctb(LUA_VLNGSTR): + return luaS_eqstr(tsvalue(k1), keystrval(n2)); + default: + return gcvalue(k1) == gcvalueraw(keyval(n2)); + } } } @@ -641,10 +651,9 @@ static void reinserthash (lua_State *L, Table *ot, Table *t) { /* -** Exchange the hash part of 't1' and 't2'. (In 'flags', only the -** dummy bit must be exchanged: The 'isrealasize' is not related -** to the hash part, and the metamethod bits do not change during -** a resize, so the "real" table can keep their values.) +** Exchange the hash part of 't1' and 't2'. (In 'flags', only the dummy +** bit must be exchanged: The metamethod bits do not change during a +** resize, so the "real" table can keep their values.) */ static void exchangehashpart (Table *t1, Table *t2) { lu_byte lsizenode = t1->lsizenode; @@ -1146,19 +1155,28 @@ void luaH_finishset (lua_State *L, Table *t, const TValue *key, lua_assert(hres != HOK); if (hres == HNOTFOUND) { TValue aux; + const TValue *actk = key; /* actual key to insert */ if (l_unlikely(ttisnil(key))) luaG_runerror(L, "table index is nil"); else if (ttisfloat(key)) { lua_Number f = fltvalue(key); lua_Integer k; - if (luaV_flttointeger(f, &k, F2Ieq)) { - setivalue(&aux, k); /* key is equal to an integer */ - key = &aux; /* insert it as an integer */ + if (luaV_flttointeger(f, &k, F2Ieq)) { /* is key equal to an integer? */ + setivalue(&aux, k); + actk = &aux; /* use the integer as the key */ } else if (l_unlikely(luai_numisnan(f))) luaG_runerror(L, "table index is NaN"); } - luaH_newkey(L, t, key, value); + else if (isextstr(key)) { /* external string? */ + /* If string is short, must internalize it to be used as table key */ + TString *ts = luaS_normstr(L, tsvalue(key)); + setsvalue2s(L, L->top.p++, ts); /* anchor 'ts' (EXTRA_STACK) */ + luaH_newkey(L, t, s2v(L->top.p - 1), value); + L->top.p--; + return; + } + luaH_newkey(L, t, actk, value); } else if (hres > 0) { /* regular Node? */ setobj2t(L, gval(gnode(t, hres - HFIRSTNODE)), value); @@ -1202,24 +1220,36 @@ void luaH_setint (lua_State *L, Table *t, lua_Integer key, TValue *value) { /* ** Try to find a boundary in the hash part of table 't'. From the -** caller, we know that 'j' is zero or present and that 'j + 1' is -** present. We want to find a larger key that is absent from the -** table, so that we can do a binary search between the two keys to -** find a boundary. We keep doubling 'j' until we get an absent index. -** If the doubling would overflow, we try LUA_MAXINTEGER. If it is -** absent, we are ready for the binary search. ('j', being max integer, -** is larger or equal to 'i', but it cannot be equal because it is -** absent while 'i' is present; so 'j > i'.) Otherwise, 'j' is a -** boundary. ('j + 1' cannot be a present integer key because it is -** not a valid integer in Lua.) +** caller, we know that 'asize + 1' is present. We want to find a larger +** key that is absent from the table, so that we can do a binary search +** between the two keys to find a boundary. We keep doubling 'j' until +** we get an absent index. If the doubling would overflow, we try +** LUA_MAXINTEGER. If it is absent, we are ready for the binary search. +** ('j', being max integer, is larger or equal to 'i', but it cannot be +** equal because it is absent while 'i' is present.) Otherwise, 'j' is a +** boundary. ('j + 1' cannot be a present integer key because it is not +** a valid integer in Lua.) +** About 'rnd': If we used a fixed algorithm, a bad actor could fill +** a table with only the keys that would be probed, in such a way that +** a small table could result in a huge length. To avoid that, we use +** the state's seed as a source of randomness. For the first probe, +** we "randomly double" 'i' by adding to it a random number roughly its +** width. */ -static lua_Unsigned hash_search (Table *t, lua_Unsigned j) { - lua_Unsigned i; - if (j == 0) j++; /* the caller ensures 'j + 1' is present */ - do { +static lua_Unsigned hash_search (lua_State *L, Table *t, unsigned asize) { + lua_Unsigned i = asize + 1; /* caller ensures t[i] is present */ + unsigned rnd = G(L)->seed; + int n = (asize > 0) ? luaO_ceillog2(asize) : 0; /* width of 'asize' */ + unsigned mask = (1u << n) - 1; /* 11...111 with the width of 'asize' */ + unsigned incr = (rnd & mask) + 1; /* first increment (at least 1) */ + lua_Unsigned j = (incr <= l_castS2U(LUA_MAXINTEGER) - i) ? i + incr : i + 1; + rnd >>= n; /* used 'n' bits from 'rnd' */ + while (!hashkeyisempty(t, j)) { /* repeat until an absent t[j] */ i = j; /* 'i' is a present index */ - if (j <= l_castS2U(LUA_MAXINTEGER) / 2) - j *= 2; + if (j <= l_castS2U(LUA_MAXINTEGER)/2 - 1) { + j = j*2 + (rnd & 1); /* try again with 2j or 2j+1 */ + rnd >>= 1; + } else { j = LUA_MAXINTEGER; if (hashkeyisempty(t, j)) /* t[j] not present? */ @@ -1227,7 +1257,7 @@ static lua_Unsigned hash_search (Table *t, lua_Unsigned j) { else /* weird case */ return j; /* well, max integer is a boundary... */ } - } while (!hashkeyisempty(t, j)); /* repeat until an absent t[j] */ + } /* i < j && t[i] present && t[j] absent */ while (j - i > 1u) { /* do a binary search between them */ lua_Unsigned m = (i + j) / 2; @@ -1268,7 +1298,7 @@ static lua_Unsigned newhint (Table *t, unsigned hint) { ** If there is no array part, or its last element is non empty, the ** border may be in the hash part. */ -lua_Unsigned luaH_getn (Table *t) { +lua_Unsigned luaH_getn (lua_State *L, Table *t) { unsigned asize = t->asize; if (asize > 0) { /* is there an array part? */ const unsigned maxvicinity = 4; @@ -1309,7 +1339,7 @@ lua_Unsigned luaH_getn (Table *t) { if (isdummy(t) || hashkeyisempty(t, asize + 1)) return asize; /* 'asize + 1' is empty */ else /* 'asize + 1' is also non empty */ - return hash_search(t, asize); + return hash_search(L, t, asize); } diff --git a/ltable.h b/ltable.h index ca21e69202..f3b7bc7e7e 100644 --- a/ltable.h +++ b/ltable.h @@ -173,7 +173,7 @@ LUAI_FUNC void luaH_resizearray (lua_State *L, Table *t, unsigned nasize); LUAI_FUNC lu_mem luaH_size (Table *t); LUAI_FUNC void luaH_free (lua_State *L, Table *t); LUAI_FUNC int luaH_next (lua_State *L, Table *t, StkId key); -LUAI_FUNC lua_Unsigned luaH_getn (Table *t); +LUAI_FUNC lua_Unsigned luaH_getn (lua_State *L, Table *t); #if defined(LUA_DEBUG) diff --git a/ltablib.c b/ltablib.c index 46ecb5e024..15c3c09f04 100644 --- a/ltablib.c +++ b/ltablib.c @@ -42,15 +42,17 @@ static int checkfield (lua_State *L, const char *key, int n) { /* ** Check that 'arg' either is a table or can behave like one (that is, -** has a metatable with the required metamethods) +** has a metatable with the required metamethods). */ static void checktab (lua_State *L, int arg, int what) { - if (lua_type(L, arg) != LUA_TTABLE) { /* is it not a table? */ + int tp = lua_type(L, arg); + if (tp != LUA_TTABLE) { /* is it not a table? */ int n = 1; /* number of elements to pop */ if (lua_getmetatable(L, arg) && /* must have metatable */ (!(what & TAB_R) || checkfield(L, "__index", ++n)) && (!(what & TAB_W) || checkfield(L, "__newindex", ++n)) && - (!(what & TAB_L) || checkfield(L, "__len", ++n))) { + (!(what & TAB_L) || /* strings don't need '__len' to have a length */ + tp == LUA_TSTRING || checkfield(L, "__len", ++n))) { lua_pop(L, n); /* pop metatable and tested metamethods */ } else @@ -204,8 +206,9 @@ static int tpack (lua_State *L) { static int tunpack (lua_State *L) { lua_Unsigned n; + lua_Integer len = aux_getn(L, 1, TAB_R); lua_Integer i = luaL_optinteger(L, 2, 1); - lua_Integer e = luaL_opt(L, luaL_checkinteger, 3, luaL_len(L, 1)); + lua_Integer e = luaL_opt(L, luaL_checkinteger, 3, len); if (i > e) return 0; /* empty range */ n = l_castS2U(e) - l_castS2U(i); /* number of elements minus 1 */ if (l_unlikely(n >= (unsigned int)INT_MAX || diff --git a/ltests.c b/ltests.c index 6b5dc27643..2bf5545a49 100644 --- a/ltests.c +++ b/ltests.c @@ -164,13 +164,13 @@ static void warnf (void *ud, const char *msg, int tocont) { #define MARK 0x55 /* 01010101 (a nice pattern) */ -typedef union Header { +typedef union memHeader { LUAI_MAXALIGN; struct { size_t size; int type; } d; -} Header; +} memHeader; #if !defined(EXTERNMEMCHECK) @@ -193,14 +193,14 @@ Memcontrol l_memcontrol = {0UL, 0UL, 0UL, 0UL, 0UL, 0UL, 0UL, 0UL, 0UL}}; -static void freeblock (Memcontrol *mc, Header *block) { +static void freeblock (Memcontrol *mc, memHeader *block) { if (block) { size_t size = block->d.size; int i; for (i = 0; i < MARKSIZE; i++) /* check marks after block */ lua_assert(*(cast_charp(block + 1) + size + i) == MARK); mc->objcount[block->d.type]--; - fillmem(block, sizeof(Header) + size + MARKSIZE); /* erase block */ + fillmem(block, sizeof(memHeader) + size + MARKSIZE); /* erase block */ free(block); /* actually free block */ mc->numblocks--; /* update counts */ mc->total -= size; @@ -210,7 +210,7 @@ static void freeblock (Memcontrol *mc, Header *block) { void *debug_realloc (void *ud, void *b, size_t oldsize, size_t size) { Memcontrol *mc = cast(Memcontrol *, ud); - Header *block = cast(Header *, b); + memHeader *block = cast(memHeader *, b); int type; if (mc->memlimit == 0) { /* first time? */ char *limit = getenv("MEMLIMIT"); /* initialize memory limit */ @@ -241,12 +241,12 @@ void *debug_realloc (void *ud, void *b, size_t oldsize, size_t size) { if (size > oldsize && mc->total+size-oldsize > mc->memlimit) return NULL; /* fake a memory allocation error */ else { - Header *newblock; + memHeader *newblock; int i; size_t commonsize = (oldsize < size) ? oldsize : size; - size_t realsize = sizeof(Header) + size + MARKSIZE; + size_t realsize = sizeof(memHeader) + size + MARKSIZE; if (realsize < size) return NULL; /* arithmetic overflow! */ - newblock = cast(Header *, malloc(realsize)); /* alloc a new block */ + newblock = cast(memHeader *, malloc(realsize)); /* alloc a new block */ if (newblock == NULL) return NULL; /* really out of memory? */ if (block) { @@ -327,37 +327,40 @@ void lua_printobj (lua_State *L, struct GCObject *o) { void lua_printvalue (TValue *v) { - switch (ttype(v)) { - case LUA_TNUMBER: { + switch (ttypetag(v)) { + case LUA_VNUMINT: case LUA_VNUMFLT: { char buff[LUA_N2SBUFFSZ]; unsigned len = luaO_tostringbuff(v, buff); buff[len] = '\0'; printf("%s", buff); break; } - case LUA_TSTRING: { - printf("'%s'", getstr(tsvalue(v))); - break; - } - case LUA_TBOOLEAN: { - printf("%s", (!l_isfalse(v) ? "true" : "false")); - break; - } - case LUA_TLIGHTUSERDATA: { - printf("light udata: %p", pvalue(v)); - break; - } - case LUA_TNIL: { - printf("nil"); - break; - } - default: { - if (ttislcf(v)) - printf("light C function: %p", fvalue(v)); - else /* must be collectable */ - printf("%s: %p", ttypename(ttype(v)), gcvalue(v)); - break; - } + case LUA_VSHRSTR: + printf("'%s'", getstr(tsvalue(v))); break; + case LUA_VLNGSTR: + printf("'%.30s...'", getstr(tsvalue(v))); break; + case LUA_VFALSE: + printf("%s", "false"); break; + case LUA_VTRUE: + printf("%s", "true"); break; + case LUA_VLIGHTUSERDATA: + printf("light udata: %p", pvalue(v)); break; + case LUA_VUSERDATA: + printf("full udata: %p", uvalue(v)); break; + case LUA_VNIL: + printf("nil"); break; + case LUA_VLCF: + printf("light C function: %p", fvalue(v)); break; + case LUA_VCCL: + printf("C closure: %p", clCvalue(v)); break; + case LUA_VLCL: + printf("Lua function: %p", clLvalue(v)); break; + case LUA_VTHREAD: + printf("thread: %p", thvalue(v)); break; + case LUA_VTABLE: + printf("table: %p", hvalue(v)); break; + default: + lua_assert(0); } } @@ -408,7 +411,7 @@ static void checktable (global_State *g, Table *h) { for (n = gnode(h, 0); n < limit; n++) { if (!isempty(gval(n))) { TValue k; - getnodekey(g->mainthread, &k, n); + getnodekey(mainthread(g), &k, n); assert(!keyisnil(n)); checkvalref(g, hgc, &k); checkvalref(g, hgc, gval(n)); @@ -477,7 +480,7 @@ static int lua_checkpc (CallInfo *ci) { } -static void checkstack (global_State *g, lua_State *L1) { +static void check_stack (global_State *g, lua_State *L1) { StkId o; CallInfo *ci; UpVal *uv; @@ -514,7 +517,7 @@ static void checkrefs (global_State *g, GCObject *o) { break; } case LUA_VTHREAD: { - checkstack(g, gco2th(o)); + check_stack(g, gco2th(o)); break; } case LUA_VLCL: { @@ -672,7 +675,7 @@ int lua_checkmemory (lua_State *L) { l_mem totalin; /* total of objects that are in gray lists */ l_mem totalshould; /* total of objects that should be in gray lists */ if (keepinvariant(g)) { - assert(!iswhite(g->mainthread)); + assert(!iswhite(mainthread(g))); assert(!iswhite(gcvalue(&g->l_registry))); } assert(!isdead(g, gcvalue(&g->l_registry))); @@ -872,6 +875,28 @@ void lua_printstack (lua_State *L) { } +int lua_printallstack (lua_State *L) { + StkId p; + int i = 1; + CallInfo *ci = &L->base_ci; + printf("stack: >>\n"); + for (p = L->stack.p; p < L->top.p; p++) { + if (ci != NULL && p == ci->func.p) { + printf(" ---\n"); + if (ci == L->ci) + ci = NULL; /* printed last frame */ + else + ci = ci->next; + } + printf("%3d: ", i++); + lua_printvalue(s2v(p)); + printf("\n"); + } + printf("<<\n"); + return 0; +} + + static int get_limits (lua_State *L) { lua_createtable(L, 0, 5); setnameval(L, "IS32INT", LUAI_IS32INT); @@ -883,11 +908,22 @@ static int get_limits (lua_State *L) { } +static int get_sizes (lua_State *L) { + lua_newtable(L); + setnameval(L, "Lua state", sizeof(lua_State)); + setnameval(L, "global state", sizeof(global_State)); + setnameval(L, "TValue", sizeof(TValue)); + setnameval(L, "Node", sizeof(Node)); + setnameval(L, "stack Value", sizeof(StackValue)); + return 1; +} + + static int mem_query (lua_State *L) { if (lua_isnone(L, 1)) { - lua_pushinteger(L, cast(lua_Integer, l_memcontrol.total)); - lua_pushinteger(L, cast(lua_Integer, l_memcontrol.numblocks)); - lua_pushinteger(L, cast(lua_Integer, l_memcontrol.maxmem)); + lua_pushinteger(L, cast_Integer(l_memcontrol.total)); + lua_pushinteger(L, cast_Integer(l_memcontrol.numblocks)); + lua_pushinteger(L, cast_Integer(l_memcontrol.maxmem)); return 3; } else if (lua_isnumber(L, 1)) { @@ -901,7 +937,7 @@ static int mem_query (lua_State *L) { int i; for (i = LUA_NUMTYPES - 1; i >= 0; i--) { if (strcmp(t, ttypename(i)) == 0) { - lua_pushinteger(L, cast(lua_Integer, l_memcontrol.objcount[i])); + lua_pushinteger(L, cast_Integer(l_memcontrol.objcount[i])); return 1; } } @@ -982,7 +1018,7 @@ static int gc_printobj (lua_State *L) { } -static const char *statenames[] = { +static const char *const statenames[] = { "propagate", "enteratomic", "atomic", "sweepallgc", "sweepfinobj", "sweeptobefnz", "sweepend", "callfin", "pause", ""}; @@ -1041,15 +1077,19 @@ static int tracegc (lua_State *L) { static int hash_query (lua_State *L) { if (lua_isnone(L, 2)) { + TString *ts; luaL_argcheck(L, lua_type(L, 1) == LUA_TSTRING, 1, "string expected"); - lua_pushinteger(L, cast_int(tsvalue(obj_at(L, 1))->hash)); + ts = tsvalue(obj_at(L, 1)); + if (ts->tt == LUA_VLNGSTR) + luaS_hashlongstr(ts); /* make sure long string has a hash */ + lua_pushinteger(L, cast_int(ts->hash)); } else { TValue *o = obj_at(L, 1); Table *t; luaL_checktype(L, 2, LUA_TTABLE); t = hvalue(obj_at(L, 2)); - lua_pushinteger(L, cast(lua_Integer, luaH_mainposition(t, o) - t->node)); + lua_pushinteger(L, cast_Integer(luaH_mainposition(t, o) - t->node)); } return 1; } @@ -1057,15 +1097,36 @@ static int hash_query (lua_State *L) { static int stacklevel (lua_State *L) { int a = 0; - lua_pushinteger(L, cast(lua_Integer, L->top.p - L->stack.p)); + lua_pushinteger(L, cast_Integer(L->top.p - L->stack.p)); lua_pushinteger(L, stacksize(L)); - lua_pushinteger(L, cast(lua_Integer, L->nCcalls)); + lua_pushinteger(L, cast_Integer(L->nCcalls)); lua_pushinteger(L, L->nci); lua_pushinteger(L, (lua_Integer)(size_t)&a); return 5; } +static int resetCI (lua_State *L) { + CallInfo *ci = L->ci; + while (ci->next != NULL) { + CallInfo *tofree = ci->next; + ci->next = ci->next->next; + luaM_free(L, tofree); + L->nci--; + } + return 0; +} + + +static int reallocstack (lua_State *L) { + int n = cast_int(luaL_checkinteger(L, 1)); + lua_lock(L); + luaD_reallocstack(L, cast_int(L->top.p - L->stack.p) + n, 1); + lua_unlock(L); + return 0; +} + + static int table_query (lua_State *L) { const Table *t; int i = cast_int(luaL_optinteger(L, 2, -1)); @@ -1074,9 +1135,9 @@ static int table_query (lua_State *L) { t = hvalue(obj_at(L, 1)); asize = t->asize; if (i == -1) { - lua_pushinteger(L, cast(lua_Integer, asize)); - lua_pushinteger(L, cast(lua_Integer, allocsizenode(t))); - lua_pushinteger(L, cast(lua_Integer, asize > 0 ? *lenhint(t) : 0)); + lua_pushinteger(L, cast_Integer(asize)); + lua_pushinteger(L, cast_Integer(allocsizenode(t))); + lua_pushinteger(L, cast_Integer(asize > 0 ? *lenhint(t) : 0)); return 3; } else if (cast_uint(i) < asize) { @@ -1084,7 +1145,7 @@ static int table_query (lua_State *L) { if (!tagisempty(*getArrTag(t, i))) arr2obj(t, cast_uint(i), s2v(L->top.p)); else - setnilvalue(s2v(L->top.p)); + setnilvalue2s(L->top.p); api_incr_top(L); lua_pushnil(L); } @@ -1132,7 +1193,7 @@ static int test_codeparam (lua_State *L) { static int test_applyparam (lua_State *L) { lua_Integer p = luaL_checkinteger(L, 1); lua_Integer x = luaL_checkinteger(L, 2); - lua_pushinteger(L, cast(lua_Integer, luaO_applyparam(cast_byte(p), x))); + lua_pushinteger(L, cast_Integer(luaO_applyparam(cast_byte(p), x))); return 1; } @@ -1232,7 +1293,7 @@ static int pushuserdata (lua_State *L) { static int udataval (lua_State *L) { - lua_pushinteger(L, cast(lua_Integer, cast(size_t, lua_touserdata(L, 1)))); + lua_pushinteger(L, cast_st2S(cast_sizet(lua_touserdata(L, 1)))); return 1; } @@ -1241,7 +1302,7 @@ static int doonnewstack (lua_State *L) { lua_State *L1 = lua_newthread(L); size_t l; const char *s = luaL_checklstring(L, 1, &l); - int status = luaL_loadbuffer(L1, s, l, s); + int status = luaL_loadbufferx(L1, s, l, s, "t"); if (status == LUA_OK) status = lua_pcall(L1, 0, 0, 0); lua_pushinteger(L, status); @@ -1269,7 +1330,7 @@ static int num2int (lua_State *L) { static int makeseed (lua_State *L) { - lua_pushinteger(L, cast(lua_Integer, luaL_makeseed(L))); + lua_pushinteger(L, cast_Integer(luaL_makeseed(L))); return 1; } @@ -1321,7 +1382,7 @@ static int doremote (lua_State *L) { const char *code = luaL_checklstring(L, 2, &lcode); int status; lua_settop(L1, 0); - status = luaL_loadbuffer(L1, code, lcode, code); + status = luaL_loadbufferx(L1, code, lcode, code, "t"); if (status == LUA_OK) status = lua_pcall(L1, 0, LUA_MULTRET, 0); if (status != LUA_OK) { @@ -1613,7 +1674,7 @@ static int runC (lua_State *L, lua_State *L1, const char *pc) { } else if EQ("func2num") { lua_CFunction func = lua_tocfunction(L1, getindex); - lua_pushinteger(L1, cast(lua_Integer, cast(size_t, func))); + lua_pushinteger(L1, cast_st2S(cast_sizet(func))); } else if EQ("getfield") { int t = getindex; @@ -1677,7 +1738,7 @@ static int runC (lua_State *L, lua_State *L1, const char *pc) { lua_pushinteger(L1, luaL_len(L1, getindex)); } else if EQ("loadfile") { - luaL_loadfile(L1, luaL_checkstring(L1, getnum)); + luaL_loadfilex(L1, luaL_checkstring(L1, getnum), "t"); } else if EQ("loadstring") { size_t slen; @@ -1986,7 +2047,7 @@ static int Cfunc (lua_State *L) { static int Cfunck (lua_State *L, int status, lua_KContext ctx) { lua_pushstring(L, statcodes[status]); lua_setglobal(L, "status"); - lua_pushinteger(L, cast(lua_Integer, ctx)); + lua_pushinteger(L, cast_Integer(ctx)); lua_setglobal(L, "ctx"); return runC(L, L, lua_tostring(L, cast_int(ctx))); } @@ -2081,6 +2142,25 @@ static int coresume (lua_State *L) { } } +#if !defined(LUA_USE_POSIX) + +#define nonblock NULL + +#else + +#include +#include + +static int nonblock (lua_State *L) { + FILE *f = cast(luaL_Stream*, luaL_checkudata(L, 1, LUA_FILEHANDLE))->f; + int fd = fileno(f); + int flags = fcntl(fd, F_GETFL, 0); + flags |= O_NONBLOCK; + fcntl(fd, F_SETFL, flags); + return 0; +} +#endif + /* }====================================================== */ @@ -2102,6 +2182,7 @@ static const struct luaL_Reg tests_funcs[] = { {"limits", get_limits}, {"listcode", listcode}, {"printcode", printcode}, + {"printallstack", lua_printallstack}, {"listk", listk}, {"listabslineinfo", listabslineinfo}, {"listlocals", listlocals}, @@ -2122,6 +2203,9 @@ static const struct luaL_Reg tests_funcs[] = { {"s2d", s2d}, {"sethook", sethook}, {"stacklevel", stacklevel}, + {"resetCI", resetCI}, + {"reallocstack", reallocstack}, + {"sizes", get_sizes}, {"testC", testC}, {"makeCfunc", makeCfunc}, {"totalmem", mem_query}, @@ -2133,6 +2217,7 @@ static const struct luaL_Reg tests_funcs[] = { {"upvalue", upvalue}, {"externKstr", externKstr}, {"externstr", externstr}, + {"nonblock", nonblock}, {NULL, NULL} }; diff --git a/ltests.h b/ltests.h index 543b0d553a..f5f14cd61c 100644 --- a/ltests.h +++ b/ltests.h @@ -13,7 +13,8 @@ /* test Lua with compatibility code */ #define LUA_COMPAT_MATHLIB -#define LUA_COMPAT_LT_LE +#undef LUA_COMPAT_GLOBAL +#define LUA_COMPAT_GLOBAL 0 #define LUA_DEBUG @@ -44,6 +45,10 @@ #define LUA_RAND32 +/* test stack reallocation without strict address use */ +#define LUAI_STRICT_ADDRESS 0 + + /* memory-allocator control variables */ typedef struct Memcontrol { int failnext; @@ -59,7 +64,7 @@ LUA_API Memcontrol l_memcontrol; #define luai_tracegc(L,f) luai_tracegctest(L, f) -LUAI_FUNC void luai_tracegctest (lua_State *L, int first); +extern void luai_tracegctest (lua_State *L, int first); /* @@ -71,25 +76,26 @@ extern void *l_Trick; /* ** Function to traverse and check all memory used by Lua */ -LUAI_FUNC int lua_checkmemory (lua_State *L); +extern int lua_checkmemory (lua_State *L); /* ** Function to print an object GC-friendly */ struct GCObject; -LUAI_FUNC void lua_printobj (lua_State *L, struct GCObject *o); +extern void lua_printobj (lua_State *L, struct GCObject *o); /* ** Function to print a value */ struct TValue; -LUAI_FUNC void lua_printvalue (struct TValue *v); +extern void lua_printvalue (struct TValue *v); /* ** Function to print the stack */ -LUAI_FUNC void lua_printstack (lua_State *L); +extern void lua_printstack (lua_State *L); +extern int lua_printallstack (lua_State *L); /* test for lock/unlock */ @@ -116,14 +122,14 @@ LUA_API int luaB_opentests (lua_State *L); LUA_API void *debug_realloc (void *ud, void *block, size_t osize, size_t nsize); -#if defined(lua_c) + #define luaL_newstate() \ lua_newstate(debug_realloc, &l_memcontrol, luaL_makeseed(NULL)) #define luai_openlibs(L) \ { luaL_openlibs(L); \ luaL_requiref(L, "T", luaB_opentests, 1); \ lua_pop(L, 1); } -#endif + @@ -137,20 +143,14 @@ LUA_API void *debug_realloc (void *ud, void *block, #define STRCACHE_N 23 #define STRCACHE_M 5 -#undef LUAI_USER_ALIGNMENT_T -#define LUAI_USER_ALIGNMENT_T union { char b[sizeof(void*) * 8]; } +#define MAXINDEXRK 1 /* -** This one is not compatible with tests for opcode optimizations, -** as it blocks some optimizations -#define MAXINDEXRK 0 +** Reduce maximum stack size to make stack-overflow tests run faster. +** (But value is still large enough to overflow smaller integers.) */ - - -/* make stack-overflow tests run faster */ -#undef LUAI_MAXSTACK -#define LUAI_MAXSTACK 50000 +#define LUAI_MAXSTACK 68000 /* test mode uses more stack space */ diff --git a/ltm.c b/ltm.c index 8eca2d6e1f..cfe90e3074 100644 --- a/ltm.c +++ b/ltm.c @@ -196,28 +196,12 @@ void luaT_trybiniTM (lua_State *L, const TValue *p1, lua_Integer i2, /* ** Calls an order tag method. -** For lessequal, LUA_COMPAT_LT_LE keeps compatibility with old -** behavior: if there is no '__le', try '__lt', based on l <= r iff -** !(r < l) (assuming a total order). If the metamethod yields during -** this substitution, the continuation has to know about it (to negate -** the result of rtop.p, event); /* try original event */ if (tag >= 0) /* found tag method? */ return !tagisfalse(tag); -#if defined(LUA_COMPAT_LT_LE) - else if (event == TM_LE) { - /* try '!(p2 < p1)' for '(p1 <= p2)' */ - L->ci->callstatus |= CIST_LEQ; /* mark it is doing 'lt' for 'le' */ - tag = callbinTM(L, p2, p1, L->top.p, TM_LT); - L->ci->callstatus ^= CIST_LEQ; /* clear mark */ - if (tag >= 0) /* found tag method? */ - return tagisfalse(tag); - } -#endif luaG_ordererror(L, p1, p2); /* no metamethod found */ return 0; /* to avoid warnings */ } @@ -240,37 +224,141 @@ int luaT_callorderiTM (lua_State *L, const TValue *p1, int v2, } -void luaT_adjustvarargs (lua_State *L, int nfixparams, CallInfo *ci, - const Proto *p) { +/* +** Create a vararg table at the top of the stack, with 'n' elements +** starting at 'f'. +*/ +static void createvarargtab (lua_State *L, StkId f, int n) { + int i; + TValue key, value; + Table *t = luaH_new(L); + sethvalue(L, s2v(L->top.p), t); + L->top.p++; + luaH_resize(L, t, cast_uint(n), 1); + setsvalue(L, &key, luaS_new(L, "n")); /* key is "n" */ + setivalue(&value, n); /* value is n */ + /* No need to anchor the key: Due to the resize, the next operation + cannot trigger a garbage collection */ + luaH_set(L, t, &key, &value); /* t.n = n */ + for (i = 0; i < n; i++) + luaH_setint(L, t, i + 1, s2v(f + i)); + luaC_checkGC(L); +} + + +/* +** initial stack: func arg1 ... argn extra1 ... +** ^ ci->func ^ L->top +** final stack: func nil ... nil extra1 ... func arg1 ... argn +** ^ ci->func +*/ +static void buildhiddenargs (lua_State *L, CallInfo *ci, const Proto *p, + int totalargs, int nfixparams, int nextra) { int i; - int actual = cast_int(L->top.p - ci->func.p) - 1; /* number of arguments */ - int nextra = actual - nfixparams; /* number of extra arguments */ ci->u.l.nextraargs = nextra; luaD_checkstack(L, p->maxstacksize + 1); - /* copy function to the top of the stack */ + /* copy function to the top of the stack, after extra arguments */ setobjs2s(L, L->top.p++, ci->func.p); - /* move fixed parameters to the top of the stack */ + /* move fixed parameters to after the copied function */ for (i = 1; i <= nfixparams; i++) { setobjs2s(L, L->top.p++, ci->func.p + i); - setnilvalue(s2v(ci->func.p + i)); /* erase original parameter (for GC) */ + setnilvalue2s(ci->func.p + i); /* erase original parameter (for GC) */ } - ci->func.p += actual + 1; - ci->top.p += actual + 1; - lua_assert(L->top.p <= ci->top.p && ci->top.p <= L->stack_last.p); + ci->func.p += totalargs + 1; /* 'func' now lives after hidden arguments */ + ci->top.p += totalargs + 1; } -void luaT_getvarargs (lua_State *L, CallInfo *ci, StkId where, int wanted) { - int i; +void luaT_adjustvarargs (lua_State *L, CallInfo *ci, const Proto *p) { + int totalargs = cast_int(L->top.p - ci->func.p) - 1; + int nfixparams = p->numparams; + int nextra = totalargs - nfixparams; /* number of extra arguments */ + if (p->flag & PF_VATAB) { /* does it need a vararg table? */ + lua_assert(!(p->flag & PF_VAHID)); + createvarargtab(L, ci->func.p + nfixparams + 1, nextra); + /* move table to proper place (last parameter) */ + setobjs2s(L, ci->func.p + nfixparams + 1, L->top.p - 1); + } + else { /* no table */ + lua_assert(p->flag & PF_VAHID); + buildhiddenargs(L, ci, p, totalargs, nfixparams, nextra); + /* set vararg parameter to nil */ + setnilvalue2s(ci->func.p + nfixparams + 1); + lua_assert(L->top.p <= ci->top.p && ci->top.p <= L->stack_last.p); + } +} + + +void luaT_getvararg (CallInfo *ci, StkId ra, TValue *rc) { int nextra = ci->u.l.nextraargs; + lua_Integer n; + if (tointegerns(rc, &n)) { /* integral value? */ + if (l_castS2U(n) - 1 < cast_uint(nextra)) { + StkId slot = ci->func.p - nextra + cast_int(n) - 1; + setobjs2s(((lua_State*)NULL), ra, slot); + return; + } + } + else if (ttisstring(rc)) { /* string value? */ + size_t len; + const char *s = getlstr(tsvalue(rc), len); + if (len == 1 && s[0] == 'n') { /* key is "n"? */ + setivalue(s2v(ra), nextra); + return; + } + } + setnilvalue2s(ra); /* else produce nil */ +} + + +/* +** Get the number of extra arguments in a vararg function. If vararg +** table has been optimized away, that number is in the call info. +** Otherwise, get the field 'n' from the vararg table and check that it +** has a proper value (non-negative integer not larger than the stack +** limit). +*/ +static int getnumargs (lua_State *L, CallInfo *ci, Table *h) { + if (h == NULL) /* no vararg table? */ + return ci->u.l.nextraargs; + else { + TValue res; + if (luaH_getshortstr(h, luaS_new(L, "n"), &res) != LUA_VNUMINT || + l_castS2U(ivalue(&res)) > cast_uint(INT_MAX/2)) + luaG_runerror(L, "vararg table has no proper 'n'"); + return cast_int(ivalue(&res)); + } +} + + +/* +** Get 'wanted' vararg arguments and put them in 'where'. 'vatab' is +** the register of the vararg table or -1 if there is no vararg table. +*/ +void luaT_getvarargs (lua_State *L, CallInfo *ci, StkId where, int wanted, + int vatab) { + Table *h = (vatab < 0) ? NULL : hvalue(s2v(ci->func.p + vatab + 1)); + int nargs = getnumargs(L, ci, h); /* number of available vararg args. */ + int i, touse; /* 'touse' is minimum between 'wanted' and 'nargs' */ if (wanted < 0) { - wanted = nextra; /* get all extra arguments available */ - checkstackp(L, nextra, where); /* ensure stack space */ - L->top.p = where + nextra; /* next instruction will need top */ + touse = wanted = nargs; /* get all extra arguments available */ + checkstackp(L, nargs, where); /* ensure stack space */ + L->top.p = where + nargs; /* next instruction will need top */ + } + else + touse = (nargs > wanted) ? wanted : nargs; + if (h == NULL) { /* no vararg table? */ + for (i = 0; i < touse; i++) /* get vararg values from the stack */ + setobjs2s(L, where + i, ci->func.p - nargs + i); + } + else { /* get vararg values from vararg table */ + for (i = 0; i < touse; i++) { + lu_byte tag = luaH_getint(h, i + 1, s2v(where + i)); + if (tagisempty(tag)) + setnilvalue2s(where + i); + } } - for (i = 0; i < wanted && i < nextra; i++) - setobjs2s(L, where + i, ci->func.p - nextra + i); for (; i < wanted; i++) /* complete required results with nil */ - setnilvalue(s2v(where + i)); + setnilvalue2s(where + i); } diff --git a/ltm.h b/ltm.h index ba2e47606e..afc7ad00e2 100644 --- a/ltm.h +++ b/ltm.h @@ -49,7 +49,7 @@ typedef enum { ** Mask with 1 in all fast-access methods. A 1 in any of these bits ** in the flag of a (meta)table means the metatable does not have the ** corresponding metamethod field. (Bit 6 of the flag indicates that -** the table is using the dummy node; bit 7 is used for 'isrealasize'.) +** the table is using the dummy node.) */ #define maskflags cast_byte(~(~0u << (TM_EQ + 1))) @@ -95,10 +95,11 @@ LUAI_FUNC int luaT_callorderTM (lua_State *L, const TValue *p1, LUAI_FUNC int luaT_callorderiTM (lua_State *L, const TValue *p1, int v2, int inv, int isfloat, TMS event); -LUAI_FUNC void luaT_adjustvarargs (lua_State *L, int nfixparams, - struct CallInfo *ci, const Proto *p); -LUAI_FUNC void luaT_getvarargs (lua_State *L, struct CallInfo *ci, - StkId where, int wanted); +LUAI_FUNC void luaT_adjustvarargs (lua_State *L, struct CallInfo *ci, + const Proto *p); +LUAI_FUNC void luaT_getvararg (CallInfo *ci, StkId ra, TValue *rc); +LUAI_FUNC void luaT_getvarargs (lua_State *L, struct CallInfo *ci, StkId where, + int wanted, int vatab); #endif diff --git a/lua.c b/lua.c index b611cbcace..858a04c075 100644 --- a/lua.c +++ b/lua.c @@ -30,6 +30,12 @@ #define LUA_INIT_VAR "LUA_INIT" #endif +/* Name of the environment variable with the name of the readline library */ +#if !defined(LUA_RLLIB_VAR) +#define LUA_RLLIB_VAR "LUA_READLINELIB" +#endif + + #define LUA_INITVARVERSION LUA_INIT_VAR LUA_VERSUFFIX @@ -201,12 +207,12 @@ static int dochunk (lua_State *L, int status) { static int dofile (lua_State *L, const char *name) { - return dochunk(L, luaL_loadfile(L, name)); + return dochunk(L, luaL_loadfilex(L, name, "bt")); } static int dostring (lua_State *L, const char *s, const char *name) { - return dochunk(L, luaL_loadbuffer(L, s, strlen(s), name)); + return dochunk(L, luaL_loadbufferx(L, s, strlen(s), name, "t")); } @@ -260,7 +266,7 @@ static int handle_script (lua_State *L, char **argv) { const char *fname = argv[0]; if (strcmp(fname, "-") == 0 && strcmp(argv[-1], "--") != 0) fname = NULL; /* stdin */ - status = luaL_loadfile(L, fname); + status = luaL_loadfilex(L, fname, "bt"); if (status == LUA_OK) { int n = pushargs(L); /* push arguments to script */ status = docall(L, n, LUA_MULTRET); @@ -303,7 +309,8 @@ static int collectargs (char **argv, int *first) { case '-': /* '--' */ if (argv[i][2] != '\0') /* extra characters after '--'? */ return has_error; /* invalid option */ - *first = i + 1; + /* if there is a script name, it comes after '--' */ + *first = (argv[i + 1] != NULL) ? i + 1 : 0; return args; case '\0': /* '-' */ return args; /* script "name" is '-' */ @@ -348,6 +355,7 @@ static int collectargs (char **argv, int *first) { */ static int runargs (lua_State *L, char **argv, int n) { int i; + lua_warning(L, "@off", 0); /* by default, Lua stand-alone has warnings off */ for (i = 1; i < n; i++) { int option = argv[i][1]; lua_assert(argv[i][0] == '-'); /* already checked */ @@ -372,12 +380,21 @@ static int runargs (lua_State *L, char **argv, int n) { } +static char *(*l_getenv)(const char *name); + +/* Function to ignore environment variables, used by option -E */ +static char *no_getenv (const char *name) { + UNUSED(name); + return NULL; +} + + static int handle_luainit (lua_State *L) { const char *name = "=" LUA_INITVARVERSION; - const char *init = getenv(name + 1); + const char *init = l_getenv(name + 1); if (init == NULL) { name = "=" LUA_INIT_VAR; - init = getenv(name + 1); /* try alternative name */ + init = l_getenv(name + 1); /* try alternative name */ } if (init == NULL) return LUA_OK; else if (init[0] == '@') @@ -432,32 +449,41 @@ static int handle_luainit (lua_State *L) { /* -** lua_readline defines how to show a prompt and then read a line from -** the standard input. -** lua_saveline defines how to "save" a read line in a "history". -** lua_freeline defines how to free a line read by lua_readline. +** * lua_initreadline initializes the readline system. +** * lua_readline defines how to show a prompt and then read a line from +** the standard input. +** * lua_saveline defines how to "save" a read line in a "history". +** * lua_freeline defines how to free a line read by lua_readline. */ -#if defined(LUA_USE_READLINE) +#if !defined(lua_readline) /* { */ +/* Otherwise, all previously listed functions should be defined. */ + +#if defined(LUA_USE_READLINE) /* { */ +/* Lua will be linked with '-lreadline' */ #include #include + #define lua_initreadline(L) ((void)L, rl_readline_name="lua") -#define lua_readline(b,p) ((void)b, readline(p)) +#define lua_readline(buff,prompt) ((void)buff, readline(prompt)) #define lua_saveline(line) add_history(line) -#define lua_freeline(b) free(b) +#define lua_freeline(line) free(line) -#endif +#else /* }{ */ +/* use dynamically loaded readline (or nothing) */ +/* pointer to 'readline' function (if any) */ +typedef char *(*l_readlineT) (const char *prompt); +static l_readlineT l_readline = NULL; -#if !defined(lua_readline) /* { */ +/* pointer to 'add_history' function (if any) */ +typedef void (*l_addhistT) (const char *string); +static l_addhistT l_addhist = NULL; -/* pointer to dynamically loaded 'readline' function (if any) */ -typedef char *(*l_readline_t) (const char *prompt); -static l_readline_t l_readline = NULL; static char *lua_readline (char *buff, const char *prompt) { - if (l_readline != NULL) /* is there a dynamic 'readline'? */ + if (l_readline != NULL) /* is there a 'readline'? */ return (*l_readline)(prompt); /* use it */ else { /* emulate 'readline' over 'buff' */ fputs(prompt, stdout); @@ -467,50 +493,55 @@ static char *lua_readline (char *buff, const char *prompt) { } -/* pointer to dynamically loaded 'add_history' function (if any) */ -typedef void (*l_addhist_t) (const char *string); -static l_addhist_t l_addhist = NULL; - static void lua_saveline (const char *line) { - if (l_addhist != NULL) /* is there a dynamic 'add_history'? */ + if (l_addhist != NULL) /* is there an 'add_history'? */ (*l_addhist)(line); /* use it */ /* else nothing to be done */ } static void lua_freeline (char *line) { - if (l_readline != NULL) /* is there a dynamic 'readline'? */ + if (l_readline != NULL) /* is there a 'readline'? */ free(line); /* free line created by it */ /* else 'lua_readline' used an automatic buffer; nothing to free */ } -#if !defined(LUA_USE_DLOPEN) || !defined(LUA_READLINELIB) - -#define lua_initreadline(L) ((void)L) - -#else /* { */ +#if defined(LUA_USE_DLOPEN) && defined(LUA_READLINELIB) /* { */ +/* try to load 'readline' dynamically */ #include - static void lua_initreadline (lua_State *L) { - void *lib = dlopen(LUA_READLINELIB, RTLD_NOW | RTLD_LOCAL); - if (lib == NULL) - lua_warning(L, "library '" LUA_READLINELIB "' not found", 0); - else { + const char *rllib = l_getenv(LUA_RLLIB_VAR); /* name of readline library */ + void *lib; /* library handle */ + if (rllib == NULL) /* no environment variable? */ + rllib = LUA_READLINELIB; /* use default name */ + lib = dlopen(rllib, RTLD_NOW | RTLD_LOCAL); + if (lib != NULL) { const char **name = cast(const char**, dlsym(lib, "rl_readline_name")); if (name != NULL) - *name = "Lua"; - l_readline = cast(l_readline_t, cast_func(dlsym(lib, "readline"))); - if (l_readline == NULL) - lua_warning(L, "unable to load 'readline'", 0); - else - l_addhist = cast(l_addhist_t, cast_func(dlsym(lib, "add_history"))); + *name = "lua"; + l_readline = cast(l_readlineT, cast_func(dlsym(lib, "readline"))); + l_addhist = cast(l_addhistT, cast_func(dlsym(lib, "add_history"))); + if (l_readline != NULL) /* could load readline function? */ + return; /* everything ok */ + /* else emit a warning */ } + lua_warning(L, "unable to load readline library '", 1); + lua_warning(L, rllib, 1); + lua_warning(L, "'", 0); } -#endif /* } */ +#else /* }{ */ +/* no dlopen or LUA_READLINELIB undefined */ + +/* Leave pointers with NULL */ +#define lua_initreadline(L) ((void)L) + +#endif /* } */ + +#endif /* } */ #endif /* } */ @@ -578,11 +609,11 @@ static int pushline (lua_State *L, int firstline) { static int addreturn (lua_State *L) { const char *line = lua_tostring(L, -1); /* original line */ const char *retline = lua_pushfstring(L, "return %s;", line); - int status = luaL_loadbuffer(L, retline, strlen(retline), "=stdin"); + int status = luaL_loadbufferx(L, retline, strlen(retline), "=stdin", "t"); if (status == LUA_OK) lua_remove(L, -2); /* remove modified line */ else - lua_pop(L, 2); /* pop result from 'luaL_loadbuffer' and modified line */ + lua_pop(L, 2); /* pop result from 'luaL_loadbufferx' and modified line */ return status; } @@ -609,7 +640,7 @@ static int multiline (lua_State *L) { const char *line = lua_tolstring(L, 1, &len); /* get first line */ checklocal(line); for (;;) { /* repeat until gets a complete statement */ - int status = luaL_loadbuffer(L, line, len, "=stdin"); /* try it */ + int status = luaL_loadbufferx(L, line, len, "=stdin", "t"); /* try it */ if (!incomplete(L, status) || !pushline(L, 0)) return status; /* should not or cannot try to add continuation line */ lua_remove(L, -2); /* remove error message (from incomplete line) */ @@ -683,7 +714,13 @@ static void doREPL (lua_State *L) { /* }================================================================== */ #if !defined(luai_openlibs) -#define luai_openlibs(L) luaL_openselectedlibs(L, ~0, 0) +#if defined(LUA_NODEBUGLIB) +/* With this option, code must require the debug library before using it */ +#define luai_openlibs(L) luaL_openselectedlibs(L, ~LUA_DBLIBK, LUA_DBLIBK) +#else +/* The default is to open all standard libraries */ +#define luai_openlibs(L) luaL_openselectedlibs(L, ~0, 0) +#endif #endif @@ -705,18 +742,19 @@ static int pmain (lua_State *L) { if (args & has_v) /* option '-v'? */ print_version(); if (args & has_E) { /* option '-E'? */ + l_getenv = &no_getenv; /* program will ignore environment variables */ lua_pushboolean(L, 1); /* signal for libraries to ignore env. vars. */ lua_setfield(L, LUA_REGISTRYINDEX, "LUA_NOENV"); } + else + l_getenv = &getenv; luai_openlibs(L); /* open standard libraries */ createargtable(L, argv, argc, script); /* create table 'arg' */ lua_gc(L, LUA_GCRESTART); /* start GC... */ lua_gc(L, LUA_GCGEN); /* ...in generational mode */ - if (!(args & has_E)) { /* no option '-E'? */ - if (handle_luainit(L) != LUA_OK) /* run LUA_INIT */ - return 0; /* error running LUA_INIT */ - } - if (!runargs(L, argv, optlim)) /* execute arguments -e and -l */ + if (handle_luainit(L) != LUA_OK) /* run LUA_INIT */ + return 0; /* error running LUA_INIT */ + if (!runargs(L, argv, optlim)) /* execute arguments -e, -l, and -W */ return 0; /* something failed */ if (script > 0) { /* execute main script (if there is one) */ if (handle_script(L, argv + script) != LUA_OK) diff --git a/lua.h b/lua.h index 95e0db321a..6deaed49c2 100644 --- a/lua.h +++ b/lua.h @@ -13,13 +13,13 @@ #include -#define LUA_COPYRIGHT LUA_RELEASE " Copyright (C) 1994-2025 Lua.org, PUC-Rio" +#define LUA_COPYRIGHT LUA_RELEASE " Copyright (C) 1994-2026 Lua.org, PUC-Rio" #define LUA_AUTHORS "R. Ierusalimschy, L. H. de Figueiredo, W. Celes" #define LUA_VERSION_MAJOR_N 5 #define LUA_VERSION_MINOR_N 5 -#define LUA_VERSION_RELEASE_N 0 +#define LUA_VERSION_RELEASE_N 1 #define LUA_VERSION_NUM (LUA_VERSION_MAJOR_N * 100 + LUA_VERSION_MINOR_N) #define LUA_VERSION_RELEASE_NUM (LUA_VERSION_NUM * 100 + LUA_VERSION_RELEASE_N) @@ -37,10 +37,10 @@ /* ** Pseudo-indices -** (-LUAI_MAXSTACK is the minimum valid index; we keep some free empty -** space after that to help overflow detection) +** (The stack size is limited to INT_MAX/2; we keep some free empty +** space after that to help overflow detection.) */ -#define LUA_REGISTRYINDEX (-LUAI_MAXSTACK - 1000) +#define LUA_REGISTRYINDEX (-(INT_MAX/2 + 1000)) #define lua_upvalueindex(i) (LUA_REGISTRYINDEX - (i)) @@ -432,13 +432,6 @@ LUA_API void (lua_closeslot) (lua_State *L, int idx); ** compatibility macros ** =============================================================== */ -#if defined(LUA_COMPAT_APIINTCASTS) - -#define lua_pushunsigned(L,n) lua_pushinteger(L, (lua_Integer)(n)) -#define lua_tounsignedx(L,i,is) ((lua_Unsigned)lua_tointegerx(L,i,is)) -#define lua_tounsigned(L,i) lua_tounsignedx(L,(i),NULL) - -#endif #define lua_newuserdata(L,s) lua_newuserdatauv(L,s,1) #define lua_getuservalue(L,idx) lua_getiuservalue(L,idx,1) @@ -528,7 +521,7 @@ struct lua_Debug { /****************************************************************************** -* Copyright (C) 1994-2025 Lua.org, PUC-Rio. +* Copyright (C) 1994-2026 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/luaconf.h b/luaconf.h index bd39465052..0a71370fd2 100644 --- a/luaconf.h +++ b/luaconf.h @@ -59,7 +59,7 @@ /* -** When Posix DLL ('LUA_USE_DLOPEN') is enabled, the Lua stand-alone +** When POSIX DLL ('LUA_USE_DLOPEN') is enabled, the Lua stand-alone ** application will try to dynamically link a 'readline' facility ** for its REPL. In that case, LUA_READLINELIB is the name of the ** library it will look for those facilities. If lua.c cannot open @@ -70,15 +70,19 @@ #if defined(LUA_USE_LINUX) #define LUA_USE_POSIX #define LUA_USE_DLOPEN /* needs an extra library: -ldl */ +#if !defined(LUA_READLINELIB) #define LUA_READLINELIB "libreadline.so" #endif +#endif #if defined(LUA_USE_MACOSX) #define LUA_USE_POSIX -#define LUA_USE_DLOPEN /* MacOS does not need -ldl */ +#define LUA_USE_DLOPEN /* macOS does not need -ldl */ +#if !defined(LUA_READLINELIB) #define LUA_READLINELIB "libedit.dylib" #endif +#endif #if defined(LUA_USE_IOS) @@ -88,7 +92,7 @@ #if defined(LUA_USE_C89) && defined(LUA_USE_POSIX) -#error "Posix is not compatible with C89" +#error "POSIX is not compatible with C89" #endif @@ -138,7 +142,7 @@ /* @@ LUA_32BITS enables Lua with 32-bit integers and 32-bit floats. */ -#define LUA_32BITS 0 +/* #define LUA_32BITS */ /* @@ -153,7 +157,7 @@ #endif -#if LUA_32BITS /* { */ +#if defined(LUA_32BITS) /* { */ /* ** 32-bit integers and 'float' */ @@ -224,17 +228,17 @@ #if !defined(LUA_PATH_DEFAULT) #define LUA_PATH_DEFAULT \ - LUA_LDIR"?.lua;" LUA_LDIR"?\\init.lua;" \ - LUA_CDIR"?.lua;" LUA_CDIR"?\\init.lua;" \ - LUA_SHRDIR"?.lua;" LUA_SHRDIR"?\\init.lua;" \ + LUA_LDIR "?.lua;" LUA_LDIR "?\\init.lua;" \ + LUA_CDIR "?.lua;" LUA_CDIR "?\\init.lua;" \ + LUA_SHRDIR "?.lua;" LUA_SHRDIR "?\\init.lua;" \ ".\\?.lua;" ".\\?\\init.lua" #endif #if !defined(LUA_CPATH_DEFAULT) #define LUA_CPATH_DEFAULT \ - LUA_CDIR"?.dll;" \ - LUA_CDIR"..\\lib\\lua\\" LUA_VDIR "\\?.dll;" \ - LUA_CDIR"loadall.dll;" ".\\?.dll" + LUA_CDIR "?.dll;" \ + LUA_CDIR "..\\lib\\lua\\" LUA_VDIR "\\?.dll;" \ + LUA_CDIR "loadall.dll;" ".\\?.dll" #endif #else /* }{ */ @@ -245,14 +249,14 @@ #if !defined(LUA_PATH_DEFAULT) #define LUA_PATH_DEFAULT \ - LUA_LDIR"?.lua;" LUA_LDIR"?/init.lua;" \ - LUA_CDIR"?.lua;" LUA_CDIR"?/init.lua;" \ + LUA_LDIR "?.lua;" LUA_LDIR "?/init.lua;" \ + LUA_CDIR "?.lua;" LUA_CDIR "?/init.lua;" \ "./?.lua;" "./?/init.lua" #endif #if !defined(LUA_CPATH_DEFAULT) #define LUA_CPATH_DEFAULT \ - LUA_CDIR"?.so;" LUA_CDIR"loadall.so;" "./?.so" + LUA_CDIR "?.so;" LUA_CDIR "loadall.so;" "./?.so" #endif #endif /* } */ @@ -319,32 +323,13 @@ ** More often than not the libs go together with the core. */ #define LUALIB_API LUA_API -#define LUAMOD_API LUA_API - - -/* -@@ LUAI_FUNC is a mark for all extern functions that are not to be -** exported to outside modules. -@@ LUAI_DDEF and LUAI_DDEC are marks for all extern (const) variables, -** none of which to be exported to outside modules (LUAI_DDEF for -** definitions and LUAI_DDEC for declarations). -** CHANGE them if you need to mark them in some special way. Elf/gcc -** (versions 3.2 and later) mark them as "hidden" to optimize access -** when Lua is compiled as a shared library. Not all elf targets support -** this attribute. Unfortunately, gcc does not offer a way to check -** whether the target offers that support, and those without support -** give a warning about it. To avoid these warnings, change to the -** default definition. -*/ -#if defined(__GNUC__) && ((__GNUC__*100 + __GNUC_MINOR__) >= 302) && \ - defined(__ELF__) /* { */ -#define LUAI_FUNC __attribute__((visibility("internal"))) extern -#else /* }{ */ -#define LUAI_FUNC extern -#endif /* } */ -#define LUAI_DDEC(dec) LUAI_FUNC dec -#define LUAI_DDEF /* empty */ +#if defined(__cplusplus) +/* Lua uses the "C name" when calling open functions */ +#define LUAMOD_API extern "C" +#else +#define LUAMOD_API LUA_API +#endif /* }================================================================== */ @@ -356,35 +341,29 @@ */ /* -@@ LUA_COMPAT_5_3 controls other macros for compatibility with Lua 5.3. -** You can define it to get all options, or change specific options -** to fit your specific needs. +@@ LUA_COMPAT_GLOBAL avoids 'global' being a reserved word */ -#if defined(LUA_COMPAT_5_3) /* { */ +#if !defined(LUA_COMPAT_GLOBAL) +#define LUA_COMPAT_GLOBAL 1 +#endif -/* -@@ LUA_COMPAT_MATHLIB controls the presence of several deprecated -** functions in the mathematical library. -** (These functions were already officially removed in 5.3; -** nevertheless they are still available here.) -*/ -#define LUA_COMPAT_MATHLIB /* -@@ LUA_COMPAT_APIINTCASTS controls the presence of macros for -** manipulating other integer types (lua_pushunsigned, lua_tounsigned, -** luaL_checkint, luaL_checklong, etc.) -** (These macros were also officially removed in 5.3, but they are still -** available here.) +@@ LUA_COMPAT_LOOPVAR makes for-loop control variables not read-only, +** as they were in previous versions. */ -#define LUA_COMPAT_APIINTCASTS +#if !defined(LUA_COMPAT_LOOPVAR) +#define LUA_COMPAT_LOOPVAR 0 +#endif /* -@@ LUA_COMPAT_LT_LE controls the emulation of the '__le' metamethod -** using '__lt'. +@@ LUA_COMPAT_MATHLIB controls the presence of several deprecated +** functions in the mathematical library. +** (These functions were already officially removed in 5.3; +** nevertheless they are still available here.) */ -#define LUA_COMPAT_LT_LE +/* #define LUA_COMPAT_MATHLIB */ /* @@ -401,8 +380,6 @@ #define lua_equal(L,idx1,idx2) lua_compare(L,(idx1),(idx2),LUA_OPEQ) #define lua_lessthan(L,idx1,idx2) lua_compare(L,(idx1),(idx2),LUA_OPLT) -#endif /* } */ - /* }================================================================== */ @@ -434,26 +411,11 @@ */ -/* The following definitions are good for most cases here */ +/* The following definition is good for most cases here */ #define l_floor(x) (l_mathop(floor)(x)) -/* -@@ lua_numbertointeger converts a float number with an integral value -** to an integer, or returns 0 if float is not within the range of -** a lua_Integer. (The range comparisons are tricky because of -** rounding. The tests here assume a two-complement representation, -** where MININTEGER always has an exact representation as a float; -** MAXINTEGER may not have one, and therefore its conversion to float -** may have an ill-defined value.) -*/ -#define lua_numbertointeger(n,p) \ - ((n) >= (LUA_NUMBER)(LUA_MININTEGER) && \ - (n) < -(LUA_NUMBER)(LUA_MININTEGER) && \ - (*(p) = (LUA_INTEGER)(n), 1)) - - /* now the variable definitions */ #if LUA_FLOAT_TYPE == LUA_FLOAT_FLOAT /* { single float */ @@ -702,7 +664,7 @@ */ #if !defined(luai_likely) -#if defined(__GNUC__) && !defined(LUA_NOBUILTIN) +#if !defined(LUA_NOBUILTIN) && defined(__GNUC__) && (__GNUC__ >= 3) #define luai_likely(x) (__builtin_expect(((x) != 0), 1)) #define luai_unlikely(x) (__builtin_expect(((x) != 0), 0)) #else @@ -713,13 +675,6 @@ #endif -#if defined(LUA_CORE) || defined(LUA_LIB) -/* shorter names for Lua's own use */ -#define l_likely(x) luai_likely(x) -#define l_unlikely(x) luai_unlikely(x) -#endif - - /* }================================================================== */ @@ -757,20 +712,6 @@ ** ===================================================================== */ -/* -@@ LUAI_MAXSTACK limits the size of the Lua stack. -** CHANGE it if you need a different limit. This limit is arbitrary; -** its only purpose is to stop Lua from consuming unlimited stack -** space and to reserve some numbers for pseudo-indices. -** (It must fit into max(int)/2.) -*/ -#if 1000000 < (INT_MAX / 2) -#define LUAI_MAXSTACK 1000000 -#else -#define LUAI_MAXSTACK (INT_MAX / 2u) -#endif - - /* @@ LUA_EXTRASPACE defines the size of a raw memory area associated with ** a Lua state with very fast access. @@ -795,10 +736,17 @@ /* -@@ LUAI_MAXALIGN defines fields that, when used in a union, ensure -** maximum alignment for the other items in that union. +@@ LUAI_MAXALIGN defines fields that ensure proper alignment for +** memory areas offered by Lua (e.g., userdata memory). +** Add fields to it if you need alignment for non-ISO objects. */ +#if defined(LLONG_MAX) +/* use ISO C99 stuff */ +#define LUAI_MAXALIGN long double u; void *s; long long l +#else +/* use only C89 stuff */ #define LUAI_MAXALIGN lua_Number n; double u; void *s; lua_Integer i; long l +#endif /* }================================================================== */ @@ -815,7 +763,5 @@ - - #endif diff --git a/lundump.c b/lundump.c index fd5a2ca6eb..d5fdc64b02 100644 --- a/lundump.c +++ b/lundump.c @@ -37,7 +37,7 @@ typedef struct { const char *name; Table *h; /* list for string reuse */ size_t offset; /* current position relative to beginning of dump */ - lua_Integer nstr; /* number of strings in the list */ + lua_Unsigned nstr; /* number of strings in the list */ lu_byte fixed; /* dump is fixed in memory */ } LoadState; @@ -52,7 +52,7 @@ static l_noret error (LoadState *S, const char *why) { ** All high-level loads go through loadVector; you can change it to ** adapt to the endianness of the input */ -#define loadVector(S,b,n) loadBlock(S,b,(n)*sizeof((b)[0])) +#define loadVector(S,b,n) loadBlock(S,b,cast_sizet(n)*sizeof((b)[0])) static void loadBlock (LoadState *S, void *b, size_t size) { if (luaZ_read(S->Z, b, size) != 0) @@ -71,7 +71,7 @@ static void loadAlign (LoadState *S, unsigned align) { } -#define getaddr(S,n,t) cast(t *, getaddr_(S,(n) * sizeof(t))) +#define getaddr(S,n,t) cast(t *, getaddr_(S,cast_sizet(n) * sizeof(t))) static const void *getaddr_ (LoadState *S, size_t size) { const void *block = luaZ_getaddr(S->Z, size); @@ -94,8 +94,8 @@ static lu_byte loadByte (LoadState *S) { } -static size_t loadVarint (LoadState *S, size_t limit) { - size_t x = 0; +static lua_Unsigned loadVarint (LoadState *S, lua_Unsigned limit) { + lua_Unsigned x = 0; int b; limit >>= 7; do { @@ -109,14 +109,7 @@ static size_t loadVarint (LoadState *S, size_t limit) { static size_t loadSize (LoadState *S) { - return loadVarint(S, MAX_SIZE); -} - - -/* -** Read an non-negative int */ -static unsigned loadUint (LoadState *S) { - return cast_uint(loadVarint(S, cast_sizet(INT_MAX))); + return cast_sizet(loadVarint(S, MAX_SIZE)); } @@ -134,9 +127,12 @@ static lua_Number loadNumber (LoadState *S) { static lua_Integer loadInteger (LoadState *S) { - lua_Integer x; - loadVar(S, x); - return x; + lua_Unsigned cx = loadVarint(S, LUA_MAXUNSIGNED); + /* decode unsigned to signed */ + if ((cx & 1) != 0) + return l_castU2S(~(cx >> 1)); + else + return l_castU2S(cx >> 1); } @@ -151,19 +147,20 @@ static void loadString (LoadState *S, Proto *p, TString **sl) { TString *ts; TValue sv; size_t size = loadSize(S); - if (size == 0) { /* no string? */ - lua_assert(*sl == NULL); /* must be prefilled */ - return; - } - else if (size == 1) { /* previously saved string? */ - lua_Integer idx = cast(lua_Integer, loadSize(S)); /* get its index */ + if (size == 0) { /* previously saved string? */ + lua_Unsigned idx = loadVarint(S, LUA_MAXUNSIGNED); /* get its index */ TValue stv; - luaH_getint(S->h, idx, &stv); /* get its value */ - *sl = ts = tsvalue(&stv); + if (idx == 0) { /* no string? */ + lua_assert(*sl == NULL); /* must be prefilled */ + return; + } + if (novariant(luaH_getint(S->h, l_castU2S(idx), &stv)) != LUA_TSTRING) + error(S, "invalid string index"); + *sl = ts = tsvalue(&stv); /* get its value */ luaC_objbarrier(L, p, ts); return; /* do not save it again */ } - else if ((size -= 2) <= LUAI_MAXSHORTLEN) { /* short string? */ + else if ((size -= 1) <= LUAI_MAXSHORTLEN) { /* short string? */ char buff[LUAI_MAXSHORTLEN + 1]; /* extra space for '\0' */ loadVector(S, buff, size + 1); /* load string into buffer */ *sl = ts = luaS_newlstr(L, buff, size); /* create string */ @@ -182,21 +179,21 @@ static void loadString (LoadState *S, Proto *p, TString **sl) { /* add string to list of saved strings */ S->nstr++; setsvalue(L, &sv, ts); - luaH_setint(L, S->h, S->nstr, &sv); + luaH_setint(L, S->h, l_castU2S(S->nstr), &sv); luaC_objbarrierback(L, obj2gco(S->h), ts); } static void loadCode (LoadState *S, Proto *f) { - unsigned n = loadUint(S); + int n = loadInt(S); loadAlign(S, sizeof(f->code[0])); if (S->fixed) { f->code = getaddr(S, n, Instruction); - f->sizecode = cast_int(n); + f->sizecode = n; } else { f->code = luaM_newvectorchecked(S->L, n, Instruction); - f->sizecode = cast_int(n); + f->sizecode = n; loadVector(S, f->code, n); } } @@ -206,10 +203,10 @@ static void loadFunction(LoadState *S, Proto *f); static void loadConstants (LoadState *S, Proto *f) { - unsigned i; - unsigned n = loadUint(S); + int i; + int n = loadInt(S); f->k = luaM_newvectorchecked(S->L, n, TValue); - f->sizek = cast_int(n); + f->sizek = n; for (i = 0; i < n; i++) setnilvalue(&f->k[i]); for (i = 0; i < n; i++) { @@ -241,17 +238,17 @@ static void loadConstants (LoadState *S, Proto *f) { f->source = NULL; break; } - default: lua_assert(0); + default: error(S, "invalid constant"); } } } static void loadProtos (LoadState *S, Proto *f) { - unsigned i; - unsigned n = loadUint(S); + int i; + int n = loadInt(S); f->p = luaM_newvectorchecked(S->L, n, Proto *); - f->sizep = cast_int(n); + f->sizep = n; for (i = 0; i < n; i++) f->p[i] = NULL; for (i = 0; i < n; i++) { @@ -269,10 +266,10 @@ static void loadProtos (LoadState *S, Proto *f) { ** in that case all prototypes must be consistent for the GC. */ static void loadUpvalues (LoadState *S, Proto *f) { - unsigned i; - unsigned n = loadUint(S); + int i; + int n = loadInt(S); f->upvalues = luaM_newvectorchecked(S->L, n, Upvaldesc); - f->sizeupvalues = cast_int(n); + f->sizeupvalues = n; for (i = 0; i < n; i++) /* make array valid for GC */ f->upvalues[i].name = NULL; for (i = 0; i < n; i++) { /* following calls can raise errors */ @@ -284,33 +281,33 @@ static void loadUpvalues (LoadState *S, Proto *f) { static void loadDebug (LoadState *S, Proto *f) { - unsigned i; - unsigned n = loadUint(S); + int i; + int n = loadInt(S); if (S->fixed) { f->lineinfo = getaddr(S, n, ls_byte); - f->sizelineinfo = cast_int(n); + f->sizelineinfo = n; } else { f->lineinfo = luaM_newvectorchecked(S->L, n, ls_byte); - f->sizelineinfo = cast_int(n); + f->sizelineinfo = n; loadVector(S, f->lineinfo, n); } - n = loadUint(S); + n = loadInt(S); if (n > 0) { loadAlign(S, sizeof(int)); if (S->fixed) { f->abslineinfo = getaddr(S, n, AbsLineInfo); - f->sizeabslineinfo = cast_int(n); + f->sizeabslineinfo = n; } else { f->abslineinfo = luaM_newvectorchecked(S->L, n, AbsLineInfo); - f->sizeabslineinfo = cast_int(n); + f->sizeabslineinfo = n; loadVector(S, f->abslineinfo, n); } } - n = loadUint(S); + n = loadInt(S); f->locvars = luaM_newvectorchecked(S->L, n, LocVar); - f->sizelocvars = cast_int(n); + f->sizelocvars = n; for (i = 0; i < n; i++) f->locvars[i].varname = NULL; for (i = 0; i < n; i++) { @@ -318,9 +315,9 @@ static void loadDebug (LoadState *S, Proto *f) { f->locvars[i].startpc = loadInt(S); f->locvars[i].endpc = loadInt(S); } - n = loadUint(S); + n = loadInt(S); if (n != 0) /* does it have debug information? */ - n = cast_uint(f->sizeupvalues); /* must be this many */ + n = f->sizeupvalues; /* must be this many */ for (i = 0; i < n; i++) loadString(S, f, &f->upvalues[i].name); } @@ -330,7 +327,8 @@ static void loadFunction (LoadState *S, Proto *f) { f->linedefined = loadInt(S); f->lastlinedefined = loadInt(S); f->numparams = loadByte(S); - f->flag = loadByte(S) & PF_ISVARARG; /* get only the meaningful flags */ + /* get only the meaningful flags */ + f->flag = cast_byte(loadByte(S) & ~PF_FIXED); if (S->fixed) f->flag |= PF_FIXED; /* signal that code is fixed */ f->maxstacksize = loadByte(S); @@ -352,13 +350,29 @@ static void checkliteral (LoadState *S, const char *s, const char *msg) { } -static void fchecksize (LoadState *S, size_t size, const char *tname) { - if (loadByte(S) != size) - error(S, luaO_pushfstring(S->L, "%s size mismatch", tname)); +static l_noret numerror (LoadState *S, const char *what, const char *tname) { + const char *msg = luaO_pushfstring(S->L, "%s %s mismatch", tname, what); + error(S, msg); } -#define checksize(S,t) fchecksize(S,sizeof(t),#t) +static void checknumsize (LoadState *S, int size, const char *tname) { + if (size != loadByte(S)) + numerror(S, "size", tname); +} + + +static void checknumformat (LoadState *S, int eq, const char *tname) { + if (!eq) + numerror(S, "format", tname); +} + + +#define checknum(S,tvar,value,tname) \ + { tvar i; checknumsize(S, sizeof(i), tname); \ + loadVar(S, i); \ + checknumformat(S, i == value, tname); } + static void checkHeader (LoadState *S) { /* skip 1st char (already read and checked) */ @@ -368,46 +382,40 @@ static void checkHeader (LoadState *S) { if (loadByte(S) != LUAC_FORMAT) error(S, "format mismatch"); checkliteral(S, LUAC_DATA, "corrupted chunk"); - checksize(S, Instruction); - checksize(S, lua_Integer); - checksize(S, lua_Number); - if (loadInteger(S) != LUAC_INT) - error(S, "integer format mismatch"); - if (loadNumber(S) != LUAC_NUM) - error(S, "float format mismatch"); + checknum(S, int, LUAC_INT, "int"); + checknum(S, Instruction, LUAC_INST, "instruction"); + checknum(S, lua_Integer, LUAC_INT, "Lua integer"); + checknum(S, lua_Number, LUAC_NUM, "Lua number"); } /* ** Load precompiled chunk. */ -LClosure *luaU_undump (lua_State *L, ZIO *Z, const char *name, int fixed) { +LClosure *luaU_undump (lua_State *L, ZIO *Z, Table *anchor, const char *name, + int fixed) { LoadState S; LClosure *cl; if (*name == '@' || *name == '=') - S.name = name + 1; + name = name + 1; else if (*name == LUA_SIGNATURE[0]) - S.name = "binary string"; - else - S.name = name; + name = "binary string"; + S.name = name; S.L = L; S.Z = Z; S.fixed = cast_byte(fixed); S.offset = 1; /* fist byte was already read */ checkHeader(&S); - cl = luaF_newLclosure(L, loadByte(&S)); - setclLvalue2s(L, L->top.p, cl); - luaD_inctop(L); - S.h = luaH_new(L); /* create list of saved strings */ + S.h = anchor; S.nstr = 0; - sethvalue2s(L, L->top.p, S.h); /* anchor it */ - luaD_inctop(L); + cl = luaF_newLclosure(L, loadByte(&S)); + luaD_anchorobj(L, anchor, obj2gco(cl)); cl->p = luaF_newproto(L); luaC_objbarrier(L, cl, cl->p); loadFunction(&S, cl->p); - lua_assert(cl->nupvalues == cl->p->sizeupvalues); + if (cl->nupvalues != cl->p->sizeupvalues) + error(&S, "corrupted chunk"); luai_verifycode(L, cl->p); - L->top.p--; /* pop table */ return cl; } diff --git a/lundump.h b/lundump.h index 1d6e50ea84..186e25f888 100644 --- a/lundump.h +++ b/lundump.h @@ -17,8 +17,9 @@ /* data to catch conversion errors */ #define LUAC_DATA "\x19\x93\r\n\x1a\n" -#define LUAC_INT 0x5678 -#define LUAC_NUM cast_num(370.5) +#define LUAC_INT -0x5678 +#define LUAC_INST 0x12345678 +#define LUAC_NUM cast_num(-370.5) /* ** Encode major-minor version in one byte, one nibble for each @@ -29,8 +30,8 @@ /* load one chunk; from lundump.c */ -LUAI_FUNC LClosure* luaU_undump (lua_State* L, ZIO* Z, const char* name, - int fixed); +LUAI_FUNC LClosure* luaU_undump (lua_State* L, ZIO* Z, Table *anchor, + const char* name, int fixed); /* dump one chunk; from ldump.c */ LUAI_FUNC int luaU_dump (lua_State* L, const Proto* f, lua_Writer w, diff --git a/lutf8lib.c b/lutf8lib.c index 4c9784e093..0cd7f9c363 100644 --- a/lutf8lib.c +++ b/lutf8lib.c @@ -10,7 +10,6 @@ #include "lprefix.h" -#include #include #include #include @@ -47,7 +46,7 @@ static lua_Integer u_posrelat (lua_Integer pos, size_t len) { ** Decode one UTF-8 sequence, returning NULL if byte sequence is ** invalid. The array 'limits' stores the minimum value for each ** sequence length, to check for overlong representations. Its first -** entry forces an error for non-ascii bytes with no continuation +** entry forces an error for non-ASCII bytes with no continuation ** bytes (count == 0). */ static const char *utf8_decode (const char *s, l_uint32 *val, int strict) { @@ -55,8 +54,10 @@ static const char *utf8_decode (const char *s, l_uint32 *val, int strict) { {~(l_uint32)0, 0x80, 0x800, 0x10000u, 0x200000u, 0x4000000u}; unsigned int c = (unsigned char)s[0]; l_uint32 res = 0; /* final result */ - if (c < 0x80) /* ascii? */ + if (c < 0x80) /* ASCII? */ res = c; + else if (c >= 0xfe) /* c >= 1111 1110b ? */ + return NULL; /* would need six or more continuation bytes */ else { int count = 0; /* to count number of continuation bytes */ for (; c & 0x40; c <<= 1) { /* while it needs continuation bytes... */ @@ -65,8 +66,9 @@ static const char *utf8_decode (const char *s, l_uint32 *val, int strict) { return NULL; /* invalid byte sequence */ res = (res << 6) | (cc & 0x3F); /* add lower 6 bits from cont. byte */ } + lua_assert(count <= 5); res |= ((l_uint32)(c & 0x7F) << (count * 5)); /* add first byte */ - if (count > 5 || res > MAXUTF || res < limits[count]) + if (res > MAXUTF || res < limits[count]) return NULL; /* invalid byte sequence */ s += count; /* skip continuation bytes read */ } @@ -147,7 +149,7 @@ static int codepoint (lua_State *L) { static void pushutfchar (lua_State *L, int arg) { lua_Unsigned code = (lua_Unsigned)luaL_checkinteger(L, arg); luaL_argcheck(L, code <= MAXUTF, arg, "value out of range"); - lua_pushfstring(L, "%U", (long)code); + lua_pushfstring(L, "%U", cast(unsigned long, code)); } @@ -215,9 +217,10 @@ static int byteoffset (lua_State *L) { } lua_pushinteger(L, posi + 1); /* initial position */ if ((s[posi] & 0x80) != 0) { /* multi-byte character? */ - do { - posi++; - } while (iscontp(s + posi + 1)); /* skip to final byte */ + if (iscont(s[posi])) + return luaL_error(L, "initial position is a continuation byte"); + while (iscontp(s + posi + 1)) + posi++; /* skip to last continuation byte */ } /* else one-byte character: final position is the initial one */ lua_pushinteger(L, posi + 1); /* 'posi' now is the final position */ diff --git a/lvm.c b/lvm.c index f0e73f9bb6..f9e87b61bb 100644 --- a/lvm.c +++ b/lvm.c @@ -268,9 +268,9 @@ static int forprep (lua_State *L, StkId ra) { /* ** Execute a step of a float numerical for loop, returning ** true iff the loop must continue. (The integer case is -** written online with opcode OP_FORLOOP, for performance.) +** written inline with opcode OP_FORLOOP, for performance.) */ -static int floatforloop (StkId ra) { +static int floatforloop (lua_State *L, StkId ra) { lua_Number step = fltvalue(s2v(ra + 1)); lua_Number limit = fltvalue(s2v(ra)); lua_Number idx = fltvalue(s2v(ra + 2)); /* control variable */ @@ -303,7 +303,7 @@ lu_byte luaV_finishget (lua_State *L, const TValue *t, TValue *key, else { /* 't' is a table */ tm = fasttm(L, hvalue(t)->metatable, TM_INDEX); /* table's metamethod */ if (tm == NULL) { /* no metamethod? */ - setnilvalue(s2v(val)); /* result is nil */ + setnilvalue2s(val); /* result is nil */ return LUA_VNIL; } /* else will try the metamethod */ @@ -325,6 +325,11 @@ lu_byte luaV_finishget (lua_State *L, const TValue *t, TValue *key, /* ** Finish a table assignment 't[key] = val'. +** About anchoring the table before the call to 'luaH_finishset': +** This call may trigger an emergency collection. When loop>0, +** the table being accessed is a field in some metatable. If this +** metatable is weak and the table is not anchored, this collection +** could collect that table while it is being updated. */ void luaV_finishset (lua_State *L, const TValue *t, TValue *key, TValue *val, int hres) { @@ -335,7 +340,10 @@ void luaV_finishset (lua_State *L, const TValue *t, TValue *key, Table *h = hvalue(t); /* save 't' table */ tm = fasttm(L, h->metatable, TM_NEWINDEX); /* get metamethod */ if (tm == NULL) { /* no metamethod? */ + sethvalue2s(L, L->top.p, h); /* anchor 't' */ + L->top.p++; /* assume EXTRA_STACK */ luaH_finishset(L, h, key, val, hres); /* set new value */ + L->top.p--; invalidateTMcache(h); luaC_barrierback(L, obj2gco(h), val); return; @@ -354,14 +362,24 @@ void luaV_finishset (lua_State *L, const TValue *t, TValue *key, } t = tm; /* else repeat assignment over 'tm' */ luaV_fastset(t, key, val, hres, luaH_pset); - if (hres == HOK) + if (hres == HOK) { + luaV_finishfastset(L, t, val); return; /* done */ + } /* else 'return luaV_finishset(L, t, key, val, slot)' (loop) */ } luaG_runerror(L, "'__newindex' chain too long; possible loop"); } +/* +** Function to be used for 0-terminated string order comparison +*/ +#if !defined(l_strcoll) +#define l_strcoll strcoll +#endif + + /* ** Compare two strings 'ts1' x 'ts2', returning an integer less-equal- ** -greater than zero if 'ts1' is less-equal-greater than 'ts2'. @@ -376,7 +394,7 @@ static int l_strcmp (const TString *ts1, const TString *ts2) { size_t rl2; const char *s2 = getlstr(ts2, rl2); for (;;) { /* for each segment */ - int temp = strcoll(s1, s2); + int temp = l_strcoll(s1, s2); if (temp != 0) /* not equal? */ return temp; /* done */ else { /* strings are equal up to a '\0' */ @@ -563,52 +581,74 @@ int luaV_lessequal (lua_State *L, const TValue *l, const TValue *r) { */ int luaV_equalobj (lua_State *L, const TValue *t1, const TValue *t2) { const TValue *tm; - if (ttypetag(t1) != ttypetag(t2)) { /* not the same variant? */ - if (ttype(t1) != ttype(t2) || ttype(t1) != LUA_TNUMBER) - return 0; /* only numbers can be equal with different variants */ - else { /* two numbers with different variants */ - /* One of them is an integer. If the other does not have an - integer value, they cannot be equal; otherwise, compare their - integer values. */ - lua_Integer i1, i2; - return (luaV_tointegerns(t1, &i1, F2Ieq) && - luaV_tointegerns(t2, &i2, F2Ieq) && - i1 == i2); + if (ttype(t1) != ttype(t2)) /* not the same type? */ + return 0; + else if (ttypetag(t1) != ttypetag(t2)) { + switch (ttypetag(t1)) { + case LUA_VNUMINT: { /* integer == float? */ + /* integer and float can only be equal if float has an integer + value equal to the integer */ + lua_Integer i2; + return (luaV_flttointeger(fltvalue(t2), &i2, F2Ieq) && + ivalue(t1) == i2); + } + case LUA_VNUMFLT: { /* float == integer? */ + lua_Integer i1; /* see comment in previous case */ + return (luaV_flttointeger(fltvalue(t1), &i1, F2Ieq) && + i1 == ivalue(t2)); + } + case LUA_VSHRSTR: case LUA_VLNGSTR: { + /* compare two strings with different variants: they can be + equal when one string is a short string and the other is + an external string */ + return luaS_eqstr(tsvalue(t1), tsvalue(t2)); + } + default: + /* only numbers (integer/float) and strings (long/short) can have + equal values with different variants */ + return 0; } } - /* values have same type and same variant */ - switch (ttypetag(t1)) { - case LUA_VNIL: case LUA_VFALSE: case LUA_VTRUE: return 1; - case LUA_VNUMINT: return (ivalue(t1) == ivalue(t2)); - case LUA_VNUMFLT: return luai_numeq(fltvalue(t1), fltvalue(t2)); - case LUA_VLIGHTUSERDATA: return pvalue(t1) == pvalue(t2); - case LUA_VLCF: return fvalue(t1) == fvalue(t2); - case LUA_VSHRSTR: return eqshrstr(tsvalue(t1), tsvalue(t2)); - case LUA_VLNGSTR: return luaS_eqlngstr(tsvalue(t1), tsvalue(t2)); - case LUA_VUSERDATA: { - if (uvalue(t1) == uvalue(t2)) return 1; - else if (L == NULL) return 0; - tm = fasttm(L, uvalue(t1)->metatable, TM_EQ); - if (tm == NULL) - tm = fasttm(L, uvalue(t2)->metatable, TM_EQ); - break; /* will try TM */ + else { /* equal variants */ + switch (ttypetag(t1)) { + case LUA_VNIL: case LUA_VFALSE: case LUA_VTRUE: + return 1; + case LUA_VNUMINT: + return (ivalue(t1) == ivalue(t2)); + case LUA_VNUMFLT: + return (fltvalue(t1) == fltvalue(t2)); + case LUA_VLIGHTUSERDATA: return pvalue(t1) == pvalue(t2); + case LUA_VSHRSTR: + return eqshrstr(tsvalue(t1), tsvalue(t2)); + case LUA_VLNGSTR: + return luaS_eqstr(tsvalue(t1), tsvalue(t2)); + case LUA_VUSERDATA: { + if (uvalue(t1) == uvalue(t2)) return 1; + else if (L == NULL) return 0; + tm = fasttm(L, uvalue(t1)->metatable, TM_EQ); + if (tm == NULL) + tm = fasttm(L, uvalue(t2)->metatable, TM_EQ); + break; /* will try TM */ + } + case LUA_VTABLE: { + if (hvalue(t1) == hvalue(t2)) return 1; + else if (L == NULL) return 0; + tm = fasttm(L, hvalue(t1)->metatable, TM_EQ); + if (tm == NULL) + tm = fasttm(L, hvalue(t2)->metatable, TM_EQ); + break; /* will try TM */ + } + case LUA_VLCF: + return (fvalue(t1) == fvalue(t2)); + default: /* functions and threads */ + return (gcvalue(t1) == gcvalue(t2)); } - case LUA_VTABLE: { - if (hvalue(t1) == hvalue(t2)) return 1; - else if (L == NULL) return 0; - tm = fasttm(L, hvalue(t1)->metatable, TM_EQ); - if (tm == NULL) - tm = fasttm(L, hvalue(t2)->metatable, TM_EQ); - break; /* will try TM */ + if (tm == NULL) /* no TM? */ + return 0; /* objects are different */ + else { + int tag = luaT_callTMres(L, tm, t1, t2, L->top.p); /* call TM */ + return !tagisfalse(tag); } - default: - return gcvalue(t1) == gcvalue(t2); - } - if (tm == NULL) /* no TM? */ - return 0; /* objects are different */ - else { - int tag = luaT_callTMres(L, tm, t1, t2, L->top.p); /* call TM */ - return !tagisfalse(tag); } } @@ -617,6 +657,11 @@ int luaV_equalobj (lua_State *L, const TValue *t1, const TValue *t2) { #define tostring(L,o) \ (ttisstring(o) || (cvt2str(o) && (luaO_tostring(L, o), 1))) +/* +** Check whether object is a short empty string to optimize concatenation. +** (External strings can be empty too; they will be concatenated like +** non-empty ones.) +*/ #define isemptystr(o) (ttisshrstring(o) && tsvalue(o)->shrlen == 0) /* copy strings in stack from top - n up to top - 1 to buffer */ @@ -651,8 +696,8 @@ void luaV_concat (lua_State *L, int total) { setobjs2s(L, top - 2, top - 1); /* result is second op. */ } else { - /* at least two non-empty string values; get as many as possible */ - size_t tl = tsslen(tsvalue(s2v(top - 1))); + /* at least two string values; get as many as possible */ + size_t tl = tsslen(tsvalue(s2v(top - 1))); /* total length */ TString *ts; /* collect total length and number of strings */ for (n = 1; n < total && tostring(L, s2v(top - n - 1)); n++) { @@ -690,7 +735,7 @@ void luaV_objlen (lua_State *L, StkId ra, const TValue *rb) { Table *h = hvalue(rb); tm = fasttm(L, h->metatable, TM_LEN); if (tm) break; /* metamethod? break switch to call it */ - setivalue(s2v(ra), l_castU2S(luaH_getn(h))); /* else primitive len */ + setivalue(s2v(ra), l_castU2S(luaH_getn(L, h))); /* else primitive len */ return; } case LUA_VSHRSTR: { @@ -764,7 +809,7 @@ lua_Number luaV_modf (lua_State *L, lua_Number m, lua_Number n) { /* number of bits in an integer */ -#define NBITS cast_int(sizeof(lua_Integer) * CHAR_BIT) +#define NBITS l_numbits(lua_Integer) /* @@ -829,12 +874,6 @@ void luaV_finishOp (lua_State *L) { case OP_EQ: { /* note that 'OP_EQI'/'OP_EQK' cannot yield */ int res = !l_isfalse(s2v(L->top.p - 1)); L->top.p--; -#if defined(LUA_COMPAT_LT_LE) - if (ci->callstatus & CIST_LEQ) { /* "<=" using "<" instead? */ - ci->callstatus ^= CIST_LEQ; /* clear mark */ - res = !res; /* negate result */ - } -#endif lua_assert(GET_OPCODE(*ci->u.l.savedpc) == OP_JMP); if (res != GETARG_k(inst)) /* condition failed? */ ci->u.l.savedpc++; /* skip jump instruction */ @@ -878,6 +917,10 @@ void luaV_finishOp (lua_State *L) { /* ** {================================================================== ** Macros for arithmetic/bitwise/comparison opcodes in 'luaV_execute' +** +** All these macros are to be used exclusively inside the main +** iterpreter loop (function luaV_execute) and may access directly +** the local variables of that function (L, i, pc, ci, etc.). ** =================================================================== */ @@ -899,17 +942,17 @@ void luaV_finishOp (lua_State *L) { ** operation, 'fop' is the float operation. */ #define op_arithI(L,iop,fop) { \ - StkId ra = RA(i); \ + TValue *ra = vRA(i); \ TValue *v1 = vRB(i); \ int imm = GETARG_sC(i); \ if (ttisinteger(v1)) { \ lua_Integer iv1 = ivalue(v1); \ - pc++; setivalue(s2v(ra), iop(L, iv1, imm)); \ + pc++; setivalue(ra, iop(L, iv1, imm)); \ } \ else if (ttisfloat(v1)) { \ lua_Number nb = fltvalue(v1); \ lua_Number fimm = cast_num(imm); \ - pc++; setfltvalue(s2v(ra), fop(L, nb, fimm)); \ + pc++; setfltvalue(ra, fop(L, nb, fimm)); \ }} @@ -920,6 +963,7 @@ void luaV_finishOp (lua_State *L) { #define op_arithf_aux(L,v1,v2,fop) { \ lua_Number n1; lua_Number n2; \ if (tonumberns(v1, n1) && tonumberns(v2, n2)) { \ + StkId ra = RA(i); \ pc++; setfltvalue(s2v(ra), fop(L, n1, n2)); \ }} @@ -928,7 +972,6 @@ void luaV_finishOp (lua_State *L) { ** Arithmetic operations over floats and others with register operands. */ #define op_arithf(L,fop) { \ - StkId ra = RA(i); \ TValue *v1 = vRB(i); \ TValue *v2 = vRC(i); \ op_arithf_aux(L, v1, v2, fop); } @@ -938,7 +981,6 @@ void luaV_finishOp (lua_State *L) { ** Arithmetic operations with K operands for floats. */ #define op_arithfK(L,fop) { \ - StkId ra = RA(i); \ TValue *v1 = vRB(i); \ TValue *v2 = KC(i); lua_assert(ttisnumber(v2)); \ op_arithf_aux(L, v1, v2, fop); } @@ -948,8 +990,8 @@ void luaV_finishOp (lua_State *L) { ** Arithmetic operations over integers and floats. */ #define op_arith_aux(L,v1,v2,iop,fop) { \ - StkId ra = RA(i); \ if (ttisinteger(v1) && ttisinteger(v2)) { \ + StkId ra = RA(i); \ lua_Integer i1 = ivalue(v1); lua_Integer i2 = ivalue(v2); \ pc++; setivalue(s2v(ra), iop(L, i1, i2)); \ } \ @@ -978,12 +1020,12 @@ void luaV_finishOp (lua_State *L) { ** Bitwise operations with constant operand. */ #define op_bitwiseK(L,op) { \ - StkId ra = RA(i); \ TValue *v1 = vRB(i); \ TValue *v2 = KC(i); \ lua_Integer i1; \ lua_Integer i2 = ivalue(v2); \ if (tointegerns(v1, &i1)) { \ + StkId ra = RA(i); \ pc++; setivalue(s2v(ra), op(i1, i2)); \ }} @@ -992,11 +1034,11 @@ void luaV_finishOp (lua_State *L) { ** Bitwise operations with register operands. */ #define op_bitwise(L,op) { \ - StkId ra = RA(i); \ TValue *v1 = vRB(i); \ TValue *v2 = vRC(i); \ lua_Integer i1; lua_Integer i2; \ if (tointegerns(v1, &i1) && tointegerns(v2, &i2)) { \ + StkId ra = RA(i); \ pc++; setivalue(s2v(ra), op(i1, i2)); \ }} @@ -1007,18 +1049,18 @@ void luaV_finishOp (lua_State *L) { ** integers. */ #define op_order(L,opi,opn,other) { \ - StkId ra = RA(i); \ + TValue *ra = vRA(i); \ int cond; \ TValue *rb = vRB(i); \ - if (ttisinteger(s2v(ra)) && ttisinteger(rb)) { \ - lua_Integer ia = ivalue(s2v(ra)); \ + if (ttisinteger(ra) && ttisinteger(rb)) { \ + lua_Integer ia = ivalue(ra); \ lua_Integer ib = ivalue(rb); \ cond = opi(ia, ib); \ } \ - else if (ttisnumber(s2v(ra)) && ttisnumber(rb)) \ - cond = opn(s2v(ra), rb); \ + else if (ttisnumber(ra) && ttisnumber(rb)) \ + cond = opn(ra, rb); \ else \ - Protect(cond = other(L, s2v(ra), rb)); \ + Protect(cond = other(L, ra, rb)); \ docondjump(); } @@ -1027,19 +1069,19 @@ void luaV_finishOp (lua_State *L) { ** always small enough to have an exact representation as a float.) */ #define op_orderI(L,opi,opf,inv,tm) { \ - StkId ra = RA(i); \ + TValue *ra = vRA(i); \ int cond; \ int im = GETARG_sB(i); \ - if (ttisinteger(s2v(ra))) \ - cond = opi(ivalue(s2v(ra)), im); \ - else if (ttisfloat(s2v(ra))) { \ - lua_Number fa = fltvalue(s2v(ra)); \ + if (ttisinteger(ra)) \ + cond = opi(ivalue(ra), im); \ + else if (ttisfloat(ra)) { \ + lua_Number fa = fltvalue(ra); \ lua_Number fim = cast_num(im); \ cond = opf(fa, fim); \ } \ else { \ int isf = GETARG_C(i); \ - Protect(cond = luaT_callorderiTM(L, s2v(ra), im, inv, isf, tm)); \ + Protect(cond = luaT_callorderiTM(L, ra, im, inv, isf, tm)); \ } \ docondjump(); } @@ -1058,6 +1100,7 @@ void luaV_finishOp (lua_State *L) { #define RA(i) (base+GETARG_A(i)) +#define vRA(i) s2v(RA(i)) #define RB(i) (base+GETARG_B(i)) #define vRB(i) s2v(RB(i)) #define KB(i) (k+GETARG_B(i)) @@ -1098,14 +1141,14 @@ void luaV_finishOp (lua_State *L) { /* ** Correct global 'pc'. */ -#define savepc(L) (ci->u.l.savedpc = pc) +#define savepc(ci) (ci->u.l.savedpc = pc) /* ** Whenever code can raise errors, the global 'pc' and the global ** 'top' must be correct to report occasional errors. */ -#define savestate(L,ci) (savepc(L), L->top.p = ci->top.p) +#define savestate(L,ci) (savepc(ci), L->top.p = ci->top.p) /* @@ -1115,7 +1158,7 @@ void luaV_finishOp (lua_State *L) { #define Protect(exp) (savestate(L,ci), (exp), updatetrap(ci)) /* special version that does not change the top */ -#define ProtectNT(exp) (savepc(L), (exp), updatetrap(ci)) +#define ProtectNT(exp) (savepc(ci), (exp), updatetrap(ci)) /* ** Protect code that can only raise errors. (That is, it cannot change @@ -1133,7 +1176,7 @@ void luaV_finishOp (lua_State *L) { /* 'c' is the limit of live values in the stack */ #define checkGC(L,c) \ - { luaC_condGC(L, (savepc(L), L->top.p = (c)), \ + { luaC_condGC(L, (savepc(ci), L->top.p = (c)), \ updatetrap(ci)); \ luai_threadyield(L); } @@ -1440,23 +1483,23 @@ void luaV_execute (lua_State *L, CallInfo *ci) { op_bitwiseK(L, l_bxor); vmbreak; } - vmcase(OP_SHRI) { + vmcase(OP_SHLI) { StkId ra = RA(i); TValue *rb = vRB(i); int ic = GETARG_sC(i); lua_Integer ib; if (tointegerns(rb, &ib)) { - pc++; setivalue(s2v(ra), luaV_shiftl(ib, -ic)); + pc++; setivalue(s2v(ra), luaV_shiftl(ic, ib)); } vmbreak; } - vmcase(OP_SHLI) { + vmcase(OP_SHRI) { StkId ra = RA(i); TValue *rb = vRB(i); int ic = GETARG_sC(i); lua_Integer ib; if (tointegerns(rb, &ib)) { - pc++; setivalue(s2v(ra), luaV_shiftl(ic, ib)); + pc++; setivalue(s2v(ra), luaV_shiftl(ib, -ic)); } vmbreak; } @@ -1502,14 +1545,14 @@ void luaV_execute (lua_State *L, CallInfo *ci) { op_bitwise(L, l_bxor); vmbreak; } - vmcase(OP_SHR) { - op_bitwise(L, luaV_shiftr); - vmbreak; - } vmcase(OP_SHL) { op_bitwise(L, luaV_shiftl); vmbreak; } + vmcase(OP_SHR) { + op_bitwise(L, luaV_shiftr); + vmbreak; + } vmcase(OP_MMBIN) { StkId ra = RA(i); Instruction pi = *(pc - 2); /* original arith. expression */ @@ -1682,7 +1725,7 @@ void luaV_execute (lua_State *L, CallInfo *ci) { if (b != 0) /* fixed number of arguments? */ L->top.p = ra + b; /* top signals number of arguments */ /* else previous instruction set top */ - savepc(L); /* in case of errors */ + savepc(ci); /* in case of errors */ if ((newci = luaD_precall(L, ra, nresults)) == NULL) updatetrap(ci); /* C call; nothing else to be done */ else { /* Lua call: run function in this same C frame */ @@ -1798,7 +1841,7 @@ void luaV_execute (lua_State *L, CallInfo *ci) { pc -= GETARG_Bx(i); /* jump back */ } } - else if (floatforloop(ra)) /* float loop */ + else if (floatforloop(L, ra)) /* float loop */ pc -= GETARG_Bx(i); /* jump back */ updatetrap(ci); /* allows a signal to break the loop */ vmbreak; @@ -1858,7 +1901,7 @@ void luaV_execute (lua_State *L, CallInfo *ci) { vmcase(OP_SETLIST) { StkId ra = RA(i); unsigned n = cast_uint(GETARG_vB(i)); - unsigned int last = cast_uint(GETARG_vC(i)); + unsigned last = cast_uint(GETARG_vC(i)); Table *h = hvalue(s2v(ra)); if (n == 0) n = cast_uint(L->top.p - ra) - 1; /* get up to the top */ @@ -1892,12 +1935,25 @@ void luaV_execute (lua_State *L, CallInfo *ci) { } vmcase(OP_VARARG) { StkId ra = RA(i); - int n = GETARG_C(i) - 1; /* required results */ - Protect(luaT_getvarargs(L, ci, ra, n)); + int n = GETARG_C(i) - 1; /* required results (-1 means all) */ + int vatab = GETARG_k(i) ? GETARG_B(i) : -1; + Protect(luaT_getvarargs(L, ci, ra, n, vatab)); + vmbreak; + } + vmcase(OP_GETVARG) { + StkId ra = RA(i); + TValue *rc = vRC(i); + luaT_getvararg(ci, ra, rc); + vmbreak; + } + vmcase(OP_ERRNNIL) { + TValue *ra = vRA(i); + if (!ttisnil(ra)) + halfProtect(luaG_errnnil(L, cl, GETARG_Bx(i))); vmbreak; } vmcase(OP_VARARGPREP) { - ProtectNT(luaT_adjustvarargs(L, GETARG_A(i), ci, cl->p)); + ProtectNT(luaT_adjustvarargs(L, ci, cl->p)); if (l_unlikely(trap)) { /* previous "Protect" updated trap */ luaD_hookcall(L, ci); L->oldpc = 1; /* next opcode will be seen as a "new" line */ diff --git a/makefile b/makefile index 8506e93c20..8144dcb6c8 100644 --- a/makefile +++ b/makefile @@ -15,9 +15,9 @@ CWARNSCPP= \ -Wdouble-promotion \ -Wmissing-declarations \ -Wconversion \ - -Wstrict-overflow=2 \ # the next warnings might be useful sometimes, # but usually they generate too much noise + # -Wstrict-overflow=2 \ # -Werror \ # -pedantic # warns if we use jump tables \ # -Wformat=2 \ @@ -60,10 +60,13 @@ CWARNS= $(CWARNSCPP) $(CWARNSC) $(CWARNGCC) # create problems; some are only available in newer gcc versions. To # use some of them, we also have to define an environment variable # ASAN_OPTIONS="detect_invalid_pointer_pairs=2". -# -fsanitize=undefined +# -fsanitize=undefined (you may need to add "-lubsan" to libs) # -fsanitize=pointer-subtract -fsanitize=address -fsanitize=pointer-compare -# TESTS= -DLUA_USER_H='"ltests.h"' -Og -g +# Test mode: Add test library, turn on asserts, redefine several +# constants ("to give some bugs a chance"), track memory use, and add +# debug information. +# TESTS= -DLUA_USER_H='"ltests.h"' -Og -g LOCAL = $(TESTS) $(CWARNS) @@ -71,13 +74,15 @@ LOCAL = $(TESTS) $(CWARNS) # To enable Linux goodies, -DLUA_USE_LINUX # For C89, "-std=c89 -DLUA_USE_C89" # Note that Linux/Posix options are not compatible with C89 +# (For 32-bit, add option "-m32" to MYCFLAGS and MYLDFLAGS.) MYCFLAGS= $(LOCAL) -std=c99 -DLUA_USE_LINUX -MYLDFLAGS= $(LOCAL) -Wl,-E +MYLDFLAGS= -Wl,-E MYLIBS= -ldl CC= gcc -CFLAGS= -Wall -O2 $(MYCFLAGS) -fno-stack-protector -fno-common -march=native +# (Optionally we can use -march=native -mno-avx512f.) +CFLAGS= -Wall -O2 $(MYCFLAGS) -fno-stack-protector -fno-common AR= ar rc RANLIB= ranlib RM= rm -f @@ -166,8 +171,7 @@ ldump.o: ldump.c lprefix.h lua.h luaconf.h lapi.h llimits.h lstate.h \ lfunc.o: lfunc.c lprefix.h lua.h luaconf.h ldebug.h lstate.h lobject.h \ llimits.h ltm.h lzio.h lmem.h ldo.h lfunc.h lgc.h lgc.o: lgc.c lprefix.h lua.h luaconf.h ldebug.h lstate.h lobject.h \ - llimits.h ltm.h lzio.h lmem.h ldo.h lfunc.h lgc.h llex.h lstring.h \ - ltable.h + llimits.h ltm.h lzio.h lmem.h ldo.h lfunc.h lgc.h lstring.h ltable.h linit.o: linit.c lprefix.h lua.h luaconf.h lualib.h lauxlib.h llimits.h liolib.o: liolib.c lprefix.h lua.h luaconf.h lauxlib.h lualib.h llimits.h llex.o: llex.c lprefix.h lua.h luaconf.h lctype.h llimits.h ldebug.h \ diff --git a/manual/2html b/manual/2html index ac5ea04351..243f3f22d4 100755 --- a/manual/2html +++ b/manual/2html @@ -1,4 +1,4 @@ -#!/usr/bin/env lua5.3 +#!/usr/bin/env lua -- special marks: @@ -8,11 +8,11 @@ --------------------------------------------------------------- header = [[ - + -Lua 5.4 Reference Manual +Lua 5.5 Reference Manual @@ -23,14 +23,14 @@ header = [[

[Lua logo] -Lua 5.4 Reference Manual +Lua 5.5 Reference Manual

by Roberto Ierusalimschy, Luiz Henrique de Figueiredo, Waldemar Celes

Copyright -© 2025 Lua.org, PUC-Rio. All rights reserved. +© 2026 Lua.org, PUC-Rio. All rights reserved.


diff --git a/manual/manual.of b/manual/manual.of index 274799e3f1..5e113336ce 100644 --- a/manual/manual.of +++ b/manual/manual.of @@ -107,7 +107,7 @@ for small machines and embedded systems. Unless stated otherwise, any overflow when manipulating integer values @def{wrap around}, -according to the usual rules of two-complement arithmetic. +according to the usual rules of two's complement arithmetic. (In other words, the actual result is the unique representable integer that is equal modulo @M{2@sp{n}} to the mathematical result, @@ -127,7 +127,8 @@ strings can contain any 8-bit value, including @x{embedded zeros} (@Char{\0}). Lua is also encoding-agnostic; it makes no assumptions about the contents of a string. -The length of any string in Lua must fit in a Lua integer. +The length of any string in Lua must fit in a Lua integer, +and the string plus a small header must fit in @id{size_t}. Lua can call (and manipulate) functions written in Lua and functions written in C @see{functioncall}. @@ -213,11 +214,89 @@ of a given value @seeF{type}. } -@sect2{globalenv| @title{Environments and the Global Environment} +@sect2{globalenv| @title{Scopes, Variables, and Environments} +@index{visibility} + +A variable name refers to a global or a local variable according +to the declaration that is in context at that point of the code. +(For the purposes of this discussion, +a function's formal parameter is equivalent to a local variable.) + +All chunks start with an implicit declaration @T{global *}, +which declares all free names as global variables; +this preambular declaration becomes void inside the scope of any other +@Rw{global} declaration, +as the following example illustrates: +@verbatim{ +X = 1 -- Ok, global by default +do + global Y -- voids the implicit initial declaration + Y = 1 -- Ok, Y declared as global + X = 1 -- ERROR, X not declared +end +X = 2 -- Ok, global by default again +} +So, outside any global declaration, +Lua works as @x{global-by-default}. +Inside any global declaration, +Lua works without a default: +All variables must be declared. + +Lua is a lexically scoped language. +The scope of a variable declaration begins at the first statement after +the declaration and lasts until the last non-void statement +of the innermost block that includes the declaration. +(@emph{Void statements} are labels and empty statements.) + +A declaration shadows any declaration for the same name that +is in context at the point of the declaration. Inside this +shadow, any outer declaration for that name is void. +See the next example: +@verbatim{ +global print, x +x = 10 -- global variable +do -- new block + local x = x -- new 'x', with value 10 + print(x) --> 10 + x = x+1 + do -- another block + local x = x+1 -- another 'x' + print(x) --> 12 + end + print(x) --> 11 +end +print(x) --> 10 (the global one) +} + +Notice that, in a declaration like @T{local x = x}, +the new @id{x} being declared is not in scope yet, +and so the @id{x} on the right-hand side refers to the outside variable. + +Because of the @x{lexical scoping} rules, +local variables can be freely accessed by functions +defined inside their scope. +A local variable used by an inner function is called an @def{upvalue} +(or @emphx{external local variable}, or simply @emphx{external variable}) +inside the inner function. + +Notice that each execution of a @Rw{local} statement +defines new local variables. +Consider the following example: +@verbatim{ +a = {} +local x = 20 +for i = 1, 10 do + local y = 0 + a[i] = function () y = y + 1; return x + y end +end +} +The loop creates ten closures +(that is, ten instances of the anonymous function). +Each of these closures uses a different @id{y} variable, +while all of them share the same @id{x}. As we will discuss further in @refsec{variables} and @refsec{assignment}, -any reference to a free name -(that is, a name not bound to any declaration) @id{var} +any reference to a global variable @id{var} is syntactically translated to @T{_ENV.var}. Moreover, every chunk is compiled in the scope of an external local variable named @id{_ENV} @see{chunks}, @@ -225,12 +304,14 @@ so @id{_ENV} itself is never a free name in a chunk. Despite the existence of this external @id{_ENV} variable and the translation of free names, -@id{_ENV} is a completely regular name. +@id{_ENV} is a regular name. In particular, you can define new variables and parameters with that name. -Each reference to a free name uses the @id{_ENV} that is -visible at that point in the program, -following the usual visibility rules of Lua @see{visibility}. +(However, you should not define @id{_ENV} as a global variable, +otherwise @T{_ENV.var} would translate to +@T{_ENV._ENV.var} and so on, in an infinite loop.) +Each reference to a global variable name uses the @id{_ENV} that is +visible at that point in the program. Any table used as the value of @id{_ENV} is called an @def{environment}. @@ -244,8 +325,8 @@ When Lua loads a chunk, the default value for its @id{_ENV} variable is the global environment @seeF{load}. Therefore, by default, -free names in Lua code refer to entries in the global environment -and, therefore, they are also called @def{global variables}. +global variables in Lua code refer to entries in the global environment +and, therefore, they act as conventional global variables. Moreover, all standard libraries are loaded in the global environment and some functions there operate on that environment. You can use @Lid{load} (or @Lid{loadfile}) @@ -290,7 +371,9 @@ an @def{error object} is propagated with information about the error. Lua itself only generates errors whose error object is a string, but programs can generate errors with -any value as the error object. +any value as the error object, +except @nil. +(Lua will change a @nil as error object to a string message.) It is up to the Lua program or its host to handle such error objects. For historical reasons, an error object is often called an @def{error message}, @@ -1029,9 +1112,9 @@ and cannot be used as names: @index{reserved words} @verbatim{ and break do else elseif end -false for function goto if in -local nil not or repeat return -then true until while +false for function global goto if +in local nil not or repeat +return then true until while } Lua is a case-sensitive language: @@ -1196,17 +1279,15 @@ global variables, local variables, and table fields. A single name can denote a global variable or a local variable (or a function's formal parameter, -which is a particular kind of local variable): +which is a particular kind of local variable) @see{globalenv}: @Produc{ @producname{var}@producbody{@bnfNter{Name}} } @bnfNter{Name} denotes identifiers @see{lexical}. -Any variable name is assumed to be global unless explicitly declared -as a local @see{localvar}. -@x{Local variables} are @emph{lexically scoped}: +Because variables are @emph{lexically scoped}, local variables can be freely accessed by functions -defined inside their scope @see{visibility}. +defined inside their scope @see{globalenv}. Before the first assignment to a variable, its value is @nil. @@ -1225,8 +1306,6 @@ The syntax @id{var.Name} is just syntactic sugar for An access to a global variable @id{x} is equivalent to @id{_ENV.x}. -Due to the way that chunks are compiled, -the variable @id{_ENV} itself is never global @see{globalenv}. } @@ -1324,6 +1403,8 @@ Chunks can also be precompiled into binary form; see the program @idx{luac} and the function @Lid{string.dump} for details. Programs in source and compiled forms are interchangeable; Lua automatically detects the file type and acts accordingly @seeF{load}. +Be aware that, unlike source code, +maliciously crafted binary chunks can crash the interpreter. } @@ -1426,7 +1507,7 @@ labels in Lua are considered statements too: A label is visible in the entire block where it is defined, except inside nested functions. A goto can jump to any visible label as long as it does not -enter into the scope of a local variable. +enter into the scope of a variable declaration. A label should not be declared where a previous label with the same name is visible, even if this other label has been declared in an enclosing block. @@ -1475,7 +1556,8 @@ It has the following syntax: exp @bnfter{,} exp @bnfopt{@bnfter{,} exp} @Rw{do} block @Rw{end}} } The given identifier (@bnfNter{Name}) defines the control variable, -which is a new read-only variable local to the loop body (@emph{block}). +which is a new read-only (@id{const}) variable local to the loop body +(@emph{block}). The loop starts by evaluating once the three control expressions. Their values are called respectively @@ -1530,7 +1612,7 @@ works as follows. The names @rep{var_i} declare loop variables local to the loop body. The first of these variables is the @emph{control variable}, -which is a read-only variable. +which is a read-only (@id{const}) variable. The loop starts by evaluating @rep{explist} to produce four values: @@ -1569,35 +1651,91 @@ Function calls are explained in @See{functioncall}. } -@sect3{localvar| @title{Local Declarations} -@x{Local variables} can be declared anywhere inside a block. +@sect3{localvar| @title{Variable Declarations} +Local and global variables can be declared anywhere inside a block. The declaration can include an initialization: @Produc{ -@producname{stat}@producbody{@Rw{local} attnamelist @bnfopt{@bnfter{=} explist}} -@producname{attnamelist}@producbody{ - @bnfNter{Name} attrib @bnfrep{@bnfter{,} @bnfNter{Name} attrib}} -} -If present, an initial assignment has the same semantics +@producname{stat}@producbody{@Rw{local} + attnamelist @bnfopt{@bnfter{=} explist}} +@producname{stat}@producbody{@Rw{global} + attnamelist @bnfopt{@bnfter{=} explist}} +} +If there is no initialization, +local variables are initialized with @nil; +global variables are left unchanged. +Otherwise, the initialization gets the same adjustment of a multiple assignment @see{assignment}. -Otherwise, all variables are initialized with @nil. - -Each variable name may be postfixed by an attribute -(a name between angle brackets): +Moreover, for global variables, +the initialization will raise a runtime error +if the variable is already defined, +that is, it has a non-nil value. + +The list of names may be prefixed by an attribute +(a name between angle brackets) +and each variable name may be postfixed by an attribute: @Produc{ -@producname{attrib}@producbody{@bnfopt{@bnfter{<} @bnfNter{Name} @bnfter{>}}} +@producname{attnamelist}@producbody{ + @bnfopt{attrib} @bnfNter{Name} @bnfopt{attrib} + @bnfrep{@bnfter{,} @bnfNter{Name} @bnfopt{attrib}}} +@producname{attrib}@producbody{@bnfter{<} @bnfNter{Name} @bnfter{>}} } +A prefixed attribute applies to all names in the list; +a postfixed attribute applies to its particular name. There are two possible attributes: @id{const}, which declares a @emph{constant} or @emph{read-only} variable, @index{constant variable} -that is, a variable that cannot be assigned to -after its initialization; +that is, a variable that cannot be used as the left-hand side of an +assignment, and @id{close}, which declares a to-be-closed variable @see{to-be-closed}. +Only local variables can have the @id{close} attribute. A list of variables can contain at most one to-be-closed variable. +Lua offers also a collective declaration for global variables: +@Produc{ +@producname{stat}@producbody{@Rw{global} @bnfopt{attrib} @bnfter{*}} +} +This special form implicitly declares +as globals all names not explicitly declared previously. +In particular, +@T{global *} implicitly declares +as read-only globals all names not explicitly declared previously; +see the following example: +@verbatim{ +global X +global * +print(math.pi) -- Ok, 'print' and 'math' are read-only +X = 1 -- Ok, declared as read-write +Y = 1 -- Error, Y is read-only +} + +As noted in @See{globalenv}, +all chunks start with an implicit declaration @T{global *}, +but this preambular declaration becomes void inside +the scope of any other @Rw{global} declaration. +Therefore, a program that does not use global declarations +or start with @T{global *} +has free read-write access to any global; +a program that starts with @T{global *} +has free read-only access to any global; +and a program that starts with any other global declaration +(e.g., @T{global none}) can only refer to declared variables. + +Note that, for global variables, +the effect of any declaration is only syntactical +(except for the optional assignment): +@verbatim{ +global X , _G +X = 1 -- ERROR +_ENV.X = 1 -- Ok +_G.print(X) -- Ok +foo() -- 'foo' can freely change any global +} + A chunk is also a block @see{chunks}, -and so local variables can be declared in a chunk outside any explicit block. +and so variables can be declared in a chunk outside any explicit block. -The visibility rules for local variables are explained in @See{visibility}. +The visibility rules for variable declarations +are explained in @See{globalenv}. } @@ -1612,10 +1750,11 @@ or exiting by an error. Here, to @emph{close} a value means to call its @idx{__close} metamethod. When calling the metamethod, -the value itself is passed as the first argument -and the error object that caused the exit (if any) +the value itself is passed as the first argument. +If there was an error, +the error object that caused the exit is passed as a second argument; -if there was no error, the second argument is @nil. +otherwise, there is no second argument. The value assigned to a to-be-closed variable must have a @idx{__close} metamethod @@ -1952,12 +2091,12 @@ Note that keys that are not positive integers do not interfere with borders. A table with exactly one border is called a @def{sequence}. -For instance, the table @T{{10, 20, 30, 40, 50}} is a sequence, +For instance, the table @T{{10,20,30,40,50}} is a sequence, as it has only one border (5). -The table @T{{10, 20, 30, nil, 50}} has two borders (3 and 5), +The table @T{{10,20,30,nil,50}} has two borders (3 and 5), and therefore it is not a sequence. (The @nil at index 4 is called a @emphx{hole}.) -The table @T{{nil, 20, 30, nil, nil, 60, nil}} +The table @T{{nil,20,30,nil,nil,60,nil}} has three borders (0, 3, and 6), so it is not a sequence, too. The table @T{{}} is a sequence with border 0. @@ -2082,7 +2221,7 @@ The form } can be used to emulate methods. A call @T{v:name(@rep{args})} -is syntactic sugar for @T{v.name(v,@rep{args})}, +is syntactic sugar for @T{v.name(v, @rep{args})}, except that @id{v} is evaluated only once. Arguments have the following syntax: @@ -2129,7 +2268,7 @@ return x or f(x) -- results adjusted to 1 @sect3{func-def| @title{Function Definitions} -The syntax for function definition is +The syntax for a function definition is @Produc{ @producname{functiondef}@producbody{@Rw{function} funcbody} @producname{funcbody}@producbody{@bnfter{(} @bnfopt{parlist} @bnfter{)} block @Rw{end}} @@ -2139,6 +2278,7 @@ The following syntactic sugar simplifies function definitions: @Produc{ @producname{stat}@producbody{@Rw{function} funcname funcbody} @producname{stat}@producbody{@Rw{local} @Rw{function} @bnfNter{Name} funcbody} +@producname{stat}@producbody{@Rw{global} @Rw{function} @bnfNter{Name} funcbody} @producname{funcname}@producbody{@bnfNter{Name} @bnfrep{@bnfter{.} @bnfNter{Name}} @bnfopt{@bnfter{:} @bnfNter{Name}}} } The statement @@ -2157,6 +2297,7 @@ translates to @verbatim{ t.a.b.c.f = function () @rep{body} end } + The statement @verbatim{ local function f () @rep{body} end @@ -2170,7 +2311,29 @@ not to local f = function () @rep{body} end } (This only makes a difference when the body of the function -contains references to @id{f}.) +contains recursive references to @id{f}.) +Similarly, the statement +@verbatim{ +global function f () @rep{body} end +} +translates to +@verbatim{ +global f; global f = function () @rep{body} end +} +The second @Rw{global} makes the assignment an initialization, +which will raise an error if that global is already defined. + +The @emphx{colon} syntax +is used to emulate @def{methods}, +adding an implicit extra parameter @idx{self} to the function. +Thus, the statement +@verbatim{ +function t.a.b.c:f (@rep{params}) @rep{body} end +} +is syntactic sugar for +@verbatim{ +t.a.b.c.f = function (self, @rep{params}) @rep{body} end +} A function definition is an executable expression, whose value has type @emph{function}. @@ -2182,11 +2345,24 @@ the function is @emph{instantiated} (or @emph{closed}). This function instance, or @emphx{closure}, is the final value of the expression. +Results are returned using the @Rw{return} statement @see{control}. +If control reaches the end of a function +without encountering a @Rw{return} statement, +then the function returns with no results. + +@index{multiple return} +There is a system-dependent limit on the number of values +that a function may return. +This limit is guaranteed to be at least 1000. + +@sect4{@title{Parameters} + Parameters act as local variables that are initialized with the argument values: @Produc{ -@producname{parlist}@producbody{namelist @bnfopt{@bnfter{,} @bnfter{...}} @Or - @bnfter{...}} +@producname{parlist}@producbody{namelist @bnfopt{@bnfter{,} varargparam} @Or + varargparam} +@producname{varargparam}@producbody{@bnfter{...} @bnfopt{@bnfNter{Name}}} } When a Lua function is called, it adjusts its list of @x{arguments} to @@ -2196,11 +2372,10 @@ which is indicated by three dots (@Char{...}) at the end of its parameter list. A variadic function does not adjust its argument list; instead, it collects all extra arguments and supplies them -to the function through a @def{vararg expression}, -which is also written as three dots. -The value of this expression is a list of all actual extra arguments, -similar to a function with multiple results @see{multires}. - +to the function through a @def{vararg table}. +In that table, +the values at indices 1, 2, etc. are the extra arguments, +and the value at index @St{n} is the number of extra arguments. As an example, consider the following definitions: @verbatim{ @@ -2209,7 +2384,7 @@ function g(a, b, ...) end function r() return 1,2,3 end } Then, we have the following mapping from arguments to parameters and -to the vararg expression: +to the vararg table: @verbatim{ CALL PARAMETERS @@ -2219,38 +2394,45 @@ f(3, 4, 5) a=3, b=4 f(r(), 10) a=1, b=10 f(r()) a=1, b=2 -g(3) a=3, b=nil, ... --> (nothing) -g(3, 4) a=3, b=4, ... --> (nothing) -g(3, 4, 5, 8) a=3, b=4, ... --> 5 8 -g(5, r()) a=5, b=1, ... --> 2 3 +g(3) a=3, b=nil, va. table -> {n = 0} +g(3, 4) a=3, b=4, va. table -> {n = 0} +g(3, 4, 5, 8) a=3, b=4, va. table -> {5, 8, n = 2} +g(5, r()) a=5, b=1, va. table -> {2, 3, n = 2} } -Results are returned using the @Rw{return} statement @see{control}. -If control reaches the end of a function -without encountering a @Rw{return} statement, -then the function returns with no results. +A vararg table in a variadic function can have an optional name, +given after the three dots. +When present, +that name denotes a read-only local variable that +refers to the vararg table. +If the vararg table does not have a name, +it can only be accessed through a vararg expression. -@index{multiple return} -There is a system-dependent limit on the number of values -that a function may return. -This limit is guaranteed to be greater than 1000. +A vararg expression is also written as three dots, +and its value is a list of the values in the vararg table, +from 1 to the integer value at index @St{n}. +(Therefore, if the code does not modify the vararg table, +this list corresponds to the extra arguments in the function call.) +This list behaves like the results from a +function with multiple results @see{multires}. + +As an optimization, +if the vararg table satisfies some conditions, +the code does not create an actual table and instead translates +the indexing expressions and the vararg expressions +into accesses to the internal vararg data. +The conditions are as follows: +If the vararg table has a name, +that name is not an upvalue in a nested function +and it is used only as the base table +in the syntactic constructions @T{t[exp]} or @T{t.id}. +Note that an anonymous vararg table always satisfy these conditions. -The @emphx{colon} syntax -is used to emulate @def{methods}, -adding an implicit extra parameter @idx{self} to the function. -Thus, the statement -@verbatim{ -function t.a.b.c:f (@rep{params}) @rep{body} end -} -is syntactic sugar for -@verbatim{ -t.a.b.c.f = function (self, @rep{params}) @rep{body} end } } -@sect3{multires| @title{Lists of expressions, multiple results, -and adjustment} +@sect3{multires| @title{Lists of Expressions, Multiple Results, and Adjustment} Both function calls and vararg expressions can result in multiple values. These expressions are called @def{multires expressions}. @@ -2267,22 +2449,22 @@ These are the places where Lua expects a list of expressions: @description{ @item{A @rw{return} statement, -for instance @T{return e1, e2, e3} @see{control}.} +for instance @T{return e1,e2,e3} @see{control}.} @item{A table constructor, -for instance @T{{e1, e2, e3}} @see{tableconstructor}.} +for instance @T{{e1,e2,e3}} @see{tableconstructor}.} @item{The arguments of a function call, -for instance @T{foo(e1, e2, e3)} @see{functioncall}.} +for instance @T{foo(e1,e2,e3)} @see{functioncall}.} @item{A multiple assignment, -for instance @T{a , b, c = e1, e2, e3} @see{assignment}.} +for instance @T{a,b,c = e1,e2,e3} @see{assignment}.} -@item{A local declaration, -for instance @T{local a , b, c = e1, e2, e3} @see{localvar}.} +@item{A local or global declaration, +which is similar to a multiple assignment.} @item{The initial values in a generic @rw{for} loop, -for instance @T{for k in e1, e2, e3 do ... end} @see{for}.} +for instance @T{for k in e1,e2,e3 do ... end} @see{for}.} } In the last four cases, @@ -2290,8 +2472,7 @@ the list of values from the list of expressions must be @emph{adjusted} to a specific length: the number of parameters in a call to a non-variadic function @see{func-def}, -the number of variables in a multiple assignment or -a local declaration, +the number of variables in a multiple assignment or a declaration, and exactly four values for a generic @rw{for} loop. The @def{adjustment} follows these rules: If there are more values than needed, @@ -2320,7 +2501,7 @@ we recommend assigning the vararg expression to a single variable and using that variable in its place. -Here are some examples of uses of mutlres expressions. +Here are some examples of uses of multires expressions. In all cases, when the construction needs @Q{the n-th result} and there is no such result, it uses a @nil. @@ -2353,58 +2534,6 @@ return x,y,f() -- returns x, y, and all results from f(). } -@sect2{visibility| @title{Visibility Rules} - -@index{visibility} -Lua is a lexically scoped language. -The scope of a local variable begins at the first statement after -its declaration and lasts until the last non-void statement -of the innermost block that includes the declaration. -(@emph{Void statements} are labels and empty statements.) -Consider the following example: -@verbatim{ -x = 10 -- global variable -do -- new block - local x = x -- new 'x', with value 10 - print(x) --> 10 - x = x+1 - do -- another block - local x = x+1 -- another 'x' - print(x) --> 12 - end - print(x) --> 11 -end -print(x) --> 10 (the global one) -} - -Notice that, in a declaration like @T{local x = x}, -the new @id{x} being declared is not in scope yet, -and so the second @id{x} refers to the outside variable. - -Because of the @x{lexical scoping} rules, -local variables can be freely accessed by functions -defined inside their scope. -A local variable used by an inner function is called an @def{upvalue} -(or @emphx{external local variable}, or simply @emphx{external variable}) -inside the inner function. - -Notice that each execution of a @Rw{local} statement -defines new local variables. -Consider the following example: -@verbatim{ -a = {} -local x = 20 -for i = 1, 10 do - local y = 0 - a[i] = function () y = y + 1; return x + y end -end -} -The loop creates ten closures -(that is, ten instances of the anonymous function). -Each of these closures uses a different @id{y} variable, -while all of them share the same @id{x}. - -} } @@ -2561,9 +2690,21 @@ which behaves like a nil value. } -@sect3{constchar|@title{Pointers to strings} +@sect3{constchar|@title{Pointers to Strings} -Several functions in the API return pointers (@T{const char*}) +Several functions in the API accept pointers (@T{const char*}) +to C strings. +Some of there parameters have an associated length (@T{size_t}). +Unless stated otherwise, +when there is an associated length, +the string can contain embedded zeros; +moreover, the pointer can be @id{NULL} if the length is zero. +When there is no associated length, +the pointer must point to a zero-terminated string. +In any case, the string contents should remain unchanged +until the function returns. + +Several functions in the API also return pointers (@T{const char*}) to Lua strings in the stack. (See @Lid{lua_pushfstring}, @Lid{lua_pushlstring}, @Lid{lua_pushstring}, and @Lid{lua_tolstring}. @@ -2736,7 +2877,16 @@ status codes to indicate different kinds of errors or other conditions: For such errors, Lua does not call the @x{message handler}. } -@item{@defid{LUA_ERRERR}| error while running the @x{message handler}.} +@item{@defid{LUA_ERRERR}| +stack overflow while running the @x{message handler} +due to another stack overflow. +More often than not, +this error is the result of some other error while running +a message handler. +An error in a message handler will call the handler again, +which will generate the error again, and so on, +until this loop exhausts the stack and cause this error. +} @item{@defid{LUA_ERRSYNTAX}| syntax error during precompilation or format error in a binary chunk.} @@ -2917,7 +3067,7 @@ typedef void * (*lua_Alloc) (void *ud, size_t osize, size_t nsize);| -The type of the @x{memory-allocation function} used by Lua states. +The type of the @x{memory-allocator function} used by Lua states. The allocator function must provide a functionality similar to @id{realloc}, but not exactly the same. @@ -2952,11 +3102,12 @@ the allocator must behave like @id{realloc}. In particular, the allocator returns @id{NULL} if and only if it cannot fulfill the request. -Here is a simple implementation for the @x{allocator function}. -It is used in the auxiliary library by @Lid{luaL_newstate}. +Here is a simple implementation for the @x{allocator function}, +corresponding to the function @Lid{luaL_alloc} from the +auxiliary library. @verbatim{ -static void *l_alloc (void *ud, void *ptr, size_t osize, - size_t nsize) { +void *luaL_alloc (void *ud, void *ptr, size_t osize, + size_t nsize) { (void)ud; (void)osize; /* not used */ if (nsize == 0) { free(ptr); @@ -3179,17 +3330,25 @@ when called through this function. Resets a thread, cleaning its call stack and closing all pending to-be-closed variables. -Returns a status code: +The parameter @id{from} represents the coroutine that is resetting @id{L}. +If there is no such coroutine, +this parameter can be @id{NULL}. + +Unless @id{L} is equal to @id{from}, +the call returns a status code: @Lid{LUA_OK} for no errors in the thread (either the original error that stopped the thread or errors in closing methods), or an error status otherwise. In case of error, -leaves the error object on the top of the stack. +the error object is put on the top of the stack. -The parameter @id{from} represents the coroutine that is resetting @id{L}. -If there is no such coroutine, -this parameter can be @id{NULL}. +If @id{L} is equal to @id{from}, +it corresponds to a thread closing itself. +In that case, +the call does not return; +instead, the resume that (re)started the thread returns. +The thread must be running inside a resume. } @@ -3375,7 +3534,7 @@ This function should not be called by a finalizer. @APIEntry{lua_Alloc lua_getallocf (lua_State *L, void **ud);| @apii{0,0,-} -Returns the @x{memory-allocation function} of a given state. +Returns the @x{memory-allocator function} of a given state. If @id{ud} is not @id{NULL}, Lua stores in @T{*ud} the opaque pointer given when the memory-allocator function was set. @@ -3493,9 +3652,9 @@ because a pseudo-index is not an actual stack position. The type of integers in Lua. By default this type is @id{long long}, -(usually a 64-bit two-complement integer), +(usually a 64-bit two's complement integer), but that can be changed to @id{long} or @id{int} -(usually a 32-bit two-complement integer). +(usually a 32-bit two's complement integer). (See @id{LUA_INT_TYPE} in @id{luaconf.h}.) Lua also defines the constants @@ -3732,7 +3891,7 @@ is a seed for the hashing of strings. @apii{0,1,m} Creates a new empty table and pushes it onto the stack. -It is equivalent to @T{lua_createtable(L, 0, 0)}. +It is equivalent to @T{lua_createtable(L,0,0)}. } @@ -3756,8 +3915,12 @@ like any Lua object. This function creates and pushes on the stack a new full userdata, with @id{nuvalue} associated Lua values, called @id{user values}, plus an associated block of raw memory with @id{size} bytes. -(The user values can be set and read with the functions -@Lid{lua_setiuservalue} and @Lid{lua_getiuservalue}.) + +The user values can be set and read with the functions +@Lid{lua_setiuservalue} and @Lid{lua_getiuservalue}. +The block of memory is suitably aligned for any @N{ISO C} object. +(See macro @id{LUAI_MAXALIGN} in file @id{luaconf.h} for other +alignment requirements.) The function returns the address of the block of memory. Lua ensures that this address is valid as long as @@ -3799,8 +3962,8 @@ this confuses the next call to @Lid{lua_next}. This function may raise an error if the given key is neither @nil nor present in the table. -See function @Lid{next} for the caveats of modifying -the table during its traversal. + +See function @Lid{next} for more details about the traversal. } @@ -3829,8 +3992,8 @@ This macro may evaluate its arguments more than once. } -@APIEntry{unsigned (lua_numbertocstring) (lua_State *L, int idx, - char *buff);| +@APIEntry{unsigned lua_numbertocstring (lua_State *L, int idx, + char *buff);| @apii{0,0,-} Converts the number at acceptable index @id{idx} to a string @@ -3955,7 +4118,7 @@ This function is equivalent to @Lid{lua_pushcclosure} with no upvalues. } -@APIEntry{const char *(lua_pushexternalstring) (lua_State *L, +@APIEntry{const char *lua_pushexternalstring (lua_State *L, const char *s, size_t len, lua_Alloc falloc, void *ud);| @apii{0,1,m} @@ -3978,21 +4141,18 @@ the string @id{s} as the block, the length plus one (to account for the ending zero) as the old size, and 0 as the new size. -Lua always @x{internalizes} strings with lengths up to 40 characters. -So, for strings in that range, -this function will immediately internalize the string -and call @id{falloc} to free the buffer. - Even when using an external buffer, Lua still has to allocate a header for the string. In case of a memory-allocation error, Lua will call @id{falloc} before raising the error. +The function returns a pointer to the string (that is, @id{s}). + } @APIEntry{const char *lua_pushfstring (lua_State *L, const char *fmt, ...);| -@apii{0,1,m} +@apii{0,1,v} Pushes onto the stack a formatted string and returns a pointer to this string @see{constchar}. @@ -4012,6 +4172,9 @@ A conversion specifier (and its corresponding extra argument) can be Every occurrence of @Char{%} in the string @id{fmt} must form a valid conversion specifier. +Besides memory allocation errors, +this function may raise an error if the resulting string is too large. + } @APIEntry{void lua_pushglobaltable (lua_State *L);| @@ -4044,7 +4207,7 @@ light userdata with the same @N{C address}. } @APIEntry{const char *lua_pushliteral (lua_State *L, const char *s);| -@apii{0,1,m} +@apii{0,1,v} This macro is equivalent to @Lid{lua_pushstring}, but should be used only when @id{s} is a literal string. @@ -4053,7 +4216,7 @@ but should be used only when @id{s} is a literal string. } @APIEntry{const char *lua_pushlstring (lua_State *L, const char *s, size_t len);| -@apii{0,1,m} +@apii{0,1,v} Pushes the string pointed to by @id{s} with size @id{len} onto the stack. @@ -4065,6 +4228,9 @@ including @x{embedded zeros}. Returns a pointer to the internal copy of the string @see{constchar}. +Besides memory allocation errors, +this function may raise an error if the string is too large. + } @APIEntry{void lua_pushnil (lua_State *L);| @@ -4282,7 +4448,7 @@ Starts and resumes a coroutine in the given thread @id{L}. To start a coroutine, you push the main function plus any arguments onto the empty stack of the thread. -then you call @Lid{lua_resume}, +Then you call @Lid{lua_resume}, with @id{nargs} being the number of arguments. The function returns when the coroutine suspends, finishes its execution, or raises an unprotected error. @@ -4462,7 +4628,7 @@ You can resume threads with status @Lid{LUA_OK} } @APIEntry{size_t lua_stringtonumber (lua_State *L, const char *s);| -@apii{0,1,-} +@apii{0,0|1,-} Converts the zero-terminated string @id{s} to a number, pushes that number into the stack, @@ -4792,7 +4958,7 @@ Lua calls the given @x{continuation function} @id{k} to continue the execution of the @N{C function} that yielded @see{continuations}. This continuation function receives the same stack from the previous function, -with the @id{n} results removed and +with all the results (@id{nresults}) removed and replaced by the arguments passed to @Lid{lua_resume}. Moreover, the continuation function receives the value @id{ctx} @@ -4882,7 +5048,7 @@ the function was defined in a string where } @item{@id{srclen}| -The length of the string @id{source}. +the length of the string @id{source}. } @item{@id{short_src}| @@ -4924,8 +5090,8 @@ then @id{name} is set to @id{NULL}. @item{@id{namewhat}| explains the @T{name} field. The value of @T{namewhat} can be -@T{"global"}, @T{"local"}, @T{"method"}, -@T{"field"}, @T{"upvalue"}, or @T{""} (the empty string), +@T{"global"}, @T{"local"}, @T{"upvalue"}, +@T{"field"}, @T{""} (the empty string), plus some other options, according to how the function was called. (Lua uses the empty string when no other option seems to apply.) } @@ -5046,7 +5212,7 @@ running at the given level; } @item{@Char{S}| -fills in the fields @id{source}, @id{short_src}, +fills in the fields @id{source}, @id{srclen}, @id{short_src}, @id{linedefined}, @id{lastlinedefined}, and @id{what}; } @@ -5222,7 +5388,9 @@ Returns @id{NULL} (and pops nothing) when the index is greater than the number of active local variables. -Parameters @id{ar} and @id{n} are as in the function @Lid{lua_getlocal}. +Parameters @id{ar} and @id{n} are as in the function @Lid{lua_getlocal}, +except that @id{ar} cannot be @id{NULL}, +as @id{lua_setlocal} only operates on activation records. } @@ -5289,7 +5457,7 @@ the auxiliary library provides higher-level functions for some common tasks. All functions and types from the auxiliary library -are defined in header file @id{lauxlib.h} and +are defined in the header file @id{lauxlib.h} and have a prefix @id{luaL_}. All functions in the auxiliary library are built on @@ -5433,7 +5601,7 @@ Its pattern of use is as follows: @item{First declare a variable @id{b} of type @Lid{luaL_Buffer}.} -@item{Then initialize it with a call @T{luaL_buffinit(L, &b)}.} +@item{Then initialize it with a call @T{luaL_buffinit(L,&b)}.} @item{ Then add string pieces to the buffer calling any of @@ -5454,12 +5622,12 @@ you can use the buffer like this: @item{First declare a variable @id{b} of type @Lid{luaL_Buffer}.} @item{Then initialize it and preallocate a space of -size @id{sz} with a call @T{luaL_buffinitsize(L, &b, sz)}.} +size @id{sz} with a call @T{luaL_buffinitsize(L,&b,sz)}.} @item{Then produce the string into that space.} @item{ -Finish by calling @T{luaL_pushresultsize(&b, sz)}, +Finish by calling @T{luaL_pushresultsize(&b,sz)}, where @id{sz} is the total size of the resulting string copied into that space (which may be less than or equal to the preallocated size). @@ -5852,9 +6020,8 @@ it does not run it. @apii{0,0,-} Returns a value with a weak attempt for randomness. -(It produces that value based on the current date and time -and the address of an internal variable, -in case the machine has Address Space Layout Randomization.) +The parameter @id{L} can be @id{NULL} +if there is no Lua state available. } @@ -5910,8 +6077,9 @@ with @id{tname} in the registry. @apii{0,0,-} Creates a new Lua state. -It calls @Lid{lua_newstate} with an -allocator based on the @N{ISO C} allocation functions +It calls @Lid{lua_newstate} with @Lid{luaL_alloc} as +the allocator function and the result of @T{luaL_makeseed(NULL)} +as the seed, and then sets a warning function and a panic function @see{C-error} that print messages to the standard error output. @@ -6064,7 +6232,7 @@ You should not manually set integer keys in the table after the first use of @Lid{luaL_ref}. You can retrieve an object referred by the reference @id{r} -by calling @T{lua_rawgeti(L, t, r)} or @T{lua_geti(L, t, r)}. +by calling @T{lua_rawgeti(L,t,r)} or @T{lua_geti(L,t,r)}. The function @Lid{luaL_unref} frees a reference. If the object on the top of the stack is @nil, @@ -6135,6 +6303,15 @@ in the registry @seeC{luaL_newmetatable}. } +@APIEntry{ +void *luaL_alloc (void *ud, void *ptr, size_t osize, size_t nsize);| + +A standard allocator function for Lua @seeF{lua_Alloc}, +built on top of the C functions @id{realloc} and @id{free}. + +} + + @APIEntry{ typedef struct luaL_Stream { FILE *f; @@ -6227,15 +6404,17 @@ Returns the name of the type of the value at the given index. @APIEntry{void luaL_unref (lua_State *L, int t, int ref);| @apii{0,0,-} -Releases the reference @id{ref} from the table at index @id{t} -@seeC{luaL_ref}. -The entry is removed from the table, +Releases a reference @see{luaL_ref}. +The integer @id{ref} must be either +@Lid{LUA_NOREF}, @Lid{LUA_REFNIL}, +or a reference previously returned by @Lid{luaL_ref} +and not already released. +If @id{ref} is either @Lid{LUA_NOREF} or @Lid{LUA_REFNIL} +this function does nothing. +Otherwise, the entry is removed from the table, so that the referred object can be collected and the reference @id{ref} can be used again by @Lid{luaL_ref}. -If @id{ref} is @Lid{LUA_NOREF} or @Lid{LUA_REFNIL}, -@Lid{luaL_unref} does nothing. - } @APIEntry{void luaL_where (lua_State *L, int lvl);| @@ -6331,7 +6510,7 @@ the host program can call the function @Lid{luaL_openlibs}. Alternatively, the host can select which libraries to open, by using @Lid{luaL_openselectedlibs}. -Both functions are defined in the header file @id{lualib.h}. +Both functions are declared in the header file @id{lualib.h}. @index{lualib.h} The stand-alone interpreter @id{lua} @see{lua-sa} @@ -6347,7 +6526,7 @@ Opens all standard Lua libraries into the given state. @APIEntry{void luaL_openselectedlibs (lua_State *L, int load, int preload);| @apii{0,0,e} -Opens (loads) and preloads selected libraries into the state @id{L}. +Opens (loads) and preloads selected standard libraries into the state @id{L}. (To @emph{preload} means to add the library loader into the table @Lid{package.preload}, so that the library can be required later by the program. @@ -6478,7 +6657,7 @@ The call always returns the previous value of the parameter. If the call does not give a new value, the value is left unchanged. -Lua rounds these values before storing them; +Lua stores these values in a compressed format, so, the value returned as the previous value may not be exactly the last value set. } @@ -6492,10 +6671,10 @@ This function should not be called by a finalizer. } @LibEntry{dofile ([filename])| -Opens the named file and executes its content as a Lua chunk. +Opens the named file and executes its content as a Lua chunk, +returning all values returned by the chunk. When called without arguments, @id{dofile} executes the content of the standard input (@id{stdin}). -Returns all values returned by the chunk. In case of errors, @id{dofile} propagates the error to its caller. (That is, @id{dofile} does not run in protected mode.) @@ -6596,11 +6775,10 @@ It may be the string @St{b} (only @x{binary chunk}s), or @St{bt} (both binary and text). The default is @St{bt}. -It is safe to load malformed binary chunks; -@id{load} signals an appropriate error. -However, -Lua does not check the consistency of the code inside binary chunks; -running maliciously crafted bytecode can crash the interpreter. +Lua does not check the consistency of binary chunks. +Maliciously crafted binary chunks can crash +the interpreter. +You can use the @id{mode} parameter to prevent loading binary chunks. } @@ -6645,19 +6823,18 @@ In particular, you may set existing fields to nil. @LibEntry{pairs (t)| If @id{t} has a metamethod @idx{__pairs}, -calls it with @id{t} as argument and returns the first three +calls it with @id{t} as argument and returns the first four results from the call. Otherwise, -returns three values: the @Lid{next} function, the table @id{t}, and @nil, +returns the @Lid{next} function, the table @id{t}, plus two @nil values, so that the construction @verbatim{ for k,v in pairs(t) do @rep{body} end } will iterate over all key@En{}value pairs of table @id{t}. -See function @Lid{next} for the caveats of modifying -the table during its traversal. +See function @Lid{next} for more details about the traversal. } @@ -6812,7 +6989,7 @@ and @St{userdata}. A global variable (not a function) that holds a string containing the running Lua version. -The current value of this variable is @St{Lua 5.4}. +The current value of this variable is @St{Lua 5.5}. } @@ -6849,18 +7026,26 @@ which come inside the table @defid{coroutine}. See @See{coroutine} for a general description of coroutines. -@LibEntry{coroutine.close (co)| +@LibEntry{coroutine.close ([co])| Closes coroutine @id{co}, that is, closes all its pending to-be-closed variables and puts the coroutine in a dead state. -The given coroutine must be dead or suspended. -In case of error +The default for @id{co} is the running coroutine. + +The given coroutine must be dead, suspended, +or be the running coroutine. +For the running coroutine, +this function does not return. +Instead, the resume that (re)started the coroutine returns. + +For other coroutines, +in case of error (either the original error that stopped the coroutine or errors in closing methods), -returns @false plus the error object; -otherwise returns @true. +this function returns @false plus the error object; +otherwise it returns @true. } @@ -7050,7 +7235,7 @@ to search for a @N{C loader}. Lua initializes the @N{C path} @Lid{package.cpath} in the same way it initializes the Lua path @Lid{package.path}, -using the environment variable @defid{LUA_CPATH_5_4}, +using the environment variable @defid{LUA_CPATH_5_5}, or the environment variable @defid{LUA_CPATH}, or a default path defined in @id{luaconf.h}. @@ -7119,7 +7304,7 @@ A string with the path used by @Lid{require} to search for a Lua loader. At start-up, Lua initializes this variable with -the value of the environment variable @defid{LUA_PATH_5_4} or +the value of the environment variable @defid{LUA_PATH_5_5} or the environment variable @defid{LUA_PATH} or with a default path defined in @id{luaconf.h}, if those environment variables are not defined. @@ -7474,25 +7659,25 @@ then there is no replacement Here are some examples: @verbatim{ x = string.gsub("hello world", "(%w+)", "%1 %1") ---> x="hello hello world world" +-- x="hello hello world world" x = string.gsub("hello world", "%w+", "%0 %0", 1) ---> x="hello hello world" +-- x="hello hello world" x = string.gsub("hello world from Lua", "(%w+)%s*(%w+)", "%2 %1") ---> x="world hello Lua from" +-- x="world hello Lua from" x = string.gsub("home = $HOME, user = $USER", "%$(%w+)", os.getenv) ---> x="home = /home/roberto, user = roberto" +-- x="home = /home/roberto, user = roberto" x = string.gsub("4+5 = $return 4+5$", "%$(.-)%$", function (s) return load(s)() end) ---> x="4+5 = 9" +-- x="4+5 = 9" -local t = {name="lua", version="5.4"} +local t = {name="lua", version="5.5"} x = string.gsub("$name-$version.tar.gz", "%$(%w+)", t) ---> x="lua-5.4.tar.gz" +-- x="lua-5.5.tar.gz" } } @@ -7576,7 +7761,7 @@ If @id{j} is absent, then it is assumed to be equal to @num{-1} In particular, the call @T{string.sub(s,1,j)} returns a prefix of @id{s} with length @id{j}, -and @T{string.sub(s, -i)} (for a positive @id{i}) +and @T{string.sub(s,-i)} (for a positive @id{i}) returns a suffix of @id{s} with length @id{i}. @@ -8012,7 +8197,7 @@ the function returns @fail. A negative @id{n} gets characters before position @id{i}. The default for @id{i} is 1 when @id{n} is non-negative and @T{#s + 1} otherwise, -so that @T{utf8.offset(s, -n)} gets the offset of the +so that @T{utf8.offset(s,-n)} gets the offset of the @id{n}-th character from the end of the string. As a special case, @@ -8065,7 +8250,7 @@ the table will have; its default is zero. Inserts element @id{value} at position @id{pos} in @id{list}, shifting up the elements -@T{list[pos], list[pos+1], @Cdots, list[#list]}. +@T{list[pos],list[pos+1],@Cdots,list[#list]}. The default value for @id{pos} is @T{#list+1}, so that a call @T{table.insert(t,x)} inserts @id{x} at the end of the list @id{t}. @@ -8081,6 +8266,8 @@ multiple assignment: The default for @id{a2} is @id{a1}. The destination range can overlap with the source range. The number of elements to be moved must fit in a Lua integer. +If @id{f} is larger than @id{e}, +nothing is moved. Returns the destination table @id{a2}. @@ -8101,7 +8288,7 @@ Removes from @id{list} the element at position @id{pos}, returning the value of the removed element. When @id{pos} is an integer between 1 and @T{#list}, it shifts down the elements -@T{list[pos+1], list[pos+2], @Cdots, list[#list]} +@T{list[pos+1],list[pos+2],@Cdots,list[#list]} and erases element @T{list[#list]}; The index @id{pos} can also be 0 when @T{#list} is 0, or @T{#list + 1}. @@ -8231,6 +8418,17 @@ that rounds the quotient towards zero. (integer/float) } +@LibEntry{math.frexp (x)| + +Returns two numbers @id{m} and @id{e} such that @M{x = m2@sp{e}}, +where @id{e} is an integer. +When @id{x} is zero, NaN, +inf, or -inf, +@id{m} is equal to @id{x}; +otherwise, the absolute value of @id{m} +is in the range @C{(} @M{[0.5, 1)} @C{]}. + +} + @LibEntry{math.huge| The float value @idx{HUGE_VAL}, @@ -8238,6 +8436,12 @@ a value greater than any other numeric value. } +@LibEntry{math.ldexp (m, e)| + +Returns @M{m2@sp{e}}, where @id{e} is an integer. + +} + @LibEntry{math.log (x [, base])| Returns the logarithm of @id{x} in the given base. @@ -8293,7 +8497,7 @@ Converts the angle @id{x} from degrees to radians. When called without arguments, returns a pseudo-random float with uniform distribution -in the range @C{(} @M{[0,1)}. @C{]} +in the range @C{(} @M{[0, 1)}. @C{]} When called with two integers @id{m} and @id{n}, @id{math.random} returns a pseudo-random integer with uniform distribution in the range @M{[m, n]}. @@ -8692,6 +8896,9 @@ Writes the value of each of its arguments to @id{file}. The arguments must be strings or numbers. In case of success, this function returns @id{file}. +Otherwise, it returns four values: +@fail, the error message, the error code, +and the number of bytes it was able to write. } @@ -8917,6 +9124,7 @@ which automatically removes the file when the program ends. This library provides the functionality of the @link{debugI|debug interface} to Lua programs. + You should exert care when using this library. Several of its functions violate basic assumptions about Lua code @@ -8926,6 +9134,8 @@ that userdata metatables cannot be changed by Lua code; that Lua programs do not crash) and therefore can compromise otherwise secure code. Moreover, some functions in this library may be slow. +It is good practice to always require this library explicitly +before using it. All functions in this library are provided inside the @defid{debug} table. @@ -9011,6 +9221,10 @@ Compile-time constants may not appear in this listing, if they were optimized away by the compiler. Negative indices refer to vararg arguments; @num{-1} is the first vararg argument. +These negative indices are only available when the vararg table +has been optimized away; +otherwise, the vararg arguments are available in the vararg table. + The function returns @fail if there is no variable with the given index, and raises an error when called with a level out of range. @@ -9223,7 +9437,7 @@ when the standard input (@id{stdin}) is a terminal, and as @T{lua -} otherwise. When called without the option @T{-E}, -the interpreter checks for an environment variable @defid{LUA_INIT_5_4} +the interpreter checks for an environment variable @defid{LUA_INIT_5_5} (or @defid{LUA_INIT} if the versioned name is not defined) before running any argument. If the variable content has the format @T{@At@rep{filename}}, @@ -9294,15 +9508,21 @@ the interpreter waits for its completion by issuing a different prompt. Note that, as each complete line is read as a new chunk, -local variables do not outlive lines: +local variables do not outlive lines. +To steer clear of confusion, +the interpreter gives a warning if a line starts with the +reserved word @Rw{local}: @verbatim{ -> x = 20 -> local x = 10; print(x) --> 10 -> print(x) --> 20 -- global 'x' -> do -- incomplete line +> x = 20 -- global 'x' +> local x = 10; print(x) + --> warning: locals do not survive across lines in interactive mode + --> 10 +> print(x) -- back to global 'x' + --> 20 +> do -- incomplete chunk >> local x = 10; print(x) -- '>>' prompts for line completion >> print(x) ->> end -- line completed; Lua will run it as a single chunk +>> end -- chunk completed --> 10 --> 10 } @@ -9392,15 +9612,31 @@ change between versions. @itemize{ @item{ -The control variable in @Rw{for} loops are read only. +The word @Rw{global} is a reserved word. +Do not use it as a regular name. + +The compilation option @id{LUA_COMPAT_GLOBAL} (see @id{luaconf.h}) +makes @id{global} a regular word. +} + +@item{ +The control variable in @Rw{for} loops is read only. If you need to change it, declare a local variable with the same name in the loop body. + +The compilation option @id{LUA_COMPAT_LOOPVAR} (see @id{luaconf.h}) +makes these variables regular (writable). } @item{ A chain of @id{__call} metamethods can have at most 15 objects. } +@item{ +In an error, a @nil as the error object is replaced by a +string message. +} + } } @@ -9513,13 +9749,17 @@ and @bnfNter{LiteralString}, see @See{lexical}.) @OrNL @Rw{for} namelist @Rw{in} explist @Rw{do} block @Rw{end} @OrNL @Rw{function} funcname funcbody @OrNL @Rw{local} @Rw{function} @bnfNter{Name} funcbody +@OrNL @Rw{global} @Rw{function} @bnfNter{Name} funcbody @OrNL @Rw{local} attnamelist @bnfopt{@bnfter{=} explist} +@OrNL @Rw{global} attnamelist @bnfopt{@bnfter{=} explist} +@OrNL @Rw{global} @bnfopt{attrib} @bnfter{*} } @producname{attnamelist}@producbody{ - @bnfNter{Name} attrib @bnfrep{@bnfter{,} @bnfNter{Name} attrib}} + @bnfopt{attrib} @bnfNter{Name} @bnfopt{attrib} + @bnfrep{@bnfter{,} @bnfNter{Name} @bnfopt{attrib}}} -@producname{attrib}@producbody{@bnfopt{@bnfter{<} @bnfNter{Name} @bnfter{>}}} +@producname{attrib}@producbody{@bnfter{<} @bnfNter{Name} @bnfter{>}} @producname{retstat}@producbody{@Rw{return} @bnfopt{explist} @bnfopt{@bnfter{;}}} @@ -9573,8 +9813,10 @@ and @bnfNter{LiteralString}, see @See{lexical}.) @producname{funcbody}@producbody{@bnfter{(} @bnfopt{parlist} @bnfter{)} block @Rw{end}} -@producname{parlist}@producbody{namelist @bnfopt{@bnfter{,} @bnfter{...}} - @Or @bnfter{...}} +@producname{parlist}@producbody{namelist @bnfopt{@bnfter{,} varargparam} @Or + varargparam} + +@producname{varargparam}@producbody{@bnfter{...} @bnfopt{@bnfNter{Name}}} @producname{tableconstructor}@producbody{@bnfter{@Open} @bnfopt{fieldlist} @bnfter{@Close}} diff --git a/onelua.c b/onelua.c index 2a43496124..e717121391 100644 --- a/onelua.c +++ b/onelua.c @@ -5,10 +5,14 @@ ** ** $ gcc -O2 -std=c99 -o lua onelua.c -lm ** -** or +** or (for C89) ** ** $ gcc -O2 -std=c89 -DLUA_USE_C89 -o lua onelua.c -lm ** +** or (for Linux) +** +** gcc -O2 -o lua -DLUA_USE_LINUX -Wl,-E onelua.c -lm -ldl +** */ /* default is to build the full interpreter */ @@ -30,7 +34,15 @@ #define LUA_USE_LINUX #define LUA_USE_MACOSX #define LUA_USE_POSIX -#define LUA_ANSI +#endif + + +/* +** Other specific features +*/ +#if 0 +#define LUA_32BITS +#define LUA_USE_C89 #endif @@ -54,12 +66,10 @@ #include #include - /* setup for luaconf.h */ #define LUA_CORE #define LUA_LIB -#define ltable_c -#define lvm_c + #include "luaconf.h" /* do not export internal symbols */ @@ -110,6 +120,11 @@ #include "linit.c" #endif +/* test library -- used only for internal development */ +#if defined(LUA_DEBUG) +#include "ltests.c" +#endif + /* lua */ #ifdef MAKE_LUA #include "lua.c" diff --git a/testes/all.lua b/testes/all.lua old mode 100644 new mode 100755 index 4ffa9efee1..d3e2f12368 --- a/testes/all.lua +++ b/testes/all.lua @@ -1,7 +1,11 @@ #!../lua -- $Id: testes/all.lua $ --- See Copyright Notice at the end of this file +-- See Copyright Notice in file lua.h +global * + +global _soft, _port, _nomsg +global T local version = "Lua 5.5" if _VERSION ~= version then @@ -34,7 +38,7 @@ if usertests then end -- tests should require debug when needed -debug = nil +global debug; debug = nil if usertests then @@ -71,7 +75,7 @@ do -- ( -- track messages for tests not performed local msgs = {} -function Message (m) +global function Message (m) if not _nomsg then print(m) msgs[#msgs+1] = string.sub(m, 3, -3) @@ -182,6 +186,7 @@ dofile('nextvar.lua') dofile('pm.lua') dofile('utf8.lua') dofile('api.lua') +dofile('memerr.lua') assert(dofile('events.lua') == 12) dofile('vararg.lua') dofile('closure.lua') @@ -282,30 +287,3 @@ end print("final OK !!!") - - ---[[ -***************************************************************************** -* 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 -* "Software"), to deal in the Software without restriction, including -* without limitation the rights to use, copy, modify, merge, publish, -* distribute, sublicense, and/or sell copies of the Software, and to -* permit persons to whom the Software is furnished to do so, subject to -* the following conditions: -* -* The above copyright notice and this permission notice shall be -* included in all copies or substantial portions of the Software. -* -* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, -* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF -* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. -* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY -* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, -* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE -* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. -***************************************************************************** -]] - diff --git a/testes/api.lua b/testes/api.lua index 21f703fd17..9855f5411d 100644 --- a/testes/api.lua +++ b/testes/api.lua @@ -1,5 +1,5 @@ -- $Id: testes/api.lua $ --- See Copyright Notice in file all.lua +-- See Copyright Notice in file lua.h if T==nil then (Message or print)('\n >>> testC not active: skipping API tests <<<\n') @@ -11,9 +11,6 @@ local debug = require "debug" local pack = table.pack --- standard error message for memory errors -local MEMERRMSG = "not enough memory" - local function tcheck (t1, t2) assert(t1.n == (t2.n or #t2) + 1) for i = 2, t1.n do assert(t1[i] == t2[i - 1]) end @@ -117,7 +114,7 @@ end -- testing warnings T.testC([[ - warningC "#This shold be a" + warningC "#This should be a" warningC " single " warning "warning" warningC "#This should be " @@ -165,7 +162,7 @@ do -- test returning more results than fit in the caller stack end -do -- testing multipe returns +do -- testing multiple returns local function foo (n) if n > 0 then return n, foo(n - 1) end end @@ -249,7 +246,8 @@ assert(not T.testC("compare LT 1 4, return 1")) assert(not T.testC("compare LE 9 1, return 1")) assert(not T.testC("compare EQ 9 9, return 1")) -local b = {__lt = function (a,b) return a[1] < b[1] end} +local b = {__lt = function (a,b) return a[1] < b[1] end, + __le = function (a,b) return a[1] <= b[1] end} local a1,a3,a4 = setmetatable({1}, b), setmetatable({3}, b), setmetatable({4}, b) @@ -432,11 +430,6 @@ do "bad argument #4 (string expected, got no value)") - -- memory error - T.totalmem(T.totalmem()+10000) -- set low memory limit (+10k) - assert(T.checkpanic("newuserdata 20000") == MEMERRMSG) - T.totalmem(0) -- restore high limit - -- memory error + thread status local x = T.checkpanic( [[ alloccount 0 # force a memory error in next line @@ -910,7 +903,7 @@ F = function (x) assert(T.udataval(A) == B) debug.getmetatable(A) -- just access it end - A = x -- ressurect userdata + A = x -- resurrect userdata B = udval return 1,2,3 end @@ -1306,241 +1299,6 @@ do end ---[[ -** {================================================================== -** Testing memory limits -** =================================================================== ---]] - -print("memory-allocation errors") - -checkerr("block too big", T.newuserdata, math.maxinteger) -collectgarbage() -local f = load"local a={}; for i=1,100000 do a[i]=i end" -T.alloccount(10) -checkerr(MEMERRMSG, f) -T.alloccount() -- remove limit - - --- test memory errors; increase limit for maximum memory by steps, --- o that we get memory errors in all allocations of a given --- task, until there is enough memory to complete the task without --- errors. -local function testbytes (s, f) - collectgarbage() - local M = T.totalmem() - local oldM = M - local a,b = nil - while true do - collectgarbage(); collectgarbage() - T.totalmem(M) - a, b = T.testC("pcall 0 1 0; pushstatus; return 2", f) - T.totalmem(0) -- remove limit - if a and b == "OK" then break end -- stop when no more errors - if b ~= "OK" and b ~= MEMERRMSG then -- not a memory error? - error(a, 0) -- propagate it - end - M = M + 7 -- increase memory limit - end - print(string.format("minimum memory for %s: %d bytes", s, M - oldM)) - return a -end - --- test memory errors; increase limit for number of allocations one --- by one, so that we get memory errors in all allocations of a given --- task, until there is enough allocations to complete the task without --- errors. - -local function testalloc (s, f) - collectgarbage() - local M = 0 - local a,b = nil - while true do - collectgarbage(); collectgarbage() - T.alloccount(M) - a, b = T.testC("pcall 0 1 0; pushstatus; return 2", f) - T.alloccount() -- remove limit - if a and b == "OK" then break end -- stop when no more errors - if b ~= "OK" and b ~= MEMERRMSG then -- not a memory error? - error(a, 0) -- propagate it - end - M = M + 1 -- increase allocation limit - end - print(string.format("minimum allocations for %s: %d allocations", s, M)) - return a -end - - -local function testamem (s, f) - testalloc(s, f) - return testbytes(s, f) -end - - --- doing nothing -b = testamem("doing nothing", function () return 10 end) -assert(b == 10) - --- testing memory errors when creating a new state - -testamem("state creation", function () - local st = T.newstate() - if st then T.closestate(st) end -- close new state - return st -end) - -testamem("empty-table creation", function () - return {} -end) - -testamem("string creation", function () - return "XXX" .. "YYY" -end) - -testamem("coroutine creation", function() - return coroutine.create(print) -end) - - --- testing to-be-closed variables -testamem("to-be-closed variables", function() - local flag - do - local x = - setmetatable({}, {__close = function () flag = true end}) - flag = false - local x = {} - end - return flag -end) - - --- testing threads - --- get main thread from registry -local mt = T.testC("rawgeti R !M; return 1") -assert(type(mt) == "thread" and coroutine.running() == mt) - - - -local function expand (n,s) - if n==0 then return "" end - local e = string.rep("=", n) - return string.format("T.doonnewstack([%s[ %s;\n collectgarbage(); %s]%s])\n", - e, s, expand(n-1,s), e) -end - -G=0; collectgarbage(); a =collectgarbage("count") -load(expand(20,"G=G+1"))() -assert(G==20); collectgarbage(); -- assert(gcinfo() <= a+1) -G = nil - -testamem("running code on new thread", function () - return T.doonnewstack("local x=1") == 0 -- try to create thread -end) - - --- testing memory x compiler - -testamem("loadstring", function () - return load("x=1") -- try to do load a string -end) - - -local testprog = [[ -local function foo () return end -local t = {"x"} -AA = "aaa" -for i = 1, #t do AA = AA .. t[i] end -return true -]] - --- testing memory x dofile -_G.AA = nil -local t =os.tmpname() -local f = assert(io.open(t, "w")) -f:write(testprog) -f:close() -testamem("dofile", function () - local a = loadfile(t) - return a and a() -end) -assert(os.remove(t)) -assert(_G.AA == "aaax") - - --- other generic tests - -testamem("gsub", function () - local a, b = string.gsub("alo alo", "(a)", function (x) return x..'b' end) - return (a == 'ablo ablo') -end) - -testamem("dump/undump", function () - local a = load(testprog) - local b = a and string.dump(a) - a = b and load(b) - return a and a() -end) - -_G.AA = nil - -local t = os.tmpname() -testamem("file creation", function () - local f = assert(io.open(t, 'w')) - assert (not io.open"nomenaoexistente") - io.close(f); - return not loadfile'nomenaoexistente' -end) -assert(os.remove(t)) - -testamem("table creation", function () - local a, lim = {}, 10 - for i=1,lim do a[i] = i; a[i..'a'] = {} end - return (type(a[lim..'a']) == 'table' and a[lim] == lim) -end) - -testamem("constructors", function () - local a = {10, 20, 30, 40, 50; a=1, b=2, c=3, d=4, e=5} - return (type(a) == 'table' and a.e == 5) -end) - -local a = 1 -local close = nil -testamem("closure creation", function () - function close (b) - return function (x) return b + x end - end - return (close(2)(4) == 6) -end) - -testamem("using coroutines", function () - local a = coroutine.wrap(function () - coroutine.yield(string.rep("a", 10)) - return {} - end) - assert(string.len(a()) == 10) - return a() -end) - -do -- auxiliary buffer - local lim = 100 - local a = {}; for i = 1, lim do a[i] = "01234567890123456789" end - testamem("auxiliary buffer", function () - return (#table.concat(a, ",") == 20*lim + lim - 1) - end) -end - -testamem("growing stack", function () - local function foo (n) - if n == 0 then return 1 else return 1 + foo(n - 1) end - end - return foo(100) -end) - --- }================================================================== - - do -- testing failing in 'lua_checkstack' local res = T.testC([[rawcheckstack 500000; return 1]]) assert(res == false) diff --git a/testes/attrib.lua b/testes/attrib.lua index 9054e0b64d..f415608699 100644 --- a/testes/attrib.lua +++ b/testes/attrib.lua @@ -1,5 +1,5 @@ -- $Id: testes/attrib.lua $ --- See Copyright Notice in file all.lua +-- See Copyright Notice in file lua.h print "testing require" @@ -308,11 +308,11 @@ else _ENV.x, _ENV.y = nil end + _ENV = _G -- testing preload - do local p = package package = {} @@ -331,6 +331,26 @@ do assert(type(package.path) == "string") end + +do print("testing external strings") + package.cpath = DC"?" + local lib2 = require"lib2-v2" + local t = {} + for _, len in ipairs{0, 10, 39, 40, 41, 1000} do + local str = string.rep("a", len) + local str1 = lib2.newstr(str) + assert(str == str1) + assert(not T or T.hash(str) == T.hash(str1)) + t[str1] = 20; assert(t[str] == 20 and t[str1] == 20) + t[str] = 10; assert(t[str1] == 10) + local tt = {[str1] = str1} + assert(next(tt) == str1 and next(tt, str1) == nil) + assert(tt[str] == str) + local str2 = lib2.newstr(str1) + assert(str == str2 and t[str2] == 10 and tt[str2] == str) + end +end + print('+') end --] @@ -447,7 +467,7 @@ do end --- test of large float/integer indices +-- test of large float/integer indices -- compute maximum integer where all bits fit in a float local maxint = math.maxinteger diff --git a/testes/big.lua b/testes/big.lua index 46fd846674..119caa6c32 100644 --- a/testes/big.lua +++ b/testes/big.lua @@ -1,5 +1,5 @@ -- $Id: testes/big.lua $ --- See Copyright Notice in file all.lua +-- See Copyright Notice in file lua.h if _soft then return 'a' diff --git a/testes/bitwise.lua b/testes/bitwise.lua index dd0a1a9a39..10afff432f 100644 --- a/testes/bitwise.lua +++ b/testes/bitwise.lua @@ -1,5 +1,5 @@ -- $Id: testes/bitwise.lua $ --- See Copyright Notice in file all.lua +-- See Copyright Notice in file lua.h print("testing bitwise operations") diff --git a/testes/bwcoercion.lua b/testes/bwcoercion.lua index cd735ab0b6..0544944d84 100644 --- a/testes/bwcoercion.lua +++ b/testes/bwcoercion.lua @@ -4,7 +4,7 @@ local strsub = string.sub local print = print -_ENV = nil +global none -- Try to convert a value to an integer, without assuming any coercion. local function toint (x) diff --git a/testes/calls.lua b/testes/calls.lua index 310282157d..cd4510a25f 100644 --- a/testes/calls.lua +++ b/testes/calls.lua @@ -1,5 +1,7 @@ -- $Id: testes/calls.lua $ --- See Copyright Notice in file all.lua +-- See Copyright Notice in file lua.h + +global * print("testing functions and calls") @@ -22,7 +24,7 @@ assert(not pcall(type)) -- testing local-function recursion -fact = false +global fact = false do local res = 1 local function fact (n) @@ -63,7 +65,7 @@ a.b.c:f2('k', 12); assert(a.b.c.k == 12) print('+') -t = nil -- 'declare' t +global t = nil -- 'declare' t function f(a,b,c) local d = 'a'; t={a,b,c,d} end f( -- this line change must be valid @@ -75,7 +77,7 @@ assert(t[1] == 1 and t[2] == 2 and t[3] == 3 and t[4] == 'a') t = nil -- delete 't' -function fat(x) +global function fat(x) if x <= 1 then return 1 else return x*load("return fat(" .. x-1 .. ")", "")() end @@ -107,7 +109,7 @@ end _G.deep = nil -- "declaration" (used by 'all.lua') -function deep (n) +global function deep (n) if n>0 then deep(n-1) end end deep(10) @@ -352,7 +354,7 @@ assert(not load(function () return true end)) -- small bug local t = {nil, "return ", "3"} -f, msg = load(function () return table.remove(t, 1) end) +local f, msg = load(function () return table.remove(t, 1) end) assert(f() == nil) -- should read the empty chunk -- another small bug (in 5.2.1) @@ -370,6 +372,32 @@ do -- another bug (in 5.4.0) end +if T then + -- check stack level when calling reader function + local function get (str) + local pos = 0 + local level = nil + return function () + pos = pos + 1 + local c = string.sub(str, pos, pos) + local newlevel = T.stacklevel() + if not level then + level = newlevel + else + assert(level == newlevel) + end + return #c > 0 and c or nil + end + end + + local str = "local function foo () end; return 121" + assert(assert(load(get(str)))() == 121) + + str = string.dump(load(str)) + assert(assert(load(get(str)))() == 121) +end + + x = string.dump(load("x = 1; return x")) a = assert(load(read1(x), nil, "b")) assert(a() == 1 and _G.x == 1) @@ -388,7 +416,8 @@ assert(load("return _ENV", nil, nil, 123)() == 123) -- load when _ENV is not first upvalue -local x; XX = 123 +global XX; local x +XX = 123 local function h () local y=x -- use 'x', so that it becomes 1st upvalue return XX -- global name @@ -480,15 +509,22 @@ assert((function (a) return a end)() == nil) print("testing binary chunks") do - local header = string.pack("c4BBc6BBB", - "\27Lua", -- signature - 0x55, -- version 5.5 (0x55) - 0, -- format - "\x19\x93\r\n\x1a\n", -- data - 4, -- size of instruction - string.packsize("j"), -- sizeof(lua integer) - string.packsize("n") -- sizeof(lua number) - ) + local headformat = "c4BBc6BiBI4BjBn" + local header = { -- header components + "\27Lua", -- signature + 0x55, -- version 5.5 (0x55) + 0, -- format + "\x19\x93\r\n\x1a\n", -- a binary string + string.packsize("i"), -- size of an int + -0x5678, -- an int + 4, -- size of an instruction + 0x12345678, -- an instruction (4 bytes) + string.packsize("j"), -- size of a Lua integer + -0x5678, -- a Lua integer + string.packsize("n"), -- size of a Lua float + -370.5, -- a Lua float + } + local c = string.dump(function () local a = 1; local b = 3; local f = function () return a + b + _ENV.c; end -- upvalues @@ -500,17 +536,23 @@ do assert(assert(load(c))() == 10) -- check header - assert(string.sub(c, 1, #header) == header) - -- check LUAC_INT and LUAC_NUM - local ci, cn = string.unpack("jn", c, #header + 1) - assert(ci == 0x5678 and cn == 370.5) - - -- corrupted header + local t = {string.unpack(headformat, c)} for i = 1, #header do + assert(t[i] == header[i]) + end + + -- Testing corrupted header. + -- A single wrong byte in the head invalidates the chunk, + -- except for the Lua float check. (If numbers are long double, + -- the representation may need padding, and changing that padding + -- will not invalidate the chunk.) + local headlen = string.packsize(headformat) + headlen = headlen - string.packsize("n") -- remove float check + for i = 1, headlen do local s = string.sub(c, 1, i - 1) .. - string.char(string.byte(string.sub(c, i, i)) + 1) .. + string.char((string.byte(string.sub(c, i, i)) + 1) & 0xFF) .. string.sub(c, i + 1, -1) - assert(#s == #c) + assert(#s == #c and s ~= c) assert(not load(s)) end diff --git a/testes/closure.lua b/testes/closure.lua index 07149ef36a..0c2e96c0f1 100644 --- a/testes/closure.lua +++ b/testes/closure.lua @@ -1,5 +1,7 @@ -- $Id: testes/closure.lua $ --- See Copyright Notice in file all.lua +-- See Copyright Notice in file lua.h + +global * print "testing closures" diff --git a/testes/code.lua b/testes/code.lua index 50ce7392f3..380ff70c1b 100644 --- a/testes/code.lua +++ b/testes/code.lua @@ -1,5 +1,7 @@ -- $Id: testes/code.lua $ --- See Copyright Notice in file all.lua +-- See Copyright Notice in file lua.h + +global * if T==nil then (Message or print)('\n >>> testC not active: skipping opcode tests <<<\n') @@ -405,8 +407,8 @@ do -- tests for table access in upvalues end -- de morgan -checkequal(function () local a; if not (a or b) then b=a end end, - function () local a; if (not a and not b) then b=a end end) +checkequal(function () local a, b; if not (a or b) then b=a end end, + function () local a, b; if (not a and not b) then b=a end end) checkequal(function (l) local a; return 0 <= a and a <= l end, function (l) local a; return not (not(a >= 0) or not(a <= l)) end) @@ -480,5 +482,23 @@ do -- basic check for SETLIST assert(count == 1) end + +do print("testing code for integer limits") + local function checkints (n) + local source = string.format( + "local a = {[true] = 0X%x}; return a[true]", n) + local f = assert(load(source)) + checkKlist(f, {n}) + assert(f() == n) + f = load(string.dump(f)) + assert(f() == n) + end + + checkints(math.maxinteger) + checkints(math.mininteger) + checkints(-1) + +end + print 'OK' diff --git a/testes/constructs.lua b/testes/constructs.lua index 6ac6816671..94f670c7a5 100644 --- a/testes/constructs.lua +++ b/testes/constructs.lua @@ -1,5 +1,5 @@ -- $Id: testes/constructs.lua $ --- See Copyright Notice in file all.lua +-- See Copyright Notice in file lua.h ;;print "testing syntax";; @@ -60,7 +60,7 @@ assert((x>y) and x or y == 2); assert(1234567890 == tonumber('1234567890') and 1234567890+1 == 1234567891) -do -- testing operators with diffent kinds of constants +do -- testing operators with different kinds of constants -- operands to consider: -- * fit in register -- * constant doesn't fit in register diff --git a/testes/coroutine.lua b/testes/coroutine.lua index 78b9bdca19..ba394e0c46 100644 --- a/testes/coroutine.lua +++ b/testes/coroutine.lua @@ -1,5 +1,5 @@ -- $Id: testes/coroutine.lua $ --- See Copyright Notice in file all.lua +-- See Copyright Notice in file lua.h print "testing coroutines" @@ -127,6 +127,18 @@ assert(#a == 22 and a[#a] == 79) x, a = nil +do -- "bug" in 5.4.2 + local function foo () foo () end -- just create a stack overflow + local co = coroutine.create(foo) + -- running this coroutine would overflow the unsigned short 'nci', the + -- counter of CallInfo structures available to the thread. + -- (The issue only manifests in an 'assert'.) + local st, msg = coroutine.resume(co) + assert(string.find(msg, "stack overflow")) + assert(coroutine.status(co) == "dead") +end + + print("to-be-closed variables in coroutines") local function func2close (f) @@ -144,12 +156,12 @@ do st, msg = coroutine.close(co) assert(st and msg == nil) + local main = coroutine.running() - -- cannot close the running coroutine - local st, msg = pcall(coroutine.close, coroutine.running()) - assert(not st and string.find(msg, "running")) + -- cannot close 'main' + local st, msg = pcall(coroutine.close, main); + assert(not st and string.find(msg, "main")) - local main = coroutine.running() -- cannot close a "normal" coroutine ;(coroutine.wrap(function () @@ -157,20 +169,19 @@ do assert(not st and string.find(msg, "normal")) end))() - -- cannot close a coroutine while closing it - do + do -- close a coroutine while closing it local co co = coroutine.create( function() local x = func2close(function() - coroutine.close(co) -- try to close it again + coroutine.close(co) -- close it again end) coroutine.yield(20) end) local st, msg = coroutine.resume(co) assert(st and msg == 20) st, msg = coroutine.close(co) - assert(not st and string.find(msg, "running coroutine")) + assert(st and msg == nil) end -- to-be-closed variables in coroutines @@ -277,6 +288,56 @@ do end +do print("coroutines closing itself") + global coroutine, string, os + global assert, error, pcall + + local X = nil + + local function new () + return coroutine.create(function (what) + + local var = func2close(function (t, err) + if what == "yield" then + coroutine.yield() + elseif what == "error" then + error(200) + else + X = "Ok" + return X + end + end) + + -- do an unprotected call so that coroutine becomes non-yieldable + string.gsub("a", "a", function () + assert(not coroutine.isyieldable()) + -- do protected calls while non-yieldable, to add recovery + -- entries (setjmp) to the stack + assert(pcall(pcall, function () + -- 'close' works even while non-yieldable + coroutine.close() -- close itself + os.exit(false) -- not reacheable + end)) + end) + end) + end + + local co = new() + local st, msg = coroutine.resume(co, "ret") + assert(st and msg == nil) + assert(X == "Ok") + + local co = new() + local st, msg = coroutine.resume(co, "error") + assert(not st and msg == 200) + + local co = new() + local st, msg = coroutine.resume(co, "yield") + assert(not st and string.find(msg, "attempt to yield")) + +end + + -- yielding across C boundaries local co = coroutine.wrap(function() @@ -493,6 +554,25 @@ assert(not pcall(a, a)) a = nil +do + -- bug in 5.4: thread can use message handler higher in the stack + -- than the variable being closed + local c = coroutine.create(function() + local clo = setmetatable({}, {__close=function() + local x = 134 -- will overwrite message handler + error(x) + end}) + -- yields coroutine but leaves a new message handler for it, + -- that would be used when closing the coroutine (except that it + -- will be overwritten) + xpcall(coroutine.yield, function() return "XXX" end) + end) + + assert(coroutine.resume(c)) -- start coroutine + local st, msg = coroutine.close(c) + assert(not st and msg == 134) +end + -- access to locals of erroneous coroutines local x = coroutine.create (function () local a = 10 @@ -622,7 +702,9 @@ else assert(t.currentline == t.linedefined + 2) assert(not debug.getinfo(c, 1)) -- no other level assert(coroutine.resume(c)) -- run next line - local n,v = debug.getlocal(c, 0, 2) -- check next local + local n,v = debug.getlocal(c, 0, 2) -- check vararg table + assert(n == "(vararg table)" and v == nil) + local n,v = debug.getlocal(c, 0, 3) -- check next local assert(n == "b" and v == 10) v = {coroutine.resume(c)} -- finish coroutine assert(v[1] == true and v[2] == 2 and v[3] == 3 and v[4] == undef) diff --git a/testes/cstack.lua b/testes/cstack.lua index 97afe9fd03..0a68a30ac2 100644 --- a/testes/cstack.lua +++ b/testes/cstack.lua @@ -1,5 +1,5 @@ -- $Id: testes/cstack.lua $ --- See Copyright Notice in file all.lua +-- See Copyright Notice in file lua.h local tracegc = require"tracegc" diff --git a/testes/db.lua b/testes/db.lua index 75730d27b0..e15a5be6bd 100644 --- a/testes/db.lua +++ b/testes/db.lua @@ -1,5 +1,5 @@ -- $Id: testes/db.lua $ --- See Copyright Notice in file all.lua +-- See Copyright Notice in file lua.h -- testing debug library @@ -349,12 +349,15 @@ end, "crl") function f(a,b) + -- declare some globals to check that they don't interfere with 'getlocal' + global collectgarbage collectgarbage() local _, x = debug.getlocal(1, 1) + global assert, g, string local _, y = debug.getlocal(1, 2) assert(x == a and y == b) - assert(debug.setlocal(2, 3, "pera") == "AA".."AA") - assert(debug.setlocal(2, 4, "manga") == "B") + assert(debug.setlocal(2, 4, "pera") == "AA".."AA") + assert(debug.setlocal(2, 5, "manga") == "B") x = debug.getinfo(2) assert(x.func == g and x.what == "Lua" and x.name == 'g' and x.nups == 2 and string.find(x.source, "^@.*db%.lua$")) @@ -386,8 +389,10 @@ function g (...) f(AAAA,B) assert(AAAA == "pera" and B == "manga") do + global * local B = 13 - local x,y = debug.getlocal(1,5) + global assert + local x,y = debug.getlocal(1,6) assert(x == 'B' and y == 13) end end @@ -431,7 +436,7 @@ do assert(a == nil and not b) end --- testing iteraction between multiple values x hooks +-- testing interaction between multiple values x hooks do local function f(...) return 3, ... end local count = 0 @@ -453,7 +458,8 @@ local function collectlocals (level) local tab = {} for i = 1, math.huge do local n, v = debug.getlocal(level + 1, i) - if not (n and string.find(n, "^[a-zA-Z0-9_]+$")) then + if not (n and string.find(n, "^[a-zA-Z0-9_]+$") or + n == "(vararg table)") then break -- consider only real variables end tab[n] = v @@ -587,7 +593,7 @@ t = getupvalues(foo2) assert(t.a == 1 and t.b == 2 and t.c == 3) assert(debug.setupvalue(foo1, 1, "xuxu") == "b") assert(({debug.getupvalue(foo2, 3)})[2] == "xuxu") --- upvalues of C functions are allways "called" "" (the empty string) +-- upvalues of C functions are always named "" (the empty string) assert(debug.getupvalue(string.gmatch("x", "x"), 1) == "") @@ -701,7 +707,7 @@ assert(debug.traceback(print, 4) == print) assert(string.find(debug.traceback("hi", 4), "^hi\n")) assert(string.find(debug.traceback("hi"), "^hi\n")) assert(not string.find(debug.traceback("hi"), "'debug.traceback'")) -assert(string.find(debug.traceback("hi", 0), "'debug.traceback'")) +assert(string.find(debug.traceback("hi", 0), "'traceback'")) assert(string.find(debug.traceback(), "^stack traceback:\n")) do -- C-function names in traceback @@ -720,6 +726,9 @@ assert(t.isvararg == false and t.nparams == 3 and t.nups == 0) t = debug.getinfo(function (a,b,...) return t[a] end, "u") assert(t.isvararg == true and t.nparams == 2 and t.nups == 1) +t = debug.getinfo(function (a,b,...t) t.n = 2; return t[a] end, "u") +assert(t.isvararg == true and t.nparams == 2 and t.nups == 0) + t = debug.getinfo(1) -- main assert(t.isvararg == true and t.nparams == 0 and t.nups == 1 and debug.getupvalue(t.func, 1) == "_ENV") @@ -829,7 +838,7 @@ end co = coroutine.create(function (x) f(x) end) a, b = coroutine.resume(co, 3) -t = {"'coroutine.yield'", "'f'", "in function <"} +t = {"'yield'", "'f'", "in function <"} while coroutine.status(co) == "suspended" do checktraceback(co, t) a, b = coroutine.resume(co) @@ -839,7 +848,7 @@ t[1] = "'error'" checktraceback(co, t) --- test acessing line numbers of a coroutine from a resume inside +-- test accessing line numbers of a coroutine from a resume inside -- a C function (this is a known bug in Lua 5.0) local function g(x) @@ -966,9 +975,9 @@ local debug = require'debug' local a = 12 -- a local variable local n, v = debug.getlocal(1, 1) -assert(n == "(temporary)" and v == debug) -- unkown name but known value +assert(n == "(temporary)" and v == debug) -- unknown name but known value n, v = debug.getlocal(1, 2) -assert(n == "(temporary)" and v == 12) -- unkown name but known value +assert(n == "(temporary)" and v == 12) -- unknown name but known value -- a function with an upvalue local f = function () local x; return a end @@ -1018,7 +1027,7 @@ do -- bug in 5.4.0: line hooks in stripped code line = l end, "l") assert(s() == 2); debug.sethook(nil) - assert(line == nil) -- hook called withoug debug info for 1st instruction + assert(line == nil) -- hook called without debug info for 1st instruction end do -- tests for 'source' in binary dumps diff --git a/testes/errors.lua b/testes/errors.lua index 027e1b03af..c82d5b3b76 100644 --- a/testes/errors.lua +++ b/testes/errors.lua @@ -1,5 +1,5 @@ -- $Id: testes/errors.lua $ --- See Copyright Notice in file all.lua +-- See Copyright Notice in file lua.h print("testing errors") @@ -45,8 +45,8 @@ end -- test error message with no extra info assert(doit("error('hi', 0)") == 'hi') --- test error message with no info -assert(doit("error()") == nil) +-- test nil error message +assert(doit("error()") == "") -- test common errors/errors that crashed in the past @@ -159,9 +159,15 @@ assert(not string.find(doit"aaa={13}; local bbbb=1; aaa[bbbb](3)", "'bbbb'")) checkmessage("aaa={13}; local bbbb=1; aaa[bbbb](3)", "number") checkmessage("aaa=(1)..{}", "a table value") +checkmessage("local function foo (...t) return t.xx + 1 end; foo()", + "field 'xx'") + -- bug in 5.4.6 checkmessage("a = {_ENV = {}}; print(a._ENV.x + 1)", "field 'x'") +-- a similar bug, since 5.4.0 +checkmessage("print(('_ENV').x + 1)", "field 'x'") + _G.aaa, _G.bbbb = nil -- calls @@ -300,14 +306,14 @@ do local f = function (a) return a + 1 end f = assert(load(string.dump(f, true))) assert(f(3) == 4) - checkerr("^%?:%-1:", f, {}) + checkerr("^%?:%?:", f, {}) -- code with a move to a local var ('OP_MOV A B' with A a, b +]], 1, "multiple") + if not _soft then - -- several tests that exaust the Lua stack + -- several tests that exhaust the Lua stack collectgarbage() print"testing stack overflow" local C = 0 @@ -555,7 +589,7 @@ if not _soft then -- error in error handling local res, msg = xpcall(error, error) - assert(not res and type(msg) == 'string') + assert(not res and msg == 'error in error handling') print('+') local function f (x) @@ -586,6 +620,27 @@ if not _soft then end +do -- errors in error handle that not necessarily go forever + local function err (n) -- function to be used as message handler + -- generate an error unless n is zero, so that there is a limited + -- loop of errors + if type(n) ~= "number" then -- some other error? + return n -- report it + elseif n == 0 then + return "END" -- that will be the final message + else error(n - 1) -- does the loop + end + end + + local res, msg = xpcall(error, err, 170) + assert(not res and msg == "END") + + -- too many levels + local res, msg = xpcall(error, err, 300) + assert(not res and msg == "C stack overflow") +end + + do -- non string messages local t = {} @@ -593,7 +648,7 @@ do assert(not res and msg == t) res, msg = pcall(function () error(nil) end) - assert(not res and msg == nil) + assert(not res and msg == "") local function f() error{msg='x'} end res, msg = xpcall(f, function (r) return {msg=r.msg..'y'} end) @@ -613,7 +668,7 @@ do assert(not res and msg == t) res, msg = pcall(assert, nil, nil) - assert(not res and msg == nil) + assert(not res and type(msg) == "string") -- 'assert' without arguments res, msg = pcall(assert) @@ -657,21 +712,26 @@ end -- testing syntax limits local function testrep (init, rep, close, repc, finalresult) - local s = init .. string.rep(rep, 100) .. close .. string.rep(repc, 100) - local res, msg = load(s) - assert(res) -- 100 levels is OK + local function gencode (n) + return init .. string.rep(rep, n) .. close .. string.rep(repc, n) + end + local res, msg = load(gencode(100)) -- 100 levels is OK + assert(res) if (finalresult) then assert(res() == finalresult) end - s = init .. string.rep(rep, 500) - local res, msg = load(s) -- 500 levels not ok + local res, msg = load(gencode(500)) -- 500 levels not ok assert(not res and (string.find(msg, "too many") or string.find(msg, "overflow"))) end +testrep("local a", ",a", ";", "") -- local variables +testrep("local a", ",a", "= 1", ",1") -- local variables initialized +testrep("local a", ",a", "= f()", "") -- local variables initialized testrep("local a; a", ",a", "= 1", ",1") -- multiple assignment -testrep("local a; a=", "{", "0", "}") -testrep("return ", "(", "2", ")", 2) +testrep("local a; a=", "{", "0", "}") -- constructors +testrep("return ", "(", "2", ")", 2) -- parentheses +-- nested calls (a(a(a(a(...))))) testrep("local function a (x) return x end; return ", "a(", "2.2", ")", 2.2) testrep("", "do ", "", " end") testrep("", "while a do ", "", " end") @@ -710,7 +770,7 @@ assert(c > 255 and string.find(b, "too many upvalues") and -- local variables s = "\nfunction foo ()\n local " -for j = 1,300 do +for j = 1,200 do s = s.."a"..j..", " end s = s.."b\n" diff --git a/testes/events.lua b/testes/events.lua index 5360ac301c..7e434b1f6f 100644 --- a/testes/events.lua +++ b/testes/events.lua @@ -1,5 +1,5 @@ -- $Id: testes/events.lua $ --- See Copyright Notice in file all.lua +-- See Copyright Notice in file lua.h print('testing metatables') @@ -379,6 +379,17 @@ x = 0 .."a".."b"..c..d.."e".."f".."g" assert(x.val == "0abcdefg") +do + -- bug since 5.4.1 (test needs T) + local mt = setmetatable({__newindex={}}, {__mode='v'}) + local t = setmetatable({}, mt) + + if T then T.allocfailnext() end + + -- seg. fault + for i=1, 10 do t[i] = 1 end +end + -- concat metamethod x numbers (bug in 5.1.1) c = {} local x @@ -481,7 +492,7 @@ assert(not pcall(function (a,b) return a[b] end, a, 10)) assert(not pcall(function (a,b,c) a[b] = c end, a, 10, true)) -- bug in 5.1 -T, K, V = nil +local T, K, V = nil grandparent = {} grandparent.__newindex = function(t,k,v) T=t; K=k; V=v end diff --git a/testes/files.lua b/testes/files.lua index 9bdf04d09a..7146ac7ca2 100644 --- a/testes/files.lua +++ b/testes/files.lua @@ -1,5 +1,7 @@ -- $Id: testes/files.lua $ --- See Copyright Notice in file all.lua +-- See Copyright Notice in file lua.h + +global * local debug = require "debug" @@ -347,7 +349,7 @@ collectgarbage() assert(io.write(' ' .. t .. ' ')) assert(io.write(';', 'end of file\n')) -f:flush(); io.flush() +assert(f:flush()); assert(io.flush()) f:close() print('+') @@ -461,7 +463,24 @@ do -- testing closing file in line iteration end --- test for multipe arguments in 'lines' +do print("testing flush") + local f = io.output("/dev/null") + assert(f:write("abcd")) -- write to buffer + assert(f:flush()) -- write to device + assert(f:write("abcd")) -- write to buffer + assert(io.flush()) -- write to device + assert(f:close()) + + local f = io.output("/dev/full") + assert(f:write("abcd")) -- write to buffer + assert(not f:flush()) -- cannot write to device + assert(f:write("abcd")) -- write to buffer + assert(not io.flush()) -- cannot write to device + assert(f:close()) +end + + +-- test for multiple arguments in 'lines' io.output(file); io.write"0123456789\n":close() for a,b in io.lines(file, 1, 1) do if a == "\n" then assert(not b) @@ -696,6 +715,37 @@ do end +if T and T.nonblock and not _port then + print("testing failed write") + + -- unable to write anything to /dev/full + local f = io.open("/dev/full", "w") + assert(f:setvbuf("no")) + local _, _, err, count = f:write("abcd") + assert(err > 0 and count == 0) + assert(f:close()) + + -- receiver will read a "few" bytes (enough to empty a large buffer) + local receiver = [[ + lua -e 'assert(io.stdin:setvbuf("no")); assert(#io.read(1e4) == 1e4)' ]] + + local f = io.popen(receiver, "w") + assert(f:setvbuf("no")) + T.nonblock(f) + + -- able to write a few bytes + assert(f:write(string.rep("a", 1e2))) + + -- Unable to write more bytes than the pipe buffer supports. + -- (In Linux, the pipe buffer size is 64K (2^16). Posix requires at + -- least 512 bytes.) + local _, _, err, count = f:write("abcd", string.rep("a", 2^17)) + assert(err > 0 and count >= 512 and count < 2^17) + + assert(f:close()) +end + + if not _soft then print("testing large files (> BUFSIZ)") io.output(file) @@ -790,13 +840,13 @@ assert(os.date("!\0\0") == "\0\0") local x = string.rep("a", 10000) assert(os.date(x) == x) local t = os.time() -D = os.date("*t", t) +global D = os.date("*t", t) assert(os.date(string.rep("%d", 1000), t) == string.rep(os.date("%d", t), 1000)) assert(os.date(string.rep("%", 200)) == string.rep("%", 100)) local function checkDateTable (t) - _G.D = os.date("*t", t) + D = os.date("*t", t) assert(os.time(D) == t) load(os.date([[assert(D.year==%Y and D.month==%m and D.day==%d and D.hour==%H and D.min==%M and D.sec==%S and diff --git a/testes/gc.lua b/testes/gc.lua index 09bfe09ab3..e50d9029b0 100644 --- a/testes/gc.lua +++ b/testes/gc.lua @@ -1,5 +1,5 @@ -- $Id: testes/gc.lua $ --- See Copyright Notice in file all.lua +-- See Copyright Notice in file lua.h print('testing incremental garbage collection') @@ -288,6 +288,21 @@ x,y,z=nil collectgarbage() assert(next(a) == string.rep('$', 11)) +do -- invalid mode + local a = setmetatable({}, {__mode = 34}) + collectgarbage() +end + + +if T then -- bug since 5.3: all-weak tables are not being revisited + T.gcstate("propagate") + local t = setmetatable({}, {__mode = "kv"}) + T.gcstate("enteratomic") -- 't' was visited + setmetatable(t, {__mode = "kv"}) + T.gcstate("pause") -- its new metatable is not being visited + assert(getmetatable(t).__mode == "kv") +end + -- 'bug' in 5.1 a = {} @@ -446,8 +461,8 @@ do -- tests for string keys in weak tables local m = collectgarbage("count") -- current memory local a = setmetatable({}, {__mode = "kv"}) a[string.rep("a", 2^22)] = 25 -- long string key -> number value - a[string.rep("b", 2^22)] = {} -- long string key -> colectable value - a[{}] = 14 -- colectable key + a[string.rep("b", 2^22)] = {} -- long string key -> collectable value + a[{}] = 14 -- collectable key collectgarbage() local k, v = next(a) -- string key with number value preserved assert(k == string.rep("a", 2^22) and v == 25) @@ -459,7 +474,7 @@ do -- tests for string keys in weak tables assert(next(a) == nil) -- make sure will not try to compare with dead key assert(a[string.rep("b", 100)] == undef) - assert(collectgarbage("count") <= m + 1) -- eveything collected + assert(collectgarbage("count") <= m + 1) -- everything collected end @@ -524,7 +539,7 @@ do local co = coroutine.create(f) assert(coroutine.resume(co, co)) end - -- Now, thread and closure are not reacheable any more. + -- Now, thread and closure are not reachable any more. collectgarbage() assert(collected) collectgarbage("restart") @@ -600,6 +615,21 @@ if T then end +if T then + collectgarbage("stop") + T.gcstate("pause") + local sup = {x = 0} + local a = setmetatable({}, {__newindex = sup}) + T.gcstate("enteratomic") + assert(T.gccolor(sup) == "black") + a.x = {} -- should not break the invariant + assert(not (T.gccolor(sup) == "black" and T.gccolor(sup.x) == "white")) + T.gcstate("pause") -- complete the GC cycle + sup.x.y = 10 + collectgarbage("restart") +end + + if T then print("emergency collections") collectgarbage() @@ -629,7 +659,7 @@ do assert(getmetatable(o) == tt) -- create new objects during GC local a = 'xuxu'..(10+3)..'joao', {} - ___Glob = o -- ressurrect object! + ___Glob = o -- resurrect object! setmetatable({}, tt) -- creates a new one with same metatable print(">>> closing state " .. "<<<\n") end @@ -677,4 +707,46 @@ end collectgarbage(oldmode) + +if T then + print("testing stack issues when calling finalizers") + + local X + local obj + + local function initobj () + X = false + obj = setmetatable({}, {__gc = function () X = true end}) + end + + local function loop (n) + if n > 0 then loop(n - 1) end + end + + -- should not try to call finalizer without a CallInfo available + initobj() + loop(20) -- ensure stack space + T.resetCI() -- remove extra CallInfos + T.alloccount(0) -- cannot allocate more CallInfos + obj = nil + collectgarbage() -- will not call finalizer + T.alloccount() + assert(X == false) + collectgarbage() -- now will call finalizer (it was still pending) + assert(X == true) + + -- should not try to call finalizer without stack space available + initobj() + loop(5) -- ensure enough CallInfos + T.reallocstack(0) -- remove extra stack slots + T.alloccount(0) -- cannot reallocate stack + obj = nil + collectgarbage() -- will not call finalizer + T.alloccount() + assert(X == false) + collectgarbage() -- now will call finalizer (it was still pending) + assert(X == true) +end + + print('OK') diff --git a/testes/gengc.lua b/testes/gengc.lua index c4f6ca1b71..6509e39d8a 100644 --- a/testes/gengc.lua +++ b/testes/gengc.lua @@ -1,5 +1,5 @@ -- $Id: testes/gengc.lua $ --- See Copyright Notice in file all.lua +-- See Copyright Notice in file lua.h print('testing generational garbage collection') @@ -176,6 +176,20 @@ do print"testing stop-the-world collection" assert(collectgarbage("param", "stepsize") == step) end + +if T then -- test GC parameter codification + for _, percentage in ipairs{5, 10, 12, 20, 50, 100, 200, 500} do + local param = T.codeparam(percentage) -- codify percentage + for _, value in ipairs{1, 2, 10, 100, 257, 1023, 6500, 100000} do + local exact = value*percentage // 100 + local aprox = T.applyparam(param, value) -- apply percentage + -- difference is at most 10% (+1 compensates difference due to + -- rounding to integers) + assert(math.abs(aprox - exact) <= exact/10 + 1) + end + end +end + collectgarbage(oldmode) print('OK') diff --git a/testes/goto.lua b/testes/goto.lua index 103cccef52..906208b553 100644 --- a/testes/goto.lua +++ b/testes/goto.lua @@ -1,5 +1,11 @@ -- $Id: testes/goto.lua $ --- See Copyright Notice in file all.lua +-- See Copyright Notice in file lua.h + +global require +global print, load, assert, string, setmetatable +global collectgarbage, error + +print("testing goto and global declarations") collectgarbage() @@ -17,15 +23,18 @@ errmsg([[ ::l1:: ::l1:: ]], "label 'l1'") errmsg([[ ::l1:: do ::l1:: end]], "label 'l1'") --- undefined label -errmsg([[ goto l1; local aa ::l1:: ::l2:: print(3) ]], "local 'aa'") --- jumping over variable definition +-- jumping over variable declaration +errmsg([[ goto l1; local aa ::l1:: ::l2:: print(3) ]], "scope of 'aa'") + +errmsg([[ goto l2; global *; ::l1:: ::l2:: print(3) ]], "scope of '*'") + errmsg([[ do local bb, cc; goto l1; end local aa ::l1:: print(3) -]], "local 'aa'") +]], "scope of 'aa'") + -- jumping into a block errmsg([[ do ::l1:: end goto l1 ]], "label 'l1'") @@ -38,7 +47,7 @@ errmsg([[ local xuxu = 10 ::cont:: until xuxu < x -]], "local 'xuxu'") +]], "scope of 'xuxu'") -- simple gotos local x @@ -252,6 +261,8 @@ assert(testG(5) == 10) do -- test goto's around to-be-closed variable + global * + -- set 'var' and return an object that will reset 'var' when -- it goes out of scope local function newobj (var) @@ -263,16 +274,16 @@ do -- test goto's around to-be-closed variable goto L1 - ::L4:: assert(not X); goto L5 -- varX dead here + ::L4:: assert(not varX); goto L5 -- varX dead here ::L1:: local varX = newobj("X") - assert(X); goto L2 -- varX alive here + assert(varX); goto L2 -- varX alive here ::L3:: - assert(X); goto L4 -- varX alive here + assert(varX); goto L4 -- varX alive here - ::L2:: assert(X); goto L3 -- varX alive here + ::L2:: assert(varX); goto L3 -- varX alive here ::L5:: -- return end @@ -280,7 +291,187 @@ end foo() --------------------------------------------------------------------------------- +-------------------------------------------------------------------------- + +-- check for compilation errors +local function checkerr (code, err) + local st, msg = load(code) + assert(not st and string.find(msg, err)) +end + +do + global T + + -- globals must be declared, after a global declaration + checkerr("global none; X = 1", "variable 'X'") + checkerr("global none; function XX() end", "variable 'XX'") + + -- global variables cannot be to-be-closed + checkerr("global X", "cannot be") + checkerr("global *", "cannot be") + + do + local X = 10 + do global X; X = 20 end + assert(X == 10) -- local X + end + assert(_ENV.X == 20) -- global X + + -- '_ENV' cannot be global + checkerr("global _ENV, a; a = 10", "variable 'a'") + + -- global declarations inside functions + checkerr([[ + global none + local function foo () XXX = 1 end --< ERROR]], "variable 'XXX'") + + if not T then -- when not in "test mode", "global" isn't reserved + assert(load("global = 1; return global")() == 1) + print " ('global' is not a reserved word)" + else + -- "global" reserved, cannot be used as a variable + assert(not load("global = 1; return global")) + end + + local foo = 20 + do + global function foo (x) + if x == 0 then return 1 else return 2 * foo(x - 1) end + end + assert(foo == _ENV.foo and foo(4) == 16) + end + assert(_ENV.foo(4) == 16) + assert(foo == 20) -- local one is in context here + + do + global foo; + function foo (x) return end -- Ok after declaration + end + checkerr([[ + global foo; + function foo (x) return end -- ERROR: foo is read-only + ]], "assign to const variable 'foo'") + + checkerr([[ + global foo ; + function foo (x) -- ERROR: foo is read-only + return + end + ]], "%:2%:") -- correct line in error message + + checkerr([[ + global *; + print(X) -- Ok to use + Y = 1 -- ERROR + ]], "assign to const variable 'Y'") + + checkerr([[ + global *; + Y = X -- Ok to use + global *; + Y = 1 -- ERROR + ]], "assign to const variable 'Y'") + + global * + Y = 10 + assert(_ENV.Y == 10) + global * + local x = Y + global * + Y = x + Y + assert(_ENV.Y == 20) + Y = nil +end + + +do -- Ok to declare hundreds of globals + global table + local code = {} + for i = 1, 1000 do + code[#code + 1] = ";global x" .. i + end + code[#code + 1] = "; return x990" + code = table.concat(code) + _ENV.x990 = 11 + assert(load(code)() == 11) + _ENV.x990 = nil +end + +do -- mixing lots of global/local declarations + global table + local code = {} + for i = 1, 200 do + code[#code + 1] = ";global x" .. i + code[#code + 1] = ";local y" .. i .. "=" .. (2*i) + end + code[#code + 1] = "; return x200 + y200" + code = table.concat(code) + _ENV.x200 = 11 + assert(assert(load(code))() == 2*200 + 11) + _ENV.x200 = nil +end + +do print "testing initialization in global declarations" + global a, b, c = 10, 20, 30 + assert(_ENV.a == 10 and b == 20 and c == 30) + _ENV.a = nil; _ENV.b = nil; _ENV.c = nil; + + global a, b, c = 10 + assert(_ENV.a == 10 and b == nil and c == nil) + _ENV.a = nil; _ENV.b = nil; _ENV.c = nil; + + global table + global a, b, c, d = table.unpack{1, 2, 3, 6, 5} + assert(_ENV.a == 1 and b == 2 and c == 3 and d == 6) + a = nil; b = nil; c = nil; d = nil + + local a, b = 100, 200 + do + global a, b = a, b + end + assert(_ENV.a == 100 and _ENV.b == 200) + _ENV.a = nil; _ENV.b = nil + + + assert(_ENV.a == nil and _ENV.b == nil and _ENV.c == nil and _ENV.d == nil) +end + +do + global table, string + -- global initialization when names don't fit in K + + -- to fill constant table + local code = {} + for i = 1, 300 do code[i] = "'" .. i .. "'" end + code = table.concat(code, ",") + code = string.format([[ + return function (_ENV) + local dummy = {%s} -- fill initial positions in constant table, + -- so that initialization must use registers for global names + global a, b, c = 10, 20, 30 + end]], code) + + local fun = assert(load(code))() + + local env = {} + fun(env) + assert(env.a == 10 and env.b == 20 and env.c == 30) +end + + +do -- testing global redefinitions + -- cannot use 'checkerr' as errors are not compile time + global pcall + local f = assert(load("global print = 10")) + local st, msg = pcall(f) + assert(string.find(msg, "global 'print' already defined")) + + local f = assert(load("local _ENV = {AA = false}; global AA = 10")) + local st, msg = pcall(f) + assert(string.find(msg, "global 'AA' already defined")) + +end print'OK' + diff --git a/testes/heavy.lua b/testes/heavy.lua index 4731c7472f..e7219a91ae 100644 --- a/testes/heavy.lua +++ b/testes/heavy.lua @@ -1,5 +1,7 @@ --- $Id: heavy.lua,v 1.7 2017/12/29 15:42:15 roberto Exp $ --- See Copyright Notice in file all.lua +-- $Id: testes/heavy.lua,v $ +-- See Copyright Notice in file lua.h + +global * local function teststring () print("creating a string too long") @@ -47,9 +49,9 @@ local function loadrep (x, what) end -function controlstruct () +local function controlstruct () print("control structure too long") - local lim = ((1 << 24) - 2) // 3 + local lim = ((1 << 24) - 2) // 4 local s = string.rep("a = a + 1\n", lim) s = "while true do " .. s .. "end" assert(load(s)) @@ -63,7 +65,7 @@ function controlstruct () end -function manylines () +local function manylines () print("loading chunk with too many lines") local st, msg = loadrep("\n", "lines") assert(not st and string.find(msg, "too many lines")) @@ -71,7 +73,7 @@ function manylines () end -function hugeid () +local function hugeid () print("loading chunk with huge identifier") local st, msg = loadrep("a", "chars") assert(not st and @@ -80,7 +82,7 @@ function hugeid () print('+') end -function toomanyinst () +local function toomanyinst () print("loading chunk with too many instructions") local st, msg = loadrep("a = 10; ", "instructions") print('+') @@ -107,7 +109,7 @@ local function loadrepfunc (prefix, f) end -function toomanyconst () +local function toomanyconst () print("loading function with too many constants") loadrepfunc("function foo () return {0,", function (n) @@ -126,7 +128,7 @@ function toomanyconst () end -function toomanystr () +local function toomanystr () local a = {} local st, msg = pcall(function () for i = 1, math.huge do @@ -144,7 +146,7 @@ function toomanystr () end -function toomanyidx () +local function toomanyidx () local a = {} local st, msg = pcall(function () for i = 1, math.huge do diff --git a/testes/libs/lib11.c b/testes/libs/lib11.c index 377d0c484f..6a85f4d621 100644 --- a/testes/libs/lib11.c +++ b/testes/libs/lib11.c @@ -1,7 +1,7 @@ #include "lua.h" /* function from lib1.c */ -int lib1_export (lua_State *L); +LUAMOD_API int lib1_export (lua_State *L); LUAMOD_API int luaopen_lib11 (lua_State *L) { return lib1_export(L); diff --git a/testes/libs/lib22.c b/testes/libs/lib22.c index 8e6565022e..b377cce520 100644 --- a/testes/libs/lib22.c +++ b/testes/libs/lib22.c @@ -1,3 +1,7 @@ +/* implementation for lib2-v2 */ + +#include + #include "lua.h" #include "lauxlib.h" @@ -8,8 +12,54 @@ static int id (lua_State *L) { } +struct STR { + void *ud; + lua_Alloc allocf; +}; + + +static void *t_freestr (void *ud, void *ptr, size_t osize, size_t nsize) { + struct STR *blk = (struct STR*)ptr - 1; + blk->allocf(blk->ud, blk, sizeof(struct STR) + osize, 0); + return NULL; +} + + +static int newstr (lua_State *L) { + size_t len; + const char *str = luaL_checklstring(L, 1, &len); + void *ud; + lua_Alloc allocf = lua_getallocf(L, &ud); + struct STR *blk = (struct STR*)allocf(ud, NULL, 0, + len + 1 + sizeof(struct STR)); + if (blk == NULL) { /* allocation error? */ + lua_pushliteral(L, "not enough memory"); + lua_error(L); /* raise a memory error */ + } + blk->ud = ud; blk->allocf = allocf; + memcpy(blk + 1, str, len + 1); + lua_pushexternalstring(L, (char *)(blk + 1), len, t_freestr, L); + return 1; +} + + +/* +** Create an external string and keep it in the registry, so that it +** will test that the library code is still available (to deallocate +** this string) when closing the state. +*/ +static void initstr (lua_State *L) { + lua_pushcfunction(L, newstr); + lua_pushstring(L, + "012345678901234567890123456789012345678901234567890123456789"); + lua_call(L, 1, 1); /* call newstr("0123...") */ + luaL_ref(L, LUA_REGISTRYINDEX); /* keep string in the registry */ +} + + static const struct luaL_Reg funcs[] = { {"id", id}, + {"newstr", newstr}, {NULL, NULL} }; @@ -18,6 +68,7 @@ LUAMOD_API int luaopen_lib2 (lua_State *L) { lua_settop(L, 2); lua_setglobal(L, "y"); /* y gets 2nd parameter */ lua_setglobal(L, "x"); /* x gets 1st parameter */ + initstr(L); luaL_newlib(L, funcs); return 1; } diff --git a/testes/libs/makefile b/testes/libs/makefile index 4e7f965e99..cf4c688152 100644 --- a/testes/libs/makefile +++ b/testes/libs/makefile @@ -5,7 +5,7 @@ LUA_DIR = ../../ CC = gcc # compilation should generate Dynamic-Link Libraries -CFLAGS = -Wall -std=c99 -O2 -I$(LUA_DIR) -fPIC -shared +CFLAGS = -Wall -O2 -I$(LUA_DIR) -fPIC -shared # libraries used by the tests all: lib1.so lib11.so lib2.so lib21.so lib2-v2.so diff --git a/testes/literals.lua b/testes/literals.lua index 30ab9ab115..336ef585c5 100644 --- a/testes/literals.lua +++ b/testes/literals.lua @@ -1,8 +1,10 @@ -- $Id: testes/literals.lua $ --- See Copyright Notice in file all.lua +-- See Copyright Notice in file lua.h print('testing scanner') +global * + local debug = require "debug" diff --git a/testes/locals.lua b/testes/locals.lua index 090d846bef..6cd1054764 100644 --- a/testes/locals.lua +++ b/testes/locals.lua @@ -1,5 +1,7 @@ -- $Id: testes/locals.lua $ --- See Copyright Notice in file all.lua +-- See Copyright Notice in file lua.h + +global * print('testing local variables and environments') @@ -39,9 +41,11 @@ f = nil local f local x = 1 -a = nil -load('local a = {}')() -assert(a == nil) +do + global a; a = nil + load('local a = {}')() + assert(a == nil) +end function f (a) local _1, _2, _3, _4, _5 @@ -154,7 +158,7 @@ local _ENV = (function (...) return ... end)(_G, dummy) -- { do local _ENV = {assert=assert}; assert(true) end local mt = {_G = _G} local foo,x -A = false -- "declare" A +global A; A = false -- "declare" A do local _ENV = mt function foo (x) A = x @@ -177,20 +181,27 @@ assert(x==20) A = nil -do -- constants +do print("testing local constants") + global assert, load, string, X + X = 1 -- not a constant local a, b, c = 10, 20, 30 b = a + c + b -- 'b' is not constant assert(a == 10 and b == 60 and c == 30) + local function checkro (name, code) local st, msg = load(code) local gab = string.format("attempt to assign to const variable '%s'", name) assert(not st and string.find(msg, gab)) end + checkro("y", "local x, y , z = 10, 20, 30; x = 11; y = 12") checkro("x", "local x , y, z = 10, 20, 30; x = 11") checkro("z", "local x , y, z = 10, 20, 30; y = 10; z = 11") - checkro("foo", "local foo = 10; function foo() end") - checkro("foo", "local foo = {}; function foo() end") + checkro("foo", "local foo = 10; function foo() end") + checkro("foo", "local foo = {}; function foo() end") + checkro("foo", "global foo ; function foo() end") + checkro("XX", "global XX ; XX = 10") + checkro("XX", "local _ENV; global XX ; XX = 10") checkro("z", [[ local a, z , b = 10; @@ -201,11 +212,26 @@ do -- constants local a, var1 = 10; function foo() a = 20; z = function () var1 = 12; end end ]]) + + checkro("var1", [[ + global a, var1 , z; + local function foo() a = 20; z = function () var1 = 12; end end + ]]) end + print"testing to-be-closed variables" + +do + local st, msg = load("local a, b") + assert(not st and string.find(msg, "multiple")) + + local st, msg = load("local a, b") + assert(not st and string.find(msg, "multiple")) +end + local function stack(n) n = ((n == 0) or stack(n - 1)) end local function func2close (f, x, y) @@ -280,6 +306,31 @@ do end +do -- testing presence of second argument + local function foo (howtoclose, obj, n) + local ca -- copy of 'a' visible inside its close metamethod + do + local a = func2close(function (...t) + assert(select("#", ...) == n) + assert(t.n == n and t[1] == ca and (t.n < 2 or t[2] == obj)) + ca = 15 -- final value to be returned if howtoclose=="scope" + end) + ca = a + if howtoclose == "ret" then return obj -- 'a' closed by return + elseif howtoclose == "err" then error(obj) -- 'a' closed by error + end + end -- 'a' closed by end of scope + return ca -- ca now should be 15 + end + -- with no errors, closing methods receive no extra argument + assert(foo("scope", nil, 1) == 15) -- close by end of scope + assert(foo("ret", 32, 1) == 32) -- close by return + -- with errors, they do + local st, msg = pcall(foo, "err", 23, 2) -- close by error + assert(not st and msg == 23) +end + + -- testing to-be-closed x compile-time constants -- (there were some bugs here in Lua 5.4-rc3, due to a confusion -- between compile levels and stack levels of variables) @@ -859,14 +910,15 @@ do local extrares -- result from extra yield (if any) - local function check (body, extra, ...) - local t = table.pack(...) -- expected returns + local function check (body, extra, ...t) local co = coroutine.wrap(body) if extra then extrares = co() -- runs until first (extra) yield end - local res = table.pack(co()) -- runs until yield inside '__close' - assert(res.n == 2 and res[2] == nil) + local res = table.pack(co()) -- runs until "regular" yield + -- regular yield will yield all values passed to the close function; + -- without errors, that is only the object being closed. + assert(res.n == 1 and type(res[1]) == "table") local res2 = table.pack(co()) -- runs until end of function assert(res2.n == t.n) for i = 1, #t do @@ -879,10 +931,10 @@ do end local function foo () - local x = func2close(coroutine.yield) + local x = func2close(coroutine.yield) -- "regular" yield local extra = func2close(function (self) assert(self == extrares) - coroutine.yield(100) + coroutine.yield(100) -- first (extra) yield end) extrares = extra return table.unpack{10, x, 30} @@ -891,21 +943,21 @@ do assert(extrares == 100) local function foo () - local x = func2close(coroutine.yield) + local x = func2close(coroutine.yield) -- "regular" yield return end check(foo, false) local function foo () - local x = func2close(coroutine.yield) + local x = func2close(coroutine.yield) -- "regular" yield local y, z = 20, 30 return x end check(foo, false, "x") local function foo () - local x = func2close(coroutine.yield) - local extra = func2close(coroutine.yield) + local x = func2close(coroutine.yield) -- "regular" yield + local extra = func2close(coroutine.yield) -- extra yield return table.unpack({}, 1, 100) -- 100 nils end check(foo, true, table.unpack({}, 1, 100)) @@ -1134,7 +1186,7 @@ do local function open (x) numopen = numopen + 1 return - function () -- iteraction function + function () -- iteration function x = x - 1 if x > 0 then return x end end, diff --git a/testes/main.lua b/testes/main.lua index 1aa7b21771..98d3695189 100644 --- a/testes/main.lua +++ b/testes/main.lua @@ -1,6 +1,6 @@ # testing special comment on first line -- $Id: testes/main.lua $ --- See Copyright Notice in file all.lua +-- See Copyright Notice in file lua.h -- most (all?) tests here assume a reasonable "Unix-like" shell if _port then return end @@ -78,6 +78,9 @@ end RUN('lua -v') +RUN('lua -v > %s', out) +local release = string.match(getoutput(), "Lua (%d+%.%d+%.%d+)") + print(string.format("(temporary program file used in these tests: %s)", prog)) -- running stdin as a file @@ -90,7 +93,7 @@ prepfile[[ 1, a ) ]] -RUN('lua - < %s > %s', prog, out) +RUN('lua - -- < %s > %s', prog, out) checkout("1\tnil\n") RUN('echo "print(10)\nprint(2)\n" | lua > %s', out) @@ -133,7 +136,7 @@ checkout("-h\n") prepfile("print(package.path)") -- test LUA_PATH -RUN('env LUA_INIT= LUA_PATH=x lua %s > %s', prog, out) +RUN('env LUA_INIT= LUA_PATH=x lua -- %s > %s', prog, out) checkout("x\n") -- test LUA_PATH_version @@ -167,7 +170,9 @@ checkout("10\n11\n") -- test errors in LUA_INIT NoRun('LUA_INIT:1: msg', 'env LUA_INIT="error(\'msg\')" lua') --- test option '-E' + +print("testing option '-E'") + local defaultpath, defaultCpath do @@ -192,6 +197,22 @@ assert(not string.find(defaultpath, "xxx") and string.find(defaultCpath, "lua")) +-- (LUA_READLINELIB was introduced in 5.5.1) +if release >= "5.5.1" then + print"testing readline library name" + -- should generate a warning when trying to load inexistent library "xuxu" + local env = [[LUA_READLINELIB=xuxu LUA_INIT="warn('@allow')"]] + local code = 'echo " " | env %s lua %s -W -i >%s 2>&1' + RUN(code, env, "", out) -- run code with no extra options + assert(string.find(getoutput(), + "warning: unable to load readline library 'xuxu'")) + + RUN(code, env, "-E", out) -- run again with option -E + -- no warning when LUA_READLINELIB is to be ignored + assert(not string.find(getoutput(), "warning")) +end + + -- test replacement of ';;' to default path local function convert (p) prepfile("print(package.path)") @@ -226,7 +247,7 @@ RUN("lua -l 'str=string' '-lm=math' -e 'print(m.sin(0))' %s > %s", prog, out) checkout("0.0\nALO ALO\t20\n") --- test module names with version sufix ("libs/lib2-v2") +-- test module names with version suffix ("libs/lib2-v2") RUN("env LUA_CPATH='./libs/?.so' lua -l lib2-v2 -e 'print(lib2.id())' > %s", out) checkout("true\n") @@ -310,8 +331,11 @@ checkprogout("ZYX)\nXYZ)\n") -- bug since 5.2: finalizer called when closing a state could -- subvert finalization order prepfile[[ --- should be called last +-- ensure tables will be collected only at the end of the program +collectgarbage"stop" + print("creating 1") +-- this finalizer should be called last setmetatable({}, {__gc = function () print(1) end}) print("creating 2") @@ -344,7 +368,7 @@ checkout("a\n") RUN([[lua "-eprint(1)" -ea=3 -e "print(a)" > %s]], out) checkout("1\n3\n") --- test iteractive mode +-- test interactive mode prepfile[[ (6*2-6) -- === a = @@ -355,7 +379,7 @@ RUN([[lua -e"_PROMPT='' _PROMPT2=''" -i < %s > %s]], prog, out) checkprogout("6\n10\n10\n\n") prepfile("a = [[b\nc\nd\ne]]\na") -RUN([[lua -e"_PROMPT='' _PROMPT2=''" -i < %s > %s]], prog, out) +RUN([[lua -e"_PROMPT='' _PROMPT2=''" -i -- < %s > %s]], prog, out) checkprogout("b\nc\nd\ne\n\n") -- input interrupted in continuation line @@ -485,12 +509,13 @@ assert(not os.remove(out)) -- invalid options NoRun("unrecognized option '-h'", "lua -h") NoRun("unrecognized option '---'", "lua ---") -NoRun("unrecognized option '-Ex'", "lua -Ex") +NoRun("unrecognized option '-Ex'", "lua -Ex --") NoRun("unrecognized option '-vv'", "lua -vv") NoRun("unrecognized option '-iv'", "lua -iv") NoRun("'-e' needs argument", "lua -e") NoRun("syntax error", "lua -e a") NoRun("'-l' needs argument", "lua -l") +NoRun("-i", "lua -- -i") -- handles -i as a script name if T then -- test library? diff --git a/testes/math.lua b/testes/math.lua index 3937b9ce56..54d19c4075 100644 --- a/testes/math.lua +++ b/testes/math.lua @@ -1,10 +1,17 @@ -- $Id: testes/math.lua $ --- See Copyright Notice in file all.lua +-- See Copyright Notice in file lua.h print("testing numbers and math lib") -local minint = math.mininteger -local maxint = math.maxinteger +local math = require "math" +local string = require "string" + +global none + +global print, assert, pcall, type, pairs, load +global tonumber, tostring, select + +local minint, maxint = math.mininteger, math.maxinteger local intbits = math.floor(math.log(maxint, 2) + 0.5) + 1 assert((1 << intbits) == 0) @@ -184,7 +191,7 @@ do for i = -3, 3 do -- variables avoid constant folding for j = -3, 3 do -- domain errors (0^(-n)) are not portable - if not _port or i ~= 0 or j > 0 then + if not _ENV._port or i ~= 0 or j > 0 then assert(eq(i^j, 1 / i^(-j))) end end @@ -430,7 +437,7 @@ for i = 2,36 do assert(tonumber('\t10000000000\t', i) == i10) end -if not _soft then +if not _ENV._soft then -- tests with very long numerals assert(tonumber("0x"..string.rep("f", 13)..".0") == 2.0^(4*13) - 1) assert(tonumber("0x"..string.rep("f", 150)..".0") == 2.0^(4*150) - 1) @@ -632,7 +639,7 @@ assert(maxint % -2 == -1) -- non-portable tests because Windows C library cannot compute -- fmod(1, huge) correctly -if not _port then +if not _ENV._port then local function anan (x) assert(isNaN(x)) end -- assert Not a Number anan(0.0 % 0) anan(1.3 % 0) @@ -678,6 +685,18 @@ assert(eq(math.exp(0), 1)) assert(eq(math.sin(10), math.sin(10%(2*math.pi)))) +do print("testing ldexp/frexp") + global ipairs + for _, x in ipairs{0, 10, 32, -math.pi, 1e10, 1e-10, math.huge, -math.huge} do + local m, p = math.frexp(x) + assert(math.ldexp(m, p) == x) + local am = math.abs(m) + assert(m == x or (0.5 <= am and am < 1)) + end + +end + + assert(tonumber(' 1.3e-2 ') == 1.3e-2) assert(tonumber(' -1.00000000000001 ') == -1.00000000000001) @@ -779,6 +798,7 @@ assert(a == '10' and b == '20') do print("testing -0 and NaN") + global rawset, undef local mz = -0.0 local z = 0.0 assert(mz == z) @@ -1071,9 +1091,10 @@ do assert(x == tonumber(tostring(x))) end - -- different numbers shold print differently. + -- different numbers should print differently. -- check pairs of floats with minimum detectable difference local p = floatbits - 1 + global ipairs for i = 1, maxexp - 1 do for _, i in ipairs{-i, i} do local x = 2^i diff --git a/testes/memerr.lua b/testes/memerr.lua new file mode 100644 index 0000000000..a55514a9e8 --- /dev/null +++ b/testes/memerr.lua @@ -0,0 +1,309 @@ +-- $Id: testes/memerr.lua $ +-- See Copyright Notice in file lua.h + + +local function checkerr (msg, f, ...) + local stat, err = pcall(f, ...) + assert(not stat and string.find(err, msg)) +end + +if T==nil then + (Message or print) + ('\n >>> testC not active: skipping memory error tests <<<\n') + return +end + +print("testing memory-allocation errors") + +local debug = require "debug" + +local pack = table.pack + +-- standard error message for memory errors +local MEMERRMSG = "not enough memory" + + +-- memory error in panic function +T.totalmem(T.totalmem()+10000) -- set low memory limit (+10k) +assert(T.checkpanic("newuserdata 20000") == MEMERRMSG) +T.totalmem(0) -- restore high limit + + + +-- {================================================================== +-- Testing memory limits +-- =================================================================== + +checkerr("block too big", T.newuserdata, math.maxinteger) +collectgarbage() +local f = load"local a={}; for i=1,100000 do a[i]=i end" +T.alloccount(10) +checkerr(MEMERRMSG, f) +T.alloccount() -- remove limit + + +-- preallocate stack space +local function deep (n) if n > 0 then deep(n - 1) end end + + +-- test memory errors; increase limit for maximum memory by steps, +-- so that we get memory errors in all allocations of a given +-- task, until there is enough memory to complete the task without +-- errors. +local function testbytes (s, f) + collectgarbage() + local M = T.totalmem() + local oldM = M + local a,b = nil + while true do + collectgarbage(); collectgarbage() + deep(4) + T.totalmem(M) + a, b = T.testC("pcall 0 1 0; pushstatus; return 2", f) + T.totalmem(0) -- remove limit + if a and b == "OK" then break end -- stop when no more errors + if b ~= "OK" and b ~= MEMERRMSG then -- not a memory error? + error(a, 0) -- propagate it + end + M = M + 7 -- increase memory limit + end + print(string.format("minimum memory for %s: %d bytes", s, M - oldM)) + return a +end + +-- test memory errors; increase limit for number of allocations one +-- by one, so that we get memory errors in all allocations of a given +-- task, until there is enough allocations to complete the task without +-- errors. + +local function testalloc (s, f) + collectgarbage() + local M = 0 + local a,b = nil + while true do + collectgarbage(); collectgarbage() + deep(4) + T.alloccount(M) + a, b = T.testC("pcall 0 1 0; pushstatus; return 2", f) + T.alloccount() -- remove limit + if a and b == "OK" then break end -- stop when no more errors + if b ~= "OK" and b ~= MEMERRMSG then -- not a memory error? + error(a, 0) -- propagate it + end + M = M + 1 -- increase allocation limit + end + print(string.format("minimum allocations for %s: %d allocations", s, M)) + return M +end + + +local function testamem (s, f) + local aloc = testalloc(s, f) + local res = testbytes(s, f) + return {aloc = aloc, res = res} +end + + +local b = testamem("function call", function () return 10 end) +assert(b.res == 10 and b.aloc == 0) + +testamem("state creation", function () + local st = T.newstate() + if st then T.closestate(st) end -- close new state + return st +end) + +testamem("empty-table creation", function () + return {} +end) + +testamem("string creation", function () + return "XXX" .. "YYY" +end) + +testamem("coroutine creation", function() + return coroutine.create(print) +end) + +do -- vararg tables + local function pack (...t) return t end + local b = testamem("vararg table", function () + return pack(10, 20, 30, 40, "hello") + end) + assert(b.aloc == 3) -- new table uses three memory blocks + -- table optimized away + local function sel (n, ...arg) return arg[n] + arg.n end + local b = testamem("optimized vararg table", + function () return sel(2.0, 20, 30) end) + assert(b.res == 32 and b.aloc == 0) -- no memory needed for this case +end + +-- testing to-be-closed variables +testamem("to-be-closed variables", function() + local flag + do + local x = + setmetatable({}, {__close = function () flag = true end}) + flag = false + local x = {} + end + return flag +end) + + +-- testing threads + +-- get main thread from registry +local mt = T.testC("rawgeti R !M; return 1") +assert(type(mt) == "thread" and coroutine.running() == mt) + + + +local function expand (n,s) + if n==0 then return "" end + local e = string.rep("=", n) + return string.format("T.doonnewstack([%s[ %s;\n collectgarbage(); %s]%s])\n", + e, s, expand(n-1,s), e) +end + +G=0; collectgarbage() +load(expand(20,"G=G+1"))() +assert(G==20); collectgarbage() +G = nil + +testamem("running code on new thread", function () + return T.doonnewstack("local x=1") == 0 -- try to create thread +end) + + +do -- external strings + local str = string.rep("a", 100) + testamem("creating external strings", function () + return T.externstr(str) + end) +end + + +-- testing memory x compiler + +testamem("loadstring", function () + return load("x=1") -- try to do load a string +end) + + +local testprog = [[ +local function foo () return end +local t = {"x"} +AA = "aaa" +for i = 1, #t do AA = AA .. t[i] end +return true +]] + +-- testing memory x dofile +_G.AA = nil +local t =os.tmpname() +local f = assert(io.open(t, "w")) +f:write(testprog) +f:close() +testamem("dofile", function () + local a = loadfile(t) + return a and a() +end) +assert(os.remove(t)) +assert(_G.AA == "aaax") + + +-- other generic tests + +testamem("gsub", function () + local a, b = string.gsub("alo alo", "(a)", function (x) return x..'b' end) + return (a == 'ablo ablo') +end) + +testamem("dump/undump", function () + local a = load(testprog) + local b = a and string.dump(a) + a = b and load(b) + return a and a() +end) + +_G.AA = nil + +local t = os.tmpname() +testamem("file creation", function () + local f = assert(io.open(t, 'w')) + assert (not io.open"nomenaoexistente") + io.close(f); + return not loadfile'nomenaoexistente' +end) +assert(os.remove(t)) + +testamem("table creation", function () + local a, lim = {}, 10 + for i=1,lim do a[i] = i; a[i..'a'] = {} end + return (type(a[lim..'a']) == 'table' and a[lim] == lim) +end) + +testamem("constructors", function () + local a = {10, 20, 30, 40, 50; a=1, b=2, c=3, d=4, e=5} + return (type(a) == 'table' and a.e == 5) +end) + +local a = 1 +local close = nil +testamem("closure creation", function () + function close (b) + return function (x) return b + x end + end + return (close(2)(4) == 6) +end) + +testamem("using coroutines", function () + local a = coroutine.wrap(function () + coroutine.yield(string.rep("a", 10)) + return {} + end) + assert(string.len(a()) == 10) + return a() +end) + +do -- auxiliary buffer + local lim = 100 + local a = {}; for i = 1, lim do a[i] = "01234567890123456789" end + testamem("auxiliary buffer", function () + return (#table.concat(a, ",") == 20*lim + lim - 1) + end) +end + +testamem("growing stack", function () + local function foo (n) + if n == 0 then return 1 else return 1 + foo(n - 1) end + end + return foo(100) +end) + + +collectgarbage() +collectgarbage() +global io, T, setmetatable, collectgarbage, print + +local Count = 0 +testamem("finalizers", function () + local X = false + local obj = setmetatable({}, {__gc = function () X = true end}) + obj = nil + T.resetCI() -- remove extra CallInfos + T.reallocstack(18) -- remove extra stack slots + Count = Count + 1 + io.stderr:write(Count, "\n") + T.trick(io) + collectgarbage() + return X +end) + +-- }================================================================== + + +print "Ok" + + diff --git a/testes/nextvar.lua b/testes/nextvar.lua index d1da3ceeb3..098e7891c9 100644 --- a/testes/nextvar.lua +++ b/testes/nextvar.lua @@ -1,5 +1,7 @@ -- $Id: testes/nextvar.lua $ --- See Copyright Notice in file all.lua +-- See Copyright Notice in file lua.h + +global * print('testing tables, next, and for') @@ -227,7 +229,7 @@ for i = 1,lim do end --- insert and delete elements until a rehash occurr. Caller must ensure +-- insert and delete elements until a rehash occur. Caller must ensure -- that a rehash will change the shape of the table. Must repeat because -- the insertion may collide with the deleted element, and then there is -- no rehash. @@ -343,13 +345,22 @@ do end end -local nofind = {} -a,b,c = 1,2,3 -a,b,c = nil +do print("testing attack on table length") + local t = {} + local lim = math.floor(math.log(math.maxinteger, 2)) - 1 + for i = lim, 0, -1 do + t[2^i] = true + end + assert(t[1 << lim]) + -- next loop should not take forever + for i = 1, #t do end +end + +local nofind = {} --- next uses always the same iteraction function +-- next uses always the same iteration function assert(next{} == next{}) local function find (name) @@ -396,7 +407,7 @@ for i=0,10000 do end end -n = {n=0} +local n = {n=0} for i,v in pairs(a) do n.n = n.n+1 assert(i and v and a[i] == v) @@ -894,13 +905,18 @@ local function foo1 (e,i) if i <= e.n then return i,a[i] end end -setmetatable(a, {__pairs = function (x) return foo, x, 0 end}) +local closed = false +setmetatable(a, {__pairs = function (x) + local tbc = setmetatable({}, {__close = function () closed = true end}) + return foo, x, 0, tbc + end}) local i = 0 for k,v in pairs(a) do i = i + 1 assert(k == i and v == k+1) end +assert(closed) -- 'tbc' has been closed a.n = 5 a[3] = 30 diff --git a/testes/packtests b/testes/packtests index 0dbb92fe5d..855c054a0c 100755 --- a/testes/packtests +++ b/testes/packtests @@ -28,6 +28,7 @@ $NAME/literals.lua \ $NAME/locals.lua \ $NAME/main.lua \ $NAME/math.lua \ +$NAME/memerr.lua \ $NAME/nextvar.lua \ $NAME/pm.lua \ $NAME/sort.lua \ diff --git a/testes/pm.lua b/testes/pm.lua index f5889fcd07..feab33dbbe 100644 --- a/testes/pm.lua +++ b/testes/pm.lua @@ -1,11 +1,13 @@ -- $Id: testes/pm.lua $ --- See Copyright Notice in file all.lua +-- See Copyright Notice in file lua.h -- UTF-8 file print('testing pattern matching') +global * + local function checkerror (msg, f, ...) local s, err = pcall(f, ...) assert(not s and string.find(err, msg)) @@ -23,9 +25,9 @@ a,b = string.find('alo', '') assert(a == 1 and b == 0) a,b = string.find('a\0o a\0o a\0o', 'a', 1) -- first position assert(a == 1 and b == 1) -a,b = string.find('a\0o a\0o a\0o', 'a\0o', 2) -- starts in the midle +a,b = string.find('a\0o a\0o a\0o', 'a\0o', 2) -- starts in the middle assert(a == 5 and b == 7) -a,b = string.find('a\0o a\0o a\0o', 'a\0o', 9) -- starts in the midle +a,b = string.find('a\0o a\0o a\0o', 'a\0o', 9) -- starts in the middle assert(a == 9 and b == 11) a,b = string.find('a\0a\0a\0a\0\0ab', '\0ab', 2); -- finds at the end assert(a == 9 and b == 11); @@ -345,6 +347,16 @@ do -- init parameter in gmatch end +do -- bug since 5.3 + local N = 20000 + local iter = string.gmatch(string.rep("a", N), string.rep("a?", N)) + pcall(iter) -- error for pattern too complex + -- calling function again found recursion count ('matchdepth') equal + -- to -1, so it did not detect next C-stack overflow + pcall(iter) +end + + -- tests for `%f' (`frontiers') assert(string.gsub("aaa aa a aaa a", "%f[%w]a", "x") == "xaa xa x xaa x") diff --git a/testes/sort.lua b/testes/sort.lua index 965e153482..92aaca3cef 100644 --- a/testes/sort.lua +++ b/testes/sort.lua @@ -1,5 +1,5 @@ -- $Id: testes/sort.lua $ --- See Copyright Notice in file all.lua +-- See Copyright Notice in file lua.h print "testing (parts of) table library" @@ -72,6 +72,19 @@ assert(a==1 and x==nil) a,x = unpack({1,2}, 1, 1) assert(a==1 and x==nil) + +do -- unpack with non-tables + local debug = require"debug" + local oldmt = debug.getmetatable(0) + local str = "hello" + debug.setmetatable(0, + { __len = function () return #str end, + __index = function (_, i) return string.sub(str, i, i) end}) + assert(table.concat({table.unpack(0)}) == str) + debug.setmetatable(0, oldmt) -- restore original metatable for numbers +end + + do local maxi = (1 << 31) - 1 -- maximum value for an int (usually) local mini = -(1 << 31) -- minimum value for an int (usually) @@ -199,7 +212,7 @@ do __index = function (_,k) pos1 = k end, __newindex = function (_,k) pos2 = k; error() end, }) local st, msg = pcall(table.move, a, f, e, t) - assert(not st and not msg and pos1 == x and pos2 == y) + assert(not st and pos1 == x and pos2 == y) end checkmove(1, maxI, 0, 1, 0) checkmove(0, maxI - 1, 1, maxI - 1, maxI) diff --git a/testes/strings.lua b/testes/strings.lua index 9bb52b35dd..84ff115483 100644 --- a/testes/strings.lua +++ b/testes/strings.lua @@ -1,8 +1,9 @@ -- $Id: testes/strings.lua $ --- See Copyright Notice in file all.lua +-- See Copyright Notice in file lua.h -- ISO Latin encoding +global * print('testing strings and string library') @@ -539,6 +540,23 @@ else assert(y == x) local z = T.externstr(x) -- external allocated long string assert(z == y) + + local e = T.externstr("") -- empty external string + assert(e .. "x" == "x" and "x" .. e == "x") + assert(e .. e == "" and #e == 0) + + -- external string as the "n" key in vararg table + local n = T.externstr("n") + local n0 = T.externstr("n\0") + local function aux (...t) assert(t[n0] == nil); return t[n] end + assert(aux(10, 20, 30) == 3) + + -- external string as mode in weak table + local t = setmetatable({}, {__mode = T.externstr("kv")}) + t[{}] = {} + assert(next(t)) + collectgarbage() + assert(next(t) == nil) end print('OK') diff --git a/testes/tpack.lua b/testes/tpack.lua index 4b32efb59b..70386178c4 100644 --- a/testes/tpack.lua +++ b/testes/tpack.lua @@ -1,5 +1,5 @@ -- $Id: testes/tpack.lua $ --- See Copyright Notice in file all.lua +-- See Copyright Notice in file lua.h local pack = string.pack local packsize = string.packsize diff --git a/testes/tracegc.lua b/testes/tracegc.lua index 9c5c1b3f51..c1154f90f2 100644 --- a/testes/tracegc.lua +++ b/testes/tracegc.lua @@ -1,12 +1,17 @@ -- track collections + local M = {} -- import list -local setmetatable, stderr, collectgarbage = - setmetatable, io.stderr, collectgarbage +local stderr, collectgarbage = io.stderr, collectgarbage + +-- the debug version of setmetatable does not create any object (such as +-- a '__metatable' string), and so it is more appropriate to be used in +-- a finalizer +local setmetatable = require"debug".setmetatable -_ENV = nil +global none local active = false diff --git a/testes/utf8.lua b/testes/utf8.lua index dc0f2f09d5..8a0213d651 100644 --- a/testes/utf8.lua +++ b/testes/utf8.lua @@ -1,8 +1,10 @@ -- $Id: testes/utf8.lua $ --- See Copyright Notice in file all.lua +-- See Copyright Notice in file lua.h -- UTF-8 file +global * + print "testing UTF-8 library" local utf8 = require'utf8' @@ -134,7 +136,7 @@ do errorcodes("\xbfinvalid") errorcodes("αλφ\xBFα") - -- calling interation function with invalid arguments + -- calling iteration function with invalid arguments local f = utf8.codes("") assert(f("", 2) == nil) assert(f("", -1) == nil) @@ -150,11 +152,20 @@ checkerror("position out of bounds", utf8.offset, "", 1, -1) checkerror("continuation byte", utf8.offset, "𦧺", 1, 2) checkerror("continuation byte", utf8.offset, "𦧺", 1, 2) checkerror("continuation byte", utf8.offset, "\x80", 1) +checkerror("continuation byte", utf8.offset, "\x9c", -1) -- error in indices for len checkerror("out of bounds", utf8.len, "abc", 0, 2) checkerror("out of bounds", utf8.len, "abc", 1, 4) +do -- missing continuation bytes + -- get what is available + local p, e = utf8.offset("\xE0", 1) + assert(p == 1 and e == 1) + local p, e = utf8.offset("\xE0\x9e", -1) + assert(p == 1 and e == 2) +end + local s = "hello World" local t = {string.byte(s, 1, -1)} @@ -227,10 +238,18 @@ s = "\0 \x7F\z s = string.gsub(s, " ", "") check(s, {0,0x7F, 0x80,0x7FF, 0x800,0xFFFF, 0x10000,0x10FFFF}) + +-- again, without strictness +s = "\xF0\x90\x80\x80 \xF7\xBF\xBF\xBF\z + \xF8\x88\x80\x80\x80 \xFB\xBF\xBF\xBF\xBF\z + \xFC\x84\x80\x80\x80\x80 \xFD\xBF\xBF\xBF\xBF\xBF" +s = string.gsub(s, " ", "") +check(s, {0x10000,0x1FFFFF, 0x200000,0x3FFFFFF, 0x4000000,0x7FFFFFFF}, true) + do -- original UTF-8 values local s = "\u{4000000}\u{7FFFFFFF}" - assert(#s == 12) + assert(s == "\xFC\x84\x80\x80\x80\x80\xFD\xBF\xBF\xBF\xBF\xBF") check(s, {0x4000000, 0x7FFFFFFF}, true) s = "\u{200000}\u{3FFFFFF}" @@ -246,6 +265,10 @@ local x = "日本語a-4\0éó" check(x, {26085, 26412, 35486, 97, 45, 52, 0, 233, 243}) +-- more than 5 continuation bytes +assert(not utf8.len("\xff\x8f\x8f\x8f\x8f\x8f\x8f\x8f")) + + -- Supplementary Characters check("𣲷𠜎𠱓𡁻𠵼ab𠺢", {0x23CB7, 0x2070E, 0x20C53, 0x2107B, 0x20D7C, 0x61, 0x62, 0x20EA2,}) diff --git a/testes/vararg.lua b/testes/vararg.lua index 1b02510244..043fa7d47a 100644 --- a/testes/vararg.lua +++ b/testes/vararg.lua @@ -1,11 +1,14 @@ -- $Id: testes/vararg.lua $ --- See Copyright Notice in file all.lua +-- See Copyright Notice in file lua.h print('testing vararg') -local function f (a, ...) +local function f (a, ...t) local x = {n = select('#', ...), ...} - for i = 1, x.n do assert(a[i] == x[i]) end + assert(x.n == t.n) + for i = 1, x.n do + assert(a[i] == x[i] and x[i] == t[i]) + end return x.n end @@ -17,7 +20,7 @@ local function c12 (...) return res, 2 end -local function vararg (...) return {n = select('#', ...), ...} end +local function vararg (... t) return t end local call = function (f, args) return f(table.unpack(args, 1, args.n)) end @@ -98,8 +101,40 @@ a,b,c,d,e = f(4) assert(a==nil and b==nil and c==nil and d==nil and e==nil) +do -- vararg expressions using unpack + local function aux (a, v, ...t) + for k, val in pairs(v) do t[k] = val end + return ... + end + + local t = table.pack(aux(10, {11, [5] = 24}, 1, 2, 3, nil, 4)) + assert(t.n == 5 and t[1] == 11 and t[2] == 2 and t[3] == 3 + and t[4] == nil and t[5] == 24) + + local t = table.pack(aux(nil, {1, [20] = "a", [30] = "b", n = 30})) + assert(t.n == 30 and t[1] == 1 and t[20] == "a" and t[30] == "b") + -- table has only those four elements + assert(next(t, next(t, next(t, next(t, next(t, nil))))) == nil) + + local a, b, c, d = aux(nil, {}, 10, 20, 30) + assert(a == 10 and b == 20 and c == 30 and d == nil) + + local function aux (a, b, n, ...t) t.n = n; return b, ... end + local t = table.pack(aux(10, 1, 10000)) + assert(t.n == 10001 and t[1] == 1 and #t == 1) + + local function checkerr (emsg, f, ...) + local st, msg = pcall(f, ...) + assert(not st and string.find(msg, emsg)) + end + checkerr("no proper 'n'", aux, 1, 1, -1) + checkerr("no proper 'n'", aux, 1, 1, math.maxinteger) + checkerr("no proper 'n'", aux, 1, 1, math.mininteger) + checkerr("no proper 'n'", aux, 1, 1, 1.0) +end + -- varargs for main chunks -local f = load[[ return {...} ]] +local f = assert(load[[ return {...} ]]) local x = f(2,3) assert(x[1] == 2 and x[2] == 3 and x[3] == undef) @@ -147,5 +182,79 @@ do local a, b = g() assert(a == nil and b == 2) end + + +do -- vararg parameter used in nested functions + local function foo (...tab1) + return function (...tab2) + return {tab1, tab2} + end + end + local f = foo(10, 20, 30) + local t = f("a", "b") + assert(t[1].n == 3 and t[1][1] == 10) + assert(t[2].n == 2 and t[2][1] == "a") +end + +do -- vararg parameter is read-only + local st, msg = load("return function (... t) t = 10 end") + assert(string.find(msg, "const variable 't'")) + + local st, msg = load[[ + local function foo (...extra) + return function (...) extra = nil end + end + ]] + assert(string.find(msg, "const variable 'extra'")) +end + + +do -- _ENV as vararg parameter + local st, msg = load[[ + local function aux (... _ENV) + global a + a = 10 + end ]] + assert(string.find(msg, "const variable 'a'")) + + local function aux (..._ENV) + global a; a = 10 + return a + end + assert(aux() == 10) + + local function aux (... _ENV) + global a = 10 + return a + end + assert(aux() == 10) +end + + +do -- access to vararg parameter + local function notab (keys, t, ...v) + for _, k in pairs(keys) do + assert(t[k] == v[k]) + end + assert(t.n == v.n) + return ... + end + + local t = table.pack(10, 20, 30) + local keys = {-1, 0, 1, t.n, t.n + 1, 1.0, 1.1, "n", print, "k", "1"} + notab(keys, t, 10, 20, 30) -- ensure stack space + local m = collectgarbage"count" + notab(keys, t, 10, 20, 30) + -- 'notab' does not create any table/object + assert(m == collectgarbage"count") + + -- writing to the vararg table + local function foo (...t) + t[1] = t[1] + 10 + return t[1] + end + assert(foo(10, 30) == 20) +end + print('OK') diff --git a/testes/verybig.lua b/testes/verybig.lua index 250ea79501..8163802c1d 100644 --- a/testes/verybig.lua +++ b/testes/verybig.lua @@ -1,5 +1,5 @@ -- $Id: testes/verybig.lua $ --- See Copyright Notice in file all.lua +-- See Copyright Notice in file lua.h print "testing RK"