Qt
Internal/Contributor docs for the Qt SDK. Note: These are NOT official API docs; those are found at https://doc.qt.io/
Loading...
Searching...
No Matches
qqmldommoduleindex.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#include "qqmldomtop_p.h"
6
7#include <QtCore/QDir>
8#include <QtCore/QFileInfo>
9#include <QtCore/QScopeGuard>
10
11#include <memory>
12
14namespace QQmlJS {
15namespace Dom {
16
18{
19 static ErrorGroups res = { { DomItem::domErrorGroup, NewErrorGroup("Exports"),
20 NewErrorGroup("Version") } };
21 return res;
22}
23
25{
26 static ErrorGroups res = { { DomItem::domErrorGroup, NewErrorGroup("Exports") } };
27 return res;
28}
29
30bool ModuleScope::iterateDirectSubpaths(const DomItem &self, DirectVisitor visitor) const
31{
32 bool cont = true;
33 cont = cont && self.invokeVisitorOnValue(visitor, PathEls::Field(Fields::uri), uri);
34 cont = cont && self.invokeVisitorOnField(visitor, Fields::version, version);
35 cont = cont && visitor(PathEls::Field(Fields::exports), [this, &self]() {
36 int minorVersion = version.minorVersion;
37 return self.subMapItem(Map(
38 self.pathFromOwner().withField(Fields::exports),
39 [minorVersion](const DomItem &mapExp, const QString &name) -> DomItem {
40 DomItem mapExpOw = mapExp.owner();
41 QList<DomItem> exports =
42 mapExp.ownerAs<ModuleIndex>()->exportsWithNameAndMinorVersion(
43 mapExpOw, name, minorVersion);
44 return mapExp.subListItem(List::fromQList<DomItem>(
45 mapExp.pathFromOwner().withKey(name), exports,
46 [](const DomItem &, const PathEls::PathComponent &,
47 const DomItem &el) { return el; },
48 ListOptions::Normal));
49 },
50 [](const DomItem &mapExp) {
51 DomItem mapExpOw = mapExp.owner();
52 return mapExp.ownerAs<ModuleIndex>()->exportNames(mapExpOw);
53 },
54 QLatin1String("List<Exports>")));
55 });
56 cont = cont && visitor(PathEls::Field(Fields::symbols), [&self]() {
57 Path basePath = Path::fromCurrent(PathCurrent::Obj).withField(Fields::exports);
58 return self.subMapItem(Map(
59 self.pathFromOwner().withField(Fields::symbols),
60 [basePath](const DomItem &mapExp, const QString &name) -> DomItem {
61 QList<Path> symb({ basePath.withKey(name) });
62 return mapExp.subReferencesItem(PathEls::Key(name), symb);
63 },
64 [](const DomItem &mapExp) {
65 DomItem mapExpOw = mapExp.owner();
66 return mapExp.ownerAs<ModuleIndex>()->exportNames(mapExpOw);
67 },
68 QLatin1String("List<References>")));
69 });
70 cont = cont && visitor(PathEls::Field(Fields::autoExports), [this, &self]() {
71 return containingObject(self).field(Fields::autoExports);
72 });
73 return cont;
74}
75
76std::shared_ptr<OwningItem> ModuleIndex::doCopy(const DomItem &) const
77{
78 return std::make_shared<ModuleIndex>(*this);
79}
80
81ModuleIndex::ModuleIndex(const ModuleIndex &o)
82 : OwningItem(o), m_uri(o.uri()), m_majorVersion(o.majorVersion())
83{
84 QMap<int, ModuleScope *> scopes;
85 {
86 QMutexLocker l2(o.mutex());
87 m_qmltypesFilesPaths += o.m_qmltypesFilesPaths;
88 m_qmldirPaths += o.m_qmldirPaths;
89 m_directoryPaths += o.m_directoryPaths;
90 scopes = o.m_moduleScope;
91 }
92 auto it = scopes.begin();
93 auto end = scopes.end();
94 while (it != end) {
95 ensureMinorVersion((*it)->version.minorVersion);
96 ++it;
97 }
98}
99
100ModuleIndex::~ModuleIndex()
101{
102 QMap<int, ModuleScope *> scopes;
103 {
104 QMutexLocker l(mutex());
105 scopes = m_moduleScope;
106 m_moduleScope.clear();
107 }
108 auto it = scopes.begin();
109 auto end = scopes.end();
110 while (it != end) {
111 delete *it;
112 ++it;
113 }
114}
115
116bool ModuleIndex::iterateDirectSubpaths(const DomItem &self, DirectVisitor visitor) const
117{
118 bool cont = self.invokeVisitorOnValue(visitor, PathEls::Field(Fields::uri), uri());
119 cont = cont && self.invokeVisitorOnValue(visitor, PathEls::Field(Fields::majorVersion),
121 cont = cont && visitor(PathEls::Field(Fields::moduleScope), [this, &self]() {
122 return self.subMapItem(Map(
123 pathFromOwner().withField(Fields::moduleScope),
124 [](const DomItem &map, const QString &minorVersionStr) {
125 bool ok;
126 int minorVersion = minorVersionStr.toInt(&ok);
127 if (minorVersionStr.isEmpty()
128 || minorVersionStr.compare(u"Latest", Qt::CaseInsensitive) == 0)
129 minorVersion = Version::Latest;
130 else if (!ok)
131 return DomItem();
132 return map.copy(
133 map.ownerAs<ModuleIndex>()->ensureMinorVersion(minorVersion));
134 },
135 [this](const DomItem &) {
136 QSet<QString> res;
137 for (int el : minorVersions())
138 if (el >= 0)
139 res.insert(QString::number(el));
140 if (!minorVersions().isEmpty())
141 res.insert(QString());
142 return res;
143 },
144 QLatin1String("Map<List<Exports>>")));
145 });
146 cont = cont && visitor(PathEls::Field(Fields::sources), [this, &self]() {
147 return self.subReferencesItem(PathEls::Field(Fields::sources), sources());
148 });
149 cont = cont && self.invokeVisitorOnLazyField(visitor, Fields::autoExports, [this, &self]() {
150 return autoExports(self);
151 });
152 return cont;
153}
154
155QSet<QString> ModuleIndex::exportNames(const DomItem &self) const
156{
157 QSet<QString> res;
158 QList<Path> mySources = sources();
159 for (int i = 0; i < mySources.size(); ++i) {
160 DomItem source = self.path(mySources.at(i));
161 res += source.field(Fields::exports).keys();
162 }
163 return res;
164}
165
166QList<DomItem> ModuleIndex::autoExports(const DomItem &self) const
167{
168 QList<DomItem> res;
169 Path selfPath = canonicalPath(self).withField(Fields::autoExports);
170 RefCacheEntry cached = RefCacheEntry::forPath(self, selfPath);
171 QList<Path> cachedPaths;
172 switch (cached.cached) {
173 case RefCacheEntry::Cached::None:
174 case RefCacheEntry::Cached::First:
175 break;
176 case RefCacheEntry::Cached::All:
177 cachedPaths += cached.canonicalPaths;
178 if (cachedPaths.isEmpty())
179 return res;
180 }
181 DomItem env = self.environment();
182 if (!cachedPaths.isEmpty()) {
183 bool outdated = false;
184 for (const Path &p : cachedPaths) {
185 DomItem newEl = env.path(p);
186 if (!newEl) {
187 outdated = true;
188 qWarning() << "referenceCache outdated, reference at " << selfPath
189 << " leads to invalid path " << p;
190 break;
191 } else {
192 res.append(newEl);
193 }
194 }
195 if (outdated) {
196 res.clear();
197 } else {
198 return res;
199 }
200 }
201 QList<Path> mySources = sources();
202 QSet<QString> knownAutoImportUris;
203 QList<ModuleAutoExport> knownExports;
204 for (const Path &p : mySources) {
205 DomItem autoExports = self.path(p).field(Fields::autoExports);
206 for (const DomItem &i : autoExports.values()) {
207 if (const ModuleAutoExport *iPtr = i.as<ModuleAutoExport>()) {
208 if (!knownAutoImportUris.contains(iPtr->import.uri.toString())
209 || !knownExports.contains(*iPtr)) {
210 knownAutoImportUris.insert(iPtr->import.uri.toString());
211 knownExports.append(*iPtr);
212 res.append(i);
213 cachedPaths.append(i.canonicalPath());
214 }
215 }
216 }
217 }
218 RefCacheEntry::addForPath(self, selfPath,
219 RefCacheEntry { RefCacheEntry::Cached::All, cachedPaths });
220 return res;
221}
222
223QList<DomItem> ModuleIndex::exportsWithNameAndMinorVersion(const DomItem &self, const QString &name,
224 int minorVersion) const
225{
226 Path myPath = Paths::moduleScopePath(uri(), Version(majorVersion(), minorVersion))
227 .withField(Fields::exports)
228 .withKey(name);
229 QList<Path> mySources = sources();
230 QList<DomItem> res;
231 QList<DomItem> undef;
232 if (minorVersion < 0)
233 minorVersion = std::numeric_limits<int>::max();
234 int vNow = Version::Undefined;
235 for (int i = 0; i < mySources.size(); ++i) {
236 DomItem source = self.path(mySources.at(i));
237 DomItem exports = source.field(Fields::exports).key(name);
238 int nExports = exports.indexes();
239 if (nExports == 0)
240 continue;
241 for (int j = 0; j < nExports; ++j) {
242 DomItem exportItem = exports.index(j);
243 if (!exportItem)
244 continue;
245 DomItem version = exportItem.field(Fields::version);
246 Version const *versionPtr = version.as<Version>();
247 if (versionPtr == nullptr || !versionPtr->isValid()) {
248 undef.append(exportItem);
249 } else {
250 if (majorVersion() < 0)
252 .error(tr("Module %1 (unversioned) has versioned entries "
253 "for '%2' from %3")
254 .arg(uri(), name,
255 source.canonicalPath().toString()))
256 .withPath(myPath)));
257 if ((versionPtr->majorVersion == majorVersion()
258 || versionPtr->majorVersion == Version::Undefined)
259 && versionPtr->minorVersion >= vNow
260 && versionPtr->minorVersion <= minorVersion) {
261 if (versionPtr->minorVersion > vNow)
262 res.clear();
263 res.append(exportItem);
264 vNow = versionPtr->minorVersion;
265 }
266 }
267 }
268 }
269 if (!undef.isEmpty()) {
270 if (!res.isEmpty()) {
272 .error(tr("Module %1 (major version %2) has versioned and "
273 "unversioned entries for '%3'")
274 .arg(uri(), QString::number(majorVersion()), name))
275 .withPath(myPath)));
276 return res + undef;
277 } else {
278 return undef;
279 }
280 }
281 return res;
282}
283
284QList<Path> ModuleIndex::sources() const
285{
286 QList<Path> res;
287 QMutexLocker l(mutex());
288 res += m_qmltypesFilesPaths;
289 if (!m_qmldirPaths.isEmpty())
290 res += m_qmldirPaths.first();
291 else if (!m_directoryPaths.isEmpty())
292 res += m_directoryPaths.first();
293 return res;
294}
295
296ModuleScope *ModuleIndex::ensureMinorVersion(int minorVersion)
297{
298 if (minorVersion < 0)
299 minorVersion = Version::Latest;
300 {
301 QMutexLocker l(mutex());
302 auto it = m_moduleScope.constFind(minorVersion);
303 if (it != m_moduleScope.cend())
304 return *it;
305 }
306 ModuleScope *res = nullptr;
307 ModuleScope *newScope = new ModuleScope(m_uri, Version(majorVersion(), minorVersion));
308 auto cleanup = qScopeGuard([&newScope] { delete newScope; });
309 {
310 QMutexLocker l(mutex());
311 auto it = m_moduleScope.constFind(minorVersion);
312 if (it != m_moduleScope.cend()) {
313 res = *it;
314 } else {
315 res = newScope;
316 newScope = nullptr;
317 m_moduleScope.insert(minorVersion, res);
318 }
319 }
320 return res;
321}
322
323void ModuleIndex::mergeWith(const std::shared_ptr<ModuleIndex> &o)
324{
325 if (o) {
326 QList<Path> qmltypesPaths;
327 QMap<int, ModuleScope *> scopes;
328 {
329 QMutexLocker l2(o->mutex());
330 qmltypesPaths = o->m_qmltypesFilesPaths;
331 scopes = o->m_moduleScope;
332 }
333 {
334 QMutexLocker l(mutex());
335 for (const Path &qttPath : qmltypesPaths) {
336 if (!m_qmltypesFilesPaths.contains((qttPath)))
337 m_qmltypesFilesPaths.append(qttPath);
338 }
339 }
340 auto it = scopes.begin();
341 auto end = scopes.end();
342 while (it != end) {
343 ensureMinorVersion((*it)->version.minorVersion);
344 ++it;
345 }
346 }
347}
348
349QList<Path> ModuleIndex::qmldirsToLoad(const DomItem &self)
350{
351 // this always checks the filesystem to the qmldir file to load
352 DomItem env = self.environment();
353 std::shared_ptr<DomEnvironment> envPtr = env.ownerAs<DomEnvironment>();
354 QStringList subPathComponents = uri().split(u'.');
355 QString subPath = subPathComponents.join(u'/');
356 QString logicalPath;
357 QString subPathV = subPath + QChar::fromLatin1('.') + QString::number(majorVersion())
358 + QLatin1String("/qmldir");
359 QString dirPath;
360 if (majorVersion() >= 0) {
361 qCDebug(QQmlJSDomImporting)
362 << "ModuleIndex::qmldirsToLoad: Searching versioned module" << subPath
363 << majorVersion() << "in" << envPtr->loadPaths().join(u", ");
364 for (const QString &path : envPtr->loadPaths()) {
365 QDir dir(path);
366 QFileInfo fInfo(dir.filePath(subPathV));
367 if (fInfo.isFile()) {
368 qCDebug(QQmlJSDomImporting)
369 << "Found versioned module in " << fInfo.canonicalFilePath();
370 logicalPath = subPathV;
371 dirPath = fInfo.canonicalFilePath();
372 break;
373 }
374 }
375 }
376 if (dirPath.isEmpty()) {
377 qCDebug(QQmlJSDomImporting) << "ModuleIndex::qmldirsToLoad: Searching unversioned module"
378 << subPath << "in" << envPtr->loadPaths().join(u", ");
379 for (const QString &path : envPtr->loadPaths()) {
380 QDir dir(path);
381 QFileInfo fInfo(dir.filePath(subPath + QLatin1String("/qmldir")));
382 if (fInfo.isFile()) {
383 qCDebug(QQmlJSDomImporting)
384 << "Found unversioned module in " << fInfo.canonicalFilePath();
385 logicalPath = subPath + QLatin1String("/qmldir");
386 dirPath = fInfo.canonicalFilePath();
387 break;
388 }
389 }
390 }
391 if (!dirPath.isEmpty()) {
392 QMutexLocker l(mutex());
393 m_qmldirPaths = QList<Path>({ Paths::qmldirFilePath(dirPath) });
394 } else if (uri() != u"QML") {
395 const QString loadPaths = envPtr->loadPaths().join(u", "_s);
396 qCDebug(QQmlJSDomImporting) << "ModuleIndex::qmldirsToLoad: qmldir at"
397 << (uri() + u"/qmldir"_s) << " was not found in " << loadPaths;
400 .warning(tr("Failed to find main qmldir file for %1 %2 in %3.")
401 .arg(uri(), QString::number(majorVersion()), loadPaths))
402 .handle());
403 }
404 return qmldirPaths();
405}
406
407} // end namespace Dom
408} // end namespace QQmlJS
409QT_END_NAMESPACE
A value type that references any element of the Dom.
DomItem path(const Path &p, const ErrorHandler &h=&defaultErrorHandler) const
DomItem environment() const
Path canonicalPath() const
void addError(ErrorMessage &&msg) const
static ErrorGroup domErrorGroup
Represents a set of tags grouping a set of related error messages.
ErrorMessage warning(const Dumper &message) const
ErrorMessage error(const Dumper &message) const
ModuleScope * ensureMinorVersion(int minorVersion)
QList< Path > sources() const
std::shared_ptr< OwningItem > doCopy(const DomItem &self) const override
Path canonicalPath(const DomItem &) const override
QList< DomItem > exportsWithNameAndMinorVersion(const DomItem &self, const QString &name, int minorVersion) const
QList< Path > qmldirsToLoad(const DomItem &self)
bool iterateDirectSubpaths(const DomItem &self, DirectVisitor visitor) const override
void mergeWith(const std::shared_ptr< ModuleIndex > &o)
QList< DomItem > autoExports(const DomItem &self) const
ModuleIndex(const ModuleIndex &o)
QSet< QString > exportNames(const DomItem &self) const
bool iterateDirectSubpaths(const DomItem &self, DirectVisitor visitor) const override
A DomItem that owns other DomItems and is managed through a shared pointer.
void addErrorLocal(ErrorMessage &&msg)
static Path fromCurrent(PathCurrent c)
static bool addForPath(const DomItem &el, const Path &canonicalPath, const RefCacheEntry &entry, AddOption addOption=AddOption::KeepExisting)
static ErrorGroups myVersioningErrors()
static ErrorGroups myExportErrors()
Combined button and popup list for selecting options.
#define NewErrorGroup(name)