/* $Id$ */ /* Tamed Pluto - Heavy-duty persistence for Lua * Copyright (C) 2004 by Ben Sunshine-Hill, and released into the public * domain. People making use of this software as part of an application * are politely requested to email the author at sneftel@gmail.com * with a brief description of the application, primarily to satisfy his * curiosity. * * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. * IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY * CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, * TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE * SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. * * Instrumented by Stefan Reich (info@luaos.net) * for Mobile Lua (http://luaos.net/pages/mobile-lua.php) */ #include "config.h" #include "sword25/util/lua/lua.h" #include "pluto.h" #undef TOTEXT #define USE_PDEP #ifdef USE_PDEP #include "pdep/pdep.h" #define LIF(prefix, name) pdep ## _ ## name #else #include "lapi.h" #include "ldo.h" #include "lfunc.h" #include "lgc.h" #include "llimits.h" #include "lmem.h" #include "lobject.h" #include "lopcodes.h" #include "lstate.h" #include "lstring.h" #include "lauxlib.h" #define LIF(prefix, name) lua ## prefix ## _ ## name #endif #include /* Define this if you want size_t values to be written in 64-bit (even on 32-bit systems). Should eliminate at least one source of 32/64 bit incompatibility. */ #define SIZES64 /* #define PLUTO_DEBUG */ #ifdef SIZES64 #define VERSION "Tamed Pluto 1.0 with SIZES64 flag" #else #define VERSION "Tamed Pluto 1.0" #endif #ifdef PLUTO_DEBUG #include #endif #define PLUTO_TPERMANENT 101 #define verify(x) { int v = (int)((x)); v=v; lua_assert(v); } #define NUMTYPES 9 static const char* typenames[] = { "nil", "boolean", "lightuserdata", "number", "string", "table", "function", "userdata", "thread" }; static int humanReadable = 0; #define hrBufSize 200 static char hrBuf[hrBufSize]; typedef struct PersistInfo_t { lua_State *L; int counter; lua_Chunkwriter writer; void *ud; #ifdef PLUTO_DEBUG int level; #endif } PersistInfo; #ifdef PLUTO_DEBUG void printindent(int indent) { int il; for(il=0; ilwriter(pi->L, hrBuf, strlen(hrBuf), ud); for (i = 0; i < size; i++) { char b = ((char *)p)[i]; snprintf(hrBuf, hrBufSize, "%X%X", (b >> 4) & 0xF, b & 0xF); pi->writer(pi->L, hrBuf, strlen(hrBuf), ud); } snprintf(hrBuf, hrBufSize, "\n"); pi->writer(pi->L, hrBuf, strlen(hrBuf), ud); } else { pi->writer(pi->L, p, size, ud); } #ifdef TOTEXT int i; printf(" pi_write %d ", (int) size); for (i = 0; i < size; i++) { char b = ((char *)p)[i]; printf("%X%X", (b >> 4) & 0xF, b & 0xF); } printf("\n"); #endif } static void hrOut(PersistInfo *pi) { pi->writer(pi->L, hrBuf, strlen(hrBuf), pi->ud); } static void write_size(PersistInfo *pi, size_t *val) { #ifdef SIZES64 int64 longval; /* yeah, you really need long long to get 8 bytes on win32... duh. */ longval = *val; pi_write(pi, &longval, 8, pi->ud); if (humanReadable) { snprintf(hrBuf, hrBufSize, "write_size64 %ld\n", longval); hrOut(pi); } #ifdef TOTEXT printf("write_size64 %ld\n", longval); #endif #else pi_write(pi, val, sizeof(size_t), pi->ud); if (humanReadable) { snprintf(hrBuf, hrBufSize, "write_size %ld\n", *((size_t *)val)); hrOut(pi); } #ifdef TOTEXT printf("write_size %ld\n", *val); #endif #endif } static void read_size(ZIO *zio, size_t *val) { #ifdef SIZES64 int64 longval; verify(LIF(Z,read)(zio, &longval, 8) == 0); *val = longval; #else verify(LIF(Z,read)(zio, val, sizeof(size_t)) == 0); #endif } /* Mutual recursion requires prototype */ static void persist(PersistInfo *pi); /* A simple reimplementation of the unfortunately static function luaA_index. * Does not support the global table, registry, or upvalues. */ static StkId getobject(lua_State *L, int stackpos) { if(stackpos > 0) { lua_assert(L->base+stackpos-1 < L->top); return L->base+stackpos-1; } else { lua_assert(L->top-stackpos >= L->base); return L->top+stackpos; } } /* Choose whether to do a regular or special persistence based on an object's * metatable. "default" is whether the object, if it doesn't have a __persist * entry, is literally persistable or not. * Pushes the unpersist closure and returns true if special persistence is * used. */ static int persistspecialobject(PersistInfo *pi, int defaction) { /* perms reftbl ... obj */ lua_checkstack(pi->L, 4); /* Check whether we should persist literally, or via the __persist * metafunction */ if(!lua_getmetatable(pi->L, -1)) { if(defaction) { { int zero = 0; pi_write(pi, &zero, sizeof(int), pi->ud); if (humanReadable) { snprintf(hrBuf, hrBufSize, "persistspecialobject_write_zero\n"); hrOut(pi); } #ifdef TOTEXT printf("persistspecialobject_write_zero\n"); #endif } return 0; } else { lua_pushstring(pi->L, "Type not literally persistable by default"); lua_error(pi->L); } } /* perms reftbl sptbl ... obj mt */ lua_pushstring(pi->L, "__persist"); /* perms reftbl sptbl ... obj mt "__persist" */ lua_rawget(pi->L, -2); /* perms reftbl sptbl ... obj mt __persist? */ if(lua_isnil(pi->L, -1)) { /* perms reftbl sptbl ... obj mt nil */ lua_pop(pi->L, 2); /* perms reftbl sptbl ... obj */ if(defaction) { { int zero = 0; pi_write(pi, &zero, sizeof(int), pi->ud); if (humanReadable) { snprintf(hrBuf, hrBufSize, "persistspecialobject_write_zero2\n"); hrOut(pi); } #ifdef TOTEXT printf("persistspecialobject_write_zero2\n"); #endif } return 0; } else { lua_pushstring(pi->L, "Type not literally persistable by default"); lua_error(pi->L); return 0; /* not reached */ } } else if(lua_isboolean(pi->L, -1)) { /* perms reftbl sptbl ... obj mt bool */ if(lua_toboolean(pi->L, -1)) { /* perms reftbl sptbl ... obj mt true */ lua_pop(pi->L, 2); /* perms reftbl sptbl ... obj */ { int zero = 0; pi_write(pi, &zero, sizeof(int), pi->ud); if (humanReadable) { snprintf(hrBuf, hrBufSize, "persistspecialobject_write_zero3\n"); hrOut(pi); } #ifdef TOTEXT printf("persistspecialobject_write_zero3\n"); #endif } return 0; } else { lua_pushstring(pi->L, "Metatable forbade persistence"); lua_error(pi->L); return 0; /* not reached */ } } else if(!lua_isfunction(pi->L, -1)) { lua_pushstring(pi->L, "__persist not nil, boolean, or function"); lua_error(pi->L); } /* perms reftbl ... obj mt __persist */ lua_pushvalue(pi->L, -3); /* perms reftbl ... obj mt __persist obj */ #ifdef PLUTO_PASS_USERDATA_TO_PERSIST lua_pushlightuserdata(pi->L, (void *)pi->writer); lua_pushlightuserdata(pi->L, pi->ud); /* perms reftbl ... obj mt __persist obj ud */ lua_call(pi->L, 3, 1); /* perms reftbl ... obj mt func? */ #else lua_call(pi->L, 1, 1); /* perms reftbl ... obj mt func? */ #endif /* perms reftbl ... obj mt func? */ if(!lua_isfunction(pi->L, -1)) { lua_pushstring(pi->L, "__persist function did not return a function"); lua_error(pi->L); } /* perms reftbl ... obj mt func */ { int one = 1; pi_write(pi, &one, sizeof(int), pi->ud); if (humanReadable) { snprintf(hrBuf, hrBufSize, "persistspecialobject_write_one\n"); hrOut(pi); } #ifdef TOTEXT printf("persistspecialobject_write_one\n"); #endif } persist(pi); /* perms reftbl ... obj mt func */ lua_pop(pi->L, 2); /* perms reftbl ... obj */ return 1; } static void persisttable(PersistInfo *pi) { if (humanReadable) { snprintf(hrBuf, hrBufSize, "persisttable\n"); hrOut(pi); } #ifdef TOTEXT printf("persisttable\n"); #endif /* perms reftbl ... tbl */ lua_checkstack(pi->L, 3); if(persistspecialobject(pi, 1)) { /* perms reftbl ... tbl */ return; } /* perms reftbl ... tbl */ /* First, persist the metatable (if any) */ if(!lua_getmetatable(pi->L, -1)) { lua_pushnil(pi->L); } /* perms reftbl ... tbl mt/nil */ persist(pi); lua_pop(pi->L, 1); /* perms reftbl ... tbl */ /* Now, persist all k/v pairs */ lua_pushnil(pi->L); /* perms reftbl ... tbl nil */ while(lua_next(pi->L, -2)) { /* perms reftbl ... tbl k v */ lua_pushvalue(pi->L, -2); /* perms reftbl ... tbl k v k */ persist(pi); lua_pop(pi->L, 1); /* perms reftbl ... tbl k v */ persist(pi); lua_pop(pi->L, 1); /* perms reftbl ... tbl k */ } /* perms reftbl ... tbl */ /* Terminate list */ lua_pushnil(pi->L); /* perms reftbl ... tbl nil */ persist(pi); lua_pop(pi->L, 1); /* perms reftbl ... tbl */ } static void persistuserdata(PersistInfo *pi) { /* perms reftbl ... udata */ lua_checkstack(pi->L, 2); if(persistspecialobject(pi, 0)) { /* perms reftbl ... udata */ return; } else { /* Use literal persistence */ size_t length = uvalue(getobject(pi->L, -1))->len; write_size(pi, &length); pi_write(pi, lua_touserdata(pi->L, -1), length, pi->ud); if (humanReadable) { snprintf(hrBuf, hrBufSize, "persistuserdata %ld\n", (long) length); hrOut(pi); } #ifdef TOTEXT printf("persistuserdata %ld\n", (long) length); #endif if(!lua_getmetatable(pi->L, -1)) { /* perms reftbl ... udata */ lua_pushnil(pi->L); /* perms reftbl ... udata mt/nil */ } persist(pi); lua_pop(pi->L, 1); /* perms reftbl ... udata */ } } static Proto *toproto(lua_State *L, int stackpos) { return gco2p(getobject(L, stackpos)->value.gc); } static UpVal *toupval(lua_State *L, int stackpos) { lua_assert(ttype(getobject(L, stackpos)) == LUA_TUPVAL); return gco2uv(getobject(L, stackpos)->value.gc); } static void pushproto(lua_State *L, Proto *proto) { TValue o; setptvalue(L, &o, proto); LIF(A,pushobject)(L, &o); } #define setuvvalue(L,obj,x) \ { TValue *i_o=(obj); \ i_o->value.gc=cast(GCObject *, (x)); i_o->tt=LUA_TUPVAL; \ checkliveness(G(L),i_o); } static void pushupval(lua_State *L, UpVal *upval) { TValue o; setuvvalue(L, &o, upval); LIF(A,pushobject)(L, &o); } static void pushclosure(lua_State *L, Closure *closure) { TValue o; setclvalue(L, &o, closure); LIF(A,pushobject)(L, &o); } static void pushstring(lua_State *L, TString *s) { TValue o; setsvalue(L, &o, s); LIF(A,pushobject)(L, &o); } static void persistfunction(PersistInfo *pi) { /* perms reftbl ... func */ Closure *cl = clvalue(getobject(pi->L, -1)); lua_checkstack(pi->L, 2); if(cl->c.isC) { /* It's a C function. For now, we aren't going to allow * persistence of C closures, even if the "C proto" is * already in the permanents table. */ lua_pushstring(pi->L, "Attempt to persist a C function"); lua_error(pi->L); } else { /* It's a Lua closure. */ { /* We don't really _NEED_ the number of upvals, * but it'll simplify things a bit */ pi_write(pi, &cl->l.p->nups, sizeof(lu_byte), pi->ud); if (humanReadable) { snprintf(hrBuf, hrBufSize, "persistfunction_number_upvalues %d\n", (int) cl->l.p->nups); hrOut(pi); } #ifdef TOTEXT printf("persistfunction_number_upvalues %d\n", (int) cl->l.p->nups); #endif } /* Persist prototype */ { pushproto(pi->L, cl->l.p); /* perms reftbl ... func proto */ persist(pi); lua_pop(pi->L, 1); /* perms reftbl ... func */ } /* Persist upvalue values (not the upvalue objects * themselves) */ { int i; for(i=0; il.p->nups; i++) { /* perms reftbl ... func */ pushupval(pi->L, cl->l.upvals[i]); /* perms reftbl ... func upval */ persist(pi); lua_pop(pi->L, 1); /* perms reftbl ... func */ } /* perms reftbl ... func */ } /* Persist function environment */ { lua_getfenv(pi->L, -1); /* perms reftbl ... func fenv */ if(lua_equal(pi->L, -1, LUA_GLOBALSINDEX)) { /* Function has the default fenv */ /* perms reftbl ... func _G */ lua_pop(pi->L, 1); /* perms reftbl ... func */ lua_pushnil(pi->L); /* perms reftbl ... func nil */ } /* perms reftbl ... func fenv/nil */ persist(pi); lua_pop(pi->L, 1); /* perms reftbl ... func */ } } } /* 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 persistproto(PersistInfo *pi) { /* perms reftbl ... proto */ Proto *p = toproto(pi->L, -1); lua_checkstack(pi->L, 2); /* Persist constant refs */ { int i; pi_write(pi, &p->sizek, sizeof(int), pi->ud); if (humanReadable) { snprintf(hrBuf, hrBufSize, "persistproto_sizek %d\n", p->sizek); hrOut(pi); } #ifdef TOTEXT printf("persistproto_sizek %d\n", p->sizek); #endif for(i=0; isizek; i++) { LIF(A,pushobject)(pi->L, &p->k[i]); /* perms reftbl ... proto const */ persist(pi); lua_pop(pi->L, 1); /* perms reftbl ... proto */ } } /* perms reftbl ... proto */ /* serialize inner Proto refs */ { int i; pi_write(pi, &p->sizep, sizeof(int), pi->ud); if (humanReadable) { snprintf(hrBuf, hrBufSize, "persistproto_sizep %d\n", p->sizep); hrOut(pi); } #ifdef TOTEXT printf("persistproto_sizep %d\n", p->sizep); #endif for(i=0; isizep; i++) { pushproto(pi->L, p->p[i]); /* perms reftbl ... proto subproto */ persist(pi); lua_pop(pi->L, 1); /* perms reftbl ... proto */ } } /* perms reftbl ... proto */ /* Serialize code */ { int len; pi_write(pi, &p->sizecode, sizeof(int), pi->ud); if (humanReadable) { snprintf(hrBuf, hrBufSize, "persistproto_sizecode %d\n", p->sizecode); hrOut(pi); } #ifdef TOTEXT printf("persistproto_sizecode %d\n", p->sizecode); #endif len = sizeof(Instruction) * p->sizecode; pi_write(pi, p->code, len, pi->ud); if (humanReadable) { snprintf(hrBuf, hrBufSize, "persistproto_code %d\n", len); hrOut(pi); } #ifdef TOTEXT printf("persistproto_code %d\n", len); #endif } /* Serialize upvalue names */ { int i; pi_write(pi, &p->sizeupvalues, sizeof(int), pi->ud); if (humanReadable) { snprintf(hrBuf, hrBufSize, "persistproto_upvalues %d\n", p->sizeupvalues); hrOut(pi); } #ifdef TOTEXT printf("persistproto_upvalues %d\n", p->sizeupvalues); #endif for(i=0; isizeupvalues; i++) { pushstring(pi->L, p->upvalues[i]); persist(pi); lua_pop(pi->L, 1); } } /* Serialize local variable infos */ { int i; pi_write(pi, &p->sizelocvars, sizeof(int), pi->ud); if (humanReadable) { snprintf(hrBuf, hrBufSize, "persistproto_sizelocvars %d\n", p->sizelocvars); hrOut(pi); } #ifdef TOTEXT printf("persistproto_sizelocvars %d\n", p->sizelocvars); #endif for(i=0; isizelocvars; i++) { pushstring(pi->L, p->locvars[i].varname); persist(pi); lua_pop(pi->L, 1); pi_write(pi, &p->locvars[i].startpc, sizeof(int), pi->ud); if (humanReadable) { snprintf(hrBuf, hrBufSize, "persistproto_startpc %d\n", p->locvars[i].startpc); hrOut(pi); } #ifdef TOTEXT printf("persistproto_startpc %d\n", p->locvars[i].startpc); #endif pi_write(pi, &p->locvars[i].endpc, sizeof(int), pi->ud); if (humanReadable) { snprintf(hrBuf, hrBufSize, "persistproto_endpc %d\n", p->locvars[i].endpc); hrOut(pi); } #ifdef TOTEXT printf("persistproto_endpc %d\n", p->locvars[i].endpc); #endif } } /* Serialize source string */ pushstring(pi->L, p->source); persist(pi); lua_pop(pi->L, 1); /* Serialize line numbers */ { pi_write(pi, &p->sizelineinfo, sizeof(int), pi->ud); if (humanReadable) { snprintf(hrBuf, hrBufSize, "persistproto_sizelineinfo %d\n", p->sizelineinfo); hrOut(pi); } #ifdef TOTEXT printf("persistproto_sizelineinfo %d\n", p->sizelineinfo); #endif if (p->sizelineinfo) { int len; len = sizeof(int) * p->sizelineinfo; pi_write(pi, p->lineinfo, len, pi->ud); if (humanReadable) { snprintf(hrBuf, hrBufSize, "persistproto_lineinfo %d\n", len); hrOut(pi); } #ifdef TOTEXT printf("persistproto_lineinfo %d\n", len); #endif } } /* Serialize linedefined and lastlinedefined */ pi_write(pi, &p->linedefined, sizeof(int), pi->ud); if (humanReadable) { snprintf(hrBuf, hrBufSize, "persistproto_linedefined %d\n", p->linedefined); hrOut(pi); } #ifdef TOTEXT printf("persistproto_linedefined %d\n", p->linedefined); #endif pi_write(pi, &p->lastlinedefined, sizeof(int), pi->ud); if (humanReadable) { snprintf(hrBuf, hrBufSize, "persistproto_lastlinedefined %d\n", p->lastlinedefined); hrOut(pi); } #ifdef TOTEXT printf("persistproto_lastlinedefined %d\n", p->lastlinedefined); #endif /* Serialize misc values */ { pi_write(pi, &p->nups, sizeof(lu_byte), pi->ud); if (humanReadable) { snprintf(hrBuf, hrBufSize, "persistproto_nups %d\n", (int) p->nups); hrOut(pi); } #ifdef TOTEXT printf("persistproto_nups %d\n", (int) p->nups); #endif pi_write(pi, &p->numparams, sizeof(lu_byte), pi->ud); if (humanReadable) { snprintf(hrBuf, hrBufSize, "persistproto_numparams %d\n", (int) p->numparams); hrOut(pi); } #ifdef TOTEXT printf("persistproto_numparams %d\n", (int) p->numparams); #endif pi_write(pi, &p->is_vararg, sizeof(lu_byte), pi->ud); if (humanReadable) { snprintf(hrBuf, hrBufSize, "persistproto_is_vararg %d\n", (int) p->is_vararg); hrOut(pi); } #ifdef TOTEXT printf("persistproto_is_vararg %d\n", (int) p->is_vararg); #endif pi_write(pi, &p->maxstacksize, sizeof(lu_byte), pi->ud); if (humanReadable) { snprintf(hrBuf, hrBufSize, "persistproto_maxstacksize %d\n", (int) p->maxstacksize); hrOut(pi); } #ifdef TOTEXT printf("persistproto_maxstacksize %d\n", (int) p->maxstacksize); #endif } /* We do not currently persist upvalue names, local variable names, * variable lifetimes, line info, or source code. */ } /* Copies a stack, but the stack is reversed in the process */ static size_t revappendstack(lua_State *from, lua_State *to) { StkId o; for(o=from->top-1; o>=from->stack; o--) { setobj2s(to, to->top, o); to->top++; } return from->top - from->stack; } /* Persist all stack members */ static void persistthread(PersistInfo *pi) { size_t posremaining; lua_State *L2; /* perms reftbl ... thr */ L2 = lua_tothread(pi->L, -1); lua_checkstack(pi->L, L2->top - L2->stack + 1); if(pi->L == L2) { lua_pushstring(pi->L, "Can't persist currently running thread"); lua_error(pi->L); return; /* not reached */ } /* Persist the stack */ posremaining = revappendstack(L2, pi->L); /* perms reftbl ... thr (rev'ed contents of L2) */ write_size(pi, &posremaining); for(; posremaining > 0; posremaining--) { persist(pi); lua_pop(pi->L, 1); } /* perms reftbl ... thr */ /* Now, persist the CallInfo stack. */ { size_t i, numframes = (L2->ci - L2->base_ci) + 1; write_size(pi, &numframes); for(i=0; ibase_ci + i; size_t stackbase = ci->base - L2->stack; size_t stackfunc = ci->func - L2->stack; size_t stacktop = ci->top - L2->stack; size_t savedpc = (ci != L2->base_ci) ? ci->savedpc - ci_func(ci)->l.p->code : 0; write_size(pi, &stackbase); write_size(pi, &stackfunc); write_size(pi, &stacktop); pi_write(pi, &ci->nresults, sizeof(int), pi->ud); if (humanReadable) { snprintf(hrBuf, hrBufSize, "persistthread %d\n", ci->nresults); hrOut(pi); } #ifdef TOTEXT printf("persistthread %d\n", ci->nresults); #endif write_size(pi, &savedpc); } } /* Serialize the state's other parameters, with the exception of upval stuff */ { size_t stackbase = L2->base - L2->stack; size_t stacktop = L2->top - L2->stack; lua_assert(L2->nCcalls <= 1); pi_write(pi, &L2->status, sizeof(lu_byte), pi->ud); if (humanReadable) { snprintf(hrBuf, hrBufSize, "persistthread_status %d\n", (int) L2->status); hrOut(pi); } #ifdef TOTEXT printf("persistthread_status %d\n", (int) L2->status); #endif write_size(pi, &stackbase); write_size(pi, &stacktop); pi_write(pi, &L2->errfunc, sizeof(ptrdiff_t), pi->ud); //write_size(pi, (size_t *)&L2->errfunc); } /* Finally, record upvalues which need to be reopened */ /* See the comment above persistupval() for why we do this */ { GCObject *gco; UpVal *uv; /* perms reftbl ... thr */ for(gco = L2->openupval; gco != NULL; gco = uv->next) { size_t stackpos; uv = gco2uv(gco); /* Make sure upvalue is really open */ lua_assert(uv->v != &uv->u.value); pushupval(pi->L, uv); /* perms reftbl ... thr uv */ persist(pi); lua_pop(pi->L, 1); /* perms reftbl ... thr */ stackpos = uv->v - L2->stack; write_size(pi, &stackpos); } /* perms reftbl ... thr */ lua_pushnil(pi->L); /* perms reftbl ... thr nil */ persist(pi); lua_pop(pi->L, 1); /* perms reftbl ... thr */ } /* perms reftbl ... thr */ } static void persistboolean(PersistInfo *pi) { int b = lua_toboolean(pi->L, -1); pi_write(pi, &b, sizeof(int), pi->ud); if (humanReadable) { snprintf(hrBuf, hrBufSize, "persistboolean %d\n", b); hrOut(pi); } #ifdef TOTEXT printf("persistboolean %d\n", b); #endif } static void persistlightuserdata(PersistInfo *pi) { void *p = lua_touserdata(pi->L, -1); pi_write(pi, &p, sizeof(void *), pi->ud); if (humanReadable) { snprintf(hrBuf, hrBufSize, "persistlightuserdata %p\n", p); hrOut(pi); } #ifdef TOTEXT printf("persistlightuserdata %d\n", (int) p); #endif } static void persistnumber(PersistInfo *pi) { lua_Number n = lua_tonumber(pi->L, -1); pi_write(pi, &n, sizeof(lua_Number), pi->ud); if (humanReadable) { snprintf(hrBuf, hrBufSize, "persistnumber %d (%d)\n", (int) n, (int) sizeof(lua_Number)); hrOut(pi); } #ifdef TOTEXT printf("persistnumber %d (%d)\n", (int) n, (int) sizeof(lua_Number)); #endif } static void persiststring(PersistInfo *pi) { if (humanReadable) { snprintf(hrBuf, hrBufSize, "persiststring\n"); hrOut(pi); } #ifdef TOTEXT printf("persiststring\n"); #endif size_t length = lua_strlen(pi->L, -1); write_size(pi, &length); const char* s = lua_tostring(pi->L, -1); pi_write(pi, s, length, pi->ud); if (humanReadable) { snprintf(hrBuf, hrBufSize, "persiststring %d \"%s\"\n", (int)length, s); hrOut(pi); } #ifdef TOTEXT printf("persiststring %d \"%s\"\n", length, s); #endif } /* Top-level delegating persist function */ static void persist(PersistInfo *pi) { /* perms reftbl ... obj */ lua_checkstack(pi->L, 2); /* If the object has already been written, write a reference to it */ lua_pushvalue(pi->L, -1); /* perms reftbl ... obj obj */ lua_rawget(pi->L, 2); /* perms reftbl ... obj ref? */ if(!lua_isnil(pi->L, -1)) { /* perms reftbl ... obj ref */ int zero = 0; // FIXME: Casting a pointer to an integer data type is a bad idea we // should really get rid of this by fixing the design of this code. // For now casting to size_t should silence most (all?) compilers, // since size_t is supposedly the same size as a pointer on most // (modern) architectures. int ref = (int)(size_t)lua_touserdata(pi->L, -1); pi_write(pi, &zero, sizeof(int), pi->ud); if (humanReadable) { snprintf(hrBuf, hrBufSize, "persist_seenobject\n"); hrOut(pi); } #ifdef TOTEXT printf("persist_seenobject\n"); #endif pi_write(pi, &ref, sizeof(int), pi->ud); if (humanReadable) { snprintf(hrBuf, hrBufSize, "persist_touserdata_ref %d\n", ref); hrOut(pi); } #ifdef TOTEXT printf("persist_touserdata_ref %d\n", ref); #endif lua_pop(pi->L, 1); /* perms reftbl ... obj ref */ #ifdef PLUTO_DEBUG printindent(pi->level); printf("0 %d\n", ref); #endif return; } /* perms reftbl ... obj nil */ lua_pop(pi->L, 1); /* perms reftbl ... obj */ /* If the object is nil, write the pseudoreference 0 */ if(lua_isnil(pi->L, -1)) { int zero = 0; /* firsttime */ pi_write(pi, &zero, sizeof(int), pi->ud); /* ref */ pi_write(pi, &zero, sizeof(int), pi->ud); if (humanReadable) { snprintf(hrBuf, hrBufSize, "persist_nil (last 2 lines)\n"); hrOut(pi); } #ifdef TOTEXT printf("persist_nil (last 2 lines)\n"); #endif #ifdef PLUTO_DEBUG printindent(pi->level); printf("0 0\n"); #endif return; } { /* indicate that it's the first time */ int one = 1; pi_write(pi, &one, sizeof(int), pi->ud); if (humanReadable) { snprintf(hrBuf, hrBufSize, "persist_newobject\n"); hrOut(pi); } #ifdef TOTEXT printf("persist_newobject\n"); #endif } lua_pushvalue(pi->L, -1); /* perms reftbl ... obj obj */ lua_pushlightuserdata(pi->L, (void *)(++(pi->counter))); /* perms reftbl ... obj obj ref */ lua_rawset(pi->L, 2); /* perms reftbl ... obj */ pi_write(pi, &pi->counter, sizeof(int), pi->ud); if (humanReadable) { snprintf(hrBuf, hrBufSize, "persist_counter %d\n", pi->counter); hrOut(pi); } #ifdef TOTEXT printf("persist_counter %d\n", pi->counter); #endif /* At this point, we'll give the permanents table a chance to play. */ { lua_pushvalue(pi->L, -1); /* perms reftbl ... obj obj */ lua_gettable(pi->L, 1); /* perms reftbl ... obj permkey? */ if(!lua_isnil(pi->L, -1)) { /* perms reftbl ... obj permkey */ int type = PLUTO_TPERMANENT; #ifdef PLUTO_DEBUG printindent(pi->level); printf("1 %d PERM\n", pi->counter); pi->level++; #endif pi_write(pi, &type, sizeof(int), pi->ud); if (humanReadable) { snprintf(hrBuf, hrBufSize, "persist_permtype %d\n", type); hrOut(pi); } #ifdef TOTEXT printf("persist_permtype %d\n", type); #endif persist(pi); lua_pop(pi->L, 1); /* perms reftbl ... obj */ #ifdef PLUTO_DEBUG pi->level--; #endif return; } else { /* perms reftbl ... obj nil */ lua_pop(pi->L, 1); /* perms reftbl ... obj */ } /* perms reftbl ... obj */ } { int type = lua_type(pi->L, -1); pi_write(pi, &type, sizeof(int), pi->ud); if (humanReadable) { snprintf(hrBuf, hrBufSize, "persist %s\n", type >= 0 && type < NUMTYPES ? typenames[type] : "?"); hrOut(pi); } #ifdef TOTEXT printf("persist %s\n", type >= 0 && type < NUMTYPES ? typenames[type] : "?"); #endif #ifdef PLUTO_DEBUG printindent(pi->level); printf("1 %d %d\n", pi->counter, type); pi->level++; #endif } switch(lua_type(pi->L, -1)) { case LUA_TBOOLEAN: persistboolean(pi); break; case LUA_TLIGHTUSERDATA: persistlightuserdata(pi); break; case LUA_TNUMBER: persistnumber(pi); break; case LUA_TSTRING: persiststring(pi); break; case LUA_TTABLE: persisttable(pi); break; case LUA_TFUNCTION: persistfunction(pi); break; case LUA_TTHREAD: persistthread(pi); break; case LUA_TPROTO: persistproto(pi); break; case LUA_TUPVAL: persistupval(pi); break; case LUA_TUSERDATA: persistuserdata(pi); break; default: lua_assert(0); } #ifdef PLUTO_DEBUG pi->level--; #endif } void pluto_persist(lua_State *L, lua_Chunkwriter writer, void *ud) { PersistInfo pi; pi.counter = 0; pi.L = L; pi.writer = writer; pi.ud = ud; #ifdef PLUTO_DEBUG pi.level = 0; #endif lua_checkstack(L, 4); /* perms? rootobj? ...? */ lua_assert(lua_gettop(L) == 2); /* perms rootobj */ lua_assert(!lua_isnil(L, 2)); /* perms rootobj */ lua_newtable(L); /* perms rootobj reftbl */ /* Now we're going to make the table weakly keyed. This prevents the * GC from visiting it and trying to mark things it doesn't want to * mark in tables, e.g. upvalues. All objects in the table are * a priori reachable, so it doesn't matter that we do this. */ lua_newtable(L); /* perms rootobj reftbl mt */ lua_pushstring(L, "__mode"); /* perms rootobj reftbl mt "__mode" */ lua_pushstring(L, "k"); /* perms rootobj reftbl mt "__mode" "k" */ lua_settable(L, 4); /* perms rootobj reftbl mt */ lua_setmetatable(L, 3); /* perms rootobj reftbl */ lua_insert(L, 2); /* perms reftbl rootobj */ persist(&pi); /* perms reftbl rootobj */ lua_remove(L, 2); /* perms rootobj */ } typedef struct WriterInfo_t { char* buf; size_t buflen; } WriterInfo; static int bufwriter (lua_State *L, const void *p, size_t sz, void *ud) { const char *cp = (const char *)p; WriterInfo *wi = (WriterInfo *)ud; LIF(M,reallocvector)(L, wi->buf, wi->buflen, wi->buflen+sz, char); while(sz) { /* how dearly I love ugly C pointer twiddling */ wi->buf[wi->buflen++] = *cp++; sz--; } return 0; } int persist_l(lua_State *L) { /* perms? rootobj? ...? */ WriterInfo wi; wi.buf = NULL; wi.buflen = 0; lua_settop(L, 2); /* perms? rootobj? */ luaL_checktype(L, 1, LUA_TTABLE); /* perms rootobj? */ luaL_checktype(L, 1, LUA_TTABLE); /* perms rootobj */ pluto_persist(L, bufwriter, &wi); lua_settop(L, 0); /* (empty) */ lua_pushlstring(L, wi.buf, wi.buflen); /* str */ pdep_freearray(L, wi.buf, wi.buflen, char); return 1; } typedef struct UnpersistInfo_t { lua_State *L; ZIO zio; #ifdef PLUTO_DEBUG int level; #endif } UnpersistInfo; static void unpersist(UnpersistInfo *upi); /* The object is left on the stack. This is primarily used by unpersist, but * may be used by GCed objects that may incur cycles in order to preregister * the object. */ static void registerobject(int ref, UnpersistInfo *upi) { /* perms reftbl ... obj */ lua_checkstack(upi->L, 2); lua_pushlightuserdata(upi->L, (void *)ref); /* perms reftbl ... obj ref */ lua_pushvalue(upi->L, -2); /* perms reftbl ... obj ref obj */ lua_settable(upi->L, 2); /* perms reftbl ... obj */ } static void unpersistboolean(UnpersistInfo *upi) { /* perms reftbl ... */ int b; lua_checkstack(upi->L, 1); verify(LIF(Z,read)(&upi->zio, &b, sizeof(int)) == 0); lua_pushboolean(upi->L, b); /* perms reftbl ... bool */ } static void unpersistlightuserdata(UnpersistInfo *upi) { /* perms reftbl ... */ void *p; lua_checkstack(upi->L, 1); verify(LIF(Z,read)(&upi->zio, &p, sizeof(void *)) == 0); lua_pushlightuserdata(upi->L, p); /* perms reftbl ... ludata */ } static void unpersistnumber(UnpersistInfo *upi) { /* perms reftbl ... */ lua_Number n; lua_checkstack(upi->L, 1); verify(LIF(Z,read)(&upi->zio, &n, sizeof(lua_Number)) == 0); lua_pushnumber(upi->L, n); /* perms reftbl ... num */ } static void unpersiststring(UnpersistInfo *upi) { /* perms reftbl sptbl ref */ /*int length;*/ size_t length; char* string; lua_checkstack(upi->L, 1); /*verify(LIF(Z,read)(&upi->zio, &length, sizeof(int)) == 0);*/ /*verify(LIF(Z,read)(&upi->zio, &length, sizeof(size_t)) == 0);*/ read_size(&upi->zio, &length); string = pdep_newvector(upi->L, length, char); verify(LIF(Z,read)(&upi->zio, string, length) == 0); lua_pushlstring(upi->L, string, length); /* perms reftbl sptbl ref str */ pdep_freearray(upi->L, string, length, char); } static void unpersistspecialtable(int ref, UnpersistInfo *upi) { /* perms reftbl ... */ lua_checkstack(upi->L, 1); unpersist(upi); /* perms reftbl ... spfunc? */ lua_assert(lua_isfunction(upi->L, -1)); /* perms reftbl ... spfunc */ lua_call(upi->L, 0, 1); /* perms reftbl ... tbl? */ lua_assert(lua_istable(upi->L, -1)); /* perms reftbl ... tbl */ } static void unpersistliteraltable(int ref, UnpersistInfo *upi) { /* perms reftbl ... */ lua_checkstack(upi->L, 3); /* Preregister table for handling of cycles */ lua_newtable(upi->L); /* perms reftbl ... tbl */ registerobject(ref, upi); /* perms reftbl ... tbl */ /* Unpersist metatable */ { unpersist(upi); /* perms reftbl ... tbl mt/nil? */ if(lua_istable(upi->L, -1)) { /* perms reftbl ... tbl mt */ lua_setmetatable(upi->L, -2); /* perms reftbl ... tbl */ } else { /* perms reftbl ... tbl nil? */ lua_assert(lua_isnil(upi->L, -1)); /* perms reftbl ... tbl nil */ lua_pop(upi->L, 1); /* perms reftbl ... tbl */ } /* perms reftbl ... tbl */ } while(1) { /* perms reftbl ... tbl */ unpersist(upi); /* perms reftbl ... tbl key/nil */ if(lua_isnil(upi->L, -1)) { /* perms reftbl ... tbl nil */ lua_pop(upi->L, 1); /* perms reftbl ... tbl */ break; } /* perms reftbl ... tbl key */ unpersist(upi); /* perms reftbl ... tbl key value? */ lua_assert(!lua_isnil(upi->L, -1)); /* perms reftbl ... tbl key value */ lua_rawset(upi->L, -3); /* perms reftbl ... tbl */ } } static void unpersisttable(int ref, UnpersistInfo *upi) { /* perms reftbl ... */ lua_checkstack(upi->L, 1); { int isspecial; verify(LIF(Z,read)(&upi->zio, &isspecial, sizeof(int)) == 0); if(isspecial) { unpersistspecialtable(ref, upi); /* perms reftbl ... tbl */ } else { unpersistliteraltable(ref, upi); /* perms reftbl ... tbl */ } /* perms reftbl ... tbl */ } } static UpVal *makeupval(lua_State *L, int stackpos) { UpVal *uv = pdep_new(L, UpVal); pdep_link(L, (GCObject *)uv, LUA_TUPVAL); uv->tt = LUA_TUPVAL; uv->v = &uv->u.value; uv->u.l.prev = NULL; uv->u.l.next = NULL; setobj(L, uv->v, getobject(L, stackpos)); return uv; } static Proto *makefakeproto(lua_State *L, lu_byte nups) { Proto *p = pdep_newproto(L); p->sizelineinfo = 1; p->lineinfo = pdep_newvector(L, 1, int); p->lineinfo[0] = 1; p->sizecode = 1; p->code = pdep_newvector(L, 1, Instruction); p->code[0] = CREATE_ABC(OP_RETURN, 0, 1, 0); p->source = pdep_newlstr(L, "", 0); p->maxstacksize = 2; p->nups = nups; p->sizek = 0; p->sizep = 0; return p; } /* 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_start(lua_State *L) { LClosure *lcl; lcl = (LClosure *)pdep_newLclosure(L, 1, hvalue(&L->l_gt)); pushclosure(L, (Closure *)lcl); /* ... func */ lcl->p = makefakeproto(L, 1); /* Temporarily initialize the upvalue to nil */ lua_pushnil(L); lcl->upvals[0] = makeupval(L, -1); lua_pop(L, 1); } static void boxupval_finish(lua_State *L) { /* ... func obj */ LClosure *lcl = (LClosure *) clvalue(getobject(L, -2)); lcl->upvals[0]->u.value = *getobject(L, -1); lua_pop(L, 1); } static void unboxupval(lua_State *L) { /* ... func */ LClosure *lcl; UpVal *uv; lcl = (LClosure *)clvalue(getobject(L, -1)); uv = lcl->upvals[0]; lua_pop(L, 1); /* ... */ pushupval(L, uv); /* ... upval */ } static void unpersistfunction(int ref, UnpersistInfo *upi) { /* perms reftbl ... */ LClosure *lcl; int i; lu_byte nupvalues; lua_checkstack(upi->L, 2); verify(LIF(Z,read)(&upi->zio, &nupvalues, sizeof(lu_byte)) == 0); lcl = (LClosure *)pdep_newLclosure(upi->L, nupvalues, hvalue(&upi->L->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; iupvals[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; iL); /* 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 unpersistupval(int ref, UnpersistInfo *upi) { /* perms reftbl ... */ lua_checkstack(upi->L, 2); boxupval_start(upi->L); /* perms reftbl ... func */ registerobject(ref, upi); unpersist(upi); /* perms reftbl ... func obj */ boxupval_finish(upi->L); /* perms reftbl ... func */ } static void unpersistproto(int ref, UnpersistInfo *upi) { /* perms reftbl ... */ Proto *p; int i; int sizep, sizek; /* We have to be careful. The GC expects a lot out of protos. In * particular, we need to give the function a valid string for its * source, and valid code, even before we actually read in the real * code. */ TString *source = pdep_newlstr(upi->L, "", 0); p = pdep_newproto(upi->L); p->source = source; p->sizecode=1; p->code = pdep_newvector(upi->L, 1, Instruction); p->code[0] = CREATE_ABC(OP_RETURN, 0, 1, 0); p->maxstacksize = 2; p->sizek = 0; p->sizep = 0; lua_checkstack(upi->L, 2); pushproto(upi->L, p); /* perms reftbl ... proto */ /* We don't need to register early, since protos can never ever be * involved in cyclic references */ /* Read in constant references */ { verify(LIF(Z,read)(&upi->zio, &sizek, sizeof(int)) == 0); LIF(M,reallocvector)(upi->L, p->k, 0, sizek, TValue); for(i=0; iL, &p->k[i], getobject(upi->L, -1)); p->sizek++; lua_pop(upi->L, 1); /* perms reftbl ... proto */ } /* perms reftbl ... proto */ } /* Read in sub-proto references */ { verify(LIF(Z,read)(&upi->zio, &sizep, sizeof(int)) == 0); LIF(M,reallocvector)(upi->L, p->p, 0, sizep, Proto*); for(i=0; ip[i] = toproto(upi->L, -1); p->sizep++; lua_pop(upi->L, 1); /* perms reftbl ... proto */ } /* perms reftbl ... proto */ } /* Read in code */ { verify(LIF(Z,read)(&upi->zio, &p->sizecode, sizeof(int)) == 0); LIF(M,reallocvector)(upi->L, p->code, 1, p->sizecode, Instruction); verify(LIF(Z,read)(&upi->zio, p->code, sizeof(Instruction) * p->sizecode) == 0); } /* Read in upvalue names */ { verify(LIF(Z,read)(&upi->zio, &p->sizeupvalues, sizeof(int)) == 0); if (p->sizeupvalues) { LIF(M,reallocvector)(upi->L, p->upvalues, 0, p->sizeupvalues, TString *); for(i=0; isizeupvalues; i++) { unpersist(upi); p->upvalues[i] = pdep_newlstr(upi->L, lua_tostring(upi->L, -1), strlen(lua_tostring(upi->L, -1))); lua_pop(upi->L, 1); } } } /* Read in local variable infos */ { verify(LIF(Z,read)(&upi->zio, &p->sizelocvars, sizeof(int)) == 0); if (p->sizelocvars) { LIF(M,reallocvector)(upi->L, p->locvars, 0, p->sizelocvars, LocVar); for(i=0; isizelocvars; i++) { unpersist(upi); p->locvars[i].varname = pdep_newlstr(upi->L, lua_tostring(upi->L, -1), strlen(lua_tostring(upi->L, -1))); lua_pop(upi->L, 1); verify(LIF(Z,read)(&upi->zio, &p->locvars[i].startpc, sizeof(int)) == 0); verify(LIF(Z,read)(&upi->zio, &p->locvars[i].endpc, sizeof(int)) == 0); } } } /* Read in source string*/ unpersist(upi); p->source = pdep_newlstr(upi->L, lua_tostring(upi->L, -1), strlen(lua_tostring(upi->L, -1))); lua_pop(upi->L, 1); /* Read in line numbers */ { verify(LIF(Z,read)(&upi->zio, &p->sizelineinfo, sizeof(int)) == 0); if (p->sizelineinfo) { LIF(M,reallocvector)(upi->L, p->lineinfo, 0, p->sizelineinfo, int); verify(LIF(Z,read)(&upi->zio, p->lineinfo, sizeof(int) * p->sizelineinfo) == 0); } } /* Read in linedefined and lastlinedefined */ verify(LIF(Z,read)(&upi->zio, &p->linedefined, sizeof(int)) == 0); verify(LIF(Z,read)(&upi->zio, &p->lastlinedefined, sizeof(int)) == 0); /* Read in misc values */ { verify(LIF(Z,read)(&upi->zio, &p->nups, sizeof(lu_byte)) == 0); verify(LIF(Z,read)(&upi->zio, &p->numparams, sizeof(lu_byte)) == 0); verify(LIF(Z,read)(&upi->zio, &p->is_vararg, sizeof(lu_byte)) == 0); verify(LIF(Z,read)(&upi->zio, &p->maxstacksize, sizeof(lu_byte)) == 0); } } /* Does basically the opposite of luaC_link(). * Right now this function is rather inefficient; it requires traversing the * entire root GC set in order to find one object. If the GC list were doubly * linked this would be much easier, but there's no reason for Lua to have * that. */ static void gcunlink(lua_State *L, GCObject *gco) { GCObject *prevslot; if(G(L)->rootgc == gco) { G(L)->rootgc = G(L)->rootgc->gch.next; return; } prevslot = G(L)->rootgc; while(prevslot->gch.next != gco) { lua_assert(prevslot->gch.next != NULL); prevslot = prevslot->gch.next; } prevslot->gch.next = prevslot->gch.next->gch.next; } /* FIXME __ALL__ field ordering */ static void unpersistthread(int ref, UnpersistInfo *upi) { /* perms reftbl ... */ lua_State *L2; size_t stacklimit = 0; L2 = lua_newthread(upi->L); lua_checkstack(upi->L, 3); /* L1: perms reftbl ... thr */ /* L2: (empty) */ registerobject(ref, upi); /* First, deserialize the object stack. */ { size_t i, stacksize; read_size(&upi->zio, &stacksize); LIF(D,growstack)(L2, (int)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; iL, L2, stacksize); /* L1: perms reftbl ... thr */ /* L2: obj* */ } /* (hereafter, stacks refer to L1) */ /* Now, deserialize the CallInfo stack. */ { size_t i, numframes; read_size(&upi->zio, &numframes); LIF(D,reallocCI)(L2,numframes*2); for(i=0; ibase_ci + i; size_t stackbase, stackfunc, stacktop, savedpc; read_size(&upi->zio, &stackbase); read_size(&upi->zio, &stackfunc); read_size(&upi->zio, &stacktop); verify(LIF(Z,read)(&upi->zio, &ci->nresults, sizeof(int)) == 0); read_size(&upi->zio, &savedpc); if(stacklimit < stacktop) stacklimit = stacktop; ci->base = L2->stack+stackbase; ci->func = L2->stack+stackfunc; ci->top = L2->stack+stacktop; ci->savedpc = (ci != L2->base_ci) ? ci_func(ci)->l.p->code+savedpc : 0; ci->tailcalls = 0; /* Update the pointer each time, to keep the GC * happy*/ L2->ci = ci; } } /* perms reftbl ... thr */ /* Deserialize the state's other parameters, with the exception of upval stuff */ { size_t stackbase, stacktop; L2->savedpc = L2->ci->savedpc; verify(LIF(Z,read)(&upi->zio, &L2->status, sizeof(lu_byte)) == 0); read_size(&upi->zio, &stackbase); read_size(&upi->zio, &stacktop); verify(LIF(Z,read)(&upi->zio, &L2->errfunc, sizeof(ptrdiff_t)) == 0); //read_size(&upi->zio, (size_t *)&L2->errfunc); L2->base = L2->stack + stackbase; L2->top = L2->stack + stacktop; } /* Finally, "reopen" upvalues (see persistupval() for why) */ { UpVal* uv; GCObject **nextslot = &L2->openupval; global_State *g = G(L2); while(1) { size_t 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 */ read_size(&upi->zio, &stackpos); uv->v = L2->stack + stackpos; gcunlink(upi->L, (GCObject *)uv); uv->marked = luaC_white(g); *nextslot = (GCObject *)uv; nextslot = &uv->next; uv->u.l.prev = &G(L2)->uvhead; uv->u.l.next = G(L2)->uvhead.u.l.next; uv->u.l.next->u.l.prev = uv; G(L2)->uvhead.u.l.next = uv; lua_assert(uv->u.l.next->u.l.prev == uv && uv->u.l.prev->u.l.next == uv); } *nextslot = NULL; } /* The stack must be valid at least to the highest value among the CallInfos */ /* 'top' and the values up to there must be filled with 'nil' */ { StkId o; LIF(D,checkstack)(L2, (int)stacklimit); for (o = L2->top; o <= L2->top + stacklimit; o++) setnilvalue(o); } } static void unpersistuserdata(int ref, UnpersistInfo *upi) { /* perms reftbl ... */ int isspecial; lua_checkstack(upi->L, 2); verify(LIF(Z,read)(&upi->zio, &isspecial, sizeof(int)) == 0); if(isspecial) { unpersist(upi); /* perms reftbl ... spfunc? */ lua_assert(lua_isfunction(upi->L, -1)); /* perms reftbl ... spfunc */ #ifdef PLUTO_PASS_USERDATA_TO_PERSIST lua_pushlightuserdata(upi->L, &upi->zio); lua_call(upi->L, 1, 1); #else lua_call(upi->L, 0, 1); #endif /* perms reftbl ... udata? */ /* This assertion might not be necessary; it's conceivable, for * example, that the SP function might decide to return a table * with equivalent functionality. For the time being, we'll * ignore this possibility in favor of stricter and more testable * requirements. */ lua_assert(lua_isuserdata(upi->L, -1)); /* perms reftbl ... udata */ } else { size_t length; read_size(&upi->zio, &length); lua_newuserdata(upi->L, length); /* perms reftbl ... udata */ registerobject(ref, upi); verify(LIF(Z,read)(&upi->zio, lua_touserdata(upi->L, -1), length) == 0); unpersist(upi); /* perms reftbl ... udata mt/nil? */ lua_assert(lua_istable(upi->L, -1) || lua_isnil(upi->L, -1)); /* perms reftbl ... udata mt/nil */ lua_setmetatable(upi->L, -2); /* perms reftbl ... udata */ } /* perms reftbl ... udata */ } static void unpersistpermanent(int ref, UnpersistInfo *upi) { /* perms reftbl ... */ lua_checkstack(upi->L, 2); unpersist(upi); /* perms reftbl permkey */ lua_gettable(upi->L, 1); /* perms reftbl perm? */ /* We assume currently that the substituted permanent value * shouldn't be nil. This may be a bad assumption. Real-life * experience is needed to evaluate this. */ lua_assert(!lua_isnil(upi->L, -1)); /* perms reftbl perm */ } #if 0 /* For debugging only; not called when lua_assert is empty */ static int inreftable(lua_State *L, int ref) { int res; lua_checkstack(L, 1); /* perms reftbl ... */ lua_pushlightuserdata(L, (void *)ref); /* perms reftbl ... ref */ lua_gettable(L, 2); /* perms reftbl ... obj? */ res = !lua_isnil(L, -1); lua_pop(L, 1); /* perms reftbl ... */ return res; } #endif static void unpersist(UnpersistInfo *upi) { /* perms reftbl ... */ int firstTime; int stacksize = lua_gettop(upi->L); stacksize = stacksize; /* DEBUG */ lua_checkstack(upi->L, 2); LIF(Z,read)(&upi->zio, &firstTime, sizeof(int)); if(firstTime) { int ref; int type; LIF(Z,read)(&upi->zio, &ref, sizeof(int)); lua_assert(!inreftable(upi->L, ref)); LIF(Z,read)(&upi->zio, &type, sizeof(int)); #ifdef PLUTO_DEBUG printindent(upi->level); printf("1 %d %d\n", ref, type); upi->level++; #endif switch(type) { case LUA_TBOOLEAN: unpersistboolean(upi); break; case LUA_TLIGHTUSERDATA: unpersistlightuserdata(upi); break; case LUA_TNUMBER: unpersistnumber(upi); break; case LUA_TSTRING: unpersiststring(upi); break; case LUA_TTABLE: unpersisttable(ref, upi); break; case LUA_TFUNCTION: unpersistfunction(ref, upi); break; case LUA_TTHREAD: unpersistthread(ref, upi); break; case LUA_TPROTO: unpersistproto(ref, upi); break; case LUA_TUPVAL: unpersistupval(ref, upi); break; case LUA_TUSERDATA: unpersistuserdata(ref, upi); break; case PLUTO_TPERMANENT: unpersistpermanent(ref, upi); break; default: lua_assert(0); } /* perms reftbl ... obj */ lua_assert(lua_type(upi->L, -1) == type || type == PLUTO_TPERMANENT || /* Remember, upvalues get a special dispensation, as * described in boxupval */ (lua_type(upi->L, -1) == LUA_TFUNCTION && type == LUA_TUPVAL)); registerobject(ref, upi); /* perms reftbl ... obj */ #ifdef PLUTO_DEBUG upi->level--; #endif } else { int ref; LIF(Z,read)(&upi->zio, &ref, sizeof(int)); #ifdef PLUTO_DEBUG printindent(upi->level); printf("0 %d\n", ref); #endif if(ref == 0) { lua_pushnil(upi->L); /* perms reftbl ... nil */ } else { lua_pushlightuserdata(upi->L, (void *)ref); /* perms reftbl ... ref */ lua_gettable(upi->L, 2); /* perms reftbl ... obj? */ lua_assert(!lua_isnil(upi->L, -1)); } /* perms reftbl ... obj/nil */ } /* perms reftbl ... obj/nil */ lua_assert(lua_gettop(upi->L) == stacksize + 1); } void pluto_unpersist(lua_State *L, lua_Chunkreader reader, void *ud) { /* We use the graciously provided ZIO (what the heck does the Z stand * for?) library so that we don't have to deal with the reader directly. * Letting the reader function decide how much data to return can be * very unpleasant. */ UnpersistInfo upi; upi.L = L; #ifdef PLUTO_DEBUG upi.level = 0; #endif lua_checkstack(L, 3); LIF(Z,init)(L, &upi.zio, reader, ud); /* perms */ lua_newtable(L); /* perms reftbl */ lua_gc(L, LUA_GCSTOP, 0); unpersist(&upi); lua_gc(L, LUA_GCRESTART, 0); /* perms reftbl rootobj */ lua_replace(L, 2); /* perms rootobj */ } typedef struct LoadInfo_t { char *buf; size_t size; } LoadInfo; static const char *bufreader(lua_State *L, void *ud, size_t *sz) { LoadInfo *li = (LoadInfo *)ud; if(li->size == 0) { return NULL; } *sz = li->size; li->size = 0; return li->buf; } int unpersist_l(lua_State *L) { LoadInfo li; char const *origbuf; char *tempbuf; size_t bufsize; /* perms? str? ...? */ lua_settop(L, 2); /* perms? str? */ origbuf = luaL_checklstring(L, 2, &bufsize); tempbuf = LIF(M,newvector)(L, bufsize, char); memcpy(tempbuf, origbuf, bufsize); li.buf = tempbuf; li.size = bufsize; /* perms? str */ lua_pop(L, 1); /* perms? */ luaL_checktype(L, 1, LUA_TTABLE); /* perms */ pluto_unpersist(L, bufreader, &li); /* perms rootobj */ LIF(M,freearray)(L, tempbuf, bufsize, char); return 1; } /* Stefan's first C function for Lua! :) Returns a string describing the Pluto version you're using. */ int version_l(lua_State *L) { const char *version = VERSION; lua_settop(L, 0); /* (empty) */ lua_pushlstring(L, version, strlen(version)); /* str */ return 1; } /* Set human-readable output on or off. */ int human_l(lua_State *L) { /* flag? ...? */ lua_settop(L, 1); /* flag? */ /*luaL_checktype(L, 1, LUA_TBOOLEAN);*/ /* flag */ humanReadable = lua_toboolean(L, 1); lua_settop(L, 0); /* (empty) */ return 0; } static luaL_reg pluto_reg[] = { { "persist", persist_l }, { "unpersist", unpersist_l }, { "version", version_l }, { "human", human_l }, { NULL, NULL } }; LUALIB_API int luaopen_pluto(lua_State *L) { luaL_openlib(L, "pluto", pluto_reg, 0); return 1; }