aboutsummaryrefslogtreecommitdiffstats
path: root/SQLiteStudio3/coreSQLiteStudio/plugins/scriptingqt.cpp
diff options
context:
space:
mode:
Diffstat (limited to 'SQLiteStudio3/coreSQLiteStudio/plugins/scriptingqt.cpp')
-rw-r--r--SQLiteStudio3/coreSQLiteStudio/plugins/scriptingqt.cpp157
1 files changed, 91 insertions, 66 deletions
diff --git a/SQLiteStudio3/coreSQLiteStudio/plugins/scriptingqt.cpp b/SQLiteStudio3/coreSQLiteStudio/plugins/scriptingqt.cpp
index f88fa85..5305f24 100644
--- a/SQLiteStudio3/coreSQLiteStudio/plugins/scriptingqt.cpp
+++ b/SQLiteStudio3/coreSQLiteStudio/plugins/scriptingqt.cpp
@@ -1,42 +1,39 @@
#include "scriptingqt.h"
-#include "common/unused.h"
#include "common/global.h"
#include "scriptingqtdbproxy.h"
-#include <QScriptEngine>
+#include "services/notifymanager.h"
+#include <QJSEngine>
#include <QMutex>
#include <QMutexLocker>
#include <QDebug>
-static QScriptValue scriptingQtDebug(QScriptContext *context, QScriptEngine *engine)
+ScriptingQt::ScriptingQt()
{
- UNUSED(engine);
- QStringList args;
- for (int i = 0; i < context->argumentCount(); i++)
- args << context->argument(i).toString();
-
- qDebug() << "[ScriptingQt]" << args;
- return QScriptValue();
+ managedMainContextsMutex = new QMutex();
}
-ScriptingQt::ScriptingQt()
+ScriptingQt::~ScriptingQt()
{
- mainEngineMutex = new QMutex();
+ safe_delete(managedMainContextsMutex);
}
-ScriptingQt::~ScriptingQt()
+QJSValueList ScriptingQt::toValueList(QJSEngine* engine, const QList<QVariant>& values)
{
- safe_delete(mainEngineMutex);
+ QJSValueList result;
+ for (const QVariant& value : values)
+ result << engine->toScriptValue(value);
+
+ return result;
}
QString ScriptingQt::getLanguage() const
{
- return QStringLiteral("QtScript");
+ return QStringLiteral("JavaScript");
}
ScriptingPlugin::Context* ScriptingQt::createContext()
{
ContextQt* ctx = new ContextQt;
- ctx->engine->pushContext();
contexts << ctx;
return ctx;
}
@@ -56,60 +53,54 @@ void ScriptingQt::resetContext(ScriptingPlugin::Context* context)
ContextQt* ctx = getContext(context);
if (!ctx)
return;
-
- ctx->engine->popContext();
- ctx->engine->pushContext();
}
-QVariant ScriptingQt::evaluate(const QString& code, const QList<QVariant>& args, Db* db, bool locking, QString* errorMessage)
+QVariant ScriptingQt::evaluate(const QString& code, const FunctionInfo& funcInfo, const QList<QVariant>& args, Db* db, bool locking, QString* errorMessage)
{
- QMutexLocker locker(mainEngineMutex);
-
- // Enter a new context
- QScriptContext* engineContext = mainContext->engine->pushContext();
-
// Call the function
- QVariant result = evaluate(mainContext, engineContext, code, args, db, locking);
+ ContextQt* context = getMainContext();
+ QVariant result = evaluate(context, code, funcInfo, args, db, locking);
// Handle errors
- if (!mainContext->error.isEmpty())
- *errorMessage = mainContext->error;
-
- // Leave the context to reset "this".
- mainContext->engine->popContext();
+ if (!context->error.isEmpty())
+ *errorMessage = context->error;
return result;
}
-QVariant ScriptingQt::evaluate(ScriptingPlugin::Context* context, const QString& code, const QList<QVariant>& args, Db* db, bool locking)
+QVariant ScriptingQt::evaluate(ScriptingPlugin::Context* context, const QString& code, const FunctionInfo& funcInfo, const QList<QVariant>& args, Db* db, bool locking)
{
ContextQt* ctx = getContext(context);
if (!ctx)
return QVariant();
- return evaluate(ctx, ctx->engine->currentContext(), code, args, db, locking);
+ return evaluate(ctx, code, funcInfo, args, db, locking);
}
-QVariant ScriptingQt::evaluate(ContextQt* ctx, QScriptContext* engineContext, const QString& code, const QList<QVariant>& args, Db* db, bool locking)
+QVariant ScriptingQt::evaluate(ContextQt* ctx, const QString& code, const FunctionInfo& funcInfo, const QList<QVariant>& args, Db* db, bool locking)
{
// Define function to call
- QScriptValue functionValue = getFunctionValue(ctx, code);
+ QJSValue functionValue = getFunctionValue(ctx, code, funcInfo);
// Db for this evaluation
ctx->dbProxy->setDb(db);
ctx->dbProxy->setUseDbLocking(locking);
// Call the function
- QScriptValue result;
+ QJSValue result;
if (args.size() > 0)
- result = functionValue.call(engineContext->activationObject(), ctx->engine->toScriptValue(args));
+ result = functionValue.call(toValueList(ctx->engine, args));
else
- result = functionValue.call(engineContext->activationObject());
+ result = functionValue.call();
// Handle errors
ctx->error.clear();
- if (ctx->engine->hasUncaughtException())
- ctx->error = ctx->engine->uncaughtException().toString();
+ if (result.isError())
+ {
+ ctx->error = QString("Uncaught exception at line %1: %2").arg(
+ result.property("lineNumber").toString(),
+ result.toString());
+ }
ctx->dbProxy->setDb(nullptr);
ctx->dbProxy->setUseDbLocking(false);
@@ -117,6 +108,20 @@ QVariant ScriptingQt::evaluate(ContextQt* ctx, QScriptContext* engineContext, co
return convertVariant(result.toVariant());
}
+ScriptingQt::ContextQt* ScriptingQt::getMainContext()
+{
+ if (mainContext.hasLocalData())
+ return mainContext.localData();
+
+ ContextQt* context = new ContextQt();
+ mainContext.setLocalData(context);
+
+ QMutexLocker locker(managedMainContextsMutex);
+ managedMainContexts << context;
+
+ return context;
+}
+
QVariant ScriptingQt::convertVariant(const QVariant& value, bool wrapStrings)
{
switch (value.type())
@@ -148,7 +153,7 @@ QVariant ScriptingQt::convertVariant(const QVariant& value, bool wrapStrings)
case QVariant::List:
{
QStringList list;
- for (const QVariant& var : value.toList())
+ for (QVariant& var : value.toList())
list << convertVariant(var, true).toString();
return "[" + list.join(", ") + "]";
@@ -176,7 +181,7 @@ void ScriptingQt::setVariable(ScriptingPlugin::Context* context, const QString&
if (!ctx)
return;
- ctx->engine->globalObject().setProperty(name, ctx->engine->newVariant(value));
+ ctx->engine->globalObject().setProperty(name, ctx->engine->toScriptValue(value));
}
QVariant ScriptingQt::getVariable(ScriptingPlugin::Context* context, const QString& name)
@@ -185,7 +190,7 @@ QVariant ScriptingQt::getVariable(ScriptingPlugin::Context* context, const QStri
if (!ctx)
return QVariant();
- QScriptValue value = ctx->engine->globalObject().property(name);
+ QJSValue value = ctx->engine->globalObject().property(name);
return convertVariant(value.toVariant());
}
@@ -214,20 +219,28 @@ QString ScriptingQt::getIconPath() const
bool ScriptingQt::init()
{
- QMutexLocker locker(mainEngineMutex);
- mainContext = new ContextQt;
return true;
}
void ScriptingQt::deinit()
{
- for (Context* ctx : contexts)
+ for (Context*& ctx : contexts)
delete ctx;
contexts.clear();
- QMutexLocker locker(mainEngineMutex);
- safe_delete(mainContext);
+ QMutexLocker locker(managedMainContextsMutex);
+ for (ContextQt*& ctx : managedMainContexts)
+ {
+#if QT_VERSION >= QT_VERSION_CHECK(5, 14, 0)
+ ctx->engine->setInterrupted(true);
+#else
+ // FIXME No way to cleanly interrupt QJSEngine before Qt 5.14
+#endif
+ delete ctx;
+ }
+
+ managedMainContexts.clear();
}
ScriptingQt::ContextQt* ScriptingQt::getContext(ScriptingPlugin::Context* context) const
@@ -239,31 +252,30 @@ ScriptingQt::ContextQt* ScriptingQt::getContext(ScriptingPlugin::Context* contex
return ctx;
}
-QScriptValue ScriptingQt::getFunctionValue(ContextQt* ctx, const QString& code)
+QJSValue ScriptingQt::getFunctionValue(ContextQt* ctx, const QString& code, const FunctionInfo& funcInfo)
{
- static const QString fnDef = QStringLiteral("(function () {%1\n})");
+ static const QString fnDef = QStringLiteral("(function (%1) {%2\n})");
- QScriptProgram* prog = nullptr;
- if (!ctx->scriptCache.contains(code))
- {
- prog = new QScriptProgram(fnDef.arg(code));
- ctx->scriptCache.insert(code, prog);
- }
- else
- {
- prog = ctx->scriptCache[code];
- }
- return ctx->engine->evaluate(*prog);
+ QString fullCode = fnDef.arg(funcInfo.getArguments().join(", "), code);
+ QJSValue* func = ctx->scriptCache[fullCode];
+ if (func)
+ return *func;
+
+ func = new QJSValue(ctx->engine->evaluate(fullCode));
+ ctx->scriptCache.insert(fullCode, func);
+ return *func;
}
ScriptingQt::ContextQt::ContextQt()
{
- engine = new QScriptEngine();
+ engine = new QJSEngine();
+ engine->installExtensions(QJSEngine::ConsoleExtension);
- dbProxy = new ScriptingQtDbProxy();
- dbProxyScriptValue = engine->newQObject(dbProxy, QScriptEngine::QtOwnership, QScriptEngine::ExcludeDeleteLater);
+ dbProxy = new ScriptingQtDbProxy(engine);
+ dbProxyScriptValue = engine->newQObject(dbProxy);
+ console = new ScriptingQtConsole(engine);
- engine->globalObject().setProperty("debug", engine->newFunction(scriptingQtDebug));
+ engine->globalObject().setProperty("console", engine->newQObject(console));
engine->globalObject().setProperty("db", dbProxyScriptValue);
scriptCache.setMaxCost(cacheSize);
@@ -271,6 +283,19 @@ ScriptingQt::ContextQt::ContextQt()
ScriptingQt::ContextQt::~ContextQt()
{
- safe_delete(engine);
+ safe_delete(console);
safe_delete(dbProxy);
+ safe_delete(engine);
+}
+
+ScriptingQtConsole::ScriptingQtConsole(QJSEngine* engine) :
+ QObject(), engine(engine)
+{
+}
+
+QJSValue ScriptingQtConsole::log(const QJSValue& value)
+{
+ static_qstring(tpl, "[JS] %1");
+ NOTIFY_MANAGER->info(tpl.arg(ScriptingQt::convertVariant(value.toVariant()).toString()));
+ return QJSValue();
}