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
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"
4#include "qqmldomelements_p.h"
5#include "qqmldom_utils_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
31{
32 bool cont = true;
33 cont = cont && self.dvValueField(visitor, Fields::uri, uri);
34 cont = cont && self.dvWrapField(visitor, Fields::version, version);
35 cont = cont && self.dvItemField(visitor, Fields::exports, [this, &self]() {
36 int minorVersion = version.minorVersion;
37 return self.subMapItem(Map(
38 self.pathFromOwner().field(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().key(name), exports,
46 [](const DomItem &, const PathEls::PathComponent &, const DomItem &el) {
47 return el;
48 },
50 },
51 [](const DomItem &mapExp) {
52 DomItem mapExpOw = mapExp.owner();
53 return mapExp.ownerAs<ModuleIndex>()->exportNames(mapExpOw);
54 },
55 QLatin1String("List<Exports>")));
56 });
57 cont = cont && self.dvItemField(visitor, Fields::symbols, [&self]() {
58 Path basePath = Path::Current(PathCurrent::Obj).field(Fields::exports);
59 return self.subMapItem(Map(
60 self.pathFromOwner().field(Fields::symbols),
61 [basePath](const DomItem &mapExp, const QString &name) -> DomItem {
62 QList<Path> symb({ basePath.key(name) });
63 return mapExp.subReferencesItem(PathEls::Key(name), symb);
64 },
65 [](const DomItem &mapExp) {
66 DomItem mapExpOw = mapExp.owner();
67 return mapExp.ownerAs<ModuleIndex>()->exportNames(mapExpOw);
68 },
69 QLatin1String("List<References>")));
70 });
71 cont = cont && self.dvItemField(visitor, Fields::autoExports, [this, &self]() {
72 return containingObject(self).field(Fields::autoExports);
73 });
74 return cont;
75}
76
77std::shared_ptr<OwningItem> ModuleIndex::doCopy(const DomItem &) const
78{
79 return std::make_shared<ModuleIndex>(*this);
80}
81
82ModuleIndex::ModuleIndex(const ModuleIndex &o)
83 : OwningItem(o), m_uri(o.uri()), m_majorVersion(o.majorVersion())
84{
85 QMap<int, ModuleScope *> scopes;
86 {
87 QMutexLocker l2(o.mutex());
88 m_qmltypesFilesPaths += o.m_qmltypesFilesPaths;
89 m_qmldirPaths += o.m_qmldirPaths;
90 m_directoryPaths += o.m_directoryPaths;
91 scopes = o.m_moduleScope;
92 }
93 auto it = scopes.begin();
94 auto end = scopes.end();
95 while (it != end) {
96 ensureMinorVersion((*it)->version.minorVersion);
97 ++it;
98 }
99}
100
102{
103 QMap<int, ModuleScope *> scopes;
104 {
105 QMutexLocker l(mutex());
106 scopes = m_moduleScope;
107 m_moduleScope.clear();
108 }
109 auto it = scopes.begin();
110 auto end = scopes.end();
111 while (it != end) {
112 delete *it;
113 ++it;
114 }
115}
116
118{
119 bool cont = self.dvValueField(visitor, Fields::uri, uri());
120 cont = cont && self.dvValueField(visitor, Fields::majorVersion, majorVersion());
121 cont = cont && self.dvItemField(visitor, Fields::moduleScope, [this, &self]() {
122 return self.subMapItem(Map(
123 pathFromOwner(self).field(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(map.ownerAs<ModuleIndex>()->ensureMinorVersion(minorVersion));
133 },
134 [this](const DomItem &) {
135 QSet<QString> res;
136 for (int el : minorVersions())
137 if (el >= 0)
139 if (!minorVersions().isEmpty())
140 res.insert(QString());
141 return res;
142 },
143 QLatin1String("Map<List<Exports>>")));
144 });
145 cont = cont && self.dvItemField(visitor, Fields::sources, [this, &self]() {
146 return self.subReferencesItem(PathEls::Field(Fields::sources), sources());
147 });
148 cont = cont && self.dvValueLazyField(visitor, Fields::autoExports, [this, &self]() {
149 return autoExports(self);
150 });
151 return cont;
152}
153
154QSet<QString> ModuleIndex::exportNames(const DomItem &self) const
155{
156 QSet<QString> res;
157 QList<Path> mySources = sources();
158 for (int i = 0; i < mySources.size(); ++i) {
159 DomItem source = self.path(mySources.at(i));
160 res += source.field(Fields::exports).keys();
161 }
162 return res;
163}
164
165QList<DomItem> ModuleIndex::autoExports(const DomItem &self) const
166{
167 QList<DomItem> res;
168 Path selfPath = canonicalPath(self).field(Fields::autoExports);
169 RefCacheEntry cached = RefCacheEntry::forPath(self, selfPath);
170 QList<Path> cachedPaths;
171 switch (cached.cached) {
174 break;
176 cachedPaths += cached.canonicalPaths;
177 if (cachedPaths.isEmpty())
178 return res;
179 }
180 DomItem env = self.environment();
181 if (!cachedPaths.isEmpty()) {
182 bool outdated = false;
183 for (const Path &p : cachedPaths) {
184 DomItem newEl = env.path(p);
185 if (!newEl) {
186 outdated = true;
187 qWarning() << "referenceCache outdated, reference at " << selfPath
188 << " leads to invalid path " << p;
189 break;
190 } else {
191 res.append(newEl);
192 }
193 }
194 if (outdated) {
195 res.clear();
196 } else {
197 return res;
198 }
199 }
200 QList<Path> mySources = sources();
201 QSet<QString> knownAutoImportUris;
202 QList<ModuleAutoExport> knownExports;
203 for (const Path &p : mySources) {
204 DomItem autoExports = self.path(p).field(Fields::autoExports);
205 for (const DomItem &i : autoExports.values()) {
206 if (const ModuleAutoExport *iPtr = i.as<ModuleAutoExport>()) {
207 if (!knownAutoImportUris.contains(iPtr->import.uri.toString())
208 || !knownExports.contains(*iPtr)) {
209 knownAutoImportUris.insert(iPtr->import.uri.toString());
210 knownExports.append(*iPtr);
211 res.append(i);
212 cachedPaths.append(i.canonicalPath());
213 }
214 }
215 }
216 }
217 RefCacheEntry::addForPath(self, selfPath,
219 return res;
220}
221
223 int minorVersion) const
224{
225 Path myPath = Paths::moduleScopePath(uri(), Version(majorVersion(), minorVersion))
226 .field(Fields::exports)
227 .key(name);
228 QList<Path> mySources = sources();
229 QList<DomItem> res;
230 QList<DomItem> undef;
231 if (minorVersion < 0)
232 minorVersion = std::numeric_limits<int>::max();
233 int vNow = Version::Undefined;
234 for (int i = 0; i < mySources.size(); ++i) {
235 DomItem source = self.path(mySources.at(i));
236 DomItem exports = source.field(Fields::exports).key(name);
237 int nExports = exports.indexes();
238 if (nExports == 0)
239 continue;
240 for (int j = 0; j < nExports; ++j) {
241 DomItem exportItem = exports.index(j);
242 if (!exportItem)
243 continue;
244 Version const *versionPtr = exportItem.field(Fields::version).as<Version>();
245 if (versionPtr == nullptr || !versionPtr->isValid()) {
246 undef.append(exportItem);
247 } else {
248 if (majorVersion() < 0)
249 self.addError(std::move(myVersioningErrors()
250 .error(tr("Module %1 (unversioned) has versioned entries "
251 "for '%2' from %3")
252 .arg(uri(), name,
253 source.canonicalPath().toString()))
254 .withPath(myPath)));
255 if ((versionPtr->majorVersion == majorVersion()
256 || versionPtr->majorVersion == Version::Undefined)
257 && versionPtr->minorVersion >= vNow
258 && versionPtr->minorVersion <= minorVersion) {
259 if (versionPtr->minorVersion > vNow)
260 res.clear();
261 res.append(exportItem);
262 vNow = versionPtr->minorVersion;
263 }
264 }
265 }
266 }
267 if (!undef.isEmpty()) {
268 if (!res.isEmpty()) {
269 self.addError(std::move(myVersioningErrors()
270 .error(tr("Module %1 (major version %2) has versioned and "
271 "unversioned entries for '%3'")
273 .withPath(myPath)));
274 return res + undef;
275 } else {
276 return undef;
277 }
278 }
279 return res;
280}
281
282QList<Path> ModuleIndex::sources() const
283{
284 QList<Path> res;
285 QMutexLocker l(mutex());
286 res += m_qmltypesFilesPaths;
287 if (!m_qmldirPaths.isEmpty())
288 res += m_qmldirPaths.first();
289 else if (!m_directoryPaths.isEmpty())
290 res += m_directoryPaths.first();
291 return res;
292}
293
295{
296 if (minorVersion < 0)
297 minorVersion = Version::Latest;
298 {
299 QMutexLocker l(mutex());
300 auto it = m_moduleScope.constFind(minorVersion);
301 if (it != m_moduleScope.cend())
302 return *it;
303 }
304 ModuleScope *res = nullptr;
305 ModuleScope *newScope = new ModuleScope(m_uri, Version(majorVersion(), minorVersion));
306 auto cleanup = qScopeGuard([&newScope] { delete newScope; });
307 {
308 QMutexLocker l(mutex());
309 auto it = m_moduleScope.constFind(minorVersion);
310 if (it != m_moduleScope.cend()) {
311 res = *it;
312 } else {
313 res = newScope;
314 newScope = nullptr;
315 m_moduleScope.insert(minorVersion, res);
316 }
317 }
318 return res;
319}
320
321void ModuleIndex::mergeWith(const std::shared_ptr<ModuleIndex> &o)
322{
323 if (o) {
324 QList<Path> qmltypesPaths;
325 QMap<int, ModuleScope *> scopes;
326 {
327 QMutexLocker l2(o->mutex());
328 qmltypesPaths = o->m_qmltypesFilesPaths;
329 scopes = o->m_moduleScope;
330 }
331 {
332 QMutexLocker l(mutex());
333 for (const Path &qttPath : qmltypesPaths) {
334 if (!m_qmltypesFilesPaths.contains((qttPath)))
335 m_qmltypesFilesPaths.append(qttPath);
336 }
337 }
338 auto it = scopes.begin();
339 auto end = scopes.end();
340 while (it != end) {
341 ensureMinorVersion((*it)->version.minorVersion);
342 ++it;
343 }
344 }
345}
346
347QList<Path> ModuleIndex::qmldirsToLoad(const DomItem &self)
348{
349 // this always checks the filesystem to the qmldir file to load
350 DomItem env = self.environment();
351 std::shared_ptr<DomEnvironment> envPtr = env.ownerAs<DomEnvironment>();
352 QStringList subPathComponents = uri().split(u'.');
353 QString subPath = subPathComponents.join(u'/');
354 QString logicalPath;
355 QString subPathV = subPath + QChar::fromLatin1('.') + QString::number(majorVersion())
356 + QLatin1String("/qmldir");
357 QString dirPath;
358 if (majorVersion() >= 0) {
359 qCDebug(QQmlJSDomImporting)
360 << "ModuleIndex::qmldirsToLoad: Searching versioned module" << subPath
361 << majorVersion() << "in" << envPtr->loadPaths().join(u", ");
362 for (const QString &path : envPtr->loadPaths()) {
363 QDir dir(path);
364 QFileInfo fInfo(dir.filePath(subPathV));
365 if (fInfo.isFile()) {
366 qCDebug(QQmlJSDomImporting)
367 << "Found versioned module in " << fInfo.canonicalFilePath();
368 logicalPath = subPathV;
369 dirPath = fInfo.canonicalFilePath();
370 break;
371 }
372 }
373 }
374 if (dirPath.isEmpty()) {
375 qCDebug(QQmlJSDomImporting) << "ModuleIndex::qmldirsToLoad: Searching unversioned module"
376 << subPath << "in" << envPtr->loadPaths().join(u", ");
377 for (const QString &path : envPtr->loadPaths()) {
378 QDir dir(path);
379 QFileInfo fInfo(dir.filePath(subPath + QLatin1String("/qmldir")));
380 if (fInfo.isFile()) {
381 qCDebug(QQmlJSDomImporting)
382 << "Found unversioned module in " << fInfo.canonicalFilePath();
383 logicalPath = subPath + QLatin1String("/qmldir");
384 dirPath = fInfo.canonicalFilePath();
385 break;
386 }
387 }
388 }
389 if (!dirPath.isEmpty()) {
390 QMutexLocker l(mutex());
391 m_qmldirPaths = QList<Path>({ Paths::qmldirFilePath(dirPath) });
392 } else if (uri() != u"QML") {
393 const QString loadPaths = envPtr->loadPaths().join(u", "_s);
394 qCDebug(QQmlJSDomImporting) << "ModuleIndex::qmldirsToLoad: qmldir at"
395 << (uri() + u"/qmldir"_s) << " was not found in " << loadPaths;
398 .warning(tr("Failed to find main qmldir file for %1 %2 in %3.")
399 .arg(uri(), QString::number(majorVersion()), loadPaths))
400 .handle());
401 }
402 return qmldirPaths();
403}
404
405} // end namespace Dom
406} // end namespace QQmlJS
\inmodule QtCore
Definition qdir.h:20
bool isEmpty() const noexcept
Definition qlist.h:401
iterator insert(const Key &key, const T &value)
Definition qmap.h:688
\inmodule QtCore
Definition qmutex.h:313
virtual DomItem field(const DomItem &self, QStringView name) const
Represents a consistent set of types organized in modules, it is the top level of the DOM.
static ErrorGroup domErrorGroup
DomItem owner() const
std::shared_ptr< T > ownerAs() const
index_type indexes() const
DomItem field(QStringView name) const
DomItem path(const Path &p, const ErrorHandler &h=&defaultErrorHandler) const
DomItem index(index_type) const
DomItem key(const QString &name) const
Represents a set of tags grouping a set of related error messages.
ModuleScope * ensureMinorVersion(int minorVersion)
QList< Path > qmldirPaths() const
QList< Path > sources() const
Path canonicalPath(const DomItem &) const override
QList< int > minorVersions() const
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
QSet< QString > exportNames(const DomItem &self) const
bool iterateDirectSubpaths(const DomItem &self, DirectVisitor visitor) const override
QBasicMutex * mutex() const
void addErrorLocal(ErrorMessage &&msg)
Path key(const QString &name) const
Path field(const QString &name) const
static Path Current(PathCurrent c)
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)
static constexpr qint32 Undefined
static constexpr qint32 Latest
iterator begin()
Definition qset.h:136
\inmodule QtCore
\macro QT_RESTRICTED_CAST_FROM_ASCII
Definition qstring.h:129
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
void clear()
Clears the contents of the string and makes it null.
Definition qstring.h:1252
QString & insert(qsizetype i, QChar c)
Definition qstring.cpp:3132
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 & append(QChar c)
Definition qstring.cpp:3252
QMap< QString, QString > map
[6]
QSet< QString >::iterator it
Path qmldirFilePath(const QString &path)
Path moduleScopePath(const QString &uri, Version version, const ErrorHandler &)
static ErrorGroups myVersioningErrors()
static ErrorGroups myExportErrors()
Combined button and popup list for selecting options.
@ CaseInsensitive
QString self
Definition language.cpp:58
static const QPainterPath::ElementType * subPath(const QPainterPath::ElementType *t, const QPainterPath::ElementType *end, const qreal *points, bool *closed)
DBusConnection const char DBusError * error
#define qWarning
Definition qlogging.h:166
#define qCDebug(category,...)
GLuint64 GLenum void * handle
GLuint GLuint end
GLuint name
GLsizei GLsizei GLchar * source
GLuint res
GLsizei const GLchar *const * path
GLfloat GLfloat p
[1]
#define NewErrorGroup(name)
QQmlJS::Dom::DomItem DomItem
QDebug warning(QAnyStringView fileName, int lineNumber)
QScopeGuard< typename std::decay< F >::type > qScopeGuard(F &&f)
[qScopeGuard]
Definition qscopeguard.h:60
SSL_CTX int void * arg
QLatin1StringView QLatin1String
Definition qstringfwd.h:31
#define tr(X)
QString dir
[11]
QStringView el