Project

Profile

Help

HostedRedmine.com has moved to the Planio platform. All logins and passwords remained the same. All users will be able to login and use Redmine just as before. *Read more...*

Feature #891047 ยป 0013-Update-included-lua-to-version-5.4.1.patch

Marko Lindqvist, 2020-10-13 02:32 AM

View differences:

dependencies/lua-5.4/README
This is Lua 5.4.0, released on 18 Jun 2020.
This is Lua 5.4.1, released on 30 Sep 2020.
For installation instructions, license details, and
further information about Lua, see doc/readme.html.
dependencies/lua-5.4/Version
Sources here are from lua-5.4.0
(http://www.lua.org/ftp/lua-5.4.0.tar.gz)
Sources here are from lua-5.4.1
(http://www.lua.org/ftp/lua-5.4.1.tar.gz)
Not entire lua distribution directory hierarchy is included here, and
some files needed for Freeciv usage have been added.
dependencies/lua-5.4/doc/readme.html
<OL>
<LI>
Open a terminal window and move to
the top-level directory, which is named <TT>lua-5.4.0</TT>.
the top-level directory, which is named <TT>lua-5.4.1</TT>.
The <TT>Makefile</TT> there controls both the build process and the installation process.
<P>
<LI>
......
<P CLASS="footer">
Last update:
Fri May 1 19:33:31 UTC 2020
Wed Sep 30 09:55:45 UTC 2020
</P>
<!--
Last change: revised for Lua 5.4.0 (final)
Last change: revised for Lua 5.4.1
-->
</BODY>
dependencies/lua-5.4/src/lapi.c
LUA_API int lua_checkstack (lua_State *L, int n) {
int res;
CallInfo *ci = L->ci;
CallInfo *ci;
lua_lock(L);
ci = L->ci;
api_check(L, n >= 0, "negative 'n'");
if (L->stack_last - L->top > n) /* stack large enough? */
res = 1; /* yes; check is OK */
......
LUA_API void lua_settop (lua_State *L, int idx) {
CallInfo *ci = L->ci;
StkId func = ci->func;
CallInfo *ci;
StkId func;
ptrdiff_t diff; /* difference for new top */
lua_lock(L);
ci = L->ci;
func = ci->func;
if (idx >= 0) {
api_check(L, idx <= ci->top - (func + 1), "new top too large");
diff = ((func + 1) + idx) - L->top;
......
LUA_API const char *lua_tolstring (lua_State *L, int idx, size_t *len) {
TValue *o = index2value(L, idx);
TValue *o;
lua_lock(L);
o = index2value(L, idx);
if (!ttisstring(o)) {
if (!cvt2str(o)) { /* not convertible? */
if (len != NULL) *len = 0;
lua_unlock(L);
return NULL;
}
lua_lock(L); /* 'luaO_tostring' may create a new string */
luaO_tostring(L, o);
luaC_checkGC(L);
o = index2value(L, idx); /* previous call may reallocate the stack */
lua_unlock(L);
}
if (len != NULL)
*len = vslen(o);
lua_unlock(L);
return svalue(o);
}
......
while (n--) {
setobj2n(L, &cl->upvalue[n], s2v(L->top + n));
/* does not need barrier because closure is white */
lua_assert(iswhite(cl));
}
setclCvalue(L, s2v(L->top), cl);
api_incr_top(L);
......
LUA_API int lua_getglobal (lua_State *L, const char *name) {
Table *reg = hvalue(&G(L)->l_registry);
Table *reg;
lua_lock(L);
reg = hvalue(&G(L)->l_registry);
return auxgetstr(L, luaH_getint(reg, LUA_RIDX_GLOBALS), name);
}
......
LUA_API void lua_setglobal (lua_State *L, const char *name) {
Table *reg = hvalue(&G(L)->l_registry);
Table *reg;
lua_lock(L); /* unlock done in 'auxsetstr' */
reg = hvalue(&G(L)->l_registry);
auxsetstr(L, luaH_getint(reg, LUA_RIDX_GLOBALS), name);
}
......
LUA_API int lua_gc (lua_State *L, int what, ...) {
va_list argp;
int res = 0;
global_State *g = G(L);
global_State *g;
lua_lock(L);
g = G(L);
va_start(argp, what);
switch (what) {
case LUA_GCSTOP: {
......
LUA_API int lua_error (lua_State *L) {
TValue *errobj;
lua_lock(L);
errobj = s2v(L->top - 1);
api_checknelems(L, 1);
luaG_errormsg(L);
/* error object is the memory error message? */
if (ttisshrstring(errobj) && eqshrstr(tsvalue(errobj), G(L)->memerrmsg))
luaM_error(L); /* raise a memory error */
else
luaG_errormsg(L); /* raise a regular error */
/* code unreachable; will unlock when control actually leaves the kernel */
return 0; /* to avoid warnings */
}
......
LUA_API void lua_concat (lua_State *L, int n) {
lua_lock(L);
api_checknelems(L, n);
if (n >= 2) {
if (n > 0)
luaV_concat(L, n);
}
else if (n == 0) { /* push empty string */
setsvalue2s(L, L->top, luaS_newlstr(L, "", 0));
else { /* nothing to concatenate */
setsvalue2s(L, L->top, luaS_newlstr(L, "", 0)); /* push empty string */
api_incr_top(L);
}
/* else n == 1; nothing to do */
luaC_checkGC(L);
lua_unlock(L);
}
dependencies/lua-5.4/src/lauxlib.c
lua_Alloc allocf = lua_getallocf(L, &ud);
UBox *box = (UBox *)lua_touserdata(L, idx);
void *temp = allocf(ud, box->box, box->bsize, newsize);
if (temp == NULL && newsize > 0) /* allocation error? */
luaL_error(L, "not enough memory");
if (temp == NULL && newsize > 0) { /* allocation error? */
lua_pushliteral(L, "not enough memory");
lua_error(L); /* raise a memory error */
}
box->box = temp;
box->bsize = newsize;
return temp;
dependencies/lua-5.4/src/lcorolib.c
static int luaB_auxwrap (lua_State *L) {
lua_State *co = lua_tothread(L, lua_upvalueindex(1));
int r = auxresume(L, co, lua_gettop(L));
if (r < 0) {
if (r < 0) { /* error? */
int stat = lua_status(co);
if (stat != LUA_OK && stat != LUA_YIELD)
lua_resetthread(co); /* close variables in case of errors */
if (lua_type(L, -1) == LUA_TSTRING) { /* error object is a string? */
if (stat != LUA_OK && stat != LUA_YIELD) /* error in the coroutine? */
lua_resetthread(co); /* close its tbc variables */
if (stat != LUA_ERRMEM && /* not a memory error and ... */
lua_type(L, -1) == LUA_TSTRING) { /* ... error object is a string? */
luaL_where(L, 1); /* add extra info, if available */
lua_insert(L, -2);
lua_concat(L, 2);
dependencies/lua-5.4/src/lctype.h
/*
** WARNING: the functions defined here do not necessarily correspond
** to the similar functions in the standard C ctype.h. They are
** optimized for the specific needs of Lua
** optimized for the specific needs of Lua.
*/
#if !defined(LUA_USE_CTYPE)
......
#define lisprint(c) testprop(c, MASK(PRINTBIT))
#define lisxdigit(c) testprop(c, MASK(XDIGITBIT))
/*
** this 'ltolower' only works for alphabetic characters
** In ASCII, this 'ltolower' is correct for alphabetic characters and
** for '.'. That is enough for Lua needs. ('check_exp' ensures that
** the character either is an upper-case letter or is unchanged by
** the transformation, which holds for lower-case letters and '.'.)
*/
#define ltolower(c) ((c) | ('A' ^ 'a'))
#define ltolower(c) \
check_exp(('A' <= (c) && (c) <= 'Z') || (c) == ((c) | ('A' ^ 'a')), \
(c) | ('A' ^ 'a'))
/* two more entries for 0 and -1 (EOZ) */
/* one entry for each character and for -1 (EOZ) */
LUAI_DDEC(const lu_byte luai_ctype_[UCHAR_MAX + 2];)
dependencies/lua-5.4/src/ldebug.c
#define noLuaClosure(f) ((f) == NULL || (f)->c.tt == LUA_VCCL)
/* Active Lua function (given call info) */
#define ci_func(ci) (clLvalue(s2v((ci)->func)))
/* inverse of 'pcRel' */
#define invpcRel(pc, p) ((p)->code + (pc) + 1)
static const char *funcnamefromcode (lua_State *L, CallInfo *ci,
const char **name);
......
/*
** This function can be called during a signal, under "reasonable"
** assumptions.
** Fields 'oldpc', 'basehookcount', and 'hookcount' (set by
** 'resethookcount') are for debug only, and it is no problem if they
** get arbitrary values (causes at most one wrong hook call). 'hookmask'
** is an atomic value. We assume that pointers are atomic too (e.g., gcc
** ensures that for all platforms where it runs). Moreover, 'hook' is
** always checked before being called (see 'luaD_hook').
** Fields 'basehookcount' and 'hookcount' (set by 'resethookcount')
** are for debug only, and it is no problem if they get arbitrary
** values (causes at most one wrong hook call). 'hookmask' is an atomic
** value. We assume that pointers are atomic too (e.g., gcc ensures that
** for all platforms where it runs). Moreover, 'hook' is always checked
** before being called (see 'luaD_hook').
*/
LUA_API void lua_sethook (lua_State *L, lua_Hook func, int mask, int count) {
if (func == NULL || mask == 0) { /* turn off hooks? */
mask = 0;
func = NULL;
}
if (isLua(L->ci))
L->oldpc = L->ci->u.l.savedpc;
L->hook = func;
L->basehookcount = count;
resethookcount(L);
......
static const char *findvararg (CallInfo *ci, int n, StkId *pos) {
if (clLvalue(s2v(ci->func))->p->is_vararg) {
int nextra = ci->u.l.nextraargs;
if (n <= nextra) {
*pos = ci->func - nextra + (n - 1);
if (n >= -nextra) { /* 'n' is negative */
*pos = ci->func - nextra - (n + 1);
return "(vararg)"; /* generic name for any vararg */
}
}
......
const char *name = NULL;
if (isLua(ci)) {
if (n < 0) /* access to vararg values? */
return findvararg(ci, -n, pos);
return findvararg(ci, n, pos);
else
name = luaF_getlocalname(ci_func(ci)->p, n, currentpc(ci));
}
......
** previous instruction 'oldpc'.
*/
static int changedline (const Proto *p, int oldpc, int newpc) {
if (p->lineinfo == NULL) /* no debug information? */
return 0;
while (oldpc++ < newpc) {
if (p->lineinfo[oldpc] != 0)
return (luaG_getfuncline(p, oldpc - 1) != luaG_getfuncline(p, newpc));
}
return 0; /* no line changes in the way */
return 0; /* no line changes between positions */
}
/*
** Traces the execution of a Lua function. Called before the execution
** of each opcode, when debug is on. 'L->oldpc' stores the last
** instruction traced, to detect line changes. When entering a new
** function, 'npci' will be zero and will test as a new line without
** the need for 'oldpc'; so, 'oldpc' does not need to be initialized
** before. Some exceptional conditions may return to a function without
** updating 'oldpc'. In that case, 'oldpc' may be invalid; if so, it is
** reset to zero. (A wrong but valid 'oldpc' at most causes an extra
** call to a line hook.)
*/
int luaG_traceexec (lua_State *L, const Instruction *pc) {
CallInfo *ci = L->ci;
lu_byte mask = L->hookmask;
const Proto *p = ci_func(ci)->p;
int counthook;
/* 'L->oldpc' may be invalid; reset it in this case */
int oldpc = (L->oldpc < p->sizecode) ? L->oldpc : 0;
if (!(mask & (LUA_MASKLINE | LUA_MASKCOUNT))) { /* no hooks? */
ci->u.l.trap = 0; /* don't need to stop again */
return 0; /* turn off 'trap' */
......
if (counthook)
luaD_hook(L, LUA_HOOKCOUNT, -1, 0, 0); /* call count hook */
if (mask & LUA_MASKLINE) {
const Proto *p = ci_func(ci)->p;
int npci = pcRel(pc, p);
if (npci == 0 || /* call linehook when enter a new function, */
pc <= L->oldpc || /* when jump back (loop), or when */
changedline(p, pcRel(L->oldpc, p), npci)) { /* enter new line */
pc <= invpcRel(oldpc, p) || /* when jump back (loop), or when */
changedline(p, oldpc, npci)) { /* enter new line */
int newline = luaG_getfuncline(p, npci);
luaD_hook(L, LUA_HOOKLINE, newline, 0, 0); /* call line hook */
}
L->oldpc = pc; /* 'pc' of last call to line hook */
L->oldpc = npci; /* 'pc' of last call to line hook */
}
if (L->status == LUA_YIELD) { /* did hook yield? */
if (counthook)
dependencies/lua-5.4/src/ldebug.h
#define pcRel(pc, p) (cast_int((pc) - (p)->code) - 1)
/* Active Lua function (given call info) */
#define ci_func(ci) (clLvalue(s2v((ci)->func)))
#define resethookcount(L) (L->hookcount = L->basehookcount)
/*
dependencies/lua-5.4/src/ldo.c
void luaD_shrinkstack (lua_State *L) {
int inuse = stackinuse(L);
int goodsize = inuse + (inuse / 8) + 2*EXTRA_STACK;
int goodsize = inuse + BASIC_STACK_SIZE;
if (goodsize > LUAI_MAXSTACK)
goodsize = LUAI_MAXSTACK; /* respect stack limit */
/* if thread is currently not handling a stack overflow and its
good size is smaller than current size, shrink its stack */
if (inuse <= (LUAI_MAXSTACK - EXTRA_STACK) &&
goodsize < L->stacksize)
if (inuse <= (LUAI_MAXSTACK - EXTRA_STACK) && goodsize < L->stacksize)
luaD_reallocstack(L, goodsize, 0); /* ok if that fails */
else /* don't change stack */
condmovestack(L,{},{}); /* (change only for debugging) */
......
ptrdiff_t oldtop = savestack(L, L->top); /* hook may change top */
int delta = 0;
if (isLuacode(ci)) {
Proto *p = clLvalue(s2v(ci->func))->p;
Proto *p = ci_func(ci)->p;
if (p->is_vararg)
delta = ci->u.l.nextraargs + p->numparams + 1;
if (L->top < ci->top)
......
luaD_hook(L, LUA_HOOKRET, -1, ftransfer, nres); /* call it */
ci->func -= delta;
}
if (isLua(ci->previous))
L->oldpc = ci->previous->u.l.savedpc; /* update 'oldpc' */
if (isLua(ci = ci->previous))
L->oldpc = pcRel(ci->u.l.savedpc, ci_func(ci)->p); /* update 'oldpc' */
return restorestack(L, oldtop);
}
......
f = fvalue(s2v(func));
Cfunc: {
int n; /* number of returns */
CallInfo *ci = next_ci(L);
checkstackp(L, LUA_MINSTACK, func); /* ensure minimum stack size */
CallInfo *ci;
checkstackGCp(L, LUA_MINSTACK, func); /* ensure minimum stack size */
L->ci = ci = next_ci(L);
ci->nresults = nresults;
ci->callstatus = CIST_C;
ci->top = L->top + LUA_MINSTACK;
ci->func = func;
L->ci = ci;
lua_assert(ci->top <= L->stack_last);
if (L->hookmask & LUA_MASKCALL) {
int narg = cast_int(L->top - func) - 1;
......
break;
}
case LUA_VLCL: { /* Lua function */
CallInfo *ci = next_ci(L);
CallInfo *ci;
Proto *p = clLvalue(s2v(func))->p;
int narg = cast_int(L->top - func) - 1; /* number of real arguments */
int nfixparams = p->numparams;
int fsize = p->maxstacksize; /* frame size */
checkstackp(L, fsize, func);
checkstackGCp(L, fsize, func);
L->ci = ci = next_ci(L);
ci->nresults = nresults;
ci->u.l.savedpc = p->code; /* starting point */
ci->callstatus = 0;
......
break;
}
default: { /* not a function */
checkstackp(L, 1, func); /* space for metamethod */
checkstackGCp(L, 1, func); /* space for metamethod */
luaD_tryfuncTM(L, func); /* try to get '__call' metamethod */
goto retry; /* try again with metamethod */
}
......
/*
** Similar to 'luaD_call', but does not allow yields during the call.
** If there is a stack overflow, freeing all CI structures will
** force the subsequent call to invoke 'luaE_extendCI', which then
** will raise any errors.
*/
void luaD_callnoyield (lua_State *L, StkId func, int nResults) {
incXCcalls(L);
if (getCcalls(L) <= CSTACKERR) /* possible stack overflow? */
luaE_freeCI(L);
if (getCcalls(L) <= CSTACKERR) { /* possible C stack overflow? */
luaE_exitCcall(L); /* to compensate decrement in next call */
luaE_enterCcall(L); /* check properly */
}
luaD_call(L, func, nResults);
decXCcalls(L);
}
......
if (from == NULL)
L->nCcalls = CSTACKTHREAD;
else /* correct 'nCcalls' for this thread */
L->nCcalls = getCcalls(from) + from->nci - L->nci - CSTACKCF;
L->nCcalls = getCcalls(from) - L->nci - CSTACKCF;
if (L->nCcalls <= CSTACKERR)
return resume_error(L, "C stack overflow", nargs);
luai_userstateresume(L, nargs);
......
LUA_API int lua_yieldk (lua_State *L, int nresults, lua_KContext ctx,
lua_KFunction k) {
CallInfo *ci = L->ci;
CallInfo *ci;
luai_userstateyield(L, nresults);
lua_lock(L);
ci = L->ci;
api_checknelems(L, nresults);
if (unlikely(!yieldable(L))) {
if (L != G(L)->mainthread)
dependencies/lua-5.4/src/ldo.h
** Macro to check stack size and grow stack if needed. Parameters
** 'pre'/'pos' allow the macro to preserve a pointer into the
** stack across reallocations, doing the work only when needed.
** It also allows the running of one GC step when the stack is
** reallocated.
** 'condmovestack' is used in heavy tests to force a stack reallocation
** at every check.
*/
......
/* macro to check stack size, preserving 'p' */
#define checkstackp(L,n,p) \
#define checkstackGCp(L,n,p) \
luaD_checkstackaux(L, n, \
ptrdiff_t t__ = savestack(L, p); /* save 'p' */ \
luaC_checkGC(L), /* stack grow uses memory */ \
......
/* macro to check stack size and GC */
#define checkstackGC(L,fsize) \
luaD_checkstackaux(L, (fsize), (void)0, luaC_checkGC(L))
luaD_checkstackaux(L, (fsize), luaC_checkGC(L), (void)0)
/* type of protected functions, to be ran by 'runprotected' */
dependencies/lua-5.4/src/lfunc.c
luaF_unlinkupval(uv);
setobj(L, slot, uv->v); /* move value to upvalue slot */
uv->v = slot; /* now current value lives here */
if (!iswhite(uv))
gray2black(uv); /* closed upvalues cannot be gray */
luaC_barrier(L, uv, slot);
if (!iswhite(uv)) { /* neither white nor dead? */
nw2black(uv); /* closed upvalues cannot be gray */
luaC_barrier(L, uv, slot);
}
}
return status;
}
dependencies/lua-5.4/src/lgc.c
#define PAUSEADJ 100
/* mask to erase all color bits (plus gen. related stuff) */
#define maskcolors (~(bitmask(BLACKBIT) | WHITEBITS | AGEBITS))
/* mask with all color bits */
#define maskcolors (bitmask(BLACKBIT) | WHITEBITS)
/* mask with all GC bits */
#define maskgcbits (maskcolors | AGEBITS)
/* macro to erase all color bits then sets only the current white bit */
/* macro to erase all color bits then set only the current white bit */
#define makewhite(g,x) \
(x->marked = cast_byte((x->marked & maskcolors) | luaC_white(g)))
(x->marked = cast_byte((x->marked & ~maskcolors) | luaC_white(g)))
/* make an object gray (neither white nor black) */
#define set2gray(x) resetbits(x->marked, maskcolors)
#define white2gray(x) resetbits(x->marked, WHITEBITS)
#define black2gray(x) resetbit(x->marked, BLACKBIT)
/* make an object black (coming from any color) */
#define set2black(x) \
(x->marked = cast_byte((x->marked & ~WHITEBITS) | bitmask(BLACKBIT)))
#define valiswhite(x) (iscollectable(x) && iswhite(gcvalue(x)))
......
#define keyiswhite(n) (keyiscollectable(n) && iswhite(gckey(n)))
#define checkconsistency(obj) \
lua_longassert(!iscollectable(obj) || righttt(obj))
/*
** Protected access to objects in values
*/
#define gcvalueN(o) (iscollectable(o) ? gcvalue(o) : NULL)
#define markvalue(g,o) { checkconsistency(o); \
#define markvalue(g,o) { checkliveness(g->mainthread,o); \
if (valiswhite(o)) reallymarkobject(g,gcvalue(o)); }
#define markkey(g, n) { if keyiswhite(n) reallymarkobject(g,gckey(n)); }
......
/*
** Link a collectable object 'o' with a known type into list pointed by 'p'.
** Link a collectable object 'o' with a known type into the list 'p'.
** (Must be a macro to access the 'gclist' field in different types.)
*/
#define linkgclist(o,p) ((o)->gclist = (p), (p) = obj2gco(o))
#define linkgclist(o,p) linkgclist_(obj2gco(o), &(o)->gclist, &(p))
static void linkgclist_ (GCObject *o, GCObject **pnext, GCObject **list) {
lua_assert(!isgray(o)); /* cannot be in a gray list */
*pnext = *list;
*list = o;
set2gray(o); /* now it is */
}
/*
** Link a generic collectable object 'o' into list pointed by 'p'.
** Link a generic collectable object 'o' into the list 'p'.
*/
#define linkobjgclist(o,p) (*getgclist(o) = (p), (p) = obj2gco(o))
#define linkobjgclist(o,p) linkgclist_(obj2gco(o), getgclist(o), &(p))
......
/*
** barrier that moves collector forward, that is, mark the white object
** 'v' being pointed by the black object 'o'. (If in sweep phase, clear
** the black object to white [sweep it] to avoid other barrier calls for
** this same object.) In the generational mode, 'v' must also become
** old, if 'o' is old; however, it cannot be changed directly to OLD,
** because it may still point to non-old objects. So, it is marked as
** OLD0. In the next cycle it will become OLD1, and in the next it
** will finally become OLD (regular old).
** Barrier that moves collector forward, that is, marks the white object
** 'v' being pointed by the black object 'o'. In the generational
** mode, 'v' must also become old, if 'o' is old; however, it cannot
** be changed directly to OLD, because it may still point to non-old
** objects. So, it is marked as OLD0. In the next cycle it will become
** OLD1, and in the next it will finally become OLD (regular old). By
** then, any object it points to will also be old. If called in the
** incremental sweep phase, it clears the black object to white (sweep
** it) to avoid other barrier calls for this same object. (That cannot
** be done is generational mode, as its sweep does not distinguish
** whites from deads.)
*/
void luaC_barrier_ (lua_State *L, GCObject *o, GCObject *v) {
global_State *g = G(L);
......
}
else { /* sweep phase */
lua_assert(issweepphase(g));
makewhite(g, o); /* mark main obj. as white to avoid other barriers */
if (g->gckind == KGC_INC) /* incremental mode? */
makewhite(g, o); /* mark 'o' as white to avoid other barriers */
}
}
......
void luaC_barrierback_ (lua_State *L, GCObject *o) {
global_State *g = G(L);
lua_assert(isblack(o) && !isdead(g, o));
lua_assert(g->gckind != KGC_GEN || (isold(o) && getage(o) != G_TOUCHED1));
if (getage(o) != G_TOUCHED2) /* not already in gray list? */
linkobjgclist(o, g->grayagain); /* link it in 'grayagain' */
black2gray(o); /* make object gray (again) */
setage(o, G_TOUCHED1); /* touched in current cycle */
lua_assert((g->gckind == KGC_GEN) == (isold(o) && getage(o) != G_TOUCHED1));
if (getage(o) == G_TOUCHED2) /* already in gray list? */
set2gray(o); /* make it gray to become touched1 */
else /* link it in 'grayagain' and paint it gray */
linkobjgclist(o, g->grayagain);
if (isold(o)) /* generational mode? */
setage(o, G_TOUCHED1); /* touched in current cycle */
}
void luaC_fix (lua_State *L, GCObject *o) {
global_State *g = G(L);
lua_assert(g->allgc == o); /* object must be 1st in 'allgc' list! */
white2gray(o); /* they will be gray forever */
set2gray(o); /* they will be gray forever */
setage(o, G_OLD); /* and old forever */
g->allgc = o->next; /* remove object from 'allgc' list */
o->next = g->fixedgc; /* link it to 'fixedgc' list */
......
/*
** Mark an object. Userdata, strings, and closed upvalues are visited
** and turned black here. Other objects are marked gray and added
** to appropriate list to be visited (and turned black) later. (Open
** upvalues are already linked in 'headuv' list. They are kept gray
** to avoid barriers, as their values will be revisited by the thread.)
** Mark an object. Userdata with no user values, strings, and closed
** upvalues are visited and turned black here. Open upvalues are
** already indirectly linked through their respective threads in the
** 'twups' list, so they don't go to the gray list; nevertheless, they
** are kept gray to avoid barriers, as their values will be revisited
** by the thread or by 'remarkupvals'. Other objects are added to the
** gray list to be visited (and turned black) later. Both userdata and
** upvalues can call this function recursively, but this recursion goes
** for at most two levels: An upvalue cannot refer to another upvalue
** (only closures can), and a userdata's metatable must be a table.
*/
static void reallymarkobject (global_State *g, GCObject *o) {
white2gray(o);
switch (o->tt) {
case LUA_VSHRSTR:
case LUA_VLNGSTR: {
gray2black(o);
set2black(o); /* nothing to visit */
break;
}
case LUA_VUPVAL: {
UpVal *uv = gco2upv(o);
if (!upisopen(uv)) /* open upvalues are kept gray */
gray2black(o);
if (upisopen(uv))
set2gray(uv); /* open upvalues are kept gray */
else
set2black(o); /* closed upvalues are visited here */
markvalue(g, uv->v); /* mark its content */
break;
}
......
Udata *u = gco2u(o);
if (u->nuvalue == 0) { /* no user values? */
markobjectN(g, u->metatable); /* mark its metatable */
gray2black(o); /* nothing else to mark */
set2black(o); /* nothing else to mark */
break;
}
/* else... */
} /* FALLTHROUGH */
case LUA_VLCL: case LUA_VCCL: case LUA_VTABLE:
case LUA_VTHREAD: case LUA_VPROTO: {
linkobjgclist(o, g->gray);
linkobjgclist(o, g->gray); /* to be visited later */
break;
}
default: lua_assert(0); break;
......
/*
** Mark all values stored in marked open upvalues from non-marked threads.
** (Values from marked threads were already marked when traversing the
** thread.) Remove from the list threads that no longer have upvalues and
** not-marked threads.
** For each non-marked thread, simulates a barrier between each open
** upvalue and its value. (If the thread is collected, the value will be
** assigned to the upvalue, but then it can be too late for the barrier
** to act. The "barrier" does not need to check colors: A non-marked
** thread must be young; upvalues cannot be older than their threads; so
** any visited upvalue must be young too.) Also removes the thread from
** the list, as it was already visited. Removes also threads with no
** upvalues, as they have nothing to be checked. (If the thread gets an
** upvalue later, it will be linked in the list again.)
*/
static int remarkupvals (global_State *g) {
lua_State *thread;
lua_State **p = &g->twups;
int work = 0;
int work = 0; /* estimate of how much work was done here */
while ((thread = *p) != NULL) {
work++;
lua_assert(!isblack(thread)); /* threads are never black */
if (isgray(thread) && thread->openupval != NULL)
if (!iswhite(thread) && thread->openupval != NULL)
p = &thread->twups; /* keep marked thread with upvalues in the list */
else { /* thread is not marked or without upvalues */
UpVal *uv;
lua_assert(!isold(thread) || thread->openupval == NULL);
*p = thread->twups; /* remove thread from the list */
thread->twups = thread; /* mark that it is out of list */
for (uv = thread->openupval; uv != NULL; uv = uv->u.open.next) {
lua_assert(getage(uv) <= getage(thread));
work++;
if (!iswhite(uv)) /* upvalue already visited? */
if (!iswhite(uv)) { /* upvalue already visited? */
lua_assert(upisopen(uv) && isgray(uv));
markvalue(g, uv->v); /* mark its value */
}
}
}
}
......
}
static void cleargraylists (global_State *g) {
g->gray = g->grayagain = NULL;
g->weak = g->allweak = g->ephemeron = NULL;
}
/*
** mark root set and reset all gray lists, to start a new collection
*/
static void restartcollection (global_State *g) {
g->gray = g->grayagain = NULL;
g->weak = g->allweak = g->ephemeron = NULL;
cleargraylists(g);
markobject(g, g->mainthread);
markvalue(g, &g->l_registry);
markmt(g);
......
** =======================================================
*/
/*
** Check whether object 'o' should be kept in the 'grayagain' list for
** post-processing by 'correctgraylist'. (It could put all old objects
** in the list and leave all the work to 'correctgraylist', but it is
** more efficient to avoid adding elements that will be removed.) Only
** TOUCHED1 objects need to be in the list. TOUCHED2 doesn't need to go
** back to a gray list, but then it must become OLD. (That is what
** 'correctgraylist' does when it finds a TOUCHED2 object.)
*/
static void genlink (global_State *g, GCObject *o) {
lua_assert(isblack(o));
if (getage(o) == G_TOUCHED1) { /* touched in this cycle? */
linkobjgclist(o, g->grayagain); /* link it back in 'grayagain' */
} /* everything else do not need to be linked back */
else if (getage(o) == G_TOUCHED2)
changeage(o, G_TOUCHED2, G_OLD); /* advance age */
}
/*
** Traverse a table with weak values and link it to proper list. During
** propagate phase, keep it in 'grayagain' list, to be revisited in the
......
** the atomic phase, if table has any white->white entry, it has to
** be revisited during ephemeron convergence (as that key may turn
** black). Otherwise, if it has any white key, table has to be cleared
** (in the atomic phase). In generational mode, it (like all visited
** tables) must be kept in some gray list for post-processing.
** (in the atomic phase). In generational mode, some tables
** must be kept in some gray list for post-processing; this is done
** by 'genlink'.
*/
static int traverseephemeron (global_State *g, Table *h, int inv) {
int marked = 0; /* true if an object is marked in this traversal */
......
linkgclist(h, g->ephemeron); /* have to propagate again */
else if (hasclears) /* table has white keys? */
linkgclist(h, g->allweak); /* may have to clean white keys */
else if (g->gckind == KGC_GEN)
linkgclist(h, g->grayagain); /* keep it in some list */
else
gray2black(h);
genlink(g, obj2gco(h)); /* check whether collector still needs to see it */
return marked;
}
......
markvalue(g, gval(n));
}
}
if (g->gckind == KGC_GEN) {
linkgclist(h, g->grayagain); /* keep it in some gray list */
black2gray(h);
}
genlink(g, obj2gco(h));
}
......
(cast_void(weakkey = strchr(svalue(mode), 'k')),
cast_void(weakvalue = strchr(svalue(mode), 'v')),
(weakkey || weakvalue))) { /* is really weak? */
black2gray(h); /* keep table gray */
if (!weakkey) /* strong keys? */
traverseweakvalue(g, h);
else if (!weakvalue) /* strong values? */
......
markobjectN(g, u->metatable); /* mark its metatable */
for (i = 0; i < u->nuvalue; i++)
markvalue(g, &u->uv[i].uv);
if (g->gckind == KGC_GEN) {
linkgclist(u, g->grayagain); /* keep it in some gray list */
black2gray(u);
}
genlink(g, obj2gco(u));
return 1 + u->nuvalue;
}
......
/*
** Traverse a thread, marking the elements in the stack up to its top
** and cleaning the rest of the stack in the final traversal.
** That ensures that the entire stack have valid (non-dead) objects.
** and cleaning the rest of the stack in the final traversal. That
** ensures that the entire stack have valid (non-dead) objects.
** Threads have no barriers. In gen. mode, old threads must be visited
** at every cycle, because they might point to young objects. In inc.
** mode, the thread can still be modified before the end of the cycle,
** and therefore it must be visited again in the atomic phase. To ensure
** these visits, threads must return to a gray list if they are not new
** (which can only happen in generational mode) or if the traverse is in
** the propagate phase (which can only happen in incremental mode).
*/
static int traversethread (global_State *g, lua_State *th) {
UpVal *uv;
StkId o = th->stack;
if (isold(th) || g->gcstate == GCSpropagate)
linkgclist(th, g->grayagain); /* insert into 'grayagain' list */
if (o == NULL)
return 1; /* stack not completely built yet */
lua_assert(g->gcstate == GCSatomic ||
......
/*
** traverse one gray object, turning it to black (except for threads,
** which are always gray).
** traverse one gray object, turning it to black.
*/
static lu_mem propagatemark (global_State *g) {
GCObject *o = g->gray;
gray2black(o);
nw2black(o);
g->gray = *getgclist(o); /* remove from 'gray' list */
switch (o->tt) {
case LUA_VTABLE: return traversetable(g, gco2t(o));
......
case LUA_VLCL: return traverseLclosure(g, gco2lcl(o));
case LUA_VCCL: return traverseCclosure(g, gco2ccl(o));
case LUA_VPROTO: return traverseproto(g, gco2p(o));
case LUA_VTHREAD: {
lua_State *th = gco2th(o);
linkgclist(th, g->grayagain); /* insert into 'grayagain' list */
black2gray(o);
return traversethread(g, th);
}
case LUA_VTHREAD: return traversethread(g, gco2th(o));
default: lua_assert(0); return 0;
}
}
......
g->ephemeron = NULL; /* tables may return to this list when traversed */
changed = 0;
while ((w = next) != NULL) { /* for each ephemeron table */
next = gco2t(w)->gclist; /* list is rebuilt during loop */
if (traverseephemeron(g, gco2t(w), dir)) { /* marked some value? */
Table *h = gco2t(w);
next = h->gclist; /* list is rebuilt during loop */
nw2black(h); /* out of the list (for now) */
if (traverseephemeron(g, h, dir)) { /* marked some value? */
propagateall(g); /* propagate changes */
changed = 1; /* will have to revisit all ephemeron tables */
}
......
freeobj(L, curr); /* erase 'curr' */
}
else { /* change mark to 'white' */
curr->marked = cast_byte((marked & maskcolors) | white);
curr->marked = cast_byte((marked & ~maskgcbits) | white);
p = &curr->next; /* go to next element */
}
}
......
resetbit(o->marked, FINALIZEDBIT); /* object is "normal" again */
if (issweepphase(g))
makewhite(g, o); /* "sweep" object */
else if (getage(o) == G_OLD1)
g->firstold1 = o; /* it is the first OLD1 object in the list */
return o;
}
......
/*
** Move all unreachable objects (or 'all' objects) that need
** finalization from list 'finobj' to list 'tobefnz' (to be finalized).
** (Note that objects after 'finobjold' cannot be white, so they
** don't need to be traversed. In incremental mode, 'finobjold' is NULL,
** (Note that objects after 'finobjold1' cannot be white, so they
** don't need to be traversed. In incremental mode, 'finobjold1' is NULL,
** so the whole list is traversed.)
*/
static void separatetobefnz (global_State *g, int all) {
GCObject *curr;
GCObject **p = &g->finobj;
GCObject **lastnext = findlast(&g->tobefnz);
while ((curr = *p) != g->finobjold) { /* traverse all finalizable objects */
while ((curr = *p) != g->finobjold1) { /* traverse all finalizable objects */
lua_assert(tofinalize(curr));
if (!(iswhite(curr) || all)) /* not being collected? */
p = &curr->next; /* don't bother with it */
......
}
/*
** If pointer 'p' points to 'o', move it to the next element.
*/
static void checkpointer (GCObject **p, GCObject *o) {
if (o == *p)
*p = o->next;
}
/*
** Correct pointers to objects inside 'allgc' list when
** object 'o' is being removed from the list.
*/
static void correctpointers (global_State *g, GCObject *o) {
checkpointer(&g->survival, o);
checkpointer(&g->old1, o);
checkpointer(&g->reallyold, o);
checkpointer(&g->firstold1, o);
}
/*
** if object 'o' has a finalizer, remove it from 'allgc' list (must
** search the list to find it) and link it in 'finobj' list.
......
if (g->sweepgc == &o->next) /* should not remove 'sweepgc' object */
g->sweepgc = sweeptolive(L, g->sweepgc); /* change 'sweepgc' */
}
else { /* correct pointers into 'allgc' list, if needed */
if (o == g->survival)
g->survival = o->next;
if (o == g->old)
g->old = o->next;
if (o == g->reallyold)
g->reallyold = o->next;
}
else
correctpointers(g, o);
/* search for pointer pointing to 'o' */
for (p = &g->allgc; *p != o; p = &(*p)->next) { /* empty */ }
*p = o->next; /* remove 'o' from 'allgc' list */
......
static void setpause (global_State *g);
/* mask to erase all color bits, not changing gen-related stuff */
#define maskgencolors (~(bitmask(BLACKBIT) | WHITEBITS))
/*
** Sweep a list of objects, deleting dead ones and turning
** the non dead to old (without changing their colors).
** Sweep a list of objects to enter generational mode. Deletes dead
** objects and turns the non dead to old. All non-dead threads---which
** are now old---must be in a gray list. Everything else is not in a
** gray list. Open upvalues are also kept gray.
*/
static void sweep2old (lua_State *L, GCObject **p) {
GCObject *curr;
global_State *g = G(L);
while ((curr = *p) != NULL) {
if (iswhite(curr)) { /* is 'curr' dead? */
lua_assert(isdead(G(L), curr));
lua_assert(isdead(g, curr));
*p = curr->next; /* remove 'curr' from list */
freeobj(L, curr); /* erase 'curr' */
}
else { /* all surviving objects become old */
setage(curr, G_OLD);
if (curr->tt == LUA_VTHREAD) { /* threads must be watched */
lua_State *th = gco2th(curr);
linkgclist(th, g->grayagain); /* insert into 'grayagain' list */
}
else if (curr->tt == LUA_VUPVAL && upisopen(gco2upv(curr)))
set2gray(curr); /* open upvalues are always gray */
else /* everything else is black */
nw2black(curr);
p = &curr->next; /* go to next element */
}
}
......
** during the sweep. So, any white object must be dead.) For
** non-dead objects, advance their ages and clear the color of
** new objects. (Old objects keep their colors.)
** The ages of G_TOUCHED1 and G_TOUCHED2 objects cannot be advanced
** here, because these old-generation objects are usually not swept
** here. They will all be advanced in 'correctgraylist'. That function
** will also remove objects turned white here from any gray list.
*/
static GCObject **sweepgen (lua_State *L, global_State *g, GCObject **p,
GCObject *limit) {
GCObject *limit, GCObject **pfirstold1) {
static const lu_byte nextage[] = {
G_SURVIVAL, /* from G_NEW */
G_OLD1, /* from G_SURVIVAL */
......
freeobj(L, curr); /* erase 'curr' */
}
else { /* correct mark and age */
if (getage(curr) == G_NEW)
curr->marked = cast_byte((curr->marked & maskgencolors) | white);
setage(curr, nextage[getage(curr)]);
if (getage(curr) == G_NEW) { /* new objects go back to white */
int marked = curr->marked & ~maskgcbits; /* erase GC bits */
curr->marked = cast_byte(marked | G_SURVIVAL | white);
}
else { /* all other objects will be old, and so keep their color */
setage(curr, nextage[getage(curr)]);
if (getage(curr) == G_OLD1 && *pfirstold1 == NULL)
*pfirstold1 = curr; /* first OLD1 object in the list */
}
p = &curr->next; /* go to next element */
}
}
......
/*
** Traverse a list making all its elements white and clearing their
** age.
** age. In incremental mode, all objects are 'new' all the time,
** except for fixed strings (which are always old).
*/
static void whitelist (global_State *g, GCObject *p) {
int white = luaC_white(g);
for (; p != NULL; p = p->next)
p->marked = cast_byte((p->marked & maskcolors) | white);
p->marked = cast_byte((p->marked & ~maskgcbits) | white);
}
/*
** Correct a list of gray objects.
** Correct a list of gray objects. Return pointer to where rest of the
** list should be linked.
** Because this correction is done after sweeping, young objects might
** be turned white and still be in the list. They are only removed.
** For tables and userdata, advance 'touched1' to 'touched2'; 'touched2'
** objects become regular old and are removed from the list.
** For threads, just remove white ones from the list.
** 'TOUCHED1' objects are advanced to 'TOUCHED2' and remain on the list;
** Non-white threads also remain on the list; 'TOUCHED2' objects become
** regular old; they and anything else are removed from the list.
*/
static GCObject **correctgraylist (GCObject **p) {
GCObject *curr;
while ((curr = *p) != NULL) {
switch (curr->tt) {
case LUA_VTABLE: case LUA_VUSERDATA: {
GCObject **next = getgclist(curr);
if (getage(curr) == G_TOUCHED1) { /* touched in this cycle? */
lua_assert(isgray(curr));
gray2black(curr); /* make it black, for next barrier */
changeage(curr, G_TOUCHED1, G_TOUCHED2);
p = next; /* go to next element */
}
else { /* not touched in this cycle */
if (!iswhite(curr)) { /* not white? */
lua_assert(isold(curr));
if (getage(curr) == G_TOUCHED2) /* advance from G_TOUCHED2... */
changeage(curr, G_TOUCHED2, G_OLD); /* ... to G_OLD */
gray2black(curr); /* make it black */
}
/* else, object is white: just remove it from this list */
*p = *next; /* remove 'curr' from gray list */
}
break;
}
case LUA_VTHREAD: {
lua_State *th = gco2th(curr);
lua_assert(!isblack(th));
if (iswhite(th)) /* new object? */
*p = th->gclist; /* remove from gray list */
else /* old threads remain gray */
p = &th->gclist; /* go to next element */
break;
}
default: lua_assert(0); /* nothing more could be gray here */
GCObject **next = getgclist(curr);
if (iswhite(curr))
goto remove; /* remove all white objects */
else if (getage(curr) == G_TOUCHED1) { /* touched in this cycle? */
lua_assert(isgray(curr));
nw2black(curr); /* make it black, for next barrier */
changeage(curr, G_TOUCHED1, G_TOUCHED2);
goto remain; /* keep it in the list and go to next element */
}
else if (curr->tt == LUA_VTHREAD) {
lua_assert(isgray(curr));
goto remain; /* keep non-white threads on the list */
}
else { /* everything else is removed */
lua_assert(isold(curr)); /* young objects should be white here */
if (getage(curr) == G_TOUCHED2) /* advance from TOUCHED2... */
changeage(curr, G_TOUCHED2, G_OLD); /* ... to OLD */
nw2black(curr); /* make object black (to be removed) */
goto remove;
}
remove: *p = *next; continue;
remain: p = next; continue;
}
return p;
}
......
/*
** Mark 'OLD1' objects when starting a new young collection.
** Mark black 'OLD1' objects when starting a new young collection.
** Gray objects are already in some gray list, and so will be visited
** in the atomic step.
*/
......
for (p = from; p != to; p = p->next) {
if (getage(p) == G_OLD1) {
lua_assert(!iswhite(p));
if (isblack(p)) {
black2gray(p); /* should be '2white', but gray works too */
changeage(p, G_OLD1, G_OLD); /* now they are old */
if (isblack(p))
reallymarkobject(g, p);
}
}
}
}
......
/*
** Does a young collection. First, mark 'OLD1' objects. (Only survival
** and "recent old" lists can contain 'OLD1' objects. New lists cannot
** contain 'OLD1' objects, at most 'OLD0' objects that were already
** visited when marked old.) Then does the atomic step. Then,
** sweep all lists and advance pointers. Finally, finish the collection.
** Does a young collection. First, mark 'OLD1' objects. Then does the
** atomic step. Then, sweep all lists and advance pointers. Finally,
** finish the collection.
*/
static void youngcollection (lua_State *L, global_State *g) {
GCObject **psurvival; /* to point to first non-dead survival object */
GCObject *dummy; /* dummy out parameter to 'sweepgen' */
lua_assert(g->gcstate == GCSpropagate);
markold(g, g->survival, g->reallyold);
if (g->firstold1) { /* are there regular OLD1 objects? */
markold(g, g->firstold1, g->reallyold); /* mark them */
g->firstold1 = NULL; /* no more OLD1 objects (for now) */
}
markold(g, g->finobj, g->finobjrold);
markold(g, g->tobefnz, NULL);
atomic(L);
/* sweep nursery and get a pointer to its last live element */
psurvival = sweepgen(L, g, &g->allgc, g->survival);
/* sweep 'survival' and 'old' */
sweepgen(L, g, psurvival, g->reallyold);
g->reallyold = g->old;
g->old = *psurvival; /* 'survival' survivals are old now */
g->gcstate = GCSswpallgc;
psurvival = sweepgen(L, g, &g->allgc, g->survival, &g->firstold1);
/* sweep 'survival' */
sweepgen(L, g, psurvival, g->old1, &g->firstold1);
g->reallyold = g->old1;
g->old1 = *psurvival; /* 'survival' survivals are old now */
g->survival = g->allgc; /* all news are survivals */
/* repeat for 'finobj' lists */
psurvival = sweepgen(L, g, &g->finobj, g->finobjsur);
/* sweep 'survival' and 'old' */
sweepgen(L, g, psurvival, g->finobjrold);
g->finobjrold = g->finobjold;
g->finobjold = *psurvival; /* 'survival' survivals are old now */
dummy = NULL; /* no 'firstold1' optimization for 'finobj' lists */
psurvival = sweepgen(L, g, &g->finobj, g->finobjsur, &dummy);
/* sweep 'survival' */
sweepgen(L, g, psurvival, g->finobjold1, &dummy);
g->finobjrold = g->finobjold1;
g->finobjold1 = *psurvival; /* 'survival' survivals are old now */
g->finobjsur = g->finobj; /* all news are survivals */
sweepgen(L, g, &g->tobefnz, NULL);
sweepgen(L, g, &g->tobefnz, NULL, &dummy);
finishgencycle(L, g);
}
/*
** Clears all gray lists, sweeps objects, and prepare sublists to enter
** generational mode. The sweeps remove dead objects and turn all
** surviving objects to old. Threads go back to 'grayagain'; everything
** else is turned black (not in any gray list).
*/
static void atomic2gen (lua_State *L, global_State *g) {
cleargraylists(g);
/* sweep all elements making them old */
g->gcstate = GCSswpallgc;
sweep2old(L, &g->allgc);
/* everything alive now is old */
g->reallyold = g->old = g->survival = g->allgc;
g->reallyold = g->old1 = g->survival = g->allgc;
g->firstold1 = NULL; /* there are no OLD1 objects anywhere */
/* repeat for 'finobj' lists */
sweep2old(L, &g->finobj);
g->finobjrold = g->finobjold = g->finobjsur = g->finobj;
g->finobjrold = g->finobjold1 = g->finobjsur = g->finobj;
sweep2old(L, &g->tobefnz);
......
/*
** Enter generational mode. Must go until the end of an atomic cycle
** to ensure that all threads and weak tables are in the gray lists.
** Then, turn all objects into old and finishes the collection.
** to ensure that all objects are correctly marked and weak tables
** are cleared. Then, turn all objects into old and finishes the
** collection.
*/
static lu_mem entergen (lua_State *L, global_State *g) {
lu_mem numobjs;
......
*/
static void enterinc (global_State *g) {
whitelist(g, g->allgc);
g->reallyold = g->old = g->survival = NULL;
g->reallyold = g->old1 = g->survival = NULL;
whitelist(g, g->finobj);
whitelist(g, g->tobefnz);
g->finobjrold = g->finobjold = g->finobjsur = NULL;
g->finobjrold = g->finobjold1 = g->finobjsur = NULL;
g->gcstate = GCSpause;
g->gckind = KGC_INC;
g->lastatomic = 0;
dependencies/lua-5.4/src/lgc.h
#include "lstate.h"
/*
** Collectable objects may have one of three colors: white, which
** means the object is not marked; gray, which means the
** object is marked, but its references may be not marked; and
** black, which means that the object and all its references are marked.
** The main invariant of the garbage collector, while marking objects,
** is that a black object can never point to a white one. Moreover,
** any gray object must be in a "gray list" (gray, grayagain, weak,
** allweak, ephemeron) so that it can be visited again before finishing
** the collection cycle. These lists have no meaning when the invariant
** is not being enforced (e.g., sweep phase).
** Collectable objects may have one of three colors: white, which means
** the object is not marked; gray, which means the object is marked, but
** its references may be not marked; and black, which means that the
** object and all its references are marked. The main invariant of the
** garbage collector, while marking objects, is that a black object can
** never point to a white one. Moreover, any gray object must be in a
** "gray list" (gray, grayagain, weak, allweak, ephemeron) so that it
** can be visited again before finishing the collection cycle. (Open
** upvalues are an exception to this rule.) These lists have no meaning
** when the invariant is not being enforced (e.g., sweep phase).
*/
......
/*
** Layout for bit use in 'marked' field. First three bits are
** used for object "age" in generational mode. Last bit is free
** to be used by respective objects.
** used for object "age" in generational mode. Last bit is used
** by tests.
*/
#define WHITE0BIT 3 /* object is white (type 0) */
#define WHITE1BIT 4 /* object is white (type 1) */
#define BLACKBIT 5 /* object is black */
#define FINALIZEDBIT 6 /* object has been marked for finalization */
#define TESTBIT 7
#define WHITEBITS bit2mask(WHITE0BIT, WHITE1BIT)
......
#define isdead(g,v) isdeadm(otherwhite(g), (v)->marked)
#define changewhite(x) ((x)->marked ^= WHITEBITS)
#define gray2black(x) l_setbit((x)->marked, BLACKBIT)
#define nw2black(x) \
check_exp(!iswhite(x), l_setbit((x)->marked, BLACKBIT))
#define luaC_white(g) cast_byte((g)->currentwhite & WHITEBITS)
dependencies/lua-5.4/src/liolib.c
** =======================================================
*/
#if !defined(l_checkmodep)
/* By default, Lua accepts only "r" or "w" as mode */
#define l_checkmodep(m) ((m[0] == 'r' || m[0] == 'w') && m[1] == '\0')
#endif
#if !defined(l_popen) /* { */
#if defined(LUA_USE_POPEN) /* { */
......
const char *filename = luaL_checkstring(L, 1);
const char *mode = luaL_optstring(L, 2, "r");
LStream *p = newprefile(L);
luaL_argcheck(L, l_checkmodep(mode), 2, "invalid mode");
p->f = l_popen(L, filename, mode);
p->closef = &io_pclose;
return (p->f == NULL) ? luaL_fileresult(L, 0, filename) : 1;
dependencies/lua-5.4/src/llex.c
const char *luaX_token2str (LexState *ls, int token) {
if (token < FIRST_RESERVED) { /* single-byte symbols? */
lua_assert(token == cast_uchar(token));
if (lisprint(token))
return luaO_pushfstring(ls->L, "'%c'", token);
else /* control character */
dependencies/lua-5.4/src/llex.h
#ifndef llex_h
#define llex_h
#include <limits.h>
#include "lobject.h"
#include "lzio.h"
#define FIRST_RESERVED 257
/*
** Single-char tokens (terminal symbols) are represented by their own
** numeric code. Other tokens start at the following value.
*/
#define FIRST_RESERVED (UCHAR_MAX + 1)
#if !defined(LUA_ENV)
dependencies/lua-5.4/src/llimits.h
typedef LUAI_UACINT l_uacInt;
/* internal assertions for in-house debugging */
/*
** Internal assertions for in-house debugging
*/
#if defined LUAI_ASSERT
#undef NDEBUG
#include <assert.h>
#define lua_assert(c) assert(c)
#endif
#if defined(lua_assert)
#define check_exp(c,e) (lua_assert(c), (e))
/* to avoid problems with conditions too long */
dependencies/lua-5.4/src/lmem.c
#include "lstate.h"
#if defined(HARDMEMTESTS)
#if defined(EMERGENCYGCTESTS)
/*
** First allocation will fail whenever not building initial state
** and not shrinking a block. (This fail will trigger 'tryagain' and
dependencies/lua-5.4/src/lobject.c
/* }====================================================== */
/* maximum length of a numeral */
/* maximum length of a numeral to be converted to a number */
#if !defined (L_MAXLENNUM)
#define L_MAXLENNUM 200
#endif
/*
** Convert string 's' to a Lua number (put in 'result'). Return NULL on
** fail or the address of the ending '\0' on success. ('mode' == 'x')
** means a hexadecimal numeral.
*/
static const char *l_str2dloc (const char *s, lua_Number *result, int mode) {
char *endptr;
*result = (mode == 'x') ? lua_strx2number(s, &endptr) /* try to convert */
: lua_str2number(s, &endptr);
if (endptr == s) return NULL; /* nothing recognized? */
while (lisspace(cast_uchar(*endptr))) endptr++; /* skip trailing spaces */
return (*endptr == '\0') ? endptr : NULL; /* OK if no trailing characters */
return (*endptr == '\0') ? endptr : NULL; /* OK iff no trailing chars */
}
/*
** Convert string 's' to a Lua number (put in 'result'). Return NULL
** on fail or the address of the ending '\0' on success.
** 'pmode' points to (and 'mode' contains) special things in the string:
** - 'x'/'X' means a hexadecimal numeral
** - 'n'/'N' means 'inf' or 'nan' (which should be rejected)
** - '.' just optimizes the search for the common case (nothing special)
** Convert string 's' to a Lua number (put in 'result') handling the
** current locale.
** This function accepts both the current locale or a dot as the radix
** mark. If the conversion fails, it may mean number has a dot but
** locale accepts something else. In that case, the code copies 's'
** to a buffer (because 's' is read-only), changes the dot to the
** current locale radix mark, and tries to convert again.
** The variable 'mode' checks for special characters in the string:
** - 'n' means 'inf' or 'nan' (which should be rejected)
** - 'x' means a hexadecimal numeral
** - '.' just optimizes the search for the common case (no special chars)
*/
static const char *l_str2d (const char *s, lua_Number *result) {
const char *endptr;
const char *pmode = strpbrk(s, ".xXnN");
const char *pmode = strpbrk(s, ".xXnN"); /* look for special chars */
int mode = pmode ? ltolower(cast_uchar(*pmode)) : 0;
if (mode == 'n') /* reject 'inf' and 'nan' */
return NULL;
......
}
/* maximum length of the conversion of a number to a string */
#define MAXNUMBER2STR 50
/*
** Maximum length of the conversion of a number to a string. Must be
** enough to accommodate both LUA_INTEGER_FMT and LUA_NUMBER_FMT.
** (For a long long int, this is 19 digits plus a sign and a final '\0',
** adding to 21. For a long double, it can go to a sign, 33 digits,
** the dot, an exponent letter, an exponent sign, 5 exponent digits,
** and a final '\0', adding to 43.)
*/
#define MAXNUMBER2STR 44
/*
......
*/
/* size for buffer space used by 'luaO_pushvfstring' */
#define BUFVFS 400
#define BUFVFS 200
/* buffer used by 'luaO_pushvfstring' */
typedef struct BuffFS {
......
/*
** Push given string to the stack, as part of the buffer. If the stack
** is almost full, join all partial strings in the stack into one.
** Push given string to the stack, as part of the buffer, and
** join the partial strings in the stack into one.
*/
static void pushstr (BuffFS *buff, const char *str, size_t l) {
lua_State *L = buff->L;
setsvalue2s(L, L->top, luaS_newlstr(L, str, l));
L->top++; /* may use one extra slot */
buff->pushed++;
if (buff->pushed > 1 && L->top + 1 >= L->stack_last) {
luaV_concat(L, buff->pushed); /* join all partial results into one */
buff->pushed = 1;
}
luaV_concat(L, buff->pushed); /* join partial results into one */
buff->pushed = 1;
}
......
}
addstr2buff(&buff, fmt, strlen(fmt)); /* rest of 'fmt' */
clearbuff(&buff); /* empty buffer into the stack */
if (buff.pushed > 1)
luaV_concat(L, buff.pushed); /* join all partial results */
lua_assert(buff.pushed == 1);
return svalue(s2v(L->top - 1));
}
dependencies/lua-5.4/src/lobject.h
/*
** Any value being manipulated by the program either is non
** collectable, or the collectable object has the right tag
** and it is not dead.
** and it is not dead. The option 'L == NULL' allows other
** macros using this one to be used where L is not available.
*/
#define checkliveness(L,obj) \
((void)L, lua_longassert(!iscollectable(obj) || \
......
*/
#define BITRAS (1 << 7)
#define isrealasize(t) (!((t)->marked & BITRAS))
#define setrealasize(t) ((t)->marked &= cast_byte(~BITRAS))
#define setnorealasize(t) ((t)->marked |= BITRAS)
#define isrealasize(t) (!((t)->flags & BITRAS))
#define setrealasize(t) ((t)->flags &= cast_byte(~BITRAS))
#define setnorealasize(t) ((t)->flags |= BITRAS)
typedef struct Table {
dependencies/lua-5.4/src/lstate.c
L->openupval = NULL;
L->status = LUA_OK;
L->errfunc = 0;
L->oldpc = 0;
}
......
LUA_API lua_State *lua_newthread (lua_State *L) {
global_State *g = G(L);
global_State *g;
lua_State *L1;
lua_lock(L);
g = G(L);
luaC_checkGC(L);
/* create new thread */
L1 = &cast(LX *, luaM_newobject(L, LUA_TTHREAD, sizeof(LX)))->l;
......
g->allgc = obj2gco(L); /* by now, only object is the main thread */
L->next = NULL;
g->Cstacklimit = L->nCcalls = LUAI_MAXCSTACK + CSTACKERR;
incnny(L); /* main thread is always non yieldable */
g->frealloc = f;
g->ud = ud;
g->warnf = NULL;
......
g->gckind = KGC_INC;
g->gcemergency = 0;
g->finobj = g->tobefnz = g->fixedgc = NULL;
g->survival = g->old = g->reallyold = NULL;
g->finobjsur = g->finobjold = g->finobjrold = NULL;
g->firstold1 = g->survival = g->old1 = g->reallyold = NULL;
g->finobjsur = g->finobjold1 = g->finobjrold = NULL;
g->sweepgc = NULL;
g->gray = g->grayagain = NULL;
g->weak = g->ephemeron = g->allweak = NULL;
......
LUA_API void lua_close (lua_State *L) {
L = G(L)->mainthread; /* only the main thread can be closed */
lua_lock(L);
L = G(L)->mainthread; /* only the main thread can be closed */
close_state(L);
}
dependencies/lua-5.4/src/lstate.h
**
** 'allgc' -> 'survival': new objects;
** 'survival' -> 'old': objects that survived one collection;
** 'old' -> 'reallyold': objects that became old in last collection;
** 'old1' -> 'reallyold': objects that became old in last collection;
** 'reallyold' -> NULL: objects old for more than one cycle.
**
** 'finobj' -> 'finobjsur': new objects marked for finalization;
** 'finobjsur' -> 'finobjold': survived """";
** 'finobjold' -> 'finobjrold': just old """";
** 'finobjsur' -> 'finobjold1': survived """";
** 'finobjold1' -> 'finobjrold': just old """";
** 'finobjrold' -> NULL: really old """".
**
** All lists can contain elements older than their main ages, due
** to 'luaC_checkfinalizer' and 'udata2finalize', which move
** objects between the normal lists and the "marked for finalization"
** lists. Moreover, barriers can age young objects in young lists as
** OLD0, which then become OLD1. However, a list never contains
** elements younger than their main ages.
**
** The generational collector also uses a pointer 'firstold1', which
** points to the first OLD1 object in the list. It is used to optimize
** 'markold'. (Potentially OLD1 objects can be anywhere between 'allgc'
** and 'reallyold', but often the list has no OLD1 objects or they are
** after 'old1'.) Note the difference between it and 'old1':
** 'firstold1': no OLD1 objects before this point; there can be all
** ages after it.
** 'old1': no objects younger than OLD1 after this point.
*/
/*
......
** can become gray have such a field. The field is not the same
** in all objects, but it always has this name.) Any gray object
** must belong to one of these lists, and all objects in these lists
** must be gray:
** must be gray (with two exceptions explained below):
**
** 'gray': regular gray objects, still waiting to be visited.
** 'grayagain': objects that must be revisited at the atomic phase.
......
** 'weak': tables with weak values to be cleared;
** 'ephemeron': ephemeron tables with white->white entries;
** 'allweak': tables with weak keys and/or weak values to be cleared.
**
** The exceptions to that "gray rule" are:
** - TOUCHED2 objects in generational mode stay in a gray list (because
** they must be visited again at the end of the cycle), but they are
** marked black because assignments to them must activate barriers (to
** move them back to TOUCHED1).
** - Open upvales are kept gray to avoid barriers, but they stay out
** of gray lists. (They don't even have a 'gclist' field.)
*/
......
GCObject *fixedgc; /* list of objects not to be collected */
/* fields for generational collector */
GCObject *survival; /* start of objects that survived one GC cycle */
GCObject *old; /* start of old objects */
GCObject *reallyold; /* old objects with more than one cycle */
GCObject *old1; /* start of old1 objects */
GCObject *reallyold; /* objects more than one cycle old ("really old") */
GCObject *firstold1; /* first OLD1 object in the list (if any) */
GCObject *finobjsur; /* list of survival objects with finalizers */
GCObject *finobjold; /* list of old objects with finalizers */
GCObject *finobjold1; /* list of old1 objects with finalizers */
GCObject *finobjrold; /* list of really old objects with finalizers */
struct lua_State *twups; /* list of threads with open upvalues */
lua_CFunction panic; /* to be called in unprotected errors */
......
StkId top; /* first free slot in the stack */
global_State *l_G;
CallInfo *ci; /* call info for current function */
const Instruction *oldpc; /* last pc traced */
StkId stack_last; /* last free slot in the stack */
StkId stack; /* stack base */
UpVal *openupval; /* list of open upvalues in this stack */
......
volatile lua_Hook hook;
ptrdiff_t errfunc; /* current error handling function (stack index) */
l_uint32 nCcalls; /* number of allowed nested C calls - 'nci' */
int oldpc; /* last pc traced */
int stacksize;
int basehookcount;
int hookcount;
......
/*
** Union of all collectable objects (only for conversions)
** ISO C99, 6.5.2.3 p.5:
** "if a union contains several structures that share a common initial
** sequence [...], and if the union object currently contains one
** of these structures, it is permitted to inspect the common initial
** part of any of them anywhere that a declaration of the complete type
** of the union is visible."
*/
union GCUnion {
GCObject gc; /* common header */
......
};
/*
** ISO C99, 6.7.2.1 p.14:
** "A pointer to a union object, suitably converted, points to each of
** its members [...], and vice versa."
*/
#define cast_u(o) cast(union GCUnion *, (o))
/* macros to convert a GCObject into a specific value */
dependencies/lua-5.4/src/ltable.c
GCObject *o = luaC_newobj(L, LUA_VTABLE, sizeof(Table));
Table *t = gco2t(o);
t->metatable = NULL;
t->flags = cast_byte(~0);
t->flags = cast_byte(maskflags); /* table has no metamethod fields */
t->array = NULL;
t->alimit = 0;
setnodevector(L, t, 0);
dependencies/lua-5.4/src/ltable.h
#define gnext(n) ((n)->u.next)
#define invalidateTMcache(t) ((t)->flags = 0)
/*
** Clear all bits of fast-access metamethods, which means that the table
** may have any of these metamethods. (First access that fails after the
** clearing will set the bit again.)
*/
#define invalidateTMcache(t) ((t)->flags &= ~maskflags)
/* true when 't' is using 'dummynode' as its hash part */
dependencies/lua-5.4/src/ltm.c
int actual = cast_int(L->top - ci->func) - 1; /* number of arguments */
int nextra = actual - nfixparams; /* number of extra arguments */
ci->u.l.nextraargs = nextra;
checkstackGC(L, p->maxstacksize + 1);
luaD_checkstack(L, p->maxstacksize + 1);
/* copy function to the top of the stack */
setobjs2s(L, L->top++, ci->func);
/* move fixed parameters to the top of the stack */
......
int nextra = ci->u.l.nextraargs;
if (wanted < 0) {
wanted = nextra; /* get all extra arguments available */
checkstackp(L, nextra, where); /* ensure stack space */
checkstackGCp(L, nextra, where); /* ensure stack space */
L->top = where + nextra; /* next instruction will need top */
}
for (i = 0; i < wanted && i < nextra; i++)
dependencies/lua-5.4/src/ltm.h
} TMS;
/*
** Mask with 1 in all fast-access methods. A 1 in any of these bits
** in the flag of a (meta)table means the metatable does not have the
** corresponding metamethod field. (Bit 7 of the flag is used for
** 'isrealasize'.)
*/
#define maskflags (~(~0u << (TM_EQ + 1)))
/*
** Test whether there is no tagmethod.
** (Because tagmethods use raw accesses, the result may be an "empty" nil.)
dependencies/lua-5.4/src/lua.h
#define LUA_VERSION_MAJOR "5"
#define LUA_VERSION_MINOR "4"
#define LUA_VERSION_RELEASE "0"
#define LUA_VERSION_RELEASE "1"
#define LUA_VERSION_NUM 504
#define LUA_VERSION_RELEASE_NUM (LUA_VERSION_NUM * 100 + 0)
dependencies/lua-5.4/src/lundump.c
}
else { /* long string */
ts = luaS_createlngstrobj(L, size); /* create string */
setsvalue2s(L, L->top, ts); /* anchor it ('loadVector' can GC) */
luaD_inctop(L);
loadVector(S, getstr(ts), size); /* load directly in final place */
L->top--; /* pop string */
}
luaC_objbarrier(L, p, ts);
return ts;
......
}
/*
** Load the upvalues for a function. The names must be filled first,
** because the filling of the other fields can raise read errors and
** the creation of the error message can call an emergency collection;
** in that case all prototypes must be consistent for the GC.
*/
static void loadUpvalues (LoadState *S, Proto *f) {
int i, n;
n = loadInt(S);
f->upvalues = luaM_newvectorchecked(S->L, n, Upvaldesc);
f->sizeupvalues = n;
for (i = 0; i < n; i++) {
for (i = 0; i < n; i++) /* make array valid for GC */
f->upvalues[i].name = NULL;
for (i = 0; i < n; i++) { /* following calls can raise errors */
f->upvalues[i].instack = loadByte(S);
f->upvalues[i].idx = loadByte(S);
f->upvalues[i].kind = loadByte(S);
dependencies/lua-5.4/src/lvm.c
** from 'L->top - total' up to 'L->top - 1'.
*/
void luaV_concat (lua_State *L, int total) {
lua_assert(total >= 2);
if (total == 1)
return; /* "all" values already concatenated */
do {
StkId top = L->top;
int n = 2; /* number of elements handled in this pass (at least 2) */
......
int a = GETARG_A(inst); /* first element to concatenate */
int total = cast_int(top - 1 - (base + a)); /* yet to concatenate */
setobjs2s(L, top - 2, top); /* put TM result in proper position */
if (total > 1) { /* are there elements to concat? */
L->top = top - 1; /* top is one after last element (at top-2) */
luaV_concat(L, total); /* concat them (may yield again) */
}
L->top = top - 1; /* top is one after last element (at top-2) */
luaV_concat(L, total); /* concat them (may yield again) */
break;
}
default: {
......
/* idem, but without changing the stack */
#define halfProtectNT(exp) (savepc(L), (exp))
/* 'c' is the limit of live values in the stack */
#define checkGC(L,c) \
{ luaC_condGC(L, L->top = (c), /* limit of live values */ \
{ luaC_condGC(L, (savepc(L), L->top = (c)), \
updatetrap(ci)); \
luai_threadyield(L); }
......
while (!ttisfunction(s2v(ra))) { /* not a function? */
luaD_tryfuncTM(L, ra); /* try '__call' metamethod */
b++; /* there is now one extra argument */
checkstackp(L, 1, ra);
checkstackGCp(L, 1, ra);
}
if (!ttisLclosure(s2v(ra))) { /* C function? */
luaD_call(L, ra, LUA_MULTRET); /* call it */
......
vmbreak;
}
vmcase(OP_VARARGPREP) {
luaT_adjustvarargs(L, GETARG_A(i), ci, cl->p);
updatetrap(ci);
ProtectNT(luaT_adjustvarargs(L, GETARG_A(i), ci, cl->p));
if (trap) {
luaD_hookcall(L, ci);
L->oldpc = pc + 1; /* next opcode will be seen as a "new" line */
L->oldpc = 1; /* next opcode will be seen as a "new" line */
}
updatebase(ci); /* function has new base after adjustment */
vmbreak;
    (1-1/1)