Browse Source

The Variant class is now meant to be used on the stack instead of allocated on the heap. As a result (and because of related changes), performance of Channels has roughly doubled when pushing and popping most types.

Alex Szpakowski 9 years ago
parent
commit
23fcaec89f

+ 104 - 81
src/common/Variant.cpp

@@ -26,35 +26,21 @@ namespace love
 
 static love::Type extractudatatype(lua_State *L, int idx)
 {
-	Type t = INVALID_ID;
-	if (!lua_isuserdata(L, idx))
-		return t;
-	if (luaL_getmetafield(L, idx, "type") == 0)
-		return t;
-	lua_pushvalue(L, idx);
-	int result = lua_pcall(L, 1, 1, 0);
-	if (result == 0)
-		getTypeName(lua_tostring(L, -1), t);
-	if (result == 0 || result == LUA_ERRRUN)
-		lua_pop(L, 1);
-	return t;
-}
+	Proxy *u = (Proxy *)lua_touserdata(L, idx);
 
-static inline void delete_table(std::vector<std::pair<Variant*, Variant*> > *table)
-{
-	while (!table->empty())
-	{
-		std::pair<Variant*, Variant*> &kv = table->back();
-		kv.first->release();
-		kv.second->release();
-		table->pop_back();
-	}
-	delete table;
+	if (u == nullptr || u->type <= INVALID_ID || u->type >= TYPE_MAX_ENUM)
+		return INVALID_ID;
+
+	// We could get rid of the dynamic_cast for more performance, but it would
+	// be less safe...
+	if (dynamic_cast<Object *>(u->object) != nullptr)
+		return u->type;
+
+	return INVALID_ID;
 }
 
 Variant::Variant()
 	: type(NIL)
-	, data()
 {
 }
 
@@ -71,19 +57,18 @@ Variant::Variant(double number)
 }
 
 Variant::Variant(const char *string, size_t len)
-	: type(STRING)
 {
-	char *buf = new char[len+1];
-	memset(buf, 0, len+1);
-	memcpy(buf, string, len);
-	data.string.str = buf;
-	data.string.len = len;
-}
-
-Variant::Variant(char c)
-	: type(CHARACTER)
-{
-	data.character = c;
+	if (len <= MAX_SMALL_STRING_LENGTH)
+	{
+		type = SMALLSTRING;
+		memcpy(data.smallstring.str, string, len);
+		data.smallstring.len = (uint8) len;
+	}
+	else
+	{
+		type = STRING;
+		data.string = new SharedString(string, len);
+	}
 }
 
 Variant::Variant(void *userdata)
@@ -107,10 +92,23 @@ Variant::Variant(love::Type udatatype, void *userdata)
 }
 
 // Variant gets ownership of the vector.
-Variant::Variant(std::vector<std::pair<Variant*, Variant*> > *table)
+Variant::Variant(std::vector<std::pair<Variant, Variant>> *table)
 	: type(TABLE)
 {
-	data.table = table;
+	data.table = new SharedTable(table);
+}
+
+Variant::Variant(const Variant &v)
+	: type(v.type)
+	, data(v.data)
+	, udatatype(v.udatatype)
+{
+	if (type == STRING)
+		data.string->retain();
+	else if (type == FUSERDATA)
+		((love::Object *) data.userdata)->retain();
+	else if (type == TABLE)
+		data.table->retain();
 }
 
 Variant::~Variant()
@@ -118,102 +116,122 @@ Variant::~Variant()
 	switch (type)
 	{
 	case STRING:
-		delete[] data.string.str;
+		data.string->release();
 		break;
 	case FUSERDATA:
 		((love::Object *) data.userdata)->release();
 		break;
 	case TABLE:
-		delete_table(data.table);
+		data.table->release();
+		break;
 	default:
 		break;
 	}
 }
 
