mirror of
https://github.com/lua/lua
synced 2026-06-08 15:34:49 +00:00
Compare commits
76 Commits
v5.4-alpha
...
v5.4-beta
| Author | SHA1 | Date | |
|---|---|---|---|
| 6c0e44464b | |||
| 6a84c32900 | |||
| 6a10f03ff8 | |||
| 7bd1e53753 | |||
| b98d41db99 | |||
| b2a580bdb1 | |||
| 89f6a85f03 | |||
| 6ef366644f | |||
| 03cde80b58 | |||
| 6b2e202df5 | |||
| 40d8832ee0 | |||
| 91dad09f65 | |||
| 4518e5df24 | |||
| 72a094bda7 | |||
| 46b84580d6 | |||
| df13f25948 | |||
| 643188d6e5 | |||
| 3df5624ff4 | |||
| be78aeae4c | |||
| 5bc47fe830 | |||
| 9405472565 | |||
| 45948e7e55 | |||
| b96b0b5abb | |||
| ca13be9af7 | |||
| a1d8eb2743 | |||
| f64a1b175a | |||
| 09b4e527a0 | |||
| 35a28a58b3 | |||
| 223bb04090 | |||
| fe040633a1 | |||
| f645d31573 | |||
| 35b4efc270 | |||
| 0d52913804 | |||
| b80077b8f3 | |||
| e70f275f32 | |||
| 9a37dc0ce6 | |||
| 0eb6aa4013 | |||
| 7f5c31cdca | |||
| 9e6807c3c9 | |||
| 2f22c6bb79 | |||
| 3c0d3c6fbe | |||
| 440a5ee78c | |||
| dc07719b0d | |||
| 9cdf6b7082 | |||
| 3c1d415bd3 | |||
| d36a31e673 | |||
| 024a6071ca | |||
| 4eefef07ab | |||
| 9c28ed05c9 | |||
| 8082906c05 | |||
| d6af81084d | |||
| 4846f7e3bb | |||
| c220b0a5d0 | |||
| 298f383ffc | |||
| 758c1ef445 | |||
| dd6d8db49a | |||
| 1fb4d53925 | |||
| f6aab3ec1f | |||
| be8445d7e4 | |||
| 3d296304ef | |||
| 54f7b46c1e | |||
| e888976bc6 | |||
| 4d46289331 | |||
| 8eca21c2e8 | |||
| 924bed7297 | |||
| 8b7cfee26b | |||
| c1a63c45f8 | |||
| 4487c28ced | |||
| 05ba288049 | |||
| 6b9490bd72 | |||
| e4b02ca8e4 | |||
| 20a9853e02 | |||
| 1d70708a78 | |||
| be73f72fcc | |||
| 3cd9b56ae6 | |||
| d2a9b4ffb8 |
@@ -2,7 +2,7 @@ make -s -j
|
||||
cd testes/libs; make -s
|
||||
cd .. # back to directory 'testes'
|
||||
ulimit -S -s 2000
|
||||
if { ../lua all.lua; } then
|
||||
if { ../lua -W all.lua; } then
|
||||
echo -e "\n\n final OK!!!!\n\n"
|
||||
else
|
||||
echo -e "\n\n >>>> BUG!!!!\n\n"
|
||||
|
||||
@@ -170,20 +170,23 @@ LUA_API int lua_gettop (lua_State *L) {
|
||||
|
||||
|
||||
LUA_API void lua_settop (lua_State *L, int idx) {
|
||||
StkId func = L->ci->func;
|
||||
CallInfo *ci = L->ci;
|
||||
StkId func = ci->func;
|
||||
ptrdiff_t diff; /* difference for new top */
|
||||
lua_lock(L);
|
||||
if (idx >= 0) {
|
||||
StkId newtop = (func + 1) + idx;
|
||||
api_check(L, idx <= L->stack_last - (func + 1), "new top too large");
|
||||
while (L->top < newtop)
|
||||
setnilvalue(s2v(L->top++));
|
||||
L->top = newtop;
|
||||
api_check(L, idx <= ci->top - (func + 1), "new top too large");
|
||||
diff = ((func + 1) + idx) - L->top;
|
||||
for (; diff > 0; diff--)
|
||||
setnilvalue(s2v(L->top++)); /* clear new slots */
|
||||
}
|
||||
else {
|
||||
api_check(L, -(idx+1) <= (L->top - (func + 1)), "invalid new top");
|
||||
L->top += idx+1; /* 'subtract' index (index is negative) */
|
||||
diff = idx + 1; /* will "subtract" index (as it is negative) */
|
||||
}
|
||||
luaF_close(L, L->top, LUA_OK);
|
||||
if (diff < 0 && hastocloseCfunc(ci->nresults))
|
||||
luaF_close(L, L->top + diff, LUA_OK);
|
||||
L->top += diff; /* correct top only after closing any upvalue */
|
||||
lua_unlock(L);
|
||||
}
|
||||
|
||||
|
||||
@@ -11,12 +11,22 @@
|
||||
#include "llimits.h"
|
||||
#include "lstate.h"
|
||||
|
||||
|
||||
/* Increments 'L->top', checking for stack overflows */
|
||||
#define api_incr_top(L) {L->top++; api_check(L, L->top <= L->ci->top, \
|
||||
"stack overflow");}
|
||||
|
||||
|
||||
/*
|
||||
** If a call returns too many multiple returns, the callee may not have
|
||||
** stack space to accomodate all results. In this case, this macro
|
||||
** increases its stack space ('L->ci->top').
|
||||
*/
|
||||
#define adjustresults(L,nres) \
|
||||
{ if ((nres) <= LUA_MULTRET && L->ci->top < L->top) L->ci->top = L->top; }
|
||||
|
||||
|
||||
/* Ensure the stack has at least 'n' elements */
|
||||
#define api_checknelems(L,n) api_check(L, (n) < (L->top - L->ci->func), \
|
||||
"not enough elements in the stack")
|
||||
|
||||
|
||||
@@ -62,7 +62,7 @@ static int findfield (lua_State *L, int objidx, int level) {
|
||||
else if (findfield(L, objidx, level - 1)) { /* try recursively */
|
||||
/* stack: lib_name, lib_table, field_name (top) */
|
||||
lua_pushliteral(L, "."); /* place '.' between the two names */
|
||||
lua_replace(L, -3); /* (in the slot ocupied by table) */
|
||||
lua_replace(L, -3); /* (in the slot occupied by table) */
|
||||
lua_concat(L, 3); /* lib_name.field_name */
|
||||
return 1;
|
||||
}
|
||||
@@ -249,7 +249,7 @@ LUALIB_API int luaL_fileresult (lua_State *L, int stat, const char *fname) {
|
||||
return 1;
|
||||
}
|
||||
else {
|
||||
lua_pushnil(L);
|
||||
luaL_pushfail(L);
|
||||
if (fname)
|
||||
lua_pushfstring(L, "%s: %s", fname, strerror(en));
|
||||
else
|
||||
@@ -291,10 +291,10 @@ LUALIB_API int luaL_execresult (lua_State *L, int stat) {
|
||||
if (*what == 'e' && stat == 0) /* successful termination? */
|
||||
lua_pushboolean(L, 1);
|
||||
else
|
||||
lua_pushnil(L);
|
||||
luaL_pushfail(L);
|
||||
lua_pushstring(L, what);
|
||||
lua_pushinteger(L, stat);
|
||||
return 3; /* return true/nil,what,code */
|
||||
return 3; /* return true/fail,what,code */
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1002,29 +1002,43 @@ static int panic (lua_State *L) {
|
||||
|
||||
|
||||
/*
|
||||
** Emit a warning. '*previoustocont' signals whether previous message
|
||||
** was to be continued by the current one.
|
||||
** Emit a warning. '*warnstate' means:
|
||||
** 0 - warning system is off;
|
||||
** 1 - ready to start a new message;
|
||||
** 2 - previous message is to be continued.
|
||||
*/
|
||||
static void warnf (void *ud, const char *message, int tocont) {
|
||||
int *previoustocont = (int *)ud;
|
||||
if (!*previoustocont) /* previous message was the last? */
|
||||
int *warnstate = (int *)ud;
|
||||
if (*warnstate != 2 && !tocont && *message == '@') { /* control message? */
|
||||
if (strcmp(message, "@off") == 0)
|
||||
*warnstate = 0;
|
||||
else if (strcmp(message, "@on") == 0)
|
||||
*warnstate = 1;
|
||||
return;
|
||||
}
|
||||
else if (*warnstate == 0) /* warnings off? */
|
||||
return;
|
||||
if (*warnstate == 1) /* previous message was the last? */
|
||||
lua_writestringerror("%s", "Lua warning: "); /* start a new warning */
|
||||
lua_writestringerror("%s", message); /* write message */
|
||||
if (!tocont) /* is this the last part? */
|
||||
if (tocont) /* not the last part? */
|
||||
*warnstate = 2; /* to be continued */
|
||||
else { /* last part */
|
||||
lua_writestringerror("%s", "\n"); /* finish message with end-of-line */
|
||||
*previoustocont = tocont;
|
||||
*warnstate = 1; /* ready to start a new message */
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
LUALIB_API lua_State *luaL_newstate (void) {
|
||||
lua_State *L = lua_newstate(l_alloc, NULL);
|
||||
if (L) {
|
||||
int *previoustocont; /* space for warning state */
|
||||
int *warnstate; /* space for warning state */
|
||||
lua_atpanic(L, &panic);
|
||||
previoustocont = (int *)lua_newuserdatauv(L, sizeof(int), 0);
|
||||
warnstate = (int *)lua_newuserdatauv(L, sizeof(int), 0);
|
||||
luaL_ref(L, LUA_REGISTRYINDEX); /* make sure it won't be collected */
|
||||
*previoustocont = 0; /* next message starts a new warning */
|
||||
lua_setwarnf(L, warnf, previoustocont);
|
||||
*warnstate = 0; /* default is warnings off */
|
||||
lua_setwarnf(L, warnf, warnstate);
|
||||
}
|
||||
return L;
|
||||
}
|
||||
|
||||
@@ -153,6 +153,10 @@ LUALIB_API void (luaL_requiref) (lua_State *L, const char *modname,
|
||||
#define luaL_loadbuffer(L,s,sz,n) luaL_loadbufferx(L,s,sz,n,NULL)
|
||||
|
||||
|
||||
/* push the value used to represent failure/error */
|
||||
#define luaL_pushfail(L) lua_pushnil(L)
|
||||
|
||||
|
||||
/*
|
||||
** {======================================================
|
||||
** Generic Buffer manipulation
|
||||
|
||||
+5
-5
@@ -48,9 +48,9 @@ static int luaB_warn (lua_State *L) {
|
||||
luaL_checkstring(L, 1); /* at least one argument */
|
||||
for (i = 2; i <= n; i++)
|
||||
luaL_checkstring(L, i); /* make sure all arguments are strings */
|
||||
for (i = 1; i <= n; i++) /* compose warning */
|
||||
for (i = 1; i < n; i++) /* compose warning */
|
||||
lua_warning(L, lua_tostring(L, i), 1);
|
||||
lua_warning(L, "", 0); /* close warning */
|
||||
lua_warning(L, lua_tostring(L, n), 0); /* close warning */
|
||||
return 0;
|
||||
}
|
||||
|
||||
@@ -106,7 +106,7 @@ static int luaB_tonumber (lua_State *L) {
|
||||
return 1;
|
||||
} /* else not a number */
|
||||
} /* else not a number */
|
||||
lua_pushnil(L); /* not a number */
|
||||
luaL_pushfail(L); /* not a number */
|
||||
return 1;
|
||||
}
|
||||
|
||||
@@ -308,9 +308,9 @@ static int load_aux (lua_State *L, int status, int envidx) {
|
||||
return 1;
|
||||
}
|
||||
else { /* error (message is on top of the stack) */
|
||||
lua_pushnil(L);
|
||||
luaL_pushfail(L);
|
||||
lua_insert(L, -2); /* put before error message */
|
||||
return 2; /* return nil plus error message */
|
||||
return 2; /* return fail plus error message */
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -52,7 +52,7 @@ l_noret luaK_semerror (LexState *ls, const char *msg) {
|
||||
** If expression is a numeric constant, fills 'v' with its value
|
||||
** and returns 1. Otherwise, returns 0.
|
||||
*/
|
||||
static int tonumeral(const expdesc *e, TValue *v) {
|
||||
static int tonumeral (const expdesc *e, TValue *v) {
|
||||
if (hasjumps(e))
|
||||
return 0; /* not a numeral */
|
||||
switch (e->k) {
|
||||
@@ -67,6 +67,42 @@ static int tonumeral(const expdesc *e, TValue *v) {
|
||||
}
|
||||
|
||||
|
||||
/*
|
||||
** Get the constant value from a constant expression
|
||||
*/
|
||||
static TValue *const2val (FuncState *fs, const expdesc *e) {
|
||||
lua_assert(e->k == VCONST);
|
||||
return &fs->ls->dyd->actvar.arr[e->u.info].k;
|
||||
}
|
||||
|
||||
|
||||
/*
|
||||
** If expression is a constant, fills 'v' with its value
|
||||
** and returns 1. Otherwise, returns 0.
|
||||
*/
|
||||
int luaK_exp2const (FuncState *fs, const expdesc *e, TValue *v) {
|
||||
if (hasjumps(e))
|
||||
return 0; /* not a constant */
|
||||
switch (e->k) {
|
||||
case VFALSE: case VTRUE:
|
||||
setbvalue(v, e->k == VTRUE);
|
||||
return 1;
|
||||
case VNIL:
|
||||
setnilvalue(v);
|
||||
return 1;
|
||||
case VKSTR: {
|
||||
setsvalue(fs->ls->L, v, e->u.strval);
|
||||
return 1;
|
||||
}
|
||||
case VCONST: {
|
||||
setobj(fs->ls->L, v, const2val(fs, e));
|
||||
return 1;
|
||||
}
|
||||
default: return tonumeral(e, v);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
/*
|
||||
** Return the previous instruction of the current code. If there
|
||||
** may be a jump target between the current instruction and the
|
||||
@@ -323,12 +359,12 @@ static void removelastlineinfo (FuncState *fs) {
|
||||
Proto *f = fs->f;
|
||||
int pc = fs->pc - 1; /* last instruction coded */
|
||||
if (f->lineinfo[pc] != ABSLINEINFO) { /* relative line info? */
|
||||
fs->previousline -= f->lineinfo[pc]; /* last line saved */
|
||||
fs->iwthabs--;
|
||||
fs->previousline -= f->lineinfo[pc]; /* correct last line saved */
|
||||
fs->iwthabs--; /* undo previous increment */
|
||||
}
|
||||
else { /* absolute line information */
|
||||
lua_assert(f->abslineinfo[fs->nabslineinfo - 1].pc == pc);
|
||||
fs->nabslineinfo--; /* remove it */
|
||||
lua_assert(f->abslineinfo[fs->nabslineinfo].pc = pc);
|
||||
fs->iwthabs = MAXIWTHABS + 1; /* force next line info to be absolute */
|
||||
}
|
||||
}
|
||||
@@ -348,7 +384,7 @@ static void removelastinstruction (FuncState *fs) {
|
||||
** Emit instruction 'i', checking for array sizes and saving also its
|
||||
** line information. Return 'i' position.
|
||||
*/
|
||||
static int luaK_code (FuncState *fs, Instruction i) {
|
||||
int luaK_code (FuncState *fs, Instruction i) {
|
||||
Proto *f = fs->f;
|
||||
/* put new instruction in code array */
|
||||
luaM_growvector(fs->ls->L, f->code, fs->pc, f->sizecode, Instruction,
|
||||
@@ -458,7 +494,7 @@ void luaK_reserveregs (FuncState *fs, int n) {
|
||||
)
|
||||
*/
|
||||
static void freereg (FuncState *fs, int reg) {
|
||||
if (reg >= fs->nactvar) {
|
||||
if (reg >= luaY_nvarstack(fs)) {
|
||||
fs->freereg--;
|
||||
lua_assert(reg == fs->freereg);
|
||||
}
|
||||
@@ -537,7 +573,7 @@ static int addk (FuncState *fs, TValue *key, TValue *v) {
|
||||
/*
|
||||
** Add a string to list of constants and return its index.
|
||||
*/
|
||||
int luaK_stringK (FuncState *fs, TString *s) {
|
||||
static int stringK (FuncState *fs, TString *s) {
|
||||
TValue o;
|
||||
setsvalue(fs->ls->L, &o, s);
|
||||
return addk(fs, &o, &o); /* use string itself as key */
|
||||
@@ -590,12 +626,12 @@ static int nilK (FuncState *fs) {
|
||||
|
||||
|
||||
/*
|
||||
** Check whether 'i' can be stored in an 'sC' operand.
|
||||
** Equivalent to (0 <= i + OFFSET_sC && i + OFFSET_sC <= MAXARG_C)
|
||||
** but without risk of overflows in the addition.
|
||||
** Check whether 'i' can be stored in an 'sC' operand. Equivalent to
|
||||
** (0 <= int2sC(i) && int2sC(i) <= MAXARG_C) but without risk of
|
||||
** overflows in the hidden addition inside 'int2sC'.
|
||||
*/
|
||||
static int fitsC (lua_Integer i) {
|
||||
return (-OFFSET_sC <= i && i <= MAXARG_C - OFFSET_sC);
|
||||
return (l_castS2U(i) + OFFSET_sC <= cast_uint(MAXARG_C));
|
||||
}
|
||||
|
||||
|
||||
@@ -615,20 +651,40 @@ void luaK_int (FuncState *fs, int reg, lua_Integer i) {
|
||||
}
|
||||
|
||||
|
||||
static int floatI (lua_Number f, lua_Integer *fi) {
|
||||
return (luaV_flttointeger(f, fi, 0) && fitsBx(*fi));
|
||||
}
|
||||
|
||||
|
||||
static void luaK_float (FuncState *fs, int reg, lua_Number f) {
|
||||
lua_Integer fi;
|
||||
if (floatI(f, &fi))
|
||||
if (luaV_flttointeger(f, &fi, 0) && fitsBx(fi))
|
||||
luaK_codeAsBx(fs, OP_LOADF, reg, cast_int(fi));
|
||||
else
|
||||
luaK_codek(fs, reg, luaK_numberK(fs, f));
|
||||
}
|
||||
|
||||
|
||||
/*
|
||||
** Convert a constant in 'v' into an expression description 'e'
|
||||
*/
|
||||
static void const2exp (TValue *v, expdesc *e) {
|
||||
switch (ttypetag(v)) {
|
||||
case LUA_TNUMINT:
|
||||
e->k = VKINT; e->u.ival = ivalue(v);
|
||||
break;
|
||||
case LUA_TNUMFLT:
|
||||
e->k = VKFLT; e->u.nval = fltvalue(v);
|
||||
break;
|
||||
case LUA_TBOOLEAN:
|
||||
e->k = bvalue(v) ? VTRUE : VFALSE;
|
||||
break;
|
||||
case LUA_TNIL:
|
||||
e->k = VNIL;
|
||||
break;
|
||||
case LUA_TSHRSTR: case LUA_TLNGSTR:
|
||||
e->k = VKSTR; e->u.strval = tsvalue(v);
|
||||
break;
|
||||
default: lua_assert(0);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
/*
|
||||
** Fix an expression to return the number of results 'nresults'.
|
||||
** Either 'e' is a multi-ret expression (function call or vararg)
|
||||
@@ -647,6 +703,16 @@ void luaK_setreturns (FuncState *fs, expdesc *e, int nresults) {
|
||||
}
|
||||
|
||||
|
||||
/*
|
||||
** Convert a VKSTR to a VK
|
||||
*/
|
||||
static void str2K (FuncState *fs, expdesc *e) {
|
||||
lua_assert(e->k == VKSTR);
|
||||
e->u.info = stringK(fs, e->u.strval);
|
||||
e->k = VK;
|
||||
}
|
||||
|
||||
|
||||
/*
|
||||
** Fix an expression to return one result.
|
||||
** If expression is not a multi-ret expression (function call or
|
||||
@@ -672,18 +738,22 @@ void luaK_setoneret (FuncState *fs, expdesc *e) {
|
||||
|
||||
|
||||
/*
|
||||
** Ensure that expression 'e' is not a variable.
|
||||
** Ensure that expression 'e' is not a variable (nor a constant).
|
||||
** (Expression still may have jump lists.)
|
||||
*/
|
||||
void luaK_dischargevars (FuncState *fs, expdesc *e) {
|
||||
switch (e->k) {
|
||||
case VCONST: {
|
||||
const2exp(const2val(fs, e), e);
|
||||
break;
|
||||
}
|
||||
case VLOCAL: { /* already in a register */
|
||||
e->u.info = e->u.var.idx;
|
||||
e->u.info = e->u.var.sidx;
|
||||
e->k = VNONRELOC; /* becomes a non-relocatable value */
|
||||
break;
|
||||
}
|
||||
case VUPVAL: { /* move value to some (pending) register */
|
||||
e->u.info = luaK_codeABC(fs, OP_GETUPVAL, 0, e->u.var.idx, 0);
|
||||
e->u.info = luaK_codeABC(fs, OP_GETUPVAL, 0, e->u.info, 0);
|
||||
e->k = VRELOC;
|
||||
break;
|
||||
}
|
||||
@@ -735,6 +805,9 @@ static void discharge2reg (FuncState *fs, expdesc *e, int reg) {
|
||||
luaK_codeABC(fs, OP_LOADBOOL, reg, e->k == VTRUE, 0);
|
||||
break;
|
||||
}
|
||||
case VKSTR: {
|
||||
str2K(fs, e);
|
||||
} /* FALLTHROUGH */
|
||||
case VK: {
|
||||
luaK_codek(fs, reg, e->u.info);
|
||||
break;
|
||||
@@ -850,7 +923,7 @@ int luaK_exp2anyreg (FuncState *fs, expdesc *e) {
|
||||
if (e->k == VNONRELOC) { /* expression already has a register? */
|
||||
if (!hasjumps(e)) /* no jumps? */
|
||||
return e->u.info; /* result is already in a register */
|
||||
if (e->u.info >= fs->nactvar) { /* reg. is not a local? */
|
||||
if (e->u.info >= luaY_nvarstack(fs)) { /* reg. is not a local? */
|
||||
exp2reg(fs, e, e->u.info); /* put final result in it */
|
||||
return e->u.info;
|
||||
}
|
||||
@@ -895,6 +968,7 @@ static int luaK_exp2K (FuncState *fs, expdesc *e) {
|
||||
case VNIL: info = nilK(fs); break;
|
||||
case VKINT: info = luaK_intK(fs, e->u.ival); break;
|
||||
case VKFLT: info = luaK_numberK(fs, e->u.nval); break;
|
||||
case VKSTR: info = stringK(fs, e->u.strval); break;
|
||||
case VK: info = e->u.info; break;
|
||||
default: return 0; /* not a constant */
|
||||
}
|
||||
@@ -939,12 +1013,12 @@ void luaK_storevar (FuncState *fs, expdesc *var, expdesc *ex) {
|
||||
switch (var->k) {
|
||||
case VLOCAL: {
|
||||
freeexp(fs, ex);
|
||||
exp2reg(fs, ex, var->u.var.idx); /* compute 'ex' into proper place */
|
||||
exp2reg(fs, ex, var->u.var.sidx); /* compute 'ex' into proper place */
|
||||
return;
|
||||
}
|
||||
case VUPVAL: {
|
||||
int e = luaK_exp2anyreg(fs, ex);
|
||||
luaK_codeABC(fs, OP_SETUPVAL, e, var->u.var.idx, 0);
|
||||
luaK_codeABC(fs, OP_SETUPVAL, e, var->u.info, 0);
|
||||
break;
|
||||
}
|
||||
case VINDEXUP: {
|
||||
@@ -1029,7 +1103,7 @@ void luaK_goiftrue (FuncState *fs, expdesc *e) {
|
||||
pc = e->u.info; /* save jump position */
|
||||
break;
|
||||
}
|
||||
case VK: case VKFLT: case VKINT: case VTRUE: {
|
||||
case VK: case VKFLT: case VKINT: case VKSTR: case VTRUE: {
|
||||
pc = NO_JUMP; /* always true; do nothing */
|
||||
break;
|
||||
}
|
||||
@@ -1074,13 +1148,12 @@ void luaK_goiffalse (FuncState *fs, expdesc *e) {
|
||||
** Code 'not e', doing constant folding.
|
||||
*/
|
||||
static void codenot (FuncState *fs, expdesc *e) {
|
||||
luaK_dischargevars(fs, e);
|
||||
switch (e->k) {
|
||||
case VNIL: case VFALSE: {
|
||||
e->k = VTRUE; /* true == not nil == not false */
|
||||
break;
|
||||
}
|
||||
case VK: case VKFLT: case VKINT: case VTRUE: {
|
||||
case VK: case VKFLT: case VKINT: case VKSTR: case VTRUE: {
|
||||
e->k = VFALSE; /* false == not "x" == not 0.5 == not 1 == not true */
|
||||
break;
|
||||
}
|
||||
@@ -1143,15 +1216,16 @@ static int isSCint (expdesc *e) {
|
||||
** Check whether expression 'e' is a literal integer or float in
|
||||
** proper range to fit in a register (sB or sC).
|
||||
*/
|
||||
static int isSCnumber (expdesc *e, lua_Integer *i, int *isfloat) {
|
||||
static int isSCnumber (expdesc *e, int *pi, int *isfloat) {
|
||||
lua_Integer i;
|
||||
if (e->k == VKINT)
|
||||
*i = e->u.ival;
|
||||
else if (!(e->k == VKFLT && floatI(e->u.nval, i)))
|
||||
return 0; /* not a number */
|
||||
else
|
||||
i = e->u.ival;
|
||||
else if (e->k == VKFLT && luaV_flttointeger(e->u.nval, &i, 0))
|
||||
*isfloat = 1;
|
||||
if (!hasjumps(e) && fitsC(*i)) {
|
||||
*i += OFFSET_sC;
|
||||
else
|
||||
return 0; /* not a number */
|
||||
if (!hasjumps(e) && fitsC(i)) {
|
||||
*pi = int2sC(cast_int(i));
|
||||
return 1;
|
||||
}
|
||||
else
|
||||
@@ -1166,18 +1240,20 @@ static int isSCnumber (expdesc *e, lua_Integer *i, int *isfloat) {
|
||||
** values in registers.
|
||||
*/
|
||||
void luaK_indexed (FuncState *fs, expdesc *t, expdesc *k) {
|
||||
if (k->k == VKSTR)
|
||||
str2K(fs, k);
|
||||
lua_assert(!hasjumps(t) &&
|
||||
(t->k == VLOCAL || t->k == VNONRELOC || t->k == VUPVAL));
|
||||
if (t->k == VUPVAL && !isKstr(fs, k)) /* upvalue indexed by non string? */
|
||||
if (t->k == VUPVAL && !isKstr(fs, k)) /* upvalue indexed by non 'Kstr'? */
|
||||
luaK_exp2anyreg(fs, t); /* put it in a register */
|
||||
if (t->k == VUPVAL) {
|
||||
t->u.ind.t = t->u.var.idx; /* upvalue index */
|
||||
t->u.ind.t = t->u.info; /* upvalue index */
|
||||
t->u.ind.idx = k->u.info; /* literal string */
|
||||
t->k = VINDEXUP;
|
||||
}
|
||||
else {
|
||||
/* register index of the table */
|
||||
t->u.ind.t = (t->k == VLOCAL) ? t->u.var.idx: t->u.info;
|
||||
t->u.ind.t = (t->k == VLOCAL) ? t->u.var.sidx: t->u.info;
|
||||
if (isKstr(fs, k)) {
|
||||
t->u.ind.idx = k->u.info; /* literal string */
|
||||
t->k = VINDEXSTR;
|
||||
@@ -1218,7 +1294,7 @@ static int validop (int op, TValue *v1, TValue *v2) {
|
||||
** (In this case, 'e1' has the final result.)
|
||||
*/
|
||||
static int constfolding (FuncState *fs, int op, expdesc *e1,
|
||||
const expdesc *e2) {
|
||||
const expdesc *e2) {
|
||||
TValue v1, v2, res;
|
||||
if (!tonumeral(e1, &v1) || !tonumeral(e2, &v2) || !validop(op, &v1, &v2))
|
||||
return 0; /* non-numeric operands or not safe to fold */
|
||||
@@ -1257,18 +1333,18 @@ static void codeunexpval (FuncState *fs, OpCode op, expdesc *e, int line) {
|
||||
** (everything but logical operators 'and'/'or' and comparison
|
||||
** operators).
|
||||
** Expression to produce final result will be encoded in 'e1'.
|
||||
** Because 'luaK_exp2anyreg' can free registers, its calls must be
|
||||
** in "stack order" (that is, first on 'e2', which may have more
|
||||
** recent registers to be released).
|
||||
*/
|
||||
static void finishbinexpval (FuncState *fs, expdesc *e1, expdesc *e2,
|
||||
OpCode op, int v2, int k, int line) {
|
||||
OpCode op, int v2, int flip, int line,
|
||||
OpCode mmop, TMS event) {
|
||||
int v1 = luaK_exp2anyreg(fs, e1);
|
||||
int pc = luaK_codeABCk(fs, op, 0, v1, v2, k);
|
||||
int pc = luaK_codeABCk(fs, op, 0, v1, v2, 0);
|
||||
freeexps(fs, e1, e2);
|
||||
e1->u.info = pc;
|
||||
e1->k = VRELOC; /* all those operations are relocatable */
|
||||
luaK_fixline(fs, line);
|
||||
luaK_codeABCk(fs, mmop, v1, v2, event, flip); /* to call metamethod */
|
||||
luaK_fixline(fs, line);
|
||||
}
|
||||
|
||||
|
||||
@@ -1279,17 +1355,43 @@ static void finishbinexpval (FuncState *fs, expdesc *e1, expdesc *e2,
|
||||
static void codebinexpval (FuncState *fs, OpCode op,
|
||||
expdesc *e1, expdesc *e2, int line) {
|
||||
int v2 = luaK_exp2anyreg(fs, e2); /* both operands are in registers */
|
||||
finishbinexpval(fs, e1, e2, op, v2, 0, line);
|
||||
lua_assert(OP_ADD <= op && op <= OP_SHR);
|
||||
finishbinexpval(fs, e1, e2, op, v2, 0, line, OP_MMBIN,
|
||||
cast(TMS, (op - OP_ADD) + TM_ADD));
|
||||
}
|
||||
|
||||
|
||||
/*
|
||||
** Code binary operators ('+', '-', ...) with immediate operands.
|
||||
** Code binary operators with immediate operands.
|
||||
*/
|
||||
static void codebini (FuncState *fs, OpCode op,
|
||||
expdesc *e1, expdesc *e2, int k, int line) {
|
||||
int v2 = cast_int(e2->u.ival) + OFFSET_sC; /* immediate operand */
|
||||
finishbinexpval(fs, e1, e2, op, v2, k, line);
|
||||
expdesc *e1, expdesc *e2, int flip, int line,
|
||||
TMS event) {
|
||||
int v2 = int2sC(cast_int(e2->u.ival)); /* immediate operand */
|
||||
lua_assert(e2->k == VKINT);
|
||||
finishbinexpval(fs, e1, e2, op, v2, flip, line, OP_MMBINI, event);
|
||||
}
|
||||
|
||||
|
||||
/* Try to code a binary operator negating its second operand.
|
||||
** For the metamethod, 2nd operand must keep its original value.
|
||||
*/
|
||||
static int finishbinexpneg (FuncState *fs, expdesc *e1, expdesc *e2,
|
||||
OpCode op, int line, TMS event) {
|
||||
if (!luaK_isKint(e2))
|
||||
return 0; /* not an integer constant */
|
||||
else {
|
||||
lua_Integer i2 = e2->u.ival;
|
||||
if (!(fitsC(i2) && fitsC(-i2)))
|
||||
return 0; /* not in the proper range */
|
||||
else { /* operating a small integer constant */
|
||||
int v2 = cast_int(i2);
|
||||
finishbinexpval(fs, e1, e2, op, int2sC(-v2), 0, line, OP_MMBINI, event);
|
||||
/* correct metamethod argument */
|
||||
SETARG_B(fs->f->code[fs->pc - 1], int2sC(v2));
|
||||
return 1; /* successfully coded */
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -1300,19 +1402,18 @@ static void swapexps (expdesc *e1, expdesc *e2) {
|
||||
|
||||
/*
|
||||
** Code arithmetic operators ('+', '-', ...). If second operand is a
|
||||
** constant in the proper range, use variant opcodes with immediate
|
||||
** operands or K operands.
|
||||
** constant in the proper range, use variant opcodes with K operands.
|
||||
*/
|
||||
static void codearith (FuncState *fs, OpCode op,
|
||||
static void codearith (FuncState *fs, BinOpr opr,
|
||||
expdesc *e1, expdesc *e2, int flip, int line) {
|
||||
if (isSCint(e2)) /* immediate operand? */
|
||||
codebini(fs, cast(OpCode, op - OP_ADD + OP_ADDI), e1, e2, flip, line);
|
||||
else if (tonumeral(e2, NULL) && luaK_exp2K(fs, e2)) { /* K operand? */
|
||||
TMS event = cast(TMS, opr + TM_ADD);
|
||||
if (tonumeral(e2, NULL) && luaK_exp2K(fs, e2)) { /* K operand? */
|
||||
int v2 = e2->u.info; /* K index */
|
||||
op = cast(OpCode, op - OP_ADD + OP_ADDK);
|
||||
finishbinexpval(fs, e1, e2, op, v2, flip, line);
|
||||
OpCode op = cast(OpCode, opr + OP_ADDK);
|
||||
finishbinexpval(fs, e1, e2, op, v2, flip, line, OP_MMBINK, event);
|
||||
}
|
||||
else { /* 'e2' is neither an immediate nor a K operand */
|
||||
OpCode op = cast(OpCode, opr + OP_ADD);
|
||||
if (flip)
|
||||
swapexps(e1, e2); /* back to original order */
|
||||
codebinexpval(fs, op, e1, e2, line); /* use standard operators */
|
||||
@@ -1325,14 +1426,17 @@ static void codearith (FuncState *fs, OpCode op,
|
||||
** numeric constant, change order of operands to try to use an
|
||||
** immediate or K operator.
|
||||
*/
|
||||
static void codecommutative (FuncState *fs, OpCode op,
|
||||
static void codecommutative (FuncState *fs, BinOpr op,
|
||||
expdesc *e1, expdesc *e2, int line) {
|
||||
int flip = 0;
|
||||
if (tonumeral(e1, NULL)) { /* is first operand a numeric constant? */
|
||||
swapexps(e1, e2); /* change order */
|
||||
flip = 1;
|
||||
}
|
||||
codearith(fs, op, e1, e2, flip, line);
|
||||
if (op == OPR_ADD && isSCint(e2)) /* immediate operand? */
|
||||
codebini(fs, cast(OpCode, OP_ADDI), e1, e2, flip, line, TM_ADD);
|
||||
else
|
||||
codearith(fs, op, e1, e2, flip, line);
|
||||
}
|
||||
|
||||
|
||||
@@ -1342,41 +1446,23 @@ static void codecommutative (FuncState *fs, OpCode op,
|
||||
*/
|
||||
static void codebitwise (FuncState *fs, BinOpr opr,
|
||||
expdesc *e1, expdesc *e2, int line) {
|
||||
int inv = 0;
|
||||
int flip = 0;
|
||||
int v2;
|
||||
OpCode op;
|
||||
if (e1->k == VKINT && luaK_exp2RK(fs, e1)) {
|
||||
swapexps(e1, e2); /* 'e2' will be the constant operand */
|
||||
inv = 1;
|
||||
flip = 1;
|
||||
}
|
||||
else if (!(e2->k == VKINT && luaK_exp2RK(fs, e2))) { /* no constants? */
|
||||
op = cast(OpCode, opr - OPR_BAND + OP_BAND);
|
||||
op = cast(OpCode, opr + OP_ADD);
|
||||
codebinexpval(fs, op, e1, e2, line); /* all-register opcodes */
|
||||
return;
|
||||
}
|
||||
v2 = e2->u.info; /* index in K array */
|
||||
op = cast(OpCode, opr - OPR_BAND + OP_BANDK);
|
||||
op = cast(OpCode, opr + OP_ADDK);
|
||||
lua_assert(ttisinteger(&fs->f->k[v2]));
|
||||
finishbinexpval(fs, e1, e2, op, v2, inv, line);
|
||||
}
|
||||
|
||||
|
||||
/*
|
||||
** Code shift operators. If second operand is constant, use immediate
|
||||
** operand (negating it if shift is in the other direction).
|
||||
*/
|
||||
static void codeshift (FuncState *fs, OpCode op,
|
||||
expdesc *e1, expdesc *e2, int line) {
|
||||
if (isSCint(e2)) {
|
||||
int changedir = 0;
|
||||
if (op == OP_SHL) {
|
||||
changedir = 1;
|
||||
e2->u.ival = -(e2->u.ival);
|
||||
}
|
||||
codebini(fs, OP_SHRI, e1, e2, changedir, line);
|
||||
}
|
||||
else
|
||||
codebinexpval(fs, op, e1, e2, line);
|
||||
finishbinexpval(fs, e1, e2, op, v2, flip, line, OP_MMBINK,
|
||||
cast(TMS, opr + TM_ADD));
|
||||
}
|
||||
|
||||
|
||||
@@ -1386,18 +1472,18 @@ static void codeshift (FuncState *fs, OpCode op,
|
||||
*/
|
||||
static void codeorder (FuncState *fs, OpCode op, expdesc *e1, expdesc *e2) {
|
||||
int r1, r2;
|
||||
lua_Integer im;
|
||||
int im;
|
||||
int isfloat = 0;
|
||||
if (isSCnumber(e2, &im, &isfloat)) {
|
||||
/* use immediate operand */
|
||||
r1 = luaK_exp2anyreg(fs, e1);
|
||||
r2 = cast_int(im);
|
||||
r2 = im;
|
||||
op = cast(OpCode, (op - OP_LT) + OP_LTI);
|
||||
}
|
||||
else if (isSCnumber(e1, &im, &isfloat)) {
|
||||
/* transform (A < B) to (B > A) and (A <= B) to (B >= A) */
|
||||
r1 = luaK_exp2anyreg(fs, e2);
|
||||
r2 = cast_int(im);
|
||||
r2 = im;
|
||||
op = (op == OP_LT) ? OP_GTI : OP_GEI;
|
||||
}
|
||||
else { /* regular case, compare two registers */
|
||||
@@ -1416,7 +1502,7 @@ static void codeorder (FuncState *fs, OpCode op, expdesc *e1, expdesc *e2) {
|
||||
*/
|
||||
static void codeeq (FuncState *fs, BinOpr opr, expdesc *e1, expdesc *e2) {
|
||||
int r1, r2;
|
||||
lua_Integer im;
|
||||
int im;
|
||||
int isfloat = 0; /* not needed here, but kept for symmetry */
|
||||
OpCode op;
|
||||
if (e1->k != VNONRELOC) {
|
||||
@@ -1426,7 +1512,7 @@ static void codeeq (FuncState *fs, BinOpr opr, expdesc *e1, expdesc *e2) {
|
||||
r1 = luaK_exp2anyreg(fs, e1); /* 1nd expression must be in register */
|
||||
if (isSCnumber(e2, &im, &isfloat)) {
|
||||
op = OP_EQI;
|
||||
r2 = cast_int(im); /* immediate operand */
|
||||
r2 = im; /* immediate operand */
|
||||
}
|
||||
else if (luaK_exp2RK(fs, e2)) { /* 1st expression is constant? */
|
||||
op = OP_EQK;
|
||||
@@ -1447,11 +1533,12 @@ static void codeeq (FuncState *fs, BinOpr opr, expdesc *e1, expdesc *e2) {
|
||||
*/
|
||||
void luaK_prefix (FuncState *fs, UnOpr op, expdesc *e, int line) {
|
||||
static const expdesc ef = {VKINT, {0}, NO_JUMP, NO_JUMP};
|
||||
luaK_dischargevars(fs, e);
|
||||
switch (op) {
|
||||
case OPR_MINUS: case OPR_BNOT: /* use 'ef' as fake 2nd operand */
|
||||
if (constfolding(fs, op + LUA_OPUNM, e, &ef))
|
||||
break;
|
||||
/* FALLTHROUGH */
|
||||
/* else */ /* FALLTHROUGH */
|
||||
case OPR_LEN:
|
||||
codeunexpval(fs, cast(OpCode, op + OP_UNM), e, line);
|
||||
break;
|
||||
@@ -1466,6 +1553,7 @@ void luaK_prefix (FuncState *fs, UnOpr op, expdesc *e, int line) {
|
||||
** 2nd operand.
|
||||
*/
|
||||
void luaK_infix (FuncState *fs, BinOpr op, expdesc *v) {
|
||||
luaK_dischargevars(fs, v);
|
||||
switch (op) {
|
||||
case OPR_AND: {
|
||||
luaK_goiftrue(fs, v); /* go ahead only if 'v' is true */
|
||||
@@ -1497,8 +1585,7 @@ void luaK_infix (FuncState *fs, BinOpr op, expdesc *v) {
|
||||
}
|
||||
case OPR_LT: case OPR_LE:
|
||||
case OPR_GT: case OPR_GE: {
|
||||
lua_Integer dummy;
|
||||
int dummy2;
|
||||
int dummy, dummy2;
|
||||
if (!isSCnumber(v, &dummy, &dummy2))
|
||||
luaK_exp2anyreg(fs, v);
|
||||
/* else keep numeral, which may be an immediate operand */
|
||||
@@ -1535,17 +1622,18 @@ static void codeconcat (FuncState *fs, expdesc *e1, expdesc *e2, int line) {
|
||||
*/
|
||||
void luaK_posfix (FuncState *fs, BinOpr opr,
|
||||
expdesc *e1, expdesc *e2, int line) {
|
||||
luaK_dischargevars(fs, e2);
|
||||
if (foldbinop(opr) && constfolding(fs, opr + LUA_OPADD, e1, e2))
|
||||
return; /* done by folding */
|
||||
switch (opr) {
|
||||
case OPR_AND: {
|
||||
lua_assert(e1->t == NO_JUMP); /* list closed by 'luK_infix' */
|
||||
luaK_dischargevars(fs, e2);
|
||||
lua_assert(e1->t == NO_JUMP); /* list closed by 'luaK_infix' */
|
||||
luaK_concat(fs, &e2->f, e1->f);
|
||||
*e1 = *e2;
|
||||
break;
|
||||
}
|
||||
case OPR_OR: {
|
||||
lua_assert(e1->f == NO_JUMP); /* list closed by 'luK_infix' */
|
||||
luaK_dischargevars(fs, e2);
|
||||
lua_assert(e1->f == NO_JUMP); /* list closed by 'luaK_infix' */
|
||||
luaK_concat(fs, &e2->t, e1->t);
|
||||
*e1 = *e2;
|
||||
break;
|
||||
@@ -1556,35 +1644,39 @@ void luaK_posfix (FuncState *fs, BinOpr opr,
|
||||
break;
|
||||
}
|
||||
case OPR_ADD: case OPR_MUL: {
|
||||
if (!constfolding(fs, opr + LUA_OPADD, e1, e2))
|
||||
codecommutative(fs, cast(OpCode, opr + OP_ADD), e1, e2, line);
|
||||
codecommutative(fs, opr, e1, e2, line);
|
||||
break;
|
||||
}
|
||||
case OPR_SUB: case OPR_DIV:
|
||||
case OPR_IDIV: case OPR_MOD: case OPR_POW: {
|
||||
if (!constfolding(fs, opr + LUA_OPADD, e1, e2))
|
||||
codearith(fs, cast(OpCode, opr + OP_ADD), e1, e2, 0, line);
|
||||
case OPR_SUB: {
|
||||
if (finishbinexpneg(fs, e1, e2, OP_ADDI, line, TM_SUB))
|
||||
break; /* coded as (r1 + -I) */
|
||||
/* ELSE */
|
||||
} /* FALLTHROUGH */
|
||||
case OPR_DIV: case OPR_IDIV: case OPR_MOD: case OPR_POW: {
|
||||
codearith(fs, opr, e1, e2, 0, line);
|
||||
break;
|
||||
}
|
||||
case OPR_BAND: case OPR_BOR: case OPR_BXOR: {
|
||||
if (!constfolding(fs, opr + LUA_OPADD, e1, e2))
|
||||
codebitwise(fs, opr, e1, e2, line);
|
||||
codebitwise(fs, opr, e1, e2, line);
|
||||
break;
|
||||
}
|
||||
case OPR_SHL: {
|
||||
if (!constfolding(fs, LUA_OPSHL, e1, e2)) {
|
||||
if (isSCint(e1)) {
|
||||
swapexps(e1, e2);
|
||||
codebini(fs, OP_SHLI, e1, e2, 1, line);
|
||||
}
|
||||
else
|
||||
codeshift(fs, OP_SHL, e1, e2, line);
|
||||
if (isSCint(e1)) {
|
||||
swapexps(e1, e2);
|
||||
codebini(fs, OP_SHLI, e1, e2, 1, line, TM_SHL); /* I << r2 */
|
||||
}
|
||||
else if (finishbinexpneg(fs, e1, e2, OP_SHRI, line, TM_SHL)) {
|
||||
/* coded as (r1 >> -I) */;
|
||||
}
|
||||
else /* regular case (two registers) */
|
||||
codebinexpval(fs, OP_SHL, e1, e2, line);
|
||||
break;
|
||||
}
|
||||
case OPR_SHR: {
|
||||
if (!constfolding(fs, LUA_OPSHR, e1, e2))
|
||||
codeshift(fs, OP_SHR, e1, e2, line);
|
||||
if (isSCint(e2))
|
||||
codebini(fs, OP_SHRI, e1, e2, 0, line, TM_SHR); /* r1 >> I */
|
||||
else /* regular case (two registers) */
|
||||
codebinexpval(fs, OP_SHR, e1, e2, line);
|
||||
break;
|
||||
}
|
||||
case OPR_EQ: case OPR_NE: {
|
||||
@@ -1618,6 +1710,22 @@ void luaK_fixline (FuncState *fs, int line) {
|
||||
}
|
||||
|
||||
|
||||
void luaK_settablesize (FuncState *fs, int pc, int ra, int rc, int rb) {
|
||||
Instruction *inst = &fs->f->code[pc];
|
||||
int extra = 0;
|
||||
int k = 0;
|
||||
if (rb != 0)
|
||||
rb = luaO_ceillog2(rb) + 1; /* hash size */
|
||||
if (rc > MAXARG_C) { /* does it need the extra argument? */
|
||||
extra = rc / (MAXARG_C + 1);
|
||||
rc %= (MAXARG_C + 1);
|
||||
k = 1;
|
||||
}
|
||||
*inst = CREATE_ABCk(OP_NEWTABLE, ra, rb, rc, k);
|
||||
*(inst + 1) = CREATE_Ax(OP_EXTRAARG, extra);
|
||||
}
|
||||
|
||||
|
||||
/*
|
||||
** Emit a SETLIST instruction.
|
||||
** 'base' is register that keeps table;
|
||||
@@ -1626,17 +1734,17 @@ void luaK_fixline (FuncState *fs, int line) {
|
||||
** table (or LUA_MULTRET to add up to stack top).
|
||||
*/
|
||||
void luaK_setlist (FuncState *fs, int base, int nelems, int tostore) {
|
||||
int c = (nelems - 1)/LFIELDS_PER_FLUSH + 1;
|
||||
int b = (tostore == LUA_MULTRET) ? 0 : tostore;
|
||||
lua_assert(tostore != 0 && tostore <= LFIELDS_PER_FLUSH);
|
||||
if (c <= MAXARG_C)
|
||||
luaK_codeABC(fs, OP_SETLIST, base, b, c);
|
||||
else if (c <= MAXARG_Ax) {
|
||||
luaK_codeABC(fs, OP_SETLIST, base, b, 0);
|
||||
codeextraarg(fs, c);
|
||||
if (tostore == LUA_MULTRET)
|
||||
tostore = 0;
|
||||
if (nelems <= MAXARG_C)
|
||||
luaK_codeABC(fs, OP_SETLIST, base, tostore, nelems);
|
||||
else {
|
||||
int extra = nelems / (MAXARG_C + 1);
|
||||
nelems %= (MAXARG_C + 1);
|
||||
luaK_codeABCk(fs, OP_SETLIST, base, tostore, nelems, 1);
|
||||
codeextraarg(fs, extra);
|
||||
}
|
||||
else
|
||||
luaX_syntaxerror(fs->ls, "constructor too long");
|
||||
fs->freereg = base + 1; /* free registers with list values */
|
||||
}
|
||||
|
||||
@@ -1675,10 +1783,10 @@ void luaK_finish (FuncState *fs) {
|
||||
SET_OPCODE(*pc, OP_RETURN);
|
||||
} /* FALLTHROUGH */
|
||||
case OP_RETURN: case OP_TAILCALL: {
|
||||
if (fs->needclose || p->is_vararg) {
|
||||
SETARG_C(*pc, p->is_vararg ? p->numparams + 1 : 0);
|
||||
SETARG_k(*pc, 1); /* signal that there is extra work */
|
||||
}
|
||||
if (fs->needclose)
|
||||
SETARG_k(*pc, 1); /* signal that it needs to close */
|
||||
if (p->is_vararg)
|
||||
SETARG_C(*pc, p->numparams + 1); /* signal that it is vararg */
|
||||
break;
|
||||
}
|
||||
case OP_JMP: {
|
||||
|
||||
@@ -24,19 +24,27 @@
|
||||
** grep "ORDER OPR" if you change these enums (ORDER OP)
|
||||
*/
|
||||
typedef enum BinOpr {
|
||||
/* arithmetic operators */
|
||||
OPR_ADD, OPR_SUB, OPR_MUL, OPR_MOD, OPR_POW,
|
||||
OPR_DIV,
|
||||
OPR_IDIV,
|
||||
OPR_DIV, OPR_IDIV,
|
||||
/* bitwise operators */
|
||||
OPR_BAND, OPR_BOR, OPR_BXOR,
|
||||
OPR_SHL, OPR_SHR,
|
||||
/* string operator */
|
||||
OPR_CONCAT,
|
||||
/* comparison operators */
|
||||
OPR_EQ, OPR_LT, OPR_LE,
|
||||
OPR_NE, OPR_GT, OPR_GE,
|
||||
/* logical operators */
|
||||
OPR_AND, OPR_OR,
|
||||
OPR_NOBINOPR
|
||||
} BinOpr;
|
||||
|
||||
|
||||
/* true if operation is foldable (that is, it is arithmetic or bitwise) */
|
||||
#define foldbinop(op) ((op) <= OPR_SHR)
|
||||
|
||||
|
||||
#define luaK_codeABC(fs,o,a,b,c) luaK_codeABCk(fs,o,a,b,c,0)
|
||||
|
||||
|
||||
@@ -51,16 +59,17 @@ typedef enum UnOpr { OPR_MINUS, OPR_BNOT, OPR_NOT, OPR_LEN, OPR_NOUNOPR } UnOpr;
|
||||
|
||||
#define luaK_jumpto(fs,t) luaK_patchlist(fs, luaK_jump(fs), t)
|
||||
|
||||
LUAI_FUNC int luaK_code (FuncState *fs, Instruction i);
|
||||
LUAI_FUNC int luaK_codeABx (FuncState *fs, OpCode o, int A, unsigned int Bx);
|
||||
LUAI_FUNC int luaK_codeAsBx (FuncState *fs, OpCode o, int A, int Bx);
|
||||
LUAI_FUNC int luaK_codeABCk (FuncState *fs, OpCode o, int A,
|
||||
int B, int C, int k);
|
||||
LUAI_FUNC int luaK_isKint (expdesc *e);
|
||||
LUAI_FUNC int luaK_exp2const (FuncState *fs, const expdesc *e, TValue *v);
|
||||
LUAI_FUNC void luaK_fixline (FuncState *fs, int line);
|
||||
LUAI_FUNC void luaK_nil (FuncState *fs, int from, int n);
|
||||
LUAI_FUNC void luaK_reserveregs (FuncState *fs, int n);
|
||||
LUAI_FUNC void luaK_checkstack (FuncState *fs, int n);
|
||||
LUAI_FUNC int luaK_stringK (FuncState *fs, TString *s);
|
||||
LUAI_FUNC void luaK_int (FuncState *fs, int reg, lua_Integer n);
|
||||
LUAI_FUNC void luaK_dischargevars (FuncState *fs, expdesc *e);
|
||||
LUAI_FUNC int luaK_exp2anyreg (FuncState *fs, expdesc *e);
|
||||
@@ -85,6 +94,8 @@ LUAI_FUNC void luaK_prefix (FuncState *fs, UnOpr op, expdesc *v, int line);
|
||||
LUAI_FUNC void luaK_infix (FuncState *fs, BinOpr op, expdesc *v);
|
||||
LUAI_FUNC void luaK_posfix (FuncState *fs, BinOpr op, expdesc *v1,
|
||||
expdesc *v2, int line);
|
||||
LUAI_FUNC void luaK_settablesize (FuncState *fs, int pc,
|
||||
int ra, int rb, int rc);
|
||||
LUAI_FUNC void luaK_setlist (FuncState *fs, int base, int nelems, int tostore);
|
||||
LUAI_FUNC void luaK_finish (FuncState *fs);
|
||||
LUAI_FUNC l_noret luaK_semerror (LexState *ls, const char *msg);
|
||||
|
||||
+2
-1
@@ -116,7 +116,8 @@ static int luaB_yield (lua_State *L) {
|
||||
#define COS_NORM 3
|
||||
|
||||
|
||||
static const char *statname[] = {"running", "dead", "suspended", "normal"};
|
||||
static const char *const statname[] =
|
||||
{"running", "dead", "suspended", "normal"};
|
||||
|
||||
|
||||
static int auxstatus (lua_State *L, lua_State *co) {
|
||||
|
||||
@@ -21,10 +21,10 @@
|
||||
|
||||
|
||||
/*
|
||||
** The hook table at registry[&HOOKKEY] maps threads to their current
|
||||
** hook function. (We only need the unique address of 'HOOKKEY'.)
|
||||
** The hook table at registry[HOOKKEY] maps threads to their current
|
||||
** hook function.
|
||||
*/
|
||||
static const int HOOKKEY = 0;
|
||||
static const char *const HOOKKEY = "_HOOKKEY";
|
||||
|
||||
|
||||
/*
|
||||
@@ -65,7 +65,7 @@ static int db_setmetatable (lua_State *L) {
|
||||
static int db_getuservalue (lua_State *L) {
|
||||
int n = (int)luaL_optinteger(L, 2, 1);
|
||||
if (lua_type(L, 1) != LUA_TUSERDATA)
|
||||
lua_pushnil(L);
|
||||
luaL_pushfail(L);
|
||||
else if (lua_getiuservalue(L, 1, n) != LUA_TNONE) {
|
||||
lua_pushboolean(L, 1);
|
||||
return 2;
|
||||
@@ -80,7 +80,7 @@ static int db_setuservalue (lua_State *L) {
|
||||
luaL_checkany(L, 2);
|
||||
lua_settop(L, 2);
|
||||
if (!lua_setiuservalue(L, 1, n))
|
||||
lua_pushnil(L);
|
||||
luaL_pushfail(L);
|
||||
return 1;
|
||||
}
|
||||
|
||||
@@ -159,7 +159,7 @@ static int db_getinfo (lua_State *L) {
|
||||
}
|
||||
else { /* stack level */
|
||||
if (!lua_getstack(L1, (int)luaL_checkinteger(L, arg + 1), &ar)) {
|
||||
lua_pushnil(L); /* level out of range */
|
||||
luaL_pushfail(L); /* level out of range */
|
||||
return 1;
|
||||
}
|
||||
}
|
||||
@@ -223,7 +223,7 @@ static int db_getlocal (lua_State *L) {
|
||||
return 2;
|
||||
}
|
||||
else {
|
||||
lua_pushnil(L); /* no name (nor value) */
|
||||
luaL_pushfail(L); /* no name (nor value) */
|
||||
return 1;
|
||||
}
|
||||
}
|
||||
@@ -314,7 +314,7 @@ static int db_upvaluejoin (lua_State *L) {
|
||||
static void hookf (lua_State *L, lua_Debug *ar) {
|
||||
static const char *const hooknames[] =
|
||||
{"call", "return", "line", "count", "tail call"};
|
||||
lua_rawgetp(L, LUA_REGISTRYINDEX, &HOOKKEY);
|
||||
lua_getfield(L, LUA_REGISTRYINDEX, HOOKKEY);
|
||||
lua_pushthread(L);
|
||||
if (lua_rawget(L, -2) == LUA_TFUNCTION) { /* is there a hook function? */
|
||||
lua_pushstring(L, hooknames[(int)ar->event]); /* push event name */
|
||||
@@ -367,14 +367,12 @@ static int db_sethook (lua_State *L) {
|
||||
count = (int)luaL_optinteger(L, arg + 3, 0);
|
||||
func = hookf; mask = makemask(smask, count);
|
||||
}
|
||||
if (lua_rawgetp(L, LUA_REGISTRYINDEX, &HOOKKEY) == LUA_TNIL) {
|
||||
lua_createtable(L, 0, 2); /* create a hook table */
|
||||
lua_pushvalue(L, -1);
|
||||
lua_rawsetp(L, LUA_REGISTRYINDEX, &HOOKKEY); /* set it in position */
|
||||
if (!luaL_getsubtable(L, LUA_REGISTRYINDEX, HOOKKEY)) {
|
||||
/* table just created; initialize it */
|
||||
lua_pushstring(L, "k");
|
||||
lua_setfield(L, -2, "__mode"); /** hooktable.__mode = "k" */
|
||||
lua_pushvalue(L, -1);
|
||||
lua_setmetatable(L, -2); /* setmetatable(hooktable) = hooktable */
|
||||
lua_setmetatable(L, -2); /* metatable(hooktable) = hooktable */
|
||||
}
|
||||
checkstack(L, L1, 1);
|
||||
lua_pushthread(L1); lua_xmove(L1, L, 1); /* key (thread) */
|
||||
@@ -391,12 +389,14 @@ static int db_gethook (lua_State *L) {
|
||||
char buff[5];
|
||||
int mask = lua_gethookmask(L1);
|
||||
lua_Hook hook = lua_gethook(L1);
|
||||
if (hook == NULL) /* no hook? */
|
||||
lua_pushnil(L);
|
||||
if (hook == NULL) { /* no hook? */
|
||||
luaL_pushfail(L);
|
||||
return 1;
|
||||
}
|
||||
else if (hook != hookf) /* external hook? */
|
||||
lua_pushliteral(L, "external hook");
|
||||
else { /* hook table must exist */
|
||||
lua_rawgetp(L, LUA_REGISTRYINDEX, &HOOKKEY);
|
||||
lua_getfield(L, LUA_REGISTRYINDEX, HOOKKEY);
|
||||
checkstack(L, L1, 1);
|
||||
lua_pushthread(L1); lua_xmove(L1, L, 1);
|
||||
lua_rawget(L, -2); /* 1st result = hooktable[L1] */
|
||||
@@ -437,6 +437,17 @@ static int db_traceback (lua_State *L) {
|
||||
}
|
||||
|
||||
|
||||
static int db_setcstacklimit (lua_State *L) {
|
||||
int limit = (int)luaL_checkinteger(L, 1);
|
||||
int res = lua_setcstacklimit(L, limit);
|
||||
if (res == 0)
|
||||
lua_pushboolean(L, 0);
|
||||
else
|
||||
lua_pushinteger(L, res);
|
||||
return 1;
|
||||
}
|
||||
|
||||
|
||||
static const luaL_Reg dblib[] = {
|
||||
{"debug", db_debug},
|
||||
{"getuservalue", db_getuservalue},
|
||||
@@ -454,6 +465,7 @@ static const luaL_Reg dblib[] = {
|
||||
{"setmetatable", db_setmetatable},
|
||||
{"setupvalue", db_setupvalue},
|
||||
{"traceback", db_traceback},
|
||||
{"setcstacklimit", db_setcstacklimit},
|
||||
{NULL, NULL}
|
||||
};
|
||||
|
||||
|
||||
@@ -465,12 +465,14 @@ static int filterpc (int pc, int jmptarget) {
|
||||
|
||||
|
||||
/*
|
||||
** try to find last instruction before 'lastpc' that modified register 'reg'
|
||||
** Try to find last instruction before 'lastpc' that modified register 'reg'.
|
||||
*/
|
||||
static int findsetreg (const Proto *p, int lastpc, int reg) {
|
||||
int pc;
|
||||
int setreg = -1; /* keep last instruction that changed 'reg' */
|
||||
int jmptarget = 0; /* any code before this address is conditional */
|
||||
if (testMMMode(GET_OPCODE(p->code[lastpc])))
|
||||
lastpc--; /* previous instruction was not actually executed */
|
||||
for (pc = 0; pc < lastpc; pc++) {
|
||||
Instruction i = p->code[pc];
|
||||
OpCode op = GET_OPCODE(i);
|
||||
@@ -620,24 +622,8 @@ static const char *funcnamefromcode (lua_State *L, CallInfo *ci,
|
||||
case OP_SETTABUP: case OP_SETTABLE: case OP_SETI: case OP_SETFIELD:
|
||||
tm = TM_NEWINDEX;
|
||||
break;
|
||||
case OP_ADDI: case OP_SUBI: case OP_MULI: case OP_MODI:
|
||||
case OP_POWI: case OP_DIVI: case OP_IDIVI: {
|
||||
int offset = GET_OPCODE(i) - OP_ADDI; /* ORDER OP */
|
||||
tm = cast(TMS, offset + TM_ADD); /* ORDER TM */
|
||||
break;
|
||||
}
|
||||
case OP_ADDK: case OP_SUBK: case OP_MULK: case OP_MODK:
|
||||
case OP_POWK: case OP_DIVK: case OP_IDIVK:
|
||||
case OP_BANDK: case OP_BORK: case OP_BXORK: {
|
||||
int offset = GET_OPCODE(i) - OP_ADDK; /* ORDER OP */
|
||||
tm = cast(TMS, offset + TM_ADD); /* ORDER TM */
|
||||
break;
|
||||
}
|
||||
case OP_ADD: case OP_SUB: case OP_MUL: case OP_MOD:
|
||||
case OP_POW: case OP_DIV: case OP_IDIV: case OP_BAND:
|
||||
case OP_BOR: case OP_BXOR: case OP_SHL: case OP_SHR: {
|
||||
int offset = GET_OPCODE(i) - OP_ADD; /* ORDER OP */
|
||||
tm = cast(TMS, offset + TM_ADD); /* ORDER TM */
|
||||
case OP_MMBIN: case OP_MMBINI: case OP_MMBINK: {
|
||||
tm = cast(TMS, GETARG_C(i));
|
||||
break;
|
||||
}
|
||||
case OP_UNM: tm = TM_UNM; break;
|
||||
@@ -648,8 +634,8 @@ static const char *funcnamefromcode (lua_State *L, CallInfo *ci,
|
||||
case OP_LT: case OP_LE: case OP_LTI: case OP_LEI:
|
||||
*name = "order"; /* '<=' can call '__lt', etc. */
|
||||
return "metamethod";
|
||||
case OP_SHRI: case OP_SHLI:
|
||||
*name = "shift";
|
||||
case OP_CLOSE: case OP_RETURN:
|
||||
*name = "close";
|
||||
return "metamethod";
|
||||
default:
|
||||
return NULL; /* cannot find a reasonable name */
|
||||
|
||||
@@ -139,9 +139,9 @@ l_noret luaD_throw (lua_State *L, int errcode) {
|
||||
|
||||
|
||||
int luaD_rawrunprotected (lua_State *L, Pfunc f, void *ud) {
|
||||
l_uint32 oldnCcalls = L->nCcalls - L->nci;
|
||||
global_State *g = G(L);
|
||||
l_uint32 oldnCcalls = g->Cstacklimit - (L->nCcalls + L->nci);
|
||||
struct lua_longjmp lj;
|
||||
lua_assert(L->nCcalls >= L->nci);
|
||||
lj.status = LUA_OK;
|
||||
lj.previous = L->errorJmp; /* chain new error handler */
|
||||
L->errorJmp = &lj;
|
||||
@@ -149,7 +149,7 @@ int luaD_rawrunprotected (lua_State *L, Pfunc f, void *ud) {
|
||||
(*f)(L, ud);
|
||||
);
|
||||
L->errorJmp = lj.previous; /* restore old error handler */
|
||||
L->nCcalls = oldnCcalls + L->nci;
|
||||
L->nCcalls = g->Cstacklimit - oldnCcalls - L->nci;
|
||||
return lj.status;
|
||||
}
|
||||
|
||||
@@ -348,18 +348,18 @@ static StkId rethook (lua_State *L, CallInfo *ci, StkId firstres, int nres) {
|
||||
|
||||
|
||||
/*
|
||||
** Check whether __call metafield of 'func' is a function. If so, put
|
||||
** it in stack below original 'func' so that 'luaD_call' can call
|
||||
** it. Raise an error if __call metafield is not a function.
|
||||
** 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
|
||||
** an error if there is no '__call' metafield.
|
||||
*/
|
||||
void luaD_tryfuncTM (lua_State *L, StkId func) {
|
||||
const TValue *tm = luaT_gettmbyobj(L, s2v(func), TM_CALL);
|
||||
StkId p;
|
||||
if (unlikely(!ttisfunction(tm)))
|
||||
luaG_typeerror(L, s2v(func), "call");
|
||||
for (p = L->top; p > func; p--)
|
||||
if (unlikely(ttisnil(tm)))
|
||||
luaG_typeerror(L, s2v(func), "call"); /* nothing to call */
|
||||
for (p = L->top; p > func; p--) /* open space for metamethod */
|
||||
setobjs2s(L, p, p-1);
|
||||
L->top++; /* assume EXTRA_STACK */
|
||||
L->top++; /* stack space pre-allocated by the caller */
|
||||
setobj2s(L, func, tm); /* metamethod is the new function to be called */
|
||||
}
|
||||
|
||||
@@ -457,13 +457,13 @@ void luaD_pretailcall (lua_State *L, CallInfo *ci, StkId func, int narg1) {
|
||||
*/
|
||||
void luaD_call (lua_State *L, StkId func, int nresults) {
|
||||
lua_CFunction f;
|
||||
TValue *funcv = s2v(func);
|
||||
switch (ttypetag(funcv)) {
|
||||
retry:
|
||||
switch (ttypetag(s2v(func))) {
|
||||
case LUA_TCCL: /* C closure */
|
||||
f = clCvalue(funcv)->f;
|
||||
f = clCvalue(s2v(func))->f;
|
||||
goto Cfunc;
|
||||
case LUA_TLCF: /* light C function */
|
||||
f = fvalue(funcv);
|
||||
f = fvalue(s2v(func));
|
||||
Cfunc: {
|
||||
int n; /* number of returns */
|
||||
CallInfo *ci;
|
||||
@@ -487,7 +487,7 @@ void luaD_call (lua_State *L, StkId func, int nresults) {
|
||||
}
|
||||
case LUA_TLCL: { /* Lua function */
|
||||
CallInfo *ci;
|
||||
Proto *p = clLvalue(funcv)->p;
|
||||
Proto *p = clLvalue(s2v(func))->p;
|
||||
int narg = cast_int(L->top - func) - 1; /* number of real arguments */
|
||||
int nfixparams = p->numparams;
|
||||
int fsize = p->maxstacksize; /* frame size */
|
||||
@@ -505,9 +505,9 @@ void luaD_call (lua_State *L, StkId func, int nresults) {
|
||||
break;
|
||||
}
|
||||
default: { /* not a function */
|
||||
checkstackp(L, 1, func); /* space for metamethod */
|
||||
luaD_tryfuncTM(L, func); /* try to get '__call' metamethod */
|
||||
luaD_call(L, func, nresults); /* now it must be a function */
|
||||
break;
|
||||
goto retry; /* try again with metamethod */
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -521,7 +521,7 @@ void luaD_call (lua_State *L, StkId func, int nresults) {
|
||||
*/
|
||||
void luaD_callnoyield (lua_State *L, StkId func, int nResults) {
|
||||
incXCcalls(L);
|
||||
if (getCcalls(L) >= LUAI_MAXCSTACK) /* possible stack overflow? */
|
||||
if (getCcalls(L) <= CSTACKERR) /* possible stack overflow? */
|
||||
luaE_freeCI(L);
|
||||
luaD_call(L, func, nResults);
|
||||
decXCcalls(L);
|
||||
@@ -672,10 +672,10 @@ LUA_API int lua_resume (lua_State *L, lua_State *from, int nargs,
|
||||
else if (L->status != LUA_YIELD) /* ended with errors? */
|
||||
return resume_error(L, "cannot resume dead coroutine", nargs);
|
||||
if (from == NULL)
|
||||
L->nCcalls = 1;
|
||||
L->nCcalls = CSTACKTHREAD;
|
||||
else /* correct 'nCcalls' for this thread */
|
||||
L->nCcalls = getCcalls(from) - from->nci + L->nci + CSTACKCF;
|
||||
if (L->nCcalls >= LUAI_MAXCSTACK)
|
||||
L->nCcalls = getCcalls(from) + from->nci - L->nci - CSTACKCF;
|
||||
if (L->nCcalls <= CSTACKERR)
|
||||
return resume_error(L, "C stack overflow", nargs);
|
||||
luai_userstateresume(L, nargs);
|
||||
api_checknelems(L, (L->status == LUA_OK) ? nargs + 1 : nargs);
|
||||
|
||||
@@ -149,6 +149,7 @@ static void DumpUpvalues (const Proto *f, DumpState *D) {
|
||||
for (i = 0; i < n; i++) {
|
||||
DumpByte(f->upvalues[i].instack, D);
|
||||
DumpByte(f->upvalues[i].idx, D);
|
||||
DumpByte(f->upvalues[i].kind, D);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -59,14 +59,15 @@ void luaF_initupvals (lua_State *L, LClosure *cl) {
|
||||
|
||||
|
||||
/*
|
||||
** Create a new upvalue with the given tag at the given level,
|
||||
** and link it to the list of open upvalues of 'L' after entry 'prev'.
|
||||
** Create a new upvalue at the given level, and link it to the list of
|
||||
** open upvalues of 'L' after entry 'prev'.
|
||||
**/
|
||||
static UpVal *newupval (lua_State *L, int tag, StkId level, UpVal **prev) {
|
||||
GCObject *o = luaC_newobj(L, tag, sizeof(UpVal));
|
||||
static UpVal *newupval (lua_State *L, int tbc, StkId level, UpVal **prev) {
|
||||
GCObject *o = luaC_newobj(L, LUA_TUPVAL, sizeof(UpVal));
|
||||
UpVal *uv = gco2upv(o);
|
||||
UpVal *next = *prev;
|
||||
uv->v = s2v(level); /* current value lives in the stack */
|
||||
uv->tbc = tbc;
|
||||
uv->u.open.next = next; /* link it to list of open upvalues */
|
||||
uv->u.open.previous = prev;
|
||||
if (next)
|
||||
@@ -81,7 +82,7 @@ static UpVal *newupval (lua_State *L, int tag, StkId level, UpVal **prev) {
|
||||
|
||||
|
||||
/*
|
||||
** Find and reuse, or create if it does not exist, a regular upvalue
|
||||
** Find and reuse, or create if it does not exist, an upvalue
|
||||
** at the given level.
|
||||
*/
|
||||
UpVal *luaF_findupval (lua_State *L, StkId level) {
|
||||
@@ -89,12 +90,13 @@ UpVal *luaF_findupval (lua_State *L, StkId level) {
|
||||
UpVal *p;
|
||||
lua_assert(isintwups(L) || L->openupval == NULL);
|
||||
while ((p = *pp) != NULL && uplevel(p) >= level) { /* search for it */
|
||||
if (uplevel(p) == level && !isdead(G(L), p)) /* corresponding upvalue? */
|
||||
lua_assert(!isdead(G(L), p));
|
||||
if (uplevel(p) == level) /* corresponding upvalue? */
|
||||
return p; /* return it */
|
||||
pp = &p->u.open.next;
|
||||
}
|
||||
/* not found: create a new upvalue after 'pp' */
|
||||
return newupval(L, LUA_TUPVAL, level, pp);
|
||||
return newupval(L, 0, level, pp);
|
||||
}
|
||||
|
||||
|
||||
@@ -121,39 +123,53 @@ static int prepclosingmethod (lua_State *L, TValue *obj, TValue *err) {
|
||||
}
|
||||
|
||||
|
||||
/*
|
||||
** Raise an error with message 'msg', inserting the name of the
|
||||
** local variable at position 'level' in the stack.
|
||||
*/
|
||||
static void varerror (lua_State *L, StkId level, const char *msg) {
|
||||
int idx = cast_int(level - L->ci->func);
|
||||
const char *vname = luaG_findlocal(L, L->ci, idx, NULL);
|
||||
if (vname == NULL) vname = "?";
|
||||
luaG_runerror(L, msg, vname);
|
||||
}
|
||||
|
||||
|
||||
/*
|
||||
** Prepare and call a closing method. If status is OK, code is still
|
||||
** inside the original protected call, and so any error will be handled
|
||||
** there. Otherwise, a previous error already activated original
|
||||
** there. Otherwise, a previous error already activated the original
|
||||
** protected call, and so the call to the closing method must be
|
||||
** protected here. (A status = CLOSEPROTECT behaves like a previous
|
||||
** protected here. (A status == CLOSEPROTECT behaves like a previous
|
||||
** error, to also run the closing method in protected mode).
|
||||
** If status is OK, the call to the closing method will be pushed
|
||||
** at the top of the stack. Otherwise, values are pushed after
|
||||
** the 'level' of the upvalue being closed, as everything after
|
||||
** that won't be used again.
|
||||
*/
|
||||
static int callclosemth (lua_State *L, TValue *uv, StkId level, int status) {
|
||||
static int callclosemth (lua_State *L, StkId level, int status) {
|
||||
TValue *uv = s2v(level); /* value being closed */
|
||||
if (likely(status == LUA_OK)) {
|
||||
if (prepclosingmethod(L, uv, &G(L)->nilvalue)) /* something to call? */
|
||||
callclose(L, NULL); /* call closing method */
|
||||
else if (!ttisnil(uv)) { /* non-closable non-nil value? */
|
||||
int idx = cast_int(level - L->ci->func);
|
||||
const char *vname = luaG_findlocal(L, L->ci, idx, NULL);
|
||||
if (vname == NULL) vname = "?";
|
||||
luaG_runerror(L, "attempt to close non-closable variable '%s'", vname);
|
||||
}
|
||||
else if (!l_isfalse(uv)) /* non-closable non-false value? */
|
||||
varerror(L, level, "attempt to close non-closable variable '%s'");
|
||||
}
|
||||
else { /* must close the object in protected mode */
|
||||
ptrdiff_t oldtop = savestack(L, level + 1);
|
||||
/* save error message and set stack top to 'level + 1' */
|
||||
luaD_seterrorobj(L, status, level);
|
||||
ptrdiff_t oldtop;
|
||||
level++; /* space for error message */
|
||||
oldtop = savestack(L, level + 1); /* top will be after that */
|
||||
luaD_seterrorobj(L, status, level); /* set error message */
|
||||
if (prepclosingmethod(L, uv, s2v(level))) { /* something to call? */
|
||||
int newstatus = luaD_pcall(L, callclose, NULL, oldtop, 0);
|
||||
if (newstatus != LUA_OK && status == CLOSEPROTECT) /* first error? */
|
||||
status = newstatus; /* this will be the new error */
|
||||
else /* leave original error (or nil) on top */
|
||||
else {
|
||||
if (newstatus != LUA_OK) /* supressed error? */
|
||||
luaE_warnerror(L, "__close metamethod");
|
||||
/* leave original error (or nil) on top */
|
||||
L->top = restorestack(L, oldtop);
|
||||
}
|
||||
}
|
||||
/* else no metamethod; ignore this case and keep original error */
|
||||
}
|
||||
@@ -166,9 +182,7 @@ static int callclosemth (lua_State *L, TValue *uv, StkId level, int status) {
|
||||
** (can raise a memory-allocation error)
|
||||
*/
|
||||
static void trynewtbcupval (lua_State *L, void *ud) {
|
||||
StkId level = cast(StkId, ud);
|
||||
lua_assert(L->openupval == NULL || uplevel(L->openupval) < level);
|
||||
newupval(L, LUA_TUPVALTBC, level, &L->openupval);
|
||||
newupval(L, 1, cast(StkId, ud), &L->openupval);
|
||||
}
|
||||
|
||||
|
||||
@@ -178,13 +192,22 @@ static void trynewtbcupval (lua_State *L, void *ud) {
|
||||
** as there is no upvalue to call it later.
|
||||
*/
|
||||
void luaF_newtbcupval (lua_State *L, StkId level) {
|
||||
int status = luaD_rawrunprotected(L, trynewtbcupval, level);
|
||||
if (unlikely(status != LUA_OK)) { /* memory error creating upvalue? */
|
||||
lua_assert(status == LUA_ERRMEM);
|
||||
luaD_seterrorobj(L, LUA_ERRMEM, level + 1); /* save error message */
|
||||
if (prepclosingmethod(L, s2v(level), s2v(level + 1)))
|
||||
TValue *obj = s2v(level);
|
||||
lua_assert(L->openupval == NULL || uplevel(L->openupval) < level);
|
||||
if (!l_isfalse(obj)) { /* false doesn't need to be closed */
|
||||
int status;
|
||||
const TValue *tm = luaT_gettmbyobj(L, obj, TM_CLOSE);
|
||||
if (ttisnil(tm)) /* no metamethod? */
|
||||
varerror(L, level, "variable '%s' got a non-closable value");
|
||||
status = luaD_rawrunprotected(L, trynewtbcupval, level);
|
||||
if (unlikely(status != LUA_OK)) { /* memory error creating upvalue? */
|
||||
lua_assert(status == LUA_ERRMEM);
|
||||
luaD_seterrorobj(L, LUA_ERRMEM, level + 1); /* save error message */
|
||||
/* next call must succeed, as object is closable */
|
||||
prepclosingmethod(L, s2v(level), s2v(level + 1));
|
||||
callclose(L, NULL); /* call closing method */
|
||||
luaD_throw(L, LUA_ERRMEM); /* throw memory error */
|
||||
luaD_throw(L, LUA_ERRMEM); /* throw memory error */
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -200,20 +223,20 @@ void luaF_unlinkupval (UpVal *uv) {
|
||||
int luaF_close (lua_State *L, StkId level, int status) {
|
||||
UpVal *uv;
|
||||
while ((uv = L->openupval) != NULL && uplevel(uv) >= level) {
|
||||
StkId upl = uplevel(uv);
|
||||
TValue *slot = &uv->u.value; /* new position for value */
|
||||
lua_assert(uplevel(uv) < L->top);
|
||||
if (uv->tbc && status != NOCLOSINGMETH) {
|
||||
/* must run closing method, which may change the stack */
|
||||
ptrdiff_t levelrel = savestack(L, level);
|
||||
status = callclosemth(L, uplevel(uv), status);
|
||||
level = restorestack(L, levelrel);
|
||||
}
|
||||
luaF_unlinkupval(uv);
|
||||
setobj(L, slot, uv->v); /* move value to upvalue slot */
|
||||
uv->v = slot; /* now current value lives here */
|
||||
if (!iswhite(uv))
|
||||
gray2black(uv); /* closed upvalues cannot be gray */
|
||||
luaC_barrier(L, uv, slot);
|
||||
if (uv->tt == LUA_TUPVALTBC && status != NOCLOSINGMETH) {
|
||||
/* must run closing method */
|
||||
ptrdiff_t levelrel = savestack(L, level);
|
||||
status = callclosemth(L, uv->v, upl, status); /* may change the stack */
|
||||
level = restorestack(L, levelrel);
|
||||
}
|
||||
}
|
||||
return status;
|
||||
}
|
||||
|
||||
@@ -273,8 +273,7 @@ static void reallymarkobject (global_State *g, GCObject *o) {
|
||||
gray2black(o);
|
||||
break;
|
||||
}
|
||||
case LUA_TUPVAL:
|
||||
case LUA_TUPVALTBC: {
|
||||
case LUA_TUPVAL: {
|
||||
UpVal *uv = gco2upv(o);
|
||||
if (!upisopen(uv)) /* open upvalues are kept gray */
|
||||
gray2black(o);
|
||||
@@ -414,13 +413,13 @@ static void traverseweakvalue (global_State *g, Table *h) {
|
||||
** (in the atomic phase). In generational mode, it (like all visited
|
||||
** tables) must be kept in some gray list for post-processing.
|
||||
*/
|
||||
static int traverseephemeron (global_State *g, Table *h) {
|
||||
static int traverseephemeron (global_State *g, Table *h, int inv) {
|
||||
int marked = 0; /* true if an object is marked in this traversal */
|
||||
int hasclears = 0; /* true if table has white keys */
|
||||
int hasww = 0; /* true if table has entry "white-key -> white-value" */
|
||||
Node *n, *limit = gnodelast(h);
|
||||
unsigned int i;
|
||||
unsigned int asize = luaH_realasize(h);
|
||||
unsigned int nsize = sizenode(h);
|
||||
/* traverse array part */
|
||||
for (i = 0; i < asize; i++) {
|
||||
if (valiswhite(&h->array[i])) {
|
||||
@@ -428,8 +427,10 @@ static int traverseephemeron (global_State *g, Table *h) {
|
||||
reallymarkobject(g, gcvalue(&h->array[i]));
|
||||
}
|
||||
}
|
||||
/* traverse hash part */
|
||||
for (n = gnode(h, 0); n < limit; n++) {
|
||||
/* traverse hash part; if 'inv', traverse descending
|
||||
(see 'convergeephemerons') */
|
||||
for (i = 0; i < nsize; i++) {
|
||||
Node *n = inv ? gnode(h, nsize - 1 - i) : gnode(h, i);
|
||||
if (isempty(gval(n))) /* entry is empty? */
|
||||
clearkey(n); /* clear its key */
|
||||
else if (iscleared(g, gckeyN(n))) { /* key is not marked (yet)? */
|
||||
@@ -491,7 +492,7 @@ static lu_mem traversetable (global_State *g, Table *h) {
|
||||
if (!weakkey) /* strong keys? */
|
||||
traverseweakvalue(g, h);
|
||||
else if (!weakvalue) /* strong values? */
|
||||
traverseephemeron(g, h);
|
||||
traverseephemeron(g, h, 0);
|
||||
else /* all weak */
|
||||
linkgclist(h, g->allweak); /* nothing to traverse now */
|
||||
}
|
||||
@@ -570,10 +571,8 @@ static int traversethread (global_State *g, lua_State *th) {
|
||||
th->openupval == NULL || isintwups(th));
|
||||
for (; o < th->top; o++) /* mark live elements in the stack */
|
||||
markvalue(g, s2v(o));
|
||||
for (uv = th->openupval; uv != NULL; uv = uv->u.open.next) {
|
||||
if (uv->tt == LUA_TUPVALTBC) /* to be closed? */
|
||||
markobject(g, uv); /* cannot be collected */
|
||||
}
|
||||
for (uv = th->openupval; uv != NULL; uv = uv->u.open.next)
|
||||
markobject(g, uv); /* open upvalues cannot be collected */
|
||||
if (g->gcstate == GCSatomic) { /* final traversal? */
|
||||
StkId lim = th->stack + th->stacksize; /* real end of stack */
|
||||
for (; o < lim; o++) /* clear not-marked stack slice */
|
||||
@@ -623,21 +622,30 @@ static lu_mem propagateall (global_State *g) {
|
||||
}
|
||||
|
||||
|
||||
/*
|
||||
** Traverse all ephemeron tables propagating marks from keys to values.
|
||||
** Repeat until it converges, that is, nothing new is marked. 'dir'
|
||||
** inverts the direction of the traversals, trying to speed up
|
||||
** convergence on chains in the same table.
|
||||
**
|
||||
*/
|
||||
static void convergeephemerons (global_State *g) {
|
||||
int changed;
|
||||
int dir = 0;
|
||||
do {
|
||||
GCObject *w;
|
||||
GCObject *next = g->ephemeron; /* get ephemeron list */
|
||||
g->ephemeron = NULL; /* tables may return to this list when traversed */
|
||||
changed = 0;
|
||||
while ((w = next) != NULL) {
|
||||
next = gco2t(w)->gclist;
|
||||
if (traverseephemeron(g, gco2t(w))) { /* traverse marked some value? */
|
||||
while ((w = next) != NULL) { /* for each ephemeron table */
|
||||
next = gco2t(w)->gclist; /* list is rebuilt during loop */
|
||||
if (traverseephemeron(g, gco2t(w), dir)) { /* marked some value? */
|
||||
propagateall(g); /* propagate changes */
|
||||
changed = 1; /* will have to revisit all ephemeron tables */
|
||||
}
|
||||
}
|
||||
} while (changed);
|
||||
dir = !dir; /* invert direction next time */
|
||||
} while (changed); /* repeat until no more changes */
|
||||
}
|
||||
|
||||
/* }====================================================== */
|
||||
@@ -706,7 +714,6 @@ static void freeobj (lua_State *L, GCObject *o) {
|
||||
luaF_freeproto(L, gco2p(o));
|
||||
break;
|
||||
case LUA_TUPVAL:
|
||||
case LUA_TUPVALTBC:
|
||||
freeupval(L, gco2upv(o));
|
||||
break;
|
||||
case LUA_TLCL:
|
||||
@@ -794,10 +801,11 @@ static GCObject **sweeptolive (lua_State *L, GCObject **p) {
|
||||
*/
|
||||
static void checkSizes (lua_State *L, global_State *g) {
|
||||
if (!g->gcemergency) {
|
||||
l_mem olddebt = g->GCdebt;
|
||||
if (g->strt.nuse < g->strt.size / 4) /* string table too big? */
|
||||
if (g->strt.nuse < g->strt.size / 4) { /* string table too big? */
|
||||
l_mem olddebt = g->GCdebt;
|
||||
luaS_resize(L, g->strt.size / 2);
|
||||
g->GCestimate += g->GCdebt - olddebt; /* correct estimate */
|
||||
g->GCestimate += g->GCdebt - olddebt; /* correct estimate */
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -838,21 +846,16 @@ static void GCTM (lua_State *L) {
|
||||
int running = g->gcrunning;
|
||||
L->allowhook = 0; /* stop debug hooks during GC metamethod */
|
||||
g->gcrunning = 0; /* avoid GC steps */
|
||||
setobj2s(L, L->top, tm); /* push finalizer... */
|
||||
setobj2s(L, L->top + 1, &v); /* ... and its argument */
|
||||
L->top += 2; /* and (next line) call the finalizer */
|
||||
setobj2s(L, L->top++, tm); /* push finalizer... */
|
||||
setobj2s(L, L->top++, &v); /* ... and its argument */
|
||||
L->ci->callstatus |= CIST_FIN; /* will run a finalizer */
|
||||
status = luaD_pcall(L, dothecall, NULL, savestack(L, L->top - 2), 0);
|
||||
L->ci->callstatus &= ~CIST_FIN; /* not running a finalizer anymore */
|
||||
L->allowhook = oldah; /* restore hooks */
|
||||
g->gcrunning = running; /* restore state */
|
||||
if (status != LUA_OK) { /* error while running __gc? */
|
||||
const char *msg = (ttisstring(s2v(L->top - 1)))
|
||||
? svalue(s2v(L->top - 1))
|
||||
: "error object is not a string";
|
||||
luaE_warning(L, "error in __gc metamethod (", 1);
|
||||
luaE_warning(L, msg, 1);
|
||||
luaE_warning(L, ")", 0);
|
||||
if (unlikely(status != LUA_OK)) { /* error while running __gc? */
|
||||
luaE_warnerror(L, "__gc metamethod");
|
||||
L->top--; /* pops error object */
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -995,7 +998,7 @@ static void sweep2old (lua_State *L, GCObject **p) {
|
||||
*/
|
||||
static GCObject **sweepgen (lua_State *L, global_State *g, GCObject **p,
|
||||
GCObject *limit) {
|
||||
static lu_byte nextage[] = {
|
||||
static const lu_byte nextage[] = {
|
||||
G_SURVIVAL, /* from G_NEW */
|
||||
G_OLD1, /* from G_SURVIVAL */
|
||||
G_OLD1, /* from G_OLD0 */
|
||||
@@ -1250,7 +1253,7 @@ static void setminordebt (global_State *g) {
|
||||
/*
|
||||
** Does a major collection after last collection was a "bad collection".
|
||||
**
|
||||
** When the program is building a big struture, it allocates lots of
|
||||
** When the program is building a big structure, it allocates lots of
|
||||
** memory but generates very little garbage. In those scenarios,
|
||||
** the generational mode just wastes time doing small collections, and
|
||||
** major collections are frequently what we call a "bad collection", a
|
||||
|
||||
@@ -153,7 +153,7 @@ static int io_type (lua_State *L) {
|
||||
luaL_checkany(L, 1);
|
||||
p = (LStream *)luaL_testudata(L, 1, LUA_FILEHANDLE);
|
||||
if (p == NULL)
|
||||
lua_pushnil(L); /* not a file */
|
||||
luaL_pushfail(L); /* not a file */
|
||||
else if (isclosed(p))
|
||||
lua_pushliteral(L, "closed file");
|
||||
else
|
||||
@@ -338,7 +338,7 @@ static int io_readline (lua_State *L);
|
||||
#define MAXARGLINE 250
|
||||
|
||||
/*
|
||||
** Auxiliar function to create the iteration function for 'lines'.
|
||||
** Auxiliary function to create the iteration function for 'lines'.
|
||||
** The iteration function is a closure over 'io_readline', with
|
||||
** the following upvalues:
|
||||
** 1) The file being read (first value in the stack)
|
||||
@@ -593,7 +593,7 @@ static int g_read (lua_State *L, FILE *f, int first) {
|
||||
return luaL_fileresult(L, 0, NULL);
|
||||
if (!success) {
|
||||
lua_pop(L, 1); /* remove last result */
|
||||
lua_pushnil(L); /* push nil instead */
|
||||
luaL_pushfail(L); /* push nil instead */
|
||||
}
|
||||
return n - first;
|
||||
}
|
||||
@@ -626,7 +626,7 @@ static int io_readline (lua_State *L) {
|
||||
lua_assert(n > 0); /* should return at least a nil */
|
||||
if (lua_toboolean(L, -n)) /* read at least one value? */
|
||||
return n; /* return them */
|
||||
else { /* first result is nil: EOF or error */
|
||||
else { /* first result is false: EOF or error */
|
||||
if (n > 1) { /* is there error information? */
|
||||
/* 2nd result is error message */
|
||||
return luaL_error(L, "%s", lua_tostring(L, -n + 1));
|
||||
@@ -742,14 +742,23 @@ static const luaL_Reg iolib[] = {
|
||||
/*
|
||||
** methods for file handles
|
||||
*/
|
||||
static const luaL_Reg flib[] = {
|
||||
{"close", f_close},
|
||||
{"flush", f_flush},
|
||||
{"lines", f_lines},
|
||||
static const luaL_Reg meth[] = {
|
||||
{"read", f_read},
|
||||
{"seek", f_seek},
|
||||
{"setvbuf", f_setvbuf},
|
||||
{"write", f_write},
|
||||
{"lines", f_lines},
|
||||
{"flush", f_flush},
|
||||
{"seek", f_seek},
|
||||
{"close", f_close},
|
||||
{"setvbuf", f_setvbuf},
|
||||
{NULL, NULL}
|
||||
};
|
||||
|
||||
|
||||
/*
|
||||
** metamethods for file handles
|
||||
*/
|
||||
static const luaL_Reg metameth[] = {
|
||||
{"__index", NULL}, /* place holder */
|
||||
{"__gc", f_gc},
|
||||
{"__close", f_gc},
|
||||
{"__tostring", f_tostring},
|
||||
@@ -758,11 +767,12 @@ static const luaL_Reg flib[] = {
|
||||
|
||||
|
||||
static void createmeta (lua_State *L) {
|
||||
luaL_newmetatable(L, LUA_FILEHANDLE); /* create metatable for file handles */
|
||||
lua_pushvalue(L, -1); /* push metatable */
|
||||
lua_setfield(L, -2, "__index"); /* metatable.__index = metatable */
|
||||
luaL_setfuncs(L, flib, 0); /* add file methods to new metatable */
|
||||
lua_pop(L, 1); /* pop new metatable */
|
||||
luaL_newmetatable(L, LUA_FILEHANDLE); /* metatable for file handles */
|
||||
luaL_setfuncs(L, metameth, 0); /* add metamethods to new metatable */
|
||||
luaL_newlibtable(L, meth); /* create method table */
|
||||
luaL_setfuncs(L, meth, 0); /* add file methods to method table */
|
||||
lua_setfield(L, -2, "__index"); /* metatable.__index = method table */
|
||||
lua_pop(L, 1); /* pop metatable */
|
||||
}
|
||||
|
||||
|
||||
@@ -772,7 +782,7 @@ static void createmeta (lua_State *L) {
|
||||
static int io_noclose (lua_State *L) {
|
||||
LStream *p = tolstream(L);
|
||||
p->closef = &io_noclose; /* keep file opened */
|
||||
lua_pushnil(L);
|
||||
luaL_pushfail(L);
|
||||
lua_pushliteral(L, "cannot close standard file");
|
||||
return 2;
|
||||
}
|
||||
|
||||
+3
-6
@@ -45,12 +45,6 @@ static void *disptab[NUM_OPCODES] = {
|
||||
&&L_OP_NEWTABLE,
|
||||
&&L_OP_SELF,
|
||||
&&L_OP_ADDI,
|
||||
&&L_OP_SUBI,
|
||||
&&L_OP_MULI,
|
||||
&&L_OP_MODI,
|
||||
&&L_OP_POWI,
|
||||
&&L_OP_DIVI,
|
||||
&&L_OP_IDIVI,
|
||||
&&L_OP_ADDK,
|
||||
&&L_OP_SUBK,
|
||||
&&L_OP_MULK,
|
||||
@@ -75,6 +69,9 @@ static void *disptab[NUM_OPCODES] = {
|
||||
&&L_OP_BXOR,
|
||||
&&L_OP_SHL,
|
||||
&&L_OP_SHR,
|
||||
&&L_OP_MMBIN,
|
||||
&&L_OP_MMBINI,
|
||||
&&L_OP_MMBINK,
|
||||
&&L_OP_UNM,
|
||||
&&L_OP_BNOT,
|
||||
&&L_OP_NOT,
|
||||
|
||||
@@ -211,8 +211,16 @@ static int check_next2 (LexState *ls, const char *set) {
|
||||
|
||||
/* LUA_NUMBER */
|
||||
/*
|
||||
** this function is quite liberal in what it accepts, as 'luaO_str2num'
|
||||
** will reject ill-formed numerals.
|
||||
** This function is quite liberal in what it accepts, as 'luaO_str2num'
|
||||
** will reject ill-formed numerals. Roughly, it accepts the following
|
||||
** pattern:
|
||||
**
|
||||
** %d(%x|%.|([Ee][+-]?))* | 0[Xx](%x|%.|([Pp][+-]?))*
|
||||
**
|
||||
** The only tricky part is to accept [+-] only after a valid exponent
|
||||
** mark, to avoid reading '3-4' or '0xe+1' as a single number.
|
||||
**
|
||||
** The caller might have already read an initial dot.
|
||||
*/
|
||||
static int read_numeral (LexState *ls, SemInfo *seminfo) {
|
||||
TValue obj;
|
||||
@@ -223,15 +231,13 @@ static int read_numeral (LexState *ls, SemInfo *seminfo) {
|
||||
if (first == '0' && check_next2(ls, "xX")) /* hexadecimal? */
|
||||
expo = "Pp";
|
||||
for (;;) {
|
||||
if (check_next2(ls, expo)) /* exponent part? */
|
||||
if (check_next2(ls, expo)) /* exponent mark? */
|
||||
check_next2(ls, "-+"); /* optional exponent sign */
|
||||
if (lisxdigit(ls->current))
|
||||
save_and_next(ls);
|
||||
else if (ls->current == '.')
|
||||
else if (lisxdigit(ls->current) || ls->current == '.') /* '%x|%.' */
|
||||
save_and_next(ls);
|
||||
else break;
|
||||
}
|
||||
if (lislalnum(ls->current)) /* is numeral touching an alpha num? */
|
||||
if (lislalpha(ls->current)) /* is numeral touching a letter? */
|
||||
save_and_next(ls); /* force an error */
|
||||
save(ls, '\0');
|
||||
if (luaO_str2num(luaZ_buffer(ls->buff), &obj) == 0) /* format error? */
|
||||
|
||||
+12
-7
@@ -77,7 +77,7 @@ static int math_toint (lua_State *L) {
|
||||
lua_pushinteger(L, n);
|
||||
else {
|
||||
luaL_checkany(L, 1);
|
||||
lua_pushnil(L); /* value is not convertible to integer */
|
||||
luaL_pushfail(L); /* value is not convertible to integer */
|
||||
}
|
||||
return 1;
|
||||
}
|
||||
@@ -235,7 +235,7 @@ static int math_type (lua_State *L) {
|
||||
lua_pushstring(L, (lua_isinteger(L, 1)) ? "integer" : "float");
|
||||
else {
|
||||
luaL_checkany(L, 1);
|
||||
lua_pushnil(L);
|
||||
luaL_pushfail(L);
|
||||
}
|
||||
return 1;
|
||||
}
|
||||
@@ -586,7 +586,8 @@ static int math_random (lua_State *L) {
|
||||
}
|
||||
|
||||
|
||||
static void setseed (Rand64 *state, lua_Unsigned n1, lua_Unsigned n2) {
|
||||
static void setseed (lua_State *L, Rand64 *state,
|
||||
lua_Unsigned n1, lua_Unsigned n2) {
|
||||
int i;
|
||||
state[0] = Int2I(n1);
|
||||
state[1] = Int2I(0xff); /* avoid a zero state */
|
||||
@@ -594,6 +595,8 @@ static void setseed (Rand64 *state, lua_Unsigned n1, lua_Unsigned n2) {
|
||||
state[3] = Int2I(0);
|
||||
for (i = 0; i < 16; i++)
|
||||
nextrand(state); /* discard initial values to "spread" seed */
|
||||
lua_pushinteger(L, n1);
|
||||
lua_pushinteger(L, n2);
|
||||
}
|
||||
|
||||
|
||||
@@ -605,20 +608,21 @@ static void setseed (Rand64 *state, lua_Unsigned n1, lua_Unsigned n2) {
|
||||
static void randseed (lua_State *L, RanState *state) {
|
||||
lua_Unsigned seed1 = (lua_Unsigned)time(NULL);
|
||||
lua_Unsigned seed2 = (lua_Unsigned)(size_t)L;
|
||||
setseed(state->s, seed1, seed2);
|
||||
setseed(L, state->s, seed1, seed2);
|
||||
}
|
||||
|
||||
|
||||
static int math_randomseed (lua_State *L) {
|
||||
RanState *state = (RanState *)lua_touserdata(L, lua_upvalueindex(1));
|
||||
if (lua_isnone(L, 1))
|
||||
if (lua_isnone(L, 1)) {
|
||||
randseed(L, state);
|
||||
}
|
||||
else {
|
||||
lua_Integer n1 = luaL_checkinteger(L, 1);
|
||||
lua_Integer n2 = luaL_optinteger(L, 2, 0);
|
||||
setseed(state->s, n1, n2);
|
||||
setseed(L, state->s, n1, n2);
|
||||
}
|
||||
return 0;
|
||||
return 2; /* return seeds */
|
||||
}
|
||||
|
||||
|
||||
@@ -635,6 +639,7 @@ static const luaL_Reg randfuncs[] = {
|
||||
static void setrandfunc (lua_State *L) {
|
||||
RanState *state = (RanState *)lua_newuserdatauv(L, sizeof(RanState), 0);
|
||||
randseed(L, state); /* initialize with a "random" seed */
|
||||
lua_pop(L, 2); /* remove pushed seeds */
|
||||
luaL_setfuncs(L, randfuncs, 1);
|
||||
}
|
||||
|
||||
|
||||
@@ -23,33 +23,56 @@
|
||||
|
||||
|
||||
#if defined(HARDMEMTESTS)
|
||||
#define hardtest(L,os,s) /* force a GC whenever possible */ \
|
||||
if ((s) > (os) && (G(L))->gcrunning) luaC_fullgc(L, 1);
|
||||
/*
|
||||
** First allocation will fail whenever not building initial state
|
||||
** and not shrinking a block. (This fail will trigger 'tryagain' and
|
||||
** a full GC cycle at every alocation.)
|
||||
*/
|
||||
static void *firsttry (global_State *g, void *block, size_t os, size_t ns) {
|
||||
if (ttisnil(&g->nilvalue) && ns > os)
|
||||
return NULL; /* fail */
|
||||
else /* normal allocation */
|
||||
return (*g->frealloc)(g->ud, block, os, ns);
|
||||
}
|
||||
#else
|
||||
#define hardtest(L,os,s) ((void)0)
|
||||
#define firsttry(g,block,os,ns) ((*g->frealloc)(g->ud, block, os, ns))
|
||||
#endif
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
/*
|
||||
** About the realloc function:
|
||||
** void * frealloc (void *ud, void *ptr, size_t osize, size_t nsize);
|
||||
** void *frealloc (void *ud, void *ptr, size_t osize, size_t nsize);
|
||||
** ('osize' is the old size, 'nsize' is the new size)
|
||||
**
|
||||
** * frealloc(ud, NULL, x, s) creates a new block of size 's' (no
|
||||
** matter 'x').
|
||||
** - frealloc(ud, p, x, 0) frees the block 'p' and returns NULL.
|
||||
** Particularly, frealloc(ud, NULL, 0, 0) does nothing,
|
||||
** which is equivalent to free(NULL) in ISO C.
|
||||
**
|
||||
** * frealloc(ud, p, x, 0) frees the block 'p'
|
||||
** (in this specific case, frealloc must return NULL);
|
||||
** particularly, frealloc(ud, NULL, 0, 0) does nothing
|
||||
** (which is equivalent to free(NULL) in ISO C)
|
||||
** - frealloc(ud, NULL, x, s) creates a new block of size 's'
|
||||
** (no matter 'x'). Returns NULL if it cannot create the new block.
|
||||
**
|
||||
** frealloc returns NULL if it cannot create or reallocate the area
|
||||
** (any reallocation to an equal or smaller size cannot fail!)
|
||||
** - otherwise, frealloc(ud, b, x, y) reallocates the block 'b' from
|
||||
** size 'x' to size 'y'. Returns NULL if it cannot reallocate the
|
||||
** block to the new size.
|
||||
*/
|
||||
|
||||
|
||||
|
||||
|
||||
/*
|
||||
** {==================================================================
|
||||
** Functions to allocate/deallocate arrays for the Parser
|
||||
** ===================================================================
|
||||
*/
|
||||
|
||||
/*
|
||||
** Minimum size for arrays during parsing, to avoid overhead of
|
||||
** reallocating to size 1, then 2, and then 4. All these arrays
|
||||
** will be reallocated to exact sizes or erased when parsing ends.
|
||||
*/
|
||||
#define MINSIZEARRAY 4
|
||||
|
||||
|
||||
@@ -71,32 +94,32 @@ void *luaM_growaux_ (lua_State *L, void *block, int nelems, int *psize,
|
||||
}
|
||||
lua_assert(nelems + 1 <= size && size <= limit);
|
||||
/* 'limit' ensures that multiplication will not overflow */
|
||||
newblock = luaM_realloc_(L, block, cast_sizet(*psize) * size_elems,
|
||||
cast_sizet(size) * size_elems);
|
||||
if (unlikely(newblock == NULL))
|
||||
luaM_error(L);
|
||||
newblock = luaM_saferealloc_(L, block, cast_sizet(*psize) * size_elems,
|
||||
cast_sizet(size) * size_elems);
|
||||
*psize = size; /* update only when everything else is OK */
|
||||
return newblock;
|
||||
}
|
||||
|
||||
|
||||
/*
|
||||
** In prototypes, the size of the array is also its number of
|
||||
** elements (to save memory). So, if it cannot shrink an array
|
||||
** to its number of elements, the only option is to raise an
|
||||
** error.
|
||||
*/
|
||||
void *luaM_shrinkvector_ (lua_State *L, void *block, int *size,
|
||||
int final_n, int size_elem) {
|
||||
global_State *g = G(L);
|
||||
void *newblock;
|
||||
size_t oldsize = cast_sizet((*size) * size_elem);
|
||||
size_t newsize = cast_sizet(final_n * size_elem);
|
||||
lua_assert(newsize <= oldsize);
|
||||
newblock = (*g->frealloc)(g->ud, block, oldsize, newsize);
|
||||
if (unlikely(newblock == NULL && final_n > 0)) /* allocation failed? */
|
||||
luaM_error(L);
|
||||
else {
|
||||
g->GCdebt += newsize - oldsize;
|
||||
*size = final_n;
|
||||
return newblock;
|
||||
}
|
||||
newblock = luaM_saferealloc_(L, block, oldsize, newsize);
|
||||
*size = final_n;
|
||||
return newblock;
|
||||
}
|
||||
|
||||
/* }================================================================== */
|
||||
|
||||
|
||||
l_noret luaM_toobig (lua_State *L) {
|
||||
luaG_runerror(L, "memory allocation error: block too big");
|
||||
@@ -132,19 +155,20 @@ static void *tryagain (lua_State *L, void *block,
|
||||
|
||||
|
||||
/*
|
||||
** generic allocation routine.
|
||||
** Generic allocation routine.
|
||||
** If allocation fails while shrinking a block, do not try again; the
|
||||
** GC shrinks some blocks and it is not reentrant.
|
||||
*/
|
||||
void *luaM_realloc_ (lua_State *L, void *block, size_t osize, size_t nsize) {
|
||||
void *newblock;
|
||||
global_State *g = G(L);
|
||||
lua_assert((osize == 0) == (block == NULL));
|
||||
hardtest(L, osize, nsize);
|
||||
newblock = (*g->frealloc)(g->ud, block, osize, nsize);
|
||||
newblock = firsttry(g, block, osize, nsize);
|
||||
if (unlikely(newblock == NULL && nsize > 0)) {
|
||||
if (nsize > osize) /* not shrinking a block? */
|
||||
newblock = tryagain(L, block, osize, nsize);
|
||||
if (newblock == NULL) /* still no memory? */
|
||||
return NULL;
|
||||
return NULL; /* do not update 'GCdebt' */
|
||||
}
|
||||
lua_assert((nsize == 0) == (newblock == NULL));
|
||||
g->GCdebt = (g->GCdebt + nsize) - osize;
|
||||
@@ -162,12 +186,11 @@ void *luaM_saferealloc_ (lua_State *L, void *block, size_t osize,
|
||||
|
||||
|
||||
void *luaM_malloc_ (lua_State *L, size_t size, int tag) {
|
||||
hardtest(L, 0, size);
|
||||
if (size == 0)
|
||||
return NULL; /* that's all */
|
||||
else {
|
||||
global_State *g = G(L);
|
||||
void *newblock = (*g->frealloc)(g->ud, NULL, tag, size);
|
||||
void *newblock = firsttry(g, NULL, tag, size);
|
||||
if (unlikely(newblock == NULL)) {
|
||||
newblock = tryagain(L, NULL, tag, size);
|
||||
if (newblock == NULL)
|
||||
|
||||
@@ -56,10 +56,10 @@
|
||||
|
||||
|
||||
/*
|
||||
** unique key for table in the registry that keeps handles
|
||||
** key for table in the registry that keeps handles
|
||||
** for all loaded C libraries
|
||||
*/
|
||||
static const int CLIBS = 0;
|
||||
static const char *const CLIBS = "_CLIBS";
|
||||
|
||||
#define LIB_FAIL "open"
|
||||
|
||||
@@ -308,7 +308,7 @@ static void setpath (lua_State *L, const char *fieldname,
|
||||
luaL_addchar(&b, *LUA_PATH_SEP);
|
||||
}
|
||||
luaL_addstring(&b, dft); /* add default */
|
||||
if (dftmark < path + len - 2) { /* is there a sufix after ';;'? */
|
||||
if (dftmark < path + len - 2) { /* is there a suffix after ';;'? */
|
||||
luaL_addchar(&b, *LUA_PATH_SEP);
|
||||
luaL_addlstring(&b, dftmark + 2, (path + len - 2) - dftmark);
|
||||
}
|
||||
@@ -327,7 +327,7 @@ static void setpath (lua_State *L, const char *fieldname,
|
||||
*/
|
||||
static void *checkclib (lua_State *L, const char *path) {
|
||||
void *plib;
|
||||
lua_rawgetp(L, LUA_REGISTRYINDEX, &CLIBS);
|
||||
lua_getfield(L, LUA_REGISTRYINDEX, CLIBS);
|
||||
lua_getfield(L, -1, path);
|
||||
plib = lua_touserdata(L, -1); /* plib = CLIBS[path] */
|
||||
lua_pop(L, 2); /* pop CLIBS table and 'plib' */
|
||||
@@ -340,7 +340,7 @@ static void *checkclib (lua_State *L, const char *path) {
|
||||
** registry.CLIBS[#CLIBS + 1] = plib -- also keep a list of all libraries
|
||||
*/
|
||||
static void addtoclib (lua_State *L, const char *path, void *plib) {
|
||||
lua_rawgetp(L, LUA_REGISTRYINDEX, &CLIBS);
|
||||
lua_getfield(L, LUA_REGISTRYINDEX, CLIBS);
|
||||
lua_pushlightuserdata(L, plib);
|
||||
lua_pushvalue(L, -1);
|
||||
lua_setfield(L, -3, path); /* CLIBS[path] = plib */
|
||||
@@ -408,10 +408,10 @@ static int ll_loadlib (lua_State *L) {
|
||||
if (stat == 0) /* no errors? */
|
||||
return 1; /* return the loaded function */
|
||||
else { /* error; error message is on stack top */
|
||||
lua_pushnil(L);
|
||||
luaL_pushfail(L);
|
||||
lua_insert(L, -2);
|
||||
lua_pushstring(L, (stat == ERRLIB) ? LIB_FAIL : "init");
|
||||
return 3; /* return nil, error message, and where */
|
||||
return 3; /* return fail, error message, and where */
|
||||
}
|
||||
}
|
||||
|
||||
@@ -505,9 +505,9 @@ static int ll_searchpath (lua_State *L) {
|
||||
luaL_optstring(L, 4, LUA_DIRSEP));
|
||||
if (f != NULL) return 1;
|
||||
else { /* error message is on top of the stack */
|
||||
lua_pushnil(L);
|
||||
luaL_pushfail(L);
|
||||
lua_insert(L, -2);
|
||||
return 2; /* return nil + error message */
|
||||
return 2; /* return fail + error message */
|
||||
}
|
||||
}
|
||||
|
||||
@@ -716,12 +716,11 @@ static void createsearcherstable (lua_State *L) {
|
||||
** setting a finalizer to close all libraries when closing state.
|
||||
*/
|
||||
static void createclibstable (lua_State *L) {
|
||||
lua_newtable(L); /* create CLIBS table */
|
||||
luaL_getsubtable(L, LUA_REGISTRYINDEX, CLIBS); /* create CLIBS table */
|
||||
lua_createtable(L, 0, 1); /* create metatable for CLIBS */
|
||||
lua_pushcfunction(L, gctm);
|
||||
lua_setfield(L, -2, "__gc"); /* set finalizer for CLIBS table */
|
||||
lua_setmetatable(L, -2);
|
||||
lua_rawsetp(L, LUA_REGISTRYINDEX, &CLIBS); /* set CLIBS table in registry */
|
||||
}
|
||||
|
||||
|
||||
|
||||
@@ -29,32 +29,6 @@
|
||||
#include "lvm.h"
|
||||
|
||||
|
||||
/*
|
||||
** converts an integer to a "floating point byte", represented as
|
||||
** (eeeeexxx), where the real value is (1xxx) * 2^(eeeee - 1) if
|
||||
** eeeee != 0 and (xxx) otherwise.
|
||||
*/
|
||||
int luaO_int2fb (unsigned int x) {
|
||||
int e = 0; /* exponent */
|
||||
if (x < 8) return x;
|
||||
while (x >= (8 << 4)) { /* coarse steps */
|
||||
x = (x + 0xf) >> 4; /* x = ceil(x / 16) */
|
||||
e += 4;
|
||||
}
|
||||
while (x >= (8 << 1)) { /* fine steps */
|
||||
x = (x + 1) >> 1; /* x = ceil(x / 2) */
|
||||
e++;
|
||||
}
|
||||
return ((e+1) << 3) | (cast_int(x) - 8);
|
||||
}
|
||||
|
||||
|
||||
/* converts back */
|
||||
int luaO_fb2int (int x) {
|
||||
return (x < 8) ? x : ((x & 7) + 8) << ((x >> 3) - 1);
|
||||
}
|
||||
|
||||
|
||||
/*
|
||||
** Computes ceil(log2(x))
|
||||
*/
|
||||
|
||||
@@ -89,8 +89,8 @@ typedef struct TValue {
|
||||
#define righttt(obj) (ttypetag(obj) == gcvalue(obj)->tt)
|
||||
|
||||
#define checkliveness(L,obj) \
|
||||
lua_longassert(!iscollectable(obj) || \
|
||||
(righttt(obj) && (L == NULL || !isdead(G(L),gcvalue(obj)))))
|
||||
((void)L, lua_longassert(!iscollectable(obj) || \
|
||||
(righttt(obj) && (L == NULL || !isdead(G(L),gcvalue(obj))))))
|
||||
|
||||
|
||||
/* Macros to set values */
|
||||
@@ -100,7 +100,7 @@ typedef struct TValue {
|
||||
#define setobj(L,obj1,obj2) \
|
||||
{ TValue *io1=(obj1); const TValue *io2=(obj2); \
|
||||
io1->value_ = io2->value_; io1->tt_ = io2->tt_; \
|
||||
(void)L; checkliveness(L,io1); lua_assert(!isreallyempty(io1)); }
|
||||
checkliveness(L,io1); lua_assert(!isreallyempty(io1)); }
|
||||
|
||||
/*
|
||||
** different types of assignments, according to destination
|
||||
@@ -460,6 +460,7 @@ typedef struct Upvaldesc {
|
||||
TString *name; /* upvalue name (for debug information) */
|
||||
lu_byte instack; /* whether it is in stack (register) */
|
||||
lu_byte idx; /* index of upvalue (in stack or in outer function's list) */
|
||||
lu_byte kind; /* kind of corresponding variable */
|
||||
} Upvaldesc;
|
||||
|
||||
|
||||
@@ -567,6 +568,7 @@ typedef struct Proto {
|
||||
*/
|
||||
typedef struct UpVal {
|
||||
CommonHeader;
|
||||
lu_byte tbc; /* true if it represents a to-be-closed variable */
|
||||
TValue *v; /* points to stack or to its own value */
|
||||
union {
|
||||
struct { /* (when open) */
|
||||
@@ -578,9 +580,6 @@ typedef struct UpVal {
|
||||
} UpVal;
|
||||
|
||||
|
||||
/* variant for "To Be Closed" upvalues */
|
||||
#define LUA_TUPVALTBC (LUA_TUPVAL | (1 << 4))
|
||||
|
||||
|
||||
#define ClosureHeader \
|
||||
CommonHeader; lu_byte nupvalues; GCObject *gclist
|
||||
@@ -650,14 +649,14 @@ typedef union Node {
|
||||
#define setnodekey(L,node,obj) \
|
||||
{ Node *n_=(node); const TValue *io_=(obj); \
|
||||
n_->u.key_val = io_->value_; n_->u.key_tt = io_->tt_; \
|
||||
(void)L; checkliveness(L,io_); }
|
||||
checkliveness(L,io_); }
|
||||
|
||||
|
||||
/* copy a value from a key */
|
||||
#define getnodekey(L,obj,node) \
|
||||
{ TValue *io_=(obj); const Node *n_=(node); \
|
||||
io_->value_ = n_->u.key_val; io_->tt_ = n_->u.key_tt; \
|
||||
(void)L; checkliveness(L,io_); }
|
||||
checkliveness(L,io_); }
|
||||
|
||||
|
||||
/*
|
||||
@@ -733,8 +732,6 @@ typedef struct Table {
|
||||
/* size of buffer for 'luaO_utf8esc' function */
|
||||
#define UTF8BUFFSZ 8
|
||||
|
||||
LUAI_FUNC int luaO_int2fb (unsigned int x);
|
||||
LUAI_FUNC int luaO_fb2int (int x);
|
||||
LUAI_FUNC int luaO_utf8esc (char *buff, unsigned long x);
|
||||
LUAI_FUNC int luaO_ceillog2 (unsigned int x);
|
||||
LUAI_FUNC int luaO_rawarith (lua_State *L, int op, const TValue *p1,
|
||||
|
||||
+82
-85
@@ -18,90 +18,87 @@
|
||||
/* ORDER OP */
|
||||
|
||||
LUAI_DDEF const lu_byte luaP_opmodes[NUM_OPCODES] = {
|
||||
/* OT IT T A mode opcode */
|
||||
opmode(0, 0, 0, 1, iABC) /* OP_MOVE */
|
||||
,opmode(0, 0, 0, 1, iAsBx) /* OP_LOADI */
|
||||
,opmode(0, 0, 0, 1, iAsBx) /* OP_LOADF */
|
||||
,opmode(0, 0, 0, 1, iABx) /* OP_LOADK */
|
||||
,opmode(0, 0, 0, 1, iABx) /* OP_LOADKX */
|
||||
,opmode(0, 0, 0, 1, iABC) /* OP_LOADBOOL */
|
||||
,opmode(0, 0, 0, 1, iABC) /* OP_LOADNIL */
|
||||
,opmode(0, 0, 0, 1, iABC) /* OP_GETUPVAL */
|
||||
,opmode(0, 0, 0, 0, iABC) /* OP_SETUPVAL */
|
||||
,opmode(0, 0, 0, 1, iABC) /* OP_GETTABUP */
|
||||
,opmode(0, 0, 0, 1, iABC) /* OP_GETTABLE */
|
||||
,opmode(0, 0, 0, 1, iABC) /* OP_GETI */
|
||||
,opmode(0, 0, 0, 1, iABC) /* OP_GETFIELD */
|
||||
,opmode(0, 0, 0, 0, iABC) /* OP_SETTABUP */
|
||||
,opmode(0, 0, 0, 0, iABC) /* OP_SETTABLE */
|
||||
,opmode(0, 0, 0, 0, iABC) /* OP_SETI */
|
||||
,opmode(0, 0, 0, 0, iABC) /* OP_SETFIELD */
|
||||
,opmode(0, 0, 0, 1, iABC) /* OP_NEWTABLE */
|
||||
,opmode(0, 0, 0, 1, iABC) /* OP_SELF */
|
||||
,opmode(0, 0, 0, 1, iABC) /* OP_ADDI */
|
||||
,opmode(0, 0, 0, 1, iABC) /* OP_SUBI */
|
||||
,opmode(0, 0, 0, 1, iABC) /* OP_MULI */
|
||||
,opmode(0, 0, 0, 1, iABC) /* OP_MODI */
|
||||
,opmode(0, 0, 0, 1, iABC) /* OP_POWI */
|
||||
,opmode(0, 0, 0, 1, iABC) /* OP_DIVI */
|
||||
,opmode(0, 0, 0, 1, iABC) /* OP_IDIVI */
|
||||
,opmode(0, 0, 0, 1, iABC) /* OP_ADDK */
|
||||
,opmode(0, 0, 0, 1, iABC) /* OP_SUBK */
|
||||
,opmode(0, 0, 0, 1, iABC) /* OP_MULK */
|
||||
,opmode(0, 0, 0, 1, iABC) /* OP_MODK */
|
||||
,opmode(0, 0, 0, 1, iABC) /* OP_POWK */
|
||||
,opmode(0, 0, 0, 1, iABC) /* OP_DIVK */
|
||||
,opmode(0, 0, 0, 1, iABC) /* OP_IDIVK */
|
||||
,opmode(0, 0, 0, 1, iABC) /* OP_BANDK */
|
||||
,opmode(0, 0, 0, 1, iABC) /* OP_BORK */
|
||||
,opmode(0, 0, 0, 1, iABC) /* OP_BXORK */
|
||||
,opmode(0, 0, 0, 1, iABC) /* OP_SHRI */
|
||||
,opmode(0, 0, 0, 1, iABC) /* OP_SHLI */
|
||||
,opmode(0, 0, 0, 1, iABC) /* OP_ADD */
|
||||
,opmode(0, 0, 0, 1, iABC) /* OP_SUB */
|
||||
,opmode(0, 0, 0, 1, iABC) /* OP_MUL */
|
||||
,opmode(0, 0, 0, 1, iABC) /* OP_MOD */
|
||||
,opmode(0, 0, 0, 1, iABC) /* OP_POW */
|
||||
,opmode(0, 0, 0, 1, iABC) /* OP_DIV */
|
||||
,opmode(0, 0, 0, 1, iABC) /* OP_IDIV */
|
||||
,opmode(0, 0, 0, 1, iABC) /* OP_BAND */
|
||||
,opmode(0, 0, 0, 1, iABC) /* OP_BOR */
|
||||
,opmode(0, 0, 0, 1, iABC) /* OP_BXOR */
|
||||
,opmode(0, 0, 0, 1, iABC) /* OP_SHL */
|
||||
,opmode(0, 0, 0, 1, iABC) /* OP_SHR */
|
||||
,opmode(0, 0, 0, 1, iABC) /* OP_UNM */
|
||||
,opmode(0, 0, 0, 1, iABC) /* OP_BNOT */
|
||||
,opmode(0, 0, 0, 1, iABC) /* OP_NOT */
|
||||
,opmode(0, 0, 0, 1, iABC) /* OP_LEN */
|
||||
,opmode(0, 0, 0, 1, iABC) /* OP_CONCAT */
|
||||
,opmode(0, 0, 0, 0, iABC) /* OP_CLOSE */
|
||||
,opmode(0, 0, 0, 0, iABC) /* OP_TBC */
|
||||
,opmode(0, 0, 0, 0, isJ) /* OP_JMP */
|
||||
,opmode(0, 0, 1, 0, iABC) /* OP_EQ */
|
||||
,opmode(0, 0, 1, 0, iABC) /* OP_LT */
|
||||
,opmode(0, 0, 1, 0, iABC) /* OP_LE */
|
||||
,opmode(0, 0, 1, 0, iABC) /* OP_EQK */
|
||||
,opmode(0, 0, 1, 0, iABC) /* OP_EQI */
|
||||
,opmode(0, 0, 1, 0, iABC) /* OP_LTI */
|
||||
,opmode(0, 0, 1, 0, iABC) /* OP_LEI */
|
||||
,opmode(0, 0, 1, 0, iABC) /* OP_GTI */
|
||||
,opmode(0, 0, 1, 0, iABC) /* OP_GEI */
|
||||
,opmode(0, 0, 1, 0, iABC) /* OP_TEST */
|
||||
,opmode(0, 0, 1, 1, iABC) /* OP_TESTSET */
|
||||
,opmode(1, 1, 0, 1, iABC) /* OP_CALL */
|
||||
,opmode(1, 1, 0, 1, iABC) /* OP_TAILCALL */
|
||||
,opmode(0, 1, 0, 0, iABC) /* OP_RETURN */
|
||||
,opmode(0, 0, 0, 0, iABC) /* OP_RETURN0 */
|
||||
,opmode(0, 0, 0, 0, iABC) /* OP_RETURN1 */
|
||||
,opmode(0, 0, 0, 1, iABx) /* OP_FORLOOP */
|
||||
,opmode(0, 0, 0, 1, iABx) /* OP_FORPREP */
|
||||
,opmode(0, 0, 0, 0, iABx) /* OP_TFORPREP */
|
||||
,opmode(0, 0, 0, 0, iABC) /* OP_TFORCALL */
|
||||
,opmode(0, 0, 0, 1, iABx) /* OP_TFORLOOP */
|
||||
,opmode(0, 1, 0, 0, iABC) /* OP_SETLIST */
|
||||
,opmode(0, 0, 0, 1, iABx) /* OP_CLOSURE */
|
||||
,opmode(1, 0, 0, 1, iABC) /* OP_VARARG */
|
||||
,opmode(0, 0, 0, 1, iABC) /* OP_VARARGPREP */
|
||||
,opmode(0, 0, 0, 0, iAx) /* OP_EXTRAARG */
|
||||
/* MM OT IT T A mode opcode */
|
||||
opmode(0, 0, 0, 0, 1, iABC) /* OP_MOVE */
|
||||
,opmode(0, 0, 0, 0, 1, iAsBx) /* OP_LOADI */
|
||||
,opmode(0, 0, 0, 0, 1, iAsBx) /* OP_LOADF */
|
||||
,opmode(0, 0, 0, 0, 1, iABx) /* OP_LOADK */
|
||||
,opmode(0, 0, 0, 0, 1, iABx) /* OP_LOADKX */
|
||||
,opmode(0, 0, 0, 0, 1, iABC) /* OP_LOADBOOL */
|
||||
,opmode(0, 0, 0, 0, 1, iABC) /* OP_LOADNIL */
|
||||
,opmode(0, 0, 0, 0, 1, iABC) /* OP_GETUPVAL */
|
||||
,opmode(0, 0, 0, 0, 0, iABC) /* OP_SETUPVAL */
|
||||
,opmode(0, 0, 0, 0, 1, iABC) /* OP_GETTABUP */
|
||||
,opmode(0, 0, 0, 0, 1, iABC) /* OP_GETTABLE */
|
||||
,opmode(0, 0, 0, 0, 1, iABC) /* OP_GETI */
|
||||
,opmode(0, 0, 0, 0, 1, iABC) /* OP_GETFIELD */
|
||||
,opmode(0, 0, 0, 0, 0, iABC) /* OP_SETTABUP */
|
||||
,opmode(0, 0, 0, 0, 0, iABC) /* OP_SETTABLE */
|
||||
,opmode(0, 0, 0, 0, 0, iABC) /* OP_SETI */
|
||||
,opmode(0, 0, 0, 0, 0, iABC) /* OP_SETFIELD */
|
||||
,opmode(0, 0, 0, 0, 1, iABC) /* OP_NEWTABLE */
|
||||
,opmode(0, 0, 0, 0, 1, iABC) /* OP_SELF */
|
||||
,opmode(0, 0, 0, 0, 1, iABC) /* OP_ADDI */
|
||||
,opmode(0, 0, 0, 0, 1, iABC) /* OP_ADDK */
|
||||
,opmode(0, 0, 0, 0, 1, iABC) /* OP_SUBK */
|
||||
,opmode(0, 0, 0, 0, 1, iABC) /* OP_MULK */
|
||||
,opmode(0, 0, 0, 0, 1, iABC) /* OP_MODK */
|
||||
,opmode(0, 0, 0, 0, 1, iABC) /* OP_POWK */
|
||||
,opmode(0, 0, 0, 0, 1, iABC) /* OP_DIVK */
|
||||
,opmode(0, 0, 0, 0, 1, iABC) /* OP_IDIVK */
|
||||
,opmode(0, 0, 0, 0, 1, iABC) /* OP_BANDK */
|
||||
,opmode(0, 0, 0, 0, 1, iABC) /* OP_BORK */
|
||||
,opmode(0, 0, 0, 0, 1, iABC) /* OP_BXORK */
|
||||
,opmode(0, 0, 0, 0, 1, iABC) /* OP_SHRI */
|
||||
,opmode(0, 0, 0, 0, 1, iABC) /* OP_SHLI */
|
||||
,opmode(0, 0, 0, 0, 1, iABC) /* OP_ADD */
|
||||
,opmode(0, 0, 0, 0, 1, iABC) /* OP_SUB */
|
||||
,opmode(0, 0, 0, 0, 1, iABC) /* OP_MUL */
|
||||
,opmode(0, 0, 0, 0, 1, iABC) /* OP_MOD */
|
||||
,opmode(0, 0, 0, 0, 1, iABC) /* OP_POW */
|
||||
,opmode(0, 0, 0, 0, 1, iABC) /* OP_DIV */
|
||||
,opmode(0, 0, 0, 0, 1, iABC) /* OP_IDIV */
|
||||
,opmode(0, 0, 0, 0, 1, iABC) /* OP_BAND */
|
||||
,opmode(0, 0, 0, 0, 1, iABC) /* OP_BOR */
|
||||
,opmode(0, 0, 0, 0, 1, iABC) /* OP_BXOR */
|
||||
,opmode(0, 0, 0, 0, 1, iABC) /* OP_SHL */
|
||||
,opmode(0, 0, 0, 0, 1, iABC) /* OP_SHR */
|
||||
,opmode(1, 0, 0, 0, 0, iABC) /* OP_MMBIN */
|
||||
,opmode(1, 0, 0, 0, 0, iABC) /* OP_MMBINI*/
|
||||
,opmode(1, 0, 0, 0, 0, iABC) /* OP_MMBINK*/
|
||||
,opmode(0, 0, 0, 0, 1, iABC) /* OP_UNM */
|
||||
,opmode(0, 0, 0, 0, 1, iABC) /* OP_BNOT */
|
||||
,opmode(0, 0, 0, 0, 1, iABC) /* OP_NOT */
|
||||
,opmode(0, 0, 0, 0, 1, iABC) /* OP_LEN */
|
||||
,opmode(0, 0, 0, 0, 1, iABC) /* OP_CONCAT */
|
||||
,opmode(0, 0, 0, 0, 0, iABC) /* OP_CLOSE */
|
||||
,opmode(0, 0, 0, 0, 0, iABC) /* OP_TBC */
|
||||
,opmode(0, 0, 0, 0, 0, isJ) /* OP_JMP */
|
||||
,opmode(0, 0, 0, 1, 0, iABC) /* OP_EQ */
|
||||
,opmode(0, 0, 0, 1, 0, iABC) /* OP_LT */
|
||||
,opmode(0, 0, 0, 1, 0, iABC) /* OP_LE */
|
||||
,opmode(0, 0, 0, 1, 0, iABC) /* OP_EQK */
|
||||
,opmode(0, 0, 0, 1, 0, iABC) /* OP_EQI */
|
||||
,opmode(0, 0, 0, 1, 0, iABC) /* OP_LTI */
|
||||
,opmode(0, 0, 0, 1, 0, iABC) /* OP_LEI */
|
||||
,opmode(0, 0, 0, 1, 0, iABC) /* OP_GTI */
|
||||
,opmode(0, 0, 0, 1, 0, iABC) /* OP_GEI */
|
||||
,opmode(0, 0, 0, 1, 0, iABC) /* OP_TEST */
|
||||
,opmode(0, 0, 0, 1, 1, iABC) /* OP_TESTSET */
|
||||
,opmode(0, 1, 1, 0, 1, iABC) /* OP_CALL */
|
||||
,opmode(0, 1, 1, 0, 1, iABC) /* OP_TAILCALL */
|
||||
,opmode(0, 0, 1, 0, 0, iABC) /* OP_RETURN */
|
||||
,opmode(0, 0, 0, 0, 0, iABC) /* OP_RETURN0 */
|
||||
,opmode(0, 0, 0, 0, 0, iABC) /* OP_RETURN1 */
|
||||
,opmode(0, 0, 0, 0, 1, iABx) /* OP_FORLOOP */
|
||||
,opmode(0, 0, 0, 0, 1, iABx) /* OP_FORPREP */
|
||||
,opmode(0, 0, 0, 0, 0, iABx) /* OP_TFORPREP */
|
||||
,opmode(0, 0, 0, 0, 0, iABC) /* OP_TFORCALL */
|
||||
,opmode(0, 0, 0, 0, 1, iABx) /* OP_TFORLOOP */
|
||||
,opmode(0, 0, 1, 0, 0, iABC) /* OP_SETLIST */
|
||||
,opmode(0, 0, 0, 0, 1, iABx) /* OP_CLOSURE */
|
||||
,opmode(0, 1, 0, 0, 1, iABC) /* OP_VARARG */
|
||||
,opmode(0, 0, 1, 0, 1, iABC) /* OP_VARARGPREP */
|
||||
,opmode(0, 0, 0, 0, 0, iAx) /* OP_EXTRAARG */
|
||||
};
|
||||
|
||||
|
||||
+32
-24
@@ -97,6 +97,9 @@ enum OpMode {iABC, iABx, iAsBx, iAx, isJ}; /* basic instruction formats */
|
||||
#define MAXARG_C ((1<<SIZE_C)-1)
|
||||
#define OFFSET_sC (MAXARG_C >> 1)
|
||||
|
||||
#define int2sC(i) ((i) + OFFSET_sC)
|
||||
#define sC2int(i) ((i) - OFFSET_sC)
|
||||
|
||||
|
||||
/* creates a mask with 'n' 1 bits at position 'p' */
|
||||
#define MASK1(n,p) ((~((~(Instruction)0)<<(n)))<<(p))
|
||||
@@ -123,11 +126,11 @@ enum OpMode {iABC, iABx, iAsBx, iAx, isJ}; /* basic instruction formats */
|
||||
#define SETARG_A(i,v) setarg(i, v, POS_A, SIZE_A)
|
||||
|
||||
#define GETARG_B(i) check_exp(checkopm(i, iABC), getarg(i, POS_B, SIZE_B))
|
||||
#define GETARG_sB(i) (GETARG_B(i) - OFFSET_sC)
|
||||
#define GETARG_sB(i) sC2int(GETARG_B(i))
|
||||
#define SETARG_B(i,v) setarg(i, v, POS_B, SIZE_B)
|
||||
|
||||
#define GETARG_C(i) check_exp(checkopm(i, iABC), getarg(i, POS_C, SIZE_C))
|
||||
#define GETARG_sC(i) (GETARG_C(i) - OFFSET_sC)
|
||||
#define GETARG_sC(i) sC2int(GETARG_C(i))
|
||||
#define SETARG_C(i,v) setarg(i, v, POS_C, SIZE_C)
|
||||
|
||||
#define TESTARG_k(i) (cast_int(((i) & (1u << POS_k))))
|
||||
@@ -214,17 +217,11 @@ OP_SETTABLE,/* A B C R(A)[R(B)] := RK(C) */
|
||||
OP_SETI,/* A B C R(A)[B] := RK(C) */
|
||||
OP_SETFIELD,/* A B C R(A)[K(B):string] := RK(C) */
|
||||
|
||||
OP_NEWTABLE,/* A B C R(A) := {} (size = B,C) */
|
||||
OP_NEWTABLE,/* A B C R(A) := {} */
|
||||
|
||||
OP_SELF,/* A B C R(A+1) := R(B); R(A) := R(B)[RK(C):string] */
|
||||
|
||||
OP_ADDI,/* A B sC R(A) := R(B) + C */
|
||||
OP_SUBI,/* A B sC R(A) := R(B) - C */
|
||||
OP_MULI,/* A B sC R(A) := R(B) * C */
|
||||
OP_MODI,/* A B sC R(A) := R(B) % C */
|
||||
OP_POWI,/* A B sC R(A) := R(B) ^ C */
|
||||
OP_DIVI,/* A B sC R(A) := R(B) / C */
|
||||
OP_IDIVI,/* A B sC R(A) := R(B) // C */
|
||||
OP_ADDI,/* A B sC R(A) := R(B) + sC */
|
||||
|
||||
OP_ADDK,/* A B C R(A) := R(B) + K(C) */
|
||||
OP_SUBK,/* A B C R(A) := R(B) - K(C) */
|
||||
@@ -238,8 +235,8 @@ OP_BANDK,/* A B C R(A) := R(B) & K(C):integer */
|
||||
OP_BORK,/* A B C R(A) := R(B) | K(C):integer */
|
||||
OP_BXORK,/* A B C R(A) := R(B) ~ K(C):integer */
|
||||
|
||||
OP_SHRI,/* A B sC R(A) := R(B) >> C */
|
||||
OP_SHLI,/* A B sC R(A) := C << R(B) */
|
||||
OP_SHRI,/* A B sC R(A) := R(B) >> sC */
|
||||
OP_SHLI,/* A B sC R(A) := sC << R(B) */
|
||||
|
||||
OP_ADD,/* A B C R(A) := R(B) + R(C) */
|
||||
OP_SUB,/* A B C R(A) := R(B) - R(C) */
|
||||
@@ -255,6 +252,10 @@ OP_BXOR,/* A B C R(A) := R(B) ~ R(C) */
|
||||
OP_SHL,/* A B C R(A) := R(B) << R(C) */
|
||||
OP_SHR,/* A B C R(A) := R(B) >> R(C) */
|
||||
|
||||
OP_MMBIN,/* A B C call C metamethod over R(A) and R(B) */
|
||||
OP_MMBINI,/* A sB C call C metamethod over R(A) and sB */
|
||||
OP_MMBINK,/* A B C call C metamethod over R(A) and K(B) */
|
||||
|
||||
OP_UNM,/* A B R(A) := -R(B) */
|
||||
OP_BNOT,/* A B R(A) := ~R(B) */
|
||||
OP_NOT,/* A B R(A) := not R(B) */
|
||||
@@ -286,9 +287,9 @@ OP_RETURN,/* A B C return R(A), ... ,R(A+B-2) (see note) */
|
||||
OP_RETURN0,/* return */
|
||||
OP_RETURN1,/* A return R(A) */
|
||||
|
||||
OP_FORLOOP,/* A Bx R(A)+=R(A+2);
|
||||
if R(A) <?= R(A+1) then { pc-=Bx; R(A+3)=R(A) } */
|
||||
OP_FORPREP,/* A Bx R(A)-=R(A+2); pc+=Bx */
|
||||
OP_FORLOOP,/* A Bx update counters; if loop continues then pc-=Bx; */
|
||||
OP_FORPREP,/* A Bx <check values and prepare counters>;
|
||||
if not to run then pc+=Bx+1; */
|
||||
|
||||
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)); */
|
||||
@@ -321,10 +322,16 @@ OP_EXTRAARG/* Ax extra (larger) argument for previous opcode */
|
||||
|
||||
(*) In OP_RETURN, if (B == 0) then return up to 'top'.
|
||||
|
||||
(*) In OP_SETLIST, if (B == 0) then real B = 'top'; if (C == 0) then
|
||||
next 'instruction' is EXTRAARG(real C).
|
||||
(*) In OP_LOADKX and OP_NEWTABLE, the next instruction is always
|
||||
EXTRAARG.
|
||||
|
||||
(*) In OP_LOADKX, the next 'instruction' is always EXTRAARG.
|
||||
(*) In OP_SETLIST, if (B == 0) then real B = 'top'; if k, then
|
||||
real C = EXTRAARG _ C (the bits of EXTRAARG concatenated with the
|
||||
bits of C).
|
||||
|
||||
(*) In OP_NEWTABLE, B is log2 of the hash size (which is always a
|
||||
power of 2) plus 1, or zero for size zero. If not k, the array size
|
||||
is C. Otherwise, the array size is EXTRAARG _ C.
|
||||
|
||||
(*) For comparisons, k specifies what condition the test should accept
|
||||
(true or false).
|
||||
@@ -332,10 +339,9 @@ OP_EXTRAARG/* Ax extra (larger) argument for previous opcode */
|
||||
(*) All 'skips' (pc++) assume that next instruction is a jump.
|
||||
|
||||
(*) In instructions OP_RETURN/OP_TAILCALL, 'k' specifies that the
|
||||
function either builds upvalues, which may need to be closed, or is
|
||||
vararg, which must be corrected before returning. When 'k' is true,
|
||||
C > 0 means the function is vararg and (C - 1) is its number of
|
||||
fixed parameters.
|
||||
function builds upvalues, which may need to be closed. C > 0 means
|
||||
the function is vararg, so that its 'func' must be corrected before
|
||||
returning; in this case, (C - 1) is its number of fixed parameters.
|
||||
|
||||
(*) In comparisons with an immediate operand, C signals whether the
|
||||
original operand was a float.
|
||||
@@ -350,6 +356,7 @@ OP_EXTRAARG/* Ax extra (larger) argument for previous opcode */
|
||||
** bit 4: operator is a test (next instruction must be a jump)
|
||||
** bit 5: instruction uses 'L->top' set by previous instruction (when B == 0)
|
||||
** bit 6: instruction sets 'L->top' for next instruction (when C == 0)
|
||||
** bit 7: instruction is an MM instruction (call a metamethod)
|
||||
*/
|
||||
|
||||
LUAI_DDEC(const lu_byte luaP_opmodes[NUM_OPCODES];)
|
||||
@@ -359,6 +366,7 @@ LUAI_DDEC(const lu_byte luaP_opmodes[NUM_OPCODES];)
|
||||
#define testTMode(m) (luaP_opmodes[m] & (1 << 4))
|
||||
#define testITMode(m) (luaP_opmodes[m] & (1 << 5))
|
||||
#define testOTMode(m) (luaP_opmodes[m] & (1 << 6))
|
||||
#define testMMMode(m) (luaP_opmodes[m] & (1 << 7))
|
||||
|
||||
/* "out top" (set top for next instruction) */
|
||||
#define isOT(i) \
|
||||
@@ -368,11 +376,11 @@ LUAI_DDEC(const lu_byte luaP_opmodes[NUM_OPCODES];)
|
||||
/* "in top" (uses top from previous instruction) */
|
||||
#define isIT(i) (testITMode(GET_OPCODE(i)) && GETARG_B(i) == 0)
|
||||
|
||||
#define opmode(ot,it,t,a,m) (((ot)<<6) | ((it)<<5) | ((t)<<4) | ((a)<<3) | (m))
|
||||
#define opmode(mm,ot,it,t,a,m) \
|
||||
(((mm) << 7) | ((ot) << 6) | ((it) << 5) | ((t) << 4) | ((a) << 3) | (m))
|
||||
|
||||
|
||||
/* number of list items to accumulate before a SETLIST instruction */
|
||||
#define LFIELDS_PER_FLUSH 50
|
||||
|
||||
|
||||
#endif
|
||||
|
||||
+3
-6
@@ -30,12 +30,6 @@ static const char *const opnames[] = {
|
||||
"NEWTABLE",
|
||||
"SELF",
|
||||
"ADDI",
|
||||
"SUBI",
|
||||
"MULI",
|
||||
"MODI",
|
||||
"POWI",
|
||||
"DIVI",
|
||||
"IDIVI",
|
||||
"ADDK",
|
||||
"SUBK",
|
||||
"MULK",
|
||||
@@ -60,6 +54,9 @@ static const char *const opnames[] = {
|
||||
"BXOR",
|
||||
"SHL",
|
||||
"SHR",
|
||||
"MMBIN",
|
||||
"MMBINI",
|
||||
"MMBINK",
|
||||
"UNM",
|
||||
"BNOT",
|
||||
"NOT",
|
||||
|
||||
@@ -58,18 +58,20 @@
|
||||
** ===================================================================
|
||||
*/
|
||||
|
||||
#if !defined(l_time_t) /* { */
|
||||
/*
|
||||
** type to represent time_t in Lua
|
||||
*/
|
||||
#if !defined(LUA_NUMTIME) /* { */
|
||||
|
||||
#define l_timet lua_Integer
|
||||
#define l_pushtime(L,t) lua_pushinteger(L,(lua_Integer)(t))
|
||||
#define l_gettime(L,arg) luaL_checkinteger(L, arg)
|
||||
|
||||
static time_t l_checktime (lua_State *L, int arg) {
|
||||
lua_Integer t = luaL_checkinteger(L, arg);
|
||||
luaL_argcheck(L, (time_t)t == t, arg, "time out-of-bounds");
|
||||
return (time_t)t;
|
||||
}
|
||||
#else /* }{ */
|
||||
|
||||
#define l_timet lua_Number
|
||||
#define l_pushtime(L,t) lua_pushnumber(L,(lua_Number)(t))
|
||||
#define l_gettime(L,arg) luaL_checknumber(L, arg)
|
||||
|
||||
#endif /* } */
|
||||
|
||||
@@ -193,11 +195,25 @@ static int os_clock (lua_State *L) {
|
||||
** =======================================================
|
||||
*/
|
||||
|
||||
static void setfield (lua_State *L, const char *key, int value) {
|
||||
lua_pushinteger(L, value);
|
||||
/*
|
||||
** About the overflow check: an overflow cannot occurr when time
|
||||
** is represented by a lua_Integer, because either lua_Integer is
|
||||
** large enough to represent all int fields or it is not large enough
|
||||
** to represent a time that cause a field to overflow. However, if
|
||||
** times are represented as doubles and lua_Integer is int, then the
|
||||
** time 0x1.e1853b0d184f6p+55 would cause an overflow when adding 1900
|
||||
** to compute the year.
|
||||
*/
|
||||
static void setfield (lua_State *L, const char *key, int value, int delta) {
|
||||
#if (defined(LUA_NUMTIME) && LUA_MAXINTEGER <= INT_MAX)
|
||||
if (value > LUA_MAXINTEGER - delta)
|
||||
luaL_error(L, "field '%s' is out-of-bound", key);
|
||||
#endif
|
||||
lua_pushinteger(L, (lua_Integer)value + delta);
|
||||
lua_setfield(L, -2, key);
|
||||
}
|
||||
|
||||
|
||||
static void setboolfield (lua_State *L, const char *key, int value) {
|
||||
if (value < 0) /* undefined? */
|
||||
return; /* does not set field */
|
||||
@@ -210,14 +226,14 @@ static void setboolfield (lua_State *L, const char *key, int value) {
|
||||
** Set all fields from structure 'tm' in the table on top of the stack
|
||||
*/
|
||||
static void setallfields (lua_State *L, struct tm *stm) {
|
||||
setfield(L, "sec", stm->tm_sec);
|
||||
setfield(L, "min", stm->tm_min);
|
||||
setfield(L, "hour", stm->tm_hour);
|
||||
setfield(L, "day", stm->tm_mday);
|
||||
setfield(L, "month", stm->tm_mon + 1);
|
||||
setfield(L, "year", stm->tm_year + 1900);
|
||||
setfield(L, "wday", stm->tm_wday + 1);
|
||||
setfield(L, "yday", stm->tm_yday + 1);
|
||||
setfield(L, "year", stm->tm_year, 1900);
|
||||
setfield(L, "month", stm->tm_mon, 1);
|
||||
setfield(L, "day", stm->tm_mday, 0);
|
||||
setfield(L, "hour", stm->tm_hour, 0);
|
||||
setfield(L, "min", stm->tm_min, 0);
|
||||
setfield(L, "sec", stm->tm_sec, 0);
|
||||
setfield(L, "yday", stm->tm_yday, 1);
|
||||
setfield(L, "wday", stm->tm_wday, 1);
|
||||
setboolfield(L, "isdst", stm->tm_isdst);
|
||||
}
|
||||
|
||||
@@ -230,11 +246,6 @@ static int getboolfield (lua_State *L, const char *key) {
|
||||
}
|
||||
|
||||
|
||||
/* maximum value for date fields (to avoid arithmetic overflows with 'int') */
|
||||
#if !defined(L_MAXDATEFIELD)
|
||||
#define L_MAXDATEFIELD (INT_MAX / 2)
|
||||
#endif
|
||||
|
||||
static int getfield (lua_State *L, const char *key, int d, int delta) {
|
||||
int isnum;
|
||||
int t = lua_getfield(L, -1, key); /* get field and its type */
|
||||
@@ -247,7 +258,9 @@ static int getfield (lua_State *L, const char *key, int d, int delta) {
|
||||
res = d;
|
||||
}
|
||||
else {
|
||||
if (!(-L_MAXDATEFIELD <= res && res <= L_MAXDATEFIELD))
|
||||
/* unsigned avoids overflow when lua_Integer has 32 bits */
|
||||
if (!(res >= 0 ? (lua_Unsigned)res <= (lua_Unsigned)INT_MAX + delta
|
||||
: (lua_Integer)INT_MIN + delta <= res))
|
||||
return luaL_error(L, "field '%s' is out-of-bound", key);
|
||||
res -= delta;
|
||||
}
|
||||
@@ -275,6 +288,13 @@ static const char *checkoption (lua_State *L, const char *conv,
|
||||
}
|
||||
|
||||
|
||||
static time_t l_checktime (lua_State *L, int arg) {
|
||||
l_timet t = l_gettime(L, arg);
|
||||
luaL_argcheck(L, (time_t)t == t, arg, "time out-of-bounds");
|
||||
return (time_t)t;
|
||||
}
|
||||
|
||||
|
||||
/* maximum size for an individual 'strftime' item */
|
||||
#define SIZETIMEFMT 250
|
||||
|
||||
@@ -293,7 +313,7 @@ static int os_date (lua_State *L) {
|
||||
stm = l_localtime(&t, &tmr);
|
||||
if (stm == NULL) /* invalid date? */
|
||||
return luaL_error(L,
|
||||
"time result cannot be represented in this installation");
|
||||
"date result cannot be represented in this installation");
|
||||
if (strcmp(s, "*t") == 0) {
|
||||
lua_createtable(L, 0, 9); /* 9 = number of fields */
|
||||
setallfields(L, stm);
|
||||
@@ -329,12 +349,12 @@ static int os_time (lua_State *L) {
|
||||
struct tm ts;
|
||||
luaL_checktype(L, 1, LUA_TTABLE);
|
||||
lua_settop(L, 1); /* make sure table is at the top */
|
||||
ts.tm_sec = getfield(L, "sec", 0, 0);
|
||||
ts.tm_min = getfield(L, "min", 0, 0);
|
||||
ts.tm_hour = getfield(L, "hour", 12, 0);
|
||||
ts.tm_mday = getfield(L, "day", -1, 0);
|
||||
ts.tm_mon = getfield(L, "month", -1, 1);
|
||||
ts.tm_year = getfield(L, "year", -1, 1900);
|
||||
ts.tm_mon = getfield(L, "month", -1, 1);
|
||||
ts.tm_mday = getfield(L, "day", -1, 0);
|
||||
ts.tm_hour = getfield(L, "hour", 12, 0);
|
||||
ts.tm_min = getfield(L, "min", 0, 0);
|
||||
ts.tm_sec = getfield(L, "sec", 0, 0);
|
||||
ts.tm_isdst = getboolfield(L, "isdst");
|
||||
t = mktime(&ts);
|
||||
setallfields(L, &ts); /* update fields with normalized values */
|
||||
|
||||
@@ -156,20 +156,15 @@ static void init_exp (expdesc *e, expkind k, int i) {
|
||||
}
|
||||
|
||||
|
||||
static void init_var (expdesc *e, expkind k, int i) {
|
||||
static void codestring (expdesc *e, TString *s) {
|
||||
e->f = e->t = NO_JUMP;
|
||||
e->k = k;
|
||||
e->u.var.idx = i;
|
||||
}
|
||||
|
||||
|
||||
static void codestring (LexState *ls, expdesc *e, TString *s) {
|
||||
init_exp(e, VK, luaK_stringK(ls->fs, s));
|
||||
e->k = VKSTR;
|
||||
e->u.strval = s;
|
||||
}
|
||||
|
||||
|
||||
static void codename (LexState *ls, expdesc *e) {
|
||||
codestring(ls, e, str_checkname(ls));
|
||||
codestring(e, str_checkname(ls));
|
||||
}
|
||||
|
||||
|
||||
@@ -177,41 +172,42 @@ static void codename (LexState *ls, expdesc *e) {
|
||||
** Register a new local variable in the active 'Proto' (for debug
|
||||
** information).
|
||||
*/
|
||||
static int registerlocalvar (LexState *ls, TString *varname) {
|
||||
FuncState *fs = ls->fs;
|
||||
static int registerlocalvar (LexState *ls, FuncState *fs, TString *varname) {
|
||||
Proto *f = fs->f;
|
||||
int oldsize = f->sizelocvars;
|
||||
luaM_growvector(ls->L, f->locvars, fs->nlocvars, f->sizelocvars,
|
||||
luaM_growvector(ls->L, f->locvars, fs->ndebugvars, f->sizelocvars,
|
||||
LocVar, SHRT_MAX, "local variables");
|
||||
while (oldsize < f->sizelocvars)
|
||||
f->locvars[oldsize++].varname = NULL;
|
||||
f->locvars[fs->nlocvars].varname = varname;
|
||||
f->locvars[fs->ndebugvars].varname = varname;
|
||||
f->locvars[fs->ndebugvars].startpc = fs->pc;
|
||||
luaC_objbarrier(ls->L, f, varname);
|
||||
return fs->nlocvars++;
|
||||
return fs->ndebugvars++;
|
||||
}
|
||||
|
||||
|
||||
/*
|
||||
** Create a new local variable with the given 'name'.
|
||||
** Create a new local variable with the given 'name'. Return its index
|
||||
** in the function.
|
||||
*/
|
||||
static Vardesc *new_localvar (LexState *ls, TString *name) {
|
||||
static int new_localvar (LexState *ls, TString *name) {
|
||||
lua_State *L = ls->L;
|
||||
FuncState *fs = ls->fs;
|
||||
Dyndata *dyd = ls->dyd;
|
||||
Vardesc *var;
|
||||
int reg = registerlocalvar(ls, name);
|
||||
checklimit(fs, dyd->actvar.n + 1 - fs->firstlocal,
|
||||
MAXVARS, "local variables");
|
||||
luaM_growvector(ls->L, dyd->actvar.arr, dyd->actvar.n + 1,
|
||||
dyd->actvar.size, Vardesc, MAX_INT, "local variables");
|
||||
MAXVARS, "local variables");
|
||||
luaM_growvector(L, dyd->actvar.arr, dyd->actvar.n + 1,
|
||||
dyd->actvar.size, Vardesc, USHRT_MAX, "local variables");
|
||||
var = &dyd->actvar.arr[dyd->actvar.n++];
|
||||
var->idx = cast(short, reg);
|
||||
var->name = name;
|
||||
var->ro = 0;
|
||||
return var;
|
||||
var->vd.kind = VDKREG; /* default */
|
||||
var->vd.name = name;
|
||||
return dyd->actvar.n - 1 - fs->firstlocal;
|
||||
}
|
||||
|
||||
#define new_localvarliteral(ls,v) \
|
||||
new_localvar(ls, luaX_newstring(ls, "" v, (sizeof(v)/sizeof(char)) - 1));
|
||||
new_localvar(ls, \
|
||||
luaX_newstring(ls, "" v, (sizeof(v)/sizeof(char)) - 1));
|
||||
|
||||
|
||||
|
||||
@@ -223,48 +219,80 @@ static Vardesc *getlocalvardesc (FuncState *fs, int i) {
|
||||
return &fs->ls->dyd->actvar.arr[fs->firstlocal + i];
|
||||
}
|
||||
|
||||
|
||||
/*
|
||||
** Get the debug-information entry for current variable 'i'.
|
||||
** Convert 'nvar' (number of active variables at some point) to
|
||||
** number of variables in the stack at that point.
|
||||
*/
|
||||
static LocVar *getlocvar (FuncState *fs, int i) {
|
||||
int idx = getlocalvardesc(fs, i)->idx;
|
||||
lua_assert(idx < fs->nlocvars);
|
||||
return &fs->f->locvars[idx];
|
||||
static int stacklevel (FuncState *fs, int nvar) {
|
||||
while (nvar > 0) {
|
||||
Vardesc *vd = getlocalvardesc(fs, nvar - 1);
|
||||
if (vd->vd.kind != RDKCTC) /* is in the stack? */
|
||||
return vd->vd.sidx + 1;
|
||||
else
|
||||
nvar--; /* try previous variable */
|
||||
}
|
||||
return 0; /* no variables */
|
||||
}
|
||||
|
||||
|
||||
/*
|
||||
** Return the "variable description" (Vardesc) of a given
|
||||
** variable or upvalue
|
||||
** Return the number of variables in the stack for function 'fs'
|
||||
*/
|
||||
static Vardesc *getvardesc (FuncState *fs, expdesc *e) {
|
||||
if (e->k == VLOCAL)
|
||||
return getlocalvardesc(fs, e->u.var.idx);
|
||||
else if (e->k != VUPVAL)
|
||||
return NULL; /* not a local variable */
|
||||
else { /* upvalue: must go up all levels up to the original local */
|
||||
int idx = e->u.var.idx;
|
||||
for (;;) {
|
||||
Upvaldesc *up = &fs->f->upvalues[idx];
|
||||
fs = fs->prev; /* must look at the previous level */
|
||||
idx = up->idx; /* at this index */
|
||||
if (fs == NULL) { /* no more levels? (can happen only with _ENV) */
|
||||
lua_assert(strcmp(getstr(up->name), LUA_ENV) == 0);
|
||||
return NULL;
|
||||
}
|
||||
else if (up->instack) /* got to the original level? */
|
||||
return getlocalvardesc(fs, idx);
|
||||
/* else repeat for previous level */
|
||||
}
|
||||
int luaY_nvarstack (FuncState *fs) {
|
||||
return stacklevel(fs, fs->nactvar);
|
||||
}
|
||||
|
||||
|
||||
/*
|
||||
** Get the debug-information entry for current variable 'i'.
|
||||
*/
|
||||
static LocVar *localdebuginfo (FuncState *fs, int i) {
|
||||
Vardesc *vd = getlocalvardesc(fs, i);
|
||||
if (vd->vd.kind == RDKCTC)
|
||||
return NULL; /* no debug info. for constants */
|
||||
else {
|
||||
int idx = vd->vd.pidx;
|
||||
lua_assert(idx < fs->ndebugvars);
|
||||
return &fs->f->locvars[idx];
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
static void init_var (FuncState *fs, expdesc *e, int i) {
|
||||
e->f = e->t = NO_JUMP;
|
||||
e->k = VLOCAL;
|
||||
e->u.var.vidx = i;
|
||||
e->u.var.sidx = getlocalvardesc(fs, i)->vd.sidx;
|
||||
}
|
||||
|
||||
|
||||
static void check_readonly (LexState *ls, expdesc *e) {
|
||||
Vardesc *vardesc = getvardesc(ls->fs, e);
|
||||
if (vardesc && vardesc->ro) { /* is variable local and const? */
|
||||
FuncState *fs = ls->fs;
|
||||
TString *varname = NULL; /* to be set if variable is const */
|
||||
switch (e->k) {
|
||||
case VCONST: {
|
||||
varname = ls->dyd->actvar.arr[e->u.info].vd.name;
|
||||
break;
|
||||
}
|
||||
case VLOCAL: {
|
||||
Vardesc *vardesc = getlocalvardesc(fs, e->u.var.vidx);
|
||||
if (vardesc->vd.kind != VDKREG) /* not a regular variable? */
|
||||
varname = vardesc->vd.name;
|
||||
break;
|
||||
}
|
||||
case VUPVAL: {
|
||||
Upvaldesc *up = &fs->f->upvalues[e->u.info];
|
||||
if (up->kind != VDKREG)
|
||||
varname = up->name;
|
||||
break;
|
||||
}
|
||||
default:
|
||||
return; /* other cases cannot be read-only */
|
||||
}
|
||||
if (varname) {
|
||||
const char *msg = luaO_pushfstring(ls->L,
|
||||
"attempt to assign to const variable '%s'", getstr(vardesc->name));
|
||||
"attempt to assign to const variable '%s'", getstr(varname));
|
||||
luaK_semerror(ls, msg); /* error */
|
||||
}
|
||||
}
|
||||
@@ -272,13 +300,16 @@ static void check_readonly (LexState *ls, expdesc *e) {
|
||||
|
||||
/*
|
||||
** Start the scope for the last 'nvars' created variables.
|
||||
** (debug info.)
|
||||
*/
|
||||
static void adjustlocalvars (LexState *ls, int nvars) {
|
||||
FuncState *fs = ls->fs;
|
||||
fs->nactvar = cast_byte(fs->nactvar + nvars);
|
||||
for (; nvars; nvars--) {
|
||||
getlocvar(fs, fs->nactvar - nvars)->startpc = fs->pc;
|
||||
int stklevel = luaY_nvarstack(fs);
|
||||
int i;
|
||||
for (i = 0; i < nvars; i++) {
|
||||
int varidx = fs->nactvar++;
|
||||
Vardesc *var = getlocalvardesc(fs, varidx);
|
||||
var->vd.sidx = stklevel++;
|
||||
var->vd.pidx = registerlocalvar(ls, fs, var->vd.name);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -289,8 +320,11 @@ static void adjustlocalvars (LexState *ls, int nvars) {
|
||||
*/
|
||||
static void removevars (FuncState *fs, int tolevel) {
|
||||
fs->ls->dyd->actvar.n -= (fs->nactvar - tolevel);
|
||||
while (fs->nactvar > tolevel)
|
||||
getlocvar(fs, --fs->nactvar)->endpc = fs->pc;
|
||||
while (fs->nactvar > tolevel) {
|
||||
LocVar *var = localdebuginfo(fs, --fs->nactvar);
|
||||
if (var) /* does it have debug information? */
|
||||
var->endpc = fs->pc;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -308,7 +342,7 @@ static int searchupvalue (FuncState *fs, TString *name) {
|
||||
}
|
||||
|
||||
|
||||
static int newupvalue (FuncState *fs, TString *name, expdesc *v) {
|
||||
static Upvaldesc *allocupvalue (FuncState *fs) {
|
||||
Proto *f = fs->f;
|
||||
int oldsize = f->sizeupvalues;
|
||||
checklimit(fs, fs->nups + 1, MAXUPVAL, "upvalues");
|
||||
@@ -316,11 +350,28 @@ static int newupvalue (FuncState *fs, TString *name, expdesc *v) {
|
||||
Upvaldesc, MAXUPVAL, "upvalues");
|
||||
while (oldsize < f->sizeupvalues)
|
||||
f->upvalues[oldsize++].name = NULL;
|
||||
f->upvalues[fs->nups].instack = (v->k == VLOCAL);
|
||||
f->upvalues[fs->nups].idx = cast_byte(v->u.var.idx);
|
||||
f->upvalues[fs->nups].name = name;
|
||||
luaC_objbarrier(fs->ls->L, f, name);
|
||||
return fs->nups++;
|
||||
return &f->upvalues[fs->nups++];
|
||||
}
|
||||
|
||||
|
||||
static int newupvalue (FuncState *fs, TString *name, expdesc *v) {
|
||||
Upvaldesc *up = allocupvalue(fs);
|
||||
FuncState *prev = fs->prev;
|
||||
if (v->k == VLOCAL) {
|
||||
up->instack = 1;
|
||||
up->idx = v->u.var.sidx;
|
||||
up->kind = getlocalvardesc(prev, v->u.var.vidx)->vd.kind;
|
||||
lua_assert(eqstr(name, getlocalvardesc(prev, v->u.var.vidx)->vd.name));
|
||||
}
|
||||
else {
|
||||
up->instack = 0;
|
||||
up->idx = cast_byte(v->u.info);
|
||||
up->kind = prev->f->upvalues[v->u.info].kind;
|
||||
lua_assert(eqstr(name, prev->f->upvalues[v->u.info].name));
|
||||
}
|
||||
up->name = name;
|
||||
luaC_objbarrier(fs->ls->L, fs->f, name);
|
||||
return fs->nups - 1;
|
||||
}
|
||||
|
||||
|
||||
@@ -328,11 +379,17 @@ static int newupvalue (FuncState *fs, TString *name, expdesc *v) {
|
||||
** Look for an active local variable with the name 'n' in the
|
||||
** function 'fs'.
|
||||
*/
|
||||
static int searchvar (FuncState *fs, TString *n) {
|
||||
static int searchvar (FuncState *fs, TString *n, expdesc *var) {
|
||||
int i;
|
||||
for (i = cast_int(fs->nactvar) - 1; i >= 0; i--) {
|
||||
if (eqstr(n, getlocvar(fs, i)->varname))
|
||||
return i;
|
||||
Vardesc *vd = getlocalvardesc(fs, i);
|
||||
if (eqstr(n, vd->vd.name)) { /* found? */
|
||||
if (vd->vd.kind == RDKCTC) /* compile-time constant? */
|
||||
init_exp(var, VCONST, fs->firstlocal + i);
|
||||
else /* real variable */
|
||||
init_var(fs, var, i);
|
||||
return var->k;
|
||||
}
|
||||
}
|
||||
return -1; /* not found */
|
||||
}
|
||||
@@ -360,22 +417,21 @@ static void singlevaraux (FuncState *fs, TString *n, expdesc *var, int base) {
|
||||
if (fs == NULL) /* no more levels? */
|
||||
init_exp(var, VVOID, 0); /* default is global */
|
||||
else {
|
||||
int v = searchvar(fs, n); /* look up locals at current level */
|
||||
int v = searchvar(fs, n, var); /* look up locals at current level */
|
||||
if (v >= 0) { /* found? */
|
||||
init_var(var, VLOCAL, v); /* variable is local */
|
||||
if (!base)
|
||||
markupval(fs, v); /* local will be used as an upval */
|
||||
if (v == VLOCAL && !base)
|
||||
markupval(fs, var->u.var.vidx); /* local will be used as an upval */
|
||||
}
|
||||
else { /* not found as local at current level; try upvalues */
|
||||
int idx = searchupvalue(fs, n); /* try existing upvalues */
|
||||
if (idx < 0) { /* not found? */
|
||||
singlevaraux(fs->prev, n, var, 0); /* try upper levels */
|
||||
if (var->k == VVOID) /* not found? */
|
||||
return; /* it is a global */
|
||||
/* else was LOCAL or UPVAL */
|
||||
idx = newupvalue(fs, n, var); /* will be a new upvalue */
|
||||
if (var->k == VLOCAL || var->k == VUPVAL) /* local or upvalue? */
|
||||
idx = newupvalue(fs, n, var); /* will be a new upvalue */
|
||||
else /* it is a global or a constant */
|
||||
return; /* don't need to do anything at this level */
|
||||
}
|
||||
init_var(var, VUPVAL, idx); /* new or old upvalue */
|
||||
init_exp(var, VUPVAL, idx); /* new or old upvalue */
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -393,7 +449,7 @@ static void singlevar (LexState *ls, expdesc *var) {
|
||||
expdesc key;
|
||||
singlevaraux(fs, ls->envn, var, 1); /* get environment variable */
|
||||
lua_assert(var->k != VVOID); /* this one must exist */
|
||||
codestring(ls, &key, varname); /* key is variable name */
|
||||
codestring(&key, varname); /* key is variable name */
|
||||
luaK_indexed(fs, var, &key); /* env[varname] */
|
||||
}
|
||||
}
|
||||
@@ -438,7 +494,7 @@ static void adjust_assign (LexState *ls, int nvars, int nexps, expdesc *e) {
|
||||
** local variable.
|
||||
*/
|
||||
static l_noret jumpscopeerror (LexState *ls, Labeldesc *gt) {
|
||||
const char *varname = getstr(getlocvar(ls->fs, gt->nactvar)->varname);
|
||||
const char *varname = getstr(getlocalvardesc(ls->fs, gt->nactvar)->vd.name);
|
||||
const char *msg = "<goto %s> at line %d jumps into the scope of local '%s'";
|
||||
msg = luaO_pushfstring(ls->L, msg, getstr(gt->name), gt->line, varname);
|
||||
luaK_semerror(ls, msg); /* raise the error */
|
||||
@@ -541,7 +597,7 @@ static int createlabel (LexState *ls, TString *name, int line,
|
||||
ll->arr[l].nactvar = fs->bl->nactvar;
|
||||
}
|
||||
if (solvegotos(ls, &ll->arr[l])) { /* need close? */
|
||||
luaK_codeABC(fs, OP_CLOSE, fs->nactvar, 0, 0);
|
||||
luaK_codeABC(fs, OP_CLOSE, luaY_nvarstack(fs), 0, 0);
|
||||
return 1;
|
||||
}
|
||||
return 0;
|
||||
@@ -557,10 +613,10 @@ static void movegotosout (FuncState *fs, BlockCnt *bl) {
|
||||
/* correct pending gotos to current block */
|
||||
for (i = bl->firstgoto; i < gl->n; i++) { /* for each pending goto */
|
||||
Labeldesc *gt = &gl->arr[i];
|
||||
if (gt->nactvar > bl->nactvar) { /* leaving a variable scope? */
|
||||
gt->nactvar = bl->nactvar; /* update goto level */
|
||||
/* leaving a variable scope? */
|
||||
if (stacklevel(fs, gt->nactvar) > stacklevel(fs, bl->nactvar))
|
||||
gt->close |= bl->upval; /* jump may need a close */
|
||||
}
|
||||
gt->nactvar = bl->nactvar; /* update goto level */
|
||||
}
|
||||
}
|
||||
|
||||
@@ -574,7 +630,7 @@ static void enterblock (FuncState *fs, BlockCnt *bl, lu_byte isloop) {
|
||||
bl->insidetbc = (fs->bl != NULL && fs->bl->insidetbc);
|
||||
bl->previous = fs->bl;
|
||||
fs->bl = bl;
|
||||
lua_assert(fs->freereg == fs->nactvar);
|
||||
lua_assert(fs->freereg == luaY_nvarstack(fs));
|
||||
}
|
||||
|
||||
|
||||
@@ -599,14 +655,15 @@ static void leaveblock (FuncState *fs) {
|
||||
BlockCnt *bl = fs->bl;
|
||||
LexState *ls = fs->ls;
|
||||
int hasclose = 0;
|
||||
int stklevel = stacklevel(fs, bl->nactvar); /* level outside the block */
|
||||
if (bl->isloop) /* fix pending breaks? */
|
||||
hasclose = createlabel(ls, luaS_newliteral(ls->L, "break"), 0, 0);
|
||||
if (!hasclose && bl->previous && bl->upval)
|
||||
luaK_codeABC(fs, OP_CLOSE, bl->nactvar, 0, 0);
|
||||
luaK_codeABC(fs, OP_CLOSE, stklevel, 0, 0);
|
||||
fs->bl = bl->previous;
|
||||
removevars(fs, bl->nactvar);
|
||||
lua_assert(bl->nactvar == fs->nactvar);
|
||||
fs->freereg = fs->nactvar; /* free registers */
|
||||
fs->freereg = stklevel; /* free registers */
|
||||
ls->dyd->label.n = bl->firstlabel; /* remove local labels */
|
||||
if (bl->previous) /* inner block? */
|
||||
movegotosout(fs, bl); /* update pending gotos to outer block */
|
||||
@@ -639,9 +696,10 @@ static Proto *addprototype (LexState *ls) {
|
||||
|
||||
/*
|
||||
** codes instruction to create new closure in parent function.
|
||||
** The OP_CLOSURE instruction must use the last available register,
|
||||
** The OP_CLOSURE instruction uses the last available register,
|
||||
** so that, if it invokes the GC, the GC knows which registers
|
||||
** are in use at that time.
|
||||
|
||||
*/
|
||||
static void codeclosure (LexState *ls, expdesc *v) {
|
||||
FuncState *fs = ls->fs->prev;
|
||||
@@ -664,7 +722,7 @@ static void open_func (LexState *ls, FuncState *fs, BlockCnt *bl) {
|
||||
fs->nabslineinfo = 0;
|
||||
fs->np = 0;
|
||||
fs->nups = 0;
|
||||
fs->nlocvars = 0;
|
||||
fs->ndebugvars = 0;
|
||||
fs->nactvar = 0;
|
||||
fs->needclose = 0;
|
||||
fs->firstlocal = ls->dyd->actvar.n;
|
||||
@@ -680,7 +738,7 @@ static void close_func (LexState *ls) {
|
||||
lua_State *L = ls->L;
|
||||
FuncState *fs = ls->fs;
|
||||
Proto *f = fs->f;
|
||||
luaK_ret(fs, fs->nactvar, 0); /* final return */
|
||||
luaK_ret(fs, luaY_nvarstack(fs), 0); /* final return */
|
||||
leaveblock(fs);
|
||||
lua_assert(fs->bl == NULL);
|
||||
luaK_finish(fs);
|
||||
@@ -690,7 +748,7 @@ static void close_func (LexState *ls) {
|
||||
fs->nabslineinfo, AbsLineInfo);
|
||||
luaM_shrinkvector(L, f->k, f->sizek, fs->nk, TValue);
|
||||
luaM_shrinkvector(L, f->p, f->sizep, fs->np, Proto *);
|
||||
luaM_shrinkvector(L, f->locvars, f->sizelocvars, fs->nlocvars, LocVar);
|
||||
luaM_shrinkvector(L, f->locvars, f->sizelocvars, fs->ndebugvars, LocVar);
|
||||
luaM_shrinkvector(L, f->upvalues, f->sizeupvalues, fs->nups, Upvaldesc);
|
||||
ls->fs = fs->prev;
|
||||
luaC_checkGC(L);
|
||||
@@ -758,16 +816,16 @@ static void yindex (LexState *ls, expdesc *v) {
|
||||
*/
|
||||
|
||||
|
||||
struct ConsControl {
|
||||
typedef struct ConsControl {
|
||||
expdesc v; /* last list item read */
|
||||
expdesc *t; /* table descriptor */
|
||||
int nh; /* total number of 'record' elements */
|
||||
int na; /* total number of array elements */
|
||||
int na; /* number of array elements already stored */
|
||||
int tostore; /* number of array elements pending to be stored */
|
||||
};
|
||||
} ConsControl;
|
||||
|
||||
|
||||
static void recfield (LexState *ls, struct ConsControl *cc) {
|
||||
static void recfield (LexState *ls, ConsControl *cc) {
|
||||
/* recfield -> (NAME | '['exp']') = exp */
|
||||
FuncState *fs = ls->fs;
|
||||
int reg = ls->fs->freereg;
|
||||
@@ -788,18 +846,19 @@ static void recfield (LexState *ls, struct ConsControl *cc) {
|
||||
}
|
||||
|
||||
|
||||
static void closelistfield (FuncState *fs, struct ConsControl *cc) {
|
||||
static void closelistfield (FuncState *fs, ConsControl *cc) {
|
||||
if (cc->v.k == VVOID) return; /* there is no list item */
|
||||
luaK_exp2nextreg(fs, &cc->v);
|
||||
cc->v.k = VVOID;
|
||||
if (cc->tostore == LFIELDS_PER_FLUSH) {
|
||||
luaK_setlist(fs, cc->t->u.info, cc->na, cc->tostore); /* flush */
|
||||
cc->na += cc->tostore;
|
||||
cc->tostore = 0; /* no more items pending */
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
static void lastlistfield (FuncState *fs, struct ConsControl *cc) {
|
||||
static void lastlistfield (FuncState *fs, ConsControl *cc) {
|
||||
if (cc->tostore == 0) return;
|
||||
if (hasmultret(cc->v.k)) {
|
||||
luaK_setmultret(fs, &cc->v);
|
||||
@@ -811,19 +870,18 @@ static void lastlistfield (FuncState *fs, struct ConsControl *cc) {
|
||||
luaK_exp2nextreg(fs, &cc->v);
|
||||
luaK_setlist(fs, cc->t->u.info, cc->na, cc->tostore);
|
||||
}
|
||||
cc->na += cc->tostore;
|
||||
}
|
||||
|
||||
|
||||
static void listfield (LexState *ls, struct ConsControl *cc) {
|
||||
static void listfield (LexState *ls, ConsControl *cc) {
|
||||
/* listfield -> exp */
|
||||
expr(ls, &cc->v);
|
||||
checklimit(ls->fs, cc->na, MAX_INT, "items in a constructor");
|
||||
cc->na++;
|
||||
cc->tostore++;
|
||||
}
|
||||
|
||||
|
||||
static void field (LexState *ls, struct ConsControl *cc) {
|
||||
static void field (LexState *ls, ConsControl *cc) {
|
||||
/* field -> listfield | recfield */
|
||||
switch(ls->t.token) {
|
||||
case TK_NAME: { /* may be 'listfield' or 'recfield' */
|
||||
@@ -851,12 +909,13 @@ static void constructor (LexState *ls, expdesc *t) {
|
||||
FuncState *fs = ls->fs;
|
||||
int line = ls->linenumber;
|
||||
int pc = luaK_codeABC(fs, OP_NEWTABLE, 0, 0, 0);
|
||||
struct ConsControl cc;
|
||||
ConsControl cc;
|
||||
luaK_code(fs, 0); /* space for extra arg. */
|
||||
cc.na = cc.nh = cc.tostore = 0;
|
||||
cc.t = t;
|
||||
init_exp(t, VRELOC, pc);
|
||||
init_exp(t, VNONRELOC, fs->freereg); /* table will be at stack top */
|
||||
luaK_reserveregs(fs, 1);
|
||||
init_exp(&cc.v, VVOID, 0); /* no value (yet) */
|
||||
luaK_exp2nextreg(ls->fs, t); /* fix it at stack top */
|
||||
checknext(ls, '{');
|
||||
do {
|
||||
lua_assert(cc.v.k == VVOID || cc.tostore > 0);
|
||||
@@ -866,8 +925,7 @@ static void constructor (LexState *ls, expdesc *t) {
|
||||
} while (testnext(ls, ',') || testnext(ls, ';'));
|
||||
check_match(ls, '}', '{', line);
|
||||
lastlistfield(fs, &cc);
|
||||
SETARG_B(fs->f->code[pc], luaO_int2fb(cc.na)); /* set initial array size */
|
||||
SETARG_C(fs->f->code[pc], luaO_int2fb(cc.nh)); /* set initial table size */
|
||||
luaK_settablesize(fs, pc, t->u.info, cc.na, cc.nh);
|
||||
}
|
||||
|
||||
/* }====================================================================== */
|
||||
@@ -966,7 +1024,7 @@ static void funcargs (LexState *ls, expdesc *f, int line) {
|
||||
break;
|
||||
}
|
||||
case TK_STRING: { /* funcargs -> STRING */
|
||||
codestring(ls, &args, ls->t.seminfo.ts);
|
||||
codestring(&args, ls->t.seminfo.ts);
|
||||
luaX_next(ls); /* must use 'seminfo' before 'next' */
|
||||
break;
|
||||
}
|
||||
@@ -1074,7 +1132,7 @@ static void simpleexp (LexState *ls, expdesc *v) {
|
||||
break;
|
||||
}
|
||||
case TK_STRING: {
|
||||
codestring(ls, v, ls->t.seminfo.ts);
|
||||
codestring(v, ls->t.seminfo.ts);
|
||||
break;
|
||||
}
|
||||
case TK_NIL: {
|
||||
@@ -1257,20 +1315,20 @@ static void check_conflict (LexState *ls, struct LHS_assign *lh, expdesc *v) {
|
||||
for (; lh; lh = lh->prev) { /* check all previous assignments */
|
||||
if (vkisindexed(lh->v.k)) { /* assignment to table field? */
|
||||
if (lh->v.k == VINDEXUP) { /* is table an upvalue? */
|
||||
if (v->k == VUPVAL && lh->v.u.ind.t == v->u.var.idx) {
|
||||
if (v->k == VUPVAL && lh->v.u.ind.t == v->u.info) {
|
||||
conflict = 1; /* table is the upvalue being assigned now */
|
||||
lh->v.k = VINDEXSTR;
|
||||
lh->v.u.ind.t = extra; /* assignment will use safe copy */
|
||||
}
|
||||
}
|
||||
else { /* table is a register */
|
||||
if (v->k == VLOCAL && lh->v.u.ind.t == v->u.var.idx) {
|
||||
if (v->k == VLOCAL && lh->v.u.ind.t == v->u.var.sidx) {
|
||||
conflict = 1; /* table is the local being assigned now */
|
||||
lh->v.u.ind.t = extra; /* assignment will use safe copy */
|
||||
}
|
||||
/* is index the local being assigned? */
|
||||
if (lh->v.k == VINDEXED && v->k == VLOCAL &&
|
||||
lh->v.u.ind.idx == v->u.var.idx) {
|
||||
lh->v.u.ind.idx == v->u.var.sidx) {
|
||||
conflict = 1;
|
||||
lh->v.u.ind.idx = extra; /* previous assignment will use safe copy */
|
||||
}
|
||||
@@ -1279,14 +1337,16 @@ static void check_conflict (LexState *ls, struct LHS_assign *lh, expdesc *v) {
|
||||
}
|
||||
if (conflict) {
|
||||
/* copy upvalue/local value to a temporary (in position 'extra') */
|
||||
OpCode op = (v->k == VLOCAL) ? OP_MOVE : OP_GETUPVAL;
|
||||
luaK_codeABC(fs, op, extra, v->u.var.idx, 0);
|
||||
if (v->k == VLOCAL)
|
||||
luaK_codeABC(fs, OP_MOVE, extra, v->u.var.sidx, 0);
|
||||
else
|
||||
luaK_codeABC(fs, OP_GETUPVAL, extra, v->u.info, 0);
|
||||
luaK_reserveregs(fs, 1);
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
** Parse and compile a mulitple assignment. The first "variable"
|
||||
** Parse and compile a multiple assignment. The first "variable"
|
||||
** (a 'suffixedexp') was already read by the caller.
|
||||
**
|
||||
** assignment -> suffixedexp restassign
|
||||
@@ -1343,8 +1403,9 @@ static void gotostat (LexState *ls) {
|
||||
newgotoentry(ls, name, line, luaK_jump(fs));
|
||||
else { /* found a label */
|
||||
/* backward jump; will be resolved here */
|
||||
if (fs->nactvar > lb->nactvar) /* leaving the scope of some variable? */
|
||||
luaK_codeABC(fs, OP_CLOSE, lb->nactvar, 0, 0);
|
||||
int lblevel = stacklevel(fs, lb->nactvar); /* label level */
|
||||
if (luaY_nvarstack(fs) > lblevel) /* leaving the scope of a variable? */
|
||||
luaK_codeABC(fs, OP_CLOSE, lblevel, 0, 0);
|
||||
/* create jump and link it to the label */
|
||||
luaK_patchlist(fs, luaK_jump(fs), lb->pc);
|
||||
}
|
||||
@@ -1419,7 +1480,7 @@ static void repeatstat (LexState *ls, int line) {
|
||||
if (bl2.upval) { /* upvalues? */
|
||||
int exit = luaK_jump(fs); /* normal exit must jump over fix */
|
||||
luaK_patchtohere(fs, condexit); /* repetition must close upvalues */
|
||||
luaK_codeABC(fs, OP_CLOSE, bl2.nactvar, 0, 0);
|
||||
luaK_codeABC(fs, OP_CLOSE, stacklevel(fs, bl2.nactvar), 0, 0);
|
||||
condexit = luaK_jump(fs); /* repeat after closing upvalues */
|
||||
luaK_patchtohere(fs, exit); /* normal exit comes to here */
|
||||
}
|
||||
@@ -1462,8 +1523,8 @@ static void fixforjump (FuncState *fs, int pc, int dest, int back) {
|
||||
*/
|
||||
static void forbody (LexState *ls, int base, int line, int nvars, int isgen) {
|
||||
/* forbody -> DO block */
|
||||
static OpCode forprep[2] = {OP_FORPREP, OP_TFORPREP};
|
||||
static OpCode forloop[2] = {OP_FORLOOP, OP_TFORLOOP};
|
||||
static const OpCode forprep[2] = {OP_FORPREP, OP_TFORPREP};
|
||||
static const OpCode forloop[2] = {OP_FORLOOP, OP_TFORLOOP};
|
||||
BlockCnt bl;
|
||||
FuncState *fs = ls->fs;
|
||||
int prep, endfor;
|
||||
@@ -1478,7 +1539,6 @@ static void forbody (LexState *ls, int base, int line, int nvars, int isgen) {
|
||||
if (isgen) { /* generic for? */
|
||||
luaK_codeABC(fs, OP_TFORCALL, base, 0, nvars);
|
||||
luaK_fixline(fs, line);
|
||||
base += 2; /* base for 'OP_TFORLOOP' (skips function and state) */
|
||||
}
|
||||
endfor = luaK_codeABx(fs, forloop[isgen], base, 0);
|
||||
fixforjump(fs, endfor, prep + 1, 1);
|
||||
@@ -1490,9 +1550,9 @@ static void fornum (LexState *ls, TString *varname, int line) {
|
||||
/* fornum -> NAME = exp,exp[,exp] forbody */
|
||||
FuncState *fs = ls->fs;
|
||||
int base = fs->freereg;
|
||||
new_localvarliteral(ls, "(for index)");
|
||||
new_localvarliteral(ls, "(for limit)");
|
||||
new_localvarliteral(ls, "(for step)");
|
||||
new_localvarliteral(ls, "(for state)");
|
||||
new_localvarliteral(ls, "(for state)");
|
||||
new_localvarliteral(ls, "(for state)");
|
||||
new_localvar(ls, varname);
|
||||
checknext(ls, '=');
|
||||
exp1(ls); /* initial value */
|
||||
@@ -1517,11 +1577,10 @@ static void forlist (LexState *ls, TString *indexname) {
|
||||
int line;
|
||||
int base = fs->freereg;
|
||||
/* create control variables */
|
||||
new_localvarliteral(ls, "(for generator)");
|
||||
new_localvarliteral(ls, "(for state)");
|
||||
markupval(fs, fs->nactvar); /* state may create an upvalue */
|
||||
new_localvarliteral(ls, "(for control)");
|
||||
new_localvarliteral(ls, "(for toclose)");
|
||||
new_localvarliteral(ls, "(for state)");
|
||||
new_localvarliteral(ls, "(for state)");
|
||||
new_localvarliteral(ls, "(for state)");
|
||||
/* create declared variables */
|
||||
new_localvar(ls, indexname);
|
||||
while (testnext(ls, ',')) {
|
||||
@@ -1532,6 +1591,7 @@ static void forlist (LexState *ls, TString *indexname) {
|
||||
line = ls->linenumber;
|
||||
adjust_assign(ls, 4, explist(ls, &e), &e);
|
||||
adjustlocalvars(ls, 4); /* control variables */
|
||||
markupval(fs, luaY_nvarstack(fs)); /* state may create an upvalue */
|
||||
luaK_checkstack(fs, 3); /* extra space to call generator */
|
||||
forbody(ls, base, line, nvars - 4, 1);
|
||||
}
|
||||
@@ -1574,7 +1634,8 @@ static int issinglejump (LexState *ls, TString **label, int *target) {
|
||||
TString *lname = ls->lookahead.seminfo.ts; /* label's id */
|
||||
Labeldesc *lb = findlabel(ls, lname);
|
||||
if (lb) { /* a backward jump? */
|
||||
if (ls->fs->nactvar > lb->nactvar) /* needs to close variables? */
|
||||
/* 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;
|
||||
}
|
||||
@@ -1646,21 +1707,60 @@ static void ifstat (LexState *ls, int line) {
|
||||
static void localfunc (LexState *ls) {
|
||||
expdesc b;
|
||||
FuncState *fs = ls->fs;
|
||||
int fvar = fs->nactvar; /* function's variable index */
|
||||
new_localvar(ls, str_checkname(ls)); /* new local variable */
|
||||
adjustlocalvars(ls, 1); /* enter its scope */
|
||||
body(ls, &b, 0, ls->linenumber); /* function created in next register */
|
||||
/* debug information will only see the variable after this point! */
|
||||
getlocvar(fs, b.u.info)->startpc = fs->pc;
|
||||
localdebuginfo(fs, fvar)->startpc = fs->pc;
|
||||
}
|
||||
|
||||
|
||||
static void commonlocalstat (LexState *ls) {
|
||||
/* stat -> LOCAL NAME {',' NAME} ['=' explist] */
|
||||
static int getlocalattribute (LexState *ls) {
|
||||
/* ATTRIB -> ['<' Name '>'] */
|
||||
if (testnext(ls, '<')) {
|
||||
const char *attr = getstr(str_checkname(ls));
|
||||
checknext(ls, '>');
|
||||
if (strcmp(attr, "const") == 0)
|
||||
return RDKCONST; /* read-only variable */
|
||||
else if (strcmp(attr, "close") == 0)
|
||||
return RDKTOCLOSE; /* to-be-closed variable */
|
||||
else
|
||||
luaK_semerror(ls,
|
||||
luaO_pushfstring(ls->L, "unknown attribute '%s'", attr));
|
||||
}
|
||||
return VDKREG;
|
||||
}
|
||||
|
||||
|
||||
static void checktoclose (LexState *ls, int level) {
|
||||
if (level != -1) { /* is there a to-be-closed variable? */
|
||||
FuncState *fs = ls->fs;
|
||||
markupval(fs, level + 1);
|
||||
fs->bl->insidetbc = 1; /* in the scope of a to-be-closed variable */
|
||||
luaK_codeABC(fs, OP_TBC, level, 0, 0);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
static void localstat (LexState *ls) {
|
||||
/* stat -> LOCAL ATTRIB NAME {',' ATTRIB NAME} ['=' explist] */
|
||||
FuncState *fs = ls->fs;
|
||||
int toclose = -1; /* index of to-be-closed variable (if any) */
|
||||
Vardesc *var; /* last variable */
|
||||
int ivar, kind; /* index and kind of last variable */
|
||||
int nvars = 0;
|
||||
int nexps;
|
||||
expdesc e;
|
||||
do {
|
||||
new_localvar(ls, str_checkname(ls));
|
||||
ivar = new_localvar(ls, str_checkname(ls));
|
||||
kind = getlocalattribute(ls);
|
||||
getlocalvardesc(fs, ivar)->vd.kind = kind;
|
||||
if (kind == RDKTOCLOSE) { /* to-be-closed? */
|
||||
if (toclose != -1) /* one already present? */
|
||||
luaK_semerror(ls, "multiple to-be-closed variables in local list");
|
||||
toclose = luaY_nvarstack(fs) + nvars;
|
||||
}
|
||||
nvars++;
|
||||
} while (testnext(ls, ','));
|
||||
if (testnext(ls, '='))
|
||||
@@ -1669,45 +1769,19 @@ static void commonlocalstat (LexState *ls) {
|
||||
e.k = VVOID;
|
||||
nexps = 0;
|
||||
}
|
||||
adjust_assign(ls, nvars, nexps, &e);
|
||||
adjustlocalvars(ls, nvars);
|
||||
}
|
||||
|
||||
|
||||
static void tocloselocalstat (LexState *ls, Vardesc *var) {
|
||||
FuncState *fs = ls->fs;
|
||||
var->ro = 1; /* to-be-closed variables are always read-only */
|
||||
markupval(fs, fs->nactvar);
|
||||
fs->bl->insidetbc = 1; /* in the scope of a to-be-closed variable */
|
||||
luaK_codeABC(fs, OP_TBC, fs->nactvar - 1, 0, 0);
|
||||
}
|
||||
|
||||
|
||||
static void attriblocalstat (LexState *ls) {
|
||||
Vardesc *var;
|
||||
TString *attr = str_checkname(ls);
|
||||
testnext(ls, '>');
|
||||
var = new_localvar(ls, str_checkname(ls));
|
||||
checknext(ls, '=');
|
||||
exp1(ls);
|
||||
adjustlocalvars(ls, 1);
|
||||
if (strcmp(getstr(attr), "const") == 0)
|
||||
var->ro = 1; /* set variable as read-only */
|
||||
else if (strcmp(getstr(attr), "toclose") == 0)
|
||||
tocloselocalstat(ls, var);
|
||||
else
|
||||
luaK_semerror(ls,
|
||||
luaO_pushfstring(ls->L, "unknown attribute '%s'", getstr(attr)));
|
||||
}
|
||||
|
||||
|
||||
static void localstat (LexState *ls) {
|
||||
/* stat -> LOCAL NAME {',' NAME} ['=' explist]
|
||||
| LOCAL *toclose NAME '=' exp */
|
||||
if (testnext(ls, '<'))
|
||||
attriblocalstat(ls);
|
||||
else
|
||||
commonlocalstat(ls);
|
||||
var = getlocalvardesc(fs, ivar); /* get last variable */
|
||||
if (nvars == nexps && /* no adjustments? */
|
||||
var->vd.kind == RDKCONST && /* last variable is const? */
|
||||
luaK_exp2const(fs, &e, &var->k)) { /* compile-time constant? */
|
||||
var->vd.kind = RDKCTC; /* variable is a compile-time constant */
|
||||
adjustlocalvars(ls, nvars - 1); /* exclude last variable */
|
||||
fs->nactvar++; /* but count it */
|
||||
}
|
||||
else {
|
||||
adjust_assign(ls, nvars, nexps, &e);
|
||||
adjustlocalvars(ls, nvars);
|
||||
}
|
||||
checktoclose(ls, toclose);
|
||||
}
|
||||
|
||||
|
||||
@@ -1759,7 +1833,7 @@ static void retstat (LexState *ls) {
|
||||
FuncState *fs = ls->fs;
|
||||
expdesc e;
|
||||
int nret; /* number of values being returned */
|
||||
int first = fs->nactvar; /* first slot to be returned */
|
||||
int first = luaY_nvarstack(fs); /* first slot to be returned */
|
||||
if (block_follow(ls, 1) || ls->t.token == ';')
|
||||
nret = 0; /* return no values */
|
||||
else {
|
||||
@@ -1768,7 +1842,7 @@ static void retstat (LexState *ls) {
|
||||
luaK_setmultret(fs, &e);
|
||||
if (e.k == VCALL && nret == 1 && !fs->bl->insidetbc) { /* tail call? */
|
||||
SET_OPCODE(getinstruction(fs,&e), OP_TAILCALL);
|
||||
lua_assert(GETARG_A(getinstruction(fs,&e)) == fs->nactvar);
|
||||
lua_assert(GETARG_A(getinstruction(fs,&e)) == luaY_nvarstack(fs));
|
||||
}
|
||||
nret = LUA_MULTRET; /* return all values */
|
||||
}
|
||||
@@ -1853,8 +1927,8 @@ static void statement (LexState *ls) {
|
||||
}
|
||||
}
|
||||
lua_assert(ls->fs->f->maxstacksize >= ls->fs->freereg &&
|
||||
ls->fs->freereg >= ls->fs->nactvar);
|
||||
ls->fs->freereg = ls->fs->nactvar; /* free registers */
|
||||
ls->fs->freereg >= luaY_nvarstack(ls->fs));
|
||||
ls->fs->freereg = luaY_nvarstack(ls->fs); /* free registers */
|
||||
leavelevel(ls);
|
||||
}
|
||||
|
||||
@@ -1867,11 +1941,14 @@ static void statement (LexState *ls) {
|
||||
*/
|
||||
static void mainfunc (LexState *ls, FuncState *fs) {
|
||||
BlockCnt bl;
|
||||
expdesc v;
|
||||
Upvaldesc *env;
|
||||
open_func(ls, fs, &bl);
|
||||
setvararg(fs, 0); /* main function is always declared vararg */
|
||||
init_var(&v, VLOCAL, 0); /* create and... */
|
||||
newupvalue(fs, ls->envn, &v); /* ...set environment upvalue */
|
||||
env = allocupvalue(fs); /* ...set environment upvalue */
|
||||
env->instack = 1;
|
||||
env->idx = 0;
|
||||
env->kind = VDKREG;
|
||||
env->name = ls->envn;
|
||||
luaX_next(ls); /* read first token */
|
||||
statlist(ls); /* parse main body */
|
||||
check(ls, TK_EOS);
|
||||
|
||||
@@ -30,11 +30,15 @@ typedef enum {
|
||||
VFALSE, /* constant false */
|
||||
VK, /* constant in 'k'; info = index of constant in 'k' */
|
||||
VKFLT, /* floating constant; nval = numerical float value */
|
||||
VKINT, /* integer constant; nval = numerical integer value */
|
||||
VKINT, /* integer constant; ival = numerical integer value */
|
||||
VKSTR, /* string constant; strval = TString address;
|
||||
(string is fixed by the lexer) */
|
||||
VNONRELOC, /* expression has its value in a fixed register;
|
||||
info = result register */
|
||||
VLOCAL, /* local variable; var.idx = local register */
|
||||
VUPVAL, /* upvalue variable; var.idx = index of upvalue in 'upvalues' */
|
||||
VLOCAL, /* local variable; var.ridx = local register;
|
||||
var.vidx = relative index in 'actvar.arr' */
|
||||
VUPVAL, /* upvalue variable; info = index of upvalue in 'upvalues' */
|
||||
VCONST, /* compile-time constant; info = absolute index in 'actvar.arr' */
|
||||
VINDEXED, /* indexed variable;
|
||||
ind.t = table register;
|
||||
ind.idx = key's R index */
|
||||
@@ -65,13 +69,15 @@ typedef struct expdesc {
|
||||
union {
|
||||
lua_Integer ival; /* for VKINT */
|
||||
lua_Number nval; /* for VKFLT */
|
||||
TString *strval; /* for VKSTR */
|
||||
int info; /* for generic use */
|
||||
struct { /* for indexed variables */
|
||||
short idx; /* index (R or "long" K) */
|
||||
lu_byte t; /* table (register or upvalue) */
|
||||
} ind;
|
||||
struct { /* for local variables and upvalues */
|
||||
lu_byte idx; /* index of the variable */
|
||||
struct { /* for local variables */
|
||||
lu_byte sidx; /* index in the stack */
|
||||
unsigned short vidx; /* index in 'actvar.arr' */
|
||||
} var;
|
||||
} u;
|
||||
int t; /* patch list of 'exit when true' */
|
||||
@@ -79,20 +85,32 @@ typedef struct expdesc {
|
||||
} expdesc;
|
||||
|
||||
|
||||
/* kinds of variables */
|
||||
#define VDKREG 0 /* regular */
|
||||
#define RDKCONST 1 /* constant */
|
||||
#define RDKTOCLOSE 2 /* to-be-closed */
|
||||
#define RDKCTC 3 /* compile-time constant */
|
||||
|
||||
/* description of an active local variable */
|
||||
typedef struct Vardesc {
|
||||
TString *name;
|
||||
short idx; /* index of the variable in the Proto's 'locvars' array */
|
||||
lu_byte ro; /* true if variable is 'const' */
|
||||
typedef union Vardesc {
|
||||
struct {
|
||||
TValuefields; /* constant value (if it is a compile-time constant) */
|
||||
lu_byte kind;
|
||||
lu_byte sidx; /* index of the variable in the stack */
|
||||
short pidx; /* index of the variable in the Proto's 'locvars' array */
|
||||
TString *name; /* variable name */
|
||||
} vd;
|
||||
TValue k; /* constant value (if any) */
|
||||
} Vardesc;
|
||||
|
||||
|
||||
|
||||
/* description of pending goto statements and label statements */
|
||||
typedef struct Labeldesc {
|
||||
TString *name; /* label identifier */
|
||||
int pc; /* position in code */
|
||||
int line; /* line where it appeared */
|
||||
lu_byte nactvar; /* local level where it appears in current block */
|
||||
lu_byte nactvar; /* number of active variables in that position */
|
||||
lu_byte close; /* goto that escapes upvalues */
|
||||
} Labeldesc;
|
||||
|
||||
@@ -135,7 +153,7 @@ typedef struct FuncState {
|
||||
int nabslineinfo; /* number of elements in 'abslineinfo' */
|
||||
int firstlocal; /* index of first local var (in Dyndata array) */
|
||||
int firstlabel; /* index of first label (in 'dyd->label->arr') */
|
||||
short nlocvars; /* number of elements in 'f->locvars' */
|
||||
short ndebugvars; /* number of elements in 'f->locvars' */
|
||||
lu_byte nactvar; /* number of active local variables */
|
||||
lu_byte nups; /* number of upvalues */
|
||||
lu_byte freereg; /* first free register */
|
||||
@@ -144,6 +162,7 @@ typedef struct FuncState {
|
||||
} FuncState;
|
||||
|
||||
|
||||
LUAI_FUNC int luaY_nvarstack (FuncState *fs);
|
||||
LUAI_FUNC LClosure *luaY_parser (lua_State *L, ZIO *z, Mbuffer *buff,
|
||||
Dyndata *dyd, const char *name, int firstchar);
|
||||
|
||||
|
||||
@@ -96,36 +96,58 @@ void luaE_setdebt (global_State *g, l_mem debt) {
|
||||
}
|
||||
|
||||
|
||||
LUA_API int lua_setcstacklimit (lua_State *L, unsigned int limit) {
|
||||
global_State *g = G(L);
|
||||
int ccalls;
|
||||
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 */
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
/*
|
||||
** Increment count of "C calls" and check for overflows. In case of
|
||||
** 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 larger than LUAI_MAXCSTACK but smaller than
|
||||
** LUAI_MAXCSTACK + CSTACKCF (plus 2 to avoid by-one errors), it means
|
||||
** it has just entered the "overflow zone", so the function raises an
|
||||
** overflow error.
|
||||
** If 'nCcalls' is larger than LUAI_MAXCSTACK + CSTACKCF + 2
|
||||
** (which means it is already handling an overflow) but smaller than
|
||||
** 9/8 of LUAI_MAXCSTACK, 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).
|
||||
** 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 >= LUAI_MAXCSTACK) { /* possible overflow? */
|
||||
L->nCcalls--;
|
||||
if (ncalls <= CSTACKERR) { /* possible overflow? */
|
||||
luaE_freeCI(L); /* release unused CIs */
|
||||
ncalls = getCcalls(L); /* update call count */
|
||||
if (ncalls >= LUAI_MAXCSTACK) { /* still overflow? */
|
||||
if (ncalls <= LUAI_MAXCSTACK + CSTACKCF + 2) {
|
||||
/* no error before increments; raise the error now */
|
||||
L->nCcalls += (CSTACKCF + 4); /* avoid raising it again */
|
||||
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 if (ncalls >= (LUAI_MAXCSTACK + (LUAI_MAXCSTACK >> 3)))
|
||||
luaD_throw(L, LUA_ERRERR); /* error while handling stack error */
|
||||
/* else stack is in the error-handling zone;
|
||||
allow message handler to work */
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -153,13 +175,13 @@ void luaE_freeCI (lua_State *L) {
|
||||
CallInfo *ci = L->ci;
|
||||
CallInfo *next = ci->next;
|
||||
ci->next = NULL;
|
||||
L->nCcalls -= L->nci; /* subtract removed elements from 'nCcalls' */
|
||||
L->nCcalls += L->nci; /* add removed elements back to 'nCcalls' */
|
||||
while ((ci = next) != NULL) {
|
||||
next = ci->next;
|
||||
luaM_free(L, ci);
|
||||
L->nci--;
|
||||
}
|
||||
L->nCcalls += L->nci; /* adjust result */
|
||||
L->nCcalls -= L->nci; /* adjust result */
|
||||
}
|
||||
|
||||
|
||||
@@ -169,7 +191,7 @@ void luaE_freeCI (lua_State *L) {
|
||||
void luaE_shrinkCI (lua_State *L) {
|
||||
CallInfo *ci = L->ci;
|
||||
CallInfo *next2; /* next's next */
|
||||
L->nCcalls -= L->nci; /* subtract removed elements from 'nCcalls' */
|
||||
L->nCcalls += L->nci; /* add removed elements back to 'nCcalls' */
|
||||
/* while there are two nexts */
|
||||
while (ci->next != NULL && (next2 = ci->next->next) != NULL) {
|
||||
luaM_free(L, ci->next); /* free next */
|
||||
@@ -178,7 +200,7 @@ void luaE_shrinkCI (lua_State *L) {
|
||||
next2->previous = ci;
|
||||
ci = next2; /* keep next's next */
|
||||
}
|
||||
L->nCcalls += L->nci; /* adjust result */
|
||||
L->nCcalls -= L->nci; /* adjust result */
|
||||
}
|
||||
|
||||
|
||||
@@ -264,7 +286,7 @@ static void preinit_thread (lua_State *L, global_State *g) {
|
||||
L->stacksize = 0;
|
||||
L->twups = L; /* thread has no upvalues */
|
||||
L->errorJmp = NULL;
|
||||
L->nCcalls = 0;
|
||||
L->nCcalls = CSTACKTHREAD;
|
||||
L->hook = NULL;
|
||||
L->hookmask = 0;
|
||||
L->basehookcount = 0;
|
||||
@@ -366,6 +388,7 @@ LUA_API lua_State *lua_newstate (lua_Alloc f, void *ud) {
|
||||
preinit_thread(L, g);
|
||||
g->allgc = obj2gco(L); /* by now, only object is the main thread */
|
||||
L->next = NULL;
|
||||
g->Cstacklimit = L->nCcalls = LUAI_MAXCSTACK + CSTACKERR;
|
||||
g->frealloc = f;
|
||||
g->ud = ud;
|
||||
g->warnf = NULL;
|
||||
@@ -420,3 +443,19 @@ void luaE_warning (lua_State *L, const char *msg, int tocont) {
|
||||
}
|
||||
|
||||
|
||||
/*
|
||||
** Generate a warning from an error message
|
||||
*/
|
||||
void luaE_warnerror (lua_State *L, const char *where) {
|
||||
TValue *errobj = s2v(L->top - 1); /* error object */
|
||||
const char *msg = (ttisstring(errobj))
|
||||
? svalue(errobj)
|
||||
: "error object is not a string";
|
||||
/* produce warning "error in %s (%s)" (where, msg) */
|
||||
luaE_warning(L, "error in ", 1);
|
||||
luaE_warning(L, where, 1);
|
||||
luaE_warning(L, " (", 1);
|
||||
luaE_warning(L, msg, 1);
|
||||
luaE_warning(L, ")", 0);
|
||||
}
|
||||
|
||||
|
||||
@@ -64,28 +64,49 @@
|
||||
|
||||
/*
|
||||
** About 'nCcalls': each thread in Lua (a lua_State) keeps a count of
|
||||
** how many "C calls" it can do in the C stack, to avoid C-stack overflow.
|
||||
** This count is very rough approximation; it considers only recursive
|
||||
** functions inside the interpreter, as non-recursive calls can be
|
||||
** considered using a fixed (although unknown) amount of stack space.
|
||||
** how many "C calls" it still can do in the C stack, to avoid C-stack
|
||||
** overflow. This count is very rough approximation; it considers only
|
||||
** recursive functions inside the interpreter, as non-recursive calls
|
||||
** can be considered using a fixed (although unknown) amount of stack
|
||||
** space.
|
||||
**
|
||||
** The count itself has two parts: the lower part is the count itself;
|
||||
** the higher part counts the number of non-yieldable calls in the stack.
|
||||
** 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 of unkown amount
|
||||
** 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.
|
||||
** to these functions add more than one to the count (see CSTACKCF).
|
||||
**
|
||||
** The proper count also includes 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
|
||||
** increment nor to check 'nCcalls', as its use of C stack is already
|
||||
** accounted for.
|
||||
** 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 */
|
||||
#define yieldable(L) (((L)->nCcalls & 0xffff0000) == 0)
|
||||
|
||||
@@ -99,11 +120,11 @@
|
||||
/* Decrement the number of non-yieldable calls */
|
||||
#define decnny(L) ((L)->nCcalls -= 0x10000)
|
||||
|
||||
/* Increment the number of non-yieldable calls and nCcalls */
|
||||
#define incXCcalls(L) ((L)->nCcalls += 0x10000 + CSTACKCF)
|
||||
/* Increment the number of non-yieldable calls and decrement nCcalls */
|
||||
#define incXCcalls(L) ((L)->nCcalls += 0x10000 - CSTACKCF)
|
||||
|
||||
/* Decrement the number of non-yieldable calls and nCcalls */
|
||||
#define decXCcalls(L) ((L)->nCcalls -= 0x10000 + CSTACKCF)
|
||||
/* Decrement the number of non-yieldable calls and increment nCcalls */
|
||||
#define decXCcalls(L) ((L)->nCcalls -= 0x10000 - CSTACKCF)
|
||||
|
||||
|
||||
|
||||
@@ -164,9 +185,9 @@ typedef struct CallInfo {
|
||||
union {
|
||||
int funcidx; /* called-function index */
|
||||
int nyield; /* number of values yielded */
|
||||
struct { /* info about transfered values (for call/return hooks) */
|
||||
unsigned short ftransfer; /* offset of first value transfered */
|
||||
unsigned short ntransfer; /* number of values transfered */
|
||||
struct { /* info about transferred values (for call/return hooks) */
|
||||
unsigned short ftransfer; /* offset of first value transferred */
|
||||
unsigned short ntransfer; /* number of values transferred */
|
||||
} transferinfo;
|
||||
} u2;
|
||||
short nresults; /* expected number of results from this function */
|
||||
@@ -250,6 +271,7 @@ typedef struct global_State {
|
||||
TString *strcache[STRCACHE_N][STRCACHE_M]; /* cache for strings in API */
|
||||
lua_WarnFunction warnf; /* warning function */
|
||||
void *ud_warn; /* auxiliary data to 'warnf' */
|
||||
unsigned int Cstacklimit; /* current limit for the C stack */
|
||||
} global_State;
|
||||
|
||||
|
||||
@@ -313,8 +335,7 @@ union GCUnion {
|
||||
#define gco2t(o) check_exp((o)->tt == LUA_TTABLE, &((cast_u(o))->h))
|
||||
#define gco2p(o) check_exp((o)->tt == LUA_TPROTO, &((cast_u(o))->p))
|
||||
#define gco2th(o) check_exp((o)->tt == LUA_TTHREAD, &((cast_u(o))->th))
|
||||
#define gco2upv(o) \
|
||||
check_exp(novariant((o)->tt) == LUA_TUPVAL, &((cast_u(o))->upv))
|
||||
#define gco2upv(o) check_exp((o)->tt == LUA_TUPVAL, &((cast_u(o))->upv))
|
||||
|
||||
|
||||
/*
|
||||
@@ -334,9 +355,10 @@ LUAI_FUNC void luaE_freeCI (lua_State *L);
|
||||
LUAI_FUNC void luaE_shrinkCI (lua_State *L);
|
||||
LUAI_FUNC void luaE_enterCcall (lua_State *L);
|
||||
LUAI_FUNC void luaE_warning (lua_State *L, const char *msg, int tocont);
|
||||
LUAI_FUNC void luaE_warnerror (lua_State *L, const char *where);
|
||||
|
||||
|
||||
#define luaE_exitCcall(L) ((L)->nCcalls--)
|
||||
#define luaE_exitCcall(L) ((L)->nCcalls++)
|
||||
|
||||
#endif
|
||||
|
||||
|
||||
@@ -233,6 +233,17 @@ static int str_dump (lua_State *L) {
|
||||
** =======================================================
|
||||
*/
|
||||
|
||||
#if defined(LUA_NOCVTS2N) /* { */
|
||||
|
||||
/* no coercion from strings to numbers */
|
||||
|
||||
static const luaL_Reg stringmetamethods[] = {
|
||||
{"__index", NULL}, /* placeholder */
|
||||
{NULL, NULL}
|
||||
};
|
||||
|
||||
#else /* }{ */
|
||||
|
||||
static int tonum (lua_State *L, int arg) {
|
||||
if (lua_type(L, arg) == LUA_TNUMBER) { /* already a number? */
|
||||
lua_pushvalue(L, arg);
|
||||
@@ -311,6 +322,8 @@ static const luaL_Reg stringmetamethods[] = {
|
||||
{NULL, NULL}
|
||||
};
|
||||
|
||||
#endif /* } */
|
||||
|
||||
/* }====================================================== */
|
||||
|
||||
/*
|
||||
@@ -744,7 +757,7 @@ static int str_find_aux (lua_State *L, int find) {
|
||||
const char *p = luaL_checklstring(L, 2, &lp);
|
||||
size_t init = posrelatI(luaL_optinteger(L, 3, 1), ls) - 1;
|
||||
if (init > ls) { /* start after string's end? */
|
||||
lua_pushnil(L); /* cannot find anything */
|
||||
luaL_pushfail(L); /* cannot find anything */
|
||||
return 1;
|
||||
}
|
||||
/* explicit request or no special characters? */
|
||||
@@ -779,7 +792,7 @@ static int str_find_aux (lua_State *L, int find) {
|
||||
}
|
||||
} while (s1++ < ms.src_end && !anchor);
|
||||
}
|
||||
lua_pushnil(L); /* not found */
|
||||
luaL_pushfail(L); /* not found */
|
||||
return 1;
|
||||
}
|
||||
|
||||
@@ -1227,16 +1240,14 @@ static int str_format (lua_State *L) {
|
||||
nb = lua_number2strx(L, buff, maxitem, form,
|
||||
luaL_checknumber(L, arg));
|
||||
break;
|
||||
case 'e': case 'E': case 'f':
|
||||
case 'g': case 'G': {
|
||||
case 'f':
|
||||
maxitem = MAX_ITEMF; /* extra space for '%f' */
|
||||
buff = luaL_prepbuffsize(&b, maxitem);
|
||||
/* FALLTHROUGH */
|
||||
case 'e': case 'E': case 'g': case 'G': {
|
||||
lua_Number n = luaL_checknumber(L, arg);
|
||||
if (*(strfrmt - 1) == 'f' && l_mathop(fabs)(n) >= 1e100) {
|
||||
/* 'n' needs more than 99 digits */
|
||||
maxitem = MAX_ITEMF; /* extra space for '%f' */
|
||||
buff = luaL_prepbuffsize(&b, maxitem);
|
||||
}
|
||||
addlenmod(form, LUA_NUMBER_FRMLEN);
|
||||
nb = l_sprintf(buff, maxitem, form, (LUAI_UACNUMBER)n);
|
||||
nb = snprintf(buff, maxitem, form, (LUAI_UACNUMBER)n);
|
||||
break;
|
||||
}
|
||||
case 'p': {
|
||||
|
||||
@@ -833,39 +833,41 @@ static unsigned int binsearch (const TValue *array, unsigned int i,
|
||||
** and 'maxinteger' if t[maxinteger] is present.)
|
||||
** (In the next explanation, we use Lua indices, that is, with base 1.
|
||||
** The code itself uses base 0 when indexing the array part of the table.)
|
||||
** The code starts with 'limit', a position in the array part that may
|
||||
** be a boundary.
|
||||
** The code starts with 'limit = t->alimit', a position in the array
|
||||
** part that may be a boundary.
|
||||
**
|
||||
** (1) If 't[limit]' is empty, there must be a boundary before it.
|
||||
** As a common case (e.g., after 't[#t]=nil'), check whether 'hint-1'
|
||||
** As a common case (e.g., after 't[#t]=nil'), check whether 'limit-1'
|
||||
** is present. If so, it is a boundary. Otherwise, do a binary search
|
||||
** between 0 and limit to find a boundary. In both cases, try to
|
||||
** use this boundary as the new 'limit', as a hint for the next call.
|
||||
** use this boundary as the new 'alimit', as a hint for the next call.
|
||||
**
|
||||
** (2) If 't[limit]' is not empty and the array has more elements
|
||||
** after 'limit', try to find a boundary there. Again, try first
|
||||
** the special case (which should be quite frequent) where 'limit+1'
|
||||
** is empty, so that 'limit' is a boundary. Otherwise, check the
|
||||
** last element of the array part (set it as a new limit). If it is empty,
|
||||
** there must be a boundary between the old limit (present) and the new
|
||||
** limit (absent), which is found with a binary search. (This boundary
|
||||
** always can be a new limit.)
|
||||
** last element of the array part. If it is empty, there must be a
|
||||
** boundary between the old limit (present) and the last element
|
||||
** (absent), which is found with a binary search. (This boundary always
|
||||
** can be a new limit.)
|
||||
**
|
||||
** (3) The last case is when there are no elements in the array part
|
||||
** (limit == 0) or its last element (the new limit) is present.
|
||||
** In this case, must check the hash part. If there is no hash part,
|
||||
** the boundary is 0. Otherwise, if 'limit+1' is absent, 'limit' is
|
||||
** a boundary. Finally, if 'limit+1' is present, call 'hash_search'
|
||||
** to find a boundary in the hash part of the table. (In those
|
||||
** cases, the boundary is not inside the array part, and therefore
|
||||
** cannot be used as a new limit.)
|
||||
** In this case, must check the hash part. If there is no hash part
|
||||
** or 'limit+1' is absent, 'limit' is a boundary. Otherwise, call
|
||||
** 'hash_search' to find a boundary in the hash part of the table.
|
||||
** (In those cases, the boundary is not inside the array part, and
|
||||
** therefore cannot be used as a new limit.)
|
||||
*/
|
||||
lua_Unsigned luaH_getn (Table *t) {
|
||||
unsigned int limit = t->alimit;
|
||||
if (limit > 0 && isempty(&t->array[limit - 1])) {
|
||||
/* (1) there must be a boundary before 'limit' */
|
||||
if (limit > 0 && isempty(&t->array[limit - 1])) { /* (1)? */
|
||||
/* there must be a boundary before 'limit' */
|
||||
if (limit >= 2 && !isempty(&t->array[limit - 2])) {
|
||||
/* 'limit - 1' is a boundary; can it be a new limit? */
|
||||
if (ispow2realasize(t) && !ispow2(limit - 1)) {
|
||||
t->alimit = limit - 1;
|
||||
setnorealasize(t);
|
||||
setnorealasize(t); /* now 'alimit' is not the real size */
|
||||
}
|
||||
return limit - 1;
|
||||
}
|
||||
@@ -880,8 +882,8 @@ lua_Unsigned luaH_getn (Table *t) {
|
||||
}
|
||||
}
|
||||
/* 'limit' is zero or present in table */
|
||||
if (!limitequalsasize(t)) {
|
||||
/* (2) 'limit' > 0 and array has more elements after 'limit' */
|
||||
if (!limitequalsasize(t)) { /* (2)? */
|
||||
/* 'limit' > 0 and array has more elements after 'limit' */
|
||||
if (isempty(&t->array[limit])) /* 'limit + 1' is empty? */
|
||||
return limit; /* this is the boundary */
|
||||
/* else, try last element in the array */
|
||||
@@ -899,7 +901,7 @@ lua_Unsigned luaH_getn (Table *t) {
|
||||
lua_assert(limit == luaH_realasize(t) &&
|
||||
(limit == 0 || !isempty(&t->array[limit - 1])));
|
||||
if (isdummy(t) || isempty(luaH_getint(t, cast(lua_Integer, limit + 1))))
|
||||
return limit; /* 'limit + 1' is absent... */
|
||||
return limit; /* 'limit + 1' is absent */
|
||||
else /* 'limit + 1' is also present */
|
||||
return hash_search(t, limit);
|
||||
}
|
||||
@@ -908,6 +910,8 @@ lua_Unsigned luaH_getn (Table *t) {
|
||||
|
||||
#if defined(LUA_DEBUG)
|
||||
|
||||
/* export these functions for the test library */
|
||||
|
||||
Node *luaH_mainposition (const Table *t, const TValue *key) {
|
||||
return mainpositionTV(t, key);
|
||||
}
|
||||
|
||||
@@ -338,7 +338,7 @@ static IdxT choosePivot (IdxT lo, IdxT up, unsigned int rnd) {
|
||||
|
||||
|
||||
/*
|
||||
** QuickSort algorithm (recursive function)
|
||||
** Quicksort algorithm (recursive function)
|
||||
*/
|
||||
static void auxsort (lua_State *L, IdxT lo, IdxT up,
|
||||
unsigned int rnd) {
|
||||
|
||||
@@ -62,8 +62,10 @@ static void pushobject (lua_State *L, const TValue *o) {
|
||||
}
|
||||
|
||||
|
||||
static void badexit (const char *fmt, const char *s) {
|
||||
fprintf(stderr, fmt, s);
|
||||
static void badexit (const char *fmt, const char *s1, const char *s2) {
|
||||
fprintf(stderr, fmt, s1);
|
||||
if (s2)
|
||||
fprintf(stderr, "extra info: %s\n", s2);
|
||||
/* avoid assertion failures when exiting */
|
||||
l_memcontrol.numblocks = l_memcontrol.total = 0;
|
||||
exit(EXIT_FAILURE);
|
||||
@@ -72,39 +74,79 @@ static void badexit (const char *fmt, const char *s) {
|
||||
|
||||
static int tpanic (lua_State *L) {
|
||||
return (badexit("PANIC: unprotected error in call to Lua API (%s)\n",
|
||||
lua_tostring(L, -1)),
|
||||
lua_tostring(L, -1), NULL),
|
||||
0); /* do not return to Lua */
|
||||
}
|
||||
|
||||
|
||||
/*
|
||||
** Warning function for tests. Fist, it concatenates all parts of
|
||||
** a warning in buffer 'buff'. Then:
|
||||
** - messages starting with '#' are shown on standard output (used to
|
||||
** test explicit warnings);
|
||||
** - messages containing '@' are stored in global '_WARN' (used to test
|
||||
** errors that generate warnings);
|
||||
** a warning in buffer 'buff'. Then, it has three modes:
|
||||
** - 0.normal: messages starting with '#' are shown on standard output;
|
||||
** - other messages abort the tests (they represent real warning
|
||||
** conditions; the standard tests should not generate these conditions
|
||||
** unexpectedly).
|
||||
** unexpectedly);
|
||||
** - 1.allow: all messages are shown;
|
||||
** - 2.store: all warnings go to the global '_WARN';
|
||||
*/
|
||||
static void warnf (void *ud, const char *msg, int tocont) {
|
||||
lua_State *L = cast(lua_State *, ud);
|
||||
static char buff[200] = ""; /* should be enough for tests... */
|
||||
static int onoff = 0;
|
||||
static int mode = 0; /* start in normal mode */
|
||||
static int lasttocont = 0;
|
||||
if (!lasttocont && !tocont && *msg == '@') { /* control message? */
|
||||
if (buff[0] != '\0')
|
||||
badexit("Control warning during warning: %s\naborting...\n", msg, buff);
|
||||
if (strcmp(msg, "@off") == 0)
|
||||
onoff = 0;
|
||||
else if (strcmp(msg, "@on") == 0)
|
||||
onoff = 1;
|
||||
else if (strcmp(msg, "@normal") == 0)
|
||||
mode = 0;
|
||||
else if (strcmp(msg, "@allow") == 0)
|
||||
mode = 1;
|
||||
else if (strcmp(msg, "@store") == 0)
|
||||
mode = 2;
|
||||
else
|
||||
badexit("Invalid control warning in test mode: %s\naborting...\n",
|
||||
msg, NULL);
|
||||
return;
|
||||
}
|
||||
lasttocont = tocont;
|
||||
if (strlen(msg) >= sizeof(buff) - strlen(buff))
|
||||
badexit("%s", "warnf-buffer overflow");
|
||||
badexit("warnf-buffer overflow (%s)\n", msg, buff);
|
||||
strcat(buff, msg); /* add new message to current warning */
|
||||
if (!tocont) { /* message finished? */
|
||||
if (buff[0] == '#') /* expected warning? */
|
||||
printf("Expected Lua warning: %s\n", buff); /* print it */
|
||||
else if (strchr(buff, '@') != NULL) { /* warning for test purposes? */
|
||||
lua_State *L = cast(lua_State *, ud);
|
||||
lua_unlock(L);
|
||||
lua_pushstring(L, buff);
|
||||
lua_setglobal(L, "_WARN"); /* assign message to global '_WARN' */
|
||||
lua_lock(L);
|
||||
lua_unlock(L);
|
||||
if (lua_getglobal(L, "_WARN") == LUA_TNIL)
|
||||
lua_pop(L, 1); /* ok, no previous unexpected warning */
|
||||
else {
|
||||
badexit("Unhandled warning in store mode: %s\naborting...\n",
|
||||
lua_tostring(L, -1), buff);
|
||||
}
|
||||
lua_lock(L);
|
||||
switch (mode) {
|
||||
case 0: { /* normal */
|
||||
if (buff[0] != '#' && onoff) /* unexpected warning? */
|
||||
badexit("Unexpected warning in test mode: %s\naborting...\n",
|
||||
buff, NULL);
|
||||
/* else */ /* FALLTHROUGH */
|
||||
}
|
||||
case 1: { /* allow */
|
||||
if (onoff)
|
||||
fprintf(stderr, "Lua warning: %s\n", buff); /* print warning */
|
||||
break;
|
||||
}
|
||||
case 2: { /* store */
|
||||
lua_unlock(L);
|
||||
lua_pushstring(L, buff);
|
||||
lua_setglobal(L, "_WARN"); /* assign message to global '_WARN' */
|
||||
lua_lock(L);
|
||||
buff[0] = '\0'; /* prepare buffer for next warning */
|
||||
break;
|
||||
}
|
||||
}
|
||||
else /* a real warning; should not happen during tests */
|
||||
badexit("Unexpected warning in test mode: %s\naborting...\n", buff);
|
||||
buff[0] = '\0'; /* prepare buffer for next warning */
|
||||
}
|
||||
}
|
||||
@@ -397,8 +439,7 @@ static void checkrefs (global_State *g, GCObject *o) {
|
||||
checkudata(g, gco2u(o));
|
||||
break;
|
||||
}
|
||||
case LUA_TUPVAL:
|
||||
case LUA_TUPVALTBC: {
|
||||
case LUA_TUPVAL: {
|
||||
checkvalref(g, o, gco2upv(o)->v);
|
||||
break;
|
||||
}
|
||||
@@ -1103,14 +1144,6 @@ static int doremote (lua_State *L) {
|
||||
}
|
||||
|
||||
|
||||
static int int2fb_aux (lua_State *L) {
|
||||
int b = luaO_int2fb((unsigned int)luaL_checkinteger(L, 1));
|
||||
lua_pushinteger(L, b);
|
||||
lua_pushinteger(L, (unsigned int)luaO_fb2int(b));
|
||||
return 2;
|
||||
}
|
||||
|
||||
|
||||
static int log2_aux (lua_State *L) {
|
||||
unsigned int x = (unsigned int)luaL_checkinteger(L, 1);
|
||||
lua_pushinteger(L, luaO_ceillog2(x));
|
||||
@@ -1488,6 +1521,10 @@ static int runC (lua_State *L, lua_State *L1, const char *pc) {
|
||||
else if EQ("pushfstringP") {
|
||||
lua_pushfstring(L1, lua_tostring(L, -2), lua_topointer(L, -1));
|
||||
}
|
||||
else if EQ("rawget") {
|
||||
int t = getindex;
|
||||
lua_rawget(L1, t);
|
||||
}
|
||||
else if EQ("rawgeti") {
|
||||
int t = getindex;
|
||||
lua_rawgeti(L1, t, getnum);
|
||||
@@ -1496,6 +1533,14 @@ static int runC (lua_State *L, lua_State *L1, const char *pc) {
|
||||
int t = getindex;
|
||||
lua_rawgetp(L1, t, cast_voidp(cast_sizet(getnum)));
|
||||
}
|
||||
else if EQ("rawset") {
|
||||
int t = getindex;
|
||||
lua_rawset(L1, t);
|
||||
}
|
||||
else if EQ("rawseti") {
|
||||
int t = getindex;
|
||||
lua_rawseti(L1, t, getnum);
|
||||
}
|
||||
else if EQ("rawsetp") {
|
||||
int t = getindex;
|
||||
lua_rawsetp(L1, t, cast_voidp(cast_sizet(getnum)));
|
||||
@@ -1538,6 +1583,10 @@ static int runC (lua_State *L, lua_State *L1, const char *pc) {
|
||||
const char *s = getstring;
|
||||
lua_setfield(L1, t, s);
|
||||
}
|
||||
else if EQ("seti") {
|
||||
int t = getindex;
|
||||
lua_seti(L1, t, getnum);
|
||||
}
|
||||
else if EQ("setglobal") {
|
||||
const char *s = getstring;
|
||||
lua_setglobal(L1, s);
|
||||
@@ -1565,6 +1614,9 @@ static int runC (lua_State *L, lua_State *L1, const char *pc) {
|
||||
else if EQ("error") {
|
||||
lua_error(L1);
|
||||
}
|
||||
else if EQ("abort") {
|
||||
abort();
|
||||
}
|
||||
else if EQ("throw") {
|
||||
#if defined(__cplusplus)
|
||||
static struct X { int x; } x;
|
||||
@@ -1764,7 +1816,6 @@ static const struct luaL_Reg tests_funcs[] = {
|
||||
{"pobj", gc_printobj},
|
||||
{"getref", getref},
|
||||
{"hash", hash_query},
|
||||
{"int2fb", int2fb_aux},
|
||||
{"log2", log2_aux},
|
||||
{"limits", get_limits},
|
||||
{"listcode", listcode},
|
||||
|
||||
@@ -31,7 +31,7 @@
|
||||
|
||||
/* compiled with -O0, Lua uses a lot of C stack space... */
|
||||
#undef LUAI_MAXCSTACK
|
||||
#define LUAI_MAXCSTACK 400
|
||||
#define LUAI_MAXCSTACK 400
|
||||
|
||||
/* to avoid warnings, and to make sure value is really unused */
|
||||
#define UNUSED(x) (x=0, (void)(x))
|
||||
|
||||
@@ -149,9 +149,6 @@ void luaT_trybinTM (lua_State *L, const TValue *p1, const TValue *p2,
|
||||
StkId res, TMS event) {
|
||||
if (!callbinTM(L, p1, p2, res, event)) {
|
||||
switch (event) {
|
||||
case TM_CONCAT:
|
||||
luaG_concaterror(L, p1, p2);
|
||||
/* call never returns, but to avoid warnings: *//* FALLTHROUGH */
|
||||
case TM_BAND: case TM_BOR: case TM_BXOR:
|
||||
case TM_SHL: case TM_SHR: case TM_BNOT: {
|
||||
if (ttisnumber(p1) && ttisnumber(p2))
|
||||
@@ -167,9 +164,16 @@ void luaT_trybinTM (lua_State *L, const TValue *p1, const TValue *p2,
|
||||
}
|
||||
|
||||
|
||||
void luaT_tryconcatTM (lua_State *L) {
|
||||
StkId top = L->top;
|
||||
if (!callbinTM(L, s2v(top - 2), s2v(top - 1), top - 2, TM_CONCAT))
|
||||
luaG_concaterror(L, s2v(top - 2), s2v(top - 1));
|
||||
}
|
||||
|
||||
|
||||
void luaT_trybinassocTM (lua_State *L, const TValue *p1, const TValue *p2,
|
||||
StkId res, int inv, TMS event) {
|
||||
if (inv)
|
||||
int flip, StkId res, TMS event) {
|
||||
if (flip)
|
||||
luaT_trybinTM(L, p2, p1, res, event);
|
||||
else
|
||||
luaT_trybinTM(L, p1, p2, res, event);
|
||||
@@ -177,10 +181,10 @@ void luaT_trybinassocTM (lua_State *L, const TValue *p1, const TValue *p2,
|
||||
|
||||
|
||||
void luaT_trybiniTM (lua_State *L, const TValue *p1, lua_Integer i2,
|
||||
int inv, StkId res, TMS event) {
|
||||
int flip, StkId res, TMS event) {
|
||||
TValue aux;
|
||||
setivalue(&aux, i2);
|
||||
luaT_trybinassocTM(L, p1, &aux, res, inv, event);
|
||||
luaT_trybinassocTM(L, p1, &aux, flip, res, event);
|
||||
}
|
||||
|
||||
|
||||
@@ -205,14 +209,14 @@ int luaT_callorderTM (lua_State *L, const TValue *p1, const TValue *p2,
|
||||
|
||||
|
||||
int luaT_callorderiTM (lua_State *L, const TValue *p1, int v2,
|
||||
int inv, int isfloat, TMS event) {
|
||||
int flip, int isfloat, TMS event) {
|
||||
TValue aux; const TValue *p2;
|
||||
if (isfloat) {
|
||||
setfltvalue(&aux, cast_num(v2));
|
||||
}
|
||||
else
|
||||
setivalue(&aux, v2);
|
||||
if (inv) { /* arguments were exchanged? */
|
||||
if (flip) { /* arguments were exchanged? */
|
||||
p2 = p1; p1 = &aux; /* correct them */
|
||||
}
|
||||
else
|
||||
|
||||
@@ -75,8 +75,9 @@ LUAI_FUNC void luaT_callTMres (lua_State *L, const TValue *f,
|
||||
const TValue *p1, const TValue *p2, StkId p3);
|
||||
LUAI_FUNC void luaT_trybinTM (lua_State *L, const TValue *p1, const TValue *p2,
|
||||
StkId res, TMS event);
|
||||
LUAI_FUNC void luaT_tryconcatTM (lua_State *L);
|
||||
LUAI_FUNC void luaT_trybinassocTM (lua_State *L, const TValue *p1,
|
||||
const TValue *p2, StkId res, int inv, TMS event);
|
||||
const TValue *p2, int inv, StkId res, TMS event);
|
||||
LUAI_FUNC void luaT_trybiniTM (lua_State *L, const TValue *p1, lua_Integer i2,
|
||||
int inv, StkId res, TMS event);
|
||||
LUAI_FUNC int luaT_callorderTM (lua_State *L, const TValue *p1,
|
||||
|
||||
@@ -73,6 +73,7 @@ static void print_usage (const char *badoption) {
|
||||
" -l name require library 'name' into global 'name'\n"
|
||||
" -v show version information\n"
|
||||
" -E ignore environment variables\n"
|
||||
" -W turn warnings on\n"
|
||||
" -- stop handling options\n"
|
||||
" - stop handling options and execute stdin\n"
|
||||
,
|
||||
@@ -259,14 +260,18 @@ static int collectargs (char **argv, int *first) {
|
||||
case '\0': /* '-' */
|
||||
return args; /* script "name" is '-' */
|
||||
case 'E':
|
||||
if (argv[i][2] != '\0') /* extra characters after 1st? */
|
||||
if (argv[i][2] != '\0') /* extra characters? */
|
||||
return has_error; /* invalid option */
|
||||
args |= has_E;
|
||||
break;
|
||||
case 'W':
|
||||
if (argv[i][2] != '\0') /* extra characters? */
|
||||
return has_error; /* invalid option */
|
||||
break;
|
||||
case 'i':
|
||||
args |= has_i; /* (-i implies -v) *//* FALLTHROUGH */
|
||||
case 'v':
|
||||
if (argv[i][2] != '\0') /* extra characters after 1st? */
|
||||
if (argv[i][2] != '\0') /* extra characters? */
|
||||
return has_error; /* invalid option */
|
||||
args |= has_v;
|
||||
break;
|
||||
@@ -289,7 +294,8 @@ static int collectargs (char **argv, int *first) {
|
||||
|
||||
|
||||
/*
|
||||
** Processes options 'e' and 'l', which involve running Lua code.
|
||||
** Processes options 'e' and 'l', which involve running Lua code, and
|
||||
** 'W', which also affects the state.
|
||||
** Returns 0 if some code raises an error.
|
||||
*/
|
||||
static int runargs (lua_State *L, char **argv, int n) {
|
||||
@@ -297,15 +303,21 @@ static int runargs (lua_State *L, char **argv, int n) {
|
||||
for (i = 1; i < n; i++) {
|
||||
int option = argv[i][1];
|
||||
lua_assert(argv[i][0] == '-'); /* already checked */
|
||||
if (option == 'e' || option == 'l') {
|
||||
int status;
|
||||
const char *extra = argv[i] + 2; /* both options need an argument */
|
||||
if (*extra == '\0') extra = argv[++i];
|
||||
lua_assert(extra != NULL);
|
||||
status = (option == 'e')
|
||||
? dostring(L, extra, "=(command line)")
|
||||
: dolibrary(L, extra);
|
||||
if (status != LUA_OK) return 0;
|
||||
switch (option) {
|
||||
case 'e': case 'l': {
|
||||
int status;
|
||||
const char *extra = argv[i] + 2; /* both options need an argument */
|
||||
if (*extra == '\0') extra = argv[++i];
|
||||
lua_assert(extra != NULL);
|
||||
status = (option == 'e')
|
||||
? dostring(L, extra, "=(command line)")
|
||||
: dolibrary(L, extra);
|
||||
if (status != LUA_OK) return 0;
|
||||
break;
|
||||
}
|
||||
case 'W':
|
||||
lua_warning(L, "@on", 0); /* warnings on */
|
||||
break;
|
||||
}
|
||||
}
|
||||
return 1;
|
||||
|
||||
@@ -462,6 +462,7 @@ LUA_API lua_Hook (lua_gethook) (lua_State *L);
|
||||
LUA_API int (lua_gethookmask) (lua_State *L);
|
||||
LUA_API int (lua_gethookcount) (lua_State *L);
|
||||
|
||||
LUA_API int (lua_setcstacklimit) (lua_State *L, unsigned int limit);
|
||||
|
||||
struct lua_Debug {
|
||||
int event;
|
||||
|
||||
@@ -47,7 +47,7 @@
|
||||
** (It will crash with a limit too high.)
|
||||
*/
|
||||
#if !defined(LUAI_MAXCSTACK)
|
||||
#define LUAI_MAXCSTACK 2200
|
||||
#define LUAI_MAXCSTACK 2000
|
||||
#endif
|
||||
|
||||
|
||||
@@ -493,7 +493,7 @@
|
||||
@@ LUA_UNSIGNED is the unsigned version of LUA_INTEGER.
|
||||
**
|
||||
@@ LUAI_UACINT is the result of a 'default argument promotion'
|
||||
@@ over a lUA_INTEGER.
|
||||
@@ over a LUA_INTEGER.
|
||||
@@ LUA_INTEGER_FRMLEN is the length modifier for reading/writing integers.
|
||||
@@ LUA_INTEGER_FMT is the format for writing integers.
|
||||
@@ LUA_MAXINTEGER is the maximum value for a LUA_INTEGER.
|
||||
|
||||
@@ -198,11 +198,11 @@ static void LoadUpvalues (LoadState *S, Proto *f) {
|
||||
n = LoadInt(S);
|
||||
f->upvalues = luaM_newvectorchecked(S->L, n, Upvaldesc);
|
||||
f->sizeupvalues = n;
|
||||
for (i = 0; i < n; i++)
|
||||
f->upvalues[i].name = NULL;
|
||||
for (i = 0; i < n; i++) {
|
||||
f->upvalues[i].name = NULL;
|
||||
f->upvalues[i].instack = LoadByte(S);
|
||||
f->upvalues[i].idx = LoadByte(S);
|
||||
f->upvalues[i].kind = LoadByte(S);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
+2
-2
@@ -103,7 +103,7 @@ static int utflen (lua_State *L) {
|
||||
while (posi <= posj) {
|
||||
const char *s1 = utf8_decode(s + posi, NULL, !lax);
|
||||
if (s1 == NULL) { /* conversion error? */
|
||||
lua_pushnil(L); /* return nil ... */
|
||||
luaL_pushfail(L); /* return fail ... */
|
||||
lua_pushinteger(L, posi + 1); /* ... and current position */
|
||||
return 2;
|
||||
}
|
||||
@@ -216,7 +216,7 @@ static int byteoffset (lua_State *L) {
|
||||
if (n == 0) /* did it find given character? */
|
||||
lua_pushinteger(L, posi + 1);
|
||||
else /* no such character */
|
||||
lua_pushnil(L);
|
||||
luaL_pushfail(L);
|
||||
return 1;
|
||||
}
|
||||
|
||||
|
||||
@@ -515,8 +515,10 @@ int luaV_equalobj (lua_State *L, const TValue *t1, const TValue *t2) {
|
||||
}
|
||||
if (tm == NULL) /* no TM? */
|
||||
return 0; /* objects are different */
|
||||
luaT_callTMres(L, tm, t1, t2, L->top); /* call TM */
|
||||
return !l_isfalse(s2v(L->top));
|
||||
else {
|
||||
luaT_callTMres(L, tm, t1, t2, L->top); /* call TM */
|
||||
return !l_isfalse(s2v(L->top));
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -548,7 +550,7 @@ void luaV_concat (lua_State *L, int total) {
|
||||
int n = 2; /* number of elements handled in this pass (at least 2) */
|
||||
if (!(ttisstring(s2v(top - 2)) || cvt2str(s2v(top - 2))) ||
|
||||
!tostring(L, s2v(top - 1)))
|
||||
luaT_trybinTM(L, s2v(top - 2), s2v(top - 1), top - 2, TM_CONCAT);
|
||||
luaT_tryconcatTM(L);
|
||||
else if (isemptystr(s2v(top - 1))) /* second operand is empty? */
|
||||
cast_void(tostring(L, s2v(top - 2))); /* result is first operand */
|
||||
else if (isemptystr(s2v(top - 2))) { /* first operand is empty string? */
|
||||
@@ -671,6 +673,8 @@ lua_Number luaV_modf (lua_State *L, lua_Number m, lua_Number n) {
|
||||
/*
|
||||
** Shift left operation. (Shift right just negates 'y'.)
|
||||
*/
|
||||
#define luaV_shiftr(x,y) luaV_shiftl(x,-(y))
|
||||
|
||||
lua_Integer luaV_shiftl (lua_Integer x, lua_Integer y) {
|
||||
if (y < 0) { /* shift right? */
|
||||
if (y <= -NBITS) return 0;
|
||||
@@ -714,15 +718,10 @@ void luaV_finishOp (lua_State *L) {
|
||||
Instruction inst = *(ci->u.l.savedpc - 1); /* interrupted instruction */
|
||||
OpCode op = GET_OPCODE(inst);
|
||||
switch (op) { /* finish its execution */
|
||||
case OP_ADDI: case OP_SUBI:
|
||||
case OP_MULI: case OP_DIVI: case OP_IDIVI:
|
||||
case OP_MODI: case OP_POWI:
|
||||
case OP_ADD: case OP_SUB:
|
||||
case OP_MUL: case OP_DIV: case OP_IDIV:
|
||||
case OP_BANDK: case OP_BORK: case OP_BXORK:
|
||||
case OP_BAND: case OP_BOR: case OP_BXOR:
|
||||
case OP_SHRI: case OP_SHL: case OP_SHR:
|
||||
case OP_MOD: case OP_POW:
|
||||
case OP_MMBIN: case OP_MMBINI: case OP_MMBINK: {
|
||||
setobjs2s(L, base + GETARG_A(*(ci->u.l.savedpc - 2)), --L->top);
|
||||
break;
|
||||
}
|
||||
case OP_UNM: case OP_BNOT: case OP_LEN:
|
||||
case OP_GETTABUP: case OP_GETTABLE: case OP_GETI:
|
||||
case OP_GETFIELD: case OP_SELF: {
|
||||
@@ -747,7 +746,7 @@ void luaV_finishOp (lua_State *L) {
|
||||
break;
|
||||
}
|
||||
case OP_CONCAT: {
|
||||
StkId top = L->top - 1; /* top when 'luaT_trybinTM' was called */
|
||||
StkId top = L->top - 1; /* top when 'luaT_tryconcatTM' was called */
|
||||
int a = GETARG_A(inst); /* first element to concatenate */
|
||||
int total = cast_int(top - 1 - (base + a)); /* yet to concatenate */
|
||||
setobjs2s(L, top - 2, top); /* put TM result in proper position */
|
||||
@@ -779,9 +778,9 @@ void luaV_finishOp (lua_State *L) {
|
||||
#define l_addi(L,a,b) intop(+, a, b)
|
||||
#define l_subi(L,a,b) intop(-, a, b)
|
||||
#define l_muli(L,a,b) intop(*, a, b)
|
||||
#define l_band(L,a,b) intop(&, a, b)
|
||||
#define l_bor(L,a,b) intop(|, a, b)
|
||||
#define l_bxor(L,a,b) intop(^, a, b)
|
||||
#define l_band(a,b) intop(&, a, b)
|
||||
#define l_bor(a,b) intop(|, a, b)
|
||||
#define l_bxor(a,b) intop(^, a, b)
|
||||
|
||||
#define l_lti(a,b) (a < b)
|
||||
#define l_lei(a,b) (a <= b)
|
||||
@@ -797,10 +796,9 @@ void luaV_finishOp (lua_State *L) {
|
||||
#define op_arithfI_aux(L,v1,imm,fop,tm,flip) { \
|
||||
lua_Number nb; \
|
||||
if (tonumberns(v1, nb)) { \
|
||||
setfltvalue(s2v(ra), fop(L, nb, cast_num(imm))); \
|
||||
} \
|
||||
else \
|
||||
Protect(luaT_trybiniTM(L, v1, imm, flip, ra, tm)); }
|
||||
lua_Number fimm = cast_num(imm); \
|
||||
pc++; setfltvalue(s2v(ra), fop(L, nb, fimm)); \
|
||||
}}
|
||||
|
||||
|
||||
/*
|
||||
@@ -819,7 +817,8 @@ void luaV_finishOp (lua_State *L) {
|
||||
TValue *v1 = vRB(i); \
|
||||
int imm = GETARG_sC(i); \
|
||||
if (ttisinteger(v1)) { \
|
||||
setivalue(s2v(ra), iop(L, ivalue(v1), imm)); \
|
||||
lua_Integer iv1 = ivalue(v1); \
|
||||
pc++; setivalue(s2v(ra), iop(L, iv1, imm)); \
|
||||
} \
|
||||
else op_arithfI_aux(L, v1, imm, fop, tm, flip); }
|
||||
|
||||
@@ -828,97 +827,87 @@ void luaV_finishOp (lua_State *L) {
|
||||
** Auxiliary function for arithmetic operations over floats and others
|
||||
** with two register operands.
|
||||
*/
|
||||
#define op_arithf_aux(L,v1,v2,fop,tm) { \
|
||||
#define op_arithf_aux(L,v1,v2,fop) { \
|
||||
lua_Number n1; lua_Number n2; \
|
||||
if (tonumberns(v1, n1) && tonumberns(v2, n2)) { \
|
||||
setfltvalue(s2v(ra), fop(L, n1, n2)); \
|
||||
} \
|
||||
else \
|
||||
Protect(luaT_trybinTM(L, v1, v2, ra, tm)); }
|
||||
pc++; setfltvalue(s2v(ra), fop(L, n1, n2)); \
|
||||
}}
|
||||
|
||||
|
||||
/*
|
||||
** Arithmetic operations over floats and others with register operands.
|
||||
*/
|
||||
#define op_arithf(L,fop,tm) { \
|
||||
#define op_arithf(L,fop) { \
|
||||
TValue *v1 = vRB(i); \
|
||||
TValue *v2 = vRC(i); \
|
||||
op_arithf_aux(L, v1, v2, fop, tm); }
|
||||
op_arithf_aux(L, v1, v2, fop); }
|
||||
|
||||
|
||||
/*
|
||||
** Arithmetic operations with register operands.
|
||||
*/
|
||||
#define op_arith(L,iop,fop,tm) { \
|
||||
#define op_arith(L,iop,fop) { \
|
||||
TValue *v1 = vRB(i); \
|
||||
TValue *v2 = vRC(i); \
|
||||
if (ttisinteger(v1) && ttisinteger(v2)) { \
|
||||
lua_Integer i1 = ivalue(v1); lua_Integer i2 = ivalue(v2); \
|
||||
setivalue(s2v(ra), iop(L, i1, i2)); \
|
||||
pc++; setivalue(s2v(ra), iop(L, i1, i2)); \
|
||||
} \
|
||||
else op_arithf_aux(L, v1, v2, fop, tm); }
|
||||
else op_arithf_aux(L, v1, v2, fop); }
|
||||
|
||||
|
||||
/*
|
||||
** Arithmetic operations with K operands.
|
||||
*/
|
||||
#define op_arithK(L,iop,fop,tm,flip) { \
|
||||
#define op_arithK(L,iop,fop,flip) { \
|
||||
TValue *v1 = vRB(i); \
|
||||
TValue *v2 = KC(i); \
|
||||
if (ttisinteger(v1) && ttisinteger(v2)) { \
|
||||
lua_Integer i1 = ivalue(v1); lua_Integer i2 = ivalue(v2); \
|
||||
setivalue(s2v(ra), iop(L, i1, i2)); \
|
||||
pc++; setivalue(s2v(ra), iop(L, i1, i2)); \
|
||||
} \
|
||||
else { \
|
||||
lua_Number n1; lua_Number n2; \
|
||||
if (tonumberns(v1, n1) && tonumberns(v2, n2)) { \
|
||||
setfltvalue(s2v(ra), fop(L, n1, n2)); \
|
||||
} \
|
||||
else \
|
||||
Protect(luaT_trybinassocTM(L, v1, v2, ra, flip, tm)); } }
|
||||
pc++; setfltvalue(s2v(ra), fop(L, n1, n2)); \
|
||||
}}}
|
||||
|
||||
|
||||
/*
|
||||
** Arithmetic operations with K operands for floats.
|
||||
*/
|
||||
#define op_arithfK(L,fop,tm) { \
|
||||
#define op_arithfK(L,fop) { \
|
||||
TValue *v1 = vRB(i); \
|
||||
TValue *v2 = KC(i); \
|
||||
lua_Number n1; lua_Number n2; \
|
||||
if (tonumberns(v1, n1) && tonumberns(v2, n2)) { \
|
||||
setfltvalue(s2v(ra), fop(L, n1, n2)); \
|
||||
} \
|
||||
else \
|
||||
Protect(luaT_trybinTM(L, v1, v2, ra, tm)); }
|
||||
pc++; setfltvalue(s2v(ra), fop(L, n1, n2)); \
|
||||
}}
|
||||
|
||||
|
||||
/*
|
||||
** Bitwise operations with constant operand.
|
||||
*/
|
||||
#define op_bitwiseK(L,op,tm) { \
|
||||
#define op_bitwiseK(L,op) { \
|
||||
TValue *v1 = vRB(i); \
|
||||
TValue *v2 = KC(i); \
|
||||
lua_Integer i1; \
|
||||
lua_Integer i2 = ivalue(v2); \
|
||||
if (tointegerns(v1, &i1)) { \
|
||||
setivalue(s2v(ra), op(L, i1, i2)); \
|
||||
} \
|
||||
else \
|
||||
Protect(luaT_trybiniTM(L, v1, i2, TESTARG_k(i), ra, tm)); }
|
||||
pc++; setivalue(s2v(ra), op(i1, i2)); \
|
||||
}}
|
||||
|
||||
|
||||
/*
|
||||
** Bitwise operations with register operands.
|
||||
*/
|
||||
#define op_bitwise(L,op,tm) { \
|
||||
#define op_bitwise(L,op) { \
|
||||
TValue *v1 = vRB(i); \
|
||||
TValue *v2 = vRC(i); \
|
||||
lua_Integer i1; lua_Integer i2; \
|
||||
if (tointegerns(v1, &i1) && tointegerns(v2, &i2)) { \
|
||||
setivalue(s2v(ra), op(L, i1, i2)); \
|
||||
} \
|
||||
else \
|
||||
Protect(luaT_trybinTM(L, v1, v2, ra, tm)); }
|
||||
pc++; setivalue(s2v(ra), op(i1, i2)); \
|
||||
}}
|
||||
|
||||
|
||||
/*
|
||||
@@ -927,8 +916,11 @@ void luaV_finishOp (lua_State *L) {
|
||||
#define op_order(L,opi,opf,other) { \
|
||||
int cond; \
|
||||
TValue *rb = vRB(i); \
|
||||
if (ttisinteger(s2v(ra)) && ttisinteger(rb)) \
|
||||
cond = opi(ivalue(s2v(ra)), ivalue(rb)); \
|
||||
if (ttisinteger(s2v(ra)) && ttisinteger(rb)) { \
|
||||
lua_Integer ia = ivalue(s2v(ra)); \
|
||||
lua_Integer ib = ivalue(rb); \
|
||||
cond = opi(ia, ib); \
|
||||
} \
|
||||
else if (ttisnumber(s2v(ra)) && ttisnumber(rb)) \
|
||||
cond = opf(s2v(ra), rb); \
|
||||
else \
|
||||
@@ -944,8 +936,11 @@ void luaV_finishOp (lua_State *L) {
|
||||
int im = GETARG_sB(i); \
|
||||
if (ttisinteger(s2v(ra))) \
|
||||
cond = opi(ivalue(s2v(ra)), im); \
|
||||
else if (ttisfloat(s2v(ra))) \
|
||||
cond = opf(fltvalue(s2v(ra)), cast_num(im)); \
|
||||
else if (ttisfloat(s2v(ra))) { \
|
||||
lua_Number fa = fltvalue(s2v(ra)); \
|
||||
lua_Number fim = cast_num(im); \
|
||||
cond = opf(fa, fim); \
|
||||
} \
|
||||
else { \
|
||||
int isf = GETARG_C(i); \
|
||||
Protect(cond = luaT_callorderiTM(L, s2v(ra), im, inv, isf, tm)); \
|
||||
@@ -993,7 +988,7 @@ void luaV_finishOp (lua_State *L) {
|
||||
|
||||
|
||||
/* for test instructions, execute the jump instruction that follows it */
|
||||
#define donextjump(ci) { i = *pc; dojump(ci, i, 1); }
|
||||
#define donextjump(ci) { Instruction ni = *pc; dojump(ci, ni, 1); }
|
||||
|
||||
/*
|
||||
** do a conditional jump: skip next instruction if 'cond' is not what
|
||||
@@ -1030,7 +1025,10 @@ void luaV_finishOp (lua_State *L) {
|
||||
** errors. (That is, it will not return to the interpreter main loop
|
||||
** after changing the stack or hooks.)
|
||||
*/
|
||||
#define halfProtect(exp) (savepc(L), (exp))
|
||||
#define halfProtect(exp) (savestate(L,ci), (exp))
|
||||
|
||||
/* idem, but without changing the stack */
|
||||
#define halfProtectNT(exp) (savepc(L), (exp))
|
||||
|
||||
|
||||
#define checkGC(L,c) \
|
||||
@@ -1083,17 +1081,13 @@ void luaV_execute (lua_State *L, CallInfo *ci) {
|
||||
vmfetch();
|
||||
lua_assert(base == ci->func + 1);
|
||||
lua_assert(base <= L->top && L->top < L->stack + L->stacksize);
|
||||
lua_assert(ci->top < L->stack + L->stacksize);
|
||||
/* invalidate top for instructions not expecting it */
|
||||
lua_assert(isIT(i) || (L->top = base));
|
||||
vmdispatch (GET_OPCODE(i)) {
|
||||
vmcase(OP_MOVE) {
|
||||
setobjs2s(L, ra, RB(i));
|
||||
vmbreak;
|
||||
}
|
||||
vmcase(OP_LOADK) {
|
||||
TValue *rb = k + GETARG_Bx(i);
|
||||
setobj2s(L, ra, rb);
|
||||
vmbreak;
|
||||
}
|
||||
vmcase(OP_LOADI) {
|
||||
lua_Integer b = GETARG_sBx(i);
|
||||
setivalue(s2v(ra), b);
|
||||
@@ -1104,6 +1098,11 @@ void luaV_execute (lua_State *L, CallInfo *ci) {
|
||||
setfltvalue(s2v(ra), cast_num(b));
|
||||
vmbreak;
|
||||
}
|
||||
vmcase(OP_LOADK) {
|
||||
TValue *rb = k + GETARG_Bx(i);
|
||||
setobj2s(L, ra, rb);
|
||||
vmbreak;
|
||||
}
|
||||
vmcase(OP_LOADKX) {
|
||||
TValue *rb;
|
||||
rb = k + GETARG_Ax(*pc); pc++;
|
||||
@@ -1239,14 +1238,19 @@ void luaV_execute (lua_State *L, CallInfo *ci) {
|
||||
vmbreak;
|
||||
}
|
||||
vmcase(OP_NEWTABLE) {
|
||||
int b = GETARG_B(i);
|
||||
int c = GETARG_C(i);
|
||||
int b = GETARG_B(i); /* log2(hash size) + 1 */
|
||||
int c = GETARG_C(i); /* array size */
|
||||
Table *t;
|
||||
L->top = ci->top; /* correct top in case of GC */
|
||||
if (b > 0)
|
||||
b = 1 << (b - 1); /* size is 2^(b - 1) */
|
||||
if (TESTARG_k(i))
|
||||
c += GETARG_Ax(*pc) * (MAXARG_C + 1);
|
||||
pc++; /* skip extra argument */
|
||||
L->top = ra + 1; /* correct top in case of emergency GC */
|
||||
t = luaH_new(L); /* memory allocation */
|
||||
sethvalue2s(L, ra, t);
|
||||
if (b != 0 || c != 0)
|
||||
luaH_resize(L, t, luaO_fb2int(b), luaO_fb2int(c)); /* idem */
|
||||
luaH_resize(L, t, c, b); /* idem */
|
||||
checkGC(L, ra + 1);
|
||||
vmbreak;
|
||||
}
|
||||
@@ -1267,108 +1271,44 @@ void luaV_execute (lua_State *L, CallInfo *ci) {
|
||||
op_arithI(L, l_addi, luai_numadd, TM_ADD, GETARG_k(i));
|
||||
vmbreak;
|
||||
}
|
||||
vmcase(OP_SUBI) {
|
||||
op_arithI(L, l_subi, luai_numsub, TM_SUB, 0);
|
||||
vmbreak;
|
||||
}
|
||||
vmcase(OP_MULI) {
|
||||
op_arithI(L, l_muli, luai_nummul, TM_MUL, GETARG_k(i));
|
||||
vmbreak;
|
||||
}
|
||||
vmcase(OP_MODI) {
|
||||
op_arithI(L, luaV_mod, luaV_modf, TM_MOD, 0);
|
||||
vmbreak;
|
||||
}
|
||||
vmcase(OP_POWI) {
|
||||
op_arithfI(L, luai_numpow, TM_POW);
|
||||
vmbreak;
|
||||
}
|
||||
vmcase(OP_DIVI) {
|
||||
op_arithfI(L, luai_numdiv, TM_DIV);
|
||||
vmbreak;
|
||||
}
|
||||
vmcase(OP_IDIVI) {
|
||||
op_arithI(L, luaV_idiv, luai_numidiv, TM_IDIV, 0);
|
||||
vmbreak;
|
||||
}
|
||||
vmcase(OP_ADDK) {
|
||||
op_arithK(L, l_addi, luai_numadd, TM_ADD, GETARG_k(i));
|
||||
op_arithK(L, l_addi, luai_numadd, GETARG_k(i));
|
||||
vmbreak;
|
||||
}
|
||||
vmcase(OP_SUBK) {
|
||||
op_arithK(L, l_subi, luai_numsub, TM_SUB, 0);
|
||||
op_arithK(L, l_subi, luai_numsub, 0);
|
||||
vmbreak;
|
||||
}
|
||||
vmcase(OP_MULK) {
|
||||
op_arithK(L, l_muli, luai_nummul, TM_MUL, GETARG_k(i));
|
||||
op_arithK(L, l_muli, luai_nummul, GETARG_k(i));
|
||||
vmbreak;
|
||||
}
|
||||
vmcase(OP_MODK) {
|
||||
op_arithK(L, luaV_mod, luaV_modf, TM_MOD, 0);
|
||||
op_arithK(L, luaV_mod, luaV_modf, 0);
|
||||
vmbreak;
|
||||
}
|
||||
vmcase(OP_POWK) {
|
||||
op_arithfK(L, luai_numpow, TM_POW);
|
||||
op_arithfK(L, luai_numpow);
|
||||
vmbreak;
|
||||
}
|
||||
vmcase(OP_DIVK) {
|
||||
op_arithfK(L, luai_numdiv, TM_DIV);
|
||||
op_arithfK(L, luai_numdiv);
|
||||
vmbreak;
|
||||
}
|
||||
vmcase(OP_IDIVK) {
|
||||
op_arithK(L, luaV_idiv, luai_numidiv, TM_IDIV, 0);
|
||||
vmbreak;
|
||||
}
|
||||
vmcase(OP_ADD) {
|
||||
op_arith(L, l_addi, luai_numadd, TM_ADD);
|
||||
vmbreak;
|
||||
}
|
||||
vmcase(OP_SUB) {
|
||||
op_arith(L, l_subi, luai_numsub, TM_SUB);
|
||||
vmbreak;
|
||||
}
|
||||
vmcase(OP_MUL) {
|
||||
op_arith(L, l_muli, luai_nummul, TM_MUL);
|
||||
vmbreak;
|
||||
}
|
||||
vmcase(OP_MOD) {
|
||||
op_arith(L, luaV_mod, luaV_modf, TM_MOD);
|
||||
vmbreak;
|
||||
}
|
||||
vmcase(OP_POW) {
|
||||
op_arithf(L, luai_numpow, TM_POW);
|
||||
vmbreak;
|
||||
}
|
||||
vmcase(OP_DIV) { /* float division (always with floats) */
|
||||
op_arithf(L, luai_numdiv, TM_DIV);
|
||||
vmbreak;
|
||||
}
|
||||
vmcase(OP_IDIV) { /* floor division */
|
||||
op_arith(L, luaV_idiv, luai_numidiv, TM_IDIV);
|
||||
op_arithK(L, luaV_idiv, luai_numidiv, 0);
|
||||
vmbreak;
|
||||
}
|
||||
vmcase(OP_BANDK) {
|
||||
op_bitwiseK(L, l_band, TM_BAND);
|
||||
op_bitwiseK(L, l_band);
|
||||
vmbreak;
|
||||
}
|
||||
vmcase(OP_BORK) {
|
||||
op_bitwiseK(L, l_bor, TM_BOR);
|
||||
op_bitwiseK(L, l_bor);
|
||||
vmbreak;
|
||||
}
|
||||
vmcase(OP_BXORK) {
|
||||
op_bitwiseK(L, l_bxor, TM_BXOR);
|
||||
vmbreak;
|
||||
}
|
||||
vmcase(OP_BAND) {
|
||||
op_bitwise(L, l_band, TM_BAND);
|
||||
vmbreak;
|
||||
}
|
||||
vmcase(OP_BOR) {
|
||||
op_bitwise(L, l_bor, TM_BOR);
|
||||
vmbreak;
|
||||
}
|
||||
vmcase(OP_BXOR) {
|
||||
op_bitwise(L, l_bxor, TM_BXOR);
|
||||
op_bitwiseK(L, l_bxor);
|
||||
vmbreak;
|
||||
}
|
||||
vmcase(OP_SHRI) {
|
||||
@@ -1376,14 +1316,7 @@ void luaV_execute (lua_State *L, CallInfo *ci) {
|
||||
int ic = GETARG_sC(i);
|
||||
lua_Integer ib;
|
||||
if (tointegerns(rb, &ib)) {
|
||||
setivalue(s2v(ra), luaV_shiftl(ib, -ic));
|
||||
}
|
||||
else {
|
||||
TMS ev = TM_SHR;
|
||||
if (TESTARG_k(i)) {
|
||||
ic = -ic; ev = TM_SHL;
|
||||
}
|
||||
Protect(luaT_trybiniTM(L, rb, ic, 0, ra, ev));
|
||||
pc++; setivalue(s2v(ra), luaV_shiftl(ib, -ic));
|
||||
}
|
||||
vmbreak;
|
||||
}
|
||||
@@ -1392,32 +1325,83 @@ void luaV_execute (lua_State *L, CallInfo *ci) {
|
||||
int ic = GETARG_sC(i);
|
||||
lua_Integer ib;
|
||||
if (tointegerns(rb, &ib)) {
|
||||
setivalue(s2v(ra), luaV_shiftl(ic, ib));
|
||||
pc++; setivalue(s2v(ra), luaV_shiftl(ic, ib));
|
||||
}
|
||||
else
|
||||
Protect(luaT_trybiniTM(L, rb, ic, 1, ra, TM_SHL));
|
||||
vmbreak;
|
||||
}
|
||||
vmcase(OP_ADD) {
|
||||
op_arith(L, l_addi, luai_numadd);
|
||||
vmbreak;
|
||||
}
|
||||
vmcase(OP_SUB) {
|
||||
op_arith(L, l_subi, luai_numsub);
|
||||
vmbreak;
|
||||
}
|
||||
vmcase(OP_MUL) {
|
||||
op_arith(L, l_muli, luai_nummul);
|
||||
vmbreak;
|
||||
}
|
||||
vmcase(OP_MOD) {
|
||||
op_arith(L, luaV_mod, luaV_modf);
|
||||
vmbreak;
|
||||
}
|
||||
vmcase(OP_POW) {
|
||||
op_arithf(L, luai_numpow);
|
||||
vmbreak;
|
||||
}
|
||||
vmcase(OP_DIV) { /* float division (always with floats) */
|
||||
op_arithf(L, luai_numdiv);
|
||||
vmbreak;
|
||||
}
|
||||
vmcase(OP_IDIV) { /* floor division */
|
||||
op_arith(L, luaV_idiv, luai_numidiv);
|
||||
vmbreak;
|
||||
}
|
||||
vmcase(OP_BAND) {
|
||||
op_bitwise(L, l_band);
|
||||
vmbreak;
|
||||
}
|
||||
vmcase(OP_BOR) {
|
||||
op_bitwise(L, l_bor);
|
||||
vmbreak;
|
||||
}
|
||||
vmcase(OP_BXOR) {
|
||||
op_bitwise(L, l_bxor);
|
||||
vmbreak;
|
||||
}
|
||||
vmcase(OP_SHR) {
|
||||
TValue *rb = vRB(i);
|
||||
TValue *rc = vRC(i);
|
||||
lua_Integer ib; lua_Integer ic;
|
||||
if (tointegerns(rb, &ib) && tointegerns(rc, &ic)) {
|
||||
setivalue(s2v(ra), luaV_shiftl(ib, -ic));
|
||||
}
|
||||
else
|
||||
Protect(luaT_trybinTM(L, rb, rc, ra, TM_SHR));
|
||||
op_bitwise(L, luaV_shiftr);
|
||||
vmbreak;
|
||||
}
|
||||
vmcase(OP_SHL) {
|
||||
op_bitwise(L, luaV_shiftl);
|
||||
vmbreak;
|
||||
}
|
||||
vmcase(OP_MMBIN) {
|
||||
Instruction pi = *(pc - 2); /* original arith. expression */
|
||||
TValue *rb = vRB(i);
|
||||
TValue *rc = vRC(i);
|
||||
lua_Integer ib; lua_Integer ic;
|
||||
if (tointegerns(rb, &ib) && tointegerns(rc, &ic)) {
|
||||
setivalue(s2v(ra), luaV_shiftl(ib, ic));
|
||||
}
|
||||
else
|
||||
Protect(luaT_trybinTM(L, rb, rc, ra, TM_SHL));
|
||||
TMS tm = (TMS)GETARG_C(i);
|
||||
StkId result = RA(pi);
|
||||
lua_assert(OP_ADD <= GET_OPCODE(pi) && GET_OPCODE(pi) <= OP_SHR);
|
||||
Protect(luaT_trybinTM(L, s2v(ra), rb, result, tm));
|
||||
vmbreak;
|
||||
}
|
||||
vmcase(OP_MMBINI) {
|
||||
Instruction pi = *(pc - 2); /* original arith. expression */
|
||||
int imm = GETARG_sB(i);
|
||||
TMS tm = (TMS)GETARG_C(i);
|
||||
int flip = GETARG_k(i);
|
||||
StkId result = RA(pi);
|
||||
Protect(luaT_trybiniTM(L, s2v(ra), imm, flip, result, tm));
|
||||
vmbreak;
|
||||
}
|
||||
vmcase(OP_MMBINK) {
|
||||
Instruction pi = *(pc - 2); /* original arith. expression */
|
||||
TValue *imm = KB(i);
|
||||
TMS tm = (TMS)GETARG_C(i);
|
||||
int flip = GETARG_k(i);
|
||||
StkId result = RA(pi);
|
||||
Protect(luaT_trybinassocTM(L, s2v(ra), imm, flip, result, tm));
|
||||
vmbreak;
|
||||
}
|
||||
vmcase(OP_UNM) {
|
||||
@@ -1462,7 +1446,6 @@ void luaV_execute (lua_State *L, CallInfo *ci) {
|
||||
vmbreak;
|
||||
}
|
||||
vmcase(OP_CLOSE) {
|
||||
L->top = ra + 1; /* everything is free after this slot */
|
||||
Protect(luaF_close(L, ra, LUA_OK));
|
||||
vmbreak;
|
||||
}
|
||||
@@ -1551,19 +1534,20 @@ void luaV_execute (lua_State *L, CallInfo *ci) {
|
||||
}
|
||||
vmcase(OP_TAILCALL) {
|
||||
int b = GETARG_B(i); /* number of arguments + 1 (function) */
|
||||
int delta = 0; /* virtual 'func' - real 'func' (vararg functions) */
|
||||
int nparams1 = GETARG_C(i);
|
||||
/* delat is virtual 'func' - real 'func' (vararg functions) */
|
||||
int delta = (nparams1) ? ci->u.l.nextraargs + nparams1 : 0;
|
||||
if (b != 0)
|
||||
L->top = ra + b;
|
||||
else /* previous instruction set top */
|
||||
b = cast_int(L->top - ra);
|
||||
savepc(ci); /* some calls here can raise errors */
|
||||
if (TESTARG_k(i)) {
|
||||
int nparams1 = GETARG_C(i);
|
||||
if (nparams1) /* vararg function? */
|
||||
delta = ci->u.l.nextraargs + nparams1;
|
||||
/* close upvalues from current call; the compiler ensures
|
||||
that there are no to-be-closed variables here */
|
||||
that there are no to-be-closed variables here, so this
|
||||
call cannot change the stack */
|
||||
luaF_close(L, base, NOCLOSINGMETH);
|
||||
lua_assert(base == ci->func + 1);
|
||||
}
|
||||
if (!ttisfunction(s2v(ra))) { /* not a function? */
|
||||
luaD_tryfuncTM(L, ra); /* try '__call' metamethod */
|
||||
@@ -1586,24 +1570,27 @@ void luaV_execute (lua_State *L, CallInfo *ci) {
|
||||
}
|
||||
vmcase(OP_RETURN) {
|
||||
int n = GETARG_B(i) - 1; /* number of results */
|
||||
int nparams1 = GETARG_C(i);
|
||||
if (n < 0) /* not fixed? */
|
||||
n = cast_int(L->top - ra); /* get what is available */
|
||||
else
|
||||
L->top = ra + n; /* set call for 'luaD_poscall' */
|
||||
savepc(ci);
|
||||
if (TESTARG_k(i)) {
|
||||
int nparams1 = GETARG_C(i);
|
||||
luaF_close(L, base, LUA_OK); /* there may be open upvalues */
|
||||
if (nparams1) /* vararg function? */
|
||||
ci->func -= ci->u.l.nextraargs + nparams1;
|
||||
if (TESTARG_k(i)) { /* may there be open upvalues? */
|
||||
if (L->top < ci->top)
|
||||
L->top = ci->top;
|
||||
luaF_close(L, base, LUA_OK);
|
||||
updatetrap(ci);
|
||||
updatestack(ci);
|
||||
}
|
||||
if (nparams1) /* vararg function? */
|
||||
ci->func -= ci->u.l.nextraargs + nparams1;
|
||||
L->top = ra + n; /* set call for 'luaD_poscall' */
|
||||
luaD_poscall(L, ci, n);
|
||||
return;
|
||||
}
|
||||
vmcase(OP_RETURN0) {
|
||||
if (L->hookmask) {
|
||||
L->top = ra;
|
||||
halfProtect(luaD_poscall(L, ci, 0)); /* no hurry... */
|
||||
halfProtectNT(luaD_poscall(L, ci, 0)); /* no hurry... */
|
||||
}
|
||||
else { /* do the 'poscall' here */
|
||||
int nres = ci->nresults;
|
||||
@@ -1617,7 +1604,7 @@ void luaV_execute (lua_State *L, CallInfo *ci) {
|
||||
vmcase(OP_RETURN1) {
|
||||
if (L->hookmask) {
|
||||
L->top = ra + 1;
|
||||
halfProtect(luaD_poscall(L, ci, 1)); /* no hurry... */
|
||||
halfProtectNT(luaD_poscall(L, ci, 1)); /* no hurry... */
|
||||
}
|
||||
else { /* do the 'poscall' here */
|
||||
int nres = ci->nresults;
|
||||
@@ -1716,10 +1703,8 @@ void luaV_execute (lua_State *L, CallInfo *ci) {
|
||||
vmbreak;
|
||||
}
|
||||
vmcase(OP_TFORPREP) {
|
||||
if (!ttisnil(s2v(ra + 3))) { /* is 'toclose' not nil? */
|
||||
/* create to-be-closed upvalue for it */
|
||||
halfProtect(luaF_newtbcupval(L, ra + 3));
|
||||
}
|
||||
/* create to-be-closed upvalue (if needed) */
|
||||
halfProtect(luaF_newtbcupval(L, ra + 3));
|
||||
pc += GETARG_Bx(i);
|
||||
i = *(pc++); /* go to next instruction */
|
||||
lua_assert(GET_OPCODE(i) == OP_TFORCALL && ra == RA(i));
|
||||
@@ -1735,35 +1720,33 @@ void luaV_execute (lua_State *L, CallInfo *ci) {
|
||||
/* push function, state, and control variable */
|
||||
memcpy(ra + 4, ra, 3 * sizeof(*ra));
|
||||
L->top = ra + 4 + 3;
|
||||
Protect(luaD_call(L, ra + 4, GETARG_C(i))); /* do the call */
|
||||
ProtectNT(luaD_call(L, ra + 4, GETARG_C(i))); /* do the call */
|
||||
updatestack(ci); /* stack may have changed */
|
||||
i = *(pc++); /* go to next instruction */
|
||||
ra += 2; /* adjust for next instruction */
|
||||
lua_assert(GET_OPCODE(i) == OP_TFORLOOP && ra == RA(i));
|
||||
goto l_tforloop;
|
||||
}
|
||||
vmcase(OP_TFORLOOP) {
|
||||
l_tforloop:
|
||||
if (!ttisnil(s2v(ra + 2))) { /* continue loop? */
|
||||
setobjs2s(L, ra, ra + 2); /* save control variable */
|
||||
if (!ttisnil(s2v(ra + 4))) { /* continue loop? */
|
||||
setobjs2s(L, ra + 2, ra + 4); /* save control variable */
|
||||
pc -= GETARG_Bx(i); /* jump back */
|
||||
}
|
||||
vmbreak;
|
||||
}
|
||||
vmcase(OP_SETLIST) {
|
||||
int n = GETARG_B(i);
|
||||
int c = GETARG_C(i);
|
||||
unsigned int last;
|
||||
Table *h;
|
||||
unsigned int last = GETARG_C(i);
|
||||
Table *h = hvalue(s2v(ra));
|
||||
if (n == 0)
|
||||
n = cast_int(L->top - ra) - 1;
|
||||
n = cast_int(L->top - ra) - 1; /* get up to the top */
|
||||
else
|
||||
L->top = ci->top; /* correct top in case of GC */
|
||||
if (c == 0) {
|
||||
c = GETARG_Ax(*pc); pc++;
|
||||
L->top = ci->top; /* correct top in case of emergency GC */
|
||||
last += n;
|
||||
if (TESTARG_k(i)) {
|
||||
last += GETARG_Ax(*pc) * (MAXARG_C + 1);
|
||||
pc++;
|
||||
}
|
||||
h = hvalue(s2v(ra));
|
||||
last = ((c-1)*LFIELDS_PER_FLUSH) + n;
|
||||
if (last > luaH_realasize(h)) /* needs more space? */
|
||||
luaH_resizearray(L, h, last); /* preallocate it at once */
|
||||
for (; n > 0; n--) {
|
||||
@@ -1804,4 +1787,3 @@ void luaV_execute (lua_State *L, CallInfo *ci) {
|
||||
}
|
||||
|
||||
/* }================================================================== */
|
||||
|
||||
|
||||
@@ -6,6 +6,7 @@
|
||||
|
||||
# Warnings valid for both C and C++
|
||||
CWARNSCPP= \
|
||||
-fmax-errors=5 \
|
||||
-Wextra \
|
||||
-Wshadow \
|
||||
-Wsign-compare \
|
||||
@@ -14,14 +15,13 @@ CWARNSCPP= \
|
||||
-Wredundant-decls \
|
||||
-Wdisabled-optimization \
|
||||
-Wdouble-promotion \
|
||||
#-Wno-aggressive-loop-optimizations \
|
||||
#-Wlogical-op \
|
||||
#-Wfatal-errors \
|
||||
#-Wstrict-aliasing=3 \
|
||||
-Wlogical-op \
|
||||
-Wno-aggressive-loop-optimizations \
|
||||
# the next warnings might be useful sometimes,
|
||||
# but usually they generate too much noise
|
||||
# -Werror \
|
||||
# -pedantic # warns if we use jump tables \
|
||||
# the next warnings generate too much noise, so they are disabled
|
||||
# -Wconversion -Wno-sign-conversion \
|
||||
# -Wconversion \
|
||||
# -Wsign-conversion \
|
||||
# -Wstrict-overflow=2 \
|
||||
# -Wformat=2 \
|
||||
@@ -46,10 +46,10 @@ CWARNS= $(CWARNSCPP) $(CWARNSC)
|
||||
# -DLUA_USE_CTYPE -DLUA_USE_APICHECK
|
||||
# ('-ftrapv' for runtime checks of integer overflows)
|
||||
# -fsanitize=undefined -ftrapv -fno-inline
|
||||
TESTS= -DLUA_USER_H='"ltests.h"' -O0
|
||||
# TESTS= -DLUA_USER_H='"ltests.h"' -O0 -g
|
||||
|
||||
|
||||
# LOCAL = $(TESTS) $(CWARNS) -g
|
||||
LOCAL = $(TESTS) $(CWARNS)
|
||||
|
||||
|
||||
# enable Linux goodies
|
||||
@@ -106,8 +106,17 @@ $(LUA_T): $(LUA_O) $(CORE_T)
|
||||
$(LUAC_T): $(LUAC_O) $(CORE_T)
|
||||
$(CC) -o $@ $(MYLDFLAGS) $(LUAC_O) $(CORE_T) $(LIBS) $(MYLIBS)
|
||||
|
||||
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:
|
||||
rcsclean -u
|
||||
$(RM) $(ALL_T) $(ALL_O)
|
||||
|
||||
depend:
|
||||
|
||||
@@ -324,6 +324,7 @@ N = function (s) return (string.gsub(s, " ", " ")) end,
|
||||
NE = id, -- tag"foreignphrase",
|
||||
num = id,
|
||||
["nil"] = fixed(Tag.b"nil"),
|
||||
fail = fixed(Tag.b"fail"),
|
||||
Open = fixed"{",
|
||||
part = section("h1", true),
|
||||
Pat = compose(verbfixed, prepos("'", "'")),
|
||||
|
||||
+253
-159
@@ -223,7 +223,7 @@ In Lua, the global variable @Lid{_G} is initialized with this same value.
|
||||
so changing its value will affect only your own code.)
|
||||
|
||||
When Lua loads a chunk,
|
||||
the default value for its @id{_ENV} upvalue
|
||||
the default value for its @id{_ENV} variable
|
||||
is the global environment @seeF{load}.
|
||||
Therefore, by default,
|
||||
free names in Lua code refer to entries in the global environment
|
||||
@@ -233,7 +233,7 @@ and some functions there operate on that environment.
|
||||
You can use @Lid{load} (or @Lid{loadfile})
|
||||
to load a chunk with a different environment.
|
||||
(In C, you have to load the chunk and then change the value
|
||||
of its first upvalue.)
|
||||
of its first upvalue; see @See{lua_setupvalue}.)
|
||||
|
||||
}
|
||||
|
||||
@@ -295,9 +295,9 @@ although this behavior can be adapted from C @seeC{lua_setwarnf}.
|
||||
Every value in Lua can have a @emph{metatable}.
|
||||
This @def{metatable} is an ordinary Lua table
|
||||
that defines the behavior of the original value
|
||||
under certain special operations.
|
||||
under certain events.
|
||||
You can change several aspects of the behavior
|
||||
of operations over a value by setting specific fields in its metatable.
|
||||
of a value by setting specific fields in its metatable.
|
||||
For instance, when a non-numeric value is the operand of an addition,
|
||||
Lua checks for a function in the field @St{__add} of the value's metatable.
|
||||
If it finds one,
|
||||
@@ -306,7 +306,7 @@ Lua calls this function to perform the addition.
|
||||
The key for each event in a metatable is a string
|
||||
with the event name prefixed by two underscores;
|
||||
the corresponding values are called @def{metamethods}.
|
||||
In the previous example, the key is @St{__add}
|
||||
In the previous example, the key is the string @St{__add}
|
||||
and the metamethod is the function that performs the addition.
|
||||
Unless stated otherwise,
|
||||
metamethods should be function values.
|
||||
@@ -328,22 +328,10 @@ one for all strings, etc.
|
||||
By default, a value has no metatable,
|
||||
but the string library sets a metatable for the string type @see{strlib}.
|
||||
|
||||
A metatable controls how an object behaves in
|
||||
arithmetic operations, bitwise operations,
|
||||
order comparisons, concatenation, length operation, calls, and indexing.
|
||||
A metatable also can define a function to be called
|
||||
when a userdata or a table is @link{GC|garbage collected}.
|
||||
|
||||
For the unary operators (negation, length, and bitwise NOT),
|
||||
the metamethod is computed and called with a dummy second operand,
|
||||
equal to the first one.
|
||||
This extra operand is only to simplify Lua's internals
|
||||
(by making these operators behave like a binary operation)
|
||||
and may be removed in future versions.
|
||||
(For most uses this extra operand is irrelevant.)
|
||||
|
||||
A detailed list of events controlled by metatables is given next.
|
||||
Each operation is identified by its corresponding key.
|
||||
A detailed list of operations controlled by metatables is given next.
|
||||
Each event is identified by its corresponding key.
|
||||
By convention, all metatable keys used by Lua are composed by
|
||||
two underscores followed by lowercase Latin letters.
|
||||
|
||||
@description{
|
||||
|
||||
@@ -351,16 +339,16 @@ Each operation is identified by its corresponding key.
|
||||
the addition (@T{+}) operation.
|
||||
If any operand for an addition is not a number,
|
||||
Lua will try to call a metamethod.
|
||||
First, Lua will check the first operand (even if it is valid).
|
||||
If that operand does not define a metamethod for @idx{__add},
|
||||
It starts by checking the first operand (even if it is a number);
|
||||
if that operand does not define a metamethod for @idx{__add},
|
||||
then Lua will check the second operand.
|
||||
If Lua can find a metamethod,
|
||||
it calls the metamethod with the two operands as arguments,
|
||||
and the result of the call
|
||||
(adjusted to one value)
|
||||
is the result of the operation.
|
||||
Otherwise,
|
||||
it raises an error.
|
||||
Otherwise, if no metamethod is found,
|
||||
Lua raises an error.
|
||||
}
|
||||
|
||||
@item{@idx{__sub}|
|
||||
@@ -467,7 +455,7 @@ the less than (@T{<}) operation.
|
||||
Behavior similar to the addition operation,
|
||||
except that Lua will try a metamethod only when the values
|
||||
being compared are neither both numbers nor both strings.
|
||||
The result of the call is always converted to a boolean.
|
||||
Moreover, the result of the call is always converted to a boolean.
|
||||
}
|
||||
|
||||
@item{@idx{__le}|
|
||||
@@ -512,9 +500,9 @@ and therefore can trigger another metamethod.
|
||||
|
||||
Whenever there is a @idx{__newindex} metamethod,
|
||||
Lua does not perform the primitive assignment.
|
||||
(If necessary,
|
||||
If needed,
|
||||
the metamethod itself can call @Lid{rawset}
|
||||
to do the assignment.)
|
||||
to do the assignment.
|
||||
}
|
||||
|
||||
@item{@idx{__call}|
|
||||
@@ -526,16 +514,29 @@ If present,
|
||||
the metamethod is called with @id{func} as its first argument,
|
||||
followed by the arguments of the original call (@id{args}).
|
||||
All results of the call
|
||||
are the result of the operation.
|
||||
are the results of the operation.
|
||||
This is the only metamethod that allows multiple results.
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
It is a good practice to add all needed metamethods to a table
|
||||
before setting it as a metatable of some object.
|
||||
In particular, the @idx{__gc} metamethod works only when this order
|
||||
is followed @see{finalizers}.
|
||||
In addition to the previous list,
|
||||
the interpreter also respects the following keys in metatables:
|
||||
@idx{__gc} @see{finalizers},
|
||||
@idx{__close} @see{to-be-closed},
|
||||
@idx{__mode} @see{weak-table},
|
||||
and @idx{__name}.
|
||||
(The entry @idx{__name},
|
||||
when it contains a string,
|
||||
is used by some error-reporting functions to build error messages.)
|
||||
|
||||
For the unary operators (negation, length, and bitwise NOT),
|
||||
the metamethod is computed and called with a dummy second operand,
|
||||
equal to the first one.
|
||||
This extra operand is only to simplify Lua's internals
|
||||
(by making these operators behave like a binary operation)
|
||||
and may be removed in future versions.
|
||||
For most uses this extra operand is irrelevant.
|
||||
|
||||
Because metatables are regular tables,
|
||||
they can contain arbitrary fields,
|
||||
@@ -544,6 +545,13 @@ Some functions in the standard library
|
||||
(e.g., @Lid{tostring})
|
||||
use other fields in metatables for their own purposes.
|
||||
|
||||
It is a good practice to add all needed metamethods to a table
|
||||
before setting it as a metatable of some object.
|
||||
In particular, the @idx{__gc} metamethod works only when this order
|
||||
is followed @see{finalizers}.
|
||||
It is also a good practice to set the metatable of an object
|
||||
right after its creation.
|
||||
|
||||
}
|
||||
|
||||
@sect2{GC| @title{Garbage Collection}
|
||||
@@ -1012,7 +1020,7 @@ it must be expressed using exactly three digits.)
|
||||
The @x{UTF-8} encoding of a @x{Unicode} character
|
||||
can be inserted in a literal string with
|
||||
the escape sequence @T{\u{@rep{XXX}}}
|
||||
(with mandatory enclosing brackets),
|
||||
(with mandatory enclosing braces),
|
||||
where @rep{XXX} is a sequence of one or more hexadecimal digits
|
||||
representing the character code point.
|
||||
This code point can be any value less than @M{2@sp{31}}.
|
||||
@@ -1224,7 +1232,7 @@ As such, chunks can define local variables,
|
||||
receive arguments, and return values.
|
||||
Moreover, such anonymous function is compiled as in the
|
||||
scope of an external local variable called @id{_ENV} @see{globalenv}.
|
||||
The resulting function always has @id{_ENV} as its only upvalue,
|
||||
The resulting function always has @id{_ENV} as its only external variable,
|
||||
even if it does not use that variable.
|
||||
|
||||
A chunk can be stored in a file or in a string inside the host program.
|
||||
@@ -1399,23 +1407,30 @@ they must all result in numbers.
|
||||
Their values are called respectively
|
||||
the @emph{initial value}, the @emph{limit}, and the @emph{step}.
|
||||
If the step is absent, it defaults @N{to 1}.
|
||||
Then the loop body is repeated with the value of the control variable
|
||||
|
||||
If both the initial value and the step are integers,
|
||||
the loop is done with integers;
|
||||
note that the limit may not be an integer.
|
||||
Otherwise, the loop is done with floats.
|
||||
(Beware of floating-point accuracy in this case.)
|
||||
|
||||
After that initialization,
|
||||
the loop body is repeated with the value of the control variable
|
||||
going through an arithmetic progression,
|
||||
starting at the initial value,
|
||||
with a common difference given by the step,
|
||||
until that value passes the limit.
|
||||
with a common difference given by the step.
|
||||
A negative step makes a decreasing sequence;
|
||||
a step equal to zero raises an error.
|
||||
The loop continues while the value is less than
|
||||
or equal to the limit
|
||||
(greater than or equal to for a negative step).
|
||||
If the initial value is already greater than the limit
|
||||
(or less than, if the step is negative),
|
||||
the body is not executed.
|
||||
|
||||
If both the initial value and the step are integers,
|
||||
the loop is done with integers;
|
||||
in this case, the range of the control variable is clipped
|
||||
by the range of integers.
|
||||
Otherwise, the loop is done with floats.
|
||||
(Beware of floating-point accuracy in this case.)
|
||||
For integer loops,
|
||||
the control variable never wraps around;
|
||||
instead, the loop ends in case of an overflow.
|
||||
|
||||
You should not change the value of the control variable
|
||||
during the loop.
|
||||
@@ -1490,22 +1505,25 @@ Function calls are explained in @See{functioncall}.
|
||||
@x{Local variables} can be declared anywhere inside a block.
|
||||
The declaration can include an initialization:
|
||||
@Produc{
|
||||
@producname{stat}@producbody{@Rw{local} namelist @bnfopt{@bnfter{=} explist}}
|
||||
@producname{stat}@producbody{
|
||||
@Rw{local} @bnfter{<} Name @bnfter{>} Name @bnfter{=} exp
|
||||
}}
|
||||
@producname{stat}@producbody{@Rw{local} attnamelist @bnfopt{@bnfter{=} explist}}
|
||||
@producname{attnamelist}@producbody{
|
||||
@bnfNter{Name} attrib @bnfrep{@bnfter{,} @bnfNter{Name} attrib}}
|
||||
}
|
||||
If present, an initial assignment has the same semantics
|
||||
of a multiple assignment @see{assignment}.
|
||||
Otherwise, all variables are initialized with @nil.
|
||||
The second syntax declares a local with a given attribute,
|
||||
which is the name between the angle brackets.
|
||||
In this case, there must be an initialization.
|
||||
|
||||
Each variable name may be postfixed by an attribute
|
||||
(a name between angle brackets):
|
||||
@Produc{
|
||||
@producname{attrib}@producbody{@bnfopt{@bnfter{<} @bnfNter{Name} @bnfter{>}}}
|
||||
}
|
||||
There are two possible attributes:
|
||||
@id{const}, which declares a @x{constant variable},
|
||||
that is, a variable that cannot be assigned to
|
||||
after its initialization;
|
||||
and @id{toclose}, which declares a to-be-closed variable @see{to-be-closed}.
|
||||
|
||||
and @id{close}, which declares a to-be-closed variable @see{to-be-closed}.
|
||||
A list of variables can contain at most one to-be-closed variable.
|
||||
|
||||
A chunk is also a block @see{chunks},
|
||||
and so local variables can be declared in a chunk outside any explicit block.
|
||||
@@ -1516,12 +1534,6 @@ The visibility rules for local variables are explained in @See{visibility}.
|
||||
|
||||
@sect3{to-be-closed| @title{To-be-closed Variables}
|
||||
|
||||
A local variable can be declared as a @def{to-be-closed} variable,
|
||||
using the identifier @id{toclose} as its attribute:
|
||||
@Produc{
|
||||
@producname{stat}@producbody{
|
||||
@Rw{local} @bnfter{<} @id{toclose} @bnfter{>} Name @bnfter{=} exp
|
||||
}}
|
||||
A to-be-closed variable behaves like a constant local variable,
|
||||
except that its value is @emph{closed} whenever the variable
|
||||
goes out of scope, including normal block termination,
|
||||
@@ -1530,37 +1542,42 @@ or exiting by an error.
|
||||
|
||||
Here, to @emph{close} a value means
|
||||
to call its @idx{__close} metamethod.
|
||||
If the value is @nil, it is ignored;
|
||||
otherwise,
|
||||
if it does not have a @idx{__close} metamethod,
|
||||
an error is raised.
|
||||
When calling the metamethod,
|
||||
the value itself is passed as the first argument
|
||||
and the error object (if any) is passed as a second argument;
|
||||
and the error object that caused the exit (if any)
|
||||
is passed as a second argument;
|
||||
if there was no error, the second argument is @nil.
|
||||
|
||||
The value assigned to a to-be-closed variable
|
||||
must have a @idx{__close} metamethod
|
||||
or be a false value.
|
||||
(@nil and @false are ignored as to-be-closed values.)
|
||||
|
||||
If several to-be-closed variables go out of scope at the same event,
|
||||
they are closed in the reverse order that they were declared.
|
||||
|
||||
If there is any error while running a closing method,
|
||||
that error is handled like an error in the regular code
|
||||
where the variable was defined;
|
||||
in particular,
|
||||
the other pending closing methods will still be called.
|
||||
After an error,
|
||||
other errors in closing methods
|
||||
interrupt the respective method,
|
||||
but are otherwise ignored;
|
||||
the error reported is the original one.
|
||||
where the variable was defined.
|
||||
However, Lua may call the method one more time.
|
||||
|
||||
If a coroutine yields inside a block and is never resumed again,
|
||||
the variables visible at that block will never go out of scope,
|
||||
After an error,
|
||||
the other pending closing methods will still be called.
|
||||
Errors in these methods
|
||||
interrupt the respective method and generate a warning,
|
||||
but are otherwise ignored;
|
||||
the error reported is only the original one.
|
||||
|
||||
If a coroutine yields and is never resumed again,
|
||||
some variables may never go out of scope,
|
||||
and therefore they will never be closed.
|
||||
(These variables are the ones created inside the coroutine
|
||||
and in scope at the point where the coroutine yielded.)
|
||||
Similarly, if a coroutine ends with an error,
|
||||
it does not unwind its stack,
|
||||
so it does not close any variable.
|
||||
In both cases,
|
||||
you should either use finalizers
|
||||
you can either use finalizers
|
||||
or call @Lid{coroutine.close} to close the variables.
|
||||
However, if the coroutine was created
|
||||
through @Lid{coroutine.wrap},
|
||||
@@ -2235,8 +2252,8 @@ and so the second @id{x} refers to the outside variable.
|
||||
Because of the @x{lexical scoping} rules,
|
||||
local variables can be freely accessed by functions
|
||||
defined inside their scope.
|
||||
A local variable used by an inner function is called
|
||||
an @def{upvalue}, or @emphx{external local variable},
|
||||
A local variable used by an inner function is called an @def{upvalue}
|
||||
(or @emphx{external local variable}, or simply @emphx{external variable})
|
||||
inside the inner function.
|
||||
|
||||
Notice that each execution of a @Rw{local} statement
|
||||
@@ -2245,9 +2262,9 @@ Consider the following example:
|
||||
@verbatim{
|
||||
a = {}
|
||||
local x = 20
|
||||
for i=1,10 do
|
||||
for i = 1, 10 do
|
||||
local y = 0
|
||||
a[i] = function () y=y+1; return x+y end
|
||||
a[i] = function () y = y + 1; return x + y end
|
||||
end
|
||||
}
|
||||
The loop creates ten closures
|
||||
@@ -2910,8 +2927,9 @@ it is left unchanged.
|
||||
@APIEntry{void lua_close (lua_State *L);|
|
||||
@apii{0,0,-}
|
||||
|
||||
Destroys all objects in the given Lua state
|
||||
(calling the corresponding garbage-collection metamethods, if any)
|
||||
Close all active to-be-closed variables in the main thread,
|
||||
release all objects in the given Lua state
|
||||
(calling the corresponding garbage-collection metamethods, if any),
|
||||
and frees all dynamic memory used by this state.
|
||||
|
||||
On several platforms, you may not need to call this function,
|
||||
@@ -4048,12 +4066,15 @@ Returns 0 if the userdata does not have that value.
|
||||
|
||||
}
|
||||
|
||||
@APIEntry{void lua_setmetatable (lua_State *L, int index);|
|
||||
@APIEntry{int lua_setmetatable (lua_State *L, int index);|
|
||||
@apii{1,0,-}
|
||||
|
||||
Pops a table from the stack and
|
||||
sets it as the new metatable for the value at the given index.
|
||||
|
||||
(For historical reasons, this function returns an @id{int},
|
||||
which now is always 1.)
|
||||
|
||||
}
|
||||
|
||||
@APIEntry{void lua_settable (lua_State *L, int index);|
|
||||
@@ -4179,7 +4200,7 @@ An index marked as to-be-closed should not be removed from the stack
|
||||
by any other function in the API except @Lid{lua_settop} or @Lid{lua_pop}.
|
||||
|
||||
This function should not be called for an index
|
||||
that is equal to or below an already marked to-be-closed index.
|
||||
that is equal to or below an active to-be-closed index.
|
||||
|
||||
This function can raise an out-of-memory error.
|
||||
In that case, the value in the given index is immediately closed,
|
||||
@@ -4360,6 +4381,8 @@ The third parameter is a boolean that
|
||||
indicates whether the message is
|
||||
to be continued by the message in the next call.
|
||||
|
||||
See @Lid{warn} for more details about warnings.
|
||||
|
||||
}
|
||||
|
||||
@APIEntry{
|
||||
@@ -4370,6 +4393,8 @@ Emits a warning with the given message.
|
||||
A message in a call with @id{tocont} true should be
|
||||
continued in another call to this function.
|
||||
|
||||
See @Lid{warn} for more details about warnings.
|
||||
|
||||
}
|
||||
|
||||
@APIEntry{
|
||||
@@ -4759,11 +4784,7 @@ and returns its name.
|
||||
Returns @id{NULL} (and pushes nothing)
|
||||
when the index @id{n} is greater than the number of upvalues.
|
||||
|
||||
For @N{C functions}, this function uses the empty string @T{""}
|
||||
as a name for all upvalues.
|
||||
(For Lua functions,
|
||||
upvalues are the external local variables that the function uses,
|
||||
and that are consequently included in its closure.)
|
||||
See @Lid{debug.getupvalue} for more information about upvalues.
|
||||
|
||||
}
|
||||
|
||||
@@ -4801,6 +4822,20 @@ 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);|
|
||||
@apii{0,0,-}
|
||||
|
||||
@@ -5509,7 +5544,6 @@ creates a new table to be used as a metatable for userdata,
|
||||
adds to this new table the pair @T{__name = tname},
|
||||
adds to the registry the pair @T{[tname] = new table},
|
||||
and returns 1.
|
||||
(The entry @idx{__name} is used by some error-reporting functions.)
|
||||
|
||||
In both cases,
|
||||
the function pushes onto the stack the final value associated
|
||||
@@ -5758,7 +5792,7 @@ that will be called to close the stream
|
||||
when the handle is closed or collected;
|
||||
this function receives the file handle as its sole argument and
|
||||
must return either @true, in case of success,
|
||||
or @nil plus an error message, in case of error.
|
||||
or a false value plus an error message, in case of error.
|
||||
Once Lua calls this field,
|
||||
it changes the field value to @id{NULL}
|
||||
to signal that the handle is closed.
|
||||
@@ -5880,6 +5914,14 @@ to its expected parameters.
|
||||
For instance, a function documented as @T{foo(arg)}
|
||||
should not be called without an argument.
|
||||
|
||||
The notation @fail means a return value representing
|
||||
some kind of failure or the absence of a better value to return.
|
||||
Currently, @fail is equal to @nil,
|
||||
but that may change in future versions.
|
||||
The recommendation is to always test the success of these functions
|
||||
with @T{(not status)}, instead of @T{(status == nil)}.
|
||||
|
||||
|
||||
Currently, Lua has the following standard libraries:
|
||||
@itemize{
|
||||
|
||||
@@ -6084,8 +6126,8 @@ with previous results.
|
||||
A return of an empty string, @nil, or no value signals the end of the chunk.
|
||||
|
||||
If there are no syntactic errors,
|
||||
returns the compiled chunk as a function;
|
||||
otherwise, returns @nil plus the error message.
|
||||
@id{load} returns the compiled chunk as a function;
|
||||
otherwise, it returns @fail plus the error message.
|
||||
|
||||
When you load a main chunk,
|
||||
the resulting function will always have exactly one upvalue,
|
||||
@@ -6277,7 +6319,7 @@ When called with no @id{base},
|
||||
If the argument is already a number or
|
||||
a string convertible to a number,
|
||||
then @id{tonumber} returns this number;
|
||||
otherwise, it returns @nil.
|
||||
otherwise, it returns @fail.
|
||||
|
||||
The conversion of strings can result in integers or floats,
|
||||
according to the lexical conventions of Lua @see{lexical}.
|
||||
@@ -6291,7 +6333,7 @@ In bases @N{above 10}, the letter @Char{A} (in either upper or lower case)
|
||||
@N{represents 10}, @Char{B} @N{represents 11}, and so forth,
|
||||
with @Char{Z} representing 35.
|
||||
If the string @id{e} is not a valid numeral in the given base,
|
||||
the function returns @nil.
|
||||
the function returns @fail.
|
||||
|
||||
}
|
||||
|
||||
@@ -6335,6 +6377,16 @@ The current value of this variable is @St{Lua 5.4}.
|
||||
Emits a warning with a message composed by the concatenation
|
||||
of all its arguments (which should be strings).
|
||||
|
||||
By convention,
|
||||
a one-piece message starting with @Char{@At}
|
||||
is intended to be a @emph{control message},
|
||||
which is a message to the warning system itself.
|
||||
In particular, the standard warning function in Lua
|
||||
recognizes the control messages @St{@At{}off},
|
||||
to stop the emission of warnings,
|
||||
and @St{@At{}on}, to (re)start the emission;
|
||||
it ignores unknown control messages.
|
||||
|
||||
}
|
||||
|
||||
@LibEntry{xpcall (f, msgh [, arg1, @Cdots])|
|
||||
@@ -6728,7 +6780,7 @@ will try to open the files
|
||||
|
||||
Returns the resulting name of the first file that it can
|
||||
open in read mode (after closing the file),
|
||||
or @nil plus an error message if none succeeds.
|
||||
or @fail plus an error message if none succeeds.
|
||||
(This error message lists all file names it tried to open.)
|
||||
|
||||
}
|
||||
@@ -6807,7 +6859,7 @@ Looks for the first match of
|
||||
@id{pattern} @see{pm} in the string @id{s}.
|
||||
If it finds a match, then @id{find} returns the indices @N{of @T{s}}
|
||||
where this occurrence starts and ends;
|
||||
otherwise, it returns @nil.
|
||||
otherwise, it returns @fail.
|
||||
A third, optional numeric argument @id{init} specifies
|
||||
where to start the search;
|
||||
its default value @N{is 1} and can be negative.
|
||||
@@ -6815,7 +6867,6 @@ A value of @true as a fourth, optional argument @id{plain}
|
||||
turns off the pattern matching facilities,
|
||||
so the function does a plain @Q{find substring} operation,
|
||||
with no characters in @id{pattern} being considered magic.
|
||||
Note that if @id{plain} is given, then @id{init} must be given as well.
|
||||
|
||||
If the pattern has captures,
|
||||
then in a successful match
|
||||
@@ -7001,7 +7052,7 @@ Looks for the first @emph{match} of
|
||||
the @id{pattern} @see{pm} in the string @id{s}.
|
||||
If it finds one, then @id{match} returns
|
||||
the captures from the pattern;
|
||||
otherwise it returns @nil.
|
||||
otherwise it returns @fail.
|
||||
If @id{pattern} specifies no captures,
|
||||
then the whole match is returned.
|
||||
A third, optional numeric argument @id{init} specifies
|
||||
@@ -7274,7 +7325,7 @@ stored as the first capture, and therefore has @N{number 1};
|
||||
the character matching @St{.} is captured with @N{number 2},
|
||||
and the part matching @St{%s*} has @N{number 3}.
|
||||
|
||||
As a special case, the empty capture @T{()} captures
|
||||
As a special case, the capture @T{()} captures
|
||||
the current string position (a number).
|
||||
For instance, if we apply the pattern @T{"()aa()"} on the
|
||||
string @T{"flaaap"}, there will be two captures: @N{3 and 5}.
|
||||
@@ -7466,7 +7517,7 @@ Returns the number of UTF-8 characters in string @id{s}
|
||||
that start between positions @id{i} and @id{j} (both inclusive).
|
||||
The default for @id{i} is @num{1} and for @id{j} is @num{-1}.
|
||||
If it finds any invalid byte sequence,
|
||||
returns a false value plus the position of the first invalid byte.
|
||||
returns @fail plus the position of the first invalid byte.
|
||||
|
||||
}
|
||||
|
||||
@@ -7482,7 +7533,7 @@ so that @T{utf8.offset(s, -n)} gets the offset of the
|
||||
@id{n}-th character from the end of the string.
|
||||
If the specified character is neither in the subject
|
||||
nor right after its end,
|
||||
the function returns @nil.
|
||||
the function returns @fail.
|
||||
|
||||
As a special case,
|
||||
when @id{n} is 0 the function returns the start of the encoding
|
||||
@@ -7782,6 +7833,11 @@ The default for @id{y} is zero.
|
||||
When called with no arguments,
|
||||
Lua generates a seed with
|
||||
a weak attempt for randomness.
|
||||
|
||||
This function returns the two seed components
|
||||
that were effectively used,
|
||||
so that setting them again repeats the sequence.
|
||||
|
||||
To ensure a required level of randomness to the initial state
|
||||
(or contrarily, to have a deterministic sequence,
|
||||
for instance when debugging a program),
|
||||
@@ -7812,7 +7868,7 @@ Returns the tangent of @id{x} (assumed to be in radians).
|
||||
|
||||
If the value @id{x} is convertible to an integer,
|
||||
returns that integer.
|
||||
Otherwise, returns @nil.
|
||||
Otherwise, returns @fail.
|
||||
|
||||
}
|
||||
|
||||
@@ -7820,7 +7876,7 @@ Otherwise, returns @nil.
|
||||
|
||||
Returns @St{integer} if @id{x} is an integer,
|
||||
@St{float} if it is a float,
|
||||
or @nil if @id{x} is not a number.
|
||||
or @fail if @id{x} is not a number.
|
||||
|
||||
}
|
||||
|
||||
@@ -7834,7 +7890,6 @@ they are compared as @x{unsigned integers}.
|
||||
|
||||
}
|
||||
|
||||
|
||||
@sect2{iolib| @title{Input and Output Facilities}
|
||||
|
||||
The I/O library provides two different styles for file manipulation.
|
||||
@@ -7860,10 +7915,10 @@ three predefined file handles with their usual meanings from C:
|
||||
The I/O library never closes these files.
|
||||
|
||||
Unless otherwise stated,
|
||||
all I/O functions return @nil on failure,
|
||||
all I/O functions return @fail on failure,
|
||||
plus an error message as a second result and
|
||||
a system-dependent error code as a third result,
|
||||
and some value different from @nil on success.
|
||||
and some non-false value on success.
|
||||
On non-POSIX systems,
|
||||
the computation of the error message and error code
|
||||
in case of errors
|
||||
@@ -7902,8 +7957,8 @@ instead of returning an error code.
|
||||
Opens the given file name in read mode
|
||||
and returns an iterator function that
|
||||
works like @T{file:lines(@Cdots)} over the opened file.
|
||||
When the iterator function detects the end of file,
|
||||
it returns no values (to finish the loop) and automatically closes the file.
|
||||
When the iterator function fails to read any value,
|
||||
it automatically closes the file.
|
||||
Besides the iterator function,
|
||||
@id{io.lines} returns three other values:
|
||||
two @nil values as placeholders,
|
||||
@@ -7917,7 +7972,8 @@ to @T{io.input():lines("l")};
|
||||
that is, it iterates over the lines of the default input file.
|
||||
In this case, the iterator does not close the file when the loop ends.
|
||||
|
||||
In case of errors this function raises the error,
|
||||
In case of errors opening the file,
|
||||
this function raises the error,
|
||||
instead of returning an error code.
|
||||
|
||||
}
|
||||
@@ -7983,7 +8039,7 @@ and it is automatically removed when the program ends.
|
||||
Checks whether @id{obj} is a valid file handle.
|
||||
Returns the string @T{"file"} if @id{obj} is an open file handle,
|
||||
@T{"closed file"} if @id{obj} is a closed file handle,
|
||||
or @nil if @id{obj} is not a file handle.
|
||||
or @fail if @id{obj} is not a file handle.
|
||||
|
||||
}
|
||||
|
||||
@@ -8029,9 +8085,6 @@ starting at the current position.
|
||||
Unlike @Lid{io.lines}, this function does not close the file
|
||||
when the loop ends.
|
||||
|
||||
In case of errors this function raises the error,
|
||||
instead of returning an error code.
|
||||
|
||||
}
|
||||
|
||||
@LibEntry{file:read (@Cdots)|
|
||||
@@ -8040,7 +8093,7 @@ Reads the file @id{file},
|
||||
according to the given formats, which specify what to read.
|
||||
For each format,
|
||||
the function returns a string or a number with the characters read,
|
||||
or @nil if it cannot read data with the specified format.
|
||||
or @fail if it cannot read data with the specified format.
|
||||
(In this latter case,
|
||||
the function does not read subsequent formats.)
|
||||
When called without arguments,
|
||||
@@ -8059,31 +8112,32 @@ is a valid prefix for a numeral;
|
||||
if that prefix does not form a valid numeral
|
||||
(e.g., an empty string, @St{0x}, or @St{3.4e-})
|
||||
or it is too long (more than 200 characters),
|
||||
it is discarded and the format returns @nil.
|
||||
it is discarded and the format returns @fail.
|
||||
}
|
||||
|
||||
@item{@St{a}|
|
||||
reads the whole file, starting at the current position.
|
||||
On end of file, it returns the empty string.
|
||||
On end of file, it returns the empty string;
|
||||
this format never fails.
|
||||
}
|
||||
|
||||
@item{@St{l}|
|
||||
reads the next line skipping the end of line,
|
||||
returning @nil on end of file.
|
||||
returning @fail on end of file.
|
||||
This is the default format.
|
||||
}
|
||||
|
||||
@item{@St{L}|
|
||||
reads the next line keeping the end-of-line character (if present),
|
||||
returning @nil on end of file.
|
||||
returning @fail on end of file.
|
||||
}
|
||||
|
||||
@item{@emph{number}|
|
||||
reads a string with up to this number of bytes,
|
||||
returning @nil on end of file.
|
||||
returning @fail on end of file.
|
||||
If @id{number} is zero,
|
||||
it reads nothing and returns an empty string,
|
||||
or @nil on end of file.
|
||||
or @fail on end of file.
|
||||
}
|
||||
|
||||
}
|
||||
@@ -8104,7 +8158,7 @@ specified by the string @id{whence}, as follows:
|
||||
}
|
||||
In case of success, @id{seek} returns the final file position,
|
||||
measured in bytes from the beginning of the file.
|
||||
If @id{seek} fails, it returns @nil,
|
||||
If @id{seek} fails, it returns @fail,
|
||||
plus a string describing the error.
|
||||
|
||||
The default value for @id{whence} is @T{"cur"},
|
||||
@@ -8120,31 +8174,22 @@ end of the file, and returns its size.
|
||||
|
||||
@LibEntry{file:setvbuf (mode [, size])|
|
||||
|
||||
Sets the buffering mode for an output file.
|
||||
Sets the buffering mode for a file.
|
||||
There are three available modes:
|
||||
@description{
|
||||
|
||||
@item{@St{no}|
|
||||
no buffering; the result of any output operation appears immediately.
|
||||
@item{@St{no}| no buffering.}
|
||||
@item{@St{full}| full buffering.}
|
||||
@item{@St{line}| line buffering.}
|
||||
}
|
||||
|
||||
@item{@St{full}|
|
||||
full buffering; output operation is performed only
|
||||
when the buffer is full or when
|
||||
you explicitly @T{flush} the file @seeF{io.flush}.
|
||||
}
|
||||
|
||||
@item{@St{line}|
|
||||
line buffering; output is buffered until a newline is output
|
||||
or there is any input from some special files
|
||||
(such as a terminal device).
|
||||
}
|
||||
|
||||
}
|
||||
For the last two cases,
|
||||
@id{size} is a hint for the size of the buffer, in bytes.
|
||||
The default is an appropriate size.
|
||||
|
||||
The specific behavior of each mode is non portable;
|
||||
check the underlying @ANSI{setvbuf} in your platform for
|
||||
more details.
|
||||
|
||||
}
|
||||
|
||||
@LibEntry{file:write (@Cdots)|
|
||||
@@ -8153,7 +8198,6 @@ Writes the value of each of its arguments to @id{file}.
|
||||
The arguments must be strings or numbers.
|
||||
|
||||
In case of success, this function returns @id{file}.
|
||||
Otherwise it returns @nil plus a string describing the error.
|
||||
|
||||
}
|
||||
|
||||
@@ -8200,7 +8244,7 @@ then @id{date} returns the date as a string,
|
||||
formatted according to the same rules as the @ANSI{strftime}.
|
||||
|
||||
If @id{format} is absent, it defaults to @St{%c},
|
||||
which gives a reasonable date and time representation
|
||||
which gives a human-readable date and time representation
|
||||
using the current locale.
|
||||
|
||||
On non-POSIX systems,
|
||||
@@ -8225,7 +8269,7 @@ This function is equivalent to the @ANSI{system}.
|
||||
It passes @id{command} to be executed by an operating system shell.
|
||||
Its first result is @true
|
||||
if the command terminated successfully,
|
||||
or @nil otherwise.
|
||||
or @fail otherwise.
|
||||
After this first result
|
||||
the function returns a string plus a number,
|
||||
as follows:
|
||||
@@ -8267,7 +8311,7 @@ closes the Lua state before exiting.
|
||||
@LibEntry{os.getenv (varname)|
|
||||
|
||||
Returns the value of the process environment variable @id{varname},
|
||||
or @nil if the variable is not defined.
|
||||
or @fail if the variable is not defined.
|
||||
|
||||
}
|
||||
|
||||
@@ -8275,7 +8319,7 @@ or @nil if the variable is not defined.
|
||||
|
||||
Deletes the file (or empty directory, on @x{POSIX} systems)
|
||||
with the given name.
|
||||
If this function fails, it returns @nil,
|
||||
If this function fails, it returns @fail
|
||||
plus a string describing the error and the error code.
|
||||
Otherwise, it returns true.
|
||||
|
||||
@@ -8284,7 +8328,7 @@ Otherwise, it returns true.
|
||||
@LibEntry{os.rename (oldname, newname)|
|
||||
|
||||
Renames the file or directory named @id{oldname} to @id{newname}.
|
||||
If this function fails, it returns @nil,
|
||||
If this function fails, it returns @fail,
|
||||
plus a string describing the error and the error code.
|
||||
Otherwise, it returns true.
|
||||
|
||||
@@ -8299,7 +8343,7 @@ Sets the current locale of the program.
|
||||
@T{"monetary"}, @T{"numeric"}, or @T{"time"};
|
||||
the default category is @T{"all"}.
|
||||
The function returns the name of the new locale,
|
||||
or @nil if the request cannot be honored.
|
||||
or @fail if the request cannot be honored.
|
||||
|
||||
If @id{locale} is the empty string,
|
||||
the current locale is set to an implementation-defined native locale.
|
||||
@@ -8418,6 +8462,8 @@ the current hook function, the current hook mask,
|
||||
and the current hook count,
|
||||
as set by the @Lid{debug.sethook} function.
|
||||
|
||||
Returns @fail if there is no active hook.
|
||||
|
||||
}
|
||||
|
||||
@LibEntry{debug.getinfo ([thread,] f [, what])|
|
||||
@@ -8432,7 +8478,7 @@ of the given thread:
|
||||
(except for tail calls, which do not count on the stack);
|
||||
and so on.
|
||||
If @id{f} is a number greater than the number of active functions,
|
||||
then @id{getinfo} returns @nil.
|
||||
then @id{getinfo} returns @fail.
|
||||
|
||||
The returned table can contain all the fields returned by @Lid{lua_getinfo},
|
||||
with the string @id{what} describing which fields to fill in.
|
||||
@@ -8466,9 +8512,12 @@ The first parameter or local variable has @N{index 1}, and so on,
|
||||
following the order that they are declared in the code,
|
||||
counting only the variables that are active
|
||||
in the current scope of the function.
|
||||
Compile-time constants may not appear in this listing,
|
||||
if they were optimized away by the compiler.
|
||||
Negative indices refer to vararg arguments;
|
||||
@num{-1} is the first vararg argument.
|
||||
The function returns @nil if there is no variable with the given index,
|
||||
The function returns @fail
|
||||
if there is no variable with the given index,
|
||||
and raises an error when called with a level out of range.
|
||||
(You can call @Lid{debug.getinfo} to check whether the level is valid.)
|
||||
|
||||
@@ -8499,10 +8548,18 @@ Returns the registry table @see{registry}.
|
||||
|
||||
This function returns the name and the value of the upvalue
|
||||
with index @id{up} of the function @id{f}.
|
||||
The function returns @nil if there is no upvalue with the given index.
|
||||
The function returns @fail
|
||||
if there is no upvalue with the given index.
|
||||
|
||||
Variable names starting with @Char{(} (open parenthesis) @C{)}
|
||||
represent variables with no known names
|
||||
(For Lua functions,
|
||||
upvalues are the external local variables that the function uses,
|
||||
and that are consequently included in its closure.)
|
||||
|
||||
For @N{C functions}, this function uses the empty string @T{""}
|
||||
as a name for all upvalues.
|
||||
|
||||
Variable name @Char{?} (interrogation mark)
|
||||
represents variables with no known names
|
||||
(variables from chunks saved without debug information).
|
||||
|
||||
}
|
||||
@@ -8515,6 +8572,34 @@ 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 falsy value.
|
||||
Otherwise,
|
||||
the call returns the old limit.
|
||||
|
||||
}
|
||||
|
||||
@LibEntry{debug.sethook ([thread,] hook, mask [, count])|
|
||||
|
||||
Sets the given function as the debug hook.
|
||||
@@ -8552,7 +8637,7 @@ and @N{level 1} is the hook function.)
|
||||
|
||||
This function assigns the value @id{value} to the local variable
|
||||
with index @id{local} of the function at level @id{level} of the stack.
|
||||
The function returns @nil if there is no local
|
||||
The function returns @fail if there is no local
|
||||
variable with the given index,
|
||||
and raises an error when called with a @id{level} out of range.
|
||||
(You can call @id{getinfo} to check whether the level is valid.)
|
||||
@@ -8575,10 +8660,12 @@ Returns @id{value}.
|
||||
|
||||
This function assigns the value @id{value} to the upvalue
|
||||
with index @id{up} of the function @id{f}.
|
||||
The function returns @nil if there is no upvalue
|
||||
The function returns @fail if there is no upvalue
|
||||
with the given index.
|
||||
Otherwise, it returns the name of the upvalue.
|
||||
|
||||
See @Lid{debug.getupvalue} for more information about upvalues.
|
||||
|
||||
}
|
||||
|
||||
@LibEntry{debug.setuservalue (udata, value, n)|
|
||||
@@ -8588,7 +8675,7 @@ the @id{n}-th user value associated to the given @id{udata}.
|
||||
@id{udata} must be a full userdata.
|
||||
|
||||
Returns @id{udata},
|
||||
or @nil if the userdata does not have that value.
|
||||
or @fail if the userdata does not have that value.
|
||||
|
||||
}
|
||||
|
||||
@@ -8655,6 +8742,7 @@ The options are:
|
||||
@item{@T{-i}| enters interactive mode after running @rep{script};}
|
||||
@item{@T{-v}| prints version information;}
|
||||
@item{@T{-E}| ignores environment variables;}
|
||||
@item{@T{-W}| turn warnings on;}
|
||||
@item{@T{--}| stops handling options;}
|
||||
@item{@T{-}| executes @id{stdin} as a file and stops handling options.}
|
||||
}
|
||||
@@ -8680,12 +8768,13 @@ setting the values of
|
||||
@Lid{package.path} and @Lid{package.cpath}
|
||||
with the default paths defined in @id{luaconf.h}.
|
||||
|
||||
All options are handled in order, except @T{-i} and @T{-E}.
|
||||
The options @T{-e}, @T{-l}, and @T{-W} are handled in
|
||||
the order they appear.
|
||||
For instance, an invocation like
|
||||
@verbatim{
|
||||
$ lua -e'a=1' -e 'print(a)' script.lua
|
||||
$ lua -e 'a=1' -llib1 script.lua
|
||||
}
|
||||
will first set @id{a} to 1, then print the value of @id{a},
|
||||
will first set @id{a} to 1, then require the library @id{lib1},
|
||||
and finally run the file @id{script.lua} with no arguments.
|
||||
(Here @T{$} is the shell prompt. Your prompt may be different.)
|
||||
|
||||
@@ -8745,7 +8834,8 @@ has a metamethod @idx{__tostring},
|
||||
the interpreter calls this metamethod to produce the final message.
|
||||
Otherwise, the interpreter converts the error object to a string
|
||||
and adds a stack traceback to it.
|
||||
Warnings are simply printed in the standard error output.
|
||||
When warnings are on,
|
||||
they are simply printed in the standard error output.
|
||||
|
||||
When finishing normally,
|
||||
the interpreter closes its main Lua state
|
||||
@@ -8979,10 +9069,14 @@ and @bnfNter{LiteralString}, see @See{lexical}.)
|
||||
@OrNL @Rw{for} namelist @Rw{in} explist @Rw{do} block @Rw{end}
|
||||
@OrNL @Rw{function} funcname funcbody
|
||||
@OrNL @Rw{local} @Rw{function} @bnfNter{Name} funcbody
|
||||
@OrNL @Rw{local} namelist @bnfopt{@bnfter{=} explist}
|
||||
@OrNL @Rw{local} @bnfter{<} Name @bnfter{>} Name @bnfter{=} exp
|
||||
@OrNL @Rw{local} attnamelist @bnfopt{@bnfter{=} explist}
|
||||
}
|
||||
|
||||
@producname{attnamelist}@producbody{
|
||||
@bnfNter{Name} attrib @bnfrep{@bnfter{,} @bnfNter{Name} attrib}}
|
||||
|
||||
@producname{attrib}@producbody{@bnfopt{@bnfter{<} @bnfNter{Name} @bnfter{>}}}
|
||||
|
||||
@producname{retstat}@producbody{@Rw{return}
|
||||
@bnfopt{explist} @bnfopt{@bnfter{;}}}
|
||||
|
||||
|
||||
+29
-5
@@ -5,8 +5,8 @@
|
||||
|
||||
local version = "Lua 5.4"
|
||||
if _VERSION ~= version then
|
||||
warn("This test suite is for ", version,
|
||||
", not for ", _VERSION, "\nExiting tests")
|
||||
io.stderr:write("This test suite is for ", version,
|
||||
", not for ", _VERSION, "\nExiting tests")
|
||||
return
|
||||
end
|
||||
|
||||
@@ -37,8 +37,6 @@ end
|
||||
-- tests should require debug when needed
|
||||
debug = nil
|
||||
|
||||
require"bwcoercion"
|
||||
|
||||
|
||||
if usertests then
|
||||
T = nil -- no "internal" tests for user tests
|
||||
@@ -46,7 +44,6 @@ else
|
||||
T = rawget(_G, "T") -- avoid problems with 'strict' module
|
||||
end
|
||||
|
||||
math.randomseed(0)
|
||||
|
||||
--[=[
|
||||
example of a long [comment],
|
||||
@@ -54,6 +51,14 @@ math.randomseed(0)
|
||||
|
||||
]=]
|
||||
|
||||
print("\n\tStarting Tests")
|
||||
|
||||
do
|
||||
-- set random seed
|
||||
local random_x, random_y = math.randomseed()
|
||||
print(string.format("random seeds: %d, %d", random_x, random_y))
|
||||
end
|
||||
|
||||
print("current path:\n****" .. package.path .. "****\n")
|
||||
|
||||
|
||||
@@ -95,6 +100,8 @@ local function F (m)
|
||||
end
|
||||
end
|
||||
|
||||
local Cstacklevel
|
||||
|
||||
local showmem
|
||||
if not T then
|
||||
local max = 0
|
||||
@@ -104,6 +111,7 @@ if not T then
|
||||
print(format(" ---- total memory: %s, max memory: %s ----\n",
|
||||
F(m), F(max)))
|
||||
end
|
||||
Cstacklevel = function () return 0 end -- no info about stack level
|
||||
else
|
||||
showmem = function ()
|
||||
T.checkmemory()
|
||||
@@ -117,9 +125,16 @@ else
|
||||
T.totalmem"string", T.totalmem"table", T.totalmem"function",
|
||||
T.totalmem"userdata", T.totalmem"thread"))
|
||||
end
|
||||
|
||||
Cstacklevel = function ()
|
||||
local _, _, ncalls, nci = T.stacklevel()
|
||||
return ncalls + nci -- number of free slots in the C stack
|
||||
end
|
||||
end
|
||||
|
||||
|
||||
local Cstack = Cstacklevel()
|
||||
|
||||
--
|
||||
-- redefine dofile to run files through dump/undump
|
||||
--
|
||||
@@ -195,7 +210,12 @@ if #msgs > 0 then
|
||||
end
|
||||
|
||||
print("(there should be two warnings now)")
|
||||
warn("@on")
|
||||
warn("#This is ", "an expected", " warning")
|
||||
warn("@off")
|
||||
warn("******** THIS WARNING SHOULD NOT APPEAR **********")
|
||||
warn("******** THIS WARNING ALSO SHOULD NOT APPEAR **********")
|
||||
warn("@on")
|
||||
warn("#This is", " another one")
|
||||
|
||||
-- no test module should define 'debug'
|
||||
@@ -211,6 +231,10 @@ debug.sethook(function (a) assert(type(a) == 'string') end, "cr")
|
||||
-- to survive outside block
|
||||
_G.showmem = showmem
|
||||
|
||||
|
||||
assert(Cstack == Cstacklevel(),
|
||||
"should be at the same C-stack level it was when started the tests")
|
||||
|
||||
end --)
|
||||
|
||||
local _G, showmem, print, format, clock, time, difftime,
|
||||
|
||||
+83
-6
@@ -241,6 +241,23 @@ assert(a == 20 and b == false)
|
||||
a,b = T.testC("compare LE 5 -6, return 2", a1, 2, 2, a1, 2, 20)
|
||||
assert(a == 20 and b == true)
|
||||
|
||||
|
||||
do -- testing lessthan and lessequal with metamethods
|
||||
local mt = {__lt = function (a,b) return a[1] < b[1] end,
|
||||
__le = function (a,b) return a[1] <= b[1] end,
|
||||
__eq = function (a,b) return a[1] == b[1] end}
|
||||
local function O (x)
|
||||
return setmetatable({x}, mt)
|
||||
end
|
||||
|
||||
local a, b = T.testC("compare LT 2 3; pushint 10; return 2", O(1), O(2))
|
||||
assert(a == true and b == 10)
|
||||
local a, b = T.testC("compare LE 2 3; pushint 10; return 2", O(3), O(2))
|
||||
assert(a == false and b == 10)
|
||||
local a, b = T.testC("compare EQ 2 3; pushint 10; return 2", O(3), O(3))
|
||||
assert(a == true and b == 10)
|
||||
end
|
||||
|
||||
-- testing length
|
||||
local t = setmetatable({x = 20}, {__len = function (t) return t.x end})
|
||||
a,b,c = T.testC([[
|
||||
@@ -354,8 +371,11 @@ assert(to("topointer", nil) == null)
|
||||
assert(to("topointer", "abc") ~= null)
|
||||
assert(to("topointer", string.rep("x", 10)) ==
|
||||
to("topointer", string.rep("x", 10))) -- short strings
|
||||
assert(to("topointer", string.rep("x", 300)) ~=
|
||||
to("topointer", string.rep("x", 300))) -- long strings
|
||||
do -- long strings
|
||||
local s1 = string.rep("x", 300)
|
||||
local s2 = string.rep("x", 300)
|
||||
assert(to("topointer", s1) ~= to("topointer", s2))
|
||||
end
|
||||
assert(to("topointer", T.pushuserdata(20)) ~= null)
|
||||
assert(to("topointer", io.read) ~= null) -- light C function
|
||||
assert(to("topointer", hfunc) ~= null) -- "heavy" C function
|
||||
@@ -498,7 +518,53 @@ do -- getp/setp
|
||||
local a = {}
|
||||
T.testC("rawsetp 2 1", a, 20)
|
||||
assert(a[T.pushuserdata(1)] == 20)
|
||||
assert(T.testC("rawgetp 2 1; return 1", a) == 20)
|
||||
assert(T.testC("rawgetp -1 1; return 1", a) == 20)
|
||||
end
|
||||
|
||||
|
||||
do -- using the table itself as index
|
||||
local a = {}
|
||||
a[a] = 10
|
||||
local prog = "gettable -1; return *"
|
||||
local res = {T.testC(prog, a)}
|
||||
assert(#res == 2 and res[1] == prog and res[2] == 10)
|
||||
|
||||
local prog = "settable -2; return *"
|
||||
local res = {T.testC(prog, a, 20)}
|
||||
assert(a[a] == 20)
|
||||
assert(#res == 1 and res[1] == prog)
|
||||
|
||||
-- raw
|
||||
a[a] = 10
|
||||
local prog = "rawget -1; return *"
|
||||
local res = {T.testC(prog, a)}
|
||||
assert(#res == 2 and res[1] == prog and res[2] == 10)
|
||||
|
||||
local prog = "rawset -2; return *"
|
||||
local res = {T.testC(prog, a, 20)}
|
||||
assert(a[a] == 20)
|
||||
assert(#res == 1 and res[1] == prog)
|
||||
|
||||
-- using the table as the value to set
|
||||
local prog = "rawset -1; return *"
|
||||
local res = {T.testC(prog, 30, a)}
|
||||
assert(a[30] == a)
|
||||
assert(#res == 1 and res[1] == prog)
|
||||
|
||||
local prog = "settable -1; return *"
|
||||
local res = {T.testC(prog, 40, a)}
|
||||
assert(a[40] == a)
|
||||
assert(#res == 1 and res[1] == prog)
|
||||
|
||||
local prog = "rawseti -1 100; return *"
|
||||
local res = {T.testC(prog, a)}
|
||||
assert(a[100] == a)
|
||||
assert(#res == 1 and res[1] == prog)
|
||||
|
||||
local prog = "seti -1 200; return *"
|
||||
local res = {T.testC(prog, a)}
|
||||
assert(a[200] == a)
|
||||
assert(#res == 1 and res[1] == prog)
|
||||
end
|
||||
|
||||
a = {x=0, y=12}
|
||||
@@ -632,7 +698,7 @@ for k, v in ipairs(t) do
|
||||
assert(v1 == v and p)
|
||||
end
|
||||
|
||||
assert(debug.getuservalue(4) == nil)
|
||||
assert(not debug.getuservalue(4))
|
||||
|
||||
debug.setuservalue(b, function () return 10 end, 10)
|
||||
collectgarbage() -- function should not be collected
|
||||
@@ -911,6 +977,7 @@ assert(t[7] == nil)
|
||||
|
||||
-------------------------------------------------------------------------
|
||||
do -- testing errors during GC
|
||||
warn("@off")
|
||||
collectgarbage("stop")
|
||||
local a = {}
|
||||
for i=1,20 do
|
||||
@@ -928,6 +995,7 @@ do -- testing errors during GC
|
||||
collectgarbage()
|
||||
assert(A == 10) -- number of normal collections
|
||||
collectgarbage("restart")
|
||||
warn("@on")
|
||||
end
|
||||
-------------------------------------------------------------------------
|
||||
-- test for userdata vals
|
||||
@@ -1030,7 +1098,7 @@ do
|
||||
assert(type(a[1]) == "string" and a[2][1] == 11)
|
||||
assert(#openresource == 0) -- was closed
|
||||
|
||||
-- error
|
||||
-- closing by error
|
||||
local a, b = pcall(T.makeCfunc[[
|
||||
call 0 1 # create resource
|
||||
toclose -1 # mark it to be closed
|
||||
@@ -1039,6 +1107,15 @@ do
|
||||
assert(a == false and b[1] == 11)
|
||||
assert(#openresource == 0) -- was closed
|
||||
|
||||
-- non-closable value
|
||||
local a, b = pcall(T.makeCfunc[[
|
||||
newtable # create non-closable object
|
||||
toclose -1 # mark it to be closed (shoud raise an error)
|
||||
abort # will not be executed
|
||||
]])
|
||||
assert(a == false and
|
||||
string.find(b, "non%-closable value"))
|
||||
|
||||
local function check (n)
|
||||
assert(#openresource == n)
|
||||
end
|
||||
@@ -1137,7 +1214,7 @@ end)
|
||||
testamem("to-be-closed variables", function()
|
||||
local flag
|
||||
do
|
||||
local <toclose> x =
|
||||
local x <close> =
|
||||
setmetatable({}, {__close = function () flag = true end})
|
||||
flag = false
|
||||
local x = {}
|
||||
|
||||
+3
-3
@@ -60,9 +60,9 @@ assert("1234.0" << "5.0" == 1234 * 32)
|
||||
assert("0xffff.0" ~ "0xAAAA" == 0x5555)
|
||||
assert(~"0x0.000p4" == -1)
|
||||
|
||||
assert("7" .. 3 << 1 == 146)
|
||||
assert(10 >> 1 .. "9" == 0)
|
||||
assert(10 | 1 .. "9" == 27)
|
||||
assert(("7" .. 3) << 1 == 146)
|
||||
assert(0xffffffff >> (1 .. "9") == 0x1fff)
|
||||
assert(10 | (1 .. "9") == 27)
|
||||
|
||||
do
|
||||
local st, msg = pcall(function () return 4 & "a" end)
|
||||
|
||||
@@ -151,6 +151,23 @@ end
|
||||
print('+')
|
||||
|
||||
|
||||
do -- testing chains of '__call'
|
||||
local N = 20
|
||||
local u = table.pack
|
||||
for i = 1, N do
|
||||
u = setmetatable({i}, {__call = u})
|
||||
end
|
||||
|
||||
local Res = u("a", "b", "c")
|
||||
|
||||
assert(Res.n == N + 3)
|
||||
for i = 1, N do
|
||||
assert(Res[i][1] == i)
|
||||
end
|
||||
assert(Res[N + 1] == "a" and Res[N + 2] == "b" and Res[N + 3] == "c")
|
||||
end
|
||||
|
||||
|
||||
a = nil
|
||||
(function (x) a=x end)(23)
|
||||
assert(a == 23 and (function (x) return x*2 end)(20) == 40)
|
||||
|
||||
+121
-62
@@ -7,6 +7,23 @@ if T==nil then
|
||||
end
|
||||
print "testing code generation and optimizations"
|
||||
|
||||
-- to test constant propagation
|
||||
local k0aux <const> = 0
|
||||
local k0 <const> = k0aux
|
||||
local k1 <const> = 1
|
||||
local k3 <const> = 3
|
||||
local k6 <const> = k3 + (k3 << k0)
|
||||
local kFF0 <const> = 0xFF0
|
||||
local k3_78 <const> = 3.78
|
||||
local x, k3_78_4 <const> = 10, k3_78 / 4
|
||||
assert(x == 10)
|
||||
|
||||
local kx <const> = "x"
|
||||
|
||||
local kTrue <const> = true
|
||||
local kFalse <const> = false
|
||||
|
||||
local kNil <const> = nil
|
||||
|
||||
-- this code gave an error for the code checker
|
||||
do
|
||||
@@ -27,12 +44,12 @@ end
|
||||
|
||||
local function foo ()
|
||||
local a
|
||||
a = 3;
|
||||
a = k3;
|
||||
a = 0; a = 0.0; a = -7 + 7
|
||||
a = 3.78/4; a = 3.78/4
|
||||
a = -3.78/4; a = 3.78/4; a = -3.78/4
|
||||
a = k3_78/4; a = k3_78_4
|
||||
a = -k3_78/4; a = k3_78/4; a = -3.78/4
|
||||
a = -3.79/4; a = 0.0; a = -0;
|
||||
a = 3; a = 3.0; a = 3; a = 3.0
|
||||
a = k3; a = 3.0; a = 3; a = 3.0
|
||||
end
|
||||
|
||||
checkKlist(foo, {3.78/4, -3.78/4, -3.79/4})
|
||||
@@ -77,19 +94,22 @@ end
|
||||
-- some basic instructions
|
||||
check(function () -- function does not create upvalues
|
||||
(function () end){f()}
|
||||
end, 'CLOSURE', 'NEWTABLE', 'GETTABUP', 'CALL', 'SETLIST', 'CALL', 'RETURN0')
|
||||
end, 'CLOSURE', 'NEWTABLE', 'EXTRAARG', 'GETTABUP', 'CALL',
|
||||
'SETLIST', 'CALL', 'RETURN0')
|
||||
|
||||
check(function (x) -- function creates upvalues
|
||||
(function () return x end){f()}
|
||||
end, 'CLOSURE', 'NEWTABLE', 'GETTABUP', 'CALL', 'SETLIST', 'CALL', 'RETURN')
|
||||
end, 'CLOSURE', 'NEWTABLE', 'EXTRAARG', 'GETTABUP', 'CALL',
|
||||
'SETLIST', 'CALL', 'RETURN')
|
||||
|
||||
|
||||
-- sequence of LOADNILs
|
||||
check(function ()
|
||||
local kNil <const> = nil
|
||||
local a,b,c
|
||||
local d; local e;
|
||||
local f,g,h;
|
||||
d = nil; d=nil; b=nil; a=nil; c=nil;
|
||||
d = nil; d=nil; b=nil; a=kNil; c=nil;
|
||||
end, 'LOADNIL', 'RETURN0')
|
||||
|
||||
check(function ()
|
||||
@@ -109,7 +129,7 @@ check (function (a,b,c) return a end, 'RETURN1')
|
||||
|
||||
|
||||
-- infinite loops
|
||||
check(function () while true do local a = -1 end end,
|
||||
check(function () while kTrue do local a = -1 end end,
|
||||
'LOADI', 'JMP', 'RETURN0')
|
||||
|
||||
check(function () while 1 do local a = -1 end end,
|
||||
@@ -125,9 +145,9 @@ check(function (a,b,c,d) return a..b..c..d end,
|
||||
|
||||
-- not
|
||||
check(function () return not not nil end, 'LOADBOOL', 'RETURN1')
|
||||
check(function () return not not false end, 'LOADBOOL', 'RETURN1')
|
||||
check(function () return not not kFalse end, 'LOADBOOL', 'RETURN1')
|
||||
check(function () return not not true end, 'LOADBOOL', 'RETURN1')
|
||||
check(function () return not not 1 end, 'LOADBOOL', 'RETURN1')
|
||||
check(function () return not not k3 end, 'LOADBOOL', 'RETURN1')
|
||||
|
||||
-- direct access to locals
|
||||
check(function ()
|
||||
@@ -136,15 +156,16 @@ check(function ()
|
||||
c.x, a[b] = -((a + d/b - a[b]) ^ a.x), b
|
||||
end,
|
||||
'LOADNIL',
|
||||
'MUL',
|
||||
'DIV', 'ADD', 'GETTABLE', 'SUB', 'GETFIELD', 'POW',
|
||||
'UNM', 'SETTABLE', 'SETFIELD', 'RETURN0')
|
||||
'MUL', 'MMBIN',
|
||||
'DIV', 'MMBIN', 'ADD', 'MMBIN', 'GETTABLE', 'SUB', 'MMBIN',
|
||||
'GETFIELD', 'POW', 'MMBIN', 'UNM', 'SETTABLE', 'SETFIELD', 'RETURN0')
|
||||
|
||||
|
||||
-- direct access to constants
|
||||
check(function ()
|
||||
local a,b
|
||||
a.x = 3.2
|
||||
local c = kNil
|
||||
a[kx] = 3.2
|
||||
a.x = b
|
||||
a[b] = 'x'
|
||||
end,
|
||||
@@ -152,8 +173,9 @@ end,
|
||||
|
||||
-- "get/set table" with numeric indices
|
||||
check(function (a)
|
||||
local k255 <const> = 255
|
||||
a[1] = a[100]
|
||||
a[255] = a[256]
|
||||
a[k255] = a[256]
|
||||
a[256] = 5
|
||||
end,
|
||||
'GETI', 'SETI',
|
||||
@@ -166,11 +188,11 @@ check(function ()
|
||||
b = a/a
|
||||
b = 5-4
|
||||
end,
|
||||
'LOADNIL', 'SUB', 'DIV', 'LOADI', 'RETURN0')
|
||||
'LOADNIL', 'SUB', 'MMBIN', 'DIV', 'MMBIN', 'LOADI', 'RETURN0')
|
||||
|
||||
check(function ()
|
||||
local a,b
|
||||
a[true] = false
|
||||
a[kTrue] = false
|
||||
end,
|
||||
'LOADNIL', 'LOADBOOL', 'SETTABLE', 'RETURN0')
|
||||
|
||||
@@ -238,68 +260,81 @@ local function checkF (func, val)
|
||||
end
|
||||
|
||||
checkF(function () return 0.0 end, 0.0)
|
||||
checkI(function () return 0 end, 0)
|
||||
checkI(function () return -0//1 end, 0)
|
||||
checkI(function () return k0 end, 0)
|
||||
checkI(function () return -k0//1 end, 0)
|
||||
checkK(function () return 3^-1 end, 1/3)
|
||||
checkK(function () return (1 + 1)^(50 + 50) end, 2^100)
|
||||
checkK(function () return (-2)^(31 - 2) end, -0x20000000 + 0.0)
|
||||
checkF(function () return (-3^0 + 5) // 3.0 end, 1.0)
|
||||
checkI(function () return -3 % 5 end, 2)
|
||||
checkF(function () return (-k3^0 + 5) // 3.0 end, 1.0)
|
||||
checkI(function () return -k3 % 5 end, 2)
|
||||
checkF(function () return -((2.0^8 + -(-1)) % 8)/2 * 4 - 3 end, -5.0)
|
||||
checkF(function () return -((2^8 + -(-1)) % 8)//2 * 4 - 3 end, -7.0)
|
||||
checkI(function () return 0xF0.0 | 0xCC.0 ~ 0xAA & 0xFD end, 0xF4)
|
||||
checkI(function () return ~(~0xFF0 | 0xFF0) end, 0)
|
||||
checkI(function () return ~(~kFF0 | kFF0) end, 0)
|
||||
checkI(function () return ~~-1024.0 end, -1024)
|
||||
checkI(function () return ((100 << 6) << -4) >> 2 end, 100)
|
||||
checkI(function () return ((100 << k6) << -4) >> 2 end, 100)
|
||||
|
||||
-- borders around MAXARG_sBx ((((1 << 17) - 1) >> 1) == 65535)
|
||||
local a = 17; local sbx = ((1 << a) - 1) >> 1 -- avoid folding
|
||||
checkI(function () return 65535 end, sbx)
|
||||
checkI(function () return -65535 end, -sbx)
|
||||
checkI(function () return 65536 end, sbx + 1)
|
||||
checkK(function () return 65537 end, sbx + 2)
|
||||
checkK(function () return -65536 end, -(sbx + 1))
|
||||
local border <const> = 65535
|
||||
checkI(function () return border end, sbx)
|
||||
checkI(function () return -border end, -sbx)
|
||||
checkI(function () return border + 1 end, sbx + 1)
|
||||
checkK(function () return border + 2 end, sbx + 2)
|
||||
checkK(function () return -(border + 1) end, -(sbx + 1))
|
||||
|
||||
checkF(function () return 65535.0 end, sbx + 0.0)
|
||||
checkF(function () return -65535.0 end, -sbx + 0.0)
|
||||
checkF(function () return 65536.0 end, (sbx + 1.0))
|
||||
checkK(function () return 65537.0 end, (sbx + 2.0))
|
||||
checkK(function () return -65536.0 end, -(sbx + 1.0))
|
||||
local border <const> = 65535.0
|
||||
checkF(function () return border end, sbx + 0.0)
|
||||
checkF(function () return -border end, -sbx + 0.0)
|
||||
checkF(function () return border + 1 end, (sbx + 1.0))
|
||||
checkK(function () return border + 2 end, (sbx + 2.0))
|
||||
checkK(function () return -(border + 1) end, -(sbx + 1.0))
|
||||
|
||||
|
||||
-- immediate operands
|
||||
checkR(function (x) return x + 1 end, 10, 11, 'ADDI', 'RETURN1')
|
||||
checkR(function (x) return 128 + x end, 0.0, 128.0, 'ADDI', 'RETURN1')
|
||||
checkR(function (x) return x * -127 end, -1.0, 127.0, 'MULI', 'RETURN1')
|
||||
checkR(function (x) return 20 * x end, 2, 40, 'MULI', 'RETURN1')
|
||||
checkR(function (x) return x ^ -2 end, 2, 0.25, 'POWI', 'RETURN1')
|
||||
checkR(function (x) return x / 40 end, 40, 1.0, 'DIVI', 'RETURN1')
|
||||
checkR(function (x) return x // 1 end, 10.0, 10.0, 'IDIVI', 'RETURN1')
|
||||
checkR(function (x) return x % (100 - 10) end, 91, 1, 'MODI', 'RETURN1')
|
||||
checkR(function (x) return 1 << x end, 3, 8, 'SHLI', 'RETURN1')
|
||||
checkR(function (x) return x << 2 end, 10, 40, 'SHRI', 'RETURN1')
|
||||
checkR(function (x) return x >> 2 end, 8, 2, 'SHRI', 'RETURN1')
|
||||
checkR(function (x) return x & 1 end, 9, 1, 'BANDK', 'RETURN1')
|
||||
checkR(function (x) return 10 | x end, 1, 11, 'BORK', 'RETURN1')
|
||||
checkR(function (x) return -10 ~ x end, -1, 9, 'BXORK', 'RETURN1')
|
||||
checkR(function (x) return x + k1 end, 10, 11, 'ADDI', 'MMBINI', 'RETURN1')
|
||||
checkR(function (x) return x - 127 end, 10, -117, 'ADDI', 'MMBINI', 'RETURN1')
|
||||
checkR(function (x) return 128 + x end, 0.0, 128.0,
|
||||
'ADDI', 'MMBINI', 'RETURN1')
|
||||
checkR(function (x) return x * -127 end, -1.0, 127.0,
|
||||
'MULK', 'MMBINK', 'RETURN1')
|
||||
checkR(function (x) return 20 * x end, 2, 40, 'MULK', 'MMBINK', 'RETURN1')
|
||||
checkR(function (x) return x ^ -2 end, 2, 0.25, 'POWK', 'MMBINK', 'RETURN1')
|
||||
checkR(function (x) return x / 40 end, 40, 1.0, 'DIVK', 'MMBINK', 'RETURN1')
|
||||
checkR(function (x) return x // 1 end, 10.0, 10.0,
|
||||
'IDIVK', 'MMBINK', 'RETURN1')
|
||||
checkR(function (x) return x % (100 - 10) end, 91, 1,
|
||||
'MODK', 'MMBINK', 'RETURN1')
|
||||
checkR(function (x) return k1 << x end, 3, 8, 'SHLI', 'MMBINI', 'RETURN1')
|
||||
checkR(function (x) return x << 127 end, 10, 0, 'SHRI', 'MMBINI', 'RETURN1')
|
||||
checkR(function (x) return x << -127 end, 10, 0, 'SHRI', 'MMBINI', 'RETURN1')
|
||||
checkR(function (x) return x >> 128 end, 8, 0, 'SHRI', 'MMBINI', 'RETURN1')
|
||||
checkR(function (x) return x >> -127 end, 8, 0, 'SHRI', 'MMBINI', 'RETURN1')
|
||||
checkR(function (x) return x & 1 end, 9, 1, 'BANDK', 'MMBINK', 'RETURN1')
|
||||
checkR(function (x) return 10 | x end, 1, 11, 'BORK', 'MMBINK', 'RETURN1')
|
||||
checkR(function (x) return -10 ~ x end, -1, 9, 'BXORK', 'MMBINK', 'RETURN1')
|
||||
|
||||
-- K operands in arithmetic operations
|
||||
checkR(function (x) return x + 0.0 end, 1, 1.0, 'ADDK', 'RETURN1')
|
||||
-- check(function (x) return 128 + x end, 'ADDK', 'RETURN1')
|
||||
checkR(function (x) return x * -10000 end, 2, -20000, 'MULK', 'RETURN1')
|
||||
-- check(function (x) return 20 * x end, 'MULK', 'RETURN1')
|
||||
checkR(function (x) return x ^ 0.5 end, 4, 2.0, 'POWK', 'RETURN1')
|
||||
checkR(function (x) return x / 2.0 end, 4, 2.0, 'DIVK', 'RETURN1')
|
||||
checkR(function (x) return x // 10000 end, 10000, 1, 'IDIVK', 'RETURN1')
|
||||
checkR(function (x) return x % (100.0 - 10) end, 91, 1.0, 'MODK', 'RETURN1')
|
||||
checkR(function (x) return x + 0.0 end, 1, 1.0, 'ADDK', 'MMBINK', 'RETURN1')
|
||||
-- check(function (x) return 128 + x end, 'ADDK', 'MMBINK', 'RETURN1')
|
||||
checkR(function (x) return x * -10000 end, 2, -20000,
|
||||
'MULK', 'MMBINK', 'RETURN1')
|
||||
-- check(function (x) return 20 * x end, 'MULK', 'MMBINK', 'RETURN1')
|
||||
checkR(function (x) return x ^ 0.5 end, 4, 2.0, 'POWK', 'MMBINK', 'RETURN1')
|
||||
checkR(function (x) return x / 2.0 end, 4, 2.0, 'DIVK', 'MMBINK', 'RETURN1')
|
||||
checkR(function (x) return x // 10000 end, 10000, 1,
|
||||
'IDIVK', 'MMBINK', 'RETURN1')
|
||||
checkR(function (x) return x % (100.0 - 10) end, 91, 1.0,
|
||||
'MODK', 'MMBINK', 'RETURN1')
|
||||
|
||||
-- no foldings (and immediate operands)
|
||||
check(function () return -0.0 end, 'LOADF', 'UNM', 'RETURN1')
|
||||
check(function () return 3/0 end, 'LOADI', 'DIVI', 'RETURN1')
|
||||
check(function () return 0%0 end, 'LOADI', 'MODI', 'RETURN1')
|
||||
check(function () return -4//0 end, 'LOADI', 'IDIVI', 'RETURN1')
|
||||
check(function (x) return x >> 2.0 end, 'LOADF', 'SHR', 'RETURN1')
|
||||
check(function (x) return x & 2.0 end, 'LOADF', 'BAND', 'RETURN1')
|
||||
check(function () return k3/0 end, 'LOADI', 'DIVK', 'MMBINK', 'RETURN1')
|
||||
check(function () return 0%0 end, 'LOADI', 'MODK', 'MMBINK', 'RETURN1')
|
||||
check(function () return -4//0 end, 'LOADI', 'IDIVK', 'MMBINK', 'RETURN1')
|
||||
check(function (x) return x >> 2.0 end, 'LOADF', 'SHR', 'MMBIN', 'RETURN1')
|
||||
check(function (x) return x << 128 end, 'LOADI', 'SHL', 'MMBIN', 'RETURN1')
|
||||
check(function (x) return x & 2.0 end, 'LOADF', 'BAND', 'MMBIN', 'RETURN1')
|
||||
|
||||
-- basic 'for' loops
|
||||
check(function () for i = -10, 10.5 do end end,
|
||||
@@ -335,7 +370,7 @@ end,
|
||||
|
||||
do -- tests for table access in upvalues
|
||||
local t
|
||||
check(function () t.x = t.y end, 'GETTABUP', 'SETTABUP')
|
||||
check(function () t[kx] = t.y end, 'GETTABUP', 'SETTABUP')
|
||||
check(function (a) t[a()] = t[a()] end,
|
||||
'MOVE', 'CALL', 'GETUPVAL', 'MOVE', 'CALL',
|
||||
'GETUPVAL', 'GETTABLE', 'SETTABLE')
|
||||
@@ -355,7 +390,7 @@ check(function (a, b)
|
||||
if b then break else a = a + 1 end
|
||||
end
|
||||
end,
|
||||
'TEST', 'JMP', 'TEST', 'JMP', 'ADDI', 'JMP', 'RETURN0')
|
||||
'TEST', 'JMP', 'TEST', 'JMP', 'ADDI', 'MMBINI', 'JMP', 'RETURN0')
|
||||
|
||||
checkequal(
|
||||
function (a) while a < 10 do a = a + 1 end end,
|
||||
@@ -379,6 +414,30 @@ function (a)
|
||||
end
|
||||
)
|
||||
|
||||
checkequal(function () return 6 or true or nil end,
|
||||
function () return k6 or kTrue or kNil end)
|
||||
|
||||
checkequal(function () return 6 and true or nil end,
|
||||
function () return k6 and kTrue or kNil end)
|
||||
|
||||
|
||||
do -- string constants
|
||||
local k0 <const> = "00000000000000000000000000000000000000000000000000"
|
||||
local function f1 ()
|
||||
local k <const> = k0
|
||||
return function ()
|
||||
return function () return k end
|
||||
end
|
||||
end
|
||||
|
||||
local f2 = f1()
|
||||
local f3 = f2()
|
||||
assert(f3() == k0)
|
||||
checkK(f3, k0)
|
||||
-- string is not needed by other functions
|
||||
assert(T.listk(f1)[1] == nil)
|
||||
assert(T.listk(f2)[1] == nil)
|
||||
end
|
||||
|
||||
print 'OK'
|
||||
|
||||
|
||||
+27
-9
@@ -211,15 +211,15 @@ assert(a==1 and b==nil)
|
||||
print'+';
|
||||
|
||||
do -- testing constants
|
||||
local <const> prog = [[local <XXX> x = 10]]
|
||||
local prog <const> = [[local x <XXX> = 10]]
|
||||
checkload(prog, "unknown attribute 'XXX'")
|
||||
|
||||
checkload([[local <const> xxx = 20; xxx = 10]],
|
||||
checkload([[local xxx <const> = 20; xxx = 10]],
|
||||
":1: attempt to assign to const variable 'xxx'")
|
||||
|
||||
checkload([[
|
||||
local xx;
|
||||
local <const> xxx = 20;
|
||||
local xxx <const> = 20;
|
||||
local yyy;
|
||||
local function foo ()
|
||||
local abc = xx + yyy + xxx;
|
||||
@@ -228,7 +228,7 @@ do -- testing constants
|
||||
]], ":6: attempt to assign to const variable 'xxx'")
|
||||
|
||||
checkload([[
|
||||
local <toclose> x = nil
|
||||
local x <close> = nil
|
||||
x = io.open()
|
||||
]], ":2: attempt to assign to const variable 'x'")
|
||||
end
|
||||
@@ -287,7 +287,7 @@ a,b = F(nil)==nil; assert(a == true and b == nil)
|
||||
------------------------------------------------------------------
|
||||
|
||||
-- sometimes will be 0, sometimes will not...
|
||||
_ENV.GLOB1 = math.floor(os.time()) % 2
|
||||
_ENV.GLOB1 = math.random(0, 1)
|
||||
|
||||
-- basic expressions with their respective values
|
||||
local basiccases = {
|
||||
@@ -298,16 +298,36 @@ local basiccases = {
|
||||
{"(0==_ENV.GLOB1)", 0 == _ENV.GLOB1},
|
||||
}
|
||||
|
||||
local prog
|
||||
|
||||
if _ENV.GLOB1 == 0 then
|
||||
basiccases[2][1] = "F" -- constant false
|
||||
|
||||
prog = [[
|
||||
local F <const> = false
|
||||
if %s then IX = true end
|
||||
return %s
|
||||
]]
|
||||
else
|
||||
basiccases[4][1] = "k10" -- constant 10
|
||||
|
||||
prog = [[
|
||||
local k10 <const> = 10
|
||||
if %s then IX = true end
|
||||
return %s
|
||||
]]
|
||||
end
|
||||
|
||||
print('testing short-circuit optimizations (' .. _ENV.GLOB1 .. ')')
|
||||
|
||||
|
||||
-- operators with their respective values
|
||||
local <const> binops = {
|
||||
local binops <const> = {
|
||||
{" and ", function (a,b) if not a then return a else return b end end},
|
||||
{" or ", function (a,b) if a then return a else return b end end},
|
||||
}
|
||||
|
||||
local <const> cases = {}
|
||||
local cases <const> = {}
|
||||
|
||||
-- creates all combinations of '(cases[i] op cases[n-i])' plus
|
||||
-- 'not(cases[i] op cases[n-i])' (syntax + value)
|
||||
@@ -337,8 +357,6 @@ cases[1] = basiccases
|
||||
for i = 2, level do cases[i] = createcases(i) end
|
||||
print("+")
|
||||
|
||||
local prog = [[if %s then IX = true end; return %s]]
|
||||
|
||||
local i = 0
|
||||
for n = 1, level do
|
||||
for _, v in pairs(cases[n]) do
|
||||
|
||||
+36
-6
@@ -151,7 +151,7 @@ do
|
||||
end
|
||||
|
||||
co = coroutine.create(function ()
|
||||
local <toclose> x = func2close(function (self, err)
|
||||
local x <close> = func2close(function (self, err)
|
||||
assert(err == nil); X = false
|
||||
end)
|
||||
X = true
|
||||
@@ -163,21 +163,30 @@ do
|
||||
assert(not X and coroutine.status(co) == "dead")
|
||||
|
||||
-- error closing a coroutine
|
||||
warn("@on")
|
||||
local x = 0
|
||||
co = coroutine.create(function()
|
||||
local <toclose> y = func2close(function (self,err)
|
||||
local y <close> = func2close(function (self,err)
|
||||
if (err ~= 111) then os.exit(false) end -- should not happen
|
||||
x = 200
|
||||
error(200)
|
||||
error("200")
|
||||
end)
|
||||
local <toclose> x = func2close(function (self, err)
|
||||
local x <close> = func2close(function (self, err)
|
||||
assert(err == nil); error(111)
|
||||
end)
|
||||
coroutine.yield()
|
||||
end)
|
||||
coroutine.resume(co)
|
||||
assert(x == 0)
|
||||
-- with test library, use 'store' mode to check warnings
|
||||
warn(not T and "@off" or "@store")
|
||||
local st, msg = coroutine.close(co)
|
||||
if not T then
|
||||
warn("@on")
|
||||
else -- test library
|
||||
assert(string.find(_WARN, "200")); _WARN = nil
|
||||
warn("@normal")
|
||||
end
|
||||
assert(st == false and coroutine.status(co) == "dead" and msg == 111)
|
||||
assert(x == 200)
|
||||
|
||||
@@ -356,7 +365,7 @@ do
|
||||
|
||||
local X = false
|
||||
A = coroutine.wrap(function()
|
||||
local <toclose> _ = setmetatable({}, {__close = function () X = true end})
|
||||
local _ <close> = setmetatable({}, {__close = function () X = true end})
|
||||
return pcall(A, 1)
|
||||
end)
|
||||
st, res = A()
|
||||
@@ -426,6 +435,10 @@ else
|
||||
while A==0 or B==0 do -- A ~= 0 when 'x' finishes (similar for 'B','y')
|
||||
if A==0 then turn = "A"; assert(T.resume(x)) end
|
||||
if B==0 then turn = "B"; assert(T.resume(y)) end
|
||||
|
||||
-- check that traceback works correctly after yields inside hooks
|
||||
debug.traceback(x)
|
||||
debug.traceback(y)
|
||||
end
|
||||
|
||||
assert(B // A == 7) -- fact(7) // fact(6)
|
||||
@@ -711,6 +724,17 @@ assert(run(function () return a / b end, {"div"}) == 10/12)
|
||||
assert(run(function () return a % b end, {"mod"}) == 10)
|
||||
assert(run(function () return a // b end, {"idiv"}) == 0)
|
||||
|
||||
-- repeat tests with larger constants (to use 'K' opcodes)
|
||||
local a1000 = new(1000)
|
||||
|
||||
assert(run(function () return a1000 + 1000 end, {"add"}) == 2000)
|
||||
assert(run(function () return a1000 - 25000 end, {"sub"}) == -24000)
|
||||
assert(run(function () return 2000 * a end, {"mul"}) == 20000)
|
||||
assert(run(function () return a1000 / 1000 end, {"div"}) == 1)
|
||||
assert(run(function () return a1000 % 600 end, {"mod"}) == 400)
|
||||
assert(run(function () return a1000 // 500 end, {"idiv"}) == 2)
|
||||
|
||||
|
||||
|
||||
assert(run(function () return a % b end, {"mod"}) == 10)
|
||||
|
||||
@@ -723,6 +747,12 @@ assert(run(function () return a >> b end, {"shr"}) == 10 >> 12)
|
||||
assert(run(function () return 10 & b end, {"band"}) == 10 & 12)
|
||||
assert(run(function () return a | 2 end, {"bor"}) == 10 | 2)
|
||||
assert(run(function () return a ~ 2 end, {"bxor"}) == 10 ~ 2)
|
||||
assert(run(function () return a >> 2 end, {"shr"}) == 10 >> 2)
|
||||
assert(run(function () return 1 >> a end, {"shr"}) == 1 >> 10)
|
||||
assert(run(function () return a << 2 end, {"shl"}) == 10 << 2)
|
||||
assert(run(function () return 1 << a end, {"shl"}) == 1 << 10)
|
||||
assert(run(function () return 2 ~ a end, {"bxor"}) == 2 ~ 10)
|
||||
|
||||
|
||||
assert(run(function () return a..b end, {"concat"}) == "1012")
|
||||
|
||||
@@ -805,7 +835,7 @@ assert(run(function ()
|
||||
-- tests for coroutine API
|
||||
if T==nil then
|
||||
(Message or print)('\n >>> testC not active: skipping coroutine API tests <<<\n')
|
||||
return
|
||||
print "OK"; return
|
||||
end
|
||||
|
||||
print('testing coroutine API')
|
||||
|
||||
+65
-3
@@ -1,12 +1,32 @@
|
||||
-- $Id: testes/cstack.lua $
|
||||
-- See Copyright Notice in file all.lua
|
||||
|
||||
local debug = require "debug"
|
||||
|
||||
print"testing C-stack overflow detection"
|
||||
print"If this test craches, see its file ('cstack.lua')"
|
||||
|
||||
-- Segmentation faults in these tests probably result from a C-stack
|
||||
-- overflow. To avoid these errors, recompile Lua with a smaller
|
||||
-- value for the constant 'LUAI_MAXCCALLS' or else ensure a larger
|
||||
-- stack for the program.
|
||||
-- overflow. To avoid these errors, you can use the function
|
||||
-- 'debug.setcstacklimit' to set a smaller limit for the use of
|
||||
-- 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.
|
||||
|
||||
-- For Linux, a limit up to 30_000 seems Ok. Windows cannot go much
|
||||
-- higher than 2_000.
|
||||
|
||||
|
||||
local origlimit = debug.setcstacklimit(400)
|
||||
print("default stack limit: " .. origlimit)
|
||||
|
||||
-- change this value for different limits for this test suite
|
||||
local currentlimit = origlimit
|
||||
debug.setcstacklimit(currentlimit)
|
||||
print("current stack limit: " .. currentlimit)
|
||||
|
||||
|
||||
local function checkerror (msg, f, ...)
|
||||
local s, err = pcall(f, ...)
|
||||
@@ -79,4 +99,46 @@ do print("testing stack-overflow in recursive 'gsub'")
|
||||
print("\tfinal count: ", count)
|
||||
end
|
||||
|
||||
|
||||
do print("testing changes in C-stack limit")
|
||||
|
||||
assert(not debug.setcstacklimit(0)) -- limit too small
|
||||
assert(not debug.setcstacklimit(50000)) -- limit too large
|
||||
local co = coroutine.wrap (function ()
|
||||
return debug.setcstacklimit(400)
|
||||
end)
|
||||
assert(co() == false) -- 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
|
||||
|
||||
assert(debug.setcstacklimit(400) == currentlimit)
|
||||
local lim400 = check()
|
||||
-- a very low limit (given that the several calls to arive here)
|
||||
local lowlimit = 38
|
||||
assert(debug.setcstacklimit(lowlimit) == 400)
|
||||
assert(check() < lowlimit - 30)
|
||||
assert(debug.setcstacklimit(600) == lowlimit)
|
||||
local lim600 = check()
|
||||
assert(lim600 == lim400 + 200)
|
||||
|
||||
|
||||
-- 'setcstacklimit' works inside protected calls. (The new stack
|
||||
-- limit is kept when 'pcall' returns.)
|
||||
assert(pcall(function ()
|
||||
assert(debug.setcstacklimit(400) == 600)
|
||||
assert(check() <= lim400)
|
||||
end))
|
||||
|
||||
assert(check() == lim400)
|
||||
assert(debug.setcstacklimit(origlimit) == 400) -- restore original limit
|
||||
end
|
||||
|
||||
|
||||
print'OK'
|
||||
|
||||
+8
-5
@@ -255,6 +255,10 @@ do -- test hook presence in debug info
|
||||
end
|
||||
|
||||
|
||||
-- hook table has weak keys
|
||||
assert(getmetatable(debug.getregistry()._HOOKKEY).__mode == 'k')
|
||||
|
||||
|
||||
a = {}; L = nil
|
||||
local glob = 1
|
||||
local oldglob = glob
|
||||
@@ -347,12 +351,12 @@ assert(g(0,0) == 30)
|
||||
|
||||
|
||||
debug.sethook(nil);
|
||||
assert(debug.gethook() == nil)
|
||||
assert(not debug.gethook())
|
||||
|
||||
|
||||
-- minimal tests for setuservalue/getuservalue
|
||||
do
|
||||
assert(debug.setuservalue(io.stdin, 10) == nil)
|
||||
assert(not debug.setuservalue(io.stdin, 10))
|
||||
local a, b = debug.getuservalue(io.stdin, 10)
|
||||
assert(a == nil and not b)
|
||||
end
|
||||
@@ -410,7 +414,7 @@ end, "c")
|
||||
a:f(1,2,3,4,5)
|
||||
assert(X.self == a and X.a == 1 and X.b == 2 and X.c == nil)
|
||||
assert(XX == 12)
|
||||
assert(debug.gethook() == nil)
|
||||
assert(not debug.gethook())
|
||||
|
||||
|
||||
-- testing access to local variables in return hook (bug in 5.2)
|
||||
@@ -802,8 +806,7 @@ assert(a+3 == "add" and 3-a == "sub" and a*3 == "mul" and
|
||||
-a == "unm" and #a == "len" and a&3 == "band")
|
||||
assert(a + 30000 == "add" and a - 3.0 == "sub" and a * 3.0 == "mul" and
|
||||
-a == "unm" and #a == "len" and a & 3 == "band")
|
||||
assert(a|3 == "bor" and 3~a == "bxor" and a<<3 == "shift" and
|
||||
a>>1 == "shift")
|
||||
assert(a|3 == "bor" and 3~a == "bxor" and a<<3 == "shl" and a>>1 == "shr")
|
||||
assert (a==b and a.op == "eq")
|
||||
assert (a>=b and a.op == "order")
|
||||
assert (a>b and a.op == "order")
|
||||
|
||||
+15
-11
@@ -18,7 +18,7 @@ end
|
||||
|
||||
local function doit (s)
|
||||
local f, msg = load(s)
|
||||
if f == nil then return msg end
|
||||
if not f then return msg end
|
||||
local cond, msg = pcall(f)
|
||||
return (not cond) and msg
|
||||
end
|
||||
@@ -312,8 +312,8 @@ end
|
||||
|
||||
local function lineerror (s, l)
|
||||
local err,msg = pcall(load(s))
|
||||
local line = string.match(msg, ":(%d+):")
|
||||
assert(tonumber(line) == l)
|
||||
local line = tonumber(string.match(msg, ":(%d+):"))
|
||||
assert(line == l or (not line and not l))
|
||||
end
|
||||
|
||||
lineerror("local a\n for i=1,'a' do \n print(i) \n end", 2)
|
||||
@@ -359,7 +359,7 @@ local p = [[
|
||||
g()
|
||||
]]
|
||||
X=3;lineerror((p), 3)
|
||||
X=0;lineerror((p), nil)
|
||||
X=0;lineerror((p), false)
|
||||
X=1;lineerror((p), 2)
|
||||
X=2;lineerror((p), 1)
|
||||
|
||||
@@ -510,7 +510,7 @@ checksyntax("a\1a = 1", "", "<\\1>", 1)
|
||||
checksyntax("\255a = 1", "", "<\\255>", 1)
|
||||
|
||||
doit('I = load("a=9+"); a=3')
|
||||
assert(a==3 and I == nil)
|
||||
assert(a==3 and not I)
|
||||
print('+')
|
||||
|
||||
lim = 1000
|
||||
@@ -523,9 +523,13 @@ end
|
||||
|
||||
-- testing syntax limits
|
||||
|
||||
local function testrep (init, rep, close, repc)
|
||||
local function testrep (init, rep, close, repc, finalresult)
|
||||
local s = init .. string.rep(rep, 100) .. close .. string.rep(repc, 100)
|
||||
assert(load(s)) -- 100 levels is OK
|
||||
local res, msg = load(s)
|
||||
assert(res) -- 100 levels is OK
|
||||
if (finalresult) then
|
||||
assert(res() == finalresult)
|
||||
end
|
||||
s = init .. string.rep(rep, 10000)
|
||||
local res, msg = load(s) -- 10000 levels not ok
|
||||
assert(not res and (string.find(msg, "too many registers") or
|
||||
@@ -534,14 +538,14 @@ end
|
||||
|
||||
testrep("local a; a", ",a", "= 1", ",1") -- multiple assignment
|
||||
testrep("local a; a=", "{", "0", "}")
|
||||
testrep("local a; a=", "(", "2", ")")
|
||||
testrep("local a; ", "a(", "2", ")")
|
||||
testrep("return ", "(", "2", ")", 2)
|
||||
testrep("local function a (x) return x end; return ", "a(", "2.2", ")", 2.2)
|
||||
testrep("", "do ", "", " end")
|
||||
testrep("", "while a do ", "", " end")
|
||||
testrep("local a; ", "if a then else ", "", " end")
|
||||
testrep("", "function foo () ", "", " end")
|
||||
testrep("local a; a=", "a..", "a", "")
|
||||
testrep("local a; a=", "a^", "a", "")
|
||||
testrep("local a = ''; return ", "a..", "'a'", "", "a")
|
||||
testrep("local a = 1; return ", "a^", "a", "", 1)
|
||||
|
||||
checkmessage("a = f(x" .. string.rep(",x", 260) .. ")", "too many registers")
|
||||
|
||||
|
||||
+13
-2
@@ -217,9 +217,16 @@ t.__le = function (a,b,c)
|
||||
return a<=b, "dummy"
|
||||
end
|
||||
|
||||
t.__eq = function (a,b,c)
|
||||
assert(c == nil)
|
||||
if type(a) == 'table' then a = a.x end
|
||||
if type(b) == 'table' then b = b.x end
|
||||
return a == b, "dummy"
|
||||
end
|
||||
|
||||
function Op(x) return setmetatable({x=x}, t) end
|
||||
|
||||
local function test ()
|
||||
local function test (a, b, c)
|
||||
assert(not(Op(1)<Op(1)) and (Op(1)<Op(2)) and not(Op(2)<Op(1)))
|
||||
assert(not(1 < Op(1)) and (Op(1) < 2) and not(2 < Op(1)))
|
||||
assert(not(Op('a')<Op('a')) and (Op('a')<Op('b')) and not(Op('b')<Op('a')))
|
||||
@@ -232,9 +239,13 @@ local function test ()
|
||||
assert((1 >= Op(1)) and not(1 >= Op(2)) and (Op(2) >= 1))
|
||||
assert((Op('a')>=Op('a')) and not(Op('a')>=Op('b')) and (Op('b')>=Op('a')))
|
||||
assert(('a' >= Op('a')) and not(Op('a') >= 'b') and (Op('b') >= Op('a')))
|
||||
assert(Op(1) == Op(1) and Op(1) ~= Op(2))
|
||||
assert(Op('a') == Op('a') and Op('a') ~= Op('b'))
|
||||
assert(a == a and a ~= b)
|
||||
assert(Op(3) == c)
|
||||
end
|
||||
|
||||
test()
|
||||
test(Op(1), Op(2), Op(3))
|
||||
|
||||
|
||||
-- test `partial order'
|
||||
|
||||
+79
-38
@@ -125,7 +125,7 @@ do
|
||||
-- closing file by scope
|
||||
local F = nil
|
||||
do
|
||||
local <toclose> f = assert(io.open(file, "w"))
|
||||
local f <close> = assert(io.open(file, "w"))
|
||||
F = f
|
||||
end
|
||||
assert(tostring(F) == "file (closed)")
|
||||
@@ -135,7 +135,7 @@ assert(os.remove(file))
|
||||
|
||||
do
|
||||
-- test writing/reading numbers
|
||||
local <toclose> f = assert(io.open(file, "w"))
|
||||
local f <close> = assert(io.open(file, "w"))
|
||||
f:write(maxint, '\n')
|
||||
f:write(string.format("0X%x\n", maxint))
|
||||
f:write("0xABCp-3", '\n')
|
||||
@@ -144,7 +144,7 @@ do
|
||||
f:write(string.format("0x%X\n", -maxint))
|
||||
f:write("-0xABCp-3", '\n')
|
||||
assert(f:close())
|
||||
local <toclose> f = assert(io.open(file, "r"))
|
||||
local f <close> = assert(io.open(file, "r"))
|
||||
assert(f:read("n") == maxint)
|
||||
assert(f:read("n") == maxint)
|
||||
assert(f:read("n") == 0xABCp-3)
|
||||
@@ -158,7 +158,7 @@ assert(os.remove(file))
|
||||
|
||||
-- testing multiple arguments to io.read
|
||||
do
|
||||
local <toclose> f = assert(io.open(file, "w"))
|
||||
local f <close> = assert(io.open(file, "w"))
|
||||
f:write[[
|
||||
a line
|
||||
another line
|
||||
@@ -170,21 +170,21 @@ three
|
||||
]]
|
||||
local l1, l2, l3, l4, n1, n2, c, dummy
|
||||
assert(f:close())
|
||||
local <toclose> f = assert(io.open(file, "r"))
|
||||
local f <close> = assert(io.open(file, "r"))
|
||||
l1, l2, n1, n2, dummy = f:read("l", "L", "n", "n")
|
||||
assert(l1 == "a line" and l2 == "another line\n" and
|
||||
n1 == 1234 and n2 == 3.45 and dummy == nil)
|
||||
assert(f:close())
|
||||
local <toclose> f = assert(io.open(file, "r"))
|
||||
local f <close> = assert(io.open(file, "r"))
|
||||
l1, l2, n1, n2, c, l3, l4, dummy = f:read(7, "l", "n", "n", 1, "l", "l")
|
||||
assert(l1 == "a line\n" and l2 == "another line" and c == '\n' and
|
||||
n1 == 1234 and n2 == 3.45 and l3 == "one" and l4 == "two"
|
||||
and dummy == nil)
|
||||
assert(f:close())
|
||||
local <toclose> f = assert(io.open(file, "r"))
|
||||
local f <close> = assert(io.open(file, "r"))
|
||||
-- second item failing
|
||||
l1, n1, n2, dummy = f:read("l", "n", "n", "l")
|
||||
assert(l1 == "a line" and n1 == nil)
|
||||
assert(l1 == "a line" and not n1)
|
||||
end
|
||||
assert(os.remove(file))
|
||||
|
||||
@@ -228,7 +228,7 @@ assert(f:read("n") == 0Xdeadbeefdeadbeef); assert(f:read(2) == "x\n")
|
||||
assert(f:read("n") == 0x1.13aP3); assert(f:read(1) == "e")
|
||||
|
||||
do -- attempt to read too long number
|
||||
assert(f:read("n") == nil) -- fails
|
||||
assert(not f:read("n")) -- fails
|
||||
local s = f:read("L") -- read rest of line
|
||||
assert(string.find(s, "^00*\n$")) -- lots of 0's left
|
||||
end
|
||||
@@ -314,13 +314,13 @@ assert(io.read() == "fourth_line")
|
||||
assert(io.read() == "") -- empty line
|
||||
assert(io.read('n') == 3450)
|
||||
assert(io.read(1) == '\n')
|
||||
assert(io.read(0) == nil) -- end of file
|
||||
assert(io.read(1) == nil) -- end of file
|
||||
assert(io.read(30000) == nil) -- end of file
|
||||
assert(not io.read(0)) -- end of file
|
||||
assert(not io.read(1)) -- end of file
|
||||
assert(not io.read(30000)) -- end of file
|
||||
assert(({io.read(1)})[2] == undef)
|
||||
assert(io.read() == nil) -- end of file
|
||||
assert(not io.read()) -- end of file
|
||||
assert(({io.read()})[2] == undef)
|
||||
assert(io.read('n') == nil) -- end of file
|
||||
assert(not io.read('n')) -- end of file
|
||||
assert(({io.read('n')})[2] == undef)
|
||||
assert(io.read('a') == '') -- end of file (OK for 'a')
|
||||
assert(io.read('a') == '') -- end of file (OK for 'a')
|
||||
@@ -356,7 +356,7 @@ assert(io.read(string.len(t)) == t)
|
||||
assert(io.read(1) == ' ')
|
||||
assert(io.read(0))
|
||||
assert(io.read('a') == ';end of file\n')
|
||||
assert(io.read(0) == nil)
|
||||
assert(not io.read(0))
|
||||
assert(io.close(io.input()))
|
||||
|
||||
|
||||
@@ -364,7 +364,7 @@ assert(io.close(io.input()))
|
||||
do
|
||||
local function ismsg (m)
|
||||
-- error message is not a code number
|
||||
return (type(m) == "string" and tonumber(m) == nil)
|
||||
return (type(m) == "string" and not tonumber(m))
|
||||
end
|
||||
|
||||
-- read
|
||||
@@ -393,7 +393,7 @@ assert(io.read"L" == "\n")
|
||||
assert(io.read"L" == "\n")
|
||||
assert(io.read"L" == "line\n")
|
||||
assert(io.read"L" == "other")
|
||||
assert(io.read"L" == nil)
|
||||
assert(not io.read"L")
|
||||
io.input():close()
|
||||
|
||||
local f = assert(io.open(file))
|
||||
@@ -427,10 +427,12 @@ do -- testing closing file in line iteration
|
||||
-- get the to-be-closed variable from a loop
|
||||
local function gettoclose (lv)
|
||||
lv = lv + 1
|
||||
for i = 1, math.maxinteger do
|
||||
local stvar = 0 -- to-be-closed is 4th state variable in the loop
|
||||
for i = 1, 1000 do
|
||||
local n, v = debug.getlocal(lv, i)
|
||||
if n == "(for toclose)" then
|
||||
return v
|
||||
if n == "(for state)" then
|
||||
stvar = stvar + 1
|
||||
if stvar == 4 then return v end
|
||||
end
|
||||
end
|
||||
end
|
||||
@@ -460,7 +462,7 @@ end
|
||||
-- test for multipe arguments in 'lines'
|
||||
io.output(file); io.write"0123456789\n":close()
|
||||
for a,b in io.lines(file, 1, 1) do
|
||||
if a == "\n" then assert(b == nil)
|
||||
if a == "\n" then assert(not b)
|
||||
else assert(tonumber(a) == tonumber(b) - 1)
|
||||
end
|
||||
end
|
||||
@@ -471,13 +473,13 @@ end
|
||||
|
||||
for a,b,c in io.lines(file, "a", 0, 1) do
|
||||
if a == "" then break end
|
||||
assert(a == "0123456789\n" and b == nil and c == nil)
|
||||
assert(a == "0123456789\n" and not b and not c)
|
||||
end
|
||||
collectgarbage() -- to close file in previous iteration
|
||||
|
||||
io.output(file); io.write"00\n10\n20\n30\n40\n":close()
|
||||
for a, b in io.lines(file, "n", "n") do
|
||||
if a == 40 then assert(b == nil)
|
||||
if a == 40 then assert(not b)
|
||||
else assert(a == b - 10)
|
||||
end
|
||||
end
|
||||
@@ -652,7 +654,7 @@ and the rest of the file
|
||||
io.input(file)
|
||||
local _,a,b,c,d,e,h,__ = io.read(1, 'n', 'n', 'l', 'l', 'l', 'a', 10)
|
||||
assert(io.close(io.input()))
|
||||
assert(_ == ' ' and __ == nil)
|
||||
assert(_ == ' ' and not __)
|
||||
assert(type(a) == 'number' and a==123.4 and b==-56e-2)
|
||||
assert(d=='second line' and e=='third line')
|
||||
assert(h==[[
|
||||
@@ -704,7 +706,7 @@ if not _soft then
|
||||
io.input():seek('set', 0)
|
||||
y = io.read() -- huge line
|
||||
assert(x == y..'\n'..io.read())
|
||||
assert(io.read() == nil)
|
||||
assert(not io.read())
|
||||
io.close(io.input())
|
||||
assert(os.remove(file))
|
||||
x = nil; y = nil
|
||||
@@ -773,11 +775,24 @@ assert(os.date(string.rep("%d", 1000), t) ==
|
||||
string.rep(os.date("%d", t), 1000))
|
||||
assert(os.date(string.rep("%", 200)) == string.rep("%", 100))
|
||||
|
||||
local t = os.time()
|
||||
D = os.date("*t", t)
|
||||
load(os.date([[assert(D.year==%Y and D.month==%m and D.day==%d and
|
||||
D.hour==%H and D.min==%M and D.sec==%S and
|
||||
D.wday==%w+1 and D.yday==%j)]], t))()
|
||||
local function checkDateTable (t)
|
||||
_G.D = os.date("*t", t)
|
||||
assert(os.time(D) == t)
|
||||
load(os.date([[assert(D.year==%Y and D.month==%m and D.day==%d and
|
||||
D.hour==%H and D.min==%M and D.sec==%S and
|
||||
D.wday==%w+1 and D.yday==%j)]], t))()
|
||||
_G.D = nil
|
||||
end
|
||||
|
||||
checkDateTable(os.time())
|
||||
if not _port then
|
||||
-- assume that time_t can represent these values
|
||||
checkDateTable(0)
|
||||
checkDateTable(1)
|
||||
checkDateTable(1000)
|
||||
checkDateTable(0x7fffffff)
|
||||
checkDateTable(0x80000000)
|
||||
end
|
||||
|
||||
checkerr("invalid conversion specifier", os.date, "%")
|
||||
checkerr("invalid conversion specifier", os.date, "%9")
|
||||
@@ -791,11 +806,24 @@ checkerr("not an integer", os.time, {year=1000, month=1, day=1, hour=1.5})
|
||||
|
||||
checkerr("missing", os.time, {hour = 12}) -- missing date
|
||||
|
||||
|
||||
if string.packsize("i") == 4 then -- 4-byte ints
|
||||
checkerr("field 'year' is out-of-bound", os.time,
|
||||
{year = -(1 << 31) + 1899, month = 1, day = 1})
|
||||
end
|
||||
|
||||
if not _port then
|
||||
-- test Posix-specific modifiers
|
||||
assert(type(os.date("%Ex")) == 'string')
|
||||
assert(type(os.date("%Oy")) == 'string')
|
||||
|
||||
-- test large dates (assume at least 4-byte ints and time_t)
|
||||
local t0 = os.time{year = 1970, month = 1, day = 0}
|
||||
local t1 = os.time{year = 1970, month = 1, day = 0, sec = (1 << 31) - 1}
|
||||
assert(t1 - t0 == (1 << 31) - 1)
|
||||
t0 = os.time{year = 1970, month = 1, day = 1}
|
||||
t1 = os.time{year = 1970, month = 1, day = 1, sec = -(1 << 31)}
|
||||
assert(t1 - t0 == -(1 << 31))
|
||||
|
||||
-- test out-of-range dates (at least for Unix)
|
||||
if maxint >= 2^62 then -- cannot do these tests in Small Lua
|
||||
@@ -810,25 +838,37 @@ if not _port then
|
||||
-- time_t has 8 bytes; an int year cannot represent a huge time
|
||||
print(" 8-byte time_t")
|
||||
checkerr("cannot be represented", os.date, "%Y", 2^60)
|
||||
-- it should have no problems with year 4000
|
||||
assert(tonumber(os.time{year=4000, month=1, day=1}))
|
||||
|
||||
-- this is the maximum year
|
||||
assert(tonumber(os.time
|
||||
{year=(1 << 31) + 1899, month=12, day=31, hour=23, min=59, sec=59}))
|
||||
|
||||
-- this is too much
|
||||
checkerr("represented", os.time,
|
||||
{year=(1 << 31) + 1899, month=12, day=31, hour=23, min=59, sec=60})
|
||||
end
|
||||
|
||||
-- internal 'int' fields cannot hold these values
|
||||
checkerr("field 'day' is out-of-bound", os.time,
|
||||
{year = 0, month = 1, day = 2^32})
|
||||
|
||||
checkerr("field 'month' is out-of-bound", os.time,
|
||||
{year = 0, month = -((1 << 31) + 1), day = 1})
|
||||
|
||||
checkerr("field 'year' is out-of-bound", os.time,
|
||||
{year = (1 << 31) + 1900, month = 1, day = 1})
|
||||
|
||||
else -- 8-byte ints
|
||||
-- assume time_t has 8 bytes too
|
||||
print(" 8-byte time_t")
|
||||
assert(tonumber(os.date("%Y", 2^60)))
|
||||
|
||||
-- but still cannot represent a huge year
|
||||
checkerr("cannot be represented", os.time, {year=2^60, month=1, day=1})
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
|
||||
D = os.date("!*t", t)
|
||||
load(os.date([[!assert(D.year==%Y and D.month==%m and D.day==%d and
|
||||
D.hour==%H and D.min==%M and D.sec==%S and
|
||||
D.wday==%w+1 and D.yday==%j)]], t))()
|
||||
|
||||
do
|
||||
local D = os.date("*t")
|
||||
local t = os.time(D)
|
||||
@@ -842,6 +882,7 @@ do
|
||||
assert(t == t1) -- if isdst is absent uses correct default
|
||||
end
|
||||
|
||||
local D = os.date("*t")
|
||||
t = os.time(D)
|
||||
D.year = D.year-1;
|
||||
local t1 = os.time(D)
|
||||
|
||||
+15
-17
@@ -99,35 +99,28 @@ local function GC() GC1(); GC2() end
|
||||
do
|
||||
print("creating many objects")
|
||||
|
||||
local contCreate = 0
|
||||
|
||||
local limit = 5000
|
||||
|
||||
while contCreate <= limit do
|
||||
for i = 1, limit do
|
||||
local a = {}; a = nil
|
||||
contCreate = contCreate+1
|
||||
end
|
||||
|
||||
local a = "a"
|
||||
|
||||
contCreate = 0
|
||||
while contCreate <= limit do
|
||||
a = contCreate .. "b";
|
||||
for i = 1, limit do
|
||||
a = i .. "b";
|
||||
a = string.gsub(a, '(%d%d*)', "%1 %1")
|
||||
a = "a"
|
||||
contCreate = contCreate+1
|
||||
end
|
||||
|
||||
|
||||
contCreate = 0
|
||||
|
||||
a = {}
|
||||
|
||||
function a:test ()
|
||||
while contCreate <= limit do
|
||||
load(string.format("function temp(a) return 'a%d' end", contCreate), "")()
|
||||
assert(temp() == string.format('a%d', contCreate))
|
||||
contCreate = contCreate+1
|
||||
for i = 1, limit do
|
||||
load(string.format("function temp(a) return 'a%d' end", i), "")()
|
||||
assert(temp() == string.format('a%d', i))
|
||||
end
|
||||
end
|
||||
|
||||
@@ -166,9 +159,8 @@ print('long strings')
|
||||
x = "01234567890123456789012345678901234567890123456789012345678901234567890123456789"
|
||||
assert(string.len(x)==80)
|
||||
s = ''
|
||||
n = 0
|
||||
k = math.min(300, (math.maxinteger // 80) // 2)
|
||||
while n < k do s = s..x; n=n+1; j=tostring(n) end
|
||||
for n = 1, k do s = s..x; j=tostring(n) end
|
||||
assert(string.len(s) == k*80)
|
||||
s = string.sub(s, 1, 10000)
|
||||
s, i = string.gsub(s, '(%d%d%d%d)', '')
|
||||
@@ -377,9 +369,10 @@ if T then
|
||||
s[n] = i
|
||||
end
|
||||
|
||||
warn("@on"); warn("@store")
|
||||
collectgarbage()
|
||||
assert(string.find(_WARN, "error in __gc metamethod"))
|
||||
assert(string.match(_WARN, "@(.-)@") == "expected")
|
||||
assert(string.match(_WARN, "@(.-)@") == "expected"); _WARN = nil
|
||||
for i = 8, 10 do assert(s[i]) end
|
||||
|
||||
for i = 1, 5 do
|
||||
@@ -391,6 +384,7 @@ if T then
|
||||
for i = 1, 10 do assert(s[i]) end
|
||||
|
||||
getmetatable(u).__gc = nil
|
||||
warn("@normal")
|
||||
|
||||
end
|
||||
print '+'
|
||||
@@ -483,9 +477,12 @@ end
|
||||
|
||||
-- errors during collection
|
||||
if T then
|
||||
warn("@store")
|
||||
u = setmetatable({}, {__gc = function () error "@expected error" end})
|
||||
u = nil
|
||||
collectgarbage()
|
||||
assert(string.find(_WARN, "@expected error")); _WARN = nil
|
||||
warn("@normal")
|
||||
end
|
||||
|
||||
|
||||
@@ -653,7 +650,7 @@ end
|
||||
|
||||
-- create several objects to raise errors when collected while closing state
|
||||
if T then
|
||||
local error, assert, find = error, assert, string.find
|
||||
local error, assert, find, warn = error, assert, string.find, warn
|
||||
local n = 0
|
||||
local lastmsg
|
||||
local mt = {__gc = function (o)
|
||||
@@ -667,6 +664,7 @@ if T then
|
||||
else
|
||||
assert(lastmsg == _WARN) -- subsequent error messages are equal
|
||||
end
|
||||
warn("@store"); _WARN = nil
|
||||
error"@expected warning"
|
||||
end}
|
||||
for i = 10, 1, -1 do
|
||||
|
||||
+1
-1
@@ -258,7 +258,7 @@ do
|
||||
::L2:: goto L3
|
||||
|
||||
::L1:: do
|
||||
local <toclose> a = setmetatable({}, {__close = function () X = true end})
|
||||
local a <close> = setmetatable({}, {__close = function () X = true end})
|
||||
assert(X == nil)
|
||||
if a then goto L2 end -- jumping back out of scope of 'a'
|
||||
end
|
||||
|
||||
@@ -11,17 +11,17 @@ CFLAGS = -Wall -std=gnu99 -O2 -I$(LUA_DIR) -fPIC -shared
|
||||
all: lib1.so lib11.so lib2.so lib21.so lib2-v2.so
|
||||
touch all
|
||||
|
||||
lib1.so: lib1.c $(LUA_DIR)/luaconf.h $(LUA_DIR)/ltests.h
|
||||
lib1.so: lib1.c $(LUA_DIR)/luaconf.h
|
||||
$(CC) $(CFLAGS) -o lib1.so lib1.c
|
||||
|
||||
lib11.so: lib11.c $(LUA_DIR)/luaconf.h $(LUA_DIR)/ltests.h
|
||||
lib11.so: lib11.c $(LUA_DIR)/luaconf.h
|
||||
$(CC) $(CFLAGS) -o lib11.so lib11.c
|
||||
|
||||
lib2.so: lib2.c $(LUA_DIR)/luaconf.h $(LUA_DIR)/ltests.h
|
||||
lib2.so: lib2.c $(LUA_DIR)/luaconf.h
|
||||
$(CC) $(CFLAGS) -o lib2.so lib2.c
|
||||
|
||||
lib21.so: lib21.c $(LUA_DIR)/luaconf.h $(LUA_DIR)/ltests.h
|
||||
lib21.so: lib21.c $(LUA_DIR)/luaconf.h
|
||||
$(CC) $(CFLAGS) -o lib21.so lib21.c
|
||||
|
||||
lib2-v2.so: lib21.c $(LUA_DIR)/luaconf.h $(LUA_DIR)/ltests.h
|
||||
lib2-v2.so: lib21.c $(LUA_DIR)/luaconf.h
|
||||
$(CC) $(CFLAGS) -o lib2-v2.so lib22.c
|
||||
|
||||
+1
-1
@@ -281,7 +281,7 @@ if os.setlocale("pt_BR") or os.setlocale("ptb") then
|
||||
|
||||
assert(" 0x.1 " + " 0x,1" + "-0X.1\t" == 0x0.1)
|
||||
|
||||
assert(tonumber"inf" == nil and tonumber"NAN" == nil)
|
||||
assert(not tonumber"inf" and not tonumber"NAN")
|
||||
|
||||
assert(assert(load(string.format("return %q", 4.51)))() == 4.51)
|
||||
|
||||
|
||||
+240
-89
@@ -82,7 +82,7 @@ assert(c.a == nil)
|
||||
f()
|
||||
assert(c.a == 3)
|
||||
|
||||
-- old test for limits for special instructions (now just a generic test)
|
||||
-- old test for limits for special instructions
|
||||
do
|
||||
local i = 2
|
||||
local p = 4 -- p == 2^i
|
||||
@@ -173,24 +173,57 @@ end
|
||||
assert(x==20)
|
||||
|
||||
|
||||
do -- constants
|
||||
local a<const>, b, c<const> = 10, 20, 30
|
||||
b = a + c + b -- 'b' is not constant
|
||||
assert(a == 10 and b == 60 and c == 30)
|
||||
local function checkro (name, code)
|
||||
local st, msg = load(code)
|
||||
local gab = string.format("attempt to assign to const variable '%s'", name)
|
||||
assert(not st and string.find(msg, gab))
|
||||
end
|
||||
checkro("y", "local x, y <const>, z = 10, 20, 30; x = 11; y = 12")
|
||||
checkro("x", "local x <const>, y, z <const> = 10, 20, 30; x = 11")
|
||||
checkro("z", "local x <const>, y, z <const> = 10, 20, 30; y = 10; z = 11")
|
||||
|
||||
checkro("z", [[
|
||||
local a, z <const>, b = 10;
|
||||
function foo() a = 20; z = 32; end
|
||||
]])
|
||||
|
||||
checkro("var1", [[
|
||||
local a, var1 <const> = 10;
|
||||
function foo() a = 20; z = function () var1 = 12; end end
|
||||
]])
|
||||
end
|
||||
|
||||
|
||||
print"testing to-be-closed variables"
|
||||
|
||||
local function stack(n) n = ((n == 0) or stack(n - 1)) end
|
||||
|
||||
local function func2close (f)
|
||||
return setmetatable({}, {__close = f})
|
||||
local function func2close (f, x, y)
|
||||
local obj = setmetatable({}, {__close = f})
|
||||
if x then
|
||||
return x, obj, y
|
||||
else
|
||||
return obj
|
||||
end
|
||||
end
|
||||
|
||||
|
||||
do
|
||||
local a = {}
|
||||
do
|
||||
local <toclose> x = setmetatable({"x"}, {__close = function (self)
|
||||
local b <close> = false -- not to be closed
|
||||
local x <close> = setmetatable({"x"}, {__close = function (self)
|
||||
a[#a + 1] = self[1] end})
|
||||
local <toclose> y = func2close(function (self, err)
|
||||
assert(err == nil); a[#a + 1] = "y"
|
||||
end)
|
||||
local w, y <close>, z = func2close(function (self, err)
|
||||
assert(err == nil); a[#a + 1] = "y"
|
||||
end, 10, 20)
|
||||
local c <close> = nil -- not to be closed
|
||||
a[#a + 1] = "in"
|
||||
assert(w == 10 and z == 20)
|
||||
end
|
||||
a[#a + 1] = "out"
|
||||
assert(a[1] == "in" and a[2] == "y" and a[3] == "x" and a[4] == "out")
|
||||
@@ -199,11 +232,12 @@ end
|
||||
do
|
||||
local X = false
|
||||
|
||||
local closescope = func2close(function () stack(10); X = true end)
|
||||
local x, closescope = func2close(function () stack(10); X = true end, 100)
|
||||
assert(x == 100); x = 101; -- 'x' is not read-only
|
||||
|
||||
-- closing functions do not corrupt returning values
|
||||
local function foo (x)
|
||||
local <toclose> _ = closescope
|
||||
local _ <close> = closescope
|
||||
return x, X, 23
|
||||
end
|
||||
|
||||
@@ -212,7 +246,7 @@ do
|
||||
|
||||
X = false
|
||||
foo = function (x)
|
||||
local <toclose> _ = closescope
|
||||
local _<close> = closescope
|
||||
local y = 15
|
||||
return y
|
||||
end
|
||||
@@ -221,7 +255,7 @@ do
|
||||
|
||||
X = false
|
||||
foo = function ()
|
||||
local <toclose> x = closescope
|
||||
local x <close> = closescope
|
||||
return x
|
||||
end
|
||||
|
||||
@@ -234,13 +268,13 @@ do
|
||||
-- calls cannot be tail in the scope of to-be-closed variables
|
||||
local X, Y
|
||||
local function foo ()
|
||||
local <toclose> _ = func2close(function () Y = 10 end)
|
||||
local _ <close> = func2close(function () Y = 10 end)
|
||||
assert(X == true and Y == nil) -- 'X' not closed yet
|
||||
return 1,2,3
|
||||
end
|
||||
|
||||
local function bar ()
|
||||
local <toclose> _ = func2close(function () X = false end)
|
||||
local _ <close> = func2close(function () X = false end)
|
||||
X = true
|
||||
do
|
||||
return foo() -- not a tail call!
|
||||
@@ -252,69 +286,187 @@ do
|
||||
end
|
||||
|
||||
|
||||
do -- errors in __close
|
||||
local log = {}
|
||||
local function foo (err)
|
||||
local <toclose> x =
|
||||
func2close(function (self, msg) log[#log + 1] = msg; error(1) end)
|
||||
local <toclose> x1 =
|
||||
func2close(function (self, msg) log[#log + 1] = msg; end)
|
||||
local <toclose> gc = func2close(function () collectgarbage() end)
|
||||
local <toclose> y =
|
||||
func2close(function (self, msg) log[#log + 1] = msg; error(2) end)
|
||||
local <toclose> z =
|
||||
func2close(function (self, msg) log[#log + 1] = msg or 10; error(3) end)
|
||||
if err then error(4) end
|
||||
-- auxiliary functions for testing warnings in '__close'
|
||||
local function prepwarn ()
|
||||
if not T then -- no test library?
|
||||
warn("@off") -- do not show (lots of) warnings
|
||||
else
|
||||
warn("@store") -- to test the warnings
|
||||
end
|
||||
local stat, msg = pcall(foo, false)
|
||||
assert(msg == 3)
|
||||
assert(log[1] == 10 and log[2] == 3 and log[3] == 3 and log[4] == 3
|
||||
and #log == 4)
|
||||
|
||||
log = {}
|
||||
local stat, msg = pcall(foo, true)
|
||||
assert(msg == 4)
|
||||
assert(log[1] == 4 and log[2] == 4 and log[3] == 4 and log[4] == 4
|
||||
and #log == 4)
|
||||
|
||||
-- error in toclose in vararg function
|
||||
function foo (...)
|
||||
local <toclose> x123 = 10
|
||||
end
|
||||
|
||||
local st, msg = pcall(foo)
|
||||
assert(string.find(msg, "'x123'"))
|
||||
|
||||
end
|
||||
|
||||
|
||||
do
|
||||
local function endwarn ()
|
||||
if not T then
|
||||
warn("@on") -- back to normal
|
||||
else
|
||||
assert(_WARN == nil)
|
||||
warn("@normal")
|
||||
end
|
||||
end
|
||||
|
||||
-- errors due to non-closable values
|
||||
|
||||
local function checkwarn (msg)
|
||||
if T then
|
||||
assert(string.find(_WARN, msg))
|
||||
_WARN = nil -- reset variable to check next warning
|
||||
end
|
||||
end
|
||||
|
||||
warn("@on")
|
||||
|
||||
do print("testing errors in __close")
|
||||
|
||||
prepwarn()
|
||||
|
||||
-- original error is in __close
|
||||
local function foo ()
|
||||
local <toclose> x = 34
|
||||
|
||||
local x <close> =
|
||||
func2close(function (self, msg)
|
||||
assert(string.find(msg, "@z"))
|
||||
error("@x")
|
||||
end)
|
||||
|
||||
local x1 <close> =
|
||||
func2close(function (self, msg)
|
||||
checkwarn("@y")
|
||||
assert(string.find(msg, "@z"))
|
||||
end)
|
||||
|
||||
local gc <close> = func2close(function () collectgarbage() end)
|
||||
|
||||
local y <close> =
|
||||
func2close(function (self, msg)
|
||||
assert(string.find(msg, "@z")) -- first error in 'z'
|
||||
checkwarn("@z") -- second error in 'z' generated a warning
|
||||
error("@y")
|
||||
end)
|
||||
|
||||
local first = true
|
||||
local z <close> =
|
||||
-- 'z' close is called twice
|
||||
func2close(function (self, msg)
|
||||
if first then
|
||||
assert(msg == nil)
|
||||
first = false
|
||||
else
|
||||
assert(string.find(msg, "@z")) -- own error
|
||||
end
|
||||
error("@z")
|
||||
end)
|
||||
|
||||
return 200
|
||||
end
|
||||
|
||||
local stat, msg = pcall(foo, false)
|
||||
assert(string.find(msg, "@z"))
|
||||
checkwarn("@x")
|
||||
|
||||
|
||||
-- original error not in __close
|
||||
local function foo ()
|
||||
|
||||
local x <close> =
|
||||
func2close(function (self, msg)
|
||||
assert(msg == 4)
|
||||
end)
|
||||
|
||||
local x1 <close> =
|
||||
func2close(function (self, msg)
|
||||
checkwarn("@y")
|
||||
assert(msg == 4)
|
||||
error("@x1")
|
||||
end)
|
||||
|
||||
local gc <close> = func2close(function () collectgarbage() end)
|
||||
|
||||
local y <close> =
|
||||
func2close(function (self, msg)
|
||||
assert(msg == 4) -- error in body
|
||||
checkwarn("@z")
|
||||
error("@y")
|
||||
end)
|
||||
|
||||
local first = true
|
||||
local z <close> =
|
||||
func2close(function (self, msg)
|
||||
-- 'z' close is called once
|
||||
assert(first and msg == 4)
|
||||
first = false
|
||||
error("@z")
|
||||
end)
|
||||
|
||||
error(4) -- original error
|
||||
end
|
||||
|
||||
local stat, msg = pcall(foo, true)
|
||||
assert(msg == 4)
|
||||
checkwarn("@x1") -- last error
|
||||
|
||||
-- error leaving a block
|
||||
local function foo (...)
|
||||
do
|
||||
local x1 <close> =
|
||||
func2close(function ()
|
||||
checkwarn("@X")
|
||||
error("@Y")
|
||||
end)
|
||||
|
||||
local x123 <close> =
|
||||
func2close(function ()
|
||||
error("@X")
|
||||
end)
|
||||
end
|
||||
os.exit(false) -- should not run
|
||||
end
|
||||
|
||||
local st, msg = xpcall(foo, debug.traceback)
|
||||
assert(string.match(msg, "^[^ ]* @X"))
|
||||
assert(string.find(msg, "in metamethod 'close'"))
|
||||
checkwarn("@Y")
|
||||
|
||||
-- error in toclose in vararg function
|
||||
local function foo (...)
|
||||
local x123 <close> = func2close(function () error("@x123") end)
|
||||
end
|
||||
|
||||
local st, msg = xpcall(foo, debug.traceback)
|
||||
assert(string.match(msg, "^[^ ]* @x123"))
|
||||
assert(string.find(msg, "in metamethod 'close'"))
|
||||
checkwarn("@x123") -- from second call to close 'x123'
|
||||
|
||||
endwarn()
|
||||
end
|
||||
|
||||
|
||||
do -- errors due to non-closable values
|
||||
local function foo ()
|
||||
local x <close> = {}
|
||||
os.exit(false) -- should not run
|
||||
end
|
||||
local stat, msg = pcall(foo)
|
||||
assert(not stat and string.find(msg, "variable 'x'"))
|
||||
assert(not stat and
|
||||
string.find(msg, "variable 'x' got a non%-closable value"))
|
||||
|
||||
|
||||
-- with other errors, non-closable values are ignored
|
||||
local function foo ()
|
||||
local <toclose> x = 34
|
||||
local <toclose> y = func2close(function () error(32) end)
|
||||
local xyz <close> = setmetatable({}, {__close = print})
|
||||
getmetatable(xyz).__close = nil -- remove metamethod
|
||||
end
|
||||
local stat, msg = pcall(foo)
|
||||
assert(not stat and msg == 32)
|
||||
|
||||
assert(not stat and
|
||||
string.find(msg, "attempt to close non%-closable variable 'xyz'"))
|
||||
end
|
||||
|
||||
|
||||
if rawget(_G, "T") then
|
||||
|
||||
warn("@off")
|
||||
|
||||
-- memory error inside closing function
|
||||
local function foo ()
|
||||
local <toclose> y = func2close(function () T.alloccount() end)
|
||||
local <toclose> x = setmetatable({}, {__close = function ()
|
||||
local y <close> = func2close(function () T.alloccount() end)
|
||||
local x <close> = setmetatable({}, {__close = function ()
|
||||
T.alloccount(0); local x = {} -- force a memory error
|
||||
end})
|
||||
error(1000) -- common error inside the function's body
|
||||
@@ -340,7 +492,7 @@ if rawget(_G, "T") then
|
||||
end
|
||||
|
||||
local function test ()
|
||||
local <toclose> x = enter(0) -- set a memory limit
|
||||
local x <close> = enter(0) -- set a memory limit
|
||||
-- creation of previous upvalue will raise a memory error
|
||||
assert(false) -- should not run
|
||||
end
|
||||
@@ -355,14 +507,14 @@ if rawget(_G, "T") then
|
||||
|
||||
-- repeat test with extra closing upvalues
|
||||
local function test ()
|
||||
local <toclose> xxx = func2close(function (self, msg)
|
||||
local xxx <close> = func2close(function (self, msg)
|
||||
assert(msg == "not enough memory");
|
||||
error(1000) -- raise another error
|
||||
end)
|
||||
local <toclose> xx = func2close(function (self, msg)
|
||||
local xx <close> = func2close(function (self, msg)
|
||||
assert(msg == "not enough memory");
|
||||
end)
|
||||
local <toclose> x = enter(0) -- set a memory limit
|
||||
local x <close> = enter(0) -- set a memory limit
|
||||
-- creation of previous upvalue will raise a memory error
|
||||
os.exit(false) -- should not run
|
||||
end
|
||||
@@ -387,7 +539,7 @@ if rawget(_G, "T") then
|
||||
|
||||
local s = string.rep("a", lim)
|
||||
|
||||
-- concat this table needs two buffer resizes (one for each 's')
|
||||
-- concat this table needs two buffer resizes (one for each 's')
|
||||
local a = {s, s}
|
||||
|
||||
collectgarbage()
|
||||
@@ -422,6 +574,8 @@ if rawget(_G, "T") then
|
||||
|
||||
print'+'
|
||||
end
|
||||
|
||||
warn("@on")
|
||||
end
|
||||
|
||||
|
||||
@@ -432,9 +586,9 @@ do
|
||||
local x = false
|
||||
local y = false
|
||||
local co = coroutine.wrap(function ()
|
||||
local <toclose> xv = func2close(function () x = true end)
|
||||
local xv <close> = func2close(function () x = true end)
|
||||
do
|
||||
local <toclose> yv = func2close(function () y = true end)
|
||||
local yv <close> = func2close(function () y = true end)
|
||||
coroutine.yield(100) -- yield doesn't close variable
|
||||
end
|
||||
coroutine.yield(200) -- yield doesn't close variable
|
||||
@@ -451,36 +605,45 @@ end
|
||||
|
||||
|
||||
do
|
||||
prepwarn()
|
||||
|
||||
-- error in a wrapped coroutine raising errors when closing a variable
|
||||
local x = 0
|
||||
local co = coroutine.wrap(function ()
|
||||
local <toclose> xx = func2close(function () x = x + 1; error("YYY") end)
|
||||
local <toclose> xv = func2close(function () x = x + 1; error("XXX") end)
|
||||
local xx <close> = func2close(function () x = x + 1; error("@YYY") end)
|
||||
local xv <close> = func2close(function () x = x + 1; error("@XXX") end)
|
||||
coroutine.yield(100)
|
||||
error(200)
|
||||
end)
|
||||
assert(co() == 100); assert(x == 0)
|
||||
local st, msg = pcall(co); assert(x == 2)
|
||||
assert(not st and msg == 200) -- should get first error raised
|
||||
checkwarn("@YYY")
|
||||
|
||||
x = 0
|
||||
local x = 0
|
||||
local y = 0
|
||||
co = coroutine.wrap(function ()
|
||||
local <toclose> xx = func2close(function () x = x + 1; error("YYY") end)
|
||||
local <toclose> xv = func2close(function () x = x + 1; error("XXX") end)
|
||||
local xx <close> = func2close(function () y = y + 1; error("YYY") end)
|
||||
local xv <close> = func2close(function () x = x + 1; error("XXX") end)
|
||||
coroutine.yield(100)
|
||||
return 200
|
||||
end)
|
||||
assert(co() == 100); assert(x == 0)
|
||||
local st, msg = pcall(co); assert(x == 2)
|
||||
local st, msg = pcall(co)
|
||||
assert(x == 2 and y == 1) -- first close is called twice
|
||||
-- should get first error raised
|
||||
assert(not st and string.find(msg, "%w+%.%w+:%d+: XXX"))
|
||||
checkwarn("YYY")
|
||||
|
||||
endwarn()
|
||||
end
|
||||
|
||||
|
||||
-- a suspended coroutine should not close its variables when collected
|
||||
local co
|
||||
co = coroutine.wrap(function()
|
||||
local <toclose> x = function () os.exit(false) end -- should not run
|
||||
-- should not run
|
||||
local x <close> = func2close(function () os.exit(false) end)
|
||||
co = nil
|
||||
coroutine.yield()
|
||||
end)
|
||||
@@ -517,27 +680,15 @@ do
|
||||
end
|
||||
assert(s == 35 and numopen == 0)
|
||||
|
||||
-- repeat test with '__open' metamethod instead of a function
|
||||
local function open (x)
|
||||
numopen = numopen + 1
|
||||
local state = setmetatable({x},
|
||||
{__close = function () numopen = numopen - 1 end})
|
||||
return
|
||||
function (t) -- iteraction function
|
||||
t[1] = t[1] - 1
|
||||
if t[1] > 0 then return t[1] end
|
||||
end,
|
||||
state,
|
||||
nil,
|
||||
state -- to-be-closed
|
||||
end
|
||||
|
||||
local s = 0
|
||||
for i in open(10) do
|
||||
if (i < 5) then break end
|
||||
s = s + i
|
||||
for j in open(10) do
|
||||
if i + j < 5 then goto endloop end
|
||||
s = s + i
|
||||
end
|
||||
end
|
||||
assert(s == 35 and numopen == 0)
|
||||
::endloop::
|
||||
assert(s == 375 and numopen == 0)
|
||||
end
|
||||
|
||||
print('OK')
|
||||
|
||||
+54
-8
@@ -43,6 +43,8 @@ local function getoutput ()
|
||||
end
|
||||
|
||||
local function checkprogout (s)
|
||||
-- expected result must end with new line
|
||||
assert(string.sub(s, -1) == "\n")
|
||||
local t = getoutput()
|
||||
for line in string.gmatch(s, ".-\n") do
|
||||
assert(string.find(t, line, 1, true))
|
||||
@@ -219,6 +221,42 @@ assert(string.find(getoutput(), "error calling 'print'"))
|
||||
RUN('echo "io.stderr:write(1000)\ncont" | lua -e "require\'debug\'.debug()" 2> %s', out)
|
||||
checkout("lua_debug> 1000lua_debug> ")
|
||||
|
||||
|
||||
print("testing warnings")
|
||||
|
||||
-- no warnings by default
|
||||
RUN('echo "io.stderr:write(1); warn[[XXX]]" | lua 2> %s', out)
|
||||
checkout("1")
|
||||
|
||||
prepfile[[
|
||||
warn("@allow") -- unknown control, ignored
|
||||
warn("@off", "XXX", "@off") -- these are not control messages
|
||||
warn("@off") -- this one is
|
||||
warn("@on", "YYY", "@on") -- not control, but warn is off
|
||||
warn("@off") -- keep it off
|
||||
warn("@on") -- restart warnings
|
||||
warn("", "@on") -- again, no control, real warning
|
||||
warn("@on") -- keep it "started"
|
||||
warn("Z", "Z", "Z") -- common warning
|
||||
]]
|
||||
RUN('lua -W %s 2> %s', prog, out)
|
||||
checkout[[
|
||||
Lua warning: @offXXX@off
|
||||
Lua warning: @on
|
||||
Lua warning: ZZZ
|
||||
]]
|
||||
|
||||
prepfile[[
|
||||
warn("@allow")
|
||||
-- create two objects to be finalized when closing state
|
||||
-- the errors in the finalizers must generate warnings
|
||||
u1 = setmetatable({}, {__gc = function () error("XYZ") end})
|
||||
u2 = setmetatable({}, {__gc = function () error("ZYX") end})
|
||||
]]
|
||||
RUN('lua -W %s 2> %s', prog, out)
|
||||
checkprogout("ZYX)\nXYZ)\n")
|
||||
|
||||
|
||||
-- test many arguments
|
||||
prepfile[[print(({...})[30])]]
|
||||
RUN('lua %s %s > %s', prog, string.rep(" a", 30), out)
|
||||
@@ -292,7 +330,7 @@ debug = require"debug"
|
||||
print(debug.getinfo(1).currentline)
|
||||
]]
|
||||
RUN('lua %s > %s', prog, out)
|
||||
checkprogout('3')
|
||||
checkprogout('3\n')
|
||||
|
||||
-- close Lua with an open file
|
||||
prepfile(string.format([[io.output(%q); io.write('alo')]], out))
|
||||
@@ -320,15 +358,16 @@ NoRun("", "lua %s", prog) -- no message
|
||||
|
||||
-- to-be-closed variables in main chunk
|
||||
prepfile[[
|
||||
local <toclose> x = function (err)
|
||||
assert(err == 120)
|
||||
print("Ok")
|
||||
end
|
||||
local <toclose> e1 = function () error(120) end
|
||||
local x <close> = setmetatable({},
|
||||
{__close = function (self, err)
|
||||
assert(err == nil)
|
||||
print("Ok")
|
||||
end})
|
||||
local e1 <close> = setmetatable({}, {__close = function () print(120) end})
|
||||
os.exit(true, true)
|
||||
]]
|
||||
RUN('lua %s > %s', prog, out)
|
||||
checkprogout("Ok")
|
||||
checkprogout("120\nOk\n")
|
||||
|
||||
|
||||
-- remove temporary files
|
||||
@@ -352,8 +391,15 @@ if T then -- test library?
|
||||
NoRun("not enough memory", "env MEMLIMIT=100 lua")
|
||||
|
||||
-- testing 'warn'
|
||||
warn("@store")
|
||||
warn("@123", "456", "789")
|
||||
assert(_WARN == "@123456789")
|
||||
assert(_WARN == "@123456789"); _WARN = nil
|
||||
|
||||
warn("zip", "", " ", "zap")
|
||||
assert(_WARN == "zip zap"); _WARN = nil
|
||||
warn("ZIP", "", " ", "ZAP")
|
||||
assert(_WARN == "ZIP ZAP"); _WARN = nil
|
||||
warn("@normal")
|
||||
end
|
||||
|
||||
do
|
||||
|
||||
+73
-62
@@ -3,10 +3,10 @@
|
||||
|
||||
print("testing numbers and math lib")
|
||||
|
||||
local <const> minint = math.mininteger
|
||||
local <const> maxint = math.maxinteger
|
||||
local minint <const> = math.mininteger
|
||||
local maxint <const> = math.maxinteger
|
||||
|
||||
local <const> intbits = math.floor(math.log(maxint, 2) + 0.5) + 1
|
||||
local intbits <const> = math.floor(math.log(maxint, 2) + 0.5) + 1
|
||||
assert((1 << intbits) == 0)
|
||||
|
||||
assert(minint == 1 << (intbits - 1))
|
||||
@@ -39,7 +39,7 @@ do
|
||||
end
|
||||
|
||||
assert(math.type(0) == "integer" and math.type(0.0) == "float"
|
||||
and math.type("10") == nil)
|
||||
and not math.type("10"))
|
||||
|
||||
|
||||
local function checkerror (msg, f, ...)
|
||||
@@ -270,7 +270,7 @@ else
|
||||
end
|
||||
|
||||
do
|
||||
local NaN = 0/0
|
||||
local NaN <const> = 0/0
|
||||
assert(not (NaN < 0))
|
||||
assert(not (NaN > minint))
|
||||
assert(not (NaN <= -9))
|
||||
@@ -381,17 +381,17 @@ assert(tonumber(1/0) == 1/0)
|
||||
|
||||
-- 'tonumber' with strings
|
||||
assert(tonumber("0") == 0)
|
||||
assert(tonumber("") == nil)
|
||||
assert(tonumber(" ") == nil)
|
||||
assert(tonumber("-") == nil)
|
||||
assert(tonumber(" -0x ") == nil)
|
||||
assert(tonumber{} == nil)
|
||||
assert(not tonumber(""))
|
||||
assert(not tonumber(" "))
|
||||
assert(not tonumber("-"))
|
||||
assert(not tonumber(" -0x "))
|
||||
assert(not tonumber{})
|
||||
assert(tonumber'+0.01' == 1/100 and tonumber'+.01' == 0.01 and
|
||||
tonumber'.01' == 0.01 and tonumber'-1.' == -1 and
|
||||
tonumber'+1.' == 1)
|
||||
assert(tonumber'+ 0.01' == nil and tonumber'+.e1' == nil and
|
||||
tonumber'1e' == nil and tonumber'1.0e+' == nil and
|
||||
tonumber'.' == nil)
|
||||
assert(not tonumber'+ 0.01' and not tonumber'+.e1' and
|
||||
not tonumber'1e' and not tonumber'1.0e+' and
|
||||
not tonumber'.')
|
||||
assert(tonumber('-012') == -010-2)
|
||||
assert(tonumber('-1.2e2') == - - -120)
|
||||
|
||||
@@ -445,45 +445,45 @@ local function f (...)
|
||||
end
|
||||
end
|
||||
|
||||
assert(f(tonumber('fFfa', 15)) == nil)
|
||||
assert(f(tonumber('099', 8)) == nil)
|
||||
assert(f(tonumber('1\0', 2)) == nil)
|
||||
assert(f(tonumber('', 8)) == nil)
|
||||
assert(f(tonumber(' ', 9)) == nil)
|
||||
assert(f(tonumber(' ', 9)) == nil)
|
||||
assert(f(tonumber('0xf', 10)) == nil)
|
||||
assert(not f(tonumber('fFfa', 15)))
|
||||
assert(not f(tonumber('099', 8)))
|
||||
assert(not f(tonumber('1\0', 2)))
|
||||
assert(not f(tonumber('', 8)))
|
||||
assert(not f(tonumber(' ', 9)))
|
||||
assert(not f(tonumber(' ', 9)))
|
||||
assert(not f(tonumber('0xf', 10)))
|
||||
|
||||
assert(f(tonumber('inf')) == nil)
|
||||
assert(f(tonumber(' INF ')) == nil)
|
||||
assert(f(tonumber('Nan')) == nil)
|
||||
assert(f(tonumber('nan')) == nil)
|
||||
assert(not f(tonumber('inf')))
|
||||
assert(not f(tonumber(' INF ')))
|
||||
assert(not f(tonumber('Nan')))
|
||||
assert(not f(tonumber('nan')))
|
||||
|
||||
assert(f(tonumber(' ')) == nil)
|
||||
assert(f(tonumber('')) == nil)
|
||||
assert(f(tonumber('1 a')) == nil)
|
||||
assert(f(tonumber('1 a', 2)) == nil)
|
||||
assert(f(tonumber('1\0')) == nil)
|
||||
assert(f(tonumber('1 \0')) == nil)
|
||||
assert(f(tonumber('1\0 ')) == nil)
|
||||
assert(f(tonumber('e1')) == nil)
|
||||
assert(f(tonumber('e 1')) == nil)
|
||||
assert(f(tonumber(' 3.4.5 ')) == nil)
|
||||
assert(not f(tonumber(' ')))
|
||||
assert(not f(tonumber('')))
|
||||
assert(not f(tonumber('1 a')))
|
||||
assert(not f(tonumber('1 a', 2)))
|
||||
assert(not f(tonumber('1\0')))
|
||||
assert(not f(tonumber('1 \0')))
|
||||
assert(not f(tonumber('1\0 ')))
|
||||
assert(not f(tonumber('e1')))
|
||||
assert(not f(tonumber('e 1')))
|
||||
assert(not f(tonumber(' 3.4.5 ')))
|
||||
|
||||
|
||||
-- testing 'tonumber' for invalid hexadecimal formats
|
||||
|
||||
assert(tonumber('0x') == nil)
|
||||
assert(tonumber('x') == nil)
|
||||
assert(tonumber('x3') == nil)
|
||||
assert(tonumber('0x3.3.3') == nil) -- two decimal points
|
||||
assert(tonumber('00x2') == nil)
|
||||
assert(tonumber('0x 2') == nil)
|
||||
assert(tonumber('0 x2') == nil)
|
||||
assert(tonumber('23x') == nil)
|
||||
assert(tonumber('- 0xaa') == nil)
|
||||
assert(tonumber('-0xaaP ') == nil) -- no exponent
|
||||
assert(tonumber('0x0.51p') == nil)
|
||||
assert(tonumber('0x5p+-2') == nil)
|
||||
assert(not tonumber('0x'))
|
||||
assert(not tonumber('x'))
|
||||
assert(not tonumber('x3'))
|
||||
assert(not tonumber('0x3.3.3')) -- two decimal points
|
||||
assert(not tonumber('00x2'))
|
||||
assert(not tonumber('0x 2'))
|
||||
assert(not tonumber('0 x2'))
|
||||
assert(not tonumber('23x'))
|
||||
assert(not tonumber('- 0xaa'))
|
||||
assert(not tonumber('-0xaaP ')) -- no exponent
|
||||
assert(not tonumber('0x0.51p'))
|
||||
assert(not tonumber('0x5p+-2'))
|
||||
|
||||
|
||||
-- testing hexadecimal numerals
|
||||
@@ -705,19 +705,19 @@ do -- testing floor & ceil
|
||||
assert(eqT(math.tointeger(maxint), maxint))
|
||||
assert(eqT(math.tointeger(maxint .. ""), maxint))
|
||||
assert(eqT(math.tointeger(minint + 0.0), minint))
|
||||
assert(math.tointeger(0.0 - minint) == nil)
|
||||
assert(math.tointeger(math.pi) == nil)
|
||||
assert(math.tointeger(-math.pi) == nil)
|
||||
assert(not math.tointeger(0.0 - minint))
|
||||
assert(not math.tointeger(math.pi))
|
||||
assert(not math.tointeger(-math.pi))
|
||||
assert(math.floor(math.huge) == math.huge)
|
||||
assert(math.ceil(math.huge) == math.huge)
|
||||
assert(math.tointeger(math.huge) == nil)
|
||||
assert(not math.tointeger(math.huge))
|
||||
assert(math.floor(-math.huge) == -math.huge)
|
||||
assert(math.ceil(-math.huge) == -math.huge)
|
||||
assert(math.tointeger(-math.huge) == nil)
|
||||
assert(not math.tointeger(-math.huge))
|
||||
assert(math.tointeger("34.0") == 34)
|
||||
assert(math.tointeger("34.3") == nil)
|
||||
assert(math.tointeger({}) == nil)
|
||||
assert(math.tointeger(0/0) == nil) -- NaN
|
||||
assert(not math.tointeger("34.3"))
|
||||
assert(not math.tointeger({}))
|
||||
assert(not math.tointeger(0/0)) -- NaN
|
||||
end
|
||||
|
||||
|
||||
@@ -767,7 +767,8 @@ assert(a == '10' and b == '20')
|
||||
|
||||
do
|
||||
print("testing -0 and NaN")
|
||||
local mz, z = -0.0, 0.0
|
||||
local mz <const> = -0.0
|
||||
local z <const> = 0.0
|
||||
assert(mz == z)
|
||||
assert(1/mz < 0 and 0 < 1/z)
|
||||
local a = {[mz] = 1}
|
||||
@@ -775,17 +776,18 @@ do
|
||||
a[z] = 2
|
||||
assert(a[z] == 2 and a[mz] == 2)
|
||||
local inf = math.huge * 2 + 1
|
||||
mz, z = -1/inf, 1/inf
|
||||
local mz <const> = -1/inf
|
||||
local z <const> = 1/inf
|
||||
assert(mz == z)
|
||||
assert(1/mz < 0 and 0 < 1/z)
|
||||
local NaN = inf - inf
|
||||
local NaN <const> = inf - inf
|
||||
assert(NaN ~= NaN)
|
||||
assert(not (NaN < NaN))
|
||||
assert(not (NaN <= NaN))
|
||||
assert(not (NaN > NaN))
|
||||
assert(not (NaN >= NaN))
|
||||
assert(not (0 < NaN) and not (NaN < 0))
|
||||
local NaN1 = 0/0
|
||||
local NaN1 <const> = 0/0
|
||||
assert(NaN ~= NaN1 and not (NaN <= NaN1) and not (NaN1 <= NaN))
|
||||
local a = {}
|
||||
assert(not pcall(rawset, a, NaN, 1))
|
||||
@@ -813,9 +815,9 @@ end
|
||||
-- low-level!! For the current implementation of random in Lua,
|
||||
-- the first call after seed 1007 should return 0x7a7040a5a323c9d6
|
||||
do
|
||||
-- all computations assume at most 32-bit integers
|
||||
local h = 0x7a7040a5 -- higher half
|
||||
local l = 0xa323c9d6 -- lower half
|
||||
-- all computations should work with 32-bit integers
|
||||
local h <const> = 0x7a7040a5 -- higher half
|
||||
local l <const> = 0xa323c9d6 -- lower half
|
||||
|
||||
math.randomseed(1007)
|
||||
-- get the low 'intbits' of the 64-bit expected result
|
||||
@@ -838,7 +840,16 @@ do
|
||||
assert(rand * 2^floatbits == res)
|
||||
end
|
||||
|
||||
math.randomseed()
|
||||
do
|
||||
-- testing return of 'randomseed'
|
||||
local x, y = math.randomseed()
|
||||
local res = math.random(0)
|
||||
x, y = math.randomseed(x, y) -- should repeat the state
|
||||
assert(math.random(0) == res)
|
||||
math.randomseed(x, y) -- again should repeat the state
|
||||
assert(math.random(0) == res)
|
||||
-- keep the random seed for following tests
|
||||
end
|
||||
|
||||
do -- test random for floats
|
||||
local randbits = math.min(floatbits, 64) -- at most 64 random bits
|
||||
|
||||
+35
-40
@@ -49,33 +49,13 @@ if not T then
|
||||
else --[
|
||||
-- testing table sizes
|
||||
|
||||
local function log2 (x) return math.log(x, 2) end
|
||||
|
||||
local function mp2 (n) -- minimum power of 2 >= n
|
||||
local mp = 2^math.ceil(log2(n))
|
||||
local mp = 2^math.ceil(math.log(n, 2))
|
||||
assert(n == 0 or (mp/2 < n and n <= mp))
|
||||
return mp
|
||||
end
|
||||
|
||||
local function fb (n)
|
||||
local r, nn = T.int2fb(n)
|
||||
assert(r < 256)
|
||||
return nn
|
||||
end
|
||||
|
||||
-- test fb function
|
||||
for a = 1, 10000 do -- all numbers up to 10^4
|
||||
local n = fb(a)
|
||||
assert(a <= n and n <= a*1.125)
|
||||
end
|
||||
local a = 1024 -- plus a few up to 2 ^30
|
||||
local lim = 2^30
|
||||
while a < lim do
|
||||
local n = fb(a)
|
||||
assert(a <= n and n <= a*1.125)
|
||||
a = math.ceil(a*1.3)
|
||||
end
|
||||
|
||||
|
||||
local function check (t, na, nh)
|
||||
local a, h = T.querytab(t)
|
||||
@@ -95,30 +75,44 @@ end
|
||||
|
||||
|
||||
-- testing constructor sizes
|
||||
local lim = 40
|
||||
local s = 'return {'
|
||||
for i=1,lim do
|
||||
s = s..i..','
|
||||
local s = s
|
||||
for k=0,lim do
|
||||
local t = load(s..'}', '')()
|
||||
assert(#t == i)
|
||||
check(t, fb(i), mp2(k))
|
||||
s = string.format('%sa%d=%d,', s, k, k)
|
||||
local sizes = {0, 1, 2, 3, 4, 5, 7, 8, 9, 15, 16, 17,
|
||||
30, 31, 32, 33, 34, 500, 1000}
|
||||
|
||||
for _, sa in ipairs(sizes) do -- 'sa' is size of the array part
|
||||
local arr = {"return {"}
|
||||
for i = 1, sa do arr[1 + i] = "1," end -- build array part
|
||||
for _, sh in ipairs(sizes) do -- 'sh' is size of the hash part
|
||||
for j = 1, sh do -- build hash part
|
||||
arr[1 + sa + j] = string.format('k%x=%d,', j, j)
|
||||
end
|
||||
arr[1 + sa + sh + 1] = "}"
|
||||
local prog = table.concat(arr)
|
||||
local f = assert(load(prog))
|
||||
f() -- call once to ensure stack space
|
||||
-- make sure table is not resized after being created
|
||||
if sa == 0 or sh == 0 then
|
||||
T.alloccount(2); -- header + array or hash part
|
||||
else
|
||||
T.alloccount(3); -- header + array part + hash part
|
||||
end
|
||||
local t = f()
|
||||
T.alloccount();
|
||||
assert(#t == sa)
|
||||
check(t, sa, mp2(sh))
|
||||
end
|
||||
end
|
||||
|
||||
|
||||
-- tests with unknown number of elements
|
||||
local a = {}
|
||||
for i=1,lim do a[i] = i end -- build auxiliary table
|
||||
for k=0,lim do
|
||||
local a = {table.unpack(a,1,k)}
|
||||
assert(#a == k)
|
||||
check(a, k, 0)
|
||||
a = {1,2,3,table.unpack(a,1,k)}
|
||||
check(a, k+3, 0)
|
||||
assert(#a == k + 3)
|
||||
for i=1,sizes[#sizes] do a[i] = i end -- build auxiliary table
|
||||
for k in ipairs(sizes) do
|
||||
local t = {table.unpack(a,1,k)}
|
||||
assert(#t == k)
|
||||
check(t, k, 0)
|
||||
t = {1,2,3,table.unpack(a,1,k)}
|
||||
check(t, k+3, 0)
|
||||
assert(#t == k + 3)
|
||||
end
|
||||
|
||||
|
||||
@@ -677,7 +671,8 @@ collectgarbage()
|
||||
|
||||
local function f (n, p)
|
||||
local t = {}; for i=1,p do t[i] = i*10 end
|
||||
return function (_,n)
|
||||
return function (_, n, ...)
|
||||
assert(select("#", ...) == 0) -- no extra arguments
|
||||
if n > 0 then
|
||||
n = n-1
|
||||
return n, table.unpack(t)
|
||||
|
||||
Executable
+54
@@ -0,0 +1,54 @@
|
||||
NAME=$1"-tests"
|
||||
|
||||
ln -s . $NAME
|
||||
ln -s .. ltests
|
||||
|
||||
tar --create --gzip --no-recursion --file=$NAME.tar.gz \
|
||||
$NAME/all.lua \
|
||||
$NAME/api.lua \
|
||||
$NAME/attrib.lua \
|
||||
$NAME/big.lua \
|
||||
$NAME/bitwise.lua \
|
||||
$NAME/bwcoercion.lua \
|
||||
$NAME/calls.lua \
|
||||
$NAME/closure.lua \
|
||||
$NAME/code.lua \
|
||||
$NAME/constructs.lua \
|
||||
$NAME/coroutine.lua \
|
||||
$NAME/cstack.lua \
|
||||
$NAME/db.lua \
|
||||
$NAME/errors.lua \
|
||||
$NAME/events.lua \
|
||||
$NAME/files.lua \
|
||||
$NAME/gc.lua \
|
||||
$NAME/gengc.lua \
|
||||
$NAME/goto.lua \
|
||||
$NAME/heavy.lua \
|
||||
$NAME/literals.lua \
|
||||
$NAME/locals.lua \
|
||||
$NAME/main.lua \
|
||||
$NAME/math.lua \
|
||||
$NAME/nextvar.lua \
|
||||
$NAME/pm.lua \
|
||||
$NAME/sort.lua \
|
||||
$NAME/strings.lua \
|
||||
$NAME/tpack.lua \
|
||||
$NAME/utf8.lua \
|
||||
$NAME/vararg.lua \
|
||||
$NAME/verybig.lua \
|
||||
$NAME/libs/makefile \
|
||||
$NAME/libs/P1 \
|
||||
$NAME/libs/lib1.c \
|
||||
$NAME/libs/lib11.c \
|
||||
$NAME/libs/lib2.c \
|
||||
$NAME/libs/lib21.c \
|
||||
$NAME/libs/lib22.c \
|
||||
$NAME/ltests/ltests.h \
|
||||
$NAME/ltests/ltests.c
|
||||
|
||||
\rm -I $NAME
|
||||
\rm -I ltests
|
||||
|
||||
echo ${NAME}.tar.gz" created"
|
||||
|
||||
|
||||
+11
-11
@@ -28,10 +28,10 @@ a,b = string.find('a\0a\0a\0a\0\0ab', '\0ab', 2); -- finds at the end
|
||||
assert(a == 9 and b == 11);
|
||||
a,b = string.find('a\0a\0a\0a\0\0ab', 'b') -- last position
|
||||
assert(a == 11 and b == 11)
|
||||
assert(string.find('a\0a\0a\0a\0\0ab', 'b\0') == nil) -- check ending
|
||||
assert(string.find('', '\0') == nil)
|
||||
assert(not string.find('a\0a\0a\0a\0\0ab', 'b\0')) -- check ending
|
||||
assert(not string.find('', '\0'))
|
||||
assert(string.find('alo123alo', '12') == 4)
|
||||
assert(string.find('alo123alo', '^12') == nil)
|
||||
assert(not string.find('alo123alo', '^12'))
|
||||
|
||||
assert(string.match("aaab", ".*b") == "aaab")
|
||||
assert(string.match("aaa", ".*a") == "aaa")
|
||||
@@ -57,17 +57,17 @@ assert(f('aaa', 'ab*a') == 'aa')
|
||||
assert(f('aba', 'ab*a') == 'aba')
|
||||
assert(f('aaab', 'a+') == 'aaa')
|
||||
assert(f('aaa', '^.+$') == 'aaa')
|
||||
assert(f('aaa', 'b+') == nil)
|
||||
assert(f('aaa', 'ab+a') == nil)
|
||||
assert(not f('aaa', 'b+'))
|
||||
assert(not f('aaa', 'ab+a'))
|
||||
assert(f('aba', 'ab+a') == 'aba')
|
||||
assert(f('a$a', '.$') == 'a')
|
||||
assert(f('a$a', '.%$') == 'a$')
|
||||
assert(f('a$a', '.$.') == 'a$a')
|
||||
assert(f('a$a', '$$') == nil)
|
||||
assert(f('a$b', 'a$') == nil)
|
||||
assert(not f('a$a', '$$'))
|
||||
assert(not f('a$b', 'a$'))
|
||||
assert(f('a$a', '$') == '')
|
||||
assert(f('', 'b*') == '')
|
||||
assert(f('aaa', 'bb*') == nil)
|
||||
assert(not f('aaa', 'bb*'))
|
||||
assert(f('aaab', 'a-') == '')
|
||||
assert(f('aaa', '^.-$') == 'aaa')
|
||||
assert(f('aabaaabaaabaaaba', 'b.*b') == 'baaabaaabaaab')
|
||||
@@ -101,7 +101,7 @@ end
|
||||
assert(f1('alo alx 123 b\0o b\0o', '(..*) %1') == "b\0o b\0o")
|
||||
assert(f1('axz123= 4= 4 34', '(.+)=(.*)=%2 %1') == '3= 4= 4 3')
|
||||
assert(f1('=======', '^(=*)=%1$') == '=======')
|
||||
assert(string.match('==========', '^([=]*)=%1$') == nil)
|
||||
assert(not string.match('==========', '^([=]*)=%1$'))
|
||||
|
||||
local function range (i, j)
|
||||
if i <= j then
|
||||
@@ -135,7 +135,7 @@ print('+');
|
||||
assert(string.match("alo xyzK", "(%w+)K") == "xyz")
|
||||
assert(string.match("254 K", "(%d*)K") == "")
|
||||
assert(string.match("alo ", "(%w*)$") == "")
|
||||
assert(string.match("alo ", "(%w+)$") == nil)
|
||||
assert(not string.match("alo ", "(%w+)$"))
|
||||
assert(string.find("(álo)", "%(á") == 1)
|
||||
local a, b, c, d, e = string.match("âlo alo", "^(((.).).* (%w*))$")
|
||||
assert(a == 'âlo alo' and b == 'âl' and c == 'â' and d == 'alo' and e == nil)
|
||||
@@ -209,7 +209,7 @@ assert(s == r and t[1] == 1 and t[3] == 3 and t[7] == 4 and t[13] == 4)
|
||||
|
||||
|
||||
function isbalanced (s)
|
||||
return string.find(string.gsub(s, "%b()", ""), "[()]") == nil
|
||||
return not string.find(string.gsub(s, "%b()", ""), "[()]")
|
||||
end
|
||||
|
||||
assert(isbalanced("(9 ((8))(\0) 7) \0\0 a b ()(c)() a"))
|
||||
|
||||
+24
-10
@@ -3,8 +3,8 @@
|
||||
|
||||
print('testing strings and string library')
|
||||
|
||||
local <const> maxi = math.maxinteger
|
||||
local <const> mini = math.mininteger
|
||||
local maxi <const> = math.maxinteger
|
||||
local mini <const> = math.mininteger
|
||||
|
||||
|
||||
local function checkerror (msg, f, ...)
|
||||
@@ -56,13 +56,13 @@ a,b = string.find("123456789", "345")
|
||||
assert(string.sub("123456789", a, b) == "345")
|
||||
assert(string.find("1234567890123456789", "345", 3) == 3)
|
||||
assert(string.find("1234567890123456789", "345", 4) == 13)
|
||||
assert(string.find("1234567890123456789", "346", 4) == nil)
|
||||
assert(not string.find("1234567890123456789", "346", 4))
|
||||
assert(string.find("1234567890123456789", ".45", -9) == 13)
|
||||
assert(string.find("abcdefg", "\0", 5, 1) == nil)
|
||||
assert(not string.find("abcdefg", "\0", 5, 1))
|
||||
assert(string.find("", "") == 1)
|
||||
assert(string.find("", "", 1) == 1)
|
||||
assert(not string.find("", "", 2))
|
||||
assert(string.find('', 'aaa', 1) == nil)
|
||||
assert(not string.find('', 'aaa', 1))
|
||||
assert(('alo(.)alo'):find('(.)', 1, 1) == 4)
|
||||
|
||||
assert(string.len("") == 0)
|
||||
@@ -163,11 +163,19 @@ do -- tests for '%p' format
|
||||
assert(string.format("%p", 4) == null)
|
||||
assert(string.format("%p", print) ~= null)
|
||||
assert(string.format("%p", coroutine.running()) ~= null)
|
||||
assert(string.format("%p", {}) ~= string.format("%p", {}))
|
||||
assert(string.format("%p", string.rep("a", 10)) ==
|
||||
string.format("%p", string.rep("a", 10))) -- short strings
|
||||
assert(string.format("%p", string.rep("a", 300)) ~=
|
||||
string.format("%p", string.rep("a", 300))) -- long strings
|
||||
do
|
||||
local t1 = {}; local t2 = {}
|
||||
assert(string.format("%p", t1) ~= string.format("%p", t2))
|
||||
end
|
||||
do -- short strings
|
||||
local s1 = string.rep("a", 10)
|
||||
local s2 = string.rep("a", 10)
|
||||
assert(string.format("%p", s1) == string.format("%p", s2))
|
||||
end
|
||||
do -- long strings
|
||||
local s1 = string.rep("a", 300); local s2 = string.rep("a", 300)
|
||||
assert(string.format("%p", s1) ~= string.format("%p", s2))
|
||||
end
|
||||
assert(#string.format("%90p", {}) == 90)
|
||||
end
|
||||
|
||||
@@ -250,6 +258,12 @@ do -- longest number that can be formatted
|
||||
local s = string.format('%.99f', -(10^i))
|
||||
assert(string.len(s) >= i + 101)
|
||||
assert(tonumber(s) == -(10^i))
|
||||
|
||||
-- limit for floats
|
||||
assert(10^38 < math.huge)
|
||||
local s = string.format('%.99f', -(10^38))
|
||||
assert(string.len(s) >= 38 + 101)
|
||||
assert(tonumber(s) == -(10^38))
|
||||
end
|
||||
|
||||
|
||||
|
||||
+2
-2
@@ -30,8 +30,8 @@ local function checksyntax (s, t)
|
||||
assert(assert(load(ts))() == s)
|
||||
end
|
||||
|
||||
assert(utf8.offset("alo", 5) == nil)
|
||||
assert(utf8.offset("alo", -4) == nil)
|
||||
assert(not utf8.offset("alo", 5))
|
||||
assert(not utf8.offset("alo", -4))
|
||||
|
||||
-- 'check' makes several tests over the validity of string 's'.
|
||||
-- 't' is the list of codepoints of 's'.
|
||||
|
||||
Reference in New Issue
Block a user