aboutsummaryrefslogtreecommitdiffstats
path: root/src/qml/qml/qqmltypeloader.cpp
diff options
context:
space:
mode:
authorSimon Hausmann <simon.hausmann@qt.io>2018-08-16 10:57:25 +0200
committerSimon Hausmann <simon.hausmann@qt.io>2018-08-17 11:06:12 +0000
commitf43c1d902d908c6cd523b0174338ac0c98a30647 (patch)
tree50fa2c720dd86aa073efcb935e22bb967e01b5de /src/qml/qml/qqmltypeloader.cpp
parent29275ef53ec8e1edb7aad66af94058786c7d1e2f (diff)
Add support for importing ES modules in .qml files
This is a straight-forward hook into the module implementation in QV4::ExecutionEngine. Modules are pre-compiled in the QML type loader thread. That thread keeps track of all pending loading scripts through the type loader's m_scriptCache. Once a module is compiled, it's thread-safely registered with the execution engine. Script instantiation and evaluation is done solely in the QQmlEngine's thread. ES Modules are identified in imports as well as qmldir files by the .mjs extension. Change-Id: Ie9c59785118afcb49f43a1e176a9f7db00f09428 Reviewed-by: Lars Knoll <lars.knoll@qt.io>
Diffstat (limited to 'src/qml/qml/qqmltypeloader.cpp')
-rw-r--r--src/qml/qml/qqmltypeloader.cpp146
1 files changed, 95 insertions, 51 deletions
diff --git a/src/qml/qml/qqmltypeloader.cpp b/src/qml/qml/qqmltypeloader.cpp
index 2aa64f350a..daa4604dca 100644
--- a/src/qml/qml/qqmltypeloader.cpp
+++ b/src/qml/qml/qqmltypeloader.cpp
@@ -51,6 +51,7 @@
#include <private/qqmltypecompiler_p.h>
#include <private/qqmlpropertyvalidator_p.h>
#include <private/qqmlpropertycachecreator_p.h>
+#include <private/qv4module_p.h>
#include <QtCore/qdir.h>
#include <QtCore/qfile.h>
@@ -2855,8 +2856,19 @@ QV4::ReturnedValue QQmlScriptData::scriptValueForContext(QQmlContextData *parent
return m_value.value();
Q_ASSERT(parentCtxt && parentCtxt->engine);
- QQmlEnginePrivate *ep = QQmlEnginePrivate::get(parentCtxt->engine);
QV4::ExecutionEngine *v4 = parentCtxt->engine->handle();
+
+ if (m_precompiledScript->unitData()->flags & QV4::CompiledData::Unit::IsESModule) {
+ m_loaded = true;
+
+ m_value.set(v4, m_precompiledScript->instantiate(v4));
+ if (!m_value.isNullOrUndefined())
+ m_precompiledScript->evaluate();
+
+ return m_value.value();
+ }
+
+ QQmlEnginePrivate *ep = QQmlEnginePrivate::get(parentCtxt->engine);
QV4::Scope scope(v4);
bool shared = m_precompiledScript->unitData()->flags & QV4::CompiledData::Unit::IsSharedLibrary;
@@ -2945,7 +2957,8 @@ void QQmlScriptData::clear()
}
QQmlScriptBlob::QQmlScriptBlob(const QUrl &url, QQmlTypeLoader *loader)
-: QQmlTypeLoader::Blob(url, JavaScriptFile, loader)
+ : QQmlTypeLoader::Blob(url, JavaScriptFile, loader)
+ , m_isModule(url.path().endsWith(QLatin1String(".mjs")))
{
}
@@ -2979,9 +2992,6 @@ void QQmlScriptBlob::dataReceived(const SourceCodeData &data)
return;
}
- QmlIR::Document irUnit(isDebugging());
-
- irUnit.jsModule.sourceTimeStamp = data.sourceTimeStamp();
QString error;
QString source = data.readAll(&error);
if (!error.isEmpty()) {
@@ -2989,31 +2999,47 @@ void QQmlScriptBlob::dataReceived(const SourceCodeData &data)
return;
}
- QmlIR::ScriptDirectivesCollector collector(&irUnit);
- irUnit.jsParserEngine.setDirectives(&collector);
+ QQmlRefPointer<QV4::CompiledData::CompilationUnit> unit;
- QList<QQmlError> errors;
- QQmlRefPointer<QV4::CompiledData::CompilationUnit> unit = QV4::Script::precompile(
- &irUnit.jsModule, &irUnit.jsParserEngine, &irUnit.jsGenerator, urlString(), finalUrlString(),
- source, &errors);
- // No need to addref on unit, it's initial refcount is 1
- source.clear();
- if (!errors.isEmpty()) {
- setError(errors);
- return;
- }
- if (!unit) {
- unit.adopt(new QV4::CompiledData::CompilationUnit);
- }
- irUnit.javaScriptCompilationUnit = unit;
+ if (m_isModule) {
+ QList<QQmlJS::DiagnosticMessage> diagnostics;
+ unit = QV4::ExecutionEngine::compileModule(isDebugging(), url(), source, &diagnostics);
+ QList<QQmlError> errors = QQmlEnginePrivate::qmlErrorFromDiagnostics(urlString(), diagnostics);
+ if (!errors.isEmpty()) {
+ setError(errors);
+ return;
+ }
+ } else {
+ QmlIR::Document irUnit(isDebugging());
- QmlIR::QmlUnitGenerator qmlGenerator;
- qmlGenerator.generate(irUnit);
+ irUnit.jsModule.sourceTimeStamp = data.sourceTimeStamp();
- if ((!disableDiskCache() || forceDiskCache()) && !isDebugging()) {
- QString errorString;
- if (!unit->saveToDisk(url(), &errorString)) {
- qCDebug(DBG_DISK_CACHE()) << "Error saving cached version of" << unit->fileName() << "to disk:" << errorString;
+ QmlIR::ScriptDirectivesCollector collector(&irUnit);
+ irUnit.jsParserEngine.setDirectives(&collector);
+
+ QList<QQmlError> errors;
+ unit = QV4::Script::precompile(
+ &irUnit.jsModule, &irUnit.jsParserEngine, &irUnit.jsGenerator, urlString(), finalUrlString(),
+ source, &errors);
+ // No need to addref on unit, it's initial refcount is 1
+ source.clear();
+ if (!errors.isEmpty()) {
+ setError(errors);
+ return;
+ }
+ if (!unit) {
+ unit.adopt(new QV4::CompiledData::CompilationUnit);
+ }
+ irUnit.javaScriptCompilationUnit = unit;
+
+ QmlIR::QmlUnitGenerator qmlGenerator;
+ qmlGenerator.generate(irUnit);
+
+ if ((!disableDiskCache() || forceDiskCache()) && !isDebugging()) {
+ QString errorString;
+ if (!unit->saveToDisk(url(), &errorString)) {
+ qCDebug(DBG_DISK_CACHE()) << "Error saving cached version of" << unit->fileName() << "to disk:" << errorString;
+ }
}
}
@@ -3049,26 +3075,28 @@ void QQmlScriptBlob::done()
}
}
- m_scriptData->typeNameCache = new QQmlTypeNameCache(m_importCache);
+ if (!m_isModule) {
+ m_scriptData->typeNameCache = new QQmlTypeNameCache(m_importCache);
- QSet<QString> ns;
+ QSet<QString> ns;
- for (int scriptIndex = 0; scriptIndex < m_scripts.count(); ++scriptIndex) {
- const ScriptReference &script = m_scripts.at(scriptIndex);
+ for (int scriptIndex = 0; scriptIndex < m_scripts.count(); ++scriptIndex) {
+ const ScriptReference &script = m_scripts.at(scriptIndex);
- m_scriptData->scripts.append(script.script);
+ m_scriptData->scripts.append(script.script);
- if (!script.nameSpace.isNull()) {
- if (!ns.contains(script.nameSpace)) {
- ns.insert(script.nameSpace);
- m_scriptData->typeNameCache->add(script.nameSpace);
+ if (!script.nameSpace.isNull()) {
+ if (!ns.contains(script.nameSpace)) {
+ ns.insert(script.nameSpace);
+ m_scriptData->typeNameCache->add(script.nameSpace);
+ }
}
+ m_scriptData->typeNameCache->add(script.qualifier, scriptIndex, script.nameSpace);
}
- m_scriptData->typeNameCache->add(script.qualifier, scriptIndex, script.nameSpace);
+
+ m_importCache.populateCache(m_scriptData->typeNameCache);
}
m_scripts.clear();
-
- m_importCache.populateCache(m_scriptData->typeNameCache);
}
QString QQmlScriptBlob::stringAt(int index) const
@@ -3099,20 +3127,36 @@ void QQmlScriptBlob::initializeFromCompilationUnit(const QQmlRefPointer<QV4::Com
QQmlRefPointer<QV4::CompiledData::CompilationUnit> script = m_scriptData->m_precompiledScript;
- QList<QQmlError> errors;
- for (quint32 i = 0, count = script->importCount(); i < count; ++i) {
- const QV4::CompiledData::Import *import = script->importAt(i);
- if (!addImport(import, &errors)) {
- Q_ASSERT(errors.size());
- QQmlError error(errors.takeFirst());
- error.setUrl(m_importCache.baseUrl());
- error.setLine(import->location.line);
- error.setColumn(import->location.column);
- errors.prepend(error); // put it back on the list after filling out information.
- setError(errors);
- return;
+ if (!m_isModule) {
+ QList<QQmlError> errors;
+ for (quint32 i = 0, count = script->importCount(); i < count; ++i) {
+ const QV4::CompiledData::Import *import = script->importAt(i);
+ if (!addImport(import, &errors)) {
+ Q_ASSERT(errors.size());
+ QQmlError error(errors.takeFirst());
+ error.setUrl(m_importCache.baseUrl());
+ error.setLine(import->location.line);
+ error.setColumn(import->location.column);
+ errors.prepend(error); // put it back on the list after filling out information.
+ setError(errors);
+ return;
+ }
}
}
+
+ auto *v4 = QQmlEnginePrivate::getV4Engine(typeLoader()->engine());
+
+ v4->injectModule(unit);
+
+ for (const QString &request: unit->moduleRequests()) {
+ if (v4->moduleForUrl(QUrl(request), unit.data()))
+ continue;
+
+ const QUrl absoluteRequest = unit->finalUrl().resolved(QUrl(request));
+ QQmlRefPointer<QQmlScriptBlob> blob = typeLoader()->getScript(absoluteRequest);
+ addDependency(blob.data());
+ scriptImported(blob, /* ### */QV4::CompiledData::Location(), /*qualifier*/QString(), /*namespace*/QString());
+ }
}
QQmlQmldirData::QQmlQmldirData(const QUrl &url, QQmlTypeLoader *loader)