diff options
Diffstat (limited to 'src/qmlcompiler/qqmljsimporter.cpp')
-rw-r--r-- | src/qmlcompiler/qqmljsimporter.cpp | 174 |
1 files changed, 125 insertions, 49 deletions
diff --git a/src/qmlcompiler/qqmljsimporter.cpp b/src/qmlcompiler/qqmljsimporter.cpp index 2b42941039..bcc8e98434 100644 --- a/src/qmlcompiler/qqmljsimporter.cpp +++ b/src/qmlcompiler/qqmljsimporter.cpp @@ -16,8 +16,24 @@ QT_BEGIN_NAMESPACE using namespace Qt::StringLiterals; -static const QLatin1String SlashQmldir = QLatin1String("/qmldir"); -static const QLatin1String SlashPluginsDotQmltypes = QLatin1String("/plugins.qmltypes"); +static const QLatin1String SlashQmldir = QLatin1String("/qmldir"); +static const QLatin1String PluginsDotQmltypes = QLatin1String("plugins.qmltypes"); + + +QQmlJS::Import::Import(QString prefix, QString name, QTypeRevision version, bool isFile, + bool isDependency) : + m_prefix(std::move(prefix)), + m_name(std::move(name)), + m_version(version), + m_isFile(isFile), + m_isDependency(isDependency) +{ +} + +bool QQmlJS::Import::isValid() const +{ + return !m_name.isEmpty(); +} static const QString prefixedName(const QString &prefix, const QString &name) { @@ -25,12 +41,21 @@ static const QString prefixedName(const QString &prefix, const QString &name) return prefix.isEmpty() ? name : (prefix + QLatin1Char('.') + name); } -static QQmlDirParser createQmldirParserForFile(const QString &filename) +QQmlDirParser QQmlJSImporter::createQmldirParserForFile(const QString &filename) { QFile f(filename); - f.open(QFile::ReadOnly); QQmlDirParser parser; - parser.parse(QString::fromUtf8(f.readAll())); + if (f.open(QFile::ReadOnly)) { + parser.parse(QString::fromUtf8(f.readAll())); + } else { + m_warnings.append({ + QStringLiteral("Could not open qmldir file: ") + + filename, + QtWarningMsg, + QQmlJS::SourceLocation() + }); + } + return parser; } @@ -129,24 +154,79 @@ static bool isComposite(const QQmlJSScope::ConstPtr &scope) return scope.factory() || scope->isComposite(); } +static QStringList aliases(const QQmlJSScope::ConstPtr &scope) +{ + return isComposite(scope) + ? QStringList() + : scope->aliases(); +} + QQmlJSImporter::QQmlJSImporter(const QStringList &importPaths, QQmlJSResourceFileMapper *mapper, bool useOptionalImports) : m_importPaths(importPaths), m_mapper(mapper), m_useOptionalImports(useOptionalImports), - m_createImportVisitor([](const QQmlJSScope::Ptr &target, QQmlJSImporter *importer, - QQmlJSLogger *logger, const QString &implicitImportDirectory, - const QStringList &qmldirFiles) { - return new QQmlJSImportVisitor(target, importer, logger, implicitImportDirectory, - qmldirFiles); + m_importVisitor([](QQmlJS::AST::Node *rootNode, QQmlJSImporter *self, + const ImportVisitorPrerequisites &p) { + auto visitor = std::unique_ptr<QQmlJS::AST::BaseVisitor>(new QQmlJSImportVisitor( + p.m_target, self, p.m_logger, p.m_implicitImportDirectory, p.m_qmldirFiles)); + QQmlJS::AST::Node::accept(rootNode, visitor.get()); }) { } -QQmlJSImporter::Import QQmlJSImporter::readQmldir(const QString &path) +static QString resolvePreferredPath( + const QString &qmldirPath, const QString &prefer, QQmlJSResourceFileMapper *mapper) { + if (prefer.isEmpty()) + return qmldirPath; + + if (!prefer.endsWith(u'/')) { + qWarning() << "Ignoring invalid prefer path" << prefer << "(has to end with slash)"; + return qmldirPath; + } + + if (prefer.startsWith(u':')) { + // Resource path: Resolve via resource file mapper if possible. + if (!mapper) + return qmldirPath; + + Q_ASSERT(prefer.endsWith(u'/')); + const auto entry = mapper->entry( + QQmlJSResourceFileMapper::resourceFileFilter(prefer.mid(1) + SlashQmldir.mid(1))); + + // This can be empty if the .qrc files does not belong to this module. + // In that case we trust the given qmldir file. + return entry.filePath.endsWith(SlashQmldir) + ? entry.filePath + : qmldirPath; + } + + // Host file system path. This should be rare. We don't generate it. + const QFileInfo f(prefer + SlashQmldir); + const QString canonical = f.canonicalFilePath(); + if (canonical.isEmpty()) { + qWarning() << "No qmldir at" << prefer; + return qmldirPath; + } + return canonical; +} + +QQmlJSImporter::Import QQmlJSImporter::readQmldir(const QString &modulePath) +{ + const QString moduleQmldirPath = modulePath + SlashQmldir; + auto reader = createQmldirParserForFile(moduleQmldirPath); + + const QString resolvedQmldirPath + = resolvePreferredPath(moduleQmldirPath, reader.preferredPath(), m_mapper); + if (resolvedQmldirPath != moduleQmldirPath) + reader = createQmldirParserForFile(resolvedQmldirPath); + + // Leave the trailing slash + Q_ASSERT(resolvedQmldirPath.endsWith(SlashQmldir)); + QStringView resolvedPath = QStringView(resolvedQmldirPath).chopped(SlashQmldir.size() - 1); + Import result; - auto reader = createQmldirParserForFile(path + SlashQmldir); result.name = reader.typeNamespace(); result.isStaticModule = reader.isStaticModule(); @@ -157,12 +237,13 @@ QQmlJSImporter::Import QQmlJSImporter::readQmldir(const QString &path) const auto typeInfos = reader.typeInfos(); for (const auto &typeInfo : typeInfos) { const QString typeInfoPath = QFileInfo(typeInfo).isRelative() - ? path + u'/' + typeInfo : typeInfo; + ? resolvedPath + typeInfo + : typeInfo; readQmltypes(typeInfoPath, &result.objects, &result.dependencies); } if (typeInfos.isEmpty() && !reader.plugins().isEmpty()) { - const QString defaultTypeInfoPath = path + SlashPluginsDotQmltypes; + const QString defaultTypeInfoPath = resolvedPath + PluginsDotQmltypes; if (QFile::exists(defaultTypeInfoPath)) { m_warnings.append({ QStringLiteral("typeinfo not declared in qmldir file: ") @@ -177,11 +258,11 @@ QQmlJSImporter::Import QQmlJSImporter::readQmldir(const QString &path) QHash<QString, QQmlJSExportedScope> qmlComponents; const auto components = reader.components(); for (auto it = components.begin(), end = components.end(); it != end; ++it) { - const QString filePath = path + QLatin1Char('/') + it->fileName; + const QString filePath = resolvedPath + it->fileName; if (!QFile::exists(filePath)) { m_warnings.append({ it->fileName + QStringLiteral(" is listed as component in ") - + path + SlashQmldir + + resolvedQmldirPath + QStringLiteral(" but does not exist.\n"), QtWarningMsg, QQmlJS::SourceLocation() @@ -208,7 +289,7 @@ QQmlJSImporter::Import QQmlJSImporter::readQmldir(const QString &path) const auto scripts = reader.scripts(); for (const auto &script : scripts) { - const QString filePath = path + QLatin1Char('/') + script.fileName; + const QString filePath = resolvedPath + script.fileName; auto mo = result.scripts.find(script.fileName); if (mo == result.scripts.end()) mo = result.scripts.insert(script.fileName, { localFile2ScopeTree(filePath), {} }); @@ -308,7 +389,7 @@ void QQmlJSImporter::importDependencies(const QQmlJSImporter::Import &import, } static bool isVersionAllowed(const QQmlJSScope::Export &exportEntry, - const QQmlJSScope::Import &importDescription) + const QQmlJS::Import &importDescription) { const QTypeRevision importVersion = importDescription.version(); const QTypeRevision exportVersion = exportEntry.version(); @@ -320,7 +401,7 @@ static bool isVersionAllowed(const QQmlJSScope::Export &exportEntry, || exportVersion.minorVersion() <= importVersion.minorVersion(); } -void QQmlJSImporter::processImport(const QQmlJSScope::Import &importDescription, +void QQmlJSImporter::processImport(const QQmlJS::Import &importDescription, const QQmlJSImporter::Import &import, QQmlJSImporter::AvailableTypes *types) { @@ -333,6 +414,12 @@ void QQmlJSImporter::processImport(const QQmlJSScope::Import &importDescription, const QString modulePrefix = QStringLiteral("$module$"); QHash<QString, QList<QQmlJSScope::Export>> seenExports; + const auto insertAliases = [&](const QQmlJSScope::ConstPtr &scope, QTypeRevision revision) { + const QStringList cppAliases = aliases(scope); + for (const QString &alias : cppAliases) + types->cppNames.setType(alias, { scope, revision }); + }; + const auto insertExports = [&](const QQmlJSExportedScope &val, const QString &cppName) { QQmlJSScope::Export bestExport; @@ -405,6 +492,8 @@ void QQmlJSImporter::processImport(const QQmlJSScope::Import &importDescription, : QTypeRevision::zero(); types->cppNames.setType(cppName, { val.scope, bestRevision }); + insertAliases(val.scope, bestRevision); + const QTypeRevision bestVersion = bestExport.isValid() ? bestExport.version() : QTypeRevision::zero(); @@ -444,6 +533,7 @@ void QQmlJSImporter::processImport(const QQmlJSScope::Import &importDescription, types->qmlNames.setType( prefixedName(internalPrefix, cppName), { val.scope, QTypeRevision() }); types->cppNames.setType(cppName, { val.scope, QTypeRevision() }); + insertAliases(val.scope, QTypeRevision()); } else { insertExports(val, cppName); } @@ -478,11 +568,10 @@ void QQmlJSImporter::processImport(const QQmlJSScope::Import &importDescription, // only happen when enumerations are involved, thus the strategy is to // resolve enumerations (which can potentially create new child scopes) // before resolving the type fully - const QQmlJSScope::ConstPtr intType = tempTypes.cppNames.type(u"int"_s).scope; const QQmlJSScope::ConstPtr arrayType = tempTypes.cppNames.type(u"Array"_s).scope; for (auto it = import.objects.begin(); it != import.objects.end(); ++it) { if (!it->scope.factory()) { - QQmlJSScope::resolveEnums(it->scope, intType); + QQmlJSScope::resolveEnums(it->scope, tempTypes.cppNames); QQmlJSScope::resolveList(it->scope, arrayType); } } @@ -522,7 +611,7 @@ QQmlJSImporter::AvailableTypes QQmlJSImporter::builtinImportHelper() if (m_builtins) return *m_builtins; - AvailableTypes builtins(ImportedTypes(ImportedTypes::INTERNAL, {}, {}, {})); + AvailableTypes builtins(ImportedTypes(ImportedTypes::INTERNAL, {}, {})); Import result; result.name = QStringLiteral("QML"); @@ -531,7 +620,7 @@ QQmlJSImporter::AvailableTypes QQmlJSImporter::builtinImportHelper() QStringLiteral("jsroot.qmltypes") }; const auto importBuiltins = [&](const QStringList &imports) { for (auto const &dir : imports) { - QDirIterator it { dir, qmltypesFiles, QDir::NoFilter, QDirIterator::Subdirectories }; + QDirIterator it { dir, qmltypesFiles, QDir::NoFilter }; while (it.hasNext() && !qmltypesFiles.isEmpty()) { readQmltypes(it.next(), &result.objects, &result.dependencies); qmltypesFiles.removeOne(it.fileName()); @@ -558,7 +647,7 @@ QQmlJSImporter::AvailableTypes QQmlJSImporter::builtinImportHelper() // Process them together since there they have interdependencies that wouldn't get resolved // otherwise - const QQmlJSScope::Import builtinImport( + const QQmlJS::Import builtinImport( QString(), QStringLiteral("QML"), QTypeRevision::fromVersion(1, 0), false, true); QQmlJSScope::ConstPtr intType; @@ -579,12 +668,10 @@ QQmlJSImporter::AvailableTypes QQmlJSImporter::builtinImportHelper() Q_ASSERT(intType); Q_ASSERT(arrayType); - m_builtins = AvailableTypes(ImportedTypes( - ImportedTypes::INTERNAL, builtins.cppNames.types(), - intType, arrayType)); - m_builtins->qmlNames = - ImportedTypes(ImportedTypes::QML, builtins.qmlNames.types(), - intType, arrayType); + m_builtins = AvailableTypes( + ImportedTypes(ImportedTypes::INTERNAL, builtins.cppNames.types(), arrayType)); + m_builtins->qmlNames + = ImportedTypes(ImportedTypes::QML, builtins.qmlNames.types(), arrayType); processImport(builtinImport, result, &(*m_builtins)); @@ -601,6 +688,7 @@ void QQmlJSImporter::importQmldirs(const QStringList &qmldirFiles) QString qmldirName; if (file.endsWith(SlashQmldir)) { result = readQmldir(file.chopped(SlashQmldir.size())); + setQualifiedNamesOn(result); qmldirName = file; } else { m_warnings.append({ @@ -674,7 +762,7 @@ bool QQmlJSImporter::importHelper(const QString &module, AvailableTypes *types, if (isDependency) Q_ASSERT(prefix.isEmpty()); - const QQmlJSScope::Import cacheKey(prefix, moduleCacheName, version, isFile, isDependency); + const QQmlJS::Import cacheKey(prefix, moduleCacheName, version, isFile, isDependency); auto getTypesFromCache = [&]() -> bool { if (!m_cachedImportTypes.contains(cacheKey)) @@ -703,9 +791,7 @@ bool QQmlJSImporter::importHelper(const QString &module, AvailableTypes *types, auto cacheTypes = QSharedPointer<QQmlJSImporter::AvailableTypes>( new QQmlJSImporter::AvailableTypes( - ImportedTypes( - ImportedTypes::INTERNAL, {}, - types->cppNames.intType(), types->cppNames.arrayType()))); + ImportedTypes(ImportedTypes::INTERNAL, {}, types->cppNames.arrayType()))); m_cachedImportTypes[cacheKey] = cacheTypes; const QPair<QString, QTypeRevision> importId { module, version }; @@ -822,8 +908,7 @@ QQmlJSImporter::ImportedTypes QQmlJSImporter::importDirectory( const AvailableTypes builtins = builtinImportHelper(); QQmlJSImporter::AvailableTypes types( ImportedTypes( - ImportedTypes::INTERNAL, {}, - builtins.cppNames.intType(), builtins.cppNames.arrayType())); + ImportedTypes::INTERNAL, {}, builtins.cppNames.arrayType())); importHelper(directory, &types, prefix, QTypeRevision(), false, true); return types.qmlNames; } @@ -857,27 +942,18 @@ void QQmlJSImporter::setQualifiedNamesOn(const Import &import) for (auto &object : import.objects) { if (object.exports.isEmpty()) continue; - const QString qualifiedName = QQmlJSScope::qualifiedNameFrom( - import.name, object.exports.first().type(), - object.exports.first().revision(), - object.exports.last().revision()); if (auto *factory = object.scope.factory()) { - factory->setQualifiedName(qualifiedName); factory->setModuleName(import.name); } else { - object.scope->setQualifiedName(qualifiedName); - object.scope->setModuleName(import.name); + object.scope->setOwnModuleName(import.name); } } } -std::unique_ptr<QQmlJSImportVisitor> -QQmlJSImporter::makeImportVisitor(const QQmlJSScope::Ptr &target, QQmlJSImporter *importer, - QQmlJSLogger *logger, const QString &implicitImportDirectory, - const QStringList &qmldirFiles) +void QQmlJSImporter::runImportVisitor(QQmlJS::AST::Node *rootNode, + const ImportVisitorPrerequisites &p) { - return std::unique_ptr<QQmlJSImportVisitor>( - m_createImportVisitor(target, importer, logger, implicitImportDirectory, qmldirFiles)); + m_importVisitor(rootNode, this, p); } QT_END_NAMESPACE |