From 97c35714ce3986b99848a780f6b195a63f8910b7 Mon Sep 17 00:00:00 2001 From: RichieSams Date: Tue, 30 Dec 2014 15:19:29 -0600 Subject: SWORD25: Rename lua serialization functions to use 'persist' in order to match the rest of the engine --- engines/sword25/module.mk | 6 +- engines/sword25/script/luascript.cpp | 6 +- engines/sword25/util/lua_persist.cpp | 787 ++++++++++++++++++++++++ engines/sword25/util/lua_persistence.h | 44 ++ engines/sword25/util/lua_persistence_util.cpp | 346 +++++++++++ engines/sword25/util/lua_persistence_util.h | 98 +++ engines/sword25/util/lua_serialization.h | 44 -- engines/sword25/util/lua_serialization_util.cpp | 346 ----------- engines/sword25/util/lua_serialization_util.h | 98 --- engines/sword25/util/lua_serializer.cpp | 787 ------------------------ engines/sword25/util/lua_unpersist.cpp | 698 +++++++++++++++++++++ engines/sword25/util/lua_unserializer.cpp | 698 --------------------- 12 files changed, 1979 insertions(+), 1979 deletions(-) create mode 100644 engines/sword25/util/lua_persist.cpp create mode 100644 engines/sword25/util/lua_persistence.h create mode 100644 engines/sword25/util/lua_persistence_util.cpp create mode 100644 engines/sword25/util/lua_persistence_util.h delete mode 100644 engines/sword25/util/lua_serialization.h delete mode 100644 engines/sword25/util/lua_serialization_util.cpp delete mode 100644 engines/sword25/util/lua_serialization_util.h delete mode 100644 engines/sword25/util/lua_serializer.cpp create mode 100644 engines/sword25/util/lua_unpersist.cpp delete mode 100644 engines/sword25/util/lua_unserializer.cpp (limited to 'engines/sword25') diff --git a/engines/sword25/module.mk b/engines/sword25/module.mk index 0135f53783..129b4f040e 100644 --- a/engines/sword25/module.mk +++ b/engines/sword25/module.mk @@ -83,9 +83,9 @@ MODULE_OBJS := \ util/lua/lzio.o \ util/lua/scummvm_file.o \ util/double_serializer.o \ - util/lua_serialization_util.o \ - util/lua_serializer.o \ - util/lua_unserializer.o + util/lua_persistence_util.o \ + util/lua_persist.o \ + util/lua_unpersist.o # This module can be built as a plugin ifeq ($(ENABLE_SWORD25), DYNAMIC_PLUGIN) diff --git a/engines/sword25/script/luascript.cpp b/engines/sword25/script/luascript.cpp index 6cf287c643..e93289596b 100644 --- a/engines/sword25/script/luascript.cpp +++ b/engines/sword25/script/luascript.cpp @@ -43,7 +43,7 @@ #include "sword25/util/lua/lua.h" #include "sword25/util/lua/lualib.h" #include "sword25/util/lua/lauxlib.h" -#include "sword25/util/lua_serialization.h" +#include "sword25/util/lua_persistence.h" namespace Sword25 { @@ -396,7 +396,7 @@ bool LuaScriptEngine::persist(OutputPersistenceBlock &writer) { // Lua persists and stores the data in a WriteStream Common::MemoryWriteStreamDynamic writeStream; - Lua::serializeLua(_state, &writeStream); + Lua::persistLua(_state, &writeStream); // Persistenzdaten in den Writer schreiben. writer.write(writeStream.getData(), writeStream.size()); @@ -482,7 +482,7 @@ bool LuaScriptEngine::unpersist(InputPersistenceBlock &reader) { reader.readByteArray(chunkData); Common::MemoryReadStream readStream(&chunkData[0], chunkData.size(), DisposeAfterUse::NO); - Lua::unserializeLua(_state, &readStream); + Lua::unpersistLua(_state, &readStream); // Permanents-Table is removed from stack lua_remove(_state, -2); diff --git a/engines/sword25/util/lua_persist.cpp b/engines/sword25/util/lua_persist.cpp new file mode 100644 index 0000000000..b9c0b13e11 --- /dev/null +++ b/engines/sword25/util/lua_persist.cpp @@ -0,0 +1,787 @@ +/* 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_persistence.h" + +#include "sword25/util/double_serializer.h" +#include "sword25/util/lua_persistence_util.h" + +#include "common/stream.h" + +#include "lua/lobject.h" +#include "lua/lstate.h" +#include "lua/lgc.h" + + +namespace Lua { + +#define PERMANENT_TYPE 101 + +struct SerializationInfo { + lua_State *luaState; + Common::WriteStream *writeStream; + uint counter; +}; + +static void serialize(SerializationInfo *info); + +static void serializeBoolean(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 persistLua(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 + serialize(&info); + + // Return the stack back to the original state + lua_remove(luaState, 2); + // >>>>> permTbl rootObj +} + +static void serialize(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 + serialize(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(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 object isn't special and should be persisted normally + info->writeStream->writeSint32LE(0); + + return false; + } else { + lua_pushstring(info->luaState, "Type not literally persistable by default"); + lua_error(info->luaState); + + return false; // Not reached + } + } + + // >>>>> 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 that the object isn't special and should be persisted normally + info->writeStream->writeSint32LE(0); + + return false; + } else { + lua_pushstring(info->luaState, "Type not literally persistable by default"); + lua_error(info->luaState); + + return false; // Return false + } + + } else if (lua_isboolean(info->luaState, -1)) { + // >>>>> permTbl indexTbl ...... obj metaTbl bool + if (lua_toboolean(info->luaState, -1)) { + // Write out a flag declaring that the object isn't special and should be persisted normally + 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 + serialize(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 */ + serialize(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 + serialize(info); + + lua_pop(info->luaState, 1); + // >>>>> permTbl indexTbl ...... tbl k v */ + + // Serialize the value + serialize(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 + + serialize(info); + + lua_pop(info->luaState, 1); + // >>>>> permTbl indexTbl ...... tbl +} + +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 */ + + serialize(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 + pushUpValue(info->luaState, cl->l.upvals[i]); + // >>>>> permTbl indexTbl ...... func upval + + serialize(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 + serialize(info); + + lua_pop(info->luaState, 1); + // >>>>> permTbl indexTbl ...... func + } +} + +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 + uint32 stackSize = static_cast(appendStackToStack_reverse(threadState, info->luaState)); + info->writeStream->writeUint32LE(stackSize); + + // >>>>> permTbl indexTbl ...... thread (reversed contents of thread stack) */ + for (; stackSize > 0; --stackSize) { + serialize(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((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(ci->base - threadState->stack); + uint32 stackFunc = static_cast(ci->func - threadState->stack); + uint32 stackTop = static_cast(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(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(threadState->base - threadState->stack); + uint32 stackFunc = static_cast(threadState->top - threadState->stack); + info->writeStream->writeUint32LE(stackBase); + info->writeStream->writeUint32LE(stackFunc); + + // Same argument as above about truncation + uint32 stackOffset = static_cast(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); + + pushUpValue(info->luaState, upVal); + // >>>>> permTbl indexTbl ...... thread upVal + + serialize(info); + + lua_pop(info->luaState, 1); + // >>>>> permTbl indexTbl ...... thread + + // Same argument as above about truncation + uint32 stackpos = static_cast(upVal->v - threadState->stack); + info->writeStream->writeUint32LE(stackpos); + } + + // >>>>> permTbl indexTbl ...... thread + lua_pushnil(info->luaState); + // >>>>> permTbl indexTbl ...... thread nil + + // Use nil as a terminator + serialize(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 + + serialize(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 */ + + serialize(info); + + lua_pop(info->luaState, 1); + // >>>>> permTbl indexTbl ...... proto + } + + // >>>>> permTbl indexTbl ...... proto + + // Serialize the code + info->writeStream->writeSint32LE(proto->sizecode); + + uint32 len = static_cast(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 + + serialize(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 + + serialize(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 + + serialize(info); + + lua_pop(info->luaState, 1); + // >>>>> permTbl indexTbl ...... proto + + // Serialize line numbers + info->writeStream->writeSint32LE(proto->sizelineinfo); + + if (proto->sizelineinfo) { + uint32 len = static_cast(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 + + serialize(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(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 + serialize(info); + + lua_pop(info->luaState, 1); + /* perms reftbl ... udata */ +} + + +} // End of namespace Lua diff --git a/engines/sword25/util/lua_persistence.h b/engines/sword25/util/lua_persistence.h new file mode 100644 index 0000000000..cf7d7e03ca --- /dev/null +++ b/engines/sword25/util/lua_persistence.h @@ -0,0 +1,44 @@ +/* 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; +class ReadStream; +} + + +namespace Lua { + +#define PERMANENT_TYPE 101 + +void persistLua(lua_State *luaState, Common::WriteStream *writeStream); +void unpersistLua(lua_State *luaState, Common::ReadStream *readStream); + +} // End of namespace Lua + +#endif diff --git a/engines/sword25/util/lua_persistence_util.cpp b/engines/sword25/util/lua_persistence_util.cpp new file mode 100644 index 0000000000..8365f5d483 --- /dev/null +++ b/engines/sword25/util/lua_persistence_util.cpp @@ -0,0 +1,346 @@ +/* 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 distri8buted 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_persistence_util.h" + +#include "common/scummsys.h" + +#include "lua/lobject.h" +#include "lua/lstate.h" +#include "lua/lgc.h" +#include "lua/lopcodes.h" + + +namespace Lua { + +void *lua_realloc(lua_State *luaState, void *block, size_t osize, size_t nsize) { + global_State *globalState = G(luaState); + + block = (*globalState->frealloc)(globalState->ud, block, osize, nsize); + globalState->totalbytes = (globalState->totalbytes - osize) + nsize; + + return block; +} + +void pushObject(lua_State *luaState, TValue *obj) { + setobj2s(luaState, luaState->top, obj); + + api_check(luaState, luaState->top < luaState->ci->top); + luaState->top++; +} + +void pushProto(lua_State *luaState, Proto *proto) { + TValue obj; + setptvalue(luaState, &obj, proto); + + pushObject(luaState, &obj); +} + +void pushUpValue(lua_State *luaState, UpVal *upval) { + TValue obj; + + obj.value.gc = cast(GCObject *, upval); + obj.tt = LUA_TUPVAL; + checkliveness(G(L), obj); + + pushObject(luaState, &obj); +} + +void pushString(lua_State *luaState, TString *str) { + TValue o; + setsvalue(luaState, &o, str); + + pushObject(luaState, &o); +} + +/* A simple reimplementation of the unfortunately static function luaA_index. + * Does not support the global table, registry, or upvalues. */ +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; + } +} + +void lua_linkObjToGC(lua_State *luaState, GCObject *obj, lu_byte type) { + global_State *globalState = G(luaState); + + obj->gch.next = globalState->rootgc; + globalState->rootgc = obj; + obj->gch.marked = luaC_white(globalState); + obj->gch.tt = type; +} + +Closure *lua_newLclosure(lua_State *luaState, int numElements, Table *elementTable) { + Closure *c = (Closure *)lua_malloc(luaState, sizeLclosure(numElements)); + lua_linkObjToGC(luaState, obj2gco(c), LUA_TFUNCTION); + + c->l.isC = 0; + c->l.env = elementTable; + c->l.nupvalues = cast_byte(numElements); + + while (numElements--) { + c->l.upvals[numElements] = NULL; + } + + return c; +} + +void pushClosure(lua_State *luaState, Closure *closure) { + TValue obj; + setclvalue(luaState, &obj, closure); + pushObject(luaState, &obj); +} + +Proto *createProto(lua_State *luaState) { + Proto *newProto = (Proto *)lua_malloc(luaState, sizeof(Proto)); + lua_linkObjToGC(luaState, obj2gco(newProto), LUA_TPROTO); + + newProto->k = NULL; + newProto->sizek = 0; + newProto->p = NULL; + newProto->sizep = 0; + newProto->code = NULL; + newProto->sizecode = 0; + newProto->sizelineinfo = 0; + newProto->sizeupvalues = 0; + newProto->nups = 0; + newProto->upvalues = NULL; + newProto->numparams = 0; + newProto->is_vararg = 0; + newProto->maxstacksize = 0; + newProto->lineinfo = NULL; + newProto->sizelocvars = 0; + newProto->locvars = NULL; + newProto->linedefined = 0; + newProto->lastlinedefined = 0; + newProto->source = NULL; + + return newProto; +} + +TString *createString(lua_State *luaState, const char *str, size_t len) { + TString *res; + lua_pushlstring(luaState, str, len); + + res = rawtsvalue(luaState->top - 1); + lua_pop(luaState, 1); + + return res; +} + +Proto *makeFakeProto(lua_State *L, lu_byte nups) { + Proto *p = createProto(L); + + p->sizelineinfo = 1; + p->lineinfo = lua_newVector(L, 1, int); + p->lineinfo[0] = 1; + p->sizecode = 1; + p->code = lua_newVector(L, 1, Instruction); + p->code[0] = CREATE_ABC(OP_RETURN, 0, 1, 0); + p->source = createString(L, "", 0); + p->maxstacksize = 2; + p->nups = nups; + p->sizek = 0; + p->sizep = 0; + + return p; +} + +UpVal *createUpValue(lua_State *luaState, int stackpos) { + UpVal *upValue = (UpVal *)lua_malloc(luaState, sizeof(UpVal)); + lua_linkObjToGC(luaState, (GCObject *)upValue, LUA_TUPVAL); + upValue->tt = LUA_TUPVAL; + upValue->v = &upValue->u.value; + upValue->u.l.prev = NULL; + upValue->u.l.next = NULL; + + const TValue *o2 = (TValue *)getObject(luaState, stackpos); + upValue->v->value = o2->value; + upValue->v->tt = o2->tt; + checkliveness(G(L), upValue->v); + + return upValue; +} + +void unboxUpValue(lua_State *luaState) { + // >>>>> ...... func + LClosure *lcl; + UpVal *uv; + + lcl = (LClosure *)clvalue(getObject(luaState, -1)); + uv = lcl->upvals[0]; + + lua_pop(luaState, 1); + // >>>>> ...... + + pushUpValue(luaState, uv); + // >>>>> ...... upValue +} + +size_t appendStackToStack_reverse(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; +} + +void correctStack(lua_State *L, TValue *oldstack) { + CallInfo *ci; + GCObject *up; + L->top = (L->top - oldstack) + L->stack; + for (up = L->openupval; up != NULL; up = up->gch.next) + gco2uv(up)->v = (gco2uv(up)->v - oldstack) + L->stack; + for (ci = L->base_ci; ci <= L->ci; ci++) { + ci->top = (ci->top - oldstack) + L->stack; + ci->base = (ci->base - oldstack) + L->stack; + ci->func = (ci->func - oldstack) + L->stack; + } + L->base = (L->base - oldstack) + L->stack; +} + +void lua_reallocstack(lua_State *L, int newsize) { + TValue *oldstack = L->stack; + int realsize = newsize + 1 + EXTRA_STACK; + + lua_reallocvector(L, L->stack, L->stacksize, realsize, TValue); + L->stacksize = realsize; + L->stack_last = L->stack + newsize; + correctStack(L, oldstack); +} + +void lua_growstack(lua_State *L, int n) { + // Double size is enough? + if (n <= L->stacksize) { + lua_reallocstack(L, 2 * L->stacksize); + } else { + lua_reallocstack(L, L->stacksize + n); + } +} + +void lua_reallocCallInfo(lua_State *lauState, int newsize) { + CallInfo *oldci = lauState->base_ci; + lua_reallocvector(lauState, lauState->base_ci, lauState->size_ci, newsize, CallInfo); + + lauState->size_ci = newsize; + lauState->ci = (lauState->ci - oldci) + lauState->base_ci; + lauState->end_ci = lauState->base_ci + lauState->size_ci - 1; +} + +void GCUnlink(lua_State *luaState, GCObject *gco) { + GCObject *prevslot; + if (G(luaState)->rootgc == gco) { + G(luaState)->rootgc = G(luaState)->rootgc->gch.next; + return; + } + + prevslot = G(luaState)->rootgc; + while (prevslot->gch.next != gco) { + prevslot = prevslot->gch.next; + } + + prevslot->gch.next = prevslot->gch.next->gch.next; +} + +TString *lua_newlstr(lua_State *luaState, const char *str, size_t len) { + lua_pushlstring(luaState, str, len); + TString *luaStr = &(luaState->top - 1)->value.gc->ts; + + lua_pop(luaState, 1); + + return luaStr; +} + +void lua_link(lua_State *luaState, GCObject *o, lu_byte tt) { + global_State *g = G(luaState); + o->gch.next = g->rootgc; + g->rootgc = o; + o->gch.marked = luaC_white(g); + o->gch.tt = tt; +} + +Proto *lua_newproto(lua_State *luaState) { + Proto *f = (Proto *)lua_malloc(luaState, sizeof(Proto)); + lua_link(luaState, obj2gco(f), LUA_TPROTO); + f->k = NULL; + f->sizek = 0; + f->p = NULL; + f->sizep = 0; + f->code = NULL; + f->sizecode = 0; + f->sizelineinfo = 0; + f->sizeupvalues = 0; + f->nups = 0; + f->upvalues = NULL; + f->numparams = 0; + f->is_vararg = 0; + f->maxstacksize = 0; + f->lineinfo = NULL; + f->sizelocvars = 0; + f->locvars = NULL; + f->linedefined = 0; + f->lastlinedefined = 0; + f->source = NULL; + return f; +} + +UpVal *makeUpValue(lua_State *luaState, int stackPos) { + UpVal *uv = lua_new(luaState, UpVal); + lua_link(luaState, (GCObject *)uv, LUA_TUPVAL); + uv->tt = LUA_TUPVAL; + uv->v = &uv->u.value; + uv->u.l.prev = NULL; + uv->u.l.next = NULL; + + setobj(luaState, uv->v, getObject(luaState, stackPos)); + + return uv; +} + +void boxUpValue_start(lua_State *luaState) { + LClosure *closure; + closure = (LClosure *)lua_newLclosure(luaState, 1, hvalue(&luaState->l_gt)); + pushClosure(luaState, (Closure *)closure); + // >>>>> ...... func + closure->p = makeFakeProto(luaState, 1); + + // Temporarily initialize the upvalue to nil + lua_pushnil(luaState); + closure->upvals[0] = makeUpValue(luaState, -1); + lua_pop(luaState, 1); +} + +void boxUpValue_finish(lua_State *luaState) { + // >>>>> ...... func obj + LClosure *lcl = (LClosure *)clvalue(getObject(luaState, -2)); + + lcl->upvals[0]->u.value = *getObject(luaState, -1); + lua_pop(luaState, 1); + // >>>>> ...... func +} + +} // End of namespace Lua diff --git a/engines/sword25/util/lua_persistence_util.h b/engines/sword25/util/lua_persistence_util.h new file mode 100644 index 0000000000..345996f606 --- /dev/null +++ b/engines/sword25/util/lua_persistence_util.h @@ -0,0 +1,98 @@ +/* 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 distri8buted 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_UTIL_H +#define LUA_SERIALIZATION_UTIL_H + + +struct lua_State; + +#include "lua/lobject.h" + +typedef TValue *StkId; + +namespace Lua { + +#define lua_malloc(luaState, nsize) lua_realloc(luaState, nullptr, 0, nsize) +#define lua_reallocv(luaState, block, on, n, e) lua_realloc(luaState, block, (on) * (e), (n) * (e)) +#define lua_reallocvector(luaState, vec, oldn, n, T) ((vec) = (T *)(lua_reallocv(luaState, vec, oldn, n, sizeof(T)))) +#define lua_newVector(luaState, num, T) ((T *)lua_reallocv(luaState, nullptr, 0, num, sizeof(T))) +#define lua_new(luaState,T) (T *)lua_malloc(luaState, sizeof(T)) + +void *lua_realloc(lua_State *luaState, void *block, size_t osize, size_t nsize); + +void pushObject(lua_State *luaState, TValue *obj); +void pushProto(lua_State *luaState, Proto *proto); +void pushUpValue(lua_State *luaState, UpVal *upval); +void pushString(lua_State *luaState, TString *str); + +StkId getObject(lua_State *luaState, int stackpos); + +void lua_linkObjToGC(lua_State *luaState, GCObject *obj, lu_byte type); + +#define sizeLclosure(n) ((sizeof(LClosure)) + sizeof(TValue *) * ((n) - 1)) + +Closure *lua_newLclosure(lua_State *luaState, int numElements, Table *elementTable); +void pushClosure(lua_State *luaState, Closure *closure); + +Proto *createProto(lua_State *luaState); +Proto *makeFakeProto(lua_State *L, lu_byte nups); + +TString *createString(lua_State *luaState, const char *str, size_t len); + +UpVal *createUpValue(lua_State *luaState, int stackpos); +void unboxUpValue(lua_State *luaState); + +/* Appends one stack to another stack, but the stack is reversed in the process */ +size_t appendStackToStack_reverse(lua_State *from, lua_State *to); +void correctStack(lua_State *L, TValue *oldstack); +void lua_reallocstack(lua_State *L, int newsize); +void lua_growstack(lua_State *L, int n); + +void lua_reallocCallInfo(lua_State *lauState, int newsize); + +/* 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. */ +void GCUnlink(lua_State *luaState, GCObject *gco); + +TString *lua_newlstr(lua_State *luaState, const char *str, size_t len); +void lua_link(lua_State *luaState, GCObject *o, lu_byte tt); +Proto *lua_newproto(lua_State *luaState) ; + +UpVal *makeUpValue(lua_State *luaState, int stackPos); +/** + * 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. + */ +void boxUpValue_start(lua_State *luaState); +void boxUpValue_finish(lua_State *luaState); + +} // End of namespace Lua + +#endif diff --git a/engines/sword25/util/lua_serialization.h b/engines/sword25/util/lua_serialization.h deleted file mode 100644 index 549ea7968d..0000000000 --- a/engines/sword25/util/lua_serialization.h +++ /dev/null @@ -1,44 +0,0 @@ -/* 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; -class ReadStream; -} - - -namespace Lua { - -#define PERMANENT_TYPE 101 - -void serializeLua(lua_State *luaState, Common::WriteStream *writeStream); -void unserializeLua(lua_State *luaState, Common::ReadStream *readStream); - -} // End of namespace Lua - -#endif diff --git a/engines/sword25/util/lua_serialization_util.cpp b/engines/sword25/util/lua_serialization_util.cpp deleted file mode 100644 index fc3f73eca6..0000000000 --- a/engines/sword25/util/lua_serialization_util.cpp +++ /dev/null @@ -1,346 +0,0 @@ -/* 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 distri8buted 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_util.h" - -#include "common/scummsys.h" - -#include "lua/lobject.h" -#include "lua/lstate.h" -#include "lua/lgc.h" -#include "lua/lopcodes.h" - - -namespace Lua { - -void *lua_realloc(lua_State *luaState, void *block, size_t osize, size_t nsize) { - global_State *globalState = G(luaState); - - block = (*globalState->frealloc)(globalState->ud, block, osize, nsize); - globalState->totalbytes = (globalState->totalbytes - osize) + nsize; - - return block; -} - -void pushObject(lua_State *luaState, TValue *obj) { - setobj2s(luaState, luaState->top, obj); - - api_check(luaState, luaState->top < luaState->ci->top); - luaState->top++; -} - -void pushProto(lua_State *luaState, Proto *proto) { - TValue obj; - setptvalue(luaState, &obj, proto); - - pushObject(luaState, &obj); -} - -void pushUpValue(lua_State *luaState, UpVal *upval) { - TValue obj; - - obj.value.gc = cast(GCObject *, upval); - obj.tt = LUA_TUPVAL; - checkliveness(G(L), obj); - - pushObject(luaState, &obj); -} - -void pushString(lua_State *luaState, TString *str) { - TValue o; - setsvalue(luaState, &o, str); - - pushObject(luaState, &o); -} - -/* A simple reimplementation of the unfortunately static function luaA_index. - * Does not support the global table, registry, or upvalues. */ -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; - } -} - -void lua_linkObjToGC(lua_State *luaState, GCObject *obj, lu_byte type) { - global_State *globalState = G(luaState); - - obj->gch.next = globalState->rootgc; - globalState->rootgc = obj; - obj->gch.marked = luaC_white(globalState); - obj->gch.tt = type; -} - -Closure *lua_newLclosure(lua_State *luaState, int numElements, Table *elementTable) { - Closure *c = (Closure *)lua_malloc(luaState, sizeLclosure(numElements)); - lua_linkObjToGC(luaState, obj2gco(c), LUA_TFUNCTION); - - c->l.isC = 0; - c->l.env = elementTable; - c->l.nupvalues = cast_byte(numElements); - - while (numElements--) { - c->l.upvals[numElements] = NULL; - } - - return c; -} - -void pushClosure(lua_State *luaState, Closure *closure) { - TValue obj; - setclvalue(luaState, &obj, closure); - pushObject(luaState, &obj); -} - -Proto *createProto(lua_State *luaState) { - Proto *newProto = (Proto *)lua_malloc(luaState, sizeof(Proto)); - lua_linkObjToGC(luaState, obj2gco(newProto), LUA_TPROTO); - - newProto->k = NULL; - newProto->sizek = 0; - newProto->p = NULL; - newProto->sizep = 0; - newProto->code = NULL; - newProto->sizecode = 0; - newProto->sizelineinfo = 0; - newProto->sizeupvalues = 0; - newProto->nups = 0; - newProto->upvalues = NULL; - newProto->numparams = 0; - newProto->is_vararg = 0; - newProto->maxstacksize = 0; - newProto->lineinfo = NULL; - newProto->sizelocvars = 0; - newProto->locvars = NULL; - newProto->linedefined = 0; - newProto->lastlinedefined = 0; - newProto->source = NULL; - - return newProto; -} - -TString *createString(lua_State *luaState, const char *str, size_t len) { - TString *res; - lua_pushlstring(luaState, str, len); - - res = rawtsvalue(luaState->top - 1); - lua_pop(luaState, 1); - - return res; -} - -Proto *makeFakeProto(lua_State *L, lu_byte nups) { - Proto *p = createProto(L); - - p->sizelineinfo = 1; - p->lineinfo = lua_newVector(L, 1, int); - p->lineinfo[0] = 1; - p->sizecode = 1; - p->code = lua_newVector(L, 1, Instruction); - p->code[0] = CREATE_ABC(OP_RETURN, 0, 1, 0); - p->source = createString(L, "", 0); - p->maxstacksize = 2; - p->nups = nups; - p->sizek = 0; - p->sizep = 0; - - return p; -} - -UpVal *createUpValue(lua_State *luaState, int stackpos) { - UpVal *upValue = (UpVal *)lua_malloc(luaState, sizeof(UpVal)); - lua_linkObjToGC(luaState, (GCObject *)upValue, LUA_TUPVAL); - upValue->tt = LUA_TUPVAL; - upValue->v = &upValue->u.value; - upValue->u.l.prev = NULL; - upValue->u.l.next = NULL; - - const TValue *o2 = (TValue *)getObject(luaState, stackpos); - upValue->v->value = o2->value; - upValue->v->tt = o2->tt; - checkliveness(G(L), upValue->v); - - return upValue; -} - -void unboxUpValue(lua_State *luaState) { - // >>>>> ...... func - LClosure *lcl; - UpVal *uv; - - lcl = (LClosure *)clvalue(getObject(luaState, -1)); - uv = lcl->upvals[0]; - - lua_pop(luaState, 1); - // >>>>> ...... - - pushUpValue(luaState, uv); - // >>>>> ...... upValue -} - -size_t appendStackToStack_reverse(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; -} - -void correctStack(lua_State *L, TValue *oldstack) { - CallInfo *ci; - GCObject *up; - L->top = (L->top - oldstack) + L->stack; - for (up = L->openupval; up != NULL; up = up->gch.next) - gco2uv(up)->v = (gco2uv(up)->v - oldstack) + L->stack; - for (ci = L->base_ci; ci <= L->ci; ci++) { - ci->top = (ci->top - oldstack) + L->stack; - ci->base = (ci->base - oldstack) + L->stack; - ci->func = (ci->func - oldstack) + L->stack; - } - L->base = (L->base - oldstack) + L->stack; -} - -void lua_reallocstack(lua_State *L, int newsize) { - TValue *oldstack = L->stack; - int realsize = newsize + 1 + EXTRA_STACK; - - lua_reallocvector(L, L->stack, L->stacksize, realsize, TValue); - L->stacksize = realsize; - L->stack_last = L->stack + newsize; - correctStack(L, oldstack); -} - -void lua_growstack(lua_State *L, int n) { - // Double size is enough? - if (n <= L->stacksize) { - lua_reallocstack(L, 2 * L->stacksize); - } else { - lua_reallocstack(L, L->stacksize + n); - } -} - -void lua_reallocCallInfo(lua_State *lauState, int newsize) { - CallInfo *oldci = lauState->base_ci; - lua_reallocvector(lauState, lauState->base_ci, lauState->size_ci, newsize, CallInfo); - - lauState->size_ci = newsize; - lauState->ci = (lauState->ci - oldci) + lauState->base_ci; - lauState->end_ci = lauState->base_ci + lauState->size_ci - 1; -} - -void GCUnlink(lua_State *luaState, GCObject *gco) { - GCObject *prevslot; - if (G(luaState)->rootgc == gco) { - G(luaState)->rootgc = G(luaState)->rootgc->gch.next; - return; - } - - prevslot = G(luaState)->rootgc; - while (prevslot->gch.next != gco) { - prevslot = prevslot->gch.next; - } - - prevslot->gch.next = prevslot->gch.next->gch.next; -} - -TString *lua_newlstr(lua_State *luaState, const char *str, size_t len) { - lua_pushlstring(luaState, str, len); - TString *luaStr = &(luaState->top - 1)->value.gc->ts; - - lua_pop(luaState, 1); - - return luaStr; -} - -void lua_link(lua_State *luaState, GCObject *o, lu_byte tt) { - global_State *g = G(luaState); - o->gch.next = g->rootgc; - g->rootgc = o; - o->gch.marked = luaC_white(g); - o->gch.tt = tt; -} - -Proto *lua_newproto(lua_State *luaState) { - Proto *f = (Proto *)lua_malloc(luaState, sizeof(Proto)); - lua_link(luaState, obj2gco(f), LUA_TPROTO); - f->k = NULL; - f->sizek = 0; - f->p = NULL; - f->sizep = 0; - f->code = NULL; - f->sizecode = 0; - f->sizelineinfo = 0; - f->sizeupvalues = 0; - f->nups = 0; - f->upvalues = NULL; - f->numparams = 0; - f->is_vararg = 0; - f->maxstacksize = 0; - f->lineinfo = NULL; - f->sizelocvars = 0; - f->locvars = NULL; - f->linedefined = 0; - f->lastlinedefined = 0; - f->source = NULL; - return f; -} - -UpVal *makeUpValue(lua_State *luaState, int stackPos) { - UpVal *uv = lua_new(luaState, UpVal); - lua_link(luaState, (GCObject *)uv, LUA_TUPVAL); - uv->tt = LUA_TUPVAL; - uv->v = &uv->u.value; - uv->u.l.prev = NULL; - uv->u.l.next = NULL; - - setobj(luaState, uv->v, getObject(luaState, stackPos)); - - return uv; -} - -void boxUpValue_start(lua_State *luaState) { - LClosure *closure; - closure = (LClosure *)lua_newLclosure(luaState, 1, hvalue(&luaState->l_gt)); - pushClosure(luaState, (Closure *)closure); - // >>>>> ...... func - closure->p = makeFakeProto(luaState, 1); - - // Temporarily initialize the upvalue to nil - lua_pushnil(luaState); - closure->upvals[0] = makeUpValue(luaState, -1); - lua_pop(luaState, 1); -} - -void boxUpValue_finish(lua_State *luaState) { - // >>>>> ...... func obj - LClosure *lcl = (LClosure *)clvalue(getObject(luaState, -2)); - - lcl->upvals[0]->u.value = *getObject(luaState, -1); - lua_pop(luaState, 1); - // >>>>> ...... func -} - -} // End of namespace Lua diff --git a/engines/sword25/util/lua_serialization_util.h b/engines/sword25/util/lua_serialization_util.h deleted file mode 100644 index 345996f606..0000000000 --- a/engines/sword25/util/lua_serialization_util.h +++ /dev/null @@ -1,98 +0,0 @@ -/* 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 distri8buted 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_UTIL_H -#define LUA_SERIALIZATION_UTIL_H - - -struct lua_State; - -#include "lua/lobject.h" - -typedef TValue *StkId; - -namespace Lua { - -#define lua_malloc(luaState, nsize) lua_realloc(luaState, nullptr, 0, nsize) -#define lua_reallocv(luaState, block, on, n, e) lua_realloc(luaState, block, (on) * (e), (n) * (e)) -#define lua_reallocvector(luaState, vec, oldn, n, T) ((vec) = (T *)(lua_reallocv(luaState, vec, oldn, n, sizeof(T)))) -#define lua_newVector(luaState, num, T) ((T *)lua_reallocv(luaState, nullptr, 0, num, sizeof(T))) -#define lua_new(luaState,T) (T *)lua_malloc(luaState, sizeof(T)) - -void *lua_realloc(lua_State *luaState, void *block, size_t osize, size_t nsize); - -void pushObject(lua_State *luaState, TValue *obj); -void pushProto(lua_State *luaState, Proto *proto); -void pushUpValue(lua_State *luaState, UpVal *upval); -void pushString(lua_State *luaState, TString *str); - -StkId getObject(lua_State *luaState, int stackpos); - -void lua_linkObjToGC(lua_State *luaState, GCObject *obj, lu_byte type); - -#define sizeLclosure(n) ((sizeof(LClosure)) + sizeof(TValue *) * ((n) - 1)) - -Closure *lua_newLclosure(lua_State *luaState, int numElements, Table *elementTable); -void pushClosure(lua_State *luaState, Closure *closure); - -Proto *createProto(lua_State *luaState); -Proto *makeFakeProto(lua_State *L, lu_byte nups); - -TString *createString(lua_State *luaState, const char *str, size_t len); - -UpVal *createUpValue(lua_State *luaState, int stackpos); -void unboxUpValue(lua_State *luaState); - -/* Appends one stack to another stack, but the stack is reversed in the process */ -size_t appendStackToStack_reverse(lua_State *from, lua_State *to); -void correctStack(lua_State *L, TValue *oldstack); -void lua_reallocstack(lua_State *L, int newsize); -void lua_growstack(lua_State *L, int n); - -void lua_reallocCallInfo(lua_State *lauState, int newsize); - -/* 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. */ -void GCUnlink(lua_State *luaState, GCObject *gco); - -TString *lua_newlstr(lua_State *luaState, const char *str, size_t len); -void lua_link(lua_State *luaState, GCObject *o, lu_byte tt); -Proto *lua_newproto(lua_State *luaState) ; - -UpVal *makeUpValue(lua_State *luaState, int stackPos); -/** - * 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. - */ -void boxUpValue_start(lua_State *luaState); -void boxUpValue_finish(lua_State *luaState); - -} // End of namespace Lua - -#endif diff --git a/engines/sword25/util/lua_serializer.cpp b/engines/sword25/util/lua_serializer.cpp deleted file mode 100644 index 55b6257324..0000000000 --- a/engines/sword25/util/lua_serializer.cpp +++ /dev/null @@ -1,787 +0,0 @@ -/* 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 "sword25/util/lua_serialization_util.h" - -#include "common/stream.h" - -#include "lua/lobject.h" -#include "lua/lstate.h" -#include "lua/lgc.h" - - -namespace Lua { - -#define PERMANENT_TYPE 101 - -struct SerializationInfo { - lua_State *luaState; - Common::WriteStream *writeStream; - uint counter; -}; - -static void serialize(SerializationInfo *info); - -static void serializeBoolean(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 - serialize(&info); - - // Return the stack back to the original state - lua_remove(luaState, 2); - // >>>>> permTbl rootObj -} - -static void serialize(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 - serialize(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(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 object isn't special and should be persisted normally - info->writeStream->writeSint32LE(0); - - return false; - } else { - lua_pushstring(info->luaState, "Type not literally persistable by default"); - lua_error(info->luaState); - - return false; // Not reached - } - } - - // >>>>> 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 that the object isn't special and should be persisted normally - info->writeStream->writeSint32LE(0); - - return false; - } else { - lua_pushstring(info->luaState, "Type not literally persistable by default"); - lua_error(info->luaState); - - return false; // Return false - } - - } else if (lua_isboolean(info->luaState, -1)) { - // >>>>> permTbl indexTbl ...... obj metaTbl bool - if (lua_toboolean(info->luaState, -1)) { - // Write out a flag declaring that the object isn't special and should be persisted normally - 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 - serialize(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 */ - serialize(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 - serialize(info); - - lua_pop(info->luaState, 1); - // >>>>> permTbl indexTbl ...... tbl k v */ - - // Serialize the value - serialize(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 - - serialize(info); - - lua_pop(info->luaState, 1); - // >>>>> permTbl indexTbl ...... tbl -} - -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 */ - - serialize(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 - pushUpValue(info->luaState, cl->l.upvals[i]); - // >>>>> permTbl indexTbl ...... func upval - - serialize(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 - serialize(info); - - lua_pop(info->luaState, 1); - // >>>>> permTbl indexTbl ...... func - } -} - -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 - uint32 stackSize = static_cast(appendStackToStack_reverse(threadState, info->luaState)); - info->writeStream->writeUint32LE(stackSize); - - // >>>>> permTbl indexTbl ...... thread (reversed contents of thread stack) */ - for (; stackSize > 0; --stackSize) { - serialize(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((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(ci->base - threadState->stack); - uint32 stackFunc = static_cast(ci->func - threadState->stack); - uint32 stackTop = static_cast(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(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(threadState->base - threadState->stack); - uint32 stackFunc = static_cast(threadState->top - threadState->stack); - info->writeStream->writeUint32LE(stackBase); - info->writeStream->writeUint32LE(stackFunc); - - // Same argument as above about truncation - uint32 stackOffset = static_cast(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); - - pushUpValue(info->luaState, upVal); - // >>>>> permTbl indexTbl ...... thread upVal - - serialize(info); - - lua_pop(info->luaState, 1); - // >>>>> permTbl indexTbl ...... thread - - // Same argument as above about truncation - uint32 stackpos = static_cast(upVal->v - threadState->stack); - info->writeStream->writeUint32LE(stackpos); - } - - // >>>>> permTbl indexTbl ...... thread - lua_pushnil(info->luaState); - // >>>>> permTbl indexTbl ...... thread nil - - // Use nil as a terminator - serialize(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 - - serialize(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 */ - - serialize(info); - - lua_pop(info->luaState, 1); - // >>>>> permTbl indexTbl ...... proto - } - - // >>>>> permTbl indexTbl ...... proto - - // Serialize the code - info->writeStream->writeSint32LE(proto->sizecode); - - uint32 len = static_cast(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 - - serialize(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 - - serialize(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 - - serialize(info); - - lua_pop(info->luaState, 1); - // >>>>> permTbl indexTbl ...... proto - - // Serialize line numbers - info->writeStream->writeSint32LE(proto->sizelineinfo); - - if (proto->sizelineinfo) { - uint32 len = static_cast(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 - - serialize(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(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 - serialize(info); - - lua_pop(info->luaState, 1); - /* perms reftbl ... udata */ -} - - -} // End of namespace Lua diff --git a/engines/sword25/util/lua_unpersist.cpp b/engines/sword25/util/lua_unpersist.cpp new file mode 100644 index 0000000000..aa924ff7c5 --- /dev/null +++ b/engines/sword25/util/lua_unpersist.cpp @@ -0,0 +1,698 @@ +/* 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_persistence.h" + +#include "sword25/util/double_serializer.h" +#include "sword25/util/lua_persistence_util.h" + +#include "common/stream.h" + +#include "lua/lobject.h" +#include "lua/lstate.h" +#include "lua/lgc.h" +#include "lua/lopcodes.h" + + +namespace Lua { + +struct UnSerializationInfo { + lua_State *luaState; + Common::ReadStream *readStream; +}; + +static void unserialize(UnSerializationInfo *info); + +static void unserializeBoolean(UnSerializationInfo *info); +static void unserializeNumber(UnSerializationInfo *info); +static void unserializeString(UnSerializationInfo *info); +static void unserializeTable(UnSerializationInfo *info, int index); +static void unserializeFunction(UnSerializationInfo *info, int index); +static void unserializeThread(UnSerializationInfo *info, int index); +static void unserializeProto(UnSerializationInfo *info, int index); +static void unserializeUpValue(UnSerializationInfo *info, int index); +static void unserializeUserData(UnSerializationInfo *info, int index); +static void unserializePermanent(UnSerializationInfo *info, int index); + + +void unpersistLua(lua_State *luaState, Common::ReadStream *readStream) { + UnSerializationInfo info; + info.luaState = luaState; + info.readStream = readStream; + + // The process starts with the lua stack as follows: + // >>>>> permTbl + // That's the table of permanents + + // Make sure there is enough room on the stack + lua_checkstack(luaState, 3); + + // Create a table to hold indexes of everything thats already been read + lua_newtable(luaState); + // >>>>> permTbl indexTbl + + // Prevent garbage collection while we unserialize + lua_gc(luaState, LUA_GCSTOP, 0); + + // Unserialize the root object + unserialize(&info); + // >>>>> permTbl indexTbl rootObj + + // Re-start garbage collection + lua_gc(luaState, LUA_GCRESTART, 0); + + // Remove the indexTbl + lua_replace(luaState, 2); + // >>>>> permTbl rootObj +} + +/* The object is left on the stack. This is primarily used by unserialize, but + * may be used by GCed objects that may incur cycles in order to preregister + * the object. */ +static void registerObjectInIndexTable(UnSerializationInfo *info, int index) { + // >>>>> permTbl indexTbl ...... obj + + // Make sure there is enough room on the stack + lua_checkstack(info->luaState, 2); + + lua_pushlightuserdata(info->luaState, (void *)index); + // >>>>> permTbl indexTbl ...... obj index + + lua_pushvalue(info->luaState, -2); + // >>>>> permTbl indexTbl ...... obj index obj + + // Push the k/v pair into the indexTbl + lua_settable(info->luaState, 2); + // >>>>> permTbl indexTbl ...... obj +} + +static void unserialize(UnSerializationInfo *info) { + // >>>>> permTbl indexTbl ...... + + // Make sure there is enough room on the stack + lua_checkstack(info->luaState, 2); + + byte isARealValue = info->readStream->readByte(); + if (isARealValue) { + int index = info->readStream->readSint32LE(); + int type = info->readStream->readSint32LE(); + + switch (type) { + case LUA_TBOOLEAN: + unserializeBoolean(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: + unserializeNumber(info); + break; + case LUA_TSTRING: + unserializeString(info); + break; + case LUA_TTABLE: + unserializeTable(info, index); + break; + case LUA_TFUNCTION: + unserializeFunction(info, index); + break; + case LUA_TTHREAD: + unserializeThread(info, index); + break; + case LUA_TPROTO: + unserializeProto(info, index); + break; + case LUA_TUPVAL: + unserializeUpValue(info, index); + break; + case LUA_TUSERDATA: + unserializeUserData(info, index); + break; + case PERMANENT_TYPE: + unserializePermanent(info, index); + break; + default: + assert(0); + } + + + // >>>>> permTbl indexTbl ...... obj + assert(lua_type(info->luaState, -1) == type || + type == PERMANENT_TYPE || + // Remember, upvalues get a special dispensation, as described in boxUpValue + (lua_type(info->luaState, -1) == LUA_TFUNCTION && type == LUA_TUPVAL)); + + registerObjectInIndexTable(info, index); + // >>>>> permTbl indexTbl ...... obj + } else { + int index = info->readStream->readSint32LE(); + + if (index == 0) { + lua_pushnil(info->luaState); + // >>>>> permTbl indexTbl ...... nil + } else { + // Fetch the object from the indexTbl + + lua_pushlightuserdata(info->luaState, (void *)index); + // >>>>> permTbl indexTbl ...... index + + lua_gettable(info->luaState, 2); + // >>>>> permTbl indexTbl ...... ?obj? + + assert(!lua_isnil(info->luaState, -1)); + } + // >>>>> permTbl indexTbl ...... obj/nil + } + + // >>>>> permTbl indexTbl ...... obj/nil +} + +static void unserializeBoolean(UnSerializationInfo *info) { + // >>>>> permTbl indexTbl ...... + + // Make sure there is enough room on the stack + lua_checkstack(info->luaState, 1); + + int value = info->readStream->readSint32LE(); + + lua_pushboolean(info->luaState, value); + // >>>>> permTbl indexTbl ...... bool +} + +static void unserializeNumber(UnSerializationInfo *info) { + // >>>>> permTbl indexTbl ...... + + // Make sure there is enough room on the stack + lua_checkstack(info->luaState, 1); + + // Read the serialized double + Util::SerializedDouble serializedValue; + serializedValue.significandOne = info->readStream->readUint32LE(); + serializedValue.signAndSignificandTwo = info->readStream->readUint32LE(); + serializedValue.exponent = info->readStream->readSint16LE(); + + lua_Number value = Util::decodeDouble(serializedValue); + + lua_pushnumber(info->luaState, value); + // >>>>> permTbl indexTbl ...... num +} + +static void unserializeString(UnSerializationInfo *info) { + // >>>>> permTbl indexTbl ...... + + // Make sure there is enough room on the stack + lua_checkstack(info->luaState, 1); + + uint32 length = info->readStream->readUint32LE(); + char *string = new char[length]; + + info->readStream->read(string, length); + lua_pushlstring(info->luaState, string, length); + + // >>>>> permTbl indexTbl ...... string + + delete[] string; +} + +static void unserializeSpecialTable(UnSerializationInfo *info, int index) { + // >>>>> permTbl indexTbl ...... + + // Make sure there is enough room on the stack + lua_checkstack(info->luaState, 1); + + unserialize(info); + + // >>>>> permTbl indexTbl ...... spfunc + lua_call(info->luaState, 0, 1); + // >>>>> permTbl indexTbl ...... tbl +} + +static void unserializeLiteralTable(UnSerializationInfo *info, int index) { + // >>>>> permTbl indexTbl ...... + + // Make sure there is enough room on the stack + lua_checkstack(info->luaState, 3); + + // Preregister table for handling of cycles + lua_newtable(info->luaState); + + // >>>>> permTbl indexTbl ...... tbl + registerObjectInIndexTable(info, index); + // >>>>> permTbl indexTbl ...... tbl + + // Unserialize metatable + unserialize(info); + // >>>>> permTbl indexTbl ...... tbl ?metaTbl/nil? + + if (lua_istable(info->luaState, -1)) { + // >>>>> permTbl indexTbl ...... tbl metaTbl + lua_setmetatable(info->luaState, -2); + // >>>>> permTbl indexTbl ...... tbl + } else { + // >>>>> permTbl indexTbl ...... tbl nil + lua_pop(info->luaState, 1); + // >>>>> permTbl indexTbl ...... tbl + } + // >>>>> permTbl indexTbl ...... tbl + + + while (1) { + // >>>>> permTbl indexTbl ...... tbl + unserialize(info); + // >>>>> permTbl indexTbl ...... tbl key/nil + + // The table serialization is nil terminated + if (lua_isnil(info->luaState, -1)) { + // >>>>> permTbl indexTbl ...... tbl nil + lua_pop(info->luaState, 1); + // >>>>> permTbl indexTbl ...... tbl + + break; + } + + // >>>>> permTbl indexTbl ...... tbl key + unserialize(info); + // >>>>> permTbl indexTbl ...... tbl value + + lua_rawset(info->luaState, -3); + // >>>>> permTbl indexTbl ...... tbl + } +} + +void unserializeTable(UnSerializationInfo *info, int index) { + // >>>>> permTbl indexTbl ...... + + // Make sure there is enough room on the stack + lua_checkstack(info->luaState, 1); + + int isSpecial = info->readStream->readSint32LE(); + + if (isSpecial) { + unserializeSpecialTable(info, index); + // >>>>> permTbl indexTbl ...... tbl + } else { + unserializeLiteralTable(info, index); + // >>>>> permTbl indexTbl ...... tbl + } +} + +void unserializeFunction(UnSerializationInfo *info, int index) { + // >>>>> permTbl indexTbl ...... + + // Make sure there is enough room on the stack + lua_checkstack(info->luaState, 2); + + byte numUpValues = info->readStream->readByte(); + + LClosure *lclosure = (LClosure *)lua_newLclosure(info->luaState, numUpValues, hvalue(&info->luaState->l_gt)); + pushClosure(info->luaState, (Closure *)lclosure); + // >>>>> permTbl indexTbl ...... func + + // Put *some* proto in the closure, before the GC can find it + lclosure->p = makeFakeProto(info->luaState, numUpValues); + + //Also, we need to temporarily fill the upvalues + lua_pushnil(info->luaState); + // >>>>> permTbl indexTbl ...... func nil + + for (byte i = 0; i < numUpValues; ++i) { + lclosure->upvals[i] = createUpValue(info->luaState, -1); + } + + lua_pop(info->luaState, 1); + // >>>>> permTbl indexTbl ...... 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 + registerObjectInIndexTable(info, index); + + // Now that it's safe, we can get the real proto + unserialize(info); + // >>>>> permTbl indexTbl ...... func proto + + lclosure->p = gco2p(getObject(info->luaState, -1)->value.gc); + + lua_pop(info->luaState, 1); + // >>>>> permTbl indexTbl ...... func + + for (byte i = 0; i < numUpValues; ++i) { + // >>>>> permTbl indexTbl ...... func + unserialize(info); + // >>>>> permTbl indexTbl ...... func func2 + + unboxUpValue(info->luaState); + // >>>>> permTbl indexTbl ...... func upValue + lclosure->upvals[i] = gco2uv(getObject(info->luaState, -1)->value.gc); + + lua_pop(info->luaState, 1); + // >>>>> permTbl indexTbl ...... func + } + + // Finally, the fenv + unserialize(info); + + // >>>>> permTbl indexTbl ...... func ?fenv/nil? + if (!lua_isnil(info->luaState, -1)) { + // >>>>> permTbl indexTbl ...... func fenv + lua_setfenv(info->luaState, -2); + // >>>>> permTbl indexTbl ...... func + } else { + // >>>>> permTbl indexTbl ...... func nil + lua_pop(info->luaState, 1); + // >>>>> permTbl indexTbl ...... func + } + + // >>>>> permTbl indexTbl ...... func +} + +void unserializeThread(UnSerializationInfo *info, int index) { + // >>>>> permTbl indexTbl ...... + + lua_State *L2; + uint32 stacklimit = 0; + + L2 = lua_newthread(info->luaState); + lua_checkstack(info->luaState, 3); + + // L1: permTbl indexTbl ...... thread + // L2: (empty) + registerObjectInIndexTable(info, index); + + // First, deserialize the object stack + uint32 stackSize = info->readStream->readUint32LE(); + lua_growstack(info->luaState, (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 (uint32 i = 0; i < stackSize; ++i) { + unserialize(info); + // L1: permTbl indexTbl ...... thread obj* + } + + lua_xmove(info->luaState, L2, stackSize); + // L1: permTbl indexTbl ...... thread + // L2: obj* + + // Hereafter, stacks refer to L1 + + + // Now, deserialize the CallInfo stack + + uint32 numFrames = info->readStream->readUint32LE(); + + lua_reallocCallInfo(L2, numFrames * 2); + for (uint32 i = 0; i < numFrames; ++i) { + CallInfo *ci = L2->base_ci + i; + uint32 stackbase = info->readStream->readUint32LE(); + uint32 stackfunc = info->readStream->readUint32LE(); + uint32 stacktop = info->readStream->readUint32LE(); + + ci->nresults = info->readStream->readSint32LE(); + + uint32 savedpc = info->readStream->readUint32LE(); + + 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; + } + + // >>>>> permTbl indexTbl ...... thread + // Deserialize the state's other parameters, with the exception of upval stuff + + L2->savedpc = L2->ci->savedpc; + L2->status = info->readStream->readByte(); + uint32 stackbase = info->readStream->readUint32LE(); + uint32 stacktop = info->readStream->readUint32LE(); + + + L2->errfunc = info->readStream->readUint32LE(); + + L2->base = L2->stack + stackbase; + L2->top = L2->stack + stacktop; + + // Finally, "reopen" upvalues. See serializeUpVal() for why we do this + UpVal *uv; + GCObject **nextslot = &L2->openupval; + global_State *g = G(L2); + + while (true) { + unserialize(info); + // >>>>> permTbl indexTbl ...... thread upVal/nil + + // The list is terminated by a nil + if (lua_isnil(info->luaState, -1)) { + // >>>>> permTbl indexTbl ...... thread nil + lua_pop(info->luaState, 1); + // >>>>> permTbl indexTbl ...... thread + break; + } + + // >>>>> permTbl indexTbl ...... thread boxedUpVal + unboxUpValue(info->luaState); + // >>>>> permTbl indexTbl ...... thread boxedUpVal + + uv = &(getObject(info->luaState, -1)->value.gc->uv); + lua_pop(info->luaState, 1); + // >>>>> permTbl indexTbl ...... thread + + uint32 stackpos = info->readStream->readUint32LE(); + uv->v = L2->stack + stackpos; + + GCUnlink(info->luaState, (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' + lua_checkstack(L2, (int)stacklimit); + for (StkId o = L2->top; o <= L2->top + stacklimit; ++o) { + setnilvalue(o); + } +} + +void unserializeProto(UnSerializationInfo *info, int index) { + // >>>>> permTbl indexTbl ...... + + // 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 = lua_newlstr(info->luaState, "", 0); + Proto *p = lua_newproto(info->luaState); + p->source = source; + p->sizecode = 1; + p->code = (Instruction *)lua_reallocv(info->luaState, NULL, 0, 1, sizeof(Instruction)); + p->code[0] = CREATE_ABC(OP_RETURN, 0, 1, 0); + p->maxstacksize = 2; + p->sizek = 0; + p->sizep = 0; + + lua_checkstack(info->luaState, 2); + + pushProto(info->luaState, p); + // >>>>> permTbl indexTbl ...... proto + + // We don't need to register early, since protos can never ever be + // involved in cyclic references + + // Read in constant references + int sizek = info->readStream->readSint32LE(); + lua_reallocvector(info->luaState, p->k, 0, sizek, TValue); + for (int i = 0; i < sizek; ++i) { + // >>>>> permTbl indexTbl ...... proto + unserialize(info); + // >>>>> permTbl indexTbl ...... proto k + + setobj2s(info->luaState, &p->k[i], getObject(info->luaState, -1)); + p->sizek++; + + lua_pop(info->luaState, 1); + // >>>>> permTbl indexTbl ...... proto + } + // >>>>> permTbl indexTbl ...... proto + + // Read in sub-proto references + + int sizep = info->readStream->readSint32LE(); + lua_reallocvector(info->luaState, p->p, 0, sizep, Proto *); + for (int i = 0; i < sizep; ++i) { + // >>>>> permTbl indexTbl ...... proto + unserialize(info); + // >>>>> permTbl indexTbl ...... proto subproto + + p->p[i] = (Proto *)getObject(info->luaState, -1)->value.gc; + p->sizep++; + + lua_pop(info->luaState, 1); + // >>>>> permTbl indexTbl ...... proto + } + // >>>>> permTbl indexTbl ...... proto + + + // Read in code + p->sizecode = info->readStream->readSint32LE(); + lua_reallocvector(info->luaState, p->code, 1, p->sizecode, Instruction); + info->readStream->read(p->code, sizeof(Instruction) * p->sizecode); + + + /* Read in upvalue names */ + p->sizeupvalues = info->readStream->readSint32LE(); + if (p->sizeupvalues) { + lua_reallocvector(info->luaState, p->upvalues, 0, p->sizeupvalues, TString *); + for (int i = 0; i < p->sizeupvalues; ++i) { + // >>>>> permTbl indexTbl ...... proto + unserialize(info); + // >>>>> permTbl indexTbl ...... proto str + + p->upvalues[i] = lua_newlstr(info->luaState, lua_tostring(info->luaState, -1), strlen(lua_tostring(info->luaState, -1))); + lua_pop(info->luaState, 1); + // >>>>> permTbl indexTbl ...... proto + } + } + // >>>>> permTbl indexTbl ...... proto + + // Read in local variable infos + p->sizelocvars = info->readStream->readSint32LE(); + if (p->sizelocvars) { + lua_reallocvector(info->luaState, p->locvars, 0, p->sizelocvars, LocVar); + for (int i = 0; i < p->sizelocvars; ++i) { + // >>>>> permTbl indexTbl ...... proto + unserialize(info); + // >>>>> permTbl indexTbl ...... proto str + + p->locvars[i].varname = lua_newlstr(info->luaState, lua_tostring(info->luaState, -1), strlen(lua_tostring(info->luaState, -1))); + lua_pop(info->luaState, 1); + // >>>>> permTbl indexTbl ...... proto + + p->locvars[i].startpc = info->readStream->readSint32LE(); + p->locvars[i].endpc = info->readStream->readSint32LE(); + } + } + // >>>>> permTbl indexTbl ...... proto + + // Read in source string + unserialize(info); + // >>>>> permTbl indexTbl ...... proto sourceStr + + p->source = lua_newlstr(info->luaState, lua_tostring(info->luaState, -1), strlen(lua_tostring(info->luaState, -1))); + lua_pop(info->luaState, 1); + // >>>>> permTbl indexTbl ...... proto + + // Read in line numbers + p->sizelineinfo = info->readStream->readSint32LE(); + if (p->sizelineinfo) { + lua_reallocvector(info->luaState, p->lineinfo, 0, p->sizelineinfo, int); + info->readStream->read(p->lineinfo, sizeof(int) * p->sizelineinfo); + } + + + /* Read in linedefined and lastlinedefined */ + p->linedefined = info->readStream->readSint32LE(); + p->lastlinedefined = info->readStream->readSint32LE(); + + // Read in misc values + p->nups = info->readStream->readByte(); + p->numparams = info->readStream->readByte(); + p->is_vararg = info->readStream->readByte(); + p->maxstacksize = info->readStream->readByte(); +} + +void unserializeUpValue(UnSerializationInfo *info, int index) { + // >>>>> permTbl indexTbl ...... + lua_checkstack(info->luaState, 2); + + boxUpValue_start(info->luaState); + // >>>>> permTbl indexTbl ...... func + registerObjectInIndexTable(info, index); + + unserialize(info); + // >>>>> permTbl indexTbl ...... func obj + + boxUpValue_finish(info->luaState); + // >>>>> permTbl indexTbl ...... func +} + +void unserializeUserData(UnSerializationInfo *info, int index) { + // >>>>> permTbl indexTbl ...... + + // Make sure there is enough room on the stack + lua_checkstack(info->luaState, 2); + + int isspecial = info->readStream->readSint32LE(); + if (isspecial) { + unserialize(info); + // >>>>> permTbl indexTbl ...... specialFunc + + lua_call(info->luaState, 0, 1); + // >>>>> permTbl indexTbl ...... udata + } else { + uint32 length = info->readStream->readUint32LE(); + lua_newuserdata(info->luaState, length); + // >>>>> permTbl indexTbl ...... udata + registerObjectInIndexTable(info, index); + + info->readStream->read(lua_touserdata(info->luaState, -1), length); + + unserialize(info); + // >>>>> permTbl indexTbl ...... udata metaTable/nil + + lua_setmetatable(info->luaState, -2); + // >>>>> permTbl indexTbl ...... udata + } + // >>>>> permTbl indexTbl ...... udata +} + +void unserializePermanent(UnSerializationInfo *info, int index) { + // >>>>> permTbl indexTbl ...... + + // Make sure there is enough room on the stack + lua_checkstack(info->luaState, 2); + + unserialize(info); + // >>>>> permTbl indexTbl ...... permKey + + lua_gettable(info->luaState, 1); + // >>>>> permTbl indexTbl ...... perm +} + +} // End of namespace Lua diff --git a/engines/sword25/util/lua_unserializer.cpp b/engines/sword25/util/lua_unserializer.cpp deleted file mode 100644 index 803c79c8c2..0000000000 --- a/engines/sword25/util/lua_unserializer.cpp +++ /dev/null @@ -1,698 +0,0 @@ -/* 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 "sword25/util/lua_serialization_util.h" - -#include "common/stream.h" - -#include "lua/lobject.h" -#include "lua/lstate.h" -#include "lua/lgc.h" -#include "lua/lopcodes.h" - - -namespace Lua { - -struct UnSerializationInfo { - lua_State *luaState; - Common::ReadStream *readStream; -}; - -static void unserialize(UnSerializationInfo *info); - -static void unserializeBoolean(UnSerializationInfo *info); -static void unserializeNumber(UnSerializationInfo *info); -static void unserializeString(UnSerializationInfo *info); -static void unserializeTable(UnSerializationInfo *info, int index); -static void unserializeFunction(UnSerializationInfo *info, int index); -static void unserializeThread(UnSerializationInfo *info, int index); -static void unserializeProto(UnSerializationInfo *info, int index); -static void unserializeUpValue(UnSerializationInfo *info, int index); -static void unserializeUserData(UnSerializationInfo *info, int index); -static void unserializePermanent(UnSerializationInfo *info, int index); - - -void unserializeLua(lua_State *luaState, Common::ReadStream *readStream) { - UnSerializationInfo info; - info.luaState = luaState; - info.readStream = readStream; - - // The process starts with the lua stack as follows: - // >>>>> permTbl - // That's the table of permanents - - // Make sure there is enough room on the stack - lua_checkstack(luaState, 3); - - // Create a table to hold indexes of everything thats already been read - lua_newtable(luaState); - // >>>>> permTbl indexTbl - - // Prevent garbage collection while we unserialize - lua_gc(luaState, LUA_GCSTOP, 0); - - // Unserialize the root object - unserialize(&info); - // >>>>> permTbl indexTbl rootObj - - // Re-start garbage collection - lua_gc(luaState, LUA_GCRESTART, 0); - - // Remove the indexTbl - lua_replace(luaState, 2); - // >>>>> permTbl rootObj -} - -/* The object is left on the stack. This is primarily used by unserialize, but - * may be used by GCed objects that may incur cycles in order to preregister - * the object. */ -static void registerObjectInIndexTable(UnSerializationInfo *info, int index) { - // >>>>> permTbl indexTbl ...... obj - - // Make sure there is enough room on the stack - lua_checkstack(info->luaState, 2); - - lua_pushlightuserdata(info->luaState, (void *)index); - // >>>>> permTbl indexTbl ...... obj index - - lua_pushvalue(info->luaState, -2); - // >>>>> permTbl indexTbl ...... obj index obj - - // Push the k/v pair into the indexTbl - lua_settable(info->luaState, 2); - // >>>>> permTbl indexTbl ...... obj -} - -static void unserialize(UnSerializationInfo *info) { - // >>>>> permTbl indexTbl ...... - - // Make sure there is enough room on the stack - lua_checkstack(info->luaState, 2); - - byte isARealValue = info->readStream->readByte(); - if (isARealValue) { - int index = info->readStream->readSint32LE(); - int type = info->readStream->readSint32LE(); - - switch (type) { - case LUA_TBOOLEAN: - unserializeBoolean(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: - unserializeNumber(info); - break; - case LUA_TSTRING: - unserializeString(info); - break; - case LUA_TTABLE: - unserializeTable(info, index); - break; - case LUA_TFUNCTION: - unserializeFunction(info, index); - break; - case LUA_TTHREAD: - unserializeThread(info, index); - break; - case LUA_TPROTO: - unserializeProto(info, index); - break; - case LUA_TUPVAL: - unserializeUpValue(info, index); - break; - case LUA_TUSERDATA: - unserializeUserData(info, index); - break; - case PERMANENT_TYPE: - unserializePermanent(info, index); - break; - default: - assert(0); - } - - - // >>>>> permTbl indexTbl ...... obj - assert(lua_type(info->luaState, -1) == type || - type == PERMANENT_TYPE || - // Remember, upvalues get a special dispensation, as described in boxUpValue - (lua_type(info->luaState, -1) == LUA_TFUNCTION && type == LUA_TUPVAL)); - - registerObjectInIndexTable(info, index); - // >>>>> permTbl indexTbl ...... obj - } else { - int index = info->readStream->readSint32LE(); - - if (index == 0) { - lua_pushnil(info->luaState); - // >>>>> permTbl indexTbl ...... nil - } else { - // Fetch the object from the indexTbl - - lua_pushlightuserdata(info->luaState, (void *)index); - // >>>>> permTbl indexTbl ...... index - - lua_gettable(info->luaState, 2); - // >>>>> permTbl indexTbl ...... ?obj? - - assert(!lua_isnil(info->luaState, -1)); - } - // >>>>> permTbl indexTbl ...... obj/nil - } - - // >>>>> permTbl indexTbl ...... obj/nil -} - -static void unserializeBoolean(UnSerializationInfo *info) { - // >>>>> permTbl indexTbl ...... - - // Make sure there is enough room on the stack - lua_checkstack(info->luaState, 1); - - int value = info->readStream->readSint32LE(); - - lua_pushboolean(info->luaState, value); - // >>>>> permTbl indexTbl ...... bool -} - -static void unserializeNumber(UnSerializationInfo *info) { - // >>>>> permTbl indexTbl ...... - - // Make sure there is enough room on the stack - lua_checkstack(info->luaState, 1); - - // Read the serialized double - Util::SerializedDouble serializedValue; - serializedValue.significandOne = info->readStream->readUint32LE(); - serializedValue.signAndSignificandTwo = info->readStream->readUint32LE(); - serializedValue.exponent = info->readStream->readSint16LE(); - - lua_Number value = Util::decodeDouble(serializedValue); - - lua_pushnumber(info->luaState, value); - // >>>>> permTbl indexTbl ...... num -} - -static void unserializeString(UnSerializationInfo *info) { - // >>>>> permTbl indexTbl ...... - - // Make sure there is enough room on the stack - lua_checkstack(info->luaState, 1); - - uint32 length = info->readStream->readUint32LE(); - char *string = new char[length]; - - info->readStream->read(string, length); - lua_pushlstring(info->luaState, string, length); - - // >>>>> permTbl indexTbl ...... string - - delete[] string; -} - -static void unserializeSpecialTable(UnSerializationInfo *info, int index) { - // >>>>> permTbl indexTbl ...... - - // Make sure there is enough room on the stack - lua_checkstack(info->luaState, 1); - - unserialize(info); - - // >>>>> permTbl indexTbl ...... spfunc - lua_call(info->luaState, 0, 1); - // >>>>> permTbl indexTbl ...... tbl -} - -static void unserializeLiteralTable(UnSerializationInfo *info, int index) { - // >>>>> permTbl indexTbl ...... - - // Make sure there is enough room on the stack - lua_checkstack(info->luaState, 3); - - // Preregister table for handling of cycles - lua_newtable(info->luaState); - - // >>>>> permTbl indexTbl ...... tbl - registerObjectInIndexTable(info, index); - // >>>>> permTbl indexTbl ...... tbl - - // Unserialize metatable - unserialize(info); - // >>>>> permTbl indexTbl ...... tbl ?metaTbl/nil? - - if (lua_istable(info->luaState, -1)) { - // >>>>> permTbl indexTbl ...... tbl metaTbl - lua_setmetatable(info->luaState, -2); - // >>>>> permTbl indexTbl ...... tbl - } else { - // >>>>> permTbl indexTbl ...... tbl nil - lua_pop(info->luaState, 1); - // >>>>> permTbl indexTbl ...... tbl - } - // >>>>> permTbl indexTbl ...... tbl - - - while (1) { - // >>>>> permTbl indexTbl ...... tbl - unserialize(info); - // >>>>> permTbl indexTbl ...... tbl key/nil - - // The table serialization is nil terminated - if (lua_isnil(info->luaState, -1)) { - // >>>>> permTbl indexTbl ...... tbl nil - lua_pop(info->luaState, 1); - // >>>>> permTbl indexTbl ...... tbl - - break; - } - - // >>>>> permTbl indexTbl ...... tbl key - unserialize(info); - // >>>>> permTbl indexTbl ...... tbl value - - lua_rawset(info->luaState, -3); - // >>>>> permTbl indexTbl ...... tbl - } -} - -void unserializeTable(UnSerializationInfo *info, int index) { - // >>>>> permTbl indexTbl ...... - - // Make sure there is enough room on the stack - lua_checkstack(info->luaState, 1); - - int isSpecial = info->readStream->readSint32LE(); - - if (isSpecial) { - unserializeSpecialTable(info, index); - // >>>>> permTbl indexTbl ...... tbl - } else { - unserializeLiteralTable(info, index); - // >>>>> permTbl indexTbl ...... tbl - } -} - -void unserializeFunction(UnSerializationInfo *info, int index) { - // >>>>> permTbl indexTbl ...... - - // Make sure there is enough room on the stack - lua_checkstack(info->luaState, 2); - - byte numUpValues = info->readStream->readByte(); - - LClosure *lclosure = (LClosure *)lua_newLclosure(info->luaState, numUpValues, hvalue(&info->luaState->l_gt)); - pushClosure(info->luaState, (Closure *)lclosure); - // >>>>> permTbl indexTbl ...... func - - // Put *some* proto in the closure, before the GC can find it - lclosure->p = makeFakeProto(info->luaState, numUpValues); - - //Also, we need to temporarily fill the upvalues - lua_pushnil(info->luaState); - // >>>>> permTbl indexTbl ...... func nil - - for (byte i = 0; i < numUpValues; ++i) { - lclosure->upvals[i] = createUpValue(info->luaState, -1); - } - - lua_pop(info->luaState, 1); - // >>>>> permTbl indexTbl ...... 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 - registerObjectInIndexTable(info, index); - - // Now that it's safe, we can get the real proto - unserialize(info); - // >>>>> permTbl indexTbl ...... func proto - - lclosure->p = gco2p(getObject(info->luaState, -1)->value.gc); - - lua_pop(info->luaState, 1); - // >>>>> permTbl indexTbl ...... func - - for (byte i = 0; i < numUpValues; ++i) { - // >>>>> permTbl indexTbl ...... func - unserialize(info); - // >>>>> permTbl indexTbl ...... func func2 - - unboxUpValue(info->luaState); - // >>>>> permTbl indexTbl ...... func upValue - lclosure->upvals[i] = gco2uv(getObject(info->luaState, -1)->value.gc); - - lua_pop(info->luaState, 1); - // >>>>> permTbl indexTbl ...... func - } - - // Finally, the fenv - unserialize(info); - - // >>>>> permTbl indexTbl ...... func ?fenv/nil? - if (!lua_isnil(info->luaState, -1)) { - // >>>>> permTbl indexTbl ...... func fenv - lua_setfenv(info->luaState, -2); - // >>>>> permTbl indexTbl ...... func - } else { - // >>>>> permTbl indexTbl ...... func nil - lua_pop(info->luaState, 1); - // >>>>> permTbl indexTbl ...... func - } - - // >>>>> permTbl indexTbl ...... func -} - -void unserializeThread(UnSerializationInfo *info, int index) { - // >>>>> permTbl indexTbl ...... - - lua_State *L2; - uint32 stacklimit = 0; - - L2 = lua_newthread(info->luaState); - lua_checkstack(info->luaState, 3); - - // L1: permTbl indexTbl ...... thread - // L2: (empty) - registerObjectInIndexTable(info, index); - - // First, deserialize the object stack - uint32 stackSize = info->readStream->readUint32LE(); - lua_growstack(info->luaState, (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 (uint32 i = 0; i < stackSize; ++i) { - unserialize(info); - // L1: permTbl indexTbl ...... thread obj* - } - - lua_xmove(info->luaState, L2, stackSize); - // L1: permTbl indexTbl ...... thread - // L2: obj* - - // Hereafter, stacks refer to L1 - - - // Now, deserialize the CallInfo stack - - uint32 numFrames = info->readStream->readUint32LE(); - - lua_reallocCallInfo(L2, numFrames * 2); - for (uint32 i = 0; i < numFrames; ++i) { - CallInfo *ci = L2->base_ci + i; - uint32 stackbase = info->readStream->readUint32LE(); - uint32 stackfunc = info->readStream->readUint32LE(); - uint32 stacktop = info->readStream->readUint32LE(); - - ci->nresults = info->readStream->readSint32LE(); - - uint32 savedpc = info->readStream->readUint32LE(); - - 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; - } - - // >>>>> permTbl indexTbl ...... thread - // Deserialize the state's other parameters, with the exception of upval stuff - - L2->savedpc = L2->ci->savedpc; - L2->status = info->readStream->readByte(); - uint32 stackbase = info->readStream->readUint32LE(); - uint32 stacktop = info->readStream->readUint32LE(); - - - L2->errfunc = info->readStream->readUint32LE(); - - L2->base = L2->stack + stackbase; - L2->top = L2->stack + stacktop; - - // Finally, "reopen" upvalues. See serializeUpVal() for why we do this - UpVal *uv; - GCObject **nextslot = &L2->openupval; - global_State *g = G(L2); - - while (true) { - unserialize(info); - // >>>>> permTbl indexTbl ...... thread upVal/nil - - // The list is terminated by a nil - if (lua_isnil(info->luaState, -1)) { - // >>>>> permTbl indexTbl ...... thread nil - lua_pop(info->luaState, 1); - // >>>>> permTbl indexTbl ...... thread - break; - } - - // >>>>> permTbl indexTbl ...... thread boxedUpVal - unboxUpValue(info->luaState); - // >>>>> permTbl indexTbl ...... thread boxedUpVal - - uv = &(getObject(info->luaState, -1)->value.gc->uv); - lua_pop(info->luaState, 1); - // >>>>> permTbl indexTbl ...... thread - - uint32 stackpos = info->readStream->readUint32LE(); - uv->v = L2->stack + stackpos; - - GCUnlink(info->luaState, (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' - lua_checkstack(L2, (int)stacklimit); - for (StkId o = L2->top; o <= L2->top + stacklimit; ++o) { - setnilvalue(o); - } -} - -void unserializeProto(UnSerializationInfo *info, int index) { - // >>>>> permTbl indexTbl ...... - - // 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 = lua_newlstr(info->luaState, "", 0); - Proto *p = lua_newproto(info->luaState); - p->source = source; - p->sizecode = 1; - p->code = (Instruction *)lua_reallocv(info->luaState, NULL, 0, 1, sizeof(Instruction)); - p->code[0] = CREATE_ABC(OP_RETURN, 0, 1, 0); - p->maxstacksize = 2; - p->sizek = 0; - p->sizep = 0; - - lua_checkstack(info->luaState, 2); - - pushProto(info->luaState, p); - // >>>>> permTbl indexTbl ...... proto - - // We don't need to register early, since protos can never ever be - // involved in cyclic references - - // Read in constant references - int sizek = info->readStream->readSint32LE(); - lua_reallocvector(info->luaState, p->k, 0, sizek, TValue); - for (int i = 0; i < sizek; ++i) { - // >>>>> permTbl indexTbl ...... proto - unserialize(info); - // >>>>> permTbl indexTbl ...... proto k - - setobj2s(info->luaState, &p->k[i], getObject(info->luaState, -1)); - p->sizek++; - - lua_pop(info->luaState, 1); - // >>>>> permTbl indexTbl ...... proto - } - // >>>>> permTbl indexTbl ...... proto - - // Read in sub-proto references - - int sizep = info->readStream->readSint32LE(); - lua_reallocvector(info->luaState, p->p, 0, sizep, Proto *); - for (int i = 0; i < sizep; ++i) { - // >>>>> permTbl indexTbl ...... proto - unserialize(info); - // >>>>> permTbl indexTbl ...... proto subproto - - p->p[i] = (Proto *)getObject(info->luaState, -1)->value.gc; - p->sizep++; - - lua_pop(info->luaState, 1); - // >>>>> permTbl indexTbl ...... proto - } - // >>>>> permTbl indexTbl ...... proto - - - // Read in code - p->sizecode = info->readStream->readSint32LE(); - lua_reallocvector(info->luaState, p->code, 1, p->sizecode, Instruction); - info->readStream->read(p->code, sizeof(Instruction) * p->sizecode); - - - /* Read in upvalue names */ - p->sizeupvalues = info->readStream->readSint32LE(); - if (p->sizeupvalues) { - lua_reallocvector(info->luaState, p->upvalues, 0, p->sizeupvalues, TString *); - for (int i = 0; i < p->sizeupvalues; ++i) { - // >>>>> permTbl indexTbl ...... proto - unserialize(info); - // >>>>> permTbl indexTbl ...... proto str - - p->upvalues[i] = lua_newlstr(info->luaState, lua_tostring(info->luaState, -1), strlen(lua_tostring(info->luaState, -1))); - lua_pop(info->luaState, 1); - // >>>>> permTbl indexTbl ...... proto - } - } - // >>>>> permTbl indexTbl ...... proto - - // Read in local variable infos - p->sizelocvars = info->readStream->readSint32LE(); - if (p->sizelocvars) { - lua_reallocvector(info->luaState, p->locvars, 0, p->sizelocvars, LocVar); - for (int i = 0; i < p->sizelocvars; ++i) { - // >>>>> permTbl indexTbl ...... proto - unserialize(info); - // >>>>> permTbl indexTbl ...... proto str - - p->locvars[i].varname = lua_newlstr(info->luaState, lua_tostring(info->luaState, -1), strlen(lua_tostring(info->luaState, -1))); - lua_pop(info->luaState, 1); - // >>>>> permTbl indexTbl ...... proto - - p->locvars[i].startpc = info->readStream->readSint32LE(); - p->locvars[i].endpc = info->readStream->readSint32LE(); - } - } - // >>>>> permTbl indexTbl ...... proto - - // Read in source string - unserialize(info); - // >>>>> permTbl indexTbl ...... proto sourceStr - - p->source = lua_newlstr(info->luaState, lua_tostring(info->luaState, -1), strlen(lua_tostring(info->luaState, -1))); - lua_pop(info->luaState, 1); - // >>>>> permTbl indexTbl ...... proto - - // Read in line numbers - p->sizelineinfo = info->readStream->readSint32LE(); - if (p->sizelineinfo) { - lua_reallocvector(info->luaState, p->lineinfo, 0, p->sizelineinfo, int); - info->readStream->read(p->lineinfo, sizeof(int) * p->sizelineinfo); - } - - - /* Read in linedefined and lastlinedefined */ - p->linedefined = info->readStream->readSint32LE(); - p->lastlinedefined = info->readStream->readSint32LE(); - - // Read in misc values - p->nups = info->readStream->readByte(); - p->numparams = info->readStream->readByte(); - p->is_vararg = info->readStream->readByte(); - p->maxstacksize = info->readStream->readByte(); -} - -void unserializeUpValue(UnSerializationInfo *info, int index) { - // >>>>> permTbl indexTbl ...... - lua_checkstack(info->luaState, 2); - - boxUpValue_start(info->luaState); - // >>>>> permTbl indexTbl ...... func - registerObjectInIndexTable(info, index); - - unserialize(info); - // >>>>> permTbl indexTbl ...... func obj - - boxUpValue_finish(info->luaState); - // >>>>> permTbl indexTbl ...... func -} - -void unserializeUserData(UnSerializationInfo *info, int index) { - // >>>>> permTbl indexTbl ...... - - // Make sure there is enough room on the stack - lua_checkstack(info->luaState, 2); - - int isspecial = info->readStream->readSint32LE(); - if (isspecial) { - unserialize(info); - // >>>>> permTbl indexTbl ...... specialFunc - - lua_call(info->luaState, 0, 1); - // >>>>> permTbl indexTbl ...... udata - } else { - uint32 length = info->readStream->readUint32LE(); - lua_newuserdata(info->luaState, length); - // >>>>> permTbl indexTbl ...... udata - registerObjectInIndexTable(info, index); - - info->readStream->read(lua_touserdata(info->luaState, -1), length); - - unserialize(info); - // >>>>> permTbl indexTbl ...... udata metaTable/nil - - lua_setmetatable(info->luaState, -2); - // >>>>> permTbl indexTbl ...... udata - } - // >>>>> permTbl indexTbl ...... udata -} - -void unserializePermanent(UnSerializationInfo *info, int index) { - // >>>>> permTbl indexTbl ...... - - // Make sure there is enough room on the stack - lua_checkstack(info->luaState, 2); - - unserialize(info); - // >>>>> permTbl indexTbl ...... permKey - - lua_gettable(info->luaState, 1); - // >>>>> permTbl indexTbl ...... perm -} - -} // End of namespace Lua -- cgit v1.2.3