aboutsummaryrefslogtreecommitdiffstats
path: root/src/qmlworkerscript
diff options
context:
space:
mode:
authorUlf Hermann <ulf.hermann@qt.io>2019-04-04 15:41:18 +0200
committerUlf Hermann <ulf.hermann@qt.io>2019-05-06 07:15:20 +0000
commit5032ff5c2b48d53e4580bb7d50b1f6de081263b0 (patch)
tree05acb8460793e1aed072e971d9b83deb4ea1c8aa /src/qmlworkerscript
parentfee256e6414d634dcc17ed8cfbec34780878a194 (diff)
Move workerscript to its own module
Change-Id: I778cfe842ddf1c600a837d8f2061a338887eed95 Reviewed-by: Lars Knoll <lars.knoll@qt.io>
Diffstat (limited to 'src/qmlworkerscript')
-rw-r--r--src/qmlworkerscript/qmlworkerscript.pro20
-rw-r--r--src/qmlworkerscript/qqmlworkerscriptmodule.cpp62
-rw-r--r--src/qmlworkerscript/qqmlworkerscriptmodule_p.h69
-rw-r--r--src/qmlworkerscript/qquickworkerscript.cpp673
-rw-r--r--src/qmlworkerscript/qquickworkerscript_p.h123
-rw-r--r--src/qmlworkerscript/qtqmlworkerscriptglobal.h58
-rw-r--r--src/qmlworkerscript/qtqmlworkerscriptglobal_p.h60
-rw-r--r--src/qmlworkerscript/qv4serialize.cpp447
-rw-r--r--src/qmlworkerscript/qv4serialize_p.h76
9 files changed, 1588 insertions, 0 deletions
diff --git a/src/qmlworkerscript/qmlworkerscript.pro b/src/qmlworkerscript/qmlworkerscript.pro
new file mode 100644
index 0000000000..908caa4ed4
--- /dev/null
+++ b/src/qmlworkerscript/qmlworkerscript.pro
@@ -0,0 +1,20 @@
+TARGET = QtQmlWorkerScript
+QT = core-private qml-private
+
+DEFINES += QT_NO_URL_CAST_FROM_STRING QT_NO_INTEGER_EVENT_COORDINATES QT_NO_FOREACH
+
+HEADERS += \
+ qqmlworkerscriptmodule_p.h \
+ qquickworkerscript_p.h \
+ qtqmlworkerscriptglobal.h \
+ qtqmlworkerscriptglobal_p.h \
+ qv4serialize_p.h
+
+SOURCES += \
+ qqmlworkerscriptmodule.cpp \
+ qquickworkerscript.cpp \
+ qv4serialize.cpp
+
+include(../3rdparty/masm/masm-defs.pri)
+
+load(qt_module)
diff --git a/src/qmlworkerscript/qqmlworkerscriptmodule.cpp b/src/qmlworkerscript/qqmlworkerscriptmodule.cpp
new file mode 100644
index 0000000000..98e82dbeba
--- /dev/null
+++ b/src/qmlworkerscript/qqmlworkerscriptmodule.cpp
@@ -0,0 +1,62 @@
+/****************************************************************************
+**
+** Copyright (C) 2019 The Qt Company Ltd.
+** Contact: https://www.qt.io/licensing/
+**
+** This file is part of the QtQml module of the Qt Toolkit.
+**
+** $QT_BEGIN_LICENSE:LGPL$
+** Commercial License Usage
+** Licensees holding valid commercial Qt licenses may use this file in
+** accordance with the commercial license agreement provided with the
+** Software or, alternatively, in accordance with the terms contained in
+** a written agreement between you and The Qt Company. For licensing terms
+** and conditions see https://www.qt.io/terms-conditions. For further
+** information use the contact form at https://www.qt.io/contact-us.
+**
+** GNU Lesser General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU Lesser
+** General Public License version 3 as published by the Free Software
+** Foundation and appearing in the file LICENSE.LGPL3 included in the
+** packaging of this file. Please review the following information to
+** ensure the GNU Lesser General Public License version 3 requirements
+** will be met: https://www.gnu.org/licenses/lgpl-3.0.html.
+**
+** GNU General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU
+** General Public License version 2.0 or (at your option) the GNU General
+** Public license version 3 or any later version approved by the KDE Free
+** Qt Foundation. The licenses are as published by the Free Software
+** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3
+** included in the packaging of this file. Please review the following
+** information to ensure the GNU General Public License requirements will
+** be met: https://www.gnu.org/licenses/gpl-2.0.html and
+** https://www.gnu.org/licenses/gpl-3.0.html.
+**
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+#include "qqmlworkerscriptmodule_p.h"
+#include "qquickworkerscript_p.h"
+
+QT_BEGIN_NAMESPACE
+
+#if QT_VERSION < QT_VERSION_CHECK(6, 0, 0)
+
+void QQmlWorkerScriptModule::registerQuickTypes()
+{
+ // Don't add anything here. These are only for backwards compatibility.
+ const char uri[] = "QtQuick";
+ qmlRegisterType<QQuickWorkerScript>(uri, 2, 0, "WorkerScript");
+}
+
+#endif // QT_VERSION < QT_VERSION_CHECK(6, 0, 0)
+
+void QQmlWorkerScriptModule::defineModule()
+{
+ const char uri[] = "QtQml.WorkerScript";
+ qmlRegisterType<QQuickWorkerScript>(uri, 2, 0, "WorkerScript");
+}
+
+QT_END_NAMESPACE
diff --git a/src/qmlworkerscript/qqmlworkerscriptmodule_p.h b/src/qmlworkerscript/qqmlworkerscriptmodule_p.h
new file mode 100644
index 0000000000..a2efb304c1
--- /dev/null
+++ b/src/qmlworkerscript/qqmlworkerscriptmodule_p.h
@@ -0,0 +1,69 @@
+/****************************************************************************
+**
+** Copyright (C) 2019 The Qt Company Ltd.
+** Contact: https://www.qt.io/licensing/
+**
+** This file is part of the QtQml module of the Qt Toolkit.
+**
+** $QT_BEGIN_LICENSE:LGPL$
+** Commercial License Usage
+** Licensees holding valid commercial Qt licenses may use this file in
+** accordance with the commercial license agreement provided with the
+** Software or, alternatively, in accordance with the terms contained in
+** a written agreement between you and The Qt Company. For licensing terms
+** and conditions see https://www.qt.io/terms-conditions. For further
+** information use the contact form at https://www.qt.io/contact-us.
+**
+** GNU Lesser General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU Lesser
+** General Public License version 3 as published by the Free Software
+** Foundation and appearing in the file LICENSE.LGPL3 included in the
+** packaging of this file. Please review the following information to
+** ensure the GNU Lesser General Public License version 3 requirements
+** will be met: https://www.gnu.org/licenses/lgpl-3.0.html.
+**
+** GNU General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU
+** General Public License version 2.0 or (at your option) the GNU General
+** Public license version 3 or any later version approved by the KDE Free
+** Qt Foundation. The licenses are as published by the Free Software
+** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3
+** included in the packaging of this file. Please review the following
+** information to ensure the GNU General Public License requirements will
+** be met: https://www.gnu.org/licenses/gpl-2.0.html and
+** https://www.gnu.org/licenses/gpl-3.0.html.
+**
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+#ifndef QQMLWORKERSCRIPTMODULE_P_H
+#define QQMLWORKERSCRIPTMODULE_P_H
+
+//
+// W A R N I N G
+// -------------
+//
+// This file is not part of the Qt API. It exists purely as an
+// implementation detail. This header file may change from version to
+// version without notice, or even be removed.
+//
+// We mean it.
+//
+
+#include <private/qtqmlworkerscriptglobal_p.h>
+
+QT_BEGIN_NAMESPACE
+
+class Q_QMLWORKERSCRIPT_PRIVATE_EXPORT QQmlWorkerScriptModule
+{
+public:
+#if QT_VERSION < QT_VERSION_CHECK(6, 0, 0)
+ static void registerQuickTypes();
+#endif
+ static void defineModule();
+};
+
+QT_END_NAMESPACE
+
+#endif // QQMLWORKERSCRIPTMODULE_P_H
diff --git a/src/qmlworkerscript/qquickworkerscript.cpp b/src/qmlworkerscript/qquickworkerscript.cpp
new file mode 100644
index 0000000000..273ec2a7a6
--- /dev/null
+++ b/src/qmlworkerscript/qquickworkerscript.cpp
@@ -0,0 +1,673 @@
+/****************************************************************************
+**
+** Copyright (C) 2016 The Qt Company Ltd.
+** Contact: https://www.qt.io/licensing/
+**
+** This file is part of the QtQml module of the Qt Toolkit.
+**
+** $QT_BEGIN_LICENSE:LGPL$
+** Commercial License Usage
+** Licensees holding valid commercial Qt licenses may use this file in
+** accordance with the commercial license agreement provided with the
+** Software or, alternatively, in accordance with the terms contained in
+** a written agreement between you and The Qt Company. For licensing terms
+** and conditions see https://www.qt.io/terms-conditions. For further
+** information use the contact form at https://www.qt.io/contact-us.
+**
+** GNU Lesser General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU Lesser
+** General Public License version 3 as published by the Free Software
+** Foundation and appearing in the file LICENSE.LGPL3 included in the
+** packaging of this file. Please review the following information to
+** ensure the GNU Lesser General Public License version 3 requirements
+** will be met: https://www.gnu.org/licenses/lgpl-3.0.html.
+**
+** GNU General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU
+** General Public License version 2.0 or (at your option) the GNU General
+** Public license version 3 or any later version approved by the KDE Free
+** Qt Foundation. The licenses are as published by the Free Software
+** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3
+** included in the packaging of this file. Please review the following
+** information to ensure the GNU General Public License requirements will
+** be met: https://www.gnu.org/licenses/gpl-2.0.html and
+** https://www.gnu.org/licenses/gpl-3.0.html.
+**
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+#include "qtqmlworkerscriptglobal_p.h"
+#include "qquickworkerscript_p.h"
+#include <private/qqmlengine_p.h>
+#include <private/qqmlexpression_p.h>
+
+#include <QtCore/qcoreevent.h>
+#include <QtCore/qcoreapplication.h>
+#include <QtCore/qdebug.h>
+#include <QtQml/qjsengine.h>
+#include <QtCore/qmutex.h>
+#include <QtCore/qwaitcondition.h>
+#include <QtCore/qfile.h>
+#include <QtCore/qdatetime.h>
+#include <QtQml/qqmlinfo.h>
+#include <QtQml/qqmlfile.h>
+#if QT_CONFIG(qml_network)
+#include <QtNetwork/qnetworkaccessmanager.h>
+#include "qqmlnetworkaccessmanagerfactory.h"
+#endif
+
+#include <private/qv8engine_p.h>
+#include <private/qv4serialize_p.h>
+
+#include <private/qv4value_p.h>
+#include <private/qv4functionobject_p.h>
+#include <private/qv4script_p.h>
+#include <private/qv4scopedvalue_p.h>
+#include <private/qv4jscall_p.h>
+
+QT_BEGIN_NAMESPACE
+
+class WorkerDataEvent : public QEvent
+{
+public:
+ enum Type { WorkerData = QEvent::User };
+
+ WorkerDataEvent(int workerId, const QByteArray &data);
+ virtual ~WorkerDataEvent();
+
+ int workerId() const;
+ QByteArray data() const;
+
+private:
+ int m_id;
+ QByteArray m_data;
+};
+
+class WorkerLoadEvent : public QEvent
+{
+public:
+ enum Type { WorkerLoad = WorkerDataEvent::WorkerData + 1 };
+
+ WorkerLoadEvent(int workerId, const QUrl &url);
+
+ int workerId() const;
+ QUrl url() const;
+
+private:
+ int m_id;
+ QUrl m_url;
+};
+
+class WorkerRemoveEvent : public QEvent
+{
+public:
+ enum Type { WorkerRemove = WorkerLoadEvent::WorkerLoad + 1 };
+
+ WorkerRemoveEvent(int workerId);
+
+ int workerId() const;
+
+private:
+ int m_id;
+};
+
+class WorkerErrorEvent : public QEvent
+{
+public:
+ enum Type { WorkerError = WorkerRemoveEvent::WorkerRemove + 1 };
+
+ WorkerErrorEvent(const QQmlError &error);
+
+ QQmlError error() const;
+
+private:
+ QQmlError m_error;
+};
+
+class QQuickWorkerScriptEnginePrivate : public QObject
+{
+ Q_OBJECT
+public:
+ enum WorkerEventTypes {
+ WorkerDestroyEvent = QEvent::User + 100
+ };
+
+ QQuickWorkerScriptEnginePrivate(QQmlEngine *eng);
+
+ QQmlEngine *qmlengine;
+
+ QMutex m_lock;
+ QWaitCondition m_wait;
+
+ struct WorkerScript : public QV8Engine {
+ WorkerScript(int id, QQuickWorkerScriptEnginePrivate *parent);
+ ~WorkerScript() override;
+
+#if QT_CONFIG(qml_network)
+ QNetworkAccessManager *networkAccessManager() override;
+#endif
+
+ QQuickWorkerScriptEnginePrivate *p = nullptr;
+ QUrl source;
+ QQuickWorkerScript *owner = nullptr;
+#if QT_CONFIG(qml_network)
+ QScopedPointer<QNetworkAccessManager> accessManager;
+#endif
+ int id = -1;
+ };
+
+ QHash<int, WorkerScript *> workers;
+ QV4::ReturnedValue getWorker(WorkerScript *);
+
+ int m_nextId;
+
+ static QV4::ReturnedValue method_sendMessage(const QV4::FunctionObject *, const QV4::Value *thisObject, const QV4::Value *argv, int argc);
+
+signals:
+ void stopThread();
+
+protected:
+ bool event(QEvent *) override;
+
+private:
+ void processMessage(int, const QByteArray &);
+ void processLoad(int, const QUrl &);
+ void reportScriptException(WorkerScript *, const QQmlError &error);
+};
+
+QQuickWorkerScriptEnginePrivate::QQuickWorkerScriptEnginePrivate(QQmlEngine *engine)
+: qmlengine(engine), m_nextId(0)
+{
+}
+
+QV4::ReturnedValue QQuickWorkerScriptEnginePrivate::method_sendMessage(const QV4::FunctionObject *b,
+ const QV4::Value *, const QV4::Value *argv, int argc)
+{
+ QV4::Scope scope(b);
+ WorkerScript *script = static_cast<WorkerScript *>(scope.engine->v8Engine);
+
+ QV4::ScopedValue v(scope, argc > 0 ? argv[0] : QV4::Value::undefinedValue());
+ QByteArray data = QV4::Serialize::serialize(v, scope.engine);
+
+ QMutexLocker locker(&script->p->m_lock);
+ if (script && script->owner)
+ QCoreApplication::postEvent(script->owner, new WorkerDataEvent(0, data));
+
+ return QV4::Encode::undefined();
+}
+
+bool QQuickWorkerScriptEnginePrivate::event(QEvent *event)
+{
+ if (event->type() == (QEvent::Type)WorkerDataEvent::WorkerData) {
+ WorkerDataEvent *workerEvent = static_cast<WorkerDataEvent *>(event);
+ processMessage(workerEvent->workerId(), workerEvent->data());
+ return true;
+ } else if (event->type() == (QEvent::Type)WorkerLoadEvent::WorkerLoad) {
+ WorkerLoadEvent *workerEvent = static_cast<WorkerLoadEvent *>(event);
+ processLoad(workerEvent->workerId(), workerEvent->url());
+ return true;
+ } else if (event->type() == (QEvent::Type)WorkerDestroyEvent) {
+ emit stopThread();
+ return true;
+ } else if (event->type() == (QEvent::Type)WorkerRemoveEvent::WorkerRemove) {
+ QMutexLocker locker(&m_lock);
+ WorkerRemoveEvent *workerEvent = static_cast<WorkerRemoveEvent *>(event);
+ QHash<int, WorkerScript *>::iterator itr = workers.find(workerEvent->workerId());
+ if (itr != workers.end()) {
+ delete itr.value();
+ workers.erase(itr);
+ }
+ return true;
+ } else {
+ return QObject::event(event);
+ }
+}
+
+void QQuickWorkerScriptEnginePrivate::processMessage(int id, const QByteArray &data)
+{
+ WorkerScript *script = workers.value(id);
+ if (!script)
+ return;
+
+ QV4::ExecutionEngine *v4 = QV8Engine::getV4(script);
+ QV4::Scope scope(v4);
+ QV4::ScopedString v(scope);
+ QV4::ScopedObject worker(scope, v4->globalObject->get((v = v4->newString(QStringLiteral("WorkerScript")))));
+ QV4::ScopedFunctionObject onmessage(scope);
+ if (worker)
+ onmessage = worker->get((v = v4->newString(QStringLiteral("onMessage"))));
+
+ if (!onmessage)
+ return;
+
+ QV4::ScopedValue value(scope, QV4::Serialize::deserialize(data, v4));
+
+ QV4::JSCallData jsCallData(scope, 1);
+ *jsCallData->thisObject = v4->global();
+ jsCallData->args[0] = value;
+ onmessage->call(jsCallData);
+ if (scope.hasException()) {
+ QQmlError error = scope.engine->catchExceptionAsQmlError();
+ reportScriptException(script, error);
+ }
+}
+
+void QQuickWorkerScriptEnginePrivate::processLoad(int id, const QUrl &url)
+{
+ if (url.isRelative())
+ return;
+
+ QString fileName = QQmlFile::urlToLocalFileOrQrc(url);
+
+ WorkerScript *script = workers.value(id);
+ if (!script)
+ return;
+
+ QV4::ExecutionEngine *v4 = QV8Engine::getV4(script);
+
+ script->source = url;
+
+ if (fileName.endsWith(QLatin1String(".mjs"))) {
+ auto moduleUnit = v4->loadModule(url);
+ if (moduleUnit) {
+ if (moduleUnit->instantiate(v4))
+ moduleUnit->evaluate();
+ } else {
+ v4->throwError(QStringLiteral("Could not load module file"));
+ }
+ } else {
+ QString error;
+ QV4::Scope scope(v4);
+ QScopedPointer<QV4::Script> program;
+ program.reset(QV4::Script::createFromFileOrCache(v4, /*qmlContext*/nullptr, fileName, url, &error));
+ if (program.isNull()) {
+ if (!error.isEmpty())
+ qWarning().nospace() << error;
+ return;
+ }
+
+ if (!v4->hasException)
+ program->run();
+ }
+
+ if (v4->hasException) {
+ QQmlError error = v4->catchExceptionAsQmlError();
+ reportScriptException(script, error);
+ }
+}
+
+void QQuickWorkerScriptEnginePrivate::reportScriptException(WorkerScript *script,
+ const QQmlError &error)
+{
+ QMutexLocker locker(&script->p->m_lock);
+ if (script->owner)
+ QCoreApplication::postEvent(script->owner, new WorkerErrorEvent(error));
+}
+
+WorkerDataEvent::WorkerDataEvent(int workerId, const QByteArray &data)
+: QEvent((QEvent::Type)WorkerData), m_id(workerId), m_data(data)
+{
+}
+
+WorkerDataEvent::~WorkerDataEvent()
+{
+}
+
+int WorkerDataEvent::workerId() const
+{
+ return m_id;
+}
+
+QByteArray WorkerDataEvent::data() const
+{
+ return m_data;
+}
+
+WorkerLoadEvent::WorkerLoadEvent(int workerId, const QUrl &url)
+: QEvent((QEvent::Type)WorkerLoad), m_id(workerId), m_url(url)
+{
+}
+
+int WorkerLoadEvent::workerId() const
+{
+ return m_id;
+}
+
+QUrl WorkerLoadEvent::url() const
+{
+ return m_url;
+}
+
+WorkerRemoveEvent::WorkerRemoveEvent(int workerId)
+: QEvent((QEvent::Type)WorkerRemove), m_id(workerId)
+{
+}
+
+int WorkerRemoveEvent::workerId() const
+{
+ return m_id;
+}
+
+WorkerErrorEvent::WorkerErrorEvent(const QQmlError &error)
+: QEvent((QEvent::Type)WorkerError), m_error(error)
+{
+}
+
+QQmlError WorkerErrorEvent::error() const
+{
+ return m_error;
+}
+
+QQuickWorkerScriptEngine::QQuickWorkerScriptEngine(QQmlEngine *parent)
+: QThread(parent), d(new QQuickWorkerScriptEnginePrivate(parent))
+{
+ d->m_lock.lock();
+ connect(d, SIGNAL(stopThread()), this, SLOT(quit()), Qt::DirectConnection);
+ start(QThread::LowestPriority);
+ d->m_wait.wait(&d->m_lock);
+ d->moveToThread(this);
+ d->m_lock.unlock();
+}
+
+QQuickWorkerScriptEngine::~QQuickWorkerScriptEngine()
+{
+ d->m_lock.lock();
+ QCoreApplication::postEvent(d, new QEvent((QEvent::Type)QQuickWorkerScriptEnginePrivate::WorkerDestroyEvent));
+ d->m_lock.unlock();
+
+ //We have to force to cleanup the main thread's event queue here
+ //to make sure the main GUI release all pending locks/wait conditions which
+ //some worker script/agent are waiting for (QQmlListModelWorkerAgent::sync() for example).
+ while (!isFinished()) {
+ // We can't simply wait here, because the worker thread will not terminate
+ // until the main thread processes the last data event it generates
+ QCoreApplication::processEvents();
+ yieldCurrentThread();
+ }
+
+ d->deleteLater();
+}
+
+QQuickWorkerScriptEnginePrivate::WorkerScript::WorkerScript(int id, QQuickWorkerScriptEnginePrivate *parent)
+ : QV8Engine(new QV4::ExecutionEngine)
+ , p(parent)
+ , id(id)
+{
+ m_v4Engine->v8Engine = this;
+
+ initQmlGlobalObject();
+
+ QV4::Scope scope(m_v4Engine);
+ QV4::ScopedObject api(scope, scope.engine->newObject());
+ QV4::ScopedString name(scope, m_v4Engine->newString(QStringLiteral("sendMessage")));
+ QV4::ScopedValue sendMessage(scope, QV4::FunctionObject::createBuiltinFunction(m_v4Engine, name, method_sendMessage, 1));
+ api->put(QV4::ScopedString(scope, scope.engine->newString(QStringLiteral("sendMessage"))), sendMessage);
+ m_v4Engine->globalObject->put(QV4::ScopedString(scope, scope.engine->newString(QStringLiteral("WorkerScript"))), api);
+}
+
+QQuickWorkerScriptEnginePrivate::WorkerScript::~WorkerScript()
+{
+ delete m_v4Engine;
+}
+
+#if QT_CONFIG(qml_network)
+QNetworkAccessManager *QQuickWorkerScriptEnginePrivate::WorkerScript::networkAccessManager()
+{
+ if (!accessManager) {
+ if (p->qmlengine && p->qmlengine->networkAccessManagerFactory()) {
+ accessManager.reset(p->qmlengine->networkAccessManagerFactory()->create(p));
+ } else {
+ accessManager.reset(new QNetworkAccessManager(p));
+ }
+ }
+ return accessManager.data();
+}
+#endif
+
+int QQuickWorkerScriptEngine::registerWorkerScript(QQuickWorkerScript *owner)
+{
+ typedef QQuickWorkerScriptEnginePrivate::WorkerScript WorkerScript;
+ WorkerScript *script = new WorkerScript(d->m_nextId++, d);
+
+ script->owner = owner;
+
+ d->m_lock.lock();
+ d->workers.insert(script->id, script);
+ d->m_lock.unlock();
+
+ return script->id;
+}
+
+void QQuickWorkerScriptEngine::removeWorkerScript(int id)
+{
+ QQuickWorkerScriptEnginePrivate::WorkerScript* script = d->workers.value(id);
+ if (script) {
+ script->owner = nullptr;
+ QCoreApplication::postEvent(d, new WorkerRemoveEvent(id));
+ }
+}
+
+void QQuickWorkerScriptEngine::executeUrl(int id, const QUrl &url)
+{
+ QCoreApplication::postEvent(d, new WorkerLoadEvent(id, url));
+}
+
+void QQuickWorkerScriptEngine::sendMessage(int id, const QByteArray &data)
+{
+ QCoreApplication::postEvent(d, new WorkerDataEvent(id, data));
+}
+
+void QQuickWorkerScriptEngine::run()
+{
+ d->m_lock.lock();
+
+ d->m_wait.wakeAll();
+
+ d->m_lock.unlock();
+
+ exec();
+
+ qDeleteAll(d->workers);
+ d->workers.clear();
+}
+
+
+/*!
+ \qmltype WorkerScript
+ \instantiates QQuickWorkerScript
+ \ingroup qtquick-threading
+ \inqmlmodule QtQml
+ \brief Enables the use of threads in a Qt Quick application.
+
+ Use WorkerScript to run operations in a new thread.
+ This is useful for running operations in the background so
+ that the main GUI thread is not blocked.
+
+ Messages can be passed between the new thread and the parent thread
+ using \l sendMessage() and the \c onMessage() handler.
+
+ An example:
+
+ \snippet qml/workerscript/workerscript.qml 0
+
+ The above worker script specifies a JavaScript file, "script.mjs", that handles
+ the operations to be performed in the new thread. Here is \c script.mjs:
+
+ \quotefile qml/workerscript/script.mjs
+
+ When the user clicks anywhere within the rectangle, \c sendMessage() is
+ called, triggering the \tt WorkerScript.onMessage() handler in
+ \tt script.mjs. This in turn sends a reply message that is then received
+ by the \tt onMessage() handler of \tt myWorker.
+
+ The example uses a script that is an ECMAScript module, because it has the ".mjs" extension.
+ It can use import statements to access functionality from other modules and it is run in JavaScript
+ strict mode.
+
+ If a worker script has the extension ".js" instead, then it is considered to contain plain JavaScript
+ statements and it is run in non-strict mode.
+
+ \note Each WorkerScript element will instantiate a separate JavaScript engine to ensure perfect
+ isolation and thread-safety. If the impact of that results in a memory consumption that is too
+ high for your environment, then consider sharing a WorkerScript element.
+
+ \section3 Restrictions
+
+ Since the \c WorkerScript.onMessage() function is run in a separate thread, the
+ JavaScript file is evaluated in a context separate from the main QML engine. This means
+ that unlike an ordinary JavaScript file that is imported into QML, the \c script.mjs
+ in the above example cannot access the properties, methods or other attributes
+ of the QML item, nor can it access any context properties set on the QML object
+ through QQmlContext.
+
+ Additionally, there are restrictions on the types of values that can be passed to and
+ from the worker script. See the sendMessage() documentation for details.
+
+ Worker scripts that are plain JavaScript sources can not use \l {qtqml-javascript-imports.html}{.import} syntax.
+ Scripts that are ECMAScript modules can freely use import and export statements.
+
+ \sa {Qt Quick Examples - Threading},
+ {Threaded ListModel Example}
+*/
+QQuickWorkerScript::QQuickWorkerScript(QObject *parent)
+: QObject(parent), m_engine(nullptr), m_scriptId(-1), m_componentComplete(true)
+{
+}
+
+QQuickWorkerScript::~QQuickWorkerScript()
+{
+ if (m_scriptId != -1) m_engine->removeWorkerScript(m_scriptId);
+}
+
+/*!
+ \qmlproperty url WorkerScript::source
+
+ This holds the url of the JavaScript file that implements the
+ \tt WorkerScript.onMessage() handler for threaded operations.
+
+ If the file name component of the url ends with ".mjs", then the script
+ is parsed as an ECMAScript module and run in strict mode. Otherwise it is considered to be
+ plain script.
+*/
+QUrl QQuickWorkerScript::source() const
+{
+ return m_source;
+}
+
+void QQuickWorkerScript::setSource(const QUrl &source)
+{
+ if (m_source == source)
+ return;
+
+ m_source = source;
+
+ if (engine())
+ m_engine->executeUrl(m_scriptId, m_source);
+
+ emit sourceChanged();
+}
+
+/*!
+ \qmlmethod WorkerScript::sendMessage(jsobject message)
+
+ Sends the given \a message to a worker script handler in another
+ thread. The other worker script handler can receive this message
+ through the onMessage() handler.
+
+ The \c message object may only contain values of the following
+ types:
+
+ \list
+ \li boolean, number, string
+ \li JavaScript objects and arrays
+ \li ListModel objects (any other type of QObject* is not allowed)
+ \endlist
+
+ All objects and arrays are copied to the \c message. With the exception
+ of ListModel objects, any modifications by the other thread to an object
+ passed in \c message will not be reflected in the original object.
+*/
+void QQuickWorkerScript::sendMessage(QQmlV4Function *args)
+{
+ if (!engine()) {
+ qWarning("QQuickWorkerScript: Attempt to send message before WorkerScript establishment");
+ return;
+ }
+
+ QV4::Scope scope(args->v4engine());
+ QV4::ScopedValue argument(scope, QV4::Value::undefinedValue());
+ if (args->length() != 0)
+ argument = (*args)[0];
+
+ m_engine->sendMessage(m_scriptId, QV4::Serialize::serialize(argument, scope.engine));
+}
+
+void QQuickWorkerScript::classBegin()
+{
+ m_componentComplete = false;
+}
+
+QQuickWorkerScriptEngine *QQuickWorkerScript::engine()
+{
+ if (m_engine) return m_engine;
+ if (m_componentComplete) {
+ QQmlEngine *engine = qmlEngine(this);
+ if (!engine) {
+ qWarning("QQuickWorkerScript: engine() called without qmlEngine() set");
+ return nullptr;
+ }
+
+ QQmlEnginePrivate *enginePrivate = QQmlEnginePrivate::get(engine);
+ if (enginePrivate->workerScriptEngine == nullptr)
+ enginePrivate->workerScriptEngine = new QQuickWorkerScriptEngine(engine);
+ m_engine = qobject_cast<QQuickWorkerScriptEngine *>(enginePrivate->workerScriptEngine);
+ Q_ASSERT(m_engine);
+ m_scriptId = m_engine->registerWorkerScript(this);
+
+ if (m_source.isValid())
+ m_engine->executeUrl(m_scriptId, m_source);
+
+ return m_engine;
+ }
+ return nullptr;
+}
+
+void QQuickWorkerScript::componentComplete()
+{
+ m_componentComplete = true;
+ engine(); // Get it started now.
+}
+
+/*!
+ \qmlsignal WorkerScript::message(jsobject msg)
+
+ This signal is emitted when a message \a msg is received from a worker
+ script in another thread through a call to sendMessage().
+
+ The corresponding handler is \c onMessage.
+*/
+
+bool QQuickWorkerScript::event(QEvent *event)
+{
+ if (event->type() == (QEvent::Type)WorkerDataEvent::WorkerData) {
+ if (QQmlEngine *engine = qmlEngine(this)) {
+ QV4::ExecutionEngine *v4 = engine->handle();
+ WorkerDataEvent *workerEvent = static_cast<WorkerDataEvent *>(event);
+ emit message(QJSValue(v4, QV4::Serialize::deserialize(workerEvent->data(), v4)));
+ }
+ return true;
+ } else if (event->type() == (QEvent::Type)WorkerErrorEvent::WorkerError) {
+ WorkerErrorEvent *workerEvent = static_cast<WorkerErrorEvent *>(event);
+ QQmlEnginePrivate::warning(qmlEngine(this), workerEvent->error());
+ return true;
+ } else {
+ return QObject::event(event);
+ }
+}
+
+QT_END_NAMESPACE
+
+#include <qquickworkerscript.moc>
+
+#include "moc_qquickworkerscript_p.cpp"
diff --git a/src/qmlworkerscript/qquickworkerscript_p.h b/src/qmlworkerscript/qquickworkerscript_p.h
new file mode 100644
index 0000000000..87cf2e9754
--- /dev/null
+++ b/src/qmlworkerscript/qquickworkerscript_p.h
@@ -0,0 +1,123 @@
+/****************************************************************************
+**
+** Copyright (C) 2016 The Qt Company Ltd.
+** Contact: https://www.qt.io/licensing/
+**
+** This file is part of the QtQml module of the Qt Toolkit.
+**
+** $QT_BEGIN_LICENSE:LGPL$
+** Commercial License Usage
+** Licensees holding valid commercial Qt licenses may use this file in
+** accordance with the commercial license agreement provided with the
+** Software or, alternatively, in accordance with the terms contained in
+** a written agreement between you and The Qt Company. For licensing terms
+** and conditions see https://www.qt.io/terms-conditions. For further
+** information use the contact form at https://www.qt.io/contact-us.
+**
+** GNU Lesser General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU Lesser
+** General Public License version 3 as published by the Free Software
+** Foundation and appearing in the file LICENSE.LGPL3 included in the
+** packaging of this file. Please review the following information to
+** ensure the GNU Lesser General Public License version 3 requirements
+** will be met: https://www.gnu.org/licenses/lgpl-3.0.html.
+**
+** GNU General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU
+** General Public License version 2.0 or (at your option) the GNU General
+** Public license version 3 or any later version approved by the KDE Free
+** Qt Foundation. The licenses are as published by the Free Software
+** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3
+** included in the packaging of this file. Please review the following
+** information to ensure the GNU General Public License requirements will
+** be met: https://www.gnu.org/licenses/gpl-2.0.html and
+** https://www.gnu.org/licenses/gpl-3.0.html.
+**
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+#ifndef QQUICKWORKERSCRIPT_P_H
+#define QQUICKWORKERSCRIPT_P_H
+
+//
+// W A R N I N G
+// -------------
+//
+// This file is not part of the Qt API. It exists purely as an
+// implementation detail. This header file may change from version to
+// version without notice, or even be removed.
+//
+// We mean it.
+//
+
+#include <qqml.h>
+
+#include <QtQml/qqmlparserstatus.h>
+#include <QtCore/qthread.h>
+#include <QtQml/qjsvalue.h>
+#include <QtCore/qurl.h>
+
+QT_BEGIN_NAMESPACE
+
+
+class QQuickWorkerScript;
+class QQuickWorkerScriptEnginePrivate;
+class QQuickWorkerScriptEngine : public QThread
+{
+Q_OBJECT
+public:
+ QQuickWorkerScriptEngine(QQmlEngine *parent = nullptr);
+ ~QQuickWorkerScriptEngine();
+
+ int registerWorkerScript(QQuickWorkerScript *);
+ void removeWorkerScript(int);
+ void executeUrl(int, const QUrl &);
+ void sendMessage(int, const QByteArray &);
+
+protected:
+ void run() override;
+
+private:
+ QQuickWorkerScriptEnginePrivate *d;
+};
+
+class QQmlV4Function;
+class Q_AUTOTEST_EXPORT QQuickWorkerScript : public QObject, public QQmlParserStatus
+{
+ Q_OBJECT
+ Q_PROPERTY(QUrl source READ source WRITE setSource NOTIFY sourceChanged)
+
+ Q_INTERFACES(QQmlParserStatus)
+public:
+ QQuickWorkerScript(QObject *parent = nullptr);
+ ~QQuickWorkerScript();
+
+ QUrl source() const;
+ void setSource(const QUrl &);
+
+public Q_SLOTS:
+ void sendMessage(QQmlV4Function*);
+
+Q_SIGNALS:
+ void sourceChanged();
+ void message(const QJSValue &messageObject);
+
+protected:
+ void classBegin() override;
+ void componentComplete() override;
+ bool event(QEvent *) override;
+
+private:
+ QQuickWorkerScriptEngine *engine();
+ QQuickWorkerScriptEngine *m_engine;
+ int m_scriptId;
+ QUrl m_source;
+ bool m_componentComplete;
+};
+
+QT_END_NAMESPACE
+
+QML_DECLARE_TYPE(QQuickWorkerScript)
+
+#endif // QQUICKWORKERSCRIPT_P_H
diff --git a/src/qmlworkerscript/qtqmlworkerscriptglobal.h b/src/qmlworkerscript/qtqmlworkerscriptglobal.h
new file mode 100644
index 0000000000..be3ea4e12a
--- /dev/null
+++ b/src/qmlworkerscript/qtqmlworkerscriptglobal.h
@@ -0,0 +1,58 @@
+/****************************************************************************
+**
+** Copyright (C) 2019 The Qt Company Ltd.
+** Contact: https://www.qt.io/licensing/
+**
+** This file is part of the QtQml module of the Qt Toolkit.
+**
+** $QT_BEGIN_LICENSE:LGPL$
+** Commercial License Usage
+** Licensees holding valid commercial Qt licenses may use this file in
+** accordance with the commercial license agreement provided with the
+** Software or, alternatively, in accordance with the terms contained in
+** a written agreement between you and The Qt Company. For licensing terms
+** and conditions see https://www.qt.io/terms-conditions. For further
+** information use the contact form at https://www.qt.io/contact-us.
+**
+** GNU Lesser General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU Lesser
+** General Public License version 3 as published by the Free Software
+** Foundation and appearing in the file LICENSE.LGPL3 included in the
+** packaging of this file. Please review the following information to
+** ensure the GNU Lesser General Public License version 3 requirements
+** will be met: https://www.gnu.org/licenses/lgpl-3.0.html.
+**
+** GNU General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU
+** General Public License version 2.0 or (at your option) the GNU General
+** Public license version 3 or any later version approved by the KDE Free
+** Qt Foundation. The licenses are as published by the Free Software
+** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3
+** included in the packaging of this file. Please review the following
+** information to ensure the GNU General Public License requirements will
+** be met: https://www.gnu.org/licenses/gpl-2.0.html and
+** https://www.gnu.org/licenses/gpl-3.0.html.
+**
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+#ifndef QTQMLWORKERSCRIPTGLOBAL_H
+#define QTQMLWORKERSCRIPTGLOBAL_H
+
+#include <QtQml/qtqmlglobal.h>
+
+QT_BEGIN_NAMESPACE
+
+#if !defined(QT_STATIC)
+# if defined(QT_BUILD_QMLWORKERSCRIPT_LIB)
+# define Q_QMLWORKERSCRIPT_EXPORT Q_DECL_EXPORT
+# else
+# define Q_QMLWORKERSCRIPT_EXPORT Q_DECL_IMPORT
+# endif
+#else
+# define Q_QMLWORKERSCRIPT_EXPORT
+#endif
+
+QT_END_NAMESPACE
+#endif // QTQMLWORKERSCRIPTGLOBAL_H
diff --git a/src/qmlworkerscript/qtqmlworkerscriptglobal_p.h b/src/qmlworkerscript/qtqmlworkerscriptglobal_p.h
new file mode 100644
index 0000000000..34236cd79e
--- /dev/null
+++ b/src/qmlworkerscript/qtqmlworkerscriptglobal_p.h
@@ -0,0 +1,60 @@
+/****************************************************************************
+**
+** Copyright (C) 2019 The Qt Company Ltd.
+** Contact: https://www.qt.io/licensing/
+**
+** This file is part of the QtQml module of the Qt Toolkit.
+**
+** $QT_BEGIN_LICENSE:LGPL$
+** Commercial License Usage
+** Licensees holding valid commercial Qt licenses may use this file in
+** accordance with the commercial license agreement provided with the
+** Software or, alternatively, in accordance with the terms contained in
+** a written agreement between you and The Qt Company. For licensing terms
+** and conditions see https://www.qt.io/terms-conditions. For further
+** information use the contact form at https://www.qt.io/contact-us.
+**
+** GNU Lesser General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU Lesser
+** General Public License version 3 as published by the Free Software
+** Foundation and appearing in the file LICENSE.LGPL3 included in the
+** packaging of this file. Please review the following information to
+** ensure the GNU Lesser General Public License version 3 requirements
+** will be met: https://www.gnu.org/licenses/lgpl-3.0.html.
+**
+** GNU General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU
+** General Public License version 2.0 or (at your option) the GNU General
+** Public license version 3 or any later version approved by the KDE Free
+** Qt Foundation. The licenses are as published by the Free Software
+** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3
+** included in the packaging of this file. Please review the following
+** information to ensure the GNU General Public License requirements will
+** be met: https://www.gnu.org/licenses/gpl-2.0.html and
+** https://www.gnu.org/licenses/gpl-3.0.html.
+**
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+#ifndef QTQMLWORKERSCRIPTGLOBAL_P_H
+#define QTQMLWORKERSCRIPTGLOBAL_P_H
+
+//
+// W A R N I N G
+// -------------
+//
+// This file is not part of the Qt API. It exists purely as an
+// implementation detail. This header file may change from version to
+// version without notice, or even be removed.
+//
+// We mean it.
+//
+
+#include <QtQml/private/qtqmlglobal_p.h>
+#include <QtQmlWorkerScript/qtqmlworkerscriptglobal.h>
+
+#define Q_QMLWORKERSCRIPT_PRIVATE_EXPORT Q_QMLWORKERSCRIPT_EXPORT
+#define Q_QMLWORKERSCRIPT_AUTOTEST_EXPORT Q_AUTOTEST_EXPORT
+
+#endif // QTQMLWORKERSCRIPTGLOBAL_P_H
diff --git a/src/qmlworkerscript/qv4serialize.cpp b/src/qmlworkerscript/qv4serialize.cpp
new file mode 100644
index 0000000000..a5e62d3e35
--- /dev/null
+++ b/src/qmlworkerscript/qv4serialize.cpp
@@ -0,0 +1,447 @@
+/****************************************************************************
+**
+** Copyright (C) 2016 The Qt Company Ltd.
+** Contact: https://www.qt.io/licensing/
+**
+** This file is part of the QtQml module of the Qt Toolkit.
+**
+** $QT_BEGIN_LICENSE:LGPL$
+** Commercial License Usage
+** Licensees holding valid commercial Qt licenses may use this file in
+** accordance with the commercial license agreement provided with the
+** Software or, alternatively, in accordance with the terms contained in
+** a written agreement between you and The Qt Company. For licensing terms
+** and conditions see https://www.qt.io/terms-conditions. For further
+** information use the contact form at https://www.qt.io/contact-us.
+**
+** GNU Lesser General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU Lesser
+** General Public License version 3 as published by the Free Software
+** Foundation and appearing in the file LICENSE.LGPL3 included in the
+** packaging of this file. Please review the following information to
+** ensure the GNU Lesser General Public License version 3 requirements
+** will be met: https://www.gnu.org/licenses/lgpl-3.0.html.
+**
+** GNU General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU
+** General Public License version 2.0 or (at your option) the GNU General
+** Public license version 3 or any later version approved by the KDE Free
+** Qt Foundation. The licenses are as published by the Free Software
+** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3
+** included in the packaging of this file. Please review the following
+** information to ensure the GNU General Public License requirements will
+** be met: https://www.gnu.org/licenses/gpl-2.0.html and
+** https://www.gnu.org/licenses/gpl-3.0.html.
+**
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+#include "qv4serialize_p.h"
+
+#include <private/qv4value_p.h>
+#include <private/qv4dateobject_p.h>
+#include <private/qv4regexpobject_p.h>
+#if QT_CONFIG(qml_sequence_object)
+#include <private/qv4sequenceobject_p.h>
+#endif
+#include <private/qv4objectproto_p.h>
+#include <private/qv4qobjectwrapper_p.h>
+
+QT_BEGIN_NAMESPACE
+
+using namespace QV4;
+
+// We allow the following JavaScript types to be passed between the main and
+// the secondary thread:
+// + undefined
+// + null
+// + Boolean
+// + String
+// + Function
+// + Array
+// + "Simple" Objects
+// + Number
+// + Date
+// + RegExp
+// <quint8 type><quint24 size><data>
+
+enum Type {
+ WorkerUndefined,
+ WorkerNull,
+ WorkerTrue,
+ WorkerFalse,
+ WorkerString,
+ WorkerFunction,
+ WorkerArray,
+ WorkerObject,
+ WorkerInt32,
+ WorkerUint32,
+ WorkerNumber,
+ WorkerDate,
+ WorkerRegexp,
+ WorkerListModel,
+#if QT_CONFIG(qml_sequence_object)
+ WorkerSequence
+#endif
+};
+
+static inline quint32 valueheader(Type type, quint32 size = 0)
+{
+ return quint8(type) << 24 | (size & 0xFFFFFF);
+}
+
+static inline Type headertype(quint32 header)
+{
+ return (Type)(header >> 24);
+}
+
+static inline quint32 headersize(quint32 header)
+{
+ return header & 0xFFFFFF;
+}
+
+static inline void push(QByteArray &data, quint32 value)
+{
+ data.append((const char *)&value, sizeof(quint32));
+}
+
+static inline void push(QByteArray &data, double value)
+{
+ data.append((const char *)&value, sizeof(double));
+}
+
+static inline void push(QByteArray &data, void *ptr)
+{
+ data.append((const char *)&ptr, sizeof(void *));
+}
+
+static inline void reserve(QByteArray &data, int extra)
+{
+ data.reserve(data.size() + extra);
+}
+
+static inline quint32 popUint32(const char *&data)
+{
+ quint32 rv = *((const quint32 *)data);
+ data += sizeof(quint32);
+ return rv;
+}
+
+static inline double popDouble(const char *&data)
+{
+ double rv = *((const double *)data);
+ data += sizeof(double);
+ return rv;
+}
+
+static inline void *popPtr(const char *&data)
+{
+ void *rv = *((void *const *)data);
+ data += sizeof(void *);
+ return rv;
+}
+
+// XXX TODO: Check that worker script is exception safe in the case of
+// serialization/deserialization failures
+
+#define ALIGN(size) (((size) + 3) & ~3)
+void Serialize::serialize(QByteArray &data, const QV4::Value &v, ExecutionEngine *engine)
+{
+ QV4::Scope scope(engine);
+
+ if (v.isEmpty()) {
+ Q_ASSERT(!"Serialize: got empty value");
+ } else if (v.isUndefined()) {
+ push(data, valueheader(WorkerUndefined));
+ } else if (v.isNull()) {
+ push(data, valueheader(WorkerNull));
+ } else if (v.isBoolean()) {
+ push(data, valueheader(v.booleanValue() == true ? WorkerTrue : WorkerFalse));
+ } else if (v.isString()) {
+ const QString &qstr = v.toQString();
+ int length = qstr.length();
+ if (length > 0xFFFFFF) {
+ push(data, valueheader(WorkerUndefined));
+ return;
+ }
+ int utf16size = ALIGN(length * sizeof(quint16));
+
+ reserve(data, utf16size + sizeof(quint32));
+ push(data, valueheader(WorkerString, length));
+
+ int offset = data.size();
+ data.resize(data.size() + utf16size);
+ char *buffer = data.data() + offset;
+
+ memcpy(buffer, qstr.constData(), length*sizeof(QChar));
+ } else if (v.as<FunctionObject>()) {
+ // XXX TODO: Implement passing function objects between the main and
+ // worker scripts
+ push(data, valueheader(WorkerUndefined));
+ } else if (const QV4::ArrayObject *array = v.as<ArrayObject>()) {
+ uint length = array->getLength();
+ if (length > 0xFFFFFF) {
+ push(data, valueheader(WorkerUndefined));
+ return;
+ }
+ reserve(data, sizeof(quint32) + length * sizeof(quint32));
+ push(data, valueheader(WorkerArray, length));
+ ScopedValue val(scope);
+ for (uint ii = 0; ii < length; ++ii)
+ serialize(data, (val = array->get(ii)), engine);
+ } else if (v.isInteger()) {
+ reserve(data, 2 * sizeof(quint32));
+ push(data, valueheader(WorkerInt32));
+ push(data, (quint32)v.integerValue());
+// } else if (v.IsUint32()) {
+// reserve(data, 2 * sizeof(quint32));
+// push(data, valueheader(WorkerUint32));
+// push(data, v.Uint32Value());
+ } else if (v.isNumber()) {
+ reserve(data, sizeof(quint32) + sizeof(double));
+ push(data, valueheader(WorkerNumber));
+ push(data, v.asDouble());
+ } else if (const QV4::DateObject *d = v.as<DateObject>()) {
+ reserve(data, sizeof(quint32) + sizeof(double));
+ push(data, valueheader(WorkerDate));
+ push(data, d->date());
+ } else if (const RegExpObject *re = v.as<RegExpObject>()) {
+ quint32 flags = re->flags();
+ QString pattern = re->source();
+ int length = pattern.length() + 1;
+ if (length > 0xFFFFFF) {
+ push(data, valueheader(WorkerUndefined));
+ return;
+ }
+ int utf16size = ALIGN(length * sizeof(quint16));
+
+ reserve(data, sizeof(quint32) + utf16size);
+ push(data, valueheader(WorkerRegexp, flags));
+ push(data, (quint32)length);
+
+ int offset = data.size();
+ data.resize(data.size() + utf16size);
+ char *buffer = data.data() + offset;
+
+ memcpy(buffer, pattern.constData(), length*sizeof(QChar));
+ } else if (const QObjectWrapper *qobjectWrapper = v.as<QV4::QObjectWrapper>()) {
+ // XXX TODO: Generalize passing objects between the main thread and worker scripts so
+ // that others can trivially plug in their elements.
+ if (QObject *lm = qobjectWrapper->object()) {
+ if (QObject *agent = qvariant_cast<QObject *>(lm->property("agent"))) {
+ if (QMetaObject::invokeMethod(agent, "addref")) {
+ push(data, valueheader(WorkerListModel));
+ push(data, (void *)agent);
+ return;
+ }
+ }
+ }
+ // No other QObject's are allowed to be sent
+ push(data, valueheader(WorkerUndefined));
+ } else if (const Object *o = v.as<Object>()) {
+#if QT_CONFIG(qml_sequence_object)
+ if (o->isListType()) {
+ // valid sequence. we generate a length (sequence length + 1 for the sequence type)
+ uint seqLength = ScopedValue(scope, o->get(engine->id_length()))->toUInt32();
+ uint length = seqLength + 1;
+ if (length > 0xFFFFFF) {
+ push(data, valueheader(WorkerUndefined));
+ return;
+ }
+ reserve(data, sizeof(quint32) + length * sizeof(quint32));
+ push(data, valueheader(WorkerSequence, length));
+ serialize(data, QV4::Value::fromInt32(QV4::SequencePrototype::metaTypeForSequence(o)), engine); // sequence type
+ ScopedValue val(scope);
+ for (uint ii = 0; ii < seqLength; ++ii)
+ serialize(data, (val = o->get(ii)), engine); // sequence elements
+
+ return;
+ }
+#endif
+
+ // regular object
+ QV4::ScopedValue val(scope, v);
+ QV4::ScopedArrayObject properties(scope, QV4::ObjectPrototype::getOwnPropertyNames(engine, val));
+ quint32 length = properties->getLength();
+ if (length > 0xFFFFFF) {
+ push(data, valueheader(WorkerUndefined));
+ return;
+ }
+ push(data, valueheader(WorkerObject, length));
+
+ QV4::ScopedValue s(scope);
+ for (quint32 ii = 0; ii < length; ++ii) {
+ s = properties->get(ii);
+ serialize(data, s, engine);
+
+ QV4::String *str = s->as<String>();
+ val = o->get(str);
+ if (scope.hasException())
+ scope.engine->catchException();
+
+ serialize(data, val, engine);
+ }
+ return;
+ } else {
+ push(data, valueheader(WorkerUndefined));
+ }
+}
+
+struct VariantRef
+{
+ VariantRef() : obj(nullptr) {}
+ VariantRef(const VariantRef &r) : obj(r.obj) { addref(); }
+ VariantRef(QObject *a) : obj(a) { addref(); }
+ ~VariantRef() { release(); }
+
+ VariantRef &operator=(const VariantRef &o) {
+ o.addref();
+ release();
+ obj = o.obj;
+ return *this;
+ }
+
+ void addref() const
+ {
+ if (obj)
+ QMetaObject::invokeMethod(obj, "addref");
+ }
+
+ void release() const
+ {
+ if (obj)
+ QMetaObject::invokeMethod(obj, "release");
+
+ }
+
+ QObject *obj;
+};
+
+QT_END_NAMESPACE
+Q_DECLARE_METATYPE(VariantRef)
+Q_DECLARE_METATYPE(QV4::ExecutionEngine *)
+QT_BEGIN_NAMESPACE
+
+ReturnedValue Serialize::deserialize(const char *&data, ExecutionEngine *engine)
+{
+ quint32 header = popUint32(data);
+ Type type = headertype(header);
+
+ Scope scope(engine);
+
+ switch (type) {
+ case WorkerUndefined:
+ return QV4::Encode::undefined();
+ case WorkerNull:
+ return QV4::Encode::null();
+ case WorkerTrue:
+ return QV4::Encode(true);
+ case WorkerFalse:
+ return QV4::Encode(false);
+ case WorkerString:
+ {
+ quint32 size = headersize(header);
+ QString qstr((const QChar *)data, size);
+ data += ALIGN(size * sizeof(quint16));
+ return QV4::Encode(engine->newString(qstr));
+ }
+ case WorkerFunction:
+ Q_ASSERT(!"Unreachable");
+ break;
+ case WorkerArray:
+ {
+ quint32 size = headersize(header);
+ ScopedArrayObject a(scope, engine->newArrayObject());
+ ScopedValue v(scope);
+ for (quint32 ii = 0; ii < size; ++ii) {
+ v = deserialize(data, engine);
+ a->put(ii, v);
+ }
+ return a.asReturnedValue();
+ }
+ case WorkerObject:
+ {
+ quint32 size = headersize(header);
+ ScopedObject o(scope, engine->newObject());
+ ScopedValue name(scope);
+ ScopedString n(scope);
+ ScopedValue value(scope);
+ for (quint32 ii = 0; ii < size; ++ii) {
+ name = deserialize(data, engine);
+ value = deserialize(data, engine);
+ n = name->asReturnedValue();
+ o->put(n, value);
+ }
+ return o.asReturnedValue();
+ }
+ case WorkerInt32:
+ return QV4::Encode((qint32)popUint32(data));
+ case WorkerUint32:
+ return QV4::Encode(popUint32(data));
+ case WorkerNumber:
+ return QV4::Encode(popDouble(data));
+ case WorkerDate:
+ return QV4::Encode(engine->newDateObject(QV4::Value::fromDouble(popDouble(data))));
+ case WorkerRegexp:
+ {
+ quint32 flags = headersize(header);
+ quint32 length = popUint32(data);
+ QString pattern = QString((const QChar *)data, length - 1);
+ data += ALIGN(length * sizeof(quint16));
+ return Encode(engine->newRegExpObject(pattern, flags));
+ }
+ case WorkerListModel:
+ {
+ QObject *agent = reinterpret_cast<QObject *>(popPtr(data));
+ QV4::ScopedValue rv(scope, QV4::QObjectWrapper::wrap(engine, agent));
+ // ### Find a better solution then the ugly property
+ VariantRef ref(agent);
+ QVariant var = QVariant::fromValue(ref);
+ QV4::ScopedValue v(scope, scope.engine->fromVariant(var));
+ QV4::ScopedString s(scope, engine->newString(QStringLiteral("__qml:hidden:ref")));
+ rv->as<Object>()->defineReadonlyProperty(s, v);
+
+ QMetaObject::invokeMethod(agent, "release");
+ agent->setProperty("engine", QVariant::fromValue(engine));
+ return rv->asReturnedValue();
+ }
+#if QT_CONFIG(qml_sequence_object)
+ case WorkerSequence:
+ {
+ ScopedValue value(scope);
+ bool succeeded = false;
+ quint32 length = headersize(header);
+ quint32 seqLength = length - 1;
+ value = deserialize(data, engine);
+ int sequenceType = value->integerValue();
+ ScopedArrayObject array(scope, engine->newArrayObject());
+ array->arrayReserve(seqLength);
+ for (quint32 ii = 0; ii < seqLength; ++ii) {
+ value = deserialize(data, engine);
+ array->arrayPut(ii, value);
+ }
+ array->setArrayLengthUnchecked(seqLength);
+ QVariant seqVariant = QV4::SequencePrototype::toVariant(array, sequenceType, &succeeded);
+ return QV4::SequencePrototype::fromVariant(engine, seqVariant, &succeeded);
+ }
+#endif
+ }
+ Q_ASSERT(!"Unreachable");
+ return QV4::Encode::undefined();
+}
+
+QByteArray Serialize::serialize(const QV4::Value &value, ExecutionEngine *engine)
+{
+ QByteArray rv;
+ serialize(rv, value, engine);
+ return rv;
+}
+
+ReturnedValue Serialize::deserialize(const QByteArray &data, ExecutionEngine *engine)
+{
+ const char *stream = data.constData();
+ return deserialize(stream, engine);
+}
+
+QT_END_NAMESPACE
diff --git a/src/qmlworkerscript/qv4serialize_p.h b/src/qmlworkerscript/qv4serialize_p.h
new file mode 100644
index 0000000000..c8700c3ca5
--- /dev/null
+++ b/src/qmlworkerscript/qv4serialize_p.h
@@ -0,0 +1,76 @@
+/****************************************************************************
+**
+** Copyright (C) 2016 The Qt Company Ltd.
+** Contact: https://www.qt.io/licensing/
+**
+** This file is part of the QtQml module of the Qt Toolkit.
+**
+** $QT_BEGIN_LICENSE:LGPL$
+** Commercial License Usage
+** Licensees holding valid commercial Qt licenses may use this file in
+** accordance with the commercial license agreement provided with the
+** Software or, alternatively, in accordance with the terms contained in
+** a written agreement between you and The Qt Company. For licensing terms
+** and conditions see https://www.qt.io/terms-conditions. For further
+** information use the contact form at https://www.qt.io/contact-us.
+**
+** GNU Lesser General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU Lesser
+** General Public License version 3 as published by the Free Software
+** Foundation and appearing in the file LICENSE.LGPL3 included in the
+** packaging of this file. Please review the following information to
+** ensure the GNU Lesser General Public License version 3 requirements
+** will be met: https://www.gnu.org/licenses/lgpl-3.0.html.
+**
+** GNU General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU
+** General Public License version 2.0 or (at your option) the GNU General
+** Public license version 3 or any later version approved by the KDE Free
+** Qt Foundation. The licenses are as published by the Free Software
+** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3
+** included in the packaging of this file. Please review the following
+** information to ensure the GNU General Public License requirements will
+** be met: https://www.gnu.org/licenses/gpl-2.0.html and
+** https://www.gnu.org/licenses/gpl-3.0.html.
+**
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+#ifndef QV4SERIALIZE_P_H
+#define QV4SERIALIZE_P_H
+
+//
+// W A R N I N G
+// -------------
+//
+// This file is not part of the Qt API. It exists purely as an
+// implementation detail. This header file may change from version to
+// version without notice, or even be removed.
+//
+// We mean it.
+//
+
+#include <QtCore/qbytearray.h>
+#include <private/qv4value_p.h>
+
+QT_BEGIN_NAMESPACE
+
+namespace QV4 {
+
+class Serialize {
+public:
+
+ static QByteArray serialize(const Value &, ExecutionEngine *);
+ static ReturnedValue deserialize(const QByteArray &, ExecutionEngine *);
+
+private:
+ static void serialize(QByteArray &, const Value &, ExecutionEngine *);
+ static ReturnedValue deserialize(const char *&, ExecutionEngine *);
+};
+
+}
+
+QT_END_NAMESPACE
+
+#endif // QV8WORKER_P_H