diff options
Diffstat (limited to 'engines/sword25/util')
-rw-r--r-- | engines/sword25/util/double_serialization.cpp | 68 | ||||
-rw-r--r-- | engines/sword25/util/double_serialization.h | 61 | ||||
-rw-r--r-- | engines/sword25/util/lua_persist.cpp | 802 | ||||
-rw-r--r-- | engines/sword25/util/lua_persistence.h | 67 | ||||
-rw-r--r-- | engines/sword25/util/lua_persistence_util.cpp | 393 | ||||
-rw-r--r-- | engines/sword25/util/lua_persistence_util.h | 122 | ||||
-rw-r--r-- | engines/sword25/util/lua_unpersist.cpp | 722 | ||||
-rw-r--r-- | engines/sword25/util/pluto/CHANGELOG | 37 | ||||
-rw-r--r-- | engines/sword25/util/pluto/FILEFORMAT | 168 | ||||
-rw-r--r-- | engines/sword25/util/pluto/README | 133 | ||||
-rw-r--r-- | engines/sword25/util/pluto/THANKS | 9 | ||||
-rw-r--r-- | engines/sword25/util/pluto/pdep.cpp | 112 | ||||
-rw-r--r-- | engines/sword25/util/pluto/pdep/README | 5 | ||||
-rw-r--r-- | engines/sword25/util/pluto/pdep/lzio.h | 65 | ||||
-rw-r--r-- | engines/sword25/util/pluto/pdep/pdep.h | 42 | ||||
-rw-r--r-- | engines/sword25/util/pluto/pluto.cpp | 2083 | ||||
-rw-r--r-- | engines/sword25/util/pluto/pluto.h | 25 | ||||
-rw-r--r-- | engines/sword25/util/pluto/plzio.cpp | 74 |
18 files changed, 2235 insertions, 2753 deletions
diff --git a/engines/sword25/util/double_serialization.cpp b/engines/sword25/util/double_serialization.cpp new file mode 100644 index 0000000000..13fa42b6be --- /dev/null +++ b/engines/sword25/util/double_serialization.cpp @@ -0,0 +1,68 @@ +/* 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/double_serialization.h" + +#include "common/scummsys.h" + + +namespace Util { + +SerializedDouble encodeDouble(double value) { + // Split the value into its significand and exponent + int exponent; + double significand = frexp(value, &exponent); + + // Shift the the first part of the significand into the integer range + double shiftedsignificandPart = ldexp(fabs(significand), 32); + uint32 significandOne = uint32(floor(shiftedsignificandPart)); + + // Shift the remainder of the significand into the integer range + shiftedsignificandPart -= significandOne; + uint32 significandTwo = (uint32)(ldexp(shiftedsignificandPart, 31)); + + SerializedDouble returnValue; + returnValue.significandOne = significandOne; // SignificandOne + returnValue.signAndSignificandTwo = ((uint32)(value < 0 ? 1 : 0) << 31) | // Sign + significandTwo; // SignificandTwo + returnValue.exponent = (int16)exponent; + return returnValue; +} + +double decodeDouble(SerializedDouble value) { + // Expand the exponent and the parts of the significand + int exponent = (int)value.exponent; + double expandedsignificandOne = (double)value.significandOne; + double expandedsignificandTwo = (double)(value.signAndSignificandTwo & 0x7FFFFFFF); + + // Deflate the significand + double shiftedsignificand = ldexp(expandedsignificandTwo, -21); + double significand = ldexp(expandedsignificandOne + shiftedsignificand, -32); + + // Re-calculate the actual double + double returnValue = ldexp(significand, exponent); + + // Check the sign bit and return + return ((value.signAndSignificandTwo & 0x80000000) == 0x80000000) ? -returnValue : returnValue; +} + +} // End of namespace Sword25 diff --git a/engines/sword25/util/double_serialization.h b/engines/sword25/util/double_serialization.h new file mode 100644 index 0000000000..af58d03c17 --- /dev/null +++ b/engines/sword25/util/double_serialization.h @@ -0,0 +1,61 @@ +/* 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 DOUBLE_SERIALIZATION_H +#define DOUBLE_SERIALIZATION_H + +#include "common/types.h" + + +namespace Util { + +struct SerializedDouble { + uint32 significandOne; + uint32 signAndSignificandTwo; + int16 exponent; +}; + +struct CompactSerializedDouble { + uint32 signAndSignificandOne; + uint32 exponentAndSignificandTwo; +}; + +/** + * Encodes a double as two uint32 and a one int16 + * + * Supports denormalized numbers. Does NOT support NaN, or Inf + * + * @param value The value to encode + * @return The encoded value + */ +SerializedDouble encodeDouble(double value); +/** + * Decodes a previously encoded double + * + * @param value The value to decode + * @return The decoded value + */ +double decodeDouble(SerializedDouble value); + +} // End of namespace Sword25 + +#endif diff --git a/engines/sword25/util/lua_persist.cpp b/engines/sword25/util/lua_persist.cpp new file mode 100644 index 0000000000..03f305b2c5 --- /dev/null +++ b/engines/sword25/util/lua_persist.cpp @@ -0,0 +1,802 @@ +/* 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. + * + */ + +/** + * This code is heavily based on the Pluto code base. Copyright below + */ + +/* Tamed Pluto - Heavy-duty persistence for Lua + * Copyright (C) 2004 by Ben Sunshine-Hill, and released into the public + * domain. People making use of this software as part of an application + * are politely requested to email the author at sneftel@gmail.com + * with a brief description of the application, primarily to satisfy his + * curiosity. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, + * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF + * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. + * IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY + * CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, + * TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE + * SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + * + * Instrumented by Stefan Reich (info@luaos.net) + * for Mobile Lua (http://luaos.net/pages/mobile-lua.php) + */ + + +#include "sword25/util/lua_persistence.h" + +#include "sword25/util/double_serialization.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 persist(SerializationInfo *info); + +static void persistBoolean(SerializationInfo *info); +static void persistNumber(SerializationInfo *info); +static void persistString(SerializationInfo *info); +static void persistTable(SerializationInfo *info); +static void persistFunction(SerializationInfo *info); +static void persistThread(SerializationInfo *info); +static void persistProto(SerializationInfo *info); +static void persistUpValue(SerializationInfo *info); +static void persistUserData(SerializationInfo *info); + + +void persistLua(lua_State *luaState, Common::WriteStream *writeStream) { + SerializationInfo info; + info.luaState = luaState; + info.writeStream = writeStream; + info.counter = 1u; + + // 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 + persist(&info); + + // Return the stack back to the original state + lua_remove(luaState, 2); + // >>>>> permTbl rootObj +} + +static void persist(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 index/nil off the stack + lua_pop(info->luaState, 1); + + // If the obj itself is nil, we represent it as an index of 0 + if (lua_isnil(info->luaState, -1)) { + // Write out a flag that indicates that it's an index + info->writeStream->writeByte(0); + // Write out the index + info->writeStream->writeUint32LE(0); + + return; + } + + // Write out a flag that indicates that this is a real object + info->writeStream->writeByte(1); + + // 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 + persist(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: + persistBoolean(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: + persistNumber(info); + break; + case LUA_TSTRING: + persistString(info); + break; + case LUA_TTABLE: + persistTable(info); + break; + case LUA_TFUNCTION: + persistFunction(info); + break; + case LUA_TTHREAD: + persistThread(info); + break; + case LUA_TPROTO: + persistProto(info); + break; + case LUA_TUPVAL: + persistUpValue(info); + break; + case LUA_TUSERDATA: + persistUserData(info); + break; + default: + assert(0); + } +} + +static void persistBoolean(SerializationInfo *info) { + int value = lua_toboolean(info->luaState, -1); + + info->writeStream->writeSint32LE(value); +} + +static void persistNumber(SerializationInfo *info) { + lua_Number value = lua_tonumber(info->luaState, -1); + + Util::SerializedDouble serializedValue(Util::encodeDouble(value)); + + info->writeStream->writeUint32LE(serializedValue.significandOne); + info->writeStream->writeUint32LE(serializedValue.signAndSignificandTwo); + info->writeStream->writeSint16LE(serializedValue.exponent); +} + +static void persistString(SerializationInfo *info) { + // Hard cast to a uint32 to force size_t to an explicit size + // *Theoretically* this could truncate, but if we have a 4gb string, we have bigger problems + uint32 length = static_cast<uint32>(lua_strlen(info->luaState, -1)); + info->writeStream->writeUint32LE(length); + + const char *str = lua_tostring(info->luaState, -1); + info->writeStream->write(str, length); +} + +/* Choose whether to do a regular or special persistence based on an object's + * metatable. "default" is whether the object, if it doesn't have a __persist + * entry, is literally persistable or not. + * Pushes the unpersist closure and returns true if special persistence is + * used. */ +static bool serializeSpecialObject(SerializationInfo *info, bool defaction) { + // Make sure there is enough room on the stack + lua_checkstack(info->luaState, 4); + + // Check whether we should persist literally, or via the __persist metafunction + if (!lua_getmetatable(info->luaState, -1)) { + if (defaction) { + // Write out a flag declaring that the 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 + persist(info); + + lua_pop(info->luaState, 2); + // >>>>> permTbl indexTbl ...... obj + + return true; +} + +static void persistTable(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 */ + persist(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 + persist(info); + + lua_pop(info->luaState, 1); + // >>>>> permTbl indexTbl ...... tbl k v */ + + // Serialize the value + persist(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 + + persist(info); + + lua_pop(info->luaState, 1); + // >>>>> permTbl indexTbl ...... tbl +} + +static void persistFunction(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 */ + + persist(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 + + persist(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 + persist(info); + + lua_pop(info->luaState, 1); + // >>>>> permTbl indexTbl ...... func + } +} + +static void persistThread(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<uint32>(appendStackToStack_reverse(threadState, info->luaState)); + info->writeStream->writeUint32LE(stackSize); + + // >>>>> permTbl indexTbl ...... thread (reversed contents of thread stack) */ + for (; stackSize > 0; --stackSize) { + persist(info); + + lua_pop(info->luaState, 1); + } + + // >>>>> permTbl indexTbl ...... thread + + // Now, serialize the CallInfo stack + + // Again, we *could* have truncation here, but if we have more than 4 billion items on a stack, we have bigger problems + uint32 numFrames = static_cast<uint32>((threadState->ci - threadState->base_ci) + 1); + info->writeStream->writeUint32LE(numFrames); + + for (uint32 i = 0; i < numFrames; i++) { + CallInfo *ci = threadState->base_ci + i; + + // Same argument as above about truncation + uint32 stackBase = static_cast<uint32>(ci->base - threadState->stack); + uint32 stackFunc = static_cast<uint32>(ci->func - threadState->stack); + uint32 stackTop = static_cast<uint32>(ci->top - threadState->stack); + + info->writeStream->writeUint32LE(stackBase); + info->writeStream->writeUint32LE(stackFunc); + info->writeStream->writeUint32LE(stackTop); + + info->writeStream->writeSint32LE(ci->nresults); + + uint32 savedpc = (ci != threadState->base_ci) ? static_cast<uint32>(ci->savedpc - ci_func(ci)->l.p->code) : 0u; + info->writeStream->writeUint32LE(savedpc); + } + + + // Serialize the state's other parameters, with the exception of upval stuff + + assert(threadState->nCcalls <= 1); + info->writeStream->writeByte(threadState->status); + + // Same argument as above about truncation + uint32 stackBase = static_cast<uint32>(threadState->base - threadState->stack); + uint32 stackFunc = static_cast<uint32>(threadState->top - threadState->stack); + info->writeStream->writeUint32LE(stackBase); + info->writeStream->writeUint32LE(stackFunc); + + // Same argument as above about truncation + uint32 stackOffset = static_cast<uint32>(threadState->errfunc); + info->writeStream->writeUint32LE(stackOffset); + + // Finally, record upvalues which need to be reopened + // See the comment above serializeUpVal() for why we do this + + UpVal *upVal; + + // >>>>> permTbl indexTbl ...... thread + for (GCObject *gcObject = threadState->openupval; gcObject != NULL; gcObject = upVal->next) { + upVal = gco2uv(gcObject); + + /* Make sure upvalue is really open */ + assert(upVal->v != &upVal->u.value); + + pushUpValue(info->luaState, upVal); + // >>>>> permTbl indexTbl ...... thread upVal + + persist(info); + + lua_pop(info->luaState, 1); + // >>>>> permTbl indexTbl ...... thread + + // Same argument as above about truncation + uint32 stackpos = static_cast<uint32>(upVal->v - threadState->stack); + info->writeStream->writeUint32LE(stackpos); + } + + // >>>>> permTbl indexTbl ...... thread + lua_pushnil(info->luaState); + // >>>>> permTbl indexTbl ...... thread nil + + // Use nil as a terminator + persist(info); + + lua_pop(info->luaState, 1); + // >>>>> permTbl indexTbl ...... thread +} + +static void persistProto(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 + + persist(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 */ + + persist(info); + + lua_pop(info->luaState, 1); + // >>>>> permTbl indexTbl ...... proto + } + + // >>>>> permTbl indexTbl ...... proto + + // Serialize the code + info->writeStream->writeSint32LE(proto->sizecode); + + uint32 len = static_cast<uint32>(sizeof(Instruction) * proto->sizecode); + info->writeStream->write(proto->code, len); + + + // Serialize upvalue names + info->writeStream->writeSint32LE(proto->sizeupvalues); + + for (int i = 0; i < proto->sizeupvalues; ++i) { + pushString(info->luaState, proto->upvalues[i]); + // >>>>> permTbl indexTbl ...... proto str + + persist(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 + + persist(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 + + persist(info); + + lua_pop(info->luaState, 1); + // >>>>> permTbl indexTbl ...... proto + + // Serialize line numbers + info->writeStream->writeSint32LE(proto->sizelineinfo); + + if (proto->sizelineinfo) { + len = static_cast<uint32>(sizeof(int) * proto->sizelineinfo); + info->writeStream->write(proto->lineinfo, len); + } + + // Serialize linedefined and lastlinedefined + info->writeStream->writeSint32LE(proto->linedefined); + info->writeStream->writeSint32LE(proto->lastlinedefined); + + + // Serialize misc values + info->writeStream->writeByte(proto->nups); + info->writeStream->writeByte(proto->numparams); + info->writeStream->writeByte(proto->is_vararg); + info->writeStream->writeByte(proto->maxstacksize); +} + +/* Upvalues are tricky. Here's why. + * + * A particular upvalue may be either "open", in which case its member v + * points into a thread's stack, or "closed" in which case it points to the + * upvalue itself. An upvalue is closed under any of the following conditions: + * -- The function that initially declared the variable "local" returns + * -- The thread in which the closure was created is garbage collected + * + * To make things wackier, just because a thread is reachable by Lua doesn't + * mean it's in our root set. We need to be able to treat an open upvalue + * from an unreachable thread as a closed upvalue. + * + * The solution: + * (a) For the purposes of serializing, don't indicate whether an upvalue is + * closed or not. + * (b) When unserializing, pretend that all upvalues are closed. + * (c) When serializing, persist all open upvalues referenced by a thread + * that is persisted, and tag each one with the corresponding stack position + * (d) When unserializing, "reopen" each of these upvalues as the thread is + * unserialized + */ +static void persistUpValue(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 + + persist(info); + // >>>>> permTbl indexTbl ...... obj +} + +static void persistUserData(SerializationInfo *info) { + // >>>>> permTbl rootObj ...... udata + + // Make sure there is enough room on the stack + lua_checkstack(info->luaState, 2); + + // Test if the object needs special serialization + if (serializeSpecialObject(info, 0)) { + return; + } + + // Use literal persistence + + // Hard cast to a uint32 length + // This could lead to truncation, but if we have a 4gb block of data, we have bigger problems + uint32 length = static_cast<uint32>(uvalue(getObject(info->luaState, -1))->len); + info->writeStream->writeUint32LE(length); + + info->writeStream->write(lua_touserdata(info->luaState, -1), length); + + // Serialize the metatable (if any) + if (!lua_getmetatable(info->luaState, -1)) { + lua_pushnil(info->luaState); + } + + // >>>>> permTbl rootObj ...... udata metaTbl/nil + persist(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..53e3dee02e --- /dev/null +++ b/engines/sword25/util/lua_persistence.h @@ -0,0 +1,67 @@ +/* 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. + * + */ + +/** + * This code is heavily based on the Pluto code base. Copyright below + */ + +/* Tamed Pluto - Heavy-duty persistence for Lua + * Copyright (C) 2004 by Ben Sunshine-Hill, and released into the public + * domain. People making use of this software as part of an application + * are politely requested to email the author at sneftel@gmail.com + * with a brief description of the application, primarily to satisfy his + * curiosity. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, + * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF + * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. + * IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY + * CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, + * TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE + * SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + * + * Instrumented by Stefan Reich (info@luaos.net) + * for Mobile Lua (http://luaos.net/pages/mobile-lua.php) + */ + +#ifndef LUA_PERSISTENCE_H +#define LUA_PERSISTENCE_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..958fb7ac3c --- /dev/null +++ b/engines/sword25/util/lua_persistence_util.cpp @@ -0,0 +1,393 @@ +/* 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. + * + */ + +/** + * This code is heavily based on the pluto code base. Copyright below + */ + +/* Tamed Pluto - Heavy-duty persistence for Lua + * Copyright (C) 2004 by Ben Sunshine-Hill, and released into the public + * domain. People making use of this software as part of an application + * are politely requested to email the author at sneftel@gmail.com + * with a brief description of the application, primarily to satisfy his + * curiosity. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, + * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF + * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. + * IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY + * CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, + * TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE + * SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + * + * Instrumented by Stefan Reich (info@luaos.net) + * for Mobile Lua (http://luaos.net/pages/mobile-lua.php) + */ + +/** + * This code is heavily based on the Pluto code base. Copyright below + */ + +/* Tamed Pluto - Heavy-duty persistence for Lua + * Copyright (C) 2004 by Ben Sunshine-Hill, and released into the public + * domain. People making use of this software as part of an application + * are politely requested to email the author at sneftel@gmail.com + * with a brief description of the application, primarily to satisfy his + * curiosity. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, + * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF + * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. + * IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY + * CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, + * TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE + * SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + * + * Instrumented by Stefan Reich (info@luaos.net) + * for Mobile Lua (http://luaos.net/pages/mobile-lua.php) + */ + + +#include "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..4d0085e242 --- /dev/null +++ b/engines/sword25/util/lua_persistence_util.h @@ -0,0 +1,122 @@ +/* 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. + * + */ + +/** + * This code is heavily based on the Pluto code base. Copyright below + */ + +/* Tamed Pluto - Heavy-duty persistence for Lua + * Copyright (C) 2004 by Ben Sunshine-Hill, and released into the public + * domain. People making use of this software as part of an application + * are politely requested to email the author at sneftel@gmail.com + * with a brief description of the application, primarily to satisfy his + * curiosity. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, + * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF + * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. + * IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY + * CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, + * TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE + * SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + * + * Instrumented by Stefan Reich (info@luaos.net) + * for Mobile Lua (http://luaos.net/pages/mobile-lua.php) + */ + + +#ifndef LUA_PERISTENCE_UTIL_H +#define LUA_PERISTENCE_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_unpersist.cpp b/engines/sword25/util/lua_unpersist.cpp new file mode 100644 index 0000000000..ef0ef31041 --- /dev/null +++ b/engines/sword25/util/lua_unpersist.cpp @@ -0,0 +1,722 @@ +/* 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. + * + */ + +/** + * This code is heavily based on the Pluto code base. Copyright below + */ + +/* Tamed Pluto - Heavy-duty persistence for Lua + * Copyright (C) 2004 by Ben Sunshine-Hill, and released into the public + * domain. People making use of this software as part of an application + * are politely requested to email the author at sneftel@gmail.com + * with a brief description of the application, primarily to satisfy his + * curiosity. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, + * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF + * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. + * IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY + * CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, + * TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE + * SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + * + * Instrumented by Stefan Reich (info@luaos.net) + * for Mobile Lua (http://luaos.net/pages/mobile-lua.php) + */ + + +#include "sword25/util/lua_persistence.h" + +#include "sword25/util/double_serialization.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 unpersist(UnSerializationInfo *info); + +static void unpersistBoolean(UnSerializationInfo *info); +static void unpersistNumber(UnSerializationInfo *info); +static void unpersistString(UnSerializationInfo *info); +static void unpersistTable(UnSerializationInfo *info, int index); +static void unpersistFunction(UnSerializationInfo *info, int index); +static void unpersistThread(UnSerializationInfo *info, int index); +static void unpersistProto(UnSerializationInfo *info, int index); +static void unpersistUpValue(UnSerializationInfo *info, int index); +static void unpersistUserData(UnSerializationInfo *info, int index); +static void unpersistPermanent(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 + unpersist(&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 unpersist(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: + unpersistBoolean(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: + unpersistNumber(info); + break; + case LUA_TSTRING: + unpersistString(info); + break; + case LUA_TTABLE: + unpersistTable(info, index); + break; + case LUA_TFUNCTION: + unpersistFunction(info, index); + break; + case LUA_TTHREAD: + unpersistThread(info, index); + break; + case LUA_TPROTO: + unpersistProto(info, index); + break; + case LUA_TUPVAL: + unpersistUpValue(info, index); + break; + case LUA_TUSERDATA: + unpersistUserData(info, index); + break; + case PERMANENT_TYPE: + unpersistPermanent(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 unpersistBoolean(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 unpersistNumber(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 unpersistString(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); + + unpersist(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 + unpersist(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 + unpersist(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 + unpersist(info); + // >>>>> permTbl indexTbl ...... tbl value + + lua_rawset(info->luaState, -3); + // >>>>> permTbl indexTbl ...... tbl + } +} + +void unpersistTable(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 unpersistFunction(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 + unpersist(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 + unpersist(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 + unpersist(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 unpersistThread(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) { + unpersist(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) { + unpersist(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 unpersistProto(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 + unpersist(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 + unpersist(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 + unpersist(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 + unpersist(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 + unpersist(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 unpersistUpValue(UnSerializationInfo *info, int index) { + // >>>>> permTbl indexTbl ...... + lua_checkstack(info->luaState, 2); + + boxUpValue_start(info->luaState); + // >>>>> permTbl indexTbl ...... func + registerObjectInIndexTable(info, index); + + unpersist(info); + // >>>>> permTbl indexTbl ...... func obj + + boxUpValue_finish(info->luaState); + // >>>>> permTbl indexTbl ...... func +} + +void unpersistUserData(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) { + unpersist(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); + + unpersist(info); + // >>>>> permTbl indexTbl ...... udata metaTable/nil + + lua_setmetatable(info->luaState, -2); + // >>>>> permTbl indexTbl ...... udata + } + // >>>>> permTbl indexTbl ...... udata +} + +void unpersistPermanent(UnSerializationInfo *info, int index) { + // >>>>> permTbl indexTbl ...... + + // Make sure there is enough room on the stack + lua_checkstack(info->luaState, 2); + + unpersist(info); + // >>>>> permTbl indexTbl ...... permKey + + lua_gettable(info->luaState, 1); + // >>>>> permTbl indexTbl ...... perm +} + +} // End of namespace Lua diff --git a/engines/sword25/util/pluto/CHANGELOG b/engines/sword25/util/pluto/CHANGELOG deleted file mode 100644 index 1be321f898..0000000000 --- a/engines/sword25/util/pluto/CHANGELOG +++ /dev/null @@ -1,37 +0,0 @@ -$Id$ - --- 2.4 -- -* Changed upval unboxing to allow upvals which contain func-housed cycles -* Added stack checking to all stack-growing functions -* Serialized debug information for functions - --- 2.3 -- -* Added LUALIB_API declaration for luaopen_pluto - --- 2.2 -- -* Rolled all internal Lua dependencies into the Pluto distribution -* Made the unit tests depend on dynamically loading Pluto - --- 2.1 -- -* Various fixes to make the GC happy -* stack size always expanded where necessary -* fixed some memory leaks -* GC disabled during unpersist -* callstack initialized for traversal - -This changelog is maintained as of version 2.0alpha1. -Earlier versions are changelogged on the LuaForge site. - --- 2.0 -- -* Fixed a few format changes to 5.1.3 -* Fixed myriad warnings -* GCC compliance: not incrementing cast results -* Fix for self-referring upvals -* Renamed loading function to work with Lua module system -* Loading tables with __newindex works -* unpersist makes buffer copy - --- 2.0alpha1 -- -* Fixed all outstanding 5.0->5.1 conversion issues -* Made heavier use of size_t in preference to int -* Fixed GC/Upval issue (thanks to Eric Jacobs) diff --git a/engines/sword25/util/pluto/FILEFORMAT b/engines/sword25/util/pluto/FILEFORMAT deleted file mode 100644 index e7716675c7..0000000000 --- a/engines/sword25/util/pluto/FILEFORMAT +++ /dev/null @@ -1,168 +0,0 @@ -$Id$ - -pluto_persist() produces a "hunk" of objects. Here's the file format adhered -to by the function, and expected by pluto_unpersist(). - -As a developer, I feel that where file format information is given it is of -utmost importance that that information precisely and accurately reflects the -actual operation of the application. Therefore, if you find any discrepancy -between this and actual operation, please lambast me thoroughly over email. - -Pseudo-C is used to express the file format. Padding is assumed to be -nonexistent. The keyword "one_of" is used to express a concept similar to -"union", except that its size is the size of the actual datatype chosen. Thus, -objects which contain, directly or indirectly, a one_of, may vary in size. - - -struct Object { - int firstTime; /* Whether this is the first time the object - is being referenced */ - one_of { - RealObject o; /* if firstTime == 1 */ - Reference r; /* if firstTime == 0 */ - }; -}; - -struct Reference { - int ref; /* The index the object was registered with */ -}; - -struct RealObject { - int type; /* The type of the object */ - one_of { - Boolean b; /* If type == LUA_TBOOLEAN */ - LightUserData l; /* If type == LUA_TLIGHTUSERDATA */ - Number n; /* If type == LUA_TNUMBER */ - String s; /* If type == LUA_TSTRING */ - Table t; /* If type == LUA_TTABLE */ - Function f; /* If type == LUA_TFUNCTION */ - Userdata u; /* If type == LUA_TUSERDATA */ - Thread th; /* If type == LUA_TTHREAD */ - Proto p; /* If type == LUA_TPROTO (from lobject.h) */ - Upval uv; /* If type == LUA_TUPVAL (from lobject.h) */ - }; /* The actual object */ -}; - -struct Boolean { - int32 bvalue; /* 0 for false, 1 for true */ -}; - -struct LightUserData { - void* luvalue; /* The actual, literal pointer */ -}; - -struct Number { - lua_Number nvalue; /* The actual number */ -}; - -struct String { - int length; /* The length of the string */ - char str[length]; /* The actual string (not null terminated) */ -}; - -struct Table { - int isspecial; /* 1 if SP is used; 0 otherwise */ - one_of { - Closure c; /* if isspecial == 1; closure to refill the table */ - LiteralTable t; /* if isspecial == 0; literal table info */ - }; -}; - -struct LiteralTable { - Object metatable; /* nil for default metatable */ - Pair p[]; /* key/value pairs */ - Object nil = nil; /* Nil reference to terminate */ -}; - -struct Pair { - Object key; - Object value; -}; - -struct Function { /* Actually a closure */ - lu_byte nups; /* Number of upvalues the function uses */ - Object proto; /* The proto this function uses */ - Object upvals[nups]; /* All upvalues */ - Object fenv; /* The FEnv (nil for the global table) -}; - -struct Upval { - Object obj; /* The object this upval refers to */ -} - -struct Userdata { - int isSpecial; /* 1 for special persistence, 0 for literal - one_of { - LiteralUserdata lu; /* if is_special is 0 */ - SpecialUserdata su; /* if is_special is 1 */ - }; -}; - -struct LiteralUserdata { - Object metatable; /* The metatable (nil for default) */ - int length; /* Size of the data */ - char data[length]; /* The actual data */ -}; - -struct SpecialUserdata { - int length; /* The size of the data */ - Object func; /* The closure used to fill the userdata */ -}; - -struct Thread { - int stacksize; /* The size of the stack filled with objects, - * including the "nil" that is hidden below - * the bottom of the stack visible to C */ - Object stack[stacksize];/* Indices of all stack values, bottom up */ - int callinfosize; /* Number of elements in the CallInfo stack */ - CallInfo callinfostack[callinfosize]; /* The CallInfo stack */ - int base; /* base = L->base - L->stack; */ - int top; /* top = L->top - L->stack; */ - OpenUpval openupvals[]; /* Upvalues to open */ - Object nil = nil; /* To terminate the open upvalues list */ -}; - -struct OpenUpval { - Object upval; /* The upvalue */ - int stackpos; /* The stack position to "reopen" it to */ - -}; - -struct CallInfo { - int base; /* base = ci->base - L->stack; */ - int top; /* top = ci->top - L->stack; */ - int pc; /* pc = ci->pc - proto->code; */ - int state; /* flags used by the CallInfo */ -}; - -struct Proto { - int sizek; /* Number of constants referenced */ - Object k[sizek]; /* Constants referenced */ - int sizep; /* Number of inner Protos referenced */ - Object p[sizep]; /* Inner Protos referenced */ - int sizecode; /* Number of instructions in code */ - Instruction code[sizecode]; /* The proto's code */ - ProtoDebug debuginfo; /* Debug information for the proto */ - lu_byte nups; /* Number of upvalues used */ - lu_byte numparams; /* Number of parameters taken */ - lu_byte is_vararg; /* 1 if function accepts varargs, 0 otherwise */ - lu_byte maxstacksize; /* Size of stack reserved for the function */ -}; - -struct ProtoDebug { - int sizeupvals; /* Number of upvalue names */ - Object upvals; /* Upvalue names */ - int sizelocvars; /* Number of local variable names */ - LocVar[sizelocvars]; /* Local variable names */ - Object source; /* The source code */ - int sizelineinfo; /* Number of opcode-line mappings */ - int lineinfo[sizelineinfo]; /* opcode-line mappings */ - int linedefined; /* Start of line range */ - int lastlinedefined; /* End of line range */ -}; - -struct LocVar { - Object name; /* Name of the local variable */ - int startpc; /* Point where variable is active */ - int endpc; /* Point where variable is dead */ -}; diff --git a/engines/sword25/util/pluto/README b/engines/sword25/util/pluto/README deleted file mode 100644 index 838fce498b..0000000000 --- a/engines/sword25/util/pluto/README +++ /dev/null @@ -1,133 +0,0 @@ -$Id$ - -PLUTO - Heavy duty persistence for Lua - -Pluto is a library which allows users to write arbitrarily large portions -of the "Lua universe" into a flat file, and later read them back into the -same or a different Lua universe. Object references are appropriately -handled, such that the file contains everything needed to recreate the -objects in question. - -Pluto has the following major features: -* Can persist any Lua function -* Can persist threads -* Works with any Lua chunkreader/chunkwriter -* Support for "invariant" permanent objects, of all datatypes -* Can invoke metafunctions for custom persistence of tables and userdata - -Pluto 2.2 requires Lua 5.1.3. If you need to use Pluto with Lua -5.0, please use version 1.2 of Pluto. - -Starting with version 2.2, Pluto no longer depends on the Lua sources. -Instead, it subsumes the required headers into its own codebase. -As a result, it may not work properly with Lua version 5.1.4 or later. - -Pluto may have bugs. Users are advised to define lua_assert in -luaconf.h to something useful when compiling in debug mode, to catch -assertions by Pluto and Lua. - -The Pluto library consists of two public functions. - -int pluto_persist(lua_State *L, lua_Chunkwriter writer, void *ud) - -This function recursively persists the Lua object in stack position 2 -and all other objects which are directly or indirectly referenced by -it, except those referenced in the permanent object table. The data -is written using the chunk-writer given, and that writer is passed -the arbitrary pointer value ud. - -The Lua stack must contain exactly and only these two items, in order: - -1. A table of permanent objects, that should not be persisted. For each -permanent object, the object itself should be the key, and a unique -object of any type should be the value. Likely candidates for this table -include Lua functions (including those in the Lua libraries) that are -loaded at load-time. It must include all non-persistable objects that -are referenced by the object to be persisted. The table is not modified -by the function. Objects in this table are considered "opaque" and are -not examined or descended into. Objects should not appear in the table -multiple times; the result of doing this is undefined (though probably -harmless). NOTE: If you are planning to persist threads, keep in mind -that all yielded threads have coroutine.yield on the tops of their -stacks. Since it's a C function, it should be put here. For complex -permanents, it may be a good idea to use the __index meta-function of -the permanents table to "search" for permanents. - -2. The single object to be persisted. In many cases, this will be the -global table. For more flexibility, however, it may be something like a -table built for the occasion, with various values to keep track of. The -object may not be nil. - - -int pluto_unpersist(lua_State *L, lua_Chunkreader reader, void *ud) - -This function loads in a Lua object and places it on top of the stack. All -objects directly or indirectly referenced by it are also loaded. - -The Lua stack must contain, as its top value, a table of permanent -objects. This table should be like the permanent object table used when -persisting, but with the key and value of each pair reversed. These -objects are used as substitutes for those referenced in their positions -when persisting, and under most circumstances should be identical objects -to those referenced in the permanents table used for persisting. It's -okay for multiple keys to refer to the same object. - - -RUNNING PLUTO FROM LUA: -It is also possible to invoke pluto from a Lua script. The C function -pluto_open() will register pluto.persist and pluto.unpersist, lua functions -which operate on strings. The first takes a permanents table and a root -object, and returns a string; the second takes a permanents table and a -string, and returns the root object. - -An error will be raised if pluto.persist is called from a thread which is -itself referenced by the root object. - -SPECIAL PERSISTENCE: -Tables and userdata have special persistence semantics. These semantics are -keyed to the value of the object's metatable's __persist member, if any. This -member may be any of the following four values: -1. Boolean "true": The table or userdata is persisted literally; tables are -persisted member-by-member, and userdata are written out as literal data. -2. Boolean "false": An error is returned, indicating that the object cannot -be persisted. -3. A function: This function should take one argument, the object in question, -and return one result, a closure. This "fixup closure", in turn, will be -persisted, and during unpersistence will be called. The closure will be -responsible for recreating the object with the appropriate data, based on -its upvalues. -4. Nil, or no metatable. In the case of tables, the table is literally -persisted. In the case of userdata, an error is returned. - -Here's an example of special persistence for a simple 3d vector object: - -vec = { x = 2, y = 1, z = 4 } -setmetatable(vec, { __persist = function(oldtbl) - local x = oldtbl.x - local y = oldtbl.y - local z = oldtbl.z - local mt = getmetatable(oldtbl) - return function() - newtbl = {} - newtbl.x = x - newtbl.y = y - newtbl.z = z - setmetatable(newtbl, mt) - return newtbl - end -end }) - -Note how x, y, z, and the mt are explicitly pulled out of the table. It is -important that the fixup closure returned not reference the original table -directly, as that table would again be persisted as an upvalue, leading to an -infinite loop. Also note that the object's metatable is NOT automatically -persisted; it is necessary for the fixup closure to reset it, if it wants. - -LIMITATIONS/TODO: -* Light userdata are persisted literally, as their pointer values. This -may or may not be what you want. -* Closures of C functions may not be persisted. Once it becomes possible -to specify a C function "proto" as a permanent object, this restriction -will be relaxed. - -BUGS: None known. Emphasis on the 'known'. diff --git a/engines/sword25/util/pluto/THANKS b/engines/sword25/util/pluto/THANKS deleted file mode 100644 index 443713fa61..0000000000 --- a/engines/sword25/util/pluto/THANKS +++ /dev/null @@ -1,9 +0,0 @@ -Pluto is surprisingly robust and useful. This would not be the case without -the hard work and helpfulness of the following people, mentioned in no -particular order: - -Ivko Stanilov -Goran Adrinek -Eric Jacobs -Anolan Milanes -Malte Thiesen diff --git a/engines/sword25/util/pluto/pdep.cpp b/engines/sword25/util/pluto/pdep.cpp deleted file mode 100644 index a32c43b42d..0000000000 --- a/engines/sword25/util/pluto/pdep.cpp +++ /dev/null @@ -1,112 +0,0 @@ -/* This file is derived from the Lua source code. Please see lua.h for -the copyright statement. -*/ - -#include "pdep/pdep.h" - -#define api_incr_top(L) {api_check(L, L->top < L->ci->top); L->top++;} - -void pdep_pushobject (lua_State *L, const TValue *o) { - setobj2s(L, L->top, o); - api_incr_top(L); -} - -void *pdep_realloc_ (lua_State *L, void *block, size_t osize, size_t nsize) { - global_State *g = G(L); - lua_assert((osize == 0) == (block == NULL)); - block = (*g->frealloc)(g->ud, block, osize, nsize); - lua_assert((nsize == 0) == (block == NULL)); - g->totalbytes = (g->totalbytes - osize) + nsize; - return block; -} - -void pdep_link (lua_State *L, GCObject *o, lu_byte tt) { - global_State *g = G(L); - o->gch.next = g->rootgc; - g->rootgc = o; - o->gch.marked = luaC_white(g); - o->gch.tt = tt; -} - -Proto *pdep_newproto (lua_State *L) { - Proto *f = pdep_new(L, Proto); - pdep_link(L, 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; -} - -Closure *pdep_newLclosure (lua_State *L, int nelems, Table *e) { - Closure *c = cast(Closure *, pdep_malloc(L, sizeLclosure(nelems))); - pdep_link(L, obj2gco(c), LUA_TFUNCTION); - c->l.isC = 0; - c->l.env = e; - c->l.nupvalues = cast_byte(nelems); - while (nelems--) c->l.upvals[nelems] = NULL; - return c; -} - -static 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 pdep_reallocstack (lua_State *L, int newsize) { - TValue *oldstack = L->stack; - int realsize = newsize + 1 + EXTRA_STACK; - lua_assert(L->stack_last - L->stack == L->stacksize - EXTRA_STACK - 1); - pdep_reallocvector(L, L->stack, L->stacksize, realsize, TValue); - L->stacksize = realsize; - L->stack_last = L->stack+newsize; - correctstack(L, oldstack); -} - -void pdep_growstack (lua_State *L, int n) { - if (n <= L->stacksize) /* double size is enough? */ - pdep_reallocstack(L, 2*L->stacksize); - else - pdep_reallocstack(L, L->stacksize + n); -} - -void pdep_reallocCI (lua_State *L, int newsize) { - CallInfo *oldci = L->base_ci; - pdep_reallocvector(L, L->base_ci, L->size_ci, newsize, CallInfo); - L->size_ci = newsize; - L->ci = (L->ci - oldci) + L->base_ci; - L->end_ci = L->base_ci + L->size_ci - 1; -} - -TString *pdep_newlstr (lua_State *L, const char *str, size_t l) { - TString *res; - lua_pushlstring(L, str, l); - res = rawtsvalue(L->top-1); - lua_pop(L, 1); - return res; -} diff --git a/engines/sword25/util/pluto/pdep/README b/engines/sword25/util/pluto/pdep/README deleted file mode 100644 index 3592754da0..0000000000 --- a/engines/sword25/util/pluto/pdep/README +++ /dev/null @@ -1,5 +0,0 @@ -These files are directly copied from the Lua distribution, with the -exception of lzio.h, which is s/lua{ZM}/pdep/g and has an include removed. - -As such, unlike the rest of Pluto, they are released under the -same terms as Lua. See "lua.h" for the copyright notice. diff --git a/engines/sword25/util/pluto/pdep/lzio.h b/engines/sword25/util/pluto/pdep/lzio.h deleted file mode 100644 index 2e37f8d202..0000000000 --- a/engines/sword25/util/pluto/pdep/lzio.h +++ /dev/null @@ -1,65 +0,0 @@ -/* -** $Id$ -** Buffered streams -** See Copyright Notice in lua.h -*/ - - -#ifndef lzio_h -#define lzio_h - -#include "sword25/util/lua/lua.h" - - -#define EOZ (-1) /* end of stream */ - -typedef struct Zio ZIO; - -#define char2int(c) cast(int, cast(unsigned char, (c))) - -#define zgetc(z) (((z)->n--)>0 ? char2int(*(z)->p++) : pdep_fill(z)) - -typedef struct Mbuffer { - char *buffer; - size_t n; - size_t buffsize; -} Mbuffer; - -#define pdep_initbuffer(L, buff) ((buff)->buffer = NULL, (buff)->buffsize = 0) - -#define pdep_buffer(buff) ((buff)->buffer) -#define pdep_sizebuffer(buff) ((buff)->buffsize) -#define pdep_bufflen(buff) ((buff)->n) - -#define pdep_resetbuffer(buff) ((buff)->n = 0) - - -#define pdep_resizebuffer(L, buff, size) \ - (pdep_reallocvector(L, (buff)->buffer, (buff)->buffsize, size, char), \ - (buff)->buffsize = size) - -#define pdep_freebuffer(L, buff) pdep_resizebuffer(L, buff, 0) - - -LUAI_FUNC char *pdep_openspace (lua_State *L, Mbuffer *buff, size_t n); -LUAI_FUNC void pdep_init (lua_State *L, ZIO *z, lua_Reader reader, - void *data); -LUAI_FUNC size_t pdep_read (ZIO* z, void* b, size_t n); /* read next n bytes */ -LUAI_FUNC int pdep_lookahead (ZIO *z); - - - -/* --------- Private Part ------------------ */ - -struct Zio { - size_t n; /* bytes still unread */ - const char *p; /* current position in buffer */ - lua_Reader reader; - void* data; /* additional data */ - lua_State *L; /* Lua state (for reader) */ -}; - - -LUAI_FUNC int pdep_fill (ZIO *z); - -#endif diff --git a/engines/sword25/util/pluto/pdep/pdep.h b/engines/sword25/util/pluto/pdep/pdep.h deleted file mode 100644 index 664fc812b5..0000000000 --- a/engines/sword25/util/pluto/pdep/pdep.h +++ /dev/null @@ -1,42 +0,0 @@ -#ifndef PDEP_H -#define PDEP_H - -#include "sword25/util/lua/lua.h" -#include "sword25/util/pluto/pdep/lzio.h" -#include "sword25/util/lua/ldo.h" -#include "sword25/util/lua/lfunc.h" -#include "sword25/util/lua/lgc.h" -#include "sword25/util/lua/llimits.h" -#include "sword25/util/lua/lobject.h" -#include "sword25/util/lua/lopcodes.h" -#include "sword25/util/lua/lstate.h" -#include "sword25/util/lua/lstring.h" -#include "sword25/util/lua/lauxlib.h" - - -#define pdep_reallocv(L,b,on,n,e) \ - pdep_realloc_(L, (b), (on)*(e), (n)*(e)) -#define pdep_reallocvector(L, v,oldn,n,t) \ - ((v)=cast(t *, pdep_reallocv(L, v, oldn, n, sizeof(t)))) -#define pdep_freearray(L, b, n, t) pdep_reallocv(L, (b), n, 0, sizeof(t)) -#define pdep_newvector(L,n,t) \ - cast(t *, pdep_reallocv(L, NULL, 0, n, sizeof(t))) -#define pdep_new(L,t) cast(t *, pdep_malloc(L, sizeof(t))) -#define pdep_malloc(L,t) pdep_realloc_(L, NULL, 0, (t)) -#define pdep_checkstack(L,n) \ - if ((char *)L->stack_last - (char *)L->top <= (n)*(int)sizeof(TValue)) \ - pdep_growstack(L, n); \ - else pdep_reallocstack(L, L->stacksize - EXTRA_STACK - 1); - - -void pdep_pushobject (lua_State *L, const TValue *o); -void *pdep_realloc_ (lua_State *L, void *block, size_t osize, size_t nsize); -void pdep_link (lua_State *L, GCObject *o, lu_byte tt); -Proto *pdep_newproto (lua_State *L); -Closure *pdep_newLclosure (lua_State *L, int nelems, Table *e); -void pdep_reallocstack (lua_State *L, int newsize); -void pdep_growstack (lua_State *L, int n); -void pdep_reallocCI (lua_State *L, int newsize); -TString *pdep_newlstr (lua_State *L, const char *str, size_t l); - -#endif diff --git a/engines/sword25/util/pluto/pluto.cpp b/engines/sword25/util/pluto/pluto.cpp deleted file mode 100644 index cbe16b0d5b..0000000000 --- a/engines/sword25/util/pluto/pluto.cpp +++ /dev/null @@ -1,2083 +0,0 @@ -/* $Id$ */ - -/* Tamed Pluto - Heavy-duty persistence for Lua - * Copyright (C) 2004 by Ben Sunshine-Hill, and released into the public - * domain. People making use of this software as part of an application - * are politely requested to email the author at sneftel@gmail.com - * with a brief description of the application, primarily to satisfy his - * curiosity. - * - * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, - * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF - * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. - * IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY - * CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, - * TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE - * SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. - * - * Instrumented by Stefan Reich (info@luaos.net) - * for Mobile Lua (http://luaos.net/pages/mobile-lua.php) - */ - -#include "sword25/util/lua/lua.h" -#include "pluto.h" - -#undef TOTEXT - -#define USE_PDEP - -#ifdef USE_PDEP -#include "pdep/pdep.h" -#define LIF(prefix, name) pdep ## _ ## name -#else -#include "lapi.h" -#include "ldo.h" -#include "lfunc.h" -#include "lgc.h" -#include "llimits.h" -#include "lmem.h" -#include "lobject.h" -#include "lopcodes.h" -#include "lstate.h" -#include "lstring.h" -#include "lauxlib.h" -#define LIF(prefix, name) lua ## prefix ## _ ## name -#endif - -#include <string.h> - - -/* Define this if you want size_t values to be written in 64-bit - (even on 32-bit systems). Should eliminate at least one source of - 32/64 bit incompatibility. */ -#define SIZES64 - - -/* #define PLUTO_DEBUG */ - - -#ifdef SIZES64 -#define VERSION "Tamed Pluto 1.0 with SIZES64 flag" -#else -#define VERSION "Tamed Pluto 1.0" -#endif - - -#ifdef PLUTO_DEBUG -#include <stdio.h> -#endif - -#define PLUTO_TPERMANENT 101 - -#define verify(x) { int v = (int)((x)); v=v; lua_assert(v); } - -#define NUMTYPES 9 -static const char* typenames[] = { - "nil", - "boolean", - "lightuserdata", - "number", - "string", - "table", - "function", - "userdata", - "thread" -}; - -static int humanReadable = 0; -#define hrBufSize 200 -static char hrBuf[hrBufSize]; - -typedef struct PersistInfo_t { - lua_State *L; - int counter; - lua_Chunkwriter writer; - void *ud; -#ifdef PLUTO_DEBUG - int level; -#endif -} PersistInfo; - -#ifdef PLUTO_DEBUG -void printindent(int indent) -{ - int il; - for(il=0; il<indent; il++) { - printf(" "); - } -} -#endif - -/* lua_Chunkwriter signature: (lua_State *L, const void *p, size_t sz, void *ud). - ud is a pointer to the WriterInfo struct (holds the buffer pointer) -*/ - -static void pi_write(PersistInfo *pi, const void *p, size_t size, void *ud) { - if (humanReadable) { - uint i; - snprintf(hrBuf, hrBufSize, " pi_write %d ", (int) size); - pi->writer(pi->L, hrBuf, strlen(hrBuf), ud); - for (i = 0; i < size; i++) { - char b = ((char *)p)[i]; - snprintf(hrBuf, hrBufSize, "%X%X", (b >> 4) & 0xF, b & 0xF); - pi->writer(pi->L, hrBuf, strlen(hrBuf), ud); - } - snprintf(hrBuf, hrBufSize, "\n"); - pi->writer(pi->L, hrBuf, strlen(hrBuf), ud); - } else { - pi->writer(pi->L, p, size, ud); - } -#ifdef TOTEXT - int i; - printf(" pi_write %d ", (int) size); - for (i = 0; i < size; i++) { - char b = ((char *)p)[i]; - printf("%X%X", (b >> 4) & 0xF, b & 0xF); - } - printf("\n"); -#endif -} - -static void hrOut(PersistInfo *pi) { - pi->writer(pi->L, hrBuf, strlen(hrBuf), pi->ud); -} - -static void write_size(PersistInfo *pi, size_t *val) -{ -#ifdef SIZES64 - int64 longval; /* yeah, you really need long long to get 8 bytes on win32... duh. */ - longval = *val; - pi_write(pi, &longval, 8, pi->ud); - if (humanReadable) { - snprintf(hrBuf, hrBufSize, "write_size64 %ld\n", longval); - hrOut(pi); - } -#ifdef TOTEXT - printf("write_size64 %ld\n", longval); -#endif -#else - pi_write(pi, val, sizeof(size_t), pi->ud); - if (humanReadable) { - snprintf(hrBuf, hrBufSize, "write_size %ld\n", *((size_t *)val)); - hrOut(pi); - } -#ifdef TOTEXT - printf("write_size %ld\n", *val); -#endif -#endif -} - -static void read_size(ZIO *zio, size_t *val) -{ -#ifdef SIZES64 - int64 longval; - verify(LIF(Z,read)(zio, &longval, 8) == 0); - *val = longval; -#else - verify(LIF(Z,read)(zio, val, sizeof(size_t)) == 0); -#endif -} - - -/* Mutual recursion requires prototype */ -static void persist(PersistInfo *pi); - -/* A simple reimplementation of the unfortunately static function luaA_index. - * Does not support the global table, registry, or upvalues. */ -static StkId getobject(lua_State *L, int stackpos) -{ - if(stackpos > 0) { - lua_assert(L->base+stackpos-1 < L->top); - return L->base+stackpos-1; - } else { - lua_assert(L->top-stackpos >= L->base); - return L->top+stackpos; - } -} - -/* Choose whether to do a regular or special persistence based on an object's - * metatable. "default" is whether the object, if it doesn't have a __persist - * entry, is literally persistable or not. - * Pushes the unpersist closure and returns true if special persistence is - * used. */ -static int persistspecialobject(PersistInfo *pi, int defaction) -{ - /* perms reftbl ... obj */ - lua_checkstack(pi->L, 4); - /* Check whether we should persist literally, or via the __persist - * metafunction */ - if(!lua_getmetatable(pi->L, -1)) { - if(defaction) { - { - int zero = 0; - pi_write(pi, &zero, sizeof(int), pi->ud); - if (humanReadable) { - snprintf(hrBuf, hrBufSize, "persistspecialobject_write_zero\n"); - hrOut(pi); - } -#ifdef TOTEXT - printf("persistspecialobject_write_zero\n"); -#endif - } - return 0; - } else { - lua_pushstring(pi->L, "Type not literally persistable by default"); - lua_error(pi->L); - } - } - /* perms reftbl sptbl ... obj mt */ - lua_pushstring(pi->L, "__persist"); - /* perms reftbl sptbl ... obj mt "__persist" */ - lua_rawget(pi->L, -2); - /* perms reftbl sptbl ... obj mt __persist? */ - if(lua_isnil(pi->L, -1)) { - /* perms reftbl sptbl ... obj mt nil */ - lua_pop(pi->L, 2); - /* perms reftbl sptbl ... obj */ - if(defaction) { - { - int zero = 0; - pi_write(pi, &zero, sizeof(int), pi->ud); - if (humanReadable) { - snprintf(hrBuf, hrBufSize, "persistspecialobject_write_zero2\n"); - hrOut(pi); - } -#ifdef TOTEXT - printf("persistspecialobject_write_zero2\n"); -#endif - } - return 0; - } else { - lua_pushstring(pi->L, "Type not literally persistable by default"); - lua_error(pi->L); - return 0; /* not reached */ - } - } else if(lua_isboolean(pi->L, -1)) { - /* perms reftbl sptbl ... obj mt bool */ - if(lua_toboolean(pi->L, -1)) { - /* perms reftbl sptbl ... obj mt true */ - lua_pop(pi->L, 2); - /* perms reftbl sptbl ... obj */ - { - int zero = 0; - pi_write(pi, &zero, sizeof(int), pi->ud); - if (humanReadable) { - snprintf(hrBuf, hrBufSize, "persistspecialobject_write_zero3\n"); - hrOut(pi); - } -#ifdef TOTEXT - printf("persistspecialobject_write_zero3\n"); -#endif - } - return 0; - } else { - lua_pushstring(pi->L, "Metatable forbade persistence"); - lua_error(pi->L); - return 0; /* not reached */ - } - } else if(!lua_isfunction(pi->L, -1)) { - lua_pushstring(pi->L, "__persist not nil, boolean, or function"); - lua_error(pi->L); - } - /* perms reftbl ... obj mt __persist */ - lua_pushvalue(pi->L, -3); - /* perms reftbl ... obj mt __persist obj */ -#ifdef PLUTO_PASS_USERDATA_TO_PERSIST - lua_pushlightuserdata(pi->L, (void *)pi->writer); - lua_pushlightuserdata(pi->L, pi->ud); - /* perms reftbl ... obj mt __persist obj ud */ - lua_call(pi->L, 3, 1); - /* perms reftbl ... obj mt func? */ -#else - lua_call(pi->L, 1, 1); - /* perms reftbl ... obj mt func? */ -#endif - /* perms reftbl ... obj mt func? */ - if(!lua_isfunction(pi->L, -1)) { - lua_pushstring(pi->L, "__persist function did not return a function"); - lua_error(pi->L); - } - /* perms reftbl ... obj mt func */ - { - int one = 1; - pi_write(pi, &one, sizeof(int), pi->ud); - if (humanReadable) { - snprintf(hrBuf, hrBufSize, "persistspecialobject_write_one\n"); - hrOut(pi); - } -#ifdef TOTEXT - printf("persistspecialobject_write_one\n"); -#endif - } - persist(pi); - /* perms reftbl ... obj mt func */ - lua_pop(pi->L, 2); - /* perms reftbl ... obj */ - return 1; -} - -static void persisttable(PersistInfo *pi) -{ - if (humanReadable) { - snprintf(hrBuf, hrBufSize, "persisttable\n"); - hrOut(pi); - } -#ifdef TOTEXT - printf("persisttable\n"); -#endif - - /* perms reftbl ... tbl */ - lua_checkstack(pi->L, 3); - if(persistspecialobject(pi, 1)) { - /* perms reftbl ... tbl */ - return; - } - /* perms reftbl ... tbl */ - /* First, persist the metatable (if any) */ - if(!lua_getmetatable(pi->L, -1)) { - lua_pushnil(pi->L); - } - /* perms reftbl ... tbl mt/nil */ - persist(pi); - lua_pop(pi->L, 1); - /* perms reftbl ... tbl */ - - /* Now, persist all k/v pairs */ - lua_pushnil(pi->L); - /* perms reftbl ... tbl nil */ - while(lua_next(pi->L, -2)) { - /* perms reftbl ... tbl k v */ - lua_pushvalue(pi->L, -2); - /* perms reftbl ... tbl k v k */ - persist(pi); - lua_pop(pi->L, 1); - /* perms reftbl ... tbl k v */ - persist(pi); - lua_pop(pi->L, 1); - /* perms reftbl ... tbl k */ - } - /* perms reftbl ... tbl */ - /* Terminate list */ - lua_pushnil(pi->L); - /* perms reftbl ... tbl nil */ - persist(pi); - lua_pop(pi->L, 1); - /* perms reftbl ... tbl */ -} - -static void persistuserdata(PersistInfo *pi) { - /* perms reftbl ... udata */ - lua_checkstack(pi->L, 2); - if(persistspecialobject(pi, 0)) { - /* perms reftbl ... udata */ - return; - } else { - /* Use literal persistence */ - size_t length = uvalue(getobject(pi->L, -1))->len; - write_size(pi, &length); - pi_write(pi, lua_touserdata(pi->L, -1), length, pi->ud); - if (humanReadable) { - snprintf(hrBuf, hrBufSize, "persistuserdata %ld\n", (long) length); - hrOut(pi); - } -#ifdef TOTEXT - printf("persistuserdata %ld\n", (long) length); -#endif - if(!lua_getmetatable(pi->L, -1)) { - /* perms reftbl ... udata */ - lua_pushnil(pi->L); - /* perms reftbl ... udata mt/nil */ - } - persist(pi); - lua_pop(pi->L, 1); - /* perms reftbl ... udata */ - } -} - - -static Proto *toproto(lua_State *L, int stackpos) -{ - return gco2p(getobject(L, stackpos)->value.gc); -} - -static UpVal *toupval(lua_State *L, int stackpos) -{ - lua_assert(ttype(getobject(L, stackpos)) == LUA_TUPVAL); - return gco2uv(getobject(L, stackpos)->value.gc); -} - -static void pushproto(lua_State *L, Proto *proto) -{ - TValue o; - setptvalue(L, &o, proto); - LIF(A,pushobject)(L, &o); -} - -#define setuvvalue(L,obj,x) \ - { TValue *i_o=(obj); \ - i_o->value.gc=cast(GCObject *, (x)); i_o->tt=LUA_TUPVAL; \ - checkliveness(G(L),i_o); } - -static void pushupval(lua_State *L, UpVal *upval) -{ - TValue o; - setuvvalue(L, &o, upval); - LIF(A,pushobject)(L, &o); -} - -static void pushclosure(lua_State *L, Closure *closure) -{ - TValue o; - setclvalue(L, &o, closure); - LIF(A,pushobject)(L, &o); -} - -static void pushstring(lua_State *L, TString *s) -{ - TValue o; - setsvalue(L, &o, s); - LIF(A,pushobject)(L, &o); -} - -static void persistfunction(PersistInfo *pi) -{ - /* perms reftbl ... func */ - Closure *cl = clvalue(getobject(pi->L, -1)); - lua_checkstack(pi->L, 2); - if(cl->c.isC) { - /* It's a C function. For now, we aren't going to allow - * persistence of C closures, even if the "C proto" is - * already in the permanents table. */ - lua_pushstring(pi->L, "Attempt to persist a C function"); - lua_error(pi->L); - } else { - /* It's a Lua closure. */ - { - /* We don't really _NEED_ the number of upvals, - * but it'll simplify things a bit */ - pi_write(pi, &cl->l.p->nups, sizeof(lu_byte), pi->ud); - if (humanReadable) { - snprintf(hrBuf, hrBufSize, "persistfunction_number_upvalues %d\n", (int) cl->l.p->nups); - hrOut(pi); - } -#ifdef TOTEXT - printf("persistfunction_number_upvalues %d\n", (int) cl->l.p->nups); -#endif - } - /* Persist prototype */ - { - pushproto(pi->L, cl->l.p); - /* perms reftbl ... func proto */ - persist(pi); - lua_pop(pi->L, 1); - /* perms reftbl ... func */ - } - /* Persist upvalue values (not the upvalue objects - * themselves) */ - { - int i; - for(i=0; i<cl->l.p->nups; i++) { - /* perms reftbl ... func */ - pushupval(pi->L, cl->l.upvals[i]); - /* perms reftbl ... func upval */ - persist(pi); - lua_pop(pi->L, 1); - /* perms reftbl ... func */ - } - /* perms reftbl ... func */ - } - /* Persist function environment */ - { - lua_getfenv(pi->L, -1); - /* perms reftbl ... func fenv */ - if(lua_equal(pi->L, -1, LUA_GLOBALSINDEX)) { - /* Function has the default fenv */ - /* perms reftbl ... func _G */ - lua_pop(pi->L, 1); - /* perms reftbl ... func */ - lua_pushnil(pi->L); - /* perms reftbl ... func nil */ - } - /* perms reftbl ... func fenv/nil */ - persist(pi); - lua_pop(pi->L, 1); - /* perms reftbl ... func */ - } - } -} - - -/* Upvalues are tricky. Here's why. - * - * A particular upvalue may be either "open", in which case its member v - * points into a thread's stack, or "closed" in which case it points to the - * upvalue itself. An upvalue is closed under any of the following conditions: - * -- The function that initially declared the variable "local" returns - * -- The thread in which the closure was created is garbage collected - * - * To make things wackier, just because a thread is reachable by Lua doesn't - * mean it's in our root set. We need to be able to treat an open upvalue - * from an unreachable thread as a closed upvalue. - * - * The solution: - * (a) For the purposes of persisting, don't indicate whether an upvalue is - * closed or not. - * (b) When unpersisting, pretend that all upvalues are closed. - * (c) When persisting, persist all open upvalues referenced by a thread - * that is persisted, and tag each one with the corresponding stack position - * (d) When unpersisting, "reopen" each of these upvalues as the thread is - * unpersisted - */ -static void persistupval(PersistInfo *pi) -{ - /* perms reftbl ... upval */ - UpVal *uv = toupval(pi->L, -1); - lua_checkstack(pi->L, 1); - - /* We can't permit the upval to linger around on the stack, as Lua - * will bail if its GC finds it. */ - - lua_pop(pi->L, 1); - /* perms reftbl ... */ - LIF(A,pushobject)(pi->L, uv->v); - /* perms reftbl ... obj */ - persist(pi); - /* perms reftbl ... obj */ -} - -static void persistproto(PersistInfo *pi) -{ - /* perms reftbl ... proto */ - Proto *p = toproto(pi->L, -1); - lua_checkstack(pi->L, 2); - - /* Persist constant refs */ - { - int i; - pi_write(pi, &p->sizek, sizeof(int), pi->ud); - if (humanReadable) { - snprintf(hrBuf, hrBufSize, "persistproto_sizek %d\n", p->sizek); - hrOut(pi); - } - #ifdef TOTEXT - printf("persistproto_sizek %d\n", p->sizek); - #endif - for(i=0; i<p->sizek; i++) { - LIF(A,pushobject)(pi->L, &p->k[i]); - /* perms reftbl ... proto const */ - persist(pi); - lua_pop(pi->L, 1); - /* perms reftbl ... proto */ - } - } - /* perms reftbl ... proto */ - - /* serialize inner Proto refs */ - { - int i; - pi_write(pi, &p->sizep, sizeof(int), pi->ud); - if (humanReadable) { - snprintf(hrBuf, hrBufSize, "persistproto_sizep %d\n", p->sizep); - hrOut(pi); - } -#ifdef TOTEXT - printf("persistproto_sizep %d\n", p->sizep); -#endif - for(i=0; i<p->sizep; i++) - { - pushproto(pi->L, p->p[i]); - /* perms reftbl ... proto subproto */ - persist(pi); - lua_pop(pi->L, 1); - /* perms reftbl ... proto */ - } - } - /* perms reftbl ... proto */ - - /* Serialize code */ - { - int len; - pi_write(pi, &p->sizecode, sizeof(int), pi->ud); - if (humanReadable) { - snprintf(hrBuf, hrBufSize, "persistproto_sizecode %d\n", p->sizecode); - hrOut(pi); - } -#ifdef TOTEXT - printf("persistproto_sizecode %d\n", p->sizecode); -#endif - len = sizeof(Instruction) * p->sizecode; - pi_write(pi, p->code, len, pi->ud); - if (humanReadable) { - snprintf(hrBuf, hrBufSize, "persistproto_code %d\n", len); - hrOut(pi); - } -#ifdef TOTEXT - printf("persistproto_code %d\n", len); -#endif - } - - /* Serialize upvalue names */ - { - int i; - pi_write(pi, &p->sizeupvalues, sizeof(int), pi->ud); - if (humanReadable) { - snprintf(hrBuf, hrBufSize, "persistproto_upvalues %d\n", p->sizeupvalues); - hrOut(pi); - } -#ifdef TOTEXT - printf("persistproto_upvalues %d\n", p->sizeupvalues); -#endif - for(i=0; i<p->sizeupvalues; i++) - { - pushstring(pi->L, p->upvalues[i]); - persist(pi); - lua_pop(pi->L, 1); - } - } - /* Serialize local variable infos */ - { - int i; - pi_write(pi, &p->sizelocvars, sizeof(int), pi->ud); - if (humanReadable) { - snprintf(hrBuf, hrBufSize, "persistproto_sizelocvars %d\n", p->sizelocvars); - hrOut(pi); - } -#ifdef TOTEXT - printf("persistproto_sizelocvars %d\n", p->sizelocvars); -#endif - for(i=0; i<p->sizelocvars; i++) - { - pushstring(pi->L, p->locvars[i].varname); - persist(pi); - lua_pop(pi->L, 1); - - pi_write(pi, &p->locvars[i].startpc, sizeof(int), pi->ud); - if (humanReadable) { - snprintf(hrBuf, hrBufSize, "persistproto_startpc %d\n", p->locvars[i].startpc); - hrOut(pi); - } -#ifdef TOTEXT - printf("persistproto_startpc %d\n", p->locvars[i].startpc); -#endif - pi_write(pi, &p->locvars[i].endpc, sizeof(int), pi->ud); - if (humanReadable) { - snprintf(hrBuf, hrBufSize, "persistproto_endpc %d\n", p->locvars[i].endpc); - hrOut(pi); - } -#ifdef TOTEXT - printf("persistproto_endpc %d\n", p->locvars[i].endpc); -#endif - } - } - - /* Serialize source string */ - pushstring(pi->L, p->source); - persist(pi); - lua_pop(pi->L, 1); - - /* Serialize line numbers */ - { - pi_write(pi, &p->sizelineinfo, sizeof(int), pi->ud); - if (humanReadable) { - snprintf(hrBuf, hrBufSize, "persistproto_sizelineinfo %d\n", p->sizelineinfo); - hrOut(pi); - } -#ifdef TOTEXT - printf("persistproto_sizelineinfo %d\n", p->sizelineinfo); -#endif - if (p->sizelineinfo) - { - int len; - len = sizeof(int) * p->sizelineinfo; - pi_write(pi, p->lineinfo, len, pi->ud); - if (humanReadable) { - snprintf(hrBuf, hrBufSize, "persistproto_lineinfo %d\n", len); - hrOut(pi); - } -#ifdef TOTEXT - printf("persistproto_lineinfo %d\n", len); -#endif - } - } - - /* Serialize linedefined and lastlinedefined */ - pi_write(pi, &p->linedefined, sizeof(int), pi->ud); - if (humanReadable) { - snprintf(hrBuf, hrBufSize, "persistproto_linedefined %d\n", p->linedefined); - hrOut(pi); - } -#ifdef TOTEXT - printf("persistproto_linedefined %d\n", p->linedefined); -#endif - pi_write(pi, &p->lastlinedefined, sizeof(int), pi->ud); - if (humanReadable) { - snprintf(hrBuf, hrBufSize, "persistproto_lastlinedefined %d\n", p->lastlinedefined); - hrOut(pi); - } -#ifdef TOTEXT - printf("persistproto_lastlinedefined %d\n", p->lastlinedefined); -#endif - - /* Serialize misc values */ - { - pi_write(pi, &p->nups, sizeof(lu_byte), pi->ud); - if (humanReadable) { - snprintf(hrBuf, hrBufSize, "persistproto_nups %d\n", (int) p->nups); - hrOut(pi); - } -#ifdef TOTEXT - printf("persistproto_nups %d\n", (int) p->nups); -#endif - pi_write(pi, &p->numparams, sizeof(lu_byte), pi->ud); - if (humanReadable) { - snprintf(hrBuf, hrBufSize, "persistproto_numparams %d\n", (int) p->numparams); - hrOut(pi); - } -#ifdef TOTEXT - printf("persistproto_numparams %d\n", (int) p->numparams); -#endif - pi_write(pi, &p->is_vararg, sizeof(lu_byte), pi->ud); - if (humanReadable) { - snprintf(hrBuf, hrBufSize, "persistproto_is_vararg %d\n", (int) p->is_vararg); - hrOut(pi); - } -#ifdef TOTEXT - printf("persistproto_is_vararg %d\n", (int) p->is_vararg); -#endif - pi_write(pi, &p->maxstacksize, sizeof(lu_byte), pi->ud); - if (humanReadable) { - snprintf(hrBuf, hrBufSize, "persistproto_maxstacksize %d\n", (int) p->maxstacksize); - hrOut(pi); - } -#ifdef TOTEXT - printf("persistproto_maxstacksize %d\n", (int) p->maxstacksize); -#endif - } - /* We do not currently persist upvalue names, local variable names, - * variable lifetimes, line info, or source code. */ -} - -/* Copies a stack, but the stack is reversed in the process - */ -static size_t revappendstack(lua_State *from, lua_State *to) -{ - StkId o; - for(o=from->top-1; o>=from->stack; o--) { - setobj2s(to, to->top, o); - to->top++; - } - return from->top - from->stack; -} - -/* Persist all stack members - */ -static void persistthread(PersistInfo *pi) -{ - size_t posremaining; - lua_State *L2; - /* perms reftbl ... thr */ - L2 = lua_tothread(pi->L, -1); - lua_checkstack(pi->L, L2->top - L2->stack + 1); - if(pi->L == L2) { - lua_pushstring(pi->L, "Can't persist currently running thread"); - lua_error(pi->L); - return; /* not reached */ - } - - /* Persist the stack */ - posremaining = revappendstack(L2, pi->L); - /* perms reftbl ... thr (rev'ed contents of L2) */ - write_size(pi, &posremaining); - for(; posremaining > 0; posremaining--) { - persist(pi); - lua_pop(pi->L, 1); - } - /* perms reftbl ... thr */ - /* Now, persist the CallInfo stack. */ - { - size_t i, numframes = (L2->ci - L2->base_ci) + 1; - write_size(pi, &numframes); - for(i=0; i<numframes; i++) { - CallInfo *ci = L2->base_ci + i; - size_t stackbase = ci->base - L2->stack; - size_t stackfunc = ci->func - L2->stack; - size_t stacktop = ci->top - L2->stack; - size_t savedpc = (ci != L2->base_ci) ? - ci->savedpc - ci_func(ci)->l.p->code : - 0; - write_size(pi, &stackbase); - write_size(pi, &stackfunc); - write_size(pi, &stacktop); - pi_write(pi, &ci->nresults, sizeof(int), pi->ud); - if (humanReadable) { - snprintf(hrBuf, hrBufSize, "persistthread %d\n", ci->nresults); - hrOut(pi); - } -#ifdef TOTEXT - printf("persistthread %d\n", ci->nresults); -#endif - write_size(pi, &savedpc); - } - } - - /* Serialize the state's other parameters, with the exception of upval stuff */ - { - size_t stackbase = L2->base - L2->stack; - size_t stacktop = L2->top - L2->stack; - lua_assert(L2->nCcalls <= 1); - pi_write(pi, &L2->status, sizeof(lu_byte), pi->ud); - if (humanReadable) { - snprintf(hrBuf, hrBufSize, "persistthread_status %d\n", (int) L2->status); - hrOut(pi); - } -#ifdef TOTEXT - printf("persistthread_status %d\n", (int) L2->status); -#endif - write_size(pi, &stackbase); - write_size(pi, &stacktop); - - // ptrdiff_t changes sizes based on 32/64 bit - // Hard cast to 64 bit size if SIZE64 is defined -#ifdef SIZES64 - uint64 ptrIndex = static_cast<uint64>(L2->errfunc); - pi_write(pi, &ptrIndex, sizeof(uint64), pi->ud); -#else - pi_write(pi, &L2->errfunc, sizeof(ptrdiff_t), pi->ud); -#endif - //write_size(pi, (size_t *)&L2->errfunc); - } - - /* Finally, record upvalues which need to be reopened */ - /* See the comment above persistupval() for why we do this */ - { - GCObject *gco; - UpVal *uv; - /* perms reftbl ... thr */ - for(gco = L2->openupval; gco != NULL; gco = uv->next) { - size_t stackpos; - uv = gco2uv(gco); - - /* Make sure upvalue is really open */ - lua_assert(uv->v != &uv->u.value); - pushupval(pi->L, uv); - /* perms reftbl ... thr uv */ - persist(pi); - lua_pop(pi->L, 1); - /* perms reftbl ... thr */ - stackpos = uv->v - L2->stack; - write_size(pi, &stackpos); - } - /* perms reftbl ... thr */ - lua_pushnil(pi->L); - /* perms reftbl ... thr nil */ - persist(pi); - lua_pop(pi->L, 1); - /* perms reftbl ... thr */ - } - /* perms reftbl ... thr */ -} - -static void persistboolean(PersistInfo *pi) -{ - int b = lua_toboolean(pi->L, -1); - pi_write(pi, &b, sizeof(int), pi->ud); - if (humanReadable) { - snprintf(hrBuf, hrBufSize, "persistboolean %d\n", b); - hrOut(pi); - } -#ifdef TOTEXT - printf("persistboolean %d\n", b); -#endif -} - -static void persistlightuserdata(PersistInfo *pi) -{ - void *p = lua_touserdata(pi->L, -1); - pi_write(pi, &p, sizeof(void *), pi->ud); - if (humanReadable) { - snprintf(hrBuf, hrBufSize, "persistlightuserdata %p\n", p); - hrOut(pi); - } -#ifdef TOTEXT - printf("persistlightuserdata %d\n", (int) p); -#endif -} - -static void persistnumber(PersistInfo *pi) -{ - lua_Number n = lua_tonumber(pi->L, -1); - pi_write(pi, &n, sizeof(lua_Number), pi->ud); - if (humanReadable) { - snprintf(hrBuf, hrBufSize, "persistnumber %d (%d)\n", (int) n, (int) sizeof(lua_Number)); - hrOut(pi); - } -#ifdef TOTEXT - printf("persistnumber %d (%d)\n", (int) n, (int) sizeof(lua_Number)); -#endif -} - -static void persiststring(PersistInfo *pi) -{ - if (humanReadable) { - snprintf(hrBuf, hrBufSize, "persiststring\n"); - hrOut(pi); - } -#ifdef TOTEXT - printf("persiststring\n"); -#endif - size_t length = lua_strlen(pi->L, -1); - write_size(pi, &length); - const char* s = lua_tostring(pi->L, -1); - pi_write(pi, s, length, pi->ud); - if (humanReadable) { - snprintf(hrBuf, hrBufSize, "persiststring %d \"%s\"\n", (int)length, s); - hrOut(pi); - } -#ifdef TOTEXT - printf("persiststring %d \"%s\"\n", length, s); -#endif -} - -/* Top-level delegating persist function - */ -static void persist(PersistInfo *pi) -{ - /* perms reftbl ... obj */ - lua_checkstack(pi->L, 2); - /* If the object has already been written, write a reference to it */ - lua_pushvalue(pi->L, -1); - /* perms reftbl ... obj obj */ - lua_rawget(pi->L, 2); - /* perms reftbl ... obj ref? */ - if(!lua_isnil(pi->L, -1)) { - /* perms reftbl ... obj ref */ - int zero = 0; - pi_write(pi, &zero, sizeof(int), pi->ud); - if (humanReadable) { - snprintf(hrBuf, hrBufSize, "persist_seenobject\n"); - hrOut(pi); - } -#ifdef TOTEXT - printf("persist_seenobject\n"); -#endif - int *ref = (int *)lua_touserdata(pi->L, -1); - pi_write(pi, ref, sizeof(int), pi->ud); - if (humanReadable) { - snprintf(hrBuf, hrBufSize, "persist_touserdata_ref %d\n", ref); - hrOut(pi); - } -#ifdef TOTEXT - printf("persist_touserdata_ref %d\n", ref); -#endif - lua_pop(pi->L, 1); - /* perms reftbl ... obj ref */ -#ifdef PLUTO_DEBUG - printindent(pi->level); - printf("0 %d\n", ref); -#endif - return; - } - /* perms reftbl ... obj nil */ - lua_pop(pi->L, 1); - /* perms reftbl ... obj */ - /* If the object is nil, write the pseudoreference 0 */ - if(lua_isnil(pi->L, -1)) { - int zero = 0; - /* firsttime */ - pi_write(pi, &zero, sizeof(int), pi->ud); - /* ref */ - pi_write(pi, &zero, sizeof(int), pi->ud); - if (humanReadable) { - snprintf(hrBuf, hrBufSize, "persist_nil (last 2 lines)\n"); - hrOut(pi); - } -#ifdef TOTEXT - printf("persist_nil (last 2 lines)\n"); -#endif -#ifdef PLUTO_DEBUG - printindent(pi->level); - printf("0 0\n"); -#endif - return; - } - { - /* indicate that it's the first time */ - int one = 1; - pi_write(pi, &one, sizeof(int), pi->ud); - if (humanReadable) { - snprintf(hrBuf, hrBufSize, "persist_newobject\n"); - hrOut(pi); - } -#ifdef TOTEXT - printf("persist_newobject\n"); -#endif - } - lua_pushvalue(pi->L, -1); - /* perms reftbl ... obj obj */ - int *ref = (int *)lua_newuserdata(pi->L, sizeof(int)); - *ref = ++(pi->counter); - /* perms reftbl ... obj obj ref */ - lua_rawset(pi->L, 2); - /* perms reftbl ... obj */ - - pi_write(pi, &pi->counter, sizeof(int), pi->ud); - if (humanReadable) { - snprintf(hrBuf, hrBufSize, "persist_counter %d\n", pi->counter); - hrOut(pi); - } -#ifdef TOTEXT - printf("persist_counter %d\n", pi->counter); -#endif - - - /* At this point, we'll give the permanents table a chance to play. */ - { - lua_pushvalue(pi->L, -1); - /* perms reftbl ... obj obj */ - lua_gettable(pi->L, 1); - /* perms reftbl ... obj permkey? */ - if(!lua_isnil(pi->L, -1)) { - /* perms reftbl ... obj permkey */ - int type = PLUTO_TPERMANENT; -#ifdef PLUTO_DEBUG - printindent(pi->level); - printf("1 %d PERM\n", pi->counter); - pi->level++; -#endif - pi_write(pi, &type, sizeof(int), pi->ud); - if (humanReadable) { - snprintf(hrBuf, hrBufSize, "persist_permtype %d\n", type); - hrOut(pi); - } -#ifdef TOTEXT - printf("persist_permtype %d\n", type); -#endif - persist(pi); - lua_pop(pi->L, 1); - /* perms reftbl ... obj */ -#ifdef PLUTO_DEBUG - pi->level--; -#endif - return; - } else { - /* perms reftbl ... obj nil */ - lua_pop(pi->L, 1); - /* perms reftbl ... obj */ - } - /* perms reftbl ... obj */ - } - { - int type = lua_type(pi->L, -1); - pi_write(pi, &type, sizeof(int), pi->ud); - if (humanReadable) { - snprintf(hrBuf, hrBufSize, "persist %s\n", type >= 0 && type < NUMTYPES ? typenames[type] : "?"); - hrOut(pi); - } -#ifdef TOTEXT - printf("persist %s\n", type >= 0 && type < NUMTYPES ? typenames[type] : "?"); -#endif - -#ifdef PLUTO_DEBUG - printindent(pi->level); - printf("1 %d %d\n", pi->counter, type); - pi->level++; -#endif - } - - switch(lua_type(pi->L, -1)) { - case LUA_TBOOLEAN: - persistboolean(pi); - break; - case LUA_TLIGHTUSERDATA: - persistlightuserdata(pi); - break; - case LUA_TNUMBER: - persistnumber(pi); - break; - case LUA_TSTRING: - persiststring(pi); - break; - case LUA_TTABLE: - persisttable(pi); - break; - case LUA_TFUNCTION: - persistfunction(pi); - break; - case LUA_TTHREAD: - persistthread(pi); - break; - case LUA_TPROTO: - persistproto(pi); - break; - case LUA_TUPVAL: - persistupval(pi); - break; - case LUA_TUSERDATA: - persistuserdata(pi); - break; - default: - lua_assert(0); - } -#ifdef PLUTO_DEBUG - pi->level--; -#endif -} - -void pluto_persist(lua_State *L, lua_Chunkwriter writer, void *ud) -{ - PersistInfo pi; - - pi.counter = 0; - pi.L = L; - pi.writer = writer; - pi.ud = ud; -#ifdef PLUTO_DEBUG - pi.level = 0; -#endif - - lua_checkstack(L, 4); - /* perms? rootobj? ...? */ - lua_assert(lua_gettop(L) == 2); - /* perms rootobj */ - lua_assert(!lua_isnil(L, 2)); - /* perms rootobj */ - lua_newtable(L); - /* perms rootobj reftbl */ - - /* Now we're going to make the table weakly keyed. This prevents the - * GC from visiting it and trying to mark things it doesn't want to - * mark in tables, e.g. upvalues. All objects in the table are - * a priori reachable, so it doesn't matter that we do this. */ - lua_newtable(L); - /* perms rootobj reftbl mt */ - lua_pushstring(L, "__mode"); - /* perms rootobj reftbl mt "__mode" */ - lua_pushstring(L, "k"); - /* perms rootobj reftbl mt "__mode" "k" */ - lua_settable(L, 4); - /* perms rootobj reftbl mt */ - lua_setmetatable(L, 3); - /* perms rootobj reftbl */ - lua_insert(L, 2); - /* perms reftbl rootobj */ - persist(&pi); - /* perms reftbl rootobj */ - lua_remove(L, 2); - /* perms rootobj */ -} - -typedef struct WriterInfo_t { - char* buf; - size_t buflen; -} WriterInfo; - -static int bufwriter (lua_State *L, const void *p, size_t sz, void *ud) { - const char *cp = (const char *)p; - WriterInfo *wi = (WriterInfo *)ud; - - LIF(M,reallocvector)(L, wi->buf, wi->buflen, wi->buflen+sz, char); - while(sz) - { - /* how dearly I love ugly C pointer twiddling */ - wi->buf[wi->buflen++] = *cp++; - sz--; - } - return 0; -} - -int persist_l(lua_State *L) -{ - /* perms? rootobj? ...? */ - WriterInfo wi; - - wi.buf = NULL; - wi.buflen = 0; - - lua_settop(L, 2); - /* perms? rootobj? */ - luaL_checktype(L, 1, LUA_TTABLE); - /* perms rootobj? */ - luaL_checktype(L, 1, LUA_TTABLE); - /* perms rootobj */ - - pluto_persist(L, bufwriter, &wi); - - lua_settop(L, 0); - /* (empty) */ - lua_pushlstring(L, wi.buf, wi.buflen); - /* str */ - pdep_freearray(L, wi.buf, wi.buflen, char); - return 1; -} - -typedef struct UnpersistInfo_t { - lua_State *L; - ZIO zio; -#ifdef PLUTO_DEBUG - int level; -#endif -} UnpersistInfo; - -static void unpersist(UnpersistInfo *upi); - -/* The object is left on the stack. This is primarily used by unpersist, but - * may be used by GCed objects that may incur cycles in order to preregister - * the object. */ -static void registerobject(int ref, UnpersistInfo *upi) -{ - /* perms reftbl ... obj */ - lua_checkstack(upi->L, 2); - lua_pushlightuserdata(upi->L, (void *)ref); - /* perms reftbl ... obj ref */ - lua_pushvalue(upi->L, -2); - /* perms reftbl ... obj ref obj */ - lua_settable(upi->L, 2); - /* perms reftbl ... obj */ -} - -static void unpersistboolean(UnpersistInfo *upi) -{ - /* perms reftbl ... */ - int b; - lua_checkstack(upi->L, 1); - verify(LIF(Z,read)(&upi->zio, &b, sizeof(int)) == 0); - lua_pushboolean(upi->L, b); - /* perms reftbl ... bool */ -} - -static void unpersistlightuserdata(UnpersistInfo *upi) -{ - /* perms reftbl ... */ - void *p; - lua_checkstack(upi->L, 1); - verify(LIF(Z,read)(&upi->zio, &p, sizeof(void *)) == 0); - lua_pushlightuserdata(upi->L, p); - /* perms reftbl ... ludata */ -} - -static void unpersistnumber(UnpersistInfo *upi) -{ - /* perms reftbl ... */ - lua_Number n; - lua_checkstack(upi->L, 1); - verify(LIF(Z,read)(&upi->zio, &n, sizeof(lua_Number)) == 0); - lua_pushnumber(upi->L, n); - /* perms reftbl ... num */ -} - -static void unpersiststring(UnpersistInfo *upi) -{ - /* perms reftbl sptbl ref */ - /*int length;*/ - size_t length; - char* string; - lua_checkstack(upi->L, 1); - /*verify(LIF(Z,read)(&upi->zio, &length, sizeof(int)) == 0);*/ - /*verify(LIF(Z,read)(&upi->zio, &length, sizeof(size_t)) == 0);*/ - read_size(&upi->zio, &length); - string = pdep_newvector(upi->L, length, char); - verify(LIF(Z,read)(&upi->zio, string, length) == 0); - lua_pushlstring(upi->L, string, length); - /* perms reftbl sptbl ref str */ - pdep_freearray(upi->L, string, length, char); -} - -static void unpersistspecialtable(int ref, UnpersistInfo *upi) -{ - /* perms reftbl ... */ - lua_checkstack(upi->L, 1); - unpersist(upi); - /* perms reftbl ... spfunc? */ - lua_assert(lua_isfunction(upi->L, -1)); - /* perms reftbl ... spfunc */ - lua_call(upi->L, 0, 1); - /* perms reftbl ... tbl? */ - lua_assert(lua_istable(upi->L, -1)); - /* perms reftbl ... tbl */ -} - -static void unpersistliteraltable(int ref, UnpersistInfo *upi) -{ - /* perms reftbl ... */ - lua_checkstack(upi->L, 3); - /* Preregister table for handling of cycles */ - lua_newtable(upi->L); - /* perms reftbl ... tbl */ - registerobject(ref, upi); - /* perms reftbl ... tbl */ - /* Unpersist metatable */ - { - unpersist(upi); - /* perms reftbl ... tbl mt/nil? */ - if(lua_istable(upi->L, -1)) { - /* perms reftbl ... tbl mt */ - lua_setmetatable(upi->L, -2); - /* perms reftbl ... tbl */ - } else { - /* perms reftbl ... tbl nil? */ - lua_assert(lua_isnil(upi->L, -1)); - /* perms reftbl ... tbl nil */ - lua_pop(upi->L, 1); - /* perms reftbl ... tbl */ - } - /* perms reftbl ... tbl */ - } - - while(1) - { - /* perms reftbl ... tbl */ - unpersist(upi); - /* perms reftbl ... tbl key/nil */ - if(lua_isnil(upi->L, -1)) { - /* perms reftbl ... tbl nil */ - lua_pop(upi->L, 1); - /* perms reftbl ... tbl */ - break; - } - /* perms reftbl ... tbl key */ - unpersist(upi); - /* perms reftbl ... tbl key value? */ - lua_assert(!lua_isnil(upi->L, -1)); - /* perms reftbl ... tbl key value */ - lua_rawset(upi->L, -3); - /* perms reftbl ... tbl */ - } -} - -static void unpersisttable(int ref, UnpersistInfo *upi) -{ - /* perms reftbl ... */ - lua_checkstack(upi->L, 1); - { - int isspecial; - verify(LIF(Z,read)(&upi->zio, &isspecial, sizeof(int)) == 0); - if(isspecial) { - unpersistspecialtable(ref, upi); - /* perms reftbl ... tbl */ - } else { - unpersistliteraltable(ref, upi); - /* perms reftbl ... tbl */ - } - /* perms reftbl ... tbl */ - } -} - -static UpVal *makeupval(lua_State *L, int stackpos) -{ - UpVal *uv = pdep_new(L, UpVal); - pdep_link(L, (GCObject *)uv, LUA_TUPVAL); - uv->tt = LUA_TUPVAL; - uv->v = &uv->u.value; - uv->u.l.prev = NULL; - uv->u.l.next = NULL; - setobj(L, uv->v, getobject(L, stackpos)); - return uv; -} - -static Proto *makefakeproto(lua_State *L, lu_byte nups) -{ - Proto *p = pdep_newproto(L); - p->sizelineinfo = 1; - p->lineinfo = pdep_newvector(L, 1, int); - p->lineinfo[0] = 1; - p->sizecode = 1; - p->code = pdep_newvector(L, 1, Instruction); - p->code[0] = CREATE_ABC(OP_RETURN, 0, 1, 0); - p->source = pdep_newlstr(L, "", 0); - p->maxstacksize = 2; - p->nups = nups; - p->sizek = 0; - p->sizep = 0; - - return p; -} - -/* The GC is not fond of finding upvalues in tables. We get around this - * during persistence using a weakly keyed table, so that the GC doesn't - * bother to mark them. This won't work in unpersisting, however, since - * if we make the values weak they'll be collected (since nothing else - * references them). Our solution, during unpersisting, is to represent - * upvalues as dummy functions, each with one upvalue. */ -static void boxupval_start(lua_State *L) -{ - LClosure *lcl; - lcl = (LClosure *)pdep_newLclosure(L, 1, hvalue(&L->l_gt)); - pushclosure(L, (Closure *)lcl); - /* ... func */ - lcl->p = makefakeproto(L, 1); - - /* Temporarily initialize the upvalue to nil */ - - lua_pushnil(L); - lcl->upvals[0] = makeupval(L, -1); - lua_pop(L, 1); -} - -static void boxupval_finish(lua_State *L) -{ - /* ... func obj */ - LClosure *lcl = (LClosure *) clvalue(getobject(L, -2)); - - lcl->upvals[0]->u.value = *getobject(L, -1); - lua_pop(L, 1); -} - - -static void unboxupval(lua_State *L) -{ - /* ... func */ - LClosure *lcl; - UpVal *uv; - - lcl = (LClosure *)clvalue(getobject(L, -1)); - uv = lcl->upvals[0]; - lua_pop(L, 1); - /* ... */ - pushupval(L, uv); - /* ... upval */ -} - -static void unpersistfunction(int ref, UnpersistInfo *upi) -{ - /* perms reftbl ... */ - LClosure *lcl; - int i; - lu_byte nupvalues; - lua_checkstack(upi->L, 2); - - verify(LIF(Z,read)(&upi->zio, &nupvalues, sizeof(lu_byte)) == 0); - - lcl = (LClosure *)pdep_newLclosure(upi->L, nupvalues, hvalue(&upi->L->l_gt)); - pushclosure(upi->L, (Closure *)lcl); - - /* perms reftbl ... func */ - /* Put *some* proto in the closure, before the GC can find it */ - lcl->p = makefakeproto(upi->L, nupvalues); - - /* Also, we need to temporarily fill the upvalues */ - lua_pushnil(upi->L); - /* perms reftbl ... func nil */ - for(i=0; i<nupvalues; i++) { - lcl->upvals[i] = makeupval(upi->L, -1); - } - lua_pop(upi->L, 1); - /* perms reftbl ... func */ - - /* I can't see offhand how a function would ever get to be self- - * referential, but just in case let's register it early */ - registerobject(ref, upi); - - /* Now that it's safe, we can get the real proto */ - unpersist(upi); - /* perms reftbl ... func proto? */ - lua_assert(lua_type(upi->L, -1) == LUA_TPROTO); - /* perms reftbl ... func proto */ - lcl->p = toproto(upi->L, -1); - lua_pop(upi->L, 1); - /* perms reftbl ... func */ - - for(i=0; i<nupvalues; i++) { - /* perms reftbl ... func */ - unpersist(upi); - /* perms reftbl ... func func2 */ - unboxupval(upi->L); - /* perms reftbl ... func upval */ - lcl->upvals[i] = toupval(upi->L, -1); - lua_pop(upi->L, 1); - /* perms reftbl ... func */ - } - /* perms reftbl ... func */ - - /* Finally, the fenv */ - unpersist(upi); - /* perms reftbl ... func fenv/nil? */ - lua_assert(lua_type(upi->L, -1) == LUA_TNIL || - lua_type(upi->L, -1) == LUA_TTABLE); - /* perms reftbl ... func fenv/nil */ - if(!lua_isnil(upi->L, -1)) { - /* perms reftbl ... func fenv */ - lua_setfenv(upi->L, -2); - /* perms reftbl ... func */ - } else { - /* perms reftbl ... func nil */ - lua_pop(upi->L, 1); - /* perms reftbl ... func */ - } - /* perms reftbl ... func */ -} - -static void unpersistupval(int ref, UnpersistInfo *upi) -{ - /* perms reftbl ... */ - lua_checkstack(upi->L, 2); - - boxupval_start(upi->L); - /* perms reftbl ... func */ - registerobject(ref, upi); - - unpersist(upi); - /* perms reftbl ... func obj */ - boxupval_finish(upi->L); - /* perms reftbl ... func */ -} - -static void unpersistproto(int ref, UnpersistInfo *upi) -{ - /* perms reftbl ... */ - Proto *p; - int i; - int sizep, sizek; - - /* We have to be careful. The GC expects a lot out of protos. In - * particular, we need to give the function a valid string for its - * source, and valid code, even before we actually read in the real - * code. */ - TString *source = pdep_newlstr(upi->L, "", 0); - p = pdep_newproto(upi->L); - p->source = source; - p->sizecode=1; - p->code = pdep_newvector(upi->L, 1, Instruction); - p->code[0] = CREATE_ABC(OP_RETURN, 0, 1, 0); - p->maxstacksize = 2; - p->sizek = 0; - p->sizep = 0; - - lua_checkstack(upi->L, 2); - - pushproto(upi->L, p); - /* perms reftbl ... proto */ - /* We don't need to register early, since protos can never ever be - * involved in cyclic references */ - - /* Read in constant references */ - { - verify(LIF(Z,read)(&upi->zio, &sizek, sizeof(int)) == 0); - LIF(M,reallocvector)(upi->L, p->k, 0, sizek, TValue); - for(i=0; i<sizek; i++) { - /* perms reftbl ... proto */ - unpersist(upi); - /* perms reftbl ... proto k */ - setobj2s(upi->L, &p->k[i], getobject(upi->L, -1)); - p->sizek++; - lua_pop(upi->L, 1); - /* perms reftbl ... proto */ - } - /* perms reftbl ... proto */ - } - /* Read in sub-proto references */ - { - verify(LIF(Z,read)(&upi->zio, &sizep, sizeof(int)) == 0); - LIF(M,reallocvector)(upi->L, p->p, 0, sizep, Proto*); - for(i=0; i<sizep; i++) { - /* perms reftbl ... proto */ - unpersist(upi); - /* perms reftbl ... proto subproto */ - p->p[i] = toproto(upi->L, -1); - p->sizep++; - lua_pop(upi->L, 1); - /* perms reftbl ... proto */ - } - /* perms reftbl ... proto */ - } - - /* Read in code */ - { - verify(LIF(Z,read)(&upi->zio, &p->sizecode, sizeof(int)) == 0); - LIF(M,reallocvector)(upi->L, p->code, 1, p->sizecode, Instruction); - verify(LIF(Z,read)(&upi->zio, p->code, - sizeof(Instruction) * p->sizecode) == 0); - } - - /* Read in upvalue names */ - { - verify(LIF(Z,read)(&upi->zio, &p->sizeupvalues, sizeof(int)) == 0); - if (p->sizeupvalues) - { - LIF(M,reallocvector)(upi->L, p->upvalues, 0, p->sizeupvalues, TString *); - for(i=0; i<p->sizeupvalues; i++) - { - unpersist(upi); - p->upvalues[i] = pdep_newlstr(upi->L, lua_tostring(upi->L, -1), strlen(lua_tostring(upi->L, -1))); - lua_pop(upi->L, 1); - } - } - } - - /* Read in local variable infos */ - { - verify(LIF(Z,read)(&upi->zio, &p->sizelocvars, sizeof(int)) == 0); - if (p->sizelocvars) - { - LIF(M,reallocvector)(upi->L, p->locvars, 0, p->sizelocvars, LocVar); - for(i=0; i<p->sizelocvars; i++) - { - unpersist(upi); - p->locvars[i].varname = pdep_newlstr(upi->L, lua_tostring(upi->L, -1), strlen(lua_tostring(upi->L, -1))); - lua_pop(upi->L, 1); - - verify(LIF(Z,read)(&upi->zio, &p->locvars[i].startpc, sizeof(int)) == 0); - verify(LIF(Z,read)(&upi->zio, &p->locvars[i].endpc, sizeof(int)) == 0); - } - } - } - - /* Read in source string*/ - unpersist(upi); - p->source = pdep_newlstr(upi->L, lua_tostring(upi->L, -1), strlen(lua_tostring(upi->L, -1))); - lua_pop(upi->L, 1); - - /* Read in line numbers */ - { - verify(LIF(Z,read)(&upi->zio, &p->sizelineinfo, sizeof(int)) == 0); - if (p->sizelineinfo) - { - LIF(M,reallocvector)(upi->L, p->lineinfo, 0, p->sizelineinfo, int); - verify(LIF(Z,read)(&upi->zio, p->lineinfo, - sizeof(int) * p->sizelineinfo) == 0); - } - } - - /* Read in linedefined and lastlinedefined */ - verify(LIF(Z,read)(&upi->zio, &p->linedefined, sizeof(int)) == 0); - verify(LIF(Z,read)(&upi->zio, &p->lastlinedefined, sizeof(int)) == 0); - - /* Read in misc values */ - { - verify(LIF(Z,read)(&upi->zio, &p->nups, sizeof(lu_byte)) == 0); - verify(LIF(Z,read)(&upi->zio, &p->numparams, sizeof(lu_byte)) == 0); - verify(LIF(Z,read)(&upi->zio, &p->is_vararg, sizeof(lu_byte)) == 0); - verify(LIF(Z,read)(&upi->zio, &p->maxstacksize, sizeof(lu_byte)) == 0); - } -} - - -/* Does basically the opposite of luaC_link(). - * Right now this function is rather inefficient; it requires traversing the - * entire root GC set in order to find one object. If the GC list were doubly - * linked this would be much easier, but there's no reason for Lua to have - * that. */ -static void gcunlink(lua_State *L, GCObject *gco) -{ - GCObject *prevslot; - if(G(L)->rootgc == gco) { - G(L)->rootgc = G(L)->rootgc->gch.next; - return; - } - - prevslot = G(L)->rootgc; - while(prevslot->gch.next != gco) { - lua_assert(prevslot->gch.next != NULL); - prevslot = prevslot->gch.next; - } - - prevslot->gch.next = prevslot->gch.next->gch.next; -} - -/* FIXME __ALL__ field ordering */ -static void unpersistthread(int ref, UnpersistInfo *upi) -{ - /* perms reftbl ... */ - lua_State *L2; - size_t stacklimit = 0; - L2 = lua_newthread(upi->L); - lua_checkstack(upi->L, 3); - /* L1: perms reftbl ... thr */ - /* L2: (empty) */ - registerobject(ref, upi); - - /* First, deserialize the object stack. */ - { - size_t i, stacksize; - read_size(&upi->zio, &stacksize); - LIF(D,growstack)(L2, (int)stacksize); - /* Make sure that the first stack element (a nil, representing - * the imaginary top-level C function) is written to the very, - * very bottom of the stack */ - L2->top--; - for(i=0; i<stacksize; i++) { - unpersist(upi); - /* L1: perms reftbl ... thr obj* */ - } - lua_xmove(upi->L, L2, stacksize); - /* L1: perms reftbl ... thr */ - /* L2: obj* */ - } - /* (hereafter, stacks refer to L1) */ - - /* Now, deserialize the CallInfo stack. */ - { - size_t i, numframes; - read_size(&upi->zio, &numframes); - LIF(D,reallocCI)(L2,numframes*2); - for(i=0; i<numframes; i++) { - CallInfo *ci = L2->base_ci + i; - size_t stackbase, stackfunc, stacktop, savedpc; - read_size(&upi->zio, &stackbase); - read_size(&upi->zio, &stackfunc); - read_size(&upi->zio, &stacktop); - verify(LIF(Z,read)(&upi->zio, &ci->nresults, sizeof(int)) == 0); - read_size(&upi->zio, &savedpc); - - if(stacklimit < stacktop) - stacklimit = stacktop; - - ci->base = L2->stack+stackbase; - ci->func = L2->stack+stackfunc; - ci->top = L2->stack+stacktop; - ci->savedpc = (ci != L2->base_ci) ? - ci_func(ci)->l.p->code+savedpc : - 0; - ci->tailcalls = 0; - /* Update the pointer each time, to keep the GC - * happy*/ - L2->ci = ci; - } - } - /* perms reftbl ... thr */ - /* Deserialize the state's other parameters, with the exception of upval stuff */ - { - size_t stackbase, stacktop; - L2->savedpc = L2->ci->savedpc; - verify(LIF(Z,read)(&upi->zio, &L2->status, sizeof(lu_byte)) == 0); - read_size(&upi->zio, &stackbase); - read_size(&upi->zio, &stacktop); - -#ifdef SIZES64 - uint64 value; - verify(LIF(Z,read)(&upi->zio, &value, sizeof(uint64)) == 0); - - L2->errfunc = static_cast<ptrdiff_t>(value); -#else - verify(LIF(Z,read)(&upi->zio, &L2->errfunc, sizeof(ptrdiff_t)) == 0); -#endif - - //read_size(&upi->zio, (size_t *)&L2->errfunc); - L2->base = L2->stack + stackbase; - L2->top = L2->stack + stacktop; - } - /* Finally, "reopen" upvalues (see persistupval() for why) */ - { - UpVal* uv; - GCObject **nextslot = &L2->openupval; - global_State *g = G(L2); - while(1) { - size_t stackpos; - unpersist(upi); - /* perms reftbl ... thr uv/nil */ - if(lua_isnil(upi->L, -1)) { - /* perms reftbl ... thr nil */ - lua_pop(upi->L, 1); - /* perms reftbl ... thr */ - break; - } - /* perms reftbl ... thr boxeduv */ - unboxupval(upi->L); - /* perms reftbl ... thr uv */ - uv = toupval(upi->L, -1); - lua_pop(upi->L, 1); - /* perms reftbl ... thr */ - - read_size(&upi->zio, &stackpos); - uv->v = L2->stack + stackpos; - gcunlink(upi->L, (GCObject *)uv); - uv->marked = luaC_white(g); - *nextslot = (GCObject *)uv; - nextslot = &uv->next; - uv->u.l.prev = &G(L2)->uvhead; - uv->u.l.next = G(L2)->uvhead.u.l.next; - uv->u.l.next->u.l.prev = uv; - G(L2)->uvhead.u.l.next = uv; - lua_assert(uv->u.l.next->u.l.prev == uv && uv->u.l.prev->u.l.next == uv); - } - *nextslot = NULL; - } - - /* The stack must be valid at least to the highest value among the CallInfos */ - /* 'top' and the values up to there must be filled with 'nil' */ - { - StkId o; - LIF(D,checkstack)(L2, (int)stacklimit); - for (o = L2->top; o <= L2->top + stacklimit; o++) - setnilvalue(o); - } -} - -static void unpersistuserdata(int ref, UnpersistInfo *upi) -{ - /* perms reftbl ... */ - int isspecial; - lua_checkstack(upi->L, 2); - verify(LIF(Z,read)(&upi->zio, &isspecial, sizeof(int)) == 0); - if(isspecial) { - unpersist(upi); - /* perms reftbl ... spfunc? */ - lua_assert(lua_isfunction(upi->L, -1)); - /* perms reftbl ... spfunc */ -#ifdef PLUTO_PASS_USERDATA_TO_PERSIST - lua_pushlightuserdata(upi->L, &upi->zio); - lua_call(upi->L, 1, 1); -#else - lua_call(upi->L, 0, 1); -#endif - /* perms reftbl ... udata? */ -/* This assertion might not be necessary; it's conceivable, for - * example, that the SP function might decide to return a table - * with equivalent functionality. For the time being, we'll - * ignore this possibility in favor of stricter and more testable - * requirements. */ - lua_assert(lua_isuserdata(upi->L, -1)); - /* perms reftbl ... udata */ - } else { - size_t length; - read_size(&upi->zio, &length); - - lua_newuserdata(upi->L, length); - /* perms reftbl ... udata */ - registerobject(ref, upi); - verify(LIF(Z,read)(&upi->zio, lua_touserdata(upi->L, -1), length) == 0); - - unpersist(upi); - /* perms reftbl ... udata mt/nil? */ - lua_assert(lua_istable(upi->L, -1) || lua_isnil(upi->L, -1)); - /* perms reftbl ... udata mt/nil */ - lua_setmetatable(upi->L, -2); - /* perms reftbl ... udata */ - } - /* perms reftbl ... udata */ -} - -static void unpersistpermanent(int ref, UnpersistInfo *upi) -{ - /* perms reftbl ... */ - lua_checkstack(upi->L, 2); - unpersist(upi); - /* perms reftbl permkey */ - lua_gettable(upi->L, 1); - /* perms reftbl perm? */ - /* We assume currently that the substituted permanent value - * shouldn't be nil. This may be a bad assumption. Real-life - * experience is needed to evaluate this. */ - lua_assert(!lua_isnil(upi->L, -1)); - /* perms reftbl perm */ -} - -#if 0 -/* For debugging only; not called when lua_assert is empty */ -static int inreftable(lua_State *L, int ref) -{ - int res; - lua_checkstack(L, 1); - /* perms reftbl ... */ - lua_pushlightuserdata(L, (void *)ref); - /* perms reftbl ... ref */ - lua_gettable(L, 2); - /* perms reftbl ... obj? */ - res = !lua_isnil(L, -1); - lua_pop(L, 1); - /* perms reftbl ... */ - return res; -} -#endif - -static void unpersist(UnpersistInfo *upi) -{ - /* perms reftbl ... */ - int firstTime; - int stacksize = lua_gettop(upi->L); stacksize = stacksize; /* DEBUG */ - lua_checkstack(upi->L, 2); - LIF(Z,read)(&upi->zio, &firstTime, sizeof(int)); - if(firstTime) { - int ref; - int type; - LIF(Z,read)(&upi->zio, &ref, sizeof(int)); - lua_assert(!inreftable(upi->L, ref)); - LIF(Z,read)(&upi->zio, &type, sizeof(int)); -#ifdef PLUTO_DEBUG - printindent(upi->level); - printf("1 %d %d\n", ref, type); - upi->level++; -#endif - switch(type) { - case LUA_TBOOLEAN: - unpersistboolean(upi); - break; - case LUA_TLIGHTUSERDATA: - unpersistlightuserdata(upi); - break; - case LUA_TNUMBER: - unpersistnumber(upi); - break; - case LUA_TSTRING: - unpersiststring(upi); - break; - case LUA_TTABLE: - unpersisttable(ref, upi); - break; - case LUA_TFUNCTION: - unpersistfunction(ref, upi); - break; - case LUA_TTHREAD: - unpersistthread(ref, upi); - break; - case LUA_TPROTO: - unpersistproto(ref, upi); - break; - case LUA_TUPVAL: - unpersistupval(ref, upi); - break; - case LUA_TUSERDATA: - unpersistuserdata(ref, upi); - break; - case PLUTO_TPERMANENT: - unpersistpermanent(ref, upi); - break; - default: - lua_assert(0); - } - /* perms reftbl ... obj */ - lua_assert(lua_type(upi->L, -1) == type || - type == PLUTO_TPERMANENT || - /* Remember, upvalues get a special dispensation, as - * described in boxupval */ - (lua_type(upi->L, -1) == LUA_TFUNCTION && - type == LUA_TUPVAL)); - registerobject(ref, upi); - /* perms reftbl ... obj */ -#ifdef PLUTO_DEBUG - upi->level--; -#endif - } else { - int ref; - LIF(Z,read)(&upi->zio, &ref, sizeof(int)); -#ifdef PLUTO_DEBUG - printindent(upi->level); - printf("0 %d\n", ref); -#endif - if(ref == 0) { - lua_pushnil(upi->L); - /* perms reftbl ... nil */ - } else { - lua_pushlightuserdata(upi->L, (void *)ref); - /* perms reftbl ... ref */ - lua_gettable(upi->L, 2); - /* perms reftbl ... obj? */ - lua_assert(!lua_isnil(upi->L, -1)); - } - /* perms reftbl ... obj/nil */ - } - /* perms reftbl ... obj/nil */ - lua_assert(lua_gettop(upi->L) == stacksize + 1); -} - -void pluto_unpersist(lua_State *L, lua_Chunkreader reader, void *ud) -{ - /* We use the graciously provided ZIO (what the heck does the Z stand - * for?) library so that we don't have to deal with the reader directly. - * Letting the reader function decide how much data to return can be - * very unpleasant. - */ - UnpersistInfo upi; - upi.L = L; -#ifdef PLUTO_DEBUG - upi.level = 0; -#endif - - lua_checkstack(L, 3); - LIF(Z,init)(L, &upi.zio, reader, ud); - - /* perms */ - lua_newtable(L); - /* perms reftbl */ - lua_gc(L, LUA_GCSTOP, 0); - unpersist(&upi); - lua_gc(L, LUA_GCRESTART, 0); - /* perms reftbl rootobj */ - lua_replace(L, 2); - /* perms rootobj */ -} - -typedef struct LoadInfo_t { - char *buf; - size_t size; -} LoadInfo; - - -static const char *bufreader(lua_State *L, void *ud, size_t *sz) { - LoadInfo *li = (LoadInfo *)ud; - if(li->size == 0) { - return NULL; - } - *sz = li->size; - li->size = 0; - return li->buf; -} - -int unpersist_l(lua_State *L) -{ - LoadInfo li; - char const *origbuf; - char *tempbuf; - size_t bufsize; - /* perms? str? ...? */ - lua_settop(L, 2); - /* perms? str? */ - origbuf = luaL_checklstring(L, 2, &bufsize); - tempbuf = LIF(M,newvector)(L, bufsize, char); - memcpy(tempbuf, origbuf, bufsize); - - li.buf = tempbuf; - li.size = bufsize; - - /* perms? str */ - lua_pop(L, 1); - /* perms? */ - luaL_checktype(L, 1, LUA_TTABLE); - /* perms */ - pluto_unpersist(L, bufreader, &li); - /* perms rootobj */ - LIF(M,freearray)(L, tempbuf, bufsize, char); - return 1; -} - -/* Stefan's first C function for Lua! :) - Returns a string describing the Pluto version you're using. */ - -int version_l(lua_State *L) -{ - const char *version = VERSION; - - lua_settop(L, 0); - /* (empty) */ - lua_pushlstring(L, version, strlen(version)); - /* str */ - return 1; -} - -/* Set human-readable output on or off. */ -int human_l(lua_State *L) -{ - /* flag? ...? */ - lua_settop(L, 1); - /* flag? */ - /*luaL_checktype(L, 1, LUA_TBOOLEAN);*/ - /* flag */ - - humanReadable = lua_toboolean(L, 1); - - lua_settop(L, 0); - /* (empty) */ - return 0; -} - -static luaL_reg pluto_reg[] = { - { "persist", persist_l }, - { "unpersist", unpersist_l }, - { "version", version_l }, - { "human", human_l }, - { NULL, NULL } -}; - -LUALIB_API int luaopen_pluto(lua_State *L) { - luaL_openlib(L, "pluto", pluto_reg, 0); - return 1; -} diff --git a/engines/sword25/util/pluto/pluto.h b/engines/sword25/util/pluto/pluto.h deleted file mode 100644 index 3674842d44..0000000000 --- a/engines/sword25/util/pluto/pluto.h +++ /dev/null @@ -1,25 +0,0 @@ -/* $Id$ */ - -/* Pluto - Heavy-duty persistence for Lua - * Copyright (C) 2004 by Ben Sunshine-Hill, and released into the public - * domain. People making use of this software as part of an application - * are politely requested to email the author at sneftel@gmail.com - * with a brief description of the application, primarily to satisfy his - * curiosity. - * - * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, - * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF - * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. - * IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY - * CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, - * TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE - * SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. - */ - -/* lua.h must be included before this file */ - -void pluto_persist(lua_State *L, lua_Chunkwriter writer, void *ud); - -void pluto_unpersist(lua_State *L, lua_Chunkreader reader, void *ud); - -LUALIB_API int luaopen_pluto(lua_State *L); diff --git a/engines/sword25/util/pluto/plzio.cpp b/engines/sword25/util/pluto/plzio.cpp deleted file mode 100644 index 21f69a9e8d..0000000000 --- a/engines/sword25/util/pluto/plzio.cpp +++ /dev/null @@ -1,74 +0,0 @@ -/* -** $Id$ -** a generic input stream interface -** See Copyright Notice in lua.h -*/ - - -#include <string.h> - -#define lzio_c -#define LUA_CORE - -#include "pdep/pdep.h" - -int pdep_fill (ZIO *z) { - size_t size; - lua_State *L = z->L; - const char *buff; - lua_unlock(L); - buff = z->reader(L, z->data, &size); - lua_lock(L); - if (buff == NULL || size == 0) return EOZ; - z->n = size - 1; - z->p = buff; - return char2int(*(z->p++)); -} - - -int pdep_lookahead (ZIO *z) { - if (z->n == 0) { - if (pdep_fill(z) == EOZ) - return EOZ; - else { - z->n++; /* pdep_fill removed first byte; put back it */ - z->p--; - } - } - return char2int(*z->p); -} - - -void pdep_init (lua_State *L, ZIO *z, lua_Reader reader, void *data) { - z->L = L; - z->reader = reader; - z->data = data; - z->n = 0; - z->p = NULL; -} - - -/* --------------------------------------------------------------- read --- */ -size_t pdep_read (ZIO *z, void *b, size_t n) { - while (n) { - size_t m; - if (pdep_lookahead(z) == EOZ) - return n; /* return number of missing bytes */ - m = (n <= z->n) ? n : z->n; /* min. between n and z->n */ - memcpy(b, z->p, m); - z->n -= m; - z->p += m; - b = (char *)b + m; - n -= m; - } - return 0; -} - -/* ------------------------------------------------------------------------ */ -char *pdep_openspace (lua_State *L, Mbuffer *buff, size_t n) { - if (n > buff->buffsize) { - if (n < LUA_MINBUFFER) n = LUA_MINBUFFER; - pdep_resizebuffer(L, buff, n); - } - return buff->buffer; -} |