-Variant *Variant::fromLua(lua_State *L, int n, bool allowTables)
+Variant &Variant::operator = (const Variant &v)
+{
+	if (v.type == STRING)
+		v.data.string->retain();
+	else if (v.type == FUSERDATA)
+		((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)
+		((love::Object *) v.data.userdata)->release();
+	else if (type == TABLE)
+		data.table->release();
+
+	type = v.type;
+	data = v.data;
+	udatatype = v.udatatype;
+
+	return *this;
+}
+
+bool Variant::fromLua(lua_State *L, int n, Variant *v, bool allowTables)
 {
-	Variant *v = nullptr;
 	size_t len;
 	const char *str;
+
 	if (n < 0) // Fix the stack position, we might modify it later
 		n += lua_gettop(L) + 1;
 
 	switch (lua_type(L, n))
 	{
 	case LUA_TBOOLEAN:
-		v = new Variant(luax_toboolean(L, n));
-		break;
+		*v = Variant(luax_toboolean(L, n));
+		return true;
 	case LUA_TNUMBER:
-		v = new Variant(lua_tonumber(L, n));
-		break;
+		*v = Variant(lua_tonumber(L, n));
+		return true;
 	case LUA_TSTRING:
 		str = lua_tolstring(L, n, &len);
-		v = new Variant(str, len);
-		break;
+		*v = Variant(str, len);
+		return true;
 	case LUA_TLIGHTUSERDATA:
-		v = new Variant(lua_touserdata(L, n));
-		break;
+		*v = Variant(lua_touserdata(L, n));
+		return true;
 	case LUA_TUSERDATA:
-		v = new Variant(extractudatatype(L, n), lua_touserdata(L, n));
-		break;
+		*v = Variant(extractudatatype(L, n), lua_touserdata(L, n));
+		return true;
 	case LUA_TNIL:
-		v = new Variant();
-		break;
+		*v = Variant();
+		return true;
 	case LUA_TTABLE:
 		if (allowTables)
 		{
 			bool success = true;
-			std::vector<std::pair<Variant*, Variant*>> *table = new std::vector<std::pair<Variant*, Variant*>>();
+			std::vector<std::pair<Variant, Variant>> *table = new std::vector<std::pair<Variant, Variant>>();
+			std::pair<Variant, Variant> pair;
+
 			lua_pushnil(L);
+
 			while (lua_next(L, n))
 			{
-				Variant *key = fromLua(L, -2, false);
-				if (!key)
-				{
-					success = false;
-					lua_pop(L, 2);
-					break;
-				}
-
-				Variant *value = fromLua(L, -1, false);
-				if (!value)
+				if (!fromLua(L, -2, &pair.first, false) || !fromLua(L, -1, &pair.second, false))
 				{
-					delete key;
 					success = false;
 					lua_pop(L, 2);
 					break;
 				}
 
-				table->push_back(std::make_pair(key, value));
-
+				table->push_back(pair);
 				lua_pop(L, 1);
 			}
 
 			if (success)
-				v = new Variant(table);
+			{
+				*v = Variant(table);
+				return true;
+			}
 			else
-				delete_table(table);
+				delete table;
 		}
 		break;
 	}
-	return v;
+
+	return false;
 }
 
-void Variant::toLua(lua_State *L)
+void Variant::toLua(lua_State *L) const
 {
 	switch (type)
 	{
 	case BOOLEAN:
 		lua_pushboolean(L, data.boolean);
 		break;
-	case CHARACTER:
-		lua_pushlstring(L, &data.character, 1);
-		break;
 	case NUMBER:
 		lua_pushnumber(L, data.number);
 		break;
 	case STRING:
-		lua_pushlstring(L, data.string.str, data.string.len);
+		lua_pushlstring(L, data.string->str, data.string->len);
+		break;
+	case SMALLSTRING:
+		lua_pushlstring(L, data.smallstring.str, data.smallstring.len);
 		break;
 	case LUSERDATA:
 		lua_pushlightuserdata(L, data.userdata);
@@ -228,15 +246,20 @@ void Variant::toLua(lua_State *L)
 		// I can do (at the moment).
 		break;
 	case TABLE:
-		lua_createtable(L, 0, (int) data.table->size());
-		for (size_t i = 0; i < data.table->size(); ++i)
+	{
+		std::vector<std::pair<Variant, Variant>> *table = data.table->table;
+		lua_createtable(L, 0, (int) table->size());
+
+		for (size_t i = 0; i < table->size(); ++i)
 		{
-			std::pair<Variant*, Variant*> &kv = data.table->at(i);
-			kv.first->toLua(L);
-			kv.second->toLua(L);
+			std::pair<Variant, Variant> &kv = table->at(i);
+			kv.first.toLua(L);
+			kv.second.toLua(L);
 			lua_settable(L, -3);
 		}
+
 		break;
+	}
 	case NIL:
 	default:
 		lua_pushnil(L);

+ 52 - 16
src/common/Variant.h

@@ -23,15 +23,15 @@
 
 #include "common/runtime.h"
 #include "common/Object.h"
+#include "common/int.h"
 
 #include <cstring>
 #include <vector>
-#include <utility>
 
 namespace love
 {
 
-class Variant : public love::Object
+class Variant
 {
 public:
 
@@ -39,42 +39,78 @@ public:
 	Variant(bool boolean);
 	Variant(double number);
 	Variant(const char *string, size_t len);
-	Variant(char c);
 	Variant(void *userdata);
 	Variant(love::Type udatatype, void *userdata);
-	Variant(std::vector<std::pair<Variant*, Variant*> > *table);
-	virtual ~Variant();
+	Variant(std::vector<std::pair<Variant, Variant>> *table);
+	Variant(const Variant &v);
+	~Variant();
 
-	static Variant *fromLua(lua_State *L, int n, bool allowTables = true);
-	void toLua(lua_State *L);
+	Variant &operator = (const Variant &v);
+
+	static bool fromLua(lua_State *L, int n, Variant *v, bool allowTables = true);
+	void toLua(lua_State *L) const;
+
+private:
+
+	class SharedString : public love::Object
+	{
+	public:
+
+		SharedString(const char *string, size_t len)
+			: len(len)
+		{
+			str = new char[len+1];
+			memcpy(str, string, len);
+		}
+		virtual ~SharedString() { delete[] str; }
+
+		char *str;
+		size_t len;
+	};
+
+	class SharedTable : public love::Object
+	{
+	public:
+
+		SharedTable(std::vector<std::pair<Variant, Variant>> *table)
+			: table(table)
+		{
+		}
+
+		virtual ~SharedTable() { delete table; }
+
+		std::vector<std::pair<Variant, Variant>> *table;
+	};
 
 	enum Type
 	{
 		UNKNOWN = 0,
 		BOOLEAN,
 		NUMBER,
-		CHARACTER,
 		STRING,
+		SMALLSTRING,
 		LUSERDATA,
 		FUSERDATA,
 		NIL,
 		TABLE
 	} type;
-	union
+
+	static const int MAX_SMALL_STRING_LENGTH = 15;
+
+	union Data
 	{
 		bool boolean;
-		char character;
 		double number;
+		SharedString *string;
+		void *userdata;
+		SharedTable *table;
 		struct
 		{
-			const char *str;
-			size_t len;
-		} string;
-		void *userdata;
-		std::vector<std::pair<Variant*, Variant*>> *table;
+			char str[MAX_SMALL_STRING_LENGTH];
+			uint8 len;
+		} smallstring;
 	} data;
 
-private:
 	love::Type udatatype;
 
 }; // Variant

+ 2 - 2
src/common/runtime.h

@@ -445,7 +445,7 @@ T *luax_checktype(lua_State *L, int idx, love::Type type)
 
 	Proxy *u = (Proxy *)lua_touserdata(L, idx);
 
-	if (!typeFlags[u->type][type])
+	if (u->type <= INVALID_ID || u->type >= TYPE_MAX_ENUM || !typeFlags[u->type][type])
 	{
 		const char *name = "Invalid";
 		getTypeName(type, name);
@@ -469,7 +469,7 @@ T *luax_getmodule(lua_State *L, love::Type type)
 
 	Proxy *u = (Proxy *)lua_touserdata(L, -1);
 
-	if (!typeFlags[u->type][type])
+	if (u->type <= INVALID_ID || u->type >= TYPE_MAX_ENUM || !typeFlags[u->type][type])
 		luaL_error(L, "Incorrect module %s", name);
 
 	lua_pop(L, 2);

+ 8 - 13
src/modules/event/Event.cpp

@@ -28,7 +28,7 @@ namespace love
 namespace event
 {
 
-Message::Message(const std::string &name, const std::vector<StrongRef<Variant>> &vargs)
+Message::Message(const std::string &name, const std::vector<Variant> &vargs)
 	: name(name)
 	, args(vargs)
 {
@@ -42,13 +42,8 @@ int Message::toLua(lua_State *L)
 {
 	luax_pushstring(L, name);
 
-	for (const StrongRef<Variant> &v : args)
-	{
-		if (v.get() != nullptr)
-			v->toLua(L);
-		else
-			lua_pushnil(L);
-	}
+	for (const Variant &v : args)
+		v.toLua(L);
 
 	return (int) args.size() + 1;
 }
@@ -56,26 +51,26 @@ int Message::toLua(lua_State *L)
 Message *Message::fromLua(lua_State *L, int n)
 {
 	std::string name = luax_checkstring(L, n);
-	std::vector<StrongRef<Variant>> vargs;
+	std::vector<Variant> vargs;
 
 	int count = lua_gettop(L) - n;
 	n++;
 
+	Variant varg;
+
 	for (int i = 0; i < count; i++)
 	{
 		if (lua_isnoneornil(L, n+i))
 			break;
 
-		vargs.push_back(Variant::fromLua(L, n+i));
-
-		if (!vargs.back().get())
+		if (!Variant::fromLua(L, n+i, &varg))
 		{
 			vargs.clear();
 			luaL_error(L, "Argument %d can't be stored safely\nExpected boolean, number, string or userdata.", n+i);
 			return nullptr;
 		}
 
-		vargs.back()->release(); // fromLua gave a +1 ref.
+		vargs.push_back(varg);
 	}
 
 	return new Message(name, vargs);

+ 2 - 2
src/modules/event/Event.h

@@ -43,7 +43,7 @@ class Message : public Object
 {
 public:
 
-	Message(const std::string &name, const std::vector<StrongRef<Variant>> &vargs = {});
+	Message(const std::string &name, const std::vector<Variant> &vargs = {});
 	~Message();
 
 	int toLua(lua_State *L);
@@ -52,7 +52,7 @@ public:
 private:
 
 	std::string name;
-	std::vector<StrongRef<Variant>> args;
+	std::vector<Variant> args;
 
 }; // Message
 

+ 53 - 74
src/modules/event/sdl/Event.cpp

@@ -150,7 +150,7 @@ Message *Event::convert(const SDL_Event &e) const
 {
 	Message *msg = nullptr;
 
-	std::vector<StrongRef<Variant>> vargs;
+	std::vector<Variant> vargs;
 	vargs.reserve(4);
 
 	love::filesystem::Filesystem *filesystem = nullptr;
@@ -192,9 +192,9 @@ Message *Event::convert(const SDL_Event &e) const
 		if (!love::keyboard::Keyboard::getConstant(scancode, txt2))
 			txt2 = "unknown";
 
-		vargs.push_back(new Variant(txt, strlen(txt)));
-		vargs.push_back(new Variant(txt2, strlen(txt2)));
-		vargs.push_back(new Variant(e.key.repeat != 0));
+		vargs.emplace_back(txt, strlen(txt));
+		vargs.emplace_back(txt2, strlen(txt2));
+		vargs.emplace_back(e.key.repeat != 0);
 		msg = new Message("keypressed", vargs);
 		break;
 	case SDL_KEYUP:
@@ -209,20 +209,20 @@ Message *Event::convert(const SDL_Event &e) const
 		if (!love::keyboard::Keyboard::getConstant(scancode, txt2))
 			txt2 = "unknown";
 
-		vargs.push_back(new Variant(txt, strlen(txt)));
-		vargs.push_back(new Variant(txt2, strlen(txt2)));
+		vargs.emplace_back(txt, strlen(txt));
+		vargs.emplace_back(txt2, strlen(txt2));
 		msg = new Message("keyreleased", vargs);
 		break;
 	case SDL_TEXTINPUT:
 		txt = e.text.text;
-		vargs.push_back(new Variant(txt, strlen(txt)));
+		vargs.emplace_back(txt, strlen(txt));
 		msg = new Message("textinput", vargs);
 		break;
 	case SDL_TEXTEDITING:
 		txt = e.edit.text;
-		vargs.push_back(new Variant(txt, strlen(txt)));
-		vargs.push_back(new Variant((double) e.edit.start));
-		vargs.push_back(new Variant((double) e.edit.length));
+		vargs.emplace_back(txt, strlen(txt));
+		vargs.emplace_back((double) e.edit.start);
+		vargs.emplace_back((double) e.edit.length);
 		msg = new Message("textedited", vargs);
 		break;
 	case SDL_MOUSEMOTION:
@@ -233,11 +233,11 @@ Message *Event::convert(const SDL_Event &e) const
 			double yrel = (double) e.motion.yrel;
 			windowToPixelCoords(&x, &y);
 			windowToPixelCoords(&xrel, &yrel);
-			vargs.push_back(new Variant(x));
-			vargs.push_back(new Variant(y));
-			vargs.push_back(new Variant(xrel));
-			vargs.push_back(new Variant(yrel));
-			vargs.push_back(new Variant(e.motion.which == SDL_TOUCH_MOUSEID));
+			vargs.emplace_back(x);
+			vargs.emplace_back(y);
+			vargs.emplace_back(xrel);
+			vargs.emplace_back(yrel);
+			vargs.emplace_back(e.motion.which == SDL_TOUCH_MOUSEID);
 			msg = new Message("mousemoved", vargs);
 		}
 		break;
@@ -260,18 +260,18 @@ Message *Event::convert(const SDL_Event &e) const
 			double x = (double) e.button.x;
 			double y = (double) e.button.y;
 			windowToPixelCoords(&x, &y);
-			vargs.push_back(new Variant(x));
-			vargs.push_back(new Variant(y));
-			vargs.push_back(new Variant((double) button));
-			vargs.push_back(new Variant(e.button.which == SDL_TOUCH_MOUSEID));
+			vargs.emplace_back(x);
+			vargs.emplace_back(y);
+			vargs.emplace_back((double) button);
+			vargs.emplace_back(e.button.which == SDL_TOUCH_MOUSEID);
 			msg = new Message((e.type == SDL_MOUSEBUTTONDOWN) ?
 							  "mousepressed" : "mousereleased",
 							  vargs);
 		}
 		break;
 	case SDL_MOUSEWHEEL:
-		vargs.push_back(new Variant((double) e.wheel.x));
-		vargs.push_back(new Variant((double) e.wheel.y));
+		vargs.emplace_back((double) e.wheel.x);
+		vargs.emplace_back((double) e.wheel.y);
 		msg = new Message("wheelmoved", vargs);
 		break;
 	case SDL_FINGERDOWN:
@@ -316,12 +316,12 @@ Message *Event::convert(const SDL_Event &e) const
 		// bits as can fit in a pointer (for now.)
 		// We use lightuserdata instead of a lua_Number (double) because doubles
 		// can't represent all possible id values on 64-bit systems.
-		vargs.push_back(new Variant((void *) (intptr_t) touchinfo.id));
-		vargs.push_back(new Variant(touchinfo.x));
-		vargs.push_back(new Variant(touchinfo.y));
-		vargs.push_back(new Variant(touchinfo.dx));
-		vargs.push_back(new Variant(touchinfo.dy));
-		vargs.push_back(new Variant(touchinfo.pressure));
+		vargs.emplace_back((void *) (intptr_t) touchinfo.id);
+		vargs.emplace_back(touchinfo.x);
+		vargs.emplace_back(touchinfo.y);
+		vargs.emplace_back(touchinfo.dx);
+		vargs.emplace_back(touchinfo.dy);
+		vargs.emplace_back(touchinfo.pressure);
 
 		if (e.type == SDL_FINGERDOWN)
 			txt = "touchpressed";
@@ -356,7 +356,7 @@ Message *Event::convert(const SDL_Event &e) const
 
 			if (filesystem->isRealDirectory(e.drop.file))
 			{
-				vargs.push_back(new Variant(e.drop.file, strlen(e.drop.file)));
+				vargs.emplace_back(e.drop.file, strlen(e.drop.file));
 				msg = new Message("directorydropped", vargs);
 			}
 			else
@@ -364,7 +364,7 @@ Message *Event::convert(const SDL_Event &e) const
 				Proxy proxy;
 				proxy.object = new love::filesystem::DroppedFile(e.drop.file);
 				proxy.type = FILESYSTEM_DROPPED_FILE_ID;
-				vargs.push_back(new Variant(proxy.type, &proxy));
+				vargs.emplace_back(proxy.type, &proxy);
 				msg = new Message("filedropped", vargs);
 				proxy.object->release();
 			}
@@ -382,13 +382,6 @@ Message *Event::convert(const SDL_Event &e) const
 		break;
 	}
 
-	// We gave +1 refs to the StrongRef list, so we should release them.
-	for (const StrongRef<Variant> &v : vargs)
-	{
-		if (v.get() != nullptr)
-			v->release();
-	}
-
 	return msg;
 }
 
@@ -400,7 +393,7 @@ Message *Event::convertJoystickEvent(const SDL_Event &e) const
 
 	Message *msg = nullptr;
 
-	std::vector<StrongRef<Variant>> vargs;
+	std::vector<Variant> vargs;
 	vargs.reserve(4);
 
 	Proxy proxy;
@@ -418,8 +411,8 @@ Message *Event::convertJoystickEvent(const SDL_Event &e) const
 		if (!proxy.object)
 			break;
 
-		vargs.push_back(new Variant(proxy.type, (void *) &proxy));
-		vargs.push_back(new Variant((double)(e.jbutton.button+1)));
+		vargs.emplace_back(proxy.type, (void *) &proxy);
+		vargs.emplace_back((double)(e.jbutton.button+1));
 		msg = new Message((e.type == SDL_JOYBUTTONDOWN) ?
 						  "joystickpressed" : "joystickreleased",
 						  vargs);
@@ -431,10 +424,10 @@ Message *Event::convertJoystickEvent(const SDL_Event &e) const
 			if (!proxy.object)
 				break;
 
-			vargs.push_back(new Variant(proxy.type, (void *) &proxy));
-			vargs.push_back(new Variant((double)(e.jaxis.axis+1)));
+			vargs.emplace_back(proxy.type, (void *) &proxy);
+			vargs.emplace_back((double)(e.jaxis.axis+1));
 			float value = joystick::Joystick::clampval(e.jaxis.value / 32768.0f);
-			vargs.push_back(new Variant((double) value));
+			vargs.emplace_back((double) value);
 			msg = new Message("joystickaxis", vargs);
 		}
 		break;
@@ -447,9 +440,9 @@ Message *Event::convertJoystickEvent(const SDL_Event &e) const
 		if (!proxy.object)
 			break;
 
-		vargs.push_back(new Variant(proxy.type, (void *) &proxy));
-		vargs.push_back(new Variant((double)(e.jhat.hat+1)));
-		vargs.push_back(new Variant(txt, strlen(txt)));
+		vargs.emplace_back(proxy.type, (void *) &proxy);
+		vargs.emplace_back((double)(e.jhat.hat+1));
+		vargs.emplace_back(txt, strlen(txt));
 		msg = new Message("joystickhat", vargs);
 		break;
 	case SDL_CONTROLLERBUTTONDOWN:
@@ -465,8 +458,8 @@ Message *Event::convertJoystickEvent(const SDL_Event &e) const
 		if (!proxy.object)
 			break;
 
-		vargs.push_back(new Variant(proxy.type, (void *) &proxy));
-		vargs.push_back(new Variant(txt, strlen(txt)));
+		vargs.emplace_back(proxy.type, (void *) &proxy);
+		vargs.emplace_back(txt, strlen(txt));
 		msg = new Message(e.type == SDL_CONTROLLERBUTTONDOWN ?
 						  "gamepadpressed" : "gamepadreleased", vargs);
 		break;
@@ -481,11 +474,11 @@ Message *Event::convertJoystickEvent(const SDL_Event &e) const
 			if (!proxy.object)
 				break;
 
-			vargs.push_back(new Variant(proxy.type, (void *) &proxy));
+			vargs.emplace_back(proxy.type, (void *) &proxy);
 
-			vargs.push_back(new Variant(txt, strlen(txt)));
+			vargs.emplace_back(txt, strlen(txt));
 			float value = joystick::Joystick::clampval(e.caxis.value / 32768.0f);
-			vargs.push_back(new Variant((double) value));
+			vargs.emplace_back((double) value);
 			msg = new Message("gamepadaxis", vargs);
 		}
 		break;
@@ -495,7 +488,7 @@ Message *Event::convertJoystickEvent(const SDL_Event &e) const
 		proxy.type = JOYSTICK_JOYSTICK_ID;
 		if (proxy.object)
 		{
-			vargs.push_back(new Variant(proxy.type, (void *) &proxy));
+			vargs.emplace_back(proxy.type, (void *) &proxy);
 			msg = new Message("joystickadded", vargs);
 		}
 		break;
@@ -506,7 +499,7 @@ Message *Event::convertJoystickEvent(const SDL_Event &e) const
 		if (proxy.object)
 		{
 			joymodule->removeJoystick((joystick::Joystick *) proxy.object);
-			vargs.push_back(new Variant(proxy.type, (void *) &proxy));
+			vargs.emplace_back(proxy.type, (void *) &proxy);
 			msg = new Message("joystickremoved", vargs);
 		}
 		break;
@@ -514,13 +507,6 @@ Message *Event::convertJoystickEvent(const SDL_Event &e) const
 		break;
 	}
 
