Compare commits

...

33 Commits
=list ... v5.4

Author SHA1 Message Date
Roberto I
312b9efaa1 Small corrections in the manual 2026-08-07 15:59:21 -03:00
Roberto I
03aabbc636 New copyright year (2026) 2026-08-05 15:04:44 -03:00
Roberto I
2f8fe031c7 New release number (5.4.9) 2026-07-24 11:39:24 -03:00
Roberto I
f4f4968fd3 Bug: 'luaL_newmetatable' used in a wrong way
The call to 'luaL_newmetatable' in 'newbox' can leave an incomplete
metatable in the registry, if 'luaL_setfuncs' raises a memory error.
2026-07-24 11:38:45 -03:00
Roberto I
e282da498c Bug: wrong initialization in result from 'gmatch'
Function returned by 'string.gmatch' can be left in an inconsistent
state after an error.
2026-07-24 11:21:24 -03:00
Roberto I
b18086ea34 Bug: Loading a binary chunk does not run the GC 2026-07-24 11:16:38 -03:00
Roberto I
af19891bce Bug: shift overflow in utf-8 decode
An initial byte \xFF will ask for 7 continuation bytes, and then the
shift by (count * 5) will try to shift 35 bits.
2026-07-22 15:07:52 -03:00
Roberto I
8511e90b7d Bug: GC checks stack space before running finalizer
If some stack does not have a minimum available space, the GC defers
calling a finalizer until the next cycle. That avoids errors while
running a finalizer that the programmer cannot control.
2026-07-22 14:42:55 -03:00
Roberto I
0f781f836a Bug: Issues with write barrier for __newindex
In 'luaV_finishset', there is an update on a table that is a field on
another table. If the first table is the same as the one with the field
(e.g., after 't.__newindex = t'), the update can change the value on
that field (if the field being updated is '__newindex' itself). After
that, the barrier is called with the table stored in that field, which
is not the correct table anymore.
2026-07-13 15:55:36 -03:00
Roberto Ierusalimschy
934fdd481c Bug: Constructors with nils can overflow counters 2025-08-27 14:58:02 -03:00
Roberto Ierusalimschy
9ac9d23f41 Bug: error with option '--' without a script 2025-08-27 14:55:35 -03:00
Roberto Ierusalimschy
1b0f943da7 Bug: new metatable in weak table can fool the GC
All-weak tables are not being revisited after being visited during
propagation; if it gets a new metatable after that, the new metatable
may not be marked.
2025-06-16 16:33:02 -03:00
Roberto Ierusalimschy
6e22fedb74 New release number, updated copyright year 2025-05-21 13:52:56 -03:00
Roberto Ierusalimschy
267ef461d0 Bug: check for constructor overflow in [exp] fields
The check for constructor overflow was considering only fields with
explicit names, ignoring fields with syntax '[exp]=exp'.
2025-05-20 17:55:08 -03:00
Roberto Ierusalimschy
d1ee2a4deb Bug: Bad error message with fields of string _ENV
Errors with fields of the string _ENV (e.g., ("_ENV").u + 1) report
the error as comming from a global.
2025-05-15 15:14:56 -03:00
Roberto Ierusalimschy
3fe7be956f Bug: message handler can be overwritten
A __close metamethod can overwrite a message handler in the stack
when closing a thread or a state.
2025-03-17 16:14:17 -03:00
Roberto Ierusalimschy
983bc433e6 Bug: Use after free in 'luaV_finishset'
If a metatable is a weak table, its __newindex field could be collected
by an emergency collection while being used in 'luaV_finishset'. (This
bug has similarities with bug 5.3.2-1, fixed in commit a272fa66.)
2025-03-13 15:42:39 -03:00
Roberto Ierusalimschy
25da574fcb Bug: 'luaD_seterrorobj' should not raise errors
This function can be called unprotected, so it should not raise any
kind of errors. (It could raise a memory-allocation error when creating
a message).
2025-03-12 16:01:03 -03:00
Roberto Ierusalimschy
f5e55be2a0 Bug: Missing error status in panic function
'luaD_throw' may call 'luaE_resetthread', which returns an error code
but clears 'L->status'; so, 'luaD_throw' should set that status again.
2025-01-16 16:25:11 -03:00
Roberto Ierusalimschy
9f0c0fe0de Bug: Wrong limit for local variables in 16-bit systems
USHRT_MAX does not fit in an 'int' in 16-bit systems.
2024-09-10 11:34:42 -03:00
Roberto Ierusalimschy
782ef85b22 Bug: wrong code gen. for indices with comparisons
In function 'luaK_exp2val', used to generate code for indices: Macro
'hasjumps' does not consider the case when the whole expression is a
"jump" (a test). In all other of its uses, the surrounding code ensures
that the expression cannot be VJMP.
2024-08-17 12:37:04 -03:00
Roberto Ierusalimschy
30982bec96 Bug: Bad stack manipulation in 'multiline' (REPL)
'incomplete' was popping error message that should be used in case
there is no more lines to complete the input, that is, 'pushline'
returns NULL, due to end of file.
2024-07-05 14:31:07 -03:00
Roberto Ierusalimschy
1ab3208a1f 'lua.h' back to redundancy in version definitions
Several tools inspect 'lua.h' to extract version information, and
they assume the file will have some specific format.
2024-06-13 15:13:52 -03:00
Roberto Ierusalimschy
21ff8de33a Bug: Tricky _PROMPT may trigger undefined behavior 2024-06-05 13:37:16 -03:00
Roberto Ierusalimschy
7eb1ed21b7 More permissive use of 'errno'
Assume that no function will put garbage on errno (although ISO C allows
that). If any function during an operation set errno, and the operation
result in an error, assume that errno has something to say.
2024-06-05 11:50:42 -03:00
Roberto Ierusalimschy
2db966fcbf Bug: luaL_traceback may need more than 5 stack slots 2024-06-04 16:40:53 -03:00
Roberto Ierusalimschy
ae9a0cbbb4 Bug: overlapping assignments
ISO C forbids assignment of a union field to another field of the same
union.
2024-06-04 12:52:52 -03:00
Roberto Ierusalimschy
d5212c13b0 More disciplined use of 'errno'
Set errno to zero before calling any function where we may use its
errno, and check errno for zero before using it (as functions may
not set it even in error).
2024-06-04 12:48:29 -03:00
Roberto Ierusalimschy
e0efebdbe4 Detail in the manual
Function 'lua_toclose' can raise a non-memory error (but not a
memory error).
2024-05-14 17:59:54 -03:00
Roberto Ierusalimschy
e84f7bf198 Details
Typos in comments.
2024-05-13 13:23:45 -03:00
Roberto Ierusalimschy
dfbde4c7d5 Bug: Active-lines for stripped vararg functions
Lua seg. faults when asked to create the 'activelines' table for a
vararg function with no debug information.
2024-05-13 13:10:35 -03:00
Roberto Ierusalimschy
de794a6527 Towards release 5.4.7 2024-04-26 14:55:18 -03:00
Roberto Ierusalimschy
8b83417de9 Avoids a warning when lua_Number is 'float' 2024-02-07 14:17:03 -03:00
33 changed files with 345 additions and 141 deletions

3
lapi.c
View File

