aboutsummaryrefslogtreecommitdiffstats
path: root/src/qmldom/qqmldomtop.cpp
diff options
context:
space:
mode:
Diffstat (limited to 'src/qmldom/qqmldomtop.cpp')
-rw-r--r--src/qmldom/qqmldomtop.cpp1626
1 files changed, 803 insertions, 823 deletions
diff --git a/src/qmldom/qqmldomtop.cpp b/src/qmldom/qqmldomtop.cpp
index 4e82a01933..a6ecc04fee 100644
--- a/src/qmldom/qqmldomtop.cpp
+++ b/src/qmldom/qqmldomtop.cpp
@@ -1,6 +1,7 @@
// Copyright (C) 2020 The Qt Company Ltd.
// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only
+#include "qqmldomitem_p.h"
#include "qqmldomtop_p.h"
#include "qqmldomexternalitems_p.h"
#include "qqmldommock_p.h"
@@ -8,6 +9,7 @@
#include "qqmldomastcreator_p.h"
#include "qqmldommoduleindex_p.h"
#include "qqmldomtypesreader_p.h"
+#include "qqmldom_utils_p.h"
#include <QtQml/private/qqmljslexer_p.h>
#include <QtQml/private/qqmljsparser_p.h>
@@ -53,21 +55,21 @@ using std::shared_ptr;
if force is true the file is always read
*/
-Path DomTop::canonicalPath(DomItem &) const
+Path DomTop::canonicalPath(const DomItem &) const
{
return canonicalPath();
}
-DomItem DomTop::containingObject(DomItem &) const
+DomItem DomTop::containingObject(const DomItem &) const
{
return DomItem();
}
-bool DomTop::iterateDirectSubpaths(DomItem &self, DirectVisitor visitor)
+bool DomTop::iterateDirectSubpaths(const DomItem &self, DirectVisitor visitor) const
{
static QHash<QString, QString> knownFields;
static QBasicMutex m;
- auto toField = [](QString f) mutable -> QStringView {
+ auto toField = [](const QString &f) mutable -> QStringView {
QMutexLocker l(&m);
if (!knownFields.contains(f))
knownFields[f] = f;
@@ -119,11 +121,10 @@ ErrorGroups DomUniverse::myErrors()
return groups;
}
-DomUniverse::DomUniverse(QString universeName, Options options):
- m_name(universeName), m_options(options)
-{}
+DomUniverse::DomUniverse(const QString &universeName) : m_name(universeName) { }
-std::shared_ptr<DomUniverse> DomUniverse::guaranteeUniverse(std::shared_ptr<DomUniverse> univ)
+std::shared_ptr<DomUniverse> DomUniverse::guaranteeUniverse(
+ const std::shared_ptr<DomUniverse> &univ)
{
const auto next = [] {
Q_CONSTINIT static std::atomic<int> counter(0);
@@ -136,9 +137,9 @@ std::shared_ptr<DomUniverse> DomUniverse::guaranteeUniverse(std::shared_ptr<DomU
QLatin1String("universe") + QString::number(next()));
}
-DomItem DomUniverse::create(QString universeName, Options options)
+DomItem DomUniverse::create(const QString &universeName)
{
- auto res = std::make_shared<DomUniverse>(universeName, options);
+ auto res = std::make_shared<DomUniverse>(universeName);
return DomItem(res);
}
@@ -147,66 +148,51 @@ Path DomUniverse::canonicalPath() const
return Path::Root(u"universe");
}
-bool DomUniverse::iterateDirectSubpaths(DomItem &self, DirectVisitor visitor)
+bool DomUniverse::iterateDirectSubpaths(const DomItem &self, DirectVisitor visitor) const
{
bool cont = true;
cont = cont && DomTop::iterateDirectSubpaths(self, visitor);
cont = cont && self.dvValueField(visitor, Fields::name, name());
- cont = cont && self.dvValueField(visitor, Fields::options, int(options()));
cont = cont && self.dvItemField(visitor, Fields::globalScopeWithName, [this, &self]() {
return self.subMapItem(Map(
Path::Field(Fields::globalScopeWithName),
- [this](DomItem &map, QString key) { return map.copy(globalScopeWithName(key)); },
- [this](DomItem &) { return globalScopeNames(); }, QLatin1String("GlobalScope")));
+ [this](const DomItem &map, const QString &key) { return map.copy(globalScopeWithName(key)); },
+ [this](const DomItem &) { return globalScopeNames(); }, QLatin1String("GlobalScope")));
});
cont = cont && self.dvItemField(visitor, Fields::qmlDirectoryWithPath, [this, &self]() {
return self.subMapItem(Map(
Path::Field(Fields::qmlDirectoryWithPath),
- [this](DomItem &map, QString key) { return map.copy(qmlDirectoryWithPath(key)); },
- [this](DomItem &) { return qmlDirectoryPaths(); }, QLatin1String("QmlDirectory")));
+ [this](const DomItem &map, const QString &key) { return map.copy(qmlDirectoryWithPath(key)); },
+ [this](const DomItem &) { return qmlDirectoryPaths(); }, QLatin1String("QmlDirectory")));
});
cont = cont && self.dvItemField(visitor, Fields::qmldirFileWithPath, [this, &self]() {
return self.subMapItem(Map(
Path::Field(Fields::qmldirFileWithPath),
- [this](DomItem &map, QString key) { return map.copy(qmldirFileWithPath(key)); },
- [this](DomItem &) { return qmldirFilePaths(); }, QLatin1String("QmldirFile")));
+ [this](const DomItem &map, const QString &key) { return map.copy(qmldirFileWithPath(key)); },
+ [this](const DomItem &) { return qmldirFilePaths(); }, QLatin1String("QmldirFile")));
});
cont = cont && self.dvItemField(visitor, Fields::qmlFileWithPath, [this, &self]() {
return self.subMapItem(Map(
Path::Field(Fields::qmlFileWithPath),
- [this](DomItem &map, QString key) { return map.copy(qmlFileWithPath(key)); },
- [this](DomItem &) { return qmlFilePaths(); }, QLatin1String("QmlFile")));
+ [this](const DomItem &map, const QString &key) { return map.copy(qmlFileWithPath(key)); },
+ [this](const DomItem &) { return qmlFilePaths(); }, QLatin1String("QmlFile")));
});
cont = cont && self.dvItemField(visitor, Fields::jsFileWithPath, [this, &self]() {
return self.subMapItem(Map(
Path::Field(Fields::jsFileWithPath),
- [this](DomItem &map, QString key) { return map.copy(jsFileWithPath(key)); },
- [this](DomItem &) { return jsFilePaths(); }, QLatin1String("JsFile")));
+ [this](const DomItem &map, const QString &key) { return map.copy(jsFileWithPath(key)); },
+ [this](const DomItem &) { return jsFilePaths(); }, QLatin1String("JsFile")));
});
cont = cont && self.dvItemField(visitor, Fields::jsFileWithPath, [this, &self]() {
return self.subMapItem(Map(
Path::Field(Fields::qmltypesFileWithPath),
- [this](DomItem &map, QString key) { return map.copy(qmltypesFileWithPath(key)); },
- [this](DomItem &) { return qmltypesFilePaths(); }, QLatin1String("QmltypesFile")));
- });
- cont = cont && self.dvItemField(visitor, Fields::queue, [this, &self]() {
- QQueue<ParsingTask> q = queue();
- return self.subListItem(List(
- Path::Field(Fields::queue),
- [q](DomItem &list, index_type i) {
- if (i >= 0 && i < q.size())
- return list.subDataItem(PathEls::Index(i), q.at(i).toCbor(),
- ConstantData::Options::FirstMapIsFields);
- else
- return DomItem();
- },
- [q](DomItem &) { return index_type(q.size()); }, nullptr,
- QLatin1String("ParsingTask")));
+ [this](const DomItem &map, const QString &key) { return map.copy(qmltypesFileWithPath(key)); },
+ [this](const DomItem &) { return qmltypesFilePaths(); }, QLatin1String("QmltypesFile")));
});
return cont;
}
-std::shared_ptr<OwningItem> DomUniverse::doCopy(DomItem &) const
+std::shared_ptr<OwningItem> DomUniverse::doCopy(const DomItem &) const
{
QRegularExpression r(QRegularExpression::anchoredPattern(QLatin1String(R"(.*Copy([0-9]*)$)")));
auto m = r.match(m_name);
@@ -219,14 +205,7 @@ std::shared_ptr<OwningItem> DomUniverse::doCopy(DomItem &) const
return res;
}
-void DomUniverse::loadFile(DomItem &self, QString filePath, QString logicalPath, Callback callback,
- LoadOptions loadOptions, std::optional<DomType> fileType)
-{
- loadFile(self, filePath, logicalPath, QString(), QDateTime::fromMSecsSinceEpoch(0, QTimeZone::UTC),
- callback, loadOptions, fileType);
-}
-
-static DomType fileTypeForPath(DomItem &self, QString canonicalFilePath)
+static DomType fileTypeForPath(const DomItem &self, const QString &canonicalFilePath)
{
if (canonicalFilePath.endsWith(u".qml", Qt::CaseInsensitive)
|| canonicalFilePath.endsWith(u".qmlannotation", Qt::CaseInsensitive)) {
@@ -236,12 +215,16 @@ static DomType fileTypeForPath(DomItem &self, QString canonicalFilePath)
} else if (QStringView(u"qmldir").compare(QFileInfo(canonicalFilePath).fileName(),
Qt::CaseInsensitive)
== 0) {
- return DomType::QmltypesFile;
+ return DomType::QmldirFile;
} else if (QFileInfo(canonicalFilePath).isDir()) {
return DomType::QmlDirectory;
- } else {
+ } else if (canonicalFilePath.endsWith(u".js", Qt::CaseInsensitive)
+ || canonicalFilePath.endsWith(u".mjs", Qt::CaseInsensitive)) {
+ return DomType::JsFile;
+ }
+ else {
self.addError(DomUniverse::myErrors()
- .error(QCoreApplication::translate("Dom::filteTypeForPath",
+ .error(QCoreApplication::translate("Dom::fileTypeForPath",
"Could not detect type of file %1")
.arg(canonicalFilePath))
.handle());
@@ -249,245 +232,120 @@ static DomType fileTypeForPath(DomItem &self, QString canonicalFilePath)
return DomType::Empty;
}
-void DomUniverse::loadFile(DomItem &self, QString canonicalFilePath, QString logicalPath,
- QString code, QDateTime codeDate, Callback callback,
- LoadOptions loadOptions, std::optional<DomType> fileType)
+DomUniverse::LoadResult DomUniverse::loadFile(const FileToLoad &file, DomType fileType,
+ DomCreationOptions creationOptions)
{
- DomType fType = (bool(fileType) ? (*fileType) : fileTypeForPath(self, canonicalFilePath));
- switch (fType) {
+ DomItem univ(shared_from_this());
+ switch (fileType) {
case DomType::QmlFile:
case DomType::QmltypesFile:
case DomType::QmldirFile:
- case DomType::QmlDirectory: {
- // Protect the queue from concurrent access.
- QMutexLocker l(mutex());
- m_queue.enqueue(ParsingTask{ QDateTime::currentDateTimeUtc(), loadOptions, fType,
- canonicalFilePath, logicalPath, code, codeDate,
- self.ownerAs<DomUniverse>(), callback });
- break;
+ case DomType::QmlDirectory:
+ case DomType::JsFile: {
+ LoadResult loadRes;
+ const auto &preLoadResult = preload(univ, file, fileType);
+ if (std::holds_alternative<LoadResult>(preLoadResult)) {
+ // universe already has the most recent version of the file
+ return std::get<LoadResult>(preLoadResult);
+ } else {
+ // content of the file needs to be parsed and value inside Universe needs to be updated
+ return load(std::get<ContentWithDate>(preLoadResult), file, fileType, creationOptions);
+ }
}
default:
- self.addError(myErrors()
+ univ.addError(myErrors()
.error(tr("Ignoring request to load file %1 of unexpected type %2, "
"calling callback immediately")
- .arg(canonicalFilePath, domTypeToString(fType)))
+ .arg(file.canonicalPath(), domTypeToString(fileType)))
.handle());
Q_ASSERT(false && "loading non supported file type");
- callback(Path(), DomItem::empty, DomItem::empty);
- return;
+ return {};
}
- if (m_options & Option::SingleThreaded)
- execQueue(); // immediate execution in the same thread
}
-template<typename T>
-QPair<std::shared_ptr<ExternalItemPair<T>>, std::shared_ptr<ExternalItemPair<T>>>
-updateEntry(DomItem &univ, std::shared_ptr<T> newItem,
- QMap<QString, std::shared_ptr<ExternalItemPair<T>>> &map, QBasicMutex *mutex)
-{
- std::shared_ptr<ExternalItemPair<T>> oldValue;
- std::shared_ptr<ExternalItemPair<T>> newValue;
- QString canonicalPath = newItem->canonicalFilePath();
- QDateTime now = QDateTime::currentDateTimeUtc();
- {
- QMutexLocker l(mutex);
- auto it = map.find(canonicalPath);
- if (it != map.cend() && (*it) && (*it)->current) {
- oldValue = *it;
- QString oldCode = oldValue->current->code();
- QString newCode = newItem->code();
- if (!oldCode.isNull() && !newCode.isNull() && oldCode == newCode) {
- newValue = oldValue;
- if (newValue->current->lastDataUpdateAt() < newItem->lastDataUpdateAt())
- newValue->current->refreshedDataAt(newItem->lastDataUpdateAt());
- } else if (oldValue->current->lastDataUpdateAt() > newItem->lastDataUpdateAt()) {
- newValue = oldValue;
- } else {
- DomItem oldValueObj = univ.copy(oldValue);
- newValue = oldValue->makeCopy(oldValueObj);
- newValue->current = newItem;
- newValue->currentExposedAt = now;
- if (newItem->isValid()) {
- newValue->valid = newItem;
- newValue->validExposedAt = now;
- }
- it = map.insert(it, canonicalPath, newValue);
- }
- } else {
- newValue = std::make_shared<ExternalItemPair<T>>(
- (newItem->isValid() ? newItem : std::shared_ptr<T>()), newItem, now, now);
- map.insert(canonicalPath, newValue);
- }
+DomUniverse::LoadResult DomUniverse::load(const ContentWithDate &codeWithDate,
+ const FileToLoad &file, DomType fType,
+ DomCreationOptions creationOptions)
+{
+ QString canonicalPath = file.canonicalPath();
+
+ DomItem oldValue; // old ExternalItemPair (might be empty, or equal to newValue)
+ DomItem newValue; // current ExternalItemPair
+ DomItem univ = DomItem(shared_from_this());
+
+ if (fType == DomType::QmlFile) {
+ auto qmlFile = parseQmlFile(codeWithDate.content, file, codeWithDate.date, creationOptions);
+ return insertOrUpdateExternalItem(std::move(qmlFile));
+ } else if (fType == DomType::QmltypesFile) {
+ auto qmltypesFile = std::make_shared<QmltypesFile>(canonicalPath, codeWithDate.content,
+ codeWithDate.date);
+ QmltypesReader reader(univ.copy(qmltypesFile));
+ reader.parse();
+ return insertOrUpdateExternalItem(std::move(qmltypesFile));
+ } else if (fType == DomType::QmldirFile) {
+ shared_ptr<QmldirFile> qmldirFile =
+ QmldirFile::fromPathAndCode(canonicalPath, codeWithDate.content);
+ return insertOrUpdateExternalItem(std::move(qmldirFile));
+ } else if (fType == DomType::QmlDirectory) {
+ auto qmlDirectory = std::make_shared<QmlDirectory>(
+ canonicalPath, codeWithDate.content.split(QLatin1Char('\n')), codeWithDate.date);
+ return insertOrUpdateExternalItem(std::move(qmlDirectory));
+ } else if (fType == DomType::JsFile) {
+ auto jsFile = parseJsFile(codeWithDate.content, file, codeWithDate.date);
+ return insertOrUpdateExternalItem(std::move(jsFile));
+ } else {
+ Q_ASSERT(false);
}
- return qMakePair(oldValue, newValue);
+ return { std::move(oldValue), std::move(newValue) };
}
-void DomUniverse::execQueue()
+/*!
+ \internal
+ This function is somewhat coupled and does the following:
+ 1. If a content of the file is provided it checks whether the item with the same content
+ already exists inside the Universe. If so, returns it as a result of the load
+ 2. If a content is not provided, it first tries to check whether Universe has the most
+ recent item. If yes, it returns it as a result of the load. Otherwise does step 1.
+ */
+DomUniverse::PreloadResult DomUniverse::preload(const DomItem &univ, const FileToLoad &file,
+ DomType fType) const
{
- ParsingTask t;
- {
- // Protect the queue from concurrent access.
- QMutexLocker l(mutex());
- if (m_queue.isEmpty())
- return;
- t = m_queue.dequeue();
- }
- shared_ptr<DomUniverse> topPtr = t.requestingUniverse.lock();
- if (!topPtr) {
- myErrors().error(tr("Ignoring callback for loading of %1: universe is not valid anymore").arg(t.canonicalPath)).handle();
- }
- QString canonicalPath = t.canonicalPath;
- QString code = t.contents;
- QDateTime contentDate = t.contentsDate;
- bool skipParse = false;
- DomItem oldValue; // old ExternalItemPair (might be empty, or equal to newValue)
- DomItem newValue; // current ExternalItemPair
- DomItem univ = DomItem(topPtr);
- QFileInfo path(canonicalPath);
- QVector<ErrorMessage> messages;
-
- if (t.kind == DomType::QmlFile || t.kind == DomType::QmltypesFile
- || t.kind == DomType::QmldirFile || t.kind == DomType::QmlDirectory) {
- auto getValue = [&t, this, &canonicalPath]() -> std::shared_ptr<ExternalItemPairBase> {
- if (t.kind == DomType::QmlFile)
- return m_qmlFileWithPath.value(canonicalPath);
- else if (t.kind == DomType::QmltypesFile)
- return m_qmlFileWithPath.value(canonicalPath);
- else if (t.kind == DomType::QmldirFile)
- return m_qmlFileWithPath.value(canonicalPath);
- else if (t.kind == DomType::QmlDirectory)
- return m_qmlDirectoryWithPath.value(canonicalPath);
- else
- Q_ASSERT(false);
- return {};
- };
- if (code.isEmpty()) {
- QFile file(canonicalPath);
- canonicalPath = path.canonicalFilePath();
- if (canonicalPath.isEmpty()) {
- messages.append(myErrors().error(tr("Non existing path %1").arg(t.canonicalPath)));
- canonicalPath = t.canonicalPath;
- }
- {
- QMutexLocker l(mutex());
- auto value = getValue();
- if (!(t.loadOptions & LoadOption::ForceLoad) && value) {
- if (value && value->currentItem()
- && path.lastModified() < value->currentItem()->lastDataUpdateAt()) {
- oldValue = newValue = univ.copy(value);
- skipParse = true;
- }
- }
- }
- if (!skipParse) {
- contentDate = QDateTime::currentDateTimeUtc();
- if (QFileInfo(canonicalPath).isDir()) {
- code = QDir(canonicalPath)
- .entryList(QDir::NoDotAndDotDot | QDir::Files, QDir::Name)
- .join(QLatin1Char('\n'));
- } else if (!file.open(QIODevice::ReadOnly)) {
- code = QStringLiteral(u"");
- messages.append(myErrors().error(tr("Error opening path %1: %2 %3")
- .arg(canonicalPath,
- QString::number(file.error()),
- file.errorString())));
- } else {
- code = QString::fromUtf8(file.readAll());
- file.close();
- }
- }
- }
- if (!skipParse) {
- QMutexLocker l(mutex());
- if (auto value = getValue()) {
- QString oldCode = value->currentItem()->code();
- if (value && value->currentItem() && !oldCode.isNull() && oldCode == code) {
- skipParse = true;
- newValue = oldValue = univ.copy(value);
- if (value->currentItem()->lastDataUpdateAt() < contentDate)
- value->currentItem()->refreshedDataAt(contentDate);
- }
- }
- }
- if (!skipParse) {
- QDateTime now(QDateTime::currentDateTimeUtc());
- if (t.kind == DomType::QmlFile) {
- auto qmlFile = std::make_shared<QmlFile>(canonicalPath, code, contentDate);
- auto envPtr = std::make_shared<DomEnvironment>(
- QStringList(), DomEnvironment::Option::NoDependencies, topPtr);
- envPtr->addQmlFile(qmlFile);
- DomItem env(envPtr);
- if (qmlFile->isValid()) {
- MutableDomItem qmlFileObj(env.copy(qmlFile));
- createDom(qmlFileObj);
- } else {
- QString errs;
- DomItem qmlFileObj = env.copy(qmlFile);
- qmlFile->iterateErrors(qmlFileObj, [&errs](DomItem, ErrorMessage m) {
- errs += m.toString();
- errs += u"\n";
- return true;
- });
- qCWarning(domLog).noquote().nospace()
- << "Parsed invalid file " << canonicalPath << errs;
- }
- auto change = updateEntry<QmlFile>(univ, qmlFile, m_qmlFileWithPath, mutex());
- oldValue = univ.copy(change.first);
- newValue = univ.copy(change.second);
- } else if (t.kind == DomType::QmltypesFile) {
- auto qmltypesFile = std::make_shared<QmltypesFile>(
- canonicalPath, code, contentDate);
- QmltypesReader reader(univ.copy(qmltypesFile));
- reader.parse();
- auto change = updateEntry<QmltypesFile>(univ, qmltypesFile, m_qmltypesFileWithPath,
- mutex());
- oldValue = univ.copy(change.first);
- newValue = univ.copy(change.second);
- } else if (t.kind == DomType::QmldirFile) {
- shared_ptr<QmldirFile> qmldirFile =
- QmldirFile::fromPathAndCode(canonicalPath, code);
- auto change =
- updateEntry<QmldirFile>(univ, qmldirFile, m_qmldirFileWithPath, mutex());
- oldValue = univ.copy(change.first);
- newValue = univ.copy(change.second);
- } else if (t.kind == DomType::QmlDirectory) {
- auto qmlDirectory = std::make_shared<QmlDirectory>(
- canonicalPath, code.split(QLatin1Char('\n')), contentDate);
- auto change = updateEntry<QmlDirectory>(univ, qmlDirectory, m_qmlDirectoryWithPath,
- mutex());
- oldValue = univ.copy(change.first);
- newValue = univ.copy(change.second);
- } else {
- Q_ASSERT(false);
- }
+ QString canonicalPath = file.canonicalPath();
+ ContentWithDate codeWithDate;
+
+ if (file.content().has_value()) {
+ codeWithDate = { file.content()->data, file.content()->date };
+ } else {
+ // When content is empty, Universe attempts to read it from the File.
+ // However if it already has the most recent version of that File it just returns it
+ const auto &curValueItem = getItemIfMostRecent(univ, fType, canonicalPath);
+ if (curValueItem.has_value()) {
+ return LoadResult{ curValueItem.value(), curValueItem.value() };
}
- for (const ErrorMessage &m : messages)
- newValue.addError(m);
- // to do: tell observers?
- // execute callback
- if (t.callback) {
- Path p;
- if (t.kind == DomType::QmlFile)
- p = Paths::qmlFileInfoPath(canonicalPath);
- else if (t.kind == DomType::QmltypesFile)
- p = Paths::qmltypesFileInfoPath(canonicalPath);
- else if (t.kind == DomType::QmldirFile)
- p = Paths::qmldirFileInfoPath(canonicalPath);
- else if (t.kind == DomType::QmlDirectory)
- p = Paths::qmlDirectoryInfoPath(canonicalPath);
- else
- Q_ASSERT(false);
- t.callback(p, oldValue, newValue);
+ // otherwise tries to read the content from the path
+ auto readResult = readFileContent(canonicalPath);
+ if (std::holds_alternative<ErrorMessage>(readResult)) {
+ DomItem newValue;
+ newValue.addError(std::move(std::get<ErrorMessage>(readResult)));
+ return LoadResult{ DomItem(), std::move(newValue) }; // read failed, nothing to parse
+ } else {
+ codeWithDate = std::get<ContentWithDate>(readResult);
}
- } else {
- Q_ASSERT(false && "Unhandled kind in queue");
}
+
+ // Once the code is provided Universe verifies if it already has an up-to-date code
+ const auto &curValueItem = getItemIfHasSameCode(univ, fType, canonicalPath, codeWithDate);
+ if (curValueItem.has_value()) {
+ return LoadResult{ curValueItem.value(), curValueItem.value() };
+ }
+ // otherwise code needs to be parsed
+ return codeWithDate;
}
void DomUniverse::removePath(const QString &path)
{
QMutexLocker l(mutex());
- auto toDelete = [path](auto it) {
+ const auto toDelete = [path](const auto &it) {
QString p = it.key();
return p.startsWith(path) && (p.size() == path.size() || p.at(path.size()) == u'/');
};
@@ -498,7 +356,191 @@ void DomUniverse::removePath(const QString &path)
m_qmltypesFileWithPath.removeIf(toDelete);
}
-std::shared_ptr<OwningItem> LoadInfo::doCopy(DomItem &self) const
+DomUniverse::ReadResult DomUniverse::readFileContent(const QString &canonicalPath) const
+{
+ if (canonicalPath.isEmpty()) {
+ return myErrors().error(tr("Non existing path %1").arg(canonicalPath));
+ }
+ QFile file(canonicalPath);
+ QFileInfo fileInfo(canonicalPath);
+ if (fileInfo.isDir()) {
+ return ContentWithDate{ QDir(canonicalPath)
+ .entryList(QDir::NoDotAndDotDot | QDir::Files, QDir::Name)
+ .join(QLatin1Char('\n')),
+ QDateTime::currentDateTimeUtc() };
+ }
+ if (!file.open(QIODevice::ReadOnly)) {
+ return myErrors().error(
+ tr("Error opening path %1: %2 %3")
+ .arg(canonicalPath, QString::number(file.error()), file.errorString()));
+ }
+ auto content = QString::fromUtf8(file.readAll());
+ file.close();
+ return ContentWithDate{ std::move(content), QDateTime::currentDateTimeUtc() };
+}
+
+std::shared_ptr<QmlFile> DomUniverse::parseQmlFile(const QString &code, const FileToLoad &file,
+ const QDateTime &contentDate,
+ DomCreationOptions creationOptions)
+{
+ auto qmlFile = std::make_shared<QmlFile>(file.canonicalPath(), code, contentDate, 0,
+ creationOptions.testFlag(WithRecovery)
+ ? QmlFile::EnableParserRecovery
+ : QmlFile::DisableParserRecovery);
+ std::shared_ptr<DomEnvironment> envPtr;
+ if (auto ptr = file.environment().lock())
+ envPtr = std::move(ptr);
+ else
+ envPtr = std::make_shared<DomEnvironment>(QStringList(),
+ DomEnvironment::Option::NoDependencies,
+ creationOptions, shared_from_this());
+ envPtr->addQmlFile(qmlFile);
+ DomItem env(envPtr);
+ if (qmlFile->isValid()) {
+ // do not call populateQmlFile twice on lazy qml files if the importer already does it!
+ if (!creationOptions.testFlag(DomCreationOption::WithSemanticAnalysis))
+ envPtr->populateFromQmlFile(MutableDomItem(env.copy(qmlFile)));
+ } else {
+ QString errs;
+ DomItem qmlFileObj = env.copy(qmlFile);
+ qmlFile->iterateErrors(qmlFileObj, [&errs](const DomItem &, const ErrorMessage &m) {
+ errs += m.toString();
+ errs += u"\n";
+ return true;
+ });
+ qCWarning(domLog).noquote().nospace()
+ << "Parsed invalid file " << file.canonicalPath() << errs;
+ }
+ return qmlFile;
+}
+
+std::shared_ptr<JsFile> DomUniverse::parseJsFile(const QString &code, const FileToLoad &file,
+ const QDateTime &contentDate)
+{
+ // WATCH OUT!
+ // DOM construction for plain JS files is not yet supported
+ // Only parsing of the file
+ // and adding ExternalItem to the Environment will happen here
+ auto jsFile = std::make_shared<JsFile>(file.canonicalPath(), code, contentDate);
+ std::shared_ptr<DomEnvironment> envPtr;
+ if (auto ptr = file.environment().lock())
+ envPtr = std::move(ptr);
+ else
+ envPtr = std::make_shared<DomEnvironment>(QStringList(),
+ DomEnvironment::Option::NoDependencies,
+ DomCreationOption::None, shared_from_this());
+ envPtr->addJsFile(jsFile);
+ DomItem env(envPtr);
+ if (!jsFile->isValid()) {
+ QString errs;
+ DomItem qmlFileObj = env.copy(jsFile);
+ jsFile->iterateErrors(qmlFileObj, [&errs](const DomItem &, const ErrorMessage &m) {
+ errs += m.toString();
+ errs += u"\n";
+ return true;
+ });
+ qCWarning(domLog).noquote().nospace()
+ << "Parsed invalid file " << file.canonicalPath() << errs;
+ }
+ return jsFile;
+}
+
+/*!
+ \internal
+ Queries the corresponding path map attempting to get the value
+ *WARNING* Usage of this function should be protected by the read lock
+ */
+std::shared_ptr<ExternalItemPairBase> DomUniverse::getPathValueOrNull(DomType fType,
+ const QString &path) const
+{
+ switch (fType) {
+ case DomType::QmlFile:
+ return m_qmlFileWithPath.value(path);
+ case DomType::QmltypesFile:
+ return m_qmltypesFileWithPath.value(path);
+ case DomType::QmldirFile:
+ return m_qmldirFileWithPath.value(path);
+ case DomType::QmlDirectory:
+ return m_qmlDirectoryWithPath.value(path);
+ case DomType::JsFile:
+ return m_jsFileWithPath.value(path);
+ default:
+ Q_ASSERT(false);
+ }
+ return nullptr;
+}
+
+std::optional<DomItem> DomUniverse::getItemIfMostRecent(const DomItem &univ, DomType fType,
+ const QString &canonicalPath) const
+{
+ QFileInfo fInfo(canonicalPath);
+ bool valueItemIsMostRecent = false;
+ std::shared_ptr<ExternalItemPairBase> value = nullptr;
+ {
+ // Mutex is to sync access to the Value and Value->CurrentItem, which can be modified
+ // through updateEnty method and currentItem->refreshedDataAt
+ QMutexLocker l(mutex());
+ value = getPathValueOrNull(fType, canonicalPath);
+ valueItemIsMostRecent = valueHasMostRecentItem(value.get(), fInfo.lastModified());
+ }
+ if (valueItemIsMostRecent) {
+ return univ.copy(value);
+ }
+ return std::nullopt;
+}
+
+std::optional<DomItem> DomUniverse::getItemIfHasSameCode(const DomItem &univ, DomType fType,
+ const QString &canonicalPath,
+ const ContentWithDate &codeWithDate) const
+{
+ std::shared_ptr<ExternalItemPairBase> value = nullptr;
+ bool valueItemHasSameCode = false;
+ {
+ // Mutex is to sync access to the Value and Value->CurrentItem, which can be modified
+ // through updateEnty method and currentItem->refreshedDataAt
+ QMutexLocker l(mutex());
+ value = getPathValueOrNull(fType, canonicalPath);
+ if (valueHasSameContent(value.get(), codeWithDate.content)) {
+ valueItemHasSameCode = true;
+ if (value->currentItem()->lastDataUpdateAt() < codeWithDate.date)
+ value->currentItem()->refreshedDataAt(codeWithDate.date);
+ }
+ }
+ if (valueItemHasSameCode) {
+ return univ.copy(value);
+ }
+ return std::nullopt;
+}
+
+/*!
+ \internal
+ Checks if value has current Item and if it was not modified since last seen
+ *WARNING* Usage of this function should be protected by the read lock
+ */
+bool DomUniverse::valueHasMostRecentItem(const ExternalItemPairBase *value,
+ const QDateTime &lastModified)
+{
+ if (!value || !value->currentItem()) {
+ return false;
+ }
+ return lastModified < value->currentItem()->lastDataUpdateAt();
+}
+
+/*!
+ \internal
+ Checks if value has current Item and if it has same content
+ *WARNING* Usage of this function should be protected by the read lock
+ */
+bool DomUniverse::valueHasSameContent(const ExternalItemPairBase *value, const QString &content)
+{
+ if (!value || !value->currentItem()) {
+ return false;
+ }
+ QString curContent = value->currentItem()->code();
+ return !curContent.isNull() && curContent == content;
+}
+
+std::shared_ptr<OwningItem> LoadInfo::doCopy(const DomItem &self) const
{
auto res = std::make_shared<LoadInfo>(*this);
if (res->status() != Status::Done) {
@@ -506,7 +548,7 @@ std::shared_ptr<OwningItem> LoadInfo::doCopy(DomItem &self) const
u"This is a copy of a LoadInfo still in progress, artificially ending it, if you "
u"use this you will *not* resume loading"));
DomEnvironment::myErrors()
- .warning([&self](Sink sink) {
+ .warning([&self](const Sink &sink) {
sink(u"Copying an in progress LoadInfo, which is most likely an error (");
self.dump(sink);
sink(u")");
@@ -521,12 +563,12 @@ std::shared_ptr<OwningItem> LoadInfo::doCopy(DomItem &self) const
return res;
}
-Path LoadInfo::canonicalPath(DomItem &) const
+Path LoadInfo::canonicalPath(const DomItem &) const
{
return Path::Root(PathRoot::Env).field(Fields::loadInfo).key(elementCanonicalPath().toString());
}
-bool LoadInfo::iterateDirectSubpaths(DomItem &self, DirectVisitor visitor)
+bool LoadInfo::iterateDirectSubpaths(const DomItem &self, DirectVisitor visitor) const
{
bool cont = OwningItem::iterateDirectSubpaths(self, visitor);
cont = cont && self.dvValueField(visitor, Fields::status, int(status()));
@@ -539,8 +581,8 @@ bool LoadInfo::iterateDirectSubpaths(DomItem &self, DirectVisitor visitor)
return cont;
}
-void LoadInfo::addEndCallback(DomItem &self,
- std::function<void(Path, DomItem &, DomItem &)> callback)
+void LoadInfo::addEndCallback(const DomItem &self,
+ std::function<void(Path, const DomItem &, const DomItem &)> callback)
{
if (!callback)
return;
@@ -562,7 +604,7 @@ void LoadInfo::addEndCallback(DomItem &self,
callback(p, el, el);
}
-void LoadInfo::advanceLoad(DomItem &self)
+void LoadInfo::advanceLoad(const DomItem &self)
{
Status myStatus;
Dependency dep;
@@ -607,26 +649,27 @@ void LoadInfo::advanceLoad(DomItem &self)
case Status::InProgress:
if (depValid) {
refreshedDataAt(QDateTime::currentDateTimeUtc());
+ auto envPtr = self.environment().ownerAs<DomEnvironment>();
+ Q_ASSERT(envPtr && "missing environment");
if (!dep.uri.isEmpty()) {
- self.loadModuleDependency(
+ envPtr->loadModuleDependency(
dep.uri, dep.version,
- [this, self, dep](Path, DomItem &, DomItem &) mutable {
- finishedLoadingDep(self, dep);
+ [this, copiedSelf = self, dep](Path, const DomItem &, const DomItem &) {
+ // Need to explicitly copy self here since we might store this and
+ // call it later.
+ finishedLoadingDep(copiedSelf, dep);
},
self.errorHandler());
Q_ASSERT(dep.filePath.isEmpty() && "dependency with both uri and file");
} else if (!dep.filePath.isEmpty()) {
- DomItem env = self.environment();
- if (std::shared_ptr<DomEnvironment> envPtr = env.ownerAs<DomEnvironment>())
- envPtr->loadFile(
- env, dep.filePath, QString(),
- [this, self, dep](Path, DomItem &, DomItem &) mutable {
- finishedLoadingDep(self, dep);
- },
- nullptr, nullptr, LoadOption::DefaultLoad, dep.fileType,
- self.errorHandler());
- else
- Q_ASSERT(false && "missing environment");
+ envPtr->loadFile(
+ FileToLoad::fromFileSystem(envPtr, dep.filePath),
+ [this, copiedSelf = self, dep](Path, const DomItem &, const DomItem &) {
+ // Need to explicitly copy self here since we might store this and
+ // call it later.
+ finishedLoadingDep(copiedSelf, dep);
+ },
+ dep.fileType, self.errorHandler());
} else {
Q_ASSERT(false && "dependency without uri and filePath");
}
@@ -643,7 +686,7 @@ void LoadInfo::advanceLoad(DomItem &self)
}
}
-void LoadInfo::finishedLoadingDep(DomItem &self, const Dependency &d)
+void LoadInfo::finishedLoadingDep(const DomItem &self, const Dependency &d)
{
bool didRemove = false;
bool unexpectedState = false;
@@ -668,7 +711,7 @@ void LoadInfo::finishedLoadingDep(DomItem &self, const Dependency &d)
}
}
if (!didRemove) {
- addErrorLocal(DomEnvironment::myErrors().error([&self](Sink sink) {
+ addErrorLocal(DomEnvironment::myErrors().error([&self](const Sink &sink) {
sink(u"LoadInfo::finishedLoadingDep did not find its dependency in those inProgress "
u"()");
self.dump(sink);
@@ -678,7 +721,7 @@ void LoadInfo::finishedLoadingDep(DomItem &self, const Dependency &d)
&& "LoadInfo::finishedLoadingDep did not find its dependency in those inProgress");
}
if (unexpectedState) {
- addErrorLocal(DomEnvironment::myErrors().error([&self](Sink sink) {
+ addErrorLocal(DomEnvironment::myErrors().error([&self](const Sink &sink) {
sink(u"LoadInfo::finishedLoadingDep found an unexpected state (");
self.dump(sink);
sink(u")");
@@ -689,9 +732,9 @@ void LoadInfo::finishedLoadingDep(DomItem &self, const Dependency &d)
execEnd(self);
}
-void LoadInfo::execEnd(DomItem &self)
+void LoadInfo::execEnd(const DomItem &self)
{
- QList<std::function<void(Path, DomItem &, DomItem &)>> endCallbacks;
+ QList<std::function<void(Path, const DomItem &, const DomItem &)>> endCallbacks;
bool unexpectedState = false;
{
QMutexLocker l(mutex());
@@ -704,7 +747,7 @@ void LoadInfo::execEnd(DomItem &self)
DomItem el = self.path(p);
{
auto cleanup = qScopeGuard([this, p, &el] {
- QList<std::function<void(Path, DomItem &, DomItem &)>> otherCallbacks;
+ QList<std::function<void(Path, const DomItem &, const DomItem &)>> otherCallbacks;
bool unexpectedState2 = false;
{
QMutexLocker l(mutex());
@@ -726,7 +769,7 @@ void LoadInfo::execEnd(DomItem &self)
}
}
-void LoadInfo::doAddDependencies(DomItem &self)
+void LoadInfo::doAddDependencies(const DomItem &self)
{
if (!elementCanonicalPath()) {
DomEnvironment::myErrors()
@@ -739,55 +782,36 @@ void LoadInfo::doAddDependencies(DomItem &self)
DomItem el = self.path(elementCanonicalPath());
if (el.internalKind() == DomType::ExternalItemInfo) {
DomItem currentFile = el.field(Fields::currentItem);
- DomItem currentImports = currentFile.field(Fields::imports);
QString currentFilePath = currentFile.canonicalFilePath();
- int iEnd = currentImports.indexes();
- for (int i = 0; i < iEnd; ++i) {
- DomItem import = currentImports.index(i);
- if (const Import *importPtr = import.as<Import>()) {
- if (importPtr->uri.isDirectory()) {
- QString path = importPtr->uri.absoluteLocalPath(currentFilePath);
- if (!path.isEmpty()) {
- addDependency(self,
- Dependency { QString(), importPtr->version, path,
- DomType::QmlDirectory });
- } else {
- self.addError(DomEnvironment::myErrors().error(
- tr("Ignoring dependencies for non resolved path import %1")
- .arg(importPtr->uri.toString())));
- }
- } else {
- addDependency(self,
- Dependency { importPtr->uri.moduleUri(), importPtr->version,
- QString(), DomType::ModuleIndex });
- }
- }
- }
- DomItem currentQmltypesFiles = currentFile.field(Fields::qmltypesFiles);
- int qEnd = currentQmltypesFiles.indexes();
- for (int i = 0; i < qEnd; ++i) {
- DomItem qmltypesRef = currentQmltypesFiles.index(i);
- if (const Reference *ref = qmltypesRef.as<Reference>()) {
- Path canonicalPath = ref->referredObjectPath[2];
- if (canonicalPath && !canonicalPath.headName().isEmpty())
- addDependency(self,
- Dependency { QString(), Version(), canonicalPath.headName(),
- DomType::QmltypesFile });
- }
- }
- DomItem currentQmlFiles = currentFile.field(Fields::qmlFiles);
- currentQmlFiles.visitKeys([this, &self](QString, DomItem &els) {
- return els.visitIndexes([this, &self](DomItem &el) {
- if (const Reference *ref = el.as<Reference>()) {
+ // do not mess with QmlFile's lazy-loading
+ if (currentFile.internalKind() != DomType::QmlFile) {
+ DomItem currentQmltypesFiles = currentFile.field(Fields::qmltypesFiles);
+ int qEnd = currentQmltypesFiles.indexes();
+ for (int i = 0; i < qEnd; ++i) {
+ DomItem qmltypesRef = currentQmltypesFiles.index(i);
+ if (const Reference *ref = qmltypesRef.as<Reference>()) {
Path canonicalPath = ref->referredObjectPath[2];
if (canonicalPath && !canonicalPath.headName().isEmpty())
- addDependency(self,
- Dependency { QString(), Version(), canonicalPath.headName(),
- DomType::QmlFile });
+ addDependency(
+ self,
+ Dependency{ QString(), Version(), canonicalPath.headName(),
+ DomType::QmltypesFile });
}
- return true;
+ }
+ DomItem currentQmlFiles = currentFile.field(Fields::qmlFiles);
+ currentQmlFiles.visitKeys([this, &self](const QString &, const DomItem &els) {
+ return els.visitIndexes([this, &self](const DomItem &el) {
+ if (const Reference *ref = el.as<Reference>()) {
+ Path canonicalPath = ref->referredObjectPath[2];
+ if (canonicalPath && !canonicalPath.headName().isEmpty())
+ addDependency(self,
+ Dependency{ QString(), Version(),
+ canonicalPath.headName(), DomType::QmlFile });
+ }
+ return true;
+ });
});
- });
+ }
} else if (shared_ptr<ModuleIndex> elPtr = el.ownerAs<ModuleIndex>()) {
const auto qmldirs = elPtr->qmldirsToLoad(el);
for (const Path &qmldirPath : qmldirs) {
@@ -798,7 +822,7 @@ void LoadInfo::doAddDependencies(DomItem &self)
DomType::QmldirFile });
}
QString uri = elPtr->uri();
- addEndCallback(self, [uri, qmldirs](Path, DomItem &, DomItem &newV) {
+ addEndCallback(self, [uri, qmldirs](Path, const DomItem &, const DomItem &newV) {
for (const Path &p : qmldirs) {
DomItem qmldir = newV.path(p);
if (std::shared_ptr<QmldirFile> qmldirFilePtr = qmldir.ownerAs<QmldirFile>()) {
@@ -818,7 +842,7 @@ void LoadInfo::doAddDependencies(DomItem &self)
}
}
-void LoadInfo::addDependency(DomItem &self, const Dependency &dep)
+void LoadInfo::addDependency(const DomItem &self, const Dependency &dep)
{
bool unexpectedState = false;
{
@@ -835,105 +859,14 @@ void LoadInfo::addDependency(DomItem &self, const Dependency &dep)
\class QQmlJS::Dom::DomEnvironment
\brief Represents a consistent set of types organized in modules, it is the top level of the DOM
- */
-template<typename T>
-DomTop::Callback envCallbackForFile(
- DomItem &self, QMap<QString, std::shared_ptr<ExternalItemInfo<T>>> DomEnvironment::*map,
- std::shared_ptr<ExternalItemInfo<T>> (DomEnvironment::*lookupF)(DomItem &, QString,
- EnvLookup) const,
- DomTop::Callback loadCallback, DomTop::Callback allDirectDepsCallback,
- DomTop::Callback endCallback)
-{
- std::shared_ptr<DomEnvironment> ePtr = self.ownerAs<DomEnvironment>();
- std::weak_ptr<DomEnvironment> selfPtr = ePtr;
- std::shared_ptr<DomEnvironment> basePtr = ePtr->base();
- return [selfPtr, basePtr, map, lookupF, loadCallback, allDirectDepsCallback,
- endCallback](Path, DomItem &, DomItem &newItem) {
- shared_ptr<DomEnvironment> envPtr = selfPtr.lock();
- if (!envPtr)
- return;
- DomItem env = DomItem(envPtr);
- shared_ptr<ExternalItemInfo<T>> oldValue;
- shared_ptr<ExternalItemInfo<T>> newValue;
- shared_ptr<T> newItemPtr;
- if (envPtr->options() & DomEnvironment::Option::KeepValid)
- newItemPtr = newItem.field(Fields::validItem).ownerAs<T>();
- if (!newItemPtr)
- newItemPtr = newItem.field(Fields::currentItem).ownerAs<T>();
- Q_ASSERT(newItemPtr && "callbackForQmlFile reached without current qmlFile");
- {
- QMutexLocker l(envPtr->mutex());
- oldValue = ((*envPtr).*map).value(newItem.canonicalFilePath());
- }
- if (oldValue) {
- // we do not change locally loaded files (avoid loading a file more than once)
- newValue = oldValue;
- } else {
- if (basePtr) {
- DomItem baseObj(basePtr);
- oldValue = ((*basePtr).*lookupF)(baseObj, newItem.canonicalFilePath(),
- EnvLookup::BaseOnly);
- }
- if (oldValue) {
- DomItem oldValueObj = env.copy(oldValue);
- newValue = oldValue->makeCopy(oldValueObj);
- if (newValue->current != newItemPtr) {
- newValue->current = newItemPtr;
- newValue->setCurrentExposedAt(QDateTime::currentDateTimeUtc());
- }
- } else {
- newValue = std::make_shared<ExternalItemInfo<T>>(
- newItemPtr, QDateTime::currentDateTimeUtc());
- }
- {
- QMutexLocker l(envPtr->mutex());
- auto value = ((*envPtr).*map).value(newItem.canonicalFilePath());
- if (value) {
- oldValue = newValue = value;
- } else {
- ((*envPtr).*map).insert(newItem.canonicalFilePath(), newValue);
- }
- }
- }
- Path p = env.copy(newValue).canonicalPath();
- {
- auto depLoad = qScopeGuard([p, &env, envPtr, allDirectDepsCallback, endCallback] {
- if (!(envPtr->options() & DomEnvironment::Option::NoDependencies)) {
- auto loadInfo = std::make_shared<LoadInfo>(p);
- if (!p)
- Q_ASSERT(false);
- DomItem loadInfoObj = env.copy(loadInfo);
- loadInfo->addEndCallback(loadInfoObj, allDirectDepsCallback);
- envPtr->addLoadInfo(env, loadInfo);
- }
- if (endCallback)
- envPtr->addAllLoadedCallback(env,
- [p, endCallback](Path, DomItem &, DomItem &env) {
- DomItem el = env.path(p);
- endCallback(p, el, el);
- });
- });
- if (loadCallback) {
- DomItem oldValueObj = env.copy(oldValue);
- DomItem newValueObj = env.copy(newValue);
- loadCallback(p, oldValueObj, newValueObj);
- }
- if ((envPtr->options() & DomEnvironment::Option::NoDependencies)
- && allDirectDepsCallback) {
- DomItem oldValueObj = env.copy(oldValue);
- DomItem newValueObj = env.copy(newValue);
- env.addError(DomEnvironment::myErrors().warning(
- QLatin1String("calling allDirectDepsCallback immediately for load with "
- "NoDependencies of %1")
- .arg(newItem.canonicalFilePath())));
- allDirectDepsCallback(p, oldValueObj, newValueObj);
- }
- }
- };
-}
+The DomEnvironment keeps a pointer m_lastValidBase to the last used valid DomEnvironment in the
+commitToBase() method. This allows the qqmldomastcreator to commit lazily loaded dependencies to the
+valid environment used by qmlls.
+ */
-ErrorGroups DomEnvironment::myErrors() {
+ErrorGroups DomEnvironment::myErrors()
+{
static ErrorGroups res = {{NewErrorGroup("Dom")}};
return res;
}
@@ -948,7 +881,7 @@ Path DomEnvironment::canonicalPath() const
return Path::Root(u"env");
}
-bool DomEnvironment::iterateDirectSubpaths(DomItem &self, DirectVisitor visitor)
+bool DomEnvironment::iterateDirectSubpaths(const DomItem &self, DirectVisitor visitor) const
{
bool cont = true;
cont = cont && DomTop::iterateDirectSubpaths(self, visitor);
@@ -962,54 +895,54 @@ bool DomEnvironment::iterateDirectSubpaths(DomItem &self, DirectVisitor visitor)
cont = cont && self.dvItemField(visitor, Fields::globalScopeWithName, [this, &self]() {
return self.subMapItem(Map(
Path::Field(Fields::globalScopeWithName),
- [&self, this](DomItem &map, QString key) {
+ [&self, this](const DomItem &map, const QString &key) {
return map.copy(globalScopeWithName(self, key));
},
- [&self, this](DomItem &) { return globalScopeNames(self); },
+ [&self, this](const DomItem &) { return globalScopeNames(self); },
QLatin1String("GlobalScope")));
});
cont = cont && self.dvItemField(visitor, Fields::qmlDirectoryWithPath, [this, &self]() {
return self.subMapItem(Map(
Path::Field(Fields::qmlDirectoryWithPath),
- [&self, this](DomItem &map, QString key) {
+ [&self, this](const DomItem &map, const QString &key) {
return map.copy(qmlDirectoryWithPath(self, key));
},
- [&self, this](DomItem &) { return qmlDirectoryPaths(self); },
+ [&self, this](const DomItem &) { return qmlDirectoryPaths(self); },
QLatin1String("QmlDirectory")));
});
cont = cont && self.dvItemField(visitor, Fields::qmldirFileWithPath, [this, &self]() {
return self.subMapItem(Map(
Path::Field(Fields::qmldirFileWithPath),
- [&self, this](DomItem &map, QString key) {
+ [&self, this](const DomItem &map, const QString &key) {
return map.copy(qmldirFileWithPath(self, key));
},
- [&self, this](DomItem &) { return qmldirFilePaths(self); },
+ [&self, this](const DomItem &) { return qmldirFilePaths(self); },
QLatin1String("QmldirFile")));
});
cont = cont && self.dvItemField(visitor, Fields::qmldirWithPath, [this, &self]() {
return self.subMapItem(Map(
Path::Field(Fields::qmldirWithPath),
- [&self, this](DomItem &map, QString key) {
+ [&self, this](const DomItem &map, const QString &key) {
return map.copy(qmlDirWithPath(self, key));
},
- [&self, this](DomItem &) { return qmlDirPaths(self); }, QLatin1String("Qmldir")));
+ [&self, this](const DomItem &) { return qmlDirPaths(self); }, QLatin1String("Qmldir")));
});
cont = cont && self.dvItemField(visitor, Fields::qmlFileWithPath, [this, &self]() {
return self.subMapItem(Map(
Path::Field(Fields::qmlFileWithPath),
- [&self, this](DomItem &map, QString key) {
+ [&self, this](const DomItem &map, const QString &key) {
return map.copy(qmlFileWithPath(self, key));
},
- [&self, this](DomItem &) { return qmlFilePaths(self); }, QLatin1String("QmlFile")));
+ [&self, this](const DomItem &) { return qmlFilePaths(self); }, QLatin1String("QmlFile")));
});
cont = cont && self.dvItemField(visitor, Fields::jsFileWithPath, [this, &self]() {
return self.subMapItem(Map(
Path::Field(Fields::jsFileWithPath),
- [this](DomItem &map, QString key) {
+ [this](const DomItem &map, const QString &key) {
DomItem mapOw(map.owner());
return map.copy(jsFileWithPath(mapOw, key));
},
- [this](DomItem &map) {
+ [this](const DomItem &map) {
DomItem mapOw = map.owner();
return jsFilePaths(mapOw);
},
@@ -1018,11 +951,11 @@ bool DomEnvironment::iterateDirectSubpaths(DomItem &self, DirectVisitor visitor)
cont = cont && self.dvItemField(visitor, Fields::qmltypesFileWithPath, [this, &self]() {
return self.subMapItem(Map(
Path::Field(Fields::qmltypesFileWithPath),
- [this](DomItem &map, QString key) {
+ [this](const DomItem &map, const QString &key) {
DomItem mapOw = map.owner();
return map.copy(qmltypesFileWithPath(mapOw, key));
},
- [this](DomItem &map) {
+ [this](const DomItem &map) {
DomItem mapOw = map.owner();
return qmltypesFilePaths(mapOw);
},
@@ -1031,10 +964,10 @@ bool DomEnvironment::iterateDirectSubpaths(DomItem &self, DirectVisitor visitor)
cont = cont && self.dvItemField(visitor, Fields::moduleIndexWithUri, [this, &self]() {
return self.subMapItem(Map(
Path::Field(Fields::moduleIndexWithUri),
- [this](DomItem &map, QString key) {
+ [this](const DomItem &map, const QString &key) {
return map.subMapItem(Map(
map.pathFromOwner().key(key),
- [this, key](DomItem &submap, QString subKey) {
+ [this, key](const DomItem &submap, const QString &subKey) {
bool ok;
int i = subKey.toInt(&ok);
if (!ok) {
@@ -1050,7 +983,7 @@ bool DomEnvironment::iterateDirectSubpaths(DomItem &self, DirectVisitor visitor)
moduleIndexWithUri(subMapOw, key, i);
return submap.copy(mIndex);
},
- [this, key](DomItem &subMap) {
+ [this, key](const DomItem &subMap) {
QSet<QString> res;
DomItem subMapOw = subMap.owner();
for (int mVersion :
@@ -1065,7 +998,7 @@ bool DomEnvironment::iterateDirectSubpaths(DomItem &self, DirectVisitor visitor)
},
QLatin1String("ModuleIndex")));
},
- [this](DomItem &map) {
+ [this](const DomItem &map) {
DomItem mapOw = map.owner();
return moduleIndexUris(mapOw);
},
@@ -1090,14 +1023,14 @@ bool DomEnvironment::iterateDirectSubpaths(DomItem &self, DirectVisitor visitor)
ensureInfo();
return self.subListItem(List(
Path::Field(Fields::loadsWithWork),
- [loadsWithWork](DomItem &list, index_type i) {
+ [loadsWithWork](const DomItem &list, index_type i) {
if (i >= 0 && i < loadsWithWork.size())
return list.subDataItem(PathEls::Index(i),
loadsWithWork.at(i).toString());
else
return DomItem();
},
- [loadsWithWork](DomItem &) {
+ [loadsWithWork](const DomItem &) {
return index_type(loadsWithWork.size());
},
nullptr, QLatin1String("Path")));
@@ -1107,22 +1040,22 @@ bool DomEnvironment::iterateDirectSubpaths(DomItem &self, DirectVisitor visitor)
ensureInfo();
return self.subListItem(List(
Path::Field(Fields::inProgress),
- [inProgress](DomItem &list, index_type i) {
+ [inProgress](const DomItem &list, index_type i) {
if (i >= 0 && i < inProgress.size())
return list.subDataItem(PathEls::Index(i),
inProgress.at(i).toString());
else
return DomItem();
},
- [inProgress](DomItem &) { return index_type(inProgress.size()); },
+ [inProgress](const DomItem &) { return index_type(inProgress.size()); },
nullptr, QLatin1String("Path")));
});
cont = cont && self.dvItemField(visitor, Fields::loadInfo, [&self, this]() {
return self.subMapItem(Map(
Path::Field(Fields::loadInfo),
- [this](DomItem &map, QString pStr) {
+ [this](const DomItem &map, const QString &pStr) {
bool hasErrors = false;
- Path p = Path::fromString(pStr, [&hasErrors](ErrorMessage m) {
+ Path p = Path::fromString(pStr, [&hasErrors](const ErrorMessage &m) {
switch (m.level) {
case ErrorLevel::Debug:
case ErrorLevel::Info:
@@ -1138,7 +1071,7 @@ bool DomEnvironment::iterateDirectSubpaths(DomItem &self, DirectVisitor visitor)
return map.copy(loadInfo(p));
return DomItem();
},
- [this](DomItem &) {
+ [this](const DomItem &) {
QSet<QString> res;
const auto infoPaths = loadInfoPaths();
for (const Path &p : infoPaths)
@@ -1157,189 +1090,136 @@ bool DomEnvironment::iterateDirectSubpaths(DomItem &self, DirectVisitor visitor)
return cont;
}
-DomItem DomEnvironment::field(DomItem &self, QStringView name) const
+DomItem DomEnvironment::field(const DomItem &self, QStringView name) const
{
return DomTop::field(self, name);
}
-std::shared_ptr<DomEnvironment> DomEnvironment::makeCopy(DomItem &self) const
+std::shared_ptr<DomEnvironment> DomEnvironment::makeCopy(const DomItem &self) const
{
return std::static_pointer_cast<DomEnvironment>(doCopy(self));
}
-void DomEnvironment::loadFile(DomItem &self, QString filePath, QString logicalPath,
- DomTop::Callback loadCallback, DomTop::Callback directDepsCallback,
- DomTop::Callback endCallback, LoadOptions loadOptions,
- std::optional<DomType> fileType, ErrorHandler h)
-{
- loadFile(self, filePath, logicalPath, QString(), QDateTime::fromMSecsSinceEpoch(0, QTimeZone::UTC),
- loadCallback, directDepsCallback, endCallback, loadOptions, fileType, h);
-}
-
-std::shared_ptr<OwningItem> DomEnvironment::doCopy(DomItem &) const
+std::shared_ptr<OwningItem> DomEnvironment::doCopy(const DomItem &) const
{
shared_ptr<DomEnvironment> res;
if (m_base)
- res = std::make_shared<DomEnvironment>(m_base, m_loadPaths, m_options);
+ res = std::make_shared<DomEnvironment>(m_base, m_loadPaths, m_options,
+ m_domCreationOptions);
else
- res = std::make_shared<DomEnvironment>(
- m_loadPaths, m_options, m_universe);
+ res = std::make_shared<DomEnvironment>(m_loadPaths, m_options, m_domCreationOptions,
+ m_universe);
return res;
}
-void DomEnvironment::loadFile(DomItem &self, QString filePath, QString logicalPath, QString code,
- QDateTime codeDate, Callback loadCallback,
- Callback directDepsCallback, Callback endCallback,
- LoadOptions loadOptions, std::optional<DomType> fileType,
- ErrorHandler h)
+void DomEnvironment::loadFile(const FileToLoad &file, const Callback &callback,
+ std::optional<DomType> fileType, const ErrorHandler &h)
{
- QFileInfo fileInfo(filePath);
- QString canonicalFilePath = fileInfo.canonicalFilePath();
- if (canonicalFilePath.isEmpty()) {
- if (code.isNull()) {
- myErrors().error(tr("Non existing path to load: '%1'").arg(filePath)).handle(h);
+ if (options() & DomEnvironment::Option::NoDependencies)
+ loadFile(file, callback, DomTop::Callback(), fileType, h);
+ else {
+ // When the file is required to be loaded with dependencies, those dependencies
+ // will be added to the "pending" queue through envCallbackForFile
+ // then those should not be forgotten to be loaded.
+ loadFile(file, DomTop::Callback(), callback, fileType, h);
+ }
+}
+
+/*!
+ \internal
+ Depending on the options, the function will be called either with loadCallback OR endCallback
+
+ Before loading the file, envCallbackForFile will be created and passed as an argument to
+ universe().loadFile(...).
+ This is a callback which will be called after the load of the file is finished. More
+ specifically when File is required to be loaded without Dependencies only loadCallback is being
+ used. Otherwise, the callback is passed as endCallback. What endCallback means is that this
+ callback will be called only at the very end, once all necessary dependencies are being loaded.
+ Management and handing of this is happening through the m_loadsWithWork.
+*/
+// TODO(QTBUG-119550) refactor this
+void DomEnvironment::loadFile(const FileToLoad &file, const Callback &loadCallback,
+ const Callback &endCallback, std::optional<DomType> fileType,
+ const ErrorHandler &h)
+{
+ DomItem self(shared_from_this());
+ if (file.canonicalPath().isEmpty()) {
+ if (!file.content() || file.content()->data.isNull()) {
+ // file's content inavailable and no path to retrieve it
+ myErrors()
+ .error(tr("Non existing path to load: '%1'").arg(file.logicalPath()))
+ .handle(h);
if (loadCallback)
loadCallback(Path(), DomItem::empty, DomItem::empty);
- if (directDepsCallback)
- directDepsCallback(Path(), DomItem::empty, DomItem::empty);
if (endCallback)
- addAllLoadedCallback(self, [endCallback](Path, DomItem &, DomItem &) {
+ addAllLoadedCallback(self, [endCallback](Path, const DomItem &, const DomItem &) {
endCallback(Path(), DomItem::empty, DomItem::empty);
});
return;
} else {
- canonicalFilePath = filePath;
+ // fallback: path invalid but file's content is already available.
+ file.canonicalPath() = file.logicalPath();
}
}
+
shared_ptr<ExternalItemInfoBase> oldValue, newValue;
- DomType fType = (bool(fileType) ? (*fileType) : fileTypeForPath(self, canonicalFilePath));
+ const DomType fType =
+ (bool(fileType) ? (*fileType) : fileTypeForPath(self, file.canonicalPath()));
switch (fType) {
case DomType::QmlDirectory: {
- {
- QMutexLocker l(mutex());
- auto it = m_qmlDirectoryWithPath.find(canonicalFilePath);
- if (it != m_qmlDirectoryWithPath.end())
- oldValue = newValue = *it;
- }
- if (!newValue && (options() & Option::NoReload) && m_base) {
- if (auto v = m_base->qmlDirectoryWithPath(self, canonicalFilePath, EnvLookup::Normal)) {
- oldValue = v;
- QDateTime now = QDateTime::currentDateTimeUtc();
- auto newV = std::make_shared<ExternalItemInfo<QmlDirectory>>(
- v->current, now, v->revision(), v->lastDataUpdateAt());
- newValue = newV;
- QMutexLocker l(mutex());
- auto it = m_qmlDirectoryWithPath.find(canonicalFilePath);
- if (it != m_qmlDirectoryWithPath.end())
- oldValue = newValue = *it;
- else
- m_qmlDirectoryWithPath.insert(canonicalFilePath, newV);
- }
- }
+ const auto &fetchResult = fetchFileFromEnvs<QmlDirectory>(file);
+ oldValue = fetchResult.first;
+ newValue = fetchResult.second;
if (!newValue) {
- self.universe().loadFile(
- canonicalFilePath, logicalPath, code, codeDate,
- callbackForQmlDirectory(self, loadCallback, directDepsCallback, endCallback),
- loadOptions, fType);
+ const auto &loadRes = universe()->loadFile(file, fType, m_domCreationOptions);
+ addExternalItemInfo<QmlDirectory>(loadRes.currentItem,
+ getLoadCallbackFor(fType, loadCallback), endCallback);
return;
}
} break;
case DomType::QmlFile: {
- {
- QMutexLocker l(mutex());
- auto it = m_qmlFileWithPath.find(canonicalFilePath);
- if (it != m_qmlFileWithPath.end())
- oldValue = newValue = *it;
- }
- if (!newValue && (options() & Option::NoReload) && m_base) {
- if (auto v = m_base->qmlFileWithPath(self, canonicalFilePath, EnvLookup::Normal)) {
- oldValue = v;
- QDateTime now = QDateTime::currentDateTimeUtc();
- auto newV = std::make_shared<ExternalItemInfo<QmlFile>>(
- v->current, now, v->revision(), v->lastDataUpdateAt());
- newValue = newV;
- QMutexLocker l(mutex());
- auto it = m_qmlFileWithPath.find(canonicalFilePath);
- if (it != m_qmlFileWithPath.end())
- oldValue = newValue = *it;
- else
- m_qmlFileWithPath.insert(canonicalFilePath, newV);
- }
- }
+ const auto &fetchResult = fetchFileFromEnvs<QmlFile>(file);
+ oldValue = fetchResult.first;
+ newValue = fetchResult.second;
if (!newValue) {
- self.universe().loadFile(
- canonicalFilePath, logicalPath, code, codeDate,
- callbackForQmlFile(self, loadCallback, directDepsCallback, endCallback),
- loadOptions, fType);
+ const auto &loadRes = universe()->loadFile(file, fType, m_domCreationOptions);
+ addExternalItemInfo<QmlFile>(loadRes.currentItem,
+ getLoadCallbackFor(fType, loadCallback), endCallback);
return;
}
} break;
case DomType::QmltypesFile: {
- {
- QMutexLocker l(mutex());
- auto it = m_qmltypesFileWithPath.find(canonicalFilePath);
- if (it != m_qmltypesFileWithPath.end())
- oldValue = newValue = *it;
- }
- if (!newValue && (options() & Option::NoReload) && m_base) {
- if (auto v = m_base->qmltypesFileWithPath(self, canonicalFilePath, EnvLookup::Normal)) {
- oldValue = v;
- QDateTime now = QDateTime::currentDateTimeUtc();
- auto newV = std::make_shared<ExternalItemInfo<QmltypesFile>>(
- v->current, now, v->revision(), v->lastDataUpdateAt());
- newValue = newV;
- QMutexLocker l(mutex());
- auto it = m_qmltypesFileWithPath.find(canonicalFilePath);
- if (it != m_qmltypesFileWithPath.end())
- oldValue = newValue = *it;
- else
- m_qmltypesFileWithPath.insert(canonicalFilePath, newV);
- }
- }
+ const auto &fetchResult = fetchFileFromEnvs<QmltypesFile>(file);
+ oldValue = fetchResult.first;
+ newValue = fetchResult.second;
if (!newValue) {
- self.universe().loadFile(
- canonicalFilePath, logicalPath, code, codeDate,
- callbackForQmltypesFile(self, loadCallback, directDepsCallback, endCallback),
- loadOptions, fType);
+ const auto &loadRes = universe()->loadFile(file, fType, m_domCreationOptions);
+ addExternalItemInfo<QmltypesFile>(loadRes.currentItem,
+ getLoadCallbackFor(fType, loadCallback), endCallback);
return;
}
} break;
case DomType::QmldirFile: {
- {
- QMutexLocker l(mutex());
- auto it = m_qmldirFileWithPath.find(canonicalFilePath);
- if (it != m_qmldirFileWithPath.end())
- oldValue = newValue = *it;
- }
- if (!newValue && (options() & Option::NoReload) && m_base) {
- if (auto v = m_base->qmldirFileWithPath(self, canonicalFilePath, EnvLookup::Normal)) {
- oldValue = v;
- QDateTime now = QDateTime::currentDateTimeUtc();
- auto newV = std::make_shared<ExternalItemInfo<QmldirFile>>(
- v->current, now, v->revision(), v->lastDataUpdateAt());
- newValue = newV;
- QMutexLocker l(mutex());
- auto it = m_qmldirFileWithPath.find(canonicalFilePath);
- if (it != m_qmldirFileWithPath.end())
- oldValue = newValue = *it;
- else
- m_qmldirFileWithPath.insert(canonicalFilePath, newV);
- }
- }
+ const auto &fetchResult = fetchFileFromEnvs<QmldirFile>(file);
+ oldValue = fetchResult.first;
+ newValue = fetchResult.second;
if (!newValue) {
- self.universe().loadFile(
- canonicalFilePath, logicalPath, code, codeDate,
- callbackForQmldirFile(self, loadCallback, directDepsCallback, endCallback),
- loadOptions, fType);
+ const auto &loadRes = universe()->loadFile(file, fType, m_domCreationOptions);
+ addExternalItemInfo<QmldirFile>(loadRes.currentItem,
+ getLoadCallbackFor(fType, loadCallback), endCallback);
return;
}
} break;
+ case DomType::JsFile: {
+ const auto &loadRes = universe()->loadFile(file, fType, m_domCreationOptions);
+ addExternalItemInfo<JsFile>(loadRes.currentItem, getLoadCallbackFor(fType, loadCallback),
+ endCallback);
+ return;
+ } break;
default: {
- myErrors().error(tr("Unexpected file to load: '%1'").arg(filePath)).handle(h);
+ myErrors().error(tr("Unexpected file to load: '%1'").arg(file.canonicalPath())).handle(h);
if (loadCallback)
loadCallback(self.canonicalPath(), DomItem::empty, DomItem::empty);
- if (directDepsCallback)
- directDepsCallback(self.canonicalPath(), DomItem::empty, DomItem::empty);
if (endCallback)
endCallback(self.canonicalPath(), DomItem::empty, DomItem::empty);
return;
@@ -1353,27 +1233,34 @@ void DomEnvironment::loadFile(DomItem &self, QString filePath, QString logicalPa
DomItem newValueObj = self.copy(newValue);
loadCallback(p, oldValueObj, newValueObj);
}
- if (directDepsCallback) {
- DomItem lInfoObj = self.copy(lInfo);
- lInfo->addEndCallback(lInfoObj, directDepsCallback);
- }
} else {
self.addError(myErrors().error(tr("missing load info in ")));
if (loadCallback)
loadCallback(self.canonicalPath(), DomItem::empty, DomItem::empty);
- if (directDepsCallback)
- directDepsCallback(self.canonicalPath(), DomItem::empty, DomItem::empty);
}
if (endCallback)
- addAllLoadedCallback(self, [p, endCallback](Path, DomItem &, DomItem &env) {
+ addAllLoadedCallback(self, [p = std::move(p), endCallback](
+ const Path &, const DomItem &, const DomItem &env) {
DomItem el = env.path(p);
endCallback(p, el, el);
});
}
-void DomEnvironment::loadModuleDependency(DomItem &self, QString uri, Version v,
+void DomEnvironment::loadModuleDependency(
+ const QString &uri, Version version,
+ const std::function<void(const Path &, const DomItem &, const DomItem &)> &callback,
+ const ErrorHandler &errorHandler)
+{
+ DomItem envItem(shared_from_this());
+ if (options() & DomEnvironment::Option::NoDependencies)
+ loadModuleDependency(envItem, uri, version, callback, nullptr, errorHandler);
+ else
+ loadModuleDependency(envItem, uri, version, nullptr, callback, errorHandler);
+}
+
+void DomEnvironment::loadModuleDependency(const DomItem &self, const QString &uri, Version v,
Callback loadCallback, Callback endCallback,
- ErrorHandler errorHandler)
+ const ErrorHandler &errorHandler)
{
Q_ASSERT(!uri.contains(u'/'));
Path p = Paths::moduleIndexPath(uri, v.majorVersion);
@@ -1388,6 +1275,9 @@ void DomEnvironment::loadModuleDependency(DomItem &self, QString uri, Version v,
QRegularExpression vRe(QRegularExpression::anchoredPattern(
QRegularExpression::escape(lastComponent) + QStringLiteral(u"\\.([0-9]*)")));
const auto lPaths = loadPaths();
+ qCDebug(QQmlJSDomImporting) << "DomEnvironment::loadModuleDependency: Searching module with"
+ " uri"
+ << uri;
for (const QString &path : lPaths) {
QDir dir(path + (subPathV.isEmpty() ? QStringLiteral(u"") : QStringLiteral(u"/"))
+ subPathV);
@@ -1399,25 +1289,39 @@ void DomEnvironment::loadModuleDependency(DomItem &self, QString uri, Version v,
if (majorV > maxV) {
QFileInfo fInfo(dir.canonicalPath() + QChar(u'/') + dirNow
+ QStringLiteral(u"/qmldir"));
- if (fInfo.isFile())
+ if (fInfo.isFile()) {
+ qCDebug(QQmlJSDomImporting)
+ << "Found qmldir in " << fInfo.canonicalFilePath();
maxV = majorV;
+ }
}
}
if (!commonV && dirNow == lastComponent) {
QFileInfo fInfo(dir.canonicalPath() + QChar(u'/') + dirNow
+ QStringLiteral(u"/qmldir"));
- if (fInfo.isFile())
+ if (fInfo.isFile()) {
+ qCDebug(QQmlJSDomImporting)
+ << "Found qmldir in " << fInfo.canonicalFilePath();
commonV = true;
+ }
}
}
}
- QAtomicInt toLoad((commonV ? 1 : 0) + ((maxV >= 0) ? 1 : 0));
- auto loadCallback2 = (loadCallback ? [p, loadCallback, toLoad](Path, DomItem &, DomItem &elV) mutable {
- if (--toLoad == 0) {
- DomItem el = elV.path(p);
- loadCallback(p, el, el);
- }
- }: Callback());
+
+ // This decrements _separately_ for each copy of the lambda. So, what we get here is not a
+ // limit on the total number of calls but a limit on the number of calls per caller
+ // location. It gets even funnier if the callback is first called and then copied further.
+ // TODO: Is this the intended behavior?
+ int toLoad = (commonV ? 1 : 0) + ((maxV >= 0) ? 1 : 0);
+ const auto loadCallback2 = loadCallback
+ ? [p, loadCallback, toLoad](Path, const DomItem &, const DomItem &elV) mutable {
+ if (--toLoad == 0) {
+ DomItem el = elV.path(p);
+ loadCallback(p, el, el);
+ }
+ }
+ : Callback();
+
if (maxV >= 0)
loadModuleDependency(self, uri, Version(maxV, v.minorVersion), loadCallback2, nullptr);
if (commonV)
@@ -1425,10 +1329,15 @@ void DomEnvironment::loadModuleDependency(DomItem &self, QString uri, Version v,
loadCallback2, nullptr);
else if (maxV < 0) {
if (uri != u"QML") {
- addErrorLocal(myErrors()
- .warning(tr("Failed to find main qmldir file for %1 %2")
- .arg(uri, v.stringValue()))
- .handle());
+ const QString loadPaths = lPaths.join(u", "_s);
+ qCDebug(QQmlJSDomImporting)
+ << "DomEnvironment::loadModuleDependency: qmldir at" << (uri + u"/qmldir"_s)
+ << "was not found in " << loadPaths;
+ addErrorLocal(
+ myErrors()
+ .warning(tr("Failed to find main qmldir file for %1 %2 in %3.")
+ .arg(uri, v.stringValue(), loadPaths))
+ .handle());
}
if (loadCallback)
loadCallback(p, DomItem::empty, DomItem::empty);
@@ -1447,14 +1356,16 @@ void DomEnvironment::loadModuleDependency(DomItem &self, QString uri, Version v,
loadCallback(p, DomItem::empty, DomItem::empty);
}
}
- if (endCallback)
- addAllLoadedCallback(self, [p, endCallback](Path, DomItem &, DomItem &env) {
+ if (endCallback) {
+ addAllLoadedCallback(self, [p = std::move(p), endCallback = std::move(endCallback)](
+ Path, const DomItem &, const DomItem &env) {
DomItem el = env.path(p);
endCallback(p, el, el);
});
+ }
}
-void DomEnvironment::loadBuiltins(DomItem &self, Callback callback, ErrorHandler h)
+void DomEnvironment::loadBuiltins(const Callback &callback, const ErrorHandler &h)
{
QString builtinsName = QLatin1String("builtins.qmltypes");
const auto lPaths = loadPaths();
@@ -1462,7 +1373,8 @@ void DomEnvironment::loadBuiltins(DomItem &self, Callback callback, ErrorHandler
QDir dir(path);
QFileInfo fInfo(dir.filePath(builtinsName));
if (fInfo.isFile()) {
- self.loadFile(fInfo.canonicalFilePath(), QString(), callback, LoadOption::DefaultLoad);
+ loadFile(FileToLoad::fromFileSystem(shared_from_this(), fInfo.canonicalFilePath()),
+ callback);
return;
}
}
@@ -1517,7 +1429,7 @@ QSet<QString> DomEnvironment::getStrings(function_ref<QSet<QString>()> getBase,
return res;
}
-QSet<QString> DomEnvironment::moduleIndexUris(DomItem &, EnvLookup lookup) const
+QSet<QString> DomEnvironment::moduleIndexUris(const DomItem &, EnvLookup lookup) const
{
DomItem baseObj = DomItem(m_base);
return this->getStrings<QMap<int, std::shared_ptr<ModuleIndex>>>(
@@ -1525,7 +1437,7 @@ QSet<QString> DomEnvironment::moduleIndexUris(DomItem &, EnvLookup lookup) const
m_moduleIndexWithUri, lookup);
}
-QSet<int> DomEnvironment::moduleIndexMajorVersions(DomItem &, QString uri, EnvLookup lookup) const
+QSet<int> DomEnvironment::moduleIndexMajorVersions(const DomItem &, const QString &uri, EnvLookup lookup) const
{
QSet<int> res;
if (lookup != EnvLookup::NoBase && m_base) {
@@ -1562,7 +1474,7 @@ std::shared_ptr<ModuleIndex> DomEnvironment::lookupModuleInEnv(const QString &ur
return it->value(majorVersion); // null shared_ptr is fine if no match
}
-DomEnvironment::ModuleLookupResult DomEnvironment::moduleIndexWithUriHelper(DomItem &self, QString uri, int majorVersion, EnvLookup options) const
+DomEnvironment::ModuleLookupResult DomEnvironment::moduleIndexWithUriHelper(const DomItem &self, const QString &uri, int majorVersion, EnvLookup options) const
{
std::shared_ptr<ModuleIndex> res;
if (options != EnvLookup::BaseOnly)
@@ -1594,10 +1506,9 @@ DomEnvironment::ModuleLookupResult DomEnvironment::moduleIndexWithUriHelper(DomI
}
}
-std::shared_ptr<ModuleIndex> DomEnvironment::moduleIndexWithUri(DomItem &self, QString uri,
- int majorVersion, EnvLookup options,
- Changeable changeable,
- ErrorHandler errorHandler)
+std::shared_ptr<ModuleIndex> DomEnvironment::moduleIndexWithUri(
+ const DomItem &self, const QString &uri, int majorVersion, EnvLookup options,
+ Changeable changeable, const ErrorHandler &errorHandler)
{
// sanity checks
Q_ASSERT((changeable == Changeable::ReadOnly
@@ -1636,7 +1547,7 @@ std::shared_ptr<ModuleIndex> DomEnvironment::moduleIndexWithUri(DomItem &self, Q
auto &modsNow = m_moduleIndexWithUri[uri];
// As we do not hold the lock for the whole operation, some other thread
// might have created the module already
- if (auto it = modsNow.find(majorVersion); it != modsNow.end())
+ if (auto it = modsNow.constFind(majorVersion); it != modsNow.cend())
return *it;
modsNow.insert(majorVersion, newModulePtr);
}
@@ -1654,30 +1565,20 @@ std::shared_ptr<ModuleIndex> DomEnvironment::moduleIndexWithUri(DomItem &self, Q
return newModulePtr;
}
-std::shared_ptr<ModuleIndex> DomEnvironment::moduleIndexWithUri(DomItem &self, QString uri,
+std::shared_ptr<ModuleIndex> DomEnvironment::moduleIndexWithUri(const DomItem &self, const QString &uri,
int majorVersion,
EnvLookup options) const
{
return moduleIndexWithUriHelper(self, uri, majorVersion, options).module;
}
-
-
std::shared_ptr<ExternalItemInfo<QmlDirectory>>
-DomEnvironment::qmlDirectoryWithPath(DomItem &self, QString path, EnvLookup options) const
+DomEnvironment::qmlDirectoryWithPath(const DomItem &, const QString &path, EnvLookup options) const
{
- if (options != EnvLookup::BaseOnly) {
- QMutexLocker l(mutex());
- if (m_qmlDirectoryWithPath.contains(path))
- return m_qmlDirectoryWithPath.value(path);
- }
- if (options != EnvLookup::NoBase && m_base) {
- return m_base->qmlDirectoryWithPath(self, path, options);
- }
- return {};
+ return lookup<QmlDirectory>(path, options);
}
-QSet<QString> DomEnvironment::qmlDirectoryPaths(DomItem &, EnvLookup options) const
+QSet<QString> DomEnvironment::qmlDirectoryPaths(const DomItem &, EnvLookup options) const
{
return getStrings<std::shared_ptr<ExternalItemInfo<QmlDirectory>>>(
[this] {
@@ -1688,20 +1589,12 @@ QSet<QString> DomEnvironment::qmlDirectoryPaths(DomItem &, EnvLookup options) co
}
std::shared_ptr<ExternalItemInfo<QmldirFile>>
-DomEnvironment::qmldirFileWithPath(DomItem &self, QString path, EnvLookup options) const
+DomEnvironment::qmldirFileWithPath(const DomItem &, const QString &path, EnvLookup options) const
{
- if (options != EnvLookup::BaseOnly) {
- QMutexLocker l(mutex());
- auto it = m_qmldirFileWithPath.find(path);
- if (it != m_qmldirFileWithPath.end())
- return *it;
- }
- if (options != EnvLookup::NoBase && m_base)
- return m_base->qmldirFileWithPath(self, path, options);
- return {};
+ return lookup<QmldirFile>(path, options);
}
-QSet<QString> DomEnvironment::qmldirFilePaths(DomItem &, EnvLookup lOptions) const
+QSet<QString> DomEnvironment::qmldirFilePaths(const DomItem &, EnvLookup lOptions) const
{
return getStrings<std::shared_ptr<ExternalItemInfo<QmldirFile>>>(
[this] {
@@ -1711,7 +1604,7 @@ QSet<QString> DomEnvironment::qmldirFilePaths(DomItem &, EnvLookup lOptions) con
m_qmldirFileWithPath, lOptions);
}
-std::shared_ptr<ExternalItemInfoBase> DomEnvironment::qmlDirWithPath(DomItem &self, QString path,
+std::shared_ptr<ExternalItemInfoBase> DomEnvironment::qmlDirWithPath(const DomItem &self, const QString &path,
EnvLookup options) const
{
if (auto qmldirFile = qmldirFileWithPath(self, path + QLatin1String("/qmldir"), options))
@@ -1719,7 +1612,7 @@ std::shared_ptr<ExternalItemInfoBase> DomEnvironment::qmlDirWithPath(DomItem &se
return qmlDirectoryWithPath(self, path, options);
}
-QSet<QString> DomEnvironment::qmlDirPaths(DomItem &self, EnvLookup options) const
+QSet<QString> DomEnvironment::qmlDirPaths(const DomItem &self, EnvLookup options) const
{
QSet<QString> res = qmlDirectoryPaths(self, options);
const auto qmldirFiles = qmldirFilePaths(self, options);
@@ -1737,20 +1630,12 @@ QSet<QString> DomEnvironment::qmlDirPaths(DomItem &self, EnvLookup options) cons
}
std::shared_ptr<ExternalItemInfo<QmlFile>>
-DomEnvironment::qmlFileWithPath(DomItem &self, QString path, EnvLookup options) const
+DomEnvironment::qmlFileWithPath(const DomItem &, const QString &path, EnvLookup options) const
{
- if (options != EnvLookup::BaseOnly) {
- QMutexLocker l(mutex());
- auto it = m_qmlFileWithPath.find(path);
- if (it != m_qmlFileWithPath.end())
- return *it;
- }
- if (options != EnvLookup::NoBase && m_base)
- return m_base->qmlFileWithPath(self, path, options);
- return {};
+ return lookup<QmlFile>(path, options);
}
-QSet<QString> DomEnvironment::qmlFilePaths(DomItem &, EnvLookup lookup) const
+QSet<QString> DomEnvironment::qmlFilePaths(const DomItem &, EnvLookup lookup) const
{
return getStrings<std::shared_ptr<ExternalItemInfo<QmlFile>>>(
[this] {
@@ -1761,19 +1646,12 @@ QSet<QString> DomEnvironment::qmlFilePaths(DomItem &, EnvLookup lookup) const
}
std::shared_ptr<ExternalItemInfo<JsFile>>
-DomEnvironment::jsFileWithPath(DomItem &self, QString path, EnvLookup options) const
+DomEnvironment::jsFileWithPath(const DomItem &, const QString &path, EnvLookup options) const
{
- if (options != EnvLookup::BaseOnly) {
- QMutexLocker l(mutex());
- if (m_jsFileWithPath.contains(path))
- return m_jsFileWithPath.value(path);
- }
- if (options != EnvLookup::NoBase && m_base)
- return m_base->jsFileWithPath(self, path, EnvLookup::Normal);
- return {};
+ return lookup<JsFile>(path, options);
}
-QSet<QString> DomEnvironment::jsFilePaths(DomItem &, EnvLookup lookup) const
+QSet<QString> DomEnvironment::jsFilePaths(const DomItem &, EnvLookup lookup) const
{
return getStrings<std::shared_ptr<ExternalItemInfo<JsFile>>>(
[this] {
@@ -1784,19 +1662,12 @@ QSet<QString> DomEnvironment::jsFilePaths(DomItem &, EnvLookup lookup) const
}
std::shared_ptr<ExternalItemInfo<QmltypesFile>>
-DomEnvironment::qmltypesFileWithPath(DomItem &self, QString path, EnvLookup options) const
+DomEnvironment::qmltypesFileWithPath(const DomItem &, const QString &path, EnvLookup options) const
{
- if (options != EnvLookup::BaseOnly) {
- QMutexLocker l(mutex());
- if (m_qmltypesFileWithPath.contains(path))
- return m_qmltypesFileWithPath.value(path);
- }
- if (options != EnvLookup::NoBase && m_base)
- return m_base->qmltypesFileWithPath(self, path, EnvLookup::Normal);
- return {};
+ return lookup<QmltypesFile>(path, options);
}
-QSet<QString> DomEnvironment::qmltypesFilePaths(DomItem &, EnvLookup lookup) const
+QSet<QString> DomEnvironment::qmltypesFilePaths(const DomItem &, EnvLookup lookup) const
{
return getStrings<std::shared_ptr<ExternalItemInfo<QmltypesFile>>>(
[this] {
@@ -1807,21 +1678,14 @@ QSet<QString> DomEnvironment::qmltypesFilePaths(DomItem &, EnvLookup lookup) con
}
std::shared_ptr<ExternalItemInfo<GlobalScope>>
-DomEnvironment::globalScopeWithName(DomItem &self, QString name, EnvLookup lookupOptions) const
+DomEnvironment::globalScopeWithName(const DomItem &, const QString &name,
+ EnvLookup lookupOptions) const
{
- if (lookupOptions != EnvLookup::BaseOnly) {
- QMutexLocker l(mutex());
- auto id = m_globalScopeWithName.find(name);
- if (id != m_globalScopeWithName.end())
- return *id;
- }
- if (lookupOptions != EnvLookup::NoBase && m_base)
- return m_base->globalScopeWithName(self, name, lookupOptions);
- return {};
+ return lookup<GlobalScope>(name, lookupOptions);
}
std::shared_ptr<ExternalItemInfo<GlobalScope>>
-DomEnvironment::ensureGlobalScopeWithName(DomItem &self, QString name, EnvLookup lookupOptions)
+DomEnvironment::ensureGlobalScopeWithName(const DomItem &self, const QString &name, EnvLookup lookupOptions)
{
if (auto current = globalScopeWithName(self, name, lookupOptions))
return current;
@@ -1844,7 +1708,7 @@ DomEnvironment::ensureGlobalScopeWithName(DomItem &self, QString name, EnvLookup
return {};
}
-QSet<QString> DomEnvironment::globalScopeNames(DomItem &, EnvLookup lookupOptions) const
+QSet<QString> DomEnvironment::globalScopeNames(const DomItem &, EnvLookup lookupOptions) const
{
QSet<QString> res;
if (lookupOptions != EnvLookup::NoBase && m_base) {
@@ -1869,7 +1733,26 @@ QSet<QString> DomEnvironment::globalScopeNames(DomItem &, EnvLookup lookupOption
return res;
}
-void DomEnvironment::addLoadInfo(DomItem &self, std::shared_ptr<LoadInfo> loadInfo)
+/*!
+ \internal
+ Depending on the creation options, this function adds LoadInfo of the provided path
+*/
+void DomEnvironment::addDependenciesToLoad(const Path &path)
+{
+ if (options() & Option::NoDependencies) {
+ return;
+ }
+ Q_ASSERT(path);
+ const auto loadInfo = std::make_shared<LoadInfo>(path);
+ return addLoadInfo(DomItem(shared_from_this()), loadInfo);
+}
+
+/*!
+ \internal
+ Enqueues path to the m_loadsWithWork (queue of the pending "load" jobs).
+ In simpler words, schedule the load of the dependencies of the path from loadInfo.
+*/
+void DomEnvironment::addLoadInfo(const DomItem &self, const std::shared_ptr<LoadInfo> &loadInfo)
{
if (!loadInfo)
return;
@@ -1891,7 +1774,7 @@ void DomEnvironment::addLoadInfo(DomItem &self, std::shared_ptr<LoadInfo> loadIn
}
}
-std::shared_ptr<LoadInfo> DomEnvironment::loadInfo(Path path) const
+std::shared_ptr<LoadInfo> DomEnvironment::loadInfo(const Path &path) const
{
QMutexLocker l(mutex());
return m_loadInfos.value(path);
@@ -1909,145 +1792,163 @@ QList<Path> DomEnvironment::loadInfoPaths() const
return lInfos.keys();
}
-DomItem::Callback DomEnvironment::callbackForQmlDirectory(DomItem &self, Callback loadCallback,
- Callback allDirectDepsCallback,
- Callback endCallback)
+DomItem::Callback DomEnvironment::getLoadCallbackFor(DomType fileType, const Callback &loadCallback)
{
- return envCallbackForFile<QmlDirectory>(self, &DomEnvironment::m_qmlDirectoryWithPath,
- &DomEnvironment::qmlDirectoryWithPath, loadCallback,
- allDirectDepsCallback, endCallback);
+ if (fileType == DomType::QmltypesFile) {
+ return [loadCallback](const Path &p, const DomItem &oldV, const DomItem &newV) {
+ DomItem newFile = newV.field(Fields::currentItem);
+ if (std::shared_ptr<QmltypesFile> newFilePtr = newFile.ownerAs<QmltypesFile>())
+ newFilePtr->ensureInModuleIndex(newFile);
+ if (loadCallback)
+ loadCallback(p, oldV, newV);
+ };
+ }
+ return loadCallback;
}
-DomItem::Callback DomEnvironment::callbackForQmlFile(DomItem &self, Callback loadCallback,
- Callback allDirectDepsCallback,
- Callback endCallback)
+DomEnvironment::DomEnvironment(const QStringList &loadPaths, Options options,
+ DomCreationOptions domCreationOptions,
+ const shared_ptr<DomUniverse> &universe)
+ : m_options(options),
+ m_universe(DomUniverse::guaranteeUniverse(universe)),
+ m_loadPaths(loadPaths),
+ m_implicitImports(defaultImplicitImports()),
+ m_domCreationOptions(domCreationOptions)
+
{
- return envCallbackForFile<QmlFile>(self, &DomEnvironment::m_qmlFileWithPath,
- &DomEnvironment::qmlFileWithPath, loadCallback,
- allDirectDepsCallback, endCallback);
}
-DomTop::Callback DomEnvironment::callbackForQmltypesFile(DomItem &self,
- DomTop::Callback loadCallback,
- Callback allDirectDepsCallback,
- DomTop::Callback endCallback)
-{
- return envCallbackForFile<QmltypesFile>(
- self, &DomEnvironment::m_qmltypesFileWithPath, &DomEnvironment::qmltypesFileWithPath,
- [loadCallback](Path p, DomItem &oldV, DomItem &newV) {
- DomItem newFile = newV.field(Fields::currentItem);
- if (std::shared_ptr<QmltypesFile> newFilePtr = newFile.ownerAs<QmltypesFile>())
- newFilePtr->ensureInModuleIndex(newFile);
- if (loadCallback)
- loadCallback(p, oldV, newV);
- },
- allDirectDepsCallback, endCallback);
+/*!
+\internal
+Do not call this method inside of DomEnvironment's constructor! It requires weak_from_this() that
+only works after the constructor call finished.
+*/
+DomEnvironment::SemanticAnalysis &DomEnvironment::semanticAnalysis()
+{
+ // QTBUG-124799: do not create a SemanticAnalysis in a temporary DomEnvironment, and use the one
+ // from the base environment instead.
+ if (m_base) {
+ auto &result = m_base->semanticAnalysis();
+ result.setLoadPaths(m_loadPaths);
+ return result;
+ }
+
+ if (m_semanticAnalysis)
+ return *m_semanticAnalysis;
+
+ Q_ASSERT(domCreationOptions().testFlag(DomCreationOption::WithSemanticAnalysis));
+ m_semanticAnalysis = SemanticAnalysis(m_loadPaths);
+ return *m_semanticAnalysis;
}
-DomTop::Callback DomEnvironment::callbackForQmldirFile(DomItem &self, DomTop::Callback loadCallback,
- Callback allDirectDepsCallback,
- DomTop::Callback endCallback)
+DomEnvironment::SemanticAnalysis::SemanticAnalysis(const QStringList &loadPaths)
+ : m_mapper(
+ std::make_shared<QQmlJSResourceFileMapper>(resourceFilesFromBuildFolders(loadPaths))),
+ m_importer(std::make_shared<QQmlJSImporter>(loadPaths, m_mapper.get(), true))
{
- return envCallbackForFile<QmldirFile>(self, &DomEnvironment::m_qmldirFileWithPath,
- &DomEnvironment::qmldirFileWithPath, loadCallback,
- allDirectDepsCallback, endCallback);
}
-DomEnvironment::DomEnvironment(QStringList loadPaths, Options options,
- shared_ptr<DomUniverse> universe)
- : m_options(options),
- m_universe(DomUniverse::guaranteeUniverse(universe)),
- m_loadPaths(loadPaths),
- m_implicitImports(defaultImplicitImports())
-{}
+void DomEnvironment::SemanticAnalysis::setLoadPaths(const QStringList &loadPaths)
+{
+ if (loadPaths == m_importer->importPaths())
+ return;
+
+ m_importer->setImportPaths(loadPaths);
+}
-DomItem DomEnvironment::create(QStringList loadPaths, Options options, DomItem &universe)
+std::shared_ptr<DomEnvironment> DomEnvironment::create(const QStringList &loadPaths,
+ Options options,
+ DomCreationOptions domCreationOptions,
+ const DomItem &universe)
{
std::shared_ptr<DomUniverse> universePtr = universe.ownerAs<DomUniverse>();
- auto envPtr = std::make_shared<DomEnvironment>(loadPaths, options, universePtr);
- return DomItem(envPtr);
+ return std::make_shared<DomEnvironment>(loadPaths, options, domCreationOptions, universePtr);
}
-DomEnvironment::DomEnvironment(shared_ptr<DomEnvironment> parent, QStringList loadPaths,
- Options options)
+DomEnvironment::DomEnvironment(const shared_ptr<DomEnvironment> &parent,
+ const QStringList &loadPaths, Options options,
+ DomCreationOptions domCreationOptions)
: m_options(options),
m_base(parent),
m_loadPaths(loadPaths),
- m_implicitImports(defaultImplicitImports())
-{}
+ m_implicitImports(defaultImplicitImports()),
+ m_domCreationOptions(domCreationOptions)
+{
+}
-template<typename T>
-std::shared_ptr<ExternalItemInfo<T>>
-addExternalItem(std::shared_ptr<T> file, QString key,
- QMap<QString, std::shared_ptr<ExternalItemInfo<T>>> &map, AddOption option,
- QBasicMutex *mutex)
+void DomEnvironment::addQmlFile(const std::shared_ptr<QmlFile> &file, AddOption options)
{
- if (!file)
- return {};
- auto eInfo = std::make_shared<ExternalItemInfo<T>>(
- file, QDateTime::currentDateTimeUtc());
- {
- QMutexLocker l(mutex);
- auto it = map.find(key);
- if (it != map.end()) {
- switch (option) {
- case AddOption::KeepExisting:
- eInfo = *it;
- break;
- case AddOption::Overwrite:
- map.insert(key, eInfo);
- break;
- }
- } else {
- map.insert(key, eInfo);
- }
+ addExternalItem(file, file->canonicalFilePath(), options);
+ if (domCreationOptions().testFlag(DomCreationOption::WithSemanticAnalysis)) {
+ const QQmlJSScope::Ptr &handle =
+ semanticAnalysis().m_importer->importFile(file->canonicalFilePath());
+
+ // force reset the outdated qqmljsscope in case it was already populated
+ QDeferredFactory<QQmlJSScope> newFactory(semanticAnalysis().m_importer.get(),
+ file->canonicalFilePath(),
+ TypeReader{ weak_from_this() });
+ file->setHandleForPopulation(handle);
+ handle.resetFactory(std::move(newFactory));
}
- return eInfo;
}
-std::shared_ptr<ExternalItemInfo<QmlFile>> DomEnvironment::addQmlFile(std::shared_ptr<QmlFile> file,
- AddOption options)
+void DomEnvironment::addQmlDirectory(const std::shared_ptr<QmlDirectory> &file, AddOption options)
{
- return addExternalItem<QmlFile>(file, file->canonicalFilePath(), m_qmlFileWithPath, options,
- mutex());
+ addExternalItem(file, file->canonicalFilePath(), options);
}
-std::shared_ptr<ExternalItemInfo<QmlDirectory>>
-DomEnvironment::addQmlDirectory(std::shared_ptr<QmlDirectory> file, AddOption options)
+void DomEnvironment::addQmldirFile(const std::shared_ptr<QmldirFile> &file, AddOption options)
{
- return addExternalItem<QmlDirectory>(file, file->canonicalFilePath(), m_qmlDirectoryWithPath,
- options, mutex());
+ addExternalItem(file, file->canonicalFilePath(), options);
}
-std::shared_ptr<ExternalItemInfo<QmldirFile>>
-DomEnvironment::addQmldirFile(std::shared_ptr<QmldirFile> file, AddOption options)
+void DomEnvironment::addQmltypesFile(const std::shared_ptr<QmltypesFile> &file, AddOption options)
{
- return addExternalItem<QmldirFile>(file, file->canonicalFilePath(), m_qmldirFileWithPath,
- options, mutex());
+ addExternalItem(file, file->canonicalFilePath(), options);
}
-std::shared_ptr<ExternalItemInfo<QmltypesFile>>
-DomEnvironment::addQmltypesFile(std::shared_ptr<QmltypesFile> file, AddOption options)
+void DomEnvironment::addJsFile(const std::shared_ptr<JsFile> &file, AddOption options)
{
- return addExternalItem<QmltypesFile>(file, file->canonicalFilePath(), m_qmltypesFileWithPath,
- options, mutex());
+ addExternalItem(file, file->canonicalFilePath(), options);
}
-std::shared_ptr<ExternalItemInfo<JsFile>> DomEnvironment::addJsFile(std::shared_ptr<JsFile> file,
- AddOption options)
+void DomEnvironment::addGlobalScope(const std::shared_ptr<GlobalScope> &scope, AddOption options)
{
- return addExternalItem<JsFile>(file, file->canonicalFilePath(), m_jsFileWithPath, options,
- mutex());
+ addExternalItem(scope, scope->name(), options);
}
-std::shared_ptr<ExternalItemInfo<GlobalScope>>
-DomEnvironment::addGlobalScope(std::shared_ptr<GlobalScope> scope, AddOption options)
+QList<QQmlJS::DiagnosticMessage>
+DomEnvironment::TypeReader::operator()(QQmlJSImporter *importer, const QString &filePath,
+ const QSharedPointer<QQmlJSScope> &scopeToPopulate)
{
- return addExternalItem<GlobalScope>(scope, scope->name(), m_globalScopeWithName, options,
- mutex());
+ Q_UNUSED(importer);
+ Q_UNUSED(scopeToPopulate);
+
+ const QFileInfo info{ filePath };
+ const QString baseName = info.baseName();
+ scopeToPopulate->setInternalName(baseName.endsWith(QStringLiteral(".ui")) ? baseName.chopped(3)
+ : baseName);
+
+ std::shared_ptr<DomEnvironment> envPtr = m_env.lock();
+ // populate QML File if from implicit import directory
+ // use the version in DomEnvironment and do *not* load from disk.
+ auto it = envPtr->m_qmlFileWithPath.constFind(filePath);
+ if (it == envPtr->m_qmlFileWithPath.constEnd()) {
+ qCDebug(domLog) << "Import visitor tried to lazily load file \"" << filePath
+ << "\", but that file was not found in the DomEnvironment. Was this "
+ "file not discovered by the Dom's dependency loading mechanism?";
+ return { QQmlJS::DiagnosticMessage{
+ u"Could not find file \"%1\" in the Dom."_s.arg(filePath), QtMsgType::QtWarningMsg,
+ SourceLocation{} } };
+ }
+ const DomItem qmlFile = it.value()->currentItem(DomItem(envPtr));
+ envPtr->populateFromQmlFile(MutableDomItem(qmlFile));
+ return {};
}
-bool DomEnvironment::commitToBase(DomItem &self, shared_ptr<DomEnvironment> validEnvPtr)
+
+bool DomEnvironment::commitToBase(
+ const DomItem &self, const shared_ptr<DomEnvironment> &validEnvPtr)
{
if (!base())
return false;
@@ -2059,6 +1960,7 @@ bool DomEnvironment::commitToBase(DomItem &self, shared_ptr<DomEnvironment> vali
QMap<QString, std::shared_ptr<ExternalItemInfo<JsFile>>> my_jsFileWithPath;
QMap<QString, std::shared_ptr<ExternalItemInfo<QmltypesFile>>> my_qmltypesFileWithPath;
QHash<Path, std::shared_ptr<LoadInfo>> my_loadInfos;
+ std::optional<SemanticAnalysis> my_semanticAnalysis;
{
QMutexLocker l(mutex());
my_moduleIndexWithUri = m_moduleIndexWithUri;
@@ -2069,9 +1971,11 @@ bool DomEnvironment::commitToBase(DomItem &self, shared_ptr<DomEnvironment> vali
my_jsFileWithPath = m_jsFileWithPath;
my_qmltypesFileWithPath = m_qmltypesFileWithPath;
my_loadInfos = m_loadInfos;
+ my_semanticAnalysis = semanticAnalysis();
}
{
QMutexLocker lBase(base()->mutex()); // be more careful about makeCopy calls with lock?
+ m_base->m_semanticAnalysis = my_semanticAnalysis;
m_base->m_globalScopeWithName.insert(my_globalScopeWithName);
m_base->m_qmlDirectoryWithPath.insert(my_qmlDirectoryWithPath);
m_base->m_qmldirFileWithPath.insert(my_qmldirFileWithPath);
@@ -2099,28 +2003,31 @@ bool DomEnvironment::commitToBase(DomItem &self, shared_ptr<DomEnvironment> vali
}
}
}
- if (validEnvPtr) {
+ if (validEnvPtr)
+ m_lastValidBase = validEnvPtr;
+ if (m_lastValidBase) {
QMutexLocker lValid(
- validEnvPtr->mutex()); // be more careful about makeCopy calls with lock?
- validEnvPtr->m_globalScopeWithName.insert(my_globalScopeWithName);
- validEnvPtr->m_qmlDirectoryWithPath.insert(my_qmlDirectoryWithPath);
- validEnvPtr->m_qmldirFileWithPath.insert(my_qmldirFileWithPath);
+ m_lastValidBase->mutex()); // be more careful about makeCopy calls with lock?
+ m_lastValidBase->m_semanticAnalysis = my_semanticAnalysis;
+ m_lastValidBase->m_globalScopeWithName.insert(my_globalScopeWithName);
+ m_lastValidBase->m_qmlDirectoryWithPath.insert(my_qmlDirectoryWithPath);
+ m_lastValidBase->m_qmldirFileWithPath.insert(my_qmldirFileWithPath);
for (auto it = my_qmlFileWithPath.cbegin(), end = my_qmlFileWithPath.cend(); it != end;
++it) {
if (it.value() && it.value()->current && it.value()->current->isValid())
- validEnvPtr->m_qmlFileWithPath.insert(it.key(), it.value());
+ m_lastValidBase->m_qmlFileWithPath.insert(it.key(), it.value());
}
for (auto it = my_jsFileWithPath.cbegin(), end = my_jsFileWithPath.cend(); it != end;
++it) {
if (it.value() && it.value()->current && it.value()->current->isValid())
- validEnvPtr->m_jsFileWithPath.insert(it.key(), it.value());
+ m_lastValidBase->m_jsFileWithPath.insert(it.key(), it.value());
}
- validEnvPtr->m_qmltypesFileWithPath.insert(my_qmltypesFileWithPath);
- validEnvPtr->m_loadInfos.insert(my_loadInfos);
+ m_lastValidBase->m_qmltypesFileWithPath.insert(my_qmltypesFileWithPath);
+ m_lastValidBase->m_loadInfos.insert(my_loadInfos);
for (auto it = my_moduleIndexWithUri.cbegin(), end = my_moduleIndexWithUri.cend();
it != end; ++it) {
QMap<int, shared_ptr<ModuleIndex>> &myVersions =
- validEnvPtr->m_moduleIndexWithUri[it.key()];
+ m_lastValidBase->m_moduleIndexWithUri[it.key()];
for (auto it2 = it.value().cbegin(), end2 = it.value().cend(); it2 != end2; ++it2) {
auto oldV = myVersions.value(it2.key());
DomItem it2Obj = self.copy(it2.value());
@@ -2130,11 +2037,31 @@ bool DomEnvironment::commitToBase(DomItem &self, shared_ptr<DomEnvironment> vali
}
}
}
+
+ auto newBaseForPopulation =
+ m_lastValidBase ? m_lastValidBase->weak_from_this() : m_base->weak_from_this();
+ // adapt the factory to the use the base or valid environment for unpopulated files, instead of
+ // the current environment which will very probably be destroyed anytime soon
+ for (const auto &qmlFile : my_qmlFileWithPath) {
+ if (!qmlFile || !qmlFile->current)
+ continue;
+ QQmlJSScope::ConstPtr handle = qmlFile->current->handleForPopulation();
+ if (!handle)
+ continue;
+ auto oldFactory = handle.factory();
+ if (!oldFactory)
+ continue;
+
+ const QDeferredFactory<QQmlJSScope> newFactory(
+ oldFactory->importer(), oldFactory->filePath(), TypeReader{ newBaseForPopulation });
+ handle.resetFactory(newFactory);
+ }
return true;
}
-void DomEnvironment::loadPendingDependencies(DomItem &self)
+void DomEnvironment::loadPendingDependencies()
{
+ DomItem self(shared_from_this());
while (true) {
Path elToDo;
std::shared_ptr<LoadInfo> loadInfo;
@@ -2147,7 +2074,7 @@ void DomEnvironment::loadPendingDependencies(DomItem &self)
loadInfo = m_loadInfos.value(elToDo);
}
if (loadInfo) {
- auto cleanup = qScopeGuard([this, elToDo, &self] {
+ auto cleanup = qScopeGuard([this, &elToDo, &self] {
QList<Callback> endCallbacks;
{
QMutexLocker l(mutex());
@@ -2176,12 +2103,12 @@ void DomEnvironment::loadPendingDependencies(DomItem &self)
}
}
-bool DomEnvironment::finishLoadingDependencies(DomItem &self, int waitMSec)
+bool DomEnvironment::finishLoadingDependencies(int waitMSec)
{
bool hasPendingLoads = true;
QDateTime endTime = QDateTime::currentDateTimeUtc().addMSecs(waitMSec);
for (int i = 0; i < waitMSec / 10 + 2; ++i) {
- loadPendingDependencies(self);
+ loadPendingDependencies();
auto lInfos = loadInfos();
auto it = lInfos.cbegin();
auto end = lInfos.cend();
@@ -2204,7 +2131,7 @@ bool DomEnvironment::finishLoadingDependencies(DomItem &self, int waitMSec)
return !hasPendingLoads;
}
-void DomEnvironment::addWorkForLoadInfo(Path elementCanonicalPath)
+void DomEnvironment::addWorkForLoadInfo(const Path &elementCanonicalPath)
{
QMutexLocker l(mutex());
m_loadsWithWork.enqueue(elementCanonicalPath);
@@ -2224,6 +2151,9 @@ void DomEnvironment::setLoadPaths(const QStringList &v)
{
QMutexLocker l(mutex());
m_loadPaths = v;
+
+ if (m_semanticAnalysis)
+ m_semanticAnalysis->setLoadPaths(v);
}
QStringList DomEnvironment::loadPaths() const
@@ -2232,6 +2162,12 @@ QStringList DomEnvironment::loadPaths() const
return m_loadPaths;
}
+QStringList DomEnvironment::qmldirFiles() const
+{
+ QMutexLocker l(mutex());
+ return m_qmldirFileWithPath.keys();
+}
+
QString DomEnvironment::globalScopeName() const
{
return m_globalScopeName;
@@ -2248,7 +2184,7 @@ QList<Import> DomEnvironment::implicitImports() const
return m_implicitImports;
}
-void DomEnvironment::addAllLoadedCallback(DomItem &self, DomTop::Callback c)
+void DomEnvironment::addAllLoadedCallback(const DomItem &self, DomTop::Callback c)
{
if (c) {
bool immediate = false;
@@ -2269,14 +2205,58 @@ void DomEnvironment::clearReferenceCache()
m_referenceCache.clear();
}
-QString ExternalItemInfoBase::canonicalFilePath(DomItem &self) const
+void DomEnvironment::populateFromQmlFile(MutableDomItem &&qmlFile)
+{
+ if (std::shared_ptr<QmlFile> qmlFilePtr = qmlFile.ownerAs<QmlFile>()) {
+ QQmlJSLogger logger;
+ logger.setFileName(qmlFile.canonicalFilePath());
+ logger.setCode(qmlFilePtr->code());
+ logger.setSilent(true);
+
+ auto setupFile = [&qmlFilePtr, &qmlFile, this](auto &&visitor) {
+ Q_UNUSED(this); // note: integrity requires "this" to be in the capture list, while
+ // other compilers complain about "this" being unused in the lambda
+ AST::Node::accept(qmlFilePtr->ast(), visitor);
+ CommentCollector collector(qmlFile);
+ collector.collectComments();
+ };
+
+ if (m_domCreationOptions.testFlag(DomCreationOption::WithSemanticAnalysis)) {
+ auto &analysis = semanticAnalysis();
+ auto scope = analysis.m_importer->importFile(qmlFile.canonicalFilePath());
+ auto v = std::make_unique<QQmlDomAstCreatorWithQQmlJSScope>(scope, qmlFile, &logger,
+ analysis.m_importer.get());
+ v->enableLoadFileLazily(true);
+ v->enableScriptExpressions(m_domCreationOptions.testFlag(DomCreationOption::WithScriptExpressions));
+
+ setupFile(v.get());
+
+ auto typeResolver =
+ std::make_shared<QQmlJSTypeResolver>(analysis.m_importer.get());
+ typeResolver->init(&v->scopeCreator(), nullptr);
+ qmlFilePtr->setTypeResolverWithDependencies(typeResolver,
+ { analysis.m_importer, analysis.m_mapper });
+ } else {
+ auto v = std::make_unique<QQmlDomAstCreator>(qmlFile);
+ v->enableScriptExpressions(
+ m_domCreationOptions.testFlag(DomCreationOption::WithScriptExpressions));
+
+ setupFile(v.get());
+ }
+ } else {
+ qCWarning(domLog) << "populateQmlFile called on non qmlFile";
+ return;
+ }
+}
+
+QString ExternalItemInfoBase::canonicalFilePath(const DomItem &self) const
{
shared_ptr<ExternalOwningItem> current = currentItem();
DomItem currentObj = currentItem(self);
return current->canonicalFilePath(currentObj);
}
-bool ExternalItemInfoBase::iterateDirectSubpaths(DomItem &self, DirectVisitor visitor)
+bool ExternalItemInfoBase::iterateDirectSubpaths(const DomItem &self, DirectVisitor visitor) const
{
if (!self.dvValueLazyField(visitor, Fields::currentRevision,
[this, &self]() { return currentRevision(self); }))
@@ -2296,38 +2276,38 @@ bool ExternalItemInfoBase::iterateDirectSubpaths(DomItem &self, DirectVisitor vi
return true;
}
-int ExternalItemInfoBase::currentRevision(DomItem &) const
+int ExternalItemInfoBase::currentRevision(const DomItem &) const
{
return currentItem()->revision();
}
-int ExternalItemInfoBase::lastRevision(DomItem &self) const
+int ExternalItemInfoBase::lastRevision(const DomItem &self) const
{
Path p = currentItem()->canonicalPath();
DomItem lastValue = self.universe()[p.mid(1, p.length() - 1)].field(u"revision");
return static_cast<int>(lastValue.value().toInteger(0));
}
-int ExternalItemInfoBase::lastValidRevision(DomItem &self) const
+int ExternalItemInfoBase::lastValidRevision(const DomItem &self) const
{
Path p = currentItem()->canonicalPath();
DomItem lastValidValue = self.universe()[p.mid(1, p.length() - 2)].field(u"validItem").field(u"revision");
return static_cast<int>(lastValidValue.value().toInteger(0));
}
-QString ExternalItemPairBase::canonicalFilePath(DomItem &) const
+QString ExternalItemPairBase::canonicalFilePath(const DomItem &) const
{
shared_ptr<ExternalOwningItem> current = currentItem();
return current->canonicalFilePath();
}
-Path ExternalItemPairBase::canonicalPath(DomItem &) const
+Path ExternalItemPairBase::canonicalPath(const DomItem &) const
{
shared_ptr<ExternalOwningItem> current = currentItem();
return current->canonicalPath().dropTail();
}
-bool ExternalItemPairBase::iterateDirectSubpaths(DomItem &self, DirectVisitor visitor)
+bool ExternalItemPairBase::iterateDirectSubpaths(const DomItem &self, DirectVisitor visitor) const
{
if (!self.dvValueLazyField(visitor, Fields::currentIsValid,
[this]() { return currentIsValid(); }))
@@ -2349,7 +2329,7 @@ bool ExternalItemPairBase::currentIsValid() const
return currentItem() == validItem();
}
-RefCacheEntry RefCacheEntry::forPath(DomItem &el, Path canonicalPath)
+RefCacheEntry RefCacheEntry::forPath(const DomItem &el, const Path &canonicalPath)
{
DomItem env = el.environment();
std::shared_ptr<DomEnvironment> envPtr = env.ownerAs<DomEnvironment>();
@@ -2365,7 +2345,7 @@ RefCacheEntry RefCacheEntry::forPath(DomItem &el, Path canonicalPath)
return cached;
}
-bool RefCacheEntry::addForPath(DomItem &el, Path canonicalPath, const RefCacheEntry &entry,
+bool RefCacheEntry::addForPath(const DomItem &el, const Path &canonicalPath, const RefCacheEntry &entry,
AddOption addOption)
{
DomItem env = el.environment();