From 37520bd18beeb6466c3c779a10eea35271fac167 Mon Sep 17 00:00:00 2001 From: Alex Szpakowski Date: Thu, 6 Oct 2016 21:59:01 -0300 Subject: [PATCH] =?UTF-8?q?Added=20Object:release.=20Calling=20it=20is=20t?= =?UTF-8?q?he=20equivalent=20of=20removing=20all=20Lua-side=20references?= =?UTF-8?q?=20to=20an=20object=20and=20doing=20a=20full=20garbage=20collec?= =?UTF-8?q?tion=20cycle=20(thus=20deleting=20the=20object=20from=20memory?= =?UTF-8?q?=20if=20nothing=20else=20in=20L=C3=96VE's=20code=20is=20referen?= =?UTF-8?q?cing=20it).?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Any attempt to call a method on the object after it has been released will result in an error. Object:release() returns true if it was successful, or false if not (if it has already been released previously). --HG-- branch : minor --- src/common/Variant.cpp | 12 ++++--- src/common/runtime.cpp | 40 +++++++++++++++++++++-- src/common/runtime.h | 10 +++++- src/modules/image/wrap_ImageData.lua | 6 ++++ src/modules/math/wrap_RandomGenerator.cpp | 2 +- src/modules/math/wrap_RandomGenerator.lua | 2 ++ src/modules/sound/wrap_SoundData.lua | 6 ++++ 7 files changed, 69 insertions(+), 9 deletions(-) diff --git a/src/common/Variant.cpp b/src/common/Variant.cpp index 8fd893f3f..e43cc7a72 100644 --- a/src/common/Variant.cpp +++ b/src/common/Variant.cpp @@ -85,7 +85,8 @@ Variant::Variant(love::Type udatatype, void *userdata) { Proxy *p = (Proxy *) userdata; data.userdata = p->object; - p->object->retain(); + if (p->object) + p->object->retain(); } else data.userdata = userdata; @@ -105,7 +106,7 @@ Variant::Variant(const Variant &v) { if (type == STRING) data.string->retain(); - else if (type == FUSERDATA) + else if (type == FUSERDATA && data.userdata != nullptr) ((love::Object *) data.userdata)->retain(); else if (type == TABLE) data.table->retain(); @@ -127,7 +128,8 @@ Variant::~Variant() data.string->release(); break; case FUSERDATA: - ((love::Object *) data.userdata)->release(); + if (data.userdata != nullptr) + ((love::Object *) data.userdata)->release(); break; case TABLE: data.table->release(); @@ -141,14 +143,14 @@ Variant &Variant::operator = (const Variant &v) { if (v.type == STRING) v.data.string->retain(); - else if (v.type == FUSERDATA) + else if (v.type == FUSERDATA && v.data.userdata != nullptr) ((love::Object *) v.data.userdata)->retain(); else if (v.type == TABLE) v.data.table->retain(); if (type == STRING) data.string->release(); - else if (type == FUSERDATA) + else if (type == FUSERDATA && v.data.userdata != nullptr) ((love::Object *) v.data.userdata)->release(); else if (type == TABLE) data.table->release(); diff --git a/src/common/runtime.cpp b/src/common/runtime.cpp index f6ecdebf3..477831bf3 100644 --- a/src/common/runtime.cpp +++ b/src/common/runtime.cpp @@ -42,7 +42,11 @@ namespace love static int w__gc(lua_State *L) { Proxy *p = (Proxy *) lua_touserdata(L, 1); - p->object->release(); + if (p->object != nullptr) + { + p->object->release(); + p->object = nullptr; + } return 0; } @@ -72,7 +76,35 @@ static int w__eq(lua_State *L) { Proxy *p1 = (Proxy *)lua_touserdata(L, 1); Proxy *p2 = (Proxy *)lua_touserdata(L, 2); - luax_pushboolean(L, p1->object == p2->object); + luax_pushboolean(L, p1->object == p2->object && p1->object != nullptr); + return 1; +} + +static int w__release(lua_State *L) +{ + Proxy *p = (Proxy *) lua_touserdata(L, 1); + Object *object = p->object; + + if (object != nullptr) + { + p->object = nullptr; + object->release(); + + // Fetch the registry table of instantiated objects. + luax_getregistry(L, REGISTRY_OBJECTS); + + if (lua_istable(L, -1)) + { + // loveobjects[object] = nil + lua_pushlightuserdata(L, object); + lua_pushnil(L); + lua_settable(L, -3); + } + + lua_pop(L, 1); + } + + luax_pushboolean(L, object != nullptr); return 1; } @@ -329,6 +361,10 @@ int luax_register_type(lua_State *L, love::Type type, const char *name, ...) lua_pushcfunction(L, w__typeOf); lua_setfield(L, -2, "typeOf"); + // Add release + lua_pushcfunction(L, w__release); + lua_setfield(L, -2, "release"); + va_list fs; va_start(fs, name); for (const luaL_Reg *f = va_arg(fs, const luaL_Reg *); f; f = va_arg(fs, const luaL_Reg *)) diff --git a/src/common/runtime.h b/src/common/runtime.h index 9072c99db..036c5764f 100644 --- a/src/common/runtime.h +++ b/src/common/runtime.h @@ -452,6 +452,9 @@ T *luax_checktype(lua_State *L, int idx, love::Type type) luax_typerror(L, idx, name); } + if (u->object == nullptr) + luaL_error(L, "Cannot use object after it has been released."); + return (T *)u->object; } @@ -513,7 +516,12 @@ T *luax_optmodule(lua_State *L, love::Type type) template T *luax_totype(lua_State *L, int idx, love::Type /*type*/) { - return (T *)(((Proxy *)lua_touserdata(L, idx))->object); + T *o = (T *)(((Proxy *)lua_touserdata(L, idx))->object); + + if (o == nullptr) + luaL_error(L, "Cannot use object after it has been released."); + + return o; } Type luax_type(lua_State *L, int idx); diff --git a/src/modules/image/wrap_ImageData.lua b/src/modules/image/wrap_ImageData.lua index e5ca26660..53ba0e964 100644 --- a/src/modules/image/wrap_ImageData.lua +++ b/src/modules/image/wrap_ImageData.lua @@ -161,6 +161,7 @@ local _getWidth = ImageData.getWidth local _getHeight = ImageData.getHeight local _getDimensions = ImageData.getDimensions local _getFormat = ImageData.getFormat +local _release = ImageData.release -- Table which holds ImageData objects as keys, and information about the objects -- as values. Uses weak keys so the ImageData objects can still be GC'd properly. @@ -270,5 +271,10 @@ function ImageData:getFormat() return objectcache[self].format end +function ImageData:release() + objectcache[self] = nil + return _release(self) +end + -- DO NOT REMOVE THE NEXT LINE. It is used to load this file as a C++ string. --)luastring"--" diff --git a/src/modules/math/wrap_RandomGenerator.cpp b/src/modules/math/wrap_RandomGenerator.cpp index 32b0e10fe..ffd38f18d 100644 --- a/src/modules/math/wrap_RandomGenerator.cpp +++ b/src/modules/math/wrap_RandomGenerator.cpp @@ -126,7 +126,7 @@ static FFI_RandomGenerator ffifuncs = [](Proxy *p) -> double // random() { // FIXME: We need better type-checking... - if (p == nullptr || !typeFlags[p->type][MATH_RANDOM_GENERATOR_ID]) + if (p == nullptr || p->object == nullptr || !typeFlags[p->type][MATH_RANDOM_GENERATOR_ID]) return 0.0; RandomGenerator *rng = (RandomGenerator *) p->object; diff --git a/src/modules/math/wrap_RandomGenerator.lua b/src/modules/math/wrap_RandomGenerator.lua index fc9ca678e..eda7b77ec 100644 --- a/src/modules/math/wrap_RandomGenerator.lua +++ b/src/modules/math/wrap_RandomGenerator.lua @@ -72,6 +72,8 @@ local ffifuncs = ffi.cast("FFI_RandomGenerator *", ffifuncspointer) -- Overwrite some regular love.math functions with FFI implementations. function RandomGenerator:random(l, u) + -- TODO: This should ideally be handled inside ffifuncs.random + if self == nil then error("bad argument #1 to 'random' (RandomGenerator expected, got no value)", 2) end local r = tonumber(ffifuncs.random(self)) return getrandom(r, l, u) end diff --git a/src/modules/sound/wrap_SoundData.lua b/src/modules/sound/wrap_SoundData.lua index 803a9a2d1..023236a17 100644 --- a/src/modules/sound/wrap_SoundData.lua +++ b/src/modules/sound/wrap_SoundData.lua @@ -46,6 +46,7 @@ local _getSampleCount = SoundData.getSampleCount local _getSampleRate = SoundData.getSampleRate local _getChannels = SoundData.getChannels local _getDuration = SoundData.getDuration +local _release = SoundData.release -- Table which holds SoundData objects as keys, and information about the objects -- as values. Uses weak keys so the SoundData objects can still be GC'd properly. @@ -133,5 +134,10 @@ function SoundData:getDuration() return objectcache[self].duration end +function SoundData:release() + objectcache[self] = nil + return _release(self) +end + -- DO NOT REMOVE THE NEXT LINE. It is used to load this file as a C++ string. --)luastring"--"