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