aboutsummaryrefslogtreecommitdiffstats
path: root/src
diff options
context:
space:
mode:
Diffstat (limited to 'src')
-rw-r--r--src/qml/qml/qqmlmetatype.cpp1
-rw-r--r--src/qml/qml/qqmlscriptblob.cpp6
-rw-r--r--src/qml/qml/qqmlscriptblob_p.h9
-rw-r--r--src/qml/qml/qqmlscriptdata_p.h1
-rw-r--r--src/qml/qml/qqmltypeloader.cpp106
-rw-r--r--src/qml/qml/qqmltypeloader_p.h20
-rw-r--r--src/qml/qml/qqmltypeloaderdata_p.h2
-rw-r--r--src/qmlworkerscript/qquickworkerscript.cpp116
-rw-r--r--src/qmlworkerscript/qquickworkerscript_p.h8
9 files changed, 170 insertions, 99 deletions
diff --git a/src/qml/qml/qqmlmetatype.cpp b/src/qml/qml/qqmlmetatype.cpp
index 4a37c446e8..e397d20b9a 100644
--- a/src/qml/qml/qqmlmetatype.cpp
+++ b/src/qml/qml/qqmlmetatype.cpp
@@ -7,6 +7,7 @@
#include <private/qqmlmetatypedata_p.h>
#include <private/qqmlpropertycachecreator_p.h>
#include <private/qqmlscriptblob_p.h>
+#include <private/qqmlscriptdata_p.h>
#include <private/qqmltype_p_p.h>
#include <private/qqmltypeloader_p.h>
#include <private/qqmltypemodule_p.h>
diff --git a/src/qml/qml/qqmlscriptblob.cpp b/src/qml/qml/qqmlscriptblob.cpp
index d6b1fe80c0..16ef9aac88 100644
--- a/src/qml/qml/qqmlscriptblob.cpp
+++ b/src/qml/qml/qqmlscriptblob.cpp
@@ -4,7 +4,6 @@
#include <private/qqmlengine_p.h>
#include <private/qqmlirbuilder_p.h>
#include <private/qqmlscriptblob_p.h>
-#include <private/qqmlscriptdata_p.h>
#include <private/qqmlsourcecoordinate_p.h>
#include <private/qqmlcontextdata_p.h>
#include <private/qv4runtimecodegen_p.h>
@@ -26,11 +25,6 @@ QQmlScriptBlob::~QQmlScriptBlob()
{
}
-QQmlRefPointer<QQmlScriptData> QQmlScriptBlob::scriptData() const
-{
- return m_scriptData;
-}
-
void QQmlScriptBlob::dataReceived(const SourceCodeData &data)
{
assertTypeLoaderThread();
diff --git a/src/qml/qml/qqmlscriptblob_p.h b/src/qml/qml/qqmlscriptblob_p.h
index 4d882b7f4f..583bb6d07a 100644
--- a/src/qml/qml/qqmlscriptblob_p.h
+++ b/src/qml/qml/qqmlscriptblob_p.h
@@ -15,13 +15,13 @@
// We mean it.
//
-#include <private/qqmltypeloader_p.h>
#include <private/qqmlnotifyingblob_p.h>
+#include <private/qqmlscriptdata_p.h>
+#include <private/qqmltypeloader_p.h>
QT_BEGIN_NAMESPACE
Q_DECLARE_LOGGING_CATEGORY(DBG_DISK_CACHE)
-class QQmlScriptData;
class Q_AUTOTEST_EXPORT QQmlScriptBlob : public QQmlNotifyingBlob
{
private:
@@ -40,7 +40,10 @@ public:
QQmlRefPointer<QQmlScriptBlob> script;
};
- QQmlRefPointer<QQmlScriptData> scriptData() const;
+ QQmlRefPointer<QQmlScriptData> scriptData() const
+ {
+ return m_scriptData;
+ }
protected:
void dataReceived(const SourceCodeData &) override;
diff --git a/src/qml/qml/qqmlscriptdata_p.h b/src/qml/qml/qqmlscriptdata_p.h
index 6eb3171505..f54b8b75a1 100644
--- a/src/qml/qml/qqmlscriptdata_p.h
+++ b/src/qml/qml/qqmlscriptdata_p.h
@@ -16,7 +16,6 @@
//
#include <private/qqmlrefcount_p.h>
-#include <private/qqmlscriptblob_p.h>
#include <private/qv4value_p.h>
#include <private/qv4persistent_p.h>
#include <private/qv4compileddata_p.h>
diff --git a/src/qml/qml/qqmltypeloader.cpp b/src/qml/qml/qqmltypeloader.cpp
index d4d711332b..0d89f93a3f 100644
--- a/src/qml/qml/qqmltypeloader.cpp
+++ b/src/qml/qml/qqmltypeloader.cpp
@@ -1295,6 +1295,32 @@ QQmlTypeLoader::~QQmlTypeLoader()
clearQmldirInfo();
}
+template<typename Blob>
+QQmlRefPointer<Blob> handleExisting(
+ const QQmlTypeLoaderSharedDataPtr &data, QQmlRefPointer<Blob> &&blob,
+ QQmlTypeLoader::Mode mode)
+{
+ if ((mode == QQmlTypeLoader::PreferSynchronous && QQmlFile::isSynchronous(blob->finalUrl()))
+ || mode == QQmlTypeLoader::Synchronous) {
+ // this was started Asynchronous, but we need to force Synchronous
+ // completion now.
+
+ // This only works when called directly from e.g. the UI thread, but not
+ // when recursively called on the QML thread via resolveTypes()
+
+ // NB: We do not want to know whether the thread is the main thread, but specifically
+ // that the thread is _not_ the thread we're waiting for.
+ // If !QT_CONFIG(qml_type_loader_thread) the QML thread is the main thread.
+
+ QQmlTypeLoaderThread *thread = data.thread();
+ if (thread && !thread->isThisThread()) {
+ while (!blob->isCompleteOrError())
+ thread->waitForNextMessage(); // Requires lock to be held, via data above
+ }
+ }
+ return blob;
+}
+
/*!
Returns a QQmlTypeData for the specified \a url. The QQmlTypeData may be cached.
*/
@@ -1306,34 +1332,14 @@ QQmlRefPointer<QQmlTypeData> QQmlTypeLoader::getType(const QUrl &unNormalizedUrl
(QQmlFile::urlToLocalFileOrQrc(unNormalizedUrl).isEmpty() ||
!QDir::isRelativePath(QQmlFile::urlToLocalFileOrQrc(unNormalizedUrl))));
- const QUrl url = QQmlMetaType::normalizedUrl(unNormalizedUrl);
-
- const auto handleExisting = [&](const QQmlRefPointer<QQmlTypeData> &typeData) {
- if ((mode == PreferSynchronous || mode == Synchronous) && QQmlFile::isSynchronous(url)) {
- // this was started Asynchronous, but we need to force Synchronous
- // completion now (if at all possible with this type of URL).
-
- // This only works when called directly from e.g. the UI thread, but not
- // when recursively called on the QML thread via resolveTypes()
-
- // NB: We do not want to know whether the thread is the main thread, but specifically
- // that the thread is _not_ the thread we're waiting for.
- // If !QT_CONFIG(qml_type_loader_thread) the QML thread is the main thread.
- if (thread() && !thread()->isThisThread()) {
- while (!typeData->isCompleteOrError())
- thread()->waitForNextMessage(); // Requires lock to be held, via data above
- }
- }
- return typeData;
- };
-
QQmlRefPointer<QQmlTypeData> typeData;
{
+ const QUrl url = QQmlMetaType::normalizedUrl(unNormalizedUrl);
QQmlTypeLoaderSharedDataPtr data(&m_data);
typeData = data->typeCache.value(url);
if (typeData)
- return handleExisting(typeData);
+ return handleExisting(data, std::move(typeData), mode);
// Trim before adding the new type, so that we don't immediately trim it away
if (data->typeCache.size() >= data->typeCacheTrimThreshold)
@@ -1345,18 +1351,7 @@ QQmlRefPointer<QQmlTypeData> QQmlTypeLoader::getType(const QUrl &unNormalizedUrl
data->typeCache.insert(url, typeData);
}
- QQmlMetaType::CachedUnitLookupError error = QQmlMetaType::CachedUnitLookupError::NoError;
- const QQmlMetaType::CacheMode cacheMode = aotCacheMode();
- if (const QQmlPrivate::CachedQmlUnit *cachedUnit = (cacheMode != QQmlMetaType::RejectAll)
- ? QQmlMetaType::findCachedCompilationUnit(typeData->url(), cacheMode, &error)
- : nullptr) {
- loadWithCachedUnit(QQmlDataBlob::Ptr(typeData.data()), cachedUnit, mode);
- } else {
- typeData->setCachedUnitStatus(error);
- load(QQmlDataBlob::Ptr(typeData.data()), mode);
- }
-
- return typeData;
+ return finalizeBlob(std::move(typeData), mode);
}
/*!
@@ -1378,6 +1373,32 @@ static bool isModuleUrl(const QUrl &url)
return url.path().endsWith(QLatin1String(".mjs"));
}
+QQmlRefPointer<QQmlScriptBlob> QQmlTypeLoader::getScript(const QUrl &unNormalizedUrl, Mode mode)
+{
+ // This can be called from either thread.
+
+ Q_ASSERT(!unNormalizedUrl.isRelative() &&
+ (QQmlFile::urlToLocalFileOrQrc(unNormalizedUrl).isEmpty() ||
+ !QDir::isRelativePath(QQmlFile::urlToLocalFileOrQrc(unNormalizedUrl))));
+
+ QQmlRefPointer<QQmlScriptBlob> scriptBlob;
+ {
+ const QUrl url = QQmlMetaType::normalizedUrl(unNormalizedUrl);
+ QQmlTypeLoaderSharedDataPtr data(&m_data);
+
+ scriptBlob = data->scriptCache.value(url);
+ if (scriptBlob)
+ return handleExisting(data, std::move(scriptBlob), mode);
+
+ scriptBlob = QQml::makeRefPointer<QQmlScriptBlob>(url, this, isModuleUrl(url)
+ ? QQmlScriptBlob::IsESModule::Yes
+ : QQmlScriptBlob::IsESModule::No);
+ data->scriptCache.insert(url, scriptBlob);
+ }
+
+ return finalizeBlob(std::move(scriptBlob), mode);
+}
+
QQmlRefPointer<QV4::CompiledData::CompilationUnit> QQmlTypeLoader::injectModule(
const QUrl &relativeUrl, const QV4::CompiledData::Unit *unit)
{
@@ -1398,7 +1419,8 @@ QQmlRefPointer<QV4::CompiledData::CompilationUnit> QQmlTypeLoader::injectModule(
}
/*!
-Return a QQmlScriptBlob for \a url. The QQmlScriptData may be cached.
+Return a QQmlScriptBlob for \a unNormalizedUrl or \a relativeUrl.
+This assumes PreferSynchronous, and therefore the result may not be ready yet.
*/
QQmlRefPointer<QQmlScriptBlob> QQmlTypeLoader::getScript(
const QUrl &unNormalizedUrl, const QUrl &relativeUrl)
@@ -1421,6 +1443,7 @@ QQmlRefPointer<QQmlScriptBlob> QQmlTypeLoader::getScript(
if (!scriptBlob && unNormalizedUrl != relativeUrl)
scriptBlob = data->scriptCache.value(relativeUrl);
+ // Do not try to finish the loading via handleExisting() here.
if (scriptBlob)
return scriptBlob;
@@ -1430,18 +1453,7 @@ QQmlRefPointer<QQmlScriptBlob> QQmlTypeLoader::getScript(
data->scriptCache.insert(url, scriptBlob);
}
- QQmlMetaType::CachedUnitLookupError error = QQmlMetaType::CachedUnitLookupError::NoError;
- const QQmlMetaType::CacheMode cacheMode = aotCacheMode();
- if (const QQmlPrivate::CachedQmlUnit *cachedUnit = (cacheMode != QQmlMetaType::RejectAll)
- ? QQmlMetaType::findCachedCompilationUnit(scriptBlob->url(), cacheMode, &error)
- : nullptr) {
- QQmlTypeLoader::loadWithCachedUnit(QQmlDataBlob::Ptr(scriptBlob.data()), cachedUnit);
- } else {
- scriptBlob->setCachedUnitStatus(error);
- QQmlTypeLoader::load(QQmlDataBlob::Ptr(scriptBlob.data()));
- }
-
- return scriptBlob;
+ return finalizeBlob(std::move(scriptBlob), PreferSynchronous);
}
/*!
diff --git a/src/qml/qml/qqmltypeloader_p.h b/src/qml/qml/qqmltypeloader_p.h
index d207acdf34..82b978a292 100644
--- a/src/qml/qml/qqmltypeloader_p.h
+++ b/src/qml/qml/qqmltypeloader_p.h
@@ -183,6 +183,9 @@ public:
QQmlRefPointer<QQmlTypeData> getType(
const QByteArray &data, const QUrl &url, Mode mode = PreferSynchronous);
+ QQmlRefPointer<QQmlScriptBlob> getScript(
+ const QUrl &unNormalizedUrl, Mode mode = PreferSynchronous);
+
QQmlRefPointer<QV4::CompiledData::CompilationUnit> injectModule(
const QUrl &relativeUrl, const QV4::CompiledData::Unit *unit);
@@ -360,6 +363,23 @@ private:
return true;
}
+ template<typename Blob>
+ QQmlRefPointer<Blob> finalizeBlob(QQmlRefPointer<Blob> &&blob, QQmlTypeLoader::Mode mode)
+ {
+ QQmlMetaType::CachedUnitLookupError error = QQmlMetaType::CachedUnitLookupError::NoError;
+ const QQmlMetaType::CacheMode cacheMode = aotCacheMode();
+ if (const QQmlPrivate::CachedQmlUnit *cachedUnit = (cacheMode != QQmlMetaType::RejectAll)
+ ? QQmlMetaType::findCachedCompilationUnit(blob->url(), cacheMode, &error)
+ : nullptr) {
+ loadWithCachedUnit(QQmlDataBlob::Ptr(blob.data()), cachedUnit, mode);
+ } else {
+ blob->setCachedUnitStatus(error);
+ load(QQmlDataBlob::Ptr(blob.data()), mode);
+ }
+
+ return blob;
+ }
+
QQmlMetaType::CacheMode aotCacheMode();
QQmlTypeLoaderLockedData m_data;
diff --git a/src/qml/qml/qqmltypeloaderdata_p.h b/src/qml/qml/qqmltypeloaderdata_p.h
index dc2cf7f549..f509b1e625 100644
--- a/src/qml/qml/qqmltypeloaderdata_p.h
+++ b/src/qml/qml/qqmltypeloaderdata_p.h
@@ -222,6 +222,8 @@ public:
Data *operator->() const { return &data->m_sharedData; }
operator Data *() const { return &data->m_sharedData; }
+ QQmlTypeLoaderThread *thread() const { return data->thread(); }
+
private:
LockedData *data = nullptr;
};
diff --git a/src/qmlworkerscript/qquickworkerscript.cpp b/src/qmlworkerscript/qquickworkerscript.cpp
index 14cd783c32..732c272582 100644
--- a/src/qmlworkerscript/qquickworkerscript.cpp
+++ b/src/qmlworkerscript/qquickworkerscript.cpp
@@ -3,9 +3,12 @@
#include "qtqmlworkerscriptglobal_p.h"
#include "qquickworkerscript_p.h"
+
#include <private/qqmlengine_p.h>
#include <private/qqmlexpression_p.h>
#include <private/qjsvalue_p.h>
+#include <private/qqmlscriptblob_p.h>
+#include <private/qqmlscriptdata_p.h>
#include <QtCore/qcoreevent.h>
#include <QtCore/qcoreapplication.h>
@@ -38,6 +41,7 @@ enum class WorkerEventType
Load,
Remove,
Error,
+ Ready,
Destroy = QEvent::User + 100,
};
@@ -99,6 +103,12 @@ private:
QQmlError m_error;
};
+class WorkerReadyEvent : public QEvent
+{
+public:
+ WorkerReadyEvent() : QEvent(QEvent::Type(WorkerEventType::Ready)) {}
+};
+
class WorkerDestroyEvent : public QEvent
{
public:
@@ -107,18 +117,23 @@ public:
struct WorkerScript
: public QV4::ExecutionEngine::Deletable
+ , public QQmlNotifyingBlob::Callback
#if QT_CONFIG(qml_network)
, public QQmlNetworkAccessManagerFactory
#endif
{
- WorkerScript(QV4::ExecutionEngine *);
+ WorkerScript(QV4::ExecutionEngine *engine);
~WorkerScript() = default;
+ QV4::ExecutionEngine *engine = nullptr;
QQuickWorkerScriptEnginePrivate *p = nullptr;
QQuickWorkerScript *owner = nullptr;
+
#if QT_CONFIG(qml_network)
QNetworkAccessManager *create(QObject *parent) final;
#endif
+
+ void ready(QQmlNotifyingBlob *blob) final;
};
V4_DEFINE_EXTENSION(WorkerScript, workerScriptExtension);
@@ -148,6 +163,9 @@ public:
static QV4::ReturnedValue method_sendMessage(const QV4::FunctionObject *, const QV4::Value *thisObject, const QV4::Value *argv, int argc);
QV4::ExecutionEngine *workerEngine(int id);
+ void reportScriptReady(WorkerScript *);
+ void reportScriptException(WorkerScript *, const QQmlError &error);
+
signals:
void stopThread();
@@ -155,9 +173,8 @@ protected:
bool event(QEvent *) override;
private:
- void processMessage(int, const QByteArray &);
- void processLoad(int, const QUrl &);
- void reportScriptException(WorkerScript *, const QQmlError &error);
+ void processMessage(int id, const QByteArray &data);
+ void processLoad(int id, const QUrl &url);
};
QV4::ReturnedValue QQuickWorkerScriptEnginePrivate::method_sendMessage(const QV4::FunctionObject *b,
@@ -226,10 +243,6 @@ QV4::ExecutionEngine *QQuickWorkerScriptEnginePrivate::workerEngine(int id)
WorkerScript *script = workerScriptExtension(engine);
script->owner = owner;
script->p = this;
-#if QT_CONFIG(qml_network)
- // Eagerly create a network access manager that can outlive the parent engine.
- engine->getNetworkAccessManager();
-#endif
*it = engine;
return engine;
}
@@ -268,39 +281,24 @@ void QQuickWorkerScriptEnginePrivate::processLoad(int id, const QUrl &url)
if (url.isRelative())
return;
- QString fileName = QQmlFile::urlToLocalFileOrQrc(url);
-
QV4::ExecutionEngine *engine = workerEngine(id);
if (!engine)
return;
WorkerScript *script = workerScriptExtension(engine);
+ QQmlRefPointer<QQmlScriptBlob> scriptBlob = engine->typeLoader()->getScript(url);
- if (fileName.endsWith(QLatin1String(".mjs"))) {
- if (auto module = engine->loadModule(url)) {
- if (module->instantiate())
- module->evaluate();
- } else {
- engine->throwError(QStringLiteral("Could not load module file"));
- }
- } else {
- QString error;
- QV4::Scope scope(engine);
- QScopedPointer<QV4::Script> program;
- program.reset(QV4::Script::createFromFileOrCache(
- engine, /*qmlContext*/nullptr, fileName, url, &error));
- if (program.isNull()) {
- if (!error.isEmpty())
- qWarning().nospace() << error;
- return;
- }
-
- if (!engine->hasException)
- program->run();
- }
+ if (scriptBlob->isCompleteOrError())
+ script->ready(scriptBlob.data());
+ else
+ scriptBlob->registerCallback(script);
+}
- if (engine->hasException)
- reportScriptException(script, engine->catchExceptionAsQmlError());
+void QQuickWorkerScriptEnginePrivate::reportScriptReady(WorkerScript *script)
+{
+ QMutexLocker locker(&script->p->m_lock);
+ if (script->owner)
+ QCoreApplication::postEvent(script->owner, new WorkerReadyEvent);
}
void QQuickWorkerScriptEnginePrivate::reportScriptException(WorkerScript *script,
@@ -340,7 +338,7 @@ QQuickWorkerScriptEngine::~QQuickWorkerScriptEngine()
}
-WorkerScript::WorkerScript(QV4::ExecutionEngine *engine)
+WorkerScript::WorkerScript(QV4::ExecutionEngine *engine) : engine(engine)
{
engine->initQmlGlobalObject();
@@ -360,6 +358,38 @@ WorkerScript::WorkerScript(QV4::ExecutionEngine *engine)
#endif // qml_network
}
+void WorkerScript::ready(QQmlNotifyingBlob *scriptBlob)
+{
+ if (scriptBlob->isComplete()) {
+ const auto cu = engine->executableCompilationUnit(
+ static_cast<QQmlScriptBlob *>(scriptBlob)->scriptData()->compilationUnit());
+ if (cu->isESModule()) {
+ if (cu->instantiate())
+ cu->evaluate();
+ } else {
+ QV4::Function *vmFunction = cu->rootFunction();
+ QScopedValueRollback<QV4::Function *> savedGlobal(engine->globalCode, vmFunction);
+ vmFunction->call(engine->globalObject, nullptr, 0, engine->rootContext());
+ }
+
+ if (engine->hasException)
+ p->reportScriptException(this, engine->catchExceptionAsQmlError());
+
+ } else {
+ Q_ASSERT(scriptBlob->isError());
+
+ const QList<QQmlError> errors = scriptBlob->errors();
+ for (const QQmlError &error : errors) {
+ // Funnel this through SyntaxError to get the right output format.
+ engine->throwSyntaxError(
+ error.description(), error.url().toString(), error.line(), error.column());
+ p->reportScriptException(this, engine->catchExceptionAsQmlError());
+ }
+ }
+
+ p->reportScriptReady(this);
+}
+
#if QT_CONFIG(qml_network)
QNetworkAccessManager *WorkerScript::create(QObject *parent)
{
@@ -478,7 +508,7 @@ void QQuickWorkerScriptEngine::run()
Scripts that are ECMAScript modules can freely use import and export statements.
*/
QQuickWorkerScript::QQuickWorkerScript(QObject *parent)
-: QObject(parent), m_engine(nullptr), m_scriptId(-1), m_componentComplete(true)
+: QObject(parent)
{
}
@@ -512,6 +542,11 @@ void QQuickWorkerScript::setSource(const QUrl &source)
if (engine()) {
const QQmlContext *context = qmlContext(this);
m_engine->executeUrl(m_scriptId, context ? context->resolvedUrl(m_source) : m_source);
+ if (m_ready) {
+ // While the new script is loading, we can't accept any events.
+ m_ready = false;
+ emit readyChanged();
+ }
}
emit sourceChanged();
@@ -525,7 +560,7 @@ void QQuickWorkerScript::setSource(const QUrl &source)
*/
bool QQuickWorkerScript::ready() const
{
- return m_engine != nullptr;
+ return m_ready;
}
/*!
@@ -589,8 +624,6 @@ QQuickWorkerScriptEngine *QQuickWorkerScript::engine()
if (m_source.isValid())
m_engine->executeUrl(m_scriptId, context->resolvedUrl(m_source));
- emit readyChanged();
-
return m_engine;
}
return nullptr;
@@ -625,6 +658,11 @@ bool QQuickWorkerScript::event(QEvent *event)
QQmlEnginePrivate::warning(qmlEngine(this), workerEvent->error());
return true;
}
+ case WorkerEventType::Ready:
+ Q_ASSERT(!m_ready);
+ m_ready = true;
+ emit readyChanged();
+ return true;
default:
break;
}
diff --git a/src/qmlworkerscript/qquickworkerscript_p.h b/src/qmlworkerscript/qquickworkerscript_p.h
index 1c4c0500fe..ad2b958c6d 100644
--- a/src/qmlworkerscript/qquickworkerscript_p.h
+++ b/src/qmlworkerscript/qquickworkerscript_p.h
@@ -82,10 +82,12 @@ protected:
private:
QQuickWorkerScriptEngine *engine();
- QQuickWorkerScriptEngine *m_engine;
- int m_scriptId;
+
QUrl m_source;
- bool m_componentComplete;
+ QQuickWorkerScriptEngine *m_engine = nullptr;
+ int m_scriptId = -1;
+ bool m_componentComplete = true;
+ bool m_ready = false;
};
QT_END_NAMESPACE