154 cont = cont && DomTop::iterateDirectSubpaths(self, visitor);
155 cont = cont && self.invokeVisitorOnValue(visitor,
PathEls::Field(Fields::name), name());
156 cont = cont && visitor(
PathEls::Field(Fields::qmlFileWithPath), [
this, &self]() {
157 return self.subMapItem(Map(
158 Path::fromField(Fields::qmlFileWithPath),
159 [
this](
const DomItem &map,
const QString &key) {
160 return map.copy(qmlFileWithPath(key));
162 [
this](
const DomItem &) {
return qmlFilePaths(); },
163 QLatin1String(
"QmlFile")));
165 cont = cont && visitor(
PathEls::Field(Fields::jsFileWithPath), [
this, &self]() {
166 return self.subMapItem(Map(
167 Path::fromField(Fields::jsFileWithPath),
168 [
this](
const DomItem &map,
const QString &key) {
169 return map.copy(jsFileWithPath(key));
171 [
this](
const DomItem &) {
return jsFilePaths(); }, QLatin1String(
"JsFile")));
178 QRegularExpression r(QRegularExpression::anchoredPattern(QLatin1String(R"(.*Copy([0-9]*)$)")));
179 auto m = r.match(m_name);
182 newName = QStringLiteral(u"%1Copy%2").arg(m_name).arg(m.captured(1).toInt() + 1);
184 newName = m_name + QLatin1String(
"Copy");
185 auto res =
std::make_shared<DomUniverse>(newName);
191 if (canonicalFilePath.endsWith(u".qml", Qt::CaseInsensitive)
192 || canonicalFilePath.endsWith(u".qmlannotation", Qt::CaseInsensitive)) {
194 }
else if (canonicalFilePath.endsWith(u".js", Qt::CaseInsensitive)
195 || canonicalFilePath.endsWith(u".mjs", Qt::CaseInsensitive)) {
200 .error(QCoreApplication::translate(
"Dom::fileTypeForPath",
201 "Could not detect type of file %1")
202 .arg(canonicalFilePath))
211 DomItem univ(shared_from_this());
215 const auto &preLoadResult = preload(univ, file, fileType);
221 return load(
std::get<ContentWithDate>(preLoadResult), file, fileType, creationOption);
226 .error(tr(
"Ignoring request to load file %1 of unexpected type %2, "
227 "calling callback immediately")
228 .arg(file.canonicalPath(), domTypeToString(fileType))
)
230 Q_ASSERT(
false &&
"loading non supported file type");
235DomUniverse::
LoadResult DomUniverse::load(
const ContentWithDate &codeWithDate,
239 QString canonicalPath = file.canonicalPath();
246 auto qmlFile = parseQmlFile(codeWithDate.content, file, codeWithDate.date, creationOption);
247 return insertOrUpdateExternalItem(
std::move(qmlFile));
249 auto jsFile = parseJsFile(codeWithDate.content, file, codeWithDate.date);
250 return insertOrUpdateExternalItem(
std::move(jsFile));
254 return {
std::move(oldValue),
std::move(newValue) };
258
259
260
261
262
263
264
265DomUniverse::PreloadResult DomUniverse::preload(
const DomItem &univ,
const FileToLoad &file,
268 QString canonicalPath = file.canonicalPath();
269 ContentWithDate codeWithDate;
271 if (file.content().has_value()) {
272 codeWithDate = { file.content()->data, file.content()->date };
276 const auto &curValueItem = getItemIfMostRecent(univ, fType, canonicalPath);
277 if (curValueItem.has_value()) {
278 return LoadResult{ curValueItem.value(), curValueItem.value() };
281 auto readResult = readFileContent(canonicalPath);
287 codeWithDate =
std::get<ContentWithDate>(readResult);
292 const auto &curValueItem = getItemIfHasSameCode(univ, fType, canonicalPath, codeWithDate);
293 if (curValueItem.has_value()) {
294 return LoadResult{ curValueItem.value(), curValueItem.value() };
302 QMutexLocker l(mutex());
303 const auto toDelete = [path](
const auto &it) {
304 QString p = it.key();
305 return p.startsWith(path) && (p.size() == path.size() || p.at(path.size()) == u'/');
307 m_qmlFileWithPath.removeIf(toDelete);
308 m_jsFileWithPath.removeIf(toDelete);
311DomUniverse::ReadResult DomUniverse::readFileContent(
const QString &canonicalPath)
const
313 if (canonicalPath.isEmpty()) {
316 QFile file(canonicalPath);
317 QFileInfo fileInfo(canonicalPath);
318 if (fileInfo.isDir()) {
319 return ContentWithDate{ QDir(canonicalPath)
320 .entryList(QDir::NoDotAndDotDot | QDir::Files, QDir::Name)
321 .join(QLatin1Char(
'\n')),
322 QDateTime::currentDateTimeUtc() };
324 if (!file.open(QIODevice::ReadOnly)) {
326 tr(
"Error opening path %1: %2 %3")
327 .arg(canonicalPath, QString::number(file.error()), file.errorString())
);
329 auto content = QString::fromUtf8(file.readAll());
331 return ContentWithDate{ std::move(content), QDateTime::currentDateTimeUtc() };
334std::shared_ptr<QmlFile> DomUniverse::parseQmlFile(
const QString &code,
const FileToLoad &file,
335 const QDateTime &contentDate,
339 std::make_shared<QmlFile>(file.canonicalPath(), code, contentDate, 0,
340 creationOption == Extended ? QmlFile::EnableParserRecovery
341 : QmlFile::DisableParserRecovery);
342 std::shared_ptr<DomEnvironment> envPtr;
343 if (
auto ptr = file.environment())
344 envPtr =
std::move(ptr);
346 envPtr =
std::make_shared<DomEnvironment>(QStringList(),
348 creationOption, shared_from_this());
349 envPtr->addQmlFile(qmlFile);
351 if (qmlFile->isValid()) {
357 DomItem qmlFileObj = env.copy(qmlFile);
359 errs += m.toString();
363 qCWarning(domLog).noquote().nospace()
364 <<
"Parsed invalid file " << file.canonicalPath() << errs;
369std::shared_ptr<JsFile> DomUniverse::parseJsFile(
const QString &code,
const FileToLoad &file,
370 const QDateTime &contentDate)
376 auto jsFile =
std::make_shared<JsFile>(file.canonicalPath(), code, contentDate);
377 std::shared_ptr<DomEnvironment> envPtr;
378 if (
auto ptr = file.environment())
379 envPtr =
std::move(ptr);
381 envPtr =
std::make_shared<DomEnvironment>(QStringList(),
384 envPtr->addJsFile(jsFile);
386 if (!jsFile->isValid()) {
388 DomItem qmlFileObj = env.copy(jsFile);
390 errs += m.toString();
394 qCWarning(domLog).noquote().nospace()
395 <<
"Parsed invalid file " << file.canonicalPath() << errs;
401
402
403
404
406 const QString &path)
const
409 case DomType::QmlFile:
410 return m_qmlFileWithPath.value(path);
411 case DomType::JsFile:
412 return m_jsFileWithPath.value(path);
420 const QString &canonicalPath)
const
422 QFileInfo fInfo(canonicalPath);
423 bool valueItemIsMostRecent =
false;
428 QMutexLocker l(mutex());
429 value = getPathValueOrNull(fType, canonicalPath);
430 valueItemIsMostRecent = valueHasMostRecentItem(value.get(), fInfo.lastModified());
432 if (valueItemIsMostRecent) {
433 return univ.copy(value);
439 const QString &canonicalPath,
440 const ContentWithDate &codeWithDate)
const
443 bool valueItemHasSameCode =
false;
447 QMutexLocker l(mutex());
448 value = getPathValueOrNull(fType, canonicalPath);
449 if (valueHasSameContent(value.get(), codeWithDate.content)) {
450 valueItemHasSameCode =
true;
451 if (value->currentItem()->lastDataUpdateAt() < codeWithDate.date)
452 value->currentItem()->refreshedDataAt(codeWithDate.date);
455 if (valueItemHasSameCode) {
456 return univ.copy(value);
462
463
464
465
467 const QDateTime &lastModified)
472 return lastModified < value->currentItem()->lastDataUpdateAt();
476
477
478
479
485 QString curContent = value->currentItem()->code();
486 return !curContent.isNull() && curContent == content;
491 auto res =
std::make_shared<LoadInfo>(*
this);
494 u"This is a copy of a LoadInfo still in progress, artificially ending it, if you "
495 u"use this you will *not* resume loading"_sv));
497 .warning([&self](
const Sink &sink) {
498 sink(u"Copying an in progress LoadInfo, which is most likely an error (");
503 QMutexLocker l(res->mutex());
506 res->m_inProgress.clear();
507 res->m_endCallbacks.clear();
519 bool cont = OwningItem::iterateDirectSubpaths(self, visitor);
520 cont = cont && self.invokeVisitorOnValue(visitor,
PathEls::Field(Fields::status),
int(
status()));
521 cont = cont && self.invokeVisitorOnValue(visitor,
PathEls::Field(Fields::nLoaded),
nLoaded());
522 cont = cont && self.invokeVisitorOnValue(visitor,
PathEls::Field(Fields::elementCanonicalPath),
524 cont = cont && self.invokeVisitorOnValue(visitor,
PathEls::Field(Fields::nNotdone),
nNotDone());
525 cont = cont && self.invokeVisitorOnValue(visitor,
PathEls::Field(Fields::nCallbacks),
nCallbacks());
535 QMutexLocker l(mutex());
537 case Status::NotStarted:
538 case Status::Starting:
539 case Status::InProgress:
540 case Status::CallingCallbacks:
541 m_endCallbacks.append(callback);
556 bool depValid =
false;
558 QMutexLocker l(mutex());
566 if (!m_toDo.isEmpty()) {
567 dep = m_toDo.dequeue();
568 m_inProgress.append(dep);
578 case Status::NotStarted:
579 refreshedDataAt(QDateTime::currentDateTimeUtc());
580 doAddDependencies(self);
581 refreshedDataAt(QDateTime::currentDateTimeUtc());
583 QMutexLocker l(mutex());
585 if (m_toDo.isEmpty() && m_inProgress.isEmpty())
596 refreshedDataAt(QDateTime::currentDateTimeUtc());
598 Q_ASSERT(envPtr &&
"missing environment");
599 if (!dep.uri.isEmpty()) {
600 Q_ASSERT(dep.filePath.isEmpty() &&
"dependency with both uri and file");
601 }
else if (!dep.filePath.isEmpty()) {
603 FileToLoad::fromFileSystem(envPtr, dep.filePath),
611 Q_ASSERT(
false &&
"dependency without uri and filePath");
615 tr(
"advanceLoad called but found no work, which should never happen")
));
621 "advanceLoad called after work should have been done, which should never happen")
));
628 bool didRemove =
false;
629 bool unexpectedState =
false;
632 QMutexLocker l(mutex());
633 didRemove = m_inProgress.removeOne(d);
638 unexpectedState =
true;
643 if (m_toDo.isEmpty() && m_inProgress.isEmpty()) {
652 sink(u"LoadInfo::finishedLoadingDep did not find its dependency in those inProgress "
658 &&
"LoadInfo::finishedLoadingDep did not find its dependency in those inProgress");
660 if (unexpectedState) {
662 sink(u"LoadInfo::finishedLoadingDep found an unexpected state (");
666 Q_ASSERT(
false &&
"LoadInfo::finishedLoadingDep did find an unexpected state");
674 QList<std::function<
void(Path,
const DomItem &,
const DomItem &)>> endCallbacks;
675 bool unexpectedState =
false;
677 QMutexLocker l(mutex());
679 endCallbacks = m_endCallbacks;
680 m_endCallbacks.clear();
682 Q_ASSERT(!unexpectedState &&
"LoadInfo::execEnd found an unexpected state");
686 auto cleanup = qScopeGuard([
this, p, &el] {
687 QList<std::function<
void(Path,
const DomItem &,
const DomItem &)>> otherCallbacks;
688 bool unexpectedState2 =
false;
690 QMutexLocker l(mutex());
693 otherCallbacks = m_endCallbacks;
694 m_endCallbacks.clear();
696 Q_ASSERT(!unexpectedState2 &&
"LoadInfo::execEnd found an unexpected state");
697 for (
auto const &cb : otherCallbacks) {
702 for (
auto const &cb : endCallbacks) {
709void LoadInfo::doAddDependencies(
const DomItem &self)
721 DomItem currentFile = el.field(Fields::currentItem);
722 QString currentFilePath = currentFile.canonicalFilePath();
725 DomItem currentQmlFiles = currentFile.field(Fields::qmlFiles);
726 currentQmlFiles.visitKeys([
this, &self](
const QString &,
const DomItem &els) {
727 return els.visitIndexes([
this, &self](
const DomItem &el) {
728 if (
const Reference *ref = el.as<Reference>()) {
729 Path canonicalPath = ref->referredObjectPath[2];
730 if (canonicalPath && !canonicalPath.headName().isEmpty())
732 Dependency{ QString(), Version(),
733 canonicalPath.headName(), DomType::QmlFile });
741 tr(
"Ignoring dependencies for empty (invalid) type %1")
742 .arg(domTypeToString(el.internalKind()))
));
746 .arg(domTypeToString(el.internalKind()),
753 bool unexpectedState =
false;
755 QMutexLocker l(mutex());
759 Q_ASSERT(!unexpectedState &&
"LoadInfo::addDependency found an unexpected state");
765
766
767
768
769
770
771
772
793 cont = cont && DomTop::iterateDirectSubpaths(self, visitor);
795 cont = cont && visitor(
PathEls::Field(Fields::universe), [
this]() {
return universe(); });
796 cont = cont && self.invokeVisitorOnValue(visitor,
PathEls::Field(Fields::options),
int(options()));
797 cont = cont && visitor(
PathEls::Field(Fields::base), [
this]() {
return base(); });
798 cont = cont && self.invokeVisitorOnLazyField(visitor, Fields::loadPaths, [
this]() {
801 cont = cont && visitor(
PathEls::Field(Fields::qmlFileWithPath), [
this, &self]() {
802 return self.subMapItem(Map(
803 Path::fromField(Fields::qmlFileWithPath),
804 [&self,
this](
const DomItem &map,
const QString &key) {
805 return map.copy(qmlFileWithPath(self, key));
807 [&self,
this](
const DomItem &) {
return qmlFilePaths(self); },
808 QLatin1String(
"QmlFile")));
810 cont = cont && visitor(
PathEls::Field(Fields::jsFileWithPath), [
this, &self]() {
811 return self.subMapItem(Map(
812 Path::fromField(Fields::jsFileWithPath),
813 [
this](
const DomItem &map,
const QString &key) {
814 DomItem mapOw(map.owner());
815 return map.copy(jsFileWithPath(mapOw, key));
817 [
this](
const DomItem &map) {
818 DomItem mapOw = map.owner();
819 return jsFilePaths(mapOw);
821 QLatin1String(
"JsFile")));
823 bool loadedLoadInfo =
false;
824 QQueue<Path> loadsWithWork;
825 QQueue<Path> inProgress;
826 int nAllLoadedCallbacks;
827 auto ensureInfo = [&]() {
828 if (!loadedLoadInfo) {
829 QMutexLocker l(mutex());
830 loadedLoadInfo =
true;
831 loadsWithWork = m_loadsWithWork;
832 inProgress = m_inProgress;
833 nAllLoadedCallbacks = m_allLoadedCallback.size();
836 cont = cont && visitor(
837 PathEls::Field(Fields::loadsWithWork), [&ensureInfo, &self, &loadsWithWork]() {
839 return self.subListItem(List(
840 Path::fromField(Fields::loadsWithWork),
841 [loadsWithWork](
const DomItem &list, index_type i) {
842 if (i >= 0 && i < loadsWithWork.size())
843 return list.subDataItem(PathEls::Index(i),
844 loadsWithWork.at(i).toString());
848 [loadsWithWork](
const DomItem &) {
849 return index_type(loadsWithWork.size());
851 nullptr, QLatin1String(
"Path")));
853 cont = cont && visitor(
PathEls::Field(Fields::inProgress), [&self, &ensureInfo, &inProgress]() {
855 return self.subListItem(List(
856 Path::fromField(Fields::inProgress),
857 [inProgress](
const DomItem &list, index_type i) {
858 if (i >= 0 && i < inProgress.size())
859 return list.subDataItem(PathEls::Index(i),
860 inProgress.at(i).toString());
864 [inProgress](
const DomItem &) {
return index_type(inProgress.size()); },
865 nullptr, QLatin1String(
"Path")));
867 cont = cont && visitor(
PathEls::Field(Fields::loadInfo), [&self,
this]() {
868 return self.subMapItem(Map(
869 Path::fromField(Fields::loadInfo),
870 [
this](
const DomItem &map,
const QString &pStr) {
871 bool hasErrors =
false;
872 Path p = Path::fromString(pStr, [&hasErrors](
const ErrorMessage &m) {
874 case ErrorLevel::Debug:
875 case ErrorLevel::Info:
877 case ErrorLevel::Warning:
878 case ErrorLevel::Error:
879 case ErrorLevel::Fatal:
885 return map.copy(loadInfo(p));
888 [
this](
const DomItem &) {
890 const auto infoPaths = loadInfoPaths();
891 for (
const Path &p : infoPaths)
892 res.insert(p.toString());
895 QLatin1String(
"LoadInfo")));
897 cont = cont && self.invokeVisitorOnField(visitor, Fields::imports, m_implicitImports);
898 cont = cont && self.invokeVisitorOnLazyField(visitor, Fields::nAllLoadedCallbacks,
899 [&nAllLoadedCallbacks, &ensureInfo]() {
901 return nAllLoadedCallbacks;
908 return DomTop::field(self, name);
913 return std::static_pointer_cast<DomEnvironment>(
doCopy(self));
918 shared_ptr<DomEnvironment> res;
920 res =
std::make_shared<DomEnvironment>(m_base, m_loadPaths, m_options, m_domCreationOption);
922 res =
std::make_shared<DomEnvironment>(m_loadPaths, m_options, m_domCreationOption,
927bool DomEnvironment::
hasFile(
const QString &filePath)
const
929 return m_qmlFileWithPath.contains(filePath);
934 if (options() & DomEnvironment::Option::NoDependencies)
935 loadFile(file, callback, DomTop::Callback(), fileType, h);
940 loadFile(file, DomTop::Callback(), callback, fileType, h);
945
946
947
948
949
950
951
952
953
954
955
957void DomEnvironment::loadFile(
const FileToLoad &_file,
const Callback &loadCallback,
958 const Callback &endCallback,
std::optional<
DomType> fileType,
961 DomItem self(shared_from_this());
963 (
bool(fileType) ? (*fileType) : fileTypeForPath(self, _file.logicalPath()));
970 file.setCanonicalPath(QQmlJSUtils::qmlSourcePathFromBuildPath(
971 semanticAnalysis().m_mapper.get(), file.canonicalPath()));
972 file.setLogicalPath(file.logicalPath());
976 if (file.canonicalPath().isEmpty()) {
977 if (!file.content() || file.content()->data.isNull()) {
980 .error(tr(
"Non existing path to load: '%1'").arg(file.logicalPath())
)
985 addAllLoadedCallback(self, [endCallback](Path,
const DomItem &,
const DomItem &) {
986 endCallback(Path(), DomItem::empty, DomItem::empty);
991 file.canonicalPath() = file.logicalPath();
998 const auto &fetchResult = fetchFileFromEnvs<QmlFile>(file);
999 oldValue = fetchResult.first;
1000 newValue = fetchResult.second;
1002 const auto &loadRes = universe()->loadFile(file, fType, m_domCreationOption);
1003 addExternalItemInfo<QmlFile>(loadRes.currentItem, loadCallback, endCallback);
1008 const auto &loadRes = universe()->loadFile(file, fType, m_domCreationOption);
1009 addExternalItemInfo<JsFile>(loadRes.currentItem, loadCallback, endCallback);
1013 myErrors().error(tr(
"Unexpected file to load: '%1'").arg(file.canonicalPath())
).handle(h);
1021 Path p = self.copy(newValue).canonicalPath();
1022 std::shared_ptr<LoadInfo> lInfo = loadInfo(p);
1025 DomItem oldValueObj = self.copy(oldValue);
1026 DomItem newValueObj = self.copy(newValue);
1027 loadCallback(p, oldValueObj, newValueObj);
1035 addAllLoadedCallback(self, [p = std::move(p), endCallback](
1036 const Path &,
const DomItem &,
const DomItem &env) {
1037 DomItem el = env.path(p);
1038 endCallback(p, el, el);
1044 QMutexLocker l(mutex());
1045 auto toDelete = [path](
auto it) {
1046 QString p = it.key();
1047 return p.startsWith(path) && (p.size() == path.size() || p.at(path.size()) == u'/');
1049 m_qmlFileWithPath.removeIf(toDelete);
1050 m_jsFileWithPath.removeIf(toDelete);
1053shared_ptr<DomUniverse> DomEnvironment::
universe()
const {
1057 return m_base->universe();
1087std::shared_ptr<ExternalItemInfo<QmlFile>>
1090 return lookup<QmlFile>(path, options);
1095 return getStrings<std::shared_ptr<ExternalItemInfo<QmlFile>>>(
1097 DomItem baseObj(m_base);
1098 return m_base->qmlFilePaths(baseObj, EnvLookup::Normal);
1100 m_qmlFileWithPath, lookup);
1103std::shared_ptr<ExternalItemInfo<JsFile>>
1106 return lookup<JsFile>(path, options);
1111 return getStrings<std::shared_ptr<ExternalItemInfo<JsFile>>>(
1113 DomItem baseObj(m_base);
1114 return m_base->jsFilePaths(baseObj, EnvLookup::Normal);
1116 m_jsFileWithPath, lookup);
1120
1121
1122
1125 if (options() & Option::NoDependencies) {
1129 const auto loadInfo =
std::make_shared<LoadInfo>(path);
1134
1135
1136
1137
1142 Path p = loadInfo->elementCanonicalPath();
1143 bool addWork = loadInfo->status() != LoadInfo::Status::Done;
1144 std::shared_ptr<LoadInfo> oldVal;
1146 QMutexLocker l(mutex());
1147 oldVal = m_loadInfos.value(p);
1148 m_loadInfos.insert(p, loadInfo);
1150 m_loadsWithWork.enqueue(p);
1154 .error(tr(
"addLoadinfo replaces unfinished load info for %1")
1162 QMutexLocker l(mutex());
1163 return m_loadInfos.value(path);
1168 QMutexLocker l(mutex());
1174 auto lInfos = loadInfos();
1175 return lInfos.keys();
1178DomEnvironment::
DomEnvironment(
const QStringList &loadPaths, Options options,
1180 const shared_ptr<DomUniverse> &universe)
1185 m_domCreationOption(domCreationOption)
1191
1192
1193
1194
1197 QMutexLocker l(mutex());
1198 return semanticAnalysisUnlocked();
1201DomEnvironment::SemanticAnalysis DomEnvironment::prepareSemanticAnalysisForFileLoading()
1203 QMutexLocker guard(mutex());
1204 auto result = semanticAnalysisUnlocked();
1205 result.updateLoadPaths(m_loadPaths);
1206 result.setResourceFiles(m_resourcePaths);
1210DomEnvironment::SemanticAnalysis DomEnvironment::semanticAnalysisUnlocked()
1215 return m_base->semanticAnalysis();
1217 if (m_semanticAnalysis)
1218 return *m_semanticAnalysis;
1221 m_semanticAnalysis = SemanticAnalysis(m_loadPaths);
1222 return *m_semanticAnalysis;
1225DomEnvironment::SemanticAnalysis::SemanticAnalysis(
const QStringList &loadPaths)
1226 : m_mapper(std::make_shared<QQmlJSResourceFileMapper>(
1227 QQmlJSUtils::resourceFilesFromBuildFolders(loadPaths))),
1228 m_importer(std::make_shared<QQmlJSImporter>(loadPaths, m_mapper.get(),
1229 QQmlJSImporterFlags{} | UseOptionalImports
1230 | TolerateFileSelectors
1231 | PreferQmlFilesFromSourceFolder))
1236
1237
1238
1239
1240
1241
1242
1243void DomEnvironment::SemanticAnalysis::updateLoadPaths(
const QStringList &loadPaths)
1245 if (loadPaths == m_importer->importPaths())
1248 m_importer->setImportPaths(loadPaths);
1251void DomEnvironment::SemanticAnalysis::setResourceFiles(
const QStringList &qrcFiles)
1253 if (qrcFiles == m_resourceFiles)
1255 m_resourceFiles = qrcFiles;
1256 *m_mapper = QQmlJSResourceFileMapper(qrcFiles);
1259std::shared_ptr<DomEnvironment> DomEnvironment::create(
const QStringList &loadPaths,
1264 std::shared_ptr<DomUniverse> universePtr = universe.ownerAs<DomUniverse>();
1265 return std::make_shared<DomEnvironment>(loadPaths, options, domCreationOption, universePtr);
1268DomEnvironment::
DomEnvironment(
const shared_ptr<DomEnvironment> &parent,
1269 const QStringList &loadPaths, Options options,
1275 m_domCreationOption(domCreationOption)
1281 addExternalItem(file, file->canonicalFilePath(), options);
1283 QQmlJSScope::Ptr handle =
1287 file->setHandleForPopulation(handle);
1289 TypeReader{ weak_from_this(), m_loadPaths });
1296 addExternalItem(file, file->canonicalFilePath(), options);
1299void DomEnvironment::TypeReader::operator()(QQmlJSImporter *importer,
const QString &filePath,
1300 const QSharedPointer<QQmlJSScope> &scopeToPopulate)
1303 Q_UNUSED(scopeToPopulate);
1305 std::shared_ptr<DomEnvironment> envPtr = m_env.lock();
1308 auto it = envPtr->m_qmlFileWithPath.constFind(filePath);
1309 if (it == envPtr->m_qmlFileWithPath.constEnd()) {
1310 qCDebug(domLog) <<
"Import visitor tried to lazily load file \"" << filePath
1311 <<
"\", but that file was not found in the DomEnvironment. Was this "
1312 "file not discovered by the Dom's dependency loading mechanism?";
1315 const DomItem qmlFile = it.value()->currentItem(
DomItem(envPtr));
1319 const QStringList oldImportPaths = envPtr->loadPaths();
1320 envPtr->setLoadPaths(m_importPaths);
1322 envPtr->setLoadPaths(oldImportPaths);
1329 QMap<QString, std::shared_ptr<ExternalItemInfo<QmlFile>>> my_qmlFileWithPath;
1330 QMap<QString, std::shared_ptr<ExternalItemInfo<JsFile>>> my_jsFileWithPath;
1331 QHash<Path, std::shared_ptr<LoadInfo>> my_loadInfos;
1332 std::optional<SemanticAnalysis> my_semanticAnalysis;
1334 QMutexLocker l(mutex());
1335 my_qmlFileWithPath = m_qmlFileWithPath;
1336 my_jsFileWithPath = m_jsFileWithPath;
1337 my_loadInfos = m_loadInfos;
1338 my_semanticAnalysis = semanticAnalysisUnlocked();
1341 QMutexLocker lBase(base()->mutex());
1342 m_base->m_semanticAnalysis = my_semanticAnalysis;
1343 m_base->m_qmlFileWithPath.insert(my_qmlFileWithPath);
1344 m_base->m_jsFileWithPath.insert(my_jsFileWithPath);
1345 m_base->m_loadInfos.insert(my_loadInfos);
1348 m_lastValidBase = validEnvPtr;
1349 if (m_lastValidBase) {
1350 QMutexLocker lValid(
1351 m_lastValidBase->mutex());
1352 m_lastValidBase->m_semanticAnalysis = std::move(my_semanticAnalysis);
1353 for (
auto it = my_qmlFileWithPath.cbegin(), end = my_qmlFileWithPath.cend(); it != end;
1355 if (it.value() && it.value()->current && it.value()->current->isValid())
1356 m_lastValidBase->m_qmlFileWithPath.insert(it.key(), it.value());
1358 for (
auto it = my_jsFileWithPath.cbegin(), end = my_jsFileWithPath.cend(); it != end;
1360 if (it.value() && it.value()->current && it.value()->current->isValid())
1361 m_lastValidBase->m_jsFileWithPath.insert(it.key(), it.value());
1363 m_lastValidBase->m_loadInfos.insert(my_loadInfos);
1366 auto newBaseForPopulation =
1367 m_lastValidBase ? m_lastValidBase->weak_from_this() : m_base->weak_from_this();
1370 for (
const auto &qmlFile : my_qmlFileWithPath) {
1371 if (!qmlFile || !qmlFile->current)
1373 QQmlJSScope::ConstPtr handle = qmlFile->current->handleForPopulation();
1376 auto oldFactory = handle.factory();
1380 resetFactory(handle, oldFactory->importer(),
1381 TypeReader{ newBaseForPopulation, m_loadPaths });
1388 DomItem self(shared_from_this());
1391 std::shared_ptr<LoadInfo> loadInfo;
1393 QMutexLocker l(mutex());
1394 if (m_loadsWithWork.isEmpty())
1396 elToDo = m_loadsWithWork.dequeue();
1397 m_inProgress.append(elToDo);
1398 loadInfo = m_loadInfos.value(elToDo);
1401 auto cleanup = qScopeGuard([
this, &elToDo, &self] {
1402 QList<Callback> endCallbacks;
1404 QMutexLocker l(mutex());
1405 m_inProgress.removeOne(elToDo);
1406 if (m_inProgress.isEmpty() && m_loadsWithWork.isEmpty()) {
1407 endCallbacks = m_allLoadedCallback;
1408 m_allLoadedCallback.clear();
1411 for (
const Callback &cb : std::as_const(endCallbacks))
1412 cb(self.canonicalPath(), self, self);
1414 DomItem loadInfoObj = self.copy(loadInfo);
1415 loadInfo->advanceLoad(loadInfoObj);
1418 u"find loadInfo listed in m_loadsWithWork"_sv)
);
1420 QMutexLocker l(mutex());
1421 m_inProgress.removeOne(elToDo);
1424 &&
"DomEnvironment::loadPendingDependencies could not find loadInfo listed in "
1432 bool hasPendingLoads =
true;
1433 QDateTime endTime = QDateTime::currentDateTimeUtc().addMSecs(waitMSec);
1434 for (
int i = 0; i < waitMSec / 10 + 2; ++i) {
1436 auto lInfos = loadInfos();
1437 auto it = lInfos.cbegin();
1438 auto end = lInfos.cend();
1439 hasPendingLoads =
false;
1442 hasPendingLoads =
true;
1444 if (!hasPendingLoads)
1446 auto missing = QDateTime::currentDateTimeUtc().msecsTo(endTime);
1451#if QT_FEATURE_thread
1452 QThread::msleep(missing);
1455 return !hasPendingLoads;
1460 QMutexLocker l(mutex());
1461 m_loadsWithWork.enqueue(elementCanonicalPath);
1469std::shared_ptr<DomEnvironment> DomEnvironment::
base()
const
1476 QMutexLocker l(mutex());
1482 QMutexLocker l(mutex());
1483 m_resourcePaths = v;
1488 QMutexLocker l(mutex());
1492QList<Import> DomEnvironment::defaultImplicitImports()
1494 return QList<
Import>({ Import::fromUriString(u"QML"_s, Version(1, 0)),
1495 Import(QmlUri::fromUriString(u"QtQml"_s), Version(6, 0)) });
1500 return m_implicitImports;
1506 bool immediate =
false;
1508 QMutexLocker l(mutex());
1509 if (m_loadsWithWork.isEmpty() && m_inProgress.isEmpty())
1512 m_allLoadedCallback.append(c);
1521 m_referenceCache.clear();
1526 if (std::shared_ptr<QmlFile> qmlFilePtr = qmlFile.ownerAs<QmlFile>()) {
1527 auto logger = std::make_shared<QQmlJSLogger>();
1528 logger->setFilePath(qmlFile.canonicalFilePath());
1529 logger->setCode(qmlFilePtr->code());
1530 logger->setSilent(
true);
1532 auto setupFile = [&qmlFilePtr, &qmlFile,
this](
auto &&visitor) {
1535 AST::Node::accept(qmlFilePtr->ast(), visitor);
1545 SemanticAnalysis analysis = prepareSemanticAnalysisForFileLoading();
1547 analysis.m_importer.get());
1548 v->enableLoadFileLazily(
true);
1549 v->enableScriptExpressions(
true);
1554 std::make_shared<QQmlJSTypeResolver>(analysis.m_importer.get());
1555 typeResolver->init(&v->scopeCreator(),
nullptr);
1556 qmlFilePtr->setTypeResolverWithDependencies(
1557 typeResolver, { analysis.m_importer, analysis.m_mapper,
std::move(logger) });
1559 auto v =
std::make_unique<QQmlDomAstCreator>(qmlFile);
1566 qCWarning(domLog) <<
"populateQmlFile called on non qmlFile";
1573 shared_ptr<ExternalOwningItem> current = currentItem();
1575 return current->canonicalFilePath(currentObj);
1580 if (!self.invokeVisitorOnLazyField(visitor, Fields::currentRevision,
1583 if (!self.invokeVisitorOnLazyField(visitor, Fields::lastRevision,
1586 if (!self.invokeVisitorOnLazyField(visitor, Fields::lastValidRevision,
1589 if (!visitor(
PathEls::Field(Fields::currentItem),
1592 if (!self.invokeVisitorOnLazyField(visitor, Fields::currentExposedAt,
1593 [
this]() {
return currentExposedAt(); }))
1600 return currentItem()->revision();
1605 Path p = currentItem()->canonicalPath();
1607 return static_cast<
int>(lastValue.value().toInteger(0));
1612 Path p = currentItem()->canonicalPath();
1614 return static_cast<
int>(lastValidValue.value().toInteger(0));
1619 shared_ptr<ExternalOwningItem> current = currentItem();
1620 return current->canonicalFilePath();
1625 shared_ptr<ExternalOwningItem> current = currentItem();
1626 return current->canonicalPath().dropTail();
1631 if (!self.invokeVisitorOnLazyField(visitor, Fields::currentIsValid,
1634 if (!visitor(
PathEls::Field(Fields::validItem), [
this, &self]() {
return validItem(self
); }))
1636 if (!visitor(
PathEls::Field(Fields::currentItem),
1639 if (!self.invokeVisitorOnValue(visitor,
PathEls::Field(Fields::validExposedAt), validExposedAt))
1641 if (!self.invokeVisitorOnValue(visitor,
PathEls::Field(Fields::currentExposedAt),
1642 currentExposedAt)) {
1656 std::shared_ptr<DomEnvironment> envPtr = env.ownerAs<DomEnvironment>();
1659 QMutexLocker l(envPtr->mutex());
1660 cached = envPtr->m_referenceCache.value(canonicalPath, {});
1662 qCWarning(domLog) <<
"No Env for reference" << canonicalPath <<
"from"
1673 std::shared_ptr<DomEnvironment> envPtr = env.ownerAs<DomEnvironment>();
1674 bool didSet =
false;
1676 QMutexLocker l(envPtr->mutex());
1677 RefCacheEntry &cached = envPtr->m_referenceCache[canonicalPath];
1678 switch (cached.cached) {
1679 case RefCacheEntry::Cached::None:
1683 case RefCacheEntry::Cached::First:
1684 if (addOption == AddOption::Overwrite || entry.cached == RefCacheEntry::Cached::All) {
1689 case RefCacheEntry::Cached::All:
1690 if (addOption == AddOption::Overwrite || entry.cached == RefCacheEntry::Cached::All) {
1695 if (cached.cached == RefCacheEntry::Cached::First && cached.canonicalPaths.isEmpty())
1696 cached.cached = RefCacheEntry::Cached::All;
1708#include "moc_qqmldomtop_p.cpp"