Update Qt to 5.15.2

This commit is contained in:
Syping 2020-11-26 04:07:09 +01:00
parent ea95d7fc10
commit ddc4ab91c4
38 changed files with 596 additions and 346 deletions

View file

@ -5,7 +5,7 @@ stages:
LuaEngine Windows: LuaEngine Windows:
stage: runtime stage: runtime
image: syping/qt5-shared-llvm-mingw:5.15.0 image: syping/qt5-shared-llvm-mingw:5.15.2
variables: variables:
QT_SELECT: "qt5-x86_64-w64-mingw32" QT_SELECT: "qt5-x86_64-w64-mingw32"
script: script:
@ -50,7 +50,7 @@ LuaEngine Debian amd64:
LuaEngine PE Windows: LuaEngine PE Windows:
stage: portable stage: portable
image: syping/qt5-static-llvm-mingw:5.15.0 image: syping/qt5-static-llvm-mingw:5.15.2
variables: variables:
QT_SELECT: "qt5-x86_64-w64-mingw32" QT_SELECT: "qt5-x86_64-w64-mingw32"
script: script:
@ -67,7 +67,7 @@ LuaEngine PE Windows:
LuaEngine Setup: LuaEngine Setup:
stage: deploy stage: deploy
image: syping/qt5-shared-llvm-mingw:5.15.0 image: syping/qt5-shared-llvm-mingw:5.15.2
script: script:
- apt-get update -qq - apt-get update -qq
- apt-get install nsis -qq - apt-get install nsis -qq

View file

