This commit is contained in:
王劲鹏
2021-01-28 17:06:40 +08:00
committed by osborn
parent 0be7a9679a
commit 93b7cc2457
53 changed files with 1020 additions and 774 deletions

View File

@@ -0,0 +1,16 @@
#include <QDebug>
#include "bridge_extension.h"
BridgeExtension::BridgeExtension(QObject *parent) : QObject(parent)
{
}
void BridgeExtension::callNative(QString contextId, QString module, QString methodName, QString callbackId, QJSValue jsValue)
{
qDebug() << "contextId: " + contextId;
qDebug() << "module: " + module;
qDebug() << "methodName: " + methodName;
qDebug() << "callbackId: " + callbackId;
qDebug() << "jsValue: " + jsValue.toString();
}

View File

@@ -0,0 +1,16 @@
#ifndef BRIDGEEXTENSION_H
#define BRIDGEEXTENSION_H
#include <QObject>
#include <QJSValue>
class BridgeExtension : public QObject
{
Q_OBJECT
public:
explicit BridgeExtension(QObject *parent = nullptr);
void callNative(QString contextId, QString module, QString methodName, QString callbackId, QJSValue jsValue);
};
#endif // BRIDGEEXTENSION_H

View File

@@ -0,0 +1,19 @@
#ifndef INTERFACE_JSE_H
#define INTERFACE_JSE_H
#include <QString>
#include <QJSValue>
#include <QVariant>
class InterfaceJSE {
public:
virtual QString loadJS(QString script, QString source) = 0;
virtual void injectGlobalJSObject(QString name, QObject *object) = 0;
virtual void injectGlobalJSFunction(QString name, QObject *function, QString property) = 0;
virtual QJSValue invokeObject(QString objectName, QString functionName, QJSValueList arguments) = 0;
};
#endif // INTERFACE_JSE_H

View File

