/* ** Garbage Collector ** See Copyright Notice in lua.h */ #define FORBIDDEN_SYMBOL_EXCEPTION_setjmp #define FORBIDDEN_SYMBOL_EXCEPTION_longjmp #include "engines/grim/lua/ldo.h" #include "engines/grim/lua/lfunc.h" #include "engines/grim/lua/lgc.h" #include "engines/grim/lua/lmem.h" #include "engines/grim/lua/lobject.h" #include "engines/grim/lua/lstate.h" #include "engines/grim/lua/lstring.h" #include "engines/grim/lua/ltable.h" #include "engines/grim/lua/ltm.h" #include "engines/grim/lua/lua.h" namespace Grim { static int32 markobject (TObject *o); /* ** ======================================================= ** REF mechanism ** ======================================================= */ int32 luaC_ref(TObject *o, int32 lock) { int32 ref; if (ttype(o) == LUA_T_NIL) ref = -1; // special ref for nil else { for (ref = 0; ref < refSize; ref++) if (refArray[ref].status == FREE) goto found; // no more empty spaces { int32 oldSize = refSize; refSize = luaM_growvector(&refArray, refSize, struct ref, refEM, MAX_WORD); for (ref = oldSize; ref < refSize; ref++) { refArray[ref].status = FREE; refArray[ref].o.ttype = LUA_T_NIL; refArray[ref].o.value.ts = nullptr; } ref = oldSize; } found: refArray[ref].o = *o; refArray[ref].status = lock ? LOCK : HOLD; } return ref; } void lua_unref(int32 r) { if (r >= 0 && r < refSize) { refArray[r].status = FREE; refArray[r].o.ttype = LUA_T_NIL; refArray[r].o.value.ts = nullptr; } } TObject* luaC_getref(int32 r) { if (r == -1) return &luaO_nilobject; if (r >= 0 && r < refSize && (refArray[r].status == LOCK || refArray[r].status == HOLD)) return &refArray[r].o; else return nullptr; } static void travlock() { int32 i; for (i = 0; i < refSize; i++) { if (refArray[i].status == LOCK) { markobject(&refArray[i].o); } } } static int32 ismarked(TObject *o) { // valid only for locked objects switch (o->ttype) { case LUA_T_STRING: return o->value.ts->head.marked; case LUA_T_ARRAY: return o->value.a->head.marked; case LUA_T_CLOSURE: return o->value.cl->head.marked; case LUA_T_PROTO: return o->value.tf->head.marked; #ifdef LUA_DEBUG case LUA_T_LINE: case LUA_T_CLMARK: case LUA_T_CMARK: case LUA_T_PMARK: LUA_INTERNALERROR("internal error"); #endif default: // nil, number or cproto return 1; } } static void invalidaterefs() { for (int32 i = 0; i < refSize; i++) if (refArray[i].status == HOLD && !ismarked(&refArray[i].o)) refArray[i].status = COLLECTED; } void luaC_hashcallIM(Hash *l) { TObject t; ttype(&t) = LUA_T_ARRAY; for (; l; l = (Hash *)l->head.next) { avalue(&t) = l; luaD_gcIM(&t); } } void luaC_strcallIM(TaggedString *l) { TObject o; ttype(&o) = LUA_T_USERDATA; for (; l; l=(TaggedString *)l->head.next) { if (l->constindex == -1) { // is userdata? tsvalue(&o) = l; luaD_gcIM(&o); } } } static GCnode *listcollect(GCnode *l) { GCnode *frees = nullptr; while (l) { GCnode *next = l->next; l->marked = 0; while (next && !next->marked) { l->next = next->next; next->next = frees; frees = next; next = l->next; } l = next; } return frees; } static void strmark(TaggedString *s) { if (!s->head.marked) s->head.marked = 1; } static void protomark(TProtoFunc *f) { if (!f->head.marked) { LocVar *v = f->locvars; f->head.marked = 1; if (f->fileName) strmark(f->fileName); for (int32 i = 0; i < f->nconsts; i++) markobject(&f->consts[i]); if (v) { for (; v->line != -1; v++) { if (v->varname) strmark(v->varname); } } } } static void closuremark(Closure *f) { if (!f->head.marked) { f->head.marked = 1; for (int32 i = f->nelems; i >= 0; i--) markobject(&f->consts[i]); } } static void hashmark(Hash *h) { if (!h->head.marked) { h->head.marked = 1; for (int32 i = 0; i < nhash(h); i++) { Node *n = node(h, i); if (ttype(ref(n)) != LUA_T_NIL) { markobject(&n->ref); markobject(&n->val); } } } } static void globalmark() { TaggedString *g; for (g = (TaggedString *)rootglobal.next; g; g = (TaggedString *)g->head.next){ if (g->globalval.ttype != LUA_T_NIL) { markobject(&g->globalval); strmark(g); // cannot collect non nil global variables } } } static int32 markobject(TObject *o) { switch (ttype(o)) { case LUA_T_STRING: strmark(tsvalue(o)); break; case LUA_T_ARRAY: hashmark(avalue(o)); break; case LUA_T_CLOSURE: case LUA_T_CLMARK: closuremark(o->value.cl); break; case LUA_T_PROTO: case LUA_T_PMARK: protomark(o->value.tf); break; default: break; // numbers, cprotos, etc } return 0; } static void markall() { luaD_travstack(markobject); // mark stack objects globalmark(); // mark global variable values and names travlock(); // mark locked objects luaT_travtagmethods(markobject); // mark fallbacks } int32 lua_collectgarbage(int32 limit) { int32 recovered = nblocks; // to subtract nblocks after gc Hash *freetable; TaggedString *freestr; TProtoFunc *freefunc; Closure *freeclos; markall(); invalidaterefs(); freestr = luaS_collector(); freetable = (Hash *)listcollect(&roottable); freefunc = (TProtoFunc *)listcollect(&rootproto); freeclos = (Closure *)listcollect(&rootcl); GCthreshold *= 4; // to avoid GC during GC luaC_hashcallIM(freetable); // GC tag methods for tables luaC_strcallIM(freestr); // GC tag methods for userdata luaD_gcIM(&luaO_nilobject); // GC tag method for nil (signal end of GC) luaH_free(freetable); luaS_free(freestr); luaF_freeproto(freefunc); luaF_freeclosure(freeclos); recovered = recovered - nblocks; GCthreshold = (limit == 0) ? 2 * nblocks : nblocks + limit; return recovered; } void luaC_checkGC() { if (nblocks >= GCthreshold) lua_collectgarbage(0); } } // end of namespace Grim