1 | // Copyright (C) 2016 The Qt Company Ltd. |
2 | // SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only |
3 | |
4 | #include "qqmlmetatype_p.h" |
5 | |
6 | #include <private/qqmlmetatypedata_p.h> |
7 | #include <private/qqmltypemodule_p.h> |
8 | #include <private/qqmltype_p_p.h> |
9 | #include <private/qqmltypeloader_p.h> |
10 | #include <private/qqmlextensionplugin_p.h> |
11 | #include <private/qqmlvaluetype_p.h> |
12 | #include <private/qv4executablecompilationunit_p.h> |
13 | |
14 | #include <QtCore/qcoreapplication.h> |
15 | #include <QtCore/qmutex.h> |
16 | #include <QtCore/qloggingcategory.h> |
17 | |
18 | Q_DECLARE_LOGGING_CATEGORY(DBG_DISK_CACHE) |
19 | Q_LOGGING_CATEGORY(lcTypeRegistration, "qt.qml.typeregistration" ) |
20 | |
21 | QT_BEGIN_NAMESPACE |
22 | |
23 | CompositeMetaTypeIds CompositeMetaTypeIds::fromCompositeName(const QByteArray &name) |
24 | { |
25 | auto ids = QQmlMetaType::registerInternalCompositeType(className: name); |
26 | ids.refCount = new int; |
27 | *ids.refCount = 1; |
28 | return ids; |
29 | } |
30 | |
31 | void CompositeMetaTypeIds::deref() |
32 | { |
33 | Q_ASSERT(refCount); |
34 | --*refCount; |
35 | if (!*refCount) { |
36 | delete refCount; |
37 | QQmlMetaType::unregisterInternalCompositeType(typeIds: *this); |
38 | refCount = nullptr; |
39 | } |
40 | } |
41 | |
42 | CompositeMetaTypeIds::~CompositeMetaTypeIds() |
43 | { |
44 | if (refCount) |
45 | deref(); |
46 | } |
47 | |
48 | |
49 | struct LockedData : private QQmlMetaTypeData |
50 | { |
51 | friend class QQmlMetaTypeDataPtr; |
52 | }; |
53 | |
54 | Q_GLOBAL_STATIC(LockedData, metaTypeData) |
55 | Q_GLOBAL_STATIC(QRecursiveMutex, metaTypeDataLock) |
56 | |
57 | struct ModuleUri : public QString |
58 | { |
59 | ModuleUri(const QString &string) : QString(string) {} |
60 | ModuleUri(const std::unique_ptr<QQmlTypeModule> &module) : QString(module->module()) {} |
61 | }; |
62 | |
63 | class QQmlMetaTypeDataPtr |
64 | { |
65 | Q_DISABLE_COPY_MOVE(QQmlMetaTypeDataPtr) |
66 | public: |
67 | QQmlMetaTypeDataPtr() : locker(metaTypeDataLock()), data(metaTypeData()) {} |
68 | ~QQmlMetaTypeDataPtr() = default; |
69 | |
70 | QQmlMetaTypeData &operator*() { return *data; } |
71 | QQmlMetaTypeData *operator->() { return data; } |
72 | operator QQmlMetaTypeData *() { return data; } |
73 | |
74 | const QQmlMetaTypeData &operator*() const { return *data; } |
75 | const QQmlMetaTypeData *operator->() const { return data; } |
76 | operator const QQmlMetaTypeData *() const { return data; } |
77 | |
78 | bool isValid() const { return data != nullptr; } |
79 | |
80 | private: |
81 | QMutexLocker<QRecursiveMutex> locker; |
82 | LockedData *data = nullptr; |
83 | }; |
84 | |
85 | static QQmlTypePrivate *createQQmlType(QQmlMetaTypeData *data, |
86 | const QQmlPrivate::RegisterInterface &type) |
87 | { |
88 | auto *d = new QQmlTypePrivate(QQmlType::InterfaceType); |
89 | d->iid = type.iid; |
90 | d->typeId = type.typeId; |
91 | d->listId = type.listId; |
92 | d->isSetup.storeRelease(newValue: true); |
93 | d->module = QString::fromUtf8(utf8: type.uri); |
94 | d->version = type.version; |
95 | data->registerType(priv: d); |
96 | return d; |
97 | } |
98 | |
99 | static QQmlTypePrivate *createQQmlType(QQmlMetaTypeData *data, const QString &elementName, |
100 | const QQmlPrivate::RegisterSingletonType &type) |
101 | { |
102 | auto *d = new QQmlTypePrivate(QQmlType::SingletonType); |
103 | data->registerType(priv: d); |
104 | |
105 | d->setName(uri: QString::fromUtf8(utf8: type.uri), element: elementName); |
106 | d->version = type.version; |
107 | |
108 | if (type.qObjectApi) { |
109 | d->baseMetaObject = type.instanceMetaObject; |
110 | d->typeId = type.typeId; |
111 | d->revision = type.revision; |
112 | } |
113 | |
114 | d->extraData.sd->singletonInstanceInfo = new QQmlType::SingletonInstanceInfo; |
115 | d->extraData.sd->singletonInstanceInfo->scriptCallback = type.scriptApi; |
116 | d->extraData.sd->singletonInstanceInfo->qobjectCallback = type.qObjectApi; |
117 | d->extraData.sd->singletonInstanceInfo->typeName = QString::fromUtf8(utf8: type.typeName); |
118 | d->extraData.sd->singletonInstanceInfo->instanceMetaObject |
119 | = type.qObjectApi ? type.instanceMetaObject : nullptr; |
120 | d->extraData.sd->extFunc = type.extensionObjectCreate; |
121 | d->extraData.sd->extMetaObject = type.extensionMetaObject; |
122 | |
123 | return d; |
124 | } |
125 | |
126 | static QQmlTypePrivate *createQQmlType(QQmlMetaTypeData *data, const QString &elementName, |
127 | const QQmlPrivate::RegisterType &type) |
128 | { |
129 | QQmlTypePrivate *d = new QQmlTypePrivate(QQmlType::CppType); |
130 | data->registerType(priv: d); |
131 | d->setName(uri: QString::fromUtf8(utf8: type.uri), element: elementName); |
132 | |
133 | d->version = type.version; |
134 | d->revision = type.revision; |
135 | d->typeId = type.typeId; |
136 | d->listId = type.listId; |
137 | d->extraData.cd->allocationSize = type.objectSize; |
138 | d->extraData.cd->userdata = type.userdata; |
139 | d->extraData.cd->newFunc = type.create; |
140 | d->extraData.cd->noCreationReason = type.noCreationReason; |
141 | d->extraData.cd->createValueTypeFunc = type.createValueType; |
142 | d->baseMetaObject = type.metaObject; |
143 | d->extraData.cd->attachedPropertiesFunc = type.attachedPropertiesFunction; |
144 | d->extraData.cd->attachedPropertiesType = type.attachedPropertiesMetaObject; |
145 | d->extraData.cd->parserStatusCast = type.parserStatusCast; |
146 | d->extraData.cd->propertyValueSourceCast = type.valueSourceCast; |
147 | d->extraData.cd->propertyValueInterceptorCast = type.valueInterceptorCast; |
148 | d->extraData.cd->finalizerCast = type.has(v: QQmlPrivate::RegisterType::FinalizerCast) |
149 | ? type.finalizerCast |
150 | : -1; |
151 | d->extraData.cd->extFunc = type.extensionObjectCreate; |
152 | d->extraData.cd->customParser = reinterpret_cast<QQmlCustomParser *>(type.customParser); |
153 | d->extraData.cd->registerEnumClassesUnscoped = true; |
154 | d->extraData.cd->registerEnumsFromRelatedTypes = true; |
155 | d->extraData.cd->constructValueType = type.has(v: QQmlPrivate::RegisterType::CreationMethod) |
156 | && type.creationMethod != QQmlPrivate::ValueTypeCreationMethod::None; |
157 | d->extraData.cd->populateValueType = type.has(v: QQmlPrivate::RegisterType::CreationMethod) |
158 | && type.creationMethod == QQmlPrivate::ValueTypeCreationMethod::Structured; |
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 indexOfUnscoped = d->baseMetaObject->indexOfClassInfo(name: "RegisterEnumClassesUnscoped" ); |
166 | if (indexOfUnscoped != -1 |
167 | && qstrcmp(str1: d->baseMetaObject->classInfo(index: indexOfUnscoped).value(), str2: "false" ) == 0) { |
168 | d->extraData.cd->registerEnumClassesUnscoped = false; |
169 | } |
170 | |
171 | auto indexOfRelated = d->baseMetaObject->indexOfClassInfo(name: "RegisterEnumsFromRelatedTypes" ); |
172 | if (indexOfRelated != -1 |
173 | && qstrcmp(str1: d->baseMetaObject->classInfo(index: indexOfRelated).value(), str2: "false" ) == 0) { |
174 | d->extraData.cd->registerEnumsFromRelatedTypes = false; |
175 | } |
176 | } |
177 | |
178 | return d; |
179 | } |
180 | |
181 | static QQmlTypePrivate *createQQmlType(QQmlMetaTypeData *data, const QString &elementName, |
182 | const QQmlPrivate::RegisterCompositeType &type) |
183 | { |
184 | auto *d = new QQmlTypePrivate(QQmlType::CompositeType); |
185 | data->registerType(priv: d); |
186 | d->setName(uri: QString::fromUtf8(utf8: type.uri), element: elementName); |
187 | d->version = type.version; |
188 | |
189 | d->extraData.fd->url = QQmlTypeLoader::normalize(unNormalizedUrl: type.url); |
190 | return d; |
191 | } |
192 | |
193 | static QQmlTypePrivate *createQQmlType(QQmlMetaTypeData *data, const QString &elementName, |
194 | const QQmlPrivate::RegisterCompositeSingletonType &type) |
195 | { |
196 | auto *d = new QQmlTypePrivate(QQmlType::CompositeSingletonType); |
197 | data->registerType(priv: d); |
198 | d->setName(uri: QString::fromUtf8(utf8: type.uri), element: elementName); |
199 | |
200 | d->version = type.version; |
201 | |
202 | d->extraData.sd->singletonInstanceInfo = new QQmlType::SingletonInstanceInfo; |
203 | d->extraData.sd->singletonInstanceInfo->url = QQmlTypeLoader::normalize(unNormalizedUrl: type.url); |
204 | d->extraData.sd->singletonInstanceInfo->typeName = QString::fromUtf8(utf8: type.typeName); |
205 | return d; |
206 | } |
207 | |
208 | void QQmlMetaType::clone(QMetaObjectBuilder &builder, const QMetaObject *mo, |
209 | const QMetaObject *ignoreStart, const QMetaObject *ignoreEnd, |
210 | QQmlMetaType::ClonePolicy policy) |
211 | { |
212 | // Set classname |
213 | builder.setClassName(mo->className()); |
214 | |
215 | // Clone Q_CLASSINFO |
216 | for (int ii = mo->classInfoOffset(); ii < mo->classInfoCount(); ++ii) { |
217 | QMetaClassInfo info = mo->classInfo(index: ii); |
218 | |
219 | int otherIndex = ignoreEnd->indexOfClassInfo(name: info.name()); |
220 | if (otherIndex >= ignoreStart->classInfoOffset() + ignoreStart->classInfoCount()) { |
221 | // Skip |
222 | } else { |
223 | builder.addClassInfo(name: info.name(), value: info.value()); |
224 | } |
225 | } |
226 | |
227 | if (policy != QQmlMetaType::CloneEnumsOnly) { |
228 | // Clone Q_METHODS - do this first to avoid duplicating the notify signals. |
229 | for (int ii = mo->methodOffset(); ii < mo->methodCount(); ++ii) { |
230 | QMetaMethod method = mo->method(index: ii); |
231 | |
232 | // More complex - need to search name |
233 | QByteArray name = method.name(); |
234 | |
235 | bool found = false; |
236 | |
237 | for (int ii = ignoreStart->methodOffset() + ignoreStart->methodCount(); |
238 | !found && ii < ignoreEnd->methodOffset() + ignoreEnd->methodCount(); ++ii) { |
239 | |
240 | QMetaMethod other = ignoreEnd->method(index: ii); |
241 | |
242 | found = name == other.name(); |
243 | } |
244 | |
245 | QMetaMethodBuilder m = builder.addMethod(prototype: method); |
246 | if (found) // SKIP |
247 | m.setAccess(QMetaMethod::Private); |
248 | } |
249 | |
250 | // Clone Q_PROPERTY |
251 | for (int ii = mo->propertyOffset(); ii < mo->propertyCount(); ++ii) { |
252 | QMetaProperty property = mo->property(index: ii); |
253 | |
254 | int otherIndex = ignoreEnd->indexOfProperty(name: property.name()); |
255 | if (otherIndex >= ignoreStart->propertyOffset() + ignoreStart->propertyCount()) { |
256 | builder.addProperty(name: QByteArray("__qml_ignore__" ) + property.name(), |
257 | type: QByteArray("void" )); |
258 | // Skip |
259 | } else { |
260 | builder.addProperty(prototype: property); |
261 | } |
262 | } |
263 | } |
264 | |
265 | // Clone enums registered with the metatype system |
266 | for (int ii = mo->enumeratorOffset(); ii < mo->enumeratorCount(); ++ii) { |
267 | QMetaEnum enumerator = mo->enumerator(index: ii); |
268 | |
269 | int otherIndex = ignoreEnd->indexOfEnumerator(name: enumerator.name()); |
270 | if (otherIndex >= ignoreStart->enumeratorOffset() + ignoreStart->enumeratorCount()) { |
271 | // Skip |
272 | } else { |
273 | builder.addEnumerator(prototype: enumerator); |
274 | } |
275 | } |
276 | } |
277 | |
278 | void QQmlMetaType::qmlInsertModuleRegistration(const QString &uri, void (*registerFunction)()) |
279 | { |
280 | QQmlMetaTypeDataPtr data; |
281 | if (data->moduleTypeRegistrationFunctions.contains(key: uri)) |
282 | qFatal(msg: "Cannot add multiple registrations for %s" , qPrintable(uri)); |
283 | else |
284 | data->moduleTypeRegistrationFunctions.insert(key: uri, value: registerFunction); |
285 | } |
286 | |
287 | void QQmlMetaType::qmlRemoveModuleRegistration(const QString &uri) |
288 | { |
289 | QQmlMetaTypeDataPtr data; |
290 | |
291 | if (!data.isValid()) |
292 | return; // shutdown/deletion race. Not a problem. |
293 | |
294 | if (!data->moduleTypeRegistrationFunctions.contains(key: uri)) |
295 | qFatal(msg: "Cannot remove multiple registrations for %s" , qPrintable(uri)); |
296 | else |
297 | data->moduleTypeRegistrationFunctions.remove(key: uri); |
298 | } |
299 | |
300 | bool QQmlMetaType::qmlRegisterModuleTypes(const QString &uri) |
301 | { |
302 | QQmlMetaTypeDataPtr data; |
303 | return data->registerModuleTypes(uri); |
304 | } |
305 | |
306 | void QQmlMetaType::clearTypeRegistrations() |
307 | { |
308 | //Only cleans global static, assumed no running engine |
309 | QQmlMetaTypeDataPtr data; |
310 | |
311 | data->uriToModule.clear(); |
312 | data->types.clear(); |
313 | data->idToType.clear(); |
314 | data->nameToType.clear(); |
315 | data->urlToType.clear(); |
316 | data->typePropertyCaches.clear(); |
317 | data->urlToNonFileImportType.clear(); |
318 | data->metaObjectToType.clear(); |
319 | data->undeletableTypes.clear(); |
320 | data->propertyCaches.clear(); |
321 | data->inlineComponentTypes.clear(); |
322 | } |
323 | |
324 | void QQmlMetaType::registerTypeAlias(int typeIndex, const QString &name) |
325 | { |
326 | QQmlMetaTypeDataPtr data; |
327 | const QQmlType type = data->types.value(i: typeIndex); |
328 | const QQmlTypePrivate *priv = type.priv(); |
329 | data->nameToType.insert(key: name, value: priv); |
330 | } |
331 | |
332 | int QQmlMetaType::registerAutoParentFunction(const QQmlPrivate::RegisterAutoParent &function) |
333 | { |
334 | if (function.structVersion > 1) |
335 | qFatal(msg: "qmlRegisterType(): Cannot mix incompatible QML versions." ); |
336 | |
337 | QQmlMetaTypeDataPtr data; |
338 | |
339 | data->parentFunctions.append(t: function.function); |
340 | |
341 | return data->parentFunctions.size() - 1; |
342 | } |
343 | |
344 | void QQmlMetaType::unregisterAutoParentFunction(const QQmlPrivate::AutoParentFunction &function) |
345 | { |
346 | QQmlMetaTypeDataPtr data; |
347 | data->parentFunctions.removeOne(t: function); |
348 | } |
349 | |
350 | QQmlType QQmlMetaType::registerInterface(const QQmlPrivate::RegisterInterface &type) |
351 | { |
352 | if (type.structVersion > 1) |
353 | qFatal(msg: "qmlRegisterType(): Cannot mix incompatible QML versions." ); |
354 | |
355 | QQmlMetaTypeDataPtr data; |
356 | QQmlTypePrivate *priv = createQQmlType(data, type); |
357 | Q_ASSERT(priv); |
358 | |
359 | |
360 | data->idToType.insert(key: priv->typeId.id(), value: priv); |
361 | data->idToType.insert(key: priv->listId.id(), value: priv); |
362 | |
363 | data->interfaces.insert(value: type.typeId.id()); |
364 | |
365 | return QQmlType(priv); |
366 | } |
367 | |
368 | static QString registrationTypeString(QQmlType::RegistrationType typeType) |
369 | { |
370 | QString typeStr; |
371 | if (typeType == QQmlType::CppType) |
372 | typeStr = QStringLiteral("element" ); |
373 | else if (typeType == QQmlType::SingletonType) |
374 | typeStr = QStringLiteral("singleton type" ); |
375 | else if (typeType == QQmlType::CompositeSingletonType) |
376 | typeStr = QStringLiteral("composite singleton type" ); |
377 | else if (typeType == QQmlType::SequentialContainerType) |
378 | typeStr = QStringLiteral("sequential container type" ); |
379 | else |
380 | typeStr = QStringLiteral("type" ); |
381 | return typeStr; |
382 | } |
383 | |
384 | // NOTE: caller must hold a QMutexLocker on "data" |
385 | static bool checkRegistration( |
386 | QQmlType::RegistrationType typeType, QQmlMetaTypeData *data, const char *uri, |
387 | const QString &typeName, QTypeRevision version, QMetaType::TypeFlags flags) |
388 | { |
389 | if (!typeName.isEmpty()) { |
390 | if (typeName.at(i: 0).isLower() && (flags & QMetaType::PointerToQObject)) { |
391 | QString failure(QCoreApplication::translate(context: "qmlRegisterType" , key: "Invalid QML %1 name \"%2\"; type names must begin with an uppercase letter" )); |
392 | data->recordTypeRegFailure(message: failure.arg(args: registrationTypeString(typeType), args: typeName)); |
393 | return false; |
394 | } |
395 | |
396 | if (typeName.at(i: 0).isUpper() |
397 | && (flags & (QMetaType::IsGadget | QMetaType::PointerToGadget))) { |
398 | qCWarning(lcTypeRegistration).noquote() |
399 | << QCoreApplication::translate( |
400 | context: "qmlRegisterType" , |
401 | key: "Invalid QML %1 name \"%2\"; " |
402 | "value type names should begin with a lowercase letter" ) |
403 | .arg(args: registrationTypeString(typeType), args: typeName); |
404 | } |
405 | |
406 | // There can also be types that aren't even gadgets, and there can be types for namespaces. |
407 | // We cannot check those, but namespaces should be uppercase. |
408 | |
409 | int typeNameLen = typeName.size(); |
410 | for (int ii = 0; ii < typeNameLen; ++ii) { |
411 | if (!(typeName.at(i: ii).isLetterOrNumber() || typeName.at(i: ii) == u'_')) { |
412 | QString failure(QCoreApplication::translate(context: "qmlRegisterType" , key: "Invalid QML %1 name \"%2\"" )); |
413 | data->recordTypeRegFailure(message: failure.arg(args: registrationTypeString(typeType), args: typeName)); |
414 | return false; |
415 | } |
416 | } |
417 | } |
418 | |
419 | if (uri && !typeName.isEmpty()) { |
420 | QString nameSpace = QString::fromUtf8(utf8: uri); |
421 | QQmlTypeModule *qqtm = data->findTypeModule(module: nameSpace, version); |
422 | if (qqtm && qqtm->lockLevel() != QQmlTypeModule::LockLevel::Open) { |
423 | QString failure(QCoreApplication::translate( |
424 | context: "qmlRegisterType" , |
425 | key: "Cannot install %1 '%2' into protected module '%3' version '%4'" )); |
426 | data->recordTypeRegFailure(message: failure |
427 | .arg(args: registrationTypeString(typeType), args: typeName, args&: nameSpace) |
428 | .arg(a: version.majorVersion())); |
429 | return false; |
430 | } |
431 | } |
432 | |
433 | return true; |
434 | } |
435 | |
436 | // NOTE: caller must hold a QMutexLocker on "data" |
437 | static QQmlTypeModule *getTypeModule( |
438 | const QHashedString &uri, QTypeRevision version, QQmlMetaTypeData *data) |
439 | { |
440 | if (QQmlTypeModule *module = data->findTypeModule(module: uri, version)) |
441 | return module; |
442 | return data->addTypeModule(module: std::make_unique<QQmlTypeModule>(args: uri, args: version.majorVersion())); |
443 | } |
444 | |
445 | // NOTE: caller must hold a QMutexLocker on "data" |
446 | static void addTypeToData(QQmlTypePrivate *type, QQmlMetaTypeData *data) |
447 | { |
448 | Q_ASSERT(type); |
449 | |
450 | if (!type->elementName.isEmpty()) |
451 | data->nameToType.insert(key: type->elementName, value: type); |
452 | |
453 | if (type->baseMetaObject) |
454 | data->metaObjectToType.insert(key: type->baseMetaObject, value: type); |
455 | |
456 | if (type->regType == QQmlType::SequentialContainerType) { |
457 | if (type->listId.isValid()) |
458 | data->idToType.insert(key: type->listId.id(), value: type); |
459 | } else { |
460 | if (type->typeId.isValid()) |
461 | data->idToType.insert(key: type->typeId.id(), value: type); |
462 | |
463 | if (type->listId.flags().testFlag(flag: QMetaType::IsQmlList)) |
464 | data->idToType.insert(key: type->listId.id(), value: type); |
465 | } |
466 | |
467 | if (!type->module.isEmpty()) { |
468 | const QHashedString &mod = type->module; |
469 | |
470 | QQmlTypeModule *module = getTypeModule(uri: mod, version: type->version, data); |
471 | Q_ASSERT(module); |
472 | module->add(type); |
473 | } |
474 | } |
475 | |
476 | QQmlType QQmlMetaType::registerType(const QQmlPrivate::RegisterType &type) |
477 | { |
478 | if (type.structVersion > int(QQmlPrivate::RegisterType::CurrentVersion)) |
479 | qFatal(msg: "qmlRegisterType(): Cannot mix incompatible QML versions." ); |
480 | |
481 | QQmlMetaTypeDataPtr data; |
482 | |
483 | QString elementName = QString::fromUtf8(utf8: type.elementName); |
484 | if (!checkRegistration(typeType: QQmlType::CppType, data, uri: type.uri, typeName: elementName, version: type.version, |
485 | flags: QMetaType(type.typeId).flags())) { |
486 | return QQmlType(); |
487 | } |
488 | |
489 | QQmlTypePrivate *priv = createQQmlType(data, elementName, type); |
490 | addTypeToData(type: priv, data); |
491 | |
492 | return QQmlType(priv); |
493 | } |
494 | |
495 | QQmlType QQmlMetaType::registerSingletonType(const QQmlPrivate::RegisterSingletonType &type) |
496 | { |
497 | if (type.structVersion > 1) |
498 | qFatal(msg: "qmlRegisterType(): Cannot mix incompatible QML versions." ); |
499 | |
500 | QQmlMetaTypeDataPtr data; |
501 | |
502 | QString typeName = QString::fromUtf8(utf8: type.typeName); |
503 | if (!checkRegistration(typeType: QQmlType::SingletonType, data, uri: type.uri, typeName, version: type.version, |
504 | flags: QMetaType(type.typeId).flags())) { |
505 | return QQmlType(); |
506 | } |
507 | |
508 | QQmlTypePrivate *priv = createQQmlType(data, elementName: typeName, type); |
509 | |
510 | addTypeToData(type: priv, data); |
511 | |
512 | return QQmlType(priv); |
513 | } |
514 | |
515 | QQmlType QQmlMetaType::registerCompositeSingletonType(const QQmlPrivate::RegisterCompositeSingletonType &type) |
516 | { |
517 | if (type.structVersion > 1) |
518 | qFatal(msg: "qmlRegisterType(): Cannot mix incompatible QML versions." ); |
519 | |
520 | // Assumes URL is absolute and valid. Checking of user input should happen before the URL enters type. |
521 | QQmlMetaTypeDataPtr data; |
522 | |
523 | QString typeName = QString::fromUtf8(utf8: type.typeName); |
524 | bool fileImport = false; |
525 | if (*(type.uri) == '\0') |
526 | fileImport = true; |
527 | if (!checkRegistration(typeType: QQmlType::CompositeSingletonType, data, uri: fileImport ? nullptr : type.uri, |
528 | typeName, version: type.version, flags: {})) { |
529 | return QQmlType(); |
530 | } |
531 | |
532 | QQmlTypePrivate *priv = createQQmlType(data, elementName: typeName, type); |
533 | addTypeToData(type: priv, data); |
534 | |
535 | QQmlMetaTypeData::Files *files = fileImport ? &(data->urlToType) : &(data->urlToNonFileImportType); |
536 | files->insert(key: QQmlTypeLoader::normalize(unNormalizedUrl: type.url), value: priv); |
537 | |
538 | return QQmlType(priv); |
539 | } |
540 | |
541 | QQmlType QQmlMetaType::registerCompositeType(const QQmlPrivate::RegisterCompositeType &type) |
542 | { |
543 | if (type.structVersion > 1) |
544 | qFatal(msg: "qmlRegisterType(): Cannot mix incompatible QML versions." ); |
545 | |
546 | // Assumes URL is absolute and valid. Checking of user input should happen before the URL enters type. |
547 | QQmlMetaTypeDataPtr data; |
548 | |
549 | QString typeName = QString::fromUtf8(utf8: type.typeName); |
550 | bool fileImport = false; |
551 | if (*(type.uri) == '\0') |
552 | fileImport = true; |
553 | if (!checkRegistration(typeType: QQmlType::CompositeType, data, uri: fileImport?nullptr:type.uri, typeName, |
554 | version: type.version, flags: {})) { |
555 | return QQmlType(); |
556 | } |
557 | |
558 | QQmlTypePrivate *priv = createQQmlType(data, elementName: typeName, type); |
559 | addTypeToData(type: priv, data); |
560 | |
561 | QQmlMetaTypeData::Files *files = fileImport ? &(data->urlToType) : &(data->urlToNonFileImportType); |
562 | files->insert(key: QQmlTypeLoader::normalize(unNormalizedUrl: type.url), value: priv); |
563 | |
564 | return QQmlType(priv); |
565 | } |
566 | |
567 | CompositeMetaTypeIds QQmlMetaType::registerInternalCompositeType(const QByteArray &className) |
568 | { |
569 | QByteArray ptr = className + '*'; |
570 | QByteArray lst = "QQmlListProperty<" + className + '>'; |
571 | |
572 | QMetaType ptr_type(new QQmlMetaTypeInterface(ptr)); |
573 | QMetaType lst_type(new QQmlListMetaTypeInterface(lst, ptr_type.iface())); |
574 | |
575 | // Retrieve the IDs once, so that the types are added to QMetaType's custom type registry. |
576 | ptr_type.id(); |
577 | lst_type.id(); |
578 | |
579 | return {ptr_type, lst_type}; |
580 | } |
581 | |
582 | void QQmlMetaType::unregisterInternalCompositeType(const CompositeMetaTypeIds &typeIds) |
583 | { |
584 | QMetaType metaType(typeIds.id); |
585 | QMetaType listMetaType(typeIds.listId); |
586 | |
587 | // This may be called from delayed dtors on shutdown when the data is already gone. |
588 | QQmlMetaTypeDataPtr data; |
589 | if (data.isValid()) { |
590 | if (QQmlValueType *vt = data->metaTypeToValueType.take(key: metaType.id())) |
591 | delete vt; |
592 | if (QQmlValueType *vt = data->metaTypeToValueType.take(key: listMetaType.id())) |
593 | delete vt; |
594 | } |
595 | |
596 | QMetaType::unregisterMetaType(type: metaType); |
597 | QMetaType::unregisterMetaType(type: listMetaType); |
598 | delete static_cast<const QQmlMetaTypeInterface *>(metaType.iface()); |
599 | delete static_cast<const QQmlListMetaTypeInterface *>(listMetaType.iface()); |
600 | } |
601 | |
602 | int QQmlMetaType::registerUnitCacheHook( |
603 | const QQmlPrivate::RegisterQmlUnitCacheHook &hookRegistration) |
604 | { |
605 | if (hookRegistration.structVersion > 1) |
606 | qFatal(msg: "qmlRegisterType(): Cannot mix incompatible QML versions." ); |
607 | |
608 | QQmlMetaTypeDataPtr data; |
609 | data->lookupCachedQmlUnit << hookRegistration.lookupCachedQmlUnit; |
610 | return 0; |
611 | } |
612 | |
613 | QQmlType QQmlMetaType::registerSequentialContainer( |
614 | const QQmlPrivate::RegisterSequentialContainer &container) |
615 | { |
616 | if (container.structVersion > 1) |
617 | qFatal(msg: "qmlRegisterSequenceContainer(): Cannot mix incompatible QML versions." ); |
618 | |
619 | QQmlMetaTypeDataPtr data; |
620 | |
621 | if (!checkRegistration(typeType: QQmlType::SequentialContainerType, data, uri: container.uri, typeName: QString(), |
622 | version: container.version, flags: {})) { |
623 | return QQmlType(); |
624 | } |
625 | |
626 | QQmlTypePrivate *priv = new QQmlTypePrivate(QQmlType::SequentialContainerType); |
627 | |
628 | data->registerType(priv); |
629 | priv->setName(uri: QString::fromUtf8(utf8: container.uri), element: QString()); |
630 | priv->version = container.version; |
631 | priv->revision = container.revision; |
632 | priv->typeId = container.metaSequence.valueMetaType(); |
633 | priv->listId = container.typeId; |
634 | *priv->extraData.ld = container.metaSequence; |
635 | |
636 | addTypeToData(type: priv, data); |
637 | |
638 | return QQmlType(priv); |
639 | } |
640 | |
641 | void QQmlMetaType::unregisterSequentialContainer(int id) |
642 | { |
643 | unregisterType(type: id); |
644 | } |
645 | |
646 | bool QQmlMetaType::protectModule(const QString &uri, QTypeRevision version, |
647 | bool weakProtectAllVersions) |
648 | { |
649 | QQmlMetaTypeDataPtr data; |
650 | if (version.hasMajorVersion()) { |
651 | if (QQmlTypeModule *module = data->findTypeModule(module: uri, version)) { |
652 | if (!weakProtectAllVersions) { |
653 | module->setLockLevel(QQmlTypeModule::LockLevel::Strong); |
654 | return true; |
655 | } |
656 | } else { |
657 | return false; |
658 | } |
659 | } |
660 | |
661 | const auto range = std::equal_range( |
662 | first: data->uriToModule.begin(), last: data->uriToModule.end(), val: uri, |
663 | comp: std::less<ModuleUri>()); |
664 | |
665 | for (auto it = range.first; it != range.second; ++it) |
666 | (*it)->setLockLevel(QQmlTypeModule::LockLevel::Weak); |
667 | |
668 | return range.first != range.second; |
669 | } |
670 | |
671 | void QQmlMetaType::registerModuleImport(const QString &uri, QTypeRevision moduleVersion, |
672 | const QQmlDirParser::Import &import) |
673 | { |
674 | QQmlMetaTypeDataPtr data; |
675 | |
676 | data->moduleImports.insert(key: QQmlMetaTypeData::VersionedUri(uri, moduleVersion), value: import); |
677 | } |
678 | |
679 | void QQmlMetaType::unregisterModuleImport(const QString &uri, QTypeRevision moduleVersion, |
680 | const QQmlDirParser::Import &import) |
681 | { |
682 | QQmlMetaTypeDataPtr data; |
683 | data->moduleImports.remove(key: QQmlMetaTypeData::VersionedUri(uri, moduleVersion), value: import); |
684 | } |
685 | |
686 | QList<QQmlDirParser::Import> QQmlMetaType::moduleImports( |
687 | const QString &uri, QTypeRevision version) |
688 | { |
689 | QQmlMetaTypeDataPtr data; |
690 | QList<QQmlDirParser::Import> result; |
691 | |
692 | const auto unrevisioned = data->moduleImports.equal_range( |
693 | akey: QQmlMetaTypeData::VersionedUri(uri, QTypeRevision())); |
694 | for (auto it = unrevisioned.second; it != unrevisioned.first;) |
695 | result.append(t: *(--it)); |
696 | |
697 | if (version.hasMajorVersion()) { |
698 | const auto revisioned = data->moduleImports.equal_range( |
699 | akey: QQmlMetaTypeData::VersionedUri(uri, version)); |
700 | for (auto it = revisioned.second; it != revisioned.first;) |
701 | result.append(t: *(--it)); |
702 | return result; |
703 | } |
704 | |
705 | // Use latest module available with that URI. |
706 | const auto begin = data->moduleImports.begin(); |
707 | auto it = unrevisioned.first; |
708 | if (it == begin) |
709 | return result; |
710 | |
711 | const QQmlMetaTypeData::VersionedUri latestVersion = (--it).key(); |
712 | if (latestVersion.uri != uri) |
713 | return result; |
714 | |
715 | do { |
716 | result += *it; |
717 | } while (it != begin && (--it).key() == latestVersion); |
718 | |
719 | return result; |
720 | } |
721 | |
722 | void QQmlMetaType::registerModule(const char *uri, QTypeRevision version) |
723 | { |
724 | QQmlMetaTypeDataPtr data; |
725 | |
726 | QQmlTypeModule *module = getTypeModule(uri: QString::fromUtf8(utf8: uri), version, data); |
727 | Q_ASSERT(module); |
728 | |
729 | if (version.hasMinorVersion()) |
730 | module->addMinorVersion(minorVersion: version.minorVersion()); |
731 | } |
732 | |
733 | int QQmlMetaType::typeId(const char *uri, QTypeRevision version, const char *qmlName) |
734 | { |
735 | QQmlMetaTypeDataPtr data; |
736 | |
737 | QQmlTypeModule *module = getTypeModule(uri: QString::fromUtf8(utf8: uri), version, data); |
738 | if (!module) |
739 | return -1; |
740 | |
741 | QQmlType type = module->type(name: QHashedStringRef(QString::fromUtf8(utf8: qmlName)), version); |
742 | if (!type.isValid()) |
743 | return -1; |
744 | |
745 | return type.index(); |
746 | } |
747 | |
748 | void QQmlMetaType::registerUndeletableType(const QQmlType &dtype) |
749 | { |
750 | QQmlMetaTypeDataPtr data; |
751 | data->undeletableTypes.insert(value: dtype); |
752 | } |
753 | |
754 | static bool namespaceContainsRegistrations(const QQmlMetaTypeData *data, const QString &uri, |
755 | QTypeRevision version) |
756 | { |
757 | // Has any type previously been installed to this namespace? |
758 | QHashedString nameSpace(uri); |
759 | for (const QQmlType &type : data->types) { |
760 | if (type.module() == nameSpace && type.version().majorVersion() == version.majorVersion()) |
761 | return true; |
762 | } |
763 | |
764 | return false; |
765 | } |
766 | |
767 | class QQmlMetaTypeRegistrationFailureRecorder |
768 | { |
769 | Q_DISABLE_COPY_MOVE(QQmlMetaTypeRegistrationFailureRecorder) |
770 | public: |
771 | QQmlMetaTypeRegistrationFailureRecorder(QQmlMetaTypeData *data, QStringList *failures) |
772 | : data(data) |
773 | { |
774 | data->setTypeRegistrationFailures(failures); |
775 | } |
776 | |
777 | ~QQmlMetaTypeRegistrationFailureRecorder() |
778 | { |
779 | data->setTypeRegistrationFailures(nullptr); |
780 | } |
781 | |
782 | QQmlMetaTypeData *data = nullptr; |
783 | }; |
784 | |
785 | |
786 | QQmlMetaType::RegistrationResult QQmlMetaType::registerPluginTypes( |
787 | QObject *instance, const QString &basePath, const QString &uri, |
788 | const QString &typeNamespace, QTypeRevision version, QList<QQmlError> *errors) |
789 | { |
790 | if (!typeNamespace.isEmpty() && typeNamespace != uri) { |
791 | // This is an 'identified' module |
792 | // The namespace for type registrations must match the URI for locating the module |
793 | if (errors) { |
794 | QQmlError error; |
795 | error.setDescription( |
796 | QStringLiteral("Module namespace '%1' does not match import URI '%2'" ) |
797 | .arg(args: typeNamespace, args: uri)); |
798 | errors->prepend(t: error); |
799 | } |
800 | return RegistrationResult::Failure; |
801 | } |
802 | |
803 | QStringList failures; |
804 | QQmlMetaTypeDataPtr data; |
805 | { |
806 | QQmlMetaTypeRegistrationFailureRecorder failureRecorder(data, &failures); |
807 | if (!typeNamespace.isEmpty()) { |
808 | // This is an 'identified' module |
809 | if (namespaceContainsRegistrations(data, uri: typeNamespace, version)) { |
810 | // Other modules have already installed to this namespace |
811 | if (errors) { |
812 | QQmlError error; |
813 | error.setDescription(QStringLiteral("Namespace '%1' has already been used " |
814 | "for type registration" ) |
815 | .arg(a: typeNamespace)); |
816 | errors->prepend(t: error); |
817 | } |
818 | return RegistrationResult::Failure; |
819 | } |
820 | } else { |
821 | // This is not an identified module - provide a warning |
822 | qWarning().nospace() << qPrintable( |
823 | QStringLiteral("Module '%1' does not contain a module identifier directive - " |
824 | "it cannot be protected from external registrations." ).arg(uri)); |
825 | } |
826 | |
827 | if (instance && !qobject_cast<QQmlEngineExtensionInterface *>(object: instance)) { |
828 | QQmlTypesExtensionInterface *iface = qobject_cast<QQmlTypesExtensionInterface *>(object: instance); |
829 | if (!iface) { |
830 | if (errors) { |
831 | QQmlError error; |
832 | // Also does not implement QQmlTypesExtensionInterface, but we want to discourage that. |
833 | error.setDescription(QStringLiteral("Module loaded for URI '%1' does not implement " |
834 | "QQmlEngineExtensionInterface" ).arg(a: typeNamespace)); |
835 | errors->prepend(t: error); |
836 | } |
837 | return RegistrationResult::Failure; |
838 | } |
839 | |
840 | #if QT_DEPRECATED_SINCE(6, 3) |
841 | if (auto *plugin = qobject_cast<QQmlExtensionPlugin *>(object: instance)) { |
842 | // basepath should point to the directory of the module, not the plugin file itself: |
843 | QQmlExtensionPluginPrivate::get(e: plugin)->baseUrl |
844 | = QQmlImports::urlFromLocalFileOrQrcOrUrl(basePath); |
845 | } |
846 | #else |
847 | Q_UNUSED(basePath) |
848 | #endif |
849 | |
850 | const QByteArray bytes = uri.toUtf8(); |
851 | const char *moduleId = bytes.constData(); |
852 | iface->registerTypes(uri: moduleId); |
853 | } |
854 | |
855 | if (failures.isEmpty() && !data->registerModuleTypes(uri)) |
856 | return RegistrationResult::NoRegistrationFunction; |
857 | |
858 | if (!failures.isEmpty()) { |
859 | if (errors) { |
860 | for (const QString &failure : std::as_const(t&: failures)) { |
861 | QQmlError error; |
862 | error.setDescription(failure); |
863 | errors->prepend(t: error); |
864 | } |
865 | } |
866 | return RegistrationResult::Failure; |
867 | } |
868 | } |
869 | |
870 | return RegistrationResult::Success; |
871 | } |
872 | |
873 | /* |
874 | \internal |
875 | |
876 | Fetches the QQmlType instance registered for \a urlString, creating a |
877 | registration for it if it is not already registered, using the associated |
878 | \a typeName, \a isCompositeSingleton, \a majorVersion and \a minorVersion |
879 | details. |
880 | |
881 | Errors (if there are any) are placed into \a errors, if it is nonzero. |
882 | Otherwise errors are printed as warnings. |
883 | */ |
884 | QQmlType QQmlMetaType::typeForUrl(const QString &urlString, |
885 | const QHashedStringRef &qualifiedType, |
886 | bool isCompositeSingleton, QList<QQmlError> *errors, |
887 | QTypeRevision version) |
888 | { |
889 | // ### unfortunate (costly) conversion |
890 | const QUrl url = QQmlTypeLoader::normalize(unNormalizedUrl: QUrl(urlString)); |
891 | |
892 | QQmlMetaTypeDataPtr data; |
893 | { |
894 | QQmlType ret(data->urlToType.value(key: url)); |
895 | if (ret.isValid() && ret.sourceUrl() == url) |
896 | return ret; |
897 | } |
898 | { |
899 | QQmlType ret(data->urlToNonFileImportType.value(key: url)); |
900 | if (ret.isValid() && ret.sourceUrl() == url) |
901 | return ret; |
902 | } |
903 | |
904 | const int dot = qualifiedType.indexOf(QLatin1Char('.')); |
905 | const QString typeName = dot < 0 |
906 | ? qualifiedType.toString() |
907 | : QString(qualifiedType.constData() + dot + 1, qualifiedType.length() - dot - 1); |
908 | |
909 | QStringList failures; |
910 | QQmlMetaTypeRegistrationFailureRecorder failureRecorder(data, &failures); |
911 | |
912 | // Register the type. Note that the URI parameters here are empty; for |
913 | // file type imports, we do not place them in a URI as we don't |
914 | // necessarily have a good and unique one (picture a library import, |
915 | // which may be found in multiple plugin locations on disk), but there |
916 | // are other reasons for this too. |
917 | // |
918 | // By not putting them in a URI, we prevent the types from being |
919 | // registered on a QQmlTypeModule; this is important, as once types are |
920 | // placed on there, they cannot be easily removed, meaning if the |
921 | // developer subsequently loads a different import (meaning different |
922 | // types) with the same URI (using, say, a different plugin path), it is |
923 | // very undesirable that we continue to associate the types from the |
924 | // "old" URI with that new module. |
925 | // |
926 | // Not having URIs also means that the types cannot be found by name |
927 | // etc, the only way to look them up is through QQmlImports -- for |
928 | // better or worse. |
929 | const QQmlType::RegistrationType registrationType = isCompositeSingleton |
930 | ? QQmlType::CompositeSingletonType |
931 | : QQmlType::CompositeType; |
932 | if (checkRegistration(typeType: registrationType, data, uri: nullptr, typeName, version, flags: {})) { |
933 | auto *priv = new QQmlTypePrivate(registrationType); |
934 | priv->setName(uri: QString(), element: typeName); |
935 | priv->version = version; |
936 | |
937 | if (isCompositeSingleton) { |
938 | priv->extraData.sd->singletonInstanceInfo = new QQmlType::SingletonInstanceInfo; |
939 | priv->extraData.sd->singletonInstanceInfo->url = url; |
940 | priv->extraData.sd->singletonInstanceInfo->typeName = typeName; |
941 | } else { |
942 | priv->extraData.fd->url = url; |
943 | } |
944 | |
945 | data->registerType(priv); |
946 | addTypeToData(type: priv, data); |
947 | data->urlToType.insert(key: url, value: priv); |
948 | return QQmlType(priv); |
949 | } |
950 | |
951 | // This means that the type couldn't be found by URL, but could not be |
952 | // registered either, meaning we most likely were passed some kind of bad |
953 | // data. |
954 | if (errors) { |
955 | QQmlError error; |
956 | error.setDescription(failures.join(sep: u'\n')); |
957 | errors->prepend(t: error); |
958 | } else { |
959 | qWarning(msg: "%s" , failures.join(sep: u'\n').toLatin1().constData()); |
960 | } |
961 | return QQmlType(); |
962 | } |
963 | |
964 | QRecursiveMutex *QQmlMetaType::typeRegistrationLock() |
965 | { |
966 | return metaTypeDataLock(); |
967 | } |
968 | |
969 | /* |
970 | Returns the latest version of \a uri installed, or an in valid QTypeRevision(). |
971 | */ |
972 | QTypeRevision QQmlMetaType::latestModuleVersion(const QString &uri) |
973 | { |
974 | QQmlMetaTypeDataPtr data; |
975 | auto upper = std::upper_bound(first: data->uriToModule.begin(), last: data->uriToModule.end(), val: uri, |
976 | comp: std::less<ModuleUri>()); |
977 | if (upper == data->uriToModule.begin()) |
978 | return QTypeRevision(); |
979 | |
980 | const auto module = (--upper)->get(); |
981 | return (module->module() == uri) |
982 | ? QTypeRevision::fromVersion(majorVersion: module->majorVersion(), minorVersion: module->maximumMinorVersion()) |
983 | : QTypeRevision(); |
984 | } |
985 | |
986 | /* |
987 | Returns true if a module \a uri of this version is installed and locked; |
988 | */ |
989 | bool QQmlMetaType::isStronglyLockedModule(const QString &uri, QTypeRevision version) |
990 | { |
991 | QQmlMetaTypeDataPtr data; |
992 | |
993 | if (QQmlTypeModule* qqtm = data->findTypeModule(module: uri, version)) |
994 | return qqtm->lockLevel() == QQmlTypeModule::LockLevel::Strong; |
995 | return false; |
996 | } |
997 | |
998 | /* |
999 | Returns the best matching registered version for the given \a module. If \a version is |
1000 | the does not have a major version, returns the latest registered version. Otherwise |
1001 | chooses the same major version and checks if the minor version is within the range |
1002 | of registered minor versions. If so, retuens the original version, otherwise returns |
1003 | an invalid version. If \a version does not have a minor version, chooses the latest one. |
1004 | */ |
1005 | QTypeRevision QQmlMetaType::matchingModuleVersion(const QString &module, QTypeRevision version) |
1006 | { |
1007 | if (!version.hasMajorVersion()) |
1008 | return latestModuleVersion(uri: module); |
1009 | |
1010 | QQmlMetaTypeDataPtr data; |
1011 | |
1012 | // first, check Types |
1013 | if (QQmlTypeModule *tm = data->findTypeModule(module, version)) { |
1014 | if (!version.hasMinorVersion()) |
1015 | return QTypeRevision::fromVersion(majorVersion: version.majorVersion(), minorVersion: tm->maximumMinorVersion()); |
1016 | |
1017 | if (tm->minimumMinorVersion() <= version.minorVersion() |
1018 | && tm->maximumMinorVersion() >= version.minorVersion()) { |
1019 | return version; |
1020 | } |
1021 | } |
1022 | |
1023 | return QTypeRevision(); |
1024 | } |
1025 | |
1026 | QQmlTypeModule *QQmlMetaType::typeModule(const QString &uri, QTypeRevision version) |
1027 | { |
1028 | QQmlMetaTypeDataPtr data; |
1029 | |
1030 | if (version.hasMajorVersion()) |
1031 | return data->findTypeModule(module: uri, version); |
1032 | |
1033 | auto range = std::equal_range(first: data->uriToModule.begin(), last: data->uriToModule.end(), |
1034 | val: uri, comp: std::less<ModuleUri>()); |
1035 | |
1036 | return range.first == range.second ? nullptr : (--range.second)->get(); |
1037 | } |
1038 | |
1039 | QList<QQmlPrivate::AutoParentFunction> QQmlMetaType::parentFunctions() |
1040 | { |
1041 | QQmlMetaTypeDataPtr data; |
1042 | return data->parentFunctions; |
1043 | } |
1044 | |
1045 | QObject *QQmlMetaType::toQObject(const QVariant &v, bool *ok) |
1046 | { |
1047 | if (!v.metaType().flags().testFlag(flag: QMetaType::PointerToQObject)) { |
1048 | if (ok) *ok = false; |
1049 | return nullptr; |
1050 | } |
1051 | |
1052 | if (ok) *ok = true; |
1053 | |
1054 | return *(QObject *const *)v.constData(); |
1055 | } |
1056 | |
1057 | /* |
1058 | Returns the item type for a list of type \a id. |
1059 | */ |
1060 | QMetaType QQmlMetaType::listValueType(QMetaType metaType) |
1061 | { |
1062 | if (isList(type: metaType)) { |
1063 | const auto iface = metaType.iface(); |
1064 | if (iface && iface->metaObjectFn == &dynamicQmlListMarker) |
1065 | return QMetaType(static_cast<const QQmlListMetaTypeInterface *>(iface)->valueType); |
1066 | } else if (metaType.flags() & QMetaType::PointerToQObject) { |
1067 | return QMetaType(); |
1068 | } |
1069 | |
1070 | QQmlMetaTypeDataPtr data; |
1071 | Q_ASSERT(data); |
1072 | QQmlTypePrivate *type = data->idToType.value(key: metaType.id()); |
1073 | |
1074 | if (type && type->listId == metaType) |
1075 | return type->typeId; |
1076 | else |
1077 | return QMetaType {}; |
1078 | } |
1079 | |
1080 | QQmlAttachedPropertiesFunc QQmlMetaType::attachedPropertiesFunc(QQmlEnginePrivate *engine, |
1081 | const QMetaObject *mo) |
1082 | { |
1083 | QQmlMetaTypeDataPtr data; |
1084 | |
1085 | QQmlType type(data->metaObjectToType.value(key: mo)); |
1086 | return type.attachedPropertiesFunction(engine); |
1087 | } |
1088 | |
1089 | QMetaProperty QQmlMetaType::defaultProperty(const QMetaObject *metaObject) |
1090 | { |
1091 | int idx = metaObject->indexOfClassInfo(name: "DefaultProperty" ); |
1092 | if (-1 == idx) |
1093 | return QMetaProperty(); |
1094 | |
1095 | QMetaClassInfo info = metaObject->classInfo(index: idx); |
1096 | if (!info.value()) |
1097 | return QMetaProperty(); |
1098 | |
1099 | idx = metaObject->indexOfProperty(name: info.value()); |
1100 | if (-1 == idx) |
1101 | return QMetaProperty(); |
1102 | |
1103 | return metaObject->property(index: idx); |
1104 | } |
1105 | |
1106 | QMetaProperty QQmlMetaType::defaultProperty(QObject *obj) |
1107 | { |
1108 | if (!obj) |
1109 | return QMetaProperty(); |
1110 | |
1111 | const QMetaObject *metaObject = obj->metaObject(); |
1112 | return defaultProperty(metaObject); |
1113 | } |
1114 | |
1115 | QMetaMethod QQmlMetaType::defaultMethod(const QMetaObject *metaObject) |
1116 | { |
1117 | int idx = metaObject->indexOfClassInfo(name: "DefaultMethod" ); |
1118 | if (-1 == idx) |
1119 | return QMetaMethod(); |
1120 | |
1121 | QMetaClassInfo info = metaObject->classInfo(index: idx); |
1122 | if (!info.value()) |
1123 | return QMetaMethod(); |
1124 | |
1125 | idx = metaObject->indexOfMethod(method: info.value()); |
1126 | if (-1 == idx) |
1127 | return QMetaMethod(); |
1128 | |
1129 | return metaObject->method(index: idx); |
1130 | } |
1131 | |
1132 | QMetaMethod QQmlMetaType::defaultMethod(QObject *obj) |
1133 | { |
1134 | if (!obj) |
1135 | return QMetaMethod(); |
1136 | |
1137 | const QMetaObject *metaObject = obj->metaObject(); |
1138 | return defaultMethod(metaObject); |
1139 | } |
1140 | |
1141 | /*! |
1142 | See qmlRegisterInterface() for information about when this will return true. |
1143 | */ |
1144 | bool QQmlMetaType::isInterface(QMetaType type) |
1145 | { |
1146 | const QQmlMetaTypeDataPtr data; |
1147 | return data->interfaces.contains(value: type.id()); |
1148 | } |
1149 | |
1150 | const char *QQmlMetaType::interfaceIId(QMetaType metaType) |
1151 | { |
1152 | const QQmlMetaTypeDataPtr data; |
1153 | const QQmlType type(data->idToType.value(key: metaType.id())); |
1154 | return (type.isInterface() && type.typeId() == metaType) ? type.interfaceIId() : nullptr; |
1155 | } |
1156 | |
1157 | bool QQmlMetaType::isList(QMetaType type) |
1158 | { |
1159 | if (type.flags().testFlag(flag: QMetaType::IsQmlList)) |
1160 | return true; |
1161 | else |
1162 | return false; |
1163 | } |
1164 | |
1165 | /*! |
1166 | Returns the type (if any) of URI-qualified named \a qualifiedName and version specified |
1167 | by \a version_major and \a version_minor. |
1168 | */ |
1169 | QQmlType QQmlMetaType::qmlType(const QString &qualifiedName, QTypeRevision version) |
1170 | { |
1171 | int slash = qualifiedName.indexOf(c: QLatin1Char('/')); |
1172 | if (slash <= 0) |
1173 | return QQmlType(); |
1174 | |
1175 | QHashedStringRef module(qualifiedName.constData(), slash); |
1176 | QHashedStringRef name(qualifiedName.constData() + slash + 1, qualifiedName.size() - slash - 1); |
1177 | |
1178 | return qmlType(name, module, version); |
1179 | } |
1180 | |
1181 | /*! |
1182 | \internal |
1183 | Returns the type (if any) of \a name in \a module and the specified \a version. |
1184 | |
1185 | If \a version has no major version, accept any version. |
1186 | If \a version has no minor version, accept any minor version. |
1187 | If \a module is empty, search in all modules and accept any version. |
1188 | */ |
1189 | QQmlType QQmlMetaType::qmlType(const QHashedStringRef &name, const QHashedStringRef &module, |
1190 | QTypeRevision version) |
1191 | { |
1192 | const QQmlMetaTypeDataPtr data; |
1193 | |
1194 | const QHashedString key(QString::fromRawData(name.constData(), size: name.length()), name.hash()); |
1195 | QQmlMetaTypeData::Names::ConstIterator it = data->nameToType.constFind(key); |
1196 | while (it != data->nameToType.cend() && it.key() == name) { |
1197 | QQmlType t(*it); |
1198 | if (module.isEmpty() || t.availableInVersion(module, version)) |
1199 | return t; |
1200 | ++it; |
1201 | } |
1202 | |
1203 | return QQmlType(); |
1204 | } |
1205 | |
1206 | /*! |
1207 | Returns the type (if any) that corresponds to the \a metaObject. Returns an invalid type if no |
1208 | such type is registered. |
1209 | */ |
1210 | QQmlType QQmlMetaType::qmlType(const QMetaObject *metaObject) |
1211 | { |
1212 | const QQmlMetaTypeDataPtr data; |
1213 | return QQmlType(data->metaObjectToType.value(key: metaObject)); |
1214 | } |
1215 | |
1216 | /*! |
1217 | Returns the type (if any) that corresponds to the \a metaObject in version specified |
1218 | by \a version_major and \a version_minor in module specified by \a uri. Returns null if no |
1219 | type is registered. |
1220 | */ |
1221 | QQmlType QQmlMetaType::qmlType(const QMetaObject *metaObject, const QHashedStringRef &module, |
1222 | QTypeRevision version) |
1223 | { |
1224 | const QQmlMetaTypeDataPtr data; |
1225 | |
1226 | const auto range = data->metaObjectToType.equal_range(key: metaObject); |
1227 | for (auto it = range.first; it != range.second; ++it) { |
1228 | QQmlType t(*it); |
1229 | if (module.isEmpty() || t.availableInVersion(module, version)) |
1230 | return t; |
1231 | } |
1232 | |
1233 | return QQmlType(); |
1234 | } |
1235 | |
1236 | /*! |
1237 | Returns the type (if any) that corresponds to \a qmlTypeId. |
1238 | Returns an invalid QQmlType if no such type is registered. |
1239 | */ |
1240 | QQmlType QQmlMetaType::qmlTypeById(int qmlTypeId) |
1241 | { |
1242 | const QQmlMetaTypeDataPtr data; |
1243 | QQmlType type = data->types.value(i: qmlTypeId); |
1244 | if (type.isValid()) |
1245 | return type; |
1246 | return QQmlType(); |
1247 | } |
1248 | |
1249 | /*! |
1250 | Returns the type (if any) that corresponds to \a metaType. |
1251 | Returns an invalid QQmlType if no such type is registered. |
1252 | */ |
1253 | QQmlType QQmlMetaType::qmlType(QMetaType metaType) |
1254 | { |
1255 | const QQmlMetaTypeDataPtr data; |
1256 | QQmlTypePrivate *type = data->idToType.value(key: metaType.id()); |
1257 | return (type && type->typeId == metaType) ? QQmlType(type) : QQmlType(); |
1258 | } |
1259 | |
1260 | QQmlType QQmlMetaType::qmlListType(QMetaType metaType) |
1261 | { |
1262 | const QQmlMetaTypeDataPtr data; |
1263 | QQmlTypePrivate *type = data->idToType.value(key: metaType.id()); |
1264 | return (type && type->listId == metaType) ? QQmlType(type) : QQmlType(); |
1265 | } |
1266 | |
1267 | /*! |
1268 | Returns the type (if any) that corresponds to the given \a url in the set of |
1269 | composite types added through file imports. |
1270 | |
1271 | Returns null if no such type is registered. |
1272 | */ |
1273 | QQmlType QQmlMetaType::qmlType(const QUrl &unNormalizedUrl, bool includeNonFileImports /* = false */) |
1274 | { |
1275 | const QUrl url = QQmlTypeLoader::normalize(unNormalizedUrl); |
1276 | const QQmlMetaTypeDataPtr data; |
1277 | |
1278 | QQmlType type(data->urlToType.value(key: url)); |
1279 | if (!type.isValid() && includeNonFileImports) |
1280 | type = QQmlType(data->urlToNonFileImportType.value(key: url)); |
1281 | |
1282 | if (type.sourceUrl() == url) |
1283 | return type; |
1284 | else |
1285 | return QQmlType(); |
1286 | } |
1287 | |
1288 | QQmlType QQmlMetaType::inlineComponentType(const QQmlType &containingType, const QString &name) |
1289 | { |
1290 | const QQmlMetaTypeDataPtr data; |
1291 | return data->inlineComponentTypes[InlineComponentKey {.containingType: containingType.priv(), .name: name}]; |
1292 | } |
1293 | |
1294 | void QQmlMetaType::associateInlineComponent( |
1295 | const QQmlType &containingType, const QString &name, |
1296 | const CompositeMetaTypeIds &metaTypeIds, QQmlType existingType) |
1297 | { |
1298 | bool const reuseExistingType = existingType.isValid(); |
1299 | auto priv = reuseExistingType |
1300 | ? const_cast<QQmlTypePrivate *>(existingType.priv()) |
1301 | : new QQmlTypePrivate { QQmlType::RegistrationType::InlineComponentType } ; |
1302 | priv->setName( uri: QString::fromUtf8(ba: existingType.typeName()), element: name); |
1303 | QUrl icUrl(existingType.sourceUrl()); |
1304 | icUrl.setFragment(fragment: name); |
1305 | priv->extraData.id->url = icUrl; |
1306 | priv->extraData.id->containingType = containingType.priv(); |
1307 | priv->typeId = metaTypeIds.id; |
1308 | priv->listId = metaTypeIds.listId; |
1309 | QQmlType icType(priv); |
1310 | |
1311 | QQmlMetaTypeDataPtr data; |
1312 | data->inlineComponentTypes.insert(key: {.containingType: containingType.priv(), .name: name}, value: icType); |
1313 | |
1314 | if (!reuseExistingType) |
1315 | priv->release(); |
1316 | } |
1317 | |
1318 | /*! |
1319 | Returns a QQmlPropertyCache for \a obj if one is available. |
1320 | |
1321 | If \a obj is null, being deleted or contains a dynamic meta object, |
1322 | nullptr is returned. |
1323 | */ |
1324 | QQmlPropertyCache::ConstPtr QQmlMetaType::propertyCache(QObject *obj, QTypeRevision version) |
1325 | { |
1326 | if (!obj || QObjectPrivate::get(o: obj)->metaObject || QObjectPrivate::get(o: obj)->wasDeleted) |
1327 | return QQmlPropertyCache::ConstPtr(); |
1328 | return QQmlMetaType::propertyCache(metaObject: obj->metaObject(), version); |
1329 | } |
1330 | |
1331 | QQmlPropertyCache::ConstPtr QQmlMetaType::propertyCache( |
1332 | const QMetaObject *metaObject, QTypeRevision version) |
1333 | { |
1334 | QQmlMetaTypeDataPtr data; // not const: the cache is created on demand |
1335 | return data->propertyCache(metaObject, version); |
1336 | } |
1337 | |
1338 | QQmlPropertyCache::ConstPtr QQmlMetaType::propertyCache( |
1339 | const QQmlType &type, QTypeRevision version) |
1340 | { |
1341 | QQmlMetaTypeDataPtr data; // not const: the cache is created on demand |
1342 | return data->propertyCache(type, version); |
1343 | } |
1344 | |
1345 | /*! |
1346 | * \internal |
1347 | * |
1348 | * Look up by type's baseMetaObject. |
1349 | */ |
1350 | QQmlMetaObject QQmlMetaType::rawMetaObjectForType(QMetaType metaType) |
1351 | { |
1352 | const QQmlMetaTypeDataPtr data; |
1353 | if (auto composite = data->findPropertyCacheInCompositeTypes(t: metaType)) |
1354 | return QQmlMetaObject(composite); |
1355 | |
1356 | const QQmlTypePrivate *type = data->idToType.value(key: metaType.id()); |
1357 | return (type && type->typeId == metaType) ? type->baseMetaObject : nullptr; |
1358 | } |
1359 | |
1360 | /*! |
1361 | * \internal |
1362 | * |
1363 | * Look up by type's metaObject. |
1364 | */ |
1365 | QQmlMetaObject QQmlMetaType::metaObjectForType(QMetaType metaType) |
1366 | { |
1367 | const QQmlMetaTypeDataPtr data; |
1368 | if (auto composite = data->findPropertyCacheInCompositeTypes(t: metaType)) |
1369 | return QQmlMetaObject(composite); |
1370 | |
1371 | const QQmlTypePrivate *type = data->idToType.value(key: metaType.id()); |
1372 | return (type && type->typeId == metaType) |
1373 | ? QQmlType(type).metaObject() |
1374 | : nullptr; |
1375 | } |
1376 | |
1377 | /*! |
1378 | * \internal |
1379 | * |
1380 | * Look up by type's metaObject and version. |
1381 | */ |
1382 | QQmlPropertyCache::ConstPtr QQmlMetaType::propertyCacheForType(QMetaType metaType) |
1383 | { |
1384 | QQmlMetaTypeDataPtr data; |
1385 | if (auto composite = data->findPropertyCacheInCompositeTypes(t: metaType)) |
1386 | return composite; |
1387 | |
1388 | const QQmlTypePrivate *type = data->idToType.value(key: metaType.id()); |
1389 | if (type && type->typeId == metaType) { |
1390 | if (const QMetaObject *mo = QQmlType(type).metaObject()) |
1391 | return data->propertyCache(metaObject: mo, version: type->version); |
1392 | } |
1393 | |
1394 | return QQmlPropertyCache::ConstPtr(); |
1395 | } |
1396 | |
1397 | /*! |
1398 | * \internal |
1399 | * |
1400 | * Look up by type's baseMetaObject and unspecified/any version. |
1401 | * TODO: Is this correct? Passing a plain QTypeRevision() rather than QTypeRevision::zero() or |
1402 | * the actual type's version seems strange. The behavior has been in place for a while. |
1403 | */ |
1404 | QQmlPropertyCache::ConstPtr QQmlMetaType::rawPropertyCacheForType(QMetaType metaType) |
1405 | { |
1406 | QQmlMetaTypeDataPtr data; |
1407 | if (auto composite = QQmlMetaType::findPropertyCacheInCompositeTypes(t: metaType)) |
1408 | return composite; |
1409 | |
1410 | const QQmlTypePrivate *type = data->idToType.value(key: metaType.id()); |
1411 | return (type && type->typeId == metaType) |
1412 | ? data->propertyCache(metaObject: type->baseMetaObject, version: QTypeRevision()) |
1413 | : QQmlPropertyCache::ConstPtr(); |
1414 | } |
1415 | |
1416 | /*! |
1417 | * \internal |
1418 | * |
1419 | * Look up by QQmlType and version. We only fall back to lookup by metaobject if the type |
1420 | * has no revisiononed attributes here. Unspecified versions are interpreted as "any". |
1421 | */ |
1422 | QQmlPropertyCache::ConstPtr QQmlMetaType::rawPropertyCacheForType( |
1423 | QMetaType metaType, QTypeRevision version) |
1424 | { |
1425 | QQmlMetaTypeDataPtr data; |
1426 | if (auto composite = data->findPropertyCacheInCompositeTypes(t: metaType)) |
1427 | return composite; |
1428 | |
1429 | const QQmlTypePrivate *typePriv = data->idToType.value(key: metaType.id()); |
1430 | if (!typePriv || typePriv->typeId != metaType) |
1431 | return QQmlPropertyCache::ConstPtr(); |
1432 | |
1433 | const QQmlType type(typePriv); |
1434 | if (type.containsRevisionedAttributes()) { |
1435 | // It can only have (revisioned) properties or methods if it has a metaobject |
1436 | Q_ASSERT(type.metaObject()); |
1437 | return data->propertyCache(type, version); |
1438 | } |
1439 | |
1440 | if (const QMetaObject *metaObject = type.metaObject()) |
1441 | return data->propertyCache(metaObject, version); |
1442 | |
1443 | return QQmlPropertyCache::ConstPtr(); |
1444 | } |
1445 | |
1446 | void QQmlMetaType::unregisterType(int typeIndex) |
1447 | { |
1448 | QQmlMetaTypeDataPtr data; |
1449 | const QQmlType type = data->types.value(i: typeIndex); |
1450 | if (const QQmlTypePrivate *d = type.priv()) { |
1451 | if (d->regType == QQmlType::CompositeType || d->regType == QQmlType::CompositeSingletonType) |
1452 | removeFromInlineComponents(container&: data->inlineComponentTypes, reference: d); |
1453 | removeQQmlTypePrivate(container&: data->idToType, reference: d); |
1454 | removeQQmlTypePrivate(container&: data->nameToType, reference: d); |
1455 | removeQQmlTypePrivate(container&: data->urlToType, reference: d); |
1456 | removeQQmlTypePrivate(container&: data->urlToNonFileImportType, reference: d); |
1457 | removeQQmlTypePrivate(container&: data->metaObjectToType, reference: d); |
1458 | for (auto & module : data->uriToModule) |
1459 | module->remove(type: d); |
1460 | data->clearPropertyCachesForVersion(index: typeIndex); |
1461 | data->types[typeIndex] = QQmlType(); |
1462 | data->undeletableTypes.remove(value: type); |
1463 | } |
1464 | } |
1465 | |
1466 | void QQmlMetaType::registerMetaObjectForType(const QMetaObject *metaobject, QQmlTypePrivate *type) |
1467 | { |
1468 | Q_ASSERT(type); |
1469 | |
1470 | QQmlMetaTypeDataPtr data; |
1471 | data->metaObjectToType.insert(key: metaobject, value: type); |
1472 | } |
1473 | |
1474 | static bool hasActiveInlineComponents(const QQmlMetaTypeData *data, const QQmlTypePrivate *d) |
1475 | { |
1476 | for (auto it = data->inlineComponentTypes.begin(), end = data->inlineComponentTypes.end(); |
1477 | it != end; ++it) { |
1478 | if (it.key().containingType != d) |
1479 | continue; |
1480 | |
1481 | const QQmlTypePrivate *icPriv = it->priv(); |
1482 | if (icPriv && icPriv->count() > 1) |
1483 | return true; |
1484 | } |
1485 | return false; |
1486 | } |
1487 | |
1488 | void QQmlMetaType::freeUnusedTypesAndCaches() |
1489 | { |
1490 | QQmlMetaTypeDataPtr data; |
1491 | |
1492 | // in case this is being called during program exit, `data` might be destructed already |
1493 | if (!data.isValid()) |
1494 | return; |
1495 | |
1496 | bool deletedAtLeastOneType; |
1497 | do { |
1498 | deletedAtLeastOneType = false; |
1499 | QList<QQmlType>::Iterator it = data->types.begin(); |
1500 | while (it != data->types.end()) { |
1501 | const QQmlTypePrivate *d = (*it).priv(); |
1502 | if (d && d->count() == 1 && !hasActiveInlineComponents(data, d)) { |
1503 | deletedAtLeastOneType = true; |
1504 | |
1505 | if (d->regType == QQmlType::CompositeType |
1506 | || d->regType == QQmlType::CompositeSingletonType) { |
1507 | removeFromInlineComponents(container&: data->inlineComponentTypes, reference: d); |
1508 | } |
1509 | removeQQmlTypePrivate(container&: data->idToType, reference: d); |
1510 | removeQQmlTypePrivate(container&: data->nameToType, reference: d); |
1511 | removeQQmlTypePrivate(container&: data->urlToType, reference: d); |
1512 | removeQQmlTypePrivate(container&: data->urlToNonFileImportType, reference: d); |
1513 | removeQQmlTypePrivate(container&: data->metaObjectToType, reference: d); |
1514 | |
1515 | for (auto &module : data->uriToModule) |
1516 | module->remove(type: d); |
1517 | |
1518 | data->clearPropertyCachesForVersion(index: d->index); |
1519 | *it = QQmlType(); |
1520 | } else { |
1521 | ++it; |
1522 | } |
1523 | } |
1524 | } while (deletedAtLeastOneType); |
1525 | |
1526 | bool deletedAtLeastOneCache; |
1527 | do { |
1528 | deletedAtLeastOneCache = false; |
1529 | auto it = data->propertyCaches.begin(); |
1530 | while (it != data->propertyCaches.end()) { |
1531 | if ((*it)->count() == 1) { |
1532 | it = data->propertyCaches.erase(it); |
1533 | deletedAtLeastOneCache = true; |
1534 | } else { |
1535 | ++it; |
1536 | } |
1537 | } |
1538 | } while (deletedAtLeastOneCache); |
1539 | } |
1540 | |
1541 | /*! |
1542 | Returns the list of registered QML type names. |
1543 | */ |
1544 | QList<QString> QQmlMetaType::qmlTypeNames() |
1545 | { |
1546 | const QQmlMetaTypeDataPtr data; |
1547 | |
1548 | QList<QString> names; |
1549 | names.reserve(asize: data->nameToType.size()); |
1550 | QQmlMetaTypeData::Names::ConstIterator it = data->nameToType.cbegin(); |
1551 | while (it != data->nameToType.cend()) { |
1552 | QQmlType t(*it); |
1553 | names += t.qmlTypeName(); |
1554 | ++it; |
1555 | } |
1556 | |
1557 | return names; |
1558 | } |
1559 | |
1560 | /*! |
1561 | Returns the list of registered QML types. |
1562 | */ |
1563 | QList<QQmlType> QQmlMetaType::qmlTypes() |
1564 | { |
1565 | const QQmlMetaTypeDataPtr data; |
1566 | |
1567 | QList<QQmlType> types; |
1568 | for (const QQmlTypePrivate *t : data->nameToType) |
1569 | types.append(t: QQmlType(t)); |
1570 | |
1571 | return types; |
1572 | } |
1573 | |
1574 | /*! |
1575 | Returns the list of all registered types. |
1576 | */ |
1577 | QList<QQmlType> QQmlMetaType::qmlAllTypes() |
1578 | { |
1579 | const QQmlMetaTypeDataPtr data; |
1580 | return data->types; |
1581 | } |
1582 | |
1583 | /*! |
1584 | Returns the list of registered QML singleton types. |
1585 | */ |
1586 | QList<QQmlType> QQmlMetaType::qmlSingletonTypes() |
1587 | { |
1588 | const QQmlMetaTypeDataPtr data; |
1589 | |
1590 | QList<QQmlType> retn; |
1591 | for (const auto t : std::as_const(t: data->nameToType)) { |
1592 | QQmlType type(t); |
1593 | if (type.isSingleton()) |
1594 | retn.append(t: type); |
1595 | } |
1596 | return retn; |
1597 | } |
1598 | |
1599 | static bool isFullyTyped(const QQmlPrivate::CachedQmlUnit *unit) |
1600 | { |
1601 | quint32 numTypedFunctions = 0; |
1602 | for (const QQmlPrivate::AOTCompiledFunction *function = unit->aotCompiledFunctions; |
1603 | function; ++function) { |
1604 | if (function->functionPtr) |
1605 | ++numTypedFunctions; |
1606 | else |
1607 | return false; |
1608 | } |
1609 | return numTypedFunctions == unit->qmlData->functionTableSize; |
1610 | } |
1611 | |
1612 | const QQmlPrivate::CachedQmlUnit *QQmlMetaType::findCachedCompilationUnit( |
1613 | const QUrl &uri, QQmlMetaType::CacheMode mode, CachedUnitLookupError *status) |
1614 | { |
1615 | Q_ASSERT(mode != RejectAll); |
1616 | const QQmlMetaTypeDataPtr data; |
1617 | |
1618 | for (const auto lookup : std::as_const(t: data->lookupCachedQmlUnit)) { |
1619 | if (const QQmlPrivate::CachedQmlUnit *unit = lookup(uri)) { |
1620 | QString error; |
1621 | if (!QV4::ExecutableCompilationUnit::verifyHeader(unit: unit->qmlData, expectedSourceTimeStamp: QDateTime(), errorString: &error)) { |
1622 | qCDebug(DBG_DISK_CACHE) << "Error loading pre-compiled file " << uri << ":" << error; |
1623 | if (status) |
1624 | *status = CachedUnitLookupError::VersionMismatch; |
1625 | return nullptr; |
1626 | } |
1627 | |
1628 | if (mode == RequireFullyTyped && !isFullyTyped(unit)) { |
1629 | qCDebug(DBG_DISK_CACHE) |
1630 | << "Error loading pre-compiled file " << uri |
1631 | << ": compilation unit contains functions not compiled to native code." ; |
1632 | if (status) |
1633 | *status = CachedUnitLookupError::NotFullyTyped; |
1634 | return nullptr; |
1635 | } |
1636 | |
1637 | if (status) |
1638 | *status = CachedUnitLookupError::NoError; |
1639 | return unit; |
1640 | } |
1641 | } |
1642 | |
1643 | if (status) |
1644 | *status = CachedUnitLookupError::NoUnitFound; |
1645 | |
1646 | return nullptr; |
1647 | } |
1648 | |
1649 | void QQmlMetaType::prependCachedUnitLookupFunction(QQmlPrivate::QmlUnitCacheLookupFunction handler) |
1650 | { |
1651 | QQmlMetaTypeDataPtr data; |
1652 | data->lookupCachedQmlUnit.prepend(t: handler); |
1653 | } |
1654 | |
1655 | void QQmlMetaType::removeCachedUnitLookupFunction(QQmlPrivate::QmlUnitCacheLookupFunction handler) |
1656 | { |
1657 | QQmlMetaTypeDataPtr data; |
1658 | data->lookupCachedQmlUnit.removeAll(t: handler); |
1659 | } |
1660 | |
1661 | /*! |
1662 | Returns the pretty QML type name (e.g. 'Item' instead of 'QtQuickItem') for the given object. |
1663 | */ |
1664 | QString QQmlMetaType::prettyTypeName(const QObject *object) |
1665 | { |
1666 | QString typeName; |
1667 | |
1668 | if (!object) |
1669 | return typeName; |
1670 | |
1671 | QQmlType type = QQmlMetaType::qmlType(metaObject: object->metaObject()); |
1672 | if (type.isValid()) { |
1673 | typeName = type.qmlTypeName(); |
1674 | const int lastSlash = typeName.lastIndexOf(c: QLatin1Char('/')); |
1675 | if (lastSlash != -1) |
1676 | typeName = typeName.mid(position: lastSlash + 1); |
1677 | } |
1678 | |
1679 | if (typeName.isEmpty()) { |
1680 | typeName = QString::fromUtf8(utf8: object->metaObject()->className()); |
1681 | int marker = typeName.indexOf(s: QLatin1String("_QMLTYPE_" )); |
1682 | if (marker != -1) |
1683 | typeName = typeName.left(n: marker); |
1684 | |
1685 | marker = typeName.indexOf(s: QLatin1String("_QML_" )); |
1686 | if (marker != -1) { |
1687 | typeName = QStringView{typeName}.left(n: marker) + QLatin1Char('*'); |
1688 | type = QQmlMetaType::qmlType(metaType: QMetaType::fromName(name: typeName.toUtf8())); |
1689 | if (type.isValid()) { |
1690 | QString qmlTypeName = type.qmlTypeName(); |
1691 | const int lastSlash = qmlTypeName.lastIndexOf(c: QLatin1Char('/')); |
1692 | if (lastSlash != -1) |
1693 | qmlTypeName = qmlTypeName.mid(position: lastSlash + 1); |
1694 | if (!qmlTypeName.isEmpty()) |
1695 | typeName = qmlTypeName; |
1696 | } |
1697 | } |
1698 | } |
1699 | |
1700 | return typeName; |
1701 | } |
1702 | |
1703 | QList<QQmlProxyMetaObject::ProxyData> QQmlMetaType::proxyData(const QMetaObject *mo, |
1704 | const QMetaObject *baseMetaObject, |
1705 | QMetaObject *lastMetaObject) |
1706 | { |
1707 | QList<QQmlProxyMetaObject::ProxyData> metaObjects; |
1708 | mo = mo->d.superdata; |
1709 | |
1710 | const QQmlMetaTypeDataPtr data; |
1711 | |
1712 | auto createProxyMetaObject = [&](QQmlTypePrivate *This, |
1713 | const QMetaObject *superdataBaseMetaObject, |
1714 | const QMetaObject *extMetaObject, |
1715 | QObject *(*extFunc)(QObject *)) { |
1716 | if (!extMetaObject) |
1717 | return; |
1718 | |
1719 | QMetaObjectBuilder builder; |
1720 | clone(builder, mo: extMetaObject, ignoreStart: superdataBaseMetaObject, ignoreEnd: baseMetaObject, |
1721 | policy: extFunc ? QQmlMetaType::CloneAll : QQmlMetaType::CloneEnumsOnly); |
1722 | QMetaObject *mmo = builder.toMetaObject(); |
1723 | mmo->d.superdata = baseMetaObject; |
1724 | if (!metaObjects.isEmpty()) |
1725 | metaObjects.constLast().metaObject->d.superdata = mmo; |
1726 | else if (lastMetaObject) |
1727 | lastMetaObject->d.superdata = mmo; |
1728 | QQmlProxyMetaObject::ProxyData data = { .metaObject: mmo, .createFunc: extFunc, .propertyOffset: 0, .methodOffset: 0 }; |
1729 | metaObjects << data; |
1730 | registerMetaObjectForType(metaobject: mmo, type: This); |
1731 | }; |
1732 | |
1733 | while (mo) { |
1734 | QQmlTypePrivate *t = data->metaObjectToType.value(key: mo); |
1735 | if (t) { |
1736 | if (t->regType == QQmlType::CppType) { |
1737 | createProxyMetaObject(t, t->baseMetaObject, t->extraData.cd->extMetaObject, |
1738 | t->extraData.cd->extFunc); |
1739 | } else if (t->regType == QQmlType::SingletonType) { |
1740 | createProxyMetaObject(t, t->baseMetaObject, t->extraData.sd->extMetaObject, |
1741 | t->extraData.sd->extFunc); |
1742 | } |
1743 | } |
1744 | mo = mo->d.superdata; |
1745 | } |
1746 | |
1747 | return metaObjects; |
1748 | } |
1749 | |
1750 | static bool isInternalType(int idx) |
1751 | { |
1752 | // Qt internal types |
1753 | switch (idx) { |
1754 | case QMetaType::UnknownType: |
1755 | case QMetaType::QStringList: |
1756 | case QMetaType::QObjectStar: |
1757 | case QMetaType::VoidStar: |
1758 | case QMetaType::Nullptr: |
1759 | case QMetaType::QVariant: |
1760 | case QMetaType::QLocale: |
1761 | case QMetaType::QImage: // scarce type, keep as QVariant |
1762 | case QMetaType::QPixmap: // scarce type, keep as QVariant |
1763 | return true; |
1764 | default: |
1765 | return false; |
1766 | } |
1767 | } |
1768 | |
1769 | bool QQmlMetaType::isValueType(QMetaType type) |
1770 | { |
1771 | if (!type.isValid() || isInternalType(idx: type.id())) |
1772 | return false; |
1773 | |
1774 | return valueType(metaType: type) != nullptr; |
1775 | } |
1776 | |
1777 | const QMetaObject *QQmlMetaType::metaObjectForValueType(QMetaType metaType) |
1778 | { |
1779 | switch (metaType.id()) { |
1780 | case QMetaType::QPoint: |
1781 | return &QQmlPointValueType::staticMetaObject; |
1782 | case QMetaType::QPointF: |
1783 | return &QQmlPointFValueType::staticMetaObject; |
1784 | case QMetaType::QSize: |
1785 | return &QQmlSizeValueType::staticMetaObject; |
1786 | case QMetaType::QSizeF: |
1787 | return &QQmlSizeFValueType::staticMetaObject; |
1788 | case QMetaType::QRect: |
1789 | return &QQmlRectValueType::staticMetaObject; |
1790 | case QMetaType::QRectF: |
1791 | return &QQmlRectFValueType::staticMetaObject; |
1792 | #if QT_CONFIG(easingcurve) |
1793 | case QMetaType::QEasingCurve: |
1794 | return &QQmlEasingValueType::staticMetaObject; |
1795 | #endif |
1796 | default: |
1797 | break; |
1798 | } |
1799 | |
1800 | // It doesn't have to be a gadget for a QML type to exist, but we don't want to |
1801 | // call QObject pointers value types. Explicitly registered types also override |
1802 | // the implicit use of gadgets. |
1803 | if (!(metaType.flags() & QMetaType::PointerToQObject)) { |
1804 | if (const QMetaObject *mo = metaObjectForValueType(qmlType: QQmlMetaType::qmlType(metaType))) |
1805 | return mo; |
1806 | } |
1807 | |
1808 | // If it _is_ a gadget, we can just use it. |
1809 | if (metaType.flags() & QMetaType::IsGadget) |
1810 | return metaType.metaObject(); |
1811 | |
1812 | return nullptr; |
1813 | } |
1814 | |
1815 | QQmlValueType *QQmlMetaType::valueType(QMetaType type) |
1816 | { |
1817 | QQmlMetaTypeDataPtr data; |
1818 | |
1819 | const auto it = data->metaTypeToValueType.constFind(key: type.id()); |
1820 | if (it != data->metaTypeToValueType.constEnd()) |
1821 | return *it; |
1822 | |
1823 | if (const QMetaObject *mo = metaObjectForValueType(metaType: type)) |
1824 | return *data->metaTypeToValueType.insert(key: type.id(), value: new QQmlValueType(type, mo)); |
1825 | return *data->metaTypeToValueType.insert(key: type.id(), value: nullptr); |
1826 | } |
1827 | |
1828 | QQmlPropertyCache::ConstPtr QQmlMetaType::findPropertyCacheInCompositeTypes(QMetaType t) |
1829 | { |
1830 | const QQmlMetaTypeDataPtr data; |
1831 | return data->findPropertyCacheInCompositeTypes(t); |
1832 | } |
1833 | |
1834 | void QQmlMetaType::registerInternalCompositeType(QV4::ExecutableCompilationUnit *compilationUnit) |
1835 | { |
1836 | compilationUnit->isRegistered = true; |
1837 | |
1838 | QQmlMetaTypeDataPtr data; |
1839 | |
1840 | // The QQmlCompiledData is not referenced here, but it is removed from this |
1841 | // hash in the QQmlCompiledData destructor |
1842 | data->compositeTypes.insert(key: compilationUnit->typeIds.id.iface(), value: compilationUnit); |
1843 | for (auto &&inlineData: compilationUnit->inlineComponentData) |
1844 | data->compositeTypes.insert(key: inlineData.typeIds.id.iface(), value: compilationUnit); |
1845 | } |
1846 | |
1847 | void QQmlMetaType::unregisterInternalCompositeType(QV4::ExecutableCompilationUnit *compilationUnit) |
1848 | { |
1849 | compilationUnit->isRegistered = false; |
1850 | |
1851 | QQmlMetaTypeDataPtr data; |
1852 | data->compositeTypes.remove(key: compilationUnit->typeIds.id.iface()); |
1853 | for (auto&& icDatum: compilationUnit->inlineComponentData) |
1854 | data->compositeTypes.remove(key: icDatum.typeIds.id.iface()); |
1855 | } |
1856 | |
1857 | QV4::ExecutableCompilationUnit *QQmlMetaType::obtainExecutableCompilationUnit(QMetaType type) |
1858 | { |
1859 | const QQmlMetaTypeDataPtr data; |
1860 | return data->compositeTypes.value(key: type.iface()); |
1861 | } |
1862 | |
1863 | QT_END_NAMESPACE |
1864 | |