1// Copyright (C) 2016 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#ifndef QQMLTYPENAMECACHE_P_H
5#define QQMLTYPENAMECACHE_P_H
6
7//
8// W A R N I N G
9// -------------
10//
11// This file is not part of the Qt API. It exists purely as an
12// implementation detail. This header file may change from version to
13// version without notice, or even be removed.
14//
15// We mean it.
16//
17
18#include <private/qqmlrefcount_p.h>
19#include "qqmlmetatype_p.h"
20
21#include <private/qstringhash_p.h>
22#include <private/qqmlimport_p.h>
23#include <private/qqmltypemoduleversion_p.h>
24
25#include <QtCore/qvector.h>
26
27QT_BEGIN_NAMESPACE
28
29struct QQmlImportRef {
30 inline QQmlImportRef()
31 : scriptIndex(-1)
32 {}
33 // Imported module
34 QVector<QQmlTypeModuleVersion> modules;
35
36 // Or, imported script
37 int scriptIndex;
38
39 // Or, imported compositeSingletons
40 QStringHash<QUrl> compositeSingletons;
41
42 // The qualifier of this import
43 QString m_qualifier;
44};
45
46class QQmlType;
47class QQmlEngine;
48class Q_QML_PRIVATE_EXPORT QQmlTypeNameCache : public QQmlRefCounted<QQmlTypeNameCache>
49{
50public:
51 QQmlTypeNameCache(const QQmlRefPointer<QQmlImports> &imports) : m_imports(imports) {}
52 ~QQmlTypeNameCache() {}
53
54 inline bool isEmpty() const;
55
56 void add(const QHashedString &name, int sciptIndex = -1, const QHashedString &nameSpace = QHashedString());
57 void add(const QHashedString &name, const QUrl &url, const QHashedString &nameSpace = QHashedString());
58
59 struct Result {
60 inline Result();
61 inline Result(const QQmlImportRef *importNamespace);
62 inline Result(const QQmlType &type);
63 inline Result(int scriptIndex);
64
65 inline bool isValid() const;
66
67 QQmlType type;
68 const QQmlImportRef *importNamespace;
69 int scriptIndex;
70 };
71
72 enum class QueryNamespaced { No, Yes };
73
74 // Restrict the types allowed for key. We don't want QV4::ScopedString, for example.
75
76 template<QQmlImport::RecursionRestriction recursionRestriction = QQmlImport::PreventRecursion>
77 Result query(const QHashedStringRef &key) const
78 {
79 return doQuery<const QHashedStringRef &, recursionRestriction>(key);
80 }
81
82 template<QueryNamespaced queryNamespaced = QueryNamespaced::Yes>
83 Result query(const QHashedStringRef &key, const QQmlImportRef *importNamespace) const
84 {
85 return doQuery<const QHashedStringRef &, queryNamespaced>(key, importNamespace);
86 }
87
88 template<QQmlImport::RecursionRestriction recursionRestriction = QQmlImport::PreventRecursion>
89 Result query(const QV4::String *key) const
90 {
91 return doQuery<const QV4::String *, recursionRestriction>(key);
92 }
93
94 template<QueryNamespaced queryNamespaced = QueryNamespaced::Yes>
95 Result query(const QV4::String *key, const QQmlImportRef *importNamespace) const
96 {
97 return doQuery<const QV4::String *, queryNamespaced>(key, importNamespace);
98 }
99
100private:
101 friend class QQmlImports;
102
103 static QHashedStringRef toHashedStringRef(const QHashedStringRef &key) { return key; }
104 static QHashedStringRef toHashedStringRef(const QV4::String *key)
105 {
106 const QV4::Heap::String *heapString = key->d();
107
108 // toQString() would also do simplifyString(). Therefore, we can be sure that this
109 // is safe. Any other operation on the string data cannot keep references on the
110 // non-simplified pieces.
111 if (heapString->subtype >= QV4::Heap::String::StringType_Complex)
112 heapString->simplifyString();
113
114 // This is safe because the string data is backed by the QV4::String we got as
115 // parameter. The contract about passing V4 values as parameters is that you have to
116 // scope them first, so that they don't get gc'd while the callee is working on them.
117 const QStringPrivate &text = heapString->text();
118 return QHashedStringRef(QStringView(text.ptr, text.size));
119 }
120
121 static QString toQString(const QHashedStringRef &key) { return key.toString(); }
122 static QString toQString(const QV4::String *key) { return key->toQStringNoThrow(); }
123
124 template<typename Key, QQmlImport::RecursionRestriction recursionRestriction>
125 Result doQuery(Key name) const
126 {
127 Result result = doQuery(m_namedImports, name);
128
129 if (!result.isValid())
130 result = typeSearch(m_anonymousImports, name);
131
132 if (!result.isValid())
133 result = doQuery(m_anonymousCompositeSingletons, name);
134
135 if (!result.isValid()) {
136 // Look up anonymous types from the imports of this document
137 // ### it would be nice if QQmlImports allowed us to resolve a namespace
138 // first, and then types on it.
139 QQmlImportNamespace *typeNamespace = nullptr;
140 QList<QQmlError> errors;
141 QQmlType t;
142 bool typeRecursionDetected = false;
143 const bool typeFound = m_imports->resolveType(
144 toHashedStringRef(name), &t, nullptr, &typeNamespace, &errors,
145 QQmlType::AnyRegistrationType,
146 recursionRestriction == QQmlImport::AllowRecursion
147 ? &typeRecursionDetected
148 : nullptr);
149 if (typeFound)
150 return Result(t);
151
152 }
153
154 return result;
155 }
156
157 template<typename Key, QueryNamespaced queryNamespaced>
158 Result doQuery(Key name, const QQmlImportRef *importNamespace) const
159 {
160 Q_ASSERT(importNamespace && importNamespace->scriptIndex == -1);
161
162 if constexpr (queryNamespaced == QueryNamespaced::Yes) {
163 QMap<const QQmlImportRef *, QStringHash<QQmlImportRef> >::const_iterator it
164 = m_namespacedImports.constFind(key: importNamespace);
165 if (it != m_namespacedImports.constEnd()) {
166 Result r = doQuery(*it, name);
167 if (r.isValid())
168 return r;
169 }
170 }
171
172 Result result = typeSearch(importNamespace->modules, name);
173
174 if (!result.isValid())
175 result = doQuery(importNamespace->compositeSingletons, name);
176
177 if (!result.isValid()) {
178 // Look up types from the imports of this document
179 // ### it would be nice if QQmlImports allowed us to resolve a namespace
180 // first, and then types on it.
181 const QString qualifiedTypeName = importNamespace->m_qualifier + u'.' + toQString(name);
182 QQmlImportNamespace *typeNamespace = nullptr;
183 QList<QQmlError> errors;
184 QQmlType t;
185 bool typeFound = m_imports->resolveType(
186 type: qualifiedTypeName, type_return: &t, version_return: nullptr, ns_return: &typeNamespace, errors: &errors);
187 if (typeFound)
188 return Result(t);
189 }
190
191 return result;
192 }
193
194 template<typename Key>
195 Result doQuery(const QStringHash<QQmlImportRef> &imports, Key key) const
196 {
197 QQmlImportRef *i = imports.value(key);
198 if (i) {
199 Q_ASSERT(!i->m_qualifier.isEmpty());
200 if (i->scriptIndex != -1) {
201 return Result(i->scriptIndex);
202 } else {
203 return Result(i);
204 }
205 }
206
207 return Result();
208 }
209
210 template<typename Key>
211 Result doQuery(const QStringHash<QUrl> &urls, Key key) const
212 {
213 QUrl *url = urls.value(key);
214 if (url) {
215 QQmlType type = QQmlMetaType::qmlType(unNormalizedUrl: *url);
216 return Result(type);
217 }
218
219 return Result();
220 }
221
222 template<typename Key>
223 Result typeSearch(const QVector<QQmlTypeModuleVersion> &modules, Key key) const
224 {
225 QVector<QQmlTypeModuleVersion>::const_iterator end = modules.constEnd();
226 for (QVector<QQmlTypeModuleVersion>::const_iterator it = modules.constBegin(); it != end; ++it) {
227 QQmlType type = it->type(key);
228 if (type.isValid())
229 return Result(type);
230 }
231
232 return Result();
233 }
234
235 QStringHash<QQmlImportRef> m_namedImports;
236 QMap<const QQmlImportRef *, QStringHash<QQmlImportRef> > m_namespacedImports;
237 QVector<QQmlTypeModuleVersion> m_anonymousImports;
238 QStringHash<QUrl> m_anonymousCompositeSingletons;
239 QQmlRefPointer<QQmlImports> m_imports;
240};
241
242QQmlTypeNameCache::Result::Result()
243: importNamespace(nullptr), scriptIndex(-1)
244{
245}
246
247QQmlTypeNameCache::Result::Result(const QQmlImportRef *importNamespace)
248: importNamespace(importNamespace), scriptIndex(-1)
249{
250}
251
252QQmlTypeNameCache::Result::Result(const QQmlType &type)
253: type(type), importNamespace(nullptr), scriptIndex(-1)
254{
255}
256
257QQmlTypeNameCache::Result::Result(int scriptIndex)
258: importNamespace(nullptr), scriptIndex(scriptIndex)
259{
260}
261
262bool QQmlTypeNameCache::Result::isValid() const
263{
264 return type.isValid() || importNamespace || scriptIndex != -1;
265}
266
267bool QQmlTypeNameCache::isEmpty() const
268{
269 return m_namedImports.isEmpty() && m_anonymousImports.isEmpty()
270 && m_anonymousCompositeSingletons.isEmpty();
271}
272
273QT_END_NAMESPACE
274
275#endif // QQMLTYPENAMECACHE_P_H
276
277

source code of qtdeclarative/src/qml/qml/qqmltypenamecache_p.h