From e71f27c65df42fbd0a1a67a76f2e37213a3a4812 Mon Sep 17 00:00:00 2001 From: Alex Szpakowski Date: Fri, 28 Feb 2020 21:54:03 -0400 Subject: [PATCH] Move Lua wrapper code for Variants out of the Variant class file. --- src/common/Object.h | 14 +++ src/common/Variant.cpp | 157 +------------------------- src/common/Variant.h | 7 +- src/common/runtime.cpp | 154 +++++++++++++++++++++++++ src/common/runtime.h | 29 +++-- src/love.cpp | 4 +- src/modules/event/Event.cpp | 40 ------- src/modules/event/Event.h | 3 - src/modules/event/wrap_Event.cpp | 45 ++++++-- src/modules/event/wrap_Event.h | 1 + src/modules/thread/Channel.h | 7 +- src/modules/thread/LuaThread.cpp | 3 +- src/modules/thread/wrap_Channel.cpp | 10 +- src/modules/thread/wrap_Channel.h | 1 + src/modules/thread/wrap_LuaThread.cpp | 2 +- src/modules/thread/wrap_LuaThread.h | 1 + 16 files changed, 237 insertions(+), 241 deletions(-) diff --git a/src/common/Object.h b/src/common/Object.h index 6d91f316b..3e5c94104 100644 --- a/src/common/Object.h +++ b/src/common/Object.h @@ -78,6 +78,20 @@ private: }; // Object +/** + * Structure wrapping an object and its associated Type instance. This is used + * for storing everything necessary to identify an object's properties in + * environments where the Type is not easily obtained otherwise, for example in + * a Lua state. + **/ +struct Proxy +{ + // Holds type information (see types.h). + love::Type *type; + + // Pointer to the actual object. + Object *object; +}; enum class Acquire { diff --git a/src/common/Variant.cpp b/src/common/Variant.cpp index c1cb763d3..5c6c01dc6 100644 --- a/src/common/Variant.cpp +++ b/src/common/Variant.cpp @@ -26,25 +26,13 @@ namespace love { -static Proxy *tryextractproxy(lua_State *L, int idx) -{ - Proxy *u = (Proxy *)lua_touserdata(L, idx); - - if (u == nullptr || u->type == nullptr) - return nullptr; - - // We could get rid of the dynamic_cast for more performance, but it would - // be less safe... - if (dynamic_cast(u->object) != nullptr) - return u; - - return nullptr; -} +Variant::Variant(Type vtype) + : type(vtype) +{} Variant::Variant() : type(NIL) -{ -} +{} Variant::Variant(bool boolean) : type(BOOLEAN) @@ -152,141 +140,4 @@ Variant &Variant::operator = (const Variant &v) return *this; } -Variant Variant::fromLua(lua_State *L, int n, bool allowuserdata, std::set *tableSet) -{ - size_t len; - const char *str; - Proxy *p = nullptr; - - 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: - return Variant(luax_toboolean(L, n)); - case LUA_TNUMBER: - return Variant(lua_tonumber(L, n)); - case LUA_TSTRING: - str = lua_tolstring(L, n, &len); - return Variant(str, len); - case LUA_TLIGHTUSERDATA: - return Variant(lua_touserdata(L, n)); - case LUA_TUSERDATA: - if (!allowuserdata) - { - luax_typerror(L, n, "copyable Lua value"); - return Variant(); - } - p = tryextractproxy(L, n); - if (p != nullptr) - return Variant(p->type, p->object); - else - { - luax_typerror(L, n, "love type"); - return Variant(); - } - case LUA_TNIL: - return Variant(); - case LUA_TTABLE: - { - bool success = true; - std::set topTableSet; - - // We can use a pointer to a stack-allocated variable because it's - // never used after the stack-allocated variable is destroyed. - if (tableSet == nullptr) - tableSet = &topTableSet; - - // Now make sure this table wasn't already serialised - const void *tablePointer = lua_topointer(L, n); - { - auto result = tableSet->insert(tablePointer); - if (!result.second) // insertion failed - throw love::Exception("Cycle detected in table"); - } - - SharedTable *table = new SharedTable(); - - size_t len = luax_objlen(L, -1); - if (len > 0) - table->pairs.reserve(len); - - lua_pushnil(L); - - while (lua_next(L, n)) - { - table->pairs.emplace_back(fromLua(L, -2, allowuserdata, tableSet), fromLua(L, -1, allowuserdata, tableSet)); - lua_pop(L, 1); - - const auto &p = table->pairs.back(); - if (p.first.getType() == UNKNOWN || p.second.getType() == UNKNOWN) - { - success = false; - break; - } - } - - // And remove the table from the set again - tableSet->erase(tablePointer); - - if (success) - return Variant(table); - else - table->release(); - } - break; - } - - Variant v; - v.type = UNKNOWN; - return v; -} - -void Variant::toLua(lua_State *L) const -{ - switch (type) - { - case BOOLEAN: - lua_pushboolean(L, data.boolean); - break; - case NUMBER: - lua_pushnumber(L, data.number); - break; - case STRING: - 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); - break; - case LOVEOBJECT: - luax_pushtype(L, *data.objectproxy.type, data.objectproxy.object); - break; - case TABLE: - { - std::vector> &table = data.table->pairs; - int tsize = (int) table.size(); - - lua_createtable(L, 0, tsize); - - for (int i = 0; i < tsize; ++i) - { - std::pair &kv = table[i]; - kv.first.toLua(L); - kv.second.toLua(L); - lua_settable(L, -3); - } - - break; - } - case NIL: - default: - lua_pushnil(L); - break; - } -} - } // love diff --git a/src/common/Variant.h b/src/common/Variant.h index 995df28b7..f7df8f686 100644 --- a/src/common/Variant.h +++ b/src/common/Variant.h @@ -22,14 +22,12 @@ #define LOVE_VARIANT_H #include "common/config.h" -#include "common/runtime.h" #include "common/Object.h" #include "common/int.h" #include #include #include -#include namespace love { @@ -112,11 +110,12 @@ public: Type getType() const { return type; } const Data &getData() const { return data; } - static Variant fromLua(lua_State *L, int n, bool allowuserdata = true, std::set *tableSet = nullptr); - void toLua(lua_State *L) const; + static Variant unknown() { return Variant(UNKNOWN); } private: + Variant(Type vtype); + Type type; Data data; diff --git a/src/common/runtime.cpp b/src/common/runtime.cpp index befeb4bb8..7b90b37f6 100644 --- a/src/common/runtime.cpp +++ b/src/common/runtime.cpp @@ -661,6 +661,160 @@ bool luax_istype(lua_State *L, int idx, love::Type &type) return false; } +static Proxy *tryextractproxy(lua_State *L, int idx) +{ + Proxy *u = (Proxy *)lua_touserdata(L, idx); + + if (u == nullptr || u->type == nullptr) + return nullptr; + + // We could get rid of the dynamic_cast for more performance, but it would + // be less safe... + if (dynamic_cast(u->object) != nullptr) + return u; + + return nullptr; +} + +Variant luax_checkvariant(lua_State *L, int n, bool allowuserdata, std::set *tableSet) +{ + size_t len; + const char *str; + Proxy *p = nullptr; + + 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: + return Variant(luax_toboolean(L, n)); + case LUA_TNUMBER: + return Variant(lua_tonumber(L, n)); + case LUA_TSTRING: + str = lua_tolstring(L, n, &len); + return Variant(str, len); + case LUA_TLIGHTUSERDATA: + return Variant(lua_touserdata(L, n)); + case LUA_TUSERDATA: + if (!allowuserdata) + { + luax_typerror(L, n, "copyable Lua value"); + return Variant(); + } + p = tryextractproxy(L, n); + if (p != nullptr) + return Variant(p->type, p->object); + else + { + luax_typerror(L, n, "love type"); + return Variant(); + } + case LUA_TNIL: + return Variant(); + case LUA_TTABLE: + { + bool success = true; + std::set topTableSet; + + // We can use a pointer to a stack-allocated variable because it's + // never used after the stack-allocated variable is destroyed. + if (tableSet == nullptr) + tableSet = &topTableSet; + + // Now make sure this table wasn't already serialised + const void *tablePointer = lua_topointer(L, n); + { + auto result = tableSet->insert(tablePointer); + if (!result.second) // insertion failed + throw love::Exception("Cycle detected in table"); + } + + Variant::SharedTable *table = new Variant::SharedTable(); + + size_t len = luax_objlen(L, -1); + if (len > 0) + table->pairs.reserve(len); + + lua_pushnil(L); + + while (lua_next(L, n)) + { + table->pairs.emplace_back( + luax_checkvariant(L, -2, allowuserdata, tableSet), + luax_checkvariant(L, -1, allowuserdata, tableSet) + ); + lua_pop(L, 1); + + const auto &p = table->pairs.back(); + if (p.first.getType() == Variant::UNKNOWN || p.second.getType() == Variant::UNKNOWN) + { + success = false; + break; + } + } + + // And remove the table from the set again + tableSet->erase(tablePointer); + + if (success) + return Variant(table); + else + table->release(); + } + break; + } + + return Variant::unknown(); +} + +void luax_pushvariant(lua_State *L, const Variant &v) +{ + const Variant::Data &data = v.getData(); + switch (v.getType()) + { + case Variant::BOOLEAN: + lua_pushboolean(L, data.boolean); + break; + case Variant::NUMBER: + lua_pushnumber(L, data.number); + break; + case Variant::STRING: + lua_pushlstring(L, data.string->str, data.string->len); + break; + case Variant::SMALLSTRING: + lua_pushlstring(L, data.smallstring.str, data.smallstring.len); + break; + case Variant::LUSERDATA: + lua_pushlightuserdata(L, data.userdata); + break; + case Variant::LOVEOBJECT: + luax_pushtype(L, *data.objectproxy.type, data.objectproxy.object); + break; + case Variant::TABLE: + { + std::vector> &table = data.table->pairs; + int tsize = (int) table.size(); + + lua_createtable(L, 0, tsize); + + for (int i = 0; i < tsize; ++i) + { + std::pair &kv = table[i]; + luax_pushvariant(L, kv.first); + luax_pushvariant(L, kv.second); + lua_settable(L, -3); + } + + break; + } + case Variant::NIL: + default: + lua_pushnil(L); + break; + } +} + int luax_getfunction(lua_State *L, const char *mod, const char *fn) { lua_getglobal(L, "love"); diff --git a/src/common/runtime.h b/src/common/runtime.h index c3e4574e5..44f6e4cd7 100644 --- a/src/common/runtime.h +++ b/src/common/runtime.h @@ -24,6 +24,8 @@ // LOVE #include "config.h" #include "types.h" +#include "Object.h" +#include "Variant.h" #include "deprecation.h" // Lua @@ -37,12 +39,12 @@ extern "C" { // C++ #include #include +#include namespace love { // Forward declarations. -class Object; class Module; class Reference; @@ -59,21 +61,6 @@ enum Registry REGISTRY_OBJECTS }; -/** - * This structure wraps all Lua-exposed objects. It exists in the - * Lua state as a full userdata (so we can catch __gc "events"), - * though the Object it refers to is light userdata in the sense - * that it is not allocated by the Lua VM. - **/ -struct Proxy -{ - // Holds type information (see types.h). - love::Type *type; - - // Pointer to the actual object. - Object *object; -}; - /** * A Module with Lua wrapper functions and other data. **/ @@ -365,6 +352,16 @@ void luax_pushtype(lua_State *L, StrongRef &object) **/ void luax_rawnewtype(lua_State *L, love::Type &type, love::Object *object); +/** + * Stores the value at the given index on the stack into a Variant object. + */ +Variant luax_checkvariant(lua_State *L, int idx, bool allowuserdata = true, std::set *tableSet = nullptr); + +/** + * Pushes the contents of the given Variant index onto the stack. + */ +void luax_pushvariant(lua_State *L, const Variant &v); + /** * Checks whether the value at idx is a certain type. * @param L The Lua state. diff --git a/src/love.cpp b/src/love.cpp index 9057167b5..3c4e729df 100644 --- a/src/love.cpp +++ b/src/love.cpp @@ -205,7 +205,7 @@ static DoneAction runlove(int argc, char **argv, int &retval, love::Variant &res } // Set love.restart = restartvalue, and clear restartvalue. - restartvalue.toLua(L); + love::luax_pushvariant(L, restartvalue); lua_setfield(L, -2, "restart"); restartvalue = love::Variant(); @@ -240,7 +240,7 @@ static DoneAction runlove(int argc, char **argv, int &retval, love::Variant &res // Disallow userdata (love objects) from being referenced by the restart // value. if (retidx < lua_gettop(L)) - restartvalue = love::Variant::fromLua(L, retidx + 1, false); + restartvalue = love::luax_checkvariant(L, retidx + 1, false); } lua_close(L); diff --git a/src/modules/event/Event.cpp b/src/modules/event/Event.cpp index a0ed327b5..8f8d82f9e 100644 --- a/src/modules/event/Event.cpp +++ b/src/modules/event/Event.cpp @@ -38,46 +38,6 @@ Message::~Message() { } -int Message::toLua(lua_State *L) -{ - luax_pushstring(L, name); - - for (const Variant &v : args) - v.toLua(L); - - return (int) args.size() + 1; -} - -Message *Message::fromLua(lua_State *L, int n) -{ - std::string name = luax_checkstring(L, n); - std::vector vargs; - - int count = lua_gettop(L) - n; - n++; - - Variant varg; - - for (int i = 0; i < count; i++) - { - if (lua_isnoneornil(L, n+i)) - break; - - luax_catchexcept(L, [&]() { - vargs.push_back(Variant::fromLua(L, n+i)); - }); - - if (vargs.back().getType() == Variant::UNKNOWN) - { - vargs.clear(); - luaL_error(L, "Argument %d can't be stored safely\nExpected boolean, number, string or userdata.", n+i); - return nullptr; - } - } - - return new Message(name, vargs); -} - Event::~Event() { } diff --git a/src/modules/event/Event.h b/src/modules/event/Event.h index 3de15f7a6..52583a877 100644 --- a/src/modules/event/Event.h +++ b/src/modules/event/Event.h @@ -46,9 +46,6 @@ public: Message(const std::string &name, const std::vector &vargs = {}); ~Message(); - int toLua(lua_State *L); - static Message *fromLua(lua_State *L, int n); - const std::string name; const std::vector args; diff --git a/src/modules/event/wrap_Event.cpp b/src/modules/event/wrap_Event.cpp index 4e8e2fea9..76388493e 100644 --- a/src/modules/event/wrap_Event.cpp +++ b/src/modules/event/wrap_Event.cpp @@ -38,13 +38,23 @@ namespace event #define instance() (Module::getInstance(Module::M_EVENT)) +static int luax_pushmessage(lua_State *L, const Message &m) +{ + luax_pushstring(L, m.name); + + for (const Variant &v : m.args) + luax_pushvariant(L, v); + + return (int) m.args.size() + 1; +} + static int w_poll_i(lua_State *L) { Message *m = nullptr; - if (instance()->poll(m)) + if (instance()->poll(m) && m != nullptr) { - int args = m->toLua(L); + int args = luax_pushmessage(L, *m); m->release(); return args; } @@ -63,9 +73,9 @@ int w_wait(lua_State *L) { Message *m = nullptr; luax_catchexcept(L, [&]() { m = instance()->wait(); }); - if (m) + if (m != nullptr) { - int args = m->toLua(L); + int args = luax_pushmessage(L, *m); m->release(); return args; } @@ -75,15 +85,28 @@ int w_wait(lua_State *L) int w_push(lua_State *L) { - StrongRef m; - luax_catchexcept(L, [&]() { m.set(Message::fromLua(L, 1), Acquire::NORETAIN); }); + std::string name = luax_checkstring(L, 1); + std::vector vargs; - luax_pushboolean(L, m.get() != nullptr); + int nargs = lua_gettop(L); + for (int i = 2; i <= nargs; i++) + { + if (lua_isnoneornil(L, i)) + break; - if (m.get() == nullptr) - return 1; + luax_catchexcept(L, [&]() { vargs.push_back(luax_checkvariant(L, i)); }); + + if (vargs.back().getType() == Variant::UNKNOWN) + { + vargs.clear(); + return luaL_error(L, "Argument %d can't be stored safely\nExpected boolean, number, string or userdata.", i); + } + } + + StrongRef m(new Message(name, vargs), Acquire::NORETAIN); instance()->push(m); + luax_pushboolean(L, true); return 1; } @@ -98,7 +121,7 @@ int w_quit(lua_State *L) luax_catchexcept(L, [&]() { std::vector args; for (int i = 1; i <= std::max(1, lua_gettop(L)); i++) - args.push_back(Variant::fromLua(L, i)); + args.push_back(luax_checkvariant(L, i)); StrongRef m(new Message("quit", args), Acquire::NORETAIN); instance()->push(m); @@ -115,7 +138,7 @@ int w_restart(lua_State *L) args.emplace_back("restart", strlen("restart")); for (int i = 1; i <= lua_gettop(L); i++) - args.push_back(Variant::fromLua(L, i)); + args.push_back(luax_checkvariant(L, i)); StrongRef m(new Message("quit", args), Acquire::NORETAIN); instance()->push(m); diff --git a/src/modules/event/wrap_Event.h b/src/modules/event/wrap_Event.h index 6c4bf85ed..d1352771d 100644 --- a/src/modules/event/wrap_Event.h +++ b/src/modules/event/wrap_Event.h @@ -24,6 +24,7 @@ // LOVE #include "common/config.h" #include "Event.h" +#include "common/runtime.h" namespace love { diff --git a/src/modules/thread/Channel.h b/src/modules/thread/Channel.h index ec6e55f5c..e3a866f9b 100644 --- a/src/modules/thread/Channel.h +++ b/src/modules/thread/Channel.h @@ -36,9 +36,6 @@ namespace thread class Channel : public love::Object { -// FOR WRAPPER USE ONLY -friend int w_Channel_performAtomic(lua_State *); - public: static love::Type type; @@ -57,11 +54,11 @@ public: bool hasRead(uint64 id) const; void clear(); -private: - void lockMutex(); void unlockMutex(); +private: + MutexRef mutex; ConditionalRef cond; std::queue queue; diff --git a/src/modules/thread/LuaThread.cpp b/src/modules/thread/LuaThread.cpp index 4b7afa6e1..9bb508c91 100644 --- a/src/modules/thread/LuaThread.cpp +++ b/src/modules/thread/LuaThread.cpp @@ -21,6 +21,7 @@ #include "LuaThread.h" #include "event/Event.h" #include "common/config.h" +#include "common/runtime.h" #ifdef LOVE_BUILD_STANDALONE extern "C" int luaopen_love(lua_State * L); @@ -76,7 +77,7 @@ void LuaThread::threadFunction() int pushedargs = (int) args.size(); for (int i = 0; i < pushedargs; i++) - args[i].toLua(L); + luax_pushvariant(L, args[i]); args.clear(); diff --git a/src/modules/thread/wrap_Channel.cpp b/src/modules/thread/wrap_Channel.cpp index c6d157f99..49a046e63 100644 --- a/src/modules/thread/wrap_Channel.cpp +++ b/src/modules/thread/wrap_Channel.cpp @@ -34,7 +34,7 @@ int w_Channel_push(lua_State *L) { Channel *c = luax_checkchannel(L, 1); luax_catchexcept(L, [&]() { - Variant var = Variant::fromLua(L, 2); + Variant var = luax_checkvariant(L, 2); if (var.getType() == Variant::UNKNOWN) luaL_argerror(L, 2, "boolean, number, string, love type, or table expected"); uint64 id = c->push(var); @@ -48,7 +48,7 @@ int w_Channel_supply(lua_State *L) Channel *c = luax_checkchannel(L, 1); bool result = false; luax_catchexcept(L, [&]() { - Variant var = Variant::fromLua(L, 2); + Variant var = luax_checkvariant(L, 2); if (var.getType() == Variant::UNKNOWN) luaL_argerror(L, 2, "boolean, number, string, love type, or table expected"); if (lua_isnumber(L, 3)) @@ -66,7 +66,7 @@ int w_Channel_pop(lua_State *L) Channel *c = luax_checkchannel(L, 1); Variant var; if (c->pop(&var)) - var.toLua(L); + luax_pushvariant(L, var); else lua_pushnil(L); return 1; @@ -84,7 +84,7 @@ int w_Channel_demand(lua_State *L) result = c->demand(&var); if (result) - var.toLua(L); + luax_pushvariant(L, var); else lua_pushnil(L); return 1; @@ -95,7 +95,7 @@ int w_Channel_peek(lua_State *L) Channel *c = luax_checkchannel(L, 1); Variant var; if (c->peek(&var)) - var.toLua(L); + luax_pushvariant(L, var); else lua_pushnil(L); return 1; diff --git a/src/modules/thread/wrap_Channel.h b/src/modules/thread/wrap_Channel.h index 013b2138f..56fb6e59c 100644 --- a/src/modules/thread/wrap_Channel.h +++ b/src/modules/thread/wrap_Channel.h @@ -23,6 +23,7 @@ // LOVE #include "Channel.h" +#include "common/runtime.h" namespace love { diff --git a/src/modules/thread/wrap_LuaThread.cpp b/src/modules/thread/wrap_LuaThread.cpp index db71f9ca1..db29c1a65 100644 --- a/src/modules/thread/wrap_LuaThread.cpp +++ b/src/modules/thread/wrap_LuaThread.cpp @@ -39,7 +39,7 @@ int w_Thread_start(lua_State *L) for (int i = 0; i < nargs; ++i) { luax_catchexcept(L, [&]() { - args.push_back(Variant::fromLua(L, i+2)); + args.push_back(luax_checkvariant(L, i+2)); }); if (args.back().getType() == Variant::UNKNOWN) diff --git a/src/modules/thread/wrap_LuaThread.h b/src/modules/thread/wrap_LuaThread.h index 50fcffa9a..26696bf85 100644 --- a/src/modules/thread/wrap_LuaThread.h +++ b/src/modules/thread/wrap_LuaThread.h @@ -23,6 +23,7 @@ // LOVE #include "ThreadModule.h" +#include "common/runtime.h" namespace love {