Compare commits

...

24 Commits

Author SHA1 Message Date
Roberto Ierusalimschy
9d067ab73b Optimization for 'n^2'
Squares are much more common than other exponentiations, and 'n*n' is
much more efficient than 'pow'.
2020-11-13 09:59:07 -03:00
Roberto Ierusalimschy
2f4162bc47 Compiler optimization back to '-O2'
Undo commit 6a10f03ff. Compiler performance is important, too.
2020-11-11 15:10:51 -03:00
Roberto Ierusalimschy
ab1aca94e8 Removed optimization for «if ... then goto»
That optimization was too complex and caused some weird traces when
debugging. The more common case «if ... then break» was kept.
2020-11-11 14:41:41 -03:00
Roberto Ierusalimschy
d282652561 Bug when growing a stack
When a stack grows, its extra area can be in use, and it becomes part
of the common area. So, the extra area must be kept correct all the
times. (Bug introduced by commit 5aa36e894f5.)
2020-11-08 11:52:26 -03:00
Roberto Ierusalimschy
58216600eb 'luaL_newstate' should not allocate extra memory
The allocation of a userdata for the state of the warn system can
cause a panic if it fails; 'luaL_ref' also can fail. This commit
re-implements the warn system so that it does not need an explicit
state. Instead, the system uses different functions to represent
the different states.
2020-11-03 16:34:36 -03:00
Roberto Ierusalimschy
94cbe46511 Details
- small corrections in the manual
- ldo.c: 'docall' -> 'ccall' ('docall' already used in 'lua.c')
- comments
2020-10-30 10:18:54 -03:00
Roberto Ierusalimschy
69b71a6919 _PROMPT can have non-string values
'get_prompt' uses 'luaL_tolstring' to convert _PROMPT or _PROMPT2
value to a string. That conversion may invoke a '__tostring'
metamethod.
2020-10-26 11:15:51 -03:00
Roberto Ierusalimschy
d742a193e5 Comments 2020-10-22 15:54:46 -03:00
Roberto Ierusalimschy
e4a38eb0e8 Fixed wrong trace of vararg functions
Trace of vararg functions was skipping an instruction when returning
from a call. (Bug introduced by commit 5d8ce05b3.)
2020-10-19 15:55:25 -03:00
Roberto Ierusalimschy
f07de22576 Fixed compiler option -DHARDSTACKTESTS to commit 5aa36e8 2020-10-19 15:43:59 -03:00
Roberto Ierusalimschy
52c8679760 Fixed bug of keys removed from tables vs 'next'
Fixed the bug that a key removed from a table might not be found
again by 'next'. (This is needed to allow keys to be removed during a
traversal.) This bug was introduced in commit 73ec04fc.
2020-10-14 15:46:58 -03:00
Roberto Ierusalimschy
849b2ecbd2 New release number (5.4.2) 2020-10-12 14:52:39 -03:00
Roberto Ierusalimschy
30528049f1 'lua_upvalueid' returns NULL on invalid upvalue index 2020-10-12 14:51:28 -03:00
Roberto Ierusalimschy
9a89fb1c9d Hash always use all characters in a long string
Hashes for long strings are computed only when they are used as keys
in a table, not a too common case. And, in that case, it is to easy to
force collisions changing only the characters which are not part of the
hash.
2020-10-12 12:29:09 -03:00
Roberto Ierusalimschy
c23cc86c54 Details
- After converting a generic GCObject to a specific type ('gco2*'),
  avoid using the original GCObject (to reduce aliasing).
- Small corrections in comments in 'lopcodes.h'
- Added tests about who calls __close metamethods
2020-10-12 12:29:09 -03:00
Roberto Ierusalimschy
171dcd7d74 'recover' finish of 'luaD_pcall' should follow the original 2020-10-12 12:29:09 -03:00
Roberto Ierusalimschy
5aa36e894f No more field 'lua_State.stacksize'
The stack size is derived from 'stack_last', when needed. Moreover,
the handling of stack sizes is more consistent, always excluding the
extra space except when allocating/deallocating the array.
2020-10-12 12:29:09 -03:00
Roberto Ierusalimschy
9ecd446141 Avoid shrinking stacks to often
Shrink a stack only when the final stack size can be at most 2/3 the
previous size with half of its entries empty. This commit also
improves the clarity of 'luaD_growstack'.
2020-10-12 12:29:09 -03:00
Roberto Ierusalimschy
fb172d0a92 No need for 'volatile' in string.pack/unpack
Type punning an address to 'char *' should be always safe.
2020-10-12 12:29:09 -03:00
Roberto Ierusalimschy
0085db4596 Avoid GCs when testing stack overflow
A GC step may invoke some finalizer, which may error and emit
a warning due to stack overflfow.
2020-10-12 12:29:09 -03:00
Roberto Ierusalimschy
490d42b5f8 Correct handling of 'luaV_execute' invocations
The previous stackless implementations marked all 'luaV_execute'
invocations as fresh. However, re-entering 'luaV_execute' when
resuming a coroutine should not be a fresh invocation. (It works
because 'unroll' called 'luaV_execute' for each call entry, but
it was slower than letting 'luaV_execute' finish all non-fresh
invocations.)
2020-10-12 12:29:09 -03:00
Roberto Ierusalimschy
287b302acb Revision of stackless implementation
- more organized handling of 'nCcalls'
- comments
- deprecation of 'setcstacklimit'
2020-10-12 12:29:09 -03:00
Roberto Ierusalimschy
5d8ce05b3f Back to a stackless implementation
A "with stack" implementation gains too little in performance to be
worth all the noise from C-stack overflows.

This commit is almost a sketch, to test performance. There are several
pending stuff:

- review control of C-stack overflow and error messages;
- what to do with setcstacklimit;
- review comments;
- review unroll of Lua calls.
2020-10-12 12:29:09 -03:00
Roberto Ierusalimschy
f83de8e34e Wrong cast in 'str_unpack' 2020-09-30 15:10:12 -03:00
41 changed files with 674 additions and 656 deletions

3
.gitignore vendored
View File

@@ -10,3 +10,6 @@ testes/time.txt
testes/time-debug.txt testes/time-debug.txt
testes/libs/all testes/libs/all
temp
lua

2
all
View File

@@ -1,7 +1,7 @@
make -s -j make -s -j
cd testes/libs; make -s cd testes/libs; make -s
cd .. # back to directory 'testes' cd .. # back to directory 'testes'
ulimit -S -s 2000 ulimit -S -s 1000
if { ../lua -W all.lua; } then if { ../lua -W all.lua; } then
echo -e "\n\n final OK!!!!\n\n" echo -e "\n\n final OK!!!!\n\n"
else else

19
lapi.c
View File

@@ -1383,13 +1383,16 @@ LUA_API const char *lua_setupvalue (lua_State *L, int funcindex, int n) {
static UpVal **getupvalref (lua_State *L, int fidx, int n, LClosure **pf) { static UpVal **getupvalref (lua_State *L, int fidx, int n, LClosure **pf) {
static const UpVal *const nullup = NULL;
LClosure *f; LClosure *f;
TValue *fi = index2value(L, fidx); TValue *fi = index2value(L, fidx);
api_check(L, ttisLclosure(fi), "Lua function expected"); api_check(L, ttisLclosure(fi), "Lua function expected");
f = clLvalue(fi); f = clLvalue(fi);
api_check(L, (1 <= n && n <= f->p->sizeupvalues), "invalid upvalue index");
if (pf) *pf = f; if (pf) *pf = f;
return &f->upvals[n - 1]; /* get its upvalue pointer */ if (1 <= n && n <= f->p->sizeupvalues)
return &f->upvals[n - 1]; /* get its upvalue pointer */
else
return (UpVal**)&nullup;
} }
@@ -1401,11 +1404,14 @@ LUA_API void *lua_upvalueid (lua_State *L, int fidx, int n) {
} }
case LUA_VCCL: { /* C closure */ case LUA_VCCL: { /* C closure */
CClosure *f = clCvalue(fi); CClosure *f = clCvalue(fi);
api_check(L, 1 <= n && n <= f->nupvalues, "invalid upvalue index"); if (1 <= n && n <= f->nupvalues)
return &f->upvalue[n - 1]; return &f->upvalue[n - 1];
} /* else */
} /* FALLTHROUGH */
case LUA_VLCF:
return NULL; /* light C functions have no upvalues */
default: { default: {
api_check(L, 0, "closure expected"); api_check(L, 0, "function expected");
return NULL; return NULL;
} }
} }
@@ -1417,6 +1423,7 @@ LUA_API void lua_upvaluejoin (lua_State *L, int fidx1, int n1,
LClosure *f1; LClosure *f1;
UpVal **up1 = getupvalref(L, fidx1, n1, &f1); UpVal **up1 = getupvalref(L, fidx1, n1, &f1);
UpVal **up2 = getupvalref(L, fidx2, n2, NULL); UpVal **up2 = getupvalref(L, fidx2, n2, NULL);
api_check(L, *up1 != NULL && *up2 != NULL, "invalid upvalue index");
*up1 = *up2; *up1 = *up2;
luaC_objbarrier(L, f1, *up1); luaC_objbarrier(L, f1, *up1);
} }

View File

