aboutsummaryrefslogtreecommitdiffstats
path: root/src/qmlcompiler/qqmljsimporter.cpp
diff options
context:
space:
mode:
Diffstat (limited to 'src/qmlcompiler/qqmljsimporter.cpp')
-rw-r--r--src/qmlcompiler/qqmljsimporter.cpp174
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