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 });
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 std::shared_ptr<DomEnvironment> envPtr = m_env.lock();
1979 // populate QML File if from implicit import directory
1980 // use the version in DomEnvironment and do *not* load from disk.
1981 auto it = envPtr->m_qmlFileWithPath.constFind(filePath);
1982 if (it == envPtr->m_qmlFileWithPath.constEnd()) {
1983 qCDebug(domLog) << "Import visitor tried to lazily load file \"" << filePath
1984 << "\", but that file was not found in the DomEnvironment. Was this "
1985 "file not discovered by the Dom's dependency loading mechanism?";
1986 return;
1987 }
1988 const DomItem qmlFile = it.value()->currentItem(DomItem(envPtr));
1989
1990 // workaround for QTBUG-137705 while waiting for qmlls to use separate DomEnvironments for files
1991 // requiring different importpaths (QTBUG-134308).
1992 const QStringList oldImportPaths = envPtr->loadPaths();
1993 envPtr->setLoadPaths(m_importPaths);
1994 envPtr->populateFromQmlFile(MutableDomItem(qmlFile));
1995 envPtr->setLoadPaths(oldImportPaths);
1996}
1997
1998
1999bool DomEnvironment::commitToBase(
2000 const DomItem &self, const shared_ptr<DomEnvironment> &validEnvPtr)
2001{
2002 if (!base())
2003 return false;
2004 QMap<QString, QMap<int, std::shared_ptr<ModuleIndex>>> my_moduleIndexWithUri;
2005 QMap<QString, std::shared_ptr<ExternalItemInfo<GlobalScope>>> my_globalScopeWithName;
2006 QMap<QString, std::shared_ptr<ExternalItemInfo<QmlDirectory>>> my_qmlDirectoryWithPath;
2007 QMap<QString, std::shared_ptr<ExternalItemInfo<QmldirFile>>> my_qmldirFileWithPath;
2008 QMap<QString, std::shared_ptr<ExternalItemInfo<QmlFile>>> my_qmlFileWithPath;
2009 QMap<QString, std::shared_ptr<ExternalItemInfo<JsFile>>> my_jsFileWithPath;
2010 QMap<QString, std::shared_ptr<ExternalItemInfo<QmltypesFile>>> my_qmltypesFileWithPath;
2011 QHash<Path, std::shared_ptr<LoadInfo>> my_loadInfos;
2012 std::optional<SemanticAnalysis> my_semanticAnalysis;
2013 {
2014 QMutexLocker l(mutex());
2015 my_moduleIndexWithUri = m_moduleIndexWithUri;
2016 my_globalScopeWithName = m_globalScopeWithName;
2017 my_qmlDirectoryWithPath = m_qmlDirectoryWithPath;
2018 my_qmldirFileWithPath = m_qmldirFileWithPath;
2019 my_qmlFileWithPath = m_qmlFileWithPath;
2020 my_jsFileWithPath = m_jsFileWithPath;
2021 my_qmltypesFileWithPath = m_qmltypesFileWithPath;
2022 my_loadInfos = m_loadInfos;
2023 my_semanticAnalysis = semanticAnalysisUnlocked();
2024 }
2025 {
2026 QMutexLocker lBase(base()->mutex()); // be more careful about makeCopy calls with lock?
2027 m_base->m_semanticAnalysis = my_semanticAnalysis;
2028 m_base->m_globalScopeWithName.insert(my_globalScopeWithName);
2029 m_base->m_qmlDirectoryWithPath.insert(my_qmlDirectoryWithPath);
2030 m_base->m_qmldirFileWithPath.insert(my_qmldirFileWithPath);
2031 m_base->m_qmlFileWithPath.insert(my_qmlFileWithPath);
2032 m_base->m_jsFileWithPath.insert(my_jsFileWithPath);
2033 m_base->m_qmltypesFileWithPath.insert(my_qmltypesFileWithPath);
2034 m_base->m_loadInfos.insert(my_loadInfos);
2035 {
2036 auto it = my_moduleIndexWithUri.cbegin();
2037 auto end = my_moduleIndexWithUri.cend();
2038 while (it != end) {
2039 QMap<int, shared_ptr<ModuleIndex>> &myVersions =
2040 m_base->m_moduleIndexWithUri[it.key()];
2041 auto it2 = it.value().cbegin();
2042 auto end2 = it.value().cend();
2043 while (it2 != end2) {
2044 auto oldV = myVersions.value(it2.key());
2045 DomItem it2Obj = self.copy(it2.value());
2046 auto newV = it2.value()->makeCopy(it2Obj);
2047 newV->mergeWith(oldV);
2048 myVersions.insert(it2.key(), newV);
2049 ++it2;
2050 }
2051 ++it;
2052 }
2053 }
2054 }
2055 if (validEnvPtr)
2056 m_lastValidBase = validEnvPtr;
2057 if (m_lastValidBase) {
2058 QMutexLocker lValid(
2059 m_lastValidBase->mutex()); // be more careful about makeCopy calls with lock?
2060 m_lastValidBase->m_semanticAnalysis = std::move(my_semanticAnalysis);
2061 m_lastValidBase->m_globalScopeWithName.insert(my_globalScopeWithName);
2062 m_lastValidBase->m_qmlDirectoryWithPath.insert(my_qmlDirectoryWithPath);
2063 m_lastValidBase->m_qmldirFileWithPath.insert(my_qmldirFileWithPath);
2064 for (auto it = my_qmlFileWithPath.cbegin(), end = my_qmlFileWithPath.cend(); it != end;
2065 ++it) {
2066 if (it.value() && it.value()->current && it.value()->current->isValid())
2067 m_lastValidBase->m_qmlFileWithPath.insert(it.key(), it.value());
2068 }
2069 for (auto it = my_jsFileWithPath.cbegin(), end = my_jsFileWithPath.cend(); it != end;
2070 ++it) {
2071 if (it.value() && it.value()->current && it.value()->current->isValid())
2072 m_lastValidBase->m_jsFileWithPath.insert(it.key(), it.value());
2073 }
2074 m_lastValidBase->m_qmltypesFileWithPath.insert(my_qmltypesFileWithPath);
2075 m_lastValidBase->m_loadInfos.insert(my_loadInfos);
2076 for (auto it = my_moduleIndexWithUri.cbegin(), end = my_moduleIndexWithUri.cend();
2077 it != end; ++it) {
2078 QMap<int, shared_ptr<ModuleIndex>> &myVersions =
2079 m_lastValidBase->m_moduleIndexWithUri[it.key()];
2080 for (auto it2 = it.value().cbegin(), end2 = it.value().cend(); it2 != end2; ++it2) {
2081 auto oldV = myVersions.value(it2.key());
2082 DomItem it2Obj = self.copy(it2.value());
2083 auto newV = it2.value()->makeCopy(it2Obj);
2084 newV->mergeWith(oldV);
2085 myVersions.insert(it2.key(), newV);
2086 }
2087 }
2088 }
2089
2090 auto newBaseForPopulation =
2091 m_lastValidBase ? m_lastValidBase->weak_from_this() : m_base->weak_from_this();
2092 // adapt the factory to the use the base or valid environment for unpopulated files, instead of
2093 // the current environment which will very probably be destroyed anytime soon
2094 for (const auto &qmlFile : my_qmlFileWithPath) {
2095 if (!qmlFile || !qmlFile->current)
2096 continue;
2097 QQmlJSScope::ConstPtr handle = qmlFile->current->handleForPopulation();
2098 if (!handle)
2099 continue;
2100 auto oldFactory = handle.factory();
2101 if (!oldFactory)
2102 continue;
2103
2104 resetFactory(handle, oldFactory->importer(),
2105 TypeReader{ newBaseForPopulation, m_loadPaths });
2106 }
2107 return true;
2108}
2109
2110void DomEnvironment::loadPendingDependencies()
2111{
2112 DomItem self(shared_from_this());
2113 while (true) {
2114 Path elToDo;
2115 std::shared_ptr<LoadInfo> loadInfo;
2116 {
2117 QMutexLocker l(mutex());
2118 if (m_loadsWithWork.isEmpty())
2119 break;
2120 elToDo = m_loadsWithWork.dequeue();
2121 m_inProgress.append(elToDo);
2122 loadInfo = m_loadInfos.value(elToDo);
2123 }
2124 if (loadInfo) {
2125 auto cleanup = qScopeGuard([this, &elToDo, &self] {
2126 QList<Callback> endCallbacks;
2127 {
2128 QMutexLocker l(mutex());
2129 m_inProgress.removeOne(elToDo);
2130 if (m_inProgress.isEmpty() && m_loadsWithWork.isEmpty()) {
2131 endCallbacks = m_allLoadedCallback;
2132 m_allLoadedCallback.clear();
2133 }
2134 }
2135 for (const Callback &cb : std::as_const(endCallbacks))
2136 cb(self.canonicalPath(), self, self);
2137 });
2138 DomItem loadInfoObj = self.copy(loadInfo);
2139 loadInfo->advanceLoad(loadInfoObj);
2140 } else {
2141 self.addError(myErrors().error(u"DomEnvironment::loadPendingDependencies could not "
2142 u"find loadInfo listed in m_loadsWithWork"_sv));
2143 {
2144 QMutexLocker l(mutex());
2145 m_inProgress.removeOne(elToDo);
2146 }
2147 Q_ASSERT(false
2148 && "DomEnvironment::loadPendingDependencies could not find loadInfo listed in "
2149 "m_loadsWithWork");
2150 }
2151 }
2152}
2153
2154bool DomEnvironment::finishLoadingDependencies(int waitMSec)
2155{
2156 bool hasPendingLoads = true;
2157 QDateTime endTime = QDateTime::currentDateTimeUtc().addMSecs(waitMSec);
2158 for (int i = 0; i < waitMSec / 10 + 2; ++i) {
2160 auto lInfos = loadInfos();
2161 auto it = lInfos.cbegin();
2162 auto end = lInfos.cend();
2163 hasPendingLoads = false;
2164 while (it != end) {
2165 if (*it && (*it)->status() != LoadInfo::Status::Done)
2166 hasPendingLoads = true;
2167 }
2168 if (!hasPendingLoads)
2169 break;
2170 auto missing = QDateTime::currentDateTimeUtc().msecsTo(endTime);
2171 if (missing < 0)
2172 break;
2173 if (missing > 100)
2174 missing = 100;
2175#if QT_FEATURE_thread
2176 QThread::msleep(missing);
2177#endif
2178 }
2179 return !hasPendingLoads;
2180}
2181
2182void DomEnvironment::addWorkForLoadInfo(const Path &elementCanonicalPath)
2183{
2184 QMutexLocker l(mutex());
2185 m_loadsWithWork.enqueue(elementCanonicalPath);
2186}
2187
2188DomEnvironment::Options DomEnvironment::options() const
2189{
2190 return m_options;
2191}
2192
2193std::shared_ptr<DomEnvironment> DomEnvironment::base() const
2194{
2195 return m_base;
2196}
2197
2198void DomEnvironment::setLoadPaths(const QStringList &v)
2199{
2200 QMutexLocker l(mutex());
2201 m_loadPaths = v;
2202
2203 if (m_semanticAnalysis)
2204 m_semanticAnalysis->updateLoadPaths(v);
2205}
2206
2207void DomEnvironment::setResourceFiles(const QStringList &v)
2208{
2209 QMutexLocker l(mutex());
2210
2211 if (m_semanticAnalysis)
2212 m_semanticAnalysis->setResourceFiles(v);
2213}
2214
2215
2216QStringList DomEnvironment::loadPaths() const
2217{
2218 QMutexLocker l(mutex());
2219 return m_loadPaths;
2220}
2221
2222QStringList DomEnvironment::qmldirFiles() const
2223{
2224 QMutexLocker l(mutex());
2225 return m_qmldirFileWithPath.keys();
2226}
2227
2228QString DomEnvironment::globalScopeName() const
2229{
2230 return m_globalScopeName;
2231}
2232
2233QList<Import> DomEnvironment::defaultImplicitImports()
2234{
2235 return QList<Import>({ Import::fromUriString(u"QML"_s, Version(1, 0)),
2236 Import(QmlUri::fromUriString(u"QtQml"_s), Version(6, 0)) });
2237}
2238
2239QList<Import> DomEnvironment::implicitImports() const
2240{
2241 return m_implicitImports;
2242}
2243
2244void DomEnvironment::addAllLoadedCallback(const DomItem &self, DomTop::Callback c)
2245{
2246 if (c) {
2247 bool immediate = false;
2248 {
2249 QMutexLocker l(mutex());
2250 if (m_loadsWithWork.isEmpty() && m_inProgress.isEmpty())
2251 immediate = true;
2252 else
2253 m_allLoadedCallback.append(c);
2254 }
2255 if (immediate)
2256 c(Path(), self, self);
2257 }
2258}
2259
2260void DomEnvironment::clearReferenceCache()
2261{
2262 m_referenceCache.clear();
2263}
2264
2265void DomEnvironment::populateFromQmlFile(MutableDomItem &&qmlFile)
2266{
2267 if (std::shared_ptr<QmlFile> qmlFilePtr = qmlFile.ownerAs<QmlFile>()) {
2268 auto logger = std::make_shared<QQmlJSLogger>();
2269 logger->setFilePath(qmlFile.canonicalFilePath());
2270 logger->setCode(qmlFilePtr->code());
2271 logger->setSilent(true);
2272
2273 auto setupFile = [&qmlFilePtr, &qmlFile, this](auto &&visitor) {
2274 Q_UNUSED(this); // note: integrity requires "this" to be in the capture list, while
2275 // other compilers complain about "this" being unused in the lambda
2276 AST::Node::accept(qmlFilePtr->ast(), visitor);
2277
2278 if (m_domCreationOption == DomCreationOption::Minimal)
2279 return;
2280
2281 CommentCollector collector(qmlFile);
2282 collector.collectComments();
2283 };
2284
2285 if (m_domCreationOption == DomCreationOption::Extended) {
2286 SemanticAnalysis analysis = semanticAnalysis();
2287 auto v = std::make_unique<QQmlDomAstCreatorWithQQmlJSScope>(qmlFile, logger.get(),
2288 analysis.m_importer.get());
2289 v->enableLoadFileLazily(true);
2290 v->enableScriptExpressions(true);
2291
2292 setupFile(v.get());
2293
2294 auto typeResolver =
2295 std::make_shared<QQmlJSTypeResolver>(analysis.m_importer.get());
2296 typeResolver->init(&v->scopeCreator(), nullptr);
2297 qmlFilePtr->setTypeResolverWithDependencies(
2298 typeResolver, { analysis.m_importer, analysis.m_mapper, std::move(logger) });
2299 } else {
2300 auto v = std::make_unique<QQmlDomAstCreator>(qmlFile);
2301 // we need file locations of script expressions in qmlformat for the "// qmlformat off"
2302 // functionality
2303 v->enableScriptExpressions(m_domCreationOption == DomCreationOption::Default);
2304 setupFile(v.get());
2305 }
2306 } else {
2307 qCWarning(domLog) << "populateQmlFile called on non qmlFile";
2308 return;
2309 }
2310}
2311
2313{
2314 shared_ptr<ExternalOwningItem> current = currentItem();
2315 DomItem currentObj = currentItem(self);
2316 return current->canonicalFilePath(currentObj);
2317}
2318
2319bool ExternalItemInfoBase::iterateDirectSubpaths(const DomItem &self, DirectVisitor visitor) const
2320{
2321 if (!self.invokeVisitorOnLazyField(visitor, Fields::currentRevision,
2322 [this, &self]() { return currentRevision(self); }))
2323 return false;
2324 if (!self.invokeVisitorOnLazyField(visitor, Fields::lastRevision,
2325 [this, &self]() { return lastRevision(self); }))
2326 return false;
2327 if (!self.invokeVisitorOnLazyField(visitor, Fields::lastValidRevision,
2328 [this, &self]() { return lastValidRevision(self); }))
2329 return false;
2330 if (!visitor(PathEls::Field(Fields::currentItem),
2331 [&self, this]() { return currentItem(self); }))
2332 return false;
2333 if (!self.invokeVisitorOnLazyField(visitor, Fields::currentExposedAt,
2334 [this]() { return currentExposedAt(); }))
2335 return false;
2336 return true;
2337}
2338
2340{
2341 return currentItem()->revision();
2342}
2343
2345{
2346 Path p = currentItem()->canonicalPath();
2347 DomItem lastValue = self.universe()[p.mid(1, p.length() - 1)].field(u"revision");
2348 return static_cast<int>(lastValue.value().toInteger(0));
2349}
2350
2352{
2353 Path p = currentItem()->canonicalPath();
2354 DomItem lastValidValue = self.universe()[p.mid(1, p.length() - 2)].field(u"validItem").field(u"revision");
2355 return static_cast<int>(lastValidValue.value().toInteger(0));
2356}
2357
2359{
2360 shared_ptr<ExternalOwningItem> current = currentItem();
2361 return current->canonicalFilePath();
2362}
2363
2365{
2366 shared_ptr<ExternalOwningItem> current = currentItem();
2367 return current->canonicalPath().dropTail();
2368}
2369
2370bool ExternalItemPairBase::iterateDirectSubpaths(const DomItem &self, DirectVisitor visitor) const
2371{
2372 if (!self.invokeVisitorOnLazyField(visitor, Fields::currentIsValid,
2373 [this]() { return currentIsValid(); }))
2374 return false;
2375 if (!visitor(PathEls::Field(Fields::validItem), [this, &self]() { return validItem(self); }))
2376 return false;
2377 if (!visitor(PathEls::Field(Fields::currentItem),
2378 [this, &self]() { return currentItem(self); }))
2379 return false;
2380 if (!self.invokeVisitorOnValue(visitor, PathEls::Field(Fields::validExposedAt), validExposedAt))
2381 return false;
2382 if (!self.invokeVisitorOnValue(visitor, PathEls::Field(Fields::currentExposedAt),
2383 currentExposedAt)) {
2384 return false;
2385 }
2386 return true;
2387}
2388
2390{
2391 return currentItem() == validItem();
2392}
2393
2394RefCacheEntry RefCacheEntry::forPath(const DomItem &el, const Path &canonicalPath)
2395{
2396 DomItem env = el.environment();
2397 std::shared_ptr<DomEnvironment> envPtr = env.ownerAs<DomEnvironment>();
2398 RefCacheEntry cached;
2399 if (envPtr) {
2400 QMutexLocker l(envPtr->mutex());
2401 cached = envPtr->m_referenceCache.value(canonicalPath, {});
2402 } else {
2403 qCWarning(domLog) << "No Env for reference" << canonicalPath << "from"
2404 << el.internalKindStr() << el.canonicalPath();
2405 Q_ASSERT(false);
2406 }
2407 return cached;
2408}
2409
2410bool RefCacheEntry::addForPath(const DomItem &el, const Path &canonicalPath, const RefCacheEntry &entry,
2411 AddOption addOption)
2412{
2413 DomItem env = el.environment();
2414 std::shared_ptr<DomEnvironment> envPtr = env.ownerAs<DomEnvironment>();
2415 bool didSet = false;
2416 if (envPtr) {
2417 QMutexLocker l(envPtr->mutex());
2418 RefCacheEntry &cached = envPtr->m_referenceCache[canonicalPath];
2419 switch (cached.cached) {
2420 case RefCacheEntry::Cached::None:
2421 cached = entry;
2422 didSet = true;
2423 break;
2424 case RefCacheEntry::Cached::First:
2425 if (addOption == AddOption::Overwrite || entry.cached == RefCacheEntry::Cached::All) {
2426 cached = entry;
2427 didSet = true;
2428 }
2429 break;
2430 case RefCacheEntry::Cached::All:
2431 if (addOption == AddOption::Overwrite || entry.cached == RefCacheEntry::Cached::All) {
2432 cached = entry;
2433 didSet = true;
2434 }
2435 }
2436 if (cached.cached == RefCacheEntry::Cached::First && cached.canonicalPaths.isEmpty())
2437 cached.cached = RefCacheEntry::Cached::All;
2438 } else {
2439 Q_ASSERT(false);
2440 }
2441 return didSet;
2442}
2443
2444} // end namespace Dom
2445} // end namespace QQmlJS
2446
2447QT_END_NAMESPACE
2448
2449#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)