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