/* Upvalues are tricky. Here's why. * * A particular upvalue may be either "open", in which case its member v * points into a thread's stack, or "closed" in which case it points to the * upvalue itself. An upvalue is closed under any of the following conditions: * -- The function that initially declared the variable "local" returns * -- The thread in which the closure was created is garbage collected * * To make things wackier, just because a thread is reachable by Lua doesn't * mean it's in our root set. We need to be able to treat an open upvalue * from an unreachable thread as a closed upvalue. * * The solution: * (a) For the purposes of persisting, don't indicate whether an upvalue is * closed or not. * (b) When unpersisting, pretend that all upvalues are closed. * (c) When persisting, persist all open upvalues referenced by a thread * that is persisted, and tag each one with the corresponding stack position * (d) When unpersisting, "reopen" each of these upvalues as the thread is * unpersisted */ static void persistupval(PersistInfo *pi) { /* perms reftbl ... upval */ UpVal *uv = toupval(pi->L, -1); luaA_pushobject(pi->L, uv->v); /* perms reftbl ... upval obj */ persist(pi); lua_pop(pi->L, 1); /* perms reftbl ... upval */ }
/* The GC is not fond of finding upvalues in tables. We get around this * during persistence using a weakly keyed table, so that the GC doesn't * bother to mark them. This won't work in unpersisting, however, since * if we make the values weak they'll be collected (since nothing else * references them). Our solution, during unpersisting, is to represent * upvalues as dummy functions, each with one upvalue. */ static void boxupval(lua_State *L) { /* ... upval */ LClosure *lcl; UpVal *uv; uv = toupval(L, -1); lua_pop(L, 1); /* ... */ lcl = (LClosure*)luaF_newLclosure(L, 1, &L->_gt); pushclosure(L, (Closure*)lcl); /* ... func */ lcl->p = makefakeproto(L, 1); lcl->upvals[0] = uv; }
/* Upvalues are tricky. Here's why. * * A particular upvalue may be either "open", in which case its member v * points into a thread's stack, or "closed" in which case it points to the * upvalue itself. An upvalue is closed under any of the following conditions: * -- The function that initially declared the variable "local" returns * -- The thread in which the closure was created is garbage collected * * To make things wackier, just because a thread is reachable by Lua doesn't * mean it's in our root set. We need to be able to treat an open upvalue * from an unreachable thread as a closed upvalue. * * The solution: * (a) For the purposes of persisting, don't indicate whether an upvalue is * closed or not. * (b) When unpersisting, pretend that all upvalues are closed. * (c) When persisting, persist all open upvalues referenced by a thread * that is persisted, and tag each one with the corresponding stack position * (d) When unpersisting, "reopen" each of these upvalues as the thread is * unpersisted */ static void persistupval(PersistInfo *pi) { /* perms reftbl ... upval */ UpVal *uv = toupval(pi->L, -1); lua_checkstack(pi->L, 1); /* We can't permit the upval to linger around on the stack, as Lua * will bail if its GC finds it. */ lua_pop(pi->L, 1); /* perms reftbl ... */ LIF(A,pushobject)(pi->L, uv->v); /* perms reftbl ... obj */ persist(pi); /* perms reftbl ... obj */ }
static void unpersistfunction(int ref, UnpersistInfo *upi) { /* perms reftbl ... */ LClosure *lcl; int i; lu_byte nupvalues; verify(luaZ_read(&upi->zio, &nupvalues, sizeof(lu_byte)) == 0); lcl = (LClosure*)luaF_newLclosure(upi->L, nupvalues, &upi->L->_gt); pushclosure(upi->L, (Closure*)lcl); /* perms reftbl ... func */ /* Put *some* proto in the closure, before the GC can find it */ lcl->p = makefakeproto(upi->L, nupvalues); /* Also, we need to temporarily fill the upvalues */ lua_pushnil(upi->L); /* perms reftbl ... func nil */ for(i=0; i<nupvalues; i++) { lcl->upvals[i] = makeupval(upi->L, -1); } lua_pop(upi->L, 1); /* perms reftbl ... func */ /* I can't see offhand how a function would ever get to be self- * referential, but just in case let's register it early */ registerobject(ref, upi); /* Now that it's safe, we can get the real proto */ unpersist(upi); /* perms reftbl ... func proto? */ lua_assert(lua_type(upi->L, -1) == LUA_TPROTO); /* perms reftbl ... func proto */ lcl->p = toproto(upi->L, -1); lua_pop(upi->L, 1); /* perms reftbl ... func */ for(i=0; i<nupvalues; i++) { /* perms reftbl ... func */ unpersist(upi); /* perms reftbl ... func func2 */ unboxupval(upi->L); /* perms reftbl ... func upval */ lcl->upvals[i] = toupval(upi->L, -1); lua_pop(upi->L, 1); /* perms reftbl ... func */ } /* perms reftbl ... func */ /* Finally, the fenv */ unpersist(upi); /* perms reftbl ... func fenv/nil? */ lua_assert(lua_type(upi->L, -1) == LUA_TNIL || lua_type(upi->L, -1) == LUA_TTABLE); /* perms reftbl ... func fenv/nil */ if(!lua_isnil(upi->L, -1)) { /* perms reftbl ... func fenv */ lua_setfenv(upi->L, -2); /* perms reftbl ... func */ } else { /* perms reftbl ... func nil */ lua_pop(upi->L, 1); /* perms reftbl ... func */ } /* perms reftbl ... func */ }
static void unpersistthread(int ref, UnpersistInfo *upi) { /* perms reftbl ... */ lua_State *L2; L2 = lua_newthread(upi->L); /* L1: perms reftbl ... thr */ /* L2: (empty) */ registerobject(ref, upi); /* First, deserialize the object stack. */ { int i, stacksize; verify(luaZ_read(&upi->zio, &stacksize, sizeof(int)) == 0); luaD_growstack(L2, stacksize); /* Make sure that the first stack element (a nil, representing * the imaginary top-level C function) is written to the very, * very bottom of the stack */ L2->top--; for(i=0; i<stacksize; i++) { unpersist(upi); /* L1: perms reftbl ... thr obj* */ } lua_xmove(upi->L, L2, stacksize); /* L1: perms reftbl ... thr */ /* L2: obj* */ } /* Now, deserialize the CallInfo stack. */ { int i, numframes; verify(luaZ_read(&upi->zio, &numframes, sizeof(int)) == 0); luaD_reallocCI(L2,numframes*2); for(i=0; i<numframes; i++) { CallInfo *ci = L2->base_ci + i; int stackbase, stacktop, pc; verify(luaZ_read(&upi->zio, &stackbase, sizeof(int)) == 0); verify(luaZ_read(&upi->zio, &stacktop, sizeof(int)) == 0); verify(luaZ_read(&upi->zio, &pc, sizeof(int)) == 0); verify(luaZ_read(&upi->zio, &(ci->state), sizeof(int)) == 0); ci->base = L2->stack+stackbase; ci->top = L2->stack+stacktop; if(!(ci->state & CI_C)) { ci->u.l.savedpc = ci_func(ci)->l.p->code + pc; } ci->u.l.tailcalls = 0; /* Update the pointer each time, to keep the GC * happy*/ L2->ci = ci; } } /* L1: perms reftbl ... thr */ { int stackbase, stacktop; verify(luaZ_read(&upi->zio, &stackbase, sizeof(int)) == 0); verify(luaZ_read(&upi->zio, &stacktop, sizeof(int)) == 0); L2->base = L2->stack + stackbase; L2->top = L2->stack + stacktop; } /* Finally, "reopen" upvalues (see persistupval() for why) */ { UpVal* uv; GCObject **nextslot = &L2->openupval; while(1) { int stackpos; unpersist(upi); /* perms reftbl ... thr uv/nil */ if(lua_isnil(upi->L, -1)) { /* perms reftbl ... thr nil */ lua_pop(upi->L, 1); /* perms reftbl ... thr */ break; } /* perms reftbl ... thr boxeduv */ unboxupval(upi->L); /* perms reftbl ... thr uv */ uv = toupval(upi->L, -1); lua_pop(upi->L, 1); /* perms reftbl ... thr */ verify(luaZ_read(&upi->zio, &stackpos, sizeof(int)) == 0); uv->v = L2->stack + stackpos; gcunlink(upi->L, valtogco(uv)); uv->marked = 1; *nextslot = valtogco(uv); nextslot = &uv->next; } *nextslot = NULL; } }