int writeObjectRaw() { lua_State *L = m_L; uint8_t iType; // Save the index to the cache lua_pushvalue(L, 2); lua_pushnumber(L, (lua_Number)(m_iNextIndex++)); lua_settable(L, 1); // Lookup the object in the permanents table lua_pushvalue(L, 2); lua_gettable(L, luaT_upvalueindex(1)); if(lua_type(L, -1) != LUA_TNIL) { // Object is in the permanents table. uint8_t iType = PERSIST_TPERMANENT; writeByteStream(&iType, 1); // Replace self's environment with self (for call to writeStackObject) lua_pushvalue(L, luaT_upvalueindex(2)); lua_replace(L, 1); // Write the key corresponding to the permanent object writeStackObject(-1); } else { // Object is not in the permanents table. lua_pop(L, 1); switch(lua_type(L, 2)) { // LUA_TNIL handled in writeStackObject // LUA_TBOOLEAN handled in writeStackObject // LUA_TNUMBER handled in writeStackObject case LUA_TSTRING: { iType = LUA_TSTRING; writeByteStream(&iType, 1); // Strings are simple: length and then bytes (not null terminated) size_t iLength; const char *sString = lua_tolstring(L, 2, &iLength); writeVUInt(iLength); writeByteStream(reinterpret_cast<const uint8_t*>(sString), iLength); break; } case LUA_TTABLE: { // Replace self's environment with self (for calls to writeStackObject) lua_pushvalue(L, luaT_upvalueindex(2)); lua_replace(L, 1); // Save env and insert prior to table lua_getfenv(L, 1); lua_insert(L, 2); int iTable = 3; table_reentry: // Handle the metatable if(lua_getmetatable(L, iTable)) { iType = PERSIST_TTABLE_WITH_META; writeByteStream(&iType, 1); writeStackObject(-1); lua_pop(L, 1); } else { iType = LUA_TTABLE; writeByteStream(&iType, 1); } // Write the children as key, value pairs lua_pushnil(L); while(lua_next(L, iTable)) { writeStackObject(-2); // The naive thing to do now would be writeStackObject(-1) // but this can easily lead to Lua's C call stack limit // being hit. To reduce the likelyhood of this happening, // we check to see if about to write another table. if(lua_type(L, -1) == LUA_TTABLE) { lua_pushvalue(L, -1); lua_rawget(L, 2); lua_pushvalue(L, -2); lua_gettable(L, luaT_upvalueindex(1)); if(lua_isnil(L, -1) && lua_isnil(L, -2)) { lua_pop(L, 2); lua_checkstack(L, 10); iTable += 2; lua_pushvalue(L, iTable); lua_pushnumber(L, (lua_Number)(m_iNextIndex++)); lua_settable(L, 2); goto table_reentry; table_resume: iTable -= 2; } else { lua_pop(L, 2); writeStackObject(-1); } } else writeStackObject(-1); lua_pop(L, 1); } // Write a nil to mark the end of the children (as nil is the // only value which cannot be used as a key in a table). iType = LUA_TNIL; writeByteStream(&iType, 1); if(iTable != 3) goto table_resume; break; } case LUA_TFUNCTION: if(lua_iscfunction(L, 2)) { setErrorObject(2); setError("Cannot persist C functions"); } else { iType = LUA_TFUNCTION; writeByteStream(&iType, 1); // Replace self's environment with self (for calls to writeStackObject) lua_pushvalue(L, luaT_upvalueindex(2)); lua_replace(L, 1); // Write the prototype (the part of a function which is common across // multiple closures - see LClosure / Proto in Lua's lobject.h). lua_Debug proto_info; lua_pushvalue(L, 2); lua_getinfo(L, ">Su", &proto_info); writePrototype(&proto_info, 2); // Write the values of the upvalues // If available, also write the upvalue IDs (so that in // the future, we could hypothetically rejoin shared // upvalues). An ID is just an opaque sequence of bytes. writeVUInt(proto_info.nups); #if LUA_VERSION_NUM >= 502 writeVUInt(sizeof(void*)); #else writeVUInt(0); #endif for(int i = 1; i <= proto_info.nups; ++i) { lua_getupvalue(L, 2, i); writeStackObject(-1); #if LUA_VERSION_NUM >= 502 void *pUpvalueID = lua_upvalueid(L, 2, i); writeByteStream((uint8_t*)&pUpvalueID, sizeof(void*)); #endif } // Write the environment table lua_getfenv(L, 2); writeStackObject(-1); lua_pop(L, 1); } break; case LUA_TUSERDATA: if(!_checkThatUserdataCanBeDepersisted(2)) break; // Replace self's environment with self (for calls to writeStackObject) lua_pushvalue(L, luaT_upvalueindex(2)); lua_replace(L, 1); // Write type, metatable, and then environment iType = LUA_TUSERDATA; writeByteStream(&iType, 1); writeStackObject(-1); lua_getfenv(L, 2); writeStackObject(-1); lua_pop(L, 1); // Write the raw data if(lua_type(L, -1) == LUA_TTABLE) { lua_getfield(L, -1, "__persist"); if(lua_isnil(L, -1)) lua_pop(L, 1); else { lua_pushvalue(L, 2); lua_pushvalue(L, luaT_upvalueindex(2)); lua_call(L, 2, 0); } } writeVUInt((uint64_t)0x42); // sync marker break; default: setError(lua_pushfstring(L, "Cannot persist %s values", luaL_typename(L, 2))); break; } } lua_pushnumber(L, 0); return 1; }
static int db_upvalueid (lua_State *L) { int n = checkupval(L, 1, 2); lua_pushlightuserdata(L, lua_upvalueid(L, 1, n)); return 1; }
void * LuaDebug::UpvalueID(int funcindex, int n) const { return lua_upvalueid(L(), funcindex, n); }