mirror of
https://github.com/lua/lua.git
synced 2026-07-27 08:29:08 +00:00
Compare commits
183 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
6443185167 | ||
|
|
e15f1f2bb7 | ||
|
|
b5c65705ca | ||
|
|
7ca8105a2e | ||
|
|
94689ac3ad | ||
|
|
ab859fe59b | ||
|
|
c4b71b7ba0 | ||
|
|
1de2f31694 | ||
|
|
02bab9fc25 | ||
|
|
5e08b41567 | ||
|
|
cf08915d62 | ||
|
|
c888ae0aea | ||
|
|
d69789da1c | ||
|
|
314745ed84 | ||
|
|
0825cf237d | ||
|
|
f874d37fa2 | ||
|
|
35e01ed21d | ||
|
|
6aabf4b15e | ||
|
|
fa2f294dd1 | ||
|
|
82fae58e25 | ||
|
|
9a77f57edc | ||
|
|
be908a7d4d | ||
|
|
9ede317c70 | ||
|
|
ee645472eb | ||
|
|
413a393e62 | ||
|
|
ba089bcb08 | ||
|
|
1e64c1391f | ||
|
|
b85816b9a8 | ||
|
|
c954db3924 | ||
|
|
14d2803e55 | ||
|
|
7f12bf40c4 | ||
|
|
26be27459b | ||
|
|
cfbe378f90 | ||
|
|
a1089b415a | ||
|
|
f8c4c4fcf2 | ||
|
|
71bc69c2af | ||
|
|
cd56f222b7 | ||
|
|
69b77b6fde | ||
|
|
997f11f543 | ||
|
|
02060b7a37 | ||
|
|
a1f77a234a | ||
|
|
c6cea857a4 | ||
|
|
d61b0c6028 | ||
|
|
196bb94d66 | ||
|
|
603b2c64ad | ||
|
|
4a00f61276 | ||
|
|
42d40581dd | ||
|
|
e435aaabef | ||
|
|
315639d3bb | ||
|
|
c764ca71a6 | ||
|
|
295cde9454 | ||
|
|
f3cfd5bf2b | ||
|
|
8426d9b4d4 | ||
|
|
1f3c6f4534 | ||
|
|
25b143dd34 | ||
|
|
5d708c3f9c | ||
|
|
8dd2c912d2 | ||
|
|
05ac2409ee | ||
|
|
597a53bbc6 | ||
|
|
86ec152433 | ||
|
|
cf613cdc6f | ||
|
|
066e0f93c4 | ||
|
|
0bfc572e51 | ||
|
|
1de95e97ef | ||
|
|
ad3942adba | ||
|
|
48835c76c8 | ||
|
|
6b3e116d44 | ||
|
|
e8deac5a41 | ||
|
|
bfbff3703e | ||
|
|
74d99057a5 | ||
|
|
1fce5bea81 | ||
|
|
3699446aaf | ||
|
|
0e5071b5fb | ||
|
|
87a9573b2e | ||
|
|
deac067ed3 | ||
|
|
2ff3471722 | ||
|
|
9db4bfed6b | ||
|
|
91673a8ec0 | ||
|
|
41871f1803 | ||
|
|
a393ac2554 | ||
|
|
65434b4d1b | ||
|
|
59acd79c05 | ||
|
|
e2c07dcbf7 | ||
|
|
439e45a2f6 | ||
|
|
62fb934427 | ||
|
|
8a32e0aa4a | ||
|
|
dbdc74dc55 | ||
|
|
6a0dace25a | ||
|
|
04e19712a5 | ||
|
|
901d760093 | ||
|
|
c0ed74c1e1 | ||
|
|
fc6c74f100 | ||
|
|
1e6918d553 | ||
|
|
681297187e | ||
|
|
5148954eed | ||
|
|
d205f3a484 | ||
|
|
47cffdc723 | ||
|
|
36de01d988 | ||
|
|
7fbe215808 | ||
|
|
bf10593a3a | ||
|
|
bef250eb8d | ||
|
|
ba81adaad9 | ||
|
|
eadd8c7178 | ||
|
|
014daf43cb | ||
|
|
05b13651f9 | ||
|
|
81c6021fb4 | ||
|
|
a7b8b27dd3 | ||
|
|
511d53a826 | ||
|
|
f5df7f91f7 | ||
|
|
e7803f7dbc | ||
|
|
b7eb21c1ef | ||
|
|
cf23a93d82 | ||
|
|
9a2de786de | ||
|
|
5276973224 | ||
|
|
f9d857a81b | ||
|
|
1537d6680b | ||
|
|
e0260eb2d4 | ||
|
|
5205f073c5 | ||
|
|
31925e4cc2 | ||
|
|
59c88f846d | ||
|
|
c03c527fd2 | ||
|
|
38cc7d40a4 | ||
|
|
bc970005ce | ||
|
|
f79ccdca9b | ||
|
|
4e47f81188 | ||
|
|
c63e5d212b | ||
|
|
dee6433a89 | ||
|
|
2bfa13e520 | ||
|
|
e500892e18 | ||
|
|
949187b049 | ||
|
|
58aa09a0b9 | ||
|
|
1f81baffad | ||
|
|
0e9254dfa0 | ||
|
|
6ccd24eff5 | ||
|
|
d0f34d9137 | ||
|
|
825ac8eca8 | ||
|
|
b07fc10e91 | ||
|
|
cc1692515e | ||
|
|
ce101dcaf7 | ||
|
|
553b37ce4f | ||
|
|
4bd10b6fe8 | ||
|
|
59e565d955 | ||
|
|
6188f3a654 | ||
|
|
7af27ef59d | ||
|
|
0ceada8da9 | ||
|
|
f9d29b0c44 | ||
|
|
409256b784 | ||
|
|
b17178b27a | ||
|
|
e1ceea5674 | ||
|
|
748d6d4e7a | ||
|
|
e2ea3b31c9 | ||
|
|
23051e830a | ||
|
|
f15589f3b0 | ||
|
|
754ca0060f | ||
|
|
c36ced53c9 | ||
|
|
d41c36bf67 | ||
|
|
d9d2904f09 | ||
|
|
65d2294454 | ||
|
|
131e3fd814 | ||
|
|
9d067ab73b | ||
|
|
2f4162bc47 | ||
|
|
ab1aca94e8 | ||
|
|
d282652561 | ||
|
|
58216600eb | ||
|
|
94cbe46511 | ||
|
|
69b71a6919 | ||
|
|
d742a193e5 | ||
|
|
e4a38eb0e8 | ||
|
|
f07de22576 | ||
|
|
52c8679760 | ||
|
|
849b2ecbd2 | ||
|
|
30528049f1 | ||
|
|
9a89fb1c9d | ||
|
|
c23cc86c54 | ||
|
|
171dcd7d74 | ||
|
|
5aa36e894f | ||
|
|
9ecd446141 | ||
|
|
fb172d0a92 | ||
|
|
0085db4596 | ||
|
|
490d42b5f8 | ||
|
|
287b302acb | ||
|
|
5d8ce05b3f | ||
|
|
f83de8e34e |
3
.gitignore
vendored
3
.gitignore
vendored
@@ -10,3 +10,6 @@ testes/time.txt
|
||||
testes/time-debug.txt
|
||||
|
||||
testes/libs/all
|
||||
|
||||
temp
|
||||
lua
|
||||
|
||||
@@ -2,6 +2,6 @@
|
||||
|
||||
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).
|
||||
Please **do not** send pull requests. To report issues, 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).
|
||||
|
||||
2
all
2
all
@@ -1,7 +1,7 @@
|
||||
make -s -j
|
||||
cd testes/libs; make -s
|
||||
cd .. # back to directory 'testes'
|
||||
ulimit -S -s 2000
|
||||
ulimit -S -s 1100
|
||||
if { ../lua -W all.lua; } then
|
||||
echo -e "\n\n final OK!!!!\n\n"
|
||||
else
|
||||
|
||||
349
lapi.c
349
lapi.c
@@ -39,7 +39,7 @@ const char lua_ident[] =
|
||||
|
||||
|
||||
/*
|
||||
** Test for a valid index.
|
||||
** Test for a valid index (one that is not the 'nilvalue').
|
||||
** '!ttisnil(o)' implies 'o != &G(L)->nilvalue', so it is not needed.
|
||||
** However, it covers the most common cases in a faster way.
|
||||
*/
|
||||
@@ -53,44 +53,57 @@ const char lua_ident[] =
|
||||
#define isupvalue(i) ((i) < LUA_REGISTRYINDEX)
|
||||
|
||||
|
||||
/*
|
||||
** Convert an acceptable index to a pointer to its respective value.
|
||||
** Non-valid indices return the special nil value 'G(L)->nilvalue'.
|
||||
*/
|
||||
static TValue *index2value (lua_State *L, int idx) {
|
||||
CallInfo *ci = L->ci;
|
||||
if (idx > 0) {
|
||||
StkId o = ci->func + idx;
|
||||
api_check(L, idx <= L->ci->top - (ci->func + 1), "unacceptable index");
|
||||
if (o >= L->top) return &G(L)->nilvalue;
|
||||
StkId o = ci->func.p + idx;
|
||||
api_check(L, idx <= ci->top.p - (ci->func.p + 1), "unacceptable index");
|
||||
if (o >= L->top.p) return &G(L)->nilvalue;
|
||||
else return s2v(o);
|
||||
}
|
||||
else if (!ispseudo(idx)) { /* negative index */
|
||||
api_check(L, idx != 0 && -idx <= L->top - (ci->func + 1), "invalid index");
|
||||
return s2v(L->top + idx);
|
||||
api_check(L, idx != 0 && -idx <= L->top.p - (ci->func.p + 1),
|
||||
"invalid index");
|
||||
return s2v(L->top.p + idx);
|
||||
}
|
||||
else if (idx == LUA_REGISTRYINDEX)
|
||||
return &G(L)->l_registry;
|
||||
else { /* upvalues */
|
||||
idx = LUA_REGISTRYINDEX - idx;
|
||||
api_check(L, idx <= MAXUPVAL + 1, "upvalue index too large");
|
||||
if (ttislcf(s2v(ci->func))) /* light C function? */
|
||||
return &G(L)->nilvalue; /* it has no upvalues */
|
||||
else {
|
||||
CClosure *func = clCvalue(s2v(ci->func));
|
||||
return (idx <= func->nupvalues) ? &func->upvalue[idx-1] : &G(L)->nilvalue;
|
||||
if (ttisCclosure(s2v(ci->func.p))) { /* C closure? */
|
||||
CClosure *func = clCvalue(s2v(ci->func.p));
|
||||
return (idx <= func->nupvalues) ? &func->upvalue[idx-1]
|
||||
: &G(L)->nilvalue;
|
||||
}
|
||||
else { /* light C function or Lua function (through a hook)?) */
|
||||
api_check(L, ttislcf(s2v(ci->func.p)), "caller not a C function");
|
||||
return &G(L)->nilvalue; /* no upvalues */
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
static StkId index2stack (lua_State *L, int idx) {
|
||||
|
||||
/*
|
||||
** Convert a valid actual index (not a pseudo-index) to its address.
|
||||
*/
|
||||
l_sinline StkId index2stack (lua_State *L, int idx) {
|
||||
CallInfo *ci = L->ci;
|
||||
if (idx > 0) {
|
||||
StkId o = ci->func + idx;
|
||||
api_check(L, o < L->top, "unacceptable index");
|
||||
StkId o = ci->func.p + idx;
|
||||
api_check(L, o < L->top.p, "invalid index");
|
||||
return o;
|
||||
}
|
||||
else { /* non-positive index */
|
||||
api_check(L, idx != 0 && -idx <= L->top - (ci->func + 1), "invalid index");
|
||||
api_check(L, idx != 0 && -idx <= L->top.p - (ci->func.p + 1),
|
||||
"invalid index");
|
||||
api_check(L, !ispseudo(idx), "invalid index");
|
||||
return L->top + idx;
|
||||
return L->top.p + idx;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -101,17 +114,12 @@ LUA_API int lua_checkstack (lua_State *L, int n) {
|
||||
lua_lock(L);
|
||||
ci = L->ci;
|
||||
api_check(L, n >= 0, "negative 'n'");
|
||||
if (L->stack_last - L->top > n) /* stack large enough? */
|
||||
if (L->stack_last.p - L->top.p > n) /* stack large enough? */
|
||||
res = 1; /* yes; check is OK */
|
||||
else { /* no; need to grow stack */
|
||||
int inuse = cast_int(L->top - L->stack) + EXTRA_STACK;
|
||||
if (inuse > LUAI_MAXSTACK - n) /* can grow without overflow? */
|
||||
res = 0; /* no */
|
||||
else /* try to grow stack */
|
||||
res = luaD_growstack(L, n, 0);
|
||||
}
|
||||
if (res && ci->top < L->top + n)
|
||||
ci->top = L->top + n; /* adjust frame top */
|
||||
else /* need to grow stack */
|
||||
res = luaD_growstack(L, n, 0);
|
||||
if (res && ci->top.p < L->top.p + n)
|
||||
ci->top.p = L->top.p + n; /* adjust frame top */
|
||||
lua_unlock(L);
|
||||
return res;
|
||||
}
|
||||
@@ -123,11 +131,11 @@ LUA_API void lua_xmove (lua_State *from, lua_State *to, int n) {
|
||||
lua_lock(to);
|
||||
api_checknelems(from, n);
|
||||
api_check(from, G(from) == G(to), "moving among independent states");
|
||||
api_check(from, to->ci->top - to->top >= n, "stack overflow");
|
||||
from->top -= n;
|
||||
api_check(from, to->ci->top.p - to->top.p >= n, "stack overflow");
|
||||
from->top.p -= n;
|
||||
for (i = 0; i < n; i++) {
|
||||
setobjs2s(to, to->top, from->top + i);
|
||||
to->top++; /* stack already checked by previous 'api_check' */
|
||||
setobjs2s(to, to->top.p, from->top.p + i);
|
||||
to->top.p++; /* stack already checked by previous 'api_check' */
|
||||
}
|
||||
lua_unlock(to);
|
||||
}
|
||||
@@ -161,35 +169,51 @@ LUA_API lua_Number lua_version (lua_State *L) {
|
||||
LUA_API int lua_absindex (lua_State *L, int idx) {
|
||||
return (idx > 0 || ispseudo(idx))
|
||||
? idx
|
||||
: cast_int(L->top - L->ci->func) + idx;
|
||||
: cast_int(L->top.p - L->ci->func.p) + idx;
|
||||
}
|
||||
|
||||
|
||||
LUA_API int lua_gettop (lua_State *L) {
|
||||
return cast_int(L->top - (L->ci->func + 1));
|
||||
return cast_int(L->top.p - (L->ci->func.p + 1));
|
||||
}
|
||||
|
||||
|
||||
LUA_API void lua_settop (lua_State *L, int idx) {
|
||||
CallInfo *ci;
|
||||
StkId func;
|
||||
StkId func, newtop;
|
||||
ptrdiff_t diff; /* difference for new top */
|
||||
lua_lock(L);
|
||||
ci = L->ci;
|
||||
func = ci->func;
|
||||
func = ci->func.p;
|
||||
if (idx >= 0) {
|
||||
api_check(L, idx <= ci->top - (func + 1), "new top too large");
|
||||
diff = ((func + 1) + idx) - L->top;
|
||||
api_check(L, idx <= ci->top.p - (func + 1), "new top too large");
|
||||
diff = ((func + 1) + idx) - L->top.p;
|
||||
for (; diff > 0; diff--)
|
||||
setnilvalue(s2v(L->top++)); /* clear new slots */
|
||||
setnilvalue(s2v(L->top.p++)); /* clear new slots */
|
||||
}
|
||||
else {
|
||||
api_check(L, -(idx+1) <= (L->top - (func + 1)), "invalid new top");
|
||||
api_check(L, -(idx+1) <= (L->top.p - (func + 1)), "invalid new top");
|
||||
diff = idx + 1; /* will "subtract" index (as it is negative) */
|
||||
}
|
||||
if (diff < 0 && hastocloseCfunc(ci->nresults))
|
||||
luaF_close(L, L->top + diff, LUA_OK);
|
||||
L->top += diff; /* correct top only after closing any upvalue */
|
||||
api_check(L, L->tbclist.p < L->top.p, "previous pop of an unclosed slot");
|
||||
newtop = L->top.p + diff;
|
||||
if (diff < 0 && L->tbclist.p >= newtop) {
|
||||
lua_assert(hastocloseCfunc(ci->nresults));
|
||||
newtop = luaF_close(L, newtop, CLOSEKTOP, 0);
|
||||
}
|
||||
L->top.p = newtop; /* correct top only after closing any upvalue */
|
||||
lua_unlock(L);
|
||||
}
|
||||
|
||||
|
||||
LUA_API void lua_closeslot (lua_State *L, int idx) {
|
||||
StkId level;
|
||||
lua_lock(L);
|
||||
level = index2stack(L, idx);
|
||||
api_check(L, hastocloseCfunc(L->ci->nresults) && L->tbclist.p == level,
|
||||
"no variable to close at given level");
|
||||
level = luaF_close(L, level, CLOSEKTOP, 0);
|
||||
setnilvalue(s2v(level));
|
||||
lua_unlock(L);
|
||||
}
|
||||
|
||||
@@ -200,7 +224,7 @@ LUA_API void lua_settop (lua_State *L, int idx) {
|
||||
** Note that we move(copy) only the value inside the stack.
|
||||
** (We do not move additional fields that may exist.)
|
||||
*/
|
||||
static void reverse (lua_State *L, StkId from, StkId to) {
|
||||
l_sinline void reverse (lua_State *L, StkId from, StkId to) {
|
||||
for (; from < to; from++, to--) {
|
||||
TValue temp;
|
||||
setobj(L, &temp, s2v(from));
|
||||
@@ -217,7 +241,7 @@ static void reverse (lua_State *L, StkId from, StkId to) {
|
||||
LUA_API void lua_rotate (lua_State *L, int idx, int n) {
|
||||
StkId p, t, m;
|
||||
lua_lock(L);
|
||||
t = L->top - 1; /* end of stack segment being rotated */
|
||||
t = L->top.p - 1; /* end of stack segment being rotated */
|
||||
p = index2stack(L, idx); /* start of segment */
|
||||
api_check(L, (n >= 0 ? n : -n) <= (t - p + 1), "invalid 'n'");
|
||||
m = (n >= 0 ? t - n : p - n - 1); /* end of prefix */
|
||||
@@ -236,7 +260,7 @@ LUA_API void lua_copy (lua_State *L, int fromidx, int toidx) {
|
||||
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);
|
||||
luaC_barrier(L, clCvalue(s2v(L->ci->func.p)), fr);
|
||||
/* LUA_REGISTRYINDEX does not need gc barrier
|
||||
(collector revisits it before finishing collection) */
|
||||
lua_unlock(L);
|
||||
@@ -245,7 +269,7 @@ LUA_API void lua_copy (lua_State *L, int fromidx, int toidx) {
|
||||
|
||||
LUA_API void lua_pushvalue (lua_State *L, int idx) {
|
||||
lua_lock(L);
|
||||
setobj2s(L, L->top, index2value(L, idx));
|
||||
setobj2s(L, L->top.p, index2value(L, idx));
|
||||
api_incr_top(L);
|
||||
lua_unlock(L);
|
||||
}
|
||||
@@ -314,12 +338,12 @@ LUA_API void lua_arith (lua_State *L, int op) {
|
||||
api_checknelems(L, 2); /* all other operations expect two operands */
|
||||
else { /* for unary operations, add fake 2nd operand */
|
||||
api_checknelems(L, 1);
|
||||
setobjs2s(L, L->top, L->top - 1);
|
||||
setobjs2s(L, L->top.p, L->top.p - 1);
|
||||
api_incr_top(L);
|
||||
}
|
||||
/* first operand at top - 2, second at top - 1; result go to top - 2 */
|
||||
luaO_arith(L, op, s2v(L->top - 2), s2v(L->top - 1), L->top - 2);
|
||||
L->top--; /* remove second operand */
|
||||
luaO_arith(L, op, s2v(L->top.p - 2), s2v(L->top.p - 1), L->top.p - 2);
|
||||
L->top.p--; /* remove second operand */
|
||||
lua_unlock(L);
|
||||
}
|
||||
|
||||
@@ -345,7 +369,7 @@ LUA_API int lua_compare (lua_State *L, int index1, int index2, int op) {
|
||||
|
||||
|
||||
LUA_API size_t lua_stringtonumber (lua_State *L, const char *s) {
|
||||
size_t sz = luaO_str2num(s, s2v(L->top));
|
||||
size_t sz = luaO_str2num(s, s2v(L->top.p));
|
||||
if (sz != 0)
|
||||
api_incr_top(L);
|
||||
return sz;
|
||||
@@ -420,7 +444,7 @@ LUA_API lua_CFunction lua_tocfunction (lua_State *L, int idx) {
|
||||
}
|
||||
|
||||
|
||||
static void *touserdata (const TValue *o) {
|
||||
l_sinline void *touserdata (const TValue *o) {
|
||||
switch (ttype(o)) {
|
||||
case LUA_TUSERDATA: return getudatamem(uvalue(o));
|
||||
case LUA_TLIGHTUSERDATA: return pvalue(o);
|
||||
@@ -472,7 +496,7 @@ LUA_API const void *lua_topointer (lua_State *L, int idx) {
|
||||
|
||||
LUA_API void lua_pushnil (lua_State *L) {
|
||||
lua_lock(L);
|
||||
setnilvalue(s2v(L->top));
|
||||
setnilvalue(s2v(L->top.p));
|
||||
api_incr_top(L);
|
||||
lua_unlock(L);
|
||||
}
|
||||
@@ -480,7 +504,7 @@ LUA_API void lua_pushnil (lua_State *L) {
|
||||
|
||||
LUA_API void lua_pushnumber (lua_State *L, lua_Number n) {
|
||||
lua_lock(L);
|
||||
setfltvalue(s2v(L->top), n);
|
||||
setfltvalue(s2v(L->top.p), n);
|
||||
api_incr_top(L);
|
||||
lua_unlock(L);
|
||||
}
|
||||
@@ -488,7 +512,7 @@ LUA_API void lua_pushnumber (lua_State *L, lua_Number n) {
|
||||
|
||||
LUA_API void lua_pushinteger (lua_State *L, lua_Integer n) {
|
||||
lua_lock(L);
|
||||
setivalue(s2v(L->top), n);
|
||||
setivalue(s2v(L->top.p), n);
|
||||
api_incr_top(L);
|
||||
lua_unlock(L);
|
||||
}
|
||||
@@ -503,7 +527,7 @@ LUA_API const char *lua_pushlstring (lua_State *L, const char *s, size_t len) {
|
||||
TString *ts;
|
||||
lua_lock(L);
|
||||
ts = (len == 0) ? luaS_new(L, "") : luaS_newlstr(L, s, len);
|
||||
setsvalue2s(L, L->top, ts);
|
||||
setsvalue2s(L, L->top.p, ts);
|
||||
api_incr_top(L);
|
||||
luaC_checkGC(L);
|
||||
lua_unlock(L);
|
||||
@@ -514,11 +538,11 @@ LUA_API const char *lua_pushlstring (lua_State *L, const char *s, size_t len) {
|
||||
LUA_API const char *lua_pushstring (lua_State *L, const char *s) {
|
||||
lua_lock(L);
|
||||
if (s == NULL)
|
||||
setnilvalue(s2v(L->top));
|
||||
setnilvalue(s2v(L->top.p));
|
||||
else {
|
||||
TString *ts;
|
||||
ts = luaS_new(L, s);
|
||||
setsvalue2s(L, L->top, ts);
|
||||
setsvalue2s(L, L->top.p, ts);
|
||||
s = getstr(ts); /* internal copy's address */
|
||||
}
|
||||
api_incr_top(L);
|
||||
@@ -555,7 +579,7 @@ LUA_API const char *lua_pushfstring (lua_State *L, const char *fmt, ...) {
|
||||
LUA_API void lua_pushcclosure (lua_State *L, lua_CFunction fn, int n) {
|
||||
lua_lock(L);
|
||||
if (n == 0) {
|
||||
setfvalue(s2v(L->top), fn);
|
||||
setfvalue(s2v(L->top.p), fn);
|
||||
api_incr_top(L);
|
||||
}
|
||||
else {
|
||||
@@ -564,13 +588,13 @@ LUA_API void lua_pushcclosure (lua_State *L, lua_CFunction fn, int n) {
|
||||
api_check(L, n <= MAXUPVAL, "upvalue index too large");
|
||||
cl = luaF_newCclosure(L, n);
|
||||
cl->f = fn;
|
||||
L->top -= n;
|
||||
L->top.p -= n;
|
||||
while (n--) {
|
||||
setobj2n(L, &cl->upvalue[n], s2v(L->top + n));
|
||||
setobj2n(L, &cl->upvalue[n], s2v(L->top.p + n));
|
||||
/* does not need barrier because closure is white */
|
||||
lua_assert(iswhite(cl));
|
||||
}
|
||||
setclCvalue(L, s2v(L->top), cl);
|
||||
setclCvalue(L, s2v(L->top.p), cl);
|
||||
api_incr_top(L);
|
||||
luaC_checkGC(L);
|
||||
}
|
||||
@@ -581,9 +605,9 @@ 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);
|
||||
if (b)
|
||||
setbtvalue(s2v(L->top));
|
||||
setbtvalue(s2v(L->top.p));
|
||||
else
|
||||
setbfvalue(s2v(L->top));
|
||||
setbfvalue(s2v(L->top.p));
|
||||
api_incr_top(L);
|
||||
lua_unlock(L);
|
||||
}
|
||||
@@ -591,7 +615,7 @@ LUA_API void lua_pushboolean (lua_State *L, int b) {
|
||||
|
||||
LUA_API void lua_pushlightuserdata (lua_State *L, void *p) {
|
||||
lua_lock(L);
|
||||
setpvalue(s2v(L->top), p);
|
||||
setpvalue(s2v(L->top.p), p);
|
||||
api_incr_top(L);
|
||||
lua_unlock(L);
|
||||
}
|
||||
@@ -599,7 +623,7 @@ LUA_API void lua_pushlightuserdata (lua_State *L, void *p) {
|
||||
|
||||
LUA_API int lua_pushthread (lua_State *L) {
|
||||
lua_lock(L);
|
||||
setthvalue(L, s2v(L->top), L);
|
||||
setthvalue(L, s2v(L->top.p), L);
|
||||
api_incr_top(L);
|
||||
lua_unlock(L);
|
||||
return (G(L)->mainthread == L);
|
||||
@@ -612,28 +636,38 @@ LUA_API int lua_pushthread (lua_State *L) {
|
||||
*/
|
||||
|
||||
|
||||
static int auxgetstr (lua_State *L, const TValue *t, const char *k) {
|
||||
l_sinline int auxgetstr (lua_State *L, const TValue *t, const char *k) {
|
||||
const TValue *slot;
|
||||
TString *str = luaS_new(L, k);
|
||||
if (luaV_fastget(L, t, str, slot, luaH_getstr)) {
|
||||
setobj2s(L, L->top, slot);
|
||||
setobj2s(L, L->top.p, slot);
|
||||
api_incr_top(L);
|
||||
}
|
||||
else {
|
||||
setsvalue2s(L, L->top, str);
|
||||
setsvalue2s(L, L->top.p, str);
|
||||
api_incr_top(L);
|
||||
luaV_finishget(L, t, s2v(L->top - 1), L->top - 1, slot);
|
||||
luaV_finishget(L, t, s2v(L->top.p - 1), L->top.p - 1, slot);
|
||||
}
|
||||
lua_unlock(L);
|
||||
return ttype(s2v(L->top - 1));
|
||||
return ttype(s2v(L->top.p - 1));
|
||||
}
|
||||
|
||||
|
||||
/*
|
||||
** Get the global table in the registry. Since all predefined
|
||||
** indices in the registry were inserted right when the registry
|
||||
** was created and never removed, they must always be in the array
|
||||
** part of the registry.
|
||||
*/
|
||||
#define getGtable(L) \
|
||||
(&hvalue(&G(L)->l_registry)->array[LUA_RIDX_GLOBALS - 1])
|
||||
|
||||
|
||||
LUA_API int lua_getglobal (lua_State *L, const char *name) {
|
||||
Table *reg;
|
||||
const TValue *G;
|
||||
lua_lock(L);
|
||||
reg = hvalue(&G(L)->l_registry);
|
||||
return auxgetstr(L, luaH_getint(reg, LUA_RIDX_GLOBALS), name);
|
||||
G = getGtable(L);
|
||||
return auxgetstr(L, G, name);
|
||||
}
|
||||
|
||||
|
||||
@@ -642,13 +676,13 @@ LUA_API int lua_gettable (lua_State *L, int idx) {
|
||||
TValue *t;
|
||||
lua_lock(L);
|
||||
t = index2value(L, idx);
|
||||
if (luaV_fastget(L, t, s2v(L->top - 1), slot, luaH_get)) {
|
||||
setobj2s(L, L->top - 1, slot);
|
||||
if (luaV_fastget(L, t, s2v(L->top.p - 1), slot, luaH_get)) {
|
||||
setobj2s(L, L->top.p - 1, slot);
|
||||
}
|
||||
else
|
||||
luaV_finishget(L, t, s2v(L->top - 1), L->top - 1, slot);
|
||||
luaV_finishget(L, t, s2v(L->top.p - 1), L->top.p - 1, slot);
|
||||
lua_unlock(L);
|
||||
return ttype(s2v(L->top - 1));
|
||||
return ttype(s2v(L->top.p - 1));
|
||||
}
|
||||
|
||||
|
||||
@@ -664,27 +698,27 @@ LUA_API int lua_geti (lua_State *L, int idx, lua_Integer n) {
|
||||
lua_lock(L);
|
||||
t = index2value(L, idx);
|
||||
if (luaV_fastgeti(L, t, n, slot)) {
|
||||
setobj2s(L, L->top, slot);
|
||||
setobj2s(L, L->top.p, slot);
|
||||
}
|
||||
else {
|
||||
TValue aux;
|
||||
setivalue(&aux, n);
|
||||
luaV_finishget(L, t, &aux, L->top, slot);
|
||||
luaV_finishget(L, t, &aux, L->top.p, slot);
|
||||
}
|
||||
api_incr_top(L);
|
||||
lua_unlock(L);
|
||||
return ttype(s2v(L->top - 1));
|
||||
return ttype(s2v(L->top.p - 1));
|
||||
}
|
||||
|
||||
|
||||
static int finishrawget (lua_State *L, const TValue *val) {
|
||||
l_sinline int finishrawget (lua_State *L, const TValue *val) {
|
||||
if (isempty(val)) /* avoid copying empty items to the stack */
|
||||
setnilvalue(s2v(L->top));
|
||||
setnilvalue(s2v(L->top.p));
|
||||
else
|
||||
setobj2s(L, L->top, val);
|
||||
setobj2s(L, L->top.p, val);
|
||||
api_incr_top(L);
|
||||
lua_unlock(L);
|
||||
return ttype(s2v(L->top - 1));
|
||||
return ttype(s2v(L->top.p - 1));
|
||||
}
|
||||
|
||||
|
||||
@@ -701,8 +735,8 @@ LUA_API int lua_rawget (lua_State *L, int idx) {
|
||||
lua_lock(L);
|
||||
api_checknelems(L, 1);
|
||||
t = gettable(L, idx);
|
||||
val = luaH_get(t, s2v(L->top - 1));
|
||||
L->top--; /* remove key */
|
||||
val = luaH_get(t, s2v(L->top.p - 1));
|
||||
L->top.p--; /* remove key */
|
||||
return finishrawget(L, val);
|
||||
}
|
||||
|
||||
@@ -729,7 +763,7 @@ LUA_API void lua_createtable (lua_State *L, int narray, int nrec) {
|
||||
Table *t;
|
||||
lua_lock(L);
|
||||
t = luaH_new(L);
|
||||
sethvalue2s(L, L->top, t);
|
||||
sethvalue2s(L, L->top.p, t);
|
||||
api_incr_top(L);
|
||||
if (narray > 0 || nrec > 0)
|
||||
luaH_resize(L, t, narray, nrec);
|
||||
@@ -756,7 +790,7 @@ LUA_API int lua_getmetatable (lua_State *L, int objindex) {
|
||||
break;
|
||||
}
|
||||
if (mt != NULL) {
|
||||
sethvalue2s(L, L->top, mt);
|
||||
sethvalue2s(L, L->top.p, mt);
|
||||
api_incr_top(L);
|
||||
res = 1;
|
||||
}
|
||||
@@ -772,12 +806,12 @@ LUA_API int lua_getiuservalue (lua_State *L, int idx, int n) {
|
||||
o = index2value(L, idx);
|
||||
api_check(L, ttisfulluserdata(o), "full userdata expected");
|
||||
if (n <= 0 || n > uvalue(o)->nuvalue) {
|
||||
setnilvalue(s2v(L->top));
|
||||
setnilvalue(s2v(L->top.p));
|
||||
t = LUA_TNONE;
|
||||
}
|
||||
else {
|
||||
setobj2s(L, L->top, &uvalue(o)->uv[n - 1].uv);
|
||||
t = ttype(s2v(L->top));
|
||||
setobj2s(L, L->top.p, &uvalue(o)->uv[n - 1].uv);
|
||||
t = ttype(s2v(L->top.p));
|
||||
}
|
||||
api_incr_top(L);
|
||||
lua_unlock(L);
|
||||
@@ -797,24 +831,24 @@ static void auxsetstr (lua_State *L, const TValue *t, const char *k) {
|
||||
TString *str = luaS_new(L, k);
|
||||
api_checknelems(L, 1);
|
||||
if (luaV_fastget(L, t, str, slot, luaH_getstr)) {
|
||||
luaV_finishfastset(L, t, slot, s2v(L->top - 1));
|
||||
L->top--; /* pop value */
|
||||
luaV_finishfastset(L, t, slot, s2v(L->top.p - 1));
|
||||
L->top.p--; /* pop value */
|
||||
}
|
||||
else {
|
||||
setsvalue2s(L, L->top, str); /* push 'str' (to make it a TValue) */
|
||||
setsvalue2s(L, L->top.p, str); /* push 'str' (to make it a TValue) */
|
||||
api_incr_top(L);
|
||||
luaV_finishset(L, t, s2v(L->top - 1), s2v(L->top - 2), slot);
|
||||
L->top -= 2; /* pop value and key */
|
||||
luaV_finishset(L, t, s2v(L->top.p - 1), s2v(L->top.p - 2), slot);
|
||||
L->top.p -= 2; /* pop value and key */
|
||||
}
|
||||
lua_unlock(L); /* lock done by caller */
|
||||
}
|
||||
|
||||
|
||||
LUA_API void lua_setglobal (lua_State *L, const char *name) {
|
||||
Table *reg;
|
||||
const TValue *G;
|
||||
lua_lock(L); /* unlock done in 'auxsetstr' */
|
||||
reg = hvalue(&G(L)->l_registry);
|
||||
auxsetstr(L, luaH_getint(reg, LUA_RIDX_GLOBALS), name);
|
||||
G = getGtable(L);
|
||||
auxsetstr(L, G, name);
|
||||
}
|
||||
|
||||
|
||||
@@ -824,12 +858,12 @@ LUA_API void lua_settable (lua_State *L, int idx) {
|
||||
lua_lock(L);
|
||||
api_checknelems(L, 2);
|
||||
t = index2value(L, idx);
|
||||
if (luaV_fastget(L, t, s2v(L->top - 2), slot, luaH_get)) {
|
||||
luaV_finishfastset(L, t, slot, s2v(L->top - 1));
|
||||
if (luaV_fastget(L, t, s2v(L->top.p - 2), slot, luaH_get)) {
|
||||
luaV_finishfastset(L, t, slot, s2v(L->top.p - 1));
|
||||
}
|
||||
else
|
||||
luaV_finishset(L, t, s2v(L->top - 2), s2v(L->top - 1), slot);
|
||||
L->top -= 2; /* pop index and value */
|
||||
luaV_finishset(L, t, s2v(L->top.p - 2), s2v(L->top.p - 1), slot);
|
||||
L->top.p -= 2; /* pop index and value */
|
||||
lua_unlock(L);
|
||||
}
|
||||
|
||||
@@ -847,35 +881,33 @@ LUA_API void lua_seti (lua_State *L, int idx, lua_Integer n) {
|
||||
api_checknelems(L, 1);
|
||||
t = index2value(L, idx);
|
||||
if (luaV_fastgeti(L, t, n, slot)) {
|
||||
luaV_finishfastset(L, t, slot, s2v(L->top - 1));
|
||||
luaV_finishfastset(L, t, slot, s2v(L->top.p - 1));
|
||||
}
|
||||
else {
|
||||
TValue aux;
|
||||
setivalue(&aux, n);
|
||||
luaV_finishset(L, t, &aux, s2v(L->top - 1), slot);
|
||||
luaV_finishset(L, t, &aux, s2v(L->top.p - 1), slot);
|
||||
}
|
||||
L->top--; /* pop value */
|
||||
L->top.p--; /* pop value */
|
||||
lua_unlock(L);
|
||||
}
|
||||
|
||||
|
||||
static void aux_rawset (lua_State *L, int idx, TValue *key, int n) {
|
||||
Table *t;
|
||||
TValue *slot;
|
||||
lua_lock(L);
|
||||
api_checknelems(L, n);
|
||||
t = gettable(L, idx);
|
||||
slot = luaH_set(L, t, key);
|
||||
setobj2t(L, slot, s2v(L->top - 1));
|
||||
luaH_set(L, t, key, s2v(L->top.p - 1));
|
||||
invalidateTMcache(t);
|
||||
luaC_barrierback(L, obj2gco(t), s2v(L->top - 1));
|
||||
L->top -= n;
|
||||
luaC_barrierback(L, obj2gco(t), s2v(L->top.p - 1));
|
||||
L->top.p -= n;
|
||||
lua_unlock(L);
|
||||
}
|
||||
|
||||
|
||||
LUA_API void lua_rawset (lua_State *L, int idx) {
|
||||
aux_rawset(L, idx, s2v(L->top - 2), 2);
|
||||
aux_rawset(L, idx, s2v(L->top.p - 2), 2);
|
||||
}
|
||||
|
||||
|
||||
@@ -891,9 +923,9 @@ LUA_API void lua_rawseti (lua_State *L, int idx, lua_Integer n) {
|
||||
lua_lock(L);
|
||||
api_checknelems(L, 1);
|
||||
t = gettable(L, idx);
|
||||
luaH_setint(L, t, n, s2v(L->top - 1));
|
||||
luaC_barrierback(L, obj2gco(t), s2v(L->top - 1));
|
||||
L->top--;
|
||||
luaH_setint(L, t, n, s2v(L->top.p - 1));
|
||||
luaC_barrierback(L, obj2gco(t), s2v(L->top.p - 1));
|
||||
L->top.p--;
|
||||
lua_unlock(L);
|
||||
}
|
||||
|
||||
@@ -904,11 +936,11 @@ LUA_API int lua_setmetatable (lua_State *L, int objindex) {
|
||||
lua_lock(L);
|
||||
api_checknelems(L, 1);
|
||||
obj = index2value(L, objindex);
|
||||
if (ttisnil(s2v(L->top - 1)))
|
||||
if (ttisnil(s2v(L->top.p - 1)))
|
||||
mt = NULL;
|
||||
else {
|
||||
api_check(L, ttistable(s2v(L->top - 1)), "table expected");
|
||||
mt = hvalue(s2v(L->top - 1));
|
||||
api_check(L, ttistable(s2v(L->top.p - 1)), "table expected");
|
||||
mt = hvalue(s2v(L->top.p - 1));
|
||||
}
|
||||
switch (ttype(obj)) {
|
||||
case LUA_TTABLE: {
|
||||
@@ -932,7 +964,7 @@ LUA_API int lua_setmetatable (lua_State *L, int objindex) {
|
||||
break;
|
||||
}
|
||||
}
|
||||
L->top--;
|
||||
L->top.p--;
|
||||
lua_unlock(L);
|
||||
return 1;
|
||||
}
|
||||
@@ -948,11 +980,11 @@ LUA_API int lua_setiuservalue (lua_State *L, int idx, int n) {
|
||||
if (!(cast_uint(n) - 1u < cast_uint(uvalue(o)->nuvalue)))
|
||||
res = 0; /* 'n' not in [1, uvalue(o)->nuvalue] */
|
||||
else {
|
||||
setobj(L, &uvalue(o)->uv[n - 1].uv, s2v(L->top - 1));
|
||||
luaC_barrierback(L, gcvalue(o), s2v(L->top - 1));
|
||||
setobj(L, &uvalue(o)->uv[n - 1].uv, s2v(L->top.p - 1));
|
||||
luaC_barrierback(L, gcvalue(o), s2v(L->top.p - 1));
|
||||
res = 1;
|
||||
}
|
||||
L->top--;
|
||||
L->top.p--;
|
||||
lua_unlock(L);
|
||||
return res;
|
||||
}
|
||||
@@ -964,7 +996,8 @@ LUA_API int lua_setiuservalue (lua_State *L, int idx, int n) {
|
||||
|
||||
|
||||
#define checkresults(L,na,nr) \
|
||||
api_check(L, (nr) == LUA_MULTRET || (L->ci->top - L->top >= (nr) - (na)), \
|
||||
api_check(L, (nr) == LUA_MULTRET \
|
||||
|| (L->ci->top.p - L->top.p >= (nr) - (na)), \
|
||||
"results from function overflow current stack size")
|
||||
|
||||
|
||||
@@ -977,7 +1010,7 @@ LUA_API void lua_callk (lua_State *L, int nargs, int nresults,
|
||||
api_checknelems(L, nargs+1);
|
||||
api_check(L, L->status == LUA_OK, "cannot do calls on non-normal thread");
|
||||
checkresults(L, nargs, nresults);
|
||||
func = L->top - (nargs+1);
|
||||
func = L->top.p - (nargs+1);
|
||||
if (k != NULL && yieldable(L)) { /* need to prepare continuation? */
|
||||
L->ci->u.c.k = k; /* save continuation */
|
||||
L->ci->u.c.ctx = ctx; /* save context */
|
||||
@@ -1025,7 +1058,7 @@ LUA_API int lua_pcallk (lua_State *L, int nargs, int nresults, int errfunc,
|
||||
api_check(L, ttisfunction(s2v(o)), "error handler must be a function");
|
||||
func = savestack(L, o);
|
||||
}
|
||||
c.func = L->top - (nargs+1); /* function to be called */
|
||||
c.func = L->top.p - (nargs+1); /* function to be called */
|
||||
if (k == NULL || !yieldable(L)) { /* no continuation or no yieldable? */
|
||||
c.nresults = nresults; /* do a 'conventional' protected call */
|
||||
status = luaD_pcall(L, f_call, &c, savestack(L, c.func), func);
|
||||
@@ -1060,13 +1093,12 @@ LUA_API int lua_load (lua_State *L, lua_Reader reader, void *data,
|
||||
luaZ_init(L, &z, reader, data);
|
||||
status = luaD_protectedparser(L, &z, chunkname, mode);
|
||||
if (status == LUA_OK) { /* no errors? */
|
||||
LClosure *f = clLvalue(s2v(L->top - 1)); /* get newly created function */
|
||||
LClosure *f = clLvalue(s2v(L->top.p - 1)); /* get new function */
|
||||
if (f->nupvalues >= 1) { /* does it have an upvalue? */
|
||||
/* get global table from registry */
|
||||
Table *reg = hvalue(&G(L)->l_registry);
|
||||
const TValue *gt = luaH_getint(reg, LUA_RIDX_GLOBALS);
|
||||
const TValue *gt = getGtable(L);
|
||||
/* set global table as 1st upvalue of 'f' (may be LUA_ENV) */
|
||||
setobj(L, f->upvals[0]->v, gt);
|
||||
setobj(L, f->upvals[0]->v.p, gt);
|
||||
luaC_barrier(L, f->upvals[0], gt);
|
||||
}
|
||||
}
|
||||
@@ -1080,7 +1112,7 @@ LUA_API int lua_dump (lua_State *L, lua_Writer writer, void *data, int strip) {
|
||||
TValue *o;
|
||||
lua_lock(L);
|
||||
api_checknelems(L, 1);
|
||||
o = s2v(L->top - 1);
|
||||
o = s2v(L->top.p - 1);
|
||||
if (isLfunction(o))
|
||||
status = luaU_dump(L, getproto(o), writer, data, strip);
|
||||
else
|
||||
@@ -1101,18 +1133,19 @@ 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;
|
||||
global_State *g = G(L);
|
||||
if (g->gcstp & GCSTPGC) /* internal stop? */
|
||||
return -1; /* all options are invalid when stopped */
|
||||
lua_lock(L);
|
||||
g = G(L);
|
||||
va_start(argp, what);
|
||||
switch (what) {
|
||||
case LUA_GCSTOP: {
|
||||
g->gcrunning = 0;
|
||||
g->gcstp = GCSTPUSR; /* stopped by the user */
|
||||
break;
|
||||
}
|
||||
case LUA_GCRESTART: {
|
||||
luaE_setdebt(g, 0);
|
||||
g->gcrunning = 1;
|
||||
g->gcstp = 0; /* (GCSTPGC must be already zero here) */
|
||||
break;
|
||||
}
|
||||
case LUA_GCCOLLECT: {
|
||||
@@ -1131,8 +1164,8 @@ LUA_API int lua_gc (lua_State *L, int what, ...) {
|
||||
case LUA_GCSTEP: {
|
||||
int data = va_arg(argp, int);
|
||||
l_mem debt = 1; /* =1 to signal that it did an actual step */
|
||||
lu_byte oldrunning = g->gcrunning;
|
||||
g->gcrunning = 1; /* allow GC to run */
|
||||
lu_byte oldstp = g->gcstp;
|
||||
g->gcstp = 0; /* allow GC to run (GCSTPGC must be zero here) */
|
||||
if (data == 0) {
|
||||
luaE_setdebt(g, 0); /* do a basic step */
|
||||
luaC_step(L);
|
||||
@@ -1142,7 +1175,7 @@ LUA_API int lua_gc (lua_State *L, int what, ...) {
|
||||
luaE_setdebt(g, debt);
|
||||
luaC_checkGC(L);
|
||||
}
|
||||
g->gcrunning = oldrunning; /* restore previous state */
|
||||
g->gcstp = oldstp; /* restore previous state */
|
||||
if (debt > 0 && g->gcstate == GCSpause) /* end of cycle? */
|
||||
res = 1; /* signal it */
|
||||
break;
|
||||
@@ -1160,7 +1193,7 @@ LUA_API int lua_gc (lua_State *L, int what, ...) {
|
||||
break;
|
||||
}
|
||||
case LUA_GCISRUNNING: {
|
||||
res = g->gcrunning;
|
||||
res = gcrunning(g);
|
||||
break;
|
||||
}
|
||||
case LUA_GCGEN: {
|
||||
@@ -1205,7 +1238,7 @@ 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);
|
||||
errobj = s2v(L->top.p - 1);
|
||||
api_checknelems(L, 1);
|
||||
/* error object is the memory error message? */
|
||||
if (ttisshrstring(errobj) && eqshrstr(tsvalue(errobj), G(L)->memerrmsg))
|
||||
@@ -1223,12 +1256,12 @@ LUA_API int lua_next (lua_State *L, int idx) {
|
||||
lua_lock(L);
|
||||
api_checknelems(L, 1);
|
||||
t = gettable(L, idx);
|
||||
more = luaH_next(L, t, L->top - 1);
|
||||
more = luaH_next(L, t, L->top.p - 1);
|
||||
if (more) {
|
||||
api_incr_top(L);
|
||||
}
|
||||
else /* no more elements */
|
||||
L->top -= 1; /* remove key */
|
||||
L->top.p -= 1; /* remove key */
|
||||
lua_unlock(L);
|
||||
return more;
|
||||
}
|
||||
@@ -1240,8 +1273,7 @@ LUA_API void lua_toclose (lua_State *L, int idx) {
|
||||
lua_lock(L);
|
||||
o = index2stack(L, idx);
|
||||
nresults = L->ci->nresults;
|
||||
api_check(L, L->openupval == NULL || uplevel(L->openupval) <= o,
|
||||
"marked index below or equal new one");
|
||||
api_check(L, L->tbclist.p < o, "given index below or equal a marked one");
|
||||
luaF_newtbcupval(L, o); /* create new to-be-closed upvalue */
|
||||
if (!hastocloseCfunc(nresults)) /* function not marked yet? */
|
||||
L->ci->nresults = codeNresults(nresults); /* mark it */
|
||||
@@ -1256,7 +1288,7 @@ LUA_API void lua_concat (lua_State *L, int n) {
|
||||
if (n > 0)
|
||||
luaV_concat(L, n);
|
||||
else { /* nothing to concatenate */
|
||||
setsvalue2s(L, L->top, luaS_newlstr(L, "", 0)); /* push empty string */
|
||||
setsvalue2s(L, L->top.p, luaS_newlstr(L, "", 0)); /* push empty string */
|
||||
api_incr_top(L);
|
||||
}
|
||||
luaC_checkGC(L);
|
||||
@@ -1268,7 +1300,7 @@ LUA_API void lua_len (lua_State *L, int idx) {
|
||||
TValue *t;
|
||||
lua_lock(L);
|
||||
t = index2value(L, idx);
|
||||
luaV_objlen(L, L->top, t);
|
||||
luaV_objlen(L, L->top.p, t);
|
||||
api_incr_top(L);
|
||||
lua_unlock(L);
|
||||
}
|
||||
@@ -1313,7 +1345,7 @@ LUA_API void *lua_newuserdatauv (lua_State *L, size_t size, int nuvalue) {
|
||||
lua_lock(L);
|
||||
api_check(L, 0 <= nuvalue && nuvalue < USHRT_MAX, "invalid value");
|
||||
u = luaS_newudata(L, size, nuvalue);
|
||||
setuvalue(L, s2v(L->top), u);
|
||||
setuvalue(L, s2v(L->top.p), u);
|
||||
api_incr_top(L);
|
||||
luaC_checkGC(L);
|
||||
lua_unlock(L);
|
||||
@@ -1339,7 +1371,7 @@ static const char *aux_upvalue (TValue *fi, int n, TValue **val,
|
||||
Proto *p = f->p;
|
||||
if (!(cast_uint(n) - 1u < cast_uint(p->sizeupvalues)))
|
||||
return NULL; /* 'n' not in [1, p->sizeupvalues] */
|
||||
*val = f->upvals[n-1]->v;
|
||||
*val = f->upvals[n-1]->v.p;
|
||||
if (owner) *owner = obj2gco(f->upvals[n - 1]);
|
||||
name = p->upvalues[n-1].name;
|
||||
return (name == NULL) ? "(no name)" : getstr(name);
|
||||
@@ -1355,7 +1387,7 @@ LUA_API const char *lua_getupvalue (lua_State *L, int funcindex, int n) {
|
||||
lua_lock(L);
|
||||
name = aux_upvalue(index2value(L, funcindex), n, &val, NULL);
|
||||
if (name) {
|
||||
setobj2s(L, L->top, val);
|
||||
setobj2s(L, L->top.p, val);
|
||||
api_incr_top(L);
|
||||
}
|
||||
lua_unlock(L);
|
||||
@@ -1373,8 +1405,8 @@ LUA_API const char *lua_setupvalue (lua_State *L, int funcindex, int n) {
|
||||
api_checknelems(L, 1);
|
||||
name = aux_upvalue(fi, n, &val, &owner);
|
||||
if (name) {
|
||||
L->top--;
|
||||
setobj(L, val, s2v(L->top));
|
||||
L->top.p--;
|
||||
setobj(L, val, s2v(L->top.p));
|
||||
luaC_barrier(L, owner, val);
|
||||
}
|
||||
lua_unlock(L);
|
||||
@@ -1383,13 +1415,16 @@ LUA_API const char *lua_setupvalue (lua_State *L, int funcindex, int n) {
|
||||
|
||||
|
||||
static UpVal **getupvalref (lua_State *L, int fidx, int n, LClosure **pf) {
|
||||
static 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;
|
||||
}
|
||||
|
||||
|
||||
@@ -1401,11 +1436,14 @@ LUA_API void *lua_upvalueid (lua_State *L, int fidx, int n) {
|
||||
}
|
||||
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;
|
||||
}
|
||||
}
|
||||
@@ -1417,6 +1455,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
lapi.h
19
lapi.h
@@ -12,23 +12,26 @@
|
||||
#include "lstate.h"
|
||||
|
||||
|
||||
/* Increments 'L->top', checking for stack overflows */
|
||||
#define api_incr_top(L) {L->top++; api_check(L, L->top <= L->ci->top, \
|
||||
"stack overflow");}
|
||||
/* Increments 'L->top.p', checking for stack overflows */
|
||||
#define api_incr_top(L) {L->top.p++; \
|
||||
api_check(L, L->top.p <= L->ci->top.p, \
|
||||
"stack overflow");}
|
||||
|
||||
|
||||
/*
|
||||
** If a call returns too many multiple returns, the callee may not have
|
||||
** stack space to accommodate all results. In this case, this macro
|
||||
** increases its stack space ('L->ci->top').
|
||||
** increases its stack space ('L->ci->top.p').
|
||||
*/
|
||||
#define adjustresults(L,nres) \
|
||||
{ if ((nres) <= LUA_MULTRET && L->ci->top < L->top) L->ci->top = L->top; }
|
||||
{ if ((nres) <= LUA_MULTRET && L->ci->top.p < L->top.p) \
|
||||
L->ci->top.p = L->top.p; }
|
||||
|
||||
|
||||
/* Ensure the stack has at least 'n' elements */
|
||||
#define api_checknelems(L,n) api_check(L, (n) < (L->top - L->ci->func), \
|
||||
"not enough elements in the stack")
|
||||
#define api_checknelems(L,n) \
|
||||
api_check(L, (n) < (L->top.p - L->ci->func.p), \
|
||||
"not enough elements in the stack")
|
||||
|
||||
|
||||
/*
|
||||
@@ -42,6 +45,8 @@
|
||||
|
||||
#define hastocloseCfunc(n) ((n) < LUA_MULTRET)
|
||||
|
||||
/* Map [-1, inf) (range of 'nresults') into (-inf, -2] */
|
||||
#define codeNresults(n) (-(n) - 3)
|
||||
#define decodeNresults(n) (-(n) - 3)
|
||||
|
||||
#endif
|
||||
|
||||
203
lauxlib.c
203
lauxlib.c
@@ -190,7 +190,7 @@ LUALIB_API int luaL_argerror (lua_State *L, int arg, const char *extramsg) {
|
||||
}
|
||||
|
||||
|
||||
int luaL_typeerror (lua_State *L, int arg, const char *tname) {
|
||||
LUALIB_API int luaL_typeerror (lua_State *L, int arg, const char *tname) {
|
||||
const char *msg;
|
||||
const char *typearg; /* name for the type of the actual argument */
|
||||
if (luaL_getmetafield(L, arg, "__name") == LUA_TSTRING)
|
||||
@@ -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 != 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);
|
||||
@@ -378,7 +378,7 @@ LUALIB_API int luaL_checkoption (lua_State *L, int arg, const char *def,
|
||||
** but without 'msg'.)
|
||||
*/
|
||||
LUALIB_API void luaL_checkstack (lua_State *L, int space, const char *msg) {
|
||||
if (!lua_checkstack(L, space)) {
|
||||
if (l_unlikely(!lua_checkstack(L, space))) {
|
||||
if (msg)
|
||||
luaL_error(L, "stack overflow (%s)", msg);
|
||||
else
|
||||
@@ -388,20 +388,20 @@ LUALIB_API void luaL_checkstack (lua_State *L, int space, const char *msg) {
|
||||
|
||||
|
||||
LUALIB_API void luaL_checktype (lua_State *L, int arg, int t) {
|
||||
if (lua_type(L, arg) != t)
|
||||
if (l_unlikely(lua_type(L, arg) != t))
|
||||
tag_error(L, arg, t);
|
||||
}
|
||||
|
||||
|
||||
LUALIB_API void luaL_checkany (lua_State *L, int arg) {
|
||||
if (lua_type(L, arg) == LUA_TNONE)
|
||||
if (l_unlikely(lua_type(L, arg) == LUA_TNONE))
|
||||
luaL_argerror(L, arg, "value expected");
|
||||
}
|
||||
|
||||
|
||||
LUALIB_API const char *luaL_checklstring (lua_State *L, int arg, size_t *len) {
|
||||
const char *s = lua_tolstring(L, arg, len);
|
||||
if (!s) tag_error(L, arg, LUA_TSTRING);
|
||||
if (l_unlikely(!s)) tag_error(L, arg, LUA_TSTRING);
|
||||
return s;
|
||||
}
|
||||
|
||||
@@ -420,7 +420,7 @@ LUALIB_API const char *luaL_optlstring (lua_State *L, int arg,
|
||||
LUALIB_API lua_Number luaL_checknumber (lua_State *L, int arg) {
|
||||
int isnum;
|
||||
lua_Number d = lua_tonumberx(L, arg, &isnum);
|
||||
if (!isnum)
|
||||
if (l_unlikely(!isnum))
|
||||
tag_error(L, arg, LUA_TNUMBER);
|
||||
return d;
|
||||
}
|
||||
@@ -442,7 +442,7 @@ static void interror (lua_State *L, int arg) {
|
||||
LUALIB_API lua_Integer luaL_checkinteger (lua_State *L, int arg) {
|
||||
int isnum;
|
||||
lua_Integer d = lua_tointegerx(L, arg, &isnum);
|
||||
if (!isnum) {
|
||||
if (l_unlikely(!isnum)) {
|
||||
interror(L, arg);
|
||||
}
|
||||
return d;
|
||||
@@ -475,7 +475,7 @@ 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? */
|
||||
if (l_unlikely(temp == NULL && newsize > 0)) { /* allocation error? */
|
||||
lua_pushliteral(L, "not enough memory");
|
||||
lua_error(L); /* raise a memory error */
|
||||
}
|
||||
@@ -515,15 +515,25 @@ static void newbox (lua_State *L) {
|
||||
#define buffonstack(B) ((B)->b != (B)->init.b)
|
||||
|
||||
|
||||
/*
|
||||
** Whenever buffer is accessed, slot 'idx' must either be a box (which
|
||||
** cannot be NULL) or it is a placeholder for the buffer.
|
||||
*/
|
||||
#define checkbufferlevel(B,idx) \
|
||||
lua_assert(buffonstack(B) ? lua_touserdata(B->L, idx) != NULL \
|
||||
: lua_touserdata(B->L, idx) == (void*)B)
|
||||
|
||||
|
||||
/*
|
||||
** Compute new size for buffer 'B', enough to accommodate extra 'sz'
|
||||
** bytes.
|
||||
** bytes. (The test for "not big enough" also gets the case when the
|
||||
** computation of 'newsize' overflows.)
|
||||
*/
|
||||
static size_t newbuffsize (luaL_Buffer *B, size_t sz) {
|
||||
size_t newsize = B->size * 2; /* double buffer size */
|
||||
if (MAX_SIZET - sz < B->n) /* overflow in (B->n + sz)? */
|
||||
size_t newsize = (B->size / 2) * 3; /* buffer size * 1.5 */
|
||||
if (l_unlikely(MAX_SIZET - sz < B->n)) /* overflow in (B->n + sz)? */
|
||||
return luaL_error(B->L, "buffer too large");
|
||||
if (newsize < B->n + sz) /* double is not big enough? */
|
||||
if (newsize < B->n + sz) /* not big enough? */
|
||||
newsize = B->n + sz;
|
||||
return newsize;
|
||||
}
|
||||
@@ -531,10 +541,11 @@ static size_t newbuffsize (luaL_Buffer *B, size_t sz) {
|
||||
|
||||
/*
|
||||
** Returns a pointer to a free area with at least 'sz' bytes in buffer
|
||||
** 'B'. 'boxidx' is the relative position in the stack where the
|
||||
** buffer's box is or should be.
|
||||
** 'B'. 'boxidx' is the relative position in the stack where is the
|
||||
** buffer's box or its placeholder.
|
||||
*/
|
||||
static char *prepbuffsize (luaL_Buffer *B, size_t sz, int boxidx) {
|
||||
checkbufferlevel(B, boxidx);
|
||||
if (B->size - B->n >= sz) /* enough space? */
|
||||
return B->b + B->n;
|
||||
else {
|
||||
@@ -545,10 +556,9 @@ static char *prepbuffsize (luaL_Buffer *B, size_t sz, int boxidx) {
|
||||
if (buffonstack(B)) /* buffer already has a box? */
|
||||
newbuff = (char *)resizebox(L, boxidx, newsize); /* resize it */
|
||||
else { /* no box yet */
|
||||
lua_pushnil(L); /* reserve slot for final result */
|
||||
lua_remove(L, boxidx); /* remove placeholder */
|
||||
newbox(L); /* create a new box */
|
||||
/* move box (and slot) to its intended position */
|
||||
lua_rotate(L, boxidx - 1, 2);
|
||||
lua_insert(L, boxidx); /* move box to its intended position */
|
||||
lua_toclose(L, boxidx);
|
||||
newbuff = (char *)resizebox(L, boxidx, newsize);
|
||||
memcpy(newbuff, B->b, B->n * sizeof(char)); /* copy original content */
|
||||
@@ -583,11 +593,11 @@ LUALIB_API void luaL_addstring (luaL_Buffer *B, const char *s) {
|
||||
|
||||
LUALIB_API void luaL_pushresult (luaL_Buffer *B) {
|
||||
lua_State *L = B->L;
|
||||
checkbufferlevel(B, -1);
|
||||
lua_pushlstring(L, B->b, B->n);
|
||||
if (buffonstack(B)) {
|
||||
lua_copy(L, -1, -3); /* move string to reserved slot */
|
||||
lua_pop(L, 2); /* pop string and box (closing the box) */
|
||||
}
|
||||
if (buffonstack(B))
|
||||
lua_closeslot(L, -2); /* close the box */
|
||||
lua_remove(L, -2); /* remove box or placeholder from the stack */
|
||||
}
|
||||
|
||||
|
||||
@@ -602,7 +612,7 @@ LUALIB_API void luaL_pushresultsize (luaL_Buffer *B, size_t sz) {
|
||||
** box (if existent) is not on the top of the stack. So, instead of
|
||||
** calling 'luaL_addlstring', it replicates the code using -2 as the
|
||||
** last argument to 'prepbuffsize', signaling that the box is (or will
|
||||
** be) bellow the string being added to the buffer. (Box creation can
|
||||
** be) below the string being added to the buffer. (Box creation can
|
||||
** trigger an emergency GC, so we should not remove the string from the
|
||||
** stack before we have the space guaranteed.)
|
||||
*/
|
||||
@@ -622,6 +632,7 @@ LUALIB_API void luaL_buffinit (lua_State *L, luaL_Buffer *B) {
|
||||
B->b = B->init.b;
|
||||
B->n = 0;
|
||||
B->size = LUAL_BUFFERSIZE;
|
||||
lua_pushlightuserdata(L, (void*)B); /* push placeholder */
|
||||
}
|
||||
|
||||
|
||||
@@ -639,10 +650,14 @@ LUALIB_API char *luaL_buffinitsize (lua_State *L, luaL_Buffer *B, size_t sz) {
|
||||
** =======================================================
|
||||
*/
|
||||
|
||||
/* index of free-list header */
|
||||
#define freelist 0
|
||||
|
||||
/* index of free-list header (after the predefined values) */
|
||||
#define freelist (LUA_RIDX_LAST + 1)
|
||||
|
||||
/*
|
||||
** The previously freed references form a linked list:
|
||||
** t[freelist] is the index of a first free index, or zero if list is
|
||||
** empty; t[t[freelist]] is the index of the second element; etc.
|
||||
*/
|
||||
LUALIB_API int luaL_ref (lua_State *L, int t) {
|
||||
int ref;
|
||||
if (lua_isnil(L, -1)) {
|
||||
@@ -650,9 +665,16 @@ LUALIB_API int luaL_ref (lua_State *L, int t) {
|
||||
return LUA_REFNIL; /* 'nil' has a unique fixed reference */
|
||||
}
|
||||
t = lua_absindex(L, t);
|
||||
lua_rawgeti(L, t, freelist); /* get first free element */
|
||||
ref = (int)lua_tointeger(L, -1); /* ref = t[freelist] */
|
||||
lua_pop(L, 1); /* remove it from stack */
|
||||
if (lua_rawgeti(L, t, freelist) == LUA_TNIL) { /* first access? */
|
||||
ref = 0; /* list is empty */
|
||||
lua_pushinteger(L, 0); /* initialize as an empty list */
|
||||
lua_rawseti(L, t, freelist); /* ref = t[freelist] = 0 */
|
||||
}
|
||||
else { /* already initialized */
|
||||
lua_assert(lua_isinteger(L, -1));
|
||||
ref = (int)lua_tointeger(L, -1); /* ref = t[freelist] */
|
||||
}
|
||||
lua_pop(L, 1); /* remove element from stack */
|
||||
if (ref != 0) { /* any free element? */
|
||||
lua_rawgeti(L, t, ref); /* remove it from list */
|
||||
lua_rawseti(L, t, freelist); /* (t[freelist] = t[ref]) */
|
||||
@@ -668,6 +690,7 @@ LUALIB_API void luaL_unref (lua_State *L, int t, int ref) {
|
||||
if (ref >= 0) {
|
||||
t = lua_absindex(L, t);
|
||||
lua_rawgeti(L, t, freelist);
|
||||
lua_assert(lua_isinteger(L, -1));
|
||||
lua_rawseti(L, t, ref); /* t[ref] = t[freelist] */
|
||||
lua_pushinteger(L, ref);
|
||||
lua_rawseti(L, t, freelist); /* t[freelist] = ref */
|
||||
@@ -717,17 +740,18 @@ static int errfile (lua_State *L, const char *what, int fnameindex) {
|
||||
}
|
||||
|
||||
|
||||
static int skipBOM (LoadF *lf) {
|
||||
const char *p = "\xEF\xBB\xBF"; /* UTF-8 BOM mark */
|
||||
int c;
|
||||
lf->n = 0;
|
||||
do {
|
||||
c = getc(lf->f);
|
||||
if (c == EOF || c != *(const unsigned char *)p++) return c;
|
||||
lf->buff[lf->n++] = c; /* to be read by the parser */
|
||||
} while (*p != '\0');
|
||||
lf->n = 0; /* prefix matched; discard it */
|
||||
return getc(lf->f); /* return next character */
|
||||
/*
|
||||
** Skip an optional BOM at the start of a stream. If there is an
|
||||
** incomplete BOM (the first character is correct but the rest is
|
||||
** not), returns the first character anyway to force an error
|
||||
** (as no chunk can start with 0xEF).
|
||||
*/
|
||||
static int skipBOM (FILE *f) {
|
||||
int c = getc(f); /* read first character */
|
||||
if (c == 0xEF && getc(f) == 0xBB && getc(f) == 0xBF) /* correct BOM? */
|
||||
return getc(f); /* ignore BOM and return next char */
|
||||
else /* no (valid) BOM */
|
||||
return c; /* return first character */
|
||||
}
|
||||
|
||||
|
||||
@@ -738,13 +762,13 @@ static int skipBOM (LoadF *lf) {
|
||||
** first "valid" character of the file (after the optional BOM and
|
||||
** a first-line comment).
|
||||
*/
|
||||
static int skipcomment (LoadF *lf, int *cp) {
|
||||
int c = *cp = skipBOM(lf);
|
||||
static int skipcomment (FILE *f, int *cp) {
|
||||
int c = *cp = skipBOM(f);
|
||||
if (c == '#') { /* first line is a comment (Unix exec. file)? */
|
||||
do { /* skip first line */
|
||||
c = getc(lf->f);
|
||||
c = getc(f);
|
||||
} while (c != EOF && c != '\n');
|
||||
*cp = getc(lf->f); /* skip end-of-line, if present */
|
||||
*cp = getc(f); /* next character after comment, if present */
|
||||
return 1; /* there was a comment */
|
||||
}
|
||||
else return 0; /* no comment */
|
||||
@@ -766,12 +790,16 @@ LUALIB_API int luaL_loadfilex (lua_State *L, const char *filename,
|
||||
lf.f = fopen(filename, "r");
|
||||
if (lf.f == NULL) return errfile(L, "open", fnameindex);
|
||||
}
|
||||
if (skipcomment(&lf, &c)) /* read initial portion */
|
||||
lf.buff[lf.n++] = '\n'; /* add line to correct line numbers */
|
||||
if (c == LUA_SIGNATURE[0] && filename) { /* binary file? */
|
||||
lf.f = freopen(filename, "rb", lf.f); /* reopen in binary mode */
|
||||
if (lf.f == NULL) return errfile(L, "reopen", fnameindex);
|
||||
skipcomment(&lf, &c); /* re-read initial portion */
|
||||
lf.n = 0;
|
||||
if (skipcomment(lf.f, &c)) /* read initial portion */
|
||||
lf.buff[lf.n++] = '\n'; /* add newline to correct line numbers */
|
||||
if (c == LUA_SIGNATURE[0]) { /* binary file? */
|
||||
lf.n = 0; /* remove possible newline */
|
||||
if (filename) { /* "real" file? */
|
||||
lf.f = freopen(filename, "rb", lf.f); /* reopen in binary mode */
|
||||
if (lf.f == NULL) return errfile(L, "reopen", fnameindex);
|
||||
skipcomment(lf.f, &c); /* re-read initial portion */
|
||||
}
|
||||
}
|
||||
if (c != EOF)
|
||||
lf.buff[lf.n++] = c; /* 'c' is the first character of the stream */
|
||||
@@ -851,7 +879,7 @@ LUALIB_API lua_Integer luaL_len (lua_State *L, int idx) {
|
||||
int isnum;
|
||||
lua_len(L, idx);
|
||||
l = lua_tointegerx(L, -1, &isnum);
|
||||
if (!isnum)
|
||||
if (l_unlikely(!isnum))
|
||||
luaL_error(L, "object length is not an integer");
|
||||
lua_pop(L, 1); /* remove object */
|
||||
return l;
|
||||
@@ -859,6 +887,7 @@ LUALIB_API lua_Integer luaL_len (lua_State *L, int idx) {
|
||||
|
||||
|
||||
LUALIB_API const char *luaL_tolstring (lua_State *L, int idx, size_t *len) {
|
||||
idx = lua_absindex(L,idx);
|
||||
if (luaL_callmeta(L, idx, "__tostring")) { /* metafield? */
|
||||
if (!lua_isstring(L, -1))
|
||||
luaL_error(L, "'__tostring' must return a string");
|
||||
@@ -1006,43 +1035,67 @@ static int panic (lua_State *L) {
|
||||
|
||||
|
||||
/*
|
||||
** 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 */
|
||||
if (l_likely(L)) {
|
||||
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;
|
||||
}
|
||||
|
||||
31
lauxlib.h
31
lauxlib.h
@@ -12,6 +12,7 @@
|
||||
#include <stddef.h>
|
||||
#include <stdio.h>
|
||||
|
||||
#include "luaconf.h"
|
||||
#include "lua.h"
|
||||
|
||||
|
||||
@@ -101,7 +102,7 @@ LUALIB_API lua_State *(luaL_newstate) (void);
|
||||
|
||||
LUALIB_API lua_Integer (luaL_len) (lua_State *L, int idx);
|
||||
|
||||
LUALIB_API void luaL_addgsub (luaL_Buffer *b, const char *s,
|
||||
LUALIB_API void (luaL_addgsub) (luaL_Buffer *b, const char *s,
|
||||
const char *p, const char *r);
|
||||
LUALIB_API const char *(luaL_gsub) (lua_State *L, const char *s,
|
||||
const char *p, const char *r);
|
||||
@@ -130,10 +131,10 @@ LUALIB_API void (luaL_requiref) (lua_State *L, const char *modname,
|
||||
(luaL_checkversion(L), luaL_newlibtable(L,l), luaL_setfuncs(L,l,0))
|
||||
|
||||
#define luaL_argcheck(L, cond,arg,extramsg) \
|
||||
((void)((cond) || luaL_argerror(L, (arg), (extramsg))))
|
||||
((void)(luai_likely(cond) || luaL_argerror(L, (arg), (extramsg))))
|
||||
|
||||
#define luaL_argexpected(L,cond,arg,tname) \
|
||||
((void)((cond) || luaL_typeerror(L, (arg), (tname))))
|
||||
((void)(luai_likely(cond) || luaL_typeerror(L, (arg), (tname))))
|
||||
|
||||
#define luaL_checkstring(L,n) (luaL_checklstring(L, (n), NULL))
|
||||
#define luaL_optstring(L,n,d) (luaL_optlstring(L, (n), (d), NULL))
|
||||
@@ -153,10 +154,34 @@ LUALIB_API void (luaL_requiref) (lua_State *L, const char *modname,
|
||||
#define luaL_loadbuffer(L,s,sz,n) luaL_loadbufferx(L,s,sz,n,NULL)
|
||||
|
||||
|
||||
/*
|
||||
** Perform arithmetic operations on lua_Integer values with wrap-around
|
||||
** semantics, as the Lua core does.
|
||||
*/
|
||||
#define luaL_intop(op,v1,v2) \
|
||||
((lua_Integer)((lua_Unsigned)(v1) op (lua_Unsigned)(v2)))
|
||||
|
||||
|
||||
/* push the value used to represent failure/error */
|
||||
#define luaL_pushfail(L) lua_pushnil(L)
|
||||
|
||||
|
||||
/*
|
||||
** Internal assertions for in-house debugging
|
||||
*/
|
||||
#if !defined(lua_assert)
|
||||
|
||||
#if defined LUAI_ASSERT
|
||||
#include <assert.h>
|
||||
#define lua_assert(c) assert(c)
|
||||
#else
|
||||
#define lua_assert(c) ((void)0)
|
||||
#endif
|
||||
|
||||
#endif
|
||||
|
||||
|
||||
|
||||
/*
|
||||
** {======================================================
|
||||
** Generic Buffer manipulation
|
||||
|
||||
40
lbaselib.c
40
lbaselib.c
@@ -138,7 +138,7 @@ static int luaB_setmetatable (lua_State *L) {
|
||||
int t = lua_type(L, 2);
|
||||
luaL_checktype(L, 1, LUA_TTABLE);
|
||||
luaL_argexpected(L, t == LUA_TNIL || t == LUA_TTABLE, 2, "nil or table");
|
||||
if (luaL_getmetafield(L, 1, "__metatable") != LUA_TNIL)
|
||||
if (l_unlikely(luaL_getmetafield(L, 1, "__metatable") != LUA_TNIL))
|
||||
return luaL_error(L, "cannot change a protected metatable");
|
||||
lua_settop(L, 2);
|
||||
lua_setmetatable(L, 1);
|
||||
@@ -182,11 +182,20 @@ static int luaB_rawset (lua_State *L) {
|
||||
|
||||
|
||||
static int pushmode (lua_State *L, int oldmode) {
|
||||
lua_pushstring(L, (oldmode == LUA_GCINC) ? "incremental" : "generational");
|
||||
if (oldmode == -1)
|
||||
luaL_pushfail(L); /* invalid call to 'lua_gc' */
|
||||
else
|
||||
lua_pushstring(L, (oldmode == LUA_GCINC) ? "incremental"
|
||||
: "generational");
|
||||
return 1;
|
||||
}
|
||||
|
||||
|
||||
/*
|
||||
** check whether call to 'lua_gc' was valid (not inside a finalizer)
|
||||
*/
|
||||
#define checkvalres(res) { if (res == -1) break; }
|
||||
|
||||
static int luaB_collectgarbage (lua_State *L) {
|
||||
static const char *const opts[] = {"stop", "restart", "collect",
|
||||
"count", "step", "setpause", "setstepmul",
|
||||
@@ -199,12 +208,14 @@ static int luaB_collectgarbage (lua_State *L) {
|
||||
case LUA_GCCOUNT: {
|
||||
int k = lua_gc(L, o);
|
||||
int b = lua_gc(L, LUA_GCCOUNTB);
|
||||
checkvalres(k);
|
||||
lua_pushnumber(L, (lua_Number)k + ((lua_Number)b/1024));
|
||||
return 1;
|
||||
}
|
||||
case LUA_GCSTEP: {
|
||||
int step = (int)luaL_optinteger(L, 2, 0);
|
||||
int res = lua_gc(L, o, step);
|
||||
checkvalres(res);
|
||||
lua_pushboolean(L, res);
|
||||
return 1;
|
||||
}
|
||||
@@ -212,11 +223,13 @@ static int luaB_collectgarbage (lua_State *L) {
|
||||
case LUA_GCSETSTEPMUL: {
|
||||
int p = (int)luaL_optinteger(L, 2, 0);
|
||||
int previous = lua_gc(L, o, p);
|
||||
checkvalres(previous);
|
||||
lua_pushinteger(L, previous);
|
||||
return 1;
|
||||
}
|
||||
case LUA_GCISRUNNING: {
|
||||
int res = lua_gc(L, o);
|
||||
checkvalres(res);
|
||||
lua_pushboolean(L, res);
|
||||
return 1;
|
||||
}
|
||||
@@ -233,10 +246,13 @@ static int luaB_collectgarbage (lua_State *L) {
|
||||
}
|
||||
default: {
|
||||
int res = lua_gc(L, o);
|
||||
checkvalres(res);
|
||||
lua_pushinteger(L, res);
|
||||
return 1;
|
||||
}
|
||||
}
|
||||
luaL_pushfail(L); /* invalid call (inside a finalizer) */
|
||||
return 1;
|
||||
}
|
||||
|
||||
|
||||
@@ -260,6 +276,11 @@ static int luaB_next (lua_State *L) {
|
||||
}
|
||||
|
||||
|
||||
static int pairscont (lua_State *L, int status, lua_KContext k) {
|
||||
(void)L; (void)status; (void)k; /* unused */
|
||||
return 3;
|
||||
}
|
||||
|
||||
static int luaB_pairs (lua_State *L) {
|
||||
luaL_checkany(L, 1);
|
||||
if (luaL_getmetafield(L, 1, "__pairs") == LUA_TNIL) { /* no metamethod? */
|
||||
@@ -269,7 +290,7 @@ static int luaB_pairs (lua_State *L) {
|
||||
}
|
||||
else {
|
||||
lua_pushvalue(L, 1); /* argument 'self' to metamethod */
|
||||
lua_call(L, 1, 3); /* get 3 values from metamethod */
|
||||
lua_callk(L, 1, 3, 0, pairscont); /* get 3 values from metamethod */
|
||||
}
|
||||
return 3;
|
||||
}
|
||||
@@ -279,7 +300,8 @@ static int luaB_pairs (lua_State *L) {
|
||||
** Traversal function for 'ipairs'
|
||||
*/
|
||||
static int ipairsaux (lua_State *L) {
|
||||
lua_Integer i = luaL_checkinteger(L, 2) + 1;
|
||||
lua_Integer i = luaL_checkinteger(L, 2);
|
||||
i = luaL_intop(+, i, 1);
|
||||
lua_pushinteger(L, i);
|
||||
return (lua_geti(L, 1, i) == LUA_TNIL) ? 1 : 2;
|
||||
}
|
||||
@@ -299,7 +321,7 @@ static int luaB_ipairs (lua_State *L) {
|
||||
|
||||
|
||||
static int load_aux (lua_State *L, int status, int envidx) {
|
||||
if (status == LUA_OK) {
|
||||
if (l_likely(status == LUA_OK)) {
|
||||
if (envidx != 0) { /* 'env' parameter? */
|
||||
lua_pushvalue(L, envidx); /* environment for loaded function */
|
||||
if (!lua_setupvalue(L, -2, 1)) /* set it as 1st upvalue */
|
||||
@@ -355,7 +377,7 @@ static const char *generic_reader (lua_State *L, void *ud, size_t *size) {
|
||||
*size = 0;
|
||||
return NULL;
|
||||
}
|
||||
else if (!lua_isstring(L, -1))
|
||||
else if (l_unlikely(!lua_isstring(L, -1)))
|
||||
luaL_error(L, "reader function must return a string");
|
||||
lua_replace(L, RESERVEDSLOT); /* save string in reserved slot */
|
||||
return lua_tolstring(L, RESERVEDSLOT, size);
|
||||
@@ -393,7 +415,7 @@ static int dofilecont (lua_State *L, int d1, lua_KContext d2) {
|
||||
static int luaB_dofile (lua_State *L) {
|
||||
const char *fname = luaL_optstring(L, 1, NULL);
|
||||
lua_settop(L, 1);
|
||||
if (luaL_loadfile(L, fname) != LUA_OK)
|
||||
if (l_unlikely(luaL_loadfile(L, fname) != LUA_OK))
|
||||
return lua_error(L);
|
||||
lua_callk(L, 0, LUA_MULTRET, 0, dofilecont);
|
||||
return dofilecont(L, 0, 0);
|
||||
@@ -401,7 +423,7 @@ static int luaB_dofile (lua_State *L) {
|
||||
|
||||
|
||||
static int luaB_assert (lua_State *L) {
|
||||
if (lua_toboolean(L, 1)) /* condition is true? */
|
||||
if (l_likely(lua_toboolean(L, 1))) /* condition is true? */
|
||||
return lua_gettop(L); /* return all arguments */
|
||||
else { /* error */
|
||||
luaL_checkany(L, 1); /* there must be a condition */
|
||||
@@ -437,7 +459,7 @@ static int luaB_select (lua_State *L) {
|
||||
** ignored).
|
||||
*/
|
||||
static int finishpcall (lua_State *L, int status, lua_KContext extra) {
|
||||
if (status != LUA_OK && status != LUA_YIELD) { /* error? */
|
||||
if (l_unlikely(status != LUA_OK && status != LUA_YIELD)) { /* error? */
|
||||
lua_pushboolean(L, 0); /* first result (false) */
|
||||
lua_pushvalue(L, -2); /* error message */
|
||||
return 2; /* return false, msg */
|
||||
|
||||
215
lcode.c
215
lcode.c
@@ -10,6 +10,7 @@
|
||||
#include "lprefix.h"
|
||||
|
||||
|
||||
#include <float.h>
|
||||
#include <limits.h>
|
||||
#include <math.h>
|
||||
#include <stdlib.h>
|
||||
@@ -314,15 +315,6 @@ void luaK_patchtohere (FuncState *fs, int list) {
|
||||
}
|
||||
|
||||
|
||||
/*
|
||||
** MAXimum number of successive Instructions WiTHout ABSolute line
|
||||
** information.
|
||||
*/
|
||||
#if !defined(MAXIWTHABS)
|
||||
#define MAXIWTHABS 120
|
||||
#endif
|
||||
|
||||
|
||||
/* limit for difference between lines in relative line info. */
|
||||
#define LIMLINEDIFF 0x80
|
||||
|
||||
@@ -337,13 +329,13 @@ void luaK_patchtohere (FuncState *fs, int list) {
|
||||
static void savelineinfo (FuncState *fs, Proto *f, int line) {
|
||||
int linedif = line - fs->previousline;
|
||||
int pc = fs->pc - 1; /* last instruction coded */
|
||||
if (abs(linedif) >= LIMLINEDIFF || fs->iwthabs++ > MAXIWTHABS) {
|
||||
if (abs(linedif) >= LIMLINEDIFF || fs->iwthabs++ >= MAXIWTHABS) {
|
||||
luaM_growvector(fs->ls->L, f->abslineinfo, fs->nabslineinfo,
|
||||
f->sizeabslineinfo, AbsLineInfo, MAX_INT, "lines");
|
||||
f->abslineinfo[fs->nabslineinfo].pc = pc;
|
||||
f->abslineinfo[fs->nabslineinfo++].line = line;
|
||||
linedif = ABSLINEINFO; /* signal that there is absolute information */
|
||||
fs->iwthabs = 0; /* restart counter */
|
||||
fs->iwthabs = 1; /* restart counter */
|
||||
}
|
||||
luaM_growvector(fs->ls->L, f->lineinfo, pc, f->sizelineinfo, ls_byte,
|
||||
MAX_INT, "opcodes");
|
||||
@@ -545,11 +537,14 @@ static void freeexps (FuncState *fs, expdesc *e1, expdesc *e2) {
|
||||
** and try to reuse constants. Because some values should not be used
|
||||
** as keys (nil cannot be a key, integer keys can collapse with float
|
||||
** keys), the caller must provide a useful 'key' for indexing the cache.
|
||||
** Note that all functions share the same table, so entering or exiting
|
||||
** a function can make some indices wrong.
|
||||
*/
|
||||
static int addk (FuncState *fs, TValue *key, TValue *v) {
|
||||
TValue val;
|
||||
lua_State *L = fs->ls->L;
|
||||
Proto *f = fs->f;
|
||||
TValue *idx = luaH_set(L, fs->ls->h, key); /* index scanner table */
|
||||
const TValue *idx = luaH_get(fs->ls->h, key); /* query scanner table */
|
||||
int k, oldsize;
|
||||
if (ttisinteger(idx)) { /* is there an index there? */
|
||||
k = cast_int(ivalue(idx));
|
||||
@@ -563,7 +558,8 @@ static int addk (FuncState *fs, TValue *key, TValue *v) {
|
||||
k = fs->nk;
|
||||
/* numerical value does not need GC barrier;
|
||||
table has no metatable, so it does not need to invalidate cache */
|
||||
setivalue(idx, k);
|
||||
setivalue(&val, k);
|
||||
luaH_finishset(L, fs->ls->h, key, idx, &val);
|
||||
luaM_growvector(L, f->k, k, f->sizek, TValue, MAXARG_Ax, "constants");
|
||||
while (oldsize < f->sizek) setnilvalue(&f->k[oldsize++]);
|
||||
setobj(L, &f->k[k], v);
|
||||
@@ -585,24 +581,41 @@ static int stringK (FuncState *fs, TString *s) {
|
||||
|
||||
/*
|
||||
** Add an integer to list of constants and return its index.
|
||||
** Integers use userdata as keys to avoid collision with floats with
|
||||
** same value; conversion to 'void*' is used only for hashing, so there
|
||||
** are no "precision" problems.
|
||||
*/
|
||||
static int luaK_intK (FuncState *fs, lua_Integer n) {
|
||||
TValue k, o;
|
||||
setpvalue(&k, cast_voidp(cast_sizet(n)));
|
||||
TValue o;
|
||||
setivalue(&o, n);
|
||||
return addk(fs, &k, &o);
|
||||
return addk(fs, &o, &o); /* use integer itself as key */
|
||||
}
|
||||
|
||||
/*
|
||||
** Add a float to list of constants and return its index.
|
||||
** Add a float to list of constants and return its index. Floats
|
||||
** with integral values need a different key, to avoid collision
|
||||
** with actual integers. To that, we add to the number its smaller
|
||||
** power-of-two fraction that is still significant in its scale.
|
||||
** For doubles, that would be 1/2^52.
|
||||
** (This method is not bulletproof: there may be another float
|
||||
** with that value, and for floats larger than 2^53 the result is
|
||||
** still an integer. At worst, this only wastes an entry with
|
||||
** a duplicate.)
|
||||
*/
|
||||
static int luaK_numberK (FuncState *fs, lua_Number r) {
|
||||
TValue o;
|
||||
lua_Integer ik;
|
||||
setfltvalue(&o, r);
|
||||
return addk(fs, &o, &o); /* use number itself as key */
|
||||
if (!luaV_flttointeger(r, &ik, F2Ieq)) /* not an integral value? */
|
||||
return addk(fs, &o, &o); /* use number itself as key */
|
||||
else { /* must build an alternative key */
|
||||
const int nbm = l_floatatt(MANT_DIG);
|
||||
const lua_Number q = l_mathop(ldexp)(l_mathop(1.0), -nbm + 1);
|
||||
const lua_Number k = (ik == 0) ? q : r + r*q; /* new key */
|
||||
TValue kv;
|
||||
setfltvalue(&kv, k);
|
||||
/* result is not an integral value, unless value is too large */
|
||||
lua_assert(!luaV_flttointeger(k, &ik, F2Ieq) ||
|
||||
l_mathop(fabs)(r) >= l_mathop(1e6));
|
||||
return addk(fs, &kv, &o);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -753,7 +766,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) {
|
||||
@@ -763,7 +776,7 @@ void luaK_dischargevars (FuncState *fs, expdesc *e) {
|
||||
break;
|
||||
}
|
||||
case VLOCAL: { /* already in a register */
|
||||
e->u.info = e->u.var.sidx;
|
||||
e->u.info = e->u.var.ridx;
|
||||
e->k = VNONRELOC; /* becomes a non-relocatable value */
|
||||
break;
|
||||
}
|
||||
@@ -805,8 +818,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) {
|
||||
@@ -860,7 +873,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) {
|
||||
@@ -946,8 +960,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;
|
||||
}
|
||||
|
||||
@@ -1032,7 +1049,7 @@ void luaK_storevar (FuncState *fs, expdesc *var, expdesc *ex) {
|
||||
switch (var->k) {
|
||||
case VLOCAL: {
|
||||
freeexp(fs, ex);
|
||||
exp2reg(fs, ex, var->u.var.sidx); /* compute 'ex' into proper place */
|
||||
exp2reg(fs, ex, var->u.var.ridx); /* compute 'ex' into proper place */
|
||||
return;
|
||||
}
|
||||
case VUPVAL: {
|
||||
@@ -1272,7 +1289,7 @@ void luaK_indexed (FuncState *fs, expdesc *t, expdesc *k) {
|
||||
}
|
||||
else {
|
||||
/* register index of the table */
|
||||
t->u.ind.t = (t->k == VLOCAL) ? t->u.var.sidx: t->u.info;
|
||||
t->u.ind.t = (t->k == VLOCAL) ? t->u.var.ridx: t->u.info;
|
||||
if (isKstr(fs, k)) {
|
||||
t->u.ind.idx = k->u.info; /* literal string */
|
||||
t->k = VINDEXSTR;
|
||||
@@ -1299,7 +1316,8 @@ static int validop (int op, TValue *v1, TValue *v2) {
|
||||
case LUA_OPBAND: case LUA_OPBOR: case LUA_OPBXOR:
|
||||
case LUA_OPSHL: case LUA_OPSHR: case LUA_OPBNOT: { /* conversion errors */
|
||||
lua_Integer i;
|
||||
return (tointegerns(v1, &i) && tointegerns(v2, &i));
|
||||
return (luaV_tointegerns(v1, &i, LUA_FLOORN2I) &&
|
||||
luaV_tointegerns(v2, &i, LUA_FLOORN2I));
|
||||
}
|
||||
case LUA_OPDIV: case LUA_OPIDIV: case LUA_OPMOD: /* division by 0 */
|
||||
return (nvalue(v2) != 0);
|
||||
@@ -1333,6 +1351,35 @@ static int constfolding (FuncState *fs, int op, expdesc *e1,
|
||||
}
|
||||
|
||||
|
||||
/*
|
||||
** Convert a BinOpr to an OpCode (ORDER OPR - ORDER OP)
|
||||
*/
|
||||
l_sinline OpCode binopr2op (BinOpr opr, BinOpr baser, OpCode base) {
|
||||
lua_assert(baser <= opr &&
|
||||
((baser == OPR_ADD && opr <= OPR_SHR) ||
|
||||
(baser == OPR_LT && opr <= OPR_LE)));
|
||||
return cast(OpCode, (cast_int(opr) - cast_int(baser)) + cast_int(base));
|
||||
}
|
||||
|
||||
|
||||
/*
|
||||
** Convert a UnOpr to an OpCode (ORDER OPR - ORDER OP)
|
||||
*/
|
||||
l_sinline OpCode unopr2op (UnOpr opr) {
|
||||
return cast(OpCode, (cast_int(opr) - cast_int(OPR_MINUS)) +
|
||||
cast_int(OP_UNM));
|
||||
}
|
||||
|
||||
|
||||
/*
|
||||
** Convert a BinOpr to a tag method (ORDER OPR - ORDER TM)
|
||||
*/
|
||||
l_sinline TMS binopr2TM (BinOpr opr) {
|
||||
lua_assert(OPR_ADD <= opr && opr <= OPR_SHR);
|
||||
return cast(TMS, (cast_int(opr) - cast_int(OPR_ADD)) + cast_int(TM_ADD));
|
||||
}
|
||||
|
||||
|
||||
/*
|
||||
** Emit code for unary expressions that "produce values"
|
||||
** (everything but 'not').
|
||||
@@ -1371,12 +1418,15 @@ static void finishbinexpval (FuncState *fs, expdesc *e1, expdesc *e2,
|
||||
** Emit code for binary expressions that "produce values" over
|
||||
** two registers.
|
||||
*/
|
||||
static void codebinexpval (FuncState *fs, OpCode op,
|
||||
static void codebinexpval (FuncState *fs, BinOpr opr,
|
||||
expdesc *e1, expdesc *e2, int line) {
|
||||
int v2 = luaK_exp2anyreg(fs, e2); /* both operands are in registers */
|
||||
OpCode op = binopr2op(opr, OPR_ADD, OP_ADD);
|
||||
int v2 = luaK_exp2anyreg(fs, e2); /* make sure 'e2' is in a register */
|
||||
/* 'e1' must be already in a register or it is a constant */
|
||||
lua_assert((VNIL <= e1->k && e1->k <= VKSTR) ||
|
||||
e1->k == VNONRELOC || e1->k == VRELOC);
|
||||
lua_assert(OP_ADD <= op && op <= OP_SHR);
|
||||
finishbinexpval(fs, e1, e2, op, v2, 0, line, OP_MMBIN,
|
||||
cast(TMS, (op - OP_ADD) + TM_ADD));
|
||||
finishbinexpval(fs, e1, e2, op, v2, 0, line, OP_MMBIN, binopr2TM(opr));
|
||||
}
|
||||
|
||||
|
||||
@@ -1392,6 +1442,18 @@ static void codebini (FuncState *fs, OpCode op,
|
||||
}
|
||||
|
||||
|
||||
/*
|
||||
** Code binary operators with K operand.
|
||||
*/
|
||||
static void codebinK (FuncState *fs, BinOpr opr,
|
||||
expdesc *e1, expdesc *e2, int flip, int line) {
|
||||
TMS event = binopr2TM(opr);
|
||||
int v2 = e2->u.info; /* K index */
|
||||
OpCode op = binopr2op(opr, OPR_ADD, OP_ADDK);
|
||||
finishbinexpval(fs, e1, e2, op, v2, flip, line, OP_MMBINK, event);
|
||||
}
|
||||
|
||||
|
||||
/* Try to code a binary operator negating its second operand.
|
||||
** For the metamethod, 2nd operand must keep its original value.
|
||||
*/
|
||||
@@ -1419,24 +1481,27 @@ static void swapexps (expdesc *e1, expdesc *e2) {
|
||||
}
|
||||
|
||||
|
||||
/*
|
||||
** Code binary operators with no constant operand.
|
||||
*/
|
||||
static void codebinNoK (FuncState *fs, BinOpr opr,
|
||||
expdesc *e1, expdesc *e2, int flip, int line) {
|
||||
if (flip)
|
||||
swapexps(e1, e2); /* back to original order */
|
||||
codebinexpval(fs, opr, e1, e2, line); /* use standard operators */
|
||||
}
|
||||
|
||||
|
||||
/*
|
||||
** Code arithmetic operators ('+', '-', ...). If second operand is a
|
||||
** constant in the proper range, use variant opcodes with K operands.
|
||||
*/
|
||||
static void codearith (FuncState *fs, BinOpr opr,
|
||||
expdesc *e1, expdesc *e2, int flip, int line) {
|
||||
TMS event = cast(TMS, opr + TM_ADD);
|
||||
if (tonumeral(e2, NULL) && luaK_exp2K(fs, e2)) { /* K operand? */
|
||||
int v2 = e2->u.info; /* K index */
|
||||
OpCode op = cast(OpCode, opr + OP_ADDK);
|
||||
finishbinexpval(fs, e1, e2, op, v2, flip, line, OP_MMBINK, event);
|
||||
}
|
||||
else { /* 'e2' is neither an immediate nor a K operand */
|
||||
OpCode op = cast(OpCode, opr + OP_ADD);
|
||||
if (flip)
|
||||
swapexps(e1, e2); /* back to original order */
|
||||
codebinexpval(fs, op, e1, e2, line); /* use standard operators */
|
||||
}
|
||||
if (tonumeral(e2, NULL) && luaK_exp2K(fs, e2)) /* K operand? */
|
||||
codebinK(fs, opr, e1, e2, flip, line);
|
||||
else /* 'e2' is neither an immediate nor a K operand */
|
||||
codebinNoK(fs, opr, e1, e2, flip, line);
|
||||
}
|
||||
|
||||
|
||||
@@ -1453,35 +1518,27 @@ static void codecommutative (FuncState *fs, BinOpr op,
|
||||
flip = 1;
|
||||
}
|
||||
if (op == OPR_ADD && isSCint(e2)) /* immediate operand? */
|
||||
codebini(fs, cast(OpCode, OP_ADDI), e1, e2, flip, line, TM_ADD);
|
||||
codebini(fs, OP_ADDI, e1, e2, flip, line, TM_ADD);
|
||||
else
|
||||
codearith(fs, op, e1, e2, flip, line);
|
||||
}
|
||||
|
||||
|
||||
/*
|
||||
** Code bitwise operations; they are all associative, so the function
|
||||
** Code bitwise operations; they are all commutative, so the function
|
||||
** tries to put an integer constant as the 2nd operand (a K operand).
|
||||
*/
|
||||
static void codebitwise (FuncState *fs, BinOpr opr,
|
||||
expdesc *e1, expdesc *e2, int line) {
|
||||
int flip = 0;
|
||||
int v2;
|
||||
OpCode op;
|
||||
if (e1->k == VKINT && luaK_exp2RK(fs, e1)) {
|
||||
if (e1->k == VKINT) {
|
||||
swapexps(e1, e2); /* 'e2' will be the constant operand */
|
||||
flip = 1;
|
||||
}
|
||||
else if (!(e2->k == VKINT && luaK_exp2RK(fs, e2))) { /* no constants? */
|
||||
op = cast(OpCode, opr + OP_ADD);
|
||||
codebinexpval(fs, op, e1, e2, line); /* all-register opcodes */
|
||||
return;
|
||||
}
|
||||
v2 = e2->u.info; /* index in K array */
|
||||
op = cast(OpCode, opr + OP_ADDK);
|
||||
lua_assert(ttisinteger(&fs->f->k[v2]));
|
||||
finishbinexpval(fs, e1, e2, op, v2, flip, line, OP_MMBINK,
|
||||
cast(TMS, opr + TM_ADD));
|
||||
if (e2->k == VKINT && luaK_exp2K(fs, e2)) /* K operand? */
|
||||
codebinK(fs, opr, e1, e2, flip, line);
|
||||
else /* no constants */
|
||||
codebinNoK(fs, opr, e1, e2, flip, line);
|
||||
}
|
||||
|
||||
|
||||
@@ -1489,25 +1546,27 @@ static void codebitwise (FuncState *fs, BinOpr opr,
|
||||
** Emit code for order comparisons. When using an immediate operand,
|
||||
** 'isfloat' tells whether the original value was a float.
|
||||
*/
|
||||
static void codeorder (FuncState *fs, OpCode op, expdesc *e1, expdesc *e2) {
|
||||
static void codeorder (FuncState *fs, BinOpr opr, expdesc *e1, expdesc *e2) {
|
||||
int r1, r2;
|
||||
int im;
|
||||
int isfloat = 0;
|
||||
OpCode op;
|
||||
if (isSCnumber(e2, &im, &isfloat)) {
|
||||
/* use immediate operand */
|
||||
r1 = luaK_exp2anyreg(fs, e1);
|
||||
r2 = im;
|
||||
op = cast(OpCode, (op - OP_LT) + OP_LTI);
|
||||
op = binopr2op(opr, OPR_LT, OP_LTI);
|
||||
}
|
||||
else if (isSCnumber(e1, &im, &isfloat)) {
|
||||
/* transform (A < B) to (B > A) and (A <= B) to (B >= A) */
|
||||
r1 = luaK_exp2anyreg(fs, e2);
|
||||
r2 = im;
|
||||
op = (op == OP_LT) ? OP_GTI : OP_GEI;
|
||||
op = binopr2op(opr, OPR_LT, OP_GTI);
|
||||
}
|
||||
else { /* regular case, compare two registers */
|
||||
r1 = luaK_exp2anyreg(fs, e1);
|
||||
r2 = luaK_exp2anyreg(fs, e2);
|
||||
op = binopr2op(opr, OPR_LT, OP_LT);
|
||||
}
|
||||
freeexps(fs, e1, e2);
|
||||
e1->u.info = condjump(fs, op, r1, r2, isfloat, 1);
|
||||
@@ -1533,7 +1592,7 @@ static void codeeq (FuncState *fs, BinOpr opr, expdesc *e1, expdesc *e2) {
|
||||
op = OP_EQI;
|
||||
r2 = im; /* immediate operand */
|
||||
}
|
||||
else if (luaK_exp2RK(fs, e2)) { /* 1st expression is constant? */
|
||||
else if (luaK_exp2RK(fs, e2)) { /* 2nd expression is constant? */
|
||||
op = OP_EQK;
|
||||
r2 = e2->u.info; /* constant index */
|
||||
}
|
||||
@@ -1550,16 +1609,16 @@ static void codeeq (FuncState *fs, BinOpr opr, expdesc *e1, expdesc *e2) {
|
||||
/*
|
||||
** Apply prefix operation 'op' to expression 'e'.
|
||||
*/
|
||||
void luaK_prefix (FuncState *fs, UnOpr op, expdesc *e, int line) {
|
||||
void luaK_prefix (FuncState *fs, UnOpr opr, expdesc *e, int line) {
|
||||
static const expdesc ef = {VKINT, {0}, NO_JUMP, NO_JUMP};
|
||||
luaK_dischargevars(fs, e);
|
||||
switch (op) {
|
||||
switch (opr) {
|
||||
case OPR_MINUS: case OPR_BNOT: /* use 'ef' as fake 2nd operand */
|
||||
if (constfolding(fs, op + LUA_OPUNM, e, &ef))
|
||||
if (constfolding(fs, opr + LUA_OPUNM, e, &ef))
|
||||
break;
|
||||
/* else */ /* FALLTHROUGH */
|
||||
case OPR_LEN:
|
||||
codeunexpval(fs, cast(OpCode, op + OP_UNM), e, line);
|
||||
codeunexpval(fs, unopr2op(opr), e, line);
|
||||
break;
|
||||
case OPR_NOT: codenot(fs, e); break;
|
||||
default: lua_assert(0);
|
||||
@@ -1593,7 +1652,8 @@ void luaK_infix (FuncState *fs, BinOpr op, expdesc *v) {
|
||||
case OPR_SHL: case OPR_SHR: {
|
||||
if (!tonumeral(v, NULL))
|
||||
luaK_exp2anyreg(fs, v);
|
||||
/* else keep numeral, which may be folded with 2nd operand */
|
||||
/* else keep numeral, which may be folded or used as an immediate
|
||||
operand */
|
||||
break;
|
||||
}
|
||||
case OPR_EQ: case OPR_NE: {
|
||||
@@ -1688,30 +1748,27 @@ void luaK_posfix (FuncState *fs, BinOpr opr,
|
||||
/* coded as (r1 >> -I) */;
|
||||
}
|
||||
else /* regular case (two registers) */
|
||||
codebinexpval(fs, OP_SHL, e1, e2, line);
|
||||
codebinexpval(fs, opr, e1, e2, line);
|
||||
break;
|
||||
}
|
||||
case OPR_SHR: {
|
||||
if (isSCint(e2))
|
||||
codebini(fs, OP_SHRI, e1, e2, 0, line, TM_SHR); /* r1 >> I */
|
||||
else /* regular case (two registers) */
|
||||
codebinexpval(fs, OP_SHR, e1, e2, line);
|
||||
codebinexpval(fs, opr, e1, e2, line);
|
||||
break;
|
||||
}
|
||||
case OPR_EQ: case OPR_NE: {
|
||||
codeeq(fs, opr, e1, e2);
|
||||
break;
|
||||
}
|
||||
case OPR_LT: case OPR_LE: {
|
||||
OpCode op = cast(OpCode, (opr - OPR_EQ) + OP_EQ);
|
||||
codeorder(fs, op, e1, e2);
|
||||
break;
|
||||
}
|
||||
case OPR_GT: case OPR_GE: {
|
||||
/* '(a > b)' <=> '(b < a)'; '(a >= b)' <=> '(b <= a)' */
|
||||
OpCode op = cast(OpCode, (opr - OPR_NE) + OP_EQ);
|
||||
swapexps(e1, e2);
|
||||
codeorder(fs, op, e1, e2);
|
||||
opr = cast(BinOpr, (opr - OPR_GT) + OPR_LT);
|
||||
} /* FALLTHROUGH */
|
||||
case OPR_LT: case OPR_LE: {
|
||||
codeorder(fs, opr, e1, e2);
|
||||
break;
|
||||
}
|
||||
default: lua_assert(0);
|
||||
|
||||
21
lcorolib.c
21
lcorolib.c
@@ -31,14 +31,14 @@ static lua_State *getco (lua_State *L) {
|
||||
*/
|
||||
static int auxresume (lua_State *L, lua_State *co, int narg) {
|
||||
int status, nres;
|
||||
if (!lua_checkstack(co, narg)) {
|
||||
if (l_unlikely(!lua_checkstack(co, narg))) {
|
||||
lua_pushliteral(L, "too many arguments to resume");
|
||||
return -1; /* error flag */
|
||||
}
|
||||
lua_xmove(L, co, narg);
|
||||
status = lua_resume(co, L, narg, &nres);
|
||||
if (status == LUA_OK || status == LUA_YIELD) {
|
||||
if (!lua_checkstack(L, nres + 1)) {
|
||||
if (l_likely(status == LUA_OK || status == LUA_YIELD)) {
|
||||
if (l_unlikely(!lua_checkstack(L, nres + 1))) {
|
||||
lua_pop(co, nres); /* remove results anyway */
|
||||
lua_pushliteral(L, "too many results to resume");
|
||||
return -1; /* error flag */
|
||||
@@ -57,7 +57,7 @@ static int luaB_coresume (lua_State *L) {
|
||||
lua_State *co = getco(L);
|
||||
int r;
|
||||
r = auxresume(L, co, lua_gettop(L) - 1);
|
||||
if (r < 0) {
|
||||
if (l_unlikely(r < 0)) {
|
||||
lua_pushboolean(L, 0);
|
||||
lua_insert(L, -2);
|
||||
return 2; /* return false + error message */
|
||||
@@ -73,10 +73,13 @@ 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) { /* error? */
|
||||
if (l_unlikely(r < 0)) { /* error? */
|
||||
int stat = lua_status(co);
|
||||
if (stat != LUA_OK && stat != LUA_YIELD) /* error in the coroutine? */
|
||||
lua_resetthread(co); /* close its tbc variables */
|
||||
if (stat != LUA_OK && stat != LUA_YIELD) { /* error in the coroutine? */
|
||||
stat = lua_closethread(co, L); /* close its tbc variables */
|
||||
lua_assert(stat != LUA_OK);
|
||||
lua_xmove(co, L, 1); /* move error message to the caller */
|
||||
}
|
||||
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 */
|
||||
@@ -169,14 +172,14 @@ static int luaB_close (lua_State *L) {
|
||||
int status = auxstatus(L, co);
|
||||
switch (status) {
|
||||
case COS_DEAD: case COS_YIELD: {
|
||||
status = lua_resetthread(co);
|
||||
status = lua_closethread(co, L);
|
||||
if (status == LUA_OK) {
|
||||
lua_pushboolean(L, 1);
|
||||
return 1;
|
||||
}
|
||||
else {
|
||||
lua_pushboolean(L, 0);
|
||||
lua_xmove(co, L, 1); /* copy error message */
|
||||
lua_xmove(co, L, 1); /* move error message */
|
||||
return 2;
|
||||
}
|
||||
}
|
||||
|
||||
40
ldblib.c
40
ldblib.c
@@ -33,7 +33,7 @@ static const char *const HOOKKEY = "_HOOKKEY";
|
||||
** checked.
|
||||
*/
|
||||
static void checkstack (lua_State *L, lua_State *L1, int n) {
|
||||
if (L != L1 && !lua_checkstack(L1, n))
|
||||
if (l_unlikely(L != L1 && !lua_checkstack(L1, n)))
|
||||
luaL_error(L, "stack overflow");
|
||||
}
|
||||
|
||||
@@ -152,6 +152,7 @@ static int db_getinfo (lua_State *L) {
|
||||
lua_State *L1 = getthread(L, &arg);
|
||||
const char *options = luaL_optstring(L, arg+2, "flnSrtu");
|
||||
checkstack(L, L1, 3);
|
||||
luaL_argcheck(L, options[0] != '>', arg + 2, "invalid option '>'");
|
||||
if (lua_isfunction(L, arg + 1)) { /* info about a function? */
|
||||
options = lua_pushfstring(L, ">%s", options); /* add '>' to 'options' */
|
||||
lua_pushvalue(L, arg + 1); /* move function to 'L1' stack */
|
||||
@@ -212,7 +213,7 @@ static int db_getlocal (lua_State *L) {
|
||||
lua_Debug ar;
|
||||
const char *name;
|
||||
int level = (int)luaL_checkinteger(L, arg + 1);
|
||||
if (!lua_getstack(L1, level, &ar)) /* out of range? */
|
||||
if (l_unlikely(!lua_getstack(L1, level, &ar))) /* out of range? */
|
||||
return luaL_argerror(L, arg+1, "level out of range");
|
||||
checkstack(L, L1, 1);
|
||||
name = lua_getlocal(L1, &ar, nvar);
|
||||
@@ -237,7 +238,7 @@ static int db_setlocal (lua_State *L) {
|
||||
lua_Debug ar;
|
||||
int level = (int)luaL_checkinteger(L, arg + 1);
|
||||
int nvar = (int)luaL_checkinteger(L, arg + 2);
|
||||
if (!lua_getstack(L1, level, &ar)) /* out of range? */
|
||||
if (l_unlikely(!lua_getstack(L1, level, &ar))) /* out of range? */
|
||||
return luaL_argerror(L, arg+1, "level out of range");
|
||||
luaL_checkany(L, arg+3);
|
||||
lua_settop(L, arg+3);
|
||||
@@ -281,25 +282,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);
|
||||
@@ -369,7 +378,7 @@ static int db_sethook (lua_State *L) {
|
||||
}
|
||||
if (!luaL_getsubtable(L, LUA_REGISTRYINDEX, HOOKKEY)) {
|
||||
/* table just created; initialize it */
|
||||
lua_pushstring(L, "k");
|
||||
lua_pushliteral(L, "k");
|
||||
lua_setfield(L, -2, "__mode"); /** hooktable.__mode = "k" */
|
||||
lua_pushvalue(L, -1);
|
||||
lua_setmetatable(L, -2); /* metatable(hooktable) = hooktable */
|
||||
@@ -412,7 +421,7 @@ static int db_debug (lua_State *L) {
|
||||
for (;;) {
|
||||
char buffer[250];
|
||||
lua_writestringerror("%s", "lua_debug> ");
|
||||
if (fgets(buffer, sizeof(buffer), stdin) == 0 ||
|
||||
if (fgets(buffer, sizeof(buffer), stdin) == NULL ||
|
||||
strcmp(buffer, "cont\n") == 0)
|
||||
return 0;
|
||||
if (luaL_loadbuffer(L, buffer, strlen(buffer), "=(debug command)") ||
|
||||
@@ -440,10 +449,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;
|
||||
}
|
||||
|
||||
|
||||
274
ldebug.c
274
ldebug.c
@@ -33,11 +33,9 @@
|
||||
|
||||
#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);
|
||||
static const char *funcnamefromcall (lua_State *L, CallInfo *ci,
|
||||
const char **name);
|
||||
|
||||
|
||||
static int currentpc (CallInfo *ci) {
|
||||
@@ -48,10 +46,16 @@ static int currentpc (CallInfo *ci) {
|
||||
|
||||
/*
|
||||
** Get a "base line" to find the line corresponding to an instruction.
|
||||
** For that, search the array of absolute line info for the largest saved
|
||||
** instruction smaller or equal to the wanted instruction. A special
|
||||
** case is when there is no absolute info or the instruction is before
|
||||
** the first absolute one.
|
||||
** Base lines are regularly placed at MAXIWTHABS intervals, so usually
|
||||
** an integer division gets the right place. When the source file has
|
||||
** large sequences of empty/comment lines, it may need extra entries,
|
||||
** so the original estimate needs a correction.
|
||||
** If the original estimate is -1, the initial 'if' ensures that the
|
||||
** 'while' will run at least once.
|
||||
** The assertion that the estimate is a lower bound for the correct base
|
||||
** is valid as long as the debug info has been generated with the same
|
||||
** value for MAXIWTHABS or smaller. (Previous releases use a little
|
||||
** smaller value.)
|
||||
*/
|
||||
static int getbaseline (const Proto *f, int pc, int *basepc) {
|
||||
if (f->sizeabslineinfo == 0 || pc < f->abslineinfo[0].pc) {
|
||||
@@ -59,20 +63,12 @@ static int getbaseline (const Proto *f, int pc, int *basepc) {
|
||||
return f->linedefined;
|
||||
}
|
||||
else {
|
||||
unsigned int i;
|
||||
if (pc >= f->abslineinfo[f->sizeabslineinfo - 1].pc)
|
||||
i = f->sizeabslineinfo - 1; /* instruction is after last saved one */
|
||||
else { /* binary search */
|
||||
unsigned int j = f->sizeabslineinfo - 1; /* pc < anchorlines[j] */
|
||||
i = 0; /* abslineinfo[i] <= pc */
|
||||
while (i < j - 1) {
|
||||
unsigned int m = (j + i) / 2;
|
||||
if (pc >= f->abslineinfo[m].pc)
|
||||
i = m;
|
||||
else
|
||||
j = m;
|
||||
}
|
||||
}
|
||||
int i = cast_uint(pc) / MAXIWTHABS - 1; /* get an estimate */
|
||||
/* estimate must be a lower bound of the correct base */
|
||||
lua_assert(i < 0 ||
|
||||
(i < f->sizeabslineinfo && f->abslineinfo[i].pc <= pc));
|
||||
while (i + 1 < f->sizeabslineinfo && pc >= f->abslineinfo[i + 1].pc)
|
||||
i++; /* low estimate; adjust it */
|
||||
*basepc = f->abslineinfo[i].pc;
|
||||
return f->abslineinfo[i].line;
|
||||
}
|
||||
@@ -186,10 +182,10 @@ 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) {
|
||||
if (clLvalue(s2v(ci->func.p))->p->is_vararg) {
|
||||
int nextra = ci->u.l.nextraargs;
|
||||
if (n >= -nextra) { /* 'n' is negative */
|
||||
*pos = ci->func - nextra - (n + 1);
|
||||
*pos = ci->func.p - nextra - (n + 1);
|
||||
return "(vararg)"; /* generic name for any vararg */
|
||||
}
|
||||
}
|
||||
@@ -198,7 +194,7 @@ static const char *findvararg (CallInfo *ci, int n, StkId *pos) {
|
||||
|
||||
|
||||
const char *luaG_findlocal (lua_State *L, CallInfo *ci, int n, StkId *pos) {
|
||||
StkId base = ci->func + 1;
|
||||
StkId base = ci->func.p + 1;
|
||||
const char *name = NULL;
|
||||
if (isLua(ci)) {
|
||||
if (n < 0) /* access to vararg values? */
|
||||
@@ -207,7 +203,7 @@ const char *luaG_findlocal (lua_State *L, CallInfo *ci, int n, StkId *pos) {
|
||||
name = luaF_getlocalname(ci_func(ci)->p, n, currentpc(ci));
|
||||
}
|
||||
if (name == NULL) { /* no 'standard' name? */
|
||||
StkId limit = (ci == L->ci) ? L->top : ci->next->func;
|
||||
StkId limit = (ci == L->ci) ? L->top.p : ci->next->func.p;
|
||||
if (limit - base >= n && n > 0) { /* is 'n' inside 'ci' stack? */
|
||||
/* generic name for any valid slot */
|
||||
name = isLua(ci) ? "(temporary)" : "(C temporary)";
|
||||
@@ -225,16 +221,16 @@ LUA_API const char *lua_getlocal (lua_State *L, const lua_Debug *ar, int n) {
|
||||
const char *name;
|
||||
lua_lock(L);
|
||||
if (ar == NULL) { /* information about non-active function? */
|
||||
if (!isLfunction(s2v(L->top - 1))) /* not a Lua function? */
|
||||
if (!isLfunction(s2v(L->top.p - 1))) /* not a Lua function? */
|
||||
name = NULL;
|
||||
else /* consider live variables at function start (parameters) */
|
||||
name = luaF_getlocalname(clLvalue(s2v(L->top - 1))->p, n, 0);
|
||||
name = luaF_getlocalname(clLvalue(s2v(L->top.p - 1))->p, n, 0);
|
||||
}
|
||||
else { /* active function; get information through 'ar' */
|
||||
StkId pos = NULL; /* to avoid warnings */
|
||||
name = luaG_findlocal(L, ar->i_ci, n, &pos);
|
||||
if (name) {
|
||||
setobjs2s(L, L->top, pos);
|
||||
setobjs2s(L, L->top.p, pos);
|
||||
api_incr_top(L);
|
||||
}
|
||||
}
|
||||
@@ -249,8 +245,8 @@ LUA_API const char *lua_setlocal (lua_State *L, const lua_Debug *ar, int n) {
|
||||
lua_lock(L);
|
||||
name = luaG_findlocal(L, ar->i_ci, n, &pos);
|
||||
if (name) {
|
||||
setobjs2s(L, pos, L->top - 1);
|
||||
L->top--; /* pop value */
|
||||
setobjs2s(L, pos, L->top.p - 1);
|
||||
L->top.p--; /* pop value */
|
||||
}
|
||||
lua_unlock(L);
|
||||
return name;
|
||||
@@ -293,7 +289,7 @@ static int nextline (const Proto *p, int currentline, int pc) {
|
||||
|
||||
static void collectvalidlines (lua_State *L, Closure *f) {
|
||||
if (noLuaClosure(f)) {
|
||||
setnilvalue(s2v(L->top));
|
||||
setnilvalue(s2v(L->top.p));
|
||||
api_incr_top(L);
|
||||
}
|
||||
else {
|
||||
@@ -302,11 +298,18 @@ static void collectvalidlines (lua_State *L, Closure *f) {
|
||||
const Proto *p = f->l.p;
|
||||
int currentline = p->linedefined;
|
||||
Table *t = luaH_new(L); /* new table to store active lines */
|
||||
sethvalue2s(L, L->top, t); /* push it on stack */
|
||||
sethvalue2s(L, L->top.p, t); /* push it on stack */
|
||||
api_incr_top(L);
|
||||
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);
|
||||
if (!p->is_vararg) /* regular function? */
|
||||
i = 0; /* consider all instructions */
|
||||
else { /* vararg function */
|
||||
lua_assert(GET_OPCODE(p->code[0]) == OP_VARARGPREP);
|
||||
currentline = nextline(p, currentline, 0);
|
||||
i = 1; /* skip first instruction (OP_VARARGPREP) */
|
||||
}
|
||||
for (; i < p->sizelineinfo; i++) { /* for each instruction */
|
||||
currentline = nextline(p, currentline, i); /* get its line */
|
||||
luaH_setint(L, t, currentline, &v); /* table[line] = true */
|
||||
}
|
||||
}
|
||||
@@ -314,15 +317,9 @@ static void collectvalidlines (lua_State *L, Closure *f) {
|
||||
|
||||
|
||||
static const char *getfuncname (lua_State *L, CallInfo *ci, const char **name) {
|
||||
if (ci == NULL) /* no 'ci'? */
|
||||
return NULL; /* no info */
|
||||
else if (ci->callstatus & CIST_FIN) { /* is this a finalizer? */
|
||||
*name = "__gc";
|
||||
return "metamethod"; /* report it as such */
|
||||
}
|
||||
/* calling function is a known Lua function? */
|
||||
else if (!(ci->callstatus & CIST_TAIL) && isLua(ci->previous))
|
||||
return funcnamefromcode(L, ci->previous, name);
|
||||
/* calling function is a known function? */
|
||||
if (ci != NULL && !(ci->callstatus & CIST_TAIL))
|
||||
return funcnamefromcall(L, ci->previous, name);
|
||||
else return NULL; /* no way to find a name */
|
||||
}
|
||||
|
||||
@@ -391,20 +388,20 @@ LUA_API int lua_getinfo (lua_State *L, const char *what, lua_Debug *ar) {
|
||||
lua_lock(L);
|
||||
if (*what == '>') {
|
||||
ci = NULL;
|
||||
func = s2v(L->top - 1);
|
||||
func = s2v(L->top.p - 1);
|
||||
api_check(L, ttisfunction(func), "function expected");
|
||||
what++; /* skip the '>' */
|
||||
L->top--; /* pop function */
|
||||
L->top.p--; /* pop function */
|
||||
}
|
||||
else {
|
||||
ci = ar->i_ci;
|
||||
func = s2v(ci->func);
|
||||
func = s2v(ci->func.p);
|
||||
lua_assert(ttisfunction(func));
|
||||
}
|
||||
cl = ttisclosure(func) ? clvalue(func) : NULL;
|
||||
status = auxgetinfo(L, what, ar, cl, ci);
|
||||
if (strchr(what, 'f')) {
|
||||
setobj2s(L, L->top, func);
|
||||
setobj2s(L, L->top.p, func);
|
||||
api_incr_top(L);
|
||||
}
|
||||
if (strchr(what, 'L'))
|
||||
@@ -594,16 +591,10 @@ static const char *getobjname (const Proto *p, int lastpc, int reg,
|
||||
** Returns what the name is (e.g., "for iterator", "method",
|
||||
** "metamethod") and sets '*name' to point to the name.
|
||||
*/
|
||||
static const char *funcnamefromcode (lua_State *L, CallInfo *ci,
|
||||
const char **name) {
|
||||
static const char *funcnamefromcode (lua_State *L, const Proto *p,
|
||||
int pc, const char **name) {
|
||||
TMS tm = (TMS)0; /* (initial value avoids warnings) */
|
||||
const Proto *p = ci_func(ci)->p; /* calling function */
|
||||
int pc = currentpc(ci); /* calling instruction index */
|
||||
Instruction i = p->code[pc]; /* calling instruction */
|
||||
if (ci->callstatus & CIST_HOOKED) { /* was it called inside a hook? */
|
||||
*name = "?";
|
||||
return "hook";
|
||||
}
|
||||
switch (GET_OPCODE(i)) {
|
||||
case OP_CALL:
|
||||
case OP_TAILCALL:
|
||||
@@ -629,12 +620,10 @@ static const char *funcnamefromcode (lua_State *L, CallInfo *ci,
|
||||
case OP_LEN: tm = TM_LEN; break;
|
||||
case OP_CONCAT: tm = TM_CONCAT; break;
|
||||
case OP_EQ: tm = TM_EQ; break;
|
||||
case OP_LT: case OP_LE: case OP_LTI: case OP_LEI:
|
||||
*name = "order"; /* '<=' can call '__lt', etc. */
|
||||
return "metamethod";
|
||||
case OP_CLOSE: case OP_RETURN:
|
||||
*name = "close";
|
||||
return "metamethod";
|
||||
/* no cases for OP_EQI and OP_EQK, as they don't call metamethods */
|
||||
case OP_LT: case OP_LTI: case OP_GTI: tm = TM_LT; break;
|
||||
case OP_LE: case OP_LEI: case OP_GEI: tm = TM_LE; break;
|
||||
case OP_CLOSE: case OP_RETURN: tm = TM_CLOSE; break;
|
||||
default:
|
||||
return NULL; /* cannot find a reasonable name */
|
||||
}
|
||||
@@ -642,19 +631,44 @@ static const char *funcnamefromcode (lua_State *L, CallInfo *ci,
|
||||
return "metamethod";
|
||||
}
|
||||
|
||||
|
||||
/*
|
||||
** Try to find a name for a function based on how it was called.
|
||||
*/
|
||||
static const char *funcnamefromcall (lua_State *L, CallInfo *ci,
|
||||
const char **name) {
|
||||
if (ci->callstatus & CIST_HOOKED) { /* was it called inside a hook? */
|
||||
*name = "?";
|
||||
return "hook";
|
||||
}
|
||||
else if (ci->callstatus & CIST_FIN) { /* was it called as a finalizer? */
|
||||
*name = "__gc";
|
||||
return "metamethod"; /* report it as such */
|
||||
}
|
||||
else if (isLua(ci))
|
||||
return funcnamefromcode(L, ci_func(ci)->p, currentpc(ci), name);
|
||||
else
|
||||
return NULL;
|
||||
}
|
||||
|
||||
/* }====================================================== */
|
||||
|
||||
|
||||
|
||||
/*
|
||||
** The subtraction of two potentially unrelated pointers is
|
||||
** not ISO C, but it should not crash a program; the subsequent
|
||||
** checks are ISO C and ensure a correct result.
|
||||
** Check whether pointer 'o' points to some value in the stack frame of
|
||||
** the current function and, if so, returns its index. Because 'o' may
|
||||
** not point to a value in this stack, we cannot compare it with the
|
||||
** region boundaries (undefined behavior in ISO C).
|
||||
*/
|
||||
static int isinstack (CallInfo *ci, const TValue *o) {
|
||||
StkId base = ci->func + 1;
|
||||
ptrdiff_t i = cast(StkId, o) - base;
|
||||
return (0 <= i && i < (ci->top - base) && s2v(base + i) == o);
|
||||
static int instack (CallInfo *ci, const TValue *o) {
|
||||
int pos;
|
||||
StkId base = ci->func.p + 1;
|
||||
for (pos = 0; base + pos < ci->top.p; pos++) {
|
||||
if (o == s2v(base + pos))
|
||||
return pos;
|
||||
}
|
||||
return -1; /* not found */
|
||||
}
|
||||
|
||||
|
||||
@@ -668,7 +682,7 @@ static const char *getupvalname (CallInfo *ci, const TValue *o,
|
||||
LClosure *c = ci_func(ci);
|
||||
int i;
|
||||
for (i = 0; i < c->nupvalues; i++) {
|
||||
if (c->upvals[i]->v == o) {
|
||||
if (c->upvals[i]->v.p == o) {
|
||||
*name = upvalname(c->p, i);
|
||||
return "upvalue";
|
||||
}
|
||||
@@ -677,23 +691,64 @@ static const char *getupvalname (CallInfo *ci, const TValue *o,
|
||||
}
|
||||
|
||||
|
||||
static const char *formatvarinfo (lua_State *L, const char *kind,
|
||||
const char *name) {
|
||||
if (kind == NULL)
|
||||
return ""; /* no information */
|
||||
else
|
||||
return luaO_pushfstring(L, " (%s '%s')", kind, name);
|
||||
}
|
||||
|
||||
/*
|
||||
** Build a string with a "description" for the value 'o', such as
|
||||
** "variable 'x'" or "upvalue 'y'".
|
||||
*/
|
||||
static const char *varinfo (lua_State *L, const TValue *o) {
|
||||
const char *name = NULL; /* to avoid warnings */
|
||||
CallInfo *ci = L->ci;
|
||||
const char *name = NULL; /* to avoid warnings */
|
||||
const char *kind = NULL;
|
||||
if (isLua(ci)) {
|
||||
kind = getupvalname(ci, o, &name); /* check whether 'o' is an upvalue */
|
||||
if (!kind && isinstack(ci, o)) /* no? try a register */
|
||||
kind = getobjname(ci_func(ci)->p, currentpc(ci),
|
||||
cast_int(cast(StkId, o) - (ci->func + 1)), &name);
|
||||
if (!kind) { /* not an upvalue? */
|
||||
int reg = instack(ci, o); /* try a register */
|
||||
if (reg >= 0) /* is 'o' a register? */
|
||||
kind = getobjname(ci_func(ci)->p, currentpc(ci), reg, &name);
|
||||
}
|
||||
}
|
||||
return (kind) ? luaO_pushfstring(L, " (%s '%s')", kind, name) : "";
|
||||
return formatvarinfo(L, kind, name);
|
||||
}
|
||||
|
||||
|
||||
l_noret luaG_typeerror (lua_State *L, const TValue *o, const char *op) {
|
||||
/*
|
||||
** Raise a type error
|
||||
*/
|
||||
static l_noret typeerror (lua_State *L, const TValue *o, const char *op,
|
||||
const char *extra) {
|
||||
const char *t = luaT_objtypename(L, o);
|
||||
luaG_runerror(L, "attempt to %s a %s value%s", op, t, varinfo(L, o));
|
||||
luaG_runerror(L, "attempt to %s a %s value%s", op, t, extra);
|
||||
}
|
||||
|
||||
|
||||
/*
|
||||
** Raise a type error with "standard" information about the faulty
|
||||
** object 'o' (using 'varinfo').
|
||||
*/
|
||||
l_noret luaG_typeerror (lua_State *L, const TValue *o, const char *op) {
|
||||
typeerror(L, o, op, varinfo(L, o));
|
||||
}
|
||||
|
||||
|
||||
/*
|
||||
** Raise an error for calling a non-callable object. Try to find a name
|
||||
** for the object based on how it was called ('funcnamefromcall'); if it
|
||||
** cannot get a name there, try 'varinfo'.
|
||||
*/
|
||||
l_noret luaG_callerror (lua_State *L, const TValue *o) {
|
||||
CallInfo *ci = L->ci;
|
||||
const char *name = NULL; /* to avoid warnings */
|
||||
const char *kind = funcnamefromcall(L, ci, &name);
|
||||
const char *extra = kind ? formatvarinfo(L, kind, name) : varinfo(L, o);
|
||||
typeerror(L, o, "call", extra);
|
||||
}
|
||||
|
||||
|
||||
@@ -722,7 +777,7 @@ l_noret luaG_opinterror (lua_State *L, const TValue *p1,
|
||||
*/
|
||||
l_noret luaG_tointerror (lua_State *L, const TValue *p1, const TValue *p2) {
|
||||
lua_Integer temp;
|
||||
if (!tointegerns(p1, &temp))
|
||||
if (!luaV_tointegerns(p1, &temp, LUA_FLOORN2I))
|
||||
p2 = p1;
|
||||
luaG_runerror(L, "number%s has no integer representation", varinfo(L, p2));
|
||||
}
|
||||
@@ -755,10 +810,10 @@ l_noret luaG_errormsg (lua_State *L) {
|
||||
if (L->errfunc != 0) { /* is there an error handling function? */
|
||||
StkId errfunc = restorestack(L, L->errfunc);
|
||||
lua_assert(ttisfunction(s2v(errfunc)));
|
||||
setobjs2s(L, L->top, L->top - 1); /* move argument */
|
||||
setobjs2s(L, L->top - 1, errfunc); /* push function */
|
||||
L->top++; /* assume EXTRA_STACK */
|
||||
luaD_callnoyield(L, L->top - 2, 1); /* call it */
|
||||
setobjs2s(L, L->top.p, L->top.p - 1); /* move argument */
|
||||
setobjs2s(L, L->top.p - 1, errfunc); /* push function */
|
||||
L->top.p++; /* assume EXTRA_STACK */
|
||||
luaD_callnoyield(L, L->top.p - 2, 1); /* call it */
|
||||
}
|
||||
luaD_throw(L, LUA_ERRRUN);
|
||||
}
|
||||
@@ -772,24 +827,41 @@ l_noret luaG_runerror (lua_State *L, const char *fmt, ...) {
|
||||
va_start(argp, fmt);
|
||||
msg = luaO_pushvfstring(L, fmt, argp); /* format message */
|
||||
va_end(argp);
|
||||
if (isLua(ci)) /* if Lua function, add source:line information */
|
||||
if (isLua(ci)) { /* if Lua function, add source:line information */
|
||||
luaG_addinfo(L, msg, ci_func(ci)->p->source, getcurrentline(ci));
|
||||
setobjs2s(L, L->top.p - 2, L->top.p - 1); /* remove 'msg' */
|
||||
L->top.p--;
|
||||
}
|
||||
luaG_errormsg(L);
|
||||
}
|
||||
|
||||
|
||||
/*
|
||||
** Check whether new instruction 'newpc' is in a different line from
|
||||
** previous instruction 'oldpc'.
|
||||
** previous instruction 'oldpc'. More often than not, 'newpc' is only
|
||||
** one or a few instructions after 'oldpc' (it must be after, see
|
||||
** caller), so try to avoid calling 'luaG_getfuncline'. If they are
|
||||
** too far apart, there is a good chance of a ABSLINEINFO in the way,
|
||||
** so it goes directly to 'luaG_getfuncline'.
|
||||
*/
|
||||
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));
|
||||
if (newpc - oldpc < MAXIWTHABS / 2) { /* not too far apart? */
|
||||
int delta = 0; /* line difference */
|
||||
int pc = oldpc;
|
||||
for (;;) {
|
||||
int lineinfo = p->lineinfo[++pc];
|
||||
if (lineinfo == ABSLINEINFO)
|
||||
break; /* cannot compute delta; fall through */
|
||||
delta += lineinfo;
|
||||
if (pc == newpc)
|
||||
return (delta != 0); /* delta computed successfully */
|
||||
}
|
||||
}
|
||||
return 0; /* no line changes between positions */
|
||||
/* either instructions are too far apart or there is an absolute line
|
||||
info in the way; compute line difference explicitly */
|
||||
return (luaG_getfuncline(p, oldpc) != luaG_getfuncline(p, newpc));
|
||||
}
|
||||
|
||||
|
||||
@@ -797,20 +869,19 @@ static int changedline (const Proto *p, int oldpc, int newpc) {
|
||||
** 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.)
|
||||
** function, 'npci' will be zero and will test as a new line whatever
|
||||
** the value of 'oldpc'. Some exceptional conditions may return to
|
||||
** a function without setting 'oldpc'. In that case, 'oldpc' may be
|
||||
** invalid; if so, use zero as a valid value. (A wrong but valid 'oldpc'
|
||||
** at most causes an extra call to a line hook.)
|
||||
** This function is not "Protected" when called, so it should correct
|
||||
** 'L->top.p' before calling anything that can run the GC.
|
||||
*/
|
||||
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' */
|
||||
@@ -826,15 +897,16 @@ int luaG_traceexec (lua_State *L, const Instruction *pc) {
|
||||
ci->callstatus &= ~CIST_HOOKYIELD; /* erase mark */
|
||||
return 1; /* do not call hook again (VM yielded, so it did not move) */
|
||||
}
|
||||
if (!isIT(*(ci->u.l.savedpc - 1)))
|
||||
L->top = ci->top; /* prepare top */
|
||||
if (!isIT(*(ci->u.l.savedpc - 1))) /* top not being used? */
|
||||
L->top.p = ci->top.p; /* correct top */
|
||||
if (counthook)
|
||||
luaD_hook(L, LUA_HOOKCOUNT, -1, 0, 0); /* call count hook */
|
||||
if (mask & LUA_MASKLINE) {
|
||||
/* 'L->oldpc' may be invalid; use zero in this case */
|
||||
int oldpc = (L->oldpc < p->sizecode) ? L->oldpc : 0;
|
||||
int npci = pcRel(pc, p);
|
||||
if (npci == 0 || /* call linehook when enter a new function, */
|
||||
pc <= invpcRel(oldpc, p) || /* when jump back (loop), or when */
|
||||
changedline(p, oldpc, npci)) { /* enter new line */
|
||||
if (npci <= oldpc || /* call hook when jump back (loop), */
|
||||
changedline(p, oldpc, npci)) { /* or when enter new line */
|
||||
int newline = luaG_getfuncline(p, npci);
|
||||
luaD_hook(L, LUA_HOOKLINE, newline, 0, 0); /* call line hook */
|
||||
}
|
||||
|
||||
13
ldebug.h
13
ldebug.h
@@ -15,7 +15,7 @@
|
||||
|
||||
|
||||
/* Active Lua function (given call info) */
|
||||
#define ci_func(ci) (clLvalue(s2v((ci)->func)))
|
||||
#define ci_func(ci) (clLvalue(s2v((ci)->func.p)))
|
||||
|
||||
|
||||
#define resethookcount(L) (L->hookcount = L->basehookcount)
|
||||
@@ -26,11 +26,22 @@
|
||||
*/
|
||||
#define ABSLINEINFO (-0x80)
|
||||
|
||||
|
||||
/*
|
||||
** MAXimum number of successive Instructions WiTHout ABSolute line
|
||||
** information. (A power of two allows fast divisions.)
|
||||
*/
|
||||
#if !defined(MAXIWTHABS)
|
||||
#define MAXIWTHABS 128
|
||||
#endif
|
||||
|
||||
|
||||
LUAI_FUNC int luaG_getfuncline (const Proto *f, int pc);
|
||||
LUAI_FUNC const char *luaG_findlocal (lua_State *L, CallInfo *ci, int n,
|
||||
StkId *pos);
|
||||
LUAI_FUNC l_noret luaG_typeerror (lua_State *L, const TValue *o,
|
||||
const char *opname);
|
||||
LUAI_FUNC l_noret luaG_callerror (lua_State *L, const TValue *o);
|
||||
LUAI_FUNC l_noret luaG_forerror (lua_State *L, const TValue *o,
|
||||
const char *what);
|
||||
LUAI_FUNC l_noret luaG_concaterror (lua_State *L, const TValue *p1,
|
||||
|
||||
21
ldo.h
21
ldo.h
@@ -8,6 +8,7 @@
|
||||
#define ldo_h
|
||||
|
||||
|
||||
#include "llimits.h"
|
||||
#include "lobject.h"
|
||||
#include "lstate.h"
|
||||
#include "lzio.h"
|
||||
@@ -23,7 +24,7 @@
|
||||
** at every check.
|
||||
*/
|
||||
#define luaD_checkstackaux(L,n,pre,pos) \
|
||||
if (L->stack_last - L->top <= (n)) \
|
||||
if (l_unlikely(L->stack_last.p - L->top.p <= (n))) \
|
||||
{ pre; luaD_growstack(L, n, 1); pos; } \
|
||||
else { condmovestack(L,pre,pos); }
|
||||
|
||||
@@ -32,11 +33,18 @@
|
||||
|
||||
|
||||
|
||||
#define savestack(L,p) ((char *)(p) - (char *)L->stack)
|
||||
#define restorestack(L,n) ((StkId)((char *)L->stack + (n)))
|
||||
#define savestack(L,pt) (cast_charp(pt) - cast_charp(L->stack.p))
|
||||
#define restorestack(L,n) cast(StkId, cast_charp(L->stack.p) + (n))
|
||||
|
||||
|
||||
/* macro to check stack size, preserving 'p' */
|
||||
#define checkstackp(L,n,p) \
|
||||
luaD_checkstackaux(L, n, \
|
||||
ptrdiff_t t__ = savestack(L, p), /* save 'p' */ \
|
||||
p = restorestack(L, t__)) /* 'pos' part: restore 'p' */
|
||||
|
||||
|
||||
/* macro to check stack size and GC, preserving 'p' */
|
||||
#define checkstackGCp(L,n,p) \
|
||||
luaD_checkstackaux(L, n, \
|
||||
ptrdiff_t t__ = savestack(L, p); /* save 'p' */ \
|
||||
@@ -58,10 +66,13 @@ LUAI_FUNC int luaD_protectedparser (lua_State *L, ZIO *z, const char *name,
|
||||
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 int luaD_pretailcall (lua_State *L, CallInfo *ci, StkId func,
|
||||
int narg1, int delta);
|
||||
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);
|
||||
LUAI_FUNC StkId luaD_tryfuncTM (lua_State *L, StkId func);
|
||||
LUAI_FUNC int luaD_closeprotected (lua_State *L, ptrdiff_t level, int status);
|
||||
LUAI_FUNC int luaD_pcall (lua_State *L, Pfunc func, void *u,
|
||||
ptrdiff_t oldtop, ptrdiff_t ef);
|
||||
LUAI_FUNC void luaD_poscall (lua_State *L, CallInfo *ci, int nres);
|
||||
|
||||
8
ldump.c
8
ldump.c
@@ -10,6 +10,7 @@
|
||||
#include "lprefix.h"
|
||||
|
||||
|
||||
#include <limits.h>
|
||||
#include <stddef.h>
|
||||
|
||||
#include "lua.h"
|
||||
@@ -55,8 +56,11 @@ static void dumpByte (DumpState *D, int y) {
|
||||
}
|
||||
|
||||
|
||||
/* dumpInt Buff Size */
|
||||
#define DIBS ((sizeof(size_t) * 8 / 7) + 1)
|
||||
/*
|
||||
** 'dumpSize' buffer size: each byte can store up to 7 bits. (The "+6"
|
||||
** rounds up the division.)
|
||||
*/
|
||||
#define DIBS ((sizeof(size_t) * CHAR_BIT + 6) / 7)
|
||||
|
||||
static void dumpSize (DumpState *D, size_t x) {
|
||||
lu_byte buff[DIBS];
|
||||
|
||||
194
lfunc.c
194
lfunc.c
@@ -50,10 +50,10 @@ void luaF_initupvals (lua_State *L, LClosure *cl) {
|
||||
for (i = 0; i < cl->nupvalues; i++) {
|
||||
GCObject *o = luaC_newobj(L, LUA_VUPVAL, sizeof(UpVal));
|
||||
UpVal *uv = gco2upv(o);
|
||||
uv->v = &uv->u.value; /* make it closed */
|
||||
setnilvalue(uv->v);
|
||||
uv->v.p = &uv->u.value; /* make it closed */
|
||||
setnilvalue(uv->v.p);
|
||||
cl->upvals[i] = uv;
|
||||
luaC_objbarrier(L, cl, o);
|
||||
luaC_objbarrier(L, cl, uv);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -62,12 +62,11 @@ void luaF_initupvals (lua_State *L, LClosure *cl) {
|
||||
** Create a new upvalue at the given level, and link it to the list of
|
||||
** open upvalues of 'L' after entry 'prev'.
|
||||
**/
|
||||
static UpVal *newupval (lua_State *L, int tbc, StkId level, UpVal **prev) {
|
||||
static UpVal *newupval (lua_State *L, StkId level, UpVal **prev) {
|
||||
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 */
|
||||
uv->tbc = tbc;
|
||||
uv->v.p = s2v(level); /* current value lives in the stack */
|
||||
uv->u.open.next = next; /* link it to list of open upvalues */
|
||||
uv->u.open.previous = prev;
|
||||
if (next)
|
||||
@@ -96,119 +95,87 @@ UpVal *luaF_findupval (lua_State *L, StkId level) {
|
||||
pp = &p->u.open.next;
|
||||
}
|
||||
/* not found: create a new upvalue after 'pp' */
|
||||
return newupval(L, 0, level, pp);
|
||||
}
|
||||
|
||||
|
||||
static void callclose (lua_State *L, void *ud) {
|
||||
UNUSED(ud);
|
||||
luaD_callnoyield(L, L->top - 3, 0);
|
||||
return newupval(L, level, pp);
|
||||
}
|
||||
|
||||
|
||||
/*
|
||||
** Prepare closing method plus its arguments for object 'obj' with
|
||||
** error message 'err'. (This function assumes EXTRA_STACK.)
|
||||
** Call closing method for object 'obj' with error message 'err'. The
|
||||
** boolean 'yy' controls whether the call is yieldable.
|
||||
** (This function assumes EXTRA_STACK.)
|
||||
*/
|
||||
static int prepclosingmethod (lua_State *L, TValue *obj, TValue *err) {
|
||||
StkId top = L->top;
|
||||
static void callclosemethod (lua_State *L, TValue *obj, TValue *err, int yy) {
|
||||
StkId top = L->top.p;
|
||||
const TValue *tm = luaT_gettmbyobj(L, obj, TM_CLOSE);
|
||||
if (ttisnil(tm)) /* no metamethod? */
|
||||
return 0; /* nothing to call */
|
||||
setobj2s(L, top, tm); /* will call metamethod... */
|
||||
setobj2s(L, top + 1, obj); /* with 'self' as the 1st argument */
|
||||
setobj2s(L, top + 2, err); /* and error msg. as 2nd argument */
|
||||
L->top = top + 3; /* add function and arguments */
|
||||
return 1;
|
||||
L->top.p = top + 3; /* add function and arguments */
|
||||
if (yy)
|
||||
luaD_call(L, top, 0);
|
||||
else
|
||||
luaD_callnoyield(L, top, 0);
|
||||
}
|
||||
|
||||
|
||||
/*
|
||||
** Raise an error with message 'msg', inserting the name of the
|
||||
** local variable at position 'level' in the stack.
|
||||
** Check whether object at given level has a close metamethod and raise
|
||||
** an error if not.
|
||||
*/
|
||||
static void varerror (lua_State *L, StkId level, const char *msg) {
|
||||
int idx = cast_int(level - L->ci->func);
|
||||
const char *vname = luaG_findlocal(L, L->ci, idx, NULL);
|
||||
if (vname == NULL) vname = "?";
|
||||
luaG_runerror(L, msg, vname);
|
||||
static void checkclosemth (lua_State *L, StkId level) {
|
||||
const TValue *tm = luaT_gettmbyobj(L, s2v(level), TM_CLOSE);
|
||||
if (ttisnil(tm)) { /* no metamethod? */
|
||||
int idx = cast_int(level - L->ci->func.p); /* variable index */
|
||||
const char *vname = luaG_findlocal(L, L->ci, idx, NULL);
|
||||
if (vname == NULL) vname = "?";
|
||||
luaG_runerror(L, "variable '%s' got a non-closable value", vname);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
/*
|
||||
** Prepare and call a closing method. If status is OK, code is still
|
||||
** inside the original protected call, and so any error will be handled
|
||||
** there. Otherwise, a previous error already activated the original
|
||||
** protected call, and so the call to the closing method must be
|
||||
** protected here. (A status == CLOSEPROTECT behaves like a previous
|
||||
** error, to also run the closing method in protected mode).
|
||||
** If status is OK, the call to the closing method will be pushed
|
||||
** at the top of the stack. Otherwise, values are pushed after
|
||||
** the 'level' of the upvalue being closed, as everything after
|
||||
** that won't be used again.
|
||||
** Prepare and call a closing method.
|
||||
** If status is CLOSEKTOP, the call to the closing method will be pushed
|
||||
** at the top of the stack. Otherwise, values can be pushed right after
|
||||
** the 'level' of the upvalue being closed, as everything after that
|
||||
** won't be used again.
|
||||
*/
|
||||
static int callclosemth (lua_State *L, StkId level, int status) {
|
||||
static void prepcallclosemth (lua_State *L, StkId level, int status, int yy) {
|
||||
TValue *uv = s2v(level); /* value being closed */
|
||||
if (likely(status == LUA_OK)) {
|
||||
if (prepclosingmethod(L, uv, &G(L)->nilvalue)) /* something to call? */
|
||||
callclose(L, NULL); /* call closing method */
|
||||
else if (!l_isfalse(uv)) /* non-closable non-false value? */
|
||||
varerror(L, level, "attempt to close non-closable variable '%s'");
|
||||
TValue *errobj;
|
||||
if (status == CLOSEKTOP)
|
||||
errobj = &G(L)->nilvalue; /* error object is nil */
|
||||
else { /* 'luaD_seterrorobj' will set top to level + 2 */
|
||||
errobj = s2v(level + 1); /* error object goes after 'uv' */
|
||||
luaD_seterrorobj(L, status, level + 1); /* set error object */
|
||||
}
|
||||
else { /* must close the object in protected mode */
|
||||
ptrdiff_t oldtop;
|
||||
level++; /* space for error message */
|
||||
oldtop = savestack(L, level + 1); /* top will be after that */
|
||||
luaD_seterrorobj(L, status, level); /* set error message */
|
||||
if (prepclosingmethod(L, uv, s2v(level))) { /* something to call? */
|
||||
int newstatus = luaD_pcall(L, callclose, NULL, oldtop, 0);
|
||||
if (newstatus != LUA_OK && status == CLOSEPROTECT) /* first error? */
|
||||
status = newstatus; /* this will be the new error */
|
||||
else {
|
||||
if (newstatus != LUA_OK) /* suppressed error? */
|
||||
luaE_warnerror(L, "__close metamethod");
|
||||
/* leave original error (or nil) on top */
|
||||
L->top = restorestack(L, oldtop);
|
||||
}
|
||||
}
|
||||
/* else no metamethod; ignore this case and keep original error */
|
||||
}
|
||||
return status;
|
||||
callclosemethod(L, uv, errobj, yy);
|
||||
}
|
||||
|
||||
|
||||
/*
|
||||
** Try to create a to-be-closed upvalue
|
||||
** (can raise a memory-allocation error)
|
||||
** Maximum value for deltas in 'tbclist', dependent on the type
|
||||
** of delta. (This macro assumes that an 'L' is in scope where it
|
||||
** is used.)
|
||||
*/
|
||||
static void trynewtbcupval (lua_State *L, void *ud) {
|
||||
newupval(L, 1, cast(StkId, ud), &L->openupval);
|
||||
}
|
||||
#define MAXDELTA \
|
||||
((256ul << ((sizeof(L->stack.p->tbclist.delta) - 1) * 8)) - 1)
|
||||
|
||||
|
||||
/*
|
||||
** Create a to-be-closed upvalue. If there is a memory error
|
||||
** when creating the upvalue, the closing method must be called here,
|
||||
** as there is no upvalue to call it later.
|
||||
** Insert a variable in the list of to-be-closed variables.
|
||||
*/
|
||||
void luaF_newtbcupval (lua_State *L, StkId level) {
|
||||
TValue *obj = s2v(level);
|
||||
lua_assert(L->openupval == NULL || uplevel(L->openupval) < level);
|
||||
if (!l_isfalse(obj)) { /* false doesn't need to be closed */
|
||||
int status;
|
||||
const TValue *tm = luaT_gettmbyobj(L, obj, TM_CLOSE);
|
||||
if (ttisnil(tm)) /* no metamethod? */
|
||||
varerror(L, level, "variable '%s' got a non-closable value");
|
||||
status = luaD_rawrunprotected(L, trynewtbcupval, level);
|
||||
if (unlikely(status != LUA_OK)) { /* memory error creating upvalue? */
|
||||
lua_assert(status == LUA_ERRMEM);
|
||||
luaD_seterrorobj(L, LUA_ERRMEM, level + 1); /* save error message */
|
||||
/* next call must succeed, as object is closable */
|
||||
prepclosingmethod(L, s2v(level), s2v(level + 1));
|
||||
callclose(L, NULL); /* call closing method */
|
||||
luaD_throw(L, LUA_ERRMEM); /* throw memory error */
|
||||
}
|
||||
lua_assert(level > L->tbclist.p);
|
||||
if (l_isfalse(s2v(level)))
|
||||
return; /* false doesn't need to be closed */
|
||||
checkclosemth(L, level); /* value must have a close method */
|
||||
while (cast_uint(level - L->tbclist.p) > MAXDELTA) {
|
||||
L->tbclist.p += MAXDELTA; /* create a dummy node at maximum delta */
|
||||
L->tbclist.p->tbclist.delta = 0;
|
||||
}
|
||||
level->tbclist.delta = cast(unsigned short, level - L->tbclist.p);
|
||||
L->tbclist.p = level;
|
||||
}
|
||||
|
||||
|
||||
@@ -220,26 +187,53 @@ void luaF_unlinkupval (UpVal *uv) {
|
||||
}
|
||||
|
||||
|
||||
int luaF_close (lua_State *L, StkId level, int status) {
|
||||
/*
|
||||
** Close all upvalues up to the given stack level.
|
||||
*/
|
||||
void luaF_closeupval (lua_State *L, StkId level) {
|
||||
UpVal *uv;
|
||||
while ((uv = L->openupval) != NULL && uplevel(uv) >= level) {
|
||||
StkId upl; /* stack index pointed by 'uv' */
|
||||
while ((uv = L->openupval) != NULL && (upl = uplevel(uv)) >= level) {
|
||||
TValue *slot = &uv->u.value; /* new position for value */
|
||||
lua_assert(uplevel(uv) < L->top);
|
||||
if (uv->tbc && status != NOCLOSINGMETH) {
|
||||
/* must run closing method, which may change the stack */
|
||||
ptrdiff_t levelrel = savestack(L, level);
|
||||
status = callclosemth(L, uplevel(uv), status);
|
||||
level = restorestack(L, levelrel);
|
||||
}
|
||||
luaF_unlinkupval(uv);
|
||||
setobj(L, slot, uv->v); /* move value to upvalue slot */
|
||||
uv->v = slot; /* now current value lives here */
|
||||
lua_assert(uplevel(uv) < L->top.p);
|
||||
luaF_unlinkupval(uv); /* remove upvalue from 'openupval' list */
|
||||
setobj(L, slot, uv->v.p); /* move value to upvalue slot */
|
||||
uv->v.p = slot; /* now current value lives here */
|
||||
if (!iswhite(uv)) { /* neither white nor dead? */
|
||||
nw2black(uv); /* closed upvalues cannot be gray */
|
||||
luaC_barrier(L, uv, slot);
|
||||
}
|
||||
}
|
||||
return status;
|
||||
}
|
||||
|
||||
|
||||
/*
|
||||
** Remove first element from the tbclist plus its dummy nodes.
|
||||
*/
|
||||
static void poptbclist (lua_State *L) {
|
||||
StkId tbc = L->tbclist.p;
|
||||
lua_assert(tbc->tbclist.delta > 0); /* first element cannot be dummy */
|
||||
tbc -= tbc->tbclist.delta;
|
||||
while (tbc > L->stack.p && tbc->tbclist.delta == 0)
|
||||
tbc -= MAXDELTA; /* remove dummy nodes */
|
||||
L->tbclist.p = tbc;
|
||||
}
|
||||
|
||||
|
||||
/*
|
||||
** Close all upvalues and to-be-closed variables up to the given stack
|
||||
** level. Return restored 'level'.
|
||||
*/
|
||||
StkId luaF_close (lua_State *L, StkId level, int status, int yy) {
|
||||
ptrdiff_t levelrel = savestack(L, level);
|
||||
luaF_closeupval(L, level); /* first, close the upvalues */
|
||||
while (L->tbclist.p >= level) { /* traverse tbc's down to that level */
|
||||
StkId tbc = L->tbclist.p; /* get variable index */
|
||||
poptbclist(L); /* remove it from list */
|
||||
prepcallclosemth(L, tbc, status, yy); /* close variable */
|
||||
level = restorestack(L, levelrel);
|
||||
}
|
||||
return level;
|
||||
}
|
||||
|
||||
|
||||
|
||||
17
lfunc.h
17
lfunc.h
@@ -29,10 +29,10 @@
|
||||
#define MAXUPVAL 255
|
||||
|
||||
|
||||
#define upisopen(up) ((up)->v != &(up)->u.value)
|
||||
#define upisopen(up) ((up)->v.p != &(up)->u.value)
|
||||
|
||||
|
||||
#define uplevel(up) check_exp(upisopen(up), cast(StkId, (up)->v))
|
||||
#define uplevel(up) check_exp(upisopen(up), cast(StkId, (up)->v.p))
|
||||
|
||||
|
||||
/*
|
||||
@@ -42,15 +42,9 @@
|
||||
#define MAXMISS 10
|
||||
|
||||
|
||||
/*
|
||||
** Special "status" for 'luaF_close'
|
||||
*/
|
||||
|
||||
/* close upvalues without running their closing methods */
|
||||
#define NOCLOSINGMETH (-1)
|
||||
|
||||
/* close upvalues running all closing methods in protected mode */
|
||||
#define CLOSEPROTECT (-2)
|
||||
/* special status to close upvalues preserving the top of the stack */
|
||||
#define CLOSEKTOP (-1)
|
||||
|
||||
|
||||
LUAI_FUNC Proto *luaF_newproto (lua_State *L);
|
||||
@@ -59,7 +53,8 @@ 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);
|
||||
LUAI_FUNC int luaF_close (lua_State *L, StkId level, int status);
|
||||
LUAI_FUNC void luaF_closeupval (lua_State *L, StkId level);
|
||||
LUAI_FUNC StkId luaF_close (lua_State *L, StkId level, int status, int yy);
|
||||
LUAI_FUNC void luaF_unlinkupval (UpVal *uv);
|
||||
LUAI_FUNC void luaF_freeproto (lua_State *L, Proto *f);
|
||||
LUAI_FUNC const char *luaF_getlocalname (const Proto *func, int local_number,
|
||||
|
||||
211
lgc.c
211
lgc.c
@@ -161,18 +161,17 @@ static void linkgclist_ (GCObject *o, GCObject **pnext, GCObject **list) {
|
||||
|
||||
|
||||
/*
|
||||
** 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 */
|
||||
}
|
||||
|
||||
|
||||
@@ -253,12 +252,13 @@ void luaC_fix (lua_State *L, GCObject *o) {
|
||||
|
||||
|
||||
/*
|
||||
** create a new collectable object (with given type and size) and link
|
||||
** it to 'allgc' list.
|
||||
** create a new collectable object (with given type, size, and offset)
|
||||
** and link it to 'allgc' list.
|
||||
*/
|
||||
GCObject *luaC_newobj (lua_State *L, int tt, size_t sz) {
|
||||
GCObject *luaC_newobjdt (lua_State *L, int tt, size_t sz, size_t offset) {
|
||||
global_State *g = G(L);
|
||||
GCObject *o = cast(GCObject *, luaM_newobject(L, novariant(tt), sz));
|
||||
char *p = cast_charp(luaM_newobject(L, novariant(tt), sz));
|
||||
GCObject *o = cast(GCObject *, p + offset);
|
||||
o->marked = luaC_white(g);
|
||||
o->tt = tt;
|
||||
o->next = g->allgc;
|
||||
@@ -266,6 +266,11 @@ GCObject *luaC_newobj (lua_State *L, int tt, size_t sz) {
|
||||
return o;
|
||||
}
|
||||
|
||||
|
||||
GCObject *luaC_newobj (lua_State *L, int tt, size_t sz) {
|
||||
return luaC_newobjdt(L, tt, sz, 0);
|
||||
}
|
||||
|
||||
/* }====================================================== */
|
||||
|
||||
|
||||
@@ -301,15 +306,15 @@ static void reallymarkobject (global_State *g, GCObject *o) {
|
||||
if (upisopen(uv))
|
||||
set2gray(uv); /* open upvalues are kept gray */
|
||||
else
|
||||
set2black(o); /* closed upvalues are visited here */
|
||||
markvalue(g, uv->v); /* mark its content */
|
||||
set2black(uv); /* closed upvalues are visited here */
|
||||
markvalue(g, uv->v.p); /* mark its content */
|
||||
break;
|
||||
}
|
||||
case LUA_VUSERDATA: {
|
||||
Udata *u = gco2u(o);
|
||||
if (u->nuvalue == 0) { /* no user values? */
|
||||
markobjectN(g, u->metatable); /* mark its metatable */
|
||||
set2black(o); /* nothing else to mark */
|
||||
set2black(u); /* nothing else to mark */
|
||||
break;
|
||||
}
|
||||
/* else... */
|
||||
@@ -377,7 +382,7 @@ static int remarkupvals (global_State *g) {
|
||||
work++;
|
||||
if (!iswhite(uv)) { /* upvalue already visited? */
|
||||
lua_assert(upisopen(uv) && isgray(uv));
|
||||
markvalue(g, uv->v); /* mark its value */
|
||||
markvalue(g, uv->v.p); /* mark its value */
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -621,21 +626,20 @@ static int traverseLclosure (global_State *g, LClosure *cl) {
|
||||
*/
|
||||
static int traversethread (global_State *g, lua_State *th) {
|
||||
UpVal *uv;
|
||||
StkId o = th->stack;
|
||||
StkId o = th->stack.p;
|
||||
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 ||
|
||||
th->openupval == NULL || isintwups(th));
|
||||
for (; o < th->top; o++) /* mark live elements in the stack */
|
||||
for (; o < th->top.p; o++) /* mark live elements in the stack */
|
||||
markvalue(g, s2v(o));
|
||||
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.p + 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 */
|
||||
@@ -644,7 +648,7 @@ 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);
|
||||
}
|
||||
|
||||
|
||||
@@ -771,12 +775,16 @@ static void freeobj (lua_State *L, GCObject *o) {
|
||||
case LUA_VUPVAL:
|
||||
freeupval(L, gco2upv(o));
|
||||
break;
|
||||
case LUA_VLCL:
|
||||
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_VCCL:
|
||||
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_VTABLE:
|
||||
luaH_free(L, gco2t(o));
|
||||
break;
|
||||
@@ -788,13 +796,17 @@ static void freeobj (lua_State *L, GCObject *o) {
|
||||
luaM_freemem(L, o, sizeudata(u->nuvalue, u->len));
|
||||
break;
|
||||
}
|
||||
case LUA_VSHRSTR:
|
||||
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_VLNGSTR:
|
||||
luaM_freemem(L, o, sizelstring(gco2ts(o)->u.lnglen));
|
||||
}
|
||||
case LUA_VLNGSTR: {
|
||||
TString *ts = gco2ts(o);
|
||||
luaM_freemem(L, ts, sizelstring(ts->u.lnglen));
|
||||
break;
|
||||
}
|
||||
default: lua_assert(0);
|
||||
}
|
||||
}
|
||||
@@ -886,7 +898,7 @@ static GCObject *udata2finalize (global_State *g) {
|
||||
|
||||
static void dothecall (lua_State *L, void *ud) {
|
||||
UNUSED(ud);
|
||||
luaD_callnoyield(L, L->top - 2, 0);
|
||||
luaD_callnoyield(L, L->top.p - 2, 0);
|
||||
}
|
||||
|
||||
|
||||
@@ -900,19 +912,19 @@ static void GCTM (lua_State *L) {
|
||||
if (!notm(tm)) { /* is there a finalizer? */
|
||||
int status;
|
||||
lu_byte oldah = L->allowhook;
|
||||
int running = g->gcrunning;
|
||||
int oldgcstp = g->gcstp;
|
||||
g->gcstp |= GCSTPGC; /* avoid GC steps */
|
||||
L->allowhook = 0; /* stop debug hooks during GC metamethod */
|
||||
g->gcrunning = 0; /* avoid GC steps */
|
||||
setobj2s(L, L->top++, tm); /* push finalizer... */
|
||||
setobj2s(L, L->top++, &v); /* ... and its argument */
|
||||
setobj2s(L, L->top.p++, tm); /* push finalizer... */
|
||||
setobj2s(L, L->top.p++, &v); /* ... and its argument */
|
||||
L->ci->callstatus |= CIST_FIN; /* will run a finalizer */
|
||||
status = luaD_pcall(L, dothecall, NULL, savestack(L, L->top - 2), 0);
|
||||
status = luaD_pcall(L, dothecall, NULL, savestack(L, L->top.p - 2), 0);
|
||||
L->ci->callstatus &= ~CIST_FIN; /* not running a finalizer anymore */
|
||||
L->allowhook = oldah; /* restore hooks */
|
||||
g->gcrunning = running; /* restore state */
|
||||
if (unlikely(status != LUA_OK)) { /* error while running __gc? */
|
||||
luaE_warnerror(L, "__gc metamethod");
|
||||
L->top--; /* pops error object */
|
||||
g->gcstp = oldgcstp; /* restore state */
|
||||
if (l_unlikely(status != LUA_OK)) { /* error while running __gc? */
|
||||
luaE_warnerror(L, "__gc");
|
||||
L->top.p--; /* pops error object */
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1005,7 +1017,8 @@ static void correctpointers (global_State *g, GCObject *o) {
|
||||
void luaC_checkfinalizer (lua_State *L, GCObject *o, Table *mt) {
|
||||
global_State *g = G(L);
|
||||
if (tofinalize(o) || /* obj. is already marked... */
|
||||
gfasttm(g, mt, TM_GC) == NULL) /* or has no finalizer? */
|
||||
gfasttm(g, mt, TM_GC) == NULL || /* or has no finalizer... */
|
||||
(g->gcstp & GCSTPCLS)) /* or closing state? */
|
||||
return; /* nothing to be done */
|
||||
else { /* move 'o' to 'finobj' list */
|
||||
GCObject **p;
|
||||
@@ -1034,7 +1047,25 @@ void luaC_checkfinalizer (lua_State *L, GCObject *o, Table *mt) {
|
||||
** =======================================================
|
||||
*/
|
||||
|
||||
static void setpause (global_State *g);
|
||||
|
||||
/*
|
||||
** Set the "time" to wait before starting a new GC cycle; cycle will
|
||||
** start when memory use hits the threshold of ('estimate' * pause /
|
||||
** PAUSEADJ). (Division by 'estimate' should be OK: it cannot be zero,
|
||||
** because Lua cannot even start with less than PAUSEADJ bytes).
|
||||
*/
|
||||
static void setpause (global_State *g) {
|
||||
l_mem threshold, debt;
|
||||
int pause = getgcparam(g->gcpause);
|
||||
l_mem estimate = g->GCestimate / PAUSEADJ; /* adjust 'estimate' */
|
||||
lua_assert(estimate > 0);
|
||||
threshold = (pause < MAX_LMEM / estimate) /* overflow? */
|
||||
? estimate * pause /* no overflow */
|
||||
: MAX_LMEM; /* overflow; truncate to maximum */
|
||||
debt = gettotalbytes(g) - threshold;
|
||||
if (debt > 0) debt = 0;
|
||||
luaE_setdebt(g, debt);
|
||||
}
|
||||
|
||||
|
||||
/*
|
||||
@@ -1278,6 +1309,15 @@ static void atomic2gen (lua_State *L, global_State *g) {
|
||||
}
|
||||
|
||||
|
||||
/*
|
||||
** Set debt for the next minor collection, which will happen when
|
||||
** memory grows 'genminormul'%.
|
||||
*/
|
||||
static void setminordebt (global_State *g) {
|
||||
luaE_setdebt(g, -(cast(l_mem, (gettotalbytes(g) / 100)) * g->genminormul));
|
||||
}
|
||||
|
||||
|
||||
/*
|
||||
** Enter generational mode. Must go until the end of an atomic cycle
|
||||
** to ensure that all objects are correctly marked and weak tables
|
||||
@@ -1290,6 +1330,7 @@ static lu_mem entergen (lua_State *L, global_State *g) {
|
||||
luaC_runtilstate(L, bitmask(GCSpropagate)); /* start new cycle */
|
||||
numobjs = atomic(L); /* propagates all and then do the atomic stuff */
|
||||
atomic2gen(L, g);
|
||||
setminordebt(g); /* set debt assuming next cycle will be minor */
|
||||
return numobjs;
|
||||
}
|
||||
|
||||
@@ -1335,15 +1376,6 @@ static lu_mem fullgen (lua_State *L, global_State *g) {
|
||||
}
|
||||
|
||||
|
||||
/*
|
||||
** Set debt for the next minor collection, which will happen when
|
||||
** memory grows 'genminormul'%.
|
||||
*/
|
||||
static void setminordebt (global_State *g) {
|
||||
luaE_setdebt(g, -(cast(l_mem, (gettotalbytes(g) / 100)) * g->genminormul));
|
||||
}
|
||||
|
||||
|
||||
/*
|
||||
** Does a major collection after last collection was a "bad collection".
|
||||
**
|
||||
@@ -1415,8 +1447,8 @@ static void genstep (lua_State *L, global_State *g) {
|
||||
lu_mem numobjs = fullgen(L, g); /* do a major collection */
|
||||
if (gettotalbytes(g) < majorbase + (majorinc / 2)) {
|
||||
/* collected at least half of memory growth since last major
|
||||
collection; keep doing minor collections */
|
||||
setminordebt(g);
|
||||
collection; keep doing minor collections. */
|
||||
lua_assert(g->lastatomic == 0);
|
||||
}
|
||||
else { /* bad collection */
|
||||
g->lastatomic = numobjs; /* signal that last collection was bad */
|
||||
@@ -1442,26 +1474,6 @@ static void genstep (lua_State *L, global_State *g) {
|
||||
*/
|
||||
|
||||
|
||||
/*
|
||||
** Set the "time" to wait before starting a new GC cycle; cycle will
|
||||
** start when memory use hits the threshold of ('estimate' * pause /
|
||||
** PAUSEADJ). (Division by 'estimate' should be OK: it cannot be zero,
|
||||
** because Lua cannot even start with less than PAUSEADJ bytes).
|
||||
*/
|
||||
static void setpause (global_State *g) {
|
||||
l_mem threshold, debt;
|
||||
int pause = getgcparam(g->gcpause);
|
||||
l_mem estimate = g->GCestimate / PAUSEADJ; /* adjust 'estimate' */
|
||||
lua_assert(estimate > 0);
|
||||
threshold = (pause < MAX_LMEM / estimate) /* overflow? */
|
||||
? estimate * pause /* no overflow */
|
||||
: MAX_LMEM; /* overflow; truncate to maximum */
|
||||
debt = gettotalbytes(g) - threshold;
|
||||
if (debt > 0) debt = 0;
|
||||
luaE_setdebt(g, debt);
|
||||
}
|
||||
|
||||
|
||||
/*
|
||||
** Enter first sweep phase.
|
||||
** The call to 'sweeptolive' makes the pointer point to an object
|
||||
@@ -1496,12 +1508,13 @@ static void deletelist (lua_State *L, GCObject *p, GCObject *limit) {
|
||||
*/
|
||||
void luaC_freeallobjects (lua_State *L) {
|
||||
global_State *g = G(L);
|
||||
g->gcstp = GCSTPCLS; /* no extra finalizers after here */
|
||||
luaC_changemode(L, KGC_INC);
|
||||
separatetobefnz(g, 1); /* separate all objects with finalizers */
|
||||
lua_assert(g->finobj == NULL);
|
||||
callallpendingfinalizers(L);
|
||||
deletelist(L, g->allgc, obj2gco(g->mainthread));
|
||||
deletelist(L, g->finobj, NULL);
|
||||
lua_assert(g->finobj == NULL); /* no new finalizers */
|
||||
deletelist(L, g->fixedgc, NULL); /* collect fixed objects */
|
||||
lua_assert(g->strt.nuse == 0);
|
||||
}
|
||||
@@ -1569,52 +1582,64 @@ static int sweepstep (lua_State *L, global_State *g,
|
||||
|
||||
static lu_mem singlestep (lua_State *L) {
|
||||
global_State *g = G(L);
|
||||
lu_mem work;
|
||||
lua_assert(!g->gcstopem); /* collector is not reentrant */
|
||||
g->gcstopem = 1; /* no emergency collections while collecting */
|
||||
switch (g->gcstate) {
|
||||
case GCSpause: {
|
||||
restartcollection(g);
|
||||
g->gcstate = GCSpropagate;
|
||||
return 1;
|
||||
work = 1;
|
||||
break;
|
||||
}
|
||||
case GCSpropagate: {
|
||||
if (g->gray == NULL) { /* no more gray objects? */
|
||||
g->gcstate = GCSenteratomic; /* finish propagate phase */
|
||||
return 0;
|
||||
work = 0;
|
||||
}
|
||||
else
|
||||
return propagatemark(g); /* traverse one gray object */
|
||||
work = propagatemark(g); /* traverse one gray object */
|
||||
break;
|
||||
}
|
||||
case GCSenteratomic: {
|
||||
lu_mem work = atomic(L); /* work is what was traversed by 'atomic' */
|
||||
work = atomic(L); /* work is what was traversed by 'atomic' */
|
||||
entersweep(L);
|
||||
g->GCestimate = gettotalbytes(g); /* first estimate */;
|
||||
return work;
|
||||
break;
|
||||
}
|
||||
case GCSswpallgc: { /* sweep "regular" objects */
|
||||
return sweepstep(L, g, GCSswpfinobj, &g->finobj);
|
||||
work = sweepstep(L, g, GCSswpfinobj, &g->finobj);
|
||||
break;
|
||||
}
|
||||
case GCSswpfinobj: { /* sweep objects with finalizers */
|
||||
return sweepstep(L, g, GCSswptobefnz, &g->tobefnz);
|
||||
work = sweepstep(L, g, GCSswptobefnz, &g->tobefnz);
|
||||
break;
|
||||
}
|
||||
case GCSswptobefnz: { /* sweep objects to be finalized */
|
||||
return sweepstep(L, g, GCSswpend, NULL);
|
||||
work = sweepstep(L, g, GCSswpend, NULL);
|
||||
break;
|
||||
}
|
||||
case GCSswpend: { /* finish sweeps */
|
||||
checkSizes(L, g);
|
||||
g->gcstate = GCScallfin;
|
||||
return 0;
|
||||
work = 0;
|
||||
break;
|
||||
}
|
||||
case GCScallfin: { /* call remaining finalizers */
|
||||
if (g->tobefnz && !g->gcemergency) {
|
||||
int n = runafewfinalizers(L, GCFINMAX);
|
||||
return n * GCFINALIZECOST;
|
||||
g->gcstopem = 0; /* ok collections during finalizers */
|
||||
work = runafewfinalizers(L, GCFINMAX) * GCFINALIZECOST;
|
||||
}
|
||||
else { /* emergency mode or no more finalizers */
|
||||
g->gcstate = GCSpause; /* finish collection */
|
||||
return 0;
|
||||
work = 0;
|
||||
}
|
||||
break;
|
||||
}
|
||||
default: lua_assert(0); return 0;
|
||||
}
|
||||
g->gcstopem = 0;
|
||||
return work;
|
||||
}
|
||||
|
||||
|
||||
@@ -1629,6 +1654,7 @@ void luaC_runtilstate (lua_State *L, int statesmask) {
|
||||
}
|
||||
|
||||
|
||||
|
||||
/*
|
||||
** Performs a basic incremental step. The debt and step size are
|
||||
** converted from bytes to "units of work"; then the function loops
|
||||
@@ -1655,12 +1681,15 @@ static void incstep (lua_State *L, global_State *g) {
|
||||
}
|
||||
|
||||
/*
|
||||
** performs a basic GC step if collector is running
|
||||
** Performs a basic GC step if collector is running. (If collector is
|
||||
** not running, set a reasonable debt to avoid it being called at
|
||||
** every single check.)
|
||||
*/
|
||||
void luaC_step (lua_State *L) {
|
||||
global_State *g = G(L);
|
||||
lua_assert(!g->gcemergency);
|
||||
if (g->gcrunning) { /* running? */
|
||||
if (!gcrunning(g)) /* not running? */
|
||||
luaE_setdebt(g, -2000);
|
||||
else {
|
||||
if(isdecGCmodegen(g))
|
||||
genstep(L, g);
|
||||
else
|
||||
|
||||
29
lgc.h
29
lgc.h
@@ -148,6 +148,16 @@
|
||||
*/
|
||||
#define isdecGCmodegen(g) (g->gckind == KGC_GEN || g->lastatomic != 0)
|
||||
|
||||
|
||||
/*
|
||||
** Control when GC is running:
|
||||
*/
|
||||
#define GCSTPUSR 1 /* bit true when GC stopped by user */
|
||||
#define GCSTPGC 2 /* bit true when GC stopped by itself */
|
||||
#define GCSTPCLS 4 /* bit true when closing Lua state */
|
||||
#define gcrunning(g) ((g)->gcstp == 0)
|
||||
|
||||
|
||||
/*
|
||||
** Does one step of collection when debt becomes positive. 'pre'/'pos'
|
||||
** allows some adjustments to be done only when needed. macro
|
||||
@@ -162,24 +172,27 @@
|
||||
#define luaC_checkGC(L) luaC_condGC(L,(void)0,(void)0)
|
||||
|
||||
|
||||
#define luaC_barrier(L,p,v) ( \
|
||||
(iscollectable(v) && isblack(p) && iswhite(gcvalue(v))) ? \
|
||||
luaC_barrier_(L,obj2gco(p),gcvalue(v)) : cast_void(0))
|
||||
|
||||
#define luaC_barrierback(L,p,v) ( \
|
||||
(iscollectable(v) && isblack(p) && iswhite(gcvalue(v))) ? \
|
||||
luaC_barrierback_(L,p) : cast_void(0))
|
||||
|
||||
#define luaC_objbarrier(L,p,o) ( \
|
||||
(isblack(p) && iswhite(o)) ? \
|
||||
luaC_barrier_(L,obj2gco(p),obj2gco(o)) : cast_void(0))
|
||||
|
||||
#define luaC_barrier(L,p,v) ( \
|
||||
iscollectable(v) ? luaC_objbarrier(L,p,gcvalue(v)) : cast_void(0))
|
||||
|
||||
#define luaC_objbarrierback(L,p,o) ( \
|
||||
(isblack(p) && iswhite(o)) ? luaC_barrierback_(L,p) : cast_void(0))
|
||||
|
||||
#define luaC_barrierback(L,p,v) ( \
|
||||
iscollectable(v) ? luaC_objbarrierback(L, p, gcvalue(v)) : cast_void(0))
|
||||
|
||||
LUAI_FUNC void luaC_fix (lua_State *L, GCObject *o);
|
||||
LUAI_FUNC void luaC_freeallobjects (lua_State *L);
|
||||
LUAI_FUNC void luaC_step (lua_State *L);
|
||||
LUAI_FUNC void luaC_runtilstate (lua_State *L, int statesmask);
|
||||
LUAI_FUNC void luaC_fullgc (lua_State *L, int isemergency);
|
||||
LUAI_FUNC GCObject *luaC_newobj (lua_State *L, int tt, size_t sz);
|
||||
LUAI_FUNC GCObject *luaC_newobjdt (lua_State *L, int tt, size_t sz,
|
||||
size_t offset);
|
||||
LUAI_FUNC void luaC_barrier_ (lua_State *L, GCObject *o, GCObject *v);
|
||||
LUAI_FUNC void luaC_barrierback_ (lua_State *L, GCObject *o);
|
||||
LUAI_FUNC void luaC_checkfinalizer (lua_State *L, GCObject *o, Table *mt);
|
||||
|
||||
35
liolib.c
35
liolib.c
@@ -52,12 +52,6 @@ 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) /* { */
|
||||
@@ -70,6 +64,12 @@ static int l_checkmode (const char *mode) {
|
||||
#define l_popen(L,c,m) (_popen(c,m))
|
||||
#define l_pclose(L,file) (_pclose(file))
|
||||
|
||||
#if !defined(l_checkmodep)
|
||||
/* Windows accepts "[rw][bt]?" as valid modes */
|
||||
#define l_checkmodep(m) ((m[0] == 'r' || m[0] == 'w') && \
|
||||
(m[1] == '\0' || ((m[1] == 'b' || m[1] == 't') && m[2] == '\0')))
|
||||
#endif
|
||||
|
||||
#else /* }{ */
|
||||
|
||||
/* ISO C definitions */
|
||||
@@ -83,6 +83,12 @@ static int l_checkmode (const char *mode) {
|
||||
|
||||
#endif /* } */
|
||||
|
||||
|
||||
#if !defined(l_checkmodep)
|
||||
/* By default, Lua accepts only "r" or "w" as valid modes */
|
||||
#define l_checkmodep(m) ((m[0] == 'r' || m[0] == 'w') && m[1] == '\0')
|
||||
#endif
|
||||
|
||||
/* }====================================================== */
|
||||
|
||||
|
||||
@@ -180,7 +186,7 @@ static int f_tostring (lua_State *L) {
|
||||
|
||||
static FILE *tofile (lua_State *L) {
|
||||
LStream *p = tolstream(L);
|
||||
if (isclosed(p))
|
||||
if (l_unlikely(isclosed(p)))
|
||||
luaL_error(L, "attempt to use a closed file");
|
||||
lua_assert(p->f);
|
||||
return p->f;
|
||||
@@ -255,7 +261,7 @@ static LStream *newfile (lua_State *L) {
|
||||
static void opencheck (lua_State *L, const char *fname, const char *mode) {
|
||||
LStream *p = newfile(L);
|
||||
p->f = fopen(fname, mode);
|
||||
if (p->f == NULL)
|
||||
if (l_unlikely(p->f == NULL))
|
||||
luaL_error(L, "cannot open file '%s' (%s)", fname, strerror(errno));
|
||||
}
|
||||
|
||||
@@ -303,7 +309,7 @@ static FILE *getiofile (lua_State *L, const char *findex) {
|
||||
LStream *p;
|
||||
lua_getfield(L, LUA_REGISTRYINDEX, findex);
|
||||
p = (LStream *)lua_touserdata(L, -1);
|
||||
if (isclosed(p))
|
||||
if (l_unlikely(isclosed(p)))
|
||||
luaL_error(L, "default %s file is closed", findex + IOPREF_LEN);
|
||||
return p->f;
|
||||
}
|
||||
@@ -430,7 +436,7 @@ typedef struct {
|
||||
** Add current char to buffer (if not out of space) and read next one
|
||||
*/
|
||||
static int nextc (RN *rn) {
|
||||
if (rn->n >= L_MAXLENNUM) { /* buffer overflow? */
|
||||
if (l_unlikely(rn->n >= L_MAXLENNUM)) { /* buffer overflow? */
|
||||
rn->buff[0] = '\0'; /* invalidate result */
|
||||
return 0; /* fail */
|
||||
}
|
||||
@@ -493,8 +499,8 @@ static int read_number (lua_State *L, FILE *f) {
|
||||
ungetc(rn.c, rn.f); /* unread look-ahead char */
|
||||
l_unlockfile(rn.f);
|
||||
rn.buff[rn.n] = '\0'; /* finish string */
|
||||
if (lua_stringtonumber(L, rn.buff)) /* is this a valid number? */
|
||||
return 1; /* ok */
|
||||
if (l_likely(lua_stringtonumber(L, rn.buff)))
|
||||
return 1; /* ok, it is a valid number */
|
||||
else { /* invalid format */
|
||||
lua_pushnil(L); /* "result" to be removed */
|
||||
return 0; /* read fails */
|
||||
@@ -670,7 +676,8 @@ static int g_write (lua_State *L, FILE *f, int arg) {
|
||||
status = status && (fwrite(s, sizeof(char), l, f) == l);
|
||||
}
|
||||
}
|
||||
if (status) return 1; /* file handle already on stack top */
|
||||
if (l_likely(status))
|
||||
return 1; /* file handle already on stack top */
|
||||
else return luaL_fileresult(L, status, NULL);
|
||||
}
|
||||
|
||||
@@ -697,7 +704,7 @@ static int f_seek (lua_State *L) {
|
||||
luaL_argcheck(L, (lua_Integer)offset == p3, 3,
|
||||
"not an integer in proper range");
|
||||
op = l_fseek(f, offset, mode[op]);
|
||||
if (op)
|
||||
if (l_unlikely(op))
|
||||
return luaL_fileresult(L, 0, NULL); /* error */
|
||||
else {
|
||||
lua_pushinteger(L, (lua_Integer)l_ftell(f));
|
||||
|
||||
56
llex.c
56
llex.c
@@ -122,26 +122,29 @@ l_noret luaX_syntaxerror (LexState *ls, const char *msg) {
|
||||
|
||||
|
||||
/*
|
||||
** creates a new string and anchors it in scanner's table so that
|
||||
** it will not be collected until the end of the compilation
|
||||
** (by that time it should be anchored somewhere)
|
||||
** Creates a new string and anchors it in scanner's table so that it
|
||||
** will not be collected until the end of the compilation; by that time
|
||||
** it should be anchored somewhere. It also internalizes long strings,
|
||||
** ensuring there is only one copy of each unique string. The table
|
||||
** here is used as a set: the string enters as the key, while its value
|
||||
** is irrelevant. We use the string itself as the value only because it
|
||||
** is a TValue readily available. Later, the code generation can change
|
||||
** this value.
|
||||
*/
|
||||
TString *luaX_newstring (LexState *ls, const char *str, size_t l) {
|
||||
lua_State *L = ls->L;
|
||||
TValue *o; /* entry for 'str' */
|
||||
TString *ts = luaS_newlstr(L, str, l); /* create new string */
|
||||
setsvalue2s(L, L->top++, ts); /* temporarily anchor it in stack */
|
||||
o = luaH_set(L, ls->h, s2v(L->top - 1));
|
||||
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 */
|
||||
setbtvalue(o); /* t[string] = true */
|
||||
const TValue *o = luaH_getstr(ls->h, ts);
|
||||
if (!ttisnil(o)) /* string already present? */
|
||||
ts = keystrval(nodefromval(o)); /* get saved copy */
|
||||
else { /* not in use yet */
|
||||
TValue *stv = s2v(L->top.p++); /* reserve stack space for string */
|
||||
setsvalue(L, stv, ts); /* temporarily anchor the string */
|
||||
luaH_finishset(L, ls->h, stv, o, stv); /* t[string] = string */
|
||||
/* table is not a metatable, so it does not need to invalidate cache */
|
||||
luaC_checkGC(L);
|
||||
L->top.p--; /* remove string from stack */
|
||||
}
|
||||
else { /* string already present */
|
||||
ts = keystrval(nodefromval(o)); /* re-use value previously stored */
|
||||
}
|
||||
L->top--; /* remove string from stack */
|
||||
return ts;
|
||||
}
|
||||
|
||||
@@ -254,9 +257,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;
|
||||
@@ -481,34 +485,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 */
|
||||
@@ -547,7 +551,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;
|
||||
|
||||
69
llimits.h
69
llimits.h
@@ -71,11 +71,24 @@ typedef signed char ls_byte;
|
||||
|
||||
|
||||
/*
|
||||
** conversion of pointer to unsigned integer:
|
||||
** this is for hashing only; there is no problem if the integer
|
||||
** cannot hold the whole pointer value
|
||||
** conversion of pointer to unsigned integer: this is for hashing only;
|
||||
** there is no problem if the integer cannot hold the whole pointer
|
||||
** value. (In strict ISO C this may cause undefined behavior, but no
|
||||
** actual machine seems to bother.)
|
||||
*/
|
||||
#define point2uint(p) ((unsigned int)((size_t)(p) & UINT_MAX))
|
||||
#if !defined(LUA_USE_C89) && defined(__STDC_VERSION__) && \
|
||||
__STDC_VERSION__ >= 199901L
|
||||
#include <stdint.h>
|
||||
#if defined(UINTPTR_MAX) /* even in C99 this type is optional */
|
||||
#define L_P2I uintptr_t
|
||||
#else /* no 'intptr'? */
|
||||
#define L_P2I uintmax_t /* use the largest available integer */
|
||||
#endif
|
||||
#else /* C89 option */
|
||||
#define L_P2I size_t
|
||||
#endif
|
||||
|
||||
#define point2uint(p) ((unsigned int)((L_P2I)(p) & UINT_MAX))
|
||||
|
||||
|
||||
|
||||
@@ -149,22 +162,6 @@ typedef LUAI_UACINT l_uacInt;
|
||||
#endif
|
||||
|
||||
|
||||
/*
|
||||
** macros to improve jump prediction (used mainly for error handling)
|
||||
*/
|
||||
#if !defined(likely)
|
||||
|
||||
#if defined(__GNUC__)
|
||||
#define likely(x) (__builtin_expect(((x) != 0), 1))
|
||||
#define unlikely(x) (__builtin_expect(((x) != 0), 0))
|
||||
#else
|
||||
#define likely(x) (x)
|
||||
#define unlikely(x) (x)
|
||||
#endif
|
||||
|
||||
#endif
|
||||
|
||||
|
||||
/*
|
||||
** non-return type
|
||||
*/
|
||||
@@ -181,6 +178,20 @@ typedef LUAI_UACINT l_uacInt;
|
||||
#endif
|
||||
|
||||
|
||||
/*
|
||||
** Inline functions
|
||||
*/
|
||||
#if !defined(LUA_USE_C89)
|
||||
#define l_inline inline
|
||||
#elif defined(__GNUC__)
|
||||
#define l_inline __inline__
|
||||
#else
|
||||
#define l_inline /* empty */
|
||||
#endif
|
||||
|
||||
#define l_sinline static l_inline
|
||||
|
||||
|
||||
/*
|
||||
** type for virtual-machine instructions;
|
||||
** must be an unsigned with (at least) 4 bytes (see details in lopcodes.h)
|
||||
@@ -234,6 +245,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')
|
||||
@@ -315,7 +337,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 */
|
||||
@@ -344,14 +367,14 @@ 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)
|
||||
#define condchangemem(L,pre,pos) ((void)0)
|
||||
#else
|
||||
#define condchangemem(L,pre,pos) \
|
||||
{ if (G(L)->gcrunning) { pre; luaC_fullgc(L, 0); pos; } }
|
||||
{ if (gcrunning(G(L))) { pre; luaC_fullgc(L, 0); pos; } }
|
||||
#endif
|
||||
|
||||
#endif
|
||||
|
||||
19
lmathlib.c
19
lmathlib.c
@@ -73,7 +73,7 @@ static int math_atan (lua_State *L) {
|
||||
static int math_toint (lua_State *L) {
|
||||
int valid;
|
||||
lua_Integer n = lua_tointegerx(L, 1, &valid);
|
||||
if (valid)
|
||||
if (l_likely(valid))
|
||||
lua_pushinteger(L, n);
|
||||
else {
|
||||
luaL_checkany(L, 1);
|
||||
@@ -175,7 +175,8 @@ static int math_log (lua_State *L) {
|
||||
lua_Number base = luaL_checknumber(L, 2);
|
||||
#if !defined(LUA_USE_C89)
|
||||
if (base == l_mathop(2.0))
|
||||
res = l_mathop(log2)(x); else
|
||||
res = l_mathop(log2)(x);
|
||||
else
|
||||
#endif
|
||||
if (base == l_mathop(10.0))
|
||||
res = l_mathop(log10)(x);
|
||||
@@ -266,7 +267,7 @@ static int math_type (lua_State *L) {
|
||||
|
||||
/* try to find an integer type with at least 64 bits */
|
||||
|
||||
#if (ULONG_MAX >> 31 >> 31) >= 3
|
||||
#if ((ULONG_MAX >> 31) >> 31) >= 3
|
||||
|
||||
/* 'long' has at least 64 bits */
|
||||
#define Rand64 unsigned long
|
||||
@@ -276,9 +277,9 @@ static int math_type (lua_State *L) {
|
||||
/* there is a 'long long' type (which must have at least 64 bits) */
|
||||
#define Rand64 unsigned long long
|
||||
|
||||
#elif (LUA_MAXUNSIGNED >> 31 >> 31) >= 3
|
||||
#elif ((LUA_MAXUNSIGNED >> 31) >> 31) >= 3
|
||||
|
||||
/* 'lua_Integer' has at least 64 bits */
|
||||
/* 'lua_Unsigned' has at least 64 bits */
|
||||
#define Rand64 lua_Unsigned
|
||||
|
||||
#endif
|
||||
@@ -474,7 +475,7 @@ static lua_Number I2d (Rand64 x) {
|
||||
|
||||
/* 2^(-FIGS) = 1.0 / 2^30 / 2^3 / 2^(FIGS-33) */
|
||||
#define scaleFIG \
|
||||
((lua_Number)1.0 / (UONE << 30) / 8.0 / (UONE << (FIGS - 33)))
|
||||
(l_mathop(1.0) / (UONE << 30) / l_mathop(8.0) / (UONE << (FIGS - 33)))
|
||||
|
||||
/*
|
||||
** use FIGS - 32 bits from lower half, throwing out the other
|
||||
@@ -485,7 +486,7 @@ static lua_Number I2d (Rand64 x) {
|
||||
/*
|
||||
** higher 32 bits go after those (FIGS - 32) bits: shiftHI = 2^(FIGS - 32)
|
||||
*/
|
||||
#define shiftHI ((lua_Number)(UONE << (FIGS - 33)) * 2.0)
|
||||
#define shiftHI ((lua_Number)(UONE << (FIGS - 33)) * l_mathop(2.0))
|
||||
|
||||
|
||||
static lua_Number I2d (Rand64 x) {
|
||||
@@ -499,12 +500,12 @@ static lua_Number I2d (Rand64 x) {
|
||||
|
||||
/* convert a 'Rand64' to a 'lua_Unsigned' */
|
||||
static lua_Unsigned I2UInt (Rand64 x) {
|
||||
return ((lua_Unsigned)trim32(x.h) << 31 << 1) | (lua_Unsigned)trim32(x.l);
|
||||
return (((lua_Unsigned)trim32(x.h) << 31) << 1) | (lua_Unsigned)trim32(x.l);
|
||||
}
|
||||
|
||||
/* convert a 'lua_Unsigned' to a 'Rand64' */
|
||||
static Rand64 Int2I (lua_Unsigned n) {
|
||||
return packI((lu_int32)(n >> 31 >> 1), (lu_int32)n);
|
||||
return packI((lu_int32)((n >> 31) >> 1), (lu_int32)n);
|
||||
}
|
||||
|
||||
#endif /* } */
|
||||
|
||||
83
lmem.c
83
lmem.c
@@ -22,25 +22,6 @@
|
||||
#include "lstate.h"
|
||||
|
||||
|
||||
#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 allocation.)
|
||||
*/
|
||||
static void *firsttry (global_State *g, void *block, size_t os, size_t ns) {
|
||||
if (ttisnil(&g->nilvalue) && ns > os)
|
||||
return NULL; /* fail */
|
||||
else /* normal allocation */
|
||||
return (*g->frealloc)(g->ud, block, os, ns);
|
||||
}
|
||||
#else
|
||||
#define firsttry(g,block,os,ns) ((*g->frealloc)(g->ud, block, os, ns))
|
||||
#endif
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
/*
|
||||
** About the realloc function:
|
||||
@@ -60,6 +41,43 @@ static void *firsttry (global_State *g, void *block, size_t os, size_t ns) {
|
||||
*/
|
||||
|
||||
|
||||
/*
|
||||
** Macro to call the allocation function.
|
||||
*/
|
||||
#define callfrealloc(g,block,os,ns) ((*g->frealloc)(g->ud, block, os, ns))
|
||||
|
||||
|
||||
/*
|
||||
** When an allocation fails, it will try again after an emergency
|
||||
** collection, except when it cannot run a collection. The GC should
|
||||
** not be called while the state is not fully built, as the collector
|
||||
** is not yet fully initialized. Also, it should not be called when
|
||||
** 'gcstopem' is true, because then the interpreter is in the middle of
|
||||
** a collection step.
|
||||
*/
|
||||
#define cantryagain(g) (completestate(g) && !g->gcstopem)
|
||||
|
||||
|
||||
|
||||
|
||||
#if defined(EMERGENCYGCTESTS)
|
||||
/*
|
||||
** First allocation will fail except when freeing a block (frees never
|
||||
** fail) and when it cannot try again; this fail will trigger 'tryagain'
|
||||
** and a full GC cycle at every allocation.
|
||||
*/
|
||||
static void *firsttry (global_State *g, void *block, size_t os, size_t ns) {
|
||||
if (ns > 0 && cantryagain(g))
|
||||
return NULL; /* fail */
|
||||
else /* normal allocation */
|
||||
return callfrealloc(g, block, os, ns);
|
||||
}
|
||||
#else
|
||||
#define firsttry(g,block,os,ns) callfrealloc(g, block, os, ns)
|
||||
#endif
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
/*
|
||||
@@ -83,7 +101,7 @@ void *luaM_growaux_ (lua_State *L, void *block, int nelems, int *psize,
|
||||
if (nelems + 1 <= size) /* does one extra element still fit? */
|
||||
return block; /* nothing to be done */
|
||||
if (size >= limit / 2) { /* cannot double it? */
|
||||
if (unlikely(size >= limit)) /* cannot grow even a little? */
|
||||
if (l_unlikely(size >= limit)) /* cannot grow even a little? */
|
||||
luaG_runerror(L, "too many %s (limit is %d)", what, limit);
|
||||
size = limit; /* still have at least one free place */
|
||||
}
|
||||
@@ -132,41 +150,36 @@ l_noret luaM_toobig (lua_State *L) {
|
||||
void luaM_free_ (lua_State *L, void *block, size_t osize) {
|
||||
global_State *g = G(L);
|
||||
lua_assert((osize == 0) == (block == NULL));
|
||||
(*g->frealloc)(g->ud, block, osize, 0);
|
||||
callfrealloc(g, block, osize, 0);
|
||||
g->GCdebt -= osize;
|
||||
}
|
||||
|
||||
|
||||
/*
|
||||
** In case of allocation fail, this function will call the GC to try
|
||||
** to free some memory and then try the allocation again.
|
||||
** (It should not be called when shrinking a block, because then the
|
||||
** interpreter may be in the middle of a collection step.)
|
||||
** In case of allocation fail, this function will do an emergency
|
||||
** collection to free some memory and then try the allocation again.
|
||||
*/
|
||||
static void *tryagain (lua_State *L, void *block,
|
||||
size_t osize, size_t nsize) {
|
||||
global_State *g = G(L);
|
||||
if (ttisnil(&g->nilvalue)) { /* is state fully build? */
|
||||
if (cantryagain(g)) {
|
||||
luaC_fullgc(L, 1); /* try to free some memory... */
|
||||
return (*g->frealloc)(g->ud, block, osize, nsize); /* try again */
|
||||
return callfrealloc(g, block, osize, nsize); /* try again */
|
||||
}
|
||||
else return NULL; /* cannot free any memory without a full state */
|
||||
else return NULL; /* cannot run an emergency collection */
|
||||
}
|
||||
|
||||
|
||||
/*
|
||||
** Generic allocation routine.
|
||||
** If allocation fails while shrinking a block, do not try again; the
|
||||
** GC shrinks some blocks and it is not reentrant.
|
||||
*/
|
||||
void *luaM_realloc_ (lua_State *L, void *block, size_t osize, size_t nsize) {
|
||||
void *newblock;
|
||||
global_State *g = G(L);
|
||||
lua_assert((osize == 0) == (block == NULL));
|
||||
newblock = firsttry(g, block, osize, nsize);
|
||||
if (unlikely(newblock == NULL && nsize > 0)) {
|
||||
if (nsize > osize) /* not shrinking a block? */
|
||||
newblock = tryagain(L, block, osize, nsize);
|
||||
if (l_unlikely(newblock == NULL && nsize > 0)) {
|
||||
newblock = tryagain(L, block, osize, nsize);
|
||||
if (newblock == NULL) /* still no memory? */
|
||||
return NULL; /* do not update 'GCdebt' */
|
||||
}
|
||||
@@ -179,7 +192,7 @@ void *luaM_realloc_ (lua_State *L, void *block, size_t osize, size_t nsize) {
|
||||
void *luaM_saferealloc_ (lua_State *L, void *block, size_t osize,
|
||||
size_t nsize) {
|
||||
void *newblock = luaM_realloc_(L, block, osize, nsize);
|
||||
if (unlikely(newblock == NULL && nsize > 0)) /* allocation failed? */
|
||||
if (l_unlikely(newblock == NULL && nsize > 0)) /* allocation failed? */
|
||||
luaM_error(L);
|
||||
return newblock;
|
||||
}
|
||||
@@ -191,7 +204,7 @@ void *luaM_malloc_ (lua_State *L, size_t size, int tag) {
|
||||
else {
|
||||
global_State *g = G(L);
|
||||
void *newblock = firsttry(g, NULL, tag, size);
|
||||
if (unlikely(newblock == NULL)) {
|
||||
if (l_unlikely(newblock == NULL)) {
|
||||
newblock = tryagain(L, NULL, tag, size);
|
||||
if (newblock == NULL)
|
||||
luaM_error(L);
|
||||
|
||||
26
loadlib.c
26
loadlib.c
@@ -132,14 +132,16 @@ static void lsys_unloadlib (void *lib) {
|
||||
|
||||
static void *lsys_load (lua_State *L, const char *path, int seeglb) {
|
||||
void *lib = dlopen(path, RTLD_NOW | (seeglb ? RTLD_GLOBAL : RTLD_LOCAL));
|
||||
if (lib == NULL) lua_pushstring(L, dlerror());
|
||||
if (l_unlikely(lib == NULL))
|
||||
lua_pushstring(L, dlerror());
|
||||
return lib;
|
||||
}
|
||||
|
||||
|
||||
static lua_CFunction lsys_sym (lua_State *L, void *lib, const char *sym) {
|
||||
lua_CFunction f = cast_func(dlsym(lib, sym));
|
||||
if (f == NULL) lua_pushstring(L, dlerror());
|
||||
if (l_unlikely(f == NULL))
|
||||
lua_pushstring(L, dlerror());
|
||||
return f;
|
||||
}
|
||||
|
||||
@@ -410,7 +412,7 @@ static int ll_loadlib (lua_State *L) {
|
||||
const char *path = luaL_checkstring(L, 1);
|
||||
const char *init = luaL_checkstring(L, 2);
|
||||
int stat = lookforfunc(L, path, init);
|
||||
if (stat == 0) /* no errors? */
|
||||
if (l_likely(stat == 0)) /* no errors? */
|
||||
return 1; /* return the loaded function */
|
||||
else { /* error; error message is on stack top */
|
||||
luaL_pushfail(L);
|
||||
@@ -523,14 +525,14 @@ static const char *findfile (lua_State *L, const char *name,
|
||||
const char *path;
|
||||
lua_getfield(L, lua_upvalueindex(1), pname);
|
||||
path = lua_tostring(L, -1);
|
||||
if (path == NULL)
|
||||
if (l_unlikely(path == NULL))
|
||||
luaL_error(L, "'package.%s' must be a string", pname);
|
||||
return searchpath(L, name, path, ".", dirsep);
|
||||
}
|
||||
|
||||
|
||||
static int checkload (lua_State *L, int stat, const char *filename) {
|
||||
if (stat) { /* module loaded successfully? */
|
||||
if (l_likely(stat)) { /* module loaded successfully? */
|
||||
lua_pushstring(L, filename); /* will be 2nd argument to module */
|
||||
return 2; /* return open function and file name */
|
||||
}
|
||||
@@ -623,13 +625,14 @@ static void findloader (lua_State *L, const char *name) {
|
||||
int i;
|
||||
luaL_Buffer msg; /* to build error message */
|
||||
/* push 'package.searchers' to index 3 in the stack */
|
||||
if (lua_getfield(L, lua_upvalueindex(1), "searchers") != LUA_TTABLE)
|
||||
if (l_unlikely(lua_getfield(L, lua_upvalueindex(1), "searchers")
|
||||
!= LUA_TTABLE))
|
||||
luaL_error(L, "'package.searchers' must be a table");
|
||||
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? */
|
||||
if (l_unlikely(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 */
|
||||
@@ -705,8 +708,13 @@ static const luaL_Reg ll_funcs[] = {
|
||||
|
||||
|
||||
static void createsearcherstable (lua_State *L) {
|
||||
static const lua_CFunction searchers[] =
|
||||
{searcher_preload, searcher_Lua, searcher_C, searcher_Croot, NULL};
|
||||
static const lua_CFunction searchers[] = {
|
||||
searcher_preload,
|
||||
searcher_Lua,
|
||||
searcher_C,
|
||||
searcher_Croot,
|
||||
NULL
|
||||
};
|
||||
int i;
|
||||
/* create 'searchers' table */
|
||||
lua_createtable(L, sizeof(searchers)/sizeof(searchers[0]) - 1, 0);
|
||||
|
||||
50
lobject.c
50
lobject.c
@@ -62,7 +62,7 @@ static lua_Integer intarith (lua_State *L, int op, lua_Integer v1,
|
||||
case LUA_OPBOR: return intop(|, v1, v2);
|
||||
case LUA_OPBXOR: return intop(^, v1, v2);
|
||||
case LUA_OPSHL: return luaV_shiftl(v1, v2);
|
||||
case LUA_OPSHR: return luaV_shiftl(v1, -v2);
|
||||
case LUA_OPSHR: return luaV_shiftr(v1, v2);
|
||||
case LUA_OPUNM: return intop(-, 0, v1);
|
||||
case LUA_OPBNOT: return intop(^, ~l_castS2U(0), v1);
|
||||
default: lua_assert(0); return 0;
|
||||
@@ -164,7 +164,7 @@ static int isneg (const char **s) {
|
||||
*/
|
||||
static lua_Number lua_strx2number (const char *s, char **endptr) {
|
||||
int dot = lua_getlocaledecpoint();
|
||||
lua_Number r = 0.0; /* result (accumulator) */
|
||||
lua_Number r = l_mathop(0.0); /* result (accumulator) */
|
||||
int sigdig = 0; /* number of significant digits */
|
||||
int nosigdig = 0; /* number of non-significant digits */
|
||||
int e = 0; /* exponent correction */
|
||||
@@ -174,7 +174,7 @@ static lua_Number lua_strx2number (const char *s, char **endptr) {
|
||||
while (lisspace(cast_uchar(*s))) s++; /* skip initial spaces */
|
||||
neg = isneg(&s); /* check sign */
|
||||
if (!(*s == '0' && (*(s + 1) == 'x' || *(s + 1) == 'X'))) /* check '0x' */
|
||||
return 0.0; /* invalid format (no '0x') */
|
||||
return l_mathop(0.0); /* invalid format (no '0x') */
|
||||
for (s += 2; ; s++) { /* skip '0x' and read numeral */
|
||||
if (*s == dot) {
|
||||
if (hasdot) break; /* second dot? stop loop */
|
||||
@@ -184,14 +184,14 @@ static lua_Number lua_strx2number (const char *s, char **endptr) {
|
||||
if (sigdig == 0 && *s == '0') /* non-significant digit (zero)? */
|
||||
nosigdig++;
|
||||
else if (++sigdig <= MAXSIGDIG) /* can read it without overflow? */
|
||||
r = (r * cast_num(16.0)) + luaO_hexavalue(*s);
|
||||
r = (r * l_mathop(16.0)) + luaO_hexavalue(*s);
|
||||
else e++; /* too many digits; ignore, but still count for exponent */
|
||||
if (hasdot) e--; /* decimal digit? correct exponent */
|
||||
}
|
||||
else break; /* neither a dot nor a digit */
|
||||
}
|
||||
if (nosigdig + sigdig == 0) /* no digits? */
|
||||
return 0.0; /* invalid format */
|
||||
return l_mathop(0.0); /* invalid format */
|
||||
*endptr = cast_charp(s); /* valid up to here */
|
||||
e *= 4; /* each digit multiplies/divides value by 2^4 */
|
||||
if (*s == 'p' || *s == 'P') { /* exponent part? */
|
||||
@@ -200,7 +200,7 @@ static lua_Number lua_strx2number (const char *s, char **endptr) {
|
||||
s++; /* skip 'p' */
|
||||
neg1 = isneg(&s); /* sign */
|
||||
if (!lisdigit(cast_uchar(*s)))
|
||||
return 0.0; /* invalid; must have at least one digit */
|
||||
return l_mathop(0.0); /* invalid; must have at least one digit */
|
||||
while (lisdigit(cast_uchar(*s))) /* read exponent */
|
||||
exp1 = exp1 * 10 + *(s++) - '0';
|
||||
if (neg1) exp1 = -exp1;
|
||||
@@ -258,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 */
|
||||
@@ -386,29 +386,39 @@ void luaO_tostring (lua_State *L, TValue *obj) {
|
||||
** ===================================================================
|
||||
*/
|
||||
|
||||
/* size for buffer space used by 'luaO_pushvfstring' */
|
||||
#define BUFVFS 200
|
||||
/*
|
||||
** Size for buffer space used by 'luaO_pushvfstring'. It should be
|
||||
** (LUA_IDSIZE + MAXNUMBER2STR) + a minimal space for basic messages,
|
||||
** so that 'luaG_addinfo' can work directly on the buffer.
|
||||
*/
|
||||
#define BUFVFS (LUA_IDSIZE + MAXNUMBER2STR + 95)
|
||||
|
||||
/* buffer used by 'luaO_pushvfstring' */
|
||||
typedef struct BuffFS {
|
||||
lua_State *L;
|
||||
int pushed; /* number of string pieces already on the stack */
|
||||
int pushed; /* true if there is a part of the result on the stack */
|
||||
int blen; /* length of partial string in 'space' */
|
||||
char space[BUFVFS]; /* holds last part of the result */
|
||||
} BuffFS;
|
||||
|
||||
|
||||
/*
|
||||
** Push given string to the stack, as part of the buffer, and
|
||||
** join the partial strings in the stack into one.
|
||||
** Push given string to the stack, as part of the result, and
|
||||
** join it to previous partial result if there is one.
|
||||
** It may call 'luaV_concat' while using one slot from EXTRA_STACK.
|
||||
** This call cannot invoke metamethods, as both operands must be
|
||||
** strings. It can, however, raise an error if the result is too
|
||||
** long. In that case, 'luaV_concat' frees the extra slot before
|
||||
** raising the error.
|
||||
*/
|
||||
static void pushstr (BuffFS *buff, const char *str, size_t l) {
|
||||
static void pushstr (BuffFS *buff, const char *str, size_t lstr) {
|
||||
lua_State *L = buff->L;
|
||||
setsvalue2s(L, L->top, luaS_newlstr(L, str, l));
|
||||
L->top++; /* may use one extra slot */
|
||||
buff->pushed++;
|
||||
luaV_concat(L, buff->pushed); /* join partial results into one */
|
||||
buff->pushed = 1;
|
||||
setsvalue2s(L, L->top.p, luaS_newlstr(L, str, lstr));
|
||||
L->top.p++; /* may use one slot from EXTRA_STACK */
|
||||
if (!buff->pushed) /* no previous string on the stack? */
|
||||
buff->pushed = 1; /* now there is one */
|
||||
else /* join previous string with new one */
|
||||
luaV_concat(L, 2);
|
||||
}
|
||||
|
||||
|
||||
@@ -454,7 +464,7 @@ static void addstr2buff (BuffFS *buff, const char *str, size_t slen) {
|
||||
|
||||
|
||||
/*
|
||||
** Add a number to the buffer.
|
||||
** Add a numeral to the buffer.
|
||||
*/
|
||||
static void addnum2buff (BuffFS *buff, TValue *num) {
|
||||
char *numbuff = getbuff(buff, MAXNUMBER2STR);
|
||||
@@ -532,7 +542,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 */
|
||||
lua_assert(buff.pushed == 1);
|
||||
return svalue(s2v(L->top - 1));
|
||||
return svalue(s2v(L->top.p - 1));
|
||||
}
|
||||
|
||||
|
||||
|
||||
55
lobject.h
55
lobject.h
@@ -21,10 +21,12 @@
|
||||
*/
|
||||
#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 types (including LUA_TNONE)
|
||||
** number of all possible types (including LUA_TNONE but excluding DEADKEY)
|
||||
*/
|
||||
#define LUA_TOTALTYPES (LUA_TPROTO + 2)
|
||||
|
||||
@@ -50,6 +52,8 @@ typedef union Value {
|
||||
lua_CFunction f; /* light C functions */
|
||||
lua_Integer i; /* integer numbers */
|
||||
lua_Number n; /* float numbers */
|
||||
/* not used, but may avoid warnings for uninitialized value */
|
||||
lu_byte ub;
|
||||
} Value;
|
||||
|
||||
|
||||
@@ -66,7 +70,7 @@ typedef struct TValue {
|
||||
|
||||
|
||||
#define val_(o) ((o)->value_)
|
||||
#define valraw(o) (&val_(o))
|
||||
#define valraw(o) (val_(o))
|
||||
|
||||
|
||||
/* raw type tag of a TValue */
|
||||
@@ -110,7 +114,7 @@ typedef struct TValue {
|
||||
#define settt_(o,t) ((o)->tt_=(t))
|
||||
|
||||
|
||||
/* main macro to copy values (from 'obj1' to 'obj2') */
|
||||
/* main macro to copy values (from 'obj2' to 'obj1') */
|
||||
#define setobj(L,obj1,obj2) \
|
||||
{ TValue *io1=(obj1); const TValue *io2=(obj2); \
|
||||
io1->value_ = io2->value_; settt_(io1, io2->tt_); \
|
||||
@@ -134,16 +138,36 @@ typedef struct TValue {
|
||||
|
||||
|
||||
/*
|
||||
** Entries in the Lua stack
|
||||
** Entries in a Lua stack. Field 'tbclist' forms a list of all
|
||||
** to-be-closed variables active in this stack. Dummy entries are
|
||||
** used when the distance between two tbc variables does not fit
|
||||
** in an unsigned short. They are represented by delta==0, and
|
||||
** their real delta is always the maximum value that fits in
|
||||
** that field.
|
||||
*/
|
||||
typedef union StackValue {
|
||||
TValue val;
|
||||
struct {
|
||||
TValuefields;
|
||||
unsigned short delta;
|
||||
} tbclist;
|
||||
} StackValue;
|
||||
|
||||
|
||||
/* index to stack elements */
|
||||
typedef StackValue *StkId;
|
||||
|
||||
|
||||
/*
|
||||
** When reallocating the stack, change all pointers to the stack into
|
||||
** proper offsets.
|
||||
*/
|
||||
typedef union {
|
||||
StkId p; /* actual pointer */
|
||||
ptrdiff_t offset; /* used while the stack is being reallocated */
|
||||
} StkIdRel;
|
||||
|
||||
|
||||
/* convert a 'StackValue' to a 'TValue' */
|
||||
#define s2v(o) (&(o)->val)
|
||||
|
||||
@@ -555,7 +579,7 @@ typedef struct Proto {
|
||||
|
||||
/*
|
||||
** {==================================================================
|
||||
** Closures
|
||||
** Functions
|
||||
** ===================================================================
|
||||
*/
|
||||
|
||||
@@ -568,10 +592,11 @@ typedef struct Proto {
|
||||
#define LUA_VCCL makevariant(LUA_TFUNCTION, 2) /* C closure */
|
||||
|
||||
#define ttisfunction(o) checktype(o, LUA_TFUNCTION)
|
||||
#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 ttisclosure(o) (ttisLclosure(o) || ttisCclosure(o))
|
||||
|
||||
|
||||
#define isLfunction(o) ttisLclosure(o)
|
||||
|
||||
@@ -603,8 +628,10 @@ typedef struct Proto {
|
||||
*/
|
||||
typedef struct UpVal {
|
||||
CommonHeader;
|
||||
lu_byte tbc; /* true if it represents a to-be-closed variable */
|
||||
TValue *v; /* points to stack or to its own value */
|
||||
union {
|
||||
TValue *p; /* points to stack or to its own value */
|
||||
ptrdiff_t offset; /* used while the stack is being reallocated */
|
||||
} v;
|
||||
union {
|
||||
struct { /* (when open) */
|
||||
struct UpVal *next; /* linked list */
|
||||
@@ -743,13 +770,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)
|
||||
|
||||
/* }================================================================== */
|
||||
|
||||
|
||||
41
lopcodes.h
41
lopcodes.h
@@ -21,7 +21,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) |
|
||||
isJ sJ (signed)(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
|
||||
@@ -190,7 +190,8 @@ enum OpMode {iABC, iABx, iAsBx, iAx, isJ}; /* basic instruction formats */
|
||||
|
||||
|
||||
/*
|
||||
** grep "ORDER OP" if you change these enums
|
||||
** Grep "ORDER OP" if you change these enums. Opcodes marked with a (*)
|
||||
** has extra descriptions in the notes after the enumeration.
|
||||
*/
|
||||
|
||||
typedef enum {
|
||||
@@ -203,7 +204,7 @@ 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_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] */
|
||||
@@ -225,13 +226,13 @@ 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_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]:number */
|
||||
OP_SUBK,/* A B C R[A] := R[B] - K[C]:number */
|
||||
OP_MULK,/* A B C R[A] := R[B] * K[C]:number */
|
||||
OP_MODK,/* A B C R[A] := R[B] % K[C]:number */
|
||||
OP_POWK,/* A B C R[A] := R[B] ^ K[C]:number */
|
||||
OP_DIVK,/* A B C R[A] := R[B] / K[C]:number */
|
||||
OP_IDIVK,/* A B C R[A] := R[B] // K[C]:number */
|
||||
|
||||
OP_BANDK,/* A B C R[A] := R[B] & K[C]:integer */
|
||||
OP_BORK,/* A B C R[A] := R[B] | K[C]:integer */
|
||||
@@ -254,14 +255,14 @@ 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_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_LEN,/* A B R[A] := #R[B] (length operator) */
|
||||
|
||||
OP_CONCAT,/* A B R[A] := R[A].. ... ..R[A + B - 1] */
|
||||
|
||||
@@ -280,7 +281,7 @@ 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 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_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 k return R[A](R[A+1], ... ,R[A+B-1]) */
|
||||
@@ -297,7 +298,7 @@ OP_TFORPREP,/* A Bx create upvalue for R[A + 3]; pc+=Bx */
|
||||
OP_TFORCALL,/* A C R[A+4], ... ,R[A+3+C] := R[A](R[A+1], R[A+2]); */
|
||||
OP_TFORLOOP,/* A Bx if R[A+2] ~= nil then { R[A]=R[A+2]; pc -= Bx } */
|
||||
|
||||
OP_SETLIST,/* A B C k R[A][(C-1)*FPF+i] := R[A+i], 1 <= i <= B */
|
||||
OP_SETLIST,/* A B C k R[A][C+i] := R[A+i], 1 <= i <= B */
|
||||
|
||||
OP_CLOSURE,/* A Bx R[A] := closure(KPROTO[Bx]) */
|
||||
|
||||
@@ -315,6 +316,18 @@ OP_EXTRAARG/* Ax extra (larger) argument for previous opcode */
|
||||
|
||||
/*===========================================================================
|
||||
Notes:
|
||||
|
||||
(*) Opcode OP_LFALSESKIP is used to convert a condition to a boolean
|
||||
value, in a code equivalent to (not cond ? false : true). (It
|
||||
produces false and skips the next instruction producing true.)
|
||||
|
||||
(*) Opcodes OP_MMBIN and variants follow each arithmetic and
|
||||
bitwise opcode. If the operation succeeds, it skips this next
|
||||
opcode. Otherwise, this opcode calls the corresponding metamethod.
|
||||
|
||||
(*) Opcode OP_TESTSET is used in short-circuit expressions that need
|
||||
both to jump and to produce a value, such as (a = b or c).
|
||||
|
||||
(*) In OP_CALL, if (B == 0) then B = top - A. If (C == 0), then
|
||||
'top' is set to last_result+1, so next open instruction (OP_CALL,
|
||||
OP_RETURN*, OP_SETLIST) may use 'top'.
|
||||
|
||||
44
loslib.c
44
loslib.c
@@ -30,23 +30,14 @@
|
||||
*/
|
||||
#if !defined(LUA_STRFTIMEOPTIONS) /* { */
|
||||
|
||||
/* options for ANSI C 89 (only 1-char options) */
|
||||
#define L_STRFTIMEC89 "aAbBcdHIjmMpSUwWxXyYZ%"
|
||||
|
||||
/* options for ISO C 99 and POSIX */
|
||||
#define L_STRFTIMEC99 "aAbBcCdDeFgGhHIjmMnprRStTuUVwWxXyYzZ%" \
|
||||
"||" "EcECExEXEyEY" "OdOeOHOIOmOMOSOuOUOVOwOWOy" /* two-char options */
|
||||
|
||||
/* options for Windows */
|
||||
#define L_STRFTIMEWIN "aAbBcdHIjmMpSUwWxXyYzZ%" \
|
||||
"||" "#c#x#d#H#I#j#m#M#S#U#w#W#y#Y" /* two-char options */
|
||||
|
||||
#if defined(LUA_USE_WINDOWS)
|
||||
#define LUA_STRFTIMEOPTIONS L_STRFTIMEWIN
|
||||
#elif defined(LUA_USE_C89)
|
||||
#define LUA_STRFTIMEOPTIONS L_STRFTIMEC89
|
||||
#define LUA_STRFTIMEOPTIONS "aAbBcdHIjmMpSUwWxXyYzZ%" \
|
||||
"||" "#c#x#d#H#I#j#m#M#S#U#w#W#y#Y" /* two-char options */
|
||||
#elif defined(LUA_USE_C89) /* ANSI C 89 (only 1-char options) */
|
||||
#define LUA_STRFTIMEOPTIONS "aAbBcdHIjmMpSUwWxXyYZ%"
|
||||
#else /* C99 specification */
|
||||
#define LUA_STRFTIMEOPTIONS L_STRFTIMEC99
|
||||
#define LUA_STRFTIMEOPTIONS "aAbBcCdDeFgGhHIjmMnprRStTuUVwWxXyYzZ%" \
|
||||
"||" "EcECExEXEyEY" "OdOeOHOIOmOMOSOuOUOVOwOWOy" /* two-char options */
|
||||
#endif
|
||||
|
||||
#endif /* } */
|
||||
@@ -138,12 +129,21 @@
|
||||
/* }================================================================== */
|
||||
|
||||
|
||||
#if !defined(l_system)
|
||||
#if defined(LUA_USE_IOS)
|
||||
/* Despite claiming to be ISO C, iOS does not implement 'system'. */
|
||||
#define l_system(cmd) ((cmd) == NULL ? 0 : -1)
|
||||
#else
|
||||
#define l_system(cmd) system(cmd) /* default definition */
|
||||
#endif
|
||||
#endif
|
||||
|
||||
|
||||
static int os_execute (lua_State *L) {
|
||||
const char *cmd = luaL_optstring(L, 1, NULL);
|
||||
int stat;
|
||||
errno = 0;
|
||||
stat = system(cmd);
|
||||
stat = l_system(cmd);
|
||||
if (cmd != NULL)
|
||||
return luaL_execresult(L, stat);
|
||||
else {
|
||||
@@ -170,7 +170,7 @@ static int os_tmpname (lua_State *L) {
|
||||
char buff[LUA_TMPNAMBUFSIZE];
|
||||
int err;
|
||||
lua_tmpnam(buff, err);
|
||||
if (err)
|
||||
if (l_unlikely(err))
|
||||
return luaL_error(L, "unable to generate a unique filename");
|
||||
lua_pushstring(L, buff);
|
||||
return 1;
|
||||
@@ -208,7 +208,7 @@ static int os_clock (lua_State *L) {
|
||||
*/
|
||||
static void setfield (lua_State *L, const char *key, int value, int delta) {
|
||||
#if (defined(LUA_NUMTIME) && LUA_MAXINTEGER <= INT_MAX)
|
||||
if (value > LUA_MAXINTEGER - delta)
|
||||
if (l_unlikely(value > LUA_MAXINTEGER - delta))
|
||||
luaL_error(L, "field '%s' is out-of-bound", key);
|
||||
#endif
|
||||
lua_pushinteger(L, (lua_Integer)value + delta);
|
||||
@@ -253,16 +253,14 @@ static int getfield (lua_State *L, const char *key, int d, int delta) {
|
||||
int t = lua_getfield(L, -1, key); /* get field and its type */
|
||||
lua_Integer res = lua_tointegerx(L, -1, &isnum);
|
||||
if (!isnum) { /* field is not an integer? */
|
||||
if (t != LUA_TNIL) /* some other value? */
|
||||
if (l_unlikely(t != LUA_TNIL)) /* some other value? */
|
||||
return luaL_error(L, "field '%s' is not an integer", key);
|
||||
else if (d < 0) /* absent field; no default? */
|
||||
else if (l_unlikely(d < 0)) /* absent field; no default? */
|
||||
return luaL_error(L, "field '%s' missing in date table", key);
|
||||
res = d;
|
||||
}
|
||||
else {
|
||||
/* unsigned avoids overflow when lua_Integer has 32 bits */
|
||||
if (!(res >= 0 ? (lua_Unsigned)res <= (lua_Unsigned)INT_MAX + delta
|
||||
: (lua_Integer)INT_MIN + delta <= res))
|
||||
if (!(res >= 0 ? res - delta <= INT_MAX : INT_MIN + delta <= res))
|
||||
return luaL_error(L, "field '%s' is out-of-bound", key);
|
||||
res -= delta;
|
||||
}
|
||||
|
||||
173
lparser.c
173
lparser.c
@@ -128,7 +128,7 @@ static void checknext (LexState *ls, int c) {
|
||||
** in line 'where' (if that is not the current line).
|
||||
*/
|
||||
static void check_match (LexState *ls, int what, int who, int where) {
|
||||
if (unlikely(!testnext(ls, what))) {
|
||||
if (l_unlikely(!testnext(ls, what))) {
|
||||
if (where == ls->linenumber) /* all in the same line? */
|
||||
error_expected(ls, what); /* do not need a complex message */
|
||||
else {
|
||||
@@ -222,26 +222,26 @@ static Vardesc *getlocalvardesc (FuncState *fs, int vidx) {
|
||||
|
||||
|
||||
/*
|
||||
** 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').
|
||||
** Convert 'nvar', a compiler index level, to its corresponding
|
||||
** register. For that, search for the highest variable below that level
|
||||
** that is in a register and uses its register index ('ridx') plus one.
|
||||
*/
|
||||
static int stacklevel (FuncState *fs, int nvar) {
|
||||
static int reglevel (FuncState *fs, int nvar) {
|
||||
while (nvar-- > 0) {
|
||||
Vardesc *vd = getlocalvardesc(fs, nvar); /* get variable */
|
||||
if (vd->vd.kind != RDKCTC) /* is in the stack? */
|
||||
return vd->vd.sidx + 1;
|
||||
Vardesc *vd = getlocalvardesc(fs, nvar); /* get previous variable */
|
||||
if (vd->vd.kind != RDKCTC) /* is in a register? */
|
||||
return vd->vd.ridx + 1;
|
||||
}
|
||||
return 0; /* no variables in the stack */
|
||||
return 0; /* no variables in registers */
|
||||
}
|
||||
|
||||
|
||||
/*
|
||||
** Return the number of variables in the stack for function 'fs'
|
||||
** Return the number of variables in the register stack for the given
|
||||
** function.
|
||||
*/
|
||||
int luaY_nvarstack (FuncState *fs) {
|
||||
return stacklevel(fs, fs->nactvar);
|
||||
return reglevel(fs, fs->nactvar);
|
||||
}
|
||||
|
||||
|
||||
@@ -267,7 +267,7 @@ static void init_var (FuncState *fs, expdesc *e, int vidx) {
|
||||
e->f = e->t = NO_JUMP;
|
||||
e->k = VLOCAL;
|
||||
e->u.var.vidx = vidx;
|
||||
e->u.var.sidx = getlocalvardesc(fs, vidx)->vd.sidx;
|
||||
e->u.var.ridx = getlocalvardesc(fs, vidx)->vd.ridx;
|
||||
}
|
||||
|
||||
|
||||
@@ -310,12 +310,12 @@ static void check_readonly (LexState *ls, expdesc *e) {
|
||||
*/
|
||||
static void adjustlocalvars (LexState *ls, int nvars) {
|
||||
FuncState *fs = ls->fs;
|
||||
int stklevel = luaY_nvarstack(fs);
|
||||
int reglevel = luaY_nvarstack(fs);
|
||||
int i;
|
||||
for (i = 0; i < nvars; i++) {
|
||||
int vidx = fs->nactvar++;
|
||||
Vardesc *var = getlocalvardesc(fs, vidx);
|
||||
var->vd.sidx = stklevel++;
|
||||
var->vd.ridx = reglevel++;
|
||||
var->vd.pidx = registerlocalvar(ls, fs, var->vd.name);
|
||||
}
|
||||
}
|
||||
@@ -366,7 +366,7 @@ static int newupvalue (FuncState *fs, TString *name, expdesc *v) {
|
||||
FuncState *prev = fs->prev;
|
||||
if (v->k == VLOCAL) {
|
||||
up->instack = 1;
|
||||
up->idx = v->u.var.sidx;
|
||||
up->idx = v->u.var.ridx;
|
||||
up->kind = getlocalvardesc(prev, v->u.var.vidx)->vd.kind;
|
||||
lua_assert(eqstr(name, getlocalvardesc(prev, v->u.var.vidx)->vd.name));
|
||||
}
|
||||
@@ -416,6 +416,17 @@ static void markupval (FuncState *fs, int level) {
|
||||
}
|
||||
|
||||
|
||||
/*
|
||||
** Mark that current block has a to-be-closed variable.
|
||||
*/
|
||||
static void marktobeclosed (FuncState *fs) {
|
||||
BlockCnt *bl = fs->bl;
|
||||
bl->upval = 1;
|
||||
bl->insidetbc = 1;
|
||||
fs->needclose = 1;
|
||||
}
|
||||
|
||||
|
||||
/*
|
||||
** Find a variable with the given name 'n'. If it is an upvalue, add
|
||||
** this upvalue into all intermediate functions. If it is a global, set
|
||||
@@ -457,6 +468,7 @@ static void singlevar (LexState *ls, expdesc *var) {
|
||||
expdesc key;
|
||||
singlevaraux(fs, ls->envn, var, 1); /* get environment variable */
|
||||
lua_assert(var->k != VVOID); /* this one must exist */
|
||||
luaK_exp2anyregup(fs, var); /* but could be a constant */
|
||||
codestring(&key, varname); /* key is variable name */
|
||||
luaK_indexed(fs, var, &key); /* env[varname] */
|
||||
}
|
||||
@@ -489,12 +501,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--)
|
||||
|
||||
|
||||
/*
|
||||
@@ -511,15 +521,15 @@ static l_noret jumpscopeerror (LexState *ls, Labeldesc *gt) {
|
||||
|
||||
/*
|
||||
** Solves the goto at index 'g' to given 'label' and removes it
|
||||
** from the list of pending goto's.
|
||||
** from the list of pending gotos.
|
||||
** If it jumps into the scope of some variable, raises an error.
|
||||
*/
|
||||
static void solvegoto (LexState *ls, int g, Labeldesc *label) {
|
||||
int i;
|
||||
Labellist *gl = &ls->dyd->gt; /* list of goto's */
|
||||
Labellist *gl = &ls->dyd->gt; /* list of gotos */
|
||||
Labeldesc *gt = &gl->arr[g]; /* goto to be resolved */
|
||||
lua_assert(eqstr(gt->name, label->name));
|
||||
if (unlikely(gt->nactvar < label->nactvar)) /* enter some scope? */
|
||||
if (l_unlikely(gt->nactvar < label->nactvar)) /* enter some scope? */
|
||||
jumpscopeerror(ls, gt);
|
||||
luaK_patchlist(ls->fs, gt->pc, label->pc);
|
||||
for (i = g; i < gl->n - 1; i++) /* remove goto from pending list */
|
||||
@@ -570,7 +580,7 @@ static int newgotoentry (LexState *ls, TString *name, int line, int pc) {
|
||||
/*
|
||||
** Solves forward jumps. Check whether new label 'lb' matches any
|
||||
** pending gotos in current block and solves them. Return true
|
||||
** if any of the goto's need to close upvalues.
|
||||
** if any of the gotos need to close upvalues.
|
||||
*/
|
||||
static int solvegotos (LexState *ls, Labeldesc *lb) {
|
||||
Labellist *gl = &ls->dyd->gt;
|
||||
@@ -591,7 +601,7 @@ static int solvegotos (LexState *ls, Labeldesc *lb) {
|
||||
/*
|
||||
** Create a new label with the given 'name' at the given 'line'.
|
||||
** 'last' tells whether label is the last non-op statement in its
|
||||
** block. Solves all pending goto's to this new label and adds
|
||||
** block. Solves all pending gotos to this new label and adds
|
||||
** a close instruction if necessary.
|
||||
** Returns true iff it added a close instruction.
|
||||
*/
|
||||
@@ -622,7 +632,7 @@ static void movegotosout (FuncState *fs, BlockCnt *bl) {
|
||||
for (i = bl->firstgoto; i < gl->n; i++) { /* for each pending goto */
|
||||
Labeldesc *gt = &gl->arr[i];
|
||||
/* leaving a variable scope? */
|
||||
if (stacklevel(fs, gt->nactvar) > stacklevel(fs, bl->nactvar))
|
||||
if (reglevel(fs, gt->nactvar) > reglevel(fs, bl->nactvar))
|
||||
gt->close |= bl->upval; /* jump may need a close */
|
||||
gt->nactvar = bl->nactvar; /* update goto level */
|
||||
}
|
||||
@@ -663,20 +673,20 @@ static void leaveblock (FuncState *fs) {
|
||||
BlockCnt *bl = fs->bl;
|
||||
LexState *ls = fs->ls;
|
||||
int hasclose = 0;
|
||||
int stklevel = stacklevel(fs, bl->nactvar); /* level outside the block */
|
||||
if (bl->isloop) /* fix pending breaks? */
|
||||
int stklevel = reglevel(fs, bl->nactvar); /* level outside the block */
|
||||
removevars(fs, bl->nactvar); /* remove block locals */
|
||||
lua_assert(bl->nactvar == fs->nactvar); /* back to level on entry */
|
||||
if (bl->isloop) /* has to fix pending breaks? */
|
||||
hasclose = createlabel(ls, luaS_newliteral(ls->L, "break"), 0, 0);
|
||||
if (!hasclose && bl->previous && bl->upval)
|
||||
if (!hasclose && bl->previous && bl->upval) /* still need a 'close'? */
|
||||
luaK_codeABC(fs, OP_CLOSE, stklevel, 0, 0);
|
||||
fs->bl = bl->previous;
|
||||
removevars(fs, bl->nactvar);
|
||||
lua_assert(bl->nactvar == fs->nactvar);
|
||||
fs->freereg = stklevel; /* free registers */
|
||||
ls->dyd->label.n = bl->firstlabel; /* remove local labels */
|
||||
if (bl->previous) /* inner block? */
|
||||
movegotosout(fs, bl); /* update pending gotos to outer block */
|
||||
fs->bl = bl->previous; /* current block now is previous one */
|
||||
if (bl->previous) /* was it a nested block? */
|
||||
movegotosout(fs, bl); /* update pending gotos to enclosing block */
|
||||
else {
|
||||
if (bl->firstgoto < ls->dyd->gt.n) /* pending gotos in outer block? */
|
||||
if (bl->firstgoto < ls->dyd->gt.n) /* still pending gotos? */
|
||||
undefgoto(ls, &ls->dyd->gt.arr[bl->firstgoto]); /* error */
|
||||
}
|
||||
}
|
||||
@@ -947,7 +957,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;
|
||||
@@ -955,12 +965,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;
|
||||
@@ -1332,13 +1342,13 @@ static void check_conflict (LexState *ls, struct LHS_assign *lh, expdesc *v) {
|
||||
}
|
||||
}
|
||||
else { /* table is a register */
|
||||
if (v->k == VLOCAL && lh->v.u.ind.t == v->u.var.sidx) {
|
||||
if (v->k == VLOCAL && lh->v.u.ind.t == v->u.var.ridx) {
|
||||
conflict = 1; /* table is the local being assigned now */
|
||||
lh->v.u.ind.t = extra; /* assignment will use safe copy */
|
||||
}
|
||||
/* is index the local being assigned? */
|
||||
if (lh->v.k == VINDEXED && v->k == VLOCAL &&
|
||||
lh->v.u.ind.idx == v->u.var.sidx) {
|
||||
lh->v.u.ind.idx == v->u.var.ridx) {
|
||||
conflict = 1;
|
||||
lh->v.u.ind.idx = extra; /* previous assignment will use safe copy */
|
||||
}
|
||||
@@ -1348,7 +1358,7 @@ static void check_conflict (LexState *ls, struct LHS_assign *lh, expdesc *v) {
|
||||
if (conflict) {
|
||||
/* copy upvalue/local value to a temporary (in position 'extra') */
|
||||
if (v->k == VLOCAL)
|
||||
luaK_codeABC(fs, OP_MOVE, extra, v->u.var.sidx, 0);
|
||||
luaK_codeABC(fs, OP_MOVE, extra, v->u.var.ridx, 0);
|
||||
else
|
||||
luaK_codeABC(fs, OP_GETUPVAL, extra, v->u.info, 0);
|
||||
luaK_reserveregs(fs, 1);
|
||||
@@ -1413,7 +1423,7 @@ static void gotostat (LexState *ls) {
|
||||
newgotoentry(ls, name, line, luaK_jump(fs));
|
||||
else { /* found a label */
|
||||
/* backward jump; will be resolved here */
|
||||
int lblevel = stacklevel(fs, lb->nactvar); /* label level */
|
||||
int lblevel = reglevel(fs, lb->nactvar); /* label level */
|
||||
if (luaY_nvarstack(fs) > lblevel) /* leaving the scope of a variable? */
|
||||
luaK_codeABC(fs, OP_CLOSE, lblevel, 0, 0);
|
||||
/* create jump and link it to the label */
|
||||
@@ -1437,7 +1447,7 @@ static void breakstat (LexState *ls) {
|
||||
*/
|
||||
static void checkrepeated (LexState *ls, TString *name) {
|
||||
Labeldesc *lb = findlabel(ls, name);
|
||||
if (unlikely(lb != NULL)) { /* already defined? */
|
||||
if (l_unlikely(lb != NULL)) { /* already defined? */
|
||||
const char *msg = "label '%s' already defined on line %d";
|
||||
msg = luaO_pushfstring(ls->L, msg, getstr(name), lb->line);
|
||||
luaK_semerror(ls, msg); /* error */
|
||||
@@ -1490,7 +1500,7 @@ static void repeatstat (LexState *ls, int line) {
|
||||
if (bl2.upval) { /* upvalues? */
|
||||
int exit = luaK_jump(fs); /* normal exit must jump over fix */
|
||||
luaK_patchtohere(fs, condexit); /* repetition must close upvalues */
|
||||
luaK_codeABC(fs, OP_CLOSE, stacklevel(fs, bl2.nactvar), 0, 0);
|
||||
luaK_codeABC(fs, OP_CLOSE, reglevel(fs, bl2.nactvar), 0, 0);
|
||||
condexit = luaK_jump(fs); /* repeat after closing upvalues */
|
||||
luaK_patchtohere(fs, exit); /* normal exit comes to here */
|
||||
}
|
||||
@@ -1522,7 +1532,7 @@ static void fixforjump (FuncState *fs, int pc, int dest, int back) {
|
||||
int offset = dest - (pc + 1);
|
||||
if (back)
|
||||
offset = -offset;
|
||||
if (unlikely(offset > MAXARG_Bx))
|
||||
if (l_unlikely(offset > MAXARG_Bx))
|
||||
luaX_syntaxerror(fs->ls, "control structure too long");
|
||||
SETARG_Bx(*jmp, offset);
|
||||
}
|
||||
@@ -1601,7 +1611,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, fs->nactvar); /* last control var. must be closed */
|
||||
marktobeclosed(fs); /* last control var. must be closed */
|
||||
luaK_checkstack(fs, 3); /* extra space to call generator */
|
||||
forbody(ls, base, line, nvars - 4, 1);
|
||||
}
|
||||
@@ -1625,59 +1635,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);
|
||||
@@ -1686,7 +1658,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;
|
||||
@@ -1743,18 +1715,16 @@ static int getlocalattribute (LexState *ls) {
|
||||
}
|
||||
|
||||
|
||||
static void checktoclose (LexState *ls, int level) {
|
||||
static void checktoclose (FuncState *fs, int level) {
|
||||
if (level != -1) { /* is there a to-be-closed variable? */
|
||||
FuncState *fs = ls->fs;
|
||||
markupval(fs, level + 1);
|
||||
fs->bl->insidetbc = 1; /* in the scope of a to-be-closed variable */
|
||||
luaK_codeABC(fs, OP_TBC, stacklevel(fs, level), 0, 0);
|
||||
marktobeclosed(fs);
|
||||
luaK_codeABC(fs, OP_TBC, reglevel(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 */
|
||||
@@ -1791,7 +1761,7 @@ static void localstat (LexState *ls) {
|
||||
adjust_assign(ls, nvars, nexps, &e);
|
||||
adjustlocalvars(ls, nvars);
|
||||
}
|
||||
checktoclose(ls, toclose);
|
||||
checktoclose(fs, toclose);
|
||||
}
|
||||
|
||||
|
||||
@@ -1816,6 +1786,7 @@ static void funcstat (LexState *ls, int line) {
|
||||
luaX_next(ls); /* skip FUNCTION */
|
||||
ismethod = funcname(ls, &v);
|
||||
body(ls, &b, ismethod, line);
|
||||
check_readonly(ls, &v);
|
||||
luaK_storevar(ls->fs, &v, &b);
|
||||
luaK_fixline(ls->fs, line); /* definition "happens" in the first line */
|
||||
}
|
||||
@@ -1973,10 +1944,10 @@ LClosure *luaY_parser (lua_State *L, ZIO *z, Mbuffer *buff,
|
||||
LexState lexstate;
|
||||
FuncState funcstate;
|
||||
LClosure *cl = luaF_newLclosure(L, 1); /* create main closure */
|
||||
setclLvalue2s(L, L->top, cl); /* anchor it (to avoid being collected) */
|
||||
setclLvalue2s(L, L->top.p, cl); /* anchor it (to avoid being collected) */
|
||||
luaD_inctop(L);
|
||||
lexstate.h = luaH_new(L); /* create table for scanner */
|
||||
sethvalue2s(L, L->top, lexstate.h); /* anchor it */
|
||||
sethvalue2s(L, L->top.p, lexstate.h); /* anchor it */
|
||||
luaD_inctop(L);
|
||||
funcstate.f = cl->p = luaF_newproto(L);
|
||||
luaC_objbarrier(L, cl, cl->p);
|
||||
@@ -1990,7 +1961,7 @@ LClosure *luaY_parser (lua_State *L, ZIO *z, Mbuffer *buff,
|
||||
lua_assert(!funcstate.prev && funcstate.nups == 1 && !lexstate.fs);
|
||||
/* all scopes should be correctly finished */
|
||||
lua_assert(dyd->actvar.n == 0 && dyd->gt.n == 0 && dyd->label.n == 0);
|
||||
L->top--; /* remove scanner's table */
|
||||
L->top.p--; /* remove scanner's table */
|
||||
return cl; /* closure is on the stack, too */
|
||||
}
|
||||
|
||||
|
||||
11
lparser.h
11
lparser.h
@@ -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.sidx = stack index (local register);
|
||||
VLOCAL, /* local variable; var.ridx = register index;
|
||||
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 */
|
||||
@@ -76,7 +77,7 @@ typedef struct expdesc {
|
||||
lu_byte t; /* table (register or upvalue) */
|
||||
} ind;
|
||||
struct { /* for local variables */
|
||||
lu_byte sidx; /* index in the stack */
|
||||
lu_byte ridx; /* register holding the variable */
|
||||
unsigned short vidx; /* compiler index (in 'actvar.arr') */
|
||||
} var;
|
||||
} u;
|
||||
@@ -96,7 +97,7 @@ typedef union Vardesc {
|
||||
struct {
|
||||
TValuefields; /* constant value (if it is a compile-time constant) */
|
||||
lu_byte kind;
|
||||
lu_byte sidx; /* index of the variable in the stack */
|
||||
lu_byte ridx; /* register holding the variable */
|
||||
short pidx; /* index of the variable in the Proto's 'locvars' array */
|
||||
TString *name; /* variable name */
|
||||
} vd;
|
||||
|
||||
205
lstate.c
205
lstate.c
@@ -76,7 +76,7 @@ static unsigned int luai_makeseed (lua_State *L) {
|
||||
addbuff(buff, p, &h); /* local variable */
|
||||
addbuff(buff, p, &lua_newstate); /* public function */
|
||||
lua_assert(p == sizeof(buff));
|
||||
return luaS_hash(buff, p, h, 1);
|
||||
return luaS_hash(buff, p, h);
|
||||
}
|
||||
|
||||
#endif
|
||||
@@ -97,66 +97,14 @@ void luaE_setdebt (global_State *g, l_mem debt) {
|
||||
|
||||
|
||||
LUA_API int lua_setcstacklimit (lua_State *L, unsigned int limit) {
|
||||
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,13 +123,11 @@ 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 */
|
||||
}
|
||||
|
||||
|
||||
@@ -194,7 +140,6 @@ void luaE_shrinkCI (lua_State *L) {
|
||||
CallInfo *next;
|
||||
if (ci == NULL)
|
||||
return; /* no extra elements */
|
||||
L->nCcalls += L->nci; /* add removed elements back to 'nCcalls' */
|
||||
while ((next = ci->next) != NULL) { /* two extra elements? */
|
||||
CallInfo *next2 = next->next; /* next's next */
|
||||
ci->next = next2; /* remove next from the list */
|
||||
@@ -207,40 +152,61 @@ void luaE_shrinkCI (lua_State *L) {
|
||||
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 handling stack error */
|
||||
}
|
||||
|
||||
|
||||
LUAI_FUNC void luaE_incCstack (lua_State *L) {
|
||||
L->nCcalls++;
|
||||
if (l_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++)
|
||||
setnilvalue(s2v(L1->stack + i)); /* erase new stack */
|
||||
L1->top = L1->stack;
|
||||
L1->stack_last = L1->stack + L1->stacksize - EXTRA_STACK;
|
||||
L1->stack.p = luaM_newvector(L, BASIC_STACK_SIZE + EXTRA_STACK, StackValue);
|
||||
L1->tbclist.p = L1->stack.p;
|
||||
for (i = 0; i < BASIC_STACK_SIZE + EXTRA_STACK; i++)
|
||||
setnilvalue(s2v(L1->stack.p + i)); /* erase new stack */
|
||||
L1->top.p = L1->stack.p;
|
||||
L1->stack_last.p = L1->stack.p + BASIC_STACK_SIZE;
|
||||
/* initialize first ci */
|
||||
ci = &L1->base_ci;
|
||||
ci->next = ci->previous = NULL;
|
||||
ci->callstatus = CIST_C;
|
||||
ci->func = L1->top;
|
||||
ci->func.p = L1->top.p;
|
||||
ci->u.c.k = NULL;
|
||||
ci->nresults = 0;
|
||||
setnilvalue(s2v(L1->top)); /* 'function' entry for this 'ci' */
|
||||
L1->top++;
|
||||
ci->top = L1->top + LUA_MINSTACK;
|
||||
setnilvalue(s2v(L1->top.p)); /* 'function' entry for this 'ci' */
|
||||
L1->top.p++;
|
||||
ci->top.p = L1->top.p + LUA_MINSTACK;
|
||||
L1->ci = ci;
|
||||
}
|
||||
|
||||
|
||||
static void freestack (lua_State *L) {
|
||||
if (L->stack == NULL)
|
||||
if (L->stack.p == NULL)
|
||||
return; /* stack not completely built yet */
|
||||
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.p, stacksize(L) + EXTRA_STACK); /* free stack */
|
||||
}
|
||||
|
||||
|
||||
@@ -248,24 +214,19 @@ static void freestack (lua_State *L) {
|
||||
** Create registry table and its predefined values
|
||||
*/
|
||||
static void init_registry (lua_State *L, global_State *g) {
|
||||
TValue temp;
|
||||
/* create registry */
|
||||
Table *registry = luaH_new(L);
|
||||
sethvalue(L, &g->l_registry, registry);
|
||||
luaH_resize(L, registry, LUA_RIDX_LAST, 0);
|
||||
/* registry[LUA_RIDX_MAINTHREAD] = L */
|
||||
setthvalue(L, &temp, L); /* temp = L */
|
||||
luaH_setint(L, registry, LUA_RIDX_MAINTHREAD, &temp);
|
||||
/* registry[LUA_RIDX_GLOBALS] = table of globals */
|
||||
sethvalue(L, &temp, luaH_new(L)); /* temp = new table (global table) */
|
||||
luaH_setint(L, registry, LUA_RIDX_GLOBALS, &temp);
|
||||
setthvalue(L, ®istry->array[LUA_RIDX_MAINTHREAD - 1], L);
|
||||
/* registry[LUA_RIDX_GLOBALS] = new table (table of globals) */
|
||||
sethvalue(L, ®istry->array[LUA_RIDX_GLOBALS - 1], luaH_new(L));
|
||||
}
|
||||
|
||||
|
||||
/*
|
||||
** open parts of the state that may cause memory-allocation errors.
|
||||
** ('g->nilvalue' being a nil value flags that the state was completely
|
||||
** build.)
|
||||
*/
|
||||
static void f_luaopen (lua_State *L, void *ud) {
|
||||
global_State *g = G(L);
|
||||
@@ -275,8 +236,8 @@ static void f_luaopen (lua_State *L, void *ud) {
|
||||
luaS_init(L);
|
||||
luaT_init(L);
|
||||
luaX_init(L);
|
||||
g->gcrunning = 1; /* allow gc */
|
||||
setnilvalue(&g->nilvalue);
|
||||
g->gcstp = 0; /* allow gc */
|
||||
setnilvalue(&g->nilvalue); /* now state is complete */
|
||||
luai_userstateopen(L);
|
||||
}
|
||||
|
||||
@@ -287,11 +248,11 @@ static void f_luaopen (lua_State *L, void *ud) {
|
||||
*/
|
||||
static void preinit_thread (lua_State *L, global_State *g) {
|
||||
G(L) = g;
|
||||
L->stack = NULL;
|
||||
L->stack.p = NULL;
|
||||
L->ci = NULL;
|
||||
L->nci = 0;
|
||||
L->stacksize = 0;
|
||||
L->twups = L; /* thread has no upvalues */
|
||||
L->nCcalls = 0;
|
||||
L->errorJmp = NULL;
|
||||
L->hook = NULL;
|
||||
L->hookmask = 0;
|
||||
@@ -307,10 +268,14 @@ static void preinit_thread (lua_State *L, global_State *g) {
|
||||
|
||||
static void close_state (lua_State *L) {
|
||||
global_State *g = G(L);
|
||||
luaF_close(L, L->stack, CLOSEPROTECT); /* close all upvalues */
|
||||
luaC_freeallobjects(L); /* collect all objects */
|
||||
if (ttisnil(&g->nilvalue)) /* closing a fully built state? */
|
||||
if (!completestate(g)) /* closing a partially built state? */
|
||||
luaC_freeallobjects(L); /* just collect its objects */
|
||||
else { /* closing a fully built state */
|
||||
L->ci = &L->base_ci; /* unwind CallInfo list */
|
||||
luaD_closeprotected(L, 1, LUA_OK); /* close all upvalues */
|
||||
luaC_freeallobjects(L); /* collect all objects */
|
||||
luai_userstateclose(L);
|
||||
}
|
||||
luaM_freearray(L, G(L)->strt.hash, G(L)->strt.size);
|
||||
freestack(L);
|
||||
lua_assert(gettotalbytes(g) == sizeof(LG));
|
||||
@@ -319,23 +284,18 @@ static void close_state (lua_State *L) {
|
||||
|
||||
|
||||
LUA_API lua_State *lua_newthread (lua_State *L) {
|
||||
global_State *g;
|
||||
global_State *g = G(L);
|
||||
GCObject *o;
|
||||
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_VTHREAD;
|
||||
/* link it on list 'allgc' */
|
||||
L1->next = g->allgc;
|
||||
g->allgc = obj2gco(L1);
|
||||
o = luaC_newobjdt(L, LUA_TTHREAD, sizeof(LX), offsetof(LX, l));
|
||||
L1 = gco2th(o);
|
||||
/* anchor it on L stack */
|
||||
setthvalue2s(L, L->top, L1);
|
||||
setthvalue2s(L, L->top.p, L1);
|
||||
api_incr_top(L);
|
||||
preinit_thread(L1, g);
|
||||
L1->nCcalls = getCcalls(L);
|
||||
L1->hookmask = L->hookmask;
|
||||
L1->basehookcount = L->basehookcount;
|
||||
L1->hook = L->hook;
|
||||
@@ -352,7 +312,7 @@ LUA_API lua_State *lua_newthread (lua_State *L) {
|
||||
|
||||
void luaE_freethread (lua_State *L, lua_State *L1) {
|
||||
LX *l = fromstate(L1);
|
||||
luaF_close(L1, L1->stack, NOCLOSINGMETH); /* close all upvalues */
|
||||
luaF_closeupval(L1, L1->stack.p); /* close all upvalues */
|
||||
lua_assert(L1->openupval == NULL);
|
||||
luai_userstatefree(L, L1);
|
||||
freestack(L1);
|
||||
@@ -360,28 +320,43 @@ void luaE_freethread (lua_State *L, lua_State *L1) {
|
||||
}
|
||||
|
||||
|
||||
int lua_resetthread (lua_State *L) {
|
||||
CallInfo *ci;
|
||||
int luaE_resetthread (lua_State *L, int status) {
|
||||
CallInfo *ci = L->ci = &L->base_ci; /* unwind CallInfo list */
|
||||
setnilvalue(s2v(L->stack.p)); /* 'function' entry for basic 'ci' */
|
||||
ci->func.p = L->stack.p;
|
||||
ci->callstatus = CIST_C;
|
||||
if (status == LUA_YIELD)
|
||||
status = LUA_OK;
|
||||
L->status = LUA_OK; /* so it can run __close metamethods */
|
||||
status = luaD_closeprotected(L, 1, status);
|
||||
if (status != LUA_OK) /* errors? */
|
||||
luaD_seterrorobj(L, status, L->stack.p + 1);
|
||||
else
|
||||
L->top.p = L->stack.p + 1;
|
||||
ci->top.p = L->top.p + LUA_MINSTACK;
|
||||
luaD_reallocstack(L, cast_int(ci->top.p - L->stack.p), 0);
|
||||
return status;
|
||||
}
|
||||
|
||||
|
||||
LUA_API int lua_closethread (lua_State *L, lua_State *from) {
|
||||
int status;
|
||||
lua_lock(L);
|
||||
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->top = L->top + LUA_MINSTACK;
|
||||
L->status = status;
|
||||
L->nCcalls = (from) ? getCcalls(from) : 0;
|
||||
status = luaE_resetthread(L, L->status);
|
||||
lua_unlock(L);
|
||||
return status;
|
||||
}
|
||||
|
||||
|
||||
/*
|
||||
** Deprecated! Use 'lua_closethread' instead.
|
||||
*/
|
||||
LUA_API int lua_resetthread (lua_State *L) {
|
||||
return lua_closethread(L, NULL);
|
||||
}
|
||||
|
||||
|
||||
LUA_API lua_State *lua_newstate (lua_Alloc f, void *ud) {
|
||||
int i;
|
||||
lua_State *L;
|
||||
@@ -396,7 +371,6 @@ LUA_API lua_State *lua_newstate (lua_Alloc f, void *ud) {
|
||||
preinit_thread(L, g);
|
||||
g->allgc = obj2gco(L); /* by now, only object is the main thread */
|
||||
L->next = NULL;
|
||||
g->Cstacklimit = L->nCcalls = LUAI_MAXCSTACK + CSTACKERR;
|
||||
incnny(L); /* main thread is always non yieldable */
|
||||
g->frealloc = f;
|
||||
g->ud = ud;
|
||||
@@ -404,13 +378,14 @@ LUA_API lua_State *lua_newstate (lua_Alloc f, void *ud) {
|
||||
g->ud_warn = NULL;
|
||||
g->mainthread = L;
|
||||
g->seed = luai_makeseed(L);
|
||||
g->gcrunning = 0; /* no GC while building state */
|
||||
g->gcstp = GCSTPGC; /* no GC while building state */
|
||||
g->strt.size = g->strt.nuse = 0;
|
||||
g->strt.hash = NULL;
|
||||
setnilvalue(&g->l_registry);
|
||||
g->panic = NULL;
|
||||
g->gcstate = GCSpause;
|
||||
g->gckind = KGC_INC;
|
||||
g->gcstopem = 0;
|
||||
g->gcemergency = 0;
|
||||
g->finobj = g->tobefnz = g->fixedgc = NULL;
|
||||
g->firstold1 = g->survival = g->old1 = g->reallyold = NULL;
|
||||
@@ -456,7 +431,7 @@ void luaE_warning (lua_State *L, const char *msg, int tocont) {
|
||||
** Generate a warning from an error message
|
||||
*/
|
||||
void luaE_warnerror (lua_State *L, const char *where) {
|
||||
TValue *errobj = s2v(L->top - 1); /* error object */
|
||||
TValue *errobj = s2v(L->top.p - 1); /* error object */
|
||||
const char *msg = (ttisstring(errobj))
|
||||
? svalue(errobj)
|
||||
: "error object is not a string";
|
||||
|
||||
151
lstate.h
151
lstate.h
@@ -9,6 +9,11 @@
|
||||
|
||||
#include "lua.h"
|
||||
|
||||
|
||||
/* Some header files included here need this definition */
|
||||
typedef struct CallInfo CallInfo;
|
||||
|
||||
|
||||
#include "lobject.h"
|
||||
#include "ltm.h"
|
||||
#include "lzio.h"
|
||||
@@ -87,49 +92,13 @@
|
||||
|
||||
|
||||
/*
|
||||
** 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)
|
||||
@@ -144,13 +113,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)
|
||||
|
||||
|
||||
|
||||
@@ -168,12 +132,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.p - (th)->stack.p)
|
||||
|
||||
|
||||
/* kinds of Garbage Collection */
|
||||
#define KGC_INC 0 /* incremental gc */
|
||||
@@ -189,10 +161,22 @@ typedef struct stringtable {
|
||||
|
||||
/*
|
||||
** Information about a call.
|
||||
** About union 'u':
|
||||
** - field 'l' is used only for Lua functions;
|
||||
** - field 'c' is used only for C functions.
|
||||
** About union 'u2':
|
||||
** - field 'funcidx' is used only by C functions while doing a
|
||||
** protected call;
|
||||
** - field 'nyield' is used only while a function is "doing" an
|
||||
** yield (from the yield until the next resume);
|
||||
** - field 'nres' is used only while closing tbc variables when
|
||||
** returning from a function;
|
||||
** - field 'transferinfo' is used only during call/returnhooks,
|
||||
** before the function starts or after it ends.
|
||||
*/
|
||||
typedef struct CallInfo {
|
||||
StkId func; /* function index in the stack */
|
||||
StkId top; /* top for this function */
|
||||
struct CallInfo {
|
||||
StkIdRel func; /* function index in the stack */
|
||||
StkIdRel top; /* top for this function */
|
||||
struct CallInfo *previous, *next; /* dynamic call link */
|
||||
union {
|
||||
struct { /* only for Lua functions */
|
||||
@@ -209,6 +193,7 @@ typedef struct CallInfo {
|
||||
union {
|
||||
int funcidx; /* called-function index */
|
||||
int nyield; /* number of values yielded */
|
||||
int nres; /* number of values returned */
|
||||
struct { /* info about transferred values (for call/return hooks) */
|
||||
unsigned short ftransfer; /* offset of first value transferred */
|
||||
unsigned short ntransfer; /* number of values transferred */
|
||||
@@ -216,7 +201,7 @@ typedef struct CallInfo {
|
||||
} u2;
|
||||
short nresults; /* expected number of results from this function */
|
||||
unsigned short callstatus;
|
||||
} CallInfo;
|
||||
};
|
||||
|
||||
|
||||
/*
|
||||
@@ -224,16 +209,34 @@ 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) /* doing 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) /* function "called" a finalizer */
|
||||
#define CIST_TRAN (1<<8) /* 'ci' has transfer information */
|
||||
#define CIST_CLSRET (1<<9) /* function is closing tbc variables */
|
||||
/* Bits 10-12 are used for CIST_RECST (see below) */
|
||||
#define CIST_RECST 10
|
||||
#if defined(LUA_COMPAT_LT_LE)
|
||||
#define CIST_LEQ (1<<8) /* using __lt for __le */
|
||||
#define CIST_LEQ (1<<13) /* using __lt for __le */
|
||||
#endif
|
||||
|
||||
|
||||
/*
|
||||
** Field CIST_RECST stores the "recover status", used to keep the error
|
||||
** status while closing to-be-closed variables in coroutines, so that
|
||||
** Lua can correctly resume after an yield from a __close method called
|
||||
** because of an error. (Three bits are enough for error status.)
|
||||
*/
|
||||
#define getcistrecst(ci) (((ci)->callstatus >> CIST_RECST) & 7)
|
||||
#define setcistrecst(ci,st) \
|
||||
check_exp(((st) & 7) == (st), /* status must fit in three bits */ \
|
||||
((ci)->callstatus = ((ci)->callstatus & ~(7 << CIST_RECST)) \
|
||||
| ((st) << CIST_RECST)))
|
||||
|
||||
|
||||
/* active function is a Lua function */
|
||||
#define isLua(ci) (!((ci)->callstatus & CIST_C))
|
||||
|
||||
@@ -262,9 +265,10 @@ typedef struct global_State {
|
||||
lu_byte currentwhite;
|
||||
lu_byte gcstate; /* state of garbage collector */
|
||||
lu_byte gckind; /* kind of GC running */
|
||||
lu_byte gcstopem; /* stops emergency collections */
|
||||
lu_byte genminormul; /* control for minor generational collections */
|
||||
lu_byte genmajormul; /* control for major generational collections */
|
||||
lu_byte gcrunning; /* true if GC is running */
|
||||
lu_byte gcstp; /* control whether GC is running */
|
||||
lu_byte gcemergency; /* true if this is an emergency collection */
|
||||
lu_byte gcpause; /* size of pause between successive GCs */
|
||||
lu_byte gcstepmul; /* GC "speed" */
|
||||
@@ -292,11 +296,10 @@ typedef struct global_State {
|
||||
struct lua_State *mainthread;
|
||||
TString *memerrmsg; /* message for memory-allocation errors */
|
||||
TString *tmname[TM_N]; /* array with tag-method names */
|
||||
struct Table *mt[LUA_NUMTAGS]; /* metatables for basic types */
|
||||
struct Table *mt[LUA_NUMTYPES]; /* metatables for basic types */
|
||||
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;
|
||||
|
||||
|
||||
@@ -308,21 +311,21 @@ struct lua_State {
|
||||
lu_byte status;
|
||||
lu_byte allowhook;
|
||||
unsigned short nci; /* number of items in 'ci' list */
|
||||
StkId top; /* first free slot in the stack */
|
||||
StkIdRel top; /* first free slot in the stack */
|
||||
global_State *l_G;
|
||||
CallInfo *ci; /* call info for current function */
|
||||
StkId stack_last; /* last free slot in the stack */
|
||||
StkId stack; /* stack base */
|
||||
StkIdRel stack_last; /* end of stack (last element + 1) */
|
||||
StkIdRel stack; /* stack base */
|
||||
UpVal *openupval; /* list of open upvalues in this stack */
|
||||
StkIdRel tbclist; /* list of to-be-closed variables */
|
||||
GCObject *gclist;
|
||||
struct lua_State *twups; /* list of threads with open upvalues */
|
||||
struct lua_longjmp *errorJmp; /* current error recover point */
|
||||
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' */
|
||||
l_uint32 nCcalls; /* number of nested (non-yieldable | C) calls */
|
||||
int oldpc; /* last pc traced */
|
||||
int stacksize;
|
||||
int basehookcount;
|
||||
int hookcount;
|
||||
volatile l_signalT hookmask;
|
||||
@@ -331,6 +334,12 @@ struct lua_State {
|
||||
|
||||
#define G(L) (L->l_G)
|
||||
|
||||
/*
|
||||
** 'g->nilvalue' being a nil value flags that the state was completely
|
||||
** build.
|
||||
*/
|
||||
#define completestate(g) ttisnil(&g->nilvalue)
|
||||
|
||||
|
||||
/*
|
||||
** Union of all collectable objects (only for conversions)
|
||||
@@ -389,12 +398,12 @@ 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);
|
||||
LUAI_FUNC int luaE_resetthread (lua_State *L, int status);
|
||||
|
||||
|
||||
#define luaE_exitCcall(L) ((L)->nCcalls++)
|
||||
|
||||
#endif
|
||||
|
||||
|
||||
28
lstring.c
28
lstring.c
@@ -22,16 +22,6 @@
|
||||
#include "lstring.h"
|
||||
|
||||
|
||||
/*
|
||||
** Lua will use at most ~(2^LUAI_HASHLIMIT) bytes from a long string to
|
||||
** compute its hash
|
||||
*/
|
||||
#if !defined(LUAI_HASHLIMIT)
|
||||
#define LUAI_HASHLIMIT 5
|
||||
#endif
|
||||
|
||||
|
||||
|
||||
/*
|
||||
** Maximum size for string table.
|
||||
*/
|
||||
@@ -50,10 +40,9 @@ int luaS_eqlngstr (TString *a, TString *b) {
|
||||
}
|
||||
|
||||
|
||||
unsigned int luaS_hash (const char *str, size_t l, unsigned int seed,
|
||||
size_t step) {
|
||||
unsigned int luaS_hash (const char *str, size_t l, unsigned int seed) {
|
||||
unsigned int h = seed ^ cast_uint(l);
|
||||
for (; l >= step; l -= step)
|
||||
for (; l > 0; l--)
|
||||
h ^= ((h<<5) + (h>>2) + cast_byte(str[l - 1]));
|
||||
return h;
|
||||
}
|
||||
@@ -63,8 +52,7 @@ unsigned int luaS_hashlongstr (TString *ts) {
|
||||
lua_assert(ts->tt == LUA_VLNGSTR);
|
||||
if (ts->extra == 0) { /* no hash? */
|
||||
size_t len = ts->u.lnglen;
|
||||
size_t step = (len >> LUAI_HASHLIMIT) + 1;
|
||||
ts->hash = luaS_hash(getstr(ts), len, ts->hash, step);
|
||||
ts->hash = luaS_hash(getstr(ts), len, ts->hash);
|
||||
ts->extra = 1; /* now it has its hash */
|
||||
}
|
||||
return ts->hash;
|
||||
@@ -101,7 +89,7 @@ void luaS_resize (lua_State *L, int nsize) {
|
||||
if (nsize < osize) /* shrinking table? */
|
||||
tablerehash(tb->hash, osize, nsize); /* depopulate shrinking part */
|
||||
newvect = luaM_reallocvector(L, tb->hash, osize, nsize, TString*);
|
||||
if (unlikely(newvect == NULL)) { /* reallocation failed? */
|
||||
if (l_unlikely(newvect == NULL)) { /* reallocation failed? */
|
||||
if (nsize < osize) /* was it shrinking table? */
|
||||
tablerehash(tb->hash, nsize, osize); /* restore to original size */
|
||||
/* leave table as it was */
|
||||
@@ -184,7 +172,7 @@ void luaS_remove (lua_State *L, TString *ts) {
|
||||
|
||||
|
||||
static void growstrtab (lua_State *L, stringtable *tb) {
|
||||
if (unlikely(tb->nuse == MAX_INT)) { /* too many strings? */
|
||||
if (l_unlikely(tb->nuse == MAX_INT)) { /* too many strings? */
|
||||
luaC_fullgc(L, 1); /* try to free some... */
|
||||
if (tb->nuse == MAX_INT) /* still too many? */
|
||||
luaM_error(L); /* cannot even create a message... */
|
||||
@@ -201,7 +189,7 @@ static TString *internshrstr (lua_State *L, const char *str, size_t l) {
|
||||
TString *ts;
|
||||
global_State *g = G(L);
|
||||
stringtable *tb = &g->strt;
|
||||
unsigned int h = luaS_hash(str, l, g->seed, 1);
|
||||
unsigned int h = luaS_hash(str, l, g->seed);
|
||||
TString **list = &tb->hash[lmod(h, tb->size)];
|
||||
lua_assert(str != NULL); /* otherwise 'memcmp'/'memcpy' are undefined */
|
||||
for (ts = *list; ts != NULL; ts = ts->u.hnext) {
|
||||
@@ -235,7 +223,7 @@ TString *luaS_newlstr (lua_State *L, const char *str, size_t l) {
|
||||
return internshrstr(L, str, l);
|
||||
else {
|
||||
TString *ts;
|
||||
if (unlikely(l >= (MAX_SIZE - sizeof(TString))/sizeof(char)))
|
||||
if (l_unlikely(l >= (MAX_SIZE - sizeof(TString))/sizeof(char)))
|
||||
luaM_toobig(L);
|
||||
ts = luaS_createlngstrobj(L, l);
|
||||
memcpy(getstr(ts), str, l * sizeof(char));
|
||||
@@ -271,7 +259,7 @@ Udata *luaS_newudata (lua_State *L, size_t s, int nuvalue) {
|
||||
Udata *u;
|
||||
int i;
|
||||
GCObject *o;
|
||||
if (unlikely(s > MAX_SIZE - udatamemoffset(nuvalue)))
|
||||
if (l_unlikely(s > MAX_SIZE - udatamemoffset(nuvalue)))
|
||||
luaM_toobig(L);
|
||||
o = luaC_newobj(L, LUA_VUSERDATA, sizeudata(nuvalue, s));
|
||||
u = gco2u(o);
|
||||
|
||||
@@ -41,8 +41,7 @@
|
||||
#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, size_t step);
|
||||
LUAI_FUNC unsigned int luaS_hash (const char *str, size_t l, unsigned int seed);
|
||||
LUAI_FUNC unsigned int luaS_hashlongstr (TString *ts);
|
||||
LUAI_FUNC int luaS_eqlngstr (TString *a, TString *b);
|
||||
LUAI_FUNC void luaS_resize (lua_State *L, int newsize);
|
||||
|
||||
253
lstrlib.c
253
lstrlib.c
@@ -152,8 +152,9 @@ static int str_rep (lua_State *L) {
|
||||
const char *s = luaL_checklstring(L, 1, &l);
|
||||
lua_Integer n = luaL_checkinteger(L, 2);
|
||||
const char *sep = luaL_optlstring(L, 3, "", &lsep);
|
||||
if (n <= 0) lua_pushliteral(L, "");
|
||||
else if (l + lsep < l || l + lsep > MAXSIZE / n) /* may overflow? */
|
||||
if (n <= 0)
|
||||
lua_pushliteral(L, "");
|
||||
else if (l_unlikely(l + lsep < l || l + lsep > MAXSIZE / n))
|
||||
return luaL_error(L, "resulting string too large");
|
||||
else {
|
||||
size_t totallen = (size_t)n * l + (size_t)(n - 1) * lsep;
|
||||
@@ -181,7 +182,7 @@ static int str_byte (lua_State *L) {
|
||||
size_t pose = getendpos(L, 3, pi, l);
|
||||
int n, i;
|
||||
if (posi > pose) return 0; /* empty interval; return no values */
|
||||
if (pose - posi >= (size_t)INT_MAX) /* arithmetic overflow? */
|
||||
if (l_unlikely(pose - posi >= (size_t)INT_MAX)) /* arithmetic overflow? */
|
||||
return luaL_error(L, "string slice too long");
|
||||
n = (int)(pose - posi) + 1;
|
||||
luaL_checkstack(L, n, "string slice too long");
|
||||
@@ -235,7 +236,7 @@ static int str_dump (lua_State *L) {
|
||||
luaL_checktype(L, 1, LUA_TFUNCTION);
|
||||
lua_settop(L, 1); /* ensure function is on the top of the stack */
|
||||
state.init = 0;
|
||||
if (lua_dump(L, writer, &state, strip) != 0)
|
||||
if (l_unlikely(lua_dump(L, writer, &state, strip) != 0))
|
||||
return luaL_error(L, "unable to dump given function");
|
||||
luaL_pushresult(&state.B);
|
||||
return 1;
|
||||
@@ -275,7 +276,8 @@ static int tonum (lua_State *L, int arg) {
|
||||
|
||||
static void trymt (lua_State *L, const char *mtname) {
|
||||
lua_settop(L, 2); /* back to the original arguments */
|
||||
if (lua_type(L, 2) == LUA_TSTRING || !luaL_getmetafield(L, 2, mtname))
|
||||
if (l_unlikely(lua_type(L, 2) == LUA_TSTRING ||
|
||||
!luaL_getmetafield(L, 2, mtname)))
|
||||
luaL_error(L, "attempt to %s a '%s' with a '%s'", mtname + 2,
|
||||
luaL_typename(L, -2), luaL_typename(L, -1));
|
||||
lua_insert(L, -3); /* put metamethod before arguments */
|
||||
@@ -383,7 +385,8 @@ static const char *match (MatchState *ms, const char *s, const char *p);
|
||||
|
||||
static int check_capture (MatchState *ms, int l) {
|
||||
l -= '1';
|
||||
if (l < 0 || l >= ms->level || ms->capture[l].len == CAP_UNFINISHED)
|
||||
if (l_unlikely(l < 0 || l >= ms->level ||
|
||||
ms->capture[l].len == CAP_UNFINISHED))
|
||||
return luaL_error(ms->L, "invalid capture index %%%d", l + 1);
|
||||
return l;
|
||||
}
|
||||
@@ -400,14 +403,14 @@ static int capture_to_close (MatchState *ms) {
|
||||
static const char *classend (MatchState *ms, const char *p) {
|
||||
switch (*p++) {
|
||||
case L_ESC: {
|
||||
if (p == ms->p_end)
|
||||
if (l_unlikely(p == ms->p_end))
|
||||
luaL_error(ms->L, "malformed pattern (ends with '%%')");
|
||||
return p+1;
|
||||
}
|
||||
case '[': {
|
||||
if (*p == '^') p++;
|
||||
do { /* look for a ']' */
|
||||
if (p == ms->p_end)
|
||||
if (l_unlikely(p == ms->p_end))
|
||||
luaL_error(ms->L, "malformed pattern (missing ']')");
|
||||
if (*(p++) == L_ESC && p < ms->p_end)
|
||||
p++; /* skip escapes (e.g. '%]') */
|
||||
@@ -482,7 +485,7 @@ static int singlematch (MatchState *ms, const char *s, const char *p,
|
||||
|
||||
static const char *matchbalance (MatchState *ms, const char *s,
|
||||
const char *p) {
|
||||
if (p >= ms->p_end - 1)
|
||||
if (l_unlikely(p >= ms->p_end - 1))
|
||||
luaL_error(ms->L, "malformed pattern (missing arguments to '%%b')");
|
||||
if (*s != *p) return NULL;
|
||||
else {
|
||||
@@ -565,9 +568,9 @@ static const char *match_capture (MatchState *ms, const char *s, int l) {
|
||||
|
||||
|
||||
static const char *match (MatchState *ms, const char *s, const char *p) {
|
||||
if (ms->matchdepth-- == 0)
|
||||
if (l_unlikely(ms->matchdepth-- == 0))
|
||||
luaL_error(ms->L, "pattern too complex");
|
||||
init: /* using goto's to optimize tail recursion */
|
||||
init: /* using goto to optimize tail recursion */
|
||||
if (p != ms->p_end) { /* end of pattern? */
|
||||
switch (*p) {
|
||||
case '(': { /* start capture */
|
||||
@@ -599,7 +602,7 @@ static const char *match (MatchState *ms, const char *s, const char *p) {
|
||||
case 'f': { /* frontier? */
|
||||
const char *ep; char previous;
|
||||
p += 2;
|
||||
if (*p != '[')
|
||||
if (l_unlikely(*p != '['))
|
||||
luaL_error(ms->L, "missing '[' after '%%f' in pattern");
|
||||
ep = classend(ms, p); /* points to what is next */
|
||||
previous = (s == ms->src_init) ? '\0' : *(s - 1);
|
||||
@@ -699,7 +702,7 @@ static const char *lmemfind (const char *s1, size_t l1,
|
||||
static size_t get_onecapture (MatchState *ms, int i, const char *s,
|
||||
const char *e, const char **cap) {
|
||||
if (i >= ms->level) {
|
||||
if (i != 0)
|
||||
if (l_unlikely(i != 0))
|
||||
luaL_error(ms->L, "invalid capture index %%%d", i + 1);
|
||||
*cap = s;
|
||||
return e - s;
|
||||
@@ -707,7 +710,7 @@ static size_t get_onecapture (MatchState *ms, int i, const char *s,
|
||||
else {
|
||||
ptrdiff_t capl = ms->capture[i].len;
|
||||
*cap = ms->capture[i].init;
|
||||
if (capl == CAP_UNFINISHED)
|
||||
if (l_unlikely(capl == CAP_UNFINISHED))
|
||||
luaL_error(ms->L, "unfinished capture");
|
||||
else if (capl == CAP_POSITION)
|
||||
lua_pushinteger(ms->L, (ms->capture[i].init - ms->src_init) + 1);
|
||||
@@ -926,7 +929,7 @@ static int add_value (MatchState *ms, luaL_Buffer *b, const char *s,
|
||||
luaL_addlstring(b, s, e - s); /* keep original text */
|
||||
return 0; /* no changes */
|
||||
}
|
||||
else if (!lua_isstring(L, -1))
|
||||
else if (l_unlikely(!lua_isstring(L, -1)))
|
||||
return luaL_error(L, "invalid replacement value (a %s)",
|
||||
luaL_typename(L, -1));
|
||||
else {
|
||||
@@ -1058,7 +1061,7 @@ static int lua_number2strx (lua_State *L, char *buff, int sz,
|
||||
for (i = 0; i < n; i++)
|
||||
buff[i] = toupper(uchar(buff[i]));
|
||||
}
|
||||
else if (fmt[SIZELENMOD] != 'a')
|
||||
else if (l_unlikely(fmt[SIZELENMOD] != 'a'))
|
||||
return luaL_error(L, "modifiers for format '%%a'/'%%A' not implemented");
|
||||
return n;
|
||||
}
|
||||
@@ -1087,13 +1090,31 @@ static int lua_number2strx (lua_State *L, char *buff, int sz,
|
||||
|
||||
|
||||
/* valid flags in a format specification */
|
||||
#if !defined(L_FMTFLAGS)
|
||||
#define L_FMTFLAGS "-+ #0"
|
||||
#if !defined(L_FMTFLAGSF)
|
||||
|
||||
/* valid flags for a, A, e, E, f, F, g, and G conversions */
|
||||
#define L_FMTFLAGSF "-+#0 "
|
||||
|
||||
/* valid flags for o, x, and X conversions */
|
||||
#define L_FMTFLAGSX "-#0"
|
||||
|
||||
/* valid flags for d and i conversions */
|
||||
#define L_FMTFLAGSI "-+0 "
|
||||
|
||||
/* valid flags for u conversions */
|
||||
#define L_FMTFLAGSU "-0"
|
||||
|
||||
/* valid flags for c, p, and s conversions */
|
||||
#define L_FMTFLAGSC "-"
|
||||
|
||||
#endif
|
||||
|
||||
|
||||
/*
|
||||
** maximum size of each format specification (such as "%-099.99d")
|
||||
** Maximum size of each format specification (such as "%-099.99d"):
|
||||
** Initial '%', flags (up to 5), width (2), period, precision (2),
|
||||
** length modifier (8), conversion specifier, and final '\0', plus some
|
||||
** extra.
|
||||
*/
|
||||
#define MAX_FORMAT 32
|
||||
|
||||
@@ -1186,25 +1207,53 @@ 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(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) */
|
||||
if (*p == '.') {
|
||||
p++;
|
||||
if (isdigit(uchar(*p))) p++; /* skip precision */
|
||||
if (isdigit(uchar(*p))) p++; /* (2 digits at most) */
|
||||
static const char *get2digits (const char *s) {
|
||||
if (isdigit(uchar(*s))) {
|
||||
s++;
|
||||
if (isdigit(uchar(*s))) s++; /* (2 digits at most) */
|
||||
}
|
||||
if (isdigit(uchar(*p)))
|
||||
luaL_error(L, "invalid format (width or precision too long)");
|
||||
return s;
|
||||
}
|
||||
|
||||
|
||||
/*
|
||||
** Check whether a conversion specification is valid. When called,
|
||||
** first character in 'form' must be '%' and last character must
|
||||
** be a valid conversion specifier. 'flags' are the accepted flags;
|
||||
** 'precision' signals whether to accept a precision.
|
||||
*/
|
||||
static void checkformat (lua_State *L, const char *form, const char *flags,
|
||||
int precision) {
|
||||
const char *spec = form + 1; /* skip '%' */
|
||||
spec += strspn(spec, flags); /* skip flags */
|
||||
if (*spec != '0') { /* a width cannot start with '0' */
|
||||
spec = get2digits(spec); /* skip width */
|
||||
if (*spec == '.' && precision) {
|
||||
spec++;
|
||||
spec = get2digits(spec); /* skip precision */
|
||||
}
|
||||
}
|
||||
if (!isalpha(uchar(*spec))) /* did not go to the end? */
|
||||
luaL_error(L, "invalid conversion specification: '%s'", form);
|
||||
}
|
||||
|
||||
|
||||
/*
|
||||
** Get a conversion specification and copy it to 'form'.
|
||||
** Return the address of its last character.
|
||||
*/
|
||||
static const char *getformat (lua_State *L, const char *strfrmt,
|
||||
char *form) {
|
||||
/* spans flags, width, and precision ('0' is included as a flag) */
|
||||
size_t len = strspn(strfrmt, L_FMTFLAGSF "123456789.");
|
||||
len++; /* adds following character (should be the specifier) */
|
||||
/* still needs space for '%', '\0', plus a length modifier */
|
||||
if (len >= MAX_FORMAT - 10)
|
||||
luaL_error(L, "invalid format (too long)");
|
||||
*(form++) = '%';
|
||||
memcpy(form, strfrmt, ((p - strfrmt) + 1) * sizeof(char));
|
||||
form += (p - strfrmt) + 1;
|
||||
*form = '\0';
|
||||
return p;
|
||||
memcpy(form, strfrmt, len * sizeof(char));
|
||||
*(form + len) = '\0';
|
||||
return strfrmt + len - 1;
|
||||
}
|
||||
|
||||
|
||||
@@ -1227,6 +1276,7 @@ static int str_format (lua_State *L) {
|
||||
size_t sfl;
|
||||
const char *strfrmt = luaL_checklstring(L, arg, &sfl);
|
||||
const char *strfrmt_end = strfrmt+sfl;
|
||||
const char *flags;
|
||||
luaL_Buffer b;
|
||||
luaL_buffinit(L, &b);
|
||||
while (strfrmt < strfrmt_end) {
|
||||
@@ -1236,25 +1286,35 @@ static int str_format (lua_State *L) {
|
||||
luaL_addchar(&b, *strfrmt++); /* %% */
|
||||
else { /* format item */
|
||||
char form[MAX_FORMAT]; /* to store the format ('%...') */
|
||||
int maxitem = MAX_ITEM;
|
||||
char *buff = luaL_prepbuffsize(&b, maxitem); /* to put formatted item */
|
||||
int nb = 0; /* number of bytes in added item */
|
||||
int maxitem = MAX_ITEM; /* maximum length for the result */
|
||||
char *buff = luaL_prepbuffsize(&b, maxitem); /* to put result */
|
||||
int nb = 0; /* number of bytes in result */
|
||||
if (++arg > top)
|
||||
return luaL_argerror(L, arg, "no value");
|
||||
strfrmt = scanformat(L, strfrmt, form);
|
||||
strfrmt = getformat(L, strfrmt, form);
|
||||
switch (*strfrmt++) {
|
||||
case 'c': {
|
||||
checkformat(L, form, L_FMTFLAGSC, 0);
|
||||
nb = l_sprintf(buff, maxitem, form, (int)luaL_checkinteger(L, arg));
|
||||
break;
|
||||
}
|
||||
case 'd': case 'i':
|
||||
case 'o': case 'u': case 'x': case 'X': {
|
||||
flags = L_FMTFLAGSI;
|
||||
goto intcase;
|
||||
case 'u':
|
||||
flags = L_FMTFLAGSU;
|
||||
goto intcase;
|
||||
case 'o': case 'x': case 'X':
|
||||
flags = L_FMTFLAGSX;
|
||||
intcase: {
|
||||
lua_Integer n = luaL_checkinteger(L, arg);
|
||||
checkformat(L, form, flags, 1);
|
||||
addlenmod(form, LUA_INTEGER_FRMLEN);
|
||||
nb = l_sprintf(buff, maxitem, form, (LUAI_UACINT)n);
|
||||
break;
|
||||
}
|
||||
case 'a': case 'A':
|
||||
checkformat(L, form, L_FMTFLAGSF, 1);
|
||||
addlenmod(form, LUA_NUMBER_FRMLEN);
|
||||
nb = lua_number2strx(L, buff, maxitem, form,
|
||||
luaL_checknumber(L, arg));
|
||||
@@ -1265,12 +1325,14 @@ static int str_format (lua_State *L) {
|
||||
/* FALLTHROUGH */
|
||||
case 'e': case 'E': case 'g': case 'G': {
|
||||
lua_Number n = luaL_checknumber(L, arg);
|
||||
checkformat(L, form, L_FMTFLAGSF, 1);
|
||||
addlenmod(form, LUA_NUMBER_FRMLEN);
|
||||
nb = l_sprintf(buff, maxitem, form, (LUAI_UACNUMBER)n);
|
||||
break;
|
||||
}
|
||||
case 'p': {
|
||||
const void *p = lua_topointer(L, arg);
|
||||
checkformat(L, form, L_FMTFLAGSC, 0);
|
||||
if (p == NULL) { /* avoid calling 'printf' with argument NULL */
|
||||
p = "(null)"; /* result */
|
||||
form[strlen(form) - 1] = 's'; /* format it as a string */
|
||||
@@ -1291,7 +1353,8 @@ static int str_format (lua_State *L) {
|
||||
luaL_addvalue(&b); /* keep entire string */
|
||||
else {
|
||||
luaL_argcheck(L, l == strlen(s), arg, "string contains zeros");
|
||||
if (!strchr(form, '.') && l >= 100) {
|
||||
checkformat(L, form, L_FMTFLAGSC, 1);
|
||||
if (strchr(form, '.') == NULL && l >= 100) {
|
||||
/* no precision and string is too long to be formatted */
|
||||
luaL_addvalue(&b); /* keep entire string */
|
||||
}
|
||||
@@ -1349,26 +1412,6 @@ static const union {
|
||||
} nativeendian = {1};
|
||||
|
||||
|
||||
/* dummy structure to get native alignment requirements */
|
||||
struct cD {
|
||||
char c;
|
||||
union { double d; void *p; lua_Integer i; lua_Number n; } u;
|
||||
};
|
||||
|
||||
#define MAXALIGN (offsetof(struct cD, u))
|
||||
|
||||
|
||||
/*
|
||||
** Union for serializing floats
|
||||
*/
|
||||
typedef union Ftypes {
|
||||
float f;
|
||||
double d;
|
||||
lua_Number n;
|
||||
char buff[5 * sizeof(lua_Number)]; /* enough for any float type */
|
||||
} Ftypes;
|
||||
|
||||
|
||||
/*
|
||||
** information to pack/unpack stuff
|
||||
*/
|
||||
@@ -1385,7 +1428,9 @@ typedef struct Header {
|
||||
typedef enum KOption {
|
||||
Kint, /* signed integers */
|
||||
Kuint, /* unsigned integers */
|
||||
Kfloat, /* floating-point numbers */
|
||||
Kfloat, /* single-precision floating-point numbers */
|
||||
Knumber, /* Lua "native" floating-point numbers */
|
||||
Kdouble, /* double-precision floating-point numbers */
|
||||
Kchar, /* fixed-length strings */
|
||||
Kstring, /* strings with prefixed length */
|
||||
Kzstr, /* zero-terminated strings */
|
||||
@@ -1420,7 +1465,7 @@ static int getnum (const char **fmt, int df) {
|
||||
*/
|
||||
static int getnumlimit (Header *h, const char **fmt, int df) {
|
||||
int sz = getnum(fmt, df);
|
||||
if (sz > MAXINTSIZE || sz <= 0)
|
||||
if (l_unlikely(sz > MAXINTSIZE || sz <= 0))
|
||||
return luaL_error(h->L, "integral size (%d) out of limits [1,%d]",
|
||||
sz, MAXINTSIZE);
|
||||
return sz;
|
||||
@@ -1441,6 +1486,8 @@ static void initheader (lua_State *L, Header *h) {
|
||||
** Read and classify next option. 'size' is filled with option's size.
|
||||
*/
|
||||
static KOption getoption (Header *h, const char **fmt, int *size) {
|
||||
/* dummy structure to get native alignment requirements */
|
||||
struct cD { char c; union { LUAI_MAXALIGN; } u; };
|
||||
int opt = *((*fmt)++);
|
||||
*size = 0; /* default */
|
||||
switch (opt) {
|
||||
@@ -1454,14 +1501,14 @@ static KOption getoption (Header *h, const char **fmt, int *size) {
|
||||
case 'J': *size = sizeof(lua_Integer); return Kuint;
|
||||
case 'T': *size = sizeof(size_t); return Kuint;
|
||||
case 'f': *size = sizeof(float); return Kfloat;
|
||||
case 'd': *size = sizeof(double); return Kfloat;
|
||||
case 'n': *size = sizeof(lua_Number); return Kfloat;
|
||||
case 'n': *size = sizeof(lua_Number); return Knumber;
|
||||
case 'd': *size = sizeof(double); return Kdouble;
|
||||
case 'i': *size = getnumlimit(h, fmt, sizeof(int)); return Kint;
|
||||
case 'I': *size = getnumlimit(h, fmt, sizeof(int)); return Kuint;
|
||||
case 's': *size = getnumlimit(h, fmt, sizeof(size_t)); return Kstring;
|
||||
case 'c':
|
||||
*size = getnum(fmt, -1);
|
||||
if (*size == -1)
|
||||
if (l_unlikely(*size == -1))
|
||||
luaL_error(h->L, "missing size for format option 'c'");
|
||||
return Kchar;
|
||||
case 'z': return Kzstr;
|
||||
@@ -1471,7 +1518,11 @@ static KOption getoption (Header *h, const char **fmt, int *size) {
|
||||
case '<': h->islittle = 1; break;
|
||||
case '>': h->islittle = 0; break;
|
||||
case '=': h->islittle = nativeendian.little; break;
|
||||
case '!': h->maxalign = getnumlimit(h, fmt, MAXALIGN); break;
|
||||
case '!': {
|
||||
const int maxalign = offsetof(struct cD, u);
|
||||
h->maxalign = getnumlimit(h, fmt, maxalign);
|
||||
break;
|
||||
}
|
||||
default: luaL_error(h->L, "invalid format option '%c'", opt);
|
||||
}
|
||||
return Knop;
|
||||
@@ -1500,7 +1551,7 @@ static KOption getdetails (Header *h, size_t totalsize,
|
||||
else {
|
||||
if (align > h->maxalign) /* enforce maximum alignment */
|
||||
align = h->maxalign;
|
||||
if ((align & (align - 1)) != 0) /* is 'align' not a power of 2? */
|
||||
if (l_unlikely((align & (align - 1)) != 0)) /* not a power of 2? */
|
||||
luaL_argerror(h->L, 1, "format asks for alignment not power of 2");
|
||||
*ntoalign = (align - (int)(totalsize & (align - 1))) & (align - 1);
|
||||
}
|
||||
@@ -1535,12 +1586,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)
|
||||
@@ -1583,15 +1632,27 @@ static int str_pack (lua_State *L) {
|
||||
packint(&b, (lua_Unsigned)n, h.islittle, size, 0);
|
||||
break;
|
||||
}
|
||||
case Kfloat: { /* floating-point options */
|
||||
volatile 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);
|
||||
case Kfloat: { /* C float */
|
||||
float f = (float)luaL_checknumber(L, arg); /* get argument */
|
||||
char *buff = luaL_prepbuffsize(&b, sizeof(f));
|
||||
/* move 'f' to final result, correcting endianness if needed */
|
||||
copywithendian(buff, (char *)&f, sizeof(f), h.islittle);
|
||||
luaL_addsize(&b, size);
|
||||
break;
|
||||
}
|
||||
case Knumber: { /* Lua float */
|
||||
lua_Number f = luaL_checknumber(L, arg); /* get argument */
|
||||
char *buff = luaL_prepbuffsize(&b, sizeof(f));
|
||||
/* move 'f' to final result, correcting endianness if needed */
|
||||
copywithendian(buff, (char *)&f, sizeof(f), h.islittle);
|
||||
luaL_addsize(&b, size);
|
||||
break;
|
||||
}
|
||||
case Kdouble: { /* C double */
|
||||
double f = (double)luaL_checknumber(L, arg); /* get argument */
|
||||
char *buff = luaL_prepbuffsize(&b, sizeof(f));
|
||||
/* move 'f' to final result, correcting endianness if needed */
|
||||
copywithendian(buff, (char *)&f, sizeof(f), h.islittle);
|
||||
luaL_addsize(&b, size);
|
||||
break;
|
||||
}
|
||||
@@ -1682,7 +1743,7 @@ static lua_Integer unpackint (lua_State *L, const char *str,
|
||||
else if (size > SZINT) { /* must check unread bytes */
|
||||
int mask = (!issigned || (lua_Integer)res >= 0) ? 0 : MC;
|
||||
for (i = limit; i < size; i++) {
|
||||
if ((unsigned char)str[islittle ? i : size - 1 - i] != mask)
|
||||
if (l_unlikely((unsigned char)str[islittle ? i : size - 1 - i] != mask))
|
||||
luaL_error(L, "%d-byte integer does not fit into Lua Integer", size);
|
||||
}
|
||||
}
|
||||
@@ -1717,13 +1778,21 @@ static int str_unpack (lua_State *L) {
|
||||
break;
|
||||
}
|
||||
case Kfloat: {
|
||||
volatile Ftypes u;
|
||||
lua_Number num;
|
||||
copywithendian(u.buff, 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;
|
||||
lua_pushnumber(L, num);
|
||||
float f;
|
||||
copywithendian((char *)&f, data + pos, sizeof(f), h.islittle);
|
||||
lua_pushnumber(L, (lua_Number)f);
|
||||
break;
|
||||
}
|
||||
case Knumber: {
|
||||
lua_Number f;
|
||||
copywithendian((char *)&f, data + pos, sizeof(f), h.islittle);
|
||||
lua_pushnumber(L, f);
|
||||
break;
|
||||
}
|
||||
case Kdouble: {
|
||||
double f;
|
||||
copywithendian((char *)&f, data + pos, sizeof(f), h.islittle);
|
||||
lua_pushnumber(L, (lua_Number)f);
|
||||
break;
|
||||
}
|
||||
case Kchar: {
|
||||
@@ -1738,7 +1807,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);
|
||||
|
||||
196
ltable.c
196
ltable.c
@@ -68,18 +68,21 @@
|
||||
#define MAXHSIZE luaM_limitN(1u << MAXHBITS, Node)
|
||||
|
||||
|
||||
/*
|
||||
** When the original hash value is good, hashing by a power of 2
|
||||
** avoids the cost of '%'.
|
||||
*/
|
||||
#define hashpow2(t,n) (gnode(t, lmod((n), sizenode(t))))
|
||||
|
||||
/*
|
||||
** for other types, it is better to avoid modulo by power of 2, as
|
||||
** they can have many 2 factors.
|
||||
*/
|
||||
#define hashmod(t,n) (gnode(t, ((n) % ((sizenode(t)-1)|1))))
|
||||
|
||||
|
||||
#define hashstr(t,str) hashpow2(t, (str)->hash)
|
||||
#define hashboolean(t,p) hashpow2(t, p)
|
||||
#define hashint(t,i) hashpow2(t, i)
|
||||
|
||||
|
||||
/*
|
||||
** for some types, it is better to avoid modulus by power of 2, as
|
||||
** they tend to have many 2 factors.
|
||||
*/
|
||||
#define hashmod(t,n) (gnode(t, ((n) % ((sizenode(t)-1)|1))))
|
||||
|
||||
|
||||
#define hashpointer(t,p) hashmod(t, point2uint(p))
|
||||
@@ -96,6 +99,20 @@ static const Node dummynode_ = {
|
||||
static const TValue absentkey = {ABSTKEYCONSTANT};
|
||||
|
||||
|
||||
/*
|
||||
** Hash for integers. To allow a good hash, use the remainder operator
|
||||
** ('%'). If integer fits as a non-negative int, compute an int
|
||||
** remainder, which is faster. Otherwise, use an unsigned-integer
|
||||
** remainder, which uses all bits and ensures a non-negative result.
|
||||
*/
|
||||
static Node *hashint (const Table *t, lua_Integer i) {
|
||||
lua_Unsigned ui = l_castS2U(i);
|
||||
if (ui <= cast_uint(INT_MAX))
|
||||
return hashmod(t, cast_int(ui));
|
||||
else
|
||||
return hashmod(t, ui);
|
||||
}
|
||||
|
||||
|
||||
/*
|
||||
** Hash for floating-point numbers.
|
||||
@@ -129,54 +146,78 @@ static int l_hashfloat (lua_Number n) {
|
||||
|
||||
/*
|
||||
** returns the 'main' position of an element in a table (that is,
|
||||
** the index of its hash value). The key comes broken (tag in 'ktt'
|
||||
** and value in 'vkl') so that we can call it on keys inserted into
|
||||
** nodes.
|
||||
** the index of its hash value).
|
||||
*/
|
||||
static Node *mainposition (const Table *t, int ktt, const Value *kvl) {
|
||||
switch (withvariant(ktt)) {
|
||||
case LUA_VNUMINT:
|
||||
return hashint(t, ivalueraw(*kvl));
|
||||
case LUA_VNUMFLT:
|
||||
return hashmod(t, l_hashfloat(fltvalueraw(*kvl)));
|
||||
case LUA_VSHRSTR:
|
||||
return hashstr(t, tsvalueraw(*kvl));
|
||||
case LUA_VLNGSTR:
|
||||
return hashpow2(t, luaS_hashlongstr(tsvalueraw(*kvl)));
|
||||
static Node *mainpositionTV (const Table *t, const TValue *key) {
|
||||
switch (ttypetag(key)) {
|
||||
case LUA_VNUMINT: {
|
||||
lua_Integer i = ivalue(key);
|
||||
return hashint(t, i);
|
||||
}
|
||||
case LUA_VNUMFLT: {
|
||||
lua_Number n = fltvalue(key);
|
||||
return hashmod(t, l_hashfloat(n));
|
||||
}
|
||||
case LUA_VSHRSTR: {
|
||||
TString *ts = tsvalue(key);
|
||||
return hashstr(t, ts);
|
||||
}
|
||||
case LUA_VLNGSTR: {
|
||||
TString *ts = tsvalue(key);
|
||||
return hashpow2(t, luaS_hashlongstr(ts));
|
||||
}
|
||||
case LUA_VFALSE:
|
||||
return hashboolean(t, 0);
|
||||
case LUA_VTRUE:
|
||||
return hashboolean(t, 1);
|
||||
case LUA_VLIGHTUSERDATA:
|
||||
return hashpointer(t, pvalueraw(*kvl));
|
||||
case LUA_VLCF:
|
||||
return hashpointer(t, fvalueraw(*kvl));
|
||||
default:
|
||||
return hashpointer(t, gcvalueraw(*kvl));
|
||||
case LUA_VLIGHTUSERDATA: {
|
||||
void *p = pvalue(key);
|
||||
return hashpointer(t, p);
|
||||
}
|
||||
case LUA_VLCF: {
|
||||
lua_CFunction f = fvalue(key);
|
||||
return hashpointer(t, f);
|
||||
}
|
||||
default: {
|
||||
GCObject *o = gcvalue(key);
|
||||
return hashpointer(t, o);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
/*
|
||||
** 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));
|
||||
l_sinline Node *mainpositionfromnode (const Table *t, Node *nd) {
|
||||
TValue key;
|
||||
getnodekey(cast(lua_State *, NULL), &key, nd);
|
||||
return mainpositionTV(t, &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)) {
|
||||
switch (keytt(n2)) {
|
||||
case LUA_VNIL: case LUA_VFALSE: case LUA_VTRUE:
|
||||
return 1;
|
||||
case LUA_VNUMINT:
|
||||
@@ -187,7 +228,7 @@ static int equalkey (const TValue *k1, const Node *n2) {
|
||||
return pvalue(k1) == pvalueraw(keyval(n2));
|
||||
case LUA_VLCF:
|
||||
return fvalue(k1) == fvalueraw(keyval(n2));
|
||||
case LUA_VLNGSTR:
|
||||
case ctb(LUA_VLNGSTR):
|
||||
return luaS_eqlngstr(tsvalue(k1), keystrval(n2));
|
||||
default:
|
||||
return gcvalue(k1) == gcvalueraw(keyval(n2));
|
||||
@@ -216,9 +257,11 @@ LUAI_FUNC unsigned int luaH_realasize (const Table *t) {
|
||||
size |= (size >> 2);
|
||||
size |= (size >> 4);
|
||||
size |= (size >> 8);
|
||||
#if (UINT_MAX >> 14) > 3 /* unsigned int has more than 16 bits */
|
||||
size |= (size >> 16);
|
||||
#if (UINT_MAX >> 30) > 3
|
||||
size |= (size >> 32); /* unsigned int has more than 32 bits */
|
||||
#endif
|
||||
#endif
|
||||
size++;
|
||||
lua_assert(ispow2(size) && size/2 < t->alimit && t->alimit < size);
|
||||
@@ -251,11 +294,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);
|
||||
@@ -292,8 +336,8 @@ 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);
|
||||
if (unlikely(isabstkey(n)))
|
||||
const TValue *n = getgeneric(t, key, 1);
|
||||
if (l_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 */
|
||||
/* hash elements are numbered after array ones */
|
||||
@@ -446,7 +490,7 @@ static void setnodevector (lua_State *L, Table *t, unsigned int size) {
|
||||
luaG_runerror(L, "table overflow");
|
||||
size = twoto(lsize);
|
||||
t->node = luaM_newvector(L, size, Node);
|
||||
for (i = 0; i < (int)size; i++) {
|
||||
for (i = 0; i < cast_int(size); i++) {
|
||||
Node *n = gnode(t, i);
|
||||
gnext(n) = 0;
|
||||
setnilkey(n);
|
||||
@@ -471,7 +515,7 @@ static void reinsert (lua_State *L, Table *ot, Table *t) {
|
||||
already present in the table */
|
||||
TValue k;
|
||||
getnodekey(L, &k, old);
|
||||
setobjt2t(L, luaH_set(L, t, &k), gval(old));
|
||||
luaH_set(L, t, &k, gval(old));
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -527,7 +571,7 @@ void luaH_resize (lua_State *L, Table *t, unsigned int newasize,
|
||||
}
|
||||
/* allocate new array */
|
||||
newarray = luaM_reallocvector(L, t->array, oldasize, newasize, TValue);
|
||||
if (unlikely(newarray == NULL && newasize > 0)) { /* allocation failed? */
|
||||
if (l_unlikely(newarray == NULL && newasize > 0)) { /* allocation failed? */
|
||||
freehash(L, &newt); /* release new hash part */
|
||||
luaM_error(L); /* raise error (with array unchanged) */
|
||||
}
|
||||
@@ -618,10 +662,10 @@ static Node *getfreepos (Table *t) {
|
||||
** put new key in its main position; otherwise (colliding node is in its main
|
||||
** position), new key goes to an empty position.
|
||||
*/
|
||||
TValue *luaH_newkey (lua_State *L, Table *t, const TValue *key) {
|
||||
void luaH_newkey (lua_State *L, Table *t, const TValue *key, TValue *value) {
|
||||
Node *mp;
|
||||
TValue aux;
|
||||
if (unlikely(ttisnil(key)))
|
||||
if (l_unlikely(ttisnil(key)))
|
||||
luaG_runerror(L, "table index is nil");
|
||||
else if (ttisfloat(key)) {
|
||||
lua_Number f = fltvalue(key);
|
||||
@@ -630,9 +674,11 @@ TValue *luaH_newkey (lua_State *L, Table *t, const TValue *key) {
|
||||
setivalue(&aux, k);
|
||||
key = &aux; /* insert it as an integer */
|
||||
}
|
||||
else if (unlikely(luai_numisnan(f)))
|
||||
else if (l_unlikely(luai_numisnan(f)))
|
||||
luaG_runerror(L, "table index is NaN");
|
||||
}
|
||||
if (ttisnil(value))
|
||||
return; /* do not insert nil values */
|
||||
mp = mainpositionTV(t, key);
|
||||
if (!isempty(gval(mp)) || isdummy(t)) { /* main position is taken? */
|
||||
Node *othern;
|
||||
@@ -640,10 +686,11 @@ TValue *luaH_newkey (lua_State *L, Table *t, const TValue *key) {
|
||||
if (f == NULL) { /* cannot find a free place? */
|
||||
rehash(L, t, key); /* grow table */
|
||||
/* whatever called 'newkey' takes care of TM cache */
|
||||
return luaH_set(L, t, key); /* insert key into grown table */
|
||||
luaH_set(L, t, key, value); /* insert key into grown table */
|
||||
return;
|
||||
}
|
||||
lua_assert(!isdummy(t));
|
||||
othern = mainposition(t, keytt(mp), &keyval(mp));
|
||||
othern = mainpositionfromnode(t, mp);
|
||||
if (othern != mp) { /* is colliding node out of its main position? */
|
||||
/* yes; move colliding node into free position */
|
||||
while (othern + gnext(othern) != mp) /* find previous */
|
||||
@@ -668,7 +715,7 @@ TValue *luaH_newkey (lua_State *L, Table *t, const TValue *key) {
|
||||
setnodekey(L, mp, key);
|
||||
luaC_barrierback(L, obj2gco(t), key);
|
||||
lua_assert(isempty(gval(mp)));
|
||||
return gval(mp);
|
||||
setobj2t(L, gval(mp), value);
|
||||
}
|
||||
|
||||
|
||||
@@ -730,7 +777,7 @@ const TValue *luaH_getstr (Table *t, TString *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);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -750,34 +797,45 @@ const TValue *luaH_get (Table *t, const TValue *key) {
|
||||
/* else... */
|
||||
} /* FALLTHROUGH */
|
||||
default:
|
||||
return getgeneric(t, key);
|
||||
return getgeneric(t, key, 0);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
/*
|
||||
** Finish a raw "set table" operation, where 'slot' is where the value
|
||||
** should have been (the result of a previous "get table").
|
||||
** Beware: when using this function you probably need to check a GC
|
||||
** barrier and invalidate the TM cache.
|
||||
*/
|
||||
void luaH_finishset (lua_State *L, Table *t, const TValue *key,
|
||||
const TValue *slot, TValue *value) {
|
||||
if (isabstkey(slot))
|
||||
luaH_newkey(L, t, key, value);
|
||||
else
|
||||
setobj2t(L, cast(TValue *, slot), value);
|
||||
}
|
||||
|
||||
|
||||
/*
|
||||
** beware: when using this function you probably need to check a GC
|
||||
** barrier and invalidate the TM cache.
|
||||
*/
|
||||
TValue *luaH_set (lua_State *L, Table *t, const TValue *key) {
|
||||
const TValue *p = luaH_get(t, key);
|
||||
if (!isabstkey(p))
|
||||
return cast(TValue *, p);
|
||||
else return luaH_newkey(L, t, key);
|
||||
void luaH_set (lua_State *L, Table *t, const TValue *key, TValue *value) {
|
||||
const TValue *slot = luaH_get(t, key);
|
||||
luaH_finishset(L, t, key, slot, value);
|
||||
}
|
||||
|
||||
|
||||
void luaH_setint (lua_State *L, Table *t, lua_Integer key, TValue *value) {
|
||||
const TValue *p = luaH_getint(t, key);
|
||||
TValue *cell;
|
||||
if (!isabstkey(p))
|
||||
cell = cast(TValue *, p);
|
||||
else {
|
||||
if (isabstkey(p)) {
|
||||
TValue k;
|
||||
setivalue(&k, key);
|
||||
cell = luaH_newkey(L, t, &k);
|
||||
luaH_newkey(L, t, &k, value);
|
||||
}
|
||||
setobj2t(L, cell, value);
|
||||
else
|
||||
setobj2t(L, cast(TValue *, p), value);
|
||||
}
|
||||
|
||||
|
||||
@@ -919,6 +977,4 @@ Node *luaH_mainposition (const Table *t, const TValue *key) {
|
||||
return mainpositionTV(t, key);
|
||||
}
|
||||
|
||||
int luaH_isdummy (const Table *t) { return isdummy(t); }
|
||||
|
||||
#endif
|
||||
|
||||
9
ltable.h
9
ltable.h
@@ -41,8 +41,12 @@ LUAI_FUNC void luaH_setint (lua_State *L, Table *t, lua_Integer key,
|
||||
LUAI_FUNC const TValue *luaH_getshortstr (Table *t, TString *key);
|
||||
LUAI_FUNC const TValue *luaH_getstr (Table *t, TString *key);
|
||||
LUAI_FUNC const TValue *luaH_get (Table *t, const TValue *key);
|
||||
LUAI_FUNC TValue *luaH_newkey (lua_State *L, Table *t, const TValue *key);
|
||||
LUAI_FUNC TValue *luaH_set (lua_State *L, Table *t, const TValue *key);
|
||||
LUAI_FUNC void luaH_newkey (lua_State *L, Table *t, const TValue *key,
|
||||
TValue *value);
|
||||
LUAI_FUNC void luaH_set (lua_State *L, Table *t, const TValue *key,
|
||||
TValue *value);
|
||||
LUAI_FUNC void luaH_finishset (lua_State *L, Table *t, const TValue *key,
|
||||
const TValue *slot, TValue *value);
|
||||
LUAI_FUNC Table *luaH_new (lua_State *L);
|
||||
LUAI_FUNC void luaH_resize (lua_State *L, Table *t, unsigned int nasize,
|
||||
unsigned int nhsize);
|
||||
@@ -55,7 +59,6 @@ LUAI_FUNC unsigned int luaH_realasize (const Table *t);
|
||||
|
||||
#if defined(LUA_DEBUG)
|
||||
LUAI_FUNC Node *luaH_mainposition (const Table *t, const TValue *key);
|
||||
LUAI_FUNC int luaH_isdummy (const Table *t);
|
||||
#endif
|
||||
|
||||
|
||||
|
||||
18
ltablib.c
18
ltablib.c
@@ -59,8 +59,9 @@ static void checktab (lua_State *L, int arg, int what) {
|
||||
|
||||
|
||||
static int tinsert (lua_State *L) {
|
||||
lua_Integer e = aux_getn(L, 1, TAB_RW) + 1; /* first empty element */
|
||||
lua_Integer pos; /* where to insert new element */
|
||||
lua_Integer e = aux_getn(L, 1, TAB_RW);
|
||||
e = luaL_intop(+, e, 1); /* first empty element */
|
||||
switch (lua_gettop(L)) {
|
||||
case 2: { /* called with only 2 arguments */
|
||||
pos = e; /* insert new element at the end */
|
||||
@@ -92,7 +93,7 @@ static int tremove (lua_State *L) {
|
||||
lua_Integer pos = luaL_optinteger(L, 2, size);
|
||||
if (pos != size) /* validate 'pos' if given */
|
||||
/* check whether 'pos' is in [1, size + 1] */
|
||||
luaL_argcheck(L, (lua_Unsigned)pos - 1u <= (lua_Unsigned)size, 1,
|
||||
luaL_argcheck(L, (lua_Unsigned)pos - 1u <= (lua_Unsigned)size, 2,
|
||||
"position out of bounds");
|
||||
lua_geti(L, 1, pos); /* result = t[pos] */
|
||||
for ( ; pos < size; pos++) {
|
||||
@@ -145,9 +146,9 @@ static int tmove (lua_State *L) {
|
||||
|
||||
static void addfield (lua_State *L, luaL_Buffer *b, lua_Integer i) {
|
||||
lua_geti(L, 1, i);
|
||||
if (!lua_isstring(L, -1))
|
||||
luaL_error(L, "invalid value (%s) at index %d in table for 'concat'",
|
||||
luaL_typename(L, -1), i);
|
||||
if (l_unlikely(!lua_isstring(L, -1)))
|
||||
luaL_error(L, "invalid value (%s) at index %I in table for 'concat'",
|
||||
luaL_typename(L, -1), (LUAI_UACINT)i);
|
||||
luaL_addvalue(b);
|
||||
}
|
||||
|
||||
@@ -196,7 +197,8 @@ static int tunpack (lua_State *L) {
|
||||
lua_Integer e = luaL_opt(L, luaL_checkinteger, 3, luaL_len(L, 1));
|
||||
if (i > e) return 0; /* empty range */
|
||||
n = (lua_Unsigned)e - i; /* number of elements minus 1 (avoid overflows) */
|
||||
if (n >= (unsigned int)INT_MAX || !lua_checkstack(L, (int)(++n)))
|
||||
if (l_unlikely(n >= (unsigned int)INT_MAX ||
|
||||
!lua_checkstack(L, (int)(++n))))
|
||||
return luaL_error(L, "too many results to unpack");
|
||||
for (; i < e; i++) { /* push arg[i..e - 1] (to avoid overflows) */
|
||||
lua_geti(L, 1, i);
|
||||
@@ -300,14 +302,14 @@ static IdxT partition (lua_State *L, IdxT lo, IdxT up) {
|
||||
for (;;) {
|
||||
/* next loop: repeat ++i while a[i] < P */
|
||||
while ((void)lua_geti(L, 1, ++i), sort_comp(L, -1, -2)) {
|
||||
if (i == up - 1) /* a[i] < P but a[up - 1] == P ?? */
|
||||
if (l_unlikely(i == up - 1)) /* a[i] < P but a[up - 1] == P ?? */
|
||||
luaL_error(L, "invalid order function for sorting");
|
||||
lua_pop(L, 1); /* remove a[i] */
|
||||
}
|
||||
/* after the loop, a[i] >= P and a[lo .. i - 1] < P */
|
||||
/* next loop: repeat --j while P < a[j] */
|
||||
while ((void)lua_geti(L, 1, --j), sort_comp(L, -3, -1)) {
|
||||
if (j < i) /* j < i but a[j] > P ?? */
|
||||
if (l_unlikely(j < i)) /* j < i but a[j] > P ?? */
|
||||
luaL_error(L, "invalid order function for sorting");
|
||||
lua_pop(L, 1); /* remove a[j] */
|
||||
}
|
||||
|
||||
162
ltests.c
162
ltests.c
@@ -44,7 +44,7 @@
|
||||
void *l_Trick = 0;
|
||||
|
||||
|
||||
#define obj_at(L,k) s2v(L->ci->func + (k))
|
||||
#define obj_at(L,k) s2v(L->ci->func.p + (k))
|
||||
|
||||
|
||||
static int runC (lua_State *L, lua_State *L1, const char *pc);
|
||||
@@ -57,7 +57,7 @@ static void setnameval (lua_State *L, const char *name, int val) {
|
||||
|
||||
|
||||
static void pushobject (lua_State *L, const TValue *o) {
|
||||
setobj2s(L, L->top, o);
|
||||
setobj2s(L, L->top.p, o);
|
||||
api_incr_top(L);
|
||||
}
|
||||
|
||||
@@ -121,6 +121,7 @@ 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);
|
||||
luaL_checkstack(L, 1, "warn stack space");
|
||||
lua_getglobal(L, "_WARN");
|
||||
if (!lua_toboolean(L, -1))
|
||||
lua_pop(L, 1); /* ok, no previous unexpected warning */
|
||||
@@ -142,6 +143,7 @@ static void warnf (void *ud, const char *msg, int tocont) {
|
||||
}
|
||||
case 2: { /* store */
|
||||
lua_unlock(L);
|
||||
luaL_checkstack(L, 1, "warn stack space");
|
||||
lua_pushstring(L, buff);
|
||||
lua_setglobal(L, "_WARN"); /* assign message to global '_WARN' */
|
||||
lua_lock(L);
|
||||
@@ -272,11 +274,15 @@ void *debug_realloc (void *ud, void *b, size_t oldsize, size_t size) {
|
||||
|
||||
|
||||
/*
|
||||
** {======================================================
|
||||
** Functions to check memory consistency
|
||||
** =======================================================
|
||||
** {=====================================================================
|
||||
** Functions to check memory consistency.
|
||||
** Most of these checks are done through asserts, so this code does
|
||||
** not make sense with asserts off. For this reason, it uses 'assert'
|
||||
** directly, instead of 'lua_assert'.
|
||||
** ======================================================================
|
||||
*/
|
||||
|
||||
#include <assert.h>
|
||||
|
||||
/*
|
||||
** Check GC invariants. For incremental mode, a black object cannot
|
||||
@@ -330,13 +336,23 @@ static int testobjref (global_State *g, GCObject *f, GCObject *t) {
|
||||
return r1;
|
||||
}
|
||||
|
||||
#define checkobjref(g,f,t) \
|
||||
{ if (t) lua_longassert(testobjref(g,f,obj2gco(t))); }
|
||||
|
||||
static void checkobjref (global_State *g, GCObject *f, GCObject *t) {
|
||||
assert(testobjref(g, f, t));
|
||||
}
|
||||
|
||||
|
||||
/*
|
||||
** Version where 't' can be NULL. In that case, it should not apply the
|
||||
** macro 'obj2gco' over the object. ('t' may have several types, so this
|
||||
** definition must be a macro.) Most checks need this version, because
|
||||
** the check may run while an object is still being created.
|
||||
*/
|
||||
#define checkobjrefN(g,f,t) { if (t) checkobjref(g,f,obj2gco(t)); }
|
||||
|
||||
|
||||
static void checkvalref (global_State *g, GCObject *f, const TValue *t) {
|
||||
lua_assert(!iscollectable(t) ||
|
||||
(righttt(t) && testobjref(g, f, gcvalue(t))));
|
||||
assert(!iscollectable(t) || (righttt(t) && testobjref(g, f, gcvalue(t))));
|
||||
}
|
||||
|
||||
|
||||
@@ -345,14 +361,14 @@ static void checktable (global_State *g, Table *h) {
|
||||
unsigned int asize = luaH_realasize(h);
|
||||
Node *n, *limit = gnode(h, sizenode(h));
|
||||
GCObject *hgc = obj2gco(h);
|
||||
checkobjref(g, hgc, h->metatable);
|
||||
checkobjrefN(g, hgc, h->metatable);
|
||||
for (i = 0; i < asize; i++)
|
||||
checkvalref(g, hgc, &h->array[i]);
|
||||
for (n = gnode(h, 0); n < limit; n++) {
|
||||
if (!isempty(gval(n))) {
|
||||
TValue k;
|
||||
getnodekey(g->mainthread, &k, n);
|
||||
lua_assert(!keyisnil(n));
|
||||
assert(!keyisnil(n));
|
||||
checkvalref(g, hgc, &k);
|
||||
checkvalref(g, hgc, gval(n));
|
||||
}
|
||||
@@ -363,30 +379,26 @@ static void checktable (global_State *g, Table *h) {
|
||||
static void checkudata (global_State *g, Udata *u) {
|
||||
int i;
|
||||
GCObject *hgc = obj2gco(u);
|
||||
checkobjref(g, hgc, u->metatable);
|
||||
checkobjrefN(g, hgc, u->metatable);
|
||||
for (i = 0; i < u->nuvalue; i++)
|
||||
checkvalref(g, hgc, &u->uv[i].uv);
|
||||
}
|
||||
|
||||
|
||||
/*
|
||||
** All marks are conditional because a GC may happen while the
|
||||
** prototype is still being created
|
||||
*/
|
||||
static void checkproto (global_State *g, Proto *f) {
|
||||
int i;
|
||||
GCObject *fgc = obj2gco(f);
|
||||
checkobjref(g, fgc, f->source);
|
||||
checkobjrefN(g, fgc, f->source);
|
||||
for (i=0; i<f->sizek; i++) {
|
||||
if (ttisstring(f->k + i))
|
||||
checkobjref(g, fgc, tsvalue(f->k + i));
|
||||
if (iscollectable(f->k + i))
|
||||
checkobjref(g, fgc, gcvalue(f->k + i));
|
||||
}
|
||||
for (i=0; i<f->sizeupvalues; i++)
|
||||
checkobjref(g, fgc, f->upvalues[i].name);
|
||||
checkobjrefN(g, fgc, f->upvalues[i].name);
|
||||
for (i=0; i<f->sizep; i++)
|
||||
checkobjref(g, fgc, f->p[i]);
|
||||
checkobjrefN(g, fgc, f->p[i]);
|
||||
for (i=0; i<f->sizelocvars; i++)
|
||||
checkobjref(g, fgc, f->locvars[i].varname);
|
||||
checkobjrefN(g, fgc, f->locvars[i].varname);
|
||||
}
|
||||
|
||||
|
||||
@@ -401,13 +413,13 @@ static void checkCclosure (global_State *g, CClosure *cl) {
|
||||
static void checkLclosure (global_State *g, LClosure *cl) {
|
||||
GCObject *clgc = obj2gco(cl);
|
||||
int i;
|
||||
checkobjref(g, clgc, cl->p);
|
||||
checkobjrefN(g, clgc, cl->p);
|
||||
for (i=0; i<cl->nupvalues; i++) {
|
||||
UpVal *uv = cl->upvals[i];
|
||||
if (uv) {
|
||||
checkobjref(g, clgc, uv);
|
||||
checkobjrefN(g, clgc, uv);
|
||||
if (!upisopen(uv))
|
||||
checkvalref(g, obj2gco(uv), uv->v);
|
||||
checkvalref(g, obj2gco(uv), uv->v.p);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -416,7 +428,7 @@ static void checkLclosure (global_State *g, LClosure *cl) {
|
||||
static int lua_checkpc (CallInfo *ci) {
|
||||
if (!isLua(ci)) return 1;
|
||||
else {
|
||||
StkId f = ci->func;
|
||||
StkId f = ci->func.p;
|
||||
Proto *p = clLvalue(s2v(f))->p;
|
||||
return p->code <= ci->u.l.savedpc &&
|
||||
ci->u.l.savedpc <= p->code + p->sizecode;
|
||||
@@ -428,19 +440,20 @@ static void checkstack (global_State *g, lua_State *L1) {
|
||||
StkId o;
|
||||
CallInfo *ci;
|
||||
UpVal *uv;
|
||||
lua_assert(!isdead(g, L1));
|
||||
if (L1->stack == NULL) { /* incomplete thread? */
|
||||
lua_assert(L1->stacksize == 0 && L1->openupval == NULL &&
|
||||
L1->ci == NULL);
|
||||
assert(!isdead(g, L1));
|
||||
if (L1->stack.p == NULL) { /* incomplete thread? */
|
||||
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 */
|
||||
assert(upisopen(uv)); /* must be open */
|
||||
assert(L1->top.p <= L1->stack_last.p);
|
||||
assert(L1->tbclist.p <= L1->top.p);
|
||||
for (ci = L1->ci; ci != NULL; ci = ci->previous) {
|
||||
lua_assert(ci->top <= L1->stack_last);
|
||||
lua_assert(lua_checkpc(ci));
|
||||
assert(ci->top.p <= L1->stack_last.p);
|
||||
assert(lua_checkpc(ci));
|
||||
}
|
||||
for (o = L1->stack; o < L1->stack_last + EXTRA_STACK; o++)
|
||||
for (o = L1->stack.p; o < L1->stack_last.p; o++)
|
||||
checkliveness(L1, s2v(o)); /* entire stack must have valid values */
|
||||
}
|
||||
|
||||
@@ -452,7 +465,7 @@ static void checkrefs (global_State *g, GCObject *o) {
|
||||
break;
|
||||
}
|
||||
case LUA_VUPVAL: {
|
||||
checkvalref(g, o, gco2upv(o)->v);
|
||||
checkvalref(g, o, gco2upv(o)->v.p);
|
||||
break;
|
||||
}
|
||||
case LUA_VTABLE: {
|
||||
@@ -477,10 +490,10 @@ static void checkrefs (global_State *g, GCObject *o) {
|
||||
}
|
||||
case LUA_VSHRSTR:
|
||||
case LUA_VLNGSTR: {
|
||||
lua_assert(!isgray(o)); /* strings are never gray */
|
||||
assert(!isgray(o)); /* strings are never gray */
|
||||
break;
|
||||
}
|
||||
default: lua_assert(0);
|
||||
default: assert(0);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -499,14 +512,14 @@ static void checkrefs (global_State *g, GCObject *o) {
|
||||
static void checkobject (global_State *g, GCObject *o, int maybedead,
|
||||
int listage) {
|
||||
if (isdead(g, o))
|
||||
lua_assert(maybedead);
|
||||
assert(maybedead);
|
||||
else {
|
||||
lua_assert(g->gcstate != GCSpause || iswhite(o));
|
||||
assert(g->gcstate != GCSpause || iswhite(o));
|
||||
if (g->gckind == KGC_GEN) { /* generational mode? */
|
||||
lua_assert(getage(o) >= listage);
|
||||
lua_assert(!iswhite(o) || !isold(o));
|
||||
assert(getage(o) >= listage);
|
||||
assert(!iswhite(o) || !isold(o));
|
||||
if (isold(o)) {
|
||||
lua_assert(isblack(o) ||
|
||||
assert(isblack(o) ||
|
||||
getage(o) == G_TOUCHED1 ||
|
||||
getage(o) == G_OLD0 ||
|
||||
o->tt == LUA_VTHREAD ||
|
||||
@@ -520,11 +533,10 @@ static void checkobject (global_State *g, GCObject *o, int maybedead,
|
||||
|
||||
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 */
|
||||
cast_void(g); /* better to keep it 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));
|
||||
assert(!!isgray(o) ^ (getage(o) == G_TOUCHED2));
|
||||
assert(!testbit(o->marked, TESTBIT));
|
||||
if (keepinvariant(g))
|
||||
l_setbit(o->marked, TESTBIT); /* mark that object is in a gray list */
|
||||
total++;
|
||||
@@ -535,10 +547,10 @@ static lu_mem checkgraylist (global_State *g, GCObject *o) {
|
||||
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);
|
||||
assert(gco2u(o)->nuvalue > 0);
|
||||
o = gco2u(o)->gclist;
|
||||
break;
|
||||
default: lua_assert(0); /* other objects cannot be in a gray list */
|
||||
default: assert(0); /* other objects cannot be in a gray list */
|
||||
}
|
||||
}
|
||||
return total;
|
||||
@@ -570,13 +582,13 @@ static void incifingray (global_State *g, GCObject *o, lu_mem *count) {
|
||||
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)));
|
||||
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));
|
||||
assert(testbit(o->marked, TESTBIT));
|
||||
resetbit(o->marked, TESTBIT); /* prepare for next cycle */
|
||||
}
|
||||
}
|
||||
@@ -589,22 +601,22 @@ static lu_mem checklist (global_State *g, int maybedead, int tof,
|
||||
for (o = newl; o != survival; o = o->next) {
|
||||
checkobject(g, o, maybedead, G_NEW);
|
||||
incifingray(g, o, &total);
|
||||
lua_assert(!tof == !tofinalize(o));
|
||||
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));
|
||||
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));
|
||||
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));
|
||||
assert(!tof == !tofinalize(o));
|
||||
}
|
||||
return total;
|
||||
}
|
||||
@@ -617,16 +629,16 @@ int lua_checkmemory (lua_State *L) {
|
||||
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)));
|
||||
assert(!iswhite(g->mainthread));
|
||||
assert(!iswhite(gcvalue(&g->l_registry)));
|
||||
}
|
||||
lua_assert(!isdead(g, gcvalue(&g->l_registry)));
|
||||
lua_assert(g->sweepgc == NULL || issweepphase(g));
|
||||
assert(!isdead(g, gcvalue(&g->l_registry)));
|
||||
assert(g->sweepgc == NULL || issweepphase(g));
|
||||
totalin = checkgrays(g);
|
||||
|
||||
/* check 'fixedgc' list */
|
||||
for (o = g->fixedgc; o != NULL; o = o->next) {
|
||||
lua_assert(o->tt == LUA_VSHRSTR && isgray(o) && getage(o) == G_OLD);
|
||||
assert(o->tt == LUA_VSHRSTR && isgray(o) && getage(o) == G_OLD);
|
||||
}
|
||||
|
||||
/* check 'allgc' list */
|
||||
@@ -642,11 +654,11 @@ int lua_checkmemory (lua_State *L) {
|
||||
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_VUSERDATA || o->tt == LUA_VTABLE);
|
||||
assert(tofinalize(o));
|
||||
assert(o->tt == LUA_VUSERDATA || o->tt == LUA_VTABLE);
|
||||
}
|
||||
if (keepinvariant(g))
|
||||
lua_assert(totalin == totalshould);
|
||||
assert(totalin == totalshould);
|
||||
return 0;
|
||||
}
|
||||
|
||||
@@ -864,7 +876,7 @@ static int alloc_failnext (lua_State *L) {
|
||||
l_memcontrol.failnext = 1;
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
||||
|
||||
static int settrick (lua_State *L) {
|
||||
if (ttisnil(obj_at(L, 1)))
|
||||
@@ -968,8 +980,8 @@ 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, (L->top.p - L->stack.p));
|
||||
lua_pushinteger(L, stacksize(L));
|
||||
lua_pushinteger(L, L->nCcalls);
|
||||
lua_pushinteger(L, L->nci);
|
||||
lua_pushinteger(L, (unsigned long)&a);
|
||||
@@ -1028,7 +1040,7 @@ static int string_query (lua_State *L) {
|
||||
TString *ts;
|
||||
int n = 0;
|
||||
for (ts = tb->hash[s]; ts != NULL; ts = ts->u.hnext) {
|
||||
setsvalue2s(L, L->top, ts);
|
||||
setsvalue2s(L, L->top.p, ts);
|
||||
api_incr_top(L);
|
||||
n++;
|
||||
}
|
||||
@@ -1043,6 +1055,7 @@ static int tref (lua_State *L) {
|
||||
luaL_checkany(L, 1);
|
||||
lua_pushvalue(L, 1);
|
||||
lua_pushinteger(L, luaL_ref(L, LUA_REGISTRYINDEX));
|
||||
cast_void(level); /* to avoid warnings */
|
||||
lua_assert(lua_gettop(L) == level+1); /* +1 for result */
|
||||
return 1;
|
||||
}
|
||||
@@ -1050,6 +1063,7 @@ static int tref (lua_State *L) {
|
||||
static int getref (lua_State *L) {
|
||||
int level = lua_gettop(L);
|
||||
lua_rawgeti(L, LUA_REGISTRYINDEX, luaL_checkinteger(L, 1));
|
||||
cast_void(level); /* to avoid warnings */
|
||||
lua_assert(lua_gettop(L) == level+1);
|
||||
return 1;
|
||||
}
|
||||
@@ -1057,6 +1071,7 @@ static int getref (lua_State *L) {
|
||||
static int unref (lua_State *L) {
|
||||
int level = lua_gettop(L);
|
||||
luaL_unref(L, LUA_REGISTRYINDEX, cast_int(luaL_checkinteger(L, 1)));
|
||||
cast_void(level); /* to avoid warnings */
|
||||
lua_assert(lua_gettop(L) == level);
|
||||
return 0;
|
||||
}
|
||||
@@ -1518,7 +1533,7 @@ static int runC (lua_State *L, lua_State *L1, const char *pc) {
|
||||
lua_newthread(L1);
|
||||
}
|
||||
else if EQ("resetthread") {
|
||||
lua_pushinteger(L1, lua_resetthread(L1));
|
||||
lua_pushinteger(L1, lua_resetthread(L1)); /* deprecated */
|
||||
}
|
||||
else if EQ("newuserdata") {
|
||||
lua_newuserdata(L1, getnum);
|
||||
@@ -1725,8 +1740,12 @@ static struct X { int x; } x;
|
||||
else if EQ("tostring") {
|
||||
const char *s = lua_tostring(L1, getindex);
|
||||
const char *s1 = lua_pushstring(L1, s);
|
||||
cast_void(s1); /* to avoid warnings */
|
||||
lua_longassert((s == NULL && s1 == NULL) || strcmp(s, s1) == 0);
|
||||
}
|
||||
else if EQ("Ltolstring") {
|
||||
luaL_tolstring(L1, getindex, NULL);
|
||||
}
|
||||
else if EQ("type") {
|
||||
lua_pushstring(L1, luaL_typename(L1, getnum));
|
||||
}
|
||||
@@ -1753,6 +1772,9 @@ static struct X { int x; } x;
|
||||
else if EQ("toclose") {
|
||||
lua_toclose(L1, getnum);
|
||||
}
|
||||
else if EQ("closeslot") {
|
||||
lua_closeslot(L1, getnum);
|
||||
}
|
||||
else luaL_error(L, "unknown instruction %s", buff);
|
||||
}
|
||||
return 0;
|
||||
@@ -1938,15 +1960,15 @@ static void checkfinalmem (void) {
|
||||
|
||||
int luaB_opentests (lua_State *L) {
|
||||
void *ud;
|
||||
lua_Alloc f = lua_getallocf(L, &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));
|
||||
lua_setallocf(L, lua_getallocf(L, NULL), ud);
|
||||
lua_assert(f == debug_realloc && ud == cast_voidp(&l_memcontrol));
|
||||
lua_setallocf(L, f, ud); /* exercise this function */
|
||||
luaL_newlib(L, tests_funcs);
|
||||
return 1;
|
||||
}
|
||||
|
||||
17
ltests.h
17
ltests.h
@@ -23,11 +23,6 @@
|
||||
#define LUAI_ASSERT
|
||||
|
||||
|
||||
|
||||
/* compiled with -O0, Lua uses a lot of C stack space... */
|
||||
#undef LUAI_MAXCSTACK
|
||||
#define LUAI_MAXCSTACK 400
|
||||
|
||||
/* to avoid warnings, and to make sure value is really unused */
|
||||
#define UNUSED(x) (x=0, (void)(x))
|
||||
|
||||
@@ -130,11 +125,23 @@ LUA_API void *debug_realloc (void *ud, void *block,
|
||||
#define LUAI_USER_ALIGNMENT_T union { char b[sizeof(void*) * 8]; }
|
||||
|
||||
|
||||
/*
|
||||
** This one is not compatible with tests for opcode optimizations,
|
||||
** as it blocks some optimizations
|
||||
#define MAXINDEXRK 0
|
||||
*/
|
||||
|
||||
|
||||
/* make stack-overflow tests run faster */
|
||||
#undef LUAI_MAXSTACK
|
||||
#define LUAI_MAXSTACK 50000
|
||||
|
||||
|
||||
/* test mode uses more stack space */
|
||||
#undef LUAI_MAXCCALLS
|
||||
#define LUAI_MAXCCALLS 180
|
||||
|
||||
|
||||
/* force Lua to use its own implementations */
|
||||
#undef lua_strx2number
|
||||
#undef lua_number2strx
|
||||
|
||||
43
ltm.c
43
ltm.c
@@ -102,12 +102,12 @@ const char *luaT_objtypename (lua_State *L, const TValue *o) {
|
||||
|
||||
void luaT_callTM (lua_State *L, const TValue *f, const TValue *p1,
|
||||
const TValue *p2, const TValue *p3) {
|
||||
StkId func = L->top;
|
||||
StkId func = L->top.p;
|
||||
setobj2s(L, func, f); /* push function (assume EXTRA_STACK) */
|
||||
setobj2s(L, func + 1, p1); /* 1st argument */
|
||||
setobj2s(L, func + 2, p2); /* 2nd argument */
|
||||
setobj2s(L, func + 3, p3); /* 3rd argument */
|
||||
L->top = func + 4;
|
||||
L->top.p = func + 4;
|
||||
/* metamethod may yield only when called from Lua code */
|
||||
if (isLuacode(L->ci))
|
||||
luaD_call(L, func, 0);
|
||||
@@ -119,18 +119,18 @@ void luaT_callTM (lua_State *L, const TValue *f, const TValue *p1,
|
||||
void luaT_callTMres (lua_State *L, const TValue *f, const TValue *p1,
|
||||
const TValue *p2, StkId res) {
|
||||
ptrdiff_t result = savestack(L, res);
|
||||
StkId func = L->top;
|
||||
StkId func = L->top.p;
|
||||
setobj2s(L, func, f); /* push function (assume EXTRA_STACK) */
|
||||
setobj2s(L, func + 1, p1); /* 1st argument */
|
||||
setobj2s(L, func + 2, p2); /* 2nd argument */
|
||||
L->top += 3;
|
||||
L->top.p += 3;
|
||||
/* metamethod may yield only when called from Lua code */
|
||||
if (isLuacode(L->ci))
|
||||
luaD_call(L, func, 1);
|
||||
else
|
||||
luaD_callnoyield(L, func, 1);
|
||||
res = restorestack(L, result);
|
||||
setobjs2s(L, res, --L->top); /* move result to its place */
|
||||
setobjs2s(L, res, --L->top.p); /* move result to its place */
|
||||
}
|
||||
|
||||
|
||||
@@ -147,7 +147,7 @@ static int callbinTM (lua_State *L, const TValue *p1, const TValue *p2,
|
||||
|
||||
void luaT_trybinTM (lua_State *L, const TValue *p1, const TValue *p2,
|
||||
StkId res, TMS event) {
|
||||
if (!callbinTM(L, p1, p2, res, event)) {
|
||||
if (l_unlikely(!callbinTM(L, p1, p2, res, event))) {
|
||||
switch (event) {
|
||||
case TM_BAND: case TM_BOR: case TM_BXOR:
|
||||
case TM_SHL: case TM_SHR: case TM_BNOT: {
|
||||
@@ -165,8 +165,9 @@ void luaT_trybinTM (lua_State *L, const TValue *p1, const TValue *p2,
|
||||
|
||||
|
||||
void luaT_tryconcatTM (lua_State *L) {
|
||||
StkId top = L->top;
|
||||
if (!callbinTM(L, s2v(top - 2), s2v(top - 1), top - 2, TM_CONCAT))
|
||||
StkId top = L->top.p;
|
||||
if (l_unlikely(!callbinTM(L, s2v(top - 2), s2v(top - 1), top - 2,
|
||||
TM_CONCAT)))
|
||||
luaG_concaterror(L, s2v(top - 2), s2v(top - 1));
|
||||
}
|
||||
|
||||
@@ -199,15 +200,15 @@ void luaT_trybiniTM (lua_State *L, const TValue *p1, lua_Integer i2,
|
||||
*/
|
||||
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 */
|
||||
return !l_isfalse(s2v(L->top));
|
||||
if (callbinTM(L, p1, p2, L->top.p, event)) /* try original event */
|
||||
return !l_isfalse(s2v(L->top.p));
|
||||
#if defined(LUA_COMPAT_LT_LE)
|
||||
else if (event == TM_LE) {
|
||||
/* try '!(p2 < p1)' for '(p1 <= p2)' */
|
||||
L->ci->callstatus |= CIST_LEQ; /* mark it is doing 'lt' for 'le' */
|
||||
if (callbinTM(L, p2, p1, L->top, TM_LT)) {
|
||||
if (callbinTM(L, p2, p1, L->top.p, TM_LT)) {
|
||||
L->ci->callstatus ^= CIST_LEQ; /* clear mark */
|
||||
return l_isfalse(s2v(L->top));
|
||||
return l_isfalse(s2v(L->top.p));
|
||||
}
|
||||
/* else error will remove this 'ci'; no need to clear mark */
|
||||
}
|
||||
@@ -237,20 +238,20 @@ int luaT_callorderiTM (lua_State *L, const TValue *p1, int v2,
|
||||
void luaT_adjustvarargs (lua_State *L, int nfixparams, CallInfo *ci,
|
||||
const Proto *p) {
|
||||
int i;
|
||||
int actual = cast_int(L->top - ci->func) - 1; /* number of arguments */
|
||||
int actual = cast_int(L->top.p - ci->func.p) - 1; /* number of arguments */
|
||||
int nextra = actual - nfixparams; /* number of extra arguments */
|
||||
ci->u.l.nextraargs = nextra;
|
||||
luaD_checkstack(L, p->maxstacksize + 1);
|
||||
/* copy function to the top of the stack */
|
||||
setobjs2s(L, L->top++, ci->func);
|
||||
setobjs2s(L, L->top.p++, ci->func.p);
|
||||
/* move fixed parameters to the top of the stack */
|
||||
for (i = 1; i <= nfixparams; i++) {
|
||||
setobjs2s(L, L->top++, ci->func + i);
|
||||
setnilvalue(s2v(ci->func + i)); /* erase original parameter (for GC) */
|
||||
setobjs2s(L, L->top.p++, ci->func.p + i);
|
||||
setnilvalue(s2v(ci->func.p + i)); /* erase original parameter (for GC) */
|
||||
}
|
||||
ci->func += actual + 1;
|
||||
ci->top += actual + 1;
|
||||
lua_assert(L->top <= ci->top && ci->top <= L->stack_last);
|
||||
ci->func.p += actual + 1;
|
||||
ci->top.p += actual + 1;
|
||||
lua_assert(L->top.p <= ci->top.p && ci->top.p <= L->stack_last.p);
|
||||
}
|
||||
|
||||
|
||||
@@ -260,10 +261,10 @@ void luaT_getvarargs (lua_State *L, CallInfo *ci, StkId where, int wanted) {
|
||||
if (wanted < 0) {
|
||||
wanted = nextra; /* get all extra arguments available */
|
||||
checkstackGCp(L, nextra, where); /* ensure stack space */
|
||||
L->top = where + nextra; /* next instruction will need top */
|
||||
L->top.p = where + nextra; /* next instruction will need top */
|
||||
}
|
||||
for (i = 0; i < wanted && i < nextra; i++)
|
||||
setobjs2s(L, where + i, ci->func - nextra + i);
|
||||
setobjs2s(L, where + i, ci->func.p - nextra + i);
|
||||
for (; i < wanted; i++) /* complete required results with nil */
|
||||
setnilvalue(s2v(where + i));
|
||||
}
|
||||
|
||||
5
ltm.h
5
ltm.h
@@ -9,6 +9,7 @@
|
||||
|
||||
|
||||
#include "lobject.h"
|
||||
#include "lstate.h"
|
||||
|
||||
|
||||
/*
|
||||
@@ -95,8 +96,8 @@ LUAI_FUNC int luaT_callorderiTM (lua_State *L, const TValue *p1, int v2,
|
||||
int inv, int isfloat, TMS event);
|
||||
|
||||
LUAI_FUNC void luaT_adjustvarargs (lua_State *L, int nfixparams,
|
||||
struct CallInfo *ci, const Proto *p);
|
||||
LUAI_FUNC void luaT_getvarargs (lua_State *L, struct CallInfo *ci,
|
||||
CallInfo *ci, const Proto *p);
|
||||
LUAI_FUNC void luaT_getvarargs (lua_State *L, CallInfo *ci,
|
||||
StkId where, int wanted);
|
||||
|
||||
|
||||
|
||||
118
lua.c
118
lua.c
@@ -37,6 +37,26 @@ static lua_State *globalL = NULL;
|
||||
static const char *progname = LUA_PROGNAME;
|
||||
|
||||
|
||||
#if defined(LUA_USE_POSIX) /* { */
|
||||
|
||||
/*
|
||||
** Use 'sigaction' when available.
|
||||
*/
|
||||
static void setsignal (int sig, void (*handler)(int)) {
|
||||
struct sigaction sa;
|
||||
sa.sa_handler = handler;
|
||||
sa.sa_flags = 0;
|
||||
sigemptyset(&sa.sa_mask); /* do not mask any signal */
|
||||
sigaction(sig, &sa, NULL);
|
||||
}
|
||||
|
||||
#else /* }{ */
|
||||
|
||||
#define setsignal signal
|
||||
|
||||
#endif /* } */
|
||||
|
||||
|
||||
/*
|
||||
** Hook set by signal function to stop the interpreter.
|
||||
*/
|
||||
@@ -55,7 +75,7 @@ static void lstop (lua_State *L, lua_Debug *ar) {
|
||||
*/
|
||||
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 */
|
||||
setsignal(i, SIG_DFL); /* if another SIGINT happens, terminate process */
|
||||
lua_sethook(globalL, lstop, flag, 1);
|
||||
}
|
||||
|
||||
@@ -69,14 +89,15 @@ static void print_usage (const char *badoption) {
|
||||
lua_writestringerror(
|
||||
"usage: %s [options] [script [args]]\n"
|
||||
"Available options are:\n"
|
||||
" -e stat execute string 'stat'\n"
|
||||
" -i enter interactive mode after executing 'script'\n"
|
||||
" -l name require library 'name' into global 'name'\n"
|
||||
" -v show version information\n"
|
||||
" -E ignore environment variables\n"
|
||||
" -W turn warnings on\n"
|
||||
" -- stop handling options\n"
|
||||
" - stop handling options and execute stdin\n"
|
||||
" -e stat execute string 'stat'\n"
|
||||
" -i enter interactive mode after executing 'script'\n"
|
||||
" -l mod require library 'mod' into global 'mod'\n"
|
||||
" -l g=mod require library 'mod' into global 'g'\n"
|
||||
" -v show version information\n"
|
||||
" -E ignore environment variables\n"
|
||||
" -W turn warnings on\n"
|
||||
" -- stop handling options\n"
|
||||
" - stop handling options and execute stdin\n"
|
||||
,
|
||||
progname);
|
||||
}
|
||||
@@ -135,9 +156,9 @@ static int docall (lua_State *L, int narg, int nres) {
|
||||
lua_pushcfunction(L, msghandler); /* push message handler */
|
||||
lua_insert(L, base); /* put it under function and args */
|
||||
globalL = L; /* to be available to 'laction' */
|
||||
signal(SIGINT, laction); /* set C-signal handler */
|
||||
setsignal(SIGINT, laction); /* set C-signal handler */
|
||||
status = lua_pcall(L, narg, nres, base);
|
||||
signal(SIGINT, SIG_DFL); /* reset C-signal handler */
|
||||
setsignal(SIGINT, SIG_DFL); /* reset C-signal handler */
|
||||
lua_remove(L, base); /* remove message handler from the stack */
|
||||
return status;
|
||||
}
|
||||
@@ -156,10 +177,11 @@ static void print_version (void) {
|
||||
** to the script (everything after 'script') go to positive indices;
|
||||
** other arguments (before the script name) go to negative indices.
|
||||
** If there is no script name, assume interpreter's name as base.
|
||||
** (If there is no interpreter's name either, 'script' is -1, so
|
||||
** table sizes are zero.)
|
||||
*/
|
||||
static void createargtable (lua_State *L, char **argv, int argc, int script) {
|
||||
int i, narg;
|
||||
if (script == argc) script = 0; /* no script name? */
|
||||
narg = argc - (script + 1); /* number of positive indices */
|
||||
lua_createtable(L, narg, script + 1);
|
||||
for (i = 0; i < argc; i++) {
|
||||
@@ -187,16 +209,22 @@ static int dostring (lua_State *L, const char *s, const char *name) {
|
||||
|
||||
|
||||
/*
|
||||
** Calls 'require(name)' and stores the result in a global variable
|
||||
** with the given name.
|
||||
** Receives 'globname[=modname]' and runs 'globname = require(modname)'.
|
||||
*/
|
||||
static int dolibrary (lua_State *L, const char *name) {
|
||||
static int dolibrary (lua_State *L, char *globname) {
|
||||
int status;
|
||||
char *modname = strchr(globname, '=');
|
||||
if (modname == NULL) /* no explicit name? */
|
||||
modname = globname; /* module name is equal to global name */
|
||||
else {
|
||||
*modname = '\0'; /* global name ends here */
|
||||
modname++; /* module name starts after the '=' */
|
||||
}
|
||||
lua_getglobal(L, "require");
|
||||
lua_pushstring(L, name);
|
||||
status = docall(L, 1, 1); /* call 'require(name)' */
|
||||
lua_pushstring(L, modname);
|
||||
status = docall(L, 1, 1); /* call 'require(modname)' */
|
||||
if (status == LUA_OK)
|
||||
lua_setglobal(L, name); /* global[name] = require return */
|
||||
lua_setglobal(L, globname); /* globname = require(modname) */
|
||||
return report(L, status);
|
||||
}
|
||||
|
||||
@@ -241,14 +269,23 @@ static int handle_script (lua_State *L, char **argv) {
|
||||
|
||||
/*
|
||||
** Traverses all arguments from 'argv', returning a mask with those
|
||||
** needed before running any Lua code (or an error code if it finds
|
||||
** any invalid argument). 'first' returns the first not-handled argument
|
||||
** (either the script name or a bad argument in case of error).
|
||||
** needed before running any Lua code or an error code if it finds any
|
||||
** invalid argument. In case of error, 'first' is the index of the bad
|
||||
** argument. Otherwise, 'first' is -1 if there is no program name,
|
||||
** 0 if there is no script name, or the index of the script name.
|
||||
*/
|
||||
static int collectargs (char **argv, int *first) {
|
||||
int args = 0;
|
||||
int i;
|
||||
for (i = 1; argv[i] != NULL; i++) {
|
||||
if (argv[0] != NULL) { /* is there a program name? */
|
||||
if (argv[0][0]) /* not empty? */
|
||||
progname = argv[0]; /* save it */
|
||||
}
|
||||
else { /* no program name */
|
||||
*first = -1;
|
||||
return 0;
|
||||
}
|
||||
for (i = 1; argv[i] != NULL; i++) { /* handle arguments */
|
||||
*first = i;
|
||||
if (argv[i][0] != '-') /* not an option? */
|
||||
return args; /* stop handling options */
|
||||
@@ -289,7 +326,7 @@ static int collectargs (char **argv, int *first) {
|
||||
return has_error;
|
||||
}
|
||||
}
|
||||
*first = i; /* no script name */
|
||||
*first = 0; /* no script name */
|
||||
return args;
|
||||
}
|
||||
|
||||
@@ -307,7 +344,7 @@ static int runargs (lua_State *L, char **argv, int n) {
|
||||
switch (option) {
|
||||
case 'e': case 'l': {
|
||||
int status;
|
||||
const char *extra = argv[i] + 2; /* both options need an argument */
|
||||
char *extra = argv[i] + 2; /* both options need an argument */
|
||||
if (*extra == '\0') extra = argv[++i];
|
||||
lua_assert(extra != NULL);
|
||||
status = (option == 'e')
|
||||
@@ -416,14 +453,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 */
|
||||
@@ -578,8 +619,8 @@ static int pmain (lua_State *L) {
|
||||
char **argv = (char **)lua_touserdata(L, 2);
|
||||
int script;
|
||||
int args = collectargs(argv, &script);
|
||||
int optlim = (script > 0) ? script : argc; /* first argv not an option */
|
||||
luaL_checkversion(L); /* check that interpreter has correct version */
|
||||
if (argv[0] && argv[0][0]) progname = argv[0];
|
||||
if (args == has_error) { /* bad arg? */
|
||||
print_usage(argv[script]); /* 'script' has index of bad arg. */
|
||||
return 0;
|
||||
@@ -592,19 +633,21 @@ static int pmain (lua_State *L) {
|
||||
}
|
||||
luaL_openlibs(L); /* open standard libraries */
|
||||
createargtable(L, argv, argc, script); /* create table 'arg' */
|
||||
lua_gc(L, LUA_GCGEN, 0, 0); /* GC in generational mode */
|
||||
lua_gc(L, LUA_GCRESTART); /* start GC... */
|
||||
lua_gc(L, LUA_GCGEN, 0, 0); /* ...in generational mode */
|
||||
if (!(args & has_E)) { /* no option '-E'? */
|
||||
if (handle_luainit(L) != LUA_OK) /* run LUA_INIT */
|
||||
return 0; /* error running LUA_INIT */
|
||||
}
|
||||
if (!runargs(L, argv, script)) /* execute arguments -e and -l */
|
||||
if (!runargs(L, argv, optlim)) /* execute arguments -e and -l */
|
||||
return 0; /* something failed */
|
||||
if (script < argc && /* execute main script (if there is one) */
|
||||
handle_script(L, argv + script) != LUA_OK)
|
||||
return 0;
|
||||
if (script > 0) { /* execute main script (if there is one) */
|
||||
if (handle_script(L, argv + script) != LUA_OK)
|
||||
return 0; /* interrupt in case of error */
|
||||
}
|
||||
if (args & has_i) /* -i option? */
|
||||
doREPL(L); /* do read-eval-print loop */
|
||||
else if (script == argc && !(args & (has_e | has_v))) { /* no arguments? */
|
||||
else if (script < 1 && !(args & (has_e | has_v))) { /* no active option? */
|
||||
if (lua_stdin_is_tty()) { /* running in interactive mode? */
|
||||
print_version();
|
||||
doREPL(L); /* do read-eval-print loop */
|
||||
@@ -623,6 +666,7 @@ int main (int argc, char **argv) {
|
||||
l_message(argv[0], "cannot create state: not enough memory");
|
||||
return EXIT_FAILURE;
|
||||
}
|
||||
lua_gc(L, LUA_GCSTOP); /* stop GC while building state */
|
||||
lua_pushcfunction(L, &pmain); /* to call 'pmain' in protected mode */
|
||||
lua_pushinteger(L, argc); /* 1st argument */
|
||||
lua_pushlightuserdata(L, argv); /* 2nd argument */
|
||||
|
||||
30
lua.h
30
lua.h
@@ -18,14 +18,14 @@
|
||||
|
||||
#define LUA_VERSION_MAJOR "5"
|
||||
#define LUA_VERSION_MINOR "4"
|
||||
#define LUA_VERSION_RELEASE "1"
|
||||
#define LUA_VERSION_RELEASE "6"
|
||||
|
||||
#define LUA_VERSION_NUM 504
|
||||
#define LUA_VERSION_RELEASE_NUM (LUA_VERSION_NUM * 100 + 0)
|
||||
#define LUA_VERSION_RELEASE_NUM (LUA_VERSION_NUM * 100 + 6)
|
||||
|
||||
#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-2020 Lua.org, PUC-Rio"
|
||||
#define LUA_COPYRIGHT LUA_RELEASE " Copyright (C) 1994-2023 Lua.org, PUC-Rio"
|
||||
#define LUA_AUTHORS "R. Ierusalimschy, L. H. de Figueiredo, W. Celes"
|
||||
|
||||
|
||||
@@ -131,6 +131,16 @@ typedef void * (*lua_Alloc) (void *ud, void *ptr, size_t osize, size_t nsize);
|
||||
typedef void (*lua_WarnFunction) (void *ud, const char *msg, int tocont);
|
||||
|
||||
|
||||
/*
|
||||
** Type used by the debug API to collect debug information
|
||||
*/
|
||||
typedef struct lua_Debug lua_Debug;
|
||||
|
||||
|
||||
/*
|
||||
** Functions to be called by the debugger in specific events
|
||||
*/
|
||||
typedef void (*lua_Hook) (lua_State *L, lua_Debug *ar);
|
||||
|
||||
|
||||
/*
|
||||
@@ -153,7 +163,8 @@ extern const char lua_ident[];
|
||||
LUA_API lua_State *(lua_newstate) (lua_Alloc f, void *ud);
|
||||
LUA_API void (lua_close) (lua_State *L);
|
||||
LUA_API lua_State *(lua_newthread) (lua_State *L);
|
||||
LUA_API int (lua_resetthread) (lua_State *L);
|
||||
LUA_API int (lua_closethread) (lua_State *L, lua_State *from);
|
||||
LUA_API int (lua_resetthread) (lua_State *L); /* Deprecated! */
|
||||
|
||||
LUA_API lua_CFunction (lua_atpanic) (lua_State *L, lua_CFunction panicf);
|
||||
|
||||
@@ -347,7 +358,8 @@ LUA_API size_t (lua_stringtonumber) (lua_State *L, const char *s);
|
||||
LUA_API lua_Alloc (lua_getallocf) (lua_State *L, void **ud);
|
||||
LUA_API void (lua_setallocf) (lua_State *L, lua_Alloc f, void *ud);
|
||||
|
||||
LUA_API void (lua_toclose) (lua_State *L, int idx);
|
||||
LUA_API void (lua_toclose) (lua_State *L, int idx);
|
||||
LUA_API void (lua_closeslot) (lua_State *L, int idx);
|
||||
|
||||
|
||||
/*
|
||||
@@ -441,12 +453,6 @@ LUA_API void (lua_toclose) (lua_State *L, int idx);
|
||||
#define LUA_MASKLINE (1 << LUA_HOOKLINE)
|
||||
#define LUA_MASKCOUNT (1 << LUA_HOOKCOUNT)
|
||||
|
||||
typedef struct lua_Debug lua_Debug; /* activation record */
|
||||
|
||||
|
||||
/* Functions to be called by the debugger in specific events */
|
||||
typedef void (*lua_Hook) (lua_State *L, lua_Debug *ar);
|
||||
|
||||
|
||||
LUA_API int (lua_getstack) (lua_State *L, int level, lua_Debug *ar);
|
||||
LUA_API int (lua_getinfo) (lua_State *L, const char *what, lua_Debug *ar);
|
||||
@@ -491,7 +497,7 @@ struct lua_Debug {
|
||||
|
||||
|
||||
/******************************************************************************
|
||||
* Copyright (C) 1994-2020 Lua.org, PUC-Rio.
|
||||
* Copyright (C) 1994-2023 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
|
||||
|
||||
145
luaconf.h
145
luaconf.h
@@ -16,13 +16,13 @@
|
||||
** ===================================================================
|
||||
** General Configuration File for Lua
|
||||
**
|
||||
** Some definitions here can be changed externally, through the
|
||||
** compiler (e.g., with '-D' options). Those are protected by
|
||||
** '#if !defined' guards. However, several other definitions should
|
||||
** be changed directly here, either because they affect the Lua
|
||||
** ABI (by making the changes here, you ensure that all software
|
||||
** connected to Lua, such as C libraries, will be compiled with the
|
||||
** same configuration); or because they are seldom changed.
|
||||
** Some definitions here can be changed externally, through the compiler
|
||||
** (e.g., with '-D' options): They are commented out or protected
|
||||
** by '#if !defined' guards. However, several other definitions
|
||||
** should be changed directly here, either because they affect the
|
||||
** Lua ABI (by making the changes here, you ensure that all software
|
||||
** connected to Lua, such as C libraries, will be compiled with the same
|
||||
** configuration); or because they are seldom changed.
|
||||
**
|
||||
** Search for "@@" to find all configurable definitions.
|
||||
** ===================================================================
|
||||
@@ -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
|
||||
@@ -85,6 +70,12 @@
|
||||
#endif
|
||||
|
||||
|
||||
#if defined(LUA_USE_IOS)
|
||||
#define LUA_USE_POSIX
|
||||
#define LUA_USE_DLOPEN
|
||||
#endif
|
||||
|
||||
|
||||
/*
|
||||
@@ LUAI_IS32INT is true iff 'int' has (at least) 32 bits.
|
||||
*/
|
||||
@@ -96,26 +87,12 @@
|
||||
|
||||
/*
|
||||
** {==================================================================
|
||||
** Configuration for Number types.
|
||||
** Configuration for Number types. These options should not be
|
||||
** set externally, because any other code connected to Lua must
|
||||
** use the same configuration.
|
||||
** ===================================================================
|
||||
*/
|
||||
|
||||
/*
|
||||
@@ LUA_32BITS enables Lua with 32-bit integers and 32-bit floats.
|
||||
*/
|
||||
/* #define LUA_32BITS */
|
||||
|
||||
|
||||
/*
|
||||
@@ LUA_C89_NUMBERS ensures that Lua uses the largest types available for
|
||||
** C89 ('long' and 'double'); Windows always has '__int64', so it does
|
||||
** not need to use this case.
|
||||
*/
|
||||
#if defined(LUA_USE_C89) && !defined(LUA_USE_WINDOWS)
|
||||
#define LUA_C89_NUMBERS
|
||||
#endif
|
||||
|
||||
|
||||
/*
|
||||
@@ LUA_INT_TYPE defines the type for Lua integers.
|
||||
@@ LUA_FLOAT_TYPE defines the type for Lua floats.
|
||||
@@ -136,7 +113,31 @@
|
||||
#define LUA_FLOAT_DOUBLE 2
|
||||
#define LUA_FLOAT_LONGDOUBLE 3
|
||||
|
||||
#if defined(LUA_32BITS) /* { */
|
||||
|
||||
/* Default configuration ('long long' and 'double', for 64-bit Lua) */
|
||||
#define LUA_INT_DEFAULT LUA_INT_LONGLONG
|
||||
#define LUA_FLOAT_DEFAULT LUA_FLOAT_DOUBLE
|
||||
|
||||
|
||||
/*
|
||||
@@ LUA_32BITS enables Lua with 32-bit integers and 32-bit floats.
|
||||
*/
|
||||
#define LUA_32BITS 0
|
||||
|
||||
|
||||
/*
|
||||
@@ LUA_C89_NUMBERS ensures that Lua uses the largest types available for
|
||||
** C89 ('long' and 'double'); Windows always has '__int64', so it does
|
||||
** not need to use this case.
|
||||
*/
|
||||
#if defined(LUA_USE_C89) && !defined(LUA_USE_WINDOWS)
|
||||
#define LUA_C89_NUMBERS 1
|
||||
#else
|
||||
#define LUA_C89_NUMBERS 0
|
||||
#endif
|
||||
|
||||
|
||||
#if LUA_32BITS /* { */
|
||||
/*
|
||||
** 32-bit integers and 'float'
|
||||
*/
|
||||
@@ -147,27 +148,22 @@
|
||||
#endif
|
||||
#define LUA_FLOAT_TYPE LUA_FLOAT_FLOAT
|
||||
|
||||
#elif defined(LUA_C89_NUMBERS) /* }{ */
|
||||
#elif LUA_C89_NUMBERS /* }{ */
|
||||
/*
|
||||
** largest types available for C89 ('long' and 'double')
|
||||
*/
|
||||
#define LUA_INT_TYPE LUA_INT_LONG
|
||||
#define LUA_FLOAT_TYPE LUA_FLOAT_DOUBLE
|
||||
|
||||
#else /* }{ */
|
||||
/* use defaults */
|
||||
|
||||
#define LUA_INT_TYPE LUA_INT_DEFAULT
|
||||
#define LUA_FLOAT_TYPE LUA_FLOAT_DEFAULT
|
||||
|
||||
#endif /* } */
|
||||
|
||||
|
||||
/*
|
||||
** default configuration for 64-bit Lua ('long long' and 'double')
|
||||
*/
|
||||
#if !defined(LUA_INT_TYPE)
|
||||
#define LUA_INT_TYPE LUA_INT_LONGLONG
|
||||
#endif
|
||||
|
||||
#if !defined(LUA_FLOAT_TYPE)
|
||||
#define LUA_FLOAT_TYPE LUA_FLOAT_DOUBLE
|
||||
#endif
|
||||
|
||||
/* }================================================================== */
|
||||
|
||||
|
||||
@@ -388,14 +384,13 @@
|
||||
|
||||
/*
|
||||
** {==================================================================
|
||||
** Configuration for Numbers.
|
||||
** Configuration for Numbers (low-level part).
|
||||
** Change these definitions if no predefined LUA_FLOAT_* / LUA_INT_*
|
||||
** satisfy your needs.
|
||||
** ===================================================================
|
||||
*/
|
||||
|
||||
/*
|
||||
@@ 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_floatatt(x) corrects float attribute 'x' to the proper float type
|
||||
@@ -488,10 +483,7 @@
|
||||
|
||||
|
||||
/*
|
||||
@@ LUA_INTEGER is the integer type used by Lua.
|
||||
**
|
||||
@@ LUA_UNSIGNED is the unsigned version of LUA_INTEGER.
|
||||
**
|
||||
@@ LUAI_UACINT is the result of a 'default argument promotion'
|
||||
@@ over a LUA_INTEGER.
|
||||
@@ LUA_INTEGER_FRMLEN is the length modifier for reading/writing integers.
|
||||
@@ -499,7 +491,6 @@
|
||||
@@ LUA_MAXINTEGER is the maximum value for a LUA_INTEGER.
|
||||
@@ LUA_MININTEGER is the minimum value for a LUA_INTEGER.
|
||||
@@ LUA_MAXUNSIGNED is the maximum value for a LUA_UNSIGNED.
|
||||
@@ LUA_UNSIGNEDBITS is the number of bits in a LUA_UNSIGNED.
|
||||
@@ lua_integer2str converts an integer to a string.
|
||||
*/
|
||||
|
||||
@@ -520,9 +511,6 @@
|
||||
#define LUA_UNSIGNED unsigned LUAI_UACINT
|
||||
|
||||
|
||||
#define LUA_UNSIGNEDBITS (sizeof(LUA_UNSIGNED) * CHAR_BIT)
|
||||
|
||||
|
||||
/* now the variable definitions */
|
||||
|
||||
#if LUA_INT_TYPE == LUA_INT_INT /* { int */
|
||||
@@ -674,6 +662,34 @@
|
||||
#define lua_getlocaledecpoint() (localeconv()->decimal_point[0])
|
||||
#endif
|
||||
|
||||
|
||||
/*
|
||||
** macros to improve jump prediction, used mostly for error handling
|
||||
** and debug facilities. (Some macros in the Lua API use these macros.
|
||||
** Define LUA_NOBUILTIN if you do not want '__builtin_expect' in your
|
||||
** code.)
|
||||
*/
|
||||
#if !defined(luai_likely)
|
||||
|
||||
#if defined(__GNUC__) && !defined(LUA_NOBUILTIN)
|
||||
#define luai_likely(x) (__builtin_expect(((x) != 0), 1))
|
||||
#define luai_unlikely(x) (__builtin_expect(((x) != 0), 0))
|
||||
#else
|
||||
#define luai_likely(x) (x)
|
||||
#define luai_unlikely(x) (x)
|
||||
#endif
|
||||
|
||||
#endif
|
||||
|
||||
|
||||
#if defined(LUA_CORE) || defined(LUA_LIB)
|
||||
/* shorter names for Lua's own use */
|
||||
#define l_likely(x) luai_likely(x)
|
||||
#define l_unlikely(x) luai_unlikely(x)
|
||||
#endif
|
||||
|
||||
|
||||
|
||||
/* }================================================================== */
|
||||
|
||||
|
||||
@@ -718,7 +734,7 @@
|
||||
** CHANGE it if you need a different limit. This limit is arbitrary;
|
||||
** its only purpose is to stop Lua from consuming unlimited stack
|
||||
** space (and to reserve some numbers for pseudo-indices).
|
||||
** (It must fit into max(size_t)/32.)
|
||||
** (It must fit into max(size_t)/32 and max(int)/2.)
|
||||
*/
|
||||
#if LUAI_IS32INT
|
||||
#define LUAI_MAXSTACK 1000000
|
||||
@@ -737,14 +753,15 @@
|
||||
|
||||
/*
|
||||
@@ LUA_IDSIZE gives the maximum size for the description of the source
|
||||
@@ of a function in debug information.
|
||||
** of a function in debug information.
|
||||
** CHANGE it if you want a different size.
|
||||
*/
|
||||
#define LUA_IDSIZE 60
|
||||
|
||||
|
||||
/*
|
||||
@@ LUAL_BUFFERSIZE is the buffer size used by the lauxlib buffer system.
|
||||
@@ LUAL_BUFFERSIZE is the initial buffer size used by the lauxlib
|
||||
** buffer system.
|
||||
*/
|
||||
#define LUAL_BUFFERSIZE ((int)(16 * sizeof(void*) * sizeof(lua_Number)))
|
||||
|
||||
|
||||
6
lualib.h
6
lualib.h
@@ -49,10 +49,4 @@ LUAMOD_API int (luaopen_package) (lua_State *L);
|
||||
LUALIB_API void (luaL_openlibs) (lua_State *L);
|
||||
|
||||
|
||||
|
||||
#if !defined(lua_assert)
|
||||
#define lua_assert(x) ((void)0)
|
||||
#endif
|
||||
|
||||
|
||||
#endif
|
||||
|
||||
@@ -120,10 +120,10 @@ static TString *loadStringN (LoadState *S, Proto *p) {
|
||||
}
|
||||
else { /* long string */
|
||||
ts = luaS_createlngstrobj(L, size); /* create string */
|
||||
setsvalue2s(L, L->top, ts); /* anchor it ('loadVector' can GC) */
|
||||
setsvalue2s(L, L->top.p, ts); /* anchor it ('loadVector' can GC) */
|
||||
luaD_inctop(L);
|
||||
loadVector(S, getstr(ts), size); /* load directly in final place */
|
||||
L->top--; /* pop string */
|
||||
L->top.p--; /* pop string */
|
||||
}
|
||||
luaC_objbarrier(L, p, ts);
|
||||
return ts;
|
||||
@@ -248,6 +248,8 @@ static void loadDebug (LoadState *S, Proto *f) {
|
||||
f->locvars[i].endpc = loadInt(S);
|
||||
}
|
||||
n = loadInt(S);
|
||||
if (n != 0) /* does it have debug information? */
|
||||
n = f->sizeupvalues; /* must be this many */
|
||||
for (i = 0; i < n; i++)
|
||||
f->upvalues[i].name = loadStringN(S, f);
|
||||
}
|
||||
@@ -321,7 +323,7 @@ LClosure *luaU_undump(lua_State *L, ZIO *Z, const char *name) {
|
||||
S.Z = Z;
|
||||
checkHeader(&S);
|
||||
cl = luaF_newLclosure(L, loadByte(&S));
|
||||
setclLvalue2s(L, L->top, cl);
|
||||
setclLvalue2s(L, L->top.p, cl);
|
||||
luaD_inctop(L);
|
||||
cl->p = luaF_newproto(L);
|
||||
luaC_objbarrier(L, cl, cl->p);
|
||||
|
||||
36
lutf8lib.c
36
lutf8lib.c
@@ -25,6 +25,9 @@
|
||||
|
||||
#define MAXUTF 0x7FFFFFFFu
|
||||
|
||||
|
||||
#define MSGInvalid "invalid UTF-8 code"
|
||||
|
||||
/*
|
||||
** Integer type for decoded UTF-8 values; MAXUTF needs 31 bits.
|
||||
*/
|
||||
@@ -35,7 +38,8 @@ typedef unsigned long utfint;
|
||||
#endif
|
||||
|
||||
|
||||
#define iscont(p) ((*(p) & 0xC0) == 0x80)
|
||||
#define iscont(c) (((c) & 0xC0) == 0x80)
|
||||
#define iscontp(p) iscont(*(p))
|
||||
|
||||
|
||||
/* from strlib */
|
||||
@@ -65,7 +69,7 @@ static const char *utf8_decode (const char *s, utfint *val, int strict) {
|
||||
int count = 0; /* to count number of continuation bytes */
|
||||
for (; c & 0x40; c <<= 1) { /* while it needs continuation bytes... */
|
||||
unsigned int cc = (unsigned char)s[++count]; /* read next byte */
|
||||
if ((cc & 0xC0) != 0x80) /* not a continuation byte? */
|
||||
if (!iscont(cc)) /* not a continuation byte? */
|
||||
return NULL; /* invalid byte sequence */
|
||||
res = (res << 6) | (cc & 0x3F); /* add lower 6 bits from cont. byte */
|
||||
}
|
||||
@@ -140,7 +144,7 @@ static int codepoint (lua_State *L) {
|
||||
utfint code;
|
||||
s = utf8_decode(s, &code, !lax);
|
||||
if (s == NULL)
|
||||
return luaL_error(L, "invalid UTF-8 code");
|
||||
return luaL_error(L, MSGInvalid);
|
||||
lua_pushinteger(L, code);
|
||||
n++;
|
||||
}
|
||||
@@ -190,16 +194,16 @@ static int byteoffset (lua_State *L) {
|
||||
"position out of bounds");
|
||||
if (n == 0) {
|
||||
/* find beginning of current byte sequence */
|
||||
while (posi > 0 && iscont(s + posi)) posi--;
|
||||
while (posi > 0 && iscontp(s + posi)) posi--;
|
||||
}
|
||||
else {
|
||||
if (iscont(s + posi))
|
||||
if (iscontp(s + posi))
|
||||
return luaL_error(L, "initial position is a continuation byte");
|
||||
if (n < 0) {
|
||||
while (n < 0 && posi > 0) { /* move back */
|
||||
do { /* find beginning of previous character */
|
||||
posi--;
|
||||
} while (posi > 0 && iscont(s + posi));
|
||||
} while (posi > 0 && iscontp(s + posi));
|
||||
n++;
|
||||
}
|
||||
}
|
||||
@@ -208,7 +212,7 @@ static int byteoffset (lua_State *L) {
|
||||
while (n > 0 && posi < (lua_Integer)len) {
|
||||
do { /* find beginning of next character */
|
||||
posi++;
|
||||
} while (iscont(s + posi)); /* (cannot pass final '\0') */
|
||||
} while (iscontp(s + posi)); /* (cannot pass final '\0') */
|
||||
n--;
|
||||
}
|
||||
}
|
||||
@@ -224,20 +228,17 @@ static int byteoffset (lua_State *L) {
|
||||
static int iter_aux (lua_State *L, int strict) {
|
||||
size_t len;
|
||||
const char *s = luaL_checklstring(L, 1, &len);
|
||||
lua_Integer n = lua_tointeger(L, 2) - 1;
|
||||
if (n < 0) /* first iteration? */
|
||||
n = 0; /* start from here */
|
||||
else if (n < (lua_Integer)len) {
|
||||
n++; /* skip current byte */
|
||||
while (iscont(s + n)) n++; /* and its continuations */
|
||||
lua_Unsigned n = (lua_Unsigned)lua_tointeger(L, 2);
|
||||
if (n < len) {
|
||||
while (iscontp(s + n)) n++; /* go to next character */
|
||||
}
|
||||
if (n >= (lua_Integer)len)
|
||||
if (n >= len) /* (also handles original 'n' being negative) */
|
||||
return 0; /* no more codepoints */
|
||||
else {
|
||||
utfint code;
|
||||
const char *next = utf8_decode(s + n, &code, strict);
|
||||
if (next == NULL)
|
||||
return luaL_error(L, "invalid UTF-8 code");
|
||||
if (next == NULL || iscontp(next))
|
||||
return luaL_error(L, MSGInvalid);
|
||||
lua_pushinteger(L, n + 1);
|
||||
lua_pushinteger(L, code);
|
||||
return 2;
|
||||
@@ -256,7 +257,8 @@ static int iter_auxlax (lua_State *L) {
|
||||
|
||||
static int iter_codes (lua_State *L) {
|
||||
int lax = lua_toboolean(L, 2);
|
||||
luaL_checkstring(L, 1);
|
||||
const char *s = luaL_checkstring(L, 1);
|
||||
luaL_argcheck(L, !iscontp(s), 1, MSGInvalid);
|
||||
lua_pushcfunction(L, lax ? iter_auxlax : iter_auxstrict);
|
||||
lua_pushvalue(L, 1);
|
||||
lua_pushinteger(L, 0);
|
||||
|
||||
11
lvm.h
11
lvm.h
@@ -60,12 +60,14 @@ typedef enum {
|
||||
|
||||
/* convert an object to an integer (including string coercion) */
|
||||
#define tointeger(o,i) \
|
||||
(ttisinteger(o) ? (*(i) = ivalue(o), 1) : luaV_tointeger(o,i,LUA_FLOORN2I))
|
||||
(l_likely(ttisinteger(o)) ? (*(i) = ivalue(o), 1) \
|
||||
: luaV_tointeger(o,i,LUA_FLOORN2I))
|
||||
|
||||
|
||||
/* convert an object to an integer (without string coercion) */
|
||||
#define tointegerns(o,i) \
|
||||
(ttisinteger(o) ? (*(i) = ivalue(o), 1) : luaV_tointegerns(o,i,LUA_FLOORN2I))
|
||||
(l_likely(ttisinteger(o)) ? (*(i) = ivalue(o), 1) \
|
||||
: luaV_tointegerns(o,i,LUA_FLOORN2I))
|
||||
|
||||
|
||||
#define intop(op,v1,v2) l_castU2S(l_castS2U(v1) op l_castS2U(v2))
|
||||
@@ -108,6 +110,11 @@ typedef enum {
|
||||
luaC_barrierback(L, gcvalue(t), v); }
|
||||
|
||||
|
||||
/*
|
||||
** Shift right is the same as shift left with a negative 'y'
|
||||
*/
|
||||
#define luaV_shiftr(x,y) luaV_shiftl(x,intop(-, 0, y))
|
||||
|
||||
|
||||
|
||||
LUAI_FUNC int luaV_equalobj (lua_State *L, const TValue *t1, const TValue *t2);
|
||||
|
||||
34
makefile
34
makefile
@@ -5,17 +5,15 @@
|
||||
|
||||
# Warnings valid for both C and C++
|
||||
CWARNSCPP= \
|
||||
-fmax-errors=5 \
|
||||
-Wfatal-errors \
|
||||
-Wextra \
|
||||
-Wshadow \
|
||||
-Wsign-compare \
|
||||
-Wundef \
|
||||
-Wwrite-strings \
|
||||
-Wredundant-decls \
|
||||
-Wdisabled-optimization \
|
||||
-Wdouble-promotion \
|
||||
-Wlogical-op \
|
||||
-Wno-aggressive-loop-optimizations \
|
||||
-Wmissing-declarations \
|
||||
# the next warnings might be useful sometimes,
|
||||
# but usually they generate too much noise
|
||||
# -Werror \
|
||||
@@ -26,6 +24,13 @@ CWARNSCPP= \
|
||||
# -Wformat=2 \
|
||||
# -Wcast-qual \
|
||||
|
||||
|
||||
# Warnings for gcc, not valid for clang
|
||||
CWARNGCC= \
|
||||
-Wlogical-op \
|
||||
-Wno-aggressive-loop-optimizations \
|
||||
|
||||
|
||||
# The next warnings are neither valid nor needed for C++
|
||||
CWARNSC= -Wdeclaration-after-statement \
|
||||
-Wmissing-prototypes \
|
||||
@@ -35,7 +40,7 @@ CWARNSC= -Wdeclaration-after-statement \
|
||||
-Wold-style-definition \
|
||||
|
||||
|
||||
CWARNS= $(CWARNSCPP) $(CWARNSC)
|
||||
CWARNS= $(CWARNSCPP) $(CWARNSC) $(CWARNGCC)
|
||||
|
||||
# Some useful compiler options for internal tests:
|
||||
# -DLUAI_ASSERT turns on all assertions inside Lua.
|
||||
@@ -51,8 +56,13 @@ CWARNS= $(CWARNSCPP) $(CWARNSC)
|
||||
|
||||
# -pg -malign-double
|
||||
# -DLUA_USE_CTYPE -DLUA_USE_APICHECK
|
||||
# ('-ftrapv' for runtime checks of integer overflows)
|
||||
# -fsanitize=undefined -ftrapv -fno-inline
|
||||
|
||||
# The following options help detect "undefined behavior"s that seldom
|
||||
# create problems; some are only available in newer gcc versions. To
|
||||
# use some of them, we also have to define an environment variable
|
||||
# ASAN_OPTIONS="detect_invalid_pointer_pairs=2".
|
||||
# -fsanitize=undefined
|
||||
# -fsanitize=pointer-subtract -fsanitize=address -fsanitize=pointer-compare
|
||||
# TESTS= -DLUA_USER_H='"ltests.h"' -O0 -g
|
||||
|
||||
|
||||
@@ -109,16 +119,6 @@ $(LUA_T): $(LUA_O) $(CORE_T)
|
||||
$(CC) -o $@ $(MYLDFLAGS) $(LUA_O) $(CORE_T) $(LIBS) $(MYLIBS) $(DL)
|
||||
|
||||
|
||||
llex.o:
|
||||
$(CC) $(CFLAGS) -Os -c llex.c
|
||||
|
||||
lparser.o:
|
||||
$(CC) $(CFLAGS) -Os -c lparser.c
|
||||
|
||||
lcode.o:
|
||||
$(CC) $(CFLAGS) -Os -c lcode.c
|
||||
|
||||
|
||||
clean:
|
||||
$(RM) $(ALL_T) $(ALL_O)
|
||||
|
||||
|
||||
@@ -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>
|
||||
© 2020 Lua.org, PUC-Rio. All rights reserved.
|
||||
© 2023 Lua.org, PUC-Rio. All rights reserved.
|
||||
</small>
|
||||
<hr>
|
||||
|
||||
|
||||
565
manual/manual.of
565
manual/manual.of
File diff suppressed because it is too large
Load Diff
20
onelua.c
20
onelua.c
@@ -1,5 +1,14 @@
|
||||
/*
|
||||
* one.c -- Lua core, libraries, and interpreter in a single file
|
||||
** Lua core, libraries, and interpreter in a single file.
|
||||
** Compiling just this file generates a complete Lua stand-alone
|
||||
** program:
|
||||
**
|
||||
** $ gcc -O2 -std=c99 -o lua onelua.c -lm
|
||||
**
|
||||
** or
|
||||
**
|
||||
** $ gcc -O2 -std=c89 -DLUA_USE_C89 -o lua onelua.c -lm
|
||||
**
|
||||
*/
|
||||
|
||||
/* default is to build the full interpreter */
|
||||
@@ -11,8 +20,12 @@
|
||||
#endif
|
||||
#endif
|
||||
|
||||
/* choose suitable platform-specific features */
|
||||
/* some of these may need extra libraries such as -ldl -lreadline -lncurses */
|
||||
|
||||
/*
|
||||
** Choose suitable platform-specific features. Default is no
|
||||
** platform-specific features. Some of these options may need extra
|
||||
** libraries such as -ldl -lreadline -lncurses
|
||||
*/
|
||||
#if 0
|
||||
#define LUA_USE_LINUX
|
||||
#define LUA_USE_MACOSX
|
||||
@@ -20,6 +33,7 @@
|
||||
#define LUA_ANSI
|
||||
#endif
|
||||
|
||||
|
||||
/* no need to change anything below this line ----------------------------- */
|
||||
|
||||
#include "lprefix.h"
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -154,18 +154,8 @@ end
|
||||
|
||||
dofile('main.lua')
|
||||
|
||||
do
|
||||
local next, setmetatable, stderr = next, setmetatable, io.stderr
|
||||
-- track collections
|
||||
local mt = {}
|
||||
-- each time a table is collected, remark it for finalization
|
||||
-- on next cycle
|
||||
mt.__gc = function (o)
|
||||
stderr:write'.' -- mark progress
|
||||
local n = setmetatable(o, mt) -- remark it
|
||||
end
|
||||
local n = setmetatable({}, mt) -- create object
|
||||
end
|
||||
-- trace GC cycles
|
||||
require"tracegc".start()
|
||||
|
||||
report"gc.lua"
|
||||
local f = assert(loadfile('gc.lua'))
|
||||
@@ -173,6 +163,7 @@ f()
|
||||
|
||||
dofile('db.lua')
|
||||
assert(dofile('calls.lua') == deep and deep)
|
||||
_G.deep = nil
|
||||
olddofile('strings.lua')
|
||||
olddofile('literals.lua')
|
||||
dofile('tpack.lua')
|
||||
|
||||
168
testes/api.lua
168
testes/api.lua
@@ -14,7 +14,7 @@ local pack = table.pack
|
||||
-- standard error message for memory errors
|
||||
local MEMERRMSG = "not enough memory"
|
||||
|
||||
function tcheck (t1, t2)
|
||||
local 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
|
||||
end
|
||||
@@ -28,7 +28,7 @@ end
|
||||
|
||||
print('testing C API')
|
||||
|
||||
a = T.testC("pushvalue R; return 1")
|
||||
local a = T.testC("pushvalue R; return 1")
|
||||
assert(a == debug.getregistry())
|
||||
|
||||
|
||||
@@ -43,10 +43,10 @@ a = T.d2s(12458954321123.0)
|
||||
assert(a == string.pack("d", 12458954321123.0))
|
||||
assert(T.s2d(a) == 12458954321123.0)
|
||||
|
||||
a,b,c = T.testC("pushnum 1; pushnum 2; pushnum 3; return 2")
|
||||
local a,b,c = T.testC("pushnum 1; pushnum 2; pushnum 3; return 2")
|
||||
assert(a == 2 and b == 3 and not c)
|
||||
|
||||
f = T.makeCfunc("pushnum 1; pushnum 2; pushnum 3; return 2")
|
||||
local f = T.makeCfunc("pushnum 1; pushnum 2; pushnum 3; return 2")
|
||||
a,b,c = f()
|
||||
assert(a == 2 and b == 3 and not c)
|
||||
|
||||
@@ -61,7 +61,7 @@ assert(a==false and b==true and c==false)
|
||||
a,b,c = T.testC("gettop; return 2", 10, 20, 30, 40)
|
||||
assert(a == 40 and b == 5 and not c)
|
||||
|
||||
t = pack(T.testC("settop 5; return *", 2, 3))
|
||||
local t = pack(T.testC("settop 5; return *", 2, 3))
|
||||
tcheck(t, {n=4,2,3})
|
||||
|
||||
t = pack(T.testC("settop 0; settop 15; return 10", 3, 1, 23))
|
||||
@@ -166,16 +166,17 @@ end
|
||||
|
||||
|
||||
-- testing globals
|
||||
_G.a = 14; _G.b = "a31"
|
||||
_G.AA = 14; _G.BB = "a31"
|
||||
local a = {T.testC[[
|
||||
getglobal a;
|
||||
getglobal b;
|
||||
getglobal b;
|
||||
setglobal a;
|
||||
getglobal AA;
|
||||
getglobal BB;
|
||||
getglobal BB;
|
||||
setglobal AA;
|
||||
return *
|
||||
]]}
|
||||
assert(a[2] == 14 and a[3] == "a31" and a[4] == nil and _G.a == "a31")
|
||||
assert(a[2] == 14 and a[3] == "a31" and a[4] == nil and _G.AA == "a31")
|
||||
|
||||
_G.AA, _G.BB = nil
|
||||
|
||||
-- testing arith
|
||||
assert(T.testC("pushnum 10; pushnum 20; arith /; return 1") == 0.5)
|
||||
@@ -199,13 +200,14 @@ a,b,c = T.testC([[pushnum 1;
|
||||
pushstring 10; arith _;
|
||||
pushstring 5; return 3]])
|
||||
assert(a == 1 and b == -10 and c == "5")
|
||||
mt = {__add = function (a,b) return setmetatable({a[1] + b[1]}, mt) end,
|
||||
local mt = {
|
||||
__add = function (a,b) return setmetatable({a[1] + b[1]}, mt) end,
|
||||
__mod = function (a,b) return setmetatable({a[1] % b[1]}, mt) end,
|
||||
__unm = function (a) return setmetatable({a[1]* 2}, mt) end}
|
||||
a,b,c = setmetatable({4}, mt),
|
||||
setmetatable({8}, mt),
|
||||
setmetatable({-3}, mt)
|
||||
x,y,z = T.testC("arith +; return 2", 10, a, b)
|
||||
local x,y,z = T.testC("arith +; return 2", 10, a, b)
|
||||
assert(x == 10 and y[1] == 12 and z == nil)
|
||||
assert(T.testC("arith %; return 1", a, c)[1] == 4%-3)
|
||||
assert(T.testC("arith _; arith +; arith %; return 1", b, a, c)[1] ==
|
||||
@@ -312,9 +314,9 @@ assert(T.testC("concat 1; return 1", "xuxu") == "xuxu")
|
||||
|
||||
-- testing lua_is
|
||||
|
||||
function B(x) return x and 1 or 0 end
|
||||
local function B (x) return x and 1 or 0 end
|
||||
|
||||
function count (x, n)
|
||||
local function count (x, n)
|
||||
n = n or 2
|
||||
local prog = [[
|
||||
isnumber %d;
|
||||
@@ -345,7 +347,7 @@ assert(count(nil, 15) == 100)
|
||||
|
||||
-- testing lua_to...
|
||||
|
||||
function to (s, x, n)
|
||||
local function to (s, x, n)
|
||||
n = n or 2
|
||||
return T.testC(string.format("%s %d; return 1", s, n), x)
|
||||
end
|
||||
@@ -486,11 +488,12 @@ a = T.testC([[
|
||||
pushvalue 3; insert -2; pcall 1 1 0;
|
||||
pcall 0 0 0;
|
||||
return 1
|
||||
]], "x=150", function (a) assert(a==nil); return 3 end)
|
||||
]], "XX=150", function (a) assert(a==nil); return 3 end)
|
||||
|
||||
assert(type(a) == 'string' and x == 150)
|
||||
assert(type(a) == 'string' and XX == 150)
|
||||
_G.XX = nil
|
||||
|
||||
function check3(p, ...)
|
||||
local function check3(p, ...)
|
||||
local arg = {...}
|
||||
assert(#arg == 3)
|
||||
assert(string.find(arg[3], p))
|
||||
@@ -500,17 +503,20 @@ check3("%.", T.testC("loadfile 2; return *", "."))
|
||||
check3("xxxx", T.testC("loadfile 2; return *", "xxxx"))
|
||||
|
||||
-- test errors in non protected threads
|
||||
function checkerrnopro (code, msg)
|
||||
local function checkerrnopro (code, msg)
|
||||
local th = coroutine.create(function () end) -- create new thread
|
||||
local stt, err = pcall(T.testC, th, code) -- run code there
|
||||
assert(not stt and string.find(err, msg))
|
||||
end
|
||||
|
||||
if not _soft then
|
||||
collectgarbage("stop") -- avoid __gc with full stack
|
||||
checkerrnopro("pushnum 3; call 0 0", "attempt to call")
|
||||
print"testing stack overflow in unprotected thread"
|
||||
function f () f() end
|
||||
checkerrnopro("getglobal 'f'; call 0 0;", "stack overflow")
|
||||
function F () F() end
|
||||
checkerrnopro("getglobal 'F'; call 0 0;", "stack overflow")
|
||||
F = nil
|
||||
collectgarbage("restart")
|
||||
end
|
||||
print"+"
|
||||
|
||||
@@ -586,7 +592,7 @@ assert(a[a] == "x")
|
||||
|
||||
b = setmetatable({p = a}, {})
|
||||
getmetatable(b).__index = function (t, i) return t.p[i] end
|
||||
k, x = T.testC("gettable 3, return 2", 4, b, 20, 35, "x")
|
||||
local k, x = T.testC("gettable 3, return 2", 4, b, 20, 35, "x")
|
||||
assert(x == 15 and k == 35)
|
||||
k = T.testC("getfield 2 y, return 1", b)
|
||||
assert(k == 12)
|
||||
@@ -746,8 +752,8 @@ local i = T.ref{}
|
||||
T.unref(i)
|
||||
assert(T.ref{} == i)
|
||||
|
||||
Arr = {}
|
||||
Lim = 100
|
||||
local Arr = {}
|
||||
local Lim = 100
|
||||
for i=1,Lim do -- lock many objects
|
||||
Arr[i] = T.ref({})
|
||||
end
|
||||
@@ -759,7 +765,7 @@ for i=1,Lim do -- unlock all them
|
||||
T.unref(Arr[i])
|
||||
end
|
||||
|
||||
function printlocks ()
|
||||
local function printlocks ()
|
||||
local f = T.makeCfunc("gettable R; return 1")
|
||||
local n = f("n")
|
||||
print("n", n)
|
||||
@@ -791,8 +797,8 @@ assert(type(T.getref(a)) == 'table')
|
||||
|
||||
|
||||
-- colect in cl the `val' of all collected userdata
|
||||
tt = {}
|
||||
cl = {n=0}
|
||||
local tt = {}
|
||||
local cl = {n=0}
|
||||
A = nil; B = nil
|
||||
local F
|
||||
F = function (x)
|
||||
@@ -802,20 +808,20 @@ F = function (x)
|
||||
d = nil
|
||||
assert(debug.getmetatable(x).__gc == F)
|
||||
assert(load("table.insert({}, {})"))() -- create more garbage
|
||||
collectgarbage() -- force a GC during GC
|
||||
assert(debug.getmetatable(x).__gc == F) -- previous GC did not mess this?
|
||||
assert(not collectgarbage()) -- GC during GC (no op)
|
||||
local dummy = {} -- create more garbage during GC
|
||||
if A ~= nil then
|
||||
assert(type(A) == "userdata")
|
||||
assert(T.udataval(A) == B)
|
||||
debug.getmetatable(A) -- just access it
|
||||
end
|
||||
A = x -- ressucita userdata
|
||||
A = x -- ressurect userdata
|
||||
B = udval
|
||||
return 1,2,3
|
||||
end
|
||||
tt.__gc = F
|
||||
|
||||
|
||||
-- test whether udate collection frees memory in the right time
|
||||
do
|
||||
collectgarbage();
|
||||
@@ -852,9 +858,9 @@ end
|
||||
collectgarbage("stop")
|
||||
|
||||
-- create 3 userdatas with tag `tt'
|
||||
a = T.newuserdata(0); debug.setmetatable(a, tt); na = T.udataval(a)
|
||||
b = T.newuserdata(0); debug.setmetatable(b, tt); nb = T.udataval(b)
|
||||
c = T.newuserdata(0); debug.setmetatable(c, tt); nc = T.udataval(c)
|
||||
a = T.newuserdata(0); debug.setmetatable(a, tt); local na = T.udataval(a)
|
||||
b = T.newuserdata(0); debug.setmetatable(b, tt); local nb = T.udataval(b)
|
||||
c = T.newuserdata(0); debug.setmetatable(c, tt); local nc = T.udataval(c)
|
||||
|
||||
-- create userdata without meta table
|
||||
x = T.newuserdata(4)
|
||||
@@ -865,9 +871,9 @@ checkerr("FILE%* expected, got userdata", io.input, x)
|
||||
|
||||
assert(debug.getmetatable(x) == nil and debug.getmetatable(y) == nil)
|
||||
|
||||
d=T.ref(a);
|
||||
e=T.ref(b);
|
||||
f=T.ref(c);
|
||||
local d = T.ref(a);
|
||||
local e = T.ref(b);
|
||||
local f = T.ref(c);
|
||||
t = {T.getref(d), T.getref(e), T.getref(f)}
|
||||
assert(t[1] == a and t[2] == b and t[3] == c)
|
||||
|
||||
@@ -887,7 +893,7 @@ tt=nil -- frees tt for GC
|
||||
A = nil
|
||||
b = nil
|
||||
T.unref(d);
|
||||
n5 = T.newuserdata(0)
|
||||
local n5 = T.newuserdata(0)
|
||||
debug.setmetatable(n5, {__gc=F})
|
||||
n5 = T.udataval(n5)
|
||||
collectgarbage()
|
||||
@@ -958,11 +964,11 @@ print'+'
|
||||
|
||||
|
||||
-- testing changing hooks during hooks
|
||||
_G.t = {}
|
||||
_G.TT = {}
|
||||
T.sethook([[
|
||||
# set a line hook after 3 count hooks
|
||||
sethook 4 0 '
|
||||
getglobal t;
|
||||
getglobal TT;
|
||||
pushvalue -3; append -2
|
||||
pushvalue -2; append -2
|
||||
']], "c", 3)
|
||||
@@ -972,12 +978,13 @@ a = 1 -- count hook (set line hook)
|
||||
a = 1 -- line hook
|
||||
a = 1 -- line hook
|
||||
debug.sethook()
|
||||
t = _G.t
|
||||
local t = _G.TT
|
||||
assert(t[1] == "line")
|
||||
line = t[2]
|
||||
local line = t[2]
|
||||
assert(t[3] == "line" and t[4] == line + 1)
|
||||
assert(t[5] == "line" and t[6] == line + 2)
|
||||
assert(t[7] == nil)
|
||||
_G.TT = nil
|
||||
|
||||
|
||||
-------------------------------------------------------------------------
|
||||
@@ -1002,6 +1009,7 @@ do -- testing errors during GC
|
||||
collectgarbage("restart")
|
||||
warn("@on")
|
||||
end
|
||||
_G.A = nil
|
||||
-------------------------------------------------------------------------
|
||||
-- test for userdata vals
|
||||
do
|
||||
@@ -1031,8 +1039,8 @@ assert(a == 'alo' and b == '3')
|
||||
|
||||
T.doremote(L1, "_ERRORMESSAGE = nil")
|
||||
-- error: `sin' is not defined
|
||||
a, _, b = T.doremote(L1, "return sin(1)")
|
||||
assert(a == nil and b == 2) -- 2 == run-time error
|
||||
a, b, c = T.doremote(L1, "return sin(1)")
|
||||
assert(a == nil and c == 2) -- 2 == run-time error
|
||||
|
||||
-- error: syntax error
|
||||
a, b, c = T.doremote(L1, "return a+")
|
||||
@@ -1125,22 +1133,49 @@ do
|
||||
assert(#openresource == n)
|
||||
end
|
||||
|
||||
-- closing resources with 'settop'
|
||||
-- closing resources with 'closeslot'
|
||||
_ENV.xxx = true
|
||||
local a = T.testC([[
|
||||
pushvalue 2
|
||||
call 0 1 # create resource
|
||||
pushvalue 2 # stack: S, NR, CH, NR
|
||||
call 0 1 # create resource; stack: S, NR, CH, R
|
||||
toclose -1 # mark it to be closed
|
||||
pushvalue 2
|
||||
call 0 1 # create another resource
|
||||
pushvalue 2 # stack: S, NR, CH, R, NR
|
||||
call 0 1 # create another resource; stack: S, NR, CH, R, R
|
||||
toclose -1 # mark it to be closed
|
||||
pushvalue 3
|
||||
pushvalue 3 # stack: S, NR, CH, R, R, CH
|
||||
pushint 2 # there should be two open resources
|
||||
call 1 0
|
||||
pop 1 # pop second resource from the stack
|
||||
pushvalue 3
|
||||
call 1 0 # stack: S, NR, CH, R, R
|
||||
closeslot -1 # close second resource
|
||||
pushvalue 3 # stack: S, NR, CH, R, R, CH
|
||||
pushint 1 # there should be one open resource
|
||||
call 1 0
|
||||
pop 1 # pop second resource from the stack
|
||||
call 1 0 # stack: S, NR, CH, R, R
|
||||
closeslot 4
|
||||
setglobal "xxx" # previous op. erased the slot
|
||||
pop 1 # pop other resource from the stack
|
||||
pushint *
|
||||
return 1 # return stack size
|
||||
]], newresource, check)
|
||||
assert(a == 3 and _ENV.xxx == nil) -- no extra items left in the stack
|
||||
|
||||
-- closing resources with 'pop'
|
||||
local a = T.testC([[
|
||||
pushvalue 2 # stack: S, NR, CH, NR
|
||||
call 0 1 # create resource; stack: S, NR, CH, R
|
||||
toclose -1 # mark it to be closed
|
||||
pushvalue 2 # stack: S, NR, CH, R, NR
|
||||
call 0 1 # create another resource; stack: S, NR, CH, R, R
|
||||
toclose -1 # mark it to be closed
|
||||
pushvalue 3 # stack: S, NR, CH, R, R, CH
|
||||
pushint 2 # there should be two open resources
|
||||
call 1 0 # stack: S, NR, CH, R, R
|
||||
pop 1 # pop second resource
|
||||
pushvalue 3 # stack: S, NR, CH, R, CH
|
||||
pushint 1 # there should be one open resource
|
||||
call 1 0 # stack: S, NR, CH, R
|
||||
pop 1 # pop other resource from the stack
|
||||
pushvalue 3 # stack: S, NR, CH, CH
|
||||
pushint 0 # there should be no open resources
|
||||
call 1 0 # stack: S, NR, CH
|
||||
pushint *
|
||||
return 1 # return stack size
|
||||
]], newresource, check)
|
||||
@@ -1176,7 +1211,7 @@ T.alloccount() -- remove limit
|
||||
-- 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)
|
||||
local function testbytes (s, f)
|
||||
collectgarbage()
|
||||
local M = T.totalmem()
|
||||
local oldM = M
|
||||
@@ -1201,7 +1236,7 @@ end
|
||||
-- task, until there is enough allocations to complete the task without
|
||||
-- errors.
|
||||
|
||||
function testalloc (s, f)
|
||||
local function testalloc (s, f)
|
||||
collectgarbage()
|
||||
local M = 0
|
||||
local a,b = nil
|
||||
@@ -1268,12 +1303,12 @@ end)
|
||||
-- testing threads
|
||||
|
||||
-- get main thread from registry (at index LUA_RIDX_MAINTHREAD == 1)
|
||||
mt = T.testC("rawgeti R 1; return 1")
|
||||
local mt = T.testC("rawgeti R 1; return 1")
|
||||
assert(type(mt) == "thread" and coroutine.running() == mt)
|
||||
|
||||
|
||||
|
||||
function expand (n,s)
|
||||
local function expand (n,s)
|
||||
if n==0 then return "" end
|
||||
local e = string.rep("=", n)
|
||||
return string.format("T.doonnewstack([%s[ %s;\n collectgarbage(); %s]%s])\n",
|
||||
@@ -1283,9 +1318,10 @@ end
|
||||
G=0; collectgarbage(); a =collectgarbage("count")
|
||||
load(expand(20,"G=G+1"))()
|
||||
assert(G==20); collectgarbage(); -- assert(gcinfo() <= a+1)
|
||||
G = nil
|
||||
|
||||
testamem("running code on new thread", function ()
|
||||
return T.doonnewstack("x=1") == 0 -- try to create thread
|
||||
return T.doonnewstack("local x=1") == 0 -- try to create thread
|
||||
end)
|
||||
|
||||
|
||||
@@ -1299,13 +1335,13 @@ end)
|
||||
local testprog = [[
|
||||
local function foo () return end
|
||||
local t = {"x"}
|
||||
a = "aaa"
|
||||
for i = 1, #t do a=a..t[i] end
|
||||
AA = "aaa"
|
||||
for i = 1, #t do AA = AA .. t[i] end
|
||||
return true
|
||||
]]
|
||||
|
||||
-- testing memory x dofile
|
||||
_G.a = nil
|
||||
_G.AA = nil
|
||||
local t =os.tmpname()
|
||||
local f = assert(io.open(t, "w"))
|
||||
f:write(testprog)
|
||||
@@ -1315,7 +1351,7 @@ testamem("dofile", function ()
|
||||
return a and a()
|
||||
end)
|
||||
assert(os.remove(t))
|
||||
assert(_G.a == "aaax")
|
||||
assert(_G.AA == "aaax")
|
||||
|
||||
|
||||
-- other generic tests
|
||||
@@ -1332,6 +1368,8 @@ testamem("dump/undump", function ()
|
||||
return a and a()
|
||||
end)
|
||||
|
||||
_G.AA = nil
|
||||
|
||||
local t = os.tmpname()
|
||||
testamem("file creation", function ()
|
||||
local f = assert(io.open(t, 'w'))
|
||||
@@ -1353,7 +1391,7 @@ testamem("constructors", function ()
|
||||
end)
|
||||
|
||||
local a = 1
|
||||
close = nil
|
||||
local close = nil
|
||||
testamem("closure creation", function ()
|
||||
function close (b)
|
||||
return function (x) return b + x end
|
||||
|
||||
@@ -85,7 +85,7 @@ local DIR = "libs" .. dirsep
|
||||
|
||||
-- prepend DIR to a name and correct directory separators
|
||||
local function D (x)
|
||||
x = string.gsub(x, "/", dirsep)
|
||||
local x = string.gsub(x, "/", dirsep)
|
||||
return DIR .. x
|
||||
end
|
||||
|
||||
@@ -106,7 +106,7 @@ local function createfiles (files, preextras, posextras)
|
||||
end
|
||||
end
|
||||
|
||||
function removefiles (files)
|
||||
local function removefiles (files)
|
||||
for n in pairs(files) do
|
||||
os.remove(D(n))
|
||||
end
|
||||
@@ -154,10 +154,9 @@ local try = function (p, n, r, ext)
|
||||
assert(ext == x)
|
||||
end
|
||||
|
||||
a = require"names"
|
||||
local a = require"names"
|
||||
assert(a[1] == "names" and a[2] == D"names.lua")
|
||||
|
||||
_G.a = nil
|
||||
local st, msg = pcall(require, "err")
|
||||
assert(not st and string.find(msg, "arithmetic") and B == 15)
|
||||
st, msg = pcall(require, "synerr")
|
||||
@@ -191,6 +190,7 @@ try("X", "XXxX", AA, "libs/XXxX")
|
||||
|
||||
|
||||
removefiles(files)
|
||||
NAME, REQUIRED, AA, B = nil
|
||||
|
||||
|
||||
-- testing require of sub-packages
|
||||
@@ -223,7 +223,7 @@ assert(require"P1" == m and m.AA == 10)
|
||||
|
||||
|
||||
removefiles(files)
|
||||
|
||||
AA = nil
|
||||
|
||||
package.path = ""
|
||||
assert(not pcall(require, "file_does_not_exist"))
|
||||
@@ -305,6 +305,7 @@ else
|
||||
assert(_ENV.x == "lib1.sub" and _ENV.y == DC"lib1")
|
||||
assert(string.find(ext, "libs/lib1", 1, true))
|
||||
assert(fs.id(45) == 45)
|
||||
_ENV.x, _ENV.y = nil
|
||||
end
|
||||
|
||||
_ENV = _G
|
||||
@@ -338,10 +339,10 @@ print("testing assignments, logical operators, and constructors")
|
||||
|
||||
local res, res2 = 27
|
||||
|
||||
a, b = 1, 2+3
|
||||
local a, b = 1, 2+3
|
||||
assert(a==1 and b==5)
|
||||
a={}
|
||||
function f() return 10, 11, 12 end
|
||||
local function f() return 10, 11, 12 end
|
||||
a.x, b, a[1] = 1, 2, f()
|
||||
assert(a.x==1 and b==2 and a[1]==10)
|
||||
a[f()], b, a[f()+3] = f(), a, 'x'
|
||||
@@ -353,15 +354,15 @@ do
|
||||
local a,b,c
|
||||
a,b = 0, f(1)
|
||||
assert(a == 0 and b == 1)
|
||||
A,b = 0, f(1)
|
||||
assert(A == 0 and b == 1)
|
||||
a,b = 0, f(1)
|
||||
assert(a == 0 and b == 1)
|
||||
a,b,c = 0,5,f(4)
|
||||
assert(a==0 and b==5 and c==1)
|
||||
a,b,c = 0,5,f(0)
|
||||
assert(a==0 and b==5 and c==nil)
|
||||
end
|
||||
|
||||
a, b, c, d = 1 and nil, 1 or nil, (1 and (nil or 1)), 6
|
||||
local a, b, c, d = 1 and nil, 1 or nil, (1 and (nil or 1)), 6
|
||||
assert(not a and b and c and d==6)
|
||||
|
||||
d = 20
|
||||
@@ -419,6 +420,7 @@ assert(not pcall(function () local a = {[nil] = 10} end))
|
||||
assert(a[nil] == undef)
|
||||
a = nil
|
||||
|
||||
local a, b, c
|
||||
a = {10,9,8,7,6,5,4,3,2; [-3]='a', [f]=print, a='a', b='ab'}
|
||||
a, a.x, a.y = a, a[-3]
|
||||
assert(a[1]==10 and a[-3]==a.a and a[f]==print and a.x=='a' and not a.y)
|
||||
@@ -434,6 +436,16 @@ a.aVeryLongName012345678901234567890123456789012345678901234567890123456789 ==
|
||||
10)
|
||||
|
||||
|
||||
do
|
||||
-- _ENV constant
|
||||
local function foo ()
|
||||
local _ENV <const> = 11
|
||||
X = "hi"
|
||||
end
|
||||
local st, msg = pcall(foo)
|
||||
assert(not st and string.find(msg, "number"))
|
||||
end
|
||||
|
||||
|
||||
-- test of large float/integer indices
|
||||
|
||||
@@ -445,7 +457,7 @@ while maxint ~= (maxint + 0.0) or (maxint - 1) ~= (maxint - 1.0) do
|
||||
maxint = maxint // 2
|
||||
end
|
||||
|
||||
maxintF = maxint + 0.0 -- float version
|
||||
local maxintF = maxint + 0.0 -- float version
|
||||
|
||||
assert(maxintF == maxint and math.type(maxintF) == "float" and
|
||||
maxintF >= 2.0^14)
|
||||
|
||||
@@ -32,7 +32,7 @@ setmetatable(env, {
|
||||
})
|
||||
|
||||
X = nil
|
||||
co = coroutine.wrap(f)
|
||||
local co = coroutine.wrap(f)
|
||||
assert(co() == 's')
|
||||
assert(co() == 'g')
|
||||
assert(co() == 'g')
|
||||
|
||||
@@ -38,6 +38,18 @@ d = d << 32
|
||||
assert(a | b ~ c & d == 0xF4000000 << 32)
|
||||
assert(~~a == a and ~a == -1 ~ a and -d == ~d + 1)
|
||||
|
||||
|
||||
do -- constant folding
|
||||
local code = string.format("return -1 >> %d", math.maxinteger)
|
||||
assert(load(code)() == 0)
|
||||
local code = string.format("return -1 >> %d", math.mininteger)
|
||||
assert(load(code)() == 0)
|
||||
local code = string.format("return -1 << %d", math.maxinteger)
|
||||
assert(load(code)() == 0)
|
||||
local code = string.format("return -1 << %d", math.mininteger)
|
||||
assert(load(code)() == 0)
|
||||
end
|
||||
|
||||
assert(-1 >> 1 == (1 << (numbits - 1)) - 1 and 1 << 31 == 0x80000000)
|
||||
assert(-1 >> (numbits - 1) == 1)
|
||||
assert(-1 >> numbits == 0 and
|
||||
@@ -45,6 +57,11 @@ assert(-1 >> numbits == 0 and
|
||||
-1 << numbits == 0 and
|
||||
-1 << -numbits == 0)
|
||||
|
||||
assert(1 >> math.mininteger == 0)
|
||||
assert(1 >> math.maxinteger == 0)
|
||||
assert(1 << math.mininteger == 0)
|
||||
assert(1 << math.maxinteger == 0)
|
||||
|
||||
assert((2^30 - 1) << 2^30 == 0)
|
||||
assert((2^30 - 1) >> 2^30 == 0)
|
||||
|
||||
|
||||
@@ -16,7 +16,7 @@ assert(type(nil) == 'nil'
|
||||
and type(type) == 'function')
|
||||
|
||||
assert(type(assert) == type(print))
|
||||
function f (x) return a:x (x) end
|
||||
local function f (x) return a:x (x) end
|
||||
assert(type(f) == 'function')
|
||||
assert(not pcall(type))
|
||||
|
||||
@@ -33,10 +33,11 @@ do
|
||||
assert(fact(5) == 120)
|
||||
end
|
||||
assert(fact == false)
|
||||
fact = nil
|
||||
|
||||
-- testing declarations
|
||||
a = {i = 10}
|
||||
self = 20
|
||||
local a = {i = 10}
|
||||
local self = 20
|
||||
function a:x (x) return x+self.i end
|
||||
function a.y (x) return x+self end
|
||||
|
||||
@@ -72,6 +73,8 @@ f(1,2, -- this one too
|
||||
3,4)
|
||||
assert(t[1] == 1 and t[2] == 2 and t[3] == 3 and t[4] == 'a')
|
||||
|
||||
t = nil -- delete 't'
|
||||
|
||||
function fat(x)
|
||||
if x <= 1 then return 1
|
||||
else return x*load("return fat(" .. x-1 .. ")", "")()
|
||||
@@ -80,26 +83,29 @@ end
|
||||
|
||||
assert(load "load 'assert(fat(6)==720)' () ")()
|
||||
a = load('return fat(5), 3')
|
||||
a,b = a()
|
||||
local a,b = a()
|
||||
assert(a == 120 and b == 3)
|
||||
fat = nil
|
||||
print('+')
|
||||
|
||||
function err_on_n (n)
|
||||
local function err_on_n (n)
|
||||
if n==0 then error(); exit(1);
|
||||
else err_on_n (n-1); exit(1);
|
||||
end
|
||||
end
|
||||
|
||||
do
|
||||
function dummy (n)
|
||||
local function dummy (n)
|
||||
if n > 0 then
|
||||
assert(not pcall(err_on_n, n))
|
||||
dummy(n-1)
|
||||
end
|
||||
end
|
||||
|
||||
dummy(10)
|
||||
end
|
||||
|
||||
dummy(10)
|
||||
_G.deep = nil -- "declaration" (used by 'all.lua')
|
||||
|
||||
function deep (n)
|
||||
if n>0 then deep(n-1) end
|
||||
@@ -151,6 +157,16 @@ do -- tail calls x varargs
|
||||
end
|
||||
|
||||
|
||||
do -- C-stack overflow while handling C-stack overflow
|
||||
local function loop ()
|
||||
assert(pcall(loop))
|
||||
end
|
||||
|
||||
local err, msg = xpcall(loop, loop)
|
||||
assert(not err and string.find(msg, "error"))
|
||||
end
|
||||
|
||||
|
||||
|
||||
do -- tail calls x chain of __call
|
||||
local n = 10000 -- depth
|
||||
@@ -199,7 +215,7 @@ assert(a == 23 and (function (x) return x*2 end)(20) == 40)
|
||||
-- testing closures
|
||||
|
||||
-- fixed-point operator
|
||||
Z = function (le)
|
||||
local Z = function (le)
|
||||
local function a (f)
|
||||
return le(function (x) return f(f)(x) end)
|
||||
end
|
||||
@@ -209,14 +225,14 @@ Z = function (le)
|
||||
|
||||
-- non-recursive factorial
|
||||
|
||||
F = function (f)
|
||||
local F = function (f)
|
||||
return function (n)
|
||||
if n == 0 then return 1
|
||||
else return n*f(n-1) end
|
||||
end
|
||||
end
|
||||
|
||||
fat = Z(F)
|
||||
local fat = Z(F)
|
||||
|
||||
assert(fat(0) == 1 and fat(4) == 24 and Z(F)(5)==5*Z(F)(4))
|
||||
|
||||
@@ -227,22 +243,21 @@ local function g (z)
|
||||
return f(z,z+1,z+2,z+3)
|
||||
end
|
||||
|
||||
f = g(10)
|
||||
local f = g(10)
|
||||
assert(f(9, 16) == 10+11+12+13+10+9+16+10)
|
||||
|
||||
Z, F, f = nil
|
||||
print('+')
|
||||
|
||||
-- testing multiple returns
|
||||
|
||||
function unlpack (t, i)
|
||||
local function unlpack (t, i)
|
||||
i = i or 1
|
||||
if (i <= #t) then
|
||||
return t[i], unlpack(t, i+1)
|
||||
end
|
||||
end
|
||||
|
||||
function equaltab (t1, t2)
|
||||
local function equaltab (t1, t2)
|
||||
assert(#t1 == #t2)
|
||||
for i = 1, #t1 do
|
||||
assert(t1[i] == t2[i])
|
||||
@@ -251,8 +266,8 @@ end
|
||||
|
||||
local pack = function (...) return (table.pack(...)) end
|
||||
|
||||
function f() return 1,2,30,4 end
|
||||
function ret2 (a,b) return a,b end
|
||||
local function f() return 1,2,30,4 end
|
||||
local function ret2 (a,b) return a,b end
|
||||
|
||||
local a,b,c,d = unlpack{1,2,3}
|
||||
assert(a==1 and b==2 and c==3 and d==nil)
|
||||
@@ -281,7 +296,7 @@ table.sort({10,9,8,4,19,23,0,0}, function (a,b) return a<b end, "extra arg")
|
||||
local x = "-- a comment\0\0\0\n x = 10 + \n23; \
|
||||
local a = function () x = 'hi' end; \
|
||||
return '\0'"
|
||||
function read1 (x)
|
||||
local function read1 (x)
|
||||
local i = 0
|
||||
return function ()
|
||||
collectgarbage()
|
||||
@@ -290,7 +305,7 @@ function read1 (x)
|
||||
end
|
||||
end
|
||||
|
||||
function cannotload (msg, a,b)
|
||||
local function cannotload (msg, a,b)
|
||||
assert(not a and string.find(b, msg))
|
||||
end
|
||||
|
||||
@@ -327,11 +342,26 @@ do -- another bug (in 5.4.0)
|
||||
end
|
||||
|
||||
|
||||
do -- another bug (since 5.2)
|
||||
-- corrupted binary dump: list of upvalue names is larger than number
|
||||
-- of upvalues, overflowing the array of upvalues.
|
||||
local code =
|
||||
"\x1b\x4c\x75\x61\x54\x00\x19\x93\x0d\x0a\x1a\x0a\x04\x08\x08\x78\x56\z
|
||||
\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x28\x77\x40\x00\x86\x40\z
|
||||
\x74\x65\x6d\x70\x81\x81\x01\x00\x02\x82\x48\x00\x02\x00\xc7\x00\x01\z
|
||||
\x00\x80\x80\x80\x82\x00\x00\x80\x81\x82\x78\x80\x82\x81\x86\x40\x74\z
|
||||
\x65\x6d\x70"
|
||||
|
||||
assert(load(code)) -- segfaults in previous versions
|
||||
end
|
||||
|
||||
|
||||
x = string.dump(load("x = 1; return x"))
|
||||
a = assert(load(read1(x), nil, "b"))
|
||||
assert(a() == 1 and _G.x == 1)
|
||||
cannotload("attempt to load a binary chunk", load(read1(x), nil, "t"))
|
||||
cannotload("attempt to load a binary chunk", load(x, nil, "t"))
|
||||
_G.x = nil
|
||||
|
||||
assert(not pcall(string.dump, print)) -- no dump of C functions
|
||||
|
||||
@@ -356,7 +386,7 @@ debug.setupvalue(x, 2, _G)
|
||||
assert(x() == 123)
|
||||
|
||||
assert(assert(load("return XX + ...", nil, nil, {XX = 13}))(4) == 17)
|
||||
|
||||
XX = nil
|
||||
|
||||
-- test generic load with nested functions
|
||||
x = [[
|
||||
|
||||
@@ -4,7 +4,7 @@
|
||||
print "testing closures"
|
||||
|
||||
local A,B = 0,{g=10}
|
||||
function f(x)
|
||||
local function f(x)
|
||||
local a = {}
|
||||
for i=1,1000 do
|
||||
local y = 0
|
||||
@@ -89,6 +89,7 @@ assert(r == "a" and s == "b")
|
||||
|
||||
|
||||
-- testing closures with 'for' control variable x break
|
||||
local f
|
||||
for i=1,3 do
|
||||
f = function () return i end
|
||||
break
|
||||
@@ -139,7 +140,7 @@ assert(b('get') == 'xuxu')
|
||||
b('set', 10); assert(b('get') == 14)
|
||||
|
||||
|
||||
local w
|
||||
local y, w
|
||||
-- testing multi-level closure
|
||||
function f(x)
|
||||
return function (y)
|
||||
@@ -230,6 +231,7 @@ t()
|
||||
-- test for debug manipulation of upvalues
|
||||
local debug = require'debug'
|
||||
|
||||
local foo1, foo2, foo3
|
||||
do
|
||||
local a , b, c = 3, 5, 7
|
||||
foo1 = function () return a+b end;
|
||||
@@ -242,7 +244,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))
|
||||
|
||||
@@ -55,10 +55,38 @@ end
|
||||
checkKlist(foo, {3.78/4, -3.78/4, -3.79/4})
|
||||
|
||||
|
||||
foo = function (f, a)
|
||||
f(100 * 1000)
|
||||
f(100.0 * 1000)
|
||||
f(-100 * 1000)
|
||||
f(-100 * 1000.0)
|
||||
f(100000)
|
||||
f(100000.0)
|
||||
f(-100000)
|
||||
f(-100000.0)
|
||||
end
|
||||
|
||||
checkKlist(foo, {100000, 100000.0, -100000, -100000.0})
|
||||
|
||||
|
||||
-- floats x integers
|
||||
foo = function (t, a)
|
||||
t[a] = 1; t[a] = 1.0
|
||||
t[a] = 1; t[a] = 1.0
|
||||
t[a] = 2; t[a] = 2.0
|
||||
t[a] = 0; t[a] = 0.0
|
||||
t[a] = 1; t[a] = 1.0
|
||||
t[a] = 2; t[a] = 2.0
|
||||
t[a] = 0; t[a] = 0.0
|
||||
end
|
||||
|
||||
checkKlist(foo, {1, 1.0, 2, 2.0, 0, 0.0})
|
||||
|
||||
|
||||
-- testing opcodes
|
||||
|
||||
-- check that 'f' opcodes match '...'
|
||||
function check (f, ...)
|
||||
local function check (f, ...)
|
||||
local arg = {...}
|
||||
local c = T.listcode(f)
|
||||
for i=1, #arg do
|
||||
@@ -71,7 +99,7 @@ end
|
||||
|
||||
|
||||
-- check that 'f' opcodes match '...' and that 'f(p) == r'.
|
||||
function checkR (f, p, r, ...)
|
||||
local function checkR (f, p, r, ...)
|
||||
local r1 = f(p)
|
||||
assert(r == r1 and math.type(r) == math.type(r1))
|
||||
check(f, ...)
|
||||
@@ -79,7 +107,7 @@ end
|
||||
|
||||
|
||||
-- check that 'a' and 'b' has the same opcodes
|
||||
function checkequal (a, b)
|
||||
local function checkequal (a, b)
|
||||
a = T.listcode(a)
|
||||
b = T.listcode(b)
|
||||
assert(#a == #b)
|
||||
@@ -392,28 +420,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)
|
||||
|
||||
|
||||
@@ -11,6 +11,7 @@ local function checkload (s, msg)
|
||||
end
|
||||
|
||||
-- testing semicollons
|
||||
local a
|
||||
do ;;; end
|
||||
; do ; a = 3; assert(a == 3) end;
|
||||
;
|
||||
@@ -49,10 +50,10 @@ assert((((nil and true) or false) and true) == false)
|
||||
|
||||
local a,b = 1,nil;
|
||||
assert(-(1 or 2) == -1 and (1 and 2)+(-1.25 or -4) == 0.75);
|
||||
x = ((b or a)+1 == 2 and (10 or a)+1 == 11); assert(x);
|
||||
local x = ((b or a)+1 == 2 and (10 or a)+1 == 11); assert(x);
|
||||
x = (((2<3) or 1) == true and (2<3 and 4) == 4); assert(x);
|
||||
|
||||
x,y=1,2;
|
||||
local x, y = 1, 2;
|
||||
assert((x>y) and x or y == 2);
|
||||
x,y=2,1;
|
||||
assert((x>y) and x or y == 2);
|
||||
@@ -77,13 +78,13 @@ do -- testing operators with diffent kinds of constants
|
||||
local gab = f(o1, o2)
|
||||
|
||||
_ENV.XX = o1
|
||||
code = string.format("return XX %s %s", op, o2)
|
||||
res = assert(load(code))()
|
||||
local code = string.format("return XX %s %s", op, o2)
|
||||
local res = assert(load(code))()
|
||||
assert(res == gab)
|
||||
|
||||
_ENV.XX = o2
|
||||
local code = string.format("return (%s) %s XX", o1, op)
|
||||
local res = assert(load(code))()
|
||||
code = string.format("return (%s) %s XX", o1, op)
|
||||
res = assert(load(code))()
|
||||
assert(res == gab)
|
||||
|
||||
code = string.format("return (%s) %s %s", o1, op, o2)
|
||||
@@ -92,6 +93,7 @@ do -- testing operators with diffent kinds of constants
|
||||
end
|
||||
end
|
||||
end
|
||||
_ENV.XX = nil
|
||||
end
|
||||
|
||||
|
||||
@@ -100,10 +102,35 @@ repeat until 1; repeat until true;
|
||||
while false do end; while nil do end;
|
||||
|
||||
do -- test old bug (first name could not be an `upvalue')
|
||||
local a; function f(x) x={a=1}; x={x=1}; x={G=1} end
|
||||
local a; local function f(x) x={a=1}; x={x=1}; x={G=1} end
|
||||
end
|
||||
|
||||
function f (i)
|
||||
|
||||
do -- bug since 5.4.0
|
||||
-- create code with a table using more than 256 constants
|
||||
local code = {"local x = {"}
|
||||
for i = 1, 257 do
|
||||
code[#code + 1] = i .. ".1,"
|
||||
end
|
||||
code[#code + 1] = "};"
|
||||
code = table.concat(code)
|
||||
|
||||
-- add "ret" to the end of that code and checks that
|
||||
-- it produces the expected value "val"
|
||||
local function check (ret, val)
|
||||
local code = code .. ret
|
||||
code = load(code)
|
||||
assert(code() == val)
|
||||
end
|
||||
|
||||
check("return (1 ~ (2 or 3))", 1 ~ 2)
|
||||
check("return (1 | (2 or 3))", 1 | 2)
|
||||
check("return (1 + (2 or 3))", 1 + 2)
|
||||
check("return (1 << (2 or 3))", 1 << 2)
|
||||
end
|
||||
|
||||
|
||||
local function f (i)
|
||||
if type(i) ~= 'number' then return i,'jojo'; end;
|
||||
if i > 0 then return i, f(i-1); end;
|
||||
end
|
||||
@@ -129,10 +156,10 @@ end
|
||||
assert(f(3) == 'a' and f(12) == 'b' and f(26) == 'c' and f(100) == nil)
|
||||
|
||||
for i=1,1000 do break; end;
|
||||
n=100;
|
||||
i=3;
|
||||
t = {};
|
||||
a=nil
|
||||
local n=100;
|
||||
local i=3;
|
||||
local t = {};
|
||||
local a=nil
|
||||
while not a do
|
||||
a=0; for i=1,n do for i=i,1,-1 do a=a+1; t[i]=1; end; end;
|
||||
end
|
||||
@@ -175,14 +202,14 @@ a={y=1}
|
||||
x = {a.y}
|
||||
assert(x[1] == 1)
|
||||
|
||||
function f(i)
|
||||
local function f (i)
|
||||
while 1 do
|
||||
if i>0 then i=i-1;
|
||||
else return; end;
|
||||
end;
|
||||
end;
|
||||
|
||||
function g(i)
|
||||
local function g(i)
|
||||
while 1 do
|
||||
if i>0 then i=i-1
|
||||
else return end
|
||||
@@ -247,7 +274,7 @@ function g (a,b,c,d,e)
|
||||
if not (a>=b or c or d and e or nil) then return 0; else return 1; end;
|
||||
end
|
||||
|
||||
function h (a,b,c,d,e)
|
||||
local function h (a,b,c,d,e)
|
||||
while (a>=b or c or (d and e) or nil) do return 1; end;
|
||||
return 0;
|
||||
end;
|
||||
@@ -275,7 +302,7 @@ do
|
||||
assert(a==2)
|
||||
end
|
||||
|
||||
function F(a)
|
||||
local function F (a)
|
||||
assert(debug.getinfo(1, "n").name == 'F')
|
||||
return a,2,3
|
||||
end
|
||||
@@ -368,6 +395,8 @@ for n = 1, level do
|
||||
if i % 60000 == 0 then print('+') end
|
||||
end
|
||||
end
|
||||
IX = nil
|
||||
_G.GLOB1 = nil
|
||||
------------------------------------------------------------------
|
||||
|
||||
-- testing some syntax errors (chosen through 'gcov')
|
||||
|
||||
@@ -30,7 +30,8 @@ local function eqtab (t1, t2)
|
||||
end
|
||||
|
||||
_G.x = nil -- declare x
|
||||
function foo (a, ...)
|
||||
_G.f = nil -- declare f
|
||||
local function foo (a, ...)
|
||||
local x, y = coroutine.running()
|
||||
assert(x == f and y == false)
|
||||
-- next call should not corrupt coroutine (but must fail,
|
||||
@@ -67,10 +68,11 @@ assert(coroutine.status(f) == "dead")
|
||||
s, a = coroutine.resume(f, "xuxu")
|
||||
assert(not s and string.find(a, "dead") and coroutine.status(f) == "dead")
|
||||
|
||||
_G.f = nil
|
||||
|
||||
-- yields in tail calls
|
||||
local function foo (i) return coroutine.yield(i) end
|
||||
f = coroutine.wrap(function ()
|
||||
local f = coroutine.wrap(function ()
|
||||
for i=1,10 do
|
||||
assert(foo(i) == _G.x)
|
||||
end
|
||||
@@ -79,8 +81,10 @@ end)
|
||||
for i=1,10 do _G.x = i; assert(f(i) == i) end
|
||||
_G.x = 'xuxu'; assert(f('xuxu') == 'a')
|
||||
|
||||
_G.x = nil
|
||||
|
||||
-- recursive
|
||||
function pf (n, i)
|
||||
local function pf (n, i)
|
||||
coroutine.yield(n)
|
||||
pf(n*i, i+1)
|
||||
end
|
||||
@@ -93,14 +97,14 @@ for i=1,10 do
|
||||
end
|
||||
|
||||
-- sieve
|
||||
function gen (n)
|
||||
local function gen (n)
|
||||
return coroutine.wrap(function ()
|
||||
for i=2,n do coroutine.yield(i) end
|
||||
end)
|
||||
end
|
||||
|
||||
|
||||
function filter (p, g)
|
||||
local function filter (p, g)
|
||||
return coroutine.wrap(function ()
|
||||
while 1 do
|
||||
local n = g()
|
||||
@@ -123,13 +127,23 @@ assert(#a == 22 and a[#a] == 79)
|
||||
x, a = nil
|
||||
|
||||
|
||||
-- coroutine closing
|
||||
print("to-be-closed variables in coroutines")
|
||||
|
||||
local function func2close (f)
|
||||
return setmetatable({}, {__close = f})
|
||||
end
|
||||
|
||||
do
|
||||
-- ok to close a dead coroutine
|
||||
local co = coroutine.create(print)
|
||||
assert(coroutine.resume(co, "testing 'coroutine.close'"))
|
||||
assert(coroutine.status(co) == "dead")
|
||||
assert(coroutine.close(co))
|
||||
local st, msg = coroutine.close(co)
|
||||
assert(st and msg == nil)
|
||||
-- also ok to close it again
|
||||
st, msg = coroutine.close(co)
|
||||
assert(st and msg == nil)
|
||||
|
||||
|
||||
-- cannot close the running coroutine
|
||||
local st, msg = pcall(coroutine.close, coroutine.running())
|
||||
@@ -143,12 +157,34 @@ do
|
||||
assert(not st and string.find(msg, "normal"))
|
||||
end))()
|
||||
|
||||
-- cannot close a coroutine while closing it
|
||||
do
|
||||
local co
|
||||
co = coroutine.create(
|
||||
function()
|
||||
local x <close> = func2close(function()
|
||||
coroutine.close(co) -- try to close it again
|
||||
end)
|
||||
coroutine.yield(20)
|
||||
end)
|
||||
local st, msg = coroutine.resume(co)
|
||||
assert(st and msg == 20)
|
||||
st, msg = coroutine.close(co)
|
||||
assert(not st and string.find(msg, "running coroutine"))
|
||||
end
|
||||
|
||||
-- to-be-closed variables in coroutines
|
||||
local X
|
||||
|
||||
local function func2close (f)
|
||||
return setmetatable({}, {__close = f})
|
||||
end
|
||||
-- closing a coroutine after an error
|
||||
local co = coroutine.create(error)
|
||||
local st, msg = coroutine.resume(co, 100)
|
||||
assert(not st and msg == 100)
|
||||
st, msg = coroutine.close(co)
|
||||
assert(not st and msg == 100)
|
||||
-- after closing, no more errors
|
||||
st, msg = coroutine.close(co)
|
||||
assert(st and msg == nil)
|
||||
|
||||
co = coroutine.create(function ()
|
||||
local x <close> = func2close(function (self, err)
|
||||
@@ -163,13 +199,12 @@ do
|
||||
assert(not X and coroutine.status(co) == "dead")
|
||||
|
||||
-- error closing a coroutine
|
||||
warn("@on")
|
||||
local x = 0
|
||||
co = coroutine.create(function()
|
||||
local y <close> = func2close(function (self,err)
|
||||
if (err ~= 111) then os.exit(false) end -- should not happen
|
||||
assert(err == 111)
|
||||
x = 200
|
||||
error("200")
|
||||
error(200)
|
||||
end)
|
||||
local x <close> = func2close(function (self, err)
|
||||
assert(err == nil); error(111)
|
||||
@@ -178,24 +213,73 @@ do
|
||||
end)
|
||||
coroutine.resume(co)
|
||||
assert(x == 0)
|
||||
-- with test library, use 'store' mode to check warnings
|
||||
warn(not T and "@off" or "@store")
|
||||
local st, msg = coroutine.close(co)
|
||||
if not T then
|
||||
warn("@on")
|
||||
else -- test library
|
||||
assert(string.find(_WARN, "200")); _WARN = false
|
||||
warn("@normal")
|
||||
end
|
||||
assert(st == false and coroutine.status(co) == "dead" and msg == 111)
|
||||
assert(st == false and coroutine.status(co) == "dead" and msg == 200)
|
||||
assert(x == 200)
|
||||
-- after closing, no more errors
|
||||
st, msg = coroutine.close(co)
|
||||
assert(st and msg == nil)
|
||||
end
|
||||
|
||||
do
|
||||
-- <close> versus pcall in coroutines
|
||||
local X = false
|
||||
local Y = false
|
||||
local function foo ()
|
||||
local x <close> = func2close(function (self, err)
|
||||
Y = debug.getinfo(2)
|
||||
X = err
|
||||
end)
|
||||
error(43)
|
||||
end
|
||||
local 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.what == "C")
|
||||
|
||||
-- recovering from errors in __close metamethods
|
||||
local track = {}
|
||||
|
||||
local function h (o)
|
||||
local hv <close> = o
|
||||
return 1
|
||||
end
|
||||
|
||||
local function foo ()
|
||||
local x <close> = func2close(function(_,msg)
|
||||
track[#track + 1] = msg or false
|
||||
error(20)
|
||||
end)
|
||||
local y <close> = func2close(function(_,msg)
|
||||
track[#track + 1] = msg or false
|
||||
return 1000
|
||||
end)
|
||||
local z <close> = func2close(function(_,msg)
|
||||
track[#track + 1] = msg or false
|
||||
error(10)
|
||||
end)
|
||||
coroutine.yield(1)
|
||||
h(func2close(function(_,msg)
|
||||
track[#track + 1] = msg or false
|
||||
error(2)
|
||||
end))
|
||||
end
|
||||
|
||||
local co = coroutine.create(pcall)
|
||||
|
||||
local st, res = coroutine.resume(co, foo) -- call 'foo' protected
|
||||
assert(st and res == 1) -- yield 1
|
||||
local st, res1, res2 = coroutine.resume(co) -- continue
|
||||
assert(coroutine.status(co) == "dead")
|
||||
assert(st and not res1 and res2 == 20) -- last error (20)
|
||||
assert(track[1] == false and track[2] == 2 and track[3] == 10 and
|
||||
track[4] == 10)
|
||||
end
|
||||
|
||||
|
||||
-- yielding across C boundaries
|
||||
|
||||
co = coroutine.wrap(function()
|
||||
local co = coroutine.wrap(function()
|
||||
assert(not pcall(table.sort,{1,2,3}, coroutine.yield))
|
||||
assert(coroutine.isyieldable())
|
||||
coroutine.yield(20)
|
||||
@@ -223,15 +307,15 @@ local r1, r2, v = f1(nil)
|
||||
assert(r1 and not r2 and v[1] == (10 + 1)*10/2)
|
||||
|
||||
|
||||
function f (a, b) a = coroutine.yield(a); error{a + b} end
|
||||
function g(x) return x[1]*2 end
|
||||
local function f (a, b) a = coroutine.yield(a); error{a + b} end
|
||||
local function g(x) return x[1]*2 end
|
||||
|
||||
co = coroutine.wrap(function ()
|
||||
coroutine.yield(xpcall(f, g, 10, 20))
|
||||
end)
|
||||
|
||||
assert(co() == 10)
|
||||
r, msg = co(100)
|
||||
local r, msg = co(100)
|
||||
assert(not r and msg == 240)
|
||||
|
||||
|
||||
@@ -293,9 +377,10 @@ assert(not a and b == foo and coroutine.status(x) == "dead")
|
||||
a,b = coroutine.resume(x)
|
||||
assert(not a and string.find(b, "dead") and coroutine.status(x) == "dead")
|
||||
|
||||
goo = nil
|
||||
|
||||
-- co-routines x for loop
|
||||
function all (a, n, k)
|
||||
local function all (a, n, k)
|
||||
if k == 0 then coroutine.yield(a)
|
||||
else
|
||||
for i=1,n do
|
||||
@@ -335,7 +420,7 @@ assert(f() == 43 and f() == 53)
|
||||
|
||||
-- old bug: attempt to resume itself
|
||||
|
||||
function co_func (current_co)
|
||||
local function co_func (current_co)
|
||||
assert(coroutine.running() == current_co)
|
||||
assert(coroutine.resume(current_co) == false)
|
||||
coroutine.yield(10, 20)
|
||||
@@ -365,7 +450,7 @@ do
|
||||
|
||||
local X = false
|
||||
A = coroutine.wrap(function()
|
||||
local _ <close> = setmetatable({}, {__close = function () X = true end})
|
||||
local _ <close> = func2close(function () X = true end)
|
||||
return pcall(A, 1)
|
||||
end)
|
||||
st, res = A()
|
||||
@@ -373,6 +458,22 @@ do
|
||||
end
|
||||
|
||||
|
||||
-- bug in 5.4.1
|
||||
do
|
||||
-- coroutine ran close metamethods with invalid status during a
|
||||
-- reset.
|
||||
local co
|
||||
co = coroutine.wrap(function()
|
||||
local x <close> = func2close(function() return pcall(co) end)
|
||||
error(111)
|
||||
end)
|
||||
local st, errobj = pcall(co)
|
||||
assert(not st and errobj == 111)
|
||||
st, errobj = pcall(co)
|
||||
assert(not st and string.find(errobj, "dead coroutine"))
|
||||
end
|
||||
|
||||
|
||||
-- attempt to resume 'normal' coroutine
|
||||
local co1, co2
|
||||
co1 = coroutine.create(function () return co2() end)
|
||||
@@ -395,15 +496,16 @@ a = nil
|
||||
-- access to locals of erroneous coroutines
|
||||
local x = coroutine.create (function ()
|
||||
local a = 10
|
||||
_G.f = function () a=a+1; return a end
|
||||
_G.F = function () a=a+1; return a end
|
||||
error('x')
|
||||
end)
|
||||
|
||||
assert(not coroutine.resume(x))
|
||||
-- overwrite previous position of local `a'
|
||||
assert(not coroutine.resume(x, 1, 1, 1, 1, 1, 1, 1))
|
||||
assert(_G.f() == 11)
|
||||
assert(_G.f() == 12)
|
||||
assert(_G.F() == 11)
|
||||
assert(_G.F() == 12)
|
||||
_G.F = nil
|
||||
|
||||
|
||||
if not T then
|
||||
@@ -414,7 +516,7 @@ else
|
||||
|
||||
local turn
|
||||
|
||||
function fact (t, x)
|
||||
local function fact (t, x)
|
||||
assert(turn == t)
|
||||
if x == 0 then return 1
|
||||
else return x*fact(t, x-1)
|
||||
@@ -444,6 +546,28 @@ else
|
||||
|
||||
assert(B // A == 7) -- fact(7) // fact(6)
|
||||
|
||||
do -- hooks vs. multiple values
|
||||
local done
|
||||
local function test (n)
|
||||
done = false
|
||||
return coroutine.wrap(function ()
|
||||
local a = {}
|
||||
for i = 1, n do a[i] = i end
|
||||
-- 'pushint' just to perturb the stack
|
||||
T.sethook("pushint 10; yield 0", "", 1) -- yield at each op.
|
||||
local a1 = {table.unpack(a)} -- must keep top between ops.
|
||||
assert(#a1 == n)
|
||||
for i = 1, n do assert(a[i] == i) end
|
||||
done = true
|
||||
end)
|
||||
end
|
||||
-- arguments to the coroutine are just to perturb its stack
|
||||
local co = test(0); while not done do co(30) end
|
||||
co = test(1); while not done do co(20, 10) end
|
||||
co = test(3); while not done do co() end
|
||||
co = test(100); while not done do co() end
|
||||
end
|
||||
|
||||
local line = debug.getinfo(1, "l").currentline + 2 -- get line number
|
||||
local function foo ()
|
||||
local x = 10 --<< this line is 'line'
|
||||
@@ -461,6 +585,7 @@ else
|
||||
_G.X = nil; co(); assert(_G.X == line + 2 and _G.XX == nil)
|
||||
_G.X = nil; co(); assert(_G.X == line + 3 and _G.XX == 20)
|
||||
assert(co() == 10)
|
||||
_G.X = nil
|
||||
|
||||
-- testing yields in count hook
|
||||
co = coroutine.wrap(function ()
|
||||
@@ -538,6 +663,8 @@ else
|
||||
|
||||
assert(X == 'a a a' and Y == 'OK')
|
||||
|
||||
X, Y = nil
|
||||
|
||||
|
||||
-- resuming running coroutine
|
||||
C = coroutine.create(function ()
|
||||
@@ -583,7 +710,7 @@ else
|
||||
X = function (x) coroutine.yield(x, 'BB'); return 'CC' end;
|
||||
return 'ok']]))
|
||||
|
||||
t = table.pack(T.testC(state, [[
|
||||
local t = table.pack(T.testC(state, [[
|
||||
rawgeti R 1 # get main thread
|
||||
pushstring 'XX'
|
||||
getglobal X # get function for body
|
||||
@@ -612,31 +739,28 @@ end
|
||||
|
||||
|
||||
-- leaving a pending coroutine open
|
||||
_X = coroutine.wrap(function ()
|
||||
_G.TO_SURVIVE = coroutine.wrap(function ()
|
||||
local a = 10
|
||||
local x = function () a = a+1 end
|
||||
coroutine.yield()
|
||||
end)
|
||||
|
||||
_X()
|
||||
_G.TO_SURVIVE()
|
||||
|
||||
|
||||
if not _soft then
|
||||
-- bug (stack overflow)
|
||||
local j = 2^9
|
||||
local lim = 1000000 -- (C stack limit; assume 32-bit machine)
|
||||
local t = {lim - 10, lim - 5, lim - 1, lim, lim + 1}
|
||||
local lim = 1000000 -- stack limit; assume 32-bit machine
|
||||
local t = {lim - 10, lim - 5, lim - 1, lim, lim + 1, lim + 5}
|
||||
for i = 1, #t do
|
||||
local j = t[i]
|
||||
co = coroutine.create(function()
|
||||
local t = {}
|
||||
for i = 1, j do t[i] = i end
|
||||
return table.unpack(t)
|
||||
local co = coroutine.create(function()
|
||||
return table.unpack({}, 1, j)
|
||||
end)
|
||||
local r, msg = coroutine.resume(co)
|
||||
assert(not r)
|
||||
-- must fail for unpacking larger than stack limit
|
||||
assert(j < lim or not r)
|
||||
end
|
||||
co = nil
|
||||
end
|
||||
|
||||
|
||||
@@ -820,7 +944,7 @@ assert(run(function ()
|
||||
do local _ENV = _ENV
|
||||
f = function () AAA = BBB + 1; return AAA end
|
||||
end
|
||||
g = new(10); g.k.BBB = 10;
|
||||
local g = new(10); g.k.BBB = 10;
|
||||
debug.setupvalue(f, 1, g)
|
||||
assert(run(f, {"idx", "nidx", "idx"}) == 11)
|
||||
assert(g.k.AAA == 11)
|
||||
@@ -960,6 +1084,8 @@ assert(#a == 3 and a[1] == a[2] and a[2] == a[3] and a[3] == 34)
|
||||
|
||||
-- testing yields with continuations
|
||||
|
||||
local y
|
||||
|
||||
co = coroutine.wrap(function (...) return
|
||||
T.testC([[ # initial function
|
||||
yieldk 1 2
|
||||
@@ -1012,6 +1138,9 @@ assert(x == "YIELD" and y == 4)
|
||||
|
||||
assert(not pcall(co)) -- coroutine should be dead
|
||||
|
||||
_G.ctx = nil
|
||||
_G.status = nil
|
||||
|
||||
|
||||
-- bug in nCcalls
|
||||
local co = coroutine.wrap(function ()
|
||||
|
||||
@@ -1,73 +1,33 @@
|
||||
-- $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 crashes, see its file ('cstack.lua')"
|
||||
local tracegc = require"tracegc"
|
||||
|
||||
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.
|
||||
|
||||
|
||||
-- get and print original limit
|
||||
local origlimit <const> = debug.setcstacklimit(400)
|
||||
print("default stack limit: " .. origlimit)
|
||||
|
||||
|
||||
-- Do the tests using the original limit. Or else you may want to change
|
||||
-- 'currentlimit' to lower values to avoid a seg. fault or to higher
|
||||
-- values to check whether they are reliable.
|
||||
local currentlimit <const> = origlimit
|
||||
debug.setcstacklimit(currentlimit)
|
||||
print("current stack limit: " .. currentlimit)
|
||||
|
||||
|
||||
local function checkerror (msg, f, ...)
|
||||
local s, err = pcall(f, ...)
|
||||
assert(not s and string.find(err, msg))
|
||||
end
|
||||
|
||||
-- auxiliary function to keep 'count' on the screen even if the program
|
||||
-- crashes.
|
||||
local count
|
||||
local back = string.rep("\b", 8)
|
||||
local function progress ()
|
||||
count = count + 1
|
||||
local n = string.format("%-8d", count)
|
||||
io.stderr:write(back, n) -- erase previous value and write new one
|
||||
end
|
||||
|
||||
|
||||
do print("testing simple recursion:")
|
||||
count = 0
|
||||
local function foo ()
|
||||
progress()
|
||||
foo() -- do recursive calls until a stack error (or crash)
|
||||
end
|
||||
checkerror("stack overflow", foo)
|
||||
print("\tfinal count: ", count)
|
||||
end
|
||||
|
||||
|
||||
do print("testing stack overflow in message handling")
|
||||
count = 0
|
||||
local count = 0
|
||||
local function loop (x, y, z)
|
||||
progress()
|
||||
count = count + 1
|
||||
return 1 + loop(x, y, z)
|
||||
end
|
||||
tracegc.stop() -- __gc should not be called with a full stack
|
||||
local res, msg = xpcall(loop, loop)
|
||||
tracegc.start()
|
||||
assert(msg == "error in error handling")
|
||||
print("\tfinal count: ", count)
|
||||
print("final count: ", count)
|
||||
end
|
||||
|
||||
|
||||
@@ -80,97 +40,158 @@ 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 -- bug in 5.4.0
|
||||
print("testing limits in coroutines inside deep calls")
|
||||
count = 0
|
||||
local count = 0
|
||||
local lim = 1000
|
||||
local function stack (n)
|
||||
progress()
|
||||
if n > 0 then return stack(n - 1) + 1
|
||||
else coroutine.wrap(function ()
|
||||
count = count + 1
|
||||
stack(lim)
|
||||
end)()
|
||||
end
|
||||
end
|
||||
|
||||
print(xpcall(stack, function () return "ok" end, lim))
|
||||
local st, msg = xpcall(stack, function () return "ok" end, lim)
|
||||
assert(not st and msg == "ok")
|
||||
print("final count: ", count)
|
||||
end
|
||||
|
||||
|
||||
do print("testing changes in C-stack limit")
|
||||
do -- bug since 5.4.0
|
||||
local count = 0
|
||||
print("chain of 'coroutine.close'")
|
||||
-- create N coroutines forming a list so that each one, when closed,
|
||||
-- closes the previous one. (With a large enough N, previous Lua
|
||||
-- versions crash in this test.)
|
||||
local coro = false
|
||||
for i = 1, 1000 do
|
||||
local previous = coro
|
||||
coro = coroutine.create(function()
|
||||
local cc <close> = setmetatable({}, {__close=function()
|
||||
count = count + 1
|
||||
if previous then
|
||||
assert(coroutine.close(previous))
|
||||
end
|
||||
end})
|
||||
coroutine.yield() -- leaves 'cc' pending to be closed
|
||||
end)
|
||||
assert(coroutine.resume(coro)) -- start it and run until it yields
|
||||
end
|
||||
local st, msg = coroutine.close(coro)
|
||||
assert(not st and string.find(msg, "C stack overflow"))
|
||||
print("final count: ", count)
|
||||
end
|
||||
|
||||
-- Just an alternative limit, different from the current one
|
||||
-- (smaller to avoid stack overflows)
|
||||
local alterlimit <const> = currentlimit * 8 // 10
|
||||
|
||||
assert(not debug.setcstacklimit(0)) -- limit too small
|
||||
assert(not debug.setcstacklimit(50000)) -- limit too large
|
||||
local co = coroutine.wrap (function ()
|
||||
return debug.setcstacklimit(alterlimit)
|
||||
end)
|
||||
assert(not co()) -- cannot change C stack inside coroutine
|
||||
do
|
||||
print("nesting of resuming yielded coroutines")
|
||||
local count = 0
|
||||
|
||||
local n
|
||||
local function foo () n = n + 1; foo () end
|
||||
|
||||
local function check ()
|
||||
n = 0
|
||||
pcall(foo)
|
||||
return n
|
||||
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
|
||||
|
||||
-- set limit to 'alterlimit'
|
||||
assert(debug.setcstacklimit(alterlimit) == currentlimit)
|
||||
local limalter <const> = check()
|
||||
-- set a very low limit (given that there are already several active
|
||||
-- calls to arrive here)
|
||||
local lowlimit <const> = 38
|
||||
assert(debug.setcstacklimit(lowlimit) == alterlimit)
|
||||
-- usable limit is much lower, due to active calls
|
||||
local actuallow = check()
|
||||
assert(actuallow < lowlimit - 30)
|
||||
-- now, add 'lowlimit' extra slots, which should all be available
|
||||
assert(debug.setcstacklimit(lowlimit + lowlimit) == lowlimit)
|
||||
local lim2 <const> = check()
|
||||
assert(lim2 == actuallow + lowlimit)
|
||||
|
||||
|
||||
-- 'setcstacklimit' works inside protected calls. (The new stack
|
||||
-- limit is kept when 'pcall' returns.)
|
||||
assert(pcall(function ()
|
||||
assert(debug.setcstacklimit(alterlimit) == lowlimit * 2)
|
||||
assert(check() <= limalter)
|
||||
end))
|
||||
|
||||
assert(check() == limalter)
|
||||
-- restore original limit
|
||||
assert(debug.setcstacklimit(origlimit) == alterlimit)
|
||||
local f = coroutine.wrap(body)
|
||||
f()
|
||||
assert(not pcall(f))
|
||||
print("final count: ", count)
|
||||
end
|
||||
|
||||
|
||||
do -- bug in 5.4.2
|
||||
print("nesting coroutines running after recoverable errors")
|
||||
local count = 0
|
||||
local function foo()
|
||||
count = count + 1
|
||||
pcall(1) -- create an error
|
||||
-- running now inside 'precover' ("protected recover")
|
||||
coroutine.wrap(foo)() -- call another coroutine
|
||||
end
|
||||
checkerror("C stack overflow", foo)
|
||||
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()
|
||||
tracegc.stop() -- __gc should not be called with a full stack
|
||||
xpcall(f, err)
|
||||
tracegc.start()
|
||||
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
|
||||
tracegc.stop() -- __gc should not be called with a full stack
|
||||
f()
|
||||
tracegc.start()
|
||||
print"+"
|
||||
end
|
||||
|
||||
print'OK'
|
||||
|
||||
@@ -16,7 +16,7 @@ end
|
||||
assert(not debug.gethook())
|
||||
|
||||
local testline = 19 -- line where 'test' is defined
|
||||
function test (s, l, p) -- this must be line 19
|
||||
local function test (s, l, p) -- this must be line 19
|
||||
collectgarbage() -- avoid gc during trace
|
||||
local function f (event, line)
|
||||
assert(event == 'line')
|
||||
@@ -31,6 +31,7 @@ end
|
||||
|
||||
do
|
||||
assert(not pcall(debug.getinfo, print, "X")) -- invalid option
|
||||
assert(not pcall(debug.getinfo, 0, ">")) -- invalid option
|
||||
assert(not debug.getinfo(1000)) -- out of range level
|
||||
assert(not debug.getinfo(-1)) -- out of range level
|
||||
local a = debug.getinfo(print)
|
||||
@@ -49,7 +50,7 @@ end
|
||||
|
||||
|
||||
-- test file and string names truncation
|
||||
a = "function f () end"
|
||||
local a = "function f () end"
|
||||
local function dostring (s, x) return load(s, x)() end
|
||||
dostring(a)
|
||||
assert(debug.getinfo(f).short_src == string.format('[string "%s"]', a))
|
||||
@@ -71,7 +72,8 @@ dostring(a, string.format("=%s", string.rep('x', 500)))
|
||||
assert(string.find(debug.getinfo(f).short_src, "^x*$"))
|
||||
dostring(a, "=")
|
||||
assert(debug.getinfo(f).short_src == "")
|
||||
a = nil; f = nil;
|
||||
_G.a = nil; _G.f = nil;
|
||||
_G[string.rep("p", 400)] = nil
|
||||
|
||||
|
||||
repeat
|
||||
@@ -119,6 +121,18 @@ else
|
||||
end
|
||||
]], {2,3,4,7})
|
||||
|
||||
|
||||
test([[
|
||||
local function foo()
|
||||
end
|
||||
foo()
|
||||
A = 1
|
||||
A = 2
|
||||
A = 3
|
||||
]], {2, 3, 2, 4, 5, 6})
|
||||
_G.A = nil
|
||||
|
||||
|
||||
test([[--
|
||||
if nil then
|
||||
a=1
|
||||
@@ -164,6 +178,8 @@ end
|
||||
|
||||
test([[for i=1,4 do a=1 end]], {1,1,1,1})
|
||||
|
||||
_G.a = nil
|
||||
|
||||
|
||||
do -- testing line info/trace with large gaps in source
|
||||
|
||||
@@ -183,6 +199,50 @@ do -- testing line info/trace with large gaps in source
|
||||
end
|
||||
end
|
||||
end
|
||||
_G.a = nil
|
||||
|
||||
|
||||
do -- testing active lines
|
||||
local function checkactivelines (f, lines)
|
||||
local t = debug.getinfo(f, "SL")
|
||||
for _, l in pairs(lines) do
|
||||
l = l + t.linedefined
|
||||
assert(t.activelines[l])
|
||||
t.activelines[l] = undef
|
||||
end
|
||||
assert(next(t.activelines) == nil) -- no extra lines
|
||||
end
|
||||
|
||||
checkactivelines(function (...) -- vararg function
|
||||
-- 1st line is empty
|
||||
-- 2nd line is empty
|
||||
-- 3th line is empty
|
||||
local a = 20
|
||||
-- 5th line is empty
|
||||
local b = 30
|
||||
-- 7th line is empty
|
||||
end, {4, 6, 8})
|
||||
|
||||
checkactivelines(function (a)
|
||||
-- 1st line is empty
|
||||
-- 2nd line is empty
|
||||
local a = 20
|
||||
local b = 30
|
||||
-- 5th line is empty
|
||||
end, {3, 4, 6})
|
||||
|
||||
checkactivelines(function (a, b, ...) end, {0})
|
||||
|
||||
checkactivelines(function (a, b)
|
||||
end, {1})
|
||||
|
||||
for _, n in pairs{0, 1, 2, 10, 50, 100, 1000, 10000} do
|
||||
checkactivelines(
|
||||
load(string.format("%s return 1", string.rep("\n", n))),
|
||||
{n + 1})
|
||||
end
|
||||
|
||||
end
|
||||
|
||||
print'+'
|
||||
|
||||
@@ -233,7 +293,6 @@ foo(200, 3, 4)
|
||||
local a = {}
|
||||
for i = 1, (_soft and 100 or 1000) do a[i] = i end
|
||||
foo(table.unpack(a))
|
||||
a = nil
|
||||
|
||||
|
||||
|
||||
@@ -253,13 +312,14 @@ do -- test hook presence in debug info
|
||||
debug.sethook()
|
||||
assert(count == 4)
|
||||
end
|
||||
_ENV.a = nil
|
||||
|
||||
|
||||
-- hook table has weak keys
|
||||
assert(getmetatable(debug.getregistry()._HOOKKEY).__mode == 'k')
|
||||
|
||||
|
||||
a = {}; L = nil
|
||||
a = {}; local L = nil
|
||||
local glob = 1
|
||||
local oldglob = glob
|
||||
debug.sethook(function (e,l)
|
||||
@@ -300,7 +360,7 @@ function foo()
|
||||
end; foo() -- set L
|
||||
-- check line counting inside strings and empty lines
|
||||
|
||||
_ = 'alo\
|
||||
local _ = 'alo\
|
||||
alo' .. [[
|
||||
|
||||
]]
|
||||
@@ -349,6 +409,7 @@ function g(a,b) return (a+1) + f() end
|
||||
|
||||
assert(g(0,0) == 30)
|
||||
|
||||
_G.f, _G.g = nil
|
||||
|
||||
debug.sethook(nil);
|
||||
assert(not debug.gethook())
|
||||
@@ -392,7 +453,7 @@ local function collectlocals (level)
|
||||
end
|
||||
|
||||
|
||||
X = nil
|
||||
local X = nil
|
||||
a = {}
|
||||
function a:f (a, b, ...) local arg = {...}; local c = 13 end
|
||||
debug.sethook(function (e)
|
||||
@@ -415,6 +476,7 @@ a:f(1,2,3,4,5)
|
||||
assert(X.self == a and X.a == 1 and X.b == 2 and X.c == nil)
|
||||
assert(XX == 12)
|
||||
assert(not debug.gethook())
|
||||
_G.XX = nil
|
||||
|
||||
|
||||
-- testing access to local variables in return hook (bug in 5.2)
|
||||
@@ -539,6 +601,7 @@ end
|
||||
|
||||
debug.sethook()
|
||||
|
||||
local g, g1
|
||||
|
||||
-- tests for tail calls
|
||||
local function f (x)
|
||||
@@ -584,7 +647,7 @@ h(false)
|
||||
debug.sethook()
|
||||
assert(b == 2) -- two tail calls
|
||||
|
||||
lim = _soft and 3000 or 30000
|
||||
local lim = _soft and 3000 or 30000
|
||||
local function foo (x)
|
||||
if x==0 then
|
||||
assert(debug.getinfo(2).what == "main")
|
||||
@@ -813,8 +876,10 @@ assert(a + 30000 == "add" and a - 3.0 == "sub" and a * 3.0 == "mul" and
|
||||
-a == "unm" and #a == "len" and a & 3 == "band")
|
||||
assert(a|3 == "bor" and 3~a == "bxor" and a<<3 == "shl" and a>>1 == "shr")
|
||||
assert (a==b and a.op == "eq")
|
||||
assert (a>=b and a.op == "order")
|
||||
assert (a>b and a.op == "order")
|
||||
assert (a>=b and a.op == "le")
|
||||
assert ("x">=a and a.op == "le")
|
||||
assert (a>b and a.op == "lt")
|
||||
assert (a>10 and a.op == "lt")
|
||||
assert(~a == "bnot")
|
||||
|
||||
do -- testing for-iterator name
|
||||
@@ -831,7 +896,7 @@ do -- testing debug info for finalizers
|
||||
|
||||
-- create a piece of garbage with a finalizer
|
||||
setmetatable({}, {__gc = function ()
|
||||
local t = debug.getinfo(2) -- get callee information
|
||||
local t = debug.getinfo(1) -- get function information
|
||||
assert(t.namewhat == "metamethod")
|
||||
name = t.name
|
||||
end})
|
||||
@@ -884,7 +949,7 @@ end
|
||||
|
||||
|
||||
print("testing debug functions on chunk without debug info")
|
||||
prog = [[-- program to be loaded without debug information (strip)
|
||||
local prog = [[-- program to be loaded without debug information (strip)
|
||||
local debug = require'debug'
|
||||
local a = 12 -- a local variable
|
||||
|
||||
|
||||
@@ -24,8 +24,9 @@ local function doit (s)
|
||||
end
|
||||
|
||||
|
||||
local function checkmessage (prog, msg)
|
||||
local function checkmessage (prog, msg, debug)
|
||||
local m = doit(prog)
|
||||
if debug then print(m, msg) end
|
||||
assert(string.find(m, msg, 1, true))
|
||||
end
|
||||
|
||||
@@ -67,6 +68,27 @@ checksyntax([[
|
||||
]], "'}' expected (to close '{' at line 1)", "<eof>", 3)
|
||||
|
||||
|
||||
do -- testing errors in goto/break
|
||||
local function checksyntax (prog, msg, line)
|
||||
local st, err = load(prog)
|
||||
assert(string.find(err, "line " .. line))
|
||||
assert(string.find(err, msg, 1, true))
|
||||
end
|
||||
|
||||
checksyntax([[
|
||||
::A:: a = 1
|
||||
::A::
|
||||
]], "label 'A' already defined", 1)
|
||||
|
||||
checksyntax([[
|
||||
a = 1
|
||||
goto A
|
||||
do ::A:: end
|
||||
]], "no visible label 'A'", 2)
|
||||
|
||||
end
|
||||
|
||||
|
||||
if not T then
|
||||
(Message or print)
|
||||
('\n >>> testC not active: skipping memory message test <<<\n')
|
||||
@@ -92,21 +114,35 @@ checkmessage("a = {} | 1", "bitwise operation")
|
||||
checkmessage("a = {} < 1", "attempt to compare")
|
||||
checkmessage("a = {} <= 1", "attempt to compare")
|
||||
|
||||
checkmessage("a=1; bbbb=2; a=math.sin(3)+bbbb(3)", "global 'bbbb'")
|
||||
checkmessage("a={}; do local a=1 end a:bbbb(3)", "method 'bbbb'")
|
||||
checkmessage("aaa=1; bbbb=2; aaa=math.sin(3)+bbbb(3)", "global 'bbbb'")
|
||||
checkmessage("aaa={}; do local aaa=1 end aaa:bbbb(3)", "method 'bbbb'")
|
||||
checkmessage("local a={}; a.bbbb(3)", "field 'bbbb'")
|
||||
assert(not string.find(doit"a={13}; local bbbb=1; a[bbbb](3)", "'bbbb'"))
|
||||
checkmessage("a={13}; local bbbb=1; a[bbbb](3)", "number")
|
||||
checkmessage("a=(1)..{}", "a table value")
|
||||
assert(not string.find(doit"aaa={13}; local bbbb=1; aaa[bbbb](3)", "'bbbb'"))
|
||||
checkmessage("aaa={13}; local bbbb=1; aaa[bbbb](3)", "number")
|
||||
checkmessage("aaa=(1)..{}", "a table value")
|
||||
|
||||
_G.aaa, _G.bbbb = nil
|
||||
|
||||
-- calls
|
||||
checkmessage("local a; a(13)", "local 'a'")
|
||||
checkmessage([[
|
||||
local a = setmetatable({}, {__add = 34})
|
||||
a = a + 1
|
||||
]], "metamethod 'add'")
|
||||
checkmessage([[
|
||||
local a = setmetatable({}, {__lt = {}})
|
||||
a = a > a
|
||||
]], "metamethod 'lt'")
|
||||
|
||||
-- tail calls
|
||||
checkmessage("local a={}; return a.bbbb(3)", "field 'bbbb'")
|
||||
checkmessage("a={}; do local a=1 end; return a:bbbb(3)", "method 'bbbb'")
|
||||
checkmessage("aaa={}; do local aaa=1 end; return aaa:bbbb(3)", "method 'bbbb'")
|
||||
|
||||
checkmessage("a = #print", "length of a function value")
|
||||
checkmessage("a = #3", "length of a number value")
|
||||
checkmessage("aaa = #print", "length of a function value")
|
||||
checkmessage("aaa = #3", "length of a number value")
|
||||
|
||||
_G.aaa = nil
|
||||
|
||||
aaa = nil
|
||||
checkmessage("aaa.bbb:ddd(9)", "global 'aaa'")
|
||||
checkmessage("local aaa={bbb=1}; aaa.bbb:ddd(9)", "field 'bbb'")
|
||||
checkmessage("local aaa={bbb={}}; aaa.bbb:ddd(9)", "method 'ddd'")
|
||||
@@ -119,15 +155,16 @@ checkmessage("local a,b,cc; (function () a.x = 1 end)()", "upvalue 'a'")
|
||||
|
||||
checkmessage("local _ENV = {x={}}; a = a + 1", "global 'a'")
|
||||
|
||||
checkmessage("b=1; local aaa={}; x=aaa+b", "local 'aaa'")
|
||||
checkmessage("BB=1; local aaa={}; x=aaa+BB", "local 'aaa'")
|
||||
checkmessage("aaa={}; x=3.3/aaa", "global 'aaa'")
|
||||
checkmessage("aaa=2; b=nil;x=aaa*b", "global 'b'")
|
||||
checkmessage("aaa=2; BB=nil;x=aaa*BB", "global 'BB'")
|
||||
checkmessage("aaa={}; x=-aaa", "global 'aaa'")
|
||||
|
||||
-- short circuit
|
||||
checkmessage("a=1; local a,bbbb=2,3; a = math.sin(1) and bbbb(3)",
|
||||
checkmessage("aaa=1; local aaa,bbbb=2,3; aaa = math.sin(1) and bbbb(3)",
|
||||
"local 'bbbb'")
|
||||
checkmessage("a=1; local a,bbbb=2,3; a = bbbb(1) or a(3)", "local 'bbbb'")
|
||||
checkmessage("aaa=1; local aaa,bbbb=2,3; aaa = bbbb(1) or aaa(3)",
|
||||
"local 'bbbb'")
|
||||
checkmessage("local a,b,c,f = 1,1,1; f((a and b) or c)", "local 'f'")
|
||||
checkmessage("local a,b,c = 1,1,1; ((a and b) or c)()", "call a number value")
|
||||
assert(not string.find(doit"aaa={}; x=(aaa or aaa)+(aaa and aaa)", "'aaa'"))
|
||||
@@ -154,8 +191,15 @@ checkmessage("return ~-3e40", "has no integer representation")
|
||||
checkmessage("return ~-3.009", "has no integer representation")
|
||||
checkmessage("return 3.009 & 1", "has no integer representation")
|
||||
checkmessage("return 34 >> {}", "table value")
|
||||
checkmessage("a = 24 // 0", "divide by zero")
|
||||
checkmessage("a = 1 % 0", "'n%0'")
|
||||
checkmessage("aaa = 24 // 0", "divide by zero")
|
||||
checkmessage("aaa = 1 % 0", "'n%0'")
|
||||
|
||||
|
||||
-- type error for an object which is neither in an upvalue nor a register.
|
||||
-- The following code will try to index the value 10 that is stored in
|
||||
-- the metatable, without moving it to a register.
|
||||
checkmessage("local a = setmetatable({}, {__index = 10}).x",
|
||||
"attempt to index a number value")
|
||||
|
||||
|
||||
-- numeric for loops
|
||||
@@ -188,6 +232,22 @@ do -- named objects (field '__name')
|
||||
checkmessage("return {} < XX", "table with My Type")
|
||||
checkmessage("return XX < io.stdin", "My Type with FILE*")
|
||||
_G.XX = nil
|
||||
|
||||
if T then -- extra tests for 'luaL_tolstring'
|
||||
-- bug in 5.4.3; 'luaL_tolstring' with negative indices
|
||||
local x = setmetatable({}, {__name="TABLE"})
|
||||
assert(T.testC("Ltolstring -1; return 1", x) == tostring(x))
|
||||
|
||||
local a, b = T.testC("pushint 10; Ltolstring -2; return 2", x)
|
||||
assert(a == 10 and b == tostring(x))
|
||||
|
||||
setmetatable(x, {__tostring=function (o)
|
||||
assert(o == x)
|
||||
return "ABC"
|
||||
end})
|
||||
local a, b, c = T.testC("pushint 10; Ltolstring -2; return 3", x)
|
||||
assert(a == x and b == 10 and c == "ABC")
|
||||
end
|
||||
end
|
||||
|
||||
-- global functions
|
||||
@@ -213,13 +273,13 @@ end
|
||||
-- tests for field accesses after RK limit
|
||||
local t = {}
|
||||
for i = 1, 1000 do
|
||||
t[i] = "a = x" .. i
|
||||
t[i] = "aaa = x" .. i
|
||||
end
|
||||
local s = table.concat(t, "; ")
|
||||
t = nil
|
||||
checkmessage(s.."; a = bbb + 1", "global 'bbb'")
|
||||
checkmessage("local _ENV=_ENV;"..s.."; a = bbb + 1", "global 'bbb'")
|
||||
checkmessage(s.."; local t = {}; a = t.bbb + 1", "field 'bbb'")
|
||||
checkmessage(s.."; aaa = bbb + 1", "global 'bbb'")
|
||||
checkmessage("local _ENV=_ENV;"..s.."; aaa = bbb + 1", "global 'bbb'")
|
||||
checkmessage(s.."; local t = {}; aaa = t.bbb + 1", "field 'bbb'")
|
||||
checkmessage(s.."; local t = {}; t:bbb()", "method 'bbb'")
|
||||
|
||||
checkmessage([[aaa=9
|
||||
@@ -249,7 +309,7 @@ end]], "global 'insert'")
|
||||
|
||||
checkmessage([[ -- tail call
|
||||
return math.sin("a")
|
||||
]], "'sin'")
|
||||
]], "sin")
|
||||
|
||||
checkmessage([[collectgarbage("nooption")]], "invalid option")
|
||||
|
||||
@@ -268,14 +328,17 @@ main()
|
||||
]], "global 'NoSuchName'")
|
||||
print'+'
|
||||
|
||||
a = {}; setmetatable(a, {__index = string})
|
||||
checkmessage("a:sub()", "bad self")
|
||||
aaa = {}; setmetatable(aaa, {__index = string})
|
||||
checkmessage("aaa:sub()", "bad self")
|
||||
checkmessage("string.sub('a', {})", "#2")
|
||||
checkmessage("('a'):sub{}", "#1")
|
||||
|
||||
checkmessage("table.sort({1,2,3}, table.sort)", "'table.sort'")
|
||||
checkmessage("string.gsub('s', 's', setmetatable)", "'setmetatable'")
|
||||
|
||||
_G.aaa = nil
|
||||
|
||||
|
||||
-- tests for errors in coroutines
|
||||
|
||||
local function f (n)
|
||||
@@ -293,7 +356,7 @@ checkerr("yield across", f)
|
||||
|
||||
-- testing size of 'source' info; size of buffer for that info is
|
||||
-- LUA_IDSIZE, declared as 60 in luaconf. Get one position for '\0'.
|
||||
idsize = 60 - 1
|
||||
local idsize = 60 - 1
|
||||
local function checksize (source)
|
||||
-- syntax error
|
||||
local _, msg = load("x", source)
|
||||
@@ -355,13 +418,14 @@ x
|
||||
|
||||
local p = [[
|
||||
function g() f() end
|
||||
function f(x) error('a', X) end
|
||||
function f(x) error('a', XX) end
|
||||
g()
|
||||
]]
|
||||
X=3;lineerror((p), 3)
|
||||
X=0;lineerror((p), false)
|
||||
X=1;lineerror((p), 2)
|
||||
X=2;lineerror((p), 1)
|
||||
XX=3;lineerror((p), 3)
|
||||
XX=0;lineerror((p), false)
|
||||
XX=1;lineerror((p), 2)
|
||||
XX=2;lineerror((p), 1)
|
||||
_G.XX, _G.g, _G.f = nil
|
||||
|
||||
|
||||
lineerror([[
|
||||
@@ -381,30 +445,56 @@ if not b then
|
||||
end]], 5)
|
||||
|
||||
|
||||
-- bug in 5.4.0
|
||||
lineerror([[
|
||||
local a = 0
|
||||
local b = 1
|
||||
local c = b % a
|
||||
]], 3)
|
||||
|
||||
do
|
||||
-- Force a negative estimate for base line. Error in instruction 2
|
||||
-- (after VARARGPREP, GETGLOBAL), with first absolute line information
|
||||
-- (forced by too many lines) in instruction 0.
|
||||
local s = string.format("%s return __A.x", string.rep("\n", 300))
|
||||
lineerror(s, 301)
|
||||
end
|
||||
|
||||
|
||||
if not _soft then
|
||||
-- several tests that exaust the Lua stack
|
||||
collectgarbage()
|
||||
print"testing stack overflow"
|
||||
C = 0
|
||||
local l = debug.getinfo(1, "l").currentline; function y () C=C+1; y() end
|
||||
local C = 0
|
||||
-- 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 YY ()
|
||||
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('+')
|
||||
assert(checkstackmessage(doit('YY()')))
|
||||
assert(checkstackmessage(doit('YY()')))
|
||||
assert(checkstackmessage(doit('YY()')))
|
||||
|
||||
_G.YY = nil
|
||||
|
||||
|
||||
-- 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('+')
|
||||
@@ -489,7 +579,7 @@ do
|
||||
end
|
||||
|
||||
-- xpcall with arguments
|
||||
a, b, c = xpcall(string.find, error, "alo", "al")
|
||||
local a, b, c = xpcall(string.find, error, "alo", "al")
|
||||
assert(a and b == 1 and c == 2)
|
||||
a, b, c = xpcall(string.find, function (x) return {} end, true, "al")
|
||||
assert(not a and type(b) == "table" and c == nil)
|
||||
@@ -509,11 +599,12 @@ checksyntax("a\1a = 1", "", "<\\1>", 1)
|
||||
-- test 255 as first char in a chunk
|
||||
checksyntax("\255a = 1", "", "<\\255>", 1)
|
||||
|
||||
doit('I = load("a=9+"); a=3')
|
||||
assert(a==3 and not I)
|
||||
doit('I = load("a=9+"); aaa=3')
|
||||
assert(_G.aaa==3 and not _G.I)
|
||||
_G.I,_G.aaa = nil
|
||||
print('+')
|
||||
|
||||
lim = 1000
|
||||
local lim = 1000
|
||||
if _soft then lim = 100 end
|
||||
for i=1,lim do
|
||||
doit('a = ')
|
||||
@@ -530,10 +621,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
|
||||
|
||||
@@ -420,6 +420,9 @@ assert(i == 3 and x[1] == 3 and x[3] == 5)
|
||||
|
||||
assert(_G.X == 20)
|
||||
|
||||
_G.X, _G.B = nil
|
||||
|
||||
|
||||
print'+'
|
||||
|
||||
local _g = _G
|
||||
|
||||
@@ -507,15 +507,17 @@ load((io.lines(file, 1)))()
|
||||
assert(_G.X == 4)
|
||||
load((io.lines(file, 3)))()
|
||||
assert(_G.X == 8)
|
||||
_G.X = nil
|
||||
|
||||
print('+')
|
||||
|
||||
local x1 = "string\n\n\\com \"\"''coisas [[estranhas]] ]]'"
|
||||
io.output(file)
|
||||
assert(io.write(string.format("x2 = %q\n-- comment without ending EOS", x1)))
|
||||
assert(io.write(string.format("X2 = %q\n-- comment without ending EOS", x1)))
|
||||
io.close()
|
||||
assert(loadfile(file))()
|
||||
assert(x1 == x2)
|
||||
assert(x1 == _G.X2)
|
||||
_G.X2 = nil
|
||||
print('+')
|
||||
assert(os.remove(file))
|
||||
assert(not os.remove(file))
|
||||
@@ -825,8 +827,17 @@ checkerr("missing", os.time, {hour = 12}) -- missing date
|
||||
if string.packsize("i") == 4 then -- 4-byte ints
|
||||
checkerr("field 'year' is out-of-bound", os.time,
|
||||
{year = -(1 << 31) + 1899, month = 1, day = 1})
|
||||
|
||||
checkerr("field 'year' is out-of-bound", os.time,
|
||||
{year = -(1 << 31), month = 1, day = 1})
|
||||
|
||||
if math.maxinteger > 2^31 then -- larger lua_integer?
|
||||
checkerr("field 'year' is out-of-bound", os.time,
|
||||
{year = (1 << 31) + 1900, month = 1, day = 1})
|
||||
end
|
||||
end
|
||||
|
||||
|
||||
if not _port then
|
||||
-- test Posix-specific modifiers
|
||||
assert(type(os.date("%Ex")) == 'string')
|
||||
|
||||
@@ -125,7 +125,7 @@ do
|
||||
end
|
||||
|
||||
a:test()
|
||||
|
||||
_G.temp = nil
|
||||
end
|
||||
|
||||
|
||||
@@ -134,7 +134,7 @@ do local f = function () end end
|
||||
|
||||
|
||||
print("functions with errors")
|
||||
prog = [[
|
||||
local prog = [[
|
||||
do
|
||||
a = 10;
|
||||
function foo(x,y)
|
||||
@@ -153,22 +153,25 @@ do
|
||||
end
|
||||
end
|
||||
end
|
||||
rawset(_G, "a", nil)
|
||||
_G.x = nil
|
||||
|
||||
foo = nil
|
||||
print('long strings')
|
||||
x = "01234567890123456789012345678901234567890123456789012345678901234567890123456789"
|
||||
assert(string.len(x)==80)
|
||||
s = ''
|
||||
k = math.min(300, (math.maxinteger // 80) // 2)
|
||||
for n = 1, k do s = s..x; j=tostring(n) end
|
||||
assert(string.len(s) == k*80)
|
||||
s = string.sub(s, 1, 10000)
|
||||
s, i = string.gsub(s, '(%d%d%d%d)', '')
|
||||
assert(i==10000 // 4)
|
||||
s = nil
|
||||
x = nil
|
||||
do
|
||||
foo = nil
|
||||
print('long strings')
|
||||
local x = "01234567890123456789012345678901234567890123456789012345678901234567890123456789"
|
||||
assert(string.len(x)==80)
|
||||
local s = ''
|
||||
local k = math.min(300, (math.maxinteger // 80) // 2)
|
||||
for n = 1, k do s = s..x; local j=tostring(n) end
|
||||
assert(string.len(s) == k*80)
|
||||
s = string.sub(s, 1, 10000)
|
||||
local s, i = string.gsub(s, '(%d%d%d%d)', '')
|
||||
assert(i==10000 // 4)
|
||||
|
||||
assert(_G["while"] == 234)
|
||||
assert(_G["while"] == 234)
|
||||
_G["while"] = nil
|
||||
end
|
||||
|
||||
|
||||
--
|
||||
@@ -227,8 +230,8 @@ end
|
||||
|
||||
|
||||
print("clearing tables")
|
||||
lim = 15
|
||||
a = {}
|
||||
local lim = 15
|
||||
local a = {}
|
||||
-- fill a with `collectable' indices
|
||||
for i=1,lim do a[{}] = i end
|
||||
b = {}
|
||||
@@ -371,7 +374,7 @@ if T then
|
||||
|
||||
warn("@on"); warn("@store")
|
||||
collectgarbage()
|
||||
assert(string.find(_WARN, "error in __gc metamethod"))
|
||||
assert(string.find(_WARN, "error in __gc"))
|
||||
assert(string.match(_WARN, "@(.-)@") == "expected"); _WARN = false
|
||||
for i = 8, 10 do assert(s[i]) end
|
||||
|
||||
@@ -552,6 +555,7 @@ do
|
||||
for i=1,1000 do _ENV.a = {} end -- no collection during the loop
|
||||
until gcinfo() > 2 * x
|
||||
collectgarbage"restart"
|
||||
_ENV.a = nil
|
||||
end
|
||||
|
||||
|
||||
@@ -676,6 +680,16 @@ end
|
||||
-- just to make sure
|
||||
assert(collectgarbage'isrunning')
|
||||
|
||||
do -- check that the collector is not reentrant in incremental mode
|
||||
local res = true
|
||||
setmetatable({}, {__gc = function ()
|
||||
res = collectgarbage()
|
||||
end})
|
||||
collectgarbage()
|
||||
assert(not res)
|
||||
end
|
||||
|
||||
|
||||
collectgarbage(oldmode)
|
||||
|
||||
print('OK')
|
||||
|
||||
@@ -11,17 +11,17 @@ CFLAGS = -Wall -std=gnu99 -O2 -I$(LUA_DIR) -fPIC -shared
|
||||
all: lib1.so lib11.so lib2.so lib21.so lib2-v2.so
|
||||
touch all
|
||||
|
||||
lib1.so: lib1.c $(LUA_DIR)/luaconf.h
|
||||
lib1.so: lib1.c $(LUA_DIR)/luaconf.h $(LUA_DIR)/lua.h
|
||||
$(CC) $(CFLAGS) -o lib1.so lib1.c
|
||||
|
||||
lib11.so: lib11.c $(LUA_DIR)/luaconf.h
|
||||
lib11.so: lib11.c $(LUA_DIR)/luaconf.h $(LUA_DIR)/lua.h
|
||||
$(CC) $(CFLAGS) -o lib11.so lib11.c
|
||||
|
||||
lib2.so: lib2.c $(LUA_DIR)/luaconf.h
|
||||
lib2.so: lib2.c $(LUA_DIR)/luaconf.h $(LUA_DIR)/lua.h
|
||||
$(CC) $(CFLAGS) -o lib2.so lib2.c
|
||||
|
||||
lib21.so: lib21.c $(LUA_DIR)/luaconf.h
|
||||
lib21.so: lib21.c $(LUA_DIR)/luaconf.h $(LUA_DIR)/lua.h
|
||||
$(CC) $(CFLAGS) -o lib21.so lib21.c
|
||||
|
||||
lib2-v2.so: lib21.c $(LUA_DIR)/luaconf.h
|
||||
lib2-v2.so: lib21.c $(LUA_DIR)/luaconf.h $(LUA_DIR)/lua.h
|
||||
$(CC) $(CFLAGS) -o lib2-v2.so lib22.c
|
||||
|
||||
@@ -10,6 +10,7 @@ local function dostring (x) return assert(load(x), "")() end
|
||||
|
||||
dostring("x \v\f = \t\r 'a\0a' \v\f\f")
|
||||
assert(x == 'a\0a' and string.len(x) == 3)
|
||||
_G.x = nil
|
||||
|
||||
-- escape sequences
|
||||
assert('\n\"\'\\' == [[
|
||||
@@ -129,16 +130,16 @@ end
|
||||
|
||||
-- long variable names
|
||||
|
||||
var1 = string.rep('a', 15000) .. '1'
|
||||
var2 = string.rep('a', 15000) .. '2'
|
||||
prog = string.format([[
|
||||
local var1 = string.rep('a', 15000) .. '1'
|
||||
local var2 = string.rep('a', 15000) .. '2'
|
||||
local prog = string.format([[
|
||||
%s = 5
|
||||
%s = %s + 1
|
||||
return function () return %s - %s end
|
||||
]], var1, var2, var1, var1, var2)
|
||||
local f = dostring(prog)
|
||||
assert(_G[var1] == 5 and _G[var2] == 6 and f() == -1)
|
||||
var1, var2, f = nil
|
||||
_G[var1], _G[var2] = nil
|
||||
print('+')
|
||||
|
||||
-- escapes --
|
||||
@@ -150,13 +151,13 @@ assert([[
|
||||
$debug]] == "\n $debug")
|
||||
assert([[ [ ]] ~= [[ ] ]])
|
||||
-- long strings --
|
||||
b = "001234567890123456789012345678901234567891234567890123456789012345678901234567890012345678901234567890123456789012345678912345678901234567890123456789012345678900123456789012345678901234567890123456789123456789012345678901234567890123456789001234567890123456789012345678901234567891234567890123456789012345678901234567890012345678901234567890123456789012345678912345678901234567890123456789012345678900123456789012345678901234567890123456789123456789012345678901234567890123456789001234567890123456789012345678901234567891234567890123456789012345678901234567890012345678901234567890123456789012345678912345678901234567890123456789012345678900123456789012345678901234567890123456789123456789012345678901234567890123456789001234567890123456789012345678901234567891234567890123456789012345678901234567890012345678901234567890123456789012345678912345678901234567890123456789012345678900123456789012345678901234567890123456789123456789012345678901234567890123456789"
|
||||
local b = "001234567890123456789012345678901234567891234567890123456789012345678901234567890012345678901234567890123456789012345678912345678901234567890123456789012345678900123456789012345678901234567890123456789123456789012345678901234567890123456789001234567890123456789012345678901234567891234567890123456789012345678901234567890012345678901234567890123456789012345678912345678901234567890123456789012345678900123456789012345678901234567890123456789123456789012345678901234567890123456789001234567890123456789012345678901234567891234567890123456789012345678901234567890012345678901234567890123456789012345678912345678901234567890123456789012345678900123456789012345678901234567890123456789123456789012345678901234567890123456789001234567890123456789012345678901234567891234567890123456789012345678901234567890012345678901234567890123456789012345678912345678901234567890123456789012345678900123456789012345678901234567890123456789123456789012345678901234567890123456789"
|
||||
assert(string.len(b) == 960)
|
||||
prog = [=[
|
||||
print('+')
|
||||
|
||||
a1 = [["this is a 'string' with several 'quotes'"]]
|
||||
a2 = "'quotes'"
|
||||
local a1 = [["this is a 'string' with several 'quotes'"]]
|
||||
local a2 = "'quotes'"
|
||||
|
||||
assert(string.find(a1, a2) == 34)
|
||||
print('+')
|
||||
@@ -164,12 +165,13 @@ print('+')
|
||||
a1 = [==[temp = [[an arbitrary value]]; ]==]
|
||||
assert(load(a1))()
|
||||
assert(temp == 'an arbitrary value')
|
||||
_G.temp = nil
|
||||
-- long strings --
|
||||
b = "001234567890123456789012345678901234567891234567890123456789012345678901234567890012345678901234567890123456789012345678912345678901234567890123456789012345678900123456789012345678901234567890123456789123456789012345678901234567890123456789001234567890123456789012345678901234567891234567890123456789012345678901234567890012345678901234567890123456789012345678912345678901234567890123456789012345678900123456789012345678901234567890123456789123456789012345678901234567890123456789001234567890123456789012345678901234567891234567890123456789012345678901234567890012345678901234567890123456789012345678912345678901234567890123456789012345678900123456789012345678901234567890123456789123456789012345678901234567890123456789001234567890123456789012345678901234567891234567890123456789012345678901234567890012345678901234567890123456789012345678912345678901234567890123456789012345678900123456789012345678901234567890123456789123456789012345678901234567890123456789"
|
||||
local b = "001234567890123456789012345678901234567891234567890123456789012345678901234567890012345678901234567890123456789012345678912345678901234567890123456789012345678900123456789012345678901234567890123456789123456789012345678901234567890123456789001234567890123456789012345678901234567891234567890123456789012345678901234567890012345678901234567890123456789012345678912345678901234567890123456789012345678900123456789012345678901234567890123456789123456789012345678901234567890123456789001234567890123456789012345678901234567891234567890123456789012345678901234567890012345678901234567890123456789012345678912345678901234567890123456789012345678900123456789012345678901234567890123456789123456789012345678901234567890123456789001234567890123456789012345678901234567891234567890123456789012345678901234567890012345678901234567890123456789012345678912345678901234567890123456789012345678900123456789012345678901234567890123456789123456789012345678901234567890123456789"
|
||||
assert(string.len(b) == 960)
|
||||
print('+')
|
||||
|
||||
a = [[00123456789012345678901234567890123456789123456789012345678901234567890123456789
|
||||
local a = [[00123456789012345678901234567890123456789123456789012345678901234567890123456789
|
||||
00123456789012345678901234567890123456789123456789012345678901234567890123456789
|
||||
00123456789012345678901234567890123456789123456789012345678901234567890123456789
|
||||
00123456789012345678901234567890123456789123456789012345678901234567890123456789
|
||||
@@ -199,19 +201,41 @@ x = 1
|
||||
]=]
|
||||
|
||||
print('+')
|
||||
x = nil
|
||||
_G.x = nil
|
||||
dostring(prog)
|
||||
assert(x)
|
||||
_G.x = nil
|
||||
|
||||
prog = nil
|
||||
a = nil
|
||||
b = nil
|
||||
|
||||
|
||||
do -- reuse of long strings
|
||||
|
||||
-- get the address of a string
|
||||
local function getadd (s) return string.format("%p", s) end
|
||||
|
||||
local s1 <const> = "01234567890123456789012345678901234567890123456789"
|
||||
local s2 <const> = "01234567890123456789012345678901234567890123456789"
|
||||
local s3 = "01234567890123456789012345678901234567890123456789"
|
||||
local function foo() return s1 end
|
||||
local function foo1() return s3 end
|
||||
local function foo2()
|
||||
return "01234567890123456789012345678901234567890123456789"
|
||||
end
|
||||
local a1 = getadd(s1)
|
||||
assert(a1 == getadd(s2))
|
||||
assert(a1 == getadd(foo()))
|
||||
assert(a1 == getadd(foo1()))
|
||||
assert(a1 == getadd(foo2()))
|
||||
|
||||
local sd = "0123456789" .. "0123456789012345678901234567890123456789"
|
||||
assert(sd == s1 and getadd(sd) ~= a1)
|
||||
end
|
||||
|
||||
|
||||
-- testing line ends
|
||||
prog = [[
|
||||
a = 1 -- a comment
|
||||
b = 2
|
||||
local a = 1 -- a comment
|
||||
local b = 2
|
||||
|
||||
|
||||
x = [=[
|
||||
@@ -228,10 +252,11 @@ for _, n in pairs{"\n", "\r", "\n\r", "\r\n"} do
|
||||
assert(dostring(prog) == nn)
|
||||
assert(_G.x == "hi\n" and _G.y == "\nhello\r\n\n")
|
||||
end
|
||||
_G.x, _G.y = nil
|
||||
|
||||
|
||||
-- testing comments and strings with long brackets
|
||||
a = [==[]=]==]
|
||||
local a = [==[]=]==]
|
||||
assert(a == "]=")
|
||||
|
||||
a = [==[[===[[=[]]=][====[]]===]===]==]
|
||||
|
||||
@@ -5,6 +5,8 @@ print('testing local variables and environments')
|
||||
|
||||
local debug = require"debug"
|
||||
|
||||
local tracegc = require"tracegc"
|
||||
|
||||
|
||||
-- bug in 5.1:
|
||||
|
||||
@@ -35,7 +37,7 @@ end
|
||||
f = nil
|
||||
|
||||
local f
|
||||
x = 1
|
||||
local x = 1
|
||||
|
||||
a = nil
|
||||
load('local a = {}')()
|
||||
@@ -150,7 +152,7 @@ local dummy
|
||||
local _ENV = (function (...) return ... end)(_G, dummy) -- {
|
||||
|
||||
do local _ENV = {assert=assert}; assert(true) end
|
||||
mt = {_G = _G}
|
||||
local mt = {_G = _G}
|
||||
local foo,x
|
||||
A = false -- "declare" A
|
||||
do local _ENV = mt
|
||||
@@ -172,6 +174,8 @@ do local _ENV = {assert=assert, A=10};
|
||||
end
|
||||
assert(x==20)
|
||||
|
||||
A = nil
|
||||
|
||||
|
||||
do -- constants
|
||||
local a<const>, b, c<const> = 10, 20, 30
|
||||
@@ -185,6 +189,8 @@ do -- constants
|
||||
checkro("y", "local x, y <const>, z = 10, 20, 30; x = 11; y = 12")
|
||||
checkro("x", "local x <const>, y, z <const> = 10, 20, 30; x = 11")
|
||||
checkro("z", "local x <const>, y, z <const> = 10, 20, 30; y = 10; z = 11")
|
||||
checkro("foo", "local foo <const> = 10; function foo() end")
|
||||
checkro("foo", "local foo <const> = {}; function foo() end")
|
||||
|
||||
checkro("z", [[
|
||||
local a, z <const>, b = 10;
|
||||
@@ -232,7 +238,11 @@ end
|
||||
do
|
||||
local X = false
|
||||
|
||||
local x, closescope = func2close(function () stack(10); X = true end, 100)
|
||||
local x, closescope = func2close(function (_, msg)
|
||||
stack(10);
|
||||
assert(msg == nil)
|
||||
X = true
|
||||
end, 100)
|
||||
assert(x == 100); x = 101; -- 'x' is not read-only
|
||||
|
||||
-- closing functions do not corrupt returning values
|
||||
@@ -246,6 +256,12 @@ do
|
||||
|
||||
X = false
|
||||
foo = function (x)
|
||||
local _<close> = func2close(function (_, msg)
|
||||
-- without errors, enclosing function should be still active when
|
||||
-- __close is called
|
||||
assert(debug.getinfo(2).name == "foo")
|
||||
assert(msg == nil)
|
||||
end)
|
||||
local _<close> = closescope
|
||||
local y = 15
|
||||
return y
|
||||
@@ -323,73 +339,76 @@ do
|
||||
end
|
||||
|
||||
|
||||
-- auxiliary functions for testing warnings in '__close'
|
||||
local function prepwarn ()
|
||||
if not T then -- no test library?
|
||||
warn("@off") -- do not show (lots of) warnings
|
||||
else
|
||||
warn("@store") -- to test the warnings
|
||||
do
|
||||
-- bug in 5.4.3: previous condition (calls cannot be tail in the
|
||||
-- scope of to-be-closed variables) must be valid for tbc variables
|
||||
-- created by 'for' loops.
|
||||
|
||||
local closed = false
|
||||
|
||||
local function foo ()
|
||||
return function () return true end, 0, 0,
|
||||
func2close(function () closed = true end)
|
||||
end
|
||||
|
||||
local function tail() return closed end
|
||||
|
||||
local function foo1 ()
|
||||
for k in foo() do return tail() end
|
||||
end
|
||||
|
||||
assert(foo1() == false)
|
||||
assert(closed == true)
|
||||
end
|
||||
|
||||
|
||||
local function endwarn ()
|
||||
if not T then
|
||||
warn("@on") -- back to normal
|
||||
else
|
||||
assert(_WARN == false)
|
||||
warn("@normal")
|
||||
do
|
||||
-- bug in 5.4.4: 'break' may generate wrong 'close' instruction when
|
||||
-- leaving a loop block.
|
||||
|
||||
local closed = false
|
||||
|
||||
local o1 = setmetatable({}, {__close=function() closed = true end})
|
||||
|
||||
local function test()
|
||||
for k, v in next, {}, nil, o1 do
|
||||
local function f() return k end -- create an upvalue
|
||||
break
|
||||
end
|
||||
assert(closed)
|
||||
end
|
||||
|
||||
test()
|
||||
end
|
||||
|
||||
|
||||
local function checkwarn (msg)
|
||||
if T then
|
||||
assert(string.find(_WARN, msg))
|
||||
_WARN = false -- reset variable to check next warning
|
||||
end
|
||||
end
|
||||
|
||||
warn("@on")
|
||||
|
||||
do print("testing errors in __close")
|
||||
|
||||
prepwarn()
|
||||
|
||||
-- original error is in __close
|
||||
local function foo ()
|
||||
|
||||
local x <close> =
|
||||
func2close(function (self, msg)
|
||||
assert(string.find(msg, "@z"))
|
||||
assert(string.find(msg, "@y"))
|
||||
error("@x")
|
||||
end)
|
||||
|
||||
local x1 <close> =
|
||||
func2close(function (self, msg)
|
||||
checkwarn("@y")
|
||||
assert(string.find(msg, "@z"))
|
||||
assert(string.find(msg, "@y"))
|
||||
end)
|
||||
|
||||
local gc <close> = func2close(function () collectgarbage() end)
|
||||
|
||||
local y <close> =
|
||||
func2close(function (self, msg)
|
||||
assert(string.find(msg, "@z")) -- first error in 'z'
|
||||
checkwarn("@z") -- second error in 'z' generated a warning
|
||||
assert(string.find(msg, "@z")) -- error in 'z'
|
||||
error("@y")
|
||||
end)
|
||||
|
||||
local first = true
|
||||
local z <close> =
|
||||
-- 'z' close is called twice
|
||||
func2close(function (self, msg)
|
||||
if first then
|
||||
assert(msg == nil)
|
||||
first = false
|
||||
else
|
||||
assert(string.find(msg, "@z")) -- own error
|
||||
end
|
||||
assert(msg == nil)
|
||||
error("@z")
|
||||
end)
|
||||
|
||||
@@ -397,8 +416,7 @@ do print("testing errors in __close")
|
||||
end
|
||||
|
||||
local stat, msg = pcall(foo, false)
|
||||
assert(string.find(msg, "@z"))
|
||||
checkwarn("@x")
|
||||
assert(string.find(msg, "@x"))
|
||||
|
||||
|
||||
-- original error not in __close
|
||||
@@ -406,13 +424,16 @@ do print("testing errors in __close")
|
||||
|
||||
local x <close> =
|
||||
func2close(function (self, msg)
|
||||
assert(msg == 4)
|
||||
-- after error, 'foo' was discarded, so caller now
|
||||
-- must be 'pcall'
|
||||
assert(debug.getinfo(2).name == "pcall")
|
||||
assert(string.find(msg, "@x1"))
|
||||
end)
|
||||
|
||||
local x1 <close> =
|
||||
func2close(function (self, msg)
|
||||
checkwarn("@y")
|
||||
assert(msg == 4)
|
||||
assert(debug.getinfo(2).name == "pcall")
|
||||
assert(string.find(msg, "@y"))
|
||||
error("@x1")
|
||||
end)
|
||||
|
||||
@@ -420,14 +441,15 @@ do print("testing errors in __close")
|
||||
|
||||
local y <close> =
|
||||
func2close(function (self, msg)
|
||||
assert(msg == 4) -- error in body
|
||||
checkwarn("@z")
|
||||
assert(debug.getinfo(2).name == "pcall")
|
||||
assert(string.find(msg, "@z"))
|
||||
error("@y")
|
||||
end)
|
||||
|
||||
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
|
||||
@@ -438,20 +460,20 @@ do print("testing errors in __close")
|
||||
end
|
||||
|
||||
local stat, msg = pcall(foo, true)
|
||||
assert(msg == 4)
|
||||
checkwarn("@x1") -- last error
|
||||
assert(string.find(msg, "@x1"))
|
||||
|
||||
-- error leaving a block
|
||||
local function foo (...)
|
||||
do
|
||||
local x1 <close> =
|
||||
func2close(function ()
|
||||
checkwarn("@X")
|
||||
func2close(function (self, msg)
|
||||
assert(string.find(msg, "@X"))
|
||||
error("@Y")
|
||||
end)
|
||||
|
||||
local x123 <close> =
|
||||
func2close(function ()
|
||||
func2close(function (_, msg)
|
||||
assert(msg == nil)
|
||||
error("@X")
|
||||
end)
|
||||
end
|
||||
@@ -459,9 +481,7 @@ do print("testing errors in __close")
|
||||
end
|
||||
|
||||
local st, msg = xpcall(foo, debug.traceback)
|
||||
assert(string.match(msg, "^[^ ]* @X"))
|
||||
assert(string.find(msg, "in metamethod 'close'"))
|
||||
checkwarn("@Y")
|
||||
assert(string.match(msg, "^[^ ]* @Y"))
|
||||
|
||||
-- error in toclose in vararg function
|
||||
local function foo (...)
|
||||
@@ -471,9 +491,6 @@ do print("testing errors in __close")
|
||||
local st, msg = xpcall(foo, debug.traceback)
|
||||
assert(string.match(msg, "^[^ ]* @x123"))
|
||||
assert(string.find(msg, "in metamethod 'close'"))
|
||||
checkwarn("@x123") -- from second call to close 'x123'
|
||||
|
||||
endwarn()
|
||||
end
|
||||
|
||||
|
||||
@@ -491,14 +508,133 @@ do -- errors due to non-closable values
|
||||
getmetatable(xyz).__close = nil -- remove metamethod
|
||||
end
|
||||
local stat, msg = pcall(foo)
|
||||
assert(not stat and
|
||||
string.find(msg, "attempt to close non%-closable variable 'xyz'"))
|
||||
assert(not stat and string.find(msg, "metamethod 'close'"))
|
||||
|
||||
local function foo ()
|
||||
local a1 <close> = func2close(function (_, msg)
|
||||
assert(string.find(msg, "number value"))
|
||||
error(12)
|
||||
end)
|
||||
local a2 <close> = setmetatable({}, {__close = print})
|
||||
local a3 <close> = func2close(function (_, msg)
|
||||
assert(msg == nil)
|
||||
error(123)
|
||||
end)
|
||||
getmetatable(a2).__close = 4 -- invalidate metamethod
|
||||
end
|
||||
local stat, msg = pcall(foo)
|
||||
assert(not stat and msg == 12)
|
||||
end
|
||||
|
||||
|
||||
do -- tbc inside close methods
|
||||
local track = {}
|
||||
local function foo ()
|
||||
local x <close> = func2close(function ()
|
||||
local xx <close> = func2close(function (_, msg)
|
||||
assert(msg == nil)
|
||||
track[#track + 1] = "xx"
|
||||
end)
|
||||
track[#track + 1] = "x"
|
||||
end)
|
||||
track[#track + 1] = "foo"
|
||||
return 20, 30, 40
|
||||
end
|
||||
local a, b, c, d = foo()
|
||||
assert(a == 20 and b == 30 and c == 40 and d == nil)
|
||||
assert(track[1] == "foo" and track[2] == "x" and track[3] == "xx")
|
||||
|
||||
-- again, with errors
|
||||
local track = {}
|
||||
local function foo ()
|
||||
local x0 <close> = func2close(function (_, msg)
|
||||
assert(msg == 202)
|
||||
track[#track + 1] = "x0"
|
||||
end)
|
||||
local x <close> = func2close(function ()
|
||||
local xx <close> = func2close(function (_, msg)
|
||||
assert(msg == 101)
|
||||
track[#track + 1] = "xx"
|
||||
error(202)
|
||||
end)
|
||||
track[#track + 1] = "x"
|
||||
error(101)
|
||||
end)
|
||||
track[#track + 1] = "foo"
|
||||
return 20, 30, 40
|
||||
end
|
||||
local st, msg = pcall(foo)
|
||||
assert(not st and msg == 202)
|
||||
assert(track[1] == "foo" and track[2] == "x" and track[3] == "xx" and
|
||||
track[4] == "x0")
|
||||
end
|
||||
|
||||
|
||||
local function checktable (t1, t2)
|
||||
assert(#t1 == #t2)
|
||||
for i = 1, #t1 do
|
||||
assert(t1[i] == t2[i])
|
||||
end
|
||||
end
|
||||
|
||||
|
||||
do -- test for tbc variable high in the stack
|
||||
|
||||
-- function to force a stack overflow
|
||||
local function overflow (n)
|
||||
overflow(n + 1)
|
||||
end
|
||||
|
||||
-- error handler will create tbc variable handling a stack overflow,
|
||||
-- high in the stack
|
||||
local function errorh (m)
|
||||
assert(string.find(m, "stack overflow"))
|
||||
local x <close> = func2close(function (o) o[1] = 10 end)
|
||||
return x
|
||||
end
|
||||
|
||||
local flag
|
||||
local st, obj
|
||||
-- run test in a coroutine so as not to swell the main stack
|
||||
local co = coroutine.wrap(function ()
|
||||
-- tbc variable down the stack
|
||||
local y <close> = func2close(function (obj, msg)
|
||||
assert(msg == nil)
|
||||
obj[1] = 100
|
||||
flag = obj
|
||||
end)
|
||||
tracegc.stop()
|
||||
st, obj = xpcall(overflow, errorh, 0)
|
||||
tracegc.start()
|
||||
end)
|
||||
co()
|
||||
assert(not st and obj[1] == 10 and flag[1] == 100)
|
||||
end
|
||||
|
||||
|
||||
if rawget(_G, "T") then
|
||||
|
||||
warn("@off")
|
||||
do
|
||||
-- bug in 5.4.3
|
||||
-- 'lua_settop' may use a pointer to stack invalidated by 'luaF_close'
|
||||
|
||||
-- reduce stack size
|
||||
collectgarbage(); collectgarbage(); collectgarbage()
|
||||
|
||||
-- force a stack reallocation
|
||||
local function loop (n)
|
||||
if n < 400 then loop(n + 1) end
|
||||
end
|
||||
|
||||
-- close metamethod will reallocate the stack
|
||||
local o = setmetatable({}, {__close = function () loop(0) end})
|
||||
|
||||
local script = [[toclose 2; settop 1; return 1]]
|
||||
|
||||
assert(T.testC(script, o) == script)
|
||||
|
||||
end
|
||||
|
||||
|
||||
-- memory error inside closing function
|
||||
local function foo ()
|
||||
@@ -514,33 +650,30 @@ if rawget(_G, "T") then
|
||||
-- despite memory error, 'y' will be executed and
|
||||
-- memory limit will be lifted
|
||||
local _, msg = pcall(foo)
|
||||
assert(msg == 1000)
|
||||
assert(msg == "not enough memory")
|
||||
|
||||
local closemsg
|
||||
local close = func2close(function (self, msg)
|
||||
T.alloccount()
|
||||
assert(msg == "not enough memory")
|
||||
closemsg = msg
|
||||
end)
|
||||
|
||||
-- set a memory limit and return a closing object to remove the limit
|
||||
local function enter (count)
|
||||
stack(10) -- reserve some stack space
|
||||
T.alloccount(count)
|
||||
closemsg = nil
|
||||
return close
|
||||
end
|
||||
|
||||
local function test ()
|
||||
local x <close> = enter(0) -- set a memory limit
|
||||
-- creation of previous upvalue will raise a memory error
|
||||
assert(false) -- should not run
|
||||
local y = {} -- raise a memory error
|
||||
end
|
||||
|
||||
local _, msg = pcall(test)
|
||||
assert(msg == "not enough memory")
|
||||
assert(msg == "not enough memory" and closemsg == "not enough memory")
|
||||
|
||||
-- now use metamethod for closing
|
||||
close = setmetatable({}, {__close = function ()
|
||||
T.alloccount()
|
||||
end})
|
||||
|
||||
-- repeat test with extra closing upvalues
|
||||
local function test ()
|
||||
@@ -552,12 +685,11 @@ if rawget(_G, "T") then
|
||||
assert(msg == "not enough memory");
|
||||
end)
|
||||
local x <close> = enter(0) -- set a memory limit
|
||||
-- creation of previous upvalue will raise a memory error
|
||||
os.exit(false) -- should not run
|
||||
local y = {} -- raise a memory error
|
||||
end
|
||||
|
||||
local _, msg = pcall(test)
|
||||
assert(msg == "not enough memory") -- reported error is the first one
|
||||
assert(msg == 1000 and closemsg == "not enough memory")
|
||||
|
||||
do -- testing 'toclose' in C string buffer
|
||||
collectgarbage()
|
||||
@@ -579,9 +711,9 @@ if rawget(_G, "T") then
|
||||
-- concat this table needs two buffer resizes (one for each 's')
|
||||
local a = {s, s}
|
||||
|
||||
collectgarbage()
|
||||
collectgarbage(); collectgarbage()
|
||||
|
||||
m = T.totalmem()
|
||||
local m = T.totalmem()
|
||||
collectgarbage("stop")
|
||||
|
||||
-- error in the first buffer allocation
|
||||
@@ -602,7 +734,7 @@ if rawget(_G, "T") then
|
||||
-- second buffer was released by 'toclose'
|
||||
assert(T.totalmem() - m <= extra)
|
||||
|
||||
-- userdata, upvalue, buffer, buffer, final string
|
||||
-- userdata, buffer, buffer, final string
|
||||
T.totalmem(m + 4*lim + extra)
|
||||
assert(#table.concat(a) == 2*lim)
|
||||
|
||||
@@ -612,12 +744,244 @@ if rawget(_G, "T") then
|
||||
print'+'
|
||||
end
|
||||
|
||||
warn("@on")
|
||||
|
||||
do
|
||||
-- '__close' vs. return hooks in C functions
|
||||
local trace = {}
|
||||
|
||||
local function hook (event)
|
||||
trace[#trace + 1] = event .. " " .. (debug.getinfo(2).name or "?")
|
||||
end
|
||||
|
||||
-- create tbc variables to be used by C function
|
||||
local x = func2close(function (_,msg)
|
||||
trace[#trace + 1] = "x"
|
||||
end)
|
||||
|
||||
local y = func2close(function (_,msg)
|
||||
trace[#trace + 1] = "y"
|
||||
end)
|
||||
|
||||
debug.sethook(hook, "r")
|
||||
local t = {T.testC([[
|
||||
toclose 2 # x
|
||||
pushnum 10
|
||||
pushint 20
|
||||
toclose 3 # y
|
||||
return 2
|
||||
]], x, y)}
|
||||
debug.sethook()
|
||||
|
||||
-- hooks ran before return hook from 'testC'
|
||||
checktable(trace,
|
||||
{"return sethook", "y", "return ?", "x", "return ?", "return testC"})
|
||||
-- results are correct
|
||||
checktable(t, {10, 20})
|
||||
end
|
||||
end
|
||||
|
||||
|
||||
do -- '__close' vs. return hooks in Lua functions
|
||||
local trace = {}
|
||||
|
||||
local function hook (event)
|
||||
trace[#trace + 1] = event .. " " .. debug.getinfo(2).name
|
||||
end
|
||||
|
||||
local function foo (...)
|
||||
local x <close> = func2close(function (_,msg)
|
||||
trace[#trace + 1] = "x"
|
||||
end)
|
||||
|
||||
local y <close> = func2close(function (_,msg)
|
||||
debug.sethook(hook, "r")
|
||||
end)
|
||||
|
||||
return ...
|
||||
end
|
||||
|
||||
local t = {foo(10,20,30)}
|
||||
debug.sethook()
|
||||
checktable(t, {10, 20, 30})
|
||||
checktable(trace,
|
||||
{"return sethook", "return close", "x", "return close", "return foo"})
|
||||
end
|
||||
|
||||
|
||||
print "to-be-closed variables in coroutines"
|
||||
|
||||
do
|
||||
-- yielding inside closing metamethods
|
||||
|
||||
local trace = {}
|
||||
local co = coroutine.wrap(function ()
|
||||
|
||||
trace[#trace + 1] = "nowX"
|
||||
|
||||
-- will be closed after 'y'
|
||||
local x <close> = func2close(function (_, msg)
|
||||
assert(msg == nil)
|
||||
trace[#trace + 1] = "x1"
|
||||
coroutine.yield("x")
|
||||
trace[#trace + 1] = "x2"
|
||||
end)
|
||||
|
||||
return pcall(function ()
|
||||
do -- 'z' will be closed first
|
||||
local z <close> = func2close(function (_, msg)
|
||||
assert(msg == nil)
|
||||
trace[#trace + 1] = "z1"
|
||||
coroutine.yield("z")
|
||||
trace[#trace + 1] = "z2"
|
||||
end)
|
||||
end
|
||||
|
||||
trace[#trace + 1] = "nowY"
|
||||
|
||||
-- will be closed after 'z'
|
||||
local y <close> = func2close(function(_, msg)
|
||||
assert(msg == nil)
|
||||
trace[#trace + 1] = "y1"
|
||||
coroutine.yield("y")
|
||||
trace[#trace + 1] = "y2"
|
||||
end)
|
||||
|
||||
return 10, 20, 30
|
||||
end)
|
||||
end)
|
||||
|
||||
assert(co() == "z")
|
||||
assert(co() == "y")
|
||||
assert(co() == "x")
|
||||
checktable({co()}, {true, 10, 20, 30})
|
||||
checktable(trace, {"nowX", "z1", "z2", "nowY", "y1", "y2", "x1", "x2"})
|
||||
|
||||
end
|
||||
|
||||
|
||||
do
|
||||
-- yielding inside closing metamethods while returning
|
||||
-- (bug in 5.4.3)
|
||||
|
||||
local extrares -- result from extra yield (if any)
|
||||
|
||||
local function check (body, extra, ...)
|
||||
local t = table.pack(...) -- expected returns
|
||||
local co = coroutine.wrap(body)
|
||||
if extra then
|
||||
extrares = co() -- runs until first (extra) yield
|
||||
end
|
||||
local res = table.pack(co()) -- runs until yield inside '__close'
|
||||
assert(res.n == 2 and res[2] == nil)
|
||||
local res2 = table.pack(co()) -- runs until end of function
|
||||
assert(res2.n == t.n)
|
||||
for i = 1, #t do
|
||||
if t[i] == "x" then
|
||||
assert(res2[i] == res[1]) -- value that was closed
|
||||
else
|
||||
assert(res2[i] == t[i])
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
local function foo ()
|
||||
local x <close> = func2close(coroutine.yield)
|
||||
local extra <close> = func2close(function (self)
|
||||
assert(self == extrares)
|
||||
coroutine.yield(100)
|
||||
end)
|
||||
extrares = extra
|
||||
return table.unpack{10, x, 30}
|
||||
end
|
||||
check(foo, true, 10, "x", 30)
|
||||
assert(extrares == 100)
|
||||
|
||||
local function foo ()
|
||||
local x <close> = func2close(coroutine.yield)
|
||||
return
|
||||
end
|
||||
check(foo, false)
|
||||
|
||||
local function foo ()
|
||||
local x <close> = func2close(coroutine.yield)
|
||||
local y, z = 20, 30
|
||||
return x
|
||||
end
|
||||
check(foo, false, "x")
|
||||
|
||||
local function foo ()
|
||||
local x <close> = func2close(coroutine.yield)
|
||||
local extra <close> = func2close(coroutine.yield)
|
||||
return table.unpack({}, 1, 100) -- 100 nils
|
||||
end
|
||||
check(foo, true, table.unpack({}, 1, 100))
|
||||
|
||||
end
|
||||
|
||||
do
|
||||
-- yielding inside closing metamethods after an error
|
||||
|
||||
local co = coroutine.wrap(function ()
|
||||
|
||||
local function foo (err)
|
||||
|
||||
local z <close> = func2close(function(_, msg)
|
||||
assert(msg == nil or msg == err + 20)
|
||||
coroutine.yield("z")
|
||||
return 100, 200
|
||||
end)
|
||||
|
||||
local y <close> = func2close(function(_, msg)
|
||||
-- still gets the original error (if any)
|
||||
assert(msg == err or (msg == nil and err == 1))
|
||||
coroutine.yield("y")
|
||||
if err then error(err + 20) end -- creates or changes the error
|
||||
end)
|
||||
|
||||
local x <close> = func2close(function(_, msg)
|
||||
assert(msg == err or (msg == nil and err == 1))
|
||||
coroutine.yield("x")
|
||||
return 100, 200
|
||||
end)
|
||||
|
||||
if err == 10 then error(err) else return 10, 20 end
|
||||
end
|
||||
|
||||
coroutine.yield(pcall(foo, nil)) -- no error
|
||||
coroutine.yield(pcall(foo, 1)) -- error in __close
|
||||
return pcall(foo, 10) -- 'foo' will raise an error
|
||||
end)
|
||||
|
||||
local a, b = co() -- first foo: no error
|
||||
assert(a == "x" and b == nil) -- yields inside 'x'; Ok
|
||||
a, b = co()
|
||||
assert(a == "y" and b == nil) -- yields inside 'y'; Ok
|
||||
a, b = co()
|
||||
assert(a == "z" and b == nil) -- yields inside 'z'; Ok
|
||||
local a, b, c = co()
|
||||
assert(a and b == 10 and c == 20) -- returns from 'pcall(foo, nil)'
|
||||
|
||||
local a, b = co() -- second foo: error in __close
|
||||
assert(a == "x" and b == nil) -- yields inside 'x'; Ok
|
||||
a, b = co()
|
||||
assert(a == "y" and b == nil) -- yields inside 'y'; Ok
|
||||
a, b = co()
|
||||
assert(a == "z" and b == nil) -- yields inside 'z'; Ok
|
||||
local st, msg = co() -- reports the error in 'y'
|
||||
assert(not st and msg == 21)
|
||||
|
||||
local a, b = co() -- third foo: error in function body
|
||||
assert(a == "x" and b == nil) -- yields inside 'x'; Ok
|
||||
a, b = co()
|
||||
assert(a == "y" and b == nil) -- yields inside 'y'; Ok
|
||||
a, b = co()
|
||||
assert(a == "z" and b == nil) -- yields inside 'z'; Ok
|
||||
local st, msg = co() -- gets final error
|
||||
assert(not st and msg == 10 + 20)
|
||||
|
||||
end
|
||||
|
||||
|
||||
do
|
||||
-- an error in a wrapped coroutine closes variables
|
||||
local x = false
|
||||
@@ -642,37 +1006,43 @@ end
|
||||
|
||||
|
||||
do
|
||||
prepwarn()
|
||||
|
||||
-- error in a wrapped coroutine raising errors when closing a variable
|
||||
local x = 0
|
||||
local co = coroutine.wrap(function ()
|
||||
local xx <close> = func2close(function () x = x + 1; error("@YYY") end)
|
||||
local xx <close> = func2close(function (_, msg)
|
||||
x = x + 1;
|
||||
assert(string.find(msg, "@XXX"))
|
||||
error("@YYY")
|
||||
end)
|
||||
local xv <close> = func2close(function () x = x + 1; error("@XXX") end)
|
||||
coroutine.yield(100)
|
||||
error(200)
|
||||
coroutine.yield(100)
|
||||
error(200)
|
||||
end)
|
||||
assert(co() == 100); assert(x == 0)
|
||||
local st, msg = pcall(co); assert(x == 2)
|
||||
assert(not st and msg == 200) -- should get first error raised
|
||||
checkwarn("@YYY")
|
||||
assert(not st and string.find(msg, "@YYY")) -- should get error raised
|
||||
|
||||
local x = 0
|
||||
local y = 0
|
||||
co = coroutine.wrap(function ()
|
||||
local xx <close> = func2close(function () y = y + 1; error("YYY") end)
|
||||
local xv <close> = func2close(function () x = x + 1; error("XXX") end)
|
||||
coroutine.yield(100)
|
||||
return 200
|
||||
local xx <close> = func2close(function (_, err)
|
||||
y = y + 1;
|
||||
assert(string.find(err, "XXX"))
|
||||
error("YYY")
|
||||
end)
|
||||
local xv <close> = func2close(function ()
|
||||
x = x + 1; error("XXX")
|
||||
end)
|
||||
coroutine.yield(100)
|
||||
return 200
|
||||
end)
|
||||
assert(co() == 100); assert(x == 0)
|
||||
local st, msg = pcall(co)
|
||||
assert(x == 2 and y == 1) -- first close is called twice
|
||||
assert(x == 1 and y == 1)
|
||||
-- should get first error raised
|
||||
assert(not st and string.find(msg, "%w+%.%w+:%d+: XXX"))
|
||||
checkwarn("YYY")
|
||||
assert(not st and string.find(msg, "%w+%.%w+:%d+: YYY"))
|
||||
|
||||
endwarn()
|
||||
end
|
||||
|
||||
|
||||
@@ -689,6 +1059,81 @@ assert(co == nil) -- eventually it will be collected
|
||||
collectgarbage()
|
||||
|
||||
|
||||
if rawget(_G, "T") then
|
||||
print("to-be-closed variables x coroutines in C")
|
||||
do
|
||||
local token = 0
|
||||
local count = 0
|
||||
local f = T.makeCfunc[[
|
||||
toclose 1
|
||||
toclose 2
|
||||
return .
|
||||
]]
|
||||
|
||||
local obj = func2close(function (_, msg)
|
||||
count = count + 1
|
||||
token = coroutine.yield(count, token)
|
||||
end)
|
||||
|
||||
local co = coroutine.wrap(f)
|
||||
local ct, res = co(obj, obj, 10, 20, 30, 3) -- will return 10, 20, 30
|
||||
-- initial token value, after closing 2nd obj
|
||||
assert(ct == 1 and res == 0)
|
||||
-- run until yield when closing 1st obj
|
||||
ct, res = co(100)
|
||||
assert(ct == 2 and res == 100)
|
||||
res = {co(200)} -- run until end
|
||||
assert(res[1] == 10 and res[2] == 20 and res[3] == 30 and res[4] == nil)
|
||||
assert(token == 200)
|
||||
end
|
||||
|
||||
do
|
||||
local f = T.makeCfunc[[
|
||||
toclose 1
|
||||
return .
|
||||
]]
|
||||
|
||||
local obj = func2close(function ()
|
||||
local temp
|
||||
local x <close> = func2close(function ()
|
||||
coroutine.yield(temp)
|
||||
return 1,2,3 -- to be ignored
|
||||
end)
|
||||
temp = coroutine.yield("closing obj")
|
||||
return 1,2,3 -- to be ignored
|
||||
end)
|
||||
|
||||
local co = coroutine.wrap(f)
|
||||
local res = co(obj, 10, 30, 1) -- will return only 30
|
||||
assert(res == "closing obj")
|
||||
res = co("closing x")
|
||||
assert(res == "closing x")
|
||||
res = {co()}
|
||||
assert(res[1] == 30 and res[2] == nil)
|
||||
end
|
||||
|
||||
do
|
||||
-- still cannot yield inside 'closeslot'
|
||||
local f = T.makeCfunc[[
|
||||
toclose 1
|
||||
closeslot 1
|
||||
]]
|
||||
local obj = func2close(coroutine.yield)
|
||||
local co = coroutine.create(f)
|
||||
local st, msg = coroutine.resume(co, obj)
|
||||
assert(not st and string.find(msg, "attempt to yield across"))
|
||||
|
||||
-- nor outside a coroutine
|
||||
local f = T.makeCfunc[[
|
||||
toclose 1
|
||||
]]
|
||||
local st, msg = pcall(f, obj)
|
||||
assert(not st and string.find(msg, "attempt to yield from outside"))
|
||||
end
|
||||
end
|
||||
|
||||
|
||||
|
||||
-- to-be-closed variables in generic for loops
|
||||
do
|
||||
local numopen = 0
|
||||
|
||||
@@ -94,6 +94,33 @@ RUN('echo "print(10)\nprint(2)\n" | lua > %s', out)
|
||||
checkout("10\n2\n")
|
||||
|
||||
|
||||
-- testing BOM
|
||||
prepfile("\xEF\xBB\xBF")
|
||||
RUN('lua %s > %s', prog, out)
|
||||
checkout("")
|
||||
|
||||
prepfile("\xEF\xBB\xBFprint(3)")
|
||||
RUN('lua %s > %s', prog, out)
|
||||
checkout("3\n")
|
||||
|
||||
prepfile("\xEF\xBB\xBF# comment!!\nprint(3)")
|
||||
RUN('lua %s > %s', prog, out)
|
||||
checkout("3\n")
|
||||
|
||||
-- bad BOMs
|
||||
prepfile("\xEF")
|
||||
NoRun("unexpected symbol", 'lua %s > %s', prog, out)
|
||||
|
||||
prepfile("\xEF\xBB")
|
||||
NoRun("unexpected symbol", 'lua %s > %s', prog, out)
|
||||
|
||||
prepfile("\xEFprint(3)")
|
||||
NoRun("unexpected symbol", 'lua %s > %s', prog, out)
|
||||
|
||||
prepfile("\xEF\xBBprint(3)")
|
||||
NoRun("unexpected symbol", 'lua %s > %s', prog, out)
|
||||
|
||||
|
||||
-- test option '-'
|
||||
RUN('echo "print(arg[1])" | lua - -h > %s', out)
|
||||
checkout("-h\n")
|
||||
@@ -190,6 +217,11 @@ prepfile(("print(a); print(_G['%s'].x)"):format(prog), otherprog)
|
||||
RUN('env LUA_PATH="?;;" lua -l %s -l%s -lstring -l io %s > %s', prog, otherprog, otherprog, out)
|
||||
checkout("1\n2\n15\n2\n15\n")
|
||||
|
||||
-- test explicit global names in -l
|
||||
prepfile("print(str.upper'alo alo', m.max(10, 20))")
|
||||
RUN("lua -l 'str=string' '-lm=math' -e 'print(m.sin(0))' %s > %s", prog, out)
|
||||
checkout("0.0\nALO ALO\t20\n")
|
||||
|
||||
-- test 'arg' table
|
||||
local a = [[
|
||||
assert(#arg == 3 and arg[1] == 'a' and
|
||||
@@ -256,6 +288,34 @@ u2 = setmetatable({}, {__gc = function () error("ZYX") end})
|
||||
RUN('lua -W %s 2> %s', prog, out)
|
||||
checkprogout("ZYX)\nXYZ)\n")
|
||||
|
||||
-- bug since 5.2: finalizer called when closing a state could
|
||||
-- subvert finalization order
|
||||
prepfile[[
|
||||
-- should be called last
|
||||
print("creating 1")
|
||||
setmetatable({}, {__gc = function () print(1) end})
|
||||
|
||||
print("creating 2")
|
||||
setmetatable({}, {__gc = function ()
|
||||
print("2")
|
||||
print("creating 3")
|
||||
-- this finalizer should not be called, as object will be
|
||||
-- created after 'lua_close' has been called
|
||||
setmetatable({}, {__gc = function () print(3) end})
|
||||
print(collectgarbage()) -- cannot call collector here
|
||||
os.exit(0, true)
|
||||
end})
|
||||
]]
|
||||
RUN('lua -W %s > %s', prog, out)
|
||||
checkout[[
|
||||
creating 1
|
||||
creating 2
|
||||
2
|
||||
creating 3
|
||||
nil
|
||||
1
|
||||
]]
|
||||
|
||||
|
||||
-- test many arguments
|
||||
prepfile[[print(({...})[30])]]
|
||||
@@ -279,7 +339,7 @@ prepfile("a = [[b\nc\nd\ne]]\n=a")
|
||||
RUN([[lua -e"_PROMPT='' _PROMPT2=''" -i < %s > %s]], prog, out)
|
||||
checkprogout("b\nc\nd\ne\n\n")
|
||||
|
||||
prompt = "alo"
|
||||
local prompt = "alo"
|
||||
prepfile[[ --
|
||||
a = 2
|
||||
]]
|
||||
@@ -287,6 +347,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"
|
||||
@@ -303,7 +390,7 @@ NoRun("error object is a table value", [[lua %s]], prog)
|
||||
|
||||
|
||||
-- chunk broken in many lines
|
||||
s = [=[ --
|
||||
local s = [=[ --
|
||||
function f ( x )
|
||||
local a = [[
|
||||
xuxu
|
||||
@@ -325,12 +412,10 @@ checkprogout("101\n13\t22\n\n")
|
||||
prepfile[[#comment in 1st line without \n at the end]]
|
||||
RUN('lua %s', prog)
|
||||
|
||||
prepfile[[#test line number when file starts with comment line
|
||||
debug = require"debug"
|
||||
print(debug.getinfo(1).currentline)
|
||||
]]
|
||||
-- first-line comment with binary file
|
||||
prepfile("#comment\n" .. string.dump(load("print(3)")))
|
||||
RUN('lua %s > %s', prog, out)
|
||||
checkprogout('3\n')
|
||||
checkout('3\n')
|
||||
|
||||
-- close Lua with an open file
|
||||
prepfile(string.format([[io.output(%q); io.write('alo')]], out))
|
||||
|
||||
@@ -50,7 +50,7 @@ end
|
||||
local msgf2i = "number.* has no integer representation"
|
||||
|
||||
-- float equality
|
||||
function eq (a,b,limit)
|
||||
local function eq (a,b,limit)
|
||||
if not limit then
|
||||
if floatbits >= 50 then limit = 1E-11
|
||||
else limit = 1E-5
|
||||
@@ -62,7 +62,7 @@ end
|
||||
|
||||
|
||||
-- equality with types
|
||||
function eqT (a,b)
|
||||
local function eqT (a,b)
|
||||
return a == b and math.type(a) == math.type(b)
|
||||
end
|
||||
|
||||
@@ -83,7 +83,7 @@ end
|
||||
do
|
||||
local x = -1
|
||||
local mz = 0/x -- minus zero
|
||||
t = {[0] = 10, 20, 30, 40, 50}
|
||||
local t = {[0] = 10, 20, 30, 40, 50}
|
||||
assert(t[mz] == t[0] and t[-0] == t[0])
|
||||
end
|
||||
|
||||
@@ -849,6 +849,7 @@ do
|
||||
math.randomseed(x, y) -- again should repeat the state
|
||||
assert(math.random(0) == res)
|
||||
-- keep the random seed for following tests
|
||||
print(string.format("random seeds: %d, %d", x, y))
|
||||
end
|
||||
|
||||
do -- test random for floats
|
||||
|
||||
@@ -9,6 +9,16 @@ local function checkerror (msg, f, ...)
|
||||
end
|
||||
|
||||
|
||||
local function check (t, na, nh)
|
||||
if not T then return end
|
||||
local a, h = T.querytab(t)
|
||||
if a ~= na or h ~= nh then
|
||||
print(na, nh, a, h)
|
||||
assert(nil)
|
||||
end
|
||||
end
|
||||
|
||||
|
||||
local a = {}
|
||||
|
||||
-- make sure table has lots of space in hash part
|
||||
@@ -20,6 +30,25 @@ for i=1,100 do
|
||||
assert(#a == i)
|
||||
end
|
||||
|
||||
|
||||
do -- rehash moving elements from array to hash
|
||||
local a = {}
|
||||
for i = 1, 100 do a[i] = i end
|
||||
check(a, 128, 0)
|
||||
|
||||
for i = 5, 95 do a[i] = nil end
|
||||
check(a, 128, 0)
|
||||
|
||||
a.x = 1 -- force a re-hash
|
||||
check(a, 4, 8)
|
||||
|
||||
for i = 1, 4 do assert(a[i] == i) end
|
||||
for i = 5, 95 do assert(a[i] == nil) end
|
||||
for i = 96, 100 do assert(a[i] == i) end
|
||||
assert(a.x == 1)
|
||||
end
|
||||
|
||||
|
||||
-- testing ipairs
|
||||
local x = 0
|
||||
for k,v in ipairs{10,20,30;x=12} do
|
||||
@@ -43,6 +72,14 @@ assert(i == 4)
|
||||
assert(type(ipairs{}) == 'function' and ipairs{} == ipairs{})
|
||||
|
||||
|
||||
do -- overflow (must wrap-around)
|
||||
local f = ipairs{}
|
||||
local k, v = f({[math.mininteger] = 10}, math.maxinteger)
|
||||
assert(k == math.mininteger and v == 10)
|
||||
k, v = f({[math.mininteger] = 10}, k)
|
||||
assert(k == nil)
|
||||
end
|
||||
|
||||
if not T then
|
||||
(Message or print)
|
||||
('\n >>> testC not active: skipping tests for table sizes <<<\n')
|
||||
@@ -57,15 +94,6 @@ local function mp2 (n) -- minimum power of 2 >= n
|
||||
end
|
||||
|
||||
|
||||
local function check (t, na, nh)
|
||||
local a, h = T.querytab(t)
|
||||
if a ~= na or h ~= nh then
|
||||
print(na, nh, a, h)
|
||||
assert(nil)
|
||||
end
|
||||
end
|
||||
|
||||
|
||||
-- testing C library sizes
|
||||
do
|
||||
local s = 0
|
||||
@@ -161,7 +189,7 @@ end
|
||||
|
||||
-- size tests for vararg
|
||||
lim = 35
|
||||
function foo (n, ...)
|
||||
local function foo (n, ...)
|
||||
local arg = {...}
|
||||
check(arg, n, 0)
|
||||
assert(select('#', ...) == n)
|
||||
@@ -359,6 +387,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);
|
||||
@@ -467,6 +527,15 @@ do -- testing table library with metamethods
|
||||
end
|
||||
|
||||
|
||||
do -- testing overflow in table.insert (must wrap-around)
|
||||
|
||||
local t = setmetatable({},
|
||||
{__len = function () return math.maxinteger end})
|
||||
table.insert(t, 20)
|
||||
local k, v = next(t)
|
||||
assert(k == math.mininteger and v == 20)
|
||||
end
|
||||
|
||||
if not T then
|
||||
(Message or print)
|
||||
('\n >>> testC not active: skipping tests for table library on non-tables <<<\n')
|
||||
@@ -732,4 +801,25 @@ for k,v in ipairs(a) do
|
||||
end
|
||||
assert(i == a.n)
|
||||
|
||||
|
||||
-- testing yield inside __pairs
|
||||
do
|
||||
local t = setmetatable({10, 20, 30}, {__pairs = function (t)
|
||||
local inc = coroutine.yield()
|
||||
return function (t, i)
|
||||
if i > 1 then return i - inc, t[i - inc] else return nil end
|
||||
end, t, #t + 1
|
||||
end})
|
||||
|
||||
local res = {}
|
||||
local co = coroutine.wrap(function ()
|
||||
for i,p in pairs(t) do res[#res + 1] = p end
|
||||
end)
|
||||
|
||||
co() -- start coroutine
|
||||
co(1) -- continue after yield
|
||||
assert(res[1] == 30 and res[2] == 20 and res[3] == 10 and #res == 3)
|
||||
|
||||
end
|
||||
|
||||
print"OK"
|
||||
|
||||
@@ -33,6 +33,7 @@ $NAME/pm.lua \
|
||||
$NAME/sort.lua \
|
||||
$NAME/strings.lua \
|
||||
$NAME/tpack.lua \
|
||||
$NAME/tracegc.lua \
|
||||
$NAME/utf8.lua \
|
||||
$NAME/vararg.lua \
|
||||
$NAME/verybig.lua \
|
||||
|
||||
@@ -9,12 +9,12 @@ local function checkerror (msg, f, ...)
|
||||
end
|
||||
|
||||
|
||||
function f(s, p)
|
||||
local function f (s, p)
|
||||
local i,e = string.find(s, p)
|
||||
if i then return string.sub(s, i, e) end
|
||||
end
|
||||
|
||||
a,b = string.find('', '') -- empty patterns are tricky
|
||||
local a,b = string.find('', '') -- empty patterns are tricky
|
||||
assert(a == 1 and b == 0);
|
||||
a,b = string.find('alo', '')
|
||||
assert(a == 1 and b == 0)
|
||||
@@ -88,7 +88,7 @@ assert(f("alo alo", "%C+") == "alo alo")
|
||||
print('+')
|
||||
|
||||
|
||||
function f1(s, p)
|
||||
local function f1 (s, p)
|
||||
p = string.gsub(p, "%%([0-9])", function (s)
|
||||
return "%" .. (tonumber(s)+1)
|
||||
end)
|
||||
@@ -113,7 +113,7 @@ local abc = string.char(range(0, 127)) .. string.char(range(128, 255));
|
||||
|
||||
assert(string.len(abc) == 256)
|
||||
|
||||
function strset (p)
|
||||
local function strset (p)
|
||||
local res = {s=''}
|
||||
string.gsub(abc, p, function (c) res.s = res.s .. c end)
|
||||
return res.s
|
||||
@@ -147,7 +147,7 @@ assert(string.gsub('
|
||||
assert(string.gsub('alo úlo ', ' +$', '') == 'alo úlo') -- trim
|
||||
assert(string.gsub(' alo alo ', '^%s*(.-)%s*$', '%1') == 'alo alo') -- double trim
|
||||
assert(string.gsub('alo alo \n 123\n ', '%s+', ' ') == 'alo alo 123 ')
|
||||
t = "abç d"
|
||||
local t = "abç d"
|
||||
a, b = string.gsub(t, '(.)', '%1@')
|
||||
assert('@'..a == string.gsub(t, '', '@') and b == 5)
|
||||
a, b = string.gsub('abçd', '(.)', '%0@', 2)
|
||||
@@ -184,6 +184,7 @@ do
|
||||
local function setglobal (n,v) rawset(_G, n, v) end
|
||||
string.gsub("a=roberto,roberto=a", "(%w+)=(%w%w*)", setglobal)
|
||||
assert(_G.a=="roberto" and _G.roberto=="a")
|
||||
_G.a = nil; _G.roberto = nil
|
||||
end
|
||||
|
||||
function f(a,b) return string.gsub(a,'.',b) end
|
||||
@@ -195,20 +196,21 @@ assert(string.gsub("alo $a='x'$ novamente $return a$",
|
||||
"$([^$]*)%$",
|
||||
dostring) == "alo novamente x")
|
||||
|
||||
x = string.gsub("$x=string.gsub('alo', '.', string.upper)$ assim vai para $return x$",
|
||||
local x = string.gsub("$x=string.gsub('alo', '.', string.upper)$ assim vai para $return x$",
|
||||
"$([^$]*)%$", dostring)
|
||||
assert(x == ' assim vai para ALO')
|
||||
_G.a, _G.x = nil
|
||||
|
||||
t = {}
|
||||
s = 'a alo jose joao'
|
||||
r = string.gsub(s, '()(%w+)()', function (a,w,b)
|
||||
assert(string.len(w) == b-a);
|
||||
t[a] = b-a;
|
||||
end)
|
||||
local t = {}
|
||||
local s = 'a alo jose joao'
|
||||
local r = string.gsub(s, '()(%w+)()', function (a,w,b)
|
||||
assert(string.len(w) == b-a);
|
||||
t[a] = b-a;
|
||||
end)
|
||||
assert(s == r and t[1] == 1 and t[3] == 3 and t[7] == 4 and t[13] == 4)
|
||||
|
||||
|
||||
function isbalanced (s)
|
||||
local function isbalanced (s)
|
||||
return not string.find(string.gsub(s, "%b()", ""), "[()]")
|
||||
end
|
||||
|
||||
@@ -251,7 +253,7 @@ if not _soft then
|
||||
end
|
||||
|
||||
-- recursive nest of gsubs
|
||||
function rev (s)
|
||||
local function rev (s)
|
||||
return string.gsub(s, "(.)(.+)", function (c,s1) return rev(s1)..c end)
|
||||
end
|
||||
|
||||
|
||||
@@ -20,7 +20,7 @@ end
|
||||
checkerror("wrong number of arguments", table.insert, {}, 2, 3, 4)
|
||||
|
||||
local x,y,z,a,n
|
||||
a = {}; lim = _soft and 200 or 2000
|
||||
a = {}; local lim = _soft and 200 or 2000
|
||||
for i=1, lim do a[i]=i end
|
||||
assert(select(lim, unpack(a)) == lim and select('#', unpack(a)) == lim)
|
||||
x = unpack(a)
|
||||
@@ -222,7 +222,7 @@ a = {"Jan", "Feb", "Mar", "Apr", "May", "Jun", "Jul", "Aug", "Sep",
|
||||
table.sort(a)
|
||||
check(a)
|
||||
|
||||
function perm (s, n)
|
||||
local function perm (s, n)
|
||||
n = n or #s
|
||||
if n == 1 then
|
||||
local t = {unpack(s)}
|
||||
@@ -248,7 +248,7 @@ perm{1,2,3,3,5}
|
||||
perm{1,2,3,4,5,6}
|
||||
perm{2,2,3,3,5,6}
|
||||
|
||||
function timesort (a, n, func, msg, pre)
|
||||
local function timesort (a, n, func, msg, pre)
|
||||
local x = os.clock()
|
||||
table.sort(a, func)
|
||||
x = (os.clock() - x) * 1000
|
||||
@@ -257,7 +257,7 @@ function timesort (a, n, func, msg, pre)
|
||||
check(a, func)
|
||||
end
|
||||
|
||||
limit = 50000
|
||||
local limit = 50000
|
||||
if _soft then limit = 5000 end
|
||||
|
||||
a = {}
|
||||
@@ -274,7 +274,7 @@ for i=1,limit do
|
||||
a[i] = math.random()
|
||||
end
|
||||
|
||||
x = os.clock(); i=0
|
||||
local x = os.clock(); local i = 0
|
||||
table.sort(a, function(x,y) i=i+1; return y<x end)
|
||||
x = (os.clock() - x) * 1000
|
||||
print(string.format("Invert-sorting other %d elements in %.2f msec., with %i comparisons",
|
||||
@@ -289,18 +289,19 @@ timesort(a, limit, function(x,y) return nil end, "equal")
|
||||
|
||||
for i,v in pairs(a) do assert(v == false) end
|
||||
|
||||
A = {"álo", "\0first :-)", "alo", "then this one", "45", "and a new"}
|
||||
table.sort(A)
|
||||
check(A)
|
||||
AA = {"álo", "\0first :-)", "alo", "then this one", "45", "and a new"}
|
||||
table.sort(AA)
|
||||
check(AA)
|
||||
|
||||
table.sort(A, function (x, y)
|
||||
load(string.format("A[%q] = ''", x), "")()
|
||||
table.sort(AA, function (x, y)
|
||||
load(string.format("AA[%q] = ''", x), "")()
|
||||
collectgarbage()
|
||||
return x<y
|
||||
end)
|
||||
|
||||
_G.AA = nil
|
||||
|
||||
tt = {__lt = function (a,b) return a.val < b.val end}
|
||||
local tt = {__lt = function (a,b) return a.val < b.val end}
|
||||
a = {}
|
||||
for i=1,10 do a[i] = {val=math.random(100)}; setmetatable(a[i], tt); end
|
||||
table.sort(a)
|
||||
|
||||
@@ -52,7 +52,7 @@ assert(("\000123456789"):sub(8) == "789")
|
||||
|
||||
-- testing string.find
|
||||
assert(string.find("123456789", "345") == 3)
|
||||
a,b = string.find("123456789", "345")
|
||||
local a,b = string.find("123456789", "345")
|
||||
assert(string.sub("123456789", a, b) == "345")
|
||||
assert(string.find("1234567890123456789", "345", 3) == 3)
|
||||
assert(string.find("1234567890123456789", "345", 4) == 13)
|
||||
@@ -192,7 +192,7 @@ do -- tests for '%p' format
|
||||
end
|
||||
end
|
||||
|
||||
x = '"ílo"\n\\'
|
||||
local x = '"ílo"\n\\'
|
||||
assert(string.format('%q%s', x, x) == '"\\"ílo\\"\\\n\\\\""ílo"\n\\')
|
||||
assert(string.format('%q', "\0") == [["\0"]])
|
||||
assert(load(string.format('return %q', x))() == x)
|
||||
@@ -202,13 +202,11 @@ assert(string.format("\0%c\0%c%x\0", string.byte("\xe4"), string.byte("b"), 140)
|
||||
"\0\xe4\0b8c\0")
|
||||
assert(string.format('') == "")
|
||||
assert(string.format("%c",34)..string.format("%c",48)..string.format("%c",90)..string.format("%c",100) ==
|
||||
string.format("%c%c%c%c", 34, 48, 90, 100))
|
||||
string.format("%1c%-c%-1c%c", 34, 48, 90, 100))
|
||||
assert(string.format("%s\0 is not \0%s", 'not be', 'be') == 'not be\0 is not \0be')
|
||||
assert(string.format("%%%d %010d", 10, 23) == "%10 0000000023")
|
||||
assert(tonumber(string.format("%f", 10.3)) == 10.3)
|
||||
x = string.format('"%-50s"', 'a')
|
||||
assert(#x == 52)
|
||||
assert(string.sub(x, 1, 4) == '"a ')
|
||||
assert(string.format('"%-50s"', 'a') == '"a' .. string.rep(' ', 49) .. '"')
|
||||
|
||||
assert(string.format("-%.20s.20s", string.rep("%", 2000)) ==
|
||||
"-"..string.rep("%", 20)..".20s")
|
||||
@@ -237,7 +235,6 @@ end
|
||||
|
||||
assert(string.format("\0%s\0", "\0\0\1") == "\0\0\0\1\0")
|
||||
checkerror("contains zeros", string.format, "%10s", "\0")
|
||||
checkerror("cannot have modifiers", string.format, "%10q", "1")
|
||||
|
||||
-- format x tostring
|
||||
assert(string.format("%s %s", nil, true) == "nil true")
|
||||
@@ -341,6 +338,26 @@ do print("testing 'format %a %A'")
|
||||
end
|
||||
|
||||
|
||||
-- testing some flags (all these results are required by ISO C)
|
||||
assert(string.format("%#12o", 10) == " 012")
|
||||
assert(string.format("%#10x", 100) == " 0x64")
|
||||
assert(string.format("%#-17X", 100) == "0X64 ")
|
||||
assert(string.format("%013i", -100) == "-000000000100")
|
||||
assert(string.format("%2.5d", -100) == "-00100")
|
||||
assert(string.format("%.u", 0) == "")
|
||||
assert(string.format("%+#014.0f", 100) == "+000000000100.")
|
||||
assert(string.format("%-16c", 97) == "a ")
|
||||
assert(string.format("%+.3G", 1.5) == "+1.5")
|
||||
assert(string.format("%.0s", "alo") == "")
|
||||
assert(string.format("%.s", "alo") == "")
|
||||
|
||||
-- ISO C89 says that "The exponent always contains at least two digits",
|
||||
-- but unlike ISO C99 it does not ensure that it contains "only as many
|
||||
-- more digits as necessary".
|
||||
assert(string.match(string.format("% 1.0E", 100), "^ 1E%+0+2$"))
|
||||
assert(string.match(string.format("% .1g", 2^10), "^ 1e%+0+3$"))
|
||||
|
||||
|
||||
-- errors in format
|
||||
|
||||
local function check (fmt, msg)
|
||||
@@ -348,19 +365,30 @@ local function check (fmt, msg)
|
||||
end
|
||||
|
||||
local aux = string.rep('0', 600)
|
||||
check("%100.3d", "too long")
|
||||
check("%100.3d", "invalid conversion")
|
||||
check("%1"..aux..".3d", "too long")
|
||||
check("%1.100d", "too long")
|
||||
check("%1.100d", "invalid conversion")
|
||||
check("%10.1"..aux.."004d", "too long")
|
||||
check("%t", "invalid conversion")
|
||||
check("%"..aux.."d", "repeated flags")
|
||||
check("%"..aux.."d", "too long")
|
||||
check("%d %d", "no value")
|
||||
check("%010c", "invalid conversion")
|
||||
check("%.10c", "invalid conversion")
|
||||
check("%0.34s", "invalid conversion")
|
||||
check("%#i", "invalid conversion")
|
||||
check("%3.1p", "invalid conversion")
|
||||
check("%0.s", "invalid conversion")
|
||||
check("%10q", "cannot have modifiers")
|
||||
check("%F", "invalid conversion") -- useless and not in C89
|
||||
|
||||
|
||||
assert(load("return 1\n--comment without ending EOL")() == 1)
|
||||
|
||||
|
||||
checkerror("table expected", table.concat, 3)
|
||||
checkerror("at index " .. maxi, table.concat, {}, " ", maxi, maxi)
|
||||
-- '%' escapes following minus signal
|
||||
checkerror("at index %" .. mini, table.concat, {}, " ", mini, mini)
|
||||
assert(table.concat{} == "")
|
||||
assert(table.concat({}, 'x') == "")
|
||||
assert(table.concat({'\0', '\0\1', '\0\1\2'}, '.\0.') == "\0.\0.\0\1.\0.\0\1\2")
|
||||
@@ -424,7 +452,7 @@ end
|
||||
do
|
||||
local f = string.gmatch("1 2 3 4 5", "%d+")
|
||||
assert(f() == "1")
|
||||
co = coroutine.wrap(f)
|
||||
local co = coroutine.wrap(f)
|
||||
assert(co() == "2")
|
||||
end
|
||||
|
||||
|
||||
@@ -35,7 +35,7 @@ print("\talignment: " .. align)
|
||||
|
||||
|
||||
-- check errors in arguments
|
||||
function checkerror (msg, f, ...)
|
||||
local function checkerror (msg, f, ...)
|
||||
local status, err = pcall(f, ...)
|
||||
-- print(status, err, msg)
|
||||
assert(not status and string.find(err, msg))
|
||||
|
||||
40
testes/tracegc.lua
Normal file
40
testes/tracegc.lua
Normal file
@@ -0,0 +1,40 @@
|
||||
-- track collections
|
||||
|
||||
local M = {}
|
||||
|
||||
-- import list
|
||||
local setmetatable, stderr, collectgarbage =
|
||||
setmetatable, io.stderr, collectgarbage
|
||||
|
||||
_ENV = nil
|
||||
|
||||
local active = false
|
||||
|
||||
|
||||
-- each time a table is collected, remark it for finalization on next
|
||||
-- cycle
|
||||
local mt = {}
|
||||
function mt.__gc (o)
|
||||
stderr:write'.' -- mark progress
|
||||
if active then
|
||||
setmetatable(o, mt) -- remark object for finalization
|
||||
end
|
||||
end
|
||||
|
||||
|
||||
function M.start ()
|
||||
if not active then
|
||||
active = true
|
||||
setmetatable({}, mt) -- create initial object
|
||||
end
|
||||
end
|
||||
|
||||
|
||||
function M.stop ()
|
||||
if active then
|
||||
active = false
|
||||
collectgarbage() -- call finalizer for the last time
|
||||
end
|
||||
end
|
||||
|
||||
return M
|
||||
@@ -97,9 +97,15 @@ do -- error indication in utf8.len
|
||||
assert(not a and b == p)
|
||||
end
|
||||
check("abc\xE3def", 4)
|
||||
check("汉字\x80", #("汉字") + 1)
|
||||
check("\xF4\x9F\xBF", 1)
|
||||
check("\xF4\x9F\xBF\xBF", 1)
|
||||
-- spurious continuation bytes
|
||||
check("汉字\x80", #("汉字") + 1)
|
||||
check("\x80hello", 1)
|
||||
check("hel\x80lo", 4)
|
||||
check("汉字\xBF", #("汉字") + 1)
|
||||
check("\xBFhello", 1)
|
||||
check("hel\xBFlo", 4)
|
||||
end
|
||||
|
||||
-- errors in utf8.codes
|
||||
@@ -112,6 +118,16 @@ do
|
||||
end
|
||||
errorcodes("ab\xff")
|
||||
errorcodes("\u{110000}")
|
||||
errorcodes("in\x80valid")
|
||||
errorcodes("\xbfinvalid")
|
||||
errorcodes("αλφ\xBFα")
|
||||
|
||||
-- calling interation function with invalid arguments
|
||||
local f = utf8.codes("")
|
||||
assert(f("", 2) == nil)
|
||||
assert(f("", -1) == nil)
|
||||
assert(f("", math.mininteger) == nil)
|
||||
|
||||
end
|
||||
|
||||
-- error in initial position for offset
|
||||
@@ -214,7 +230,7 @@ do
|
||||
check(s, {0x10000, 0x1FFFFF}, true)
|
||||
end
|
||||
|
||||
x = "日本語a-4\0éó"
|
||||
local x = "日本語a-4\0éó"
|
||||
check(x, {26085, 26412, 35486, 97, 45, 52, 0, 233, 243})
|
||||
|
||||
|
||||
|
||||
@@ -3,13 +3,13 @@
|
||||
|
||||
print('testing vararg')
|
||||
|
||||
function f(a, ...)
|
||||
local function f (a, ...)
|
||||
local x = {n = select('#', ...), ...}
|
||||
for i = 1, x.n do assert(a[i] == x[i]) end
|
||||
return x.n
|
||||
end
|
||||
|
||||
function c12 (...)
|
||||
local function c12 (...)
|
||||
assert(arg == _G.arg) -- no local 'arg'
|
||||
local x = {...}; x.n = #x
|
||||
local res = (x.n==2 and x[1] == 1 and x[2] == 2)
|
||||
@@ -17,7 +17,7 @@ function c12 (...)
|
||||
return res, 2
|
||||
end
|
||||
|
||||
function vararg (...) return {n = select('#', ...), ...} end
|
||||
local function vararg (...) return {n = select('#', ...), ...} end
|
||||
|
||||
local call = function (f, args) return f(table.unpack(args, 1, args.n)) end
|
||||
|
||||
@@ -29,7 +29,7 @@ assert(vararg().n == 0)
|
||||
assert(vararg(nil, nil).n == 2)
|
||||
|
||||
assert(c12(1,2)==55)
|
||||
a,b = assert(call(c12, {1,2}))
|
||||
local a,b = assert(call(c12, {1,2}))
|
||||
assert(a == 55 and b == 2)
|
||||
a = call(c12, {1,2;n=2})
|
||||
assert(a == 55 and b == 2)
|
||||
@@ -49,7 +49,7 @@ function t:f (...) local arg = {...}; return self[...]+#arg end
|
||||
assert(t:f(1,4) == 3 and t:f(2) == 11)
|
||||
print('+')
|
||||
|
||||
lim = 20
|
||||
local lim = 20
|
||||
local i, a = 1, {}
|
||||
while i <= lim do a[i] = i+0.3; i=i+1 end
|
||||
|
||||
@@ -59,7 +59,7 @@ function f(a, b, c, d, ...)
|
||||
more[lim-4] == lim+0.3 and not more[lim-3])
|
||||
end
|
||||
|
||||
function g(a,b,c)
|
||||
local function g (a,b,c)
|
||||
assert(a == 1.3 and b == 2.3 and c == 3.3)
|
||||
end
|
||||
|
||||
@@ -76,7 +76,7 @@ print("+")
|
||||
|
||||
-- new-style varargs
|
||||
|
||||
function oneless (a, ...) return ... end
|
||||
local function oneless (a, ...) return ... end
|
||||
|
||||
function f (n, a, ...)
|
||||
local b
|
||||
@@ -99,8 +99,8 @@ assert(a==nil and b==nil and c==nil and d==nil and e==nil)
|
||||
|
||||
|
||||
-- varargs for main chunks
|
||||
f = load[[ return {...} ]]
|
||||
x = f(2,3)
|
||||
local f = load[[ return {...} ]]
|
||||
local x = f(2,3)
|
||||
assert(x[1] == 2 and x[2] == 3 and x[3] == undef)
|
||||
|
||||
|
||||
|
||||
@@ -52,7 +52,7 @@ if _soft then return 10 end
|
||||
print "testing large programs (>64k)"
|
||||
|
||||
-- template to create a very big test file
|
||||
prog = [[$
|
||||
local prog = [[$
|
||||
|
||||
local a,b
|
||||
|
||||
@@ -85,7 +85,7 @@ function b:xxx (a,b) return a+b end
|
||||
assert(b:xxx(10, 12) == 22) -- pushself with non-constant index
|
||||
b["xxx"] = undef
|
||||
|
||||
s = 0; n=0
|
||||
local s = 0; local n=0
|
||||
for a,b in pairs(b) do s=s+b; n=n+1 end
|
||||
-- with 32-bit floats, exact value of 's' depends on summation order
|
||||
assert(81800000.0 < s and s < 81860000 and n == 70001)
|
||||
@@ -93,7 +93,7 @@ assert(81800000.0 < s and s < 81860000 and n == 70001)
|
||||
a = nil; b = nil
|
||||
print'+'
|
||||
|
||||
function f(x) b=x end
|
||||
local function f(x) b=x end
|
||||
|
||||
a = f{$3$} or 10
|
||||
|
||||
@@ -118,7 +118,7 @@ local function sig (x)
|
||||
return (x % 2 == 0) and '' or '-'
|
||||
end
|
||||
|
||||
F = {
|
||||
local F = {
|
||||
function () -- $1$
|
||||
for i=10,50009 do
|
||||
io.write('a', i, ' = ', sig(i), 5+((i-10)/2), ',\n')
|
||||
@@ -138,14 +138,14 @@ function () -- $3$
|
||||
end,
|
||||
}
|
||||
|
||||
file = os.tmpname()
|
||||
local file = os.tmpname()
|
||||
io.output(file)
|
||||
for s in string.gmatch(prog, "$([^$]+)") do
|
||||
local n = tonumber(s)
|
||||
if not n then io.write(s) else F[n]() end
|
||||
end
|
||||
io.close()
|
||||
result = dofile(file)
|
||||
local result = dofile(file)
|
||||
assert(os.remove(file))
|
||||
print'OK'
|
||||
return result
|
||||
|
||||
Reference in New Issue
Block a user