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