1 | /**************************************************************************** |
2 | ** |
3 | ** Copyright (C) 2016 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 "qqmlmetatype_p.h" |
41 | |
42 | #include <private/qqmlmetatypedata_p.h> |
43 | #include <private/qqmltypemodule_p_p.h> |
44 | #include <private/qqmltype_p_p.h> |
45 | #include <private/qqmltypeloader_p.h> |
46 | #include <private/qqmlextensionplugin_p.h> |
47 | #include <private/qv4executablecompilationunit_p.h> |
48 | |
49 | #include <QtCore/qcoreapplication.h> |
50 | #include <QtCore/qmutex.h> |
51 | #include <QtCore/qloggingcategory.h> |
52 | |
53 | Q_DECLARE_LOGGING_CATEGORY(DBG_DISK_CACHE) |
54 | |
55 | QT_BEGIN_NAMESPACE |
56 | |
57 | struct LockedData : private QQmlMetaTypeData |
58 | { |
59 | friend class QQmlMetaTypeDataPtr; |
60 | }; |
61 | |
62 | Q_GLOBAL_STATIC(LockedData, metaTypeData) |
63 | Q_GLOBAL_STATIC(QRecursiveMutex, metaTypeDataLock) |
64 | |
65 | class QQmlMetaTypeDataPtr |
66 | { |
67 | Q_DISABLE_COPY_MOVE(QQmlMetaTypeDataPtr) |
68 | public: |
69 | QQmlMetaTypeDataPtr() : locker(metaTypeDataLock()), data(metaTypeData()) {} |
70 | ~QQmlMetaTypeDataPtr() = default; |
71 | |
72 | QQmlMetaTypeData &operator*() { return *data; } |
73 | QQmlMetaTypeData *operator->() { return data; } |
74 | operator QQmlMetaTypeData *() { return data; } |
75 | |
76 | const QQmlMetaTypeData &operator*() const { return *data; } |
77 | const QQmlMetaTypeData *operator->() const { return data; } |
78 | operator const QQmlMetaTypeData *() const { return data; } |
79 | |
80 | bool isValid() const { return data != nullptr; } |
81 | |
82 | private: |
83 | QMutexLocker locker; |
84 | LockedData *data = nullptr; |
85 | }; |
86 | |
87 | static QQmlTypePrivate *createQQmlType(QQmlMetaTypeData *data, |
88 | const QQmlPrivate::RegisterInterface &type) |
89 | { |
90 | auto *d = new QQmlTypePrivate(QQmlType::InterfaceType); |
91 | d->iid = type.iid; |
92 | d->typeId = type.typeId; |
93 | d->listId = type.listId; |
94 | d->isSetup = true; |
95 | d->version_min = 0; |
96 | if (type.version > 0) { |
97 | d->module = QString::fromUtf8(str: type.uri); |
98 | d->version_maj = type.versionMajor; |
99 | } else { |
100 | d->version_maj = 0; |
101 | } |
102 | data->registerType(priv: d); |
103 | return d; |
104 | } |
105 | |
106 | static QQmlTypePrivate *createQQmlType(QQmlMetaTypeData *data, const QString &elementName, |
107 | const QQmlPrivate::RegisterSingletonType &type) |
108 | { |
109 | auto *d = new QQmlTypePrivate(QQmlType::SingletonType); |
110 | data->registerType(priv: d); |
111 | |
112 | d->setName(uri: QString::fromUtf8(str: type.uri), element: elementName); |
113 | d->version_maj = type.versionMajor; |
114 | d->version_min = type.versionMinor; |
115 | |
116 | if (type.qobjectApi || (type.version >= 3 && type.generalizedQobjectApi)) { |
117 | if (type.version >= 1) // static metaobject added in version 1 |
118 | d->baseMetaObject = type.instanceMetaObject; |
119 | if (type.version >= 2) // typeId added in version 2 |
120 | d->typeId = type.typeId; |
121 | if (type.version >= 2) // revisions added in version 2 |
122 | d->revision = type.revision; |
123 | } |
124 | |
125 | d->extraData.sd->singletonInstanceInfo = new QQmlType::SingletonInstanceInfo; |
126 | d->extraData.sd->singletonInstanceInfo->scriptCallback = type.scriptApi; |
127 | if (type.version >= 3) { |
128 | d->extraData.sd->singletonInstanceInfo->qobjectCallback = type.generalizedQobjectApi; |
129 | } else { |
130 | d->extraData.sd->singletonInstanceInfo->qobjectCallback = type.qobjectApi; |
131 | } |
132 | d->extraData.sd->singletonInstanceInfo->typeName = QString::fromUtf8(str: type.typeName); |
133 | d->extraData.sd->singletonInstanceInfo->instanceMetaObject |
134 | = ((type.qobjectApi || (type.version >= 3 && type.generalizedQobjectApi) ) && type.version >= 1) ? type.instanceMetaObject : nullptr; |
135 | |
136 | return d; |
137 | } |
138 | |
139 | static QQmlTypePrivate *createQQmlType(QQmlMetaTypeData *data, const QString &elementName, |
140 | const QQmlPrivate::RegisterType &type) |
141 | { |
142 | QQmlTypePrivate *d = new QQmlTypePrivate(QQmlType::CppType); |
143 | data->registerType(priv: d); |
144 | d->setName(uri: QString::fromUtf8(str: type.uri), element: elementName); |
145 | |
146 | d->version_maj = type.versionMajor; |
147 | d->version_min = type.versionMinor; |
148 | if (type.version >= 1) // revisions added in version 1 |
149 | d->revision = type.revision; |
150 | d->typeId = type.typeId; |
151 | d->listId = type.listId; |
152 | d->extraData.cd->allocationSize = type.objectSize; |
153 | d->extraData.cd->newFunc = type.create; |
154 | d->extraData.cd->noCreationReason = type.noCreationReason; |
155 | d->baseMetaObject = type.metaObject; |
156 | d->extraData.cd->attachedPropertiesFunc = type.attachedPropertiesFunction; |
157 | d->extraData.cd->attachedPropertiesType = type.attachedPropertiesMetaObject; |
158 | d->extraData.cd->parserStatusCast = type.parserStatusCast; |
159 | d->extraData.cd->propertyValueSourceCast = type.valueSourceCast; |
160 | d->extraData.cd->propertyValueInterceptorCast = type.valueInterceptorCast; |
161 | d->extraData.cd->extFunc = type.extensionObjectCreate; |
162 | d->extraData.cd->customParser = reinterpret_cast<QQmlCustomParser *>(type.customParser); |
163 | d->extraData.cd->registerEnumClassesUnscoped = true; |
164 | |
165 | if (type.extensionMetaObject) |
166 | d->extraData.cd->extMetaObject = type.extensionMetaObject; |
167 | |
168 | // Check if the user wants only scoped enum classes |
169 | if (d->baseMetaObject) { |
170 | auto indexOfClassInfo = d->baseMetaObject->indexOfClassInfo(name: "RegisterEnumClassesUnscoped" ); |
171 | if (indexOfClassInfo != -1 && QString::fromUtf8(str: d->baseMetaObject->classInfo(index: indexOfClassInfo).value()) == QLatin1String("false" )) |
172 | d->extraData.cd->registerEnumClassesUnscoped = false; |
173 | } |
174 | |
175 | return d; |
176 | } |
177 | |
178 | static QQmlTypePrivate *createQQmlType(QQmlMetaTypeData *data, const QString &elementName, |
179 | const QQmlPrivate::RegisterCompositeType &type) |
180 | { |
181 | auto *d = new QQmlTypePrivate(QQmlType::CompositeType); |
182 | data->registerType(priv: d); |
183 | d->setName(uri: QString::fromUtf8(str: type.uri), element: elementName); |
184 | d->version_maj = type.versionMajor; |
185 | d->version_min = type.versionMinor; |
186 | |
187 | d->extraData.fd->url = QQmlTypeLoader::normalize(unNormalizedUrl: type.url); |
188 | return d; |
189 | } |
190 | |
191 | static QQmlTypePrivate *createQQmlType(QQmlMetaTypeData *data, const QString &elementName, |
192 | const QQmlPrivate::RegisterCompositeSingletonType &type) |
193 | { |
194 | auto *d = new QQmlTypePrivate(QQmlType::CompositeSingletonType); |
195 | data->registerType(priv: d); |
196 | d->setName(uri: QString::fromUtf8(str: type.uri), element: elementName); |
197 | |
198 | d->version_maj = type.versionMajor; |
199 | d->version_min = type.versionMinor; |
200 | |
201 | d->extraData.sd->singletonInstanceInfo = new QQmlType::SingletonInstanceInfo; |
202 | d->extraData.sd->singletonInstanceInfo->url = QQmlTypeLoader::normalize(unNormalizedUrl: type.url); |
203 | d->extraData.sd->singletonInstanceInfo->typeName = QString::fromUtf8(str: type.typeName); |
204 | return d; |
205 | } |
206 | |
207 | void QQmlMetaType::clone(QMetaObjectBuilder &builder, const QMetaObject *mo, |
208 | const QMetaObject *ignoreStart, const QMetaObject *ignoreEnd) |
209 | { |
210 | // Set classname |
211 | builder.setClassName(ignoreEnd->className()); |
212 | |
213 | // Clone Q_CLASSINFO |
214 | for (int ii = mo->classInfoOffset(); ii < mo->classInfoCount(); ++ii) { |
215 | QMetaClassInfo info = mo->classInfo(index: ii); |
216 | |
217 | int otherIndex = ignoreEnd->indexOfClassInfo(name: info.name()); |
218 | if (otherIndex >= ignoreStart->classInfoOffset() + ignoreStart->classInfoCount()) { |
219 | // Skip |
220 | } else { |
221 | builder.addClassInfo(name: info.name(), value: info.value()); |
222 | } |
223 | } |
224 | |
225 | // Clone Q_PROPERTY |
226 | for (int ii = mo->propertyOffset(); ii < mo->propertyCount(); ++ii) { |
227 | QMetaProperty property = mo->property(index: ii); |
228 | |
229 | int otherIndex = ignoreEnd->indexOfProperty(name: property.name()); |
230 | if (otherIndex >= ignoreStart->propertyOffset() + ignoreStart->propertyCount()) { |
231 | builder.addProperty(name: QByteArray("__qml_ignore__" ) + property.name(), type: QByteArray("void" )); |
232 | // Skip |
233 | } else { |
234 | builder.addProperty(prototype: property); |
235 | } |
236 | } |
237 | |
238 | // Clone Q_METHODS |
239 | for (int ii = mo->methodOffset(); ii < mo->methodCount(); ++ii) { |
240 | QMetaMethod method = mo->method(index: ii); |
241 | |
242 | // More complex - need to search name |
243 | QByteArray name = method.name(); |
244 | |
245 | |
246 | bool found = false; |
247 | |
248 | for (int ii = ignoreStart->methodOffset() + ignoreStart->methodCount(); |
249 | !found && ii < ignoreEnd->methodOffset() + ignoreEnd->methodCount(); |
250 | ++ii) { |
251 | |
252 | QMetaMethod other = ignoreEnd->method(index: ii); |
253 | |
254 | found = name == other.name(); |
255 | } |
256 | |
257 | QMetaMethodBuilder m = builder.addMethod(prototype: method); |
258 | if (found) // SKIP |
259 | m.setAccess(QMetaMethod::Private); |
260 | } |
261 | |
262 | // Clone Q_ENUMS |
263 | for (int ii = mo->enumeratorOffset(); ii < mo->enumeratorCount(); ++ii) { |
264 | QMetaEnum enumerator = mo->enumerator(index: ii); |
265 | |
266 | int otherIndex = ignoreEnd->indexOfEnumerator(name: enumerator.name()); |
267 | if (otherIndex >= ignoreStart->enumeratorOffset() + ignoreStart->enumeratorCount()) { |
268 | // Skip |
269 | } else { |
270 | builder.addEnumerator(prototype: enumerator); |
271 | } |
272 | } |
273 | } |
274 | |
275 | void QQmlMetaType::qmlInsertModuleRegistration(const QString &uri, int majorVersion, |
276 | void (*registerFunction)()) |
277 | { |
278 | const QQmlMetaTypeData::VersionedUri versionedUri(uri, majorVersion); |
279 | QQmlMetaTypeDataPtr data; |
280 | if (data->moduleTypeRegistrationFunctions.contains(akey: versionedUri)) |
281 | qFatal(msg: "Cannot add multiple registrations for %s %d" , qPrintable(uri), majorVersion); |
282 | else |
283 | data->moduleTypeRegistrationFunctions.insert(akey: versionedUri, avalue: registerFunction); |
284 | } |
285 | |
286 | void QQmlMetaType::qmlRemoveModuleRegistration(const QString &uri, int majorVersion) |
287 | { |
288 | const QQmlMetaTypeData::VersionedUri versionedUri(uri, majorVersion); |
289 | QQmlMetaTypeDataPtr data; |
290 | |
291 | if (!data.isValid()) |
292 | return; // shutdown/deletion race. Not a problem. |
293 | |
294 | if (!data->moduleTypeRegistrationFunctions.contains(akey: versionedUri)) |
295 | qFatal(msg: "Cannot remove multiple registrations for %s %d" , qPrintable(uri), majorVersion); |
296 | else |
297 | data->moduleTypeRegistrationFunctions.remove(akey: versionedUri); |
298 | } |
299 | |
300 | bool QQmlMetaType::qmlRegisterModuleTypes(const QString &uri, int majorVersion) |
301 | { |
302 | QQmlMetaTypeDataPtr data; |
303 | return data->registerModuleTypes(versionedUri: QQmlMetaTypeData::VersionedUri(uri, majorVersion)); |
304 | } |
305 | |
306 | /*! |
307 | \internal |
308 | Method is only used to in tst_qqmlenginecleanup.cpp to test whether all |
309 | types have been removed from qmlLists after shutdown of QQmlEngine |
310 | */ |
311 | int QQmlMetaType::qmlRegisteredListTypeCount() |
312 | { |
313 | QQmlMetaTypeDataPtr data; |
314 | return data->qmlLists.count(); |
315 | } |
316 | |
317 | void QQmlMetaType::clearTypeRegistrations() |
318 | { |
319 | //Only cleans global static, assumed no running engine |
320 | QQmlMetaTypeDataPtr data; |
321 | |
322 | for (QQmlMetaTypeData::TypeModules::const_iterator i = data->uriToModule.constBegin(), cend = data->uriToModule.constEnd(); i != cend; ++i) |
323 | delete *i; |
324 | |
325 | data->types.clear(); |
326 | data->idToType.clear(); |
327 | data->nameToType.clear(); |
328 | data->urlToType.clear(); |
329 | data->typePropertyCaches.clear(); |
330 | data->urlToNonFileImportType.clear(); |
331 | data->metaObjectToType.clear(); |
332 | data->uriToModule.clear(); |
333 | data->undeletableTypes.clear(); |
334 | } |
335 | |
336 | int QQmlMetaType::registerAutoParentFunction(const QQmlPrivate::RegisterAutoParent &autoparent) |
337 | { |
338 | QQmlMetaTypeDataPtr data; |
339 | |
340 | data->parentFunctions.append(t: autoparent.function); |
341 | |
342 | return data->parentFunctions.count() - 1; |
343 | } |
344 | |
345 | void QQmlMetaType::unregisterAutoParentFunction(const QQmlPrivate::AutoParentFunction &function) |
346 | { |
347 | QQmlMetaTypeDataPtr data; |
348 | data->parentFunctions.removeOne(t: function); |
349 | } |
350 | |
351 | QQmlType QQmlMetaType::registerInterface(const QQmlPrivate::RegisterInterface &type) |
352 | { |
353 | if (type.version > 1) |
354 | qFatal(msg: "qmlRegisterType(): Cannot mix incompatible QML versions." ); |
355 | |
356 | QQmlMetaTypeDataPtr data; |
357 | QQmlTypePrivate *priv = createQQmlType(data, type); |
358 | Q_ASSERT(priv); |
359 | |
360 | data->idToType.insert(akey: priv->typeId, avalue: priv); |
361 | data->idToType.insert(akey: priv->listId, avalue: priv); |
362 | |
363 | if (data->interfaces.size() <= type.typeId) |
364 | data->interfaces.resize(size: type.typeId + 16); |
365 | if (data->lists.size() <= type.listId) |
366 | data->lists.resize(size: type.listId + 16); |
367 | data->interfaces.setBit(i: type.typeId, val: true); |
368 | data->lists.setBit(i: type.listId, val: true); |
369 | |
370 | return QQmlType(priv); |
371 | } |
372 | |
373 | QString registrationTypeString(QQmlType::RegistrationType typeType) |
374 | { |
375 | QString typeStr; |
376 | if (typeType == QQmlType::CppType) |
377 | typeStr = QStringLiteral("element" ); |
378 | else if (typeType == QQmlType::SingletonType) |
379 | typeStr = QStringLiteral("singleton type" ); |
380 | else if (typeType == QQmlType::CompositeSingletonType) |
381 | typeStr = QStringLiteral("composite singleton type" ); |
382 | else |
383 | typeStr = QStringLiteral("type" ); |
384 | return typeStr; |
385 | } |
386 | |
387 | // NOTE: caller must hold a QMutexLocker on "data" |
388 | bool checkRegistration(QQmlType::RegistrationType typeType, QQmlMetaTypeData *data, |
389 | const char *uri, const QString &typeName, int majorVersion) |
390 | { |
391 | if (!typeName.isEmpty()) { |
392 | if (typeName.at(i: 0).isLower()) { |
393 | QString failure(QCoreApplication::translate(context: "qmlRegisterType" , key: "Invalid QML %1 name \"%2\"; type names must begin with an uppercase letter" )); |
394 | data->recordTypeRegFailure(message: failure.arg(a: registrationTypeString(typeType)).arg(a: typeName)); |
395 | return false; |
396 | } |
397 | |
398 | int typeNameLen = typeName.length(); |
399 | for (int ii = 0; ii < typeNameLen; ++ii) { |
400 | if (!(typeName.at(i: ii).isLetterOrNumber() || typeName.at(i: ii) == '_')) { |
401 | QString failure(QCoreApplication::translate(context: "qmlRegisterType" , key: "Invalid QML %1 name \"%2\"" )); |
402 | data->recordTypeRegFailure(message: failure.arg(a: registrationTypeString(typeType)).arg(a: typeName)); |
403 | return false; |
404 | } |
405 | } |
406 | } |
407 | |
408 | if (uri && !typeName.isEmpty()) { |
409 | QString nameSpace = QString::fromUtf8(str: uri); |
410 | QQmlMetaTypeData::VersionedUri versionedUri; |
411 | versionedUri.uri = nameSpace; |
412 | versionedUri.majorVersion = majorVersion; |
413 | if (QQmlTypeModule* qqtm = data->uriToModule.value(akey: versionedUri, adefaultValue: 0)){ |
414 | if (qqtm->isLocked()){ |
415 | QString failure(QCoreApplication::translate(context: "qmlRegisterType" , |
416 | key: "Cannot install %1 '%2' into protected module '%3' version '%4'" )); |
417 | data->recordTypeRegFailure(message: failure.arg(a: registrationTypeString(typeType)).arg(a: typeName).arg(a: nameSpace).arg(a: majorVersion)); |
418 | return false; |
419 | } |
420 | } |
421 | } |
422 | |
423 | return true; |
424 | } |
425 | |
426 | // NOTE: caller must hold a QMutexLocker on "data" |
427 | QQmlTypeModule *getTypeModule(const QHashedString &uri, int majorVersion, QQmlMetaTypeData *data) |
428 | { |
429 | QQmlMetaTypeData::VersionedUri versionedUri(uri, majorVersion); |
430 | QQmlTypeModule *module = data->uriToModule.value(akey: versionedUri); |
431 | if (!module) { |
432 | module = new QQmlTypeModule(versionedUri.uri, versionedUri.majorVersion); |
433 | data->uriToModule.insert(akey: versionedUri, avalue: module); |
434 | } |
435 | return module; |
436 | } |
437 | |
438 | // NOTE: caller must hold a QMutexLocker on "data" |
439 | void addTypeToData(QQmlTypePrivate *type, QQmlMetaTypeData *data) |
440 | { |
441 | Q_ASSERT(type); |
442 | |
443 | if (!type->elementName.isEmpty()) |
444 | data->nameToType.insert(akey: type->elementName, avalue: type); |
445 | |
446 | if (type->baseMetaObject) |
447 | data->metaObjectToType.insert(akey: type->baseMetaObject, avalue: type); |
448 | |
449 | if (type->typeId) { |
450 | data->idToType.insert(akey: type->typeId, avalue: type); |
451 | if (data->objects.size() <= type->typeId) |
452 | data->objects.resize(size: type->typeId + 16); |
453 | data->objects.setBit(i: type->typeId, val: true); |
454 | } |
455 | |
456 | if (type->listId) { |
457 | if (data->lists.size() <= type->listId) |
458 | data->lists.resize(size: type->listId + 16); |
459 | data->lists.setBit(i: type->listId, val: true); |
460 | data->idToType.insert(akey: type->listId, avalue: type); |
461 | } |
462 | |
463 | if (!type->module.isEmpty()) { |
464 | const QHashedString &mod = type->module; |
465 | |
466 | QQmlTypeModule *module = getTypeModule(uri: mod, majorVersion: type->version_maj, data); |
467 | Q_ASSERT(module); |
468 | module->add(type); |
469 | } |
470 | } |
471 | |
472 | QQmlType QQmlMetaType::registerType(const QQmlPrivate::RegisterType &type) |
473 | { |
474 | QQmlMetaTypeDataPtr data; |
475 | |
476 | QString elementName = QString::fromUtf8(str: type.elementName); |
477 | if (!checkRegistration(typeType: QQmlType::CppType, data, uri: type.uri, typeName: elementName, majorVersion: type.versionMajor)) |
478 | return QQmlType(); |
479 | |
480 | QQmlTypePrivate *priv = createQQmlType(data, elementName, type); |
481 | |
482 | addTypeToData(type: priv, data); |
483 | if (!type.typeId) |
484 | data->idToType.insert(akey: priv->typeId, avalue: priv); |
485 | |
486 | return QQmlType(priv); |
487 | } |
488 | |
489 | QQmlType QQmlMetaType::registerSingletonType(const QQmlPrivate::RegisterSingletonType &type) |
490 | { |
491 | QQmlMetaTypeDataPtr data; |
492 | |
493 | QString typeName = QString::fromUtf8(str: type.typeName); |
494 | if (!checkRegistration(typeType: QQmlType::SingletonType, data, uri: type.uri, typeName, majorVersion: type.versionMajor)) |
495 | return QQmlType(); |
496 | |
497 | QQmlTypePrivate *priv = createQQmlType(data, elementName: typeName, type); |
498 | |
499 | addTypeToData(type: priv, data); |
500 | |
501 | return QQmlType(priv); |
502 | } |
503 | |
504 | QQmlType QQmlMetaType::registerCompositeSingletonType(const QQmlPrivate::RegisterCompositeSingletonType &type) |
505 | { |
506 | // Assumes URL is absolute and valid. Checking of user input should happen before the URL enters type. |
507 | QQmlMetaTypeDataPtr data; |
508 | |
509 | QString typeName = QString::fromUtf8(str: type.typeName); |
510 | bool fileImport = false; |
511 | if (*(type.uri) == '\0') |
512 | fileImport = true; |
513 | if (!checkRegistration(typeType: QQmlType::CompositeSingletonType, data, uri: fileImport ? nullptr : type.uri, |
514 | typeName, majorVersion: type.versionMajor)) { |
515 | return QQmlType(); |
516 | } |
517 | |
518 | QQmlTypePrivate *priv = createQQmlType(data, elementName: typeName, type); |
519 | addTypeToData(type: priv, data); |
520 | |
521 | QQmlMetaTypeData::Files *files = fileImport ? &(data->urlToType) : &(data->urlToNonFileImportType); |
522 | files->insert(akey: QQmlTypeLoader::normalize(unNormalizedUrl: type.url), avalue: priv); |
523 | |
524 | return QQmlType(priv); |
525 | } |
526 | |
527 | QQmlType QQmlMetaType::registerCompositeType(const QQmlPrivate::RegisterCompositeType &type) |
528 | { |
529 | // Assumes URL is absolute and valid. Checking of user input should happen before the URL enters type. |
530 | QQmlMetaTypeDataPtr data; |
531 | |
532 | QString typeName = QString::fromUtf8(str: type.typeName); |
533 | bool fileImport = false; |
534 | if (*(type.uri) == '\0') |
535 | fileImport = true; |
536 | if (!checkRegistration(typeType: QQmlType::CompositeType, data, uri: fileImport?nullptr:type.uri, typeName, majorVersion: type.versionMajor)) |
537 | return QQmlType(); |
538 | |
539 | QQmlTypePrivate *priv = createQQmlType(data, elementName: typeName, type); |
540 | addTypeToData(type: priv, data); |
541 | |
542 | QQmlMetaTypeData::Files *files = fileImport ? &(data->urlToType) : &(data->urlToNonFileImportType); |
543 | files->insert(akey: QQmlTypeLoader::normalize(unNormalizedUrl: type.url), avalue: priv); |
544 | |
545 | return QQmlType(priv); |
546 | } |
547 | |
548 | CompositeMetaTypeIds QQmlMetaType::registerInternalCompositeType(const QByteArray &className) |
549 | { |
550 | QByteArray ptr = className + '*'; |
551 | QByteArray lst = "QQmlListProperty<" + className + '>'; |
552 | |
553 | int ptr_type = QMetaType::registerNormalizedType(normalizedTypeName: ptr, |
554 | destructor: QtMetaTypePrivate::QMetaTypeFunctionHelper<QObject*>::Destruct, |
555 | constructor: QtMetaTypePrivate::QMetaTypeFunctionHelper<QObject*>::Construct, |
556 | size: sizeof(QObject*), |
557 | flags: static_cast<QFlags<QMetaType::TypeFlag> >(QtPrivate::QMetaTypeTypeFlags<QObject*>::Flags), |
558 | metaObject: nullptr); |
559 | int lst_type = QMetaType::registerNormalizedType(normalizedTypeName: lst, |
560 | destructor: QtMetaTypePrivate::QMetaTypeFunctionHelper<QQmlListProperty<QObject> >::Destruct, |
561 | constructor: QtMetaTypePrivate::QMetaTypeFunctionHelper<QQmlListProperty<QObject> >::Construct, |
562 | size: sizeof(QQmlListProperty<QObject>), |
563 | flags: static_cast<QFlags<QMetaType::TypeFlag> >(QtPrivate::QMetaTypeTypeFlags<QQmlListProperty<QObject> >::Flags), |
564 | metaObject: static_cast<QMetaObject*>(nullptr)); |
565 | |
566 | QQmlMetaTypeDataPtr data; |
567 | data->qmlLists.insert(akey: lst_type, avalue: ptr_type); |
568 | |
569 | return {ptr_type, lst_type}; |
570 | } |
571 | |
572 | void QQmlMetaType::unregisterInternalCompositeType(const CompositeMetaTypeIds &typeIds) |
573 | { |
574 | QQmlMetaTypeDataPtr data; |
575 | data->qmlLists.remove(akey: typeIds.listId); |
576 | |
577 | QMetaType::unregisterType(type: typeIds.id); |
578 | QMetaType::unregisterType(type: typeIds.listId); |
579 | } |
580 | |
581 | int QQmlMetaType::registerUnitCacheHook( |
582 | const QQmlPrivate::RegisterQmlUnitCacheHook &hookRegistration) |
583 | { |
584 | if (hookRegistration.version > 0) |
585 | qFatal(msg: "qmlRegisterType(): Cannot mix incompatible QML versions." ); |
586 | |
587 | QQmlMetaTypeDataPtr data; |
588 | data->lookupCachedQmlUnit << hookRegistration.lookupCachedQmlUnit; |
589 | return 0; |
590 | } |
591 | |
592 | bool QQmlMetaType::protectModule(const QString &uri, int majVersion) |
593 | { |
594 | QQmlMetaTypeDataPtr data; |
595 | |
596 | QQmlMetaTypeData::VersionedUri versionedUri; |
597 | versionedUri.uri = uri; |
598 | versionedUri.majorVersion = majVersion; |
599 | |
600 | if (QQmlTypeModule* qqtm = data->uriToModule.value(akey: versionedUri, adefaultValue: 0)) { |
601 | qqtm->lock(); |
602 | return true; |
603 | } |
604 | return false; |
605 | } |
606 | |
607 | void QQmlMetaType::registerModule(const char *uri, int versionMajor, int versionMinor) |
608 | { |
609 | QQmlMetaTypeDataPtr data; |
610 | |
611 | QQmlTypeModule *module = getTypeModule(uri: QString::fromUtf8(str: uri), majorVersion: versionMajor, data); |
612 | Q_ASSERT(module); |
613 | |
614 | module->addMinorVersion(minorVersion: versionMinor); |
615 | } |
616 | |
617 | int QQmlMetaType::typeId(const char *uri, int versionMajor, int versionMinor, const char *qmlName) |
618 | { |
619 | QQmlMetaTypeDataPtr data; |
620 | |
621 | QQmlTypeModule *module = getTypeModule(uri: QString::fromUtf8(str: uri), majorVersion: versionMajor, data); |
622 | if (!module) |
623 | return -1; |
624 | |
625 | QQmlType type = module->type(QHashedStringRef(QString::fromUtf8(str: qmlName)), versionMinor); |
626 | if (!type.isValid()) |
627 | return -1; |
628 | |
629 | return type.index(); |
630 | } |
631 | |
632 | void QQmlMetaType::registerUndeletableType(const QQmlType &dtype) |
633 | { |
634 | QQmlMetaTypeDataPtr data; |
635 | data->undeletableTypes.insert(value: dtype); |
636 | } |
637 | |
638 | static bool namespaceContainsRegistrations(const QQmlMetaTypeData *data, const QString &uri, |
639 | int majorVersion) |
640 | { |
641 | // Has any type previously been installed to this namespace? |
642 | QHashedString nameSpace(uri); |
643 | for (const QQmlType &type : data->types) { |
644 | if (type.module() == nameSpace && type.majorVersion() == majorVersion) |
645 | return true; |
646 | } |
647 | |
648 | return false; |
649 | } |
650 | |
651 | class QQmlMetaTypeRegistrationFailureRecorder |
652 | { |
653 | Q_DISABLE_COPY_MOVE(QQmlMetaTypeRegistrationFailureRecorder) |
654 | public: |
655 | QQmlMetaTypeRegistrationFailureRecorder(QQmlMetaTypeData *data, QStringList *failures) |
656 | : data(data) |
657 | { |
658 | data->setTypeRegistrationFailures(failures); |
659 | } |
660 | |
661 | ~QQmlMetaTypeRegistrationFailureRecorder() |
662 | { |
663 | data->setTypeRegistrationFailures(nullptr); |
664 | } |
665 | |
666 | QQmlMetaTypeData *data = nullptr; |
667 | }; |
668 | |
669 | |
670 | bool QQmlMetaType::registerPluginTypes(QObject *instance, const QString &basePath, |
671 | const QString &uri, const QString &typeNamespace, int vmaj, |
672 | QList<QQmlError> *errors) |
673 | { |
674 | if (!typeNamespace.isEmpty() && typeNamespace != uri) { |
675 | // This is an 'identified' module |
676 | // The namespace for type registrations must match the URI for locating the module |
677 | if (errors) { |
678 | QQmlError error; |
679 | error.setDescription( |
680 | QStringLiteral("Module namespace '%1' does not match import URI '%2'" ) |
681 | .arg(a: typeNamespace).arg(a: uri)); |
682 | errors->prepend(t: error); |
683 | } |
684 | return false; |
685 | } |
686 | |
687 | QStringList failures; |
688 | QQmlMetaTypeDataPtr data; |
689 | { |
690 | QQmlMetaTypeRegistrationFailureRecorder failureRecorder(data, &failures); |
691 | if (!typeNamespace.isEmpty()) { |
692 | // This is an 'identified' module |
693 | if (namespaceContainsRegistrations(data, uri: typeNamespace, majorVersion: vmaj)) { |
694 | // Other modules have already installed to this namespace |
695 | if (errors) { |
696 | QQmlError error; |
697 | error.setDescription(QStringLiteral("Namespace '%1' has already been used " |
698 | "for type registration" ) |
699 | .arg(a: typeNamespace)); |
700 | errors->prepend(t: error); |
701 | } |
702 | return false; |
703 | } |
704 | } else { |
705 | // This is not an identified module - provide a warning |
706 | qWarning().nospace() << qPrintable( |
707 | QStringLiteral("Module '%1' does not contain a module identifier directive - " |
708 | "it cannot be protected from external registrations." ).arg(uri)); |
709 | } |
710 | |
711 | if (!qobject_cast<QQmlEngineExtensionInterface *>(object: instance)) { |
712 | QQmlTypesExtensionInterface *iface = qobject_cast<QQmlTypesExtensionInterface *>(object: instance); |
713 | if (!iface) { |
714 | if (errors) { |
715 | QQmlError error; |
716 | // Also does not implement QQmlTypesExtensionInterface, but we want to discourage that. |
717 | error.setDescription(QStringLiteral("Module loaded for URI '%1' does not implement " |
718 | "QQmlEngineExtensionInterface" ).arg(a: typeNamespace)); |
719 | errors->prepend(t: error); |
720 | } |
721 | return false; |
722 | } |
723 | |
724 | if (auto *plugin = qobject_cast<QQmlExtensionPlugin *>(object: instance)) { |
725 | // basepath should point to the directory of the module, not the plugin file itself: |
726 | QQmlExtensionPluginPrivate::get(e: plugin)->baseUrl |
727 | = QQmlImports::urlFromLocalFileOrQrcOrUrl(basePath); |
728 | } |
729 | |
730 | const QByteArray bytes = uri.toUtf8(); |
731 | const char *moduleId = bytes.constData(); |
732 | iface->registerTypes(uri: moduleId); |
733 | } |
734 | |
735 | data->registerModuleTypes(versionedUri: QQmlMetaTypeData::VersionedUri(uri, vmaj)); |
736 | |
737 | if (!failures.isEmpty()) { |
738 | if (errors) { |
739 | for (const QString &failure : qAsConst(t&: failures)) { |
740 | QQmlError error; |
741 | error.setDescription(failure); |
742 | errors->prepend(t: error); |
743 | } |
744 | } |
745 | return false; |
746 | } |
747 | } |
748 | |
749 | return true; |
750 | } |
751 | |
752 | /* |
753 | \internal |
754 | |
755 | Fetches the QQmlType instance registered for \a urlString, creating a |
756 | registration for it if it is not already registered, using the associated |
757 | \a typeName, \a isCompositeSingleton, \a majorVersion and \a minorVersion |
758 | details. |
759 | |
760 | Errors (if there are any) are placed into \a errors, if it is nonzero. |
761 | Otherwise errors are printed as warnings. |
762 | */ |
763 | QQmlType QQmlMetaType::typeForUrl(const QString &urlString, |
764 | const QHashedStringRef &qualifiedType, |
765 | bool isCompositeSingleton, QList<QQmlError> *errors, |
766 | int majorVersion, int minorVersion) |
767 | { |
768 | // ### unfortunate (costly) conversion |
769 | const QUrl url = QQmlTypeLoader::normalize(unNormalizedUrl: QUrl(urlString)); |
770 | |
771 | QQmlMetaTypeDataPtr data; |
772 | { |
773 | QQmlType ret(data->urlToType.value(akey: url)); |
774 | if (ret.isValid() && ret.sourceUrl() == url) |
775 | return ret; |
776 | } |
777 | { |
778 | QQmlType ret(data->urlToNonFileImportType.value(akey: url)); |
779 | if (ret.isValid() && ret.sourceUrl() == url) |
780 | return ret; |
781 | } |
782 | |
783 | const int dot = qualifiedType.indexOf(QLatin1Char('.')); |
784 | const QString typeName = dot < 0 |
785 | ? qualifiedType.toString() |
786 | : QString(qualifiedType.constData() + dot + 1, qualifiedType.length() - dot - 1); |
787 | |
788 | QStringList failures; |
789 | QQmlMetaTypeRegistrationFailureRecorder failureRecorder(data, &failures); |
790 | |
791 | // Register the type. Note that the URI parameters here are empty; for |
792 | // file type imports, we do not place them in a URI as we don't |
793 | // necessarily have a good and unique one (picture a library import, |
794 | // which may be found in multiple plugin locations on disk), but there |
795 | // are other reasons for this too. |
796 | // |
797 | // By not putting them in a URI, we prevent the types from being |
798 | // registered on a QQmlTypeModule; this is important, as once types are |
799 | // placed on there, they cannot be easily removed, meaning if the |
800 | // developer subsequently loads a different import (meaning different |
801 | // types) with the same URI (using, say, a different plugin path), it is |
802 | // very undesirable that we continue to associate the types from the |
803 | // "old" URI with that new module. |
804 | // |
805 | // Not having URIs also means that the types cannot be found by name |
806 | // etc, the only way to look them up is through QQmlImports -- for |
807 | // better or worse. |
808 | const QQmlType::RegistrationType registrationType = isCompositeSingleton |
809 | ? QQmlType::CompositeSingletonType |
810 | : QQmlType::CompositeType; |
811 | if (checkRegistration(typeType: registrationType, data, uri: nullptr, typeName, majorVersion)) { |
812 | auto *priv = new QQmlTypePrivate(registrationType); |
813 | priv->setName(uri: QString(), element: typeName); |
814 | priv->version_maj = majorVersion; |
815 | priv->version_min = minorVersion; |
816 | |
817 | if (isCompositeSingleton) { |
818 | priv->extraData.sd->singletonInstanceInfo = new QQmlType::SingletonInstanceInfo; |
819 | priv->extraData.sd->singletonInstanceInfo->url = url; |
820 | priv->extraData.sd->singletonInstanceInfo->typeName = typeName; |
821 | } else { |
822 | priv->extraData.fd->url = url; |
823 | } |
824 | |
825 | data->registerType(priv); |
826 | addTypeToData(type: priv, data); |
827 | data->urlToType.insert(akey: url, avalue: priv); |
828 | return QQmlType(priv); |
829 | } |
830 | |
831 | // This means that the type couldn't be found by URL, but could not be |
832 | // registered either, meaning we most likely were passed some kind of bad |
833 | // data. |
834 | if (errors) { |
835 | QQmlError error; |
836 | error.setDescription(failures.join(sep: '\n')); |
837 | errors->prepend(t: error); |
838 | } else { |
839 | qWarning(msg: "%s" , failures.join(sep: '\n').toLatin1().constData()); |
840 | } |
841 | return QQmlType(); |
842 | } |
843 | |
844 | QRecursiveMutex *QQmlMetaType::typeRegistrationLock() |
845 | { |
846 | return metaTypeDataLock(); |
847 | } |
848 | |
849 | /* |
850 | Returns true if a module \a uri of any version is installed. |
851 | */ |
852 | bool QQmlMetaType::isAnyModule(const QString &uri) |
853 | { |
854 | QQmlMetaTypeDataPtr data; |
855 | |
856 | for (QQmlMetaTypeData::TypeModules::ConstIterator iter = data->uriToModule.cbegin(); |
857 | iter != data->uriToModule.cend(); ++iter) { |
858 | if ((*iter)->module() == uri) |
859 | return true; |
860 | } |
861 | |
862 | return false; |
863 | } |
864 | |
865 | /* |
866 | Returns true if a module \a uri of this version is installed and locked; |
867 | */ |
868 | bool QQmlMetaType::isLockedModule(const QString &uri, int majVersion) |
869 | { |
870 | QQmlMetaTypeDataPtr data; |
871 | |
872 | QQmlMetaTypeData::VersionedUri versionedUri; |
873 | versionedUri.uri = uri; |
874 | versionedUri.majorVersion = majVersion; |
875 | if (QQmlTypeModule* qqtm = data->uriToModule.value(akey: versionedUri, adefaultValue: 0)) |
876 | return qqtm->isLocked(); |
877 | return false; |
878 | } |
879 | |
880 | /* |
881 | Returns true if any type or API has been registered for the given \a module with at least |
882 | versionMajor.versionMinor, or if types have been registered for \a module with at most |
883 | versionMajor.versionMinor. |
884 | |
885 | So if only 4.7 and 4.9 have been registered, 4.7,4.8, and 4.9 are valid, but not 4.6 nor 4.10. |
886 | */ |
887 | bool QQmlMetaType::isModule(const QString &module, int versionMajor, int versionMinor) |
888 | { |
889 | Q_ASSERT(versionMajor >= 0 && versionMinor >= 0); |
890 | QQmlMetaTypeDataPtr data; |
891 | |
892 | // first, check Types |
893 | QQmlTypeModule *tm = |
894 | data->uriToModule.value(akey: QQmlMetaTypeData::VersionedUri(module, versionMajor)); |
895 | if (tm && tm->minimumMinorVersion() <= versionMinor && tm->maximumMinorVersion() >= versionMinor) |
896 | return true; |
897 | |
898 | return false; |
899 | } |
900 | |
901 | QQmlTypeModule *QQmlMetaType::typeModule(const QString &uri, int majorVersion) |
902 | { |
903 | QQmlMetaTypeDataPtr data; |
904 | return data->uriToModule.value(akey: QQmlMetaTypeData::VersionedUri(uri, majorVersion)); |
905 | } |
906 | |
907 | QList<QQmlPrivate::AutoParentFunction> QQmlMetaType::parentFunctions() |
908 | { |
909 | QQmlMetaTypeDataPtr data; |
910 | return data->parentFunctions; |
911 | } |
912 | |
913 | QObject *QQmlMetaType::toQObject(const QVariant &v, bool *ok) |
914 | { |
915 | if (!isQObject(v.userType())) { |
916 | if (ok) *ok = false; |
917 | return nullptr; |
918 | } |
919 | |
920 | if (ok) *ok = true; |
921 | |
922 | return *(QObject *const *)v.constData(); |
923 | } |
924 | |
925 | bool QQmlMetaType::isQObject(int userType) |
926 | { |
927 | if (userType == QMetaType::QObjectStar) |
928 | return true; |
929 | |
930 | QQmlMetaTypeDataPtr data; |
931 | return userType >= 0 && userType < data->objects.size() && data->objects.testBit(i: userType); |
932 | } |
933 | |
934 | /* |
935 | Returns the item type for a list of type \a id. |
936 | */ |
937 | int QQmlMetaType::listType(int id) |
938 | { |
939 | QQmlMetaTypeDataPtr data; |
940 | QHash<int, int>::ConstIterator iter = data->qmlLists.constFind(akey: id); |
941 | if (iter != data->qmlLists.cend()) |
942 | return *iter; |
943 | QQmlTypePrivate *type = data->idToType.value(akey: id); |
944 | if (type && type->listId == id) |
945 | return type->typeId; |
946 | else |
947 | return 0; |
948 | } |
949 | |
950 | #if QT_DEPRECATED_SINCE(5, 14) |
951 | int QQmlMetaType::attachedPropertiesFuncId(QQmlEnginePrivate *engine, const QMetaObject *mo) |
952 | { |
953 | QQmlMetaTypeDataPtr data; |
954 | |
955 | for (auto it = data->metaObjectToType.constFind(akey: mo), end = data->metaObjectToType.constEnd(); |
956 | it != end && it.key() == mo; ++it) { |
957 | if (const QQmlTypePrivate *type = it.value()) { |
958 | if (const QQmlTypePrivate *base = type->attachedPropertiesBase(engine)) |
959 | return base->index; |
960 | } |
961 | } |
962 | return -1; |
963 | } |
964 | |
965 | QQmlAttachedPropertiesFunc QQmlMetaType::attachedPropertiesFuncById(QQmlEnginePrivate *engine, int id) |
966 | { |
967 | if (id < 0) |
968 | return nullptr; |
969 | QQmlMetaTypeDataPtr data; |
970 | return data->types.at(i: id).attachedPropertiesFunction(engine); |
971 | } |
972 | #endif |
973 | |
974 | QQmlAttachedPropertiesFunc QQmlMetaType::attachedPropertiesFunc(QQmlEnginePrivate *engine, |
975 | const QMetaObject *mo) |
976 | { |
977 | QQmlMetaTypeDataPtr data; |
978 | |
979 | QQmlType type(data->metaObjectToType.value(akey: mo)); |
980 | return type.attachedPropertiesFunction(engine); |
981 | } |
982 | |
983 | QMetaProperty QQmlMetaType::defaultProperty(const QMetaObject *metaObject) |
984 | { |
985 | int idx = metaObject->indexOfClassInfo(name: "DefaultProperty" ); |
986 | if (-1 == idx) |
987 | return QMetaProperty(); |
988 | |
989 | QMetaClassInfo info = metaObject->classInfo(index: idx); |
990 | if (!info.value()) |
991 | return QMetaProperty(); |
992 | |
993 | idx = metaObject->indexOfProperty(name: info.value()); |
994 | if (-1 == idx) |
995 | return QMetaProperty(); |
996 | |
997 | return metaObject->property(index: idx); |
998 | } |
999 | |
1000 | QMetaProperty QQmlMetaType::defaultProperty(QObject *obj) |
1001 | { |
1002 | if (!obj) |
1003 | return QMetaProperty(); |
1004 | |
1005 | const QMetaObject *metaObject = obj->metaObject(); |
1006 | return defaultProperty(metaObject); |
1007 | } |
1008 | |
1009 | QMetaMethod QQmlMetaType::defaultMethod(const QMetaObject *metaObject) |
1010 | { |
1011 | int idx = metaObject->indexOfClassInfo(name: "DefaultMethod" ); |
1012 | if (-1 == idx) |
1013 | return QMetaMethod(); |
1014 | |
1015 | QMetaClassInfo info = metaObject->classInfo(index: idx); |
1016 | if (!info.value()) |
1017 | return QMetaMethod(); |
1018 | |
1019 | idx = metaObject->indexOfMethod(method: info.value()); |
1020 | if (-1 == idx) |
1021 | return QMetaMethod(); |
1022 | |
1023 | return metaObject->method(index: idx); |
1024 | } |
1025 | |
1026 | QMetaMethod QQmlMetaType::defaultMethod(QObject *obj) |
1027 | { |
1028 | if (!obj) |
1029 | return QMetaMethod(); |
1030 | |
1031 | const QMetaObject *metaObject = obj->metaObject(); |
1032 | return defaultMethod(metaObject); |
1033 | } |
1034 | |
1035 | QQmlMetaType::TypeCategory QQmlMetaType::typeCategory(int userType) |
1036 | { |
1037 | if (userType < 0) |
1038 | return Unknown; |
1039 | if (userType == QMetaType::QObjectStar) |
1040 | return Object; |
1041 | |
1042 | QQmlMetaTypeDataPtr data; |
1043 | if (data->qmlLists.contains(akey: userType)) |
1044 | return List; |
1045 | else if (userType < data->objects.size() && data->objects.testBit(i: userType)) |
1046 | return Object; |
1047 | else if (userType < data->lists.size() && data->lists.testBit(i: userType)) |
1048 | return List; |
1049 | else |
1050 | return Unknown; |
1051 | } |
1052 | |
1053 | /*! |
1054 | See qmlRegisterInterface() for information about when this will return true. |
1055 | */ |
1056 | bool QQmlMetaType::isInterface(int userType) |
1057 | { |
1058 | const QQmlMetaTypeDataPtr data; |
1059 | return userType >= 0 && userType < data->interfaces.size() && data->interfaces.testBit(i: userType); |
1060 | } |
1061 | |
1062 | const char *QQmlMetaType::interfaceIId(int userType) |
1063 | { |
1064 | |
1065 | QQmlTypePrivate *typePrivate = nullptr; |
1066 | { |
1067 | QQmlMetaTypeDataPtr data; |
1068 | typePrivate = data->idToType.value(akey: userType); |
1069 | } |
1070 | |
1071 | QQmlType type(typePrivate); |
1072 | if (type.isInterface() && type.typeId() == userType) |
1073 | return type.interfaceIId(); |
1074 | else |
1075 | return nullptr; |
1076 | } |
1077 | |
1078 | bool QQmlMetaType::isList(int userType) |
1079 | { |
1080 | const QQmlMetaTypeDataPtr data; |
1081 | if (data->qmlLists.contains(akey: userType)) |
1082 | return true; |
1083 | return userType >= 0 && userType < data->lists.size() && data->lists.testBit(i: userType); |
1084 | } |
1085 | |
1086 | /*! |
1087 | A custom string convertor allows you to specify a function pointer that |
1088 | returns a variant of \a type. For example, if you have written your own icon |
1089 | class that you want to support as an object property assignable in QML: |
1090 | |
1091 | \code |
1092 | int type = qRegisterMetaType<SuperIcon>("SuperIcon"); |
1093 | QML::addCustomStringConvertor(type, &SuperIcon::pixmapFromString); |
1094 | \endcode |
1095 | |
1096 | The function pointer must be of the form: |
1097 | \code |
1098 | QVariant (*StringConverter)(const QString &); |
1099 | \endcode |
1100 | */ |
1101 | void QQmlMetaType::registerCustomStringConverter(int type, StringConverter converter) |
1102 | { |
1103 | QQmlMetaTypeDataPtr data; |
1104 | if (data->stringConverters.contains(akey: type)) |
1105 | return; |
1106 | data->stringConverters.insert(akey: type, avalue: converter); |
1107 | } |
1108 | |
1109 | /*! |
1110 | Return the custom string converter for \a type, previously installed through |
1111 | registerCustomStringConverter() |
1112 | */ |
1113 | QQmlMetaType::StringConverter QQmlMetaType::customStringConverter(int type) |
1114 | { |
1115 | const QQmlMetaTypeDataPtr data; |
1116 | return data->stringConverters.value(akey: type); |
1117 | } |
1118 | |
1119 | /*! |
1120 | Returns the type (if any) of URI-qualified named \a qualifiedName and version specified |
1121 | by \a version_major and \a version_minor. |
1122 | */ |
1123 | QQmlType QQmlMetaType::qmlType(const QString &qualifiedName, int version_major, int version_minor) |
1124 | { |
1125 | int slash = qualifiedName.indexOf(c: QLatin1Char('/')); |
1126 | if (slash <= 0) |
1127 | return QQmlType(); |
1128 | |
1129 | QHashedStringRef module(qualifiedName.constData(), slash); |
1130 | QHashedStringRef name(qualifiedName.constData() + slash + 1, qualifiedName.length() - slash - 1); |
1131 | |
1132 | return qmlType(name, module, version_major, version_minor); |
1133 | } |
1134 | |
1135 | /*! |
1136 | Returns the type (if any) of \a name in \a module and version specified |
1137 | by \a version_major and \a version_minor. |
1138 | */ |
1139 | QQmlType QQmlMetaType::qmlType(const QHashedStringRef &name, const QHashedStringRef &module, int version_major, int version_minor) |
1140 | { |
1141 | Q_ASSERT(version_major >= 0 && version_minor >= 0); |
1142 | const QQmlMetaTypeDataPtr data; |
1143 | |
1144 | QQmlMetaTypeData::Names::ConstIterator it = data->nameToType.constFind(akey: name); |
1145 | while (it != data->nameToType.cend() && it.key() == name) { |
1146 | QQmlType t(*it); |
1147 | // XXX version_major<0 just a kludge for QQmlPropertyPrivate::initProperty |
1148 | if (version_major < 0 || module.isEmpty() || t.availableInVersion(module, vmajor: version_major,vminor: version_minor)) |
1149 | return t; |
1150 | ++it; |
1151 | } |
1152 | |
1153 | return QQmlType(); |
1154 | } |
1155 | |
1156 | /*! |
1157 | Returns the type (if any) that corresponds to the \a metaObject. Returns null if no |
1158 | type is registered. |
1159 | */ |
1160 | QQmlType QQmlMetaType::qmlType(const QMetaObject *metaObject) |
1161 | { |
1162 | const QQmlMetaTypeDataPtr data; |
1163 | return QQmlType(data->metaObjectToType.value(akey: metaObject)); |
1164 | } |
1165 | |
1166 | /*! |
1167 | Returns the type (if any) that corresponds to the \a metaObject in version specified |
1168 | by \a version_major and \a version_minor in module specified by \a uri. Returns null if no |
1169 | type is registered. |
1170 | */ |
1171 | QQmlType QQmlMetaType::qmlType(const QMetaObject *metaObject, const QHashedStringRef &module, int version_major, int version_minor) |
1172 | { |
1173 | Q_ASSERT(version_major >= 0 && version_minor >= 0); |
1174 | const QQmlMetaTypeDataPtr data; |
1175 | |
1176 | QQmlMetaTypeData::MetaObjects::const_iterator it = data->metaObjectToType.constFind(akey: metaObject); |
1177 | while (it != data->metaObjectToType.cend() && it.key() == metaObject) { |
1178 | QQmlType t(*it); |
1179 | if (version_major < 0 || module.isEmpty() || t.availableInVersion(module, vmajor: version_major,vminor: version_minor)) |
1180 | return t; |
1181 | ++it; |
1182 | } |
1183 | |
1184 | return QQmlType(); |
1185 | } |
1186 | |
1187 | /*! |
1188 | Returns the type (if any) that corresponds to \a typeId. Depending on \a category, the |
1189 | \a typeId is interpreted either as QVariant::Type or as QML type id returned by one of the |
1190 | qml type registration functions. Returns null if no type is registered. |
1191 | */ |
1192 | QQmlType QQmlMetaType::qmlType(int typeId, TypeIdCategory category) |
1193 | { |
1194 | const QQmlMetaTypeDataPtr data; |
1195 | |
1196 | if (category == TypeIdCategory::MetaType) { |
1197 | QQmlTypePrivate *type = data->idToType.value(akey: typeId); |
1198 | if (type && type->typeId == typeId) |
1199 | return QQmlType(type); |
1200 | } else if (category == TypeIdCategory::QmlType) { |
1201 | QQmlType type = data->types.value(i: typeId); |
1202 | if (type.isValid()) |
1203 | return type; |
1204 | } |
1205 | return QQmlType(); |
1206 | } |
1207 | |
1208 | /*! |
1209 | Returns the type (if any) that corresponds to the given \a url in the set of |
1210 | composite types added through file imports. |
1211 | |
1212 | Returns null if no such type is registered. |
1213 | */ |
1214 | QQmlType QQmlMetaType::qmlType(const QUrl &unNormalizedUrl, bool includeNonFileImports /* = false */) |
1215 | { |
1216 | const QUrl url = QQmlTypeLoader::normalize(unNormalizedUrl); |
1217 | const QQmlMetaTypeDataPtr data; |
1218 | |
1219 | QQmlType type(data->urlToType.value(akey: url)); |
1220 | if (!type.isValid() && includeNonFileImports) |
1221 | type = QQmlType(data->urlToNonFileImportType.value(akey: url)); |
1222 | |
1223 | if (type.sourceUrl() == url) |
1224 | return type; |
1225 | else |
1226 | return QQmlType(); |
1227 | } |
1228 | |
1229 | QQmlPropertyCache *QQmlMetaType::propertyCache(const QMetaObject *metaObject, int minorVersion, bool doRef) |
1230 | { |
1231 | QQmlMetaTypeDataPtr data; // not const: the cache is created on demand |
1232 | auto ret = data->propertyCache(metaObject, minorVersion); |
1233 | if (doRef) |
1234 | return ret.take(); |
1235 | else |
1236 | return ret.data(); |
1237 | } |
1238 | |
1239 | QQmlPropertyCache *QQmlMetaType::propertyCache(const QQmlType &type, int minorVersion) |
1240 | { |
1241 | QQmlMetaTypeDataPtr data; // not const: the cache is created on demand |
1242 | return data->propertyCache(type, minorVersion); |
1243 | } |
1244 | |
1245 | void QQmlMetaType::unregisterType(int typeIndex) |
1246 | { |
1247 | QQmlMetaTypeDataPtr data; |
1248 | const QQmlType type = data->types.value(i: typeIndex); |
1249 | if (const QQmlTypePrivate *d = type.priv()) { |
1250 | removeQQmlTypePrivate(container&: data->idToType, reference: d); |
1251 | removeQQmlTypePrivate(container&: data->nameToType, reference: d); |
1252 | removeQQmlTypePrivate(container&: data->urlToType, reference: d); |
1253 | removeQQmlTypePrivate(container&: data->urlToNonFileImportType, reference: d); |
1254 | removeQQmlTypePrivate(container&: data->metaObjectToType, reference: d); |
1255 | for (auto & module : data->uriToModule) |
1256 | module->remove(type: d); |
1257 | data->clearPropertyCachesForMinorVersion(index: typeIndex); |
1258 | data->types[typeIndex] = QQmlType(); |
1259 | data->undeletableTypes.remove(value: type); |
1260 | } |
1261 | } |
1262 | |
1263 | static bool hasActiveInlineComponents(const QQmlTypePrivate *d) |
1264 | { |
1265 | for (const QQmlType &ic : qAsConst(t&: d->objectIdToICType)) { |
1266 | const QQmlTypePrivate *icPriv = ic.priv(); |
1267 | if (icPriv && icPriv->count() > 1) |
1268 | return true; |
1269 | } |
1270 | return false; |
1271 | } |
1272 | |
1273 | void QQmlMetaType::freeUnusedTypesAndCaches() |
1274 | { |
1275 | QQmlMetaTypeDataPtr data; |
1276 | |
1277 | // in case this is being called during program exit, `data` might be destructed already |
1278 | if (!data.isValid()) |
1279 | return; |
1280 | |
1281 | bool deletedAtLeastOneType; |
1282 | do { |
1283 | deletedAtLeastOneType = false; |
1284 | QList<QQmlType>::Iterator it = data->types.begin(); |
1285 | while (it != data->types.end()) { |
1286 | const QQmlTypePrivate *d = (*it).priv(); |
1287 | if (d && d->count() == 1 && !hasActiveInlineComponents(d)) { |
1288 | deletedAtLeastOneType = true; |
1289 | |
1290 | removeQQmlTypePrivate(container&: data->idToType, reference: d); |
1291 | removeQQmlTypePrivate(container&: data->nameToType, reference: d); |
1292 | removeQQmlTypePrivate(container&: data->urlToType, reference: d); |
1293 | removeQQmlTypePrivate(container&: data->urlToNonFileImportType, reference: d); |
1294 | removeQQmlTypePrivate(container&: data->metaObjectToType, reference: d); |
1295 | |
1296 | for (auto &module : data->uriToModule) |
1297 | module->remove(type: d); |
1298 | |
1299 | data->clearPropertyCachesForMinorVersion(index: d->index); |
1300 | *it = QQmlType(); |
1301 | } else { |
1302 | ++it; |
1303 | } |
1304 | } |
1305 | } while (deletedAtLeastOneType); |
1306 | |
1307 | bool deletedAtLeastOneCache; |
1308 | do { |
1309 | deletedAtLeastOneCache = false; |
1310 | QHash<const QMetaObject *, QQmlPropertyCache *>::Iterator it = data->propertyCaches.begin(); |
1311 | while (it != data->propertyCaches.end()) { |
1312 | |
1313 | if ((*it)->count() == 1) { |
1314 | QQmlPropertyCache *pc = nullptr; |
1315 | qSwap(value1&: pc, value2&: *it); |
1316 | it = data->propertyCaches.erase(it); |
1317 | pc->release(); |
1318 | deletedAtLeastOneCache = true; |
1319 | } else { |
1320 | ++it; |
1321 | } |
1322 | } |
1323 | } while (deletedAtLeastOneCache); |
1324 | } |
1325 | |
1326 | /*! |
1327 | Returns the list of registered QML type names. |
1328 | */ |
1329 | QList<QString> QQmlMetaType::qmlTypeNames() |
1330 | { |
1331 | const QQmlMetaTypeDataPtr data; |
1332 | |
1333 | QList<QString> names; |
1334 | names.reserve(alloc: data->nameToType.count()); |
1335 | QQmlMetaTypeData::Names::ConstIterator it = data->nameToType.cbegin(); |
1336 | while (it != data->nameToType.cend()) { |
1337 | QQmlType t(*it); |
1338 | names += t.qmlTypeName(); |
1339 | ++it; |
1340 | } |
1341 | |
1342 | return names; |
1343 | } |
1344 | |
1345 | /*! |
1346 | Returns the list of registered QML types. |
1347 | */ |
1348 | QList<QQmlType> QQmlMetaType::qmlTypes() |
1349 | { |
1350 | const QQmlMetaTypeDataPtr data; |
1351 | |
1352 | QList<QQmlType> types; |
1353 | for (QQmlTypePrivate *t : data->nameToType) |
1354 | types.append(t: QQmlType(t)); |
1355 | |
1356 | return types; |
1357 | } |
1358 | |
1359 | /*! |
1360 | Returns the list of all registered types. |
1361 | */ |
1362 | QList<QQmlType> QQmlMetaType::qmlAllTypes() |
1363 | { |
1364 | const QQmlMetaTypeDataPtr data; |
1365 | return data->types; |
1366 | } |
1367 | |
1368 | /*! |
1369 | Returns the list of registered QML singleton types. |
1370 | */ |
1371 | QList<QQmlType> QQmlMetaType::qmlSingletonTypes() |
1372 | { |
1373 | const QQmlMetaTypeDataPtr data; |
1374 | |
1375 | QList<QQmlType> retn; |
1376 | for (const auto t : qAsConst(t: data->nameToType)) { |
1377 | QQmlType type(t); |
1378 | if (type.isSingleton()) |
1379 | retn.append(t: type); |
1380 | } |
1381 | return retn; |
1382 | } |
1383 | |
1384 | const QV4::CompiledData::Unit *QQmlMetaType::findCachedCompilationUnit(const QUrl &uri, CachedUnitLookupError *status) |
1385 | { |
1386 | const QQmlMetaTypeDataPtr data; |
1387 | |
1388 | for (const auto lookup : qAsConst(t: data->lookupCachedQmlUnit)) { |
1389 | if (const QQmlPrivate::CachedQmlUnit *unit = lookup(uri)) { |
1390 | QString error; |
1391 | if (!QV4::ExecutableCompilationUnit::verifyHeader(unit: unit->qmlData, expectedSourceTimeStamp: QDateTime(), errorString: &error)) { |
1392 | qCDebug(DBG_DISK_CACHE) << "Error loading pre-compiled file " << uri << ":" << error; |
1393 | if (status) |
1394 | *status = CachedUnitLookupError::VersionMismatch; |
1395 | return nullptr; |
1396 | } |
1397 | if (status) |
1398 | *status = CachedUnitLookupError::NoError; |
1399 | return unit->qmlData; |
1400 | } |
1401 | } |
1402 | |
1403 | if (status) |
1404 | *status = CachedUnitLookupError::NoUnitFound; |
1405 | |
1406 | return nullptr; |
1407 | } |
1408 | |
1409 | void QQmlMetaType::prependCachedUnitLookupFunction(QQmlPrivate::QmlUnitCacheLookupFunction handler) |
1410 | { |
1411 | QQmlMetaTypeDataPtr data; |
1412 | data->lookupCachedQmlUnit.prepend(t: handler); |
1413 | } |
1414 | |
1415 | void QQmlMetaType::removeCachedUnitLookupFunction(QQmlPrivate::QmlUnitCacheLookupFunction handler) |
1416 | { |
1417 | QQmlMetaTypeDataPtr data; |
1418 | data->lookupCachedQmlUnit.removeAll(t: handler); |
1419 | } |
1420 | |
1421 | /*! |
1422 | Returns the pretty QML type name (e.g. 'Item' instead of 'QtQuickItem') for the given object. |
1423 | */ |
1424 | QString QQmlMetaType::prettyTypeName(const QObject *object) |
1425 | { |
1426 | QString typeName; |
1427 | |
1428 | if (!object) |
1429 | return typeName; |
1430 | |
1431 | QQmlType type = QQmlMetaType::qmlType(metaObject: object->metaObject()); |
1432 | if (type.isValid()) { |
1433 | typeName = type.qmlTypeName(); |
1434 | const int lastSlash = typeName.lastIndexOf(c: QLatin1Char('/')); |
1435 | if (lastSlash != -1) |
1436 | typeName = typeName.mid(position: lastSlash + 1); |
1437 | } |
1438 | |
1439 | if (typeName.isEmpty()) { |
1440 | typeName = QString::fromUtf8(str: object->metaObject()->className()); |
1441 | int marker = typeName.indexOf(s: QLatin1String("_QMLTYPE_" )); |
1442 | if (marker != -1) |
1443 | typeName = typeName.left(n: marker); |
1444 | |
1445 | marker = typeName.indexOf(s: QLatin1String("_QML_" )); |
1446 | if (marker != -1) { |
1447 | typeName = typeName.leftRef(n: marker) + QLatin1Char('*'); |
1448 | type = QQmlMetaType::qmlType(typeId: QMetaType::type(typeName: typeName.toLatin1())); |
1449 | if (type.isValid()) { |
1450 | QString qmlTypeName = type.qmlTypeName(); |
1451 | const int lastSlash = qmlTypeName.lastIndexOf(c: QLatin1Char('/')); |
1452 | if (lastSlash != -1) |
1453 | qmlTypeName = qmlTypeName.mid(position: lastSlash + 1); |
1454 | if (!qmlTypeName.isEmpty()) |
1455 | typeName = qmlTypeName; |
1456 | } |
1457 | } |
1458 | } |
1459 | |
1460 | return typeName; |
1461 | } |
1462 | |
1463 | QList<QQmlProxyMetaObject::ProxyData> QQmlMetaType::proxyData(const QMetaObject *mo, |
1464 | const QMetaObject *baseMetaObject, |
1465 | QMetaObject *lastMetaObject) |
1466 | { |
1467 | QList<QQmlProxyMetaObject::ProxyData> metaObjects; |
1468 | mo = mo->d.superdata; |
1469 | |
1470 | const QQmlMetaTypeDataPtr data; |
1471 | |
1472 | while (mo) { |
1473 | QQmlTypePrivate *t = data->metaObjectToType.value(akey: mo); |
1474 | if (t) { |
1475 | if (t->regType == QQmlType::CppType) { |
1476 | if (t->extraData.cd->extFunc) { |
1477 | QMetaObjectBuilder builder; |
1478 | clone(builder, mo: t->extraData.cd->extMetaObject, ignoreStart: t->baseMetaObject, ignoreEnd: baseMetaObject); |
1479 | builder.setFlags(QMetaObjectBuilder::DynamicMetaObject); |
1480 | QMetaObject *mmo = builder.toMetaObject(); |
1481 | mmo->d.superdata = baseMetaObject; |
1482 | if (!metaObjects.isEmpty()) |
1483 | metaObjects.constLast().metaObject->d.superdata = mmo; |
1484 | else if (lastMetaObject) |
1485 | lastMetaObject->d.superdata = mmo; |
1486 | QQmlProxyMetaObject::ProxyData data = { .metaObject: mmo, .createFunc: t->extraData.cd->extFunc, .propertyOffset: 0, .methodOffset: 0 }; |
1487 | metaObjects << data; |
1488 | } |
1489 | } |
1490 | } |
1491 | mo = mo->d.superdata; |
1492 | } |
1493 | |
1494 | return metaObjects; |
1495 | } |
1496 | |
1497 | QT_END_NAMESPACE |
1498 | |