@@ -283,10 +283,10 @@ 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) {
const char *what = "exit"; /* type of termination */
if (stat != 0 && errno != 0) /* error with an 'errno'? */ if (stat != 0 && errno != 0) /* error with an 'errno'? */
return luaL_fileresult(L, 0, NULL); return luaL_fileresult(L, 0, NULL);
else { else {
const char *what = "exit"; /* type of termination */
l_inspectstat(stat, what); /* interpret result */ l_inspectstat(stat, what); /* interpret result */
if (*what == 'e' && stat == 0) /* successful termination? */ if (*what == 'e' && stat == 0) /* successful termination? */
lua_pushboolean(L, 1); lua_pushboolean(L, 1);
@@ -1006,43 +1006,67 @@ static int panic (lua_State *L) {
/* /*
** Emit a warning. '*warnstate' means: ** Warning functions:
** 0 - warning system is off; ** warnfoff: warning system is off
** 1 - ready to start a new message; ** warnfon: ready to start a new message
** 2 - previous message is to be continued. ** warnfcont: previous message is to be continued
*/ */
static void warnf (void *ud, const char *message, int tocont) { static void warnfoff (void *ud, const char *message, int tocont);
int *warnstate = (int *)ud; static void warnfon (void *ud, const char *message, int tocont);
if (*warnstate != 2 && !tocont && *message == '@') { /* control message? */ static void warnfcont (void *ud, const char *message, int tocont);
if (strcmp(message, "@off") == 0)
*warnstate = 0;
else if (strcmp(message, "@on") == 0) /*
*warnstate = 1; ** Check whether message is a control message. If so, execute the
return; ** control or ignore it if unknown.
*/
static int checkcontrol (lua_State *L, const char *message, int tocont) {
if (tocont || *(message++) != '@') /* not a control message? */
return 0;
else {
if (strcmp(message, "off") == 0)
lua_setwarnf(L, warnfoff, L); /* turn warnings off */
else if (strcmp(message, "on") == 0)
lua_setwarnf(L, warnfon, L); /* turn warnings on */
return 1; /* it was a control message */
} }
else if (*warnstate == 0) /* warnings off? */ }
return;
if (*warnstate == 1) /* previous message was the last? */
lua_writestringerror("%s", "Lua warning: "); /* start a new warning */ static void warnfoff (void *ud, const char *message, int tocont) {
checkcontrol((lua_State *)ud, message, tocont);
}
/*
** Writes the message and handle 'tocont', finishing the message
** if needed and setting the next warn function.
*/
static void warnfcont (void *ud, const char *message, int tocont) {
lua_State *L = (lua_State *)ud;
lua_writestringerror("%s", message); /* write message */ lua_writestringerror("%s", message); /* write message */
if (tocont) /* not the last part? */ if (tocont) /* not the last part? */
*warnstate = 2; /* to be continued */ lua_setwarnf(L, warnfcont, L); /* to be continued */
else { /* last part */ else { /* last part */
lua_writestringerror("%s", "\n"); /* finish message with end-of-line */ lua_writestringerror("%s", "\n"); /* finish message with end-of-line */
*warnstate = 1; /* ready to start a new message */ lua_setwarnf(L, warnfon, L); /* next call is a new message */
} }
} }
static void warnfon (void *ud, const char *message, int tocont) {
if (checkcontrol((lua_State *)ud, message, tocont)) /* control message? */
return; /* nothing else to be done */
lua_writestringerror("%s", "Lua warning: "); /* start a new warning */
warnfcont(ud, message, tocont); /* finish processing */
}
LUALIB_API lua_State *luaL_newstate (void) { LUALIB_API lua_State *luaL_newstate (void) {
lua_State *L = lua_newstate(l_alloc, NULL); lua_State *L = lua_newstate(l_alloc, NULL);
if (L) { if (L) {
int *warnstate; /* space for warning state */
lua_atpanic(L, &panic); lua_atpanic(L, &panic);
warnstate = (int *)lua_newuserdatauv(L, sizeof(int), 0); lua_setwarnf(L, warnfoff, L); /* default is warnings off */
luaL_ref(L, LUA_REGISTRYINDEX); /* make sure it won't be collected */
*warnstate = 0; /* default is warnings off */
lua_setwarnf(L, warnf, warnstate);
} }
return L; return L;
} }

14
lcode.c
View File

@@ -753,7 +753,7 @@ void luaK_setoneret (FuncState *fs, expdesc *e) {
/* /*
** Ensure that expression 'e' is not a variable (nor a constant). ** Ensure that expression 'e' is not a variable (nor a <const>).
** (Expression still may have jump lists.) ** (Expression still may have jump lists.)
*/ */
void luaK_dischargevars (FuncState *fs, expdesc *e) { void luaK_dischargevars (FuncState *fs, expdesc *e) {
@@ -805,8 +805,8 @@ void luaK_dischargevars (FuncState *fs, expdesc *e) {
/* /*
** Ensures expression value is in register 'reg' (and therefore ** Ensure expression value is in register 'reg', making 'e' a
** 'e' will become a non-relocatable expression). ** non-relocatable expression.
** (Expression still may have jump lists.) ** (Expression still may have jump lists.)
*/ */
static void discharge2reg (FuncState *fs, expdesc *e, int reg) { static void discharge2reg (FuncState *fs, expdesc *e, int reg) {
@@ -860,7 +860,8 @@ static void discharge2reg (FuncState *fs, expdesc *e, int reg) {
/* /*
** Ensures expression value is in any register. ** Ensure expression value is in a register, making 'e' a
** non-relocatable expression.
** (Expression still may have jump lists.) ** (Expression still may have jump lists.)
*/ */
static void discharge2anyreg (FuncState *fs, expdesc *e) { static void discharge2anyreg (FuncState *fs, expdesc *e) {
@@ -946,8 +947,11 @@ int luaK_exp2anyreg (FuncState *fs, expdesc *e) {
exp2reg(fs, e, e->u.info); /* put final result in it */ exp2reg(fs, e, e->u.info); /* put final result in it */
return e->u.info; return e->u.info;
} }
/* else expression has jumps and cannot change its register
to hold the jump values, because it is a local variable.
Go through to the default case. */
} }
luaK_exp2nextreg(fs, e); /* otherwise, use next available register */ luaK_exp2nextreg(fs, e); /* default: use next available register */
return e->u.info; return e->u.info;
} }

View File

@@ -281,25 +281,33 @@ static int db_setupvalue (lua_State *L) {
** Check whether a given upvalue from a given closure exists and ** Check whether a given upvalue from a given closure exists and
** returns its index ** returns its index
*/ */
static int checkupval (lua_State *L, int argf, int argnup) { static void *checkupval (lua_State *L, int argf, int argnup, int *pnup) {
void *id;
int nup = (int)luaL_checkinteger(L, argnup); /* upvalue index */ int nup = (int)luaL_checkinteger(L, argnup); /* upvalue index */
luaL_checktype(L, argf, LUA_TFUNCTION); /* closure */ luaL_checktype(L, argf, LUA_TFUNCTION); /* closure */
luaL_argcheck(L, (lua_getupvalue(L, argf, nup) != NULL), argnup, id = lua_upvalueid(L, argf, nup);
"invalid upvalue index"); if (pnup) {
return nup; luaL_argcheck(L, id != NULL, argnup, "invalid upvalue index");
*pnup = nup;
}
return id;
} }
static int db_upvalueid (lua_State *L) { static int db_upvalueid (lua_State *L) {
int n = checkupval(L, 1, 2); void *id = checkupval(L, 1, 2, NULL);
lua_pushlightuserdata(L, lua_upvalueid(L, 1, n)); if (id != NULL)
lua_pushlightuserdata(L, id);
else
luaL_pushfail(L);
return 1; return 1;
} }
static int db_upvaluejoin (lua_State *L) { static int db_upvaluejoin (lua_State *L) {
int n1 = checkupval(L, 1, 2); int n1, n2;
int n2 = checkupval(L, 3, 4); checkupval(L, 1, 2, &n1);
checkupval(L, 3, 4, &n2);
luaL_argcheck(L, !lua_iscfunction(L, 1), 1, "Lua function expected"); luaL_argcheck(L, !lua_iscfunction(L, 1), 1, "Lua function expected");
luaL_argcheck(L, !lua_iscfunction(L, 3), 3, "Lua function expected"); luaL_argcheck(L, !lua_iscfunction(L, 3), 3, "Lua function expected");
lua_upvaluejoin(L, 1, n1, 3, n2); lua_upvaluejoin(L, 1, n1, 3, n2);
@@ -440,10 +448,7 @@ static int db_traceback (lua_State *L) {
static int db_setcstacklimit (lua_State *L) { static int db_setcstacklimit (lua_State *L) {
int limit = (int)luaL_checkinteger(L, 1); int limit = (int)luaL_checkinteger(L, 1);
int res = lua_setcstacklimit(L, limit); int res = lua_setcstacklimit(L, limit);
if (res == 0) lua_pushinteger(L, res);
lua_pushboolean(L, 0);
else
lua_pushinteger(L, res);
return 1; return 1;
} }

147
ldo.c
View File

@@ -139,8 +139,7 @@ l_noret luaD_throw (lua_State *L, int errcode) {
int luaD_rawrunprotected (lua_State *L, Pfunc f, void *ud) { int luaD_rawrunprotected (lua_State *L, Pfunc f, void *ud) {
global_State *g = G(L); l_uint32 oldnCcalls = L->nCcalls;
l_uint32 oldnCcalls = g->Cstacklimit - (L->nCcalls + L->nci);
struct lua_longjmp lj; struct lua_longjmp lj;
lj.status = LUA_OK; lj.status = LUA_OK;
lj.previous = L->errorJmp; /* chain new error handler */ lj.previous = L->errorJmp; /* chain new error handler */
@@ -149,7 +148,7 @@ int luaD_rawrunprotected (lua_State *L, Pfunc f, void *ud) {
(*f)(L, ud); (*f)(L, ud);
); );
L->errorJmp = lj.previous; /* restore old error handler */ L->errorJmp = lj.previous; /* restore old error handler */
L->nCcalls = g->Cstacklimit - oldnCcalls - L->nci; L->nCcalls = oldnCcalls;
return lj.status; return lj.status;
} }
@@ -183,21 +182,20 @@ static void correctstack (lua_State *L, StkId oldstack, StkId newstack) {
int luaD_reallocstack (lua_State *L, int newsize, int raiseerror) { int luaD_reallocstack (lua_State *L, int newsize, int raiseerror) {
int lim = L->stacksize; int lim = stacksize(L);
StkId newstack = luaM_reallocvector(L, L->stack, lim, newsize, StackValue); StkId newstack = luaM_reallocvector(L, L->stack,
lim + EXTRA_STACK, newsize + EXTRA_STACK, StackValue);
lua_assert(newsize <= LUAI_MAXSTACK || newsize == ERRORSTACKSIZE); lua_assert(newsize <= LUAI_MAXSTACK || newsize == ERRORSTACKSIZE);
lua_assert(L->stack_last - L->stack == L->stacksize - EXTRA_STACK);
if (unlikely(newstack == NULL)) { /* reallocation failed? */ if (unlikely(newstack == NULL)) { /* reallocation failed? */
if (raiseerror) if (raiseerror)
luaM_error(L); luaM_error(L);
else return 0; /* do not raise an error */ else return 0; /* do not raise an error */
} }
for (; lim < newsize; lim++) for (; lim < newsize; lim++)
setnilvalue(s2v(newstack + lim)); /* erase new segment */ setnilvalue(s2v(newstack + lim + EXTRA_STACK)); /* erase new segment */
correctstack(L, L->stack, newstack); correctstack(L, L->stack, newstack);
L->stack = newstack; L->stack = newstack;
L->stacksize = newsize; L->stack_last = L->stack + newsize;
L->stack_last = L->stack + newsize - EXTRA_STACK;
return 1; return 1;
} }
@@ -207,51 +205,73 @@ int luaD_reallocstack (lua_State *L, int newsize, int raiseerror) {
** is true, raises any error; otherwise, return 0 in case of errors. ** is true, raises any error; otherwise, return 0 in case of errors.
*/ */
int luaD_growstack (lua_State *L, int n, int raiseerror) { int luaD_growstack (lua_State *L, int n, int raiseerror) {
int size = L->stacksize; int size = stacksize(L);
int newsize = 2 * size; /* tentative new size */ if (unlikely(size > LUAI_MAXSTACK)) {
if (unlikely(size > LUAI_MAXSTACK)) { /* need more space after extra size? */ /* if stack is larger than maximum, thread is already using the
extra space reserved for errors, that is, thread is handling
a stack error; cannot grow further than that. */
lua_assert(stacksize(L) == ERRORSTACKSIZE);
if (raiseerror) if (raiseerror)
luaD_throw(L, LUA_ERRERR); /* error inside message handler */ luaD_throw(L, LUA_ERRERR); /* error inside message handler */
else return 0; return 0; /* if not 'raiseerror', just signal it */
} }
else { else {
int needed = cast_int(L->top - L->stack) + n + EXTRA_STACK; int newsize = 2 * size; /* tentative new size */
int needed = cast_int(L->top - L->stack) + n;
if (newsize > LUAI_MAXSTACK) /* cannot cross the limit */ if (newsize > LUAI_MAXSTACK) /* cannot cross the limit */
newsize = LUAI_MAXSTACK; newsize = LUAI_MAXSTACK;
if (newsize < needed) /* but must respect what was asked for */ if (newsize < needed) /* but must respect what was asked for */
newsize = needed; newsize = needed;
if (unlikely(newsize > LUAI_MAXSTACK)) { /* stack overflow? */ if (likely(newsize <= LUAI_MAXSTACK))
return luaD_reallocstack(L, newsize, raiseerror);
else { /* stack overflow */
/* add extra size to be able to handle the error message */ /* add extra size to be able to handle the error message */
luaD_reallocstack(L, ERRORSTACKSIZE, raiseerror); luaD_reallocstack(L, ERRORSTACKSIZE, raiseerror);
if (raiseerror) if (raiseerror)
luaG_runerror(L, "stack overflow"); luaG_runerror(L, "stack overflow");
else return 0; return 0;
} }
} /* else no errors */ }
return luaD_reallocstack(L, newsize, raiseerror);
} }
static int stackinuse (lua_State *L) { static int stackinuse (lua_State *L) {
CallInfo *ci; CallInfo *ci;
int res;
StkId lim = L->top; StkId lim = L->top;
for (ci = L->ci; ci != NULL; ci = ci->previous) { for (ci = L->ci; ci != NULL; ci = ci->previous) {
if (lim < ci->top) lim = ci->top; if (lim < ci->top) lim = ci->top;
} }
lua_assert(lim <= L->stack_last); lua_assert(lim <= L->stack_last);
return cast_int(lim - L->stack) + 1; /* part of stack in use */ res = cast_int(lim - L->stack) + 1; /* part of stack in use */
if (res < LUA_MINSTACK)
res = LUA_MINSTACK; /* ensure a minimum size */
return res;
} }
/*
** If stack size is more than 3 times the current use, reduce that size
** to twice the current use. (So, the final stack size is at most 2/3 the
** previous size, and half of its entries are empty.)
** As a particular case, if stack was handling a stack overflow and now
** it is not, 'max' (limited by LUAI_MAXSTACK) will be smaller than
** stacksize (equal to ERRORSTACKSIZE in this case), and so the stack
** will be reduced to a "regular" size.
*/
void luaD_shrinkstack (lua_State *L) { void luaD_shrinkstack (lua_State *L) {
int inuse = stackinuse(L); int inuse = stackinuse(L);
int goodsize = inuse + BASIC_STACK_SIZE; int nsize = inuse * 2; /* proposed new size */
if (goodsize > LUAI_MAXSTACK) int max = inuse * 3; /* maximum "reasonable" size */
goodsize = LUAI_MAXSTACK; /* respect stack limit */ if (max > LUAI_MAXSTACK) {
max = LUAI_MAXSTACK; /* respect stack limit */
if (nsize > LUAI_MAXSTACK)
nsize = LUAI_MAXSTACK;
}
/* if thread is currently not handling a stack overflow and its /* if thread is currently not handling a stack overflow and its
good size is smaller than current size, shrink its stack */ size is larger than maximum "reasonable" size, shrink it */
if (inuse <= (LUAI_MAXSTACK - EXTRA_STACK) && goodsize < L->stacksize) if (inuse <= LUAI_MAXSTACK && stacksize(L) > max)
luaD_reallocstack(L, goodsize, 0); /* ok if that fails */ luaD_reallocstack(L, nsize, 0); /* ok if that fails */
else /* don't change stack */ else /* don't change stack */
condmovestack(L,{},{}); /* (change only for debugging) */ condmovestack(L,{},{}); /* (change only for debugging) */
luaE_shrinkCI(L); /* shrink CI list */ luaE_shrinkCI(L); /* shrink CI list */
@@ -348,7 +368,7 @@ static StkId rethook (lua_State *L, CallInfo *ci, StkId firstres, int nres) {
/* /*
** Check whether 'func' has a '__call' metafield. If so, put it in the ** Check whether 'func' has a '__call' metafield. If so, put it in the
** stack, below original 'func', so that 'luaD_call' can call it. Raise ** stack, below original 'func', so that 'luaD_precall' can call it. Raise
** an error if there is no '__call' metafield. ** an error if there is no '__call' metafield.
*/ */
void luaD_tryfuncTM (lua_State *L, StkId func) { void luaD_tryfuncTM (lua_State *L, StkId func) {
@@ -449,12 +469,14 @@ void luaD_pretailcall (lua_State *L, CallInfo *ci, StkId func, int narg1) {
/* /*
** Call a function (C or Lua). The function to be called is at *func. ** Prepares the call to a function (C or Lua). For C functions, also do
** The arguments are on the stack, right after the function. ** the call. The function to be called is at '*func'. The arguments
** When returns, all the results are on the stack, starting at the original ** are on the stack, right after the function. Returns the CallInfo
** function position. ** to be executed, if it was a Lua function. Otherwise (a C function)
** returns NULL, with all the results on the stack, starting at the
** original function position.
*/ */
void luaD_call (lua_State *L, StkId func, int nresults) { CallInfo *luaD_precall (lua_State *L, StkId func, int nresults) {
lua_CFunction f; lua_CFunction f;
retry: retry:
switch (ttypetag(s2v(func))) { switch (ttypetag(s2v(func))) {
@@ -482,7 +504,7 @@ void luaD_call (lua_State *L, StkId func, int nresults) {
lua_lock(L); lua_lock(L);
api_checknelems(L, n); api_checknelems(L, n);
luaD_poscall(L, ci, n); luaD_poscall(L, ci, n);
break; return NULL;
} }
case LUA_VLCL: { /* Lua function */ case LUA_VLCL: { /* Lua function */
CallInfo *ci; CallInfo *ci;
@@ -494,15 +516,13 @@ void luaD_call (lua_State *L, StkId func, int nresults) {
L->ci = ci = next_ci(L); L->ci = ci = next_ci(L);
ci->nresults = nresults; ci->nresults = nresults;
ci->u.l.savedpc = p->code; /* starting point */ ci->u.l.savedpc = p->code; /* starting point */
ci->callstatus = 0;
ci->top = func + 1 + fsize; ci->top = func + 1 + fsize;
ci->func = func; ci->func = func;
L->ci = ci; L->ci = ci;
for (; narg < nfixparams; narg++) for (; narg < nfixparams; narg++)
setnilvalue(s2v(L->top++)); /* complete missing arguments */ setnilvalue(s2v(L->top++)); /* complete missing arguments */
lua_assert(ci->top <= L->stack_last); lua_assert(ci->top <= L->stack_last);
luaV_execute(L, ci); /* run the function */ return ci;
break;
} }
default: { /* not a function */ default: { /* not a function */
checkstackGCp(L, 1, func); /* space for metamethod */ checkstackGCp(L, 1, func); /* space for metamethod */
@@ -513,17 +533,37 @@ void luaD_call (lua_State *L, StkId func, int nresults) {
} }
/*
** Call a function (C or Lua) through C. 'inc' can be 1 (increment
** number of recursive invocations in the C stack) or nyci (the same
** plus increment number of non-yieldable calls).
*/
static void ccall (lua_State *L, StkId func, int nResults, int inc) {
CallInfo *ci;
L->nCcalls += inc;
if (unlikely(getCcalls(L) >= LUAI_MAXCCALLS))
luaE_checkcstack(L);
if ((ci = luaD_precall(L, func, nResults)) != NULL) { /* Lua function? */
ci->callstatus = CIST_FRESH; /* mark that it is a "fresh" execute */
luaV_execute(L, ci); /* call it */
}
L->nCcalls -= inc;
}
/*
** External interface for 'ccall'
*/
void luaD_call (lua_State *L, StkId func, int nResults) {
ccall(L, func, nResults, 1);
}
/* /*
** Similar to 'luaD_call', but does not allow yields during the call. ** Similar to 'luaD_call', but does not allow yields during the call.
*/ */
void luaD_callnoyield (lua_State *L, StkId func, int nResults) { void luaD_callnoyield (lua_State *L, StkId func, int nResults) {
incXCcalls(L); ccall(L, func, nResults, nyci);
if (getCcalls(L) <= CSTACKERR) { /* possible C stack overflow? */
luaE_exitCcall(L); /* to compensate decrement in next call */
luaE_enterCcall(L); /* check properly */
}
luaD_call(L, func, nResults);
decXCcalls(L);
} }
@@ -601,12 +641,12 @@ static int recover (lua_State *L, int status) {
if (ci == NULL) return 0; /* no recovery point */ if (ci == NULL) return 0; /* no recovery point */
/* "finish" luaD_pcall */ /* "finish" luaD_pcall */
oldtop = restorestack(L, ci->u2.funcidx); oldtop = restorestack(L, ci->u2.funcidx);
luaF_close(L, oldtop, status); /* may change the stack */
oldtop = restorestack(L, ci->u2.funcidx);
luaD_seterrorobj(L, status, oldtop);
L->ci = ci; L->ci = ci;
L->allowhook = getoah(ci->callstatus); /* restore original 'allowhook' */ L->allowhook = getoah(ci->callstatus); /* restore original 'allowhook' */
luaD_shrinkstack(L); status = luaF_close(L, oldtop, status); /* may change the stack */
oldtop = restorestack(L, ci->u2.funcidx);
luaD_seterrorobj(L, status, oldtop);
luaD_shrinkstack(L); /* restore stack size in case of overflow */
L->errfunc = ci->u.c.old_errfunc; L->errfunc = ci->u.c.old_errfunc;
return 1; /* continue running the coroutine */ return 1; /* continue running the coroutine */
} }
@@ -637,12 +677,12 @@ static void resume (lua_State *L, void *ud) {
int n = *(cast(int*, ud)); /* number of arguments */ int n = *(cast(int*, ud)); /* number of arguments */
StkId firstArg = L->top - n; /* first argument */ StkId firstArg = L->top - n; /* first argument */
CallInfo *ci = L->ci; CallInfo *ci = L->ci;
if (L->status == LUA_OK) { /* starting a coroutine? */ if (L->status == LUA_OK) /* starting a coroutine? */
luaD_call(L, firstArg - 1, LUA_MULTRET); ccall(L, firstArg - 1, LUA_MULTRET, 1); /* just call its body */
}
else { /* resuming from previous yield */ else { /* resuming from previous yield */
lua_assert(L->status == LUA_YIELD); lua_assert(L->status == LUA_YIELD);
L->status = LUA_OK; /* mark that it is running (again) */ L->status = LUA_OK; /* mark that it is running (again) */
luaE_incCstack(L); /* control the C stack */
if (isLua(ci)) /* yielded inside a hook? */ if (isLua(ci)) /* yielded inside a hook? */
luaV_execute(L, ci); /* just continue running Lua code */ luaV_execute(L, ci); /* just continue running Lua code */
else { /* 'common' yield */ else { /* 'common' yield */
@@ -670,12 +710,7 @@ LUA_API int lua_resume (lua_State *L, lua_State *from, int nargs,
} }
else if (L->status != LUA_YIELD) /* ended with errors? */ else if (L->status != LUA_YIELD) /* ended with errors? */
return resume_error(L, "cannot resume dead coroutine", nargs); return resume_error(L, "cannot resume dead coroutine", nargs);
if (from == NULL) L->nCcalls = (from) ? getCcalls(from) : 0;
L->nCcalls = CSTACKTHREAD;
else /* correct 'nCcalls' for this thread */
L->nCcalls = getCcalls(from) - L->nci - CSTACKCF;
if (L->nCcalls <= CSTACKERR)
return resume_error(L, "C stack overflow", nargs);
luai_userstateresume(L, nargs); luai_userstateresume(L, nargs);
api_checknelems(L, (L->status == LUA_OK) ? nargs + 1 : nargs); api_checknelems(L, (L->status == LUA_OK) ? nargs + 1 : nargs);
status = luaD_rawrunprotected(L, resume, &nargs); status = luaD_rawrunprotected(L, resume, &nargs);
@@ -754,7 +789,7 @@ int luaD_pcall (lua_State *L, Pfunc func, void *u,
status = luaF_close(L, oldtop, status); status = luaF_close(L, oldtop, status);
oldtop = restorestack(L, old_top); /* previous call may change stack */ oldtop = restorestack(L, old_top); /* previous call may change stack */
luaD_seterrorobj(L, status, oldtop); luaD_seterrorobj(L, status, oldtop);
luaD_shrinkstack(L); luaD_shrinkstack(L); /* restore stack size in case of overflow */
} }
L->errfunc = old_errfunc; L->errfunc = old_errfunc;
return status; return status;

1
ldo.h
View File

@@ -59,6 +59,7 @@ LUAI_FUNC void luaD_hook (lua_State *L, int event, int line,
int fTransfer, int nTransfer); int fTransfer, int nTransfer);
LUAI_FUNC void luaD_hookcall (lua_State *L, CallInfo *ci); LUAI_FUNC void luaD_hookcall (lua_State *L, CallInfo *ci);
LUAI_FUNC void luaD_pretailcall (lua_State *L, CallInfo *ci, StkId func, int n); LUAI_FUNC void luaD_pretailcall (lua_State *L, CallInfo *ci, StkId func, int n);
LUAI_FUNC CallInfo *luaD_precall (lua_State *L, StkId func, int nResults);
LUAI_FUNC void luaD_call (lua_State *L, StkId func, int nResults); LUAI_FUNC void luaD_call (lua_State *L, StkId func, int nResults);
LUAI_FUNC void luaD_callnoyield (lua_State *L, StkId func, int nResults); LUAI_FUNC void luaD_callnoyield (lua_State *L, StkId func, int nResults);
LUAI_FUNC void luaD_tryfuncTM (lua_State *L, StkId func); LUAI_FUNC void luaD_tryfuncTM (lua_State *L, StkId func);

View File

@@ -53,7 +53,7 @@ void luaF_initupvals (lua_State *L, LClosure *cl) {
uv->v = &uv->u.value; /* make it closed */ uv->v = &uv->u.value; /* make it closed */
setnilvalue(uv->v); setnilvalue(uv->v);
cl->upvals[i] = uv; cl->upvals[i] = uv;
luaC_objbarrier(L, cl, o); luaC_objbarrier(L, cl, uv);
} }
} }

54
lgc.c
View File

@@ -161,18 +161,17 @@ static void linkgclist_ (GCObject *o, GCObject **pnext, GCObject **list) {
/* /*
** Clear keys for empty entries in tables. If entry is empty ** Clear keys for empty entries in tables. If entry is empty, mark its
** and its key is not marked, mark its entry as dead. This allows the ** entry as dead. This allows the collection of the key, but keeps its
** collection of the key, but keeps its entry in the table (its removal ** entry in the table: its removal could break a chain and could break
** could break a chain). The main feature of a dead key is that it must ** a table traversal. Other places never manipulate dead keys, because
** be different from any other value, to do not disturb searches. ** its associated empty value is enough to signal that the entry is
** Other places never manipulate dead keys, because its associated empty ** logically empty.
** value is enough to signal that the entry is logically empty.
*/ */
static void clearkey (Node *n) { static void clearkey (Node *n) {
lua_assert(isempty(gval(n))); lua_assert(isempty(gval(n)));
if (keyiswhite(n)) if (keyiscollectable(n))
setdeadkey(n); /* unused and unmarked key; remove it */ setdeadkey(n); /* unused key; remove it */
} }
@@ -301,7 +300,7 @@ static void reallymarkobject (global_State *g, GCObject *o) {
if (upisopen(uv)) if (upisopen(uv))
set2gray(uv); /* open upvalues are kept gray */ set2gray(uv); /* open upvalues are kept gray */
else else
set2black(o); /* closed upvalues are visited here */ set2black(uv); /* closed upvalues are visited here */
markvalue(g, uv->v); /* mark its content */ markvalue(g, uv->v); /* mark its content */
break; break;
} }
@@ -309,7 +308,7 @@ static void reallymarkobject (global_State *g, GCObject *o) {
Udata *u = gco2u(o); Udata *u = gco2u(o);
if (u->nuvalue == 0) { /* no user values? */ if (u->nuvalue == 0) { /* no user values? */
markobjectN(g, u->metatable); /* mark its metatable */ markobjectN(g, u->metatable); /* mark its metatable */
set2black(o); /* nothing else to mark */ set2black(u); /* nothing else to mark */
break; break;
} }
/* else... */ /* else... */
@@ -633,9 +632,8 @@ static int traversethread (global_State *g, lua_State *th) {
for (uv = th->openupval; uv != NULL; uv = uv->u.open.next) for (uv = th->openupval; uv != NULL; uv = uv->u.open.next)
markobject(g, uv); /* open upvalues cannot be collected */ markobject(g, uv); /* open upvalues cannot be collected */
if (g->gcstate == GCSatomic) { /* final traversal? */ if (g->gcstate == GCSatomic) { /* final traversal? */
StkId lim = th->stack + th->stacksize; /* real end of stack */ for (; o < th->stack_last + EXTRA_STACK; o++)
for (; o < lim; o++) /* clear not-marked stack slice */ setnilvalue(s2v(o)); /* clear dead stack slice */
setnilvalue(s2v(o));
/* 'remarkupvals' may have removed thread from 'twups' list */ /* 'remarkupvals' may have removed thread from 'twups' list */
if (!isintwups(th) && th->openupval != NULL) { if (!isintwups(th) && th->openupval != NULL) {
th->twups = g->twups; /* link it back to the list */ th->twups = g->twups; /* link it back to the list */
@@ -644,7 +642,7 @@ static int traversethread (global_State *g, lua_State *th) {
} }
else if (!g->gcemergency) else if (!g->gcemergency)
luaD_shrinkstack(th); /* do not change stack in emergency cycle */ luaD_shrinkstack(th); /* do not change stack in emergency cycle */
return 1 + th->stacksize; return 1 + stacksize(th);
} }
@@ -771,12 +769,16 @@ static void freeobj (lua_State *L, GCObject *o) {
case LUA_VUPVAL: case LUA_VUPVAL:
freeupval(L, gco2upv(o)); freeupval(L, gco2upv(o));
break; break;
case LUA_VLCL: case LUA_VLCL: {
luaM_freemem(L, o, sizeLclosure(gco2lcl(o)->nupvalues)); LClosure *cl = gco2lcl(o);
luaM_freemem(L, cl, sizeLclosure(cl->nupvalues));
break; break;
case LUA_VCCL: }
luaM_freemem(L, o, sizeCclosure(gco2ccl(o)->nupvalues)); case LUA_VCCL: {
CClosure *cl = gco2ccl(o);
luaM_freemem(L, cl, sizeCclosure(cl->nupvalues));
break; break;
}
case LUA_VTABLE: case LUA_VTABLE:
luaH_free(L, gco2t(o)); luaH_free(L, gco2t(o));
break; break;
@@ -788,13 +790,17 @@ static void freeobj (lua_State *L, GCObject *o) {
luaM_freemem(L, o, sizeudata(u->nuvalue, u->len)); luaM_freemem(L, o, sizeudata(u->nuvalue, u->len));
break; break;
} }
case LUA_VSHRSTR: case LUA_VSHRSTR: {
luaS_remove(L, gco2ts(o)); /* remove it from hash table */ TString *ts = gco2ts(o);
luaM_freemem(L, o, sizelstring(gco2ts(o)->shrlen)); luaS_remove(L, ts); /* remove it from hash table */
luaM_freemem(L, ts, sizelstring(ts->shrlen));
break; break;
case LUA_VLNGSTR: }
luaM_freemem(L, o, sizelstring(gco2ts(o)->u.lnglen)); case LUA_VLNGSTR: {
TString *ts = gco2ts(o);
luaM_freemem(L, ts, sizelstring(ts->u.lnglen));
break; break;
}
default: lua_assert(0); default: lua_assert(0);
} }
} }

25
llex.c
View File

@@ -254,9 +254,10 @@ static int read_numeral (LexState *ls, SemInfo *seminfo) {
/* /*
** reads a sequence '[=*[' or ']=*]', leaving the last bracket. ** read a sequence '[=*[' or ']=*]', leaving the last bracket. If
** If sequence is well formed, return its number of '='s + 2; otherwise, ** sequence is well formed, return its number of '='s + 2; otherwise,
** return 1 if there is no '='s or 0 otherwise (an unfinished '[==...'). ** return 1 if it is a single bracket (no '='s and no 2nd bracket);
** otherwise (an unfinished '[==...') return 0.
*/ */
static size_t skip_sep (LexState *ls) { static size_t skip_sep (LexState *ls) {
size_t count = 0; size_t count = 0;
@@ -481,34 +482,34 @@ static int llex (LexState *ls, SemInfo *seminfo) {
} }
case '=': { case '=': {
next(ls); next(ls);
if (check_next1(ls, '=')) return TK_EQ; if (check_next1(ls, '=')) return TK_EQ; /* '==' */
else return '='; else return '=';
} }
case '<': { case '<': {
next(ls); next(ls);
if (check_next1(ls, '=')) return TK_LE; if (check_next1(ls, '=')) return TK_LE; /* '<=' */
else if (check_next1(ls, '<')) return TK_SHL; else if (check_next1(ls, '<')) return TK_SHL; /* '<<' */
else return '<'; else return '<';
} }
case '>': { case '>': {
next(ls); next(ls);
if (check_next1(ls, '=')) return TK_GE; if (check_next1(ls, '=')) return TK_GE; /* '>=' */
else if (check_next1(ls, '>')) return TK_SHR; else if (check_next1(ls, '>')) return TK_SHR; /* '>>' */
else return '>'; else return '>';
} }
case '/': { case '/': {
next(ls); next(ls);
if (check_next1(ls, '/')) return TK_IDIV; if (check_next1(ls, '/')) return TK_IDIV; /* '//' */
else return '/'; else return '/';
} }
case '~': { case '~': {
next(ls); next(ls);
if (check_next1(ls, '=')) return TK_NE; if (check_next1(ls, '=')) return TK_NE; /* '~=' */
else return '~'; else return '~';
} }
case ':': { case ':': {
next(ls); next(ls);
if (check_next1(ls, ':')) return TK_DBCOLON; if (check_next1(ls, ':')) return TK_DBCOLON; /* '::' */
else return ':'; else return ':';
} }
case '"': case '\'': { /* short literal strings */ case '"': case '\'': { /* short literal strings */
@@ -547,7 +548,7 @@ static int llex (LexState *ls, SemInfo *seminfo) {
return TK_NAME; return TK_NAME;
} }
} }
else { /* single-char tokens (+ - / ...) */ else { /* single-char tokens ('+', '*', '%', '{', '}', ...) */
int c = ls->current; int c = ls->current;
next(ls); next(ls);
return c; return c;

View File

@@ -234,6 +234,17 @@ typedef l_uint32 Instruction;
#endif #endif
/*
** Maximum depth for nested C calls, syntactical nested non-terminals,
** and other features implemented through recursion in C. (Value must
** fit in a 16-bit unsigned integer. It must also be compatible with
** the size of the C stack.)
*/
#if !defined(LUAI_MAXCCALLS)
#define LUAI_MAXCCALLS 200
#endif
/* /*
** macros that are executed whenever program enters the Lua core ** macros that are executed whenever program enters the Lua core
** ('lua_lock') and leaves the core ('lua_unlock') ** ('lua_lock') and leaves the core ('lua_unlock')
@@ -315,7 +326,8 @@ typedef l_uint32 Instruction;
/* exponentiation */ /* exponentiation */
#if !defined(luai_numpow) #if !defined(luai_numpow)
#define luai_numpow(L,a,b) ((void)L, l_mathop(pow)(a,b)) #define luai_numpow(L,a,b) \
((void)L, (b == 2) ? (a)*(a) : l_mathop(pow)(a,b))
#endif #endif
/* the others are quite standard operations */ /* the others are quite standard operations */
@@ -344,7 +356,7 @@ typedef l_uint32 Instruction;
#else #else
/* realloc stack keeping its size */ /* realloc stack keeping its size */
#define condmovestack(L,pre,pos) \ #define condmovestack(L,pre,pos) \
{ int sz_ = (L)->stacksize; pre; luaD_reallocstack((L), sz_, 0); pos; } { int sz_ = stacksize(L); pre; luaD_reallocstack((L), sz_, 0); pos; }
#endif #endif
#if !defined(HARDMEMTESTS) #if !defined(HARDMEMTESTS)

View File

@@ -258,7 +258,7 @@ static const char *l_str2d (const char *s, lua_Number *result) {
if (endptr == NULL) { /* failed? may be a different locale */ if (endptr == NULL) { /* failed? may be a different locale */
char buff[L_MAXLENNUM + 1]; char buff[L_MAXLENNUM + 1];
const char *pdot = strchr(s, '.'); const char *pdot = strchr(s, '.');
if (strlen(s) > L_MAXLENNUM || pdot == NULL) if (pdot == NULL || strlen(s) > L_MAXLENNUM)
return NULL; /* string too long or no dot; fail */ return NULL; /* string too long or no dot; fail */
strcpy(buff, s); /* copy string to buffer */ strcpy(buff, s); /* copy string to buffer */
buff[pdot - s] = lua_getlocaledecpoint(); /* correct decimal point */ buff[pdot - s] = lua_getlocaledecpoint(); /* correct decimal point */

View File

@@ -21,10 +21,12 @@
*/ */
#define LUA_TUPVAL LUA_NUMTYPES /* upvalues */ #define LUA_TUPVAL LUA_NUMTYPES /* upvalues */
#define LUA_TPROTO (LUA_NUMTYPES+1) /* function prototypes */ #define LUA_TPROTO (LUA_NUMTYPES+1) /* function prototypes */
#define LUA_TDEADKEY (LUA_NUMTYPES+2) /* removed keys in tables */
/* /*
** number of all possible types (including LUA_TNONE) ** number of all possible types (including LUA_TNONE but excluding DEADKEY)
*/ */
#define LUA_TOTALTYPES (LUA_TPROTO + 2) #define LUA_TOTALTYPES (LUA_TPROTO + 2)
@@ -555,7 +557,7 @@ typedef struct Proto {
/* /*
** {================================================================== ** {==================================================================
** Closures ** Functions
** =================================================================== ** ===================================================================
*/ */
@@ -743,13 +745,13 @@ typedef struct Table {
/* /*
** Use a "nil table" to mark dead keys in a table. Those keys serve ** Dead keys in tables have the tag DEADKEY but keep their original
** to keep space for removed entries, which may still be part of ** gcvalue. This distinguishes them from regular keys but allows them to
** chains. Note that the 'keytt' does not have the BIT_ISCOLLECTABLE ** be found when searched in a special way. ('next' needs that to find
** set, so these values are considered not collectable and are different ** keys removed from a table during a traversal.)
** from any valid value.
*/ */
#define setdeadkey(n) (keytt(n) = LUA_TTABLE, gckey(n) = NULL) #define setdeadkey(node) (keytt(node) = LUA_TDEADKEY)
#define keyisdead(node) (keytt(node) == LUA_TDEADKEY)
/* }================================================================== */ /* }================================================================== */

View File

@@ -261,7 +261,7 @@ OP_MMBINK,/* A B C k call C metamethod over R[A] and K[B] */
OP_UNM,/* A B R[A] := -R[B] */ OP_UNM,/* A B R[A] := -R[B] */
OP_BNOT,/* A B R[A] := ~R[B] */ OP_BNOT,/* A B R[A] := ~R[B] */
OP_NOT,/* A B R[A] := not R[B] */ OP_NOT,/* A B R[A] := not R[B] */
OP_LEN,/* A B R[A] := length of R[B] */ OP_LEN,/* A B R[A] := #R[B] (length operator) */
OP_CONCAT,/* A B R[A] := R[A].. ... ..R[A + B - 1] */ OP_CONCAT,/* A B R[A] := R[A].. ... ..R[A + B - 1] */
@@ -297,7 +297,7 @@ OP_TFORPREP,/* A Bx create upvalue for R[A + 3]; pc+=Bx */
OP_TFORCALL,/* A C R[A+4], ... ,R[A+3+C] := R[A](R[A+1], R[A+2]); */ OP_TFORCALL,/* A C R[A+4], ... ,R[A+3+C] := R[A](R[A+1], R[A+2]); */
OP_TFORLOOP,/* A Bx if R[A+2] ~= nil then { R[A]=R[A+2]; pc -= Bx } */ OP_TFORLOOP,/* A Bx if R[A+2] ~= nil then { R[A]=R[A+2]; pc -= Bx } */
OP_SETLIST,/* A B C k R[A][(C-1)*FPF+i] := R[A+i], 1 <= i <= B */ OP_SETLIST,/* A B C k R[A][C+i] := R[A+i], 1 <= i <= B */
OP_CLOSURE,/* A Bx R[A] := closure(KPROTO[Bx]) */ OP_CLOSURE,/* A Bx R[A] := closure(KPROTO[Bx]) */

View File

@@ -489,12 +489,10 @@ static void adjust_assign (LexState *ls, int nvars, int nexps, expdesc *e) {
} }
/* #define enterlevel(ls) luaE_incCstack(ls->L)
** Macros to limit the maximum recursion depth while parsing
*/
#define enterlevel(ls) luaE_enterCcall((ls)->L)
#define leavelevel(ls) luaE_exitCcall((ls)->L)
#define leavelevel(ls) ((ls)->L->nCcalls--)
/* /*
@@ -947,7 +945,7 @@ static void setvararg (FuncState *fs, int nparams) {
static void parlist (LexState *ls) { static void parlist (LexState *ls) {
/* parlist -> [ param { ',' param } ] */ /* parlist -> [ {NAME ','} (NAME | '...') ] */
FuncState *fs = ls->fs; FuncState *fs = ls->fs;
Proto *f = fs->f; Proto *f = fs->f;
int nparams = 0; int nparams = 0;
@@ -955,12 +953,12 @@ static void parlist (LexState *ls) {
if (ls->t.token != ')') { /* is 'parlist' not empty? */ if (ls->t.token != ')') { /* is 'parlist' not empty? */
do { do {
switch (ls->t.token) { switch (ls->t.token) {
case TK_NAME: { /* param -> NAME */ case TK_NAME: {
new_localvar(ls, str_checkname(ls)); new_localvar(ls, str_checkname(ls));
nparams++; nparams++;
break; break;
} }
case TK_DOTS: { /* param -> '...' */ case TK_DOTS: {
luaX_next(ls); luaX_next(ls);
isvararg = 1; isvararg = 1;
break; break;
@@ -1625,59 +1623,21 @@ static void forstat (LexState *ls, int line) {
} }
/*
** Check whether next instruction is a single jump (a 'break', a 'goto'
** to a forward label, or a 'goto' to a backward label with no variable
** to close). If so, set the name of the 'label' it is jumping to
** ("break" for a 'break') or to where it is jumping to ('target') and
** return true. If not a single jump, leave input unchanged, to be
** handled as a regular statement.
*/
static int issinglejump (LexState *ls, TString **label, int *target) {
if (testnext(ls, TK_BREAK)) { /* a break? */
*label = luaS_newliteral(ls->L, "break");
return 1;
}
else if (ls->t.token != TK_GOTO || luaX_lookahead(ls) != TK_NAME)
return 0; /* not a valid goto */
else {
TString *lname = ls->lookahead.seminfo.ts; /* label's id */
Labeldesc *lb = findlabel(ls, lname);
if (lb) { /* a backward jump? */
/* does it need to close variables? */
if (luaY_nvarstack(ls->fs) > stacklevel(ls->fs, lb->nactvar))
return 0; /* not a single jump; cannot optimize */
*target = lb->pc;
}
else /* jump forward */
*label = lname;
luaX_next(ls); /* skip goto */
luaX_next(ls); /* skip name */
return 1;
}
}
static void test_then_block (LexState *ls, int *escapelist) { static void test_then_block (LexState *ls, int *escapelist) {
/* test_then_block -> [IF | ELSEIF] cond THEN block */ /* test_then_block -> [IF | ELSEIF] cond THEN block */
BlockCnt bl; BlockCnt bl;
int line;
FuncState *fs = ls->fs; FuncState *fs = ls->fs;
TString *jlb = NULL;
int target = NO_JUMP;
expdesc v; expdesc v;
int jf; /* instruction to skip 'then' code (if condition is false) */ int jf; /* instruction to skip 'then' code (if condition is false) */
luaX_next(ls); /* skip IF or ELSEIF */ luaX_next(ls); /* skip IF or ELSEIF */
expr(ls, &v); /* read condition */ expr(ls, &v); /* read condition */
checknext(ls, TK_THEN); checknext(ls, TK_THEN);
line = ls->linenumber; if (ls->t.token == TK_BREAK) { /* 'if x then break' ? */
if (issinglejump(ls, &jlb, &target)) { /* 'if x then goto' ? */ int line = ls->linenumber;
luaK_goiffalse(ls->fs, &v); /* will jump to label if condition is true */ luaK_goiffalse(ls->fs, &v); /* will jump if condition is true */
luaX_next(ls); /* skip 'break' */
enterblock(fs, &bl, 0); /* must enter block before 'goto' */ enterblock(fs, &bl, 0); /* must enter block before 'goto' */
if (jlb != NULL) /* forward jump? */ newgotoentry(ls, luaS_newliteral(ls->L, "break"), line, v.t);
newgotoentry(ls, jlb, line, v.t); /* will be resolved later */
else /* backward jump */
luaK_patchlist(fs, v.t, target); /* jump directly to 'target' */
while (testnext(ls, ';')) {} /* skip semicolons */ while (testnext(ls, ';')) {} /* skip semicolons */
if (block_follow(ls, 0)) { /* jump is the entire block? */ if (block_follow(ls, 0)) { /* jump is the entire block? */
leaveblock(fs); leaveblock(fs);
@@ -1686,7 +1646,7 @@ static void test_then_block (LexState *ls, int *escapelist) {
else /* must skip over 'then' part if condition is false */ else /* must skip over 'then' part if condition is false */
jf = luaK_jump(fs); jf = luaK_jump(fs);
} }
else { /* regular case (not a jump) */ else { /* regular case (not a break) */
luaK_goiftrue(ls->fs, &v); /* skip over block if condition is false */ luaK_goiftrue(ls->fs, &v); /* skip over block if condition is false */
enterblock(fs, &bl, 0); enterblock(fs, &bl, 0);
jf = v.f; jf = v.f;
@@ -1754,7 +1714,7 @@ static void checktoclose (LexState *ls, int level) {
static void localstat (LexState *ls) { static void localstat (LexState *ls) {
/* stat -> LOCAL ATTRIB NAME {',' ATTRIB NAME} ['=' explist] */ /* stat -> LOCAL NAME ATTRIB { ',' NAME ATTRIB } ['=' explist] */
FuncState *fs = ls->fs; FuncState *fs = ls->fs;
int toclose = -1; /* index of to-be-closed variable (if any) */ int toclose = -1; /* index of to-be-closed variable (if any) */
Vardesc *var; /* last variable */ Vardesc *var; /* last variable */

View File

@@ -23,7 +23,7 @@
/* kinds of variables/expressions */ /* kinds of variables/expressions */
typedef enum { typedef enum {
VVOID, /* when 'expdesc' describes the last expression a list, VVOID, /* when 'expdesc' describes the last expression of a list,
this kind means an empty list (so, no expression) */ this kind means an empty list (so, no expression) */
VNIL, /* constant nil */ VNIL, /* constant nil */
VTRUE, /* constant true */ VTRUE, /* constant true */
@@ -38,7 +38,8 @@ typedef enum {
VLOCAL, /* local variable; var.sidx = stack index (local register); VLOCAL, /* local variable; var.sidx = stack index (local register);
var.vidx = relative index in 'actvar.arr' */ var.vidx = relative index in 'actvar.arr' */
VUPVAL, /* upvalue variable; info = index of upvalue in 'upvalues' */ VUPVAL, /* upvalue variable; info = index of upvalue in 'upvalues' */
VCONST, /* compile-time constant; info = absolute index in 'actvar.arr' */ VCONST, /* compile-time <const> variable;
info = absolute index in 'actvar.arr' */
VINDEXED, /* indexed variable; VINDEXED, /* indexed variable;
ind.t = table register; ind.t = table register;
ind.idx = key's R index */ ind.idx = key's R index */

View File

@@ -76,7 +76,7 @@ static unsigned int luai_makeseed (lua_State *L) {
addbuff(buff, p, &h); /* local variable */ addbuff(buff, p, &h); /* local variable */
addbuff(buff, p, &lua_newstate); /* public function */ addbuff(buff, p, &lua_newstate); /* public function */
lua_assert(p == sizeof(buff)); lua_assert(p == sizeof(buff));
return luaS_hash(buff, p, h, 1); return luaS_hash(buff, p, h);
} }
#endif #endif
@@ -97,66 +97,14 @@ void luaE_setdebt (global_State *g, l_mem debt) {
LUA_API int lua_setcstacklimit (lua_State *L, unsigned int limit) { LUA_API int lua_setcstacklimit (lua_State *L, unsigned int limit) {
global_State *g = G(L); UNUSED(L); UNUSED(limit);
int ccalls; return LUAI_MAXCCALLS; /* warning?? */
luaE_freeCI(L); /* release unused CIs */
ccalls = getCcalls(L);
if (limit >= 40000)
return 0; /* out of bounds */
limit += CSTACKERR;
if (L != g-> mainthread)
return 0; /* only main thread can change the C stack */
else if (ccalls <= CSTACKERR)
return 0; /* handling overflow */
else {
int diff = limit - g->Cstacklimit;
if (ccalls + diff <= CSTACKERR)
return 0; /* new limit would cause an overflow */
g->Cstacklimit = limit; /* set new limit */
L->nCcalls += diff; /* correct 'nCcalls' */
return limit - diff - CSTACKERR; /* success; return previous limit */
}
}
/*
** Decrement count of "C calls" and check for overflows. In case of
** a stack overflow, check appropriate error ("regular" overflow or
** overflow while handling stack overflow). If 'nCcalls' is smaller
** than CSTACKERR but larger than CSTACKMARK, it means it has just
** entered the "overflow zone", so the function raises an overflow
** error. If 'nCcalls' is smaller than CSTACKMARK (which means it is
** already handling an overflow) but larger than CSTACKERRMARK, does
** not report an error (to allow message handling to work). Otherwise,
** report a stack overflow while handling a stack overflow (probably
** caused by a repeating error in the message handling function).
*/
void luaE_enterCcall (lua_State *L) {
int ncalls = getCcalls(L);
L->nCcalls--;
if (ncalls <= CSTACKERR) { /* possible overflow? */
luaE_freeCI(L); /* release unused CIs */
ncalls = getCcalls(L); /* update call count */
if (ncalls <= CSTACKERR) { /* still overflow? */
if (ncalls <= CSTACKERRMARK) /* below error-handling zone? */
luaD_throw(L, LUA_ERRERR); /* error while handling stack error */
else if (ncalls >= CSTACKMARK) {
/* not in error-handling zone; raise the error now */
L->nCcalls = (CSTACKMARK - 1); /* enter error-handling zone */
luaG_runerror(L, "C stack overflow");
}
/* else stack is in the error-handling zone;
allow message handler to work */
}
}
} }
CallInfo *luaE_extendCI (lua_State *L) { CallInfo *luaE_extendCI (lua_State *L) {
CallInfo *ci; CallInfo *ci;
lua_assert(L->ci->next == NULL); lua_assert(L->ci->next == NULL);
luaE_enterCcall(L);
ci = luaM_new(L, CallInfo); ci = luaM_new(L, CallInfo);
lua_assert(L->ci->next == NULL); lua_assert(L->ci->next == NULL);
L->ci->next = ci; L->ci->next = ci;
@@ -175,13 +123,11 @@ void luaE_freeCI (lua_State *L) {
CallInfo *ci = L->ci; CallInfo *ci = L->ci;
CallInfo *next = ci->next; CallInfo *next = ci->next;
ci->next = NULL; ci->next = NULL;
L->nCcalls += L->nci; /* add removed elements back to 'nCcalls' */
while ((ci = next) != NULL) { while ((ci = next) != NULL) {
next = ci->next; next = ci->next;
luaM_free(L, ci); luaM_free(L, ci);
L->nci--; L->nci--;
} }
L->nCcalls -= L->nci; /* adjust result */
} }
@@ -194,7 +140,6 @@ void luaE_shrinkCI (lua_State *L) {
CallInfo *next; CallInfo *next;
if (ci == NULL) if (ci == NULL)
return; /* no extra elements */ return; /* no extra elements */
L->nCcalls += L->nci; /* add removed elements back to 'nCcalls' */
while ((next = ci->next) != NULL) { /* two extra elements? */ while ((next = ci->next) != NULL) { /* two extra elements? */
CallInfo *next2 = next->next; /* next's next */ CallInfo *next2 = next->next; /* next's next */
ci->next = next2; /* remove next from the list */ ci->next = next2; /* remove next from the list */
@@ -207,19 +152,39 @@ void luaE_shrinkCI (lua_State *L) {
ci = next2; /* continue */ ci = next2; /* continue */
} }
} }
L->nCcalls -= L->nci; /* adjust result */ }
/*
** Called when 'getCcalls(L)' larger or equal to LUAI_MAXCCALLS.
** If equal, raises an overflow error. If value is larger than
** LUAI_MAXCCALLS (which means it is handling an overflow) but
** not much larger, does not report an error (to allow overflow
** handling to work).
*/
void luaE_checkcstack (lua_State *L) {
if (getCcalls(L) == LUAI_MAXCCALLS)
luaG_runerror(L, "C stack overflow");
else if (getCcalls(L) >= (LUAI_MAXCCALLS / 10 * 11))
luaD_throw(L, LUA_ERRERR); /* error while handing stack error */
}
LUAI_FUNC void luaE_incCstack (lua_State *L) {
L->nCcalls++;
if (unlikely(getCcalls(L) >= LUAI_MAXCCALLS))
luaE_checkcstack(L);
} }
static void stack_init (lua_State *L1, lua_State *L) { static void stack_init (lua_State *L1, lua_State *L) {
int i; CallInfo *ci; int i; CallInfo *ci;
/* initialize stack array */ /* initialize stack array */
L1->stack = luaM_newvector(L, BASIC_STACK_SIZE, StackValue); L1->stack = luaM_newvector(L, BASIC_STACK_SIZE + EXTRA_STACK, StackValue);
L1->stacksize = BASIC_STACK_SIZE; for (i = 0; i < BASIC_STACK_SIZE + EXTRA_STACK; i++)
for (i = 0; i < BASIC_STACK_SIZE; i++)
setnilvalue(s2v(L1->stack + i)); /* erase new stack */ setnilvalue(s2v(L1->stack + i)); /* erase new stack */
L1->top = L1->stack; L1->top = L1->stack;
L1->stack_last = L1->stack + L1->stacksize - EXTRA_STACK; L1->stack_last = L1->stack + BASIC_STACK_SIZE;
/* initialize first ci */ /* initialize first ci */
ci = &L1->base_ci; ci = &L1->base_ci;
ci->next = ci->previous = NULL; ci->next = ci->previous = NULL;
@@ -240,7 +205,7 @@ static void freestack (lua_State *L) {
L->ci = &L->base_ci; /* free the entire 'ci' list */ L->ci = &L->base_ci; /* free the entire 'ci' list */
luaE_freeCI(L); luaE_freeCI(L);
lua_assert(L->nci == 0); lua_assert(L->nci == 0);
luaM_freearray(L, L->stack, L->stacksize); /* free stack array */ luaM_freearray(L, L->stack, stacksize(L) + EXTRA_STACK); /* free stack */
} }
@@ -290,7 +255,6 @@ static void preinit_thread (lua_State *L, global_State *g) {
L->stack = NULL; L->stack = NULL;
L->ci = NULL; L->ci = NULL;
L->nci = 0; L->nci = 0;
L->stacksize = 0;
L->twups = L; /* thread has no upvalues */ L->twups = L; /* thread has no upvalues */
L->errorJmp = NULL; L->errorJmp = NULL;
L->hook = NULL; L->hook = NULL;
@@ -335,7 +299,7 @@ LUA_API lua_State *lua_newthread (lua_State *L) {
setthvalue2s(L, L->top, L1); setthvalue2s(L, L->top, L1);
api_incr_top(L); api_incr_top(L);
preinit_thread(L1, g); preinit_thread(L1, g);
L1->nCcalls = getCcalls(L); L1->nCcalls = 0;
L1->hookmask = L->hookmask; L1->hookmask = L->hookmask;
L1->basehookcount = L->basehookcount; L1->basehookcount = L->basehookcount;
L1->hook = L->hook; L1->hook = L->hook;
@@ -396,7 +360,7 @@ LUA_API lua_State *lua_newstate (lua_Alloc f, void *ud) {
preinit_thread(L, g); preinit_thread(L, g);
g->allgc = obj2gco(L); /* by now, only object is the main thread */ g->allgc = obj2gco(L); /* by now, only object is the main thread */
L->next = NULL; L->next = NULL;
g->Cstacklimit = L->nCcalls = LUAI_MAXCSTACK + CSTACKERR; L->nCcalls = 0;
incnny(L); /* main thread is always non yieldable */ incnny(L); /* main thread is always non yieldable */
g->frealloc = f; g->frealloc = f;
g->ud = ud; g->ud = ud;

View File

@@ -87,49 +87,13 @@
/* /*
** About 'nCcalls': each thread in Lua (a lua_State) keeps a count of ** About 'nCcalls': This count has two parts: the lower 16 bits counts
** how many "C calls" it still can do in the C stack, to avoid C-stack ** the number of recursive invocations in the C stack; the higher
** overflow. This count is very rough approximation; it considers only ** 16 bits counts the number of non-yieldable calls in the stack.
** recursive functions inside the interpreter, as non-recursive calls ** (They are together so that we can change and save both with one
** can be considered using a fixed (although unknown) amount of stack ** instruction.)
** space.
**
** The count has two parts: the lower part is the count itself; the
** higher part counts the number of non-yieldable calls in the stack.
** (They are together so that we can change both with one instruction.)
**
** Because calls to external C functions can use an unknown amount
** of space (e.g., functions using an auxiliary buffer), calls
** to these functions add more than one to the count (see CSTACKCF).
**
** The proper count excludes the number of CallInfo structures allocated
** by Lua, as a kind of "potential" calls. So, when Lua calls a function
** (and "consumes" one CallInfo), it needs neither to decrement nor to
** check 'nCcalls', as its use of C stack is already accounted for.
*/ */
/* number of "C stack slots" used by an external C function */
#define CSTACKCF 10
/*
** The C-stack size is sliced in the following zones:
** - larger than CSTACKERR: normal stack;
** - [CSTACKMARK, CSTACKERR]: buffer zone to signal a stack overflow;
** - [CSTACKCF, CSTACKERRMARK]: error-handling zone;
** - below CSTACKERRMARK: buffer zone to signal overflow during overflow;
** (Because the counter can be decremented CSTACKCF at once, we need
** the so called "buffer zones", with at least that size, to properly
** detect a change from one zone to the next.)
*/
#define CSTACKERR (8 * CSTACKCF)
#define CSTACKMARK (CSTACKERR - (CSTACKCF + 2))
#define CSTACKERRMARK (CSTACKCF + 2)
/* initial limit for the C-stack of threads */
#define CSTACKTHREAD (2 * CSTACKERR)
/* true if this thread does not have non-yieldable calls in the stack */ /* true if this thread does not have non-yieldable calls in the stack */
#define yieldable(L) (((L)->nCcalls & 0xffff0000) == 0) #define yieldable(L) (((L)->nCcalls & 0xffff0000) == 0)
@@ -144,13 +108,8 @@
/* Decrement the number of non-yieldable calls */ /* Decrement the number of non-yieldable calls */
#define decnny(L) ((L)->nCcalls -= 0x10000) #define decnny(L) ((L)->nCcalls -= 0x10000)
/* Increment the number of non-yieldable calls and decrement nCcalls */ /* Non-yieldable call increment */
#define incXCcalls(L) ((L)->nCcalls += 0x10000 - CSTACKCF) #define nyci (0x10000 | 1)
/* Decrement the number of non-yieldable calls and increment nCcalls */
#define decXCcalls(L) ((L)->nCcalls -= 0x10000 - CSTACKCF)
@@ -168,12 +127,20 @@ struct lua_longjmp; /* defined in ldo.c */
#endif #endif
/* extra stack space to handle TM calls and some other extras */ /*
** Extra stack space to handle TM calls and some other extras. This
** space is not included in 'stack_last'. It is used only to avoid stack
** checks, either because the element will be promptly popped or because
** there will be a stack check soon after the push. Function frames
** never use this extra space, so it does not need to be kept clean.
*/
#define EXTRA_STACK 5 #define EXTRA_STACK 5
#define BASIC_STACK_SIZE (2*LUA_MINSTACK) #define BASIC_STACK_SIZE (2*LUA_MINSTACK)
#define stacksize(th) cast_int((th)->stack_last - (th)->stack)
/* kinds of Garbage Collection */ /* kinds of Garbage Collection */
#define KGC_INC 0 /* incremental gc */ #define KGC_INC 0 /* incremental gc */
@@ -224,14 +191,15 @@ typedef struct CallInfo {
*/ */
#define CIST_OAH (1<<0) /* original value of 'allowhook' */ #define CIST_OAH (1<<0) /* original value of 'allowhook' */
#define CIST_C (1<<1) /* call is running a C function */ #define CIST_C (1<<1) /* call is running a C function */
#define CIST_HOOKED (1<<2) /* call is running a debug hook */ #define CIST_FRESH (1<<2) /* call is on a fresh "luaV_execute" frame */
#define CIST_YPCALL (1<<3) /* call is a yieldable protected call */ #define CIST_HOOKED (1<<3) /* call is running a debug hook */
#define CIST_TAIL (1<<4) /* call was tail called */ #define CIST_YPCALL (1<<4) /* call is a yieldable protected call */
#define CIST_HOOKYIELD (1<<5) /* last hook called yielded */ #define CIST_TAIL (1<<5) /* call was tail called */
#define CIST_FIN (1<<6) /* call is running a finalizer */ #define CIST_HOOKYIELD (1<<6) /* last hook called yielded */
#define CIST_TRAN (1<<7) /* 'ci' has transfer information */ #define CIST_FIN (1<<7) /* call is running a finalizer */
#define CIST_TRAN (1<<8) /* 'ci' has transfer information */
#if defined(LUA_COMPAT_LT_LE) #if defined(LUA_COMPAT_LT_LE)
#define CIST_LEQ (1<<8) /* using __lt for __le */ #define CIST_LEQ (1<<9) /* using __lt for __le */
#endif #endif
/* active function is a Lua function */ /* active function is a Lua function */
@@ -296,7 +264,6 @@ typedef struct global_State {
TString *strcache[STRCACHE_N][STRCACHE_M]; /* cache for strings in API */ TString *strcache[STRCACHE_N][STRCACHE_M]; /* cache for strings in API */
lua_WarnFunction warnf; /* warning function */ lua_WarnFunction warnf; /* warning function */
void *ud_warn; /* auxiliary data to 'warnf' */ void *ud_warn; /* auxiliary data to 'warnf' */
unsigned int Cstacklimit; /* current limit for the C stack */
} global_State; } global_State;
@@ -311,7 +278,7 @@ struct lua_State {
StkId top; /* first free slot in the stack */ StkId top; /* first free slot in the stack */
global_State *l_G; global_State *l_G;
CallInfo *ci; /* call info for current function */ CallInfo *ci; /* call info for current function */
StkId stack_last; /* last free slot in the stack */ StkId stack_last; /* end of stack (last element + 1) */
StkId stack; /* stack base */ StkId stack; /* stack base */
UpVal *openupval; /* list of open upvalues in this stack */ UpVal *openupval; /* list of open upvalues in this stack */
GCObject *gclist; GCObject *gclist;
@@ -320,9 +287,8 @@ struct lua_State {
CallInfo base_ci; /* CallInfo for first level (C calling Lua) */ CallInfo base_ci; /* CallInfo for first level (C calling Lua) */
volatile lua_Hook hook; volatile lua_Hook hook;
ptrdiff_t errfunc; /* current error handling function (stack index) */ ptrdiff_t errfunc; /* current error handling function (stack index) */
l_uint32 nCcalls; /* number of allowed nested C calls - 'nci' */ l_uint32 nCcalls; /* number of nested (non-yieldable | C) calls */
int oldpc; /* last pc traced */ int oldpc; /* last pc traced */
int stacksize;
int basehookcount; int basehookcount;
int hookcount; int hookcount;
volatile l_signalT hookmask; volatile l_signalT hookmask;
@@ -389,12 +355,11 @@ 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);
LUAI_FUNC void luaE_freeCI (lua_State *L); LUAI_FUNC void luaE_freeCI (lua_State *L);
LUAI_FUNC void luaE_shrinkCI (lua_State *L); LUAI_FUNC void luaE_shrinkCI (lua_State *L);
LUAI_FUNC void luaE_enterCcall (lua_State *L); LUAI_FUNC void luaE_checkcstack (lua_State *L);
LUAI_FUNC void luaE_incCstack (lua_State *L);
LUAI_FUNC void luaE_warning (lua_State *L, const char *msg, int tocont); LUAI_FUNC void luaE_warning (lua_State *L, const char *msg, int tocont);
LUAI_FUNC void luaE_warnerror (lua_State *L, const char *where); LUAI_FUNC void luaE_warnerror (lua_State *L, const char *where);
#define luaE_exitCcall(L) ((L)->nCcalls++)
#endif #endif

View File

@@ -22,16 +22,6 @@
#include "lstring.h" #include "lstring.h"
/*
** Lua will use at most ~(2^LUAI_HASHLIMIT) bytes from a long string to
** compute its hash
*/
#if !defined(LUAI_HASHLIMIT)
#define LUAI_HASHLIMIT 5
#endif
/* /*
** Maximum size for string table. ** Maximum size for string table.
*/ */
@@ -50,10 +40,9 @@ int luaS_eqlngstr (TString *a, TString *b) {
} }
unsigned int luaS_hash (const char *str, size_t l, unsigned int seed, unsigned int luaS_hash (const char *str, size_t l, unsigned int seed) {
size_t step) {
unsigned int h = seed ^ cast_uint(l); unsigned int h = seed ^ cast_uint(l);
for (; l >= step; l -= step) for (; l > 0; l--)
h ^= ((h<<5) + (h>>2) + cast_byte(str[l - 1])); h ^= ((h<<5) + (h>>2) + cast_byte(str[l - 1]));
return h; return h;
} }
@@ -63,8 +52,7 @@ unsigned int luaS_hashlongstr (TString *ts) {
lua_assert(ts->tt == LUA_VLNGSTR); lua_assert(ts->tt == LUA_VLNGSTR);
if (ts->extra == 0) { /* no hash? */ if (ts->extra == 0) { /* no hash? */
size_t len = ts->u.lnglen; size_t len = ts->u.lnglen;
size_t step = (len >> LUAI_HASHLIMIT) + 1; ts->hash = luaS_hash(getstr(ts), len, ts->hash);
ts->hash = luaS_hash(getstr(ts), len, ts->hash, step);
ts->extra = 1; /* now it has its hash */ ts->extra = 1; /* now it has its hash */
} }
return ts->hash; return ts->hash;
@@ -201,7 +189,7 @@ static TString *internshrstr (lua_State *L, const char *str, size_t l) {
TString *ts; TString *ts;
global_State *g = G(L); global_State *g = G(L);
stringtable *tb = &g->strt; stringtable *tb = &g->strt;
unsigned int h = luaS_hash(str, l, g->seed, 1); unsigned int h = luaS_hash(str, l, g->seed);
TString **list = &tb->hash[lmod(h, tb->size)]; TString **list = &tb->hash[lmod(h, tb->size)];
lua_assert(str != NULL); /* otherwise 'memcmp'/'memcpy' are undefined */ lua_assert(str != NULL); /* otherwise 'memcmp'/'memcpy' are undefined */
for (ts = *list; ts != NULL; ts = ts->u.hnext) { for (ts = *list; ts != NULL; ts = ts->u.hnext) {

View File

@@ -41,8 +41,7 @@
#define eqshrstr(a,b) check_exp((a)->tt == LUA_VSHRSTR, (a) == (b)) #define eqshrstr(a,b) check_exp((a)->tt == LUA_VSHRSTR, (a) == (b))
LUAI_FUNC unsigned int luaS_hash (const char *str, size_t l, LUAI_FUNC unsigned int luaS_hash (const char *str, size_t l, unsigned int seed);
unsigned int seed, size_t step);
LUAI_FUNC unsigned int luaS_hashlongstr (TString *ts); LUAI_FUNC unsigned int luaS_hashlongstr (TString *ts);
LUAI_FUNC int luaS_eqlngstr (TString *a, TString *b); LUAI_FUNC int luaS_eqlngstr (TString *a, TString *b);
LUAI_FUNC void luaS_resize (lua_State *L, int newsize); LUAI_FUNC void luaS_resize (lua_State *L, int newsize);

View File

@@ -1365,7 +1365,6 @@ typedef union Ftypes {
float f; float f;
double d; double d;
lua_Number n; lua_Number n;
char buff[5 * sizeof(lua_Number)]; /* enough for any float type */
} Ftypes; } Ftypes;
@@ -1535,12 +1534,10 @@ static void packint (luaL_Buffer *b, lua_Unsigned n,
** Copy 'size' bytes from 'src' to 'dest', correcting endianness if ** Copy 'size' bytes from 'src' to 'dest', correcting endianness if
** given 'islittle' is different from native endianness. ** given 'islittle' is different from native endianness.
*/ */
static void copywithendian (volatile char *dest, volatile const char *src, static void copywithendian (char *dest, const char *src,
int size, int islittle) { int size, int islittle) {
if (islittle == nativeendian.little) { if (islittle == nativeendian.little)
while (size-- != 0) memcpy(dest, src, size);
*(dest++) = *(src++);
}
else { else {
dest += size - 1; dest += size - 1;
while (size-- != 0) while (size-- != 0)
@@ -1584,14 +1581,14 @@ static int str_pack (lua_State *L) {
break; break;
} }
case Kfloat: { /* floating-point options */ case Kfloat: { /* floating-point options */
volatile Ftypes u; Ftypes u;
char *buff = luaL_prepbuffsize(&b, size); char *buff = luaL_prepbuffsize(&b, size);
lua_Number n = luaL_checknumber(L, arg); /* get argument */ lua_Number n = luaL_checknumber(L, arg); /* get argument */
if (size == sizeof(u.f)) u.f = (float)n; /* copy it into 'u' */ if (size == sizeof(u.f)) u.f = (float)n; /* copy it into 'u' */
else if (size == sizeof(u.d)) u.d = (double)n; else if (size == sizeof(u.d)) u.d = (double)n;
else u.n = n; else u.n = n;
/* move 'u' to final result, correcting endianness if needed */ /* move 'u' to final result, correcting endianness if needed */
copywithendian(buff, u.buff, size, h.islittle); copywithendian(buff, (char *)&u, size, h.islittle);
luaL_addsize(&b, size); luaL_addsize(&b, size);
break; break;
} }
@@ -1717,9 +1714,9 @@ static int str_unpack (lua_State *L) {
break; break;
} }
case Kfloat: { case Kfloat: {
volatile Ftypes u; Ftypes u;
lua_Number num; lua_Number num;
copywithendian(u.buff, data + pos, size, h.islittle); copywithendian((char *)&u, data + pos, size, h.islittle);
if (size == sizeof(u.f)) num = (lua_Number)u.f; if (size == sizeof(u.f)) num = (lua_Number)u.f;
else if (size == sizeof(u.d)) num = (lua_Number)u.d; else if (size == sizeof(u.d)) num = (lua_Number)u.d;
else num = u.n; else num = u.n;
@@ -1738,7 +1735,7 @@ static int str_unpack (lua_State *L) {
break; break;
} }
case Kzstr: { case Kzstr: {
size_t len = (int)strlen(data + pos); size_t len = strlen(data + pos);
luaL_argcheck(L, pos + len < ld, 2, luaL_argcheck(L, pos + len < ld, 2,
"unfinished string for format 'z'"); "unfinished string for format 'z'");
lua_pushlstring(L, data + pos, len); lua_pushlstring(L, data + pos, len);

View File

@@ -166,17 +166,30 @@ static Node *mainpositionTV (const Table *t, const TValue *key) {
/* /*
** Check whether key 'k1' is equal to the key in node 'n2'. ** Check whether key 'k1' is equal to the key in node 'n2'. This
** This equality is raw, so there are no metamethods. Floats ** equality is raw, so there are no metamethods. Floats with integer
** with integer values have been normalized, so integers cannot ** values have been normalized, so integers cannot be equal to
** be equal to floats. It is assumed that 'eqshrstr' is simply ** floats. It is assumed that 'eqshrstr' is simply pointer equality, so
** pointer equality, so that short strings are handled in the ** that short strings are handled in the default case.
** default case. ** A true 'deadok' means to accept dead keys as equal to their original
** values. All dead keys are compared in the default case, by pointer
** identity. (Only collectable objects can produce dead keys.) Note that
** dead long strings are also compared by identity.
** Once a key is dead, its corresponding value may be collected, and
** then another value can be created with the same address. If this
** other value is given to 'next', 'equalkey' will signal a false
** positive. In a regular traversal, this situation should never happen,
** as all keys given to 'next' came from the table itself, and therefore
** could not have been collected. Outside a regular traversal, we
** have garbage in, garbage out. What is relevant is that this false
** positive does not break anything. (In particular, 'next' will return
** some other valid item on the table or nil.)
*/ */
static int equalkey (const TValue *k1, const Node *n2) { static int equalkey (const TValue *k1, const Node *n2, int deadok) {
if (rawtt(k1) != keytt(n2)) /* not the same variants? */ if ((rawtt(k1) != keytt(n2)) && /* not the same variants? */
!(deadok && keyisdead(n2) && iscollectable(k1)))
return 0; /* cannot be same key */ return 0; /* cannot be same key */
switch (ttypetag(k1)) { switch (keytt(n2)) {
case LUA_VNIL: case LUA_VFALSE: case LUA_VTRUE: case LUA_VNIL: case LUA_VFALSE: case LUA_VTRUE:
return 1; return 1;
case LUA_VNUMINT: case LUA_VNUMINT:
@@ -187,7 +200,7 @@ static int equalkey (const TValue *k1, const Node *n2) {
return pvalue(k1) == pvalueraw(keyval(n2)); return pvalue(k1) == pvalueraw(keyval(n2));
case LUA_VLCF: case LUA_VLCF:
return fvalue(k1) == fvalueraw(keyval(n2)); return fvalue(k1) == fvalueraw(keyval(n2));
case LUA_VLNGSTR: case ctb(LUA_VLNGSTR):
return luaS_eqlngstr(tsvalue(k1), keystrval(n2)); return luaS_eqlngstr(tsvalue(k1), keystrval(n2));
default: default:
return gcvalue(k1) == gcvalueraw(keyval(n2)); return gcvalue(k1) == gcvalueraw(keyval(n2));
@@ -251,11 +264,12 @@ static unsigned int setlimittosize (Table *t) {
/* /*
** "Generic" get version. (Not that generic: not valid for integers, ** "Generic" get version. (Not that generic: not valid for integers,
** which may be in array part, nor for floats with integral values.) ** which may be in array part, nor for floats with integral values.)
** See explanation about 'deadok' in function 'equalkey'.
*/ */
static const TValue *getgeneric (Table *t, const TValue *key) { static const TValue *getgeneric (Table *t, const TValue *key, int deadok) {
Node *n = mainpositionTV(t, key); Node *n = mainpositionTV(t, key);
for (;;) { /* check whether 'key' is somewhere in the chain */ for (;;) { /* check whether 'key' is somewhere in the chain */
if (equalkey(key, n)) if (equalkey(key, n, deadok))
return gval(n); /* that's it */ return gval(n); /* that's it */
else { else {
int nx = gnext(n); int nx = gnext(n);
@@ -292,7 +306,7 @@ static unsigned int findindex (lua_State *L, Table *t, TValue *key,
if (i - 1u < asize) /* is 'key' inside array part? */ if (i - 1u < asize) /* is 'key' inside array part? */
return i; /* yes; that's the index */ return i; /* yes; that's the index */
else { else {
const TValue *n = getgeneric(t, key); const TValue *n = getgeneric(t, key, 1);
if (unlikely(isabstkey(n))) if (unlikely(isabstkey(n)))
luaG_runerror(L, "invalid key to 'next'"); /* key not found */ luaG_runerror(L, "invalid key to 'next'"); /* key not found */
i = cast_int(nodefromval(n) - gnode(t, 0)); /* key index in hash table */ i = cast_int(nodefromval(n) - gnode(t, 0)); /* key index in hash table */
@@ -730,7 +744,7 @@ const TValue *luaH_getstr (Table *t, TString *key) {
else { /* for long strings, use generic case */ else { /* for long strings, use generic case */
TValue ko; TValue ko;
setsvalue(cast(lua_State *, NULL), &ko, key); setsvalue(cast(lua_State *, NULL), &ko, key);
return getgeneric(t, &ko); return getgeneric(t, &ko, 0);
} }
} }
@@ -750,7 +764,7 @@ const TValue *luaH_get (Table *t, const TValue *key) {
/* else... */ /* else... */
} /* FALLTHROUGH */ } /* FALLTHROUGH */
default: default:
return getgeneric(t, key); return getgeneric(t, key, 0);
} }
} }

View File

@@ -430,17 +430,17 @@ static void checkstack (global_State *g, lua_State *L1) {
UpVal *uv; UpVal *uv;
lua_assert(!isdead(g, L1)); lua_assert(!isdead(g, L1));
if (L1->stack == NULL) { /* incomplete thread? */ if (L1->stack == NULL) { /* incomplete thread? */
lua_assert(L1->stacksize == 0 && L1->openupval == NULL && lua_assert(L1->openupval == NULL && L1->ci == NULL);
L1->ci == NULL);
return; return;
} }
for (uv = L1->openupval; uv != NULL; uv = uv->u.open.next) for (uv = L1->openupval; uv != NULL; uv = uv->u.open.next)
lua_assert(upisopen(uv)); /* must be open */ lua_assert(upisopen(uv)); /* must be open */
lua_assert(L1->top <= L1->stack_last);
for (ci = L1->ci; ci != NULL; ci = ci->previous) { for (ci = L1->ci; ci != NULL; ci = ci->previous) {
lua_assert(ci->top <= L1->stack_last); lua_assert(ci->top <= L1->stack_last);
lua_assert(lua_checkpc(ci)); lua_assert(lua_checkpc(ci));
} }
for (o = L1->stack; o < L1->stack_last + EXTRA_STACK; o++) for (o = L1->stack; o < L1->stack_last; o++)
checkliveness(L1, s2v(o)); /* entire stack must have valid values */ checkliveness(L1, s2v(o)); /* entire stack must have valid values */
} }
@@ -523,7 +523,6 @@ static lu_mem checkgraylist (global_State *g, GCObject *o) {
((void)g); /* better to keep it available if we need to print an object */ ((void)g); /* better to keep it available if we need to print an object */
while (o) { while (o) {
lua_assert(!!isgray(o) ^ (getage(o) == G_TOUCHED2)); lua_assert(!!isgray(o) ^ (getage(o) == G_TOUCHED2));
//lua_assert(isgray(o) || getage(o) == G_TOUCHED2);
lua_assert(!testbit(o->marked, TESTBIT)); lua_assert(!testbit(o->marked, TESTBIT));
if (keepinvariant(g)) if (keepinvariant(g))
l_setbit(o->marked, TESTBIT); /* mark that object is in a gray list */ l_setbit(o->marked, TESTBIT); /* mark that object is in a gray list */
@@ -864,7 +863,7 @@ static int alloc_failnext (lua_State *L) {
l_memcontrol.failnext = 1; l_memcontrol.failnext = 1;
return 0; return 0;
} }
static int settrick (lua_State *L) { static int settrick (lua_State *L) {
if (ttisnil(obj_at(L, 1))) if (ttisnil(obj_at(L, 1)))
@@ -969,7 +968,7 @@ static int hash_query (lua_State *L) {
static int stacklevel (lua_State *L) { static int stacklevel (lua_State *L) {
unsigned long a = 0; unsigned long a = 0;
lua_pushinteger(L, (L->top - L->stack)); lua_pushinteger(L, (L->top - L->stack));
lua_pushinteger(L, (L->stack_last - L->stack)); lua_pushinteger(L, stacksize(L));
lua_pushinteger(L, L->nCcalls); lua_pushinteger(L, L->nCcalls);
lua_pushinteger(L, L->nci); lua_pushinteger(L, L->nci);
lua_pushinteger(L, (unsigned long)&a); lua_pushinteger(L, (unsigned long)&a);

View File

@@ -23,11 +23,6 @@
#define LUAI_ASSERT #define LUAI_ASSERT
/* compiled with -O0, Lua uses a lot of C stack space... */
#undef LUAI_MAXCSTACK
#define LUAI_MAXCSTACK 400
/* to avoid warnings, and to make sure value is really unused */ /* to avoid warnings, and to make sure value is really unused */
#define UNUSED(x) (x=0, (void)(x)) #define UNUSED(x) (x=0, (void)(x))

16
lua.c
View File

@@ -416,14 +416,18 @@ static int handle_luainit (lua_State *L) {
/* /*
** Returns the string to be used as a prompt by the interpreter. ** Return the string to be used as a prompt by the interpreter. Leave
** the string (or nil, if using the default value) on the stack, to keep
** it anchored.
*/ */
static const char *get_prompt (lua_State *L, int firstline) { static const char *get_prompt (lua_State *L, int firstline) {
const char *p; if (lua_getglobal(L, firstline ? "_PROMPT" : "_PROMPT2") == LUA_TNIL)
lua_getglobal(L, firstline ? "_PROMPT" : "_PROMPT2"); return (firstline ? LUA_PROMPT : LUA_PROMPT2); /* use the default */
p = lua_tostring(L, -1); else { /* apply 'tostring' over the value */
if (p == NULL) p = (firstline ? LUA_PROMPT : LUA_PROMPT2); const char *p = luaL_tolstring(L, -1, NULL);
return p; lua_remove(L, -2); /* remove original value */
return p;
}
} }
/* mark in error messages for incomplete statements */ /* mark in error messages for incomplete statements */

2
lua.h
View File

@@ -18,7 +18,7 @@
#define LUA_VERSION_MAJOR "5" #define LUA_VERSION_MAJOR "5"
#define LUA_VERSION_MINOR "4" #define LUA_VERSION_MINOR "4"
#define LUA_VERSION_RELEASE "1" #define LUA_VERSION_RELEASE "2"
#define LUA_VERSION_NUM 504 #define LUA_VERSION_NUM 504
#define LUA_VERSION_RELEASE_NUM (LUA_VERSION_NUM * 100 + 0) #define LUA_VERSION_RELEASE_NUM (LUA_VERSION_NUM * 100 + 0)

View File

@@ -36,21 +36,6 @@
** ===================================================================== ** =====================================================================
*/ */
/*
@@ LUAI_MAXCSTACK defines the maximum depth for nested calls and
** also limits the maximum depth of other recursive algorithms in
** the implementation, such as syntactic analysis. A value too
** large may allow the interpreter to crash (C-stack overflow).
** The default value seems ok for regular machines, but may be
** too high for restricted hardware.
** The test file 'cstack.lua' may help finding a good limit.
** (It will crash with a limit too high.)
*/
#if !defined(LUAI_MAXCSTACK)
#define LUAI_MAXCSTACK 2000
#endif
/* /*
@@ LUA_USE_C89 controls the use of non-ISO-C89 features. @@ LUA_USE_C89 controls the use of non-ISO-C89 features.
** Define it if you want Lua to avoid the use of a few C99 features ** Define it if you want Lua to avoid the use of a few C99 features

75
lvm.c
View File

@@ -229,7 +229,7 @@ static int forprep (lua_State *L, StkId ra) {
count /= l_castS2U(-(step + 1)) + 1u; count /= l_castS2U(-(step + 1)) + 1u;
} }
/* store the counter in place of the limit (which won't be /* store the counter in place of the limit (which won't be
needed anymore */ needed anymore) */
setivalue(plimit, l_castU2S(count)); setivalue(plimit, l_castU2S(count));
} }
} }
@@ -1092,15 +1092,11 @@ void luaV_finishOp (lua_State *L) {
#define ProtectNT(exp) (savepc(L), (exp), updatetrap(ci)) #define ProtectNT(exp) (savepc(L), (exp), updatetrap(ci))
/* /*
** Protect code that will finish the loop (returns) or can only raise ** Protect code that can only raise errors. (That is, it cannnot change
** errors. (That is, it will not return to the interpreter main loop ** the stack or hooks.)
** after changing the stack or hooks.)
*/ */
#define halfProtect(exp) (savestate(L,ci), (exp)) #define halfProtect(exp) (savestate(L,ci), (exp))
/* idem, but without changing the stack */
#define halfProtectNT(exp) (savepc(L), (exp))
/* 'c' is the limit of live values in the stack */ /* 'c' is the limit of live values in the stack */
#define checkGC(L,c) \ #define checkGC(L,c) \
{ luaC_condGC(L, (savepc(L), L->top = (c)), \ { luaC_condGC(L, (savepc(L), L->top = (c)), \
@@ -1132,17 +1128,20 @@ void luaV_execute (lua_State *L, CallInfo *ci) {
#if LUA_USE_JUMPTABLE #if LUA_USE_JUMPTABLE
#include "ljumptab.h" #include "ljumptab.h"
#endif #endif
tailcall: startfunc:
trap = L->hookmask; trap = L->hookmask;
returning: /* trap already set */
cl = clLvalue(s2v(ci->func)); cl = clLvalue(s2v(ci->func));
k = cl->p->k; k = cl->p->k;
pc = ci->u.l.savedpc; pc = ci->u.l.savedpc;
if (trap) { if (trap) {
if (cl->p->is_vararg) if (pc == cl->p->code) { /* first instruction (not resuming)? */
trap = 0; /* hooks will start after VARARGPREP instruction */ if (cl->p->is_vararg)
else if (pc == cl->p->code) /* first instruction (not resuming)? */ trap = 0; /* hooks will start after VARARGPREP instruction */
luaD_hookcall(L, ci); else /* check 'call' hook */
ci->u.l.trap = 1; /* there may be other hooks */ luaD_hookcall(L, ci);
}
ci->u.l.trap = 1; /* assume trap is on, for now */
} }
base = ci->func + 1; base = ci->func + 1;
/* main loop of interpreter */ /* main loop of interpreter */
@@ -1151,7 +1150,7 @@ void luaV_execute (lua_State *L, CallInfo *ci) {
StkId ra; /* instruction's A register */ StkId ra; /* instruction's A register */
vmfetch(); vmfetch();
lua_assert(base == ci->func + 1); lua_assert(base == ci->func + 1);
lua_assert(base <= L->top && L->top < L->stack + L->stacksize); lua_assert(base <= L->top && L->top < L->stack_last);
/* invalidate top for instructions not expecting it */ /* invalidate top for instructions not expecting it */
lua_assert(isIT(i) || (cast_void(L->top = base), 1)); lua_assert(isIT(i) || (cast_void(L->top = base), 1));
vmdispatch (GET_OPCODE(i)) { vmdispatch (GET_OPCODE(i)) {
@@ -1606,24 +1605,32 @@ void luaV_execute (lua_State *L, CallInfo *ci) {
vmbreak; vmbreak;
} }
vmcase(OP_CALL) { vmcase(OP_CALL) {
CallInfo *newci;
int b = GETARG_B(i); int b = GETARG_B(i);
int nresults = GETARG_C(i) - 1; int nresults = GETARG_C(i) - 1;
if (b != 0) /* fixed number of arguments? */ if (b != 0) /* fixed number of arguments? */
L->top = ra + b; /* top signals number of arguments */ L->top = ra + b; /* top signals number of arguments */
/* else previous instruction set top */ /* else previous instruction set top */
ProtectNT(luaD_call(L, ra, nresults)); savepc(L); /* in case of errors */
if ((newci = luaD_precall(L, ra, nresults)) == NULL)
updatetrap(ci); /* C call; nothing else to be done */
else { /* Lua call: run function in this same C frame */
ci = newci;
ci->callstatus = 0; /* call re-uses 'luaV_execute' */
goto startfunc;
}
vmbreak; vmbreak;
} }
vmcase(OP_TAILCALL) { vmcase(OP_TAILCALL) {
int b = GETARG_B(i); /* number of arguments + 1 (function) */ int b = GETARG_B(i); /* number of arguments + 1 (function) */
int nparams1 = GETARG_C(i); int nparams1 = GETARG_C(i);
/* delat is virtual 'func' - real 'func' (vararg functions) */ /* delta is virtual 'func' - real 'func' (vararg functions) */
int delta = (nparams1) ? ci->u.l.nextraargs + nparams1 : 0; int delta = (nparams1) ? ci->u.l.nextraargs + nparams1 : 0;
if (b != 0) if (b != 0)
L->top = ra + b; L->top = ra + b;
else /* previous instruction set top */ else /* previous instruction set top */
b = cast_int(L->top - ra); b = cast_int(L->top - ra);
savepc(ci); /* some calls here can raise errors */ savepc(ci); /* several calls here can raise errors */
if (TESTARG_k(i)) { if (TESTARG_k(i)) {
/* close upvalues from current call; the compiler ensures /* close upvalues from current call; the compiler ensures
that there are no to-be-closed variables here, so this that there are no to-be-closed variables here, so this
@@ -1637,16 +1644,17 @@ void luaV_execute (lua_State *L, CallInfo *ci) {
checkstackGCp(L, 1, ra); checkstackGCp(L, 1, ra);
} }
if (!ttisLclosure(s2v(ra))) { /* C function? */ if (!ttisLclosure(s2v(ra))) { /* C function? */
luaD_call(L, ra, LUA_MULTRET); /* call it */ luaD_precall(L, ra, LUA_MULTRET); /* call it */
updatetrap(ci); updatetrap(ci);
updatestack(ci); /* stack may have been relocated */ updatestack(ci); /* stack may have been relocated */
ci->func -= delta; ci->func -= delta; /* restore 'func' (if vararg) */
luaD_poscall(L, ci, cast_int(L->top - ra)); luaD_poscall(L, ci, cast_int(L->top - ra)); /* finish caller */
return; updatetrap(ci); /* 'luaD_poscall' can change hooks */
goto ret; /* caller returns after the tail call */
} }
ci->func -= delta; ci->func -= delta; /* restore 'func' (if vararg) */
luaD_pretailcall(L, ci, ra, b); /* prepare call frame */ luaD_pretailcall(L, ci, ra, b); /* prepare call frame */
goto tailcall; goto startfunc; /* execute the callee */
} }
vmcase(OP_RETURN) { vmcase(OP_RETURN) {
int n = GETARG_B(i) - 1; /* number of results */ int n = GETARG_B(i) - 1; /* number of results */
@@ -1665,12 +1673,15 @@ void luaV_execute (lua_State *L, CallInfo *ci) {
ci->func -= ci->u.l.nextraargs + nparams1; ci->func -= ci->u.l.nextraargs + nparams1;
L->top = ra + n; /* set call for 'luaD_poscall' */ L->top = ra + n; /* set call for 'luaD_poscall' */
luaD_poscall(L, ci, n); luaD_poscall(L, ci, n);
return; updatetrap(ci); /* 'luaD_poscall' can change hooks */
goto ret;
} }
vmcase(OP_RETURN0) { vmcase(OP_RETURN0) {
if (L->hookmask) { if (L->hookmask) {
L->top = ra; L->top = ra;
halfProtectNT(luaD_poscall(L, ci, 0)); /* no hurry... */ savepc(ci);
luaD_poscall(L, ci, 0); /* no hurry... */
trap = 1;
} }
else { /* do the 'poscall' here */ else { /* do the 'poscall' here */
int nres = ci->nresults; int nres = ci->nresults;
@@ -1679,12 +1690,14 @@ void luaV_execute (lua_State *L, CallInfo *ci) {
while (nres-- > 0) while (nres-- > 0)
setnilvalue(s2v(L->top++)); /* all results are nil */ setnilvalue(s2v(L->top++)); /* all results are nil */
} }
return; goto ret;
} }
vmcase(OP_RETURN1) { vmcase(OP_RETURN1) {
if (L->hookmask) { if (L->hookmask) {
L->top = ra + 1; L->top = ra + 1;
halfProtectNT(luaD_poscall(L, ci, 1)); /* no hurry... */ savepc(ci);
luaD_poscall(L, ci, 1); /* no hurry... */
trap = 1;
} }
else { /* do the 'poscall' here */ else { /* do the 'poscall' here */
int nres = ci->nresults; int nres = ci->nresults;
@@ -1698,7 +1711,13 @@ void luaV_execute (lua_State *L, CallInfo *ci) {
setnilvalue(s2v(L->top++)); setnilvalue(s2v(L->top++));
} }
} }
return; ret: /* return from a Lua function */
if (ci->callstatus & CIST_FRESH)
return; /* end this frame */
else {
ci = ci->previous;
goto returning; /* continue running caller in this frame */
}
} }
vmcase(OP_FORLOOP) { vmcase(OP_FORLOOP) {
if (ttisinteger(s2v(ra + 2))) { /* integer loop? */ if (ttisinteger(s2v(ra + 2))) { /* integer loop? */

View File

@@ -109,16 +109,6 @@ $(LUA_T): $(LUA_O) $(CORE_T)
$(CC) -o $@ $(MYLDFLAGS) $(LUA_O) $(CORE_T) $(LIBS) $(MYLIBS) $(DL) $(CC) -o $@ $(MYLDFLAGS) $(LUA_O) $(CORE_T) $(LIBS) $(MYLIBS) $(DL)
llex.o:
$(CC) $(CFLAGS) -Os -c llex.c
lparser.o:
$(CC) $(CFLAGS) -Os -c lparser.c
lcode.o:
$(CC) $(CFLAGS) -Os -c lcode.c
clean: clean:
$(RM) $(ALL_T) $(ALL_O) $(RM) $(ALL_T) $(ALL_O)

View File

@@ -2436,8 +2436,16 @@ When you interact with the Lua API,
you are responsible for ensuring consistency. you are responsible for ensuring consistency.
In particular, In particular,
@emph{you are responsible for controlling stack overflow}. @emph{you are responsible for controlling stack overflow}.
You can use the function @Lid{lua_checkstack} When you call any API function,
to ensure that the stack has enough space for pushing new elements. you must ensure the stack has enough room to accommodate the results.
There is one exception to the above rule:
When you call a Lua function
without a fixed number of results @seeF{lua_call},
Lua ensures that the stack has enough space for all results.
However, it does not ensure any extra space.
So, before pushing anything on the stack after such a call
you should use @Lid{lua_checkstack}.
Whenever Lua calls C, Whenever Lua calls C,
it ensures that the stack has space for it ensures that the stack has space for
@@ -2446,13 +2454,9 @@ that is, you can safely push up to @id{LUA_MINSTACK} values into it.
@id{LUA_MINSTACK} is defined as 20, @id{LUA_MINSTACK} is defined as 20,
so that usually you do not have to worry about stack space so that usually you do not have to worry about stack space
unless your code has loops pushing elements onto the stack. unless your code has loops pushing elements onto the stack.
Whenever necessary,
When you call a Lua function you can use the function @Lid{lua_checkstack}
without a fixed number of results @seeF{lua_call}, to ensure that the stack has enough space for pushing new elements.
Lua ensures that the stack has enough space for all results,
but it does not ensure any extra space.
So, before pushing anything on the stack after such a call
you should use @Lid{lua_checkstack}.
} }
@@ -2512,7 +2516,8 @@ Lua's garbage collection can free or move internal memory
and then invalidate pointers to internal strings. and then invalidate pointers to internal strings.
To allow a safe use of these pointers, To allow a safe use of these pointers,
The API guarantees that any pointer to a string in a stack index The API guarantees that any pointer to a string in a stack index
is valid while the value at that index is neither modified nor popped. is valid while the string value at that index is not removed from the stack.
(It can be moved to another index, though.)
When the index is a pseudo-index (referring to an upvalue), When the index is a pseudo-index (referring to an upvalue),
the pointer is valid while the corresponding call is active and the pointer is valid while the corresponding call is active and
the corresponding upvalue is not modified. the corresponding upvalue is not modified.
@@ -2695,7 +2700,7 @@ Therefore, if a @N{C function} @id{foo} calls an API function
and this API function yields and this API function yields
(directly or indirectly by calling another function that yields), (directly or indirectly by calling another function that yields),
Lua cannot return to @id{foo} any more, Lua cannot return to @id{foo} any more,
because the @id{longjmp} removes its frame from the C stack. because the @id{longjmp} removes its frame from the @N{C stack}.
To avoid this kind of problem, To avoid this kind of problem,
Lua raises an error whenever it tries to yield across an API call, Lua raises an error whenever it tries to yield across an API call,
@@ -2719,7 +2724,7 @@ After the thread resumes,
it eventually will finish running the callee function. it eventually will finish running the callee function.
However, However,
the callee function cannot return to the original function, the callee function cannot return to the original function,
because its frame in the C stack was destroyed by the yield. because its frame in the @N{C stack} was destroyed by the yield.
Instead, Lua calls a @def{continuation function}, Instead, Lua calls a @def{continuation function},
which was given as an argument to the callee function. which was given as an argument to the callee function.
As the name implies, As the name implies,
@@ -2841,7 +2846,7 @@ and therefore may raise any errors.
Converts the @x{acceptable index} @id{idx} Converts the @x{acceptable index} @id{idx}
into an equivalent @x{absolute index} into an equivalent @x{absolute index}
(that is, one that does not depend on the stack top). (that is, one that does not depend on the stack size).
} }
@@ -3740,10 +3745,13 @@ except that it allows the called function to yield @see{continuations}.
} }
@APIEntry{void lua_pop (lua_State *L, int n);| @APIEntry{void lua_pop (lua_State *L, int n);|
@apii{n,0,-} @apii{n,0,e}
Pops @id{n} elements from the stack. Pops @id{n} elements from the stack.
This function can run arbitrary code when removing an index
marked as to-be-closed from the stack.
} }
@APIEntry{void lua_pushboolean (lua_State *L, int b);| @APIEntry{void lua_pushboolean (lua_State *L, int b);|
@@ -4223,7 +4231,7 @@ for the @Q{newindex} event @see{metatable}.
} }
@APIEntry{void lua_settop (lua_State *L, int index);| @APIEntry{void lua_settop (lua_State *L, int index);|
@apii{?,?,-} @apii{?,?,e}
Accepts any index, @N{or 0}, Accepts any index, @N{or 0},
and sets the stack top to this index. and sets the stack top to this index.
@@ -4231,6 +4239,9 @@ If the new top is greater than the old one,
then the new elements are filled with @nil. then the new elements are filled with @nil.
If @id{index} @N{is 0}, then all stack elements are removed. If @id{index} @N{is 0}, then all stack elements are removed.
This function can run arbitrary code when removing an index
marked as to-be-closed from the stack.
} }
@APIEntry{void lua_setwarnf (lua_State *L, lua_WarnFunction f, void *ud);| @APIEntry{void lua_setwarnf (lua_State *L, lua_WarnFunction f, void *ud);|
@@ -4340,7 +4351,7 @@ as if it was already marked.
Note that, both in case of errors and of a regular return, Note that, both in case of errors and of a regular return,
by the time the @idx{__close} metamethod runs, by the time the @idx{__close} metamethod runs,
the @N{C stack} was already unwound, the @N{C stack} was already unwound,
so that any automatic C variable declared in the calling function so that any automatic @N{C variable} declared in the calling function
will be out of scope. will be out of scope.
} }
@@ -4955,20 +4966,6 @@ calling @Lid{lua_yield} with @id{nresults} equal to zero
} }
@APIEntry{int (lua_setcstacklimit) (lua_State *L, unsigned int limit);|
@apii{0,0,-}
Sets a new limit for the C stack.
This limit controls how deeply nested calls can go in Lua,
with the intent of avoiding a stack overflow.
Returns the old limit in case of success,
or zero in case of error.
For more details about this function,
see @Lid{debug.setcstacklimit},
its equivalent in the standard library.
}
@APIEntry{void lua_sethook (lua_State *L, lua_Hook f, int mask, int count);| @APIEntry{void lua_sethook (lua_State *L, lua_Hook f, int mask, int count);|
@apii{0,0,-} @apii{0,0,-}
@@ -8756,34 +8753,6 @@ to the userdata @id{u} plus a boolean,
} }
@LibEntry{debug.setcstacklimit (limit)|
Sets a new limit for the C stack.
This limit controls how deeply nested calls can go in Lua,
with the intent of avoiding a stack overflow.
A limit too small restricts recursive calls pointlessly;
a limit too large exposes the interpreter to stack-overflow crashes.
Unfortunately, there is no way to know a priori
the maximum safe limit for a platform.
Each call made from Lua code counts one unit.
Other operations (e.g., calls made from C to Lua or resuming a coroutine)
may have a higher cost.
This function has the following restrictions:
@description{
@item{It can only be called from the main coroutine (thread);}
@item{It cannot be called while handling a stack-overflow error;}
@item{@id{limit} must be less than 40000;}
@item{@id{limit} cannot be less than the amount of C stack in use.}
}
If a call does not respect some restriction,
it returns a false value.
Otherwise,
the call returns the old limit.
}
@LibEntry{debug.sethook ([thread,] hook, mask [, count])| @LibEntry{debug.sethook ([thread,] hook, mask [, count])|
Sets the given function as the debug hook. Sets the given function as the debug hook.

View File

@@ -127,8 +127,8 @@ else
end end
Cstacklevel = function () Cstacklevel = function ()
local _, _, ncalls, nci = T.stacklevel() local _, _, ncalls = T.stacklevel()
return ncalls + nci -- number of free slots in the C stack return ncalls -- number of C calls
end end
end end

View File

@@ -242,7 +242,7 @@ end
assert(debug.upvalueid(foo1, 1)) assert(debug.upvalueid(foo1, 1))
assert(debug.upvalueid(foo1, 2)) assert(debug.upvalueid(foo1, 2))
assert(not pcall(debug.upvalueid, foo1, 3)) assert(not debug.upvalueid(foo1, 3))
assert(debug.upvalueid(foo1, 1) == debug.upvalueid(foo2, 2)) assert(debug.upvalueid(foo1, 1) == debug.upvalueid(foo2, 2))
assert(debug.upvalueid(foo1, 2) == debug.upvalueid(foo2, 1)) assert(debug.upvalueid(foo1, 2) == debug.upvalueid(foo2, 1))
assert(debug.upvalueid(foo3, 1)) assert(debug.upvalueid(foo3, 1))

View File

@@ -392,28 +392,6 @@ check(function (a, b)
end, end,
'TEST', 'JMP', 'TEST', 'JMP', 'ADDI', 'MMBINI', 'JMP', 'RETURN0') 'TEST', 'JMP', 'TEST', 'JMP', 'ADDI', 'MMBINI', 'JMP', 'RETURN0')
checkequal(
function (a) while a < 10 do a = a + 1 end end,
function (a)
::loop::
if not (a < 10) then goto exit end
a = a + 1
goto loop
::exit::
end
)
checkequal(
function (a) repeat local x = a + 1; a = x until a > 0 end,
function (a)
::loop:: do
local x = a + 1
a = x
end
if not (a > 0) then goto loop end
end
)
checkequal(function () return 6 or true or nil end, checkequal(function () return 6 or true or nil end,
function () return k6 or kTrue or kNil end) function () return k6 or kTrue or kNil end)

View File

@@ -124,6 +124,11 @@ x, a = nil
-- coroutine closing -- coroutine closing
local function func2close (f)
return setmetatable({}, {__close = f})
end
do do
-- ok to close a dead coroutine -- ok to close a dead coroutine
local co = coroutine.create(print) local co = coroutine.create(print)
@@ -146,10 +151,6 @@ do
-- to-be-closed variables in coroutines -- to-be-closed variables in coroutines
local X local X
local function func2close (f)
return setmetatable({}, {__close = f})
end
co = coroutine.create(function () co = coroutine.create(function ()
local x <close> = func2close(function (self, err) local x <close> = func2close(function (self, err)
assert(err == nil); X = false assert(err == nil); X = false
@@ -192,6 +193,23 @@ do
end end
do
-- <close> versus pcall in coroutines
local X = false
local Y = false
function foo ()
local x <close> = func2close(function (self, err)
Y = debug.getinfo(2)
X = err
end)
error(43)
end
co = coroutine.create(function () return pcall(foo) end)
local st1, st2, err = coroutine.resume(co)
assert(st1 and not st2 and err == 43)
assert(X == 43 and Y.name == "pcall")
end
-- yielding across C boundaries -- yielding across C boundaries

View File

@@ -1,73 +1,29 @@
-- $Id: testes/cstack.lua $ -- $Id: testes/cstack.lua $
-- See Copyright Notice in file all.lua -- See Copyright Notice in file all.lua
local debug = require "debug"
print"testing C-stack overflow detection" print"testing stack overflow detection"
print"If this test crashes, see its file ('cstack.lua')"
-- Segmentation faults in these tests probably result from a C-stack -- Segmentation faults in these tests probably result from a C-stack
-- overflow. To avoid these errors, you can use the function -- overflow. To avoid these errors, you should set a smaller limit for
-- 'debug.setcstacklimit' to set a smaller limit for the use of -- the use of C stack by Lua, by changing the constant 'LUAI_MAXCCALLS'.
-- C stack by Lua. After finding a reliable limit, you might want
-- to recompile Lua with this limit as the value for
-- the constant 'LUAI_MAXCCALLS', which defines the default limit.
-- (The default limit is printed by this test.)
-- Alternatively, you can ensure a larger stack for the program. -- Alternatively, you can ensure a larger stack for the program.
-- For Linux, a limit up to 30_000 seems Ok. Windows cannot go much
-- higher than 2_000.
-- get and print original limit
local origlimit <const> = debug.setcstacklimit(400)
print("default stack limit: " .. origlimit)
-- Do the tests using the original limit. Or else you may want to change
-- 'currentlimit' to lower values to avoid a seg. fault or to higher
-- values to check whether they are reliable.
local currentlimit <const> = origlimit
debug.setcstacklimit(currentlimit)
print("current stack limit: " .. currentlimit)
local function checkerror (msg, f, ...) local function checkerror (msg, f, ...)
local s, err = pcall(f, ...) local s, err = pcall(f, ...)
assert(not s and string.find(err, msg)) assert(not s and string.find(err, msg))
end end
-- auxiliary function to keep 'count' on the screen even if the program
-- crashes.
local count
local back = string.rep("\b", 8)
local function progress ()
count = count + 1
local n = string.format("%-8d", count)
io.stderr:write(back, n) -- erase previous value and write new one
end
do print("testing simple recursion:")
count = 0
local function foo ()
progress()
foo() -- do recursive calls until a stack error (or crash)
end
checkerror("stack overflow", foo)
print("\tfinal count: ", count)
end
do print("testing stack overflow in message handling") do print("testing stack overflow in message handling")
count = 0 local count = 0
local function loop (x, y, z) local function loop (x, y, z)
progress() count = count + 1
return 1 + loop(x, y, z) return 1 + loop(x, y, z)
end end
local res, msg = xpcall(loop, loop) local res, msg = xpcall(loop, loop)
assert(msg == "error in error handling") assert(msg == "error in error handling")
print("\tfinal count: ", count) print("final count: ", count)
end end
@@ -80,97 +36,114 @@ do print("testing recursion inside pattern matching")
end end
local m = f(80) local m = f(80)
assert(#m == 80) assert(#m == 80)
checkerror("too complex", f, 200000) checkerror("too complex", f, 2000)
end end
do print("testing stack-overflow in recursive 'gsub'") do print("testing stack-overflow in recursive 'gsub'")
count = 0 local count = 0
local function foo () local function foo ()
progress() count = count + 1
string.gsub("a", ".", foo) string.gsub("a", ".", foo)
end end
checkerror("stack overflow", foo) checkerror("stack overflow", foo)
print("\tfinal count: ", count) print("final count: ", count)
print("testing stack-overflow in recursive 'gsub' with metatables") print("testing stack-overflow in recursive 'gsub' with metatables")
count = 0 local count = 0
local t = setmetatable({}, {__index = foo}) local t = setmetatable({}, {__index = foo})
foo = function () foo = function ()
count = count + 1 count = count + 1
progress(count)
string.gsub("a", ".", t) string.gsub("a", ".", t)
end end
checkerror("stack overflow", foo) checkerror("stack overflow", foo)
print("\tfinal count: ", count) print("final count: ", count)
end end
do -- bug in 5.4.0 do -- bug in 5.4.0
print("testing limits in coroutines inside deep calls") print("testing limits in coroutines inside deep calls")
count = 0 local count = 0
local lim = 1000 local lim = 1000
local function stack (n) local function stack (n)
progress()
if n > 0 then return stack(n - 1) + 1 if n > 0 then return stack(n - 1) + 1
else coroutine.wrap(function () else coroutine.wrap(function ()
count = count + 1
stack(lim) stack(lim)
end)() end)()
end end
end end
print(xpcall(stack, function () return "ok" end, lim)) local st, msg = xpcall(stack, function () return "ok" end, lim)
assert(not st and msg == "ok")
print("final count: ", count)
end end
do print("testing changes in C-stack limit") do
print("nesting of resuming yielded coroutines")
local count = 0
-- Just an alternative limit, different from the current one local function body ()
-- (smaller to avoid stack overflows) coroutine.yield()
local alterlimit <const> = currentlimit * 8 // 10 local f = coroutine.wrap(body)
f(); -- start new coroutine (will stop in previous yield)
assert(not debug.setcstacklimit(0)) -- limit too small count = count + 1
assert(not debug.setcstacklimit(50000)) -- limit too large f() -- call it recursively
local co = coroutine.wrap (function ()
return debug.setcstacklimit(alterlimit)
end)
assert(not co()) -- cannot change C stack inside coroutine
local n
local function foo () n = n + 1; foo () end
local function check ()
n = 0
pcall(foo)
return n
end end
-- set limit to 'alterlimit' local f = coroutine.wrap(body)
assert(debug.setcstacklimit(alterlimit) == currentlimit) f()
local limalter <const> = check() assert(not pcall(f))
-- set a very low limit (given that there are already several active print("final count: ", count)
-- calls to arrive here)
local lowlimit <const> = 38
assert(debug.setcstacklimit(lowlimit) == alterlimit)
-- usable limit is much lower, due to active calls
local actuallow = check()
assert(actuallow < lowlimit - 30)
-- now, add 'lowlimit' extra slots, which should all be available
assert(debug.setcstacklimit(lowlimit + lowlimit) == lowlimit)
local lim2 <const> = check()
assert(lim2 == actuallow + lowlimit)
-- 'setcstacklimit' works inside protected calls. (The new stack
-- limit is kept when 'pcall' returns.)
assert(pcall(function ()
assert(debug.setcstacklimit(alterlimit) == lowlimit * 2)
assert(check() <= limalter)
end))
assert(check() == limalter)
-- restore original limit
assert(debug.setcstacklimit(origlimit) == alterlimit)
end end
if T then
print("testing stack recovery")
local N = 0 -- trace number of calls
local LIM = -1 -- will store N just before stack overflow
-- trace stack size; after stack overflow, it should be
-- the maximum allowed stack size.
local stack1
local dummy
local function err(msg)
assert(string.find(msg, "stack overflow"))
local _, stacknow = T.stacklevel()
assert(stacknow == stack1 + 200)
end
-- When LIM==-1, the 'if' is not executed, so this function only
-- counts and stores the stack limits up to overflow. Then, LIM
-- becomes N, and then the 'if' code is run when the stack is
-- full. Then, there is a stack overflow inside 'xpcall', after which
-- the stack must have been restored back to its maximum normal size.
local function f()
dummy, stack1 = T.stacklevel()
if N == LIM then
xpcall(f, err)
local _, stacknow = T.stacklevel()
assert(stacknow == stack1)
return
end
N = N + 1
f()
end
local topB, sizeB -- top and size Before overflow
local topA, sizeA -- top and size After overflow
topB, sizeB = T.stacklevel()
xpcall(f, err)
topA, sizeA = T.stacklevel()
-- sizes should be comparable
assert(topA == topB and sizeA < sizeB * 2)
print(string.format("maximum stack size: %d", stack1))
LIM = N -- will stop recursion at maximum level
N = 0 -- to count again
f()
print"+"
end
print'OK' print'OK'

View File

@@ -119,6 +119,16 @@ else
end end
]], {2,3,4,7}) ]], {2,3,4,7})
test([[
local function foo()
end
foo()
A = 1
A = 2
A = 3
]], {2, 3, 2, 4, 5, 6})
test([[-- test([[--
if nil then if nil then
a=1 a=1

View File

@@ -386,25 +386,33 @@ if not _soft then
collectgarbage() collectgarbage()
print"testing stack overflow" print"testing stack overflow"
C = 0 C = 0
local l = debug.getinfo(1, "l").currentline; function y () C=C+1; y() end -- get line where stack overflow will happen
local l = debug.getinfo(1, "l").currentline + 1
local function auxy () C=C+1; auxy() end -- produce a stack overflow
function y ()
collectgarbage("stop") -- avoid running finalizers without stack space
auxy()
collectgarbage("restart")
end
local function checkstackmessage (m) local function checkstackmessage (m)
print("(expected stack overflow after " .. C .. " calls)")
C = 0 -- prepare next count
return (string.find(m, "stack overflow")) return (string.find(m, "stack overflow"))
end end
-- repeated stack overflows (to check stack recovery) -- repeated stack overflows (to check stack recovery)
assert(checkstackmessage(doit('y()'))) assert(checkstackmessage(doit('y()')))
print('+')
assert(checkstackmessage(doit('y()'))) assert(checkstackmessage(doit('y()')))
print('+')
assert(checkstackmessage(doit('y()'))) assert(checkstackmessage(doit('y()')))
print('+')
-- error lines in stack overflow -- error lines in stack overflow
C = 0
local l1 local l1
local function g(x) local function g(x)
l1 = debug.getinfo(x, "l").currentline; y() l1 = debug.getinfo(x, "l").currentline + 2
collectgarbage("stop") -- avoid running finalizers without stack space
auxy()
collectgarbage("restart")
end end
local _, stackmsg = xpcall(g, debug.traceback, 1) local _, stackmsg = xpcall(g, debug.traceback, 1)
print('+') print('+')
@@ -530,10 +538,10 @@ local function testrep (init, rep, close, repc, finalresult)
if (finalresult) then if (finalresult) then
assert(res() == finalresult) assert(res() == finalresult)
end end
s = init .. string.rep(rep, 10000) s = init .. string.rep(rep, 500)
local res, msg = load(s) -- 10000 levels not ok local res, msg = load(s) -- 500 levels not ok
assert(not res and (string.find(msg, "too many registers") or assert(not res and (string.find(msg, "too many") or
string.find(msg, "stack overflow"))) string.find(msg, "overflow")))
end end
testrep("local a; a", ",a", "= 1", ",1") -- multiple assignment testrep("local a; a", ",a", "= 1", ",1") -- multiple assignment

View File

@@ -246,6 +246,11 @@ do
X = false X = false
foo = function (x) foo = function (x)
local _<close> = func2close(function ()
-- without errors, enclosing function should be still active when
-- __close is called
assert(debug.getinfo(2).name == "foo")
end)
local _<close> = closescope local _<close> = closescope
local y = 15 local y = 15
return y return y
@@ -343,6 +348,18 @@ local function endwarn ()
end end
-- errors inside __close can generate a warning instead of an
-- error. This new 'assert' force them to appear.
local function assert(cond, msg)
if not cond then
local line = debug.getinfo(2).currentline or "?"
msg = string.format("assertion failed! line %d (%s)\n", line, msg or "")
io.stderr:write(msg)
os.exit(1)
end
end
local function checkwarn (msg) local function checkwarn (msg)
if T then if T then
assert(string.find(_WARN, msg)) assert(string.find(_WARN, msg))
@@ -406,11 +423,15 @@ do print("testing errors in __close")
local x <close> = local x <close> =
func2close(function (self, msg) func2close(function (self, msg)
-- after error, 'foo' was discarded, so caller now
-- must be 'pcall'
assert(debug.getinfo(2).name == "pcall")
assert(msg == 4) assert(msg == 4)
end) end)
local x1 <close> = local x1 <close> =
func2close(function (self, msg) func2close(function (self, msg)
assert(debug.getinfo(2).name == "pcall")
checkwarn("@y") checkwarn("@y")
assert(msg == 4) assert(msg == 4)
error("@x1") error("@x1")
@@ -420,6 +441,7 @@ do print("testing errors in __close")
local y <close> = local y <close> =
func2close(function (self, msg) func2close(function (self, msg)
assert(debug.getinfo(2).name == "pcall")
assert(msg == 4) -- error in body assert(msg == 4) -- error in body
checkwarn("@z") checkwarn("@z")
error("@y") error("@y")
@@ -428,6 +450,7 @@ do print("testing errors in __close")
local first = true local first = true
local z <close> = local z <close> =
func2close(function (self, msg) func2close(function (self, msg)
assert(debug.getinfo(2).name == "pcall")
-- 'z' close is called once -- 'z' close is called once
assert(first and msg == 4) assert(first and msg == 4)
first = false first = false

View File

@@ -287,6 +287,33 @@ RUN([[lua "-e_PROMPT='%s'" -i < %s > %s]], prompt, prog, out)
local t = getoutput() local t = getoutput()
assert(string.find(t, prompt .. ".*" .. prompt .. ".*" .. prompt)) assert(string.find(t, prompt .. ".*" .. prompt .. ".*" .. prompt))
-- using the prompt default
prepfile[[ --
a = 2
]]
RUN([[lua -i < %s > %s]], prog, out)
local t = getoutput()
prompt = "> " -- the default
assert(string.find(t, prompt .. ".*" .. prompt .. ".*" .. prompt))
-- non-string prompt
prompt =
"local C = 0;\z
_PROMPT=setmetatable({},{__tostring = function () \z
C = C + 1; return C end})"
prepfile[[ --
a = 2
]]
RUN([[lua -e "%s" -i < %s > %s]], prompt, prog, out)
local t = getoutput()
assert(string.find(t, [[
1 --
2a = 2
3
]], 1, true))
-- test for error objects -- test for error objects
prepfile[[ prepfile[[
debug = require "debug" debug = require "debug"

View File

@@ -359,6 +359,38 @@ end
assert(n == 5) assert(n == 5)
do
print("testing next x GC of deleted keys")
-- bug in 5.4.1
local co = coroutine.wrap(function (t)
for k, v in pairs(t) do
local k1 = next(t) -- all previous keys were deleted
assert(k == k1) -- current key is the first in the table
t[k] = nil
local expected = (type(k) == "table" and k[1] or
type(k) == "function" and k() or
string.sub(k, 1, 1))
assert(expected == v)
coroutine.yield(v)
end
end)
local t = {}
t[{1}] = 1 -- add several unanchored, collectable keys
t[{2}] = 2
t[string.rep("a", 50)] = "a" -- long string
t[string.rep("b", 50)] = "b"
t[{3}] = 3
t[string.rep("c", 10)] = "c" -- short string
t[function () return 10 end] = 10
local count = 7
while co(t) do
collectgarbage("collect") -- collect dead keys
count = count - 1
end
assert(count == 0 and next(t) == nil) -- traversed the whole table
end
local function test (a) local function test (a)
assert(not pcall(table.insert, a, 2, 20)); assert(not pcall(table.insert, a, 2, 20));
table.insert(a, 10); table.insert(a, 2, 20); table.insert(a, 10); table.insert(a, 2, 20);