-	// We gave +1 refs to the StrongRef list, so we should release them.
-	for (const StrongRef<Variant> &v : vargs)
-	{
-		if (v.get() != nullptr)
-			v->release();
-	}
-
 	return msg;
 }
 
@@ -528,7 +514,7 @@ Message *Event::convertWindowEvent(const SDL_Event &e) const
 {
 	Message *msg = nullptr;
 
-	std::vector<StrongRef<Variant>> vargs;
+	std::vector<Variant> vargs;
 	vargs.reserve(4);
 
 	window::Window *win = nullptr;
@@ -540,17 +526,17 @@ Message *Event::convertWindowEvent(const SDL_Event &e) const
 	{
 	case SDL_WINDOWEVENT_FOCUS_GAINED:
 	case SDL_WINDOWEVENT_FOCUS_LOST:
-		vargs.push_back(new Variant(e.window.event == SDL_WINDOWEVENT_FOCUS_GAINED));
+		vargs.emplace_back(e.window.event == SDL_WINDOWEVENT_FOCUS_GAINED);
 		msg = new Message("focus", vargs);
 		break;
 	case SDL_WINDOWEVENT_ENTER:
 	case SDL_WINDOWEVENT_LEAVE:
-		vargs.push_back(new Variant(e.window.event == SDL_WINDOWEVENT_ENTER));
+		vargs.emplace_back(e.window.event == SDL_WINDOWEVENT_ENTER);
 		msg = new Message("mousefocus", vargs);
 		break;
 	case SDL_WINDOWEVENT_SHOWN:
 	case SDL_WINDOWEVENT_HIDDEN:
-		vargs.push_back(new Variant(e.window.event == SDL_WINDOWEVENT_SHOWN));
+		vargs.emplace_back(e.window.event == SDL_WINDOWEVENT_SHOWN);
 		msg = new Message("visible", vargs);
 		break;
 	case SDL_WINDOWEVENT_RESIZED:
@@ -562,10 +548,10 @@ Message *Event::convertWindowEvent(const SDL_Event &e) const
 			if (sdlwin)
 				SDL_GL_GetDrawableSize(sdlwin, &px_w, &px_h);
 
-			vargs.push_back(new Variant((double) px_w));
-			vargs.push_back(new Variant((double) px_h));
-			vargs.push_back(new Variant((double) e.window.data1));
-			vargs.push_back(new Variant((double) e.window.data2));
+			vargs.emplace_back((double) px_w);
+			vargs.emplace_back((double) px_h);
+			vargs.emplace_back((double) e.window.data1);
+			vargs.emplace_back((double) e.window.data2);
 			msg = new Message("resize", vargs);
 		}
 		break;
@@ -591,13 +577,6 @@ Message *Event::convertWindowEvent(const SDL_Event &e) const
 		break;
 	}
 