@@ -1,81 +1,107 @@
#include <QDebug>
#include <QFile>
#include <QResource>
#include <QGuiApplication>
#include <QJsonObject>
#include <QRect>
#include <QScreen>
#include <QSysInfo>
#include <QtConcurrent/QtConcurrent>
#include "constant.h"
#include "js_engine.h"
#include "native_jse.h"
#include "../utils/constant.h"
#include "native_log.h"
#include "native_empty.h"
#include "timer_extension.h"
#include "bridge_extension.h"
#include "../utils/utils.h"
JSEngine::JSEngine() {
initJSEngine();
injectGlobal();
initDoricRuntime();
JSEngine::JSEngine(QObject *parent) : QObject(parent)
{
mJSThreadPool.setMaxThreadCount(1);
QtConcurrent::run(&mJSThreadPool, [this]{
mJSE = new NativeJSE();
});
QtConcurrent::run(&mJSThreadPool, [this]{
// inject env
QScreen *screen = QGuiApplication::primaryScreen();
QRect screenGeometry = screen->geometry();
int screenWidth = screenGeometry.width();
int screenHeight = screenGeometry.height();
QObject *envObject = new QObject();
envObject->setProperty("platform", "Qt");
envObject->setProperty("platformVersion", qVersion());
envObject->setProperty("appName", "appName");
envObject->setProperty("appVersion", "appVersion");
envObject->setProperty("screenWidth", screenWidth);
envObject->setProperty("screenHeight", screenHeight);
envObject->setProperty("screenScale", 1);
envObject->setProperty("statusBarHeight", 0);
envObject->setProperty("hasNotch", false);
envObject->setProperty("deviceBrand", QSysInfo::prettyProductName());
envObject->setProperty("deviceModel", QSysInfo::productType());
mJSE->injectGlobalJSObject(Constant::INJECT_ENVIRONMENT, envObject);
// inject log
NativeLog *nativeLog = new NativeLog();
mJSE->injectGlobalJSFunction(Constant::INJECT_LOG, nativeLog, "function");
// inject empty
NativeEmpty *nativeEmpty = new NativeEmpty();
mJSE->injectGlobalJSFunction(Constant::INJECT_EMPTY, nativeEmpty, "function");
// inject timer set & clear
std::function<void(void)> func = [](){};
TimerExtension *timerExtension = new TimerExtension([this](long timerId){
QJSValueList arguments;
arguments.append(QJSValue((int)timerId));
this->invokeDoricMethod(Constant::DORIC_TIMER_CALLBACK, arguments);
});
mJSE->injectGlobalJSFunction(Constant::INJECT_TIMER_SET, timerExtension, "setTimer");
mJSE->injectGlobalJSFunction(Constant::INJECT_TIMER_CLEAR, timerExtension, "clearTimer");
BridgeExtension *bridgeExtension = new BridgeExtension();
mJSE->injectGlobalJSFunction(Constant::INJECT_BRIDGE, bridgeExtension, "callNative");
});
QtConcurrent::run(&mJSThreadPool, [this]{
loadBuiltinJS(Constant::DORIC_BUNDLE_SANDBOX);
QString libName = Constant::DORIC_MODULE_LIB;
QString libJS = Utils::readAssetFile("/doric", Constant::DORIC_BUNDLE_LIB);
QString script = packageModuleScript(libName, libJS);
mJSE->loadJS(script, "Module://" + libName);
});
}
void JSEngine::prepareContext(int contextId, QString *script) {
QString contextIdString = QString::number(contextId);
QString source = QString(Constant::TEMPLATE_CONTEXT_CREATE)
.replace("%s1", *script)
.replace("%s2", contextIdString)
.replace("%s3", contextIdString)
.replace("%s4", contextIdString);
QJSValue result = engine->evaluate(source, "context://" + contextIdString);
qDebug() << "context://" + contextIdString + " result: " + result.toString();
QJSValue JSEngine::invokeDoricMethod(QString method, QJSValueList arguments)
{
return mJSE->invokeObject(Constant::GLOBAL_DORIC, method, arguments);
}
void JSEngine::destroyContext(int contextId) {
QString contextIdString = QString::number(contextId);
QString source = QString(Constant::TEMPLATE_CONTEXT_DESTROY)
.replace("%s", contextIdString);
QJSValue result = engine->evaluate(source, "_context://" + contextIdString);
qDebug() << "context://" + contextIdString + " result: " + result.toString();
void JSEngine::loadBuiltinJS(QString assetName)
{
QString script = Utils::readAssetFile("/doric", assetName);
QString result = mJSE->loadJS(script, "Assets://" + assetName);
}
void JSEngine::initJSEngine() {
engine->installExtensions(QJSEngine::AllExtensions);
void JSEngine::prepareContext(QString contextId, QString script, QString source)
{
mJSE->loadJS(packageContextScript(contextId, script), "Context://" + source);
}
void JSEngine::injectGlobal() {
QJSValue log = engine->newQObject(nativeLog);
engine->globalObject().setProperty(Constant::INJECT_LOG, log.property("function"));
QJSValue timer = engine->newQObject(nativeTimer);
engine->globalObject().setProperty(Constant::INJECT_TIMER_SET, timer.property("setTimer"));
engine->globalObject().setProperty(Constant::INJECT_TIMER_CLEAR, timer.property("clearTimer"));
QJSValue empty = engine->newQObject(nativeEmpty);
engine->globalObject().setProperty(Constant::INJECT_EMPTY, empty.property("function"));
QJSValue bridge = engine->newQObject(nativeBridge);
engine->globalObject().setProperty(Constant::INJECT_BRIDGE, bridge.property("function"));
QString JSEngine::packageContextScript(QString contextId, QString content)
{
return QString(Constant::TEMPLATE_CONTEXT_CREATE).replace("%s1", content).replace("%s2", contextId).replace("%s3", contextId);
}
void JSEngine::initDoricRuntime() {
{
QResource resource(":/doric/doric-sandbox.js");
QFile *file = new QFile(resource.fileName());
file->open(QFile::ReadOnly | QFile::Text);
QTextStream in(file);
QString script = in.readAll();
file->close();
delete file;
QJSValue result = engine->evaluate(script, "doric-sandbox.js");
qDebug() << "doric-sandbox.js result: " + result.toString();
}
{
QResource resource(":/doric/doric-lib.js");
QFile *file = new QFile(resource.fileName());
file->open(QFile::ReadOnly | QFile::Text);
QTextStream in(file);
QString script = in.readAll();
file->close();
delete file;
QString lib = QString(Constant::TEMPLATE_MODULE)
.replace("%s1", "doric")
.replace("%s2", script);
QJSValue result = engine->evaluate(lib, "doric-lib.js");
qDebug() << "doric-lib.js result: " + result.toString();
}
QString JSEngine::packageModuleScript(QString moduleName, QString content)
{
return QString(Constant::TEMPLATE_MODULE).replace("%s1", moduleName).replace("%s2", content);
}
JSEngine::~JSEngine()
{
}

View File

@@ -1,38 +1,28 @@
#ifndef JS_ENGINE_H
#define JS_ENGINE_H
#ifndef JSENGINE_H
#define JSENGINE_H
#include <QJSEngine>
#include <QJSValue>
#include <QThreadPool>
#include "native/native_bridge.h"
#include "native/native_empty.h"
#include "native/native_log.h"
#include "native/native_timer.h"
#include "registry.h"
class JSEngine {
public:
QJSEngine *engine = new QJSEngine();
Registry *registry = new Registry();
JSEngine();
void prepareContext(int contextId, QString *script);
void destroyContext(int contextId);
#include "interface_jse.h"
class JSEngine : public QObject
{
Q_OBJECT
private:
NativeLog *nativeLog = new NativeLog();
NativeTimer *nativeTimer = new NativeTimer(engine);
NativeEmpty *nativeEmpty = new NativeEmpty();
NativeBridge *nativeBridge = new NativeBridge();
QThreadPool mJSThreadPool;
InterfaceJSE *mJSE;
void initJSEngine();
void loadBuiltinJS(QString assetName);
void prepareContext(QString contextId, QString script, QString source);
QString packageContextScript(QString contextId, QString content);
QString packageModuleScript(QString moduleName, QString content);
public:
explicit JSEngine(QObject *parent = nullptr);
void injectGlobal();
QJSValue invokeDoricMethod(QString method, QJSValueList arguments);
void initDoricRuntime();
~JSEngine();
};
#endif // JS_ENGINE_H
#endif // JSENGINE_H

View File

@@ -0,0 +1,7 @@
#include "native_empty.h"
#include <QDebug>
Q_INVOKABLE QJSValue NativeEmpty::function() {
qDebug() << "nativeEmpty";
return QJSValue::NullValue;
}

View File

@@ -0,0 +1,16 @@
#ifndef NATIVEEMPTY_H
#define NATIVEEMPTY_H
#include <QObject>
#include <QJSValue>
class NativeEmpty : public QObject {
Q_OBJECT
public:
NativeEmpty(QObject *parent = nullptr) : QObject(parent) {}
Q_INVOKABLE QJSValue function();
};
#endif // NATIVEEMPTY_H

View File

@@ -0,0 +1,43 @@
#include "native_jse.h"
#include <QDebug>
NativeJSE::NativeJSE()
{
mJSEngine.installExtensions(QJSEngine::AllExtensions);
}
QString NativeJSE::loadJS(QString script, QString source)
{
return mJSEngine.evaluate(script, source).toString();
}
void NativeJSE::injectGlobalJSObject(QString name, QObject *object)
{
QJSValue jsObject = mJSEngine.newQObject(object);
QList<QByteArray> propertyNames = object->dynamicPropertyNames();
foreach(QByteArray propertyName, propertyNames)
{
QString key = QString::fromStdString(propertyName.toStdString());
if (key == "undefined") {
} else {
jsObject.setProperty(key, mJSEngine.toScriptValue(object->property(propertyName)));
}
}
mJSEngine.globalObject().setProperty(name, jsObject);
}
void NativeJSE::injectGlobalJSFunction(QString name, QObject *function, QString property)
{
QJSValue functionObject = mJSEngine.newQObject(function);
mJSEngine.globalObject().setProperty(name, functionObject.property(property));
}
QJSValue NativeJSE::invokeObject(QString objectName, QString functionName, QJSValueList arguments)
{
QJSValue object = mJSEngine.evaluate(objectName);
QJSValue function = object.property(functionName);
return function.call(arguments);
}

View File

@@ -0,0 +1,23 @@
#ifndef NATIVE_JSE_H
#define NATIVE_JSE_H
#include <QJSEngine>
#include "interface_jse.h"
class NativeJSE : public InterfaceJSE
{
private:
QJSEngine mJSEngine;
public:
NativeJSE();
QString loadJS(QString script, QString source) override;
void injectGlobalJSObject(QString name, QObject *object) override;
void injectGlobalJSFunction(QString name, QObject *function, QString property) override;
QJSValue invokeObject(QString objectName, QString functionName, QJSValueList arguments) override;
};
#endif // NATIVE_JSE_H

View File

@@ -0,0 +1,13 @@
#include <QDebug>
#include "native_log.h"
Q_INVOKABLE void NativeLog::function(QString level, QString content) {
if (level == 'w') {
qWarning() << content;
} else if (level == 'd') {
qDebug() << content;
} else if (level == 'e') {
qCritical() << content;
}
}

View File

@@ -0,0 +1,15 @@
#ifndef NATIVE_LOG_H
#define NATIVE_LOG_H
#include <QObject>
class NativeLog : public QObject {
Q_OBJECT
public:
NativeLog(QObject *parent = nullptr) : QObject(parent) {}
Q_INVOKABLE void function(QString level, QString content);
};
#endif // NATIVE_LOG_H

View File

@@ -0,0 +1,27 @@
#include <QTimer>
#include "timer_extension.h"
#include "../utils/constant.h"
Q_INVOKABLE void TimerExtension::setTimer(long timerId, int time, bool repeat) {
QTimer *timer = new QTimer(this);
timer->setSingleShot(!repeat);
connect(timer, &QTimer::timeout, this, [=] () {
if (deletedTimerIds->contains(timerId)) {
deletedTimerIds->remove(timerId);
delete timer;
} else {
this->method(timerId);
if (!repeat) {
deletedTimerIds->remove(timerId);
delete timer;
}
}
});
timer->start(time);
}
Q_INVOKABLE void TimerExtension::clearTimer(long timerId) {
deletedTimerIds->insert(timerId);
}

View File

@@ -0,0 +1,24 @@
#ifndef NATIVETIMER_H
#define NATIVETIMER_H
#include <QObject>
#include <QSet>
class TimerExtension : public QObject {
Q_OBJECT
private:
QSet<long> *deletedTimerIds = new QSet<long>();
std::function<void(long)> method;
public:
explicit TimerExtension(std::function<void(long)> method, QObject *parent = nullptr) : QObject(parent) {
this->method = method;
}
Q_INVOKABLE void setTimer(long timerId, int time, bool repeat);
Q_INVOKABLE void clearTimer(long timerId);
};
#endif // NATIVETIMER_H