mirror of
https://github.com/lua/lua
synced 2026-06-08 15:34:49 +00:00
Compare commits
134 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
| 9d067ab73b | |||
| 2f4162bc47 | |||
| ab1aca94e8 | |||
| d282652561 | |||
| 58216600eb | |||
| 94cbe46511 | |||
| 69b71a6919 | |||
| d742a193e5 | |||
| e4a38eb0e8 | |||
| f07de22576 | |||
| 52c8679760 | |||
| 849b2ecbd2 | |||
| 30528049f1 | |||
| 9a89fb1c9d | |||
| c23cc86c54 | |||
| 171dcd7d74 | |||
| 5aa36e894f | |||
| 9ecd446141 | |||
| fb172d0a92 | |||
| 0085db4596 | |||
| 490d42b5f8 | |||
| 287b302acb | |||
| 5d8ce05b3f | |||
| f83de8e34e | |||
| 9f791535cd | |||
| 51c29ec0c4 | |||
| fbaf040f5e | |||
| e51564d1be | |||
| 98ec799591 | |||
| b6888a158b | |||
| 613513d09f | |||
| 8496112a18 | |||
| 6bc0f13505 | |||
| 8a89da07ba | |||
| f849885a4b | |||
| f7ce7e5faa | |||
| 65141832d2 | |||
| f13dc59416 | |||
| 7c3cb71fa4 | |||
| 68109afcdb | |||
| 9cf3299faf | |||
| b9b554e0f6 | |||
| 0dc5deca1c | |||
| b4c353434f | |||
| 71f70df327 | |||
| 663f83f647 | |||
| ae5b5ba529 | |||
| a585eae6e7 | |||
| d2c2e32e8a | |||
| 8c7c9ea065 | |||
| a6da1472c0 | |||
| 34affe7a63 | |||
| a2195644d8 | |||
| 1ecfbfa1a1 | |||
| e1d8770f12 | |||
| 0f1cd0eba9 | |||
| 127e7a6c89 | |||
| 6f5bd5072d | |||
| 56ec432281 | |||
| 31b8c2d438 | |||
| eb41999461 | |||
| 314c6057b7 | |||
| d39ea8b3ce | |||
| 6298903e35 | |||
| b57574d6fb | |||
| bfcf06d91a | |||
| 0280407fc5 | |||
| ae809e9fd1 | |||
| e96385aded | |||
| 56a165bf0f | |||
| 422ce50d2e | |||
| c33b1728ae | |||
| a304199836 | |||
| 6d7cd31fee | |||
| 993c58fde3 | |||
| d49b288728 | |||
| 69e84805e4 | |||
| 364e569945 | |||
| 63295f1f7f | |||
| 50523b107d | |||
| aa8d4a782d | |||
| efcf24be0c | |||
| 17dbaa8639 | |||
| 9514abc2da | |||
| 0be57b9b6d | |||
| 61a4e64a66 | |||
| 9a6f47f0ed | |||
| 948fb628d9 | |||
| a901c505ab | |||
| 0ddc0f47bd | |||
| cac075a122 | |||
| 9e0a8475cd | |||
| 7ccc6d8290 | |||
| 7288528a1e | |||
| 513559cc47 | |||
| e460752323 | |||
| 92594f0939 | |||
| e8a52281d9 | |||
| 6eb53b7526 | |||
| 9b7987a9d1 | |||
| 28ef7061bb | |||
| 46c3587a6f | |||
| 69c7139ff8 | |||
| 5ff408d218 | |||
| e3c83835e7 | |||
| bd1b87c579 | |||
| d7bb8df841 | |||
| c646e57fd6 | |||
| e0ab13c62f | |||
| e0cbaa50fa | |||
| 3747487320 | |||
| 1e0ad018ce | |||
| 95735bda46 | |||
| d30569c064 | |||
| 2d92102dee | |||
| 490ecfcaa1 | |||
| e174f43807 | |||
| 81f2401c6d | |||
| 508a705c1c | |||
| 6f1c033d72 | |||
| 5f83fb6582 | |||
| 679dc72c08 | |||
| 1499680f9e | |||
| bdcfae2e1c | |||
| 7d526e75a7 | |||
| c12983cf8a | |||
| ba9cd0d25a | |||
| b93f3b00bb | |||
| 4c32d9300c | |||
| 6e285e5392 | |||
| b8cdea0190 | |||
| e592f94a64 | |||
| 6e1aec7a67 | |||
| 6055a039b5 |
+15
@@ -0,0 +1,15 @@
|
||||
.gitattributes
|
||||
|
||||
*.so
|
||||
*.o
|
||||
*.a
|
||||
|
||||
manual/manual.html
|
||||
|
||||
testes/time.txt
|
||||
testes/time-debug.txt
|
||||
|
||||
testes/libs/all
|
||||
|
||||
temp
|
||||
lua
|
||||
@@ -0,0 +1,7 @@
|
||||
# Lua
|
||||
|
||||
This is the repository of Lua development code, as seen by the Lua team. It contains the full history of all commits but is mirrored irregularly. For complete information about Lua, visit [Lua.org](https://www.lua.org/).
|
||||
|
||||
Please **do not** send pull requests. To report issues and send patches, post a message to the [Lua mailing list](https://www.lua.org/lua-l.html).
|
||||
|
||||
Download official Lua releases from [Lua.org](https://www.lua.org/download.html).
|
||||
@@ -1,7 +1,7 @@
|
||||
make -s -j
|
||||
cd testes/libs; make -s
|
||||
cd .. # back to directory 'testes'
|
||||
ulimit -S -s 2000
|
||||
ulimit -S -s 1000
|
||||
if { ../lua -W all.lua; } then
|
||||
echo -e "\n\n final OK!!!!\n\n"
|
||||
else
|
||||
|
||||
@@ -97,8 +97,9 @@ static StkId index2stack (lua_State *L, int idx) {
|
||||
|
||||
LUA_API int lua_checkstack (lua_State *L, int n) {
|
||||
int res;
|
||||
CallInfo *ci = L->ci;
|
||||
CallInfo *ci;
|
||||
lua_lock(L);
|
||||
ci = L->ci;
|
||||
api_check(L, n >= 0, "negative 'n'");
|
||||
if (L->stack_last - L->top > n) /* stack large enough? */
|
||||
res = 1; /* yes; check is OK */
|
||||
@@ -170,10 +171,12 @@ LUA_API int lua_gettop (lua_State *L) {
|
||||
|
||||
|
||||
LUA_API void lua_settop (lua_State *L, int idx) {
|
||||
CallInfo *ci = L->ci;
|
||||
StkId func = ci->func;
|
||||
CallInfo *ci;
|
||||
StkId func;
|
||||
ptrdiff_t diff; /* difference for new top */
|
||||
lua_lock(L);
|
||||
ci = L->ci;
|
||||
func = ci->func;
|
||||
if (idx >= 0) {
|
||||
api_check(L, idx <= ci->top - (func + 1), "new top too large");
|
||||
diff = ((func + 1) + idx) - L->top;
|
||||
@@ -230,7 +233,7 @@ LUA_API void lua_copy (lua_State *L, int fromidx, int toidx) {
|
||||
lua_lock(L);
|
||||
fr = index2value(L, fromidx);
|
||||
to = index2value(L, toidx);
|
||||
api_check(l, isvalid(L, to), "invalid index");
|
||||
api_check(L, isvalid(L, to), "invalid index");
|
||||
setobj(L, to, fr);
|
||||
if (isupvalue(toidx)) /* function upvalue? */
|
||||
luaC_barrier(L, clCvalue(s2v(L->ci->func)), fr);
|
||||
@@ -262,7 +265,7 @@ LUA_API int lua_type (lua_State *L, int idx) {
|
||||
|
||||
LUA_API const char *lua_typename (lua_State *L, int t) {
|
||||
UNUSED(L);
|
||||
api_check(L, LUA_TNONE <= t && t < LUA_NUMTAGS, "invalid tag");
|
||||
api_check(L, LUA_TNONE <= t && t < LUA_NUMTYPES, "invalid type");
|
||||
return ttypename(t);
|
||||
}
|
||||
|
||||
@@ -350,23 +353,21 @@ LUA_API size_t lua_stringtonumber (lua_State *L, const char *s) {
|
||||
|
||||
|
||||
LUA_API lua_Number lua_tonumberx (lua_State *L, int idx, int *pisnum) {
|
||||
lua_Number n;
|
||||
lua_Number n = 0;
|
||||
const TValue *o = index2value(L, idx);
|
||||
int isnum = tonumber(o, &n);
|
||||
if (!isnum)
|
||||
n = 0; /* call to 'tonumber' may change 'n' even if it fails */
|
||||
if (pisnum) *pisnum = isnum;
|
||||
if (pisnum)
|
||||
*pisnum = isnum;
|
||||
return n;
|
||||
}
|
||||
|
||||
|
||||
LUA_API lua_Integer lua_tointegerx (lua_State *L, int idx, int *pisnum) {
|
||||
lua_Integer res;
|
||||
lua_Integer res = 0;
|
||||
const TValue *o = index2value(L, idx);
|
||||
int isnum = tointeger(o, &res);
|
||||
if (!isnum)
|
||||
res = 0; /* call to 'tointeger' may change 'n' even if it fails */
|
||||
if (pisnum) *pisnum = isnum;
|
||||
if (pisnum)
|
||||
*pisnum = isnum;
|
||||
return res;
|
||||
}
|
||||
|
||||
@@ -378,20 +379,22 @@ LUA_API int lua_toboolean (lua_State *L, int idx) {
|
||||
|
||||
|
||||
LUA_API const char *lua_tolstring (lua_State *L, int idx, size_t *len) {
|
||||
TValue *o = index2value(L, idx);
|
||||
TValue *o;
|
||||
lua_lock(L);
|
||||
o = index2value(L, idx);
|
||||
if (!ttisstring(o)) {
|
||||
if (!cvt2str(o)) { /* not convertible? */
|
||||
if (len != NULL) *len = 0;
|
||||
lua_unlock(L);
|
||||
return NULL;
|
||||
}
|
||||
lua_lock(L); /* 'luaO_tostring' may create a new string */
|
||||
luaO_tostring(L, o);
|
||||
luaC_checkGC(L);
|
||||
o = index2value(L, idx); /* previous call may reallocate the stack */
|
||||
lua_unlock(L);
|
||||
}
|
||||
if (len != NULL)
|
||||
*len = vslen(o);
|
||||
lua_unlock(L);
|
||||
return svalue(o);
|
||||
}
|
||||
|
||||
@@ -399,10 +402,10 @@ LUA_API const char *lua_tolstring (lua_State *L, int idx, size_t *len) {
|
||||
LUA_API lua_Unsigned lua_rawlen (lua_State *L, int idx) {
|
||||
const TValue *o = index2value(L, idx);
|
||||
switch (ttypetag(o)) {
|
||||
case LUA_TSHRSTR: return tsvalue(o)->shrlen;
|
||||
case LUA_TLNGSTR: return tsvalue(o)->u.lnglen;
|
||||
case LUA_TUSERDATA: return uvalue(o)->len;
|
||||
case LUA_TTABLE: return luaH_getn(hvalue(o));
|
||||
case LUA_VSHRSTR: return tsvalue(o)->shrlen;
|
||||
case LUA_VLNGSTR: return tsvalue(o)->u.lnglen;
|
||||
case LUA_VUSERDATA: return uvalue(o)->len;
|
||||
case LUA_VTABLE: return luaH_getn(hvalue(o));
|
||||
default: return 0;
|
||||
}
|
||||
}
|
||||
@@ -448,8 +451,8 @@ LUA_API lua_State *lua_tothread (lua_State *L, int idx) {
|
||||
LUA_API const void *lua_topointer (lua_State *L, int idx) {
|
||||
const TValue *o = index2value(L, idx);
|
||||
switch (ttypetag(o)) {
|
||||
case LUA_TLCF: return cast_voidp(cast_sizet(fvalue(o)));
|
||||
case LUA_TUSERDATA: case LUA_TLIGHTUSERDATA:
|
||||
case LUA_VLCF: return cast_voidp(cast_sizet(fvalue(o)));
|
||||
case LUA_VUSERDATA: case LUA_VLIGHTUSERDATA:
|
||||
return touserdata(o);
|
||||
default: {
|
||||
if (iscollectable(o))
|
||||
@@ -565,6 +568,7 @@ LUA_API void lua_pushcclosure (lua_State *L, lua_CFunction fn, int n) {
|
||||
while (n--) {
|
||||
setobj2n(L, &cl->upvalue[n], s2v(L->top + n));
|
||||
/* does not need barrier because closure is white */
|
||||
lua_assert(iswhite(cl));
|
||||
}
|
||||
setclCvalue(L, s2v(L->top), cl);
|
||||
api_incr_top(L);
|
||||
@@ -576,7 +580,10 @@ LUA_API void lua_pushcclosure (lua_State *L, lua_CFunction fn, int n) {
|
||||
|
||||
LUA_API void lua_pushboolean (lua_State *L, int b) {
|
||||
lua_lock(L);
|
||||
setbvalue(s2v(L->top), (b != 0)); /* ensure that true is 1 */
|
||||
if (b)
|
||||
setbtvalue(s2v(L->top));
|
||||
else
|
||||
setbfvalue(s2v(L->top));
|
||||
api_incr_top(L);
|
||||
lua_unlock(L);
|
||||
}
|
||||
@@ -623,8 +630,9 @@ static int auxgetstr (lua_State *L, const TValue *t, const char *k) {
|
||||
|
||||
|
||||
LUA_API int lua_getglobal (lua_State *L, const char *name) {
|
||||
Table *reg = hvalue(&G(L)->l_registry);
|
||||
Table *reg;
|
||||
lua_lock(L);
|
||||
reg = hvalue(&G(L)->l_registry);
|
||||
return auxgetstr(L, luaH_getint(reg, LUA_RIDX_GLOBALS), name);
|
||||
}
|
||||
|
||||
@@ -803,8 +811,9 @@ static void auxsetstr (lua_State *L, const TValue *t, const char *k) {
|
||||
|
||||
|
||||
LUA_API void lua_setglobal (lua_State *L, const char *name) {
|
||||
Table *reg = hvalue(&G(L)->l_registry);
|
||||
Table *reg;
|
||||
lua_lock(L); /* unlock done in 'auxsetstr' */
|
||||
reg = hvalue(&G(L)->l_registry);
|
||||
auxsetstr(L, luaH_getint(reg, LUA_RIDX_GLOBALS), name);
|
||||
}
|
||||
|
||||
@@ -850,21 +859,33 @@ LUA_API void lua_seti (lua_State *L, int idx, lua_Integer n) {
|
||||
}
|
||||
|
||||
|
||||
LUA_API void lua_rawset (lua_State *L, int idx) {
|
||||
static void aux_rawset (lua_State *L, int idx, TValue *key, int n) {
|
||||
Table *t;
|
||||
TValue *slot;
|
||||
lua_lock(L);
|
||||
api_checknelems(L, 2);
|
||||
api_checknelems(L, n);
|
||||
t = gettable(L, idx);
|
||||
slot = luaH_set(L, t, s2v(L->top - 2));
|
||||
slot = luaH_set(L, t, key);
|
||||
setobj2t(L, slot, s2v(L->top - 1));
|
||||
invalidateTMcache(t);
|
||||
luaC_barrierback(L, obj2gco(t), s2v(L->top - 1));
|
||||
L->top -= 2;
|
||||
L->top -= n;
|
||||
lua_unlock(L);
|
||||
}
|
||||
|
||||
|
||||
LUA_API void lua_rawset (lua_State *L, int idx) {
|
||||
aux_rawset(L, idx, s2v(L->top - 2), 2);
|
||||
}
|
||||
|
||||
|
||||
LUA_API void lua_rawsetp (lua_State *L, int idx, const void *p) {
|
||||
TValue k;
|
||||
setpvalue(&k, cast_voidp(p));
|
||||
aux_rawset(L, idx, &k, 1);
|
||||
}
|
||||
|
||||
|
||||
LUA_API void lua_rawseti (lua_State *L, int idx, lua_Integer n) {
|
||||
Table *t;
|
||||
lua_lock(L);
|
||||
@@ -877,21 +898,6 @@ LUA_API void lua_rawseti (lua_State *L, int idx, lua_Integer n) {
|
||||
}
|
||||
|
||||
|
||||
LUA_API void lua_rawsetp (lua_State *L, int idx, const void *p) {
|
||||
Table *t;
|
||||
TValue k, *slot;
|
||||
lua_lock(L);
|
||||
api_checknelems(L, 1);
|
||||
t = gettable(L, idx);
|
||||
setpvalue(&k, cast_voidp(p));
|
||||
slot = luaH_set(L, t, &k);
|
||||
setobj2t(L, slot, s2v(L->top - 1));
|
||||
luaC_barrierback(L, obj2gco(t), s2v(L->top - 1));
|
||||
L->top--;
|
||||
lua_unlock(L);
|
||||
}
|
||||
|
||||
|
||||
LUA_API int lua_setmetatable (lua_State *L, int objindex) {
|
||||
TValue *obj;
|
||||
Table *mt;
|
||||
@@ -1095,8 +1101,9 @@ LUA_API int lua_status (lua_State *L) {
|
||||
LUA_API int lua_gc (lua_State *L, int what, ...) {
|
||||
va_list argp;
|
||||
int res = 0;
|
||||
global_State *g = G(L);
|
||||
global_State *g;
|
||||
lua_lock(L);
|
||||
g = G(L);
|
||||
va_start(argp, what);
|
||||
switch (what) {
|
||||
case LUA_GCSTOP: {
|
||||
@@ -1196,9 +1203,15 @@ LUA_API int lua_gc (lua_State *L, int what, ...) {
|
||||
|
||||
|
||||
LUA_API int lua_error (lua_State *L) {
|
||||
TValue *errobj;
|
||||
lua_lock(L);
|
||||
errobj = s2v(L->top - 1);
|
||||
api_checknelems(L, 1);
|
||||
luaG_errormsg(L);
|
||||
/* error object is the memory error message? */
|
||||
if (ttisshrstring(errobj) && eqshrstr(tsvalue(errobj), G(L)->memerrmsg))
|
||||
luaM_error(L); /* raise a memory error */
|
||||
else
|
||||
luaG_errormsg(L); /* raise a regular error */
|
||||
/* code unreachable; will unlock when control actually leaves the kernel */
|
||||
return 0; /* to avoid warnings */
|
||||
}
|
||||
@@ -1240,14 +1253,12 @@ LUA_API void lua_toclose (lua_State *L, int idx) {
|
||||
LUA_API void lua_concat (lua_State *L, int n) {
|
||||
lua_lock(L);
|
||||
api_checknelems(L, n);
|
||||
if (n >= 2) {
|
||||
if (n > 0)
|
||||
luaV_concat(L, n);
|
||||
}
|
||||
else if (n == 0) { /* push empty string */
|
||||
setsvalue2s(L, L->top, luaS_newlstr(L, "", 0));
|
||||
else { /* nothing to concatenate */
|
||||
setsvalue2s(L, L->top, luaS_newlstr(L, "", 0)); /* push empty string */
|
||||
api_incr_top(L);
|
||||
}
|
||||
/* else n == 1; nothing to do */
|
||||
luaC_checkGC(L);
|
||||
lua_unlock(L);
|
||||
}
|
||||
@@ -1314,7 +1325,7 @@ LUA_API void *lua_newuserdatauv (lua_State *L, size_t size, int nuvalue) {
|
||||
static const char *aux_upvalue (TValue *fi, int n, TValue **val,
|
||||
GCObject **owner) {
|
||||
switch (ttypetag(fi)) {
|
||||
case LUA_TCCL: { /* C closure */
|
||||
case LUA_VCCL: { /* C closure */
|
||||
CClosure *f = clCvalue(fi);
|
||||
if (!(cast_uint(n) - 1u < cast_uint(f->nupvalues)))
|
||||
return NULL; /* 'n' not in [1, f->nupvalues] */
|
||||
@@ -1322,7 +1333,7 @@ static const char *aux_upvalue (TValue *fi, int n, TValue **val,
|
||||
if (owner) *owner = obj2gco(f);
|
||||
return "";
|
||||
}
|
||||
case LUA_TLCL: { /* Lua closure */
|
||||
case LUA_VLCL: { /* Lua closure */
|
||||
LClosure *f = clLvalue(fi);
|
||||
TString *name;
|
||||
Proto *p = f->p;
|
||||
@@ -1372,29 +1383,35 @@ LUA_API const char *lua_setupvalue (lua_State *L, int funcindex, int n) {
|
||||
|
||||
|
||||
static UpVal **getupvalref (lua_State *L, int fidx, int n, LClosure **pf) {
|
||||
static const UpVal *const nullup = NULL;
|
||||
LClosure *f;
|
||||
TValue *fi = index2value(L, fidx);
|
||||
api_check(L, ttisLclosure(fi), "Lua function expected");
|
||||
f = clLvalue(fi);
|
||||
api_check(L, (1 <= n && n <= f->p->sizeupvalues), "invalid upvalue index");
|
||||
if (pf) *pf = f;
|
||||
return &f->upvals[n - 1]; /* get its upvalue pointer */
|
||||
if (1 <= n && n <= f->p->sizeupvalues)
|
||||
return &f->upvals[n - 1]; /* get its upvalue pointer */
|
||||
else
|
||||
return (UpVal**)&nullup;
|
||||
}
|
||||
|
||||
|
||||
LUA_API void *lua_upvalueid (lua_State *L, int fidx, int n) {
|
||||
TValue *fi = index2value(L, fidx);
|
||||
switch (ttypetag(fi)) {
|
||||
case LUA_TLCL: { /* lua closure */
|
||||
case LUA_VLCL: { /* lua closure */
|
||||
return *getupvalref(L, fidx, n, NULL);
|
||||
}
|
||||
case LUA_TCCL: { /* C closure */
|
||||
case LUA_VCCL: { /* C closure */
|
||||
CClosure *f = clCvalue(fi);
|
||||
api_check(L, 1 <= n && n <= f->nupvalues, "invalid upvalue index");
|
||||
return &f->upvalue[n - 1];
|
||||
}
|
||||
if (1 <= n && n <= f->nupvalues)
|
||||
return &f->upvalue[n - 1];
|
||||
/* else */
|
||||
} /* FALLTHROUGH */
|
||||
case LUA_VLCF:
|
||||
return NULL; /* light C functions have no upvalues */
|
||||
default: {
|
||||
api_check(L, 0, "closure expected");
|
||||
api_check(L, 0, "function expected");
|
||||
return NULL;
|
||||
}
|
||||
}
|
||||
@@ -1406,6 +1423,7 @@ LUA_API void lua_upvaluejoin (lua_State *L, int fidx1, int n1,
|
||||
LClosure *f1;
|
||||
UpVal **up1 = getupvalref(L, fidx1, n1, &f1);
|
||||
UpVal **up2 = getupvalref(L, fidx2, n2, NULL);
|
||||
api_check(L, *up1 != NULL && *up2 != NULL, "invalid upvalue index");
|
||||
*up1 = *up2;
|
||||
luaC_objbarrier(L, f1, *up1);
|
||||
}
|
||||
|
||||
@@ -19,7 +19,7 @@
|
||||
|
||||
/*
|
||||
** If a call returns too many multiple returns, the callee may not have
|
||||
** stack space to accomodate all results. In this case, this macro
|
||||
** stack space to accommodate all results. In this case, this macro
|
||||
** increases its stack space ('L->ci->top').
|
||||
*/
|
||||
#define adjustresults(L,nres) \
|
||||
|
||||
@@ -87,7 +87,7 @@ static int pushglobalfuncname (lua_State *L, lua_Debug *ar) {
|
||||
lua_remove(L, -2); /* remove original name */
|
||||
}
|
||||
lua_copy(L, -1, top + 1); /* copy name to proper place */
|
||||
lua_settop(L, top + 1); /* remove table "loaded" an name copy */
|
||||
lua_settop(L, top + 1); /* remove table "loaded" and name copy */
|
||||
return 1;
|
||||
}
|
||||
else {
|
||||
@@ -283,10 +283,10 @@ LUALIB_API int luaL_fileresult (lua_State *L, int stat, const char *fname) {
|
||||
|
||||
|
||||
LUALIB_API int luaL_execresult (lua_State *L, int stat) {
|
||||
const char *what = "exit"; /* type of termination */
|
||||
if (stat == -1) /* error? */
|
||||
if (stat != 0 && errno != 0) /* error with an 'errno'? */
|
||||
return luaL_fileresult(L, 0, NULL);
|
||||
else {
|
||||
const char *what = "exit"; /* type of termination */
|
||||
l_inspectstat(stat, what); /* interpret result */
|
||||
if (*what == 'e' && stat == 0) /* successful termination? */
|
||||
lua_pushboolean(L, 1);
|
||||
@@ -475,8 +475,10 @@ static void *resizebox (lua_State *L, int idx, size_t newsize) {
|
||||
lua_Alloc allocf = lua_getallocf(L, &ud);
|
||||
UBox *box = (UBox *)lua_touserdata(L, idx);
|
||||
void *temp = allocf(ud, box->box, box->bsize, newsize);
|
||||
if (temp == NULL && newsize > 0) /* allocation error? */
|
||||
luaL_error(L, "not enough memory for buffer allocation");
|
||||
if (temp == NULL && newsize > 0) { /* allocation error? */
|
||||
lua_pushliteral(L, "not enough memory");
|
||||
lua_error(L); /* raise a memory error */
|
||||
}
|
||||
box->box = temp;
|
||||
box->bsize = newsize;
|
||||
return temp;
|
||||
@@ -902,10 +904,10 @@ LUALIB_API const char *luaL_tolstring (lua_State *L, int idx, size_t *len) {
|
||||
LUALIB_API void luaL_setfuncs (lua_State *L, const luaL_Reg *l, int nup) {
|
||||
luaL_checkstack(L, nup, "too many upvalues");
|
||||
for (; l->name != NULL; l++) { /* fill the table with given functions */
|
||||
int i;
|
||||
if (l->func == NULL) /* place holder? */
|
||||
lua_pushboolean(L, 0);
|
||||
else {
|
||||
int i;
|
||||
for (i = 0; i < nup; i++) /* copy upvalues to the top */
|
||||
lua_pushvalue(L, -nup);
|
||||
lua_pushcclosure(L, l->func, nup); /* closure with those upvalues */
|
||||
@@ -995,50 +997,76 @@ static void *l_alloc (void *ud, void *ptr, size_t osize, size_t nsize) {
|
||||
|
||||
|
||||
static int panic (lua_State *L) {
|
||||
const char *msg = lua_tostring(L, -1);
|
||||
if (msg == NULL) msg = "error object is not a string";
|
||||
lua_writestringerror("PANIC: unprotected error in call to Lua API (%s)\n",
|
||||
lua_tostring(L, -1));
|
||||
msg);
|
||||
return 0; /* return to Lua to abort */
|
||||
}
|
||||
|
||||
|
||||
/*
|
||||
** Emit a warning. '*warnstate' means:
|
||||
** 0 - warning system is off;
|
||||
** 1 - ready to start a new message;
|
||||
** 2 - previous message is to be continued.
|
||||
** Warning functions:
|
||||
** warnfoff: warning system is off
|
||||
** warnfon: ready to start a new message
|
||||
** warnfcont: previous message is to be continued
|
||||
*/
|
||||
static void warnf (void *ud, const char *message, int tocont) {
|
||||
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;
|
||||
static void warnfoff (void *ud, const char *message, int tocont);
|
||||
static void warnfon (void *ud, const char *message, int tocont);
|
||||
static void warnfcont (void *ud, const char *message, int tocont);
|
||||
|
||||
|
||||
/*
|
||||
** Check whether message is a control message. If so, execute the
|
||||
** control or ignore it if unknown.
|
||||
*/
|
||||
static int checkcontrol (lua_State *L, const char *message, int tocont) {
|
||||
if (tocont || *(message++) != '@') /* not a control message? */
|
||||
return 0;
|
||||
else {
|
||||
if (strcmp(message, "off") == 0)
|
||||
lua_setwarnf(L, warnfoff, L); /* turn warnings off */
|
||||
else if (strcmp(message, "on") == 0)
|
||||
lua_setwarnf(L, warnfon, L); /* turn warnings on */
|
||||
return 1; /* it was a control message */
|
||||
}
|
||||
else if (*warnstate == 0) /* warnings off? */
|
||||
return;
|
||||
if (*warnstate == 1) /* previous message was the last? */
|
||||
lua_writestringerror("%s", "Lua warning: "); /* start a new warning */
|
||||
}
|
||||
|
||||
|
||||
static void warnfoff (void *ud, const char *message, int tocont) {
|
||||
checkcontrol((lua_State *)ud, message, tocont);
|
||||
}
|
||||
|
||||
|
||||
/*
|
||||
** Writes the message and handle 'tocont', finishing the message
|
||||
** if needed and setting the next warn function.
|
||||
*/
|
||||
static void warnfcont (void *ud, const char *message, int tocont) {
|
||||
lua_State *L = (lua_State *)ud;
|
||||
lua_writestringerror("%s", message); /* write message */
|
||||
if (tocont) /* not the last part? */
|
||||
*warnstate = 2; /* to be continued */
|
||||
lua_setwarnf(L, warnfcont, L); /* to be continued */
|
||||
else { /* last part */
|
||||
lua_writestringerror("%s", "\n"); /* finish message with end-of-line */
|
||||
*warnstate = 1; /* ready to start a new message */
|
||||
lua_setwarnf(L, warnfon, L); /* next call is a new message */
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
static void warnfon (void *ud, const char *message, int tocont) {
|
||||
if (checkcontrol((lua_State *)ud, message, tocont)) /* control message? */
|
||||
return; /* nothing else to be done */
|
||||
lua_writestringerror("%s", "Lua warning: "); /* start a new warning */
|
||||
warnfcont(ud, message, tocont); /* finish processing */
|
||||
}
|
||||
|
||||
|
||||
LUALIB_API lua_State *luaL_newstate (void) {
|
||||
lua_State *L = lua_newstate(l_alloc, NULL);
|
||||
if (L) {
|
||||
int *warnstate; /* space for warning state */
|
||||
lua_atpanic(L, &panic);
|
||||
warnstate = (int *)lua_newuserdatauv(L, sizeof(int), 0);
|
||||
luaL_ref(L, LUA_REGISTRYINDEX); /* make sure it won't be collected */
|
||||
*warnstate = 0; /* default is warnings off */
|
||||
lua_setwarnf(L, warnf, warnstate);
|
||||
lua_setwarnf(L, warnfoff, L); /* default is warnings off */
|
||||
}
|
||||
return L;
|
||||
}
|
||||
|
||||
@@ -16,7 +16,7 @@
|
||||
|
||||
|
||||
/* global table */
|
||||
#define LUA_GNAME "_G"
|
||||
#define LUA_GNAME "_G"
|
||||
|
||||
|
||||
typedef struct luaL_Buffer luaL_Buffer;
|
||||
@@ -185,6 +185,8 @@ struct luaL_Buffer {
|
||||
|
||||
#define luaL_addsize(B,s) ((B)->n += (s))
|
||||
|
||||
#define luaL_buffsub(B,s) ((B)->n -= (s))
|
||||
|
||||
LUALIB_API void (luaL_buffinit) (lua_State *L, luaL_Buffer *B);
|
||||
LUALIB_API char *(luaL_prepbuffsize) (luaL_Buffer *B, size_t sz);
|
||||
LUALIB_API void (luaL_addlstring) (luaL_Buffer *B, const char *s, size_t l);
|
||||
|
||||
@@ -84,8 +84,11 @@ 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);
|
||||
case VFALSE:
|
||||
setbfvalue(v);
|
||||
return 1;
|
||||
case VTRUE:
|
||||
setbtvalue(v);
|
||||
return 1;
|
||||
case VNIL:
|
||||
setnilvalue(v);
|
||||
@@ -110,7 +113,7 @@ int luaK_exp2const (FuncState *fs, const expdesc *e, TValue *v) {
|
||||
** optimizations).
|
||||
*/
|
||||
static Instruction *previousinstruction (FuncState *fs) {
|
||||
static const Instruction invalidinstruction = -1;
|
||||
static const Instruction invalidinstruction = ~(Instruction)0;
|
||||
if (fs->pc > fs->lasttarget)
|
||||
return &fs->f->code[fs->pc - 1]; /* previous instruction */
|
||||
else
|
||||
@@ -604,11 +607,21 @@ static int luaK_numberK (FuncState *fs, lua_Number r) {
|
||||
|
||||
|
||||
/*
|
||||
** Add a boolean to list of constants and return its index.
|
||||
** Add a false to list of constants and return its index.
|
||||
*/
|
||||
static int boolK (FuncState *fs, int b) {
|
||||
static int boolF (FuncState *fs) {
|
||||
TValue o;
|
||||
setbvalue(&o, b);
|
||||
setbfvalue(&o);
|
||||
return addk(fs, &o, &o); /* use boolean itself as key */
|
||||
}
|
||||
|
||||
|
||||
/*
|
||||
** Add a true to list of constants and return its index.
|
||||
*/
|
||||
static int boolT (FuncState *fs) {
|
||||
TValue o;
|
||||
setbtvalue(&o);
|
||||
return addk(fs, &o, &o); /* use boolean itself as key */
|
||||
}
|
||||
|
||||
@@ -653,7 +666,7 @@ void luaK_int (FuncState *fs, int reg, lua_Integer i) {
|
||||
|
||||
static void luaK_float (FuncState *fs, int reg, lua_Number f) {
|
||||
lua_Integer fi;
|
||||
if (luaV_flttointeger(f, &fi, 0) && fitsBx(fi))
|
||||
if (luaV_flttointeger(f, &fi, F2Ieq) && fitsBx(fi))
|
||||
luaK_codeAsBx(fs, OP_LOADF, reg, cast_int(fi));
|
||||
else
|
||||
luaK_codek(fs, reg, luaK_numberK(fs, f));
|
||||
@@ -665,19 +678,22 @@ static void luaK_float (FuncState *fs, int reg, lua_Number f) {
|
||||
*/
|
||||
static void const2exp (TValue *v, expdesc *e) {
|
||||
switch (ttypetag(v)) {
|
||||
case LUA_TNUMINT:
|
||||
case LUA_VNUMINT:
|
||||
e->k = VKINT; e->u.ival = ivalue(v);
|
||||
break;
|
||||
case LUA_TNUMFLT:
|
||||
case LUA_VNUMFLT:
|
||||
e->k = VKFLT; e->u.nval = fltvalue(v);
|
||||
break;
|
||||
case LUA_TBOOLEAN:
|
||||
e->k = bvalue(v) ? VTRUE : VFALSE;
|
||||
case LUA_VFALSE:
|
||||
e->k = VFALSE;
|
||||
break;
|
||||
case LUA_TNIL:
|
||||
case LUA_VTRUE:
|
||||
e->k = VTRUE;
|
||||
break;
|
||||
case LUA_VNIL:
|
||||
e->k = VNIL;
|
||||
break;
|
||||
case LUA_TSHRSTR: case LUA_TLNGSTR:
|
||||
case LUA_VSHRSTR: case LUA_VLNGSTR:
|
||||
e->k = VKSTR; e->u.strval = tsvalue(v);
|
||||
break;
|
||||
default: lua_assert(0);
|
||||
@@ -687,19 +703,18 @@ static void const2exp (TValue *v, expdesc *e) {
|
||||
|
||||
/*
|
||||
** Fix an expression to return the number of results 'nresults'.
|
||||
** Either 'e' is a multi-ret expression (function call or vararg)
|
||||
** or 'nresults' is LUA_MULTRET (as any expression can satisfy that).
|
||||
** 'e' must be a multi-ret expression (function call or vararg).
|
||||
*/
|
||||
void luaK_setreturns (FuncState *fs, expdesc *e, int nresults) {
|
||||
Instruction *pc = &getinstruction(fs, e);
|
||||
if (e->k == VCALL) /* expression is an open function call? */
|
||||
SETARG_C(*pc, nresults + 1);
|
||||
else if (e->k == VVARARG) {
|
||||
else {
|
||||
lua_assert(e->k == VVARARG);
|
||||
SETARG_C(*pc, nresults + 1);
|
||||
SETARG_A(*pc, fs->freereg);
|
||||
luaK_reserveregs(fs, 1);
|
||||
}
|
||||
else lua_assert(nresults == LUA_MULTRET);
|
||||
}
|
||||
|
||||
|
||||
@@ -738,7 +753,7 @@ void luaK_setoneret (FuncState *fs, expdesc *e) {
|
||||
|
||||
|
||||
/*
|
||||
** Ensure that expression 'e' is not a variable (nor a constant).
|
||||
** Ensure that expression 'e' is not a variable (nor a <const>).
|
||||
** (Expression still may have jump lists.)
|
||||
*/
|
||||
void luaK_dischargevars (FuncState *fs, expdesc *e) {
|
||||
@@ -790,8 +805,8 @@ void luaK_dischargevars (FuncState *fs, expdesc *e) {
|
||||
|
||||
|
||||
/*
|
||||
** Ensures expression value is in register 'reg' (and therefore
|
||||
** 'e' will become a non-relocatable expression).
|
||||
** Ensure expression value is in register 'reg', making 'e' a
|
||||
** non-relocatable expression.
|
||||
** (Expression still may have jump lists.)
|
||||
*/
|
||||
static void discharge2reg (FuncState *fs, expdesc *e, int reg) {
|
||||
@@ -801,8 +816,12 @@ static void discharge2reg (FuncState *fs, expdesc *e, int reg) {
|
||||
luaK_nil(fs, reg, 1);
|
||||
break;
|
||||
}
|
||||
case VFALSE: case VTRUE: {
|
||||
luaK_codeABC(fs, OP_LOADBOOL, reg, e->k == VTRUE, 0);
|
||||
case VFALSE: {
|
||||
luaK_codeABC(fs, OP_LOADFALSE, reg, 0, 0);
|
||||
break;
|
||||
}
|
||||
case VTRUE: {
|
||||
luaK_codeABC(fs, OP_LOADTRUE, reg, 0, 0);
|
||||
break;
|
||||
}
|
||||
case VKSTR: {
|
||||
@@ -841,7 +860,8 @@ static void discharge2reg (FuncState *fs, expdesc *e, int reg) {
|
||||
|
||||
|
||||
/*
|
||||
** Ensures expression value is in any register.
|
||||
** Ensure expression value is in a register, making 'e' a
|
||||
** non-relocatable expression.
|
||||
** (Expression still may have jump lists.)
|
||||
*/
|
||||
static void discharge2anyreg (FuncState *fs, expdesc *e) {
|
||||
@@ -852,9 +872,9 @@ static void discharge2anyreg (FuncState *fs, expdesc *e) {
|
||||
}
|
||||
|
||||
|
||||
static int code_loadbool (FuncState *fs, int A, int b, int jump) {
|
||||
static int code_loadbool (FuncState *fs, int A, OpCode op) {
|
||||
luaK_getlabel(fs); /* those instructions may be jump targets */
|
||||
return luaK_codeABC(fs, OP_LOADBOOL, A, b, jump);
|
||||
return luaK_codeABC(fs, op, A, 0, 0);
|
||||
}
|
||||
|
||||
|
||||
@@ -888,8 +908,8 @@ static void exp2reg (FuncState *fs, expdesc *e, int reg) {
|
||||
int p_t = NO_JUMP; /* position of an eventual LOAD true */
|
||||
if (need_value(fs, e->t) || need_value(fs, e->f)) {
|
||||
int fj = (e->k == VJMP) ? NO_JUMP : luaK_jump(fs);
|
||||
p_f = code_loadbool(fs, reg, 0, 1); /* load false and skip next i. */
|
||||
p_t = code_loadbool(fs, reg, 1, 0); /* load true */
|
||||
p_f = code_loadbool(fs, reg, OP_LFALSESKIP); /* skip next inst. */
|
||||
p_t = code_loadbool(fs, reg, OP_LOADTRUE);
|
||||
/* jump around these booleans if 'e' is not a test */
|
||||
luaK_patchtohere(fs, fj);
|
||||
}
|
||||
@@ -927,8 +947,11 @@ int luaK_exp2anyreg (FuncState *fs, expdesc *e) {
|
||||
exp2reg(fs, e, e->u.info); /* put final result in it */
|
||||
return e->u.info;
|
||||
}
|
||||
/* else expression has jumps and cannot change its register
|
||||
to hold the jump values, because it is a local variable.
|
||||
Go through to the default case. */
|
||||
}
|
||||
luaK_exp2nextreg(fs, e); /* otherwise, use next available register */
|
||||
luaK_exp2nextreg(fs, e); /* default: use next available register */
|
||||
return e->u.info;
|
||||
}
|
||||
|
||||
@@ -963,8 +986,8 @@ static int luaK_exp2K (FuncState *fs, expdesc *e) {
|
||||
if (!hasjumps(e)) {
|
||||
int info;
|
||||
switch (e->k) { /* move constants to 'k' */
|
||||
case VTRUE: info = boolK(fs, 1); break;
|
||||
case VFALSE: info = boolK(fs, 0); break;
|
||||
case VTRUE: info = boolT(fs); break;
|
||||
case VFALSE: info = boolF(fs); break;
|
||||
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;
|
||||
@@ -1220,7 +1243,7 @@ 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 && luaV_flttointeger(e->u.nval, &i, 0))
|
||||
else if (e->k == VKFLT && luaV_flttointeger(e->u.nval, &i, F2Ieq))
|
||||
*isfloat = 1;
|
||||
else
|
||||
return 0; /* not a number */
|
||||
@@ -1509,7 +1532,7 @@ static void codeeq (FuncState *fs, BinOpr opr, expdesc *e1, expdesc *e2) {
|
||||
lua_assert(e1->k == VK || e1->k == VKINT || e1->k == VKFLT);
|
||||
swapexps(e1, e2);
|
||||
}
|
||||
r1 = luaK_exp2anyreg(fs, e1); /* 1nd expression must be in register */
|
||||
r1 = luaK_exp2anyreg(fs, e1); /* 1st expression must be in register */
|
||||
if (isSCnumber(e2, &im, &isfloat)) {
|
||||
op = OP_EQI;
|
||||
r2 = im; /* immediate operand */
|
||||
@@ -1710,17 +1733,12 @@ void luaK_fixline (FuncState *fs, int line) {
|
||||
}
|
||||
|
||||
|
||||
void luaK_settablesize (FuncState *fs, int pc, int ra, int rc, int rb) {
|
||||
void luaK_settablesize (FuncState *fs, int pc, int ra, int asize, int hsize) {
|
||||
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;
|
||||
}
|
||||
int rb = (hsize != 0) ? luaO_ceillog2(hsize) + 1 : 0; /* hash size */
|
||||
int extra = asize / (MAXARG_C + 1); /* higher bits of array size */
|
||||
int rc = asize % (MAXARG_C + 1); /* lower bits of array size */
|
||||
int k = (extra > 0); /* true iff needs extra argument */
|
||||
*inst = CREATE_ABCk(OP_NEWTABLE, ra, rb, rc, k);
|
||||
*(inst + 1) = CREATE_Ax(OP_EXTRAARG, extra);
|
||||
}
|
||||
|
||||
@@ -95,7 +95,7 @@ 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);
|
||||
int ra, int asize, int hsize);
|
||||
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);
|
||||
|
||||
+5
-4
@@ -73,11 +73,12 @@ static int luaB_coresume (lua_State *L) {
|
||||
static int luaB_auxwrap (lua_State *L) {
|
||||
lua_State *co = lua_tothread(L, lua_upvalueindex(1));
|
||||
int r = auxresume(L, co, lua_gettop(L));
|
||||
if (r < 0) {
|
||||
if (r < 0) { /* error? */
|
||||
int stat = lua_status(co);
|
||||
if (stat != LUA_OK && stat != LUA_YIELD)
|
||||
lua_resetthread(co); /* close variables in case of errors */
|
||||
if (lua_type(L, -1) == LUA_TSTRING) { /* error object is a string? */
|
||||
if (stat != LUA_OK && stat != LUA_YIELD) /* error in the coroutine? */
|
||||
lua_resetthread(co); /* close its tbc variables */
|
||||
if (stat != LUA_ERRMEM && /* not a memory error and ... */
|
||||
lua_type(L, -1) == LUA_TSTRING) { /* ... error object is a string? */
|
||||
luaL_where(L, 1); /* add extra info, if available */
|
||||
lua_insert(L, -2);
|
||||
lua_concat(L, 2);
|
||||
|
||||
@@ -16,6 +16,15 @@
|
||||
|
||||
#include <limits.h>
|
||||
|
||||
|
||||
#if defined (LUA_UCID) /* accept UniCode IDentifiers? */
|
||||
/* consider all non-ascii codepoints to be alphabetic */
|
||||
#define NONA 0x01
|
||||
#else
|
||||
#define NONA 0x00 /* default */
|
||||
#endif
|
||||
|
||||
|
||||
LUAI_DDEF const lu_byte luai_ctype_[UCHAR_MAX + 2] = {
|
||||
0x00, /* EOZ */
|
||||
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0. */
|
||||
@@ -34,22 +43,22 @@ LUAI_DDEF const lu_byte luai_ctype_[UCHAR_MAX + 2] = {
|
||||
0x05, 0x05, 0x05, 0x05, 0x05, 0x05, 0x05, 0x05,
|
||||
0x05, 0x05, 0x05, 0x05, 0x05, 0x05, 0x05, 0x05, /* 7. */
|
||||
0x05, 0x05, 0x05, 0x04, 0x04, 0x04, 0x04, 0x00,
|
||||
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 8. */
|
||||
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
|
||||
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 9. */
|
||||
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
|
||||
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* a. */
|
||||
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
|
||||
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* b. */
|
||||
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
|
||||
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* c. */
|
||||
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
|
||||
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* d. */
|
||||
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
|
||||
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* e. */
|
||||
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
|
||||
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* f. */
|
||||
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
|
||||
NONA, NONA, NONA, NONA, NONA, NONA, NONA, NONA, /* 8. */
|
||||
NONA, NONA, NONA, NONA, NONA, NONA, NONA, NONA,
|
||||
NONA, NONA, NONA, NONA, NONA, NONA, NONA, NONA, /* 9. */
|
||||
NONA, NONA, NONA, NONA, NONA, NONA, NONA, NONA,
|
||||
NONA, NONA, NONA, NONA, NONA, NONA, NONA, NONA, /* a. */
|
||||
NONA, NONA, NONA, NONA, NONA, NONA, NONA, NONA,
|
||||
NONA, NONA, NONA, NONA, NONA, NONA, NONA, NONA, /* b. */
|
||||
NONA, NONA, NONA, NONA, NONA, NONA, NONA, NONA,
|
||||
0x00, 0x00, NONA, NONA, NONA, NONA, NONA, NONA, /* c. */
|
||||
NONA, NONA, NONA, NONA, NONA, NONA, NONA, NONA,
|
||||
NONA, NONA, NONA, NONA, NONA, NONA, NONA, NONA, /* d. */
|
||||
NONA, NONA, NONA, NONA, NONA, NONA, NONA, NONA,
|
||||
NONA, NONA, NONA, NONA, NONA, NONA, NONA, NONA, /* e. */
|
||||
NONA, NONA, NONA, NONA, NONA, NONA, NONA, NONA,
|
||||
NONA, NONA, NONA, NONA, NONA, 0x00, 0x00, 0x00, /* f. */
|
||||
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00
|
||||
};
|
||||
|
||||
#endif /* } */
|
||||
|
||||
@@ -13,7 +13,7 @@
|
||||
/*
|
||||
** WARNING: the functions defined here do not necessarily correspond
|
||||
** to the similar functions in the standard C ctype.h. They are
|
||||
** optimized for the specific needs of Lua
|
||||
** optimized for the specific needs of Lua.
|
||||
*/
|
||||
|
||||
#if !defined(LUA_USE_CTYPE)
|
||||
@@ -61,13 +61,19 @@
|
||||
#define lisprint(c) testprop(c, MASK(PRINTBIT))
|
||||
#define lisxdigit(c) testprop(c, MASK(XDIGITBIT))
|
||||
|
||||
|
||||
/*
|
||||
** this 'ltolower' only works for alphabetic characters
|
||||
** In ASCII, this 'ltolower' is correct for alphabetic characters and
|
||||
** for '.'. That is enough for Lua needs. ('check_exp' ensures that
|
||||
** the character either is an upper-case letter or is unchanged by
|
||||
** the transformation, which holds for lower-case letters and '.'.)
|
||||
*/
|
||||
#define ltolower(c) ((c) | ('A' ^ 'a'))
|
||||
#define ltolower(c) \
|
||||
check_exp(('A' <= (c) && (c) <= 'Z') || (c) == ((c) | ('A' ^ 'a')), \
|
||||
(c) | ('A' ^ 'a'))
|
||||
|
||||
|
||||
/* two more entries for 0 and -1 (EOZ) */
|
||||
/* one entry for each character and for -1 (EOZ) */
|
||||
LUAI_DDEC(const lu_byte luai_ctype_[UCHAR_MAX + 2];)
|
||||
|
||||
|
||||
|
||||
@@ -202,8 +202,6 @@ static int db_getinfo (lua_State *L) {
|
||||
static int db_getlocal (lua_State *L) {
|
||||
int arg;
|
||||
lua_State *L1 = getthread(L, &arg);
|
||||
lua_Debug ar;
|
||||
const char *name;
|
||||
int nvar = (int)luaL_checkinteger(L, arg + 2); /* local-variable index */
|
||||
if (lua_isfunction(L, arg + 1)) { /* function argument? */
|
||||
lua_pushvalue(L, arg + 1); /* push function */
|
||||
@@ -211,6 +209,8 @@ static int db_getlocal (lua_State *L) {
|
||||
return 1; /* return only name (there is no value) */
|
||||
}
|
||||
else { /* stack-level argument */
|
||||
lua_Debug ar;
|
||||
const char *name;
|
||||
int level = (int)luaL_checkinteger(L, arg + 1);
|
||||
if (!lua_getstack(L1, level, &ar)) /* out of range? */
|
||||
return luaL_argerror(L, arg+1, "level out of range");
|
||||
@@ -281,25 +281,33 @@ static int db_setupvalue (lua_State *L) {
|
||||
** Check whether a given upvalue from a given closure exists and
|
||||
** returns its index
|
||||
*/
|
||||
static int checkupval (lua_State *L, int argf, int argnup) {
|
||||
static void *checkupval (lua_State *L, int argf, int argnup, int *pnup) {
|
||||
void *id;
|
||||
int nup = (int)luaL_checkinteger(L, argnup); /* upvalue index */
|
||||
luaL_checktype(L, argf, LUA_TFUNCTION); /* closure */
|
||||
luaL_argcheck(L, (lua_getupvalue(L, argf, nup) != NULL), argnup,
|
||||
"invalid upvalue index");
|
||||
return nup;
|
||||
id = lua_upvalueid(L, argf, nup);
|
||||
if (pnup) {
|
||||
luaL_argcheck(L, id != NULL, argnup, "invalid upvalue index");
|
||||
*pnup = nup;
|
||||
}
|
||||
return id;
|
||||
}
|
||||
|
||||
|
||||
static int db_upvalueid (lua_State *L) {
|
||||
int n = checkupval(L, 1, 2);
|
||||
lua_pushlightuserdata(L, lua_upvalueid(L, 1, n));
|
||||
void *id = checkupval(L, 1, 2, NULL);
|
||||
if (id != NULL)
|
||||
lua_pushlightuserdata(L, id);
|
||||
else
|
||||
luaL_pushfail(L);
|
||||
return 1;
|
||||
}
|
||||
|
||||
|
||||
static int db_upvaluejoin (lua_State *L) {
|
||||
int n1 = checkupval(L, 1, 2);
|
||||
int n2 = checkupval(L, 3, 4);
|
||||
int n1, n2;
|
||||
checkupval(L, 1, 2, &n1);
|
||||
checkupval(L, 3, 4, &n2);
|
||||
luaL_argcheck(L, !lua_iscfunction(L, 1), 1, "Lua function expected");
|
||||
luaL_argcheck(L, !lua_iscfunction(L, 3), 3, "Lua function expected");
|
||||
lua_upvaluejoin(L, 1, n1, 3, n2);
|
||||
@@ -417,7 +425,7 @@ static int db_debug (lua_State *L) {
|
||||
return 0;
|
||||
if (luaL_loadbuffer(L, buffer, strlen(buffer), "=(debug command)") ||
|
||||
lua_pcall(L, 0, 0, 0))
|
||||
lua_writestringerror("%s\n", lua_tostring(L, -1));
|
||||
lua_writestringerror("%s\n", luaL_tolstring(L, -1, NULL));
|
||||
lua_settop(L, 0); /* remove eventual returns */
|
||||
}
|
||||
}
|
||||
@@ -440,10 +448,7 @@ 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);
|
||||
lua_pushinteger(L, res);
|
||||
return 1;
|
||||
}
|
||||
|
||||
|
||||
@@ -31,12 +31,10 @@
|
||||
|
||||
|
||||
|
||||
#define noLuaClosure(f) ((f) == NULL || (f)->c.tt == LUA_TCCL)
|
||||
|
||||
|
||||
/* Active Lua function (given call info) */
|
||||
#define ci_func(ci) (clLvalue(s2v((ci)->func)))
|
||||
#define noLuaClosure(f) ((f) == NULL || (f)->c.tt == LUA_VCCL)
|
||||
|
||||
/* inverse of 'pcRel' */
|
||||
#define invpcRel(pc, p) ((p)->code + (pc) + 1)
|
||||
|
||||
static const char *funcnamefromcode (lua_State *L, CallInfo *ci,
|
||||
const char **name);
|
||||
@@ -101,19 +99,21 @@ int luaG_getfuncline (const Proto *f, int pc) {
|
||||
}
|
||||
|
||||
|
||||
static int currentline (CallInfo *ci) {
|
||||
static int getcurrentline (CallInfo *ci) {
|
||||
return luaG_getfuncline(ci_func(ci)->p, currentpc(ci));
|
||||
}
|
||||
|
||||
|
||||
/*
|
||||
** This function can be called asynchronously (e.g. during a signal),
|
||||
** under "reasonable" assumptions. A new 'ci' is completely linked
|
||||
** in the list before it becomes part of the "active" list, and
|
||||
** we assume that pointers are atomic (see comment in next function).
|
||||
** (If we traverse one more item, there is no problem. If we traverse
|
||||
** one less item, the worst that can happen is that the signal will
|
||||
** not interrupt the script.)
|
||||
** Set 'trap' for all active Lua frames.
|
||||
** This function can be called during a signal, under "reasonable"
|
||||
** assumptions. A new 'ci' is completely linked in the list before it
|
||||
** becomes part of the "active" list, and we assume that pointers are
|
||||
** atomic; see comment in next function.
|
||||
** (A compiler doing interprocedural optimizations could, theoretically,
|
||||
** reorder memory writes in such a way that the list could be
|
||||
** temporarily broken while inserting a new element. We simply assume it
|
||||
** has no good reasons to do that.)
|
||||
*/
|
||||
static void settraps (CallInfo *ci) {
|
||||
for (; ci != NULL; ci = ci->previous)
|
||||
@@ -123,22 +123,20 @@ static void settraps (CallInfo *ci) {
|
||||
|
||||
|
||||
/*
|
||||
** This function can be called asynchronously (e.g. during a signal),
|
||||
** under "reasonable" assumptions.
|
||||
** Fields 'oldpc', 'basehookcount', and 'hookcount' (set by
|
||||
** 'resethookcount') are for debug only, and it is no problem if they
|
||||
** get arbitrary values (causes at most one wrong hook call). 'hookmask'
|
||||
** is an atomic value. We assume that pointers are atomic too (e.g., gcc
|
||||
** ensures that for all platforms where it runs). Moreover, 'hook' is
|
||||
** always checked before being called (see 'luaD_hook').
|
||||
** This function can be called during a signal, under "reasonable"
|
||||
** assumptions.
|
||||
** Fields 'basehookcount' and 'hookcount' (set by 'resethookcount')
|
||||
** are for debug only, and it is no problem if they get arbitrary
|
||||
** values (causes at most one wrong hook call). 'hookmask' is an atomic
|
||||
** value. We assume that pointers are atomic too (e.g., gcc ensures that
|
||||
** for all platforms where it runs). Moreover, 'hook' is always checked
|
||||
** before being called (see 'luaD_hook').
|
||||
*/
|
||||
LUA_API void lua_sethook (lua_State *L, lua_Hook func, int mask, int count) {
|
||||
if (func == NULL || mask == 0) { /* turn off hooks? */
|
||||
mask = 0;
|
||||
func = NULL;
|
||||
}
|
||||
if (isLua(L->ci))
|
||||
L->oldpc = L->ci->u.l.savedpc;
|
||||
L->hook = func;
|
||||
L->basehookcount = count;
|
||||
resethookcount(L);
|
||||
@@ -190,8 +188,8 @@ static const char *upvalname (const Proto *p, int uv) {
|
||||
static const char *findvararg (CallInfo *ci, int n, StkId *pos) {
|
||||
if (clLvalue(s2v(ci->func))->p->is_vararg) {
|
||||
int nextra = ci->u.l.nextraargs;
|
||||
if (n <= nextra) {
|
||||
*pos = ci->func - nextra + (n - 1);
|
||||
if (n >= -nextra) { /* 'n' is negative */
|
||||
*pos = ci->func - nextra - (n + 1);
|
||||
return "(vararg)"; /* generic name for any vararg */
|
||||
}
|
||||
}
|
||||
@@ -204,7 +202,7 @@ const char *luaG_findlocal (lua_State *L, CallInfo *ci, int n, StkId *pos) {
|
||||
const char *name = NULL;
|
||||
if (isLua(ci)) {
|
||||
if (n < 0) /* access to vararg values? */
|
||||
return findvararg(ci, -n, pos);
|
||||
return findvararg(ci, n, pos);
|
||||
else
|
||||
name = luaF_getlocalname(ci_func(ci)->p, n, currentpc(ci));
|
||||
}
|
||||
@@ -306,7 +304,7 @@ static void collectvalidlines (lua_State *L, Closure *f) {
|
||||
Table *t = luaH_new(L); /* new table to store active lines */
|
||||
sethvalue2s(L, L->top, t); /* push it on stack */
|
||||
api_incr_top(L);
|
||||
setbvalue(&v, 1); /* boolean 'true' to be the value of all indices */
|
||||
setbtvalue(&v); /* boolean 'true' to be the value of all indices */
|
||||
for (i = 0; i < p->sizelineinfo; i++) { /* for all lines with code */
|
||||
currentline = nextline(p, currentline, i);
|
||||
luaH_setint(L, t, currentline, &v); /* table[line] = true */
|
||||
@@ -339,7 +337,7 @@ static int auxgetinfo (lua_State *L, const char *what, lua_Debug *ar,
|
||||
break;
|
||||
}
|
||||
case 'l': {
|
||||
ar->currentline = (ci && isLua(ci)) ? currentline(ci) : -1;
|
||||
ar->currentline = (ci && isLua(ci)) ? getcurrentline(ci) : -1;
|
||||
break;
|
||||
}
|
||||
case 'u': {
|
||||
@@ -775,7 +773,7 @@ l_noret luaG_runerror (lua_State *L, const char *fmt, ...) {
|
||||
msg = luaO_pushvfstring(L, fmt, argp); /* format message */
|
||||
va_end(argp);
|
||||
if (isLua(ci)) /* if Lua function, add source:line information */
|
||||
luaG_addinfo(L, msg, ci_func(ci)->p->source, currentline(ci));
|
||||
luaG_addinfo(L, msg, ci_func(ci)->p->source, getcurrentline(ci));
|
||||
luaG_errormsg(L);
|
||||
}
|
||||
|
||||
@@ -785,18 +783,34 @@ l_noret luaG_runerror (lua_State *L, const char *fmt, ...) {
|
||||
** previous instruction 'oldpc'.
|
||||
*/
|
||||
static int changedline (const Proto *p, int oldpc, int newpc) {
|
||||
if (p->lineinfo == NULL) /* no debug information? */
|
||||
return 0;
|
||||
while (oldpc++ < newpc) {
|
||||
if (p->lineinfo[oldpc] != 0)
|
||||
return (luaG_getfuncline(p, oldpc - 1) != luaG_getfuncline(p, newpc));
|
||||
}
|
||||
return 0; /* no line changes in the way */
|
||||
return 0; /* no line changes between positions */
|
||||
}
|
||||
|
||||
|
||||
/*
|
||||
** Traces the execution of a Lua function. Called before the execution
|
||||
** of each opcode, when debug is on. 'L->oldpc' stores the last
|
||||
** instruction traced, to detect line changes. When entering a new
|
||||
** function, 'npci' will be zero and will test as a new line without
|
||||
** the need for 'oldpc'; so, 'oldpc' does not need to be initialized
|
||||
** before. Some exceptional conditions may return to a function without
|
||||
** updating 'oldpc'. In that case, 'oldpc' may be invalid; if so, it is
|
||||
** reset to zero. (A wrong but valid 'oldpc' at most causes an extra
|
||||
** call to a line hook.)
|
||||
*/
|
||||
int luaG_traceexec (lua_State *L, const Instruction *pc) {
|
||||
CallInfo *ci = L->ci;
|
||||
lu_byte mask = L->hookmask;
|
||||
const Proto *p = ci_func(ci)->p;
|
||||
int counthook;
|
||||
/* 'L->oldpc' may be invalid; reset it in this case */
|
||||
int oldpc = (L->oldpc < p->sizecode) ? L->oldpc : 0;
|
||||
if (!(mask & (LUA_MASKLINE | LUA_MASKCOUNT))) { /* no hooks? */
|
||||
ci->u.l.trap = 0; /* don't need to stop again */
|
||||
return 0; /* turn off 'trap' */
|
||||
@@ -817,15 +831,14 @@ int luaG_traceexec (lua_State *L, const Instruction *pc) {
|
||||
if (counthook)
|
||||
luaD_hook(L, LUA_HOOKCOUNT, -1, 0, 0); /* call count hook */
|
||||
if (mask & LUA_MASKLINE) {
|
||||
const Proto *p = ci_func(ci)->p;
|
||||
int npci = pcRel(pc, p);
|
||||
if (npci == 0 || /* call linehook when enter a new function, */
|
||||
pc <= L->oldpc || /* when jump back (loop), or when */
|
||||
changedline(p, pcRel(L->oldpc, p), npci)) { /* enter new line */
|
||||
pc <= invpcRel(oldpc, p) || /* when jump back (loop), or when */
|
||||
changedline(p, oldpc, npci)) { /* enter new line */
|
||||
int newline = luaG_getfuncline(p, npci);
|
||||
luaD_hook(L, LUA_HOOKLINE, newline, 0, 0); /* call line hook */
|
||||
}
|
||||
L->oldpc = pc; /* 'pc' of last call to line hook */
|
||||
L->oldpc = npci; /* 'pc' of last call to line hook */
|
||||
}
|
||||
if (L->status == LUA_YIELD) { /* did hook yield? */
|
||||
if (counthook)
|
||||
|
||||
@@ -13,6 +13,11 @@
|
||||
|
||||
#define pcRel(pc, p) (cast_int((pc) - (p)->code) - 1)
|
||||
|
||||
|
||||
/* Active Lua function (given call info) */
|
||||
#define ci_func(ci) (clLvalue(s2v((ci)->func)))
|
||||
|
||||
|
||||
#define resethookcount(L) (L->hookcount = L->basehookcount)
|
||||
|
||||
/*
|
||||
|
||||
@@ -139,8 +139,7 @@ l_noret luaD_throw (lua_State *L, int errcode) {
|
||||
|
||||
|
||||
int luaD_rawrunprotected (lua_State *L, Pfunc f, void *ud) {
|
||||
global_State *g = G(L);
|
||||
l_uint32 oldnCcalls = g->Cstacklimit - (L->nCcalls + L->nci);
|
||||
l_uint32 oldnCcalls = L->nCcalls;
|
||||
struct lua_longjmp lj;
|
||||
lj.status = LUA_OK;
|
||||
lj.previous = L->errorJmp; /* chain new error handler */
|
||||
@@ -149,7 +148,7 @@ int luaD_rawrunprotected (lua_State *L, Pfunc f, void *ud) {
|
||||
(*f)(L, ud);
|
||||
);
|
||||
L->errorJmp = lj.previous; /* restore old error handler */
|
||||
L->nCcalls = g->Cstacklimit - oldnCcalls - L->nci;
|
||||
L->nCcalls = oldnCcalls;
|
||||
return lj.status;
|
||||
}
|
||||
|
||||
@@ -183,21 +182,20 @@ static void correctstack (lua_State *L, StkId oldstack, StkId newstack) {
|
||||
|
||||
|
||||
int luaD_reallocstack (lua_State *L, int newsize, int raiseerror) {
|
||||
int lim = L->stacksize;
|
||||
StkId newstack = luaM_reallocvector(L, L->stack, lim, newsize, StackValue);
|
||||
int lim = stacksize(L);
|
||||
StkId newstack = luaM_reallocvector(L, L->stack,
|
||||
lim + EXTRA_STACK, newsize + EXTRA_STACK, StackValue);
|
||||
lua_assert(newsize <= LUAI_MAXSTACK || newsize == ERRORSTACKSIZE);
|
||||
lua_assert(L->stack_last - L->stack == L->stacksize - EXTRA_STACK);
|
||||
if (unlikely(newstack == NULL)) { /* reallocation failed? */
|
||||
if (raiseerror)
|
||||
luaM_error(L);
|
||||
else return 0; /* do not raise an error */
|
||||
}
|
||||
for (; lim < newsize; lim++)
|
||||
setnilvalue(s2v(newstack + lim)); /* erase new segment */
|
||||
setnilvalue(s2v(newstack + lim + EXTRA_STACK)); /* erase new segment */
|
||||
correctstack(L, L->stack, newstack);
|
||||
L->stack = newstack;
|
||||
L->stacksize = newsize;
|
||||
L->stack_last = L->stack + newsize - EXTRA_STACK;
|
||||
L->stack_last = L->stack + newsize;
|
||||
return 1;
|
||||
}
|
||||
|
||||
@@ -207,52 +205,73 @@ int luaD_reallocstack (lua_State *L, int newsize, int raiseerror) {
|
||||
** is true, raises any error; otherwise, return 0 in case of errors.
|
||||
*/
|
||||
int luaD_growstack (lua_State *L, int n, int raiseerror) {
|
||||
int size = L->stacksize;
|
||||
int newsize = 2 * size; /* tentative new size */
|
||||
if (unlikely(size > LUAI_MAXSTACK)) { /* need more space after extra size? */
|
||||
int size = stacksize(L);
|
||||
if (unlikely(size > LUAI_MAXSTACK)) {
|
||||
/* if stack is larger than maximum, thread is already using the
|
||||
extra space reserved for errors, that is, thread is handling
|
||||
a stack error; cannot grow further than that. */
|
||||
lua_assert(stacksize(L) == ERRORSTACKSIZE);
|
||||
if (raiseerror)
|
||||
luaD_throw(L, LUA_ERRERR); /* error inside message handler */
|
||||
else return 0;
|
||||
return 0; /* if not 'raiseerror', just signal it */
|
||||
}
|
||||
else {
|
||||
int needed = cast_int(L->top - L->stack) + n + EXTRA_STACK;
|
||||
int newsize = 2 * size; /* tentative new size */
|
||||
int needed = cast_int(L->top - L->stack) + n;
|
||||
if (newsize > LUAI_MAXSTACK) /* cannot cross the limit */
|
||||
newsize = LUAI_MAXSTACK;
|
||||
if (newsize < needed) /* but must respect what was asked for */
|
||||
newsize = needed;
|
||||
if (unlikely(newsize > LUAI_MAXSTACK)) { /* stack overflow? */
|
||||
if (likely(newsize <= LUAI_MAXSTACK))
|
||||
return luaD_reallocstack(L, newsize, raiseerror);
|
||||
else { /* stack overflow */
|
||||
/* add extra size to be able to handle the error message */
|
||||
luaD_reallocstack(L, ERRORSTACKSIZE, raiseerror);
|
||||
if (raiseerror)
|
||||
luaG_runerror(L, "stack overflow");
|
||||
else return 0;
|
||||
return 0;
|
||||
}
|
||||
} /* else no errors */
|
||||
return luaD_reallocstack(L, newsize, raiseerror);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
static int stackinuse (lua_State *L) {
|
||||
CallInfo *ci;
|
||||
int res;
|
||||
StkId lim = L->top;
|
||||
for (ci = L->ci; ci != NULL; ci = ci->previous) {
|
||||
if (lim < ci->top) lim = ci->top;
|
||||
}
|
||||
lua_assert(lim <= L->stack_last);
|
||||
return cast_int(lim - L->stack) + 1; /* part of stack in use */
|
||||
res = cast_int(lim - L->stack) + 1; /* part of stack in use */
|
||||
if (res < LUA_MINSTACK)
|
||||
res = LUA_MINSTACK; /* ensure a minimum size */
|
||||
return res;
|
||||
}
|
||||
|
||||
|
||||
/*
|
||||
** If stack size is more than 3 times the current use, reduce that size
|
||||
** to twice the current use. (So, the final stack size is at most 2/3 the
|
||||
** previous size, and half of its entries are empty.)
|
||||
** As a particular case, if stack was handling a stack overflow and now
|
||||
** it is not, 'max' (limited by LUAI_MAXSTACK) will be smaller than
|
||||
** stacksize (equal to ERRORSTACKSIZE in this case), and so the stack
|
||||
** will be reduced to a "regular" size.
|
||||
*/
|
||||
void luaD_shrinkstack (lua_State *L) {
|
||||
int inuse = stackinuse(L);
|
||||
int goodsize = inuse + (inuse / 8) + 2*EXTRA_STACK;
|
||||
if (goodsize > LUAI_MAXSTACK)
|
||||
goodsize = LUAI_MAXSTACK; /* respect stack limit */
|
||||
int nsize = inuse * 2; /* proposed new size */
|
||||
int max = inuse * 3; /* maximum "reasonable" size */
|
||||
if (max > LUAI_MAXSTACK) {
|
||||
max = LUAI_MAXSTACK; /* respect stack limit */
|
||||
if (nsize > LUAI_MAXSTACK)
|
||||
nsize = LUAI_MAXSTACK;
|
||||
}
|
||||
/* if thread is currently not handling a stack overflow and its
|
||||
good size is smaller than current size, shrink its stack */
|
||||
if (inuse <= (LUAI_MAXSTACK - EXTRA_STACK) &&
|
||||
goodsize < L->stacksize)
|
||||
luaD_reallocstack(L, goodsize, 0); /* ok if that fails */
|
||||
size is larger than maximum "reasonable" size, shrink it */
|
||||
if (inuse <= LUAI_MAXSTACK && stacksize(L) > max)
|
||||
luaD_reallocstack(L, nsize, 0); /* ok if that fails */
|
||||
else /* don't change stack */
|
||||
condmovestack(L,{},{}); /* (change only for debugging) */
|
||||
luaE_shrinkCI(L); /* shrink CI list */
|
||||
@@ -328,7 +347,7 @@ static StkId rethook (lua_State *L, CallInfo *ci, StkId firstres, int nres) {
|
||||
ptrdiff_t oldtop = savestack(L, L->top); /* hook may change top */
|
||||
int delta = 0;
|
||||
if (isLuacode(ci)) {
|
||||
Proto *p = clLvalue(s2v(ci->func))->p;
|
||||
Proto *p = ci_func(ci)->p;
|
||||
if (p->is_vararg)
|
||||
delta = ci->u.l.nextraargs + p->numparams + 1;
|
||||
if (L->top < ci->top)
|
||||
@@ -341,15 +360,15 @@ static StkId rethook (lua_State *L, CallInfo *ci, StkId firstres, int nres) {
|
||||
luaD_hook(L, LUA_HOOKRET, -1, ftransfer, nres); /* call it */
|
||||
ci->func -= delta;
|
||||
}
|
||||
if (isLua(ci->previous))
|
||||
L->oldpc = ci->previous->u.l.savedpc; /* update 'oldpc' */
|
||||
if (isLua(ci = ci->previous))
|
||||
L->oldpc = pcRel(ci->u.l.savedpc, ci_func(ci)->p); /* update 'oldpc' */
|
||||
return restorestack(L, oldtop);
|
||||
}
|
||||
|
||||
|
||||
/*
|
||||
** Check whether 'func' has a '__call' metafield. If so, put it in the
|
||||
** stack, below original 'func', so that 'luaD_call' can call it. Raise
|
||||
** stack, below original 'func', so that 'luaD_precall' can call it. Raise
|
||||
** an error if there is no '__call' metafield.
|
||||
*/
|
||||
void luaD_tryfuncTM (lua_State *L, StkId func) {
|
||||
@@ -422,7 +441,7 @@ void luaD_poscall (lua_State *L, CallInfo *ci, int nres) {
|
||||
|
||||
|
||||
|
||||
#define next_ci(L) (L->ci = (L->ci->next ? L->ci->next : luaE_extendCI(L)))
|
||||
#define next_ci(L) (L->ci->next ? L->ci->next : luaE_extendCI(L))
|
||||
|
||||
|
||||
/*
|
||||
@@ -450,25 +469,27 @@ void luaD_pretailcall (lua_State *L, CallInfo *ci, StkId func, int narg1) {
|
||||
|
||||
|
||||
/*
|
||||
** Call a function (C or Lua). The function to be called is at *func.
|
||||
** The arguments are on the stack, right after the function.
|
||||
** When returns, all the results are on the stack, starting at the original
|
||||
** function position.
|
||||
** Prepares the call to a function (C or Lua). For C functions, also do
|
||||
** the call. The function to be called is at '*func'. The arguments
|
||||
** are on the stack, right after the function. Returns the CallInfo
|
||||
** to be executed, if it was a Lua function. Otherwise (a C function)
|
||||
** returns NULL, with all the results on the stack, starting at the
|
||||
** original function position.
|
||||
*/
|
||||
void luaD_call (lua_State *L, StkId func, int nresults) {
|
||||
CallInfo *luaD_precall (lua_State *L, StkId func, int nresults) {
|
||||
lua_CFunction f;
|
||||
retry:
|
||||
switch (ttypetag(s2v(func))) {
|
||||
case LUA_TCCL: /* C closure */
|
||||
case LUA_VCCL: /* C closure */
|
||||
f = clCvalue(s2v(func))->f;
|
||||
goto Cfunc;
|
||||
case LUA_TLCF: /* light C function */
|
||||
case LUA_VLCF: /* light C function */
|
||||
f = fvalue(s2v(func));
|
||||
Cfunc: {
|
||||
int n; /* number of returns */
|
||||
CallInfo *ci;
|
||||
checkstackp(L, LUA_MINSTACK, func); /* ensure minimum stack size */
|
||||
ci = next_ci(L);
|
||||
checkstackGCp(L, LUA_MINSTACK, func); /* ensure minimum stack size */
|
||||
L->ci = ci = next_ci(L);
|
||||
ci->nresults = nresults;
|
||||
ci->callstatus = CIST_C;
|
||||
ci->top = L->top + LUA_MINSTACK;
|
||||
@@ -483,29 +504,28 @@ void luaD_call (lua_State *L, StkId func, int nresults) {
|
||||
lua_lock(L);
|
||||
api_checknelems(L, n);
|
||||
luaD_poscall(L, ci, n);
|
||||
break;
|
||||
return NULL;
|
||||
}
|
||||
case LUA_TLCL: { /* Lua function */
|
||||
case LUA_VLCL: { /* Lua function */
|
||||
CallInfo *ci;
|
||||
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 */
|
||||
checkstackp(L, fsize, func);
|
||||
ci = next_ci(L);
|
||||
checkstackGCp(L, fsize, func);
|
||||
L->ci = ci = next_ci(L);
|
||||
ci->nresults = nresults;
|
||||
ci->u.l.savedpc = p->code; /* starting point */
|
||||
ci->callstatus = 0;
|
||||
ci->top = func + 1 + fsize;
|
||||
ci->func = func;
|
||||
L->ci = ci;
|
||||
for (; narg < nfixparams; narg++)
|
||||
setnilvalue(s2v(L->top++)); /* complete missing arguments */
|
||||
lua_assert(ci->top <= L->stack_last);
|
||||
luaV_execute(L, ci); /* run the function */
|
||||
break;
|
||||
return ci;
|
||||
}
|
||||
default: { /* not a function */
|
||||
checkstackp(L, 1, func); /* space for metamethod */
|
||||
checkstackGCp(L, 1, func); /* space for metamethod */
|
||||
luaD_tryfuncTM(L, func); /* try to get '__call' metamethod */
|
||||
goto retry; /* try again with metamethod */
|
||||
}
|
||||
@@ -513,18 +533,37 @@ void luaD_call (lua_State *L, StkId func, int nresults) {
|
||||
}
|
||||
|
||||
|
||||
/*
|
||||
** Call a function (C or Lua) through C. 'inc' can be 1 (increment
|
||||
** number of recursive invocations in the C stack) or nyci (the same
|
||||
** plus increment number of non-yieldable calls).
|
||||
*/
|
||||
static void ccall (lua_State *L, StkId func, int nResults, int inc) {
|
||||
CallInfo *ci;
|
||||
L->nCcalls += inc;
|
||||
if (unlikely(getCcalls(L) >= LUAI_MAXCCALLS))
|
||||
luaE_checkcstack(L);
|
||||
if ((ci = luaD_precall(L, func, nResults)) != NULL) { /* Lua function? */
|
||||
ci->callstatus = CIST_FRESH; /* mark that it is a "fresh" execute */
|
||||
luaV_execute(L, ci); /* call it */
|
||||
}
|
||||
L->nCcalls -= inc;
|
||||
}
|
||||
|
||||
|
||||
/*
|
||||
** External interface for 'ccall'
|
||||
*/
|
||||
void luaD_call (lua_State *L, StkId func, int nResults) {
|
||||
ccall(L, func, nResults, 1);
|
||||
}
|
||||
|
||||
|
||||
/*
|
||||
** Similar to 'luaD_call', but does not allow yields during the call.
|
||||
** If there is a stack overflow, freeing all CI structures will
|
||||
** force the subsequent call to invoke 'luaE_extendCI', which then
|
||||
** will raise any errors.
|
||||
*/
|
||||
void luaD_callnoyield (lua_State *L, StkId func, int nResults) {
|
||||
incXCcalls(L);
|
||||
if (getCcalls(L) <= CSTACKERR) /* possible stack overflow? */
|
||||
luaE_freeCI(L);
|
||||
luaD_call(L, func, nResults);
|
||||
decXCcalls(L);
|
||||
ccall(L, func, nResults, nyci);
|
||||
}
|
||||
|
||||
|
||||
@@ -602,12 +641,12 @@ static int recover (lua_State *L, int status) {
|
||||
if (ci == NULL) return 0; /* no recovery point */
|
||||
/* "finish" luaD_pcall */
|
||||
oldtop = restorestack(L, ci->u2.funcidx);
|
||||
luaF_close(L, oldtop, status); /* may change the stack */
|
||||
oldtop = restorestack(L, ci->u2.funcidx);
|
||||
luaD_seterrorobj(L, status, oldtop);
|
||||
L->ci = ci;
|
||||
L->allowhook = getoah(ci->callstatus); /* restore original 'allowhook' */
|
||||
luaD_shrinkstack(L);
|
||||
status = luaF_close(L, oldtop, status); /* may change the stack */
|
||||
oldtop = restorestack(L, ci->u2.funcidx);
|
||||
luaD_seterrorobj(L, status, oldtop);
|
||||
luaD_shrinkstack(L); /* restore stack size in case of overflow */
|
||||
L->errfunc = ci->u.c.old_errfunc;
|
||||
return 1; /* continue running the coroutine */
|
||||
}
|
||||
@@ -638,12 +677,12 @@ static void resume (lua_State *L, void *ud) {
|
||||
int n = *(cast(int*, ud)); /* number of arguments */
|
||||
StkId firstArg = L->top - n; /* first argument */
|
||||
CallInfo *ci = L->ci;
|
||||
if (L->status == LUA_OK) { /* starting a coroutine? */
|
||||
luaD_call(L, firstArg - 1, LUA_MULTRET);
|
||||
}
|
||||
if (L->status == LUA_OK) /* starting a coroutine? */
|
||||
ccall(L, firstArg - 1, LUA_MULTRET, 1); /* just call its body */
|
||||
else { /* resuming from previous yield */
|
||||
lua_assert(L->status == LUA_YIELD);
|
||||
L->status = LUA_OK; /* mark that it is running (again) */
|
||||
luaE_incCstack(L); /* control the C stack */
|
||||
if (isLua(ci)) /* yielded inside a hook? */
|
||||
luaV_execute(L, ci); /* just continue running Lua code */
|
||||
else { /* 'common' yield */
|
||||
@@ -671,12 +710,7 @@ LUA_API int lua_resume (lua_State *L, lua_State *from, int nargs,
|
||||
}
|
||||
else if (L->status != LUA_YIELD) /* ended with errors? */
|
||||
return resume_error(L, "cannot resume dead coroutine", nargs);
|
||||
if (from == NULL)
|
||||
L->nCcalls = CSTACKTHREAD;
|
||||
else /* correct 'nCcalls' for this thread */
|
||||
L->nCcalls = getCcalls(from) + from->nci - L->nci - CSTACKCF;
|
||||
if (L->nCcalls <= CSTACKERR)
|
||||
return resume_error(L, "C stack overflow", nargs);
|
||||
L->nCcalls = (from) ? getCcalls(from) : 0;
|
||||
luai_userstateresume(L, nargs);
|
||||
api_checknelems(L, (L->status == LUA_OK) ? nargs + 1 : nargs);
|
||||
status = luaD_rawrunprotected(L, resume, &nargs);
|
||||
@@ -706,9 +740,10 @@ LUA_API int lua_isyieldable (lua_State *L) {
|
||||
|
||||
LUA_API int lua_yieldk (lua_State *L, int nresults, lua_KContext ctx,
|
||||
lua_KFunction k) {
|
||||
CallInfo *ci = L->ci;
|
||||
CallInfo *ci;
|
||||
luai_userstateyield(L, nresults);
|
||||
lua_lock(L);
|
||||
ci = L->ci;
|
||||
api_checknelems(L, nresults);
|
||||
if (unlikely(!yieldable(L))) {
|
||||
if (L != G(L)->mainthread)
|
||||
@@ -754,7 +789,7 @@ int luaD_pcall (lua_State *L, Pfunc func, void *u,
|
||||
status = luaF_close(L, oldtop, status);
|
||||
oldtop = restorestack(L, old_top); /* previous call may change stack */
|
||||
luaD_seterrorobj(L, status, oldtop);
|
||||
luaD_shrinkstack(L);
|
||||
luaD_shrinkstack(L); /* restore stack size in case of overflow */
|
||||
}
|
||||
L->errfunc = old_errfunc;
|
||||
return status;
|
||||
|
||||
@@ -17,6 +17,8 @@
|
||||
** Macro to check stack size and grow stack if needed. Parameters
|
||||
** 'pre'/'pos' allow the macro to preserve a pointer into the
|
||||
** stack across reallocations, doing the work only when needed.
|
||||
** It also allows the running of one GC step when the stack is
|
||||
** reallocated.
|
||||
** 'condmovestack' is used in heavy tests to force a stack reallocation
|
||||
** at every check.
|
||||
*/
|
||||
@@ -35,7 +37,7 @@
|
||||
|
||||
|
||||
/* macro to check stack size, preserving 'p' */
|
||||
#define checkstackp(L,n,p) \
|
||||
#define checkstackGCp(L,n,p) \
|
||||
luaD_checkstackaux(L, n, \
|
||||
ptrdiff_t t__ = savestack(L, p); /* save 'p' */ \
|
||||
luaC_checkGC(L), /* stack grow uses memory */ \
|
||||
@@ -44,7 +46,7 @@
|
||||
|
||||
/* macro to check stack size and GC */
|
||||
#define checkstackGC(L,fsize) \
|
||||
luaD_checkstackaux(L, (fsize), (void)0, luaC_checkGC(L))
|
||||
luaD_checkstackaux(L, (fsize), luaC_checkGC(L), (void)0)
|
||||
|
||||
|
||||
/* type of protected functions, to be ran by 'runprotected' */
|
||||
@@ -57,6 +59,7 @@ LUAI_FUNC void luaD_hook (lua_State *L, int event, int line,
|
||||
int fTransfer, int nTransfer);
|
||||
LUAI_FUNC void luaD_hookcall (lua_State *L, CallInfo *ci);
|
||||
LUAI_FUNC void luaD_pretailcall (lua_State *L, CallInfo *ci, StkId func, int n);
|
||||
LUAI_FUNC CallInfo *luaD_precall (lua_State *L, StkId func, int nResults);
|
||||
LUAI_FUNC void luaD_call (lua_State *L, StkId func, int nResults);
|
||||
LUAI_FUNC void luaD_callnoyield (lua_State *L, StkId func, int nResults);
|
||||
LUAI_FUNC void luaD_tryfuncTM (lua_State *L, StkId func);
|
||||
|
||||
@@ -29,15 +29,15 @@ typedef struct {
|
||||
|
||||
|
||||
/*
|
||||
** All high-level dumps go through DumpVector; you can change it to
|
||||
** All high-level dumps go through dumpVector; you can change it to
|
||||
** change the endianness of the result
|
||||
*/
|
||||
#define DumpVector(v,n,D) DumpBlock(v,(n)*sizeof((v)[0]),D)
|
||||
#define dumpVector(D,v,n) dumpBlock(D,v,(n)*sizeof((v)[0]))
|
||||
|
||||
#define DumpLiteral(s,D) DumpBlock(s, sizeof(s) - sizeof(char), D)
|
||||
#define dumpLiteral(D, s) dumpBlock(D,s,sizeof(s) - sizeof(char))
|
||||
|
||||
|
||||
static void DumpBlock (const void *b, size_t size, DumpState *D) {
|
||||
static void dumpBlock (DumpState *D, const void *b, size_t size) {
|
||||
if (D->status == 0 && size > 0) {
|
||||
lua_unlock(D->L);
|
||||
D->status = (*D->writer)(D->L, b, size, D->data);
|
||||
@@ -46,19 +46,19 @@ static void DumpBlock (const void *b, size_t size, DumpState *D) {
|
||||
}
|
||||
|
||||
|
||||
#define DumpVar(x,D) DumpVector(&x,1,D)
|
||||
#define dumpVar(D,x) dumpVector(D,&x,1)
|
||||
|
||||
|
||||
static void DumpByte (int y, DumpState *D) {
|
||||
static void dumpByte (DumpState *D, int y) {
|
||||
lu_byte x = (lu_byte)y;
|
||||
DumpVar(x, D);
|
||||
dumpVar(D, x);
|
||||
}
|
||||
|
||||
|
||||
/* DumpInt Buff Size */
|
||||
/* dumpInt Buff Size */
|
||||
#define DIBS ((sizeof(size_t) * 8 / 7) + 1)
|
||||
|
||||
static void DumpSize (size_t x, DumpState *D) {
|
||||
static void dumpSize (DumpState *D, size_t x) {
|
||||
lu_byte buff[DIBS];
|
||||
int n = 0;
|
||||
do {
|
||||
@@ -66,147 +66,144 @@ static void DumpSize (size_t x, DumpState *D) {
|
||||
x >>= 7;
|
||||
} while (x != 0);
|
||||
buff[DIBS - 1] |= 0x80; /* mark last byte */
|
||||
DumpVector(buff + DIBS - n, n, D);
|
||||
dumpVector(D, buff + DIBS - n, n);
|
||||
}
|
||||
|
||||
|
||||
static void DumpInt (int x, DumpState *D) {
|
||||
DumpSize(x, D);
|
||||
static void dumpInt (DumpState *D, int x) {
|
||||
dumpSize(D, x);
|
||||
}
|
||||
|
||||
|
||||
static void DumpNumber (lua_Number x, DumpState *D) {
|
||||
DumpVar(x, D);
|
||||
static void dumpNumber (DumpState *D, lua_Number x) {
|
||||
dumpVar(D, x);
|
||||
}
|
||||
|
||||
|
||||
static void DumpInteger (lua_Integer x, DumpState *D) {
|
||||
DumpVar(x, D);
|
||||
static void dumpInteger (DumpState *D, lua_Integer x) {
|
||||
dumpVar(D, x);
|
||||
}
|
||||
|
||||
|
||||
static void DumpString (const TString *s, DumpState *D) {
|
||||
static void dumpString (DumpState *D, const TString *s) {
|
||||
if (s == NULL)
|
||||
DumpSize(0, D);
|
||||
dumpSize(D, 0);
|
||||
else {
|
||||
size_t size = tsslen(s);
|
||||
const char *str = getstr(s);
|
||||
DumpSize(size + 1, D);
|
||||
DumpVector(str, size, D);
|
||||
dumpSize(D, size + 1);
|
||||
dumpVector(D, str, size);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
static void DumpCode (const Proto *f, DumpState *D) {
|
||||
DumpInt(f->sizecode, D);
|
||||
DumpVector(f->code, f->sizecode, D);
|
||||
static void dumpCode (DumpState *D, const Proto *f) {
|
||||
dumpInt(D, f->sizecode);
|
||||
dumpVector(D, f->code, f->sizecode);
|
||||
}
|
||||
|
||||
|
||||
static void DumpFunction(const Proto *f, TString *psource, DumpState *D);
|
||||
static void dumpFunction(DumpState *D, const Proto *f, TString *psource);
|
||||
|
||||
static void DumpConstants (const Proto *f, DumpState *D) {
|
||||
static void dumpConstants (DumpState *D, const Proto *f) {
|
||||
int i;
|
||||
int n = f->sizek;
|
||||
DumpInt(n, D);
|
||||
dumpInt(D, n);
|
||||
for (i = 0; i < n; i++) {
|
||||
const TValue *o = &f->k[i];
|
||||
DumpByte(ttypetag(o), D);
|
||||
switch (ttypetag(o)) {
|
||||
case LUA_TNIL:
|
||||
int tt = ttypetag(o);
|
||||
dumpByte(D, tt);
|
||||
switch (tt) {
|
||||
case LUA_VNUMFLT:
|
||||
dumpNumber(D, fltvalue(o));
|
||||
break;
|
||||
case LUA_TBOOLEAN:
|
||||
DumpByte(bvalue(o), D);
|
||||
case LUA_VNUMINT:
|
||||
dumpInteger(D, ivalue(o));
|
||||
break;
|
||||
case LUA_TNUMFLT:
|
||||
DumpNumber(fltvalue(o), D);
|
||||
case LUA_VSHRSTR:
|
||||
case LUA_VLNGSTR:
|
||||
dumpString(D, tsvalue(o));
|
||||
break;
|
||||
case LUA_TNUMINT:
|
||||
DumpInteger(ivalue(o), D);
|
||||
break;
|
||||
case LUA_TSHRSTR:
|
||||
case LUA_TLNGSTR:
|
||||
DumpString(tsvalue(o), D);
|
||||
break;
|
||||
default: lua_assert(0);
|
||||
default:
|
||||
lua_assert(tt == LUA_VNIL || tt == LUA_VFALSE || tt == LUA_VTRUE);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
static void DumpProtos (const Proto *f, DumpState *D) {
|
||||
static void dumpProtos (DumpState *D, const Proto *f) {
|
||||
int i;
|
||||
int n = f->sizep;
|
||||
DumpInt(n, D);
|
||||
dumpInt(D, n);
|
||||
for (i = 0; i < n; i++)
|
||||
DumpFunction(f->p[i], f->source, D);
|
||||
dumpFunction(D, f->p[i], f->source);
|
||||
}
|
||||
|
||||
|
||||
static void DumpUpvalues (const Proto *f, DumpState *D) {
|
||||
static void dumpUpvalues (DumpState *D, const Proto *f) {
|
||||
int i, n = f->sizeupvalues;
|
||||
DumpInt(n, D);
|
||||
dumpInt(D, n);
|
||||
for (i = 0; i < n; i++) {
|
||||
DumpByte(f->upvalues[i].instack, D);
|
||||
DumpByte(f->upvalues[i].idx, D);
|
||||
DumpByte(f->upvalues[i].kind, D);
|
||||
dumpByte(D, f->upvalues[i].instack);
|
||||
dumpByte(D, f->upvalues[i].idx);
|
||||
dumpByte(D, f->upvalues[i].kind);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
static void DumpDebug (const Proto *f, DumpState *D) {
|
||||
static void dumpDebug (DumpState *D, const Proto *f) {
|
||||
int i, n;
|
||||
n = (D->strip) ? 0 : f->sizelineinfo;
|
||||
DumpInt(n, D);
|
||||
DumpVector(f->lineinfo, n, D);
|
||||
dumpInt(D, n);
|
||||
dumpVector(D, f->lineinfo, n);
|
||||
n = (D->strip) ? 0 : f->sizeabslineinfo;
|
||||
DumpInt(n, D);
|
||||
dumpInt(D, n);
|
||||
for (i = 0; i < n; i++) {
|
||||
DumpInt(f->abslineinfo[i].pc, D);
|
||||
DumpInt(f->abslineinfo[i].line, D);
|
||||
dumpInt(D, f->abslineinfo[i].pc);
|
||||
dumpInt(D, f->abslineinfo[i].line);
|
||||
}
|
||||
n = (D->strip) ? 0 : f->sizelocvars;
|
||||
DumpInt(n, D);
|
||||
dumpInt(D, n);
|
||||
for (i = 0; i < n; i++) {
|
||||
DumpString(f->locvars[i].varname, D);
|
||||
DumpInt(f->locvars[i].startpc, D);
|
||||
DumpInt(f->locvars[i].endpc, D);
|
||||
dumpString(D, f->locvars[i].varname);
|
||||
dumpInt(D, f->locvars[i].startpc);
|
||||
dumpInt(D, f->locvars[i].endpc);
|
||||
}
|
||||
n = (D->strip) ? 0 : f->sizeupvalues;
|
||||
DumpInt(n, D);
|
||||
dumpInt(D, n);
|
||||
for (i = 0; i < n; i++)
|
||||
DumpString(f->upvalues[i].name, D);
|
||||
dumpString(D, f->upvalues[i].name);
|
||||
}
|
||||
|
||||
|
||||
static void DumpFunction (const Proto *f, TString *psource, DumpState *D) {
|
||||
static void dumpFunction (DumpState *D, const Proto *f, TString *psource) {
|
||||
if (D->strip || f->source == psource)
|
||||
DumpString(NULL, D); /* no debug info or same source as its parent */
|
||||
dumpString(D, NULL); /* no debug info or same source as its parent */
|
||||
else
|
||||
DumpString(f->source, D);
|
||||
DumpInt(f->linedefined, D);
|
||||
DumpInt(f->lastlinedefined, D);
|
||||
DumpByte(f->numparams, D);
|
||||
DumpByte(f->is_vararg, D);
|
||||
DumpByte(f->maxstacksize, D);
|
||||
DumpCode(f, D);
|
||||
DumpConstants(f, D);
|
||||
DumpUpvalues(f, D);
|
||||
DumpProtos(f, D);
|
||||
DumpDebug(f, D);
|
||||
dumpString(D, f->source);
|
||||
dumpInt(D, f->linedefined);
|
||||
dumpInt(D, f->lastlinedefined);
|
||||
dumpByte(D, f->numparams);
|
||||
dumpByte(D, f->is_vararg);
|
||||
dumpByte(D, f->maxstacksize);
|
||||
dumpCode(D, f);
|
||||
dumpConstants(D, f);
|
||||
dumpUpvalues(D, f);
|
||||
dumpProtos(D, f);
|
||||
dumpDebug(D, f);
|
||||
}
|
||||
|
||||
|
||||
static void DumpHeader (DumpState *D) {
|
||||
DumpLiteral(LUA_SIGNATURE, D);
|
||||
DumpInt(LUAC_VERSION, D);
|
||||
DumpByte(LUAC_FORMAT, D);
|
||||
DumpLiteral(LUAC_DATA, D);
|
||||
DumpByte(sizeof(Instruction), D);
|
||||
DumpByte(sizeof(lua_Integer), D);
|
||||
DumpByte(sizeof(lua_Number), D);
|
||||
DumpInteger(LUAC_INT, D);
|
||||
DumpNumber(LUAC_NUM, D);
|
||||
static void dumpHeader (DumpState *D) {
|
||||
dumpLiteral(D, LUA_SIGNATURE);
|
||||
dumpByte(D, LUAC_VERSION);
|
||||
dumpByte(D, LUAC_FORMAT);
|
||||
dumpLiteral(D, LUAC_DATA);
|
||||
dumpByte(D, sizeof(Instruction));
|
||||
dumpByte(D, sizeof(lua_Integer));
|
||||
dumpByte(D, sizeof(lua_Number));
|
||||
dumpInteger(D, LUAC_INT);
|
||||
dumpNumber(D, LUAC_NUM);
|
||||
}
|
||||
|
||||
|
||||
@@ -221,9 +218,9 @@ int luaU_dump(lua_State *L, const Proto *f, lua_Writer w, void *data,
|
||||
D.data = data;
|
||||
D.strip = strip;
|
||||
D.status = 0;
|
||||
DumpHeader(&D);
|
||||
DumpByte(f->sizeupvalues, &D);
|
||||
DumpFunction(f, NULL, &D);
|
||||
dumpHeader(&D);
|
||||
dumpByte(&D, f->sizeupvalues);
|
||||
dumpFunction(&D, f, NULL);
|
||||
return D.status;
|
||||
}
|
||||
|
||||
|
||||
@@ -24,20 +24,20 @@
|
||||
|
||||
|
||||
|
||||
CClosure *luaF_newCclosure (lua_State *L, int n) {
|
||||
GCObject *o = luaC_newobj(L, LUA_TCCL, sizeCclosure(n));
|
||||
CClosure *luaF_newCclosure (lua_State *L, int nupvals) {
|
||||
GCObject *o = luaC_newobj(L, LUA_VCCL, sizeCclosure(nupvals));
|
||||
CClosure *c = gco2ccl(o);
|
||||
c->nupvalues = cast_byte(n);
|
||||
c->nupvalues = cast_byte(nupvals);
|
||||
return c;
|
||||
}
|
||||
|
||||
|
||||
LClosure *luaF_newLclosure (lua_State *L, int n) {
|
||||
GCObject *o = luaC_newobj(L, LUA_TLCL, sizeLclosure(n));
|
||||
LClosure *luaF_newLclosure (lua_State *L, int nupvals) {
|
||||
GCObject *o = luaC_newobj(L, LUA_VLCL, sizeLclosure(nupvals));
|
||||
LClosure *c = gco2lcl(o);
|
||||
c->p = NULL;
|
||||
c->nupvalues = cast_byte(n);
|
||||
while (n--) c->upvals[n] = NULL;
|
||||
c->nupvalues = cast_byte(nupvals);
|
||||
while (nupvals--) c->upvals[nupvals] = NULL;
|
||||
return c;
|
||||
}
|
||||
|
||||
@@ -48,12 +48,12 @@ LClosure *luaF_newLclosure (lua_State *L, int n) {
|
||||
void luaF_initupvals (lua_State *L, LClosure *cl) {
|
||||
int i;
|
||||
for (i = 0; i < cl->nupvalues; i++) {
|
||||
GCObject *o = luaC_newobj(L, LUA_TUPVAL, sizeof(UpVal));
|
||||
GCObject *o = luaC_newobj(L, LUA_VUPVAL, sizeof(UpVal));
|
||||
UpVal *uv = gco2upv(o);
|
||||
uv->v = &uv->u.value; /* make it closed */
|
||||
setnilvalue(uv->v);
|
||||
cl->upvals[i] = uv;
|
||||
luaC_objbarrier(L, cl, o);
|
||||
luaC_objbarrier(L, cl, uv);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -63,7 +63,7 @@ void luaF_initupvals (lua_State *L, LClosure *cl) {
|
||||
** open upvalues of 'L' after entry 'prev'.
|
||||
**/
|
||||
static UpVal *newupval (lua_State *L, int tbc, StkId level, UpVal **prev) {
|
||||
GCObject *o = luaC_newobj(L, LUA_TUPVAL, sizeof(UpVal));
|
||||
GCObject *o = luaC_newobj(L, LUA_VUPVAL, sizeof(UpVal));
|
||||
UpVal *uv = gco2upv(o);
|
||||
UpVal *next = *prev;
|
||||
uv->v = s2v(level); /* current value lives in the stack */
|
||||
@@ -165,7 +165,7 @@ static int callclosemth (lua_State *L, StkId level, int status) {
|
||||
if (newstatus != LUA_OK && status == CLOSEPROTECT) /* first error? */
|
||||
status = newstatus; /* this will be the new error */
|
||||
else {
|
||||
if (newstatus != LUA_OK) /* supressed error? */
|
||||
if (newstatus != LUA_OK) /* suppressed error? */
|
||||
luaE_warnerror(L, "__close metamethod");
|
||||
/* leave original error (or nil) on top */
|
||||
L->top = restorestack(L, oldtop);
|
||||
@@ -234,16 +234,17 @@ int luaF_close (lua_State *L, StkId level, int status) {
|
||||
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 (!iswhite(uv)) { /* neither white nor dead? */
|
||||
nw2black(uv); /* closed upvalues cannot be gray */
|
||||
luaC_barrier(L, uv, slot);
|
||||
}
|
||||
}
|
||||
return status;
|
||||
}
|
||||
|
||||
|
||||
Proto *luaF_newproto (lua_State *L) {
|
||||
GCObject *o = luaC_newobj(L, LUA_TPROTO, sizeof(Proto));
|
||||
GCObject *o = luaC_newobj(L, LUA_VPROTO, sizeof(Proto));
|
||||
Proto *f = gco2p(o);
|
||||
f->k = NULL;
|
||||
f->sizek = 0;
|
||||
|
||||
@@ -54,8 +54,8 @@
|
||||
|
||||
|
||||
LUAI_FUNC Proto *luaF_newproto (lua_State *L);
|
||||
LUAI_FUNC CClosure *luaF_newCclosure (lua_State *L, int nelems);
|
||||
LUAI_FUNC LClosure *luaF_newLclosure (lua_State *L, int nelems);
|
||||
LUAI_FUNC CClosure *luaF_newCclosure (lua_State *L, int nupvals);
|
||||
LUAI_FUNC LClosure *luaF_newLclosure (lua_State *L, int nupvals);
|
||||
LUAI_FUNC void luaF_initupvals (lua_State *L, LClosure *cl);
|
||||
LUAI_FUNC UpVal *luaF_findupval (lua_State *L, StkId level);
|
||||
LUAI_FUNC void luaF_newtbcupval (lua_State *L, StkId level);
|
||||
|
||||
@@ -60,16 +60,24 @@
|
||||
#define PAUSEADJ 100
|
||||
|
||||
|
||||
/* mask to erase all color bits (plus gen. related stuff) */
|
||||
#define maskcolors (~(bitmask(BLACKBIT) | WHITEBITS | AGEBITS))
|
||||
/* mask with all color bits */
|
||||
#define maskcolors (bitmask(BLACKBIT) | WHITEBITS)
|
||||
|
||||
/* mask with all GC bits */
|
||||
#define maskgcbits (maskcolors | AGEBITS)
|
||||
|
||||
|
||||
/* macro to erase all color bits then sets only the current white bit */
|
||||
/* macro to erase all color bits then set only the current white bit */
|
||||
#define makewhite(g,x) \
|
||||
(x->marked = cast_byte((x->marked & maskcolors) | luaC_white(g)))
|
||||
(x->marked = cast_byte((x->marked & ~maskcolors) | luaC_white(g)))
|
||||
|
||||
#define white2gray(x) resetbits(x->marked, WHITEBITS)
|
||||
#define black2gray(x) resetbit(x->marked, BLACKBIT)
|
||||
/* make an object gray (neither white nor black) */
|
||||
#define set2gray(x) resetbits(x->marked, maskcolors)
|
||||
|
||||
|
||||
/* make an object black (coming from any color) */
|
||||
#define set2black(x) \
|
||||
(x->marked = cast_byte((x->marked & ~WHITEBITS) | bitmask(BLACKBIT)))
|
||||
|
||||
|
||||
#define valiswhite(x) (iscollectable(x) && iswhite(gcvalue(x)))
|
||||
@@ -77,16 +85,13 @@
|
||||
#define keyiswhite(n) (keyiscollectable(n) && iswhite(gckey(n)))
|
||||
|
||||
|
||||
#define checkconsistency(obj) \
|
||||
lua_longassert(!iscollectable(obj) || righttt(obj))
|
||||
|
||||
/*
|
||||
** Protected access to objects in values
|
||||
*/
|
||||
#define gcvalueN(o) (iscollectable(o) ? gcvalue(o) : NULL)
|
||||
|
||||
|
||||
#define markvalue(g,o) { checkconsistency(o); \
|
||||
#define markvalue(g,o) { checkliveness(g->mainthread,o); \
|
||||
if (valiswhite(o)) reallymarkobject(g,gcvalue(o)); }
|
||||
|
||||
#define markkey(g, n) { if keyiswhite(n) reallymarkobject(g,gckey(n)); }
|
||||
@@ -119,12 +124,12 @@ static void entersweep (lua_State *L);
|
||||
|
||||
static GCObject **getgclist (GCObject *o) {
|
||||
switch (o->tt) {
|
||||
case LUA_TTABLE: return &gco2t(o)->gclist;
|
||||
case LUA_TLCL: return &gco2lcl(o)->gclist;
|
||||
case LUA_TCCL: return &gco2ccl(o)->gclist;
|
||||
case LUA_TTHREAD: return &gco2th(o)->gclist;
|
||||
case LUA_TPROTO: return &gco2p(o)->gclist;
|
||||
case LUA_TUSERDATA: {
|
||||
case LUA_VTABLE: return &gco2t(o)->gclist;
|
||||
case LUA_VLCL: return &gco2lcl(o)->gclist;
|
||||
case LUA_VCCL: return &gco2ccl(o)->gclist;
|
||||
case LUA_VTHREAD: return &gco2th(o)->gclist;
|
||||
case LUA_VPROTO: return &gco2p(o)->gclist;
|
||||
case LUA_VUSERDATA: {
|
||||
Udata *u = gco2u(o);
|
||||
lua_assert(u->nuvalue > 0);
|
||||
return &u->gclist;
|
||||
@@ -135,31 +140,38 @@ static GCObject **getgclist (GCObject *o) {
|
||||
|
||||
|
||||
/*
|
||||
** Link a collectable object 'o' with a known type into list pointed by 'p'.
|
||||
** Link a collectable object 'o' with a known type into the list 'p'.
|
||||
** (Must be a macro to access the 'gclist' field in different types.)
|
||||
*/
|
||||
#define linkgclist(o,p) ((o)->gclist = (p), (p) = obj2gco(o))
|
||||
#define linkgclist(o,p) linkgclist_(obj2gco(o), &(o)->gclist, &(p))
|
||||
|
||||
static void linkgclist_ (GCObject *o, GCObject **pnext, GCObject **list) {
|
||||
lua_assert(!isgray(o)); /* cannot be in a gray list */
|
||||
*pnext = *list;
|
||||
*list = o;
|
||||
set2gray(o); /* now it is */
|
||||
}
|
||||
|
||||
|
||||
/*
|
||||
** Link a generic collectable object 'o' into list pointed by 'p'.
|
||||
** Link a generic collectable object 'o' into the list 'p'.
|
||||
*/
|
||||
#define linkobjgclist(o,p) (*getgclist(o) = (p), (p) = obj2gco(o))
|
||||
#define linkobjgclist(o,p) linkgclist_(obj2gco(o), getgclist(o), &(p))
|
||||
|
||||
|
||||
|
||||
/*
|
||||
** Clear keys for empty entries in tables. If entry is empty
|
||||
** and its key is not marked, mark its entry as dead. This allows the
|
||||
** collection of the key, but keeps its entry in the table (its removal
|
||||
** could break a chain). The main feature of a dead key is that it must
|
||||
** be different from any other value, to do not disturb searches.
|
||||
** Other places never manipulate dead keys, because its associated empty
|
||||
** value is enough to signal that the entry is logically empty.
|
||||
** Clear keys for empty entries in tables. If entry is empty, mark its
|
||||
** entry as dead. This allows the collection of the key, but keeps its
|
||||
** entry in the table: its removal could break a chain and could break
|
||||
** a table traversal. Other places never manipulate dead keys, because
|
||||
** its associated empty value is enough to signal that the entry is
|
||||
** logically empty.
|
||||
*/
|
||||
static void clearkey (Node *n) {
|
||||
lua_assert(isempty(gval(n)));
|
||||
if (keyiswhite(n))
|
||||
setdeadkey(n); /* unused and unmarked key; remove it */
|
||||
if (keyiscollectable(n))
|
||||
setdeadkey(n); /* unused key; remove it */
|
||||
}
|
||||
|
||||
|
||||
@@ -181,14 +193,17 @@ static int iscleared (global_State *g, const GCObject *o) {
|
||||
|
||||
|
||||
/*
|
||||
** barrier that moves collector forward, that is, mark the white object
|
||||
** 'v' being pointed by the black object 'o'. (If in sweep phase, clear
|
||||
** the black object to white [sweep it] to avoid other barrier calls for
|
||||
** this same object.) In the generational mode, 'v' must also become
|
||||
** old, if 'o' is old; however, it cannot be changed directly to OLD,
|
||||
** because it may still point to non-old objects. So, it is marked as
|
||||
** OLD0. In the next cycle it will become OLD1, and in the next it
|
||||
** will finally become OLD (regular old).
|
||||
** Barrier that moves collector forward, that is, marks the white object
|
||||
** 'v' being pointed by the black object 'o'. In the generational
|
||||
** mode, 'v' must also become old, if 'o' is old; however, it cannot
|
||||
** be changed directly to OLD, because it may still point to non-old
|
||||
** objects. So, it is marked as OLD0. In the next cycle it will become
|
||||
** OLD1, and in the next it will finally become OLD (regular old). By
|
||||
** then, any object it points to will also be old. If called in the
|
||||
** incremental sweep phase, it clears the black object to white (sweep
|
||||
** it) to avoid other barrier calls for this same object. (That cannot
|
||||
** be done is generational mode, as its sweep does not distinguish
|
||||
** whites from deads.)
|
||||
*/
|
||||
void luaC_barrier_ (lua_State *L, GCObject *o, GCObject *v) {
|
||||
global_State *g = G(L);
|
||||
@@ -202,7 +217,8 @@ void luaC_barrier_ (lua_State *L, GCObject *o, GCObject *v) {
|
||||
}
|
||||
else { /* sweep phase */
|
||||
lua_assert(issweepphase(g));
|
||||
makewhite(g, o); /* mark main obj. as white to avoid other barriers */
|
||||
if (g->gckind == KGC_INC) /* incremental mode? */
|
||||
makewhite(g, o); /* mark 'o' as white to avoid other barriers */
|
||||
}
|
||||
}
|
||||
|
||||
@@ -214,18 +230,20 @@ void luaC_barrier_ (lua_State *L, GCObject *o, GCObject *v) {
|
||||
void luaC_barrierback_ (lua_State *L, GCObject *o) {
|
||||
global_State *g = G(L);
|
||||
lua_assert(isblack(o) && !isdead(g, o));
|
||||
lua_assert(g->gckind != KGC_GEN || (isold(o) && getage(o) != G_TOUCHED1));
|
||||
if (getage(o) != G_TOUCHED2) /* not already in gray list? */
|
||||
linkobjgclist(o, g->grayagain); /* link it in 'grayagain' */
|
||||
black2gray(o); /* make object gray (again) */
|
||||
setage(o, G_TOUCHED1); /* touched in current cycle */
|
||||
lua_assert((g->gckind == KGC_GEN) == (isold(o) && getage(o) != G_TOUCHED1));
|
||||
if (getage(o) == G_TOUCHED2) /* already in gray list? */
|
||||
set2gray(o); /* make it gray to become touched1 */
|
||||
else /* link it in 'grayagain' and paint it gray */
|
||||
linkobjgclist(o, g->grayagain);
|
||||
if (isold(o)) /* generational mode? */
|
||||
setage(o, G_TOUCHED1); /* touched in current cycle */
|
||||
}
|
||||
|
||||
|
||||
void luaC_fix (lua_State *L, GCObject *o) {
|
||||
global_State *g = G(L);
|
||||
lua_assert(g->allgc == o); /* object must be 1st in 'allgc' list! */
|
||||
white2gray(o); /* they will be gray forever */
|
||||
set2gray(o); /* they will be gray forever */
|
||||
setage(o, G_OLD); /* and old forever */
|
||||
g->allgc = o->next; /* remove object from 'allgc' list */
|
||||
o->next = g->fixedgc; /* link it to 'fixedgc' list */
|
||||
@@ -259,39 +277,45 @@ GCObject *luaC_newobj (lua_State *L, int tt, size_t sz) {
|
||||
|
||||
|
||||
/*
|
||||
** Mark an object. Userdata, strings, and closed upvalues are visited
|
||||
** and turned black here. Other objects are marked gray and added
|
||||
** to appropriate list to be visited (and turned black) later. (Open
|
||||
** upvalues are already linked in 'headuv' list. They are kept gray
|
||||
** to avoid barriers, as their values will be revisited by the thread.)
|
||||
** Mark an object. Userdata with no user values, strings, and closed
|
||||
** upvalues are visited and turned black here. Open upvalues are
|
||||
** already indirectly linked through their respective threads in the
|
||||
** 'twups' list, so they don't go to the gray list; nevertheless, they
|
||||
** are kept gray to avoid barriers, as their values will be revisited
|
||||
** by the thread or by 'remarkupvals'. Other objects are added to the
|
||||
** gray list to be visited (and turned black) later. Both userdata and
|
||||
** upvalues can call this function recursively, but this recursion goes
|
||||
** for at most two levels: An upvalue cannot refer to another upvalue
|
||||
** (only closures can), and a userdata's metatable must be a table.
|
||||
*/
|
||||
static void reallymarkobject (global_State *g, GCObject *o) {
|
||||
white2gray(o);
|
||||
switch (o->tt) {
|
||||
case LUA_TSHRSTR:
|
||||
case LUA_TLNGSTR: {
|
||||
gray2black(o);
|
||||
case LUA_VSHRSTR:
|
||||
case LUA_VLNGSTR: {
|
||||
set2black(o); /* nothing to visit */
|
||||
break;
|
||||
}
|
||||
case LUA_TUPVAL: {
|
||||
case LUA_VUPVAL: {
|
||||
UpVal *uv = gco2upv(o);
|
||||
if (!upisopen(uv)) /* open upvalues are kept gray */
|
||||
gray2black(o);
|
||||
if (upisopen(uv))
|
||||
set2gray(uv); /* open upvalues are kept gray */
|
||||
else
|
||||
set2black(uv); /* closed upvalues are visited here */
|
||||
markvalue(g, uv->v); /* mark its content */
|
||||
break;
|
||||
}
|
||||
case LUA_TUSERDATA: {
|
||||
case LUA_VUSERDATA: {
|
||||
Udata *u = gco2u(o);
|
||||
if (u->nuvalue == 0) { /* no user values? */
|
||||
markobjectN(g, u->metatable); /* mark its metatable */
|
||||
gray2black(o); /* nothing else to mark */
|
||||
set2black(u); /* nothing else to mark */
|
||||
break;
|
||||
}
|
||||
/* else... */
|
||||
} /* FALLTHROUGH */
|
||||
case LUA_TLCL: case LUA_TCCL: case LUA_TTABLE:
|
||||
case LUA_TTHREAD: case LUA_TPROTO: {
|
||||
linkobjgclist(o, g->gray);
|
||||
case LUA_VLCL: case LUA_VCCL: case LUA_VTABLE:
|
||||
case LUA_VTHREAD: case LUA_VPROTO: {
|
||||
linkobjgclist(o, g->gray); /* to be visited later */
|
||||
break;
|
||||
}
|
||||
default: lua_assert(0); break;
|
||||
@@ -324,28 +348,36 @@ static lu_mem markbeingfnz (global_State *g) {
|
||||
|
||||
|
||||
/*
|
||||
** Mark all values stored in marked open upvalues from non-marked threads.
|
||||
** (Values from marked threads were already marked when traversing the
|
||||
** thread.) Remove from the list threads that no longer have upvalues and
|
||||
** not-marked threads.
|
||||
** For each non-marked thread, simulates a barrier between each open
|
||||
** upvalue and its value. (If the thread is collected, the value will be
|
||||
** assigned to the upvalue, but then it can be too late for the barrier
|
||||
** to act. The "barrier" does not need to check colors: A non-marked
|
||||
** thread must be young; upvalues cannot be older than their threads; so
|
||||
** any visited upvalue must be young too.) Also removes the thread from
|
||||
** the list, as it was already visited. Removes also threads with no
|
||||
** upvalues, as they have nothing to be checked. (If the thread gets an
|
||||
** upvalue later, it will be linked in the list again.)
|
||||
*/
|
||||
static int remarkupvals (global_State *g) {
|
||||
lua_State *thread;
|
||||
lua_State **p = &g->twups;
|
||||
int work = 0;
|
||||
int work = 0; /* estimate of how much work was done here */
|
||||
while ((thread = *p) != NULL) {
|
||||
work++;
|
||||
lua_assert(!isblack(thread)); /* threads are never black */
|
||||
if (isgray(thread) && thread->openupval != NULL)
|
||||
if (!iswhite(thread) && thread->openupval != NULL)
|
||||
p = &thread->twups; /* keep marked thread with upvalues in the list */
|
||||
else { /* thread is not marked or without upvalues */
|
||||
UpVal *uv;
|
||||
lua_assert(!isold(thread) || thread->openupval == NULL);
|
||||
*p = thread->twups; /* remove thread from the list */
|
||||
thread->twups = thread; /* mark that it is out of list */
|
||||
for (uv = thread->openupval; uv != NULL; uv = uv->u.open.next) {
|
||||
lua_assert(getage(uv) <= getage(thread));
|
||||
work++;
|
||||
if (!iswhite(uv)) /* upvalue already visited? */
|
||||
if (!iswhite(uv)) { /* upvalue already visited? */
|
||||
lua_assert(upisopen(uv) && isgray(uv));
|
||||
markvalue(g, uv->v); /* mark its value */
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -353,12 +385,17 @@ static int remarkupvals (global_State *g) {
|
||||
}
|
||||
|
||||
|
||||
static void cleargraylists (global_State *g) {
|
||||
g->gray = g->grayagain = NULL;
|
||||
g->weak = g->allweak = g->ephemeron = NULL;
|
||||
}
|
||||
|
||||
|
||||
/*
|
||||
** mark root set and reset all gray lists, to start a new collection
|
||||
*/
|
||||
static void restartcollection (global_State *g) {
|
||||
g->gray = g->grayagain = NULL;
|
||||
g->weak = g->allweak = g->ephemeron = NULL;
|
||||
cleargraylists(g);
|
||||
markobject(g, g->mainthread);
|
||||
markvalue(g, &g->l_registry);
|
||||
markmt(g);
|
||||
@@ -374,6 +411,26 @@ static void restartcollection (global_State *g) {
|
||||
** =======================================================
|
||||
*/
|
||||
|
||||
|
||||
/*
|
||||
** Check whether object 'o' should be kept in the 'grayagain' list for
|
||||
** post-processing by 'correctgraylist'. (It could put all old objects
|
||||
** in the list and leave all the work to 'correctgraylist', but it is
|
||||
** more efficient to avoid adding elements that will be removed.) Only
|
||||
** TOUCHED1 objects need to be in the list. TOUCHED2 doesn't need to go
|
||||
** back to a gray list, but then it must become OLD. (That is what
|
||||
** 'correctgraylist' does when it finds a TOUCHED2 object.)
|
||||
*/
|
||||
static void genlink (global_State *g, GCObject *o) {
|
||||
lua_assert(isblack(o));
|
||||
if (getage(o) == G_TOUCHED1) { /* touched in this cycle? */
|
||||
linkobjgclist(o, g->grayagain); /* link it back in 'grayagain' */
|
||||
} /* everything else do not need to be linked back */
|
||||
else if (getage(o) == G_TOUCHED2)
|
||||
changeage(o, G_TOUCHED2, G_OLD); /* advance age */
|
||||
}
|
||||
|
||||
|
||||
/*
|
||||
** Traverse a table with weak values and link it to proper list. During
|
||||
** propagate phase, keep it in 'grayagain' list, to be revisited in the
|
||||
@@ -410,8 +467,9 @@ static void traverseweakvalue (global_State *g, Table *h) {
|
||||
** the atomic phase, if table has any white->white entry, it has to
|
||||
** be revisited during ephemeron convergence (as that key may turn
|
||||
** black). Otherwise, if it has any white key, table has to be cleared
|
||||
** (in the atomic phase). In generational mode, it (like all visited
|
||||
** tables) must be kept in some gray list for post-processing.
|
||||
** (in the atomic phase). In generational mode, some tables
|
||||
** must be kept in some gray list for post-processing; this is done
|
||||
** by 'genlink'.
|
||||
*/
|
||||
static int traverseephemeron (global_State *g, Table *h, int inv) {
|
||||
int marked = 0; /* true if an object is marked in this traversal */
|
||||
@@ -450,10 +508,8 @@ static int traverseephemeron (global_State *g, Table *h, int inv) {
|
||||
linkgclist(h, g->ephemeron); /* have to propagate again */
|
||||
else if (hasclears) /* table has white keys? */
|
||||
linkgclist(h, g->allweak); /* may have to clean white keys */
|
||||
else if (g->gckind == KGC_GEN)
|
||||
linkgclist(h, g->grayagain); /* keep it in some list */
|
||||
else
|
||||
gray2black(h);
|
||||
genlink(g, obj2gco(h)); /* check whether collector still needs to see it */
|
||||
return marked;
|
||||
}
|
||||
|
||||
@@ -473,10 +529,7 @@ static void traversestrongtable (global_State *g, Table *h) {
|
||||
markvalue(g, gval(n));
|
||||
}
|
||||
}
|
||||
if (g->gckind == KGC_GEN) {
|
||||
linkgclist(h, g->grayagain); /* keep it in some gray list */
|
||||
black2gray(h);
|
||||
}
|
||||
genlink(g, obj2gco(h));
|
||||
}
|
||||
|
||||
|
||||
@@ -488,7 +541,6 @@ static lu_mem traversetable (global_State *g, Table *h) {
|
||||
(cast_void(weakkey = strchr(svalue(mode), 'k')),
|
||||
cast_void(weakvalue = strchr(svalue(mode), 'v')),
|
||||
(weakkey || weakvalue))) { /* is really weak? */
|
||||
black2gray(h); /* keep table gray */
|
||||
if (!weakkey) /* strong keys? */
|
||||
traverseweakvalue(g, h);
|
||||
else if (!weakvalue) /* strong values? */
|
||||
@@ -507,10 +559,7 @@ static int traverseudata (global_State *g, Udata *u) {
|
||||
markobjectN(g, u->metatable); /* mark its metatable */
|
||||
for (i = 0; i < u->nuvalue; i++)
|
||||
markvalue(g, &u->uv[i].uv);
|
||||
if (g->gckind == KGC_GEN) {
|
||||
linkgclist(u, g->grayagain); /* keep it in some gray list */
|
||||
black2gray(u);
|
||||
}
|
||||
genlink(g, obj2gco(u));
|
||||
return 1 + u->nuvalue;
|
||||
}
|
||||
|
||||
@@ -559,12 +608,21 @@ static int traverseLclosure (global_State *g, LClosure *cl) {
|
||||
|
||||
/*
|
||||
** Traverse a thread, marking the elements in the stack up to its top
|
||||
** and cleaning the rest of the stack in the final traversal.
|
||||
** That ensures that the entire stack have valid (non-dead) objects.
|
||||
** and cleaning the rest of the stack in the final traversal. That
|
||||
** ensures that the entire stack have valid (non-dead) objects.
|
||||
** Threads have no barriers. In gen. mode, old threads must be visited
|
||||
** at every cycle, because they might point to young objects. In inc.
|
||||
** mode, the thread can still be modified before the end of the cycle,
|
||||
** and therefore it must be visited again in the atomic phase. To ensure
|
||||
** these visits, threads must return to a gray list if they are not new
|
||||
** (which can only happen in generational mode) or if the traverse is in
|
||||
** the propagate phase (which can only happen in incremental mode).
|
||||
*/
|
||||
static int traversethread (global_State *g, lua_State *th) {
|
||||
UpVal *uv;
|
||||
StkId o = th->stack;
|
||||
if (isold(th) || g->gcstate == GCSpropagate)
|
||||
linkgclist(th, g->grayagain); /* insert into 'grayagain' list */
|
||||
if (o == NULL)
|
||||
return 1; /* stack not completely built yet */
|
||||
lua_assert(g->gcstate == GCSatomic ||
|
||||
@@ -574,9 +632,8 @@ static int traversethread (global_State *g, lua_State *th) {
|
||||
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 */
|
||||
setnilvalue(s2v(o));
|
||||
for (; o < th->stack_last + EXTRA_STACK; o++)
|
||||
setnilvalue(s2v(o)); /* clear dead stack slice */
|
||||
/* 'remarkupvals' may have removed thread from 'twups' list */
|
||||
if (!isintwups(th) && th->openupval != NULL) {
|
||||
th->twups = g->twups; /* link it back to the list */
|
||||
@@ -585,30 +642,24 @@ static int traversethread (global_State *g, lua_State *th) {
|
||||
}
|
||||
else if (!g->gcemergency)
|
||||
luaD_shrinkstack(th); /* do not change stack in emergency cycle */
|
||||
return 1 + th->stacksize;
|
||||
return 1 + stacksize(th);
|
||||
}
|
||||
|
||||
|
||||
/*
|
||||
** traverse one gray object, turning it to black (except for threads,
|
||||
** which are always gray).
|
||||
** traverse one gray object, turning it to black.
|
||||
*/
|
||||
static lu_mem propagatemark (global_State *g) {
|
||||
GCObject *o = g->gray;
|
||||
gray2black(o);
|
||||
nw2black(o);
|
||||
g->gray = *getgclist(o); /* remove from 'gray' list */
|
||||
switch (o->tt) {
|
||||
case LUA_TTABLE: return traversetable(g, gco2t(o));
|
||||
case LUA_TUSERDATA: return traverseudata(g, gco2u(o));
|
||||
case LUA_TLCL: return traverseLclosure(g, gco2lcl(o));
|
||||
case LUA_TCCL: return traverseCclosure(g, gco2ccl(o));
|
||||
case LUA_TPROTO: return traverseproto(g, gco2p(o));
|
||||
case LUA_TTHREAD: {
|
||||
lua_State *th = gco2th(o);
|
||||
linkgclist(th, g->grayagain); /* insert into 'grayagain' list */
|
||||
black2gray(o);
|
||||
return traversethread(g, th);
|
||||
}
|
||||
case LUA_VTABLE: return traversetable(g, gco2t(o));
|
||||
case LUA_VUSERDATA: return traverseudata(g, gco2u(o));
|
||||
case LUA_VLCL: return traverseLclosure(g, gco2lcl(o));
|
||||
case LUA_VCCL: return traverseCclosure(g, gco2ccl(o));
|
||||
case LUA_VPROTO: return traverseproto(g, gco2p(o));
|
||||
case LUA_VTHREAD: return traversethread(g, gco2th(o));
|
||||
default: lua_assert(0); return 0;
|
||||
}
|
||||
}
|
||||
@@ -638,8 +689,10 @@ static void convergeephemerons (global_State *g) {
|
||||
g->ephemeron = NULL; /* tables may return to this list when traversed */
|
||||
changed = 0;
|
||||
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? */
|
||||
Table *h = gco2t(w);
|
||||
next = h->gclist; /* list is rebuilt during loop */
|
||||
nw2black(h); /* out of the list (for now) */
|
||||
if (traverseephemeron(g, h, dir)) { /* marked some value? */
|
||||
propagateall(g); /* propagate changes */
|
||||
changed = 1; /* will have to revisit all ephemeron tables */
|
||||
}
|
||||
@@ -710,36 +763,44 @@ static void freeupval (lua_State *L, UpVal *uv) {
|
||||
|
||||
static void freeobj (lua_State *L, GCObject *o) {
|
||||
switch (o->tt) {
|
||||
case LUA_TPROTO:
|
||||
case LUA_VPROTO:
|
||||
luaF_freeproto(L, gco2p(o));
|
||||
break;
|
||||
case LUA_TUPVAL:
|
||||
case LUA_VUPVAL:
|
||||
freeupval(L, gco2upv(o));
|
||||
break;
|
||||
case LUA_TLCL:
|
||||
luaM_freemem(L, o, sizeLclosure(gco2lcl(o)->nupvalues));
|
||||
case LUA_VLCL: {
|
||||
LClosure *cl = gco2lcl(o);
|
||||
luaM_freemem(L, cl, sizeLclosure(cl->nupvalues));
|
||||
break;
|
||||
case LUA_TCCL:
|
||||
luaM_freemem(L, o, sizeCclosure(gco2ccl(o)->nupvalues));
|
||||
}
|
||||
case LUA_VCCL: {
|
||||
CClosure *cl = gco2ccl(o);
|
||||
luaM_freemem(L, cl, sizeCclosure(cl->nupvalues));
|
||||
break;
|
||||
case LUA_TTABLE:
|
||||
}
|
||||
case LUA_VTABLE:
|
||||
luaH_free(L, gco2t(o));
|
||||
break;
|
||||
case LUA_TTHREAD:
|
||||
case LUA_VTHREAD:
|
||||
luaE_freethread(L, gco2th(o));
|
||||
break;
|
||||
case LUA_TUSERDATA: {
|
||||
case LUA_VUSERDATA: {
|
||||
Udata *u = gco2u(o);
|
||||
luaM_freemem(L, o, sizeudata(u->nuvalue, u->len));
|
||||
break;
|
||||
}
|
||||
case LUA_TSHRSTR:
|
||||
luaS_remove(L, gco2ts(o)); /* remove it from hash table */
|
||||
luaM_freemem(L, o, sizelstring(gco2ts(o)->shrlen));
|
||||
case LUA_VSHRSTR: {
|
||||
TString *ts = gco2ts(o);
|
||||
luaS_remove(L, ts); /* remove it from hash table */
|
||||
luaM_freemem(L, ts, sizelstring(ts->shrlen));
|
||||
break;
|
||||
case LUA_TLNGSTR:
|
||||
luaM_freemem(L, o, sizelstring(gco2ts(o)->u.lnglen));
|
||||
}
|
||||
case LUA_VLNGSTR: {
|
||||
TString *ts = gco2ts(o);
|
||||
luaM_freemem(L, ts, sizelstring(ts->u.lnglen));
|
||||
break;
|
||||
}
|
||||
default: lua_assert(0);
|
||||
}
|
||||
}
|
||||
@@ -766,7 +827,7 @@ static GCObject **sweeplist (lua_State *L, GCObject **p, int countin,
|
||||
freeobj(L, curr); /* erase 'curr' */
|
||||
}
|
||||
else { /* change mark to 'white' */
|
||||
curr->marked = cast_byte((marked & maskcolors) | white);
|
||||
curr->marked = cast_byte((marked & ~maskgcbits) | white);
|
||||
p = &curr->next; /* go to next element */
|
||||
}
|
||||
}
|
||||
@@ -823,6 +884,8 @@ static GCObject *udata2finalize (global_State *g) {
|
||||
resetbit(o->marked, FINALIZEDBIT); /* object is "normal" again */
|
||||
if (issweepphase(g))
|
||||
makewhite(g, o); /* "sweep" object */
|
||||
else if (getage(o) == G_OLD1)
|
||||
g->firstold1 = o; /* it is the first OLD1 object in the list */
|
||||
return o;
|
||||
}
|
||||
|
||||
@@ -896,15 +959,15 @@ static GCObject **findlast (GCObject **p) {
|
||||
/*
|
||||
** Move all unreachable objects (or 'all' objects) that need
|
||||
** finalization from list 'finobj' to list 'tobefnz' (to be finalized).
|
||||
** (Note that objects after 'finobjold' cannot be white, so they
|
||||
** don't need to be traversed. In incremental mode, 'finobjold' is NULL,
|
||||
** (Note that objects after 'finobjold1' cannot be white, so they
|
||||
** don't need to be traversed. In incremental mode, 'finobjold1' is NULL,
|
||||
** so the whole list is traversed.)
|
||||
*/
|
||||
static void separatetobefnz (global_State *g, int all) {
|
||||
GCObject *curr;
|
||||
GCObject **p = &g->finobj;
|
||||
GCObject **lastnext = findlast(&g->tobefnz);
|
||||
while ((curr = *p) != g->finobjold) { /* traverse all finalizable objects */
|
||||
while ((curr = *p) != g->finobjold1) { /* traverse all finalizable objects */
|
||||
lua_assert(tofinalize(curr));
|
||||
if (!(iswhite(curr) || all)) /* not being collected? */
|
||||
p = &curr->next; /* don't bother with it */
|
||||
@@ -920,6 +983,27 @@ static void separatetobefnz (global_State *g, int all) {
|
||||
}
|
||||
|
||||
|
||||
/*
|
||||
** If pointer 'p' points to 'o', move it to the next element.
|
||||
*/
|
||||
static void checkpointer (GCObject **p, GCObject *o) {
|
||||
if (o == *p)
|
||||
*p = o->next;
|
||||
}
|
||||
|
||||
|
||||
/*
|
||||
** Correct pointers to objects inside 'allgc' list when
|
||||
** object 'o' is being removed from the list.
|
||||
*/
|
||||
static void correctpointers (global_State *g, GCObject *o) {
|
||||
checkpointer(&g->survival, o);
|
||||
checkpointer(&g->old1, o);
|
||||
checkpointer(&g->reallyold, o);
|
||||
checkpointer(&g->firstold1, o);
|
||||
}
|
||||
|
||||
|
||||
/*
|
||||
** if object 'o' has a finalizer, remove it from 'allgc' list (must
|
||||
** search the list to find it) and link it in 'finobj' list.
|
||||
@@ -936,14 +1020,8 @@ void luaC_checkfinalizer (lua_State *L, GCObject *o, Table *mt) {
|
||||
if (g->sweepgc == &o->next) /* should not remove 'sweepgc' object */
|
||||
g->sweepgc = sweeptolive(L, g->sweepgc); /* change 'sweepgc' */
|
||||
}
|
||||
else { /* correct pointers into 'allgc' list, if needed */
|
||||
if (o == g->survival)
|
||||
g->survival = o->next;
|
||||
if (o == g->old)
|
||||
g->old = o->next;
|
||||
if (o == g->reallyold)
|
||||
g->reallyold = o->next;
|
||||
}
|
||||
else
|
||||
correctpointers(g, o);
|
||||
/* search for pointer pointing to 'o' */
|
||||
for (p = &g->allgc; *p != o; p = &(*p)->next) { /* empty */ }
|
||||
*p = o->next; /* remove 'o' from 'allgc' list */
|
||||
@@ -965,24 +1043,31 @@ void luaC_checkfinalizer (lua_State *L, GCObject *o, Table *mt) {
|
||||
static void setpause (global_State *g);
|
||||
|
||||
|
||||
/* mask to erase all color bits, not changing gen-related stuff */
|
||||
#define maskgencolors (~(bitmask(BLACKBIT) | WHITEBITS))
|
||||
|
||||
|
||||
/*
|
||||
** Sweep a list of objects, deleting dead ones and turning
|
||||
** the non dead to old (without changing their colors).
|
||||
** Sweep a list of objects to enter generational mode. Deletes dead
|
||||
** objects and turns the non dead to old. All non-dead threads---which
|
||||
** are now old---must be in a gray list. Everything else is not in a
|
||||
** gray list. Open upvalues are also kept gray.
|
||||
*/
|
||||
static void sweep2old (lua_State *L, GCObject **p) {
|
||||
GCObject *curr;
|
||||
global_State *g = G(L);
|
||||
while ((curr = *p) != NULL) {
|
||||
if (iswhite(curr)) { /* is 'curr' dead? */
|
||||
lua_assert(isdead(G(L), curr));
|
||||
lua_assert(isdead(g, curr));
|
||||
*p = curr->next; /* remove 'curr' from list */
|
||||
freeobj(L, curr); /* erase 'curr' */
|
||||
}
|
||||
else { /* all surviving objects become old */
|
||||
setage(curr, G_OLD);
|
||||
if (curr->tt == LUA_VTHREAD) { /* threads must be watched */
|
||||
lua_State *th = gco2th(curr);
|
||||
linkgclist(th, g->grayagain); /* insert into 'grayagain' list */
|
||||
}
|
||||
else if (curr->tt == LUA_VUPVAL && upisopen(gco2upv(curr)))
|
||||
set2gray(curr); /* open upvalues are always gray */
|
||||
else /* everything else is black */
|
||||
nw2black(curr);
|
||||
p = &curr->next; /* go to next element */
|
||||
}
|
||||
}
|
||||
@@ -995,9 +1080,13 @@ static void sweep2old (lua_State *L, GCObject **p) {
|
||||
** during the sweep. So, any white object must be dead.) For
|
||||
** non-dead objects, advance their ages and clear the color of
|
||||
** new objects. (Old objects keep their colors.)
|
||||
** The ages of G_TOUCHED1 and G_TOUCHED2 objects cannot be advanced
|
||||
** here, because these old-generation objects are usually not swept
|
||||
** here. They will all be advanced in 'correctgraylist'. That function
|
||||
** will also remove objects turned white here from any gray list.
|
||||
*/
|
||||
static GCObject **sweepgen (lua_State *L, global_State *g, GCObject **p,
|
||||
GCObject *limit) {
|
||||
GCObject *limit, GCObject **pfirstold1) {
|
||||
static const lu_byte nextage[] = {
|
||||
G_SURVIVAL, /* from G_NEW */
|
||||
G_OLD1, /* from G_SURVIVAL */
|
||||
@@ -1016,9 +1105,15 @@ static GCObject **sweepgen (lua_State *L, global_State *g, GCObject **p,
|
||||
freeobj(L, curr); /* erase 'curr' */
|
||||
}
|
||||
else { /* correct mark and age */
|
||||
if (getage(curr) == G_NEW)
|
||||
curr->marked = cast_byte((curr->marked & maskgencolors) | white);
|
||||
setage(curr, nextage[getage(curr)]);
|
||||
if (getage(curr) == G_NEW) { /* new objects go back to white */
|
||||
int marked = curr->marked & ~maskgcbits; /* erase GC bits */
|
||||
curr->marked = cast_byte(marked | G_SURVIVAL | white);
|
||||
}
|
||||
else { /* all other objects will be old, and so keep their color */
|
||||
setage(curr, nextage[getage(curr)]);
|
||||
if (getage(curr) == G_OLD1 && *pfirstold1 == NULL)
|
||||
*pfirstold1 = curr; /* first OLD1 object in the list */
|
||||
}
|
||||
p = &curr->next; /* go to next element */
|
||||
}
|
||||
}
|
||||
@@ -1028,58 +1123,50 @@ static GCObject **sweepgen (lua_State *L, global_State *g, GCObject **p,
|
||||
|
||||
/*
|
||||
** Traverse a list making all its elements white and clearing their
|
||||
** age.
|
||||
** age. In incremental mode, all objects are 'new' all the time,
|
||||
** except for fixed strings (which are always old).
|
||||
*/
|
||||
static void whitelist (global_State *g, GCObject *p) {
|
||||
int white = luaC_white(g);
|
||||
for (; p != NULL; p = p->next)
|
||||
p->marked = cast_byte((p->marked & maskcolors) | white);
|
||||
p->marked = cast_byte((p->marked & ~maskgcbits) | white);
|
||||
}
|
||||
|
||||
|
||||
/*
|
||||
** Correct a list of gray objects.
|
||||
** Correct a list of gray objects. Return pointer to where rest of the
|
||||
** list should be linked.
|
||||
** Because this correction is done after sweeping, young objects might
|
||||
** be turned white and still be in the list. They are only removed.
|
||||
** For tables and userdata, advance 'touched1' to 'touched2'; 'touched2'
|
||||
** objects become regular old and are removed from the list.
|
||||
** For threads, just remove white ones from the list.
|
||||
** 'TOUCHED1' objects are advanced to 'TOUCHED2' and remain on the list;
|
||||
** Non-white threads also remain on the list; 'TOUCHED2' objects become
|
||||
** regular old; they and anything else are removed from the list.
|
||||
*/
|
||||
static GCObject **correctgraylist (GCObject **p) {
|
||||
GCObject *curr;
|
||||
while ((curr = *p) != NULL) {
|
||||
switch (curr->tt) {
|
||||
case LUA_TTABLE: case LUA_TUSERDATA: {
|
||||
GCObject **next = getgclist(curr);
|
||||
if (getage(curr) == G_TOUCHED1) { /* touched in this cycle? */
|
||||
lua_assert(isgray(curr));
|
||||
gray2black(curr); /* make it black, for next barrier */
|
||||
changeage(curr, G_TOUCHED1, G_TOUCHED2);
|
||||
p = next; /* go to next element */
|
||||
}
|
||||
else { /* not touched in this cycle */
|
||||
if (!iswhite(curr)) { /* not white? */
|
||||
lua_assert(isold(curr));
|
||||
if (getage(curr) == G_TOUCHED2) /* advance from G_TOUCHED2... */
|
||||
changeage(curr, G_TOUCHED2, G_OLD); /* ... to G_OLD */
|
||||
gray2black(curr); /* make it black */
|
||||
}
|
||||
/* else, object is white: just remove it from this list */
|
||||
*p = *next; /* remove 'curr' from gray list */
|
||||
}
|
||||
break;
|
||||
}
|
||||
case LUA_TTHREAD: {
|
||||
lua_State *th = gco2th(curr);
|
||||
lua_assert(!isblack(th));
|
||||
if (iswhite(th)) /* new object? */
|
||||
*p = th->gclist; /* remove from gray list */
|
||||
else /* old threads remain gray */
|
||||
p = &th->gclist; /* go to next element */
|
||||
break;
|
||||
}
|
||||
default: lua_assert(0); /* nothing more could be gray here */
|
||||
GCObject **next = getgclist(curr);
|
||||
if (iswhite(curr))
|
||||
goto remove; /* remove all white objects */
|
||||
else if (getage(curr) == G_TOUCHED1) { /* touched in this cycle? */
|
||||
lua_assert(isgray(curr));
|
||||
nw2black(curr); /* make it black, for next barrier */
|
||||
changeage(curr, G_TOUCHED1, G_TOUCHED2);
|
||||
goto remain; /* keep it in the list and go to next element */
|
||||
}
|
||||
else if (curr->tt == LUA_VTHREAD) {
|
||||
lua_assert(isgray(curr));
|
||||
goto remain; /* keep non-white threads on the list */
|
||||
}
|
||||
else { /* everything else is removed */
|
||||
lua_assert(isold(curr)); /* young objects should be white here */
|
||||
if (getage(curr) == G_TOUCHED2) /* advance from TOUCHED2... */
|
||||
changeage(curr, G_TOUCHED2, G_OLD); /* ... to OLD */
|
||||
nw2black(curr); /* make object black (to be removed) */
|
||||
goto remove;
|
||||
}
|
||||
remove: *p = *next; continue;
|
||||
remain: p = next; continue;
|
||||
}
|
||||
return p;
|
||||
}
|
||||
@@ -1100,7 +1187,7 @@ static void correctgraylists (global_State *g) {
|
||||
|
||||
|
||||
/*
|
||||
** Mark 'OLD1' objects when starting a new young collection.
|
||||
** Mark black 'OLD1' objects when starting a new young collection.
|
||||
** Gray objects are already in some gray list, and so will be visited
|
||||
** in the atomic step.
|
||||
*/
|
||||
@@ -1109,10 +1196,9 @@ static void markold (global_State *g, GCObject *from, GCObject *to) {
|
||||
for (p = from; p != to; p = p->next) {
|
||||
if (getage(p) == G_OLD1) {
|
||||
lua_assert(!iswhite(p));
|
||||
if (isblack(p)) {
|
||||
black2gray(p); /* should be '2white', but gray works too */
|
||||
changeage(p, G_OLD1, G_OLD); /* now they are old */
|
||||
if (isblack(p))
|
||||
reallymarkobject(g, p);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1131,50 +1217,63 @@ static void finishgencycle (lua_State *L, global_State *g) {
|
||||
|
||||
|
||||
/*
|
||||
** Does a young collection. First, mark 'OLD1' objects. (Only survival
|
||||
** and "recent old" lists can contain 'OLD1' objects. New lists cannot
|
||||
** contain 'OLD1' objects, at most 'OLD0' objects that were already
|
||||
** visited when marked old.) Then does the atomic step. Then,
|
||||
** sweep all lists and advance pointers. Finally, finish the collection.
|
||||
** Does a young collection. First, mark 'OLD1' objects. Then does the
|
||||
** atomic step. Then, sweep all lists and advance pointers. Finally,
|
||||
** finish the collection.
|
||||
*/
|
||||
static void youngcollection (lua_State *L, global_State *g) {
|
||||
GCObject **psurvival; /* to point to first non-dead survival object */
|
||||
GCObject *dummy; /* dummy out parameter to 'sweepgen' */
|
||||
lua_assert(g->gcstate == GCSpropagate);
|
||||
markold(g, g->survival, g->reallyold);
|
||||
if (g->firstold1) { /* are there regular OLD1 objects? */
|
||||
markold(g, g->firstold1, g->reallyold); /* mark them */
|
||||
g->firstold1 = NULL; /* no more OLD1 objects (for now) */
|
||||
}
|
||||
markold(g, g->finobj, g->finobjrold);
|
||||
markold(g, g->tobefnz, NULL);
|
||||
atomic(L);
|
||||
|
||||
/* sweep nursery and get a pointer to its last live element */
|
||||
psurvival = sweepgen(L, g, &g->allgc, g->survival);
|
||||
/* sweep 'survival' and 'old' */
|
||||
sweepgen(L, g, psurvival, g->reallyold);
|
||||
g->reallyold = g->old;
|
||||
g->old = *psurvival; /* 'survival' survivals are old now */
|
||||
g->gcstate = GCSswpallgc;
|
||||
psurvival = sweepgen(L, g, &g->allgc, g->survival, &g->firstold1);
|
||||
/* sweep 'survival' */
|
||||
sweepgen(L, g, psurvival, g->old1, &g->firstold1);
|
||||
g->reallyold = g->old1;
|
||||
g->old1 = *psurvival; /* 'survival' survivals are old now */
|
||||
g->survival = g->allgc; /* all news are survivals */
|
||||
|
||||
/* repeat for 'finobj' lists */
|
||||
psurvival = sweepgen(L, g, &g->finobj, g->finobjsur);
|
||||
/* sweep 'survival' and 'old' */
|
||||
sweepgen(L, g, psurvival, g->finobjrold);
|
||||
g->finobjrold = g->finobjold;
|
||||
g->finobjold = *psurvival; /* 'survival' survivals are old now */
|
||||
dummy = NULL; /* no 'firstold1' optimization for 'finobj' lists */
|
||||
psurvival = sweepgen(L, g, &g->finobj, g->finobjsur, &dummy);
|
||||
/* sweep 'survival' */
|
||||
sweepgen(L, g, psurvival, g->finobjold1, &dummy);
|
||||
g->finobjrold = g->finobjold1;
|
||||
g->finobjold1 = *psurvival; /* 'survival' survivals are old now */
|
||||
g->finobjsur = g->finobj; /* all news are survivals */
|
||||
|
||||
sweepgen(L, g, &g->tobefnz, NULL);
|
||||
|
||||
sweepgen(L, g, &g->tobefnz, NULL, &dummy);
|
||||
finishgencycle(L, g);
|
||||
}
|
||||
|
||||
|
||||
/*
|
||||
** Clears all gray lists, sweeps objects, and prepare sublists to enter
|
||||
** generational mode. The sweeps remove dead objects and turn all
|
||||
** surviving objects to old. Threads go back to 'grayagain'; everything
|
||||
** else is turned black (not in any gray list).
|
||||
*/
|
||||
static void atomic2gen (lua_State *L, global_State *g) {
|
||||
cleargraylists(g);
|
||||
/* sweep all elements making them old */
|
||||
g->gcstate = GCSswpallgc;
|
||||
sweep2old(L, &g->allgc);
|
||||
/* everything alive now is old */
|
||||
g->reallyold = g->old = g->survival = g->allgc;
|
||||
g->reallyold = g->old1 = g->survival = g->allgc;
|
||||
g->firstold1 = NULL; /* there are no OLD1 objects anywhere */
|
||||
|
||||
/* repeat for 'finobj' lists */
|
||||
sweep2old(L, &g->finobj);
|
||||
g->finobjrold = g->finobjold = g->finobjsur = g->finobj;
|
||||
g->finobjrold = g->finobjold1 = g->finobjsur = g->finobj;
|
||||
|
||||
sweep2old(L, &g->tobefnz);
|
||||
|
||||
@@ -1187,8 +1286,9 @@ static void atomic2gen (lua_State *L, global_State *g) {
|
||||
|
||||
/*
|
||||
** Enter generational mode. Must go until the end of an atomic cycle
|
||||
** to ensure that all threads and weak tables are in the gray lists.
|
||||
** Then, turn all objects into old and finishes the collection.
|
||||
** to ensure that all objects are correctly marked and weak tables
|
||||
** are cleared. Then, turn all objects into old and finishes the
|
||||
** collection.
|
||||
*/
|
||||
static lu_mem entergen (lua_State *L, global_State *g) {
|
||||
lu_mem numobjs;
|
||||
@@ -1207,10 +1307,10 @@ static lu_mem entergen (lua_State *L, global_State *g) {
|
||||
*/
|
||||
static void enterinc (global_State *g) {
|
||||
whitelist(g, g->allgc);
|
||||
g->reallyold = g->old = g->survival = NULL;
|
||||
g->reallyold = g->old1 = g->survival = NULL;
|
||||
whitelist(g, g->finobj);
|
||||
whitelist(g, g->tobefnz);
|
||||
g->finobjrold = g->finobjold = g->finobjsur = NULL;
|
||||
g->finobjrold = g->finobjold1 = g->finobjsur = NULL;
|
||||
g->gcstate = GCSpause;
|
||||
g->gckind = KGC_INC;
|
||||
g->lastatomic = 0;
|
||||
@@ -1565,6 +1665,7 @@ static void incstep (lua_State *L, global_State *g) {
|
||||
*/
|
||||
void luaC_step (lua_State *L) {
|
||||
global_State *g = G(L);
|
||||
lua_assert(!g->gcemergency);
|
||||
if (g->gcrunning) { /* running? */
|
||||
if(isdecGCmodegen(g))
|
||||
genstep(L, g);
|
||||
|
||||
@@ -12,16 +12,16 @@
|
||||
#include "lstate.h"
|
||||
|
||||
/*
|
||||
** Collectable objects may have one of three colors: white, which
|
||||
** means the object is not marked; gray, which means the
|
||||
** object is marked, but its references may be not marked; and
|
||||
** black, which means that the object and all its references are marked.
|
||||
** The main invariant of the garbage collector, while marking objects,
|
||||
** is that a black object can never point to a white one. Moreover,
|
||||
** any gray object must be in a "gray list" (gray, grayagain, weak,
|
||||
** allweak, ephemeron) so that it can be visited again before finishing
|
||||
** the collection cycle. These lists have no meaning when the invariant
|
||||
** is not being enforced (e.g., sweep phase).
|
||||
** Collectable objects may have one of three colors: white, which means
|
||||
** the object is not marked; gray, which means the object is marked, but
|
||||
** its references may be not marked; and black, which means that the
|
||||
** object and all its references are marked. The main invariant of the
|
||||
** garbage collector, while marking objects, is that a black object can
|
||||
** never point to a white one. Moreover, any gray object must be in a
|
||||
** "gray list" (gray, grayagain, weak, allweak, ephemeron) so that it
|
||||
** can be visited again before finishing the collection cycle. (Open
|
||||
** upvalues are an exception to this rule.) These lists have no meaning
|
||||
** when the invariant is not being enforced (e.g., sweep phase).
|
||||
*/
|
||||
|
||||
|
||||
@@ -69,14 +69,16 @@
|
||||
|
||||
/*
|
||||
** Layout for bit use in 'marked' field. First three bits are
|
||||
** used for object "age" in generational mode. Last bit is free
|
||||
** to be used by respective objects.
|
||||
** used for object "age" in generational mode. Last bit is used
|
||||
** by tests.
|
||||
*/
|
||||
#define WHITE0BIT 3 /* object is white (type 0) */
|
||||
#define WHITE1BIT 4 /* object is white (type 1) */
|
||||
#define BLACKBIT 5 /* object is black */
|
||||
#define FINALIZEDBIT 6 /* object has been marked for finalization */
|
||||
|
||||
#define TESTBIT 7
|
||||
|
||||
|
||||
|
||||
#define WHITEBITS bit2mask(WHITE0BIT, WHITE1BIT)
|
||||
@@ -94,7 +96,8 @@
|
||||
#define isdead(g,v) isdeadm(otherwhite(g), (v)->marked)
|
||||
|
||||
#define changewhite(x) ((x)->marked ^= WHITEBITS)
|
||||
#define gray2black(x) l_setbit((x)->marked, BLACKBIT)
|
||||
#define nw2black(x) \
|
||||
check_exp(!iswhite(x), l_setbit((x)->marked, BLACKBIT))
|
||||
|
||||
#define luaC_white(g) cast_byte((g)->currentwhite & WHITEBITS)
|
||||
|
||||
|
||||
@@ -52,6 +52,12 @@ static int l_checkmode (const char *mode) {
|
||||
** =======================================================
|
||||
*/
|
||||
|
||||
#if !defined(l_checkmodep)
|
||||
/* By default, Lua accepts only "r" or "w" as mode */
|
||||
#define l_checkmodep(m) ((m[0] == 'r' || m[0] == 'w') && m[1] == '\0')
|
||||
#endif
|
||||
|
||||
|
||||
#if !defined(l_popen) /* { */
|
||||
|
||||
#if defined(LUA_USE_POSIX) /* { */
|
||||
@@ -215,7 +221,7 @@ static int f_close (lua_State *L) {
|
||||
|
||||
static int io_close (lua_State *L) {
|
||||
if (lua_isnone(L, 1)) /* no argument? */
|
||||
lua_getfield(L, LUA_REGISTRYINDEX, IO_OUTPUT); /* use standard output */
|
||||
lua_getfield(L, LUA_REGISTRYINDEX, IO_OUTPUT); /* use default output */
|
||||
return f_close(L);
|
||||
}
|
||||
|
||||
@@ -270,6 +276,7 @@ static int io_open (lua_State *L) {
|
||||
*/
|
||||
static int io_pclose (lua_State *L) {
|
||||
LStream *p = tolstream(L);
|
||||
errno = 0;
|
||||
return luaL_execresult(L, l_pclose(L, p->f));
|
||||
}
|
||||
|
||||
@@ -278,6 +285,7 @@ static int io_popen (lua_State *L) {
|
||||
const char *filename = luaL_checkstring(L, 1);
|
||||
const char *mode = luaL_optstring(L, 2, "r");
|
||||
LStream *p = newprefile(L);
|
||||
luaL_argcheck(L, l_checkmodep(mode), 2, "invalid mode");
|
||||
p->f = l_popen(L, filename, mode);
|
||||
p->closef = &io_pclose;
|
||||
return (p->f == NULL) ? luaL_fileresult(L, 0, filename) : 1;
|
||||
@@ -296,7 +304,7 @@ static FILE *getiofile (lua_State *L, const char *findex) {
|
||||
lua_getfield(L, LUA_REGISTRYINDEX, findex);
|
||||
p = (LStream *)lua_touserdata(L, -1);
|
||||
if (isclosed(p))
|
||||
luaL_error(L, "standard %s file is closed", findex + IOPREF_LEN);
|
||||
luaL_error(L, "default %s file is closed", findex + IOPREF_LEN);
|
||||
return p->f;
|
||||
}
|
||||
|
||||
|
||||
+4
-2
@@ -16,7 +16,7 @@
|
||||
#define vmbreak vmfetch(); vmdispatch(GET_OPCODE(i));
|
||||
|
||||
|
||||
static void *disptab[NUM_OPCODES] = {
|
||||
static const void *const disptab[NUM_OPCODES] = {
|
||||
|
||||
#if 0
|
||||
** you can update the following list with this command:
|
||||
@@ -30,7 +30,9 @@ static void *disptab[NUM_OPCODES] = {
|
||||
&&L_OP_LOADF,
|
||||
&&L_OP_LOADK,
|
||||
&&L_OP_LOADKX,
|
||||
&&L_OP_LOADBOOL,
|
||||
&&L_OP_LOADFALSE,
|
||||
&&L_OP_LFALSESKIP,
|
||||
&&L_OP_LOADTRUE,
|
||||
&&L_OP_LOADNIL,
|
||||
&&L_OP_GETUPVAL,
|
||||
&&L_OP_SETUPVAL,
|
||||
|
||||
@@ -81,7 +81,6 @@ void luaX_init (lua_State *L) {
|
||||
|
||||
const char *luaX_token2str (LexState *ls, int token) {
|
||||
if (token < FIRST_RESERVED) { /* single-byte symbols? */
|
||||
lua_assert(token == cast_uchar(token));
|
||||
if (lisprint(token))
|
||||
return luaO_pushfstring(ls->L, "'%c'", token);
|
||||
else /* control character */
|
||||
@@ -136,7 +135,7 @@ TString *luaX_newstring (LexState *ls, const char *str, size_t l) {
|
||||
if (isempty(o)) { /* not in use yet? */
|
||||
/* boolean value does not need GC barrier;
|
||||
table is not a metatable, so it does not need to invalidate cache */
|
||||
setbvalue(o, 1); /* t[string] = true */
|
||||
setbtvalue(o); /* t[string] = true */
|
||||
luaC_checkGC(L);
|
||||
}
|
||||
else { /* string already present */
|
||||
@@ -255,9 +254,10 @@ static int read_numeral (LexState *ls, SemInfo *seminfo) {
|
||||
|
||||
|
||||
/*
|
||||
** reads a sequence '[=*[' or ']=*]', leaving the last bracket.
|
||||
** If sequence is well formed, return its number of '='s + 2; otherwise,
|
||||
** return 1 if there is no '='s or 0 otherwise (an unfinished '[==...').
|
||||
** read a sequence '[=*[' or ']=*]', leaving the last bracket. If
|
||||
** sequence is well formed, return its number of '='s + 2; otherwise,
|
||||
** return 1 if it is a single bracket (no '='s and no 2nd bracket);
|
||||
** otherwise (an unfinished '[==...') return 0.
|
||||
*/
|
||||
static size_t skip_sep (LexState *ls) {
|
||||
size_t count = 0;
|
||||
@@ -482,34 +482,34 @@ static int llex (LexState *ls, SemInfo *seminfo) {
|
||||
}
|
||||
case '=': {
|
||||
next(ls);
|
||||
if (check_next1(ls, '=')) return TK_EQ;
|
||||
if (check_next1(ls, '=')) return TK_EQ; /* '==' */
|
||||
else return '=';
|
||||
}
|
||||
case '<': {
|
||||
next(ls);
|
||||
if (check_next1(ls, '=')) return TK_LE;
|
||||
else if (check_next1(ls, '<')) return TK_SHL;
|
||||
if (check_next1(ls, '=')) return TK_LE; /* '<=' */
|
||||
else if (check_next1(ls, '<')) return TK_SHL; /* '<<' */
|
||||
else return '<';
|
||||
}
|
||||
case '>': {
|
||||
next(ls);
|
||||
if (check_next1(ls, '=')) return TK_GE;
|
||||
else if (check_next1(ls, '>')) return TK_SHR;
|
||||
if (check_next1(ls, '=')) return TK_GE; /* '>=' */
|
||||
else if (check_next1(ls, '>')) return TK_SHR; /* '>>' */
|
||||
else return '>';
|
||||
}
|
||||
case '/': {
|
||||
next(ls);
|
||||
if (check_next1(ls, '/')) return TK_IDIV;
|
||||
if (check_next1(ls, '/')) return TK_IDIV; /* '//' */
|
||||
else return '/';
|
||||
}
|
||||
case '~': {
|
||||
next(ls);
|
||||
if (check_next1(ls, '=')) return TK_NE;
|
||||
if (check_next1(ls, '=')) return TK_NE; /* '~=' */
|
||||
else return '~';
|
||||
}
|
||||
case ':': {
|
||||
next(ls);
|
||||
if (check_next1(ls, ':')) return TK_DBCOLON;
|
||||
if (check_next1(ls, ':')) return TK_DBCOLON; /* '::' */
|
||||
else return ':';
|
||||
}
|
||||
case '"': case '\'': { /* short literal strings */
|
||||
@@ -548,7 +548,7 @@ static int llex (LexState *ls, SemInfo *seminfo) {
|
||||
return TK_NAME;
|
||||
}
|
||||
}
|
||||
else { /* single-char tokens (+ - / ...) */
|
||||
else { /* single-char tokens ('+', '*', '%', '{', '}', ...) */
|
||||
int c = ls->current;
|
||||
next(ls);
|
||||
return c;
|
||||
|
||||
@@ -7,11 +7,17 @@
|
||||
#ifndef llex_h
|
||||
#define llex_h
|
||||
|
||||
#include <limits.h>
|
||||
|
||||
#include "lobject.h"
|
||||
#include "lzio.h"
|
||||
|
||||
|
||||
#define FIRST_RESERVED 257
|
||||
/*
|
||||
** Single-char tokens (terminal symbols) are represented by their own
|
||||
** numeric code. Other tokens start at the following value.
|
||||
*/
|
||||
#define FIRST_RESERVED (UCHAR_MAX + 1)
|
||||
|
||||
|
||||
#if !defined(LUA_ENV)
|
||||
|
||||
@@ -84,7 +84,15 @@ typedef LUAI_UACNUMBER l_uacNumber;
|
||||
typedef LUAI_UACINT l_uacInt;
|
||||
|
||||
|
||||
/* internal assertions for in-house debugging */
|
||||
/*
|
||||
** Internal assertions for in-house debugging
|
||||
*/
|
||||
#if defined LUAI_ASSERT
|
||||
#undef NDEBUG
|
||||
#include <assert.h>
|
||||
#define lua_assert(c) assert(c)
|
||||
#endif
|
||||
|
||||
#if defined(lua_assert)
|
||||
#define check_exp(c,e) (lua_assert(c), (e))
|
||||
/* to avoid problems with conditions too long */
|
||||
@@ -99,7 +107,7 @@ typedef LUAI_UACINT l_uacInt;
|
||||
** assertion for checking API calls
|
||||
*/
|
||||
#if !defined(luai_apicheck)
|
||||
#define luai_apicheck(l,e) lua_assert(e)
|
||||
#define luai_apicheck(l,e) ((void)l, lua_assert(e))
|
||||
#endif
|
||||
|
||||
#define api_check(l,e,msg) luai_apicheck(l,(e) && msg)
|
||||
@@ -226,6 +234,17 @@ typedef l_uint32 Instruction;
|
||||
#endif
|
||||
|
||||
|
||||
/*
|
||||
** Maximum depth for nested C calls, syntactical nested non-terminals,
|
||||
** and other features implemented through recursion in C. (Value must
|
||||
** fit in a 16-bit unsigned integer. It must also be compatible with
|
||||
** the size of the C stack.)
|
||||
*/
|
||||
#if !defined(LUAI_MAXCCALLS)
|
||||
#define LUAI_MAXCCALLS 200
|
||||
#endif
|
||||
|
||||
|
||||
/*
|
||||
** macros that are executed whenever program enters the Lua core
|
||||
** ('lua_lock') and leaves the core ('lua_unlock')
|
||||
@@ -307,7 +326,8 @@ typedef l_uint32 Instruction;
|
||||
|
||||
/* exponentiation */
|
||||
#if !defined(luai_numpow)
|
||||
#define luai_numpow(L,a,b) ((void)L, l_mathop(pow)(a,b))
|
||||
#define luai_numpow(L,a,b) \
|
||||
((void)L, (b == 2) ? (a)*(a) : l_mathop(pow)(a,b))
|
||||
#endif
|
||||
|
||||
/* the others are quite standard operations */
|
||||
@@ -336,7 +356,7 @@ typedef l_uint32 Instruction;
|
||||
#else
|
||||
/* realloc stack keeping its size */
|
||||
#define condmovestack(L,pre,pos) \
|
||||
{ int sz_ = (L)->stacksize; pre; luaD_reallocstack((L), sz_, 0); pos; }
|
||||
{ int sz_ = stacksize(L); pre; luaD_reallocstack((L), sz_, 0); pos; }
|
||||
#endif
|
||||
|
||||
#if !defined(HARDMEMTESTS)
|
||||
|
||||
+17
-15
@@ -249,7 +249,7 @@ static int math_type (lua_State *L) {
|
||||
*/
|
||||
|
||||
/* number of binary digits in the mantissa of a float */
|
||||
#define FIGS l_mathlim(MANT_DIG)
|
||||
#define FIGS l_floatatt(MANT_DIG)
|
||||
|
||||
#if FIGS > 64
|
||||
/* there are only 64 random bits; use them all */
|
||||
@@ -328,7 +328,7 @@ static Rand64 nextrand (Rand64 *state) {
|
||||
*/
|
||||
|
||||
/* must throw out the extra (64 - FIGS) bits */
|
||||
#define shift64_FIG (64 - FIGS)
|
||||
#define shift64_FIG (64 - FIGS)
|
||||
|
||||
/* to scale to [0, 1), multiply by scaleFIG = 2^(-FIGS) */
|
||||
#define scaleFIG (l_mathop(0.5) / ((Rand64)1 << (FIGS - 1)))
|
||||
@@ -522,16 +522,18 @@ typedef struct {
|
||||
** Project the random integer 'ran' into the interval [0, n].
|
||||
** Because 'ran' has 2^B possible values, the projection can only be
|
||||
** uniform when the size of the interval is a power of 2 (exact
|
||||
** division). To get a uniform projection into [0, n], we first compute
|
||||
** 'lim', the smallest Mersenne number not smaller than 'n'. We then
|
||||
** project 'ran' into the interval [0, lim]. If the result is inside
|
||||
** [0, n], we are done. Otherwise, we try with another 'ran', until we
|
||||
** have a result inside the interval.
|
||||
** division). Otherwise, to get a uniform projection into [0, n], we
|
||||
** first compute 'lim', the smallest Mersenne number not smaller than
|
||||
** 'n'. We then project 'ran' into the interval [0, lim]. If the result
|
||||
** is inside [0, n], we are done. Otherwise, we try with another 'ran',
|
||||
** until we have a result inside the interval.
|
||||
*/
|
||||
static lua_Unsigned project (lua_Unsigned ran, lua_Unsigned n,
|
||||
RanState *state) {
|
||||
lua_Unsigned lim = n;
|
||||
if ((lim & (lim + 1)) > 0) { /* 'lim + 1' is not a power of 2? */
|
||||
if ((n & (n + 1)) == 0) /* is 'n + 1' a power of 2? */
|
||||
return ran & n; /* no bias */
|
||||
else {
|
||||
lua_Unsigned lim = n;
|
||||
/* compute the smallest (2^b - 1) not smaller than 'n' */
|
||||
lim |= (lim >> 1);
|
||||
lim |= (lim >> 2);
|
||||
@@ -541,13 +543,13 @@ static lua_Unsigned project (lua_Unsigned ran, lua_Unsigned n,
|
||||
#if (LUA_MAXUNSIGNED >> 31) >= 3
|
||||
lim |= (lim >> 32); /* integer type has more than 32 bits */
|
||||
#endif
|
||||
lua_assert((lim & (lim + 1)) == 0 /* 'lim + 1' is a power of 2, */
|
||||
&& lim >= n /* not smaller than 'n', */
|
||||
&& (lim >> 1) < n); /* and it is the smallest one */
|
||||
while ((ran &= lim) > n) /* project 'ran' into [0..lim] */
|
||||
ran = I2UInt(nextrand(state->s)); /* not inside [0..n]? try again */
|
||||
return ran;
|
||||
}
|
||||
lua_assert((lim & (lim + 1)) == 0 /* 'lim + 1' is a power of 2, */
|
||||
&& lim >= n /* not smaller than 'n', */
|
||||
&& (lim == 0 || (lim >> 1) < n)); /* and it is the smallest one */
|
||||
while ((ran &= lim) > n) /* project 'ran' into [0..lim] */
|
||||
ran = I2UInt(nextrand(state->s)); /* not inside [0..n]? try again */
|
||||
return ran;
|
||||
}
|
||||
|
||||
|
||||
|
||||
@@ -22,11 +22,11 @@
|
||||
#include "lstate.h"
|
||||
|
||||
|
||||
#if defined(HARDMEMTESTS)
|
||||
#if defined(EMERGENCYGCTESTS)
|
||||
/*
|
||||
** 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.)
|
||||
** a full GC cycle at every allocation.)
|
||||
*/
|
||||
static void *firsttry (global_State *g, void *block, size_t os, size_t ns) {
|
||||
if (ttisnil(&g->nilvalue) && ns > os)
|
||||
|
||||
@@ -67,6 +67,13 @@ static const char *const CLIBS = "_CLIBS";
|
||||
#define setprogdir(L) ((void)0)
|
||||
|
||||
|
||||
/*
|
||||
** Special type equivalent to '(void*)' for functions in gcc
|
||||
** (to suppress warnings when converting function pointers)
|
||||
*/
|
||||
typedef void (*voidf)(void);
|
||||
|
||||
|
||||
/*
|
||||
** system-dependent functions
|
||||
*/
|
||||
@@ -206,7 +213,7 @@ static void *lsys_load (lua_State *L, const char *path, int seeglb) {
|
||||
|
||||
|
||||
static lua_CFunction lsys_sym (lua_State *L, void *lib, const char *sym) {
|
||||
lua_CFunction f = (lua_CFunction)GetProcAddress((HMODULE)lib, sym);
|
||||
lua_CFunction f = (lua_CFunction)(voidf)GetProcAddress((HMODULE)lib, sym);
|
||||
if (f == NULL) pusherror(L);
|
||||
return f;
|
||||
}
|
||||
@@ -269,8 +276,6 @@ static lua_CFunction lsys_sym (lua_State *L, void *lib, const char *sym) {
|
||||
#endif
|
||||
|
||||
|
||||
#define AUXMARK "\1" /* auxiliary mark */
|
||||
|
||||
|
||||
/*
|
||||
** return registry.LUA_NOENV as a boolean
|
||||
@@ -458,13 +463,13 @@ static const char *getnextfilename (char **path, char *end) {
|
||||
/*
|
||||
** Given a path such as ";blabla.so;blublu.so", pushes the string
|
||||
**
|
||||
** no file 'blabla.so'
|
||||
** no file 'blabla.so'
|
||||
** no file 'blublu.so'
|
||||
*/
|
||||
static void pusherrornotfound (lua_State *L, const char *path) {
|
||||
luaL_Buffer b;
|
||||
luaL_buffinit(L, &b);
|
||||
luaL_addstring(&b, "\n\tno file '");
|
||||
luaL_addstring(&b, "no file '");
|
||||
luaL_addgsub(&b, path, LUA_PATH_SEP, "'\n\tno file '");
|
||||
luaL_addstring(&b, "'");
|
||||
luaL_pushresult(&b);
|
||||
@@ -591,7 +596,7 @@ static int searcher_Croot (lua_State *L) {
|
||||
if (stat != ERRFUNC)
|
||||
return checkload(L, 0, filename); /* real error */
|
||||
else { /* open function not found */
|
||||
lua_pushfstring(L, "\n\tno module '%s' in file '%s'", name, filename);
|
||||
lua_pushfstring(L, "no module '%s' in file '%s'", name, filename);
|
||||
return 1;
|
||||
}
|
||||
}
|
||||
@@ -604,7 +609,7 @@ static int searcher_preload (lua_State *L) {
|
||||
const char *name = luaL_checkstring(L, 1);
|
||||
lua_getfield(L, LUA_REGISTRYINDEX, LUA_PRELOAD_TABLE);
|
||||
if (lua_getfield(L, -1, name) == LUA_TNIL) { /* not found? */
|
||||
lua_pushfstring(L, "\n\tno field package.preload['%s']", name);
|
||||
lua_pushfstring(L, "no field package.preload['%s']", name);
|
||||
return 1;
|
||||
}
|
||||
else {
|
||||
@@ -623,8 +628,10 @@ static void findloader (lua_State *L, const char *name) {
|
||||
luaL_buffinit(L, &msg);
|
||||
/* iterate over available searchers to find a loader */
|
||||
for (i = 1; ; i++) {
|
||||
luaL_addstring(&msg, "\n\t"); /* error-message prefix */
|
||||
if (lua_rawgeti(L, 3, i) == LUA_TNIL) { /* no more searchers? */
|
||||
lua_pop(L, 1); /* remove nil */
|
||||
luaL_buffsub(&msg, 2); /* remove prefix */
|
||||
luaL_pushresult(&msg); /* create error message */
|
||||
luaL_error(L, "module '%s' not found:%s", name, lua_tostring(L, -1));
|
||||
}
|
||||
@@ -636,8 +643,10 @@ static void findloader (lua_State *L, const char *name) {
|
||||
lua_pop(L, 1); /* remove extra return */
|
||||
luaL_addvalue(&msg); /* concatenate error message */
|
||||
}
|
||||
else
|
||||
else { /* no error message */
|
||||
lua_pop(L, 2); /* remove both returns */
|
||||
luaL_buffsub(&msg, 2); /* remove prefix */
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -215,37 +215,42 @@ static lua_Number lua_strx2number (const char *s, char **endptr) {
|
||||
/* }====================================================== */
|
||||
|
||||
|
||||
/* maximum length of a numeral */
|
||||
/* maximum length of a numeral to be converted to a number */
|
||||
#if !defined (L_MAXLENNUM)
|
||||
#define L_MAXLENNUM 200
|
||||
#endif
|
||||
|
||||
/*
|
||||
** Convert string 's' to a Lua number (put in 'result'). Return NULL on
|
||||
** fail or the address of the ending '\0' on success. ('mode' == 'x')
|
||||
** means a hexadecimal numeral.
|
||||
*/
|
||||
static const char *l_str2dloc (const char *s, lua_Number *result, int mode) {
|
||||
char *endptr;
|
||||
*result = (mode == 'x') ? lua_strx2number(s, &endptr) /* try to convert */
|
||||
: lua_str2number(s, &endptr);
|
||||
if (endptr == s) return NULL; /* nothing recognized? */
|
||||
while (lisspace(cast_uchar(*endptr))) endptr++; /* skip trailing spaces */
|
||||
return (*endptr == '\0') ? endptr : NULL; /* OK if no trailing characters */
|
||||
return (*endptr == '\0') ? endptr : NULL; /* OK iff no trailing chars */
|
||||
}
|
||||
|
||||
|
||||
/*
|
||||
** Convert string 's' to a Lua number (put in 'result'). Return NULL
|
||||
** on fail or the address of the ending '\0' on success.
|
||||
** 'pmode' points to (and 'mode' contains) special things in the string:
|
||||
** - 'x'/'X' means a hexadecimal numeral
|
||||
** - 'n'/'N' means 'inf' or 'nan' (which should be rejected)
|
||||
** - '.' just optimizes the search for the common case (nothing special)
|
||||
** Convert string 's' to a Lua number (put in 'result') handling the
|
||||
** current locale.
|
||||
** This function accepts both the current locale or a dot as the radix
|
||||
** mark. If the conversion fails, it may mean number has a dot but
|
||||
** locale accepts something else. In that case, the code copies 's'
|
||||
** to a buffer (because 's' is read-only), changes the dot to the
|
||||
** current locale radix mark, and tries to convert again.
|
||||
** The variable 'mode' checks for special characters in the string:
|
||||
** - 'n' means 'inf' or 'nan' (which should be rejected)
|
||||
** - 'x' means a hexadecimal numeral
|
||||
** - '.' just optimizes the search for the common case (no special chars)
|
||||
*/
|
||||
static const char *l_str2d (const char *s, lua_Number *result) {
|
||||
const char *endptr;
|
||||
const char *pmode = strpbrk(s, ".xXnN");
|
||||
const char *pmode = strpbrk(s, ".xXnN"); /* look for special chars */
|
||||
int mode = pmode ? ltolower(cast_uchar(*pmode)) : 0;
|
||||
if (mode == 'n') /* reject 'inf' and 'nan' */
|
||||
return NULL;
|
||||
@@ -253,7 +258,7 @@ static const char *l_str2d (const char *s, lua_Number *result) {
|
||||
if (endptr == NULL) { /* failed? may be a different locale */
|
||||
char buff[L_MAXLENNUM + 1];
|
||||
const char *pdot = strchr(s, '.');
|
||||
if (strlen(s) > L_MAXLENNUM || pdot == NULL)
|
||||
if (pdot == NULL || strlen(s) > L_MAXLENNUM)
|
||||
return NULL; /* string too long or no dot; fail */
|
||||
strcpy(buff, s); /* copy string to buffer */
|
||||
buff[pdot - s] = lua_getlocaledecpoint(); /* correct decimal point */
|
||||
@@ -333,8 +338,15 @@ int luaO_utf8esc (char *buff, unsigned long x) {
|
||||
}
|
||||
|
||||
|
||||
/* maximum length of the conversion of a number to a string */
|
||||
#define MAXNUMBER2STR 50
|
||||
/*
|
||||
** Maximum length of the conversion of a number to a string. Must be
|
||||
** enough to accommodate both LUA_INTEGER_FMT and LUA_NUMBER_FMT.
|
||||
** (For a long long int, this is 19 digits plus a sign and a final '\0',
|
||||
** adding to 21. For a long double, it can go to a sign, 33 digits,
|
||||
** the dot, an exponent letter, an exponent sign, 5 exponent digits,
|
||||
** and a final '\0', adding to 43.)
|
||||
*/
|
||||
#define MAXNUMBER2STR 44
|
||||
|
||||
|
||||
/*
|
||||
@@ -375,7 +387,7 @@ void luaO_tostring (lua_State *L, TValue *obj) {
|
||||
*/
|
||||
|
||||
/* size for buffer space used by 'luaO_pushvfstring' */
|
||||
#define BUFVFS 400
|
||||
#define BUFVFS 200
|
||||
|
||||
/* buffer used by 'luaO_pushvfstring' */
|
||||
typedef struct BuffFS {
|
||||
@@ -387,18 +399,16 @@ typedef struct BuffFS {
|
||||
|
||||
|
||||
/*
|
||||
** Push given string to the stack, as part of the buffer. If the stack
|
||||
** is almost full, join all partial strings in the stack into one.
|
||||
** Push given string to the stack, as part of the buffer, and
|
||||
** join the partial strings in the stack into one.
|
||||
*/
|
||||
static void pushstr (BuffFS *buff, const char *str, size_t l) {
|
||||
lua_State *L = buff->L;
|
||||
setsvalue2s(L, L->top, luaS_newlstr(L, str, l));
|
||||
L->top++; /* may use one extra slot */
|
||||
buff->pushed++;
|
||||
if (buff->pushed > 1 && L->top + 1 >= L->stack_last) {
|
||||
luaV_concat(L, buff->pushed); /* join all partial results into one */
|
||||
buff->pushed = 1;
|
||||
}
|
||||
luaV_concat(L, buff->pushed); /* join partial results into one */
|
||||
buff->pushed = 1;
|
||||
}
|
||||
|
||||
|
||||
@@ -521,8 +531,7 @@ const char *luaO_pushvfstring (lua_State *L, const char *fmt, va_list argp) {
|
||||
}
|
||||
addstr2buff(&buff, fmt, strlen(fmt)); /* rest of 'fmt' */
|
||||
clearbuff(&buff); /* empty buffer into the stack */
|
||||
if (buff.pushed > 1)
|
||||
luaV_concat(L, buff.pushed); /* join all partial results */
|
||||
lua_assert(buff.pushed == 1);
|
||||
return svalue(s2v(L->top - 1));
|
||||
}
|
||||
|
||||
|
||||
@@ -17,24 +17,30 @@
|
||||
|
||||
|
||||
/*
|
||||
** Extra tags for non-values
|
||||
** Extra types for collectable non-values
|
||||
*/
|
||||
#define LUA_TUPVAL LUA_NUMTAGS /* upvalues */
|
||||
#define LUA_TPROTO (LUA_NUMTAGS+1) /* function prototypes */
|
||||
#define LUA_TUPVAL LUA_NUMTYPES /* upvalues */
|
||||
#define LUA_TPROTO (LUA_NUMTYPES+1) /* function prototypes */
|
||||
#define LUA_TDEADKEY (LUA_NUMTYPES+2) /* removed keys in tables */
|
||||
|
||||
|
||||
|
||||
/*
|
||||
** number of all possible tags (including LUA_TNONE)
|
||||
** number of all possible types (including LUA_TNONE but excluding DEADKEY)
|
||||
*/
|
||||
#define LUA_TOTALTAGS (LUA_TPROTO + 2)
|
||||
#define LUA_TOTALTYPES (LUA_TPROTO + 2)
|
||||
|
||||
|
||||
/*
|
||||
** tags for Tagged Values have the following use of bits:
|
||||
** bits 0-3: actual tag (a LUA_T* value)
|
||||
** bits 0-3: actual tag (a LUA_T* constant)
|
||||
** bits 4-5: variant bits
|
||||
** bit 6: whether value is collectable
|
||||
*/
|
||||
|
||||
/* add variant bits to a type */
|
||||
#define makevariant(t,v) ((t) | ((v) << 4))
|
||||
|
||||
|
||||
|
||||
/*
|
||||
@@ -43,7 +49,6 @@
|
||||
typedef union Value {
|
||||
struct GCObject *gc; /* collectable objects */
|
||||
void *p; /* light userdata */
|
||||
int b; /* booleans */
|
||||
lua_CFunction f; /* light C functions */
|
||||
lua_Integer i; /* integer numbers */
|
||||
lua_Number n; /* float numbers */
|
||||
@@ -86,24 +91,36 @@ typedef struct TValue {
|
||||
|
||||
|
||||
/* Macros for internal tests */
|
||||
|
||||
/* collectable object has the same tag as the original value */
|
||||
#define righttt(obj) (ttypetag(obj) == gcvalue(obj)->tt)
|
||||
|
||||
/*
|
||||
** Any value being manipulated by the program either is non
|
||||
** collectable, or the collectable object has the right tag
|
||||
** and it is not dead. The option 'L == NULL' allows other
|
||||
** macros using this one to be used where L is not available.
|
||||
*/
|
||||
#define checkliveness(L,obj) \
|
||||
((void)L, lua_longassert(!iscollectable(obj) || \
|
||||
(righttt(obj) && (L == NULL || !isdead(G(L),gcvalue(obj))))))
|
||||
|
||||
|
||||
/* Macros to set values */
|
||||
|
||||
/* set a value's tag */
|
||||
#define settt_(o,t) ((o)->tt_=(t))
|
||||
|
||||
|
||||
/* main macro to copy values (from 'obj1' to 'obj2') */
|
||||
#define setobj(L,obj1,obj2) \
|
||||
{ TValue *io1=(obj1); const TValue *io2=(obj2); \
|
||||
io1->value_ = io2->value_; io1->tt_ = io2->tt_; \
|
||||
checkliveness(L,io1); lua_assert(!isreallyempty(io1)); }
|
||||
io1->value_ = io2->value_; settt_(io1, io2->tt_); \
|
||||
checkliveness(L,io1); lua_assert(!isnonstrictnil(io1)); }
|
||||
|
||||
/*
|
||||
** different types of assignments, according to destination
|
||||
** Different types of assignments, according to source and destination.
|
||||
** (They are mostly equal now, but may be different in the future.)
|
||||
*/
|
||||
|
||||
/* from stack to stack */
|
||||
@@ -118,13 +135,16 @@ typedef struct TValue {
|
||||
#define setobj2t setobj
|
||||
|
||||
|
||||
|
||||
/*
|
||||
** Entries in the Lua stack
|
||||
*/
|
||||
typedef union StackValue {
|
||||
TValue val;
|
||||
} StackValue;
|
||||
|
||||
|
||||
typedef StackValue *StkId; /* index to stack elements */
|
||||
/* index to stack elements */
|
||||
typedef StackValue *StkId;
|
||||
|
||||
/* convert a 'StackValue' to a 'TValue' */
|
||||
#define s2v(o) (&(o)->val)
|
||||
@@ -137,36 +157,34 @@ typedef StackValue *StkId; /* index to stack elements */
|
||||
** ===================================================================
|
||||
*/
|
||||
|
||||
/* Standard nil */
|
||||
#define LUA_VNIL makevariant(LUA_TNIL, 0)
|
||||
|
||||
/* Empty slot (which might be different from a slot containing nil) */
|
||||
#define LUA_VEMPTY makevariant(LUA_TNIL, 1)
|
||||
|
||||
/* Value returned for a key not found in a table (absent key) */
|
||||
#define LUA_VABSTKEY makevariant(LUA_TNIL, 2)
|
||||
|
||||
|
||||
/* macro to test for (any kind of) nil */
|
||||
#define ttisnil(v) checktype((v), LUA_TNIL)
|
||||
|
||||
/* macro to test for a "pure" nil */
|
||||
#define ttisstrictnil(o) checktag((o), LUA_TNIL)
|
||||
|
||||
/* macro to test for a standard nil */
|
||||
#define ttisstrictnil(o) checktag((o), LUA_VNIL)
|
||||
|
||||
|
||||
#define setnilvalue(obj) settt_(obj, LUA_TNIL)
|
||||
#define setnilvalue(obj) settt_(obj, LUA_VNIL)
|
||||
|
||||
|
||||
/*
|
||||
** Variant tag, used only in tables to signal an empty slot
|
||||
** (which might be different from a slot containing nil)
|
||||
*/
|
||||
#define LUA_TEMPTY (LUA_TNIL | (1 << 4))
|
||||
|
||||
/*
|
||||
** Variant used only in the value returned for a key not found in a
|
||||
** table (absent key).
|
||||
*/
|
||||
#define LUA_TABSTKEY (LUA_TNIL | (2 << 4))
|
||||
|
||||
|
||||
#define isabstkey(v) checktag((v), LUA_TABSTKEY)
|
||||
#define isabstkey(v) checktag((v), LUA_VABSTKEY)
|
||||
|
||||
|
||||
/*
|
||||
** macro to detect non-standard nils (used only in assertions)
|
||||
*/
|
||||
#define isreallyempty(v) (ttisnil(v) && !ttisstrictnil(v))
|
||||
#define isnonstrictnil(v) (ttisnil(v) && !ttisstrictnil(v))
|
||||
|
||||
|
||||
/*
|
||||
@@ -178,11 +196,11 @@ typedef StackValue *StkId; /* index to stack elements */
|
||||
|
||||
|
||||
/* macro defining a value corresponding to an absent key */
|
||||
#define ABSTKEYCONSTANT {NULL}, LUA_TABSTKEY
|
||||
#define ABSTKEYCONSTANT {NULL}, LUA_VABSTKEY
|
||||
|
||||
|
||||
/* mark an entry as empty */
|
||||
#define setempty(v) settt_(v, LUA_TEMPTY)
|
||||
#define setempty(v) settt_(v, LUA_VEMPTY)
|
||||
|
||||
|
||||
|
||||
@@ -195,16 +213,20 @@ typedef StackValue *StkId; /* index to stack elements */
|
||||
** ===================================================================
|
||||
*/
|
||||
|
||||
#define ttisboolean(o) checktag((o), LUA_TBOOLEAN)
|
||||
|
||||
#define bvalue(o) check_exp(ttisboolean(o), val_(o).b)
|
||||
#define LUA_VFALSE makevariant(LUA_TBOOLEAN, 0)
|
||||
#define LUA_VTRUE makevariant(LUA_TBOOLEAN, 1)
|
||||
|
||||
#define bvalueraw(v) ((v).b)
|
||||
#define ttisboolean(o) checktype((o), LUA_TBOOLEAN)
|
||||
#define ttisfalse(o) checktag((o), LUA_VFALSE)
|
||||
#define ttistrue(o) checktag((o), LUA_VTRUE)
|
||||
|
||||
#define l_isfalse(o) (ttisnil(o) || (ttisboolean(o) && bvalue(o) == 0))
|
||||
|
||||
#define setbvalue(obj,x) \
|
||||
{ TValue *io=(obj); val_(io).b=(x); settt_(io, LUA_TBOOLEAN); }
|
||||
#define l_isfalse(o) (ttisfalse(o) || ttisnil(o))
|
||||
|
||||
|
||||
#define setbfvalue(obj) settt_(obj, LUA_VFALSE)
|
||||
#define setbtvalue(obj) settt_(obj, LUA_VTRUE)
|
||||
|
||||
/* }================================================================== */
|
||||
|
||||
@@ -215,13 +237,15 @@ typedef StackValue *StkId; /* index to stack elements */
|
||||
** ===================================================================
|
||||
*/
|
||||
|
||||
#define ttisthread(o) checktag((o), ctb(LUA_TTHREAD))
|
||||
#define LUA_VTHREAD makevariant(LUA_TTHREAD, 0)
|
||||
|
||||
#define ttisthread(o) checktag((o), ctb(LUA_VTHREAD))
|
||||
|
||||
#define thvalue(o) check_exp(ttisthread(o), gco2th(val_(o).gc))
|
||||
|
||||
#define setthvalue(L,obj,x) \
|
||||
{ TValue *io = (obj); lua_State *x_ = (x); \
|
||||
val_(io).gc = obj2gco(x_); settt_(io, ctb(LUA_TTHREAD)); \
|
||||
val_(io).gc = obj2gco(x_); settt_(io, ctb(LUA_VTHREAD)); \
|
||||
checkliveness(L,io); }
|
||||
|
||||
#define setthvalue2s(L,o,t) setthvalue(L,s2v(o),t)
|
||||
@@ -274,12 +298,12 @@ typedef struct GCObject {
|
||||
*/
|
||||
|
||||
/* Variant tags for numbers */
|
||||
#define LUA_TNUMFLT (LUA_TNUMBER | (1 << 4)) /* float numbers */
|
||||
#define LUA_TNUMINT (LUA_TNUMBER | (2 << 4)) /* integer numbers */
|
||||
#define LUA_VNUMINT makevariant(LUA_TNUMBER, 0) /* integer numbers */
|
||||
#define LUA_VNUMFLT makevariant(LUA_TNUMBER, 1) /* float numbers */
|
||||
|
||||
#define ttisnumber(o) checktype((o), LUA_TNUMBER)
|
||||
#define ttisfloat(o) checktag((o), LUA_TNUMFLT)
|
||||
#define ttisinteger(o) checktag((o), LUA_TNUMINT)
|
||||
#define ttisfloat(o) checktag((o), LUA_VNUMFLT)
|
||||
#define ttisinteger(o) checktag((o), LUA_VNUMINT)
|
||||
|
||||
#define nvalue(o) check_exp(ttisnumber(o), \
|
||||
(ttisinteger(o) ? cast_num(ivalue(o)) : fltvalue(o)))
|
||||
@@ -290,13 +314,13 @@ typedef struct GCObject {
|
||||
#define ivalueraw(v) ((v).i)
|
||||
|
||||
#define setfltvalue(obj,x) \
|
||||
{ TValue *io=(obj); val_(io).n=(x); settt_(io, LUA_TNUMFLT); }
|
||||
{ TValue *io=(obj); val_(io).n=(x); settt_(io, LUA_VNUMFLT); }
|
||||
|
||||
#define chgfltvalue(obj,x) \
|
||||
{ TValue *io=(obj); lua_assert(ttisfloat(io)); val_(io).n=(x); }
|
||||
|
||||
#define setivalue(obj,x) \
|
||||
{ TValue *io=(obj); val_(io).i=(x); settt_(io, LUA_TNUMINT); }
|
||||
{ TValue *io=(obj); val_(io).i=(x); settt_(io, LUA_VNUMINT); }
|
||||
|
||||
#define chgivalue(obj,x) \
|
||||
{ TValue *io=(obj); lua_assert(ttisinteger(io)); val_(io).i=(x); }
|
||||
@@ -311,12 +335,12 @@ typedef struct GCObject {
|
||||
*/
|
||||
|
||||
/* Variant tags for strings */
|
||||
#define LUA_TSHRSTR (LUA_TSTRING | (1 << 4)) /* short strings */
|
||||
#define LUA_TLNGSTR (LUA_TSTRING | (2 << 4)) /* long strings */
|
||||
#define LUA_VSHRSTR makevariant(LUA_TSTRING, 0) /* short strings */
|
||||
#define LUA_VLNGSTR makevariant(LUA_TSTRING, 1) /* long strings */
|
||||
|
||||
#define ttisstring(o) checktype((o), LUA_TSTRING)
|
||||
#define ttisshrstring(o) checktag((o), ctb(LUA_TSHRSTR))
|
||||
#define ttislngstring(o) checktag((o), ctb(LUA_TLNGSTR))
|
||||
#define ttisshrstring(o) checktag((o), ctb(LUA_VSHRSTR))
|
||||
#define ttislngstring(o) checktag((o), ctb(LUA_VLNGSTR))
|
||||
|
||||
#define tsvalueraw(v) (gco2ts((v).gc))
|
||||
|
||||
@@ -335,8 +359,7 @@ typedef struct GCObject {
|
||||
|
||||
|
||||
/*
|
||||
** Header for string value; string bytes follow the end of this structure
|
||||
** (aligned according to 'UTString'; see next).
|
||||
** Header for a string value.
|
||||
*/
|
||||
typedef struct TString {
|
||||
CommonHeader;
|
||||
@@ -347,23 +370,22 @@ typedef struct TString {
|
||||
size_t lnglen; /* length for long strings */
|
||||
struct TString *hnext; /* linked list for hash table */
|
||||
} u;
|
||||
char contents[1];
|
||||
} TString;
|
||||
|
||||
|
||||
|
||||
/*
|
||||
** Get the actual string (array of bytes) from a 'TString'.
|
||||
** (Access to 'extra' ensures that value is really a 'TString'.)
|
||||
*/
|
||||
#define getstr(ts) \
|
||||
check_exp(sizeof((ts)->extra), cast_charp((ts)) + sizeof(TString))
|
||||
#define getstr(ts) ((ts)->contents)
|
||||
|
||||
|
||||
/* get the actual string (array of bytes) from a Lua value */
|
||||
#define svalue(o) getstr(tsvalue(o))
|
||||
|
||||
/* get string length from 'TString *s' */
|
||||
#define tsslen(s) ((s)->tt == LUA_TSHRSTR ? (s)->shrlen : (s)->u.lnglen)
|
||||
#define tsslen(s) ((s)->tt == LUA_VSHRSTR ? (s)->shrlen : (s)->u.lnglen)
|
||||
|
||||
/* get string length from 'TValue *o' */
|
||||
#define vslen(o) tsslen(tsvalue(o))
|
||||
@@ -377,8 +399,17 @@ typedef struct TString {
|
||||
** ===================================================================
|
||||
*/
|
||||
|
||||
#define ttislightuserdata(o) checktag((o), LUA_TLIGHTUSERDATA)
|
||||
#define ttisfulluserdata(o) checktype((o), LUA_TUSERDATA)
|
||||
|
||||
/*
|
||||
** Light userdata should be a variant of userdata, but for compatibility
|
||||
** reasons they are also different types.
|
||||
*/
|
||||
#define LUA_VLIGHTUSERDATA makevariant(LUA_TLIGHTUSERDATA, 0)
|
||||
|
||||
#define LUA_VUSERDATA makevariant(LUA_TUSERDATA, 0)
|
||||
|
||||
#define ttislightuserdata(o) checktag((o), LUA_VLIGHTUSERDATA)
|
||||
#define ttisfulluserdata(o) checktag((o), ctb(LUA_VUSERDATA))
|
||||
|
||||
#define pvalue(o) check_exp(ttislightuserdata(o), val_(o).p)
|
||||
#define uvalue(o) check_exp(ttisfulluserdata(o), gco2u(val_(o).gc))
|
||||
@@ -386,11 +417,11 @@ typedef struct TString {
|
||||
#define pvalueraw(v) ((v).p)
|
||||
|
||||
#define setpvalue(obj,x) \
|
||||
{ TValue *io=(obj); val_(io).p=(x); settt_(io, LUA_TLIGHTUSERDATA); }
|
||||
{ TValue *io=(obj); val_(io).p=(x); settt_(io, LUA_VLIGHTUSERDATA); }
|
||||
|
||||
#define setuvalue(L,obj,x) \
|
||||
{ TValue *io = (obj); Udata *x_ = (x); \
|
||||
val_(io).gc = obj2gco(x_); settt_(io, ctb(LUA_TUSERDATA)); \
|
||||
val_(io).gc = obj2gco(x_); settt_(io, ctb(LUA_VUSERDATA)); \
|
||||
checkliveness(L,io); }
|
||||
|
||||
|
||||
@@ -453,6 +484,9 @@ typedef struct Udata0 {
|
||||
** ===================================================================
|
||||
*/
|
||||
|
||||
#define LUA_VPROTO makevariant(LUA_TPROTO, 0)
|
||||
|
||||
|
||||
/*
|
||||
** Description of an upvalue for function prototypes
|
||||
*/
|
||||
@@ -523,20 +557,23 @@ typedef struct Proto {
|
||||
|
||||
/*
|
||||
** {==================================================================
|
||||
** Closures
|
||||
** Functions
|
||||
** ===================================================================
|
||||
*/
|
||||
|
||||
#define LUA_VUPVAL makevariant(LUA_TUPVAL, 0)
|
||||
|
||||
|
||||
/* Variant tags for functions */
|
||||
#define LUA_TLCL (LUA_TFUNCTION | (1 << 4)) /* Lua closure */
|
||||
#define LUA_TLCF (LUA_TFUNCTION | (2 << 4)) /* light C function */
|
||||
#define LUA_TCCL (LUA_TFUNCTION | (3 << 4)) /* C closure */
|
||||
#define LUA_VLCL makevariant(LUA_TFUNCTION, 0) /* Lua closure */
|
||||
#define LUA_VLCF makevariant(LUA_TFUNCTION, 1) /* light C function */
|
||||
#define LUA_VCCL makevariant(LUA_TFUNCTION, 2) /* C closure */
|
||||
|
||||
#define ttisfunction(o) checktype(o, LUA_TFUNCTION)
|
||||
#define ttisclosure(o) ((rawtt(o) & 0x1F) == LUA_TLCL)
|
||||
#define ttisLclosure(o) checktag((o), ctb(LUA_TLCL))
|
||||
#define ttislcf(o) checktag((o), LUA_TLCF)
|
||||
#define ttisCclosure(o) checktag((o), ctb(LUA_TCCL))
|
||||
#define ttisclosure(o) ((rawtt(o) & 0x1F) == LUA_VLCL)
|
||||
#define ttisLclosure(o) checktag((o), ctb(LUA_VLCL))
|
||||
#define ttislcf(o) checktag((o), LUA_VLCF)
|
||||
#define ttisCclosure(o) checktag((o), ctb(LUA_VCCL))
|
||||
|
||||
#define isLfunction(o) ttisLclosure(o)
|
||||
|
||||
@@ -549,17 +586,17 @@ typedef struct Proto {
|
||||
|
||||
#define setclLvalue(L,obj,x) \
|
||||
{ TValue *io = (obj); LClosure *x_ = (x); \
|
||||
val_(io).gc = obj2gco(x_); settt_(io, ctb(LUA_TLCL)); \
|
||||
val_(io).gc = obj2gco(x_); settt_(io, ctb(LUA_VLCL)); \
|
||||
checkliveness(L,io); }
|
||||
|
||||
#define setclLvalue2s(L,o,cl) setclLvalue(L,s2v(o),cl)
|
||||
|
||||
#define setfvalue(obj,x) \
|
||||
{ TValue *io=(obj); val_(io).f=(x); settt_(io, LUA_TLCF); }
|
||||
{ TValue *io=(obj); val_(io).f=(x); settt_(io, LUA_VLCF); }
|
||||
|
||||
#define setclCvalue(L,obj,x) \
|
||||
{ TValue *io = (obj); CClosure *x_ = (x); \
|
||||
val_(io).gc = obj2gco(x_); settt_(io, ctb(LUA_TCCL)); \
|
||||
val_(io).gc = obj2gco(x_); settt_(io, ctb(LUA_VCCL)); \
|
||||
checkliveness(L,io); }
|
||||
|
||||
|
||||
@@ -615,13 +652,15 @@ typedef union Closure {
|
||||
** ===================================================================
|
||||
*/
|
||||
|
||||
#define ttistable(o) checktag((o), ctb(LUA_TTABLE))
|
||||
#define LUA_VTABLE makevariant(LUA_TTABLE, 0)
|
||||
|
||||
#define ttistable(o) checktag((o), ctb(LUA_VTABLE))
|
||||
|
||||
#define hvalue(o) check_exp(ttistable(o), gco2t(val_(o).gc))
|
||||
|
||||
#define sethvalue(L,obj,x) \
|
||||
{ TValue *io = (obj); Table *x_ = (x); \
|
||||
val_(io).gc = obj2gco(x_); settt_(io, ctb(LUA_TTABLE)); \
|
||||
val_(io).gc = obj2gco(x_); settt_(io, ctb(LUA_VTABLE)); \
|
||||
checkliveness(L,io); }
|
||||
|
||||
#define sethvalue2s(L,o,h) sethvalue(L,s2v(o),h)
|
||||
@@ -667,9 +706,9 @@ typedef union Node {
|
||||
*/
|
||||
|
||||
#define BITRAS (1 << 7)
|
||||
#define isrealasize(t) (!((t)->marked & BITRAS))
|
||||
#define setrealasize(t) ((t)->marked &= cast_byte(~BITRAS))
|
||||
#define setnorealasize(t) ((t)->marked |= BITRAS)
|
||||
#define isrealasize(t) (!((t)->flags & BITRAS))
|
||||
#define setrealasize(t) ((t)->flags &= cast_byte(~BITRAS))
|
||||
#define setnorealasize(t) ((t)->flags |= BITRAS)
|
||||
|
||||
|
||||
typedef struct Table {
|
||||
@@ -692,9 +731,9 @@ typedef struct Table {
|
||||
#define keyval(node) ((node)->u.key_val)
|
||||
|
||||
#define keyisnil(node) (keytt(node) == LUA_TNIL)
|
||||
#define keyisinteger(node) (keytt(node) == LUA_TNUMINT)
|
||||
#define keyisinteger(node) (keytt(node) == LUA_VNUMINT)
|
||||
#define keyival(node) (keyval(node).i)
|
||||
#define keyisshrstr(node) (keytt(node) == ctb(LUA_TSHRSTR))
|
||||
#define keyisshrstr(node) (keytt(node) == ctb(LUA_VSHRSTR))
|
||||
#define keystrval(node) (gco2ts(keyval(node).gc))
|
||||
|
||||
#define setnilkey(node) (keytt(node) = LUA_TNIL)
|
||||
@@ -706,13 +745,13 @@ typedef struct Table {
|
||||
|
||||
|
||||
/*
|
||||
** Use a "nil table" to mark dead keys in a table. Those keys serve
|
||||
** to keep space for removed entries, which may still be part of
|
||||
** chains. Note that the 'keytt' does not have the BIT_ISCOLLECTABLE
|
||||
** set, so these values are considered not collectable and are different
|
||||
** from any valid value.
|
||||
** Dead keys in tables have the tag DEADKEY but keep their original
|
||||
** gcvalue. This distinguishes them from regular keys but allows them to
|
||||
** be found when searched in a special way. ('next' needs that to find
|
||||
** keys removed from a table during a traversal.)
|
||||
*/
|
||||
#define setdeadkey(n) (keytt(n) = LUA_TTABLE, gckey(n) = NULL)
|
||||
#define setdeadkey(node) (keytt(node) = LUA_TDEADKEY)
|
||||
#define keyisdead(node) (keytt(node) == LUA_TDEADKEY)
|
||||
|
||||
/* }================================================================== */
|
||||
|
||||
|
||||
+3
-3
@@ -10,8 +10,6 @@
|
||||
#include "lprefix.h"
|
||||
|
||||
|
||||
#include <stddef.h>
|
||||
|
||||
#include "lopcodes.h"
|
||||
|
||||
|
||||
@@ -24,7 +22,9 @@ LUAI_DDEF const lu_byte luaP_opmodes[NUM_OPCODES] = {
|
||||
,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_LOADFALSE */
|
||||
,opmode(0, 0, 0, 0, 1, iABC) /* OP_LFALSESKIP */
|
||||
,opmode(0, 0, 0, 0, 1, iABC) /* OP_LOADTRUE */
|
||||
,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 */
|
||||
|
||||
+95
-89
@@ -17,11 +17,11 @@
|
||||
|
||||
3 3 2 2 2 2 2 2 2 2 2 2 1 1 1 1 1 1 1 1 1 1 0 0 0 0 0 0 0 0 0 0
|
||||
1 0 9 8 7 6 5 4 3 2 1 0 9 8 7 6 5 4 3 2 1 0 9 8 7 6 5 4 3 2 1 0
|
||||
iABC C(8) | B(8) |k| A(8) | Op(7) |
|
||||
iABx Bx(17) | A(8) | Op(7) |
|
||||
iAsB sBx (signed)(17) | A(8) | Op(7) |
|
||||
iAx Ax(25) | Op(7) |
|
||||
isJ sJ(25) | Op(7) |
|
||||
iABC C(8) | B(8) |k| A(8) | Op(7) |
|
||||
iABx Bx(17) | A(8) | Op(7) |
|
||||
iAsBx sBx (signed)(17) | A(8) | Op(7) |
|
||||
iAx Ax(25) | Op(7) |
|
||||
isJ sJ(25) | Op(7) |
|
||||
|
||||
A signed argument is represented in excess K: the represented value is
|
||||
the written unsigned value minus K, where K is half the maximum for the
|
||||
@@ -133,7 +133,7 @@ enum OpMode {iABC, iABx, iAsBx, iAx, isJ}; /* basic instruction formats */
|
||||
#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))))
|
||||
#define TESTARG_k(i) check_exp(checkopm(i, iABC), (cast_int(((i) & (1u << POS_k)))))
|
||||
#define GETARG_k(i) check_exp(checkopm(i, iABC), getarg(i, POS_k, 1))
|
||||
#define SETARG_k(i,v) setarg(i, v, POS_k, 1)
|
||||
|
||||
@@ -183,9 +183,9 @@ enum OpMode {iABC, iABx, iAsBx, iAx, isJ}; /* basic instruction formats */
|
||||
|
||||
|
||||
/*
|
||||
** R(x) - register
|
||||
** K(x) - constant (in constant table)
|
||||
** RK(x) == if k(i) then K(x) else R(x)
|
||||
** R[x] - register
|
||||
** K[x] - constant (in constant table)
|
||||
** RK(x) == if k(i) then K[x] else R[x]
|
||||
*/
|
||||
|
||||
|
||||
@@ -195,113 +195,115 @@ enum OpMode {iABC, iABx, iAsBx, iAx, isJ}; /* basic instruction formats */
|
||||
|
||||
typedef enum {
|
||||
/*----------------------------------------------------------------------
|
||||
name args description
|
||||
name args description
|
||||
------------------------------------------------------------------------*/
|
||||
OP_MOVE,/* A B R(A) := R(B) */
|
||||
OP_LOADI,/* A sBx R(A) := sBx */
|
||||
OP_LOADF,/* A sBx R(A) := (lua_Number)sBx */
|
||||
OP_LOADK,/* A Bx R(A) := K(Bx) */
|
||||
OP_LOADKX,/* A R(A) := K(extra arg) */
|
||||
OP_LOADBOOL,/* A B C R(A) := (Bool)B; if (C) pc++ */
|
||||
OP_LOADNIL,/* A B R(A), R(A+1), ..., R(A+B) := nil */
|
||||
OP_GETUPVAL,/* A B R(A) := UpValue[B] */
|
||||
OP_SETUPVAL,/* A B UpValue[B] := R(A) */
|
||||
OP_MOVE,/* A B R[A] := R[B] */
|
||||
OP_LOADI,/* A sBx R[A] := sBx */
|
||||
OP_LOADF,/* A sBx R[A] := (lua_Number)sBx */
|
||||
OP_LOADK,/* A Bx R[A] := K[Bx] */
|
||||
OP_LOADKX,/* A R[A] := K[extra arg] */
|
||||
OP_LOADFALSE,/* A R[A] := false */
|
||||
OP_LFALSESKIP,/*A R[A] := false; pc++ */
|
||||
OP_LOADTRUE,/* A R[A] := true */
|
||||
OP_LOADNIL,/* A B R[A], R[A+1], ..., R[A+B] := nil */
|
||||
OP_GETUPVAL,/* A B R[A] := UpValue[B] */
|
||||
OP_SETUPVAL,/* A B UpValue[B] := R[A] */
|
||||
|
||||
OP_GETTABUP,/* A B C R(A) := UpValue[B][K(C):string] */
|
||||
OP_GETTABLE,/* A B C R(A) := R(B)[R(C)] */
|
||||
OP_GETI,/* A B C R(A) := R(B)[C] */
|
||||
OP_GETFIELD,/* A B C R(A) := R(B)[K(C):string] */
|
||||
OP_GETTABUP,/* A B C R[A] := UpValue[B][K[C]:string] */
|
||||
OP_GETTABLE,/* A B C R[A] := R[B][R[C]] */
|
||||
OP_GETI,/* A B C R[A] := R[B][C] */
|
||||
OP_GETFIELD,/* A B C R[A] := R[B][K[C]:string] */
|
||||
|
||||
OP_SETTABUP,/* A B C UpValue[A][K(B):string] := RK(C) */
|
||||
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_SETTABUP,/* A B C UpValue[A][K[B]:string] := RK(C) */
|
||||
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) := {} */
|
||||
OP_NEWTABLE,/* A B C k R[A] := {} */
|
||||
|
||||
OP_SELF,/* A B C R(A+1) := R(B); R(A) := R(B)[RK(C):string] */
|
||||
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) + sC */
|
||||
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) */
|
||||
OP_MULK,/* A B C R(A) := R(B) * K(C) */
|
||||
OP_MODK,/* A B C R(A) := R(B) % K(C) */
|
||||
OP_POWK,/* A B C R(A) := R(B) ^ K(C) */
|
||||
OP_DIVK,/* A B C R(A) := R(B) / K(C) */
|
||||
OP_IDIVK,/* A B C R(A) := R(B) // K(C) */
|
||||
OP_ADDK,/* A B C R[A] := R[B] + K[C] */
|
||||
OP_SUBK,/* A B C R[A] := R[B] - K[C] */
|
||||
OP_MULK,/* A B C R[A] := R[B] * K[C] */
|
||||
OP_MODK,/* A B C R[A] := R[B] % K[C] */
|
||||
OP_POWK,/* A B C R[A] := R[B] ^ K[C] */
|
||||
OP_DIVK,/* A B C R[A] := R[B] / K[C] */
|
||||
OP_IDIVK,/* A B C R[A] := R[B] // K[C] */
|
||||
|
||||
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_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) >> sC */
|
||||
OP_SHLI,/* A B sC R(A) := sC << 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) */
|
||||
OP_MUL,/* A B C R(A) := R(B) * R(C) */
|
||||
OP_MOD,/* A B C R(A) := R(B) % R(C) */
|
||||
OP_POW,/* A B C R(A) := R(B) ^ R(C) */
|
||||
OP_DIV,/* A B C R(A) := R(B) / R(C) */
|
||||
OP_IDIV,/* A B C R(A) := R(B) // R(C) */
|
||||
OP_ADD,/* A B C R[A] := R[B] + R[C] */
|
||||
OP_SUB,/* A B C R[A] := R[B] - R[C] */
|
||||
OP_MUL,/* A B C R[A] := R[B] * R[C] */
|
||||
OP_MOD,/* A B C R[A] := R[B] % R[C] */
|
||||
OP_POW,/* A B C R[A] := R[B] ^ R[C] */
|
||||
OP_DIV,/* A B C R[A] := R[B] / R[C] */
|
||||
OP_IDIV,/* A B C R[A] := R[B] // R[C] */
|
||||
|
||||
OP_BAND,/* A B C R(A) := R(B) & R(C) */
|
||||
OP_BOR,/* A B C R(A) := R(B) | R(C) */
|
||||
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_BAND,/* A B C R[A] := R[B] & R[C] */
|
||||
OP_BOR,/* A B C R[A] := R[B] | R[C] */
|
||||
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_MMBIN,/* A B C call C metamethod over R[A] and R[B] */
|
||||
OP_MMBINI,/* A sB C k call C metamethod over R[A] and sB */
|
||||
OP_MMBINK,/* A B C k 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) */
|
||||
OP_LEN,/* A B R(A) := length of R(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] */
|
||||
OP_LEN,/* A B R[A] := #R[B] (length operator) */
|
||||
|
||||
OP_CONCAT,/* A B R(A) := R(A).. ... ..R(A + B - 1) */
|
||||
OP_CONCAT,/* A B R[A] := R[A].. ... ..R[A + B - 1] */
|
||||
|
||||
OP_CLOSE,/* A close all upvalues >= R(A) */
|
||||
OP_CLOSE,/* A close all upvalues >= R[A] */
|
||||
OP_TBC,/* A mark variable A "to be closed" */
|
||||
OP_JMP,/* k sJ pc += sJ (k is used in code generation) */
|
||||
OP_EQ,/* A B if ((R(A) == R(B)) ~= k) then pc++ */
|
||||
OP_LT,/* A B if ((R(A) < R(B)) ~= k) then pc++ */
|
||||
OP_LE,/* A B if ((R(A) <= R(B)) ~= k) then pc++ */
|
||||
OP_JMP,/* sJ pc += sJ */
|
||||
OP_EQ,/* A B k if ((R[A] == R[B]) ~= k) then pc++ */
|
||||
OP_LT,/* A B k if ((R[A] < R[B]) ~= k) then pc++ */
|
||||
OP_LE,/* A B k if ((R[A] <= R[B]) ~= k) then pc++ */
|
||||
|
||||
OP_EQK,/* A B if ((R(A) == K(B)) ~= k) then pc++ */
|
||||
OP_EQI,/* A sB if ((R(A) == sB) ~= k) then pc++ */
|
||||
OP_LTI,/* A sB if ((R(A) < sB) ~= k) then pc++ */
|
||||
OP_LEI,/* A sB if ((R(A) <= sB) ~= k) then pc++ */
|
||||
OP_GTI,/* A sB if ((R(A) > sB) ~= k) then pc++ */
|
||||
OP_GEI,/* A sB if ((R(A) >= sB) ~= k) then pc++ */
|
||||
OP_EQK,/* A B k if ((R[A] == K[B]) ~= k) then pc++ */
|
||||
OP_EQI,/* A sB k if ((R[A] == sB) ~= k) then pc++ */
|
||||
OP_LTI,/* A sB k if ((R[A] < sB) ~= k) then pc++ */
|
||||
OP_LEI,/* A sB k if ((R[A] <= sB) ~= k) then pc++ */
|
||||
OP_GTI,/* A sB k if ((R[A] > sB) ~= k) then pc++ */
|
||||
OP_GEI,/* A sB k if ((R[A] >= sB) ~= k) then pc++ */
|
||||
|
||||
OP_TEST,/* A if (not R(A) == k) then pc++ */
|
||||
OP_TESTSET,/* A B if (not R(B) == k) then pc++ else R(A) := R(B) */
|
||||
OP_TEST,/* A k if (not R[A] == k) then pc++ */
|
||||
OP_TESTSET,/* A B k if (not R[B] == k) then pc++ else R[A] := R[B] */
|
||||
|
||||
OP_CALL,/* A B C R(A), ... ,R(A+C-2) := R(A)(R(A+1), ... ,R(A+B-1)) */
|
||||
OP_TAILCALL,/* A B C return R(A)(R(A+1), ... ,R(A+B-1)) */
|
||||
OP_CALL,/* A B C R[A], ... ,R[A+C-2] := R[A](R[A+1], ... ,R[A+B-1]) */
|
||||
OP_TAILCALL,/* A B C k return R[A](R[A+1], ... ,R[A+B-1]) */
|
||||
|
||||
OP_RETURN,/* A B C return R(A), ... ,R(A+B-2) (see note) */
|
||||
OP_RETURN0,/* return */
|
||||
OP_RETURN1,/* A return R(A) */
|
||||
OP_RETURN,/* A B C k return R[A], ... ,R[A+B-2] (see note) */
|
||||
OP_RETURN0,/* return */
|
||||
OP_RETURN1,/* A return R[A] */
|
||||
|
||||
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)); */
|
||||
OP_TFORLOOP,/* A Bx if R(A+2) ~= nil then { R(A)=R(A+2); pc -= Bx } */
|
||||
OP_TFORPREP,/* A Bx create upvalue for R[A + 3]; pc+=Bx */
|
||||
OP_TFORCALL,/* A C R[A+4], ... ,R[A+3+C] := R[A](R[A+1], R[A+2]); */
|
||||
OP_TFORLOOP,/* A Bx if R[A+2] ~= nil then { R[A]=R[A+2]; pc -= Bx } */
|
||||
|
||||
OP_SETLIST,/* A B C R(A)[(C-1)*FPF+i] := R(A+i), 1 <= i <= B */
|
||||
OP_SETLIST,/* A B C k R[A][C+i] := R[A+i], 1 <= i <= B */
|
||||
|
||||
OP_CLOSURE,/* A Bx R(A) := closure(KPROTO[Bx]) */
|
||||
OP_CLOSURE,/* A Bx R[A] := closure(KPROTO[Bx]) */
|
||||
|
||||
OP_VARARG,/* A C R(A), R(A+1), ..., R(A+C-2) = vararg */
|
||||
OP_VARARG,/* A C R[A], R[A+1], ..., R[A+C-2] = vararg */
|
||||
|
||||
OP_VARARGPREP,/*A (adjust vararg parameters) */
|
||||
OP_VARARGPREP,/*A (adjust vararg parameters) */
|
||||
|
||||
OP_EXTRAARG/* Ax extra (larger) argument for previous opcode */
|
||||
} OpCode;
|
||||
@@ -323,7 +325,7 @@ OP_EXTRAARG/* Ax extra (larger) argument for previous opcode */
|
||||
(*) In OP_RETURN, if (B == 0) then return up to 'top'.
|
||||
|
||||
(*) In OP_LOADKX and OP_NEWTABLE, the next instruction is always
|
||||
EXTRAARG.
|
||||
OP_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
|
||||
@@ -336,6 +338,9 @@ OP_EXTRAARG/* Ax extra (larger) argument for previous opcode */
|
||||
(*) For comparisons, k specifies what condition the test should accept
|
||||
(true or false).
|
||||
|
||||
(*) In OP_MMBINI/OP_MMBINK, k means the arguments were flipped
|
||||
(the constant is the first operand).
|
||||
|
||||
(*) All 'skips' (pc++) assume that next instruction is a jump.
|
||||
|
||||
(*) In instructions OP_RETURN/OP_TAILCALL, 'k' specifies that the
|
||||
@@ -344,7 +349,8 @@ OP_EXTRAARG/* Ax extra (larger) argument for previous opcode */
|
||||
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.
|
||||
original operand was a float. (It must be corrected in case of
|
||||
metamethods.)
|
||||
|
||||
===========================================================================*/
|
||||
|
||||
|
||||
+6
-1
@@ -7,6 +7,9 @@
|
||||
#if !defined(lopnames_h)
|
||||
#define lopnames_h
|
||||
|
||||
#include <stddef.h>
|
||||
|
||||
|
||||
/* ORDER OP */
|
||||
|
||||
static const char *const opnames[] = {
|
||||
@@ -15,7 +18,9 @@ static const char *const opnames[] = {
|
||||
"LOADF",
|
||||
"LOADK",
|
||||
"LOADKX",
|
||||
"LOADBOOL",
|
||||
"LOADFALSE",
|
||||
"LFALSESKIP",
|
||||
"LOADTRUE",
|
||||
"LOADNIL",
|
||||
"GETUPVAL",
|
||||
"SETUPVAL",
|
||||
|
||||
@@ -10,6 +10,7 @@
|
||||
#include "lprefix.h"
|
||||
|
||||
|
||||
#include <errno.h>
|
||||
#include <locale.h>
|
||||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
@@ -91,7 +92,7 @@
|
||||
|
||||
/* ISO C definitions */
|
||||
#define l_gmtime(t,r) ((void)(r)->tm_sec, gmtime(t))
|
||||
#define l_localtime(t,r) ((void)(r)->tm_sec, localtime(t))
|
||||
#define l_localtime(t,r) ((void)(r)->tm_sec, localtime(t))
|
||||
|
||||
#endif /* } */
|
||||
|
||||
@@ -138,10 +139,11 @@
|
||||
|
||||
|
||||
|
||||
|
||||
static int os_execute (lua_State *L) {
|
||||
const char *cmd = luaL_optstring(L, 1, NULL);
|
||||
int stat = system(cmd);
|
||||
int stat;
|
||||
errno = 0;
|
||||
stat = system(cmd);
|
||||
if (cmd != NULL)
|
||||
return luaL_execresult(L, stat);
|
||||
else {
|
||||
@@ -196,7 +198,7 @@ static int os_clock (lua_State *L) {
|
||||
*/
|
||||
|
||||
/*
|
||||
** About the overflow check: an overflow cannot occurr when time
|
||||
** About the overflow check: an overflow cannot occur 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
|
||||
|
||||
@@ -212,27 +212,28 @@ static int new_localvar (LexState *ls, TString *name) {
|
||||
|
||||
|
||||
/*
|
||||
** Return the "variable description" (Vardesc) of a given
|
||||
** variable
|
||||
** Return the "variable description" (Vardesc) of a given variable.
|
||||
** (Unless noted otherwise, all variables are referred to by their
|
||||
** compiler indices.)
|
||||
*/
|
||||
static Vardesc *getlocalvardesc (FuncState *fs, int i) {
|
||||
return &fs->ls->dyd->actvar.arr[fs->firstlocal + i];
|
||||
static Vardesc *getlocalvardesc (FuncState *fs, int vidx) {
|
||||
return &fs->ls->dyd->actvar.arr[fs->firstlocal + vidx];
|
||||
}
|
||||
|
||||
|
||||
/*
|
||||
** Convert 'nvar' (number of active variables at some point) to
|
||||
** number of variables in the stack at that point.
|
||||
** Convert 'nvar', a compiler index level, to it corresponding
|
||||
** stack index level. For that, search for the highest variable
|
||||
** below that level that is in the stack and uses its stack
|
||||
** index ('sidx').
|
||||
*/
|
||||
static int stacklevel (FuncState *fs, int nvar) {
|
||||
while (nvar > 0) {
|
||||
Vardesc *vd = getlocalvardesc(fs, nvar - 1);
|
||||
while (nvar-- > 0) {
|
||||
Vardesc *vd = getlocalvardesc(fs, nvar); /* get variable */
|
||||
if (vd->vd.kind != RDKCTC) /* is in the stack? */
|
||||
return vd->vd.sidx + 1;
|
||||
else
|
||||
nvar--; /* try previous variable */
|
||||
}
|
||||
return 0; /* no variables */
|
||||
return 0; /* no variables in the stack */
|
||||
}
|
||||
|
||||
|
||||
@@ -245,10 +246,10 @@ int luaY_nvarstack (FuncState *fs) {
|
||||
|
||||
|
||||
/*
|
||||
** Get the debug-information entry for current variable 'i'.
|
||||
** Get the debug-information entry for current variable 'vidx'.
|
||||
*/
|
||||
static LocVar *localdebuginfo (FuncState *fs, int i) {
|
||||
Vardesc *vd = getlocalvardesc(fs, i);
|
||||
static LocVar *localdebuginfo (FuncState *fs, int vidx) {
|
||||
Vardesc *vd = getlocalvardesc(fs, vidx);
|
||||
if (vd->vd.kind == RDKCTC)
|
||||
return NULL; /* no debug info. for constants */
|
||||
else {
|
||||
@@ -259,14 +260,20 @@ static LocVar *localdebuginfo (FuncState *fs, int i) {
|
||||
}
|
||||
|
||||
|
||||
static void init_var (FuncState *fs, expdesc *e, int i) {
|
||||
/*
|
||||
** Create an expression representing variable 'vidx'
|
||||
*/
|
||||
static void init_var (FuncState *fs, expdesc *e, int vidx) {
|
||||
e->f = e->t = NO_JUMP;
|
||||
e->k = VLOCAL;
|
||||
e->u.var.vidx = i;
|
||||
e->u.var.sidx = getlocalvardesc(fs, i)->vd.sidx;
|
||||
e->u.var.vidx = vidx;
|
||||
e->u.var.sidx = getlocalvardesc(fs, vidx)->vd.sidx;
|
||||
}
|
||||
|
||||
|
||||
/*
|
||||
** Raises an error if variable described by 'e' is read only
|
||||
*/
|
||||
static void check_readonly (LexState *ls, expdesc *e) {
|
||||
FuncState *fs = ls->fs;
|
||||
TString *varname = NULL; /* to be set if variable is const */
|
||||
@@ -306,8 +313,8 @@ static void adjustlocalvars (LexState *ls, int nvars) {
|
||||
int stklevel = luaY_nvarstack(fs);
|
||||
int i;
|
||||
for (i = 0; i < nvars; i++) {
|
||||
int varidx = fs->nactvar++;
|
||||
Vardesc *var = getlocalvardesc(fs, varidx);
|
||||
int vidx = fs->nactvar++;
|
||||
Vardesc *var = getlocalvardesc(fs, vidx);
|
||||
var->vd.sidx = stklevel++;
|
||||
var->vd.pidx = registerlocalvar(ls, fs, var->vd.name);
|
||||
}
|
||||
@@ -377,7 +384,8 @@ static int newupvalue (FuncState *fs, TString *name, expdesc *v) {
|
||||
|
||||
/*
|
||||
** Look for an active local variable with the name 'n' in the
|
||||
** function 'fs'.
|
||||
** function 'fs'. If found, initialize 'var' with it and return
|
||||
** its expression kind; otherwise return -1.
|
||||
*/
|
||||
static int searchvar (FuncState *fs, TString *n, expdesc *var) {
|
||||
int i;
|
||||
@@ -481,12 +489,10 @@ static void adjust_assign (LexState *ls, int nvars, int nexps, expdesc *e) {
|
||||
}
|
||||
|
||||
|
||||
/*
|
||||
** Macros to limit the maximum recursion depth while parsing
|
||||
*/
|
||||
#define enterlevel(ls) luaE_enterCcall((ls)->L)
|
||||
#define enterlevel(ls) luaE_incCstack(ls->L)
|
||||
|
||||
#define leavelevel(ls) luaE_exitCcall((ls)->L)
|
||||
|
||||
#define leavelevel(ls) ((ls)->L->nCcalls--)
|
||||
|
||||
|
||||
/*
|
||||
@@ -729,6 +735,7 @@ static void open_func (LexState *ls, FuncState *fs, BlockCnt *bl) {
|
||||
fs->firstlabel = ls->dyd->label.n;
|
||||
fs->bl = NULL;
|
||||
f->source = ls->source;
|
||||
luaC_objbarrier(ls->L, f, f->source);
|
||||
f->maxstacksize = 2; /* registers 0/1 are always valid */
|
||||
enterblock(fs, bl, 0);
|
||||
}
|
||||
@@ -938,7 +945,7 @@ static void setvararg (FuncState *fs, int nparams) {
|
||||
|
||||
|
||||
static void parlist (LexState *ls) {
|
||||
/* parlist -> [ param { ',' param } ] */
|
||||
/* parlist -> [ {NAME ','} (NAME | '...') ] */
|
||||
FuncState *fs = ls->fs;
|
||||
Proto *f = fs->f;
|
||||
int nparams = 0;
|
||||
@@ -946,12 +953,12 @@ static void parlist (LexState *ls) {
|
||||
if (ls->t.token != ')') { /* is 'parlist' not empty? */
|
||||
do {
|
||||
switch (ls->t.token) {
|
||||
case TK_NAME: { /* param -> NAME */
|
||||
case TK_NAME: {
|
||||
new_localvar(ls, str_checkname(ls));
|
||||
nparams++;
|
||||
break;
|
||||
}
|
||||
case TK_DOTS: { /* param -> '...' */
|
||||
case TK_DOTS: {
|
||||
luaX_next(ls);
|
||||
isvararg = 1;
|
||||
break;
|
||||
@@ -1014,7 +1021,8 @@ static void funcargs (LexState *ls, expdesc *f, int line) {
|
||||
args.k = VVOID;
|
||||
else {
|
||||
explist(ls, &args);
|
||||
luaK_setmultret(fs, &args);
|
||||
if (hasmultret(args.k))
|
||||
luaK_setmultret(fs, &args);
|
||||
}
|
||||
check_match(ls, ')', '(', line);
|
||||
break;
|
||||
@@ -1591,7 +1599,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 */
|
||||
markupval(fs, fs->nactvar); /* last control var. must be closed */
|
||||
luaK_checkstack(fs, 3); /* extra space to call generator */
|
||||
forbody(ls, base, line, nvars - 4, 1);
|
||||
}
|
||||
@@ -1615,59 +1623,21 @@ static void forstat (LexState *ls, int line) {
|
||||
}
|
||||
|
||||
|
||||
/*
|
||||
** Check whether next instruction is a single jump (a 'break', a 'goto'
|
||||
** to a forward label, or a 'goto' to a backward label with no variable
|
||||
** to close). If so, set the name of the 'label' it is jumping to
|
||||
** ("break" for a 'break') or to where it is jumping to ('target') and
|
||||
** return true. If not a single jump, leave input unchanged, to be
|
||||
** handled as a regular statement.
|
||||
*/
|
||||
static int issinglejump (LexState *ls, TString **label, int *target) {
|
||||
if (testnext(ls, TK_BREAK)) { /* a break? */
|
||||
*label = luaS_newliteral(ls->L, "break");
|
||||
return 1;
|
||||
}
|
||||
else if (ls->t.token != TK_GOTO || luaX_lookahead(ls) != TK_NAME)
|
||||
return 0; /* not a valid goto */
|
||||
else {
|
||||
TString *lname = ls->lookahead.seminfo.ts; /* label's id */
|
||||
Labeldesc *lb = findlabel(ls, lname);
|
||||
if (lb) { /* a backward jump? */
|
||||
/* does it need to close variables? */
|
||||
if (luaY_nvarstack(ls->fs) > stacklevel(ls->fs, lb->nactvar))
|
||||
return 0; /* not a single jump; cannot optimize */
|
||||
*target = lb->pc;
|
||||
}
|
||||
else /* jump forward */
|
||||
*label = lname;
|
||||
luaX_next(ls); /* skip goto */
|
||||
luaX_next(ls); /* skip name */
|
||||
return 1;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
static void test_then_block (LexState *ls, int *escapelist) {
|
||||
/* test_then_block -> [IF | ELSEIF] cond THEN block */
|
||||
BlockCnt bl;
|
||||
int line;
|
||||
FuncState *fs = ls->fs;
|
||||
TString *jlb = NULL;
|
||||
int target = NO_JUMP;
|
||||
expdesc v;
|
||||
int jf; /* instruction to skip 'then' code (if condition is false) */
|
||||
luaX_next(ls); /* skip IF or ELSEIF */
|
||||
expr(ls, &v); /* read condition */
|
||||
checknext(ls, TK_THEN);
|
||||
line = ls->linenumber;
|
||||
if (issinglejump(ls, &jlb, &target)) { /* 'if x then goto' ? */
|
||||
luaK_goiffalse(ls->fs, &v); /* will jump to label if condition is true */
|
||||
if (ls->t.token == TK_BREAK) { /* 'if x then break' ? */
|
||||
int line = ls->linenumber;
|
||||
luaK_goiffalse(ls->fs, &v); /* will jump if condition is true */
|
||||
luaX_next(ls); /* skip 'break' */
|
||||
enterblock(fs, &bl, 0); /* must enter block before 'goto' */
|
||||
if (jlb != NULL) /* forward jump? */
|
||||
newgotoentry(ls, jlb, line, v.t); /* will be resolved later */
|
||||
else /* backward jump */
|
||||
luaK_patchlist(fs, v.t, target); /* jump directly to 'target' */
|
||||
newgotoentry(ls, luaS_newliteral(ls->L, "break"), line, v.t);
|
||||
while (testnext(ls, ';')) {} /* skip semicolons */
|
||||
if (block_follow(ls, 0)) { /* jump is the entire block? */
|
||||
leaveblock(fs);
|
||||
@@ -1676,7 +1646,7 @@ static void test_then_block (LexState *ls, int *escapelist) {
|
||||
else /* must skip over 'then' part if condition is false */
|
||||
jf = luaK_jump(fs);
|
||||
}
|
||||
else { /* regular case (not a jump) */
|
||||
else { /* regular case (not a break) */
|
||||
luaK_goiftrue(ls->fs, &v); /* skip over block if condition is false */
|
||||
enterblock(fs, &bl, 0);
|
||||
jf = v.f;
|
||||
@@ -1729,7 +1699,7 @@ static int getlocalattribute (LexState *ls) {
|
||||
luaK_semerror(ls,
|
||||
luaO_pushfstring(ls->L, "unknown attribute '%s'", attr));
|
||||
}
|
||||
return VDKREG;
|
||||
return VDKREG; /* regular variable */
|
||||
}
|
||||
|
||||
|
||||
@@ -1738,28 +1708,28 @@ static void checktoclose (LexState *ls, int level) {
|
||||
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);
|
||||
luaK_codeABC(fs, OP_TBC, stacklevel(fs, level), 0, 0);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
static void localstat (LexState *ls) {
|
||||
/* stat -> LOCAL ATTRIB NAME {',' ATTRIB NAME} ['=' explist] */
|
||||
/* stat -> LOCAL NAME ATTRIB { ',' NAME ATTRIB } ['=' 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 vidx, kind; /* index and kind of last variable */
|
||||
int nvars = 0;
|
||||
int nexps;
|
||||
expdesc e;
|
||||
do {
|
||||
ivar = new_localvar(ls, str_checkname(ls));
|
||||
vidx = new_localvar(ls, str_checkname(ls));
|
||||
kind = getlocalattribute(ls);
|
||||
getlocalvardesc(fs, ivar)->vd.kind = kind;
|
||||
getlocalvardesc(fs, vidx)->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;
|
||||
toclose = fs->nactvar + nvars;
|
||||
}
|
||||
nvars++;
|
||||
} while (testnext(ls, ','));
|
||||
@@ -1769,7 +1739,7 @@ static void localstat (LexState *ls) {
|
||||
e.k = VVOID;
|
||||
nexps = 0;
|
||||
}
|
||||
var = getlocalvardesc(fs, ivar); /* get last variable */
|
||||
var = getlocalvardesc(fs, vidx); /* 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? */
|
||||
@@ -1821,8 +1791,9 @@ static void exprstat (LexState *ls) {
|
||||
restassign(ls, &v, 1);
|
||||
}
|
||||
else { /* stat -> func */
|
||||
Instruction *inst = &getinstruction(fs, &v.v);
|
||||
Instruction *inst;
|
||||
check_condition(ls, v.v.k == VCALL, "syntax error");
|
||||
inst = &getinstruction(fs, &v.v);
|
||||
SETARG_C(*inst, 1); /* call statement uses no results */
|
||||
}
|
||||
}
|
||||
@@ -1949,6 +1920,7 @@ static void mainfunc (LexState *ls, FuncState *fs) {
|
||||
env->idx = 0;
|
||||
env->kind = VDKREG;
|
||||
env->name = ls->envn;
|
||||
luaC_objbarrier(ls->L, fs->f, env->name);
|
||||
luaX_next(ls); /* read first token */
|
||||
statlist(ls); /* parse main body */
|
||||
check(ls, TK_EOS);
|
||||
@@ -1967,6 +1939,7 @@ LClosure *luaY_parser (lua_State *L, ZIO *z, Mbuffer *buff,
|
||||
sethvalue2s(L, L->top, lexstate.h); /* anchor it */
|
||||
luaD_inctop(L);
|
||||
funcstate.f = cl->p = luaF_newproto(L);
|
||||
luaC_objbarrier(L, cl, cl->p);
|
||||
funcstate.f->source = luaS_new(L, name); /* create and anchor TString */
|
||||
luaC_objbarrier(L, funcstate.f, funcstate.f->source);
|
||||
lexstate.buff = buff;
|
||||
|
||||
@@ -23,7 +23,7 @@
|
||||
|
||||
/* kinds of variables/expressions */
|
||||
typedef enum {
|
||||
VVOID, /* when 'expdesc' describes the last expression a list,
|
||||
VVOID, /* when 'expdesc' describes the last expression of a list,
|
||||
this kind means an empty list (so, no expression) */
|
||||
VNIL, /* constant nil */
|
||||
VTRUE, /* constant true */
|
||||
@@ -35,10 +35,11 @@ typedef enum {
|
||||
(string is fixed by the lexer) */
|
||||
VNONRELOC, /* expression has its value in a fixed register;
|
||||
info = result register */
|
||||
VLOCAL, /* local variable; var.ridx = local register;
|
||||
VLOCAL, /* local variable; var.sidx = stack index (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' */
|
||||
VCONST, /* compile-time <const> variable;
|
||||
info = absolute index in 'actvar.arr' */
|
||||
VINDEXED, /* indexed variable;
|
||||
ind.t = table register;
|
||||
ind.idx = key's R index */
|
||||
@@ -77,7 +78,7 @@ typedef struct expdesc {
|
||||
} ind;
|
||||
struct { /* for local variables */
|
||||
lu_byte sidx; /* index in the stack */
|
||||
unsigned short vidx; /* index in 'actvar.arr' */
|
||||
unsigned short vidx; /* compiler index (in 'actvar.arr') */
|
||||
} var;
|
||||
} u;
|
||||
int t; /* patch list of 'exit when true' */
|
||||
@@ -125,7 +126,7 @@ typedef struct Labellist {
|
||||
|
||||
/* dynamic structures used by the parser */
|
||||
typedef struct Dyndata {
|
||||
struct { /* list of active local variables */
|
||||
struct { /* list of all active local variables */
|
||||
Vardesc *arr;
|
||||
int n;
|
||||
int size;
|
||||
|
||||
@@ -33,7 +33,7 @@
|
||||
/*
|
||||
** Windows stuff
|
||||
*/
|
||||
#if defined(_WIN32) /* { */
|
||||
#if defined(_WIN32) /* { */
|
||||
|
||||
#if !defined(_CRT_SECURE_NO_WARNINGS)
|
||||
#define _CRT_SECURE_NO_WARNINGS /* avoid warnings about ISO C functions */
|
||||
|
||||
@@ -97,66 +97,14 @@ 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 */
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
/*
|
||||
** Decrement count of "C calls" and check for overflows. In case of
|
||||
** a stack overflow, check appropriate error ("regular" overflow or
|
||||
** overflow while handling stack overflow). If 'nCcalls' is smaller
|
||||
** than CSTACKERR but larger than CSTACKMARK, it means it has just
|
||||
** entered the "overflow zone", so the function raises an overflow
|
||||
** error. If 'nCcalls' is smaller than CSTACKMARK (which means it is
|
||||
** already handling an overflow) but larger than CSTACKERRMARK, does
|
||||
** not report an error (to allow message handling to work). Otherwise,
|
||||
** report a stack overflow while handling a stack overflow (probably
|
||||
** caused by a repeating error in the message handling function).
|
||||
*/
|
||||
|
||||
void luaE_enterCcall (lua_State *L) {
|
||||
int ncalls = getCcalls(L);
|
||||
L->nCcalls--;
|
||||
if (ncalls <= CSTACKERR) { /* possible overflow? */
|
||||
luaE_freeCI(L); /* release unused CIs */
|
||||
ncalls = getCcalls(L); /* update call count */
|
||||
if (ncalls <= CSTACKERR) { /* still overflow? */
|
||||
if (ncalls <= CSTACKERRMARK) /* below error-handling zone? */
|
||||
luaD_throw(L, LUA_ERRERR); /* error while handling stack error */
|
||||
else if (ncalls >= CSTACKMARK) {
|
||||
/* not in error-handling zone; raise the error now */
|
||||
L->nCcalls = (CSTACKMARK - 1); /* enter error-handling zone */
|
||||
luaG_runerror(L, "C stack overflow");
|
||||
}
|
||||
/* else stack is in the error-handling zone;
|
||||
allow message handler to work */
|
||||
}
|
||||
}
|
||||
UNUSED(L); UNUSED(limit);
|
||||
return LUAI_MAXCCALLS; /* warning?? */
|
||||
}
|
||||
|
||||
|
||||
CallInfo *luaE_extendCI (lua_State *L) {
|
||||
CallInfo *ci;
|
||||
lua_assert(L->ci->next == NULL);
|
||||
luaE_enterCcall(L);
|
||||
ci = luaM_new(L, CallInfo);
|
||||
lua_assert(L->ci->next == NULL);
|
||||
L->ci->next = ci;
|
||||
@@ -175,44 +123,68 @@ void luaE_freeCI (lua_State *L) {
|
||||
CallInfo *ci = L->ci;
|
||||
CallInfo *next = ci->next;
|
||||
ci->next = NULL;
|
||||
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 */
|
||||
}
|
||||
|
||||
|
||||
/*
|
||||
** free half of the CallInfo structures not in use by a thread
|
||||
** free half of the CallInfo structures not in use by a thread,
|
||||
** keeping the first one.
|
||||
*/
|
||||
void luaE_shrinkCI (lua_State *L) {
|
||||
CallInfo *ci = L->ci;
|
||||
CallInfo *next2; /* next's next */
|
||||
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 */
|
||||
CallInfo *ci = L->ci->next; /* first free CallInfo */
|
||||
CallInfo *next;
|
||||
if (ci == NULL)
|
||||
return; /* no extra elements */
|
||||
while ((next = ci->next) != NULL) { /* two extra elements? */
|
||||
CallInfo *next2 = next->next; /* next's next */
|
||||
ci->next = next2; /* remove next from the list */
|
||||
L->nci--;
|
||||
ci->next = next2; /* remove 'next' from the list */
|
||||
next2->previous = ci;
|
||||
ci = next2; /* keep next's next */
|
||||
luaM_free(L, next); /* free next */
|
||||
if (next2 == NULL)
|
||||
break; /* no more elements */
|
||||
else {
|
||||
next2->previous = ci;
|
||||
ci = next2; /* continue */
|
||||
}
|
||||
}
|
||||
L->nCcalls -= L->nci; /* adjust result */
|
||||
}
|
||||
|
||||
|
||||
/*
|
||||
** Called when 'getCcalls(L)' larger or equal to LUAI_MAXCCALLS.
|
||||
** If equal, raises an overflow error. If value is larger than
|
||||
** LUAI_MAXCCALLS (which means it is handling an overflow) but
|
||||
** not much larger, does not report an error (to allow overflow
|
||||
** handling to work).
|
||||
*/
|
||||
void luaE_checkcstack (lua_State *L) {
|
||||
if (getCcalls(L) == LUAI_MAXCCALLS)
|
||||
luaG_runerror(L, "C stack overflow");
|
||||
else if (getCcalls(L) >= (LUAI_MAXCCALLS / 10 * 11))
|
||||
luaD_throw(L, LUA_ERRERR); /* error while handing stack error */
|
||||
}
|
||||
|
||||
|
||||
LUAI_FUNC void luaE_incCstack (lua_State *L) {
|
||||
L->nCcalls++;
|
||||
if (unlikely(getCcalls(L) >= LUAI_MAXCCALLS))
|
||||
luaE_checkcstack(L);
|
||||
}
|
||||
|
||||
|
||||
static void stack_init (lua_State *L1, lua_State *L) {
|
||||
int i; CallInfo *ci;
|
||||
/* initialize stack array */
|
||||
L1->stack = luaM_newvector(L, BASIC_STACK_SIZE, StackValue);
|
||||
L1->stacksize = BASIC_STACK_SIZE;
|
||||
for (i = 0; i < BASIC_STACK_SIZE; i++)
|
||||
L1->stack = luaM_newvector(L, BASIC_STACK_SIZE + EXTRA_STACK, StackValue);
|
||||
for (i = 0; i < BASIC_STACK_SIZE + EXTRA_STACK; i++)
|
||||
setnilvalue(s2v(L1->stack + i)); /* erase new stack */
|
||||
L1->top = L1->stack;
|
||||
L1->stack_last = L1->stack + L1->stacksize - EXTRA_STACK;
|
||||
L1->stack_last = L1->stack + BASIC_STACK_SIZE;
|
||||
/* initialize first ci */
|
||||
ci = &L1->base_ci;
|
||||
ci->next = ci->previous = NULL;
|
||||
@@ -233,7 +205,7 @@ static void freestack (lua_State *L) {
|
||||
L->ci = &L->base_ci; /* free the entire 'ci' list */
|
||||
luaE_freeCI(L);
|
||||
lua_assert(L->nci == 0);
|
||||
luaM_freearray(L, L->stack, L->stacksize); /* free stack array */
|
||||
luaM_freearray(L, L->stack, stacksize(L) + EXTRA_STACK); /* free stack */
|
||||
}
|
||||
|
||||
|
||||
@@ -283,10 +255,8 @@ static void preinit_thread (lua_State *L, global_State *g) {
|
||||
L->stack = NULL;
|
||||
L->ci = NULL;
|
||||
L->nci = 0;
|
||||
L->stacksize = 0;
|
||||
L->twups = L; /* thread has no upvalues */
|
||||
L->errorJmp = NULL;
|
||||
L->nCcalls = CSTACKTHREAD;
|
||||
L->hook = NULL;
|
||||
L->hookmask = 0;
|
||||
L->basehookcount = 0;
|
||||
@@ -295,6 +265,7 @@ static void preinit_thread (lua_State *L, global_State *g) {
|
||||
L->openupval = NULL;
|
||||
L->status = LUA_OK;
|
||||
L->errfunc = 0;
|
||||
L->oldpc = 0;
|
||||
}
|
||||
|
||||
|
||||
@@ -312,14 +283,15 @@ static void close_state (lua_State *L) {
|
||||
|
||||
|
||||
LUA_API lua_State *lua_newthread (lua_State *L) {
|
||||
global_State *g = G(L);
|
||||
global_State *g;
|
||||
lua_State *L1;
|
||||
lua_lock(L);
|
||||
g = G(L);
|
||||
luaC_checkGC(L);
|
||||
/* create new thread */
|
||||
L1 = &cast(LX *, luaM_newobject(L, LUA_TTHREAD, sizeof(LX)))->l;
|
||||
L1->marked = luaC_white(g);
|
||||
L1->tt = LUA_TTHREAD;
|
||||
L1->tt = LUA_VTHREAD;
|
||||
/* link it on list 'allgc' */
|
||||
L1->next = g->allgc;
|
||||
g->allgc = obj2gco(L1);
|
||||
@@ -327,6 +299,7 @@ LUA_API lua_State *lua_newthread (lua_State *L) {
|
||||
setthvalue2s(L, L->top, L1);
|
||||
api_incr_top(L);
|
||||
preinit_thread(L1, g);
|
||||
L1->nCcalls = 0;
|
||||
L1->hookmask = L->hookmask;
|
||||
L1->basehookcount = L->basehookcount;
|
||||
L1->hook = L->hook;
|
||||
@@ -355,19 +328,18 @@ int lua_resetthread (lua_State *L) {
|
||||
CallInfo *ci;
|
||||
int status;
|
||||
lua_lock(L);
|
||||
ci = &L->base_ci;
|
||||
status = luaF_close(L, L->stack, CLOSEPROTECT);
|
||||
L->ci = ci = &L->base_ci; /* unwind CallInfo list */
|
||||
setnilvalue(s2v(L->stack)); /* 'function' entry for basic 'ci' */
|
||||
ci->func = L->stack;
|
||||
ci->callstatus = CIST_C;
|
||||
status = luaF_close(L, L->stack, CLOSEPROTECT);
|
||||
if (status != CLOSEPROTECT) /* real errors? */
|
||||
luaD_seterrorobj(L, status, L->stack + 1);
|
||||
else {
|
||||
status = LUA_OK;
|
||||
L->top = L->stack + 1;
|
||||
}
|
||||
ci->callstatus = CIST_C;
|
||||
ci->func = L->stack;
|
||||
ci->top = L->top + LUA_MINSTACK;
|
||||
L->ci = ci;
|
||||
L->status = status;
|
||||
lua_unlock(L);
|
||||
return status;
|
||||
@@ -382,13 +354,14 @@ LUA_API lua_State *lua_newstate (lua_Alloc f, void *ud) {
|
||||
if (l == NULL) return NULL;
|
||||
L = &l->l.l;
|
||||
g = &l->g;
|
||||
L->tt = LUA_TTHREAD;
|
||||
L->tt = LUA_VTHREAD;
|
||||
g->currentwhite = bitmask(WHITE0BIT);
|
||||
L->marked = luaC_white(g);
|
||||
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;
|
||||
L->nCcalls = 0;
|
||||
incnny(L); /* main thread is always non yieldable */
|
||||
g->frealloc = f;
|
||||
g->ud = ud;
|
||||
g->warnf = NULL;
|
||||
@@ -404,8 +377,8 @@ LUA_API lua_State *lua_newstate (lua_Alloc f, void *ud) {
|
||||
g->gckind = KGC_INC;
|
||||
g->gcemergency = 0;
|
||||
g->finobj = g->tobefnz = g->fixedgc = NULL;
|
||||
g->survival = g->old = g->reallyold = NULL;
|
||||
g->finobjsur = g->finobjold = g->finobjrold = NULL;
|
||||
g->firstold1 = g->survival = g->old1 = g->reallyold = NULL;
|
||||
g->finobjsur = g->finobjold1 = g->finobjrold = NULL;
|
||||
g->sweepgc = NULL;
|
||||
g->gray = g->grayagain = NULL;
|
||||
g->weak = g->ephemeron = g->allweak = NULL;
|
||||
@@ -430,8 +403,8 @@ LUA_API lua_State *lua_newstate (lua_Alloc f, void *ud) {
|
||||
|
||||
|
||||
LUA_API void lua_close (lua_State *L) {
|
||||
L = G(L)->mainthread; /* only the main thread can be closed */
|
||||
lua_lock(L);
|
||||
L = G(L)->mainthread; /* only the main thread can be closed */
|
||||
close_state(L);
|
||||
}
|
||||
|
||||
|
||||
@@ -32,13 +32,29 @@
|
||||
**
|
||||
** 'allgc' -> 'survival': new objects;
|
||||
** 'survival' -> 'old': objects that survived one collection;
|
||||
** 'old' -> 'reallyold': objects that became old in last collection;
|
||||
** 'old1' -> 'reallyold': objects that became old in last collection;
|
||||
** 'reallyold' -> NULL: objects old for more than one cycle.
|
||||
**
|
||||
** 'finobj' -> 'finobjsur': new objects marked for finalization;
|
||||
** 'finobjsur' -> 'finobjold': survived """";
|
||||
** 'finobjold' -> 'finobjrold': just old """";
|
||||
** 'finobjsur' -> 'finobjold1': survived """";
|
||||
** 'finobjold1' -> 'finobjrold': just old """";
|
||||
** 'finobjrold' -> NULL: really old """".
|
||||
**
|
||||
** All lists can contain elements older than their main ages, due
|
||||
** to 'luaC_checkfinalizer' and 'udata2finalize', which move
|
||||
** objects between the normal lists and the "marked for finalization"
|
||||
** lists. Moreover, barriers can age young objects in young lists as
|
||||
** OLD0, which then become OLD1. However, a list never contains
|
||||
** elements younger than their main ages.
|
||||
**
|
||||
** The generational collector also uses a pointer 'firstold1', which
|
||||
** points to the first OLD1 object in the list. It is used to optimize
|
||||
** 'markold'. (Potentially OLD1 objects can be anywhere between 'allgc'
|
||||
** and 'reallyold', but often the list has no OLD1 objects or they are
|
||||
** after 'old1'.) Note the difference between it and 'old1':
|
||||
** 'firstold1': no OLD1 objects before this point; there can be all
|
||||
** ages after it.
|
||||
** 'old1': no objects younger than OLD1 after this point.
|
||||
*/
|
||||
|
||||
/*
|
||||
@@ -47,7 +63,7 @@
|
||||
** can become gray have such a field. The field is not the same
|
||||
** in all objects, but it always has this name.) Any gray object
|
||||
** must belong to one of these lists, and all objects in these lists
|
||||
** must be gray:
|
||||
** must be gray (with two exceptions explained below):
|
||||
**
|
||||
** 'gray': regular gray objects, still waiting to be visited.
|
||||
** 'grayagain': objects that must be revisited at the atomic phase.
|
||||
@@ -58,54 +74,26 @@
|
||||
** 'weak': tables with weak values to be cleared;
|
||||
** 'ephemeron': ephemeron tables with white->white entries;
|
||||
** 'allweak': tables with weak keys and/or weak values to be cleared.
|
||||
**
|
||||
** The exceptions to that "gray rule" are:
|
||||
** - TOUCHED2 objects in generational mode stay in a gray list (because
|
||||
** they must be visited again at the end of the cycle), but they are
|
||||
** marked black because assignments to them must activate barriers (to
|
||||
** move them back to TOUCHED1).
|
||||
** - Open upvales are kept gray to avoid barriers, but they stay out
|
||||
** of gray lists. (They don't even have a 'gclist' field.)
|
||||
*/
|
||||
|
||||
|
||||
|
||||
/*
|
||||
** About 'nCcalls': each thread in Lua (a lua_State) keeps a count of
|
||||
** 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 has two parts: the lower part is the count itself; the
|
||||
** higher part counts the number of non-yieldable calls in the stack.
|
||||
** (They are together so that we can change both with one instruction.)
|
||||
**
|
||||
** Because calls to external C functions can use an unknown amount
|
||||
** of space (e.g., functions using an auxiliary buffer), calls
|
||||
** to these functions add more than one to the count (see CSTACKCF).
|
||||
**
|
||||
** The proper count excludes the number of CallInfo structures allocated
|
||||
** by Lua, as a kind of "potential" calls. So, when Lua calls a function
|
||||
** (and "consumes" one CallInfo), it needs neither to decrement nor to
|
||||
** check 'nCcalls', as its use of C stack is already accounted for.
|
||||
** About 'nCcalls': This count has two parts: the lower 16 bits counts
|
||||
** the number of recursive invocations in the C stack; the higher
|
||||
** 16 bits counts the number of non-yieldable calls in the stack.
|
||||
** (They are together so that we can change and save both with one
|
||||
** instruction.)
|
||||
*/
|
||||
|
||||
/* 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)
|
||||
@@ -120,13 +108,8 @@
|
||||
/* Decrement the number of non-yieldable calls */
|
||||
#define decnny(L) ((L)->nCcalls -= 0x10000)
|
||||
|
||||
/* 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 increment nCcalls */
|
||||
#define decXCcalls(L) ((L)->nCcalls -= 0x10000 - CSTACKCF)
|
||||
|
||||
|
||||
/* Non-yieldable call increment */
|
||||
#define nyci (0x10000 | 1)
|
||||
|
||||
|
||||
|
||||
@@ -144,12 +127,20 @@ struct lua_longjmp; /* defined in ldo.c */
|
||||
#endif
|
||||
|
||||
|
||||
/* extra stack space to handle TM calls and some other extras */
|
||||
/*
|
||||
** Extra stack space to handle TM calls and some other extras. This
|
||||
** space is not included in 'stack_last'. It is used only to avoid stack
|
||||
** checks, either because the element will be promptly popped or because
|
||||
** there will be a stack check soon after the push. Function frames
|
||||
** never use this extra space, so it does not need to be kept clean.
|
||||
*/
|
||||
#define EXTRA_STACK 5
|
||||
|
||||
|
||||
#define BASIC_STACK_SIZE (2*LUA_MINSTACK)
|
||||
|
||||
#define stacksize(th) cast_int((th)->stack_last - (th)->stack)
|
||||
|
||||
|
||||
/* kinds of Garbage Collection */
|
||||
#define KGC_INC 0 /* incremental gc */
|
||||
@@ -173,7 +164,7 @@ typedef struct CallInfo {
|
||||
union {
|
||||
struct { /* only for Lua functions */
|
||||
const Instruction *savedpc;
|
||||
l_signalT trap;
|
||||
volatile l_signalT trap;
|
||||
int nextraargs; /* # of extra arguments in vararg functions */
|
||||
} l;
|
||||
struct { /* only for C functions */
|
||||
@@ -200,14 +191,15 @@ typedef struct CallInfo {
|
||||
*/
|
||||
#define CIST_OAH (1<<0) /* original value of 'allowhook' */
|
||||
#define CIST_C (1<<1) /* call is running a C function */
|
||||
#define CIST_HOOKED (1<<2) /* call is running a debug hook */
|
||||
#define CIST_YPCALL (1<<3) /* call is a yieldable protected call */
|
||||
#define CIST_TAIL (1<<4) /* call was tail called */
|
||||
#define CIST_HOOKYIELD (1<<5) /* last hook called yielded */
|
||||
#define CIST_FIN (1<<6) /* call is running a finalizer */
|
||||
#define CIST_TRAN (1<<7) /* 'ci' has transfer information */
|
||||
#define CIST_FRESH (1<<2) /* call is on a fresh "luaV_execute" frame */
|
||||
#define CIST_HOOKED (1<<3) /* call is running a debug hook */
|
||||
#define CIST_YPCALL (1<<4) /* call is a yieldable protected call */
|
||||
#define CIST_TAIL (1<<5) /* call was tail called */
|
||||
#define CIST_HOOKYIELD (1<<6) /* last hook called yielded */
|
||||
#define CIST_FIN (1<<7) /* call is running a finalizer */
|
||||
#define CIST_TRAN (1<<8) /* 'ci' has transfer information */
|
||||
#if defined(LUA_COMPAT_LT_LE)
|
||||
#define CIST_LEQ (1<<8) /* using __lt for __le */
|
||||
#define CIST_LEQ (1<<9) /* using __lt for __le */
|
||||
#endif
|
||||
|
||||
/* active function is a Lua function */
|
||||
@@ -257,10 +249,11 @@ typedef struct global_State {
|
||||
GCObject *fixedgc; /* list of objects not to be collected */
|
||||
/* fields for generational collector */
|
||||
GCObject *survival; /* start of objects that survived one GC cycle */
|
||||
GCObject *old; /* start of old objects */
|
||||
GCObject *reallyold; /* old objects with more than one cycle */
|
||||
GCObject *old1; /* start of old1 objects */
|
||||
GCObject *reallyold; /* objects more than one cycle old ("really old") */
|
||||
GCObject *firstold1; /* first OLD1 object in the list (if any) */
|
||||
GCObject *finobjsur; /* list of survival objects with finalizers */
|
||||
GCObject *finobjold; /* list of old objects with finalizers */
|
||||
GCObject *finobjold1; /* list of old1 objects with finalizers */
|
||||
GCObject *finobjrold; /* list of really old objects with finalizers */
|
||||
struct lua_State *twups; /* list of threads with open upvalues */
|
||||
lua_CFunction panic; /* to be called in unprotected errors */
|
||||
@@ -271,7 +264,6 @@ 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;
|
||||
|
||||
|
||||
@@ -286,8 +278,7 @@ struct lua_State {
|
||||
StkId top; /* first free slot in the stack */
|
||||
global_State *l_G;
|
||||
CallInfo *ci; /* call info for current function */
|
||||
const Instruction *oldpc; /* last pc traced */
|
||||
StkId stack_last; /* last free slot in the stack */
|
||||
StkId stack_last; /* end of stack (last element + 1) */
|
||||
StkId stack; /* stack base */
|
||||
UpVal *openupval; /* list of open upvalues in this stack */
|
||||
GCObject *gclist;
|
||||
@@ -296,11 +287,11 @@ struct lua_State {
|
||||
CallInfo base_ci; /* CallInfo for first level (C calling Lua) */
|
||||
volatile lua_Hook hook;
|
||||
ptrdiff_t errfunc; /* current error handling function (stack index) */
|
||||
l_uint32 nCcalls; /* number of allowed nested C calls - 'nci' */
|
||||
int stacksize;
|
||||
l_uint32 nCcalls; /* number of nested (non-yieldable | C) calls */
|
||||
int oldpc; /* last pc traced */
|
||||
int basehookcount;
|
||||
int hookcount;
|
||||
l_signalT hookmask;
|
||||
volatile l_signalT hookmask;
|
||||
};
|
||||
|
||||
|
||||
@@ -309,6 +300,12 @@ struct lua_State {
|
||||
|
||||
/*
|
||||
** Union of all collectable objects (only for conversions)
|
||||
** ISO C99, 6.5.2.3 p.5:
|
||||
** "if a union contains several structures that share a common initial
|
||||
** sequence [...], and if the union object currently contains one
|
||||
** of these structures, it is permitted to inspect the common initial
|
||||
** part of any of them anywhere that a declaration of the complete type
|
||||
** of the union is visible."
|
||||
*/
|
||||
union GCUnion {
|
||||
GCObject gc; /* common header */
|
||||
@@ -322,20 +319,25 @@ union GCUnion {
|
||||
};
|
||||
|
||||
|
||||
/*
|
||||
** ISO C99, 6.7.2.1 p.14:
|
||||
** "A pointer to a union object, suitably converted, points to each of
|
||||
** its members [...], and vice versa."
|
||||
*/
|
||||
#define cast_u(o) cast(union GCUnion *, (o))
|
||||
|
||||
/* macros to convert a GCObject into a specific value */
|
||||
#define gco2ts(o) \
|
||||
check_exp(novariant((o)->tt) == LUA_TSTRING, &((cast_u(o))->ts))
|
||||
#define gco2u(o) check_exp((o)->tt == LUA_TUSERDATA, &((cast_u(o))->u))
|
||||
#define gco2lcl(o) check_exp((o)->tt == LUA_TLCL, &((cast_u(o))->cl.l))
|
||||
#define gco2ccl(o) check_exp((o)->tt == LUA_TCCL, &((cast_u(o))->cl.c))
|
||||
#define gco2u(o) check_exp((o)->tt == LUA_VUSERDATA, &((cast_u(o))->u))
|
||||
#define gco2lcl(o) check_exp((o)->tt == LUA_VLCL, &((cast_u(o))->cl.l))
|
||||
#define gco2ccl(o) check_exp((o)->tt == LUA_VCCL, &((cast_u(o))->cl.c))
|
||||
#define gco2cl(o) \
|
||||
check_exp(novariant((o)->tt) == LUA_TFUNCTION, &((cast_u(o))->cl))
|
||||
#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((o)->tt == LUA_TUPVAL, &((cast_u(o))->upv))
|
||||
#define gco2t(o) check_exp((o)->tt == LUA_VTABLE, &((cast_u(o))->h))
|
||||
#define gco2p(o) check_exp((o)->tt == LUA_VPROTO, &((cast_u(o))->p))
|
||||
#define gco2th(o) check_exp((o)->tt == LUA_VTHREAD, &((cast_u(o))->th))
|
||||
#define gco2upv(o) check_exp((o)->tt == LUA_VUPVAL, &((cast_u(o))->upv))
|
||||
|
||||
|
||||
/*
|
||||
@@ -353,12 +355,11 @@ LUAI_FUNC void luaE_freethread (lua_State *L, lua_State *L1);
|
||||
LUAI_FUNC CallInfo *luaE_extendCI (lua_State *L);
|
||||
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_checkcstack (lua_State *L);
|
||||
LUAI_FUNC void luaE_incCstack (lua_State *L);
|
||||
LUAI_FUNC void luaE_warning (lua_State *L, const char *msg, int tocont);
|
||||
LUAI_FUNC void luaE_warnerror (lua_State *L, const char *where);
|
||||
|
||||
|
||||
#define luaE_exitCcall(L) ((L)->nCcalls++)
|
||||
|
||||
#endif
|
||||
|
||||
|
||||
@@ -22,16 +22,6 @@
|
||||
#include "lstring.h"
|
||||
|
||||
|
||||
/*
|
||||
** Lua will use at most ~(2^LUAI_HASHLIMIT) bytes from a string to
|
||||
** compute its hash
|
||||
*/
|
||||
#if !defined(LUAI_HASHLIMIT)
|
||||
#define LUAI_HASHLIMIT 5
|
||||
#endif
|
||||
|
||||
|
||||
|
||||
/*
|
||||
** Maximum size for string table.
|
||||
*/
|
||||
@@ -43,7 +33,7 @@
|
||||
*/
|
||||
int luaS_eqlngstr (TString *a, TString *b) {
|
||||
size_t len = a->u.lnglen;
|
||||
lua_assert(a->tt == LUA_TLNGSTR && b->tt == LUA_TLNGSTR);
|
||||
lua_assert(a->tt == LUA_VLNGSTR && b->tt == LUA_VLNGSTR);
|
||||
return (a == b) || /* same instance or... */
|
||||
((len == b->u.lnglen) && /* equal length and ... */
|
||||
(memcmp(getstr(a), getstr(b), len) == 0)); /* equal contents */
|
||||
@@ -52,17 +42,17 @@ int luaS_eqlngstr (TString *a, TString *b) {
|
||||
|
||||
unsigned int luaS_hash (const char *str, size_t l, unsigned int seed) {
|
||||
unsigned int h = seed ^ cast_uint(l);
|
||||
size_t step = (l >> LUAI_HASHLIMIT) + 1;
|
||||
for (; l >= step; l -= step)
|
||||
for (; l > 0; l--)
|
||||
h ^= ((h<<5) + (h>>2) + cast_byte(str[l - 1]));
|
||||
return h;
|
||||
}
|
||||
|
||||
|
||||
unsigned int luaS_hashlongstr (TString *ts) {
|
||||
lua_assert(ts->tt == LUA_TLNGSTR);
|
||||
lua_assert(ts->tt == LUA_VLNGSTR);
|
||||
if (ts->extra == 0) { /* no hash? */
|
||||
ts->hash = luaS_hash(getstr(ts), ts->u.lnglen, ts->hash);
|
||||
size_t len = ts->u.lnglen;
|
||||
ts->hash = luaS_hash(getstr(ts), len, ts->hash);
|
||||
ts->extra = 1; /* now it has its hash */
|
||||
}
|
||||
return ts->hash;
|
||||
@@ -165,7 +155,7 @@ static TString *createstrobj (lua_State *L, size_t l, int tag, unsigned int h) {
|
||||
|
||||
|
||||
TString *luaS_createlngstrobj (lua_State *L, size_t l) {
|
||||
TString *ts = createstrobj(L, l, LUA_TLNGSTR, G(L)->seed);
|
||||
TString *ts = createstrobj(L, l, LUA_VLNGSTR, G(L)->seed);
|
||||
ts->u.lnglen = l;
|
||||
return ts;
|
||||
}
|
||||
@@ -215,7 +205,7 @@ static TString *internshrstr (lua_State *L, const char *str, size_t l) {
|
||||
growstrtab(L, tb);
|
||||
list = &tb->hash[lmod(h, tb->size)]; /* rehash with new size */
|
||||
}
|
||||
ts = createstrobj(L, l, LUA_TSHRSTR, h);
|
||||
ts = createstrobj(L, l, LUA_VSHRSTR, h);
|
||||
memcpy(getstr(ts), str, l * sizeof(char));
|
||||
ts->shrlen = cast_byte(l);
|
||||
ts->u.hnext = *list;
|
||||
@@ -271,7 +261,7 @@ Udata *luaS_newudata (lua_State *L, size_t s, int nuvalue) {
|
||||
GCObject *o;
|
||||
if (unlikely(s > MAX_SIZE - udatamemoffset(nuvalue)))
|
||||
luaM_toobig(L);
|
||||
o = luaC_newobj(L, LUA_TUSERDATA, sizeudata(nuvalue, s));
|
||||
o = luaC_newobj(L, LUA_VUSERDATA, sizeudata(nuvalue, s));
|
||||
u = gco2u(o);
|
||||
u->len = s;
|
||||
u->nuvalue = nuvalue;
|
||||
|
||||
@@ -19,7 +19,11 @@
|
||||
#define MEMERRMSG "not enough memory"
|
||||
|
||||
|
||||
#define sizelstring(l) (sizeof(TString) + ((l) + 1) * sizeof(char))
|
||||
/*
|
||||
** Size of a TString: Size of the header plus space for the string
|
||||
** itself (including final '\0').
|
||||
*/
|
||||
#define sizelstring(l) (offsetof(TString, contents) + ((l) + 1) * sizeof(char))
|
||||
|
||||
#define luaS_newliteral(L, s) (luaS_newlstr(L, "" s, \
|
||||
(sizeof(s)/sizeof(char))-1))
|
||||
@@ -28,13 +32,13 @@
|
||||
/*
|
||||
** test whether a string is a reserved word
|
||||
*/
|
||||
#define isreserved(s) ((s)->tt == LUA_TSHRSTR && (s)->extra > 0)
|
||||
#define isreserved(s) ((s)->tt == LUA_VSHRSTR && (s)->extra > 0)
|
||||
|
||||
|
||||
/*
|
||||
** equality for short strings, which are always internalized
|
||||
*/
|
||||
#define eqshrstr(a,b) check_exp((a)->tt == LUA_TSHRSTR, (a) == (b))
|
||||
#define eqshrstr(a,b) check_exp((a)->tt == LUA_VSHRSTR, (a) == (b))
|
||||
|
||||
|
||||
LUAI_FUNC unsigned int luaS_hash (const char *str, size_t l, unsigned int seed);
|
||||
|
||||
@@ -206,22 +206,38 @@ static int str_char (lua_State *L) {
|
||||
}
|
||||
|
||||
|
||||
static int writer (lua_State *L, const void *b, size_t size, void *B) {
|
||||
(void)L;
|
||||
luaL_addlstring((luaL_Buffer *) B, (const char *)b, size);
|
||||
/*
|
||||
** Buffer to store the result of 'string.dump'. It must be initialized
|
||||
** after the call to 'lua_dump', to ensure that the function is on the
|
||||
** top of the stack when 'lua_dump' is called. ('luaL_buffinit' might
|
||||
** push stuff.)
|
||||
*/
|
||||
struct str_Writer {
|
||||
int init; /* true iff buffer has been initialized */
|
||||
luaL_Buffer B;
|
||||
};
|
||||
|
||||
|
||||
static int writer (lua_State *L, const void *b, size_t size, void *ud) {
|
||||
struct str_Writer *state = (struct str_Writer *)ud;
|
||||
if (!state->init) {
|
||||
state->init = 1;
|
||||
luaL_buffinit(L, &state->B);
|
||||
}
|
||||
luaL_addlstring(&state->B, (const char *)b, size);
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
||||
static int str_dump (lua_State *L) {
|
||||
luaL_Buffer b;
|
||||
struct str_Writer state;
|
||||
int strip = lua_toboolean(L, 2);
|
||||
luaL_checktype(L, 1, LUA_TFUNCTION);
|
||||
lua_settop(L, 1);
|
||||
luaL_buffinit(L,&b);
|
||||
if (lua_dump(L, writer, &b, strip) != 0)
|
||||
lua_settop(L, 1); /* ensure function is on the top of the stack */
|
||||
state.init = 0;
|
||||
if (lua_dump(L, writer, &state, strip) != 0)
|
||||
return luaL_error(L, "unable to dump given function");
|
||||
luaL_pushresult(&b);
|
||||
luaL_pushresult(&state.B);
|
||||
return 1;
|
||||
}
|
||||
|
||||
@@ -988,7 +1004,7 @@ static int str_gsub (lua_State *L) {
|
||||
** to nibble boundaries by making what is left after that first digit a
|
||||
** multiple of 4.
|
||||
*/
|
||||
#define L_NBFD ((l_mathlim(MANT_DIG) - 1)%4 + 1)
|
||||
#define L_NBFD ((l_floatatt(MANT_DIG) - 1)%4 + 1)
|
||||
|
||||
|
||||
/*
|
||||
@@ -1056,7 +1072,7 @@ static int lua_number2strx (lua_State *L, char *buff, int sz,
|
||||
** and '\0') + number of decimal digits to represent maxfloat (which
|
||||
** is maximum exponent + 1). (99+3+1, adding some extra, 110)
|
||||
*/
|
||||
#define MAX_ITEMF (110 + l_mathlim(MAX_10_EXP))
|
||||
#define MAX_ITEMF (110 + l_floatatt(MAX_10_EXP))
|
||||
|
||||
|
||||
/*
|
||||
@@ -1071,7 +1087,10 @@ static int lua_number2strx (lua_State *L, char *buff, int sz,
|
||||
|
||||
|
||||
/* valid flags in a format specification */
|
||||
#define FLAGS "-+ #0"
|
||||
#if !defined(L_FMTFLAGS)
|
||||
#define L_FMTFLAGS "-+ #0"
|
||||
#endif
|
||||
|
||||
|
||||
/*
|
||||
** maximum size of each format specification (such as "%-099.99d")
|
||||
@@ -1169,8 +1188,8 @@ static void addliteral (lua_State *L, luaL_Buffer *b, int arg) {
|
||||
|
||||
static const char *scanformat (lua_State *L, const char *strfrmt, char *form) {
|
||||
const char *p = strfrmt;
|
||||
while (*p != '\0' && strchr(FLAGS, *p) != NULL) p++; /* skip flags */
|
||||
if ((size_t)(p - strfrmt) >= sizeof(FLAGS)/sizeof(char))
|
||||
while (*p != '\0' && strchr(L_FMTFLAGS, *p) != NULL) p++; /* skip flags */
|
||||
if ((size_t)(p - strfrmt) >= sizeof(L_FMTFLAGS)/sizeof(char))
|
||||
luaL_error(L, "invalid format (repeated flags)");
|
||||
if (isdigit(uchar(*p))) p++; /* skip width */
|
||||
if (isdigit(uchar(*p))) p++; /* (2 digits at most) */
|
||||
@@ -1247,11 +1266,15 @@ static int str_format (lua_State *L) {
|
||||
case 'e': case 'E': case 'g': case 'G': {
|
||||
lua_Number n = luaL_checknumber(L, arg);
|
||||
addlenmod(form, LUA_NUMBER_FRMLEN);
|
||||
nb = snprintf(buff, maxitem, form, (LUAI_UACNUMBER)n);
|
||||
nb = l_sprintf(buff, maxitem, form, (LUAI_UACNUMBER)n);
|
||||
break;
|
||||
}
|
||||
case 'p': {
|
||||
const void *p = lua_topointer(L, arg);
|
||||
if (p == NULL) { /* avoid calling 'printf' with argument NULL */
|
||||
p = "(null)"; /* result */
|
||||
form[strlen(form) - 1] = 's'; /* format it as a string */
|
||||
}
|
||||
nb = l_sprintf(buff, maxitem, form, p);
|
||||
break;
|
||||
}
|
||||
@@ -1342,7 +1365,6 @@ typedef union Ftypes {
|
||||
float f;
|
||||
double d;
|
||||
lua_Number n;
|
||||
char buff[5 * sizeof(lua_Number)]; /* enough for any float type */
|
||||
} Ftypes;
|
||||
|
||||
|
||||
@@ -1512,12 +1534,10 @@ static void packint (luaL_Buffer *b, lua_Unsigned n,
|
||||
** Copy 'size' bytes from 'src' to 'dest', correcting endianness if
|
||||
** given 'islittle' is different from native endianness.
|
||||
*/
|
||||
static void copywithendian (volatile char *dest, volatile const char *src,
|
||||
static void copywithendian (char *dest, const char *src,
|
||||
int size, int islittle) {
|
||||
if (islittle == nativeendian.little) {
|
||||
while (size-- != 0)
|
||||
*(dest++) = *(src++);
|
||||
}
|
||||
if (islittle == nativeendian.little)
|
||||
memcpy(dest, src, size);
|
||||
else {
|
||||
dest += size - 1;
|
||||
while (size-- != 0)
|
||||
@@ -1561,14 +1581,14 @@ static int str_pack (lua_State *L) {
|
||||
break;
|
||||
}
|
||||
case Kfloat: { /* floating-point options */
|
||||
volatile Ftypes u;
|
||||
Ftypes u;
|
||||
char *buff = luaL_prepbuffsize(&b, size);
|
||||
lua_Number n = luaL_checknumber(L, arg); /* get argument */
|
||||
if (size == sizeof(u.f)) u.f = (float)n; /* copy it into 'u' */
|
||||
else if (size == sizeof(u.d)) u.d = (double)n;
|
||||
else u.n = n;
|
||||
/* move 'u' to final result, correcting endianness if needed */
|
||||
copywithendian(buff, u.buff, size, h.islittle);
|
||||
copywithendian(buff, (char *)&u, size, h.islittle);
|
||||
luaL_addsize(&b, size);
|
||||
break;
|
||||
}
|
||||
@@ -1694,9 +1714,9 @@ static int str_unpack (lua_State *L) {
|
||||
break;
|
||||
}
|
||||
case Kfloat: {
|
||||
volatile Ftypes u;
|
||||
Ftypes u;
|
||||
lua_Number num;
|
||||
copywithendian(u.buff, data + pos, size, h.islittle);
|
||||
copywithendian((char *)&u, data + pos, size, h.islittle);
|
||||
if (size == sizeof(u.f)) num = (lua_Number)u.f;
|
||||
else if (size == sizeof(u.d)) num = (lua_Number)u.d;
|
||||
else num = u.n;
|
||||
@@ -1715,7 +1735,7 @@ static int str_unpack (lua_State *L) {
|
||||
break;
|
||||
}
|
||||
case Kzstr: {
|
||||
size_t len = (int)strlen(data + pos);
|
||||
size_t len = strlen(data + pos);
|
||||
luaL_argcheck(L, pos + len < ld, 2,
|
||||
"unfinished string for format 'z'");
|
||||
lua_pushlstring(L, data + pos, len);
|
||||
|
||||
@@ -88,8 +88,8 @@
|
||||
#define dummynode (&dummynode_)
|
||||
|
||||
static const Node dummynode_ = {
|
||||
{{NULL}, LUA_TEMPTY, /* value's value and type */
|
||||
LUA_TNIL, 0, {NULL}} /* key type, next, and key value */
|
||||
{{NULL}, LUA_VEMPTY, /* value's value and type */
|
||||
LUA_VNIL, 0, {NULL}} /* key type, next, and key value */
|
||||
};
|
||||
|
||||
|
||||
@@ -135,19 +135,21 @@ static int l_hashfloat (lua_Number n) {
|
||||
*/
|
||||
static Node *mainposition (const Table *t, int ktt, const Value *kvl) {
|
||||
switch (withvariant(ktt)) {
|
||||
case LUA_TNUMINT:
|
||||
case LUA_VNUMINT:
|
||||
return hashint(t, ivalueraw(*kvl));
|
||||
case LUA_TNUMFLT:
|
||||
case LUA_VNUMFLT:
|
||||
return hashmod(t, l_hashfloat(fltvalueraw(*kvl)));
|
||||
case LUA_TSHRSTR:
|
||||
case LUA_VSHRSTR:
|
||||
return hashstr(t, tsvalueraw(*kvl));
|
||||
case LUA_TLNGSTR:
|
||||
case LUA_VLNGSTR:
|
||||
return hashpow2(t, luaS_hashlongstr(tsvalueraw(*kvl)));
|
||||
case LUA_TBOOLEAN:
|
||||
return hashboolean(t, bvalueraw(*kvl));
|
||||
case LUA_TLIGHTUSERDATA:
|
||||
case LUA_VFALSE:
|
||||
return hashboolean(t, 0);
|
||||
case LUA_VTRUE:
|
||||
return hashboolean(t, 1);
|
||||
case LUA_VLIGHTUSERDATA:
|
||||
return hashpointer(t, pvalueraw(*kvl));
|
||||
case LUA_TLCF:
|
||||
case LUA_VLCF:
|
||||
return hashpointer(t, fvalueraw(*kvl));
|
||||
default:
|
||||
return hashpointer(t, gcvalueraw(*kvl));
|
||||
@@ -155,36 +157,50 @@ static Node *mainposition (const Table *t, int ktt, const Value *kvl) {
|
||||
}
|
||||
|
||||
|
||||
/*
|
||||
** Returns the main position of an element given as a 'TValue'
|
||||
*/
|
||||
static Node *mainpositionTV (const Table *t, const TValue *key) {
|
||||
return mainposition(t, rawtt(key), valraw(key));
|
||||
}
|
||||
|
||||
|
||||
/*
|
||||
** Check whether key 'k1' is equal to the key in node 'n2'.
|
||||
** This equality is raw, so there are no metamethods. Floats
|
||||
** with integer values have been normalized, so integers cannot
|
||||
** be equal to floats. It is assumed that 'eqshrstr' is simply
|
||||
** pointer equality, so that short strings are handled in the
|
||||
** default case.
|
||||
** Check whether key 'k1' is equal to the key in node 'n2'. This
|
||||
** equality is raw, so there are no metamethods. Floats with integer
|
||||
** values have been normalized, so integers cannot be equal to
|
||||
** floats. It is assumed that 'eqshrstr' is simply pointer equality, so
|
||||
** that short strings are handled in the default case.
|
||||
** A true 'deadok' means to accept dead keys as equal to their original
|
||||
** values. All dead keys are compared in the default case, by pointer
|
||||
** identity. (Only collectable objects can produce dead keys.) Note that
|
||||
** dead long strings are also compared by identity.
|
||||
** Once a key is dead, its corresponding value may be collected, and
|
||||
** then another value can be created with the same address. If this
|
||||
** other value is given to 'next', 'equalkey' will signal a false
|
||||
** positive. In a regular traversal, this situation should never happen,
|
||||
** as all keys given to 'next' came from the table itself, and therefore
|
||||
** could not have been collected. Outside a regular traversal, we
|
||||
** have garbage in, garbage out. What is relevant is that this false
|
||||
** positive does not break anything. (In particular, 'next' will return
|
||||
** some other valid item on the table or nil.)
|
||||
*/
|
||||
static int equalkey (const TValue *k1, const Node *n2) {
|
||||
if (rawtt(k1) != keytt(n2)) /* not the same variants? */
|
||||
static int equalkey (const TValue *k1, const Node *n2, int deadok) {
|
||||
if ((rawtt(k1) != keytt(n2)) && /* not the same variants? */
|
||||
!(deadok && keyisdead(n2) && iscollectable(k1)))
|
||||
return 0; /* cannot be same key */
|
||||
switch (ttypetag(k1)) {
|
||||
case LUA_TNIL:
|
||||
switch (keytt(n2)) {
|
||||
case LUA_VNIL: case LUA_VFALSE: case LUA_VTRUE:
|
||||
return 1;
|
||||
case LUA_TNUMINT:
|
||||
case LUA_VNUMINT:
|
||||
return (ivalue(k1) == keyival(n2));
|
||||
case LUA_TNUMFLT:
|
||||
case LUA_VNUMFLT:
|
||||
return luai_numeq(fltvalue(k1), fltvalueraw(keyval(n2)));
|
||||
case LUA_TBOOLEAN:
|
||||
return bvalue(k1) == bvalueraw(keyval(n2));
|
||||
case LUA_TLIGHTUSERDATA:
|
||||
case LUA_VLIGHTUSERDATA:
|
||||
return pvalue(k1) == pvalueraw(keyval(n2));
|
||||
case LUA_TLCF:
|
||||
case LUA_VLCF:
|
||||
return fvalue(k1) == fvalueraw(keyval(n2));
|
||||
case LUA_TLNGSTR:
|
||||
case ctb(LUA_VLNGSTR):
|
||||
return luaS_eqlngstr(tsvalue(k1), keystrval(n2));
|
||||
default:
|
||||
return gcvalue(k1) == gcvalueraw(keyval(n2));
|
||||
@@ -248,11 +264,12 @@ static unsigned int setlimittosize (Table *t) {
|
||||
/*
|
||||
** "Generic" get version. (Not that generic: not valid for integers,
|
||||
** which may be in array part, nor for floats with integral values.)
|
||||
** See explanation about 'deadok' in function 'equalkey'.
|
||||
*/
|
||||
static const TValue *getgeneric (Table *t, const TValue *key) {
|
||||
static const TValue *getgeneric (Table *t, const TValue *key, int deadok) {
|
||||
Node *n = mainpositionTV(t, key);
|
||||
for (;;) { /* check whether 'key' is somewhere in the chain */
|
||||
if (equalkey(key, n))
|
||||
if (equalkey(key, n, deadok))
|
||||
return gval(n); /* that's it */
|
||||
else {
|
||||
int nx = gnext(n);
|
||||
@@ -289,7 +306,7 @@ static unsigned int findindex (lua_State *L, Table *t, TValue *key,
|
||||
if (i - 1u < asize) /* is 'key' inside array part? */
|
||||
return i; /* yes; that's the index */
|
||||
else {
|
||||
const TValue *n = getgeneric(t, key);
|
||||
const TValue *n = getgeneric(t, key, 1);
|
||||
if (unlikely(isabstkey(n)))
|
||||
luaG_runerror(L, "invalid key to 'next'"); /* key not found */
|
||||
i = cast_int(nodefromval(n) - gnode(t, 0)); /* key index in hash table */
|
||||
@@ -577,10 +594,10 @@ static void rehash (lua_State *L, Table *t, const TValue *ek) {
|
||||
|
||||
|
||||
Table *luaH_new (lua_State *L) {
|
||||
GCObject *o = luaC_newobj(L, LUA_TTABLE, sizeof(Table));
|
||||
GCObject *o = luaC_newobj(L, LUA_VTABLE, sizeof(Table));
|
||||
Table *t = gco2t(o);
|
||||
t->metatable = NULL;
|
||||
t->flags = cast_byte(~0);
|
||||
t->flags = cast_byte(maskflags); /* table has no metamethod fields */
|
||||
t->array = NULL;
|
||||
t->alimit = 0;
|
||||
setnodevector(L, t, 0);
|
||||
@@ -623,7 +640,7 @@ TValue *luaH_newkey (lua_State *L, Table *t, const TValue *key) {
|
||||
else if (ttisfloat(key)) {
|
||||
lua_Number f = fltvalue(key);
|
||||
lua_Integer k;
|
||||
if (luaV_flttointeger(f, &k, 0)) { /* does key fit in an integer? */
|
||||
if (luaV_flttointeger(f, &k, F2Ieq)) { /* does key fit in an integer? */
|
||||
setivalue(&aux, k);
|
||||
key = &aux; /* insert it as an integer */
|
||||
}
|
||||
@@ -707,7 +724,7 @@ const TValue *luaH_getint (Table *t, lua_Integer key) {
|
||||
*/
|
||||
const TValue *luaH_getshortstr (Table *t, TString *key) {
|
||||
Node *n = hashstr(t, key);
|
||||
lua_assert(key->tt == LUA_TSHRSTR);
|
||||
lua_assert(key->tt == LUA_VSHRSTR);
|
||||
for (;;) { /* check whether 'key' is somewhere in the chain */
|
||||
if (keyisshrstr(n) && eqshrstr(keystrval(n), key))
|
||||
return gval(n); /* that's it */
|
||||
@@ -722,12 +739,12 @@ const TValue *luaH_getshortstr (Table *t, TString *key) {
|
||||
|
||||
|
||||
const TValue *luaH_getstr (Table *t, TString *key) {
|
||||
if (key->tt == LUA_TSHRSTR)
|
||||
if (key->tt == LUA_VSHRSTR)
|
||||
return luaH_getshortstr(t, key);
|
||||
else { /* for long strings, use generic case */
|
||||
TValue ko;
|
||||
setsvalue(cast(lua_State *, NULL), &ko, key);
|
||||
return getgeneric(t, &ko);
|
||||
return getgeneric(t, &ko, 0);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -737,17 +754,17 @@ const TValue *luaH_getstr (Table *t, TString *key) {
|
||||
*/
|
||||
const TValue *luaH_get (Table *t, const TValue *key) {
|
||||
switch (ttypetag(key)) {
|
||||
case LUA_TSHRSTR: return luaH_getshortstr(t, tsvalue(key));
|
||||
case LUA_TNUMINT: return luaH_getint(t, ivalue(key));
|
||||
case LUA_TNIL: return &absentkey;
|
||||
case LUA_TNUMFLT: {
|
||||
case LUA_VSHRSTR: return luaH_getshortstr(t, tsvalue(key));
|
||||
case LUA_VNUMINT: return luaH_getint(t, ivalue(key));
|
||||
case LUA_VNIL: return &absentkey;
|
||||
case LUA_VNUMFLT: {
|
||||
lua_Integer k;
|
||||
if (luaV_flttointeger(fltvalue(key), &k, 0)) /* index is an integral? */
|
||||
if (luaV_flttointeger(fltvalue(key), &k, F2Ieq)) /* integral index? */
|
||||
return luaH_getint(t, k); /* use specialized version */
|
||||
/* else... */
|
||||
} /* FALLTHROUGH */
|
||||
default:
|
||||
return getgeneric(t, key);
|
||||
return getgeneric(t, key, 0);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -15,7 +15,12 @@
|
||||
#define gnext(n) ((n)->u.next)
|
||||
|
||||
|
||||
#define invalidateTMcache(t) ((t)->flags = 0)
|
||||
/*
|
||||
** Clear all bits of fast-access metamethods, which means that the table
|
||||
** may have any of these metamethods. (First access that fails after the
|
||||
** clearing will set the bit again.)
|
||||
*/
|
||||
#define invalidateTMcache(t) ((t)->flags &= ~maskflags)
|
||||
|
||||
|
||||
/* true when 't' is using 'dummynode' as its hash part */
|
||||
@@ -27,7 +32,7 @@
|
||||
|
||||
|
||||
/* returns the Node, given the value of a table entry */
|
||||
#define nodefromval(v) cast(Node *, (v))
|
||||
#define nodefromval(v) cast(Node *, (v))
|
||||
|
||||
|
||||
LUAI_FUNC const TValue *luaH_getint (Table *t, lua_Integer key);
|
||||
|
||||
@@ -73,14 +73,16 @@ static void badexit (const char *fmt, const char *s1, const char *s2) {
|
||||
|
||||
|
||||
static int tpanic (lua_State *L) {
|
||||
const char *msg = lua_tostring(L, -1);
|
||||
if (msg == NULL) msg = "error object is not a string";
|
||||
return (badexit("PANIC: unprotected error in call to Lua API (%s)\n",
|
||||
lua_tostring(L, -1), NULL),
|
||||
msg, NULL),
|
||||
0); /* do not return to Lua */
|
||||
}
|
||||
|
||||
|
||||
/*
|
||||
** Warning function for tests. Fist, it concatenates all parts of
|
||||
** Warning function for tests. First, it concatenates all parts of
|
||||
** 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
|
||||
@@ -119,7 +121,8 @@ static void warnf (void *ud, const char *msg, int tocont) {
|
||||
strcat(buff, msg); /* add new message to current warning */
|
||||
if (!tocont) { /* message finished? */
|
||||
lua_unlock(L);
|
||||
if (lua_getglobal(L, "_WARN") == LUA_TNIL)
|
||||
lua_getglobal(L, "_WARN");
|
||||
if (!lua_toboolean(L, -1))
|
||||
lua_pop(L, 1); /* ok, no previous unexpected warning */
|
||||
else {
|
||||
badexit("Unhandled warning in store mode: %s\naborting...\n",
|
||||
@@ -131,8 +134,7 @@ static void warnf (void *ud, const char *msg, int tocont) {
|
||||
if (buff[0] != '#' && onoff) /* unexpected warning? */
|
||||
badexit("Unexpected warning in test mode: %s\naborting...\n",
|
||||
buff, NULL);
|
||||
/* else */ /* FALLTHROUGH */
|
||||
}
|
||||
} /* FALLTHROUGH */
|
||||
case 1: { /* allow */
|
||||
if (onoff)
|
||||
fprintf(stderr, "Lua warning: %s\n", buff); /* print warning */
|
||||
@@ -143,7 +145,6 @@ static void warnf (void *ud, const char *msg, int tocont) {
|
||||
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;
|
||||
}
|
||||
}
|
||||
@@ -185,7 +186,8 @@ typedef union Header {
|
||||
|
||||
|
||||
Memcontrol l_memcontrol =
|
||||
{0UL, 0UL, 0UL, 0UL, (~0UL), {0UL, 0UL, 0UL, 0UL, 0UL, 0UL, 0UL, 0UL, 0UL}};
|
||||
{0, 0UL, 0UL, 0UL, 0UL, (~0UL),
|
||||
{0UL, 0UL, 0UL, 0UL, 0UL, 0UL, 0UL, 0UL, 0UL}};
|
||||
|
||||
|
||||
static void freeblock (Memcontrol *mc, Header *block) {
|
||||
@@ -224,6 +226,10 @@ void *debug_realloc (void *ud, void *b, size_t oldsize, size_t size) {
|
||||
freeblock(mc, block);
|
||||
return NULL;
|
||||
}
|
||||
if (mc->failnext) {
|
||||
mc->failnext = 0;
|
||||
return NULL; /* fake a single memory allocation error */
|
||||
}
|
||||
if (mc->countlimit != ~0UL && size != oldsize) { /* count limit in use? */
|
||||
if (mc->countlimit == 0)
|
||||
return NULL; /* fake a memory allocation error */
|
||||
@@ -303,11 +309,15 @@ static void printobj (global_State *g, GCObject *o) {
|
||||
ttypename(novariant(o->tt)), (void *)o,
|
||||
isdead(g,o) ? 'd' : isblack(o) ? 'b' : iswhite(o) ? 'w' : 'g',
|
||||
"ns01oTt"[getage(o)], o->marked);
|
||||
if (o->tt == LUA_TSHRSTR || o->tt == LUA_TLNGSTR)
|
||||
if (o->tt == LUA_VSHRSTR || o->tt == LUA_VLNGSTR)
|
||||
printf(" '%s'", getstr(gco2ts(o)));
|
||||
}
|
||||
|
||||
|
||||
void lua_printobj (lua_State *L, struct GCObject *o) {
|
||||
printobj(G(L), o);
|
||||
}
|
||||
|
||||
static int testobjref (global_State *g, GCObject *f, GCObject *t) {
|
||||
int r1 = testobjref1(g, f, t);
|
||||
if (!r1) {
|
||||
@@ -419,52 +429,54 @@ static void checkstack (global_State *g, lua_State *L1) {
|
||||
CallInfo *ci;
|
||||
UpVal *uv;
|
||||
lua_assert(!isdead(g, L1));
|
||||
if (L1->stack == NULL) { /* incomplete thread? */
|
||||
lua_assert(L1->openupval == NULL && L1->ci == NULL);
|
||||
return;
|
||||
}
|
||||
for (uv = L1->openupval; uv != NULL; uv = uv->u.open.next)
|
||||
lua_assert(upisopen(uv)); /* must be open */
|
||||
lua_assert(L1->top <= L1->stack_last);
|
||||
for (ci = L1->ci; ci != NULL; ci = ci->previous) {
|
||||
lua_assert(ci->top <= L1->stack_last);
|
||||
lua_assert(lua_checkpc(ci));
|
||||
}
|
||||
if (L1->stack) { /* complete thread? */
|
||||
for (o = L1->stack; o < L1->stack_last + EXTRA_STACK; o++)
|
||||
checkliveness(L1, s2v(o)); /* entire stack must have valid values */
|
||||
}
|
||||
else lua_assert(L1->stacksize == 0);
|
||||
for (o = L1->stack; o < L1->stack_last; o++)
|
||||
checkliveness(L1, s2v(o)); /* entire stack must have valid values */
|
||||
}
|
||||
|
||||
|
||||
static void checkrefs (global_State *g, GCObject *o) {
|
||||
switch (o->tt) {
|
||||
case LUA_TUSERDATA: {
|
||||
case LUA_VUSERDATA: {
|
||||
checkudata(g, gco2u(o));
|
||||
break;
|
||||
}
|
||||
case LUA_TUPVAL: {
|
||||
case LUA_VUPVAL: {
|
||||
checkvalref(g, o, gco2upv(o)->v);
|
||||
break;
|
||||
}
|
||||
case LUA_TTABLE: {
|
||||
case LUA_VTABLE: {
|
||||
checktable(g, gco2t(o));
|
||||
break;
|
||||
}
|
||||
case LUA_TTHREAD: {
|
||||
case LUA_VTHREAD: {
|
||||
checkstack(g, gco2th(o));
|
||||
break;
|
||||
}
|
||||
case LUA_TLCL: {
|
||||
case LUA_VLCL: {
|
||||
checkLclosure(g, gco2lcl(o));
|
||||
break;
|
||||
}
|
||||
case LUA_TCCL: {
|
||||
case LUA_VCCL: {
|
||||
checkCclosure(g, gco2ccl(o));
|
||||
break;
|
||||
}
|
||||
case LUA_TPROTO: {
|
||||
case LUA_VPROTO: {
|
||||
checkproto(g, gco2p(o));
|
||||
break;
|
||||
}
|
||||
case LUA_TSHRSTR:
|
||||
case LUA_TLNGSTR: {
|
||||
case LUA_VSHRSTR:
|
||||
case LUA_VLNGSTR: {
|
||||
lua_assert(!isgray(o)); /* strings are never gray */
|
||||
break;
|
||||
}
|
||||
@@ -476,7 +488,7 @@ static void checkrefs (global_State *g, GCObject *o) {
|
||||
/*
|
||||
** Check consistency of an object:
|
||||
** - Dead objects can only happen in the 'allgc' list during a sweep
|
||||
** phase (controled by the caller through 'maybedead').
|
||||
** phase (controlled by the caller through 'maybedead').
|
||||
** - During pause, all objects must be white.
|
||||
** - In generational mode:
|
||||
** * objects must be old enough for their lists ('listage').
|
||||
@@ -497,8 +509,8 @@ static void checkobject (global_State *g, GCObject *o, int maybedead,
|
||||
lua_assert(isblack(o) ||
|
||||
getage(o) == G_TOUCHED1 ||
|
||||
getage(o) == G_OLD0 ||
|
||||
o->tt == LUA_TTHREAD ||
|
||||
(o->tt == LUA_TUPVAL && upisopen(gco2upv(o))));
|
||||
o->tt == LUA_VTHREAD ||
|
||||
(o->tt == LUA_VUPVAL && upisopen(gco2upv(o))));
|
||||
}
|
||||
}
|
||||
checkrefs(g, o);
|
||||
@@ -506,53 +518,94 @@ static void checkobject (global_State *g, GCObject *o, int maybedead,
|
||||
}
|
||||
|
||||
|
||||
static void checkgraylist (global_State *g, GCObject *o) {
|
||||
static lu_mem checkgraylist (global_State *g, GCObject *o) {
|
||||
int total = 0; /* count number of elements in the list */
|
||||
((void)g); /* better to keep it available if we need to print an object */
|
||||
while (o) {
|
||||
lua_assert(isgray(o) || getage(o) == G_TOUCHED2);
|
||||
lua_assert(!!isgray(o) ^ (getage(o) == G_TOUCHED2));
|
||||
lua_assert(!testbit(o->marked, TESTBIT));
|
||||
if (keepinvariant(g))
|
||||
l_setbit(o->marked, TESTBIT); /* mark that object is in a gray list */
|
||||
total++;
|
||||
switch (o->tt) {
|
||||
case LUA_TTABLE: o = gco2t(o)->gclist; break;
|
||||
case LUA_TLCL: o = gco2lcl(o)->gclist; break;
|
||||
case LUA_TCCL: o = gco2ccl(o)->gclist; break;
|
||||
case LUA_TTHREAD: o = gco2th(o)->gclist; break;
|
||||
case LUA_TPROTO: o = gco2p(o)->gclist; break;
|
||||
case LUA_VTABLE: o = gco2t(o)->gclist; break;
|
||||
case LUA_VLCL: o = gco2lcl(o)->gclist; break;
|
||||
case LUA_VCCL: o = gco2ccl(o)->gclist; break;
|
||||
case LUA_VTHREAD: o = gco2th(o)->gclist; break;
|
||||
case LUA_VPROTO: o = gco2p(o)->gclist; break;
|
||||
case LUA_VUSERDATA:
|
||||
lua_assert(gco2u(o)->nuvalue > 0);
|
||||
o = gco2u(o)->gclist;
|
||||
break;
|
||||
default: lua_assert(0); /* other objects cannot be in a gray list */
|
||||
}
|
||||
}
|
||||
return total;
|
||||
}
|
||||
|
||||
|
||||
/*
|
||||
** Check objects in gray lists.
|
||||
*/
|
||||
static void checkgrays (global_State *g) {
|
||||
if (!keepinvariant(g)) return;
|
||||
checkgraylist(g, g->gray);
|
||||
checkgraylist(g, g->grayagain);
|
||||
checkgraylist(g, g->weak);
|
||||
checkgraylist(g, g->ephemeron);
|
||||
static lu_mem checkgrays (global_State *g) {
|
||||
int total = 0; /* count number of elements in all lists */
|
||||
if (!keepinvariant(g)) return total;
|
||||
total += checkgraylist(g, g->gray);
|
||||
total += checkgraylist(g, g->grayagain);
|
||||
total += checkgraylist(g, g->weak);
|
||||
total += checkgraylist(g, g->allweak);
|
||||
total += checkgraylist(g, g->ephemeron);
|
||||
return total;
|
||||
}
|
||||
|
||||
|
||||
static void checklist (global_State *g, int maybedead, int tof,
|
||||
/*
|
||||
** Check whether 'o' should be in a gray list. If so, increment
|
||||
** 'count' and check its TESTBIT. (It must have been previously set by
|
||||
** 'checkgraylist'.)
|
||||
*/
|
||||
static void incifingray (global_State *g, GCObject *o, lu_mem *count) {
|
||||
if (!keepinvariant(g))
|
||||
return; /* gray lists not being kept in these phases */
|
||||
if (o->tt == LUA_VUPVAL) {
|
||||
/* only open upvalues can be gray */
|
||||
lua_assert(!isgray(o) || upisopen(gco2upv(o)));
|
||||
return; /* upvalues are never in gray lists */
|
||||
}
|
||||
/* these are the ones that must be in gray lists */
|
||||
if (isgray(o) || getage(o) == G_TOUCHED2) {
|
||||
(*count)++;
|
||||
lua_assert(testbit(o->marked, TESTBIT));
|
||||
resetbit(o->marked, TESTBIT); /* prepare for next cycle */
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
static lu_mem checklist (global_State *g, int maybedead, int tof,
|
||||
GCObject *newl, GCObject *survival, GCObject *old, GCObject *reallyold) {
|
||||
GCObject *o;
|
||||
lu_mem total = 0; /* number of object that should be in gray lists */
|
||||
for (o = newl; o != survival; o = o->next) {
|
||||
checkobject(g, o, maybedead, G_NEW);
|
||||
incifingray(g, o, &total);
|
||||
lua_assert(!tof == !tofinalize(o));
|
||||
}
|
||||
for (o = survival; o != old; o = o->next) {
|
||||
checkobject(g, o, 0, G_SURVIVAL);
|
||||
incifingray(g, o, &total);
|
||||
lua_assert(!tof == !tofinalize(o));
|
||||
}
|
||||
for (o = old; o != reallyold; o = o->next) {
|
||||
checkobject(g, o, 0, G_OLD1);
|
||||
incifingray(g, o, &total);
|
||||
lua_assert(!tof == !tofinalize(o));
|
||||
}
|
||||
for (o = reallyold; o != NULL; o = o->next) {
|
||||
checkobject(g, o, 0, G_OLD);
|
||||
incifingray(g, o, &total);
|
||||
lua_assert(!tof == !tofinalize(o));
|
||||
}
|
||||
return total;
|
||||
}
|
||||
|
||||
|
||||
@@ -560,32 +613,39 @@ int lua_checkmemory (lua_State *L) {
|
||||
global_State *g = G(L);
|
||||
GCObject *o;
|
||||
int maybedead;
|
||||
lu_mem totalin; /* total of objects that are in gray lists */
|
||||
lu_mem totalshould; /* total of objects that should be in gray lists */
|
||||
if (keepinvariant(g)) {
|
||||
lua_assert(!iswhite(g->mainthread));
|
||||
lua_assert(!iswhite(gcvalue(&g->l_registry)));
|
||||
}
|
||||
lua_assert(!isdead(g, gcvalue(&g->l_registry)));
|
||||
lua_assert(g->sweepgc == NULL || issweepphase(g));
|
||||
checkgrays(g);
|
||||
totalin = checkgrays(g);
|
||||
|
||||
/* check 'fixedgc' list */
|
||||
for (o = g->fixedgc; o != NULL; o = o->next) {
|
||||
lua_assert(o->tt == LUA_TSHRSTR && isgray(o) && getage(o) == G_OLD);
|
||||
lua_assert(o->tt == LUA_VSHRSTR && isgray(o) && getage(o) == G_OLD);
|
||||
}
|
||||
|
||||
/* check 'allgc' list */
|
||||
maybedead = (GCSatomic < g->gcstate && g->gcstate <= GCSswpallgc);
|
||||
checklist(g, maybedead, 0, g->allgc, g->survival, g->old, g->reallyold);
|
||||
totalshould = checklist(g, maybedead, 0, g->allgc,
|
||||
g->survival, g->old1, g->reallyold);
|
||||
|
||||
/* check 'finobj' list */
|
||||
checklist(g, 0, 1, g->finobj, g->finobjsur, g->finobjold, g->finobjrold);
|
||||
totalshould += checklist(g, 0, 1, g->finobj,
|
||||
g->finobjsur, g->finobjold1, g->finobjrold);
|
||||
|
||||
/* check 'tobefnz' list */
|
||||
for (o = g->tobefnz; o != NULL; o = o->next) {
|
||||
checkobject(g, o, 0, G_NEW);
|
||||
incifingray(g, o, &totalshould);
|
||||
lua_assert(tofinalize(o));
|
||||
lua_assert(o->tt == LUA_TUSERDATA || o->tt == LUA_TTABLE);
|
||||
lua_assert(o->tt == LUA_VUSERDATA || o->tt == LUA_VTABLE);
|
||||
}
|
||||
if (keepinvariant(g))
|
||||
lua_assert(totalin == totalshould);
|
||||
return 0;
|
||||
}
|
||||
|
||||
@@ -741,11 +801,12 @@ static int listlocals (lua_State *L) {
|
||||
static void printstack (lua_State *L) {
|
||||
int i;
|
||||
int n = lua_gettop(L);
|
||||
printf("stack: >>\n");
|
||||
for (i = 1; i <= n; i++) {
|
||||
printf("%3d: %s\n", i, luaL_tolstring(L, i, NULL));
|
||||
lua_pop(L, 1);
|
||||
}
|
||||
printf("\n");
|
||||
printf("<<\n");
|
||||
}
|
||||
|
||||
|
||||
@@ -783,7 +844,7 @@ static int mem_query (lua_State *L) {
|
||||
return 1;
|
||||
}
|
||||
}
|
||||
return luaL_error(L, "unkown type '%s'", t);
|
||||
return luaL_error(L, "unknown type '%s'", t);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -795,7 +856,14 @@ static int alloc_count (lua_State *L) {
|
||||
l_memcontrol.countlimit = luaL_checkinteger(L, 1);
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
||||
|
||||
static int alloc_failnext (lua_State *L) {
|
||||
UNUSED(L);
|
||||
l_memcontrol.failnext = 1;
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
||||
static int settrick (lua_State *L) {
|
||||
if (ttisnil(obj_at(L, 1)))
|
||||
@@ -900,7 +968,7 @@ static int hash_query (lua_State *L) {
|
||||
static int stacklevel (lua_State *L) {
|
||||
unsigned long a = 0;
|
||||
lua_pushinteger(L, (L->top - L->stack));
|
||||
lua_pushinteger(L, (L->stack_last - L->stack));
|
||||
lua_pushinteger(L, stacksize(L));
|
||||
lua_pushinteger(L, L->nCcalls);
|
||||
lua_pushinteger(L, L->nci);
|
||||
lua_pushinteger(L, (unsigned long)&a);
|
||||
@@ -1275,10 +1343,19 @@ static int getindex_aux (lua_State *L, lua_State *L1, const char **pc) {
|
||||
}
|
||||
|
||||
|
||||
static void pushcode (lua_State *L, int code) {
|
||||
static const char *const codes[] = {"OK", "YIELD", "ERRRUN",
|
||||
"ERRSYNTAX", MEMERRMSG, "ERRGCMM", "ERRERR"};
|
||||
lua_pushstring(L, codes[code]);
|
||||
static const char *const statcodes[] = {"OK", "YIELD", "ERRRUN",
|
||||
"ERRSYNTAX", MEMERRMSG, "ERRGCMM", "ERRERR"};
|
||||
|
||||
/*
|
||||
** Avoid these stat codes from being collected, to avoid possible
|
||||
** memory error when pushing them.
|
||||
*/
|
||||
static void regcodes (lua_State *L) {
|
||||
unsigned int i;
|
||||
for (i = 0; i < sizeof(statcodes) / sizeof(statcodes[0]); i++) {
|
||||
lua_pushboolean(L, 1);
|
||||
lua_setfield(L, LUA_REGISTRYINDEX, statcodes[i]);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -1501,7 +1578,7 @@ static int runC (lua_State *L, lua_State *L1, const char *pc) {
|
||||
lua_pushnumber(L1, (lua_Number)getnum);
|
||||
}
|
||||
else if EQ("pushstatus") {
|
||||
pushcode(L1, status);
|
||||
lua_pushstring(L1, statcodes[status]);
|
||||
}
|
||||
else if EQ("pushstring") {
|
||||
lua_pushstring(L1, getstring);
|
||||
@@ -1661,6 +1738,9 @@ static struct X { int x; } x;
|
||||
if (n == 0) n = lua_gettop(fs);
|
||||
lua_xmove(fs, ts, n);
|
||||
}
|
||||
else if EQ("isyieldable") {
|
||||
lua_pushboolean(L1, lua_isyieldable(lua_tothread(L1, getindex)));
|
||||
}
|
||||
else if EQ("yield") {
|
||||
return lua_yield(L1, getnum);
|
||||
}
|
||||
@@ -1703,7 +1783,7 @@ static int Cfunc (lua_State *L) {
|
||||
|
||||
|
||||
static int Cfunck (lua_State *L, int status, lua_KContext ctx) {
|
||||
pushcode(L, status);
|
||||
lua_pushstring(L, statcodes[status]);
|
||||
lua_setglobal(L, "status");
|
||||
lua_pushinteger(L, ctx);
|
||||
lua_setglobal(L, "ctx");
|
||||
@@ -1840,6 +1920,7 @@ static const struct luaL_Reg tests_funcs[] = {
|
||||
{"makeCfunc", makeCfunc},
|
||||
{"totalmem", mem_query},
|
||||
{"alloccount", alloc_count},
|
||||
{"allocfailnext", alloc_failnext},
|
||||
{"trick", settrick},
|
||||
{"udataval", udataval},
|
||||
{"unref", unref},
|
||||
@@ -1858,6 +1939,9 @@ int luaB_opentests (lua_State *L) {
|
||||
void *ud;
|
||||
lua_atpanic(L, &tpanic);
|
||||
lua_setwarnf(L, &warnf, L);
|
||||
lua_pushboolean(L, 0);
|
||||
lua_setglobal(L, "_WARN"); /* _WARN = false */
|
||||
regcodes(L);
|
||||
atexit(checkfinalmem);
|
||||
lua_assert(lua_getallocf(L, &ud) == debug_realloc);
|
||||
lua_assert(ud == cast_voidp(&l_memcontrol));
|
||||
|
||||
@@ -20,19 +20,9 @@
|
||||
|
||||
|
||||
/* turn on assertions */
|
||||
#undef NDEBUG
|
||||
#include <assert.h>
|
||||
#define lua_assert(c) assert(c)
|
||||
#define LUAI_ASSERT
|
||||
|
||||
|
||||
/* include opcode names */
|
||||
#define LUAI_DEFOPNAMES
|
||||
|
||||
|
||||
/* compiled with -O0, Lua uses a lot of C stack space... */
|
||||
#undef LUAI_MAXCSTACK
|
||||
#define LUAI_MAXCSTACK 400
|
||||
|
||||
/* to avoid warnings, and to make sure value is really unused */
|
||||
#define UNUSED(x) (x=0, (void)(x))
|
||||
|
||||
@@ -56,12 +46,13 @@
|
||||
|
||||
/* memory-allocator control variables */
|
||||
typedef struct Memcontrol {
|
||||
int failnext;
|
||||
unsigned long numblocks;
|
||||
unsigned long total;
|
||||
unsigned long maxmem;
|
||||
unsigned long memlimit;
|
||||
unsigned long countlimit;
|
||||
unsigned long objcount[LUA_NUMTAGS];
|
||||
unsigned long objcount[LUA_NUMTYPES];
|
||||
} Memcontrol;
|
||||
|
||||
LUA_API Memcontrol l_memcontrol;
|
||||
@@ -77,7 +68,13 @@ extern void *l_Trick;
|
||||
/*
|
||||
** Function to traverse and check all memory used by Lua
|
||||
*/
|
||||
int lua_checkmemory (lua_State *L);
|
||||
LUAI_FUNC int lua_checkmemory (lua_State *L);
|
||||
|
||||
/*
|
||||
** Function to print an object GC-friendly
|
||||
*/
|
||||
struct GCObject;
|
||||
LUAI_FUNC void lua_printobj (lua_State *L, struct GCObject *o);
|
||||
|
||||
|
||||
/* test for lock/unlock */
|
||||
@@ -121,18 +118,22 @@ LUA_API void *debug_realloc (void *ud, void *block,
|
||||
#define MINSTRTABSIZE 2
|
||||
#define MAXIWTHABS 3
|
||||
|
||||
#define STRCACHE_N 23
|
||||
#define STRCACHE_M 5
|
||||
|
||||
#undef LUAI_USER_ALIGNMENT_T
|
||||
#define LUAI_USER_ALIGNMENT_T union { char b[sizeof(void*) * 8]; }
|
||||
|
||||
|
||||
/* make stack-overflow tests run faster */
|
||||
#undef LUAI_MAXSTACK
|
||||
#define LUAI_MAXSTACK 50000
|
||||
|
||||
|
||||
#undef LUAI_USER_ALIGNMENT_T
|
||||
#define LUAI_USER_ALIGNMENT_T union { char b[sizeof(void*) * 8]; }
|
||||
/* force Lua to use its own implementations */
|
||||
#undef lua_strx2number
|
||||
#undef lua_number2strx
|
||||
|
||||
|
||||
#define STRCACHE_N 23
|
||||
#define STRCACHE_M 5
|
||||
|
||||
#endif
|
||||
|
||||
|
||||
@@ -27,7 +27,7 @@
|
||||
|
||||
static const char udatatypename[] = "userdata";
|
||||
|
||||
LUAI_DDEF const char *const luaT_typenames_[LUA_TOTALTAGS] = {
|
||||
LUAI_DDEF const char *const luaT_typenames_[LUA_TOTALTYPES] = {
|
||||
"no value",
|
||||
"nil", "boolean", udatatypename, "number",
|
||||
"string", "table", "function", udatatypename, "thread",
|
||||
@@ -188,6 +188,15 @@ void luaT_trybiniTM (lua_State *L, const TValue *p1, lua_Integer i2,
|
||||
}
|
||||
|
||||
|
||||
/*
|
||||
** Calls an order tag method.
|
||||
** For lessequal, LUA_COMPAT_LT_LE keeps compatibility with old
|
||||
** behavior: if there is no '__le', try '__lt', based on l <= r iff
|
||||
** !(r < l) (assuming a total order). If the metamethod yields during
|
||||
** this substitution, the continuation has to know about it (to negate
|
||||
** the result of r<l); bit CIST_LEQ in the call status keeps that
|
||||
** information.
|
||||
*/
|
||||
int luaT_callorderTM (lua_State *L, const TValue *p1, const TValue *p2,
|
||||
TMS event) {
|
||||
if (callbinTM(L, p1, p2, L->top, event)) /* try original event */
|
||||
@@ -231,7 +240,7 @@ void luaT_adjustvarargs (lua_State *L, int nfixparams, CallInfo *ci,
|
||||
int actual = cast_int(L->top - ci->func) - 1; /* number of arguments */
|
||||
int nextra = actual - nfixparams; /* number of extra arguments */
|
||||
ci->u.l.nextraargs = nextra;
|
||||
checkstackGC(L, p->maxstacksize + 1);
|
||||
luaD_checkstack(L, p->maxstacksize + 1);
|
||||
/* copy function to the top of the stack */
|
||||
setobjs2s(L, L->top++, ci->func);
|
||||
/* move fixed parameters to the top of the stack */
|
||||
@@ -250,7 +259,7 @@ void luaT_getvarargs (lua_State *L, CallInfo *ci, StkId where, int wanted) {
|
||||
int nextra = ci->u.l.nextraargs;
|
||||
if (wanted < 0) {
|
||||
wanted = nextra; /* get all extra arguments available */
|
||||
checkstackp(L, nextra, where); /* ensure stack space */
|
||||
checkstackGCp(L, nextra, where); /* ensure stack space */
|
||||
L->top = where + nextra; /* next instruction will need top */
|
||||
}
|
||||
for (i = 0; i < wanted && i < nextra; i++)
|
||||
|
||||
@@ -45,6 +45,15 @@ typedef enum {
|
||||
} TMS;
|
||||
|
||||
|
||||
/*
|
||||
** Mask with 1 in all fast-access methods. A 1 in any of these bits
|
||||
** in the flag of a (meta)table means the metatable does not have the
|
||||
** corresponding metamethod field. (Bit 7 of the flag is used for
|
||||
** 'isrealasize'.)
|
||||
*/
|
||||
#define maskflags (~(~0u << (TM_EQ + 1)))
|
||||
|
||||
|
||||
/*
|
||||
** Test whether there is no tagmethod.
|
||||
** (Because tagmethods use raw accesses, the result may be an "empty" nil.)
|
||||
@@ -59,7 +68,7 @@ typedef enum {
|
||||
|
||||
#define ttypename(x) luaT_typenames_[(x) + 1]
|
||||
|
||||
LUAI_DDEC(const char *const luaT_typenames_[LUA_TOTALTAGS];)
|
||||
LUAI_DDEC(const char *const luaT_typenames_[LUA_TOTALTYPES];)
|
||||
|
||||
|
||||
LUAI_FUNC const char *luaT_objtypename (lua_State *L, const TValue *o);
|
||||
|
||||
@@ -54,8 +54,9 @@ static void lstop (lua_State *L, lua_Debug *ar) {
|
||||
** interpreter.
|
||||
*/
|
||||
static void laction (int i) {
|
||||
int flag = LUA_MASKCALL | LUA_MASKRET | LUA_MASKLINE | LUA_MASKCOUNT;
|
||||
signal(i, SIG_DFL); /* if another SIGINT happens, terminate process */
|
||||
lua_sethook(globalL, lstop, LUA_MASKCALL | LUA_MASKRET | LUA_MASKCOUNT, 1);
|
||||
lua_sethook(globalL, lstop, flag, 1);
|
||||
}
|
||||
|
||||
|
||||
@@ -415,14 +416,18 @@ static int handle_luainit (lua_State *L) {
|
||||
|
||||
|
||||
/*
|
||||
** Returns the string to be used as a prompt by the interpreter.
|
||||
** Return the string to be used as a prompt by the interpreter. Leave
|
||||
** the string (or nil, if using the default value) on the stack, to keep
|
||||
** it anchored.
|
||||
*/
|
||||
static const char *get_prompt (lua_State *L, int firstline) {
|
||||
const char *p;
|
||||
lua_getglobal(L, firstline ? "_PROMPT" : "_PROMPT2");
|
||||
p = lua_tostring(L, -1);
|
||||
if (p == NULL) p = (firstline ? LUA_PROMPT : LUA_PROMPT2);
|
||||
return p;
|
||||
if (lua_getglobal(L, firstline ? "_PROMPT" : "_PROMPT2") == LUA_TNIL)
|
||||
return (firstline ? LUA_PROMPT : LUA_PROMPT2); /* use the default */
|
||||
else { /* apply 'tostring' over the value */
|
||||
const char *p = luaL_tolstring(L, -1, NULL);
|
||||
lua_remove(L, -2); /* remove original value */
|
||||
return p;
|
||||
}
|
||||
}
|
||||
|
||||
/* mark in error messages for incomplete statements */
|
||||
|
||||
@@ -18,14 +18,14 @@
|
||||
|
||||
#define LUA_VERSION_MAJOR "5"
|
||||
#define LUA_VERSION_MINOR "4"
|
||||
#define LUA_VERSION_RELEASE "0"
|
||||
#define LUA_VERSION_RELEASE "2"
|
||||
|
||||
#define LUA_VERSION_NUM 504
|
||||
#define LUA_VERSION_RELEASE_NUM (LUA_VERSION_NUM * 100 + 0)
|
||||
|
||||
#define LUA_VERSION "Lua " LUA_VERSION_MAJOR "." LUA_VERSION_MINOR
|
||||
#define LUA_RELEASE LUA_VERSION "." LUA_VERSION_RELEASE
|
||||
#define LUA_COPYRIGHT LUA_RELEASE " Copyright (C) 1994-2019 Lua.org, PUC-Rio"
|
||||
#define LUA_COPYRIGHT LUA_RELEASE " Copyright (C) 1994-2020 Lua.org, PUC-Rio"
|
||||
#define LUA_AUTHORS "R. Ierusalimschy, L. H. de Figueiredo, W. Celes"
|
||||
|
||||
|
||||
@@ -72,7 +72,7 @@ typedef struct lua_State lua_State;
|
||||
#define LUA_TUSERDATA 7
|
||||
#define LUA_TTHREAD 8
|
||||
|
||||
#define LUA_NUMTAGS 9
|
||||
#define LUA_NUMTYPES 9
|
||||
|
||||
|
||||
|
||||
@@ -412,6 +412,8 @@ LUA_API void (lua_toclose) (lua_State *L, int idx);
|
||||
#define lua_getuservalue(L,idx) lua_getiuservalue(L,idx,1)
|
||||
#define lua_setuservalue(L,idx) lua_setiuservalue(L,idx,1)
|
||||
|
||||
#define LUA_NUMTAGS LUA_NUMTYPES
|
||||
|
||||
/* }============================================================== */
|
||||
|
||||
/*
|
||||
@@ -489,7 +491,7 @@ struct lua_Debug {
|
||||
|
||||
|
||||
/******************************************************************************
|
||||
* Copyright (C) 1994-2019 Lua.org, PUC-Rio.
|
||||
* Copyright (C) 1994-2020 Lua.org, PUC-Rio.
|
||||
*
|
||||
* Permission is hereby granted, free of charge, to any person obtaining
|
||||
* a copy of this software and associated documentation files (the
|
||||
|
||||
@@ -36,21 +36,6 @@
|
||||
** =====================================================================
|
||||
*/
|
||||
|
||||
/*
|
||||
@@ LUAI_MAXCSTACK defines the maximum depth for nested calls and
|
||||
** also limits the maximum depth of other recursive algorithms in
|
||||
** the implementation, such as syntactic analysis. A value too
|
||||
** large may allow the interpreter to crash (C-stack overflow).
|
||||
** The default value seems ok for regular machines, but may be
|
||||
** too high for restricted hardware.
|
||||
** The test file 'cstack.lua' may help finding a good limit.
|
||||
** (It will crash with a limit too high.)
|
||||
*/
|
||||
#if !defined(LUAI_MAXCSTACK)
|
||||
#define LUAI_MAXCSTACK 2000
|
||||
#endif
|
||||
|
||||
|
||||
/*
|
||||
@@ LUA_USE_C89 controls the use of non-ISO-C89 features.
|
||||
** Define it if you want Lua to avoid the use of a few C99 features
|
||||
@@ -398,7 +383,7 @@
|
||||
@@ LUA_NUMBER is the floating-point type used by Lua.
|
||||
@@ LUAI_UACNUMBER is the result of a 'default argument promotion'
|
||||
@@ over a floating number.
|
||||
@@ l_mathlim(x) corrects limit name 'x' to the proper float type
|
||||
@@ l_floatatt(x) corrects float attribute 'x' to the proper float type
|
||||
** by prefixing it with one of FLT/DBL/LDBL.
|
||||
@@ LUA_NUMBER_FRMLEN is the length modifier for writing floats.
|
||||
@@ LUA_NUMBER_FMT is the format for writing floats.
|
||||
@@ -437,7 +422,7 @@
|
||||
|
||||
#define LUA_NUMBER float
|
||||
|
||||
#define l_mathlim(n) (FLT_##n)
|
||||
#define l_floatatt(n) (FLT_##n)
|
||||
|
||||
#define LUAI_UACNUMBER double
|
||||
|
||||
@@ -453,7 +438,7 @@
|
||||
|
||||
#define LUA_NUMBER long double
|
||||
|
||||
#define l_mathlim(n) (LDBL_##n)
|
||||
#define l_floatatt(n) (LDBL_##n)
|
||||
|
||||
#define LUAI_UACNUMBER long double
|
||||
|
||||
@@ -468,7 +453,7 @@
|
||||
|
||||
#define LUA_NUMBER double
|
||||
|
||||
#define l_mathlim(n) (DBL_##n)
|
||||
#define l_floatatt(n) (DBL_##n)
|
||||
|
||||
#define LUAI_UACNUMBER double
|
||||
|
||||
|
||||
@@ -26,7 +26,7 @@
|
||||
|
||||
|
||||
#if !defined(luai_verifycode)
|
||||
#define luai_verifycode(L,b,f) /* empty */
|
||||
#define luai_verifycode(L,f) /* empty */
|
||||
#endif
|
||||
|
||||
|
||||
@@ -44,21 +44,21 @@ static l_noret error (LoadState *S, const char *why) {
|
||||
|
||||
|
||||
/*
|
||||
** All high-level loads go through LoadVector; you can change it to
|
||||
** All high-level loads go through loadVector; you can change it to
|
||||
** adapt to the endianness of the input
|
||||
*/
|
||||
#define LoadVector(S,b,n) LoadBlock(S,b,(n)*sizeof((b)[0]))
|
||||
#define loadVector(S,b,n) loadBlock(S,b,(n)*sizeof((b)[0]))
|
||||
|
||||
static void LoadBlock (LoadState *S, void *b, size_t size) {
|
||||
static void loadBlock (LoadState *S, void *b, size_t size) {
|
||||
if (luaZ_read(S->Z, b, size) != 0)
|
||||
error(S, "truncated chunk");
|
||||
}
|
||||
|
||||
|
||||
#define LoadVar(S,x) LoadVector(S,&x,1)
|
||||
#define loadVar(S,x) loadVector(S,&x,1)
|
||||
|
||||
|
||||
static lu_byte LoadByte (LoadState *S) {
|
||||
static lu_byte loadByte (LoadState *S) {
|
||||
int b = zgetc(S->Z);
|
||||
if (b == EOZ)
|
||||
error(S, "truncated chunk");
|
||||
@@ -66,12 +66,12 @@ static lu_byte LoadByte (LoadState *S) {
|
||||
}
|
||||
|
||||
|
||||
static size_t LoadUnsigned (LoadState *S, size_t limit) {
|
||||
static size_t loadUnsigned (LoadState *S, size_t limit) {
|
||||
size_t x = 0;
|
||||
int b;
|
||||
limit >>= 7;
|
||||
do {
|
||||
b = LoadByte(S);
|
||||
b = loadByte(S);
|
||||
if (x >= limit)
|
||||
error(S, "integer overflow");
|
||||
x = (x << 7) | (b & 0x7f);
|
||||
@@ -80,98 +80,107 @@ static size_t LoadUnsigned (LoadState *S, size_t limit) {
|
||||
}
|
||||
|
||||
|
||||
static size_t LoadSize (LoadState *S) {
|
||||
return LoadUnsigned(S, ~(size_t)0);
|
||||
static size_t loadSize (LoadState *S) {
|
||||
return loadUnsigned(S, ~(size_t)0);
|
||||
}
|
||||
|
||||
|
||||
static int LoadInt (LoadState *S) {
|
||||
return cast_int(LoadUnsigned(S, INT_MAX));
|
||||
static int loadInt (LoadState *S) {
|
||||
return cast_int(loadUnsigned(S, INT_MAX));
|
||||
}
|
||||
|
||||
|
||||
static lua_Number LoadNumber (LoadState *S) {
|
||||
static lua_Number loadNumber (LoadState *S) {
|
||||
lua_Number x;
|
||||
LoadVar(S, x);
|
||||
loadVar(S, x);
|
||||
return x;
|
||||
}
|
||||
|
||||
|
||||
static lua_Integer LoadInteger (LoadState *S) {
|
||||
static lua_Integer loadInteger (LoadState *S) {
|
||||
lua_Integer x;
|
||||
LoadVar(S, x);
|
||||
loadVar(S, x);
|
||||
return x;
|
||||
}
|
||||
|
||||
|
||||
/*
|
||||
** Load a nullable string
|
||||
** Load a nullable string into prototype 'p'.
|
||||
*/
|
||||
static TString *LoadStringN (LoadState *S) {
|
||||
size_t size = LoadSize(S);
|
||||
if (size == 0)
|
||||
static TString *loadStringN (LoadState *S, Proto *p) {
|
||||
lua_State *L = S->L;
|
||||
TString *ts;
|
||||
size_t size = loadSize(S);
|
||||
if (size == 0) /* no string? */
|
||||
return NULL;
|
||||
else if (--size <= LUAI_MAXSHORTLEN) { /* short string? */
|
||||
char buff[LUAI_MAXSHORTLEN];
|
||||
LoadVector(S, buff, size);
|
||||
return luaS_newlstr(S->L, buff, size);
|
||||
loadVector(S, buff, size); /* load string into buffer */
|
||||
ts = luaS_newlstr(L, buff, size); /* create string */
|
||||
}
|
||||
else { /* long string */
|
||||
TString *ts = luaS_createlngstrobj(S->L, size);
|
||||
LoadVector(S, getstr(ts), size); /* load directly in final place */
|
||||
return ts;
|
||||
ts = luaS_createlngstrobj(L, size); /* create string */
|
||||
setsvalue2s(L, L->top, ts); /* anchor it ('loadVector' can GC) */
|
||||
luaD_inctop(L);
|
||||
loadVector(S, getstr(ts), size); /* load directly in final place */
|
||||
L->top--; /* pop string */
|
||||
}
|
||||
luaC_objbarrier(L, p, ts);
|
||||
return ts;
|
||||
}
|
||||
|
||||
|
||||
/*
|
||||
** Load a non-nullable string.
|
||||
** Load a non-nullable string into prototype 'p'.
|
||||
*/
|
||||
static TString *LoadString (LoadState *S) {
|
||||
TString *st = LoadStringN(S);
|
||||
static TString *loadString (LoadState *S, Proto *p) {
|
||||
TString *st = loadStringN(S, p);
|
||||
if (st == NULL)
|
||||
error(S, "bad format for constant string");
|
||||
return st;
|
||||
}
|
||||
|
||||
|
||||
static void LoadCode (LoadState *S, Proto *f) {
|
||||
int n = LoadInt(S);
|
||||
static void loadCode (LoadState *S, Proto *f) {
|
||||
int n = loadInt(S);
|
||||
f->code = luaM_newvectorchecked(S->L, n, Instruction);
|
||||
f->sizecode = n;
|
||||
LoadVector(S, f->code, n);
|
||||
loadVector(S, f->code, n);
|
||||
}
|
||||
|
||||
|
||||
static void LoadFunction(LoadState *S, Proto *f, TString *psource);
|
||||
static void loadFunction(LoadState *S, Proto *f, TString *psource);
|
||||
|
||||
|
||||
static void LoadConstants (LoadState *S, Proto *f) {
|
||||
static void loadConstants (LoadState *S, Proto *f) {
|
||||
int i;
|
||||
int n = LoadInt(S);
|
||||
int n = loadInt(S);
|
||||
f->k = luaM_newvectorchecked(S->L, n, TValue);
|
||||
f->sizek = n;
|
||||
for (i = 0; i < n; i++)
|
||||
setnilvalue(&f->k[i]);
|
||||
for (i = 0; i < n; i++) {
|
||||
TValue *o = &f->k[i];
|
||||
int t = LoadByte(S);
|
||||
int t = loadByte(S);
|
||||
switch (t) {
|
||||
case LUA_TNIL:
|
||||
case LUA_VNIL:
|
||||
setnilvalue(o);
|
||||
break;
|
||||
case LUA_TBOOLEAN:
|
||||
setbvalue(o, LoadByte(S));
|
||||
case LUA_VFALSE:
|
||||
setbfvalue(o);
|
||||
break;
|
||||
case LUA_TNUMFLT:
|
||||
setfltvalue(o, LoadNumber(S));
|
||||
case LUA_VTRUE:
|
||||
setbtvalue(o);
|
||||
break;
|
||||
case LUA_TNUMINT:
|
||||
setivalue(o, LoadInteger(S));
|
||||
case LUA_VNUMFLT:
|
||||
setfltvalue(o, loadNumber(S));
|
||||
break;
|
||||
case LUA_TSHRSTR:
|
||||
case LUA_TLNGSTR:
|
||||
setsvalue2n(S->L, o, LoadString(S));
|
||||
case LUA_VNUMINT:
|
||||
setivalue(o, loadInteger(S));
|
||||
break;
|
||||
case LUA_VSHRSTR:
|
||||
case LUA_VLNGSTR:
|
||||
setsvalue2n(S->L, o, loadString(S, f));
|
||||
break;
|
||||
default: lua_assert(0);
|
||||
}
|
||||
@@ -179,91 +188,99 @@ static void LoadConstants (LoadState *S, Proto *f) {
|
||||
}
|
||||
|
||||
|
||||
static void LoadProtos (LoadState *S, Proto *f) {
|
||||
static void loadProtos (LoadState *S, Proto *f) {
|
||||
int i;
|
||||
int n = LoadInt(S);
|
||||
int n = loadInt(S);
|
||||
f->p = luaM_newvectorchecked(S->L, n, Proto *);
|
||||
f->sizep = n;
|
||||
for (i = 0; i < n; i++)
|
||||
f->p[i] = NULL;
|
||||
for (i = 0; i < n; i++) {
|
||||
f->p[i] = luaF_newproto(S->L);
|
||||
LoadFunction(S, f->p[i], f->source);
|
||||
luaC_objbarrier(S->L, f, f->p[i]);
|
||||
loadFunction(S, f->p[i], f->source);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
static void LoadUpvalues (LoadState *S, Proto *f) {
|
||||
/*
|
||||
** Load the upvalues for a function. The names must be filled first,
|
||||
** because the filling of the other fields can raise read errors and
|
||||
** the creation of the error message can call an emergency collection;
|
||||
** in that case all prototypes must be consistent for the GC.
|
||||
*/
|
||||
static void loadUpvalues (LoadState *S, Proto *f) {
|
||||
int i, n;
|
||||
n = LoadInt(S);
|
||||
n = loadInt(S);
|
||||
f->upvalues = luaM_newvectorchecked(S->L, n, Upvaldesc);
|
||||
f->sizeupvalues = n;
|
||||
for (i = 0; i < n; i++) {
|
||||
for (i = 0; i < n; i++) /* make array valid for GC */
|
||||
f->upvalues[i].name = NULL;
|
||||
f->upvalues[i].instack = LoadByte(S);
|
||||
f->upvalues[i].idx = LoadByte(S);
|
||||
f->upvalues[i].kind = LoadByte(S);
|
||||
for (i = 0; i < n; i++) { /* following calls can raise errors */
|
||||
f->upvalues[i].instack = loadByte(S);
|
||||
f->upvalues[i].idx = loadByte(S);
|
||||
f->upvalues[i].kind = loadByte(S);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
static void LoadDebug (LoadState *S, Proto *f) {
|
||||
static void loadDebug (LoadState *S, Proto *f) {
|
||||
int i, n;
|
||||
n = LoadInt(S);
|
||||
n = loadInt(S);
|
||||
f->lineinfo = luaM_newvectorchecked(S->L, n, ls_byte);
|
||||
f->sizelineinfo = n;
|
||||
LoadVector(S, f->lineinfo, n);
|
||||
n = LoadInt(S);
|
||||
loadVector(S, f->lineinfo, n);
|
||||
n = loadInt(S);
|
||||
f->abslineinfo = luaM_newvectorchecked(S->L, n, AbsLineInfo);
|
||||
f->sizeabslineinfo = n;
|
||||
for (i = 0; i < n; i++) {
|
||||
f->abslineinfo[i].pc = LoadInt(S);
|
||||
f->abslineinfo[i].line = LoadInt(S);
|
||||
f->abslineinfo[i].pc = loadInt(S);
|
||||
f->abslineinfo[i].line = loadInt(S);
|
||||
}
|
||||
n = LoadInt(S);
|
||||
n = loadInt(S);
|
||||
f->locvars = luaM_newvectorchecked(S->L, n, LocVar);
|
||||
f->sizelocvars = n;
|
||||
for (i = 0; i < n; i++)
|
||||
f->locvars[i].varname = NULL;
|
||||
for (i = 0; i < n; i++) {
|
||||
f->locvars[i].varname = LoadStringN(S);
|
||||
f->locvars[i].startpc = LoadInt(S);
|
||||
f->locvars[i].endpc = LoadInt(S);
|
||||
f->locvars[i].varname = loadStringN(S, f);
|
||||
f->locvars[i].startpc = loadInt(S);
|
||||
f->locvars[i].endpc = loadInt(S);
|
||||
}
|
||||
n = LoadInt(S);
|
||||
n = loadInt(S);
|
||||
for (i = 0; i < n; i++)
|
||||
f->upvalues[i].name = LoadStringN(S);
|
||||
f->upvalues[i].name = loadStringN(S, f);
|
||||
}
|
||||
|
||||
|
||||
static void LoadFunction (LoadState *S, Proto *f, TString *psource) {
|
||||
f->source = LoadStringN(S);
|
||||
static void loadFunction (LoadState *S, Proto *f, TString *psource) {
|
||||
f->source = loadStringN(S, f);
|
||||
if (f->source == NULL) /* no source in dump? */
|
||||
f->source = psource; /* reuse parent's source */
|
||||
f->linedefined = LoadInt(S);
|
||||
f->lastlinedefined = LoadInt(S);
|
||||
f->numparams = LoadByte(S);
|
||||
f->is_vararg = LoadByte(S);
|
||||
f->maxstacksize = LoadByte(S);
|
||||
LoadCode(S, f);
|
||||
LoadConstants(S, f);
|
||||
LoadUpvalues(S, f);
|
||||
LoadProtos(S, f);
|
||||
LoadDebug(S, f);
|
||||
f->linedefined = loadInt(S);
|
||||
f->lastlinedefined = loadInt(S);
|
||||
f->numparams = loadByte(S);
|
||||
f->is_vararg = loadByte(S);
|
||||
f->maxstacksize = loadByte(S);
|
||||
loadCode(S, f);
|
||||
loadConstants(S, f);
|
||||
loadUpvalues(S, f);
|
||||
loadProtos(S, f);
|
||||
loadDebug(S, f);
|
||||
}
|
||||
|
||||
|
||||
static void checkliteral (LoadState *S, const char *s, const char *msg) {
|
||||
char buff[sizeof(LUA_SIGNATURE) + sizeof(LUAC_DATA)]; /* larger than both */
|
||||
size_t len = strlen(s);
|
||||
LoadVector(S, buff, len);
|
||||
loadVector(S, buff, len);
|
||||
if (memcmp(s, buff, len) != 0)
|
||||
error(S, msg);
|
||||
}
|
||||
|
||||
|
||||
static void fchecksize (LoadState *S, size_t size, const char *tname) {
|
||||
if (LoadByte(S) != size)
|
||||
if (loadByte(S) != size)
|
||||
error(S, luaO_pushfstring(S->L, "%s size mismatch", tname));
|
||||
}
|
||||
|
||||
@@ -273,23 +290,23 @@ static void fchecksize (LoadState *S, size_t size, const char *tname) {
|
||||
static void checkHeader (LoadState *S) {
|
||||
/* skip 1st char (already read and checked) */
|
||||
checkliteral(S, &LUA_SIGNATURE[1], "not a binary chunk");
|
||||
if (LoadInt(S) != LUAC_VERSION)
|
||||
if (loadByte(S) != LUAC_VERSION)
|
||||
error(S, "version mismatch");
|
||||
if (LoadByte(S) != LUAC_FORMAT)
|
||||
if (loadByte(S) != LUAC_FORMAT)
|
||||
error(S, "format mismatch");
|
||||
checkliteral(S, LUAC_DATA, "corrupted chunk");
|
||||
checksize(S, Instruction);
|
||||
checksize(S, lua_Integer);
|
||||
checksize(S, lua_Number);
|
||||
if (LoadInteger(S) != LUAC_INT)
|
||||
if (loadInteger(S) != LUAC_INT)
|
||||
error(S, "integer format mismatch");
|
||||
if (LoadNumber(S) != LUAC_NUM)
|
||||
if (loadNumber(S) != LUAC_NUM)
|
||||
error(S, "float format mismatch");
|
||||
}
|
||||
|
||||
|
||||
/*
|
||||
** load precompiled chunk
|
||||
** Load precompiled chunk.
|
||||
*/
|
||||
LClosure *luaU_undump(lua_State *L, ZIO *Z, const char *name) {
|
||||
LoadState S;
|
||||
@@ -303,13 +320,14 @@ LClosure *luaU_undump(lua_State *L, ZIO *Z, const char *name) {
|
||||
S.L = L;
|
||||
S.Z = Z;
|
||||
checkHeader(&S);
|
||||
cl = luaF_newLclosure(L, LoadByte(&S));
|
||||
cl = luaF_newLclosure(L, loadByte(&S));
|
||||
setclLvalue2s(L, L->top, cl);
|
||||
luaD_inctop(L);
|
||||
cl->p = luaF_newproto(L);
|
||||
LoadFunction(&S, cl->p, NULL);
|
||||
luaC_objbarrier(L, cl, cl->p);
|
||||
loadFunction(&S, cl->p, NULL);
|
||||
lua_assert(cl->nupvalues == cl->p->sizeupvalues);
|
||||
luai_verifycode(L, buff, cl->p);
|
||||
luai_verifycode(L, cl->p);
|
||||
return cl;
|
||||
}
|
||||
|
||||
|
||||
@@ -18,7 +18,12 @@
|
||||
#define LUAC_INT 0x5678
|
||||
#define LUAC_NUM cast_num(370.5)
|
||||
|
||||
#define LUAC_VERSION LUA_VERSION_NUM
|
||||
/*
|
||||
** Encode major-minor version in one byte, one nibble for each
|
||||
*/
|
||||
#define MYINT(s) (s[0]-'0') /* assume one-digit numerals */
|
||||
#define LUAC_VERSION (MYINT(LUA_VERSION_MAJOR)*16+MYINT(LUA_VERSION_MINOR))
|
||||
|
||||
#define LUAC_FORMAT 0 /* this is the official format */
|
||||
|
||||
/* load one chunk; from lundump.c */
|
||||
|
||||
+6
-6
@@ -29,7 +29,7 @@
|
||||
** Integer type for decoded UTF-8 values; MAXUTF needs 31 bits.
|
||||
*/
|
||||
#if (UINT_MAX >> 30) >= 1
|
||||
typedef unsigned int utfint;
|
||||
typedef unsigned int utfint;
|
||||
#else
|
||||
typedef unsigned long utfint;
|
||||
#endif
|
||||
@@ -97,9 +97,9 @@ static int utflen (lua_State *L) {
|
||||
lua_Integer posj = u_posrelat(luaL_optinteger(L, 3, -1), len);
|
||||
int lax = lua_toboolean(L, 4);
|
||||
luaL_argcheck(L, 1 <= posi && --posi <= (lua_Integer)len, 2,
|
||||
"initial position out of string");
|
||||
"initial position out of bounds");
|
||||
luaL_argcheck(L, --posj < (lua_Integer)len, 3,
|
||||
"final position out of string");
|
||||
"final position out of bounds");
|
||||
while (posi <= posj) {
|
||||
const char *s1 = utf8_decode(s + posi, NULL, !lax);
|
||||
if (s1 == NULL) { /* conversion error? */
|
||||
@@ -127,8 +127,8 @@ static int codepoint (lua_State *L) {
|
||||
int lax = lua_toboolean(L, 4);
|
||||
int n;
|
||||
const char *se;
|
||||
luaL_argcheck(L, posi >= 1, 2, "out of range");
|
||||
luaL_argcheck(L, pose <= (lua_Integer)len, 3, "out of range");
|
||||
luaL_argcheck(L, posi >= 1, 2, "out of bounds");
|
||||
luaL_argcheck(L, pose <= (lua_Integer)len, 3, "out of bounds");
|
||||
if (posi > pose) return 0; /* empty interval; return no values */
|
||||
if (pose - posi >= INT_MAX) /* (lua_Integer -> int) overflow? */
|
||||
return luaL_error(L, "string slice too long");
|
||||
@@ -187,7 +187,7 @@ static int byteoffset (lua_State *L) {
|
||||
lua_Integer posi = (n >= 0) ? 1 : len + 1;
|
||||
posi = u_posrelat(luaL_optinteger(L, 3, posi), len);
|
||||
luaL_argcheck(L, 1 <= posi && --posi <= (lua_Integer)len, 3,
|
||||
"position out of range");
|
||||
"position out of bounds");
|
||||
if (n == 0) {
|
||||
/* find beginning of current byte sequence */
|
||||
while (posi > 0 && iscont(s + posi)) posi--;
|
||||
|
||||
@@ -55,7 +55,7 @@
|
||||
*/
|
||||
|
||||
/* number of bits in the mantissa of a float */
|
||||
#define NBM (l_mathlim(MANT_DIG))
|
||||
#define NBM (l_floatatt(MANT_DIG))
|
||||
|
||||
/*
|
||||
** Check whether some integers may not fit in a float, testing whether
|
||||
@@ -80,6 +80,21 @@
|
||||
#endif
|
||||
|
||||
|
||||
/*
|
||||
** Try to convert a value from string to a number value.
|
||||
** If the value is not a string or is a string not representing
|
||||
** a valid numeral (or if coercions from strings to numbers
|
||||
** are disabled via macro 'cvt2num'), do not modify 'result'
|
||||
** and return 0.
|
||||
*/
|
||||
static int l_strton (const TValue *obj, TValue *result) {
|
||||
lua_assert(obj != result);
|
||||
if (!cvt2num(obj)) /* is object not a string? */
|
||||
return 0;
|
||||
else
|
||||
return (luaO_str2num(svalue(obj), result) == vslen(obj) + 1);
|
||||
}
|
||||
|
||||
|
||||
/*
|
||||
** Try to convert a value to a float. The float case is already handled
|
||||
@@ -91,8 +106,7 @@ int luaV_tonumber_ (const TValue *obj, lua_Number *n) {
|
||||
*n = cast_num(ivalue(obj));
|
||||
return 1;
|
||||
}
|
||||
else if (cvt2num(obj) && /* string coercible to number? */
|
||||
luaO_str2num(svalue(obj), &v) == vslen(obj) + 1) {
|
||||
else if (l_strton(obj, &v)) { /* string coercible to number? */
|
||||
*n = nvalue(&v); /* convert result of 'luaO_str2num' to a float */
|
||||
return 1;
|
||||
}
|
||||
@@ -102,16 +116,13 @@ int luaV_tonumber_ (const TValue *obj, lua_Number *n) {
|
||||
|
||||
|
||||
/*
|
||||
** try to convert a float to an integer, rounding according to 'mode':
|
||||
** mode == 0: accepts only integral values
|
||||
** mode == 1: takes the floor of the number
|
||||
** mode == 2: takes the ceil of the number
|
||||
** try to convert a float to an integer, rounding according to 'mode'.
|
||||
*/
|
||||
int luaV_flttointeger (lua_Number n, lua_Integer *p, int mode) {
|
||||
int luaV_flttointeger (lua_Number n, lua_Integer *p, F2Imod mode) {
|
||||
lua_Number f = l_floor(n);
|
||||
if (n != f) { /* not an integral value? */
|
||||
if (mode == 0) return 0; /* fails if mode demands integral value */
|
||||
else if (mode > 1) /* needs ceil? */
|
||||
if (mode == F2Ieq) return 0; /* fails if mode demands integral value */
|
||||
else if (mode == F2Iceil) /* needs ceil? */
|
||||
f += 1; /* convert floor to ceil (remember: n != f) */
|
||||
}
|
||||
return lua_numbertointeger(f, p);
|
||||
@@ -123,7 +134,7 @@ int luaV_flttointeger (lua_Number n, lua_Integer *p, int mode) {
|
||||
** without string coercion.
|
||||
** ("Fast track" handled by macro 'tointegerns'.)
|
||||
*/
|
||||
int luaV_tointegerns (const TValue *obj, lua_Integer *p, int mode) {
|
||||
int luaV_tointegerns (const TValue *obj, lua_Integer *p, F2Imod mode) {
|
||||
if (ttisfloat(obj))
|
||||
return luaV_flttointeger(fltvalue(obj), p, mode);
|
||||
else if (ttisinteger(obj)) {
|
||||
@@ -138,33 +149,33 @@ int luaV_tointegerns (const TValue *obj, lua_Integer *p, int mode) {
|
||||
/*
|
||||
** try to convert a value to an integer.
|
||||
*/
|
||||
int luaV_tointeger (const TValue *obj, lua_Integer *p, int mode) {
|
||||
int luaV_tointeger (const TValue *obj, lua_Integer *p, F2Imod mode) {
|
||||
TValue v;
|
||||
if (cvt2num(obj) && luaO_str2num(svalue(obj), &v) == vslen(obj) + 1)
|
||||
obj = &v; /* change string to its corresponding number */
|
||||
if (l_strton(obj, &v)) /* does 'obj' point to a numerical string? */
|
||||
obj = &v; /* change it to point to its corresponding number */
|
||||
return luaV_tointegerns(obj, p, mode);
|
||||
}
|
||||
|
||||
|
||||
/*
|
||||
** Try to convert a 'for' limit to an integer, preserving the semantics
|
||||
** of the loop. (The following explanation assumes a positive step;
|
||||
** it is valid for negative steps mutatis mutandis.)
|
||||
** Return true if the loop must not run.
|
||||
** of the loop. Return true if the loop must not run; otherwise, '*p'
|
||||
** gets the integer limit.
|
||||
** (The following explanation assumes a positive step; it is valid for
|
||||
** negative steps mutatis mutandis.)
|
||||
** If the limit is an integer or can be converted to an integer,
|
||||
** rounding down, that is the limit.
|
||||
** Otherwise, check whether the limit can be converted to a float. If
|
||||
** the float is too large, clip it to LUA_MAXINTEGER. If the float
|
||||
** is too negative, the loop should not run, because any initial
|
||||
** integer value is greater than such limit; so, it returns true to
|
||||
** signal that.
|
||||
** (For this latter case, no integer limit would be correct; even a
|
||||
** limit of LUA_MININTEGER would run the loop once for an initial
|
||||
** value equal to LUA_MININTEGER.)
|
||||
** integer value is greater than such limit; so, the function returns
|
||||
** true to signal that. (For this latter case, no integer limit would be
|
||||
** correct; even a limit of LUA_MININTEGER would run the loop once for
|
||||
** an initial value equal to LUA_MININTEGER.)
|
||||
*/
|
||||
static int forlimit (lua_State *L, lua_Integer init, const TValue *lim,
|
||||
lua_Integer *p, lua_Integer step) {
|
||||
if (!luaV_tointeger(lim, p, (step < 0 ? 2 : 1))) {
|
||||
if (!luaV_tointeger(lim, p, (step < 0 ? F2Iceil : F2Ifloor))) {
|
||||
/* not coercible to in integer */
|
||||
lua_Number flim; /* try to convert to float */
|
||||
if (!tonumber(lim, &flim)) /* cannot convert to float? */
|
||||
@@ -183,6 +194,91 @@ static int forlimit (lua_State *L, lua_Integer init, const TValue *lim,
|
||||
}
|
||||
|
||||
|
||||
/*
|
||||
** Prepare a numerical for loop (opcode OP_FORPREP).
|
||||
** Return true to skip the loop. Otherwise,
|
||||
** after preparation, stack will be as follows:
|
||||
** ra : internal index (safe copy of the control variable)
|
||||
** ra + 1 : loop counter (integer loops) or limit (float loops)
|
||||
** ra + 2 : step
|
||||
** ra + 3 : control variable
|
||||
*/
|
||||
static int forprep (lua_State *L, StkId ra) {
|
||||
TValue *pinit = s2v(ra);
|
||||
TValue *plimit = s2v(ra + 1);
|
||||
TValue *pstep = s2v(ra + 2);
|
||||
if (ttisinteger(pinit) && ttisinteger(pstep)) { /* integer loop? */
|
||||
lua_Integer init = ivalue(pinit);
|
||||
lua_Integer step = ivalue(pstep);
|
||||
lua_Integer limit;
|
||||
if (step == 0)
|
||||
luaG_runerror(L, "'for' step is zero");
|
||||
setivalue(s2v(ra + 3), init); /* control variable */
|
||||
if (forlimit(L, init, plimit, &limit, step))
|
||||
return 1; /* skip the loop */
|
||||
else { /* prepare loop counter */
|
||||
lua_Unsigned count;
|
||||
if (step > 0) { /* ascending loop? */
|
||||
count = l_castS2U(limit) - l_castS2U(init);
|
||||
if (step != 1) /* avoid division in the too common case */
|
||||
count /= l_castS2U(step);
|
||||
}
|
||||
else { /* step < 0; descending loop */
|
||||
count = l_castS2U(init) - l_castS2U(limit);
|
||||
/* 'step+1' avoids negating 'mininteger' */
|
||||
count /= l_castS2U(-(step + 1)) + 1u;
|
||||
}
|
||||
/* store the counter in place of the limit (which won't be
|
||||
needed anymore) */
|
||||
setivalue(plimit, l_castU2S(count));
|
||||
}
|
||||
}
|
||||
else { /* try making all values floats */
|
||||
lua_Number init; lua_Number limit; lua_Number step;
|
||||
if (unlikely(!tonumber(plimit, &limit)))
|
||||
luaG_forerror(L, plimit, "limit");
|
||||
if (unlikely(!tonumber(pstep, &step)))
|
||||
luaG_forerror(L, pstep, "step");
|
||||
if (unlikely(!tonumber(pinit, &init)))
|
||||
luaG_forerror(L, pinit, "initial value");
|
||||
if (step == 0)
|
||||
luaG_runerror(L, "'for' step is zero");
|
||||
if (luai_numlt(0, step) ? luai_numlt(limit, init)
|
||||
: luai_numlt(init, limit))
|
||||
return 1; /* skip the loop */
|
||||
else {
|
||||
/* make sure internal values are all floats */
|
||||
setfltvalue(plimit, limit);
|
||||
setfltvalue(pstep, step);
|
||||
setfltvalue(s2v(ra), init); /* internal index */
|
||||
setfltvalue(s2v(ra + 3), init); /* control variable */
|
||||
}
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
||||
/*
|
||||
** Execute a step of a float numerical for loop, returning
|
||||
** true iff the loop must continue. (The integer case is
|
||||
** written online with opcode OP_FORLOOP, for performance.)
|
||||
*/
|
||||
static int floatforloop (StkId ra) {
|
||||
lua_Number step = fltvalue(s2v(ra + 2));
|
||||
lua_Number limit = fltvalue(s2v(ra + 1));
|
||||
lua_Number idx = fltvalue(s2v(ra)); /* internal index */
|
||||
idx = luai_numadd(L, idx, step); /* increment index */
|
||||
if (luai_numlt(0, step) ? luai_numle(idx, limit)
|
||||
: luai_numle(limit, idx)) {
|
||||
chgfltvalue(s2v(ra), idx); /* update internal index */
|
||||
setfltvalue(s2v(ra + 3), idx); /* and control variable */
|
||||
return 1; /* jump back */
|
||||
}
|
||||
else
|
||||
return 0; /* finish the loop */
|
||||
}
|
||||
|
||||
|
||||
/*
|
||||
** Finish the table access 'val = t[key]'.
|
||||
** if 'slot' is NULL, 't' is not a table; otherwise, 'slot' points to
|
||||
@@ -318,7 +414,7 @@ static int LTintfloat (lua_Integer i, lua_Number f) {
|
||||
return luai_numlt(cast_num(i), f); /* compare them as floats */
|
||||
else { /* i < f <=> i < ceil(f) */
|
||||
lua_Integer fi;
|
||||
if (luaV_flttointeger(f, &fi, 2)) /* fi = ceil(f) */
|
||||
if (luaV_flttointeger(f, &fi, F2Iceil)) /* fi = ceil(f) */
|
||||
return i < fi; /* compare them as integers */
|
||||
else /* 'f' is either greater or less than all integers */
|
||||
return f > 0; /* greater? */
|
||||
@@ -335,7 +431,7 @@ static int LEintfloat (lua_Integer i, lua_Number f) {
|
||||
return luai_numle(cast_num(i), f); /* compare them as floats */
|
||||
else { /* i <= f <=> i <= floor(f) */
|
||||
lua_Integer fi;
|
||||
if (luaV_flttointeger(f, &fi, 1)) /* fi = floor(f) */
|
||||
if (luaV_flttointeger(f, &fi, F2Ifloor)) /* fi = floor(f) */
|
||||
return i <= fi; /* compare them as integers */
|
||||
else /* 'f' is either greater or less than all integers */
|
||||
return f > 0; /* greater? */
|
||||
@@ -352,7 +448,7 @@ static int LTfloatint (lua_Number f, lua_Integer i) {
|
||||
return luai_numlt(f, cast_num(i)); /* compare them as floats */
|
||||
else { /* f < i <=> floor(f) < i */
|
||||
lua_Integer fi;
|
||||
if (luaV_flttointeger(f, &fi, 1)) /* fi = floor(f) */
|
||||
if (luaV_flttointeger(f, &fi, F2Ifloor)) /* fi = floor(f) */
|
||||
return fi < i; /* compare them as integers */
|
||||
else /* 'f' is either greater or less than all integers */
|
||||
return f < 0; /* less? */
|
||||
@@ -369,7 +465,7 @@ static int LEfloatint (lua_Number f, lua_Integer i) {
|
||||
return luai_numle(f, cast_num(i)); /* compare them as floats */
|
||||
else { /* f <= i <=> ceil(f) <= i */
|
||||
lua_Integer fi;
|
||||
if (luaV_flttointeger(f, &fi, 2)) /* fi = ceil(f) */
|
||||
if (luaV_flttointeger(f, &fi, F2Iceil)) /* fi = ceil(f) */
|
||||
return fi <= i; /* compare them as integers */
|
||||
else /* 'f' is either greater or less than all integers */
|
||||
return f < 0; /* less? */
|
||||
@@ -445,11 +541,6 @@ int luaV_lessthan (lua_State *L, const TValue *l, const TValue *r) {
|
||||
|
||||
/*
|
||||
** return 'l <= r' for non-numbers.
|
||||
** If it needs a metamethod and there is no '__le', try '__lt', based
|
||||
** on l <= r iff !(r < l) (assuming a total order). If the metamethod
|
||||
** yields during this substitution, the continuation has to know about
|
||||
** it (to negate the result of r<l); bit CIST_LEQ in the call status
|
||||
** keeps that information.
|
||||
*/
|
||||
static int lessequalothers (lua_State *L, const TValue *l, const TValue *r) {
|
||||
lua_assert(!ttisnumber(l) || !ttisnumber(r));
|
||||
@@ -486,15 +577,14 @@ int luaV_equalobj (lua_State *L, const TValue *t1, const TValue *t2) {
|
||||
}
|
||||
/* values have same type and same variant */
|
||||
switch (ttypetag(t1)) {
|
||||
case LUA_TNIL: return 1;
|
||||
case LUA_TNUMINT: return (ivalue(t1) == ivalue(t2));
|
||||
case LUA_TNUMFLT: return luai_numeq(fltvalue(t1), fltvalue(t2));
|
||||
case LUA_TBOOLEAN: return bvalue(t1) == bvalue(t2); /* true must be 1! */
|
||||
case LUA_TLIGHTUSERDATA: return pvalue(t1) == pvalue(t2);
|
||||
case LUA_TLCF: return fvalue(t1) == fvalue(t2);
|
||||
case LUA_TSHRSTR: return eqshrstr(tsvalue(t1), tsvalue(t2));
|
||||
case LUA_TLNGSTR: return luaS_eqlngstr(tsvalue(t1), tsvalue(t2));
|
||||
case LUA_TUSERDATA: {
|
||||
case LUA_VNIL: case LUA_VFALSE: case LUA_VTRUE: return 1;
|
||||
case LUA_VNUMINT: return (ivalue(t1) == ivalue(t2));
|
||||
case LUA_VNUMFLT: return luai_numeq(fltvalue(t1), fltvalue(t2));
|
||||
case LUA_VLIGHTUSERDATA: return pvalue(t1) == pvalue(t2);
|
||||
case LUA_VLCF: return fvalue(t1) == fvalue(t2);
|
||||
case LUA_VSHRSTR: return eqshrstr(tsvalue(t1), tsvalue(t2));
|
||||
case LUA_VLNGSTR: return luaS_eqlngstr(tsvalue(t1), tsvalue(t2));
|
||||
case LUA_VUSERDATA: {
|
||||
if (uvalue(t1) == uvalue(t2)) return 1;
|
||||
else if (L == NULL) return 0;
|
||||
tm = fasttm(L, uvalue(t1)->metatable, TM_EQ);
|
||||
@@ -502,7 +592,7 @@ int luaV_equalobj (lua_State *L, const TValue *t1, const TValue *t2) {
|
||||
tm = fasttm(L, uvalue(t2)->metatable, TM_EQ);
|
||||
break; /* will try TM */
|
||||
}
|
||||
case LUA_TTABLE: {
|
||||
case LUA_VTABLE: {
|
||||
if (hvalue(t1) == hvalue(t2)) return 1;
|
||||
else if (L == NULL) return 0;
|
||||
tm = fasttm(L, hvalue(t1)->metatable, TM_EQ);
|
||||
@@ -544,7 +634,8 @@ static void copy2buff (StkId top, int n, char *buff) {
|
||||
** from 'L->top - total' up to 'L->top - 1'.
|
||||
*/
|
||||
void luaV_concat (lua_State *L, int total) {
|
||||
lua_assert(total >= 2);
|
||||
if (total == 1)
|
||||
return; /* "all" values already concatenated */
|
||||
do {
|
||||
StkId top = L->top;
|
||||
int n = 2; /* number of elements handled in this pass (at least 2) */
|
||||
@@ -590,18 +681,18 @@ void luaV_concat (lua_State *L, int total) {
|
||||
void luaV_objlen (lua_State *L, StkId ra, const TValue *rb) {
|
||||
const TValue *tm;
|
||||
switch (ttypetag(rb)) {
|
||||
case LUA_TTABLE: {
|
||||
case LUA_VTABLE: {
|
||||
Table *h = hvalue(rb);
|
||||
tm = fasttm(L, h->metatable, TM_LEN);
|
||||
if (tm) break; /* metamethod? break switch to call it */
|
||||
setivalue(s2v(ra), luaH_getn(h)); /* else primitive len */
|
||||
return;
|
||||
}
|
||||
case LUA_TSHRSTR: {
|
||||
case LUA_VSHRSTR: {
|
||||
setivalue(s2v(ra), tsvalue(rb)->shrlen);
|
||||
return;
|
||||
}
|
||||
case LUA_TLNGSTR: {
|
||||
case LUA_VLNGSTR: {
|
||||
setivalue(s2v(ra), tsvalue(rb)->u.lnglen);
|
||||
return;
|
||||
}
|
||||
@@ -750,10 +841,8 @@ void luaV_finishOp (lua_State *L) {
|
||||
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 */
|
||||
if (total > 1) { /* are there elements to concat? */
|
||||
L->top = top - 1; /* top is one after last element (at top-2) */
|
||||
luaV_concat(L, total); /* concat them (may yield again) */
|
||||
}
|
||||
L->top = top - 1; /* top is one after last element (at top-2) */
|
||||
luaV_concat(L, total); /* concat them (may yield again) */
|
||||
break;
|
||||
}
|
||||
default: {
|
||||
@@ -788,39 +877,22 @@ void luaV_finishOp (lua_State *L) {
|
||||
#define l_gei(a,b) (a >= b)
|
||||
|
||||
|
||||
/*
|
||||
** Auxiliary macro for arithmetic operations over floats and others
|
||||
** with immediate operand. 'fop' is the float operation; 'tm' is the
|
||||
** corresponding metamethod; 'flip' is true if operands were flipped.
|
||||
*/
|
||||
#define op_arithfI_aux(L,v1,imm,fop,tm,flip) { \
|
||||
lua_Number nb; \
|
||||
if (tonumberns(v1, nb)) { \
|
||||
lua_Number fimm = cast_num(imm); \
|
||||
pc++; setfltvalue(s2v(ra), fop(L, nb, fimm)); \
|
||||
}}
|
||||
|
||||
|
||||
/*
|
||||
** Arithmetic operations over floats and others with immediate operand.
|
||||
*/
|
||||
#define op_arithfI(L,fop,tm) { \
|
||||
TValue *v1 = vRB(i); \
|
||||
int imm = GETARG_sC(i); \
|
||||
op_arithfI_aux(L, v1, imm, fop, tm, 0); }
|
||||
|
||||
/*
|
||||
** Arithmetic operations with immediate operands. 'iop' is the integer
|
||||
** operation.
|
||||
** operation, 'fop' is the float operation.
|
||||
*/
|
||||
#define op_arithI(L,iop,fop,tm,flip) { \
|
||||
#define op_arithI(L,iop,fop) { \
|
||||
TValue *v1 = vRB(i); \
|
||||
int imm = GETARG_sC(i); \
|
||||
if (ttisinteger(v1)) { \
|
||||
lua_Integer iv1 = ivalue(v1); \
|
||||
pc++; setivalue(s2v(ra), iop(L, iv1, imm)); \
|
||||
} \
|
||||
else op_arithfI_aux(L, v1, imm, fop, tm, flip); }
|
||||
else if (ttisfloat(v1)) { \
|
||||
lua_Number nb = fltvalue(v1); \
|
||||
lua_Number fimm = cast_num(imm); \
|
||||
pc++; setfltvalue(s2v(ra), fop(L, nb, fimm)); \
|
||||
}}
|
||||
|
||||
|
||||
/*
|
||||
@@ -844,11 +916,18 @@ void luaV_finishOp (lua_State *L) {
|
||||
|
||||
|
||||
/*
|
||||
** Arithmetic operations with register operands.
|
||||
** Arithmetic operations with K operands for floats.
|
||||
*/
|
||||
#define op_arith(L,iop,fop) { \
|
||||
#define op_arithfK(L,fop) { \
|
||||
TValue *v1 = vRB(i); \
|
||||
TValue *v2 = vRC(i); \
|
||||
TValue *v2 = KC(i); \
|
||||
op_arithf_aux(L, v1, v2, fop); }
|
||||
|
||||
|
||||
/*
|
||||
** Arithmetic operations over integers and floats.
|
||||
*/
|
||||
#define op_arith_aux(L,v1,v2,iop,fop) { \
|
||||
if (ttisinteger(v1) && ttisinteger(v2)) { \
|
||||
lua_Integer i1 = ivalue(v1); lua_Integer i2 = ivalue(v2); \
|
||||
pc++; setivalue(s2v(ra), iop(L, i1, i2)); \
|
||||
@@ -857,32 +936,21 @@ void luaV_finishOp (lua_State *L) {
|
||||
|
||||
|
||||
/*
|
||||
** Arithmetic operations with K operands.
|
||||
** Arithmetic operations with register operands.
|
||||
*/
|
||||
#define op_arithK(L,iop,fop,flip) { \
|
||||
#define op_arith(L,iop,fop) { \
|
||||
TValue *v1 = vRB(i); \
|
||||
TValue *v2 = KC(i); \
|
||||
if (ttisinteger(v1) && ttisinteger(v2)) { \
|
||||
lua_Integer i1 = ivalue(v1); lua_Integer i2 = ivalue(v2); \
|
||||
pc++; setivalue(s2v(ra), iop(L, i1, i2)); \
|
||||
} \
|
||||
else { \
|
||||
lua_Number n1; lua_Number n2; \
|
||||
if (tonumberns(v1, n1) && tonumberns(v2, n2)) { \
|
||||
pc++; setfltvalue(s2v(ra), fop(L, n1, n2)); \
|
||||
}}}
|
||||
TValue *v2 = vRC(i); \
|
||||
op_arith_aux(L, v1, v2, iop, fop); }
|
||||
|
||||
|
||||
/*
|
||||
** Arithmetic operations with K operands for floats.
|
||||
** Arithmetic operations with K operands.
|
||||
*/
|
||||
#define op_arithfK(L,fop) { \
|
||||
#define op_arithK(L,iop,fop) { \
|
||||
TValue *v1 = vRB(i); \
|
||||
TValue *v2 = KC(i); \
|
||||
lua_Number n1; lua_Number n2; \
|
||||
if (tonumberns(v1, n1) && tonumberns(v2, n2)) { \
|
||||
pc++; setfltvalue(s2v(ra), fop(L, n1, n2)); \
|
||||
}}
|
||||
op_arith_aux(L, v1, v2, iop, fop); }
|
||||
|
||||
|
||||
/*
|
||||
@@ -911,9 +979,11 @@ void luaV_finishOp (lua_State *L) {
|
||||
|
||||
|
||||
/*
|
||||
** Order operations with register operands.
|
||||
** Order operations with register operands. 'opn' actually works
|
||||
** for all numbers, but the fast track improves performance for
|
||||
** integers.
|
||||
*/
|
||||
#define op_order(L,opi,opf,other) { \
|
||||
#define op_order(L,opi,opn,other) { \
|
||||
int cond; \
|
||||
TValue *rb = vRB(i); \
|
||||
if (ttisinteger(s2v(ra)) && ttisinteger(rb)) { \
|
||||
@@ -922,14 +992,15 @@ void luaV_finishOp (lua_State *L) {
|
||||
cond = opi(ia, ib); \
|
||||
} \
|
||||
else if (ttisnumber(s2v(ra)) && ttisnumber(rb)) \
|
||||
cond = opf(s2v(ra), rb); \
|
||||
cond = opn(s2v(ra), rb); \
|
||||
else \
|
||||
Protect(cond = other(L, s2v(ra), rb)); \
|
||||
docondjump(); }
|
||||
|
||||
|
||||
/*
|
||||
** Order operations with immediate operand.
|
||||
** Order operations with immediate operand. (Immediate operand is
|
||||
** always small enough to have an exact representation as a float.)
|
||||
*/
|
||||
#define op_orderI(L,opi,opf,inv,tm) { \
|
||||
int cond; \
|
||||
@@ -1021,18 +1092,14 @@ void luaV_finishOp (lua_State *L) {
|
||||
#define ProtectNT(exp) (savepc(L), (exp), updatetrap(ci))
|
||||
|
||||
/*
|
||||
** Protect code that will finish the loop (returns) or can only raise
|
||||
** errors. (That is, it will not return to the interpreter main loop
|
||||
** after changing the stack or hooks.)
|
||||
** Protect code that can only raise errors. (That is, it cannnot change
|
||||
** the stack or hooks.)
|
||||
*/
|
||||
#define halfProtect(exp) (savestate(L,ci), (exp))
|
||||
|
||||
/* idem, but without changing the stack */
|
||||
#define halfProtectNT(exp) (savepc(L), (exp))
|
||||
|
||||
|
||||
/* 'c' is the limit of live values in the stack */
|
||||
#define checkGC(L,c) \
|
||||
{ luaC_condGC(L, L->top = (c), /* limit of live values */ \
|
||||
{ luaC_condGC(L, (savepc(L), L->top = (c)), \
|
||||
updatetrap(ci)); \
|
||||
luai_threadyield(L); }
|
||||
|
||||
@@ -1061,17 +1128,20 @@ void luaV_execute (lua_State *L, CallInfo *ci) {
|
||||
#if LUA_USE_JUMPTABLE
|
||||
#include "ljumptab.h"
|
||||
#endif
|
||||
tailcall:
|
||||
startfunc:
|
||||
trap = L->hookmask;
|
||||
returning: /* trap already set */
|
||||
cl = clLvalue(s2v(ci->func));
|
||||
k = cl->p->k;
|
||||
pc = ci->u.l.savedpc;
|
||||
if (trap) {
|
||||
if (cl->p->is_vararg)
|
||||
trap = 0; /* hooks will start after VARARGPREP instruction */
|
||||
else if (pc == cl->p->code) /* first instruction (not resuming)? */
|
||||
luaD_hookcall(L, ci);
|
||||
ci->u.l.trap = 1; /* there may be other hooks */
|
||||
if (pc == cl->p->code) { /* first instruction (not resuming)? */
|
||||
if (cl->p->is_vararg)
|
||||
trap = 0; /* hooks will start after VARARGPREP instruction */
|
||||
else /* check 'call' hook */
|
||||
luaD_hookcall(L, ci);
|
||||
}
|
||||
ci->u.l.trap = 1; /* assume trap is on, for now */
|
||||
}
|
||||
base = ci->func + 1;
|
||||
/* main loop of interpreter */
|
||||
@@ -1080,9 +1150,9 @@ void luaV_execute (lua_State *L, CallInfo *ci) {
|
||||
StkId ra; /* instruction's A register */
|
||||
vmfetch();
|
||||
lua_assert(base == ci->func + 1);
|
||||
lua_assert(base <= L->top && L->top < L->stack + L->stacksize);
|
||||
lua_assert(base <= L->top && L->top < L->stack_last);
|
||||
/* invalidate top for instructions not expecting it */
|
||||
lua_assert(isIT(i) || (L->top = base));
|
||||
lua_assert(isIT(i) || (cast_void(L->top = base), 1));
|
||||
vmdispatch (GET_OPCODE(i)) {
|
||||
vmcase(OP_MOVE) {
|
||||
setobjs2s(L, ra, RB(i));
|
||||
@@ -1109,9 +1179,17 @@ void luaV_execute (lua_State *L, CallInfo *ci) {
|
||||
setobj2s(L, ra, rb);
|
||||
vmbreak;
|
||||
}
|
||||
vmcase(OP_LOADBOOL) {
|
||||
setbvalue(s2v(ra), GETARG_B(i));
|
||||
if (GETARG_C(i)) pc++; /* skip next instruction (if C) */
|
||||
vmcase(OP_LOADFALSE) {
|
||||
setbfvalue(s2v(ra));
|
||||
vmbreak;
|
||||
}
|
||||
vmcase(OP_LFALSESKIP) {
|
||||
setbfvalue(s2v(ra));
|
||||
pc++; /* skip next instruction */
|
||||
vmbreak;
|
||||
}
|
||||
vmcase(OP_LOADTRUE) {
|
||||
setbtvalue(s2v(ra));
|
||||
vmbreak;
|
||||
}
|
||||
vmcase(OP_LOADNIL) {
|
||||
@@ -1243,8 +1321,9 @@ void luaV_execute (lua_State *L, CallInfo *ci) {
|
||||
Table *t;
|
||||
if (b > 0)
|
||||
b = 1 << (b - 1); /* size is 2^(b - 1) */
|
||||
if (TESTARG_k(i))
|
||||
c += GETARG_Ax(*pc) * (MAXARG_C + 1);
|
||||
lua_assert((!TESTARG_k(i)) == (GETARG_Ax(*pc) == 0));
|
||||
if (TESTARG_k(i)) /* non-zero extra argument? */
|
||||
c += GETARG_Ax(*pc) * (MAXARG_C + 1); /* add it to size */
|
||||
pc++; /* skip extra argument */
|
||||
L->top = ra + 1; /* correct top in case of emergency GC */
|
||||
t = luaH_new(L); /* memory allocation */
|
||||
@@ -1268,23 +1347,23 @@ void luaV_execute (lua_State *L, CallInfo *ci) {
|
||||
vmbreak;
|
||||
}
|
||||
vmcase(OP_ADDI) {
|
||||
op_arithI(L, l_addi, luai_numadd, TM_ADD, GETARG_k(i));
|
||||
op_arithI(L, l_addi, luai_numadd);
|
||||
vmbreak;
|
||||
}
|
||||
vmcase(OP_ADDK) {
|
||||
op_arithK(L, l_addi, luai_numadd, GETARG_k(i));
|
||||
op_arithK(L, l_addi, luai_numadd);
|
||||
vmbreak;
|
||||
}
|
||||
vmcase(OP_SUBK) {
|
||||
op_arithK(L, l_subi, luai_numsub, 0);
|
||||
op_arithK(L, l_subi, luai_numsub);
|
||||
vmbreak;
|
||||
}
|
||||
vmcase(OP_MULK) {
|
||||
op_arithK(L, l_muli, luai_nummul, GETARG_k(i));
|
||||
op_arithK(L, l_muli, luai_nummul);
|
||||
vmbreak;
|
||||
}
|
||||
vmcase(OP_MODK) {
|
||||
op_arithK(L, luaV_mod, luaV_modf, 0);
|
||||
op_arithK(L, luaV_mod, luaV_modf);
|
||||
vmbreak;
|
||||
}
|
||||
vmcase(OP_POWK) {
|
||||
@@ -1296,7 +1375,7 @@ void luaV_execute (lua_State *L, CallInfo *ci) {
|
||||
vmbreak;
|
||||
}
|
||||
vmcase(OP_IDIVK) {
|
||||
op_arithK(L, luaV_idiv, luai_numidiv, 0);
|
||||
op_arithK(L, luaV_idiv, luai_numidiv);
|
||||
vmbreak;
|
||||
}
|
||||
vmcase(OP_BANDK) {
|
||||
@@ -1430,8 +1509,10 @@ void luaV_execute (lua_State *L, CallInfo *ci) {
|
||||
}
|
||||
vmcase(OP_NOT) {
|
||||
TValue *rb = vRB(i);
|
||||
int nrb = l_isfalse(rb); /* next assignment may change this value */
|
||||
setbvalue(s2v(ra), nrb);
|
||||
if (l_isfalse(rb))
|
||||
setbtvalue(s2v(ra));
|
||||
else
|
||||
setbfvalue(s2v(ra));
|
||||
vmbreak;
|
||||
}
|
||||
vmcase(OP_LEN) {
|
||||
@@ -1476,7 +1557,7 @@ void luaV_execute (lua_State *L, CallInfo *ci) {
|
||||
vmcase(OP_EQK) {
|
||||
TValue *rb = KB(i);
|
||||
/* basic types do not use '__eq'; we can use raw equality */
|
||||
int cond = luaV_equalobj(NULL, s2v(ra), rb);
|
||||
int cond = luaV_rawequalobj(s2v(ra), rb);
|
||||
docondjump();
|
||||
vmbreak;
|
||||
}
|
||||
@@ -1524,24 +1605,32 @@ void luaV_execute (lua_State *L, CallInfo *ci) {
|
||||
vmbreak;
|
||||
}
|
||||
vmcase(OP_CALL) {
|
||||
CallInfo *newci;
|
||||
int b = GETARG_B(i);
|
||||
int nresults = GETARG_C(i) - 1;
|
||||
if (b != 0) /* fixed number of arguments? */
|
||||
L->top = ra + b; /* top signals number of arguments */
|
||||
/* else previous instruction set top */
|
||||
ProtectNT(luaD_call(L, ra, nresults));
|
||||
savepc(L); /* in case of errors */
|
||||
if ((newci = luaD_precall(L, ra, nresults)) == NULL)
|
||||
updatetrap(ci); /* C call; nothing else to be done */
|
||||
else { /* Lua call: run function in this same C frame */
|
||||
ci = newci;
|
||||
ci->callstatus = 0; /* call re-uses 'luaV_execute' */
|
||||
goto startfunc;
|
||||
}
|
||||
vmbreak;
|
||||
}
|
||||
vmcase(OP_TAILCALL) {
|
||||
int b = GETARG_B(i); /* number of arguments + 1 (function) */
|
||||
int nparams1 = GETARG_C(i);
|
||||
/* delat is virtual 'func' - real 'func' (vararg functions) */
|
||||
/* delta is virtual 'func' - real 'func' (vararg functions) */
|
||||
int delta = (nparams1) ? ci->u.l.nextraargs + nparams1 : 0;
|
||||
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 */
|
||||
savepc(ci); /* several calls here can raise errors */
|
||||
if (TESTARG_k(i)) {
|
||||
/* close upvalues from current call; the compiler ensures
|
||||
that there are no to-be-closed variables here, so this
|
||||
@@ -1549,24 +1638,23 @@ void luaV_execute (lua_State *L, CallInfo *ci) {
|
||||
luaF_close(L, base, NOCLOSINGMETH);
|
||||
lua_assert(base == ci->func + 1);
|
||||
}
|
||||
if (!ttisfunction(s2v(ra))) { /* not a function? */
|
||||
while (!ttisfunction(s2v(ra))) { /* not a function? */
|
||||
luaD_tryfuncTM(L, ra); /* try '__call' metamethod */
|
||||
b++; /* there is now one extra argument */
|
||||
checkstackGCp(L, 1, ra);
|
||||
}
|
||||
if (!ttisLclosure(s2v(ra))) { /* C function? */
|
||||
luaD_call(L, ra, LUA_MULTRET); /* call it */
|
||||
luaD_precall(L, ra, LUA_MULTRET); /* call it */
|
||||
updatetrap(ci);
|
||||
updatestack(ci); /* stack may have been relocated */
|
||||
ci->func -= delta;
|
||||
luaD_poscall(L, ci, cast_int(L->top - ra));
|
||||
return;
|
||||
ci->func -= delta; /* restore 'func' (if vararg) */
|
||||
luaD_poscall(L, ci, cast_int(L->top - ra)); /* finish caller */
|
||||
updatetrap(ci); /* 'luaD_poscall' can change hooks */
|
||||
goto ret; /* caller returns after the tail call */
|
||||
}
|
||||
else { /* Lua tail call */
|
||||
ci->func -= delta;
|
||||
luaD_pretailcall(L, ci, ra, b); /* prepare call frame */
|
||||
goto tailcall;
|
||||
}
|
||||
vmbreak;
|
||||
ci->func -= delta; /* restore 'func' (if vararg) */
|
||||
luaD_pretailcall(L, ci, ra, b); /* prepare call frame */
|
||||
goto startfunc; /* execute the callee */
|
||||
}
|
||||
vmcase(OP_RETURN) {
|
||||
int n = GETARG_B(i) - 1; /* number of results */
|
||||
@@ -1585,12 +1673,15 @@ void luaV_execute (lua_State *L, CallInfo *ci) {
|
||||
ci->func -= ci->u.l.nextraargs + nparams1;
|
||||
L->top = ra + n; /* set call for 'luaD_poscall' */
|
||||
luaD_poscall(L, ci, n);
|
||||
return;
|
||||
updatetrap(ci); /* 'luaD_poscall' can change hooks */
|
||||
goto ret;
|
||||
}
|
||||
vmcase(OP_RETURN0) {
|
||||
if (L->hookmask) {
|
||||
L->top = ra;
|
||||
halfProtectNT(luaD_poscall(L, ci, 0)); /* no hurry... */
|
||||
savepc(ci);
|
||||
luaD_poscall(L, ci, 0); /* no hurry... */
|
||||
trap = 1;
|
||||
}
|
||||
else { /* do the 'poscall' here */
|
||||
int nres = ci->nresults;
|
||||
@@ -1599,12 +1690,14 @@ void luaV_execute (lua_State *L, CallInfo *ci) {
|
||||
while (nres-- > 0)
|
||||
setnilvalue(s2v(L->top++)); /* all results are nil */
|
||||
}
|
||||
return;
|
||||
goto ret;
|
||||
}
|
||||
vmcase(OP_RETURN1) {
|
||||
if (L->hookmask) {
|
||||
L->top = ra + 1;
|
||||
halfProtectNT(luaD_poscall(L, ci, 1)); /* no hurry... */
|
||||
savepc(ci);
|
||||
luaD_poscall(L, ci, 1); /* no hurry... */
|
||||
trap = 1;
|
||||
}
|
||||
else { /* do the 'poscall' here */
|
||||
int nres = ci->nresults;
|
||||
@@ -1618,7 +1711,13 @@ void luaV_execute (lua_State *L, CallInfo *ci) {
|
||||
setnilvalue(s2v(L->top++));
|
||||
}
|
||||
}
|
||||
return;
|
||||
ret: /* return from a Lua function */
|
||||
if (ci->callstatus & CIST_FRESH)
|
||||
return; /* end this frame */
|
||||
else {
|
||||
ci = ci->previous;
|
||||
goto returning; /* continue running caller in this frame */
|
||||
}
|
||||
}
|
||||
vmcase(OP_FORLOOP) {
|
||||
if (ttisinteger(s2v(ra + 2))) { /* integer loop? */
|
||||
@@ -1633,73 +1732,15 @@ void luaV_execute (lua_State *L, CallInfo *ci) {
|
||||
pc -= GETARG_Bx(i); /* jump back */
|
||||
}
|
||||
}
|
||||
else { /* floating loop */
|
||||
lua_Number step = fltvalue(s2v(ra + 2));
|
||||
lua_Number limit = fltvalue(s2v(ra + 1));
|
||||
lua_Number idx = fltvalue(s2v(ra));
|
||||
idx = luai_numadd(L, idx, step); /* increment index */
|
||||
if (luai_numlt(0, step) ? luai_numle(idx, limit)
|
||||
: luai_numle(limit, idx)) {
|
||||
chgfltvalue(s2v(ra), idx); /* update internal index */
|
||||
setfltvalue(s2v(ra + 3), idx); /* and control variable */
|
||||
pc -= GETARG_Bx(i); /* jump back */
|
||||
}
|
||||
}
|
||||
else if (floatforloop(ra)) /* float loop */
|
||||
pc -= GETARG_Bx(i); /* jump back */
|
||||
updatetrap(ci); /* allows a signal to break the loop */
|
||||
vmbreak;
|
||||
}
|
||||
vmcase(OP_FORPREP) {
|
||||
TValue *pinit = s2v(ra);
|
||||
TValue *plimit = s2v(ra + 1);
|
||||
TValue *pstep = s2v(ra + 2);
|
||||
savestate(L, ci); /* in case of errors */
|
||||
if (ttisinteger(pinit) && ttisinteger(pstep)) { /* integer loop? */
|
||||
lua_Integer init = ivalue(pinit);
|
||||
lua_Integer step = ivalue(pstep);
|
||||
lua_Integer limit;
|
||||
if (step == 0)
|
||||
luaG_runerror(L, "'for' step is zero");
|
||||
setivalue(s2v(ra + 3), init); /* control variable */
|
||||
if (forlimit(L, init, plimit, &limit, step))
|
||||
pc += GETARG_Bx(i) + 1; /* skip the loop */
|
||||
else { /* prepare loop counter */
|
||||
lua_Unsigned count;
|
||||
if (step > 0) { /* ascending loop? */
|
||||
count = l_castS2U(limit) - l_castS2U(init);
|
||||
if (step != 1) /* avoid division in the too common case */
|
||||
count /= l_castS2U(step);
|
||||
}
|
||||
else { /* step < 0; descending loop */
|
||||
count = l_castS2U(init) - l_castS2U(limit);
|
||||
/* 'step+1' avoids negating 'mininteger' */
|
||||
count /= l_castS2U(-(step + 1)) + 1u;
|
||||
}
|
||||
/* store the counter in place of the limit (which won't be
|
||||
needed anymore */
|
||||
setivalue(plimit, l_castU2S(count));
|
||||
}
|
||||
}
|
||||
else { /* try making all values floats */
|
||||
lua_Number init; lua_Number limit; lua_Number step;
|
||||
if (unlikely(!tonumber(plimit, &limit)))
|
||||
luaG_forerror(L, plimit, "limit");
|
||||
if (unlikely(!tonumber(pstep, &step)))
|
||||
luaG_forerror(L, pstep, "step");
|
||||
if (unlikely(!tonumber(pinit, &init)))
|
||||
luaG_forerror(L, pinit, "initial value");
|
||||
if (step == 0)
|
||||
luaG_runerror(L, "'for' step is zero");
|
||||
if (luai_numlt(0, step) ? luai_numlt(limit, init)
|
||||
: luai_numlt(init, limit))
|
||||
pc += GETARG_Bx(i) + 1; /* skip the loop */
|
||||
else {
|
||||
/* make sure internal values are all float */
|
||||
setfltvalue(plimit, limit);
|
||||
setfltvalue(pstep, step);
|
||||
setfltvalue(s2v(ra), init); /* internal index */
|
||||
setfltvalue(s2v(ra + 3), init); /* control variable */
|
||||
}
|
||||
}
|
||||
if (forprep(L, ra))
|
||||
pc += GETARG_Bx(i) + 1; /* skip the loop */
|
||||
vmbreak;
|
||||
}
|
||||
vmcase(OP_TFORPREP) {
|
||||
@@ -1769,11 +1810,10 @@ void luaV_execute (lua_State *L, CallInfo *ci) {
|
||||
vmbreak;
|
||||
}
|
||||
vmcase(OP_VARARGPREP) {
|
||||
luaT_adjustvarargs(L, GETARG_A(i), ci, cl->p);
|
||||
updatetrap(ci);
|
||||
ProtectNT(luaT_adjustvarargs(L, GETARG_A(i), ci, cl->p));
|
||||
if (trap) {
|
||||
luaD_hookcall(L, ci);
|
||||
L->oldpc = pc + 1; /* next opcode will be seen as a "new" line */
|
||||
L->oldpc = 1; /* next opcode will be seen as a "new" line */
|
||||
}
|
||||
updatebase(ci); /* function has new base after adjustment */
|
||||
vmbreak;
|
||||
|
||||
@@ -33,10 +33,20 @@
|
||||
** integral values)
|
||||
*/
|
||||
#if !defined(LUA_FLOORN2I)
|
||||
#define LUA_FLOORN2I 0
|
||||
#define LUA_FLOORN2I F2Ieq
|
||||
#endif
|
||||
|
||||
|
||||
/*
|
||||
** Rounding modes for float->integer coercion
|
||||
*/
|
||||
typedef enum {
|
||||
F2Ieq, /* no rounding; accepts only integral values */
|
||||
F2Ifloor, /* takes the floor of the number */
|
||||
F2Iceil /* takes the ceil of the number */
|
||||
} F2Imod;
|
||||
|
||||
|
||||
/* convert an object to a float (including string coercion) */
|
||||
#define tonumber(o,n) \
|
||||
(ttisfloat(o) ? (*(n) = fltvalue(o), 1) : luaV_tonumber_(o,n))
|
||||
@@ -104,9 +114,10 @@ LUAI_FUNC int luaV_equalobj (lua_State *L, const TValue *t1, const TValue *t2);
|
||||
LUAI_FUNC int luaV_lessthan (lua_State *L, const TValue *l, const TValue *r);
|
||||
LUAI_FUNC int luaV_lessequal (lua_State *L, const TValue *l, const TValue *r);
|
||||
LUAI_FUNC int luaV_tonumber_ (const TValue *obj, lua_Number *n);
|
||||
LUAI_FUNC int luaV_tointeger (const TValue *obj, lua_Integer *p, int mode);
|
||||
LUAI_FUNC int luaV_tointegerns (const TValue *obj, lua_Integer *p, int mode);
|
||||
LUAI_FUNC int luaV_flttointeger (lua_Number n, lua_Integer *p, int mode);
|
||||
LUAI_FUNC int luaV_tointeger (const TValue *obj, lua_Integer *p, F2Imod mode);
|
||||
LUAI_FUNC int luaV_tointegerns (const TValue *obj, lua_Integer *p,
|
||||
F2Imod mode);
|
||||
LUAI_FUNC int luaV_flttointeger (lua_Number n, lua_Integer *p, F2Imod mode);
|
||||
LUAI_FUNC void luaV_finishget (lua_State *L, const TValue *t, TValue *key,
|
||||
StkId val, const TValue *slot);
|
||||
LUAI_FUNC void luaV_finishset (lua_State *L, const TValue *t, TValue *key,
|
||||
|
||||
@@ -1,6 +1,5 @@
|
||||
# makefile for building Lua
|
||||
# see INSTALL for installation instructions
|
||||
# see ../Makefile and luaconf.h for further customization
|
||||
# Developer's makefile for building Lua
|
||||
# see luaconf.h for further customization
|
||||
|
||||
# == CHANGE THE SETTINGS BELOW TO SUIT YOUR ENVIRONMENT =======================
|
||||
|
||||
@@ -38,10 +37,18 @@ CWARNSC= -Wdeclaration-after-statement \
|
||||
|
||||
CWARNS= $(CWARNSCPP) $(CWARNSC)
|
||||
|
||||
# Some useful compiler options for internal tests:
|
||||
# -DLUAI_ASSERT turns on all assertions inside Lua.
|
||||
# -DHARDSTACKTESTS forces a reallocation of the stack at every point where
|
||||
# the stack can be reallocated.
|
||||
# -DHARDMEMTESTS forces a full collection at all points where the collector
|
||||
# can run.
|
||||
# -DEMERGENCYGCTESTS forces an emergency collection at every single allocation.
|
||||
# -DEXTERNMEMCHECK removes internal consistency checking of blocks being
|
||||
# deallocated (useful when an external tool like valgrind does the check).
|
||||
# -DMAXINDEXRK=k limits range of constants in RK instruction operands.
|
||||
# -DLUA_COMPAT_5_3
|
||||
|
||||
# -DEXTERNMEMCHECK -DHARDSTACKTESTS -DHARDMEMTESTS -DTRACEMEM='"tempmem"'
|
||||
# -DMAXINDEXRK=1 -DLUA_COMPAT_5_3
|
||||
# -g -DLUA_USER_H='"ltests.h"'
|
||||
# -pg -malign-double
|
||||
# -DLUA_USE_CTYPE -DLUA_USE_APICHECK
|
||||
# ('-ftrapv' for runtime checks of integer overflows)
|
||||
@@ -82,11 +89,9 @@ LIB_O= lbaselib.o ldblib.o liolib.o lmathlib.o loslib.o ltablib.o lstrlib.o \
|
||||
LUA_T= lua
|
||||
LUA_O= lua.o
|
||||
|
||||
# LUAC_T= luac
|
||||
# LUAC_O= luac.o print.o
|
||||
|
||||
ALL_T= $(CORE_T) $(LUA_T) $(LUAC_T)
|
||||
ALL_O= $(CORE_O) $(LUA_O) $(LUAC_O) $(AUX_O) $(LIB_O)
|
||||
ALL_T= $(CORE_T) $(LUA_T)
|
||||
ALL_O= $(CORE_O) $(LUA_O) $(AUX_O) $(LIB_O)
|
||||
ALL_A= $(CORE_T)
|
||||
|
||||
all: $(ALL_T)
|
||||
@@ -103,18 +108,6 @@ $(CORE_T): $(CORE_O) $(AUX_O) $(LIB_O)
|
||||
$(LUA_T): $(LUA_O) $(CORE_T)
|
||||
$(CC) -o $@ $(MYLDFLAGS) $(LUA_O) $(CORE_T) $(LIBS) $(MYLIBS) $(DL)
|
||||
|
||||
$(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:
|
||||
$(RM) $(ALL_T) $(ALL_O)
|
||||
|
||||
+1
-1
@@ -30,7 +30,7 @@ by Roberto Ierusalimschy, Luiz Henrique de Figueiredo, Waldemar Celes
|
||||
<p>
|
||||
<small>
|
||||
<a href="http://www.lua.org/copyright.html">Copyright</a>
|
||||
© 2019 Lua.org, PUC-Rio. All rights reserved.
|
||||
© 2020 Lua.org, PUC-Rio. All rights reserved.
|
||||
</small>
|
||||
<hr>
|
||||
|
||||
|
||||
+432
-258
File diff suppressed because it is too large
Load Diff
+2
-2
@@ -127,8 +127,8 @@ else
|
||||
end
|
||||
|
||||
Cstacklevel = function ()
|
||||
local _, _, ncalls, nci = T.stacklevel()
|
||||
return ncalls + nci -- number of free slots in the C stack
|
||||
local _, _, ncalls = T.stacklevel()
|
||||
return ncalls -- number of C calls
|
||||
end
|
||||
end
|
||||
|
||||
|
||||
+65
-20
@@ -11,6 +11,9 @@ local debug = require "debug"
|
||||
local pack = table.pack
|
||||
|
||||
|
||||
-- standard error message for memory errors
|
||||
local MEMERRMSG = "not enough memory"
|
||||
|
||||
function tcheck (t1, t2)
|
||||
assert(t1.n == (t2.n or #t2) + 1)
|
||||
for i = 2, t1.n do assert(t1[i] == t2[i - 1]) end
|
||||
@@ -408,7 +411,7 @@ do
|
||||
|
||||
-- memory error
|
||||
T.totalmem(T.totalmem()+10000) -- set low memory limit (+10k)
|
||||
assert(T.checkpanic("newuserdata 20000") == "not enough memory")
|
||||
assert(T.checkpanic("newuserdata 20000") == MEMERRMSG)
|
||||
T.totalmem(0) -- restore high limit
|
||||
|
||||
-- stack error
|
||||
@@ -516,9 +519,11 @@ print"+"
|
||||
|
||||
do -- getp/setp
|
||||
local a = {}
|
||||
T.testC("rawsetp 2 1", a, 20)
|
||||
local a1 = T.testC("rawsetp 2 1; return 1", a, 20)
|
||||
assert(a == a1)
|
||||
assert(a[T.pushuserdata(1)] == 20)
|
||||
assert(T.testC("rawgetp -1 1; return 1", a) == 20)
|
||||
local a1, res = T.testC("rawgetp -1 1; return 2", a)
|
||||
assert(a == a1 and res == 20)
|
||||
end
|
||||
|
||||
|
||||
@@ -803,7 +808,7 @@ F = function (x)
|
||||
if A ~= nil then
|
||||
assert(type(A) == "userdata")
|
||||
assert(T.udataval(A) == B)
|
||||
debug.getmetatable(A) -- just acess it
|
||||
debug.getmetatable(A) -- just access it
|
||||
end
|
||||
A = x -- ressucita userdata
|
||||
B = udval
|
||||
@@ -1110,7 +1115,7 @@ do
|
||||
-- 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)
|
||||
toclose -1 # mark it to be closed (should raise an error)
|
||||
abort # will not be executed
|
||||
]])
|
||||
assert(a == false and
|
||||
@@ -1151,40 +1156,74 @@ do
|
||||
end
|
||||
|
||||
|
||||
-------------------------------------------------------------------------
|
||||
-- testing memory limits
|
||||
-------------------------------------------------------------------------
|
||||
--[[
|
||||
** {==================================================================
|
||||
** Testing memory limits
|
||||
** ===================================================================
|
||||
--]]
|
||||
|
||||
print("memory-allocation errors")
|
||||
|
||||
checkerr("block too big", T.newuserdata, math.maxinteger)
|
||||
collectgarbage()
|
||||
local f = load"local a={}; for i=1,100000 do a[i]=i end"
|
||||
T.alloccount(10)
|
||||
checkerr("not enough memory", f)
|
||||
checkerr(MEMERRMSG, f)
|
||||
T.alloccount() -- remove limit
|
||||
|
||||
|
||||
-- test memory errors; increase limit for maximum memory by steps,
|
||||
-- o that we get memory errors in all allocations of a given
|
||||
-- task, until there is enough memory to complete the task without
|
||||
-- errors.
|
||||
function testbytes (s, f)
|
||||
collectgarbage()
|
||||
local M = T.totalmem()
|
||||
local oldM = M
|
||||
local a,b = nil
|
||||
while true do
|
||||
collectgarbage(); collectgarbage()
|
||||
T.totalmem(M)
|
||||
a, b = T.testC("pcall 0 1 0; pushstatus; return 2", f)
|
||||
T.totalmem(0) -- remove limit
|
||||
if a and b == "OK" then break end -- stop when no more errors
|
||||
if b ~= "OK" and b ~= MEMERRMSG then -- not a memory error?
|
||||
error(a, 0) -- propagate it
|
||||
end
|
||||
M = M + 7 -- increase memory limit
|
||||
end
|
||||
print(string.format("minimum memory for %s: %d bytes", s, M - oldM))
|
||||
return a
|
||||
end
|
||||
|
||||
-- test memory errors; increase limit for number of allocations one
|
||||
-- by one, so that we get memory errors in all allocations of a given
|
||||
-- task, until there is enough allocations to complete the task without
|
||||
-- errors.
|
||||
|
||||
function testamem (s, f)
|
||||
collectgarbage(); collectgarbage()
|
||||
function testalloc (s, f)
|
||||
collectgarbage()
|
||||
local M = 0
|
||||
local a,b = nil
|
||||
while true do
|
||||
collectgarbage(); collectgarbage()
|
||||
T.alloccount(M)
|
||||
a, b = pcall(f)
|
||||
a, b = T.testC("pcall 0 1 0; pushstatus; return 2", f)
|
||||
T.alloccount() -- remove limit
|
||||
if a and b then break end -- stop when no more errors
|
||||
if not a and not -- `real' error?
|
||||
(string.find(b, "memory") or string.find(b, "overflow")) then
|
||||
error(b, 0) -- propagate it
|
||||
if a and b == "OK" then break end -- stop when no more errors
|
||||
if b ~= "OK" and b ~= MEMERRMSG then -- not a memory error?
|
||||
error(a, 0) -- propagate it
|
||||
end
|
||||
M = M + 1 -- increase allocation limit
|
||||
end
|
||||
print(string.format("limit for %s: %d allocations", s, M))
|
||||
return b
|
||||
print(string.format("minimum allocations for %s: %d allocations", s, M))
|
||||
return a
|
||||
end
|
||||
|
||||
|
||||
local function testamem (s, f)
|
||||
testalloc(s, f)
|
||||
return testbytes(s, f)
|
||||
end
|
||||
|
||||
|
||||
@@ -1194,8 +1233,11 @@ assert(b == 10)
|
||||
|
||||
-- testing memory errors when creating a new state
|
||||
|
||||
b = testamem("state creation", T.newstate)
|
||||
T.closestate(b); -- close new state
|
||||
testamem("state creation", function ()
|
||||
local st = T.newstate()
|
||||
if st then T.closestate(st) end -- close new state
|
||||
return st
|
||||
end)
|
||||
|
||||
testamem("empty-table creation", function ()
|
||||
return {}
|
||||
@@ -1343,6 +1385,9 @@ testamem("growing stack", function ()
|
||||
return foo(100)
|
||||
end)
|
||||
|
||||
-- }==================================================================
|
||||
|
||||
|
||||
do -- testing failing in 'lua_checkstack'
|
||||
local res = T.testC([[rawcheckstack 500000; return 1]])
|
||||
assert(res == false)
|
||||
|
||||
+24
-1
@@ -28,7 +28,7 @@ do
|
||||
local path = table.concat(t, ";")
|
||||
-- use that path in a search
|
||||
local s, err = package.searchpath("xuxu", path)
|
||||
-- search fails; check that message has an occurence of
|
||||
-- search fails; check that message has an occurrence of
|
||||
-- '??????????' with ? replaced by xuxu and at least 'max' lines
|
||||
assert(not s and
|
||||
string.find(err, string.rep("xuxu", 10)) and
|
||||
@@ -47,6 +47,29 @@ do
|
||||
package.path = oldpath
|
||||
end
|
||||
|
||||
|
||||
do print"testing 'require' message"
|
||||
local oldpath = package.path
|
||||
local oldcpath = package.cpath
|
||||
|
||||
package.path = "?.lua;?/?"
|
||||
package.cpath = "?.so;?/init"
|
||||
|
||||
local st, msg = pcall(require, 'XXX')
|
||||
|
||||
local expected = [[module 'XXX' not found:
|
||||
no field package.preload['XXX']
|
||||
no file 'XXX.lua'
|
||||
no file 'XXX/XXX'
|
||||
no file 'XXX.so'
|
||||
no file 'XXX/init']]
|
||||
|
||||
assert(msg == expected)
|
||||
|
||||
package.path = oldpath
|
||||
package.cpath = oldcpath
|
||||
end
|
||||
|
||||
print('+')
|
||||
|
||||
|
||||
|
||||
+1
-1
@@ -66,7 +66,7 @@ assert(repstrings * ssize > 2.0^32) -- it should be larger than maximum size
|
||||
|
||||
local longs = string.rep("\0", ssize) -- create one long string
|
||||
|
||||
-- create function to concatentate 'repstrings' copies of its argument
|
||||
-- create function to concatenate 'repstrings' copies of its argument
|
||||
local rep = assert(load(
|
||||
"local a = ...; return " .. string.rep("a", repstrings, "..")))
|
||||
|
||||
|
||||
+55
-9
@@ -107,7 +107,9 @@ end
|
||||
deep(10)
|
||||
deep(180)
|
||||
|
||||
-- testing tail calls
|
||||
|
||||
print"testing tail calls"
|
||||
|
||||
function deep (n) if n>0 then return deep(n-1) else return 101 end end
|
||||
assert(deep(30000) == 101)
|
||||
a = {}
|
||||
@@ -148,6 +150,27 @@ do -- tail calls x varargs
|
||||
assert(X == 10 and Y == 20 and #A == 1 and A[1] == 30)
|
||||
end
|
||||
|
||||
|
||||
|
||||
do -- tail calls x chain of __call
|
||||
local n = 10000 -- depth
|
||||
|
||||
local function foo ()
|
||||
if n == 0 then return 1023
|
||||
else n = n - 1; return foo()
|
||||
end
|
||||
end
|
||||
|
||||
-- build a chain of __call metamethods ending in function 'foo'
|
||||
for i = 1, 100 do
|
||||
foo = setmetatable({}, {__call = foo})
|
||||
end
|
||||
|
||||
-- call the first one as a tail call in a new coroutine
|
||||
-- (to ensure stack is not preallocated)
|
||||
assert(coroutine.wrap(function() return foo() end)() == 1023)
|
||||
end
|
||||
|
||||
print('+')
|
||||
|
||||
|
||||
@@ -294,6 +317,16 @@ f = load(string.dump(function () return 1 end), nil, "b", {})
|
||||
assert(type(f) == "function" and f() == 1)
|
||||
|
||||
|
||||
do -- another bug (in 5.4.0)
|
||||
-- loading a binary long string interrupted by GC cycles
|
||||
local f = string.dump(function ()
|
||||
return '01234567890123456789012345678901234567890123456789'
|
||||
end)
|
||||
f = load(read1(f))
|
||||
assert(f() == '01234567890123456789012345678901234567890123456789')
|
||||
end
|
||||
|
||||
|
||||
x = string.dump(load("x = 1; return x"))
|
||||
a = assert(load(read1(x), nil, "b"))
|
||||
assert(a() == 1 and _G.x == 1)
|
||||
@@ -335,8 +368,12 @@ x = [[
|
||||
end
|
||||
end
|
||||
]]
|
||||
a = assert(load(read1(x), "read", "t"))
|
||||
assert(a()(2)(3)(10) == 15)
|
||||
|
||||
a = assert(load(read1(x)))
|
||||
-- repeat the test loading a binary chunk
|
||||
x = string.dump(a)
|
||||
a = assert(load(read1(x), "read", "b"))
|
||||
assert(a()(2)(3)(10) == 15)
|
||||
|
||||
|
||||
@@ -399,20 +436,30 @@ assert((function (a) return a end)() == nil)
|
||||
|
||||
print("testing binary chunks")
|
||||
do
|
||||
local header = string.pack("c4BBBc6BBBj",
|
||||
local header = string.pack("c4BBc6BBB",
|
||||
"\27Lua", -- signature
|
||||
(504 >> 7) & 0x7f, (504 & 0x7f) | 0x80, -- version 5.4 (504)
|
||||
0x54, -- version 5.4 (0x54)
|
||||
0, -- format
|
||||
"\x19\x93\r\n\x1a\n", -- data
|
||||
4, -- size of instruction
|
||||
string.packsize("j"), -- sizeof(lua integer)
|
||||
string.packsize("n"), -- sizeof(lua number)
|
||||
0x5678 -- LUAC_INT
|
||||
-- LUAC_NUM may not have a unique binary representation (padding...)
|
||||
string.packsize("n") -- sizeof(lua number)
|
||||
)
|
||||
local c = string.dump(function () local a = 1; local b = 3; return a+b*3 end)
|
||||
local c = string.dump(function ()
|
||||
local a = 1; local b = 3;
|
||||
local f = function () return a + b + _ENV.c; end -- upvalues
|
||||
local s1 = "a constant"
|
||||
local s2 = "another constant"
|
||||
return a + b * 3
|
||||
end)
|
||||
|
||||
assert(assert(load(c))() == 10)
|
||||
|
||||
-- check header
|
||||
assert(string.sub(c, 1, #header) == header)
|
||||
-- check LUAC_INT and LUAC_NUM
|
||||
local ci, cn = string.unpack("jn", c, #header + 1)
|
||||
assert(ci == 0x5678 and cn == 370.5)
|
||||
|
||||
-- corrupted header
|
||||
for i = 1, #header do
|
||||
@@ -428,7 +475,6 @@ do
|
||||
local st, msg = load(string.sub(c, 1, i))
|
||||
assert(not st and string.find(msg, "truncated"))
|
||||
end
|
||||
assert(assert(load(c))() == 10)
|
||||
end
|
||||
|
||||
print('OK')
|
||||
|
||||
+1
-1
@@ -242,7 +242,7 @@ end
|
||||
|
||||
assert(debug.upvalueid(foo1, 1))
|
||||
assert(debug.upvalueid(foo1, 2))
|
||||
assert(not pcall(debug.upvalueid, foo1, 3))
|
||||
assert(not debug.upvalueid(foo1, 3))
|
||||
assert(debug.upvalueid(foo1, 1) == debug.upvalueid(foo2, 2))
|
||||
assert(debug.upvalueid(foo1, 2) == debug.upvalueid(foo2, 1))
|
||||
assert(debug.upvalueid(foo3, 1))
|
||||
|
||||
+5
-27
@@ -144,10 +144,10 @@ check(function (a,b,c,d) return a..b..c..d end,
|
||||
'MOVE', 'MOVE', 'MOVE', 'MOVE', 'CONCAT', 'RETURN1')
|
||||
|
||||
-- not
|
||||
check(function () return not not nil 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 k3 end, 'LOADBOOL', 'RETURN1')
|
||||
check(function () return not not nil end, 'LOADFALSE', 'RETURN1')
|
||||
check(function () return not not kFalse end, 'LOADFALSE', 'RETURN1')
|
||||
check(function () return not not true end, 'LOADTRUE', 'RETURN1')
|
||||
check(function () return not not k3 end, 'LOADTRUE', 'RETURN1')
|
||||
|
||||
-- direct access to locals
|
||||
check(function ()
|
||||
@@ -194,7 +194,7 @@ check(function ()
|
||||
local a,b
|
||||
a[kTrue] = false
|
||||
end,
|
||||
'LOADNIL', 'LOADBOOL', 'SETTABLE', 'RETURN0')
|
||||
'LOADNIL', 'LOADTRUE', 'SETTABLE', 'RETURN0')
|
||||
|
||||
|
||||
-- equalities
|
||||
@@ -392,28 +392,6 @@ check(function (a, b)
|
||||
end,
|
||||
'TEST', 'JMP', 'TEST', 'JMP', 'ADDI', 'MMBINI', 'JMP', 'RETURN0')
|
||||
|
||||
checkequal(
|
||||
function (a) while a < 10 do a = a + 1 end end,
|
||||
function (a)
|
||||
::loop::
|
||||
if not (a < 10) then goto exit end
|
||||
a = a + 1
|
||||
goto loop
|
||||
::exit::
|
||||
end
|
||||
)
|
||||
|
||||
checkequal(
|
||||
function (a) repeat local x = a + 1; a = x until a > 0 end,
|
||||
function (a)
|
||||
::loop:: do
|
||||
local x = a + 1
|
||||
a = x
|
||||
end
|
||||
if not (a > 0) then goto loop end
|
||||
end
|
||||
)
|
||||
|
||||
checkequal(function () return 6 or true or nil end,
|
||||
function () return k6 or kTrue or kNil end)
|
||||
|
||||
|
||||
+36
-8
@@ -124,6 +124,11 @@ x, a = nil
|
||||
|
||||
|
||||
-- coroutine closing
|
||||
|
||||
local function func2close (f)
|
||||
return setmetatable({}, {__close = f})
|
||||
end
|
||||
|
||||
do
|
||||
-- ok to close a dead coroutine
|
||||
local co = coroutine.create(print)
|
||||
@@ -146,10 +151,6 @@ do
|
||||
-- to-be-closed variables in coroutines
|
||||
local X
|
||||
|
||||
local function func2close (f)
|
||||
return setmetatable({}, {__close = f})
|
||||
end
|
||||
|
||||
co = coroutine.create(function ()
|
||||
local x <close> = func2close(function (self, err)
|
||||
assert(err == nil); X = false
|
||||
@@ -184,7 +185,7 @@ do
|
||||
if not T then
|
||||
warn("@on")
|
||||
else -- test library
|
||||
assert(string.find(_WARN, "200")); _WARN = nil
|
||||
assert(string.find(_WARN, "200")); _WARN = false
|
||||
warn("@normal")
|
||||
end
|
||||
assert(st == false and coroutine.status(co) == "dead" and msg == 111)
|
||||
@@ -192,6 +193,23 @@ do
|
||||
|
||||
end
|
||||
|
||||
do
|
||||
-- <close> versus pcall in coroutines
|
||||
local X = false
|
||||
local Y = false
|
||||
function foo ()
|
||||
local x <close> = func2close(function (self, err)
|
||||
Y = debug.getinfo(2)
|
||||
X = err
|
||||
end)
|
||||
error(43)
|
||||
end
|
||||
co = coroutine.create(function () return pcall(foo) end)
|
||||
local st1, st2, err = coroutine.resume(co)
|
||||
assert(st1 and not st2 and err == 43)
|
||||
assert(X == 43 and Y.name == "pcall")
|
||||
end
|
||||
|
||||
|
||||
-- yielding across C boundaries
|
||||
|
||||
@@ -407,7 +425,8 @@ assert(_G.f() == 12)
|
||||
|
||||
|
||||
if not T then
|
||||
(Message or print)('\n >>> testC not active: skipping yield/hook tests <<<\n')
|
||||
(Message or print)
|
||||
('\n >>> testC not active: skipping coroutine API tests <<<\n')
|
||||
else
|
||||
print "testing yields inside hooks"
|
||||
|
||||
@@ -564,8 +583,17 @@ else
|
||||
c == "ERRRUN" and d == 4)
|
||||
|
||||
|
||||
-- using a main thread as a coroutine
|
||||
-- using a main thread as a coroutine (dubious use!)
|
||||
local state = T.newstate()
|
||||
|
||||
-- check that yielddable is working correctly
|
||||
assert(T.testC(state, "newthread; isyieldable -1; remove 1; return 1"))
|
||||
|
||||
-- main thread is not yieldable
|
||||
assert(not T.testC(state, "rawgeti R 1; isyieldable -1; remove 1; return 1"))
|
||||
|
||||
T.testC(state, "settop 0")
|
||||
|
||||
T.loadlib(state)
|
||||
|
||||
assert(T.doremote(state, [[
|
||||
@@ -763,7 +791,7 @@ assert(run(function() return "a" .. "b" .. a .. "c" .. c .. b .. "x" end,
|
||||
{"concat", "concat", "concat"}) == "ab10chello12x")
|
||||
|
||||
|
||||
do -- a few more tests for comparsion operators
|
||||
do -- a few more tests for comparison operators
|
||||
local mt1 = {
|
||||
__le = function (a,b)
|
||||
coroutine.yield(10)
|
||||
|
||||
+92
-87
@@ -1,67 +1,29 @@
|
||||
-- $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')"
|
||||
print"testing stack overflow detection"
|
||||
|
||||
-- Segmentation faults in these tests probably result from a C-stack
|
||||
-- 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.)
|
||||
-- overflow. To avoid these errors, you should set a smaller limit for
|
||||
-- the use of C stack by Lua, by changing the constant 'LUAI_MAXCCALLS'.
|
||||
-- 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, ...)
|
||||
assert(not s and string.find(err, msg))
|
||||
end
|
||||
|
||||
local count
|
||||
local back = string.rep("\b", 8)
|
||||
local function progress ()
|
||||
count = count + 1
|
||||
local n = string.format("%-8d", count)
|
||||
io.stderr:write(back, n)
|
||||
end
|
||||
|
||||
|
||||
do print("testing simple recursion:")
|
||||
count = 0
|
||||
local function foo ()
|
||||
progress()
|
||||
foo()
|
||||
end
|
||||
checkerror("stack overflow", foo)
|
||||
print("\tfinal count: ", count)
|
||||
end
|
||||
|
||||
|
||||
do print("testing stack overflow in message handling")
|
||||
count = 0
|
||||
local count = 0
|
||||
local function loop (x, y, z)
|
||||
progress()
|
||||
count = count + 1
|
||||
return 1 + loop(x, y, z)
|
||||
end
|
||||
local res, msg = xpcall(loop, loop)
|
||||
assert(msg == "error in error handling")
|
||||
print("\tfinal count: ", count)
|
||||
print("final count: ", count)
|
||||
end
|
||||
|
||||
|
||||
@@ -74,71 +36,114 @@ do print("testing recursion inside pattern matching")
|
||||
end
|
||||
local m = f(80)
|
||||
assert(#m == 80)
|
||||
checkerror("too complex", f, 200000)
|
||||
checkerror("too complex", f, 2000)
|
||||
end
|
||||
|
||||
|
||||
do print("testing stack-overflow in recursive 'gsub'")
|
||||
count = 0
|
||||
local count = 0
|
||||
local function foo ()
|
||||
progress()
|
||||
count = count + 1
|
||||
string.gsub("a", ".", foo)
|
||||
end
|
||||
checkerror("stack overflow", foo)
|
||||
print("\tfinal count: ", count)
|
||||
print("final count: ", count)
|
||||
|
||||
print("testing stack-overflow in recursive 'gsub' with metatables")
|
||||
count = 0
|
||||
local count = 0
|
||||
local t = setmetatable({}, {__index = foo})
|
||||
foo = function ()
|
||||
count = count + 1
|
||||
progress(count)
|
||||
string.gsub("a", ".", t)
|
||||
end
|
||||
checkerror("stack overflow", foo)
|
||||
print("\tfinal count: ", count)
|
||||
print("final 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
|
||||
do -- bug in 5.4.0
|
||||
print("testing limits in coroutines inside deep calls")
|
||||
local count = 0
|
||||
local lim = 1000
|
||||
local function stack (n)
|
||||
if n > 0 then return stack(n - 1) + 1
|
||||
else coroutine.wrap(function ()
|
||||
count = count + 1
|
||||
stack(lim)
|
||||
end)()
|
||||
end
|
||||
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
|
||||
local st, msg = xpcall(stack, function () return "ok" end, lim)
|
||||
assert(not st and msg == "ok")
|
||||
print("final count: ", count)
|
||||
end
|
||||
|
||||
|
||||
do
|
||||
print("nesting of resuming yielded coroutines")
|
||||
local count = 0
|
||||
|
||||
local function body ()
|
||||
coroutine.yield()
|
||||
local f = coroutine.wrap(body)
|
||||
f(); -- start new coroutine (will stop in previous yield)
|
||||
count = count + 1
|
||||
f() -- call it recursively
|
||||
end
|
||||
|
||||
local f = coroutine.wrap(body)
|
||||
f()
|
||||
assert(not pcall(f))
|
||||
print("final count: ", count)
|
||||
end
|
||||
|
||||
|
||||
if T then
|
||||
print("testing stack recovery")
|
||||
local N = 0 -- trace number of calls
|
||||
local LIM = -1 -- will store N just before stack overflow
|
||||
|
||||
-- trace stack size; after stack overflow, it should be
|
||||
-- the maximum allowed stack size.
|
||||
local stack1
|
||||
local dummy
|
||||
|
||||
local function err(msg)
|
||||
assert(string.find(msg, "stack overflow"))
|
||||
local _, stacknow = T.stacklevel()
|
||||
assert(stacknow == stack1 + 200)
|
||||
end
|
||||
|
||||
-- When LIM==-1, the 'if' is not executed, so this function only
|
||||
-- counts and stores the stack limits up to overflow. Then, LIM
|
||||
-- becomes N, and then the 'if' code is run when the stack is
|
||||
-- full. Then, there is a stack overflow inside 'xpcall', after which
|
||||
-- the stack must have been restored back to its maximum normal size.
|
||||
local function f()
|
||||
dummy, stack1 = T.stacklevel()
|
||||
if N == LIM then
|
||||
xpcall(f, err)
|
||||
local _, stacknow = T.stacklevel()
|
||||
assert(stacknow == stack1)
|
||||
return
|
||||
end
|
||||
N = N + 1
|
||||
f()
|
||||
end
|
||||
|
||||
local topB, sizeB -- top and size Before overflow
|
||||
local topA, sizeA -- top and size After overflow
|
||||
topB, sizeB = T.stacklevel()
|
||||
xpcall(f, err)
|
||||
topA, sizeA = T.stacklevel()
|
||||
-- sizes should be comparable
|
||||
assert(topA == topB and sizeA < sizeB * 2)
|
||||
print(string.format("maximum stack size: %d", stack1))
|
||||
LIM = N -- will stop recursion at maximum level
|
||||
N = 0 -- to count again
|
||||
f()
|
||||
print"+"
|
||||
end
|
||||
|
||||
print'OK'
|
||||
|
||||
+33
-1
@@ -119,6 +119,16 @@ else
|
||||
end
|
||||
]], {2,3,4,7})
|
||||
|
||||
test([[
|
||||
local function foo()
|
||||
end
|
||||
foo()
|
||||
A = 1
|
||||
A = 2
|
||||
A = 3
|
||||
]], {2, 3, 2, 4, 5, 6})
|
||||
|
||||
|
||||
test([[--
|
||||
if nil then
|
||||
a=1
|
||||
@@ -649,6 +659,11 @@ t = debug.getinfo(1) -- main
|
||||
assert(t.isvararg == true and t.nparams == 0 and t.nups == 1 and
|
||||
debug.getupvalue(t.func, 1) == "_ENV")
|
||||
|
||||
t = debug.getinfo(math.sin) -- C function
|
||||
assert(t.isvararg == true and t.nparams == 0 and t.nups == 0)
|
||||
|
||||
t = debug.getinfo(string.gmatch("abc", "a")) -- C closure
|
||||
assert(t.isvararg == true and t.nparams == 0 and t.nups > 0)
|
||||
|
||||
|
||||
|
||||
@@ -879,7 +894,7 @@ end
|
||||
|
||||
|
||||
print("testing debug functions on chunk without debug info")
|
||||
prog = [[-- program to be loaded without debug information
|
||||
prog = [[-- program to be loaded without debug information (strip)
|
||||
local debug = require'debug'
|
||||
local a = 12 -- a local variable
|
||||
|
||||
@@ -922,6 +937,23 @@ local f = assert(load(string.dump(load(prog), true)))
|
||||
|
||||
assert(f() == 13)
|
||||
|
||||
do -- bug in 5.4.0: line hooks in stripped code
|
||||
local function foo ()
|
||||
local a = 1
|
||||
local b = 2
|
||||
return b
|
||||
end
|
||||
|
||||
local s = load(string.dump(foo, true))
|
||||
local line = true
|
||||
debug.sethook(function (e, l)
|
||||
assert(e == "line")
|
||||
line = l
|
||||
end, "l")
|
||||
assert(s() == 2); debug.sethook(nil)
|
||||
assert(line == nil) -- hook called withoug debug info for 1st instruction
|
||||
end
|
||||
|
||||
do -- tests for 'source' in binary dumps
|
||||
local prog = [[
|
||||
return function (x)
|
||||
|
||||
+18
-10
@@ -386,25 +386,33 @@ if not _soft then
|
||||
collectgarbage()
|
||||
print"testing stack overflow"
|
||||
C = 0
|
||||
local l = debug.getinfo(1, "l").currentline; function y () C=C+1; y() end
|
||||
-- get line where stack overflow will happen
|
||||
local l = debug.getinfo(1, "l").currentline + 1
|
||||
local function auxy () C=C+1; auxy() end -- produce a stack overflow
|
||||
function y ()
|
||||
collectgarbage("stop") -- avoid running finalizers without stack space
|
||||
auxy()
|
||||
collectgarbage("restart")
|
||||
end
|
||||
|
||||
local function checkstackmessage (m)
|
||||
print("(expected stack overflow after " .. C .. " calls)")
|
||||
C = 0 -- prepare next count
|
||||
return (string.find(m, "stack overflow"))
|
||||
end
|
||||
-- repeated stack overflows (to check stack recovery)
|
||||
assert(checkstackmessage(doit('y()')))
|
||||
print('+')
|
||||
assert(checkstackmessage(doit('y()')))
|
||||
print('+')
|
||||
assert(checkstackmessage(doit('y()')))
|
||||
print('+')
|
||||
|
||||
|
||||
-- error lines in stack overflow
|
||||
C = 0
|
||||
local l1
|
||||
local function g(x)
|
||||
l1 = debug.getinfo(x, "l").currentline; y()
|
||||
l1 = debug.getinfo(x, "l").currentline + 2
|
||||
collectgarbage("stop") -- avoid running finalizers without stack space
|
||||
auxy()
|
||||
collectgarbage("restart")
|
||||
end
|
||||
local _, stackmsg = xpcall(g, debug.traceback, 1)
|
||||
print('+')
|
||||
@@ -530,10 +538,10 @@ local function testrep (init, rep, close, repc, finalresult)
|
||||
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
|
||||
string.find(msg, "stack overflow")))
|
||||
s = init .. string.rep(rep, 500)
|
||||
local res, msg = load(s) -- 500 levels not ok
|
||||
assert(not res and (string.find(msg, "too many") or
|
||||
string.find(msg, "overflow")))
|
||||
end
|
||||
|
||||
testrep("local a; a", ",a", "= 1", ",1") -- multiple assignment
|
||||
|
||||
+13
-1
@@ -182,7 +182,7 @@ assert(~a == a); checkcap{"bnot", a, a}
|
||||
assert(a << 3 == a); checkcap{"shl", a, 3}
|
||||
assert(1.5 >> a == 1.5); checkcap{"shr", 1.5, a}
|
||||
|
||||
-- for comparsion operators, all results are true
|
||||
-- for comparison operators, all results are true
|
||||
assert(5.0 > a); checkcap{"lt", a, 5.0}
|
||||
assert(a >= 10); checkcap{"le", 10, a}
|
||||
assert(a <= -10.0); checkcap{"le", a, -10.0}
|
||||
@@ -305,6 +305,17 @@ t[Set{1,3,5}] = 1
|
||||
assert(t[Set{1,3,5}] == undef)
|
||||
|
||||
|
||||
do -- test invalidating flags
|
||||
local mt = {__eq = true}
|
||||
local a = setmetatable({10}, mt)
|
||||
local b = setmetatable({10}, mt)
|
||||
mt.__eq = nil
|
||||
assert(a ~= b) -- no metamethod
|
||||
mt.__eq = function (x,y) return x[1] == y[1] end
|
||||
assert(a == b) -- must use metamethod now
|
||||
end
|
||||
|
||||
|
||||
if not T then
|
||||
(Message or print)('\n >>> testC not active: skipping tests for \z
|
||||
userdata <<<\n')
|
||||
@@ -325,6 +336,7 @@ else
|
||||
assert(u1 == u3 and u3 == u1 and u1 ~= u2)
|
||||
assert(u2 == u1 and u2 == u3 and u3 == u2)
|
||||
assert(u2 ~= {}) -- different types cannot be equal
|
||||
assert(rawequal(u1, u1) and not rawequal(u1, u3))
|
||||
|
||||
local mirror = {}
|
||||
debug.setmetatable(u3, {__index = mirror, __newindex = mirror})
|
||||
|
||||
@@ -721,6 +721,21 @@ if not _port then
|
||||
progname = '"' .. arg[i + 1] .. '"'
|
||||
end
|
||||
print("testing popen/pclose and execute")
|
||||
-- invalid mode for popen
|
||||
checkerr("invalid mode", io.popen, "cat", "")
|
||||
checkerr("invalid mode", io.popen, "cat", "r+")
|
||||
checkerr("invalid mode", io.popen, "cat", "rw")
|
||||
do -- basic tests for popen
|
||||
local file = os.tmpname()
|
||||
local f = assert(io.popen("cat - > " .. file, "w"))
|
||||
f:write("a line")
|
||||
assert(f:close())
|
||||
local f = assert(io.popen("cat - < " .. file, "r"))
|
||||
assert(f:read("a") == "a line")
|
||||
assert(f:close())
|
||||
assert(os.remove(file))
|
||||
end
|
||||
|
||||
local tests = {
|
||||
-- command, what, code
|
||||
{"ls > /dev/null", "ok"},
|
||||
|
||||
+5
-5
@@ -372,7 +372,7 @@ if T then
|
||||
warn("@on"); warn("@store")
|
||||
collectgarbage()
|
||||
assert(string.find(_WARN, "error in __gc metamethod"))
|
||||
assert(string.match(_WARN, "@(.-)@") == "expected"); _WARN = nil
|
||||
assert(string.match(_WARN, "@(.-)@") == "expected"); _WARN = false
|
||||
for i = 8, 10 do assert(s[i]) end
|
||||
|
||||
for i = 1, 5 do
|
||||
@@ -481,7 +481,7 @@ if T then
|
||||
u = setmetatable({}, {__gc = function () error "@expected error" end})
|
||||
u = nil
|
||||
collectgarbage()
|
||||
assert(string.find(_WARN, "@expected error")); _WARN = nil
|
||||
assert(string.find(_WARN, "@expected error")); _WARN = false
|
||||
warn("@normal")
|
||||
end
|
||||
|
||||
@@ -640,7 +640,7 @@ do
|
||||
assert(getmetatable(o) == tt)
|
||||
-- create new objects during GC
|
||||
local a = 'xuxu'..(10+3)..'joao', {}
|
||||
___Glob = o -- ressurect object!
|
||||
___Glob = o -- ressurrect object!
|
||||
setmetatable({}, tt) -- creates a new one with same metatable
|
||||
print(">>> closing state " .. "<<<\n")
|
||||
end
|
||||
@@ -657,14 +657,14 @@ if T then
|
||||
n = n + 1
|
||||
assert(n == o[1])
|
||||
if n == 1 then
|
||||
_WARN = nil
|
||||
_WARN = false
|
||||
elseif n == 2 then
|
||||
assert(find(_WARN, "@expected warning"))
|
||||
lastmsg = _WARN -- get message from previous error (first 'o')
|
||||
else
|
||||
assert(lastmsg == _WARN) -- subsequent error messages are equal
|
||||
end
|
||||
warn("@store"); _WARN = nil
|
||||
warn("@store"); _WARN = false
|
||||
error"@expected warning"
|
||||
end}
|
||||
for i = 10, 1, -1 do
|
||||
|
||||
@@ -37,6 +37,92 @@ do
|
||||
end
|
||||
|
||||
|
||||
do
|
||||
-- ensure that 'firstold1' is corrected when object is removed from
|
||||
-- the 'allgc' list
|
||||
local function foo () end
|
||||
local old = {10}
|
||||
collectgarbage() -- make 'old' old
|
||||
assert(not T or T.gcage(old) == "old")
|
||||
setmetatable(old, {}) -- new table becomes OLD0 (barrier)
|
||||
assert(not T or T.gcage(getmetatable(old)) == "old0")
|
||||
collectgarbage("step", 0) -- new table becomes OLD1 and firstold1
|
||||
assert(not T or T.gcage(getmetatable(old)) == "old1")
|
||||
setmetatable(getmetatable(old), {__gc = foo}) -- get it out of allgc list
|
||||
collectgarbage("step", 0) -- should not seg. fault
|
||||
end
|
||||
|
||||
|
||||
do -- bug in 5.4.0
|
||||
-- When an object aged OLD1 is finalized, it is moved from the list
|
||||
-- 'finobj' to the *beginning* of the list 'allgc', but that part of the
|
||||
-- list was not being visited by 'markold'.
|
||||
local A = {}
|
||||
A[1] = false -- old anchor for object
|
||||
|
||||
-- obj finalizer
|
||||
local function gcf (obj)
|
||||
A[1] = obj -- anchor object
|
||||
assert(not T or T.gcage(obj) == "old1")
|
||||
obj = nil -- remove it from the stack
|
||||
collectgarbage("step", 0) -- do a young collection
|
||||
print(getmetatable(A[1]).x) -- metatable was collected
|
||||
end
|
||||
|
||||
collectgarbage() -- make A old
|
||||
local obj = {} -- create a new object
|
||||
collectgarbage("step", 0) -- make it a survival
|
||||
assert(not T or T.gcage(obj) == "survival")
|
||||
setmetatable(obj, {__gc = gcf, x = "+"}) -- create its metatable
|
||||
assert(not T or T.gcage(getmetatable(obj)) == "new")
|
||||
obj = nil -- clear object
|
||||
collectgarbage("step", 0) -- will call obj's finalizer
|
||||
end
|
||||
|
||||
|
||||
do -- another bug in 5.4.0
|
||||
local old = {10}
|
||||
collectgarbage() -- make 'old' old
|
||||
local co = coroutine.create(
|
||||
function ()
|
||||
local x = nil
|
||||
local f = function ()
|
||||
return x[1]
|
||||
end
|
||||
x = coroutine.yield(f)
|
||||
coroutine.yield()
|
||||
end
|
||||
)
|
||||
local _, f = coroutine.resume(co) -- create closure over 'x' in coroutine
|
||||
collectgarbage("step", 0) -- make upvalue a survival
|
||||
old[1] = {"hello"} -- 'old' go to grayagain as 'touched1'
|
||||
coroutine.resume(co, {123}) -- its value will be new
|
||||
co = nil
|
||||
collectgarbage("step", 0) -- hit the barrier
|
||||
assert(f() == 123 and old[1][1] == "hello")
|
||||
collectgarbage("step", 0) -- run the collector once more
|
||||
-- make sure old[1] was not collected
|
||||
assert(f() == 123 and old[1][1] == "hello")
|
||||
end
|
||||
|
||||
|
||||
do -- bug introduced in commit 9cf3299fa
|
||||
local t = setmetatable({}, {__mode = "kv"}) -- all-weak table
|
||||
collectgarbage() -- full collection
|
||||
assert(not T or T.gcage(t) == "old")
|
||||
t[1] = {10}
|
||||
assert(not T or (T.gcage(t) == "touched1" and T.gccolor(t) == "gray"))
|
||||
collectgarbage("step", 0) -- minor collection
|
||||
assert(not T or (T.gcage(t) == "touched2" and T.gccolor(t) == "black"))
|
||||
collectgarbage("step", 0) -- minor collection
|
||||
assert(not T or T.gcage(t) == "old") -- t should be black, but it was gray
|
||||
t[1] = {10} -- no barrier here, so t was still old
|
||||
collectgarbage("step", 0) -- minor collection
|
||||
-- t, being old, is ignored by the collection, so it is not cleared
|
||||
assert(t[1] == nil) -- fails with the bug
|
||||
end
|
||||
|
||||
|
||||
if T == nil then
|
||||
(Message or print)('\n >>> testC not active: \z
|
||||
skipping some generational tests <<<\n')
|
||||
@@ -72,6 +158,9 @@ do
|
||||
assert(debug.getuservalue(U).x[1] == 234)
|
||||
end
|
||||
|
||||
-- just to make sure
|
||||
assert(collectgarbage'isrunning')
|
||||
|
||||
|
||||
|
||||
-- just to make sure
|
||||
|
||||
+63
-3
@@ -114,7 +114,7 @@ if rawget(_G, "T") then
|
||||
local t = T.querytab(a)
|
||||
|
||||
for k,_ in pairs(a) do a[k] = undef end
|
||||
collectgarbage() -- restore GC and collect dead fiels in `a'
|
||||
collectgarbage() -- restore GC and collect dead fields in 'a'
|
||||
for i=0,t-1 do
|
||||
local k = querytab(a, i)
|
||||
assert(k == nil or type(k) == 'number' or k == 'alo')
|
||||
@@ -246,6 +246,11 @@ do
|
||||
|
||||
X = false
|
||||
foo = function (x)
|
||||
local _<close> = func2close(function ()
|
||||
-- without errors, enclosing function should be still active when
|
||||
-- __close is called
|
||||
assert(debug.getinfo(2).name == "foo")
|
||||
end)
|
||||
local _<close> = closescope
|
||||
local y = 15
|
||||
return y
|
||||
@@ -264,6 +269,43 @@ do
|
||||
end
|
||||
|
||||
|
||||
-- testing to-be-closed x compile-time constants
|
||||
-- (there were some bugs here in Lua 5.4-rc3, due to a confusion
|
||||
-- between compile levels and stack levels of variables)
|
||||
do
|
||||
local flag = false
|
||||
local x = setmetatable({},
|
||||
{__close = function() assert(flag == false); flag = true end})
|
||||
local y <const> = nil
|
||||
local z <const> = nil
|
||||
do
|
||||
local a <close> = x
|
||||
end
|
||||
assert(flag) -- 'x' must be closed here
|
||||
end
|
||||
|
||||
do
|
||||
-- similar problem, but with implicit close in for loops
|
||||
local flag = false
|
||||
local x = setmetatable({},
|
||||
{__close = function () assert(flag == false); flag = true end})
|
||||
-- return an empty iterator, nil, nil, and 'x' to be closed
|
||||
local function a ()
|
||||
return (function () return nil end), nil, nil, x
|
||||
end
|
||||
local v <const> = 1
|
||||
local w <const> = 1
|
||||
local x <const> = 1
|
||||
local y <const> = 1
|
||||
local z <const> = 1
|
||||
for k in a() do
|
||||
a = k
|
||||
end -- ending the loop must close 'x'
|
||||
assert(flag) -- 'x' must be closed here
|
||||
end
|
||||
|
||||
|
||||
|
||||
do
|
||||
-- calls cannot be tail in the scope of to-be-closed variables
|
||||
local X, Y
|
||||
@@ -300,16 +342,28 @@ local function endwarn ()
|
||||
if not T then
|
||||
warn("@on") -- back to normal
|
||||
else
|
||||
assert(_WARN == nil)
|
||||
assert(_WARN == false)
|
||||
warn("@normal")
|
||||
end
|
||||
end
|
||||
|
||||
|
||||
-- errors inside __close can generate a warning instead of an
|
||||
-- error. This new 'assert' force them to appear.
|
||||
local function assert(cond, msg)
|
||||
if not cond then
|
||||
local line = debug.getinfo(2).currentline or "?"
|
||||
msg = string.format("assertion failed! line %d (%s)\n", line, msg or "")
|
||||
io.stderr:write(msg)
|
||||
os.exit(1)
|
||||
end
|
||||
end
|
||||
|
||||
|
||||
local function checkwarn (msg)
|
||||
if T then
|
||||
assert(string.find(_WARN, msg))
|
||||
_WARN = nil -- reset variable to check next warning
|
||||
_WARN = false -- reset variable to check next warning
|
||||
end
|
||||
end
|
||||
|
||||
@@ -369,11 +423,15 @@ do print("testing errors in __close")
|
||||
|
||||
local x <close> =
|
||||
func2close(function (self, msg)
|
||||
-- after error, 'foo' was discarded, so caller now
|
||||
-- must be 'pcall'
|
||||
assert(debug.getinfo(2).name == "pcall")
|
||||
assert(msg == 4)
|
||||
end)
|
||||
|
||||
local x1 <close> =
|
||||
func2close(function (self, msg)
|
||||
assert(debug.getinfo(2).name == "pcall")
|
||||
checkwarn("@y")
|
||||
assert(msg == 4)
|
||||
error("@x1")
|
||||
@@ -383,6 +441,7 @@ do print("testing errors in __close")
|
||||
|
||||
local y <close> =
|
||||
func2close(function (self, msg)
|
||||
assert(debug.getinfo(2).name == "pcall")
|
||||
assert(msg == 4) -- error in body
|
||||
checkwarn("@z")
|
||||
error("@y")
|
||||
@@ -391,6 +450,7 @@ do print("testing errors in __close")
|
||||
local first = true
|
||||
local z <close> =
|
||||
func2close(function (self, msg)
|
||||
assert(debug.getinfo(2).name == "pcall")
|
||||
-- 'z' close is called once
|
||||
assert(first and msg == 4)
|
||||
first = false
|
||||
|
||||
+30
-3
@@ -287,6 +287,33 @@ RUN([[lua "-e_PROMPT='%s'" -i < %s > %s]], prompt, prog, out)
|
||||
local t = getoutput()
|
||||
assert(string.find(t, prompt .. ".*" .. prompt .. ".*" .. prompt))
|
||||
|
||||
-- using the prompt default
|
||||
prepfile[[ --
|
||||
a = 2
|
||||
]]
|
||||
RUN([[lua -i < %s > %s]], prog, out)
|
||||
local t = getoutput()
|
||||
prompt = "> " -- the default
|
||||
assert(string.find(t, prompt .. ".*" .. prompt .. ".*" .. prompt))
|
||||
|
||||
|
||||
-- non-string prompt
|
||||
prompt =
|
||||
"local C = 0;\z
|
||||
_PROMPT=setmetatable({},{__tostring = function () \z
|
||||
C = C + 1; return C end})"
|
||||
prepfile[[ --
|
||||
a = 2
|
||||
]]
|
||||
RUN([[lua -e "%s" -i < %s > %s]], prompt, prog, out)
|
||||
local t = getoutput()
|
||||
assert(string.find(t, [[
|
||||
1 --
|
||||
2a = 2
|
||||
3
|
||||
]], 1, true))
|
||||
|
||||
|
||||
-- test for error objects
|
||||
prepfile[[
|
||||
debug = require "debug"
|
||||
@@ -393,12 +420,12 @@ if T then -- test library?
|
||||
-- testing 'warn'
|
||||
warn("@store")
|
||||
warn("@123", "456", "789")
|
||||
assert(_WARN == "@123456789"); _WARN = nil
|
||||
assert(_WARN == "@123456789"); _WARN = false
|
||||
|
||||
warn("zip", "", " ", "zap")
|
||||
assert(_WARN == "zip zap"); _WARN = nil
|
||||
assert(_WARN == "zip zap"); _WARN = false
|
||||
warn("ZIP", "", " ", "ZAP")
|
||||
assert(_WARN == "ZIP ZAP"); _WARN = nil
|
||||
assert(_WARN == "ZIP ZAP"); _WARN = false
|
||||
warn("@normal")
|
||||
end
|
||||
|
||||
|
||||
+5
-1
@@ -758,7 +758,7 @@ do -- testing max/min
|
||||
assert(eqT(math.min(maxint, maxint - 1), maxint - 1))
|
||||
assert(eqT(math.min(maxint - 2, maxint, maxint - 1), maxint - 2))
|
||||
end
|
||||
-- testing implicit convertions
|
||||
-- testing implicit conversions
|
||||
|
||||
local a,b = '10', '20'
|
||||
assert(a*b == 200 and a+b == 30 and a-b == -10 and a/b == 0.5 and -b == -20)
|
||||
@@ -960,7 +960,10 @@ do
|
||||
aux(-10,0)
|
||||
aux(1, 6)
|
||||
aux(1, 2)
|
||||
aux(1, 13)
|
||||
aux(1, 31)
|
||||
aux(1, 32)
|
||||
aux(1, 33)
|
||||
aux(-10, 10)
|
||||
aux(-10,-10) -- unit set
|
||||
aux(minint, minint) -- unit set
|
||||
@@ -998,6 +1001,7 @@ do
|
||||
end
|
||||
aux(0, maxint)
|
||||
aux(1, maxint)
|
||||
aux(3, maxint // 3)
|
||||
aux(minint, -1)
|
||||
aux(minint // 2, maxint // 2)
|
||||
aux(minint, maxint)
|
||||
|
||||
+35
-1
@@ -76,7 +76,7 @@ end
|
||||
|
||||
-- testing constructor sizes
|
||||
local sizes = {0, 1, 2, 3, 4, 5, 7, 8, 9, 15, 16, 17,
|
||||
30, 31, 32, 33, 34, 500, 1000}
|
||||
30, 31, 32, 33, 34, 254, 255, 256, 500, 1000}
|
||||
|
||||
for _, sa in ipairs(sizes) do -- 'sa' is size of the array part
|
||||
local arr = {"return {"}
|
||||
@@ -88,6 +88,7 @@ for _, sa in ipairs(sizes) do -- 'sa' is size of the array part
|
||||
arr[1 + sa + sh + 1] = "}"
|
||||
local prog = table.concat(arr)
|
||||
local f = assert(load(prog))
|
||||
collectgarbage("stop")
|
||||
f() -- call once to ensure stack space
|
||||
-- make sure table is not resized after being created
|
||||
if sa == 0 or sh == 0 then
|
||||
@@ -97,6 +98,7 @@ for _, sa in ipairs(sizes) do -- 'sa' is size of the array part
|
||||
end
|
||||
local t = f()
|
||||
T.alloccount();
|
||||
collectgarbage("restart")
|
||||
assert(#t == sa)
|
||||
check(t, sa, mp2(sh))
|
||||
end
|
||||
@@ -357,6 +359,38 @@ end
|
||||
assert(n == 5)
|
||||
|
||||
|
||||
do
|
||||
print("testing next x GC of deleted keys")
|
||||
-- bug in 5.4.1
|
||||
local co = coroutine.wrap(function (t)
|
||||
for k, v in pairs(t) do
|
||||
local k1 = next(t) -- all previous keys were deleted
|
||||
assert(k == k1) -- current key is the first in the table
|
||||
t[k] = nil
|
||||
local expected = (type(k) == "table" and k[1] or
|
||||
type(k) == "function" and k() or
|
||||
string.sub(k, 1, 1))
|
||||
assert(expected == v)
|
||||
coroutine.yield(v)
|
||||
end
|
||||
end)
|
||||
local t = {}
|
||||
t[{1}] = 1 -- add several unanchored, collectable keys
|
||||
t[{2}] = 2
|
||||
t[string.rep("a", 50)] = "a" -- long string
|
||||
t[string.rep("b", 50)] = "b"
|
||||
t[{3}] = 3
|
||||
t[string.rep("c", 10)] = "c" -- short string
|
||||
t[function () return 10 end] = 10
|
||||
local count = 7
|
||||
while co(t) do
|
||||
collectgarbage("collect") -- collect dead keys
|
||||
count = count - 1
|
||||
end
|
||||
assert(count == 0 and next(t) == nil) -- traversed the whole table
|
||||
end
|
||||
|
||||
|
||||
local function test (a)
|
||||
assert(not pcall(table.insert, a, 2, 20));
|
||||
table.insert(a, 10); table.insert(a, 2, 20);
|
||||
|
||||
+22
-9
@@ -158,25 +158,38 @@ do -- tests for '%p' format
|
||||
-- not much to test, as C does not specify what '%p' does.
|
||||
-- ("The value of the pointer is converted to a sequence of printing
|
||||
-- characters, in an implementation-defined manner.")
|
||||
local null = string.format("%p", nil)
|
||||
assert(string.format("%p", {}) ~= null)
|
||||
local null = "(null)" -- nulls are formatted by Lua
|
||||
assert(string.format("%p", 4) == null)
|
||||
assert(string.format("%p", true) == null)
|
||||
assert(string.format("%p", nil) == null)
|
||||
assert(string.format("%p", {}) ~= null)
|
||||
assert(string.format("%p", print) ~= null)
|
||||
assert(string.format("%p", coroutine.running()) ~= null)
|
||||
assert(string.format("%p", io.stdin) ~= null)
|
||||
assert(string.format("%p", io.stdin) == string.format("%p", io.stdin))
|
||||
assert(string.format("%p", print) == string.format("%p", print))
|
||||
assert(string.format("%p", print) ~= string.format("%p", assert))
|
||||
|
||||
assert(#string.format("%90p", {}) == 90)
|
||||
assert(#string.format("%-60p", {}) == 60)
|
||||
assert(string.format("%10p", false) == string.rep(" ", 10 - #null) .. null)
|
||||
assert(string.format("%-12p", 1.5) == null .. string.rep(" ", 12 - #null))
|
||||
|
||||
do
|
||||
local t1 = {}; local t2 = {}
|
||||
assert(string.format("%p", t1) ~= string.format("%p", t2))
|
||||
end
|
||||
do -- short strings
|
||||
|
||||
do -- short strings are internalized
|
||||
local s1 = string.rep("a", 10)
|
||||
local s2 = string.rep("a", 10)
|
||||
local s2 = string.rep("aa", 5)
|
||||
assert(string.format("%p", s1) == string.format("%p", s2))
|
||||
end
|
||||
do -- long strings
|
||||
|
||||
do -- long strings aren't internalized
|
||||
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
|
||||
|
||||
x = '"ílo"\n\\'
|
||||
@@ -309,8 +322,8 @@ do print("testing 'format %a %A'")
|
||||
matchhexa(n)
|
||||
end
|
||||
|
||||
assert(string.find(string.format("%A", 0.0), "^0X0%.?0?P%+?0$"))
|
||||
assert(string.find(string.format("%a", -0.0), "^%-0x0%.?0?p%+?0$"))
|
||||
assert(string.find(string.format("%A", 0.0), "^0X0%.?0*P%+?0$"))
|
||||
assert(string.find(string.format("%a", -0.0), "^%-0x0%.?0*p%+?0$"))
|
||||
|
||||
if not _port then -- test inf, -inf, NaN, and -0.0
|
||||
assert(string.find(string.format("%a", 1/0), "^inf"))
|
||||
@@ -425,7 +438,7 @@ else
|
||||
|
||||
-- formats %U, %f, %I already tested elsewhere
|
||||
|
||||
local blen = 400 -- internal buffer length in 'luaO_pushfstring'
|
||||
local blen = 200 -- internal buffer length in 'luaO_pushfstring'
|
||||
|
||||
local function callpfs (op, fmt, n)
|
||||
local x = {T.testC("pushfstring" .. op .. "; return *", fmt, n)}
|
||||
|
||||
+9
-9
@@ -115,17 +115,17 @@ do
|
||||
end
|
||||
|
||||
-- error in initial position for offset
|
||||
checkerror("position out of range", utf8.offset, "abc", 1, 5)
|
||||
checkerror("position out of range", utf8.offset, "abc", 1, -4)
|
||||
checkerror("position out of range", utf8.offset, "", 1, 2)
|
||||
checkerror("position out of range", utf8.offset, "", 1, -1)
|
||||
checkerror("position out of bounds", utf8.offset, "abc", 1, 5)
|
||||
checkerror("position out of bounds", utf8.offset, "abc", 1, -4)
|
||||
checkerror("position out of bounds", utf8.offset, "", 1, 2)
|
||||
checkerror("position out of bounds", utf8.offset, "", 1, -1)
|
||||
checkerror("continuation byte", utf8.offset, "𦧺", 1, 2)
|
||||
checkerror("continuation byte", utf8.offset, "𦧺", 1, 2)
|
||||
checkerror("continuation byte", utf8.offset, "\x80", 1)
|
||||
|
||||
-- error in indices for len
|
||||
checkerror("out of string", utf8.len, "abc", 0, 2)
|
||||
checkerror("out of string", utf8.len, "abc", 1, 4)
|
||||
checkerror("out of bounds", utf8.len, "abc", 0, 2)
|
||||
checkerror("out of bounds", utf8.len, "abc", 1, 4)
|
||||
|
||||
|
||||
local s = "hello World"
|
||||
@@ -140,11 +140,11 @@ do
|
||||
local t = {utf8.codepoint(s,1,#s - 1)}
|
||||
assert(#t == 3 and t[1] == 225 and t[2] == 233 and t[3] == 237)
|
||||
checkerror("invalid UTF%-8 code", utf8.codepoint, s, 1, #s)
|
||||
checkerror("out of range", utf8.codepoint, s, #s + 1)
|
||||
checkerror("out of bounds", utf8.codepoint, s, #s + 1)
|
||||
t = {utf8.codepoint(s, 4, 3)}
|
||||
assert(#t == 0)
|
||||
checkerror("out of range", utf8.codepoint, s, -(#s + 1), 1)
|
||||
checkerror("out of range", utf8.codepoint, s, 1, #s + 1)
|
||||
checkerror("out of bounds", utf8.codepoint, s, -(#s + 1), 1)
|
||||
checkerror("out of bounds", utf8.codepoint, s, 1, #s + 1)
|
||||
-- surrogates
|
||||
assert(utf8.codepoint("\u{D7FF}") == 0xD800 - 1)
|
||||
assert(utf8.codepoint("\u{E000}") == 0xDFFF + 1)
|
||||
|
||||
Reference in New Issue
Block a user