mirror of
https://github.com/lua/lua.git
synced 2026-09-02 16:38:19 +00:00
Compare commits
57 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
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 |
@@ -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/).
|
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).
|
Download official Lua releases from [Lua.org](https://www.lua.org/download.html).
|
||||||
|
|||||||
62
lapi.c
62
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.
|
** '!ttisnil(o)' implies 'o != &G(L)->nilvalue', so it is not needed.
|
||||||
** However, it covers the most common cases in a faster way.
|
** However, it covers the most common cases in a faster way.
|
||||||
*/
|
*/
|
||||||
@@ -74,7 +74,8 @@ static TValue *index2value (lua_State *L, int idx) {
|
|||||||
return &G(L)->nilvalue; /* it has no upvalues */
|
return &G(L)->nilvalue; /* it has no upvalues */
|
||||||
else {
|
else {
|
||||||
CClosure *func = clCvalue(s2v(ci->func));
|
CClosure *func = clCvalue(s2v(ci->func));
|
||||||
return (idx <= func->nupvalues) ? &func->upvalue[idx-1] : &G(L)->nilvalue;
|
return (idx <= func->nupvalues) ? &func->upvalue[idx-1]
|
||||||
|
: &G(L)->nilvalue;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -172,7 +173,7 @@ LUA_API int lua_gettop (lua_State *L) {
|
|||||||
|
|
||||||
LUA_API void lua_settop (lua_State *L, int idx) {
|
LUA_API void lua_settop (lua_State *L, int idx) {
|
||||||
CallInfo *ci;
|
CallInfo *ci;
|
||||||
StkId func;
|
StkId func, newtop;
|
||||||
ptrdiff_t diff; /* difference for new top */
|
ptrdiff_t diff; /* difference for new top */
|
||||||
lua_lock(L);
|
lua_lock(L);
|
||||||
ci = L->ci;
|
ci = L->ci;
|
||||||
@@ -187,9 +188,26 @@ LUA_API void lua_settop (lua_State *L, int idx) {
|
|||||||
api_check(L, -(idx+1) <= (L->top - (func + 1)), "invalid new top");
|
api_check(L, -(idx+1) <= (L->top - (func + 1)), "invalid new top");
|
||||||
diff = idx + 1; /* will "subtract" index (as it is negative) */
|
diff = idx + 1; /* will "subtract" index (as it is negative) */
|
||||||
}
|
}
|
||||||
if (diff < 0 && hastocloseCfunc(ci->nresults))
|
api_check(L, L->tbclist < L->top, "previous pop of an unclosed slot");
|
||||||
luaF_close(L, L->top + diff, LUA_OK);
|
newtop = L->top + diff;
|
||||||
L->top += diff; /* correct top only after closing any upvalue */
|
if (diff < 0 && L->tbclist >= newtop) {
|
||||||
|
lua_assert(hastocloseCfunc(ci->nresults));
|
||||||
|
luaF_close(L, newtop, CLOSEKTOP, 0);
|
||||||
|
}
|
||||||
|
L->top = 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 == level,
|
||||||
|
"no variable to close at given level");
|
||||||
|
luaF_close(L, level, CLOSEKTOP, 0);
|
||||||
|
level = index2stack(L, idx); /* stack may be moved */
|
||||||
|
setnilvalue(s2v(level));
|
||||||
lua_unlock(L);
|
lua_unlock(L);
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -629,11 +647,21 @@ static int auxgetstr (lua_State *L, const TValue *t, const char *k) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
/*
|
||||||
|
** 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) {
|
LUA_API int lua_getglobal (lua_State *L, const char *name) {
|
||||||
Table *reg;
|
const TValue *G;
|
||||||
lua_lock(L);
|
lua_lock(L);
|
||||||
reg = hvalue(&G(L)->l_registry);
|
G = getGtable(L);
|
||||||
return auxgetstr(L, luaH_getint(reg, LUA_RIDX_GLOBALS), name);
|
return auxgetstr(L, G, name);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
@@ -811,10 +839,10 @@ static void auxsetstr (lua_State *L, const TValue *t, const char *k) {
|
|||||||
|
|
||||||
|
|
||||||
LUA_API void lua_setglobal (lua_State *L, const char *name) {
|
LUA_API void lua_setglobal (lua_State *L, const char *name) {
|
||||||
Table *reg;
|
const TValue *G;
|
||||||
lua_lock(L); /* unlock done in 'auxsetstr' */
|
lua_lock(L); /* unlock done in 'auxsetstr' */
|
||||||
reg = hvalue(&G(L)->l_registry);
|
G = getGtable(L);
|
||||||
auxsetstr(L, luaH_getint(reg, LUA_RIDX_GLOBALS), name);
|
auxsetstr(L, G, name);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
@@ -861,12 +889,10 @@ LUA_API void lua_seti (lua_State *L, int idx, lua_Integer n) {
|
|||||||
|
|
||||||
static void aux_rawset (lua_State *L, int idx, TValue *key, int n) {
|
static void aux_rawset (lua_State *L, int idx, TValue *key, int n) {
|
||||||
Table *t;
|
Table *t;
|
||||||
TValue *slot;
|
|
||||||
lua_lock(L);
|
lua_lock(L);
|
||||||
api_checknelems(L, n);
|
api_checknelems(L, n);
|
||||||
t = gettable(L, idx);
|
t = gettable(L, idx);
|
||||||
slot = luaH_set(L, t, key);
|
luaH_set(L, t, key, s2v(L->top - 1));
|
||||||
setobj2t(L, slot, s2v(L->top - 1));
|
|
||||||
invalidateTMcache(t);
|
invalidateTMcache(t);
|
||||||
luaC_barrierback(L, obj2gco(t), s2v(L->top - 1));
|
luaC_barrierback(L, obj2gco(t), s2v(L->top - 1));
|
||||||
L->top -= n;
|
L->top -= n;
|
||||||
@@ -1063,8 +1089,7 @@ LUA_API int lua_load (lua_State *L, lua_Reader reader, void *data,
|
|||||||
LClosure *f = clLvalue(s2v(L->top - 1)); /* get newly created function */
|
LClosure *f = clLvalue(s2v(L->top - 1)); /* get newly created function */
|
||||||
if (f->nupvalues >= 1) { /* does it have an upvalue? */
|
if (f->nupvalues >= 1) { /* does it have an upvalue? */
|
||||||
/* get global table from registry */
|
/* get global table from registry */
|
||||||
Table *reg = hvalue(&G(L)->l_registry);
|
const TValue *gt = getGtable(L);
|
||||||
const TValue *gt = luaH_getint(reg, LUA_RIDX_GLOBALS);
|
|
||||||
/* set global table as 1st upvalue of 'f' (may be LUA_ENV) */
|
/* 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, gt);
|
||||||
luaC_barrier(L, f->upvals[0], gt);
|
luaC_barrier(L, f->upvals[0], gt);
|
||||||
@@ -1240,8 +1265,7 @@ LUA_API void lua_toclose (lua_State *L, int idx) {
|
|||||||
lua_lock(L);
|
lua_lock(L);
|
||||||
o = index2stack(L, idx);
|
o = index2stack(L, idx);
|
||||||
nresults = L->ci->nresults;
|
nresults = L->ci->nresults;
|
||||||
api_check(L, L->openupval == NULL || uplevel(L->openupval) <= o,
|
api_check(L, L->tbclist < o, "given index below or equal a marked one");
|
||||||
"marked index below or equal new one");
|
|
||||||
luaF_newtbcupval(L, o); /* create new to-be-closed upvalue */
|
luaF_newtbcupval(L, o); /* create new to-be-closed upvalue */
|
||||||
if (!hastocloseCfunc(nresults)) /* function not marked yet? */
|
if (!hastocloseCfunc(nresults)) /* function not marked yet? */
|
||||||
L->ci->nresults = codeNresults(nresults); /* mark it */
|
L->ci->nresults = codeNresults(nresults); /* mark it */
|
||||||
|
|||||||
2
lapi.h
2
lapi.h
@@ -42,6 +42,8 @@
|
|||||||
|
|
||||||
#define hastocloseCfunc(n) ((n) < LUA_MULTRET)
|
#define hastocloseCfunc(n) ((n) < LUA_MULTRET)
|
||||||
|
|
||||||
|
/* Map [-1, inf) (range of 'nresults') into (-inf, -2] */
|
||||||
#define codeNresults(n) (-(n) - 3)
|
#define codeNresults(n) (-(n) - 3)
|
||||||
|
#define decodeNresults(n) (-(n) - 3)
|
||||||
|
|
||||||
#endif
|
#endif
|
||||||
|
|||||||
74
lauxlib.c
74
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 *msg;
|
||||||
const char *typearg; /* name for the type of the actual argument */
|
const char *typearg; /* name for the type of the actual argument */
|
||||||
if (luaL_getmetafield(L, arg, "__name") == LUA_TSTRING)
|
if (luaL_getmetafield(L, arg, "__name") == LUA_TSTRING)
|
||||||
@@ -378,7 +378,7 @@ LUALIB_API int luaL_checkoption (lua_State *L, int arg, const char *def,
|
|||||||
** but without 'msg'.)
|
** but without 'msg'.)
|
||||||
*/
|
*/
|
||||||
LUALIB_API void luaL_checkstack (lua_State *L, int space, const char *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)
|
if (msg)
|
||||||
luaL_error(L, "stack overflow (%s)", msg);
|
luaL_error(L, "stack overflow (%s)", msg);
|
||||||
else
|
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) {
|
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);
|
tag_error(L, arg, t);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
LUALIB_API void luaL_checkany (lua_State *L, int arg) {
|
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");
|
luaL_argerror(L, arg, "value expected");
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
LUALIB_API const char *luaL_checklstring (lua_State *L, int arg, size_t *len) {
|
LUALIB_API const char *luaL_checklstring (lua_State *L, int arg, size_t *len) {
|
||||||
const char *s = lua_tolstring(L, arg, 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;
|
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) {
|
LUALIB_API lua_Number luaL_checknumber (lua_State *L, int arg) {
|
||||||
int isnum;
|
int isnum;
|
||||||
lua_Number d = lua_tonumberx(L, arg, &isnum);
|
lua_Number d = lua_tonumberx(L, arg, &isnum);
|
||||||
if (!isnum)
|
if (l_unlikely(!isnum))
|
||||||
tag_error(L, arg, LUA_TNUMBER);
|
tag_error(L, arg, LUA_TNUMBER);
|
||||||
return d;
|
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) {
|
LUALIB_API lua_Integer luaL_checkinteger (lua_State *L, int arg) {
|
||||||
int isnum;
|
int isnum;
|
||||||
lua_Integer d = lua_tointegerx(L, arg, &isnum);
|
lua_Integer d = lua_tointegerx(L, arg, &isnum);
|
||||||
if (!isnum) {
|
if (l_unlikely(!isnum)) {
|
||||||
interror(L, arg);
|
interror(L, arg);
|
||||||
}
|
}
|
||||||
return d;
|
return d;
|
||||||
@@ -475,7 +475,7 @@ static void *resizebox (lua_State *L, int idx, size_t newsize) {
|
|||||||
lua_Alloc allocf = lua_getallocf(L, &ud);
|
lua_Alloc allocf = lua_getallocf(L, &ud);
|
||||||
UBox *box = (UBox *)lua_touserdata(L, idx);
|
UBox *box = (UBox *)lua_touserdata(L, idx);
|
||||||
void *temp = allocf(ud, box->box, box->bsize, newsize);
|
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_pushliteral(L, "not enough memory");
|
||||||
lua_error(L); /* raise a memory error */
|
lua_error(L); /* raise a memory error */
|
||||||
}
|
}
|
||||||
@@ -515,13 +515,22 @@ static void newbox (lua_State *L) {
|
|||||||
#define buffonstack(B) ((B)->b != (B)->init.b)
|
#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'
|
** Compute new size for buffer 'B', enough to accommodate extra 'sz'
|
||||||
** bytes.
|
** bytes.
|
||||||
*/
|
*/
|
||||||
static size_t newbuffsize (luaL_Buffer *B, size_t sz) {
|
static size_t newbuffsize (luaL_Buffer *B, size_t sz) {
|
||||||
size_t newsize = B->size * 2; /* double buffer size */
|
size_t newsize = B->size * 2; /* double buffer size */
|
||||||
if (MAX_SIZET - sz < B->n) /* overflow in (B->n + sz)? */
|
if (l_unlikely(MAX_SIZET - sz < B->n)) /* overflow in (B->n + sz)? */
|
||||||
return luaL_error(B->L, "buffer too large");
|
return luaL_error(B->L, "buffer too large");
|
||||||
if (newsize < B->n + sz) /* double is not big enough? */
|
if (newsize < B->n + sz) /* double is not big enough? */
|
||||||
newsize = B->n + sz;
|
newsize = B->n + sz;
|
||||||
@@ -531,10 +540,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
|
** 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
|
** 'B'. 'boxidx' is the relative position in the stack where is the
|
||||||
** buffer's box is or should be.
|
** buffer's box or its placeholder.
|
||||||
*/
|
*/
|
||||||
static char *prepbuffsize (luaL_Buffer *B, size_t sz, int boxidx) {
|
static char *prepbuffsize (luaL_Buffer *B, size_t sz, int boxidx) {
|
||||||
|
checkbufferlevel(B, boxidx);
|
||||||
if (B->size - B->n >= sz) /* enough space? */
|
if (B->size - B->n >= sz) /* enough space? */
|
||||||
return B->b + B->n;
|
return B->b + B->n;
|
||||||
else {
|
else {
|
||||||
@@ -545,10 +555,9 @@ static char *prepbuffsize (luaL_Buffer *B, size_t sz, int boxidx) {
|
|||||||
if (buffonstack(B)) /* buffer already has a box? */
|
if (buffonstack(B)) /* buffer already has a box? */
|
||||||
newbuff = (char *)resizebox(L, boxidx, newsize); /* resize it */
|
newbuff = (char *)resizebox(L, boxidx, newsize); /* resize it */
|
||||||
else { /* no box yet */
|
else { /* no box yet */
|
||||||
lua_pushnil(L); /* reserve slot for final result */
|
lua_remove(L, boxidx); /* remove placeholder */
|
||||||
newbox(L); /* create a new box */
|
newbox(L); /* create a new box */
|
||||||
/* move box (and slot) to its intended position */
|
lua_insert(L, boxidx); /* move box to its intended position */
|
||||||
lua_rotate(L, boxidx - 1, 2);
|
|
||||||
lua_toclose(L, boxidx);
|
lua_toclose(L, boxidx);
|
||||||
newbuff = (char *)resizebox(L, boxidx, newsize);
|
newbuff = (char *)resizebox(L, boxidx, newsize);
|
||||||
memcpy(newbuff, B->b, B->n * sizeof(char)); /* copy original content */
|
memcpy(newbuff, B->b, B->n * sizeof(char)); /* copy original content */
|
||||||
@@ -583,11 +592,11 @@ LUALIB_API void luaL_addstring (luaL_Buffer *B, const char *s) {
|
|||||||
|
|
||||||
LUALIB_API void luaL_pushresult (luaL_Buffer *B) {
|
LUALIB_API void luaL_pushresult (luaL_Buffer *B) {
|
||||||
lua_State *L = B->L;
|
lua_State *L = B->L;
|
||||||
|
checkbufferlevel(B, -1);
|
||||||
lua_pushlstring(L, B->b, B->n);
|
lua_pushlstring(L, B->b, B->n);
|
||||||
if (buffonstack(B)) {
|
if (buffonstack(B))
|
||||||
lua_copy(L, -1, -3); /* move string to reserved slot */
|
lua_closeslot(L, -2); /* close the box */
|
||||||
lua_pop(L, 2); /* pop string and box (closing the box) */
|
lua_remove(L, -2); /* remove box or placeholder from the stack */
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
@@ -622,6 +631,7 @@ LUALIB_API void luaL_buffinit (lua_State *L, luaL_Buffer *B) {
|
|||||||
B->b = B->init.b;
|
B->b = B->init.b;
|
||||||
B->n = 0;
|
B->n = 0;
|
||||||
B->size = LUAL_BUFFERSIZE;
|
B->size = LUAL_BUFFERSIZE;
|
||||||
|
lua_pushlightuserdata(L, (void*)B); /* push placeholder */
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
@@ -639,10 +649,14 @@ LUALIB_API char *luaL_buffinitsize (lua_State *L, luaL_Buffer *B, size_t sz) {
|
|||||||
** =======================================================
|
** =======================================================
|
||||||
*/
|
*/
|
||||||
|
|
||||||
/* index of free-list header */
|
/* index of free-list header (after the predefined values) */
|
||||||
#define freelist 0
|
#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) {
|
LUALIB_API int luaL_ref (lua_State *L, int t) {
|
||||||
int ref;
|
int ref;
|
||||||
if (lua_isnil(L, -1)) {
|
if (lua_isnil(L, -1)) {
|
||||||
@@ -650,9 +664,16 @@ LUALIB_API int luaL_ref (lua_State *L, int t) {
|
|||||||
return LUA_REFNIL; /* 'nil' has a unique fixed reference */
|
return LUA_REFNIL; /* 'nil' has a unique fixed reference */
|
||||||
}
|
}
|
||||||
t = lua_absindex(L, t);
|
t = lua_absindex(L, t);
|
||||||
lua_rawgeti(L, t, freelist); /* get first free element */
|
if (lua_rawgeti(L, t, freelist) == LUA_TNIL) { /* first access? */
|
||||||
ref = (int)lua_tointeger(L, -1); /* ref = t[freelist] */
|
ref = 0; /* list is empty */
|
||||||
lua_pop(L, 1); /* remove it from stack */
|
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? */
|
if (ref != 0) { /* any free element? */
|
||||||
lua_rawgeti(L, t, ref); /* remove it from list */
|
lua_rawgeti(L, t, ref); /* remove it from list */
|
||||||
lua_rawseti(L, t, freelist); /* (t[freelist] = t[ref]) */
|
lua_rawseti(L, t, freelist); /* (t[freelist] = t[ref]) */
|
||||||
@@ -668,6 +689,7 @@ LUALIB_API void luaL_unref (lua_State *L, int t, int ref) {
|
|||||||
if (ref >= 0) {
|
if (ref >= 0) {
|
||||||
t = lua_absindex(L, t);
|
t = lua_absindex(L, t);
|
||||||
lua_rawgeti(L, t, freelist);
|
lua_rawgeti(L, t, freelist);
|
||||||
|
lua_assert(lua_isinteger(L, -1));
|
||||||
lua_rawseti(L, t, ref); /* t[ref] = t[freelist] */
|
lua_rawseti(L, t, ref); /* t[ref] = t[freelist] */
|
||||||
lua_pushinteger(L, ref);
|
lua_pushinteger(L, ref);
|
||||||
lua_rawseti(L, t, freelist); /* t[freelist] = ref */
|
lua_rawseti(L, t, freelist); /* t[freelist] = ref */
|
||||||
@@ -851,7 +873,7 @@ LUALIB_API lua_Integer luaL_len (lua_State *L, int idx) {
|
|||||||
int isnum;
|
int isnum;
|
||||||
lua_len(L, idx);
|
lua_len(L, idx);
|
||||||
l = lua_tointegerx(L, -1, &isnum);
|
l = lua_tointegerx(L, -1, &isnum);
|
||||||
if (!isnum)
|
if (l_unlikely(!isnum))
|
||||||
luaL_error(L, "object length is not an integer");
|
luaL_error(L, "object length is not an integer");
|
||||||
lua_pop(L, 1); /* remove object */
|
lua_pop(L, 1); /* remove object */
|
||||||
return l;
|
return l;
|
||||||
@@ -1064,7 +1086,7 @@ static void warnfon (void *ud, const char *message, int tocont) {
|
|||||||
|
|
||||||
LUALIB_API lua_State *luaL_newstate (void) {
|
LUALIB_API lua_State *luaL_newstate (void) {
|
||||||
lua_State *L = lua_newstate(l_alloc, NULL);
|
lua_State *L = lua_newstate(l_alloc, NULL);
|
||||||
if (L) {
|
if (l_likely(L)) {
|
||||||
lua_atpanic(L, &panic);
|
lua_atpanic(L, &panic);
|
||||||
lua_setwarnf(L, warnfoff, L); /* default is warnings off */
|
lua_setwarnf(L, warnfoff, L); /* default is warnings off */
|
||||||
}
|
}
|
||||||
|
|||||||
21
lauxlib.h
21
lauxlib.h
@@ -12,6 +12,7 @@
|
|||||||
#include <stddef.h>
|
#include <stddef.h>
|
||||||
#include <stdio.h>
|
#include <stdio.h>
|
||||||
|
|
||||||
|
#include "luaconf.h"
|
||||||
#include "lua.h"
|
#include "lua.h"
|
||||||
|
|
||||||
|
|
||||||
@@ -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))
|
(luaL_checkversion(L), luaL_newlibtable(L,l), luaL_setfuncs(L,l,0))
|
||||||
|
|
||||||
#define luaL_argcheck(L, cond,arg,extramsg) \
|
#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) \
|
#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_checkstring(L,n) (luaL_checklstring(L, (n), NULL))
|
||||||
#define luaL_optstring(L,n,d) (luaL_optlstring(L, (n), (d), NULL))
|
#define luaL_optstring(L,n,d) (luaL_optlstring(L, (n), (d), NULL))
|
||||||
@@ -157,6 +158,22 @@ LUALIB_API void (luaL_requiref) (lua_State *L, const char *modname,
|
|||||||
#define luaL_pushfail(L) lua_pushnil(L)
|
#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
|
** Generic Buffer manipulation
|
||||||
|
|||||||
15
lbaselib.c
15
lbaselib.c
@@ -138,7 +138,7 @@ static int luaB_setmetatable (lua_State *L) {
|
|||||||
int t = lua_type(L, 2);
|
int t = lua_type(L, 2);
|
||||||
luaL_checktype(L, 1, LUA_TTABLE);
|
luaL_checktype(L, 1, LUA_TTABLE);
|
||||||
luaL_argexpected(L, t == LUA_TNIL || t == LUA_TTABLE, 2, "nil or table");
|
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");
|
return luaL_error(L, "cannot change a protected metatable");
|
||||||
lua_settop(L, 2);
|
lua_settop(L, 2);
|
||||||
lua_setmetatable(L, 1);
|
lua_setmetatable(L, 1);
|
||||||
@@ -182,7 +182,8 @@ static int luaB_rawset (lua_State *L) {
|
|||||||
|
|
||||||
|
|
||||||
static int pushmode (lua_State *L, int oldmode) {
|
static int pushmode (lua_State *L, int oldmode) {
|
||||||
lua_pushstring(L, (oldmode == LUA_GCINC) ? "incremental" : "generational");
|
lua_pushstring(L, (oldmode == LUA_GCINC) ? "incremental"
|
||||||
|
: "generational");
|
||||||
return 1;
|
return 1;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -299,7 +300,7 @@ static int luaB_ipairs (lua_State *L) {
|
|||||||
|
|
||||||
|
|
||||||
static int load_aux (lua_State *L, int status, int envidx) {
|
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? */
|
if (envidx != 0) { /* 'env' parameter? */
|
||||||
lua_pushvalue(L, envidx); /* environment for loaded function */
|
lua_pushvalue(L, envidx); /* environment for loaded function */
|
||||||
if (!lua_setupvalue(L, -2, 1)) /* set it as 1st upvalue */
|
if (!lua_setupvalue(L, -2, 1)) /* set it as 1st upvalue */
|
||||||
@@ -355,7 +356,7 @@ static const char *generic_reader (lua_State *L, void *ud, size_t *size) {
|
|||||||
*size = 0;
|
*size = 0;
|
||||||
return NULL;
|
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");
|
luaL_error(L, "reader function must return a string");
|
||||||
lua_replace(L, RESERVEDSLOT); /* save string in reserved slot */
|
lua_replace(L, RESERVEDSLOT); /* save string in reserved slot */
|
||||||
return lua_tolstring(L, RESERVEDSLOT, size);
|
return lua_tolstring(L, RESERVEDSLOT, size);
|
||||||
@@ -393,7 +394,7 @@ static int dofilecont (lua_State *L, int d1, lua_KContext d2) {
|
|||||||
static int luaB_dofile (lua_State *L) {
|
static int luaB_dofile (lua_State *L) {
|
||||||
const char *fname = luaL_optstring(L, 1, NULL);
|
const char *fname = luaL_optstring(L, 1, NULL);
|
||||||
lua_settop(L, 1);
|
lua_settop(L, 1);
|
||||||
if (luaL_loadfile(L, fname) != LUA_OK)
|
if (l_unlikely(luaL_loadfile(L, fname) != LUA_OK))
|
||||||
return lua_error(L);
|
return lua_error(L);
|
||||||
lua_callk(L, 0, LUA_MULTRET, 0, dofilecont);
|
lua_callk(L, 0, LUA_MULTRET, 0, dofilecont);
|
||||||
return dofilecont(L, 0, 0);
|
return dofilecont(L, 0, 0);
|
||||||
@@ -401,7 +402,7 @@ static int luaB_dofile (lua_State *L) {
|
|||||||
|
|
||||||
|
|
||||||
static int luaB_assert (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 */
|
return lua_gettop(L); /* return all arguments */
|
||||||
else { /* error */
|
else { /* error */
|
||||||
luaL_checkany(L, 1); /* there must be a condition */
|
luaL_checkany(L, 1); /* there must be a condition */
|
||||||
@@ -437,7 +438,7 @@ static int luaB_select (lua_State *L) {
|
|||||||
** ignored).
|
** ignored).
|
||||||
*/
|
*/
|
||||||
static int finishpcall (lua_State *L, int status, lua_KContext extra) {
|
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_pushboolean(L, 0); /* first result (false) */
|
||||||
lua_pushvalue(L, -2); /* error message */
|
lua_pushvalue(L, -2); /* error message */
|
||||||
return 2; /* return false, msg */
|
return 2; /* return false, msg */
|
||||||
|
|||||||
30
lcode.c
30
lcode.c
@@ -314,15 +314,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. */
|
/* limit for difference between lines in relative line info. */
|
||||||
#define LIMLINEDIFF 0x80
|
#define LIMLINEDIFF 0x80
|
||||||
|
|
||||||
@@ -337,13 +328,13 @@ void luaK_patchtohere (FuncState *fs, int list) {
|
|||||||
static void savelineinfo (FuncState *fs, Proto *f, int line) {
|
static void savelineinfo (FuncState *fs, Proto *f, int line) {
|
||||||
int linedif = line - fs->previousline;
|
int linedif = line - fs->previousline;
|
||||||
int pc = fs->pc - 1; /* last instruction coded */
|
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,
|
luaM_growvector(fs->ls->L, f->abslineinfo, fs->nabslineinfo,
|
||||||
f->sizeabslineinfo, AbsLineInfo, MAX_INT, "lines");
|
f->sizeabslineinfo, AbsLineInfo, MAX_INT, "lines");
|
||||||
f->abslineinfo[fs->nabslineinfo].pc = pc;
|
f->abslineinfo[fs->nabslineinfo].pc = pc;
|
||||||
f->abslineinfo[fs->nabslineinfo++].line = line;
|
f->abslineinfo[fs->nabslineinfo++].line = line;
|
||||||
linedif = ABSLINEINFO; /* signal that there is absolute information */
|
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,
|
luaM_growvector(fs->ls->L, f->lineinfo, pc, f->sizelineinfo, ls_byte,
|
||||||
MAX_INT, "opcodes");
|
MAX_INT, "opcodes");
|
||||||
@@ -545,11 +536,14 @@ static void freeexps (FuncState *fs, expdesc *e1, expdesc *e2) {
|
|||||||
** and try to reuse constants. Because some values should not be used
|
** 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
|
** 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.
|
** 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) {
|
static int addk (FuncState *fs, TValue *key, TValue *v) {
|
||||||
|
TValue val;
|
||||||
lua_State *L = fs->ls->L;
|
lua_State *L = fs->ls->L;
|
||||||
Proto *f = fs->f;
|
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;
|
int k, oldsize;
|
||||||
if (ttisinteger(idx)) { /* is there an index there? */
|
if (ttisinteger(idx)) { /* is there an index there? */
|
||||||
k = cast_int(ivalue(idx));
|
k = cast_int(ivalue(idx));
|
||||||
@@ -563,7 +557,8 @@ static int addk (FuncState *fs, TValue *key, TValue *v) {
|
|||||||
k = fs->nk;
|
k = fs->nk;
|
||||||
/* numerical value does not need GC barrier;
|
/* numerical value does not need GC barrier;
|
||||||
table has no metatable, so it does not need to invalidate cache */
|
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");
|
luaM_growvector(L, f->k, k, f->sizek, TValue, MAXARG_Ax, "constants");
|
||||||
while (oldsize < f->sizek) setnilvalue(&f->k[oldsize++]);
|
while (oldsize < f->sizek) setnilvalue(&f->k[oldsize++]);
|
||||||
setobj(L, &f->k[k], v);
|
setobj(L, &f->k[k], v);
|
||||||
@@ -763,7 +758,7 @@ void luaK_dischargevars (FuncState *fs, expdesc *e) {
|
|||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
case VLOCAL: { /* already in a register */
|
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 */
|
e->k = VNONRELOC; /* becomes a non-relocatable value */
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
@@ -1036,7 +1031,7 @@ void luaK_storevar (FuncState *fs, expdesc *var, expdesc *ex) {
|
|||||||
switch (var->k) {
|
switch (var->k) {
|
||||||
case VLOCAL: {
|
case VLOCAL: {
|
||||||
freeexp(fs, ex);
|
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;
|
return;
|
||||||
}
|
}
|
||||||
case VUPVAL: {
|
case VUPVAL: {
|
||||||
@@ -1276,7 +1271,7 @@ void luaK_indexed (FuncState *fs, expdesc *t, expdesc *k) {
|
|||||||
}
|
}
|
||||||
else {
|
else {
|
||||||
/* register index of the table */
|
/* 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)) {
|
if (isKstr(fs, k)) {
|
||||||
t->u.ind.idx = k->u.info; /* literal string */
|
t->u.ind.idx = k->u.info; /* literal string */
|
||||||
t->k = VINDEXSTR;
|
t->k = VINDEXSTR;
|
||||||
@@ -1303,7 +1298,8 @@ static int validop (int op, TValue *v1, TValue *v2) {
|
|||||||
case LUA_OPBAND: case LUA_OPBOR: case LUA_OPBXOR:
|
case LUA_OPBAND: case LUA_OPBOR: case LUA_OPBXOR:
|
||||||
case LUA_OPSHL: case LUA_OPSHR: case LUA_OPBNOT: { /* conversion errors */
|
case LUA_OPSHL: case LUA_OPSHR: case LUA_OPBNOT: { /* conversion errors */
|
||||||
lua_Integer i;
|
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 */
|
case LUA_OPDIV: case LUA_OPIDIV: case LUA_OPMOD: /* division by 0 */
|
||||||
return (nvalue(v2) != 0);
|
return (nvalue(v2) != 0);
|
||||||
|
|||||||
17
lcorolib.c
17
lcorolib.c
@@ -31,14 +31,14 @@ static lua_State *getco (lua_State *L) {
|
|||||||
*/
|
*/
|
||||||
static int auxresume (lua_State *L, lua_State *co, int narg) {
|
static int auxresume (lua_State *L, lua_State *co, int narg) {
|
||||||
int status, nres;
|
int status, nres;
|
||||||
if (!lua_checkstack(co, narg)) {
|
if (l_unlikely(!lua_checkstack(co, narg))) {
|
||||||
lua_pushliteral(L, "too many arguments to resume");
|
lua_pushliteral(L, "too many arguments to resume");
|
||||||
return -1; /* error flag */
|
return -1; /* error flag */
|
||||||
}
|
}
|
||||||
lua_xmove(L, co, narg);
|
lua_xmove(L, co, narg);
|
||||||
status = lua_resume(co, L, narg, &nres);
|
status = lua_resume(co, L, narg, &nres);
|
||||||
if (status == LUA_OK || status == LUA_YIELD) {
|
if (l_likely(status == LUA_OK || status == LUA_YIELD)) {
|
||||||
if (!lua_checkstack(L, nres + 1)) {
|
if (l_unlikely(!lua_checkstack(L, nres + 1))) {
|
||||||
lua_pop(co, nres); /* remove results anyway */
|
lua_pop(co, nres); /* remove results anyway */
|
||||||
lua_pushliteral(L, "too many results to resume");
|
lua_pushliteral(L, "too many results to resume");
|
||||||
return -1; /* error flag */
|
return -1; /* error flag */
|
||||||
@@ -57,7 +57,7 @@ static int luaB_coresume (lua_State *L) {
|
|||||||
lua_State *co = getco(L);
|
lua_State *co = getco(L);
|
||||||
int r;
|
int r;
|
||||||
r = auxresume(L, co, lua_gettop(L) - 1);
|
r = auxresume(L, co, lua_gettop(L) - 1);
|
||||||
if (r < 0) {
|
if (l_unlikely(r < 0)) {
|
||||||
lua_pushboolean(L, 0);
|
lua_pushboolean(L, 0);
|
||||||
lua_insert(L, -2);
|
lua_insert(L, -2);
|
||||||
return 2; /* return false + error message */
|
return 2; /* return false + error message */
|
||||||
@@ -73,10 +73,13 @@ static int luaB_coresume (lua_State *L) {
|
|||||||
static int luaB_auxwrap (lua_State *L) {
|
static int luaB_auxwrap (lua_State *L) {
|
||||||
lua_State *co = lua_tothread(L, lua_upvalueindex(1));
|
lua_State *co = lua_tothread(L, lua_upvalueindex(1));
|
||||||
int r = auxresume(L, co, lua_gettop(L));
|
int r = auxresume(L, co, lua_gettop(L));
|
||||||
if (r < 0) { /* error? */
|
if (l_unlikely(r < 0)) { /* error? */
|
||||||
int stat = lua_status(co);
|
int stat = lua_status(co);
|
||||||
if (stat != LUA_OK && stat != LUA_YIELD) /* error in the coroutine? */
|
if (stat != LUA_OK && stat != LUA_YIELD) { /* error in the coroutine? */
|
||||||
lua_resetthread(co); /* close its tbc variables */
|
stat = lua_resetthread(co); /* close its tbc variables */
|
||||||
|
lua_assert(stat != LUA_OK);
|
||||||
|
lua_xmove(co, L, 1); /* copy error message */
|
||||||
|
}
|
||||||
if (stat != LUA_ERRMEM && /* not a memory error and ... */
|
if (stat != LUA_ERRMEM && /* not a memory error and ... */
|
||||||
lua_type(L, -1) == LUA_TSTRING) { /* ... error object is a string? */
|
lua_type(L, -1) == LUA_TSTRING) { /* ... error object is a string? */
|
||||||
luaL_where(L, 1); /* add extra info, if available */
|
luaL_where(L, 1); /* add extra info, if available */
|
||||||
|
|||||||
11
ldblib.c
11
ldblib.c
@@ -33,7 +33,7 @@ static const char *const HOOKKEY = "_HOOKKEY";
|
|||||||
** checked.
|
** checked.
|
||||||
*/
|
*/
|
||||||
static void checkstack (lua_State *L, lua_State *L1, int n) {
|
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");
|
luaL_error(L, "stack overflow");
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -152,6 +152,7 @@ static int db_getinfo (lua_State *L) {
|
|||||||
lua_State *L1 = getthread(L, &arg);
|
lua_State *L1 = getthread(L, &arg);
|
||||||
const char *options = luaL_optstring(L, arg+2, "flnSrtu");
|
const char *options = luaL_optstring(L, arg+2, "flnSrtu");
|
||||||
checkstack(L, L1, 3);
|
checkstack(L, L1, 3);
|
||||||
|
luaL_argcheck(L, options[0] != '>', arg + 2, "invalid option '>'");
|
||||||
if (lua_isfunction(L, arg + 1)) { /* info about a function? */
|
if (lua_isfunction(L, arg + 1)) { /* info about a function? */
|
||||||
options = lua_pushfstring(L, ">%s", options); /* add '>' to 'options' */
|
options = lua_pushfstring(L, ">%s", options); /* add '>' to 'options' */
|
||||||
lua_pushvalue(L, arg + 1); /* move function to 'L1' stack */
|
lua_pushvalue(L, arg + 1); /* move function to 'L1' stack */
|
||||||
@@ -212,7 +213,7 @@ static int db_getlocal (lua_State *L) {
|
|||||||
lua_Debug ar;
|
lua_Debug ar;
|
||||||
const char *name;
|
const char *name;
|
||||||
int level = (int)luaL_checkinteger(L, arg + 1);
|
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");
|
return luaL_argerror(L, arg+1, "level out of range");
|
||||||
checkstack(L, L1, 1);
|
checkstack(L, L1, 1);
|
||||||
name = lua_getlocal(L1, &ar, nvar);
|
name = lua_getlocal(L1, &ar, nvar);
|
||||||
@@ -237,7 +238,7 @@ static int db_setlocal (lua_State *L) {
|
|||||||
lua_Debug ar;
|
lua_Debug ar;
|
||||||
int level = (int)luaL_checkinteger(L, arg + 1);
|
int level = (int)luaL_checkinteger(L, arg + 1);
|
||||||
int nvar = (int)luaL_checkinteger(L, arg + 2);
|
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");
|
return luaL_argerror(L, arg+1, "level out of range");
|
||||||
luaL_checkany(L, arg+3);
|
luaL_checkany(L, arg+3);
|
||||||
lua_settop(L, arg+3);
|
lua_settop(L, arg+3);
|
||||||
@@ -377,7 +378,7 @@ static int db_sethook (lua_State *L) {
|
|||||||
}
|
}
|
||||||
if (!luaL_getsubtable(L, LUA_REGISTRYINDEX, HOOKKEY)) {
|
if (!luaL_getsubtable(L, LUA_REGISTRYINDEX, HOOKKEY)) {
|
||||||
/* table just created; initialize it */
|
/* table just created; initialize it */
|
||||||
lua_pushstring(L, "k");
|
lua_pushliteral(L, "k");
|
||||||
lua_setfield(L, -2, "__mode"); /** hooktable.__mode = "k" */
|
lua_setfield(L, -2, "__mode"); /** hooktable.__mode = "k" */
|
||||||
lua_pushvalue(L, -1);
|
lua_pushvalue(L, -1);
|
||||||
lua_setmetatable(L, -2); /* metatable(hooktable) = hooktable */
|
lua_setmetatable(L, -2); /* metatable(hooktable) = hooktable */
|
||||||
@@ -420,7 +421,7 @@ static int db_debug (lua_State *L) {
|
|||||||
for (;;) {
|
for (;;) {
|
||||||
char buffer[250];
|
char buffer[250];
|
||||||
lua_writestringerror("%s", "lua_debug> ");
|
lua_writestringerror("%s", "lua_debug> ");
|
||||||
if (fgets(buffer, sizeof(buffer), stdin) == 0 ||
|
if (fgets(buffer, sizeof(buffer), stdin) == NULL ||
|
||||||
strcmp(buffer, "cont\n") == 0)
|
strcmp(buffer, "cont\n") == 0)
|
||||||
return 0;
|
return 0;
|
||||||
if (luaL_loadbuffer(L, buffer, strlen(buffer), "=(debug command)") ||
|
if (luaL_loadbuffer(L, buffer, strlen(buffer), "=(debug command)") ||
|
||||||
|
|||||||
131
ldebug.c
131
ldebug.c
@@ -33,8 +33,6 @@
|
|||||||
|
|
||||||
#define noLuaClosure(f) ((f) == NULL || (f)->c.tt == LUA_VCCL)
|
#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,
|
static const char *funcnamefromcode (lua_State *L, CallInfo *ci,
|
||||||
const char **name);
|
const char **name);
|
||||||
@@ -48,10 +46,16 @@ static int currentpc (CallInfo *ci) {
|
|||||||
|
|
||||||
/*
|
/*
|
||||||
** Get a "base line" to find the line corresponding to an instruction.
|
** 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
|
** Base lines are regularly placed at MAXIWTHABS intervals, so usually
|
||||||
** instruction smaller or equal to the wanted instruction. A special
|
** an integer division gets the right place. When the source file has
|
||||||
** case is when there is no absolute info or the instruction is before
|
** large sequences of empty/comment lines, it may need extra entries,
|
||||||
** the first absolute one.
|
** 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) {
|
static int getbaseline (const Proto *f, int pc, int *basepc) {
|
||||||
if (f->sizeabslineinfo == 0 || pc < f->abslineinfo[0].pc) {
|
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;
|
return f->linedefined;
|
||||||
}
|
}
|
||||||
else {
|
else {
|
||||||
unsigned int i;
|
int i = cast_uint(pc) / MAXIWTHABS - 1; /* get an estimate */
|
||||||
if (pc >= f->abslineinfo[f->sizeabslineinfo - 1].pc)
|
/* estimate must be a lower bond of the correct base */
|
||||||
i = f->sizeabslineinfo - 1; /* instruction is after last saved one */
|
lua_assert(i < 0 ||
|
||||||
else { /* binary search */
|
(i < f->sizeabslineinfo && f->abslineinfo[i].pc <= pc));
|
||||||
unsigned int j = f->sizeabslineinfo - 1; /* pc < anchorlines[j] */
|
while (i + 1 < f->sizeabslineinfo && pc >= f->abslineinfo[i + 1].pc)
|
||||||
i = 0; /* abslineinfo[i] <= pc */
|
i++; /* low estimate; adjust it */
|
||||||
while (i < j - 1) {
|
|
||||||
unsigned int m = (j + i) / 2;
|
|
||||||
if (pc >= f->abslineinfo[m].pc)
|
|
||||||
i = m;
|
|
||||||
else
|
|
||||||
j = m;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
*basepc = f->abslineinfo[i].pc;
|
*basepc = f->abslineinfo[i].pc;
|
||||||
return f->abslineinfo[i].line;
|
return f->abslineinfo[i].line;
|
||||||
}
|
}
|
||||||
@@ -305,8 +301,8 @@ static void collectvalidlines (lua_State *L, Closure *f) {
|
|||||||
sethvalue2s(L, L->top, t); /* push it on stack */
|
sethvalue2s(L, L->top, t); /* push it on stack */
|
||||||
api_incr_top(L);
|
api_incr_top(L);
|
||||||
setbtvalue(&v); /* boolean 'true' to be the value of all indices */
|
setbtvalue(&v); /* boolean 'true' to be the value of all indices */
|
||||||
for (i = 0; i < p->sizelineinfo; i++) { /* for all lines with code */
|
for (i = 0; i < p->sizelineinfo; i++) { /* for all instructions */
|
||||||
currentline = nextline(p, currentline, i);
|
currentline = nextline(p, currentline, i); /* get its line */
|
||||||
luaH_setint(L, t, currentline, &v); /* table[line] = true */
|
luaH_setint(L, t, currentline, &v); /* table[line] = true */
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -629,12 +625,10 @@ static const char *funcnamefromcode (lua_State *L, CallInfo *ci,
|
|||||||
case OP_LEN: tm = TM_LEN; break;
|
case OP_LEN: tm = TM_LEN; break;
|
||||||
case OP_CONCAT: tm = TM_CONCAT; break;
|
case OP_CONCAT: tm = TM_CONCAT; break;
|
||||||
case OP_EQ: tm = TM_EQ; break;
|
case OP_EQ: tm = TM_EQ; break;
|
||||||
case OP_LT: case OP_LE: case OP_LTI: case OP_LEI:
|
/* no cases for OP_EQI and OP_EQK, as they don't call metamethods */
|
||||||
*name = "order"; /* '<=' can call '__lt', etc. */
|
case OP_LT: case OP_LTI: case OP_GTI: tm = TM_LT; break;
|
||||||
return "metamethod";
|
case OP_LE: case OP_LEI: case OP_GEI: tm = TM_LE; break;
|
||||||
case OP_CLOSE: case OP_RETURN:
|
case OP_CLOSE: case OP_RETURN: tm = TM_CLOSE; break;
|
||||||
*name = "close";
|
|
||||||
return "metamethod";
|
|
||||||
default:
|
default:
|
||||||
return NULL; /* cannot find a reasonable name */
|
return NULL; /* cannot find a reasonable name */
|
||||||
}
|
}
|
||||||
@@ -647,14 +641,18 @@ static const char *funcnamefromcode (lua_State *L, CallInfo *ci,
|
|||||||
|
|
||||||
|
|
||||||
/*
|
/*
|
||||||
** The subtraction of two potentially unrelated pointers is
|
** Check whether pointer 'o' points to some value in the stack
|
||||||
** not ISO C, but it should not crash a program; the subsequent
|
** frame of the current function. Because 'o' may not point to a
|
||||||
** checks are ISO C and ensure a correct result.
|
** value in this stack, we cannot compare it with the region
|
||||||
|
** boundaries (undefined behaviour in ISO C).
|
||||||
*/
|
*/
|
||||||
static int isinstack (CallInfo *ci, const TValue *o) {
|
static int isinstack (CallInfo *ci, const TValue *o) {
|
||||||
StkId base = ci->func + 1;
|
StkId pos;
|
||||||
ptrdiff_t i = cast(StkId, o) - base;
|
for (pos = ci->func + 1; pos < ci->top; pos++) {
|
||||||
return (0 <= i && i < (ci->top - base) && s2v(base + i) == o);
|
if (o == s2v(pos))
|
||||||
|
return 1;
|
||||||
|
}
|
||||||
|
return 0; /* not found */
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
@@ -697,6 +695,19 @@ l_noret luaG_typeerror (lua_State *L, const TValue *o, const char *op) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
l_noret luaG_callerror (lua_State *L, const TValue *o) {
|
||||||
|
CallInfo *ci = L->ci;
|
||||||
|
const char *name = NULL; /* to avoid warnings */
|
||||||
|
const char *what = (isLua(ci)) ? funcnamefromcode(L, ci, &name) : NULL;
|
||||||
|
if (what != NULL) {
|
||||||
|
const char *t = luaT_objtypename(L, o);
|
||||||
|
luaG_runerror(L, "%s '%s' is not callable (a %s value)", what, name, t);
|
||||||
|
}
|
||||||
|
else
|
||||||
|
luaG_typeerror(L, o, "call");
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
l_noret luaG_forerror (lua_State *L, const TValue *o, const char *what) {
|
l_noret luaG_forerror (lua_State *L, const TValue *o, const char *what) {
|
||||||
luaG_runerror(L, "bad 'for' %s (number expected, got %s)",
|
luaG_runerror(L, "bad 'for' %s (number expected, got %s)",
|
||||||
what, luaT_objtypename(L, o));
|
what, luaT_objtypename(L, o));
|
||||||
@@ -722,7 +733,7 @@ l_noret luaG_opinterror (lua_State *L, const TValue *p1,
|
|||||||
*/
|
*/
|
||||||
l_noret luaG_tointerror (lua_State *L, const TValue *p1, const TValue *p2) {
|
l_noret luaG_tointerror (lua_State *L, const TValue *p1, const TValue *p2) {
|
||||||
lua_Integer temp;
|
lua_Integer temp;
|
||||||
if (!tointegerns(p1, &temp))
|
if (!luaV_tointegerns(p1, &temp, LUA_FLOORN2I))
|
||||||
p2 = p1;
|
p2 = p1;
|
||||||
luaG_runerror(L, "number%s has no integer representation", varinfo(L, p2));
|
luaG_runerror(L, "number%s has no integer representation", varinfo(L, p2));
|
||||||
}
|
}
|
||||||
@@ -780,16 +791,30 @@ l_noret luaG_runerror (lua_State *L, const char *fmt, ...) {
|
|||||||
|
|
||||||
/*
|
/*
|
||||||
** Check whether new instruction 'newpc' is in a different line from
|
** 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) {
|
static int changedline (const Proto *p, int oldpc, int newpc) {
|
||||||
if (p->lineinfo == NULL) /* no debug information? */
|
if (p->lineinfo == NULL) /* no debug information? */
|
||||||
return 0;
|
return 0;
|
||||||
while (oldpc++ < newpc) {
|
if (newpc - oldpc < MAXIWTHABS / 2) { /* not too far apart? */
|
||||||
if (p->lineinfo[oldpc] != 0)
|
int delta = 0; /* line diference */
|
||||||
return (luaG_getfuncline(p, oldpc - 1) != luaG_getfuncline(p, newpc));
|
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 +822,19 @@ static int changedline (const Proto *p, int oldpc, int newpc) {
|
|||||||
** Traces the execution of a Lua function. Called before the execution
|
** Traces the execution of a Lua function. Called before the execution
|
||||||
** of each opcode, when debug is on. 'L->oldpc' stores the last
|
** of each opcode, when debug is on. 'L->oldpc' stores the last
|
||||||
** instruction traced, to detect line changes. When entering a new
|
** instruction traced, to detect line changes. When entering a new
|
||||||
** function, 'npci' will be zero and will test as a new line without
|
** function, 'npci' will be zero and will test as a new line whatever
|
||||||
** the need for 'oldpc'; so, 'oldpc' does not need to be initialized
|
** the value of 'oldpc'. Some exceptional conditions may return to
|
||||||
** before. Some exceptional conditions may return to a function without
|
** a function without setting 'oldpc'. In that case, 'oldpc' may be
|
||||||
** updating 'oldpc'. In that case, 'oldpc' may be invalid; if so, it is
|
** invalid; if so, use zero as a valid value. (A wrong but valid 'oldpc'
|
||||||
** reset to zero. (A wrong but valid 'oldpc' at most causes an extra
|
** at most causes an extra call to a line hook.)
|
||||||
** call to a line hook.)
|
** This function is not "Protected" when called, so it should correct
|
||||||
|
** 'L->top' before calling anything that can run the GC.
|
||||||
*/
|
*/
|
||||||
int luaG_traceexec (lua_State *L, const Instruction *pc) {
|
int luaG_traceexec (lua_State *L, const Instruction *pc) {
|
||||||
CallInfo *ci = L->ci;
|
CallInfo *ci = L->ci;
|
||||||
lu_byte mask = L->hookmask;
|
lu_byte mask = L->hookmask;
|
||||||
const Proto *p = ci_func(ci)->p;
|
const Proto *p = ci_func(ci)->p;
|
||||||
int counthook;
|
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? */
|
if (!(mask & (LUA_MASKLINE | LUA_MASKCOUNT))) { /* no hooks? */
|
||||||
ci->u.l.trap = 0; /* don't need to stop again */
|
ci->u.l.trap = 0; /* don't need to stop again */
|
||||||
return 0; /* turn off 'trap' */
|
return 0; /* turn off 'trap' */
|
||||||
@@ -826,15 +850,16 @@ int luaG_traceexec (lua_State *L, const Instruction *pc) {
|
|||||||
ci->callstatus &= ~CIST_HOOKYIELD; /* erase mark */
|
ci->callstatus &= ~CIST_HOOKYIELD; /* erase mark */
|
||||||
return 1; /* do not call hook again (VM yielded, so it did not move) */
|
return 1; /* do not call hook again (VM yielded, so it did not move) */
|
||||||
}
|
}
|
||||||
if (!isIT(*(ci->u.l.savedpc - 1)))
|
if (!isIT(*(ci->u.l.savedpc - 1))) /* top not being used? */
|
||||||
L->top = ci->top; /* prepare top */
|
L->top = ci->top; /* correct top */
|
||||||
if (counthook)
|
if (counthook)
|
||||||
luaD_hook(L, LUA_HOOKCOUNT, -1, 0, 0); /* call count hook */
|
luaD_hook(L, LUA_HOOKCOUNT, -1, 0, 0); /* call count hook */
|
||||||
if (mask & LUA_MASKLINE) {
|
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);
|
int npci = pcRel(pc, p);
|
||||||
if (npci == 0 || /* call linehook when enter a new function, */
|
if (npci <= oldpc || /* call hook when jump back (loop), */
|
||||||
pc <= invpcRel(oldpc, p) || /* when jump back (loop), or when */
|
changedline(p, oldpc, npci)) { /* or when enter new line */
|
||||||
changedline(p, oldpc, npci)) { /* enter new line */
|
|
||||||
int newline = luaG_getfuncline(p, npci);
|
int newline = luaG_getfuncline(p, npci);
|
||||||
luaD_hook(L, LUA_HOOKLINE, newline, 0, 0); /* call line hook */
|
luaD_hook(L, LUA_HOOKLINE, newline, 0, 0); /* call line hook */
|
||||||
}
|
}
|
||||||
|
|||||||
11
ldebug.h
11
ldebug.h
@@ -26,11 +26,22 @@
|
|||||||
*/
|
*/
|
||||||
#define ABSLINEINFO (-0x80)
|
#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 int luaG_getfuncline (const Proto *f, int pc);
|
||||||
LUAI_FUNC const char *luaG_findlocal (lua_State *L, CallInfo *ci, int n,
|
LUAI_FUNC const char *luaG_findlocal (lua_State *L, CallInfo *ci, int n,
|
||||||
StkId *pos);
|
StkId *pos);
|
||||||
LUAI_FUNC l_noret luaG_typeerror (lua_State *L, const TValue *o,
|
LUAI_FUNC l_noret luaG_typeerror (lua_State *L, const TValue *o,
|
||||||
const char *opname);
|
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,
|
LUAI_FUNC l_noret luaG_forerror (lua_State *L, const TValue *o,
|
||||||
const char *what);
|
const char *what);
|
||||||
LUAI_FUNC l_noret luaG_concaterror (lua_State *L, const TValue *p1,
|
LUAI_FUNC l_noret luaG_concaterror (lua_State *L, const TValue *p1,
|
||||||
|
|||||||
346
ldo.c
346
ldo.c
@@ -98,11 +98,12 @@ void luaD_seterrorobj (lua_State *L, int errcode, StkId oldtop) {
|
|||||||
setsvalue2s(L, oldtop, luaS_newliteral(L, "error in error handling"));
|
setsvalue2s(L, oldtop, luaS_newliteral(L, "error in error handling"));
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
case CLOSEPROTECT: {
|
case LUA_OK: { /* special case only for closing upvalues */
|
||||||
setnilvalue(s2v(oldtop)); /* no error message */
|
setnilvalue(s2v(oldtop)); /* no error message */
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
default: {
|
default: {
|
||||||
|
lua_assert(errorstatus(errcode)); /* real error */
|
||||||
setobjs2s(L, oldtop, L->top - 1); /* error message on current top */
|
setobjs2s(L, oldtop, L->top - 1); /* error message on current top */
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
@@ -118,17 +119,13 @@ l_noret luaD_throw (lua_State *L, int errcode) {
|
|||||||
}
|
}
|
||||||
else { /* thread has no error handler */
|
else { /* thread has no error handler */
|
||||||
global_State *g = G(L);
|
global_State *g = G(L);
|
||||||
errcode = luaF_close(L, L->stack, errcode); /* close all upvalues */
|
errcode = luaE_resetthread(L, errcode); /* close all upvalues */
|
||||||
L->status = cast_byte(errcode); /* mark it as dead */
|
|
||||||
if (g->mainthread->errorJmp) { /* main thread has a handler? */
|
if (g->mainthread->errorJmp) { /* main thread has a handler? */
|
||||||
setobjs2s(L, g->mainthread->top++, L->top - 1); /* copy error obj. */
|
setobjs2s(L, g->mainthread->top++, L->top - 1); /* copy error obj. */
|
||||||
luaD_throw(g->mainthread, errcode); /* re-throw in main thread */
|
luaD_throw(g->mainthread, errcode); /* re-throw in main thread */
|
||||||
}
|
}
|
||||||
else { /* no handler at all; abort */
|
else { /* no handler at all; abort */
|
||||||
if (g->panic) { /* panic function? */
|
if (g->panic) { /* panic function? */
|
||||||
luaD_seterrorobj(L, errcode, L->top); /* assume EXTRA_STACK */
|
|
||||||
if (L->ci->top < L->top)
|
|
||||||
L->ci->top = L->top; /* pushing msg. can break this invariant */
|
|
||||||
lua_unlock(L);
|
lua_unlock(L);
|
||||||
g->panic(L); /* call panic function (last chance to jump out) */
|
g->panic(L); /* call panic function (last chance to jump out) */
|
||||||
}
|
}
|
||||||
@@ -163,9 +160,8 @@ int luaD_rawrunprotected (lua_State *L, Pfunc f, void *ud) {
|
|||||||
static void correctstack (lua_State *L, StkId oldstack, StkId newstack) {
|
static void correctstack (lua_State *L, StkId oldstack, StkId newstack) {
|
||||||
CallInfo *ci;
|
CallInfo *ci;
|
||||||
UpVal *up;
|
UpVal *up;
|
||||||
if (oldstack == newstack)
|
|
||||||
return; /* stack address did not change */
|
|
||||||
L->top = (L->top - oldstack) + newstack;
|
L->top = (L->top - oldstack) + newstack;
|
||||||
|
L->tbclist = (L->tbclist - oldstack) + newstack;
|
||||||
for (up = L->openupval; up != NULL; up = up->u.open.next)
|
for (up = L->openupval; up != NULL; up = up->u.open.next)
|
||||||
up->v = s2v((uplevel(up) - oldstack) + newstack);
|
up->v = s2v((uplevel(up) - oldstack) + newstack);
|
||||||
for (ci = L->ci; ci != NULL; ci = ci->previous) {
|
for (ci = L->ci; ci != NULL; ci = ci->previous) {
|
||||||
@@ -181,19 +177,35 @@ static void correctstack (lua_State *L, StkId oldstack, StkId newstack) {
|
|||||||
#define ERRORSTACKSIZE (LUAI_MAXSTACK + 200)
|
#define ERRORSTACKSIZE (LUAI_MAXSTACK + 200)
|
||||||
|
|
||||||
|
|
||||||
|
/*
|
||||||
|
** Reallocate the stack to a new size, correcting all pointers into
|
||||||
|
** it. (There are pointers to a stack from its upvalues, from its list
|
||||||
|
** of call infos, plus a few individual pointers.) The reallocation is
|
||||||
|
** done in two steps (allocation + free) because the correction must be
|
||||||
|
** done while both addresses (the old stack and the new one) are valid.
|
||||||
|
** (In ISO C, any pointer use after the pointer has been deallocated is
|
||||||
|
** undefined behavior.)
|
||||||
|
** In case of allocation error, raise an error or return false according
|
||||||
|
** to 'raiseerror'.
|
||||||
|
*/
|
||||||
int luaD_reallocstack (lua_State *L, int newsize, int raiseerror) {
|
int luaD_reallocstack (lua_State *L, int newsize, int raiseerror) {
|
||||||
int lim = stacksize(L);
|
int oldsize = stacksize(L);
|
||||||
StkId newstack = luaM_reallocvector(L, L->stack,
|
int i;
|
||||||
lim + EXTRA_STACK, newsize + EXTRA_STACK, StackValue);
|
StkId newstack = luaM_reallocvector(L, NULL, 0,
|
||||||
|
newsize + EXTRA_STACK, StackValue);
|
||||||
lua_assert(newsize <= LUAI_MAXSTACK || newsize == ERRORSTACKSIZE);
|
lua_assert(newsize <= LUAI_MAXSTACK || newsize == ERRORSTACKSIZE);
|
||||||
if (unlikely(newstack == NULL)) { /* reallocation failed? */
|
if (l_unlikely(newstack == NULL)) { /* reallocation failed? */
|
||||||
if (raiseerror)
|
if (raiseerror)
|
||||||
luaM_error(L);
|
luaM_error(L);
|
||||||
else return 0; /* do not raise an error */
|
else return 0; /* do not raise an error */
|
||||||
}
|
}
|
||||||
for (; lim < newsize; lim++)
|
/* number of elements to be copied to the new stack */
|
||||||
setnilvalue(s2v(newstack + lim + EXTRA_STACK)); /* erase new segment */
|
i = ((oldsize <= newsize) ? oldsize : newsize) + EXTRA_STACK;
|
||||||
|
memcpy(newstack, L->stack, i * sizeof(StackValue));
|
||||||
|
for (; i < newsize + EXTRA_STACK; i++)
|
||||||
|
setnilvalue(s2v(newstack + i)); /* erase new segment */
|
||||||
correctstack(L, L->stack, newstack);
|
correctstack(L, L->stack, newstack);
|
||||||
|
luaM_freearray(L, L->stack, oldsize + EXTRA_STACK);
|
||||||
L->stack = newstack;
|
L->stack = newstack;
|
||||||
L->stack_last = L->stack + newsize;
|
L->stack_last = L->stack + newsize;
|
||||||
return 1;
|
return 1;
|
||||||
@@ -206,7 +218,7 @@ int luaD_reallocstack (lua_State *L, int newsize, int raiseerror) {
|
|||||||
*/
|
*/
|
||||||
int luaD_growstack (lua_State *L, int n, int raiseerror) {
|
int luaD_growstack (lua_State *L, int n, int raiseerror) {
|
||||||
int size = stacksize(L);
|
int size = stacksize(L);
|
||||||
if (unlikely(size > LUAI_MAXSTACK)) {
|
if (l_unlikely(size > LUAI_MAXSTACK)) {
|
||||||
/* if stack is larger than maximum, thread is already using the
|
/* if stack is larger than maximum, thread is already using the
|
||||||
extra space reserved for errors, that is, thread is handling
|
extra space reserved for errors, that is, thread is handling
|
||||||
a stack error; cannot grow further than that. */
|
a stack error; cannot grow further than that. */
|
||||||
@@ -222,7 +234,7 @@ int luaD_growstack (lua_State *L, int n, int raiseerror) {
|
|||||||
newsize = LUAI_MAXSTACK;
|
newsize = LUAI_MAXSTACK;
|
||||||
if (newsize < needed) /* but must respect what was asked for */
|
if (newsize < needed) /* but must respect what was asked for */
|
||||||
newsize = needed;
|
newsize = needed;
|
||||||
if (likely(newsize <= LUAI_MAXSTACK))
|
if (l_likely(newsize <= LUAI_MAXSTACK))
|
||||||
return luaD_reallocstack(L, newsize, raiseerror);
|
return luaD_reallocstack(L, newsize, raiseerror);
|
||||||
else { /* stack overflow */
|
else { /* stack overflow */
|
||||||
/* add extra size to be able to handle the error message */
|
/* add extra size to be able to handle the error message */
|
||||||
@@ -297,8 +309,8 @@ void luaD_hook (lua_State *L, int event, int line,
|
|||||||
if (hook && L->allowhook) { /* make sure there is a hook */
|
if (hook && L->allowhook) { /* make sure there is a hook */
|
||||||
int mask = CIST_HOOKED;
|
int mask = CIST_HOOKED;
|
||||||
CallInfo *ci = L->ci;
|
CallInfo *ci = L->ci;
|
||||||
ptrdiff_t top = savestack(L, L->top);
|
ptrdiff_t top = savestack(L, L->top); /* preserve original 'top' */
|
||||||
ptrdiff_t ci_top = savestack(L, ci->top);
|
ptrdiff_t ci_top = savestack(L, ci->top); /* idem for 'ci->top' */
|
||||||
lua_Debug ar;
|
lua_Debug ar;
|
||||||
ar.event = event;
|
ar.event = event;
|
||||||
ar.currentline = line;
|
ar.currentline = line;
|
||||||
@@ -308,8 +320,10 @@ void luaD_hook (lua_State *L, int event, int line,
|
|||||||
ci->u2.transferinfo.ftransfer = ftransfer;
|
ci->u2.transferinfo.ftransfer = ftransfer;
|
||||||
ci->u2.transferinfo.ntransfer = ntransfer;
|
ci->u2.transferinfo.ntransfer = ntransfer;
|
||||||
}
|
}
|
||||||
|
if (isLua(ci) && L->top < ci->top)
|
||||||
|
L->top = ci->top; /* protect entire activation register */
|
||||||
luaD_checkstack(L, LUA_MINSTACK); /* ensure minimum stack size */
|
luaD_checkstack(L, LUA_MINSTACK); /* ensure minimum stack size */
|
||||||
if (L->top + LUA_MINSTACK > ci->top)
|
if (ci->top < L->top + LUA_MINSTACK)
|
||||||
ci->top = L->top + LUA_MINSTACK;
|
ci->top = L->top + LUA_MINSTACK;
|
||||||
L->allowhook = 0; /* cannot call hooks inside a hook */
|
L->allowhook = 0; /* cannot call hooks inside a hook */
|
||||||
ci->callstatus |= mask;
|
ci->callstatus |= mask;
|
||||||
@@ -331,38 +345,40 @@ void luaD_hook (lua_State *L, int event, int line,
|
|||||||
** active.
|
** active.
|
||||||
*/
|
*/
|
||||||
void luaD_hookcall (lua_State *L, CallInfo *ci) {
|
void luaD_hookcall (lua_State *L, CallInfo *ci) {
|
||||||
int hook = (ci->callstatus & CIST_TAIL) ? LUA_HOOKTAILCALL : LUA_HOOKCALL;
|
L->oldpc = 0; /* set 'oldpc' for new function */
|
||||||
Proto *p;
|
if (L->hookmask & LUA_MASKCALL) { /* is call hook on? */
|
||||||
if (!(L->hookmask & LUA_MASKCALL)) /* some other hook? */
|
int event = (ci->callstatus & CIST_TAIL) ? LUA_HOOKTAILCALL
|
||||||
return; /* don't call hook */
|
: LUA_HOOKCALL;
|
||||||
p = clLvalue(s2v(ci->func))->p;
|
Proto *p = ci_func(ci)->p;
|
||||||
L->top = ci->top; /* prepare top */
|
ci->u.l.savedpc++; /* hooks assume 'pc' is already incremented */
|
||||||
ci->u.l.savedpc++; /* hooks assume 'pc' is already incremented */
|
luaD_hook(L, event, -1, 1, p->numparams);
|
||||||
luaD_hook(L, hook, -1, 1, p->numparams);
|
ci->u.l.savedpc--; /* correct 'pc' */
|
||||||
ci->u.l.savedpc--; /* correct 'pc' */
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
static StkId rethook (lua_State *L, CallInfo *ci, StkId firstres, int nres) {
|
/*
|
||||||
ptrdiff_t oldtop = savestack(L, L->top); /* hook may change top */
|
** Executes a return hook for Lua and C functions and sets/corrects
|
||||||
int delta = 0;
|
** 'oldpc'. (Note that this correction is needed by the line hook, so it
|
||||||
if (isLuacode(ci)) {
|
** is done even when return hooks are off.)
|
||||||
Proto *p = ci_func(ci)->p;
|
*/
|
||||||
if (p->is_vararg)
|
static void rethook (lua_State *L, CallInfo *ci, int nres) {
|
||||||
delta = ci->u.l.nextraargs + p->numparams + 1;
|
|
||||||
if (L->top < ci->top)
|
|
||||||
L->top = ci->top; /* correct top to run hook */
|
|
||||||
}
|
|
||||||
if (L->hookmask & LUA_MASKRET) { /* is return hook on? */
|
if (L->hookmask & LUA_MASKRET) { /* is return hook on? */
|
||||||
|
StkId firstres = L->top - nres; /* index of first result */
|
||||||
|
int delta = 0; /* correction for vararg functions */
|
||||||
int ftransfer;
|
int ftransfer;
|
||||||
|
if (isLua(ci)) {
|
||||||
|
Proto *p = ci_func(ci)->p;
|
||||||
|
if (p->is_vararg)
|
||||||
|
delta = ci->u.l.nextraargs + p->numparams + 1;
|
||||||
|
}
|
||||||
ci->func += delta; /* if vararg, back to virtual 'func' */
|
ci->func += delta; /* if vararg, back to virtual 'func' */
|
||||||
ftransfer = cast(unsigned short, firstres - ci->func);
|
ftransfer = cast(unsigned short, firstres - ci->func);
|
||||||
luaD_hook(L, LUA_HOOKRET, -1, ftransfer, nres); /* call it */
|
luaD_hook(L, LUA_HOOKRET, -1, ftransfer, nres); /* call it */
|
||||||
ci->func -= delta;
|
ci->func -= delta;
|
||||||
}
|
}
|
||||||
if (isLua(ci = ci->previous))
|
if (isLua(ci = ci->previous))
|
||||||
L->oldpc = pcRel(ci->u.l.savedpc, ci_func(ci)->p); /* update 'oldpc' */
|
L->oldpc = pcRel(ci->u.l.savedpc, ci_func(ci)->p); /* set 'oldpc' */
|
||||||
return restorestack(L, oldtop);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
@@ -374,8 +390,8 @@ static StkId rethook (lua_State *L, CallInfo *ci, StkId firstres, int nres) {
|
|||||||
void luaD_tryfuncTM (lua_State *L, StkId func) {
|
void luaD_tryfuncTM (lua_State *L, StkId func) {
|
||||||
const TValue *tm = luaT_gettmbyobj(L, s2v(func), TM_CALL);
|
const TValue *tm = luaT_gettmbyobj(L, s2v(func), TM_CALL);
|
||||||
StkId p;
|
StkId p;
|
||||||
if (unlikely(ttisnil(tm)))
|
if (l_unlikely(ttisnil(tm)))
|
||||||
luaG_typeerror(L, s2v(func), "call"); /* nothing to call */
|
luaG_callerror(L, s2v(func)); /* nothing to call */
|
||||||
for (p = L->top; p > func; p--) /* open space for metamethod */
|
for (p = L->top; p > func; p--) /* open space for metamethod */
|
||||||
setobjs2s(L, p, p-1);
|
setobjs2s(L, p, p-1);
|
||||||
L->top++; /* stack space pre-allocated by the caller */
|
L->top++; /* stack space pre-allocated by the caller */
|
||||||
@@ -399,27 +415,34 @@ static void moveresults (lua_State *L, StkId res, int nres, int wanted) {
|
|||||||
case 1: /* one value needed */
|
case 1: /* one value needed */
|
||||||
if (nres == 0) /* no results? */
|
if (nres == 0) /* no results? */
|
||||||
setnilvalue(s2v(res)); /* adjust with nil */
|
setnilvalue(s2v(res)); /* adjust with nil */
|
||||||
else
|
else /* at least one result */
|
||||||
setobjs2s(L, res, L->top - nres); /* move it to proper place */
|
setobjs2s(L, res, L->top - nres); /* move it to proper place */
|
||||||
L->top = res + 1;
|
L->top = res + 1;
|
||||||
return;
|
return;
|
||||||
case LUA_MULTRET:
|
case LUA_MULTRET:
|
||||||
wanted = nres; /* we want all results */
|
wanted = nres; /* we want all results */
|
||||||
break;
|
break;
|
||||||
default: /* multiple results (or to-be-closed variables) */
|
default: /* two/more results and/or to-be-closed variables */
|
||||||
if (hastocloseCfunc(wanted)) { /* to-be-closed variables? */
|
if (hastocloseCfunc(wanted)) { /* to-be-closed variables? */
|
||||||
ptrdiff_t savedres = savestack(L, res);
|
ptrdiff_t savedres = savestack(L, res);
|
||||||
luaF_close(L, res, LUA_OK); /* may change the stack */
|
L->ci->callstatus |= CIST_CLSRET; /* in case of yields */
|
||||||
res = restorestack(L, savedres);
|
L->ci->u2.nres = nres;
|
||||||
wanted = codeNresults(wanted); /* correct value */
|
luaF_close(L, res, CLOSEKTOP, 1);
|
||||||
|
L->ci->callstatus &= ~CIST_CLSRET;
|
||||||
|
if (L->hookmask) /* if needed, call hook after '__close's */
|
||||||
|
rethook(L, L->ci, nres);
|
||||||
|
res = restorestack(L, savedres); /* close and hook can move stack */
|
||||||
|
wanted = decodeNresults(wanted);
|
||||||
if (wanted == LUA_MULTRET)
|
if (wanted == LUA_MULTRET)
|
||||||
wanted = nres;
|
wanted = nres; /* we want all results */
|
||||||
}
|
}
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
/* generic case */
|
||||||
firstresult = L->top - nres; /* index of first result */
|
firstresult = L->top - nres; /* index of first result */
|
||||||
/* move all results to correct place */
|
if (nres > wanted) /* extra results? */
|
||||||
for (i = 0; i < nres && i < wanted; i++)
|
nres = wanted; /* don't need them */
|
||||||
|
for (i = 0; i < nres; i++) /* move all results to correct place */
|
||||||
setobjs2s(L, res + i, firstresult + i);
|
setobjs2s(L, res + i, firstresult + i);
|
||||||
for (; i < wanted; i++) /* complete wanted number of results */
|
for (; i < wanted; i++) /* complete wanted number of results */
|
||||||
setnilvalue(s2v(res + i));
|
setnilvalue(s2v(res + i));
|
||||||
@@ -428,15 +451,21 @@ static void moveresults (lua_State *L, StkId res, int nres, int wanted) {
|
|||||||
|
|
||||||
|
|
||||||
/*
|
/*
|
||||||
** Finishes a function call: calls hook if necessary, removes CallInfo,
|
** Finishes a function call: calls hook if necessary, moves current
|
||||||
** moves current number of results to proper place.
|
** number of results to proper place, and returns to previous call
|
||||||
|
** info. If function has to close variables, hook must be called after
|
||||||
|
** that.
|
||||||
*/
|
*/
|
||||||
void luaD_poscall (lua_State *L, CallInfo *ci, int nres) {
|
void luaD_poscall (lua_State *L, CallInfo *ci, int nres) {
|
||||||
if (L->hookmask)
|
int wanted = ci->nresults;
|
||||||
L->top = rethook(L, ci, L->top - nres, nres);
|
if (l_unlikely(L->hookmask && !hastocloseCfunc(wanted)))
|
||||||
L->ci = ci->previous; /* back to caller */
|
rethook(L, ci, nres);
|
||||||
/* move results to proper place */
|
/* move results to proper place */
|
||||||
moveresults(L, ci->func, nres, ci->nresults);
|
moveresults(L, ci->func, nres, wanted);
|
||||||
|
/* function cannot be in any of these cases when returning */
|
||||||
|
lua_assert(!(ci->callstatus &
|
||||||
|
(CIST_HOOKED | CIST_YPCALL | CIST_FIN | CIST_TRAN | CIST_CLSRET)));
|
||||||
|
L->ci = ci->previous; /* back to caller (after closing variables) */
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
@@ -495,7 +524,7 @@ CallInfo *luaD_precall (lua_State *L, StkId func, int nresults) {
|
|||||||
ci->top = L->top + LUA_MINSTACK;
|
ci->top = L->top + LUA_MINSTACK;
|
||||||
ci->func = func;
|
ci->func = func;
|
||||||
lua_assert(ci->top <= L->stack_last);
|
lua_assert(ci->top <= L->stack_last);
|
||||||
if (L->hookmask & LUA_MASKCALL) {
|
if (l_unlikely(L->hookmask & LUA_MASKCALL)) {
|
||||||
int narg = cast_int(L->top - func) - 1;
|
int narg = cast_int(L->top - func) - 1;
|
||||||
luaD_hook(L, LUA_HOOKCALL, -1, 1, narg);
|
luaD_hook(L, LUA_HOOKCALL, -1, 1, narg);
|
||||||
}
|
}
|
||||||
@@ -541,7 +570,7 @@ CallInfo *luaD_precall (lua_State *L, StkId func, int nresults) {
|
|||||||
static void ccall (lua_State *L, StkId func, int nResults, int inc) {
|
static void ccall (lua_State *L, StkId func, int nResults, int inc) {
|
||||||
CallInfo *ci;
|
CallInfo *ci;
|
||||||
L->nCcalls += inc;
|
L->nCcalls += inc;
|
||||||
if (unlikely(getCcalls(L) >= LUAI_MAXCCALLS))
|
if (l_unlikely(getCcalls(L) >= LUAI_MAXCCALLS))
|
||||||
luaE_checkcstack(L);
|
luaE_checkcstack(L);
|
||||||
if ((ci = luaD_precall(L, func, nResults)) != NULL) { /* Lua function? */
|
if ((ci = luaD_precall(L, func, nResults)) != NULL) { /* Lua function? */
|
||||||
ci->callstatus = CIST_FRESH; /* mark that it is a "fresh" execute */
|
ci->callstatus = CIST_FRESH; /* mark that it is a "fresh" execute */
|
||||||
@@ -568,27 +597,74 @@ void luaD_callnoyield (lua_State *L, StkId func, int nResults) {
|
|||||||
|
|
||||||
|
|
||||||
/*
|
/*
|
||||||
** Completes the execution of an interrupted C function, calling its
|
** Finish the job of 'lua_pcallk' after it was interrupted by an yield.
|
||||||
** continuation function.
|
** (The caller, 'finishCcall', does the final call to 'adjustresults'.)
|
||||||
|
** The main job is to complete the 'luaD_pcall' called by 'lua_pcallk'.
|
||||||
|
** If a '__close' method yields here, eventually control will be back
|
||||||
|
** to 'finishCcall' (when that '__close' method finally returns) and
|
||||||
|
** 'finishpcallk' will run again and close any still pending '__close'
|
||||||
|
** methods. Similarly, if a '__close' method errs, 'precover' calls
|
||||||
|
** 'unroll' which calls ''finishCcall' and we are back here again, to
|
||||||
|
** close any pending '__close' methods.
|
||||||
|
** Note that, up to the call to 'luaF_close', the corresponding
|
||||||
|
** 'CallInfo' is not modified, so that this repeated run works like the
|
||||||
|
** first one (except that it has at least one less '__close' to do). In
|
||||||
|
** particular, field CIST_RECST preserves the error status across these
|
||||||
|
** multiple runs, changing only if there is a new error.
|
||||||
*/
|
*/
|
||||||
static void finishCcall (lua_State *L, int status) {
|
static int finishpcallk (lua_State *L, CallInfo *ci) {
|
||||||
CallInfo *ci = L->ci;
|
int status = getcistrecst(ci); /* get original status */
|
||||||
int n;
|
if (l_likely(status == LUA_OK)) /* no error? */
|
||||||
/* must have a continuation and must be able to call it */
|
status = LUA_YIELD; /* was interrupted by an yield */
|
||||||
lua_assert(ci->u.c.k != NULL && yieldable(L));
|
else { /* error */
|
||||||
/* error status can only happen in a protected call */
|
StkId func = restorestack(L, ci->u2.funcidx);
|
||||||
lua_assert((ci->callstatus & CIST_YPCALL) || status == LUA_YIELD);
|
L->allowhook = getoah(ci->callstatus); /* restore 'allowhook' */
|
||||||
if (ci->callstatus & CIST_YPCALL) { /* was inside a pcall? */
|
luaF_close(L, func, status, 1); /* can yield or raise an error */
|
||||||
ci->callstatus &= ~CIST_YPCALL; /* continuation is also inside it */
|
func = restorestack(L, ci->u2.funcidx); /* stack may be moved */
|
||||||
L->errfunc = ci->u.c.old_errfunc; /* with the same error function */
|
luaD_seterrorobj(L, status, func);
|
||||||
|
luaD_shrinkstack(L); /* restore stack size in case of overflow */
|
||||||
|
setcistrecst(ci, LUA_OK); /* clear original status */
|
||||||
|
}
|
||||||
|
ci->callstatus &= ~CIST_YPCALL;
|
||||||
|
L->errfunc = ci->u.c.old_errfunc;
|
||||||
|
/* if it is here, there were errors or yields; unlike 'lua_pcallk',
|
||||||
|
do not change status */
|
||||||
|
return status;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
/*
|
||||||
|
** Completes the execution of a C function interrupted by an yield.
|
||||||
|
** The interruption must have happened while the function was either
|
||||||
|
** closing its tbc variables in 'moveresults' or executing
|
||||||
|
** 'lua_callk'/'lua_pcallk'. In the first case, it just redoes
|
||||||
|
** 'luaD_poscall'. In the second case, the call to 'finishpcallk'
|
||||||
|
** finishes the interrupted execution of 'lua_pcallk'. After that, it
|
||||||
|
** calls the continuation of the interrupted function and finally it
|
||||||
|
** completes the job of the 'luaD_call' that called the function. In
|
||||||
|
** the call to 'adjustresults', we do not know the number of results
|
||||||
|
** of the function called by 'lua_callk'/'lua_pcallk', so we are
|
||||||
|
** conservative and use LUA_MULTRET (always adjust).
|
||||||
|
*/
|
||||||
|
static void finishCcall (lua_State *L, CallInfo *ci) {
|
||||||
|
int n; /* actual number of results from C function */
|
||||||
|
if (ci->callstatus & CIST_CLSRET) { /* was returning? */
|
||||||
|
lua_assert(hastocloseCfunc(ci->nresults));
|
||||||
|
n = ci->u2.nres; /* just redo 'luaD_poscall' */
|
||||||
|
/* don't need to reset CIST_CLSRET, as it will be set again anyway */
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
int status = LUA_YIELD; /* default if there were no errors */
|
||||||
|
/* must have a continuation and must be able to call it */
|
||||||
|
lua_assert(ci->u.c.k != NULL && yieldable(L));
|
||||||
|
if (ci->callstatus & CIST_YPCALL) /* was inside a 'lua_pcallk'? */
|
||||||
|
status = finishpcallk(L, ci); /* finish it */
|
||||||
|
adjustresults(L, LUA_MULTRET); /* finish 'lua_callk' */
|
||||||
|
lua_unlock(L);
|
||||||
|
n = (*ci->u.c.k)(L, status, ci->u.c.ctx); /* call continuation */
|
||||||
|
lua_lock(L);
|
||||||
|
api_checknelems(L, n);
|
||||||
}
|
}
|
||||||
/* finish 'lua_callk'/'lua_pcall'; CIST_YPCALL and 'errfunc' already
|
|
||||||
handled */
|
|
||||||
adjustresults(L, ci->nresults);
|
|
||||||
lua_unlock(L);
|
|
||||||
n = (*ci->u.c.k)(L, status, ci->u.c.ctx); /* call continuation function */
|
|
||||||
lua_lock(L);
|
|
||||||
api_checknelems(L, n);
|
|
||||||
luaD_poscall(L, ci, n); /* finish 'luaD_call' */
|
luaD_poscall(L, ci, n); /* finish 'luaD_call' */
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -596,18 +672,14 @@ static void finishCcall (lua_State *L, int status) {
|
|||||||
/*
|
/*
|
||||||
** Executes "full continuation" (everything in the stack) of a
|
** Executes "full continuation" (everything in the stack) of a
|
||||||
** previously interrupted coroutine until the stack is empty (or another
|
** previously interrupted coroutine until the stack is empty (or another
|
||||||
** interruption long-jumps out of the loop). If the coroutine is
|
** interruption long-jumps out of the loop).
|
||||||
** recovering from an error, 'ud' points to the error status, which must
|
|
||||||
** be passed to the first continuation function (otherwise the default
|
|
||||||
** status is LUA_YIELD).
|
|
||||||
*/
|
*/
|
||||||
static void unroll (lua_State *L, void *ud) {
|
static void unroll (lua_State *L, void *ud) {
|
||||||
CallInfo *ci;
|
CallInfo *ci;
|
||||||
if (ud != NULL) /* error status? */
|
UNUSED(ud);
|
||||||
finishCcall(L, *(int *)ud); /* finish 'lua_pcallk' callee */
|
|
||||||
while ((ci = L->ci) != &L->base_ci) { /* something in the stack */
|
while ((ci = L->ci) != &L->base_ci) { /* something in the stack */
|
||||||
if (!isLua(ci)) /* C function? */
|
if (!isLua(ci)) /* C function? */
|
||||||
finishCcall(L, LUA_YIELD); /* complete its execution */
|
finishCcall(L, ci); /* complete its execution */
|
||||||
else { /* Lua function */
|
else { /* Lua function */
|
||||||
luaV_finishOp(L); /* finish interrupted instruction */
|
luaV_finishOp(L); /* finish interrupted instruction */
|
||||||
luaV_execute(L, ci); /* execute down to higher C 'boundary' */
|
luaV_execute(L, ci); /* execute down to higher C 'boundary' */
|
||||||
@@ -630,28 +702,6 @@ static CallInfo *findpcall (lua_State *L) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
/*
|
|
||||||
** Recovers from an error in a coroutine. Finds a recover point (if
|
|
||||||
** there is one) and completes the execution of the interrupted
|
|
||||||
** 'luaD_pcall'. If there is no recover point, returns zero.
|
|
||||||
*/
|
|
||||||
static int recover (lua_State *L, int status) {
|
|
||||||
StkId oldtop;
|
|
||||||
CallInfo *ci = findpcall(L);
|
|
||||||
if (ci == NULL) return 0; /* no recovery point */
|
|
||||||
/* "finish" luaD_pcall */
|
|
||||||
oldtop = restorestack(L, ci->u2.funcidx);
|
|
||||||
L->ci = ci;
|
|
||||||
L->allowhook = getoah(ci->callstatus); /* restore original 'allowhook' */
|
|
||||||
status = luaF_close(L, oldtop, status); /* may change the stack */
|
|
||||||
oldtop = restorestack(L, ci->u2.funcidx);
|
|
||||||
luaD_seterrorobj(L, status, oldtop);
|
|
||||||
luaD_shrinkstack(L); /* restore stack size in case of overflow */
|
|
||||||
L->errfunc = ci->u.c.old_errfunc;
|
|
||||||
return 1; /* continue running the coroutine */
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
/*
|
/*
|
||||||
** Signal an error in the call to 'lua_resume', not in the execution
|
** Signal an error in the call to 'lua_resume', not in the execution
|
||||||
** of the coroutine itself. (Such errors should not be handled by any
|
** of the coroutine itself. (Such errors should not be handled by any
|
||||||
@@ -683,8 +733,10 @@ static void resume (lua_State *L, void *ud) {
|
|||||||
lua_assert(L->status == LUA_YIELD);
|
lua_assert(L->status == LUA_YIELD);
|
||||||
L->status = LUA_OK; /* mark that it is running (again) */
|
L->status = LUA_OK; /* mark that it is running (again) */
|
||||||
luaE_incCstack(L); /* control the C stack */
|
luaE_incCstack(L); /* control the C stack */
|
||||||
if (isLua(ci)) /* yielded inside a hook? */
|
if (isLua(ci)) { /* yielded inside a hook? */
|
||||||
|
L->top = firstArg; /* discard arguments */
|
||||||
luaV_execute(L, ci); /* just continue running Lua code */
|
luaV_execute(L, ci); /* just continue running Lua code */
|
||||||
|
}
|
||||||
else { /* 'common' yield */
|
else { /* 'common' yield */
|
||||||
if (ci->u.c.k != NULL) { /* does it have a continuation function? */
|
if (ci->u.c.k != NULL) { /* does it have a continuation function? */
|
||||||
lua_unlock(L);
|
lua_unlock(L);
|
||||||
@@ -698,6 +750,26 @@ static void resume (lua_State *L, void *ud) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
/*
|
||||||
|
** Unrolls a coroutine in protected mode while there are recoverable
|
||||||
|
** errors, that is, errors inside a protected call. (Any error
|
||||||
|
** interrupts 'unroll', and this loop protects it again so it can
|
||||||
|
** continue.) Stops with a normal end (status == LUA_OK), an yield
|
||||||
|
** (status == LUA_YIELD), or an unprotected error ('findpcall' doesn't
|
||||||
|
** find a recover point).
|
||||||
|
*/
|
||||||
|
static int precover (lua_State *L, int status) {
|
||||||
|
CallInfo *ci;
|
||||||
|
while (errorstatus(status) && (ci = findpcall(L)) != NULL) {
|
||||||
|
L->ci = ci; /* go down to recovery functions */
|
||||||
|
setcistrecst(ci, status); /* status to finish 'pcall' */
|
||||||
|
status = luaD_rawrunprotected(L, unroll, NULL);
|
||||||
|
}
|
||||||
|
return status;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
LUA_API int lua_resume (lua_State *L, lua_State *from, int nargs,
|
LUA_API int lua_resume (lua_State *L, lua_State *from, int nargs,
|
||||||
int *nresults) {
|
int *nresults) {
|
||||||
int status;
|
int status;
|
||||||
@@ -715,11 +787,8 @@ LUA_API int lua_resume (lua_State *L, lua_State *from, int nargs,
|
|||||||
api_checknelems(L, (L->status == LUA_OK) ? nargs + 1 : nargs);
|
api_checknelems(L, (L->status == LUA_OK) ? nargs + 1 : nargs);
|
||||||
status = luaD_rawrunprotected(L, resume, &nargs);
|
status = luaD_rawrunprotected(L, resume, &nargs);
|
||||||
/* continue running after recoverable errors */
|
/* continue running after recoverable errors */
|
||||||
while (errorstatus(status) && recover(L, status)) {
|
status = precover(L, status);
|
||||||
/* unroll continuation */
|
if (l_likely(!errorstatus(status)))
|
||||||
status = luaD_rawrunprotected(L, unroll, &status);
|
|
||||||
}
|
|
||||||
if (likely(!errorstatus(status)))
|
|
||||||
lua_assert(status == L->status); /* normal end or yield */
|
lua_assert(status == L->status); /* normal end or yield */
|
||||||
else { /* unrecoverable error */
|
else { /* unrecoverable error */
|
||||||
L->status = cast_byte(status); /* mark thread as 'dead' */
|
L->status = cast_byte(status); /* mark thread as 'dead' */
|
||||||
@@ -745,22 +814,22 @@ LUA_API int lua_yieldk (lua_State *L, int nresults, lua_KContext ctx,
|
|||||||
lua_lock(L);
|
lua_lock(L);
|
||||||
ci = L->ci;
|
ci = L->ci;
|
||||||
api_checknelems(L, nresults);
|
api_checknelems(L, nresults);
|
||||||
if (unlikely(!yieldable(L))) {
|
if (l_unlikely(!yieldable(L))) {
|
||||||
if (L != G(L)->mainthread)
|
if (L != G(L)->mainthread)
|
||||||
luaG_runerror(L, "attempt to yield across a C-call boundary");
|
luaG_runerror(L, "attempt to yield across a C-call boundary");
|
||||||
else
|
else
|
||||||
luaG_runerror(L, "attempt to yield from outside a coroutine");
|
luaG_runerror(L, "attempt to yield from outside a coroutine");
|
||||||
}
|
}
|
||||||
L->status = LUA_YIELD;
|
L->status = LUA_YIELD;
|
||||||
|
ci->u2.nyield = nresults; /* save number of results */
|
||||||
if (isLua(ci)) { /* inside a hook? */
|
if (isLua(ci)) { /* inside a hook? */
|
||||||
lua_assert(!isLuacode(ci));
|
lua_assert(!isLuacode(ci));
|
||||||
|
api_check(L, nresults == 0, "hooks cannot yield values");
|
||||||
api_check(L, k == NULL, "hooks cannot continue after yielding");
|
api_check(L, k == NULL, "hooks cannot continue after yielding");
|
||||||
ci->u2.nyield = 0; /* no results */
|
|
||||||
}
|
}
|
||||||
else {
|
else {
|
||||||
if ((ci->u.c.k = k) != NULL) /* is there a continuation? */
|
if ((ci->u.c.k = k) != NULL) /* is there a continuation? */
|
||||||
ci->u.c.ctx = ctx; /* save context */
|
ci->u.c.ctx = ctx; /* save context */
|
||||||
ci->u2.nyield = nresults; /* save number of results */
|
|
||||||
luaD_throw(L, LUA_YIELD);
|
luaD_throw(L, LUA_YIELD);
|
||||||
}
|
}
|
||||||
lua_assert(ci->callstatus & CIST_HOOKED); /* must be inside a hook */
|
lua_assert(ci->callstatus & CIST_HOOKED); /* must be inside a hook */
|
||||||
@@ -769,6 +838,45 @@ LUA_API int lua_yieldk (lua_State *L, int nresults, lua_KContext ctx,
|
|||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
/*
|
||||||
|
** Auxiliary structure to call 'luaF_close' in protected mode.
|
||||||
|
*/
|
||||||
|
struct CloseP {
|
||||||
|
StkId level;
|
||||||
|
int status;
|
||||||
|
};
|
||||||
|
|
||||||
|
|
||||||
|
/*
|
||||||
|
** Auxiliary function to call 'luaF_close' in protected mode.
|
||||||
|
*/
|
||||||
|
static void closepaux (lua_State *L, void *ud) {
|
||||||
|
struct CloseP *pcl = cast(struct CloseP *, ud);
|
||||||
|
luaF_close(L, pcl->level, pcl->status, 0);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
/*
|
||||||
|
** Calls 'luaF_close' in protected mode. Return the original status
|
||||||
|
** or, in case of errors, the new status.
|
||||||
|
*/
|
||||||
|
int luaD_closeprotected (lua_State *L, ptrdiff_t level, int status) {
|
||||||
|
CallInfo *old_ci = L->ci;
|
||||||
|
lu_byte old_allowhooks = L->allowhook;
|
||||||
|
for (;;) { /* keep closing upvalues until no more errors */
|
||||||
|
struct CloseP pcl;
|
||||||
|
pcl.level = restorestack(L, level); pcl.status = status;
|
||||||
|
status = luaD_rawrunprotected(L, &closepaux, &pcl);
|
||||||
|
if (l_likely(status == LUA_OK)) /* no more errors? */
|
||||||
|
return pcl.status;
|
||||||
|
else { /* an error occurred; restore saved state and repeat */
|
||||||
|
L->ci = old_ci;
|
||||||
|
L->allowhook = old_allowhooks;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
/*
|
/*
|
||||||
** Call the C function 'func' in protected mode, restoring basic
|
** Call the C function 'func' in protected mode, restoring basic
|
||||||
** thread information ('allowhook', etc.) and in particular
|
** thread information ('allowhook', etc.) and in particular
|
||||||
@@ -782,13 +890,11 @@ int luaD_pcall (lua_State *L, Pfunc func, void *u,
|
|||||||
ptrdiff_t old_errfunc = L->errfunc;
|
ptrdiff_t old_errfunc = L->errfunc;
|
||||||
L->errfunc = ef;
|
L->errfunc = ef;
|
||||||
status = luaD_rawrunprotected(L, func, u);
|
status = luaD_rawrunprotected(L, func, u);
|
||||||
if (unlikely(status != LUA_OK)) { /* an error occurred? */
|
if (l_unlikely(status != LUA_OK)) { /* an error occurred? */
|
||||||
StkId oldtop = restorestack(L, old_top);
|
|
||||||
L->ci = old_ci;
|
L->ci = old_ci;
|
||||||
L->allowhook = old_allowhooks;
|
L->allowhook = old_allowhooks;
|
||||||
status = luaF_close(L, oldtop, status);
|
status = luaD_closeprotected(L, old_top, status);
|
||||||
oldtop = restorestack(L, old_top); /* previous call may change stack */
|
luaD_seterrorobj(L, status, restorestack(L, old_top));
|
||||||
luaD_seterrorobj(L, status, oldtop);
|
|
||||||
luaD_shrinkstack(L); /* restore stack size in case of overflow */
|
luaD_shrinkstack(L); /* restore stack size in case of overflow */
|
||||||
}
|
}
|
||||||
L->errfunc = old_errfunc;
|
L->errfunc = old_errfunc;
|
||||||
|
|||||||
3
ldo.h
3
ldo.h
@@ -23,7 +23,7 @@
|
|||||||
** at every check.
|
** at every check.
|
||||||
*/
|
*/
|
||||||
#define luaD_checkstackaux(L,n,pre,pos) \
|
#define luaD_checkstackaux(L,n,pre,pos) \
|
||||||
if (L->stack_last - L->top <= (n)) \
|
if (l_unlikely(L->stack_last - L->top <= (n))) \
|
||||||
{ pre; luaD_growstack(L, n, 1); pos; } \
|
{ pre; luaD_growstack(L, n, 1); pos; } \
|
||||||
else { condmovestack(L,pre,pos); }
|
else { condmovestack(L,pre,pos); }
|
||||||
|
|
||||||
@@ -63,6 +63,7 @@ LUAI_FUNC CallInfo *luaD_precall (lua_State *L, StkId func, int nResults);
|
|||||||
LUAI_FUNC void luaD_call (lua_State *L, StkId func, int nResults);
|
LUAI_FUNC void luaD_call (lua_State *L, StkId func, int nResults);
|
||||||
LUAI_FUNC void luaD_callnoyield (lua_State *L, StkId func, int nResults);
|
LUAI_FUNC void luaD_callnoyield (lua_State *L, StkId func, int nResults);
|
||||||
LUAI_FUNC void luaD_tryfuncTM (lua_State *L, StkId func);
|
LUAI_FUNC void luaD_tryfuncTM (lua_State *L, StkId func);
|
||||||
|
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,
|
LUAI_FUNC int luaD_pcall (lua_State *L, Pfunc func, void *u,
|
||||||
ptrdiff_t oldtop, ptrdiff_t ef);
|
ptrdiff_t oldtop, ptrdiff_t ef);
|
||||||
LUAI_FUNC void luaD_poscall (lua_State *L, CallInfo *ci, int nres);
|
LUAI_FUNC void luaD_poscall (lua_State *L, CallInfo *ci, int nres);
|
||||||
|
|||||||
170
lfunc.c
170
lfunc.c
@@ -100,115 +100,83 @@ UpVal *luaF_findupval (lua_State *L, StkId level) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
static void callclose (lua_State *L, void *ud) {
|
|
||||||
UNUSED(ud);
|
|
||||||
luaD_callnoyield(L, L->top - 3, 0);
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
/*
|
/*
|
||||||
** Prepare closing method plus its arguments for object 'obj' with
|
** Call closing method for object 'obj' with error message 'err'. The
|
||||||
** error message 'err'. (This function assumes EXTRA_STACK.)
|
** boolean 'yy' controls whether the call is yieldable.
|
||||||
|
** (This function assumes EXTRA_STACK.)
|
||||||
*/
|
*/
|
||||||
static int prepclosingmethod (lua_State *L, TValue *obj, TValue *err) {
|
static void callclosemethod (lua_State *L, TValue *obj, TValue *err, int yy) {
|
||||||
StkId top = L->top;
|
StkId top = L->top;
|
||||||
const TValue *tm = luaT_gettmbyobj(L, obj, TM_CLOSE);
|
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, tm); /* will call metamethod... */
|
||||||
setobj2s(L, top + 1, obj); /* with 'self' as the 1st argument */
|
setobj2s(L, top + 1, obj); /* with 'self' as the 1st argument */
|
||||||
setobj2s(L, top + 2, err); /* and error msg. as 2nd argument */
|
setobj2s(L, top + 2, err); /* and error msg. as 2nd argument */
|
||||||
L->top = top + 3; /* add function and arguments */
|
L->top = top + 3; /* add function and arguments */
|
||||||
return 1;
|
if (yy)
|
||||||
|
luaD_call(L, top, 0);
|
||||||
|
else
|
||||||
|
luaD_callnoyield(L, top, 0);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
/*
|
/*
|
||||||
** Raise an error with message 'msg', inserting the name of the
|
** Check whether object at given level has a close metamethod and raise
|
||||||
** local variable at position 'level' in the stack.
|
** an error if not.
|
||||||
*/
|
*/
|
||||||
static void varerror (lua_State *L, StkId level, const char *msg) {
|
static void checkclosemth (lua_State *L, StkId level) {
|
||||||
int idx = cast_int(level - L->ci->func);
|
const TValue *tm = luaT_gettmbyobj(L, s2v(level), TM_CLOSE);
|
||||||
const char *vname = luaG_findlocal(L, L->ci, idx, NULL);
|
if (ttisnil(tm)) { /* no metamethod? */
|
||||||
if (vname == NULL) vname = "?";
|
int idx = cast_int(level - L->ci->func); /* variable index */
|
||||||
luaG_runerror(L, msg, vname);
|
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
|
** Prepare and call a closing method.
|
||||||
** inside the original protected call, and so any error will be handled
|
** If status is CLOSEKTOP, the call to the closing method will be pushed
|
||||||
** there. Otherwise, a previous error already activated the original
|
** at the top of the stack. Otherwise, values can be pushed right after
|
||||||
** protected call, and so the call to the closing method must be
|
** the 'level' of the upvalue being closed, as everything after that
|
||||||
** protected here. (A status == CLOSEPROTECT behaves like a previous
|
** won't be used again.
|
||||||
** error, to also run the closing method in protected mode).
|
|
||||||
** If status is OK, the call to the closing method will be pushed
|
|
||||||
** at the top of the stack. Otherwise, values are pushed after
|
|
||||||
** the 'level' of the upvalue being closed, as everything after
|
|
||||||
** that won't be used again.
|
|
||||||
*/
|
*/
|
||||||
static int callclosemth (lua_State *L, StkId level, int status) {
|
static void prepcallclosemth (lua_State *L, StkId level, int status, int yy) {
|
||||||
TValue *uv = s2v(level); /* value being closed */
|
TValue *uv = s2v(level); /* value being closed */
|
||||||
if (likely(status == LUA_OK)) {
|
TValue *errobj;
|
||||||
if (prepclosingmethod(L, uv, &G(L)->nilvalue)) /* something to call? */
|
if (status == CLOSEKTOP)
|
||||||
callclose(L, NULL); /* call closing method */
|
errobj = &G(L)->nilvalue; /* error object is nil */
|
||||||
else if (!l_isfalse(uv)) /* non-closable non-false value? */
|
else { /* 'luaD_seterrorobj' will set top to level + 2 */
|
||||||
varerror(L, level, "attempt to close non-closable variable '%s'");
|
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 */
|
callclosemethod(L, uv, errobj, yy);
|
||||||
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;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
/*
|
/*
|
||||||
** Try to create a to-be-closed upvalue
|
** Maximum value for deltas in 'tbclist', dependent on the type
|
||||||
** (can raise a memory-allocation error)
|
** of delta. (This macro assumes that an 'L' is in scope where it
|
||||||
|
** is used.)
|
||||||
*/
|
*/
|
||||||
static void trynewtbcupval (lua_State *L, void *ud) {
|
#define MAXDELTA \
|
||||||
newupval(L, 1, cast(StkId, ud), &L->openupval);
|
((256ul << ((sizeof(L->stack->tbclist.delta) - 1) * 8)) - 1)
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
/*
|
/*
|
||||||
** Create a to-be-closed upvalue. If there is a memory error
|
** Insert a variable in the list of to-be-closed variables.
|
||||||
** when creating the upvalue, the closing method must be called here,
|
|
||||||
** as there is no upvalue to call it later.
|
|
||||||
*/
|
*/
|
||||||
void luaF_newtbcupval (lua_State *L, StkId level) {
|
void luaF_newtbcupval (lua_State *L, StkId level) {
|
||||||
TValue *obj = s2v(level);
|
lua_assert(level > L->tbclist);
|
||||||
lua_assert(L->openupval == NULL || uplevel(L->openupval) < level);
|
if (l_isfalse(s2v(level)))
|
||||||
if (!l_isfalse(obj)) { /* false doesn't need to be closed */
|
return; /* false doesn't need to be closed */
|
||||||
int status;
|
checkclosemth(L, level); /* value must have a close method */
|
||||||
const TValue *tm = luaT_gettmbyobj(L, obj, TM_CLOSE);
|
while (cast_uint(level - L->tbclist) > MAXDELTA) {
|
||||||
if (ttisnil(tm)) /* no metamethod? */
|
L->tbclist += MAXDELTA; /* create a dummy node at maximum delta */
|
||||||
varerror(L, level, "variable '%s' got a non-closable value");
|
L->tbclist->tbclist.delta = 0;
|
||||||
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 */
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
level->tbclist.delta = cast(unsigned short, level - L->tbclist);
|
||||||
|
L->tbclist = level;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
@@ -220,18 +188,16 @@ 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;
|
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 */
|
TValue *slot = &uv->u.value; /* new position for value */
|
||||||
lua_assert(uplevel(uv) < L->top);
|
lua_assert(uplevel(uv) < L->top);
|
||||||
if (uv->tbc && status != NOCLOSINGMETH) {
|
luaF_unlinkupval(uv); /* remove upvalue from 'openupval' list */
|
||||||
/* 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 */
|
setobj(L, slot, uv->v); /* move value to upvalue slot */
|
||||||
uv->v = slot; /* now current value lives here */
|
uv->v = slot; /* now current value lives here */
|
||||||
if (!iswhite(uv)) { /* neither white nor dead? */
|
if (!iswhite(uv)) { /* neither white nor dead? */
|
||||||
@@ -239,7 +205,35 @@ int luaF_close (lua_State *L, StkId level, int status) {
|
|||||||
luaC_barrier(L, uv, slot);
|
luaC_barrier(L, uv, slot);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
return status;
|
}
|
||||||
|
|
||||||
|
|
||||||
|
/*
|
||||||
|
** Remove firt element from the tbclist plus its dummy nodes.
|
||||||
|
*/
|
||||||
|
static void poptbclist (lua_State *L) {
|
||||||
|
StkId tbc = L->tbclist;
|
||||||
|
lua_assert(tbc->tbclist.delta > 0); /* first element cannot be dummy */
|
||||||
|
tbc -= tbc->tbclist.delta;
|
||||||
|
while (tbc > L->stack && tbc->tbclist.delta == 0)
|
||||||
|
tbc -= MAXDELTA; /* remove dummy nodes */
|
||||||
|
L->tbclist = tbc;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
/*
|
||||||
|
** Close all upvalues and to-be-closed variables up to the given stack
|
||||||
|
** level.
|
||||||
|
*/
|
||||||
|
void 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 >= level) { /* traverse tbc's down to that level */
|
||||||
|
StkId tbc = L->tbclist; /* get variable index */
|
||||||
|
poptbclist(L); /* remove it from list */
|
||||||
|
prepcallclosemth(L, tbc, status, yy); /* close variable */
|
||||||
|
level = restorestack(L, levelrel);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|||||||
13
lfunc.h
13
lfunc.h
@@ -42,15 +42,9 @@
|
|||||||
#define MAXMISS 10
|
#define MAXMISS 10
|
||||||
|
|
||||||
|
|
||||||
/*
|
|
||||||
** Special "status" for 'luaF_close'
|
|
||||||
*/
|
|
||||||
|
|
||||||
/* close upvalues without running their closing methods */
|
/* special status to close upvalues preserving the top of the stack */
|
||||||
#define NOCLOSINGMETH (-1)
|
#define CLOSEKTOP (-1)
|
||||||
|
|
||||||
/* close upvalues running all closing methods in protected mode */
|
|
||||||
#define CLOSEPROTECT (-2)
|
|
||||||
|
|
||||||
|
|
||||||
LUAI_FUNC Proto *luaF_newproto (lua_State *L);
|
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 void luaF_initupvals (lua_State *L, LClosure *cl);
|
||||||
LUAI_FUNC UpVal *luaF_findupval (lua_State *L, StkId level);
|
LUAI_FUNC UpVal *luaF_findupval (lua_State *L, StkId level);
|
||||||
LUAI_FUNC void luaF_newtbcupval (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 void luaF_close (lua_State *L, StkId level, int status, int yy);
|
||||||
LUAI_FUNC void luaF_unlinkupval (UpVal *uv);
|
LUAI_FUNC void luaF_unlinkupval (UpVal *uv);
|
||||||
LUAI_FUNC void luaF_freeproto (lua_State *L, Proto *f);
|
LUAI_FUNC void luaF_freeproto (lua_State *L, Proto *f);
|
||||||
LUAI_FUNC const char *luaF_getlocalname (const Proto *func, int local_number,
|
LUAI_FUNC const char *luaF_getlocalname (const Proto *func, int local_number,
|
||||||
|
|||||||
38
lgc.c
38
lgc.c
@@ -916,7 +916,7 @@ static void GCTM (lua_State *L) {
|
|||||||
L->ci->callstatus &= ~CIST_FIN; /* not running a finalizer anymore */
|
L->ci->callstatus &= ~CIST_FIN; /* not running a finalizer anymore */
|
||||||
L->allowhook = oldah; /* restore hooks */
|
L->allowhook = oldah; /* restore hooks */
|
||||||
g->gcrunning = running; /* restore state */
|
g->gcrunning = running; /* restore state */
|
||||||
if (unlikely(status != LUA_OK)) { /* error while running __gc? */
|
if (l_unlikely(status != LUA_OK)) { /* error while running __gc? */
|
||||||
luaE_warnerror(L, "__gc metamethod");
|
luaE_warnerror(L, "__gc metamethod");
|
||||||
L->top--; /* pops error object */
|
L->top--; /* pops error object */
|
||||||
}
|
}
|
||||||
@@ -1575,52 +1575,64 @@ static int sweepstep (lua_State *L, global_State *g,
|
|||||||
|
|
||||||
static lu_mem singlestep (lua_State *L) {
|
static lu_mem singlestep (lua_State *L) {
|
||||||
global_State *g = G(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) {
|
switch (g->gcstate) {
|
||||||
case GCSpause: {
|
case GCSpause: {
|
||||||
restartcollection(g);
|
restartcollection(g);
|
||||||
g->gcstate = GCSpropagate;
|
g->gcstate = GCSpropagate;
|
||||||
return 1;
|
work = 1;
|
||||||
|
break;
|
||||||
}
|
}
|
||||||
case GCSpropagate: {
|
case GCSpropagate: {
|
||||||
if (g->gray == NULL) { /* no more gray objects? */
|
if (g->gray == NULL) { /* no more gray objects? */
|
||||||
g->gcstate = GCSenteratomic; /* finish propagate phase */
|
g->gcstate = GCSenteratomic; /* finish propagate phase */
|
||||||
return 0;
|
work = 0;
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
return propagatemark(g); /* traverse one gray object */
|
work = propagatemark(g); /* traverse one gray object */
|
||||||
|
break;
|
||||||
}
|
}
|
||||||
case GCSenteratomic: {
|
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);
|
entersweep(L);
|
||||||
g->GCestimate = gettotalbytes(g); /* first estimate */;
|
g->GCestimate = gettotalbytes(g); /* first estimate */;
|
||||||
return work;
|
break;
|
||||||
}
|
}
|
||||||
case GCSswpallgc: { /* sweep "regular" objects */
|
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 */
|
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 */
|
case GCSswptobefnz: { /* sweep objects to be finalized */
|
||||||
return sweepstep(L, g, GCSswpend, NULL);
|
work = sweepstep(L, g, GCSswpend, NULL);
|
||||||
|
break;
|
||||||
}
|
}
|
||||||
case GCSswpend: { /* finish sweeps */
|
case GCSswpend: { /* finish sweeps */
|
||||||
checkSizes(L, g);
|
checkSizes(L, g);
|
||||||
g->gcstate = GCScallfin;
|
g->gcstate = GCScallfin;
|
||||||
return 0;
|
work = 0;
|
||||||
|
break;
|
||||||
}
|
}
|
||||||
case GCScallfin: { /* call remaining finalizers */
|
case GCScallfin: { /* call remaining finalizers */
|
||||||
if (g->tobefnz && !g->gcemergency) {
|
if (g->tobefnz && !g->gcemergency) {
|
||||||
int n = runafewfinalizers(L, GCFINMAX);
|
g->gcstopem = 0; /* ok collections during finalizers */
|
||||||
return n * GCFINALIZECOST;
|
work = runafewfinalizers(L, GCFINMAX) * GCFINALIZECOST;
|
||||||
}
|
}
|
||||||
else { /* emergency mode or no more finalizers */
|
else { /* emergency mode or no more finalizers */
|
||||||
g->gcstate = GCSpause; /* finish collection */
|
g->gcstate = GCSpause; /* finish collection */
|
||||||
return 0;
|
work = 0;
|
||||||
}
|
}
|
||||||
|
break;
|
||||||
}
|
}
|
||||||
default: lua_assert(0); return 0;
|
default: lua_assert(0); return 0;
|
||||||
}
|
}
|
||||||
|
g->gcstopem = 0;
|
||||||
|
return work;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|||||||
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(l_popen) /* { */
|
||||||
|
|
||||||
#if defined(LUA_USE_POSIX) /* { */
|
#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_popen(L,c,m) (_popen(c,m))
|
||||||
#define l_pclose(L,file) (_pclose(file))
|
#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 /* }{ */
|
#else /* }{ */
|
||||||
|
|
||||||
/* ISO C definitions */
|
/* ISO C definitions */
|
||||||
@@ -83,6 +83,12 @@ static int l_checkmode (const char *mode) {
|
|||||||
|
|
||||||
#endif /* } */
|
#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) {
|
static FILE *tofile (lua_State *L) {
|
||||||
LStream *p = tolstream(L);
|
LStream *p = tolstream(L);
|
||||||
if (isclosed(p))
|
if (l_unlikely(isclosed(p)))
|
||||||
luaL_error(L, "attempt to use a closed file");
|
luaL_error(L, "attempt to use a closed file");
|
||||||
lua_assert(p->f);
|
lua_assert(p->f);
|
||||||
return 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) {
|
static void opencheck (lua_State *L, const char *fname, const char *mode) {
|
||||||
LStream *p = newfile(L);
|
LStream *p = newfile(L);
|
||||||
p->f = fopen(fname, mode);
|
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));
|
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;
|
LStream *p;
|
||||||
lua_getfield(L, LUA_REGISTRYINDEX, findex);
|
lua_getfield(L, LUA_REGISTRYINDEX, findex);
|
||||||
p = (LStream *)lua_touserdata(L, -1);
|
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);
|
luaL_error(L, "default %s file is closed", findex + IOPREF_LEN);
|
||||||
return p->f;
|
return p->f;
|
||||||
}
|
}
|
||||||
@@ -430,7 +436,7 @@ typedef struct {
|
|||||||
** Add current char to buffer (if not out of space) and read next one
|
** Add current char to buffer (if not out of space) and read next one
|
||||||
*/
|
*/
|
||||||
static int nextc (RN *rn) {
|
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 */
|
rn->buff[0] = '\0'; /* invalidate result */
|
||||||
return 0; /* fail */
|
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 */
|
ungetc(rn.c, rn.f); /* unread look-ahead char */
|
||||||
l_unlockfile(rn.f);
|
l_unlockfile(rn.f);
|
||||||
rn.buff[rn.n] = '\0'; /* finish string */
|
rn.buff[rn.n] = '\0'; /* finish string */
|
||||||
if (lua_stringtonumber(L, rn.buff)) /* is this a valid number? */
|
if (l_likely(lua_stringtonumber(L, rn.buff)))
|
||||||
return 1; /* ok */
|
return 1; /* ok, it is a valid number */
|
||||||
else { /* invalid format */
|
else { /* invalid format */
|
||||||
lua_pushnil(L); /* "result" to be removed */
|
lua_pushnil(L); /* "result" to be removed */
|
||||||
return 0; /* read fails */
|
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);
|
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);
|
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,
|
luaL_argcheck(L, (lua_Integer)offset == p3, 3,
|
||||||
"not an integer in proper range");
|
"not an integer in proper range");
|
||||||
op = l_fseek(f, offset, mode[op]);
|
op = l_fseek(f, offset, mode[op]);
|
||||||
if (op)
|
if (l_unlikely(op))
|
||||||
return luaL_fileresult(L, 0, NULL); /* error */
|
return luaL_fileresult(L, 0, NULL); /* error */
|
||||||
else {
|
else {
|
||||||
lua_pushinteger(L, (lua_Integer)l_ftell(f));
|
lua_pushinteger(L, (lua_Integer)l_ftell(f));
|
||||||
|
|||||||
31
llex.c
31
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
|
** Creates a new string and anchors it in scanner's table so that it
|
||||||
** it will not be collected until the end of the compilation
|
** will not be collected until the end of the compilation; by that time
|
||||||
** (by that time it should be anchored somewhere)
|
** 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 readly available. Later, the code generation can change
|
||||||
|
** this value.
|
||||||
*/
|
*/
|
||||||
TString *luaX_newstring (LexState *ls, const char *str, size_t l) {
|
TString *luaX_newstring (LexState *ls, const char *str, size_t l) {
|
||||||
lua_State *L = ls->L;
|
lua_State *L = ls->L;
|
||||||
TValue *o; /* entry for 'str' */
|
|
||||||
TString *ts = luaS_newlstr(L, str, l); /* create new string */
|
TString *ts = luaS_newlstr(L, str, l); /* create new string */
|
||||||
setsvalue2s(L, L->top++, ts); /* temporarily anchor it in stack */
|
const TValue *o = luaH_getstr(ls->h, ts);
|
||||||
o = luaH_set(L, ls->h, s2v(L->top - 1));
|
if (!ttisnil(o)) /* string already present? */
|
||||||
if (isempty(o)) { /* not in use yet? */
|
ts = keystrval(nodefromval(o)); /* get saved copy */
|
||||||
/* boolean value does not need GC barrier;
|
else { /* not in use yet */
|
||||||
table is not a metatable, so it does not need to invalidate cache */
|
TValue *stv = s2v(L->top++); /* reserve stack space for string */
|
||||||
setbtvalue(o); /* t[string] = true */
|
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);
|
luaC_checkGC(L);
|
||||||
|
L->top--; /* 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;
|
return ts;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
16
llimits.h
16
llimits.h
@@ -149,22 +149,6 @@ typedef LUAI_UACINT l_uacInt;
|
|||||||
#endif
|
#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
|
** non-return type
|
||||||
*/
|
*/
|
||||||
|
|||||||
@@ -73,7 +73,7 @@ static int math_atan (lua_State *L) {
|
|||||||
static int math_toint (lua_State *L) {
|
static int math_toint (lua_State *L) {
|
||||||
int valid;
|
int valid;
|
||||||
lua_Integer n = lua_tointegerx(L, 1, &valid);
|
lua_Integer n = lua_tointegerx(L, 1, &valid);
|
||||||
if (valid)
|
if (l_likely(valid))
|
||||||
lua_pushinteger(L, n);
|
lua_pushinteger(L, n);
|
||||||
else {
|
else {
|
||||||
luaL_checkany(L, 1);
|
luaL_checkany(L, 1);
|
||||||
@@ -175,7 +175,8 @@ static int math_log (lua_State *L) {
|
|||||||
lua_Number base = luaL_checknumber(L, 2);
|
lua_Number base = luaL_checknumber(L, 2);
|
||||||
#if !defined(LUA_USE_C89)
|
#if !defined(LUA_USE_C89)
|
||||||
if (base == l_mathop(2.0))
|
if (base == l_mathop(2.0))
|
||||||
res = l_mathop(log2)(x); else
|
res = l_mathop(log2)(x);
|
||||||
|
else
|
||||||
#endif
|
#endif
|
||||||
if (base == l_mathop(10.0))
|
if (base == l_mathop(10.0))
|
||||||
res = l_mathop(log10)(x);
|
res = l_mathop(log10)(x);
|
||||||
|
|||||||
33
lmem.c
33
lmem.c
@@ -24,12 +24,12 @@
|
|||||||
|
|
||||||
#if defined(EMERGENCYGCTESTS)
|
#if defined(EMERGENCYGCTESTS)
|
||||||
/*
|
/*
|
||||||
** First allocation will fail whenever not building initial state
|
** First allocation will fail whenever not building initial state.
|
||||||
** and not shrinking a block. (This fail will trigger 'tryagain' and
|
** (This fail will trigger 'tryagain' and a full GC cycle at every
|
||||||
** a full GC cycle at every allocation.)
|
** allocation.)
|
||||||
*/
|
*/
|
||||||
static void *firsttry (global_State *g, void *block, size_t os, size_t ns) {
|
static void *firsttry (global_State *g, void *block, size_t os, size_t ns) {
|
||||||
if (ttisnil(&g->nilvalue) && ns > os)
|
if (completestate(g) && ns > 0) /* frees never fail */
|
||||||
return NULL; /* fail */
|
return NULL; /* fail */
|
||||||
else /* normal allocation */
|
else /* normal allocation */
|
||||||
return (*g->frealloc)(g->ud, block, os, ns);
|
return (*g->frealloc)(g->ud, block, os, ns);
|
||||||
@@ -83,7 +83,7 @@ void *luaM_growaux_ (lua_State *L, void *block, int nelems, int *psize,
|
|||||||
if (nelems + 1 <= size) /* does one extra element still fit? */
|
if (nelems + 1 <= size) /* does one extra element still fit? */
|
||||||
return block; /* nothing to be done */
|
return block; /* nothing to be done */
|
||||||
if (size >= limit / 2) { /* cannot double it? */
|
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);
|
luaG_runerror(L, "too many %s (limit is %d)", what, limit);
|
||||||
size = limit; /* still have at least one free place */
|
size = limit; /* still have at least one free place */
|
||||||
}
|
}
|
||||||
@@ -138,15 +138,17 @@ void luaM_free_ (lua_State *L, void *block, size_t osize) {
|
|||||||
|
|
||||||
|
|
||||||
/*
|
/*
|
||||||
** In case of allocation fail, this function will call the GC to try
|
** In case of allocation fail, this function will do an emergency
|
||||||
** to free some memory and then try the allocation again.
|
** collection to free some memory and then try the allocation again.
|
||||||
** (It should not be called when shrinking a block, because then the
|
** The GC should not be called while state is not fully built, as the
|
||||||
** interpreter may be in the middle of a collection step.)
|
** 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.
|
||||||
*/
|
*/
|
||||||
static void *tryagain (lua_State *L, void *block,
|
static void *tryagain (lua_State *L, void *block,
|
||||||
size_t osize, size_t nsize) {
|
size_t osize, size_t nsize) {
|
||||||
global_State *g = G(L);
|
global_State *g = G(L);
|
||||||
if (ttisnil(&g->nilvalue)) { /* is state fully build? */
|
if (completestate(g) && !g->gcstopem) {
|
||||||
luaC_fullgc(L, 1); /* try to free some memory... */
|
luaC_fullgc(L, 1); /* try to free some memory... */
|
||||||
return (*g->frealloc)(g->ud, block, osize, nsize); /* try again */
|
return (*g->frealloc)(g->ud, block, osize, nsize); /* try again */
|
||||||
}
|
}
|
||||||
@@ -156,17 +158,14 @@ static void *tryagain (lua_State *L, void *block,
|
|||||||
|
|
||||||
/*
|
/*
|
||||||
** Generic allocation routine.
|
** Generic allocation routine.
|
||||||
** If allocation fails while shrinking a block, do not try again; the
|
|
||||||
** GC shrinks some blocks and it is not reentrant.
|
|
||||||
*/
|
*/
|
||||||
void *luaM_realloc_ (lua_State *L, void *block, size_t osize, size_t nsize) {
|
void *luaM_realloc_ (lua_State *L, void *block, size_t osize, size_t nsize) {
|
||||||
void *newblock;
|
void *newblock;
|
||||||
global_State *g = G(L);
|
global_State *g = G(L);
|
||||||
lua_assert((osize == 0) == (block == NULL));
|
lua_assert((osize == 0) == (block == NULL));
|
||||||
newblock = firsttry(g, block, osize, nsize);
|
newblock = firsttry(g, block, osize, nsize);
|
||||||
if (unlikely(newblock == NULL && nsize > 0)) {
|
if (l_unlikely(newblock == NULL && nsize > 0)) {
|
||||||
if (nsize > osize) /* not shrinking a block? */
|
newblock = tryagain(L, block, osize, nsize);
|
||||||
newblock = tryagain(L, block, osize, nsize);
|
|
||||||
if (newblock == NULL) /* still no memory? */
|
if (newblock == NULL) /* still no memory? */
|
||||||
return NULL; /* do not update 'GCdebt' */
|
return NULL; /* do not update 'GCdebt' */
|
||||||
}
|
}
|
||||||
@@ -179,7 +178,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,
|
void *luaM_saferealloc_ (lua_State *L, void *block, size_t osize,
|
||||||
size_t nsize) {
|
size_t nsize) {
|
||||||
void *newblock = luaM_realloc_(L, block, osize, 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);
|
luaM_error(L);
|
||||||
return newblock;
|
return newblock;
|
||||||
}
|
}
|
||||||
@@ -191,7 +190,7 @@ void *luaM_malloc_ (lua_State *L, size_t size, int tag) {
|
|||||||
else {
|
else {
|
||||||
global_State *g = G(L);
|
global_State *g = G(L);
|
||||||
void *newblock = firsttry(g, NULL, tag, size);
|
void *newblock = firsttry(g, NULL, tag, size);
|
||||||
if (unlikely(newblock == NULL)) {
|
if (l_unlikely(newblock == NULL)) {
|
||||||
newblock = tryagain(L, NULL, tag, size);
|
newblock = tryagain(L, NULL, tag, size);
|
||||||
if (newblock == NULL)
|
if (newblock == NULL)
|
||||||
luaM_error(L);
|
luaM_error(L);
|
||||||
|
|||||||
17
loadlib.c
17
loadlib.c
@@ -132,14 +132,16 @@ static void lsys_unloadlib (void *lib) {
|
|||||||
|
|
||||||
static void *lsys_load (lua_State *L, const char *path, int seeglb) {
|
static void *lsys_load (lua_State *L, const char *path, int seeglb) {
|
||||||
void *lib = dlopen(path, RTLD_NOW | (seeglb ? RTLD_GLOBAL : RTLD_LOCAL));
|
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;
|
return lib;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
static lua_CFunction lsys_sym (lua_State *L, void *lib, const char *sym) {
|
static lua_CFunction lsys_sym (lua_State *L, void *lib, const char *sym) {
|
||||||
lua_CFunction f = cast_func(dlsym(lib, 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;
|
return f;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -410,7 +412,7 @@ static int ll_loadlib (lua_State *L) {
|
|||||||
const char *path = luaL_checkstring(L, 1);
|
const char *path = luaL_checkstring(L, 1);
|
||||||
const char *init = luaL_checkstring(L, 2);
|
const char *init = luaL_checkstring(L, 2);
|
||||||
int stat = lookforfunc(L, path, init);
|
int stat = lookforfunc(L, path, init);
|
||||||
if (stat == 0) /* no errors? */
|
if (l_likely(stat == 0)) /* no errors? */
|
||||||
return 1; /* return the loaded function */
|
return 1; /* return the loaded function */
|
||||||
else { /* error; error message is on stack top */
|
else { /* error; error message is on stack top */
|
||||||
luaL_pushfail(L);
|
luaL_pushfail(L);
|
||||||
@@ -523,14 +525,14 @@ static const char *findfile (lua_State *L, const char *name,
|
|||||||
const char *path;
|
const char *path;
|
||||||
lua_getfield(L, lua_upvalueindex(1), pname);
|
lua_getfield(L, lua_upvalueindex(1), pname);
|
||||||
path = lua_tostring(L, -1);
|
path = lua_tostring(L, -1);
|
||||||
if (path == NULL)
|
if (l_unlikely(path == NULL))
|
||||||
luaL_error(L, "'package.%s' must be a string", pname);
|
luaL_error(L, "'package.%s' must be a string", pname);
|
||||||
return searchpath(L, name, path, ".", dirsep);
|
return searchpath(L, name, path, ".", dirsep);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
static int checkload (lua_State *L, int stat, const char *filename) {
|
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 */
|
lua_pushstring(L, filename); /* will be 2nd argument to module */
|
||||||
return 2; /* return open function and file name */
|
return 2; /* return open function and file name */
|
||||||
}
|
}
|
||||||
@@ -623,13 +625,14 @@ static void findloader (lua_State *L, const char *name) {
|
|||||||
int i;
|
int i;
|
||||||
luaL_Buffer msg; /* to build error message */
|
luaL_Buffer msg; /* to build error message */
|
||||||
/* push 'package.searchers' to index 3 in the stack */
|
/* 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_error(L, "'package.searchers' must be a table");
|
||||||
luaL_buffinit(L, &msg);
|
luaL_buffinit(L, &msg);
|
||||||
/* iterate over available searchers to find a loader */
|
/* iterate over available searchers to find a loader */
|
||||||
for (i = 1; ; i++) {
|
for (i = 1; ; i++) {
|
||||||
luaL_addstring(&msg, "\n\t"); /* error-message prefix */
|
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 */
|
lua_pop(L, 1); /* remove nil */
|
||||||
luaL_buffsub(&msg, 2); /* remove prefix */
|
luaL_buffsub(&msg, 2); /* remove prefix */
|
||||||
luaL_pushresult(&msg); /* create error message */
|
luaL_pushresult(&msg); /* create error message */
|
||||||
|
|||||||
14
lobject.h
14
lobject.h
@@ -136,10 +136,19 @@ 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 {
|
typedef union StackValue {
|
||||||
TValue val;
|
TValue val;
|
||||||
|
struct {
|
||||||
|
TValuefields;
|
||||||
|
unsigned short delta;
|
||||||
|
} tbclist;
|
||||||
} StackValue;
|
} StackValue;
|
||||||
|
|
||||||
|
|
||||||
@@ -570,10 +579,11 @@ typedef struct Proto {
|
|||||||
#define LUA_VCCL makevariant(LUA_TFUNCTION, 2) /* C closure */
|
#define LUA_VCCL makevariant(LUA_TFUNCTION, 2) /* C closure */
|
||||||
|
|
||||||
#define ttisfunction(o) checktype(o, LUA_TFUNCTION)
|
#define ttisfunction(o) checktype(o, LUA_TFUNCTION)
|
||||||
#define ttisclosure(o) ((rawtt(o) & 0x1F) == LUA_VLCL)
|
|
||||||
#define ttisLclosure(o) checktag((o), ctb(LUA_VLCL))
|
#define ttisLclosure(o) checktag((o), ctb(LUA_VLCL))
|
||||||
#define ttislcf(o) checktag((o), LUA_VLCF)
|
#define ttislcf(o) checktag((o), LUA_VLCF)
|
||||||
#define ttisCclosure(o) checktag((o), ctb(LUA_VCCL))
|
#define ttisCclosure(o) checktag((o), ctb(LUA_VCCL))
|
||||||
|
#define ttisclosure(o) (ttisLclosure(o) || ttisCclosure(o))
|
||||||
|
|
||||||
|
|
||||||
#define isLfunction(o) ttisLclosure(o)
|
#define isLfunction(o) ttisLclosure(o)
|
||||||
|
|
||||||
|
|||||||
14
lopcodes.h
14
lopcodes.h
@@ -225,13 +225,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_ADDI,/* A B sC R[A] := R[B] + sC */
|
||||||
|
|
||||||
OP_ADDK,/* 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] */
|
OP_SUBK,/* A B C R[A] := R[B] - K[C]:number */
|
||||||
OP_MULK,/* A B C R[A] := R[B] * K[C] */
|
OP_MULK,/* A B C R[A] := R[B] * K[C]:number */
|
||||||
OP_MODK,/* A B C R[A] := R[B] % K[C] */
|
OP_MODK,/* A B C R[A] := R[B] % K[C]:number */
|
||||||
OP_POWK,/* A B C R[A] := R[B] ^ K[C] */
|
OP_POWK,/* A B C R[A] := R[B] ^ K[C]:number */
|
||||||
OP_DIVK,/* A B C R[A] := R[B] / K[C] */
|
OP_DIVK,/* A B C R[A] := R[B] / K[C]:number */
|
||||||
OP_IDIVK,/* A B C R[A] := R[B] // K[C] */
|
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_BANDK,/* A B C R[A] := R[B] & K[C]:integer */
|
||||||
OP_BORK,/* A B C R[A] := R[B] | K[C]:integer */
|
OP_BORK,/* A B C R[A] := R[B] | K[C]:integer */
|
||||||
|
|||||||
8
loslib.c
8
loslib.c
@@ -170,7 +170,7 @@ static int os_tmpname (lua_State *L) {
|
|||||||
char buff[LUA_TMPNAMBUFSIZE];
|
char buff[LUA_TMPNAMBUFSIZE];
|
||||||
int err;
|
int err;
|
||||||
lua_tmpnam(buff, err);
|
lua_tmpnam(buff, err);
|
||||||
if (err)
|
if (l_unlikely(err))
|
||||||
return luaL_error(L, "unable to generate a unique filename");
|
return luaL_error(L, "unable to generate a unique filename");
|
||||||
lua_pushstring(L, buff);
|
lua_pushstring(L, buff);
|
||||||
return 1;
|
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) {
|
static void setfield (lua_State *L, const char *key, int value, int delta) {
|
||||||
#if (defined(LUA_NUMTIME) && LUA_MAXINTEGER <= INT_MAX)
|
#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);
|
luaL_error(L, "field '%s' is out-of-bound", key);
|
||||||
#endif
|
#endif
|
||||||
lua_pushinteger(L, (lua_Integer)value + delta);
|
lua_pushinteger(L, (lua_Integer)value + delta);
|
||||||
@@ -253,9 +253,9 @@ 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 */
|
int t = lua_getfield(L, -1, key); /* get field and its type */
|
||||||
lua_Integer res = lua_tointegerx(L, -1, &isnum);
|
lua_Integer res = lua_tointegerx(L, -1, &isnum);
|
||||||
if (!isnum) { /* field is not an integer? */
|
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);
|
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);
|
return luaL_error(L, "field '%s' missing in date table", key);
|
||||||
res = d;
|
res = d;
|
||||||
}
|
}
|
||||||
|
|||||||
54
lparser.c
54
lparser.c
@@ -128,7 +128,7 @@ static void checknext (LexState *ls, int c) {
|
|||||||
** in line 'where' (if that is not the current line).
|
** in line 'where' (if that is not the current line).
|
||||||
*/
|
*/
|
||||||
static void check_match (LexState *ls, int what, int who, int where) {
|
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? */
|
if (where == ls->linenumber) /* all in the same line? */
|
||||||
error_expected(ls, what); /* do not need a complex message */
|
error_expected(ls, what); /* do not need a complex message */
|
||||||
else {
|
else {
|
||||||
@@ -222,26 +222,26 @@ static Vardesc *getlocalvardesc (FuncState *fs, int vidx) {
|
|||||||
|
|
||||||
|
|
||||||
/*
|
/*
|
||||||
** Convert 'nvar', a compiler index level, to it corresponding
|
** Convert 'nvar', a compiler index level, to its corresponding
|
||||||
** stack index level. For that, search for the highest variable
|
** register. For that, search for the highest variable below that level
|
||||||
** below that level that is in the stack and uses its stack
|
** that is in a register and uses its register index ('ridx') plus one.
|
||||||
** index ('sidx').
|
|
||||||
*/
|
*/
|
||||||
static int stacklevel (FuncState *fs, int nvar) {
|
static int reglevel (FuncState *fs, int nvar) {
|
||||||
while (nvar-- > 0) {
|
while (nvar-- > 0) {
|
||||||
Vardesc *vd = getlocalvardesc(fs, nvar); /* get variable */
|
Vardesc *vd = getlocalvardesc(fs, nvar); /* get previous variable */
|
||||||
if (vd->vd.kind != RDKCTC) /* is in the stack? */
|
if (vd->vd.kind != RDKCTC) /* is in a register? */
|
||||||
return vd->vd.sidx + 1;
|
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) {
|
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->f = e->t = NO_JUMP;
|
||||||
e->k = VLOCAL;
|
e->k = VLOCAL;
|
||||||
e->u.var.vidx = vidx;
|
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) {
|
static void adjustlocalvars (LexState *ls, int nvars) {
|
||||||
FuncState *fs = ls->fs;
|
FuncState *fs = ls->fs;
|
||||||
int stklevel = luaY_nvarstack(fs);
|
int reglevel = luaY_nvarstack(fs);
|
||||||
int i;
|
int i;
|
||||||
for (i = 0; i < nvars; i++) {
|
for (i = 0; i < nvars; i++) {
|
||||||
int vidx = fs->nactvar++;
|
int vidx = fs->nactvar++;
|
||||||
Vardesc *var = getlocalvardesc(fs, vidx);
|
Vardesc *var = getlocalvardesc(fs, vidx);
|
||||||
var->vd.sidx = stklevel++;
|
var->vd.ridx = reglevel++;
|
||||||
var->vd.pidx = registerlocalvar(ls, fs, var->vd.name);
|
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;
|
FuncState *prev = fs->prev;
|
||||||
if (v->k == VLOCAL) {
|
if (v->k == VLOCAL) {
|
||||||
up->instack = 1;
|
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;
|
up->kind = getlocalvardesc(prev, v->u.var.vidx)->vd.kind;
|
||||||
lua_assert(eqstr(name, getlocalvardesc(prev, v->u.var.vidx)->vd.name));
|
lua_assert(eqstr(name, getlocalvardesc(prev, v->u.var.vidx)->vd.name));
|
||||||
}
|
}
|
||||||
@@ -517,7 +517,7 @@ static void solvegoto (LexState *ls, int g, Labeldesc *label) {
|
|||||||
Labellist *gl = &ls->dyd->gt; /* list of goto's */
|
Labellist *gl = &ls->dyd->gt; /* list of goto's */
|
||||||
Labeldesc *gt = &gl->arr[g]; /* goto to be resolved */
|
Labeldesc *gt = &gl->arr[g]; /* goto to be resolved */
|
||||||
lua_assert(eqstr(gt->name, label->name));
|
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);
|
jumpscopeerror(ls, gt);
|
||||||
luaK_patchlist(ls->fs, gt->pc, label->pc);
|
luaK_patchlist(ls->fs, gt->pc, label->pc);
|
||||||
for (i = g; i < gl->n - 1; i++) /* remove goto from pending list */
|
for (i = g; i < gl->n - 1; i++) /* remove goto from pending list */
|
||||||
@@ -620,7 +620,7 @@ static void movegotosout (FuncState *fs, BlockCnt *bl) {
|
|||||||
for (i = bl->firstgoto; i < gl->n; i++) { /* for each pending goto */
|
for (i = bl->firstgoto; i < gl->n; i++) { /* for each pending goto */
|
||||||
Labeldesc *gt = &gl->arr[i];
|
Labeldesc *gt = &gl->arr[i];
|
||||||
/* leaving a variable scope? */
|
/* 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->close |= bl->upval; /* jump may need a close */
|
||||||
gt->nactvar = bl->nactvar; /* update goto level */
|
gt->nactvar = bl->nactvar; /* update goto level */
|
||||||
}
|
}
|
||||||
@@ -661,7 +661,7 @@ static void leaveblock (FuncState *fs) {
|
|||||||
BlockCnt *bl = fs->bl;
|
BlockCnt *bl = fs->bl;
|
||||||
LexState *ls = fs->ls;
|
LexState *ls = fs->ls;
|
||||||
int hasclose = 0;
|
int hasclose = 0;
|
||||||
int stklevel = stacklevel(fs, bl->nactvar); /* level outside the block */
|
int stklevel = reglevel(fs, bl->nactvar); /* level outside the block */
|
||||||
if (bl->isloop) /* fix pending breaks? */
|
if (bl->isloop) /* fix pending breaks? */
|
||||||
hasclose = createlabel(ls, luaS_newliteral(ls->L, "break"), 0, 0);
|
hasclose = createlabel(ls, luaS_newliteral(ls->L, "break"), 0, 0);
|
||||||
if (!hasclose && bl->previous && bl->upval)
|
if (!hasclose && bl->previous && bl->upval)
|
||||||
@@ -1330,13 +1330,13 @@ static void check_conflict (LexState *ls, struct LHS_assign *lh, expdesc *v) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
else { /* table is a register */
|
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 */
|
conflict = 1; /* table is the local being assigned now */
|
||||||
lh->v.u.ind.t = extra; /* assignment will use safe copy */
|
lh->v.u.ind.t = extra; /* assignment will use safe copy */
|
||||||
}
|
}
|
||||||
/* is index the local being assigned? */
|
/* is index the local being assigned? */
|
||||||
if (lh->v.k == VINDEXED && v->k == VLOCAL &&
|
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;
|
conflict = 1;
|
||||||
lh->v.u.ind.idx = extra; /* previous assignment will use safe copy */
|
lh->v.u.ind.idx = extra; /* previous assignment will use safe copy */
|
||||||
}
|
}
|
||||||
@@ -1346,7 +1346,7 @@ static void check_conflict (LexState *ls, struct LHS_assign *lh, expdesc *v) {
|
|||||||
if (conflict) {
|
if (conflict) {
|
||||||
/* copy upvalue/local value to a temporary (in position 'extra') */
|
/* copy upvalue/local value to a temporary (in position 'extra') */
|
||||||
if (v->k == VLOCAL)
|
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
|
else
|
||||||
luaK_codeABC(fs, OP_GETUPVAL, extra, v->u.info, 0);
|
luaK_codeABC(fs, OP_GETUPVAL, extra, v->u.info, 0);
|
||||||
luaK_reserveregs(fs, 1);
|
luaK_reserveregs(fs, 1);
|
||||||
@@ -1411,7 +1411,7 @@ static void gotostat (LexState *ls) {
|
|||||||
newgotoentry(ls, name, line, luaK_jump(fs));
|
newgotoentry(ls, name, line, luaK_jump(fs));
|
||||||
else { /* found a label */
|
else { /* found a label */
|
||||||
/* backward jump; will be resolved here */
|
/* 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? */
|
if (luaY_nvarstack(fs) > lblevel) /* leaving the scope of a variable? */
|
||||||
luaK_codeABC(fs, OP_CLOSE, lblevel, 0, 0);
|
luaK_codeABC(fs, OP_CLOSE, lblevel, 0, 0);
|
||||||
/* create jump and link it to the label */
|
/* create jump and link it to the label */
|
||||||
@@ -1435,7 +1435,7 @@ static void breakstat (LexState *ls) {
|
|||||||
*/
|
*/
|
||||||
static void checkrepeated (LexState *ls, TString *name) {
|
static void checkrepeated (LexState *ls, TString *name) {
|
||||||
Labeldesc *lb = findlabel(ls, 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";
|
const char *msg = "label '%s' already defined on line %d";
|
||||||
msg = luaO_pushfstring(ls->L, msg, getstr(name), lb->line);
|
msg = luaO_pushfstring(ls->L, msg, getstr(name), lb->line);
|
||||||
luaK_semerror(ls, msg); /* error */
|
luaK_semerror(ls, msg); /* error */
|
||||||
@@ -1488,7 +1488,7 @@ static void repeatstat (LexState *ls, int line) {
|
|||||||
if (bl2.upval) { /* upvalues? */
|
if (bl2.upval) { /* upvalues? */
|
||||||
int exit = luaK_jump(fs); /* normal exit must jump over fix */
|
int exit = luaK_jump(fs); /* normal exit must jump over fix */
|
||||||
luaK_patchtohere(fs, condexit); /* repetition must close upvalues */
|
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 */
|
condexit = luaK_jump(fs); /* repeat after closing upvalues */
|
||||||
luaK_patchtohere(fs, exit); /* normal exit comes to here */
|
luaK_patchtohere(fs, exit); /* normal exit comes to here */
|
||||||
}
|
}
|
||||||
@@ -1520,7 +1520,7 @@ static void fixforjump (FuncState *fs, int pc, int dest, int back) {
|
|||||||
int offset = dest - (pc + 1);
|
int offset = dest - (pc + 1);
|
||||||
if (back)
|
if (back)
|
||||||
offset = -offset;
|
offset = -offset;
|
||||||
if (unlikely(offset > MAXARG_Bx))
|
if (l_unlikely(offset > MAXARG_Bx))
|
||||||
luaX_syntaxerror(fs->ls, "control structure too long");
|
luaX_syntaxerror(fs->ls, "control structure too long");
|
||||||
SETARG_Bx(*jmp, offset);
|
SETARG_Bx(*jmp, offset);
|
||||||
}
|
}
|
||||||
@@ -1708,7 +1708,7 @@ static void checktoclose (LexState *ls, int level) {
|
|||||||
FuncState *fs = ls->fs;
|
FuncState *fs = ls->fs;
|
||||||
markupval(fs, level + 1);
|
markupval(fs, level + 1);
|
||||||
fs->bl->insidetbc = 1; /* in the scope of a to-be-closed variable */
|
fs->bl->insidetbc = 1; /* in the scope of a to-be-closed variable */
|
||||||
luaK_codeABC(fs, OP_TBC, stacklevel(fs, level), 0, 0);
|
luaK_codeABC(fs, OP_TBC, reglevel(fs, level), 0, 0);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -35,7 +35,7 @@ typedef enum {
|
|||||||
(string is fixed by the lexer) */
|
(string is fixed by the lexer) */
|
||||||
VNONRELOC, /* expression has its value in a fixed register;
|
VNONRELOC, /* expression has its value in a fixed register;
|
||||||
info = result 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' */
|
var.vidx = relative index in 'actvar.arr' */
|
||||||
VUPVAL, /* upvalue variable; info = index of upvalue in 'upvalues' */
|
VUPVAL, /* upvalue variable; info = index of upvalue in 'upvalues' */
|
||||||
VCONST, /* compile-time <const> variable;
|
VCONST, /* compile-time <const> variable;
|
||||||
@@ -77,7 +77,7 @@ typedef struct expdesc {
|
|||||||
lu_byte t; /* table (register or upvalue) */
|
lu_byte t; /* table (register or upvalue) */
|
||||||
} ind;
|
} ind;
|
||||||
struct { /* for local variables */
|
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') */
|
unsigned short vidx; /* compiler index (in 'actvar.arr') */
|
||||||
} var;
|
} var;
|
||||||
} u;
|
} u;
|
||||||
@@ -97,7 +97,7 @@ typedef union Vardesc {
|
|||||||
struct {
|
struct {
|
||||||
TValuefields; /* constant value (if it is a compile-time constant) */
|
TValuefields; /* constant value (if it is a compile-time constant) */
|
||||||
lu_byte kind;
|
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 */
|
short pidx; /* index of the variable in the Proto's 'locvars' array */
|
||||||
TString *name; /* variable name */
|
TString *name; /* variable name */
|
||||||
} vd;
|
} vd;
|
||||||
|
|||||||
59
lstate.c
59
lstate.c
@@ -172,7 +172,7 @@ void luaE_checkcstack (lua_State *L) {
|
|||||||
|
|
||||||
LUAI_FUNC void luaE_incCstack (lua_State *L) {
|
LUAI_FUNC void luaE_incCstack (lua_State *L) {
|
||||||
L->nCcalls++;
|
L->nCcalls++;
|
||||||
if (unlikely(getCcalls(L) >= LUAI_MAXCCALLS))
|
if (l_unlikely(getCcalls(L) >= LUAI_MAXCCALLS))
|
||||||
luaE_checkcstack(L);
|
luaE_checkcstack(L);
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -181,6 +181,7 @@ static void stack_init (lua_State *L1, lua_State *L) {
|
|||||||
int i; CallInfo *ci;
|
int i; CallInfo *ci;
|
||||||
/* initialize stack array */
|
/* initialize stack array */
|
||||||
L1->stack = luaM_newvector(L, BASIC_STACK_SIZE + EXTRA_STACK, StackValue);
|
L1->stack = luaM_newvector(L, BASIC_STACK_SIZE + EXTRA_STACK, StackValue);
|
||||||
|
L1->tbclist = L1->stack;
|
||||||
for (i = 0; i < BASIC_STACK_SIZE + EXTRA_STACK; i++)
|
for (i = 0; i < BASIC_STACK_SIZE + EXTRA_STACK; i++)
|
||||||
setnilvalue(s2v(L1->stack + i)); /* erase new stack */
|
setnilvalue(s2v(L1->stack + i)); /* erase new stack */
|
||||||
L1->top = L1->stack;
|
L1->top = L1->stack;
|
||||||
@@ -213,24 +214,19 @@ static void freestack (lua_State *L) {
|
|||||||
** Create registry table and its predefined values
|
** Create registry table and its predefined values
|
||||||
*/
|
*/
|
||||||
static void init_registry (lua_State *L, global_State *g) {
|
static void init_registry (lua_State *L, global_State *g) {
|
||||||
TValue temp;
|
|
||||||
/* create registry */
|
/* create registry */
|
||||||
Table *registry = luaH_new(L);
|
Table *registry = luaH_new(L);
|
||||||
sethvalue(L, &g->l_registry, registry);
|
sethvalue(L, &g->l_registry, registry);
|
||||||
luaH_resize(L, registry, LUA_RIDX_LAST, 0);
|
luaH_resize(L, registry, LUA_RIDX_LAST, 0);
|
||||||
/* registry[LUA_RIDX_MAINTHREAD] = L */
|
/* registry[LUA_RIDX_MAINTHREAD] = L */
|
||||||
setthvalue(L, &temp, L); /* temp = L */
|
setthvalue(L, ®istry->array[LUA_RIDX_MAINTHREAD - 1], L);
|
||||||
luaH_setint(L, registry, LUA_RIDX_MAINTHREAD, &temp);
|
/* registry[LUA_RIDX_GLOBALS] = new table (table of globals) */
|
||||||
/* registry[LUA_RIDX_GLOBALS] = table of globals */
|
sethvalue(L, ®istry->array[LUA_RIDX_GLOBALS - 1], luaH_new(L));
|
||||||
sethvalue(L, &temp, luaH_new(L)); /* temp = new table (global table) */
|
|
||||||
luaH_setint(L, registry, LUA_RIDX_GLOBALS, &temp);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
/*
|
/*
|
||||||
** open parts of the state that may cause memory-allocation errors.
|
** 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) {
|
static void f_luaopen (lua_State *L, void *ud) {
|
||||||
global_State *g = G(L);
|
global_State *g = G(L);
|
||||||
@@ -241,7 +237,7 @@ static void f_luaopen (lua_State *L, void *ud) {
|
|||||||
luaT_init(L);
|
luaT_init(L);
|
||||||
luaX_init(L);
|
luaX_init(L);
|
||||||
g->gcrunning = 1; /* allow gc */
|
g->gcrunning = 1; /* allow gc */
|
||||||
setnilvalue(&g->nilvalue);
|
setnilvalue(&g->nilvalue); /* now state is complete */
|
||||||
luai_userstateopen(L);
|
luai_userstateopen(L);
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -256,6 +252,7 @@ static void preinit_thread (lua_State *L, global_State *g) {
|
|||||||
L->ci = NULL;
|
L->ci = NULL;
|
||||||
L->nci = 0;
|
L->nci = 0;
|
||||||
L->twups = L; /* thread has no upvalues */
|
L->twups = L; /* thread has no upvalues */
|
||||||
|
L->nCcalls = 0;
|
||||||
L->errorJmp = NULL;
|
L->errorJmp = NULL;
|
||||||
L->hook = NULL;
|
L->hook = NULL;
|
||||||
L->hookmask = 0;
|
L->hookmask = 0;
|
||||||
@@ -271,10 +268,13 @@ static void preinit_thread (lua_State *L, global_State *g) {
|
|||||||
|
|
||||||
static void close_state (lua_State *L) {
|
static void close_state (lua_State *L) {
|
||||||
global_State *g = G(L);
|
global_State *g = G(L);
|
||||||
luaF_close(L, L->stack, CLOSEPROTECT); /* close all upvalues */
|
if (!completestate(g)) /* closing a partially built state? */
|
||||||
luaC_freeallobjects(L); /* collect all objects */
|
luaC_freeallobjects(L); /* jucst collect its objects */
|
||||||
if (ttisnil(&g->nilvalue)) /* closing a fully built state? */
|
else { /* closing a fully built state */
|
||||||
|
luaD_closeprotected(L, 1, LUA_OK); /* close all upvalues */
|
||||||
|
luaC_freeallobjects(L); /* collect all objects */
|
||||||
luai_userstateclose(L);
|
luai_userstateclose(L);
|
||||||
|
}
|
||||||
luaM_freearray(L, G(L)->strt.hash, G(L)->strt.size);
|
luaM_freearray(L, G(L)->strt.hash, G(L)->strt.size);
|
||||||
freestack(L);
|
freestack(L);
|
||||||
lua_assert(gettotalbytes(g) == sizeof(LG));
|
lua_assert(gettotalbytes(g) == sizeof(LG));
|
||||||
@@ -299,7 +299,6 @@ LUA_API lua_State *lua_newthread (lua_State *L) {
|
|||||||
setthvalue2s(L, L->top, L1);
|
setthvalue2s(L, L->top, L1);
|
||||||
api_incr_top(L);
|
api_incr_top(L);
|
||||||
preinit_thread(L1, g);
|
preinit_thread(L1, g);
|
||||||
L1->nCcalls = 0;
|
|
||||||
L1->hookmask = L->hookmask;
|
L1->hookmask = L->hookmask;
|
||||||
L1->basehookcount = L->basehookcount;
|
L1->basehookcount = L->basehookcount;
|
||||||
L1->hook = L->hook;
|
L1->hook = L->hook;
|
||||||
@@ -316,7 +315,7 @@ LUA_API lua_State *lua_newthread (lua_State *L) {
|
|||||||
|
|
||||||
void luaE_freethread (lua_State *L, lua_State *L1) {
|
void luaE_freethread (lua_State *L, lua_State *L1) {
|
||||||
LX *l = fromstate(L1);
|
LX *l = fromstate(L1);
|
||||||
luaF_close(L1, L1->stack, NOCLOSINGMETH); /* close all upvalues */
|
luaF_closeupval(L1, L1->stack); /* close all upvalues */
|
||||||
lua_assert(L1->openupval == NULL);
|
lua_assert(L1->openupval == NULL);
|
||||||
luai_userstatefree(L, L1);
|
luai_userstatefree(L, L1);
|
||||||
freestack(L1);
|
freestack(L1);
|
||||||
@@ -324,23 +323,29 @@ void luaE_freethread (lua_State *L, lua_State *L1) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
int lua_resetthread (lua_State *L) {
|
int luaE_resetthread (lua_State *L, int status) {
|
||||||
CallInfo *ci;
|
CallInfo *ci = L->ci = &L->base_ci; /* unwind CallInfo list */
|
||||||
int status;
|
|
||||||
lua_lock(L);
|
|
||||||
L->ci = ci = &L->base_ci; /* unwind CallInfo list */
|
|
||||||
setnilvalue(s2v(L->stack)); /* 'function' entry for basic 'ci' */
|
setnilvalue(s2v(L->stack)); /* 'function' entry for basic 'ci' */
|
||||||
ci->func = L->stack;
|
ci->func = L->stack;
|
||||||
ci->callstatus = CIST_C;
|
ci->callstatus = CIST_C;
|
||||||
status = luaF_close(L, L->stack, CLOSEPROTECT);
|
if (status == LUA_YIELD)
|
||||||
if (status != CLOSEPROTECT) /* real errors? */
|
|
||||||
luaD_seterrorobj(L, status, L->stack + 1);
|
|
||||||
else {
|
|
||||||
status = LUA_OK;
|
status = LUA_OK;
|
||||||
|
status = luaD_closeprotected(L, 1, status);
|
||||||
|
if (status != LUA_OK) /* errors? */
|
||||||
|
luaD_seterrorobj(L, status, L->stack + 1);
|
||||||
|
else
|
||||||
L->top = L->stack + 1;
|
L->top = L->stack + 1;
|
||||||
}
|
|
||||||
ci->top = L->top + LUA_MINSTACK;
|
ci->top = L->top + LUA_MINSTACK;
|
||||||
L->status = status;
|
L->status = cast_byte(status);
|
||||||
|
luaD_reallocstack(L, cast_int(ci->top - L->stack), 0);
|
||||||
|
return status;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
LUA_API int lua_resetthread (lua_State *L) {
|
||||||
|
int status;
|
||||||
|
lua_lock(L);
|
||||||
|
status = luaE_resetthread(L, L->status);
|
||||||
lua_unlock(L);
|
lua_unlock(L);
|
||||||
return status;
|
return status;
|
||||||
}
|
}
|
||||||
@@ -360,7 +365,6 @@ LUA_API lua_State *lua_newstate (lua_Alloc f, void *ud) {
|
|||||||
preinit_thread(L, g);
|
preinit_thread(L, g);
|
||||||
g->allgc = obj2gco(L); /* by now, only object is the main thread */
|
g->allgc = obj2gco(L); /* by now, only object is the main thread */
|
||||||
L->next = NULL;
|
L->next = NULL;
|
||||||
L->nCcalls = 0;
|
|
||||||
incnny(L); /* main thread is always non yieldable */
|
incnny(L); /* main thread is always non yieldable */
|
||||||
g->frealloc = f;
|
g->frealloc = f;
|
||||||
g->ud = ud;
|
g->ud = ud;
|
||||||
@@ -375,6 +379,7 @@ LUA_API lua_State *lua_newstate (lua_Alloc f, void *ud) {
|
|||||||
g->panic = NULL;
|
g->panic = NULL;
|
||||||
g->gcstate = GCSpause;
|
g->gcstate = GCSpause;
|
||||||
g->gckind = KGC_INC;
|
g->gckind = KGC_INC;
|
||||||
|
g->gcstopem = 0;
|
||||||
g->gcemergency = 0;
|
g->gcemergency = 0;
|
||||||
g->finobj = g->tobefnz = g->fixedgc = NULL;
|
g->finobj = g->tobefnz = g->fixedgc = NULL;
|
||||||
g->firstold1 = g->survival = g->old1 = g->reallyold = NULL;
|
g->firstold1 = g->survival = g->old1 = g->reallyold = NULL;
|
||||||
|
|||||||
47
lstate.h
47
lstate.h
@@ -156,6 +156,18 @@ typedef struct stringtable {
|
|||||||
|
|
||||||
/*
|
/*
|
||||||
** Information about a call.
|
** 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 C function;
|
||||||
|
** - field 'transferinfo' is used only during call/returnhooks,
|
||||||
|
** before the function starts or after it ends.
|
||||||
*/
|
*/
|
||||||
typedef struct CallInfo {
|
typedef struct CallInfo {
|
||||||
StkId func; /* function index in the stack */
|
StkId func; /* function index in the stack */
|
||||||
@@ -176,6 +188,7 @@ typedef struct CallInfo {
|
|||||||
union {
|
union {
|
||||||
int funcidx; /* called-function index */
|
int funcidx; /* called-function index */
|
||||||
int nyield; /* number of values yielded */
|
int nyield; /* number of values yielded */
|
||||||
|
int nres; /* number of values returned */
|
||||||
struct { /* info about transferred values (for call/return hooks) */
|
struct { /* info about transferred values (for call/return hooks) */
|
||||||
unsigned short ftransfer; /* offset of first value transferred */
|
unsigned short ftransfer; /* offset of first value transferred */
|
||||||
unsigned short ntransfer; /* number of values transferred */
|
unsigned short ntransfer; /* number of values transferred */
|
||||||
@@ -191,17 +204,34 @@ typedef struct CallInfo {
|
|||||||
*/
|
*/
|
||||||
#define CIST_OAH (1<<0) /* original value of 'allowhook' */
|
#define CIST_OAH (1<<0) /* original value of 'allowhook' */
|
||||||
#define CIST_C (1<<1) /* call is running a C function */
|
#define CIST_C (1<<1) /* call is running a C function */
|
||||||
#define CIST_FRESH (1<<2) /* call is on a fresh "luaV_execute" frame */
|
#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_HOOKED (1<<3) /* call is running a debug hook */
|
||||||
#define CIST_YPCALL (1<<4) /* call is a yieldable protected call */
|
#define CIST_YPCALL (1<<4) /* doing a yieldable protected call */
|
||||||
#define CIST_TAIL (1<<5) /* call was tail called */
|
#define CIST_TAIL (1<<5) /* call was tail called */
|
||||||
#define CIST_HOOKYIELD (1<<6) /* last hook called yielded */
|
#define CIST_HOOKYIELD (1<<6) /* last hook called yielded */
|
||||||
#define CIST_FIN (1<<7) /* call is running a finalizer */
|
#define CIST_FIN (1<<7) /* call is running a finalizer */
|
||||||
#define CIST_TRAN (1<<8) /* 'ci' has transfer information */
|
#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)
|
#if defined(LUA_COMPAT_LT_LE)
|
||||||
#define CIST_LEQ (1<<9) /* using __lt for __le */
|
#define CIST_LEQ (1<<13) /* using __lt for __le */
|
||||||
#endif
|
#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 */
|
/* active function is a Lua function */
|
||||||
#define isLua(ci) (!((ci)->callstatus & CIST_C))
|
#define isLua(ci) (!((ci)->callstatus & CIST_C))
|
||||||
|
|
||||||
@@ -230,6 +260,7 @@ typedef struct global_State {
|
|||||||
lu_byte currentwhite;
|
lu_byte currentwhite;
|
||||||
lu_byte gcstate; /* state of garbage collector */
|
lu_byte gcstate; /* state of garbage collector */
|
||||||
lu_byte gckind; /* kind of GC running */
|
lu_byte gckind; /* kind of GC running */
|
||||||
|
lu_byte gcstopem; /* stops emergency collections */
|
||||||
lu_byte genminormul; /* control for minor generational collections */
|
lu_byte genminormul; /* control for minor generational collections */
|
||||||
lu_byte genmajormul; /* control for major generational collections */
|
lu_byte genmajormul; /* control for major generational collections */
|
||||||
lu_byte gcrunning; /* true if GC is running */
|
lu_byte gcrunning; /* true if GC is running */
|
||||||
@@ -281,6 +312,7 @@ struct lua_State {
|
|||||||
StkId stack_last; /* end of stack (last element + 1) */
|
StkId stack_last; /* end of stack (last element + 1) */
|
||||||
StkId stack; /* stack base */
|
StkId stack; /* stack base */
|
||||||
UpVal *openupval; /* list of open upvalues in this stack */
|
UpVal *openupval; /* list of open upvalues in this stack */
|
||||||
|
StkId tbclist; /* list of to-be-closed variables */
|
||||||
GCObject *gclist;
|
GCObject *gclist;
|
||||||
struct lua_State *twups; /* list of threads with open upvalues */
|
struct lua_State *twups; /* list of threads with open upvalues */
|
||||||
struct lua_longjmp *errorJmp; /* current error recover point */
|
struct lua_longjmp *errorJmp; /* current error recover point */
|
||||||
@@ -297,6 +329,12 @@ struct lua_State {
|
|||||||
|
|
||||||
#define G(L) (L->l_G)
|
#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)
|
** Union of all collectable objects (only for conversions)
|
||||||
@@ -359,6 +397,7 @@ LUAI_FUNC void luaE_checkcstack (lua_State *L);
|
|||||||
LUAI_FUNC void luaE_incCstack (lua_State *L);
|
LUAI_FUNC void luaE_incCstack (lua_State *L);
|
||||||
LUAI_FUNC void luaE_warning (lua_State *L, const char *msg, int tocont);
|
LUAI_FUNC void luaE_warning (lua_State *L, const char *msg, int tocont);
|
||||||
LUAI_FUNC void luaE_warnerror (lua_State *L, const char *where);
|
LUAI_FUNC void luaE_warnerror (lua_State *L, const char *where);
|
||||||
|
LUAI_FUNC int luaE_resetthread (lua_State *L, int status);
|
||||||
|
|
||||||
|
|
||||||
#endif
|
#endif
|
||||||
|
|||||||
@@ -89,7 +89,7 @@ void luaS_resize (lua_State *L, int nsize) {
|
|||||||
if (nsize < osize) /* shrinking table? */
|
if (nsize < osize) /* shrinking table? */
|
||||||
tablerehash(tb->hash, osize, nsize); /* depopulate shrinking part */
|
tablerehash(tb->hash, osize, nsize); /* depopulate shrinking part */
|
||||||
newvect = luaM_reallocvector(L, tb->hash, osize, nsize, TString*);
|
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? */
|
if (nsize < osize) /* was it shrinking table? */
|
||||||
tablerehash(tb->hash, nsize, osize); /* restore to original size */
|
tablerehash(tb->hash, nsize, osize); /* restore to original size */
|
||||||
/* leave table as it was */
|
/* leave table as it was */
|
||||||
@@ -172,7 +172,7 @@ void luaS_remove (lua_State *L, TString *ts) {
|
|||||||
|
|
||||||
|
|
||||||
static void growstrtab (lua_State *L, stringtable *tb) {
|
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... */
|
luaC_fullgc(L, 1); /* try to free some... */
|
||||||
if (tb->nuse == MAX_INT) /* still too many? */
|
if (tb->nuse == MAX_INT) /* still too many? */
|
||||||
luaM_error(L); /* cannot even create a message... */
|
luaM_error(L); /* cannot even create a message... */
|
||||||
@@ -223,7 +223,7 @@ TString *luaS_newlstr (lua_State *L, const char *str, size_t l) {
|
|||||||
return internshrstr(L, str, l);
|
return internshrstr(L, str, l);
|
||||||
else {
|
else {
|
||||||
TString *ts;
|
TString *ts;
|
||||||
if (unlikely(l >= (MAX_SIZE - sizeof(TString))/sizeof(char)))
|
if (l_unlikely(l >= (MAX_SIZE - sizeof(TString))/sizeof(char)))
|
||||||
luaM_toobig(L);
|
luaM_toobig(L);
|
||||||
ts = luaS_createlngstrobj(L, l);
|
ts = luaS_createlngstrobj(L, l);
|
||||||
memcpy(getstr(ts), str, l * sizeof(char));
|
memcpy(getstr(ts), str, l * sizeof(char));
|
||||||
@@ -259,7 +259,7 @@ Udata *luaS_newudata (lua_State *L, size_t s, int nuvalue) {
|
|||||||
Udata *u;
|
Udata *u;
|
||||||
int i;
|
int i;
|
||||||
GCObject *o;
|
GCObject *o;
|
||||||
if (unlikely(s > MAX_SIZE - udatamemoffset(nuvalue)))
|
if (l_unlikely(s > MAX_SIZE - udatamemoffset(nuvalue)))
|
||||||
luaM_toobig(L);
|
luaM_toobig(L);
|
||||||
o = luaC_newobj(L, LUA_VUSERDATA, sizeudata(nuvalue, s));
|
o = luaC_newobj(L, LUA_VUSERDATA, sizeudata(nuvalue, s));
|
||||||
u = gco2u(o);
|
u = gco2u(o);
|
||||||
|
|||||||
111
lstrlib.c
111
lstrlib.c
@@ -152,8 +152,9 @@ static int str_rep (lua_State *L) {
|
|||||||
const char *s = luaL_checklstring(L, 1, &l);
|
const char *s = luaL_checklstring(L, 1, &l);
|
||||||
lua_Integer n = luaL_checkinteger(L, 2);
|
lua_Integer n = luaL_checkinteger(L, 2);
|
||||||
const char *sep = luaL_optlstring(L, 3, "", &lsep);
|
const char *sep = luaL_optlstring(L, 3, "", &lsep);
|
||||||
if (n <= 0) lua_pushliteral(L, "");
|
if (n <= 0)
|
||||||
else if (l + lsep < l || l + lsep > MAXSIZE / n) /* may overflow? */
|
lua_pushliteral(L, "");
|
||||||
|
else if (l_unlikely(l + lsep < l || l + lsep > MAXSIZE / n))
|
||||||
return luaL_error(L, "resulting string too large");
|
return luaL_error(L, "resulting string too large");
|
||||||
else {
|
else {
|
||||||
size_t totallen = (size_t)n * l + (size_t)(n - 1) * lsep;
|
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);
|
size_t pose = getendpos(L, 3, pi, l);
|
||||||
int n, i;
|
int n, i;
|
||||||
if (posi > pose) return 0; /* empty interval; return no values */
|
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");
|
return luaL_error(L, "string slice too long");
|
||||||
n = (int)(pose - posi) + 1;
|
n = (int)(pose - posi) + 1;
|
||||||
luaL_checkstack(L, n, "string slice too long");
|
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);
|
luaL_checktype(L, 1, LUA_TFUNCTION);
|
||||||
lua_settop(L, 1); /* ensure function is on the top of the stack */
|
lua_settop(L, 1); /* ensure function is on the top of the stack */
|
||||||
state.init = 0;
|
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");
|
return luaL_error(L, "unable to dump given function");
|
||||||
luaL_pushresult(&state.B);
|
luaL_pushresult(&state.B);
|
||||||
return 1;
|
return 1;
|
||||||
@@ -275,7 +276,8 @@ static int tonum (lua_State *L, int arg) {
|
|||||||
|
|
||||||
static void trymt (lua_State *L, const char *mtname) {
|
static void trymt (lua_State *L, const char *mtname) {
|
||||||
lua_settop(L, 2); /* back to the original arguments */
|
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_error(L, "attempt to %s a '%s' with a '%s'", mtname + 2,
|
||||||
luaL_typename(L, -2), luaL_typename(L, -1));
|
luaL_typename(L, -2), luaL_typename(L, -1));
|
||||||
lua_insert(L, -3); /* put metamethod before arguments */
|
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) {
|
static int check_capture (MatchState *ms, int l) {
|
||||||
l -= '1';
|
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 luaL_error(ms->L, "invalid capture index %%%d", l + 1);
|
||||||
return l;
|
return l;
|
||||||
}
|
}
|
||||||
@@ -400,14 +403,14 @@ static int capture_to_close (MatchState *ms) {
|
|||||||
static const char *classend (MatchState *ms, const char *p) {
|
static const char *classend (MatchState *ms, const char *p) {
|
||||||
switch (*p++) {
|
switch (*p++) {
|
||||||
case L_ESC: {
|
case L_ESC: {
|
||||||
if (p == ms->p_end)
|
if (l_unlikely(p == ms->p_end))
|
||||||
luaL_error(ms->L, "malformed pattern (ends with '%%')");
|
luaL_error(ms->L, "malformed pattern (ends with '%%')");
|
||||||
return p+1;
|
return p+1;
|
||||||
}
|
}
|
||||||
case '[': {
|
case '[': {
|
||||||
if (*p == '^') p++;
|
if (*p == '^') p++;
|
||||||
do { /* look for a ']' */
|
do { /* look for a ']' */
|
||||||
if (p == ms->p_end)
|
if (l_unlikely(p == ms->p_end))
|
||||||
luaL_error(ms->L, "malformed pattern (missing ']')");
|
luaL_error(ms->L, "malformed pattern (missing ']')");
|
||||||
if (*(p++) == L_ESC && p < ms->p_end)
|
if (*(p++) == L_ESC && p < ms->p_end)
|
||||||
p++; /* skip escapes (e.g. '%]') */
|
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,
|
static const char *matchbalance (MatchState *ms, const char *s,
|
||||||
const char *p) {
|
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')");
|
luaL_error(ms->L, "malformed pattern (missing arguments to '%%b')");
|
||||||
if (*s != *p) return NULL;
|
if (*s != *p) return NULL;
|
||||||
else {
|
else {
|
||||||
@@ -565,7 +568,7 @@ static const char *match_capture (MatchState *ms, const char *s, int l) {
|
|||||||
|
|
||||||
|
|
||||||
static const char *match (MatchState *ms, const char *s, const char *p) {
|
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");
|
luaL_error(ms->L, "pattern too complex");
|
||||||
init: /* using goto's to optimize tail recursion */
|
init: /* using goto's to optimize tail recursion */
|
||||||
if (p != ms->p_end) { /* end of pattern? */
|
if (p != ms->p_end) { /* end of pattern? */
|
||||||
@@ -599,7 +602,7 @@ static const char *match (MatchState *ms, const char *s, const char *p) {
|
|||||||
case 'f': { /* frontier? */
|
case 'f': { /* frontier? */
|
||||||
const char *ep; char previous;
|
const char *ep; char previous;
|
||||||
p += 2;
|
p += 2;
|
||||||
if (*p != '[')
|
if (l_unlikely(*p != '['))
|
||||||
luaL_error(ms->L, "missing '[' after '%%f' in pattern");
|
luaL_error(ms->L, "missing '[' after '%%f' in pattern");
|
||||||
ep = classend(ms, p); /* points to what is next */
|
ep = classend(ms, p); /* points to what is next */
|
||||||
previous = (s == ms->src_init) ? '\0' : *(s - 1);
|
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,
|
static size_t get_onecapture (MatchState *ms, int i, const char *s,
|
||||||
const char *e, const char **cap) {
|
const char *e, const char **cap) {
|
||||||
if (i >= ms->level) {
|
if (i >= ms->level) {
|
||||||
if (i != 0)
|
if (l_unlikely(i != 0))
|
||||||
luaL_error(ms->L, "invalid capture index %%%d", i + 1);
|
luaL_error(ms->L, "invalid capture index %%%d", i + 1);
|
||||||
*cap = s;
|
*cap = s;
|
||||||
return e - s;
|
return e - s;
|
||||||
@@ -707,7 +710,7 @@ static size_t get_onecapture (MatchState *ms, int i, const char *s,
|
|||||||
else {
|
else {
|
||||||
ptrdiff_t capl = ms->capture[i].len;
|
ptrdiff_t capl = ms->capture[i].len;
|
||||||
*cap = ms->capture[i].init;
|
*cap = ms->capture[i].init;
|
||||||
if (capl == CAP_UNFINISHED)
|
if (l_unlikely(capl == CAP_UNFINISHED))
|
||||||
luaL_error(ms->L, "unfinished capture");
|
luaL_error(ms->L, "unfinished capture");
|
||||||
else if (capl == CAP_POSITION)
|
else if (capl == CAP_POSITION)
|
||||||
lua_pushinteger(ms->L, (ms->capture[i].init - ms->src_init) + 1);
|
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 */
|
luaL_addlstring(b, s, e - s); /* keep original text */
|
||||||
return 0; /* no changes */
|
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)",
|
return luaL_error(L, "invalid replacement value (a %s)",
|
||||||
luaL_typename(L, -1));
|
luaL_typename(L, -1));
|
||||||
else {
|
else {
|
||||||
@@ -1058,7 +1061,7 @@ static int lua_number2strx (lua_State *L, char *buff, int sz,
|
|||||||
for (i = 0; i < n; i++)
|
for (i = 0; i < n; i++)
|
||||||
buff[i] = toupper(uchar(buff[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 luaL_error(L, "modifiers for format '%%a'/'%%A' not implemented");
|
||||||
return n;
|
return n;
|
||||||
}
|
}
|
||||||
@@ -1358,16 +1361,6 @@ struct cD {
|
|||||||
#define MAXALIGN (offsetof(struct cD, u))
|
#define MAXALIGN (offsetof(struct cD, u))
|
||||||
|
|
||||||
|
|
||||||
/*
|
|
||||||
** Union for serializing floats
|
|
||||||
*/
|
|
||||||
typedef union Ftypes {
|
|
||||||
float f;
|
|
||||||
double d;
|
|
||||||
lua_Number n;
|
|
||||||
} Ftypes;
|
|
||||||
|
|
||||||
|
|
||||||
/*
|
/*
|
||||||
** information to pack/unpack stuff
|
** information to pack/unpack stuff
|
||||||
*/
|
*/
|
||||||
@@ -1384,7 +1377,9 @@ typedef struct Header {
|
|||||||
typedef enum KOption {
|
typedef enum KOption {
|
||||||
Kint, /* signed integers */
|
Kint, /* signed integers */
|
||||||
Kuint, /* unsigned 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 */
|
Kchar, /* fixed-length strings */
|
||||||
Kstring, /* strings with prefixed length */
|
Kstring, /* strings with prefixed length */
|
||||||
Kzstr, /* zero-terminated strings */
|
Kzstr, /* zero-terminated strings */
|
||||||
@@ -1419,7 +1414,7 @@ static int getnum (const char **fmt, int df) {
|
|||||||
*/
|
*/
|
||||||
static int getnumlimit (Header *h, const char **fmt, int df) {
|
static int getnumlimit (Header *h, const char **fmt, int df) {
|
||||||
int sz = getnum(fmt, 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]",
|
return luaL_error(h->L, "integral size (%d) out of limits [1,%d]",
|
||||||
sz, MAXINTSIZE);
|
sz, MAXINTSIZE);
|
||||||
return sz;
|
return sz;
|
||||||
@@ -1453,14 +1448,14 @@ static KOption getoption (Header *h, const char **fmt, int *size) {
|
|||||||
case 'J': *size = sizeof(lua_Integer); return Kuint;
|
case 'J': *size = sizeof(lua_Integer); return Kuint;
|
||||||
case 'T': *size = sizeof(size_t); return Kuint;
|
case 'T': *size = sizeof(size_t); return Kuint;
|
||||||
case 'f': *size = sizeof(float); return Kfloat;
|
case 'f': *size = sizeof(float); return Kfloat;
|
||||||
case 'd': *size = sizeof(double); return Kfloat;
|
case 'n': *size = sizeof(lua_Number); return Knumber;
|
||||||
case 'n': *size = sizeof(lua_Number); return Kfloat;
|
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 Kint;
|
||||||
case 'I': *size = getnumlimit(h, fmt, sizeof(int)); return Kuint;
|
case 'I': *size = getnumlimit(h, fmt, sizeof(int)); return Kuint;
|
||||||
case 's': *size = getnumlimit(h, fmt, sizeof(size_t)); return Kstring;
|
case 's': *size = getnumlimit(h, fmt, sizeof(size_t)); return Kstring;
|
||||||
case 'c':
|
case 'c':
|
||||||
*size = getnum(fmt, -1);
|
*size = getnum(fmt, -1);
|
||||||
if (*size == -1)
|
if (l_unlikely(*size == -1))
|
||||||
luaL_error(h->L, "missing size for format option 'c'");
|
luaL_error(h->L, "missing size for format option 'c'");
|
||||||
return Kchar;
|
return Kchar;
|
||||||
case 'z': return Kzstr;
|
case 'z': return Kzstr;
|
||||||
@@ -1499,7 +1494,7 @@ static KOption getdetails (Header *h, size_t totalsize,
|
|||||||
else {
|
else {
|
||||||
if (align > h->maxalign) /* enforce maximum alignment */
|
if (align > h->maxalign) /* enforce maximum alignment */
|
||||||
align = h->maxalign;
|
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");
|
luaL_argerror(h->L, 1, "format asks for alignment not power of 2");
|
||||||
*ntoalign = (align - (int)(totalsize & (align - 1))) & (align - 1);
|
*ntoalign = (align - (int)(totalsize & (align - 1))) & (align - 1);
|
||||||
}
|
}
|
||||||
@@ -1580,15 +1575,27 @@ static int str_pack (lua_State *L) {
|
|||||||
packint(&b, (lua_Unsigned)n, h.islittle, size, 0);
|
packint(&b, (lua_Unsigned)n, h.islittle, size, 0);
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
case Kfloat: { /* floating-point options */
|
case Kfloat: { /* C float */
|
||||||
Ftypes u;
|
float f = (float)luaL_checknumber(L, arg); /* get argument */
|
||||||
char *buff = luaL_prepbuffsize(&b, size);
|
char *buff = luaL_prepbuffsize(&b, sizeof(f));
|
||||||
lua_Number n = luaL_checknumber(L, arg); /* get argument */
|
/* move 'f' to final result, correcting endianness if needed */
|
||||||
if (size == sizeof(u.f)) u.f = (float)n; /* copy it into 'u' */
|
copywithendian(buff, (char *)&f, sizeof(f), h.islittle);
|
||||||
else if (size == sizeof(u.d)) u.d = (double)n;
|
luaL_addsize(&b, size);
|
||||||
else u.n = n;
|
break;
|
||||||
/* move 'u' to final result, correcting endianness if needed */
|
}
|
||||||
copywithendian(buff, (char *)&u, size, h.islittle);
|
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);
|
luaL_addsize(&b, size);
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
@@ -1679,7 +1686,7 @@ static lua_Integer unpackint (lua_State *L, const char *str,
|
|||||||
else if (size > SZINT) { /* must check unread bytes */
|
else if (size > SZINT) { /* must check unread bytes */
|
||||||
int mask = (!issigned || (lua_Integer)res >= 0) ? 0 : MC;
|
int mask = (!issigned || (lua_Integer)res >= 0) ? 0 : MC;
|
||||||
for (i = limit; i < size; i++) {
|
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);
|
luaL_error(L, "%d-byte integer does not fit into Lua Integer", size);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -1714,13 +1721,21 @@ static int str_unpack (lua_State *L) {
|
|||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
case Kfloat: {
|
case Kfloat: {
|
||||||
Ftypes u;
|
float f;
|
||||||
lua_Number num;
|
copywithendian((char *)&f, data + pos, sizeof(f), h.islittle);
|
||||||
copywithendian((char *)&u, data + pos, size, h.islittle);
|
lua_pushnumber(L, (lua_Number)f);
|
||||||
if (size == sizeof(u.f)) num = (lua_Number)u.f;
|
break;
|
||||||
else if (size == sizeof(u.d)) num = (lua_Number)u.d;
|
}
|
||||||
else num = u.n;
|
case Knumber: {
|
||||||
lua_pushnumber(L, num);
|
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;
|
break;
|
||||||
}
|
}
|
||||||
case Kchar: {
|
case Kchar: {
|
||||||
|
|||||||
113
ltable.c
113
ltable.c
@@ -68,20 +68,25 @@
|
|||||||
#define MAXHSIZE luaM_limitN(1u << MAXHBITS, Node)
|
#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))))
|
#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 hashstr(t,str) hashpow2(t, (str)->hash)
|
||||||
#define hashboolean(t,p) hashpow2(t, p)
|
#define hashboolean(t,p) hashpow2(t, p)
|
||||||
|
|
||||||
#define hashint(t,i) hashpow2(t, i)
|
#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))
|
#define hashpointer(t,p) hashmod(t, point2uint(p))
|
||||||
|
|
||||||
|
|
||||||
@@ -135,24 +140,38 @@ static int l_hashfloat (lua_Number n) {
|
|||||||
*/
|
*/
|
||||||
static Node *mainposition (const Table *t, int ktt, const Value *kvl) {
|
static Node *mainposition (const Table *t, int ktt, const Value *kvl) {
|
||||||
switch (withvariant(ktt)) {
|
switch (withvariant(ktt)) {
|
||||||
case LUA_VNUMINT:
|
case LUA_VNUMINT: {
|
||||||
return hashint(t, ivalueraw(*kvl));
|
lua_Integer key = ivalueraw(*kvl);
|
||||||
case LUA_VNUMFLT:
|
return hashint(t, key);
|
||||||
return hashmod(t, l_hashfloat(fltvalueraw(*kvl)));
|
}
|
||||||
case LUA_VSHRSTR:
|
case LUA_VNUMFLT: {
|
||||||
return hashstr(t, tsvalueraw(*kvl));
|
lua_Number n = fltvalueraw(*kvl);
|
||||||
case LUA_VLNGSTR:
|
return hashmod(t, l_hashfloat(n));
|
||||||
return hashpow2(t, luaS_hashlongstr(tsvalueraw(*kvl)));
|
}
|
||||||
|
case LUA_VSHRSTR: {
|
||||||
|
TString *ts = tsvalueraw(*kvl);
|
||||||
|
return hashstr(t, ts);
|
||||||
|
}
|
||||||
|
case LUA_VLNGSTR: {
|
||||||
|
TString *ts = tsvalueraw(*kvl);
|
||||||
|
return hashpow2(t, luaS_hashlongstr(ts));
|
||||||
|
}
|
||||||
case LUA_VFALSE:
|
case LUA_VFALSE:
|
||||||
return hashboolean(t, 0);
|
return hashboolean(t, 0);
|
||||||
case LUA_VTRUE:
|
case LUA_VTRUE:
|
||||||
return hashboolean(t, 1);
|
return hashboolean(t, 1);
|
||||||
case LUA_VLIGHTUSERDATA:
|
case LUA_VLIGHTUSERDATA: {
|
||||||
return hashpointer(t, pvalueraw(*kvl));
|
void *p = pvalueraw(*kvl);
|
||||||
case LUA_VLCF:
|
return hashpointer(t, p);
|
||||||
return hashpointer(t, fvalueraw(*kvl));
|
}
|
||||||
default:
|
case LUA_VLCF: {
|
||||||
return hashpointer(t, gcvalueraw(*kvl));
|
lua_CFunction f = fvalueraw(*kvl);
|
||||||
|
return hashpointer(t, f);
|
||||||
|
}
|
||||||
|
default: {
|
||||||
|
GCObject *o = gcvalueraw(*kvl);
|
||||||
|
return hashpointer(t, o);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -307,7 +326,7 @@ static unsigned int findindex (lua_State *L, Table *t, TValue *key,
|
|||||||
return i; /* yes; that's the index */
|
return i; /* yes; that's the index */
|
||||||
else {
|
else {
|
||||||
const TValue *n = getgeneric(t, key, 1);
|
const TValue *n = getgeneric(t, key, 1);
|
||||||
if (unlikely(isabstkey(n)))
|
if (l_unlikely(isabstkey(n)))
|
||||||
luaG_runerror(L, "invalid key to 'next'"); /* key not found */
|
luaG_runerror(L, "invalid key to 'next'"); /* key not found */
|
||||||
i = cast_int(nodefromval(n) - gnode(t, 0)); /* key index in hash table */
|
i = cast_int(nodefromval(n) - gnode(t, 0)); /* key index in hash table */
|
||||||
/* hash elements are numbered after array ones */
|
/* hash elements are numbered after array ones */
|
||||||
@@ -485,7 +504,7 @@ static void reinsert (lua_State *L, Table *ot, Table *t) {
|
|||||||
already present in the table */
|
already present in the table */
|
||||||
TValue k;
|
TValue k;
|
||||||
getnodekey(L, &k, old);
|
getnodekey(L, &k, old);
|
||||||
setobjt2t(L, luaH_set(L, t, &k), gval(old));
|
luaH_set(L, t, &k, gval(old));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -541,7 +560,7 @@ void luaH_resize (lua_State *L, Table *t, unsigned int newasize,
|
|||||||
}
|
}
|
||||||
/* allocate new array */
|
/* allocate new array */
|
||||||
newarray = luaM_reallocvector(L, t->array, oldasize, newasize, TValue);
|
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 */
|
freehash(L, &newt); /* release new hash part */
|
||||||
luaM_error(L); /* raise error (with array unchanged) */
|
luaM_error(L); /* raise error (with array unchanged) */
|
||||||
}
|
}
|
||||||
@@ -632,10 +651,10 @@ static Node *getfreepos (Table *t) {
|
|||||||
** put new key in its main position; otherwise (colliding node is in its main
|
** put new key in its main position; otherwise (colliding node is in its main
|
||||||
** position), new key goes to an empty position.
|
** 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;
|
Node *mp;
|
||||||
TValue aux;
|
TValue aux;
|
||||||
if (unlikely(ttisnil(key)))
|
if (l_unlikely(ttisnil(key)))
|
||||||
luaG_runerror(L, "table index is nil");
|
luaG_runerror(L, "table index is nil");
|
||||||
else if (ttisfloat(key)) {
|
else if (ttisfloat(key)) {
|
||||||
lua_Number f = fltvalue(key);
|
lua_Number f = fltvalue(key);
|
||||||
@@ -644,9 +663,11 @@ TValue *luaH_newkey (lua_State *L, Table *t, const TValue *key) {
|
|||||||
setivalue(&aux, k);
|
setivalue(&aux, k);
|
||||||
key = &aux; /* insert it as an integer */
|
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");
|
luaG_runerror(L, "table index is NaN");
|
||||||
}
|
}
|
||||||
|
if (ttisnil(value))
|
||||||
|
return; /* do not insert nil values */
|
||||||
mp = mainpositionTV(t, key);
|
mp = mainpositionTV(t, key);
|
||||||
if (!isempty(gval(mp)) || isdummy(t)) { /* main position is taken? */
|
if (!isempty(gval(mp)) || isdummy(t)) { /* main position is taken? */
|
||||||
Node *othern;
|
Node *othern;
|
||||||
@@ -654,7 +675,8 @@ TValue *luaH_newkey (lua_State *L, Table *t, const TValue *key) {
|
|||||||
if (f == NULL) { /* cannot find a free place? */
|
if (f == NULL) { /* cannot find a free place? */
|
||||||
rehash(L, t, key); /* grow table */
|
rehash(L, t, key); /* grow table */
|
||||||
/* whatever called 'newkey' takes care of TM cache */
|
/* 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));
|
lua_assert(!isdummy(t));
|
||||||
othern = mainposition(t, keytt(mp), &keyval(mp));
|
othern = mainposition(t, keytt(mp), &keyval(mp));
|
||||||
@@ -682,7 +704,7 @@ TValue *luaH_newkey (lua_State *L, Table *t, const TValue *key) {
|
|||||||
setnodekey(L, mp, key);
|
setnodekey(L, mp, key);
|
||||||
luaC_barrierback(L, obj2gco(t), key);
|
luaC_barrierback(L, obj2gco(t), key);
|
||||||
lua_assert(isempty(gval(mp)));
|
lua_assert(isempty(gval(mp)));
|
||||||
return gval(mp);
|
setobj2t(L, gval(mp), value);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
@@ -769,29 +791,40 @@ const TValue *luaH_get (Table *t, const TValue *key) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
/*
|
||||||
|
** 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
|
** beware: when using this function you probably need to check a GC
|
||||||
** barrier and invalidate the TM cache.
|
** barrier and invalidate the TM cache.
|
||||||
*/
|
*/
|
||||||
TValue *luaH_set (lua_State *L, Table *t, const TValue *key) {
|
void luaH_set (lua_State *L, Table *t, const TValue *key, TValue *value) {
|
||||||
const TValue *p = luaH_get(t, key);
|
const TValue *slot = luaH_get(t, key);
|
||||||
if (!isabstkey(p))
|
luaH_finishset(L, t, key, slot, value);
|
||||||
return cast(TValue *, p);
|
|
||||||
else return luaH_newkey(L, t, key);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
void luaH_setint (lua_State *L, Table *t, lua_Integer key, TValue *value) {
|
void luaH_setint (lua_State *L, Table *t, lua_Integer key, TValue *value) {
|
||||||
const TValue *p = luaH_getint(t, key);
|
const TValue *p = luaH_getint(t, key);
|
||||||
TValue *cell;
|
if (isabstkey(p)) {
|
||||||
if (!isabstkey(p))
|
|
||||||
cell = cast(TValue *, p);
|
|
||||||
else {
|
|
||||||
TValue k;
|
TValue k;
|
||||||
setivalue(&k, key);
|
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);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|||||||
8
ltable.h
8
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_getshortstr (Table *t, TString *key);
|
||||||
LUAI_FUNC const TValue *luaH_getstr (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 const TValue *luaH_get (Table *t, const TValue *key);
|
||||||
LUAI_FUNC TValue *luaH_newkey (lua_State *L, Table *t, const TValue *key);
|
LUAI_FUNC void luaH_newkey (lua_State *L, Table *t, const TValue *key,
|
||||||
LUAI_FUNC TValue *luaH_set (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 Table *luaH_new (lua_State *L);
|
||||||
LUAI_FUNC void luaH_resize (lua_State *L, Table *t, unsigned int nasize,
|
LUAI_FUNC void luaH_resize (lua_State *L, Table *t, unsigned int nasize,
|
||||||
unsigned int nhsize);
|
unsigned int nhsize);
|
||||||
|
|||||||
11
ltablib.c
11
ltablib.c
@@ -145,8 +145,8 @@ static int tmove (lua_State *L) {
|
|||||||
|
|
||||||
static void addfield (lua_State *L, luaL_Buffer *b, lua_Integer i) {
|
static void addfield (lua_State *L, luaL_Buffer *b, lua_Integer i) {
|
||||||
lua_geti(L, 1, i);
|
lua_geti(L, 1, i);
|
||||||
if (!lua_isstring(L, -1))
|
if (l_unlikely(!lua_isstring(L, -1)))
|
||||||
luaL_error(L, "invalid value (%s) at index %d in table for 'concat'",
|
luaL_error(L, "invalid value (%s) at index %I in table for 'concat'",
|
||||||
luaL_typename(L, -1), i);
|
luaL_typename(L, -1), i);
|
||||||
luaL_addvalue(b);
|
luaL_addvalue(b);
|
||||||
}
|
}
|
||||||
@@ -196,7 +196,8 @@ static int tunpack (lua_State *L) {
|
|||||||
lua_Integer e = luaL_opt(L, luaL_checkinteger, 3, luaL_len(L, 1));
|
lua_Integer e = luaL_opt(L, luaL_checkinteger, 3, luaL_len(L, 1));
|
||||||
if (i > e) return 0; /* empty range */
|
if (i > e) return 0; /* empty range */
|
||||||
n = (lua_Unsigned)e - i; /* number of elements minus 1 (avoid overflows) */
|
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");
|
return luaL_error(L, "too many results to unpack");
|
||||||
for (; i < e; i++) { /* push arg[i..e - 1] (to avoid overflows) */
|
for (; i < e; i++) { /* push arg[i..e - 1] (to avoid overflows) */
|
||||||
lua_geti(L, 1, i);
|
lua_geti(L, 1, i);
|
||||||
@@ -300,14 +301,14 @@ static IdxT partition (lua_State *L, IdxT lo, IdxT up) {
|
|||||||
for (;;) {
|
for (;;) {
|
||||||
/* next loop: repeat ++i while a[i] < P */
|
/* next loop: repeat ++i while a[i] < P */
|
||||||
while ((void)lua_geti(L, 1, ++i), sort_comp(L, -1, -2)) {
|
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");
|
luaL_error(L, "invalid order function for sorting");
|
||||||
lua_pop(L, 1); /* remove a[i] */
|
lua_pop(L, 1); /* remove a[i] */
|
||||||
}
|
}
|
||||||
/* after the loop, a[i] >= P and a[lo .. i - 1] < P */
|
/* after the loop, a[i] >= P and a[lo .. i - 1] < P */
|
||||||
/* next loop: repeat --j while P < a[j] */
|
/* next loop: repeat --j while P < a[j] */
|
||||||
while ((void)lua_geti(L, 1, --j), sort_comp(L, -3, -1)) {
|
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");
|
luaL_error(L, "invalid order function for sorting");
|
||||||
lua_pop(L, 1); /* remove a[j] */
|
lua_pop(L, 1); /* remove a[j] */
|
||||||
}
|
}
|
||||||
|
|||||||
132
ltests.c
132
ltests.c
@@ -121,6 +121,7 @@ static void warnf (void *ud, const char *msg, int tocont) {
|
|||||||
strcat(buff, msg); /* add new message to current warning */
|
strcat(buff, msg); /* add new message to current warning */
|
||||||
if (!tocont) { /* message finished? */
|
if (!tocont) { /* message finished? */
|
||||||
lua_unlock(L);
|
lua_unlock(L);
|
||||||
|
luaL_checkstack(L, 1, "warn stack space");
|
||||||
lua_getglobal(L, "_WARN");
|
lua_getglobal(L, "_WARN");
|
||||||
if (!lua_toboolean(L, -1))
|
if (!lua_toboolean(L, -1))
|
||||||
lua_pop(L, 1); /* ok, no previous unexpected warning */
|
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 */
|
case 2: { /* store */
|
||||||
lua_unlock(L);
|
lua_unlock(L);
|
||||||
|
luaL_checkstack(L, 1, "warn stack space");
|
||||||
lua_pushstring(L, buff);
|
lua_pushstring(L, buff);
|
||||||
lua_setglobal(L, "_WARN"); /* assign message to global '_WARN' */
|
lua_setglobal(L, "_WARN"); /* assign message to global '_WARN' */
|
||||||
lua_lock(L);
|
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
|
** 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;
|
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) {
|
static void checkvalref (global_State *g, GCObject *f, const TValue *t) {
|
||||||
lua_assert(!iscollectable(t) ||
|
assert(!iscollectable(t) || (righttt(t) && testobjref(g, f, gcvalue(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);
|
unsigned int asize = luaH_realasize(h);
|
||||||
Node *n, *limit = gnode(h, sizenode(h));
|
Node *n, *limit = gnode(h, sizenode(h));
|
||||||
GCObject *hgc = obj2gco(h);
|
GCObject *hgc = obj2gco(h);
|
||||||
checkobjref(g, hgc, h->metatable);
|
checkobjrefN(g, hgc, h->metatable);
|
||||||
for (i = 0; i < asize; i++)
|
for (i = 0; i < asize; i++)
|
||||||
checkvalref(g, hgc, &h->array[i]);
|
checkvalref(g, hgc, &h->array[i]);
|
||||||
for (n = gnode(h, 0); n < limit; n++) {
|
for (n = gnode(h, 0); n < limit; n++) {
|
||||||
if (!isempty(gval(n))) {
|
if (!isempty(gval(n))) {
|
||||||
TValue k;
|
TValue k;
|
||||||
getnodekey(g->mainthread, &k, n);
|
getnodekey(g->mainthread, &k, n);
|
||||||
lua_assert(!keyisnil(n));
|
assert(!keyisnil(n));
|
||||||
checkvalref(g, hgc, &k);
|
checkvalref(g, hgc, &k);
|
||||||
checkvalref(g, hgc, gval(n));
|
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) {
|
static void checkudata (global_State *g, Udata *u) {
|
||||||
int i;
|
int i;
|
||||||
GCObject *hgc = obj2gco(u);
|
GCObject *hgc = obj2gco(u);
|
||||||
checkobjref(g, hgc, u->metatable);
|
checkobjrefN(g, hgc, u->metatable);
|
||||||
for (i = 0; i < u->nuvalue; i++)
|
for (i = 0; i < u->nuvalue; i++)
|
||||||
checkvalref(g, hgc, &u->uv[i].uv);
|
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) {
|
static void checkproto (global_State *g, Proto *f) {
|
||||||
int i;
|
int i;
|
||||||
GCObject *fgc = obj2gco(f);
|
GCObject *fgc = obj2gco(f);
|
||||||
checkobjref(g, fgc, f->source);
|
checkobjrefN(g, fgc, f->source);
|
||||||
for (i=0; i<f->sizek; i++) {
|
for (i=0; i<f->sizek; i++) {
|
||||||
if (ttisstring(f->k + i))
|
if (iscollectable(f->k + i))
|
||||||
checkobjref(g, fgc, tsvalue(f->k + i));
|
checkobjref(g, fgc, gcvalue(f->k + i));
|
||||||
}
|
}
|
||||||
for (i=0; i<f->sizeupvalues; 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++)
|
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++)
|
for (i=0; i<f->sizelocvars; i++)
|
||||||
checkobjref(g, fgc, f->locvars[i].varname);
|
checkobjrefN(g, fgc, f->locvars[i].varname);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
@@ -401,11 +413,11 @@ static void checkCclosure (global_State *g, CClosure *cl) {
|
|||||||
static void checkLclosure (global_State *g, LClosure *cl) {
|
static void checkLclosure (global_State *g, LClosure *cl) {
|
||||||
GCObject *clgc = obj2gco(cl);
|
GCObject *clgc = obj2gco(cl);
|
||||||
int i;
|
int i;
|
||||||
checkobjref(g, clgc, cl->p);
|
checkobjrefN(g, clgc, cl->p);
|
||||||
for (i=0; i<cl->nupvalues; i++) {
|
for (i=0; i<cl->nupvalues; i++) {
|
||||||
UpVal *uv = cl->upvals[i];
|
UpVal *uv = cl->upvals[i];
|
||||||
if (uv) {
|
if (uv) {
|
||||||
checkobjref(g, clgc, uv);
|
checkobjrefN(g, clgc, uv);
|
||||||
if (!upisopen(uv))
|
if (!upisopen(uv))
|
||||||
checkvalref(g, obj2gco(uv), uv->v);
|
checkvalref(g, obj2gco(uv), uv->v);
|
||||||
}
|
}
|
||||||
@@ -428,17 +440,18 @@ static void checkstack (global_State *g, lua_State *L1) {
|
|||||||
StkId o;
|
StkId o;
|
||||||
CallInfo *ci;
|
CallInfo *ci;
|
||||||
UpVal *uv;
|
UpVal *uv;
|
||||||
lua_assert(!isdead(g, L1));
|
assert(!isdead(g, L1));
|
||||||
if (L1->stack == NULL) { /* incomplete thread? */
|
if (L1->stack == NULL) { /* incomplete thread? */
|
||||||
lua_assert(L1->openupval == NULL && L1->ci == NULL);
|
assert(L1->openupval == NULL && L1->ci == NULL);
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
for (uv = L1->openupval; uv != NULL; uv = uv->u.open.next)
|
for (uv = L1->openupval; uv != NULL; uv = uv->u.open.next)
|
||||||
lua_assert(upisopen(uv)); /* must be open */
|
assert(upisopen(uv)); /* must be open */
|
||||||
lua_assert(L1->top <= L1->stack_last);
|
assert(L1->top <= L1->stack_last);
|
||||||
|
assert(L1->tbclist <= L1->top);
|
||||||
for (ci = L1->ci; ci != NULL; ci = ci->previous) {
|
for (ci = L1->ci; ci != NULL; ci = ci->previous) {
|
||||||
lua_assert(ci->top <= L1->stack_last);
|
assert(ci->top <= L1->stack_last);
|
||||||
lua_assert(lua_checkpc(ci));
|
assert(lua_checkpc(ci));
|
||||||
}
|
}
|
||||||
for (o = L1->stack; o < L1->stack_last; o++)
|
for (o = L1->stack; o < L1->stack_last; o++)
|
||||||
checkliveness(L1, s2v(o)); /* entire stack must have valid values */
|
checkliveness(L1, s2v(o)); /* entire stack must have valid values */
|
||||||
@@ -477,10 +490,10 @@ static void checkrefs (global_State *g, GCObject *o) {
|
|||||||
}
|
}
|
||||||
case LUA_VSHRSTR:
|
case LUA_VSHRSTR:
|
||||||
case LUA_VLNGSTR: {
|
case LUA_VLNGSTR: {
|
||||||
lua_assert(!isgray(o)); /* strings are never gray */
|
assert(!isgray(o)); /* strings are never gray */
|
||||||
break;
|
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,
|
static void checkobject (global_State *g, GCObject *o, int maybedead,
|
||||||
int listage) {
|
int listage) {
|
||||||
if (isdead(g, o))
|
if (isdead(g, o))
|
||||||
lua_assert(maybedead);
|
assert(maybedead);
|
||||||
else {
|
else {
|
||||||
lua_assert(g->gcstate != GCSpause || iswhite(o));
|
assert(g->gcstate != GCSpause || iswhite(o));
|
||||||
if (g->gckind == KGC_GEN) { /* generational mode? */
|
if (g->gckind == KGC_GEN) { /* generational mode? */
|
||||||
lua_assert(getage(o) >= listage);
|
assert(getage(o) >= listage);
|
||||||
lua_assert(!iswhite(o) || !isold(o));
|
assert(!iswhite(o) || !isold(o));
|
||||||
if (isold(o)) {
|
if (isold(o)) {
|
||||||
lua_assert(isblack(o) ||
|
assert(isblack(o) ||
|
||||||
getage(o) == G_TOUCHED1 ||
|
getage(o) == G_TOUCHED1 ||
|
||||||
getage(o) == G_OLD0 ||
|
getage(o) == G_OLD0 ||
|
||||||
o->tt == LUA_VTHREAD ||
|
o->tt == LUA_VTHREAD ||
|
||||||
@@ -522,8 +535,8 @@ static lu_mem checkgraylist (global_State *g, GCObject *o) {
|
|||||||
int total = 0; /* count number of elements in the list */
|
int total = 0; /* count number of elements in the list */
|
||||||
((void)g); /* better to keep it available if we need to print an object */
|
((void)g); /* better to keep it available if we need to print an object */
|
||||||
while (o) {
|
while (o) {
|
||||||
lua_assert(!!isgray(o) ^ (getage(o) == G_TOUCHED2));
|
assert(!!isgray(o) ^ (getage(o) == G_TOUCHED2));
|
||||||
lua_assert(!testbit(o->marked, TESTBIT));
|
assert(!testbit(o->marked, TESTBIT));
|
||||||
if (keepinvariant(g))
|
if (keepinvariant(g))
|
||||||
l_setbit(o->marked, TESTBIT); /* mark that object is in a gray list */
|
l_setbit(o->marked, TESTBIT); /* mark that object is in a gray list */
|
||||||
total++;
|
total++;
|
||||||
@@ -534,10 +547,10 @@ static lu_mem checkgraylist (global_State *g, GCObject *o) {
|
|||||||
case LUA_VTHREAD: o = gco2th(o)->gclist; break;
|
case LUA_VTHREAD: o = gco2th(o)->gclist; break;
|
||||||
case LUA_VPROTO: o = gco2p(o)->gclist; break;
|
case LUA_VPROTO: o = gco2p(o)->gclist; break;
|
||||||
case LUA_VUSERDATA:
|
case LUA_VUSERDATA:
|
||||||
lua_assert(gco2u(o)->nuvalue > 0);
|
assert(gco2u(o)->nuvalue > 0);
|
||||||
o = gco2u(o)->gclist;
|
o = gco2u(o)->gclist;
|
||||||
break;
|
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;
|
return total;
|
||||||
@@ -569,13 +582,13 @@ static void incifingray (global_State *g, GCObject *o, lu_mem *count) {
|
|||||||
return; /* gray lists not being kept in these phases */
|
return; /* gray lists not being kept in these phases */
|
||||||
if (o->tt == LUA_VUPVAL) {
|
if (o->tt == LUA_VUPVAL) {
|
||||||
/* only open upvalues can be gray */
|
/* 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 */
|
return; /* upvalues are never in gray lists */
|
||||||
}
|
}
|
||||||
/* these are the ones that must be in gray lists */
|
/* these are the ones that must be in gray lists */
|
||||||
if (isgray(o) || getage(o) == G_TOUCHED2) {
|
if (isgray(o) || getage(o) == G_TOUCHED2) {
|
||||||
(*count)++;
|
(*count)++;
|
||||||
lua_assert(testbit(o->marked, TESTBIT));
|
assert(testbit(o->marked, TESTBIT));
|
||||||
resetbit(o->marked, TESTBIT); /* prepare for next cycle */
|
resetbit(o->marked, TESTBIT); /* prepare for next cycle */
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -588,22 +601,22 @@ static lu_mem checklist (global_State *g, int maybedead, int tof,
|
|||||||
for (o = newl; o != survival; o = o->next) {
|
for (o = newl; o != survival; o = o->next) {
|
||||||
checkobject(g, o, maybedead, G_NEW);
|
checkobject(g, o, maybedead, G_NEW);
|
||||||
incifingray(g, o, &total);
|
incifingray(g, o, &total);
|
||||||
lua_assert(!tof == !tofinalize(o));
|
assert(!tof == !tofinalize(o));
|
||||||
}
|
}
|
||||||
for (o = survival; o != old; o = o->next) {
|
for (o = survival; o != old; o = o->next) {
|
||||||
checkobject(g, o, 0, G_SURVIVAL);
|
checkobject(g, o, 0, G_SURVIVAL);
|
||||||
incifingray(g, o, &total);
|
incifingray(g, o, &total);
|
||||||
lua_assert(!tof == !tofinalize(o));
|
assert(!tof == !tofinalize(o));
|
||||||
}
|
}
|
||||||
for (o = old; o != reallyold; o = o->next) {
|
for (o = old; o != reallyold; o = o->next) {
|
||||||
checkobject(g, o, 0, G_OLD1);
|
checkobject(g, o, 0, G_OLD1);
|
||||||
incifingray(g, o, &total);
|
incifingray(g, o, &total);
|
||||||
lua_assert(!tof == !tofinalize(o));
|
assert(!tof == !tofinalize(o));
|
||||||
}
|
}
|
||||||
for (o = reallyold; o != NULL; o = o->next) {
|
for (o = reallyold; o != NULL; o = o->next) {
|
||||||
checkobject(g, o, 0, G_OLD);
|
checkobject(g, o, 0, G_OLD);
|
||||||
incifingray(g, o, &total);
|
incifingray(g, o, &total);
|
||||||
lua_assert(!tof == !tofinalize(o));
|
assert(!tof == !tofinalize(o));
|
||||||
}
|
}
|
||||||
return total;
|
return total;
|
||||||
}
|
}
|
||||||
@@ -616,16 +629,16 @@ int lua_checkmemory (lua_State *L) {
|
|||||||
lu_mem totalin; /* total of objects that are in gray lists */
|
lu_mem totalin; /* total of objects that are in gray lists */
|
||||||
lu_mem totalshould; /* total of objects that should be in gray lists */
|
lu_mem totalshould; /* total of objects that should be in gray lists */
|
||||||
if (keepinvariant(g)) {
|
if (keepinvariant(g)) {
|
||||||
lua_assert(!iswhite(g->mainthread));
|
assert(!iswhite(g->mainthread));
|
||||||
lua_assert(!iswhite(gcvalue(&g->l_registry)));
|
assert(!iswhite(gcvalue(&g->l_registry)));
|
||||||
}
|
}
|
||||||
lua_assert(!isdead(g, gcvalue(&g->l_registry)));
|
assert(!isdead(g, gcvalue(&g->l_registry)));
|
||||||
lua_assert(g->sweepgc == NULL || issweepphase(g));
|
assert(g->sweepgc == NULL || issweepphase(g));
|
||||||
totalin = checkgrays(g);
|
totalin = checkgrays(g);
|
||||||
|
|
||||||
/* check 'fixedgc' list */
|
/* check 'fixedgc' list */
|
||||||
for (o = g->fixedgc; o != NULL; o = o->next) {
|
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 */
|
/* check 'allgc' list */
|
||||||
@@ -641,11 +654,11 @@ int lua_checkmemory (lua_State *L) {
|
|||||||
for (o = g->tobefnz; o != NULL; o = o->next) {
|
for (o = g->tobefnz; o != NULL; o = o->next) {
|
||||||
checkobject(g, o, 0, G_NEW);
|
checkobject(g, o, 0, G_NEW);
|
||||||
incifingray(g, o, &totalshould);
|
incifingray(g, o, &totalshould);
|
||||||
lua_assert(tofinalize(o));
|
assert(tofinalize(o));
|
||||||
lua_assert(o->tt == LUA_VUSERDATA || o->tt == LUA_VTABLE);
|
assert(o->tt == LUA_VUSERDATA || o->tt == LUA_VTABLE);
|
||||||
}
|
}
|
||||||
if (keepinvariant(g))
|
if (keepinvariant(g))
|
||||||
lua_assert(totalin == totalshould);
|
assert(totalin == totalshould);
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -1042,6 +1055,7 @@ static int tref (lua_State *L) {
|
|||||||
luaL_checkany(L, 1);
|
luaL_checkany(L, 1);
|
||||||
lua_pushvalue(L, 1);
|
lua_pushvalue(L, 1);
|
||||||
lua_pushinteger(L, luaL_ref(L, LUA_REGISTRYINDEX));
|
lua_pushinteger(L, luaL_ref(L, LUA_REGISTRYINDEX));
|
||||||
|
(void)level; /* to avoid warnings */
|
||||||
lua_assert(lua_gettop(L) == level+1); /* +1 for result */
|
lua_assert(lua_gettop(L) == level+1); /* +1 for result */
|
||||||
return 1;
|
return 1;
|
||||||
}
|
}
|
||||||
@@ -1049,6 +1063,7 @@ static int tref (lua_State *L) {
|
|||||||
static int getref (lua_State *L) {
|
static int getref (lua_State *L) {
|
||||||
int level = lua_gettop(L);
|
int level = lua_gettop(L);
|
||||||
lua_rawgeti(L, LUA_REGISTRYINDEX, luaL_checkinteger(L, 1));
|
lua_rawgeti(L, LUA_REGISTRYINDEX, luaL_checkinteger(L, 1));
|
||||||
|
(void)level; /* to avoid warnings */
|
||||||
lua_assert(lua_gettop(L) == level+1);
|
lua_assert(lua_gettop(L) == level+1);
|
||||||
return 1;
|
return 1;
|
||||||
}
|
}
|
||||||
@@ -1056,6 +1071,7 @@ static int getref (lua_State *L) {
|
|||||||
static int unref (lua_State *L) {
|
static int unref (lua_State *L) {
|
||||||
int level = lua_gettop(L);
|
int level = lua_gettop(L);
|
||||||
luaL_unref(L, LUA_REGISTRYINDEX, cast_int(luaL_checkinteger(L, 1)));
|
luaL_unref(L, LUA_REGISTRYINDEX, cast_int(luaL_checkinteger(L, 1)));
|
||||||
|
(void)level; /* to avoid warnings */
|
||||||
lua_assert(lua_gettop(L) == level);
|
lua_assert(lua_gettop(L) == level);
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
@@ -1724,6 +1740,7 @@ static struct X { int x; } x;
|
|||||||
else if EQ("tostring") {
|
else if EQ("tostring") {
|
||||||
const char *s = lua_tostring(L1, getindex);
|
const char *s = lua_tostring(L1, getindex);
|
||||||
const char *s1 = lua_pushstring(L1, s);
|
const char *s1 = lua_pushstring(L1, s);
|
||||||
|
(void)s1; /* to avoid warnings */
|
||||||
lua_longassert((s == NULL && s1 == NULL) || strcmp(s, s1) == 0);
|
lua_longassert((s == NULL && s1 == NULL) || strcmp(s, s1) == 0);
|
||||||
}
|
}
|
||||||
else if EQ("type") {
|
else if EQ("type") {
|
||||||
@@ -1752,6 +1769,9 @@ static struct X { int x; } x;
|
|||||||
else if EQ("toclose") {
|
else if EQ("toclose") {
|
||||||
lua_toclose(L1, getnum);
|
lua_toclose(L1, getnum);
|
||||||
}
|
}
|
||||||
|
else if EQ("closeslot") {
|
||||||
|
lua_closeslot(L1, getnum);
|
||||||
|
}
|
||||||
else luaL_error(L, "unknown instruction %s", buff);
|
else luaL_error(L, "unknown instruction %s", buff);
|
||||||
}
|
}
|
||||||
return 0;
|
return 0;
|
||||||
@@ -1937,15 +1957,15 @@ static void checkfinalmem (void) {
|
|||||||
|
|
||||||
int luaB_opentests (lua_State *L) {
|
int luaB_opentests (lua_State *L) {
|
||||||
void *ud;
|
void *ud;
|
||||||
|
lua_Alloc f = lua_getallocf(L, &ud);
|
||||||
lua_atpanic(L, &tpanic);
|
lua_atpanic(L, &tpanic);
|
||||||
lua_setwarnf(L, &warnf, L);
|
lua_setwarnf(L, &warnf, L);
|
||||||
lua_pushboolean(L, 0);
|
lua_pushboolean(L, 0);
|
||||||
lua_setglobal(L, "_WARN"); /* _WARN = false */
|
lua_setglobal(L, "_WARN"); /* _WARN = false */
|
||||||
regcodes(L);
|
regcodes(L);
|
||||||
atexit(checkfinalmem);
|
atexit(checkfinalmem);
|
||||||
lua_assert(lua_getallocf(L, &ud) == debug_realloc);
|
lua_assert(f == debug_realloc && ud == cast_voidp(&l_memcontrol));
|
||||||
lua_assert(ud == cast_voidp(&l_memcontrol));
|
lua_setallocf(L, f, ud); /* exercise this function */
|
||||||
lua_setallocf(L, lua_getallocf(L, NULL), ud);
|
|
||||||
luaL_newlib(L, tests_funcs);
|
luaL_newlib(L, tests_funcs);
|
||||||
return 1;
|
return 1;
|
||||||
}
|
}
|
||||||
|
|||||||
5
ltests.h
5
ltests.h
@@ -130,6 +130,11 @@ LUA_API void *debug_realloc (void *ud, void *block,
|
|||||||
#define LUAI_MAXSTACK 50000
|
#define LUAI_MAXSTACK 50000
|
||||||
|
|
||||||
|
|
||||||
|
/* test mode uses more stack space */
|
||||||
|
#undef LUAI_MAXCCALLS
|
||||||
|
#define LUAI_MAXCCALLS 180
|
||||||
|
|
||||||
|
|
||||||
/* force Lua to use its own implementations */
|
/* force Lua to use its own implementations */
|
||||||
#undef lua_strx2number
|
#undef lua_strx2number
|
||||||
#undef lua_number2strx
|
#undef lua_number2strx
|
||||||
|
|||||||
5
ltm.c
5
ltm.c
@@ -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,
|
void luaT_trybinTM (lua_State *L, const TValue *p1, const TValue *p2,
|
||||||
StkId res, TMS event) {
|
StkId res, TMS event) {
|
||||||
if (!callbinTM(L, p1, p2, res, event)) {
|
if (l_unlikely(!callbinTM(L, p1, p2, res, event))) {
|
||||||
switch (event) {
|
switch (event) {
|
||||||
case TM_BAND: case TM_BOR: case TM_BXOR:
|
case TM_BAND: case TM_BOR: case TM_BXOR:
|
||||||
case TM_SHL: case TM_SHR: case TM_BNOT: {
|
case TM_SHL: case TM_SHR: case TM_BNOT: {
|
||||||
@@ -166,7 +166,8 @@ void luaT_trybinTM (lua_State *L, const TValue *p1, const TValue *p2,
|
|||||||
|
|
||||||
void luaT_tryconcatTM (lua_State *L) {
|
void luaT_tryconcatTM (lua_State *L) {
|
||||||
StkId top = L->top;
|
StkId top = L->top;
|
||||||
if (!callbinTM(L, s2v(top - 2), s2v(top - 1), top - 2, TM_CONCAT))
|
if (l_unlikely(!callbinTM(L, s2v(top - 2), s2v(top - 1), top - 2,
|
||||||
|
TM_CONCAT)))
|
||||||
luaG_concaterror(L, s2v(top - 2), s2v(top - 1));
|
luaG_concaterror(L, s2v(top - 2), s2v(top - 1));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
26
lua.c
26
lua.c
@@ -37,6 +37,26 @@ static lua_State *globalL = NULL;
|
|||||||
static const char *progname = LUA_PROGNAME;
|
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.
|
** 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) {
|
static void laction (int i) {
|
||||||
int flag = LUA_MASKCALL | LUA_MASKRET | LUA_MASKLINE | LUA_MASKCOUNT;
|
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);
|
lua_sethook(globalL, lstop, flag, 1);
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -135,9 +155,9 @@ static int docall (lua_State *L, int narg, int nres) {
|
|||||||
lua_pushcfunction(L, msghandler); /* push message handler */
|
lua_pushcfunction(L, msghandler); /* push message handler */
|
||||||
lua_insert(L, base); /* put it under function and args */
|
lua_insert(L, base); /* put it under function and args */
|
||||||
globalL = L; /* to be available to 'laction' */
|
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);
|
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 */
|
lua_remove(L, base); /* remove message handler from the stack */
|
||||||
return status;
|
return status;
|
||||||
}
|
}
|
||||||
|
|||||||
9
lua.h
9
lua.h
@@ -18,14 +18,14 @@
|
|||||||
|
|
||||||
#define LUA_VERSION_MAJOR "5"
|
#define LUA_VERSION_MAJOR "5"
|
||||||
#define LUA_VERSION_MINOR "4"
|
#define LUA_VERSION_MINOR "4"
|
||||||
#define LUA_VERSION_RELEASE "2"
|
#define LUA_VERSION_RELEASE "3"
|
||||||
|
|
||||||
#define LUA_VERSION_NUM 504
|
#define LUA_VERSION_NUM 504
|
||||||
#define LUA_VERSION_RELEASE_NUM (LUA_VERSION_NUM * 100 + 0)
|
#define LUA_VERSION_RELEASE_NUM (LUA_VERSION_NUM * 100 + 0)
|
||||||
|
|
||||||
#define LUA_VERSION "Lua " LUA_VERSION_MAJOR "." LUA_VERSION_MINOR
|
#define LUA_VERSION "Lua " LUA_VERSION_MAJOR "." LUA_VERSION_MINOR
|
||||||
#define LUA_RELEASE LUA_VERSION "." LUA_VERSION_RELEASE
|
#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-2021 Lua.org, PUC-Rio"
|
||||||
#define LUA_AUTHORS "R. Ierusalimschy, L. H. de Figueiredo, W. Celes"
|
#define LUA_AUTHORS "R. Ierusalimschy, L. H. de Figueiredo, W. Celes"
|
||||||
|
|
||||||
|
|
||||||
@@ -347,7 +347,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 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_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);
|
||||||
|
|
||||||
|
|
||||||
/*
|
/*
|
||||||
@@ -491,7 +492,7 @@ struct lua_Debug {
|
|||||||
|
|
||||||
|
|
||||||
/******************************************************************************
|
/******************************************************************************
|
||||||
* Copyright (C) 1994-2020 Lua.org, PUC-Rio.
|
* Copyright (C) 1994-2021 Lua.org, PUC-Rio.
|
||||||
*
|
*
|
||||||
* Permission is hereby granted, free of charge, to any person obtaining
|
* Permission is hereby granted, free of charge, to any person obtaining
|
||||||
* a copy of this software and associated documentation files (the
|
* a copy of this software and associated documentation files (the
|
||||||
|
|||||||
113
luaconf.h
113
luaconf.h
@@ -16,13 +16,13 @@
|
|||||||
** ===================================================================
|
** ===================================================================
|
||||||
** General Configuration File for Lua
|
** General Configuration File for Lua
|
||||||
**
|
**
|
||||||
** Some definitions here can be changed externally, through the
|
** Some definitions here can be changed externally, through the compiler
|
||||||
** compiler (e.g., with '-D' options). Those are protected by
|
** (e.g., with '-D' options): They are commented out or protected
|
||||||
** '#if !defined' guards. However, several other definitions should
|
** by '#if !defined' guards. However, several other definitions
|
||||||
** be changed directly here, either because they affect the Lua
|
** should be changed directly here, either because they affect the
|
||||||
** ABI (by making the changes here, you ensure that all software
|
** Lua ABI (by making the changes here, you ensure that all software
|
||||||
** connected to Lua, such as C libraries, will be compiled with the
|
** connected to Lua, such as C libraries, will be compiled with the same
|
||||||
** same configuration); or because they are seldom changed.
|
** configuration); or because they are seldom changed.
|
||||||
**
|
**
|
||||||
** Search for "@@" to find all configurable definitions.
|
** Search for "@@" to find all configurable definitions.
|
||||||
** ===================================================================
|
** ===================================================================
|
||||||
@@ -81,26 +81,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_INT_TYPE defines the type for Lua integers.
|
||||||
@@ LUA_FLOAT_TYPE defines the type for Lua floats.
|
@@ LUA_FLOAT_TYPE defines the type for Lua floats.
|
||||||
@@ -121,7 +107,31 @@
|
|||||||
#define LUA_FLOAT_DOUBLE 2
|
#define LUA_FLOAT_DOUBLE 2
|
||||||
#define LUA_FLOAT_LONGDOUBLE 3
|
#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'
|
** 32-bit integers and 'float'
|
||||||
*/
|
*/
|
||||||
@@ -132,27 +142,22 @@
|
|||||||
#endif
|
#endif
|
||||||
#define LUA_FLOAT_TYPE LUA_FLOAT_FLOAT
|
#define LUA_FLOAT_TYPE LUA_FLOAT_FLOAT
|
||||||
|
|
||||||
#elif defined(LUA_C89_NUMBERS) /* }{ */
|
#elif LUA_C89_NUMBERS /* }{ */
|
||||||
/*
|
/*
|
||||||
** largest types available for C89 ('long' and 'double')
|
** largest types available for C89 ('long' and 'double')
|
||||||
*/
|
*/
|
||||||
#define LUA_INT_TYPE LUA_INT_LONG
|
#define LUA_INT_TYPE LUA_INT_LONG
|
||||||
#define LUA_FLOAT_TYPE LUA_FLOAT_DOUBLE
|
#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 /* } */
|
#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
|
|
||||||
|
|
||||||
/* }================================================================== */
|
/* }================================================================== */
|
||||||
|
|
||||||
|
|
||||||
@@ -373,14 +378,13 @@
|
|||||||
|
|
||||||
/*
|
/*
|
||||||
** {==================================================================
|
** {==================================================================
|
||||||
** Configuration for Numbers.
|
** Configuration for Numbers (low-level part).
|
||||||
** Change these definitions if no predefined LUA_FLOAT_* / LUA_INT_*
|
** Change these definitions if no predefined LUA_FLOAT_* / LUA_INT_*
|
||||||
** satisfy your needs.
|
** satisfy your needs.
|
||||||
** ===================================================================
|
** ===================================================================
|
||||||
*/
|
*/
|
||||||
|
|
||||||
/*
|
/*
|
||||||
@@ LUA_NUMBER is the floating-point type used by Lua.
|
|
||||||
@@ LUAI_UACNUMBER is the result of a 'default argument promotion'
|
@@ LUAI_UACNUMBER is the result of a 'default argument promotion'
|
||||||
@@ over a floating number.
|
@@ over a floating number.
|
||||||
@@ l_floatatt(x) corrects float attribute 'x' to the proper float type
|
@@ l_floatatt(x) corrects float attribute 'x' to the proper float type
|
||||||
@@ -473,10 +477,7 @@
|
|||||||
|
|
||||||
|
|
||||||
/*
|
/*
|
||||||
@@ LUA_INTEGER is the integer type used by Lua.
|
|
||||||
**
|
|
||||||
@@ LUA_UNSIGNED is the unsigned version of LUA_INTEGER.
|
@@ LUA_UNSIGNED is the unsigned version of LUA_INTEGER.
|
||||||
**
|
|
||||||
@@ LUAI_UACINT is the result of a 'default argument promotion'
|
@@ LUAI_UACINT is the result of a 'default argument promotion'
|
||||||
@@ over a LUA_INTEGER.
|
@@ over a LUA_INTEGER.
|
||||||
@@ LUA_INTEGER_FRMLEN is the length modifier for reading/writing integers.
|
@@ LUA_INTEGER_FRMLEN is the length modifier for reading/writing integers.
|
||||||
@@ -659,6 +660,34 @@
|
|||||||
#define lua_getlocaledecpoint() (localeconv()->decimal_point[0])
|
#define lua_getlocaledecpoint() (localeconv()->decimal_point[0])
|
||||||
#endif
|
#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
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
/* }================================================================== */
|
/* }================================================================== */
|
||||||
|
|
||||||
|
|
||||||
|
|||||||
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);
|
LUALIB_API void (luaL_openlibs) (lua_State *L);
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
#if !defined(lua_assert)
|
|
||||||
#define lua_assert(x) ((void)0)
|
|
||||||
#endif
|
|
||||||
|
|
||||||
|
|
||||||
#endif
|
#endif
|
||||||
|
|||||||
73
lvm.c
73
lvm.c
@@ -235,11 +235,11 @@ static int forprep (lua_State *L, StkId ra) {
|
|||||||
}
|
}
|
||||||
else { /* try making all values floats */
|
else { /* try making all values floats */
|
||||||
lua_Number init; lua_Number limit; lua_Number step;
|
lua_Number init; lua_Number limit; lua_Number step;
|
||||||
if (unlikely(!tonumber(plimit, &limit)))
|
if (l_unlikely(!tonumber(plimit, &limit)))
|
||||||
luaG_forerror(L, plimit, "limit");
|
luaG_forerror(L, plimit, "limit");
|
||||||
if (unlikely(!tonumber(pstep, &step)))
|
if (l_unlikely(!tonumber(pstep, &step)))
|
||||||
luaG_forerror(L, pstep, "step");
|
luaG_forerror(L, pstep, "step");
|
||||||
if (unlikely(!tonumber(pinit, &init)))
|
if (l_unlikely(!tonumber(pinit, &init)))
|
||||||
luaG_forerror(L, pinit, "initial value");
|
luaG_forerror(L, pinit, "initial value");
|
||||||
if (step == 0)
|
if (step == 0)
|
||||||
luaG_runerror(L, "'for' step is zero");
|
luaG_runerror(L, "'for' step is zero");
|
||||||
@@ -292,7 +292,7 @@ void luaV_finishget (lua_State *L, const TValue *t, TValue *key, StkId val,
|
|||||||
if (slot == NULL) { /* 't' is not a table? */
|
if (slot == NULL) { /* 't' is not a table? */
|
||||||
lua_assert(!ttistable(t));
|
lua_assert(!ttistable(t));
|
||||||
tm = luaT_gettmbyobj(L, t, TM_INDEX);
|
tm = luaT_gettmbyobj(L, t, TM_INDEX);
|
||||||
if (unlikely(notm(tm)))
|
if (l_unlikely(notm(tm)))
|
||||||
luaG_typeerror(L, t, "index"); /* no metamethod */
|
luaG_typeerror(L, t, "index"); /* no metamethod */
|
||||||
/* else will try the metamethod */
|
/* else will try the metamethod */
|
||||||
}
|
}
|
||||||
@@ -337,10 +337,7 @@ void luaV_finishset (lua_State *L, const TValue *t, TValue *key,
|
|||||||
lua_assert(isempty(slot)); /* slot must be empty */
|
lua_assert(isempty(slot)); /* slot must be empty */
|
||||||
tm = fasttm(L, h->metatable, TM_NEWINDEX); /* get metamethod */
|
tm = fasttm(L, h->metatable, TM_NEWINDEX); /* get metamethod */
|
||||||
if (tm == NULL) { /* no metamethod? */
|
if (tm == NULL) { /* no metamethod? */
|
||||||
if (isabstkey(slot)) /* no previous entry? */
|
luaH_finishset(L, h, key, slot, val); /* set new value */
|
||||||
slot = luaH_newkey(L, h, key); /* create one */
|
|
||||||
/* no metamethod and (now) there is an entry with given key */
|
|
||||||
setobj2t(L, cast(TValue *, slot), val); /* set its new value */
|
|
||||||
invalidateTMcache(h);
|
invalidateTMcache(h);
|
||||||
luaC_barrierback(L, obj2gco(h), val);
|
luaC_barrierback(L, obj2gco(h), val);
|
||||||
return;
|
return;
|
||||||
@@ -349,7 +346,7 @@ void luaV_finishset (lua_State *L, const TValue *t, TValue *key,
|
|||||||
}
|
}
|
||||||
else { /* not a table; check metamethod */
|
else { /* not a table; check metamethod */
|
||||||
tm = luaT_gettmbyobj(L, t, TM_NEWINDEX);
|
tm = luaT_gettmbyobj(L, t, TM_NEWINDEX);
|
||||||
if (unlikely(notm(tm)))
|
if (l_unlikely(notm(tm)))
|
||||||
luaG_typeerror(L, t, "index");
|
luaG_typeerror(L, t, "index");
|
||||||
}
|
}
|
||||||
/* try the metamethod */
|
/* try the metamethod */
|
||||||
@@ -571,8 +568,13 @@ int luaV_equalobj (lua_State *L, const TValue *t1, const TValue *t2) {
|
|||||||
if (ttype(t1) != ttype(t2) || ttype(t1) != LUA_TNUMBER)
|
if (ttype(t1) != ttype(t2) || ttype(t1) != LUA_TNUMBER)
|
||||||
return 0; /* only numbers can be equal with different variants */
|
return 0; /* only numbers can be equal with different variants */
|
||||||
else { /* two numbers with different variants */
|
else { /* two numbers with different variants */
|
||||||
lua_Integer i1, i2; /* compare them as integers */
|
/* One of them is an integer. If the other does not have an
|
||||||
return (tointegerns(t1, &i1) && tointegerns(t2, &i2) && i1 == i2);
|
integer value, they cannot be equal; otherwise, compare their
|
||||||
|
integer values. */
|
||||||
|
lua_Integer i1, i2;
|
||||||
|
return (luaV_tointegerns(t1, &i1, F2Ieq) &&
|
||||||
|
luaV_tointegerns(t2, &i2, F2Ieq) &&
|
||||||
|
i1 == i2);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
/* values have same type and same variant */
|
/* values have same type and same variant */
|
||||||
@@ -654,7 +656,7 @@ void luaV_concat (lua_State *L, int total) {
|
|||||||
/* collect total length and number of strings */
|
/* collect total length and number of strings */
|
||||||
for (n = 1; n < total && tostring(L, s2v(top - n - 1)); n++) {
|
for (n = 1; n < total && tostring(L, s2v(top - n - 1)); n++) {
|
||||||
size_t l = vslen(s2v(top - n - 1));
|
size_t l = vslen(s2v(top - n - 1));
|
||||||
if (unlikely(l >= (MAX_SIZE/sizeof(char)) - tl))
|
if (l_unlikely(l >= (MAX_SIZE/sizeof(char)) - tl))
|
||||||
luaG_runerror(L, "string length overflow");
|
luaG_runerror(L, "string length overflow");
|
||||||
tl += l;
|
tl += l;
|
||||||
}
|
}
|
||||||
@@ -698,7 +700,7 @@ void luaV_objlen (lua_State *L, StkId ra, const TValue *rb) {
|
|||||||
}
|
}
|
||||||
default: { /* try metamethod */
|
default: { /* try metamethod */
|
||||||
tm = luaT_gettmbyobj(L, rb, TM_LEN);
|
tm = luaT_gettmbyobj(L, rb, TM_LEN);
|
||||||
if (unlikely(notm(tm))) /* no metamethod? */
|
if (l_unlikely(notm(tm))) /* no metamethod? */
|
||||||
luaG_typeerror(L, rb, "get length of");
|
luaG_typeerror(L, rb, "get length of");
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
@@ -714,7 +716,7 @@ void luaV_objlen (lua_State *L, StkId ra, const TValue *rb) {
|
|||||||
** otherwise 'floor(q) == trunc(q) - 1'.
|
** otherwise 'floor(q) == trunc(q) - 1'.
|
||||||
*/
|
*/
|
||||||
lua_Integer luaV_idiv (lua_State *L, lua_Integer m, lua_Integer n) {
|
lua_Integer luaV_idiv (lua_State *L, lua_Integer m, lua_Integer n) {
|
||||||
if (unlikely(l_castS2U(n) + 1u <= 1u)) { /* special cases: -1 or 0 */
|
if (l_unlikely(l_castS2U(n) + 1u <= 1u)) { /* special cases: -1 or 0 */
|
||||||
if (n == 0)
|
if (n == 0)
|
||||||
luaG_runerror(L, "attempt to divide by zero");
|
luaG_runerror(L, "attempt to divide by zero");
|
||||||
return intop(-, 0, m); /* n==-1; avoid overflow with 0x80000...//-1 */
|
return intop(-, 0, m); /* n==-1; avoid overflow with 0x80000...//-1 */
|
||||||
@@ -734,7 +736,7 @@ lua_Integer luaV_idiv (lua_State *L, lua_Integer m, lua_Integer n) {
|
|||||||
** about luaV_idiv.)
|
** about luaV_idiv.)
|
||||||
*/
|
*/
|
||||||
lua_Integer luaV_mod (lua_State *L, lua_Integer m, lua_Integer n) {
|
lua_Integer luaV_mod (lua_State *L, lua_Integer m, lua_Integer n) {
|
||||||
if (unlikely(l_castS2U(n) + 1u <= 1u)) { /* special cases: -1 or 0 */
|
if (l_unlikely(l_castS2U(n) + 1u <= 1u)) { /* special cases: -1 or 0 */
|
||||||
if (n == 0)
|
if (n == 0)
|
||||||
luaG_runerror(L, "attempt to perform 'n%%0'");
|
luaG_runerror(L, "attempt to perform 'n%%0'");
|
||||||
return 0; /* m % -1 == 0; avoid overflow with 0x80000...%-1 */
|
return 0; /* m % -1 == 0; avoid overflow with 0x80000...%-1 */
|
||||||
@@ -845,6 +847,10 @@ void luaV_finishOp (lua_State *L) {
|
|||||||
luaV_concat(L, total); /* concat them (may yield again) */
|
luaV_concat(L, total); /* concat them (may yield again) */
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
case OP_CLOSE: case OP_RETURN: { /* yielded closing variables */
|
||||||
|
ci->u.l.savedpc--; /* repeat instruction to close other vars. */
|
||||||
|
break;
|
||||||
|
}
|
||||||
default: {
|
default: {
|
||||||
/* only these other opcodes can yield */
|
/* only these other opcodes can yield */
|
||||||
lua_assert(op == OP_TFORCALL || op == OP_CALL ||
|
lua_assert(op == OP_TFORCALL || op == OP_CALL ||
|
||||||
@@ -920,7 +926,7 @@ void luaV_finishOp (lua_State *L) {
|
|||||||
*/
|
*/
|
||||||
#define op_arithfK(L,fop) { \
|
#define op_arithfK(L,fop) { \
|
||||||
TValue *v1 = vRB(i); \
|
TValue *v1 = vRB(i); \
|
||||||
TValue *v2 = KC(i); \
|
TValue *v2 = KC(i); lua_assert(ttisnumber(v2)); \
|
||||||
op_arithf_aux(L, v1, v2, fop); }
|
op_arithf_aux(L, v1, v2, fop); }
|
||||||
|
|
||||||
|
|
||||||
@@ -949,7 +955,7 @@ void luaV_finishOp (lua_State *L) {
|
|||||||
*/
|
*/
|
||||||
#define op_arithK(L,iop,fop) { \
|
#define op_arithK(L,iop,fop) { \
|
||||||
TValue *v1 = vRB(i); \
|
TValue *v1 = vRB(i); \
|
||||||
TValue *v2 = KC(i); \
|
TValue *v2 = KC(i); lua_assert(ttisnumber(v2)); \
|
||||||
op_arith_aux(L, v1, v2, iop, fop); }
|
op_arith_aux(L, v1, v2, iop, fop); }
|
||||||
|
|
||||||
|
|
||||||
@@ -1048,7 +1054,8 @@ void luaV_finishOp (lua_State *L) {
|
|||||||
#define updatebase(ci) (base = ci->func + 1)
|
#define updatebase(ci) (base = ci->func + 1)
|
||||||
|
|
||||||
|
|
||||||
#define updatestack(ci) { if (trap) { updatebase(ci); ra = RA(i); } }
|
#define updatestack(ci) \
|
||||||
|
{ if (l_unlikely(trap)) { updatebase(ci); ra = RA(i); } }
|
||||||
|
|
||||||
|
|
||||||
/*
|
/*
|
||||||
@@ -1106,7 +1113,7 @@ void luaV_finishOp (lua_State *L) {
|
|||||||
|
|
||||||
/* fetch an instruction and prepare its execution */
|
/* fetch an instruction and prepare its execution */
|
||||||
#define vmfetch() { \
|
#define vmfetch() { \
|
||||||
if (trap) { /* stack reallocation or hooks? */ \
|
if (l_unlikely(trap)) { /* stack reallocation or hooks? */ \
|
||||||
trap = luaG_traceexec(L, pc); /* handle hooks */ \
|
trap = luaG_traceexec(L, pc); /* handle hooks */ \
|
||||||
updatebase(ci); /* correct stack */ \
|
updatebase(ci); /* correct stack */ \
|
||||||
} \
|
} \
|
||||||
@@ -1134,7 +1141,7 @@ void luaV_execute (lua_State *L, CallInfo *ci) {
|
|||||||
cl = clLvalue(s2v(ci->func));
|
cl = clLvalue(s2v(ci->func));
|
||||||
k = cl->p->k;
|
k = cl->p->k;
|
||||||
pc = ci->u.l.savedpc;
|
pc = ci->u.l.savedpc;
|
||||||
if (trap) {
|
if (l_unlikely(trap)) {
|
||||||
if (pc == cl->p->code) { /* first instruction (not resuming)? */
|
if (pc == cl->p->code) { /* first instruction (not resuming)? */
|
||||||
if (cl->p->is_vararg)
|
if (cl->p->is_vararg)
|
||||||
trap = 0; /* hooks will start after VARARGPREP instruction */
|
trap = 0; /* hooks will start after VARARGPREP instruction */
|
||||||
@@ -1149,6 +1156,8 @@ void luaV_execute (lua_State *L, CallInfo *ci) {
|
|||||||
Instruction i; /* instruction being executed */
|
Instruction i; /* instruction being executed */
|
||||||
StkId ra; /* instruction's A register */
|
StkId ra; /* instruction's A register */
|
||||||
vmfetch();
|
vmfetch();
|
||||||
|
// low-level line tracing for debugging Lua
|
||||||
|
// printf("line: %d\n", luaG_getfuncline(cl->p, pcRel(pc, cl->p)));
|
||||||
lua_assert(base == ci->func + 1);
|
lua_assert(base == ci->func + 1);
|
||||||
lua_assert(base <= L->top && L->top < L->stack_last);
|
lua_assert(base <= L->top && L->top < L->stack_last);
|
||||||
/* invalidate top for instructions not expecting it */
|
/* invalidate top for instructions not expecting it */
|
||||||
@@ -1527,7 +1536,7 @@ void luaV_execute (lua_State *L, CallInfo *ci) {
|
|||||||
vmbreak;
|
vmbreak;
|
||||||
}
|
}
|
||||||
vmcase(OP_CLOSE) {
|
vmcase(OP_CLOSE) {
|
||||||
Protect(luaF_close(L, ra, LUA_OK));
|
Protect(luaF_close(L, ra, LUA_OK, 1));
|
||||||
vmbreak;
|
vmbreak;
|
||||||
}
|
}
|
||||||
vmcase(OP_TBC) {
|
vmcase(OP_TBC) {
|
||||||
@@ -1632,10 +1641,8 @@ void luaV_execute (lua_State *L, CallInfo *ci) {
|
|||||||
b = cast_int(L->top - ra);
|
b = cast_int(L->top - ra);
|
||||||
savepc(ci); /* several calls here can raise errors */
|
savepc(ci); /* several calls here can raise errors */
|
||||||
if (TESTARG_k(i)) {
|
if (TESTARG_k(i)) {
|
||||||
/* close upvalues from current call; the compiler ensures
|
luaF_closeupval(L, base); /* close upvalues from current call */
|
||||||
that there are no to-be-closed variables here, so this
|
lua_assert(L->tbclist < base); /* no pending tbc variables */
|
||||||
call cannot change the stack */
|
|
||||||
luaF_close(L, base, NOCLOSINGMETH);
|
|
||||||
lua_assert(base == ci->func + 1);
|
lua_assert(base == ci->func + 1);
|
||||||
}
|
}
|
||||||
while (!ttisfunction(s2v(ra))) { /* not a function? */
|
while (!ttisfunction(s2v(ra))) { /* not a function? */
|
||||||
@@ -1665,7 +1672,7 @@ void luaV_execute (lua_State *L, CallInfo *ci) {
|
|||||||
if (TESTARG_k(i)) { /* may there be open upvalues? */
|
if (TESTARG_k(i)) { /* may there be open upvalues? */
|
||||||
if (L->top < ci->top)
|
if (L->top < ci->top)
|
||||||
L->top = ci->top;
|
L->top = ci->top;
|
||||||
luaF_close(L, base, LUA_OK);
|
luaF_close(L, base, CLOSEKTOP, 1);
|
||||||
updatetrap(ci);
|
updatetrap(ci);
|
||||||
updatestack(ci);
|
updatestack(ci);
|
||||||
}
|
}
|
||||||
@@ -1677,23 +1684,23 @@ void luaV_execute (lua_State *L, CallInfo *ci) {
|
|||||||
goto ret;
|
goto ret;
|
||||||
}
|
}
|
||||||
vmcase(OP_RETURN0) {
|
vmcase(OP_RETURN0) {
|
||||||
if (L->hookmask) {
|
if (l_unlikely(L->hookmask)) {
|
||||||
L->top = ra;
|
L->top = ra;
|
||||||
savepc(ci);
|
savepc(ci);
|
||||||
luaD_poscall(L, ci, 0); /* no hurry... */
|
luaD_poscall(L, ci, 0); /* no hurry... */
|
||||||
trap = 1;
|
trap = 1;
|
||||||
}
|
}
|
||||||
else { /* do the 'poscall' here */
|
else { /* do the 'poscall' here */
|
||||||
int nres = ci->nresults;
|
int nres;
|
||||||
L->ci = ci->previous; /* back to caller */
|
L->ci = ci->previous; /* back to caller */
|
||||||
L->top = base - 1;
|
L->top = base - 1;
|
||||||
while (nres-- > 0)
|
for (nres = ci->nresults; l_unlikely(nres > 0); nres--)
|
||||||
setnilvalue(s2v(L->top++)); /* all results are nil */
|
setnilvalue(s2v(L->top++)); /* all results are nil */
|
||||||
}
|
}
|
||||||
goto ret;
|
goto ret;
|
||||||
}
|
}
|
||||||
vmcase(OP_RETURN1) {
|
vmcase(OP_RETURN1) {
|
||||||
if (L->hookmask) {
|
if (l_unlikely(L->hookmask)) {
|
||||||
L->top = ra + 1;
|
L->top = ra + 1;
|
||||||
savepc(ci);
|
savepc(ci);
|
||||||
luaD_poscall(L, ci, 1); /* no hurry... */
|
luaD_poscall(L, ci, 1); /* no hurry... */
|
||||||
@@ -1707,8 +1714,8 @@ void luaV_execute (lua_State *L, CallInfo *ci) {
|
|||||||
else {
|
else {
|
||||||
setobjs2s(L, base - 1, ra); /* at least this result */
|
setobjs2s(L, base - 1, ra); /* at least this result */
|
||||||
L->top = base;
|
L->top = base;
|
||||||
while (--nres > 0) /* complete missing results */
|
for (; l_unlikely(nres > 1); nres--)
|
||||||
setnilvalue(s2v(L->top++));
|
setnilvalue(s2v(L->top++)); /* complete missing results */
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
ret: /* return from a Lua function */
|
ret: /* return from a Lua function */
|
||||||
@@ -1811,7 +1818,7 @@ void luaV_execute (lua_State *L, CallInfo *ci) {
|
|||||||
}
|
}
|
||||||
vmcase(OP_VARARGPREP) {
|
vmcase(OP_VARARGPREP) {
|
||||||
ProtectNT(luaT_adjustvarargs(L, GETARG_A(i), ci, cl->p));
|
ProtectNT(luaT_adjustvarargs(L, GETARG_A(i), ci, cl->p));
|
||||||
if (trap) {
|
if (l_unlikely(trap)) { /* previous "Protect" updated trap */
|
||||||
luaD_hookcall(L, ci);
|
luaD_hookcall(L, ci);
|
||||||
L->oldpc = 1; /* next opcode will be seen as a "new" line */
|
L->oldpc = 1; /* next opcode will be seen as a "new" line */
|
||||||
}
|
}
|
||||||
|
|||||||
6
lvm.h
6
lvm.h
@@ -60,12 +60,14 @@ typedef enum {
|
|||||||
|
|
||||||
/* convert an object to an integer (including string coercion) */
|
/* convert an object to an integer (including string coercion) */
|
||||||
#define tointeger(o,i) \
|
#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) */
|
/* convert an object to an integer (without string coercion) */
|
||||||
#define tointegerns(o,i) \
|
#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))
|
#define intop(op,v1,v2) l_castU2S(l_castS2U(v1) op l_castS2U(v2))
|
||||||
|
|||||||
13
makefile
13
makefile
@@ -5,7 +5,7 @@
|
|||||||
|
|
||||||
# Warnings valid for both C and C++
|
# Warnings valid for both C and C++
|
||||||
CWARNSCPP= \
|
CWARNSCPP= \
|
||||||
-fmax-errors=5 \
|
-Wfatal-errors \
|
||||||
-Wextra \
|
-Wextra \
|
||||||
-Wshadow \
|
-Wshadow \
|
||||||
-Wsign-compare \
|
-Wsign-compare \
|
||||||
@@ -14,8 +14,6 @@ CWARNSCPP= \
|
|||||||
-Wredundant-decls \
|
-Wredundant-decls \
|
||||||
-Wdisabled-optimization \
|
-Wdisabled-optimization \
|
||||||
-Wdouble-promotion \
|
-Wdouble-promotion \
|
||||||
-Wlogical-op \
|
|
||||||
-Wno-aggressive-loop-optimizations \
|
|
||||||
# the next warnings might be useful sometimes,
|
# the next warnings might be useful sometimes,
|
||||||
# but usually they generate too much noise
|
# but usually they generate too much noise
|
||||||
# -Werror \
|
# -Werror \
|
||||||
@@ -26,6 +24,13 @@ CWARNSCPP= \
|
|||||||
# -Wformat=2 \
|
# -Wformat=2 \
|
||||||
# -Wcast-qual \
|
# -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++
|
# The next warnings are neither valid nor needed for C++
|
||||||
CWARNSC= -Wdeclaration-after-statement \
|
CWARNSC= -Wdeclaration-after-statement \
|
||||||
-Wmissing-prototypes \
|
-Wmissing-prototypes \
|
||||||
@@ -35,7 +40,7 @@ CWARNSC= -Wdeclaration-after-statement \
|
|||||||
-Wold-style-definition \
|
-Wold-style-definition \
|
||||||
|
|
||||||
|
|
||||||
CWARNS= $(CWARNSCPP) $(CWARNSC)
|
CWARNS= $(CWARNSCPP) $(CWARNSC) $(CWARNGCC)
|
||||||
|
|
||||||
# Some useful compiler options for internal tests:
|
# Some useful compiler options for internal tests:
|
||||||
# -DLUAI_ASSERT turns on all assertions inside Lua.
|
# -DLUAI_ASSERT turns on all assertions inside Lua.
|
||||||
|
|||||||
@@ -30,7 +30,7 @@ by Roberto Ierusalimschy, Luiz Henrique de Figueiredo, Waldemar Celes
|
|||||||
<p>
|
<p>
|
||||||
<small>
|
<small>
|
||||||
<a href="http://www.lua.org/copyright.html">Copyright</a>
|
<a href="http://www.lua.org/copyright.html">Copyright</a>
|
||||||
© 2020 Lua.org, PUC-Rio. All rights reserved.
|
© 2021 Lua.org, PUC-Rio. All rights reserved.
|
||||||
</small>
|
</small>
|
||||||
<hr>
|
<hr>
|
||||||
|
|
||||||
|
|||||||
@@ -88,6 +88,11 @@ The type @emph{boolean} has two values, @false and @true.
|
|||||||
Both @nil and @false make a condition false;
|
Both @nil and @false make a condition false;
|
||||||
they are collectively called @def{false values}.
|
they are collectively called @def{false values}.
|
||||||
Any other value makes a condition true.
|
Any other value makes a condition true.
|
||||||
|
Despite its name,
|
||||||
|
@false is frequently used as an alternative to @nil,
|
||||||
|
with the key difference that @false behaves
|
||||||
|
like a regular value in a table,
|
||||||
|
while a @nil in a table represents an absent key.
|
||||||
|
|
||||||
The type @emph{number} represents both
|
The type @emph{number} represents both
|
||||||
integer numbers and real (floating-point) numbers,
|
integer numbers and real (floating-point) numbers,
|
||||||
@@ -326,7 +331,7 @@ under certain events.
|
|||||||
You can change several aspects of the behavior
|
You can change several aspects of the behavior
|
||||||
of a value by setting specific fields in its metatable.
|
of a value by setting specific fields in its metatable.
|
||||||
For instance, when a non-numeric value is the operand of an addition,
|
For instance, when a non-numeric value is the operand of an addition,
|
||||||
Lua checks for a function in the field @St{__add} of the value's metatable.
|
Lua checks for a function in the field @idx{__add} of the value's metatable.
|
||||||
If it finds one,
|
If it finds one,
|
||||||
Lua calls this function to perform the addition.
|
Lua calls this function to perform the addition.
|
||||||
|
|
||||||
@@ -339,7 +344,7 @@ In the previous example, the key is the string @St{__add}
|
|||||||
and the metamethod is the function that performs the addition.
|
and the metamethod is the function that performs the addition.
|
||||||
Unless stated otherwise,
|
Unless stated otherwise,
|
||||||
a metamethod may in fact be any @x{callable value},
|
a metamethod may in fact be any @x{callable value},
|
||||||
which is either a function or a value with a @id{__call} metamethod.
|
which is either a function or a value with a @idx{__call} metamethod.
|
||||||
|
|
||||||
You can query the metatable of any value
|
You can query the metatable of any value
|
||||||
using the @Lid{getmetatable} function.
|
using the @Lid{getmetatable} function.
|
||||||
@@ -500,7 +505,7 @@ when @id{key} is not present in @id{table}.
|
|||||||
The metavalue is looked up in the metatable of @id{table}.
|
The metavalue is looked up in the metatable of @id{table}.
|
||||||
|
|
||||||
The metavalue for this event can be either a function, a table,
|
The metavalue for this event can be either a function, a table,
|
||||||
or any value with an @id{__index} metavalue.
|
or any value with an @idx{__index} metavalue.
|
||||||
If it is a function,
|
If it is a function,
|
||||||
it is called with @id{table} and @id{key} as arguments,
|
it is called with @id{table} and @id{key} as arguments,
|
||||||
and the result of the call
|
and the result of the call
|
||||||
@@ -509,7 +514,7 @@ is the result of the operation.
|
|||||||
Otherwise,
|
Otherwise,
|
||||||
the final result is the result of indexing this metavalue with @id{key}.
|
the final result is the result of indexing this metavalue with @id{key}.
|
||||||
This indexing is regular, not raw,
|
This indexing is regular, not raw,
|
||||||
and therefore can trigger another @id{__index} metavalue.
|
and therefore can trigger another @idx{__index} metavalue.
|
||||||
}
|
}
|
||||||
|
|
||||||
@item{@idx{__newindex}|
|
@item{@idx{__newindex}|
|
||||||
@@ -521,14 +526,14 @@ The metavalue is looked up in the metatable of @id{table}.
|
|||||||
|
|
||||||
Like with indexing,
|
Like with indexing,
|
||||||
the metavalue for this event can be either a function, a table,
|
the metavalue for this event can be either a function, a table,
|
||||||
or any value with an @id{__newindex} metavalue.
|
or any value with an @idx{__newindex} metavalue.
|
||||||
If it is a function,
|
If it is a function,
|
||||||
it is called with @id{table}, @id{key}, and @id{value} as arguments.
|
it is called with @id{table}, @id{key}, and @id{value} as arguments.
|
||||||
Otherwise,
|
Otherwise,
|
||||||
Lua repeats the indexing assignment over this metavalue
|
Lua repeats the indexing assignment over this metavalue
|
||||||
with the same key and value.
|
with the same key and value.
|
||||||
This assignment is regular, not raw,
|
This assignment is regular, not raw,
|
||||||
and therefore can trigger another @id{__newindex} metavalue.
|
and therefore can trigger another @idx{__newindex} metavalue.
|
||||||
|
|
||||||
Whenever a @idx{__newindex} metavalue is invoked,
|
Whenever a @idx{__newindex} metavalue is invoked,
|
||||||
Lua does not perform the primitive assignment.
|
Lua does not perform the primitive assignment.
|
||||||
@@ -737,7 +742,7 @@ For an object (table or userdata) to be finalized when collected,
|
|||||||
you must @emph{mark} it for finalization.
|
you must @emph{mark} it for finalization.
|
||||||
@index{mark (for finalization)}
|
@index{mark (for finalization)}
|
||||||
You mark an object for finalization when you set its metatable
|
You mark an object for finalization when you set its metatable
|
||||||
and the metatable has a field indexed by the string @St{__gc}.
|
and the metatable has a @idx{__gc} metamethod.
|
||||||
Note that if you set a metatable without a @idx{__gc} field
|
Note that if you set a metatable without a @idx{__gc} field
|
||||||
and later create that field in the metatable,
|
and later create that field in the metatable,
|
||||||
the object will not be marked for finalization.
|
the object will not be marked for finalization.
|
||||||
@@ -1625,14 +1630,8 @@ they are closed in the reverse order that they were declared.
|
|||||||
If there is any error while running a closing method,
|
If there is any error while running a closing method,
|
||||||
that error is handled like an error in the regular code
|
that error is handled like an error in the regular code
|
||||||
where the variable was defined.
|
where the variable was defined.
|
||||||
However, Lua may call the method one more time.
|
|
||||||
|
|
||||||
After an error,
|
After an error,
|
||||||
the other pending closing methods will still be called.
|
the other pending closing methods will still be called.
|
||||||
Errors in these methods
|
|
||||||
interrupt the respective method and generate a warning,
|
|
||||||
but are otherwise ignored;
|
|
||||||
the error reported is only the original one.
|
|
||||||
|
|
||||||
If a coroutine yields and is never resumed again,
|
If a coroutine yields and is never resumed again,
|
||||||
some variables may never go out of scope,
|
some variables may never go out of scope,
|
||||||
@@ -3096,6 +3095,21 @@ will probably need to close states as soon as they are not needed.
|
|||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@APIEntry{void lua_closeslot (lua_State *L, int index);|
|
||||||
|
@apii{0,0,e}
|
||||||
|
|
||||||
|
Close the to-be-closed slot at the given index and set its value to @nil.
|
||||||
|
The index must be the last index previously marked to be closed
|
||||||
|
@see{lua_toclose} that is still active (that is, not closed yet).
|
||||||
|
|
||||||
|
A @idx{__close} metamethod cannot yield
|
||||||
|
when called through this function.
|
||||||
|
|
||||||
|
(Exceptionally, this function was introduced in release 5.4.3.
|
||||||
|
It is not present in previous 5.4 releases.)
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
@APIEntry{int lua_compare (lua_State *L, int index1, int index2, int op);|
|
@APIEntry{int lua_compare (lua_State *L, int index1, int index2, int op);|
|
||||||
@apii{0,0,e}
|
@apii{0,0,e}
|
||||||
|
|
||||||
@@ -3748,9 +3762,7 @@ except that it allows the called function to yield @see{continuations}.
|
|||||||
@apii{n,0,e}
|
@apii{n,0,e}
|
||||||
|
|
||||||
Pops @id{n} elements from the stack.
|
Pops @id{n} elements from the stack.
|
||||||
|
It is implemented as a macro over @Lid{lua_settop}.
|
||||||
This function can run arbitrary code when removing an index
|
|
||||||
marked as to-be-closed from the stack.
|
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -4093,10 +4105,12 @@ and then pops the top element.
|
|||||||
Resets a thread, cleaning its call stack and closing all pending
|
Resets a thread, cleaning its call stack and closing all pending
|
||||||
to-be-closed variables.
|
to-be-closed variables.
|
||||||
Returns a status code:
|
Returns a status code:
|
||||||
@Lid{LUA_OK} for no errors in closing methods,
|
@Lid{LUA_OK} for no errors in the thread
|
||||||
|
(either the original error that stopped the thread or
|
||||||
|
errors in closing methods),
|
||||||
or an error status otherwise.
|
or an error status otherwise.
|
||||||
In case of error,
|
In case of error,
|
||||||
leaves the error object on the top of the stack,
|
leaves the error object on the top of the stack.
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -4329,30 +4343,28 @@ otherwise, returns @id{NULL}.
|
|||||||
@apii{0,0,m}
|
@apii{0,0,m}
|
||||||
|
|
||||||
Marks the given index in the stack as a
|
Marks the given index in the stack as a
|
||||||
to-be-closed @Q{variable} @see{to-be-closed}.
|
to-be-closed slot @see{to-be-closed}.
|
||||||
Like a to-be-closed variable in Lua,
|
Like a to-be-closed variable in Lua,
|
||||||
the value at that index in the stack will be closed
|
the value at that slot in the stack will be closed
|
||||||
when it goes out of scope.
|
when it goes out of scope.
|
||||||
Here, in the context of a C function,
|
Here, in the context of a C function,
|
||||||
to go out of scope means that the running function returns to Lua,
|
to go out of scope means that the running function returns to Lua,
|
||||||
there is an error,
|
or there is an error,
|
||||||
or the index is removed from the stack through
|
or the slot is removed from the stack through
|
||||||
@Lid{lua_settop} or @Lid{lua_pop}.
|
@Lid{lua_settop} or @Lid{lua_pop},
|
||||||
An index marked as to-be-closed should not be removed from the stack
|
or there is a call to @Lid{lua_closeslot}.
|
||||||
by any other function in the API except @Lid{lua_settop} or @Lid{lua_pop}.
|
A slot marked as to-be-closed should not be removed from the stack
|
||||||
|
by any other function in the API except @Lid{lua_settop} or @Lid{lua_pop},
|
||||||
|
unless previously deactivated by @Lid{lua_closeslot}.
|
||||||
|
|
||||||
This function should not be called for an index
|
This function should not be called for an index
|
||||||
that is equal to or below an active to-be-closed index.
|
that is equal to or below an active to-be-closed slot.
|
||||||
|
|
||||||
In the case of an out-of-memory error,
|
|
||||||
the value in the given index is immediately closed,
|
|
||||||
as if it was already marked.
|
|
||||||
|
|
||||||
Note that, both in case of errors and of a regular return,
|
Note that, both in case of errors and of a regular return,
|
||||||
by the time the @idx{__close} metamethod runs,
|
by the time the @idx{__close} metamethod runs,
|
||||||
the @N{C stack} was already unwound,
|
the @N{C stack} was already unwound,
|
||||||
so that any automatic @N{C variable} declared in the calling function
|
so that any automatic @N{C variable} declared in the calling function
|
||||||
will be out of scope.
|
(e.g., a buffer) will be out of scope.
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -6572,7 +6584,9 @@ that is,
|
|||||||
closes all its pending to-be-closed variables
|
closes all its pending to-be-closed variables
|
||||||
and puts the coroutine in a dead state.
|
and puts the coroutine in a dead state.
|
||||||
The given coroutine must be dead or suspended.
|
The given coroutine must be dead or suspended.
|
||||||
In case of error closing some variable,
|
In case of error
|
||||||
|
(either the original error that stopped the coroutine or
|
||||||
|
errors in closing methods),
|
||||||
returns @false plus the error object;
|
returns @false plus the error object;
|
||||||
otherwise returns @true.
|
otherwise returns @true.
|
||||||
|
|
||||||
@@ -7805,19 +7819,19 @@ from @T{list[1]} to @T{list[#list]}.
|
|||||||
If @id{comp} is given,
|
If @id{comp} is given,
|
||||||
then it must be a function that receives two list elements
|
then it must be a function that receives two list elements
|
||||||
and returns true when the first element must come
|
and returns true when the first element must come
|
||||||
before the second in the final order
|
before the second in the final order,
|
||||||
(so that, after the sort,
|
so that, after the sort,
|
||||||
@T{i < j} implies @T{not comp(list[j],list[i])}).
|
@T{i <= j} implies @T{not comp(list[j],list[i])}.
|
||||||
If @id{comp} is not given,
|
If @id{comp} is not given,
|
||||||
then the standard Lua operator @T{<} is used instead.
|
then the standard Lua operator @T{<} is used instead.
|
||||||
|
|
||||||
Note that the @id{comp} function must define
|
The @id{comp} function must define a consistent order;
|
||||||
a strict partial order over the elements in the list;
|
more formally, the function must define a strict weak order.
|
||||||
that is, it must be asymmetric and transitive.
|
(A weak order is similar to a total order,
|
||||||
Otherwise, no valid sort may be possible.
|
but it can equate different elements for comparison purposes.)
|
||||||
|
|
||||||
The sort algorithm is not stable:
|
The sort algorithm is not stable:
|
||||||
elements considered equal by the given order
|
Different elements considered equal by the given order
|
||||||
may have their relative positions changed by the sort.
|
may have their relative positions changed by the sort.
|
||||||
|
|
||||||
}
|
}
|
||||||
@@ -9079,7 +9093,7 @@ want the old behavior
|
|||||||
}
|
}
|
||||||
|
|
||||||
@item{
|
@item{
|
||||||
The use of the @idx{__lt} metamethod to emulate @id{__le}
|
The use of the @idx{__lt} metamethod to emulate @idx{__le}
|
||||||
has been removed.
|
has been removed.
|
||||||
When needed, this metamethod must be explicitly defined.
|
When needed, this metamethod must be explicitly defined.
|
||||||
}
|
}
|
||||||
@@ -9115,7 +9129,7 @@ like any other error when calling a finalizer.)
|
|||||||
The function @Lid{print} does not call @Lid{tostring}
|
The function @Lid{print} does not call @Lid{tostring}
|
||||||
to format its arguments;
|
to format its arguments;
|
||||||
instead, it has this functionality hardwired.
|
instead, it has this functionality hardwired.
|
||||||
You should use @id{__tostring} to modify how values are printed.
|
You should use @idx{__tostring} to modify how values are printed.
|
||||||
}
|
}
|
||||||
|
|
||||||
@item{
|
@item{
|
||||||
|
|||||||
@@ -154,18 +154,8 @@ end
|
|||||||
|
|
||||||
dofile('main.lua')
|
dofile('main.lua')
|
||||||
|
|
||||||
do
|
-- trace GC cycles
|
||||||
local next, setmetatable, stderr = next, setmetatable, io.stderr
|
require"tracegc".start()
|
||||||
-- 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
|
|
||||||
|
|
||||||
report"gc.lua"
|
report"gc.lua"
|
||||||
local f = assert(loadfile('gc.lua'))
|
local f = assert(loadfile('gc.lua'))
|
||||||
|
|||||||
@@ -507,10 +507,12 @@ function checkerrnopro (code, msg)
|
|||||||
end
|
end
|
||||||
|
|
||||||
if not _soft then
|
if not _soft then
|
||||||
|
collectgarbage("stop") -- avoid __gc with full stack
|
||||||
checkerrnopro("pushnum 3; call 0 0", "attempt to call")
|
checkerrnopro("pushnum 3; call 0 0", "attempt to call")
|
||||||
print"testing stack overflow in unprotected thread"
|
print"testing stack overflow in unprotected thread"
|
||||||
function f () f() end
|
function f () f() end
|
||||||
checkerrnopro("getglobal 'f'; call 0 0;", "stack overflow")
|
checkerrnopro("getglobal 'f'; call 0 0;", "stack overflow")
|
||||||
|
collectgarbage("restart")
|
||||||
end
|
end
|
||||||
print"+"
|
print"+"
|
||||||
|
|
||||||
@@ -1125,22 +1127,49 @@ do
|
|||||||
assert(#openresource == n)
|
assert(#openresource == n)
|
||||||
end
|
end
|
||||||
|
|
||||||
-- closing resources with 'settop'
|
-- closing resources with 'closeslot'
|
||||||
|
_ENV.xxx = true
|
||||||
local a = T.testC([[
|
local a = T.testC([[
|
||||||
pushvalue 2
|
pushvalue 2 # stack: S, NR, CH, NR
|
||||||
call 0 1 # create resource
|
call 0 1 # create resource; stack: S, NR, CH, R
|
||||||
toclose -1 # mark it to be closed
|
toclose -1 # mark it to be closed
|
||||||
pushvalue 2
|
pushvalue 2 # stack: S, NR, CH, R, NR
|
||||||
call 0 1 # create another resource
|
call 0 1 # create another resource; stack: S, NR, CH, R, R
|
||||||
toclose -1 # mark it to be closed
|
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
|
pushint 2 # there should be two open resources
|
||||||
call 1 0
|
call 1 0 # stack: S, NR, CH, R, R
|
||||||
pop 1 # pop second resource from the stack
|
closeslot -1 # close second resource
|
||||||
pushvalue 3
|
pushvalue 3 # stack: S, NR, CH, R, R, CH
|
||||||
pushint 1 # there should be one open resource
|
pushint 1 # there should be one open resource
|
||||||
call 1 0
|
call 1 0 # stack: S, NR, CH, R, R
|
||||||
pop 1 # pop second resource from the stack
|
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 *
|
pushint *
|
||||||
return 1 # return stack size
|
return 1 # return stack size
|
||||||
]], newresource, check)
|
]], newresource, check)
|
||||||
|
|||||||
@@ -55,6 +55,20 @@ end
|
|||||||
checkKlist(foo, {3.78/4, -3.78/4, -3.79/4})
|
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})
|
||||||
|
|
||||||
|
|
||||||
-- testing opcodes
|
-- testing opcodes
|
||||||
|
|
||||||
-- check that 'f' opcodes match '...'
|
-- check that 'f' opcodes match '...'
|
||||||
|
|||||||
@@ -123,7 +123,7 @@ assert(#a == 22 and a[#a] == 79)
|
|||||||
x, a = nil
|
x, a = nil
|
||||||
|
|
||||||
|
|
||||||
-- coroutine closing
|
print("to-be-closed variables in coroutines")
|
||||||
|
|
||||||
local function func2close (f)
|
local function func2close (f)
|
||||||
return setmetatable({}, {__close = f})
|
return setmetatable({}, {__close = f})
|
||||||
@@ -134,7 +134,8 @@ do
|
|||||||
local co = coroutine.create(print)
|
local co = coroutine.create(print)
|
||||||
assert(coroutine.resume(co, "testing 'coroutine.close'"))
|
assert(coroutine.resume(co, "testing 'coroutine.close'"))
|
||||||
assert(coroutine.status(co) == "dead")
|
assert(coroutine.status(co) == "dead")
|
||||||
assert(coroutine.close(co))
|
local st, msg = coroutine.close(co)
|
||||||
|
assert(st and msg == nil)
|
||||||
|
|
||||||
-- cannot close the running coroutine
|
-- cannot close the running coroutine
|
||||||
local st, msg = pcall(coroutine.close, coroutine.running())
|
local st, msg = pcall(coroutine.close, coroutine.running())
|
||||||
@@ -151,6 +152,13 @@ do
|
|||||||
-- to-be-closed variables in coroutines
|
-- to-be-closed variables in coroutines
|
||||||
local X
|
local X
|
||||||
|
|
||||||
|
-- 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)
|
||||||
|
|
||||||
co = coroutine.create(function ()
|
co = coroutine.create(function ()
|
||||||
local x <close> = func2close(function (self, err)
|
local x <close> = func2close(function (self, err)
|
||||||
assert(err == nil); X = false
|
assert(err == nil); X = false
|
||||||
@@ -164,13 +172,12 @@ do
|
|||||||
assert(not X and coroutine.status(co) == "dead")
|
assert(not X and coroutine.status(co) == "dead")
|
||||||
|
|
||||||
-- error closing a coroutine
|
-- error closing a coroutine
|
||||||
warn("@on")
|
|
||||||
local x = 0
|
local x = 0
|
||||||
co = coroutine.create(function()
|
co = coroutine.create(function()
|
||||||
local y <close> = func2close(function (self,err)
|
local y <close> = func2close(function (self,err)
|
||||||
if (err ~= 111) then os.exit(false) end -- should not happen
|
assert(err == 111)
|
||||||
x = 200
|
x = 200
|
||||||
error("200")
|
error(200)
|
||||||
end)
|
end)
|
||||||
local x <close> = func2close(function (self, err)
|
local x <close> = func2close(function (self, err)
|
||||||
assert(err == nil); error(111)
|
assert(err == nil); error(111)
|
||||||
@@ -179,18 +186,9 @@ do
|
|||||||
end)
|
end)
|
||||||
coroutine.resume(co)
|
coroutine.resume(co)
|
||||||
assert(x == 0)
|
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)
|
local st, msg = coroutine.close(co)
|
||||||
if not T then
|
assert(st == false and coroutine.status(co) == "dead" and msg == 200)
|
||||||
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(x == 200)
|
assert(x == 200)
|
||||||
|
|
||||||
end
|
end
|
||||||
|
|
||||||
do
|
do
|
||||||
@@ -208,6 +206,44 @@ do
|
|||||||
local st1, st2, err = coroutine.resume(co)
|
local st1, st2, err = coroutine.resume(co)
|
||||||
assert(st1 and not st2 and err == 43)
|
assert(st1 and not st2 and err == 43)
|
||||||
assert(X == 43 and Y.name == "pcall")
|
assert(X == 43 and Y.name == "pcall")
|
||||||
|
|
||||||
|
-- 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
|
end
|
||||||
|
|
||||||
|
|
||||||
@@ -462,6 +498,28 @@ else
|
|||||||
|
|
||||||
assert(B // A == 7) -- fact(7) // fact(6)
|
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 line = debug.getinfo(1, "l").currentline + 2 -- get line number
|
||||||
local function foo ()
|
local function foo ()
|
||||||
local x = 10 --<< this line is 'line'
|
local x = 10 --<< this line is 'line'
|
||||||
|
|||||||
@@ -2,6 +2,8 @@
|
|||||||
-- See Copyright Notice in file all.lua
|
-- See Copyright Notice in file all.lua
|
||||||
|
|
||||||
|
|
||||||
|
local tracegc = require"tracegc"
|
||||||
|
|
||||||
print"testing stack overflow detection"
|
print"testing stack overflow detection"
|
||||||
|
|
||||||
-- Segmentation faults in these tests probably result from a C-stack
|
-- Segmentation faults in these tests probably result from a C-stack
|
||||||
@@ -21,7 +23,9 @@ do print("testing stack overflow in message handling")
|
|||||||
count = count + 1
|
count = count + 1
|
||||||
return 1 + loop(x, y, z)
|
return 1 + loop(x, y, z)
|
||||||
end
|
end
|
||||||
|
tracegc.stop() -- __gc should not be called with a full stack
|
||||||
local res, msg = xpcall(loop, loop)
|
local res, msg = xpcall(loop, loop)
|
||||||
|
tracegc.start()
|
||||||
assert(msg == "error in error handling")
|
assert(msg == "error in error handling")
|
||||||
print("final count: ", count)
|
print("final count: ", count)
|
||||||
end
|
end
|
||||||
@@ -135,14 +139,18 @@ if T then
|
|||||||
local topB, sizeB -- top and size Before overflow
|
local topB, sizeB -- top and size Before overflow
|
||||||
local topA, sizeA -- top and size After overflow
|
local topA, sizeA -- top and size After overflow
|
||||||
topB, sizeB = T.stacklevel()
|
topB, sizeB = T.stacklevel()
|
||||||
|
tracegc.stop() -- __gc should not be called with a full stack
|
||||||
xpcall(f, err)
|
xpcall(f, err)
|
||||||
|
tracegc.start()
|
||||||
topA, sizeA = T.stacklevel()
|
topA, sizeA = T.stacklevel()
|
||||||
-- sizes should be comparable
|
-- sizes should be comparable
|
||||||
assert(topA == topB and sizeA < sizeB * 2)
|
assert(topA == topB and sizeA < sizeB * 2)
|
||||||
print(string.format("maximum stack size: %d", stack1))
|
print(string.format("maximum stack size: %d", stack1))
|
||||||
LIM = N -- will stop recursion at maximum level
|
LIM = N -- will stop recursion at maximum level
|
||||||
N = 0 -- to count again
|
N = 0 -- to count again
|
||||||
|
tracegc.stop() -- __gc should not be called with a full stack
|
||||||
f()
|
f()
|
||||||
|
tracegc.start()
|
||||||
print"+"
|
print"+"
|
||||||
end
|
end
|
||||||
|
|
||||||
|
|||||||
@@ -31,6 +31,7 @@ end
|
|||||||
|
|
||||||
do
|
do
|
||||||
assert(not pcall(debug.getinfo, print, "X")) -- invalid option
|
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(1000)) -- out of range level
|
||||||
assert(not debug.getinfo(-1)) -- out of range level
|
assert(not debug.getinfo(-1)) -- out of range level
|
||||||
local a = debug.getinfo(print)
|
local a = debug.getinfo(print)
|
||||||
@@ -823,8 +824,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")
|
-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|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 == "eq")
|
||||||
assert (a>=b and a.op == "order")
|
assert (a>=b and a.op == "le")
|
||||||
assert (a>b and a.op == "order")
|
assert ("x">=a and a.op == "le")
|
||||||
|
assert (a>b and a.op == "lt")
|
||||||
|
assert (a>10 and a.op == "lt")
|
||||||
assert(~a == "bnot")
|
assert(~a == "bnot")
|
||||||
|
|
||||||
do -- testing for-iterator name
|
do -- testing for-iterator name
|
||||||
|
|||||||
@@ -24,8 +24,9 @@ local function doit (s)
|
|||||||
end
|
end
|
||||||
|
|
||||||
|
|
||||||
local function checkmessage (prog, msg)
|
local function checkmessage (prog, msg, debug)
|
||||||
local m = doit(prog)
|
local m = doit(prog)
|
||||||
|
if debug then print(m) end
|
||||||
assert(string.find(m, msg, 1, true))
|
assert(string.find(m, msg, 1, true))
|
||||||
end
|
end
|
||||||
|
|
||||||
@@ -67,6 +68,27 @@ checksyntax([[
|
|||||||
]], "'}' expected (to close '{' at line 1)", "<eof>", 3)
|
]], "'}' 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
|
if not T then
|
||||||
(Message or print)
|
(Message or print)
|
||||||
('\n >>> testC not active: skipping memory message test <<<\n')
|
('\n >>> testC not active: skipping memory message test <<<\n')
|
||||||
@@ -99,6 +121,17 @@ 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={13}; local bbbb=1; a[bbbb](3)", "number")
|
||||||
checkmessage("a=(1)..{}", "a table value")
|
checkmessage("a=(1)..{}", "a table value")
|
||||||
|
|
||||||
|
-- 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
|
-- tail calls
|
||||||
checkmessage("local a={}; return a.bbbb(3)", "field 'bbbb'")
|
checkmessage("local a={}; return a.bbbb(3)", "field 'bbbb'")
|
||||||
checkmessage("a={}; do local a=1 end; return a:bbbb(3)", "method 'bbbb'")
|
checkmessage("a={}; do local a=1 end; return a:bbbb(3)", "method 'bbbb'")
|
||||||
@@ -158,6 +191,13 @@ checkmessage("a = 24 // 0", "divide by zero")
|
|||||||
checkmessage("a = 1 % 0", "'n%0'")
|
checkmessage("a = 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
|
-- numeric for loops
|
||||||
checkmessage("for i = {}, 10 do end", "table")
|
checkmessage("for i = {}, 10 do end", "table")
|
||||||
checkmessage("for i = io.stdin, 10 do end", "FILE")
|
checkmessage("for i = io.stdin, 10 do end", "FILE")
|
||||||
@@ -380,6 +420,14 @@ if not b then
|
|||||||
end
|
end
|
||||||
end]], 5)
|
end]], 5)
|
||||||
|
|
||||||
|
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
|
if not _soft then
|
||||||
-- several tests that exaust the Lua stack
|
-- several tests that exaust the Lua stack
|
||||||
|
|||||||
@@ -676,6 +676,14 @@ end
|
|||||||
-- just to make sure
|
-- just to make sure
|
||||||
assert(collectgarbage'isrunning')
|
assert(collectgarbage'isrunning')
|
||||||
|
|
||||||
|
do -- check that the collector is reentrant in incremental mode
|
||||||
|
setmetatable({}, {__gc = function ()
|
||||||
|
collectgarbage()
|
||||||
|
end})
|
||||||
|
collectgarbage()
|
||||||
|
end
|
||||||
|
|
||||||
|
|
||||||
collectgarbage(oldmode)
|
collectgarbage(oldmode)
|
||||||
|
|
||||||
print('OK')
|
print('OK')
|
||||||
|
|||||||
@@ -5,6 +5,8 @@ print('testing local variables and environments')
|
|||||||
|
|
||||||
local debug = require"debug"
|
local debug = require"debug"
|
||||||
|
|
||||||
|
local tracegc = require"tracegc"
|
||||||
|
|
||||||
|
|
||||||
-- bug in 5.1:
|
-- bug in 5.1:
|
||||||
|
|
||||||
@@ -232,7 +234,11 @@ end
|
|||||||
do
|
do
|
||||||
local X = false
|
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
|
assert(x == 100); x = 101; -- 'x' is not read-only
|
||||||
|
|
||||||
-- closing functions do not corrupt returning values
|
-- closing functions do not corrupt returning values
|
||||||
@@ -246,10 +252,11 @@ do
|
|||||||
|
|
||||||
X = false
|
X = false
|
||||||
foo = function (x)
|
foo = function (x)
|
||||||
local _<close> = func2close(function ()
|
local _<close> = func2close(function (_, msg)
|
||||||
-- without errors, enclosing function should be still active when
|
-- without errors, enclosing function should be still active when
|
||||||
-- __close is called
|
-- __close is called
|
||||||
assert(debug.getinfo(2).name == "foo")
|
assert(debug.getinfo(2).name == "foo")
|
||||||
|
assert(msg == nil)
|
||||||
end)
|
end)
|
||||||
local _<close> = closescope
|
local _<close> = closescope
|
||||||
local y = 15
|
local y = 15
|
||||||
@@ -328,85 +335,33 @@ do
|
|||||||
end
|
end
|
||||||
|
|
||||||
|
|
||||||
-- auxiliary functions for testing warnings in '__close'
|
|
||||||
local function prepwarn ()
|
|
||||||
if not T then -- no test library?
|
|
||||||
warn("@off") -- do not show (lots of) warnings
|
|
||||||
else
|
|
||||||
warn("@store") -- to test the warnings
|
|
||||||
end
|
|
||||||
end
|
|
||||||
|
|
||||||
|
|
||||||
local function endwarn ()
|
|
||||||
if not T then
|
|
||||||
warn("@on") -- back to normal
|
|
||||||
else
|
|
||||||
assert(_WARN == false)
|
|
||||||
warn("@normal")
|
|
||||||
end
|
|
||||||
end
|
|
||||||
|
|
||||||
|
|
||||||
-- errors inside __close can generate a warning instead of an
|
|
||||||
-- error. This new 'assert' force them to appear.
|
|
||||||
local function assert(cond, msg)
|
|
||||||
if not cond then
|
|
||||||
local line = debug.getinfo(2).currentline or "?"
|
|
||||||
msg = string.format("assertion failed! line %d (%s)\n", line, msg or "")
|
|
||||||
io.stderr:write(msg)
|
|
||||||
os.exit(1)
|
|
||||||
end
|
|
||||||
end
|
|
||||||
|
|
||||||
|
|
||||||
local function checkwarn (msg)
|
|
||||||
if T then
|
|
||||||
assert(string.find(_WARN, msg))
|
|
||||||
_WARN = false -- reset variable to check next warning
|
|
||||||
end
|
|
||||||
end
|
|
||||||
|
|
||||||
warn("@on")
|
|
||||||
|
|
||||||
do print("testing errors in __close")
|
do print("testing errors in __close")
|
||||||
|
|
||||||
prepwarn()
|
|
||||||
|
|
||||||
-- original error is in __close
|
-- original error is in __close
|
||||||
local function foo ()
|
local function foo ()
|
||||||
|
|
||||||
local x <close> =
|
local x <close> =
|
||||||
func2close(function (self, msg)
|
func2close(function (self, msg)
|
||||||
assert(string.find(msg, "@z"))
|
assert(string.find(msg, "@y"))
|
||||||
error("@x")
|
error("@x")
|
||||||
end)
|
end)
|
||||||
|
|
||||||
local x1 <close> =
|
local x1 <close> =
|
||||||
func2close(function (self, msg)
|
func2close(function (self, msg)
|
||||||
checkwarn("@y")
|
assert(string.find(msg, "@y"))
|
||||||
assert(string.find(msg, "@z"))
|
|
||||||
end)
|
end)
|
||||||
|
|
||||||
local gc <close> = func2close(function () collectgarbage() end)
|
local gc <close> = func2close(function () collectgarbage() end)
|
||||||
|
|
||||||
local y <close> =
|
local y <close> =
|
||||||
func2close(function (self, msg)
|
func2close(function (self, msg)
|
||||||
assert(string.find(msg, "@z")) -- first error in 'z'
|
assert(string.find(msg, "@z")) -- error in 'z'
|
||||||
checkwarn("@z") -- second error in 'z' generated a warning
|
|
||||||
error("@y")
|
error("@y")
|
||||||
end)
|
end)
|
||||||
|
|
||||||
local first = true
|
|
||||||
local z <close> =
|
local z <close> =
|
||||||
-- 'z' close is called twice
|
|
||||||
func2close(function (self, msg)
|
func2close(function (self, msg)
|
||||||
if first then
|
assert(msg == nil)
|
||||||
assert(msg == nil)
|
|
||||||
first = false
|
|
||||||
else
|
|
||||||
assert(string.find(msg, "@z")) -- own error
|
|
||||||
end
|
|
||||||
error("@z")
|
error("@z")
|
||||||
end)
|
end)
|
||||||
|
|
||||||
@@ -414,8 +369,7 @@ do print("testing errors in __close")
|
|||||||
end
|
end
|
||||||
|
|
||||||
local stat, msg = pcall(foo, false)
|
local stat, msg = pcall(foo, false)
|
||||||
assert(string.find(msg, "@z"))
|
assert(string.find(msg, "@x"))
|
||||||
checkwarn("@x")
|
|
||||||
|
|
||||||
|
|
||||||
-- original error not in __close
|
-- original error not in __close
|
||||||
@@ -426,14 +380,13 @@ do print("testing errors in __close")
|
|||||||
-- after error, 'foo' was discarded, so caller now
|
-- after error, 'foo' was discarded, so caller now
|
||||||
-- must be 'pcall'
|
-- must be 'pcall'
|
||||||
assert(debug.getinfo(2).name == "pcall")
|
assert(debug.getinfo(2).name == "pcall")
|
||||||
assert(msg == 4)
|
assert(string.find(msg, "@x1"))
|
||||||
end)
|
end)
|
||||||
|
|
||||||
local x1 <close> =
|
local x1 <close> =
|
||||||
func2close(function (self, msg)
|
func2close(function (self, msg)
|
||||||
assert(debug.getinfo(2).name == "pcall")
|
assert(debug.getinfo(2).name == "pcall")
|
||||||
checkwarn("@y")
|
assert(string.find(msg, "@y"))
|
||||||
assert(msg == 4)
|
|
||||||
error("@x1")
|
error("@x1")
|
||||||
end)
|
end)
|
||||||
|
|
||||||
@@ -442,8 +395,7 @@ do print("testing errors in __close")
|
|||||||
local y <close> =
|
local y <close> =
|
||||||
func2close(function (self, msg)
|
func2close(function (self, msg)
|
||||||
assert(debug.getinfo(2).name == "pcall")
|
assert(debug.getinfo(2).name == "pcall")
|
||||||
assert(msg == 4) -- error in body
|
assert(string.find(msg, "@z"))
|
||||||
checkwarn("@z")
|
|
||||||
error("@y")
|
error("@y")
|
||||||
end)
|
end)
|
||||||
|
|
||||||
@@ -461,20 +413,20 @@ do print("testing errors in __close")
|
|||||||
end
|
end
|
||||||
|
|
||||||
local stat, msg = pcall(foo, true)
|
local stat, msg = pcall(foo, true)
|
||||||
assert(msg == 4)
|
assert(string.find(msg, "@x1"))
|
||||||
checkwarn("@x1") -- last error
|
|
||||||
|
|
||||||
-- error leaving a block
|
-- error leaving a block
|
||||||
local function foo (...)
|
local function foo (...)
|
||||||
do
|
do
|
||||||
local x1 <close> =
|
local x1 <close> =
|
||||||
func2close(function ()
|
func2close(function (self, msg)
|
||||||
checkwarn("@X")
|
assert(string.find(msg, "@X"))
|
||||||
error("@Y")
|
error("@Y")
|
||||||
end)
|
end)
|
||||||
|
|
||||||
local x123 <close> =
|
local x123 <close> =
|
||||||
func2close(function ()
|
func2close(function (_, msg)
|
||||||
|
assert(msg == nil)
|
||||||
error("@X")
|
error("@X")
|
||||||
end)
|
end)
|
||||||
end
|
end
|
||||||
@@ -482,9 +434,7 @@ do print("testing errors in __close")
|
|||||||
end
|
end
|
||||||
|
|
||||||
local st, msg = xpcall(foo, debug.traceback)
|
local st, msg = xpcall(foo, debug.traceback)
|
||||||
assert(string.match(msg, "^[^ ]* @X"))
|
assert(string.match(msg, "^[^ ]* @Y"))
|
||||||
assert(string.find(msg, "in metamethod 'close'"))
|
|
||||||
checkwarn("@Y")
|
|
||||||
|
|
||||||
-- error in toclose in vararg function
|
-- error in toclose in vararg function
|
||||||
local function foo (...)
|
local function foo (...)
|
||||||
@@ -494,9 +444,6 @@ do print("testing errors in __close")
|
|||||||
local st, msg = xpcall(foo, debug.traceback)
|
local st, msg = xpcall(foo, debug.traceback)
|
||||||
assert(string.match(msg, "^[^ ]* @x123"))
|
assert(string.match(msg, "^[^ ]* @x123"))
|
||||||
assert(string.find(msg, "in metamethod 'close'"))
|
assert(string.find(msg, "in metamethod 'close'"))
|
||||||
checkwarn("@x123") -- from second call to close 'x123'
|
|
||||||
|
|
||||||
endwarn()
|
|
||||||
end
|
end
|
||||||
|
|
||||||
|
|
||||||
@@ -514,15 +461,112 @@ do -- errors due to non-closable values
|
|||||||
getmetatable(xyz).__close = nil -- remove metamethod
|
getmetatable(xyz).__close = nil -- remove metamethod
|
||||||
end
|
end
|
||||||
local stat, msg = pcall(foo)
|
local stat, msg = pcall(foo)
|
||||||
assert(not stat and
|
assert(not stat and string.find(msg, "metamethod 'close'"))
|
||||||
string.find(msg, "attempt to close non%-closable variable 'xyz'"))
|
|
||||||
|
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
|
end
|
||||||
|
|
||||||
|
|
||||||
if rawget(_G, "T") then
|
if rawget(_G, "T") then
|
||||||
|
|
||||||
warn("@off")
|
|
||||||
|
|
||||||
-- memory error inside closing function
|
-- memory error inside closing function
|
||||||
local function foo ()
|
local function foo ()
|
||||||
local y <close> = func2close(function () T.alloccount() end)
|
local y <close> = func2close(function () T.alloccount() end)
|
||||||
@@ -537,33 +581,30 @@ if rawget(_G, "T") then
|
|||||||
-- despite memory error, 'y' will be executed and
|
-- despite memory error, 'y' will be executed and
|
||||||
-- memory limit will be lifted
|
-- memory limit will be lifted
|
||||||
local _, msg = pcall(foo)
|
local _, msg = pcall(foo)
|
||||||
assert(msg == 1000)
|
assert(msg == "not enough memory")
|
||||||
|
|
||||||
|
local closemsg
|
||||||
local close = func2close(function (self, msg)
|
local close = func2close(function (self, msg)
|
||||||
T.alloccount()
|
T.alloccount()
|
||||||
assert(msg == "not enough memory")
|
closemsg = msg
|
||||||
end)
|
end)
|
||||||
|
|
||||||
-- set a memory limit and return a closing object to remove the limit
|
-- set a memory limit and return a closing object to remove the limit
|
||||||
local function enter (count)
|
local function enter (count)
|
||||||
stack(10) -- reserve some stack space
|
stack(10) -- reserve some stack space
|
||||||
T.alloccount(count)
|
T.alloccount(count)
|
||||||
|
closemsg = nil
|
||||||
return close
|
return close
|
||||||
end
|
end
|
||||||
|
|
||||||
local function test ()
|
local function test ()
|
||||||
local x <close> = enter(0) -- set a memory limit
|
local x <close> = enter(0) -- set a memory limit
|
||||||
-- creation of previous upvalue will raise a memory error
|
local y = {} -- raise a memory error
|
||||||
assert(false) -- should not run
|
|
||||||
end
|
end
|
||||||
|
|
||||||
local _, msg = pcall(test)
|
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
|
-- repeat test with extra closing upvalues
|
||||||
local function test ()
|
local function test ()
|
||||||
@@ -575,12 +616,11 @@ if rawget(_G, "T") then
|
|||||||
assert(msg == "not enough memory");
|
assert(msg == "not enough memory");
|
||||||
end)
|
end)
|
||||||
local x <close> = enter(0) -- set a memory limit
|
local x <close> = enter(0) -- set a memory limit
|
||||||
-- creation of previous upvalue will raise a memory error
|
local y = {} -- raise a memory error
|
||||||
os.exit(false) -- should not run
|
|
||||||
end
|
end
|
||||||
|
|
||||||
local _, msg = pcall(test)
|
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
|
do -- testing 'toclose' in C string buffer
|
||||||
collectgarbage()
|
collectgarbage()
|
||||||
@@ -602,7 +642,7 @@ if rawget(_G, "T") then
|
|||||||
-- concat this table needs two buffer resizes (one for each 's')
|
-- concat this table needs two buffer resizes (one for each 's')
|
||||||
local a = {s, s}
|
local a = {s, s}
|
||||||
|
|
||||||
collectgarbage()
|
collectgarbage(); collectgarbage()
|
||||||
|
|
||||||
m = T.totalmem()
|
m = T.totalmem()
|
||||||
collectgarbage("stop")
|
collectgarbage("stop")
|
||||||
@@ -625,7 +665,7 @@ if rawget(_G, "T") then
|
|||||||
-- second buffer was released by 'toclose'
|
-- second buffer was released by 'toclose'
|
||||||
assert(T.totalmem() - m <= extra)
|
assert(T.totalmem() - m <= extra)
|
||||||
|
|
||||||
-- userdata, upvalue, buffer, buffer, final string
|
-- userdata, buffer, buffer, final string
|
||||||
T.totalmem(m + 4*lim + extra)
|
T.totalmem(m + 4*lim + extra)
|
||||||
assert(#table.concat(a) == 2*lim)
|
assert(#table.concat(a) == 2*lim)
|
||||||
|
|
||||||
@@ -635,12 +675,185 @@ if rawget(_G, "T") then
|
|||||||
print'+'
|
print'+'
|
||||||
end
|
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
|
end
|
||||||
|
|
||||||
|
|
||||||
print "to-be-closed variables in coroutines"
|
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 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
|
do
|
||||||
-- an error in a wrapped coroutine closes variables
|
-- an error in a wrapped coroutine closes variables
|
||||||
local x = false
|
local x = false
|
||||||
@@ -665,37 +878,43 @@ end
|
|||||||
|
|
||||||
|
|
||||||
do
|
do
|
||||||
prepwarn()
|
|
||||||
|
|
||||||
-- error in a wrapped coroutine raising errors when closing a variable
|
-- error in a wrapped coroutine raising errors when closing a variable
|
||||||
local x = 0
|
local x = 0
|
||||||
local co = coroutine.wrap(function ()
|
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)
|
local xv <close> = func2close(function () x = x + 1; error("@XXX") end)
|
||||||
coroutine.yield(100)
|
coroutine.yield(100)
|
||||||
error(200)
|
error(200)
|
||||||
end)
|
end)
|
||||||
assert(co() == 100); assert(x == 0)
|
assert(co() == 100); assert(x == 0)
|
||||||
local st, msg = pcall(co); assert(x == 2)
|
local st, msg = pcall(co); assert(x == 2)
|
||||||
assert(not st and msg == 200) -- should get first error raised
|
assert(not st and string.find(msg, "@YYY")) -- should get error raised
|
||||||
checkwarn("@YYY")
|
|
||||||
|
|
||||||
local x = 0
|
local x = 0
|
||||||
local y = 0
|
local y = 0
|
||||||
co = coroutine.wrap(function ()
|
co = coroutine.wrap(function ()
|
||||||
local xx <close> = func2close(function () y = y + 1; error("YYY") end)
|
local xx <close> = func2close(function (_, err)
|
||||||
local xv <close> = func2close(function () x = x + 1; error("XXX") end)
|
y = y + 1;
|
||||||
coroutine.yield(100)
|
assert(string.find(err, "XXX"))
|
||||||
return 200
|
error("YYY")
|
||||||
|
end)
|
||||||
|
local xv <close> = func2close(function ()
|
||||||
|
x = x + 1; error("XXX")
|
||||||
|
end)
|
||||||
|
coroutine.yield(100)
|
||||||
|
return 200
|
||||||
end)
|
end)
|
||||||
assert(co() == 100); assert(x == 0)
|
assert(co() == 100); assert(x == 0)
|
||||||
local st, msg = pcall(co)
|
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
|
-- should get first error raised
|
||||||
assert(not st and string.find(msg, "%w+%.%w+:%d+: XXX"))
|
assert(not st and string.find(msg, "%w+%.%w+:%d+: YYY"))
|
||||||
checkwarn("YYY")
|
|
||||||
|
|
||||||
endwarn()
|
|
||||||
end
|
end
|
||||||
|
|
||||||
|
|
||||||
@@ -712,6 +931,81 @@ assert(co == nil) -- eventually it will be collected
|
|||||||
collectgarbage()
|
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
|
-- to-be-closed variables in generic for loops
|
||||||
do
|
do
|
||||||
local numopen = 0
|
local numopen = 0
|
||||||
|
|||||||
@@ -33,6 +33,7 @@ $NAME/pm.lua \
|
|||||||
$NAME/sort.lua \
|
$NAME/sort.lua \
|
||||||
$NAME/strings.lua \
|
$NAME/strings.lua \
|
||||||
$NAME/tpack.lua \
|
$NAME/tpack.lua \
|
||||||
|
$NAME/tracegc.lua \
|
||||||
$NAME/utf8.lua \
|
$NAME/utf8.lua \
|
||||||
$NAME/vararg.lua \
|
$NAME/vararg.lua \
|
||||||
$NAME/verybig.lua \
|
$NAME/verybig.lua \
|
||||||
|
|||||||
@@ -361,6 +361,9 @@ assert(load("return 1\n--comment without ending EOL")() == 1)
|
|||||||
|
|
||||||
|
|
||||||
checkerror("table expected", table.concat, 3)
|
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{} == "")
|
||||||
assert(table.concat({}, 'x') == "")
|
assert(table.concat({}, 'x') == "")
|
||||||
assert(table.concat({'\0', '\0\1', '\0\1\2'}, '.\0.') == "\0.\0.\0\1.\0.\0\1\2")
|
assert(table.concat({'\0', '\0\1', '\0\1\2'}, '.\0.') == "\0.\0.\0\1.\0.\0\1\2")
|
||||||
|
|||||||
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
|
||||||
Reference in New Issue
Block a user