Qt
Internal/Contributor docs for the Qt SDK. Note: These are NOT official API docs; those are found at https://doc.qt.io/
Loading...
Searching...
No Matches
qqmldomtop.cpp
Go to the documentation of this file.
1// Copyright (C) 2020 The Qt Company Ltd.
2// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only
3// Qt-Security score:significant
4
6#include "qqmldomtop_p.h"
14
15#include <QtQml/private/qqmljslexer_p.h>
16#include <QtQml/private/qqmljsparser_p.h>
17#include <QtQml/private/qqmljsengine_p.h>
18#include <QtQml/private/qqmljsastvisitor_p.h>
19#include <QtQml/private/qqmljsast_p.h>
20
21#include <QtQmlCompiler/private/qqmljsutils_p.h>
22
23#include <QtCore/QBasicMutex>
24#include <QtCore/QCborArray>
25#include <QtCore/QDebug>
26#include <QtCore/QDir>
27#include <QtCore/QFile>
28#include <QtCore/QFileInfo>
29#include <QtCore/QRegularExpression>
30#include <QtCore/QScopeGuard>
31#if QT_FEATURE_thread
32# include <QtCore/QThread>
33#endif
34
35#include <memory>
36
38
39using namespace Qt::StringLiterals;
40
41namespace QQmlJS {
42namespace Dom {
43
44using std::shared_ptr;
45
46
47/*!
48 \internal
49 \brief QQml::Dom::DomTop::loadFile
50 \param filePath
51 the file path to load
52 \param logicalPath
53 the path from the
54 \param callback
55 a callback called with an canonical path, the old value, and the current value.
56 \param loadOptions are
57 if force is true the file is always read
58 */
59
61{
62 return canonicalPath();
63}
64
66{
67 return DomItem();
68}
69
70bool DomTop::iterateDirectSubpaths(const DomItem &self, DirectVisitor visitor) const
71{
72 static QHash<QString, QString> knownFields;
73 static QBasicMutex m;
74 auto toField = [](const QString &f) mutable -> QStringView {
75 QMutexLocker l(&m);
76 if (!knownFields.contains(f))
77 knownFields[f] = f;
78 return knownFields[f];
79 };
80 bool cont = true;
81 auto objs = m_extraOwningItems;
82 auto itO = objs.cbegin();
83 auto endO = objs.cend();
84 while (itO != endO) {
85 cont = cont && self.dvItemField(visitor, toField(itO.key()), [&self, &itO]() {
86 return std::visit([&self](auto &&el) { return self.copy(el); }, *itO);
87 });
88 ++itO;
89 }
90 return cont;
91}
92
94{
95 QMutexLocker l(mutex());
96 m_extraOwningItems.clear();
97}
98
100{
101 QMutexLocker l(mutex());
102 QMap<QString, OwnerT> res = m_extraOwningItems;
103 return res;
104}
105
106/*!
107\class QQmlJS::Dom::DomUniverse
108
109\brief Represents a set of parsed/loaded modules libraries and a plugins
110
111This can be used to share parsing and updates between several Dom models, and kickstart a model
112without reparsing everything.
113
114The universe is peculiar, because stepping into it from an environment looses the connection with
115the environment.
116
117This implementation is a placeholder, a later patch will introduce it.
118 */
119
120ErrorGroups DomUniverse::myErrors()
121{
122 static ErrorGroups groups = {{ DomItem::domErrorGroup, NewErrorGroup("Universe") }};
123 return groups;
124}
125
126DomUniverse::DomUniverse(const QString &universeName) : m_name(universeName) { }
127
128std::shared_ptr<DomUniverse> DomUniverse::guaranteeUniverse(
129 const std::shared_ptr<DomUniverse> &univ)
130{
131 const auto next = [] {
132 Q_CONSTINIT static std::atomic<int> counter(0);
133 return counter.fetch_add(1, std::memory_order_relaxed) + 1;
134 };
135 if (univ)
136 return univ;
137
138 return std::make_shared<DomUniverse>(
139 QLatin1String("universe") + QString::number(next()));
140}
141
142DomItem DomUniverse::create(const QString &universeName)
143{
144 auto res = std::make_shared<DomUniverse>(universeName);
145 return DomItem(res);
146}
147
148Path DomUniverse::canonicalPath() const
149{
150 return Path::fromRoot(u"universe");
151}
152
153bool DomUniverse::iterateDirectSubpaths(const DomItem &self, DirectVisitor visitor) const
155 bool cont = true;
156 cont = cont && DomTop::iterateDirectSubpaths(self, visitor);
157 cont = cont && self.dvValueField(visitor, Fields::name, name());
158 cont = cont && self.dvItemField(visitor, Fields::globalScopeWithName, [this, &self]() {
159 return self.subMapItem(Map(
160 Path::fromField(Fields::globalScopeWithName),
161 [this](const DomItem &map, const QString &key) { return map.copy(globalScopeWithName(key)); },
162 [this](const DomItem &) { return globalScopeNames(); }, QLatin1String("GlobalScope")));
163 });
164 cont = cont && self.dvItemField(visitor, Fields::qmlDirectoryWithPath, [this, &self]() {
165 return self.subMapItem(Map(
166 Path::fromField(Fields::qmlDirectoryWithPath),
167 [this](const DomItem &map, const QString &key) { return map.copy(qmlDirectoryWithPath(key)); },
168 [this](const DomItem &) { return qmlDirectoryPaths(); }, QLatin1String("QmlDirectory")));
169 });
170 cont = cont && self.dvItemField(visitor, Fields::qmldirFileWithPath, [this, &self]() {
171 return self.subMapItem(Map(
172 Path::fromField(Fields::qmldirFileWithPath),
173 [this](const DomItem &map, const QString &key) { return map.copy(qmldirFileWithPath(key)); },
174 [this](const DomItem &) { return qmldirFilePaths(); }, QLatin1String("QmldirFile")));
175 });
176 cont = cont && self.dvItemField(visitor, Fields::qmlFileWithPath, [this, &self]() {
177 return self.subMapItem(Map(
178 Path::fromField(Fields::qmlFileWithPath),
179 [this](const DomItem &map, const QString &key) { return map.copy(qmlFileWithPath(key)); },
180 [this](const DomItem &) { return qmlFilePaths(); }, QLatin1String("QmlFile")));
181 });
182 cont = cont && self.dvItemField(visitor, Fields::jsFileWithPath, [this, &self]() {
183 return self.subMapItem(Map(
184 Path::fromField(Fields::jsFileWithPath),
185 [this](const DomItem &map, const QString &key) { return map.copy(jsFileWithPath(key)); },
186 [this](const DomItem &) { return jsFilePaths(); }, QLatin1String("JsFile")));
187 });
188 cont = cont && self.dvItemField(visitor, Fields::jsFileWithPath, [this, &self]() {
189 return self.subMapItem(Map(
190 Path::fromField(Fields::qmltypesFileWithPath),
191 [this](const DomItem &map, const QString &key) { return map.copy(qmltypesFileWithPath(key)); },
192 [this](const DomItem &) { return qmltypesFilePaths(); }, QLatin1String("QmltypesFile")));
193 });
194 return cont;
195}
196
197std::shared_ptr<OwningItem> DomUniverse::doCopy(const DomItem &) const
198{
199 QRegularExpression r(QRegularExpression::anchoredPattern(QLatin1String(R"(.*Copy([0-9]*)$)")));
200 auto m = r.match(m_name);
201 QString newName;
202 if (m.hasMatch())
203 newName = QStringLiteral(u"%1Copy%2").arg(m_name).arg(m.captured(1).toInt() + 1);
204 else
205 newName = m_name + QLatin1String("Copy");
206 auto res = std::make_shared<DomUniverse>(newName);
207 return res;
208}
209
210static DomType fileTypeForPath(const DomItem &self, const QString &canonicalFilePath)
211{
212 if (canonicalFilePath.endsWith(u".qml", Qt::CaseInsensitive)
213 || canonicalFilePath.endsWith(u".qmlannotation", Qt::CaseInsensitive)) {
214 return DomType::QmlFile;
215 } else if (canonicalFilePath.endsWith(u".qmltypes")) {
217 } else if (QStringView(u"qmldir").compare(QFileInfo(canonicalFilePath).fileName(),
218 Qt::CaseInsensitive)
219 == 0) {
220 return DomType::QmldirFile;
221 } else if (QFileInfo(canonicalFilePath).isDir()) {
223 } else if (canonicalFilePath.endsWith(u".js", Qt::CaseInsensitive)
224 || canonicalFilePath.endsWith(u".mjs", Qt::CaseInsensitive)) {
225 return DomType::JsFile;
226 }
227 else {
228 self.addError(DomUniverse::myErrors()
229 .error(QCoreApplication::translate("Dom::fileTypeForPath",
230 "Could not detect type of file %1")
231 .arg(canonicalFilePath))
232 .handle());
233 }
234 return DomType::Empty;
235}
236
237DomUniverse::LoadResult DomUniverse::loadFile(const FileToLoad &file, DomType fileType,
238 DomCreationOption creationOption)
239{
240 DomItem univ(shared_from_this());
241 switch (fileType) {
242 case DomType::QmlFile:
246 case DomType::JsFile: {
247 LoadResult loadRes;
248 const auto &preLoadResult = preload(univ, file, fileType);
249 if (std::holds_alternative<LoadResult>(preLoadResult)) {
250 // universe already has the most recent version of the file
251 return std::get<LoadResult>(preLoadResult);
252 } else {
253 // content of the file needs to be parsed and value inside Universe needs to be updated
254 return load(std::get<ContentWithDate>(preLoadResult), file, fileType, creationOption);
255 }
256 }
257 default:
258 univ.addError(myErrors()
259 .error(tr("Ignoring request to load file %1 of unexpected type %2, "
260 "calling callback immediately")
261 .arg(file.canonicalPath(), domTypeToString(fileType)))
262 .handle());
263 Q_ASSERT(false && "loading non supported file type");
264 return {};
265 }
266}
267
268DomUniverse::LoadResult DomUniverse::load(const ContentWithDate &codeWithDate,
269 const FileToLoad &file, DomType fType,
270 DomCreationOption creationOption)
271{
272 QString canonicalPath = file.canonicalPath();
273
274 DomItem oldValue; // old ExternalItemPair (might be empty, or equal to newValue)
275 DomItem newValue; // current ExternalItemPair
276 DomItem univ = DomItem(shared_from_this());
277
278 if (fType == DomType::QmlFile) {
279 auto qmlFile = parseQmlFile(codeWithDate.content, file, codeWithDate.date, creationOption);
280 return insertOrUpdateExternalItem(std::move(qmlFile));
281 } else if (fType == DomType::QmltypesFile) {
282 auto qmltypesFile = std::make_shared<QmltypesFile>(canonicalPath, codeWithDate.content,
283 codeWithDate.date);
284 QmltypesReader reader(qmltypesFile);
285 reader.parse();
286 return insertOrUpdateExternalItem(std::move(qmltypesFile));
287 } else if (fType == DomType::QmldirFile) {
288 shared_ptr<QmldirFile> qmldirFile =
289 QmldirFile::fromPathAndCode(canonicalPath, codeWithDate.content);
290 return insertOrUpdateExternalItem(std::move(qmldirFile));
291 } else if (fType == DomType::QmlDirectory) {
292 auto qmlDirectory = std::make_shared<QmlDirectory>(
293 canonicalPath, codeWithDate.content.split(QLatin1Char('\n')), codeWithDate.date);
294 return insertOrUpdateExternalItem(std::move(qmlDirectory));
295 } else if (fType == DomType::JsFile) {
296 auto jsFile = parseJsFile(codeWithDate.content, file, codeWithDate.date);
297 return insertOrUpdateExternalItem(std::move(jsFile));
298 } else {
299 Q_ASSERT(false);
300 }
301 return { std::move(oldValue), std::move(newValue) };
302}
303
304/*!
305 \internal
306 This function is somewhat coupled and does the following:
307 1. If a content of the file is provided it checks whether the item with the same content
308 already exists inside the Universe. If so, returns it as a result of the load
309 2. If a content is not provided, it first tries to check whether Universe has the most
310 recent item. If yes, it returns it as a result of the load. Otherwise does step 1.
311 */
312DomUniverse::PreloadResult DomUniverse::preload(const DomItem &univ, const FileToLoad &file,
313 DomType fType) const
314{
315 QString canonicalPath = file.canonicalPath();
316 ContentWithDate codeWithDate;
317
318 if (file.content().has_value()) {
319 codeWithDate = { file.content()->data, file.content()->date };
320 } else {
321 // When content is empty, Universe attempts to read it from the File.
322 // However if it already has the most recent version of that File it just returns it
323 const auto &curValueItem = getItemIfMostRecent(univ, fType, canonicalPath);
324 if (curValueItem.has_value()) {
325 return LoadResult{ curValueItem.value(), curValueItem.value() };
326 }
327 // otherwise tries to read the content from the path
328 auto readResult = readFileContent(canonicalPath);
329 if (std::holds_alternative<ErrorMessage>(readResult)) {
330 DomItem newValue;
331 newValue.addError(std::move(std::get<ErrorMessage>(readResult)));
332 return LoadResult{ DomItem(), std::move(newValue) }; // read failed, nothing to parse
333 } else {
334 codeWithDate = std::get<ContentWithDate>(readResult);
335 }
336 }
337
338 // Once the code is provided Universe verifies if it already has an up-to-date code
339 const auto &curValueItem = getItemIfHasSameCode(univ, fType, canonicalPath, codeWithDate);
340 if (curValueItem.has_value()) {
341 return LoadResult{ curValueItem.value(), curValueItem.value() };
342 }
343 // otherwise code needs to be parsed
344 return codeWithDate;
345}
346
347void DomUniverse::removePath(const QString &path)
348{
349 QMutexLocker l(mutex());
350 const auto toDelete = [path](const auto &it) {
351 QString p = it.key();
352 return p.startsWith(path) && (p.size() == path.size() || p.at(path.size()) == u'/');
353 };
354 m_qmlDirectoryWithPath.removeIf(toDelete);
355 m_qmldirFileWithPath.removeIf(toDelete);
356 m_qmlFileWithPath.removeIf(toDelete);
357 m_jsFileWithPath.removeIf(toDelete);
358 m_qmltypesFileWithPath.removeIf(toDelete);
359}
360
361DomUniverse::ReadResult DomUniverse::readFileContent(const QString &canonicalPath) const
362{
363 if (canonicalPath.isEmpty()) {
364 return myErrors().error(tr("Non existing path %1").arg(canonicalPath));
365 }
366 QFile file(canonicalPath);
367 QFileInfo fileInfo(canonicalPath);
368 if (fileInfo.isDir()) {
369 return ContentWithDate{ QDir(canonicalPath)
370 .entryList(QDir::NoDotAndDotDot | QDir::Files, QDir::Name)
371 .join(QLatin1Char('\n')),
372 QDateTime::currentDateTimeUtc() };
373 }
374 if (!file.open(QIODevice::ReadOnly)) {
375 return myErrors().error(
376 tr("Error opening path %1: %2 %3")
377 .arg(canonicalPath, QString::number(file.error()), file.errorString()));
378 }
379 auto content = QString::fromUtf8(file.readAll());
380 file.close();
381 return ContentWithDate{ std::move(content), QDateTime::currentDateTimeUtc() };
382}
383
384std::shared_ptr<QmlFile> DomUniverse::parseQmlFile(const QString &code, const FileToLoad &file,
385 const QDateTime &contentDate,
386 DomCreationOption creationOption)
387{
388 auto qmlFile =
389 std::make_shared<QmlFile>(file.canonicalPath(), code, contentDate, 0,
390 creationOption == Extended ? QmlFile::EnableParserRecovery
391 : QmlFile::DisableParserRecovery);
392 std::shared_ptr<DomEnvironment> envPtr;
393 if (auto ptr = file.environment().lock())
394 envPtr = std::move(ptr);
395 else
396 envPtr = std::make_shared<DomEnvironment>(QStringList(),
397 DomEnvironment::Option::NoDependencies,
398 creationOption, shared_from_this());
399 envPtr->addQmlFile(qmlFile);
400 DomItem env(envPtr);
401 if (qmlFile->isValid()) {
402 // do not call populateQmlFile twice on lazy qml files if the importer already does it!
403 if (creationOption != DomCreationOption::Extended)
404 envPtr->populateFromQmlFile(MutableDomItem(env.copy(qmlFile)));
405 } else {
406 QString errs;
407 DomItem qmlFileObj = env.copy(qmlFile);
408 qmlFile->iterateErrors(qmlFileObj, [&errs](const DomItem &, const ErrorMessage &m) {
409 errs += m.toString();
410 errs += u"\n";
411 return true;
412 });
413 qCWarning(domLog).noquote().nospace()
414 << "Parsed invalid file " << file.canonicalPath() << errs;
415 }
416 return qmlFile;
417}
418
419std::shared_ptr<JsFile> DomUniverse::parseJsFile(const QString &code, const FileToLoad &file,
420 const QDateTime &contentDate)
421{
422 // WATCH OUT!
423 // DOM construction for plain JS files is not yet supported
424 // Only parsing of the file
425 // and adding ExternalItem to the Environment will happen here
426 auto jsFile = std::make_shared<JsFile>(file.canonicalPath(), code, contentDate);
427 std::shared_ptr<DomEnvironment> envPtr;
428 if (auto ptr = file.environment().lock())
429 envPtr = std::move(ptr);
430 else
431 envPtr = std::make_shared<DomEnvironment>(QStringList(),
432 DomEnvironment::Option::NoDependencies,
433 DomCreationOption::Default, shared_from_this());
434 envPtr->addJsFile(jsFile);
435 DomItem env(envPtr);
436 if (!jsFile->isValid()) {
437 QString errs;
438 DomItem qmlFileObj = env.copy(jsFile);
439 jsFile->iterateErrors(qmlFileObj, [&errs](const DomItem &, const ErrorMessage &m) {
440 errs += m.toString();
441 errs += u"\n";
442 return true;
443 });
444 qCWarning(domLog).noquote().nospace()
445 << "Parsed invalid file " << file.canonicalPath() << errs;
446 }
447 return jsFile;
448}
449
450/*!
451 \internal
452 Queries the corresponding path map attempting to get the value
453 *WARNING* Usage of this function should be protected by the read lock
454 */
455std::shared_ptr<ExternalItemPairBase> DomUniverse::getPathValueOrNull(DomType fType,
456 const QString &path) const
457{
458 switch (fType) {
459 case DomType::QmlFile:
460 return m_qmlFileWithPath.value(path);
461 case DomType::QmltypesFile:
462 return m_qmltypesFileWithPath.value(path);
463 case DomType::QmldirFile:
464 return m_qmldirFileWithPath.value(path);
465 case DomType::QmlDirectory:
466 return m_qmlDirectoryWithPath.value(path);
467 case DomType::JsFile:
468 return m_jsFileWithPath.value(path);
469 default:
470 Q_ASSERT(false);
471 }
472 return nullptr;
473}
474
475std::optional<DomItem> DomUniverse::getItemIfMostRecent(const DomItem &univ, DomType fType,
476 const QString &canonicalPath) const
477{
478 QFileInfo fInfo(canonicalPath);
479 bool valueItemIsMostRecent = false;
480 std::shared_ptr<ExternalItemPairBase> value = nullptr;
481 {
482 // Mutex is to sync access to the Value and Value->CurrentItem, which can be modified
483 // through updateEnty method and currentItem->refreshedDataAt
484 QMutexLocker l(mutex());
485 value = getPathValueOrNull(fType, canonicalPath);
486 valueItemIsMostRecent = valueHasMostRecentItem(value.get(), fInfo.lastModified());
487 }
488 if (valueItemIsMostRecent) {
489 return univ.copy(value);
490 }
491 return std::nullopt;
492}
493
494std::optional<DomItem> DomUniverse::getItemIfHasSameCode(const DomItem &univ, DomType fType,
495 const QString &canonicalPath,
496 const ContentWithDate &codeWithDate) const
497{
498 std::shared_ptr<ExternalItemPairBase> value = nullptr;
499 bool valueItemHasSameCode = false;
500 {
501 // Mutex is to sync access to the Value and Value->CurrentItem, which can be modified
502 // through updateEnty method and currentItem->refreshedDataAt
503 QMutexLocker l(mutex());
504 value = getPathValueOrNull(fType, canonicalPath);
505 if (valueHasSameContent(value.get(), codeWithDate.content)) {
506 valueItemHasSameCode = true;
507 if (value->currentItem()->lastDataUpdateAt() < codeWithDate.date)
508 value->currentItem()->refreshedDataAt(codeWithDate.date);
509 }
510 }
511 if (valueItemHasSameCode) {
512 return univ.copy(value);
513 }
514 return std::nullopt;
515}
516
517/*!
518 \internal
519 Checks if value has current Item and if it was not modified since last seen
520 *WARNING* Usage of this function should be protected by the read lock
521 */
522bool DomUniverse::valueHasMostRecentItem(const ExternalItemPairBase *value,
523 const QDateTime &lastModified)
524{
525 if (!value || !value->currentItem()) {
526 return false;
527 }
528 return lastModified < value->currentItem()->lastDataUpdateAt();
529}
530
531/*!
532 \internal
533 Checks if value has current Item and if it has same content
534 *WARNING* Usage of this function should be protected by the read lock
535 */
536bool DomUniverse::valueHasSameContent(const ExternalItemPairBase *value, const QString &content)
537{
538 if (!value || !value->currentItem()) {
539 return false;
540 }
541 QString curContent = value->currentItem()->code();
542 return !curContent.isNull() && curContent == content;
543}
544
545std::shared_ptr<OwningItem> LoadInfo::doCopy(const DomItem &self) const
546{
547 auto res = std::make_shared<LoadInfo>(*this);
548 if (res->status() != Status::Done) {
549 res->addErrorLocal(DomEnvironment::myErrors().warning(
550 u"This is a copy of a LoadInfo still in progress, artificially ending it, if you "
551 u"use this you will *not* resume loading"_sv));
552 DomEnvironment::myErrors()
553 .warning([&self](const Sink &sink) {
554 sink(u"Copying an in progress LoadInfo, which is most likely an error (");
555 self.dump(sink);
556 sink(u")");
557 })
558 .handle();
559 QMutexLocker l(res->mutex());
560 res->m_status = Status::Done;
561 res->m_toDo.clear();
562 res->m_inProgress.clear();
563 res->m_endCallbacks.clear();
564 }
565 return res;
566}
567
568Path LoadInfo::canonicalPath(const DomItem &) const
569{
570 return Path::fromRoot(PathRoot::Env).withField(Fields::loadInfo).withKey(elementCanonicalPath().toString());
571}
572
573bool LoadInfo::iterateDirectSubpaths(const DomItem &self, DirectVisitor visitor) const
574{
575 bool cont = OwningItem::iterateDirectSubpaths(self, visitor);
576 cont = cont && self.dvValueField(visitor, Fields::status, int(status()));
577 cont = cont && self.dvValueField(visitor, Fields::nLoaded, nLoaded());
578 cont = cont
579 && self.dvValueField(visitor, Fields::elementCanonicalPath,
580 elementCanonicalPath().toString());
581 cont = cont && self.dvValueField(visitor, Fields::nNotdone, nNotDone());
582 cont = cont && self.dvValueField(visitor, Fields::nCallbacks, nCallbacks());
583 return cont;
584}
585
586void LoadInfo::addEndCallback(const DomItem &self,
587 std::function<void(Path, const DomItem &, const DomItem &)> callback)
588{
589 if (!callback)
590 return;
591 {
592 QMutexLocker l(mutex());
593 switch (m_status) {
594 case Status::NotStarted:
595 case Status::Starting:
596 case Status::InProgress:
597 case Status::CallingCallbacks:
598 m_endCallbacks.append(callback);
599 return;
600 case Status::Done:
601 break;
602 }
603 }
605 DomItem el = self.path(p);
606 callback(p, el, el);
607}
608
609void LoadInfo::advanceLoad(const DomItem &self)
610{
611 Status myStatus;
612 Dependency dep;
613 bool depValid = false;
614 {
615 QMutexLocker l(mutex());
616 myStatus = m_status;
617 switch (myStatus) {
619 m_status = Status::Starting;
620 break;
621 case Status::Starting:
623 if (!m_toDo.isEmpty()) {
624 dep = m_toDo.dequeue();
625 m_inProgress.append(dep);
626 depValid = true;
627 }
628 break;
630 case Status::Done:
631 break;
632 }
633 }
634 switch (myStatus) {
635 case Status::NotStarted:
636 refreshedDataAt(QDateTime::currentDateTimeUtc());
637 doAddDependencies(self);
638 refreshedDataAt(QDateTime::currentDateTimeUtc());
639 {
640 QMutexLocker l(mutex());
641 Q_ASSERT(m_status == Status::Starting);
642 if (m_toDo.isEmpty() && m_inProgress.isEmpty())
643 myStatus = m_status = Status::CallingCallbacks;
644 else
645 myStatus = m_status = Status::InProgress;
646 }
647 if (myStatus == Status::CallingCallbacks)
648 execEnd(self);
649 break;
650 case Status::Starting:
652 if (depValid) {
653 refreshedDataAt(QDateTime::currentDateTimeUtc());
654 auto envPtr = self.environment().ownerAs<DomEnvironment>();
655 Q_ASSERT(envPtr && "missing environment");
656 if (!dep.uri.isEmpty()) {
657 envPtr->loadModuleDependency(
658 dep.uri, dep.version,
659 [this, copiedSelf = self, dep](Path, const DomItem &, const DomItem &) {
660 // Need to explicitly copy self here since we might store this and
661 // call it later.
662 finishedLoadingDep(copiedSelf, dep);
663 },
664 self.errorHandler());
665 Q_ASSERT(dep.filePath.isEmpty() && "dependency with both uri and file");
666 } else if (!dep.filePath.isEmpty()) {
667 envPtr->loadFile(
668 FileToLoad::fromFileSystem(envPtr, dep.filePath),
669 [this, copiedSelf = self, dep](Path, const DomItem &, const DomItem &) {
670 // Need to explicitly copy self here since we might store this and
671 // call it later.
672 finishedLoadingDep(copiedSelf, dep);
673 },
674 dep.fileType, self.errorHandler());
675 } else {
676 Q_ASSERT(false && "dependency without uri and filePath");
677 }
678 } else {
680 tr("advanceLoad called but found no work, which should never happen")));
681 }
682 break;
684 case Status::Done:
685 addErrorLocal(DomEnvironment::myErrors().error(tr(
686 "advanceLoad called after work should have been done, which should never happen")));
687 break;
688 }
689}
690
691void LoadInfo::finishedLoadingDep(const DomItem &self, const Dependency &d)
692{
693 bool didRemove = false;
694 bool unexpectedState = false;
695 bool doEnd = false;
696 {
697 QMutexLocker l(mutex());
698 didRemove = m_inProgress.removeOne(d);
699 switch (m_status) {
702 case Status::Done:
703 unexpectedState = true;
704 break;
705 case Status::Starting:
706 break;
708 if (m_toDo.isEmpty() && m_inProgress.isEmpty()) {
709 m_status = Status::CallingCallbacks;
710 doEnd = true;
711 }
712 break;
713 }
714 }
715 if (!didRemove) {
716 addErrorLocal(DomEnvironment::myErrors().error([&self](const Sink &sink) {
717 sink(u"LoadInfo::finishedLoadingDep did not find its dependency in those inProgress "
718 u"()");
719 self.dump(sink);
720 sink(u")");
721 }));
722 Q_ASSERT(false
723 && "LoadInfo::finishedLoadingDep did not find its dependency in those inProgress");
724 }
725 if (unexpectedState) {
726 addErrorLocal(DomEnvironment::myErrors().error([&self](const Sink &sink) {
727 sink(u"LoadInfo::finishedLoadingDep found an unexpected state (");
728 self.dump(sink);
729 sink(u")");
730 }));
731 Q_ASSERT(false && "LoadInfo::finishedLoadingDep did find an unexpected state");
732 }
733 if (doEnd)
734 execEnd(self);
735}
736
737void LoadInfo::execEnd(const DomItem &self)
738{
739 QList<std::function<void(Path, const DomItem &, const DomItem &)>> endCallbacks;
740 bool unexpectedState = false;
741 {
742 QMutexLocker l(mutex());
743 unexpectedState = m_status != Status::CallingCallbacks;
744 endCallbacks = m_endCallbacks;
745 m_endCallbacks.clear();
746 }
747 Q_ASSERT(!unexpectedState && "LoadInfo::execEnd found an unexpected state");
749 DomItem el = self.path(p);
750 {
751 auto cleanup = qScopeGuard([this, p, &el] {
752 QList<std::function<void(Path, const DomItem &, const DomItem &)>> otherCallbacks;
753 bool unexpectedState2 = false;
754 {
755 QMutexLocker l(mutex());
756 unexpectedState2 = m_status != Status::CallingCallbacks;
757 m_status = Status::Done;
758 otherCallbacks = m_endCallbacks;
759 m_endCallbacks.clear();
760 }
761 Q_ASSERT(!unexpectedState2 && "LoadInfo::execEnd found an unexpected state");
762 for (auto const &cb : otherCallbacks) {
763 if (cb)
764 cb(p, el, el);
765 }
766 });
767 for (auto const &cb : endCallbacks) {
768 if (cb)
769 cb(p, el, el);
770 }
771 }
772}
773
774void LoadInfo::doAddDependencies(const DomItem &self)
775{
777 DomEnvironment::myErrors()
778 .error(tr("Uninitialized LoadInfo %1").arg(self.canonicalPath().toString()))
779 .handle(nullptr);
780 Q_ASSERT(false);
781 return;
782 }
783 // sychronous add of all dependencies
786 DomItem currentFile = el.field(Fields::currentItem);
787 QString currentFilePath = currentFile.canonicalFilePath();
788 // do not mess with QmlFile's lazy-loading
789 if (currentFile.internalKind() != DomType::QmlFile) {
790 DomItem currentQmltypesFiles = currentFile.field(Fields::qmltypesFiles);
791 int qEnd = currentQmltypesFiles.indexes();
792 for (int i = 0; i < qEnd; ++i) {
793 DomItem qmltypesRef = currentQmltypesFiles.index(i);
794 if (const Reference *ref = qmltypesRef.as<Reference>()) {
795 Path canonicalPath = ref->referredObjectPath[2];
796 if (canonicalPath && !canonicalPath.headName().isEmpty())
797 addDependency(
798 self,
799 Dependency{ QString(), Version(), canonicalPath.headName(),
801 }
802 }
803 DomItem currentQmlFiles = currentFile.field(Fields::qmlFiles);
804 currentQmlFiles.visitKeys([this, &self](const QString &, const DomItem &els) {
805 return els.visitIndexes([this, &self](const DomItem &el) {
806 if (const Reference *ref = el.as<Reference>()) {
807 Path canonicalPath = ref->referredObjectPath[2];
808 if (canonicalPath && !canonicalPath.headName().isEmpty())
809 addDependency(self,
810 Dependency{ QString(), Version(),
811 canonicalPath.headName(), DomType::QmlFile });
812 }
813 return true;
814 });
815 });
816 }
817 } else if (shared_ptr<ModuleIndex> elPtr = el.ownerAs<ModuleIndex>()) {
818 const auto qmldirs = elPtr->qmldirsToLoad(el);
819 for (const Path &qmldirPath : qmldirs) {
820 Path canonicalPath = qmldirPath[2];
821 if (canonicalPath && !canonicalPath.headName().isEmpty())
822 addDependency(self,
823 Dependency { QString(), Version(), canonicalPath.headName(),
824 DomType::QmldirFile });
825 }
826 QString uri = elPtr->uri();
827 addEndCallback(self, [uri, qmldirs](Path, const DomItem &, const DomItem &newV) {
828 for (const Path &p : qmldirs) {
829 DomItem qmldir = newV.path(p);
830 if (std::shared_ptr<QmldirFile> qmldirFilePtr = qmldir.ownerAs<QmldirFile>()) {
831 qmldirFilePtr->ensureInModuleIndex(qmldir, uri);
832 }
833 }
834 });
835 } else if (!el) {
836 self.addError(DomEnvironment::myErrors().error(
837 tr("Ignoring dependencies for empty (invalid) type %1")
838 .arg(domTypeToString(el.internalKind()))));
839 } else {
840 self.addError(
841 DomEnvironment::myErrors().error(tr("dependencies of %1 (%2) not yet implemented")
842 .arg(domTypeToString(el.internalKind()),
843 elementCanonicalPath().toString())));
844 }
845}
846
847void LoadInfo::addDependency(const DomItem &self, const Dependency &dep)
848{
849 bool unexpectedState = false;
850 {
851 QMutexLocker l(mutex());
852 unexpectedState = m_status != Status::Starting;
853 m_toDo.enqueue(dep);
854 }
855 Q_ASSERT(!unexpectedState && "LoadInfo::addDependency found an unexpected state");
856 DomItem env = self.environment();
857 env.ownerAs<DomEnvironment>()->addWorkForLoadInfo(elementCanonicalPath());
858}
859
860/*!
861\class QQmlJS::Dom::DomEnvironment
862
863\brief Represents a consistent set of types organized in modules, it is the top level of the DOM
864
865The DomEnvironment keeps a pointer m_lastValidBase to the last used valid DomEnvironment in the
866commitToBase() method. This allows the qqmldomastcreator to commit lazily loaded dependencies to the
867valid environment used by qmlls.
868 */
869
870ErrorGroups DomEnvironment::myErrors()
871{
872 static ErrorGroups res = {{NewErrorGroup("Dom")}};
873 return res;
874}
875
876DomType DomEnvironment::kind() const
877{
878 return kindValue;
879}
880
881Path DomEnvironment::canonicalPath() const
882{
883 return Path::fromRoot(u"env");
884}
885
886bool DomEnvironment::iterateDirectSubpaths(const DomItem &self, DirectVisitor visitor) const
887{
888 bool cont = true;
889 cont = cont && DomTop::iterateDirectSubpaths(self, visitor);
890 DomItem univ = universe();
891 cont = cont && self.dvItemField(visitor, Fields::universe, [this]() { return universe(); });
892 cont = cont && self.dvValueField(visitor, Fields::options, int(options()));
893 cont = cont && self.dvItemField(visitor, Fields::base, [this]() { return base(); });
894 cont = cont
895 && self.dvValueLazyField(visitor, Fields::loadPaths, [this]() { return loadPaths(); });
896 cont = cont && self.dvValueField(visitor, Fields::globalScopeName, globalScopeName());
897 cont = cont && self.dvItemField(visitor, Fields::globalScopeWithName, [this, &self]() {
898 return self.subMapItem(Map(
899 Path::fromField(Fields::globalScopeWithName),
900 [&self, this](const DomItem &map, const QString &key) {
901 return map.copy(globalScopeWithName(self, key));
902 },
903 [&self, this](const DomItem &) { return globalScopeNames(self); },
904 QLatin1String("GlobalScope")));
905 });
906 cont = cont && self.dvItemField(visitor, Fields::qmlDirectoryWithPath, [this, &self]() {
907 return self.subMapItem(Map(
908 Path::fromField(Fields::qmlDirectoryWithPath),
909 [&self, this](const DomItem &map, const QString &key) {
910 return map.copy(qmlDirectoryWithPath(self, key));
911 },
912 [&self, this](const DomItem &) { return qmlDirectoryPaths(self); },
913 QLatin1String("QmlDirectory")));
914 });
915 cont = cont && self.dvItemField(visitor, Fields::qmldirFileWithPath, [this, &self]() {
916 return self.subMapItem(Map(
917 Path::fromField(Fields::qmldirFileWithPath),
918 [&self, this](const DomItem &map, const QString &key) {
919 return map.copy(qmldirFileWithPath(self, key));
920 },
921 [&self, this](const DomItem &) { return qmldirFilePaths(self); },
922 QLatin1String("QmldirFile")));
923 });
924 cont = cont && self.dvItemField(visitor, Fields::qmldirWithPath, [this, &self]() {
925 return self.subMapItem(Map(
926 Path::fromField(Fields::qmldirWithPath),
927 [&self, this](const DomItem &map, const QString &key) {
928 return map.copy(qmlDirWithPath(self, key));
929 },
930 [&self, this](const DomItem &) { return qmlDirPaths(self); }, QLatin1String("Qmldir")));
931 });
932 cont = cont && self.dvItemField(visitor, Fields::qmlFileWithPath, [this, &self]() {
933 return self.subMapItem(Map(
934 Path::fromField(Fields::qmlFileWithPath),
935 [&self, this](const DomItem &map, const QString &key) {
936 return map.copy(qmlFileWithPath(self, key));
937 },
938 [&self, this](const DomItem &) { return qmlFilePaths(self); }, QLatin1String("QmlFile")));
939 });
940 cont = cont && self.dvItemField(visitor, Fields::jsFileWithPath, [this, &self]() {
941 return self.subMapItem(Map(
942 Path::fromField(Fields::jsFileWithPath),
943 [this](const DomItem &map, const QString &key) {
944 DomItem mapOw(map.owner());
945 return map.copy(jsFileWithPath(mapOw, key));
946 },
947 [this](const DomItem &map) {
948 DomItem mapOw = map.owner();
949 return jsFilePaths(mapOw);
950 },
951 QLatin1String("JsFile")));
952 });
953 cont = cont && self.dvItemField(visitor, Fields::qmltypesFileWithPath, [this, &self]() {
954 return self.subMapItem(Map(
955 Path::fromField(Fields::qmltypesFileWithPath),
956 [this](const DomItem &map, const QString &key) {
957 DomItem mapOw = map.owner();
958 return map.copy(qmltypesFileWithPath(mapOw, key));
959 },
960 [this](const DomItem &map) {
961 DomItem mapOw = map.owner();
962 return qmltypesFilePaths(mapOw);
963 },
964 QLatin1String("QmltypesFile")));
965 });
966 cont = cont && self.dvItemField(visitor, Fields::moduleIndexWithUri, [this, &self]() {
967 return self.subMapItem(Map(
968 Path::fromField(Fields::moduleIndexWithUri),
969 [this](const DomItem &map, const QString &key) {
970 return map.subMapItem(Map(
971 map.pathFromOwner().withKey(key),
972 [this, key](const DomItem &submap, const QString &subKey) {
973 bool ok;
974 int i = subKey.toInt(&ok);
975 if (!ok) {
976 if (subKey.isEmpty())
977 i = Version::Undefined;
978 else if (subKey.compare(u"Latest", Qt::CaseInsensitive) == 0)
979 i = Version::Latest;
980 else
981 return DomItem();
982 }
983 DomItem subMapOw = submap.owner();
984 std::shared_ptr<ModuleIndex> mIndex =
985 moduleIndexWithUri(subMapOw, key, i);
986 return submap.copy(mIndex);
987 },
988 [this, key](const DomItem &subMap) {
989 QSet<QString> res;
990 DomItem subMapOw = subMap.owner();
991 for (int mVersion :
992 moduleIndexMajorVersions(subMapOw, key, EnvLookup::Normal))
993 if (mVersion == Version::Undefined)
994 res.insert(QString());
995 else
996 res.insert(QString::number(mVersion));
997 if (!res.isEmpty())
998 res.insert(QLatin1String("Latest"));
999 return res;
1000 },
1001 QLatin1String("ModuleIndex")));
1002 },
1003 [this](const DomItem &map) {
1004 DomItem mapOw = map.owner();
1005 return moduleIndexUris(mapOw);
1006 },
1007 QLatin1String("Map<ModuleIndex>")));
1008 });
1009 bool loadedLoadInfo = false;
1010 QQueue<Path> loadsWithWork;
1011 QQueue<Path> inProgress;
1012 int nAllLoadedCallbacks;
1013 auto ensureInfo = [&]() {
1014 if (!loadedLoadInfo) {
1015 QMutexLocker l(mutex());
1016 loadedLoadInfo = true;
1017 loadsWithWork = m_loadsWithWork;
1018 inProgress = m_inProgress;
1019 nAllLoadedCallbacks = m_allLoadedCallback.size();
1020 }
1021 };
1022 cont = cont
1023 && self.dvItemField(
1024 visitor, Fields::loadsWithWork, [&ensureInfo, &self, &loadsWithWork]() {
1025 ensureInfo();
1026 return self.subListItem(List(
1027 Path::fromField(Fields::loadsWithWork),
1028 [loadsWithWork](const DomItem &list, index_type i) {
1029 if (i >= 0 && i < loadsWithWork.size())
1030 return list.subDataItem(PathEls::Index(i),
1031 loadsWithWork.at(i).toString());
1032 else
1033 return DomItem();
1034 },
1035 [loadsWithWork](const DomItem &) {
1036 return index_type(loadsWithWork.size());
1037 },
1038 nullptr, QLatin1String("Path")));
1039 });
1040 cont = cont
1041 && self.dvItemField(visitor, Fields::inProgress, [&self, &ensureInfo, &inProgress]() {
1042 ensureInfo();
1043 return self.subListItem(List(
1044 Path::fromField(Fields::inProgress),
1045 [inProgress](const DomItem &list, index_type i) {
1046 if (i >= 0 && i < inProgress.size())
1047 return list.subDataItem(PathEls::Index(i),
1048 inProgress.at(i).toString());
1049 else
1050 return DomItem();
1051 },
1052 [inProgress](const DomItem &) { return index_type(inProgress.size()); },
1053 nullptr, QLatin1String("Path")));
1054 });
1055 cont = cont && self.dvItemField(visitor, Fields::loadInfo, [&self, this]() {
1056 return self.subMapItem(Map(
1057 Path::fromField(Fields::loadInfo),
1058 [this](const DomItem &map, const QString &pStr) {
1059 bool hasErrors = false;
1060 Path p = Path::fromString(pStr, [&hasErrors](const ErrorMessage &m) {
1061 switch (m.level) {
1062 case ErrorLevel::Debug:
1063 case ErrorLevel::Info:
1064 break;
1065 case ErrorLevel::Warning:
1066 case ErrorLevel::Error:
1067 case ErrorLevel::Fatal:
1068 hasErrors = true;
1069 break;
1070 }
1071 });
1072 if (!hasErrors)
1073 return map.copy(loadInfo(p));
1074 return DomItem();
1075 },
1076 [this](const DomItem &) {
1077 QSet<QString> res;
1078 const auto infoPaths = loadInfoPaths();
1079 for (const Path &p : infoPaths)
1080 res.insert(p.toString());
1081 return res;
1082 },
1083 QLatin1String("LoadInfo")));
1084 });
1085 cont = cont && self.dvWrapField(visitor, Fields::imports, m_implicitImports);
1086 cont = cont
1087 && self.dvValueLazyField(visitor, Fields::nAllLoadedCallbacks,
1088 [&nAllLoadedCallbacks, &ensureInfo]() {
1089 ensureInfo();
1090 return nAllLoadedCallbacks;
1091 });
1092 return cont;
1093}
1094
1095DomItem DomEnvironment::field(const DomItem &self, QStringView name) const
1096{
1097 return DomTop::field(self, name);
1098}
1099
1100std::shared_ptr<DomEnvironment> DomEnvironment::makeCopy(const DomItem &self) const
1101{
1102 return std::static_pointer_cast<DomEnvironment>(doCopy(self));
1103}
1104
1105std::shared_ptr<OwningItem> DomEnvironment::doCopy(const DomItem &) const
1106{
1107 shared_ptr<DomEnvironment> res;
1108 if (m_base)
1109 res = std::make_shared<DomEnvironment>(m_base, m_loadPaths, m_options, m_domCreationOption);
1110 else
1111 res = std::make_shared<DomEnvironment>(m_loadPaths, m_options, m_domCreationOption,
1112 m_universe);
1113 return res;
1114}
1115
1116void DomEnvironment::loadFile(const FileToLoad &file, const Callback &callback,
1117 std::optional<DomType> fileType, const ErrorHandler &h)
1118{
1119 if (options() & DomEnvironment::Option::NoDependencies)
1120 loadFile(file, callback, DomTop::Callback(), fileType, h);
1121 else {
1122 // When the file is required to be loaded with dependencies, those dependencies
1123 // will be added to the "pending" queue through envCallbackForFile
1124 // then those should not be forgotten to be loaded.
1125 loadFile(file, DomTop::Callback(), callback, fileType, h);
1126 }
1127}
1128
1129/*!
1130 \internal
1131 Depending on the options, the function will be called either with loadCallback OR endCallback
1132
1133 Before loading the file, envCallbackForFile will be created and passed as an argument to
1134 universe().loadFile(...).
1135 This is a callback which will be called after the load of the file is finished. More
1136 specifically when File is required to be loaded without Dependencies only loadCallback is being
1137 used. Otherwise, the callback is passed as endCallback. What endCallback means is that this
1138 callback will be called only at the very end, once all necessary dependencies are being loaded.
1139 Management and handing of this is happening through the m_loadsWithWork.
1140*/
1141// TODO(QTBUG-119550) refactor this
1142void DomEnvironment::loadFile(const FileToLoad &_file, const Callback &loadCallback,
1143 const Callback &endCallback, std::optional<DomType> fileType,
1144 const ErrorHandler &h)
1145{
1146 DomItem self(shared_from_this());
1147 const DomType fType =
1148 (bool(fileType) ? (*fileType) : fileTypeForPath(self, _file.logicalPath()));
1149
1150 FileToLoad file {_file};
1151
1153 // use source folders when loading qml files and build folders otherwise
1154 if (fType == DomType::QmlFile) {
1155 file.setCanonicalPath(QQmlJSUtils::qmlSourcePathFromBuildPath(
1156 semanticAnalysis().m_mapper.get(), file.canonicalPath()));
1157 file.setLogicalPath(file.logicalPath());
1158 }
1159 }
1160
1161 if (file.canonicalPath().isEmpty()) {
1162 if (!file.content() || file.content()->data.isNull()) {
1163 // file's content inavailable and no path to retrieve it
1165 .error(tr("Non existing path to load: '%1'").arg(file.logicalPath()))
1166 .handle(h);
1167 if (loadCallback)
1168 loadCallback(Path(), DomItem::empty, DomItem::empty);
1169 if (endCallback)
1170 addAllLoadedCallback(self, [endCallback](Path, const DomItem &, const DomItem &) {
1171 endCallback(Path(), DomItem::empty, DomItem::empty);
1172 });
1173 return;
1174 } else {
1175 // fallback: path invalid but file's content is already available.
1176 file.canonicalPath() = file.logicalPath();
1177 }
1178 }
1179
1180 shared_ptr<ExternalItemInfoBase> oldValue, newValue;
1181 switch (fType) {
1182 case DomType::QmlDirectory: {
1183 const auto &fetchResult = fetchFileFromEnvs<QmlDirectory>(file);
1184 oldValue = fetchResult.first;
1185 newValue = fetchResult.second;
1186 if (!newValue) {
1187 const auto &loadRes = universe()->loadFile(file, fType, m_domCreationOption);
1188 addExternalItemInfo<QmlDirectory>(loadRes.currentItem,
1189 getLoadCallbackFor(fType, loadCallback), endCallback);
1190 return;
1191 }
1192 } break;
1193 case DomType::QmlFile: {
1194 const auto &fetchResult = fetchFileFromEnvs<QmlFile>(file);
1195 oldValue = fetchResult.first;
1196 newValue = fetchResult.second;
1197 if (!newValue) {
1198 const auto &loadRes = universe()->loadFile(file, fType, m_domCreationOption);
1199 addExternalItemInfo<QmlFile>(loadRes.currentItem,
1200 getLoadCallbackFor(fType, loadCallback), endCallback);
1201 return;
1202 }
1203 } break;
1204 case DomType::QmltypesFile: {
1205 const auto &fetchResult = fetchFileFromEnvs<QmltypesFile>(file);
1206 oldValue = fetchResult.first;
1207 newValue = fetchResult.second;
1208 if (!newValue) {
1209 const auto &loadRes = universe()->loadFile(file, fType, m_domCreationOption);
1210 addExternalItemInfo<QmltypesFile>(loadRes.currentItem,
1211 getLoadCallbackFor(fType, loadCallback), endCallback);
1212 return;
1213 }
1214 } break;
1215 case DomType::QmldirFile: {
1216 const auto &fetchResult = fetchFileFromEnvs<QmldirFile>(file);
1217 oldValue = fetchResult.first;
1218 newValue = fetchResult.second;
1219 if (!newValue) {
1220 const auto &loadRes = universe()->loadFile(file, fType, m_domCreationOption);
1221 addExternalItemInfo<QmldirFile>(loadRes.currentItem,
1222 getLoadCallbackFor(fType, loadCallback), endCallback);
1223 return;
1224 }
1225 } break;
1226 case DomType::JsFile: {
1227 const auto &loadRes = universe()->loadFile(file, fType, m_domCreationOption);
1228 addExternalItemInfo<JsFile>(loadRes.currentItem, getLoadCallbackFor(fType, loadCallback),
1229 endCallback);
1230 return;
1231 } break;
1232 default: {
1233 myErrors().error(tr("Unexpected file to load: '%1'").arg(file.canonicalPath())).handle(h);
1234 if (loadCallback)
1236 if (endCallback)
1238 return;
1239 } break;
1240 }
1241 Path p = self.copy(newValue).canonicalPath();
1242 std::shared_ptr<LoadInfo> lInfo = loadInfo(p);
1243 if (lInfo) {
1244 if (loadCallback) {
1245 DomItem oldValueObj = self.copy(oldValue);
1246 DomItem newValueObj = self.copy(newValue);
1247 loadCallback(p, oldValueObj, newValueObj);
1248 }
1249 } else {
1250 self.addError(myErrors().error(tr("missing load info in ")));
1251 if (loadCallback)
1253 }
1254 if (endCallback)
1255 addAllLoadedCallback(self, [p = std::move(p), endCallback](
1256 const Path &, const DomItem &, const DomItem &env) {
1257 DomItem el = env.path(p);
1258 endCallback(p, el, el);
1259 });
1260}
1261
1262void DomEnvironment::loadModuleDependency(
1263 const QString &uri, Version version,
1264 const std::function<void(const Path &, const DomItem &, const DomItem &)> &callback,
1265 const ErrorHandler &errorHandler)
1266{
1267 DomItem envItem(shared_from_this());
1268 if (options() & DomEnvironment::Option::NoDependencies)
1269 loadModuleDependency(envItem, uri, version, callback, nullptr, errorHandler);
1270 else
1271 loadModuleDependency(envItem, uri, version, nullptr, callback, errorHandler);
1272}
1273
1274void DomEnvironment::loadModuleDependency(
1275 const DomItem &self, const QString &uri, Version v, const Callback &loadCallback,
1276 const Callback &endCallback, const ErrorHandler &errorHandler)
1277{
1278 Q_ASSERT(!uri.contains(u'/'));
1279 Path p = Paths::moduleIndexPath(uri, v.majorVersion);
1280 if (v.majorVersion == Version::Latest) {
1281 // load both the latest .<version> directory, and the common one
1282 QStringList subPathComponents = uri.split(QLatin1Char('.'));
1283 int maxV = -1;
1284 bool commonV = false;
1285 QString lastComponent = subPathComponents.last();
1286 subPathComponents.removeLast();
1287 QString subPathV = subPathComponents.join(u'/');
1288 QRegularExpression vRe(QRegularExpression::anchoredPattern(
1289 QRegularExpression::escape(lastComponent) + QStringLiteral(u"\\.([0-9]*)")));
1290 const auto lPaths = loadPaths();
1291 qCDebug(QQmlJSDomImporting) << "DomEnvironment::loadModuleDependency: Searching module with"
1292 " uri"
1293 << uri;
1294 for (const QString &path : lPaths) {
1295 QDir dir(path + (subPathV.isEmpty() ? QStringLiteral(u"") : QStringLiteral(u"/"))
1296 + subPathV);
1297 const auto eList = dir.entryList(QDir::Dirs | QDir::NoDotAndDotDot);
1298 for (const QString &dirNow : eList) {
1299 auto m = vRe.match(dirNow);
1300 if (m.hasMatch()) {
1301 int majorV = m.captured(1).toInt();
1302 if (majorV > maxV) {
1303 QFileInfo fInfo(dir.canonicalPath() + QChar(u'/') + dirNow
1304 + QStringLiteral(u"/qmldir"));
1305 if (fInfo.isFile()) {
1306 qCDebug(QQmlJSDomImporting)
1307 << "Found qmldir in " << fInfo.canonicalFilePath();
1308 maxV = majorV;
1309 }
1310 }
1311 }
1312 if (!commonV && dirNow == lastComponent) {
1313 QFileInfo fInfo(dir.canonicalPath() + QChar(u'/') + dirNow
1314 + QStringLiteral(u"/qmldir"));
1315 if (fInfo.isFile()) {
1316 qCDebug(QQmlJSDomImporting)
1317 << "Found qmldir in " << fInfo.canonicalFilePath();
1318 commonV = true;
1319 }
1320 }
1321 }
1322 }
1323
1324 // This decrements _separately_ for each copy of the lambda. So, what we get here is not a
1325 // limit on the total number of calls but a limit on the number of calls per caller
1326 // location. It gets even funnier if the callback is first called and then copied further.
1327 // TODO: Is this the intended behavior?
1328 int toLoad = (commonV ? 1 : 0) + ((maxV >= 0) ? 1 : 0);
1329 const auto &loadCallback2 = loadCallback
1330 ? [p, loadCallback, toLoad](Path, const DomItem &, const DomItem &elV) mutable {
1331 if (--toLoad == 0) {
1332 DomItem el = elV.path(p);
1333 loadCallback(p, el, el);
1334 }
1335 }
1336 : Callback();
1337
1338 if (maxV >= 0)
1339 loadModuleDependency(self, uri, Version(maxV, v.minorVersion), loadCallback2, nullptr);
1340 if (commonV)
1341 loadModuleDependency(self, uri, Version(Version::Undefined, v.minorVersion),
1342 loadCallback2, nullptr);
1343 else if (maxV < 0) {
1344 if (uri != u"QML") {
1345 const QString loadPaths = lPaths.join(u", "_s);
1346 qCDebug(QQmlJSDomImporting)
1347 << "DomEnvironment::loadModuleDependency: qmldir at" << (uri + u"/qmldir"_s)
1348 << "was not found in " << loadPaths;
1351 .warning(tr("Failed to find main qmldir file for %1 %2 in %3.")
1352 .arg(uri, v.stringValue(), loadPaths))
1353 .handle());
1354 }
1355 if (loadCallback)
1356 loadCallback(p, DomItem::empty, DomItem::empty);
1357 }
1358 } else {
1359 std::shared_ptr<ModuleIndex> mIndex = moduleIndexWithUri(
1360 self, uri, v.majorVersion, EnvLookup::Normal, Changeable::Writable, errorHandler);
1361 std::shared_ptr<LoadInfo> lInfo = loadInfo(p);
1362 if (lInfo) {
1363 DomItem lInfoObj = self.copy(lInfo);
1364 lInfo->addEndCallback(lInfoObj, loadCallback);
1365 } else {
1367 myErrors().warning(tr("Missing loadInfo for %1").arg(p.toString())).handle());
1368 if (loadCallback)
1369 loadCallback(p, DomItem::empty, DomItem::empty);
1370 }
1371 }
1372 if (endCallback) {
1373 addAllLoadedCallback(self, [p = std::move(p), endCallback = std::move(endCallback)](
1374 Path, const DomItem &, const DomItem &env) {
1375 DomItem el = env.path(p);
1376 endCallback(p, el, el);
1377 });
1378 }
1379}
1380
1381void DomEnvironment::loadBuiltins(const Callback &callback, const ErrorHandler &h)
1382{
1383 QString builtinsName = QLatin1String("QML/plugins.qmltypes");
1384 const auto lPaths = loadPaths();
1385 for (const QString &path : lPaths) {
1386 QDir dir(path);
1387 QFileInfo fInfo(dir.filePath(builtinsName));
1388 if (fInfo.isFile()) {
1389 loadFile(FileToLoad::fromFileSystem(shared_from_this(), fInfo.canonicalFilePath()),
1390 callback);
1391 return;
1392 }
1393 }
1394 myErrors().error(tr("Could not find the QML/plugins.qmltypes file")).handle(h);
1395}
1396
1397void DomEnvironment::removePath(const QString &path)
1398{
1399 QMutexLocker l(mutex());
1400 auto toDelete = [path](auto it) {
1401 QString p = it.key();
1402 return p.startsWith(path) && (p.size() == path.size() || p.at(path.size()) == u'/');
1403 };
1404 m_qmlDirectoryWithPath.removeIf(toDelete);
1405 m_qmldirFileWithPath.removeIf(toDelete);
1406 m_qmlFileWithPath.removeIf(toDelete);
1407 m_jsFileWithPath.removeIf(toDelete);
1408 m_qmltypesFileWithPath.removeIf(toDelete);
1409}
1410
1411shared_ptr<DomUniverse> DomEnvironment::universe() const {
1412 if (m_universe)
1413 return m_universe;
1414 else if (m_base)
1415 return m_base->universe();
1416 else
1417 return {};
1418}
1419
1420template<typename T>
1421QSet<QString> DomEnvironment::getStrings(function_ref<QSet<QString>()> getBase,
1422 const QMap<QString, T> &selfMap, EnvLookup options) const
1423{
1424 QSet<QString> res;
1425 if (options != EnvLookup::NoBase && m_base) {
1426 if (m_base)
1427 res = getBase();
1428 }
1429 if (options != EnvLookup::BaseOnly) {
1430 QMap<QString, T> map;
1431 {
1432 QMutexLocker l(mutex());
1433 map = selfMap;
1434 }
1435 auto it = map.keyBegin();
1436 auto end = map.keyEnd();
1437 while (it != end) {
1438 res += *it;
1439 ++it;
1440 }
1441 }
1442 return res;
1443}
1444
1445QSet<QString> DomEnvironment::moduleIndexUris(const DomItem &, EnvLookup lookup) const
1446{
1447 DomItem baseObj = DomItem(m_base);
1448 return this->getStrings<QMap<int, std::shared_ptr<ModuleIndex>>>(
1449 [this, &baseObj] { return m_base->moduleIndexUris(baseObj, EnvLookup::Normal); },
1450 m_moduleIndexWithUri, lookup);
1451}
1452
1453QSet<int> DomEnvironment::moduleIndexMajorVersions(const DomItem &, const QString &uri, EnvLookup lookup) const
1454{
1455 QSet<int> res;
1456 if (lookup != EnvLookup::NoBase && m_base) {
1457 DomItem baseObj(m_base);
1458 res = m_base->moduleIndexMajorVersions(baseObj, uri, EnvLookup::Normal);
1459 }
1460 if (lookup != EnvLookup::BaseOnly) {
1461 QMap<int, std::shared_ptr<ModuleIndex>> map;
1462 {
1463 QMutexLocker l(mutex());
1464 map = m_moduleIndexWithUri.value(uri);
1465 }
1466 auto it = map.keyBegin();
1467 auto end = map.keyEnd();
1468 while (it != end) {
1469 res += *it;
1470 ++it;
1471 }
1472 }
1473 return res;
1474}
1475
1476std::shared_ptr<ModuleIndex> DomEnvironment::lookupModuleInEnv(const QString &uri, int majorVersion) const
1477{
1478 QMutexLocker l(mutex());
1479 auto it = m_moduleIndexWithUri.find(uri);
1480 if (it == m_moduleIndexWithUri.end())
1481 return {}; // we haven't seen the module yet
1482 if (it->empty())
1483 return {}; // module contains nothing
1484 if (majorVersion == Version::Latest)
1485 return it->last(); // map is ordered by version, so last == Latest
1486 else
1487 return it->value(majorVersion); // null shared_ptr is fine if no match
1488}
1489
1490DomEnvironment::ModuleLookupResult DomEnvironment::moduleIndexWithUriHelper(const DomItem &self, const QString &uri, int majorVersion, EnvLookup options) const
1491{
1492 std::shared_ptr<ModuleIndex> res;
1493 if (options != EnvLookup::BaseOnly)
1494 res = lookupModuleInEnv(uri, majorVersion);
1495 // if there is no base, or if we should not consider it
1496 // then the only result we can end up with is the module we looked up above
1497 if (options == EnvLookup::NoBase || !m_base)
1498 return {std::move(res), ModuleLookupResult::FromGlobal };
1499 const std::shared_ptr existingMod =
1500 m_base->moduleIndexWithUri(self, uri, majorVersion, options, Changeable::ReadOnly);
1501 if (!res) // the only module we can find at all is the one in base (might be null, too, though)
1502 return { std::move(existingMod), ModuleLookupResult::FromBase };
1503 if (!existingMod) // on the other hand, if there was nothing in base, we can only return what was in the larger env
1504 return {std::move(res), ModuleLookupResult::FromGlobal };
1505
1506 // if we have both res and existingMod, res and existingMod should be the same
1507 // _unless_ we looked for the latest version. Then one might have a higher version than the other
1508 // and we have to check it
1509
1510 if (majorVersion == Version::Latest) {
1511 if (res->majorVersion() >= existingMod->majorVersion())
1512 return { std::move(res), ModuleLookupResult::FromGlobal };
1513 else
1514 return { std::move(existingMod), ModuleLookupResult::FromBase };
1515 } else {
1516 // doesn't really matter which we return, but the other overload benefits from using the
1517 // version from m_moduleIndexWithUri
1518 return { std::move(res), ModuleLookupResult::FromGlobal };
1519 }
1520}
1521
1522std::shared_ptr<ModuleIndex> DomEnvironment::moduleIndexWithUri(
1523 const DomItem &self, const QString &uri, int majorVersion, EnvLookup options,
1524 Changeable changeable, const ErrorHandler &errorHandler)
1525{
1526 // sanity checks
1527 Q_ASSERT((changeable == Changeable::ReadOnly
1528 || (majorVersion >= 0 || majorVersion == Version::Undefined))
1529 && "A writeable moduleIndexWithUri call should have a version (not with "
1530 "Version::Latest)");
1531 if (changeable == Changeable::Writable && (m_options & Option::Exported))
1532 myErrors().error(tr("A mutable module was requested in a multithreaded environment")).handle(errorHandler);
1533
1534
1535 // use the overload which does not care about changing m_moduleIndexWithUri to find a candidate
1536 auto candidate = moduleIndexWithUriHelper(self, uri, majorVersion, options);
1537
1538 // A ModuleIndex from m_moduleIndexWithUri can always be returned
1539 if (candidate.module && candidate.fromBase == ModuleLookupResult::FromGlobal)
1540 return std::move(candidate.module);
1541
1542 // If we don't want to modify anything, return the candidate that we have found (if any)
1543 if (changeable == Changeable::ReadOnly)
1544 return std::move(candidate.module);
1545
1546 // Else we want to create a modifyable version
1547 std::shared_ptr<ModuleIndex> newModulePtr = [&] {
1548 // which is a completely new module in case we don't have candidate
1549 if (!candidate.module)
1550 return std::make_shared<ModuleIndex>(uri, majorVersion);
1551 // or a copy of the candidate otherwise
1552 DomItem existingModObj = self.copy(candidate.module);
1553 return candidate.module->makeCopy(existingModObj);
1554 }();
1555
1556 DomItem newModule = self.copy(newModulePtr);
1557 Path p = newModule.canonicalPath();
1558 {
1559 QMutexLocker l(mutex());
1560 auto &modsNow = m_moduleIndexWithUri[uri];
1561 // As we do not hold the lock for the whole operation, some other thread
1562 // might have created the module already
1563 if (auto it = modsNow.constFind(majorVersion); it != modsNow.cend())
1564 return *it;
1565 modsNow.insert(majorVersion, newModulePtr);
1566 }
1567 if (p) {
1568 auto lInfo = std::make_shared<LoadInfo>(p);
1569 addLoadInfo(self, lInfo);
1570 } else {
1572 .error(tr("Could not get path for newly created ModuleIndex %1 %2")
1573 .arg(uri)
1574 .arg(majorVersion))
1575 .handle(errorHandler);
1576 }
1577
1578 return newModulePtr;
1579}
1580
1581std::shared_ptr<ModuleIndex> DomEnvironment::moduleIndexWithUri(const DomItem &self, const QString &uri,
1582 int majorVersion,
1583 EnvLookup options) const
1584{
1585 return moduleIndexWithUriHelper(self, uri, majorVersion, options).module;
1586}
1587
1588std::shared_ptr<ExternalItemInfo<QmlDirectory>>
1589DomEnvironment::qmlDirectoryWithPath(const DomItem &, const QString &path, EnvLookup options) const
1590{
1591 return lookup<QmlDirectory>(path, options);
1592}
1593
1594QSet<QString> DomEnvironment::qmlDirectoryPaths(const DomItem &, EnvLookup options) const
1595{
1596 return getStrings<std::shared_ptr<ExternalItemInfo<QmlDirectory>>>(
1597 [this] {
1598 DomItem baseObj(m_base);
1599 return m_base->qmlDirectoryPaths(baseObj, EnvLookup::Normal);
1600 },
1601 m_qmlDirectoryWithPath, options);
1602}
1603
1604std::shared_ptr<ExternalItemInfo<QmldirFile>>
1605DomEnvironment::qmldirFileWithPath(const DomItem &, const QString &path, EnvLookup options) const
1606{
1607 return lookup<QmldirFile>(path, options);
1608}
1609
1610QSet<QString> DomEnvironment::qmldirFilePaths(const DomItem &, EnvLookup lOptions) const
1611{
1612 return getStrings<std::shared_ptr<ExternalItemInfo<QmldirFile>>>(
1613 [this] {
1614 DomItem baseObj(m_base);
1615 return m_base->qmldirFilePaths(baseObj, EnvLookup::Normal);
1616 },
1617 m_qmldirFileWithPath, lOptions);
1618}
1619
1620std::shared_ptr<ExternalItemInfoBase> DomEnvironment::qmlDirWithPath(const DomItem &self, const QString &path,
1621 EnvLookup options) const
1622{
1623 if (auto qmldirFile = qmldirFileWithPath(self, path + QLatin1String("/qmldir"), options))
1624 return qmldirFile;
1625 return qmlDirectoryWithPath(self, path, options);
1626}
1627
1628QSet<QString> DomEnvironment::qmlDirPaths(const DomItem &self, EnvLookup options) const
1629{
1630 QSet<QString> res = qmlDirectoryPaths(self, options);
1631 const auto qmldirFiles = qmldirFilePaths(self, options);
1632 for (const QString &p : qmldirFiles) {
1633 if (p.endsWith(u"/qmldir")) {
1634 res.insert(p.left(p.size() - 7));
1635 } else {
1636 myErrors()
1637 .warning(tr("Unexpected path not ending with qmldir in qmldirFilePaths: %1")
1638 .arg(p))
1639 .handle();
1640 }
1641 }
1642 return res;
1643}
1644
1645std::shared_ptr<ExternalItemInfo<QmlFile>>
1646DomEnvironment::qmlFileWithPath(const DomItem &, const QString &path, EnvLookup options) const
1647{
1648 return lookup<QmlFile>(path, options);
1649}
1650
1651QSet<QString> DomEnvironment::qmlFilePaths(const DomItem &, EnvLookup lookup) const
1652{
1653 return getStrings<std::shared_ptr<ExternalItemInfo<QmlFile>>>(
1654 [this] {
1655 DomItem baseObj(m_base);
1656 return m_base->qmlFilePaths(baseObj, EnvLookup::Normal);
1657 },
1658 m_qmlFileWithPath, lookup);
1659}
1660
1661std::shared_ptr<ExternalItemInfo<JsFile>>
1662DomEnvironment::jsFileWithPath(const DomItem &, const QString &path, EnvLookup options) const
1663{
1664 return lookup<JsFile>(path, options);
1665}
1666
1667QSet<QString> DomEnvironment::jsFilePaths(const DomItem &, EnvLookup lookup) const
1668{
1669 return getStrings<std::shared_ptr<ExternalItemInfo<JsFile>>>(
1670 [this] {
1671 DomItem baseObj(m_base);
1672 return m_base->jsFilePaths(baseObj, EnvLookup::Normal);
1673 },
1674 m_jsFileWithPath, lookup);
1675}
1676
1677std::shared_ptr<ExternalItemInfo<QmltypesFile>>
1678DomEnvironment::qmltypesFileWithPath(const DomItem &, const QString &path, EnvLookup options) const
1679{
1680 return lookup<QmltypesFile>(path, options);
1681}
1682
1683QSet<QString> DomEnvironment::qmltypesFilePaths(const DomItem &, EnvLookup lookup) const
1684{
1685 return getStrings<std::shared_ptr<ExternalItemInfo<QmltypesFile>>>(
1686 [this] {
1687 DomItem baseObj(m_base);
1688 return m_base->qmltypesFilePaths(baseObj, EnvLookup::Normal);
1689 },
1690 m_qmltypesFileWithPath, lookup);
1691}
1692
1693std::shared_ptr<ExternalItemInfo<GlobalScope>>
1694DomEnvironment::globalScopeWithName(const DomItem &, const QString &name,
1695 EnvLookup lookupOptions) const
1696{
1697 return lookup<GlobalScope>(name, lookupOptions);
1698}
1699
1700std::shared_ptr<ExternalItemInfo<GlobalScope>>
1701DomEnvironment::ensureGlobalScopeWithName(const DomItem &self, const QString &name, EnvLookup lookupOptions)
1702{
1703 if (auto current = globalScopeWithName(self, name, lookupOptions))
1704 return current;
1705 if (auto u = universe()) {
1706 if (auto newVal = u->ensureGlobalScopeWithName(name)) {
1707 if (auto current = newVal->current) {
1708 DomItem currentObj = DomItem(u).copy(current);
1709 auto newScope = current->makeCopy(currentObj);
1710 auto newCopy = std::make_shared<ExternalItemInfo<GlobalScope>>(
1711 newScope);
1712 QMutexLocker l(mutex());
1713 if (auto oldVal = m_globalScopeWithName.value(name))
1714 return oldVal;
1715 m_globalScopeWithName.insert(name, newCopy);
1716 return newCopy;
1717 }
1718 }
1719 }
1720 Q_ASSERT_X(false, "DomEnvironment::ensureGlobalScopeWithName", "could not ensure globalScope");
1721 return {};
1722}
1723
1724QSet<QString> DomEnvironment::globalScopeNames(const DomItem &, EnvLookup lookupOptions) const
1725{
1726 QSet<QString> res;
1727 if (lookupOptions != EnvLookup::NoBase && m_base) {
1728 if (m_base) {
1729 DomItem baseObj(m_base);
1730 res = m_base->globalScopeNames(baseObj, EnvLookup::Normal);
1731 }
1732 }
1733 if (lookupOptions != EnvLookup::BaseOnly) {
1734 QMap<QString, std::shared_ptr<ExternalItemInfo<GlobalScope>>> map;
1735 {
1736 QMutexLocker l(mutex());
1737 map = m_globalScopeWithName;
1738 }
1739 auto it = map.keyBegin();
1740 auto end = map.keyEnd();
1741 while (it != end) {
1742 res += *it;
1743 ++it;
1744 }
1745 }
1746 return res;
1747}
1748
1749/*!
1750 \internal
1751 Depending on the creation options, this function adds LoadInfo of the provided path
1752*/
1753void DomEnvironment::addDependenciesToLoad(const Path &path)
1754{
1755 if (options() & Option::NoDependencies) {
1756 return;
1757 }
1758 Q_ASSERT(path);
1759 const auto loadInfo = std::make_shared<LoadInfo>(path);
1760 return addLoadInfo(DomItem(shared_from_this()), loadInfo);
1761}
1762
1763/*!
1764 \internal
1765 Enqueues path to the m_loadsWithWork (queue of the pending "load" jobs).
1766 In simpler words, schedule the load of the dependencies of the path from loadInfo.
1767*/
1768void DomEnvironment::addLoadInfo(const DomItem &self, const std::shared_ptr<LoadInfo> &loadInfo)
1769{
1770 if (!loadInfo)
1771 return;
1772 Path p = loadInfo->elementCanonicalPath();
1773 bool addWork = loadInfo->status() != LoadInfo::Status::Done;
1774 std::shared_ptr<LoadInfo> oldVal;
1775 {
1776 QMutexLocker l(mutex());
1777 oldVal = m_loadInfos.value(p);
1778 m_loadInfos.insert(p, loadInfo);
1779 if (addWork)
1780 m_loadsWithWork.enqueue(p);
1781 }
1782 if (oldVal && oldVal->status() != LoadInfo::Status::Done) {
1784 .error(tr("addLoadinfo replaces unfinished load info for %1")
1785 .arg(p.toString()))
1786 .handle());
1787 }
1788}
1789
1790std::shared_ptr<LoadInfo> DomEnvironment::loadInfo(const Path &path) const
1791{
1792 QMutexLocker l(mutex());
1793 return m_loadInfos.value(path);
1794}
1795
1796QHash<Path, std::shared_ptr<LoadInfo>> DomEnvironment::loadInfos() const
1797{
1798 QMutexLocker l(mutex());
1799 return m_loadInfos;
1800}
1801
1802QList<Path> DomEnvironment::loadInfoPaths() const
1803{
1804 auto lInfos = loadInfos();
1805 return lInfos.keys();
1806}
1807
1808DomItem::Callback DomEnvironment::getLoadCallbackFor(DomType fileType, const Callback &loadCallback)
1809{
1810 if (fileType == DomType::QmltypesFile) {
1811 return [loadCallback](const Path &p, const DomItem &oldV, const DomItem &newV) {
1812 DomItem newFile = newV.field(Fields::currentItem);
1813 if (std::shared_ptr<QmltypesFile> newFilePtr = newFile.ownerAs<QmltypesFile>())
1814 newFilePtr->ensureInModuleIndex(newFile);
1815 if (loadCallback)
1816 loadCallback(p, oldV, newV);
1817 };
1818 }
1819 return loadCallback;
1820}
1821
1822DomEnvironment::DomEnvironment(const QStringList &loadPaths, Options options,
1823 DomCreationOption domCreationOption,
1824 const shared_ptr<DomUniverse> &universe)
1825 : m_options(options),
1829 m_domCreationOption(domCreationOption)
1830
1831{
1832}
1833
1834/*!
1835\internal
1836Do not call this method inside of DomEnvironment's constructor! It requires weak_from_this() that
1837only works after the constructor call finished.
1838*/
1839DomEnvironment::SemanticAnalysis DomEnvironment::semanticAnalysis()
1840{
1841 // QTBUG-124799: do not create a SemanticAnalysis in a temporary DomEnvironment, and use the one
1842 // from the base environment instead.
1843 if (m_base) {
1844 auto result = m_base->semanticAnalysis();
1845 result.updateLoadPaths(m_loadPaths);
1846 return result;
1847 }
1848
1849 if (m_semanticAnalysis)
1850 return *m_semanticAnalysis;
1851
1853 m_semanticAnalysis = SemanticAnalysis(m_loadPaths);
1854 return *m_semanticAnalysis;
1855}
1856
1857DomEnvironment::SemanticAnalysis::SemanticAnalysis(const QStringList &loadPaths)
1858 : m_mapper(std::make_shared<QQmlJSResourceFileMapper>(
1859 QQmlJSUtils::resourceFilesFromBuildFolders(loadPaths))),
1860 m_importer(std::make_shared<QQmlJSImporter>(loadPaths, m_mapper.get(),
1861 QQmlJSImporterFlags{} | UseOptionalImports
1862 | TolerateFileSelectors
1863 | PreferQmlFilesFromSourceFolder))
1864{
1865}
1866
1867/*!
1868\internal
1869
1870Sets the new load paths in the importer and recreate the mapper.
1871
1872This affects all copies of SemanticAnalysis that use the same QQmlJSImporter and QQmlJSMapper
1873pointers.
1874*/
1875void DomEnvironment::SemanticAnalysis::updateLoadPaths(const QStringList &loadPaths)
1876{
1877 if (loadPaths == m_importer->importPaths())
1878 return;
1879
1880 m_importer->setImportPaths(loadPaths);
1881 *m_mapper = QQmlJSResourceFileMapper(QQmlJSUtils::resourceFilesFromBuildFolders(loadPaths));
1882}
1883
1884std::shared_ptr<DomEnvironment> DomEnvironment::create(const QStringList &loadPaths,
1885 Options options,
1886 DomCreationOption domCreationOption,
1887 const DomItem &universe)
1888{
1889 std::shared_ptr<DomUniverse> universePtr = universe.ownerAs<DomUniverse>();
1890 return std::make_shared<DomEnvironment>(loadPaths, options, domCreationOption, universePtr);
1891}
1892
1893DomEnvironment::DomEnvironment(const shared_ptr<DomEnvironment> &parent,
1894 const QStringList &loadPaths, Options options,
1895 DomCreationOption domCreationOption)
1896 : m_options(options),
1897 m_base(parent),
1900 m_domCreationOption(domCreationOption)
1901{
1902}
1903
1904void DomEnvironment::addQmlFile(const std::shared_ptr<QmlFile> &file, AddOption options)
1905{
1906 addExternalItem(file, file->canonicalFilePath(), options);
1908 QQmlJSScope::Ptr handle =
1909 semanticAnalysis().m_importer->importFile(file->canonicalFilePath());
1910
1911 // force reset the outdated qqmljsscope in case it was already populated
1912 QDeferredFactory<QQmlJSScope> newFactory(semanticAnalysis().m_importer.get(),
1913 file->canonicalFilePath(),
1914 TypeReader{ weak_from_this(), m_loadPaths });
1915 file->setHandleForPopulation(handle);
1916 handle.resetFactory(std::move(newFactory));
1917 }
1918}
1919
1920void DomEnvironment::addQmlDirectory(const std::shared_ptr<QmlDirectory> &file, AddOption options)
1921{
1922 addExternalItem(file, file->canonicalFilePath(), options);
1923}
1924
1925void DomEnvironment::addQmldirFile(const std::shared_ptr<QmldirFile> &file, AddOption options)
1926{
1927 addExternalItem(file, file->canonicalFilePath(), options);
1928}
1929
1930void DomEnvironment::addQmltypesFile(const std::shared_ptr<QmltypesFile> &file, AddOption options)
1931{
1932 addExternalItem(file, file->canonicalFilePath(), options);
1933}
1934
1935void DomEnvironment::addJsFile(const std::shared_ptr<JsFile> &file, AddOption options)
1936{
1937 addExternalItem(file, file->canonicalFilePath(), options);
1938}
1939
1940void DomEnvironment::addGlobalScope(const std::shared_ptr<GlobalScope> &scope, AddOption options)
1941{
1942 addExternalItem(scope, scope->name(), options);
1943}
1944
1945QList<QQmlJS::DiagnosticMessage>
1946DomEnvironment::TypeReader::operator()(QQmlJSImporter *importer, const QString &filePath,
1947 const QSharedPointer<QQmlJSScope> &scopeToPopulate)
1948{
1949 Q_UNUSED(importer);
1950 Q_UNUSED(scopeToPopulate);
1951
1952 const QFileInfo info{ filePath };
1953 const QString baseName = info.baseName();
1954 scopeToPopulate->setInternalName(baseName.endsWith(QStringLiteral(".ui")) ? baseName.chopped(3)
1955 : baseName);
1956
1957 std::shared_ptr<DomEnvironment> envPtr = m_env.lock();
1958 // populate QML File if from implicit import directory
1959 // use the version in DomEnvironment and do *not* load from disk.
1960 auto it = envPtr->m_qmlFileWithPath.constFind(filePath);
1961 if (it == envPtr->m_qmlFileWithPath.constEnd()) {
1962 qCDebug(domLog) << "Import visitor tried to lazily load file \"" << filePath
1963 << "\", but that file was not found in the DomEnvironment. Was this "
1964 "file not discovered by the Dom's dependency loading mechanism?";
1965 return { QQmlJS::DiagnosticMessage{
1966 u"Could not find file \"%1\" in the Dom."_s.arg(filePath), QtMsgType::QtWarningMsg,
1967 SourceLocation{} } };
1968 }
1969 const DomItem qmlFile = it.value()->currentItem(DomItem(envPtr));
1970
1971 // workaround for QTBUG-137705 while waiting for qmlls to use separate DomEnvironments for files
1972 // requiring different importpaths (QTBUG-134308).
1973 const QStringList oldImportPaths = envPtr->loadPaths();
1974 envPtr->setLoadPaths(m_importPaths);
1975 envPtr->populateFromQmlFile(MutableDomItem(qmlFile));
1976 envPtr->setLoadPaths(oldImportPaths);
1977 return {};
1978}
1979
1980
1981bool DomEnvironment::commitToBase(
1982 const DomItem &self, const shared_ptr<DomEnvironment> &validEnvPtr)
1983{
1984 if (!base())
1985 return false;
1986 QMap<QString, QMap<int, std::shared_ptr<ModuleIndex>>> my_moduleIndexWithUri;
1987 QMap<QString, std::shared_ptr<ExternalItemInfo<GlobalScope>>> my_globalScopeWithName;
1988 QMap<QString, std::shared_ptr<ExternalItemInfo<QmlDirectory>>> my_qmlDirectoryWithPath;
1989 QMap<QString, std::shared_ptr<ExternalItemInfo<QmldirFile>>> my_qmldirFileWithPath;
1990 QMap<QString, std::shared_ptr<ExternalItemInfo<QmlFile>>> my_qmlFileWithPath;
1991 QMap<QString, std::shared_ptr<ExternalItemInfo<JsFile>>> my_jsFileWithPath;
1992 QMap<QString, std::shared_ptr<ExternalItemInfo<QmltypesFile>>> my_qmltypesFileWithPath;
1993 QHash<Path, std::shared_ptr<LoadInfo>> my_loadInfos;
1994 std::optional<SemanticAnalysis> my_semanticAnalysis;
1995 {
1996 QMutexLocker l(mutex());
1997 my_moduleIndexWithUri = m_moduleIndexWithUri;
1998 my_globalScopeWithName = m_globalScopeWithName;
1999 my_qmlDirectoryWithPath = m_qmlDirectoryWithPath;
2000 my_qmldirFileWithPath = m_qmldirFileWithPath;
2001 my_qmlFileWithPath = m_qmlFileWithPath;
2002 my_jsFileWithPath = m_jsFileWithPath;
2003 my_qmltypesFileWithPath = m_qmltypesFileWithPath;
2004 my_loadInfos = m_loadInfos;
2005 my_semanticAnalysis = semanticAnalysis();
2006 }
2007 {
2008 QMutexLocker lBase(base()->mutex()); // be more careful about makeCopy calls with lock?
2009 m_base->m_semanticAnalysis = my_semanticAnalysis;
2010 m_base->m_globalScopeWithName.insert(my_globalScopeWithName);
2011 m_base->m_qmlDirectoryWithPath.insert(my_qmlDirectoryWithPath);
2012 m_base->m_qmldirFileWithPath.insert(my_qmldirFileWithPath);
2013 m_base->m_qmlFileWithPath.insert(my_qmlFileWithPath);
2014 m_base->m_jsFileWithPath.insert(my_jsFileWithPath);
2015 m_base->m_qmltypesFileWithPath.insert(my_qmltypesFileWithPath);
2016 m_base->m_loadInfos.insert(my_loadInfos);
2017 {
2018 auto it = my_moduleIndexWithUri.cbegin();
2019 auto end = my_moduleIndexWithUri.cend();
2020 while (it != end) {
2021 QMap<int, shared_ptr<ModuleIndex>> &myVersions =
2022 m_base->m_moduleIndexWithUri[it.key()];
2023 auto it2 = it.value().cbegin();
2024 auto end2 = it.value().cend();
2025 while (it2 != end2) {
2026 auto oldV = myVersions.value(it2.key());
2027 DomItem it2Obj = self.copy(it2.value());
2028 auto newV = it2.value()->makeCopy(it2Obj);
2029 newV->mergeWith(oldV);
2030 myVersions.insert(it2.key(), newV);
2031 ++it2;
2032 }
2033 ++it;
2034 }
2035 }
2036 }
2037 if (validEnvPtr)
2038 m_lastValidBase = validEnvPtr;
2039 if (m_lastValidBase) {
2040 QMutexLocker lValid(
2041 m_lastValidBase->mutex()); // be more careful about makeCopy calls with lock?
2042 m_lastValidBase->m_semanticAnalysis = std::move(my_semanticAnalysis);
2043 m_lastValidBase->m_globalScopeWithName.insert(my_globalScopeWithName);
2044 m_lastValidBase->m_qmlDirectoryWithPath.insert(my_qmlDirectoryWithPath);
2045 m_lastValidBase->m_qmldirFileWithPath.insert(my_qmldirFileWithPath);
2046 for (auto it = my_qmlFileWithPath.cbegin(), end = my_qmlFileWithPath.cend(); it != end;
2047 ++it) {
2048 if (it.value() && it.value()->current && it.value()->current->isValid())
2049 m_lastValidBase->m_qmlFileWithPath.insert(it.key(), it.value());
2050 }
2051 for (auto it = my_jsFileWithPath.cbegin(), end = my_jsFileWithPath.cend(); it != end;
2052 ++it) {
2053 if (it.value() && it.value()->current && it.value()->current->isValid())
2054 m_lastValidBase->m_jsFileWithPath.insert(it.key(), it.value());
2055 }
2056 m_lastValidBase->m_qmltypesFileWithPath.insert(my_qmltypesFileWithPath);
2057 m_lastValidBase->m_loadInfos.insert(my_loadInfos);
2058 for (auto it = my_moduleIndexWithUri.cbegin(), end = my_moduleIndexWithUri.cend();
2059 it != end; ++it) {
2060 QMap<int, shared_ptr<ModuleIndex>> &myVersions =
2061 m_lastValidBase->m_moduleIndexWithUri[it.key()];
2062 for (auto it2 = it.value().cbegin(), end2 = it.value().cend(); it2 != end2; ++it2) {
2063 auto oldV = myVersions.value(it2.key());
2064 DomItem it2Obj = self.copy(it2.value());
2065 auto newV = it2.value()->makeCopy(it2Obj);
2066 newV->mergeWith(oldV);
2067 myVersions.insert(it2.key(), newV);
2068 }
2069 }
2070 }
2071
2072 auto newBaseForPopulation =
2073 m_lastValidBase ? m_lastValidBase->weak_from_this() : m_base->weak_from_this();
2074 // adapt the factory to the use the base or valid environment for unpopulated files, instead of
2075 // the current environment which will very probably be destroyed anytime soon
2076 for (const auto &qmlFile : my_qmlFileWithPath) {
2077 if (!qmlFile || !qmlFile->current)
2078 continue;
2079 QQmlJSScope::ConstPtr handle = qmlFile->current->handleForPopulation();
2080 if (!handle)
2081 continue;
2082 auto oldFactory = handle.factory();
2083 if (!oldFactory)
2084 continue;
2085
2086 const QDeferredFactory<QQmlJSScope> newFactory(
2087 oldFactory->importer(), oldFactory->filePath(),
2088 TypeReader{ newBaseForPopulation, m_loadPaths });
2089 handle.resetFactory(newFactory);
2090 }
2091 return true;
2092}
2093
2094void DomEnvironment::loadPendingDependencies()
2095{
2096 DomItem self(shared_from_this());
2097 while (true) {
2098 Path elToDo;
2099 std::shared_ptr<LoadInfo> loadInfo;
2100 {
2101 QMutexLocker l(mutex());
2102 if (m_loadsWithWork.isEmpty())
2103 break;
2104 elToDo = m_loadsWithWork.dequeue();
2105 m_inProgress.append(elToDo);
2106 loadInfo = m_loadInfos.value(elToDo);
2107 }
2108 if (loadInfo) {
2109 auto cleanup = qScopeGuard([this, &elToDo, &self] {
2110 QList<Callback> endCallbacks;
2111 {
2112 QMutexLocker l(mutex());
2113 m_inProgress.removeOne(elToDo);
2114 if (m_inProgress.isEmpty() && m_loadsWithWork.isEmpty()) {
2115 endCallbacks = m_allLoadedCallback;
2116 m_allLoadedCallback.clear();
2117 }
2118 }
2119 for (const Callback &cb : std::as_const(endCallbacks))
2120 cb(self.canonicalPath(), self, self);
2121 });
2122 DomItem loadInfoObj = self.copy(loadInfo);
2123 loadInfo->advanceLoad(loadInfoObj);
2124 } else {
2125 self.addError(myErrors().error(u"DomEnvironment::loadPendingDependencies could not "
2126 u"find loadInfo listed in m_loadsWithWork"_sv));
2127 {
2128 QMutexLocker l(mutex());
2129 m_inProgress.removeOne(elToDo);
2130 }
2131 Q_ASSERT(false
2132 && "DomEnvironment::loadPendingDependencies could not find loadInfo listed in "
2133 "m_loadsWithWork");
2134 }
2135 }
2136}
2137
2138bool DomEnvironment::finishLoadingDependencies(int waitMSec)
2139{
2140 bool hasPendingLoads = true;
2141 QDateTime endTime = QDateTime::currentDateTimeUtc().addMSecs(waitMSec);
2142 for (int i = 0; i < waitMSec / 10 + 2; ++i) {
2144 auto lInfos = loadInfos();
2145 auto it = lInfos.cbegin();
2146 auto end = lInfos.cend();
2147 hasPendingLoads = false;
2148 while (it != end) {
2149 if (*it && (*it)->status() != LoadInfo::Status::Done)
2150 hasPendingLoads = true;
2151 }
2152 if (!hasPendingLoads)
2153 break;
2154 auto missing = QDateTime::currentDateTimeUtc().msecsTo(endTime);
2155 if (missing < 0)
2156 break;
2157 if (missing > 100)
2158 missing = 100;
2159#if QT_FEATURE_thread
2160 QThread::msleep(missing);
2161#endif
2162 }
2163 return !hasPendingLoads;
2164}
2165
2166void DomEnvironment::addWorkForLoadInfo(const Path &elementCanonicalPath)
2167{
2168 QMutexLocker l(mutex());
2169 m_loadsWithWork.enqueue(elementCanonicalPath);
2170}
2171
2172DomEnvironment::Options DomEnvironment::options() const
2173{
2174 return m_options;
2175}
2176
2177std::shared_ptr<DomEnvironment> DomEnvironment::base() const
2178{
2179 return m_base;
2180}
2181
2182void DomEnvironment::setLoadPaths(const QStringList &v)
2183{
2184 QMutexLocker l(mutex());
2185 m_loadPaths = v;
2186
2187 if (m_semanticAnalysis)
2188 m_semanticAnalysis->updateLoadPaths(v);
2189}
2190
2191QStringList DomEnvironment::loadPaths() const
2192{
2193 QMutexLocker l(mutex());
2194 return m_loadPaths;
2195}
2196
2197QStringList DomEnvironment::qmldirFiles() const
2198{
2199 QMutexLocker l(mutex());
2200 return m_qmldirFileWithPath.keys();
2201}
2202
2203QString DomEnvironment::globalScopeName() const
2204{
2205 return m_globalScopeName;
2206}
2207
2208QList<Import> DomEnvironment::defaultImplicitImports()
2209{
2210 return QList<Import>({ Import::fromUriString(u"QML"_s, Version(1, 0)),
2211 Import(QmlUri::fromUriString(u"QtQml"_s), Version(6, 0)) });
2212}
2213
2214QList<Import> DomEnvironment::implicitImports() const
2215{
2216 return m_implicitImports;
2217}
2218
2219void DomEnvironment::addAllLoadedCallback(const DomItem &self, DomTop::Callback c)
2220{
2221 if (c) {
2222 bool immediate = false;
2223 {
2224 QMutexLocker l(mutex());
2225 if (m_loadsWithWork.isEmpty() && m_inProgress.isEmpty())
2226 immediate = true;
2227 else
2228 m_allLoadedCallback.append(c);
2229 }
2230 if (immediate)
2231 c(Path(), self, self);
2232 }
2233}
2234
2235void DomEnvironment::clearReferenceCache()
2236{
2237 m_referenceCache.clear();
2238}
2239
2240void DomEnvironment::populateFromQmlFile(MutableDomItem &&qmlFile)
2241{
2242 if (std::shared_ptr<QmlFile> qmlFilePtr = qmlFile.ownerAs<QmlFile>()) {
2243 auto logger = std::make_shared<QQmlJSLogger>();
2244 logger->setFilePath(qmlFile.canonicalFilePath());
2245 logger->setCode(qmlFilePtr->code());
2246 logger->setSilent(true);
2247
2248 auto setupFile = [&qmlFilePtr, &qmlFile, this](auto &&visitor) {
2249 Q_UNUSED(this); // note: integrity requires "this" to be in the capture list, while
2250 // other compilers complain about "this" being unused in the lambda
2251 AST::Node::accept(qmlFilePtr->ast(), visitor);
2252
2253 if (m_domCreationOption == DomCreationOption::Minimal)
2254 return;
2255
2256 CommentCollector collector(qmlFile);
2257 collector.collectComments();
2258 };
2259
2260 if (m_domCreationOption == DomCreationOption::Extended) {
2261 SemanticAnalysis analysis = semanticAnalysis();
2262 auto scope = analysis.m_importer->importFile(qmlFile.canonicalFilePath());
2263 auto v = std::make_unique<QQmlDomAstCreatorWithQQmlJSScope>(
2264 scope, qmlFile, logger.get(), analysis.m_importer.get());
2265 v->enableLoadFileLazily(true);
2266 v->enableScriptExpressions(true);
2267
2268 setupFile(v.get());
2269
2270 auto typeResolver =
2271 std::make_shared<QQmlJSTypeResolver>(analysis.m_importer.get());
2272 typeResolver->init(&v->scopeCreator(), nullptr);
2273 qmlFilePtr->setTypeResolverWithDependencies(
2274 typeResolver, { analysis.m_importer, analysis.m_mapper, std::move(logger) });
2275 } else {
2276 auto v = std::make_unique<QQmlDomAstCreator>(qmlFile);
2277 // we need file locations of script expressions in qmlformat for the "// qmlformat off"
2278 // functionality
2279 v->enableScriptExpressions(m_domCreationOption == DomCreationOption::Default);
2280 setupFile(v.get());
2281 }
2282 } else {
2283 qCWarning(domLog) << "populateQmlFile called on non qmlFile";
2284 return;
2285 }
2286}
2287
2289{
2290 shared_ptr<ExternalOwningItem> current = currentItem();
2291 DomItem currentObj = currentItem(self);
2292 return current->canonicalFilePath(currentObj);
2293}
2294
2295bool ExternalItemInfoBase::iterateDirectSubpaths(const DomItem &self, DirectVisitor visitor) const
2296{
2297 if (!self.dvValueLazyField(visitor, Fields::currentRevision,
2298 [this, &self]() { return currentRevision(self); }))
2299 return false;
2300 if (!self.dvValueLazyField(visitor, Fields::lastRevision,
2301 [this, &self]() { return lastRevision(self); }))
2302 return false;
2303 if (!self.dvValueLazyField(visitor, Fields::lastValidRevision,
2304 [this, &self]() { return lastValidRevision(self); }))
2305 return false;
2306 if (!visitor(PathEls::Field(Fields::currentItem),
2307 [&self, this]() { return currentItem(self); }))
2308 return false;
2309 if (!self.dvValueLazyField(visitor, Fields::currentExposedAt,
2310 [this]() { return currentExposedAt(); }))
2311 return false;
2312 return true;
2313}
2314
2316{
2317 return currentItem()->revision();
2318}
2319
2321{
2322 Path p = currentItem()->canonicalPath();
2323 DomItem lastValue = self.universe()[p.mid(1, p.length() - 1)].field(u"revision");
2324 return static_cast<int>(lastValue.value().toInteger(0));
2325}
2326
2328{
2329 Path p = currentItem()->canonicalPath();
2330 DomItem lastValidValue = self.universe()[p.mid(1, p.length() - 2)].field(u"validItem").field(u"revision");
2331 return static_cast<int>(lastValidValue.value().toInteger(0));
2332}
2333
2335{
2336 shared_ptr<ExternalOwningItem> current = currentItem();
2337 return current->canonicalFilePath();
2338}
2339
2341{
2342 shared_ptr<ExternalOwningItem> current = currentItem();
2343 return current->canonicalPath().dropTail();
2344}
2345
2346bool ExternalItemPairBase::iterateDirectSubpaths(const DomItem &self, DirectVisitor visitor) const
2347{
2348 if (!self.dvValueLazyField(visitor, Fields::currentIsValid,
2349 [this]() { return currentIsValid(); }))
2350 return false;
2351 if (!visitor(PathEls::Field(Fields::validItem), [this, &self]() { return validItem(self); }))
2352 return false;
2353 if (!visitor(PathEls::Field(Fields::currentItem),
2354 [this, &self]() { return currentItem(self); }))
2355 return false;
2356 if (!self.dvValueField(visitor, Fields::validExposedAt, validExposedAt))
2357 return false;
2358 if (!self.dvValueField(visitor, Fields::currentExposedAt, currentExposedAt))
2359 return false;
2360 return true;
2361}
2362
2364{
2365 return currentItem() == validItem();
2366}
2367
2368RefCacheEntry RefCacheEntry::forPath(const DomItem &el, const Path &canonicalPath)
2369{
2370 DomItem env = el.environment();
2371 std::shared_ptr<DomEnvironment> envPtr = env.ownerAs<DomEnvironment>();
2372 RefCacheEntry cached;
2373 if (envPtr) {
2374 QMutexLocker l(envPtr->mutex());
2375 cached = envPtr->m_referenceCache.value(canonicalPath, {});
2376 } else {
2377 qCWarning(domLog) << "No Env for reference" << canonicalPath << "from"
2378 << el.internalKindStr() << el.canonicalPath();
2379 Q_ASSERT(false);
2380 }
2381 return cached;
2382}
2383
2384bool RefCacheEntry::addForPath(const DomItem &el, const Path &canonicalPath, const RefCacheEntry &entry,
2385 AddOption addOption)
2386{
2387 DomItem env = el.environment();
2388 std::shared_ptr<DomEnvironment> envPtr = env.ownerAs<DomEnvironment>();
2389 bool didSet = false;
2390 if (envPtr) {
2391 QMutexLocker l(envPtr->mutex());
2392 RefCacheEntry &cached = envPtr->m_referenceCache[canonicalPath];
2393 switch (cached.cached) {
2394 case RefCacheEntry::Cached::None:
2395 cached = entry;
2396 didSet = true;
2397 break;
2398 case RefCacheEntry::Cached::First:
2399 if (addOption == AddOption::Overwrite || entry.cached == RefCacheEntry::Cached::All) {
2400 cached = entry;
2401 didSet = true;
2402 }
2403 break;
2404 case RefCacheEntry::Cached::All:
2405 if (addOption == AddOption::Overwrite || entry.cached == RefCacheEntry::Cached::All) {
2406 cached = entry;
2407 didSet = true;
2408 }
2409 }
2410 if (cached.cached == RefCacheEntry::Cached::First && cached.canonicalPaths.isEmpty())
2411 cached.cached = RefCacheEntry::Cached::All;
2412 } else {
2413 Q_ASSERT(false);
2414 }
2415 return didSet;
2416}
2417
2418} // end namespace Dom
2419} // end namespace QQmlJS
2420
2421QT_END_NAMESPACE
2422
2423#include "moc_qqmldomtop_p.cpp"
CommentCollector(MutableDomItem item)
SemanticAnalysis semanticAnalysis()
QString globalScopeName() const
void addQmlFile(const std::shared_ptr< QmlFile > &file, AddOption option=AddOption::KeepExisting)
bool finishLoadingDependencies(int waitMSec=30000)
QStringList qmldirFiles() const
std::shared_ptr< ExternalItemInfo< QmlFile > > qmlFileWithPath(const DomItem &self, const QString &path, EnvLookup options=EnvLookup::Normal) const
QSet< QString > moduleIndexUris(const DomItem &self, EnvLookup lookup=EnvLookup::Normal) const
void addGlobalScope(const std::shared_ptr< GlobalScope > &file, AddOption option=AddOption::KeepExisting)
void populateFromQmlFile(MutableDomItem &&qmlFile)
void addQmlDirectory(const std::shared_ptr< QmlDirectory > &file, AddOption option=AddOption::KeepExisting)
void addQmldirFile(const std::shared_ptr< QmldirFile > &file, AddOption option=AddOption::KeepExisting)
std::shared_ptr< LoadInfo > loadInfo(const Path &path) const
void addDependenciesToLoad(const Path &path)
std::shared_ptr< ModuleIndex > moduleIndexWithUri(const DomItem &self, const QString &uri, int majorVersion, EnvLookup lookup, Changeable changeable, const ErrorHandler &errorHandler=nullptr)
Path canonicalPath() const override
std::shared_ptr< ExternalItemInfoBase > qmlDirWithPath(const DomItem &self, const QString &path, EnvLookup options=EnvLookup::Normal) const
void addJsFile(const std::shared_ptr< JsFile > &file, AddOption option=AddOption::KeepExisting)
DomItem field(const DomItem &self, QStringView name) const final override
void removePath(const QString &path)
bool commitToBase(const DomItem &self, const std::shared_ptr< DomEnvironment > &validEnv=nullptr)
QSet< QString > qmlFilePaths(const DomItem &self, EnvLookup lookup=EnvLookup::Normal) const
DomEnvironment(const std::shared_ptr< DomEnvironment > &parent, const QStringList &loadPaths, Options options=Option::SingleThreaded, DomCreationOption domCreationOption=Default)
std::shared_ptr< ExternalItemInfo< QmlDirectory > > qmlDirectoryWithPath(const DomItem &self, const QString &path, EnvLookup options=EnvLookup::Normal) const
std::shared_ptr< ExternalItemInfo< GlobalScope > > globalScopeWithName(const DomItem &self, const QString &name, EnvLookup lookup=EnvLookup::Normal) const
QSet< QString > qmlDirPaths(const DomItem &self, EnvLookup options=EnvLookup::Normal) const
std::shared_ptr< ExternalItemInfo< GlobalScope > > ensureGlobalScopeWithName(const DomItem &self, const QString &name, EnvLookup lookup=EnvLookup::Normal)
static ErrorGroups myErrors()
QSet< int > moduleIndexMajorVersions(const DomItem &self, const QString &uri, EnvLookup lookup=EnvLookup::Normal) const
QSet< QString > qmldirFilePaths(const DomItem &self, EnvLookup options=EnvLookup::Normal) const
void setLoadPaths(const QStringList &v)
QSet< QString > globalScopeNames(const DomItem &self, EnvLookup lookup=EnvLookup::Normal) const
std::shared_ptr< DomEnvironment > base() const
std::shared_ptr< DomEnvironment > makeCopy(const DomItem &self) const
void loadBuiltins(const Callback &callback=nullptr, const ErrorHandler &h=nullptr)
std::shared_ptr< ModuleIndex > moduleIndexWithUri(const DomItem &self, const QString &uri, int majorVersion, EnvLookup lookup=EnvLookup::Normal) const
QSet< QString > qmlDirectoryPaths(const DomItem &self, EnvLookup options=EnvLookup::Normal) const
QList< Path > loadInfoPaths() const
void addQmltypesFile(const std::shared_ptr< QmltypesFile > &file, AddOption option=AddOption::KeepExisting)
std::shared_ptr< ExternalItemInfo< JsFile > > jsFileWithPath(const DomItem &self, const QString &path, EnvLookup options=EnvLookup::Normal) const
void addWorkForLoadInfo(const Path &elementCanonicalPath)
std::shared_ptr< OwningItem > doCopy(const DomItem &self) const override
std::shared_ptr< ExternalItemInfo< QmltypesFile > > qmltypesFileWithPath(const DomItem &self, const QString &path, EnvLookup options=EnvLookup::Normal) const
static constexpr DomType kindValue
void addLoadInfo(const DomItem &self, const std::shared_ptr< LoadInfo > &loadInfo)
DomEnvironment(const QStringList &loadPaths, Options options=Option::SingleThreaded, DomCreationOption domCreationOption=Default, const std::shared_ptr< DomUniverse > &universe=nullptr)
void loadFile(const FileToLoad &file, const Callback &callback, std::optional< DomType > fileType=std::optional< DomType >(), const ErrorHandler &h=nullptr)
std::shared_ptr< DomUniverse > universe() const
DomCreationOption domCreationOption() const
QList< Import > implicitImports() const
QHash< Path, std::shared_ptr< LoadInfo > > loadInfos() const
bool iterateDirectSubpaths(const DomItem &self, DirectVisitor) const override
std::shared_ptr< ExternalItemInfo< QmldirFile > > qmldirFileWithPath(const DomItem &self, const QString &path, EnvLookup options=EnvLookup::Normal) const
QSet< QString > qmltypesFilePaths(const DomItem &self, EnvLookup lookup=EnvLookup::Normal) const
DomType kind() const override
void addAllLoadedCallback(const DomItem &self, Callback c)
QStringList loadPaths() const
QSet< QString > jsFilePaths(const DomItem &self, EnvLookup lookup=EnvLookup::Normal) const
A value type that references any element of the Dom.
DomItem path(const Path &p, const ErrorHandler &h=&defaultErrorHandler) const
function< void(const Path &, const DomItem &, const DomItem &)> Callback
DomItem universe() const
DomItem environment() const
Path canonicalPath() const
void addError(ErrorMessage &&msg) const
InternalKind internalKind() const
ErrorHandler errorHandler() const
DomItem operator[](const Path &path) const
static DomItem empty
static ErrorGroup domErrorGroup
Path canonicalPath(const DomItem &) const override
QQml::Dom::DomTop::loadFile.
bool iterateDirectSubpaths(const DomItem &self, DirectVisitor) const override
QMap< QString, OwnerT > extraOwningItems() const
virtual Path canonicalPath() const =0
DomItem containingObject(const DomItem &) const override
static ErrorGroups myErrors()
std::shared_ptr< OwningItem > doCopy(const DomItem &self) const override
static std::shared_ptr< DomUniverse > guaranteeUniverse(const std::shared_ptr< DomUniverse > &univ)
void removePath(const QString &dir)
DomUniverse(const QString &universeName)
LoadResult loadFile(const FileToLoad &file, DomType fileType, DomCreationOption creationOption={})
Path canonicalPath() const override
bool iterateDirectSubpaths(const DomItem &self, DirectVisitor) const override
Represents a set of tags grouping a set of related error messages.
ErrorMessage warning(const Dumper &message) const
ErrorMessage error(const Dumper &message) const
Represents an error message connected to the dom.
ErrorMessage handle(const ErrorHandler &errorHandler=nullptr)
QString canonicalFilePath(const DomItem &) const final override
bool iterateDirectSubpaths(const DomItem &self, DirectVisitor) const final override
int lastValidRevision(const DomItem &self) const
int lastRevision(const DomItem &self) const
int currentRevision(const DomItem &self) const
virtual DomItem currentItem(const DomItem &) const =0
bool iterateDirectSubpaths(const DomItem &self, DirectVisitor) const final override
QString canonicalFilePath(const DomItem &) const final override
Path canonicalPath(const DomItem &self) const final override
virtual DomItem validItem(const DomItem &self) const =0
virtual std::shared_ptr< ExternalOwningItem > currentItem() const =0
virtual std::shared_ptr< ExternalOwningItem > validItem() const =0
virtual DomItem currentItem(const DomItem &self) const =0
void addEndCallback(const DomItem &self, std::function< void(Path, const DomItem &, const DomItem &)> callback)
void finishedLoadingDep(const DomItem &self, const Dependency &d)
Status status() const
void execEnd(const DomItem &self)
void advanceLoad(const DomItem &self)
std::shared_ptr< OwningItem > doCopy(const DomItem &self) const override
Path canonicalPath(const DomItem &self) const override
Path elementCanonicalPath() const
bool iterateDirectSubpaths(const DomItem &self, DirectVisitor) const override
MutableDomItem(const DomItem &item)
A DomItem that owns other DomItems and is managed through a shared pointer.
void addErrorLocal(ErrorMessage &&msg)
static Path fromRoot(PathRoot r)
Path mid(int offset, int length) const
static bool addForPath(const DomItem &el, const Path &canonicalPath, const RefCacheEntry &entry, AddOption addOption=AddOption::KeepExisting)
static constexpr DomType kindValue
static DomType fileTypeForPath(const DomItem &self, const QString &canonicalFilePath)
std::function< void(const ErrorMessage &)> ErrorHandler
#define NewErrorGroup(name)