luaengineapp/src/luaenginecore/luaengine/LuaEngine.cpp

536 lines
16 KiB
C++

/*****************************************************************************
* luaEngine Lua Engine for Qt
* Copyright (C) 2018-2021 Syping
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*****************************************************************************/
#define LUA_LIB
#include "LuaEngine.h"
#include "LuaEngineRegistry.h"
#include <QCoreApplication>
#include <QTextStream>
#include <QMetaMethod>
LuaEngine::LuaEngine(QObject *parent, bool loadBaseLibraries) : QObject(parent)
{
LuaEngine(LuaEngineType::UnknownEngineType, parent, loadBaseLibraries);
}
LuaEngine::LuaEngine(LuaEngineType engineType, QObject *parent, bool loadBaseLibraries) : QObject(parent), p_engineType(engineType)
{
L = luaL_newstate();
if (loadBaseLibraries)
luaL_openlibs(L);
engineRegistry->registerEngine((void*)L, (void*)this);
pushVariant("DeleteInstant", 0);
pushVariant("DeleteLater", 1);
pushVariant("CliExecuted", "CliExecuted");
pushFunction("delete", luaObjectDelete_p);
pushFunction("connect", luaTriggerConnect_p);
pushFunction("disconnect", luaTriggerDisconnect_p);
pushFunction("getObjectParent", luaObjectGetParent_p);
pushFunction("setObjectParent", luaObjectSetParent_p);
pushFunction("luaEngineQuit", luaEngineQuit_p);
pushFunction("luaEngineVersion", luaEngineVersion_p);
pushFunction("luaEnginePlatform", luaEnginePlatform_p);
}
LuaEngine::~LuaEngine()
{
engineRegistry->unregisterEngine(L);
lua_close(L);
}
lua_State* LuaEngine::luaState()
{
return L;
}
LuaEngine::LuaEngineType LuaEngine::engineType()
{
return p_engineType;
}
void LuaEngine::loadBaseLibraries()
{
luaL_openlibs(L);
}
int LuaEngine::luaEngineWriter_p(lua_State *L_p, const void *buffer, size_t size, void *array)
{
Q_UNUSED(L_p)
((QByteArray*)array)->append(QByteArray(static_cast<const char*>(buffer), (int)size));
return 0;
}
int LuaEngine::luaEngineQuit_p(lua_State *L_p)
{
int argumentCount = getArgumentCount(L_p);
if (argumentCount == 1) {
bool ok;
int retcode = getVariant(L_p, 1).toInt(&ok);
if (ok) {
QCoreApplication::exit(retcode);
return 0;
}
}
QCoreApplication::quit();
return 0;
}
int LuaEngine::luaEngineVersion_p(lua_State *L_p)
{
pushVariant(L_p, "0.1");
return 1;
}
int LuaEngine::luaEnginePlatform_p(lua_State *L_p)
{
#ifdef Q_OS_ANDROID
pushVariant(L_p, "Android");
#elif defined(Q_OS_LINUX)
pushVariant(L_p, "Linux");
#elif defined(Q_OS_FREEBSD)
pushVariant(L_p, "FreeBSD");
#elif defined(Q_OS_OPENBSD)
pushVariant(L_p, "OpenBSD");
#elif defined(Q_OS_MACOS)
pushVariant(L_p, "macOS");
#elif defined(Q_OS_DARWIN)
pushVariant(L_p, "Darwin");
#elif defined(Q_OS_UNIX)
pushVariant(L_p, "Unix");
#elif defined(Q_OS_WIN)
pushVariant(L_p, "Windows");
#else
pushVariant(L_p, "Unknown");
#endif
return 1;
}
QByteArray LuaEngine::dumpLuaScript()
{
QByteArray array;
lua_lock(L);
lua_dump(L, luaEngineWriter_p, (void*)&array, 1);
lua_unlock(L);
return array;
}
bool LuaEngine::loadLuaScript(const QByteArray &data)
{
int result = luaL_loadbuffer(L, data.data(), data.size(), "script");
return (result == 0) ? true : false;
}
bool LuaEngine::loadLuaScript(QIODevice *device, bool closeDevice)
{
QByteArray data;
if (!device->isOpen()) {
if (device->open(QIODevice::ReadOnly)) {
data = device->readAll();
if (closeDevice)
device->close();
return loadLuaScript(data);
}
}
else {
data = device->readAll();
if (closeDevice)
device->close();
return loadLuaScript(data);
}
return false;
}
bool LuaEngine::executeLuaScript(const QByteArray &data)
{
if (loadLuaScript(data))
return (lua_pcall(L, 0, LUA_MULTRET, 0) == 0);
return false;
}
bool LuaEngine::executeLuaScript(QIODevice *device, bool closeDevice)
{
QByteArray data;
if (!device->isOpen()) {
if (device->open(QIODevice::ReadOnly)) {
data = device->readAll();
if (closeDevice)
device->close();
return executeLuaScript(data);
}
}
else {
data = device->readAll();
if (closeDevice)
device->close();
return executeLuaScript(data);
}
return false;
}
bool LuaEngine::executeLuaFunction(const char *name, bool requireReturn)
{
return executeLuaFunction(L, name, requireReturn);
}
bool LuaEngine::executeLuaFunction(lua_State *L_p, const char *name, bool requireReturn)
{
int returnCount = (requireReturn) ? LUA_MULTRET : 0;
lua_getglobal(L_p, name);
return (lua_pcall(L_p, 0, returnCount, 0) == 0);
}
bool LuaEngine::executeLuaFunction(const char *name, const QVariant &argument, bool requireReturn)
{
return executeLuaFunction(L, name, argument, requireReturn);
}
bool LuaEngine::executeLuaFunction(lua_State *L_p, const char *name, const QVariant &argument, bool requireReturn)
{
int returnCount = (requireReturn) ? LUA_MULTRET : 0;
lua_getglobal(L_p, name);
pushVariant(L_p, argument);
return (lua_pcall(L_p, 1, returnCount, 0) == 0);
}
bool LuaEngine::executeLuaFunction(const char *name, const QVariantList &args, bool requireReturn)
{
return executeLuaFunction(L, name, args, requireReturn);
}
bool LuaEngine::executeLuaFunction(lua_State *L_p, const char *name, const QVariantList &args, bool requireReturn)
{
int returnCount = (requireReturn) ? LUA_MULTRET : 0;
lua_getglobal(L_p, name);
for (const QVariant &argument : args) {
pushVariant(L_p, argument);
}
return (lua_pcall(L_p, args.count(), returnCount, 0) == 0);
}
void LuaEngine::pushFunction(const char *name, lua_CFunction function)
{
pushFunction(L, name, function);
}
void LuaEngine::pushFunction(lua_State *L_p, const char *name, lua_CFunction function)
{
lua_pushcfunction(L_p, function);
lua_setglobal(L_p, name);
}
void LuaEngine::pushPointer(const char *name, void *pointer)
{
pushPointer(L, name, pointer);
}
void LuaEngine::pushPointer(lua_State *L_p, const char *name, void *pointer)
{
pushPointer(L_p, pointer);
lua_setglobal(L_p, name);
}
void LuaEngine::pushPointer(void *pointer)
{
pushPointer(L, pointer);
}
void LuaEngine::pushPointer(lua_State *L_p, void *pointer)
{
lua_pushlightuserdata(L_p, pointer);
}
void LuaEngine::pushVariant(const char *name, const QVariant &variant)
{
pushVariant(L, name, variant);
}
void LuaEngine::pushVariant(lua_State *L_p, const char *name, const QVariant &variant)
{
pushVariant(L_p, variant);
lua_setglobal(L_p, name);
}
void LuaEngine::pushVariant(const QVariant &variant)
{
pushVariant(L, variant);
}
void LuaEngine::pushVariant(lua_State *L_p, const QVariant &variant)
{
if (variant.type() == QVariant::Bool) {
lua_pushboolean(L_p, (int)variant.toBool());
}
else if (variant.type() == QVariant::Int) {
lua_pushinteger(L_p, variant.toInt());
}
else if (variant.type() == QVariant::Double) {
lua_pushnumber(L_p, variant.toDouble());
}
else if (variant.type() == QVariant::String) {
lua_pushstring(L_p, variant.toString().toUtf8().data());
}
else if (variant.type() == QVariant::StringList) {
QStringList stringList = variant.toStringList();
lua_createtable(L_p, 0, stringList.count());
int currentId = 1;
for (const QString &string : qAsConst(stringList)) {
lua_pushinteger(L_p, currentId);
lua_pushstring(L_p, string.toUtf8().data());
lua_settable(L_p, -3);
currentId++;
}
}
else if ((QMetaType::Type)variant.type() == QMetaType::QVariantList) {
QVariantList variantList = variant.toList();
lua_createtable(L_p, 0, variantList.count());
int currentId = 1;
for (const QVariant &variant : qAsConst(variantList)) {
lua_pushinteger(L_p, currentId);
pushVariant(L_p, variant);
lua_settable(L_p, -3);
currentId++;
}
}
else if ((QMetaType::Type)variant.type() == QMetaType::QVariantMap) {
QVariantMap variantMap = variant.toMap();
lua_createtable(L_p, 0, variantMap.count());
for (auto it = variantMap.constBegin(); it != variantMap.constEnd(); it++) {
lua_pushstring(L_p, it.key().toUtf8().data());
pushVariant(L_p, it.value());
lua_settable(L_p, -3);
}
}
else if ((QMetaType::Type)variant.type() == QMetaType::Void || (QMetaType::Type)variant.type() == QMetaType::VoidStar) {
lua_pushlightuserdata(L_p, variant.value<void*>());
}
else {
lua_pushnil(L_p);
}
}
QVariant LuaEngine::getVariant(const char *name)
{
lua_getglobal(L, name);
return returnVariant();
}
QVariant LuaEngine::getVariant(lua_State *L_p, const char *name)
{
lua_getglobal(L_p, name);
return returnVariant(L_p);
}
QVariant LuaEngine::getVariant(int index)
{
return getVariant(L, index);
}
QVariant LuaEngine::getVariant(lua_State *L_p, int index)
{
if (lua_isboolean(L_p, index)) {
return QVariant::fromValue((bool)lua_toboolean(L_p, index));
}
else if (lua_isinteger(L_p, index)) {
return QVariant::fromValue(lua_tointeger(L_p, index));
}
else if (lua_isnumber(L_p, index)) {
return QVariant::fromValue(lua_tonumber(L_p, index));
}
else if (lua_isstring(L_p, index)) {
return QVariant::fromValue(QString(lua_tostring(L_p, index)));
}
else if (lua_istable(L_p, index)) {
QVariantMap variantMap;
lua_pushvalue(L_p, index);
lua_pushnil(L_p);
while (lua_next(L_p, -2) != 0) {
lua_pushvalue(L_p, -2);
QString key = QString(lua_tostring(L_p, -1));
QVariant value = getVariant(L_p, -2);
variantMap.insert(key, value);
lua_pop(L_p, 2);
}
lua_pop(L_p, 1);
return QVariant::fromValue(variantMap);
}
else if (lua_isuserdata(L_p, index)) {
return QVariant::fromValue(lua_touserdata(L_p, index));
}
else if (lua_isnoneornil(L_p, index)) {
return QVariant();
}
QTextStream(stderr) << "Warning: Didn't catch lua_isnoneornil before empty QVariant got returned" << Qt::endl;
return QVariant();
}
void* LuaEngine::returnPointer()
{
return returnPointer(L);
}
void* LuaEngine::returnPointer(lua_State *L_p)
{
return getPointer(L_p, -1);
}
void* LuaEngine::getPointer(const char *name)
{
lua_getglobal(L, name);
return returnPointer();
}
void* LuaEngine::getPointer(lua_State *L_p, const char *name)
{
lua_getglobal(L_p, name);
return returnPointer(L_p);
}
void* LuaEngine::getPointer(int index)
{
return getPointer(L, index);
}
void* LuaEngine::getPointer(lua_State *L_p, int index)
{
return lua_touserdata(L_p, index);
}
QVariant LuaEngine::returnVariant()
{
return returnVariant(L);
}
QVariant LuaEngine::returnVariant(lua_State *L_p)
{
return getVariant(L_p, -1);
}
QVariantList LuaEngine::getArguments(lua_State *L_p)
{
QVariantList arguments;
int argumentCount = getArgumentCount(L_p);
for (int i = 1; i < (argumentCount + 1); i++) {
arguments << getVariant(L_p, i);
}
return arguments;
}
int LuaEngine::getArgumentCount(lua_State *L_p)
{
return lua_gettop(L_p);
}
int LuaEngine::luaObjectDelete_p(lua_State *L_p)
{
if (getArgumentCount(L_p) >= 1) {
void *pointer = getPointer(L_p, 1);
if (pointer != NULL) {
switch (getVariant(L_p, 2).toInt()) {
case 1:
((QObject*)pointer)->deleteLater();
break;
default:
delete ((QObject*)pointer);
}
}
}
return 0;
}
int LuaEngine::luaObjectGetParent_p(lua_State *L_p)
{
if (getArgumentCount(L_p) >= 1) {
void *pointer = getPointer(L_p, 1);
if (pointer != NULL) {
pushPointer(L_p, ((QObject*)pointer)->parent());
return 1;
}
}
return 0;
}
int LuaEngine::luaObjectSetParent_p(lua_State *L_p)
{
if (getArgumentCount(L_p) >= 2) {
void *o_pointer = getPointer(L_p, 1);
void *p_pointer = getPointer(L_p, 2);
if (o_pointer != NULL && p_pointer != NULL && ((QObject*)o_pointer)->inherits("QObject") && ((QObject*)p_pointer)->inherits("QObject")) {
((QObject*)o_pointer)->setParent((QObject*)p_pointer);
}
}
return 0;
}
int LuaEngine::luaTriggerConnect_p(lua_State *L_p)
{
if (getArgumentCount(L_p) >= 3) {
void *pointer = getPointer(L_p, 1);
if (pointer != NULL) {
QObject *object = (QObject*)pointer;
QString signalString = getVariant(L_p, 2).toString();
int signalIndex = object->metaObject()->indexOfSignal(signalString.toUtf8().data());
if (signalIndex != -1) {
LuaEngine *engine = (LuaEngine*)engineRegistry->getEngine(L_p);
int slotIndex = engine->metaObject()->indexOfSlot("luaTriggerSlot_p()");
if (slotIndex != -1) {
QMetaMethod signal = object->metaObject()->method(signalIndex);
QMetaMethod slot = engine->metaObject()->method(slotIndex);
QString funcStorage;
QTextStream(&funcStorage) << "__ConnectFunc_" << object << "_" << signal.name();
engine->setProperty(funcStorage.toUtf8().data(), getVariant(L_p, 3));
QObject::connect(object, signal, engine, slot);
}
}
}
}
return 0;
}
int LuaEngine::luaTriggerDisconnect_p(lua_State *L_p)
{
if (getArgumentCount(L_p) >= 2) {
void *pointer = getPointer(L_p, 1);
if (pointer != NULL) {
QObject *object = (QObject*)pointer;
QString signalString = getVariant(L_p, 2).toString();
int signalIndex = object->metaObject()->indexOfSignal(signalString.toUtf8().data());
if (signalIndex != -1) {
LuaEngine *engine = (LuaEngine*)engineRegistry->getEngine(L_p);
int slotIndex = engine->metaObject()->indexOfSlot("luaTriggerSlot_p()");
if (slotIndex != -1) {
QMetaMethod signal = object->metaObject()->method(signalIndex);
QMetaMethod slot = engine->metaObject()->method(slotIndex);
QString funcStorage;
QTextStream(&funcStorage) << "__ConnectFunc_" << object << "_" << signal.name();
engine->setProperty(funcStorage.toUtf8().data(), QVariant());
QObject::disconnect(object, signal, engine, slot);
}
}
}
}
return 0;
}
void LuaEngine::luaTriggerSlot_p()
{
QMetaMethod signal = sender()->metaObject()->method(senderSignalIndex());
QString funcStorage;
QTextStream(&funcStorage) << "__ConnectFunc_" << sender() << "_" << signal.name();
QString luaConnectFunc = property(funcStorage.toUtf8().data()).toString();
executeLuaFunction(luaConnectFunc.toUtf8().data(), QVariant::fromValue((void*)sender()));
}