Merge branch 'master' of https://github.com/shewitt-au/lua into msdev-build

This commit is contained in:
shewitt.au 2026-03-17 04:16:25 +11:00
commit 1a0d4151e8
31 changed files with 331 additions and 95 deletions

11
lapi.c
View file

@ -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);
@ -1201,11 +1201,16 @@ 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)
n = g->GCdebt; /* force to run one basic step */ newdebt = 0; /* force to run one basic step */
luaE_setdebt(g, g->GCdebt - n); else if (g->GCdebt >= n - MAX_LMEM) /* no overflow? */
newdebt = g->GCdebt - n;
else /* overflow */
newdebt = -MAX_LMEM; /* set debt to miminum value */
luaE_setdebt(g, newdebt);
luaC_condGC(L, (void)0, work = 1); 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 */

View file

@ -81,8 +81,8 @@ 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, LUALIB_API void *(luaL_alloc) (void *ud, void *ptr, size_t osize,
size_t nsize); size_t nsize);
/* predefined references */ /* predefined references */
@ -103,7 +103,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

@ -827,7 +827,7 @@ void luaK_dischargevars (FuncState *fs, expdesc *e) {
} /* FALLTHROUGH */ } /* 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; /* (can't do a direct assignment; values overlap) */ e->u.info = temp; /* (avoid a direct assignment; values overlap) */
e->k = VNONRELOC; /* becomes a non-relocatable value */ e->k = VNONRELOC; /* becomes a non-relocatable value */
break; break;
} }
@ -1365,7 +1365,7 @@ void luaK_indexed (FuncState *fs, expdesc *t, expdesc *k) {
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; /* (can't do a direct assignment; values overlap) */ t->u.ind.t = temp; /* (avoid a direct assignment; values overlap) */
lua_assert(isKstr(fs, k)); lua_assert(isKstr(fs, k));
fillidxk(t, k->u.info, VINDEXUP); /* literal short string */ fillidxk(t, k->u.info, VINDEXUP); /* literal short string */
} }
@ -1373,12 +1373,13 @@ void luaK_indexed (FuncState *fs, expdesc *t, expdesc *k) {
int kreg = luaK_exp2anyreg(fs, k); /* put key in some register */ int kreg = luaK_exp2anyreg(fs, k); /* put key in some register */
lu_byte vreg = cast_byte(t->u.var.ridx); /* register with vararg param. */ lu_byte vreg = cast_byte(t->u.var.ridx); /* register with vararg param. */
lua_assert(vreg == fs->f->numparams); lua_assert(vreg == fs->f->numparams);
t->u.ind.t = vreg; /* (avoid a direct assignment; values may overlap) */ t->u.ind.t = vreg; /* (avoid a direct assignment; values may overlap?) */
fillidxk(t, kreg, VVARGIND); /* 't' represents 'vararg[k]' */ fillidxk(t, kreg, VVARGIND); /* 't' represents 'vararg[k]' */
} }
else { else {
/* register index of the table */ /* register index of the table */
t->u.ind.t = cast_byte((t->k == VLOCAL) ? t->u.var.ridx: t->u.info); lu_byte temp = cast_byte((t->k == VLOCAL) ? t->u.var.ridx: t->u.info);
t->u.ind.t = temp; /* (avoid a direct assignment; values may overlap?) */
if (isKstr(fs, k)) if (isKstr(fs, k))
fillidxk(t, k->u.info, VINDEXSTR); /* literal short string */ fillidxk(t, k->u.info, VINDEXSTR); /* literal short string */
else if (isCint(k)) /* int. constant in proper range? */ else if (isCint(k)) /* int. constant in proper range? */

20
ldo.c
View file

@ -221,13 +221,21 @@ l_noret luaD_errerr (lua_State *L) {
/* /*
** Check whether stack has enough space to run a simple function (such ** Check whether stacks have enough space to run a simple function (such
** as a finalizer): At least BASIC_STACK_SIZE in the Lua stack and ** as a finalizer): At least BASIC_STACK_SIZE in the Lua stack, two
** 2 slots in the C stack. ** available CallInfos, and two "slots" in the C stack.
*/ */
int luaD_checkminstack (lua_State *L) { int luaD_checkminstack (lua_State *L) {
return ((stacksize(L) < MAXSTACK - BASIC_STACK_SIZE) && if (getCcalls(L) >= LUAI_MAXCCALLS - 2)
(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);
} }
@ -616,7 +624,7 @@ void luaD_poscall (lua_State *L, CallInfo *ci, int nres) {
#define next_ci(L) (L->ci->next ? L->ci->next : luaE_extendCI(L)) #define next_ci(L) (L->ci->next ? L->ci->next : luaE_extendCI(L, 1))
/* /*

4
lgc.c
View file

@ -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->gcemergency && luaD_checkminstack(L)) if (g->tobefnz != NULL && !g->gcemergency && luaD_checkminstack(L))
callallpendingfinalizers(L); callallpendingfinalizers(L);
} }
@ -1672,7 +1672,7 @@ static l_mem singlestep (lua_State *L, int fast) {
GCTM(L); /* call one finalizer */ GCTM(L); /* call one finalizer */
stepresult = CWUFIN; stepresult = CWUFIN;
} }
else { /* no more finalizers or emergency mode or no enough stack else { /* no more finalizers or emergency mode or not enough stack
to run finalizers */ to run finalizers */
g->gcstate = GCSpause; /* finish collection */ g->gcstate = GCSpause; /* finish collection */
stepresult = step2pause; stepresult = step2pause;

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 defined(LUA_COMPAT_GLOBAL) #if 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

@ -234,12 +234,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) ((void)L, l_floor(luai_numdiv(L,a,b))) #define luai_numidiv(L,a,b) 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) ((a)/(b)) #define luai_numdiv(L,a,b) ((void)L, (a)/(b))
#endif #endif
/* /*
@ -267,10 +267,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) ((a)+(b)) #define luai_numadd(L,a,b) ((void)L, (a)+(b))
#define luai_numsub(L,a,b) ((a)-(b)) #define luai_numsub(L,a,b) ((void)L, (a)-(b))
#define luai_nummul(L,a,b) ((a)*(b)) #define luai_nummul(L,a,b) ((void)L, (a)*(b))
#define luai_numunm(L,a) (-(a)) #define luai_numunm(L,a) ((void)L, -(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))

View file

@ -104,7 +104,7 @@ LUAI_DDEF const lu_byte luaP_opmodes[NUM_OPCODES] = {
,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, 0, 0, 1, iABC) /* OP_GETVARG */
,opmode(0, 0, 0, 0, 0, iABx) /* OP_ERRNNIL */ ,opmode(0, 0, 0, 0, 0, iABx) /* OP_ERRNNIL */
,opmode(0, 0, 1, 0, 1, iABC) /* OP_VARARGPREP */ ,opmode(0, 0, 1, 0, 0, iABC) /* OP_VARARGPREP */
,opmode(0, 0, 0, 0, 0, iAx) /* OP_EXTRAARG */ ,opmode(0, 0, 0, 0, 0, iAx) /* OP_EXTRAARG */
}; };

View file

@ -1682,13 +1682,22 @@ static void forbody (LexState *ls, int base, int line, int nvars, int isgen) {
} }
/*
** Control whether for-loop control variables are read-only
*/
#if LUA_COMPAT_LOOPVAR
#define LOOPVARKIND VDKREG
#else /* by default, these variables are read only */
#define LOOPVARKIND RDKCONST
#endif
static void fornum (LexState *ls, TString *varname, int line) { static void fornum (LexState *ls, TString *varname, int line) {
/* fornum -> NAME = exp,exp[,exp] forbody */ /* fornum -> NAME = exp,exp[,exp] forbody */
FuncState *fs = ls->fs; FuncState *fs = ls->fs;
int base = fs->freereg; int base = fs->freereg;
new_localvarliteral(ls, "(for state)"); new_localvarliteral(ls, "(for state)");
new_localvarliteral(ls, "(for state)"); new_localvarliteral(ls, "(for state)");
new_varkind(ls, varname, RDKCONST); /* control variable */ new_varkind(ls, varname, LOOPVARKIND); /* control variable */
checknext(ls, '='); checknext(ls, '=');
exp1(ls); /* initial value */ exp1(ls); /* initial value */
checknext(ls, ','); checknext(ls, ',');
@ -1715,7 +1724,7 @@ static void forlist (LexState *ls, TString *indexname) {
new_localvarliteral(ls, "(for state)"); /* iterator function */ new_localvarliteral(ls, "(for state)"); /* iterator function */
new_localvarliteral(ls, "(for state)"); /* state */ new_localvarliteral(ls, "(for state)"); /* state */
new_localvarliteral(ls, "(for state)"); /* closing var. (after swap) */ new_localvarliteral(ls, "(for state)"); /* closing var. (after swap) */
new_varkind(ls, indexname, RDKCONST); /* control variable */ new_varkind(ls, indexname, LOOPVARKIND); /* control variable */
/* other declared variables */ /* other declared variables */
while (testnext(ls, ',')) { while (testnext(ls, ',')) {
new_localvar(ls, str_checkname(ls)); new_localvar(ls, str_checkname(ls));
@ -2111,7 +2120,7 @@ static void statement (LexState *ls) {
gotostat(ls, line); gotostat(ls, line);
break; break;
} }
#if defined(LUA_COMPAT_GLOBAL) #if 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 */

View file

@ -68,14 +68,19 @@ void luaE_setdebt (global_State *g, l_mem debt) {
} }
CallInfo *luaE_extendCI (lua_State *L) { CallInfo *luaE_extendCI (lua_State *L, int err) {
CallInfo *ci; 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;

View file

@ -438,7 +438,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); 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

@ -141,8 +141,8 @@ static int str_rep (lua_State *L) {
const char *s = luaL_checklstring(L, 1, &len); const char *s = luaL_checklstring(L, 1, &len);
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) if (n <= 0 || (len | lsep) == 0)
lua_pushliteral(L, ""); lua_pushliteral(L, ""); /* no repetitions or both strings empty */
else if (l_unlikely(len > MAX_SIZE - lsep || else if (l_unlikely(len > MAX_SIZE - lsep ||
cast_st2S(len + lsep) > cast_st2S(MAX_SIZE) / 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");
@ -968,7 +968,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 */
@ -1726,7 +1726,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 <= LUA_MAXINTEGER - size, luaL_argcheck(L, totalsize <= MAX_SIZE - size,
1, "format result too large"); 1, "format result too large");
totalsize += size; totalsize += size;
} }

View file

@ -651,10 +651,9 @@ static void reinserthash (lua_State *L, Table *ot, Table *t) {
/* /*
** Exchange the hash part of 't1' and 't2'. (In 'flags', only the ** Exchange the hash part of 't1' and 't2'. (In 'flags', only the dummy
** dummy bit must be exchanged: The 'isrealasize' is not related ** bit must be exchanged: The metamethod bits do not change during a
** to the hash part, and the metamethod bits do not change during ** resize, so the "real" table can keep their values.)
** 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;
@ -1156,14 +1155,15 @@ 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)) { if (luaV_flttointeger(f, &k, F2Ieq)) { /* is key equal to an integer? */
setivalue(&aux, k); /* key is equal to an integer */ setivalue(&aux, k);
key = &aux; /* insert it as an integer */ actk = &aux; /* use the integer as the key */
} }
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");
@ -1176,7 +1176,7 @@ void luaH_finishset (lua_State *L, Table *t, const TValue *key,
L->top.p--; L->top.p--;
return; return;
} }
luaH_newkey(L, t, key, value); 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);

View file

@ -42,15 +42,17 @@ static int checkfield (lua_State *L, const char *key, int n) {
/* /*
** Check that 'arg' either is a table or can behave like one (that is, ** 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) {
if (lua_type(L, arg) != LUA_TTABLE) { /* is it not a table? */ int tp = lua_type(L, arg);
if (tp != LUA_TTABLE) { /* is it not a table? */
int n = 1; /* number of elements to pop */ 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) || checkfield(L, "__len", ++n))) { (!(what & TAB_L) || /* strings don't need '__len' to have a length */
tp == LUA_TSTRING || checkfield(L, "__len", ++n))) {
lua_pop(L, n); /* pop metatable and tested metamethods */ lua_pop(L, n); /* pop metatable and tested metamethods */
} }
else else
@ -204,8 +206,9 @@ 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, luaL_len(L, 1)); lua_Integer e = luaL_opt(L, luaL_checkinteger, 3, len);
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