-	// We gave +1 refs to the StrongRef list, so we should release them.
-	for (const StrongRef<Variant> &v : vargs)
-	{
-		if (v.get() != nullptr)
-			v->release();
-	}
-
 	return msg;
 }
 

+ 3 - 6
src/modules/event/wrap_Event.cpp

@@ -95,14 +95,11 @@ int w_clear(lua_State *)
 
 int w_quit(lua_State *L)
 {
-	std::vector<StrongRef<Variant>> args;
+	std::vector<Variant> args;
 
-	Variant *v = Variant::fromLua(L, 1);
-	if (v)
-	{
+	Variant v;
+	if (Variant::fromLua(L, 1, &v))
 		args.push_back(v);
-		v->release();
-	}
 
 	Message *m = new Message("quit", args);
 	instance()->push(m);

+ 16 - 33
src/modules/thread/Channel.cpp

@@ -88,12 +88,6 @@ Channel::Channel(const std::string &name)
 
 Channel::~Channel()
 {
-	while (!queue.empty())
-	{
-		queue.front()->release();
-		queue.pop();
-	}
-
 	delete mutex;
 	delete cond;
 
@@ -101,13 +95,9 @@ Channel::~Channel()
 		namedChannels.erase(name);
 }
 
-unsigned long Channel::push(Variant *var)
+unsigned long Channel::push(const Variant &var)
 {
-	if (!var)
-		return 0;
-
 	Lock l(mutex);
-	var->retain();
 
 	// Keep a reference to ourselves
 	// if we're non-empty and named.
@@ -120,11 +110,8 @@ unsigned long Channel::push(Variant *var)
 	return ++sent;
 }
 
-void Channel::supply(Variant *var)
+void Channel::supply(const Variant &var)
 {
-	if (!var)
-		return;
-
 	Lock l(mutex);
 	unsigned long id = push(var);
 
@@ -132,13 +119,14 @@ void Channel::supply(Variant *var)
 		cond->wait(mutex);
 }
 
-Variant *Channel::pop()
+bool Channel::pop(Variant *var)
 {
 	Lock l(mutex);
+
 	if (queue.empty())
-		return 0;
+		return false;
 
-	Variant *var = queue.front();
+	*var = queue.front();
 	queue.pop();
 
 	received++;
@@ -149,28 +137,26 @@ Variant *Channel::pop()
 	if (named && queue.empty())
 		release();
 
-	return var;
-} // NOTE: Returns a retained Variant
+	return true;
+}
 
