QML: Make modules imported with registerModule() available in QML
So far, you could only use them from pure JavaScript programs. Also, fix re-exporting parts of native modules. Fixes: QTBUG-105901 Change-Id: I170017083284e6457b1aa0c6e606fd26227edae3 Reviewed-by: Fabian Kosmale <fabian.kosmale@qt.io>
This commit is contained in:
parent
7c56dc7e98
commit
b297e8fccf
|
@ -566,20 +566,26 @@ QJSValue QJSEngine::evaluate(const QString& program, const QString& fileName, in
|
|||
QJSValue QJSEngine::importModule(const QString &fileName)
|
||||
{
|
||||
const QUrl url = urlForFileName(QFileInfo(fileName).canonicalFilePath());
|
||||
auto moduleUnit = m_v4Engine->loadModule(url);
|
||||
const auto module = m_v4Engine->loadModule(url);
|
||||
if (m_v4Engine->hasException)
|
||||
return QJSValuePrivate::fromReturnedValue(m_v4Engine->catchException());
|
||||
|
||||
QV4::Scope scope(m_v4Engine);
|
||||
QV4::Scoped<QV4::Module> moduleNamespace(scope, moduleUnit->instantiate(m_v4Engine));
|
||||
if (m_v4Engine->hasException)
|
||||
return QJSValuePrivate::fromReturnedValue(m_v4Engine->catchException());
|
||||
moduleUnit->evaluate();
|
||||
if (!m_v4Engine->isInterrupted.loadRelaxed())
|
||||
return QJSValuePrivate::fromReturnedValue(moduleNamespace->asReturnedValue());
|
||||
if (const auto compiled = module.compiled) {
|
||||
QV4::Scoped<QV4::Module> moduleNamespace(scope, compiled->instantiate(m_v4Engine));
|
||||
if (m_v4Engine->hasException)
|
||||
return QJSValuePrivate::fromReturnedValue(m_v4Engine->catchException());
|
||||
compiled->evaluate();
|
||||
if (!m_v4Engine->isInterrupted.loadRelaxed())
|
||||
return QJSValuePrivate::fromReturnedValue(moduleNamespace->asReturnedValue());
|
||||
return QJSValuePrivate::fromReturnedValue(
|
||||
m_v4Engine->newErrorObject(QStringLiteral("Interrupted"))->asReturnedValue());
|
||||
}
|
||||
|
||||
return QJSValuePrivate::fromReturnedValue(
|
||||
m_v4Engine->newErrorObject(QStringLiteral("Interrupted"))->asReturnedValue());
|
||||
// If there is neither a native nor a compiled module, we should have seen an exception
|
||||
Q_ASSERT(module.native);
|
||||
|
||||
return QJSValuePrivate::fromReturnedValue(module.native->asReturnedValue());
|
||||
}
|
||||
|
||||
/*!
|
||||
|
@ -609,7 +615,9 @@ QJSValue QJSEngine::importModule(const QString &fileName)
|
|||
*/
|
||||
bool QJSEngine::registerModule(const QString &moduleName, const QJSValue &value)
|
||||
{
|
||||
m_v4Engine->registerModule(moduleName, value);
|
||||
QV4::Scope scope(m_v4Engine);
|
||||
QV4::ScopedValue v4Value(scope, QJSValuePrivate::asReturnedValue(&value));
|
||||
m_v4Engine->registerNativeModule(QUrl(moduleName), v4Value);
|
||||
if (m_v4Engine->hasException)
|
||||
return false;
|
||||
return true;
|
||||
|
|
|
@ -2035,7 +2035,7 @@ QQmlRefPointer<ExecutableCompilationUnit> ExecutionEngine::compileModule(
|
|||
return ExecutableCompilationUnit::create(std::move(unit));
|
||||
}
|
||||
|
||||
void ExecutionEngine::injectModule(const QQmlRefPointer<ExecutableCompilationUnit> &moduleUnit)
|
||||
void ExecutionEngine::injectCompiledModule(const QQmlRefPointer<ExecutableCompilationUnit> &moduleUnit)
|
||||
{
|
||||
// Injection can happen from the QML type loader thread for example, but instantiation and
|
||||
// evaluation must be limited to the ExecutionEngine's thread.
|
||||
|
@ -2043,52 +2043,59 @@ void ExecutionEngine::injectModule(const QQmlRefPointer<ExecutableCompilationUni
|
|||
modules.insert(moduleUnit->finalUrl(), moduleUnit);
|
||||
}
|
||||
|
||||
QQmlRefPointer<ExecutableCompilationUnit> ExecutionEngine::moduleForUrl(const QUrl &_url, const ExecutableCompilationUnit *referrer) const
|
||||
ExecutionEngine::Module ExecutionEngine::moduleForUrl(
|
||||
const QUrl &url, const ExecutableCompilationUnit *referrer) const
|
||||
{
|
||||
QUrl url = QQmlTypeLoader::normalize(_url);
|
||||
if (referrer)
|
||||
url = referrer->finalUrl().resolved(url);
|
||||
|
||||
QMutexLocker moduleGuard(&moduleMutex);
|
||||
auto existingModule = modules.find(url);
|
||||
const auto nativeModule = nativeModules.find(url);
|
||||
if (nativeModule != nativeModules.end())
|
||||
return Module { nullptr, *nativeModule };
|
||||
|
||||
const QUrl resolved = referrer
|
||||
? referrer->finalUrl().resolved(QQmlTypeLoader::normalize(url))
|
||||
: QQmlTypeLoader::normalize(url);
|
||||
auto existingModule = modules.find(resolved);
|
||||
if (existingModule == modules.end())
|
||||
return nullptr;
|
||||
return *existingModule;
|
||||
return Module { nullptr, nullptr };
|
||||
return Module { *existingModule, nullptr };
|
||||
}
|
||||
|
||||
QQmlRefPointer<ExecutableCompilationUnit> ExecutionEngine::loadModule(const QUrl &_url, const ExecutableCompilationUnit *referrer)
|
||||
ExecutionEngine::Module ExecutionEngine::loadModule(const QUrl &url, const ExecutableCompilationUnit *referrer)
|
||||
{
|
||||
QUrl url = QQmlTypeLoader::normalize(_url);
|
||||
if (referrer)
|
||||
url = referrer->finalUrl().resolved(url);
|
||||
|
||||
QMutexLocker moduleGuard(&moduleMutex);
|
||||
auto existingModule = modules.find(url);
|
||||
const auto nativeModule = nativeModules.find(url);
|
||||
if (nativeModule != nativeModules.end())
|
||||
return Module { nullptr, *nativeModule };
|
||||
|
||||
const QUrl resolved = referrer
|
||||
? referrer->finalUrl().resolved(QQmlTypeLoader::normalize(url))
|
||||
: QQmlTypeLoader::normalize(url);
|
||||
auto existingModule = modules.find(resolved);
|
||||
if (existingModule != modules.end())
|
||||
return *existingModule;
|
||||
return Module { *existingModule, nullptr };
|
||||
|
||||
moduleGuard.unlock();
|
||||
|
||||
auto newModule = compileModule(url);
|
||||
auto newModule = compileModule(resolved);
|
||||
if (newModule) {
|
||||
moduleGuard.relock();
|
||||
modules.insert(url, newModule);
|
||||
modules.insert(resolved, newModule);
|
||||
}
|
||||
|
||||
return newModule;
|
||||
return Module { newModule, nullptr };
|
||||
}
|
||||
|
||||
void ExecutionEngine::registerModule(const QString &_name, const QJSValue &module)
|
||||
QV4::Value *ExecutionEngine::registerNativeModule(const QUrl &url, const QV4::Value &module)
|
||||
{
|
||||
const QUrl url(_name);
|
||||
QMutexLocker moduleGuard(&moduleMutex);
|
||||
const auto existingModule = nativeModules.find(url);
|
||||
if (existingModule != nativeModules.end())
|
||||
return;
|
||||
return nullptr;
|
||||
|
||||
QV4::Value* val = this->memoryManager->m_persistentValues->allocate();
|
||||
*val = QJSValuePrivate::asReturnedValue(&module);
|
||||
QV4::Value *val = this->memoryManager->m_persistentValues->allocate();
|
||||
*val = module.asReturnedValue();
|
||||
nativeModules.insert(url, val);
|
||||
return val;
|
||||
}
|
||||
|
||||
bool ExecutionEngine::diskCacheEnabled() const
|
||||
|
|
|
@ -714,19 +714,18 @@ public:
|
|||
QQmlRefPointer<ExecutableCompilationUnit> compileModule(
|
||||
const QUrl &url, const QString &sourceCode, const QDateTime &sourceTimeStamp);
|
||||
|
||||
mutable QMutex moduleMutex;
|
||||
QHash<QUrl, QQmlRefPointer<ExecutableCompilationUnit>> modules;
|
||||
void injectCompiledModule(const QQmlRefPointer<ExecutableCompilationUnit> &moduleUnit);
|
||||
QV4::Value *registerNativeModule(const QUrl &url, const QV4::Value &module);
|
||||
|
||||
// QV4::PersistentValue would be preferred, but using QHash will create copies,
|
||||
// and QV4::PersistentValue doesn't like creating copies.
|
||||
// Instead, we allocate a raw pointer using the same manual memory management
|
||||
// technique in QV4::PersistentValue.
|
||||
QHash<QUrl, QV4::Value*> nativeModules;
|
||||
struct Module {
|
||||
QQmlRefPointer<ExecutableCompilationUnit> compiled;
|
||||
|
||||
void injectModule(const QQmlRefPointer<ExecutableCompilationUnit> &moduleUnit);
|
||||
QQmlRefPointer<ExecutableCompilationUnit> moduleForUrl(const QUrl &_url, const ExecutableCompilationUnit *referrer = nullptr) const;
|
||||
QQmlRefPointer<ExecutableCompilationUnit> loadModule(const QUrl &_url, const ExecutableCompilationUnit *referrer = nullptr);
|
||||
void registerModule(const QString &name, const QJSValue &module);
|
||||
// We can pass a raw value pointer here, but nowhere else. See below.
|
||||
Value *native = nullptr;
|
||||
};
|
||||
|
||||
Module moduleForUrl(const QUrl &_url, const ExecutableCompilationUnit *referrer = nullptr) const;
|
||||
Module loadModule(const QUrl &_url, const ExecutableCompilationUnit *referrer = nullptr);
|
||||
|
||||
bool diskCacheEnabled() const;
|
||||
|
||||
|
@ -767,6 +766,15 @@ private:
|
|||
QHash<QString, quint32> m_consoleCount;
|
||||
|
||||
QVector<Deletable *> m_extensionData;
|
||||
|
||||
mutable QMutex moduleMutex;
|
||||
QHash<QUrl, QQmlRefPointer<ExecutableCompilationUnit>> modules;
|
||||
|
||||
// QV4::PersistentValue would be preferred, but using QHash will create copies,
|
||||
// and QV4::PersistentValue doesn't like creating copies.
|
||||
// Instead, we allocate a raw pointer using the same manual memory management
|
||||
// technique in QV4::PersistentValue.
|
||||
QHash<QUrl, Value *> nativeModules;
|
||||
};
|
||||
|
||||
#define CHECK_STACK_LIMITS(v4) if ((v4)->checkStackLimits()) return Encode::undefined(); \
|
||||
|
|
|
@ -22,6 +22,7 @@
|
|||
#include <private/qqmltypewrapper_p.h>
|
||||
#include <private/inlinecomponentutils_p.h>
|
||||
#include <private/qv4resolvedtypereference_p.h>
|
||||
#include <private/qv4objectiterator_p.h>
|
||||
|
||||
#include <QtQml/qqmlfile.h>
|
||||
#include <QtQml/qqmlpropertymap.h>
|
||||
|
@ -535,13 +536,11 @@ Heap::Module *ExecutableCompilationUnit::instantiate(ExecutionEngine *engine)
|
|||
|
||||
for (const QString &request: moduleRequests()) {
|
||||
const QUrl url(request);
|
||||
if (engine->nativeModules.contains(url))
|
||||
continue;
|
||||
|
||||
auto dependentModuleUnit = engine->loadModule(url, this);
|
||||
const auto dependentModuleUnit = engine->loadModule(url, this);
|
||||
if (engine->hasException)
|
||||
return nullptr;
|
||||
dependentModuleUnit->instantiate(engine);
|
||||
if (dependentModuleUnit.compiled)
|
||||
dependentModuleUnit.compiled->instantiate(engine);
|
||||
}
|
||||
|
||||
ScopedString importName(scope);
|
||||
|
@ -554,12 +553,22 @@ Heap::Module *ExecutableCompilationUnit::instantiate(ExecutionEngine *engine)
|
|||
for (uint i = 0; i < importCount; ++i) {
|
||||
const CompiledData::ImportEntry &entry = data->importEntryTable()[i];
|
||||
QUrl url = urlAt(entry.moduleRequest);
|
||||
const auto nativeModule = engine->nativeModules.find(url);
|
||||
if (nativeModule != engine->nativeModules.end()) {
|
||||
importName = runtimeStrings[entry.importName];
|
||||
const QString name = importName->toQString();
|
||||
importName = runtimeStrings[entry.importName];
|
||||
|
||||
QV4::Value *value = nativeModule.value();
|
||||
const auto module = engine->loadModule(url, this);
|
||||
if (module.compiled) {
|
||||
const Value *valuePtr = module.compiled->resolveExport(importName);
|
||||
if (!valuePtr) {
|
||||
QString referenceErrorMessage = QStringLiteral("Unable to resolve import reference ");
|
||||
referenceErrorMessage += importName->toQString();
|
||||
engine->throwReferenceError(
|
||||
referenceErrorMessage, fileName(),
|
||||
entry.location.line(), entry.location.column());
|
||||
return nullptr;
|
||||
}
|
||||
imports[i] = valuePtr;
|
||||
} else if (Value *value = module.native) {
|
||||
const QString name = importName->toQString();
|
||||
if (value->isNullOrUndefined()) {
|
||||
QString errorMessage = name;
|
||||
errorMessage += QStringLiteral(" from ");
|
||||
|
@ -573,9 +582,9 @@ Heap::Module *ExecutableCompilationUnit::instantiate(ExecutionEngine *engine)
|
|||
imports[i] = value;
|
||||
} else {
|
||||
url.setFragment(name);
|
||||
auto fragment = engine->nativeModules.find(url);
|
||||
if (fragment != engine->nativeModules.end()) {
|
||||
imports[i] = fragment.value();
|
||||
const auto fragment = engine->moduleForUrl(url, this);
|
||||
if (fragment.native) {
|
||||
imports[i] = fragment.native;
|
||||
} else {
|
||||
Scope scope(this->engine);
|
||||
ScopedObject o(scope, value);
|
||||
|
@ -593,42 +602,37 @@ Heap::Module *ExecutableCompilationUnit::instantiate(ExecutionEngine *engine)
|
|||
|
||||
const ScopedPropertyKey key(scope, scope.engine->identifierTable->asPropertyKey(name));
|
||||
const ScopedValue result(scope, o->get(key));
|
||||
Value *valuePtr = engine->memoryManager->m_persistentValues->allocate();
|
||||
*valuePtr = result->asReturnedValue();
|
||||
imports[i] = valuePtr;
|
||||
engine->nativeModules.insert(url, valuePtr);
|
||||
imports[i] = engine->registerNativeModule(url, result);
|
||||
}
|
||||
}
|
||||
} else {
|
||||
auto dependentModuleUnit = engine->loadModule(url, this);
|
||||
importName = runtimeStrings[entry.importName];
|
||||
const Value *valuePtr = dependentModuleUnit->resolveExport(importName);
|
||||
if (!valuePtr) {
|
||||
QString referenceErrorMessage = QStringLiteral("Unable to resolve import reference ");
|
||||
referenceErrorMessage += importName->toQString();
|
||||
engine->throwReferenceError(
|
||||
referenceErrorMessage, fileName(),
|
||||
entry.location.line(), entry.location.column());
|
||||
return nullptr;
|
||||
}
|
||||
imports[i] = valuePtr;
|
||||
}
|
||||
}
|
||||
|
||||
const auto throwReferenceError = [&](const CompiledData::ExportEntry &entry, const QString &importName) {
|
||||
QString referenceErrorMessage = QStringLiteral("Unable to resolve re-export reference ");
|
||||
referenceErrorMessage += importName;
|
||||
engine->throwReferenceError(
|
||||
referenceErrorMessage, fileName(),
|
||||
entry.location.line(), entry.location.column());
|
||||
};
|
||||
|
||||
for (uint i = 0; i < data->indirectExportEntryTableSize; ++i) {
|
||||
const CompiledData::ExportEntry &entry = data->indirectExportEntryTable()[i];
|
||||
auto dependentModuleUnit = engine->loadModule(urlAt(entry.moduleRequest), this);
|
||||
if (!dependentModuleUnit)
|
||||
return nullptr;
|
||||
|
||||
auto dependentModule = engine->loadModule(urlAt(entry.moduleRequest), this);
|
||||
ScopedString importName(scope, runtimeStrings[entry.importName]);
|
||||
if (!dependentModuleUnit->resolveExport(importName)) {
|
||||
QString referenceErrorMessage = QStringLiteral("Unable to resolve re-export reference ");
|
||||
referenceErrorMessage += importName->toQString();
|
||||
engine->throwReferenceError(
|
||||
referenceErrorMessage, fileName(),
|
||||
entry.location.line(), entry.location.column());
|
||||
return nullptr;
|
||||
if (const auto dependentModuleUnit = dependentModule.compiled) {
|
||||
if (!dependentModuleUnit->resolveExport(importName)) {
|
||||
throwReferenceError(entry, importName->toQString());
|
||||
return nullptr;
|
||||
}
|
||||
} else if (const auto native = dependentModule.native) {
|
||||
ScopedObject o(scope, native);
|
||||
const ScopedPropertyKey key(scope, scope.engine->identifierTable->asPropertyKey(importName));
|
||||
const ScopedValue result(scope, o->get(key));
|
||||
if (result->isUndefined()) {
|
||||
throwReferenceError(entry, importName->toQString());
|
||||
return nullptr;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -665,13 +669,31 @@ const Value *ExecutableCompilationUnit::resolveExportRecursively(
|
|||
|
||||
if (auto indirectExport = lookupNameInExportTable(
|
||||
data->indirectExportEntryTable(), data->indirectExportEntryTableSize, exportName)) {
|
||||
auto dependentModuleUnit = engine->loadModule(urlAt(indirectExport->moduleRequest), this);
|
||||
if (!dependentModuleUnit)
|
||||
return nullptr;
|
||||
QUrl request = urlAt(indirectExport->moduleRequest);
|
||||
auto dependentModule = engine->loadModule(request, this);
|
||||
ScopedString importName(scope, runtimeStrings[indirectExport->importName]);
|
||||
return dependentModuleUnit->resolveExportRecursively(importName, resolveSet);
|
||||
}
|
||||
if (dependentModule.compiled) {
|
||||
return dependentModule.compiled->resolveExportRecursively(importName, resolveSet);
|
||||
} else if (dependentModule.native) {
|
||||
if (exportName->toQString() == QLatin1String("*"))
|
||||
return dependentModule.native;
|
||||
if (exportName->toQString() == QLatin1String("default"))
|
||||
return nullptr;
|
||||
|
||||
request.setFragment(importName->toQString());
|
||||
const auto fragment = engine->moduleForUrl(request);
|
||||
if (fragment.native)
|
||||
return fragment.native;
|
||||
|
||||
ScopedObject o(scope, dependentModule.native);
|
||||
if (o)
|
||||
return engine->registerNativeModule(request, o->get(importName));
|
||||
|
||||
return nullptr;
|
||||
} else {
|
||||
return nullptr;
|
||||
}
|
||||
}
|
||||
|
||||
if (exportName->toQString() == QLatin1String("default"))
|
||||
return nullptr;
|
||||
|
@ -680,11 +702,28 @@ const Value *ExecutableCompilationUnit::resolveExportRecursively(
|
|||
|
||||
for (uint i = 0; i < data->starExportEntryTableSize; ++i) {
|
||||
const CompiledData::ExportEntry &entry = data->starExportEntryTable()[i];
|
||||
auto dependentModuleUnit = engine->loadModule(urlAt(entry.moduleRequest), this);
|
||||
if (!dependentModuleUnit)
|
||||
return nullptr;
|
||||
QUrl request = urlAt(entry.moduleRequest);
|
||||
auto dependentModule = engine->loadModule(request, this);
|
||||
const Value *resolution = nullptr;
|
||||
if (dependentModule.compiled) {
|
||||
resolution = dependentModule.compiled->resolveExportRecursively(
|
||||
exportName, resolveSet);
|
||||
} else if (dependentModule.native) {
|
||||
if (exportName->toQString() == QLatin1String("*")) {
|
||||
resolution = dependentModule.native;
|
||||
} else if (exportName->toQString() != QLatin1String("default")) {
|
||||
request.setFragment(exportName->toQString());
|
||||
const auto fragment = engine->moduleForUrl(request);
|
||||
if (fragment.native) {
|
||||
resolution = fragment.native;
|
||||
} else {
|
||||
ScopedObject o(scope, dependentModule.native);
|
||||
if (o)
|
||||
resolution = engine->registerNativeModule(request, o->get(exportName));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
const Value *resolution = dependentModuleUnit->resolveExportRecursively(exportName, resolveSet);
|
||||
// ### handle ambiguous
|
||||
if (resolution) {
|
||||
if (!starResolution) {
|
||||
|
@ -737,10 +776,21 @@ void ExecutableCompilationUnit::getExportedNamesRecursively(
|
|||
|
||||
for (uint i = 0; i < data->starExportEntryTableSize; ++i) {
|
||||
const CompiledData::ExportEntry &entry = data->starExportEntryTable()[i];
|
||||
auto dependentModuleUnit = engine->loadModule(urlAt(entry.moduleRequest), this);
|
||||
if (!dependentModuleUnit)
|
||||
return;
|
||||
dependentModuleUnit->getExportedNamesRecursively(names, exportNameSet, /*includeDefaultExport*/false);
|
||||
auto dependentModule = engine->loadModule(urlAt(entry.moduleRequest), this);
|
||||
if (dependentModule.compiled) {
|
||||
dependentModule.compiled->getExportedNamesRecursively(
|
||||
names, exportNameSet, /*includeDefaultExport*/false);
|
||||
} else if (dependentModule.native) {
|
||||
Scope scope(engine);
|
||||
ScopedObject o(scope, dependentModule.native);
|
||||
ObjectIterator iterator(scope, o, ObjectIterator::EnumerableOnly);
|
||||
while (true) {
|
||||
ScopedValue val(scope, iterator.nextPropertyNameAsString());
|
||||
if (val->isNull())
|
||||
break;
|
||||
append(val->toQString());
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -754,12 +804,15 @@ void ExecutableCompilationUnit::evaluate()
|
|||
void ExecutableCompilationUnit::evaluateModuleRequests()
|
||||
{
|
||||
for (const QString &request: moduleRequests()) {
|
||||
if (engine->nativeModules.contains(QUrl(request)))
|
||||
auto dependentModule = engine->loadModule(QUrl(request), this);
|
||||
if (dependentModule.native)
|
||||
continue;
|
||||
auto dependentModuleUnit = engine->loadModule(QUrl(request), this);
|
||||
|
||||
if (engine->hasException)
|
||||
return;
|
||||
dependentModuleUnit->evaluate();
|
||||
|
||||
Q_ASSERT(dependentModule.compiled);
|
||||
dependentModule.compiled->evaluate();
|
||||
if (engine->hasException)
|
||||
return;
|
||||
}
|
||||
|
|
|
@ -213,10 +213,11 @@ void QQmlScriptBlob::initializeFromCompilationUnit(const QQmlRefPointer<QV4::Exe
|
|||
|
||||
auto *v4 = QQmlEnginePrivate::getV4Engine(typeLoader()->engine());
|
||||
|
||||
v4->injectModule(unit);
|
||||
v4->injectCompiledModule(unit);
|
||||
|
||||
for (const QString &request: unit->moduleRequests()) {
|
||||
if (v4->moduleForUrl(QUrl(request), unit.data()))
|
||||
const auto module = v4->moduleForUrl(QUrl(request), unit.data());
|
||||
if (module.compiled || module.native)
|
||||
continue;
|
||||
|
||||
const QUrl absoluteRequest = unit->finalUrl().resolved(QUrl(request));
|
||||
|
@ -226,4 +227,23 @@ void QQmlScriptBlob::initializeFromCompilationUnit(const QQmlRefPointer<QV4::Exe
|
|||
}
|
||||
}
|
||||
|
||||
/*!
|
||||
\internal
|
||||
|
||||
This initializes a dummy script blob from a "native" ECMAScript module.
|
||||
Native modules are just JavaScript values, possibly objects with members.
|
||||
|
||||
\sa QJSEngine::registerModule()
|
||||
*/
|
||||
void QQmlScriptBlob::initializeFromNative(const QV4::Value &value)
|
||||
{
|
||||
Q_ASSERT(!m_scriptData);
|
||||
m_scriptData.adopt(new QQmlScriptData());
|
||||
m_scriptData->url = finalUrl();
|
||||
m_scriptData->urlString = finalUrlString();
|
||||
m_scriptData->m_loaded = true;
|
||||
m_scriptData->m_value.set(QQmlEnginePrivate::getV4Engine(typeLoader()->engine()), value);
|
||||
m_importCache->setBaseUrl(finalUrl(), finalUrlString());
|
||||
}
|
||||
|
||||
QT_END_NAMESPACE
|
||||
|
|
|
@ -50,6 +50,7 @@ protected:
|
|||
private:
|
||||
void scriptImported(const QQmlRefPointer<QQmlScriptBlob> &blob, const QV4::CompiledData::Location &location, const QString &qualifier, const QString &nameSpace) override;
|
||||
void initializeFromCompilationUnit(const QQmlRefPointer<QV4::ExecutableCompilationUnit> &unit);
|
||||
void initializeFromNative(const QV4::Value &value);
|
||||
|
||||
QList<ScriptReference> m_scripts;
|
||||
QQmlRefPointer<QQmlScriptData> m_scriptData;
|
||||
|
|
|
@ -23,7 +23,7 @@ QQmlRefPointer<QQmlContextData> QQmlScriptData::qmlContextDataForContext(
|
|||
{
|
||||
Q_ASSERT(parentQmlContextData && parentQmlContextData->engine());
|
||||
|
||||
if (m_precompiledScript->isESModule())
|
||||
if (!m_precompiledScript || m_precompiledScript->isESModule())
|
||||
return nullptr;
|
||||
|
||||
QQmlRefPointer<QQmlContextData> qmlContextData = m_precompiledScript->isSharedLibrary()
|
||||
|
|
|
@ -530,8 +530,16 @@ bool QQmlTypeLoader::Blob::updateQmldir(const QQmlRefPointer<QQmlQmldirData> &da
|
|||
|
||||
bool QQmlTypeLoader::Blob::addScriptImport(const QQmlTypeLoader::Blob::PendingImportPtr &import)
|
||||
{
|
||||
QUrl scriptUrl = finalUrl().resolved(QUrl(import->uri));
|
||||
QQmlRefPointer<QQmlScriptBlob> blob = typeLoader()->getScript(scriptUrl);
|
||||
const QUrl url(import->uri);
|
||||
const auto module = m_typeLoader->engine()->handle()->moduleForUrl(url);
|
||||
QQmlRefPointer<QQmlScriptBlob> blob;
|
||||
if (module.native) {
|
||||
blob.adopt(new QQmlScriptBlob(url, m_typeLoader));
|
||||
blob->initializeFromNative(*module.native);
|
||||
blob->tryDone();
|
||||
} else {
|
||||
blob = typeLoader()->getScript(finalUrl().resolved(url));
|
||||
}
|
||||
addDependency(blob.data());
|
||||
scriptImported(blob, import->location, import->qualifier, QString());
|
||||
return true;
|
||||
|
|
|
@ -230,10 +230,12 @@ void QQuickWorkerScriptEnginePrivate::processLoad(int id, const QUrl &url)
|
|||
script->source = url;
|
||||
|
||||
if (fileName.endsWith(QLatin1String(".mjs"))) {
|
||||
auto moduleUnit = engine->loadModule(url);
|
||||
if (moduleUnit) {
|
||||
if (moduleUnit->instantiate(engine))
|
||||
moduleUnit->evaluate();
|
||||
auto module = engine->loadModule(url);
|
||||
if (module.compiled) {
|
||||
if (module.compiled->instantiate(engine))
|
||||
module.compiled->evaluate();
|
||||
} else if (module.native) {
|
||||
// Nothing to do. There is no global code in a native module.
|
||||
} else {
|
||||
engine->throwError(QStringLiteral("Could not load module file"));
|
||||
}
|
||||
|
|
|
@ -503,7 +503,7 @@ static TestCase::Result executeTest(const QByteArray &data, bool runAsModule = f
|
|||
module = vm.compileModule(url.toString(), QString::fromUtf8(content.constData(), content.length()), QFileInfo(f).lastModified());
|
||||
if (vm.hasException)
|
||||
break;
|
||||
vm.injectModule(module);
|
||||
vm.injectCompiledModule(module);
|
||||
} else {
|
||||
vm.throwError(QStringLiteral("Could not load module"));
|
||||
break;
|
||||
|
@ -511,16 +511,16 @@ static TestCase::Result executeTest(const QByteArray &data, bool runAsModule = f
|
|||
|
||||
for (const QString &request: module->moduleRequests()) {
|
||||
const QUrl absoluteRequest = module->finalUrl().resolved(QUrl(request));
|
||||
if (!vm.modules.contains(absoluteRequest))
|
||||
const auto module = vm.moduleForUrl(absoluteRequest);
|
||||
if (module.native == nullptr && module.compiled == nullptr)
|
||||
modulesToLoad << absoluteRequest;
|
||||
}
|
||||
}
|
||||
|
||||
if (!vm.hasException) {
|
||||
if (auto rootModuleUnit = vm.loadModule(rootModuleUrl)) {
|
||||
if (rootModuleUnit->instantiate(&vm))
|
||||
rootModuleUnit->evaluate();
|
||||
}
|
||||
const auto rootModule = vm.loadModule(rootModuleUrl);
|
||||
if (rootModule.compiled && rootModule.compiled->instantiate(&vm))
|
||||
rootModule.compiled->evaluate();
|
||||
}
|
||||
} else {
|
||||
QV4::ScopedContext ctx(scope, vm.rootContext());
|
||||
|
|
|
@ -0,0 +1,16 @@
|
|||
// Copyright (C) 2022 The Qt Company Ltd.
|
||||
// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0
|
||||
|
||||
import { name } from "info.mjs";
|
||||
|
||||
export function getName()
|
||||
{
|
||||
return name;
|
||||
}
|
||||
|
||||
export function func() {
|
||||
return "Hello World"
|
||||
}
|
||||
|
||||
export {name as foo} from "info.mjs";
|
||||
export * from "info.mjs";
|
|
@ -0,0 +1,15 @@
|
|||
// Copyright (C) 2022 The Qt Company Ltd.
|
||||
// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0
|
||||
|
||||
import QtQml
|
||||
|
||||
import "info.mjs" as Info
|
||||
import "nativeModuleImport.mjs" as Test
|
||||
|
||||
QtObject {
|
||||
property string a: Test.func()
|
||||
property string b: Test.getName()
|
||||
property string c: Info.name
|
||||
property string d: Test.name
|
||||
property string e: Test.foo
|
||||
}
|
|
@ -70,6 +70,7 @@ private slots:
|
|||
void stringToColor();
|
||||
void qobjectToString();
|
||||
void qtNamespaceInQtObject();
|
||||
void nativeModuleImport();
|
||||
|
||||
public slots:
|
||||
QObject *createAQObjectForOwnershipTest ()
|
||||
|
@ -1615,6 +1616,27 @@ void tst_qqmlengine::qtNamespaceInQtObject()
|
|||
QVERIFY(qtObject.hasProperty(QStringLiteral("objectName")));
|
||||
}
|
||||
|
||||
void tst_qqmlengine::nativeModuleImport()
|
||||
{
|
||||
QQmlEngine engine;
|
||||
|
||||
QJSValue name("TheName");
|
||||
QJSValue obj = engine.newObject();
|
||||
obj.setProperty("name", name);
|
||||
engine.registerModule("info.mjs", obj);
|
||||
|
||||
QQmlComponent c(&engine, testFileUrl("nativeModuleImport.qml"));
|
||||
QVERIFY2(c.isReady(), qPrintable(c.errorString()));
|
||||
|
||||
QScopedPointer<QObject> o(c.create());
|
||||
|
||||
QCOMPARE(o->property("a").toString(), QStringLiteral("Hello World"));
|
||||
QCOMPARE(o->property("b").toString(), QStringLiteral("TheName"));
|
||||
QCOMPARE(o->property("c").toString(), QStringLiteral("TheName"));
|
||||
QCOMPARE(o->property("d").toString(), QStringLiteral("TheName"));
|
||||
QCOMPARE(o->property("e").toString(), QStringLiteral("TheName"));
|
||||
}
|
||||
|
||||
QTEST_MAIN(tst_qqmlengine)
|
||||
|
||||
#include "tst_qqmlengine.moc"
|
||||
|
|
|
@ -97,10 +97,12 @@ int main(int argc, char *argv[])
|
|||
for (const QString &fn : qAsConst(args)) {
|
||||
QV4::ScopedValue result(scope);
|
||||
if (runAsModule) {
|
||||
auto moduleUnit = vm.loadModule(QUrl::fromLocalFile(QFileInfo(fn).absoluteFilePath()));
|
||||
if (moduleUnit) {
|
||||
if (moduleUnit->instantiate(&vm))
|
||||
moduleUnit->evaluate();
|
||||
auto module = vm.loadModule(QUrl::fromLocalFile(QFileInfo(fn).absoluteFilePath()));
|
||||
if (module.compiled) {
|
||||
if (module.compiled->instantiate(&vm))
|
||||
module.compiled->evaluate();
|
||||
} else if (module.native) {
|
||||
// Nothing to do. Native modules have no global code.
|
||||
} else {
|
||||
vm.throwError(QStringLiteral("Could not load module file"));
|
||||
}
|
||||
|
|
Loading…
Reference in New Issue