@ -1106,6 +1106,27 @@ 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));
@ -2182,6 +2203,8 @@ 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}, {"sizes", get_sizes},
{"testC", testC}, {"testC", testC},
{"makeCfunc", makeCfunc}, {"makeCfunc", makeCfunc},

View file

@ -14,6 +14,7 @@
/* test Lua with compatibility code */ /* test Lua with compatibility code */
#define LUA_COMPAT_MATHLIB #define LUA_COMPAT_MATHLIB
#undef LUA_COMPAT_GLOBAL #undef LUA_COMPAT_GLOBAL
#define LUA_COMPAT_GLOBAL 0
#define LUA_DEBUG #define LUA_DEBUG

2
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; bit 7 is used for 'isrealasize'.) ** the table is using the dummy node.)
*/ */
#define maskflags cast_byte(~(~0u << (TM_EQ + 1))) #define maskflags cast_byte(~(~0u << (TM_EQ + 1)))

46
lua.c
View file

@ -30,6 +30,12 @@
#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
@ -374,12 +380,21 @@ static int runargs (lua_State *L, char **argv, int n) {
} }
static char *(*l_getenv)(const char *name);
/* Function to ignore environment variables, used by option -E */
static char *no_getenv (const char *name) {
UNUSED(name);
return NULL;
}
static int handle_luainit (lua_State *L) { static int handle_luainit (lua_State *L) {
const char *name = "=" LUA_INITVARVERSION; const char *name = "=" LUA_INITVARVERSION;
const char *init = getenv(name + 1); const char *init = l_getenv(name + 1);
if (init == NULL) { if (init == NULL) {
name = "=" LUA_INIT_VAR; name = "=" LUA_INIT_VAR;
init = getenv(name + 1); /* try alternative name */ init = l_getenv(name + 1); /* try alternative name */
} }
if (init == NULL) return LUA_OK; if (init == NULL) return LUA_OK;
else if (init[0] == '@') else if (init[0] == '@')
@ -498,18 +513,24 @@ static void lua_freeline (char *line) {
#include <dlfcn.h> #include <dlfcn.h>
static void lua_initreadline (lua_State *L) { static void lua_initreadline (lua_State *L) {
void *lib = dlopen(LUA_READLINELIB, RTLD_NOW | RTLD_LOCAL); const char *rllib = l_getenv(LUA_RLLIB_VAR); /* name of readline library */
if (lib == NULL) void *lib; /* library handle */
lua_warning(L, "library '" LUA_READLINELIB "' not found", 0); if (rllib == NULL) /* no environment variable? */
else { rllib = LUA_READLINELIB; /* use default name */
lib = dlopen(rllib, RTLD_NOW | RTLD_LOCAL);
if (lib != NULL) {
const char **name = cast(const char**, dlsym(lib, "rl_readline_name")); 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) if (l_readline != NULL) /* could load readline function? */
lua_warning(L, "unable to load 'readline'", 0); 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 /* }{ */
@ -715,17 +736,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 (!(args & has_E)) { /* no option '-E'? */ if (handle_luainit(L) != LUA_OK) /* run LUA_INIT */
if (handle_luainit(L) != LUA_OK) /* run LUA_INIT */ return 0; /* error running LUA_INIT */
return 0; /* error running LUA_INIT */
}
if (!runargs(L, argv, optlim)) /* execute arguments -e, -l, and -W */ if (!runargs(L, argv, optlim)) /* execute arguments -e, -l, and -W */
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) */

6
lua.h
View file

@ -13,13 +13,13 @@
#include <stddef.h> #include <stddef.h>
#define LUA_COPYRIGHT LUA_RELEASE " Copyright (C) 1994-2025 Lua.org, PUC-Rio" #define LUA_COPYRIGHT LUA_RELEASE " Copyright (C) 1994-2026 Lua.org, PUC-Rio"
#define LUA_AUTHORS "R. Ierusalimschy, L. H. de Figueiredo, W. Celes" #define LUA_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 0 #define LUA_VERSION_RELEASE_N 1
#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)
@ -521,7 +521,7 @@ struct lua_Debug {
/****************************************************************************** /******************************************************************************
* Copyright (C) 1994-2025 Lua.org, PUC-Rio. * Copyright (C) 1994-2026 Lua.org, PUC-Rio.
* *
* Permission is hereby granted, free of charge, to any person obtaining * 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

@ -70,15 +70,19 @@
#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 */
#if !defined(LUA_READLINELIB)
#define LUA_READLINELIB "libedit.dylib" #define LUA_READLINELIB "libedit.dylib"
#endif #endif
#endif
#if defined(LUA_USE_IOS) #if defined(LUA_USE_IOS)
@ -224,17 +228,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 /* }{ */
@ -245,14 +249,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 /* } */
@ -339,7 +343,18 @@
/* /*
@@ LUA_COMPAT_GLOBAL avoids 'global' being a reserved word @@ LUA_COMPAT_GLOBAL avoids 'global' being a reserved word
*/ */
#define LUA_COMPAT_GLOBAL #if !defined(LUA_COMPAT_GLOBAL)
#define LUA_COMPAT_GLOBAL 1
#endif
/*
@@ LUA_COMPAT_LOOPVAR makes for-loop control variables not read-only,
** as they were in previous versions.
*/
#if !defined(LUA_COMPAT_LOOPVAR)
#define LUA_COMPAT_LOOPVAR 0
#endif
/* /*
@ -721,10 +736,17 @@
/* /*
@@ LUAI_MAXALIGN defines fields that, when used in a union, ensure @@ LUAI_MAXALIGN defines fields that ensure proper alignment for
** maximum alignment for the other items in that union. ** memory areas offered by Lua (e.g., userdata memory).
** Add fields to it if you need alignment for non-ISO objects.
*/ */
#if defined(LLONG_MAX)
/* use ISO C99 stuff */
#define LUAI_MAXALIGN long double u; void *s; long long l
#else
/* use only C89 stuff */
#define LUAI_MAXALIGN lua_Number n; double u; void *s; lua_Integer i; long l #define LUAI_MAXALIGN lua_Number n; double u; void *s; lua_Integer i; long l
#endif
/* }================================================================== */ /* }================================================================== */

View file

@ -56,6 +56,8 @@ static const char *utf8_decode (const char *s, l_uint32 *val, int strict) {
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... */
@ -64,8 +66,9 @@ 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 (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 */
} }
@ -146,7 +149,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", (long)code); lua_pushfstring(L, "%U", cast(unsigned long, code));
} }

6
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 online with opcode OP_FORLOOP, for performance.) ** written inline with opcode OP_FORLOOP, for performance.)
*/ */
static int floatforloop (StkId ra) { static int floatforloop (lua_State *L, StkId ra) {
lua_Number step = fltvalue(s2v(ra + 1)); lua_Number 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 */
@ -1841,7 +1841,7 @@ void luaV_execute (lua_State *L, CallInfo *ci) {
pc -= GETARG_Bx(i); /* jump back */ pc -= GETARG_Bx(i); /* jump back */
} }
} }
else if (floatforloop(ra)) /* float loop */ else if (floatforloop(L, ra)) /* float loop */
pc -= GETARG_Bx(i); /* jump back */ 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;

View file

@ -60,7 +60,7 @@ 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 # -fsanitize=undefined (you may need to add "-lubsan" to libs)
# -fsanitize=pointer-subtract -fsanitize=address -fsanitize=pointer-compare # -fsanitize=pointer-subtract -fsanitize=address -fsanitize=pointer-compare
# TESTS= -DLUA_USER_H='"ltests.h"' -Og -g # TESTS= -DLUA_USER_H='"ltests.h"' -Og -g

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; 2025 Lua.org, PUC-Rio. All rights reserved. &copy; 2026 Lua.org, PUC-Rio. All rights reserved.
</small> </small>
<hr> <hr>

View file

@ -2692,7 +2692,19 @@ which behaves like a nil value.
@sect3{constchar|@title{Pointers to Strings} @sect3{constchar|@title{Pointers to Strings}
Several functions in the API return pointers (@T{const char*}) Several functions in the API accept pointers (@T{const char*})
to C strings.
Some of there parameters have an associated length (@T{size_t}).
Unless stated otherwise,
when there is an associated length,
the string can contain embedded zeros;
moreover, the pointer can be @id{NULL} if the length is zero.
When there is no associated length,
the pointer must point to a zero-terminated string.
In any case, the string contents should remain unchanged
until the function returns.
Several functions in the API also return pointers (@T{const char*})
to Lua strings in the stack. 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}.
@ -3903,8 +3915,12 @@ 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
@Lid{lua_setiuservalue} and @Lid{lua_getiuservalue}.) The user values can be set and read with the functions
@Lid{lua_setiuservalue} and @Lid{lua_getiuservalue}.
The block of memory is suitably aligned for any @N{ISO C} object.
(See macro @id{LUAI_MAXALIGN} in file @id{luaconf.h} for other
alignment requirements.)
The function returns the address of the block of memory. 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
@ -9594,12 +9610,18 @@ 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{
The control variable in @Rw{for} loops is read only. The control variable in @Rw{for} loops is read only.
If you need to change it, If you need to change it,
declare a local variable with the same name in the loop body. declare a local variable with the same name in the loop body.
The compilation option @id{LUA_COMPAT_LOOPVAR} (see @id{luaconf.h})
makes these variables regular (writable).
} }
@item{ @item{
@ -9725,7 +9747,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 @OrNL @Rw{global} attnamelist @bnfopt{@bnfter{=} explist}
@OrNL @Rw{global} @bnfopt{attrib} @bnfter{*} @OrNL @Rw{global} @bnfopt{attrib} @bnfter{*}
} }

View file

@ -707,4 +707,46 @@ 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

@ -78,6 +78,9 @@ 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
@ -167,7 +170,9 @@ 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
@ -192,6 +197,22 @@ 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)")

View file

@ -282,6 +282,25 @@ 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

@ -72,6 +72,19 @@ 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

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

View file

@ -238,10 +238,18 @@ 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 == 12) assert(s == "\xFC\x84\x80\x80\x80\x80\xFD\xBF\xBF\xBF\xBF\xBF")
check(s, {0x4000000, 0x7FFFFFFF}, true) check(s, {0x4000000, 0x7FFFFFFF}, true)
s = "\u{200000}\u{3FFFFFF}" s = "\u{200000}\u{3FFFFFF}"
@ -257,6 +265,10 @@ 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,})