aboutsummaryrefslogtreecommitdiff
path: root/engines/sword25/util
diff options
context:
space:
mode:
authorAdrian Astley2014-09-17 15:52:26 -0500
committerAdrian Astley2014-12-19 11:29:22 -0600
commitefcd6196eeaa2bff468bbed8d07040e60ca3c136 (patch)
treeffa1b51a52ef81d7b4d59b794a230e29ab75eb3e /engines/sword25/util
parentff35d7118c0a61a472b74d87337ee62eac993ce1 (diff)
downloadscummvm-rg350-efcd6196eeaa2bff468bbed8d07040e60ca3c136.tar.gz
scummvm-rg350-efcd6196eeaa2bff468bbed8d07040e60ca3c136.tar.bz2
scummvm-rg350-efcd6196eeaa2bff468bbed8d07040e60ca3c136.zip
SWORD25: Create a function for serializing lua objects
This function is very similar to the Pluto function. However, this code is much cleaner and is endian-safe
Diffstat (limited to 'engines/sword25/util')
-rw-r--r--engines/sword25/util/lua_serialization.h42
-rw-r--r--engines/sword25/util/lua_serializer.cpp852
2 files changed, 894 insertions, 0 deletions
diff --git a/engines/sword25/util/lua_serialization.h b/engines/sword25/util/lua_serialization.h
new file mode 100644
index 0000000000..0f0c3bd0b2
--- /dev/null
+++ b/engines/sword25/util/lua_serialization.h
@@ -0,0 +1,42 @@
+/* ScummVM - Graphic Adventure Engine
+ *
+ * ScummVM is the legal property of its developers, whose names
+ * are too numerous to list here. Please refer to the COPYRIGHT
+ * file distributed with this source distribution.
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version 2
+ * of the License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+ *
+ */
+
+#ifndef LUA_SERIALIZATION_H
+#define LUA_SERIALIZATION_H
+
+#include "sword25/util/lua/lua.h"
+
+
+namespace Common {
+class WriteStream;
+}
+
+
+namespace Lua {
+
+#define PERMANENT_TYPE 101
+
+void serializeLua(lua_State *luaState, Common::WriteStream *writeStream);
+
+} // End of namespace Lua
+
+#endif
diff --git a/engines/sword25/util/lua_serializer.cpp b/engines/sword25/util/lua_serializer.cpp
new file mode 100644
index 0000000000..b2bbca6be9
--- /dev/null
+++ b/engines/sword25/util/lua_serializer.cpp
@@ -0,0 +1,852 @@
+/* ScummVM - Graphic Adventure Engine
+ *
+ * ScummVM is the legal property of its developers, whose names
+ * are too numerous to list here. Please refer to the COPYRIGHT
+ * file distributed with this source distribution.
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version 2
+ * of the License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+ *
+ */
+
+#include "sword25/util/lua_serialization.h"
+
+#include "sword25/util/double_serializer.h"
+
+#include "common/stream.h"
+
+#include "lua/lobject.h"
+#include "lua/lstate.h"
+
+
+namespace Lua {
+
+#define NUMTYPES 9
+
+static const char* typenames[] = {
+ "nil",
+ "boolean",
+ "lightuserdata",
+ "number",
+ "string",
+ "table",
+ "function",
+ "userdata",
+ "thread"
+};
+
+#define PERMANENT_TYPE 101
+
+/* A simple reimplementation of the unfortunately static function luaA_index.
+ * Does not support the global table, registry, or upvalues. */
+static StkId getobject(lua_State *luaState, int stackpos) {
+ if(stackpos > 0) {
+ lua_assert(luaState->base+stackpos-1 < luaState->top);
+ return luaState->base+stackpos-1;
+ } else {
+ lua_assert(L->top-stackpos >= L->base);
+ return luaState->top+stackpos;
+ }
+}
+
+
+struct SerializationInfo {
+ lua_State *luaState;
+ Common::WriteStream *writeStream;
+ uint counter;
+};
+
+static void serializeObject(SerializationInfo *info);
+
+static void serializeBoolean(SerializationInfo *info);
+static void serializeLightUserData(SerializationInfo *info);
+static void serializeNumber(SerializationInfo *info);
+static void serializeString(SerializationInfo *info);
+static void serializeTable(SerializationInfo *info);
+static void serializeFunction(SerializationInfo *info);
+static void serializeThread(SerializationInfo *info);
+static void serializeProto(SerializationInfo *info);
+static void serializeUpValue(SerializationInfo *info);
+static void serializeUserData(SerializationInfo *info);
+
+
+void serializeLua(lua_State *luaState, Common::WriteStream *writeStream) {
+ SerializationInfo info;
+ info.luaState = luaState;
+ info.writeStream = writeStream;
+ info.counter = 0u;
+
+ // The process starts with the lua stack as follows:
+ // >>>>> permTbl rootObj
+ // That's the table of permanents and the root object to be serialized
+
+ // Make sure there is enough room on the stack
+ lua_checkstack(luaState, 4);
+ assert(lua_gettop(luaState) == 2);
+ // And that the root isn't nil
+ assert(!lua_isnil(luaState, 2));
+
+ // Create a table to hold indexes of everything that's serialized
+ // This allows us to only serialize an object once
+ // Every other time, just reference the index
+ lua_newtable(luaState);
+ // >>>>> permTbl rootObj indexTbl
+
+ // 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.
+
+ // Create the metatable
+ lua_newtable(luaState);
+ // >>>>> permTbl rootObj indexTbl metaTbl
+
+ lua_pushstring(luaState, "__mode");
+ // >>>>> permTbl rootObj indexTbl metaTbl "__mode"
+
+ lua_pushstring(luaState, "k");
+ // >>>>> permTbl rootObj indexTbl metaTbl "__mode" "k"
+
+ lua_settable(luaState, 4);
+ // >>>>> permTbl rootObj indexTbl metaTbl
+
+ lua_setmetatable(luaState, 3);
+ // >>>>> permTbl rootObj indexTbl
+
+ // Swap the indexTable and the rootObj
+ lua_insert(luaState, 2);
+ // >>>>> permTbl indexTbl rootObj
+
+ // Serialize the root recursively
+ serializeObject(&info);
+
+ // Return the stack back to the original state
+ lua_remove(luaState, 2);
+ // >>>>> permTbl rootObj
+}
+
+static void serializeObject(SerializationInfo *info) {
+ // The stack can potentially have many things on it
+ // The object we want to serialize is the item on the top of the stack
+ // >>>>> permTbl indexTbl rootObj ...... obj
+
+ // Make sure there is enough room on the stack
+ lua_checkstack(info->luaState, 2);
+
+ // If the object has already been written, don't write it again
+ // Instead write the index of the object from the indexTbl
+
+ // Check the indexTbl
+ lua_pushvalue(info->luaState, -1);
+ // >>>>> permTbl indexTbl rootObj ...... obj obj
+
+ lua_rawget(info->luaState, 2);
+ // >>>>> permTbl indexTbl rootObj ...... obj ?index?
+
+ // If the index isn't nil, the object has already been written
+ if (!lua_isnil(info->luaState, -1)) {
+ // Write out a flag that indicates that it's an index
+ info->writeStream->writeByte(0);
+
+ // Retrieve the index from the stack
+ uint *index = (uint *)lua_touserdata(info->luaState, -1);
+
+ // Write out the index
+ info->writeStream->writeUint32LE(*index);
+
+ // Pop the index off the stack
+ lua_pop(info->luaState, 1);
+
+ return;
+ }
+
+ // Pop the nil off the stack
+ lua_pop(info->luaState, 1);
+
+ // Write out a flag that indicates that this is a real object
+ info->writeStream->writeByte(1);
+
+ // If the object itself is nil, then write out a zero as a placeholder
+ if (lua_isnil(info->luaState, -1)) {
+ info->writeStream->writeByte(0);
+
+ return;
+ }
+
+ // Add the object to the indexTbl
+
+ lua_pushvalue(info->luaState, -1);
+ // >>>>> permTbl indexTbl rootObj ...... obj obj
+
+ uint *ref = (uint *)lua_newuserdata(info->luaState, sizeof(uint));
+ *ref = ++(info->counter);
+ // >>>>> permTbl indexTbl rootObj ...... obj obj index
+
+ lua_rawset(info->luaState, 2);
+ // >>>>> permTbl indexTbl rootObj ...... obj
+
+
+ // Write out the index
+ info->writeStream->writeUint32LE(info->counter);
+
+
+ // Objects that are in the permanents table are serialized in a special way
+
+ lua_pushvalue(info->luaState, -1);
+ // >>>>> permTbl indexTbl rootObj ...... obj obj
+
+ lua_gettable(info->luaState, 1);
+ // >>>>> permTbl indexTbl rootObj ...... obj obj ?permKey?
+
+ if (!lua_isnil(info->luaState, -1)) {
+ // Write out the type
+ info->writeStream->writeSint32LE(PERMANENT_TYPE);
+
+ // Serialize the key
+ serializeObject(info);
+
+ // Pop the key off the stack
+ lua_pop(info->luaState, 1);
+
+ return;
+ }
+
+ // Pop the nil off the stack
+ lua_pop(info->luaState, 1);
+
+ // Query the type of the object
+ int objType = lua_type(info->luaState, -1);
+
+ // Write it out
+ info->writeStream->writeSint32LE(objType);
+
+ // Serialize the object by its type
+
+ switch (objType) {
+ case LUA_TBOOLEAN:
+ serializeBoolean(info);
+ break;
+ case LUA_TLIGHTUSERDATA:
+ // You can't serialize a pointer
+ // It would be meaningless on the next run
+ assert(0);
+ break;
+ case LUA_TNUMBER:
+ serializeNumber(info);
+ break;
+ case LUA_TSTRING:
+ serializeString(info);
+ break;
+ case LUA_TTABLE:
+ serializeTable(info);
+ break;
+ case LUA_TFUNCTION:
+ serializeFunction(info);
+ break;
+ case LUA_TTHREAD:
+ serializeThread(info);
+ break;
+ case LUA_TPROTO:
+ serializeProto(info);
+ break;
+ case LUA_TUPVAL:
+ serializeUpValue(info);
+ break;
+ case LUA_TUSERDATA:
+ serializeUserData(info);
+ break;
+ default:
+ assert(0);
+ }
+}
+
+static void serializeBoolean(SerializationInfo *info) {
+ int value = lua_toboolean(info->luaState, -1);
+
+ info->writeStream->writeSint32LE(value);
+}
+
+static void serializeNumber(SerializationInfo *info) {
+ lua_Number value = lua_tonumber(info->luaState, -1);
+
+#if 1
+ Util::SerializedDouble serializedValue(Util::encodeDouble(value));
+
+ info->writeStream->writeUint32LE(serializedValue.significandOne);
+ info->writeStream->writeUint32LE(serializedValue.signAndSignificandTwo);
+ info->writeStream->writeSint16LE(serializedValue.exponent);
+#else
+ // NOTE: We need to store a double. Unfortunately, we have to accommodate endianness.
+ // Also, I don't know if we can assume all compilers use IEEE double
+ // Therefore, I have chosen to store the double as a string.
+ Common::String buffer = Common::String::format("%f", value);
+
+ info->writeStream->write(buffer.c_str(), buffer.size());
+#endif
+
+}
+
+static void serializeString(SerializationInfo *info) {
+ // Hard cast to a uint32 to force size_t to an explicit size
+ // *Theoretically* this could truncate, but if we have a 4gb string, we have bigger problems
+ uint32 length = static_cast<uint32>(lua_strlen(info->luaState, -1));
+ info->writeStream->writeUint32LE(length);
+
+ const char* str = lua_tostring(info->luaState, -1);
+ info->writeStream->write(str, length);
+}
+
+/* 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 bool serializeSpecialObject(SerializationInfo *info, bool defaction) {
+ // Make sure there is enough room on the stack
+ lua_checkstack(info->luaState, 4);
+
+ // Check whether we should persist literally, or via the __persist metafunction
+ if (!lua_getmetatable(info->luaState, -1)) {
+ if (defaction) {
+ // Write out a flag declaring that the metatable doesn't exist
+ info->writeStream->writeSint32LE(0);
+
+ return false;
+ } else {
+ lua_pushstring(info->luaState, "Type not literally persistable by default");
+ lua_error(info->luaState);
+ }
+ }
+
+ // >>>>> permTbl indexTbl ...... obj metaTbl
+ lua_pushstring(info->luaState, "__persist");
+ // >>>>> permTbl indexTbl rootObj ...... obj metaTbl "__persist"
+
+ lua_rawget(info->luaState, -2);
+ // >>>>> permTbl indexTbl ...... obj metaTbl ?__persist?
+
+ if (lua_isnil(info->luaState, -1)) {
+ // >>>>> permTbl indexTbl ...... obj metaTbl nil
+ lua_pop(info->luaState, 2);
+ // >>>>> permTbl indexTbl ...... obj
+
+ if (defaction) {
+ // Write out a flag declaring there is no persistence metafunction
+ info->writeStream->writeSint32LE(0);
+
+ return 0;
+ } else {
+ lua_pushstring(info->luaState, "Type not literally persistable by default");
+ lua_error(info->luaState);
+
+ return 0; /* not reached */
+ }
+
+ } else if (lua_isboolean(info->luaState, -1)) {
+ // >>>>> permTbl indexTbl ...... obj metaTbl bool
+ if (lua_toboolean(info->luaState, -1)) {
+ // Write out a flag declaring such
+ info->writeStream->writeSint32LE(0);
+
+ // >>>>> permTbl indexTbl ...... obj metaTbl true */
+ lua_pop(info->luaState, 2);
+ // >>>>> permTbl indexTbl ...... obj
+
+ return false;
+ } else {
+ lua_pushstring(info->luaState, "Metatable forbade persistence");
+ lua_error(info->luaState);
+
+ return false; /* not reached */
+ }
+ } else if (!lua_isfunction(info->luaState, -1)) {
+ lua_pushstring(info->luaState, "__persist not nil, boolean, or function");
+ lua_error(info->luaState);
+ }
+
+ // >>>>> permTbl indexTbl ...... obj metaTbl __persist
+ lua_pushvalue(info->luaState, -3);
+ // >>>>> permTbl indexTbl ...... obj metaTbl __persist obj
+
+ // >>>>> permTbl indexTbl ...... obj metaTbl ?func?
+
+ if (!lua_isfunction(info->luaState, -1)) {
+ lua_pushstring(info->luaState, "__persist function did not return a function");
+ lua_error(info->luaState);
+ }
+
+ // >>>>> permTbl indexTbl ...... obj metaTbl func
+
+ // Write out a flag that the function exists
+ info->writeStream->writeSint32LE(1);
+
+ // Serialize the function
+ serializeObject(info);
+
+ lua_pop(info->luaState, 2);
+ // >>>>> permTbl indexTbl ...... obj
+
+ return true;
+}
+
+static void serializeTable(SerializationInfo *info) {
+ // >>>>> permTbl indexTbl ...... tbl
+
+ // Make sure there is enough room on the stack
+ lua_checkstack(info->luaState, 3);
+
+ // Test if the object needs special serialization
+ if (serializeSpecialObject(info, 1)) {
+ return;
+ }
+
+ // >>>>> permTbl indexTbl ...... tbl
+
+ // First, serialize the metatable (if any)
+ if (!lua_getmetatable(info->luaState, -1)) {
+ lua_pushnil(info->luaState);
+ }
+
+ // >>>>> permTbl indexTbl ...... tbl metaTbl/nil */
+ serializeObject(info);
+
+ lua_pop(info->luaState, 1);
+ // >>>>> permTbl indexTbl ...... tbl
+
+
+ lua_pushnil(info->luaState);
+ // >>>>> permTbl indexTbl ...... tbl nil
+
+ // Now, persist all k/v pairs
+ while (lua_next(info->luaState, -2)) {
+ // >>>>> permTbl indexTbl ...... tbl k v */
+
+ lua_pushvalue(info->luaState, -2);
+ // >>>>> permTbl indexTbl ...... tbl k v k */
+
+ // Serialize the key
+ serializeObject(info);
+
+ lua_pop(info->luaState, 1);
+ // >>>>> permTbl indexTbl ...... tbl k v */
+
+ // Serialize the value
+ serializeObject(info);
+
+ lua_pop(info->luaState, 1);
+ // >>>>> permTbl indexTbl ...... tbl k */
+ }
+
+ // >>>>> permTbl indexTbl ...... tbl
+
+ // Terminate the list with a nil
+ lua_pushnil(info->luaState);
+ // >>>>> permTbl indexTbl ...... tbl
+
+ serializeObject(info);
+
+ lua_pop(info->luaState, 1);
+ // >>>>> permTbl indexTbl ...... tbl
+}
+
+static void pushObject(lua_State *luaState, TValue *obj) {
+ setobj2s(luaState, luaState->top, obj);
+
+ api_check(luaState, luaState->top < luaState->ci->top);
+ luaState->top++;
+}
+
+static void pushProto(lua_State *luaState, Proto *proto) {
+ TValue obj;
+ setptvalue(luaState, &obj, proto);
+
+ pushObject(luaState, &obj);
+}
+
+static void pushUpVal(lua_State *luaState, UpVal *upval) {
+ TValue obj;
+
+ obj.value.gc = cast(GCObject *, upval);
+ obj.tt = LUA_TUPVAL;
+ checkliveness(G(L), obj);
+
+ pushObject(luaState, &obj);
+}
+
+static void pushString(lua_State *luaState, TString *str) {
+ TValue o;
+ setsvalue(luaState, &o, str);
+
+ pushObject(luaState, &o);
+}
+
+static void serializeFunction(SerializationInfo *info) {
+ // >>>>> permTbl indexTbl ...... func
+ Closure *cl = clvalue(getobject(info->luaState, -1));
+ lua_checkstack(info->luaState, 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(info->luaState, "Attempt to persist a C function");
+ lua_error(info->luaState);
+ } else {
+ // It's a Lua closure
+
+ // We don't really _NEED_ the number of upvals, but it'll simplify things a bit
+ info->writeStream->writeByte(cl->l.p->nups);
+
+ // Serialize the prototype
+ pushProto(info->luaState, cl->l.p);
+ // >>>>> permTbl indexTbl ...... func proto */
+
+ serializeObject(info);
+
+ lua_pop(info->luaState, 1);
+ // >>>>> permTbl indexTbl ...... func
+
+ // Serialize upvalue values (not the upvalue objects themselves)
+ for (byte i=0; i<cl->l.p->nups; i++) {
+ // >>>>> permTbl indexTbl ...... func
+ pushUpVal(info->luaState, cl->l.upvals[i]);
+ // >>>>> permTbl indexTbl ...... func upval
+
+ serializeObject(info);
+
+ lua_pop(info->luaState, 1);
+ // >>>>> permTbl indexTbl ...... func
+ }
+
+ // >>>>> permTbl indexTbl ...... func
+
+ // Serialize function environment
+ lua_getfenv(info->luaState, -1);
+ // >>>>> permTbl indexTbl ...... func fenv
+
+ if (lua_equal(info->luaState, -1, LUA_GLOBALSINDEX)) {
+ // Function has the default fenv
+
+ // >>>>> permTbl indexTbl ...... func _G
+ lua_pop(info->luaState, 1);
+ // >>>>> permTbl indexTbl ...... func
+
+ lua_pushnil(info->luaState);
+ // >>>>> permTbl indexTbl ...... func nil
+ }
+
+ // >>>>> permTbl indexTbl ...... func fenv/nil
+ serializeObject(info);
+
+ lua_pop(info->luaState, 1);
+ // >>>>> permTbl indexTbl ...... func
+ }
+}
+
+/* Appends one stack to another stack, but the stack is reversed in the process */
+static size_t appendStackToStack_rev(lua_State *from, lua_State *to) {
+ for (StkId id = from->top - 1; id >= from->stack; --id) {
+ setobj2s(to, to->top, id);
+ to->top++;
+ }
+
+ return from->top - from->stack;
+}
+
+static void serializeThread(SerializationInfo *info) {
+ // >>>>> permTbl indexTbl ...... thread
+ lua_State *threadState = lua_tothread(info->luaState, -1);
+
+ // Make sure there is enough room on the stack
+ lua_checkstack(info->luaState, threadState->top - threadState->stack + 1);
+
+ if (info->luaState == threadState) {
+ lua_pushstring(info->luaState, "Can't persist currently running thread");
+ lua_error(info->luaState);
+ return; /* not reached */
+ }
+
+ // Persist the stack
+
+ // We *could* have truncation here, but if we have more than 4 billion items on a stack, we have bigger problems
+ uint stackSize = static_cast<uint32>(appendStackToStack_rev(threadState, info->luaState));
+ info->writeStream->writeUint32LE(stackSize);
+
+ // >>>>> permTbl indexTbl ...... thread (reversed contents of thread stack) */
+ for (; stackSize > 0; --stackSize) {
+ serializeObject(info);
+
+ lua_pop(info->luaState, 1);
+ }
+
+ // >>>>> permTbl indexTbl ...... thread
+
+ // Now, serialize the CallInfo stack
+
+ // Again, we *could* have truncation here, but if we have more than 4 billion items on a stack, we have bigger problems
+ uint32 numFrames = static_cast<uint32>((threadState->ci - threadState->base_ci) + 1);
+ info->writeStream->writeUint32LE(numFrames);
+
+ for (uint32 i = 0; i < numFrames; i++) {
+ CallInfo *ci = threadState->base_ci + i;
+
+ // Same argument as above about truncation
+ uint32 stackBase = static_cast<uint32>(ci->base - threadState->stack);
+ uint32 stackFunc = static_cast<uint32>(ci->func - threadState->stack);
+ uint32 stackTop = static_cast<uint32>(ci->top - threadState->stack);
+
+ info->writeStream->writeUint32LE(stackBase);
+ info->writeStream->writeUint32LE(stackFunc);
+ info->writeStream->writeUint32LE(stackTop);
+
+ info->writeStream->writeSint32LE(ci->nresults);
+
+ uint32 savedpc = (ci != threadState->base_ci) ? static_cast<uint32>(ci->savedpc - ci_func(ci)->l.p->code) : 0u;
+ info->writeStream->writeUint32LE(savedpc);
+ }
+
+
+ // Serialize the state's other parameters, with the exception of upval stuff
+
+ assert(threadState->nCcalls <= 1);
+ info->writeStream->writeByte(threadState->status);
+
+ // Same argument as above about truncation
+ uint32 stackBase = static_cast<uint32>(threadState->base - threadState->stack);
+ uint32 stackFunc = static_cast<uint32>(threadState->top - threadState->stack);
+ info->writeStream->writeUint32LE(stackBase);
+ info->writeStream->writeUint32LE(stackFunc);
+
+ // Same argument as above about truncation
+ uint32 stackOffset = static_cast<uint32>(threadState->errfunc);
+ info->writeStream->writeUint32LE(stackOffset);
+
+ // Finally, record upvalues which need to be reopened
+ // See the comment above serializeUpVal() for why we do this
+
+ UpVal *upVal;
+
+ // >>>>> permTbl indexTbl ...... thread
+ for (GCObject *gcObject = threadState->openupval; gcObject != NULL; gcObject = upVal->next) {
+ upVal = gco2uv(gcObject);
+
+ /* Make sure upvalue is really open */
+ assert(upVal->v != &upVal->u.value);
+
+ pushUpVal(info->luaState, upVal);
+ // >>>>> permTbl indexTbl ...... thread upVal
+
+ serializeObject(info);
+
+ lua_pop(info->luaState, 1);
+ // >>>>> permTbl indexTbl ...... thread
+
+ // Same argument as above about truncation
+ uint32 stackpos = static_cast<uint32>(upVal->v - threadState->stack);
+ info->writeStream->writeUint32LE(stackpos);
+ }
+
+ // >>>>> permTbl indexTbl ...... thread
+ lua_pushnil(info->luaState);
+ // >>>>> permTbl indexTbl ...... thread nil
+
+ // Use nil as a terminator
+ serializeObject(info);
+
+ lua_pop(info->luaState, 1);
+ // >>>>> permTbl indexTbl ...... thread
+}
+
+static void serializeProto(SerializationInfo *info) {
+ // >>>>> permTbl indexTbl ...... proto
+ Proto *proto = gco2p(getobject(info->luaState, -1)->value.gc);
+
+ // Make sure there is enough room on the stack
+ lua_checkstack(info->luaState, 2);
+
+ // Serialize constant refs */
+ info->writeStream->writeSint32LE(proto->sizek);
+
+ for (int i = 0; i < proto->sizek; ++i) {
+ pushObject(info->luaState, &proto->k[i]);
+ // >>>>> permTbl indexTbl ...... proto const
+
+ serializeObject(info);
+
+ lua_pop(info->luaState, 1);
+ // >>>>> permTbl indexTbl ...... proto
+ }
+
+ // >>>>> permTbl indexTbl ...... proto
+
+ // Serialize inner Proto refs
+ info->writeStream->writeSint32LE(proto->sizep);
+
+ for (int i = 0; i < proto->sizep; ++i)
+ {
+ pushProto(info->luaState, proto->p[i]);
+ // >>>>> permTbl indexTbl ...... proto subProto */
+
+ serializeObject(info);
+
+ lua_pop(info->luaState, 1);
+ // >>>>> permTbl indexTbl ...... proto
+ }
+
+ // >>>>> permTbl indexTbl ...... proto
+
+ // Serialize the code
+ info->writeStream->writeSint32LE(proto->sizecode);
+
+ uint32 len = static_cast<uint32>(sizeof(Instruction) * proto->sizecode);
+ info->writeStream->write(proto->code, len);
+
+
+ // Serialize upvalue names
+ info->writeStream->writeSint32LE(proto->sizeupvalues);
+
+ for (int i = 0; i < proto->sizeupvalues; ++i)
+ {
+ pushString(info->luaState, proto->upvalues[i]);
+ // >>>>> permTbl indexTbl ...... proto str
+
+ serializeObject(info);
+
+ lua_pop(info->luaState, 1);
+ // >>>>> permTbl indexTbl ...... proto
+ }
+
+
+ // Serialize local variable infos
+ info->writeStream->writeSint32LE(proto->sizelocvars);
+
+ for (int i = 0; i < proto->sizelocvars; ++i) {
+ pushString(info->luaState, proto->locvars[i].varname);
+ // >>>>> permTbl indexTbl ...... proto str
+
+ serializeObject(info);
+
+ lua_pop(info->luaState, 1);
+ // >>>>> permTbl indexTbl ...... proto
+
+ info->writeStream->writeSint32LE(proto->locvars[i].startpc);
+ info->writeStream->writeSint32LE(proto->locvars[i].endpc);
+ }
+
+
+ // Serialize source string
+ pushString(info->luaState, proto->source);
+ // >>>>> permTbl indexTbl ...... proto sourceStr
+
+ serializeObject(info);
+
+ lua_pop(info->luaState, 1);
+ // >>>>> permTbl indexTbl ...... proto
+
+ // Serialize line numbers
+ info->writeStream->writeSint32LE(proto->sizelineinfo);
+
+ if (proto->sizelineinfo) {
+ uint32 len = static_cast<uint32>(sizeof(int) * proto->sizelineinfo);
+ info->writeStream->write(proto->lineinfo, len);
+ }
+
+ // Serialize linedefined and lastlinedefined
+ info->writeStream->writeSint32LE(proto->linedefined);
+ info->writeStream->writeSint32LE(proto->lastlinedefined);
+
+
+ // Serialize misc values
+ info->writeStream->writeByte(proto->nups);
+ info->writeStream->writeByte(proto->numparams);
+ info->writeStream->writeByte(proto->is_vararg);
+ info->writeStream->writeByte(proto->maxstacksize);
+}
+
+/* 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 serializing, don't indicate whether an upvalue is
+ * closed or not.
+ * (b) When unserializing, pretend that all upvalues are closed.
+ * (c) When serializing, persist all open upvalues referenced by a thread
+ * that is persisted, and tag each one with the corresponding stack position
+ * (d) When unserializing, "reopen" each of these upvalues as the thread is
+ * unserialized
+ */
+static void serializeUpValue(SerializationInfo *info) {
+ // >>>>> permTbl indexTbl ...... upval
+ assert(ttype(getobject(info->luaState, -1)) == LUA_TUPVAL);
+ UpVal *upValue = gco2uv(getobject(info->luaState, -1)->value.gc);
+
+ // Make sure there is enough room on the stack
+ lua_checkstack(info->luaState, 1);
+
+ // We can't permit the upValue to linger around on the stack, as Lua
+ // will bail if its GC finds it.
+
+ lua_pop(info->luaState, 1);
+ // >>>>> permTbl indexTbl ......
+
+ pushObject(info->luaState, upValue->v);
+ // >>>>> permTbl indexTbl ...... obj
+
+ serializeObject(info);
+ // >>>>> permTbl indexTbl ...... obj
+}
+
+static void serializeUserData(SerializationInfo *info) {
+ // >>>>> permTbl rootObj ...... udata
+
+ // Make sure there is enough room on the stack
+ lua_checkstack(info->luaState, 2);
+
+ // Test if the object needs special serialization
+ if (serializeSpecialObject(info, 0)) {
+ return;
+ }
+
+ // Use literal persistence
+
+ // Hard cast to a uint32 length
+ // This could lead to truncation, but if we have a 4gb block of data, we have bigger problems
+ uint32 length = static_cast<uint32>(uvalue(getobject(info->luaState, -1))->len);
+ info->writeStream->writeUint32LE(length);
+
+ info->writeStream->write(lua_touserdata(info->luaState, -1), length);
+
+ // Serialize the metatable (if any)
+ if (!lua_getmetatable(info->luaState, -1)) {
+ lua_pushnil(info->luaState);
+ }
+
+ // >>>>> permTbl rootObj ...... udata metaTbl/nil
+ serializeObject(info);
+
+ lua_pop(info->luaState, 1);
+ /* perms reftbl ... udata */
+}
+
+
+} // End of namespace Lua