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 | |
27 | QT_BEGIN_NAMESPACE |
28 | |
29 | struct 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 | |
46 | class QQmlType; |
47 | class QQmlEngine; |
48 | class Q_QML_PRIVATE_EXPORT QQmlTypeNameCache : public QQmlRefCounted<QQmlTypeNameCache> |
49 | { |
50 | public: |
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 | |
100 | private: |
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 | |
242 | QQmlTypeNameCache::Result::Result() |
243 | : importNamespace(nullptr), scriptIndex(-1) |
244 | { |
245 | } |
246 | |
247 | QQmlTypeNameCache::Result::Result(const QQmlImportRef *importNamespace) |
248 | : importNamespace(importNamespace), scriptIndex(-1) |
249 | { |
250 | } |
251 | |
252 | QQmlTypeNameCache::Result::Result(const QQmlType &type) |
253 | : type(type), importNamespace(nullptr), scriptIndex(-1) |
254 | { |
255 | } |
256 | |
257 | QQmlTypeNameCache::Result::Result(int scriptIndex) |
258 | : importNamespace(nullptr), scriptIndex(scriptIndex) |
259 | { |
260 | } |
261 | |
262 | bool QQmlTypeNameCache::Result::isValid() const |
263 | { |
264 | return type.isValid() || importNamespace || scriptIndex != -1; |
265 | } |
266 | |
267 | bool QQmlTypeNameCache::isEmpty() const |
268 | { |
269 | return m_namedImports.isEmpty() && m_anonymousImports.isEmpty() |
270 | && m_anonymousCompositeSingletons.isEmpty(); |
271 | } |
272 | |
273 | QT_END_NAMESPACE |
274 | |
275 | #endif // QQMLTYPENAMECACHE_P_H |
276 | |
277 | |