1/****************************************************************************
2**
3** Copyright (C) 2019 The Qt Company Ltd.
4** Contact: https://www.qt.io/licensing/
5**
6** This file is part of the QtQml module of the Qt Toolkit.
7**
8** $QT_BEGIN_LICENSE:LGPL$
9** Commercial License Usage
10** Licensees holding valid commercial Qt licenses may use this file in
11** accordance with the commercial license agreement provided with the
12** Software or, alternatively, in accordance with the terms contained in
13** a written agreement between you and The Qt Company. For licensing terms
14** and conditions see https://www.qt.io/terms-conditions. For further
15** information use the contact form at https://www.qt.io/contact-us.
16**
17** GNU Lesser General Public License Usage
18** Alternatively, this file may be used under the terms of the GNU Lesser
19** General Public License version 3 as published by the Free Software
20** Foundation and appearing in the file LICENSE.LGPL3 included in the
21** packaging of this file. Please review the following information to
22** ensure the GNU Lesser General Public License version 3 requirements
23** will be met: https://www.gnu.org/licenses/lgpl-3.0.html.
24**
25** GNU General Public License Usage
26** Alternatively, this file may be used under the terms of the GNU
27** General Public License version 2.0 or (at your option) the GNU General
28** Public license version 3 or any later version approved by the KDE Free
29** Qt Foundation. The licenses are as published by the Free Software
30** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3
31** included in the packaging of this file. Please review the following
32** information to ensure the GNU General Public License requirements will
33** be met: https://www.gnu.org/licenses/gpl-2.0.html and
34** https://www.gnu.org/licenses/gpl-3.0.html.
35**
36** $QT_END_LICENSE$
37**
38****************************************************************************/
39
40#include "qqml.h"
41
42#include <QtQml/qqmlprivate.h>
43
44#include <private/qqmlengine_p.h>
45#include <private/qqmlmetatype_p.h>
46#include <private/qqmlmetatypedata_p.h>
47#include <private/qqmltype_p_p.h>
48#include <private/qqmltypemodule_p_p.h>
49#include <private/qqmltypenotavailable_p.h>
50
51#include <QtCore/qmutex.h>
52
53QT_BEGIN_NAMESPACE
54
55void qmlClearTypeRegistrations() // Declared in qqml.h
56{
57 QQmlMetaType::clearTypeRegistrations();
58 QQmlEnginePrivate::baseModulesUninitialized = true; //So the engine re-registers its types
59 qmlClearEnginePlugins();
60}
61
62//From qqml.h
63bool qmlProtectModule(const char *uri, int majVersion)
64{
65 return QQmlMetaType::protectModule(uri: QString::fromUtf8(str: uri), majVersion);
66}
67
68//From qqml.h
69void qmlRegisterModule(const char *uri, int versionMajor, int versionMinor)
70{
71 QQmlMetaType::registerModule(uri, versionMajor, versionMinor);
72}
73
74//From qqml.h
75int qmlTypeId(const char *uri, int versionMajor, int versionMinor, const char *qmlName)
76{
77 return QQmlMetaType::typeId(uri, versionMajor, versionMinor, qmlName);
78}
79
80// From qqmlprivate.h
81QObject *QQmlPrivate::RegisterSingletonFunctor::operator()(QQmlEngine *qeng, QJSEngine *)
82{
83 if (!m_object) {
84 QQmlError error;
85 error.setDescription(QLatin1String("The registered singleton has already been deleted. Ensure that it outlives the engine."));
86 QQmlEnginePrivate::get(e: qeng)->warning(qeng, error);
87 return nullptr;
88 }
89
90 if (qeng->thread() != m_object->thread()) {
91 QQmlError error;
92 error.setDescription(QLatin1String("Registered object must live in the same thread as the engine it was registered with"));
93 QQmlEnginePrivate::get(e: qeng)->warning(qeng, error);
94 return nullptr;
95 }
96 if (alreadyCalled) {
97 QQmlError error;
98 error.setDescription(QLatin1String("Singleton registered by registerSingletonInstance must only be accessed from one engine"));
99 QQmlEnginePrivate::get(e: qeng)->warning(qeng, error);
100 return nullptr;
101 }
102 alreadyCalled = true;
103 qeng->setObjectOwnership(m_object, QQmlEngine::CppOwnership);
104 return m_object;
105};
106
107static QVector<int> availableRevisions(const QMetaObject *metaObject)
108{
109 QVector<int> revisions;
110 if (!metaObject)
111 return revisions;
112 const int propertyOffset = metaObject->propertyOffset();
113 const int propertyCount = metaObject->propertyCount();
114 for (int propertyIndex = propertyOffset, propertyEnd = propertyOffset + propertyCount;
115 propertyIndex < propertyEnd; ++propertyIndex) {
116 const QMetaProperty property = metaObject->property(index: propertyIndex);
117 if (int revision = property.revision())
118 revisions.append(t: revision);
119 }
120 const int methodOffset = metaObject->methodOffset();
121 const int methodCount = metaObject->methodCount();
122 for (int methodIndex = methodOffset, methodEnd = methodOffset + methodCount;
123 methodIndex < methodEnd; ++methodIndex) {
124 const QMetaMethod method = metaObject->method(index: methodIndex);
125 if (int revision = method.revision())
126 revisions.append(t: revision);
127 }
128
129 // Need to also check parent meta objects, as their revisions are inherited.
130 if (const QMetaObject *superMeta = metaObject->superClass())
131 revisions += availableRevisions(metaObject: superMeta);
132
133 return revisions;
134}
135
136/*
137This method is "over generalized" to allow us to (potentially) register more types of things in
138the future without adding exported symbols.
139*/
140int QQmlPrivate::qmlregister(RegistrationType type, void *data)
141{
142 QQmlType dtype;
143 switch (type) {
144 case AutoParentRegistration:
145 return QQmlMetaType::registerAutoParentFunction(
146 autoparent: *reinterpret_cast<RegisterAutoParent *>(data));
147 case QmlUnitCacheHookRegistration:
148 return QQmlMetaType::registerUnitCacheHook(
149 hookRegistration: *reinterpret_cast<RegisterQmlUnitCacheHook *>(data));
150 case TypeAndRevisionsRegistration: {
151 const RegisterTypeAndRevisions &type = *reinterpret_cast<RegisterTypeAndRevisions *>(data);
152 const char *elementName = classElementName(metaObject: type.classInfoMetaObject);
153 const bool creatable = (elementName != nullptr)
154 && boolClassInfo(metaObject: type.classInfoMetaObject, key: "QML.Creatable", defaultValue: true);
155
156 const QString noCreateReason = creatable
157 ? QString()
158 : QString::fromUtf8(str: classInfo(metaObject: type.classInfoMetaObject, key: "QML.UncreatableReason"));
159 RegisterType revisionRegistration = {
160 .version: 1,
161 .typeId: type.typeId,
162 .listId: type.listId,
163 .objectSize: creatable ? type.objectSize : 0,
164 .create: nullptr,
165 .noCreationReason: noCreateReason,
166 .uri: type.uri,
167 .versionMajor: type.versionMajor,
168 .versionMinor: -1,
169 .elementName: nullptr,
170 .metaObject: type.metaObject,
171 .attachedPropertiesFunction: type.attachedPropertiesFunction,
172 .attachedPropertiesMetaObject: type.attachedPropertiesMetaObject,
173 .parserStatusCast: type.parserStatusCast,
174 .valueSourceCast: type.valueSourceCast,
175 .valueInterceptorCast: type.valueInterceptorCast,
176 .extensionObjectCreate: type.extensionObjectCreate,
177 .extensionMetaObject: type.extensionMetaObject,
178 .customParser: nullptr,
179 .revision: -1
180 };
181
182 const int added = intClassInfo(metaObject: type.classInfoMetaObject, key: "QML.AddedInMinorVersion", defaultValue: 0);
183 const int removed = intClassInfo(metaObject: type.classInfoMetaObject, key: "QML.RemovedInMinorVersion", defaultValue: -1);
184
185 auto revisions = availableRevisions(metaObject: type.metaObject);
186 revisions.append(t: qMax(a: added, b: 0));
187 if (type.attachedPropertiesMetaObject)
188 revisions += availableRevisions(metaObject: type.attachedPropertiesMetaObject);
189
190 std::sort(first: revisions.begin(), last: revisions.end());
191 const auto it = std::unique(first: revisions.begin(), last: revisions.end());
192 revisions.erase(abegin: it, aend: revisions.end());
193
194 const bool typeWasRemoved = removed >= added;
195 for (int revision : revisions) {
196 if (revision < added)
197 continue;
198
199 // When removed, we still add revisions, but anonymous ones
200 if (typeWasRemoved && revision >= removed) {
201 revisionRegistration.elementName = nullptr;
202 revisionRegistration.create = nullptr;
203 } else {
204 revisionRegistration.elementName = elementName;
205 revisionRegistration.create = creatable ? type.create : nullptr;
206 }
207
208 // Equivalent of qmlRegisterRevision<T, revision>(...)
209 revisionRegistration.versionMinor = revision;
210 revisionRegistration.revision = revision;
211 revisionRegistration.customParser = type.customParserFactory();
212
213 qmlregister(type: TypeRegistration, data: &revisionRegistration);
214 }
215 break;
216 }
217 case SingletonAndRevisionsRegistration: {
218 const RegisterSingletonTypeAndRevisions &type
219 = *reinterpret_cast<RegisterSingletonTypeAndRevisions *>(data);
220 const char *elementName = classElementName(metaObject: type.classInfoMetaObject);
221 RegisterSingletonType revisionRegistration = {
222 .version: QmlCurrentSingletonTypeRegistrationVersion,
223 .uri: type.uri,
224 .versionMajor: type.versionMajor,
225 .versionMinor: -1,
226 .typeName: elementName,
227
228 .scriptApi: type.scriptApi,
229 .qobjectApi: nullptr,
230 .instanceMetaObject: type.instanceMetaObject,
231 .typeId: type.typeId,
232 .revision: -1,
233
234 .generalizedQobjectApi: type.generalizedQobjectApi
235 };
236
237 const int added = intClassInfo(metaObject: type.classInfoMetaObject, key: "QML.AddedInMinorVersion", defaultValue: 0);
238 const int removed = intClassInfo(metaObject: type.classInfoMetaObject, key: "QML.RemovedInMinorVersion", defaultValue: -1);
239
240 auto revisions = availableRevisions(metaObject: type.instanceMetaObject);
241 revisions.append(t: qMax(a: added, b: 0));
242
243 std::sort(first: revisions.begin(), last: revisions.end());
244 const auto it = std::unique(first: revisions.begin(), last: revisions.end());
245 revisions.erase(abegin: it, aend: revisions.end());
246
247 const bool typeWasRemoved = removed >= added;
248 for (int revision : qAsConst(t&: revisions)) {
249 if (revision < added)
250 continue;
251
252 // When removed, we still add revisions, but anonymous ones
253 if (typeWasRemoved && revision >= removed) {
254 revisionRegistration.typeName = nullptr;
255 revisionRegistration.scriptApi = nullptr;
256 revisionRegistration.generalizedQobjectApi = nullptr;
257 } else {
258 revisionRegistration.typeName = elementName;
259 revisionRegistration.scriptApi = type.scriptApi;
260 revisionRegistration.generalizedQobjectApi = type.generalizedQobjectApi;
261 }
262
263 // Equivalent of qmlRegisterRevision<T, revision>(...)
264 revisionRegistration.versionMinor = revision;
265 revisionRegistration.revision = revision;
266
267 qmlregister(type: SingletonRegistration, data: &revisionRegistration);
268 }
269 break;
270 }
271 case TypeRegistration:
272 dtype = QQmlMetaType::registerType(type: *reinterpret_cast<RegisterType *>(data));
273 break;
274 case InterfaceRegistration:
275 dtype = QQmlMetaType::registerInterface(type: *reinterpret_cast<RegisterInterface *>(data));
276 break;
277 case SingletonRegistration:
278 dtype = QQmlMetaType::registerSingletonType(type: *reinterpret_cast<RegisterSingletonType *>(data));
279 break;
280 case CompositeRegistration:
281 dtype = QQmlMetaType::registerCompositeType(type: *reinterpret_cast<RegisterCompositeType *>(data));
282 break;
283 case CompositeSingletonRegistration:
284 dtype = QQmlMetaType::registerCompositeSingletonType(type: *reinterpret_cast<RegisterCompositeSingletonType *>(data));
285 break;
286 default:
287 return -1;
288 }
289
290 if (!dtype.isValid())
291 return -1;
292
293 QQmlMetaType::registerUndeletableType(dtype);
294 return dtype.index();
295}
296
297void QQmlPrivate::qmlunregister(RegistrationType type, quintptr data)
298{
299 switch (type) {
300 case AutoParentRegistration:
301 QQmlMetaType::unregisterAutoParentFunction(function: reinterpret_cast<AutoParentFunction>(data));
302 break;
303 case QmlUnitCacheHookRegistration:
304 QQmlMetaType::removeCachedUnitLookupFunction(
305 handler: reinterpret_cast<QmlUnitCacheLookupFunction>(data));
306 break;
307 case TypeRegistration:
308 case InterfaceRegistration:
309 case SingletonRegistration:
310 case CompositeRegistration:
311 case CompositeSingletonRegistration:
312 QQmlMetaType::unregisterType(type: data);
313 break;
314 case TypeAndRevisionsRegistration:
315 case SingletonAndRevisionsRegistration:
316 // Currently unnecessary. We'd need a special data structure to hold
317 // URI + majorVersion and then we'd iterate the minor versions, look up the
318 // associated QQmlType objects by uri/elementName/major/minor and qmlunregister
319 // each of them.
320 Q_UNREACHABLE();
321 break;
322 }
323}
324
325namespace QQmlPrivate {
326 template<>
327 void qmlRegisterTypeAndRevisions<QQmlTypeNotAvailable, void>(
328 const char *uri, int versionMajor, const QMetaObject *classInfoMetaObject)
329 {
330 using T = QQmlTypeNotAvailable;
331
332 QML_GETTYPENAMES
333
334 RegisterTypeAndRevisions type = {
335 .version: 0,
336 .typeId: qRegisterNormalizedMetaType<T *>(normalizedTypeName: pointerName.constData()),
337 .listId: qRegisterNormalizedMetaType<QQmlListProperty<T> >(normalizedTypeName: listName.constData()),
338 .objectSize: 0,
339 .create: nullptr,
340
341 .uri: uri,
342 .versionMajor: versionMajor,
343
344 .metaObject: &QQmlTypeNotAvailable::staticMetaObject,
345 .classInfoMetaObject: classInfoMetaObject,
346
347 .attachedPropertiesFunction: attachedPropertiesFunc<T>(),
348 .attachedPropertiesMetaObject: attachedPropertiesMetaObject<T>(),
349
350 .parserStatusCast: StaticCastSelector<T, QQmlParserStatus>::cast(),
351 .valueSourceCast: StaticCastSelector<T, QQmlPropertyValueSource>::cast(),
352 .valueInterceptorCast: StaticCastSelector<T, QQmlPropertyValueInterceptor>::cast(),
353
354 .extensionObjectCreate: nullptr, .extensionMetaObject: nullptr, .customParserFactory: qmlCreateCustomParser<T>
355 };
356
357 qmlregister(type: TypeAndRevisionsRegistration, data: &type);
358 }
359}
360
361QT_END_NAMESPACE
362

source code of qtdeclarative/src/qml/qml/qqml.cpp