1// Copyright (C) 2024 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 QQMLTYPELOADERDATA_P_H
5#define QQMLTYPELOADERDATA_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 <private/qqmltypeloaderqmldircontent_p.h>
20#include <private/qqmltypeloaderthread_p.h>
21#include <private/qv4engine_p.h>
22
23#include <QtQml/qtqmlglobal.h>
24#include <QtQml/qqmlengine.h>
25
26#include <QtCore/qcache.h>
27#include <QtCore/qthread.h>
28
29QT_BEGIN_NAMESPACE
30
31class QQmlProfiler;
32class QQmlQmldirData;
33class QQmlScriptBlob;
34class QQmlTypeData;
35
36class QQmlTypeLoaderSharedData
37{
38 Q_DISABLE_COPY_MOVE(QQmlTypeLoaderSharedData)
39public:
40 enum : int { MinimumTypeCacheTrimThreshold = 64 };
41
42 using TypeCache = QHash<QUrl, QQmlRefPointer<QQmlTypeData>>;
43 using ScriptCache = QHash<QUrl, QQmlRefPointer<QQmlScriptBlob>>;
44 using QmldirCache = QHash<QUrl, QQmlRefPointer<QQmlQmldirData>>;
45 using ImportDirCache = QCache<QString, QCache<QString, bool>>;
46
47 QQmlTypeLoaderSharedData() = default;
48
49 ScriptCache scriptCache;
50 TypeCache typeCache;
51 QmldirCache qmldirCache;
52 ImportDirCache importDirCache;
53
54 int typeCacheTrimThreshold = MinimumTypeCacheTrimThreshold;
55};
56
57class QQmlTypeLoaderThreadData
58{
59 Q_DISABLE_COPY_MOVE(QQmlTypeLoaderThreadData)
60public:
61 using ChecksumCache = QHash<quintptr, QByteArray>;
62 using ImportQmlDirCache = QStringHash<QQmlTypeLoaderQmldirContent *>;
63
64 struct QmldirInfo {
65 QTypeRevision version;
66 QString qmldirFilePath;
67 QString qmldirPathUrl;
68 QmldirInfo *next;
69 };
70
71 QQmlTypeLoaderThreadData() = default;
72
73 ImportQmlDirCache importQmlDirCache;
74 ChecksumCache checksumCache;
75
76 // Maps from an import to a linked list of qmldir info.
77 // Used in locateLocalQmldir()
78 QStringHash<QmldirInfo *> qmldirInfo;
79
80 // Modules for which plugins have been loaded and processed in the context of this type
81 // loader's engine. Plugins can have engine-specific initialization callbacks. This is why
82 // we have to keep track of this.
83 QSet<QString> modulesForWhichPluginsHaveBeenProcessed;
84
85 // Plugins that have been initialized in the context of this engine. In theory, the same
86 // plugin can be used for multiple modules. Therefore, we need to keep track of this
87 // separately from modulesForWhichPluginsHaveBeenProcessed.
88 QSet<QString> initializedPlugins;
89
90#if QT_CONFIG(qml_network)
91 typedef QHash<QNetworkReply *, QQmlDataBlob::Ptr> NetworkReplies;
92 NetworkReplies networkReplies;
93#endif
94};
95
96class QQmlTypeLoaderConfiguredData
97{
98 Q_DISABLE_COPY_MOVE(QQmlTypeLoaderConfiguredData)
99public:
100 QQmlTypeLoaderConfiguredData() = default;
101
102 QStringList importPaths;
103 QStringList pluginPaths;
104
105 // URL interceptors must be set before loading any types. Otherwise we get data races.
106 QList<QQmlAbstractUrlInterceptor *> urlInterceptors;
107
108#if QT_CONFIG(qml_debug)
109 QScopedPointer<QQmlProfiler> profiler;
110#endif
111
112 QV4::ExecutionEngine::DiskCacheOptions diskCacheOptions
113 = QV4::ExecutionEngine::DiskCache::Enabled;
114 bool isDebugging = false;
115 bool initialized = false;
116};
117
118#if QT_CONFIG(qml_network)
119class QQmlTypeLoaderNetworkAccessManagerData
120{
121 Q_DISABLE_COPY_MOVE(QQmlTypeLoaderNetworkAccessManagerData)
122public:
123 QQmlTypeLoaderNetworkAccessManagerData() = default;
124
125 QQmlNetworkAccessManagerFactory *networkAccessManagerFactory = nullptr;
126
127 // We need a separate mutex because the network access manger factory can be accessed not
128 // only from the engine thread and the type loader thread, but also from any WorkerScripts
129 // running in parallel to both.
130 mutable QMutex networkAccessManagerMutex;
131};
132#endif // QTCONFIG(qml_network)
133
134class QQmlTypeLoaderLockedData
135{
136 template<typename Data, typename LockedData>
137 friend class QQmlTypeLoaderSharedDataPtrBase;
138
139 template<typename Data, typename LockedData>
140 friend class QQmlTypeLoaderThreadDataPtrBase;
141
142 template<typename Data, typename LockedData>
143 friend class QQmlTypeLoaderConfiguredDataPtrBase;
144
145 template<typename Data, typename LockedData>
146 friend class QQmlNetworkAccessManagerFactoryPtrBase;
147 friend class QQmlNetworkAccessManagerFactoryPtr;
148
149 Q_DISABLE_COPY_MOVE(QQmlTypeLoaderLockedData)
150public:
151 QQmlTypeLoaderLockedData(QQmlEngine *engine);
152
153 QQmlTypeLoaderThread *thread() const { return m_thread; }
154
155 void createThread(QQmlTypeLoader *loader)
156 {
157 Q_ASSERT(m_engine->thread()->isCurrentThread());
158 m_thread = new QQmlTypeLoaderThread(loader);
159 m_thread->startup();
160 }
161
162 void deleteThread()
163 {
164 Q_ASSERT(m_engine->thread()->isCurrentThread());
165 Q_ASSERT(m_thread);
166
167 // Shut it down first, then set it to nullptr, then delete it.
168 // This makes sure that any blobs deleted as part of the deletion
169 // do not see the thread anymore.
170 m_thread->shutdown();
171 delete std::exchange(obj&: m_thread, new_val: nullptr);
172 }
173
174 QQmlEngine *engine() const
175 {
176 Q_ASSERT(m_engine->thread()->isCurrentThread());
177 return m_engine;
178 }
179
180private:
181 QQmlTypeLoaderSharedData m_sharedData;
182 QQmlTypeLoaderThreadData m_threadData;
183 QQmlTypeLoaderConfiguredData m_configuredData;
184#if QT_CONFIG(qml_network)
185 QQmlTypeLoaderNetworkAccessManagerData m_networkAccessManagerData;
186#endif
187
188 QQmlEngine *m_engine = nullptr;
189 QQmlTypeLoaderThread *m_thread = nullptr;
190};
191
192template<typename Data, typename LockedData>
193class QQmlTypeLoaderSharedDataPtrBase
194{
195 Q_DISABLE_COPY_MOVE(QQmlTypeLoaderSharedDataPtrBase)
196public:
197 Q_NODISCARD_CTOR QQmlTypeLoaderSharedDataPtrBase(LockedData *data) : data(data)
198 {
199 Q_ASSERT(data);
200 if (QQmlTypeLoaderThread *thread = data->thread())
201 thread->lock();
202 }
203
204 ~QQmlTypeLoaderSharedDataPtrBase()
205 {
206 Q_ASSERT(data);
207 if (QQmlTypeLoaderThread *thread = data->thread())
208 thread->unlock();
209 }
210
211 Data &operator*() const { return data->m_sharedData; }
212 Data *operator->() const { return &data->m_sharedData; }
213 operator Data *() const { return &data->m_sharedData; }
214
215private:
216 LockedData *data = nullptr;
217};
218
219using QQmlTypeLoaderSharedDataPtr
220 = QQmlTypeLoaderSharedDataPtrBase<QQmlTypeLoaderSharedData, QQmlTypeLoaderLockedData>;
221using QQmlTypeLoaderSharedDataConstPtr
222 = QQmlTypeLoaderSharedDataPtrBase<const QQmlTypeLoaderSharedData, const QQmlTypeLoaderLockedData>;
223
224template<typename Data, typename LockedData>
225class QQmlTypeLoaderThreadDataPtrBase
226{
227 Q_DISABLE_COPY_MOVE(QQmlTypeLoaderThreadDataPtrBase)
228public:
229 Q_NODISCARD_CTOR QQmlTypeLoaderThreadDataPtrBase(LockedData *data) : data(data)
230 {
231 Q_ASSERT(data);
232
233 // You have to either be on the type loader thread or shut it down before accessing this.
234 Q_ASSERT(!data->thread() || data->thread()->isThisThread());
235 }
236
237 Data &operator*() const { return data->m_threadData; }
238 Data *operator->() const { return &data->m_threadData; }
239 operator Data *() const { return &data->m_threadData; }
240
241private:
242 LockedData *data = nullptr;
243};
244
245using QQmlTypeLoaderThreadDataPtr
246 = QQmlTypeLoaderThreadDataPtrBase<QQmlTypeLoaderThreadData, QQmlTypeLoaderLockedData>;
247using QQmlTypeLoaderThreadDataConstPtr
248 = QQmlTypeLoaderThreadDataPtrBase<const QQmlTypeLoaderThreadData, const QQmlTypeLoaderLockedData>;
249
250
251template<typename Data, typename LockedData>
252class QQmlTypeLoaderConfiguredDataPtrBase
253{
254 Q_DISABLE_COPY_MOVE(QQmlTypeLoaderConfiguredDataPtrBase)
255public:
256 Q_NODISCARD_CTOR QQmlTypeLoaderConfiguredDataPtrBase(LockedData *data) : data(data)
257 {
258 Q_ASSERT(data);
259
260 if constexpr (!std::is_const_v<Data>) {
261 // const access is generally fine
262 // For mutable access we first need to make sure the thread is shut down.
263 if (data->thread())
264 data->deleteThread();
265 }
266 }
267
268 Data &operator*() const { return data->m_configuredData; }
269 Data *operator->() const { return &data->m_configuredData; }
270 operator Data *() const { return &data->m_configuredData; }
271
272private:
273 LockedData *data = nullptr;
274};
275
276using QQmlTypeLoaderConfiguredDataPtr
277 = QQmlTypeLoaderConfiguredDataPtrBase<QQmlTypeLoaderConfiguredData, QQmlTypeLoaderLockedData>;
278using QQmlTypeLoaderConfiguredDataConstPtr
279 = QQmlTypeLoaderConfiguredDataPtrBase<const QQmlTypeLoaderConfiguredData, const QQmlTypeLoaderLockedData>;
280
281#if QT_CONFIG(qml_network)
282class QQmlEnginePublicAPIToken;
283template<typename Data, typename LockedData>
284class QQmlNetworkAccessManagerFactoryPtrBase
285{
286 Q_DISABLE_COPY_MOVE(QQmlNetworkAccessManagerFactoryPtrBase)
287public:
288 Q_NODISCARD_CTOR QQmlNetworkAccessManagerFactoryPtrBase(LockedData *data) : data(data)
289 {
290 Q_ASSERT(data);
291 data->m_networkAccessManagerData.networkAccessManagerMutex.lock();
292 }
293
294 ~QQmlNetworkAccessManagerFactoryPtrBase()
295 {
296 Q_ASSERT(data);
297 data->m_networkAccessManagerData.networkAccessManagerMutex.unlock();
298 }
299
300 QQmlNetworkAccessManagerFactory &operator*() const
301 {
302 return *data->m_networkAccessManagerData.networkAccessManagerFactory;
303 }
304
305 QQmlNetworkAccessManagerFactory *operator->() const
306 {
307 return data->m_networkAccessManagerData.networkAccessManagerFactory;
308 }
309
310 operator bool() const
311 {
312 return data->m_networkAccessManagerData.networkAccessManagerFactory != nullptr;
313 }
314
315 // This is dangerous since it allows you to subvert the locking.
316 // You must only do this when serving public API that can't be changed for now.
317 QQmlNetworkAccessManagerFactory *get(const QQmlEnginePublicAPIToken &) const
318 {
319 return data->m_networkAccessManagerData.networkAccessManagerFactory;
320 }
321
322protected:
323 LockedData *data = nullptr;
324};
325
326// This is const in the sense that you cannot reset the pointer. Therefore the "Const" postfix.
327// That's in contrast to the other *Ptr classes here that are const in the way that the _data_
328// pointed to cannot be modified.
329using QQmlNetworkAccessManagerFactoryPtrConst
330 = QQmlNetworkAccessManagerFactoryPtrBase<const QQmlTypeLoaderNetworkAccessManagerData, const QQmlTypeLoaderLockedData>;
331
332class QQmlNetworkAccessManagerFactoryPtr
333 : public QQmlNetworkAccessManagerFactoryPtrBase<QQmlTypeLoaderNetworkAccessManagerData, QQmlTypeLoaderLockedData>
334{
335 Q_DISABLE_COPY(QQmlNetworkAccessManagerFactoryPtr)
336public:
337 Q_NODISCARD_CTOR QQmlNetworkAccessManagerFactoryPtr(QQmlTypeLoaderLockedData *lockedData)
338 : QQmlNetworkAccessManagerFactoryPtrBase<QQmlTypeLoaderNetworkAccessManagerData, QQmlTypeLoaderLockedData>(lockedData)
339 {
340 }
341
342 void reset(QQmlNetworkAccessManagerFactory *factory)
343 {
344 data->m_networkAccessManagerData.networkAccessManagerFactory = factory;
345 }
346};
347#endif // QT_CONFIG(qml_network)
348
349QT_END_NAMESPACE
350
351#endif // QQMLTYPELOADERDATA_P_H
352

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