Compare commits

..

No commits in common. "master" and "v5.5-beta" have entirely different histories.

81 changed files with 1168 additions and 2451 deletions

40
lapi.c
View file

@ -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"); api_check(L, idx <= ci->top.p - (func + 1), "new top too large");
diff = ((func + 1) + idx) - L->top.p; diff = ((func + 1) + idx) - L->top.p;
for (; diff > 0; diff--) for (; diff > 0; diff--)
setnilvalue2s(L->top.p++); /* clear new slots */ setnilvalue(s2v(L->top.p++)); /* clear new slots */
} }
else { else {
api_check(L, -(idx+1) <= (L->top.p - (func + 1)), "invalid new top"); 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), api_check(L, (L->ci->callstatus & CIST_TBC) && (L->tbclist.p == level),
"no variable to close at given level"); "no variable to close at given level");
level = luaF_close(L, level, CLOSEKTOP, 0); level = luaF_close(L, level, CLOSEKTOP, 0);
setnilvalue2s(level); setnilvalue(s2v(level));
lua_unlock(L); 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); const TValue *o = index2value(L, idx);
if (ttisnumber(o)) { if (ttisnumber(o)) {
unsigned len = luaO_tostringbuff(o, buff); unsigned len = luaO_tostringbuff(o, buff);
@ -440,13 +440,7 @@ LUA_API lua_Unsigned lua_rawlen (lua_State *L, int idx) {
case LUA_VSHRSTR: return cast(lua_Unsigned, tsvalue(o)->shrlen); case LUA_VSHRSTR: return cast(lua_Unsigned, tsvalue(o)->shrlen);
case LUA_VLNGSTR: return cast(lua_Unsigned, tsvalue(o)->u.lnglen); case LUA_VLNGSTR: return cast(lua_Unsigned, tsvalue(o)->u.lnglen);
case LUA_VUSERDATA: return cast(lua_Unsigned, uvalue(o)->len); case LUA_VUSERDATA: return cast(lua_Unsigned, uvalue(o)->len);
case LUA_VTABLE: { case LUA_VTABLE: return luaH_getn(hvalue(o));
lua_Unsigned res;
lua_lock(L);
res = luaH_getn(L, hvalue(o));
lua_unlock(L);
return res;
}
default: return 0; default: return 0;
} }
} }
@ -484,7 +478,7 @@ LUA_API lua_State *lua_tothread (lua_State *L, int idx) {
/* /*
** Returns a pointer to the internal representation of an object. ** Returns a pointer to the internal representation of an object.
** Note that ISO C does not allow the conversion of a pointer to ** Note that ANSI C does not allow the conversion of a pointer to
** function to a 'void*', so the conversion here goes through ** function to a 'void*', so the conversion here goes through
** a 'size_t'. (As the returned pointer is only informative, this ** a 'size_t'. (As the returned pointer is only informative, this
** conversion should not be a problem.) ** conversion should not be a problem.)
@ -513,7 +507,7 @@ LUA_API const void *lua_topointer (lua_State *L, int idx) {
LUA_API void lua_pushnil (lua_State *L) { LUA_API void lua_pushnil (lua_State *L) {
lua_lock(L); lua_lock(L);
setnilvalue2s(L->top.p); setnilvalue(s2v(L->top.p));
api_incr_top(L); api_incr_top(L);
lua_unlock(L); lua_unlock(L);
} }
@ -570,7 +564,7 @@ LUA_API const char *lua_pushexternalstring (lua_State *L,
LUA_API const char *lua_pushstring (lua_State *L, const char *s) { LUA_API const char *lua_pushstring (lua_State *L, const char *s) {
lua_lock(L); lua_lock(L);
if (s == NULL) if (s == NULL)
setnilvalue2s(L->top.p); setnilvalue(s2v(L->top.p));
else { else {
TString *ts; TString *ts;
ts = luaS_new(L, s); ts = luaS_new(L, s);
@ -685,7 +679,7 @@ static int auxgetstr (lua_State *L, const TValue *t, const char *k) {
/* /*
** The following function assumes that the registry cannot be a weak ** The following function assumes that the registry cannot be a weak
** table; so, an emergency collection while using the global table ** table, so that en mergency collection while using the global table
** cannot collect it. ** cannot collect it.
*/ */
static void getGlobalTable (lua_State *L, TValue *gt) { static void getGlobalTable (lua_State *L, TValue *gt) {
@ -743,7 +737,7 @@ LUA_API int lua_geti (lua_State *L, int idx, lua_Integer n) {
static int finishrawget (lua_State *L, lu_byte tag) { static int finishrawget (lua_State *L, lu_byte tag) {
if (tagisempty(tag)) /* avoid copying empty items to the stack */ if (tagisempty(tag)) /* avoid copying empty items to the stack */
setnilvalue2s(L->top.p); setnilvalue(s2v(L->top.p));
api_incr_top(L); api_incr_top(L);
lua_unlock(L); lua_unlock(L);
return novariant(tag); return novariant(tag);
@ -836,7 +830,7 @@ LUA_API int lua_getiuservalue (lua_State *L, int idx, int n) {
o = index2value(L, idx); o = index2value(L, idx);
api_check(L, ttisfulluserdata(o), "full userdata expected"); api_check(L, ttisfulluserdata(o), "full userdata expected");
if (n <= 0 || n > uvalue(o)->nuvalue) { if (n <= 0 || n > uvalue(o)->nuvalue) {
setnilvalue2s(L->top.p); setnilvalue(s2v(L->top.p));
t = LUA_TNONE; t = LUA_TNONE;
} }
else { else {
@ -1122,7 +1116,6 @@ LUA_API int lua_load (lua_State *L, lua_Reader reader, void *data,
ZIO z; ZIO z;
TStatus status; TStatus status;
lua_lock(L); lua_lock(L);
luaC_checkGC(L);
if (!chunkname) chunkname = "?"; if (!chunkname) chunkname = "?";
luaZ_init(L, &z, reader, data); luaZ_init(L, &z, reader, data);
status = luaD_protectedparser(L, &z, chunkname, mode); status = luaD_protectedparser(L, &z, chunkname, mode);
@ -1202,16 +1195,11 @@ LUA_API int lua_gc (lua_State *L, int what, ...) {
case LUA_GCSTEP: { case LUA_GCSTEP: {
lu_byte oldstp = g->gcstp; lu_byte oldstp = g->gcstp;
l_mem n = cast(l_mem, va_arg(argp, size_t)); l_mem n = cast(l_mem, va_arg(argp, size_t));
l_mem newdebt;
int work = 0; /* true if GC did some work */ int work = 0; /* true if GC did some work */
g->gcstp = 0; /* allow GC to run (other bits must be zero here) */ g->gcstp = 0; /* allow GC to run (other bits must be zero here) */
if (n <= 0) if (n <= 0)
newdebt = 0; /* force to run one basic step */ n = g->GCdebt; /* force to run one basic step */
else if (g->GCdebt >= n - MAX_LMEM) /* no overflow? */ luaE_setdebt(g, g->GCdebt - n);
newdebt = g->GCdebt - n;
else /* overflow */
newdebt = -MAX_LMEM; /* set debt to mininum value */
luaE_setdebt(g, newdebt);
luaC_condGC(L, (void)0, work = 1); luaC_condGC(L, (void)0, work = 1);
if (work && g->gcstate == GCSpause) /* end of cycle? */ if (work && g->gcstate == GCSpause) /* end of cycle? */
res = 1; /* signal it */ res = 1; /* signal it */
@ -1299,8 +1287,8 @@ LUA_API void lua_toclose (lua_State *L, int idx) {
LUA_API void lua_concat (lua_State *L, int n) { LUA_API void lua_concat (lua_State *L, int n) {
lua_lock(L); lua_lock(L);
api_checknelems(L, n);
if (n > 0) { if (n > 0) {
api_checkpop(L, n);
luaV_concat(L, n); luaV_concat(L, n);
luaC_checkGC(L); luaC_checkGC(L);
} }
@ -1418,7 +1406,7 @@ LUA_API const char *lua_setupvalue (lua_State *L, int funcindex, int n) {
TValue *fi; TValue *fi;
lua_lock(L); lua_lock(L);
fi = index2value(L, funcindex); fi = index2value(L, funcindex);
api_checkpop(L, 1); api_checknelems(L, 1);
name = aux_upvalue(fi, n, &val, &owner); name = aux_upvalue(fi, n, &val, &owner);
if (name) { if (name) {
L->top.p--; L->top.p--;

View file

@ -513,25 +513,12 @@ static const luaL_Reg boxmt[] = { /* box metamethods */
}; };
/*
** Get/create metatable (MT) for boxes
*/
static void getBoxMT (lua_State *L) {
const char *BOXMT = "_UBOX*"; /* key for the metatable */
if (luaL_getmetatable(L, BOXMT) == LUA_TNIL) { /* MT not created yet? */
luaL_newlibtable(L, boxmt); /* create it */
luaL_setfuncs(L, boxmt, 0); /* initialize it */
lua_copy(L, -1, -2); /* change stack from nil,MT to MT,MT */
lua_setfield(L, LUA_REGISTRYINDEX, BOXMT); /* store MT in the registry */
}
}
static void newbox (lua_State *L) { static void newbox (lua_State *L) {
UBox *box = (UBox *)lua_newuserdatauv(L, sizeof(UBox), 0); UBox *box = (UBox *)lua_newuserdatauv(L, sizeof(UBox), 0);
box->box = NULL; box->box = NULL;
box->bsize = 0; box->bsize = 0;
getBoxMT(L); if (luaL_newmetatable(L, "_UBOX*")) /* creating metatable? */
luaL_setfuncs(L, boxmt, 0); /* set its metamethods */
lua_setmetatable(L, -2); lua_setmetatable(L, -2);
} }
@ -755,7 +742,7 @@ typedef struct LoadF {
static const char *getF (lua_State *L, void *ud, size_t *size) { static const char *getF (lua_State *L, void *ud, size_t *size) {
LoadF *lf = (LoadF *)ud; LoadF *lf = (LoadF *)ud;
UNUSED(L); (void)L; /* not used */
if (lf->n > 0) { /* are there pre-read characters to be read? */ if (lf->n > 0) { /* are there pre-read characters to be read? */
*size = lf->n; /* return them (chars already in buffer) */ *size = lf->n; /* return them (chars already in buffer) */
lf->n = 0; /* no more pre-read characters */ lf->n = 0; /* no more pre-read characters */
@ -869,7 +856,7 @@ typedef struct LoadS {
static const char *getS (lua_State *L, void *ud, size_t *size) { static const char *getS (lua_State *L, void *ud, size_t *size) {
LoadS *ls = (LoadS *)ud; LoadS *ls = (LoadS *)ud;
UNUSED(L); (void)L; /* not used */
if (ls->size == 0) return NULL; if (ls->size == 0) return NULL;
*size = ls->size; *size = ls->size;
ls->size = 0; ls->size = 0;
@ -887,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) { LUALIB_API int luaL_loadstring (lua_State *L, const char *s) {
return luaL_loadbufferx(L, s, strlen(s), s, "t"); return luaL_loadbuffer(L, s, strlen(s), s);
} }
/* }====================================================== */ /* }====================================================== */
@ -1059,8 +1046,8 @@ LUALIB_API const char *luaL_gsub (lua_State *L, const char *s,
} }
void *luaL_alloc (void *ud, void *ptr, size_t osize, size_t nsize) { static void *l_alloc (void *ud, void *ptr, size_t osize, size_t nsize) {
UNUSED(ud); UNUSED(osize); (void)ud; (void)osize; /* not used */
if (nsize == 0) { if (nsize == 0) {
free(ptr); free(ptr);
return NULL; return NULL;
@ -1185,20 +1172,16 @@ static unsigned int luai_makeseed (void) {
LUALIB_API unsigned int luaL_makeseed (lua_State *L) { LUALIB_API unsigned int luaL_makeseed (lua_State *L) {
UNUSED(L); (void)L; /* unused */
return luai_makeseed(); return luai_makeseed();
} }
/* LUALIB_API lua_State *luaL_newstate (void) {
** Use the name with parentheses so that headers can redefine it lua_State *L = lua_newstate(l_alloc, NULL, luai_makeseed());
** 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)) { if (l_likely(L)) {
lua_atpanic(L, &panic); lua_atpanic(L, &panic);
lua_setwarnf(L, warnfon, L); lua_setwarnf(L, warnfoff, L); /* default is warnings off */
} }
return L; return L;
} }

View file

@ -81,9 +81,6 @@ 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_fileresult) (lua_State *L, int stat, const char *fname);
LUALIB_API int (luaL_execresult) (lua_State *L, int stat); 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 */ /* predefined references */
#define LUA_NOREF (-2) #define LUA_NOREF (-2)
@ -103,7 +100,7 @@ LUALIB_API int (luaL_loadstring) (lua_State *L, const char *s);
LUALIB_API lua_State *(luaL_newstate) (void); 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); LUALIB_API lua_Integer (luaL_len) (lua_State *L, int idx);

View file

@ -279,22 +279,21 @@ static int luaB_next (lua_State *L) {
static int pairscont (lua_State *L, int status, lua_KContext k) { static int pairscont (lua_State *L, int status, lua_KContext k) {
(void)L; (void)status; (void)k; /* unused */ (void)L; (void)status; (void)k; /* unused */
return 4; /* __pairs did all the work, just return its results */ return 3;
} }
static int luaB_pairs (lua_State *L) { static int luaB_pairs (lua_State *L) {
luaL_checkany(L, 1); luaL_checkany(L, 1);
if (luaL_getmetafield(L, 1, "__pairs") == LUA_TNIL) { /* no metamethod? */ if (luaL_getmetafield(L, 1, "__pairs") == LUA_TNIL) { /* no metamethod? */
lua_pushcfunction(L, luaB_next); /* will return generator and */ lua_pushcfunction(L, luaB_next); /* will return generator, */
lua_pushvalue(L, 1); /* state */ lua_pushvalue(L, 1); /* state, */
lua_pushnil(L); /* initial value */ lua_pushnil(L); /* and initial value */
lua_pushnil(L); /* to-be-closed object */
} }
else { else {
lua_pushvalue(L, 1); /* argument 'self' to metamethod */ lua_pushvalue(L, 1); /* argument 'self' to metamethod */
lua_callk(L, 1, 4, 0, pairscont); /* get 4 values from metamethod */ lua_callk(L, 1, 3, 0, pairscont); /* get 3 values from metamethod */
} }
return 4; return 3;
} }
@ -340,11 +339,9 @@ static int load_aux (lua_State *L, int status, int envidx) {
static const char *getMode (lua_State *L, int idx) { static const char *getMode (lua_State *L, int idx) {
const char *mode = luaL_optstring(L, idx, NULL); const char *mode = luaL_optstring(L, idx, "bt");
if (mode != NULL && strchr(mode, 'B') != NULL) { if (strchr(mode, 'B') != NULL) /* Lua code cannot use fixed buffers */
/* Lua code cannot use fixed buffers */
luaL_argerror(L, idx, "invalid mode"); luaL_argerror(L, idx, "invalid mode");
}
return mode; return mode;
} }
@ -366,24 +363,33 @@ static int luaB_loadfile (lua_State *L) {
/* /*
** Reader for generic 'load' function. ** 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.
*/ */
static const char *generic_reader (lua_State *L, void *ud, size_t *size) { static const char *generic_reader (lua_State *L, void *ud, size_t *size) {
int *firstcall = cast(int *, ud); (void)(ud); /* not used */
luaL_checkstack(L, 2, "too many nested functions"); 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_pushvalue(L, 1); /* get function */
lua_call(L, 0, 1); /* call it */ lua_call(L, 0, 1); /* call it */
if (lua_isnil(L, -1)) { if (lua_isnil(L, -1)) {
lua_pop(L, 1); /* pop result */
*size = 0; *size = 0;
return NULL; return NULL;
} }
else if (l_unlikely(!lua_isstring(L, -1))) else if (l_unlikely(!lua_isstring(L, -1)))
luaL_error(L, "reader function must return a string"); luaL_error(L, "reader function must return a string");
return lua_tolstring(L, -1, size); lua_replace(L, RESERVEDSLOT); /* save string in reserved slot */
return lua_tolstring(L, RESERVEDSLOT, size);
} }
@ -398,10 +404,10 @@ static int luaB_load (lua_State *L) {
status = luaL_loadbufferx(L, s, l, chunkname, mode); status = luaL_loadbufferx(L, s, l, chunkname, mode);
} }
else { /* loading from a reader function */ else { /* loading from a reader function */
int firstcall = 1; /* userdata for generic_reader */
const char *chunkname = luaL_optstring(L, 2, "=(load)"); const char *chunkname = luaL_optstring(L, 2, "=(load)");
luaL_checktype(L, 1, LUA_TFUNCTION); luaL_checktype(L, 1, LUA_TFUNCTION);
status = lua_load(L, generic_reader, &firstcall, chunkname, mode); lua_settop(L, RESERVEDSLOT); /* create reserved slot */
status = lua_load(L, generic_reader, NULL, chunkname, mode);
} }
return load_aux(L, status, env); return load_aux(L, status, env);
} }
@ -418,7 +424,7 @@ static int dofilecont (lua_State *L, int d1, lua_KContext d2) {
static int luaB_dofile (lua_State *L) { static int luaB_dofile (lua_State *L) {
const char *fname = luaL_optstring(L, 1, NULL); const char *fname = luaL_optstring(L, 1, NULL);
lua_settop(L, 1); lua_settop(L, 1);
if (l_unlikely(luaL_loadfilex(L, fname, "bt") != LUA_OK)) if (l_unlikely(luaL_loadfile(L, fname) != LUA_OK))
return lua_error(L); return lua_error(L);
lua_callk(L, 0, LUA_MULTRET, 0, dofilecont); lua_callk(L, 0, LUA_MULTRET, 0, dofilecont);
return dofilecont(L, 0, 0); return dofilecont(L, 0, 0);

155
lcode.c
View file

@ -45,7 +45,6 @@ l_noret luaK_semerror (LexState *ls, const char *fmt, ...) {
va_list argp; va_list argp;
pushvfstring(ls->L, argp, fmt, msg); pushvfstring(ls->L, argp, fmt, msg);
ls->t.token = 0; /* remove "near <token>" from final message */ ls->t.token = 0; /* remove "near <token>" from final message */
ls->linenumber = ls->lastline; /* back to line of last used token */
luaX_syntaxerror(ls, msg); luaX_syntaxerror(ls, msg);
} }
@ -566,20 +565,20 @@ static int k2proto (FuncState *fs, TValue *key, TValue *v) {
TValue val; TValue val;
Proto *f = fs->f; Proto *f = fs->f;
int tag = luaH_get(fs->kcache, key, &val); /* query scanner table */ int tag = luaH_get(fs->kcache, key, &val); /* query scanner table */
int k;
if (!tagisempty(tag)) { /* is there an index there? */ if (!tagisempty(tag)) { /* is there an index there? */
int k = cast_int(ivalue(&val)); k = cast_int(ivalue(&val));
/* collisions can happen only for float keys */ /* collisions can happen only for float keys */
lua_assert(ttisfloat(key) || luaV_rawequalobj(&f->k[k], v)); lua_assert(ttisfloat(key) || luaV_rawequalobj(&f->k[k], v));
return k; /* reuse index */ return k; /* reuse index */
} }
else { /* constant not found; create a new entry */ /* constant not found; create a new entry */
int k = addk(fs, f, v); k = addk(fs, f, v);
/* cache it for reuse; numerical value does not need GC barrier; /* cache it for reuse; numerical value does not need GC barrier;
table is not a metatable, so it does not need to invalidate cache */ table is not a metatable, so it does not need to invalidate cache */
setivalue(&val, k); setivalue(&val, k);
luaH_set(fs->ls->L, fs->kcache, key, &val); luaH_set(fs->ls->L, fs->kcache, key, &val);
return k; return k;
}
} }
@ -605,14 +604,13 @@ static int luaK_intK (FuncState *fs, lua_Integer n) {
/* /*
** Add a float to list of constants and return its index. Floats ** Add a float to list of constants and return its index. Floats
** with integral values need a different key, to avoid collision ** with integral values need a different key, to avoid collision
** with actual integers. To that end, we add to the number its smaller ** with actual integers. To that, we add to the number its smaller
** power-of-two fraction that is still significant in its scale. ** power-of-two fraction that is still significant in its scale.
** (For doubles, the fraction would be 2^-52). ** For doubles, that would be 1/2^52.
** This method is not bulletproof: different numbers may generate the ** This method is not bulletproof: different numbers may generate the
** same key (e.g., very large numbers will overflow to 'inf') and for ** same key (e.g., very large numbers will overflow to 'inf') and for
** floats larger than 2^53 the result is still an integer. For those ** floats larger than 2^53 the result is still an integer. At worst,
** cases, just generate a new entry. At worst, this only wastes an entry ** this only wastes an entry with a duplicate.
** with a duplicate.
*/ */
static int luaK_numberK (FuncState *fs, lua_Number r) { static int luaK_numberK (FuncState *fs, lua_Number r) {
TValue o, kv; TValue o, kv;
@ -627,7 +625,7 @@ static int luaK_numberK (FuncState *fs, lua_Number r) {
const lua_Number k = r * (1 + q); /* key */ const lua_Number k = r * (1 + q); /* key */
lua_Integer ik; lua_Integer ik;
setfltvalue(&kv, k); /* key as a TValue */ setfltvalue(&kv, k); /* key as a TValue */
if (!luaV_flttointeger(k, &ik, F2Ieq)) { /* not an integer value? */ if (!luaV_flttointeger(k, &ik, F2Ieq)) { /* not an integral value? */
int n = k2proto(fs, &kv, &o); /* use key */ int n = k2proto(fs, &kv, &o); /* use key */
if (luaV_rawequalobj(&fs->f->k[n], &o)) /* correct value? */ if (luaV_rawequalobj(&fs->f->k[n], &o)) /* correct value? */
return n; return n;
@ -663,11 +661,11 @@ static int boolT (FuncState *fs) {
** Add nil to list of constants and return its index. ** Add nil to list of constants and return its index.
*/ */
static int nilK (FuncState *fs) { static int nilK (FuncState *fs) {
lua_State *L = fs->ls->L; TValue k, v;
TValue k; setnilvalue(&v);
/* cannot use nil as key; instead use table itself */ /* cannot use nil as key; instead use table itself */
sethvalue(L, &k, fs->kcache); sethvalue(fs->ls->L, &k, fs->kcache);
return k2proto(fs, &k, &G(L)->nilvalue); return k2proto(fs, &k, &v);
} }
@ -706,22 +704,6 @@ 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' ** Convert a constant in 'v' into an expression description 'e'
*/ */
@ -802,15 +784,6 @@ 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 <const>). ** Ensure that expression 'e' is not a variable (nor a <const>).
@ -822,12 +795,9 @@ void luaK_dischargevars (FuncState *fs, expdesc *e) {
const2exp(const2val(fs, e), e); const2exp(const2val(fs, e), e);
break; break;
} }
case VVARGVAR: {
luaK_vapar2local(fs, e); /* turn it into a local variable */
} /* FALLTHROUGH */
case VLOCAL: { /* already in a register */ case VLOCAL: { /* already in a register */
int temp = e->u.var.ridx; int temp = e->u.var.ridx;
e->u.info = temp; /* (avoid a direct assignment; values overlap) */ e->u.info = temp; /* (can't do a direct assignment; values overlap) */
e->k = VNONRELOC; /* becomes a non-relocatable value */ e->k = VNONRELOC; /* becomes a non-relocatable value */
break; break;
} }
@ -859,12 +829,6 @@ void luaK_dischargevars (FuncState *fs, expdesc *e) {
e->k = VRELOC; e->k = VRELOC;
break; 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: { case VVARARG: case VCALL: {
luaK_setoneret(fs, e); luaK_setoneret(fs, e);
break; break;
@ -1027,11 +991,11 @@ int luaK_exp2anyreg (FuncState *fs, expdesc *e) {
/* /*
** Ensures final expression result is either in a register, ** Ensures final expression result is either in a register
** in an upvalue, or it is the vararg parameter. ** or in an upvalue.
*/ */
void luaK_exp2anyregup (FuncState *fs, expdesc *e) { void luaK_exp2anyregup (FuncState *fs, expdesc *e) {
if ((e->k != VUPVAL && e->k != VVARGVAR) || hasjumps(e)) if (e->k != VUPVAL || hasjumps(e))
luaK_exp2anyreg(fs, e); luaK_exp2anyreg(fs, e);
} }
@ -1126,10 +1090,6 @@ void luaK_storevar (FuncState *fs, expdesc *var, expdesc *ex) {
codeABRK(fs, OP_SETFIELD, var->u.ind.t, var->u.ind.idx, ex); codeABRK(fs, OP_SETFIELD, var->u.ind.t, var->u.ind.idx, ex);
break; break;
} }
case VVARGIND: {
needvatab(fs->f); /* function will need a vararg table */
/* now, assignment is to a regular table */
} /* FALLTHROUGH */
case VINDEXED: { case VINDEXED: {
codeABRK(fs, OP_SETTABLE, var->u.ind.t, var->u.ind.idx, ex); codeABRK(fs, OP_SETTABLE, var->u.ind.t, var->u.ind.idx, ex);
break; break;
@ -1202,7 +1162,7 @@ void luaK_goiftrue (FuncState *fs, expdesc *e) {
/* /*
** Emit code to go through if 'e' is false, jump otherwise. ** Emit code to go through if 'e' is false, jump otherwise.
*/ */
static void luaK_goiffalse (FuncState *fs, expdesc *e) { void luaK_goiffalse (FuncState *fs, expdesc *e) {
int pc; /* pc of new jump */ int pc; /* pc of new jump */
luaK_dischargevars(fs, e); luaK_dischargevars(fs, e);
switch (e->k) { switch (e->k) {
@ -1263,7 +1223,7 @@ static void codenot (FuncState *fs, expdesc *e) {
** Check whether expression 'e' is a short literal string ** Check whether expression 'e' is a short literal string
*/ */
static int isKstr (FuncState *fs, expdesc *e) { static int isKstr (FuncState *fs, expdesc *e) {
return (e->k == VK && !hasjumps(e) && e->u.info <= MAXINDEXRK && return (e->k == VK && !hasjumps(e) && e->u.info <= MAXARG_B &&
ttisshrstring(&fs->f->k[e->u.info])); ttisshrstring(&fs->f->k[e->u.info]));
} }
@ -1341,13 +1301,6 @@ 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 ** Create expression 't[k]'. 't' must have its final result already in a
** register or upvalue. Upvalues can only be indexed by literal strings. ** register or upvalue. Upvalues can only be indexed by literal strings.
@ -1359,33 +1312,31 @@ void luaK_indexed (FuncState *fs, expdesc *t, expdesc *k) {
if (k->k == VKSTR) if (k->k == VKSTR)
keystr = str2K(fs, k); keystr = str2K(fs, k);
lua_assert(!hasjumps(t) && lua_assert(!hasjumps(t) &&
(t->k == VLOCAL || t->k == VVARGVAR || (t->k == VLOCAL || t->k == VNONRELOC || t->k == VUPVAL));
t->k == VNONRELOC || t->k == VUPVAL));
if (t->k == VUPVAL && !isKstr(fs, k)) /* upvalue indexed by non 'Kstr'? */ if (t->k == VUPVAL && !isKstr(fs, k)) /* upvalue indexed by non 'Kstr'? */
luaK_exp2anyreg(fs, t); /* put it in a register */ luaK_exp2anyreg(fs, t); /* put it in a register */
if (t->k == VUPVAL) { if (t->k == VUPVAL) {
lu_byte temp = cast_byte(t->u.info); /* upvalue index */ lu_byte temp = cast_byte(t->u.info); /* upvalue index */
t->u.ind.t = temp; /* (avoid a direct assignment; values overlap) */ t->u.ind.t = temp; /* (can't do a direct assignment; values overlap) */
lua_assert(isKstr(fs, k)); lua_assert(isKstr(fs, k));
fillidxk(t, k->u.info, VINDEXUP); /* literal short string */ t->u.ind.idx = cast_short(k->u.info); /* literal short string */
} t->k = VINDEXUP;
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 { else {
/* register index of the table */ /* register index of the table */
lu_byte temp = cast_byte((t->k == VLOCAL) ? t->u.var.ridx: t->u.info); t->u.ind.t = 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)) {
if (isKstr(fs, k)) t->u.ind.idx = cast_short(k->u.info); /* literal short string */
fillidxk(t, k->u.info, VINDEXSTR); /* literal short string */ t->k = VINDEXSTR;
else if (isCint(k)) /* int. constant in proper range? */ }
fillidxk(t, cast_int(k->u.ival), VINDEXI); else if (isCint(k)) { /* int. constant in proper range? */
else t->u.ind.idx = cast_short(k->u.ival);
fillidxk(t, luaK_exp2anyreg(fs, k), VINDEXED); /* register */ t->k = VINDEXI;
}
else {
t->u.ind.idx = cast_short(luaK_exp2anyreg(fs, k)); /* register */
t->k = VINDEXED;
}
} }
t->u.ind.keystr = keystr; /* string index in 'k' */ t->u.ind.keystr = keystr; /* string index in 'k' */
t->u.ind.ro = 0; /* by default, not read-only */ t->u.ind.ro = 0; /* by default, not read-only */
@ -1930,14 +1881,14 @@ static int finaltarget (Instruction *code, int i) {
void luaK_finish (FuncState *fs) { void luaK_finish (FuncState *fs) {
int i; int i;
Proto *p = fs->f; 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++) { for (i = 0; i < fs->pc; i++) {
Instruction *pc = &p->code[i]; Instruction *pc = &p->code[i];
/* avoid "not used" warnings when assert is off (for 'onelua.c') */
(void)luaP_isOT; (void)luaP_isIT;
lua_assert(i == 0 || luaP_isOT(*(pc - 1)) == luaP_isIT(*pc)); lua_assert(i == 0 || luaP_isOT(*(pc - 1)) == luaP_isIT(*pc));
switch (GET_OPCODE(*pc)) { switch (GET_OPCODE(*pc)) {
case OP_RETURN0: case OP_RETURN1: { case OP_RETURN0: case OP_RETURN1: {
if (!(fs->needclose || (p->flag & PF_VAHID))) if (!(fs->needclose || (p->flag & PF_ISVARARG)))
break; /* no extra work */ break; /* no extra work */
/* else use OP_RETURN to do the extra work */ /* else use OP_RETURN to do the extra work */
SET_OPCODE(*pc, OP_RETURN); SET_OPCODE(*pc, OP_RETURN);
@ -1945,23 +1896,13 @@ void luaK_finish (FuncState *fs) {
case OP_RETURN: case OP_TAILCALL: { case OP_RETURN: case OP_TAILCALL: {
if (fs->needclose) if (fs->needclose)
SETARG_k(*pc, 1); /* signal that it needs to close */ SETARG_k(*pc, 1); /* signal that it needs to close */
if (p->flag & PF_VAHID) /* does it use hidden arguments? */ if (p->flag & PF_ISVARARG)
SETARG_C(*pc, p->numparams + 1); /* signal that */ SETARG_C(*pc, p->numparams + 1); /* signal that it is vararg */
break; break;
} }
case OP_GETVARG: { case OP_JMP: {
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: { /* to optimize jumps to jumps */
int target = finaltarget(p->code, i); int target = finaltarget(p->code, i);
fixjump(fs, i, target); /* jump directly to final target */ fixjump(fs, i, target);
break; break;
} }
default: break; default: break;

View file

@ -68,12 +68,9 @@ 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 int luaK_exp2const (FuncState *fs, const expdesc *e, TValue *v);
LUAI_FUNC void luaK_fixline (FuncState *fs, int line); LUAI_FUNC void luaK_fixline (FuncState *fs, int line);
LUAI_FUNC void luaK_nil (FuncState *fs, int from, int n); 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_reserveregs (FuncState *fs, int n);
LUAI_FUNC void luaK_checkstack (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_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 void luaK_dischargevars (FuncState *fs, expdesc *e);
LUAI_FUNC int luaK_exp2anyreg (FuncState *fs, expdesc *e); LUAI_FUNC int luaK_exp2anyreg (FuncState *fs, expdesc *e);
LUAI_FUNC void luaK_exp2anyregup (FuncState *fs, expdesc *e); LUAI_FUNC void luaK_exp2anyregup (FuncState *fs, expdesc *e);
@ -82,6 +79,7 @@ LUAI_FUNC void luaK_exp2val (FuncState *fs, expdesc *e);
LUAI_FUNC void luaK_self (FuncState *fs, expdesc *e, expdesc *key); 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_indexed (FuncState *fs, expdesc *t, expdesc *k);
LUAI_FUNC void luaK_goiftrue (FuncState *fs, expdesc *e); 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_storevar (FuncState *fs, expdesc *var, expdesc *e);
LUAI_FUNC void luaK_setreturns (FuncState *fs, expdesc *e, int nresults); LUAI_FUNC void luaK_setreturns (FuncState *fs, expdesc *e, int nresults);
LUAI_FUNC void luaK_setoneret (FuncState *fs, expdesc *e); LUAI_FUNC void luaK_setoneret (FuncState *fs, expdesc *e);

View file

@ -189,17 +189,15 @@ static int luaB_close (lua_State *L) {
return 2; return 2;
} }
} }
case COS_NORM: case COS_RUN: /* running coroutine? */
return luaL_error(L, "cannot close a %s coroutine", statname[status]);
case COS_RUN:
lua_geti(L, LUA_REGISTRYINDEX, LUA_RIDX_MAINTHREAD); /* get main */ lua_geti(L, LUA_REGISTRYINDEX, LUA_RIDX_MAINTHREAD); /* get main */
if (lua_tothread(L, -1) == co) if (lua_tothread(L, -1) == co)
return luaL_error(L, "cannot close main thread"); return luaL_error(L, "cannot close main thread");
lua_closethread(co, L); /* close itself */ lua_closethread(co, L); /* close itself */
/* previous call does not return *//* FALLTHROUGH */ lua_assert(0); /* previous call does not return */
default:
lua_assert(0);
return 0; return 0;
default: /* normal or running coroutine */
return luaL_error(L, "cannot close a %s coroutine", statname[status]);
} }
} }

View file

@ -18,7 +18,7 @@
#if defined (LUA_UCID) /* accept UniCode IDentifiers? */ #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 #define NONA 0x01
#else #else
#define NONA 0x00 /* default */ #define NONA 0x00 /* default */

View file

@ -427,7 +427,7 @@ static int db_debug (lua_State *L) {
if (fgets(buffer, sizeof(buffer), stdin) == NULL || if (fgets(buffer, sizeof(buffer), stdin) == NULL ||
strcmp(buffer, "cont\n") == 0) strcmp(buffer, "cont\n") == 0)
return 0; return 0;
if (luaL_loadbufferx(L, buffer, strlen(buffer), "=(debug command)", "t") || if (luaL_loadbuffer(L, buffer, strlen(buffer), "=(debug command)") ||
lua_pcall(L, 0, 0, 0)) lua_pcall(L, 0, 0, 0))
lua_writestringerror("%s\n", luaL_tolstring(L, -1, NULL)); lua_writestringerror("%s\n", luaL_tolstring(L, -1, NULL));
lua_settop(L, 0); /* remove eventual returns */ lua_settop(L, 0); /* remove eventual returns */

View file

@ -150,7 +150,7 @@ LUA_API lua_Hook lua_gethook (lua_State *L) {
LUA_API int lua_gethookmask (lua_State *L) { LUA_API int lua_gethookmask (lua_State *L) {
return cast_int(L->hookmask); return L->hookmask;
} }
@ -184,7 +184,7 @@ static const char *upvalname (const Proto *p, int uv) {
static const char *findvararg (CallInfo *ci, int n, StkId *pos) { static const char *findvararg (CallInfo *ci, int n, StkId *pos) {
if (clLvalue(s2v(ci->func.p))->p->flag & PF_VAHID) { if (clLvalue(s2v(ci->func.p))->p->flag & PF_ISVARARG) {
int nextra = ci->u.l.nextraargs; int nextra = ci->u.l.nextraargs;
if (n >= -nextra) { /* 'n' is negative */ if (n >= -nextra) { /* 'n' is negative */
*pos = ci->func.p - nextra - (n + 1); *pos = ci->func.p - nextra - (n + 1);
@ -291,7 +291,7 @@ static int nextline (const Proto *p, int currentline, int pc) {
static void collectvalidlines (lua_State *L, Closure *f) { static void collectvalidlines (lua_State *L, Closure *f) {
if (!LuaClosure(f)) { if (!LuaClosure(f)) {
setnilvalue2s(L->top.p); setnilvalue(s2v(L->top.p));
api_incr_top(L); api_incr_top(L);
} }
else { else {
@ -304,7 +304,7 @@ static void collectvalidlines (lua_State *L, Closure *f) {
int i; int i;
TValue v; TValue v;
setbtvalue(&v); /* boolean 'true' to be the value of all indices */ setbtvalue(&v); /* boolean 'true' to be the value of all indices */
if (!(isvararg(p))) /* regular function? */ if (!(p->flag & PF_ISVARARG)) /* regular function? */
i = 0; /* consider all instructions */ i = 0; /* consider all instructions */
else { /* vararg function */ else { /* vararg function */
lua_assert(GET_OPCODE(p->code[0]) == OP_VARARGPREP); lua_assert(GET_OPCODE(p->code[0]) == OP_VARARGPREP);
@ -348,7 +348,7 @@ static int auxgetinfo (lua_State *L, const char *what, lua_Debug *ar,
ar->nparams = 0; ar->nparams = 0;
} }
else { else {
ar->isvararg = (isvararg(f->l.p)) ? 1 : 0; ar->isvararg = (f->l.p->flag & PF_ISVARARG) ? 1 : 0;
ar->nparams = f->l.p->numparams; ar->nparams = f->l.p->numparams;
} }
break; break;
@ -580,7 +580,7 @@ static const char *getobjname (const Proto *p, int lastpc, int reg,
kname(p, k, name); kname(p, k, name);
return isEnv(p, lastpc, i, 1); return isEnv(p, lastpc, i, 1);
} }
case OP_GETTABLE: case OP_GETVARG: { case OP_GETTABLE: {
int k = GETARG_C(i); /* key index */ int k = GETARG_C(i); /* key index */
rname(p, lastpc, k, name); rname(p, lastpc, k, name);
return isEnv(p, lastpc, i, 0); return isEnv(p, lastpc, i, 0);
@ -814,14 +814,6 @@ 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' */ /* add src:line information to 'msg' */
const char *luaG_addinfo (lua_State *L, const char *msg, TString *src, const char *luaG_addinfo (lua_State *L, const char *msg, TString *src,
int line) { int line) {
@ -912,7 +904,7 @@ int luaG_tracecall (lua_State *L) {
Proto *p = ci_func(ci)->p; Proto *p = ci_func(ci)->p;
ci->u.l.trap = 1; /* ensure hooks will be checked */ ci->u.l.trap = 1; /* ensure hooks will be checked */
if (ci->u.l.savedpc == p->code) { /* first instruction (not resuming)? */ if (ci->u.l.savedpc == p->code) { /* first instruction (not resuming)? */
if (isvararg(p)) if (p->flag & PF_ISVARARG)
return 0; /* hooks will start at VARARGPREP instruction */ return 0; /* hooks will start at VARARGPREP instruction */
else if (!(ci->callstatus & CIST_HOOKYIELD)) /* not yielded? */ else if (!(ci->callstatus & CIST_HOOKYIELD)) /* not yielded? */
luaD_hookcall(L, ci); /* check 'call' hook */ luaD_hookcall(L, ci); /* check 'call' hook */

View file

@ -53,7 +53,6 @@ LUAI_FUNC l_noret luaG_tointerror (lua_State *L, const TValue *p1,
const TValue *p2); const TValue *p2);
LUAI_FUNC l_noret luaG_ordererror (lua_State *L, const TValue *p1, LUAI_FUNC l_noret luaG_ordererror (lua_State *L, const TValue *p1,
const TValue *p2); 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 l_noret luaG_runerror (lua_State *L, const char *fmt, ...);
LUAI_FUNC const char *luaG_addinfo (lua_State *L, const char *msg, LUAI_FUNC const char *luaG_addinfo (lua_State *L, const char *msg,
TString *src, int line); TString *src, int line);

131
ldo.c
View file

@ -57,18 +57,10 @@
** ======================================================= ** =======================================================
*/ */
/* 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 ** LUAI_THROW/LUAI_TRY define how Lua does exception handling. By
** default, Lua handles errors with exceptions when compiling as ** default, Lua handles errors with exceptions when compiling as
** C++ code, with _longjmp/_setjmp when available (POSIX), and with ** C++ code, with _longjmp/_setjmp when asked to use them, and with
** longjmp/setjmp otherwise. ** longjmp/setjmp otherwise.
*/ */
#if !defined(LUAI_THROW) /* { */ #if !defined(LUAI_THROW) /* { */
@ -77,38 +69,38 @@ typedef struct lua_longjmp {
/* C++ exceptions */ /* C++ exceptions */
#define LUAI_THROW(L,c) throw(c) #define LUAI_THROW(L,c) throw(c)
#define LUAI_TRY(L,c,f,ud) \
static void LUAI_TRY (lua_State *L, lua_longjmp *c, Pfunc f, void *ud) { try { (f)(L, ud); } catch(...) { if ((c)->status == 0) (c)->status = -1; }
try { #define luai_jmpbuf int /* dummy field */
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) /* }{ */ #elif defined(LUA_USE_POSIX) /* }{ */
/* in POSIX, use _longjmp/_setjmp (more efficient) */ /* in POSIX, try _longjmp/_setjmp (more efficient) */
#define LUAI_THROW(L,c) _longjmp((c)->b, 1) #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_TRY(L,c,f,ud) if (_setjmp((c)->b) == 0) ((f)(L, ud))
#define luai_jmpbuf jmp_buf
#else /* }{ */ #else /* }{ */
/* ISO C handling with long jumps */ /* ISO C handling with long jumps */
#define LUAI_THROW(L,c) longjmp((c)->b, 1) #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_TRY(L,c,f,ud) if (setjmp((c)->b) == 0) ((f)(L, ud))
#define luai_jmpbuf jmp_buf
#endif /* } */ #endif /* } */
#endif /* } */ #endif /* } */
/* chain list of long jump buffers */
struct lua_longjmp {
struct lua_longjmp *previous;
luai_jmpbuf b;
volatile TStatus status; /* error code */
};
void luaD_seterrorobj (lua_State *L, TStatus errcode, StkId oldtop) { void luaD_seterrorobj (lua_State *L, TStatus errcode, StkId oldtop) {
if (errcode == LUA_ERRMEM) { /* memory error? */ if (errcode == LUA_ERRMEM) { /* memory error? */
setsvalue2s(L, oldtop, G(L)->memerrmsg); /* reuse preregistered msg. */ setsvalue2s(L, oldtop, G(L)->memerrmsg); /* reuse preregistered msg. */
@ -159,7 +151,7 @@ l_noret luaD_throwbaselevel (lua_State *L, TStatus errcode) {
TStatus luaD_rawrunprotected (lua_State *L, Pfunc f, void *ud) { TStatus luaD_rawrunprotected (lua_State *L, Pfunc f, void *ud) {
l_uint32 oldnCcalls = L->nCcalls; l_uint32 oldnCcalls = L->nCcalls;
lua_longjmp lj; struct lua_longjmp lj;
lj.status = LUA_OK; lj.status = LUA_OK;
lj.previous = L->errorJmp; /* chain new error handler */ lj.previous = L->errorJmp; /* chain new error handler */
L->errorJmp = &lj; L->errorJmp = &lj;
@ -182,20 +174,6 @@ TStatus luaD_rawrunprotected (lua_State *L, Pfunc f, void *ud) {
#define STACKERRSPACE 200 #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 */ /* maximum stack size that respects size_t */
#define MAXSTACK_BYSIZET ((MAX_SIZET / sizeof(StackValue)) - STACKERRSPACE) #define MAXSTACK_BYSIZET ((MAX_SIZET / sizeof(StackValue)) - STACKERRSPACE)
@ -211,7 +189,7 @@ TStatus luaD_rawrunprotected (lua_State *L, Pfunc f, void *ud) {
#define ERRORSTACKSIZE (MAXSTACK + STACKERRSPACE) #define ERRORSTACKSIZE (MAXSTACK + STACKERRSPACE)
/* raise a stack error while running the message handler */ /* raise an error while running the message handler */
l_noret luaD_errerr (lua_State *L) { l_noret luaD_errerr (lua_State *L) {
TString *msg = luaS_newliteral(L, "error in error handling"); TString *msg = luaS_newliteral(L, "error in error handling");
setsvalue2s(L, L->top.p, msg); setsvalue2s(L, L->top.p, msg);
@ -220,25 +198,6 @@ l_noret luaD_errerr (lua_State *L) {
} }
/*
** 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 ** In ISO C, any pointer use after the pointer has been deallocated is
** undefined behavior. So, before a stack reallocation, all pointers ** undefined behavior. So, before a stack reallocation, all pointers
@ -349,7 +308,7 @@ int luaD_reallocstack (lua_State *L, int newsize, int raiseerror) {
correctstack(L, oldstack); /* change offsets back to pointers */ correctstack(L, oldstack); /* change offsets back to pointers */
L->stack_last.p = L->stack.p + newsize; L->stack_last.p = L->stack.p + newsize;
for (i = oldsize + EXTRA_STACK; i < newsize + EXTRA_STACK; i++) for (i = oldsize + EXTRA_STACK; i < newsize + EXTRA_STACK; i++)
setnilvalue2s(newstack + i); /* erase new segment */ setnilvalue(s2v(newstack + i)); /* erase new segment */
return 1; return 1;
} }
@ -366,7 +325,7 @@ int luaD_growstack (lua_State *L, int n, int raiseerror) {
a stack error; cannot grow further than that. */ a stack error; cannot grow further than that. */
lua_assert(stacksize(L) == ERRORSTACKSIZE); lua_assert(stacksize(L) == ERRORSTACKSIZE);
if (raiseerror) if (raiseerror)
luaD_errerr(L); /* stack error inside message handler */ luaD_errerr(L); /* error inside message handler */
return 0; /* if not 'raiseerror', just signal it */ return 0; /* if not 'raiseerror', just signal it */
} }
else if (n < MAXSTACK) { /* avoids arithmetic overflows */ else if (n < MAXSTACK) { /* avoids arithmetic overflows */
@ -430,6 +389,12 @@ void luaD_shrinkstack (lua_State *L) {
luaE_shrinkCI(L); /* shrink CI list */ luaE_shrinkCI(L); /* shrink CI list */
} }
void luaD_inctop (lua_State *L) {
L->top.p++;
luaD_checkstack(L, 1);
}
/* }================================================================== */ /* }================================================================== */
@ -500,7 +465,7 @@ static void rethook (lua_State *L, CallInfo *ci, int nres) {
int ftransfer; int ftransfer;
if (isLua(ci)) { if (isLua(ci)) {
Proto *p = ci_func(ci)->p; Proto *p = ci_func(ci)->p;
if (p->flag & PF_VAHID) if (p->flag & PF_ISVARARG)
delta = ci->u.l.nextraargs + p->numparams + 1; delta = ci->u.l.nextraargs + p->numparams + 1;
} }
ci->func.p += delta; /* if vararg, back to virtual 'func' */ ci->func.p += delta; /* if vararg, back to virtual 'func' */
@ -548,7 +513,7 @@ l_sinline void genmoveresults (lua_State *L, StkId res, int nres,
for (i = 0; i < nres; i++) /* move all results to correct place */ for (i = 0; i < nres; i++) /* move all results to correct place */
setobjs2s(L, res + i, firstresult + i); setobjs2s(L, res + i, firstresult + i);
for (; i < wanted; i++) /* complete wanted number of results */ for (; i < wanted; i++) /* complete wanted number of results */
setnilvalue2s(res + i); setnilvalue(s2v(res + i));
L->top.p = res + wanted; /* top points after the last result */ L->top.p = res + wanted; /* top points after the last result */
} }
@ -568,7 +533,7 @@ l_sinline void moveresults (lua_State *L, StkId res, int nres,
return; return;
case 1 + 1: /* one value needed */ case 1 + 1: /* one value needed */
if (nres == 0) /* no results? */ if (nres == 0) /* no results? */
setnilvalue2s(res); /* adjust with nil */ setnilvalue(s2v(res)); /* adjust with nil */
else /* at least one result */ else /* at least one result */
setobjs2s(L, res, L->top.p - nres); /* move it to proper place */ setobjs2s(L, res, L->top.p - nres); /* move it to proper place */
L->top.p = res + 1; L->top.p = res + 1;
@ -618,7 +583,7 @@ void luaD_poscall (lua_State *L, CallInfo *ci, int nres) {
#define next_ci(L) (L->ci->next ? L->ci->next : luaE_extendCI(L, 1)) #define next_ci(L) (L->ci->next ? L->ci->next : luaE_extendCI(L))
/* /*
@ -688,7 +653,7 @@ int luaD_pretailcall (lua_State *L, CallInfo *ci, StkId func,
setobjs2s(L, ci->func.p + i, func + i); setobjs2s(L, ci->func.p + i, func + i);
func = ci->func.p; /* moved-down function */ func = ci->func.p; /* moved-down function */
for (; narg1 <= nfixparams; narg1++) for (; narg1 <= nfixparams; narg1++)
setnilvalue2s(func + narg1); /* complete missing arguments */ setnilvalue(s2v(func + narg1)); /* complete missing arguments */
ci->top.p = func + 1 + fsize; /* top for new function */ ci->top.p = func + 1 + fsize; /* top for new function */
lua_assert(ci->top.p <= L->stack_last.p); lua_assert(ci->top.p <= L->stack_last.p);
ci->u.l.savedpc = p->code; /* starting point */ ci->u.l.savedpc = p->code; /* starting point */
@ -735,7 +700,7 @@ CallInfo *luaD_precall (lua_State *L, StkId func, int nresults) {
L->ci = ci = prepCallInfo(L, func, status, func + 1 + fsize); L->ci = ci = prepCallInfo(L, func, status, func + 1 + fsize);
ci->u.l.savedpc = p->code; /* starting point */ ci->u.l.savedpc = p->code; /* starting point */
for (; narg < nfixparams; narg++) for (; narg < nfixparams; narg++)
setnilvalue2s(L->top.p++); /* complete missing arguments */ setnilvalue(s2v(L->top.p++)); /* complete missing arguments */
lua_assert(ci->top.p <= L->stack_last.p); lua_assert(ci->top.p <= L->stack_last.p);
return ci; return ci;
} }
@ -1122,54 +1087,28 @@ 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) { static void f_parser (lua_State *L, void *ud) {
LClosure *cl; LClosure *cl;
struct SParser *p = cast(struct SParser *, ud); struct SParser *p = cast(struct SParser *, ud);
const char *mode = p->mode ? p->mode : "bt"; const char *mode = p->mode ? p->mode : "bt";
int c; int c = zgetc(p->z); /* read first character */
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]) { if (c == LUA_SIGNATURE[0]) {
int fixed = 0; int fixed = 0;
if (strchr(mode, 'B') != NULL) if (strchr(mode, 'B') != NULL)
fixed = 1; fixed = 1;
else else
checkmode(L, mode, "binary"); checkmode(L, mode, "binary");
cl = luaU_undump(L, p->z, anchor, p->name, fixed); cl = luaU_undump(L, p->z, p->name, fixed);
} }
else { else {
checkmode(L, mode, "text"); checkmode(L, mode, "text");
cl = luaY_parser(L, p->z, anchor, &p->buff, &p->dyd, p->name, c); cl = luaY_parser(L, p->z, &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); lua_assert(cl->nupvalues == cl->p->sizeupvalues);
luaF_initupvals(L, cl); luaF_initupvals(L, cl);
} }
/*
** 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, TStatus luaD_protectedparser (lua_State *L, ZIO *z, const char *name,
const char *mode) { const char *mode) {
struct SParser p; struct SParser p;

3
ldo.h
View file

@ -88,8 +88,7 @@ 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_reallocstack (lua_State *L, int newsize, int raiseerror);
LUAI_FUNC int luaD_growstack (lua_State *L, int n, 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_shrinkstack (lua_State *L);
LUAI_FUNC int luaD_checkminstack (lua_State *L); LUAI_FUNC void luaD_inctop (lua_State *L);
LUAI_FUNC void luaD_anchorobj (lua_State *L, Table *anchor, GCObject *obj);
LUAI_FUNC l_noret luaD_throw (lua_State *L, TStatus errcode); LUAI_FUNC l_noret luaD_throw (lua_State *L, TStatus errcode);
LUAI_FUNC l_noret luaD_throwbaselevel (lua_State *L, TStatus errcode); LUAI_FUNC l_noret luaD_throwbaselevel (lua_State *L, TStatus errcode);

22
ldump.c
View file

@ -132,31 +132,27 @@ static void dumpInteger (DumpState *D, lua_Integer x) {
/* /*
** Dump a String. First dump its "size": ** Dump a String. First dump its "size": size==0 means NULL;
** size==0 is followed by an index and means "reuse saved string with ** size==1 is followed by an index and means "reuse saved string with
** that index"; index==0 means NULL. ** that index"; size>=2 is followed by the string contents with real
** size>=1 is followed by the string contents with real size==size-1 and ** size==size-2 and means that string, which will be saved with
** means that string, which will be saved with the next available index. ** 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) { static void dumpString (DumpState *D, TString *ts) {
if (ts == NULL) { if (ts == NULL)
dumpVarint(D, 0); /* will "reuse" NULL */ dumpSize(D, 0);
dumpVarint(D, 0); /* special index for NULL */
}
else { else {
TValue idx; TValue idx;
int tag = luaH_getstr(D->h, ts, &idx); int tag = luaH_getstr(D->h, ts, &idx);
if (!tagisempty(tag)) { /* string already saved? */ if (!tagisempty(tag)) { /* string already saved? */
dumpVarint(D, 0); /* reuse a saved string */ dumpVarint(D, 1); /* reuse a saved string */
dumpVarint(D, l_castS2U(ivalue(&idx))); /* index of saved string */ dumpVarint(D, l_castS2U(ivalue(&idx))); /* index of saved string */
} }
else { /* must write and save the string */ else { /* must write and save the string */
TValue key, value; /* to save the string in the hash */ TValue key, value; /* to save the string in the hash */
size_t size; size_t size;
const char *s = getlstr(ts, size); const char *s = getlstr(ts, size);
dumpSize(D, size + 1); dumpSize(D, size + 2);
dumpVector(D, s, size + 1); /* include ending '\0' */ dumpVector(D, s, size + 1); /* include ending '\0' */
D->nstr++; /* one more saved string */ D->nstr++; /* one more saved string */
setsvalue(D->L, &key, ts); /* the string is the key */ setsvalue(D->L, &key, ts); /* the string is the key */

View file

@ -196,7 +196,8 @@ void luaF_unlinkupval (UpVal *uv) {
*/ */
void luaF_closeupval (lua_State *L, StkId level) { void luaF_closeupval (lua_State *L, StkId level) {
UpVal *uv; UpVal *uv;
while ((uv = L->openupval) != NULL && uplevel(uv) >= level) { StkId upl; /* stack index pointed by 'uv' */
while ((uv = L->openupval) != NULL && (upl = uplevel(uv)) >= level) {
TValue *slot = &uv->u.value; /* new position for value */ TValue *slot = &uv->u.value; /* new position for value */
lua_assert(uplevel(uv) < L->top.p); lua_assert(uplevel(uv) < L->top.p);
luaF_unlinkupval(uv); /* remove upvalue from 'openupval' list */ luaF_unlinkupval(uv); /* remove upvalue from 'openupval' list */

20
lgc.c
View file

@ -594,10 +594,10 @@ static void traversestrongtable (global_State *g, Table *h) {
*/ */
static int getmode (global_State *g, Table *h) { static int getmode (global_State *g, Table *h) {
const TValue *mode = gfasttm(g, h->metatable, TM_MODE); const TValue *mode = gfasttm(g, h->metatable, TM_MODE);
if (mode == NULL || !ttisstring(mode)) if (mode == NULL || !ttisshrstring(mode))
return 0; /* ignore non-string modes */ return 0; /* ignore non-(short)string modes */
else { else {
const char *smode = getstr(tsvalue(mode)); const char *smode = getshrstr(tsvalue(mode));
const char *weakkey = strchr(smode, 'k'); const char *weakkey = strchr(smode, 'k');
const char *weakvalue = strchr(smode, 'v'); const char *weakvalue = strchr(smode, 'v');
return ((weakkey != NULL) << 1) | (weakvalue != NULL); return ((weakkey != NULL) << 1) | (weakvalue != NULL);
@ -624,7 +624,7 @@ static l_mem traversetable (global_State *g, Table *h) {
linkgclist(h, g->allweak); /* must clear collected entries */ linkgclist(h, g->allweak); /* must clear collected entries */
break; break;
} }
return cast(l_mem, 1 + 2*sizenode(h) + h->asize); return 1 + 2*sizenode(h) + h->asize;
} }
@ -709,7 +709,7 @@ static l_mem traversethread (global_State *g, lua_State *th) {
if (!g->gcemergency) if (!g->gcemergency)
luaD_shrinkstack(th); /* do not change stack in emergency cycle */ luaD_shrinkstack(th); /* do not change stack in emergency cycle */
for (o = th->top.p; o < th->stack_last.p + EXTRA_STACK; o++) for (o = th->top.p; o < th->stack_last.p + EXTRA_STACK; o++)
setnilvalue2s(o); /* clear dead stack slice */ setnilvalue(s2v(o)); /* clear dead stack slice */
/* 'remarkupvals' may have removed thread from 'twups' list */ /* 'remarkupvals' may have removed thread from 'twups' list */
if (!isintwups(th) && th->openupval != NULL) { if (!isintwups(th) && th->openupval != NULL) {
th->twups = g->twups; /* link it back to the list */ th->twups = g->twups; /* link it back to the list */
@ -1293,7 +1293,7 @@ static void finishgencycle (lua_State *L, global_State *g) {
correctgraylists(g); correctgraylists(g);
checkSizes(L, g); checkSizes(L, g);
g->gcstate = GCSpropagate; /* skip restart */ g->gcstate = GCSpropagate; /* skip restart */
if (g->tobefnz != NULL && !g->gcemergency && luaD_checkminstack(L)) if (!g->gcemergency)
callallpendingfinalizers(L); callallpendingfinalizers(L);
} }
@ -1472,8 +1472,7 @@ static int checkmajorminor (lua_State *L, global_State *g) {
if (g->gckind == KGC_GENMAJOR) { /* generational mode? */ if (g->gckind == KGC_GENMAJOR) { /* generational mode? */
l_mem numbytes = gettotalbytes(g); l_mem numbytes = gettotalbytes(g);
l_mem addedbytes = numbytes - g->GCmajorminor; l_mem addedbytes = numbytes - g->GCmajorminor;
l_mem limit = (addedbytes < 0) ? 0 l_mem limit = applygcparam(g, MAJORMINOR, addedbytes);
: applygcparam(g, MAJORMINOR, addedbytes);
l_mem tobecollected = numbytes - g->GCmarked; l_mem tobecollected = numbytes - g->GCmarked;
if (tobecollected > limit) { if (tobecollected > limit) {
atomic2gen(L, g); /* return to generational mode */ atomic2gen(L, g); /* return to generational mode */
@ -1668,13 +1667,12 @@ static l_mem singlestep (lua_State *L, int fast) {
break; break;
} }
case GCScallfin: { /* call finalizers */ case GCScallfin: { /* call finalizers */
if (g->tobefnz && !g->gcemergency && luaD_checkminstack(L)) { if (g->tobefnz && !g->gcemergency) {
g->gcstopem = 0; /* ok collections during finalizers */ g->gcstopem = 0; /* ok collections during finalizers */
GCTM(L); /* call one finalizer */ GCTM(L); /* call one finalizer */
stepresult = CWUFIN; stepresult = CWUFIN;
} }
else { /* no more finalizers or emergency mode or not enough stack else { /* emergency mode or no more finalizers */
to run finalizers */
g->gcstate = GCSpause; /* finish collection */ g->gcstate = GCSpause; /* finish collection */
stepresult = step2pause; stepresult = step2pause;
} }

5
lgc.h
View file

@ -231,9 +231,8 @@
#endif #endif
#define luaC_condGC(L,pre,pos) \ #define luaC_condGC(L,pre,pos) \
{ if (G(L)->GCdebt <= 0) \ { if (G(L)->GCdebt <= 0) { pre; luaC_step(L); pos;}; \
{ pre; luaC_step(L); condchangemem(L,{},{},0); pos;} \ condchangemem(L,pre,pos,0); }
else condchangemem(L,pre,pos,0); }
/* more often than not, 'pre'/'pos' are empty */ /* more often than not, 'pre'/'pos' are empty */
#define luaC_checkGC(L) luaC_condGC(L,(void)0,(void)0) #define luaC_checkGC(L) luaC_condGC(L,(void)0,(void)0)

View file

@ -114,7 +114,7 @@ static int l_checkmode (const char *mode) {
#if !defined(l_fseek) /* { */ #if !defined(l_fseek) /* { */
#if defined(LUA_USE_POSIX) || defined(LUA_USE_OFF_T) /* { */ #if defined(LUA_USE_POSIX) /* { */
#include <sys/types.h> #include <sys/types.h>

View file

@ -21,7 +21,7 @@ static const void *const disptab[NUM_OPCODES] = {
#if 0 #if 0
** you can update the following list with this command: ** 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 #endif
@ -57,8 +57,8 @@ static const void *const disptab[NUM_OPCODES] = {
&&L_OP_BANDK, &&L_OP_BANDK,
&&L_OP_BORK, &&L_OP_BORK,
&&L_OP_BXORK, &&L_OP_BXORK,
&&L_OP_SHLI,
&&L_OP_SHRI, &&L_OP_SHRI,
&&L_OP_SHLI,
&&L_OP_ADD, &&L_OP_ADD,
&&L_OP_SUB, &&L_OP_SUB,
&&L_OP_MUL, &&L_OP_MUL,
@ -106,8 +106,6 @@ static const void *const disptab[NUM_OPCODES] = {
&&L_OP_SETLIST, &&L_OP_SETLIST,
&&L_OP_CLOSURE, &&L_OP_CLOSURE,
&&L_OP_VARARG, &&L_OP_VARARG,
&&L_OP_GETVARG,
&&L_OP_ERRNNIL,
&&L_OP_VARARGPREP, &&L_OP_VARARGPREP,
&&L_OP_EXTRAARG &&L_OP_EXTRAARG

2
llex.c
View file

@ -188,7 +188,7 @@ void luaX_setinput (lua_State *L, LexState *ls, ZIO *z, TString *source,
so they cannot be collected */ so they cannot be collected */
ls->envn = luaS_newliteral(L, LUA_ENV); /* get env string */ ls->envn = luaS_newliteral(L, LUA_ENV); /* get env string */
ls->brkn = luaS_newliteral(L, "break"); /* get "break" string */ ls->brkn = luaS_newliteral(L, "break"); /* get "break" string */
#if LUA_COMPAT_GLOBAL #if defined(LUA_COMPAT_GLOBAL)
/* compatibility mode: "global" is not a reserved word */ /* compatibility mode: "global" is not a reserved word */
ls->glbn = luaS_newliteral(L, "global"); /* get "global" string */ ls->glbn = luaS_newliteral(L, "global"); /* get "global" string */
ls->glbn->extra = 0; /* mark it as not reserved */ ls->glbn->extra = 0; /* mark it as not reserved */

View file

@ -20,8 +20,8 @@
/* /*
** 'l_mem' is a signed integer big enough to count the total memory ** '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 ** used by Lua. (It is signed due to the use of debt in several
** computations.) 'lu_mem' is a corresponding unsigned type. Usually, ** computations.) Usually, 'ptrdiff_t' should work, but we use 'long'
** 'ptrdiff_t' should work, but we use 'long' for 16-bit machines. ** for 16-bit machines.
*/ */
#if defined(LUAI_MEM) /* { external definitions? */ #if defined(LUAI_MEM) /* { external definitions? */
typedef LUAI_MEM l_mem; typedef LUAI_MEM l_mem;
@ -59,6 +59,13 @@ typedef lu_byte TStatus;
#define MAX_SIZE (sizeof(size_t) < sizeof(lua_Integer) ? MAX_SIZET \ #define MAX_SIZE (sizeof(size_t) < sizeof(lua_Integer) ? MAX_SIZET \
: cast_sizet(LUA_MAXINTEGER)) : 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) (l_numbits(t) - 2)
/* /*
** test whether an unsigned value is a power of 2 (or zero) ** test whether an unsigned value is a power of 2 (or zero)
*/ */
@ -234,12 +241,12 @@ typedef unsigned long l_uint32;
/* floor division (defined as 'floor(a/b)') */ /* floor division (defined as 'floor(a/b)') */
#if !defined(luai_numidiv) #if !defined(luai_numidiv)
#define luai_numidiv(L,a,b) l_floor(luai_numdiv(L,a,b)) #define luai_numidiv(L,a,b) ((void)L, l_floor(luai_numdiv(L,a,b)))
#endif #endif
/* float division */ /* float division */
#if !defined(luai_numdiv) #if !defined(luai_numdiv)
#define luai_numdiv(L,a,b) ((void)L, (a)/(b)) #define luai_numdiv(L,a,b) ((a)/(b))
#endif #endif
/* /*
@ -267,10 +274,10 @@ typedef unsigned long l_uint32;
/* the others are quite standard operations */ /* the others are quite standard operations */
#if !defined(luai_numadd) #if !defined(luai_numadd)
#define luai_numadd(L,a,b) ((void)L, (a)+(b)) #define luai_numadd(L,a,b) ((a)+(b))
#define luai_numsub(L,a,b) ((void)L, (a)-(b)) #define luai_numsub(L,a,b) ((a)-(b))
#define luai_nummul(L,a,b) ((void)L, (a)*(b)) #define luai_nummul(L,a,b) ((a)*(b))
#define luai_numunm(L,a) ((void)L, -(a)) #define luai_numunm(L,a) (-(a))
#define luai_numeq(a,b) ((a)==(b)) #define luai_numeq(a,b) ((a)==(b))
#define luai_numlt(a,b) ((a)<(b)) #define luai_numlt(a,b) ((a)<(b))
#define luai_numle(a,b) ((a)<=(b)) #define luai_numle(a,b) ((a)<=(b))
@ -280,55 +287,6 @@ typedef unsigned long l_uint32;
#endif #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 ** "Abstraction Layer" for basic report of messages and errors

View file

@ -38,37 +38,31 @@ static int math_abs (lua_State *L) {
return 1; return 1;
} }
static int math_sin (lua_State *L) { static int math_sin (lua_State *L) {
lua_pushnumber(L, l_mathop(sin)(luaL_checknumber(L, 1))); lua_pushnumber(L, l_mathop(sin)(luaL_checknumber(L, 1)));
return 1; return 1;
} }
static int math_cos (lua_State *L) { static int math_cos (lua_State *L) {
lua_pushnumber(L, l_mathop(cos)(luaL_checknumber(L, 1))); lua_pushnumber(L, l_mathop(cos)(luaL_checknumber(L, 1)));
return 1; return 1;
} }
static int math_tan (lua_State *L) { static int math_tan (lua_State *L) {
lua_pushnumber(L, l_mathop(tan)(luaL_checknumber(L, 1))); lua_pushnumber(L, l_mathop(tan)(luaL_checknumber(L, 1)));
return 1; return 1;
} }
static int math_asin (lua_State *L) { static int math_asin (lua_State *L) {
lua_pushnumber(L, l_mathop(asin)(luaL_checknumber(L, 1))); lua_pushnumber(L, l_mathop(asin)(luaL_checknumber(L, 1)));
return 1; return 1;
} }
static int math_acos (lua_State *L) { static int math_acos (lua_State *L) {
lua_pushnumber(L, l_mathop(acos)(luaL_checknumber(L, 1))); lua_pushnumber(L, l_mathop(acos)(luaL_checknumber(L, 1)));
return 1; return 1;
} }
static int math_atan (lua_State *L) { static int math_atan (lua_State *L) {
lua_Number y = luaL_checknumber(L, 1); lua_Number y = luaL_checknumber(L, 1);
lua_Number x = luaL_optnumber(L, 2, 1); lua_Number x = luaL_optnumber(L, 2, 1);
@ -173,7 +167,6 @@ static int math_ult (lua_State *L) {
return 1; return 1;
} }
static int math_log (lua_State *L) { static int math_log (lua_State *L) {
lua_Number x = luaL_checknumber(L, 1); lua_Number x = luaL_checknumber(L, 1);
lua_Number res; lua_Number res;
@ -195,42 +188,22 @@ static int math_log (lua_State *L) {
return 1; return 1;
} }
static int math_exp (lua_State *L) { static int math_exp (lua_State *L) {
lua_pushnumber(L, l_mathop(exp)(luaL_checknumber(L, 1))); lua_pushnumber(L, l_mathop(exp)(luaL_checknumber(L, 1)));
return 1; return 1;
} }
static int math_deg (lua_State *L) { static int math_deg (lua_State *L) {
lua_pushnumber(L, luaL_checknumber(L, 1) * (l_mathop(180.0) / PI)); lua_pushnumber(L, luaL_checknumber(L, 1) * (l_mathop(180.0) / PI));
return 1; return 1;
} }
static int math_rad (lua_State *L) { static int math_rad (lua_State *L) {
lua_pushnumber(L, luaL_checknumber(L, 1) * (PI / l_mathop(180.0))); lua_pushnumber(L, luaL_checknumber(L, 1) * (PI / l_mathop(180.0)));
return 1; 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) { static int math_min (lua_State *L) {
int n = lua_gettop(L); /* number of arguments */ int n = lua_gettop(L); /* number of arguments */
int imin = 1; /* index of current minimum value */ int imin = 1; /* index of current minimum value */
@ -278,7 +251,7 @@ static int math_type (lua_State *L) {
*/ */
/* /*
** This code uses lots of shifts. ISO C does not allow shifts greater ** This code uses lots of shifts. ANSI C does not allow shifts greater
** than or equal to the width of the type being shifted, so some shifts ** than or equal to the width of the type being shifted, so some shifts
** are written in convoluted ways to match that restriction. For ** are written in convoluted ways to match that restriction. For
** preprocessor tests, it assumes a width of 32 bits, so the maximum ** preprocessor tests, it assumes a width of 32 bits, so the maximum
@ -693,6 +666,20 @@ static int math_pow (lua_State *L) {
return 1; 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) { static int math_log10 (lua_State *L) {
lua_pushnumber(L, l_mathop(log10)(luaL_checknumber(L, 1))); lua_pushnumber(L, l_mathop(log10)(luaL_checknumber(L, 1)));
return 1; return 1;
@ -704,35 +691,35 @@ static int math_log10 (lua_State *L) {
static const luaL_Reg mathlib[] = { static const luaL_Reg mathlib[] = {
{"abs", math_abs}, {"abs", math_abs},
{"acos", math_acos}, {"acos", math_acos},
{"asin", math_asin}, {"asin", math_asin},
{"atan", math_atan}, {"atan", math_atan},
{"ceil", math_ceil}, {"ceil", math_ceil},
{"cos", math_cos}, {"cos", math_cos},
{"deg", math_deg}, {"deg", math_deg},
{"exp", math_exp}, {"exp", math_exp},
{"tointeger", math_toint}, {"tointeger", math_toint},
{"floor", math_floor}, {"floor", math_floor},
{"fmod", math_fmod}, {"fmod", math_fmod},
{"frexp", math_frexp}, {"ult", math_ult},
{"ult", math_ult}, {"log", math_log},
{"ldexp", math_ldexp}, {"max", math_max},
{"log", math_log}, {"min", math_min},
{"max", math_max}, {"modf", math_modf},
{"min", math_min}, {"rad", math_rad},
{"modf", math_modf}, {"sin", math_sin},
{"rad", math_rad}, {"sqrt", math_sqrt},
{"sin", math_sin}, {"tan", math_tan},
{"sqrt", math_sqrt},
{"tan", math_tan},
{"type", math_type}, {"type", math_type},
#if defined(LUA_COMPAT_MATHLIB) #if defined(LUA_COMPAT_MATHLIB)
{"atan2", math_atan}, {"atan2", math_atan},
{"cosh", math_cosh}, {"cosh", math_cosh},
{"sinh", math_sinh}, {"sinh", math_sinh},
{"tanh", math_tanh}, {"tanh", math_tanh},
{"pow", math_pow}, {"pow", math_pow},
{"frexp", math_frexp},
{"ldexp", math_ldexp},
{"log10", math_log10}, {"log10", math_log10},
#endif #endif
/* placeholders */ /* placeholders */

View file

@ -306,16 +306,6 @@ 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] ** return registry.CLIBS[path]
*/ */
@ -330,42 +320,35 @@ static void *checkclib (lua_State *L, const char *path) {
/* /*
** Deallocate function for library strings. ** registry.CLIBS[path] = plib -- for queries
** Unload the DLL associated with the string being deallocated. ** registry.CLIBS[#CLIBS + 1] = plib -- also keep a list of all libraries
*/
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;
}
/*
** Create a library string that, when deallocated, will unload 'plib'
*/
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) { static void addtoclib (lua_State *L, const char *path, void *plib) {
lua_getfield(L, LUA_REGISTRYINDEX, CLIBS); lua_getfield(L, LUA_REGISTRYINDEX, CLIBS);
lua_pushlightuserdata(L, plib); lua_pushlightuserdata(L, plib);
lua_setfield(L, -2, path); /* CLIBS[path] = plib */ lua_pushvalue(L, -1);
createlibstr(L, plib); lua_setfield(L, -3, path); /* CLIBS[path] = plib */
luaL_ref(L, -2); /* keep library string in CLIBS */ lua_rawseti(L, -2, luaL_len(L, -2) + 1); /* CLIBS[#CLIBS + 1] = plib */
lua_pop(L, 1); /* pop CLIBS table */ lua_pop(L, 1); /* pop CLIBS table */
} }
/*
** __gc tag method for CLIBS table: calls 'lsys_unloadlib' for all lib
** handles in list CLIBS
*/
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;
}
/* error codes for 'lookforfunc' */ /* error codes for 'lookforfunc' */
#define ERRLIB 1 #define ERRLIB 1
#define ERRFUNC 2 #define ERRFUNC 2
@ -378,8 +361,8 @@ static void addtoclib (lua_State *L, const char *path, void *plib) {
** Then, if 'sym' is '*', return true (as library has been loaded). ** Then, if 'sym' is '*', return true (as library has been loaded).
** Otherwise, look for symbol 'sym' in the library and push a ** Otherwise, look for symbol 'sym' in the library and push a
** C function with that symbol. ** C function with that symbol.
** Return 0 with 'true' or a function in the stack; in case of ** Return 0 and 'true' or a function in the stack; in case of
** errors, return an error code with an error message in the stack. ** errors, return an error code and an error message in the stack.
*/ */
static int lookforfunc (lua_State *L, const char *path, const char *sym) { static int lookforfunc (lua_State *L, const char *path, const char *sym) {
void *reg = checkclib(L, path); /* check loaded C libraries */ void *reg = checkclib(L, path); /* check loaded C libraries */
@ -541,7 +524,7 @@ static int searcher_Lua (lua_State *L) {
const char *name = luaL_checkstring(L, 1); const char *name = luaL_checkstring(L, 1);
filename = findfile(L, name, "path", LUA_LSUBSEP); filename = findfile(L, name, "path", LUA_LSUBSEP);
if (filename == NULL) return 1; /* module not found in this path */ if (filename == NULL) return 1; /* module not found in this path */
return checkload(L, (luaL_loadfilex(L, filename, "bt") == LUA_OK), filename); return checkload(L, (luaL_loadfile(L, filename) == LUA_OK), filename);
} }
@ -721,9 +704,21 @@ static void createsearcherstable (lua_State *L) {
} }
LUAMOD_API int luaopen_package (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 */ luaL_getsubtable(L, LUA_REGISTRYINDEX, CLIBS); /* create CLIBS table */
lua_pop(L, 1); /* will not use it now */ 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_newlib(L, pk_funcs); /* create 'package' table */ luaL_newlib(L, pk_funcs); /* create 'package' table */
createsearcherstable(L); createsearcherstable(L);
/* set paths */ /* set paths */

View file

@ -31,8 +31,7 @@
/* /*
** Computes ceil(log2(x)), which is the smallest integer n such that ** Computes ceil(log2(x))
** x <= (1 << n).
*/ */
lu_byte luaO_ceillog2 (unsigned int x) { lu_byte luaO_ceillog2 (unsigned int x) {
static const lu_byte log_2[256] = { /* log_2[i - 1] = ceil(log2(i)) */ static const lu_byte log_2[256] = { /* log_2[i - 1] = ceil(log2(i)) */
@ -87,9 +86,8 @@ lu_byte luaO_codeparam (unsigned int p) {
** overflow, so we check which order is best. ** overflow, so we check which order is best.
*/ */
l_mem luaO_applyparam (lu_byte p, l_mem x) { l_mem luaO_applyparam (lu_byte p, l_mem x) {
int m = p & 0xF; /* mantissa */ unsigned int m = p & 0xF; /* mantissa */
int e = (p >> 4); /* exponent */ int e = (p >> 4); /* exponent */
lua_assert(x >= 0);
if (e > 0) { /* normalized? */ if (e > 0) { /* normalized? */
e--; /* correct exponent */ e--; /* correct exponent */
m += 0x10; /* correct mantissa; maximum value is 0x1F */ m += 0x10; /* correct mantissa; maximum value is 0x1F */
@ -387,7 +385,7 @@ size_t luaO_str2num (const char *s, TValue *o) {
int luaO_utf8esc (char *buff, l_uint32 x) { int luaO_utf8esc (char *buff, l_uint32 x) {
int n = 1; /* number of bytes put in buffer (backwards) */ int n = 1; /* number of bytes put in buffer (backwards) */
lua_assert(x <= 0x7FFFFFFFu); lua_assert(x <= 0x7FFFFFFFu);
if (x < 0x80) /* ASCII? */ if (x < 0x80) /* ascii? */
buff[UTF8BUFFSZ - 1] = cast_char(x); buff[UTF8BUFFSZ - 1] = cast_char(x);
else { /* need continuation bytes */ else { /* need continuation bytes */
unsigned int mfb = 0x3f; /* maximum that fits in first byte */ unsigned int mfb = 0x3f; /* maximum that fits in first byte */

View file

@ -208,8 +208,7 @@ typedef union {
#define ttisstrictnil(o) checktag((o), LUA_VNIL) #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) #define isabstkey(v) checktag((v), LUA_VABSTKEY)
@ -419,7 +418,6 @@ typedef struct TString {
#define strisshr(ts) ((ts)->shrlen >= 0) #define strisshr(ts) ((ts)->shrlen >= 0)
#define isextstr(ts) (ttislngstring(ts) && tsvalue(ts)->shrlen != LSTRREG)
/* /*
@ -584,18 +582,9 @@ typedef struct AbsLineInfo {
/* /*
** Flags in Prototypes ** Flags in Prototypes
*/ */
#define PF_VAHID 1 /* function has hidden vararg arguments */ #define PF_ISVARARG 1
#define PF_VATAB 2 /* function has vararg table */ #define PF_FIXED 2 /* prototype has parts in fixed memory */
#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 ** Function Prototypes
@ -827,7 +816,7 @@ typedef struct Table {
#define twoto(x) (1u<<(x)) #define twoto(x) (1u<<(x))
#define sizenode(t) cast_uint(twoto((t)->lsizenode)) #define sizenode(t) (twoto((t)->lsizenode))
/* size of buffer for 'luaO_utf8esc' function */ /* size of buffer for 'luaO_utf8esc' function */

View file

@ -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_BANDK */
,opmode(0, 0, 0, 0, 1, iABC) /* OP_BORK */ ,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_BXORK */
,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_SHRI */
,opmode(0, 0, 0, 0, 1, iABC) /* OP_SHLI */
,opmode(0, 0, 0, 0, 1, iABC) /* OP_ADD */ ,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_SUB */
,opmode(0, 0, 0, 0, 1, iABC) /* OP_MUL */ ,opmode(0, 0, 0, 0, 1, iABC) /* OP_MUL */
@ -102,28 +102,35 @@ LUAI_DDEF const lu_byte luaP_opmodes[NUM_OPCODES] = {
,opmode(0, 0, 1, 0, 0, ivABC) /* OP_SETLIST */ ,opmode(0, 0, 1, 0, 0, ivABC) /* OP_SETLIST */
,opmode(0, 0, 0, 0, 1, iABx) /* OP_CLOSURE */ ,opmode(0, 0, 0, 0, 1, iABx) /* OP_CLOSURE */
,opmode(0, 1, 0, 0, 1, iABC) /* OP_VARARG */ ,opmode(0, 1, 0, 0, 1, iABC) /* OP_VARARG */
,opmode(0, 0, 0, 0, 1, iABC) /* OP_GETVARG */ ,opmode(0, 0, 1, 0, 1, iABC) /* OP_VARARGPREP */
,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 */ ,opmode(0, 0, 0, 0, 0, iAx) /* OP_EXTRAARG */
}; };
#define testITMode(m) (luaP_opmodes[m] & (1 << 5))
/*
** Check whether instruction sets top for next instruction, that is,
** it results in multiple values.
*/
int luaP_isOT (Instruction i) {
OpCode op = GET_OPCODE(i);
switch (op) {
case OP_TAILCALL: return 1;
default:
return testOTMode(op) && GETARG_C(i) == 0;
}
}
/* /*
** Check whether instruction uses top. That happens for OP_VARARGPREP ** Check whether instruction uses top from previous instruction, that is,
** and for instructions that use multiple values set by the previous ** it accepts multiple results.
** instruction.
*/ */
int luaP_isIT (Instruction i) { int luaP_isIT (Instruction i) {
OpCode op = GET_OPCODE(i); OpCode op = GET_OPCODE(i);
switch (op) { switch (op) {
case OP_SETLIST: case OP_SETLIST:
return GETARG_vB(i) == 0; return testITMode(GET_OPCODE(i)) && GETARG_vB(i) == 0;
case OP_VARARGPREP:
return 1;
default: default:
return testITMode(GET_OPCODE(i)) && GETARG_B(i) == 0; return testITMode(GET_OPCODE(i)) && GETARG_B(i) == 0;
} }

View file

@ -224,8 +224,8 @@ enum OpMode {iABC, ivABC, iABx, iAsBx, iAx, isJ};
/* /*
** Grep "ORDER OP" if you change this enum. ** Grep "ORDER OP" if you change these enums. Opcodes marked with a (*)
** See "Notes" below for more information about some instructions. ** has extra descriptions in the notes after the enumeration.
*/ */
typedef enum { typedef enum {
@ -238,7 +238,7 @@ OP_LOADF,/* A sBx R[A] := (lua_Number)sBx */
OP_LOADK,/* A Bx R[A] := K[Bx] */ OP_LOADK,/* A Bx R[A] := K[Bx] */
OP_LOADKX,/* A R[A] := K[extra arg] */ OP_LOADKX,/* A R[A] := K[extra arg] */
OP_LOADFALSE,/* A R[A] := false */ OP_LOADFALSE,/* A R[A] := false */
OP_LFALSESKIP,/*A R[A] := false; pc++ */ OP_LFALSESKIP,/*A R[A] := false; pc++ (*) */
OP_LOADTRUE,/* A R[A] := true */ OP_LOADTRUE,/* A R[A] := true */
OP_LOADNIL,/* A B R[A], R[A+1], ..., R[A+B] := nil */ OP_LOADNIL,/* A B R[A], R[A+1], ..., R[A+B] := nil */
OP_GETUPVAL,/* A B R[A] := UpValue[B] */ OP_GETUPVAL,/* A B R[A] := UpValue[B] */
@ -272,8 +272,8 @@ OP_BANDK,/* A B C R[A] := R[B] & K[C]:integer */
OP_BORK,/* A B C R[A] := R[B] | K[C]:integer */ OP_BORK,/* A B C R[A] := R[B] | K[C]:integer */
OP_BXORK,/* A B C R[A] := R[B] ~ K[C]:integer */ OP_BXORK,/* A B C R[A] := R[B] ~ K[C]:integer */
OP_SHLI,/* A B sC R[A] := sC << R[B] */
OP_SHRI,/* A B sC R[A] := R[B] >> sC */ OP_SHRI,/* A B sC R[A] := R[B] >> sC */
OP_SHLI,/* A B sC R[A] := sC << R[B] */
OP_ADD,/* A B C R[A] := R[B] + R[C] */ OP_ADD,/* A B C R[A] := R[B] + R[C] */
OP_SUB,/* 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_SHL,/* A B C R[A] := R[B] << R[C] */
OP_SHR,/* 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_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] */ 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_GEI,/* A sB k if ((R[A] >= sB) ~= k) then pc++ */
OP_TEST,/* A k if (not R[A] == 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_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_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] */ OP_RETURN,/* A B C k return R[A], ... ,R[A+B-2] (see note) */
OP_RETURN0,/* return */ OP_RETURN0,/* return */
OP_RETURN1,/* A return R[A] */ OP_RETURN1,/* A return R[A] */
@ -336,13 +336,9 @@ 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_CLOSURE,/* A Bx R[A] := closure(KPROTO[Bx]) */
OP_VARARG,/* A B C k R[A], ..., R[A+C-2] = varargs */ OP_VARARG,/* A C R[A], R[A+1], ..., R[A+C-2] = vararg */
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 */ OP_EXTRAARG/* Ax extra (larger) argument for previous opcode */
} OpCode; } OpCode;
@ -371,8 +367,7 @@ OP_EXTRAARG/* Ax extra (larger) argument for previous opcode */
OP_RETURN*, OP_SETLIST) may use 'top'. OP_RETURN*, OP_SETLIST) may use 'top'.
(*) In OP_VARARG, if (C == 0) then use actual number of varargs and (*) In OP_VARARG, if (C == 0) then use actual number of varargs and
set top (like in OP_CALL with C == 0). 'k' means function has a set top (like in OP_CALL with C == 0).
vararg table, which is in R[B].
(*) In OP_RETURN, if (B == 0) then return up to 'top'. (*) In OP_RETURN, if (B == 0) then return up to 'top'.
@ -387,23 +382,18 @@ OP_EXTRAARG/* Ax extra (larger) argument for previous opcode */
power of 2) plus 1, or zero for size zero. If not k, the array size power of 2) plus 1, or zero for size zero. If not k, the array size
is vC. Otherwise, the array size is EXTRAARG _ vC. 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 (*) For comparisons, k specifies what condition the test should accept
(true or false). (true or false).
(*) In OP_MMBINI/OP_MMBINK, k means the arguments were flipped (*) In OP_MMBINI/OP_MMBINK, k means the arguments were flipped
(the constant is the first operand). (the constant is the first operand).
(*) All comparison and test instructions assume that the instruction (*) All 'skips' (pc++) assume that next instruction is a jump.
being skipped (pc++) is a jump.
(*) In instructions OP_RETURN/OP_TAILCALL, 'k' specifies that the (*) In instructions OP_RETURN/OP_TAILCALL, 'k' specifies that the
function builds upvalues, which may need to be closed. C > 0 means function builds upvalues, which may need to be closed. C > 0 means
the function has hidden vararg arguments, so that its 'func' must be the function is vararg, so that its 'func' must be corrected before
corrected before returning; in this case, (C - 1) is its number of returning; in this case, (C - 1) is its number of fixed parameters.
fixed parameters.
(*) In comparisons with an immediate operand, C signals whether the (*) In comparisons with an immediate operand, C signals whether the
original operand was a float. (It must be corrected in case of original operand was a float. (It must be corrected in case of
@ -417,8 +407,8 @@ OP_EXTRAARG/* Ax extra (larger) argument for previous opcode */
** bits 0-2: op mode ** bits 0-2: op mode
** bit 3: instruction set register A ** bit 3: instruction set register A
** bit 4: operator is a test (next instruction must be a jump) ** bit 4: operator is a test (next instruction must be a jump)
** bit 5: used by 'luaP_isIT' ** bit 5: instruction uses 'L->top' set by previous instruction (when B == 0)
** bit 6: used by 'luaP_isOT' ** bit 6: instruction sets 'L->top' for next instruction (when C == 0)
** bit 7: instruction is an MM instruction (call a metamethod) ** bit 7: instruction is an MM instruction (call a metamethod)
*/ */
@ -427,17 +417,12 @@ LUAI_DDEC(const lu_byte luaP_opmodes[NUM_OPCODES];)
#define getOpMode(m) (cast(enum OpMode, luaP_opmodes[m] & 7)) #define getOpMode(m) (cast(enum OpMode, luaP_opmodes[m] & 7))
#define testAMode(m) (luaP_opmodes[m] & (1 << 3)) #define testAMode(m) (luaP_opmodes[m] & (1 << 3))
#define testTMode(m) (luaP_opmodes[m] & (1 << 4)) #define testTMode(m) (luaP_opmodes[m] & (1 << 4))
#define testITMode(m) (luaP_opmodes[m] & (1 << 5))
#define testOTMode(m) (luaP_opmodes[m] & (1 << 6))
#define testMMMode(m) (luaP_opmodes[m] & (1 << 7)) #define testMMMode(m) (luaP_opmodes[m] & (1 << 7))
/* Check whether instruction sets top for next instruction, that is, LUAI_FUNC int luaP_isOT (Instruction i);
** it results in multiple values. Used only for tests.
*/
#define luaP_isOT(i) \
(GET_OPCODE(i) == OP_TAILCALL || \
((luaP_opmodes[GET_OPCODE(i)] & (1 << 6)) && GETARG_C(i) == 0))
LUAI_FUNC int luaP_isIT (Instruction i); LUAI_FUNC int luaP_isIT (Instruction i);

View file

@ -45,8 +45,8 @@ static const char *const opnames[] = {
"BANDK", "BANDK",
"BORK", "BORK",
"BXORK", "BXORK",
"SHLI",
"SHRI", "SHRI",
"SHLI",
"ADD", "ADD",
"SUB", "SUB",
"MUL", "MUL",
@ -94,8 +94,6 @@ static const char *const opnames[] = {
"SETLIST", "SETLIST",
"CLOSURE", "CLOSURE",
"VARARG", "VARARG",
"GETVARG",
"ERRNNIL",
"VARARGPREP", "VARARGPREP",
"EXTRAARG", "EXTRAARG",
NULL NULL

View file

@ -34,7 +34,7 @@
#if defined(LUA_USE_WINDOWS) #if defined(LUA_USE_WINDOWS)
#define LUA_STRFTIMEOPTIONS "aAbBcdHIjmMpSUwWxXyYzZ%" \ #define LUA_STRFTIMEOPTIONS "aAbBcdHIjmMpSUwWxXyYzZ%" \
"||" "#c#x#d#H#I#j#m#M#S#U#w#W#y#Y" /* two-char options */ "||" "#c#x#d#H#I#j#m#M#S#U#w#W#y#Y" /* two-char options */
#elif defined(LUA_USE_C89) /* C89 (only 1-char options) */ #elif defined(LUA_USE_C89) /* ANSI C 89 (only 1-char options) */
#define LUA_STRFTIMEOPTIONS "aAbBcdHIjmMpSUwWxXyYZ%" #define LUA_STRFTIMEOPTIONS "aAbBcdHIjmMpSUwWxXyYZ%"
#else /* C99 specification */ #else /* C99 specification */
#define LUA_STRFTIMEOPTIONS "aAbBcCdDeFgGhHIjmMnprRStTuUVwWxXyYzZ%" \ #define LUA_STRFTIMEOPTIONS "aAbBcCdDeFgGhHIjmMnprRStTuUVwWxXyYzZ%" \
@ -345,22 +345,11 @@ static int os_date (lua_State *L) {
} }
static int os_time_aux (lua_State *L, time_t t, int err) {
if (err || t != (time_t)(l_timet)t)
return luaL_error(L,
"time result cannot be represented in this installation");
l_pushtime(L, t);
return 1;
}
static int os_time (lua_State *L) { static int os_time (lua_State *L) {
if (lua_isnoneornil(L, 1)) { /* called without args? */ time_t t;
time_t t = time(NULL); /* get current time; error if it is -1 */ if (lua_isnoneornil(L, 1)) /* called without args? */
return os_time_aux(L, t, (t == (time_t)(-1))); t = time(NULL); /* get current time */
}
else { else {
time_t t;
struct tm ts; struct tm ts;
luaL_checktype(L, 1, LUA_TTABLE); luaL_checktype(L, 1, LUA_TTABLE);
lua_settop(L, 1); /* make sure table is at the top */ lua_settop(L, 1); /* make sure table is at the top */
@ -371,12 +360,14 @@ static int os_time (lua_State *L) {
ts.tm_min = getfield(L, "min", 0, 0); ts.tm_min = getfield(L, "min", 0, 0);
ts.tm_sec = getfield(L, "sec", 0, 0); ts.tm_sec = getfield(L, "sec", 0, 0);
ts.tm_isdst = getboolfield(L, "isdst"); ts.tm_isdst = getboolfield(L, "isdst");
ts.tm_wday = -1; /* if call succeeds, it will set this value */
t = mktime(&ts); t = mktime(&ts);
setallfields(L, &ts); /* update fields with normalized values */ setallfields(L, &ts); /* update fields with normalized values */
/* error if tm_wday was not set */
return os_time_aux(L, t, ts.tm_wday == -1);
} }
if (t != (time_t)(l_timet)t || t == (time_t)(-1))
return luaL_error(L,
"time result cannot be represented in this installation");
l_pushtime(L, t);
return 1;
} }
@ -416,17 +407,17 @@ static int os_exit (lua_State *L) {
static const luaL_Reg syslib[] = { static const luaL_Reg syslib[] = {
{"clock", os_clock}, {"clock", os_clock},
{"date", os_date}, {"date", os_date},
{"difftime", os_difftime}, {"difftime", os_difftime},
{"execute", os_execute}, {"execute", os_execute},
{"exit", os_exit}, {"exit", os_exit},
{"getenv", os_getenv}, {"getenv", os_getenv},
{"remove", os_remove}, {"remove", os_remove},
{"rename", os_rename}, {"rename", os_rename},
{"setlocale", os_setlocale}, {"setlocale", os_setlocale},
{"time", os_time}, {"time", os_time},
{"tmpname", os_tmpname}, {"tmpname", os_tmpname},
{NULL, NULL} {NULL, NULL}
}; };

220
lparser.c
View file

@ -30,7 +30,7 @@
/* maximum number of variable declarations per function (must be /* maximum number of variable declarationss per function (must be
smaller than 250, due to the bytecode format) */ smaller than 250, due to the bytecode format) */
#define MAXVARS 200 #define MAXVARS 200
@ -197,7 +197,7 @@ static int new_varkind (LexState *ls, TString *name, lu_byte kind) {
Dyndata *dyd = ls->dyd; Dyndata *dyd = ls->dyd;
Vardesc *var; Vardesc *var;
luaM_growvector(L, dyd->actvar.arr, dyd->actvar.n + 1, luaM_growvector(L, dyd->actvar.arr, dyd->actvar.n + 1,
dyd->actvar.size, Vardesc, SHRT_MAX, "variable declarations"); dyd->actvar.size, Vardesc, SHRT_MAX, "variable declarationss");
var = &dyd->actvar.arr[dyd->actvar.n++]; var = &dyd->actvar.arr[dyd->actvar.n++];
var->vd.kind = kind; /* default */ var->vd.kind = kind; /* default */
var->vd.name = name; var->vd.name = name;
@ -279,9 +279,7 @@ static void init_var (FuncState *fs, expdesc *e, int vidx) {
/* /*
** Raises an error if variable described by 'e' is read only; moreover, ** Raises an error if variable described by 'e' is read only
** 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) { static void check_readonly (LexState *ls, expdesc *e) {
FuncState *fs = ls->fs; FuncState *fs = ls->fs;
@ -291,7 +289,7 @@ static void check_readonly (LexState *ls, expdesc *e) {
varname = ls->dyd->actvar.arr[e->u.info].vd.name; varname = ls->dyd->actvar.arr[e->u.info].vd.name;
break; break;
} }
case VLOCAL: case VVARGVAR: { case VLOCAL: {
Vardesc *vardesc = getlocalvardesc(fs, e->u.var.vidx); Vardesc *vardesc = getlocalvardesc(fs, e->u.var.vidx);
if (vardesc->vd.kind != VDKREG) /* not a regular variable? */ if (vardesc->vd.kind != VDKREG) /* not a regular variable? */
varname = vardesc->vd.name; varname = vardesc->vd.name;
@ -303,10 +301,6 @@ static void check_readonly (LexState *ls, expdesc *e) {
varname = up->name; varname = up->name;
break; break;
} }
case VVARGIND: {
needvatab(fs->f); /* function will need a vararg table */
e->k = VINDEXED;
} /* FALLTHROUGH */
case VINDEXUP: case VINDEXSTR: case VINDEXED: { /* global variable */ case VINDEXUP: case VINDEXSTR: case VINDEXED: { /* global variable */
if (e->u.ind.ro) /* read-only? */ if (e->u.ind.ro) /* read-only? */
varname = tsvalue(&fs->f->k[e->u.ind.keystr]); varname = tsvalue(&fs->f->k[e->u.ind.keystr]);
@ -432,11 +426,8 @@ static int searchvar (FuncState *fs, TString *n, expdesc *var) {
else if (eqstr(n, vd->vd.name)) { /* found? */ else if (eqstr(n, vd->vd.name)) { /* found? */
if (vd->vd.kind == RDKCTC) /* compile-time constant? */ if (vd->vd.kind == RDKCTC) /* compile-time constant? */
init_exp(var, VCONST, fs->firstlocal + i); init_exp(var, VCONST, fs->firstlocal + i);
else { /* local variable */ else /* local variable */
init_var(fs, var, i); init_var(fs, var, i);
if (vd->vd.kind == RDKVAVAR) /* vararg parameter? */
var->k = VVARGVAR;
}
return cast_int(var->k); return cast_int(var->k);
} }
} }
@ -476,13 +467,8 @@ static void marktobeclosed (FuncState *fs) {
static void singlevaraux (FuncState *fs, TString *n, expdesc *var, int base) { static void singlevaraux (FuncState *fs, TString *n, expdesc *var, int base) {
int v = searchvar(fs, n, var); /* look up variables at current level */ int v = searchvar(fs, n, var); /* look up variables at current level */
if (v >= 0) { /* found? */ if (v >= 0) { /* found? */
if (!base) { if (v == VLOCAL && !base)
if (var->k == VVARGVAR) /* vararg parameter? */ markupval(fs, var->u.var.vidx); /* local will be used as an upval */
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 nothing else to be done */
} }
else { /* not found at current level; try upvalues */ else { /* not found at current level; try upvalues */
int idx = searchupvalue(fs, n); /* try existing upvalues */ int idx = searchupvalue(fs, n); /* try existing upvalues */
@ -499,20 +485,6 @@ static void singlevaraux (FuncState *fs, TString *n, expdesc *var, int base) {
} }
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 ** Find a variable with the given name 'n', handling global variables
** too. ** too.
@ -522,11 +494,18 @@ static void buildvar (LexState *ls, TString *varname, expdesc *var) {
init_exp(var, VGLOBAL, -1); /* global by default */ init_exp(var, VGLOBAL, -1); /* global by default */
singlevaraux(fs, varname, var, 1); singlevaraux(fs, varname, var, 1);
if (var->k == VGLOBAL) { /* global name? */ if (var->k == VGLOBAL) { /* global name? */
expdesc key;
int info = var->u.info; int info = var->u.info;
/* global by default in the scope of a global declaration? */ /* global by default in the scope of a global declaration? */
if (info == -2) if (info == -2)
luaK_semerror(ls, "variable '%s' not declared", getstr(varname)); luaK_semerror(ls, "variable '%s' not declared", getstr(varname));
buildglobal(ls, varname, var); singlevaraux(fs, ls->envn, var, 1); /* get environment variable */
if (var->k == VGLOBAL)
luaK_semerror(ls, "_ENV is global when accessing variable '%s'",
getstr(varname));
luaK_exp2anyregup(fs, var); /* but could be a constant */
codestring(&key, varname); /* key is variable name */
luaK_indexed(fs, var, &key); /* env[varname] */
if (info != -1 && ls->dyd->actvar.arr[info].vd.kind == GDKCONST) if (info != -1 && ls->dyd->actvar.arr[info].vd.kind == GDKCONST)
var->u.ind.ro = 1; /* mark variable as read-only */ var->u.ind.ro = 1; /* mark variable as read-only */
else /* anyway must be a global */ else /* anyway must be a global */
@ -547,7 +526,6 @@ static void singlevar (LexState *ls, expdesc *var) {
static void adjust_assign (LexState *ls, int nvars, int nexps, expdesc *e) { static void adjust_assign (LexState *ls, int nvars, int nexps, expdesc *e) {
FuncState *fs = ls->fs; FuncState *fs = ls->fs;
int needed = nvars - nexps; /* extra values needed */ int needed = nvars - nexps; /* extra values needed */
luaK_checkstack(fs, needed);
if (hasmultret(e->k)) { /* last expression has multiple returns? */ if (hasmultret(e->k)) { /* last expression has multiple returns? */
int extra = needed + 1; /* discount last expression itself */ int extra = needed + 1; /* discount last expression itself */
if (extra < 0) if (extra < 0)
@ -687,7 +665,7 @@ static void createlabel (LexState *ls, TString *name, int line, int last) {
/* /*
** Traverse the pending gotos of the finishing block checking whether ** Traverse the pending goto's of the finishing block checking whether
** each match some label of that block. Those that do not match are ** each match some label of that block. Those that do not match are
** "exported" to the outer block, to be solved there. In particular, ** "exported" to the outer block, to be solved there. In particular,
** its 'nactvar' is updated with the level of the inner block, ** its 'nactvar' is updated with the level of the inner block,
@ -821,7 +799,8 @@ static void open_func (LexState *ls, FuncState *fs, BlockCnt *bl) {
luaC_objbarrier(L, f, f->source); luaC_objbarrier(L, f, f->source);
f->maxstacksize = 2; /* registers 0/1 are always valid */ f->maxstacksize = 2; /* registers 0/1 are always valid */
fs->kcache = luaH_new(L); /* create table for function */ fs->kcache = luaH_new(L); /* create table for function */
luaD_anchorobj(L, ls->h, obj2gco(fs->kcache)); /* anchor it */ sethvalue2s(L, L->top.p, fs->kcache); /* anchor it */
luaD_inctop(L);
enterblock(fs, bl, 0); enterblock(fs, bl, 0);
} }
@ -830,7 +809,6 @@ static void close_func (LexState *ls) {
lua_State *L = ls->L; lua_State *L = ls->L;
FuncState *fs = ls->fs; FuncState *fs = ls->fs;
Proto *f = fs->f; Proto *f = fs->f;
TValue temp;
luaK_ret(fs, luaY_nvarstack(fs), 0); /* final return */ luaK_ret(fs, luaY_nvarstack(fs), 0); /* final return */
leaveblock(fs); leaveblock(fs);
lua_assert(fs->bl == NULL); lua_assert(fs->bl == NULL);
@ -843,10 +821,8 @@ static void close_func (LexState *ls) {
luaM_shrinkvector(L, f->p, f->sizep, fs->np, Proto *); luaM_shrinkvector(L, f->p, f->sizep, fs->np, Proto *);
luaM_shrinkvector(L, f->locvars, f->sizelocvars, fs->ndebugvars, LocVar); luaM_shrinkvector(L, f->locvars, f->sizelocvars, fs->ndebugvars, LocVar);
luaM_shrinkvector(L, f->upvalues, f->sizeupvalues, fs->nups, Upvaldesc); 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; ls->fs = fs->prev;
L->top.p--; /* pop kcache table */
luaC_checkGC(L); luaC_checkGC(L);
} }
@ -922,19 +898,6 @@ typedef struct ConsControl {
} 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) { static void recfield (LexState *ls, ConsControl *cc) {
/* recfield -> (NAME | '['exp']') = exp */ /* recfield -> (NAME | '['exp']') = exp */
FuncState *fs = ls->fs; FuncState *fs = ls->fs;
@ -955,7 +918,7 @@ static void recfield (LexState *ls, ConsControl *cc) {
static void closelistfield (FuncState *fs, ConsControl *cc) { static void closelistfield (FuncState *fs, ConsControl *cc) {
lua_assert(cc->tostore > 0); if (cc->v.k == VVOID) return; /* there is no list item */
luaK_exp2nextreg(fs, &cc->v); luaK_exp2nextreg(fs, &cc->v);
cc->v.k = VVOID; cc->v.k = VVOID;
if (cc->tostore >= cc->maxtostore) { if (cc->tostore >= cc->maxtostore) {
@ -1043,12 +1006,10 @@ static void constructor (LexState *ls, expdesc *t) {
checknext(ls, '{' /*}*/); checknext(ls, '{' /*}*/);
cc.maxtostore = maxtostore(fs); cc.maxtostore = maxtostore(fs);
do { do {
lua_assert(cc.v.k == VVOID || cc.tostore > 0);
if (ls->t.token == /*{*/ '}') break; if (ls->t.token == /*{*/ '}') break;
if (cc.v.k != VVOID) /* is there a previous list item? */ closelistfield(fs, &cc);
closelistfield(fs, &cc); /* close it */
field(ls, &cc); field(ls, &cc);
luaY_checklimit(fs, cc.tostore + cc.na + cc.nh, MAX_CNST,
"items in a constructor");
} while (testnext(ls, ',') || testnext(ls, ';')); } while (testnext(ls, ',') || testnext(ls, ';'));
check_match(ls, /*{*/ '}', '{' /*}*/, line); check_match(ls, /*{*/ '}', '{' /*}*/, line);
lastlistfield(fs, &cc); lastlistfield(fs, &cc);
@ -1058,9 +1019,9 @@ static void constructor (LexState *ls, expdesc *t) {
/* }====================================================================== */ /* }====================================================================== */
static void setvararg (FuncState *fs) { static void setvararg (FuncState *fs, int nparams) {
fs->f->flag |= PF_VAHID; /* by default, use hidden vararg arguments */ fs->f->flag |= PF_ISVARARG;
luaK_codeABC(fs, OP_VARARGPREP, 0, 0, 0); luaK_codeABC(fs, OP_VARARGPREP, nparams, 0, 0);
} }
@ -1069,7 +1030,7 @@ static void parlist (LexState *ls) {
FuncState *fs = ls->fs; FuncState *fs = ls->fs;
Proto *f = fs->f; Proto *f = fs->f;
int nparams = 0; int nparams = 0;
int varargk = 0; int isvararg = 0;
if (ls->t.token != ')') { /* is 'parlist' not empty? */ if (ls->t.token != ')') { /* is 'parlist' not empty? */
do { do {
switch (ls->t.token) { switch (ls->t.token) {
@ -1079,26 +1040,19 @@ static void parlist (LexState *ls) {
break; break;
} }
case TK_DOTS: { case TK_DOTS: {
varargk = 1; luaX_next(ls);
luaX_next(ls); /* skip '...' */ isvararg = 1;
if (ls->t.token == TK_NAME)
new_varkind(ls, str_checkname(ls), RDKVAVAR);
else
new_localvarliteral(ls, "(vararg table)");
break; break;
} }
default: luaX_syntaxerror(ls, "<name> or '...' expected"); default: luaX_syntaxerror(ls, "<name> or '...' expected");
} }
} while (!varargk && testnext(ls, ',')); } while (!isvararg && testnext(ls, ','));
} }
adjustlocalvars(ls, nparams); adjustlocalvars(ls, nparams);
f->numparams = cast_byte(fs->nactvar); f->numparams = cast_byte(fs->nactvar);
if (varargk) { if (isvararg)
setvararg(fs); /* declared vararg */ setvararg(fs, f->numparams); /* declared vararg */
adjustlocalvars(ls, 1); /* vararg parameter */ luaK_reserveregs(fs, fs->nactvar); /* reserve registers for parameters */
}
/* reserve registers for parameters (plus vararg parameter, if present) */
luaK_reserveregs(fs, fs->nactvar);
} }
@ -1166,7 +1120,6 @@ static void funcargs (LexState *ls, expdesc *f) {
} }
default: { default: {
luaX_syntaxerror(ls, "function arguments expected"); luaX_syntaxerror(ls, "function arguments expected");
return; /* to avoid warnings */
} }
} }
lua_assert(f->k == VNONRELOC); lua_assert(f->k == VNONRELOC);
@ -1286,9 +1239,9 @@ static void simpleexp (LexState *ls, expdesc *v) {
} }
case TK_DOTS: { /* vararg */ case TK_DOTS: { /* vararg */
FuncState *fs = ls->fs; FuncState *fs = ls->fs;
check_condition(ls, isvararg(fs->f), check_condition(ls, fs->f->flag & PF_ISVARARG,
"cannot use '...' outside a vararg function"); "cannot use '...' outside a vararg function");
init_exp(v, VVARARG, luaK_codeABC(fs, OP_VARARG, 0, fs->f->numparams, 1)); init_exp(v, VVARARG, luaK_codeABC(fs, OP_VARARG, 0, 0, 1));
break; break;
} }
case '{' /*}*/: { /* constructor */ case '{' /*}*/: { /* constructor */
@ -1482,15 +1435,6 @@ 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" ** Parse and compile a multiple assignment. The first "variable"
** (a 'suffixedexp') was already read by the caller. ** (a 'suffixedexp') was already read by the caller.
@ -1524,7 +1468,8 @@ static void restassign (LexState *ls, struct LHS_assign *lh, int nvars) {
return; /* avoid default */ return; /* avoid default */
} }
} }
storevartop(ls->fs, &lh->v); /* default assignment */ init_exp(&e, VNONRELOC, ls->fs->freereg-1); /* default assignment */
luaK_storevar(ls->fs, &lh->v, &e);
} }
@ -1614,6 +1559,7 @@ static void repeatstat (LexState *ls, int line) {
statlist(ls); statlist(ls);
check_match(ls, TK_UNTIL, TK_REPEAT, line); check_match(ls, TK_UNTIL, TK_REPEAT, line);
condexit = cond(ls); /* read condition (inside scope block) */ condexit = cond(ls); /* read condition (inside scope block) */
leaveblock(fs); /* finish scope */
if (bl2.upval) { /* upvalues? */ if (bl2.upval) { /* upvalues? */
int exit = luaK_jump(fs); /* normal exit must jump over fix */ int exit = luaK_jump(fs); /* normal exit must jump over fix */
luaK_patchtohere(fs, condexit); /* repetition must close upvalues */ luaK_patchtohere(fs, condexit); /* repetition must close upvalues */
@ -1622,7 +1568,6 @@ static void repeatstat (LexState *ls, int line) {
luaK_patchtohere(fs, exit); /* normal exit comes to here */ luaK_patchtohere(fs, exit); /* normal exit comes to here */
} }
luaK_patchlist(fs, condexit, repeat_init); /* close the loop */ luaK_patchlist(fs, condexit, repeat_init); /* close the loop */
leaveblock(fs); /* finish scope */
leaveblock(fs); /* finish loop */ leaveblock(fs); /* finish loop */
} }
@ -1867,7 +1812,7 @@ static lu_byte getglobalattribute (LexState *ls, lu_byte df) {
switch (kind) { switch (kind) {
case RDKTOCLOSE: case RDKTOCLOSE:
luaK_semerror(ls, "global variables cannot be to-be-closed"); luaK_semerror(ls, "global variables cannot be to-be-closed");
return kind; /* to avoid warnings */ break; /* to avoid warnings */
case RDKCONST: case RDKCONST:
return GDKCONST; /* adjust kind for global variable */ return GDKCONST; /* adjust kind for global variable */
default: default:
@ -1876,74 +1821,25 @@ static lu_byte getglobalattribute (LexState *ls, lu_byte df) {
} }
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) { static void globalstat (LexState *ls) {
/* globalstat -> (GLOBAL) attrib '*' /* globalstat -> (GLOBAL) attrib '*'
globalstat -> (GLOBAL) attrib NAME attrib {',' NAME attrib} */ globalstat -> (GLOBAL) attrib NAME attrib {',' NAME attrib} */
FuncState *fs = ls->fs; FuncState *fs = ls->fs;
/* get prefixed attribute (if any); default is regular global variable */ /* get prefixed attribute (if any); default is regular global variable */
lu_byte defkind = getglobalattribute(ls, GDKREG); lu_byte defkind = getglobalattribute(ls, GDKREG);
if (!testnext(ls, '*')) if (testnext(ls, '*')) {
globalnames(ls, defkind);
else {
/* use NULL as name to represent '*' entries */ /* use NULL as name to represent '*' entries */
new_varkind(ls, NULL, defkind); new_varkind(ls, NULL, defkind);
fs->nactvar++; /* activate declaration */ fs->nactvar++; /* activate declaration */
} }
else {
do { /* list of names */
TString *vname = str_checkname(ls);
lu_byte kind = getglobalattribute(ls, defkind);
new_varkind(ls, vname, kind);
fs->nactvar++; /* activate declaration */
} while (testnext(ls, ','));
}
} }
@ -1954,9 +1850,8 @@ static void globalfunc (LexState *ls, int line) {
TString *fname = str_checkname(ls); TString *fname = str_checkname(ls);
new_varkind(ls, fname, GDKREG); /* declare global variable */ new_varkind(ls, fname, GDKREG); /* declare global variable */
fs->nactvar++; /* enter its scope */ fs->nactvar++; /* enter its scope */
buildglobal(ls, fname, &var); buildvar(ls, fname, &var);
body(ls, &b, 0, ls->linenumber); /* compile and return closure in 'b' */ body(ls, &b, 0, ls->linenumber); /* compile and return closure in 'b' */
checkglobal(ls, fname, line);
luaK_storevar(fs, &var, &b); luaK_storevar(fs, &var, &b);
luaK_fixline(fs, line); /* definition "happens" in the first line */ luaK_fixline(fs, line); /* definition "happens" in the first line */
} }
@ -2114,7 +2009,7 @@ static void statement (LexState *ls) {
gotostat(ls, line); gotostat(ls, line);
break; break;
} }
#if LUA_COMPAT_GLOBAL #if defined(LUA_COMPAT_GLOBAL)
case TK_NAME: { case TK_NAME: {
/* compatibility code to parse global keyword when "global" /* compatibility code to parse global keyword when "global"
is not reserved */ is not reserved */
@ -2154,7 +2049,7 @@ static void mainfunc (LexState *ls, FuncState *fs) {
BlockCnt bl; BlockCnt bl;
Upvaldesc *env; Upvaldesc *env;
open_func(ls, fs, &bl); open_func(ls, fs, &bl);
setvararg(fs); /* main function is always vararg */ setvararg(fs, 0); /* main function is always declared vararg */
env = allocupvalue(fs); /* ...set environment upvalue */ env = allocupvalue(fs); /* ...set environment upvalue */
env->instack = 1; env->instack = 1;
env->idx = 0; env->idx = 0;
@ -2168,14 +2063,16 @@ static void mainfunc (LexState *ls, FuncState *fs) {
} }
LClosure *luaY_parser (lua_State *L, ZIO *z, Table *anchor, Mbuffer *buff, LClosure *luaY_parser (lua_State *L, ZIO *z, Mbuffer *buff,
Dyndata *dyd, const char *name, int firstchar) { Dyndata *dyd, const char *name, int firstchar) {
LexState lexstate; LexState lexstate;
FuncState funcstate; FuncState funcstate;
LClosure *cl; LClosure *cl = luaF_newLclosure(L, 1); /* create main closure */
lexstate.h = anchor; /* table for scanner */ setclLvalue2s(L, L->top.p, cl); /* anchor it (to avoid being collected) */
cl = luaF_newLclosure(L, 1); /* create main closure */ luaD_inctop(L);
luaD_anchorobj(L, anchor, obj2gco(cl)); /* anchor it in scanner table */ lexstate.h = luaH_new(L); /* create table for scanner */
sethvalue2s(L, L->top.p, lexstate.h); /* anchor it */
luaD_inctop(L);
funcstate.f = cl->p = luaF_newproto(L); funcstate.f = cl->p = luaF_newproto(L);
luaC_objbarrier(L, cl, cl->p); luaC_objbarrier(L, cl, cl->p);
funcstate.f->source = luaS_new(L, name); /* create and anchor TString */ funcstate.f->source = luaS_new(L, name); /* create and anchor TString */
@ -2188,6 +2085,7 @@ LClosure *luaY_parser (lua_State *L, ZIO *z, Table *anchor, Mbuffer *buff,
lua_assert(!funcstate.prev && funcstate.nups == 1 && !lexstate.fs); lua_assert(!funcstate.prev && funcstate.nups == 1 && !lexstate.fs);
/* all scopes should be correctly finished */ /* all scopes should be correctly finished */
lua_assert(dyd->actvar.n == 0 && dyd->gt.n == 0 && dyd->label.n == 0); lua_assert(dyd->actvar.n == 0 && dyd->gt.n == 0 && dyd->label.n == 0);
return cl; L->top.p--; /* remove scanner's table */
return cl; /* closure is on the stack, too */
} }

View file

@ -37,8 +37,6 @@ typedef enum {
info = result register */ info = result register */
VLOCAL, /* local variable; var.ridx = register index; VLOCAL, /* local variable; var.ridx = register index;
var.vidx = relative index in 'actvar.arr' */ var.vidx = relative index in 'actvar.arr' */
VVARGVAR, /* vararg parameter; var.ridx = register index;
var.vidx = relative index in 'actvar.arr' */
VGLOBAL, /* global variable; VGLOBAL, /* global variable;
info = relative index in 'actvar.arr' (or -1 for info = relative index in 'actvar.arr' (or -1 for
implicit declaration) */ implicit declaration) */
@ -51,8 +49,6 @@ typedef enum {
ind.ro = true if it represents a read-only global; ind.ro = true if it represents a read-only global;
ind.keystr = if key is a string, index in 'k' of that string; ind.keystr = if key is a string, index in 'k' of that string;
-1 if key is not a string */ -1 if key is not a string */
VVARGIND, /* indexed vararg parameter;
ind.* as in VINDEXED */
VINDEXUP, /* indexed upvalue; VINDEXUP, /* indexed upvalue;
ind.idx = key's K index; ind.idx = key's K index;
ind.* as in VINDEXED */ ind.* as in VINDEXED */
@ -101,11 +97,10 @@ typedef struct expdesc {
/* kinds of variables */ /* kinds of variables */
#define VDKREG 0 /* regular local */ #define VDKREG 0 /* regular local */
#define RDKCONST 1 /* local constant */ #define RDKCONST 1 /* local constant */
#define RDKVAVAR 2 /* vararg parameter */ #define RDKTOCLOSE 2 /* to-be-closed */
#define RDKTOCLOSE 3 /* to-be-closed */ #define RDKCTC 3 /* local compile-time constant */
#define RDKCTC 4 /* local compile-time constant */ #define GDKREG 4 /* regular global */
#define GDKREG 5 /* regular global */ #define GDKCONST 5 /* global constant */
#define GDKCONST 6 /* global constant */
/* variables that live in registers */ /* variables that live in registers */
#define varinreg(v) ((v)->vd.kind <= RDKTOCLOSE) #define varinreg(v) ((v)->vd.kind <= RDKTOCLOSE)
@ -189,9 +184,8 @@ typedef struct FuncState {
LUAI_FUNC lu_byte luaY_nvarstack (FuncState *fs); LUAI_FUNC lu_byte luaY_nvarstack (FuncState *fs);
LUAI_FUNC void luaY_checklimit (FuncState *fs, int v, int l, LUAI_FUNC void luaY_checklimit (FuncState *fs, int v, int l,
const char *what); const char *what);
LUAI_FUNC LClosure *luaY_parser (lua_State *L, ZIO *z, Table *anchor, LUAI_FUNC LClosure *luaY_parser (lua_State *L, ZIO *z, Mbuffer *buff,
Mbuffer *buff, Dyndata *dyd, Dyndata *dyd, const char *name, int firstchar);
const char *name, int firstchar);
#endif #endif

View file

@ -68,19 +68,14 @@ void luaE_setdebt (global_State *g, l_mem debt) {
} }
CallInfo *luaE_extendCI (lua_State *L, int err) { CallInfo *luaE_extendCI (lua_State *L) {
CallInfo *ci; CallInfo *ci;
ci = luaM_reallocvector(L, NULL, 0, 1, CallInfo); lua_assert(L->ci->next == NULL);
if (l_unlikely(ci == NULL)) { /* allocation failed? */ ci = luaM_new(L, CallInfo);
if (err) lua_assert(L->ci->next == NULL);
luaM_error(L); /* raise the error */
return NULL; /* else only report it */
}
ci->next = L->ci->next;
ci->previous = L->ci;
L->ci->next = ci; L->ci->next = ci;
if (ci->next) ci->previous = L->ci;
ci->next->previous = ci; ci->next = NULL;
ci->u.l.trap = 0; ci->u.l.trap = 0;
L->nci++; L->nci++;
return ci; return ci;
@ -151,7 +146,7 @@ LUAI_FUNC void luaE_incCstack (lua_State *L) {
static void resetCI (lua_State *L) { static void resetCI (lua_State *L) {
CallInfo *ci = L->ci = &L->base_ci; CallInfo *ci = L->ci = &L->base_ci;
ci->func.p = L->stack.p; ci->func.p = L->stack.p;
setnilvalue2s(ci->func.p); /* 'function' entry for basic 'ci' */ setnilvalue(s2v(ci->func.p)); /* 'function' entry for basic 'ci' */
ci->top.p = ci->func.p + 1 + LUA_MINSTACK; /* +1 for 'function' entry */ ci->top.p = ci->func.p + 1 + LUA_MINSTACK; /* +1 for 'function' entry */
ci->u.c.k = NULL; ci->u.c.k = NULL;
ci->callstatus = CIST_C; ci->callstatus = CIST_C;
@ -166,7 +161,7 @@ static void stack_init (lua_State *L1, lua_State *L) {
L1->stack.p = luaM_newvector(L, BASIC_STACK_SIZE + EXTRA_STACK, StackValue); L1->stack.p = luaM_newvector(L, BASIC_STACK_SIZE + EXTRA_STACK, StackValue);
L1->tbclist.p = L1->stack.p; L1->tbclist.p = L1->stack.p;
for (i = 0; i < BASIC_STACK_SIZE + EXTRA_STACK; i++) for (i = 0; i < BASIC_STACK_SIZE + EXTRA_STACK; i++)
setnilvalue2s(L1->stack.p + i); /* erase new stack */ setnilvalue(s2v(L1->stack.p + i)); /* erase new stack */
L1->stack_last.p = L1->stack.p + BASIC_STACK_SIZE; L1->stack_last.p = L1->stack.p + BASIC_STACK_SIZE;
/* initialize first ci */ /* initialize first ci */
resetCI(L1); resetCI(L1);

View file

@ -85,7 +85,7 @@ typedef struct CallInfo CallInfo;
** they must be visited again at the end of the cycle), but they are ** they must be visited again at the end of the cycle), but they are
** marked black because assignments to them must activate barriers (to ** marked black because assignments to them must activate barriers (to
** move them back to TOUCHED1). ** move them back to TOUCHED1).
** - Open upvalues are kept gray to avoid barriers, but they stay out ** - Open upvales are kept gray to avoid barriers, but they stay out
** of gray lists. (They don't even have a 'gclist' field.) ** 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) */ /* call is running a C function (still in first 16 bits) */
#define CIST_C (1u << (CIST_RECST + 3)) #define CIST_C (1u << (CIST_RECST + 3))
/* call is on a fresh "luaV_execute" frame */ /* 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 */ /* function is closing tbc variables */
#define CIST_CLSRET (CIST_FRESH << 1) #define CIST_CLSRET (CIST_FRESH << 1)
/* function has tbc variables to close */ /* function has tbc variables to close */
@ -249,6 +249,10 @@ struct CallInfo {
#define CIST_HOOKYIELD (CIST_TAIL << 1) #define CIST_HOOKYIELD (CIST_TAIL << 1)
/* function "called" a finalizer */ /* function "called" a finalizer */
#define CIST_FIN (CIST_HOOKYIELD << 1) #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) #define get_nresults(cs) (cast_int((cs) & CIST_NRESULTS) - 1)
@ -383,7 +387,7 @@ typedef struct global_State {
/* /*
** Union of all collectable objects ** Union of all collectable objects (only for conversions)
** ISO C99, 6.5.2.3 p.5: ** ISO C99, 6.5.2.3 p.5:
** "if a union contains several structures that share a common initial ** "if a union contains several structures that share a common initial
** sequence [...], and if the union object currently contains one ** sequence [...], and if the union object currently contains one
@ -403,32 +407,32 @@ union GCUnion {
}; };
/* macros to convert a GCObject into a specific value /*
** ISO C99, 6.3.2.2 p.7: ** ISO C99, 6.7.2.1 p.14:
** "A pointer to an object or incomplete type may be converted to a ** "A pointer to a union object, suitably converted, points to each of
** pointer to a different object or incomplete type. If the resulting ** its members [...], and vice versa."
** pointer is not correctly aligned for the pointed-to type, the
** behavior is undefined. Otherwise, when converted back again, the
** result shall compare equal to the original pointer."
*/ */
#define gco2(v,T,o) check_exp((o)->tt == v, cast(T*, o)) #define cast_u(o) cast(union GCUnion *, (o))
#define gco2nv(t,T,o) check_exp(novariant((o)->tt) == t, cast(T*, o))
#define gco2ts(o) gco2nv(LUA_TSTRING, TString, o) /* macros to convert a GCObject into a specific value */
#define gco2u(o) gco2(LUA_VUSERDATA, Udata, o) #define gco2ts(o) \
#define gco2lcl(o) (&gco2(LUA_VLCL, Closure, o)->l) check_exp(novariant((o)->tt) == LUA_TSTRING, &((cast_u(o))->ts))
#define gco2ccl(o) (&gco2(LUA_VCCL, Closure, o)->c) #define gco2u(o) check_exp((o)->tt == LUA_VUSERDATA, &((cast_u(o))->u))
#define gco2cl(o) gco2nv(LUA_TFUNCTION, Closure, o) #define gco2lcl(o) check_exp((o)->tt == LUA_VLCL, &((cast_u(o))->cl.l))
#define gco2t(o) gco2(LUA_VTABLE, Table, o) #define gco2ccl(o) check_exp((o)->tt == LUA_VCCL, &((cast_u(o))->cl.c))
#define gco2p(o) gco2(LUA_VPROTO, Proto, o) #define gco2cl(o) \
#define gco2th(o) gco2(LUA_VTHREAD, lua_State, o) check_exp(novariant((o)->tt) == LUA_TFUNCTION, &((cast_u(o))->cl))
#define gco2upv(o) gco2(LUA_VUPVAL, UpVal, o) #define gco2t(o) check_exp((o)->tt == LUA_VTABLE, &((cast_u(o))->h))
#define gco2p(o) check_exp((o)->tt == LUA_VPROTO, &((cast_u(o))->p))
#define gco2th(o) check_exp((o)->tt == LUA_VTHREAD, &((cast_u(o))->th))
#define gco2upv(o) check_exp((o)->tt == LUA_VUPVAL, &((cast_u(o))->upv))
/* /*
** macro to convert a Lua object into a GCObject ** 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) \ #define obj2gco(v) check_exp((v)->tt >= LUA_TSTRING, &(cast_u(v)->gc))
check_exp(novariant((v)->tt) >= LUA_TSTRING, cast(GCObject*, v))
/* actual number of total memory allocated */ /* actual number of total memory allocated */
@ -438,7 +442,7 @@ union GCUnion {
LUAI_FUNC void luaE_setdebt (global_State *g, l_mem debt); LUAI_FUNC void luaE_setdebt (global_State *g, l_mem debt);
LUAI_FUNC void luaE_freethread (lua_State *L, lua_State *L1); LUAI_FUNC void luaE_freethread (lua_State *L, lua_State *L1);
LUAI_FUNC lu_mem luaE_threadsize (lua_State *L); LUAI_FUNC lu_mem luaE_threadsize (lua_State *L);
LUAI_FUNC CallInfo *luaE_extendCI (lua_State *L, int err); LUAI_FUNC CallInfo *luaE_extendCI (lua_State *L);
LUAI_FUNC void luaE_shrinkCI (lua_State *L); LUAI_FUNC void luaE_shrinkCI (lua_State *L);
LUAI_FUNC void luaE_checkcstack (lua_State *L); LUAI_FUNC void luaE_checkcstack (lua_State *L);
LUAI_FUNC void luaE_incCstack (lua_State *L); LUAI_FUNC void luaE_incCstack (lua_State *L);

View file

@ -39,18 +39,18 @@
/* /*
** generic equality for strings ** equality for long strings
*/ */
int luaS_eqstr (TString *a, TString *b) { int luaS_eqlngstr (TString *a, TString *b) {
size_t len1, len2; size_t len = a->u.lnglen;
const char *s1 = getlstr(a, len1); lua_assert(a->tt == LUA_VLNGSTR && b->tt == LUA_VLNGSTR);
const char *s2 = getlstr(b, len2); return (a == b) || /* same instance or... */
return ((len1 == len2) && /* equal length and ... */ ((len == b->u.lnglen) && /* equal length and ... */
(memcmp(s1, s2, len1) == 0)); /* equal contents */ (memcmp(getlngstr(a), getlngstr(b), len) == 0)); /* equal contents */
} }
static unsigned luaS_hash (const char *str, size_t l, unsigned seed) { unsigned luaS_hash (const char *str, size_t l, unsigned seed) {
unsigned int h = seed ^ cast_uint(l); unsigned int h = seed ^ cast_uint(l);
for (; l > 0; l--) for (; l > 0; l--)
h ^= ((h<<5) + (h>>2) + cast_byte(str[l - 1])); h ^= ((h<<5) + (h>>2) + cast_byte(str[l - 1]));
@ -315,9 +315,28 @@ 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, TString *luaS_newextlstr (lua_State *L,
const char *s, size_t len, lua_Alloc falloc, void *ud) { const char *s, size_t len, lua_Alloc falloc, void *ud) {
struct NewExt ne; 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 {
TStatus 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) { if (!falloc) {
ne.kind = LSTRFIX; ne.kind = LSTRFIX;
f_newext(L, &ne); /* just create header */ f_newext(L, &ne); /* just create header */
@ -338,16 +357,3 @@ 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);
}
}

View file

@ -54,8 +54,9 @@
#define eqshrstr(a,b) check_exp((a)->tt == LUA_VSHRSTR, (a) == (b)) #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 unsigned luaS_hashlongstr (TString *ts);
LUAI_FUNC int luaS_eqstr (TString *a, TString *b); LUAI_FUNC int luaS_eqlngstr (TString *a, TString *b);
LUAI_FUNC void luaS_resize (lua_State *L, int newsize); LUAI_FUNC void luaS_resize (lua_State *L, int newsize);
LUAI_FUNC void luaS_clearcache (global_State *g); LUAI_FUNC void luaS_clearcache (global_State *g);
LUAI_FUNC void luaS_init (lua_State *L); LUAI_FUNC void luaS_init (lua_State *L);
@ -68,6 +69,5 @@ LUAI_FUNC TString *luaS_createlngstrobj (lua_State *L, size_t l);
LUAI_FUNC TString *luaS_newextlstr (lua_State *L, LUAI_FUNC TString *luaS_newextlstr (lua_State *L,
const char *s, size_t len, lua_Alloc falloc, void *ud); const char *s, size_t len, lua_Alloc falloc, void *ud);
LUAI_FUNC size_t luaS_sizelngstr (size_t len, int kind); LUAI_FUNC size_t luaS_sizelngstr (size_t len, int kind);
LUAI_FUNC TString *luaS_normstr (lua_State *L, TString *ts);
#endif #endif

View file

@ -132,31 +132,27 @@ 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) { static int str_rep (lua_State *L) {
size_t len, lsep; size_t l, lsep;
const char *s = luaL_checklstring(L, 1, &len); const char *s = luaL_checklstring(L, 1, &l);
lua_Integer n = luaL_checkinteger(L, 2); lua_Integer n = luaL_checkinteger(L, 2);
const char *sep = luaL_optlstring(L, 3, "", &lsep); const char *sep = luaL_optlstring(L, 3, "", &lsep);
if (n <= 0 || (len | lsep) == 0) if (n <= 0)
lua_pushliteral(L, ""); /* no repetitions or both strings empty */ lua_pushliteral(L, "");
else if (l_unlikely(len > MAX_SIZE - lsep || else if (l_unlikely(l + lsep < l || l + lsep > MAX_SIZE / cast_sizet(n)))
cast_st2S(len + lsep) > cast_st2S(MAX_SIZE) / n))
return luaL_error(L, "resulting string too large"); return luaL_error(L, "resulting string too large");
else { else {
size_t totallen = (cast_sizet(n) * (len + lsep)) - lsep; size_t totallen = ((size_t)n * (l + lsep)) - lsep;
luaL_Buffer b; luaL_Buffer b;
char *p = luaL_buffinitsize(L, &b, totallen); char *p = luaL_buffinitsize(L, &b, totallen);
while (n-- > 1) { /* first n-1 copies (followed by separator) */ while (n-- > 1) { /* first n-1 copies (followed by separator) */
memcpy(p, s, len * sizeof(char)); p += len; memcpy(p, s, l * sizeof(char)); p += l;
if (lsep > 0) { /* empty 'memcpy' is not that cheap */ 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, len * sizeof(char)); /* last copy without separator */ memcpy(p, s, l * sizeof(char)); /* last copy (not followed by separator) */
luaL_pushresultsize(&b, totallen); luaL_pushresultsize(&b, totallen);
} }
return 1; return 1;
@ -269,18 +265,11 @@ 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 */ lua_settop(L, 2); /* back to the original arguments */
if (l_unlikely(lua_type(L, 2) == LUA_TSTRING || if (l_unlikely(lua_type(L, 2) == LUA_TSTRING ||
!luaL_getmetafield(L, 2, mtkey))) !luaL_getmetafield(L, 2, mtname)))
luaL_error(L, "attempt to %s a '%s' with a '%s'", opname, luaL_error(L, "attempt to %s a '%s' with a '%s'", mtname + 2,
luaL_typename(L, -2), luaL_typename(L, -1)); luaL_typename(L, -2), luaL_typename(L, -1));
lua_insert(L, -3); /* put metamethod before arguments */ lua_insert(L, -3); /* put metamethod before arguments */
lua_call(L, 2, 1); /* call metamethod */ lua_call(L, 2, 1); /* call metamethod */
@ -291,7 +280,7 @@ static int arith (lua_State *L, int op, const char *mtname) {
if (tonum(L, 1) && tonum(L, 2)) if (tonum(L, 1) && tonum(L, 2))
lua_arith(L, op); /* result will be on the top */ lua_arith(L, op); /* result will be on the top */
else else
trymt(L, mtname, mtname + 2); trymt(L, mtname);
return 1; return 1;
} }
@ -757,25 +746,19 @@ 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, static void prepstate (MatchState *ms, lua_State *L,
const char *s, size_t ls, const char *p, size_t lp) { const char *s, size_t ls, const char *p, size_t lp) {
ms->L = L; ms->L = L;
ms->matchdepth = MAXCCALLS;
ms->src_init = s; ms->src_init = s;
ms->src_end = s + ls; ms->src_end = s + ls;
ms->p_end = p + lp; ms->p_end = p + lp;
} }
/*
** (Re)prepare state for a match, setting fields that change during
** each match.
*/
static void reprepstate (MatchState *ms) { static void reprepstate (MatchState *ms) {
ms->matchdepth = MAXCCALLS;
ms->level = 0; ms->level = 0;
lua_assert(ms->matchdepth == MAXCCALLS);
} }
@ -974,7 +957,7 @@ static int str_gsub (lua_State *L) {
reprepstate(&ms); /* (re)prepare state for new match */ reprepstate(&ms); /* (re)prepare state for new match */
if ((e = match(&ms, src, p)) != NULL && e != lastmatch) { /* match? */ if ((e = match(&ms, src, p)) != NULL && e != lastmatch) { /* match? */
n++; n++;
changed = add_value(&ms, &b, src, e, tr) || changed; changed = add_value(&ms, &b, src, e, tr) | changed;
src = lastmatch = e; src = lastmatch = e;
} }
else if (src < ms.src_end) /* otherwise, skip one character */ else if (src < ms.src_end) /* otherwise, skip one character */
@ -1732,7 +1715,7 @@ static int str_packsize (lua_State *L) {
luaL_argcheck(L, opt != Kstring && opt != Kzstr, 1, luaL_argcheck(L, opt != Kstring && opt != Kzstr, 1,
"variable-length format"); "variable-length format");
size += ntoalign; /* total space used by option */ size += ntoalign; /* total space used by option */
luaL_argcheck(L, totalsize <= MAX_SIZE - size, luaL_argcheck(L, totalsize <= LUA_MAXINTEGER - size,
1, "format result too large"); 1, "format result too large");
totalsize += size; totalsize += size;
} }
@ -1828,8 +1811,8 @@ static int str_unpack (lua_State *L) {
lua_Unsigned len = (lua_Unsigned)unpackint(L, data + pos, lua_Unsigned len = (lua_Unsigned)unpackint(L, data + pos,
h.islittle, cast_int(size), 0); h.islittle, cast_int(size), 0);
luaL_argcheck(L, len <= ld - pos - size, 2, "data string too short"); luaL_argcheck(L, len <= ld - pos - size, 2, "data string too short");
lua_pushlstring(L, data + pos + size, cast_sizet(len)); lua_pushlstring(L, data + pos + size, len);
pos += cast_sizet(len); /* skip string */ pos += len; /* skip string */
break; break;
} }
case Kzstr: { case Kzstr: {

150
ltable.c
View file

@ -156,7 +156,7 @@ static Node *hashint (const Table *t, lua_Integer i) {
** The main computation should be just ** The main computation should be just
** n = frexp(n, &i); return (n * INT_MAX) + i ** n = frexp(n, &i); return (n * INT_MAX) + i
** but there are some numerical subtleties. ** but there are some numerical subtleties.
** In a two-complement representation, INT_MAX may not have an exact ** In a two-complement representation, INT_MAX does not has an exact
** representation as a float, but INT_MIN does; because the absolute ** representation as a float, but INT_MIN does; because the absolute
** value of 'frexp' is smaller than 1 (unless 'n' is inf/NaN), the ** 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 ** absolute value of the product 'frexp * -INT_MIN' is smaller or equal
@ -234,51 +234,41 @@ l_sinline Node *mainpositionfromnode (const Table *t, Node *nd) {
** Check whether key 'k1' is equal to the key in node 'n2'. This ** Check whether key 'k1' is equal to the key in node 'n2'. This
** equality is raw, so there are no metamethods. Floats with integer ** equality is raw, so there are no metamethods. Floats with integer
** values have been normalized, so integers cannot be equal to ** values have been normalized, so integers cannot be equal to
** floats. It is assumed that 'eqshrstr' is simply pointer equality, ** floats. It is assumed that 'eqshrstr' is simply pointer equality, so
** so that short strings are handled in the default case. The flag ** that short strings are handled in the default case.
** 'deadok' means to accept dead keys as equal to their original values. ** A true 'deadok' means to accept dead keys as equal to their original
** (Only collectable objects can produce dead keys.) Note that dead ** values. All dead keys are compared in the default case, by pointer
** long strings are also compared by identity. Once a key is dead, ** identity. (Only collectable objects can produce dead keys.) Note that
** its corresponding value may be collected, and then another value ** dead long strings are also compared by identity.
** can be created with the same address. If this other value is given ** Once a key is dead, its corresponding value may be collected, and
** to 'next', 'equalkey' will signal a false positive. In a regular ** then another value can be created with the same address. If this
** traversal, this situation should never happen, as all keys given to ** other value is given to 'next', 'equalkey' will signal a false
** 'next' came from the table itself, and therefore could not have been ** positive. In a regular traversal, this situation should never happen,
** collected. Outside a regular traversal, we have garbage in, garbage ** as all keys given to 'next' came from the table itself, and therefore
** out. What is relevant is that this false positive does not break ** could not have been collected. Outside a regular traversal, we
** anything. (In particular, 'next' will return some other valid item ** have garbage in, garbage out. What is relevant is that this false
** on the table or nil.) ** 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) { static int equalkey (const TValue *k1, const Node *n2, int deadok) {
if (rawtt(k1) != keytt(n2)) { /* not the same variants? */ if ((rawtt(k1) != keytt(n2)) && /* not the same variants? */
if (keyisshrstr(n2) && ttislngstring(k1)) { !(deadok && keyisdead(n2) && iscollectable(k1)))
/* an external string can be equal to a short-string key */ return 0; /* cannot be same key */
return luaS_eqstr(tsvalue(k1), keystrval(n2)); switch (keytt(n2)) {
} case LUA_VNIL: case LUA_VFALSE: case LUA_VTRUE:
else if (deadok && keyisdead(n2) && iscollectable(k1)) { return 1;
/* a collectable value can be equal to a dead key */ 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:
return gcvalue(k1) == gcvalueraw(keyval(n2)); 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));
}
} }
} }
@ -651,9 +641,10 @@ static void reinserthash (lua_State *L, Table *ot, Table *t) {
/* /*
** Exchange the hash part of 't1' and 't2'. (In 'flags', only the dummy ** Exchange the hash part of 't1' and 't2'. (In 'flags', only the
** bit must be exchanged: The metamethod bits do not change during a ** dummy bit must be exchanged: The 'isrealasize' is not related
** resize, so the "real" table can keep their values.) ** to the hash part, and the metamethod bits do not change during
** a resize, so the "real" table can keep their values.)
*/ */
static void exchangehashpart (Table *t1, Table *t2) { static void exchangehashpart (Table *t1, Table *t2) {
lu_byte lsizenode = t1->lsizenode; lu_byte lsizenode = t1->lsizenode;
@ -1155,28 +1146,19 @@ void luaH_finishset (lua_State *L, Table *t, const TValue *key,
lua_assert(hres != HOK); lua_assert(hres != HOK);
if (hres == HNOTFOUND) { if (hres == HNOTFOUND) {
TValue aux; TValue aux;
const TValue *actk = key; /* actual key to insert */
if (l_unlikely(ttisnil(key))) if (l_unlikely(ttisnil(key)))
luaG_runerror(L, "table index is nil"); luaG_runerror(L, "table index is nil");
else if (ttisfloat(key)) { else if (ttisfloat(key)) {
lua_Number f = fltvalue(key); lua_Number f = fltvalue(key);
lua_Integer k; lua_Integer k;
if (luaV_flttointeger(f, &k, F2Ieq)) { /* is key equal to an integer? */ if (luaV_flttointeger(f, &k, F2Ieq)) {
setivalue(&aux, k); setivalue(&aux, k); /* key is equal to an integer */
actk = &aux; /* use the integer as the key */ key = &aux; /* insert it as an integer */
} }
else if (l_unlikely(luai_numisnan(f))) else if (l_unlikely(luai_numisnan(f)))
luaG_runerror(L, "table index is NaN"); luaG_runerror(L, "table index is NaN");
} }
else if (isextstr(key)) { /* external string? */ luaH_newkey(L, t, key, value);
/* 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? */ else if (hres > 0) { /* regular Node? */
setobj2t(L, gval(gnode(t, hres - HFIRSTNODE)), value); setobj2t(L, gval(gnode(t, hres - HFIRSTNODE)), value);
@ -1220,36 +1202,24 @@ 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 ** Try to find a boundary in the hash part of table 't'. From the
** caller, we know that 'asize + 1' is present. We want to find a larger ** caller, we know that 'j' is zero or present and that 'j + 1' is
** key that is absent from the table, so that we can do a binary search ** present. We want to find a larger key that is absent from the
** between the two keys to find a boundary. We keep doubling 'j' until ** table, so that we can do a binary search between the two keys to
** we get an absent index. If the doubling would overflow, we try ** find a boundary. We keep doubling 'j' until we get an absent index.
** LUA_MAXINTEGER. If it is absent, we are ready for the binary search. ** If the doubling would overflow, we try LUA_MAXINTEGER. If it is
** ('j', being max integer, is larger or equal to 'i', but it cannot be ** absent, we are ready for the binary search. ('j', being max integer,
** equal because it is absent while 'i' is present.) Otherwise, 'j' is a ** is larger or equal to 'i', but it cannot be equal because it is
** boundary. ('j + 1' cannot be a present integer key because it is not ** absent while 'i' is present; so 'j > i'.) Otherwise, 'j' is a
** a valid integer in Lua.) ** boundary. ('j + 1' cannot be a present integer key because it is
** About 'rnd': If we used a fixed algorithm, a bad actor could fill ** not a valid integer in Lua.)
** 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 (lua_State *L, Table *t, unsigned asize) { static lua_Unsigned hash_search (Table *t, lua_Unsigned j) {
lua_Unsigned i = asize + 1; /* caller ensures t[i] is present */ lua_Unsigned i;
unsigned rnd = G(L)->seed; if (j == 0) j++; /* the caller ensures 'j + 1' is present */
int n = (asize > 0) ? luaO_ceillog2(asize) : 0; /* width of 'asize' */ do {
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 */ i = j; /* 'i' is a present index */
if (j <= l_castS2U(LUA_MAXINTEGER)/2 - 1) { if (j <= l_castS2U(LUA_MAXINTEGER) / 2)
j = j*2 + (rnd & 1); /* try again with 2j or 2j+1 */ j *= 2;
rnd >>= 1;
}
else { else {
j = LUA_MAXINTEGER; j = LUA_MAXINTEGER;
if (hashkeyisempty(t, j)) /* t[j] not present? */ if (hashkeyisempty(t, j)) /* t[j] not present? */
@ -1257,7 +1227,7 @@ static lua_Unsigned hash_search (lua_State *L, Table *t, unsigned asize) {
else /* weird case */ else /* weird case */
return j; /* well, max integer is a boundary... */ 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 */ /* i < j && t[i] present && t[j] absent */
while (j - i > 1u) { /* do a binary search between them */ while (j - i > 1u) { /* do a binary search between them */
lua_Unsigned m = (i + j) / 2; lua_Unsigned m = (i + j) / 2;
@ -1298,7 +1268,7 @@ static lua_Unsigned newhint (Table *t, unsigned hint) {
** If there is no array part, or its last element is non empty, the ** If there is no array part, or its last element is non empty, the
** border may be in the hash part. ** border may be in the hash part.
*/ */
lua_Unsigned luaH_getn (lua_State *L, Table *t) { lua_Unsigned luaH_getn (Table *t) {
unsigned asize = t->asize; unsigned asize = t->asize;
if (asize > 0) { /* is there an array part? */ if (asize > 0) { /* is there an array part? */
const unsigned maxvicinity = 4; const unsigned maxvicinity = 4;
@ -1339,7 +1309,7 @@ lua_Unsigned luaH_getn (lua_State *L, Table *t) {
if (isdummy(t) || hashkeyisempty(t, asize + 1)) if (isdummy(t) || hashkeyisempty(t, asize + 1))
return asize; /* 'asize + 1' is empty */ return asize; /* 'asize + 1' is empty */
else /* 'asize + 1' is also non empty */ else /* 'asize + 1' is also non empty */
return hash_search(L, t, asize); return hash_search(t, asize);
} }

View file

@ -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 lu_mem luaH_size (Table *t);
LUAI_FUNC void luaH_free (lua_State *L, 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 int luaH_next (lua_State *L, Table *t, StkId key);
LUAI_FUNC lua_Unsigned luaH_getn (lua_State *L, Table *t); LUAI_FUNC lua_Unsigned luaH_getn (Table *t);
#if defined(LUA_DEBUG) #if defined(LUA_DEBUG)

View file

@ -42,17 +42,15 @@ 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, ** 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) { static void checktab (lua_State *L, int arg, int what) {
int tp = lua_type(L, arg); if (lua_type(L, arg) != LUA_TTABLE) { /* is it not a table? */
if (tp != LUA_TTABLE) { /* is it not a table? */
int n = 1; /* number of elements to pop */ int n = 1; /* number of elements to pop */
if (lua_getmetatable(L, arg) && /* must have metatable */ if (lua_getmetatable(L, arg) && /* must have metatable */
(!(what & TAB_R) || checkfield(L, "__index", ++n)) && (!(what & TAB_R) || checkfield(L, "__index", ++n)) &&
(!(what & TAB_W) || checkfield(L, "__newindex", ++n)) && (!(what & TAB_W) || checkfield(L, "__newindex", ++n)) &&
(!(what & TAB_L) || /* strings don't need '__len' to have a length */ (!(what & TAB_L) || checkfield(L, "__len", ++n))) {
tp == LUA_TSTRING || checkfield(L, "__len", ++n))) {
lua_pop(L, n); /* pop metatable and tested metamethods */ lua_pop(L, n); /* pop metatable and tested metamethods */
} }
else else
@ -206,9 +204,8 @@ static int tpack (lua_State *L) {
static int tunpack (lua_State *L) { static int tunpack (lua_State *L) {
lua_Unsigned n; lua_Unsigned n;
lua_Integer len = aux_getn(L, 1, TAB_R);
lua_Integer i = luaL_optinteger(L, 2, 1); lua_Integer i = luaL_optinteger(L, 2, 1);
lua_Integer e = luaL_opt(L, luaL_checkinteger, 3, len); lua_Integer e = luaL_opt(L, luaL_checkinteger, 3, luaL_len(L, 1));
if (i > e) return 0; /* empty range */ if (i > e) return 0; /* empty range */
n = l_castS2U(e) - l_castS2U(i); /* number of elements minus 1 */ n = l_castS2U(e) - l_castS2U(i); /* number of elements minus 1 */
if (l_unlikely(n >= (unsigned int)INT_MAX || if (l_unlikely(n >= (unsigned int)INT_MAX ||

View file

@ -164,13 +164,13 @@ static void warnf (void *ud, const char *msg, int tocont) {
#define MARK 0x55 /* 01010101 (a nice pattern) */ #define MARK 0x55 /* 01010101 (a nice pattern) */
typedef union memHeader { typedef union Header {
LUAI_MAXALIGN; LUAI_MAXALIGN;
struct { struct {
size_t size; size_t size;
int type; int type;
} d; } d;
} memHeader; } Header;
#if !defined(EXTERNMEMCHECK) #if !defined(EXTERNMEMCHECK)
@ -193,14 +193,14 @@ Memcontrol l_memcontrol =
{0UL, 0UL, 0UL, 0UL, 0UL, 0UL, 0UL, 0UL, 0UL}}; {0UL, 0UL, 0UL, 0UL, 0UL, 0UL, 0UL, 0UL, 0UL}};
static void freeblock (Memcontrol *mc, memHeader *block) { static void freeblock (Memcontrol *mc, Header *block) {
if (block) { if (block) {
size_t size = block->d.size; size_t size = block->d.size;
int i; int i;
for (i = 0; i < MARKSIZE; i++) /* check marks after block */ for (i = 0; i < MARKSIZE; i++) /* check marks after block */
lua_assert(*(cast_charp(block + 1) + size + i) == MARK); lua_assert(*(cast_charp(block + 1) + size + i) == MARK);
mc->objcount[block->d.type]--; mc->objcount[block->d.type]--;
fillmem(block, sizeof(memHeader) + size + MARKSIZE); /* erase block */ fillmem(block, sizeof(Header) + size + MARKSIZE); /* erase block */
free(block); /* actually free block */ free(block); /* actually free block */
mc->numblocks--; /* update counts */ mc->numblocks--; /* update counts */
mc->total -= size; mc->total -= size;
@ -210,7 +210,7 @@ static void freeblock (Memcontrol *mc, memHeader *block) {
void *debug_realloc (void *ud, void *b, size_t oldsize, size_t size) { void *debug_realloc (void *ud, void *b, size_t oldsize, size_t size) {
Memcontrol *mc = cast(Memcontrol *, ud); Memcontrol *mc = cast(Memcontrol *, ud);
memHeader *block = cast(memHeader *, b); Header *block = cast(Header *, b);
int type; int type;
if (mc->memlimit == 0) { /* first time? */ if (mc->memlimit == 0) { /* first time? */
char *limit = getenv("MEMLIMIT"); /* initialize memory limit */ 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) if (size > oldsize && mc->total+size-oldsize > mc->memlimit)
return NULL; /* fake a memory allocation error */ return NULL; /* fake a memory allocation error */
else { else {
memHeader *newblock; Header *newblock;
int i; int i;
size_t commonsize = (oldsize < size) ? oldsize : size; size_t commonsize = (oldsize < size) ? oldsize : size;
size_t realsize = sizeof(memHeader) + size + MARKSIZE; size_t realsize = sizeof(Header) + size + MARKSIZE;
if (realsize < size) return NULL; /* arithmetic overflow! */ if (realsize < size) return NULL; /* arithmetic overflow! */
newblock = cast(memHeader *, malloc(realsize)); /* alloc a new block */ newblock = cast(Header *, malloc(realsize)); /* alloc a new block */
if (newblock == NULL) if (newblock == NULL)
return NULL; /* really out of memory? */ return NULL; /* really out of memory? */
if (block) { if (block) {
@ -480,7 +480,7 @@ static int lua_checkpc (CallInfo *ci) {
} }
static void check_stack (global_State *g, lua_State *L1) { static void checkstack (global_State *g, lua_State *L1) {
StkId o; StkId o;
CallInfo *ci; CallInfo *ci;
UpVal *uv; UpVal *uv;
@ -517,7 +517,7 @@ static void checkrefs (global_State *g, GCObject *o) {
break; break;
} }
case LUA_VTHREAD: { case LUA_VTHREAD: {
check_stack(g, gco2th(o)); checkstack(g, gco2th(o));
break; break;
} }
case LUA_VLCL: { case LUA_VLCL: {
@ -908,17 +908,6 @@ 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) { static int mem_query (lua_State *L) {
if (lua_isnone(L, 1)) { if (lua_isnone(L, 1)) {
lua_pushinteger(L, cast_Integer(l_memcontrol.total)); lua_pushinteger(L, cast_Integer(l_memcontrol.total));
@ -1077,12 +1066,8 @@ static int tracegc (lua_State *L) {
static int hash_query (lua_State *L) { static int hash_query (lua_State *L) {
if (lua_isnone(L, 2)) { if (lua_isnone(L, 2)) {
TString *ts;
luaL_argcheck(L, lua_type(L, 1) == LUA_TSTRING, 1, "string expected"); luaL_argcheck(L, lua_type(L, 1) == LUA_TSTRING, 1, "string expected");
ts = tsvalue(obj_at(L, 1)); lua_pushinteger(L, cast_int(tsvalue(obj_at(L, 1))->hash));
if (ts->tt == LUA_VLNGSTR)
luaS_hashlongstr(ts); /* make sure long string has a hash */
lua_pushinteger(L, cast_int(ts->hash));
} }
else { else {
TValue *o = obj_at(L, 1); TValue *o = obj_at(L, 1);
@ -1106,27 +1091,6 @@ static int stacklevel (lua_State *L) {
} }
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) { static int table_query (lua_State *L) {
const Table *t; const Table *t;
int i = cast_int(luaL_optinteger(L, 2, -1)); int i = cast_int(luaL_optinteger(L, 2, -1));
@ -1145,7 +1109,7 @@ static int table_query (lua_State *L) {
if (!tagisempty(*getArrTag(t, i))) if (!tagisempty(*getArrTag(t, i)))
arr2obj(t, cast_uint(i), s2v(L->top.p)); arr2obj(t, cast_uint(i), s2v(L->top.p));
else else
setnilvalue2s(L->top.p); setnilvalue(s2v(L->top.p));
api_incr_top(L); api_incr_top(L);
lua_pushnil(L); lua_pushnil(L);
} }
@ -1302,7 +1266,7 @@ static int doonnewstack (lua_State *L) {
lua_State *L1 = lua_newthread(L); lua_State *L1 = lua_newthread(L);
size_t l; size_t l;
const char *s = luaL_checklstring(L, 1, &l); const char *s = luaL_checklstring(L, 1, &l);
int status = luaL_loadbufferx(L1, s, l, s, "t"); int status = luaL_loadbuffer(L1, s, l, s);
if (status == LUA_OK) if (status == LUA_OK)
status = lua_pcall(L1, 0, 0, 0); status = lua_pcall(L1, 0, 0, 0);
lua_pushinteger(L, status); lua_pushinteger(L, status);
@ -1382,7 +1346,7 @@ static int doremote (lua_State *L) {
const char *code = luaL_checklstring(L, 2, &lcode); const char *code = luaL_checklstring(L, 2, &lcode);
int status; int status;
lua_settop(L1, 0); lua_settop(L1, 0);
status = luaL_loadbufferx(L1, code, lcode, code, "t"); status = luaL_loadbuffer(L1, code, lcode, code);
if (status == LUA_OK) if (status == LUA_OK)
status = lua_pcall(L1, 0, LUA_MULTRET, 0); status = lua_pcall(L1, 0, LUA_MULTRET, 0);
if (status != LUA_OK) { if (status != LUA_OK) {
@ -1738,7 +1702,7 @@ static int runC (lua_State *L, lua_State *L1, const char *pc) {
lua_pushinteger(L1, luaL_len(L1, getindex)); lua_pushinteger(L1, luaL_len(L1, getindex));
} }
else if EQ("loadfile") { else if EQ("loadfile") {
luaL_loadfilex(L1, luaL_checkstring(L1, getnum), "t"); luaL_loadfile(L1, luaL_checkstring(L1, getnum));
} }
else if EQ("loadstring") { else if EQ("loadstring") {
size_t slen; size_t slen;
@ -2203,9 +2167,6 @@ static const struct luaL_Reg tests_funcs[] = {
{"s2d", s2d}, {"s2d", s2d},
{"sethook", sethook}, {"sethook", sethook},
{"stacklevel", stacklevel}, {"stacklevel", stacklevel},
{"resetCI", resetCI},
{"reallocstack", reallocstack},
{"sizes", get_sizes},
{"testC", testC}, {"testC", testC},
{"makeCfunc", makeCfunc}, {"makeCfunc", makeCfunc},
{"totalmem", mem_query}, {"totalmem", mem_query},

View file

@ -13,8 +13,8 @@
/* test Lua with compatibility code */ /* test Lua with compatibility code */
#define LUA_COMPAT_MATHLIB #define LUA_COMPAT_MATHLIB
#define LUA_COMPAT_LT_LE
#undef LUA_COMPAT_GLOBAL #undef LUA_COMPAT_GLOBAL
#define LUA_COMPAT_GLOBAL 0
#define LUA_DEBUG #define LUA_DEBUG
@ -64,7 +64,7 @@ LUA_API Memcontrol l_memcontrol;
#define luai_tracegc(L,f) luai_tracegctest(L, f) #define luai_tracegc(L,f) luai_tracegctest(L, f)
extern void luai_tracegctest (lua_State *L, int first); LUAI_FUNC void luai_tracegctest (lua_State *L, int first);
/* /*
@ -76,26 +76,26 @@ extern void *l_Trick;
/* /*
** Function to traverse and check all memory used by Lua ** Function to traverse and check all memory used by Lua
*/ */
extern int lua_checkmemory (lua_State *L); LUAI_FUNC int lua_checkmemory (lua_State *L);
/* /*
** Function to print an object GC-friendly ** Function to print an object GC-friendly
*/ */
struct GCObject; struct GCObject;
extern void lua_printobj (lua_State *L, struct GCObject *o); LUAI_FUNC void lua_printobj (lua_State *L, struct GCObject *o);
/* /*
** Function to print a value ** Function to print a value
*/ */
struct TValue; struct TValue;
extern void lua_printvalue (struct TValue *v); LUAI_FUNC void lua_printvalue (struct TValue *v);
/* /*
** Function to print the stack ** Function to print the stack
*/ */
extern void lua_printstack (lua_State *L); LUAI_FUNC void lua_printstack (lua_State *L);
extern int lua_printallstack (lua_State *L); LUAI_FUNC int lua_printallstack (lua_State *L);
/* test for lock/unlock */ /* test for lock/unlock */
@ -122,14 +122,14 @@ LUA_API int luaB_opentests (lua_State *L);
LUA_API void *debug_realloc (void *ud, void *block, LUA_API void *debug_realloc (void *ud, void *block,
size_t osize, size_t nsize); size_t osize, size_t nsize);
#if defined(lua_c)
#define luaL_newstate() \ #define luaL_newstate() \
lua_newstate(debug_realloc, &l_memcontrol, luaL_makeseed(NULL)) lua_newstate(debug_realloc, &l_memcontrol, luaL_makeseed(NULL))
#define luai_openlibs(L) \ #define luai_openlibs(L) \
{ luaL_openlibs(L); \ { luaL_openlibs(L); \
luaL_requiref(L, "T", luaB_opentests, 1); \ luaL_requiref(L, "T", luaB_opentests, 1); \
lua_pop(L, 1); } lua_pop(L, 1); }
#endif
@ -143,13 +143,19 @@ LUA_API void *debug_realloc (void *ud, void *block,
#define STRCACHE_N 23 #define STRCACHE_N 23
#define STRCACHE_M 5 #define STRCACHE_M 5
#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. ** Reduce maximum stack size to make stack-overflow tests run faster.
** (But value is still large enough to overflow smaller integers.) ** (But value is still large enough to overflow smaller integers.)
*/ */
#undef LUAI_MAXSTACK
#define LUAI_MAXSTACK 68000 #define LUAI_MAXSTACK 68000

156
ltm.c
View file

@ -196,12 +196,28 @@ void luaT_trybiniTM (lua_State *L, const TValue *p1, lua_Integer i2,
/* /*
** Calls an order tag method. ** 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 r<l); bit CIST_LEQ in the call status keeps that
** information.
*/ */
int luaT_callorderTM (lua_State *L, const TValue *p1, const TValue *p2, int luaT_callorderTM (lua_State *L, const TValue *p1, const TValue *p2,
TMS event) { TMS event) {
int tag = callbinTM(L, p1, p2, L->top.p, event); /* try original event */ int tag = callbinTM(L, p1, p2, L->top.p, event); /* try original event */
if (tag >= 0) /* found tag method? */ if (tag >= 0) /* found tag method? */
return !tagisfalse(tag); 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 */ luaG_ordererror(L, p1, p2); /* no metamethod found */
return 0; /* to avoid warnings */ return 0; /* to avoid warnings */
} }
@ -224,141 +240,37 @@ int luaT_callorderiTM (lua_State *L, const TValue *p1, int v2,
} }
/* void luaT_adjustvarargs (lua_State *L, int nfixparams, CallInfo *ci,
** Create a vararg table at the top of the stack, with 'n' elements const Proto *p) {
** 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 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; ci->u.l.nextraargs = nextra;
luaD_checkstack(L, p->maxstacksize + 1); luaD_checkstack(L, p->maxstacksize + 1);
/* copy function to the top of the stack, after extra arguments */ /* copy function to the top of the stack */
setobjs2s(L, L->top.p++, ci->func.p); setobjs2s(L, L->top.p++, ci->func.p);
/* move fixed parameters to after the copied function */ /* move fixed parameters to the top of the stack */
for (i = 1; i <= nfixparams; i++) { for (i = 1; i <= nfixparams; i++) {
setobjs2s(L, L->top.p++, ci->func.p + i); setobjs2s(L, L->top.p++, ci->func.p + i);
setnilvalue2s(ci->func.p + i); /* erase original parameter (for GC) */ setnilvalue(s2v(ci->func.p + i)); /* erase original parameter (for GC) */
} }
ci->func.p += totalargs + 1; /* 'func' now lives after hidden arguments */ ci->func.p += actual + 1;
ci->top.p += totalargs + 1; ci->top.p += actual + 1;
lua_assert(L->top.p <= ci->top.p && ci->top.p <= L->stack_last.p);
} }
void luaT_adjustvarargs (lua_State *L, CallInfo *ci, const Proto *p) { void luaT_getvarargs (lua_State *L, CallInfo *ci, StkId where, int wanted) {
int totalargs = cast_int(L->top.p - ci->func.p) - 1; int i;
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; 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) { if (wanted < 0) {
touse = wanted = nargs; /* get all extra arguments available */ wanted = nextra; /* get all extra arguments available */
checkstackp(L, nargs, where); /* ensure stack space */ checkstackp(L, nextra, where); /* ensure stack space */
L->top.p = where + nargs; /* next instruction will need top */ L->top.p = where + nextra; /* 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 */ for (; i < wanted; i++) /* complete required results with nil */
setnilvalue2s(where + i); setnilvalue(s2v(where + i));
} }

11
ltm.h
View file

@ -49,7 +49,7 @@ typedef enum {
** Mask with 1 in all fast-access methods. A 1 in any of these bits ** 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 ** in the flag of a (meta)table means the metatable does not have the
** corresponding metamethod field. (Bit 6 of the flag indicates that ** corresponding metamethod field. (Bit 6 of the flag indicates that
** the table is using the dummy node.) ** the table is using the dummy node; bit 7 is used for 'isrealasize'.)
*/ */
#define maskflags cast_byte(~(~0u << (TM_EQ + 1))) #define maskflags cast_byte(~(~0u << (TM_EQ + 1)))
@ -95,11 +95,10 @@ LUAI_FUNC int luaT_callorderTM (lua_State *L, const TValue *p1,
LUAI_FUNC int luaT_callorderiTM (lua_State *L, const TValue *p1, int v2, LUAI_FUNC int luaT_callorderiTM (lua_State *L, const TValue *p1, int v2,
int inv, int isfloat, TMS event); int inv, int isfloat, TMS event);
LUAI_FUNC void luaT_adjustvarargs (lua_State *L, struct CallInfo *ci, LUAI_FUNC void luaT_adjustvarargs (lua_State *L, int nfixparams,
const Proto *p); 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,
LUAI_FUNC void luaT_getvarargs (lua_State *L, struct CallInfo *ci, StkId where, StkId where, int wanted);
int wanted, int vatab);
#endif #endif

115
lua.c
View file

@ -30,12 +30,6 @@
#define LUA_INIT_VAR "LUA_INIT" #define LUA_INIT_VAR "LUA_INIT"
#endif #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 #define LUA_INITVARVERSION LUA_INIT_VAR LUA_VERSUFFIX
@ -207,12 +201,12 @@ static int dochunk (lua_State *L, int status) {
static int dofile (lua_State *L, const char *name) { static int dofile (lua_State *L, const char *name) {
return dochunk(L, luaL_loadfilex(L, name, "bt")); return dochunk(L, luaL_loadfile(L, name));
} }
static int dostring (lua_State *L, const char *s, const char *name) { static int dostring (lua_State *L, const char *s, const char *name) {
return dochunk(L, luaL_loadbufferx(L, s, strlen(s), name, "t")); return dochunk(L, luaL_loadbuffer(L, s, strlen(s), name));
} }
@ -266,7 +260,7 @@ static int handle_script (lua_State *L, char **argv) {
const char *fname = argv[0]; const char *fname = argv[0];
if (strcmp(fname, "-") == 0 && strcmp(argv[-1], "--") != 0) if (strcmp(fname, "-") == 0 && strcmp(argv[-1], "--") != 0)
fname = NULL; /* stdin */ fname = NULL; /* stdin */
status = luaL_loadfilex(L, fname, "bt"); status = luaL_loadfile(L, fname);
if (status == LUA_OK) { if (status == LUA_OK) {
int n = pushargs(L); /* push arguments to script */ int n = pushargs(L); /* push arguments to script */
status = docall(L, n, LUA_MULTRET); status = docall(L, n, LUA_MULTRET);
@ -309,8 +303,7 @@ static int collectargs (char **argv, int *first) {
case '-': /* '--' */ case '-': /* '--' */
if (argv[i][2] != '\0') /* extra characters after '--'? */ if (argv[i][2] != '\0') /* extra characters after '--'? */
return has_error; /* invalid option */ return has_error; /* invalid option */
/* if there is a script name, it comes after '--' */ *first = i + 1;
*first = (argv[i + 1] != NULL) ? i + 1 : 0;
return args; return args;
case '\0': /* '-' */ case '\0': /* '-' */
return args; /* script "name" is '-' */ return args; /* script "name" is '-' */
@ -355,7 +348,6 @@ static int collectargs (char **argv, int *first) {
*/ */
static int runargs (lua_State *L, char **argv, int n) { static int runargs (lua_State *L, char **argv, int n) {
int i; int i;
lua_warning(L, "@off", 0); /* by default, Lua stand-alone has warnings off */
for (i = 1; i < n; i++) { for (i = 1; i < n; i++) {
int option = argv[i][1]; int option = argv[i][1];
lua_assert(argv[i][0] == '-'); /* already checked */ lua_assert(argv[i][0] == '-'); /* already checked */
@ -380,21 +372,12 @@ 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) { static int handle_luainit (lua_State *L) {
const char *name = "=" LUA_INITVARVERSION; const char *name = "=" LUA_INITVARVERSION;
const char *init = l_getenv(name + 1); const char *init = getenv(name + 1);
if (init == NULL) { if (init == NULL) {
name = "=" LUA_INIT_VAR; name = "=" LUA_INIT_VAR;
init = l_getenv(name + 1); /* try alternative name */ init = getenv(name + 1); /* try alternative name */
} }
if (init == NULL) return LUA_OK; if (init == NULL) return LUA_OK;
else if (init[0] == '@') else if (init[0] == '@')
@ -454,24 +437,13 @@ static int handle_luainit (lua_State *L) {
** the standard input. ** the standard input.
** * lua_saveline defines how to "save" a read line in a "history". ** * 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_freeline defines how to free a line read by lua_readline.
**
** If lua_readline is defined, all of them should be defined.
*/ */
#if !defined(lua_readline) /* { */ #if !defined(lua_readline) /* { */
/* Otherwise, all previously listed functions should be defined. */
#if defined(LUA_USE_READLINE) /* { */ /* Code to use the readline library, either statically or dynamically linked */
/* Lua will be linked with '-lreadline' */
#include <readline/readline.h>
#include <readline/history.h>
#define lua_initreadline(L) ((void)L, rl_readline_name="lua")
#define lua_readline(buff,prompt) ((void)buff, readline(prompt))
#define lua_saveline(line) add_history(line)
#define lua_freeline(line) free(line)
#else /* }{ */
/* use dynamically loaded readline (or nothing) */
/* pointer to 'readline' function (if any) */ /* pointer to 'readline' function (if any) */
typedef char *(*l_readlineT) (const char *prompt); typedef char *(*l_readlineT) (const char *prompt);
@ -507,41 +479,43 @@ static void lua_freeline (char *line) {
} }
#if defined(LUA_USE_DLOPEN) && defined(LUA_READLINELIB) /* { */ #if defined(LUA_USE_READLINE) /* { */
/* try to load 'readline' dynamically */
/* assume Lua will be linked with '-lreadline' */
#include <readline/readline.h>
#include <readline/history.h>
static void lua_initreadline(lua_State *L) {
UNUSED(L);
rl_readline_name = "lua";
l_readline = cast(l_readlineT, readline);
l_addhist = cast(l_addhistT, add_history);
}
#elif defined(LUA_USE_DLOPEN) && defined(LUA_READLINELIB) /* }{ */
/* try to load 'readline' dynamically */
#include <dlfcn.h> #include <dlfcn.h>
static void lua_initreadline (lua_State *L) { static void lua_initreadline (lua_State *L) {
const char *rllib = l_getenv(LUA_RLLIB_VAR); /* name of readline library */ void *lib = dlopen(LUA_READLINELIB, RTLD_NOW | RTLD_LOCAL);
void *lib; /* library handle */ if (lib == NULL)
if (rllib == NULL) /* no environment variable? */ lua_warning(L, "library '" LUA_READLINELIB "' not found", 0);
rllib = LUA_READLINELIB; /* use default name */ else {
lib = dlopen(rllib, RTLD_NOW | RTLD_LOCAL);
if (lib != NULL) {
const char **name = cast(const char**, dlsym(lib, "rl_readline_name")); const char **name = cast(const char**, dlsym(lib, "rl_readline_name"));
if (name != NULL) if (name != NULL)
*name = "lua"; *name = "lua";
l_readline = cast(l_readlineT, cast_func(dlsym(lib, "readline"))); l_readline = cast(l_readlineT, cast_func(dlsym(lib, "readline")));
l_addhist = cast(l_addhistT, cast_func(dlsym(lib, "add_history"))); 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);
} }
#else /* }{ */ #else /* }{ */
/* no dlopen or LUA_READLINELIB undefined */
/* Leave pointers with NULL */ /* no readline; leave function pointers as NULL */
#define lua_initreadline(L) ((void)L) #define lua_initreadline(L) cast(void, L)
#endif /* } */ #endif /* } */
#endif /* } */
#endif /* } */ #endif /* } */
@ -609,11 +583,11 @@ static int pushline (lua_State *L, int firstline) {
static int addreturn (lua_State *L) { static int addreturn (lua_State *L) {
const char *line = lua_tostring(L, -1); /* original line */ const char *line = lua_tostring(L, -1); /* original line */
const char *retline = lua_pushfstring(L, "return %s;", line); const char *retline = lua_pushfstring(L, "return %s;", line);
int status = luaL_loadbufferx(L, retline, strlen(retline), "=stdin", "t"); int status = luaL_loadbuffer(L, retline, strlen(retline), "=stdin");
if (status == LUA_OK) if (status == LUA_OK)
lua_remove(L, -2); /* remove modified line */ lua_remove(L, -2); /* remove modified line */
else else
lua_pop(L, 2); /* pop result from 'luaL_loadbufferx' and modified line */ lua_pop(L, 2); /* pop result from 'luaL_loadbuffer' and modified line */
return status; return status;
} }
@ -640,7 +614,7 @@ static int multiline (lua_State *L) {
const char *line = lua_tolstring(L, 1, &len); /* get first line */ const char *line = lua_tolstring(L, 1, &len); /* get first line */
checklocal(line); checklocal(line);
for (;;) { /* repeat until gets a complete statement */ for (;;) { /* repeat until gets a complete statement */
int status = luaL_loadbufferx(L, line, len, "=stdin", "t"); /* try it */ int status = luaL_loadbuffer(L, line, len, "=stdin"); /* try it */
if (!incomplete(L, status) || !pushline(L, 0)) if (!incomplete(L, status) || !pushline(L, 0))
return status; /* should not or cannot try to add continuation line */ return status; /* should not or cannot try to add continuation line */
lua_remove(L, -2); /* remove error message (from incomplete line) */ lua_remove(L, -2); /* remove error message (from incomplete line) */
@ -714,13 +688,7 @@ static void doREPL (lua_State *L) {
/* }================================================================== */ /* }================================================================== */
#if !defined(luai_openlibs) #if !defined(luai_openlibs)
#if defined(LUA_NODEBUGLIB) #define luai_openlibs(L) luaL_openselectedlibs(L, ~0, 0)
/* 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 #endif
@ -742,19 +710,18 @@ static int pmain (lua_State *L) {
if (args & has_v) /* option '-v'? */ if (args & has_v) /* option '-v'? */
print_version(); print_version();
if (args & has_E) { /* option '-E'? */ 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_pushboolean(L, 1); /* signal for libraries to ignore env. vars. */
lua_setfield(L, LUA_REGISTRYINDEX, "LUA_NOENV"); lua_setfield(L, LUA_REGISTRYINDEX, "LUA_NOENV");
} }
else
l_getenv = &getenv;
luai_openlibs(L); /* open standard libraries */ luai_openlibs(L); /* open standard libraries */
createargtable(L, argv, argc, script); /* create table 'arg' */ createargtable(L, argv, argc, script); /* create table 'arg' */
lua_gc(L, LUA_GCRESTART); /* start GC... */ lua_gc(L, LUA_GCRESTART); /* start GC... */
lua_gc(L, LUA_GCGEN); /* ...in generational mode */ lua_gc(L, LUA_GCGEN); /* ...in generational mode */
if (handle_luainit(L) != LUA_OK) /* run LUA_INIT */ if (!(args & has_E)) { /* no option '-E'? */
return 0; /* error running LUA_INIT */ if (handle_luainit(L) != LUA_OK) /* run LUA_INIT */
if (!runargs(L, argv, optlim)) /* execute arguments -e, -l, and -W */ return 0; /* error running LUA_INIT */
}
if (!runargs(L, argv, optlim)) /* execute arguments -e and -l */
return 0; /* something failed */ return 0; /* something failed */
if (script > 0) { /* execute main script (if there is one) */ if (script > 0) { /* execute main script (if there is one) */
if (handle_script(L, argv + script) != LUA_OK) if (handle_script(L, argv + script) != LUA_OK)

19
lua.h
View file

@ -13,13 +13,13 @@
#include <stddef.h> #include <stddef.h>
#define LUA_COPYRIGHT LUA_RELEASE " Copyright (C) 1994-2026 Lua.org, PUC-Rio" #define LUA_COPYRIGHT LUA_RELEASE " Copyright (C) 1994-2025 Lua.org, PUC-Rio"
#define LUA_AUTHORS "R. Ierusalimschy, L. H. de Figueiredo, W. Celes" #define LUA_AUTHORS "R. Ierusalimschy, L. H. de Figueiredo, W. Celes"
#define LUA_VERSION_MAJOR_N 5 #define LUA_VERSION_MAJOR_N 5
#define LUA_VERSION_MINOR_N 5 #define LUA_VERSION_MINOR_N 5
#define LUA_VERSION_RELEASE_N 1 #define LUA_VERSION_RELEASE_N 0
#define LUA_VERSION_NUM (LUA_VERSION_MAJOR_N * 100 + LUA_VERSION_MINOR_N) #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) #define LUA_VERSION_RELEASE_NUM (LUA_VERSION_NUM * 100 + LUA_VERSION_RELEASE_N)
@ -37,10 +37,10 @@
/* /*
** Pseudo-indices ** Pseudo-indices
** (The stack size is limited to INT_MAX/2; we keep some free empty ** (-LUAI_MAXSTACK is the minimum valid index; we keep some free empty
** space after that to help overflow detection.) ** space after that to help overflow detection)
*/ */
#define LUA_REGISTRYINDEX (-(INT_MAX/2 + 1000)) #define LUA_REGISTRYINDEX (-LUAI_MAXSTACK - 1000)
#define lua_upvalueindex(i) (LUA_REGISTRYINDEX - (i)) #define lua_upvalueindex(i) (LUA_REGISTRYINDEX - (i))
@ -432,6 +432,13 @@ LUA_API void (lua_closeslot) (lua_State *L, int idx);
** compatibility macros ** 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_newuserdata(L,s) lua_newuserdatauv(L,s,1)
#define lua_getuservalue(L,idx) lua_getiuservalue(L,idx,1) #define lua_getuservalue(L,idx) lua_getiuservalue(L,idx,1)
@ -521,7 +528,7 @@ struct lua_Debug {
/****************************************************************************** /******************************************************************************
* Copyright (C) 1994-2026 Lua.org, PUC-Rio. * Copyright (C) 1994-2025 Lua.org, PUC-Rio.
* *
* Permission is hereby granted, free of charge, to any person obtaining * Permission is hereby granted, free of charge, to any person obtaining
* a copy of this software and associated documentation files (the * a copy of this software and associated documentation files (the

147
luaconf.h
View file

@ -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 ** application will try to dynamically link a 'readline' facility
** for its REPL. In that case, LUA_READLINELIB is the name of the ** 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 ** library it will look for those facilities. If lua.c cannot open
@ -70,16 +70,14 @@
#if defined(LUA_USE_LINUX) #if defined(LUA_USE_LINUX)
#define LUA_USE_POSIX #define LUA_USE_POSIX
#define LUA_USE_DLOPEN /* needs an extra library: -ldl */ #define LUA_USE_DLOPEN /* needs an extra library: -ldl */
#if !defined(LUA_READLINELIB)
#define LUA_READLINELIB "libreadline.so" #define LUA_READLINELIB "libreadline.so"
#endif #endif
#endif
#if defined(LUA_USE_MACOSX) #if defined(LUA_USE_MACOSX)
#define LUA_USE_POSIX #define LUA_USE_POSIX
#define LUA_USE_DLOPEN /* macOS does not need -ldl */ #define LUA_USE_DLOPEN /* MacOS does not need -ldl */
#define LUA_USE_READLINE /* needs an extra library: -lreadline */ #define LUA_READLINELIB "libedit.dylib"
#endif #endif
@ -90,7 +88,7 @@
#if defined(LUA_USE_C89) && defined(LUA_USE_POSIX) #if defined(LUA_USE_C89) && defined(LUA_USE_POSIX)
#error "POSIX is not compatible with C89" #error "Posix is not compatible with C89"
#endif #endif
@ -140,7 +138,7 @@
/* /*
@@ LUA_32BITS enables Lua with 32-bit integers and 32-bit floats. @@ LUA_32BITS enables Lua with 32-bit integers and 32-bit floats.
*/ */
/* #define LUA_32BITS */ #define LUA_32BITS 0
/* /*
@ -155,7 +153,7 @@
#endif #endif
#if defined(LUA_32BITS) /* { */ #if LUA_32BITS /* { */
/* /*
** 32-bit integers and 'float' ** 32-bit integers and 'float'
*/ */
@ -226,17 +224,17 @@
#if !defined(LUA_PATH_DEFAULT) #if !defined(LUA_PATH_DEFAULT)
#define LUA_PATH_DEFAULT \ #define LUA_PATH_DEFAULT \
LUA_LDIR "?.lua;" LUA_LDIR "?\\init.lua;" \ LUA_LDIR"?.lua;" LUA_LDIR"?\\init.lua;" \
LUA_CDIR "?.lua;" LUA_CDIR "?\\init.lua;" \ LUA_CDIR"?.lua;" LUA_CDIR"?\\init.lua;" \
LUA_SHRDIR "?.lua;" LUA_SHRDIR "?\\init.lua;" \ LUA_SHRDIR"?.lua;" LUA_SHRDIR"?\\init.lua;" \
".\\?.lua;" ".\\?\\init.lua" ".\\?.lua;" ".\\?\\init.lua"
#endif #endif
#if !defined(LUA_CPATH_DEFAULT) #if !defined(LUA_CPATH_DEFAULT)
#define LUA_CPATH_DEFAULT \ #define LUA_CPATH_DEFAULT \
LUA_CDIR "?.dll;" \ LUA_CDIR"?.dll;" \
LUA_CDIR "..\\lib\\lua\\" LUA_VDIR "\\?.dll;" \ LUA_CDIR"..\\lib\\lua\\" LUA_VDIR "\\?.dll;" \
LUA_CDIR "loadall.dll;" ".\\?.dll" LUA_CDIR"loadall.dll;" ".\\?.dll"
#endif #endif
#else /* }{ */ #else /* }{ */
@ -247,14 +245,14 @@
#if !defined(LUA_PATH_DEFAULT) #if !defined(LUA_PATH_DEFAULT)
#define LUA_PATH_DEFAULT \ #define LUA_PATH_DEFAULT \
LUA_LDIR "?.lua;" LUA_LDIR "?/init.lua;" \ LUA_LDIR"?.lua;" LUA_LDIR"?/init.lua;" \
LUA_CDIR "?.lua;" LUA_CDIR "?/init.lua;" \ LUA_CDIR"?.lua;" LUA_CDIR"?/init.lua;" \
"./?.lua;" "./?/init.lua" "./?.lua;" "./?/init.lua"
#endif #endif
#if !defined(LUA_CPATH_DEFAULT) #if !defined(LUA_CPATH_DEFAULT)
#define 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
#endif /* } */ #endif /* } */
@ -321,13 +319,32 @@
** More often than not the libs go together with the core. ** More often than not the libs go together with the core.
*/ */
#define LUALIB_API LUA_API #define LUALIB_API LUA_API
#if defined(__cplusplus)
/* Lua uses the "C name" when calling open functions */
#define LUAMOD_API extern "C"
#else
#define LUAMOD_API LUA_API #define LUAMOD_API LUA_API
#endif
/*
@@ 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 */
/* }================================================================== */ /* }================================================================== */
@ -341,18 +358,39 @@
/* /*
@@ LUA_COMPAT_GLOBAL avoids 'global' being a reserved word @@ LUA_COMPAT_GLOBAL avoids 'global' being a reserved word
*/ */
#if !defined(LUA_COMPAT_GLOBAL) #define LUA_COMPAT_GLOBAL
#define LUA_COMPAT_GLOBAL 1
#endif
/*
@@ 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.
*/
#if defined(LUA_COMPAT_5_3) /* { */
/* /*
@@ LUA_COMPAT_MATHLIB controls the presence of several deprecated @@ LUA_COMPAT_MATHLIB controls the presence of several deprecated
** functions in the mathematical library. ** functions in the mathematical library.
** (These functions were already officially removed in 5.3; ** (These functions were already officially removed in 5.3;
** nevertheless they are still available here.) ** nevertheless they are still available here.)
*/ */
/* #define LUA_COMPAT_MATHLIB */ #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.)
*/
#define LUA_COMPAT_APIINTCASTS
/*
@@ LUA_COMPAT_LT_LE controls the emulation of the '__le' metamethod
** using '__lt'.
*/
#define LUA_COMPAT_LT_LE
/* /*
@ -369,6 +407,8 @@
#define lua_equal(L,idx1,idx2) lua_compare(L,(idx1),(idx2),LUA_OPEQ) #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) #define lua_lessthan(L,idx1,idx2) lua_compare(L,(idx1),(idx2),LUA_OPLT)
#endif /* } */
/* }================================================================== */ /* }================================================================== */
@ -400,11 +440,26 @@
*/ */
/* The following definition is good for most cases here */ /* The following definitions are good for most cases here */
#define l_floor(x) (l_mathop(floor)(x)) #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 */ /* now the variable definitions */
#if LUA_FLOAT_TYPE == LUA_FLOAT_FLOAT /* { single float */ #if LUA_FLOAT_TYPE == LUA_FLOAT_FLOAT /* { single float */
@ -653,7 +708,7 @@
*/ */
#if !defined(luai_likely) #if !defined(luai_likely)
#if !defined(LUA_NOBUILTIN) && defined(__GNUC__) && (__GNUC__ >= 3) #if defined(__GNUC__) && !defined(LUA_NOBUILTIN)
#define luai_likely(x) (__builtin_expect(((x) != 0), 1)) #define luai_likely(x) (__builtin_expect(((x) != 0), 1))
#define luai_unlikely(x) (__builtin_expect(((x) != 0), 0)) #define luai_unlikely(x) (__builtin_expect(((x) != 0), 0))
#else #else
@ -664,6 +719,13 @@
#endif #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
/* }================================================================== */ /* }================================================================== */
@ -701,6 +763,20 @@
** ===================================================================== ** =====================================================================
*/ */
/*
@@ 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 @@ LUA_EXTRASPACE defines the size of a raw memory area associated with
** a Lua state with very fast access. ** a Lua state with very fast access.
@ -725,17 +801,10 @@
/* /*
@@ LUAI_MAXALIGN defines fields that ensure proper alignment for @@ LUAI_MAXALIGN defines fields that, when used in a union, ensure
** memory areas offered by Lua (e.g., userdata memory). ** maximum alignment for the other items in that union.
** 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 #define LUAI_MAXALIGN lua_Number n; double u; void *s; lua_Integer i; long l
#endif
/* }================================================================== */ /* }================================================================== */
@ -752,5 +821,7 @@
#endif #endif

View file

@ -109,7 +109,7 @@ static lua_Unsigned loadVarint (LoadState *S, lua_Unsigned limit) {
static size_t loadSize (LoadState *S) { static size_t loadSize (LoadState *S) {
return cast_sizet(loadVarint(S, MAX_SIZE)); return loadVarint(S, MAX_SIZE);
} }
@ -147,20 +147,20 @@ static void loadString (LoadState *S, Proto *p, TString **sl) {
TString *ts; TString *ts;
TValue sv; TValue sv;
size_t size = loadSize(S); size_t size = loadSize(S);
if (size == 0) { /* previously saved string? */ if (size == 0) { /* no string? */
lua_assert(*sl == NULL); /* must be prefilled */
return;
}
else if (size == 1) { /* previously saved string? */
lua_Unsigned idx = loadVarint(S, LUA_MAXUNSIGNED); /* get its index */ lua_Unsigned idx = loadVarint(S, LUA_MAXUNSIGNED); /* get its index */
TValue stv; TValue 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) if (novariant(luaH_getint(S->h, l_castU2S(idx), &stv)) != LUA_TSTRING)
error(S, "invalid string index"); error(S, "invalid string index");
*sl = ts = tsvalue(&stv); /* get its value */ *sl = ts = tsvalue(&stv); /* get its value */
luaC_objbarrier(L, p, ts); luaC_objbarrier(L, p, ts);
return; /* do not save it again */ return; /* do not save it again */
} }
else if ((size -= 1) <= LUAI_MAXSHORTLEN) { /* short string? */ else if ((size -= 2) <= LUAI_MAXSHORTLEN) { /* short string? */
char buff[LUAI_MAXSHORTLEN + 1]; /* extra space for '\0' */ char buff[LUAI_MAXSHORTLEN + 1]; /* extra space for '\0' */
loadVector(S, buff, size + 1); /* load string into buffer */ loadVector(S, buff, size + 1); /* load string into buffer */
*sl = ts = luaS_newlstr(L, buff, size); /* create string */ *sl = ts = luaS_newlstr(L, buff, size); /* create string */
@ -327,8 +327,7 @@ static void loadFunction (LoadState *S, Proto *f) {
f->linedefined = loadInt(S); f->linedefined = loadInt(S);
f->lastlinedefined = loadInt(S); f->lastlinedefined = loadInt(S);
f->numparams = loadByte(S); f->numparams = loadByte(S);
/* get only the meaningful flags */ f->flag = loadByte(S) & PF_ISVARARG; /* get only the meaningful flags */
f->flag = cast_byte(loadByte(S) & ~PF_FIXED);
if (S->fixed) if (S->fixed)
f->flag |= PF_FIXED; /* signal that code is fixed */ f->flag |= PF_FIXED; /* signal that code is fixed */
f->maxstacksize = loadByte(S); f->maxstacksize = loadByte(S);
@ -392,8 +391,7 @@ static void checkHeader (LoadState *S) {
/* /*
** Load precompiled chunk. ** Load precompiled chunk.
*/ */
LClosure *luaU_undump (lua_State *L, ZIO *Z, Table *anchor, const char *name, LClosure *luaU_undump (lua_State *L, ZIO *Z, const char *name, int fixed) {
int fixed) {
LoadState S; LoadState S;
LClosure *cl; LClosure *cl;
if (*name == '@' || *name == '=') if (*name == '@' || *name == '=')
@ -406,16 +404,20 @@ LClosure *luaU_undump (lua_State *L, ZIO *Z, Table *anchor, const char *name,
S.fixed = cast_byte(fixed); S.fixed = cast_byte(fixed);
S.offset = 1; /* fist byte was already read */ S.offset = 1; /* fist byte was already read */
checkHeader(&S); checkHeader(&S);
S.h = anchor;
S.nstr = 0;
cl = luaF_newLclosure(L, loadByte(&S)); cl = luaF_newLclosure(L, loadByte(&S));
luaD_anchorobj(L, anchor, obj2gco(cl)); setclLvalue2s(L, L->top.p, cl);
luaD_inctop(L);
S.h = luaH_new(L); /* create list of saved strings */
S.nstr = 0;
sethvalue2s(L, L->top.p, S.h); /* anchor it */
luaD_inctop(L);
cl->p = luaF_newproto(L); cl->p = luaF_newproto(L);
luaC_objbarrier(L, cl, cl->p); luaC_objbarrier(L, cl, cl->p);
loadFunction(&S, cl->p); loadFunction(&S, cl->p);
if (cl->nupvalues != cl->p->sizeupvalues) if (cl->nupvalues != cl->p->sizeupvalues)
error(&S, "corrupted chunk"); error(&S, "corrupted chunk");
luai_verifycode(L, cl->p); luai_verifycode(L, cl->p);
L->top.p--; /* pop table */
return cl; return cl;
} }

View file

@ -30,8 +30,8 @@
/* load one chunk; from lundump.c */ /* load one chunk; from lundump.c */
LUAI_FUNC LClosure* luaU_undump (lua_State* L, ZIO* Z, Table *anchor, LUAI_FUNC LClosure* luaU_undump (lua_State* L, ZIO* Z, const char* name,
const char* name, int fixed); int fixed);
/* dump one chunk; from ldump.c */ /* dump one chunk; from ldump.c */
LUAI_FUNC int luaU_dump (lua_State* L, const Proto* f, lua_Writer w, LUAI_FUNC int luaU_dump (lua_State* L, const Proto* f, lua_Writer w,

View file

@ -10,6 +10,7 @@
#include "lprefix.h" #include "lprefix.h"
#include <assert.h>
#include <limits.h> #include <limits.h>
#include <stdlib.h> #include <stdlib.h>
#include <string.h> #include <string.h>
@ -46,7 +47,7 @@ static lua_Integer u_posrelat (lua_Integer pos, size_t len) {
** Decode one UTF-8 sequence, returning NULL if byte sequence is ** Decode one UTF-8 sequence, returning NULL if byte sequence is
** invalid. The array 'limits' stores the minimum value for each ** invalid. The array 'limits' stores the minimum value for each
** sequence length, to check for overlong representations. Its first ** 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). ** bytes (count == 0).
*/ */
static const char *utf8_decode (const char *s, l_uint32 *val, int strict) { static const char *utf8_decode (const char *s, l_uint32 *val, int strict) {
@ -54,10 +55,8 @@ static const char *utf8_decode (const char *s, l_uint32 *val, int strict) {
{~(l_uint32)0, 0x80, 0x800, 0x10000u, 0x200000u, 0x4000000u}; {~(l_uint32)0, 0x80, 0x800, 0x10000u, 0x200000u, 0x4000000u};
unsigned int c = (unsigned char)s[0]; unsigned int c = (unsigned char)s[0];
l_uint32 res = 0; /* final result */ l_uint32 res = 0; /* final result */
if (c < 0x80) /* ASCII? */ if (c < 0x80) /* ascii? */
res = c; res = c;
else if (c >= 0xfe) /* c >= 1111 1110b ? */
return NULL; /* would need six or more continuation bytes */
else { else {
int count = 0; /* to count number of continuation bytes */ int count = 0; /* to count number of continuation bytes */
for (; c & 0x40; c <<= 1) { /* while it needs continuation bytes... */ for (; c & 0x40; c <<= 1) { /* while it needs continuation bytes... */
@ -66,9 +65,8 @@ static const char *utf8_decode (const char *s, l_uint32 *val, int strict) {
return NULL; /* invalid byte sequence */ return NULL; /* invalid byte sequence */
res = (res << 6) | (cc & 0x3F); /* add lower 6 bits from cont. byte */ 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 */ res |= ((l_uint32)(c & 0x7F) << (count * 5)); /* add first byte */
if (res > MAXUTF || res < limits[count]) if (count > 5 || res > MAXUTF || res < limits[count])
return NULL; /* invalid byte sequence */ return NULL; /* invalid byte sequence */
s += count; /* skip continuation bytes read */ s += count; /* skip continuation bytes read */
} }
@ -149,7 +147,7 @@ static int codepoint (lua_State *L) {
static void pushutfchar (lua_State *L, int arg) { static void pushutfchar (lua_State *L, int arg) {
lua_Unsigned code = (lua_Unsigned)luaL_checkinteger(L, arg); lua_Unsigned code = (lua_Unsigned)luaL_checkinteger(L, arg);
luaL_argcheck(L, code <= MAXUTF, arg, "value out of range"); luaL_argcheck(L, code <= MAXUTF, arg, "value out of range");
lua_pushfstring(L, "%U", cast(unsigned long, code)); lua_pushfstring(L, "%U", (long)code);
} }
@ -217,10 +215,9 @@ static int byteoffset (lua_State *L) {
} }
lua_pushinteger(L, posi + 1); /* initial position */ lua_pushinteger(L, posi + 1); /* initial position */
if ((s[posi] & 0x80) != 0) { /* multi-byte character? */ if ((s[posi] & 0x80) != 0) { /* multi-byte character? */
if (iscont(s[posi])) do {
return luaL_error(L, "initial position is a continuation byte"); posi++;
while (iscontp(s + posi + 1)) } while (iscontp(s + posi + 1)); /* skip to final byte */
posi++; /* skip to last continuation byte */
} }
/* else one-byte character: final position is the initial one */ /* else one-byte character: final position is the initial one */
lua_pushinteger(L, posi + 1); /* 'posi' now is the final position */ lua_pushinteger(L, posi + 1); /* 'posi' now is the final position */

266
lvm.c
View file

@ -268,9 +268,9 @@ static int forprep (lua_State *L, StkId ra) {
/* /*
** Execute a step of a float numerical for loop, returning ** Execute a step of a float numerical for loop, returning
** true iff the loop must continue. (The integer case is ** true iff the loop must continue. (The integer case is
** written inline with opcode OP_FORLOOP, for performance.) ** written online with opcode OP_FORLOOP, for performance.)
*/ */
static int floatforloop (lua_State *L, StkId ra) { static int floatforloop (StkId ra) {
lua_Number step = fltvalue(s2v(ra + 1)); lua_Number step = fltvalue(s2v(ra + 1));
lua_Number limit = fltvalue(s2v(ra)); lua_Number limit = fltvalue(s2v(ra));
lua_Number idx = fltvalue(s2v(ra + 2)); /* control variable */ 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 */ else { /* 't' is a table */
tm = fasttm(L, hvalue(t)->metatable, TM_INDEX); /* table's metamethod */ tm = fasttm(L, hvalue(t)->metatable, TM_INDEX); /* table's metamethod */
if (tm == NULL) { /* no metamethod? */ if (tm == NULL) { /* no metamethod? */
setnilvalue2s(val); /* result is nil */ setnilvalue(s2v(val)); /* result is nil */
return LUA_VNIL; return LUA_VNIL;
} }
/* else will try the metamethod */ /* else will try the metamethod */
@ -360,32 +360,18 @@ void luaV_finishset (lua_State *L, const TValue *t, TValue *key,
luaT_callTM(L, tm, t, key, val); luaT_callTM(L, tm, t, key, val);
return; return;
} }
t = tm; /* else must repeat assignment over 'tm' */ t = tm; /* else repeat assignment over 'tm' */
/* do the equivalent to 'luaV_fastset', but saving 'h' */ luaV_fastset(t, key, val, hres, luaH_pset);
if (!ttistable(t)) if (hres == HOK) {
hres = HNOTATABLE; luaV_finishfastset(L, t, val);
else { return; /* done */
Table *h = hvalue(t); /* next call can change the value at 't' */
hres = luaH_pset(h, key, val);
if (hres == HOK) {
luaC_barrierback(L, obj2gco(h), val); /* luaV_finishfastset */
return; /* done */
}
} }
/* else 'return luaV_finishset(L, t, key, val, hres)' (loop) */ /* else 'return luaV_finishset(L, t, key, val, slot)' (loop) */
} }
luaG_runerror(L, "'__newindex' chain too long; possible 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- ** Compare two strings 'ts1' x 'ts2', returning an integer less-equal-
** -greater than zero if 'ts1' is less-equal-greater than 'ts2'. ** -greater than zero if 'ts1' is less-equal-greater than 'ts2'.
@ -400,7 +386,7 @@ static int l_strcmp (const TString *ts1, const TString *ts2) {
size_t rl2; size_t rl2;
const char *s2 = getlstr(ts2, rl2); const char *s2 = getlstr(ts2, rl2);
for (;;) { /* for each segment */ for (;;) { /* for each segment */
int temp = l_strcoll(s1, s2); int temp = strcoll(s1, s2);
if (temp != 0) /* not equal? */ if (temp != 0) /* not equal? */
return temp; /* done */ return temp; /* done */
else { /* strings are equal up to a '\0' */ else { /* strings are equal up to a '\0' */
@ -587,74 +573,52 @@ int luaV_lessequal (lua_State *L, const TValue *l, const TValue *r) {
*/ */
int luaV_equalobj (lua_State *L, const TValue *t1, const TValue *t2) { int luaV_equalobj (lua_State *L, const TValue *t1, const TValue *t2) {
const TValue *tm; const TValue *tm;
if (ttype(t1) != ttype(t2)) /* not the same type? */ if (ttypetag(t1) != ttypetag(t2)) { /* not the same variant? */
return 0; if (ttype(t1) != ttype(t2) || ttype(t1) != LUA_TNUMBER)
else if (ttypetag(t1) != ttypetag(t2)) { return 0; /* only numbers can be equal with different variants */
switch (ttypetag(t1)) { else { /* two numbers with different variants */
case LUA_VNUMINT: { /* integer == float? */ /* One of them is an integer. If the other does not have an
/* integer and float can only be equal if float has an integer integer value, they cannot be equal; otherwise, compare their
value equal to the integer */ integer values. */
lua_Integer i2; lua_Integer i1, i2;
return (luaV_flttointeger(fltvalue(t2), &i2, F2Ieq) && return (luaV_tointegerns(t1, &i1, F2Ieq) &&
ivalue(t1) == i2); luaV_tointegerns(t2, &i2, F2Ieq) &&
} i1 == 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;
} }
} }
else { /* equal variants */ /* values have same type and same variant */
switch (ttypetag(t1)) { switch (ttypetag(t1)) {
case LUA_VNIL: case LUA_VFALSE: case LUA_VTRUE: case LUA_VNIL: case LUA_VFALSE: case LUA_VTRUE: return 1;
return 1; case LUA_VNUMINT: return (ivalue(t1) == ivalue(t2));
case LUA_VNUMINT: case LUA_VNUMFLT: return luai_numeq(fltvalue(t1), fltvalue(t2));
return (ivalue(t1) == ivalue(t2)); case LUA_VLIGHTUSERDATA: return pvalue(t1) == pvalue(t2);
case LUA_VNUMFLT: case LUA_VLCF: return fvalue(t1) == fvalue(t2);
return (fltvalue(t1) == fltvalue(t2)); case LUA_VSHRSTR: return eqshrstr(tsvalue(t1), tsvalue(t2));
case LUA_VLIGHTUSERDATA: return pvalue(t1) == pvalue(t2); case LUA_VLNGSTR: return luaS_eqlngstr(tsvalue(t1), tsvalue(t2));
case LUA_VSHRSTR: case LUA_VUSERDATA: {
return eqshrstr(tsvalue(t1), tsvalue(t2)); if (uvalue(t1) == uvalue(t2)) return 1;
case LUA_VLNGSTR: else if (L == NULL) return 0;
return luaS_eqstr(tsvalue(t1), tsvalue(t2)); tm = fasttm(L, uvalue(t1)->metatable, TM_EQ);
case LUA_VUSERDATA: { if (tm == NULL)
if (uvalue(t1) == uvalue(t2)) return 1; tm = fasttm(L, uvalue(t2)->metatable, TM_EQ);
else if (L == NULL) return 0; break; /* will try TM */
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));
} }
if (tm == NULL) /* no TM? */ case LUA_VTABLE: {
return 0; /* objects are different */ if (hvalue(t1) == hvalue(t2)) return 1;
else { else if (L == NULL) return 0;
int tag = luaT_callTMres(L, tm, t1, t2, L->top.p); /* call TM */ tm = fasttm(L, hvalue(t1)->metatable, TM_EQ);
return !tagisfalse(tag); if (tm == NULL)
tm = fasttm(L, hvalue(t2)->metatable, TM_EQ);
break; /* will try TM */
} }
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);
} }
} }
@ -663,11 +627,6 @@ int luaV_equalobj (lua_State *L, const TValue *t1, const TValue *t2) {
#define tostring(L,o) \ #define tostring(L,o) \
(ttisstring(o) || (cvt2str(o) && (luaO_tostring(L, o), 1))) (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) #define isemptystr(o) (ttisshrstring(o) && tsvalue(o)->shrlen == 0)
/* copy strings in stack from top - n up to top - 1 to buffer */ /* copy strings in stack from top - n up to top - 1 to buffer */
@ -702,8 +661,8 @@ void luaV_concat (lua_State *L, int total) {
setobjs2s(L, top - 2, top - 1); /* result is second op. */ setobjs2s(L, top - 2, top - 1); /* result is second op. */
} }
else { else {
/* at least two string values; get as many as possible */ /* at least two non-empty string values; get as many as possible */
size_t tl = tsslen(tsvalue(s2v(top - 1))); /* total length */ size_t tl = tsslen(tsvalue(s2v(top - 1)));
TString *ts; TString *ts;
/* collect total length and number of strings */ /* collect total length and number of strings */
for (n = 1; n < total && tostring(L, s2v(top - n - 1)); n++) { for (n = 1; n < total && tostring(L, s2v(top - n - 1)); n++) {
@ -741,7 +700,7 @@ void luaV_objlen (lua_State *L, StkId ra, const TValue *rb) {
Table *h = hvalue(rb); Table *h = hvalue(rb);
tm = fasttm(L, h->metatable, TM_LEN); tm = fasttm(L, h->metatable, TM_LEN);
if (tm) break; /* metamethod? break switch to call it */ if (tm) break; /* metamethod? break switch to call it */
setivalue(s2v(ra), l_castU2S(luaH_getn(L, h))); /* else primitive len */ setivalue(s2v(ra), l_castU2S(luaH_getn(h))); /* else primitive len */
return; return;
} }
case LUA_VSHRSTR: { case LUA_VSHRSTR: {
@ -880,6 +839,12 @@ void luaV_finishOp (lua_State *L) {
case OP_EQ: { /* note that 'OP_EQI'/'OP_EQK' cannot yield */ case OP_EQ: { /* note that 'OP_EQI'/'OP_EQK' cannot yield */
int res = !l_isfalse(s2v(L->top.p - 1)); int res = !l_isfalse(s2v(L->top.p - 1));
L->top.p--; 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); lua_assert(GET_OPCODE(*ci->u.l.savedpc) == OP_JMP);
if (res != GETARG_k(inst)) /* condition failed? */ if (res != GETARG_k(inst)) /* condition failed? */
ci->u.l.savedpc++; /* skip jump instruction */ ci->u.l.savedpc++; /* skip jump instruction */
@ -923,10 +888,6 @@ void luaV_finishOp (lua_State *L) {
/* /*
** {================================================================== ** {==================================================================
** Macros for arithmetic/bitwise/comparison opcodes in 'luaV_execute' ** Macros for arithmetic/bitwise/comparison opcodes in 'luaV_execute'
**
** All these macros are to be used exclusively inside the main
** interpreter loop (function luaV_execute) and may access directly
** the local variables of that function (L, i, pc, ci, etc.).
** =================================================================== ** ===================================================================
*/ */
@ -948,17 +909,17 @@ void luaV_finishOp (lua_State *L) {
** operation, 'fop' is the float operation. ** operation, 'fop' is the float operation.
*/ */
#define op_arithI(L,iop,fop) { \ #define op_arithI(L,iop,fop) { \
TValue *ra = vRA(i); \ StkId ra = RA(i); \
TValue *v1 = vRB(i); \ TValue *v1 = vRB(i); \
int imm = GETARG_sC(i); \ int imm = GETARG_sC(i); \
if (ttisinteger(v1)) { \ if (ttisinteger(v1)) { \
lua_Integer iv1 = ivalue(v1); \ lua_Integer iv1 = ivalue(v1); \
pc++; setivalue(ra, iop(L, iv1, imm)); \ pc++; setivalue(s2v(ra), iop(L, iv1, imm)); \
} \ } \
else if (ttisfloat(v1)) { \ else if (ttisfloat(v1)) { \
lua_Number nb = fltvalue(v1); \ lua_Number nb = fltvalue(v1); \
lua_Number fimm = cast_num(imm); \ lua_Number fimm = cast_num(imm); \
pc++; setfltvalue(ra, fop(L, nb, fimm)); \ pc++; setfltvalue(s2v(ra), fop(L, nb, fimm)); \
}} }}
@ -969,7 +930,6 @@ void luaV_finishOp (lua_State *L) {
#define op_arithf_aux(L,v1,v2,fop) { \ #define op_arithf_aux(L,v1,v2,fop) { \
lua_Number n1; lua_Number n2; \ lua_Number n1; lua_Number n2; \
if (tonumberns(v1, n1) && tonumberns(v2, n2)) { \ if (tonumberns(v1, n1) && tonumberns(v2, n2)) { \
StkId ra = RA(i); \
pc++; setfltvalue(s2v(ra), fop(L, n1, n2)); \ pc++; setfltvalue(s2v(ra), fop(L, n1, n2)); \
}} }}
@ -978,6 +938,7 @@ void luaV_finishOp (lua_State *L) {
** Arithmetic operations over floats and others with register operands. ** Arithmetic operations over floats and others with register operands.
*/ */
#define op_arithf(L,fop) { \ #define op_arithf(L,fop) { \
StkId ra = RA(i); \
TValue *v1 = vRB(i); \ TValue *v1 = vRB(i); \
TValue *v2 = vRC(i); \ TValue *v2 = vRC(i); \
op_arithf_aux(L, v1, v2, fop); } op_arithf_aux(L, v1, v2, fop); }
@ -987,6 +948,7 @@ void luaV_finishOp (lua_State *L) {
** Arithmetic operations with K operands for floats. ** Arithmetic operations with K operands for floats.
*/ */
#define op_arithfK(L,fop) { \ #define op_arithfK(L,fop) { \
StkId ra = RA(i); \
TValue *v1 = vRB(i); \ TValue *v1 = vRB(i); \
TValue *v2 = KC(i); lua_assert(ttisnumber(v2)); \ TValue *v2 = KC(i); lua_assert(ttisnumber(v2)); \
op_arithf_aux(L, v1, v2, fop); } op_arithf_aux(L, v1, v2, fop); }
@ -996,8 +958,8 @@ void luaV_finishOp (lua_State *L) {
** Arithmetic operations over integers and floats. ** Arithmetic operations over integers and floats.
*/ */
#define op_arith_aux(L,v1,v2,iop,fop) { \ #define op_arith_aux(L,v1,v2,iop,fop) { \
StkId ra = RA(i); \
if (ttisinteger(v1) && ttisinteger(v2)) { \ if (ttisinteger(v1) && ttisinteger(v2)) { \
StkId ra = RA(i); \
lua_Integer i1 = ivalue(v1); lua_Integer i2 = ivalue(v2); \ lua_Integer i1 = ivalue(v1); lua_Integer i2 = ivalue(v2); \
pc++; setivalue(s2v(ra), iop(L, i1, i2)); \ pc++; setivalue(s2v(ra), iop(L, i1, i2)); \
} \ } \
@ -1026,12 +988,12 @@ void luaV_finishOp (lua_State *L) {
** Bitwise operations with constant operand. ** Bitwise operations with constant operand.
*/ */
#define op_bitwiseK(L,op) { \ #define op_bitwiseK(L,op) { \
StkId ra = RA(i); \
TValue *v1 = vRB(i); \ TValue *v1 = vRB(i); \
TValue *v2 = KC(i); \ TValue *v2 = KC(i); \
lua_Integer i1; \ lua_Integer i1; \
lua_Integer i2 = ivalue(v2); \ lua_Integer i2 = ivalue(v2); \
if (tointegerns(v1, &i1)) { \ if (tointegerns(v1, &i1)) { \
StkId ra = RA(i); \
pc++; setivalue(s2v(ra), op(i1, i2)); \ pc++; setivalue(s2v(ra), op(i1, i2)); \
}} }}
@ -1040,11 +1002,11 @@ void luaV_finishOp (lua_State *L) {
** Bitwise operations with register operands. ** Bitwise operations with register operands.
*/ */
#define op_bitwise(L,op) { \ #define op_bitwise(L,op) { \
StkId ra = RA(i); \
TValue *v1 = vRB(i); \ TValue *v1 = vRB(i); \
TValue *v2 = vRC(i); \ TValue *v2 = vRC(i); \
lua_Integer i1; lua_Integer i2; \ lua_Integer i1; lua_Integer i2; \
if (tointegerns(v1, &i1) && tointegerns(v2, &i2)) { \ if (tointegerns(v1, &i1) && tointegerns(v2, &i2)) { \
StkId ra = RA(i); \
pc++; setivalue(s2v(ra), op(i1, i2)); \ pc++; setivalue(s2v(ra), op(i1, i2)); \
}} }}
@ -1055,18 +1017,18 @@ void luaV_finishOp (lua_State *L) {
** integers. ** integers.
*/ */
#define op_order(L,opi,opn,other) { \ #define op_order(L,opi,opn,other) { \
TValue *ra = vRA(i); \ StkId ra = RA(i); \
int cond; \ int cond; \
TValue *rb = vRB(i); \ TValue *rb = vRB(i); \
if (ttisinteger(ra) && ttisinteger(rb)) { \ if (ttisinteger(s2v(ra)) && ttisinteger(rb)) { \
lua_Integer ia = ivalue(ra); \ lua_Integer ia = ivalue(s2v(ra)); \
lua_Integer ib = ivalue(rb); \ lua_Integer ib = ivalue(rb); \
cond = opi(ia, ib); \ cond = opi(ia, ib); \
} \ } \
else if (ttisnumber(ra) && ttisnumber(rb)) \ else if (ttisnumber(s2v(ra)) && ttisnumber(rb)) \
cond = opn(ra, rb); \ cond = opn(s2v(ra), rb); \
else \ else \
Protect(cond = other(L, ra, rb)); \ Protect(cond = other(L, s2v(ra), rb)); \
docondjump(); } docondjump(); }
@ -1075,19 +1037,19 @@ void luaV_finishOp (lua_State *L) {
** always small enough to have an exact representation as a float.) ** always small enough to have an exact representation as a float.)
*/ */
#define op_orderI(L,opi,opf,inv,tm) { \ #define op_orderI(L,opi,opf,inv,tm) { \
TValue *ra = vRA(i); \ StkId ra = RA(i); \
int cond; \ int cond; \
int im = GETARG_sB(i); \ int im = GETARG_sB(i); \
if (ttisinteger(ra)) \ if (ttisinteger(s2v(ra))) \
cond = opi(ivalue(ra), im); \ cond = opi(ivalue(s2v(ra)), im); \
else if (ttisfloat(ra)) { \ else if (ttisfloat(s2v(ra))) { \
lua_Number fa = fltvalue(ra); \ lua_Number fa = fltvalue(s2v(ra)); \
lua_Number fim = cast_num(im); \ lua_Number fim = cast_num(im); \
cond = opf(fa, fim); \ cond = opf(fa, fim); \
} \ } \
else { \ else { \
int isf = GETARG_C(i); \ int isf = GETARG_C(i); \
Protect(cond = luaT_callorderiTM(L, ra, im, inv, isf, tm)); \ Protect(cond = luaT_callorderiTM(L, s2v(ra), im, inv, isf, tm)); \
} \ } \
docondjump(); } docondjump(); }
@ -1106,7 +1068,6 @@ void luaV_finishOp (lua_State *L) {
#define RA(i) (base+GETARG_A(i)) #define RA(i) (base+GETARG_A(i))
#define vRA(i) s2v(RA(i))
#define RB(i) (base+GETARG_B(i)) #define RB(i) (base+GETARG_B(i))
#define vRB(i) s2v(RB(i)) #define vRB(i) s2v(RB(i))
#define KB(i) (k+GETARG_B(i)) #define KB(i) (k+GETARG_B(i))
@ -1117,7 +1078,7 @@ void luaV_finishOp (lua_State *L) {
#define updatetrap(ci) (trap = cast_int(ci->u.l.trap)) #define updatetrap(ci) (trap = ci->u.l.trap)
#define updatebase(ci) (base = ci->func.p + 1) #define updatebase(ci) (base = ci->func.p + 1)
@ -1147,14 +1108,14 @@ void luaV_finishOp (lua_State *L) {
/* /*
** Correct global 'pc'. ** Correct global 'pc'.
*/ */
#define savepc(ci) (ci->u.l.savedpc = pc) #define savepc(L) (ci->u.l.savedpc = pc)
/* /*
** Whenever code can raise errors, the global 'pc' and the global ** Whenever code can raise errors, the global 'pc' and the global
** 'top' must be correct to report occasional errors. ** 'top' must be correct to report occasional errors.
*/ */
#define savestate(L,ci) (savepc(ci), L->top.p = ci->top.p) #define savestate(L,ci) (savepc(L), L->top.p = ci->top.p)
/* /*
@ -1164,7 +1125,7 @@ void luaV_finishOp (lua_State *L) {
#define Protect(exp) (savestate(L,ci), (exp), updatetrap(ci)) #define Protect(exp) (savestate(L,ci), (exp), updatetrap(ci))
/* special version that does not change the top */ /* special version that does not change the top */
#define ProtectNT(exp) (savepc(ci), (exp), updatetrap(ci)) #define ProtectNT(exp) (savepc(L), (exp), updatetrap(ci))
/* /*
** Protect code that can only raise errors. (That is, it cannot change ** Protect code that can only raise errors. (That is, it cannot change
@ -1182,7 +1143,7 @@ void luaV_finishOp (lua_State *L) {
/* 'c' is the limit of live values in the stack */ /* 'c' is the limit of live values in the stack */
#define checkGC(L,c) \ #define checkGC(L,c) \
{ luaC_condGC(L, (savepc(ci), L->top.p = (c)), \ { luaC_condGC(L, (savepc(L), L->top.p = (c)), \
updatetrap(ci)); \ updatetrap(ci)); \
luai_threadyield(L); } luai_threadyield(L); }
@ -1211,7 +1172,7 @@ void luaV_execute (lua_State *L, CallInfo *ci) {
#include "ljumptab.h" #include "ljumptab.h"
#endif #endif
startfunc: startfunc:
trap = cast_int(L->hookmask); trap = L->hookmask;
returning: /* trap already set */ returning: /* trap already set */
cl = ci_func(ci); cl = ci_func(ci);
k = cl->p->k; k = cl->p->k;
@ -1489,16 +1450,6 @@ void luaV_execute (lua_State *L, CallInfo *ci) {
op_bitwiseK(L, l_bxor); op_bitwiseK(L, l_bxor);
vmbreak; vmbreak;
} }
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(ic, ib));
}
vmbreak;
}
vmcase(OP_SHRI) { vmcase(OP_SHRI) {
StkId ra = RA(i); StkId ra = RA(i);
TValue *rb = vRB(i); TValue *rb = vRB(i);
@ -1509,6 +1460,16 @@ void luaV_execute (lua_State *L, CallInfo *ci) {
} }
vmbreak; vmbreak;
} }
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(ic, ib));
}
vmbreak;
}
vmcase(OP_ADD) { vmcase(OP_ADD) {
op_arith(L, l_addi, luai_numadd); op_arith(L, l_addi, luai_numadd);
vmbreak; vmbreak;
@ -1551,14 +1512,14 @@ void luaV_execute (lua_State *L, CallInfo *ci) {
op_bitwise(L, l_bxor); op_bitwise(L, l_bxor);
vmbreak; vmbreak;
} }
vmcase(OP_SHL) {
op_bitwise(L, luaV_shiftl);
vmbreak;
}
vmcase(OP_SHR) { vmcase(OP_SHR) {
op_bitwise(L, luaV_shiftr); op_bitwise(L, luaV_shiftr);
vmbreak; vmbreak;
} }
vmcase(OP_SHL) {
op_bitwise(L, luaV_shiftl);
vmbreak;
}
vmcase(OP_MMBIN) { vmcase(OP_MMBIN) {
StkId ra = RA(i); StkId ra = RA(i);
Instruction pi = *(pc - 2); /* original arith. expression */ Instruction pi = *(pc - 2); /* original arith. expression */
@ -1731,7 +1692,7 @@ void luaV_execute (lua_State *L, CallInfo *ci) {
if (b != 0) /* fixed number of arguments? */ if (b != 0) /* fixed number of arguments? */
L->top.p = ra + b; /* top signals number of arguments */ L->top.p = ra + b; /* top signals number of arguments */
/* else previous instruction set top */ /* else previous instruction set top */
savepc(ci); /* in case of errors */ savepc(L); /* in case of errors */
if ((newci = luaD_precall(L, ra, nresults)) == NULL) if ((newci = luaD_precall(L, ra, nresults)) == NULL)
updatetrap(ci); /* C call; nothing else to be done */ updatetrap(ci); /* C call; nothing else to be done */
else { /* Lua call: run function in this same C frame */ else { /* Lua call: run function in this same C frame */
@ -1847,7 +1808,7 @@ void luaV_execute (lua_State *L, CallInfo *ci) {
pc -= GETARG_Bx(i); /* jump back */ pc -= GETARG_Bx(i); /* jump back */
} }
} }
else if (floatforloop(L, ra)) /* float loop */ else if (floatforloop(ra)) /* float loop */
pc -= GETARG_Bx(i); /* jump back */ pc -= GETARG_Bx(i); /* jump back */
updatetrap(ci); /* allows a signal to break the loop */ updatetrap(ci); /* allows a signal to break the loop */
vmbreak; vmbreak;
@ -1907,7 +1868,7 @@ void luaV_execute (lua_State *L, CallInfo *ci) {
vmcase(OP_SETLIST) { vmcase(OP_SETLIST) {
StkId ra = RA(i); StkId ra = RA(i);
unsigned n = cast_uint(GETARG_vB(i)); unsigned n = cast_uint(GETARG_vB(i));
unsigned last = cast_uint(GETARG_vC(i)); unsigned int last = cast_uint(GETARG_vC(i));
Table *h = hvalue(s2v(ra)); Table *h = hvalue(s2v(ra));
if (n == 0) if (n == 0)
n = cast_uint(L->top.p - ra) - 1; /* get up to the top */ n = cast_uint(L->top.p - ra) - 1; /* get up to the top */
@ -1941,25 +1902,12 @@ void luaV_execute (lua_State *L, CallInfo *ci) {
} }
vmcase(OP_VARARG) { vmcase(OP_VARARG) {
StkId ra = RA(i); StkId ra = RA(i);
int n = GETARG_C(i) - 1; /* required results (-1 means all) */ int n = GETARG_C(i) - 1; /* required results */
int vatab = GETARG_k(i) ? GETARG_B(i) : -1; Protect(luaT_getvarargs(L, ci, ra, n));
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; vmbreak;
} }
vmcase(OP_VARARGPREP) { vmcase(OP_VARARGPREP) {
ProtectNT(luaT_adjustvarargs(L, ci, cl->p)); ProtectNT(luaT_adjustvarargs(L, GETARG_A(i), ci, cl->p));
if (l_unlikely(trap)) { /* previous "Protect" updated trap */ if (l_unlikely(trap)) { /* previous "Protect" updated trap */
luaD_hookcall(L, ci); luaD_hookcall(L, ci);
L->oldpc = 1; /* next opcode will be seen as a "new" line */ L->oldpc = 1; /* next opcode will be seen as a "new" line */

View file

@ -15,9 +15,9 @@ CWARNSCPP= \
-Wdouble-promotion \ -Wdouble-promotion \
-Wmissing-declarations \ -Wmissing-declarations \
-Wconversion \ -Wconversion \
-Wstrict-overflow=2 \
# the next warnings might be useful sometimes, # the next warnings might be useful sometimes,
# but usually they generate too much noise # but usually they generate too much noise
# -Wstrict-overflow=2 \
# -Werror \ # -Werror \
# -pedantic # warns if we use jump tables \ # -pedantic # warns if we use jump tables \
# -Wformat=2 \ # -Wformat=2 \
@ -60,29 +60,24 @@ CWARNS= $(CWARNSCPP) $(CWARNSC) $(CWARNGCC)
# create problems; some are only available in newer gcc versions. To # create problems; some are only available in newer gcc versions. To
# use some of them, we also have to define an environment variable # use some of them, we also have to define an environment variable
# ASAN_OPTIONS="detect_invalid_pointer_pairs=2". # ASAN_OPTIONS="detect_invalid_pointer_pairs=2".
# -fsanitize=undefined (you may need to add "-lubsan" to libs) # -fsanitize=undefined
# -fsanitize=pointer-subtract -fsanitize=address -fsanitize=pointer-compare # -fsanitize=pointer-subtract -fsanitize=address -fsanitize=pointer-compare
# 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 # TESTS= -DLUA_USER_H='"ltests.h"' -Og -g
LOCAL = $(TESTS) $(CWARNS) LOCAL = $(TESTS) $(CWARNS)
# To enable Linux goodies, -DLUA_USE_LINUX # To enable Linux goodies, -DLUA_USE_LINUX
# For C89, "-std=c89 -DLUA_USE_C89" # For C89, "-std=c89 -DLUA_USE_C89"
# Note that Linux/Posix options are not compatible with 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 MYCFLAGS= $(LOCAL) -std=c99 -DLUA_USE_LINUX
MYLDFLAGS= -Wl,-E MYLDFLAGS= $(LOCAL) -Wl,-E
MYLIBS= -ldl MYLIBS= -ldl
CC= gcc CC= gcc
# (Optionally we can use -march=native -mno-avx512f.) CFLAGS= -Wall -O2 $(MYCFLAGS) -fno-stack-protector -fno-common -march=native
CFLAGS= -Wall -O2 $(MYCFLAGS) -fno-stack-protector -fno-common
AR= ar rc AR= ar rc
RANLIB= ranlib RANLIB= ranlib
RM= rm -f RM= rm -f
@ -171,7 +166,8 @@ 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 \ 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 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 \ 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 lstring.h ltable.h llimits.h ltm.h lzio.h lmem.h ldo.h lfunc.h lgc.h llex.h lstring.h \
ltable.h
linit.o: linit.c lprefix.h lua.h luaconf.h lualib.h lauxlib.h llimits.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 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 \ llex.o: llex.c lprefix.h lua.h luaconf.h lctype.h llimits.h ldebug.h \

View file

@ -1,4 +1,4 @@
#!/usr/bin/env lua #!/usr/bin/env lua5.3
-- special marks: -- special marks:
@ -30,7 +30,7 @@ by Roberto Ierusalimschy, Luiz Henrique de Figueiredo, Waldemar Celes
<p> <p>
<small> <small>
<a href="http://www.lua.org/copyright.html">Copyright</a> <a href="http://www.lua.org/copyright.html">Copyright</a>
&copy; 2026 Lua.org, PUC-Rio. All rights reserved. &copy; 2025 Lua.org, PUC-Rio. All rights reserved.
</small> </small>
<hr> <hr>
@ -58,7 +58,7 @@ end
local function compose (f,g) local function compose (f,g)
assert(f and g) assert(f and g)
return function (...) return g(f(...)) end return function (s) return g(f(s)) end
end end
local function concat (f, g) local function concat (f, g)
@ -395,10 +395,9 @@ APIEntry = function (e)
local apiicmd, ne = string.match(e, "^(.-</span>)(.*)") local apiicmd, ne = string.match(e, "^(.-</span>)(.*)")
--io.stderr:write(e) --io.stderr:write(e)
if not apiicmd then if not apiicmd then
return antipara(Tag.hr() .. Tag.h3(a)) .. Tag.pre(h, {class="api"}) .. e return antipara(Tag.hr() .. Tag.h3(a)) .. Tag.pre(h) .. e
else else
return antipara(Tag.hr() .. Tag.h3(a)) .. apiicmd .. return antipara(Tag.hr() .. Tag.h3(a)) .. apiicmd .. Tag.pre(h) .. ne
Tag.pre(h, {class="api"}) .. ne
end end
end, end,

View file

@ -107,7 +107,7 @@ for small machines and embedded systems.
Unless stated otherwise, Unless stated otherwise,
any overflow when manipulating integer values @def{wrap around}, any overflow when manipulating integer values @def{wrap around},
according to the usual rules of two's complement arithmetic. according to the usual rules of two-complement arithmetic.
(In other words, (In other words,
the actual result is the unique representable integer the actual result is the unique representable integer
that is equal modulo @M{2@sp{n}} to the mathematical result, that is equal modulo @M{2@sp{n}} to the mathematical result,
@ -127,8 +127,7 @@ strings can contain any 8-bit value,
including @x{embedded zeros} (@Char{\0}). including @x{embedded zeros} (@Char{\0}).
Lua is also encoding-agnostic; Lua is also encoding-agnostic;
it makes no assumptions about the contents of a string. 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 Lua can call (and manipulate) functions written in Lua and
functions written in C @see{functioncall}. functions written in C @see{functioncall}.
@ -230,7 +229,7 @@ as the following example illustrates:
@verbatim{ @verbatim{
X = 1 -- Ok, global by default X = 1 -- Ok, global by default
do do
global Y -- voids the implicit initial declaration global Y -- voids implicit initial declaration
Y = 1 -- Ok, Y declared as global Y = 1 -- Ok, Y declared as global
X = 1 -- ERROR, X not declared X = 1 -- ERROR, X not declared
end end
@ -270,7 +269,7 @@ print(x) --> 10 (the global one)
Notice that, in a declaration like @T{local x = x}, Notice that, in a declaration like @T{local x = x},
the new @id{x} being declared is not in scope yet, 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. and so the @id{x} in the left-hand side refers to the outside variable.
Because of the @x{lexical scoping} rules, Because of the @x{lexical scoping} rules,
local variables can be freely accessed by functions local variables can be freely accessed by functions
@ -1556,8 +1555,7 @@ It has the following syntax:
exp @bnfter{,} exp @bnfopt{@bnfter{,} exp} @Rw{do} block @Rw{end}} exp @bnfter{,} exp @bnfopt{@bnfter{,} exp} @Rw{do} block @Rw{end}}
} }
The given identifier (@bnfNter{Name}) defines the control variable, The given identifier (@bnfNter{Name}) defines the control variable,
which is a new read-only (@id{const}) variable local to the loop body which is a new read-only variable local to the loop body (@emph{block}).
(@emph{block}).
The loop starts by evaluating once the three control expressions. The loop starts by evaluating once the three control expressions.
Their values are called respectively Their values are called respectively
@ -1612,7 +1610,7 @@ works as follows.
The names @rep{var_i} declare loop variables local to the loop body. The names @rep{var_i} declare loop variables local to the loop body.
The first of these variables is the @emph{control variable}, The first of these variables is the @emph{control variable},
which is a read-only (@id{const}) variable. which is a read-only variable.
The loop starts by evaluating @rep{explist} The loop starts by evaluating @rep{explist}
to produce four values: to produce four values:
@ -1653,22 +1651,15 @@ Function calls are explained in @See{functioncall}.
@sect3{localvar| @title{Variable Declarations} @sect3{localvar| @title{Variable Declarations}
Local and global variables can be declared anywhere inside a block. Local and global variables can be declared anywhere inside a block.
The declaration can include an initialization: The declaration for locals can include an initialization:
@Produc{ @Produc{
@producname{stat}@producbody{@Rw{local} @producname{stat}@producbody{@Rw{local}
attnamelist @bnfopt{@bnfter{=} explist}} attnamelist @bnfopt{@bnfter{=} explist}}
@producname{stat}@producbody{@Rw{global} @producname{stat}@producbody{@Rw{global} attnamelist}
attnamelist @bnfopt{@bnfter{=} explist}}
} }
If there is no initialization, If present, an initial assignment has the same semantics
local variables are initialized with @nil;
global variables are left unchanged.
Otherwise, the initialization gets the same adjustment
of a multiple assignment @see{assignment}. of a multiple assignment @see{assignment}.
Moreover, for global variables, Otherwise, all local variables are initialized with @nil.
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 The list of names may be prefixed by an attribute
(a name between angle brackets) (a name between angle brackets)
@ -1721,14 +1712,13 @@ and a program that starts with any other global declaration
(e.g., @T{global none}) can only refer to declared variables. (e.g., @T{global none}) can only refer to declared variables.
Note that, for global variables, Note that, for global variables,
the effect of any declaration is only syntactical the effect of any declaration is only syntactical:
(except for the optional assignment):
@verbatim{ @verbatim{
global X <const>, _G global X <const>, _G
X = 1 -- ERROR X = 1 -- ERROR
_ENV.X = 1 -- Ok _ENV.X = 1 -- Ok
_G.print(X) -- Ok _G.print(X) -- Ok
foo() -- 'foo' can freely change any global foo() -- 'foo' can freely change any global
} }
A chunk is also a block @see{chunks}, A chunk is also a block @see{chunks},
@ -2091,12 +2081,12 @@ Note that keys that are not positive integers
do not interfere with borders. do not interfere with borders.
A table with exactly one border is called a @def{sequence}. 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). 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. and therefore it is not a sequence.
(The @nil at index 4 is called a @emphx{hole}.) (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), has three borders (0, 3, and 6),
so it is not a sequence, too. so it is not a sequence, too.
The table @T{{}} is a sequence with border 0. The table @T{{}} is a sequence with border 0.
@ -2221,7 +2211,7 @@ The form
} }
can be used to emulate methods. can be used to emulate methods.
A call @T{v:name(@rep{args})} 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. except that @id{v} is evaluated only once.
Arguments have the following syntax: Arguments have the following syntax:
@ -2268,7 +2258,7 @@ return x or f(x) -- results adjusted to 1
@sect3{func-def| @title{Function Definitions} @sect3{func-def| @title{Function Definitions}
The syntax for a function definition is The syntax for function definition is
@Produc{ @Produc{
@producname{functiondef}@producbody{@Rw{function} funcbody} @producname{functiondef}@producbody{@Rw{function} funcbody}
@producname{funcbody}@producbody{@bnfter{(} @bnfopt{parlist} @bnfter{)} block @Rw{end}} @producname{funcbody}@producbody{@bnfter{(} @bnfopt{parlist} @bnfter{)} block @Rw{end}}
@ -2318,10 +2308,71 @@ global function f () @rep{body} end
} }
translates to translates to
@verbatim{ @verbatim{
global f; global f = function () @rep{body} end global f; 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. A function definition is an executable expression,
whose value has type @emph{function}.
When Lua precompiles a chunk,
all its function bodies are precompiled too,
but they are not created yet.
Then, whenever Lua executes the function definition,
the function is @emph{instantiated} (or @emph{closed}).
This function instance, or @emphx{closure},
is the final value of the expression.
Parameters act as local variables that are
initialized with the argument values:
@Produc{
@producname{parlist}@producbody{namelist @bnfopt{@bnfter{,} @bnfter{...}} @Or
@bnfter{...}}
}
When a Lua function is called,
it adjusts its list of @x{arguments} to
the length of its list of parameters @see{multires},
unless the function is a @def{variadic function},
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}.
As an example, consider the following definitions:
@verbatim{
function f(a, b) end
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:
@verbatim{
CALL PARAMETERS
f(3) a=3, b=nil
f(3, 4) a=3, b=4
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
}
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.
The @emphx{colon} syntax The @emphx{colon} syntax
is used to emulate @def{methods}, is used to emulate @def{methods},
@ -2335,104 +2386,10 @@ is syntactic sugar for
t.a.b.c.f = function (self, @rep{params}) @rep{body} end 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}.
When Lua precompiles a chunk,
all its function bodies are precompiled too,
but they are not created yet.
Then, whenever Lua executes the function definition,
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{,} varargparam} @Or
varargparam}
@producname{varargparam}@producbody{@bnfter{...} @bnfopt{@bnfNter{Name}}}
}
When a Lua function is called,
it adjusts its list of @x{arguments} to
the length of its list of parameters @see{multires},
unless the function is a @def{variadic function},
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 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{
function f(a, b) end
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 table:
@verbatim{
CALL PARAMETERS
f(3) a=3, b=nil
f(3, 4) a=3, b=4
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, 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}
} }
A vararg table in a variadic function can have an optional name, @sect3{multires| @title{Lists of expressions, multiple results,
given after the three dots. and adjustment}
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.
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.
}
}
@sect3{multires| @title{Lists of Expressions, Multiple Results, and Adjustment}
Both function calls and vararg expressions can result in multiple values. Both function calls and vararg expressions can result in multiple values.
These expressions are called @def{multires expressions}. These expressions are called @def{multires expressions}.
@ -2449,22 +2406,22 @@ These are the places where Lua expects a list of expressions:
@description{ @description{
@item{A @rw{return} statement, @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, @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, @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, @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 or global declaration, @item{A local declaration,
which is similar to a multiple assignment.} for instance @T{local a , b, c = e1, e2, e3} @see{localvar}.}
@item{The initial values in a generic @rw{for} loop, @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, In the last four cases,
@ -2472,7 +2429,8 @@ the list of values from the list of expressions
must be @emph{adjusted} to a specific length: must be @emph{adjusted} to a specific length:
the number of parameters in a call to a non-variadic function the number of parameters in a call to a non-variadic function
@see{func-def}, @see{func-def},
the number of variables in a multiple assignment or a declaration, the number of variables in a multiple assignment or
a local declaration,
and exactly four values for a generic @rw{for} loop. and exactly four values for a generic @rw{for} loop.
The @def{adjustment} follows these rules: The @def{adjustment} follows these rules:
If there are more values than needed, If there are more values than needed,
@ -2501,7 +2459,7 @@ we recommend assigning the vararg expression
to a single variable and using that variable to a single variable and using that variable
in its place. in its place.
Here are some examples of uses of multires expressions. Here are some examples of uses of mutlres expressions.
In all cases, when the construction needs In all cases, when the construction needs
@Q{the n-th result} and there is no such result, @Q{the n-th result} and there is no such result,
it uses a @nil. it uses a @nil.
@ -2690,21 +2648,9 @@ which behaves like a nil value.
} }
@sect3{constchar|@title{Pointers to Strings} @sect3{constchar|@title{Pointers to strings}
Several functions in the API have parameters Several functions in the API return pointers (@T{const char*})
that are pointers to C strings (@T{const char*}).
Some of these 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. to Lua strings in the stack.
(See @Lid{lua_pushfstring}, @Lid{lua_pushlstring}, (See @Lid{lua_pushfstring}, @Lid{lua_pushlstring},
@Lid{lua_pushstring}, and @Lid{lua_tolstring}. @Lid{lua_pushstring}, and @Lid{lua_tolstring}.
@ -2877,16 +2823,7 @@ status codes to indicate different kinds of errors or other conditions:
For such errors, Lua does not call the @x{message handler}. For such errors, Lua does not call the @x{message handler}.
} }
@item{@defid{LUA_ERRERR}| @item{@defid{LUA_ERRERR}| error while running the @x{message handler}.}
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 @item{@defid{LUA_ERRSYNTAX}| syntax error during precompilation
or format error in a binary chunk.} or format error in a binary chunk.}
@ -3067,7 +3004,7 @@ typedef void * (*lua_Alloc) (void *ud,
size_t osize, size_t osize,
size_t nsize);| size_t nsize);|
The type of the @x{memory-allocator function} used by Lua states. The type of the @x{memory-allocation function} used by Lua states.
The allocator function must provide a The allocator function must provide a
functionality similar to @id{realloc}, functionality similar to @id{realloc},
but not exactly the same. but not exactly the same.
@ -3102,12 +3039,11 @@ the allocator must behave like @id{realloc}.
In particular, the allocator returns @id{NULL} In particular, the allocator returns @id{NULL}
if and only if it cannot fulfill the request. if and only if it cannot fulfill the request.
Here is a simple implementation for the @x{allocator function}, Here is a simple implementation for the @x{allocator function}.
corresponding to the function @Lid{luaL_alloc} from the It is used in the auxiliary library by @Lid{luaL_newstate}.
auxiliary library.
@verbatim{ @verbatim{
void *luaL_alloc (void *ud, void *ptr, size_t osize, static void *l_alloc (void *ud, void *ptr, size_t osize,
size_t nsize) { size_t nsize) {
(void)ud; (void)osize; /* not used */ (void)ud; (void)osize; /* not used */
if (nsize == 0) { if (nsize == 0) {
free(ptr); free(ptr);
@ -3534,7 +3470,7 @@ This function should not be called by a finalizer.
@APIEntry{lua_Alloc lua_getallocf (lua_State *L, void **ud);| @APIEntry{lua_Alloc lua_getallocf (lua_State *L, void **ud);|
@apii{0,0,-} @apii{0,0,-}
Returns the @x{memory-allocator function} of a given state. Returns the @x{memory-allocation function} of a given state.
If @id{ud} is not @id{NULL}, Lua stores in @T{*ud} the If @id{ud} is not @id{NULL}, Lua stores in @T{*ud} the
opaque pointer given when the memory-allocator function was set. opaque pointer given when the memory-allocator function was set.
@ -3652,9 +3588,9 @@ because a pseudo-index is not an actual stack position.
The type of integers in Lua. The type of integers in Lua.
By default this type is @id{long long}, By default this type is @id{long long},
(usually a 64-bit two's complement integer), (usually a 64-bit two-complement integer),
but that can be changed to @id{long} or @id{int} but that can be changed to @id{long} or @id{int}
(usually a 32-bit two's complement integer). (usually a 32-bit two-complement integer).
(See @id{LUA_INT_TYPE} in @id{luaconf.h}.) (See @id{LUA_INT_TYPE} in @id{luaconf.h}.)
Lua also defines the constants Lua also defines the constants
@ -3891,7 +3827,7 @@ is a seed for the hashing of strings.
@apii{0,1,m} @apii{0,1,m}
Creates a new empty table and pushes it onto the stack. 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)}.
} }
@ -3915,12 +3851,8 @@ like any Lua object.
This function creates and pushes on the stack a new full userdata, This function creates and pushes on the stack a new full userdata,
with @id{nuvalue} associated Lua values, called @id{user values}, with @id{nuvalue} associated Lua values, called @id{user values},
plus an associated block of raw memory with @id{size} bytes. plus an associated block of raw memory with @id{size} bytes.
(The user values can be set and read with the functions
The user values can be set and read with the functions @Lid{lua_setiuservalue} and @Lid{lua_getiuservalue}.)
@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. The function returns the address of the block of memory.
Lua ensures that this address is valid as long as Lua ensures that this address is valid as long as
@ -3962,8 +3894,8 @@ this confuses the next call to @Lid{lua_next}.
This function may raise an error if the given key This function may raise an error if the given key
is neither @nil nor present in the table. is neither @nil nor present in the table.
See function @Lid{next} for the caveats of modifying
See function @Lid{next} for more details about the traversal. the table during its traversal.
} }
@ -3992,8 +3924,8 @@ This macro may evaluate its arguments more than once.
} }
@APIEntry{unsigned lua_numbertocstring (lua_State *L, int idx, @APIEntry{unsigned (lua_numbertocstring) (lua_State *L, int idx,
char *buff);| char *buff);|
@apii{0,0,-} @apii{0,0,-}
Converts the number at acceptable index @id{idx} to a string Converts the number at acceptable index @id{idx} to a string
@ -4118,7 +4050,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);| const char *s, size_t len, lua_Alloc falloc, void *ud);|
@apii{0,1,m} @apii{0,1,m}
@ -4141,18 +4073,21 @@ the string @id{s} as the block,
the length plus one (to account for the ending zero) as the old size, the length plus one (to account for the ending zero) as the old size,
and 0 as the new 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, Even when using an external buffer,
Lua still has to allocate a header for the string. Lua still has to allocate a header for the string.
In case of a memory-allocation error, In case of a memory-allocation error,
Lua will call @id{falloc} before raising the 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, ...);| @APIEntry{const char *lua_pushfstring (lua_State *L, const char *fmt, ...);|
@apii{0,1,v} @apii{0,1,m}
Pushes onto the stack a formatted string Pushes onto the stack a formatted string
and returns a pointer to this string @see{constchar}. and returns a pointer to this string @see{constchar}.
@ -4172,9 +4107,6 @@ A conversion specifier (and its corresponding extra argument) can be
Every occurrence of @Char{%} in the string @id{fmt} Every occurrence of @Char{%} in the string @id{fmt}
must form a valid conversion specifier. 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);| @APIEntry{void lua_pushglobaltable (lua_State *L);|
@ -4207,7 +4139,7 @@ light userdata with the same @N{C address}.
} }
@APIEntry{const char *lua_pushliteral (lua_State *L, const char *s);| @APIEntry{const char *lua_pushliteral (lua_State *L, const char *s);|
@apii{0,1,v} @apii{0,1,m}
This macro is equivalent to @Lid{lua_pushstring}, This macro is equivalent to @Lid{lua_pushstring},
but should be used only when @id{s} is a literal string. but should be used only when @id{s} is a literal string.
@ -4216,7 +4148,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);| @APIEntry{const char *lua_pushlstring (lua_State *L, const char *s, size_t len);|
@apii{0,1,v} @apii{0,1,m}
Pushes the string pointed to by @id{s} with size @id{len} Pushes the string pointed to by @id{s} with size @id{len}
onto the stack. onto the stack.
@ -4228,9 +4160,6 @@ including @x{embedded zeros}.
Returns a pointer to the internal copy of the string @see{constchar}. 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);| @APIEntry{void lua_pushnil (lua_State *L);|
@ -4448,7 +4377,7 @@ Starts and resumes a coroutine in the given thread @id{L}.
To start a coroutine, To start a coroutine,
you push the main function plus any arguments you push the main function plus any arguments
onto the empty stack of the thread. 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. with @id{nargs} being the number of arguments.
The function returns when the coroutine suspends, The function returns when the coroutine suspends,
finishes its execution, or raises an unprotected error. finishes its execution, or raises an unprotected error.
@ -4628,7 +4557,7 @@ You can resume threads with status @Lid{LUA_OK}
} }
@APIEntry{size_t lua_stringtonumber (lua_State *L, const char *s);| @APIEntry{size_t lua_stringtonumber (lua_State *L, const char *s);|
@apii{0,0|1,-} @apii{0,1,-}
Converts the zero-terminated string @id{s} to a number, Converts the zero-terminated string @id{s} to a number,
pushes that number into the stack, pushes that number into the stack,
@ -4958,7 +4887,7 @@ Lua calls the given @x{continuation function} @id{k} to continue
the execution of the @N{C function} that yielded @see{continuations}. the execution of the @N{C function} that yielded @see{continuations}.
This continuation function receives the same stack This continuation function receives the same stack
from the previous function, from the previous function,
with all the results (@id{nresults}) removed and with the @id{n} results removed and
replaced by the arguments passed to @Lid{lua_resume}. replaced by the arguments passed to @Lid{lua_resume}.
Moreover, Moreover,
the continuation function receives the value @id{ctx} the continuation function receives the value @id{ctx}
@ -5048,7 +4977,7 @@ the function was defined in a string where
} }
@item{@id{srclen}| @item{@id{srclen}|
the length of the string @id{source}. The length of the string @id{source}.
} }
@item{@id{short_src}| @item{@id{short_src}|
@ -5090,8 +5019,8 @@ then @id{name} is set to @id{NULL}.
@item{@id{namewhat}| @item{@id{namewhat}|
explains the @T{name} field. explains the @T{name} field.
The value of @T{namewhat} can be The value of @T{namewhat} can be
@T{"global"}, @T{"local"}, @T{"upvalue"}, @T{"global"}, @T{"local"}, @T{"method"},
@T{"field"}, @T{""} (the empty string), plus some other options, @T{"field"}, @T{"upvalue"}, or @T{""} (the empty string),
according to how the function was called. according to how the function was called.
(Lua uses the empty string when no other option seems to apply.) (Lua uses the empty string when no other option seems to apply.)
} }
@ -5212,7 +5141,7 @@ running at the given level;
} }
@item{@Char{S}| @item{@Char{S}|
fills in the fields @id{source}, @id{srclen}, @id{short_src}, fills in the fields @id{source}, @id{short_src},
@id{linedefined}, @id{lastlinedefined}, and @id{what}; @id{linedefined}, @id{lastlinedefined}, and @id{what};
} }
@ -5388,9 +5317,7 @@ Returns @id{NULL} (and pops nothing)
when the index is greater than when the index is greater than
the number of active local variables. 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.
} }
@ -5457,7 +5384,7 @@ the auxiliary library provides higher-level functions for some
common tasks. common tasks.
All functions and types from the auxiliary library All functions and types from the auxiliary library
are defined in the header file @id{lauxlib.h} and are defined in header file @id{lauxlib.h} and
have a prefix @id{luaL_}. have a prefix @id{luaL_}.
All functions in the auxiliary library are built on All functions in the auxiliary library are built on
@ -5601,7 +5528,7 @@ Its pattern of use is as follows:
@item{First declare a variable @id{b} of type @Lid{luaL_Buffer}.} @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{ @item{
Then add string pieces to the buffer calling any of Then add string pieces to the buffer calling any of
@ -5622,12 +5549,12 @@ you can use the buffer like this:
@item{First declare a variable @id{b} of type @Lid{luaL_Buffer}.} @item{First declare a variable @id{b} of type @Lid{luaL_Buffer}.}
@item{Then initialize it and preallocate a space of @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{Then produce the string into that space.}
@item{ @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 where @id{sz} is the total size of the resulting string
copied into that space (which may be less than or copied into that space (which may be less than or
equal to the preallocated size). equal to the preallocated size).
@ -6020,8 +5947,9 @@ it does not run it.
@apii{0,0,-} @apii{0,0,-}
Returns a value with a weak attempt for randomness. Returns a value with a weak attempt for randomness.
The parameter @id{L} can be @id{NULL} (It produces that value based on the current date and time
if there is no Lua state available. and the address of an internal variable,
in case the machine has Address Space Layout Randomization.)
} }
@ -6071,23 +5999,14 @@ In both cases,
the function pushes onto the stack the final value associated the function pushes onto the stack the final value associated
with @id{tname} in the registry. with @id{tname} in the registry.
Usage note: Beware the use of the return value of this function
to conditionally initialize the new metatable
(e.g., by adding metamethods to it).
If the initialization raises an error,
the metatable will not be properly initialized,
but a subsequent execution of that code will detect that the
metatable already exists and then skip the initialization.
} }
@APIEntry{lua_State *luaL_newstate (void);| @APIEntry{lua_State *luaL_newstate (void);|
@apii{0,0,-} @apii{0,0,-}
Creates a new Lua state. Creates a new Lua state.
It calls @Lid{lua_newstate} with @Lid{luaL_alloc} as It calls @Lid{lua_newstate} with an
the allocator function and the result of @T{luaL_makeseed(NULL)} allocator based on the @N{ISO C} allocation functions
as the seed,
and then sets a warning function and a panic function @see{C-error} and then sets a warning function and a panic function @see{C-error}
that print messages to the standard error output. that print messages to the standard error output.
@ -6240,7 +6159,7 @@ You should not manually set integer keys in the table
after the first use of @Lid{luaL_ref}. after the first use of @Lid{luaL_ref}.
You can retrieve an object referred by the reference @id{r} 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. The function @Lid{luaL_unref} frees a reference.
If the object on the top of the stack is @nil, If the object on the top of the stack is @nil,
@ -6311,15 +6230,6 @@ 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{ @APIEntry{
typedef struct luaL_Stream { typedef struct luaL_Stream {
FILE *f; FILE *f;
@ -6518,7 +6428,7 @@ the host program can call the function @Lid{luaL_openlibs}.
Alternatively, Alternatively,
the host can select which libraries to open, the host can select which libraries to open,
by using @Lid{luaL_openselectedlibs}. by using @Lid{luaL_openselectedlibs}.
Both functions are declared in the header file @id{lualib.h}. Both functions are defined in the header file @id{lualib.h}.
@index{lualib.h} @index{lualib.h}
The stand-alone interpreter @id{lua} @see{lua-sa} The stand-alone interpreter @id{lua} @see{lua-sa}
@ -6665,7 +6575,7 @@ The call always returns the previous value of the parameter.
If the call does not give a new value, If the call does not give a new value,
the value is left unchanged. the value is left unchanged.
Lua stores these values in a compressed format, Lua rounds these values before storing them;
so, the value returned as the previous value may not be so, the value returned as the previous value may not be
exactly the last value set. exactly the last value set.
} }
@ -6679,10 +6589,10 @@ This function should not be called by a finalizer.
} }
@LibEntry{dofile ([filename])| @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, When called without arguments,
@id{dofile} executes the content of the standard input (@id{stdin}). @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 In case of errors, @id{dofile} propagates the error
to its caller. to its caller.
(That is, @id{dofile} does not run in protected mode.) (That is, @id{dofile} does not run in protected mode.)
@ -6831,18 +6741,19 @@ In particular, you may set existing fields to nil.
@LibEntry{pairs (t)| @LibEntry{pairs (t)|
If @id{t} has a metamethod @idx{__pairs}, If @id{t} has a metamethod @idx{__pairs},
calls it with @id{t} as argument and returns the first four calls it with @id{t} as argument and returns the first three
results from the call. results from the call.
Otherwise, Otherwise,
returns the @Lid{next} function, the table @id{t}, plus two @nil values, returns three values: the @Lid{next} function, the table @id{t}, and @nil,
so that the construction so that the construction
@verbatim{ @verbatim{
for k,v in pairs(t) do @rep{body} end for k,v in pairs(t) do @rep{body} end
} }
will iterate over all key@En{}value pairs of table @id{t}. will iterate over all key@En{}value pairs of table @id{t}.
See function @Lid{next} for more details about the traversal. See function @Lid{next} for the caveats of modifying
the table during its traversal.
} }
@ -7053,7 +6964,7 @@ in case of error
(either the original error that stopped the coroutine or (either the original error that stopped the coroutine or
errors in closing methods), errors in closing methods),
this function returns @false plus the error object; this function returns @false plus the error object;
otherwise it returns @true. otherwise ir returns @true.
} }
@ -7769,7 +7680,7 @@ If @id{j} is absent, then it is assumed to be equal to @num{-1}
In particular, In particular,
the call @T{string.sub(s,1,j)} returns a prefix of @id{s} the call @T{string.sub(s,1,j)} returns a prefix of @id{s}
with length @id{j}, 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} returns a suffix of @id{s}
with length @id{i}. with length @id{i}.
@ -8205,7 +8116,7 @@ the function returns @fail.
A negative @id{n} gets characters before position @id{i}. A negative @id{n} gets characters before position @id{i}.
The default for @id{i} is 1 when @id{n} is non-negative The default for @id{i} is 1 when @id{n} is non-negative
and @T{#s + 1} otherwise, 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. @id{n}-th character from the end of the string.
As a special case, As a special case,
@ -8258,7 +8169,7 @@ the table will have; its default is zero.
Inserts element @id{value} at position @id{pos} in @id{list}, Inserts element @id{value} at position @id{pos} in @id{list},
shifting up the elements 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}, 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 so that a call @T{table.insert(t,x)} inserts @id{x} at the end
of the list @id{t}. of the list @id{t}.
@ -8296,7 +8207,7 @@ Removes from @id{list} the element at position @id{pos},
returning the value of the removed element. returning the value of the removed element.
When @id{pos} is an integer between 1 and @T{#list}, When @id{pos} is an integer between 1 and @T{#list},
it shifts down the elements 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]}; and erases element @T{list[#list]};
The index @id{pos} can also be 0 when @T{#list} is 0, The index @id{pos} can also be 0 when @T{#list} is 0,
or @T{#list + 1}. or @T{#list + 1}.
@ -8426,17 +8337,6 @@ 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| @LibEntry{math.huge|
The float value @idx{HUGE_VAL}, The float value @idx{HUGE_VAL},
@ -8444,12 +8344,6 @@ 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])| @LibEntry{math.log (x [, base])|
Returns the logarithm of @id{x} in the given base. Returns the logarithm of @id{x} in the given base.
@ -8505,7 +8399,7 @@ Converts the angle @id{x} from degrees to radians.
When called without arguments, When called without arguments,
returns a pseudo-random float with uniform distribution 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}, When called with two integers @id{m} and @id{n},
@id{math.random} returns a pseudo-random integer @id{math.random} returns a pseudo-random integer
with uniform distribution in the range @M{[m, n]}. with uniform distribution in the range @M{[m, n]}.
@ -9132,7 +9026,6 @@ which automatically removes the file when the program ends.
This library provides This library provides
the functionality of the @link{debugI|debug interface} to Lua programs. the functionality of the @link{debugI|debug interface} to Lua programs.
You should exert care when using this library. You should exert care when using this library.
Several of its functions Several of its functions
violate basic assumptions about Lua code violate basic assumptions about Lua code
@ -9142,8 +9035,6 @@ that userdata metatables cannot be changed by Lua code;
that Lua programs do not crash) that Lua programs do not crash)
and therefore can compromise otherwise secure code. and therefore can compromise otherwise secure code.
Moreover, some functions in this library may be slow. 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 All functions in this library are provided
inside the @defid{debug} table. inside the @defid{debug} table.
@ -9229,10 +9120,6 @@ Compile-time constants may not appear in this listing,
if they were optimized away by the compiler. if they were optimized away by the compiler.
Negative indices refer to vararg arguments; Negative indices refer to vararg arguments;
@num{-1} is the first vararg argument. @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 The function returns @fail
if there is no variable with the given index, if there is no variable with the given index,
and raises an error when called with a level out of range. and raises an error when called with a level out of range.
@ -9622,9 +9509,6 @@ change between versions.
@item{ @item{
The word @Rw{global} is a reserved word. The word @Rw{global} is a reserved word.
Do not use it as a regular name. 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{ @item{
@ -9756,7 +9640,7 @@ and @bnfNter{LiteralString}, see @See{lexical}.)
@OrNL @Rw{local} @Rw{function} @bnfNter{Name} funcbody @OrNL @Rw{local} @Rw{function} @bnfNter{Name} funcbody
@OrNL @Rw{global} @Rw{function} @bnfNter{Name} funcbody @OrNL @Rw{global} @Rw{function} @bnfNter{Name} funcbody
@OrNL @Rw{local} attnamelist @bnfopt{@bnfter{=} explist} @OrNL @Rw{local} attnamelist @bnfopt{@bnfter{=} explist}
@OrNL @Rw{global} attnamelist @bnfopt{@bnfter{=} explist} @OrNL @Rw{global} attnamelist
@OrNL @Rw{global} @bnfopt{attrib} @bnfter{*} @OrNL @Rw{global} @bnfopt{attrib} @bnfter{*}
} }
@ -9818,10 +9702,8 @@ and @bnfNter{LiteralString}, see @See{lexical}.)
@producname{funcbody}@producbody{@bnfter{(} @bnfopt{parlist} @bnfter{)} block @Rw{end}} @producname{funcbody}@producbody{@bnfter{(} @bnfopt{parlist} @bnfter{)} block @Rw{end}}
@producname{parlist}@producbody{namelist @bnfopt{@bnfter{,} varargparam} @Or @producname{parlist}@producbody{namelist @bnfopt{@bnfter{,} @bnfter{...}}
varargparam} @Or @bnfter{...}}
@producname{varargparam}@producbody{@bnfter{...} @bnfopt{@bnfNter{Name}}}
@producname{tableconstructor}@producbody{@bnfter{@Open} @bnfopt{fieldlist} @bnfter{@Close}} @producname{tableconstructor}@producbody{@bnfter{@Open} @bnfopt{fieldlist} @bnfter{@Close}}

View file

@ -5,14 +5,10 @@
** **
** $ gcc -O2 -std=c99 -o lua onelua.c -lm ** $ gcc -O2 -std=c99 -o lua onelua.c -lm
** **
** or (for C89) ** or
** **
** $ gcc -O2 -std=c89 -DLUA_USE_C89 -o lua onelua.c -lm ** $ 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 */ /* default is to build the full interpreter */
@ -34,15 +30,7 @@
#define LUA_USE_LINUX #define LUA_USE_LINUX
#define LUA_USE_MACOSX #define LUA_USE_MACOSX
#define LUA_USE_POSIX #define LUA_USE_POSIX
#endif #define LUA_ANSI
/*
** Other specific features
*/
#if 0
#define LUA_32BITS
#define LUA_USE_C89
#endif #endif
@ -66,10 +54,12 @@
#include <string.h> #include <string.h>
#include <time.h> #include <time.h>
/* setup for luaconf.h */ /* setup for luaconf.h */
#define LUA_CORE #define LUA_CORE
#define LUA_LIB #define LUA_LIB
#define ltable_c
#define lvm_c
#include "luaconf.h" #include "luaconf.h"
/* do not export internal symbols */ /* do not export internal symbols */
@ -120,11 +110,6 @@
#include "linit.c" #include "linit.c"
#endif #endif
/* test library -- used only for internal development */
#if defined(LUA_DEBUG)
#include "ltests.c"
#endif
/* lua */ /* lua */
#ifdef MAKE_LUA #ifdef MAKE_LUA
#include "lua.c" #include "lua.c"

View file

@ -246,8 +246,7 @@ 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 LE 9 1, return 1"))
assert(not T.testC("compare EQ 9 9, 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), local a1,a3,a4 = setmetatable({1}, b),
setmetatable({3}, b), setmetatable({3}, b),
setmetatable({4}, b) setmetatable({4}, b)

View file

@ -308,11 +308,11 @@ else
_ENV.x, _ENV.y = nil _ENV.x, _ENV.y = nil
end end
_ENV = _G _ENV = _G
-- testing preload -- testing preload
do do
local p = package local p = package
package = {} package = {}
@ -331,26 +331,6 @@ do
assert(type(package.path) == "string") assert(type(package.path) == "string")
end 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('+') print('+')
end --] end --]
@ -467,7 +447,7 @@ do
end end
-- test of large float/integer indices -- test of large float/integer indices
-- compute maximum integer where all bits fit in a float -- compute maximum integer where all bits fit in a float
local maxint = math.maxinteger local maxint = math.maxinteger

View file

@ -4,7 +4,7 @@ local strsub = string.sub
local print = print local print = print
global none _ENV = nil
-- Try to convert a value to an integer, without assuming any coercion. -- Try to convert a value to an integer, without assuming any coercion.
local function toint (x) local function toint (x)

View file

@ -24,7 +24,7 @@ assert(not pcall(type))
-- testing local-function recursion -- testing local-function recursion
global fact = false global fact; fact = false
do do
local res = 1 local res = 1
local function fact (n) local function fact (n)
@ -65,7 +65,7 @@ a.b.c:f2('k', 12); assert(a.b.c.k == 12)
print('+') print('+')
global t = nil -- 'declare' t global t; t = nil -- 'declare' t
function f(a,b,c) local d = 'a'; t={a,b,c,d} end function f(a,b,c) local d = 'a'; t={a,b,c,d} end
f( -- this line change must be valid f( -- this line change must be valid
@ -372,32 +372,6 @@ do -- another bug (in 5.4.0)
end 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")) x = string.dump(load("x = 1; return x"))
a = assert(load(read1(x), nil, "b")) a = assert(load(read1(x), nil, "b"))
assert(a() == 1 and _G.x == 1) assert(a() == 1 and _G.x == 1)

View file

@ -702,9 +702,7 @@ else
assert(t.currentline == t.linedefined + 2) assert(t.currentline == t.linedefined + 2)
assert(not debug.getinfo(c, 1)) -- no other level assert(not debug.getinfo(c, 1)) -- no other level
assert(coroutine.resume(c)) -- run next line assert(coroutine.resume(c)) -- run next line
local n,v = debug.getlocal(c, 0, 2) -- check vararg table local n,v = debug.getlocal(c, 0, 2) -- check next local
assert(n == "(vararg table)" and v == nil)
local n,v = debug.getlocal(c, 0, 3) -- check next local
assert(n == "b" and v == 10) assert(n == "b" and v == 10)
v = {coroutine.resume(c)} -- finish coroutine v = {coroutine.resume(c)} -- finish coroutine
assert(v[1] == true and v[2] == 2 and v[3] == 3 and v[4] == undef) assert(v[1] == true and v[2] == 2 and v[3] == 3 and v[4] == undef)

View file

@ -356,8 +356,8 @@ function f(a,b)
global assert, g, string global assert, g, string
local _, y = debug.getlocal(1, 2) local _, y = debug.getlocal(1, 2)
assert(x == a and y == b) assert(x == a and y == b)
assert(debug.setlocal(2, 4, "pera") == "AA".."AA") assert(debug.setlocal(2, 3, "pera") == "AA".."AA")
assert(debug.setlocal(2, 5, "manga") == "B") assert(debug.setlocal(2, 4, "manga") == "B")
x = debug.getinfo(2) x = debug.getinfo(2)
assert(x.func == g and x.what == "Lua" and x.name == 'g' and assert(x.func == g and x.what == "Lua" and x.name == 'g' and
x.nups == 2 and string.find(x.source, "^@.*db%.lua$")) x.nups == 2 and string.find(x.source, "^@.*db%.lua$"))
@ -392,7 +392,7 @@ function g (...)
global * global *
local B = 13 local B = 13
global<const> assert global<const> assert
local x,y = debug.getlocal(1,6) local x,y = debug.getlocal(1,5)
assert(x == 'B' and y == 13) assert(x == 'B' and y == 13)
end end
end end
@ -458,8 +458,7 @@ local function collectlocals (level)
local tab = {} local tab = {}
for i = 1, math.huge do for i = 1, math.huge do
local n, v = debug.getlocal(level + 1, i) local n, v = debug.getlocal(level + 1, i)
if not (n and string.find(n, "^[a-zA-Z0-9_]+$") or if not (n and string.find(n, "^[a-zA-Z0-9_]+$")) then
n == "(vararg table)") then
break -- consider only real variables break -- consider only real variables
end end
tab[n] = v tab[n] = v
@ -726,9 +725,6 @@ assert(t.isvararg == false and t.nparams == 3 and t.nups == 0)
t = debug.getinfo(function (a,b,...) return t[a] end, "u") t = debug.getinfo(function (a,b,...) return t[a] end, "u")
assert(t.isvararg == true and t.nparams == 2 and t.nups == 1) 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 t = debug.getinfo(1) -- main
assert(t.isvararg == true and t.nparams == 0 and t.nups == 1 and assert(t.isvararg == true and t.nparams == 0 and t.nups == 1 and
debug.getupvalue(t.func, 1) == "_ENV") debug.getupvalue(t.func, 1) == "_ENV")

View file

@ -159,9 +159,6 @@ 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={13}; local bbbb=1; aaa[bbbb](3)", "number")
checkmessage("aaa=(1)..{}", "a table value") checkmessage("aaa=(1)..{}", "a table value")
checkmessage("local function foo (...t) return t.xx + 1 end; foo()",
"field 'xx'")
-- bug in 5.4.6 -- bug in 5.4.6
checkmessage("a = {_ENV = {}}; print(a._ENV.x + 1)", "field 'x'") checkmessage("a = {_ENV = {}}; print(a._ENV.x + 1)", "field 'x'")
@ -421,28 +418,28 @@ end
-- testing line error -- testing line error
local function lineerror (s, l, w) local function lineerror (s, l)
local err,msg = pcall(load(s)) local err,msg = pcall(load(s))
local line = tonumber(string.match(msg, ":(%d+):")) local line = tonumber(string.match(msg, ":(%d+):"))
assert((line == l or (not line and not l)) and string.find(msg, w)) assert(line == l or (not line and not l))
end end
lineerror("local a\n for i=1,'a' do \n print(i) \n end", 2, "limit") lineerror("local a\n for i=1,'a' do \n print(i) \n end", 2)
lineerror("\n local a \n for k,v in 3 \n do \n print(k) \n end", 3, "to call") lineerror("\n local a \n for k,v in 3 \n do \n print(k) \n end", 3)
lineerror("\n\n for k,v in \n 3 \n do \n print(k) \n end", 4, "to call") lineerror("\n\n for k,v in \n 3 \n do \n print(k) \n end", 4)
lineerror("function a.x.y ()\na=a+1\nend", 1, "index") lineerror("function a.x.y ()\na=a+1\nend", 1)
lineerror("a = \na\n+\n{}", 3, "arithmetic") lineerror("a = \na\n+\n{}", 3)
lineerror("a = \n3\n+\n(\n4\n/\nprint)", 6, "arithmetic") lineerror("a = \n3\n+\n(\n4\n/\nprint)", 6)
lineerror("a = \nprint\n+\n(\n4\n/\n7)", 3, "arithmetic") lineerror("a = \nprint\n+\n(\n4\n/\n7)", 3)
lineerror("a\n=\n-\n\nprint\n;", 3, "arithmetic") lineerror("a\n=\n-\n\nprint\n;", 3)
lineerror([[ lineerror([[
a a
( -- << ( -- <<
23) 23)
]], 2, "call") ]], 2)
lineerror([[ lineerror([[
local a = {x = 13} local a = {x = 13}
@ -452,7 +449,7 @@ x
( -- << ( -- <<
23 23
) )
]], 5, "call") ]], 5)
lineerror([[ lineerror([[
local a = {x = 13} local a = {x = 13}
@ -462,17 +459,17 @@ x
( (
23 + a 23 + a
) )
]], 6, "arithmetic") ]], 6)
local p = [[ local p = [[
function g() f() end function g() f() end
function f(x) error('a', XX) end function f(x) error('a', XX) end
g() g()
]] ]]
XX=3;lineerror((p), 3, "a") XX=3;lineerror((p), 3)
XX=0;lineerror((p), false, "a") XX=0;lineerror((p), false)
XX=1;lineerror((p), 2, "a") XX=1;lineerror((p), 2)
XX=2;lineerror((p), 1, "a") XX=2;lineerror((p), 1)
_G.XX, _G.g, _G.f = nil _G.XX, _G.g, _G.f = nil
@ -480,7 +477,7 @@ lineerror([[
local b = false local b = false
if not b then if not b then
error 'test' error 'test'
end]], 3, "test") end]], 3)
lineerror([[ lineerror([[
local b = false local b = false
@ -490,7 +487,7 @@ if not b then
error 'test' error 'test'
end end
end end
end]], 5, "test") end]], 5)
lineerror([[ lineerror([[
_ENV = 1 _ENV = 1
@ -498,7 +495,7 @@ global function foo ()
local a = 10 local a = 10
return a return a
end end
]], 2, "index") ]], 2)
-- bug in 5.4.0 -- bug in 5.4.0
@ -506,37 +503,17 @@ lineerror([[
local a = 0 local a = 0
local b = 1 local b = 1
local c = b % a local c = b % a
]], 3, "perform") ]], 3)
do do
-- Force a negative estimate for base line. Error in instruction 2 -- Force a negative estimate for base line. Error in instruction 2
-- (after VARARGPREP, GETGLOBAL), with first absolute line information -- (after VARARGPREP, GETGLOBAL), with first absolute line information
-- (forced by too many lines) in instruction 0. -- (forced by too many lines) in instruction 0.
local s = string.format("%s return __A.x", string.rep("\n", 300)) local s = string.format("%s return __A.x", string.rep("\n", 300))
lineerror(s, 301, "index") lineerror(s, 301)
end end
local function stxlineerror (s, l, w)
local err,msg = load(s)
local line = tonumber(string.match(msg, ":(%d+):"))
assert((line == l or (not line and not l)) and string.find(msg, w, 1, true))
end
stxlineerror([[
::L1::
::L1::
]], 2, "already defined")
stxlineerror([[
global none
local x = b
]], 2, "not declared")
stxlineerror([[
local <close> a, b
]], 1, "multiple")
if not _soft then if not _soft then
-- several tests that exhaust the Lua stack -- several tests that exhaust the Lua stack
collectgarbage() collectgarbage()
@ -712,26 +689,21 @@ end
-- testing syntax limits -- testing syntax limits
local function testrep (init, rep, close, repc, finalresult) local function testrep (init, rep, close, repc, finalresult)
local function gencode (n) local s = init .. string.rep(rep, 100) .. close .. string.rep(repc, 100)
return init .. string.rep(rep, n) .. close .. string.rep(repc, n) local res, msg = load(s)
end assert(res) -- 100 levels is OK
local res, msg = load(gencode(100)) -- 100 levels is OK
assert(res)
if (finalresult) then if (finalresult) then
assert(res() == finalresult) assert(res() == finalresult)
end end
local res, msg = load(gencode(500)) -- 500 levels not ok s = init .. string.rep(rep, 500)
local res, msg = load(s) -- 500 levels not ok
assert(not res and (string.find(msg, "too many") or assert(not res and (string.find(msg, "too many") or
string.find(msg, "overflow"))) string.find(msg, "overflow")))
end 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", ",a", "= 1", ",1") -- multiple assignment
testrep("local a; a=", "{", "0", "}") -- constructors testrep("local a; a=", "{", "0", "}")
testrep("return ", "(", "2", ")", 2) -- parentheses testrep("return ", "(", "2", ")", 2)
-- nested calls (a(a(a(a(...)))))
testrep("local function a (x) return x end; return ", "a(", "2.2", ")", 2.2) testrep("local function a (x) return x end; return ", "a(", "2.2", ")", 2.2)
testrep("", "do ", "", " end") testrep("", "do ", "", " end")
testrep("", "while a do ", "", " end") testrep("", "while a do ", "", " end")

View file

@ -390,18 +390,6 @@ do
for i=1, 10 do t[i] = 1 end for i=1, 10 do t[i] = 1 end
end end
do -- bug since 5.4
local parent = {}
parent.__newindex = parent
collectgarbage()
local child = setmetatable({}, parent)
child.__newindex = {x = "hello"}
collectgarbage("step")
assert(parent.__newindex.x == "hello")
end
-- concat metamethod x numbers (bug in 5.1.1) -- concat metamethod x numbers (bug in 5.1.1)
c = {} c = {}
local x local x

View file

@ -715,7 +715,7 @@ do
end end
if T and T.nonblock and not _port then if T and T.nonblock then
print("testing failed write") print("testing failed write")
-- unable to write anything to /dev/full -- unable to write anything to /dev/full
@ -840,7 +840,7 @@ assert(os.date("!\0\0") == "\0\0")
local x = string.rep("a", 10000) local x = string.rep("a", 10000)
assert(os.date(x) == x) assert(os.date(x) == x)
local t = os.time() local t = os.time()
global D = os.date("*t", t) global D; D = os.date("*t", t)
assert(os.date(string.rep("%d", 1000), t) == assert(os.date(string.rep("%d", 1000), t) ==
string.rep(os.date("%d", t), 1000)) string.rep(os.date("%d", t), 1000))
assert(os.date(string.rep("%", 200)) == string.rep("%", 100)) assert(os.date(string.rep("%", 200)) == string.rep("%", 100))
@ -854,13 +854,6 @@ local function checkDateTable (t)
_G.D = nil _G.D = nil
end end
do -- testing mktime returning -1
local t = os.date("*t", -1)
assert(os.time(t) == -1)
end
checkDateTable(os.time()) checkDateTable(os.time())
if not _port then if not _port then
-- assume that time_t can represent these values -- assume that time_t can represent these values

View file

@ -707,46 +707,4 @@ end
collectgarbage(oldmode) 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') print('OK')

View file

@ -162,51 +162,6 @@ end
assert(collectgarbage'isrunning') assert(collectgarbage'isrunning')
do
-- bug in 5.0: when computing whether it should return from gen-major
-- to gen-minor, the difference between the total memory and the
-- previous total memory can be negative, which results in that
-- negative value being left-shifted (UB)
local lim = 1e6
-- make major collections non-incremental
local oldsm = collectgarbage("param", "stepmul", 0)
-- make "majorminor" large enough to force a left-shift
-- when applying the parameter (internal details)
local oldmm = collectgarbage("param", "majorminor", 2000)
collectgarbage(); collectgarbage()
assert(not T or T.gcquery() == "genminor")
local M = collectgarbage"count" * 1024
-- create a large table
local t = {}
for i = 1, lim do t[i] = true end
assert(collectgarbage"count" * 1024 > M + lim * string.packsize"j")
-- force collector to "generational major" mode, doing several
-- minor collections that recover no memory
collectgarbage"step"; collectgarbage"step"; collectgarbage"step"
assert(not T or T.gcquery() == "genmajor")
-- shrink the table
for i = 1, lim do t[i] = nil end
t[2 * lim] = true
assert(collectgarbage"count" < M * 5/4)
-- bug was here, an assert violation when checking whether to
-- return to 'genminor'
collectgarbage"step"
-- restore previous parameters
collectgarbage("param", "stepmul", oldsm)
collectgarbage("param", "majorminor", oldmm)
end
do print"testing stop-the-world collection" do print"testing stop-the-world collection"
local step = collectgarbage("param", "stepsize", 0); local step = collectgarbage("param", "stepsize", 0);
collectgarbage("incremental") collectgarbage("incremental")
@ -221,20 +176,6 @@ do print"testing stop-the-world collection"
assert(collectgarbage("param", "stepsize") == step) assert(collectgarbage("param", "stepsize") == step)
end 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) collectgarbage(oldmode)
print('OK') print('OK')

View file

@ -293,7 +293,6 @@ end
foo() foo()
-------------------------------------------------------------------------- --------------------------------------------------------------------------
-- check for compilation errors
local function checkerr (code, err) local function checkerr (code, err)
local st, msg = load(code) local st, msg = load(code)
assert(not st and string.find(msg, err)) assert(not st and string.find(msg, err))
@ -381,7 +380,7 @@ do
global * global *
Y = x + Y Y = x + Y
assert(_ENV.Y == 20) assert(_ENV.Y == 20)
Y = nil
end end
@ -412,66 +411,5 @@ do -- mixing lots of global/local declarations
_ENV.x200 = nil _ENV.x200 = nil
end end
do print "testing initialization in global declarations"
global<const> 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<const> 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' print'OK'

View file

@ -1,8 +1,6 @@
-- $Id: testes/heavy.lua,v $ -- $Id: testes/heavy.lua,v $
-- See Copyright Notice in file lua.h -- See Copyright Notice in file lua.h
global <const> *
local function teststring () local function teststring ()
print("creating a string too long") print("creating a string too long")
do do
@ -49,9 +47,9 @@ local function loadrep (x, what)
end end
local function controlstruct () function controlstruct ()
print("control structure too long") print("control structure too long")
local lim = ((1 << 24) - 2) // 4 local lim = ((1 << 24) - 2) // 3
local s = string.rep("a = a + 1\n", lim) local s = string.rep("a = a + 1\n", lim)
s = "while true do " .. s .. "end" s = "while true do " .. s .. "end"
assert(load(s)) assert(load(s))
@ -65,7 +63,7 @@ local function controlstruct ()
end end
local function manylines () function manylines ()
print("loading chunk with too many lines") print("loading chunk with too many lines")
local st, msg = loadrep("\n", "lines") local st, msg = loadrep("\n", "lines")
assert(not st and string.find(msg, "too many lines")) assert(not st and string.find(msg, "too many lines"))
@ -73,7 +71,7 @@ local function manylines ()
end end
local function hugeid () function hugeid ()
print("loading chunk with huge identifier") print("loading chunk with huge identifier")
local st, msg = loadrep("a", "chars") local st, msg = loadrep("a", "chars")
assert(not st and assert(not st and
@ -82,7 +80,7 @@ local function hugeid ()
print('+') print('+')
end end
local function toomanyinst () function toomanyinst ()
print("loading chunk with too many instructions") print("loading chunk with too many instructions")
local st, msg = loadrep("a = 10; ", "instructions") local st, msg = loadrep("a = 10; ", "instructions")
print('+') print('+')
@ -109,7 +107,7 @@ local function loadrepfunc (prefix, f)
end end
local function toomanyconst () function toomanyconst ()
print("loading function with too many constants") print("loading function with too many constants")
loadrepfunc("function foo () return {0,", loadrepfunc("function foo () return {0,",
function (n) function (n)
@ -128,7 +126,7 @@ local function toomanyconst ()
end end
local function toomanystr () function toomanystr ()
local a = {} local a = {}
local st, msg = pcall(function () local st, msg = pcall(function ()
for i = 1, math.huge do for i = 1, math.huge do
@ -146,7 +144,7 @@ local function toomanystr ()
end end
local function toomanyidx () function toomanyidx ()
local a = {} local a = {}
local st, msg = pcall(function () local st, msg = pcall(function ()
for i = 1, math.huge do for i = 1, math.huge do

View file

@ -1,7 +1,7 @@
#include "lua.h" #include "lua.h"
/* function from lib1.c */ /* function from lib1.c */
LUAMOD_API int lib1_export (lua_State *L); int lib1_export (lua_State *L);
LUAMOD_API int luaopen_lib11 (lua_State *L) { LUAMOD_API int luaopen_lib11 (lua_State *L) {
return lib1_export(L); return lib1_export(L);

View file

@ -1,7 +1,3 @@
/* implementation for lib2-v2 */
#include <string.h>
#include "lua.h" #include "lua.h"
#include "lauxlib.h" #include "lauxlib.h"
@ -12,54 +8,8 @@ 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[] = { static const struct luaL_Reg funcs[] = {
{"id", id}, {"id", id},
{"newstr", newstr},
{NULL, NULL} {NULL, NULL}
}; };
@ -68,7 +18,6 @@ LUAMOD_API int luaopen_lib2 (lua_State *L) {
lua_settop(L, 2); lua_settop(L, 2);
lua_setglobal(L, "y"); /* y gets 2nd parameter */ lua_setglobal(L, "y"); /* y gets 2nd parameter */
lua_setglobal(L, "x"); /* x gets 1st parameter */ lua_setglobal(L, "x"); /* x gets 1st parameter */
initstr(L);
luaL_newlib(L, funcs); luaL_newlib(L, funcs);
return 1; return 1;
} }

View file

@ -5,7 +5,7 @@ LUA_DIR = ../../
CC = gcc CC = gcc
# compilation should generate Dynamic-Link Libraries # compilation should generate Dynamic-Link Libraries
CFLAGS = -Wall -O2 -I$(LUA_DIR) -fPIC -shared CFLAGS = -Wall -std=c99 -O2 -I$(LUA_DIR) -fPIC -shared
# libraries used by the tests # libraries used by the tests
all: lib1.so lib11.so lib2.so lib21.so lib2-v2.so all: lib1.so lib11.so lib2.so lib21.so lib2-v2.so

View file

@ -310,7 +310,8 @@ do -- testing presence of second argument
local function foo (howtoclose, obj, n) local function foo (howtoclose, obj, n)
local ca -- copy of 'a' visible inside its close metamethod local ca -- copy of 'a' visible inside its close metamethod
do do
local a <close> = func2close(function (...t) local a <close> = func2close(function (...)
local t = table.pack(...)
assert(select("#", ...) == n) assert(select("#", ...) == n)
assert(t.n == n and t[1] == ca and (t.n < 2 or t[2] == obj)) 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" ca = 15 -- final value to be returned if howtoclose=="scope"
@ -910,7 +911,8 @@ do
local extrares -- result from extra yield (if any) local extrares -- result from extra yield (if any)
local function check (body, extra, ...t) local function check (body, extra, ...)
local t = table.pack(...) -- expected returns
local co = coroutine.wrap(body) local co = coroutine.wrap(body)
if extra then if extra then
extrares = co() -- runs until first (extra) yield extrares = co() -- runs until first (extra) yield
@ -1179,25 +1181,6 @@ if rawget(_G, "T") then
end end
do
-- detail in scopes of variables in the loop of 'repeat-until'
local res = {}
local function foo (a)
repeat
local x
local i <close> = setmetatable({}, {__close = function ()
res[#res + 1] = debug.getlocal(2, 2) -- get 'x'
res[#res + 1] = debug.getlocal(2, 3) -- get 'i'
a = true
end})
until a
end
foo(false)
-- loop variables still in scope when closing 'i', both when 'repeat'
-- repeats and when 'repeat' stops.
assert(res[1] == "x" and res[2] == "i" and res[3] == "x" and res[4] == "i")
end
-- to-be-closed variables in generic for loops -- to-be-closed variables in generic for loops
do do

View file

@ -78,9 +78,6 @@ end
RUN('lua -v') 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)) print(string.format("(temporary program file used in these tests: %s)", prog))
-- running stdin as a file -- running stdin as a file
@ -93,7 +90,7 @@ prepfile[[
1, a 1, a
) )
]] ]]
RUN('lua - -- < %s > %s', prog, out) RUN('lua - < %s > %s', prog, out)
checkout("1\tnil\n") checkout("1\tnil\n")
RUN('echo "print(10)\nprint(2)\n" | lua > %s', out) RUN('echo "print(10)\nprint(2)\n" | lua > %s', out)
@ -136,7 +133,7 @@ checkout("-h\n")
prepfile("print(package.path)") prepfile("print(package.path)")
-- test LUA_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") checkout("x\n")
-- test LUA_PATH_version -- test LUA_PATH_version
@ -170,9 +167,7 @@ checkout("10\n11\n")
-- test errors in LUA_INIT -- test errors in LUA_INIT
NoRun('LUA_INIT:1: msg', 'env LUA_INIT="error(\'msg\')" lua') NoRun('LUA_INIT:1: msg', 'env LUA_INIT="error(\'msg\')" lua')
-- test option '-E'
print("testing option '-E'")
local defaultpath, defaultCpath local defaultpath, defaultCpath
do do
@ -197,22 +192,6 @@ assert(not string.find(defaultpath, "xxx") and
string.find(defaultCpath, "lua")) 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 -- test replacement of ';;' to default path
local function convert (p) local function convert (p)
prepfile("print(package.path)") prepfile("print(package.path)")
@ -379,7 +358,7 @@ RUN([[lua -e"_PROMPT='' _PROMPT2=''" -i < %s > %s]], prog, out)
checkprogout("6\n10\n10\n\n") checkprogout("6\n10\n10\n\n")
prepfile("a = [[b\nc\nd\ne]]\na") 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") checkprogout("b\nc\nd\ne\n\n")
-- input interrupted in continuation line -- input interrupted in continuation line
@ -509,13 +488,12 @@ assert(not os.remove(out))
-- invalid options -- invalid options
NoRun("unrecognized option '-h'", "lua -h") NoRun("unrecognized option '-h'", "lua -h")
NoRun("unrecognized option '---'", "lua ---") 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 '-vv'", "lua -vv")
NoRun("unrecognized option '-iv'", "lua -iv") NoRun("unrecognized option '-iv'", "lua -iv")
NoRun("'-e' needs argument", "lua -e") NoRun("'-e' needs argument", "lua -e")
NoRun("syntax error", "lua -e a") NoRun("syntax error", "lua -e a")
NoRun("'-l' needs argument", "lua -l") NoRun("'-l' needs argument", "lua -l")
NoRun("-i", "lua -- -i") -- handles -i as a script name
if T then -- test library? if T then -- test library?

View file

@ -685,18 +685,6 @@ assert(eq(math.exp(0), 1))
assert(eq(math.sin(10), math.sin(10%(2*math.pi)))) 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.3e-2 ') == 1.3e-2)
assert(tonumber(' -1.00000000000001 ') == -1.00000000000001) assert(tonumber(' -1.00000000000001 ') == -1.00000000000001)

View file

@ -42,12 +42,8 @@ checkerr(MEMERRMSG, f)
T.alloccount() -- remove limit 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, -- test memory errors; increase limit for maximum memory by steps,
-- so that we get memory errors in all allocations of a given -- o that we get memory errors in all allocations of a given
-- task, until there is enough memory to complete the task without -- task, until there is enough memory to complete the task without
-- errors. -- errors.
local function testbytes (s, f) local function testbytes (s, f)
@ -57,7 +53,6 @@ local function testbytes (s, f)
local a,b = nil local a,b = nil
while true do while true do
collectgarbage(); collectgarbage() collectgarbage(); collectgarbage()
deep(4)
T.totalmem(M) T.totalmem(M)
a, b = T.testC("pcall 0 1 0; pushstatus; return 2", f) a, b = T.testC("pcall 0 1 0; pushstatus; return 2", f)
T.totalmem(0) -- remove limit T.totalmem(0) -- remove limit
@ -82,7 +77,6 @@ local function testalloc (s, f)
local a,b = nil local a,b = nil
while true do while true do
collectgarbage(); collectgarbage() collectgarbage(); collectgarbage()
deep(4)
T.alloccount(M) T.alloccount(M)
a, b = T.testC("pcall 0 1 0; pushstatus; return 2", f) a, b = T.testC("pcall 0 1 0; pushstatus; return 2", f)
T.alloccount() -- remove limit T.alloccount() -- remove limit
@ -93,19 +87,21 @@ local function testalloc (s, f)
M = M + 1 -- increase allocation limit M = M + 1 -- increase allocation limit
end end
print(string.format("minimum allocations for %s: %d allocations", s, M)) print(string.format("minimum allocations for %s: %d allocations", s, M))
return M return a
end end
local function testamem (s, f) local function testamem (s, f)
local aloc = testalloc(s, f) testalloc(s, f)
local res = testbytes(s, f) return testbytes(s, f)
return {aloc = aloc, res = res}
end end
local b = testamem("function call", function () return 10 end) -- doing nothing
assert(b.res == 10 and b.aloc == 0) b = testamem("doing nothing", function () return 10 end)
assert(b == 10)
-- testing memory errors when creating a new state
testamem("state creation", function () testamem("state creation", function ()
local st = T.newstate() local st = T.newstate()
@ -125,18 +121,6 @@ testamem("coroutine creation", function()
return coroutine.create(print) return coroutine.create(print)
end) 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 -- testing to-be-closed variables
testamem("to-be-closed variables", function() testamem("to-be-closed variables", function()
@ -166,9 +150,9 @@ local function expand (n,s)
e, s, expand(n-1,s), e) e, s, expand(n-1,s), e)
end end
G=0; collectgarbage() G=0; collectgarbage(); a =collectgarbage("count")
load(expand(20,"G=G+1"))() load(expand(20,"G=G+1"))()
assert(G==20); collectgarbage() assert(G==20); collectgarbage(); -- assert(gcinfo() <= a+1)
G = nil G = nil
testamem("running code on new thread", function () testamem("running code on new thread", function ()
@ -176,14 +160,6 @@ testamem("running code on new thread", function ()
end) 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 -- testing memory x compiler
testamem("loadstring", function () testamem("loadstring", function ()
@ -282,25 +258,6 @@ testamem("growing stack", function ()
return foo(100) return foo(100)
end) 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)
-- }================================================================== -- }==================================================================

View file

@ -345,18 +345,6 @@ do
end end
end end
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 = {} local nofind = {}
@ -905,18 +893,13 @@ local function foo1 (e,i)
if i <= e.n then return i,a[i] end if i <= e.n then return i,a[i] end
end end
local closed = false setmetatable(a, {__pairs = function (x) return foo, x, 0 end})
setmetatable(a, {__pairs = function (x)
local tbc = setmetatable({}, {__close = function () closed = true end})
return foo, x, 0, tbc
end})
local i = 0 local i = 0
for k,v in pairs(a) do for k,v in pairs(a) do
i = i + 1 i = i + 1
assert(k == i and v == k+1) assert(k == i and v == k+1)
end end
assert(closed) -- 'tbc' has been closed
a.n = 5 a.n = 5
a[3] = 30 a[3] = 30

View file

@ -347,16 +347,6 @@ do -- init parameter in gmatch
end 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') -- tests for `%f' (`frontiers')
assert(string.gsub("aaa aa a aaa a", "%f[%w]a", "x") == "xaa xa x xaa x") assert(string.gsub("aaa aa a aaa a", "%f[%w]a", "x") == "xaa xa x xaa x")

View file

@ -72,19 +72,6 @@ assert(a==1 and x==nil)
a,x = unpack({1,2}, 1, 1) a,x = unpack({1,2}, 1, 1)
assert(a==1 and x==nil) 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 do
local maxi = (1 << 31) - 1 -- maximum value for an int (usually) local maxi = (1 << 31) - 1 -- maximum value for an int (usually)
local mini = -(1 << 31) -- minimum value for an int (usually) local mini = -(1 << 31) -- minimum value for an int (usually)

View file

@ -540,23 +540,6 @@ else
assert(y == x) assert(y == x)
local z = T.externstr(x) -- external allocated long string local z = T.externstr(x) -- external allocated long string
assert(z == y) 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 end
print('OK') print('OK')

View file

@ -1,17 +1,12 @@
-- track collections -- track collections
local M = {} local M = {}
-- import list -- import list
local stderr, collectgarbage = io.stderr, collectgarbage local setmetatable, stderr, collectgarbage =
setmetatable, io.stderr, collectgarbage
-- the debug version of setmetatable does not create any object (such as _ENV = nil
-- a '__metatable' string), and so it is more appropriate to be used in
-- a finalizer
local setmetatable = require"debug".setmetatable
global none
local active = false local active = false

View file

@ -152,20 +152,11 @@ 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, "𦧺", 1, 2) checkerror("continuation byte", utf8.offset, "𦧺", 1, 2)
checkerror("continuation byte", utf8.offset, "\x80", 1) checkerror("continuation byte", utf8.offset, "\x80", 1)
checkerror("continuation byte", utf8.offset, "\x9c", -1)
-- error in indices for len -- error in indices for len
checkerror("out of bounds", utf8.len, "abc", 0, 2) checkerror("out of bounds", utf8.len, "abc", 0, 2)
checkerror("out of bounds", utf8.len, "abc", 1, 4) 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 s = "hello World"
local t = {string.byte(s, 1, -1)} local t = {string.byte(s, 1, -1)}
@ -238,18 +229,10 @@ s = "\0 \x7F\z
s = string.gsub(s, " ", "") s = string.gsub(s, " ", "")
check(s, {0,0x7F, 0x80,0x7FF, 0x800,0xFFFF, 0x10000,0x10FFFF}) 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 do
-- original UTF-8 values -- original UTF-8 values
local s = "\u{4000000}\u{7FFFFFFF}" local s = "\u{4000000}\u{7FFFFFFF}"
assert(s == "\xFC\x84\x80\x80\x80\x80\xFD\xBF\xBF\xBF\xBF\xBF") assert(#s == 12)
check(s, {0x4000000, 0x7FFFFFFF}, true) check(s, {0x4000000, 0x7FFFFFFF}, true)
s = "\u{200000}\u{3FFFFFF}" s = "\u{200000}\u{3FFFFFF}"
@ -265,10 +248,6 @@ local x = "日本語a-4\0éó"
check(x, {26085, 26412, 35486, 97, 45, 52, 0, 233, 243}) 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 -- Supplementary Characters
check("𣲷𠜎𠱓𡁻𠵼ab𠺢", check("𣲷𠜎𠱓𡁻𠵼ab𠺢",
{0x23CB7, 0x2070E, 0x20C53, 0x2107B, 0x20D7C, 0x61, 0x62, 0x20EA2,}) {0x23CB7, 0x2070E, 0x20C53, 0x2107B, 0x20D7C, 0x61, 0x62, 0x20EA2,})

View file

@ -3,12 +3,9 @@
print('testing vararg') print('testing vararg')
local function f (a, ...t) local function f (a, ...)
local x = {n = select('#', ...), ...} local x = {n = select('#', ...), ...}
assert(x.n == t.n) for i = 1, x.n do assert(a[i] == x[i]) end
for i = 1, x.n do
assert(a[i] == x[i] and x[i] == t[i])
end
return x.n return x.n
end end
@ -20,7 +17,7 @@ local function c12 (...)
return res, 2 return res, 2
end end
local function vararg (... t) return t end local function vararg (...) return {n = select('#', ...), ...} end
local call = function (f, args) return f(table.unpack(args, 1, args.n)) end local call = function (f, args) return f(table.unpack(args, 1, args.n)) end
@ -101,40 +98,8 @@ a,b,c,d,e = f(4)
assert(a==nil and b==nil and c==nil and d==nil and e==nil) 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 -- varargs for main chunks
local f = assert(load[[ return {...} ]]) local f = load[[ return {...} ]]
local x = f(2,3) local x = f(2,3)
assert(x[1] == 2 and x[2] == 3 and x[3] == undef) assert(x[1] == 2 and x[2] == 3 and x[3] == undef)
@ -182,79 +147,5 @@ do
local a, b = g() local a, b = g()
assert(a == nil and b == 2) assert(a == nil and b == 2)
end 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 <const> 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') print('OK')