@ -1254,13 +1254,12 @@ LUA_API const char *lua_setupvalue (lua_State *L, int funcindex, int n) {
} }
static UpVal **getupvalref (lua_State *L, int fidx, int n, LClosure **pf) { static UpVal **getupvalref (lua_State *L, int fidx, int n) {
LClosure *f; LClosure *f;
StkId fi = index2addr(L, fidx); StkId fi = index2addr(L, fidx);
api_check(L, ttisLclosure(fi), "Lua function expected"); api_check(L, ttisLclosure(fi), "Lua function expected");
f = clLvalue(fi); f = clLvalue(fi);
api_check(L, (1 <= n && n <= f->p->sizeupvalues), "invalid upvalue index"); api_check(L, (1 <= n && n <= f->p->sizeupvalues), "invalid upvalue index");
if (pf) *pf = f;
return &f->upvals[n - 1]; /* get its upvalue pointer */ return &f->upvals[n - 1]; /* get its upvalue pointer */
} }
@ -1269,7 +1268,7 @@ LUA_API void *lua_upvalueid (lua_State *L, int fidx, int n) {
StkId fi = index2addr(L, fidx); StkId fi = index2addr(L, fidx);
switch (ttype(fi)) { switch (ttype(fi)) {
case LUA_TLCL: { /* lua closure */ case LUA_TLCL: { /* lua closure */
return *getupvalref(L, fidx, n, NULL); return *getupvalref(L, fidx, n);
} }
case LUA_TCCL: { /* C closure */ case LUA_TCCL: { /* C closure */
CClosure *f = clCvalue(fi); CClosure *f = clCvalue(fi);
@ -1286,9 +1285,10 @@ LUA_API void *lua_upvalueid (lua_State *L, int fidx, int n) {
LUA_API void lua_upvaluejoin (lua_State *L, int fidx1, int n1, LUA_API void lua_upvaluejoin (lua_State *L, int fidx1, int n1,
int fidx2, int n2) { int fidx2, int n2) {
LClosure *f1; UpVal **up1 = getupvalref(L, fidx1, n1);
UpVal **up1 = getupvalref(L, fidx1, n1, &f1); UpVal **up2 = getupvalref(L, fidx2, n2);
UpVal **up2 = getupvalref(L, fidx2, n2, NULL); if (*up1 == *up2)
return;
luaC_upvdeccount(L, *up1); luaC_upvdeccount(L, *up1);
*up1 = *up2; *up1 = *up2;
(*up1)->refcount++; (*up1)->refcount++;

View file

@ -1011,8 +1011,13 @@ static void *l_alloc (void *ud, void *ptr, size_t osize, size_t nsize) {
free(ptr); free(ptr);
return NULL; return NULL;
} }
else else { /* cannot fail when shrinking a block */
return realloc(ptr, nsize); void *newptr = realloc(ptr, nsize);
if (newptr == NULL && ptr != NULL && nsize <= osize)
return ptr; /* keep the original block */
else /* no fail or not shrinking */
return newptr; /* use the new block */
}
} }

View file

@ -1061,7 +1061,7 @@ static void codecomp (FuncState *fs, BinOpr opr, expdesc *e1, expdesc *e2) {
/* /*
** Aplly prefix operation 'op' to expression 'e'. ** Apply prefix operation 'op' to expression 'e'.
*/ */
void luaK_prefix (FuncState *fs, UnOpr op, expdesc *e, int line) { void luaK_prefix (FuncState *fs, UnOpr op, expdesc *e, int line) {
static const expdesc ef = {VKINT, {0}, NO_JUMP, NO_JUMP}; static const expdesc ef = {VKINT, {0}, NO_JUMP, NO_JUMP};

View file

@ -133,10 +133,11 @@ static const char *upvalname (Proto *p, int uv) {
static const char *findvararg (CallInfo *ci, int n, StkId *pos) { static const char *findvararg (CallInfo *ci, int n, StkId *pos) {
int nparams = clLvalue(ci->func)->p->numparams; int nparams = clLvalue(ci->func)->p->numparams;
if (n >= cast_int(ci->u.l.base - ci->func) - nparams) int nvararg = cast_int(ci->u.l.base - ci->func) - nparams;
if (n <= -nvararg)
return NULL; /* no such vararg */ return NULL; /* no such vararg */
else { else {
*pos = ci->func + nparams + n; *pos = ci->func + nparams - n;
return "(*vararg)"; /* generic name for any vararg */ return "(*vararg)"; /* generic name for any vararg */
} }
} }
@ -148,7 +149,7 @@ static const char *findlocal (lua_State *L, CallInfo *ci, int n,
StkId base; StkId base;
if (isLua(ci)) { if (isLua(ci)) {
if (n < 0) /* access to vararg values? */ if (n < 0) /* access to vararg values? */
return findvararg(ci, -n, pos); return findvararg(ci, n, pos);
else { else {
base = ci->u.l.base; base = ci->u.l.base;
name = luaF_getlocalname(ci_func(ci)->p, n, currentpc(ci)); name = luaF_getlocalname(ci_func(ci)->p, n, currentpc(ci));

View file

@ -277,6 +277,8 @@ static int io_popen (lua_State *L) {
const char *filename = luaL_checkstring(L, 1); const char *filename = luaL_checkstring(L, 1);
const char *mode = luaL_optstring(L, 2, "r"); const char *mode = luaL_optstring(L, 2, "r");
LStream *p = newprefile(L); LStream *p = newprefile(L);
luaL_argcheck(L, ((mode[0] == 'r' || mode[0] == 'w') && mode[1] == '\0'),
2, "invalid mode");
p->f = l_popen(L, filename, mode); p->f = l_popen(L, filename, mode);
p->closef = &io_pclose; p->closef = &io_pclose;
return (p->f == NULL) ? luaL_fileresult(L, 0, filename) : 1; return (p->f == NULL) ? luaL_fileresult(L, 0, filename) : 1;

View file

@ -244,12 +244,12 @@ static int read_numeral (LexState *ls, SemInfo *seminfo) {
/* /*
** skip a sequence '[=*[' or ']=*]'; if sequence is well formed, return ** reads a sequence '[=*[' or ']=*]', leaving the last bracket.
** its number of '='s; otherwise, return a negative number (-1 iff there ** If sequence is well formed, return its number of '='s + 2; otherwise,
** are no '='s after initial bracket) ** return 1 if there is no '='s or 0 otherwise (an unfinished '[==...').
*/ */
static int skip_sep (LexState *ls) { static size_t skip_sep (LexState *ls) {
int count = 0; size_t count = 0;
int s = ls->current; int s = ls->current;
lua_assert(s == '[' || s == ']'); lua_assert(s == '[' || s == ']');
save_and_next(ls); save_and_next(ls);
@ -257,11 +257,14 @@ static int skip_sep (LexState *ls) {
save_and_next(ls); save_and_next(ls);
count++; count++;
} }
return (ls->current == s) ? count : (-count) - 1; return (ls->current == s) ? count + 2
: (count == 0) ? 1
: 0;
} }
static void read_long_string (LexState *ls, SemInfo *seminfo, int sep) { static void read_long_string (LexState *ls, SemInfo *seminfo, size_t sep) {
int line = ls->linenumber; /* initial line (for error message) */ int line = ls->linenumber; /* initial line (for error message) */
save_and_next(ls); /* skip 2nd '[' */ save_and_next(ls); /* skip 2nd '[' */
if (currIsNewline(ls)) /* string starts with a newline? */ if (currIsNewline(ls)) /* string starts with a newline? */
@ -295,8 +298,8 @@ static void read_long_string (LexState *ls, SemInfo *seminfo, int sep) {
} }
} endloop: } endloop:
if (seminfo) if (seminfo)
seminfo->ts = luaX_newstring(ls, luaZ_buffer(ls->buff) + (2 + sep), seminfo->ts = luaX_newstring(ls, luaZ_buffer(ls->buff) + sep,
luaZ_bufflen(ls->buff) - 2*(2 + sep)); luaZ_bufflen(ls->buff) - 2 * sep);
} }
@ -444,9 +447,9 @@ static int llex (LexState *ls, SemInfo *seminfo) {
/* else is a comment */ /* else is a comment */
next(ls); next(ls);
if (ls->current == '[') { /* long comment? */ if (ls->current == '[') { /* long comment? */
int sep = skip_sep(ls); size_t sep = skip_sep(ls);
luaZ_resetbuffer(ls->buff); /* 'skip_sep' may dirty the buffer */ luaZ_resetbuffer(ls->buff); /* 'skip_sep' may dirty the buffer */
if (sep >= 0) { if (sep >= 2) {
read_long_string(ls, NULL, sep); /* skip long comment */ read_long_string(ls, NULL, sep); /* skip long comment */
luaZ_resetbuffer(ls->buff); /* previous call may dirty the buff. */ luaZ_resetbuffer(ls->buff); /* previous call may dirty the buff. */
break; break;
@ -458,12 +461,12 @@ static int llex (LexState *ls, SemInfo *seminfo) {
break; break;
} }
case '[': { /* long string or simply '[' */ case '[': { /* long string or simply '[' */
int sep = skip_sep(ls); size_t sep = skip_sep(ls);
if (sep >= 0) { if (sep >= 2) {
read_long_string(ls, seminfo, sep); read_long_string(ls, seminfo, sep);
return TK_STRING; return TK_STRING;
} }
else if (sep != -1) /* '[=...' missing second bracket */ else if (sep == 0) /* '[=...' missing second bracket */
lexerror(ls, "invalid long string delimiter", TK_STRING); lexerror(ls, "invalid long string delimiter", TK_STRING);
return '['; return '[';
} }

View file

@ -266,7 +266,7 @@ static const char *l_str2dloc (const char *s, lua_Number *result, int mode) {
** - 'n'/'N' means 'inf' or 'nan' (which should be rejected) ** - 'n'/'N' means 'inf' or 'nan' (which should be rejected)
** - '.' just optimizes the search for the common case (nothing special) ** - '.' just optimizes the search for the common case (nothing special)
** This function accepts both the current locale or a dot as the radix ** This function accepts both the current locale or a dot as the radix
** mark. If the convertion fails, it may mean number has a dot but ** mark. If the conversion fails, it may mean number has a dot but
** locale accepts something else. In that case, the code copies 's' ** locale accepts something else. In that case, the code copies 's'
** to a buffer (because 's' is read-only), changes the dot to the ** to a buffer (because 's' is read-only), changes the dot to the
** current locale radix mark, and tries to convert again. ** current locale radix mark, and tries to convert again.

View file

@ -544,6 +544,7 @@ static void open_func (LexState *ls, FuncState *fs, BlockCnt *bl) {
fs->bl = NULL; fs->bl = NULL;
f = fs->f; f = fs->f;
f->source = ls->source; f->source = ls->source;
luaC_objbarrier(ls->L, f, f->source);
f->maxstacksize = 2; /* registers 0/1 are always valid */ f->maxstacksize = 2; /* registers 0/1 are always valid */
enterblock(fs, bl, 0); enterblock(fs, bl, 0);
} }
@ -1616,6 +1617,7 @@ static void mainfunc (LexState *ls, FuncState *fs) {
fs->f->is_vararg = 1; /* main function is always declared vararg */ fs->f->is_vararg = 1; /* main function is always declared vararg */
init_exp(&v, VLOCAL, 0); /* create and... */ init_exp(&v, VLOCAL, 0); /* create and... */
newupvalue(fs, ls->envn, &v); /* ...set environment upvalue */ newupvalue(fs, ls->envn, &v); /* ...set environment upvalue */
luaC_objbarrier(ls->L, fs->f, ls->envn);
luaX_next(ls); /* read first token */ luaX_next(ls); /* read first token */
statlist(ls); /* parse main body */ statlist(ls); /* parse main body */
check(ls, TK_EOS); check(ls, TK_EOS);
@ -1634,6 +1636,7 @@ LClosure *luaY_parser (lua_State *L, ZIO *z, Mbuffer *buff,
sethvalue(L, L->top, lexstate.h); /* anchor it */ sethvalue(L, L->top, lexstate.h); /* anchor it */
luaD_inctop(L); luaD_inctop(L);
funcstate.f = cl->p = luaF_newproto(L); funcstate.f = cl->p = luaF_newproto(L);
luaC_objbarrier(L, cl, cl->p);
funcstate.f->source = luaS_new(L, name); /* create and anchor TString */ funcstate.f->source = luaS_new(L, name); /* create and anchor TString */
lua_assert(iswhite(funcstate.f)); /* do not need barrier here */ lua_assert(iswhite(funcstate.f)); /* do not need barrier here */
lexstate.buff = buff; lexstate.buff = buff;

View file

@ -1,5 +1,4 @@
/* /*
** $Id: lua.h,v 1.332.1.2 2018/06/13 16:58:17 roberto Exp $
** Lua - A Scripting Language ** Lua - A Scripting Language
** Lua.org, PUC-Rio, Brazil (http://www.lua.org) ** Lua.org, PUC-Rio, Brazil (http://www.lua.org)
** See Copyright Notice at the end of this file ** See Copyright Notice at the end of this file
@ -19,11 +18,11 @@
#define LUA_VERSION_MAJOR "5" #define LUA_VERSION_MAJOR "5"
#define LUA_VERSION_MINOR "3" #define LUA_VERSION_MINOR "3"
#define LUA_VERSION_NUM 503 #define LUA_VERSION_NUM 503
#define LUA_VERSION_RELEASE "5" #define LUA_VERSION_RELEASE "6"
#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-2018 Lua.org, PUC-Rio" #define LUA_COPYRIGHT LUA_RELEASE " Copyright (C) 1994-2020 Lua.org, PUC-Rio"
#define LUA_AUTHORS "R. Ierusalimschy, L. H. de Figueiredo, W. Celes" #define LUA_AUTHORS "R. Ierusalimschy, L. H. de Figueiredo, W. Celes"
@ -460,7 +459,7 @@ struct lua_Debug {
/****************************************************************************** /******************************************************************************
* Copyright (C) 1994-2018 Lua.org, PUC-Rio. * Copyright (C) 1994-2020 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

View file

@ -85,8 +85,10 @@ static lua_Integer LoadInteger (LoadState *S) {
} }
static TString *LoadString (LoadState *S) { static TString *LoadString (LoadState *S, Proto *p) {
lua_State *L = S->L;
size_t size = LoadByte(S); size_t size = LoadByte(S);
TString *ts;
if (size == 0xFF) if (size == 0xFF)
LoadVar(S, size); LoadVar(S, size);
if (size == 0) if (size == 0)
@ -94,13 +96,17 @@ static TString *LoadString (LoadState *S) {
else if (--size <= LUAI_MAXSHORTLEN) { /* short string? */ else if (--size <= LUAI_MAXSHORTLEN) { /* short string? */
char buff[LUAI_MAXSHORTLEN]; char buff[LUAI_MAXSHORTLEN];
LoadVector(S, buff, size); LoadVector(S, buff, size);
return luaS_newlstr(S->L, buff, size); ts = luaS_newlstr(L, buff, size);
} }
else { /* long string */ else { /* long string */
TString *ts = luaS_createlngstrobj(S->L, size); ts = luaS_createlngstrobj(L, size);
setsvalue2s(L, L->top, ts); /* anchor it ('loadVector' can GC) */
luaD_inctop(L);
LoadVector(S, getstr(ts), size); /* load directly in final place */ LoadVector(S, getstr(ts), size); /* load directly in final place */
return ts; L->top--; /* pop string */
} }
luaC_objbarrier(L, p, ts);
return ts;
} }
@ -140,7 +146,7 @@ static void LoadConstants (LoadState *S, Proto *f) {
break; break;
case LUA_TSHRSTR: case LUA_TSHRSTR:
case LUA_TLNGSTR: case LUA_TLNGSTR:
setsvalue2n(S->L, o, LoadString(S)); setsvalue2n(S->L, o, LoadString(S, f));
break; break;
default: default:
lua_assert(0); lua_assert(0);
@ -158,6 +164,7 @@ static void LoadProtos (LoadState *S, Proto *f) {
f->p[i] = NULL; f->p[i] = NULL;
for (i = 0; i < n; i++) { for (i = 0; i < n; i++) {
f->p[i] = luaF_newproto(S->L); f->p[i] = luaF_newproto(S->L);
luaC_objbarrier(S->L, f, f->p[i]);
LoadFunction(S, f->p[i], f->source); LoadFunction(S, f->p[i], f->source);
} }
} }
@ -189,18 +196,18 @@ static void LoadDebug (LoadState *S, Proto *f) {
for (i = 0; i < n; i++) for (i = 0; i < n; i++)
f->locvars[i].varname = NULL; f->locvars[i].varname = NULL;
for (i = 0; i < n; i++) { for (i = 0; i < n; i++) {
f->locvars[i].varname = LoadString(S); f->locvars[i].varname = LoadString(S, f);
f->locvars[i].startpc = LoadInt(S); f->locvars[i].startpc = LoadInt(S);
f->locvars[i].endpc = LoadInt(S); f->locvars[i].endpc = LoadInt(S);
} }
n = LoadInt(S); n = LoadInt(S);
for (i = 0; i < n; i++) for (i = 0; i < n; i++)
f->upvalues[i].name = LoadString(S); f->upvalues[i].name = LoadString(S, f);
} }
static void LoadFunction (LoadState *S, Proto *f, TString *psource) { static void LoadFunction (LoadState *S, Proto *f, TString *psource) {
f->source = LoadString(S); f->source = LoadString(S, f);
if (f->source == NULL) /* no source in dump? */ if (f->source == NULL) /* no source in dump? */
f->source = psource; /* reuse parent's source */ f->source = psource; /* reuse parent's source */
f->linedefined = LoadInt(S); f->linedefined = LoadInt(S);
@ -271,6 +278,7 @@ LClosure *luaU_undump(lua_State *L, ZIO *Z, const char *name) {
setclLvalue(L, L->top, cl); setclLvalue(L, L->top, cl);
luaD_inctop(L); luaD_inctop(L);
cl->p = luaF_newproto(L); cl->p = luaF_newproto(L);
luaC_objbarrier(L, cl, cl->p);
LoadFunction(&S, cl->p, NULL); LoadFunction(&S, cl->p, NULL);
lua_assert(cl->nupvalues == cl->p->sizeupvalues); lua_assert(cl->nupvalues == cl->p->sizeupvalues);
luai_verifycode(L, buff, cl->p); luai_verifycode(L, buff, cl->p);

View file

@ -97,8 +97,9 @@ static StkId index2stack (lua_State *L, int idx) {
LUA_API int lua_checkstack (lua_State *L, int n) { LUA_API int lua_checkstack (lua_State *L, int n) {
int res; int res;
CallInfo *ci = L->ci; CallInfo *ci;
lua_lock(L); lua_lock(L);
ci = L->ci;
api_check(L, n >= 0, "negative 'n'"); api_check(L, n >= 0, "negative 'n'");
if (L->stack_last - L->top > n) /* stack large enough? */ if (L->stack_last - L->top > n) /* stack large enough? */
res = 1; /* yes; check is OK */ res = 1; /* yes; check is OK */
@ -170,10 +171,12 @@ LUA_API int lua_gettop (lua_State *L) {
LUA_API void lua_settop (lua_State *L, int idx) { LUA_API void lua_settop (lua_State *L, int idx) {
CallInfo *ci = L->ci; CallInfo *ci;
StkId func = ci->func; StkId func;
ptrdiff_t diff; /* difference for new top */ ptrdiff_t diff; /* difference for new top */
lua_lock(L); lua_lock(L);
ci = L->ci;
func = ci->func;
if (idx >= 0) { if (idx >= 0) {
api_check(L, idx <= ci->top - (func + 1), "new top too large"); api_check(L, idx <= ci->top - (func + 1), "new top too large");
diff = ((func + 1) + idx) - L->top; diff = ((func + 1) + idx) - L->top;
@ -376,20 +379,22 @@ LUA_API int lua_toboolean (lua_State *L, int idx) {
LUA_API const char *lua_tolstring (lua_State *L, int idx, size_t *len) { 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 (!ttisstring(o)) {
if (!cvt2str(o)) { /* not convertible? */ if (!cvt2str(o)) { /* not convertible? */
if (len != NULL) *len = 0; if (len != NULL) *len = 0;
lua_unlock(L);
return NULL; return NULL;
} }
lua_lock(L); /* 'luaO_tostring' may create a new string */
luaO_tostring(L, o); luaO_tostring(L, o);
luaC_checkGC(L); luaC_checkGC(L);
o = index2value(L, idx); /* previous call may reallocate the stack */ o = index2value(L, idx); /* previous call may reallocate the stack */
lua_unlock(L);
} }
if (len != NULL) if (len != NULL)
*len = vslen(o); *len = vslen(o);
lua_unlock(L);
return svalue(o); return svalue(o);
} }
@ -563,6 +568,7 @@ LUA_API void lua_pushcclosure (lua_State *L, lua_CFunction fn, int n) {
while (n--) { while (n--) {
setobj2n(L, &cl->upvalue[n], s2v(L->top + n)); setobj2n(L, &cl->upvalue[n], s2v(L->top + n));
/* does not need barrier because closure is white */ /* does not need barrier because closure is white */
lua_assert(iswhite(cl));
} }
setclCvalue(L, s2v(L->top), cl); setclCvalue(L, s2v(L->top), cl);
api_incr_top(L); api_incr_top(L);
@ -624,8 +630,9 @@ static int auxgetstr (lua_State *L, const TValue *t, const char *k) {
LUA_API int lua_getglobal (lua_State *L, const char *name) { LUA_API int lua_getglobal (lua_State *L, const char *name) {
Table *reg = hvalue(&G(L)->l_registry); Table *reg;
lua_lock(L); lua_lock(L);
reg = hvalue(&G(L)->l_registry);
return auxgetstr(L, luaH_getint(reg, LUA_RIDX_GLOBALS), name); return auxgetstr(L, luaH_getint(reg, LUA_RIDX_GLOBALS), name);
} }
@ -804,8 +811,9 @@ static void auxsetstr (lua_State *L, const TValue *t, const char *k) {
LUA_API void lua_setglobal (lua_State *L, const char *name) { 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' */ lua_lock(L); /* unlock done in 'auxsetstr' */
reg = hvalue(&G(L)->l_registry);
auxsetstr(L, luaH_getint(reg, LUA_RIDX_GLOBALS), name); auxsetstr(L, luaH_getint(reg, LUA_RIDX_GLOBALS), name);
} }
@ -1093,8 +1101,9 @@ LUA_API int lua_status (lua_State *L) {
LUA_API int lua_gc (lua_State *L, int what, ...) { LUA_API int lua_gc (lua_State *L, int what, ...) {
va_list argp; va_list argp;
int res = 0; int res = 0;
global_State *g = G(L); global_State *g;
lua_lock(L); lua_lock(L);
g = G(L);
va_start(argp, what); va_start(argp, what);
switch (what) { switch (what) {
case LUA_GCSTOP: { case LUA_GCSTOP: {
@ -1194,9 +1203,15 @@ LUA_API int lua_gc (lua_State *L, int what, ...) {
LUA_API int lua_error (lua_State *L) { LUA_API int lua_error (lua_State *L) {
TValue *errobj;
lua_lock(L); lua_lock(L);
errobj = s2v(L->top - 1);
api_checknelems(L, 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 */ /* code unreachable; will unlock when control actually leaves the kernel */
return 0; /* to avoid warnings */ return 0; /* to avoid warnings */
} }
@ -1238,14 +1253,12 @@ LUA_API void lua_toclose (lua_State *L, int idx) {
LUA_API void lua_concat (lua_State *L, int n) { LUA_API void lua_concat (lua_State *L, int n) {
lua_lock(L); lua_lock(L);
api_checknelems(L, n); api_checknelems(L, n);
if (n >= 2) { if (n > 0)
luaV_concat(L, n); luaV_concat(L, n);
} else { /* nothing to concatenate */
else if (n == 0) { /* push empty string */ setsvalue2s(L, L->top, luaS_newlstr(L, "", 0)); /* push empty string */
setsvalue2s(L, L->top, luaS_newlstr(L, "", 0));
api_incr_top(L); api_incr_top(L);
} }
/* else n == 1; nothing to do */
luaC_checkGC(L); luaC_checkGC(L);
lua_unlock(L); lua_unlock(L);
} }

View file

@ -475,8 +475,10 @@ 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 (temp == NULL && newsize > 0) { /* allocation error? */
luaL_error(L, "not enough memory"); lua_pushliteral(L, "not enough memory");
lua_error(L); /* raise a memory error */
}
box->box = temp; box->box = temp;
box->bsize = newsize; box->bsize = newsize;
return temp; return temp;

View file

@ -73,11 +73,12 @@ 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) { if (r < 0) { /* error? */
int stat = lua_status(co); int stat = lua_status(co);
if (stat != LUA_OK && stat != LUA_YIELD) if (stat != LUA_OK && stat != LUA_YIELD) /* error in the coroutine? */
lua_resetthread(co); /* close variables in case of errors */ lua_resetthread(co); /* close its tbc variables */
if (lua_type(L, -1) == LUA_TSTRING) { /* error object is a string? */ 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 */ luaL_where(L, 1); /* add extra info, if available */
lua_insert(L, -2); lua_insert(L, -2);
lua_concat(L, 2); lua_concat(L, 2);

View file

@ -13,7 +13,7 @@
/* /*
** WARNING: the functions defined here do not necessarily correspond ** WARNING: the functions defined here do not necessarily correspond
** to the similar functions in the standard C ctype.h. They are ** 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) #if !defined(LUA_USE_CTYPE)
@ -61,13 +61,19 @@
#define lisprint(c) testprop(c, MASK(PRINTBIT)) #define lisprint(c) testprop(c, MASK(PRINTBIT))
#define lisxdigit(c) testprop(c, MASK(XDIGITBIT)) #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];) LUAI_DDEC(const lu_byte luai_ctype_[UCHAR_MAX + 2];)

View file

@ -33,10 +33,8 @@
#define noLuaClosure(f) ((f) == NULL || (f)->c.tt == LUA_VCCL) #define noLuaClosure(f) ((f) == NULL || (f)->c.tt == LUA_VCCL)
/* inverse of 'pcRel' */
/* Active Lua function (given call info) */ #define invpcRel(pc, p) ((p)->code + (pc) + 1)
#define ci_func(ci) (clLvalue(s2v((ci)->func)))
static const char *funcnamefromcode (lua_State *L, CallInfo *ci, static const char *funcnamefromcode (lua_State *L, CallInfo *ci,
const char **name); const char **name);
@ -127,20 +125,18 @@ static void settraps (CallInfo *ci) {
/* /*
** This function can be called during a signal, under "reasonable" ** This function can be called during a signal, under "reasonable"
** assumptions. ** assumptions.
** Fields 'oldpc', 'basehookcount', and 'hookcount' (set by ** Fields 'basehookcount' and 'hookcount' (set by 'resethookcount')
** 'resethookcount') are for debug only, and it is no problem if they ** are for debug only, and it is no problem if they get arbitrary
** get arbitrary values (causes at most one wrong hook call). 'hookmask' ** values (causes at most one wrong hook call). 'hookmask' is an atomic
** is an atomic value. We assume that pointers are atomic too (e.g., gcc ** value. We assume that pointers are atomic too (e.g., gcc ensures that
** ensures that for all platforms where it runs). Moreover, 'hook' is ** for all platforms where it runs). Moreover, 'hook' is always checked
** always checked before being called (see 'luaD_hook'). ** before being called (see 'luaD_hook').
*/ */
LUA_API void lua_sethook (lua_State *L, lua_Hook func, int mask, int count) { LUA_API void lua_sethook (lua_State *L, lua_Hook func, int mask, int count) {
if (func == NULL || mask == 0) { /* turn off hooks? */ if (func == NULL || mask == 0) { /* turn off hooks? */
mask = 0; mask = 0;
func = NULL; func = NULL;
} }
if (isLua(L->ci))
L->oldpc = L->ci->u.l.savedpc;
L->hook = func; L->hook = func;
L->basehookcount = count; L->basehookcount = count;
resethookcount(L); resethookcount(L);
@ -192,8 +188,8 @@ static const char *upvalname (const Proto *p, int uv) {
static const char *findvararg (CallInfo *ci, int n, StkId *pos) { static const char *findvararg (CallInfo *ci, int n, StkId *pos) {
if (clLvalue(s2v(ci->func))->p->is_vararg) { if (clLvalue(s2v(ci->func))->p->is_vararg) {
int nextra = ci->u.l.nextraargs; int nextra = ci->u.l.nextraargs;
if (n <= nextra) { if (n >= -nextra) { /* 'n' is negative */
*pos = ci->func - nextra + (n - 1); *pos = ci->func - nextra - (n + 1);
return "(vararg)"; /* generic name for any vararg */ return "(vararg)"; /* generic name for any vararg */
} }
} }
@ -206,7 +202,7 @@ const char *luaG_findlocal (lua_State *L, CallInfo *ci, int n, StkId *pos) {
const char *name = NULL; const char *name = NULL;
if (isLua(ci)) { if (isLua(ci)) {
if (n < 0) /* access to vararg values? */ if (n < 0) /* access to vararg values? */
return findvararg(ci, -n, pos); return findvararg(ci, n, pos);
else else
name = luaF_getlocalname(ci_func(ci)->p, n, currentpc(ci)); name = luaF_getlocalname(ci_func(ci)->p, n, currentpc(ci));
} }
@ -787,18 +783,34 @@ l_noret luaG_runerror (lua_State *L, const char *fmt, ...) {
** previous instruction 'oldpc'. ** previous instruction 'oldpc'.
*/ */
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? */
return 0;
while (oldpc++ < newpc) { while (oldpc++ < newpc) {
if (p->lineinfo[oldpc] != 0) if (p->lineinfo[oldpc] != 0)
return (luaG_getfuncline(p, oldpc - 1) != luaG_getfuncline(p, newpc)); 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) { 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;
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' */
@ -819,15 +831,14 @@ int luaG_traceexec (lua_State *L, const Instruction *pc) {
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) {
const Proto *p = ci_func(ci)->p;
int npci = pcRel(pc, p); int npci = pcRel(pc, p);
if (npci == 0 || /* call linehook when enter a new function, */ if (npci == 0 || /* call linehook when enter a new function, */
pc <= L->oldpc || /* when jump back (loop), or when */ pc <= invpcRel(oldpc, p) || /* when jump back (loop), or when */
changedline(p, pcRel(L->oldpc, p), npci)) { /* 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 */
} }
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 (L->status == LUA_YIELD) { /* did hook yield? */
if (counthook) if (counthook)

View file

@ -13,6 +13,11 @@
#define pcRel(pc, p) (cast_int((pc) - (p)->code) - 1) #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) #define resethookcount(L) (L->hookcount = L->basehookcount)
/* /*

View file

@ -245,13 +245,12 @@ static int stackinuse (lua_State *L) {
void luaD_shrinkstack (lua_State *L) { void luaD_shrinkstack (lua_State *L) {
int inuse = stackinuse(L); int inuse = stackinuse(L);
int goodsize = inuse + (inuse / 8) + 2*EXTRA_STACK; int goodsize = inuse + BASIC_STACK_SIZE;
if (goodsize > LUAI_MAXSTACK) if (goodsize > LUAI_MAXSTACK)
goodsize = LUAI_MAXSTACK; /* respect stack limit */ goodsize = LUAI_MAXSTACK; /* respect stack limit */
/* if thread is currently not handling a stack overflow and its /* if thread is currently not handling a stack overflow and its
good size is smaller than current size, shrink its stack */ good size is smaller than current size, shrink its stack */
if (inuse <= (LUAI_MAXSTACK - EXTRA_STACK) && if (inuse <= (LUAI_MAXSTACK - EXTRA_STACK) && goodsize < L->stacksize)
goodsize < L->stacksize)
luaD_reallocstack(L, goodsize, 0); /* ok if that fails */ luaD_reallocstack(L, goodsize, 0); /* ok if that fails */
else /* don't change stack */ else /* don't change stack */
condmovestack(L,{},{}); /* (change only for debugging) */ condmovestack(L,{},{}); /* (change only for debugging) */
@ -328,7 +327,7 @@ static StkId rethook (lua_State *L, CallInfo *ci, StkId firstres, int nres) {
ptrdiff_t oldtop = savestack(L, L->top); /* hook may change top */ ptrdiff_t oldtop = savestack(L, L->top); /* hook may change top */
int delta = 0; int delta = 0;
if (isLuacode(ci)) { if (isLuacode(ci)) {
Proto *p = clLvalue(s2v(ci->func))->p; Proto *p = ci_func(ci)->p;
if (p->is_vararg) if (p->is_vararg)
delta = ci->u.l.nextraargs + p->numparams + 1; delta = ci->u.l.nextraargs + p->numparams + 1;
if (L->top < ci->top) if (L->top < ci->top)
@ -341,8 +340,8 @@ static StkId rethook (lua_State *L, CallInfo *ci, StkId firstres, int nres) {
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->previous)) if (isLua(ci = ci->previous))
L->oldpc = ci->previous->u.l.savedpc; /* update 'oldpc' */ L->oldpc = pcRel(ci->u.l.savedpc, ci_func(ci)->p); /* update 'oldpc' */
return restorestack(L, oldtop); return restorestack(L, oldtop);
} }
@ -466,13 +465,13 @@ void luaD_call (lua_State *L, StkId func, int nresults) {
f = fvalue(s2v(func)); f = fvalue(s2v(func));
Cfunc: { Cfunc: {
int n; /* number of returns */ int n; /* number of returns */
CallInfo *ci = next_ci(L); CallInfo *ci;
checkstackp(L, LUA_MINSTACK, func); /* ensure minimum stack size */ checkstackGCp(L, LUA_MINSTACK, func); /* ensure minimum stack size */
L->ci = ci = next_ci(L);
ci->nresults = nresults; ci->nresults = nresults;
ci->callstatus = CIST_C; ci->callstatus = CIST_C;
ci->top = L->top + LUA_MINSTACK; ci->top = L->top + LUA_MINSTACK;
ci->func = func; ci->func = func;
L->ci = ci;
lua_assert(ci->top <= L->stack_last); lua_assert(ci->top <= L->stack_last);
if (L->hookmask & LUA_MASKCALL) { if (L->hookmask & LUA_MASKCALL) {
int narg = cast_int(L->top - func) - 1; int narg = cast_int(L->top - func) - 1;
@ -486,12 +485,13 @@ void luaD_call (lua_State *L, StkId func, int nresults) {
break; break;
} }
case LUA_VLCL: { /* Lua function */ case LUA_VLCL: { /* Lua function */
CallInfo *ci = next_ci(L); CallInfo *ci;
Proto *p = clLvalue(s2v(func))->p; Proto *p = clLvalue(s2v(func))->p;
int narg = cast_int(L->top - func) - 1; /* number of real arguments */ int narg = cast_int(L->top - func) - 1; /* number of real arguments */
int nfixparams = p->numparams; int nfixparams = p->numparams;
int fsize = p->maxstacksize; /* frame size */ int fsize = p->maxstacksize; /* frame size */
checkstackp(L, fsize, func); checkstackGCp(L, fsize, func);
L->ci = ci = next_ci(L);
ci->nresults = nresults; ci->nresults = nresults;
ci->u.l.savedpc = p->code; /* starting point */ ci->u.l.savedpc = p->code; /* starting point */
ci->callstatus = 0; ci->callstatus = 0;
@ -505,7 +505,7 @@ void luaD_call (lua_State *L, StkId func, int nresults) {
break; break;
} }
default: { /* not a function */ 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 */ luaD_tryfuncTM(L, func); /* try to get '__call' metamethod */
goto retry; /* try again with metamethod */ goto retry; /* try again with metamethod */
} }
@ -515,14 +515,13 @@ void luaD_call (lua_State *L, StkId func, int nresults) {
/* /*
** Similar to 'luaD_call', but does not allow yields during the call. ** 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) { void luaD_callnoyield (lua_State *L, StkId func, int nResults) {
incXCcalls(L); incXCcalls(L);
if (getCcalls(L) <= CSTACKERR) /* possible stack overflow? */ if (getCcalls(L) <= CSTACKERR) { /* possible C stack overflow? */
luaE_freeCI(L); luaE_exitCcall(L); /* to compensate decrement in next call */
luaE_enterCcall(L); /* check properly */
}
luaD_call(L, func, nResults); luaD_call(L, func, nResults);
decXCcalls(L); decXCcalls(L);
} }
@ -674,7 +673,7 @@ LUA_API int lua_resume (lua_State *L, lua_State *from, int nargs,
if (from == NULL) if (from == NULL)
L->nCcalls = CSTACKTHREAD; L->nCcalls = CSTACKTHREAD;
else /* correct 'nCcalls' for this thread */ 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) if (L->nCcalls <= CSTACKERR)
return resume_error(L, "C stack overflow", nargs); return resume_error(L, "C stack overflow", nargs);
luai_userstateresume(L, nargs); luai_userstateresume(L, nargs);
@ -706,9 +705,10 @@ LUA_API int lua_isyieldable (lua_State *L) {
LUA_API int lua_yieldk (lua_State *L, int nresults, lua_KContext ctx, LUA_API int lua_yieldk (lua_State *L, int nresults, lua_KContext ctx,
lua_KFunction k) { lua_KFunction k) {
CallInfo *ci = L->ci; CallInfo *ci;
luai_userstateyield(L, nresults); luai_userstateyield(L, nresults);
lua_lock(L); lua_lock(L);
ci = L->ci;
api_checknelems(L, nresults); api_checknelems(L, nresults);
if (unlikely(!yieldable(L))) { if (unlikely(!yieldable(L))) {
if (L != G(L)->mainthread) if (L != G(L)->mainthread)

View file

@ -17,6 +17,8 @@
** Macro to check stack size and grow stack if needed. Parameters ** Macro to check stack size and grow stack if needed. Parameters
** 'pre'/'pos' allow the macro to preserve a pointer into the ** 'pre'/'pos' allow the macro to preserve a pointer into the
** stack across reallocations, doing the work only when needed. ** 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 ** 'condmovestack' is used in heavy tests to force a stack reallocation
** at every check. ** at every check.
*/ */
@ -35,7 +37,7 @@
/* macro to check stack size, preserving 'p' */ /* macro to check stack size, preserving 'p' */
#define checkstackp(L,n,p) \ #define checkstackGCp(L,n,p) \
luaD_checkstackaux(L, n, \ luaD_checkstackaux(L, n, \
ptrdiff_t t__ = savestack(L, p); /* save 'p' */ \ ptrdiff_t t__ = savestack(L, p); /* save 'p' */ \
luaC_checkGC(L), /* stack grow uses memory */ \ luaC_checkGC(L), /* stack grow uses memory */ \
@ -44,7 +46,7 @@
/* macro to check stack size and GC */ /* macro to check stack size and GC */
#define checkstackGC(L,fsize) \ #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' */ /* type of protected functions, to be ran by 'runprotected' */

View file

@ -234,10 +234,11 @@ int luaF_close (lua_State *L, StkId level, int status) {
luaF_unlinkupval(uv); 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)) if (!iswhite(uv)) { /* neither white nor dead? */
gray2black(uv); /* closed upvalues cannot be gray */ nw2black(uv); /* closed upvalues cannot be gray */
luaC_barrier(L, uv, slot); luaC_barrier(L, uv, slot);
} }
}
return status; return status;
} }

View file

@ -60,16 +60,24 @@
#define PAUSEADJ 100 #define PAUSEADJ 100
/* mask to erase all color bits (plus gen. related stuff) */ /* mask with all color bits */
#define maskcolors (~(bitmask(BLACKBIT) | WHITEBITS | AGEBITS)) #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) \ #define makewhite(g,x) \
(x->marked = cast_byte((x->marked & maskcolors) | luaC_white(g))) (x->marked = cast_byte((x->marked & ~maskcolors) | luaC_white(g)))
#define white2gray(x) resetbits(x->marked, WHITEBITS) /* make an object gray (neither white nor black) */
#define black2gray(x) resetbit(x->marked, BLACKBIT) #define set2gray(x) resetbits(x->marked, maskcolors)
/* make an object black (coming from any color) */
#define set2black(x) \
(x->marked = cast_byte((x->marked & ~WHITEBITS) | bitmask(BLACKBIT)))
#define valiswhite(x) (iscollectable(x) && iswhite(gcvalue(x))) #define valiswhite(x) (iscollectable(x) && iswhite(gcvalue(x)))
@ -77,16 +85,13 @@
#define keyiswhite(n) (keyiscollectable(n) && iswhite(gckey(n))) #define keyiswhite(n) (keyiscollectable(n) && iswhite(gckey(n)))
#define checkconsistency(obj) \
lua_longassert(!iscollectable(obj) || righttt(obj))
/* /*
** Protected access to objects in values ** Protected access to objects in values
*/ */
#define gcvalueN(o) (iscollectable(o) ? gcvalue(o) : NULL) #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)); } if (valiswhite(o)) reallymarkobject(g,gcvalue(o)); }
#define markkey(g, n) { if keyiswhite(n) reallymarkobject(g,gckey(n)); } #define markkey(g, n) { if keyiswhite(n) reallymarkobject(g,gckey(n)); }
@ -135,15 +140,23 @@ static GCObject **getgclist (GCObject *o) {
/* /*
** Link a collectable object 'o' with a known type into list pointed by 'p'. ** Link a collectable object 'o' with a known type into the list 'p'.
** (Must be a macro to access the 'gclist' field in different types.)
*/ */
#define linkgclist(o,p) ((o)->gclist = (p), (p) = obj2gco(o)) #define linkgclist(o,p) linkgclist_(obj2gco(o), &(o)->gclist, &(p))
static void linkgclist_ (GCObject *o, GCObject **pnext, GCObject **list) {
lua_assert(!isgray(o)); /* cannot be in a gray list */
*pnext = *list;
*list = o;
set2gray(o); /* now it is */
}
/* /*
** Link a generic collectable object 'o' into list pointed by 'p'. ** Link a generic collectable object 'o' into the list 'p'.
*/ */
#define linkobjgclist(o,p) (*getgclist(o) = (p), (p) = obj2gco(o)) #define linkobjgclist(o,p) linkgclist_(obj2gco(o), getgclist(o), &(p))
@ -181,14 +194,17 @@ static int iscleared (global_State *g, const GCObject *o) {
/* /*
** barrier that moves collector forward, that is, mark the white object ** Barrier that moves collector forward, that is, marks the white object
** 'v' being pointed by the black object 'o'. (If in sweep phase, clear ** 'v' being pointed by the black object 'o'. In the generational
** the black object to white [sweep it] to avoid other barrier calls for ** mode, 'v' must also become old, if 'o' is old; however, it cannot
** this same object.) In the generational mode, 'v' must also become ** be changed directly to OLD, because it may still point to non-old
** old, if 'o' is old; however, it cannot be changed directly to OLD, ** objects. So, it is marked as OLD0. In the next cycle it will become
** because it may still point to non-old objects. So, it is marked as ** OLD1, and in the next it will finally become OLD (regular old). By
** OLD0. In the next cycle it will become OLD1, and in the next it ** then, any object it points to will also be old. If called in the
** will finally become OLD (regular old). ** 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) { void luaC_barrier_ (lua_State *L, GCObject *o, GCObject *v) {
global_State *g = G(L); global_State *g = G(L);
@ -202,7 +218,8 @@ void luaC_barrier_ (lua_State *L, GCObject *o, GCObject *v) {
} }
else { /* sweep phase */ else { /* sweep phase */
lua_assert(issweepphase(g)); lua_assert(issweepphase(g));
makewhite(g, o); /* mark main obj. as white to avoid other barriers */ if (g->gckind == KGC_INC) /* incremental mode? */
makewhite(g, o); /* mark 'o' as white to avoid other barriers */
} }
} }
@ -214,10 +231,12 @@ void luaC_barrier_ (lua_State *L, GCObject *o, GCObject *v) {
void luaC_barrierback_ (lua_State *L, GCObject *o) { void luaC_barrierback_ (lua_State *L, GCObject *o) {
global_State *g = G(L); global_State *g = G(L);
lua_assert(isblack(o) && !isdead(g, o)); lua_assert(isblack(o) && !isdead(g, o));
lua_assert(g->gckind != KGC_GEN || (isold(o) && getage(o) != G_TOUCHED1)); lua_assert((g->gckind == KGC_GEN) == (isold(o) && getage(o) != G_TOUCHED1));
if (getage(o) != G_TOUCHED2) /* not already in gray list? */ if (getage(o) == G_TOUCHED2) /* already in gray list? */
linkobjgclist(o, g->grayagain); /* link it in 'grayagain' */ set2gray(o); /* make it gray to become touched1 */
black2gray(o); /* make object gray (again) */ 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 */ setage(o, G_TOUCHED1); /* touched in current cycle */
} }
@ -225,7 +244,7 @@ void luaC_barrierback_ (lua_State *L, GCObject *o) {
void luaC_fix (lua_State *L, GCObject *o) { void luaC_fix (lua_State *L, GCObject *o) {
global_State *g = G(L); global_State *g = G(L);
lua_assert(g->allgc == o); /* object must be 1st in 'allgc' list! */ 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 */ setage(o, G_OLD); /* and old forever */
g->allgc = o->next; /* remove object from 'allgc' list */ g->allgc = o->next; /* remove object from 'allgc' list */
o->next = g->fixedgc; /* link it to 'fixedgc' list */ o->next = g->fixedgc; /* link it to 'fixedgc' list */
@ -259,24 +278,30 @@ GCObject *luaC_newobj (lua_State *L, int tt, size_t sz) {
/* /*
** Mark an object. Userdata, strings, and closed upvalues are visited ** Mark an object. Userdata with no user values, strings, and closed
** and turned black here. Other objects are marked gray and added ** upvalues are visited and turned black here. Open upvalues are
** to appropriate list to be visited (and turned black) later. (Open ** already indirectly linked through their respective threads in the
** upvalues are already linked in 'headuv' list. They are kept gray ** 'twups' list, so they don't go to the gray list; nevertheless, they
** to avoid barriers, as their values will be revisited by the thread.) ** 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) { static void reallymarkobject (global_State *g, GCObject *o) {
white2gray(o);
switch (o->tt) { switch (o->tt) {
case LUA_VSHRSTR: case LUA_VSHRSTR:
case LUA_VLNGSTR: { case LUA_VLNGSTR: {
gray2black(o); set2black(o); /* nothing to visit */
break; break;
} }
case LUA_VUPVAL: { case LUA_VUPVAL: {
UpVal *uv = gco2upv(o); UpVal *uv = gco2upv(o);
if (!upisopen(uv)) /* open upvalues are kept gray */ if (upisopen(uv))
gray2black(o); set2gray(uv); /* open upvalues are kept gray */
else
set2black(o); /* closed upvalues are visited here */
markvalue(g, uv->v); /* mark its content */ markvalue(g, uv->v); /* mark its content */
break; break;
} }
@ -284,14 +309,14 @@ static void reallymarkobject (global_State *g, GCObject *o) {
Udata *u = gco2u(o); Udata *u = gco2u(o);
if (u->nuvalue == 0) { /* no user values? */ if (u->nuvalue == 0) { /* no user values? */
markobjectN(g, u->metatable); /* mark its metatable */ markobjectN(g, u->metatable); /* mark its metatable */
gray2black(o); /* nothing else to mark */ set2black(o); /* nothing else to mark */
break; break;
} }
/* else... */ /* else... */
} /* FALLTHROUGH */ } /* FALLTHROUGH */
case LUA_VLCL: case LUA_VCCL: case LUA_VTABLE: case LUA_VLCL: case LUA_VCCL: case LUA_VTABLE:
case LUA_VTHREAD: case LUA_VPROTO: { case LUA_VTHREAD: case LUA_VPROTO: {
linkobjgclist(o, g->gray); linkobjgclist(o, g->gray); /* to be visited later */
break; break;
} }
default: lua_assert(0); break; default: lua_assert(0); break;
@ -324,41 +349,54 @@ static lu_mem markbeingfnz (global_State *g) {
/* /*
** Mark all values stored in marked open upvalues from non-marked threads. ** For each non-marked thread, simulates a barrier between each open
** (Values from marked threads were already marked when traversing the ** upvalue and its value. (If the thread is collected, the value will be
** thread.) Remove from the list threads that no longer have upvalues and ** assigned to the upvalue, but then it can be too late for the barrier
** not-marked threads. ** 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) { static int remarkupvals (global_State *g) {
lua_State *thread; lua_State *thread;
lua_State **p = &g->twups; lua_State **p = &g->twups;
int work = 0; int work = 0; /* estimate of how much work was done here */
while ((thread = *p) != NULL) { while ((thread = *p) != NULL) {
work++; work++;
lua_assert(!isblack(thread)); /* threads are never black */ if (!iswhite(thread) && thread->openupval != NULL)
if (isgray(thread) && thread->openupval != NULL)
p = &thread->twups; /* keep marked thread with upvalues in the list */ p = &thread->twups; /* keep marked thread with upvalues in the list */
else { /* thread is not marked or without upvalues */ else { /* thread is not marked or without upvalues */
UpVal *uv; UpVal *uv;
lua_assert(!isold(thread) || thread->openupval == NULL);
*p = thread->twups; /* remove thread from the list */ *p = thread->twups; /* remove thread from the list */
thread->twups = thread; /* mark that it is out of list */ thread->twups = thread; /* mark that it is out of list */
for (uv = thread->openupval; uv != NULL; uv = uv->u.open.next) { for (uv = thread->openupval; uv != NULL; uv = uv->u.open.next) {
lua_assert(getage(uv) <= getage(thread));
work++; 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 */ markvalue(g, uv->v); /* mark its value */
} }
} }
} }
}
return work; return work;
} }
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 ** mark root set and reset all gray lists, to start a new collection
*/ */
static void restartcollection (global_State *g) { static void restartcollection (global_State *g) {
g->gray = g->grayagain = NULL; cleargraylists(g);
g->weak = g->allweak = g->ephemeron = NULL;
markobject(g, g->mainthread); markobject(g, g->mainthread);
markvalue(g, &g->l_registry); markvalue(g, &g->l_registry);
markmt(g); markmt(g);
@ -374,6 +412,26 @@ static void restartcollection (global_State *g) {
** ======================================================= ** =======================================================
*/ */
/*
** Check whether object 'o' should be kept in the 'grayagain' list for
** post-processing by 'correctgraylist'. (It could put all old objects
** in the list and leave all the work to 'correctgraylist', but it is
** more efficient to avoid adding elements that will be removed.) Only
** TOUCHED1 objects need to be in the list. TOUCHED2 doesn't need to go
** back to a gray list, but then it must become OLD. (That is what
** 'correctgraylist' does when it finds a TOUCHED2 object.)
*/
static void genlink (global_State *g, GCObject *o) {
lua_assert(isblack(o));
if (getage(o) == G_TOUCHED1) { /* touched in this cycle? */
linkobjgclist(o, g->grayagain); /* link it back in 'grayagain' */
} /* everything else do not need to be linked back */
else if (getage(o) == G_TOUCHED2)
changeage(o, G_TOUCHED2, G_OLD); /* advance age */
}
/* /*
** Traverse a table with weak values and link it to proper list. During ** 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 ** propagate phase, keep it in 'grayagain' list, to be revisited in the
@ -410,8 +468,9 @@ static void traverseweakvalue (global_State *g, Table *h) {
** the atomic phase, if table has any white->white entry, it has to ** the atomic phase, if table has any white->white entry, it has to
** be revisited during ephemeron convergence (as that key may turn ** be revisited during ephemeron convergence (as that key may turn
** black). Otherwise, if it has any white key, table has to be cleared ** black). Otherwise, if it has any white key, table has to be cleared
** (in the atomic phase). In generational mode, it (like all visited ** (in the atomic phase). In generational mode, some tables
** tables) must be kept in some gray list for post-processing. ** 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) { static int traverseephemeron (global_State *g, Table *h, int inv) {
int marked = 0; /* true if an object is marked in this traversal */ int marked = 0; /* true if an object is marked in this traversal */
@ -450,10 +509,8 @@ static int traverseephemeron (global_State *g, Table *h, int inv) {
linkgclist(h, g->ephemeron); /* have to propagate again */ linkgclist(h, g->ephemeron); /* have to propagate again */
else if (hasclears) /* table has white keys? */ else if (hasclears) /* table has white keys? */
linkgclist(h, g->allweak); /* may have to clean 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 else
gray2black(h); genlink(g, obj2gco(h)); /* check whether collector still needs to see it */
return marked; return marked;
} }
@ -473,10 +530,7 @@ static void traversestrongtable (global_State *g, Table *h) {
markvalue(g, gval(n)); markvalue(g, gval(n));
} }
} }
if (g->gckind == KGC_GEN) { genlink(g, obj2gco(h));
linkgclist(h, g->grayagain); /* keep it in some gray list */
black2gray(h);
}
} }
@ -488,7 +542,6 @@ static lu_mem traversetable (global_State *g, Table *h) {
(cast_void(weakkey = strchr(svalue(mode), 'k')), (cast_void(weakkey = strchr(svalue(mode), 'k')),
cast_void(weakvalue = strchr(svalue(mode), 'v')), cast_void(weakvalue = strchr(svalue(mode), 'v')),
(weakkey || weakvalue))) { /* is really weak? */ (weakkey || weakvalue))) { /* is really weak? */
black2gray(h); /* keep table gray */
if (!weakkey) /* strong keys? */ if (!weakkey) /* strong keys? */
traverseweakvalue(g, h); traverseweakvalue(g, h);
else if (!weakvalue) /* strong values? */ else if (!weakvalue) /* strong values? */
@ -507,10 +560,7 @@ static int traverseudata (global_State *g, Udata *u) {
markobjectN(g, u->metatable); /* mark its metatable */ markobjectN(g, u->metatable); /* mark its metatable */
for (i = 0; i < u->nuvalue; i++) for (i = 0; i < u->nuvalue; i++)
markvalue(g, &u->uv[i].uv); markvalue(g, &u->uv[i].uv);
if (g->gckind == KGC_GEN) { genlink(g, obj2gco(u));
linkgclist(u, g->grayagain); /* keep it in some gray list */
black2gray(u);
}
return 1 + u->nuvalue; return 1 + u->nuvalue;
} }
@ -559,12 +609,21 @@ static int traverseLclosure (global_State *g, LClosure *cl) {
/* /*
** Traverse a thread, marking the elements in the stack up to its top ** Traverse a thread, marking the elements in the stack up to its top
** and cleaning the rest of the stack in the final traversal. ** and cleaning the rest of the stack in the final traversal. That
** That ensures that the entire stack have valid (non-dead) objects. ** 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) { static int traversethread (global_State *g, lua_State *th) {
UpVal *uv; UpVal *uv;
StkId o = th->stack; StkId o = th->stack;
if (isold(th) || g->gcstate == GCSpropagate)
linkgclist(th, g->grayagain); /* insert into 'grayagain' list */
if (o == NULL) if (o == NULL)
return 1; /* stack not completely built yet */ return 1; /* stack not completely built yet */
lua_assert(g->gcstate == GCSatomic || lua_assert(g->gcstate == GCSatomic ||
@ -590,12 +649,11 @@ static int traversethread (global_State *g, lua_State *th) {
/* /*
** traverse one gray object, turning it to black (except for threads, ** traverse one gray object, turning it to black.
** which are always gray).
*/ */
static lu_mem propagatemark (global_State *g) { static lu_mem propagatemark (global_State *g) {
GCObject *o = g->gray; GCObject *o = g->gray;
gray2black(o); nw2black(o);
g->gray = *getgclist(o); /* remove from 'gray' list */ g->gray = *getgclist(o); /* remove from 'gray' list */
switch (o->tt) { switch (o->tt) {
case LUA_VTABLE: return traversetable(g, gco2t(o)); case LUA_VTABLE: return traversetable(g, gco2t(o));
@ -603,12 +661,7 @@ static lu_mem propagatemark (global_State *g) {
case LUA_VLCL: return traverseLclosure(g, gco2lcl(o)); case LUA_VLCL: return traverseLclosure(g, gco2lcl(o));
case LUA_VCCL: return traverseCclosure(g, gco2ccl(o)); case LUA_VCCL: return traverseCclosure(g, gco2ccl(o));
case LUA_VPROTO: return traverseproto(g, gco2p(o)); case LUA_VPROTO: return traverseproto(g, gco2p(o));
case LUA_VTHREAD: { case LUA_VTHREAD: return traversethread(g, gco2th(o));
lua_State *th = gco2th(o);
linkgclist(th, g->grayagain); /* insert into 'grayagain' list */
black2gray(o);
return traversethread(g, th);
}
default: lua_assert(0); return 0; default: lua_assert(0); return 0;
} }
} }
@ -638,8 +691,10 @@ static void convergeephemerons (global_State *g) {
g->ephemeron = NULL; /* tables may return to this list when traversed */ g->ephemeron = NULL; /* tables may return to this list when traversed */
changed = 0; changed = 0;
while ((w = next) != NULL) { /* for each ephemeron table */ while ((w = next) != NULL) { /* for each ephemeron table */
next = gco2t(w)->gclist; /* list is rebuilt during loop */ Table *h = gco2t(w);
if (traverseephemeron(g, gco2t(w), dir)) { /* marked some value? */ 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 */ propagateall(g); /* propagate changes */
changed = 1; /* will have to revisit all ephemeron tables */ changed = 1; /* will have to revisit all ephemeron tables */
} }
@ -766,7 +821,7 @@ static GCObject **sweeplist (lua_State *L, GCObject **p, int countin,
freeobj(L, curr); /* erase 'curr' */ freeobj(L, curr); /* erase 'curr' */
} }
else { /* change mark to 'white' */ 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 */ p = &curr->next; /* go to next element */
} }
} }
@ -823,6 +878,8 @@ static GCObject *udata2finalize (global_State *g) {
resetbit(o->marked, FINALIZEDBIT); /* object is "normal" again */ resetbit(o->marked, FINALIZEDBIT); /* object is "normal" again */
if (issweepphase(g)) if (issweepphase(g))
makewhite(g, o); /* "sweep" object */ 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; return o;
} }
@ -896,15 +953,15 @@ static GCObject **findlast (GCObject **p) {
/* /*
** Move all unreachable objects (or 'all' objects) that need ** Move all unreachable objects (or 'all' objects) that need
** finalization from list 'finobj' to list 'tobefnz' (to be finalized). ** finalization from list 'finobj' to list 'tobefnz' (to be finalized).
** (Note that objects after 'finobjold' cannot be white, so they ** (Note that objects after 'finobjold1' cannot be white, so they
** don't need to be traversed. In incremental mode, 'finobjold' is NULL, ** don't need to be traversed. In incremental mode, 'finobjold1' is NULL,
** so the whole list is traversed.) ** so the whole list is traversed.)
*/ */
static void separatetobefnz (global_State *g, int all) { static void separatetobefnz (global_State *g, int all) {
GCObject *curr; GCObject *curr;
GCObject **p = &g->finobj; GCObject **p = &g->finobj;
GCObject **lastnext = findlast(&g->tobefnz); 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)); lua_assert(tofinalize(curr));
if (!(iswhite(curr) || all)) /* not being collected? */ if (!(iswhite(curr) || all)) /* not being collected? */
p = &curr->next; /* don't bother with it */ p = &curr->next; /* don't bother with it */
@ -920,6 +977,27 @@ static void separatetobefnz (global_State *g, int all) {
} }
/*
** If pointer 'p' points to 'o', move it to the next element.
*/
static void checkpointer (GCObject **p, GCObject *o) {
if (o == *p)
*p = o->next;
}
/*
** Correct pointers to objects inside 'allgc' list when
** object 'o' is being removed from the list.
*/
static void correctpointers (global_State *g, GCObject *o) {
checkpointer(&g->survival, o);
checkpointer(&g->old1, o);
checkpointer(&g->reallyold, o);
checkpointer(&g->firstold1, o);
}
/* /*
** if object 'o' has a finalizer, remove it from 'allgc' list (must ** if object 'o' has a finalizer, remove it from 'allgc' list (must
** search the list to find it) and link it in 'finobj' list. ** search the list to find it) and link it in 'finobj' list.
@ -936,14 +1014,8 @@ void luaC_checkfinalizer (lua_State *L, GCObject *o, Table *mt) {
if (g->sweepgc == &o->next) /* should not remove 'sweepgc' object */ if (g->sweepgc == &o->next) /* should not remove 'sweepgc' object */
g->sweepgc = sweeptolive(L, g->sweepgc); /* change 'sweepgc' */ g->sweepgc = sweeptolive(L, g->sweepgc); /* change 'sweepgc' */
} }
else { /* correct pointers into 'allgc' list, if needed */ else
if (o == g->survival) correctpointers(g, o);
g->survival = o->next;
if (o == g->old)
g->old = o->next;
if (o == g->reallyold)
g->reallyold = o->next;
}
/* search for pointer pointing to 'o' */ /* search for pointer pointing to 'o' */
for (p = &g->allgc; *p != o; p = &(*p)->next) { /* empty */ } for (p = &g->allgc; *p != o; p = &(*p)->next) { /* empty */ }
*p = o->next; /* remove 'o' from 'allgc' list */ *p = o->next; /* remove 'o' from 'allgc' list */
@ -965,24 +1037,31 @@ void luaC_checkfinalizer (lua_State *L, GCObject *o, Table *mt) {
static void setpause (global_State *g); 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 ** Sweep a list of objects to enter generational mode. Deletes dead
** the non dead to old (without changing their colors). ** 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) { static void sweep2old (lua_State *L, GCObject **p) {
GCObject *curr; GCObject *curr;
global_State *g = G(L);
while ((curr = *p) != NULL) { while ((curr = *p) != NULL) {
if (iswhite(curr)) { /* is 'curr' dead? */ if (iswhite(curr)) { /* is 'curr' dead? */
lua_assert(isdead(G(L), curr)); lua_assert(isdead(g, curr));
*p = curr->next; /* remove 'curr' from list */ *p = curr->next; /* remove 'curr' from list */
freeobj(L, curr); /* erase 'curr' */ freeobj(L, curr); /* erase 'curr' */
} }
else { /* all surviving objects become old */ else { /* all surviving objects become old */
setage(curr, G_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 */ p = &curr->next; /* go to next element */
} }
} }
@ -995,9 +1074,13 @@ static void sweep2old (lua_State *L, GCObject **p) {
** during the sweep. So, any white object must be dead.) For ** during the sweep. So, any white object must be dead.) For
** non-dead objects, advance their ages and clear the color of ** non-dead objects, advance their ages and clear the color of
** new objects. (Old objects keep their colors.) ** 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, static GCObject **sweepgen (lua_State *L, global_State *g, GCObject **p,
GCObject *limit) { GCObject *limit, GCObject **pfirstold1) {
static const lu_byte nextage[] = { static const lu_byte nextage[] = {
G_SURVIVAL, /* from G_NEW */ G_SURVIVAL, /* from G_NEW */
G_OLD1, /* from G_SURVIVAL */ G_OLD1, /* from G_SURVIVAL */
@ -1016,9 +1099,15 @@ static GCObject **sweepgen (lua_State *L, global_State *g, GCObject **p,
freeobj(L, curr); /* erase 'curr' */ freeobj(L, curr); /* erase 'curr' */
} }
else { /* correct mark and age */ else { /* correct mark and age */
if (getage(curr) == G_NEW) if (getage(curr) == G_NEW) { /* new objects go back to white */
curr->marked = cast_byte((curr->marked & maskgencolors) | 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)]); 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 */ p = &curr->next; /* go to next element */
} }
} }
@ -1028,58 +1117,50 @@ static GCObject **sweepgen (lua_State *L, global_State *g, GCObject **p,
/* /*
** Traverse a list making all its elements white and clearing their ** 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) { static void whitelist (global_State *g, GCObject *p) {
int white = luaC_white(g); int white = luaC_white(g);
for (; p != NULL; p = p->next) 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 ** Because this correction is done after sweeping, young objects might
** be turned white and still be in the list. They are only removed. ** be turned white and still be in the list. They are only removed.
** For tables and userdata, advance 'touched1' to 'touched2'; 'touched2' ** 'TOUCHED1' objects are advanced to 'TOUCHED2' and remain on the list;
** objects become regular old and are removed from the list. ** Non-white threads also remain on the list; 'TOUCHED2' objects become
** For threads, just remove white ones from the list. ** regular old; they and anything else are removed from the list.
*/ */
static GCObject **correctgraylist (GCObject **p) { static GCObject **correctgraylist (GCObject **p) {
GCObject *curr; GCObject *curr;
while ((curr = *p) != NULL) { while ((curr = *p) != NULL) {
switch (curr->tt) {
case LUA_VTABLE: case LUA_VUSERDATA: {
GCObject **next = getgclist(curr); GCObject **next = getgclist(curr);
if (getage(curr) == G_TOUCHED1) { /* touched in this cycle? */ if (iswhite(curr))
goto remove; /* remove all white objects */
else if (getage(curr) == G_TOUCHED1) { /* touched in this cycle? */
lua_assert(isgray(curr)); lua_assert(isgray(curr));
gray2black(curr); /* make it black, for next barrier */ nw2black(curr); /* make it black, for next barrier */
changeage(curr, G_TOUCHED1, G_TOUCHED2); changeage(curr, G_TOUCHED1, G_TOUCHED2);
p = next; /* go to next element */ goto remain; /* keep it in the list and go to next element */
} }
else { /* not touched in this cycle */ else if (curr->tt == LUA_VTHREAD) {
if (!iswhite(curr)) { /* not white? */ lua_assert(isgray(curr));
lua_assert(isold(curr)); goto remain; /* keep non-white threads on the list */
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 */ else { /* everything else is removed */
*p = *next; /* remove 'curr' from gray list */ lua_assert(isold(curr)); /* young objects should be white here */
} if (getage(curr) == G_TOUCHED2) /* advance from TOUCHED2... */
break; changeage(curr, G_TOUCHED2, G_OLD); /* ... to OLD */
} nw2black(curr); /* make object black (to be removed) */
case LUA_VTHREAD: { goto remove;
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 */
} }
remove: *p = *next; continue;
remain: p = next; continue;
} }
return p; return p;
} }
@ -1100,7 +1181,7 @@ static void correctgraylists (global_State *g) {
/* /*
** Mark 'OLD1' objects when starting a new young collection. ** Mark black 'OLD1' objects when starting a new young collection.
** Gray objects are already in some gray list, and so will be visited ** Gray objects are already in some gray list, and so will be visited
** in the atomic step. ** in the atomic step.
*/ */
@ -1109,13 +1190,12 @@ static void markold (global_State *g, GCObject *from, GCObject *to) {
for (p = from; p != to; p = p->next) { for (p = from; p != to; p = p->next) {
if (getage(p) == G_OLD1) { if (getage(p) == G_OLD1) {
lua_assert(!iswhite(p)); lua_assert(!iswhite(p));
if (isblack(p)) { changeage(p, G_OLD1, G_OLD); /* now they are old */
black2gray(p); /* should be '2white', but gray works too */ if (isblack(p))
reallymarkobject(g, p); reallymarkobject(g, p);
} }
} }
} }
}
/* /*
@ -1131,50 +1211,63 @@ static void finishgencycle (lua_State *L, global_State *g) {
/* /*
** Does a young collection. First, mark 'OLD1' objects. (Only survival ** Does a young collection. First, mark 'OLD1' objects. Then does the
** and "recent old" lists can contain 'OLD1' objects. New lists cannot ** atomic step. Then, sweep all lists and advance pointers. Finally,
** contain 'OLD1' objects, at most 'OLD0' objects that were already ** finish the collection.
** visited when marked old.) 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) { static void youngcollection (lua_State *L, global_State *g) {
GCObject **psurvival; /* to point to first non-dead survival object */ GCObject **psurvival; /* to point to first non-dead survival object */
GCObject *dummy; /* dummy out parameter to 'sweepgen' */
lua_assert(g->gcstate == GCSpropagate); 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->finobj, g->finobjrold);
markold(g, g->tobefnz, NULL);
atomic(L); atomic(L);
/* sweep nursery and get a pointer to its last live element */ /* sweep nursery and get a pointer to its last live element */
psurvival = sweepgen(L, g, &g->allgc, g->survival); g->gcstate = GCSswpallgc;
/* sweep 'survival' and 'old' */ psurvival = sweepgen(L, g, &g->allgc, g->survival, &g->firstold1);
sweepgen(L, g, psurvival, g->reallyold); /* sweep 'survival' */
g->reallyold = g->old; sweepgen(L, g, psurvival, g->old1, &g->firstold1);
g->old = *psurvival; /* 'survival' survivals are old now */ g->reallyold = g->old1;
g->old1 = *psurvival; /* 'survival' survivals are old now */
g->survival = g->allgc; /* all news are survivals */ g->survival = g->allgc; /* all news are survivals */
/* repeat for 'finobj' lists */ /* repeat for 'finobj' lists */
psurvival = sweepgen(L, g, &g->finobj, g->finobjsur); dummy = NULL; /* no 'firstold1' optimization for 'finobj' lists */
/* sweep 'survival' and 'old' */ psurvival = sweepgen(L, g, &g->finobj, g->finobjsur, &dummy);
sweepgen(L, g, psurvival, g->finobjrold); /* sweep 'survival' */
g->finobjrold = g->finobjold; sweepgen(L, g, psurvival, g->finobjold1, &dummy);
g->finobjold = *psurvival; /* 'survival' survivals are old now */ g->finobjrold = g->finobjold1;
g->finobjold1 = *psurvival; /* 'survival' survivals are old now */
g->finobjsur = g->finobj; /* all news are survivals */ g->finobjsur = g->finobj; /* all news are survivals */
sweepgen(L, g, &g->tobefnz, NULL); sweepgen(L, g, &g->tobefnz, NULL, &dummy);
finishgencycle(L, g); 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) { static void atomic2gen (lua_State *L, global_State *g) {
cleargraylists(g);
/* sweep all elements making them old */ /* sweep all elements making them old */
g->gcstate = GCSswpallgc;
sweep2old(L, &g->allgc); sweep2old(L, &g->allgc);
/* everything alive now is old */ /* 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 */ /* repeat for 'finobj' lists */
sweep2old(L, &g->finobj); sweep2old(L, &g->finobj);
g->finobjrold = g->finobjold = g->finobjsur = g->finobj; g->finobjrold = g->finobjold1 = g->finobjsur = g->finobj;
sweep2old(L, &g->tobefnz); sweep2old(L, &g->tobefnz);
@ -1187,8 +1280,9 @@ static void atomic2gen (lua_State *L, global_State *g) {
/* /*
** Enter generational mode. Must go until the end of an atomic cycle ** 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. ** to ensure that all objects are correctly marked and weak tables
** Then, turn all objects into old and finishes the collection. ** are cleared. Then, turn all objects into old and finishes the
** collection.
*/ */
static lu_mem entergen (lua_State *L, global_State *g) { static lu_mem entergen (lua_State *L, global_State *g) {
lu_mem numobjs; lu_mem numobjs;
@ -1207,10 +1301,10 @@ static lu_mem entergen (lua_State *L, global_State *g) {
*/ */
static void enterinc (global_State *g) { static void enterinc (global_State *g) {
whitelist(g, g->allgc); 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->finobj);
whitelist(g, g->tobefnz); whitelist(g, g->tobefnz);
g->finobjrold = g->finobjold = g->finobjsur = NULL; g->finobjrold = g->finobjold1 = g->finobjsur = NULL;
g->gcstate = GCSpause; g->gcstate = GCSpause;
g->gckind = KGC_INC; g->gckind = KGC_INC;
g->lastatomic = 0; g->lastatomic = 0;

View file

@ -12,16 +12,16 @@
#include "lstate.h" #include "lstate.h"
/* /*
** Collectable objects may have one of three colors: white, which ** Collectable objects may have one of three colors: white, which means
** means the object is not marked; gray, which means the ** the object is not marked; gray, which means the object is marked, but
** object is marked, but its references may be not marked; and ** its references may be not marked; and black, which means that the
** black, which means that the object and all its references are marked. ** object and all its references are marked. The main invariant of the
** The main invariant of the garbage collector, while marking objects, ** garbage collector, while marking objects, is that a black object can
** is that a black object can never point to a white one. Moreover, ** never point to a white one. Moreover, any gray object must be in a
** any gray object must be in a "gray list" (gray, grayagain, weak, ** "gray list" (gray, grayagain, weak, allweak, ephemeron) so that it
** allweak, ephemeron) so that it can be visited again before finishing ** can be visited again before finishing the collection cycle. (Open
** the collection cycle. These lists have no meaning when the invariant ** upvalues are an exception to this rule.) These lists have no meaning
** is not being enforced (e.g., sweep phase). ** when the invariant is not being enforced (e.g., sweep phase).
*/ */
@ -69,14 +69,16 @@
/* /*
** Layout for bit use in 'marked' field. First three bits are ** Layout for bit use in 'marked' field. First three bits are
** used for object "age" in generational mode. Last bit is free ** used for object "age" in generational mode. Last bit is used
** to be used by respective objects. ** by tests.
*/ */
#define WHITE0BIT 3 /* object is white (type 0) */ #define WHITE0BIT 3 /* object is white (type 0) */
#define WHITE1BIT 4 /* object is white (type 1) */ #define WHITE1BIT 4 /* object is white (type 1) */
#define BLACKBIT 5 /* object is black */ #define BLACKBIT 5 /* object is black */
#define FINALIZEDBIT 6 /* object has been marked for finalization */ #define FINALIZEDBIT 6 /* object has been marked for finalization */
#define TESTBIT 7
#define WHITEBITS bit2mask(WHITE0BIT, WHITE1BIT) #define WHITEBITS bit2mask(WHITE0BIT, WHITE1BIT)
@ -94,7 +96,8 @@
#define isdead(g,v) isdeadm(otherwhite(g), (v)->marked) #define isdead(g,v) isdeadm(otherwhite(g), (v)->marked)
#define changewhite(x) ((x)->marked ^= WHITEBITS) #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) #define luaC_white(g) cast_byte((g)->currentwhite & WHITEBITS)

View file

@ -52,6 +52,12 @@ static int l_checkmode (const char *mode) {
** ======================================================= ** =======================================================
*/ */
#if !defined(l_checkmodep)
/* By default, Lua accepts only "r" or "w" as mode */
#define l_checkmodep(m) ((m[0] == 'r' || m[0] == 'w') && m[1] == '\0')
#endif
#if !defined(l_popen) /* { */ #if !defined(l_popen) /* { */
#if defined(LUA_USE_POSIX) /* { */ #if defined(LUA_USE_POSIX) /* { */
@ -279,6 +285,7 @@ static int io_popen (lua_State *L) {
const char *filename = luaL_checkstring(L, 1); const char *filename = luaL_checkstring(L, 1);
const char *mode = luaL_optstring(L, 2, "r"); const char *mode = luaL_optstring(L, 2, "r");
LStream *p = newprefile(L); LStream *p = newprefile(L);
luaL_argcheck(L, l_checkmodep(mode), 2, "invalid mode");
p->f = l_popen(L, filename, mode); p->f = l_popen(L, filename, mode);
p->closef = &io_pclose; p->closef = &io_pclose;
return (p->f == NULL) ? luaL_fileresult(L, 0, filename) : 1; return (p->f == NULL) ? luaL_fileresult(L, 0, filename) : 1;

View file

@ -81,7 +81,6 @@ void luaX_init (lua_State *L) {
const char *luaX_token2str (LexState *ls, int token) { const char *luaX_token2str (LexState *ls, int token) {
if (token < FIRST_RESERVED) { /* single-byte symbols? */ if (token < FIRST_RESERVED) { /* single-byte symbols? */
lua_assert(token == cast_uchar(token));
if (lisprint(token)) if (lisprint(token))
return luaO_pushfstring(ls->L, "'%c'", token); return luaO_pushfstring(ls->L, "'%c'", token);
else /* control character */ else /* control character */

View file

@ -7,11 +7,17 @@
#ifndef llex_h #ifndef llex_h
#define llex_h #define llex_h
#include <limits.h>
#include "lobject.h" #include "lobject.h"
#include "lzio.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) #if !defined(LUA_ENV)

View file

@ -84,7 +84,15 @@ typedef LUAI_UACNUMBER l_uacNumber;
typedef LUAI_UACINT l_uacInt; 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) #if defined(lua_assert)
#define check_exp(c,e) (lua_assert(c), (e)) #define check_exp(c,e) (lua_assert(c), (e))
/* to avoid problems with conditions too long */ /* to avoid problems with conditions too long */

View file

@ -22,7 +22,7 @@
#include "lstate.h" #include "lstate.h"
#if defined(HARDMEMTESTS) #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 ** and not shrinking a block. (This fail will trigger 'tryagain' and

View file

@ -215,37 +215,42 @@ static lua_Number lua_strx2number (const char *s, char **endptr) {
/* }====================================================== */ /* }====================================================== */
/* maximum length of a numeral */ /* maximum length of a numeral to be converted to a number */
#if !defined (L_MAXLENNUM) #if !defined (L_MAXLENNUM)
#define L_MAXLENNUM 200 #define L_MAXLENNUM 200
#endif #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) { static const char *l_str2dloc (const char *s, lua_Number *result, int mode) {
char *endptr; char *endptr;
*result = (mode == 'x') ? lua_strx2number(s, &endptr) /* try to convert */ *result = (mode == 'x') ? lua_strx2number(s, &endptr) /* try to convert */
: lua_str2number(s, &endptr); : lua_str2number(s, &endptr);
if (endptr == s) return NULL; /* nothing recognized? */ if (endptr == s) return NULL; /* nothing recognized? */
while (lisspace(cast_uchar(*endptr))) endptr++; /* skip trailing spaces */ 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 ** Convert string 's' to a Lua number (put in 'result') handling the
** on fail or the address of the ending '\0' on success. ** current locale.
** '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)
** This function accepts both the current locale or a dot as the radix ** 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 ** mark. If the conversion fails, it may mean number has a dot but
** locale accepts something else. In that case, the code copies 's' ** locale accepts something else. In that case, the code copies 's'
** to a buffer (because 's' is read-only), changes the dot to the ** to a buffer (because 's' is read-only), changes the dot to the
** current locale radix mark, and tries to convert again. ** 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) { static const char *l_str2d (const char *s, lua_Number *result) {
const char *endptr; 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; int mode = pmode ? ltolower(cast_uchar(*pmode)) : 0;
if (mode == 'n') /* reject 'inf' and 'nan' */ if (mode == 'n') /* reject 'inf' and 'nan' */
return NULL; return NULL;
@ -333,8 +338,15 @@ int luaO_utf8esc (char *buff, unsigned long x) {
} }
/* maximum length of the conversion of a number to a string */ /*
#define MAXNUMBER2STR 50 ** Maximum length of the conversion of a number to a string. Must be
** enough to accommodate both LUA_INTEGER_FMT and LUA_NUMBER_FMT.
** (For a long long int, this is 19 digits plus a sign and a final '\0',
** adding to 21. For a long double, it can go to a sign, 33 digits,
** the dot, an exponent letter, an exponent sign, 5 exponent digits,
** and a final '\0', adding to 43.)
*/
#define MAXNUMBER2STR 44
/* /*
@ -375,7 +387,7 @@ void luaO_tostring (lua_State *L, TValue *obj) {
*/ */
/* size for buffer space used by 'luaO_pushvfstring' */ /* size for buffer space used by 'luaO_pushvfstring' */
#define BUFVFS 400 #define BUFVFS 200
/* buffer used by 'luaO_pushvfstring' */ /* buffer used by 'luaO_pushvfstring' */
typedef struct BuffFS { typedef struct BuffFS {
@ -387,19 +399,17 @@ typedef struct BuffFS {
/* /*
** Push given string to the stack, as part of the buffer. If the stack ** Push given string to the stack, as part of the buffer, and
** is almost full, join all partial strings in the stack into one. ** join the partial strings in the stack into one.
*/ */
static void pushstr (BuffFS *buff, const char *str, size_t l) { static void pushstr (BuffFS *buff, const char *str, size_t l) {
lua_State *L = buff->L; lua_State *L = buff->L;
setsvalue2s(L, L->top, luaS_newlstr(L, str, l)); setsvalue2s(L, L->top, luaS_newlstr(L, str, l));
L->top++; /* may use one extra slot */ L->top++; /* may use one extra slot */
buff->pushed++; buff->pushed++;
if (buff->pushed > 1 && L->top + 1 >= L->stack_last) { luaV_concat(L, buff->pushed); /* join partial results into one */
luaV_concat(L, buff->pushed); /* join all partial results into one */
buff->pushed = 1; buff->pushed = 1;
} }
}
/* /*
@ -521,8 +531,7 @@ const char *luaO_pushvfstring (lua_State *L, const char *fmt, va_list argp) {
} }
addstr2buff(&buff, fmt, strlen(fmt)); /* rest of 'fmt' */ addstr2buff(&buff, fmt, strlen(fmt)); /* rest of 'fmt' */
clearbuff(&buff); /* empty buffer into the stack */ clearbuff(&buff); /* empty buffer into the stack */
if (buff.pushed > 1) lua_assert(buff.pushed == 1);
luaV_concat(L, buff.pushed); /* join all partial results */
return svalue(s2v(L->top - 1)); return svalue(s2v(L->top - 1));
} }

View file

@ -96,7 +96,8 @@ typedef struct TValue {
/* /*
** Any value being manipulated by the program either is non ** Any value being manipulated by the program either is non
** collectable, or the collectable object has the right tag ** 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) \ #define checkliveness(L,obj) \
((void)L, lua_longassert(!iscollectable(obj) || \ ((void)L, lua_longassert(!iscollectable(obj) || \
@ -703,9 +704,9 @@ typedef union Node {
*/ */
#define BITRAS (1 << 7) #define BITRAS (1 << 7)
#define isrealasize(t) (!((t)->marked & BITRAS)) #define isrealasize(t) (!((t)->flags & BITRAS))
#define setrealasize(t) ((t)->marked &= cast_byte(~BITRAS)) #define setrealasize(t) ((t)->flags &= cast_byte(~BITRAS))
#define setnorealasize(t) ((t)->marked |= BITRAS) #define setnorealasize(t) ((t)->flags |= BITRAS)
typedef struct Table { typedef struct Table {

View file

@ -301,6 +301,7 @@ static void preinit_thread (lua_State *L, global_State *g) {
L->openupval = NULL; L->openupval = NULL;
L->status = LUA_OK; L->status = LUA_OK;
L->errfunc = 0; L->errfunc = 0;
L->oldpc = 0;
} }
@ -318,9 +319,10 @@ static void close_state (lua_State *L) {
LUA_API lua_State *lua_newthread (lua_State *L) { LUA_API lua_State *lua_newthread (lua_State *L) {
global_State *g = G(L); global_State *g;
lua_State *L1; lua_State *L1;
lua_lock(L); lua_lock(L);
g = G(L);
luaC_checkGC(L); luaC_checkGC(L);
/* create new thread */ /* create new thread */
L1 = &cast(LX *, luaM_newobject(L, LUA_TTHREAD, sizeof(LX)))->l; L1 = &cast(LX *, luaM_newobject(L, LUA_TTHREAD, sizeof(LX)))->l;
@ -395,6 +397,7 @@ LUA_API lua_State *lua_newstate (lua_Alloc f, void *ud) {
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;
g->Cstacklimit = L->nCcalls = LUAI_MAXCSTACK + CSTACKERR; g->Cstacklimit = L->nCcalls = LUAI_MAXCSTACK + CSTACKERR;
incnny(L); /* main thread is always non yieldable */
g->frealloc = f; g->frealloc = f;
g->ud = ud; g->ud = ud;
g->warnf = NULL; g->warnf = NULL;
@ -410,8 +413,8 @@ LUA_API lua_State *lua_newstate (lua_Alloc f, void *ud) {
g->gckind = KGC_INC; g->gckind = KGC_INC;
g->gcemergency = 0; g->gcemergency = 0;
g->finobj = g->tobefnz = g->fixedgc = NULL; g->finobj = g->tobefnz = g->fixedgc = NULL;
g->survival = g->old = g->reallyold = NULL; g->firstold1 = g->survival = g->old1 = g->reallyold = NULL;
g->finobjsur = g->finobjold = g->finobjrold = NULL; g->finobjsur = g->finobjold1 = g->finobjrold = NULL;
g->sweepgc = NULL; g->sweepgc = NULL;
g->gray = g->grayagain = NULL; g->gray = g->grayagain = NULL;
g->weak = g->ephemeron = g->allweak = NULL; g->weak = g->ephemeron = g->allweak = NULL;
@ -436,8 +439,8 @@ LUA_API lua_State *lua_newstate (lua_Alloc f, void *ud) {
LUA_API void lua_close (lua_State *L) { LUA_API void lua_close (lua_State *L) {
L = G(L)->mainthread; /* only the main thread can be closed */
lua_lock(L); lua_lock(L);
L = G(L)->mainthread; /* only the main thread can be closed */
close_state(L); close_state(L);
} }

View file

@ -32,13 +32,29 @@
** **
** 'allgc' -> 'survival': new objects; ** 'allgc' -> 'survival': new objects;
** 'survival' -> 'old': objects that survived one collection; ** '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. ** 'reallyold' -> NULL: objects old for more than one cycle.
** **
** 'finobj' -> 'finobjsur': new objects marked for finalization; ** 'finobj' -> 'finobjsur': new objects marked for finalization;
** 'finobjsur' -> 'finobjold': survived """"; ** 'finobjsur' -> 'finobjold1': survived """";
** 'finobjold' -> 'finobjrold': just old """"; ** 'finobjold1' -> 'finobjrold': just old """";
** 'finobjrold' -> NULL: really old """". ** 'finobjrold' -> NULL: really old """".
**
** All lists can contain elements older than their main ages, due
** to 'luaC_checkfinalizer' and 'udata2finalize', which move
** objects between the normal lists and the "marked for finalization"
** lists. Moreover, barriers can age young objects in young lists as
** OLD0, which then become OLD1. However, a list never contains
** elements younger than their main ages.
**
** The generational collector also uses a pointer 'firstold1', which
** points to the first OLD1 object in the list. It is used to optimize
** 'markold'. (Potentially OLD1 objects can be anywhere between 'allgc'
** and 'reallyold', but often the list has no OLD1 objects or they are
** after 'old1'.) Note the difference between it and 'old1':
** 'firstold1': no OLD1 objects before this point; there can be all
** ages after it.
** 'old1': no objects younger than OLD1 after this point.
*/ */
/* /*
@ -47,7 +63,7 @@
** can become gray have such a field. The field is not the same ** 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 ** 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 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. ** 'gray': regular gray objects, still waiting to be visited.
** 'grayagain': objects that must be revisited at the atomic phase. ** 'grayagain': objects that must be revisited at the atomic phase.
@ -58,6 +74,14 @@
** 'weak': tables with weak values to be cleared; ** 'weak': tables with weak values to be cleared;
** 'ephemeron': ephemeron tables with white->white entries; ** 'ephemeron': ephemeron tables with white->white entries;
** 'allweak': tables with weak keys and/or weak values to be cleared. ** '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.)
*/ */
@ -257,10 +281,11 @@ typedef struct global_State {
GCObject *fixedgc; /* list of objects not to be collected */ GCObject *fixedgc; /* list of objects not to be collected */
/* fields for generational collector */ /* fields for generational collector */
GCObject *survival; /* start of objects that survived one GC cycle */ GCObject *survival; /* start of objects that survived one GC cycle */
GCObject *old; /* start of old objects */ GCObject *old1; /* start of old1 objects */
GCObject *reallyold; /* old objects with more than one cycle */ 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 *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 */ GCObject *finobjrold; /* list of really old objects with finalizers */
struct lua_State *twups; /* list of threads with open upvalues */ struct lua_State *twups; /* list of threads with open upvalues */
lua_CFunction panic; /* to be called in unprotected errors */ lua_CFunction panic; /* to be called in unprotected errors */
@ -286,7 +311,6 @@ struct lua_State {
StkId top; /* first free slot in the stack */ StkId top; /* first free slot in the stack */
global_State *l_G; global_State *l_G;
CallInfo *ci; /* call info for current function */ CallInfo *ci; /* call info for current function */
const Instruction *oldpc; /* last pc traced */
StkId stack_last; /* last free slot in the stack */ StkId stack_last; /* last free slot in the stack */
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 */
@ -297,6 +321,7 @@ struct lua_State {
volatile lua_Hook hook; volatile lua_Hook hook;
ptrdiff_t errfunc; /* current error handling function (stack index) */ ptrdiff_t errfunc; /* current error handling function (stack index) */
l_uint32 nCcalls; /* number of allowed nested C calls - 'nci' */ l_uint32 nCcalls; /* number of allowed nested C calls - 'nci' */
int oldpc; /* last pc traced */
int stacksize; int stacksize;
int basehookcount; int basehookcount;
int hookcount; int hookcount;
@ -309,6 +334,12 @@ struct lua_State {
/* /*
** Union of all collectable objects (only for conversions) ** 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 { union GCUnion {
GCObject gc; /* common header */ GCObject gc; /* common header */
@ -322,6 +353,11 @@ union GCUnion {
}; };
/*
** ISO C99, 6.7.2.1 p.14:
** "A pointer to a union object, suitably converted, points to each of
** its members [...], and vice versa."
*/
#define cast_u(o) cast(union GCUnion *, (o)) #define cast_u(o) cast(union GCUnion *, (o))
/* macros to convert a GCObject into a specific value */ /* macros to convert a GCObject into a specific value */

View file

@ -583,7 +583,7 @@ Table *luaH_new (lua_State *L) {
GCObject *o = luaC_newobj(L, LUA_VTABLE, sizeof(Table)); GCObject *o = luaC_newobj(L, LUA_VTABLE, sizeof(Table));
Table *t = gco2t(o); Table *t = gco2t(o);
t->metatable = NULL; t->metatable = NULL;
t->flags = cast_byte(~0); t->flags = cast_byte(maskflags); /* table has no metamethod fields */
t->array = NULL; t->array = NULL;
t->alimit = 0; t->alimit = 0;
setnodevector(L, t, 0); setnodevector(L, t, 0);

View file

@ -15,7 +15,12 @@
#define gnext(n) ((n)->u.next) #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 */ /* true when 't' is using 'dummynode' as its hash part */

View file

@ -240,7 +240,7 @@ void luaT_adjustvarargs (lua_State *L, int nfixparams, CallInfo *ci,
int actual = cast_int(L->top - ci->func) - 1; /* number of arguments */ int actual = cast_int(L->top - ci->func) - 1; /* number of arguments */
int nextra = actual - nfixparams; /* number of extra arguments */ int nextra = actual - nfixparams; /* number of extra arguments */
ci->u.l.nextraargs = nextra; ci->u.l.nextraargs = nextra;
checkstackGC(L, p->maxstacksize + 1); luaD_checkstack(L, p->maxstacksize + 1);
/* copy function to the top of the stack */ /* copy function to the top of the stack */
setobjs2s(L, L->top++, ci->func); setobjs2s(L, L->top++, ci->func);
/* move fixed parameters to the top of the stack */ /* move fixed parameters to the top of the stack */
@ -259,7 +259,7 @@ void luaT_getvarargs (lua_State *L, CallInfo *ci, StkId where, int wanted) {
int nextra = ci->u.l.nextraargs; int nextra = ci->u.l.nextraargs;
if (wanted < 0) { if (wanted < 0) {
wanted = nextra; /* get all extra arguments available */ 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 */ L->top = where + nextra; /* next instruction will need top */
} }
for (i = 0; i < wanted && i < nextra; i++) for (i = 0; i < wanted && i < nextra; i++)

View file

@ -45,6 +45,15 @@ typedef enum {
} TMS; } 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. ** Test whether there is no tagmethod.
** (Because tagmethods use raw accesses, the result may be an "empty" nil.) ** (Because tagmethods use raw accesses, the result may be an "empty" nil.)

View file

@ -18,7 +18,7 @@
#define LUA_VERSION_MAJOR "5" #define LUA_VERSION_MAJOR "5"
#define LUA_VERSION_MINOR "4" #define LUA_VERSION_MINOR "4"
#define LUA_VERSION_RELEASE "0" #define LUA_VERSION_RELEASE "1"
#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)

View file

@ -120,7 +120,10 @@ static TString *loadStringN (LoadState *S, Proto *p) {
} }
else { /* long string */ else { /* long string */
ts = luaS_createlngstrobj(L, size); /* create 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 */ loadVector(S, getstr(ts), size); /* load directly in final place */
L->top--; /* pop string */
} }
luaC_objbarrier(L, p, ts); luaC_objbarrier(L, p, ts);
return ts; return ts;
@ -200,13 +203,20 @@ static void loadProtos (LoadState *S, Proto *f) {
} }
/*
** Load the upvalues for a function. The names must be filled first,
** because the filling of the other fields can raise read errors and
** the creation of the error message can call an emergency collection;
** in that case all prototypes must be consistent for the GC.
*/
static void loadUpvalues (LoadState *S, Proto *f) { static void loadUpvalues (LoadState *S, Proto *f) {
int i, n; int i, n;
n = loadInt(S); n = loadInt(S);
f->upvalues = luaM_newvectorchecked(S->L, n, Upvaldesc); f->upvalues = luaM_newvectorchecked(S->L, n, Upvaldesc);
f->sizeupvalues = n; f->sizeupvalues = n;
for (i = 0; i < n; i++) { for (i = 0; i < n; i++) /* make array valid for GC */
f->upvalues[i].name = NULL; f->upvalues[i].name = NULL;
for (i = 0; i < n; i++) { /* following calls can raise errors */
f->upvalues[i].instack = loadByte(S); f->upvalues[i].instack = loadByte(S);
f->upvalues[i].idx = loadByte(S); f->upvalues[i].idx = loadByte(S);
f->upvalues[i].kind = loadByte(S); f->upvalues[i].kind = loadByte(S);

View file

@ -634,7 +634,8 @@ static void copy2buff (StkId top, int n, char *buff) {
** from 'L->top - total' up to 'L->top - 1'. ** from 'L->top - total' up to 'L->top - 1'.
*/ */
void luaV_concat (lua_State *L, int total) { void luaV_concat (lua_State *L, int total) {
lua_assert(total >= 2); if (total == 1)
return; /* "all" values already concatenated */
do { do {
StkId top = L->top; StkId top = L->top;
int n = 2; /* number of elements handled in this pass (at least 2) */ int n = 2; /* number of elements handled in this pass (at least 2) */
@ -840,10 +841,8 @@ void luaV_finishOp (lua_State *L) {
int a = GETARG_A(inst); /* first element to concatenate */ int a = GETARG_A(inst); /* first element to concatenate */
int total = cast_int(top - 1 - (base + a)); /* yet to concatenate */ int total = cast_int(top - 1 - (base + a)); /* yet to concatenate */
setobjs2s(L, top - 2, top); /* put TM result in proper position */ 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) */ L->top = top - 1; /* top is one after last element (at top-2) */
luaV_concat(L, total); /* concat them (may yield again) */ luaV_concat(L, total); /* concat them (may yield again) */
}
break; break;
} }
default: { default: {
@ -1102,9 +1101,9 @@ void luaV_finishOp (lua_State *L) {
/* idem, but without changing the stack */ /* idem, but without changing the stack */
#define halfProtectNT(exp) (savepc(L), (exp)) #define halfProtectNT(exp) (savepc(L), (exp))
/* 'c' is the limit of live values in the stack */
#define checkGC(L,c) \ #define checkGC(L,c) \
{ luaC_condGC(L, L->top = (c), /* limit of live values */ \ { luaC_condGC(L, (savepc(L), L->top = (c)), \
updatetrap(ci)); \ updatetrap(ci)); \
luai_threadyield(L); } luai_threadyield(L); }
@ -1635,7 +1634,7 @@ void luaV_execute (lua_State *L, CallInfo *ci) {
while (!ttisfunction(s2v(ra))) { /* not a function? */ while (!ttisfunction(s2v(ra))) { /* not a function? */
luaD_tryfuncTM(L, ra); /* try '__call' metamethod */ luaD_tryfuncTM(L, ra); /* try '__call' metamethod */
b++; /* there is now one extra argument */ b++; /* there is now one extra argument */
checkstackp(L, 1, ra); checkstackGCp(L, 1, ra);
} }
if (!ttisLclosure(s2v(ra))) { /* C function? */ if (!ttisLclosure(s2v(ra))) { /* C function? */
luaD_call(L, ra, LUA_MULTRET); /* call it */ luaD_call(L, ra, LUA_MULTRET); /* call it */
@ -1792,11 +1791,10 @@ void luaV_execute (lua_State *L, CallInfo *ci) {
vmbreak; vmbreak;
} }
vmcase(OP_VARARGPREP) { vmcase(OP_VARARGPREP) {
luaT_adjustvarargs(L, GETARG_A(i), ci, cl->p); ProtectNT(luaT_adjustvarargs(L, GETARG_A(i), ci, cl->p));
updatetrap(ci);
if (trap) { if (trap) {
luaD_hookcall(L, ci); 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 */ updatebase(ci); /* function has new base after adjustment */
vmbreak; vmbreak;