Move Lua wrapper code for Variants out of the Variant class file.

This commit is contained in:
Alex Szpakowski
2020-02-28 21:54:03 -04:00
parent b95788c104
commit e71f27c65d
16 changed files with 237 additions and 241 deletions
+14
View File
@@ -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
{
+4 -153
View File
@@ -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<Object *>(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<const void*> *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<const void *> 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<std::pair<Variant, Variant>> &table = data.table->pairs;
int tsize = (int) table.size();
lua_createtable(L, 0, tsize);
for (int i = 0; i < tsize; ++i)
{
std::pair<Variant, Variant> &kv = table[i];
kv.first.toLua(L);
kv.second.toLua(L);
lua_settable(L, -3);
}
break;
}
case NIL:
default:
lua_pushnil(L);
break;
}
}
} // love
+3 -4
View File
@@ -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 <cstring>
#include <string>
#include <vector>
#include <set>
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<const void*> *tableSet = nullptr);
void toLua(lua_State *L) const;
static Variant unknown() { return Variant(UNKNOWN); }
private:
Variant(Type vtype);
Type type;
Data data;
+154
View File
@@ -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<Object *>(u->object) != nullptr)
return u;
return nullptr;
}
Variant luax_checkvariant(lua_State *L, int n, bool allowuserdata, std::set<const void*> *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<const void *> 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<std::pair<Variant, Variant>> &table = data.table->pairs;
int tsize = (int) table.size();
lua_createtable(L, 0, tsize);
for (int i = 0; i < tsize; ++i)
{
std::pair<Variant, Variant> &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");
+13 -16
View File
@@ -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 <exception>
#include <algorithm>
#include <set>
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<T> &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<const void*> *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.
+2 -2
View File
@@ -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);
-40
View File
@@ -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<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;
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()
{
}
-3
View File
@@ -46,9 +46,6 @@ public:
Message(const std::string &name, const std::vector<Variant> &vargs = {});
~Message();
int toLua(lua_State *L);
static Message *fromLua(lua_State *L, int n);
const std::string name;
const std::vector<Variant> args;
+34 -11
View File
@@ -38,13 +38,23 @@ namespace event
#define instance() (Module::getInstance<Event>(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<Message> m;
luax_catchexcept(L, [&]() { m.set(Message::fromLua(L, 1), Acquire::NORETAIN); });
std::string name = luax_checkstring(L, 1);
std::vector<Variant> 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<Message> 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<Variant> 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<Message> 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<Message> m(new Message("quit", args), Acquire::NORETAIN);
instance()->push(m);
+1
View File
@@ -24,6 +24,7 @@
// LOVE
#include "common/config.h"
#include "Event.h"
#include "common/runtime.h"
namespace love
{
+2 -5
View File
@@ -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<Variant> queue;
+2 -1
View File
@@ -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();
+5 -5
View File
@@ -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;
+1
View File
@@ -23,6 +23,7 @@
// LOVE
#include "Channel.h"
#include "common/runtime.h"
namespace love
{
+1 -1
View File
@@ -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)
+1
View File
@@ -23,6 +23,7 @@
// LOVE
#include "ThreadModule.h"
#include "common/runtime.h"
namespace love
{