@@ -1089,6 +1089,7 @@ LUA_API int lua_load (lua_State *L, lua_Reader reader, void *data,
ZIO z; ZIO z;
int status; int 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);
@@ -1343,7 +1344,7 @@ void lua_warning (lua_State *L, const char *msg, int tocont) {
LUA_API void *lua_newuserdatauv (lua_State *L, size_t size, int nuvalue) { LUA_API void *lua_newuserdatauv (lua_State *L, size_t size, int nuvalue) {
Udata *u; Udata *u;
lua_lock(L); lua_lock(L);
api_check(L, 0 <= nuvalue && nuvalue < USHRT_MAX, "invalid value"); api_check(L, 0 <= nuvalue && nuvalue < SHRT_MAX, "invalid value");
u = luaS_newudata(L, size, nuvalue); u = luaS_newudata(L, size, nuvalue);
setuvalue(L, s2v(L->top.p), u); setuvalue(L, s2v(L->top.p), u);
api_incr_top(L); api_incr_top(L);

View File

@@ -80,6 +80,7 @@ static int pushglobalfuncname (lua_State *L, lua_Debug *ar) {
int top = lua_gettop(L); int top = lua_gettop(L);
lua_getinfo(L, "f", ar); /* push function */ lua_getinfo(L, "f", ar); /* push function */
lua_getfield(L, LUA_REGISTRYINDEX, LUA_LOADED_TABLE); lua_getfield(L, LUA_REGISTRYINDEX, LUA_LOADED_TABLE);
luaL_checkstack(L, 6, "not enough stack"); /* slots for 'findfield' */
if (findfield(L, top + 1, 2)) { if (findfield(L, top + 1, 2)) {
const char *name = lua_tostring(L, -1); const char *name = lua_tostring(L, -1);
if (strncmp(name, LUA_GNAME ".", 3) == 0) { /* name start with '_G.'? */ if (strncmp(name, LUA_GNAME ".", 3) == 0) { /* name start with '_G.'? */
@@ -249,11 +250,13 @@ LUALIB_API int luaL_fileresult (lua_State *L, int stat, const char *fname) {
return 1; return 1;
} }
else { else {
const char *msg;
luaL_pushfail(L); luaL_pushfail(L);
msg = (en != 0) ? strerror(en) : "(no extra info)";
if (fname) if (fname)
lua_pushfstring(L, "%s: %s", fname, strerror(en)); lua_pushfstring(L, "%s: %s", fname, msg);
else else
lua_pushstring(L, strerror(en)); lua_pushstring(L, msg);
lua_pushinteger(L, en); lua_pushinteger(L, en);
return 3; return 3;
} }
@@ -498,12 +501,25 @@ 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;
if (luaL_newmetatable(L, "_UBOX*")) /* creating metatable? */ getBoxMT(L);
luaL_setfuncs(L, boxmt, 0); /* set its metamethods */
lua_setmetatable(L, -2); lua_setmetatable(L, -2);
} }
@@ -732,9 +748,12 @@ static const char *getF (lua_State *L, void *ud, size_t *size) {
static int errfile (lua_State *L, const char *what, int fnameindex) { static int errfile (lua_State *L, const char *what, int fnameindex) {
const char *serr = strerror(errno); int err = errno;
const char *filename = lua_tostring(L, fnameindex) + 1; const char *filename = lua_tostring(L, fnameindex) + 1;
lua_pushfstring(L, "cannot %s %s: %s", what, filename, serr); if (err != 0)
lua_pushfstring(L, "cannot %s %s: %s", what, filename, strerror(err));
else
lua_pushfstring(L, "cannot %s %s", what, filename);
lua_remove(L, fnameindex); lua_remove(L, fnameindex);
return LUA_ERRFILE; return LUA_ERRFILE;
} }
@@ -787,6 +806,7 @@ LUALIB_API int luaL_loadfilex (lua_State *L, const char *filename,
} }
else { else {
lua_pushfstring(L, "@%s", filename); lua_pushfstring(L, "@%s", filename);
errno = 0;
lf.f = fopen(filename, "r"); lf.f = fopen(filename, "r");
if (lf.f == NULL) return errfile(L, "open", fnameindex); if (lf.f == NULL) return errfile(L, "open", fnameindex);
} }
@@ -796,6 +816,7 @@ LUALIB_API int luaL_loadfilex (lua_State *L, const char *filename,
if (c == LUA_SIGNATURE[0]) { /* binary file? */ if (c == LUA_SIGNATURE[0]) { /* binary file? */
lf.n = 0; /* remove possible newline */ lf.n = 0; /* remove possible newline */
if (filename) { /* "real" file? */ if (filename) { /* "real" file? */
errno = 0;
lf.f = freopen(filename, "rb", lf.f); /* reopen in binary mode */ lf.f = freopen(filename, "rb", lf.f); /* reopen in binary mode */
if (lf.f == NULL) return errfile(L, "reopen", fnameindex); if (lf.f == NULL) return errfile(L, "reopen", fnameindex);
skipcomment(lf.f, &c); /* re-read initial portion */ skipcomment(lf.f, &c); /* re-read initial portion */
@@ -803,6 +824,7 @@ LUALIB_API int luaL_loadfilex (lua_State *L, const char *filename,
} }
if (c != EOF) if (c != EOF)
lf.buff[lf.n++] = c; /* 'c' is the first character of the stream */ lf.buff[lf.n++] = c; /* 'c' is the first character of the stream */
errno = 0;
status = lua_load(L, getF, &lf, lua_tostring(L, -1), mode); status = lua_load(L, getF, &lf, lua_tostring(L, -1), mode);
readstatus = ferror(lf.f); readstatus = ferror(lf.f);
if (filename) fclose(lf.f); /* close file (even in case of errors) */ if (filename) fclose(lf.f); /* close file (even in case of errors) */
@@ -933,7 +955,7 @@ LUALIB_API const char *luaL_tolstring (lua_State *L, int idx, size_t *len) {
LUALIB_API void luaL_setfuncs (lua_State *L, const luaL_Reg *l, int nup) { LUALIB_API void luaL_setfuncs (lua_State *L, const luaL_Reg *l, int nup) {
luaL_checkstack(L, nup, "too many upvalues"); luaL_checkstack(L, nup, "too many upvalues");
for (; l->name != NULL; l++) { /* fill the table with given functions */ for (; l->name != NULL; l++) { /* fill the table with given functions */
if (l->func == NULL) /* place holder? */ if (l->func == NULL) /* placeholder? */
lua_pushboolean(L, 0); lua_pushboolean(L, 0);
else { else {
int i; int i;

View File

@@ -35,6 +35,7 @@
#define MAXREGS 255 #define MAXREGS 255
/* (note that expressions VJMP also have jumps.) */
#define hasjumps(e) ((e)->t != (e)->f) #define hasjumps(e) ((e)->t != (e)->f)
@@ -776,7 +777,8 @@ void luaK_dischargevars (FuncState *fs, expdesc *e) {
break; break;
} }
case VLOCAL: { /* already in a register */ case VLOCAL: { /* already in a register */
e->u.info = e->u.var.ridx; int temp = e->u.var.ridx;
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;
} }
@@ -984,7 +986,7 @@ void luaK_exp2anyregup (FuncState *fs, expdesc *e) {
** or it is a constant. ** or it is a constant.
*/ */
void luaK_exp2val (FuncState *fs, expdesc *e) { void luaK_exp2val (FuncState *fs, expdesc *e) {
if (hasjumps(e)) if (e->k == VJMP || hasjumps(e))
luaK_exp2anyreg(fs, e); luaK_exp2anyreg(fs, e);
else else
luaK_dischargevars(fs, e); luaK_dischargevars(fs, e);
@@ -1283,8 +1285,9 @@ void luaK_indexed (FuncState *fs, expdesc *t, expdesc *k) {
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) {
int temp = t->u.info; /* upvalue index */
lua_assert(isKstr(fs, k)); lua_assert(isKstr(fs, k));
t->u.ind.t = t->u.info; /* upvalue index */ t->u.ind.t = temp; /* (can't do a direct assignment; values overlap) */
t->u.ind.idx = k->u.info; /* literal short string */ t->u.ind.idx = k->u.info; /* literal short string */
t->k = VINDEXUP; t->k = VINDEXUP;
} }

View File

@@ -31,12 +31,15 @@
#define noLuaClosure(f) ((f) == NULL || (f)->c.tt == LUA_VCCL) #define LuaClosure(f) ((f) != NULL && (f)->c.tt == LUA_VLCL)
static const char *funcnamefromcall (lua_State *L, CallInfo *ci, static const char *funcnamefromcall (lua_State *L, CallInfo *ci,
const char **name); const char **name);
static const char strlocal[] = "local";
static const char strupval[] = "upvalue";
static int currentpc (CallInfo *ci) { static int currentpc (CallInfo *ci) {
lua_assert(isLua(ci)); lua_assert(isLua(ci));
@@ -254,7 +257,7 @@ LUA_API const char *lua_setlocal (lua_State *L, const lua_Debug *ar, int n) {
static void funcinfo (lua_Debug *ar, Closure *cl) { static void funcinfo (lua_Debug *ar, Closure *cl) {
if (noLuaClosure(cl)) { if (!LuaClosure(cl)) {
ar->source = "=[C]"; ar->source = "=[C]";
ar->srclen = LL("=[C]"); ar->srclen = LL("=[C]");
ar->linedefined = -1; ar->linedefined = -1;
@@ -288,29 +291,31 @@ 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 (noLuaClosure(f)) { if (!LuaClosure(f)) {
setnilvalue(s2v(L->top.p)); setnilvalue(s2v(L->top.p));
api_incr_top(L); api_incr_top(L);
} }
else { else {
int i;
TValue v;
const Proto *p = f->l.p; const Proto *p = f->l.p;
int currentline = p->linedefined; int currentline = p->linedefined;
Table *t = luaH_new(L); /* new table to store active lines */ Table *t = luaH_new(L); /* new table to store active lines */
sethvalue2s(L, L->top.p, t); /* push it on stack */ sethvalue2s(L, L->top.p, t); /* push it on stack */
api_incr_top(L); api_incr_top(L);
setbtvalue(&v); /* boolean 'true' to be the value of all indices */ if (p->lineinfo != NULL) { /* proto with debug information? */
if (!p->is_vararg) /* regular function? */ int i;
i = 0; /* consider all instructions */ TValue v;
else { /* vararg function */ setbtvalue(&v); /* boolean 'true' to be the value of all indices */
lua_assert(GET_OPCODE(p->code[0]) == OP_VARARGPREP); if (!p->is_vararg) /* regular function? */
currentline = nextline(p, currentline, 0); i = 0; /* consider all instructions */
i = 1; /* skip first instruction (OP_VARARGPREP) */ else { /* vararg function */
} lua_assert(GET_OPCODE(p->code[0]) == OP_VARARGPREP);
for (; i < p->sizelineinfo; i++) { /* for each instruction */ currentline = nextline(p, currentline, 0);
currentline = nextline(p, currentline, i); /* get its line */ i = 1; /* skip first instruction (OP_VARARGPREP) */
luaH_setint(L, t, currentline, &v); /* table[line] = true */ }
for (; i < p->sizelineinfo; i++) { /* for each instruction */
currentline = nextline(p, currentline, i); /* get its line */
luaH_setint(L, t, currentline, &v); /* table[line] = true */
}
} }
} }
} }
@@ -339,7 +344,7 @@ static int auxgetinfo (lua_State *L, const char *what, lua_Debug *ar,
} }
case 'u': { case 'u': {
ar->nups = (f == NULL) ? 0 : f->c.nupvalues; ar->nups = (f == NULL) ? 0 : f->c.nupvalues;
if (noLuaClosure(f)) { if (!LuaClosure(f)) {
ar->isvararg = 1; ar->isvararg = 1;
ar->nparams = 0; ar->nparams = 0;
} }
@@ -495,7 +500,7 @@ static const char *basicgetobjname (const Proto *p, int *ppc, int reg,
int pc = *ppc; int pc = *ppc;
*name = luaF_getlocalname(p, reg + 1, pc); *name = luaF_getlocalname(p, reg + 1, pc);
if (*name) /* is a local? */ if (*name) /* is a local? */
return "local"; return strlocal;
/* else try symbolic execution */ /* else try symbolic execution */
*ppc = pc = findsetreg(p, pc, reg); *ppc = pc = findsetreg(p, pc, reg);
if (pc != -1) { /* could find instruction? */ if (pc != -1) { /* could find instruction? */
@@ -510,7 +515,7 @@ static const char *basicgetobjname (const Proto *p, int *ppc, int reg,
} }
case OP_GETUPVAL: { case OP_GETUPVAL: {
*name = upvalname(p, GETARG_B(i)); *name = upvalname(p, GETARG_B(i));
return "upvalue"; return strupval;
} }
case OP_LOADK: return kname(p, GETARG_Bx(i), name); case OP_LOADK: return kname(p, GETARG_Bx(i), name);
case OP_LOADKX: return kname(p, GETARG_Ax(p->code[pc + 1]), name); case OP_LOADKX: return kname(p, GETARG_Ax(p->code[pc + 1]), name);
@@ -545,15 +550,21 @@ static void rkname (const Proto *p, int pc, Instruction i, const char **name) {
/* /*
** Check whether table being indexed by instruction 'i' is the ** Check whether table being indexed by instruction 'i' is the
** environment '_ENV' ** environment '_ENV'. If the table is an upvalue, get its name;
** otherwise, find some "name" for the table and check whether
** that name is the name of a local variable (and not, for instance,
** a string). Then check that, if there is a name, it is '_ENV'.
*/ */
static const char *isEnv (const Proto *p, int pc, Instruction i, int isup) { static const char *isEnv (const Proto *p, int pc, Instruction i, int isup) {
int t = GETARG_B(i); /* table index */ int t = GETARG_B(i); /* table index */
const char *name; /* name of indexed variable */ const char *name; /* name of indexed variable */
if (isup) /* is 't' an upvalue? */ if (isup) /* is 't' an upvalue? */
name = upvalname(p, t); name = upvalname(p, t);
else /* 't' is a register */ else { /* 't' is a register */
basicgetobjname(p, &pc, t, &name); const char *what = basicgetobjname(p, &pc, t, &name);
if (what != strlocal && what != strupval)
name = NULL; /* cannot be the variable _ENV */
}
return (name && strcmp(name, LUA_ENV) == 0) ? "global" : "field"; return (name && strcmp(name, LUA_ENV) == 0) ? "global" : "field";
} }
@@ -699,7 +710,7 @@ static const char *getupvalname (CallInfo *ci, const TValue *o,
for (i = 0; i < c->nupvalues; i++) { for (i = 0; i < c->nupvalues; i++) {
if (c->upvals[i]->v.p == o) { if (c->upvals[i]->v.p == o) {
*name = upvalname(c->p, i); *name = upvalname(c->p, i);
return "upvalue"; return strupval;
} }
} }
return NULL; return NULL;

38
ldo.c
View File

@@ -94,10 +94,6 @@ void luaD_seterrorobj (lua_State *L, int errcode, StkId oldtop) {
setsvalue2s(L, oldtop, G(L)->memerrmsg); /* reuse preregistered msg. */ setsvalue2s(L, oldtop, G(L)->memerrmsg); /* reuse preregistered msg. */
break; break;
} }
case LUA_ERRERR: {
setsvalue2s(L, oldtop, luaS_newliteral(L, "error in error handling"));
break;
}
case LUA_OK: { /* special case only for closing upvalues */ case LUA_OK: { /* special case only for closing upvalues */
setnilvalue(s2v(oldtop)); /* no error message */ setnilvalue(s2v(oldtop)); /* no error message */
break; break;
@@ -120,6 +116,7 @@ l_noret luaD_throw (lua_State *L, int errcode) {
else { /* thread has no error handler */ else { /* thread has no error handler */
global_State *g = G(L); global_State *g = G(L);
errcode = luaE_resetthread(L, errcode); /* close all upvalues */ errcode = luaE_resetthread(L, errcode); /* close all upvalues */
L->status = errcode;
if (g->mainthread->errorJmp) { /* main thread has a handler? */ if (g->mainthread->errorJmp) { /* main thread has a handler? */
setobjs2s(L, g->mainthread->top.p++, L->top.p - 1); /* copy error obj. */ setobjs2s(L, g->mainthread->top.p++, L->top.p - 1); /* copy error obj. */
luaD_throw(g->mainthread, errcode); /* re-throw in main thread */ luaD_throw(g->mainthread, errcode); /* re-throw in main thread */
@@ -198,6 +195,35 @@ static void correctstack (lua_State *L) {
/* some space for error handling */ /* some space for error handling */
#define ERRORSTACKSIZE (LUAI_MAXSTACK + 200) #define ERRORSTACKSIZE (LUAI_MAXSTACK + 200)
/* raise an error while running the message handler */
l_noret luaD_errerr (lua_State *L) {
TString *msg = luaS_newliteral(L, "error in error handling");
setsvalue2s(L, L->top.p, msg);
L->top.p++; /* assume EXTRA_STACK */
luaD_throw(L, LUA_ERRERR);
}
/*
** Check whether stacks have enough space to run a simple function (such
** as a finalizer): At least BASIC_STACK_SIZE in the Lua stack, two
** available CallInfos, and two "slots" in the C stack.
*/
int luaD_checkminstack (lua_State *L) {
if (getCcalls(L) >= LUAI_MAXCCALLS - 2)
return 0; /* not enough C-stack slots */
if (L->ci->next == NULL && luaE_extendCI(L, 0) == NULL)
return 0; /* unable to allocate first ci */
if (L->ci->next->next == NULL && luaE_extendCI(L, 0) == NULL)
return 0; /* unable to allocate second ci */
if (L->stack_last.p - L->top.p >= BASIC_STACK_SIZE)
return 1; /* enough (BASIC_STACK_SIZE) free slots in the Lua stack */
else /* try to grow stack to a size with enough free slots */
return luaD_growstack(L, BASIC_STACK_SIZE, 0);
}
/* /*
** Reallocate the stack to a new size, correcting all pointers into it. ** Reallocate the stack to a new size, correcting all pointers into it.
** 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
@@ -247,7 +273,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_throw(L, LUA_ERRERR); /* 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 < LUAI_MAXSTACK) { /* avoids arithmetic overflows */ else if (n < LUAI_MAXSTACK) { /* avoids arithmetic overflows */
@@ -496,7 +522,7 @@ void luaD_poscall (lua_State *L, CallInfo *ci, int nres) {
#define next_ci(L) (L->ci->next ? L->ci->next : luaE_extendCI(L)) #define next_ci(L) (L->ci->next ? L->ci->next : luaE_extendCI(L, 1))
l_sinline CallInfo *prepCallInfo (lua_State *L, StkId func, int nret, l_sinline CallInfo *prepCallInfo (lua_State *L, StkId func, int nret,

2
ldo.h
View File

@@ -60,6 +60,7 @@
/* type of protected functions, to be ran by 'runprotected' */ /* type of protected functions, to be ran by 'runprotected' */
typedef void (*Pfunc) (lua_State *L, void *ud); typedef void (*Pfunc) (lua_State *L, void *ud);
LUAI_FUNC l_noret luaD_errerr (lua_State *L);
LUAI_FUNC void luaD_seterrorobj (lua_State *L, int errcode, StkId oldtop); LUAI_FUNC void luaD_seterrorobj (lua_State *L, int errcode, StkId oldtop);
LUAI_FUNC int luaD_protectedparser (lua_State *L, ZIO *z, const char *name, LUAI_FUNC int luaD_protectedparser (lua_State *L, ZIO *z, const char *name,
const char *mode); const char *mode);
@@ -79,6 +80,7 @@ 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 void luaD_inctop (lua_State *L); LUAI_FUNC void luaD_inctop (lua_State *L);
LUAI_FUNC int luaD_checkminstack (lua_State *L);
LUAI_FUNC l_noret luaD_throw (lua_State *L, int errcode); LUAI_FUNC l_noret luaD_throw (lua_State *L, int errcode);
LUAI_FUNC int luaD_rawrunprotected (lua_State *L, Pfunc f, void *ud); LUAI_FUNC int luaD_rawrunprotected (lua_State *L, Pfunc f, void *ud);

17
lgc.c
View File

@@ -553,8 +553,12 @@ static lu_mem traversetable (global_State *g, Table *h) {
traverseweakvalue(g, h); traverseweakvalue(g, h);
else if (!weakvalue) /* strong values? */ else if (!weakvalue) /* strong values? */
traverseephemeron(g, h, 0); traverseephemeron(g, h, 0);
else /* all weak */ else { /* all weak */
linkgclist(h, g->allweak); /* nothing to traverse now */ if (g->gcstate == GCSpropagate)
linkgclist(h, g->grayagain); /* must visit again its metatable */
else
linkgclist(h, g->allweak); /* must clear collected entries */
}
} }
else /* not weak */ else /* not weak */
traversestrongtable(g, h); traversestrongtable(g, h);
@@ -1238,7 +1242,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->gcemergency) if (g->tobefnz != NULL && !g->gcemergency && luaD_checkminstack(L))
callallpendingfinalizers(L); callallpendingfinalizers(L);
} }
@@ -1628,11 +1632,12 @@ static lu_mem singlestep (lua_State *L) {
break; break;
} }
case GCScallfin: { /* call remaining finalizers */ case GCScallfin: { /* call remaining finalizers */
if (g->tobefnz && !g->gcemergency) { if (g->tobefnz && !g->gcemergency && luaD_checkminstack(L)) {
g->gcstopem = 0; /* ok collections during finalizers */ g->gcstopem = 0; /* ok collections during finalizers */
work = runafewfinalizers(L, GCFINMAX) * GCFINALIZECOST; work = runafewfinalizers(L, GCFINMAX) * GCFINALIZECOST;
} }
else { /* emergency mode or no more finalizers */ else { /* no more finalizers or emergency mode or no enough stack
to run finalizers */
g->gcstate = GCSpause; /* finish collection */ g->gcstate = GCSpause; /* finish collection */
work = 0; work = 0;
} }
@@ -1713,7 +1718,7 @@ static void fullinc (lua_State *L, global_State *g) {
/* finish any pending sweep phase to start a new cycle */ /* finish any pending sweep phase to start a new cycle */
luaC_runtilstate(L, bitmask(GCSpause)); luaC_runtilstate(L, bitmask(GCSpause));
luaC_runtilstate(L, bitmask(GCSpropagate)); /* start new cycle */ luaC_runtilstate(L, bitmask(GCSpropagate)); /* start new cycle */
g->gcstate = GCSenteratomic; /* go straight to atomic phase ??? */ g->gcstate = GCSenteratomic; /* go straight to atomic phase */
luaC_runtilstate(L, bitmask(GCScallfin)); /* run up to finalizers */ luaC_runtilstate(L, bitmask(GCScallfin)); /* run up to finalizers */
/* estimate must be correct after a full GC cycle */ /* estimate must be correct after a full GC cycle */
lua_assert(g->GCestimate == gettotalbytes(g)); lua_assert(g->GCestimate == gettotalbytes(g));

View File

@@ -245,8 +245,8 @@ static int f_gc (lua_State *L) {
*/ */
static int io_fclose (lua_State *L) { static int io_fclose (lua_State *L) {
LStream *p = tolstream(L); LStream *p = tolstream(L);
int res = fclose(p->f); errno = 0;
return luaL_fileresult(L, (res == 0), NULL); return luaL_fileresult(L, (fclose(p->f) == 0), NULL);
} }
@@ -272,6 +272,7 @@ static int io_open (lua_State *L) {
LStream *p = newfile(L); LStream *p = newfile(L);
const char *md = mode; /* to traverse/check mode */ const char *md = mode; /* to traverse/check mode */
luaL_argcheck(L, l_checkmode(md), 2, "invalid mode"); luaL_argcheck(L, l_checkmode(md), 2, "invalid mode");
errno = 0;
p->f = fopen(filename, mode); p->f = fopen(filename, mode);
return (p->f == NULL) ? luaL_fileresult(L, 0, filename) : 1; return (p->f == NULL) ? luaL_fileresult(L, 0, filename) : 1;
} }
@@ -292,6 +293,7 @@ static int io_popen (lua_State *L) {
const char *mode = luaL_optstring(L, 2, "r"); const char *mode = luaL_optstring(L, 2, "r");
LStream *p = newprefile(L); LStream *p = newprefile(L);
luaL_argcheck(L, l_checkmodep(mode), 2, "invalid mode"); luaL_argcheck(L, l_checkmodep(mode), 2, "invalid mode");
errno = 0;
p->f = l_popen(L, filename, mode); p->f = l_popen(L, filename, mode);
p->closef = &io_pclose; p->closef = &io_pclose;
return (p->f == NULL) ? luaL_fileresult(L, 0, filename) : 1; return (p->f == NULL) ? luaL_fileresult(L, 0, filename) : 1;
@@ -300,6 +302,7 @@ static int io_popen (lua_State *L) {
static int io_tmpfile (lua_State *L) { static int io_tmpfile (lua_State *L) {
LStream *p = newfile(L); LStream *p = newfile(L);
errno = 0;
p->f = tmpfile(); p->f = tmpfile();
return (p->f == NULL) ? luaL_fileresult(L, 0, NULL) : 1; return (p->f == NULL) ? luaL_fileresult(L, 0, NULL) : 1;
} }
@@ -567,6 +570,7 @@ static int g_read (lua_State *L, FILE *f, int first) {
int nargs = lua_gettop(L) - 1; int nargs = lua_gettop(L) - 1;
int n, success; int n, success;
clearerr(f); clearerr(f);
errno = 0;
if (nargs == 0) { /* no arguments? */ if (nargs == 0) { /* no arguments? */
success = read_line(L, f, 1); success = read_line(L, f, 1);
n = first + 1; /* to return 1 result */ n = first + 1; /* to return 1 result */
@@ -660,6 +664,7 @@ static int io_readline (lua_State *L) {
static int g_write (lua_State *L, FILE *f, int arg) { static int g_write (lua_State *L, FILE *f, int arg) {
int nargs = lua_gettop(L) - arg; int nargs = lua_gettop(L) - arg;
int status = 1; int status = 1;
errno = 0;
for (; nargs--; arg++) { for (; nargs--; arg++) {
if (lua_type(L, arg) == LUA_TNUMBER) { if (lua_type(L, arg) == LUA_TNUMBER) {
/* optimization: could be done exactly as for strings */ /* optimization: could be done exactly as for strings */
@@ -678,7 +683,8 @@ static int g_write (lua_State *L, FILE *f, int arg) {
} }
if (l_likely(status)) if (l_likely(status))
return 1; /* file handle already on stack top */ return 1; /* file handle already on stack top */
else return luaL_fileresult(L, status, NULL); else
return luaL_fileresult(L, status, NULL);
} }
@@ -703,6 +709,7 @@ static int f_seek (lua_State *L) {
l_seeknum offset = (l_seeknum)p3; l_seeknum offset = (l_seeknum)p3;
luaL_argcheck(L, (lua_Integer)offset == p3, 3, luaL_argcheck(L, (lua_Integer)offset == p3, 3,
"not an integer in proper range"); "not an integer in proper range");
errno = 0;
op = l_fseek(f, offset, mode[op]); op = l_fseek(f, offset, mode[op]);
if (l_unlikely(op)) if (l_unlikely(op))
return luaL_fileresult(L, 0, NULL); /* error */ return luaL_fileresult(L, 0, NULL); /* error */
@@ -719,19 +726,25 @@ static int f_setvbuf (lua_State *L) {
FILE *f = tofile(L); FILE *f = tofile(L);
int op = luaL_checkoption(L, 2, NULL, modenames); int op = luaL_checkoption(L, 2, NULL, modenames);
lua_Integer sz = luaL_optinteger(L, 3, LUAL_BUFFERSIZE); lua_Integer sz = luaL_optinteger(L, 3, LUAL_BUFFERSIZE);
int res = setvbuf(f, NULL, mode[op], (size_t)sz); int res;
errno = 0;
res = setvbuf(f, NULL, mode[op], (size_t)sz);
return luaL_fileresult(L, res == 0, NULL); return luaL_fileresult(L, res == 0, NULL);
} }
static int io_flush (lua_State *L) { static int io_flush (lua_State *L) {
return luaL_fileresult(L, fflush(getiofile(L, IO_OUTPUT)) == 0, NULL); FILE *f = getiofile(L, IO_OUTPUT);
errno = 0;
return luaL_fileresult(L, fflush(f) == 0, NULL);
} }
static int f_flush (lua_State *L) { static int f_flush (lua_State *L) {
return luaL_fileresult(L, fflush(tofile(L)) == 0, NULL); FILE *f = tofile(L);
errno = 0;
return luaL_fileresult(L, fflush(f) == 0, NULL);
} }
@@ -773,7 +786,7 @@ static const luaL_Reg meth[] = {
** metamethods for file handles ** metamethods for file handles
*/ */
static const luaL_Reg metameth[] = { static const luaL_Reg metameth[] = {
{"__index", NULL}, /* place holder */ {"__index", NULL}, /* placeholder */
{"__gc", f_gc}, {"__gc", f_gc},
{"__close", f_gc}, {"__close", f_gc},
{"__tostring", f_tostring}, {"__tostring", f_tostring},

View File

@@ -352,7 +352,7 @@ static lua_Number I2d (Rand64 x) {
SRand64 sx = (SRand64)(trim64(x) >> shift64_FIG); SRand64 sx = (SRand64)(trim64(x) >> shift64_FIG);
lua_Number res = (lua_Number)(sx) * scaleFIG; lua_Number res = (lua_Number)(sx) * scaleFIG;
if (sx < 0) if (sx < 0)
res += 1.0; /* correct the two's complement if negative */ res += l_mathop(1.0); /* correct the two's complement if negative */
lua_assert(0 <= res && res < 1); lua_assert(0 <= res && res < 1);
return res; return res;
} }

View File

@@ -155,6 +155,7 @@ static int os_execute (lua_State *L) {
static int os_remove (lua_State *L) { static int os_remove (lua_State *L) {
const char *filename = luaL_checkstring(L, 1); const char *filename = luaL_checkstring(L, 1);
errno = 0;
return luaL_fileresult(L, remove(filename) == 0, filename); return luaL_fileresult(L, remove(filename) == 0, filename);
} }
@@ -162,6 +163,7 @@ static int os_remove (lua_State *L) {
static int os_rename (lua_State *L) { static int os_rename (lua_State *L) {
const char *fromname = luaL_checkstring(L, 1); const char *fromname = luaL_checkstring(L, 1);
const char *toname = luaL_checkstring(L, 2); const char *toname = luaL_checkstring(L, 2);
errno = 0;
return luaL_fileresult(L, rename(fromname, toname) == 0, NULL); return luaL_fileresult(L, rename(fromname, toname) == 0, NULL);
} }

View File

@@ -198,7 +198,7 @@ static int new_localvar (LexState *ls, TString *name) {
checklimit(fs, dyd->actvar.n + 1 - fs->firstlocal, checklimit(fs, dyd->actvar.n + 1 - fs->firstlocal,
MAXVARS, "local variables"); MAXVARS, "local variables");
luaM_growvector(L, dyd->actvar.arr, dyd->actvar.n + 1, luaM_growvector(L, dyd->actvar.arr, dyd->actvar.n + 1,
dyd->actvar.size, Vardesc, USHRT_MAX, "local variables"); dyd->actvar.size, Vardesc, SHRT_MAX, "local variables");
var = &dyd->actvar.arr[dyd->actvar.n++]; var = &dyd->actvar.arr[dyd->actvar.n++];
var->vd.kind = VDKREG; /* default */ var->vd.kind = VDKREG; /* default */
var->vd.name = name; var->vd.name = name;
@@ -849,12 +849,11 @@ static void recfield (LexState *ls, ConsControl *cc) {
FuncState *fs = ls->fs; FuncState *fs = ls->fs;
int reg = ls->fs->freereg; int reg = ls->fs->freereg;
expdesc tab, key, val; expdesc tab, key, val;
if (ls->t.token == TK_NAME) { if (ls->t.token == TK_NAME)
checklimit(fs, cc->nh, MAX_INT, "items in a constructor");
codename(ls, &key); codename(ls, &key);
}
else /* ls->t.token == '[' */ else /* ls->t.token == '[' */
yindex(ls, &key); yindex(ls, &key);
checklimit(fs, cc->nh, MAX_INT, "items in a constructor");
cc->nh++; cc->nh++;
checknext(ls, '='); checknext(ls, '=');
tab = *cc->t; tab = *cc->t;
@@ -941,6 +940,8 @@ static void constructor (LexState *ls, expdesc *t) {
if (ls->t.token == '}') break; if (ls->t.token == '}') break;
closelistfield(fs, &cc); closelistfield(fs, &cc);
field(ls, &cc); field(ls, &cc);
checklimit(fs, cc.tostore + cc.na + cc.nh, INT_MAX/2,
"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);

View File

@@ -102,14 +102,19 @@ LUA_API int lua_setcstacklimit (lua_State *L, unsigned int limit) {
} }
CallInfo *luaE_extendCI (lua_State *L) { CallInfo *luaE_extendCI (lua_State *L, int err) {
CallInfo *ci; CallInfo *ci;
lua_assert(L->ci->next == NULL); ci = luaM_reallocvector(L, NULL, 0, 1, CallInfo);
ci = luaM_new(L, CallInfo); if (l_unlikely(ci == NULL)) { /* allocation failed? */
lua_assert(L->ci->next == NULL); if (err)
L->ci->next = ci; luaM_error(L); /* raise the error */
return NULL; /* else only report it */
}
ci->next = L->ci->next;
ci->previous = L->ci; ci->previous = L->ci;
ci->next = NULL; L->ci->next = ci;
if (ci->next)
ci->next->previous = ci;
ci->u.l.trap = 0; ci->u.l.trap = 0;
L->nci++; L->nci++;
return ci; return ci;
@@ -166,7 +171,7 @@ void luaE_checkcstack (lua_State *L) {
if (getCcalls(L) == LUAI_MAXCCALLS) if (getCcalls(L) == LUAI_MAXCCALLS)
luaG_runerror(L, "C stack overflow"); luaG_runerror(L, "C stack overflow");
else if (getCcalls(L) >= (LUAI_MAXCCALLS / 10 * 11)) else if (getCcalls(L) >= (LUAI_MAXCCALLS / 10 * 11))
luaD_throw(L, LUA_ERRERR); /* error while handling stack error */ luaD_errerr(L); /* error while handling stack error */
} }
@@ -272,7 +277,9 @@ static void close_state (lua_State *L) {
luaC_freeallobjects(L); /* just collect its objects */ luaC_freeallobjects(L); /* just collect its objects */
else { /* closing a fully built state */ else { /* closing a fully built state */
L->ci = &L->base_ci; /* unwind CallInfo list */ L->ci = &L->base_ci; /* unwind CallInfo list */
L->errfunc = 0; /* stack unwind can "throw away" the error function */
luaD_closeprotected(L, 1, LUA_OK); /* close all upvalues */ luaD_closeprotected(L, 1, LUA_OK); /* close all upvalues */
L->top.p = L->stack.p + 1; /* empty the stack to run finalizers */
luaC_freeallobjects(L); /* collect all objects */ luaC_freeallobjects(L); /* collect all objects */
luai_userstateclose(L); luai_userstateclose(L);
} }
@@ -328,6 +335,7 @@ int luaE_resetthread (lua_State *L, int status) {
if (status == LUA_YIELD) if (status == LUA_YIELD)
status = LUA_OK; status = LUA_OK;
L->status = LUA_OK; /* so it can run __close metamethods */ L->status = LUA_OK; /* so it can run __close metamethods */
L->errfunc = 0; /* stack unwind can "throw away" the error function */
status = luaD_closeprotected(L, 1, status); status = luaD_closeprotected(L, 1, status);
if (status != LUA_OK) /* errors? */ if (status != LUA_OK) /* errors? */
luaD_seterrorobj(L, status, L->stack.p + 1); luaD_seterrorobj(L, status, L->stack.p + 1);

View File

@@ -395,7 +395,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 CallInfo *luaE_extendCI (lua_State *L); LUAI_FUNC CallInfo *luaE_extendCI (lua_State *L, int err);
LUAI_FUNC void luaE_shrinkCI (lua_State *L); LUAI_FUNC void luaE_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

@@ -757,7 +757,6 @@ static int nospecials (const char *p, size_t l) {
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;
@@ -765,8 +764,8 @@ static void prepstate (MatchState *ms, lua_State *L,
static void reprepstate (MatchState *ms) { static void reprepstate (MatchState *ms) {
ms->matchdepth = MAXCCALLS;
ms->level = 0; ms->level = 0;
lua_assert(ms->matchdepth == MAXCCALLS);
} }

View File

@@ -1650,6 +1650,14 @@ static int runC (lua_State *L, lua_State *L1, const char *pc) {
int nres; int nres;
status = lua_resume(lua_tothread(L1, i), L, getnum, &nres); status = lua_resume(lua_tothread(L1, i), L, getnum, &nres);
} }
else if EQ("traceback") {
const char *msg = getstring;
int level = getnum;
luaL_traceback(L1, L1, msg, level);
}
else if EQ("threadstatus") {
lua_pushstring(L1, statcodes[lua_status(L1)]);
}
else if EQ("return") { else if EQ("return") {
int n = getnum; int n = getnum;
if (L1 != L) { if (L1 != L) {

23
lua.c
View File

@@ -115,12 +115,13 @@ static void l_message (const char *pname, const char *msg) {
/* /*
** Check whether 'status' is not OK and, if so, prints the error ** Check whether 'status' is not OK and, if so, prints the error
** message on the top of the stack. It assumes that the error object ** message on the top of the stack.
** is a string, as it was either generated by Lua or by 'msghandler'.
*/ */
static int report (lua_State *L, int status) { static int report (lua_State *L, int status) {
if (status != LUA_OK) { if (status != LUA_OK) {
const char *msg = lua_tostring(L, -1); const char *msg = lua_tostring(L, -1);
if (msg == NULL)
msg = "(error message not a string)";
l_message(progname, msg); l_message(progname, msg);
lua_pop(L, 1); /* remove message */ lua_pop(L, 1); /* remove message */
} }
@@ -211,7 +212,7 @@ static int dostring (lua_State *L, const char *s, const char *name) {
/* /*
** Receives 'globname[=modname]' and runs 'globname = require(modname)'. ** Receives 'globname[=modname]' and runs 'globname = require(modname)'.
** If there is no explicit modname and globname contains a '-', cut ** If there is no explicit modname and globname contains a '-', cut
** the sufix after '-' (the "version") to make the global name. ** the suffix after '-' (the "version") to make the global name.
*/ */
static int dolibrary (lua_State *L, char *globname) { static int dolibrary (lua_State *L, char *globname) {
int status; int status;
@@ -230,7 +231,7 @@ static int dolibrary (lua_State *L, char *globname) {
status = docall(L, 1, 1); /* call 'require(modname)' */ status = docall(L, 1, 1); /* call 'require(modname)' */
if (status == LUA_OK) { if (status == LUA_OK) {
if (suffix != NULL) /* is there a suffix mark? */ if (suffix != NULL) /* is there a suffix mark? */
*suffix = '\0'; /* remove sufix from global name */ *suffix = '\0'; /* remove suffix from global name */
lua_setglobal(L, globname); /* globname = require(modname) */ lua_setglobal(L, globname); /* globname = require(modname) */
} }
return report(L, status); return report(L, status);
@@ -301,7 +302,8 @@ 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 */
*first = i + 1; /* if there is a script name, it comes after '--' */
*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 '-' */
@@ -489,10 +491,8 @@ static int incomplete (lua_State *L, int status) {
if (status == LUA_ERRSYNTAX) { if (status == LUA_ERRSYNTAX) {
size_t lmsg; size_t lmsg;
const char *msg = lua_tolstring(L, -1, &lmsg); const char *msg = lua_tolstring(L, -1, &lmsg);
if (lmsg >= marklen && strcmp(msg + lmsg - marklen, EOFMARK) == 0) { if (lmsg >= marklen && strcmp(msg + lmsg - marklen, EOFMARK) == 0)
lua_pop(L, 1);
return 1; return 1;
}
} }
return 0; /* else... */ return 0; /* else... */
} }
@@ -507,9 +507,9 @@ static int pushline (lua_State *L, int firstline) {
size_t l; size_t l;
const char *prmt = get_prompt(L, firstline); const char *prmt = get_prompt(L, firstline);
int readstatus = lua_readline(L, b, prmt); int readstatus = lua_readline(L, b, prmt);
if (readstatus == 0)
return 0; /* no input (prompt will be popped by caller) */
lua_pop(L, 1); /* remove prompt */ lua_pop(L, 1); /* remove prompt */
if (readstatus == 0)
return 0; /* no input */
l = strlen(b); l = strlen(b);
if (l > 0 && b[l-1] == '\n') /* line ends with newline? */ if (l > 0 && b[l-1] == '\n') /* line ends with newline? */
b[--l] = '\0'; /* remove it */ b[--l] = '\0'; /* remove it */
@@ -551,8 +551,9 @@ static int multiline (lua_State *L) {
int status = luaL_loadbuffer(L, line, len, "=stdin"); /* 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)) {
lua_saveline(L, line); /* keep history */ lua_saveline(L, line); /* keep history */
return status; /* cannot or should not 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_pushliteral(L, "\n"); /* add newline... */ lua_pushliteral(L, "\n"); /* add newline... */
lua_insert(L, -2); /* ...between the two lines */ lua_insert(L, -2); /* ...between the two lines */
lua_concat(L, 3); /* join them */ lua_concat(L, 3); /* join them */

40
lua.h
View File

@@ -1,7 +1,7 @@
/* /*
** $Id: lua.h $ ** $Id: lua.h $
** Lua - A Scripting Language ** Lua - A Scripting Language
** Lua.org, PUC-Rio, Brazil (www.lua.org) ** Lua.org, PUC-Rio, Brazil (http://www.lua.org)
** See Copyright Notice at the end of this file ** See Copyright Notice at the end of this file
*/ */
@@ -13,21 +13,22 @@
#include <stddef.h> #include <stddef.h>
#define LUA_COPYRIGHT LUA_RELEASE " Copyright (C) 1994-2023 Lua.org, PUC-Rio"
#define LUA_AUTHORS "R. Ierusalimschy, L. H. de Figueiredo, W. Celes"
#define LUA_VERSION_MAJOR_N 5
#define LUA_VERSION_MINOR_N 4
#define LUA_VERSION_RELEASE_N 6
#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)
#include "luaconf.h" #include "luaconf.h"
#define LUA_VERSION_MAJOR "5"
#define LUA_VERSION_MINOR "4"
#define LUA_VERSION_RELEASE "9"
#define LUA_VERSION_NUM 504
#define LUA_VERSION_RELEASE_NUM (LUA_VERSION_NUM * 100 + 9)
#define LUA_VERSION "Lua " LUA_VERSION_MAJOR "." LUA_VERSION_MINOR
#define LUA_RELEASE LUA_VERSION "." LUA_VERSION_RELEASE
#define LUA_COPYRIGHT LUA_RELEASE " Copyright (C) 1994-2026 Lua.org, PUC-Rio"
#define LUA_AUTHORS "R. Ierusalimschy, L. H. de Figueiredo, W. Celes"
/* mark for precompiled code ('<esc>Lua') */ /* mark for precompiled code ('<esc>Lua') */
#define LUA_SIGNATURE "\x1bLua" #define LUA_SIGNATURE "\x1bLua"
@@ -495,19 +496,8 @@ struct lua_Debug {
/* }====================================================================== */ /* }====================================================================== */
#define LUAI_TOSTRAUX(x) #x
#define LUAI_TOSTR(x) LUAI_TOSTRAUX(x)
#define LUA_VERSION_MAJOR LUAI_TOSTR(LUA_VERSION_MAJOR_N)
#define LUA_VERSION_MINOR LUAI_TOSTR(LUA_VERSION_MINOR_N)
#define LUA_VERSION_RELEASE LUAI_TOSTR(LUA_VERSION_RELEASE_N)
#define LUA_VERSION "Lua " LUA_VERSION_MAJOR "." LUA_VERSION_MINOR
#define LUA_RELEASE LUA_VERSION "." LUA_VERSION_RELEASE
/****************************************************************************** /******************************************************************************
* Copyright (C) 1994-2023 Lua.org, PUC-Rio. * Copyright (C) 1994-2026 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

View File

@@ -261,7 +261,7 @@
/* /*
** LUA_IGMARK is a mark to ignore all after it when building the ** LUA_IGMARK is a mark to ignore all after it when building the
** module name (e.g., used to build the luaopen_ function name). ** module name (e.g., used to build the luaopen_ function name).
** Typically, the sufix after the mark is the module version, ** Typically, the suffix after the mark is the module version,
** as in "mod-v1.2.so". ** as in "mod-v1.2.so".
*/ */
#define LUA_IGMARK "-" #define LUA_IGMARK "-"

View File

@@ -21,7 +21,7 @@
/* /*
** Encode major-minor version in one byte, one nibble for each ** Encode major-minor version in one byte, one nibble for each
*/ */
#define LUAC_VERSION (LUA_VERSION_MAJOR_N*16+LUA_VERSION_MINOR_N) #define LUAC_VERSION (((LUA_VERSION_NUM / 100) * 16) + LUA_VERSION_NUM % 100)
#define LUAC_FORMAT 0 /* this is the official format */ #define LUAC_FORMAT 0 /* this is the official format */

View File

@@ -65,6 +65,8 @@ static const char *utf8_decode (const char *s, utfint *val, int strict) {
utfint res = 0; /* final result */ utfint 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... */
@@ -74,7 +76,7 @@ static const char *utf8_decode (const char *s, utfint *val, int strict) {
res = (res << 6) | (cc & 0x3F); /* add lower 6 bits from cont. byte */ res = (res << 6) | (cc & 0x3F); /* add lower 6 bits from cont. byte */
} }
res |= ((utfint)(c & 0x7F) << (count * 5)); /* add first byte */ res |= ((utfint)(c & 0x7F) << (count * 5)); /* add first byte */
if (count > 5 || res > MAXUTF || res < limits[count]) if (res > MAXUTF || res < limits[count])
return NULL; /* invalid byte sequence */ return NULL; /* invalid byte sequence */
s += count; /* skip continuation bytes read */ s += count; /* skip continuation bytes read */
} }

12
lvm.c
View File

@@ -92,7 +92,7 @@ static int l_strton (const TValue *obj, TValue *result) {
if (!cvt2num(obj)) /* is object not a string? */ if (!cvt2num(obj)) /* is object not a string? */
return 0; return 0;
else { else {
TString *st = tsvalue(obj); TString *st = tsvalue(obj);
return (luaO_str2num(getstr(st), result) == tsslen(st) + 1); return (luaO_str2num(getstr(st), result) == tsslen(st) + 1);
} }
} }
@@ -339,7 +339,10 @@ void luaV_finishset (lua_State *L, const TValue *t, TValue *key,
lua_assert(isempty(slot)); /* slot must be empty */ lua_assert(isempty(slot)); /* slot must be empty */
tm = fasttm(L, h->metatable, TM_NEWINDEX); /* get metamethod */ tm = fasttm(L, h->metatable, TM_NEWINDEX); /* get metamethod */
if (tm == NULL) { /* no metamethod? */ if (tm == NULL) { /* no metamethod? */
sethvalue2s(L, L->top.p, h); /* anchor 't' */
L->top.p++; /* assume EXTRA_STACK */
luaH_finishset(L, h, key, slot, val); /* set new value */ luaH_finishset(L, h, key, slot, val); /* set new value */
L->top.p--;
invalidateTMcache(h); invalidateTMcache(h);
luaC_barrierback(L, obj2gco(h), val); luaC_barrierback(L, obj2gco(h), val);
return; return;
@@ -358,7 +361,12 @@ void luaV_finishset (lua_State *L, const TValue *t, TValue *key,
} }
t = tm; /* else repeat assignment over 'tm' */ t = tm; /* else repeat assignment over 'tm' */
if (luaV_fastget(L, t, key, slot, luaH_get)) { if (luaV_fastget(L, t, key, slot, luaH_get)) {
luaV_finishfastset(L, t, slot, val); /* execute 'luaV_finishfastset', but preserving the original 't'
for the barrier. 't' and 'slot' can point to the same value,
and so the assignment can change 't' value */
GCObject *h = gcvalue(t);
setobj2t(L, cast(TValue *,slot), val);
luaC_barrierback(L, h, val);
return; /* done */ return; /* done */
} }
/* else 'return luaV_finishset(L, t, key, val, slot)' (loop) */ /* else 'return luaV_finishset(L, t, key, val, slot)' (loop) */

View File

@@ -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; 2023 Lua.org, PUC-Rio. All rights reserved. &copy; 2026 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 (s) return g(f(s)) end return function (...) return g(f(...)) end
end end
local function concat (f, g) local function concat (f, g)
@@ -395,9 +395,10 @@ 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) .. e return antipara(Tag.hr() .. Tag.h3(a)) .. Tag.pre(h, {class="api"}) .. e
else else
return antipara(Tag.hr() .. Tag.h3(a)) .. apiicmd .. Tag.pre(h) .. ne return antipara(Tag.hr() .. Tag.h3(a)) .. apiicmd ..
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-complement arithmetic. according to the usual rules of two's 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,
@@ -289,7 +289,7 @@ Whenever there is an error,
an @def{error object} an @def{error object}
is propagated with information about the error. is propagated with information about the error.
Lua itself only generates errors whose error object is a string, Lua itself only generates errors whose error object is a string,
but programs may generate errors with but programs can generate errors with
any value as the error object. any value as the error object.
It is up to the Lua program or its host to handle such error objects. It is up to the Lua program or its host to handle such error objects.
For historical reasons, For historical reasons,
@@ -298,7 +298,7 @@ even though it does not have to be a string.
When you use @Lid{xpcall} (or @Lid{lua_pcall}, in C) When you use @Lid{xpcall} (or @Lid{lua_pcall}, in C)
you may give a @def{message handler} you can give a @def{message handler}
to be called in case of errors. to be called in case of errors.
This function is called with the original error object This function is called with the original error object
and returns a new error object. and returns a new error object.
@@ -343,7 +343,7 @@ which is then called a @def{metamethod}.
In the previous example, the key is the string @St{__add} In the previous example, the key is the string @St{__add}
and the metamethod is the function that performs the addition. and the metamethod is the function that performs the addition.
Unless stated otherwise, Unless stated otherwise,
a metamethod may in fact be any @x{callable value}, a metamethod can in fact be any @x{callable value},
which is either a function or a value with a @idx{__call} metamethod. which is either a function or a value with a @idx{__call} metamethod.
You can query the metatable of any value You can query the metatable of any value
@@ -609,7 +609,7 @@ as soon as the collector can be sure the object
will not be accessed again in the normal execution of the program. will not be accessed again in the normal execution of the program.
(@Q{Normal execution} here excludes finalizers, (@Q{Normal execution} here excludes finalizers,
which can resurrect dead objects @see{finalizers}, which can resurrect dead objects @see{finalizers},
and excludes also operations using the debug library.) and it excludes also operations using the debug library.)
Note that the time when the collector can be sure that an object Note that the time when the collector can be sure that an object
is dead may not coincide with the programmer's expectations. is dead may not coincide with the programmer's expectations.
The only guarantees are that Lua will not collect an object The only guarantees are that Lua will not collect an object
@@ -1417,7 +1417,7 @@ labels in Lua are considered statements too:
A label is visible in the entire block where it is defined, A label is visible in the entire block where it is defined,
except inside nested functions. except inside nested functions.
A goto may jump to any visible label as long as it does not A goto can jump to any visible label as long as it does not
enter into the scope of a local variable. enter into the scope of a local variable.
A label should not be declared A label should not be declared
where a label with the same name is visible, where a label with the same name is visible,
@@ -1950,12 +1950,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.
@@ -2318,7 +2318,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 mutlres expressions. Here are some examples of uses of multires 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.
@@ -3471,9 +3471,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-complement integer), (usually a 64-bit two's 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-complement integer). (usually a 32-bit two's 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
@@ -3689,7 +3689,7 @@ passes to the allocator in every call.
@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)}.
} }
@@ -4416,7 +4416,7 @@ otherwise, returns @id{NULL}.
} }
@APIEntry{void lua_toclose (lua_State *L, int index);| @APIEntry{void lua_toclose (lua_State *L, int index);|
@apii{0,0,m} @apii{0,0,v}
Marks the given index in the stack as a Marks the given index in the stack as a
to-be-closed slot @see{to-be-closed}. to-be-closed slot @see{to-be-closed}.
@@ -4433,6 +4433,9 @@ A slot marked as to-be-closed should not be removed from the stack
by any other function in the API except @Lid{lua_settop} or @Lid{lua_pop}, by any other function in the API except @Lid{lua_settop} or @Lid{lua_pop},
unless previously deactivated by @Lid{lua_closeslot}. unless previously deactivated by @Lid{lua_closeslot}.
This function raises an error if the value at the given slot
neither has a @idx{__close} metamethod nor is a false value.
This function should not be called for an index This function should not be called for an index
that is equal to or below an active to-be-closed slot. that is equal to or below an active to-be-closed slot.
@@ -4488,7 +4491,7 @@ but can contain other zeros in its body.
This function can raise memory errors only This function can raise memory errors only
when converting a number to a string when converting a number to a string
(as then it has to create a new string). (as then it may create a new string).
} }
@@ -8773,13 +8776,13 @@ The returned table can contain all the fields returned by @Lid{lua_getinfo},
with the string @id{what} describing which fields to fill in. with the string @id{what} describing which fields to fill in.
The default for @id{what} is to get all information available, The default for @id{what} is to get all information available,
except the table of valid lines. except the table of valid lines.
If present, The option @Char{f}
the option @Char{f}
adds a field named @id{func} with the function itself. adds a field named @id{func} with the function itself.
If present, The option @Char{L} adds a field named @id{activelines}
the option @Char{L} with the table of valid lines,
adds a field named @id{activelines} with the table of provided the function is a Lua function.
valid lines. If the function has no debug information,
the table is empty.
For instance, the expression @T{debug.getinfo(1,"n").name} returns For instance, the expression @T{debug.getinfo(1,"n").name} returns
a name for the current function, a name for the current function,

View File

@@ -287,7 +287,7 @@ print("final OK !!!")
--[[ --[[
***************************************************************************** *****************************************************************************
* Copyright (C) 1994-2016 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

View File

@@ -399,6 +399,10 @@ do
-- trivial error -- trivial error
assert(T.checkpanic("pushstring hi; error") == "hi") assert(T.checkpanic("pushstring hi; error") == "hi")
-- thread status inside panic (bug in 5.4.4)
assert(T.checkpanic("pushstring hi; error", "threadstatus; return 2") ==
"ERRRUN")
-- using the stack inside panic -- using the stack inside panic
assert(T.checkpanic("pushstring hi; error;", assert(T.checkpanic("pushstring hi; error;",
[[checkstack 5 XX [[checkstack 5 XX

View File

@@ -3,6 +3,14 @@
print "testing closures" print "testing closures"
do -- bug in 5.4.7
_ENV[true] = 10
local function aux () return _ENV[1 < 2] end
assert(aux() == 10)
_ENV[true] = nil
end
local A,B = 0,{g=10} local A,B = 0,{g=10}
local function f(x) local function f(x)
local a = {} local a = {}

View File

@@ -493,6 +493,25 @@ assert(not pcall(a, a))
a = nil a = nil
do
-- bug in 5.4: thread can use message handler higher in the stack
-- than the variable being closed
local c = coroutine.create(function()
local clo <close> = setmetatable({}, {__close=function()
local x = 134 -- will overwrite message handler
error(x)
end})
-- yields coroutine but leaves a new message handler for it,
-- that would be used when closing the coroutine (except that it
-- will be overwritten)
xpcall(coroutine.yield, function() return "XXX" end)
end)
assert(coroutine.resume(c)) -- start coroutine
local st, msg = coroutine.close(c)
assert(not st and msg == 134)
end
-- access to locals of erroneous coroutines -- access to locals of erroneous coroutines
local x = coroutine.create (function () local x = coroutine.create (function ()
local a = 10 local a = 10

View File

@@ -49,6 +49,15 @@ do
end end
-- bug in 5.4.4-5.4.6: activelines in vararg functions
-- without debug information
do
local func = load(string.dump(load("print(10)"), true))
local actl = debug.getinfo(func, "L").activelines
assert(#actl == 0) -- no line info
end
-- test file and string names truncation -- test file and string names truncation
local a = "function f () end" local a = "function f () end"
local function dostring (s, x) return load(s, x)() end local function dostring (s, x) return load(s, x)() end

View File

@@ -91,7 +91,7 @@ end
if not T then if not T then
(Message or print) (Message or print)
('\n >>> testC not active: skipping memory message test <<<\n') ('\n >>> testC not active: skipping tests for messages in C <<<\n')
else else
print "testing memory error message" print "testing memory error message"
local a = {} local a = {}
@@ -104,6 +104,19 @@ else
end) end)
T.totalmem(0) T.totalmem(0)
assert(not st and msg == "not enough" .. " memory") assert(not st and msg == "not enough" .. " memory")
-- stack space for luaL_traceback (bug in 5.4.6)
local res = T.testC[[
# push 16 elements on the stack
pushnum 1; pushnum 1; pushnum 1; pushnum 1; pushnum 1;
pushnum 1; pushnum 1; pushnum 1; pushnum 1; pushnum 1;
pushnum 1; pushnum 1; pushnum 1; pushnum 1; pushnum 1;
pushnum 1;
# traceback should work with 4 remaining slots
traceback xuxu 1;
return 1
]]
assert(string.find(res, "xuxu.-main chunk"))
end end
@@ -124,6 +137,10 @@ checkmessage("aaa=(1)..{}", "a table value")
-- 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'")
-- a similar bug in 5.4.7, since 5.4.0
checkmessage("print(('_ENV').x + 1)", "field 'x'")
_G.aaa, _G.bbbb = nil _G.aaa, _G.bbbb = nil
-- calls -- calls

View File

@@ -370,6 +370,29 @@ x = 0 .."a".."b"..c..d.."e".."f".."g"
assert(x.val == "0abcdefg") assert(x.val == "0abcdefg")
do
-- bug since 5.4.1
local mt = setmetatable({__newindex={}}, {__mode='v'})
local t = setmetatable({}, mt)
if T then T.allocfailnext() end
-- seg. fault
for i=1, 10 do t[i] = 1 end
end
do -- bug in 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

@@ -301,6 +301,16 @@ collectgarbage()
assert(next(a) == string.rep('$', 11)) assert(next(a) == string.rep('$', 11))
if T then -- bug since 5.3: all-weak tables are not being revisited
T.gcstate("propagate")
local t = setmetatable({}, {__mode = "kv"})
T.gcstate("atomic") -- 't' was visited
setmetatable(t, {__mode = "kv"})
T.gcstate("pause") -- its new metatable is not being visited
assert(getmetatable(t).__mode == "kv")
end
-- 'bug' in 5.1 -- 'bug' in 5.1
a = {} a = {}
local t = {x = 10} local t = {x = 10}

View File

@@ -90,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)
@@ -133,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
@@ -346,9 +346,14 @@ 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]]\n=a") prepfile("a = [[b\nc\nd\ne]]\n=a")
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
prepfile("a.\n")
RUN([[lua -i < %s > /dev/null 2> %s]], prog, out)
checkprogout("near <eof>\n")
local prompt = "alo" local prompt = "alo"
prepfile[[ -- prepfile[[ --
a = 2 a = 2
@@ -473,12 +478,14 @@ 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

@@ -56,7 +56,8 @@ assert(f(" \n\r*&\n\r xuxu \n\n", "%g%g%g+") == "xuxu")
-- Adapt a pattern to UTF-8 -- Adapt a pattern to UTF-8
local function PU (p) local function PU (p)
-- break '?' into each individual byte of a character -- reapply '?' into each individual byte of a character.
-- (For instance, "á?" becomes "\195?\161?".)
p = string.gsub(p, "(" .. utf8.charpattern .. ")%?", function (c) p = string.gsub(p, "(" .. utf8.charpattern .. ")%?", function (c)
return string.gsub(c, ".", "%0?") return string.gsub(c, ".", "%0?")
end) end)