-Variant *Channel::demand()
+void Channel::demand(Variant *var)
 {
-	Variant *var;
 	Lock l(mutex);
-	while (!(var = pop()))
-		cond->wait(mutex);
 
-	return var;
+	while (!pop(var))
+		cond->wait(mutex);
 }
 
-Variant *Channel::peek()
+bool Channel::peek(Variant *var)
 {
 	Lock l(mutex);
+
 	if (queue.empty())
-		return 0;
+		return false;
 
-	Variant *var = queue.front();
-	var->retain();
-	return var;
+	*var = queue.front();
+	return true;
 }
 
 int Channel::getCount()
@@ -188,10 +174,7 @@ void Channel::clear()
 		return;
 
 	while (!queue.empty())
-	{
-		queue.front()->release();
 		queue.pop();
-	}
 
 	// Finish all the supply waits
 	received = sent;

+ 6 - 8
src/modules/thread/Channel.h

@@ -37,8 +37,6 @@ namespace thread
 class Channel : public love::Object
 {
 // FOR WRAPPER USE ONLY
-friend void retainVariant(Channel *, Variant *);
-friend void releaseVariant(Channel *, Variant *);
 friend int w_Channel_performAtomic(lua_State *);
 
 public:
@@ -48,11 +46,11 @@ public:
 
 	static Channel *getChannel(const std::string &name);
 
-	unsigned long push(Variant *var);
-	void supply(Variant *var); // blocking push
-	Variant *pop();
-	Variant *demand(); // blocking pop
-	Variant *peek();
+	unsigned long push(const Variant &var);
+	void supply(const Variant &var); // blocking push
+	bool pop(Variant *var);
+	void demand(Variant *var); // blocking pop
+	bool peek(Variant *var);
 	int getCount();
 	void clear();
 
@@ -67,7 +65,7 @@ private:
 
 	Mutex *mutex;
 	Conditional *cond;
-	std::queue<Variant *> queue;
+	std::queue<Variant> queue;
 	bool named;
 	std::string name;
 

+ 10 - 29
src/modules/thread/LuaThread.cpp

@@ -33,18 +33,12 @@ namespace thread
 LuaThread::LuaThread(const std::string &name, love::Data *code)
 	: code(code)
 	, name(name)
-	, args(nullptr)
-	, nargs(0)
 {
 	threadName = name;
 }
 
 LuaThread::~LuaThread()
 {
-	// No args should still exist at this point,
-	// but you never know.
-	for (int i = 0; i < nargs; ++i)
-		args[i]->release();
 }
 
 void LuaThread::threadFunction()
@@ -74,16 +68,12 @@ void LuaThread::threadFunction()
 		error = luax_tostring(L, -1);
 	else
 	{
-		int pushedargs = nargs;
-		for (int i = 0; i < nargs; ++i)
-		{
-			args[i]->toLua(L);
-			args[i]->release();
-		}
-		// Set both args and nargs to nil, prevents the deconstructor from
-		// accessing it again.
-		nargs = 0;
-		args = nullptr;
+		int pushedargs = (int) args.size();
+
+		for (int i = 0; i < pushedargs; i++)
+			args[i].toLua(L);
+
+		args.clear();
 
 		if (lua_pcall(L, pushedargs, 0, 0) != 0)
 			error = luax_tostring(L, -1);
@@ -97,14 +87,9 @@ void LuaThread::threadFunction()
 	this->release();
 }
 
-bool LuaThread::start(Variant **args, int nargs)
+bool LuaThread::start(const std::vector<Variant> &args)
 {
-	for (int i = 0; i < this->nargs; ++i)
-		this->args[i]->release();
-
 	this->args = args;
-	this->nargs = nargs;
-
 	return Threadable::start();
 }
 
@@ -126,16 +111,12 @@ void LuaThread::onError()
 	p.type = THREAD_THREAD_ID;
 	p.object = this;
 
-	std::vector<StrongRef<Variant>> vargs = {
-		new Variant(THREAD_THREAD_ID, &p),
-		new Variant(error.c_str(), error.length())
+	std::vector<Variant> vargs = {
+		Variant(p.type, &p),
+		Variant(error.c_str(), error.length())
 	};
 
 	event::Message *msg = new event::Message("threaderror", vargs);
-
-	for (const StrongRef<Variant> &v : vargs)
-		v->release();
-
 	eventmodule->push(msg);
 	msg->release();
 }

+ 3 - 3
src/modules/thread/LuaThread.h

@@ -23,6 +23,7 @@
 
 // STL
 #include <string>
+#include <vector>
 
 // LOVE
 #include "common/Data.h"
@@ -44,7 +45,7 @@ public:
 	void threadFunction();
 	const std::string &getError() const;
 
-	bool start(Variant **args, int nargs);
+	bool start(const std::vector<Variant> &args);
 
 private:
 
@@ -54,8 +55,7 @@ private:
 	std::string name;
 	std::string error;
 
-	Variant **args;
-	int nargs;
+	std::vector<Variant> args;
 
 }; // LuaThread
 

