split project with app & doric module
This commit is contained in:
34
doric-Qt/example/doric/engine/DoricBridgeExtension.cpp
Normal file
34
doric-Qt/example/doric/engine/DoricBridgeExtension.cpp
Normal file
@@ -0,0 +1,34 @@
|
||||
#include <QDebug>
|
||||
#include <QJsonDocument>
|
||||
#include <QJsonObject>
|
||||
#include <QMetaObject>
|
||||
|
||||
#include "../DoricContextManager.h"
|
||||
#include "DoricBridgeExtension.h"
|
||||
|
||||
DoricBridgeExtension::DoricBridgeExtension(QObject *parent) : QObject(parent) {}
|
||||
|
||||
void DoricBridgeExtension::callNative(QString contextId, QString module,
|
||||
QString methodName, QString callbackId,
|
||||
QString argument) {
|
||||
qDebug() << "contextId: " + contextId << "module: " + module
|
||||
<< "methodName: " + methodName << "callbackId: " + callbackId
|
||||
<< "jsValue: " + argument;
|
||||
|
||||
DoricContext *context =
|
||||
DoricContextManager::getInstance()->getContext(contextId);
|
||||
|
||||
if (context == nullptr) {
|
||||
qCritical() << "cannot find context: " + contextId;
|
||||
return;
|
||||
}
|
||||
bool classRegistered =
|
||||
context->getDriver()->getRegistry()->acquirePluginInfo(module);
|
||||
if (classRegistered) {
|
||||
QObject *plugin = context->obtainPlugin(module);
|
||||
QMetaObject::invokeMethod(plugin, methodName.toUtf8(), Qt::DirectConnection,
|
||||
QGenericReturnArgument(),
|
||||
Q_ARG(QString, argument),
|
||||
Q_ARG(QString, callbackId));
|
||||
}
|
||||
}
|
17
doric-Qt/example/doric/engine/DoricBridgeExtension.h
Normal file
17
doric-Qt/example/doric/engine/DoricBridgeExtension.h
Normal file
@@ -0,0 +1,17 @@
|
||||
#ifndef BRIDGEEXTENSION_H
|
||||
#define BRIDGEEXTENSION_H
|
||||
|
||||
#include <QJSValue>
|
||||
#include <QObject>
|
||||
|
||||
class DoricBridgeExtension : public QObject {
|
||||
Q_OBJECT
|
||||
public:
|
||||
explicit DoricBridgeExtension(QObject *parent = nullptr);
|
||||
|
||||
Q_INVOKABLE void callNative(QString contextId, QString module,
|
||||
QString methodName, QString callbackId,
|
||||
QString argument);
|
||||
};
|
||||
|
||||
#endif // BRIDGEEXTENSION_H
|
20
doric-Qt/example/doric/engine/DoricInterfaceJSE.h
Normal file
20
doric-Qt/example/doric/engine/DoricInterfaceJSE.h
Normal file
@@ -0,0 +1,20 @@
|
||||
#ifndef INTERFACE_JSE_H
|
||||
#define INTERFACE_JSE_H
|
||||
|
||||
#include <QString>
|
||||
#include <QVariant>
|
||||
|
||||
class DoricInterfaceJSE {
|
||||
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 void invokeObject(QString objectName, QString functionName,
|
||||
QVariantList arguments) = 0;
|
||||
};
|
||||
|
||||
#endif // INTERFACE_JSE_H
|
137
doric-Qt/example/doric/engine/DoricJSEngine.cpp
Normal file
137
doric-Qt/example/doric/engine/DoricJSEngine.cpp
Normal file
@@ -0,0 +1,137 @@
|
||||
#include <QGuiApplication>
|
||||
#include <QJsonObject>
|
||||
#include <QRect>
|
||||
#include <QScreen>
|
||||
#include <QSysInfo>
|
||||
#include <QtConcurrent/QtConcurrent>
|
||||
|
||||
#include "../utils/DoricConstant.h"
|
||||
#include "../utils/DoricUtils.h"
|
||||
#include "DoricBridgeExtension.h"
|
||||
#include "DoricJSEngine.h"
|
||||
#include "DoricNativeEmpty.h"
|
||||
#include "DoricNativeJSE.h"
|
||||
#include "DoricNativeLog.h"
|
||||
#include "DoricNativeRequire.h"
|
||||
#include "DoricTimerExtension.h"
|
||||
|
||||
DoricJSEngine::DoricJSEngine(QObject *parent) : QObject(parent) {
|
||||
mJSThreadPool.setMaxThreadCount(1);
|
||||
mJSThreadPool.setExpiryTimeout(-1);
|
||||
|
||||
{
|
||||
auto result = QtConcurrent::run(
|
||||
&mJSThreadPool, [this] { mJSE = new DoricNativeJSE(JSEType::V8); });
|
||||
}
|
||||
{
|
||||
auto result = 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(DoricConstant::INJECT_ENVIRONMENT, envObject);
|
||||
|
||||
// inject log
|
||||
DoricNativeLog *nativeLog = new DoricNativeLog();
|
||||
mJSE->injectGlobalJSFunction(DoricConstant::INJECT_LOG, nativeLog,
|
||||
"function");
|
||||
|
||||
// inject empty
|
||||
DoricNativeEmpty *nativeEmpty = new DoricNativeEmpty();
|
||||
mJSE->injectGlobalJSFunction(DoricConstant::INJECT_EMPTY, nativeEmpty,
|
||||
"function");
|
||||
|
||||
// inject require
|
||||
DoricNativeRequire *nativeRequire = new DoricNativeRequire();
|
||||
mJSE->injectGlobalJSFunction(DoricConstant::INJECT_REQUIRE, nativeRequire,
|
||||
"function");
|
||||
|
||||
// inject timer set & clear
|
||||
DoricTimerExtension *timerExtension =
|
||||
new DoricTimerExtension([this](long timerId) {
|
||||
auto result = QtConcurrent::run(&mJSThreadPool, [this, timerId] {
|
||||
QVariantList arguments;
|
||||
arguments.push_back(QVariant((int)timerId));
|
||||
this->invokeDoricMethod(DoricConstant::DORIC_TIMER_CALLBACK,
|
||||
arguments);
|
||||
});
|
||||
});
|
||||
mJSE->injectGlobalJSFunction(DoricConstant::INJECT_TIMER_SET,
|
||||
timerExtension, "setTimer");
|
||||
mJSE->injectGlobalJSFunction(DoricConstant::INJECT_TIMER_CLEAR,
|
||||
timerExtension, "clearTimer");
|
||||
|
||||
DoricBridgeExtension *bridgeExtension = new DoricBridgeExtension();
|
||||
mJSE->injectGlobalJSFunction(DoricConstant::INJECT_BRIDGE,
|
||||
bridgeExtension, "callNative");
|
||||
});
|
||||
}
|
||||
|
||||
{
|
||||
auto result = QtConcurrent::run(&mJSThreadPool, [this] {
|
||||
loadBuiltinJS(DoricConstant::DORIC_BUNDLE_SANDBOX);
|
||||
|
||||
QString libName = DoricConstant::DORIC_MODULE_LIB;
|
||||
QString libJS =
|
||||
DoricUtils::readAssetFile("/doric", DoricConstant::DORIC_BUNDLE_LIB);
|
||||
QString script = packageModuleScript(libName, libJS);
|
||||
|
||||
mJSE->loadJS(script, "Module://" + libName);
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
void DoricJSEngine::prepareContext(QString contextId, QString script,
|
||||
QString source) {
|
||||
mJSE->loadJS(packageContextScript(contextId, script), "Context://" + source);
|
||||
}
|
||||
|
||||
void DoricJSEngine::destroyContext(QString contextId) {
|
||||
QString script =
|
||||
QString(DoricConstant::TEMPLATE_CONTEXT_DESTROY).replace("%s", contextId);
|
||||
mJSE->loadJS(script, "_Context://" + contextId);
|
||||
}
|
||||
|
||||
void DoricJSEngine::invokeDoricMethod(QString method, QVariantList arguments) {
|
||||
return mJSE->invokeObject(DoricConstant::GLOBAL_DORIC, method, arguments);
|
||||
}
|
||||
|
||||
void DoricJSEngine::loadBuiltinJS(QString assetName) {
|
||||
QString script = DoricUtils::readAssetFile("/doric", assetName);
|
||||
QString result = mJSE->loadJS(script, "Assets://" + assetName);
|
||||
qDebug() << result;
|
||||
}
|
||||
|
||||
QString DoricJSEngine::packageContextScript(QString contextId,
|
||||
QString content) {
|
||||
return QString(DoricConstant::TEMPLATE_CONTEXT_CREATE)
|
||||
.replace("%s1", content)
|
||||
.replace("%s2", contextId)
|
||||
.replace("%s3", contextId);
|
||||
}
|
||||
|
||||
QString DoricJSEngine::packageModuleScript(QString moduleName,
|
||||
QString content) {
|
||||
return QString(DoricConstant::TEMPLATE_MODULE)
|
||||
.replace("%s1", moduleName)
|
||||
.replace("%s2", content);
|
||||
}
|
||||
|
||||
DoricRegistry *DoricJSEngine::getRegistry() { return this->mRegistry; }
|
||||
|
||||
DoricJSEngine::~DoricJSEngine() {}
|
36
doric-Qt/example/doric/engine/DoricJSEngine.h
Normal file
36
doric-Qt/example/doric/engine/DoricJSEngine.h
Normal file
@@ -0,0 +1,36 @@
|
||||
#ifndef JSENGINE_H
|
||||
#define JSENGINE_H
|
||||
|
||||
#include <QJSValue>
|
||||
#include <QThreadPool>
|
||||
|
||||
#include "../DoricRegistry.h"
|
||||
#include "DoricInterfaceJSE.h"
|
||||
|
||||
class DoricJSEngine : public QObject {
|
||||
Q_OBJECT
|
||||
private:
|
||||
DoricInterfaceJSE *mJSE;
|
||||
DoricRegistry *mRegistry = new DoricRegistry();
|
||||
|
||||
void loadBuiltinJS(QString assetName);
|
||||
QString packageContextScript(QString contextId, QString content);
|
||||
QString packageModuleScript(QString moduleName, QString content);
|
||||
|
||||
public:
|
||||
QThreadPool mJSThreadPool;
|
||||
|
||||
explicit DoricJSEngine(QObject *parent = nullptr);
|
||||
|
||||
~DoricJSEngine();
|
||||
|
||||
void prepareContext(QString contextId, QString script, QString source);
|
||||
|
||||
void destroyContext(QString contextId);
|
||||
|
||||
void invokeDoricMethod(QString method, QVariantList arguments);
|
||||
|
||||
DoricRegistry *getRegistry();
|
||||
};
|
||||
|
||||
#endif // JSENGINE_H
|
6
doric-Qt/example/doric/engine/DoricNativeEmpty.cpp
Normal file
6
doric-Qt/example/doric/engine/DoricNativeEmpty.cpp
Normal file
@@ -0,0 +1,6 @@
|
||||
#include "DoricNativeEmpty.h"
|
||||
#include <QDebug>
|
||||
|
||||
Q_INVOKABLE void DoricNativeEmpty::function() {
|
||||
qDebug() << "nativeEmpty";
|
||||
}
|
15
doric-Qt/example/doric/engine/DoricNativeEmpty.h
Normal file
15
doric-Qt/example/doric/engine/DoricNativeEmpty.h
Normal file
@@ -0,0 +1,15 @@
|
||||
#ifndef NATIVEEMPTY_H
|
||||
#define NATIVEEMPTY_H
|
||||
|
||||
#include <QObject>
|
||||
|
||||
class DoricNativeEmpty : public QObject {
|
||||
Q_OBJECT
|
||||
|
||||
public:
|
||||
DoricNativeEmpty(QObject *parent = nullptr) : QObject(parent) {}
|
||||
|
||||
Q_INVOKABLE void function();
|
||||
};
|
||||
|
||||
#endif // NATIVEEMPTY_H
|
66
doric-Qt/example/doric/engine/DoricNativeJSE.cpp
Normal file
66
doric-Qt/example/doric/engine/DoricNativeJSE.cpp
Normal file
@@ -0,0 +1,66 @@
|
||||
#include <QDebug>
|
||||
#include <QJsonDocument>
|
||||
#include <QJsonObject>
|
||||
|
||||
#include "../utils/DoricUtils.h"
|
||||
#include "DoricNativeJSE.h"
|
||||
|
||||
DoricNativeJSE::DoricNativeJSE(JSEType type) {
|
||||
mType = type;
|
||||
if (mType == JSEType::V8) {
|
||||
v8Executor = new V8Executor();
|
||||
} else if (mType == JSEType::Native) {
|
||||
nativeExecutor = new NativeExecutor();
|
||||
}
|
||||
}
|
||||
|
||||
QString DoricNativeJSE::loadJS(QString script, QString source) {
|
||||
if (mType == JSEType::V8) {
|
||||
return v8Executor->loadJS(script, source);
|
||||
} else if (mType == JSEType::Native) {
|
||||
return nativeExecutor->loadJS(script, source);
|
||||
}
|
||||
}
|
||||
|
||||
void DoricNativeJSE::injectGlobalJSObject(QString name, QObject *object) {
|
||||
if (mType == JSEType::V8) {
|
||||
QJsonObject jsonObject;
|
||||
|
||||
QList<QByteArray> propertyNames = object->dynamicPropertyNames();
|
||||
foreach (QByteArray propertyName, propertyNames) {
|
||||
QString key = QString::fromStdString(propertyName.toStdString());
|
||||
object->property(propertyName).toString();
|
||||
if (key == "undefined" || key.isEmpty()) {
|
||||
|
||||
} else {
|
||||
jsonObject[key] =
|
||||
QJsonValue::fromVariant(object->property(propertyName));
|
||||
}
|
||||
}
|
||||
|
||||
QJsonDocument doc(jsonObject);
|
||||
QString strJson(doc.toJson(QJsonDocument::Compact));
|
||||
|
||||
v8Executor->injectGlobalJSObject(name, strJson.toUtf8().constData());
|
||||
} else if (mType == JSEType::Native) {
|
||||
nativeExecutor->injectGlobalJSObject(name, object);
|
||||
}
|
||||
}
|
||||
|
||||
void DoricNativeJSE::injectGlobalJSFunction(QString name, QObject *function,
|
||||
QString property) {
|
||||
if (mType == JSEType::V8) {
|
||||
v8Executor->injectGlobalJSFunction(name, function, property);
|
||||
} else if (mType == JSEType::Native) {
|
||||
nativeExecutor->injectGlobalJSFunction(name, function, property);
|
||||
}
|
||||
}
|
||||
|
||||
void DoricNativeJSE::invokeObject(QString objectName, QString functionName,
|
||||
QVariantList arguments) {
|
||||
if (mType == JSEType::V8) {
|
||||
v8Executor->invokeObject(objectName, functionName, arguments);
|
||||
} else if (mType == JSEType::Native) {
|
||||
nativeExecutor->invokeObject(objectName, functionName, arguments);
|
||||
}
|
||||
}
|
31
doric-Qt/example/doric/engine/DoricNativeJSE.h
Normal file
31
doric-Qt/example/doric/engine/DoricNativeJSE.h
Normal file
@@ -0,0 +1,31 @@
|
||||
#ifndef NATIVE_JSE_H
|
||||
#define NATIVE_JSE_H
|
||||
|
||||
#include "DoricInterfaceJSE.h"
|
||||
#include "native/NativeExecutor.h"
|
||||
#include "v8/V8Executor.h"
|
||||
|
||||
enum class JSEType { V8, Native };
|
||||
|
||||
class DoricNativeJSE : public DoricInterfaceJSE {
|
||||
private:
|
||||
JSEType mType;
|
||||
|
||||
V8Executor *v8Executor;
|
||||
NativeExecutor *nativeExecutor;
|
||||
|
||||
public:
|
||||
DoricNativeJSE(JSEType type);
|
||||
|
||||
QString loadJS(QString script, QString source) override;
|
||||
|
||||
void injectGlobalJSObject(QString name, QObject *object) override;
|
||||
|
||||
void injectGlobalJSFunction(QString name, QObject *function,
|
||||
QString property) override;
|
||||
|
||||
void invokeObject(QString objectName, QString functionName,
|
||||
QVariantList arguments) override;
|
||||
};
|
||||
|
||||
#endif // NATIVE_JSE_H
|
13
doric-Qt/example/doric/engine/DoricNativeLog.cpp
Normal file
13
doric-Qt/example/doric/engine/DoricNativeLog.cpp
Normal file
@@ -0,0 +1,13 @@
|
||||
#include <QDebug>
|
||||
|
||||
#include "DoricNativeLog.h"
|
||||
|
||||
Q_INVOKABLE void DoricNativeLog::function(QString level, QString content) {
|
||||
if (level == 'w') {
|
||||
qWarning() << content;
|
||||
} else if (level == 'd') {
|
||||
qDebug() << content;
|
||||
} else if (level == 'e') {
|
||||
qCritical() << content;
|
||||
}
|
||||
}
|
15
doric-Qt/example/doric/engine/DoricNativeLog.h
Normal file
15
doric-Qt/example/doric/engine/DoricNativeLog.h
Normal file
@@ -0,0 +1,15 @@
|
||||
#ifndef NATIVE_LOG_H
|
||||
#define NATIVE_LOG_H
|
||||
|
||||
#include <QObject>
|
||||
|
||||
class DoricNativeLog : public QObject {
|
||||
Q_OBJECT
|
||||
|
||||
public:
|
||||
DoricNativeLog(QObject *parent = nullptr) : QObject(parent) {}
|
||||
|
||||
Q_INVOKABLE void function(QString level, QString content);
|
||||
};
|
||||
|
||||
#endif // NATIVE_LOG_H
|
7
doric-Qt/example/doric/engine/DoricNativeRequire.cpp
Normal file
7
doric-Qt/example/doric/engine/DoricNativeRequire.cpp
Normal file
@@ -0,0 +1,7 @@
|
||||
#include "DoricNativeRequire.h"
|
||||
|
||||
#include <QDebug>
|
||||
|
||||
Q_INVOKABLE void DoricNativeRequire::function(QString name) {
|
||||
qDebug() << "nativeRequire: " << name.toUtf8();
|
||||
}
|
15
doric-Qt/example/doric/engine/DoricNativeRequire.h
Normal file
15
doric-Qt/example/doric/engine/DoricNativeRequire.h
Normal file
@@ -0,0 +1,15 @@
|
||||
#ifndef NATIVE_REQUIRE_H
|
||||
#define NATIVE_REQUIRE_H
|
||||
|
||||
#include <QObject>
|
||||
|
||||
class DoricNativeRequire : public QObject {
|
||||
Q_OBJECT
|
||||
|
||||
public:
|
||||
DoricNativeRequire(QObject *parent = nullptr) : QObject(parent) {}
|
||||
|
||||
Q_INVOKABLE void function(QString name);
|
||||
};
|
||||
|
||||
#endif // NATIVE_REQUIRE_H
|
36
doric-Qt/example/doric/engine/DoricPromise.h
Normal file
36
doric-Qt/example/doric/engine/DoricPromise.h
Normal file
@@ -0,0 +1,36 @@
|
||||
#ifndef DORICPROMISE_H
|
||||
#define DORICPROMISE_H
|
||||
|
||||
#include <QDebug>
|
||||
|
||||
#include "DoricContext.h"
|
||||
#include "utils/DoricConstant.h"
|
||||
|
||||
class DoricPromise {
|
||||
public:
|
||||
static void resolve(DoricContext *context, QString callbackId,
|
||||
QVariantList args) {
|
||||
QVariantList params;
|
||||
params.append(context->getContextId());
|
||||
params.append(callbackId);
|
||||
|
||||
foreach (QVariant arg, args) { params.append(arg); }
|
||||
|
||||
context->getDriver()->invokeDoricMethod(DoricConstant::DORIC_BRIDGE_RESOLVE,
|
||||
params);
|
||||
}
|
||||
|
||||
static void reject(DoricContext *context, QString callbackId,
|
||||
QVariantList args) {
|
||||
QVariantList params;
|
||||
params.append(context->getContextId());
|
||||
params.append(callbackId);
|
||||
|
||||
foreach (QVariant arg, args) { params.append(arg); }
|
||||
|
||||
context->getDriver()->invokeDoricMethod(DoricConstant::DORIC_BRIDGE_REJECT,
|
||||
params);
|
||||
}
|
||||
};
|
||||
|
||||
#endif // DORICPROMISE_H
|
36
doric-Qt/example/doric/engine/DoricTimerExtension.cpp
Normal file
36
doric-Qt/example/doric/engine/DoricTimerExtension.cpp
Normal file
@@ -0,0 +1,36 @@
|
||||
#include <QCoreApplication>
|
||||
#include <QTimer>
|
||||
|
||||
#include "../utils/DoricConstant.h"
|
||||
#include "DoricTimerExtension.h"
|
||||
|
||||
Q_INVOKABLE void DoricTimerExtension::setTimer(long timerId, int time,
|
||||
bool repeat) {
|
||||
|
||||
connect(this, &DoricTimerExtension::startTimer, qApp, [=]() {
|
||||
QTimer *timer = new QTimer();
|
||||
timer->setSingleShot(!repeat);
|
||||
|
||||
connect(timer, &QTimer::timeout, [=]() {
|
||||
if (deletedTimerIds->contains(timerId)) {
|
||||
deletedTimerIds->remove(timerId);
|
||||
timer->deleteLater();
|
||||
} else {
|
||||
this->method(timerId);
|
||||
|
||||
if (!repeat) {
|
||||
deletedTimerIds->remove(timerId);
|
||||
timer->deleteLater();
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
timer->start(time);
|
||||
});
|
||||
|
||||
emit startTimer();
|
||||
}
|
||||
|
||||
Q_INVOKABLE void DoricTimerExtension::clearTimer(long timerId) {
|
||||
deletedTimerIds->insert(timerId);
|
||||
}
|
28
doric-Qt/example/doric/engine/DoricTimerExtension.h
Normal file
28
doric-Qt/example/doric/engine/DoricTimerExtension.h
Normal file
@@ -0,0 +1,28 @@
|
||||
#ifndef NATIVETIMER_H
|
||||
#define NATIVETIMER_H
|
||||
|
||||
#include <QObject>
|
||||
#include <QSet>
|
||||
|
||||
class DoricTimerExtension : public QObject {
|
||||
Q_OBJECT
|
||||
|
||||
private:
|
||||
QSet<long> *deletedTimerIds = new QSet<long>();
|
||||
std::function<void(long)> method;
|
||||
|
||||
public:
|
||||
explicit DoricTimerExtension(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);
|
||||
|
||||
signals:
|
||||
void startTimer();
|
||||
};
|
||||
#endif // NATIVETIMER_H
|
82
doric-Qt/example/doric/engine/native/NativeExecutor.cpp
Normal file
82
doric-Qt/example/doric/engine/native/NativeExecutor.cpp
Normal file
@@ -0,0 +1,82 @@
|
||||
#include "NativeExecutor.h"
|
||||
|
||||
#include <QDebug>
|
||||
|
||||
NativeExecutor::NativeExecutor() {
|
||||
mJSEngine = new QJSEngine();
|
||||
mJSEngine->installExtensions(QJSEngine::AllExtensions);
|
||||
}
|
||||
|
||||
NativeExecutor::~NativeExecutor() { delete mJSEngine; }
|
||||
|
||||
QString NativeExecutor::loadJS(QString script, QString source) {
|
||||
return mJSEngine->evaluate(script, source).toString();
|
||||
}
|
||||
|
||||
void NativeExecutor::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 NativeExecutor::injectGlobalJSFunction(QString name, QObject *function,
|
||||
QString property) {
|
||||
QJSValue functionObject = mJSEngine->newQObject(function);
|
||||
mJSEngine->globalObject().setProperty(name,
|
||||
functionObject.property(property));
|
||||
}
|
||||
|
||||
QJSValue NativeExecutor::invokeObject(QString objectName, QString functionName,
|
||||
QVariantList arguments) {
|
||||
QJSValue object = mJSEngine->evaluate(objectName);
|
||||
QJSValue function = object.property(functionName);
|
||||
|
||||
QJSValueList args;
|
||||
foreach (QVariant variant, arguments) {
|
||||
if (variant.type() == QVariant::String) {
|
||||
args.push_back(QJSValue(variant.toString()));
|
||||
} else if (variant.type() == QVariant::Map) {
|
||||
QJSValue arg = mJSEngine->newObject();
|
||||
QMap<QString, QVariant> map = variant.toMap();
|
||||
foreach (QString key, map.keys()) {
|
||||
QVariant value = map.value(key);
|
||||
if (value.type() == QVariant::String) {
|
||||
arg.setProperty(key, value.toString());
|
||||
} else if (value.type() == QVariant::Int) {
|
||||
arg.setProperty(key, value.toInt());
|
||||
}
|
||||
}
|
||||
args.push_back(arg);
|
||||
} else if (variant.type() == QVariant::StringList) {
|
||||
QStringList array = variant.toStringList();
|
||||
QJSValue arg = mJSEngine->newArray(array.size());
|
||||
|
||||
for (int i = 0; i != array.size(); i++) {
|
||||
arg.setProperty(i, array.at(i));
|
||||
}
|
||||
args.push_back(arg);
|
||||
}
|
||||
}
|
||||
|
||||
QJSValue result = function.call(args);
|
||||
if (result.isError()) {
|
||||
qCritical() << "++++++++++++++++++++++++++++++++++++++++++++++++";
|
||||
qCritical() << result.toString();
|
||||
QStringList stacktraces = result.property("stack").toString().split("\n");
|
||||
foreach (QString stacktrace, stacktraces) { qDebug() << stacktrace; }
|
||||
qCritical() << "------------------------------------------------";
|
||||
}
|
||||
|
||||
return result;
|
||||
}
|
27
doric-Qt/example/doric/engine/native/NativeExecutor.h
Normal file
27
doric-Qt/example/doric/engine/native/NativeExecutor.h
Normal file
@@ -0,0 +1,27 @@
|
||||
#ifndef NATIVEEXECUTOR_H
|
||||
#define NATIVEEXECUTOR_H
|
||||
|
||||
#include <QJSEngine>
|
||||
|
||||
class NativeExecutor {
|
||||
|
||||
private:
|
||||
QJSEngine *mJSEngine;
|
||||
|
||||
public:
|
||||
NativeExecutor();
|
||||
|
||||
~NativeExecutor();
|
||||
|
||||
QString loadJS(QString script, QString source);
|
||||
|
||||
void injectGlobalJSObject(QString name, QObject *object);
|
||||
|
||||
void injectGlobalJSFunction(QString name, QObject *function,
|
||||
QString property);
|
||||
|
||||
QJSValue invokeObject(QString objectName, QString functionName,
|
||||
QVariantList arguments);
|
||||
};
|
||||
|
||||
#endif // NATIVEEXECUTOR_H
|
95
doric-Qt/example/doric/engine/v8/JSValueHelper.cpp
Normal file
95
doric-Qt/example/doric/engine/v8/JSValueHelper.cpp
Normal file
@@ -0,0 +1,95 @@
|
||||
#include "JSValueHelper.h"
|
||||
|
||||
#include <QJsonDocument>
|
||||
#include <QJsonObject>
|
||||
|
||||
std::string JS2String(v8::Local<v8::Value> object) {
|
||||
v8::Isolate *isolate = v8::Isolate::GetCurrent();
|
||||
v8::HandleScope handleScope(isolate);
|
||||
v8::Local<v8::Context> context = isolate->GetEnteredOrMicrotaskContext();
|
||||
if (object->IsString() || object->IsRegExp() || object->IsFunction()) {
|
||||
v8::String::Utf8Value utf8(isolate, object);
|
||||
return std::string(*utf8);
|
||||
}
|
||||
if (object->IsObject()) {
|
||||
v8::MaybeLocal<v8::String> str = v8::JSON::Stringify(
|
||||
context, object->ToObject(context).ToLocalChecked());
|
||||
if (str.IsEmpty()) {
|
||||
return "<string conversion failed>";
|
||||
}
|
||||
v8::Local<v8::String> s = str.ToLocalChecked();
|
||||
v8::String::Utf8Value str2(isolate, s);
|
||||
return std::string(*str2 ? *str2 : "<string conversion failed>");
|
||||
}
|
||||
|
||||
v8::String::Utf8Value str(
|
||||
isolate, v8::JSON::Stringify(context, object).ToLocalChecked());
|
||||
return std::string(*str ? *str : "<string conversion failed>");
|
||||
}
|
||||
|
||||
double JS2Number(v8::Local<v8::Value> value) {
|
||||
v8::HandleScope handleScope(v8::Isolate::GetCurrent());
|
||||
v8::Local<v8::Context> context =
|
||||
v8::Isolate::GetCurrent()->GetCurrentContext();
|
||||
if (value->IsNumber()) {
|
||||
return value->ToNumber(context).ToLocalChecked()->Value();
|
||||
} else {
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
|
||||
bool JS2Bool(v8::Local<v8::Value> value) {
|
||||
v8::HandleScope handleScope(v8::Isolate::GetCurrent());
|
||||
if (value->IsBoolean()) {
|
||||
return value->ToBoolean(v8::Isolate::GetCurrent())->Value();
|
||||
} else {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
v8::Local<v8::Value> String2JS(std::string string) {
|
||||
v8::Isolate *isolate = v8::Isolate::GetCurrent();
|
||||
v8::EscapableHandleScope handleScope(isolate);
|
||||
v8::Local<v8::Context> context = isolate->GetEnteredOrMicrotaskContext();
|
||||
v8::Local<v8::String> jsString = NewV8String(string.c_str());
|
||||
|
||||
v8::Local<v8::Value> ret =
|
||||
v8::JSON::Parse(context, jsString).ToLocalChecked();
|
||||
|
||||
return handleScope.Escape(ret);
|
||||
}
|
||||
|
||||
v8::Local<v8::Value> Variant2JS(QVariant variant) {
|
||||
v8::Isolate *isolate = v8::Isolate::GetCurrent();
|
||||
|
||||
v8::EscapableHandleScope handle_scope(isolate);
|
||||
v8::Local<v8::Context> context = isolate->GetEnteredOrMicrotaskContext();
|
||||
|
||||
v8::Local<v8::Value> jsValue;
|
||||
if (variant.type() == QVariant::String) {
|
||||
jsValue = NewV8String(variant.toString().toUtf8().constData());
|
||||
} else if (variant.type() == QVariant::Map) {
|
||||
QMap<QString, QVariant> map = variant.toMap();
|
||||
|
||||
QJsonObject jsonObject;
|
||||
foreach (QString key, map.keys()) {
|
||||
QVariant value = map.value(key);
|
||||
jsonObject.insert(key, QJsonValue::fromVariant(value));
|
||||
}
|
||||
QJsonDocument doc(jsonObject);
|
||||
QString strJson(doc.toJson(QJsonDocument::Compact));
|
||||
jsValue = String2JS(strJson.toUtf8().constData());
|
||||
} else if (variant.type() == QVariant::StringList) {
|
||||
QStringList list = variant.toStringList();
|
||||
v8::Handle<v8::Array> array = v8::Array::New(isolate, list.size());
|
||||
for (int i = 0; i != list.size(); i++) {
|
||||
v8::Local<v8::Value> value = NewV8String(list.at(i).toUtf8().constData());
|
||||
auto result = array->Set(context, i, value);
|
||||
result.ToChecked();
|
||||
}
|
||||
jsValue = array;
|
||||
} else if (variant.type() == QVariant::Int) {
|
||||
jsValue = v8::Number::New(isolate, variant.toDouble());
|
||||
}
|
||||
return handle_scope.Escape(jsValue);
|
||||
}
|
24
doric-Qt/example/doric/engine/v8/JSValueHelper.h
Normal file
24
doric-Qt/example/doric/engine/v8/JSValueHelper.h
Normal file
@@ -0,0 +1,24 @@
|
||||
#ifndef JSVALUEHELPER_H
|
||||
#define JSVALUEHELPER_H
|
||||
|
||||
#include <QVariant>
|
||||
#include <string>
|
||||
|
||||
#include "v8.h"
|
||||
|
||||
#define NewV8String(name) \
|
||||
v8::String::NewFromUtf8(v8::Isolate::GetCurrent(), name, \
|
||||
v8::NewStringType::kNormal) \
|
||||
.ToLocalChecked()
|
||||
|
||||
std::string JS2String(v8::Local<v8::Value> object);
|
||||
|
||||
double JS2Number(v8::Local<v8::Value> value);
|
||||
|
||||
bool JS2Bool(v8::Local<v8::Value> value);
|
||||
|
||||
v8::Local<v8::Value> String2JS(std::string string);
|
||||
|
||||
v8::Local<v8::Value> Variant2JS(QVariant variant);
|
||||
|
||||
#endif // JSVALUEHELPER_H
|
348
doric-Qt/example/doric/engine/v8/V8Executor.cpp
Normal file
348
doric-Qt/example/doric/engine/v8/V8Executor.cpp
Normal file
@@ -0,0 +1,348 @@
|
||||
#include <QDebug>
|
||||
|
||||
#include "JSValueHelper.h"
|
||||
#include "V8Executor.h"
|
||||
|
||||
V8Executor::V8Executor() : platform(v8::platform::NewDefaultPlatform()) {
|
||||
v8::V8::InitializeICUDefaultLocation("");
|
||||
v8::V8::InitializeExternalStartupData("");
|
||||
v8::V8::InitializePlatform(platform.get());
|
||||
v8::V8::Initialize();
|
||||
|
||||
create_params.array_buffer_allocator =
|
||||
v8::ArrayBuffer::Allocator::NewDefaultAllocator();
|
||||
m_isolate = v8::Isolate::New(create_params);
|
||||
m_isolate_scope = new v8::Isolate::Scope(m_isolate);
|
||||
|
||||
v8::Isolate *isolate = v8::Isolate::GetCurrent();
|
||||
v8::HandleScope scope(isolate);
|
||||
|
||||
v8::Local<v8::ObjectTemplate> global = v8::ObjectTemplate::New(isolate);
|
||||
v8::Local<v8::Context> context = v8::Context::New(isolate, nullptr, global);
|
||||
context->Enter();
|
||||
m_global_context = new v8::Global<v8::Context>(isolate, context);
|
||||
}
|
||||
|
||||
V8Executor::~V8Executor() {
|
||||
{
|
||||
v8::HandleScope scope(m_isolate);
|
||||
v8::Local<v8::Context> context = m_global_context->Get(m_isolate);
|
||||
context->Exit();
|
||||
}
|
||||
m_global_context->Reset();
|
||||
delete m_global_context;
|
||||
delete m_isolate_scope;
|
||||
m_isolate->Dispose();
|
||||
v8::V8::Dispose();
|
||||
v8::V8::ShutdownPlatform();
|
||||
delete create_params.array_buffer_allocator;
|
||||
}
|
||||
|
||||
QString V8Executor::loadJS(QString script, QString source) {
|
||||
v8::Isolate *isolate = m_isolate;
|
||||
|
||||
std::string exception;
|
||||
|
||||
v8::HandleScope scope(isolate);
|
||||
v8::Local<v8::Value> ret = innerExec(script.toUtf8().constData(),
|
||||
source.toUtf8().constData(), &exception);
|
||||
std::string result = JS2String(ret);
|
||||
|
||||
return QString::fromUtf8(result.c_str());
|
||||
}
|
||||
|
||||
void V8Executor::injectGlobalJSObject(QString name, std::string target) {
|
||||
v8::Isolate *isolate = m_isolate;
|
||||
v8::HandleScope handleScope(isolate);
|
||||
|
||||
v8::Local<v8::Value> local = String2JS(target);
|
||||
|
||||
injectObject(name.toUtf8().constData(), local);
|
||||
}
|
||||
|
||||
void V8Executor::injectGlobalJSFunction(QString name, QObject *function,
|
||||
QString property) {
|
||||
if (map->keys().contains(name)) {
|
||||
qCritical() << "already injected";
|
||||
return;
|
||||
} else {
|
||||
QPair<QObject *, QString> pair(function, property);
|
||||
map->insert(name, pair);
|
||||
}
|
||||
injectFunctions(nullptr, name.toUtf8().constData(), true);
|
||||
}
|
||||
|
||||
void V8Executor::invokeObject(QString objectName, QString functionName,
|
||||
QVariantList arguments) {
|
||||
std::string exception;
|
||||
v8::HandleScope handleScope(m_isolate);
|
||||
int valueSize = arguments.size();
|
||||
|
||||
auto js_values = new v8::Local<v8::Value>[valueSize];
|
||||
for (int i = 0; i < valueSize; i++) {
|
||||
js_values[i] = Variant2JS(arguments.at(i));
|
||||
}
|
||||
v8::Local<v8::Value> value = invokeMethod(objectName.toUtf8().constData(),
|
||||
functionName.toUtf8().constData(),
|
||||
valueSize, js_values, &exception);
|
||||
delete[] js_values;
|
||||
}
|
||||
|
||||
// private segment
|
||||
void V8Executor::injectObject(const char *string, v8::Local<v8::Value> local) {
|
||||
v8::Isolate *isolate = m_isolate;
|
||||
|
||||
v8::HandleScope handle_scope(isolate);
|
||||
v8::Local<v8::Context> context = isolate->GetEnteredOrMicrotaskContext();
|
||||
v8::Local<v8::Object> object = context->Global();
|
||||
v8::Maybe<bool> result = object->Set(context, NewV8String(string), local);
|
||||
result.ToChecked();
|
||||
}
|
||||
|
||||
v8::Local<v8::Value> V8Executor::innerExec(const char *script,
|
||||
const char *source,
|
||||
std::string *exception_str) {
|
||||
v8::Isolate *isolate = m_isolate;
|
||||
|
||||
v8::EscapableHandleScope handle_scope(isolate);
|
||||
v8::Local<v8::Value> result = Undefined(isolate);
|
||||
v8::Local<v8::Context> context = isolate->GetEnteredOrMicrotaskContext();
|
||||
v8::Context::Scope context_scope(context);
|
||||
v8::ScriptOrigin origin(NewV8String(source));
|
||||
if (script) {
|
||||
v8::Local<v8::String> jsSource = NewV8String(script);
|
||||
v8::TryCatch try_catch(isolate);
|
||||
try_catch.SetVerbose(true);
|
||||
|
||||
v8::MaybeLocal<v8::Script> maybeScript =
|
||||
v8::Script::Compile(context, jsSource, &origin);
|
||||
|
||||
if (!maybeScript.IsEmpty()) {
|
||||
v8::Local<v8::Script> js_script = maybeScript.ToLocalChecked();
|
||||
v8::MaybeLocal<v8::Value> res = js_script->Run(context);
|
||||
if (!res.IsEmpty()) {
|
||||
result = res.ToLocalChecked();
|
||||
}
|
||||
}
|
||||
|
||||
v8::Local<v8::Value> exception = try_catch.Exception();
|
||||
if (!exception.IsEmpty()) {
|
||||
if (exception->IsObject()) {
|
||||
v8::Local<v8::Object> exc = v8::Local<v8::Object>::Cast(exception);
|
||||
v8::Local<v8::Value> stack =
|
||||
exc->Get(context, NewV8String("stack")).FromMaybe(exception);
|
||||
*exception_str = JS2String(stack);
|
||||
} else {
|
||||
*exception_str = JS2String(exception);
|
||||
}
|
||||
}
|
||||
}
|
||||
return handle_scope.Escape(result);
|
||||
}
|
||||
|
||||
static void InjectedFunction(const v8::FunctionCallbackInfo<v8::Value> &args) {
|
||||
v8::Isolate *isolate = args.GetIsolate();
|
||||
v8::HandleScope scope(isolate);
|
||||
v8::Local<v8::Context> context = isolate->GetEnteredOrMicrotaskContext();
|
||||
v8::Local<v8::Object> data = args.Data()->ToObject(context).ToLocalChecked();
|
||||
|
||||
std::string objectKey =
|
||||
JS2String(data->Get(context, NewV8String("obj")).ToLocalChecked())
|
||||
.c_str();
|
||||
std::string functionKey =
|
||||
JS2String(data->Get(context, NewV8String("func")).ToLocalChecked())
|
||||
.c_str();
|
||||
bool hashKey = data->Get(context, NewV8String("hashKey"))
|
||||
.ToLocalChecked()
|
||||
->BooleanValue(isolate);
|
||||
|
||||
// invoke
|
||||
QPair<QObject *, QString> pair =
|
||||
map->find(QString::fromUtf8(functionKey.c_str())).value();
|
||||
|
||||
QString functionKeyQString = QString::fromUtf8(functionKey.c_str());
|
||||
|
||||
if (args.Length() == 0) {
|
||||
if (functionKeyQString == "nativeEmpty") {
|
||||
QMetaObject::invokeMethod(pair.first, pair.second.toUtf8().constData(),
|
||||
Qt::DirectConnection, QGenericReturnArgument());
|
||||
}
|
||||
} else if (args.Length() == 1) {
|
||||
if (functionKeyQString == "nativeRequire") {
|
||||
v8::Local<v8::Value> arg = args[0];
|
||||
std::string argString = JS2String(arg);
|
||||
|
||||
QMetaObject::invokeMethod(
|
||||
pair.first, pair.second.toUtf8().constData(), Qt::DirectConnection,
|
||||
QGenericReturnArgument(),
|
||||
Q_ARG(QString, QString::fromUtf8(argString.c_str())));
|
||||
} else if (functionKeyQString == "nativeClearTimer") {
|
||||
v8::Local<v8::Value> arg = args[0];
|
||||
double number = JS2Number(arg);
|
||||
|
||||
QMetaObject::invokeMethod(pair.first, pair.second.toUtf8().constData(),
|
||||
Qt::DirectConnection, QGenericReturnArgument(),
|
||||
Q_ARG(long, number));
|
||||
}
|
||||
|
||||
} else if (args.Length() == 2) {
|
||||
v8::Local<v8::Value> arg0 = args[0];
|
||||
std::string argString0 = JS2String(arg0);
|
||||
|
||||
v8::Local<v8::Value> arg1 = args[1];
|
||||
std::string argString1 = JS2String(arg1);
|
||||
|
||||
QMetaObject::invokeMethod(
|
||||
pair.first, pair.second.toUtf8().constData(), Qt::DirectConnection,
|
||||
QGenericReturnArgument(),
|
||||
Q_ARG(QString, QString::fromUtf8(argString0.c_str())),
|
||||
Q_ARG(QString, QString::fromUtf8(argString1.c_str())));
|
||||
} else if (args.Length() == 3) {
|
||||
v8::Local<v8::Value> arg0 = args[0];
|
||||
long argLong = JS2Number(arg0);
|
||||
|
||||
v8::Local<v8::Value> arg1 = args[1];
|
||||
int argInt = JS2Number(arg1);
|
||||
|
||||
v8::Local<v8::Value> arg2 = args[2];
|
||||
bool argBool = JS2Bool(arg2);
|
||||
|
||||
QMetaObject::invokeMethod(pair.first, pair.second.toUtf8().constData(),
|
||||
Qt::DirectConnection, QGenericReturnArgument(),
|
||||
Q_ARG(long, argLong), Q_ARG(int, argInt),
|
||||
Q_ARG(bool, argBool));
|
||||
} else if (args.Length() == 5) {
|
||||
v8::Local<v8::Value> arg0 = args[0];
|
||||
std::string argString0 = JS2String(arg0);
|
||||
|
||||
v8::Local<v8::Value> arg1 = args[1];
|
||||
std::string argString1 = JS2String(arg1);
|
||||
|
||||
v8::Local<v8::Value> arg2 = args[2];
|
||||
std::string argString2 = JS2String(arg2);
|
||||
|
||||
v8::Local<v8::Value> arg3 = args[3];
|
||||
std::string argString3 = JS2String(arg3);
|
||||
|
||||
v8::Local<v8::Value> arg4 = args[4];
|
||||
std::string argString4 = JS2String(arg4);
|
||||
|
||||
QMetaObject::invokeMethod(
|
||||
pair.first, pair.second.toUtf8().constData(), Qt::DirectConnection,
|
||||
QGenericReturnArgument(),
|
||||
Q_ARG(QString, QString::fromUtf8(argString0.c_str())),
|
||||
Q_ARG(QString, QString::fromUtf8(argString1.c_str())),
|
||||
Q_ARG(QString, QString::fromUtf8(argString2.c_str())),
|
||||
Q_ARG(QString, QString::fromUtf8(argString3.c_str())),
|
||||
Q_ARG(QString, QString::fromUtf8(argString4.c_str())));
|
||||
}
|
||||
|
||||
// begin check to perform micro task checkpoint
|
||||
|
||||
std::string objectNameString = "global";
|
||||
std::string functionKeyString = "nativeEmpty";
|
||||
|
||||
int objectCompareResult = strncmp(objectKey.c_str(), objectNameString.c_str(),
|
||||
strlen(objectKey.c_str()));
|
||||
|
||||
if (objectCompareResult == 0) {
|
||||
int functionCompareResult =
|
||||
strncmp(functionKey.c_str(), functionKeyString.c_str(),
|
||||
strlen(functionKey.c_str()));
|
||||
if (functionCompareResult == 0) {
|
||||
isolate->PerformMicrotaskCheckpoint();
|
||||
}
|
||||
}
|
||||
// end check
|
||||
}
|
||||
|
||||
void V8Executor::injectFunctions(const char *objectName,
|
||||
const char *functionName, bool hashKey) {
|
||||
v8::Isolate *isolate = m_isolate;
|
||||
|
||||
v8::HandleScope handle_scope(isolate);
|
||||
v8::Local<v8::Context> context = isolate->GetEnteredOrMicrotaskContext();
|
||||
v8::Local<v8::Object> object = context->Global();
|
||||
if (objectName) {
|
||||
object = object->Get(context, NewV8String(objectName))
|
||||
.ToLocalChecked()
|
||||
->ToObject(context)
|
||||
.ToLocalChecked();
|
||||
}
|
||||
|
||||
v8::Local<v8::String> name = NewV8String(functionName);
|
||||
v8::Local<v8::Object> data = v8::Object::New(isolate);
|
||||
if (objectName) {
|
||||
v8::Maybe<bool> result =
|
||||
data->Set(context, NewV8String("obj"), NewV8String(objectName));
|
||||
result.ToChecked();
|
||||
} else {
|
||||
v8::Maybe<bool> result =
|
||||
data->Set(context, NewV8String("obj"), NewV8String("global"));
|
||||
result.ToChecked();
|
||||
}
|
||||
{
|
||||
v8::Maybe<bool> result =
|
||||
data->Set(context, NewV8String("func"), NewV8String(functionName));
|
||||
result.ToChecked();
|
||||
}
|
||||
{
|
||||
v8::Maybe<bool> result = data->Set(context, NewV8String("hashKey"),
|
||||
v8::Boolean::New(isolate, hashKey));
|
||||
result.ToChecked();
|
||||
}
|
||||
|
||||
v8::Local<v8::Function> function =
|
||||
v8::Function::New(context, InjectedFunction, data).ToLocalChecked();
|
||||
v8::Maybe<bool> result = object->Set(context, name, function);
|
||||
result.ToChecked();
|
||||
}
|
||||
|
||||
v8::Local<v8::Value> V8Executor::invokeMethod(const char *objectName,
|
||||
const char *functionName,
|
||||
int argc,
|
||||
v8::Local<v8::Value> argv[],
|
||||
std::string *exception_str) {
|
||||
v8::Isolate *isolate = m_isolate;
|
||||
v8::EscapableHandleScope handle_scope(isolate);
|
||||
v8::Local<v8::Context> context = isolate->GetEnteredOrMicrotaskContext();
|
||||
v8::Local<v8::Object> object = context->Global();
|
||||
v8::Local<v8::Value> result = Undefined(isolate);
|
||||
|
||||
if (objectName) {
|
||||
object = object->Get(context, NewV8String(objectName))
|
||||
.ToLocalChecked()
|
||||
->ToObject(context)
|
||||
.ToLocalChecked();
|
||||
}
|
||||
if (object.IsEmpty()) {
|
||||
*exception_str = std::string("Cannot find Object:") +
|
||||
std::string(objectName ? objectName : "global");
|
||||
return handle_scope.Escape(result);
|
||||
}
|
||||
v8::Local<v8::Value> target_value =
|
||||
object->Get(context, NewV8String(functionName)).ToLocalChecked();
|
||||
if (!target_value->IsFunction()) {
|
||||
*exception_str =
|
||||
std::string("In ") + std::string(objectName ? objectName : "global") +
|
||||
std::string("cannot find target function ") + std::string(functionName);
|
||||
return handle_scope.Escape(result);
|
||||
}
|
||||
v8::TryCatch try_catch(isolate);
|
||||
try_catch.SetVerbose(true);
|
||||
v8::Local<v8::Function> target_function =
|
||||
v8::Local<v8::Function>::Cast(target_value);
|
||||
result = target_function->Call(context, object, argc, argv).ToLocalChecked();
|
||||
v8::Local<v8::Value> exception = try_catch.Exception();
|
||||
if (!exception.IsEmpty()) {
|
||||
if (exception->IsObject()) {
|
||||
v8::Local<v8::Object> exc = v8::Local<v8::Object>::Cast(exception);
|
||||
v8::Local<v8::Value> stack =
|
||||
exc->Get(context, NewV8String("stack")).FromMaybe(exception);
|
||||
*exception_str = JS2String(stack);
|
||||
} else {
|
||||
*exception_str = JS2String(exception);
|
||||
}
|
||||
}
|
||||
return handle_scope.Escape(result);
|
||||
}
|
52
doric-Qt/example/doric/engine/v8/V8Executor.h
Normal file
52
doric-Qt/example/doric/engine/v8/V8Executor.h
Normal file
@@ -0,0 +1,52 @@
|
||||
#ifndef V8EXECUTOR_H
|
||||
#define V8EXECUTOR_H
|
||||
|
||||
#include "libplatform/libplatform.h"
|
||||
#include "v8.h"
|
||||
|
||||
#include <QMap>
|
||||
#include <QObject>
|
||||
#include <QString>
|
||||
|
||||
static QMap<QString, QPair<QObject *, QString>> *map =
|
||||
new QMap<QString, QPair<QObject *, QString>>();
|
||||
|
||||
class V8Executor {
|
||||
|
||||
private:
|
||||
std::unique_ptr<v8::Platform> platform;
|
||||
v8::Isolate::CreateParams create_params;
|
||||
v8::Isolate *m_isolate;
|
||||
v8::Isolate::Scope *m_isolate_scope;
|
||||
v8::Global<v8::Context> *m_global_context;
|
||||
|
||||
void injectObject(const char *string, v8::Local<v8::Value> local);
|
||||
|
||||
v8::Local<v8::Value> innerExec(const char *script, const char *source,
|
||||
std::string *exception_str);
|
||||
|
||||
void injectFunctions(const char *objectName, const char *functionName,
|
||||
bool hashKey);
|
||||
|
||||
v8::Local<v8::Value> invokeMethod(const char *objectName,
|
||||
const char *functionName, int argc,
|
||||
v8::Local<v8::Value> argv[],
|
||||
std::string *exception_str);
|
||||
|
||||
public:
|
||||
V8Executor();
|
||||
|
||||
~V8Executor();
|
||||
|
||||
QString loadJS(QString script, QString source);
|
||||
|
||||
void injectGlobalJSObject(QString name, std::string target);
|
||||
|
||||
void injectGlobalJSFunction(QString name, QObject *function,
|
||||
QString property);
|
||||
|
||||
void invokeObject(QString objectName, QString functionName,
|
||||
QVariantList arguments);
|
||||
};
|
||||
|
||||
#endif // V8EXECUTOR_H
|
Reference in New Issue
Block a user