static int traversetable (global_State *g, Table *h) { const TValue *mode = gfasttm(g, h->metatable, TM_MODE); markobject(g, h->metatable); if (mode && ttisstring(mode)) { /* is there a weak mode? */ int weakkey = (strchr(svalue(mode), 'k') != NULL); int weakvalue = (strchr(svalue(mode), 'v') != NULL); if (weakkey || weakvalue) { /* is really weak? */ black2gray(obj2gco(h)); /* keep table gray */ if (!weakkey) { /* strong keys? */ traverseweakvalue(g, h); return TRAVCOST + sizenode(h); } else if (!weakvalue) { /* strong values? */ traverseephemeron(g, h); return TRAVCOST + h->sizearray + sizenode(h); } else { linktable(h, &g->allweak); /* nothing to traverse now */ return TRAVCOST; } } /* else go through */ } traversestrongtable(g, h); return TRAVCOST + h->sizearray + (2 * sizenode(h)); }
static void traverseweakvalue (global_State *g, Table *h) { Node *n, *limit = gnodelast(h); /* if there is array part, assume it may have white values (do not traverse it just to check) */ int hasclears = (h->sizearray > 0); for (n = gnode(h, 0); n < limit; n++) { checkdeadkey(n); if (ttisnil(gval(n))) /* entry is empty? */ removeentry(n); /* remove it */ else { lua_assert(!ttisnil(gkey(n))); markvalue(g, gkey(n)); /* mark key */ if (!hasclears && iscleared(g, gval(n))) /* is there a white value? */ hasclears = 1; /* table will have to be cleared */ } } if (hasclears) linktable(h, &g->weak); /* has to be cleared later */ else /* no white values */ linktable(h, &g->grayagain); /* no need to clean */ }
/* ** mark an object. Userdata, strings, and closed upvalues are visited ** and turned black here. Other objects are marked gray and added ** to appropriate list to be visited (and turned black) later. (Open ** upvalues are already linked in 'headuv' list.) */ static void reallymarkobject (global_State *g, GCObject *o) { lu_mem size; white2gray(o); switch (gch(o)->tt) { case LUA_TSHRSTR: case LUA_TLNGSTR: { size = sizestring(gco2ts(o)); break; /* nothing else to mark; make it black */ } case LUA_TUSERDATA: { Table *mt = gco2u(o)->metatable; markobject(g, mt); markobject(g, gco2u(o)->env); size = sizeudata(gco2u(o)); break; } case LUA_TUPVAL: { UpVal *uv = gco2uv(o); markvalue(g, uv->v); if (uv->v != &uv->u.value) /* open? */ return; /* open upvalues remain gray */ size = sizeof(UpVal); break; } case LUA_TLCL: { gco2lcl(o)->gclist = g->gray; g->gray = o; return; } case LUA_TCCL: { gco2ccl(o)->gclist = g->gray; g->gray = o; return; } case LUA_TTABLE: { linktable(gco2t(o), &g->gray); return; } case LUA_TTHREAD: { gco2th(o)->gclist = g->gray; g->gray = o; return; } case LUA_TPROTO: { gco2p(o)->gclist = g->gray; g->gray = o; return; } default: lua_assert(0); return; } gray2black(o); g->GCmemtrav += size; }
/* ** mark an object. Userdata, strings, and closed upvalues are visited ** and turned black here. Other objects are marked gray and added ** to appropriate list to be visited (and turned black) later. (Open ** upvalues are already linked in 'headuv' list.) */ static void reallymarkobject (global_State *g, GCObject *o) { reentry: white2gray(o); switch (gch(o)->tt) { case LUA_TSHRSTR: case LUA_TLNGSTR: { gray2black(o); g->GCmemtrav += sizestring(gco2ts(o)); break; } case LUA_TUSERDATA: { TValue uvalue; markobject(g, gco2u(o)->metatable); /* mark its metatable */ gray2black(o); g->GCmemtrav += sizeudata(gco2u(o)); getuservalue(g->mainthread, rawgco2u(o), &uvalue); if (valiswhite(&uvalue)) { /* markvalue(g, &uvalue); */ o = gcvalue(&uvalue); goto reentry; } break; } case LUA_TLCL: { gco2lcl(o)->gclist = g->gray; g->gray = o; break; } case LUA_TCCL: { gco2ccl(o)->gclist = g->gray; g->gray = o; break; } case LUA_TTABLE: { linktable(gco2t(o), &g->gray); break; } case LUA_TTHREAD: { gco2th(o)->gclist = g->gray; g->gray = o; break; } case LUA_TPROTO: { gco2p(o)->gclist = g->gray; g->gray = o; break; } default: lua_assert(0); break; } }
static int traverseephemeron (global_State *g, Table *h) { int marked = 0; /* true if an object is marked in this traversal */ int hasclears = 0; /* true if table has white keys */ int prop = 0; /* true if table has entry "white-key -> white-value" */ Node *n, *limit = gnodelast(h); int i; /* traverse array part (numeric keys are 'strong') */ for (i = 0; i < h->sizearray; i++) { if (valiswhite(&h->array[i])) { marked = 1; reallymarkobject(g, gcvalue(&h->array[i])); } } /* traverse hash part */ for (n = gnode(h, 0); n < limit; n++) { checkdeadkey(n); if (ttisnil(gval(n))) /* entry is empty? */ removeentry(n); /* remove it */ else if (iscleared(g, gkey(n))) { /* key is not marked (yet)? */ hasclears = 1; /* table must be cleared */ if (valiswhite(gval(n))) /* value not marked yet? */ prop = 1; /* must propagate again */ } else if (valiswhite(gval(n))) { /* value not marked yet? */ marked = 1; reallymarkobject(g, gcvalue(gval(n))); /* mark it now */ } } if (prop) linktable(h, &g->ephemeron); /* have to propagate again */ else if (hasclears) /* does table have white keys? */ linktable(h, &g->allweak); /* may have to clean white keys */ else /* no white keys */ linktable(h, &g->grayagain); /* no need to clean */ return marked; }
/* ** mark an object. Userdata and closed upvalues are visited and turned ** black here. Strings remain gray (it is the same as making them ** black). Other objects are marked gray and added to appropriate list ** to be visited (and turned black) later. (Open upvalues are already ** linked in 'headuv' list.) */ static void reallymarkobject (global_State *g, GCObject *o) { lua_assert(iswhite(o) && !isdead(g, o)); white2gray(o); switch (gch(o)->tt) { case LUA_TSTRING: { return; /* for strings, gray is as good as black */ } case LUA_TUSERDATA: { Table *mt = gco2u(o)->metatable; markobject(g, mt); markobject(g, gco2u(o)->env); gray2black(o); /* all pointers marked */ return; } case LUA_TUPVAL: { UpVal *uv = gco2uv(o); markvalue(g, uv->v); if (uv->v == &uv->u.value) /* closed? (open upvalues remain gray) */ gray2black(o); /* make it black */ return; } case LUA_TFUNCTION: { gco2cl(o)->c.gclist = g->gray; g->gray = o; break; } case LUA_TTABLE: { linktable(gco2t(o), &g->gray); break; } case LUA_TTHREAD: { gco2th(o)->gclist = g->gray; g->gray = o; break; } case LUA_TPROTO: { gco2p(o)->gclist = g->gray; g->gray = o; break; } default: lua_assert(0); } }
static lu_mem traversetable (global_State *g, Table *h) { const char *weakkey, *weakvalue; const TValue *mode = gfasttm(g, h->metatable, TM_MODE); markobject(g, h->metatable); if (mode && ttisstring(mode) && /* is there a weak mode? */ ((weakkey = strchr(svalue(mode), 'k')), (weakvalue = strchr(svalue(mode), 'v')), (weakkey || weakvalue))) { /* is really weak? */ black2gray(obj2gco(h)); /* keep table gray */ if (!weakkey) /* strong keys? */ traverseweakvalue(g, h); else if (!weakvalue) /* strong values? */ traverseephemeron(g, h); else /* all weak */ linktable(h, &g->allweak); /* nothing to traverse now */ } else /* not weak */ traversestrongtable(g, h); return sizeof(Table) + sizeof(TValue) * h->sizearray + sizeof(Node) * cast(size_t, sizenode(h)); }