+ 13 - 35
src/modules/thread/wrap_Channel.cpp

@@ -25,20 +25,6 @@ namespace love
 namespace thread
 {
 
-void retainVariant(Channel *c, Variant *v)
-{
-	c->lockMutex();
-	v->retain();
-	c->unlockMutex();
-}
-
-void releaseVariant(Channel *c, Variant *v)
-{
-	c->lockMutex();
-	v->release();
-	c->unlockMutex();
-}
-
 Channel *luax_checkchannel(lua_State *L, int idx)
 {
 	return luax_checktype<Channel>(L, idx, THREAD_CHANNEL_ID);
@@ -47,34 +33,29 @@ Channel *luax_checkchannel(lua_State *L, int idx)
 int w_Channel_push(lua_State *L)
 {
 	Channel *c = luax_checkchannel(L, 1);
-	Variant *var = lua_isnoneornil(L, 2) ? 0 : Variant::fromLua(L, 2);
-	if (!var)
+	Variant var;
+	if (!Variant::fromLua(L, 2, &var))
 		return luaL_argerror(L, 2, "boolean, number, string, love type, or flat table expected");
 	c->push(var);
-	releaseVariant(c, var);
 	return 0;
 }
 
 int w_Channel_supply(lua_State *L)
 {
 	Channel *c = luax_checkchannel(L, 1);
-	Variant *var = lua_isnoneornil(L, 2) ? 0 : Variant::fromLua(L, 2);
-	if (!var)
+	Variant var;
+	if (!Variant::fromLua(L, 2, &var))
 		return luaL_argerror(L, 2, "boolean, number, string, love type, or flat table expected");
 	c->supply(var);
-	releaseVariant(c, var);
 	return 0;
 }
 
 int w_Channel_pop(lua_State *L)
 {
 	Channel *c = luax_checkchannel(L, 1);
-	Variant *var = c->pop();
-	if (var)
-	{
-		var->toLua(L);
-		releaseVariant(c, var);
-	}
+	Variant var;
+	if (c->pop(&var))
+		var.toLua(L);
 	else
 		lua_pushnil(L);
 	return 1;
@@ -83,21 +64,18 @@ int w_Channel_pop(lua_State *L)
 int w_Channel_demand(lua_State *L)
 {
 	Channel *c = luax_checkchannel(L, 1);
-	Variant *var = c->demand();
-	var->toLua(L);
-	releaseVariant(c, var);
+	Variant var;
+	c->demand(&var);
+	var.toLua(L);
 	return 1;
 }
 
 int w_Channel_peek(lua_State *L)
 {
 	Channel *c = luax_checkchannel(L, 1);
-	Variant *var = c->peek();
-	if (var)
-	{
-		var->toLua(L);
-		releaseVariant(c, var);
-	}
+	Variant var;
+	if (c->peek(&var))
+		var.toLua(L);
 	else
 		lua_pushnil(L);
 	return 1;

+ 9 - 13
src/modules/thread/wrap_LuaThread.cpp

@@ -33,26 +33,22 @@ LuaThread *luax_checkthread(lua_State *L, int idx)
 int w_Thread_start(lua_State *L)
 {
 	LuaThread *t = luax_checkthread(L, 1);
+	std::vector<Variant> args;
 	int nargs = lua_gettop(L) - 1;
-	Variant **args = 0;
 
-	if (nargs > 0)
+	Variant v;
+	for (int i = 0; i < nargs; ++i)
 	{
-		args = new Variant*[nargs];
-		for (int i = 0; i < nargs; ++i)
+		if (!Variant::fromLua(L, i+2, &v))
 		{
-			args[i] = Variant::fromLua(L, i+2);
-			if (!args[i])
-			{
-				for (int j = i; j >= 0; j--)
-					delete args[j];
-				delete[] args;
-				return luaL_argerror(L, i+2, "boolean, number, string, love type, or flat table expected");
-			}
+			args.clear();
+			return luaL_argerror(L, i+2, "boolean, number, string, love type, or flat table expected");
 		}
+
+		args.push_back(v);
 	}
 
-	luax_pushboolean(L, t->start(args, nargs));
+	luax_pushboolean(L, t->start(args));
 	return 1;
 }